Move all classes to a sub-namespace
Signed-off-by: Vitor Mattos <vitor@php.rio>
This commit is contained in:
committed by
Julius Härtl
parent
24c8b2f4aa
commit
f2b6934ac3
444
lib/Service/Importer/BoardImportService.php
Normal file
444
lib/Service/Importer/BoardImportService.php
Normal file
@@ -0,0 +1,444 @@
|
||||
<?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\Importer;
|
||||
|
||||
use JsonSchema\Constraints\Constraint;
|
||||
use JsonSchema\Validator;
|
||||
use OCA\Deck\AppInfo\Application;
|
||||
use OCA\Deck\BadRequestException;
|
||||
use OCA\Deck\Db\AclMapper;
|
||||
use OCA\Deck\Db\AssignmentMapper;
|
||||
use OCA\Deck\Db\Attachment;
|
||||
use OCA\Deck\Db\AttachmentMapper;
|
||||
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 OCA\Deck\Exceptions\ConflictException;
|
||||
use OCA\Deck\NotFoundException;
|
||||
use OCP\Comments\IComment;
|
||||
use OCP\Comments\ICommentsManager;
|
||||
use OCP\Comments\NotFoundException as CommentNotFoundException;
|
||||
use OCP\IUserManager;
|
||||
|
||||
class BoardImportService {
|
||||
/** @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 AttachmentMapper */
|
||||
private $attachmentMapper;
|
||||
/** @var ICommentsManager */
|
||||
private $commentsManager;
|
||||
/** @var string */
|
||||
private $system = '';
|
||||
/** @var null|ABoardImportService */
|
||||
private $systemInstance;
|
||||
/** @var array */
|
||||
private $allowedSystems = [];
|
||||
/**
|
||||
* Data object created from config JSON
|
||||
*
|
||||
* @var \stdClass
|
||||
* @psalm-suppress PropertyNotSetInConstructor
|
||||
*/
|
||||
public $config;
|
||||
/**
|
||||
* Data object created from JSON of origin system
|
||||
*
|
||||
* @var \stdClass
|
||||
* @psalm-suppress PropertyNotSetInConstructor
|
||||
*/
|
||||
private $data;
|
||||
/**
|
||||
* @var Board
|
||||
*/
|
||||
private $board;
|
||||
|
||||
public function __construct(
|
||||
IUserManager $userManager,
|
||||
BoardMapper $boardMapper,
|
||||
AclMapper $aclMapper,
|
||||
LabelMapper $labelMapper,
|
||||
StackMapper $stackMapper,
|
||||
AssignmentMapper $assignmentMapper,
|
||||
AttachmentMapper $attachmentMapper,
|
||||
CardMapper $cardMapper,
|
||||
ICommentsManager $commentsManager
|
||||
) {
|
||||
$this->userManager = $userManager;
|
||||
$this->boardMapper = $boardMapper;
|
||||
$this->aclMapper = $aclMapper;
|
||||
$this->labelMapper = $labelMapper;
|
||||
$this->stackMapper = $stackMapper;
|
||||
$this->cardMapper = $cardMapper;
|
||||
$this->assignmentMapper = $assignmentMapper;
|
||||
$this->attachmentMapper = $attachmentMapper;
|
||||
$this->commentsManager = $commentsManager;
|
||||
$this->board = new Board();
|
||||
$this->disableCommentsEvents();
|
||||
}
|
||||
|
||||
private function disableCommentsEvents(): void {
|
||||
if (defined('PHPUNIT_RUN')) {
|
||||
return;
|
||||
}
|
||||
$propertyEventHandlers = new \ReflectionProperty($this->commentsManager, 'eventHandlers');
|
||||
$propertyEventHandlers->setAccessible(true);
|
||||
$propertyEventHandlers->setValue($this->commentsManager, []);
|
||||
|
||||
$propertyEventHandlerClosures = new \ReflectionProperty($this->commentsManager, 'eventHandlerClosures');
|
||||
$propertyEventHandlerClosures->setAccessible(true);
|
||||
$propertyEventHandlerClosures->setValue($this->commentsManager, []);
|
||||
}
|
||||
|
||||
public function import(): void {
|
||||
$this->bootstrap();
|
||||
try {
|
||||
$this->importBoard();
|
||||
$this->importAcl();
|
||||
$this->importLabels();
|
||||
$this->importStacks();
|
||||
$this->importCards();
|
||||
$this->assignCardsToLabels();
|
||||
$this->importComments();
|
||||
$this->importCardAssignments();
|
||||
} catch (\Throwable $th) {
|
||||
throw new BadRequestException($th->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function validateSystem(): void {
|
||||
$allowedSystems = $this->getAllowedImportSystems();
|
||||
$allowedSystems = array_column($allowedSystems, 'internalName');
|
||||
if (!in_array($this->getSystem(), $allowedSystems)) {
|
||||
throw new NotFoundException('Invalid system');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $system
|
||||
* @return self
|
||||
*/
|
||||
public function setSystem($system): self {
|
||||
$this->system = $system;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getSystem(): string {
|
||||
return $this->system;
|
||||
}
|
||||
|
||||
public function getAllowedImportSystems(): array {
|
||||
if (!$this->allowedSystems) {
|
||||
$allowedSystems = glob(__DIR__ . '/Systems/*Service.php');
|
||||
$allowedSystems = array_map(function ($filename) {
|
||||
preg_match('/\/(?<class>(?<system>\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);
|
||||
}
|
||||
return $this->allowedSystems;
|
||||
}
|
||||
|
||||
public function getImportSystem(): ABoardImportService {
|
||||
if (!$this->getSystem()) {
|
||||
throw new NotFoundException('System to import not found');
|
||||
}
|
||||
if (!is_object($this->systemInstance)) {
|
||||
$systemClass = 'OCA\\Deck\\Service\\BoardImport' . ucfirst($this->getSystem()) . 'Service';
|
||||
$this->systemInstance = \OC::$server->get($systemClass);
|
||||
$this->systemInstance->setImportService($this);
|
||||
}
|
||||
return $this->systemInstance;
|
||||
}
|
||||
|
||||
public function setImportSystem(ABoardImportService $instance): void {
|
||||
$this->systemInstance = $instance;
|
||||
}
|
||||
|
||||
public function importBoard(): void {
|
||||
$board = $this->getImportSystem()->getBoard();
|
||||
if ($board) {
|
||||
$this->boardMapper->insert($board);
|
||||
$this->board = $board;
|
||||
}
|
||||
}
|
||||
|
||||
public function getBoard(bool $reset = false): Board {
|
||||
if ($reset) {
|
||||
$this->board = new Board();
|
||||
}
|
||||
return $this->board;
|
||||
}
|
||||
|
||||
public function importAcl(): void {
|
||||
$aclList = $this->getImportSystem()->getAclList();
|
||||
foreach ($aclList as $code => $acl) {
|
||||
$this->aclMapper->insert($acl);
|
||||
$this->getImportSystem()->updateAcl($code, $acl);
|
||||
}
|
||||
$this->getBoard()->setAcl($aclList);
|
||||
}
|
||||
|
||||
public function importLabels(): void {
|
||||
$labels = $this->getImportSystem()->getLabels();
|
||||
foreach ($labels as $code => $label) {
|
||||
$this->labelMapper->insert($label);
|
||||
$this->getImportSystem()->updateLabel($code, $label);
|
||||
}
|
||||
$this->getBoard()->setLabels($labels);
|
||||
}
|
||||
|
||||
public function importStacks(): void {
|
||||
$stacks = $this->getImportSystem()->getStacks();
|
||||
foreach ($stacks as $code => $stack) {
|
||||
$this->stackMapper->insert($stack);
|
||||
$this->getImportSystem()->updateStack($code, $stack);
|
||||
}
|
||||
$this->getBoard()->setStacks(array_values($stacks));
|
||||
}
|
||||
|
||||
public function importCards(): void {
|
||||
$cards = $this->getImportSystem()->getCards();
|
||||
foreach ($cards as $code => $card) {
|
||||
$createdAt = $card->getCreatedAt();
|
||||
$lastModified = $card->getLastModified();
|
||||
$this->cardMapper->insert($card);
|
||||
$updateDate = false;
|
||||
if ($createdAt && $createdAt !== $card->getCreatedAt()) {
|
||||
$card->setCreatedAt($createdAt);
|
||||
$updateDate = true;
|
||||
}
|
||||
if ($lastModified && $lastModified !== $card->getLastModified()) {
|
||||
$card->setLastModified($lastModified);
|
||||
$updateDate = true;
|
||||
}
|
||||
if ($updateDate) {
|
||||
$this->cardMapper->update($card, false);
|
||||
}
|
||||
$this->getImportSystem()->updateCard($code, $card);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $cardId
|
||||
* @param mixed $labelId
|
||||
* @return self
|
||||
*/
|
||||
public function assignCardToLabel($cardId, $labelId): self {
|
||||
$this->cardMapper->assignLabel(
|
||||
$cardId,
|
||||
$labelId
|
||||
);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function assignCardsToLabels(): void {
|
||||
$data = $this->getImportSystem()->getCardLabelAssignment();
|
||||
foreach ($data as $cardId => $assignemnt) {
|
||||
foreach ($assignemnt as $assignmentId => $labelId) {
|
||||
$this->assignCardToLabel(
|
||||
$cardId,
|
||||
$labelId
|
||||
);
|
||||
$this->getImportSystem()->updateCardLabelsAssignment($cardId, $assignmentId, $labelId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function importComments(): void {
|
||||
$allComments = $this->getImportSystem()->getComments();
|
||||
foreach ($allComments as $cardId => $comments) {
|
||||
foreach ($comments as $commentId => $comment) {
|
||||
$this->insertComment($cardId, $comment);
|
||||
$this->getImportSystem()->updateComment($cardId, $commentId, $comment);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function insertComment(string $cardId, IComment $comment): void {
|
||||
$comment->setObject('deckCard', $cardId);
|
||||
$comment->setVerb('comment');
|
||||
// Check if parent is a comment on the same card
|
||||
if ($comment->getParentId() !== '0') {
|
||||
try {
|
||||
$parent = $this->commentsManager->get($comment->getParentId());
|
||||
if ($parent->getObjectType() !== Application::COMMENT_ENTITY_TYPE || $parent->getObjectId() !== $cardId) {
|
||||
throw new CommentNotFoundException();
|
||||
}
|
||||
} catch (CommentNotFoundException $e) {
|
||||
throw new BadRequestException('Invalid parent id: The parent comment was not found or belongs to a different card');
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
$this->commentsManager->save($comment);
|
||||
} catch (\InvalidArgumentException $e) {
|
||||
throw new BadRequestException('Invalid input values');
|
||||
} catch (CommentNotFoundException $e) {
|
||||
throw new NotFoundException('Could not create comment.');
|
||||
}
|
||||
}
|
||||
|
||||
public function importCardAssignments(): void {
|
||||
$allAssignments = $this->getImportSystem()->getCardAssignments();
|
||||
foreach ($allAssignments as $cardId => $assignments) {
|
||||
foreach ($assignments as $assignmentId => $assignment) {
|
||||
$this->assignmentMapper->insert($assignment);
|
||||
$this->getImportSystem()->updateCardAssignment($cardId, $assignmentId, $assignment);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function insertAttachment(Attachment $attachment, string $content): Attachment {
|
||||
$service = \OC::$server->get(FileService::class);
|
||||
$folder = $service->getFolder($attachment);
|
||||
|
||||
if ($folder->fileExists($attachment->getData())) {
|
||||
$attachment = $this->attachmentMapper->findByData($attachment->getCardId(), $attachment->getData());
|
||||
throw new ConflictException('File already exists.', $attachment);
|
||||
}
|
||||
|
||||
$target = $folder->newFile($attachment->getData());
|
||||
$target->putContent($content);
|
||||
|
||||
$attachment = $this->attachmentMapper->insert($attachment);
|
||||
|
||||
$service->extendData($attachment);
|
||||
return $attachment;
|
||||
}
|
||||
|
||||
public function setData(\stdClass $data): void {
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
public function getData(): \stdClass {
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Define a config
|
||||
*
|
||||
* @param string $configName
|
||||
* @param mixed $value
|
||||
* @return void
|
||||
*/
|
||||
public function setConfig(string $configName, $value): void {
|
||||
if (empty((array) $this->config)) {
|
||||
$this->setConfigInstance(new \stdClass);
|
||||
}
|
||||
$this->config->$configName = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a config
|
||||
*
|
||||
* @param string $configName config name
|
||||
* @return mixed
|
||||
*/
|
||||
public function getConfig(string $configName) {
|
||||
if (!property_exists($this->config, $configName)) {
|
||||
return;
|
||||
}
|
||||
return $this->config->$configName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \stdClass $config
|
||||
* @return self
|
||||
*/
|
||||
public function setConfigInstance($config): self {
|
||||
$this->config = $config;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getConfigInstance(): \stdClass {
|
||||
return $this->config;
|
||||
}
|
||||
|
||||
protected function validateConfig(): void {
|
||||
$config = $this->getConfigInstance();
|
||||
$schemaPath = $this->getJsonSchemaPath();
|
||||
$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 getJsonSchemaPath(): string {
|
||||
return __DIR__ . '/fixtures/config-' . $this->getSystem() . '-schema.json';
|
||||
}
|
||||
|
||||
public function validateOwner(): void {
|
||||
$owner = $this->userManager->get($this->getConfig('owner'));
|
||||
if (!$owner) {
|
||||
throw new \LogicException('Owner "' . $this->getConfig('owner')->getUID() . '" not found on Nextcloud. Check setting json.');
|
||||
}
|
||||
$this->setConfig('owner', $owner);
|
||||
}
|
||||
|
||||
protected function validateData(): void {
|
||||
}
|
||||
|
||||
public function bootstrap(): void {
|
||||
$this->validateSystem();
|
||||
$this->validateConfig();
|
||||
$this->validateData();
|
||||
$this->getImportSystem()->bootstrap();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user