From 392748db571ae6eabbb45a773ac0f6eff1889bc2 Mon Sep 17 00:00:00 2001 From: Yuri Kuznetsov Date: Sun, 5 Sep 2021 16:54:24 +0300 Subject: [PATCH] refactoring --- .../Espo/Classes/ConsoleCommands/Import.php | 17 +- application/Espo/Controllers/Import.php | 126 +++---- application/Espo/Entities/Import.php | 37 ++ application/Espo/Services/Import.php | 315 +--------------- application/Espo/Tools/Import/Import.php | 37 +- .../Espo/Tools/Import/Jobs/RunIdle.php | 87 +++++ application/Espo/Tools/Import/Service.php | 342 ++++++++++++++++++ 7 files changed, 541 insertions(+), 420 deletions(-) create mode 100644 application/Espo/Tools/Import/Jobs/RunIdle.php create mode 100644 application/Espo/Tools/Import/Service.php diff --git a/application/Espo/Classes/ConsoleCommands/Import.php b/application/Espo/Classes/ConsoleCommands/Import.php index dfa6c1cd34..b87e6b944c 100644 --- a/application/Espo/Classes/ConsoleCommands/Import.php +++ b/application/Espo/Classes/ConsoleCommands/Import.php @@ -29,8 +29,9 @@ namespace Espo\Classes\ConsoleCommands; +use Espo\Tools\Import\Service; + use Espo\Core\{ - ServiceFactory, Console\Command, Console\Params, Console\IO, @@ -40,11 +41,11 @@ use Throwable; class Import implements Command { - protected $serviceFactory; + private $service; - public function __construct(ServiceFactory $serviceFactory) + public function __construct(Service $service) { - $this->serviceFactory = $serviceFactory; + $this->service = $service; } public function run(Params $params, IO $io) : void @@ -56,8 +57,6 @@ class Import implements Command $forceResume = $params->hasFlag('resume'); $revert = $params->hasFlag('revert'); - $service = $this->serviceFactory->create('Import'); - if (!$id && $filePath) { if (!$paramsId) { $io->writeLine("You need to specify --params-id option."); @@ -74,7 +73,7 @@ class Import implements Command $contents = file_get_contents($filePath); try { - $result = $service->importFileWithParamsId($contents, $paramsId); + $result = $this->service->importContentsWithParamsId($contents, $paramsId); $resultId = $result->id; $countCreated = $result->countCreated; @@ -95,7 +94,7 @@ class Import implements Command $io->writeLine("Reverting import..."); try { - $service->revert($id); + $this->service->revert($id); } catch (Throwable $e) { $io->writeLine("Error occurred: " . $e->getMessage() . ""); @@ -112,7 +111,7 @@ class Import implements Command $io->writeLine("Running import, this may take a while..."); try { - $result = $service->importById($id, true, $forceResume); + $result = $this->service->importById($id, true, $forceResume); } catch (Throwable $e) { $io->writeLine("Error occurred: " . $e->getMessage() . ""); diff --git a/application/Espo/Controllers/Import.php b/application/Espo/Controllers/Import.php index 7e2e09ca92..55b1e790f2 100644 --- a/application/Espo/Controllers/Import.php +++ b/application/Espo/Controllers/Import.php @@ -33,6 +33,7 @@ use Espo\Core\Exceptions\Forbidden; use Espo\Core\Exceptions\BadRequest; use Espo\Tools\Import\Params as ImportParams; +use Espo\Tools\Import\Service as Service; use Espo\Core\{ Controllers\Record, @@ -40,29 +41,43 @@ use Espo\Core\{ Api\Response, }; -use StdClass; +use Espo\Core\Di\InjectableFactoryAware; +use Espo\Core\Di\InjectableFactorySetter; + +use stdClass; class Import extends Record + + implements InjectableFactoryAware { + use InjectableFactorySetter; + protected function checkAccess(): bool { return $this->acl->check('Import'); } - public function postActionUploadFile(Request $request): StdClass + private function getImportService(): Service + { + return $this->injectableFactory->create(Service::class); + } + + public function postActionUploadFile(Request $request): stdClass { $contents = $request->getBodyContents(); - $attachmentId = $this->getService('Import')->uploadFile($contents); + $attachmentId = $this->getImportService()->uploadFile($contents); - return (object) ['attachmentId' => $attachmentId]; + return (object) [ + 'attachmentId' => $attachmentId + ]; } public function postActionRevert(Request $request): bool { $data = $request->getParsedBody(); - $this->getService('Import')->revert($data->id); + $this->getImportService()->revert($data->id); return true; } @@ -75,97 +90,36 @@ class Import extends Record throw new BadRequest(); } - $this->getService('Import')->removeDuplicates($data->id); + $this->getImportService()->removeDuplicates($data->id); return true; } - public function postActionCreate(Request $request, Response $response): StdClass + public function postActionCreate(Request $request, Response $response): stdClass { $data = $request->getParsedBody(); - if (!isset($data->delimiter)) { - throw new BadRequest(); + $entityType = $data->entityType ?? null; + $attributeList = $data->attributeList ?? null; + $attachmentId = $data->attachmentId ?? null; + + if (!is_array($attributeList)) { + throw new BadRequest("No attributeList."); } - if (!isset($data->textQualifier)) { - throw new BadRequest(); + if (!$attachmentId) { + throw new BadRequest("No attachmentId."); } - if (!isset($data->dateFormat)) { - throw new BadRequest(); + if (!$entityType) { + throw new BadRequest("No entityType."); } - if (!isset($data->timeFormat)) { - throw new BadRequest(); - } + $params = ImportParams::fromRaw($data); - if (!isset($data->personNameFormat)) { - throw new BadRequest(); - } - - if (!isset($data->decimalMark)) { - throw new BadRequest(); - } - - if (!isset($data->defaultValues)) { - throw new BadRequest(); - } - - if (!isset($data->action)) { - throw new BadRequest(); - } - - if (!isset($data->attachmentId)) { - throw new BadRequest(); - } - - if (!isset($data->entityType)) { - throw new BadRequest(); - } - - if (!isset($data->attributeList)) { - throw new BadRequest(); - } - - $timezone = 'UTC'; - if (isset($data->timezone)) { - $timezone = $data->timezone; - } - - $rawParams = [ - 'headerRow' => !empty($data->headerRow), - 'delimiter' => $data->delimiter, - 'textQualifier' => $data->textQualifier, - 'dateFormat' => $data->dateFormat, - 'timeFormat' => $data->timeFormat, - 'timezone' => $timezone, - 'personNameFormat' => $data->personNameFormat, - 'decimalMark' => $data->decimalMark, - 'currency' => $data->currency, - 'defaultValues' => $data->defaultValues, - 'action' => $data->action, - 'skipDuplicateChecking' => !empty($data->skipDuplicateChecking), - 'idleMode' => !empty($data->idleMode), - 'silentMode' => !empty($data->silentMode), - 'manualMode' => !empty($data->manualMode), - ]; - - if (property_exists($data, 'updateBy')) { - $rawParams['updateBy'] = $data->updateBy; - } - - $params = ImportParams::fromRaw($rawParams); - - $attachmentId = $data->attachmentId; - - if (!$this->acl->check($data->entityType, 'edit')) { - throw new Forbidden(); - } - - return $this->getService('Import')->import( - $data->entityType, - $data->attributeList, + return $this->getImportService()->import( + $entityType, + $attributeList, $attachmentId, $params ); @@ -175,11 +129,15 @@ class Import extends Record { $data = $request->getParsedBody(); - if (empty($data->id) || empty($data->entityType) || empty($data->entityId)) { + if ( + empty($data->id) || + empty($data->entityType) || + empty($data->entityId) + ) { throw new BadRequest(); } - $this->getService('Import')->unmarkAsDuplicate($data->id, $data->entityType, $data->entityId); + $this->getImportService()->unmarkAsDuplicate($data->id, $data->entityType, $data->entityId); return true; } diff --git a/application/Espo/Entities/Import.php b/application/Espo/Entities/Import.php index 94380e56ba..6a67658e4f 100644 --- a/application/Espo/Entities/Import.php +++ b/application/Espo/Entities/Import.php @@ -29,7 +29,44 @@ namespace Espo\Entities; +use stdClass; + class Import extends \Espo\Core\ORM\Entity { public const ENTITY_TYPE = 'Import'; + + public const STATUS_STANDBY = 'Standby'; + + public const STATUS_IN_PROCESS = 'In Process'; + + public const STATUS_FAILED = 'Failed'; + + public const STATUS_PENDING = 'Pending'; + + public const STATUS_COMPLETE = 'Complete'; + + public function getStatus(): ?string + { + return $this->get('status'); + } + + public function getParams(): ?stdClass + { + return $this->get('params'); + } + + public function getFileId(): ?string + { + return $this->get('fileId'); + } + + public function getTargetEntityType(): ?string + { + return $this->get('entityType'); + } + + public function getTargetAttributeList(): ?array + { + return $this->get('attributeList'); + } } diff --git a/application/Espo/Services/Import.php b/application/Espo/Services/Import.php index 2491177841..4a88d8a859 100644 --- a/application/Espo/Services/Import.php +++ b/application/Espo/Services/Import.php @@ -31,33 +31,15 @@ namespace Espo\Services; use Espo\Core\{ Exceptions\Forbidden, - Exceptions\NotFound, - Exceptions\Error, Record\Collection as RecordCollection, - Di, Select\SearchParams, FieldProcessing\ListLoadProcessor, }; -use Espo\{ - Services\Record, - Tools\Import\Import as ImportTool, - Tools\Import\Params as ImportParams, -}; +use Espo\Services\Record; -use StdClass; -use DateTime; - -class Import extends Record implements - - Di\FileManagerAware, - Di\FileStorageManagerAware +class Import extends Record { - use Di\FileManagerSetter; - use Di\FileStorageManagerSetter; - - const REVERT_PERMANENTLY_REMOVE_PERIOD_DAYS = 2; - public function findLinked(string $id, string $link, SearchParams $searchParams): RecordCollection { if (!in_array($link, ['imported', 'duplicates', 'updated'])) { @@ -98,297 +80,4 @@ class Import extends Record implements return new RecordCollection($collection, $total); } - - public function uploadFile(string $contents): string - { - $attachment = $this->getEntityManager()->getEntity('Attachment'); - - $attachment->set('type', 'text/csv'); - $attachment->set('role', 'Import File'); - $attachment->set('name', 'import-file.csv'); - $attachment->set('contents', $contents); - - $this->getEntityManager()->saveEntity($attachment); - - return $attachment->id; - } - - public function revert(string $id): void - { - $import = $this->entityManager->getEntity('Import', $id); - - if (empty($import)) { - throw new NotFound("Could not find import record."); - } - - if (!$this->acl->check($import, 'delete')) { - throw new Forbidden("No access import record."); - } - - $importEntityList = $this->entityManager->getRepository('ImportEntity') - ->sth() - ->where([ - 'importId' => $import->id, - 'isImported' => true, - ]) - ->find(); - - $removeFromDb = false; - - $createdAt = $import->get('createdAt'); - - if ($createdAt) { - $dtNow = new DateTime(); - $createdAtDt = new DateTime($createdAt); - - $dayDiff = ($dtNow->getTimestamp() - $createdAtDt->getTimestamp()) / 60 / 60 / 24; - - if ($dayDiff < self::REVERT_PERMANENTLY_REMOVE_PERIOD_DAYS) { - $removeFromDb = true; - } - } - - foreach ($importEntityList as $importEntity) { - $entityType = $importEntity->get('entityType'); - $entityId = $importEntity->get('entityId'); - - if (!$entityType || !$entityId) { - continue; - } - - if (!$this->entityManager->hasRepository($entityType)) { - continue; - } - - $entity = $this->entityManager->getRepository($entityType) - ->select(['id']) - ->where(['id' => $entityId]) - ->findOne(); - - if (!$entity) { - continue; - } - - $this->entityManager->removeEntity($entity, [ - 'noStream' => true, - 'noNotifications' => true, - 'import' => true, - 'silent' => true, - ]); - - if ($removeFromDb) { - $this->entityManager->getRepository($entityType)->deleteFromDb($entityId); - } - } - - $this->getEntityManager()->removeEntity($import); - - $this->processActionHistoryRecord('delete', $import); - } - - public function removeDuplicates(string $id): void - { - $import = $this->entityManager->getEntity('Import', $id); - - if (empty($import)) { - throw new NotFound(); - } - - if (!$this->acl->check($import, 'delete')) { - throw new Forbidden(); - } - - $importEntityList = $this->entityManager->getRepository('ImportEntity') - ->sth() - ->where([ - 'importId' => $import->id, - 'isDuplicate' => true, - ]) - ->find(); - - foreach ($importEntityList as $importEntity) { - $entityType = $importEntity->get('entityType'); - $entityId = $importEntity->get('entityId'); - - if (!$entityType || !$entityId) { - continue; - } - - if (!$this->entityManager->hasRepository($entityType)) { - continue; - } - - $entity = $this->entityManager->getRepository($entityType) - ->select(['id']) - ->where(['id' => $entityId]) - ->findOne(); - - if (!$entity) { - continue; - } - - $this->entityManager->removeEntity($entity, [ - 'noStream' => true, - 'noNotifications' => true, - 'import' => true, - 'silent' => true, - ]); - - $this->entityManager->getRepository($entityType)->deleteFromDb($entityId); - } - } - - protected function createImportTool(): ImportTool - { - return $this->injectableFactory->create(ImportTool::class); - } - - public function jobRunIdleImport(StdClass $data): StdClass - { - if ( - empty($data->userId) || - empty($data->userId) || - !isset($data->importAttributeList) || - !isset($data->params) || - !isset($data->entityType) - ) { - throw new Error("Import: Bad job data."); - } - - $entityType = $data->entityType; - $attachmentId = $data->attachmentId; - $importId = $data->importId; - $importAttributeList = $data->importAttributeList; - $userId = $data->userId; - - $params = ImportParams::fromRaw($data->params); - - $user = $this->getEntityManager()->getEntity('User', $userId); - - if (!$user) { - throw new Error("Import: User not found."); - } - - if (!$user->get('isActive')) { - throw new Error("Import: User is not active."); - } - - return $this->createImportTool() - ->setEntityType($entityType) - ->setAttributeList($importAttributeList) - ->setAttachmentId($attachmentId) - ->setParams($params) - ->setId($importId) - ->setUser($user) - ->run(); - } - - public function importById(string $id, bool $startFromLastIndex = false, bool $forceResume = false): StdClass - { - $import = $this->getEntityManager()->getEntity('Import', $id); - - if (!$import) { - throw new NotFound("Import '{$id}' not found."); - } - - $status = $import->get('status'); - - if ($status !== 'Standby') { - if (in_array($status, ['In Process', 'Failed'])) { - if (!$forceResume) { - throw new Forbidden("Import has '{$status}' status. Use -r flag to force resume."); - } - } else { - throw new Forbidden("Can't run import with '{$status}' status."); - } - } - - $entityType = $import->get('entityType'); - $attributeList = $import->get('attributeList') ?? []; - - $params = ImportParams - ::fromRaw($import->get('params')) - ->withStartFromLastIndex($startFromLastIndex); - - $attachmentId = $import->get('fileId'); - - return $this->createImportTool() - ->setEntityType($entityType) - ->setAttributeList($attributeList) - ->setAttachmentId($attachmentId) - ->setParams($params) - ->setId($id) - ->run(); - } - - public function import( - string $entityType, - array $attributeList, - string $attachmentId, - ImportParams $params - ): StdClass { - - $result = $this->createImportTool() - ->setEntityType($entityType) - ->setAttributeList($attributeList) - ->setAttachmentId($attachmentId) - ->setParams($params) - ->run(); - - $id = $result->id ?? null; - - if ($id) { - $import = $this->entityManager->getEntity('Import', $id); - - if ($import) { - $this->processActionHistoryRecord('create', $import); - } - } - - return $result; - } - - public function importFileWithParamsId(string $contents, string $importParamsId): StdClass - { - if (!$contents) { - throw new Error("File contents is empty."); - } - - $source = $this->getEntityManager()->getEntity('Import', $importParamsId); - - if (!$source) { - throw new Error("Import {$importParamsId} not found."); - } - - $entityType = $source->get('entityType'); - $attributeList = $source->get('attributeList') ?? []; - - $params = ImportParams::fromRaw($source->get('params')) - ->withIdleMode(false) - ->withManualMode(false); - - $attachmentId = $this->uploadFile($contents); - - return $this->import($entityType, $attributeList, $attachmentId, $params); - } - - public function unmarkAsDuplicate(string $importId, string $entityType, string $entityId): void - { - $e = $this->getEntityManager() - ->getRepository('ImportEntity') - ->where([ - 'importId' => $importId, - 'entityType' => $entityType, - 'entityId' => $entityId, - ]) - ->findOne(); - - if (!$e) { - throw new NotFound(); - } - - $e->set('isDuplicate', false); - - $this->getEntityManager()->saveEntity($e); - } } diff --git a/application/Espo/Tools/Import/Import.php b/application/Espo/Tools/Import/Import.php index 94433988bf..92392ed9dd 100644 --- a/application/Espo/Tools/Import/Import.php +++ b/application/Espo/Tools/Import/Import.php @@ -29,6 +29,10 @@ namespace Espo\Tools\Import; +use Espo\Core\Job\JobSchedulerFactory; + +use Espo\Tools\Import\Jobs\RunIdle; + use Espo\Core\{ Exceptions\Error, Utils\Json, @@ -45,6 +49,7 @@ use Espo\ORM\Entity; use Espo\ORM\EntityManager; use Espo\Entities\User; +use Espo\Entities\Import as ImportEntity; use stdClass; use DateTime; @@ -87,6 +92,8 @@ class Import private $fileStorageManager; + private $jobSchedulerFactory; + private $log; public function __construct( @@ -97,6 +104,7 @@ class Import User $user, FileStorageManager $fileStorageManager, RecordServiceContainer $recordServiceContainer, + JobSchedulerFactory $jobSchedulerFactory, Log $log ) { $this->aclManager = $aclManager; @@ -106,6 +114,7 @@ class Import $this->user = $user; $this->fileStorageManager = $fileStorageManager; $this->recordServiceContainer = $recordServiceContainer; + $this->jobSchedulerFactory = $jobSchedulerFactory; $this->log = $log; $this->params = Params::create(); @@ -247,25 +256,25 @@ class Import $startFromIndex = $import->get('lastIndex'); } - $import->set('status', 'In Process'); + $import->set('status', ImportEntity::STATUS_IN_PROCESS); } else { - $import = $this->entityManager->getEntity('Import'); + $import = $this->entityManager->getEntity(ImportEntity::ENTITY_TYPE); $import->set([ 'entityType' => $this->entityType, 'fileId' => $this->attachmentId, ]); - $import->set('status', 'In Process'); + $import->set('status', ImportEntity::STATUS_IN_PROCESS); if ($params->isManualMode()) { $params = $params->withIdleMode(false); - $import->set('status', 'Standby'); + $import->set('status', ImportEntity::STATUS_STANDBY); } else if ($params->isIdleMode()) { - $import->set('status', 'Pending'); + $import->set('status', ImportEntity::STATUS_PENDING); } $import->set('params', $params->getRaw()); @@ -284,18 +293,18 @@ class Import } if ($params->isIdleMode()) { - $this->entityManager->createEntity('Job', [ - 'serviceName' => 'Import', - 'methodName' => 'jobRunIdleImport', - 'data' => [ + $this->jobSchedulerFactory + ->create() + ->setClassName(RunIdle::class) + ->setData([ 'entityType' => $this->entityType, 'params' => $params->withIdleMode(false)->getRaw(), 'attachmentId' => $this->attachmentId, 'importAttributeList' => $attributeList, 'importId' => $import->getId(), 'userId' => $this->user->getId(), - ], - ]); + ]) + ->schedule(); return (object) [ 'id' => $import->getId(), @@ -367,15 +376,15 @@ class Import } catch (Exception $e) { $this->log->error('Import Error: '. $e->getMessage()); - $import->set('status', 'Failed'); + $import->set('status', ImportEntity::STATUS_FAILED); } - $import->set('status', 'Complete'); + $import->set('status', ImportEntity::STATUS_COMPLETE); $this->entityManager->saveEntity($import); return (object) [ - 'id' => $import->id, + 'id' => $import->getId(), 'countCreated' => count($result->importedIds), 'countUpdated' => count($result->updatedIds), ]; diff --git a/application/Espo/Tools/Import/Jobs/RunIdle.php b/application/Espo/Tools/Import/Jobs/RunIdle.php new file mode 100644 index 0000000000..615b737f09 --- /dev/null +++ b/application/Espo/Tools/Import/Jobs/RunIdle.php @@ -0,0 +1,87 @@ +factory = $factory; + $this->entityManager = $entityManager; + } + + public function run(JobData $data): void + { + $raw = $data->getRaw(); + + $entityType = $raw->entityType; + $attachmentId = $raw->attachmentId; + $importId = $raw->importId; + $importAttributeList = $raw->importAttributeList; + $userId = $raw->userId; + + $params = ImportParams::fromRaw($raw->params); + + $user = $this->entityManager->getEntity(User::ENTITY_TYPE, $userId); + + if (!$user) { + throw new Error("Import: User not found."); + } + + if (!$user->get('isActive')) { + throw new Error("Import: User is not active."); + } + + $this->factory + ->create() + ->setEntityType($entityType) + ->setAttributeList($importAttributeList) + ->setAttachmentId($attachmentId) + ->setParams($params) + ->setId($importId) + ->setUser($user) + ->run(); + } +} diff --git a/application/Espo/Tools/Import/Service.php b/application/Espo/Tools/Import/Service.php new file mode 100644 index 0000000000..997aac751c --- /dev/null +++ b/application/Espo/Tools/Import/Service.php @@ -0,0 +1,342 @@ +factory = $factory; + $this->recordServiceContainer = $recordServiceContainer; + $this->entityManager = $entityManager; + $this->acl = $acl; + } + + public function import( + string $entityType, + array $attributeList, + string $attachmentId, + Params $params + ): stdClass { + + if (!$this->acl->check($entityType, Table::ACTION_CREATE)) { + throw new Forbidden("No create access for '{$entityType}'."); + } + + $result = $this->factory + ->create() + ->setEntityType($entityType) + ->setAttributeList($attributeList) + ->setAttachmentId($attachmentId) + ->setParams($params) + ->run(); + + $id = $result->id ?? null; + + if ($id) { + $import = $this->entityManager->getEntity(ImportEntity::ENTITY_TYPE, $id); + + if ($import) { + $this->recordServiceContainer + ->get(ImportEntity::ENTITY_TYPE) + ->processActionHistoryRecord('create', $import); + } + } + + return $result; + } + + public function importContentsWithParamsId(string $contents, string $importParamsId): stdClass + { + if (!$contents) { + throw new Error("Contents is empty."); + } + + /** @var ImportEntity $source */ + $source = $this->entityManager->getEntity(ImportEntity::ENTITY_TYPE, $importParamsId); + + if (!$source) { + throw new Error("Import '{$importParamsId}' not found."); + } + + $entityType = $source->getTargetEntityType(); + $attributeList = $source->getTargetAttributeList() ?? []; + + $params = ImportParams::fromRaw($source->getParams()) + ->withIdleMode(false) + ->withManualMode(false); + + $attachmentId = $this->uploadFile($contents); + + return $this->import($entityType, $attributeList, $attachmentId, $params); + } + + public function importById(string $id, bool $startFromLastIndex = false, bool $forceResume = false): stdClass + { + /** @var ImportEntity $import */ + $import = $this->entityManager->getEntity(ImportEntity::ENTITY_TYPE, $id); + + if (!$import) { + throw new NotFound("Import '{$id}' not found."); + } + + $status = $import->getStatus(); + + if ($status !== ImportEntity::STATUS_STANDBY) { + if (!in_array($status, [ImportEntity::STATUS_IN_PROCESS, ImportEntity::STATUS_FAILED])) { + throw new Forbidden("Can't run import with '{$status}' status."); + } + + if (!$forceResume) { + throw new Forbidden("Import has '{$status}' status. Use -r flag to force resume."); + } + } + + $entityType = $import->getTargetEntityType(); + $attributeList = $import->getTargetAttributeList() ?? []; + + $params = ImportParams + ::fromRaw($import->getParams()) + ->withStartFromLastIndex($startFromLastIndex); + + $attachmentId = $import->getFileId(); + + return $this->factory + ->create() + ->setEntityType($entityType) + ->setAttributeList($attributeList) + ->setAttachmentId($attachmentId) + ->setParams($params) + ->setId($id) + ->run(); + } + + public function revert(string $id): void + { + $import = $this->entityManager->getEntity('Import', $id); + + if (!$import) { + throw new NotFound("Could not find import record."); + } + + if (!$this->acl->checkEntityDelete($import)) { + throw new Forbidden("No access import record."); + } + + $importEntityList = $this->entityManager + ->getRDBRepository('ImportEntity') + ->sth() + ->where([ + 'importId' => $import->getId(), + 'isImported' => true, + ]) + ->find(); + + $removeFromDb = false; + + $createdAt = $import->get('createdAt'); + + if ($createdAt) { + $dtNow = new DateTime(); + $createdAtDt = new DateTime($createdAt); + + $dayDiff = ($dtNow->getTimestamp() - $createdAtDt->getTimestamp()) / 60 / 60 / 24; + + if ($dayDiff < self::REVERT_PERMANENTLY_REMOVE_PERIOD_DAYS) { + $removeFromDb = true; + } + } + + foreach ($importEntityList as $importEntity) { + $entityType = $importEntity->get('entityType'); + $entityId = $importEntity->get('entityId'); + + if (!$entityType || !$entityId) { + continue; + } + + if (!$this->entityManager->hasRepository($entityType)) { + continue; + } + + $entity = $this->entityManager + ->getRDBRepository($entityType) + ->select(['id']) + ->where(['id' => $entityId]) + ->findOne(); + + if (!$entity) { + continue; + } + + $this->entityManager->removeEntity($entity, [ + 'noStream' => true, + 'noNotifications' => true, + 'import' => true, + 'silent' => true, + ]); + + if ($removeFromDb) { + $this->entityManager + ->getRDBRepository($entityType) + ->deleteFromDb($entityId); + } + } + + $this->entityManager->removeEntity($import); + + $this->recordServiceContainer + ->get(ImportEntity::ENTITY_TYPE) + ->processActionHistoryRecord('delete', $import); + } + + /** + * @return Attachment ID. + */ + public function uploadFile(string $contents): string + { + $attachment = $this->entityManager->getEntity(Attachment::ENTITY_TYPE); + + $attachment->set('type', 'text/csv'); + $attachment->set('role', 'Import File'); + $attachment->set('name', 'import-file.csv'); + $attachment->set('contents', $contents); + + $this->entityManager->saveEntity($attachment); + + return $attachment->getId(); + } + + public function removeDuplicates(string $id): void + { + $import = $this->entityManager->getEntity(ImportEntity::ENTITY_TYPE, $id); + + if (!$import) { + throw new NotFound("Import '{$id}' not found."); + } + + if (!$this->acl->checkEntityDelete($import)) { + throw new Forbidden("No delete access."); + } + + $importEntityList = $this->entityManager + ->getRDBRepository('ImportEntity') + ->sth() + ->where([ + 'importId' => $import->getId(), + 'isDuplicate' => true, + ]) + ->find(); + + foreach ($importEntityList as $importEntity) { + $entityType = $importEntity->get('entityType'); + $entityId = $importEntity->get('entityId'); + + if (!$entityType || !$entityId) { + continue; + } + + if (!$this->entityManager->hasRepository($entityType)) { + continue; + } + + $entity = $this->entityManager + ->getRDBRepository($entityType) + ->select(['id']) + ->where(['id' => $entityId]) + ->findOne(); + + if (!$entity) { + continue; + } + + $this->entityManager->removeEntity($entity, [ + 'noStream' => true, + 'noNotifications' => true, + 'import' => true, + 'silent' => true, + ]); + + $this->entityManager + ->getRDBRepository($entityType) + ->deleteFromDb($entityId); + } + } + + public function unmarkAsDuplicate(string $importId, string $entityType, string $entityId): void + { + $entity = $this->entityManager + ->getRDBRepository('ImportEntity') + ->where([ + 'importId' => $importId, + 'entityType' => $entityType, + 'entityId' => $entityId, + ]) + ->findOne(); + + if (!$entity) { + throw new NotFound(); + } + + $entity->set('isDuplicate', false); + + $this->entityManager->saveEntity($entity); + } +}