diff --git a/docs/implement-import.md b/docs/implement-import.md index 01066ca6b..08f944e29 100644 --- a/docs/implement-import.md +++ b/docs/implement-import.md @@ -1,4 +1,32 @@ ## Implement import -* Create a new class `lib/Service/Importer/Systems/Service.php` where `` is the name of the source system. +* Create a new importer class extending `ABoardImportService` +* Create a listener for event `BoardImportGetAllowedEvent` to enable your importer. + > You can read more about listeners on [Nextcloud](https://docs.nextcloud.com/server/latest/developer_manual/basics/events.html?highlight=event#writing-a-listener) doc. + + Example: + +```php +class YourCustomImporterListener { + public function handle(Event $event): void { + if (!($event instanceof BoardImportGetAllowedEvent)) { + return; + } + + $event->getService()->addAllowedImportSystem([ + 'name' => YourCustomImporterService::$name, + 'class' => YourCustomImporterService::class, + 'internalName' => 'YourCustomImporter' + ]); + } +} +``` + * Register your listener on your `Application` class like this: +```php +$dispatcher = $this->getContainer()->query(IEventDispatcher::class); +$dispatcher->registerEventListener( + BoardImportGetAllowedEvent::class, + YourCustomImporterListener::class +); +``` * Use the `lib/Service/Importer/Systems/TrelloJsonService.php` class as inspiration \ No newline at end of file diff --git a/lib/Event/ABoardImportGetAllowedEvent.php b/lib/Event/ABoardImportGetAllowedEvent.php new file mode 100644 index 000000000..9dc4287ee --- /dev/null +++ b/lib/Event/ABoardImportGetAllowedEvent.php @@ -0,0 +1,44 @@ + + * + * @author Vitor Mattos + * + * @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 . + * + */ + +declare(strict_types=1); + + +namespace OCA\Deck\Event; + +use OCA\Deck\Service\Importer\BoardImportService; +use OCP\EventDispatcher\Event; + +abstract class ABoardImportGetAllowedEvent extends Event { + private $service; + + public function __construct(BoardImportService $service) { + parent::__construct(); + + $this->service = $service; + } + + public function getService(): BoardImportService { + return $this->service; + } +} diff --git a/lib/Event/BoardImportGetAllowedEvent.php b/lib/Event/BoardImportGetAllowedEvent.php new file mode 100644 index 000000000..323bfc87e --- /dev/null +++ b/lib/Event/BoardImportGetAllowedEvent.php @@ -0,0 +1,29 @@ + + * + * @author Vitor Mattos + * + * @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 . + * + */ + +declare(strict_types=1); + +namespace OCA\Deck\Event; + +class BoardImportGetAllowedEvent extends ABoardImportGetAllowedEvent { +} diff --git a/lib/Service/Importer/ABoardImportService.php b/lib/Service/Importer/ABoardImportService.php index 68a65cbdc..2e6acb605 100644 --- a/lib/Service/Importer/ABoardImportService.php +++ b/lib/Service/Importer/ABoardImportService.php @@ -92,6 +92,8 @@ abstract class ABoardImportService { abstract public function validateUsers(): void; + abstract public function getJsonSchemaPath(): string; + public function updateStack(string $id, Stack $stack): void { $this->stacks[$id] = $stack; } diff --git a/lib/Service/Importer/BoardImportCommandService.php b/lib/Service/Importer/BoardImportCommandService.php index 8e25f417f..d45c784de 100644 --- a/lib/Service/Importer/BoardImportCommandService.php +++ b/lib/Service/Importer/BoardImportCommandService.php @@ -114,8 +114,7 @@ class BoardImportCommandService extends BoardImportService { return $v['message']; }, $e->getData())); $this->getOutput()->writeln('Valid schema:'); - $schemaPath = __DIR__ . '/fixtures/config-' . $this->getSystem() . '-schema.json'; - $this->getOutput()->writeln(print_r(file_get_contents($schemaPath), true)); + $this->getOutput()->writeln(print_r(file_get_contents($this->getJsonSchemaPath()), true)); $this->getInput()->setOption('config', ''); } $this->validateConfig(); diff --git a/lib/Service/Importer/BoardImportService.php b/lib/Service/Importer/BoardImportService.php index d6de6cd80..72b8b0c28 100644 --- a/lib/Service/Importer/BoardImportService.php +++ b/lib/Service/Importer/BoardImportService.php @@ -36,11 +36,16 @@ use OCA\Deck\Db\BoardMapper; use OCA\Deck\Db\CardMapper; use OCA\Deck\Db\LabelMapper; use OCA\Deck\Db\StackMapper; +use OCA\Deck\Event\BoardImportGetAllowedEvent; use OCA\Deck\Exceptions\ConflictException; use OCA\Deck\NotFoundException; +use OCA\Deck\Service\FileService; +use OCA\Deck\Service\Importer\Systems\TrelloApiService; +use OCA\Deck\Service\Importer\Systems\TrelloJsonService; use OCP\Comments\IComment; use OCP\Comments\ICommentsManager; use OCP\Comments\NotFoundException as CommentNotFoundException; +use OCP\EventDispatcher\IEventDispatcher; use OCP\IUserManager; class BoardImportService { @@ -62,6 +67,8 @@ class BoardImportService { private $attachmentMapper; /** @var ICommentsManager */ private $commentsManager; + /** @var IEventDispatcher */ + private $eventDispatcher; /** @var string */ private $system = ''; /** @var null|ABoardImportService */ @@ -96,7 +103,8 @@ class BoardImportService { AssignmentMapper $assignmentMapper, AttachmentMapper $attachmentMapper, CardMapper $cardMapper, - ICommentsManager $commentsManager + ICommentsManager $commentsManager, + IEventDispatcher $eventDispatcher ) { $this->userManager = $userManager; $this->boardMapper = $boardMapper; @@ -107,6 +115,7 @@ class BoardImportService { $this->assignmentMapper = $assignmentMapper; $this->attachmentMapper = $attachmentMapper; $this->commentsManager = $commentsManager; + $this->eventDispatcher = $eventDispatcher; $this->board = new Board(); $this->disableCommentsEvents(); } @@ -161,29 +170,25 @@ class BoardImportService { return $this->system; } + public function addAllowedImportSystem($system): self { + $this->allowedSystems[] = $system; + return $this; + } + public function getAllowedImportSystems(): array { if (!$this->allowedSystems) { - $allowedSystems = glob(__DIR__ . '/Systems/*Service.php'); - $allowedSystems = array_map(function ($filename) { - preg_match('/\/(?(?\w+)Service)\.php$/', $filename, $matches); - $className = 'OCA\Deck\Service\Importer\Systems\\'.$matches['class']; - if (!class_exists($className)) { - /** @psalm-suppress UnresolvableInclude */ - require_once $className; - } - /** @psalm-suppress InvalidPropertyFetch */ - $name = $className::$name; - if (empty($name)) { - $name = lcfirst($matches['system']); - } - return [ - 'name' => $name, - 'class' => $className, - 'internalName' => lcfirst($matches['system']) - ]; - }, $allowedSystems); - $this->allowedSystems = array_values($allowedSystems); + $this->addAllowedImportSystem([ + 'name' => TrelloApiService::$name, + 'class' => TrelloApiService::class, + 'internalName' => 'TrelloApi' + ]); + $this->addAllowedImportSystem([ + 'name' => TrelloJsonService::$name, + 'class' => TrelloJsonService::class, + 'internalName' => 'TrelloJson' + ]); } + $this->eventDispatcher->dispatchTyped(new BoardImportGetAllowedEvent($this)); return $this->allowedSystems; } @@ -192,7 +197,7 @@ class BoardImportService { throw new NotFoundException('System to import not found'); } if (!is_object($this->systemInstance)) { - $systemClass = 'OCA\\Deck\\Service\\BoardImport' . ucfirst($this->getSystem()) . 'Service'; + $systemClass = 'OCA\\Deck\\Service\\Importer\\Systems\\' . ucfirst($this->getSystem()) . 'Service'; $this->systemInstance = \OC::$server->get($systemClass); $this->systemInstance->setImportService($this); } @@ -421,7 +426,7 @@ class BoardImportService { } public function getJsonSchemaPath(): string { - return __DIR__ . '/fixtures/config-' . $this->getSystem() . '-schema.json'; + return $this->getImportSystem()->getJsonSchemaPath(); } public function validateOwner(): void { diff --git a/lib/Service/Importer/Systems/TrelloApiService.php b/lib/Service/Importer/Systems/TrelloApiService.php index e95afb6b7..d2466ae1b 100644 --- a/lib/Service/Importer/Systems/TrelloApiService.php +++ b/lib/Service/Importer/Systems/TrelloApiService.php @@ -66,6 +66,15 @@ class TrelloApiService extends TrelloJsonService { parent::bootstrap(); } + public function getJsonSchemaPath(): string { + return implode(DIRECTORY_SEPARATOR, [ + __DIR__, + '..', + 'fixtures', + 'config-trelloApi-schema.json', + ]); + } + private function populateActions(): void { $data = $this->getImportService()->getData(); $data->actions = $this->doRequest( diff --git a/lib/Service/Importer/Systems/TrelloJsonService.php b/lib/Service/Importer/Systems/TrelloJsonService.php index 49b02a71a..6aeebd0b0 100644 --- a/lib/Service/Importer/Systems/TrelloJsonService.php +++ b/lib/Service/Importer/Systems/TrelloJsonService.php @@ -65,6 +65,15 @@ class TrelloJsonService extends ABoardImportService { $this->validateUsers(); } + public function getJsonSchemaPath(): string { + return implode(DIRECTORY_SEPARATOR, [ + __DIR__, + '..', + 'fixtures', + 'config-trelloJson-schema.json', + ]); + } + public function validateUsers(): void { if (empty($this->getImportService()->getConfig('uidRelation'))) { return; diff --git a/tests/unit/Service/Importer/BoardImportServiceTest.php b/tests/unit/Service/Importer/BoardImportServiceTest.php index 787dfdd5f..9e4229529 100644 --- a/tests/unit/Service/Importer/BoardImportServiceTest.php +++ b/tests/unit/Service/Importer/BoardImportServiceTest.php @@ -35,8 +35,10 @@ use OCA\Deck\Db\Label; use OCA\Deck\Db\LabelMapper; use OCA\Deck\Db\Stack; use OCA\Deck\Db\StackMapper; +use OCA\Deck\Event\BoardImportGetAllowedEvent; use OCA\Deck\Service\Importer\Systems\TrelloJsonService; use OCP\Comments\ICommentsManager; +use OCP\EventDispatcher\IEventDispatcher; use OCP\IDBConnection; use OCP\IUser; use OCP\IUserManager; @@ -63,6 +65,8 @@ class BoardImportServiceTest extends \Test\TestCase { private $attachmentMapper; /** @var ICommentsManager|MockObject */ private $commentsManager; + /** @var IEventDispatcher|MockObject */ + private $eventDispatcher; /** @var TrelloJsonService|MockObject */ private $trelloJsonService; /** @var BoardImportService|MockObject */ @@ -77,6 +81,7 @@ class BoardImportServiceTest extends \Test\TestCase { $this->assignmentMapper = $this->createMock(AssignmentMapper::class); $this->attachmentMapper = $this->createMock(AttachmentMapper::class); $this->commentsManager = $this->createMock(ICommentsManager::class); + $this->eventDispatcher = $this->createMock(IEventDispatcher::class); $this->boardImportService = new BoardImportService( $this->userManager, $this->boardMapper, @@ -86,18 +91,33 @@ class BoardImportServiceTest extends \Test\TestCase { $this->assignmentMapper, $this->attachmentMapper, $this->cardMapper, - $this->commentsManager + $this->commentsManager, + $this->eventDispatcher ); $this->boardImportService->setSystem('trelloJson'); + $this->eventDispatcher + ->method('dispatchTyped') + ->willReturnCallback(function (BoardImportGetAllowedEvent $event) { + $event->getService()->addAllowedImportSystem([ + 'name' => TrelloJsonService::$name, + 'class' => TrelloJsonService::class, + 'internalName' => 'trelloJson' + ]); + }); + $data = json_decode(file_get_contents(__DIR__ . '/../../../data/data-trelloJson.json')); $this->boardImportService->setData($data); - $configInstance = json_decode(file_get_contents(__DIR__ . '/../../../data/config-trelloJson.json')); + $configFile = __DIR__ . '/../../../data/config-trelloJson.json'; + $configInstance = json_decode(file_get_contents($configFile)); $this->boardImportService->setConfigInstance($configInstance); $this->trelloJsonService = $this->createMock(TrelloJsonService::class); + $this->trelloJsonService + ->method('getJsonSchemaPath') + ->willReturn($configFile); $this->boardImportService->setImportSystem($this->trelloJsonService); $owner = $this->createMock(IUser::class);