diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 1d01cf86e..a770c6376 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1,15 +1,5 @@ parameters: ignoreErrors: - - - message: '#^Property SwagMigrationAssistant\\Migration\\Converter\\Converter\:\:\$mainMapping \(array\{id\: string, connectionId\: string, oldIdentifier\: string\|null, entityUuid\: string\|null, entityValue\: string\|null, checksum\: string\|null, additionalData\: array\\|null\}\|null\) does not accept array\{additionalData\: non\-empty\-array\\}\|array\{id\: string, connectionId\: string, oldIdentifier\: string\|null, entityUuid\: string\|null, entityValue\: string\|null, checksum\: string\|null, additionalData\: non\-empty\-array\\}\.$#' - count: 1 - path: src/Migration/Converter/Converter.php - - - - message: '#^Property SwagMigrationAssistant\\Migration\\Converter\\Converter\:\:\$mainMapping \(array\{id\: string, connectionId\: string, oldIdentifier\: string\|null, entityUuid\: string\|null, entityValue\: string\|null, checksum\: string\|null, additionalData\: array\\|null\}\|null\) does not accept array\{checksum\: string\}\|array\{id\: string, connectionId\: string, oldIdentifier\: string\|null, entityUuid\: string\|null, entityValue\: string\|null, checksum\: string, additionalData\: array\\|null\}\.$#' - count: 1 - path: src/Migration/Converter/Converter.php - - message: "#^swag_migration_data\\.run association has a configured autoload\\=\\=\\=true, this is forbidden for platform integrations$#" count: 1 @@ -820,11 +810,6 @@ parameters: count: 1 path: src/Profile/Shopware6/Converter/NumberRangeConverter.php - - - message: "#^Method SwagMigrationAssistant\\\\Profile\\\\Shopware6\\\\Converter\\\\ProductConverter\\:\\:checkDefaultCurrency\\(\\) has parameter \\$source with no value type specified in iterable type array\\.$#" - count: 1 - path: src/Profile/Shopware6/Converter/ProductConverter.php - - message: "#^Method SwagMigrationAssistant\\\\Profile\\\\Shopware6\\\\Converter\\\\PropertyGroupConverter\\:\\:convertOption\\(\\) has parameter \\$option with no value type specified in iterable type array\\.$#" count: 1 diff --git a/src/DependencyInjection/migration.xml b/src/DependencyInjection/migration.xml index b67dc462e..0fb9337d4 100644 --- a/src/DependencyInjection/migration.xml +++ b/src/DependencyInjection/migration.xml @@ -234,13 +234,6 @@ - - - - - - - @@ -318,14 +311,6 @@ - - - - - - - - @@ -407,8 +392,11 @@ - + + + + diff --git a/src/Migration/Logging/Log/CannotGetFileRunLog.php b/src/Migration/Logging/Log/CannotGetFileRunLog.php index 56397ae6e..e66336cee 100644 --- a/src/Migration/Logging/Log/CannotGetFileRunLog.php +++ b/src/Migration/Logging/Log/CannotGetFileRunLog.php @@ -7,7 +7,6 @@ namespace SwagMigrationAssistant\Migration\Logging\Log; -use GuzzleHttp\Exception\RequestException; use Shopware\Core\Framework\Log\Package; #[Package('fundamentals@after-sales')] @@ -18,7 +17,7 @@ public function __construct( string $entity, string $sourceId, private readonly string $uri, - private readonly ?RequestException $requestException = null, + private readonly ?\Throwable $error = null, ) { parent::__construct($runId, $entity, $sourceId); } @@ -71,10 +70,10 @@ public function getDescription(): string $args['sourceId'] ); - if ($this->requestException !== null) { + if ($this->error !== null) { $description .= \sprintf( - ' The following request error occurred: %s', - $this->requestException->getMessage() + ' The following error occurred: %s', + $this->error->getMessage() ); } diff --git a/src/Migration/Media/Processor/HttpDownloadServiceBase.php b/src/Migration/Media/Processor/HttpDownloadServiceBase.php index 2f02e92d6..111781050 100644 --- a/src/Migration/Media/Processor/HttpDownloadServiceBase.php +++ b/src/Migration/Media/Processor/HttpDownloadServiceBase.php @@ -9,7 +9,6 @@ use Doctrine\DBAL\Connection; use GuzzleHttp\Exception\RequestException; -use GuzzleHttp\Promise; use GuzzleHttp\Promise\PromiseInterface; use GuzzleHttp\Promise\Utils; use Psr\Http\Message\ResponseInterface; @@ -30,7 +29,7 @@ use SwagMigrationAssistant\Migration\Media\MediaFileProcessorInterface; use SwagMigrationAssistant\Migration\Media\MediaProcessWorkloadStruct; use SwagMigrationAssistant\Migration\Media\SwagMigrationMediaFileCollection; -use SwagMigrationAssistant\Migration\MessageQueue\Handler\ProcessMediaHandler; +use SwagMigrationAssistant\Migration\MessageQueue\Handler\Processor\MediaProcessingProcessor; use SwagMigrationAssistant\Migration\MigrationContextInterface; /** @@ -114,7 +113,7 @@ function (MediaProcessWorkloadStruct $work) use ($uuid) { $work->setAdditionalData($additionalData); $work->setErrorCount($work->getErrorCount() + 1); - if ($work->getErrorCount() > ProcessMediaHandler::MEDIA_ERROR_THRESHOLD) { + if ($work->getErrorCount() > MediaProcessingProcessor::MEDIA_ERROR_THRESHOLD) { $failureUuids[] = $uuid; $work->setState(MediaProcessWorkloadStruct::ERROR_STATE); $this->loggingService->addLogEntry(new CannotGetFileRunLog( diff --git a/src/Migration/MessageQueue/Handler/ProcessMediaHandler.php b/src/Migration/MessageQueue/Handler/ProcessMediaHandler.php deleted file mode 100644 index 6839764c6..000000000 --- a/src/Migration/MessageQueue/Handler/ProcessMediaHandler.php +++ /dev/null @@ -1,131 +0,0 @@ - - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace SwagMigrationAssistant\Migration\MessageQueue\Handler; - -use Shopware\Core\Framework\Context; -use Shopware\Core\Framework\DataAbstractionLayer\EntityRepository; -use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria; -use Shopware\Core\Framework\Log\Package; -use SwagMigrationAssistant\Exception\MigrationException; -use SwagMigrationAssistant\Exception\NoConnectionFoundException; -use SwagMigrationAssistant\Migration\Logging\Log\ExceptionRunLog; -use SwagMigrationAssistant\Migration\Logging\Log\ProcessorNotFoundLog; -use SwagMigrationAssistant\Migration\Logging\LoggingServiceInterface; -use SwagMigrationAssistant\Migration\Media\MediaFileProcessorInterface; -use SwagMigrationAssistant\Migration\Media\MediaFileProcessorRegistryInterface; -use SwagMigrationAssistant\Migration\Media\MediaProcessWorkloadStruct; -use SwagMigrationAssistant\Migration\MessageQueue\Message\ProcessMediaMessage; -use SwagMigrationAssistant\Migration\MigrationContextFactoryInterface; -use SwagMigrationAssistant\Migration\MigrationContextInterface; -use SwagMigrationAssistant\Migration\Run\SwagMigrationRunCollection; -use SwagMigrationAssistant\Migration\Run\SwagMigrationRunEntity; -use Symfony\Component\Messenger\Attribute\AsMessageHandler; - -/** - * @internal - */ -#[AsMessageHandler] -#[Package('fundamentals@after-sales')] -final class ProcessMediaHandler -{ - final public const MEDIA_ERROR_THRESHOLD = 3; - - /** - * @param EntityRepository $migrationRunRepo - */ - public function __construct( - private readonly EntityRepository $migrationRunRepo, - private readonly MediaFileProcessorRegistryInterface $mediaFileProcessorRegistry, - private readonly LoggingServiceInterface $loggingService, - private readonly MigrationContextFactoryInterface $migrationContextFactory, - ) { - } - - /** - * @throws MigrationException - */ - public function __invoke(ProcessMediaMessage $message): void - { - $context = $message->getContext(); - - $run = $this->migrationRunRepo->search(new Criteria([$message->getRunId()]), $context)->first(); - - if (!$run instanceof SwagMigrationRunEntity) { - throw MigrationException::entityNotExists(SwagMigrationRunEntity::class, $message->getRunId()); - } - - $connection = $run->getConnection(); - if ($connection === null) { - throw MigrationException::entityNotExists(SwagMigrationRunEntity::class, $message->getRunId()); - } - - $migrationContext = $this->migrationContextFactory->create($run, 0, 0, $message->getEntityName()); - - if ($migrationContext === null) { - throw MigrationException::entityNotExists(SwagMigrationRunEntity::class, $message->getRunId()); - } - - $workload = []; - foreach ($message->getMediaFileIds() as $mediaFileId) { - $workload[] = new MediaProcessWorkloadStruct( - $mediaFileId, - $message->getRunId(), - MediaProcessWorkloadStruct::IN_PROGRESS_STATE - ); - } - - try { - $processor = $this->mediaFileProcessorRegistry->getProcessor($migrationContext); - $workload = $processor->process($migrationContext, $context, $workload); - $this->processFailures($context, $migrationContext, $processor, $workload); - } catch (NoConnectionFoundException $e) { - $this->loggingService->addLogEntry(new ProcessorNotFoundLog( - $message->getRunId(), - $message->getEntityName(), - $connection->getProfileName(), - $connection->getGatewayName() - )); - - $this->loggingService->saveLogging($context); - } catch (\Exception $e) { - $this->loggingService->addLogEntry(new ExceptionRunLog( - $message->getRunId(), - $message->getEntityName(), - $e - )); - - $this->loggingService->saveLogging($context); - } - } - - /** - * @param MediaProcessWorkloadStruct[] $workload - */ - private function processFailures( - Context $context, - MigrationContextInterface $migrationContext, - MediaFileProcessorInterface $processor, - array $workload, - ): void { - for ($i = 0; $i < self::MEDIA_ERROR_THRESHOLD; ++$i) { - $errorWorkload = []; - - foreach ($workload as $item) { - if ($item->getErrorCount() > 0) { - $errorWorkload[] = $item; - } - } - - if (empty($errorWorkload)) { - break; - } - - $workload = $processor->process($migrationContext, $context, $errorWorkload); - } - } -} diff --git a/src/Migration/MessageQueue/Handler/Processor/MediaProcessingProcessor.php b/src/Migration/MessageQueue/Handler/Processor/MediaProcessingProcessor.php index 2698a6a72..38968fcc2 100644 --- a/src/Migration/MessageQueue/Handler/Processor/MediaProcessingProcessor.php +++ b/src/Migration/MessageQueue/Handler/Processor/MediaProcessingProcessor.php @@ -7,10 +7,26 @@ namespace SwagMigrationAssistant\Migration\MessageQueue\Handler\Processor; +use Doctrine\DBAL\Connection; use Shopware\Core\Framework\Context; use Shopware\Core\Framework\DataAbstractionLayer\EntityRepository; +use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria; +use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter; +use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\MultiFilter; use Shopware\Core\Framework\Log\Package; +use Shopware\Core\Framework\Uuid\Uuid; +use SwagMigrationAssistant\Exception\DataSetNotFoundException; +use SwagMigrationAssistant\Exception\MigrationException; +use SwagMigrationAssistant\Exception\NoConnectionFoundException; use SwagMigrationAssistant\Migration\Data\SwagMigrationDataCollection; +use SwagMigrationAssistant\Migration\DataSelection\DataSet\DataSetRegistry; +use SwagMigrationAssistant\Migration\Logging\Log\DataSetNotFoundLog; +use SwagMigrationAssistant\Migration\Logging\Log\ExceptionRunLog; +use SwagMigrationAssistant\Migration\Logging\Log\ProcessorNotFoundLog; +use SwagMigrationAssistant\Migration\Logging\LoggingService; +use SwagMigrationAssistant\Migration\Media\MediaFileProcessorInterface; +use SwagMigrationAssistant\Migration\Media\MediaFileProcessorRegistryInterface; +use SwagMigrationAssistant\Migration\Media\MediaProcessWorkloadStruct; use SwagMigrationAssistant\Migration\Media\SwagMigrationMediaFileCollection; use SwagMigrationAssistant\Migration\MessageQueue\Message\MigrationProcessMessage; use SwagMigrationAssistant\Migration\MigrationContextInterface; @@ -19,12 +35,14 @@ use SwagMigrationAssistant\Migration\Run\RunTransitionServiceInterface; use SwagMigrationAssistant\Migration\Run\SwagMigrationRunCollection; use SwagMigrationAssistant\Migration\Run\SwagMigrationRunEntity; -use SwagMigrationAssistant\Migration\Service\MediaFileProcessorServiceInterface; use Symfony\Component\Messenger\MessageBusInterface; #[Package('fundamentals@after-sales')] class MediaProcessingProcessor extends AbstractProcessor { + final public const MEDIA_ERROR_THRESHOLD = 3; + final public const MESSAGE_SIZE = 10; + /** * @param EntityRepository $migrationRunRepo * @param EntityRepository $migrationDataRepo @@ -35,8 +53,11 @@ public function __construct( EntityRepository $migrationDataRepo, EntityRepository $migrationMediaFileRepo, RunTransitionServiceInterface $runTransitionService, - private readonly MediaFileProcessorServiceInterface $mediaFileProcessorService, private readonly MessageBusInterface $bus, + private readonly LoggingService $loggingService, + private readonly Connection $dbalConnection, + private readonly MediaFileProcessorRegistryInterface $mediaFileProcessorRegistry, + private readonly DataSetRegistry $dataSetRegistry, ) { parent::__construct( $migrationRunRepo, @@ -57,19 +78,182 @@ public function process( SwagMigrationRunEntity $run, MigrationProgress $progress, ): void { - $fileCount = $this->mediaFileProcessorService->processMediaFiles($migrationContext, $context); + $connection = $run->getConnection(); + if ($connection === null) { + throw MigrationException::entityNotExists(SwagMigrationRunEntity::class, $migrationContext->getRunUuid()); + } - if ($fileCount <= 0) { + $mediaFiles = $this->getMediaFiles($migrationContext); + if (empty($mediaFiles)) { $this->runTransitionService->transitionToRunStep($migrationContext->getRunUuid(), MigrationStep::CLEANUP); - $this->updateProgress($migrationContext->getRunUuid(), $progress, $context); $this->bus->dispatch(new MigrationProcessMessage($context, $migrationContext->getRunUuid())); return; } - $progress->setCurrentEntityProgress($progress->getCurrentEntityProgress() + $fileCount); - $progress->setProgress($progress->getProgress() + $fileCount); - $this->updateProgress($migrationContext->getRunUuid(), $progress, $context); + $currentDataSet = null; + $currentCount = 0; + $workload = []; + foreach ($mediaFiles as $mediaFile) { + if ($currentDataSet === null) { + try { + $currentDataSet = $this->dataSetRegistry->getDataSet($migrationContext, $mediaFile['entity']); + } catch (DataSetNotFoundException $e) { + $this->logDataSetNotFoundException($migrationContext, $mediaFile); + + continue; + } + } + + if ($currentDataSet::getEntity() !== $mediaFile['entity']) { + break; + } + + ++$currentCount; + + if ($currentCount > self::MESSAGE_SIZE) { + break; + } + + $workload[] = new MediaProcessWorkloadStruct( + $mediaFile['media_id'], + $run->getId(), + MediaProcessWorkloadStruct::IN_PROGRESS_STATE + ); + } + + \assert($currentDataSet !== null); + + try { + $processor = $this->mediaFileProcessorRegistry->getProcessor($migrationContext); + $workload = $processor->process($migrationContext, $context, $workload); + $this->processFailures($context, $migrationContext, $processor, $workload); + } catch (NoConnectionFoundException $e) { + $this->loggingService->addLogEntry(new ProcessorNotFoundLog( + $run->getId(), + $currentDataSet::getEntity(), + $connection->getProfileName(), + $connection->getGatewayName() + )); + + $this->loggingService->saveLogging($context); + } catch (\Throwable $e) { + $this->loggingService->addLogEntry(new ExceptionRunLog( + $run->getId(), + $currentDataSet::getEntity(), + $e + )); + + $this->loggingService->saveLogging($context); + } + + $this->loggingService->saveLogging($context); + + $progress->setCurrentEntityProgress($progress->getCurrentEntityProgress() + \count($workload)); + $progress->setProgress($progress->getProgress() + \count($workload)); + $this->updateProgress($run->getId(), $progress, $context); + + if ($this->isAllMediaProcessed($context, $migrationContext->getRunUuid())) { + $this->runTransitionService->transitionToRunStep($migrationContext->getRunUuid(), MigrationStep::CLEANUP); + } + $this->bus->dispatch(new MigrationProcessMessage($context, $migrationContext->getRunUuid())); } + + /** + * @return array> + */ + private function getMediaFiles(MigrationContextInterface $migrationContext): array + { + $queryBuilder = $this->dbalConnection->createQueryBuilder(); + + $result = $queryBuilder + ->select('*') + ->from('swag_migration_media_file') + ->where('run_id = :runId') + ->andWhere('written = 1') + ->orderBy('entity, file_size') + ->setFirstResult($migrationContext->getOffset()) + ->setMaxResults($migrationContext->getLimit()) + ->setParameter('runId', Uuid::fromHexToBytes($migrationContext->getRunUuid())) + ->executeQuery() + ->fetchAllAssociative(); + foreach ($result as &$media) { + $media['id'] = Uuid::fromBytesToHex($media['id']); + $media['run_id'] = Uuid::fromBytesToHex($media['run_id']); + $media['media_id'] = Uuid::fromBytesToHex($media['media_id']); + } + + return $result; + } + + /** + * @param MediaProcessWorkloadStruct[] $workload + */ + private function processFailures( + Context $context, + MigrationContextInterface $migrationContext, + MediaFileProcessorInterface $processor, + array $workload, + ): void { + for ($i = 0; $i < self::MEDIA_ERROR_THRESHOLD; ++$i) { + $errorWorkload = []; + + foreach ($workload as $item) { + if ($item->getErrorCount() > 0) { + $errorWorkload[] = $item; + } + } + + if (empty($errorWorkload)) { + break; + } + + $workload = $processor->process($migrationContext, $context, $errorWorkload); + } + } + + private function isAllMediaProcessed(Context $context, string $runId): bool + { + $criteria = new Criteria(); + $criteria->addFilter( + new EqualsFilter('runId', $runId) + ); + $criteria->addFilter( + new MultiFilter( + MultiFilter::CONNECTION_AND, + [ + new EqualsFilter('processed', false), + new EqualsFilter('processFailure', false), + ] + ) + ); + + $unprocessedCount = $this->migrationMediaFileRepo->search($criteria, $context)->getTotal(); + + return $unprocessedCount === 0; + } + + /** + * @param array $mediaFile + */ + private function logDataSetNotFoundException( + MigrationContextInterface $migrationContext, + array $mediaFile, + ): void { + $connection = $migrationContext->getConnection(); + + if ($connection === null) { + return; + } + + $this->loggingService->addLogEntry( + new DataSetNotFoundLog( + $migrationContext->getRunUuid(), + $mediaFile['entity'], + $mediaFile['id'], + $connection->getProfileName() + ) + ); + } } diff --git a/src/Migration/MessageQueue/Message/ProcessMediaMessage.php b/src/Migration/MessageQueue/Message/ProcessMediaMessage.php deleted file mode 100644 index 2bbdfa279..000000000 --- a/src/Migration/MessageQueue/Message/ProcessMediaMessage.php +++ /dev/null @@ -1,73 +0,0 @@ - - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace SwagMigrationAssistant\Migration\MessageQueue\Message; - -use Shopware\Core\Framework\Context; -use Shopware\Core\Framework\Log\Package; -use Shopware\Core\Framework\MessageQueue\AsyncMessageInterface; - -#[Package('fundamentals@after-sales')] -class ProcessMediaMessage implements AsyncMessageInterface -{ - /** - * @param array $mediaFileIds - */ - public function __construct( - private array $mediaFileIds, - private string $runId, - private string $entityName, - private Context $context, - ) { - } - - public function getContext(): Context - { - return $this->context; - } - - public function setContext(Context $context): void - { - $this->context = $context; - } - - /** - * @param array $mediaFileIds - */ - public function setMediaFileIds(array $mediaFileIds): void - { - $this->mediaFileIds = $mediaFileIds; - } - - public function setRunId(string $runId): void - { - $this->runId = $runId; - } - - /** - * @return string[] - */ - public function getMediaFileIds(): array - { - return $this->mediaFileIds; - } - - public function getRunId(): string - { - return $this->runId; - } - - public function getEntityName(): string - { - return $this->entityName; - } - - public function setEntityName(string $entityName): void - { - $this->entityName = $entityName; - } -} diff --git a/src/Migration/Run/MigrationProgressFieldSerializer.php b/src/Migration/Run/MigrationProgressFieldSerializer.php index 3736d4416..d0b5b9cf7 100644 --- a/src/Migration/Run/MigrationProgressFieldSerializer.php +++ b/src/Migration/Run/MigrationProgressFieldSerializer.php @@ -44,12 +44,15 @@ public function encode( $dataSet = $dataSet->jsonSerialize(); } } + + unset($dataSet); } if (isset($value['dataSets']) && \is_array($value['dataSets'])) { foreach ($value['dataSets'] as &$dataSet) { unset($dataSet['extensions']); } + unset($dataSet); } $data = new KeyValuePair($data->getKey(), $value, $data->isRaw()); diff --git a/src/Migration/Service/MediaFileProcessorService.php b/src/Migration/Service/MediaFileProcessorService.php deleted file mode 100644 index 09ccc695b..000000000 --- a/src/Migration/Service/MediaFileProcessorService.php +++ /dev/null @@ -1,153 +0,0 @@ - - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace SwagMigrationAssistant\Migration\Service; - -use Doctrine\DBAL\Connection; -use Shopware\Core\Framework\Context; -use Shopware\Core\Framework\Log\Package; -use Shopware\Core\Framework\Uuid\Uuid; -use SwagMigrationAssistant\Exception\DataSetNotFoundException; -use SwagMigrationAssistant\Migration\DataSelection\DataSet\DataSet; -use SwagMigrationAssistant\Migration\DataSelection\DataSet\DataSetRegistry; -use SwagMigrationAssistant\Migration\Logging\Log\DataSetNotFoundLog; -use SwagMigrationAssistant\Migration\Logging\LoggingService; -use SwagMigrationAssistant\Migration\MessageQueue\Message\ProcessMediaMessage; -use SwagMigrationAssistant\Migration\MigrationContextInterface; -use Symfony\Component\Messenger\MessageBusInterface; - -#[Package('fundamentals@after-sales')] -class MediaFileProcessorService implements MediaFileProcessorServiceInterface -{ - final public const MESSAGE_SIZE = 5; - - public function __construct( - private readonly MessageBusInterface $messageBus, - private readonly DataSetRegistry $dataSetRegistry, - private readonly LoggingService $loggingService, - private readonly Connection $dbalConnection, - ) { - } - - public function processMediaFiles(MigrationContextInterface $migrationContext, Context $context): int - { - $mediaFiles = $this->getMediaFiles($migrationContext); - - $currentDataSet = null; - $currentCount = 0; - $messageMediaUuids = []; - foreach ($mediaFiles as $mediaFile) { - if ($currentDataSet === null) { - try { - $currentDataSet = $this->dataSetRegistry->getDataSet($migrationContext, $mediaFile['entity']); - } catch (DataSetNotFoundException $e) { - $this->logDataSetNotFoundException($migrationContext, $mediaFile); - - continue; - } - } - - if ($currentDataSet::getEntity() !== $mediaFile['entity']) { - $this->addMessageToBus($migrationContext->getRunUuid(), $context, $currentDataSet, $messageMediaUuids); - - try { - $messageMediaUuids = []; - $currentCount = 0; - $currentDataSet = $this->dataSetRegistry->getDataSet($migrationContext, $mediaFile['entity']); - } catch (DataSetNotFoundException $e) { - $this->logDataSetNotFoundException($migrationContext, $mediaFile); - - continue; - } - } - - ++$currentCount; - $messageMediaUuids[] = $mediaFile['media_id']; - - if ($currentCount < self::MESSAGE_SIZE) { - continue; - } - - $this->addMessageToBus($migrationContext->getRunUuid(), $context, $currentDataSet, $messageMediaUuids); - $messageMediaUuids = []; - $currentCount = 0; - } - - if ($currentCount > 0 && $currentDataSet !== null) { - $this->addMessageToBus($migrationContext->getRunUuid(), $context, $currentDataSet, $messageMediaUuids); - } - - $this->loggingService->saveLogging($context); - - return \count($mediaFiles); - } - - /** - * @return array> - */ - private function getMediaFiles(MigrationContextInterface $migrationContext): array - { - $queryBuilder = $this->dbalConnection->createQueryBuilder(); - - $result = $queryBuilder - ->select('*') - ->from('swag_migration_media_file') - ->where('run_id = :runId') - ->andWhere('written = 1') - ->orderBy('entity, file_size') - ->setFirstResult($migrationContext->getOffset()) - ->setMaxResults($migrationContext->getLimit()) - ->setParameter('runId', Uuid::fromHexToBytes($migrationContext->getRunUuid())) - ->executeQuery() - ->fetchAllAssociative(); - foreach ($result as &$media) { - $media['id'] = Uuid::fromBytesToHex($media['id']); - $media['run_id'] = Uuid::fromBytesToHex($media['run_id']); - $media['media_id'] = Uuid::fromBytesToHex($media['media_id']); - } - - return $result; - } - - /** - * @param array $mediaUuids - */ - private function addMessageToBus(string $runUuid, Context $context, DataSet $dataSet, array $mediaUuids): void - { - $message = new ProcessMediaMessage( - $mediaUuids, - $runUuid, - $dataSet::getEntity(), - $context - ); - - $this->messageBus->dispatch($message); - } - - /** - * @param array $mediaFile - */ - private function logDataSetNotFoundException( - MigrationContextInterface $migrationContext, - array $mediaFile, - ): void { - $connection = $migrationContext->getConnection(); - - if ($connection === null) { - return; - } - - $this->loggingService->addLogEntry( - new DataSetNotFoundLog( - $migrationContext->getRunUuid(), - $mediaFile['entity'], - $mediaFile['id'], - $connection->getProfileName() - ) - ); - } -} diff --git a/src/Migration/Service/MediaFileProcessorServiceInterface.php b/src/Migration/Service/MediaFileProcessorServiceInterface.php deleted file mode 100644 index 17e2013cb..000000000 --- a/src/Migration/Service/MediaFileProcessorServiceInterface.php +++ /dev/null @@ -1,18 +0,0 @@ - - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace SwagMigrationAssistant\Migration\Service; - -use Shopware\Core\Framework\Context; -use Shopware\Core\Framework\Log\Package; -use SwagMigrationAssistant\Migration\MigrationContextInterface; - -#[Package('fundamentals@after-sales')] -interface MediaFileProcessorServiceInterface -{ - public function processMediaFiles(MigrationContextInterface $migrationContext, Context $context): int; -} diff --git a/src/Profile/Shopware/Converter/ShippingMethodConverter.php b/src/Profile/Shopware/Converter/ShippingMethodConverter.php index 12954c92a..f24ad3af5 100644 --- a/src/Profile/Shopware/Converter/ShippingMethodConverter.php +++ b/src/Profile/Shopware/Converter/ShippingMethodConverter.php @@ -1060,6 +1060,7 @@ private function setOtherConditions( ], ]; + // @phpstan-ignore-next-line parameterByRef.type $mainOrContainer['children'][0]['children'][] = $condition; } } diff --git a/src/Profile/Shopware6/Converter/ProductConverter.php b/src/Profile/Shopware6/Converter/ProductConverter.php index aff6ef54b..60494ae22 100644 --- a/src/Profile/Shopware6/Converter/ProductConverter.php +++ b/src/Profile/Shopware6/Converter/ProductConverter.php @@ -147,7 +147,6 @@ protected function convertData(array $data): ConvertStruct DefaultEntities::PRODUCT ); - /** @phpstan-ignore-next-line */ $this->checkDefaultCurrency($setting, 'price'); } @@ -209,6 +208,9 @@ protected function convertData(array $data): ConvertStruct return new ConvertStruct($converted, null, $this->mainMapping['id'] ?? null); } + /** + * @param array $source + */ private function checkDefaultCurrency(array &$source, string $key): void { // If the default currency of source and destination is identically, there is no need to add a default price diff --git a/src/Profile/Shopware6/Media/HttpOrderDocumentGenerationService.php b/src/Profile/Shopware6/Media/HttpOrderDocumentGenerationService.php index b49ae8d10..e7551f23e 100644 --- a/src/Profile/Shopware6/Media/HttpOrderDocumentGenerationService.php +++ b/src/Profile/Shopware6/Media/HttpOrderDocumentGenerationService.php @@ -28,7 +28,7 @@ use SwagMigrationAssistant\Migration\Media\MediaProcessWorkloadStruct; use SwagMigrationAssistant\Migration\Media\Processor\BaseMediaService; use SwagMigrationAssistant\Migration\Media\SwagMigrationMediaFileCollection; -use SwagMigrationAssistant\Migration\MessageQueue\Handler\ProcessMediaHandler; +use SwagMigrationAssistant\Migration\MessageQueue\Handler\Processor\MediaProcessingProcessor; use SwagMigrationAssistant\Migration\MigrationContextInterface; use SwagMigrationAssistant\Profile\Shopware\Gateway\Api\ShopwareApiGateway; use SwagMigrationAssistant\Profile\Shopware6\Gateway\Connection\ConnectionFactoryInterface; @@ -277,7 +277,7 @@ private function handleFailedRequest( $mappedWorkload->setAdditionalData($additionalData); $mappedWorkload->setErrorCount($mappedWorkload->getErrorCount() + 1); - if ($mappedWorkload->getErrorCount() > ProcessMediaHandler::MEDIA_ERROR_THRESHOLD) { + if ($mappedWorkload->getErrorCount() > MediaProcessingProcessor::MEDIA_ERROR_THRESHOLD) { $failureUuids[] = $uuid; $mappedWorkload->setState(MediaProcessWorkloadStruct::ERROR_STATE); $this->loggingService->addLogEntry(new CannotGetFileRunLog( diff --git a/src/Resources/app/administration/src/module/swag-migration/component/loading-screen/swag-migration-result-screen/swag-migration-result-screen.html.twig b/src/Resources/app/administration/src/module/swag-migration/component/loading-screen/swag-migration-result-screen/swag-migration-result-screen.html.twig index 460e41e09..c7ab4febc 100644 --- a/src/Resources/app/administration/src/module/swag-migration/component/loading-screen/swag-migration-result-screen/swag-migration-result-screen.html.twig +++ b/src/Resources/app/administration/src/module/swag-migration/component/loading-screen/swag-migration-result-screen/swag-migration-result-screen.html.twig @@ -21,9 +21,6 @@
{{ $tc('swag-migration.index.loadingScreenCard.result.title') }}
-
- {{ $tc('swag-migration.index.loadingScreenCard.result.caption') }} -
{% endblock %} {% endblock %} diff --git a/src/Resources/app/administration/src/module/swag-migration/snippet/de.json b/src/Resources/app/administration/src/module/swag-migration/snippet/de.json index b17f020bb..7361dfc3c 100644 --- a/src/Resources/app/administration/src/module/swag-migration/snippet/de.json +++ b/src/Resources/app/administration/src/module/swag-migration/snippet/de.json @@ -426,9 +426,8 @@ }, "result": { "title": "Der Migrations-Assistent ist fertig", - "caption": "Die Medien werden weiter im Hintergrund heruntergeladen. Große Dateien können etwas Zeit in Anspruch nehmen.", "logSummary": "Logbuch", - "historyHint": "Du kannst diese Informationen jederzeit in der Migrations-Historie einsehen. Es kann sein, dass zu einem späteren Zeitpunkt noch mehr Log-Nachrichten hinzukommen, weil der Mediendownload noch im Hintergrund läuft." + "historyHint": "Du kannst diese Informationen jederzeit in der Migrations-Historie einsehen." } }, "confirmAbortDialog": { diff --git a/src/Resources/app/administration/src/module/swag-migration/snippet/en.json b/src/Resources/app/administration/src/module/swag-migration/snippet/en.json index 00f616c8f..82a3e2490 100644 --- a/src/Resources/app/administration/src/module/swag-migration/snippet/en.json +++ b/src/Resources/app/administration/src/module/swag-migration/snippet/en.json @@ -426,9 +426,8 @@ }, "result": { "title": "The Migration Assistant is done", - "caption": "The media download continues in the background. Large files can take some time.", "logSummary": "Logbook", - "historyHint": "You can view this information any time in the migration history. It is possible that more log messages will be added at a later time because the media download is still running in the background." + "historyHint": "You can view this information any time in the migration history." } }, "confirmAbortDialog": { diff --git a/tests/Migration/Mapping/Lookup/TaxLookupTest.php b/tests/Migration/Mapping/Lookup/TaxLookupTest.php index 2c58a2b84..b5554fe4a 100644 --- a/tests/Migration/Mapping/Lookup/TaxLookupTest.php +++ b/tests/Migration/Mapping/Lookup/TaxLookupTest.php @@ -217,7 +217,7 @@ private function getMockedTaxLookup(): TaxLookup $cacheData = []; $taxRateCache = []; foreach ($databaseData as $data) { - $cacheData[$data['taxRate']] = $data['expectedResult']; + $cacheData[(string) $data['taxRate']] = $data['expectedResult']; $cacheData[$data['taxRate'] . '-' . $data['name']] = $data['expectedResult']; $taxRateCache[$data['expectedResult']] = $data['taxRate']; diff --git a/tests/Migration/Media/Process/HttpDownloadServiceBaseTest.php b/tests/Migration/Media/Process/HttpDownloadServiceBaseTest.php index 6638c42d8..728e78aed 100644 --- a/tests/Migration/Media/Process/HttpDownloadServiceBaseTest.php +++ b/tests/Migration/Media/Process/HttpDownloadServiceBaseTest.php @@ -198,7 +198,7 @@ public function testProcessWithRequestFailure(): void 'level' => 'warning', 'code' => 'SWAG_MIGRATION_CANNOT_GET_MEDIA_FILE', 'title' => 'The media file cannot be downloaded / copied', - 'description' => 'The media file with the uri "' . $mediaFiles[0]['uri'] . '" and media id "' . $mediaFiles[0]['mediaId'] . '" cannot be downloaded / copied. The following request error occurred: Request failed', + 'description' => 'The media file with the uri "' . $mediaFiles[0]['uri'] . '" and media id "' . $mediaFiles[0]['mediaId'] . '" cannot be downloaded / copied. The following error occurred: Request failed', 'parameters' => [ 'entity' => 'media', 'sourceId' => $mediaFiles[0]['mediaId'], diff --git a/tests/Migration/MessageQueue/Handler/Processor/MediaProcessingProcessorTest.php b/tests/Migration/MessageQueue/Handler/Processor/MediaProcessingProcessorTest.php index 602d994f1..ebec83667 100644 --- a/tests/Migration/MessageQueue/Handler/Processor/MediaProcessingProcessorTest.php +++ b/tests/Migration/MessageQueue/Handler/Processor/MediaProcessingProcessorTest.php @@ -7,13 +7,30 @@ namespace SwagMigrationAssistant\Test\Migration\MessageQueue\Handler\Processor; +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Query\QueryBuilder; +use Doctrine\DBAL\Result; use PHPUnit\Framework\TestCase; use Shopware\Core\Framework\Context; +use Shopware\Core\Framework\DataAbstractionLayer\EntityCollection; use Shopware\Core\Framework\DataAbstractionLayer\EntityRepository; +use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria; +use Shopware\Core\Framework\DataAbstractionLayer\Search\EntitySearchResult; use Shopware\Core\Framework\Log\Package; use Shopware\Core\Framework\Uuid\Uuid; use Shopware\Core\Test\Stub\MessageBus\CollectingMessageBus; +use SwagMigrationAssistant\Exception\DataSetNotFoundException; +use SwagMigrationAssistant\Exception\MigrationException; +use SwagMigrationAssistant\Exception\NoConnectionFoundException; use SwagMigrationAssistant\Migration\Connection\SwagMigrationConnectionEntity; +use SwagMigrationAssistant\Migration\DataSelection\DataSet\DataSetRegistry; +use SwagMigrationAssistant\Migration\Logging\Log\DataSetNotFoundLog; +use SwagMigrationAssistant\Migration\Logging\Log\ProcessorNotFoundLog; +use SwagMigrationAssistant\Migration\Logging\LoggingService; +use SwagMigrationAssistant\Migration\Media\MediaFileProcessorInterface; +use SwagMigrationAssistant\Migration\Media\MediaFileProcessorRegistryInterface; +use SwagMigrationAssistant\Migration\Media\MediaProcessWorkloadStruct; +use SwagMigrationAssistant\Migration\Media\SwagMigrationMediaFileEntity; use SwagMigrationAssistant\Migration\MessageQueue\Handler\Processor\MediaProcessingProcessor; use SwagMigrationAssistant\Migration\MigrationContext; use SwagMigrationAssistant\Migration\Run\MigrationProgress; @@ -22,11 +39,9 @@ use SwagMigrationAssistant\Migration\Run\ProgressDataSetCollection; use SwagMigrationAssistant\Migration\Run\RunTransitionServiceInterface; use SwagMigrationAssistant\Migration\Run\SwagMigrationRunEntity; -use SwagMigrationAssistant\Migration\Service\MediaFileProcessorService; +use SwagMigrationAssistant\Profile\Shopware\DataSelection\DataSet\MediaDataSet; use SwagMigrationAssistant\Profile\Shopware55\Shopware55Profile; -use function PHPUnit\Framework\once; - #[Package('fundamentals@after-sales')] class MediaProcessingProcessorTest extends TestCase { @@ -34,116 +49,422 @@ class MediaProcessingProcessorTest extends TestCase private CollectingMessageBus $bus; + private MigrationContext $migrationContext; + + private SwagMigrationRunEntity $runEntity; + + private MigrationProgress $progress; + + /** + * @var array> + */ + private array $mediaFiles = []; + + private Connection $dbalConnection; + protected function setUp(): void { $this->bus = new CollectingMessageBus(); - $this->processor = new MediaProcessingProcessor( - $this->createMock(EntityRepository::class), - $this->createMock(EntityRepository::class), - $this->createMock(EntityRepository::class), - $this->createMock(RunTransitionServiceInterface::class), - $this->createMock(MediaFileProcessorService::class), - $this->bus - ); - } - public function testProcessingWithoutMediaFiles(): void - { - $progress = new MigrationProgress( + $this->progress = new MigrationProgress( 0, 0, new ProgressDataSetCollection([ - 'product' => new ProgressDataSet('product', 1000), + 'media' => new ProgressDataSet('media', 1000), ]), - 'product', + 'media', 100 ); - $run = new SwagMigrationRunEntity(); - $run->setId(Uuid::randomHex()); - $run->setProgress($progress); - $run->setStep(MigrationStep::FETCHING); - $connection = new SwagMigrationConnectionEntity(); $connection->setId(Uuid::randomHex()); - $migrationContext = new MigrationContext(new Shopware55Profile(), $connection, $run->getId()); + $this->runEntity = new SwagMigrationRunEntity(); + $this->runEntity->setId(Uuid::randomHex()); + $this->runEntity->setProgress($this->progress); + $this->runEntity->setStep(MigrationStep::FETCHING); + $this->runEntity->setConnection($connection); + $this->migrationContext = new MigrationContext(new Shopware55Profile(), $connection, $this->runEntity->getId()); + + $result = $this->createMock(Result::class); + $result->method('fetchAllAssociative')->willReturnCallback(fn () => $this->mediaFiles); + + $queryBuilder = $this->createMock(QueryBuilder::class); + $queryBuilder->method('select')->willReturnSelf(); + $queryBuilder->method('from')->willReturnSelf(); + $queryBuilder->method('where')->willReturnSelf(); + $queryBuilder->method('andWhere')->willReturnSelf(); + $queryBuilder->method('orderBy')->willReturnSelf(); + $queryBuilder->method('setFirstResult')->willReturnSelf(); + $queryBuilder->method('setMaxResults')->willReturnSelf(); + $queryBuilder->method('setParameter')->willReturnSelf(); + $queryBuilder->method('executeQuery')->willReturn($result); + + $this->dbalConnection = $this->createMock(Connection::class); + $this->dbalConnection->method('createQueryBuilder')->willReturn($queryBuilder); + + $this->processor = new MediaProcessingProcessor( + $this->createMock(EntityRepository::class), + $this->createMock(EntityRepository::class), + $this->createMock(EntityRepository::class), + $this->createMock(RunTransitionServiceInterface::class), + $this->bus, + $this->createMock(LoggingService::class), + $this->dbalConnection, + $this->createMock(MediaFileProcessorRegistryInterface::class), + $this->createMock(DataSetRegistry::class), + ); + } + + public function testThrowsExceptionIfNoConnectionIsSet(): void + { + $this->runEntity = new SwagMigrationRunEntity(); + + try { + $this->processor->process( + $this->migrationContext, + Context::createDefaultContext(), + $this->runEntity, + $this->progress + ); + } catch (\Exception $e) { + static::assertInstanceOf(MigrationException::class, $e); + static::assertSame(MigrationException::ENTITY_NOT_EXISTS, $e->getErrorCode()); + } + } + + public function testTransitionsToNextStepIfNoMediaFiles(): void + { $runTransitionService = $this->createMock(RunTransitionServiceInterface::class); - $runTransitionService - ->expects(once()) + $runTransitionService->expects(static::once()) ->method('transitionToRunStep') - ->with($run->getId(), MigrationStep::CLEANUP); + ->with( + $this->migrationContext->getRunUuid(), + MigrationStep::CLEANUP + ); $this->processor = new MediaProcessingProcessor( $this->createMock(EntityRepository::class), $this->createMock(EntityRepository::class), $this->createMock(EntityRepository::class), $runTransitionService, - $this->createMock(MediaFileProcessorService::class), - $this->bus + $this->bus, + $this->createMock(LoggingService::class), + $this->createMock(Connection::class), + $this->createMock(MediaFileProcessorRegistryInterface::class), + $this->createMock(DataSetRegistry::class), ); $this->processor->process( - $migrationContext, + $this->migrationContext, Context::createDefaultContext(), - $run, - $progress + $this->runEntity, + $this->progress ); static::assertCount(1, $this->bus->getMessages()); } - public function testProcessing(): void + public function testHandlesDataSetNotFoundExceptionGracefully(): void { - $progress = new MigrationProgress( - 0, - 0, - new ProgressDataSetCollection([ - 'product' => new ProgressDataSet('product', 1000), - ]), - 'product', - 100 + $this->mediaFiles = [ + [ + 'id' => Uuid::randomBytes(), + 'run_id' => Uuid::randomBytes(), + 'media_id' => Uuid::randomBytes(), + 'entity' => 'media', + 'written' => 1, + 'file_size' => 10, + ], + ]; + + $dataSetRegistry = $this->createMock(DataSetRegistry::class); + $dataSetRegistry->method('getDataSet')->willThrowException( + new DataSetNotFoundException(400, MigrationException::DATASET_NOT_FOUND, 'unknown') ); - $run = new SwagMigrationRunEntity(); - $run->setId(Uuid::randomHex()); - $run->setProgress($progress); - $run->setStep(MigrationStep::FETCHING); + $logging = $this->createMock(LoggingService::class); + $logging->expects(static::once())->method('addLogEntry')->with( + static::isInstanceOf(DataSetNotFoundLog::class) + ); - $connection = new SwagMigrationConnectionEntity(); - $connection->setId(Uuid::randomHex()); + $processor = new MediaProcessingProcessor( + $this->createMock(EntityRepository::class), + $this->createMock(EntityRepository::class), + $this->createMock(EntityRepository::class), + $this->createMock(RunTransitionServiceInterface::class), + $this->bus, + $logging, + $this->dbalConnection, + $this->createMock(MediaFileProcessorRegistryInterface::class), + $dataSetRegistry + ); + + $processor->process( + $this->migrationContext, + Context::createDefaultContext(), + $this->runEntity, + $this->progress + ); + + static::assertCount(1, $this->bus->getMessages()); + } + + public function testHandlesNoConnectionFoundException(): void + { + $processorMock = $this->createMock(MediaFileProcessorInterface::class); + $processorMock->method('process')->willThrowException( + new NoConnectionFoundException(400, MigrationException::DATASET_NOT_FOUND, 'unknown') + ); + + $registry = $this->createMock(MediaFileProcessorRegistryInterface::class); + $registry->method('getProcessor')->willReturn($processorMock); + + $logging = $this->createMock(LoggingService::class); + $logging->expects(static::once())->method('addLogEntry')->with( + static::isInstanceOf(ProcessorNotFoundLog::class) + ); + + $this->mediaFiles = [ + [ + 'id' => Uuid::randomBytes(), + 'run_id' => Uuid::randomBytes(), + 'media_id' => Uuid::randomBytes(), + 'entity' => 'media', + 'written' => 1, + 'file_size' => 10, + ], + ]; - $migrationContext = new MigrationContext(new Shopware55Profile(), $connection, $run->getId()); + $dataSetRegistry = $this->createMock(DataSetRegistry::class); + $dataSetRegistry->method('getDataSet')->willReturn(new MediaDataSet()); + + $processor = new MediaProcessingProcessor( + $this->createMock(EntityRepository::class), + $this->createMock(EntityRepository::class), + $this->createMock(EntityRepository::class), + $this->createMock(RunTransitionServiceInterface::class), + $this->bus, + $logging, + $this->dbalConnection, + $registry, + $dataSetRegistry + ); + + $processor->process( + $this->migrationContext, + Context::createDefaultContext(), + $this->runEntity, + $this->progress + ); + + static::assertCount(1, $this->bus->getMessages()); + } + + public function testProcess(): void + { + $processorMock = $this->createMock(MediaFileProcessorInterface::class); + + $workload = [ + new MediaProcessWorkloadStruct( + Uuid::randomHex(), + Uuid::randomHex(), + MediaProcessWorkloadStruct::IN_PROGRESS_STATE, + [], + 0 + ), + ]; + + $processorMock->expects(static::once()) + ->method('process') + ->willReturn($workload); + + $processorRegistry = $this->createMock(MediaFileProcessorRegistryInterface::class); + $processorRegistry->method('getProcessor')->willReturn($processorMock); + + $this->mediaFiles = [ + [ + 'id' => Uuid::randomBytes(), + 'run_id' => Uuid::randomBytes(), + 'media_id' => Uuid::randomBytes(), + 'entity' => 'media', + 'written' => 1, + 'file_size' => 10, + ], + ]; + + $dataSetRegistry = $this->createMock(DataSetRegistry::class); + $dataSetRegistry->method('getDataSet')->willReturn(new MediaDataSet()); + + $processor = new MediaProcessingProcessor( + $this->createMock(EntityRepository::class), + $this->createMock(EntityRepository::class), + $this->createMock(EntityRepository::class), + $this->createMock(RunTransitionServiceInterface::class), + $this->bus, + $this->createMock(LoggingService::class), + $this->dbalConnection, + $processorRegistry, + $dataSetRegistry + ); + + $processor->process( + $this->migrationContext, + Context::createDefaultContext(), + $this->runEntity, + $this->progress + ); + + static::assertSame(1, $this->progress->getProgress()); + static::assertSame(101, $this->progress->getCurrentEntityProgress()); + } + + public function testProcessRetriesUntilNoErrors(): void + { + $processorMock = $this->createMock(MediaFileProcessorInterface::class); + + // First call returns workload with errorCount 1 + // Second call returns workload with errorCount 0 + $firstWorkload = [ + new MediaProcessWorkloadStruct( + Uuid::randomHex(), + Uuid::randomHex(), + MediaProcessWorkloadStruct::IN_PROGRESS_STATE, + [], + 1 + ), + ]; + $secondWorkload = [ + new MediaProcessWorkloadStruct( + Uuid::randomHex(), + Uuid::randomHex(), + MediaProcessWorkloadStruct::IN_PROGRESS_STATE, + [], + 0 + ), + ]; + + $processorMock->expects(static::exactly(2)) + ->method('process') + ->willReturnOnConsecutiveCalls($firstWorkload, $secondWorkload); + + $processorRegistry = $this->createMock(MediaFileProcessorRegistryInterface::class); + $processorRegistry->method('getProcessor')->willReturn($processorMock); + + $this->mediaFiles = [ + [ + 'id' => Uuid::randomBytes(), + 'run_id' => Uuid::randomBytes(), + 'media_id' => Uuid::randomBytes(), + 'entity' => 'media', + 'written' => 1, + 'file_size' => 10, + ], + ]; + + $dataSetRegistry = $this->createMock(DataSetRegistry::class); + $dataSetRegistry->method('getDataSet')->willReturn(new MediaDataSet()); + + $processor = new MediaProcessingProcessor( + $this->createMock(EntityRepository::class), + $this->createMock(EntityRepository::class), + $this->createMock(EntityRepository::class), + $this->createMock(RunTransitionServiceInterface::class), + $this->bus, + $this->createMock(LoggingService::class), + $this->dbalConnection, + $processorRegistry, + $dataSetRegistry + ); + + $processor->process( + $this->migrationContext, + Context::createDefaultContext(), + $this->runEntity, + $this->progress + ); + + static::assertSame(1, $this->progress->getProgress()); + static::assertSame(101, $this->progress->getCurrentEntityProgress()); + } + + public function testTransitionsIfAllMediaIsProcessed(): void + { + $processorMock = $this->createMock(MediaFileProcessorInterface::class); + + $workload = [ + new MediaProcessWorkloadStruct( + Uuid::randomHex(), + Uuid::randomHex(), + MediaProcessWorkloadStruct::FINISH_STATE, + [], + 0 + ), + ]; + + $processorMock->expects(static::once()) + ->method('process') + ->willReturn($workload); + + $processorRegistry = $this->createMock(MediaFileProcessorRegistryInterface::class); + $processorRegistry->method('getProcessor')->willReturn($processorMock); + + $this->mediaFiles = [ + [ + 'id' => Uuid::randomBytes(), + 'run_id' => Uuid::randomBytes(), + 'media_id' => Uuid::randomBytes(), + 'entity' => 'media', + 'written' => 1, + 'file_size' => 10, + ], + ]; + + $dataSetRegistry = $this->createMock(DataSetRegistry::class); + $dataSetRegistry->method('getDataSet')->willReturn(new MediaDataSet()); $runTransitionService = $this->createMock(RunTransitionServiceInterface::class); - $runTransitionService - ->expects(static::never()) + $runTransitionService->expects(static::once()) ->method('transitionToRunStep') - ->with($run->getId(), MigrationStep::CLEANUP); + ->with( + $this->migrationContext->getRunUuid(), + MigrationStep::CLEANUP + ); - $mediaFileProcessorService = $this->createMock(MediaFileProcessorService::class); - $mediaFileProcessorService - ->expects(static::once()) - ->method('processMediaFiles') - ->willReturn(100); + $migrationMediaFileRepository = $this->createMock(EntityRepository::class); + $migrationMediaFileRepository->method('search')->willReturn( + new EntitySearchResult( + SwagMigrationMediaFileEntity::class, + 0, + new EntityCollection(), + null, + new Criteria(), + Context::createDefaultContext() + ) + ); - $this->processor = new MediaProcessingProcessor( - $this->createMock(EntityRepository::class), + $processor = new MediaProcessingProcessor( $this->createMock(EntityRepository::class), $this->createMock(EntityRepository::class), + $migrationMediaFileRepository, $runTransitionService, - $mediaFileProcessorService, - $this->bus + $this->bus, + $this->createMock(LoggingService::class), + $this->dbalConnection, + $processorRegistry, + $dataSetRegistry ); - $this->processor->process( - $migrationContext, + $processor->process( + $this->migrationContext, Context::createDefaultContext(), - $run, - $progress + $this->runEntity, + $this->progress ); static::assertCount(1, $this->bus->getMessages()); + static::assertSame(1, $this->progress->getProgress()); + static::assertSame(101, $this->progress->getCurrentEntityProgress()); } } diff --git a/tests/Mock/Migration/Service/DummyMediaFileProcessorService.php b/tests/Mock/Migration/Service/DummyMediaFileProcessorService.php deleted file mode 100644 index 602df5d03..000000000 --- a/tests/Mock/Migration/Service/DummyMediaFileProcessorService.php +++ /dev/null @@ -1,24 +0,0 @@ - - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace SwagMigrationAssistant\Test\Mock\Migration\Service; - -use Shopware\Core\Framework\Context; -use Shopware\Core\Framework\Log\Package; -use SwagMigrationAssistant\Migration\MigrationContextInterface; -use SwagMigrationAssistant\Migration\Service\MediaFileProcessorService; - -#[Package('fundamentals@after-sales')] -class DummyMediaFileProcessorService extends MediaFileProcessorService -{ - public function processMediaFiles( - MigrationContextInterface $migrationContext, - Context $context, - ): int { - return 0; - } -} diff --git a/tests/Profile/Shopware55/Converter/PropertyGroupOptionConverterTest.php b/tests/Profile/Shopware55/Converter/PropertyGroupOptionConverterTest.php index 0361cdcf2..0395c6159 100644 --- a/tests/Profile/Shopware55/Converter/PropertyGroupOptionConverterTest.php +++ b/tests/Profile/Shopware55/Converter/PropertyGroupOptionConverterTest.php @@ -182,6 +182,8 @@ public function testConvertWithPropertiesAndProductConfigurators(): void ++$iterator; } + unset($relation); + $iterator = 0; foreach ($propertyRelationData as &$relation) { $relation['productId'] = $productData[5]['detail']['articleID']; @@ -198,6 +200,8 @@ public function testConvertWithPropertiesAndProductConfigurators(): void ++$iterator; } + + unset($relation); } public function testConvertWithPropertiesAndProductConfiguratorsAndOldIdentifier(): void @@ -261,6 +265,7 @@ public function testConvertWithPropertiesAndProductConfiguratorsAndOldIdentifier ++$iterator; } + unset($relation); $iterator = 0; foreach ($propertyRelationData as &$relation) { @@ -278,5 +283,7 @@ public function testConvertWithPropertiesAndProductConfiguratorsAndOldIdentifier ++$iterator; } + + unset($relation); } } diff --git a/tests/Profile/Shopware6/Converter/ShippingMethodConverterTest.php b/tests/Profile/Shopware6/Converter/ShippingMethodConverterTest.php index b96389a2c..4bbb14492 100644 --- a/tests/Profile/Shopware6/Converter/ShippingMethodConverterTest.php +++ b/tests/Profile/Shopware6/Converter/ShippingMethodConverterTest.php @@ -32,14 +32,18 @@ protected function createConverter( MediaFileServiceInterface $mediaFileService, ?array $mappingArray = [], ): ConverterInterface { + $uuid = Uuid::randomHex(); + /** @var StaticEntityRepository $shippingMethodRepository */ $shippingMethodRepository = new StaticEntityRepository([ new IdSearchResult( 1, // trigger already existing technical name check - [[ - 'primaryKey' => Uuid::randomHex(), - 'data' => [], - ]], + [ + $uuid => [ + 'primaryKey' => $uuid, + 'data' => [], + ], + ], new Criteria(), Context::createDefaultContext() ), diff --git a/tests/acceptance/fixtures/AcceptanceTest.ts b/tests/acceptance/fixtures/AcceptanceTest.ts index 75c7fad03..5fd9607d3 100644 --- a/tests/acceptance/fixtures/AcceptanceTest.ts +++ b/tests/acceptance/fixtures/AcceptanceTest.ts @@ -4,7 +4,6 @@ import type { FixtureTypes as BaseTypes } from '@shopware-ag/acceptance-test-sui import { MigrationUser } from './MigrationUser'; import { DatabaseCredentials, DatabaseCredentialsStruct } from './DatabaseCredentials'; import {EntityCounter, EntityCounterStruct} from './EntityCounter'; -import {MediaProcessObserver, MediaProcessObserverStruct} from './MediaProcessObserver'; export * from '@shopware-ag/acceptance-test-suite'; @@ -12,7 +11,6 @@ export interface MigrationFixtureTypes { MigrationUser: FixtureTypes['ShopAdmin'], DatabaseCredentials: DatabaseCredentialsStruct, EntityCounter: EntityCounterStruct, - MediaProcessObserver: MediaProcessObserverStruct, } export type FixtureTypes = MigrationFixtureTypes & BaseTypes; @@ -22,5 +20,4 @@ export const test = mergeTests( MigrationUser, DatabaseCredentials, EntityCounter, - MediaProcessObserver, ); diff --git a/tests/acceptance/fixtures/MediaProcessObserver.ts b/tests/acceptance/fixtures/MediaProcessObserver.ts deleted file mode 100644 index ff7f1012f..000000000 --- a/tests/acceptance/fixtures/MediaProcessObserver.ts +++ /dev/null @@ -1,21 +0,0 @@ -import {test as base, expect } from '@playwright/test'; -import {FixtureTypes} from './AcceptanceTest'; - -export interface MediaProcessObserverStruct { - isMediaProcessing: () => Promise -} - -// ToDo MIG-985: remove this workaround when the underlying issue is fixed -export const MediaProcessObserver = base.extend({ - MediaProcessObserver: async ({ AdminApiContext }, use) => { - const isMediaProcessing = async () => { - const response = await AdminApiContext.get(`/api/_action/migration/is-media-processing`, {}); - expect(response.ok()).toBeTruthy(); - return await response.json(); - }; - - await use({ - isMediaProcessing, - }); - }, -}); diff --git a/tests/acceptance/tests/MigrationByUiFlow.spec.ts b/tests/acceptance/tests/MigrationByUiFlow.spec.ts index 506c52414..c3f035e69 100644 --- a/tests/acceptance/tests/MigrationByUiFlow.spec.ts +++ b/tests/acceptance/tests/MigrationByUiFlow.spec.ts @@ -12,7 +12,6 @@ test('As a shop owner I want to migrate my data from my old SW5 shop to SW6 via MigrationUser, DatabaseCredentials, EntityCounter, - MediaProcessObserver, }) => { const page = MigrationUser.page; await page.goto('/admin'); @@ -102,17 +101,6 @@ test('As a shop owner I want to migrate my data from my old SW5 shop to SW6 via await page.getByRole('button', { name: 'Back to overview' }).click(); }); - // ToDo MIG-985: Remove this if the underlying issue is fixed - await test.step('Wait for media download to finish', async () => { - await expect.poll(async () => { - return await MediaProcessObserver.isMediaProcessing(); - }, { - // Probe after 100ms and then every second - intervals: [100, 1_000], - timeout: 300_000, - }).toBe(false); - }); - await test.step('Expect entities to be there', async () => { await EntityCounter.checkEntityCount('swag_migration_logging', 699); @@ -127,8 +115,9 @@ test('As a shop owner I want to migrate my data from my old SW5 shop to SW6 via await EntityCounter.checkEntityCount('customer', 3); await EntityCounter.checkEntityCount('cms_page', 10); - await EntityCounter.checkEntityCount('media', 603); - await EntityCounter.checkEntityCount('media_folder', 26); + await EntityCounter.checkEntityCount('media', 595); + await EntityCounter.checkEntityCount('media_folder', 24); + await EntityCounter.checkEntityCount('document', 8); await EntityCounter.checkEntityCount('newsletter_recipient', 0); await EntityCounter.checkEntityCount('promotion', 4);