diff options
Diffstat (limited to 'apps/testing/lib')
23 files changed, 1453 insertions, 170 deletions
diff --git a/apps/testing/lib/AlternativeHomeUserBackend.php b/apps/testing/lib/AlternativeHomeUserBackend.php index b6d39cf6b99..0524ebe110d 100644 --- a/apps/testing/lib/AlternativeHomeUserBackend.php +++ b/apps/testing/lib/AlternativeHomeUserBackend.php @@ -1,26 +1,15 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud GmbH. - * - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016 ownCloud GmbH. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OCA\Testing; +use OC\User\Database; +use OCP\IConfig; +use OCP\Server; + /** * Alternative home user backend. * @@ -33,7 +22,7 @@ namespace OCA\Testing; * ], * ] */ -class AlternativeHomeUserBackend extends \OC\User\Database { +class AlternativeHomeUserBackend extends Database { public function __construct() { parent::__construct(); } @@ -48,7 +37,7 @@ class AlternativeHomeUserBackend extends \OC\User\Database { if ($uid !== 'admin') { $uid = md5($uid); } - return \OC::$server->getConfig()->getSystemValue('datadirectory', \OC::$SERVERROOT . '/data') . '/' . $uid; + return Server::get(IConfig::class)->getSystemValue('datadirectory', \OC::$SERVERROOT . '/data') . '/' . $uid; } return false; diff --git a/apps/testing/lib/AppInfo/Application.php b/apps/testing/lib/AppInfo/Application.php index 4bf4b5cb470..2add318f327 100644 --- a/apps/testing/lib/AppInfo/Application.php +++ b/apps/testing/lib/AppInfo/Application.php @@ -1,52 +1,78 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud GmbH - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2017-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud GmbH + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OCA\Testing\AppInfo; use OCA\Testing\AlternativeHomeUserBackend; +use OCA\Testing\Conversion\ConversionProvider; +use OCA\Testing\HiddenGroupBackend; +use OCA\Testing\Listener\GetDeclarativeSettingsValueListener; +use OCA\Testing\Listener\RegisterDeclarativeSettingsListener; +use OCA\Testing\Listener\SetDeclarativeSettingsValueListener; +use OCA\Testing\Provider\FakeText2ImageProvider; +use OCA\Testing\Provider\FakeTextProcessingProvider; +use OCA\Testing\Provider\FakeTextProcessingProviderSync; +use OCA\Testing\Provider\FakeTranslationProvider; +use OCA\Testing\Settings\DeclarativeSettingsForm; +use OCA\Testing\TaskProcessing\FakeContextWriteProvider; +use OCA\Testing\TaskProcessing\FakeTextToImageProvider; +use OCA\Testing\TaskProcessing\FakeTextToTextProvider; +use OCA\Testing\TaskProcessing\FakeTextToTextSummaryProvider; +use OCA\Testing\TaskProcessing\FakeTranscribeProvider; +use OCA\Testing\TaskProcessing\FakeTranslateProvider; use OCP\AppFramework\App; use OCP\AppFramework\Bootstrap\IBootContext; use OCP\AppFramework\Bootstrap\IBootstrap; use OCP\AppFramework\Bootstrap\IRegistrationContext; +use OCP\IGroupManager; +use OCP\Settings\Events\DeclarativeSettingsGetValueEvent; +use OCP\Settings\Events\DeclarativeSettingsRegisterFormEvent; +use OCP\Settings\Events\DeclarativeSettingsSetValueEvent; class Application extends App implements IBootstrap { + public const APP_ID = 'testing'; + public function __construct(array $urlParams = []) { - parent::__construct('testing', $urlParams); + parent::__construct(self::APP_ID, $urlParams); } public function register(IRegistrationContext $context): void { + $context->registerTranslationProvider(FakeTranslationProvider::class); + $context->registerTextProcessingProvider(FakeTextProcessingProvider::class); + $context->registerTextProcessingProvider(FakeTextProcessingProviderSync::class); + $context->registerTextToImageProvider(FakeText2ImageProvider::class); + + $context->registerTaskProcessingProvider(FakeTextToTextProvider::class); + $context->registerTaskProcessingProvider(FakeTextToTextSummaryProvider::class); + $context->registerTaskProcessingProvider(FakeTextToImageProvider::class); + $context->registerTaskProcessingProvider(FakeTranslateProvider::class); + $context->registerTaskProcessingProvider(FakeTranscribeProvider::class); + $context->registerTaskProcessingProvider(FakeContextWriteProvider::class); + + $context->registerFileConversionProvider(ConversionProvider::class); + + $context->registerDeclarativeSettings(DeclarativeSettingsForm::class); + $context->registerEventListener(DeclarativeSettingsRegisterFormEvent::class, RegisterDeclarativeSettingsListener::class); + $context->registerEventListener(DeclarativeSettingsGetValueEvent::class, GetDeclarativeSettingsValueListener::class); + $context->registerEventListener(DeclarativeSettingsSetValueEvent::class, SetDeclarativeSettingsValueListener::class); } public function boot(IBootContext $context): void { $server = $context->getServerContainer(); $config = $server->getConfig(); - if ($config->getAppValue('testing', 'enable_alt_user_backend', 'no') === 'yes') { + if ($config->getAppValue(self::APP_ID, 'enable_alt_user_backend', 'no') === 'yes') { $userManager = $server->getUserManager(); // replace all user backends with this one $userManager->clearBackends(); $userManager->registerBackend($context->getAppContainer()->get(AlternativeHomeUserBackend::class)); } + + $groupManager = $server->get(IGroupManager::class); + $groupManager->addBackend($server->get(HiddenGroupBackend::class)); } } diff --git a/apps/testing/lib/Controller/ConfigController.php b/apps/testing/lib/Controller/ConfigController.php index 76f1e1a1ee1..1b38666e51c 100644 --- a/apps/testing/lib/Controller/ConfigController.php +++ b/apps/testing/lib/Controller/ConfigController.php @@ -1,24 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Joas Schilling <coding@schilljs.com> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2017-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OCA\Testing\Controller; @@ -29,19 +14,17 @@ use OCP\IRequest; class ConfigController extends OCSController { - /** @var IConfig */ - private $config; - /** * @param string $appName * @param IRequest $request * @param IConfig $config */ - public function __construct($appName, - IRequest $request, - IConfig $config) { + public function __construct( + $appName, + IRequest $request, + private IConfig $config, + ) { parent::__construct($appName, $request); - $this->config = $config; } /** diff --git a/apps/testing/lib/Controller/LockingController.php b/apps/testing/lib/Controller/LockingController.php index c01ad218c84..edc66d012ee 100644 --- a/apps/testing/lib/Controller/LockingController.php +++ b/apps/testing/lib/Controller/LockingController.php @@ -1,24 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2017-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OCA\Testing\Controller; @@ -39,21 +24,6 @@ use OCP\Lock\LockedException; class LockingController extends OCSController { - /** @var ILockingProvider */ - protected $lockingProvider; - - /** @var FakeDBLockingProvider */ - protected $fakeDBLockingProvider; - - /** @var IDBConnection */ - protected $connection; - - /** @var IConfig */ - protected $config; - - /** @var IRootFolder */ - protected $rootFolder; - /** * @param string $appName * @param IRequest $request @@ -63,20 +33,16 @@ class LockingController extends OCSController { * @param IConfig $config * @param IRootFolder $rootFolder */ - public function __construct($appName, - IRequest $request, - ILockingProvider $lockingProvider, - FakeDBLockingProvider $fakeDBLockingProvider, - IDBConnection $connection, - IConfig $config, - IRootFolder $rootFolder) { + public function __construct( + $appName, + IRequest $request, + protected ILockingProvider $lockingProvider, + protected FakeDBLockingProvider $fakeDBLockingProvider, + protected IDBConnection $connection, + protected IConfig $config, + protected IRootFolder $rootFolder, + ) { parent::__construct($appName, $request); - - $this->lockingProvider = $lockingProvider; - $this->fakeDBLockingProvider = $fakeDBLockingProvider; - $this->connection = $connection; - $this->config = $config; - $this->rootFolder = $rootFolder; } /** @@ -199,7 +165,7 @@ class LockingController extends OCSController { throw new OCSException('', Http::STATUS_LOCKED); } - public function releaseAll(int $type = null): DataResponse { + public function releaseAll(?int $type = null): DataResponse { $lockingProvider = $this->getLockingProvider(); foreach ($this->config->getAppKeys('testing') as $lock) { diff --git a/apps/testing/lib/Controller/RateLimitTestController.php b/apps/testing/lib/Controller/RateLimitTestController.php index d9a8fde5c0d..d3700b69858 100644 --- a/apps/testing/lib/Controller/RateLimitTestController.php +++ b/apps/testing/lib/Controller/RateLimitTestController.php @@ -1,52 +1,36 @@ <?php + /** - * @copyright Copyright (c) 2017 Lukas Reschke <lukas@statuscode.ch> - * - * @author Lukas Reschke <lukas@statuscode.ch> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OCA\Testing\Controller; use OCP\AppFramework\Controller; +use OCP\AppFramework\Http\Attribute\AnonRateLimit; +use OCP\AppFramework\Http\Attribute\NoCSRFRequired; +use OCP\AppFramework\Http\Attribute\PublicPage; +use OCP\AppFramework\Http\Attribute\UserRateLimit; use OCP\AppFramework\Http\JSONResponse; class RateLimitTestController extends Controller { /** - * @PublicPage - * @NoCSRFRequired - * - * @UserRateThrottle(limit=5, period=100) - * @AnonRateThrottle(limit=1, period=100) - * * @return JSONResponse */ + #[PublicPage] + #[NoCSRFRequired] + #[UserRateLimit(limit: 5, period: 100)] + #[AnonRateLimit(limit: 1, period: 100)] public function userAndAnonProtected() { return new JSONResponse(); } /** - * @PublicPage - * @NoCSRFRequired - * - * @AnonRateThrottle(limit=1, period=10) - * * @return JSONResponse */ + #[PublicPage] + #[NoCSRFRequired] + #[AnonRateLimit(limit: 1, period: 10)] public function onlyAnonProtected() { return new JSONResponse(); } diff --git a/apps/testing/lib/Conversion/ConversionProvider.php b/apps/testing/lib/Conversion/ConversionProvider.php new file mode 100644 index 00000000000..b8d93428694 --- /dev/null +++ b/apps/testing/lib/Conversion/ConversionProvider.php @@ -0,0 +1,47 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace OCA\Testing\Conversion; + +use OCP\Files\Conversion\ConversionMimeProvider; +use OCP\Files\Conversion\IConversionProvider; +use OCP\Files\File; +use OCP\IL10N; + +class ConversionProvider implements IConversionProvider { + public function __construct( + private IL10N $l10n, + ) { + } + + public function getSupportedMimeTypes(): array { + return [ + new ConversionMimeProvider('image/jpeg', 'image/png', 'png', $this->l10n->t('Image (.png)')), + new ConversionMimeProvider('image/jpeg', 'image/gif', 'gif', $this->l10n->t('Image (.gif)')), + ]; + } + + public function convertFile(File $file, string $targetMimeType): mixed { + $image = imagecreatefromstring($file->getContent()); + imagepalettetotruecolor($image); + + // Start output buffering + ob_start(); + + // Convert the image to the target format + if ($targetMimeType === 'image/gif') { + imagegif($image); + } else { + imagepng($image); + } + + // End and return the output buffer + return ob_get_clean(); + } +} diff --git a/apps/testing/lib/HiddenGroupBackend.php b/apps/testing/lib/HiddenGroupBackend.php new file mode 100644 index 00000000000..96ead46c06e --- /dev/null +++ b/apps/testing/lib/HiddenGroupBackend.php @@ -0,0 +1,44 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace OCA\Testing; + +use OCP\Group\Backend\ABackend; +use OCP\Group\Backend\IHideFromCollaborationBackend; + +class HiddenGroupBackend extends ABackend implements IHideFromCollaborationBackend { + public function __construct( + private string $groupName = 'hidden_group', + ) { + } + + public function inGroup($uid, $gid): bool { + return false; + } + + public function getUserGroups($uid): array { + return []; + } + + public function getGroups($search = '', $limit = -1, $offset = 0): array { + return $offset === 0 ? [$this->groupName] : []; + } + + public function groupExists($gid): bool { + return $gid === $this->groupName; + } + + public function usersInGroup($gid, $search = '', $limit = -1, $offset = 0): array { + return []; + } + + public function hideGroup(string $groupId): bool { + return true; + } +} diff --git a/apps/testing/lib/Listener/GetDeclarativeSettingsValueListener.php b/apps/testing/lib/Listener/GetDeclarativeSettingsValueListener.php new file mode 100644 index 00000000000..0df58168007 --- /dev/null +++ b/apps/testing/lib/Listener/GetDeclarativeSettingsValueListener.php @@ -0,0 +1,37 @@ +<?php + +declare(strict_types=1); +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OCA\Testing\Listener; + +use OCP\EventDispatcher\Event; +use OCP\EventDispatcher\IEventListener; +use OCP\IConfig; +use OCP\Settings\Events\DeclarativeSettingsGetValueEvent; + +/** + * @template-implements IEventListener<DeclarativeSettingsGetValueEvent> + */ +class GetDeclarativeSettingsValueListener implements IEventListener { + + public function __construct( + private IConfig $config, + ) { + } + + public function handle(Event $event): void { + if (!$event instanceof DeclarativeSettingsGetValueEvent) { + return; + } + + if ($event->getApp() !== 'testing') { + return; + } + + $value = $this->config->getUserValue($event->getUser()->getUID(), $event->getApp(), $event->getFieldId()); + $event->setValue($value); + } +} diff --git a/apps/testing/lib/Listener/RegisterDeclarativeSettingsListener.php b/apps/testing/lib/Listener/RegisterDeclarativeSettingsListener.php new file mode 100644 index 00000000000..aab39b78d76 --- /dev/null +++ b/apps/testing/lib/Listener/RegisterDeclarativeSettingsListener.php @@ -0,0 +1,71 @@ +<?php + +declare(strict_types=1); +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OCA\Testing\Listener; + +use OCP\EventDispatcher\Event; +use OCP\EventDispatcher\IEventListener; +use OCP\Settings\DeclarativeSettingsTypes; +use OCP\Settings\Events\DeclarativeSettingsRegisterFormEvent; + +/** + * @template-implements IEventListener<DeclarativeSettingsRegisterFormEvent> + */ +class RegisterDeclarativeSettingsListener implements IEventListener { + + public function __construct() { + } + + public function handle(Event $event): void { + if (!($event instanceof DeclarativeSettingsRegisterFormEvent)) { + // Unrelated + return; + } + + $event->registerSchema('testing', [ + 'id' => 'test_declarative_form_event', + 'priority' => 20, + 'section_type' => DeclarativeSettingsTypes::SECTION_TYPE_ADMIN, + 'section_id' => 'additional', + 'storage_type' => DeclarativeSettingsTypes::STORAGE_TYPE_INTERNAL, + 'title' => 'Test declarative settings event', // NcSettingsSection name + 'description' => 'This form is registered via the RegisterDeclarativeSettingsFormEvent', // NcSettingsSection description + 'fields' => [ + [ + 'id' => 'event_field_1', + 'title' => 'Why is 42 this answer to all questions?', + 'description' => 'Hint: It\'s not', + 'type' => DeclarativeSettingsTypes::TEXT, + 'placeholder' => 'Enter your answer', + 'default' => 'Because it is', + ], + [ + 'id' => 'feature_rating', + 'title' => 'How would you rate this feature?', + 'description' => 'Your vote is not anonymous', + 'type' => DeclarativeSettingsTypes::RADIO, // radio, radio-button (NcCheckboxRadioSwitch button-variant) + 'label' => 'Select single toggle', + 'default' => '3', + 'options' => [ + [ + 'name' => 'Awesome', // NcCheckboxRadioSwitch display name + 'value' => '1' // NcCheckboxRadioSwitch value + ], + [ + 'name' => 'Very awesome', + 'value' => '2' + ], + [ + 'name' => 'Super awesome', + 'value' => '3' + ], + ], + ], + ], + ]); + } +} diff --git a/apps/testing/lib/Listener/SetDeclarativeSettingsValueListener.php b/apps/testing/lib/Listener/SetDeclarativeSettingsValueListener.php new file mode 100644 index 00000000000..0058e7df43e --- /dev/null +++ b/apps/testing/lib/Listener/SetDeclarativeSettingsValueListener.php @@ -0,0 +1,37 @@ +<?php + +declare(strict_types=1); +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OCA\Testing\Listener; + +use OCP\EventDispatcher\Event; +use OCP\EventDispatcher\IEventListener; +use OCP\IConfig; +use OCP\Settings\Events\DeclarativeSettingsSetValueEvent; + +/** + * @template-implements IEventListener<DeclarativeSettingsSetValueEvent> + */ +class SetDeclarativeSettingsValueListener implements IEventListener { + + public function __construct( + private IConfig $config, + ) { + } + + public function handle(Event $event): void { + if (!$event instanceof DeclarativeSettingsSetValueEvent) { + return; + } + + if ($event->getApp() !== 'testing') { + return; + } + + error_log('Testing app wants to store ' . $event->getValue() . ' for field ' . $event->getFieldId() . ' for user ' . $event->getUser()->getUID()); + $this->config->setUserValue($event->getUser()->getUID(), $event->getApp(), $event->getFieldId(), $event->getValue()); + } +} diff --git a/apps/testing/lib/Locking/FakeDBLockingProvider.php b/apps/testing/lib/Locking/FakeDBLockingProvider.php index 2556ba29a64..f77bacc7a63 100644 --- a/apps/testing/lib/Locking/FakeDBLockingProvider.php +++ b/apps/testing/lib/Locking/FakeDBLockingProvider.php @@ -1,32 +1,15 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2017-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OCA\Testing\Locking; +use OC\Lock\DBLockingProvider; use OCP\AppFramework\Utility\ITimeFactory; use OCP\IDBConnection; -use Psr\Log\LoggerInterface; -use OC\Lock\DBLockingProvider; class FakeDBLockingProvider extends DBLockingProvider { // Lock for 10 hours just to be sure @@ -39,7 +22,7 @@ class FakeDBLockingProvider extends DBLockingProvider { public function __construct( IDBConnection $connection, - ITimeFactory $timeFactory + ITimeFactory $timeFactory, ) { parent::__construct($connection, $timeFactory); $this->db = $connection; diff --git a/apps/testing/lib/Migration/Version30000Date20240102030405.php b/apps/testing/lib/Migration/Version30000Date20240102030405.php new file mode 100644 index 00000000000..e7b6bdcd618 --- /dev/null +++ b/apps/testing/lib/Migration/Version30000Date20240102030405.php @@ -0,0 +1,41 @@ +<?php + +declare(strict_types=1); +/** + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace OCA\Testing\Migration; + +use Closure; +use OCP\Migration\Attributes\AddColumn; +use OCP\Migration\Attributes\AddIndex; +use OCP\Migration\Attributes\ColumnType; +use OCP\Migration\Attributes\CreateTable; +use OCP\Migration\Attributes\DropColumn; +use OCP\Migration\Attributes\DropIndex; +use OCP\Migration\Attributes\DropTable; +use OCP\Migration\Attributes\IndexType; +use OCP\Migration\Attributes\ModifyColumn; +use OCP\Migration\IOutput; +use OCP\Migration\SimpleMigrationStep; + +#[DropTable(table: 'old_table')] +#[CreateTable(table: 'new_table', description: 'Table is used to store things, but also to get more things', notes: ['this is a notice', 'and another one, if really needed'])] +#[AddColumn(table: 'my_table')] +#[AddColumn(table: 'my_table', name: 'another_field')] +#[AddColumn(table: 'other_table', name: 'last_one', type: ColumnType::DATE)] +#[AddIndex(table: 'my_table')] +#[AddIndex(table: 'my_table', type: IndexType::PRIMARY)] +#[DropColumn(table: 'other_table')] +#[DropColumn(table: 'other_table', name: 'old_column', description: 'field is not used anymore and replaced by \'last_one\'')] +#[DropIndex(table: 'other_table')] +#[ModifyColumn(table: 'other_table')] +#[ModifyColumn(table: 'other_table', name: 'this_field')] +#[ModifyColumn(table: 'other_table', name: 'this_field', type: ColumnType::BIGINT)] +class Version30000Date20240102030405 extends SimpleMigrationStep { + public function changeSchema(IOutput $output, Closure $schemaClosure, array $options) { + return null; + } +} diff --git a/apps/testing/lib/Provider/FakeText2ImageProvider.php b/apps/testing/lib/Provider/FakeText2ImageProvider.php new file mode 100644 index 00000000000..6b607f23347 --- /dev/null +++ b/apps/testing/lib/Provider/FakeText2ImageProvider.php @@ -0,0 +1,33 @@ +<?php + +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace OCA\Testing\Provider; + +use OCP\TextToImage\IProvider; + +class FakeText2ImageProvider implements IProvider { + + public function getName(): string { + return 'Fake Text2Image provider'; + } + + public function generate(string $prompt, array $resources): void { + foreach ($resources as $resource) { + $read = fopen(__DIR__ . '/../../img/logo.png', 'r'); + stream_copy_to_stream($read, $resource); + fclose($read); + } + } + + public function getExpectedRuntime(): int { + return 1; + } + + public function getId(): string { + return 'testing-fake-text2image-provider'; + } +} diff --git a/apps/testing/lib/Provider/FakeTextProcessingProvider.php b/apps/testing/lib/Provider/FakeTextProcessingProvider.php new file mode 100644 index 00000000000..d3b16c55c67 --- /dev/null +++ b/apps/testing/lib/Provider/FakeTextProcessingProvider.php @@ -0,0 +1,39 @@ +<?php + +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace OCA\Testing\Provider; + +use OCP\TextProcessing\FreePromptTaskType; +use OCP\TextProcessing\IProvider; +use OCP\TextProcessing\ITaskType; + +/** @template-implements IProvider<FreePromptTaskType|ITaskType> */ +class FakeTextProcessingProvider implements IProvider { + + public function getName(): string { + return 'Fake text processing provider (asynchronous)'; + } + + public function process(string $prompt): string { + return $this->mb_strrev($prompt) . ' (done with FakeTextProcessingProvider)'; + } + + public function getTaskType(): string { + return FreePromptTaskType::class; + } + + /** + * Reverse a miltibyte string. + * + * @param string $string The string to be reversed. + * @return string The reversed string + */ + private function mb_strrev(string $string): string { + $chars = mb_str_split($string, 1); + return implode('', array_reverse($chars)); + } +} diff --git a/apps/testing/lib/Provider/FakeTextProcessingProviderSync.php b/apps/testing/lib/Provider/FakeTextProcessingProviderSync.php new file mode 100644 index 00000000000..ea822199109 --- /dev/null +++ b/apps/testing/lib/Provider/FakeTextProcessingProviderSync.php @@ -0,0 +1,45 @@ +<?php + +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace OCA\Testing\Provider; + +use OCP\TextProcessing\FreePromptTaskType; +use OCP\TextProcessing\IProviderWithExpectedRuntime; +use OCP\TextProcessing\ITaskType; + +/** + * @template-implements IProviderWithExpectedRuntime<FreePromptTaskType|ITaskType> + */ +class FakeTextProcessingProviderSync implements IProviderWithExpectedRuntime { + + public function getName(): string { + return 'Fake text processing provider (synchronous)'; + } + + public function process(string $prompt): string { + return $this->mb_strrev($prompt) . ' (done with FakeTextProcessingProviderSync)'; + } + + public function getTaskType(): string { + return FreePromptTaskType::class; + } + + public function getExpectedRuntime(): int { + return 1; + } + + /** + * Reverse a miltibyte string. + * + * @param string $string The string to be reversed. + * @return string The reversed string + */ + private function mb_strrev(string $string): string { + $chars = mb_str_split($string, 1); + return implode('', array_reverse($chars)); + } +} diff --git a/apps/testing/lib/Provider/FakeTranslationProvider.php b/apps/testing/lib/Provider/FakeTranslationProvider.php new file mode 100644 index 00000000000..cc2d13db646 --- /dev/null +++ b/apps/testing/lib/Provider/FakeTranslationProvider.php @@ -0,0 +1,29 @@ +<?php + +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace OCA\Testing\Provider; + +use OCP\Translation\ITranslationProvider; +use OCP\Translation\LanguageTuple; + +class FakeTranslationProvider implements ITranslationProvider { + + public function getName(): string { + return 'Fake translation'; + } + + public function getAvailableLanguages(): array { + return [ + new LanguageTuple('de', 'German', 'en', 'English'), + new LanguageTuple('en', 'English', 'de', 'German'), + ]; + } + + public function translate(?string $fromLanguage, string $toLanguage, string $text): string { + return strrev($text); + } +} diff --git a/apps/testing/lib/Settings/DeclarativeSettingsForm.php b/apps/testing/lib/Settings/DeclarativeSettingsForm.php new file mode 100644 index 00000000000..55e44cbcbea --- /dev/null +++ b/apps/testing/lib/Settings/DeclarativeSettingsForm.php @@ -0,0 +1,205 @@ +<?php + +declare(strict_types=1); +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OCA\Testing\Settings; + +use OCP\Settings\DeclarativeSettingsTypes; +use OCP\Settings\IDeclarativeSettingsForm; + +class DeclarativeSettingsForm implements IDeclarativeSettingsForm { + public function getSchema(): array { + return [ + 'id' => 'test_declarative_form', + 'priority' => 10, + 'section_type' => DeclarativeSettingsTypes::SECTION_TYPE_ADMIN, // admin, personal + 'section_id' => 'additional', + 'storage_type' => DeclarativeSettingsTypes::STORAGE_TYPE_INTERNAL, // external, internal (handled by core to store in appconfig and preferences) + 'title' => 'Test declarative settings class', // NcSettingsSection name + 'description' => 'This form is registered with a DeclarativeSettingsForm class', // NcSettingsSection description + 'doc_url' => '', // NcSettingsSection doc_url for documentation or help page, empty string if not needed + 'fields' => [ + [ + 'id' => 'test_ex_app_field_7', // configkey + 'title' => 'Multi-selection', // name or label + 'description' => 'Select some option setting', // hint + 'type' => DeclarativeSettingsTypes::MULTI_SELECT, // select, radio, multi-select + 'options' => ['foo', 'bar', 'baz'], // simple options for select, radio, multi-select + 'placeholder' => 'Select some multiple options', // input placeholder + 'default' => ['foo', 'bar'], + ], + [ + 'id' => 'some_real_setting', + 'title' => 'Choose init status check background job interval', + 'description' => 'How often AppAPI should check for initialization status', + 'type' => DeclarativeSettingsTypes::RADIO, // radio (NcCheckboxRadioSwitch type radio) + 'placeholder' => 'Choose init status check background job interval', + 'default' => '40m', + 'options' => [ + [ + 'name' => 'Each 40 minutes', // NcCheckboxRadioSwitch display name + 'value' => '40m' // NcCheckboxRadioSwitch value + ], + [ + 'name' => 'Each 60 minutes', + 'value' => '60m' + ], + [ + 'name' => 'Each 120 minutes', + 'value' => '120m' + ], + [ + 'name' => 'Each day', + 'value' => 60 * 24 . 'm' + ], + ], + ], + [ + 'id' => 'test_ex_app_field_1', // configkey + 'title' => 'Default text field', // label + 'description' => 'Set some simple text setting', // hint + 'type' => DeclarativeSettingsTypes::TEXT, // text, password, email, tel, url, number + 'placeholder' => 'Enter text setting', // placeholder + 'default' => 'foo', + ], + [ + 'id' => 'test_ex_app_field_1_1', + 'title' => 'Email field', + 'description' => 'Set email config', + 'type' => DeclarativeSettingsTypes::EMAIL, + 'placeholder' => 'Enter email', + 'default' => '', + ], + [ + 'id' => 'test_ex_app_field_1_2', + 'title' => 'Tel field', + 'description' => 'Set tel config', + 'type' => DeclarativeSettingsTypes::TEL, + 'placeholder' => 'Enter your tel', + 'default' => '', + ], + [ + 'id' => 'test_ex_app_field_1_3', + 'title' => 'Url (website) field', + 'description' => 'Set url config', + 'type' => 'url', + 'placeholder' => 'Enter url', + 'default' => '', + ], + [ + 'id' => 'test_ex_app_field_1_4', + 'title' => 'Number field', + 'description' => 'Set number config', + 'type' => DeclarativeSettingsTypes::NUMBER, + 'placeholder' => 'Enter number value', + 'default' => 0, + ], + [ + 'id' => 'test_ex_app_field_2', + 'title' => 'Password', + 'description' => 'Set some secure value setting', + 'type' => 'password', + 'placeholder' => 'Set secure value', + 'default' => '', + ], + [ + 'id' => 'test_ex_app_field_3', + 'title' => 'Selection', + 'description' => 'Select some option setting', + 'type' => DeclarativeSettingsTypes::SELECT, // select, radio, multi-select + 'options' => ['foo', 'bar', 'baz'], + 'placeholder' => 'Select some option setting', + 'default' => 'foo', + ], + [ + 'id' => 'test_ex_app_field_4', + 'title' => 'Toggle something', + 'description' => 'Select checkbox option setting', + 'type' => DeclarativeSettingsTypes::CHECKBOX, // checkbox, multiple-checkbox + 'label' => 'Verify something if enabled', + 'default' => false, + ], + [ + 'id' => 'test_ex_app_field_5', + 'title' => 'Multiple checkbox toggles, describing one setting, checked options are saved as an JSON object {foo: true, bar: false}', + 'description' => 'Select checkbox option setting', + 'type' => DeclarativeSettingsTypes::MULTI_CHECKBOX, // checkbox, multi-checkbox + 'default' => ['foo' => true, 'bar' => true, 'baz' => true], + 'options' => [ + [ + 'name' => 'Foo', + 'value' => 'foo', // multiple-checkbox configkey + ], + [ + 'name' => 'Bar', + 'value' => 'bar', + ], + [ + 'name' => 'Baz', + 'value' => 'baz', + ], + [ + 'name' => 'Qux', + 'value' => 'qux', + ], + ], + ], + [ + 'id' => 'test_ex_app_field_6', + 'title' => 'Radio toggles, describing one setting like single select', + 'description' => 'Select radio option setting', + 'type' => DeclarativeSettingsTypes::RADIO, // radio (NcCheckboxRadioSwitch type radio) + 'label' => 'Select single toggle', + 'default' => 'foo', + 'options' => [ + [ + 'name' => 'First radio', // NcCheckboxRadioSwitch display name + 'value' => 'foo' // NcCheckboxRadioSwitch value + ], + [ + 'name' => 'Second radio', + 'value' => 'bar' + ], + [ + 'name' => 'Third radio', + 'value' => 'baz' + ], + ], + ], + [ + 'id' => 'test_sensitive_field', + 'title' => 'Sensitive text field', + 'description' => 'Set some secure value setting that is stored encrypted', + 'type' => DeclarativeSettingsTypes::TEXT, + 'label' => 'Sensitive field', + 'placeholder' => 'Set secure value', + 'default' => '', + 'sensitive' => true, // only for TEXT, PASSWORD types + ], + [ + 'id' => 'test_sensitive_field_2', + 'title' => 'Sensitive password field', + 'description' => 'Set some password setting that is stored encrypted', + 'type' => DeclarativeSettingsTypes::PASSWORD, + 'label' => 'Sensitive field', + 'placeholder' => 'Set secure value', + 'default' => '', + 'sensitive' => true, // only for TEXT, PASSWORD types + ], + [ + 'id' => 'test_non_sensitive_field', + 'title' => 'Password field', + 'description' => 'Set some password setting', + 'type' => DeclarativeSettingsTypes::PASSWORD, + 'label' => 'Password field', + 'placeholder' => 'Set secure value', + 'default' => '', + 'sensitive' => false, + ], + ], + ]; + } +} diff --git a/apps/testing/lib/TaskProcessing/FakeContextWriteProvider.php b/apps/testing/lib/TaskProcessing/FakeContextWriteProvider.php new file mode 100644 index 00000000000..9a5574f5147 --- /dev/null +++ b/apps/testing/lib/TaskProcessing/FakeContextWriteProvider.php @@ -0,0 +1,130 @@ +<?php + +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +declare(strict_types=1); + +namespace OCA\Testing\TaskProcessing; + +use OCA\Testing\AppInfo\Application; +use OCP\AppFramework\Services\IAppConfig; +use OCP\TaskProcessing\EShapeType; +use OCP\TaskProcessing\Exception\ProcessingException; +use OCP\TaskProcessing\ISynchronousProvider; +use OCP\TaskProcessing\ShapeDescriptor; +use OCP\TaskProcessing\ShapeEnumValue; +use OCP\TaskProcessing\TaskTypes\ContextWrite; +use RuntimeException; + +class FakeContextWriteProvider implements ISynchronousProvider { + + public function __construct( + protected IAppConfig $appConfig, + ) { + } + + public function getId(): string { + return Application::APP_ID . '-contextwrite'; + } + + public function getName(): string { + return 'Fake context write task processing provider'; + } + + public function getTaskTypeId(): string { + return ContextWrite::ID; + } + + public function getExpectedRuntime(): int { + return 1; + } + + public function getInputShapeEnumValues(): array { + return []; + } + + public function getInputShapeDefaults(): array { + return []; + } + + public function getOptionalInputShape(): array { + return [ + 'max_tokens' => new ShapeDescriptor( + 'Maximum output words', + 'The maximum number of words/tokens that can be generated in the completion.', + EShapeType::Number + ), + 'model' => new ShapeDescriptor( + 'Model', + 'The model used to generate the completion', + EShapeType::Enum + ), + ]; + } + + public function getOptionalInputShapeEnumValues(): array { + return [ + 'model' => [ + new ShapeEnumValue('Model 1', 'model_1'), + new ShapeEnumValue('Model 2', 'model_2'), + new ShapeEnumValue('Model 3', 'model_3'), + ], + ]; + } + + public function getOptionalInputShapeDefaults(): array { + return [ + 'max_tokens' => 4321, + 'model' => 'model_2', + ]; + } + + public function getOutputShapeEnumValues(): array { + return []; + } + + public function getOptionalOutputShape(): array { + return []; + } + + public function getOptionalOutputShapeEnumValues(): array { + return []; + } + + public function process(?string $userId, array $input, callable $reportProgress): array { + if ($this->appConfig->getAppValueBool('fail-' . $this->getId())) { + throw new ProcessingException('Failing as set by AppConfig'); + } + + if ( + !isset($input['style_input']) || !is_string($input['style_input']) + || !isset($input['source_input']) || !is_string($input['source_input']) + ) { + throw new RuntimeException('Invalid inputs'); + } + $writingStyle = $input['style_input']; + $sourceMaterial = $input['source_input']; + + if (isset($input['model']) && is_string($input['model'])) { + $model = $input['model']; + } else { + $model = 'unknown model'; + } + + $maxTokens = null; + if (isset($input['max_tokens']) && is_int($input['max_tokens'])) { + $maxTokens = $input['max_tokens']; + } + + $fakeResult = 'This is a fake result: ' + . "\n\n- Style input: " . $writingStyle + . "\n- Source input: " . $sourceMaterial + . "\n- Model: " . $model + . "\n- Maximum number of words: " . $maxTokens; + + return ['output' => $fakeResult]; + } +} diff --git a/apps/testing/lib/TaskProcessing/FakeTextToImageProvider.php b/apps/testing/lib/TaskProcessing/FakeTextToImageProvider.php new file mode 100644 index 00000000000..455d6c0b518 --- /dev/null +++ b/apps/testing/lib/TaskProcessing/FakeTextToImageProvider.php @@ -0,0 +1,108 @@ +<?php + +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +declare(strict_types=1); + +namespace OCA\Testing\TaskProcessing; + +use OCA\Testing\AppInfo\Application; +use OCP\AppFramework\Services\IAppConfig; +use OCP\TaskProcessing\EShapeType; +use OCP\TaskProcessing\Exception\ProcessingException; +use OCP\TaskProcessing\ISynchronousProvider; +use OCP\TaskProcessing\ShapeDescriptor; +use OCP\TaskProcessing\TaskTypes\TextToImage; +use RuntimeException; + +class FakeTextToImageProvider implements ISynchronousProvider { + + public function __construct( + protected IAppConfig $appConfig, + ) { + } + + public function getId(): string { + return Application::APP_ID . '-text2image'; + } + + public function getName(): string { + return 'Fake text2image task processing provider'; + } + + public function getTaskTypeId(): string { + return TextToImage::ID; + } + + public function getExpectedRuntime(): int { + return 1; + } + + public function getInputShapeEnumValues(): array { + return []; + } + + public function getInputShapeDefaults(): array { + return [ + 'numberOfImages' => 1, + ]; + } + + public function getOptionalInputShape(): array { + return [ + 'size' => new ShapeDescriptor( + 'Size', + 'Optional. The size of the generated images. Must be in 256x256 format.', + EShapeType::Text + ), + ]; + } + + public function getOptionalInputShapeEnumValues(): array { + return []; + } + + public function getOptionalInputShapeDefaults(): array { + return []; + } + + public function getOutputShapeEnumValues(): array { + return []; + } + + public function getOptionalOutputShape(): array { + return []; + } + + public function getOptionalOutputShapeEnumValues(): array { + return []; + } + + public function process(?string $userId, array $input, callable $reportProgress): array { + if ($this->appConfig->getAppValueBool('fail-' . $this->getId())) { + throw new ProcessingException('Failing as set by AppConfig'); + } + + if (!isset($input['input']) || !is_string($input['input'])) { + throw new RuntimeException('Invalid prompt'); + } + $prompt = $input['input']; + + $nbImages = 1; + if (isset($input['numberOfImages']) && is_int($input['numberOfImages'])) { + $nbImages = $input['numberOfImages']; + } + + $fakeContent = file_get_contents(__DIR__ . '/../../img/logo.png'); + + $output = ['images' => []]; + foreach (range(1, $nbImages) as $i) { + $output['images'][] = $fakeContent; + } + /** @var array<string, list<numeric|string>|numeric|string> $output */ + return $output; + } +} diff --git a/apps/testing/lib/TaskProcessing/FakeTextToTextProvider.php b/apps/testing/lib/TaskProcessing/FakeTextToTextProvider.php new file mode 100644 index 00000000000..5012823024e --- /dev/null +++ b/apps/testing/lib/TaskProcessing/FakeTextToTextProvider.php @@ -0,0 +1,122 @@ +<?php + +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +declare(strict_types=1); + +namespace OCA\Testing\TaskProcessing; + +use OCA\Testing\AppInfo\Application; +use OCP\AppFramework\Services\IAppConfig; +use OCP\TaskProcessing\EShapeType; +use OCP\TaskProcessing\Exception\ProcessingException; +use OCP\TaskProcessing\ISynchronousProvider; +use OCP\TaskProcessing\ShapeDescriptor; +use OCP\TaskProcessing\ShapeEnumValue; +use OCP\TaskProcessing\TaskTypes\TextToText; +use RuntimeException; + +class FakeTextToTextProvider implements ISynchronousProvider { + + public function __construct( + protected IAppConfig $appConfig, + ) { + } + + public function getId(): string { + return Application::APP_ID . '-text2text'; + } + + public function getName(): string { + return 'Fake text2text task processing provider'; + } + + public function getTaskTypeId(): string { + return TextToText::ID; + } + + public function getExpectedRuntime(): int { + return 1; + } + + public function getInputShapeEnumValues(): array { + return []; + } + + public function getInputShapeDefaults(): array { + return []; + } + + public function getOptionalInputShape(): array { + return [ + 'max_tokens' => new ShapeDescriptor( + 'Maximum output words', + 'The maximum number of words/tokens that can be generated in the completion.', + EShapeType::Number + ), + 'model' => new ShapeDescriptor( + 'Model', + 'The model used to generate the completion', + EShapeType::Enum + ), + ]; + } + + public function getOptionalInputShapeEnumValues(): array { + return [ + 'model' => [ + new ShapeEnumValue('Model 1', 'model_1'), + new ShapeEnumValue('Model 2', 'model_2'), + new ShapeEnumValue('Model 3', 'model_3'), + ], + ]; + } + + public function getOptionalInputShapeDefaults(): array { + return [ + 'max_tokens' => 1234, + 'model' => 'model_2', + ]; + } + + public function getOptionalOutputShape(): array { + return []; + } + + public function getOutputShapeEnumValues(): array { + return []; + } + + public function getOptionalOutputShapeEnumValues(): array { + return []; + } + + public function process(?string $userId, array $input, callable $reportProgress): array { + if ($this->appConfig->getAppValueBool('fail-' . $this->getId())) { + throw new ProcessingException('Failing as set by AppConfig'); + } + + if (isset($input['model']) && is_string($input['model'])) { + $model = $input['model']; + } else { + $model = 'unknown model'; + } + + if (!isset($input['input']) || !is_string($input['input'])) { + throw new RuntimeException('Invalid prompt'); + } + $prompt = $input['input']; + + $maxTokens = null; + if (isset($input['max_tokens']) && is_int($input['max_tokens'])) { + $maxTokens = $input['max_tokens']; + } + + return [ + 'output' => 'This is a fake result: ' . "\n\n- Prompt: " . $prompt . "\n- Model: " . $model . "\n- Maximum number of words: " . $maxTokens, + ]; + } +} diff --git a/apps/testing/lib/TaskProcessing/FakeTextToTextSummaryProvider.php b/apps/testing/lib/TaskProcessing/FakeTextToTextSummaryProvider.php new file mode 100644 index 00000000000..58816d8a0df --- /dev/null +++ b/apps/testing/lib/TaskProcessing/FakeTextToTextSummaryProvider.php @@ -0,0 +1,122 @@ +<?php + +declare(strict_types=1); +/** + * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + + +namespace OCA\Testing\TaskProcessing; + +use OCA\Testing\AppInfo\Application; +use OCP\AppFramework\Services\IAppConfig; +use OCP\TaskProcessing\EShapeType; +use OCP\TaskProcessing\Exception\ProcessingException; +use OCP\TaskProcessing\ISynchronousProvider; +use OCP\TaskProcessing\ShapeDescriptor; +use OCP\TaskProcessing\ShapeEnumValue; +use OCP\TaskProcessing\TaskTypes\TextToTextSummary; +use RuntimeException; + +class FakeTextToTextSummaryProvider implements ISynchronousProvider { + + public function __construct( + protected IAppConfig $appConfig, + ) { + } + + public function getId(): string { + return Application::APP_ID . '-text2text-summary'; + } + + public function getName(): string { + return 'Fake text2text summary task processing provider'; + } + + public function getTaskTypeId(): string { + return TextToTextSummary::ID; + } + + public function getExpectedRuntime(): int { + return 1; + } + + public function getInputShapeEnumValues(): array { + return []; + } + + public function getInputShapeDefaults(): array { + return []; + } + + public function getOptionalInputShape(): array { + return [ + 'max_tokens' => new ShapeDescriptor( + 'Maximum output words', + 'The maximum number of words/tokens that can be generated in the completion.', + EShapeType::Number + ), + 'model' => new ShapeDescriptor( + 'Model', + 'The model used to generate the completion', + EShapeType::Enum + ), + ]; + } + + public function getOptionalInputShapeEnumValues(): array { + return [ + 'model' => [ + new ShapeEnumValue('Model 1', 'model_1'), + new ShapeEnumValue('Model 2', 'model_2'), + new ShapeEnumValue('Model 3', 'model_3'), + ], + ]; + } + + public function getOptionalInputShapeDefaults(): array { + return [ + 'max_tokens' => 1234, + 'model' => 'model_2', + ]; + } + + public function getOptionalOutputShape(): array { + return []; + } + + public function getOutputShapeEnumValues(): array { + return []; + } + + public function getOptionalOutputShapeEnumValues(): array { + return []; + } + + public function process(?string $userId, array $input, callable $reportProgress): array { + if ($this->appConfig->getAppValueBool('fail-' . $this->getId())) { + throw new ProcessingException('Failing as set by AppConfig'); + } + + if (isset($input['model']) && is_string($input['model'])) { + $model = $input['model']; + } else { + $model = 'unknown model'; + } + + if (!isset($input['input']) || !is_string($input['input'])) { + throw new RuntimeException('Invalid prompt'); + } + $prompt = $input['input']; + + $maxTokens = null; + if (isset($input['max_tokens']) && is_int($input['max_tokens'])) { + $maxTokens = $input['max_tokens']; + } + + return [ + 'output' => 'This is a fake summary: ',// . "\n\n- Prompt: " . $prompt . "\n- Model: " . $model . "\n- Maximum number of words: " . $maxTokens, + ]; + } +} diff --git a/apps/testing/lib/TaskProcessing/FakeTranscribeProvider.php b/apps/testing/lib/TaskProcessing/FakeTranscribeProvider.php new file mode 100644 index 00000000000..4827a07037a --- /dev/null +++ b/apps/testing/lib/TaskProcessing/FakeTranscribeProvider.php @@ -0,0 +1,88 @@ +<?php + +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +declare(strict_types=1); + +namespace OCA\Testing\TaskProcessing; + +use OCA\Testing\AppInfo\Application; +use OCP\AppFramework\Services\IAppConfig; +use OCP\Files\File; +use OCP\TaskProcessing\Exception\ProcessingException; +use OCP\TaskProcessing\ISynchronousProvider; +use OCP\TaskProcessing\TaskTypes\AudioToText; +use RuntimeException; + +class FakeTranscribeProvider implements ISynchronousProvider { + + public function __construct( + protected IAppConfig $appConfig, + ) { + } + + public function getId(): string { + return Application::APP_ID . '-audio2text'; + } + + public function getName(): string { + return 'Fake audio2text task processing provider'; + } + + public function getTaskTypeId(): string { + return AudioToText::ID; + } + + public function getExpectedRuntime(): int { + return 1; + } + + public function getInputShapeEnumValues(): array { + return []; + } + + public function getInputShapeDefaults(): array { + return []; + } + + public function getOptionalInputShape(): array { + return []; + } + + public function getOptionalInputShapeEnumValues(): array { + return []; + } + + public function getOptionalInputShapeDefaults(): array { + return []; + } + + public function getOutputShapeEnumValues(): array { + return []; + } + + public function getOptionalOutputShape(): array { + return []; + } + + public function getOptionalOutputShapeEnumValues(): array { + return []; + } + + public function process(?string $userId, array $input, callable $reportProgress): array { + if (!isset($input['input']) || !$input['input'] instanceof File || !$input['input']->isReadable()) { + throw new RuntimeException('Invalid input file'); + } + if ($this->appConfig->getAppValueBool('fail-' . $this->getId())) { + throw new ProcessingException('Failing as set by AppConfig'); + } + + $inputFile = $input['input']; + $transcription = 'Fake transcription result'; + + return ['output' => $transcription]; + } +} diff --git a/apps/testing/lib/TaskProcessing/FakeTranslateProvider.php b/apps/testing/lib/TaskProcessing/FakeTranslateProvider.php new file mode 100644 index 00000000000..22be8e83049 --- /dev/null +++ b/apps/testing/lib/TaskProcessing/FakeTranslateProvider.php @@ -0,0 +1,154 @@ +<?php + +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +declare(strict_types=1); + +namespace OCA\Testing\TaskProcessing; + +use OCA\Testing\AppInfo\Application; +use OCP\AppFramework\Services\IAppConfig; +use OCP\L10N\IFactory; +use OCP\TaskProcessing\EShapeType; +use OCP\TaskProcessing\Exception\ProcessingException; +use OCP\TaskProcessing\ISynchronousProvider; +use OCP\TaskProcessing\ShapeDescriptor; +use OCP\TaskProcessing\ShapeEnumValue; +use OCP\TaskProcessing\TaskTypes\TextToTextTranslate; +use RuntimeException; + +class FakeTranslateProvider implements ISynchronousProvider { + + public function __construct( + private IFactory $l10nFactory, + protected IAppConfig $appConfig, + ) { + } + + public function getId(): string { + return Application::APP_ID . '-translate'; + } + + public function getName(): string { + return 'Fake translate task processing provider'; + } + + public function getTaskTypeId(): string { + return TextToTextTranslate::ID; + } + + public function getExpectedRuntime(): int { + return 1; + } + + public function getInputShapeEnumValues(): array { + $coreL = $this->l10nFactory->getLanguages(); + $languages = array_merge($coreL['commonLanguages'], $coreL['otherLanguages']); + $languageEnumValues = array_map(static function (array $language) { + return new ShapeEnumValue($language['name'], $language['code']); + }, $languages); + $detectLanguageEnumValue = new ShapeEnumValue('Detect language', 'detect_language'); + return [ + 'origin_language' => array_merge([$detectLanguageEnumValue], $languageEnumValues), + 'target_language' => $languageEnumValues, + ]; + } + + public function getInputShapeDefaults(): array { + return [ + 'origin_language' => 'detect_language', + ]; + } + + public function getOptionalInputShape(): array { + return [ + 'max_tokens' => new ShapeDescriptor( + 'Maximum output words', + 'The maximum number of words/tokens that can be generated in the completion.', + EShapeType::Number + ), + 'model' => new ShapeDescriptor( + 'Model', + 'The model used to generate the completion', + EShapeType::Enum + ), + ]; + } + + public function getOptionalInputShapeEnumValues(): array { + return [ + 'model' => [ + new ShapeEnumValue('Model 1', 'model_1'), + new ShapeEnumValue('Model 2', 'model_2'), + new ShapeEnumValue('Model 3', 'model_3'), + ], + ]; + } + + public function getOptionalInputShapeDefaults(): array { + return [ + 'max_tokens' => 200, + 'model' => 'model_3', + ]; + } + + public function getOptionalOutputShape(): array { + return []; + } + + public function getOutputShapeEnumValues(): array { + return []; + } + + public function getOptionalOutputShapeEnumValues(): array { + return []; + } + + private function getCoreLanguagesByCode(): array { + $coreL = $this->l10nFactory->getLanguages(); + $coreLanguages = array_reduce(array_merge($coreL['commonLanguages'], $coreL['otherLanguages']), function ($carry, $val) { + $carry[$val['code']] = $val['name']; + return $carry; + }); + return $coreLanguages; + } + + public function process(?string $userId, array $input, callable $reportProgress): array { + if ($this->appConfig->getAppValueBool('fail-' . $this->getId())) { + throw new ProcessingException('Failing as set by AppConfig'); + } + + if (isset($input['model']) && is_string($input['model'])) { + $model = $input['model']; + } else { + $model = 'model_3'; + } + + if (!isset($input['input']) || !is_string($input['input'])) { + throw new RuntimeException('Invalid input text'); + } + $inputText = $input['input']; + + $maxTokens = null; + if (isset($input['max_tokens']) && is_int($input['max_tokens'])) { + $maxTokens = $input['max_tokens']; + } + + $coreLanguages = $this->getCoreLanguagesByCode(); + + $toLanguage = $coreLanguages[$input['target_language']] ?? $input['target_language']; + if ($input['origin_language'] !== 'detect_language') { + $fromLanguage = $coreLanguages[$input['origin_language']] ?? $input['origin_language']; + $prompt = 'Fake translation from ' . $fromLanguage . ' to ' . $toLanguage . ': ' . $inputText; + } else { + $prompt = 'Fake Translation to ' . $toLanguage . ': ' . $inputText; + } + + $fakeResult = $prompt . "\n\nModel: " . $model . "\nMax tokens: " . $maxTokens; + + return ['output' => $fakeResult]; + } +} |