From 4ac7f8275b5e89023c8c6c4f468d82d5c782c0d4 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Tue, 9 Jul 2024 11:43:11 +0200 Subject: feat(TaskProcessing): Allow setting task results for file slots Signed-off-by: Marcel Klehr --- lib/private/TaskProcessing/Manager.php | 129 ++++++++++++++++++++++----------- 1 file changed, 87 insertions(+), 42 deletions(-) (limited to 'lib/private/TaskProcessing/Manager.php') diff --git a/lib/private/TaskProcessing/Manager.php b/lib/private/TaskProcessing/Manager.php index f0d1d4ba51a..234534936d4 100644 --- a/lib/private/TaskProcessing/Manager.php +++ b/lib/private/TaskProcessing/Manager.php @@ -18,10 +18,12 @@ use OCP\BackgroundJob\IJobList; use OCP\DB\Exception; use OCP\EventDispatcher\IEventDispatcher; use OCP\Files\AppData\IAppDataFactory; +use OCP\Files\Config\IUserMountCache; use OCP\Files\File; use OCP\Files\GenericFileException; use OCP\Files\IAppData; use OCP\Files\IRootFolder; +use OCP\Files\Node; use OCP\Files\NotPermittedException; use OCP\Files\SimpleFS\ISimpleFile; use OCP\IL10N; @@ -77,7 +79,7 @@ class Manager implements IManager { private \OCP\TextProcessing\IManager $textProcessingManager, private \OCP\TextToImage\IManager $textToImageManager, private \OCP\SpeechToText\ISpeechToTextManager $speechToTextManager, - private \OCP\Share\IManager $shareManager, + private IUserMountCache $userMountCache, ) { $this->appData = $appDataFactory->get('core'); } @@ -561,19 +563,8 @@ class Manager implements IManager { } } foreach ($ids as $fileId) { - $node = $this->rootFolder->getFirstNodeById($fileId); - if ($node === null) { - $node = $this->rootFolder->getFirstNodeByIdInPath($fileId, '/' . $this->rootFolder->getAppDataDirectoryName() . '/'); - if ($node === null) { - throw new ValidationException('Could not find file ' . $fileId); - } - } - /** @var array{users:array, remote: array, mail: array} $accessList */ - $accessList = $this->shareManager->getAccessList($node, true, true); - $userIds = array_map(fn ($id) => strval($id), array_keys($accessList['users'])); - if (!in_array($task->getUserId(), $userIds)) { - throw new UnauthorizedException('User ' . $task->getUserId() . ' does not have access to file ' . $fileId); - } + $this->validateFileId($fileId); + $this->validateUserAccessToFile($fileId, $task->getUserId()); } // remove superfluous keys and set input $task->setInput($this->removeSuperfluousArrayKeys($task->getInput(), $inputShape, $optionalInputShape)); @@ -643,7 +634,7 @@ class Manager implements IManager { return true; } - public function setTaskResult(int $id, ?string $error, ?array $result): void { + public function setTaskResult(int $id, ?string $error, ?array $result, bool $isUsingFileIds = false): void { // TODO: Not sure if we should rather catch the exceptions of getTask here and fail silently $task = $this->getTask($id); if ($task->getStatus() === Task::STATUS_CANCELLED) { @@ -664,7 +655,11 @@ class Manager implements IManager { $this->validateOutput($optionalOutputShape, $result, true); $output = $this->removeSuperfluousArrayKeys($result, $outputShape, $optionalOutputShape); // extract raw data and put it in files, replace it with file ids - $output = $this->encapsulateOutputFileData($output, $outputShape, $optionalOutputShape); + if (!$isUsingFileIds) { + $output = $this->encapsulateOutputFileData($output, $outputShape, $optionalOutputShape); + } else { + $output = $this->validateOutputFileIds($output, $outputShape, $optionalOutputShape); + } $task->setOutput($output); $task->setProgress(1); $task->setStatus(Task::STATUS_SUCCESSFUL); @@ -711,16 +706,13 @@ class Manager implements IManager { } /** - * Takes task input or output data and replaces fileIds with base64 data + * Takes task input data and replaces fileIds with File objects * * @param string|null $userId * @param array|numeric|string> $input * @param ShapeDescriptor[] ...$specs the specs * @return array|numeric|string|File> - * @throws GenericFileException - * @throws LockedException - * @throws NotPermittedException - * @throws ValidationException + * @throws GenericFileException|LockedException|NotPermittedException|ValidationException|UnauthorizedException */ public function fillInputFileData(?string $userId, array $input, ...$specs): array { if ($userId !== null) { @@ -738,30 +730,14 @@ class Manager implements IManager { continue; } if ($type->value < 10) { - $node = $this->rootFolder->getFirstNodeById((int)$input[$key]); - if ($node === null) { - $node = $this->rootFolder->getFirstNodeByIdInPath((int)$input[$key], '/' . $this->rootFolder->getAppDataDirectoryName() . '/'); - if (!$node instanceof File) { - throw new ValidationException('File id given for key "' . $key . '" is not a file'); - } - } elseif (!$node instanceof File) { - throw new ValidationException('File id given for key "' . $key . '" is not a file'); - } - // TODO: Validate if userId has access to this file + $node = $this->validateFileId((int)$input[$key]); + $this->validateUserAccessToFile($input[$key], $userId); $newInputOutput[$key] = $node; } else { $newInputOutput[$key] = []; foreach ($input[$key] as $item) { - $node = $this->rootFolder->getFirstNodeById((int)$item); - if ($node === null) { - $node = $this->rootFolder->getFirstNodeByIdInPath((int)$item, '/' . $this->rootFolder->getAppDataDirectoryName() . '/'); - if (!$node instanceof File) { - throw new ValidationException('File id given for key "' . $key . '" is not a file'); - } - } elseif (!$node instanceof File) { - throw new ValidationException('File id given for key "' . $key . '" is not a file'); - } - // TODO: Validate if userId has access to this file + $node = $this->validateFileId((int)$item); + $this->validateUserAccessToFile($item, $userId); $newInputOutput[$key][] = $node; } } @@ -851,7 +827,7 @@ class Manager implements IManager { * @throws GenericFileException * @throws LockedException * @throws NotPermittedException - * @throws ValidationException + * @throws ValidationException|UnauthorizedException */ public function prepareInputData(Task $task): array { $taskTypes = $this->getAvailableTaskTypes(); @@ -884,4 +860,73 @@ class Manager implements IManager { $taskEntity = \OC\TaskProcessing\Db\Task::fromPublicTask($task); $this->taskMapper->update($taskEntity); } + + /** + * @param array $output + * @param ShapeDescriptor[] ...$specs the specs that define which keys to keep + * @return array + * @throws NotPermittedException + */ + private function validateOutputFileIds(array $output, ...$specs): array { + $newOutput = []; + $spec = array_reduce($specs, fn ($carry, $spec) => $carry + $spec, []); + foreach($spec as $key => $descriptor) { + $type = $descriptor->getShapeType(); + if (!isset($output[$key])) { + continue; + } + if (!in_array(EShapeType::getScalarType($type), [EShapeType::Image, EShapeType::Audio, EShapeType::Video, EShapeType::File], true)) { + $newOutput[$key] = $output[$key]; + continue; + } + if ($type->value < 10) { + // Is scalar file ID + $newOutput[$key] = $this->validateFileId($output[$key]); + } else { + // Is list of file IDs + $newOutput = []; + foreach ($output[$key] as $item) { + $newOutput[$key][] = $this->validateFileId($item); + } + } + } + return $newOutput; + } + + /** + * @param mixed $id + * @return Node + * @throws ValidationException + */ + private function validateFileId(mixed $id): Node { + $node = $this->rootFolder->getFirstNodeById($id); + if ($node === null) { + $node = $this->rootFolder->getFirstNodeByIdInPath($id, '/' . $this->rootFolder->getAppDataDirectoryName() . '/'); + if ($node === null) { + throw new ValidationException('Could not find file ' . $id); + } elseif (!$node instanceof File) { + throw new ValidationException('File with id "' . $id . '" is not a file'); + } + } elseif (!$node instanceof File) { + throw new ValidationException('File with id "' . $id . '" is not a file'); + } + return $node; + } + + /** + * @param mixed $fileId + * @param string $userId + * @return void + * @throws UnauthorizedException + */ + private function validateUserAccessToFile(mixed $fileId, ?string $userId): void { + if ($userId === null) { + throw new UnauthorizedException('User does not have access to file ' . $fileId); + } + $mounts = $this->userMountCache->getMountsForFileId($fileId); + $userIds = array_map(fn ($mount) => $mount->getUser()->getUID(), $mounts); + if (!in_array($userId, $userIds)) { + throw new UnauthorizedException('User ' . $userId . ' does not have access to file ' . $fileId); + } + } } -- cgit v1.2.3 From 5c457c64e88c4c7140c25a580c0964463b7c1094 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Tue, 9 Jul 2024 12:43:31 +0200 Subject: fix: Validate output properly Differentiate between output with file IDs and output with File data Signed-off-by: Marcel Klehr --- lib/private/TaskProcessing/Manager.php | 37 ++++++++++++++++++++++++++++---- lib/public/TaskProcessing/EShapeType.php | 35 +++++++++++++++++++++++++++++- 2 files changed, 67 insertions(+), 5 deletions(-) (limited to 'lib/private/TaskProcessing/Manager.php') diff --git a/lib/private/TaskProcessing/Manager.php b/lib/private/TaskProcessing/Manager.php index 234534936d4..4ec8dd9cad2 100644 --- a/lib/private/TaskProcessing/Manager.php +++ b/lib/private/TaskProcessing/Manager.php @@ -456,7 +456,7 @@ class Manager implements IManager { * @return void * @throws ValidationException */ - private function validateOutput(array $spec, array $io, bool $optional = false): void { + private function validateOutputWithFileIds(array $spec, array $io, bool $optional = false): void { foreach ($spec as $key => $descriptor) { $type = $descriptor->getShapeType(); if (!isset($io[$key])) { @@ -466,7 +466,31 @@ class Manager implements IManager { throw new ValidationException('Missing key: "' . $key . '"'); } try { - $type->validateOutput($io[$key]); + $type->validateOutputWithFileIds($io[$key]); + } catch (ValidationException $e) { + throw new ValidationException('Failed to validate output key "' . $key . '": ' . $e->getMessage()); + } + } + } + + /** + * @param ShapeDescriptor[] $spec + * @param array $io + * @param bool $optional + * @return void + * @throws ValidationException + */ + private function validateOutputWithFileData(array $spec, array $io, bool $optional = false): void { + foreach ($spec as $key => $descriptor) { + $type = $descriptor->getShapeType(); + if (!isset($io[$key])) { + if ($optional) { + continue; + } + throw new ValidationException('Missing key: "' . $key . '"'); + } + try { + $type->validateOutputWithFileData($io[$key]); } catch (ValidationException $e) { throw new ValidationException('Failed to validate output key "' . $key . '": ' . $e->getMessage()); } @@ -651,8 +675,13 @@ class Manager implements IManager { $optionalOutputShape = $taskTypes[$task->getTaskTypeId()]['optionalOutputShape']; try { // validate output - $this->validateOutput($outputShape, $result); - $this->validateOutput($optionalOutputShape, $result, true); + if (!$isUsingFileIds) { + $this->validateOutputWithFileData($outputShape, $result); + $this->validateOutputWithFileData($optionalOutputShape, $result, true); + } else { + $this->validateOutputWithFileIds($outputShape, $result); + $this->validateOutputWithFileIds($optionalOutputShape, $result, true); + } $output = $this->removeSuperfluousArrayKeys($result, $outputShape, $optionalOutputShape); // extract raw data and put it in files, replace it with file ids if (!$isUsingFileIds) { diff --git a/lib/public/TaskProcessing/EShapeType.php b/lib/public/TaskProcessing/EShapeType.php index d66de6e01a8..ade78fda71a 100644 --- a/lib/public/TaskProcessing/EShapeType.php +++ b/lib/public/TaskProcessing/EShapeType.php @@ -89,7 +89,7 @@ enum EShapeType: int { * @throws ValidationException * @since 30.0.0 */ - public function validateOutput(mixed $value) { + public function validateOutputWithFileData(mixed $value): void { $this->validateNonFileType($value); if ($this === EShapeType::Image && !is_string($value)) { throw new ValidationException('Non-image item provided for Image slot'); @@ -117,6 +117,39 @@ enum EShapeType: int { } } + /** + * @param mixed $value + * @return void + * @throws ValidationException + */ + public function validateOutputWithFileIds(mixed $value): void { + $this->validateNonFileType($value); + if ($this === EShapeType::Image && !is_numeric($value)) { + throw new ValidationException('Non-image item provided for Image slot'); + } + if ($this === EShapeType::ListOfImages && (!is_array($value) || count(array_filter($value, fn ($item) => !is_numeric($item))) > 0)) { + throw new ValidationException('Non-image list item provided for ListOfImages slot'); + } + if ($this === EShapeType::Audio && !is_string($value)) { + throw new ValidationException('Non-audio item provided for Audio slot'); + } + if ($this === EShapeType::ListOfAudios && (!is_array($value) || count(array_filter($value, fn ($item) => !is_numeric($item))) > 0)) { + throw new ValidationException('Non-audio list item provided for ListOfAudio slot'); + } + if ($this === EShapeType::Video && !is_string($value)) { + throw new ValidationException('Non-video item provided for Video slot'); + } + if ($this === EShapeType::ListOfVideos && (!is_array($value) || count(array_filter($value, fn ($item) => !is_numeric($item))) > 0)) { + throw new ValidationException('Non-video list item provided for ListOfTexts slot'); + } + if ($this === EShapeType::File && !is_string($value)) { + throw new ValidationException('Non-file item provided for File slot'); + } + if ($this === EShapeType::ListOfFiles && (!is_array($value) || count(array_filter($value, fn ($item) => !is_numeric($item))) > 0)) { + throw new ValidationException('Non-audio list item provided for ListOfFiles slot'); + } + } + /** * @param EShapeType $type * @return EShapeType -- cgit v1.2.3 From 3937cccd4b564c77620f6fc001b1d623d5e15c1b Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Tue, 9 Jul 2024 13:35:46 +0200 Subject: fix(TaskProcessing\Manager#setTaskResult): Replace files contents with ID instead of File object Signed-off-by: Marcel Klehr --- lib/private/TaskProcessing/Manager.php | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'lib/private/TaskProcessing/Manager.php') diff --git a/lib/private/TaskProcessing/Manager.php b/lib/private/TaskProcessing/Manager.php index 4ec8dd9cad2..1158c4a8519 100644 --- a/lib/private/TaskProcessing/Manager.php +++ b/lib/private/TaskProcessing/Manager.php @@ -689,6 +689,11 @@ class Manager implements IManager { } else { $output = $this->validateOutputFileIds($output, $outputShape, $optionalOutputShape); } + foreach ($output as $key => $value) { + if ($value instanceof Node) { + $output[$key] = $value->getId(); + } + } $task->setOutput($output); $task->setProgress(1); $task->setStatus(Task::STATUS_SUCCESSFUL); -- cgit v1.2.3 From c1f2c76f447b91d4c7de43177f25e594d14bace1 Mon Sep 17 00:00:00 2001 From: Alexander Piskun <13381981+bigcat88@users.noreply.github.com> Date: Sat, 13 Jul 2024 11:49:53 +0300 Subject: fix: do not overwrite the output if NodeID exists Signed-off-by: Alexander Piskun Signed-off-by: Marcel Klehr --- lib/private/TaskProcessing/Manager.php | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'lib/private/TaskProcessing/Manager.php') diff --git a/lib/private/TaskProcessing/Manager.php b/lib/private/TaskProcessing/Manager.php index 1158c4a8519..c5ddbb31dc3 100644 --- a/lib/private/TaskProcessing/Manager.php +++ b/lib/private/TaskProcessing/Manager.php @@ -687,12 +687,7 @@ class Manager implements IManager { if (!$isUsingFileIds) { $output = $this->encapsulateOutputFileData($output, $outputShape, $optionalOutputShape); } else { - $output = $this->validateOutputFileIds($output, $outputShape, $optionalOutputShape); - } - foreach ($output as $key => $value) { - if ($value instanceof Node) { - $output[$key] = $value->getId(); - } + $this->validateOutputFileIds($output, $outputShape, $optionalOutputShape); } $task->setOutput($output); $task->setProgress(1); -- cgit v1.2.3 From ee7502ab1c126d4bf744bea816c602ef3a458e35 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Tue, 9 Jul 2024 13:35:46 +0200 Subject: fix(TaskProcessing\Manager#setTaskResult): Replace files contents with ID instead of File object Signed-off-by: Marcel Klehr --- lib/private/TaskProcessing/Manager.php | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'lib/private/TaskProcessing/Manager.php') diff --git a/lib/private/TaskProcessing/Manager.php b/lib/private/TaskProcessing/Manager.php index c5ddbb31dc3..f2b4cecd99c 100644 --- a/lib/private/TaskProcessing/Manager.php +++ b/lib/private/TaskProcessing/Manager.php @@ -689,6 +689,15 @@ class Manager implements IManager { } else { $this->validateOutputFileIds($output, $outputShape, $optionalOutputShape); } + // Turn file objects into IDs + foreach ($output as $key => $value) { + if ($value instanceof Node) { + $output[$key] = $value->getId(); + } + if (is_array($value) && $value[0] instanceof Node) { + $output[$key] = array_map(fn($node) => $node->getId(), $value); + } + } $task->setOutput($output); $task->setProgress(1); $task->setStatus(Task::STATUS_SUCCESSFUL); -- cgit v1.2.3 From 2fed2fc433350cc7633a5c5ffeac149c450483b7 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Sat, 13 Jul 2024 11:41:44 +0200 Subject: fix(TaskProcessingA/Manager): Use time() along with rand int for file names Signed-off-by: Marcel Klehr --- lib/private/TaskProcessing/Manager.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib/private/TaskProcessing/Manager.php') diff --git a/lib/private/TaskProcessing/Manager.php b/lib/private/TaskProcessing/Manager.php index f2b4cecd99c..5d4c4497afd 100644 --- a/lib/private/TaskProcessing/Manager.php +++ b/lib/private/TaskProcessing/Manager.php @@ -845,13 +845,13 @@ class Manager implements IManager { } if ($type->value < 10) { /** @var SimpleFile $file */ - $file = $folder->newFile((string) rand(0, 10000000), $output[$key]); + $file = $folder->newFile(time() . '-' . rand(1, 100000), $output[$key]); $newOutput[$key] = $file->getId(); // polymorphic call to SimpleFile } else { $newOutput = []; foreach ($output[$key] as $item) { /** @var SimpleFile $file */ - $file = $folder->newFile((string) rand(0, 10000000), $item); + $file = $folder->newFile(time() . '-' . rand(1, 100000), $item); $newOutput[$key][] = $file->getId(); } } -- cgit v1.2.3 From fb34b13439fb9751a2929edff5be6aabf430f181 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Sat, 13 Jul 2024 11:42:06 +0200 Subject: fix(TaskProcessingA/Manager): Catch new error Signed-off-by: Marcel Klehr --- lib/private/TaskProcessing/Manager.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'lib/private/TaskProcessing/Manager.php') diff --git a/lib/private/TaskProcessing/Manager.php b/lib/private/TaskProcessing/Manager.php index 5d4c4497afd..421bf39de07 100644 --- a/lib/private/TaskProcessing/Manager.php +++ b/lib/private/TaskProcessing/Manager.php @@ -22,6 +22,7 @@ use OCP\Files\Config\IUserMountCache; use OCP\Files\File; use OCP\Files\GenericFileException; use OCP\Files\IAppData; +use OCP\Files\InvalidPathException; use OCP\Files\IRootFolder; use OCP\Files\Node; use OCP\Files\NotPermittedException; @@ -713,7 +714,12 @@ class Manager implements IManager { $error = 'The task was processed successfully but storing the output in a file failed'; $task->setErrorMessage($error); $this->logger->error($error, ['exception' => $e]); - + } catch (InvalidPathException|\OCP\Files\NotFoundException $e) { + $task->setProgress(1); + $task->setStatus(Task::STATUS_FAILED); + $error = 'The task was processed successfully but the result file could not be found'; + $task->setErrorMessage($error); + $this->logger->error($error, ['exception' => $e]); } } $taskEntity = \OC\TaskProcessing\Db\Task::fromPublicTask($task); -- cgit v1.2.3 From ba33e6220cd8e07cc1723c13490e660b9789c858 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Sat, 13 Jul 2024 12:22:22 +0200 Subject: fix(TaskProcessing): Use getScalarType instead of relying on magic integers Signed-off-by: Marcel Klehr --- lib/private/TaskProcessing/Manager.php | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'lib/private/TaskProcessing/Manager.php') diff --git a/lib/private/TaskProcessing/Manager.php b/lib/private/TaskProcessing/Manager.php index 421bf39de07..669b0d70a80 100644 --- a/lib/private/TaskProcessing/Manager.php +++ b/lib/private/TaskProcessing/Manager.php @@ -773,11 +773,13 @@ class Manager implements IManager { $newInputOutput[$key] = $input[$key]; continue; } - if ($type->value < 10) { + if (EShapeType::getScalarType($type) === $type) { + // is scalar $node = $this->validateFileId((int)$input[$key]); $this->validateUserAccessToFile($input[$key], $userId); $newInputOutput[$key] = $node; } else { + // is list $newInputOutput[$key] = []; foreach ($input[$key] as $item) { $node = $this->validateFileId((int)$item); @@ -849,7 +851,7 @@ class Manager implements IManager { $newOutput[$key] = $output[$key]; continue; } - if ($type->value < 10) { + if (EShapeType::getScalarType($type) === $type) { /** @var SimpleFile $file */ $file = $folder->newFile(time() . '-' . rand(1, 100000), $output[$key]); $newOutput[$key] = $file->getId(); // polymorphic call to SimpleFile @@ -923,7 +925,7 @@ class Manager implements IManager { $newOutput[$key] = $output[$key]; continue; } - if ($type->value < 10) { + if (EShapeType::getScalarType($type) === $type) { // Is scalar file ID $newOutput[$key] = $this->validateFileId($output[$key]); } else { @@ -939,10 +941,10 @@ class Manager implements IManager { /** * @param mixed $id - * @return Node + * @return File * @throws ValidationException */ - private function validateFileId(mixed $id): Node { + private function validateFileId(mixed $id): File { $node = $this->rootFolder->getFirstNodeById($id); if ($node === null) { $node = $this->rootFolder->getFirstNodeByIdInPath($id, '/' . $this->rootFolder->getAppDataDirectoryName() . '/'); -- cgit v1.2.3 From 969cc52851aa579b7b265624c1f3ad6f8b90d6ed Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Sat, 13 Jul 2024 12:23:05 +0200 Subject: fix(TaskProcessing): Run cs:fix Signed-off-by: Marcel Klehr --- lib/private/TaskProcessing/Manager.php | 2 +- tests/lib/TaskProcessing/TaskProcessingTest.php | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) (limited to 'lib/private/TaskProcessing/Manager.php') diff --git a/lib/private/TaskProcessing/Manager.php b/lib/private/TaskProcessing/Manager.php index 669b0d70a80..821fac01b99 100644 --- a/lib/private/TaskProcessing/Manager.php +++ b/lib/private/TaskProcessing/Manager.php @@ -696,7 +696,7 @@ class Manager implements IManager { $output[$key] = $value->getId(); } if (is_array($value) && $value[0] instanceof Node) { - $output[$key] = array_map(fn($node) => $node->getId(), $value); + $output[$key] = array_map(fn ($node) => $node->getId(), $value); } } $task->setOutput($output); diff --git a/tests/lib/TaskProcessing/TaskProcessingTest.php b/tests/lib/TaskProcessing/TaskProcessingTest.php index 2db90019119..699a7d6b2c2 100644 --- a/tests/lib/TaskProcessing/TaskProcessingTest.php +++ b/tests/lib/TaskProcessing/TaskProcessingTest.php @@ -18,7 +18,6 @@ use OCP\EventDispatcher\IEventDispatcher; use OCP\Files\AppData\IAppDataFactory; use OCP\Files\Config\ICachedMountInfo; use OCP\Files\Config\IUserMountCache; -use OCP\Files\IAppData; use OCP\Files\IRootFolder; use OCP\IConfig; use OCP\IDBConnection; -- cgit v1.2.3