From 4138953208df28ad3c0af7e76fb43b09a7f350df Mon Sep 17 00:00:00 2001 From: Vitor Mattos Date: Fri, 16 Jul 2021 00:44:45 -0300 Subject: [PATCH] Changes to make possible implement api endpoint Update documentation Start implementing getSystems route Code to route getSystems Controller to board import Change return Increase coverage Signed-off-by: Vitor Mattos --- appinfo/routes.php | 3 +- docs/API.md | 26 +++ lib/Command/BoardImport.php | 2 + lib/Controller/BoardImportApiController.php | 32 +++- lib/Service/ABoardImportService.php | 6 +- lib/Service/BoardImportCommandService.php | 83 +++++---- lib/Service/BoardImportService.php | 114 +++++++----- lib/Service/BoardImportTrelloService.php | 48 +++-- tests/unit/Command/BoardImportTest.php | 42 ++--- .../unit/Command/Helper/TrelloHelperTest.php | 92 ---------- tests/unit/Service/BoardImportServiceTest.php | 66 +++++-- .../Service/BoardImportTrelloServiceTest.php | 169 ++++++++++++++++++ tests/unit/Service/BoardServiceTest.php | 4 - .../unit/Service/TrelloImportServiceTest.php | 104 ----------- .../BoardImportApiControllerTest.php | 74 ++++++++ 15 files changed, 511 insertions(+), 354 deletions(-) delete mode 100644 tests/unit/Command/Helper/TrelloHelperTest.php create mode 100644 tests/unit/Service/BoardImportTrelloServiceTest.php delete mode 100644 tests/unit/Service/TrelloImportServiceTest.php create mode 100644 tests/unit/controller/BoardImportApiControllerTest.php diff --git a/appinfo/routes.php b/appinfo/routes.php index 63d98159f..55ffc4513 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -90,7 +90,8 @@ return [ ['name' => 'board_api#deleteAcl', 'url' => '/api/v{apiVersion}/boards/{boardId}/acl/{aclId}', 'verb' => 'DELETE'], ['name' => 'board_api#updateAcl', 'url' => '/api/v{apiVersion}/boards/{boardId}/acl/{aclId}', 'verb' => 'PUT'], - ['name' => 'board_import_api#import', 'url' => '/api/v{apiVersion}/boards/import','verb' => 'POST', 'requirements' => ['apiVersion' => '1.1']], + ['name' => 'board_import_api#getAllowedSystems', 'url' => '/api/v{apiVersion}/boards/import/getSystems','verb' => 'GET'], + ['name' => 'board_import_api#import', 'url' => '/api/v{apiVersion}/boards/import','verb' => 'POST'], ['name' => 'stack_api#index', 'url' => '/api/v{apiVersion}/boards/{boardId}/stacks', 'verb' => 'GET'], diff --git a/docs/API.md b/docs/API.md index 00cb8f987..def6499fc 100644 --- a/docs/API.md +++ b/docs/API.md @@ -988,6 +988,32 @@ For now only `deck_file` is supported as an attachment type. ##### 200 Success +### GET /boards/import/getSystems - Import a board + +#### Request parameters + +#### Response + +```json +[ + "trello" +] +``` + +### POST /boards/import - Import a board + +#### Request parameters + +| Parameter | Type | Description | +| ------------ | ------- | --------------------------------------------- | +| system | string | The allowed name of system to import from | +| config | Object | The config object (JSON) | +| data | Object | The data object to import (JSON) | + +#### Response + +##### 200 Success + # OCS API The following endpoints are available through the Nextcloud OCS endpoint, which is available at `/ocs/v2.php/apps/deck/api/v1.0/`. diff --git a/lib/Command/BoardImport.php b/lib/Command/BoardImport.php index 2d0cd0678..a05b35c7f 100644 --- a/lib/Command/BoardImport.php +++ b/lib/Command/BoardImport.php @@ -81,6 +81,8 @@ class BoardImport extends Command { $this->boardImportCommandService ->setInput($input) ->setOutput($output) + ->setSystem($input->getOption('system')) + ->setConfigInstance($input->getOption('config')) ->validate(); } diff --git a/lib/Controller/BoardImportApiController.php b/lib/Controller/BoardImportApiController.php index 3545a34ab..506b3f7b6 100644 --- a/lib/Controller/BoardImportApiController.php +++ b/lib/Controller/BoardImportApiController.php @@ -24,18 +24,26 @@ namespace OCA\Deck\Controller; use OCA\Deck\Service\BoardImportService; -use OCA\Files\Controller\ApiController; +use OCP\AppFramework\ApiController; use OCP\AppFramework\Http; use OCP\AppFramework\Http\DataResponse; +use OCP\IRequest; class BoardImportApiController extends ApiController { /** @var BoardImportService */ private $boardImportService; + /** @var string */ + private $userId; public function __construct( - BoardImportService $boardImportService + $appName, + IRequest $request, + BoardImportService $boardImportService, + $userId ) { + parent::__construct($appName, $request); $this->boardImportService = $boardImportService; + $this->userId = $userId; } /** @@ -44,7 +52,23 @@ class BoardImportApiController extends ApiController { * @NoCSRFRequired */ public function import($system, $config, $data) { - $board = $this->boardImportService->import($system, $config, $data); - return new DataResponse($board, Http::STATUS_OK); + $this->boardImportService->setSystem($system); + $config = json_decode(json_encode($config)); + $config->owner = $this->userId; + $this->boardImportService->setConfigInstance($config); + $this->boardImportService->setData(json_decode(json_encode($data))); + $this->boardImportService->validate(); + $this->boardImportService->import(); + return new DataResponse($this->boardImportService->getBoard(), Http::STATUS_OK); + } + + /** + * @NoAdminRequired + * @CORS + * @NoCSRFRequired + */ + public function getAllowedSystems() { + $allowedSystems = $this->boardImportService->getAllowedImportSystems(); + return new DataResponse($allowedSystems, Http::STATUS_OK); } } diff --git a/lib/Service/ABoardImportService.php b/lib/Service/ABoardImportService.php index d085e0e8f..98c654fd9 100644 --- a/lib/Service/ABoardImportService.php +++ b/lib/Service/ABoardImportService.php @@ -5,6 +5,7 @@ namespace OCA\Deck\Service; use OCA\Deck\Db\Acl; use OCA\Deck\Db\Board; use OCA\Deck\Db\Card; +use OCA\Deck\Db\Label; use OCA\Deck\Db\Stack; abstract class ABoardImportService { @@ -34,9 +35,10 @@ abstract class ABoardImportService { abstract public function importParticipants(): self; - abstract public function importComments(): self; + abstract public function importComments(); - abstract public function importLabels(): self; + /** @return Label[] */ + abstract public function importLabels(): array; abstract public function assignCardsToLabels(): self; diff --git a/lib/Service/BoardImportCommandService.php b/lib/Service/BoardImportCommandService.php index 4a4b92096..8b7b3a416 100644 --- a/lib/Service/BoardImportCommandService.php +++ b/lib/Service/BoardImportCommandService.php @@ -1,10 +1,31 @@ + * + * @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 . + * + */ namespace OCA\Deck\Service; -use JsonSchema\Constraints\Constraint; -use JsonSchema\Validator; use OCA\Deck\Command\BoardImport; +use OCA\Deck\Exceptions\ConflictException; +use OCA\Deck\NotFoundException; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; @@ -60,16 +81,16 @@ class BoardImportCommandService extends BoardImportService { return $this->output; } - public function validate(): self { - $this->validateSystem(); - $this->validateConfig(); + public function validate() { $this->validateData(); - return $this; + parent::validate(); } - private function validateConfig(): void { - $configFile = $this->getInput()->getOption('config'); - if (!is_file($configFile)) { + protected function validateConfig() { + try { + parent::validateConfig(); + return; + } catch (NotFoundException $e) { $helper = $this->getCommand()->getHelper('question'); $question = new Question( 'Please inform a valid config json file: ', @@ -84,50 +105,39 @@ class BoardImportCommandService extends BoardImportService { return $answer; }); $configFile = $helper->ask($this->getInput(), $this->getOutput(), $question); - $this->getInput()->setOption('config', $configFile); - } - - $config = json_decode(file_get_contents($configFile)); - $system = $this->getSystem(); - $schemaPath = __DIR__ . '/fixtures/config-' . $system . '-schema.json'; - $validator = new Validator(); - $validator->validate( - $config, - (object)['$ref' => 'file://' . realpath($schemaPath)], - Constraint::CHECK_MODE_APPLY_DEFAULTS - ); - if (!$validator->isValid()) { + $this->setConfigInstance($configFile); + } catch (ConflictException $e) { $this->getOutput()->writeln('Invalid config file'); $this->getOutput()->writeln(array_map(function ($v) { return $v['message']; - }, $validator->getErrors())); + }, $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->getInput()->setOption('config', null); - $this->validateConfig($this->getInput(), $this->getOutput()); + $this->setConfigInstance(''); } - $this->setConfigInstance($config); - $this->validateOwner(); + parent::validateConfig(); + return; } - /** - * @return void - */ - private function validateSystem(): self { - $system = $this->getInput()->getOption('system'); - if (in_array($system, $this->getAllowedImportSystems())) { - return $this->setSystem($system); + protected function validateSystem() { + try { + parent::validateSystem(); + return; + } catch (\Throwable $th) { } $helper = $this->getCommand()->getHelper('question'); $question = new ChoiceQuestion( 'Please inform a source system', - $this->allowedSystems, + $this->getAllowedImportSystems(), 0 ); $question->setErrorMessage('System %s is invalid.'); $system = $helper->ask($this->getInput(), $this->getOutput(), $question); $this->getInput()->setOption('system', $system); - return $this->setSystem($system); + $this->setSystem($system); + return; } private function validateData(): self { @@ -152,9 +162,8 @@ class BoardImportCommandService extends BoardImportService { $this->setData(json_decode(file_get_contents($filename))); if (!$this->getData()) { $this->getOutput()->writeln('Is not a json file: ' . $filename . ''); - $this->validateData($this->getInput(), $this->getOutput()); + $this->validateData(); } - $this->validateUsers(); return $this; } diff --git a/lib/Service/BoardImportService.php b/lib/Service/BoardImportService.php index fec0f707d..3dfb562cb 100644 --- a/lib/Service/BoardImportService.php +++ b/lib/Service/BoardImportService.php @@ -34,9 +34,10 @@ use OCA\Deck\Db\BoardMapper; use OCA\Deck\Db\CardMapper; use OCA\Deck\Db\Label; use OCA\Deck\Db\LabelMapper; +use OCA\Deck\Db\Stack; use OCA\Deck\Db\StackMapper; +use OCA\Deck\Exceptions\ConflictException; use OCA\Deck\NotFoundException; -use OCP\Comments\IComment; use OCP\Comments\ICommentsManager; use OCP\Comments\NotFoundException as CommentNotFoundException; use OCP\IDBConnection; @@ -105,39 +106,30 @@ class BoardImportService { } public function import(): void { - $this->validate(); - $schemaPath = __DIR__ . '/fixtures/config-' . $system . '-schema.json'; - $validator = new Validator(); - $validator->validate( - $config, - (object)['$ref' => 'file://' . realpath($schemaPath)], - Constraint::CHECK_MODE_APPLY_DEFAULTS - ); - if (!$validator->isValid()) { - throw new BadRequestException('invalid config'); + try { + $this->importBoard(); + $this->importAcl(); + $this->importLabels(); + $this->importStacks(); + $this->importCards(); + $this->assignCardsToLabels(); + $this->importComments(); + $this->importParticipants(); + } catch (\Throwable $th) { + throw new BadRequestException($th->getMessage()); } - - if (empty($data)) { - throw new BadRequestException('data must be provided'); - } - $this->getImportService()->setData($data); - $this->getImportService()->import(); - // return $newBoard; } - public function validate(): self { - if (is_string($system) === false) { - throw new BadRequestException('system must be provided'); - } + public function validate() { + $this->validateSystem(); + $this->validateConfig(); + $this->validateUsers(); + } - if (!in_array($system, $this->getAllowedImportSystems())) { - throw new BadRequestException('not allowed system'); + protected function validateSystem() { + if (!in_array($this->getSystem(), $this->getAllowedImportSystems())) { + throw new NotFoundException('Invalid system'); } - - if (empty($config)) { - throw new BadRequestException('config must be provided'); - } - return $this; } public function setSystem(string $system): self { @@ -162,10 +154,11 @@ class BoardImportService { } return true; }); - $this->allowedSystems = array_map(function ($name) { + $allowedSystems = array_map(function ($name) { preg_match('/\/BoardImport(?\w+)Service\.php$/', $name, $matches); return lcfirst($matches['system']); }, $allowedSystems); + $this->allowedSystems = array_values($allowedSystems); } return $this->allowedSystems; } @@ -180,6 +173,10 @@ class BoardImportService { return $this->systemInstance; } + public function setImportSystem($instance) { + $this->systemInstance = $instance; + } + public function insertAssignment($assignment): self { $this->assignmentMapper->insert($assignment); return $this; @@ -203,12 +200,14 @@ class BoardImportService { foreach ($aclList as $acl) { $this->aclMapper->insert($acl); } + $this->getBoard()->setAcl($aclList); return $this; } - public function importLabels(): self { - $this->getImportSystem()->importLabels(); - return $this; + public function importLabels(): array { + $labels = $this->getImportSystem()->importLabels(); + $this->getBoard()->setLabels($labels); + return $labels; } public function createLabel($title, $color, $boardId): Label { @@ -219,13 +218,17 @@ class BoardImportService { return $this->labelMapper->insert($label); } - public function importStacks(): self { - $stack = $this->getImportSystem()->getStacks(); - foreach ($stack as $code => $stack) { + /** + * @return Stack[] + */ + public function importStacks(): array { + $stacks = $this->getImportSystem()->getStacks(); + foreach ($stacks as $code => $stack) { $this->stackMapper->insert($stack); $this->getImportSystem()->updateStack($code, $stack); } - return $this; + $this->getBoard()->setStacks(array_values($stacks)); + return $stacks; } public function importCards(): self { @@ -255,7 +258,7 @@ class BoardImportService { return $this; } - public function insertComment($cardId, IComment $comment): IComment { + public function insertComment($cardId, $comment) { $comment->setObject('deckCard', (string) $cardId); $comment->setVerb('comment'); // Check if parent is a comment on the same card @@ -338,10 +341,7 @@ class BoardImportService { * @return mixed */ public function getConfig(string $configName = null) { - if (!is_object($this->config)) { - return; - } - if (!$configName) { + if (!is_object($this->config) || !$configName) { return $this->config; } if (!property_exists($this->config, $configName)) { @@ -350,15 +350,41 @@ class BoardImportService { return $this->config->$configName; } - public function setConfigInstance(\stdClass $config): self { + public function setConfigInstance($config): self { $this->config = $config; return $this; } + protected function validateConfig() { + $config = $this->getConfig(); + if (empty($config)) { + throw new NotFoundException('Please inform a valid config json file'); + } + if (is_string($config)) { + if (!is_file($config)) { + throw new NotFoundException('Please inform a valid config json file'); + } + $config = json_decode(file_get_contents($config)); + } + $schemaPath = __DIR__ . '/fixtures/config-' . $this->getSystem() . '-schema.json'; + $validator = new Validator(); + $newConfig = clone $config; + $validator->validate( + $newConfig, + (object)['$ref' => 'file://' . realpath($schemaPath)], + Constraint::CHECK_MODE_APPLY_DEFAULTS + ); + if (!$validator->isValid()) { + throw new ConflictException('Invalid config file', $validator->getErrors()); + } + $this->setConfigInstance($newConfig); + $this->validateOwner(); + } + public function validateOwner(): self { $owner = $this->userManager->get($this->getConfig('owner')); if (!$owner) { - throw new \LogicException('Owner "' . $this->getConfigboardImportService->getConfig('owner')->getUID() . '" not found on Nextcloud. Check setting json.'); + throw new \LogicException('Owner "' . $this->getConfig('owner')->getUID() . '" not found on Nextcloud. Check setting json.'); } $this->setConfig('owner', $owner); return $this; diff --git a/lib/Service/BoardImportTrelloService.php b/lib/Service/BoardImportTrelloService.php index c429f1cd8..5f5cb614b 100644 --- a/lib/Service/BoardImportTrelloService.php +++ b/lib/Service/BoardImportTrelloService.php @@ -24,9 +24,9 @@ namespace OCA\Deck\Service; use OC\Comments\Comment; +use OCA\Deck\BadRequestException; use OCA\Deck\Db\Acl; use OCA\Deck\Db\Assignment; -use OCA\Deck\Db\AssignmentMapper; use OCA\Deck\Db\Board; use OCA\Deck\Db\Card; use OCA\Deck\Db\Label; @@ -36,8 +36,6 @@ use OCP\IUser; use OCP\IUserManager; class BoardImportTrelloService extends ABoardImportService { - /** @var AssignmentMapper */ - private $assignmentMapper; /** @var IUserManager */ private $userManager; /** @var IL10N */ @@ -60,25 +58,15 @@ class BoardImportTrelloService extends ABoardImportService { private $members = []; public function __construct( - BoardService $boardService, - AssignmentMapper $assignmentMapper, IUserManager $userManager, IL10N $l10n ) { - $this->boardService = $boardService; - $this->assignmentMapper = $assignmentMapper; $this->userManager = $userManager; $this->l10n = $l10n; } - public function validate(): ABoardImportService { - $this->boardImportTrelloService->validateOwner(); - $this->boardImportTrelloService->validateUsers(); - return $this; - } - /** - * @return ABoardImportService + * @return self */ public function validateUsers(): self { if (empty($this->getImportService()->getConfig('uidRelation'))) { @@ -91,8 +79,8 @@ class BoardImportTrelloService extends ABoardImportService { if (!$user) { throw new \LogicException('Trello user ' . $trelloUid . ' not found in property "members" of json data'); } - if (!is_string($nextcloudUid)) { - throw new \LogicException('User on setting uidRelation must be a string'); + if (!is_string($nextcloudUid) && !is_numeric($nextcloudUid)) { + throw new \LogicException('User on setting uidRelation is invalid'); } $this->getImportService()->getConfig('uidRelation')->$trelloUid = $this->userManager->get($nextcloudUid); if (!$this->getImportService()->getConfig('uidRelation')->$trelloUid) { @@ -170,6 +158,9 @@ class BoardImportTrelloService extends ABoardImportService { $card->setTitle($trelloCard->name); $card->setStackId($this->stacks[$trelloCard->idList]->getId()); + $cardsOnStack = $this->stacks[$trelloCard->idList]->getCards(); + $cardsOnStack[] = $card; + $this->stacks[$trelloCard->idList]->setCards($cardsOnStack); $card->setType('plain'); $card->setOrder($trelloCard->idShort); $card->setOwner($this->getImportService()->getConfig('owner')->getUID()); @@ -184,17 +175,17 @@ class BoardImportTrelloService extends ABoardImportService { return $this->cards; } - public function updateCard($cardTrelloId, Card $card): self { - $this->cards[$cardTrelloId] = $card; + public function updateCard($id, Card $card): self { + $this->cards[$id] = $card; return $this; } /** - * @return ABoardImportService + * @return self */ - private function appendAttachmentsToDescription($trelloCard) { + private function appendAttachmentsToDescription($trelloCard): self { if (empty($trelloCard->attachments)) { - return; + return $this; } $trelloCard->desc .= "\n\n## {$this->l10n->t('Attachments')}\n"; $trelloCard->desc .= "| {$this->l10n->t('File')} | {$this->l10n->t('date')} |\n"; @@ -206,9 +197,12 @@ class BoardImportTrelloService extends ABoardImportService { return $this; } - public function importParticipants(): ABoardImportService { + public function importParticipants(): self { foreach ($this->getImportService()->getData()->cards as $trelloCard) { foreach ($trelloCard->idMembers as $idMember) { + if (empty($this->members[$idMember])) { + continue; + } $assignment = new Assignment(); $assignment->setCardId($this->cards[$trelloCard->id]->getId()); $assignment->setParticipant($this->members[$idMember]->getUID()); @@ -219,7 +213,7 @@ class BoardImportTrelloService extends ABoardImportService { return $this; } - public function importComments(): ABoardImportService { + public function importComments() { foreach ($this->getImportService()->getData()->cards as $trelloCard) { $comments = array_filter( $this->getImportService()->getData()->actions, @@ -246,7 +240,6 @@ class BoardImportTrelloService extends ABoardImportService { ); } } - return $this; } private function replaceUsernames($text) { @@ -320,13 +313,16 @@ class BoardImportTrelloService extends ABoardImportService { public function getBoard(): Board { $board = new Board(); + if (!$this->getImportService()->getData()->name) { + throw new BadRequestException('Invalid name of board'); + } $board->setTitle($this->getImportService()->getData()->name); $board->setOwner($this->getImportService()->getConfig('owner')->getUID()); $board->setColor($this->getImportService()->getConfig('color')); return $board; } - public function importLabels(): self { + public function importLabels(): array { foreach ($this->getImportService()->getData()->labels as $label) { if (empty($label->name)) { $labelTitle = 'Unnamed ' . $label->color . ' label'; @@ -340,6 +336,6 @@ class BoardImportTrelloService extends ABoardImportService { ); $this->labels[$label->id] = $newLabel; } - return $this; + return $this->labels; } } diff --git a/tests/unit/Command/BoardImportTest.php b/tests/unit/Command/BoardImportTest.php index 4f36d54bd..e2cdbbfbc 100644 --- a/tests/unit/Command/BoardImportTest.php +++ b/tests/unit/Command/BoardImportTest.php @@ -23,32 +23,23 @@ namespace OCA\Deck\Command; -use OCA\Deck\Command\ImportHelper\TrelloHelper; -use OCA\Deck\Service\AImportService; -use OCA\Deck\Service\BoardImportService; +use OCA\Deck\Service\BoardImportCommandService; use Symfony\Component\Console\Helper\HelperSet; use Symfony\Component\Console\Helper\QuestionHelper; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; class BoardImportTest extends \Test\TestCase { - /** @var TrelloHelper */ - private $trelloHelper; - /** @var BoardImportService */ - private $boardImportService; + /** @var BoardImportCommandService */ + private $boardImportCommandService; /** @var BoardImport */ private $boardImport; public function setUp(): void { parent::setUp(); - $this->trelloHelper = $this->createMock(TrelloHelper::class); - $this->boardImportService = $this->createMock(BoardImportService::class); - $this->boardImportService - ->method('getAllowedImportSystems') - ->willReturn(['trello']); + $this->boardImportCommandService = $this->createMock(boardImportCommandService::class); $this->boardImport = new BoardImport( - $this->trelloHelper, - $this->boardImportService + $this->boardImportCommandService ); $questionHelper = new QuestionHelper(); $this->boardImport->setHelperSet( @@ -60,33 +51,26 @@ class BoardImportTest extends \Test\TestCase { public function testExecuteWithSuccess() { $input = $this->createMock(InputInterface::class); - - $input->method('getOption') + $input + ->method('getOption') ->withConsecutive( - [$this->equalTo('system')], - [$this->equalTo('config')], - [$this->equalTo('data')] + ['system'], + ['config'] ) ->will($this->returnValueMap([ ['system', 'trello'], - ['config', __DIR__ . '/../../data/config-trello.json'], - ['data', __DIR__ . '/../../data/data-trello.json'] + ['config', null] ])); + $output = $this->createMock(OutputInterface::class); $output ->expects($this->once()) ->method('writeLn') ->with('Done!'); - $this->boardImportService - ->method('getSystem') - ->willReturn('trello'); - $importService = $this->createMock(AImportService::class); - $this->boardImportService - ->method('getImportService') - ->willReturn($importService); - $this->invokePrivate($this->boardImport, 'interact', [$input, $output]); + $actual = $this->invokePrivate($this->boardImport, 'interact', [$input, $output]); + $this->assertNull($actual); $actual = $this->invokePrivate($this->boardImport, 'execute', [$input, $output]); $this->assertEquals(0, $actual); } diff --git a/tests/unit/Command/Helper/TrelloHelperTest.php b/tests/unit/Command/Helper/TrelloHelperTest.php deleted file mode 100644 index 25972a7d8..000000000 --- a/tests/unit/Command/Helper/TrelloHelperTest.php +++ /dev/null @@ -1,92 +0,0 @@ - - * - * @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 . - * - */ - -namespace OCA\Deck\Command; - -use OCA\Deck\Command\ImportHelper\TrelloHelper; -use OCA\Deck\Service\AImportService; -use OCA\Deck\Service\BoardImportService; -use OCA\Deck\Service\TrelloImportService; -use Symfony\Component\Console\Helper\HelperSet; -use Symfony\Component\Console\Helper\QuestionHelper; -use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Output\OutputInterface; - -class TrelloHelperTest extends \Test\TestCase { - /** @var TrelloImportService */ - private $trelloImportService; - /** @var BoardImportService */ - private $boardImportService; - /** @var TrelloHelper */ - private $trelloHelper; - public function setUp(): void { - parent::setUp(); - $this->trelloImportService = $this->createMock(TrelloImportService::class); - $this->trelloHelper = new TrelloHelper( - $this->trelloImportService - ); - $questionHelper = new QuestionHelper(); - $this->boardImportService = $this->createMock(BoardImportService::class); - $this->boardImportService - ->method('getAllowedImportSystems') - ->willReturn(['trello']); - $command = new BoardImport( - $this->trelloHelper, - $this->boardImportService - ); - $command->setHelperSet( - new HelperSet([ - $questionHelper - ]) - ); - $this->trelloHelper->setCommand($command); - } - - public function testImportWithSuccess() { - $input = $this->createMock(InputInterface::class); - - $input->method('getOption') - ->withConsecutive( - [$this->equalTo('system')], - [$this->equalTo('config')] - ) - ->will($this->returnValueMap([ - ['system', 'trello'], - ['config', __DIR__ . '/../../../data/config-trello.json'] - ])); - $output = $this->createMock(OutputInterface::class); - - $this->boardImportService - ->method('getSystem') - ->willReturn('trello'); - $importService = $this->createMock(AImportService::class); - $this->boardImportService - ->method('getImportService') - ->willReturn($importService); - - $this->invokePrivate($this->trelloHelper->getCommand(), 'validateSystem', [$input, $output]); - $this->invokePrivate($this->trelloHelper->getCommand(), 'validateConfig', [$input, $output]); - $actual = $this->trelloHelper->import($input, $output); - $this->assertNull($actual); - } -} diff --git a/tests/unit/Service/BoardImportServiceTest.php b/tests/unit/Service/BoardImportServiceTest.php index 7de0cadd1..d0c55dbf0 100644 --- a/tests/unit/Service/BoardImportServiceTest.php +++ b/tests/unit/Service/BoardImportServiceTest.php @@ -22,25 +22,69 @@ */ namespace OCA\Deck\Service; +use OCA\Deck\Db\AclMapper; +use OCA\Deck\Db\AssignmentMapper; +use OCA\Deck\Db\Board; +use OCA\Deck\Db\BoardMapper; +use OCA\Deck\Db\CardMapper; +use OCA\Deck\Db\LabelMapper; +use OCA\Deck\Db\StackMapper; +use OCP\Comments\ICommentsManager; +use OCP\IDBConnection; +use OCP\IUserManager; + class BoardImportServiceTest extends \Test\TestCase { - /** @var TrelloImportService */ - private $trelloImportService; + /** @var IDBConnection */ + protected $dbConn; + /** @var IUserManager */ + private $userManager; + /** @var BoardMapper */ + private $boardMapper; + /** @var AclMapper */ + private $aclMapper; + /** @var LabelMapper */ + private $labelMapper; + /** @var StackMapper */ + private $stackMapper; + /** @var CardMapper */ + private $cardMapper; + /** @var AssignmentMapper */ + private $assignmentMapper; + /** @var ICommentsManager */ + private $commentsManager; /** @var BoardImportService */ private $boardImportService; public function setUp(): void { - $this->trelloImportService = $this->createMock(TrelloImportService::class); + $this->dbConn = $this->createMock(IDBConnection::class); + $this->userManager = $this->createMock(IUserManager::class); + $this->boardMapper = $this->createMock(BoardMapper::class); + $this->aclMapper = $this->createMock(AclMapper::class); + $this->labelMapper = $this->createMock(LabelMapper::class); + $this->stackMapper = $this->createMock(StackMapper::class); + $this->cardMapper = $this->createMock(AssignmentMapper::class); + $this->assignmentMapper = $this->createMock(CardMapper::class); + $this->commentsManager = $this->createMock(ICommentsManager::class); $this->boardImportService = new BoardImportService( - $this->trelloImportService + $this->dbConn, + $this->userManager, + $this->boardMapper, + $this->aclMapper, + $this->labelMapper, + $this->stackMapper, + $this->cardMapper, + $this->assignmentMapper, + $this->commentsManager ); } public function testImportSuccess() { - $config = json_decode(file_get_contents(__DIR__ . '/../../data/config-trello.json')); - $data = json_decode(file_get_contents(__DIR__ . '/../../data/data-trello.json')); - $actual = $this->boardImportService->import( - 'trello', - $config, - $data - ); + $importService = $this->createMock(ABoardImportService::class); + $board = new Board(); + $importService + ->method('getBoard') + ->willReturn($board); + $this->boardImportService->setImportSystem($importService); + $actual = $this->boardImportService->import(); + $this->assertNull($actual); } } diff --git a/tests/unit/Service/BoardImportTrelloServiceTest.php b/tests/unit/Service/BoardImportTrelloServiceTest.php new file mode 100644 index 000000000..b33fd42af --- /dev/null +++ b/tests/unit/Service/BoardImportTrelloServiceTest.php @@ -0,0 +1,169 @@ + + * + * @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 . + * + */ +namespace OCA\Deck\Service; + +use OCP\IL10N; +use OCP\IUser; +use OCP\IUserManager; + +class BoardImportTrelloServiceTest extends \Test\TestCase { + /** @var BoardImportTrelloService */ + private $service; + /** @var IUserManager */ + private $userManager; + /** @var IL10N */ + private $l10n; + public function setUp(): void { + $this->userManager = $this->createMock(IUserManager::class); + $this->l10n = $this->createMock(IL10N::class); + $this->service = new BoardImportTrelloService( + $this->userManager, + $this->l10n + ); + } + + public function testValidateUsersWithoutUsers() { + $importService = $this->createMock(BoardImportService::class); + $this->service->setImportService($importService); + $actual = $this->service->validateUsers(); + $this->assertInstanceOf(BoardImportTrelloService::class, $actual); + } + + public function testValidateUsersWithInvalidUser() { + $this->expectErrorMessage('Trello user trello_user not found in property "members" of json data'); + $importService = $this->createMock(BoardImportService::class); + $importService + ->method('getConfig') + ->willReturn([ + 'trello_user' => 'nextcloud_user' + ]); + $importService + ->method('getData') + ->willReturn(json_decode( + <<service->setImportService($importService); + $actual = $this->service->validateUsers(); + $this->assertInstanceOf(BoardImportTrelloService::class, $actual); + } + + public function testValidateUsersWithNotStringNextcloud() { + $this->expectErrorMessage('User on setting uidRelation is invalid'); + $importService = $this->createMock(BoardImportService::class); + $importService + ->method('getConfig') + ->willReturn([ + 'trello_user' => [] + ]); + $importService + ->method('getData') + ->willReturn(json_decode( + <<service->setImportService($importService); + $actual = $this->service->validateUsers(); + $this->assertInstanceOf(BoardImportTrelloService::class, $actual); + } + + public function testValidateUsersWithNotFoundUser() { + $this->expectErrorMessage('User on setting uidRelation not found: nextcloud_user'); + $importService = $this->createMock(BoardImportService::class); + $importService + ->method('getConfig') + ->willReturn(json_decode( + <<method('getData') + ->willReturn(json_decode( + <<service->setImportService($importService); + $actual = $this->service->validateUsers(); + $this->assertInstanceOf(BoardImportTrelloService::class, $actual); + } + + public function testValidateUsersWithValidUsers() { + $importService = $this->createMock(BoardImportService::class); + $importService + ->method('getConfig') + ->willReturn(json_decode( + <<method('getData') + ->willReturn(json_decode( + <<createMock(IUser::class); + $this->userManager + // ->expects($this->once()) + ->method('get') + ->with('nextcloud_user') + ->willReturn($fakeUser); + $this->service->setImportService($importService); + $actual = $this->service->validateUsers(); + $this->assertInstanceOf(BoardImportTrelloService::class, $actual); + } +} diff --git a/tests/unit/Service/BoardServiceTest.php b/tests/unit/Service/BoardServiceTest.php index be2785378..7dba39b5d 100644 --- a/tests/unit/Service/BoardServiceTest.php +++ b/tests/unit/Service/BoardServiceTest.php @@ -73,8 +73,6 @@ class BoardServiceTest extends TestCase { private $changeHelper; /** @var IEventDispatcher */ private $eventDispatcher; - /** @var TrelloImportService */ - private $trelloImportService; private $userId = 'admin'; public function setUp(): void { @@ -93,7 +91,6 @@ class BoardServiceTest extends TestCase { $this->activityManager = $this->createMock(ActivityManager::class); $this->changeHelper = $this->createMock(ChangeHelper::class); $this->eventDispatcher = $this->createMock(IEventDispatcher::class); - $this->trelloImportService = $this->createMock(TrelloImportService::class); $this->service = new BoardService( $this->boardMapper, @@ -110,7 +107,6 @@ class BoardServiceTest extends TestCase { $this->activityManager, $this->eventDispatcher, $this->changeHelper, - $this->trelloImportService, $this->userId ); diff --git a/tests/unit/Service/TrelloImportServiceTest.php b/tests/unit/Service/TrelloImportServiceTest.php deleted file mode 100644 index d9bc2ed50..000000000 --- a/tests/unit/Service/TrelloImportServiceTest.php +++ /dev/null @@ -1,104 +0,0 @@ - - * - * @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 . - * - */ - -namespace OCA\Deck\Service; - -use OCA\Deck\Db\AclMapper; -use OCA\Deck\Db\AssignmentMapper; -use OCA\Deck\Db\CardMapper; -use OCA\Deck\Db\StackMapper; -use OCP\IDBConnection; -use OCP\IL10N; -use OCP\IUserManager; - -class TrelloImportServiceTest extends \Test\TestCase { - /** @var TrelloImportService */ - private $trelloImportService; - /** @var BoardService */ - private $boardService; - /** @var LabelService */ - private $labelService; - /** @var StackMapper */ - private $stackMapper; - /** @var CardMapper */ - private $cardMapper; - /** @var AssignmentMapper */ - private $assignmentMapper; - /** @var AclMapper */ - private $aclMapper; - /** @var IDBConnection */ - private $connection; - /** @var IUserManager */ - private $userManager; - /** @var IL10N */ - private $l10n; - public function setUp(): void { - parent::setUp(); - $this->boardService = $this->createMock(BoardService::class); - $this->labelService = $this->createMock(LabelService::class); - $this->stackMapper = $this->createMock(StackMapper::class); - $this->cardMapper = $this->createMock(CardMapper::class); - $this->assignmentMapper = $this->createMock(AssignmentMapper::class); - $this->aclMapper = $this->createMock(AclMapper::class); - $this->connection = $this->createMock(IDBConnection::class); - $this->userManager = $this->createMock(IUserManager::class); - $this->l10n = $this->createMock(IL10N::class); - $this->trelloImportService = new TrelloImportService( - $this->boardService, - $this->labelService, - $this->stackMapper, - $this->cardMapper, - $this->assignmentMapper, - $this->aclMapper, - $this->connection, - $this->userManager, - $this->l10n - ); - } - - public function testValidateOwnerWithFaliure() { - $owner = $this->createMock(\OCP\IUser::class); - $owner - ->method('getUID') - ->willReturn('admin'); - $this->trelloImportService->setConfig('owner', $owner); - $this->userManager - ->method('get') - ->willReturn(null); - $this->expectErrorMessage('Owner "admin" not found on Nextcloud. Check setting json.'); - $this->trelloImportService->validateOwner(); - } - - public function testValidateOwnerWithSuccess() { - $owner = $this->createMock(\OCP\IUser::class); - $owner - ->method('getUID') - ->willReturn('admin'); - $this->trelloImportService->setConfig('owner', $owner); - $this->userManager - ->method('get') - ->willReturn($owner); - $actual = $this->trelloImportService->validateOwner(); - $this->assertNull($actual); - } -} diff --git a/tests/unit/controller/BoardImportApiControllerTest.php b/tests/unit/controller/BoardImportApiControllerTest.php new file mode 100644 index 000000000..5389b46f1 --- /dev/null +++ b/tests/unit/controller/BoardImportApiControllerTest.php @@ -0,0 +1,74 @@ + + * + * @author Ryan Fletcher + * + * @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 . + * + */ +namespace OCA\Deck\Controller; + +use OCA\Deck\Db\Board; +use OCP\AppFramework\Http; +use OCP\AppFramework\Http\DataResponse; +use OCP\IRequest; +use OCA\Deck\Service\BoardImportService; + +class BoardImportApiControllerTest extends \Test\TestCase { + private $appName = 'deck'; + private $userId = 'admin'; + /** @var BoardImportApiController */ + private $controller; + /** @var BoardImportService */ + private $boardImportService; + + public function setUp(): void { + parent::setUp(); + $this->request = $this->createMock(IRequest::class); + $this->boardImportService = $this->createMock(BoardImportService::class); + + $this->controller = new BoardImportApiController( + $this->appName, + $this->request, + $this->boardImportService, + $this->userId + ); + } + + public function testGetAllowedSystems() { + $this->boardImportService + ->method('getAllowedImportSystems') + ->willReturn(['trello']); + $actual = $this->controller->getAllowedSystems(); + $expected = new DataResponse(['trello'], HTTP::STATUS_OK); + $this->assertEquals($expected, $actual); + } + + public function testImport() { + $system = 'trello'; + $config = [ + 'owner' => 'test' + ]; + $data = [ + 'name' => 'test' + ]; + $actual = $this->controller->import($system, $config, $data); + $board = $this->createMock(Board::class); + $this->assertInstanceOf(Board::class, $board); + $this->assertEquals(HTTP::STATUS_OK, $actual->getStatus()); + } +}