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 <vitor@php.rio>
This commit is contained in:
Vitor Mattos
2021-07-16 00:44:45 -03:00
committed by Julius Härtl
parent 39a927de18
commit 4138953208
15 changed files with 511 additions and 354 deletions

View File

@@ -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;

View File

@@ -1,10 +1,31 @@
<?php
/**
* @copyright Copyright (c) 2021 Vitor Mattos <vitor@php.rio>
*
* @author Vitor Mattos <vitor@php.rio>
*
* @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/>.
*
*/
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('<error>Invalid config file</error>');
$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('<error>Is not a json file: ' . $filename . '</error>');
$this->validateData($this->getInput(), $this->getOutput());
$this->validateData();
}
$this->validateUsers();
return $this;
}

View File

@@ -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(?<system>\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;

View File

@@ -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;
}
}