diff options
-rw-r--r-- | core/Controller/TaskProcessingApiController.php | 48 | ||||
-rw-r--r-- | core/ResponseDefinitions.php | 14 | ||||
-rw-r--r-- | lib/private/TaskProcessing/Manager.php | 196 | ||||
-rw-r--r-- | lib/public/TaskProcessing/EShapeType.php | 31 | ||||
-rw-r--r-- | lib/public/TaskProcessing/IManager.php | 2 | ||||
-rw-r--r-- | lib/public/TaskProcessing/IProvider.php | 48 | ||||
-rw-r--r-- | lib/public/TaskProcessing/ShapeDescriptor.php | 4 | ||||
-rw-r--r-- | lib/public/TaskProcessing/ShapeEnumValue.php | 43 | ||||
-rw-r--r-- | tests/lib/TaskProcessing/TaskProcessingTest.php | 96 |
9 files changed, 423 insertions, 59 deletions
diff --git a/core/Controller/TaskProcessingApiController.php b/core/Controller/TaskProcessingApiController.php index 6e2a039606f..3bab6bf6fd2 100644 --- a/core/Controller/TaskProcessingApiController.php +++ b/core/Controller/TaskProcessingApiController.php @@ -37,6 +37,7 @@ use OCP\TaskProcessing\Exception\UnauthorizedException; use OCP\TaskProcessing\Exception\ValidationException; use OCP\TaskProcessing\IManager; use OCP\TaskProcessing\ShapeDescriptor; +use OCP\TaskProcessing\ShapeEnumValue; use OCP\TaskProcessing\Task; use RuntimeException; @@ -67,26 +68,35 @@ class TaskProcessingApiController extends \OCP\AppFramework\OCSController { #[PublicPage] #[ApiRoute(verb: 'GET', url: '/tasktypes', root: '/taskprocessing')] public function taskTypes(): DataResponse { - $taskTypes = $this->taskProcessingManager->getAvailableTaskTypes(); - - $serializedTaskTypes = []; - foreach ($taskTypes as $key => $taskType) { - $serializedTaskTypes[$key] = [ - 'name' => $taskType['name'], - 'description' => $taskType['description'], - 'inputShape' => array_map(fn (ShapeDescriptor $descriptor) => - $descriptor->jsonSerialize() + ['mandatory' => true], $taskType['inputShape']) - + array_map(fn (ShapeDescriptor $descriptor) => - $descriptor->jsonSerialize() + ['mandatory' => false], $taskType['optionalInputShape']), - 'outputShape' => array_map(fn (ShapeDescriptor $descriptor) => - $descriptor->jsonSerialize() + ['mandatory' => true], $taskType['outputShape']) - + array_map(fn (ShapeDescriptor $descriptor) => - $descriptor->jsonSerialize() + ['mandatory' => false], $taskType['optionalOutputShape']), - ]; - } - + $taskTypes = array_map(function(array $tt) { + $tt['inputShape'] = array_map(function($descriptor) { + return $descriptor->jsonSerialize(); + }, $tt['inputShape']); + $tt['outputShape'] = array_map(function($descriptor) { + return $descriptor->jsonSerialize(); + }, $tt['outputShape']); + $tt['optionalInputShape'] = array_map(function($descriptor) { + return $descriptor->jsonSerialize(); + }, $tt['optionalInputShape']); + $tt['optionalOutputShape'] = array_map(function($descriptor) { + return $descriptor->jsonSerialize(); + }, $tt['optionalOutputShape']); + $tt['inputShapeEnumValues'] = array_map(function(array $enumValues) { + return array_map(fn(ShapeEnumValue $enumValue) => $enumValue->jsonSerialize(), $enumValues); + }, $tt['inputShapeEnumValues']); + $tt['optionalInputShapeEnumValues'] = array_map(function(array $enumValues) { + return array_map(fn(ShapeEnumValue $enumValue) => $enumValue->jsonSerialize(), $enumValues); + }, $tt['optionalInputShapeEnumValues']); + $tt['outputShapeEnumValues'] = array_map(function(array $enumValues) { + return array_map(fn(ShapeEnumValue $enumValue) => $enumValue->jsonSerialize(), $enumValues); + }, $tt['outputShapeEnumValues']); + $tt['optionalOutputShapeEnumValues'] = array_map(function(array $enumValues) { + return array_map(fn(ShapeEnumValue $enumValue) => $enumValue->jsonSerialize(), $enumValues); + }, $tt['optionalOutputShapeEnumValues']); + return $tt; + }, $this->taskProcessingManager->getAvailableTaskTypes()); return new DataResponse([ - 'types' => $serializedTaskTypes, + 'types' => $taskTypes, ]); } diff --git a/core/ResponseDefinitions.php b/core/ResponseDefinitions.php index 1108e8013a6..10386748c8d 100644 --- a/core/ResponseDefinitions.php +++ b/core/ResponseDefinitions.php @@ -9,6 +9,9 @@ declare(strict_types=1); namespace OC\Core; +use OCP\TaskProcessing\ShapeDescriptor; +use OCP\TaskProcessing\ShapeEnumValue; + /** * @psalm-type CoreLoginFlowV2Credentials = array{ * server: string, @@ -165,15 +168,22 @@ namespace OC\Core; * @psalm-type CoreTaskProcessingShape = array{ * name: string, * description: string, - * type: "Number"|"Text"|"Audio"|"Image"|"Video"|"File"|"ListOfNumbers"|"ListOfTexts"|"ListOfImages"|"ListOfAudios"|"ListOfVideos"|"ListOfFiles", - * mandatory: bool, + * type: "Number"|"Text"|"Audio"|"Image"|"Video"|"File"|"Enum"|"ListOfNumbers"|"ListOfTexts"|"ListOfImages"|"ListOfAudios"|"ListOfVideos"|"ListOfFiles", * } * * @psalm-type CoreTaskProcessingTaskType = array{ * name: string, * description: string, * inputShape: CoreTaskProcessingShape[], + * inputShapeEnumValues: array{name: string, value: string}[][], + * inputShapeDefaults: array<array-key, numeric|string>, + * optionalInputShape: CoreTaskProcessingShape[], + * optionalInputShapeEnumValues: array{name: string, value: string}[][], + * optionalInputShapeDefaults: array<array-key, numeric|string>, * outputShape: CoreTaskProcessingShape[], + * outputShapeEnumValues: array{name: string, value: string}[][], + * optionalOutputShape: CoreTaskProcessingShape[], + * optionalOutputShapeEnumValues: array{name: string, value: string}[][]} * } * * @psalm-type CoreTaskProcessingIO = array<string, numeric|list<numeric>|string|list<string>> diff --git a/lib/private/TaskProcessing/Manager.php b/lib/private/TaskProcessing/Manager.php index ad690acefd7..f62e264ea69 100644 --- a/lib/private/TaskProcessing/Manager.php +++ b/lib/private/TaskProcessing/Manager.php @@ -50,6 +50,7 @@ use OCP\TaskProcessing\IProvider; use OCP\TaskProcessing\ISynchronousProvider; use OCP\TaskProcessing\ITaskType; use OCP\TaskProcessing\ShapeDescriptor; +use OCP\TaskProcessing\ShapeEnumValue; use OCP\TaskProcessing\Task; use OCP\TaskProcessing\TaskTypes\AudioToText; use OCP\TaskProcessing\TaskTypes\TextToImage; @@ -70,34 +71,33 @@ class Manager implements IManager { /** @var list<IProvider>|null */ private ?array $providers = null; - /** @var array<string,array{name: string, description: string, inputShape: array<string, ShapeDescriptor>, optionalInputShape: array<string, ShapeDescriptor>, outputShape: array<string, ShapeDescriptor>, optionalOutputShape: array<string, ShapeDescriptor>}>|null */ + /** + * @var array<array-key,array{name: string, description: string, inputShape: ShapeDescriptor[], inputShapeEnumValues: ShapeEnumValue[][], inputShapeDefaults: array<array-key, numeric|string>, optionalInputShape: ShapeDescriptor[], optionalInputShapeEnumValues: ShapeEnumValue[][], optionalInputShapeDefaults: array<array-key, numeric|string>, outputShape: ShapeDescriptor[], outputShapeEnumValues: ShapeEnumValue[][], optionalOutputShape: ShapeDescriptor[], optionalOutputShapeEnumValues: ShapeEnumValue[][]}> + */ private ?array $availableTaskTypes = null; private IAppData $appData; - public function __construct( - private IConfig $config, - private Coordinator $coordinator, - private IServerContainer $serverContainer, - private LoggerInterface $logger, - private TaskMapper $taskMapper, - private IJobList $jobList, - private IEventDispatcher $dispatcher, - IAppDataFactory $appDataFactory, - private IRootFolder $rootFolder, - private \OCP\TextProcessing\IManager $textProcessingManager, - private \OCP\TextToImage\IManager $textToImageManager, - private \OCP\SpeechToText\ISpeechToTextManager $speechToTextManager, - private IUserMountCache $userMountCache, - private IClientService $clientService, - private IAppManager $appManager, - ) { - $this->appData = $appDataFactory->get('core'); + private IConfig $config, + private Coordinator $coordinator, + private IServerContainer $serverContainer, + private LoggerInterface $logger, + private TaskMapper $taskMapper, + private IJobList $jobList, + private IEventDispatcher $dispatcher, + IAppDataFactory $appDataFactory, + private IRootFolder $rootFolder, + private \OCP\TextProcessing\IManager $textProcessingManager, + private \OCP\TextToImage\IManager $textToImageManager, + private \OCP\SpeechToText\ISpeechToTextManager $speechToTextManager, + private IUserMountCache $userMountCache, + private IClientService $clientService, + private IAppManager $appManager, + ) { + $this->appData = $appDataFactory->get('core'); } - /** - * @return IProvider[] - */ + private function _getTextProcessingProviders(): array { $oldProviders = $this->textProcessingManager->getProviders(); $newProviders = []; @@ -155,6 +155,30 @@ class Manager implements IManager { throw new ProcessingException($e->getMessage(), 0, $e); } } + + public function getInputShapeEnumValues(): array { + return []; + } + + public function getInputShapeDefaults(): array { + return []; + } + + public function getOptionalInputShapeEnumValues(): array { + return []; + } + + public function getOptionalInputShapeDefaults(): array { + return []; + } + + public function getOutputShapeEnumValues(): array { + return []; + } + + public function getOptionalOutputShapeEnumValues(): array { + return []; + } }; $newProviders[$provider->getId()] = $provider; } @@ -289,6 +313,30 @@ class Manager implements IManager { } return ['images' => array_map(fn (ISimpleFile $file) => $file->getContent(), $files)]; } + + public function getInputShapeEnumValues(): array { + return []; + } + + public function getInputShapeDefaults(): array { + return []; + } + + public function getOptionalInputShapeEnumValues(): array { + return []; + } + + public function getOptionalInputShapeDefaults(): array { + return []; + } + + public function getOutputShapeEnumValues(): array { + return []; + } + + public function getOptionalOutputShapeEnumValues(): array { + return []; + } }; $newProviders[$newProvider->getId()] = $newProvider; } @@ -351,6 +399,30 @@ class Manager implements IManager { } return ['output' => $result]; } + + public function getInputShapeEnumValues(): array { + return []; + } + + public function getInputShapeDefaults(): array { + return []; + } + + public function getOptionalInputShapeEnumValues(): array { + return []; + } + + public function getOptionalInputShapeDefaults(): array { + return []; + } + + public function getOutputShapeEnumValues(): array { + return []; + } + + public function getOptionalOutputShapeEnumValues(): array { + return []; + } }; $newProviders[$newProvider->getId()] = $newProvider; } @@ -439,21 +511,37 @@ class Manager implements IManager { /** * @param ShapeDescriptor[] $spec + * @param array<array-key, string|numeric> $defaults + * @param array<array-key, ShapeEnumValue[]> $enumValues * @param array $io + * @param bool $optional * @return void * @throws ValidationException */ - private function validateInput(array $spec, array $io, bool $optional = false): void { + private static function validateInput(array $spec, array $defaults, array $enumValues, array $io, bool $optional = false): void { foreach ($spec as $key => $descriptor) { $type = $descriptor->getShapeType(); if (!isset($io[$key])) { if ($optional) { continue; } + if (isset($defaults[$key])) { + if (EShapeType::getScalarType($type) !== $type) { + throw new ValidationException('Provider tried to set a default value for a non-scalar slot'); + } + if (EShapeType::isFileType($type)) { + throw new ValidationException('Provider tried to set a default value for a slot that is not text or number'); + } + $type->validateInput($defaults[$key]); + continue; + } throw new ValidationException('Missing key: "' . $key . '"'); } try { $type->validateInput($io[$key]); + if (isset($enumValues[$key])) { + $type->validateEnum($io[$key], $enumValues[$key]); + } } catch (ValidationException $e) { throw new ValidationException('Failed to validate input key "' . $key . '": ' . $e->getMessage()); } @@ -461,13 +549,26 @@ class Manager implements IManager { } /** + * Takes task input data and replaces fileIds with File objects + * + * @param array<array-key, list<numeric|string>|numeric|string> $input + * @param array<array-key, numeric|string> ...$defaultSpecs the specs + * @return array<array-key, list<numeric|string>|numeric|string> + */ + public function fillInputDefaults(array $input, ...$defaultSpecs): array { + $spec = array_reduce($defaultSpecs, fn ($carry, $spec) => $carry + $spec, []); + return $spec + $input; + } + + /** * @param ShapeDescriptor[] $spec + * @param array<array-key, ShapeEnumValue[]> $enumValues * @param array $io * @param bool $optional * @return void * @throws ValidationException */ - private function validateOutputWithFileIds(array $spec, array $io, bool $optional = false): void { + private static function validateOutputWithFileIds(array $spec, array $enumValues, array $io, bool $optional = false): void { foreach ($spec as $key => $descriptor) { $type = $descriptor->getShapeType(); if (!isset($io[$key])) { @@ -478,6 +579,9 @@ class Manager implements IManager { } try { $type->validateOutputWithFileIds($io[$key]); + if (isset($enumValues[$key])) { + $type->validateEnum($io[$key], $enumValues[$key]); + } } catch (ValidationException $e) { throw new ValidationException('Failed to validate output key "' . $key . '": ' . $e->getMessage()); } @@ -486,12 +590,13 @@ class Manager implements IManager { /** * @param ShapeDescriptor[] $spec + * @param array<array-key, ShapeEnumValue[]> $enumValues * @param array $io * @param bool $optional * @return void * @throws ValidationException */ - private function validateOutputWithFileData(array $spec, array $io, bool $optional = false): void { + private static function validateOutputWithFileData(array $spec, array $enumValues, array $io, bool $optional = false): void { foreach ($spec as $key => $descriptor) { $type = $descriptor->getShapeType(); if (!isset($io[$key])) { @@ -502,6 +607,9 @@ class Manager implements IManager { } try { $type->validateOutputWithFileData($io[$key]); + if (isset($enumValues[$key])) { + $type->validateEnum($io[$key], $enumValues[$key]); + } } catch (ValidationException $e) { throw new ValidationException('Failed to validate output key "' . $key . '": ' . $e->getMessage()); } @@ -569,10 +677,16 @@ class Manager implements IManager { $availableTaskTypes[$provider->getTaskTypeId()] = [ 'name' => $taskType->getName(), 'description' => $taskType->getDescription(), - 'inputShape' => $taskType->getInputShape(), 'optionalInputShape' => $provider->getOptionalInputShape(), + 'inputShapeEnumValues' => $provider->getInputShapeEnumValues(), + 'inputShapeDefaults' => $provider->getInputShapeDefaults(), + 'inputShape' => $taskType->getInputShape(), + 'optionalInputShapeEnumValues' => $provider->getOptionalInputShapeEnumValues(), + 'optionalInputShapeDefaults' => $provider->getOptionalInputShapeDefaults(), 'outputShape' => $taskType->getOutputShape(), + 'outputShapeEnumValues' => $provider->getOutputShapeEnumValues(), 'optionalOutputShape' => $provider->getOptionalOutputShape(), + 'optionalOutputShapeEnumValues' => $provider->getOptionalOutputShapeEnumValues(), ]; } @@ -592,10 +706,14 @@ class Manager implements IManager { } $taskTypes = $this->getAvailableTaskTypes(); $inputShape = $taskTypes[$task->getTaskTypeId()]['inputShape']; + $inputShapeDefaults = $taskTypes[$task->getTaskTypeId()]['inputShapeDefaults']; + $inputShapeEnumValues = $taskTypes[$task->getTaskTypeId()]['inputShapeEnumValues']; $optionalInputShape = $taskTypes[$task->getTaskTypeId()]['optionalInputShape']; + $optionalInputShapeEnumValues = $taskTypes[$task->getTaskTypeId()]['optionalInputShapeEnumValues']; + $optionalInputShapeDefaults = $taskTypes[$task->getTaskTypeId()]['optionalInputShapeDefaults']; // validate input - $this->validateInput($inputShape, $task->getInput()); - $this->validateInput($optionalInputShape, $task->getInput(), true); + $this->validateInput($inputShape, $inputShapeDefaults, $inputShapeEnumValues, $task->getInput()); + $this->validateInput($optionalInputShape, $optionalInputShapeDefaults, $optionalInputShapeEnumValues, $task->getInput(), true); // authenticate access to mentioned files $ids = []; foreach ($inputShape + $optionalInputShape as $key => $descriptor) { @@ -614,7 +732,9 @@ class Manager implements IManager { $this->validateUserAccessToFile($fileId, $task->getUserId()); } // remove superfluous keys and set input - $task->setInput($this->removeSuperfluousArrayKeys($task->getInput(), $inputShape, $optionalInputShape)); + $input = $this->removeSuperfluousArrayKeys($task->getInput(), $inputShape, $optionalInputShape); + $inputWithDefaults = $this->fillInputDefaults($input, $inputShapeDefaults, $optionalInputShapeDefaults); + $task->setInput($inputWithDefaults); $task->setStatus(Task::STATUS_SCHEDULED); $task->setScheduledAt(time()); $provider = $this->getPreferredProvider($task->getTaskTypeId()); @@ -703,15 +823,17 @@ class Manager implements IManager { } elseif ($result !== null) { $taskTypes = $this->getAvailableTaskTypes(); $outputShape = $taskTypes[$task->getTaskTypeId()]['outputShape']; + $outputShapeEnumValues = $taskTypes[$task->getTaskTypeId()]['outputShapeEnumValues']; $optionalOutputShape = $taskTypes[$task->getTaskTypeId()]['optionalOutputShape']; + $optionalOutputShapeEnumValues = $taskTypes[$task->getTaskTypeId()]['optionalOutputShapeEnumValues']; try { // validate output if (!$isUsingFileIds) { - $this->validateOutputWithFileData($outputShape, $result); - $this->validateOutputWithFileData($optionalOutputShape, $result, true); + $this->validateOutputWithFileData($outputShape, $outputShapeEnumValues, $result); + $this->validateOutputWithFileData($optionalOutputShape, $optionalOutputShapeEnumValues, $result, true); } else { - $this->validateOutputWithFileIds($outputShape, $result); - $this->validateOutputWithFileIds($optionalOutputShape, $result, true); + $this->validateOutputWithFileIds($outputShape, $outputShapeEnumValues, $result); + $this->validateOutputWithFileIds($optionalOutputShape, $optionalOutputShapeEnumValues, $result, true); } $output = $this->removeSuperfluousArrayKeys($result, $outputShape, $optionalOutputShape); // extract raw data and put it in files, replace it with file ids @@ -927,11 +1049,15 @@ class Manager implements IManager { public function prepareInputData(Task $task): array { $taskTypes = $this->getAvailableTaskTypes(); $inputShape = $taskTypes[$task->getTaskTypeId()]['inputShape']; + $inputShapeDefaults = $taskTypes[$task->getTaskTypeId()]['inputShapeDefaults']; + $inputShapeEnumValues = $taskTypes[$task->getTaskTypeId()]['inputShapeEnumValues']; $optionalInputShape = $taskTypes[$task->getTaskTypeId()]['optionalInputShape']; + $optionalInputShapeEnumValues = $taskTypes[$task->getTaskTypeId()]['optionalInputShapeEnumValues']; + $optionalInputShapeDefaults = $taskTypes[$task->getTaskTypeId()]['optionalInputShapeDefaults']; $input = $task->getInput(); // validate input, again for good measure (should have been validated in scheduleTask) - $this->validateInput($inputShape, $input); - $this->validateInput($optionalInputShape, $input, true); + $this->validateInput($inputShape, $inputShapeDefaults, $inputShapeEnumValues, $input); + $this->validateInput($optionalInputShape, $optionalInputShapeDefaults, $optionalInputShapeEnumValues, $input, true); $input = $this->removeSuperfluousArrayKeys($input, $inputShape, $optionalInputShape); $input = $this->fillInputFileData($task->getUserId(), $input, $inputShape, $optionalInputShape); return $input; diff --git a/lib/public/TaskProcessing/EShapeType.php b/lib/public/TaskProcessing/EShapeType.php index 059f9d0c3c7..504f1672100 100644 --- a/lib/public/TaskProcessing/EShapeType.php +++ b/lib/public/TaskProcessing/EShapeType.php @@ -23,6 +23,7 @@ enum EShapeType: int { case Audio = 3; case Video = 4; case File = 5; + case Enum = 6; case ListOfNumbers = 10; case ListOfTexts = 11; case ListOfImages = 12; @@ -32,11 +33,32 @@ enum EShapeType: int { /** * @param mixed $value + * @param ShapeEnumValue[] $enumValues + * @return void + * @throws ValidationException + */ + public function validateEnum(mixed $value, array $enumValues): void { + if ($this !== EShapeType::Enum) { + throw new ValidationException('Provider provided enum values for non-enum slot'); + } + foreach ($enumValues as $enumValue) { + if ($value === $enumValue->getValue()) { + return; + } + } + throw new ValidationException('Wrong value given for Enum slot. Got "' . $value . '", but expected one of the provided enum values: "' . implode('", "', array_map(fn($enumValue) => $enumValue->getValue(), $enumValues)) . '"'); + } + + /** + * @param mixed $value * @return void * @throws ValidationException * @since 30.0.0 */ private function validateNonFileType(mixed $value): void { + if ($this === EShapeType::Enum && !is_string($value)) { + throw new ValidationException('Non-text item provided for Enum slot'); + } if ($this === EShapeType::Text && !is_string($value)) { throw new ValidationException('Non-text item provided for Text slot'); } @@ -159,4 +181,13 @@ enum EShapeType: int { public static function getScalarType(EShapeType $type): EShapeType { return EShapeType::from($type->value % 10); } + + /** + * @param EShapeType $type + * @return bool + * @since 30.0.0 + */ + public static function isFileType(EShapeType $type): bool { + return in_array(EShapeType::getScalarType($type), [EShapeType::File, EShapeType::Image, EShapeType::Audio, EShapeType::Video], true); + } } diff --git a/lib/public/TaskProcessing/IManager.php b/lib/public/TaskProcessing/IManager.php index d7cd96edc45..e3e6b3be09d 100644 --- a/lib/public/TaskProcessing/IManager.php +++ b/lib/public/TaskProcessing/IManager.php @@ -46,7 +46,7 @@ interface IManager { public function getPreferredProvider(string $taskType); /** - * @return array<string,array{name: string, description: string, inputShape: ShapeDescriptor[], optionalInputShape: ShapeDescriptor[], outputShape: ShapeDescriptor[], optionalOutputShape: ShapeDescriptor[]}> + * @return array<array-key,array{name: string, description: string, inputShape: ShapeDescriptor[], inputShapeEnumValues: ShapeEnumValue[][], inputShapeDefaults: array<array-key, numeric|string>, optionalInputShape: ShapeDescriptor[], optionalInputShapeEnumValues: ShapeEnumValue[][], optionalInputShapeDefaults: array<array-key, numeric|string>, outputShape: ShapeDescriptor[], outputShapeEnumValues: ShapeEnumValue[][], optionalOutputShape: ShapeDescriptor[], optionalOutputShapeEnumValues: ShapeEnumValue[][]}> * @since 30.0.0 */ public function getAvailableTaskTypes(): array; diff --git a/lib/public/TaskProcessing/IProvider.php b/lib/public/TaskProcessing/IProvider.php index 68a708ca834..a4e752216c7 100644 --- a/lib/public/TaskProcessing/IProvider.php +++ b/lib/public/TaskProcessing/IProvider.php @@ -58,4 +58,52 @@ interface IProvider { * @psalm-return ShapeDescriptor[] */ public function getOptionalOutputShape(): array; + + /** + * Returns the option list for each input shape ENUM slot + * + * @since 30.0.0 + * @psalm-return ShapeEnumValue[][] + */ + public function getInputShapeEnumValues(): array; + + /** + * Returns the default values for input shape slots + * + * @since 30.0.0 + * @psalm-return array<array-key, string|numeric> + */ + public function getInputShapeDefaults(): array; + + /** + * Returns the option list for each optional input shape ENUM slot + * + * @since 30.0.0 + * @psalm-return ShapeEnumValue[][] + */ + public function getOptionalInputShapeEnumValues(): array; + + /** + * Returns the default values for optional input shape slots + * + * @since 30.0.0 + * @psalm-return array<array-key, string|numeric> + */ + public function getOptionalInputShapeDefaults(): array; + + /** + * Returns the option list for each output shape ENUM slot + * + * @since 30.0.0 + * @psalm-return ShapeEnumValue[][] + */ + public function getOutputShapeEnumValues(): array; + + /** + * Returns the option list for each optional output shape ENUM slot + * + * @since 30.0.0 + * @psalm-return ShapeEnumValue[][] + */ + public function getOptionalOutputShapeEnumValues(): array; } diff --git a/lib/public/TaskProcessing/ShapeDescriptor.php b/lib/public/TaskProcessing/ShapeDescriptor.php index 5759b260865..19e57c8a91d 100644 --- a/lib/public/TaskProcessing/ShapeDescriptor.php +++ b/lib/public/TaskProcessing/ShapeDescriptor.php @@ -49,11 +49,11 @@ class ShapeDescriptor implements \JsonSerializable { } /** - * @return array{name: string, description: string, type: "Number"|"Text"|"Audio"|"Image"|"Video"|"File"|"ListOfNumbers"|"ListOfTexts"|"ListOfImages"|"ListOfAudios"|"ListOfVideos"|"ListOfFiles"} + * @return array{name: string, description: string, type: "Number"|"Text"|"Audio"|"Image"|"Video"|"File"|"Enum"|"ListOfNumbers"|"ListOfTexts"|"ListOfImages"|"ListOfAudios"|"ListOfVideos"|"ListOfFiles"} * @since 30.0.0 */ public function jsonSerialize(): array { - /** @var "Number"|"Text"|"Audio"|"Image"|"Video"|"File"|"ListOfNumbers"|"ListOfTexts"|"ListOfImages"|"ListOfAudios"|"ListOfVideos"|"ListOfFiles" $type */ + /** @var "Number"|"Text"|"Audio"|"Image"|"Video"|"File"|"Enum"|"ListOfNumbers"|"ListOfTexts"|"ListOfImages"|"ListOfAudios"|"ListOfVideos"|"ListOfFiles" $type */ $type = $this->getShapeType()->name; return [ 'name' => $this->getName(), diff --git a/lib/public/TaskProcessing/ShapeEnumValue.php b/lib/public/TaskProcessing/ShapeEnumValue.php new file mode 100644 index 00000000000..33bf9c99d63 --- /dev/null +++ b/lib/public/TaskProcessing/ShapeEnumValue.php @@ -0,0 +1,43 @@ +<?php + +namespace OCP\TaskProcessing; + +class ShapeEnumValue implements \JsonSerializable { + /** + * @param string $name + * @param string $value + * @since 30.0.0 + */ + public function __construct( + private string $name, + private string $value, + ) { + } + + /** + * @return string + * @since 30.0.0 + */ + public function getName(): string { + return $this->name; + } + + /** + * @return string + * @since 30.0.0 + */ + public function getValue(): string { + return $this->value; + } + + /** + * @return array{name: string, value: string} + * @since 30.0.0 + */ + public function jsonSerialize(): array { + return [ + 'name' => $this->getName(), + 'value' => $this->getValue(), + ]; + } +} diff --git a/tests/lib/TaskProcessing/TaskProcessingTest.php b/tests/lib/TaskProcessing/TaskProcessingTest.php index c88f73a861c..ac9dec1cd1d 100644 --- a/tests/lib/TaskProcessing/TaskProcessingTest.php +++ b/tests/lib/TaskProcessing/TaskProcessingTest.php @@ -104,6 +104,30 @@ class AsyncProvider implements IProvider { 'optionalKey' => new ShapeDescriptor('optional Key', 'AN optional key', EShapeType::Text), ]; } + + public function getInputShapeEnumValues(): array { + return []; + } + + public function getInputShapeDefaults(): array { + return []; + } + + public function getOptionalInputShapeEnumValues(): array { + return []; + } + + public function getOptionalInputShapeDefaults(): array { + return []; + } + + public function getOutputShapeEnumValues(): array { + return []; + } + + public function getOptionalOutputShapeEnumValues(): array { + return []; + } } class SuccessfulSyncProvider implements IProvider, ISynchronousProvider { @@ -138,6 +162,30 @@ class SuccessfulSyncProvider implements IProvider, ISynchronousProvider { public function process(?string $userId, array $input, callable $reportProgress): array { return ['output' => $input['input']]; } + + public function getInputShapeEnumValues(): array { + return []; + } + + public function getInputShapeDefaults(): array { + return []; + } + + public function getOptionalInputShapeEnumValues(): array { + return []; + } + + public function getOptionalInputShapeDefaults(): array { + return []; + } + + public function getOutputShapeEnumValues(): array { + return []; + } + + public function getOptionalOutputShapeEnumValues(): array { + return []; + } } class FailingSyncProvider implements IProvider, ISynchronousProvider { @@ -173,6 +221,30 @@ class FailingSyncProvider implements IProvider, ISynchronousProvider { public function process(?string $userId, array $input, callable $reportProgress): array { throw new ProcessingException(self::ERROR_MESSAGE); } + + public function getInputShapeEnumValues(): array { + return []; + } + + public function getInputShapeDefaults(): array { + return []; + } + + public function getOptionalInputShapeEnumValues(): array { + return []; + } + + public function getOptionalInputShapeDefaults(): array { + return []; + } + + public function getOutputShapeEnumValues(): array { + return []; + } + + public function getOptionalOutputShapeEnumValues(): array { + return []; + } } class BrokenSyncProvider implements IProvider, ISynchronousProvider { @@ -207,6 +279,30 @@ class BrokenSyncProvider implements IProvider, ISynchronousProvider { public function process(?string $userId, array $input, callable $reportProgress): array { return []; } + + public function getInputShapeEnumValues(): array { + return []; + } + + public function getInputShapeDefaults(): array { + return []; + } + + public function getOptionalInputShapeEnumValues(): array { + return []; + } + + public function getOptionalInputShapeDefaults(): array { + return []; + } + + public function getOutputShapeEnumValues(): array { + return []; + } + + public function getOptionalOutputShapeEnumValues(): array { + return []; + } } class SuccessfulTextProcessingSummaryProvider implements \OCP\TextProcessing\IProvider { |