diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 000000000..056169d44 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,25 @@ +### 1. Why is this change necessary? + +// Please link to the relevant issues, if they exists, or explain the solved issue here. e.g. +// closes https://github.com/shopware/shopware/issues/{ISSUE_NUMBER} + +### 2. What does this change do, exactly? + +// Shortly explain how your change solves the issue +// If the issue has more than one obvious solution, +// shortly explain your reasoning for your chosen approach + +### 3. Describe each step to reproduce the issue or behaviour. + + +### 5. Checklist + +- [ ] I have created the PR in draft status and only open it when it's ready for review +- [ ] If the change is large: I have thought about splitting it up into smaller PRs +- [ ] I have written tests and if it's a bug fix verified that they fail without my change or that new code is covered by tests +- [ ] I have updated developer-facing release notes if this change affects external developers +- [ ] I have written or adjusted the documentation according to my changes +- [ ] I added the correct package annotation to each `src` file (not strictly necessary for tests) +- [ ] This change has code comments where appropriate, especially for non-obvious lines of code +- [ ] Ensure the pull request title as well as first commit follows conventional commits +- [ ] I have thought about well-defined extension points and marked everything else as `@internal` / `@private` diff --git a/CHANGELOG.md b/CHANGELOG.md index 3002374a5..7efc964ee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,10 @@ - #11808 - Removed CLI commands `migration:abort`, `migration:get-progress`, and `migration:start`. You have to start the migration via the Administration UI, it still runs in the background message queue - #11808 - Checksum reset and migration data cleanup now run asynchronously via message queue. +# 15.0.4 +- Added `shopIdV2` to the list of ignored system config entries for Shopware 6 connections +- Media processing has been simplified so that the UI can show the actual progress + # 15.0.3 - Fixed translations of error groups missing details like the entity @@ -63,10 +67,10 @@ # 13.1.0 -- MIG-981 - Media migration now uses the system's temporary directory to download files -- MIG-1016 - Improves the warnings for different default currency and default language in the data selection -- MIG-1016 - Added new block `{% block swag_migration_confirm_warning_alert %}` in `swag-migration/component/card/swag-migration-confirm-warning/swag-migration-confirm-warning.html.twig` -- MIG-1037 - Fixes a rare issue that in certain situations not all entities are migrated (some were skipped). Was detected during translations of SW5 +- MIG-981 - Media migration now uses the system's temporary directory to download files. +- MIG-1016 - Improves the warnings for different default currency and default language in the data selection. +- MIG-1016 - Added new block `{% block swag_migration_confirm_warning_alert %}` in `swag-migration/component/card/swag-migration-confirm-warning/swag-migration-confirm-warning.html.twig`. +- MIG-1037 - Fixes a rare issue that in certain situations not all entities are migrated (some were skipped). Was detected during translations of SW5. # 13.0.0 diff --git a/CHANGELOG_de-DE.md b/CHANGELOG_de-DE.md index 20954c0f6..ae3d72c8c 100644 --- a/CHANGELOG_de-DE.md +++ b/CHANGELOG_de-DE.md @@ -9,6 +9,10 @@ - #11808 - CLI-Befehle `migration:abort`, `migration:get-progress` und `migration:start` entfernt. Die Migration kann über die Administrations-UI gestartet werden und wird weiterhin in der Message-Queue ausgeführt. - #11808 - Zurücksetzen der Prüfsummen und Bereinigung der Migrationsdaten erfolgen jetzt asynchron über die Message Queue. +# 15.0.4 +- `shopIdV2` wurde zur Liste der ignorierten Systemkonfigurationseinträge für Shopware 6-Verbindungen hinzugefügt +- Die Medienverarbeitung wurde vereinfacht, sodass die Benutzeroberfläche den tatsächlichen Fortschritt anzeigen kann + # 15.0.3 - Übersetzungen von Fehlergruppen behoben, welche Details wie den Entitätsnamen nicht darstellten diff --git a/UPGRADE.md b/UPGRADE.md index d454fcac6..ff322fc91 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -235,6 +235,9 @@ - Added fields `profile_name`, `gateway_name` and `user_fixable` to log definition `SwagMigrationAssistant\Migration\Logging\SwagMigrationLoggingDefinition` - Added properties `profileName`, `gatewayName` and `userFixable` to log entity `SwagMigrationAssistant\Migration\Logging\SwagMigrationLoggingEntity` +# 14.0.4 +- Media processing has been simplified. It's no longer done in parallel messages. The migration UI reflects the media migration progress more accurately. When the step is marked as complete, all media are migrated. No need to wait for additional messages to finish. + # 14.0.0 - [BREAKING] MIG-1053 - Removed ability to set the `verify` flag for the guzzle API client. This is now always true by default. diff --git a/composer.json b/composer.json index deb970c59..be27fde57 100644 --- a/composer.json +++ b/composer.json @@ -1,7 +1,7 @@ { "name": "swag/migration-assistant", "description": "Migration plugin for shopware/platform", - "version": "15.0.3", + "version": "15.0.4", "type": "shopware-platform-plugin", "license": "MIT", "authors": [ diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index b3503d722..278a3f428 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1,7 +1,7 @@ parameters: ignoreErrors: - - message: '#^swag_migration_data\.run association has a configured autoload\=\=\=true, this is forbidden for platform integrations$#' + message: "#^swag_migration_data\\.run association has a configured autoload\\=\\=\\=true, this is forbidden for platform integrations$#" identifier: shopware.associationAutoload count: 1 path: src/Migration/Data/SwagMigrationDataDefinition.php @@ -955,7 +955,7 @@ parameters: path: src/Profile/Shopware6/Converter/NumberRangeConverter.php - - message: '#^Method SwagMigrationAssistant\\Profile\\Shopware6\\Converter\\PropertyGroupConverter\:\:convertOption\(\) has parameter \$option with no value type specified in iterable type array\.$#' + message: "#^Method SwagMigrationAssistant\\\\Profile\\\\Shopware6\\\\Converter\\\\PropertyGroupConverter\\:\\:convertOption\\(\\) has parameter \\$option with no value type specified in iterable type array\\.$#" identifier: missingType.iterableValue count: 1 path: src/Profile/Shopware6/Converter/PropertyGroupConverter.php diff --git a/src/DataProvider/Provider/Data/MailHeaderFooterProvider.php b/src/DataProvider/Provider/Data/MailHeaderFooterProvider.php new file mode 100644 index 000000000..9713327a7 --- /dev/null +++ b/src/DataProvider/Provider/Data/MailHeaderFooterProvider.php @@ -0,0 +1,51 @@ + + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SwagMigrationAssistant\DataProvider\Provider\Data; + +use Shopware\Core\Content\MailTemplate\Aggregate\MailHeaderFooter\MailHeaderFooterCollection; +use Shopware\Core\Framework\Context; +use Shopware\Core\Framework\DataAbstractionLayer\EntityRepository; +use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria; +use Shopware\Core\Framework\DataAbstractionLayer\Search\Sorting\FieldSorting; +use Shopware\Core\Framework\Log\Package; +use SwagMigrationAssistant\Migration\DataSelection\DefaultEntities; + +#[Package('fundamentals@after-sales')] +class MailHeaderFooterProvider extends AbstractProvider +{ + /** + * @param EntityRepository $mailHeaderFooterRepo + */ + public function __construct(private readonly EntityRepository $mailHeaderFooterRepo) + { + } + + public function getIdentifier(): string + { + return DefaultEntities::MAIL_HEADER_FOOTER; + } + + public function getProvidedData(int $limit, int $offset, Context $context): array + { + $criteria = new Criteria(); + $criteria->setLimit($limit); + $criteria->setOffset($offset); + $criteria->addAssociation('translations'); + $criteria->addSorting(new FieldSorting('id')); + $result = $this->mailHeaderFooterRepo->search($criteria, $context); + + return $this->cleanupSearchResult($result, [ + 'mailHeaderFooterId', + ]); + } + + public function getProvidedTotal(Context $context): int + { + return $this->readTotalFromRepo($this->mailHeaderFooterRepo, $context); + } +} diff --git a/src/DataProvider/Provider/Data/SystemConfigProvider.php b/src/DataProvider/Provider/Data/SystemConfigProvider.php index a5f9ef5f6..29efed507 100644 --- a/src/DataProvider/Provider/Data/SystemConfigProvider.php +++ b/src/DataProvider/Provider/Data/SystemConfigProvider.php @@ -26,6 +26,7 @@ class SystemConfigProvider extends AbstractProvider public static array $CONFIG_KEY_BLOCK_LIST = [ // shop instance, license related or consent data 'core.app.shopId', + 'core.app.shopIdV2', 'core.basicInformation.shopName', 'core.basicInformation.activeCaptchas', 'core.basicInformation.activeCaptchasV2', diff --git a/src/DependencyInjection/dataProvider.xml b/src/DependencyInjection/dataProvider.xml index 4e9d72701..715a4b6aa 100644 --- a/src/DependencyInjection/dataProvider.xml +++ b/src/DependencyInjection/dataProvider.xml @@ -154,6 +154,11 @@ + + + + + diff --git a/src/DependencyInjection/migration.xml b/src/DependencyInjection/migration.xml index b008b1b85..5ae806f3c 100644 --- a/src/DependencyInjection/migration.xml +++ b/src/DependencyInjection/migration.xml @@ -239,13 +239,6 @@ - - - - - - - @@ -314,14 +307,6 @@ - - - - - - - - @@ -409,8 +394,11 @@ - + + + + diff --git a/src/DependencyInjection/shopware.xml b/src/DependencyInjection/shopware.xml index 77f6e4503..bc5201298 100644 --- a/src/DependencyInjection/shopware.xml +++ b/src/DependencyInjection/shopware.xml @@ -178,6 +178,7 @@ + + + + + @@ -478,6 +483,10 @@ + + + + @@ -725,6 +734,11 @@ + + + + @@ -919,6 +933,13 @@ + + + + + + diff --git a/src/Migration/DataSelection/DefaultEntities.php b/src/Migration/DataSelection/DefaultEntities.php index 38e10a5ff..22fa556ec 100644 --- a/src/Migration/DataSelection/DefaultEntities.php +++ b/src/Migration/DataSelection/DefaultEntities.php @@ -70,6 +70,8 @@ final class DefaultEntities final public const LOCALE = 'locale'; + final public const MAIL_HEADER_FOOTER = 'mail_header_footer'; + final public const MAIL_TEMPLATE = 'mail_template'; final public const MAIL_TEMPLATE_TYPE = 'mail_template_type'; diff --git a/src/Migration/Mapping/Lookup/GlobalDocumentBaseConfigLookup.php b/src/Migration/Mapping/Lookup/GlobalDocumentBaseConfigLookup.php index 2b6915fb9..73c91d559 100644 --- a/src/Migration/Mapping/Lookup/GlobalDocumentBaseConfigLookup.php +++ b/src/Migration/Mapping/Lookup/GlobalDocumentBaseConfigLookup.php @@ -23,6 +23,11 @@ class GlobalDocumentBaseConfigLookup implements ResetInterface */ private array $cache = []; + /** + * @var array|null> + */ + private array $configCache = []; + /** * @param EntityRepository $documentBaseConfigRepository * @@ -51,8 +56,28 @@ public function get(string $documentTypeId, Context $context): ?string return $baseConfigId; } + /** + * @return array|null + */ + public function getBaseConfig(string $baseConfigId, Context $context): ?array + { + if (\array_key_exists($baseConfigId, $this->configCache)) { + return $this->configCache[$baseConfigId]; + } + + $criteria = new Criteria([$baseConfigId]); + + $baseConfig = $this->documentBaseConfigRepository->search($criteria, $context)->first(); + + $config = $baseConfig?->getConfig(); + $this->configCache[$baseConfigId] = $config; + + return $config; + } + public function reset(): void { $this->cache = []; + $this->configCache = []; } } diff --git a/src/Migration/Media/Processor/HttpDownloadServiceBase.php b/src/Migration/Media/Processor/HttpDownloadServiceBase.php index 224858cf7..745e99019 100644 --- a/src/Migration/Media/Processor/HttpDownloadServiceBase.php +++ b/src/Migration/Media/Processor/HttpDownloadServiceBase.php @@ -32,7 +32,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; /** @@ -120,7 +120,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); diff --git a/src/Migration/MessageQueue/Handler/ProcessMediaHandler.php b/src/Migration/MessageQueue/Handler/ProcessMediaHandler.php deleted file mode 100644 index 6c8f8d366..000000000 --- a/src/Migration/MessageQueue/Handler/ProcessMediaHandler.php +++ /dev/null @@ -1,139 +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\Content\Media\MediaDefinition; -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\Migration\Logging\Log\Builder\MigrationLogBuilder; -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); - } catch (MigrationException $exception) { - $this->loggingService->addLogEntry( - MigrationLogBuilder::fromMigrationContext($migrationContext) - ->withExceptionMessage($exception->getMessage()) - ->withEntityName(MediaDefinition::ENTITY_NAME) - ->build(ProcessorNotFoundLog::class) - ); - - $this->loggingService->saveLogging($context); - - return; - } - - try { - $workload = $processor->process($migrationContext, $context, $workload); - $this->processFailures($context, $migrationContext, $processor, $workload); - } catch (\Exception $exception) { - $this->loggingService->addLogEntry( - MigrationLogBuilder::fromMigrationContext($migrationContext) - ->withExceptionMessage($exception->getMessage()) - ->withExceptionTrace($exception->getTrace()) - ->withEntityName(MediaDefinition::ENTITY_NAME) - ->build(ExceptionRunLog::class) - ); - - $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..ca440512c 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,183 @@ 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']); + $migrationContext->setDataSet($currentDataSet); + } 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/MigrationContextInterface.php b/src/Migration/MigrationContextInterface.php index 8acac406d..b969d6fcc 100644 --- a/src/Migration/MigrationContextInterface.php +++ b/src/Migration/MigrationContextInterface.php @@ -39,4 +39,6 @@ public function setOffset(int $offset): void; public function getLimit(): int; public function setLimit(int $limit): void; + + public function setDataSet(DataSet $dataSet): void; } diff --git a/src/Migration/Service/MediaFileProcessorService.php b/src/Migration/Service/MediaFileProcessorService.php deleted file mode 100644 index d4c8c2c56..000000000 --- a/src/Migration/Service/MediaFileProcessorService.php +++ /dev/null @@ -1,152 +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\MigrationException; -use SwagMigrationAssistant\Migration\DataSelection\DataSet\DataSet; -use SwagMigrationAssistant\Migration\DataSelection\DataSet\DataSetRegistry; -use SwagMigrationAssistant\Migration\Logging\Log\Builder\MigrationLogBuilder; -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 (MigrationException $exception) { - if ($exception->getErrorCode() === MigrationException::DATASET_NOT_FOUND) { - $this->logDataSetNotFoundException($migrationContext, $exception); - - continue; - } - - throw $exception; - } - } - - if ($currentDataSet::getEntity() !== $mediaFile['entity']) { - $this->addMessageToBus($migrationContext->getRunUuid(), $context, $currentDataSet, $messageMediaUuids); - - try { - $messageMediaUuids = []; - $currentCount = 0; - $currentDataSet = $this->dataSetRegistry->getDataSet($migrationContext, $mediaFile['entity']); - } catch (MigrationException $exception) { - if ($exception->getErrorCode() === MigrationException::DATASET_NOT_FOUND) { - $this->logDataSetNotFoundException($migrationContext, $exception); - - continue; - } - - throw $exception; - } - } - - ++$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); - } - - private function logDataSetNotFoundException( - MigrationContextInterface $migrationContext, - \Throwable $exception, - ): void { - $this->loggingService->addLogEntry( - MigrationLogBuilder::fromMigrationContext($migrationContext) - ->withExceptionMessage($exception->getMessage()) - ->withExceptionTrace($exception->getTrace()) - ->build(DataSetNotFoundLog::class) - ); - } -} 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/OrderDocumentConverter.php b/src/Profile/Shopware/Converter/OrderDocumentConverter.php index f3590b14f..d9a43bbb2 100644 --- a/src/Profile/Shopware/Converter/OrderDocumentConverter.php +++ b/src/Profile/Shopware/Converter/OrderDocumentConverter.php @@ -19,6 +19,7 @@ use SwagMigrationAssistant\Migration\Logging\Log\DocumentTypeNotSupportedLog; use SwagMigrationAssistant\Migration\Logging\LoggingServiceInterface; use SwagMigrationAssistant\Migration\Mapping\Lookup\DocumentTypeLookup; +use SwagMigrationAssistant\Migration\Mapping\Lookup\GlobalDocumentBaseConfigLookup; use SwagMigrationAssistant\Migration\Mapping\Lookup\MediaDefaultFolderLookup; use SwagMigrationAssistant\Migration\Mapping\MappingServiceInterface; use SwagMigrationAssistant\Migration\Media\MediaFileServiceInterface; @@ -44,6 +45,7 @@ public function __construct( protected MediaFileServiceInterface $mediaFileService, protected readonly MediaDefaultFolderLookup $mediaFolderLookup, protected readonly DocumentTypeLookup $documentTypeLookup, + protected readonly GlobalDocumentBaseConfigLookup $globalDocumentBaseConfigLookup, ) { parent::__construct($mappingService, $loggingService); } @@ -101,8 +103,16 @@ public function convert(array $data, Context $context, MigrationContextInterface $converted['fileType'] = FileTypes::PDF; $converted['static'] = true; $converted['deepLinkCode'] = Random::getAlphanumericString(32); - $converted['config'] = []; + if (\array_key_exists('sent', $data)) { + $converted['sent'] = $data['sent']; + } else { + // In Shopware 5 "sent" not exists, so we force it to true, because we assume that if there is a document, the customer received it. + $converted['sent'] = true; + } + $documentType = $this->getDocumentType($data['documenttype']); + $converted['documentType'] = $documentType; + $converted['config'] = $this->getBaseDocumentTypeConfig($documentType['id'], $context); if (isset($data['docID'])) { $converted['config']['documentNumber'] = $data['docID']; @@ -116,6 +126,7 @@ public function convert(array $data, Context $context, MigrationContextInterface unset($data['docID'], $data['documenttype']); } + unset($data['documenttype']); if (isset($data['attributes'])) { $converted['customFields'] = $this->getAttributes($data['attributes'], DefaultEntities::ORDER_DOCUMENT, $this->connectionName, ['id', 'documentID'], $this->context); @@ -185,6 +196,24 @@ protected function getDocumentType(array $data): array return $documentType; } + /** + * @return array + */ + protected function getBaseDocumentTypeConfig(string $documentTypeId, Context $context): array + { + $documentConfigId = $this->globalDocumentBaseConfigLookup->get($documentTypeId, $context); + if ($documentConfigId === null) { + return []; + } + + $documentConfig = $this->globalDocumentBaseConfigLookup->getBaseConfig($documentConfigId, $context); + if ($documentConfig === null) { + return []; + } + + return $documentConfig; + } + /** * @param array $data * diff --git a/src/Profile/Shopware6/Converter/MailHeaderFooterConverter.php b/src/Profile/Shopware6/Converter/MailHeaderFooterConverter.php new file mode 100644 index 000000000..495286267 --- /dev/null +++ b/src/Profile/Shopware6/Converter/MailHeaderFooterConverter.php @@ -0,0 +1,45 @@ + + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SwagMigrationAssistant\Profile\Shopware6\Converter; + +use Shopware\Core\Framework\Log\Package; +use SwagMigrationAssistant\Migration\Converter\ConvertStruct; +use SwagMigrationAssistant\Migration\DataSelection\DefaultEntities; +use SwagMigrationAssistant\Migration\MigrationContextInterface; +use SwagMigrationAssistant\Profile\Shopware6\DataSelection\DataSet\MailHeaderFooterDataSet; +use SwagMigrationAssistant\Profile\Shopware6\Shopware6MajorProfile; + +#[Package('fundamentals@after-sales')] +class MailHeaderFooterConverter extends ShopwareConverter +{ + public function supports(MigrationContextInterface $migrationContext): bool + { + return $migrationContext->getProfile()->getName() === Shopware6MajorProfile::PROFILE_NAME + && $this->getDataSetEntity($migrationContext) === MailHeaderFooterDataSet::getEntity(); + } + + protected function convertData(array $data): ConvertStruct + { + $converted = $data; + + $this->mainMapping = $this->getOrCreateMappingMainCompleteFacade( + DefaultEntities::MAIL_HEADER_FOOTER, + $data['id'], + $converted['id'] + ); + + $this->updateAssociationIds( + $converted['translations'], + DefaultEntities::LANGUAGE, + 'languageId', + DefaultEntities::MAIL_HEADER_FOOTER + ); + + return new ConvertStruct($converted, null, $this->mainMapping['id'] ?? null); + } +} diff --git a/src/Profile/Shopware6/Converter/SalesChannelConverter.php b/src/Profile/Shopware6/Converter/SalesChannelConverter.php index cfe101ee8..d2884b192 100644 --- a/src/Profile/Shopware6/Converter/SalesChannelConverter.php +++ b/src/Profile/Shopware6/Converter/SalesChannelConverter.php @@ -109,6 +109,9 @@ protected function convertData(array $data): ConvertStruct if (isset($data['serviceCategoryId'])) { $converted['serviceCategoryId'] = $this->getMappingIdFacade(DefaultEntities::CATEGORY, $data['serviceCategoryId']); } + if (isset($data['mailHeaderFooterId'])) { + $converted['mailHeaderFooterId'] = $this->getMappingIdFacade(DefaultEntities::MAIL_HEADER_FOOTER, $data['mailHeaderFooterId']); + } $converted['languageId'] = $this->getMappingIdFacade(DefaultEntities::LANGUAGE, $data['languageId']); $converted['currencyId'] = $this->getMappingIdFacade(DefaultEntities::CURRENCY, $data['currencyId']); @@ -131,10 +134,6 @@ protected function convertData(array $data): ConvertStruct ); } - unset( - $converted['mailHeaderFooterId'] - ); - return new ConvertStruct($converted, null, $this->mainMapping['id'] ?? null); } } diff --git a/src/Profile/Shopware6/DataSelection/BasicSettingsDataSelection.php b/src/Profile/Shopware6/DataSelection/BasicSettingsDataSelection.php index 243f7334a..668bf5851 100644 --- a/src/Profile/Shopware6/DataSelection/BasicSettingsDataSelection.php +++ b/src/Profile/Shopware6/DataSelection/BasicSettingsDataSelection.php @@ -21,6 +21,7 @@ use SwagMigrationAssistant\Profile\Shopware6\DataSelection\DataSet\DeliveryTimeDataSet; use SwagMigrationAssistant\Profile\Shopware6\DataSelection\DataSet\DocumentBaseConfigDataSet; use SwagMigrationAssistant\Profile\Shopware6\DataSelection\DataSet\LanguageDataSet; +use SwagMigrationAssistant\Profile\Shopware6\DataSelection\DataSet\MailHeaderFooterDataSet; use SwagMigrationAssistant\Profile\Shopware6\DataSelection\DataSet\MailTemplateDataSet; use SwagMigrationAssistant\Profile\Shopware6\DataSelection\DataSet\MediaFolderDataSet; use SwagMigrationAssistant\Profile\Shopware6\DataSelection\DataSet\MediaFolderInheritanceDataSet; @@ -79,6 +80,7 @@ public function getDataSets(): array new RuleDataSet(), new SnippetSetDataSet(), new SnippetDataSet(), + new MailHeaderFooterDataSet(), new MailTemplateDataSet(), new DeliveryTimeDataSet(), new ShippingMethodDataSet(), diff --git a/src/Profile/Shopware6/DataSelection/DataSet/MailHeaderFooterDataSet.php b/src/Profile/Shopware6/DataSelection/DataSet/MailHeaderFooterDataSet.php new file mode 100644 index 000000000..045b52460 --- /dev/null +++ b/src/Profile/Shopware6/DataSelection/DataSet/MailHeaderFooterDataSet.php @@ -0,0 +1,28 @@ + + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SwagMigrationAssistant\Profile\Shopware6\DataSelection\DataSet; + +use Shopware\Core\Framework\Log\Package; +use SwagMigrationAssistant\Migration\DataSelection\DataSet\DataSet; +use SwagMigrationAssistant\Migration\DataSelection\DefaultEntities; +use SwagMigrationAssistant\Migration\MigrationContextInterface; +use SwagMigrationAssistant\Profile\Shopware6\Shopware6ProfileInterface; + +#[Package('fundamentals@after-sales')] +class MailHeaderFooterDataSet extends DataSet +{ + public static function getEntity(): string + { + return DefaultEntities::MAIL_HEADER_FOOTER; + } + + public function supports(MigrationContextInterface $migrationContext): bool + { + return $migrationContext->getProfile() instanceof Shopware6ProfileInterface; + } +} diff --git a/src/Profile/Shopware6/Gateway/Api/Reader/MailHeaderFooterReader.php b/src/Profile/Shopware6/Gateway/Api/Reader/MailHeaderFooterReader.php new file mode 100644 index 000000000..157675ecc --- /dev/null +++ b/src/Profile/Shopware6/Gateway/Api/Reader/MailHeaderFooterReader.php @@ -0,0 +1,20 @@ + + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SwagMigrationAssistant\Profile\Shopware6\Gateway\Api\Reader; + +use Shopware\Core\Framework\Log\Package; +use SwagMigrationAssistant\Migration\DataSelection\DefaultEntities; + +#[Package('fundamentals@after-sales')] +class MailHeaderFooterReader extends ApiReader +{ + protected function getIdentifier(): string + { + return DefaultEntities::MAIL_HEADER_FOOTER; + } +} diff --git a/src/Profile/Shopware6/Media/HttpOrderDocumentGenerationService.php b/src/Profile/Shopware6/Media/HttpOrderDocumentGenerationService.php index 3283a7852..19a752cb1 100644 --- a/src/Profile/Shopware6/Media/HttpOrderDocumentGenerationService.php +++ b/src/Profile/Shopware6/Media/HttpOrderDocumentGenerationService.php @@ -29,7 +29,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; @@ -284,7 +284,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); diff --git a/src/Profile/Shopware6/Writer/MailHeaderFooterWriter.php b/src/Profile/Shopware6/Writer/MailHeaderFooterWriter.php new file mode 100644 index 000000000..2e9e461ba --- /dev/null +++ b/src/Profile/Shopware6/Writer/MailHeaderFooterWriter.php @@ -0,0 +1,21 @@ + + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SwagMigrationAssistant\Profile\Shopware6\Writer; + +use Shopware\Core\Framework\Log\Package; +use SwagMigrationAssistant\Migration\DataSelection\DefaultEntities; +use SwagMigrationAssistant\Migration\Writer\AbstractWriter; + +#[Package('fundamentals@after-sales')] +class MailHeaderFooterWriter extends AbstractWriter +{ + public function supports(): string + { + return DefaultEntities::MAIL_HEADER_FOOTER; + } +} 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 6fe398d3d..d5550478a 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 @@ -330,6 +330,7 @@ "custom_field_set": "Zusatzfelder", "snippet": "Textbausteine", "snippet_set": "Textbaustein-Sets", + "mail_header_footer": "E-Mail-Kopf- und Fußzeilen", "mail_template": "E-Mail-Template", "product_feature_set": "Wesentliche Merkmale", "product_sorting": "Produkt-Sortierungen", @@ -434,9 +435,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 b9e2998a7..4af536c7b 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 @@ -348,6 +348,7 @@ "custom_field_set": "Custom field sets", "snippet": "Snippets", "snippet_set": "Snippet sets", + "mail_header_footer": "Email headers and footers", "mail_template": "Email templates", "product_feature_set": "Essential characteristics", "product_sorting": "Product sorting", @@ -446,9 +447,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." } }, "error-resolution": { diff --git a/tests/Controller/DataProviderControllerTest.php b/tests/Controller/DataProviderControllerTest.php new file mode 100644 index 000000000..6af110489 --- /dev/null +++ b/tests/Controller/DataProviderControllerTest.php @@ -0,0 +1,73 @@ + + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SwagMigrationAssistant\Test\Controller; + +use PHPUnit\Framework\TestCase; +use Shopware\Core\Framework\Context; +use Shopware\Core\Framework\Log\Package; +use Shopware\Core\Framework\Test\TestCaseBase\AdminApiTestBehaviour; +use Shopware\Core\Framework\Test\TestCaseBase\IntegrationTestBehaviour; +use Shopware\Core\Framework\Test\TestCaseBase\SalesChannelApiTestBehaviour; +use Shopware\Core\Framework\Uuid\Uuid; +use SwagMigrationAssistant\DataProvider\Provider\Data\SystemConfigProvider; +use SwagMigrationAssistant\Migration\DataSelection\DefaultEntities; + +#[Package('fundamentals@after-sales')] +class DataProviderControllerTest extends TestCase +{ + use AdminApiTestBehaviour; + use IntegrationTestBehaviour; + use SalesChannelApiTestBehaviour; + + public function testSystemConfigEntriesGetFiltered(): void + { + $salesChannelId = Uuid::randomHex(); + + $this->createSalesChannel(['id' => $salesChannelId]); + + $configEntries = []; + foreach (SystemConfigProvider::$CONFIG_KEY_BLOCK_LIST as $configKey) { + $configEntries[] = [ + 'configurationKey' => $configKey, + 'configurationValue' => 'should be filtered', + 'salesChannelId' => $salesChannelId, + ]; + } + + $configEntries[] = [ + 'configurationKey' => 'core.some.allowedConfigKey', + 'configurationValue' => 'should be visible', + 'salesChannelId' => $salesChannelId, + ]; + + $this->getContainer()->get('system_config.repository')->create($configEntries, Context::createDefaultContext()); + + $browser = $this->createClient(); + + $browser->request( + 'GET', + '/api/_action/data-provider/get-data', + [ + 'identifier' => DefaultEntities::SYSTEM_CONFIG, + ] + ); + + $response = $browser->getResponse()->getContent(); + + static::assertNotFalse($response); + + $response = json_decode($response, true); + + foreach ($response as $configEntry) { + static::assertNotContains($configEntry['configurationKey'], SystemConfigProvider::$CONFIG_KEY_BLOCK_LIST); + } + + static::assertNotEmpty(array_filter($response, static fn ($entry) => $entry['configurationKey'] === 'core.some.allowedConfigKey')); + } +} diff --git a/tests/Migration/Mapping/Lookup/GlobalDocumentBaseConfigLookupTest.php b/tests/Migration/Mapping/Lookup/GlobalDocumentBaseConfigLookupTest.php index 543fd82eb..75f748a5e 100644 --- a/tests/Migration/Mapping/Lookup/GlobalDocumentBaseConfigLookupTest.php +++ b/tests/Migration/Mapping/Lookup/GlobalDocumentBaseConfigLookupTest.php @@ -45,11 +45,47 @@ public function testReset(): void $cacheProperty = new \ReflectionProperty(GlobalDocumentBaseConfigLookup::class, 'cache'); $cacheProperty->setAccessible(true); + $configCacheProperty = new \ReflectionProperty(GlobalDocumentBaseConfigLookup::class, 'configCache'); + $configCacheProperty->setAccessible(true); + static::assertNotEmpty($cacheProperty->getValue($globalDocumentBaseConfigLookup)); + static::assertNotEmpty($configCacheProperty->getValue($globalDocumentBaseConfigLookup)); $globalDocumentBaseConfigLookup->reset(); static::assertEmpty($cacheProperty->getValue($globalDocumentBaseConfigLookup)); + static::assertEmpty($configCacheProperty->getValue($globalDocumentBaseConfigLookup)); + } + + public function testGetBaseConfig(): void + { + $context = Context::createDefaultContext(); + + $data = self::getDatabaseData(); + $documentTypeID = $data[0]['documentTypeId']; + static::assertIsString($documentTypeID); + + $globalDocumentBaseConfigLookup = $this->getGlobalDocumentBaseConfigLookup(); + $configId = $globalDocumentBaseConfigLookup->get($documentTypeID, $context); + static::assertIsString($configId); + + $baseConfig = $globalDocumentBaseConfigLookup->getBaseConfig($configId, $context); + + static::assertIsArray($baseConfig); + static::assertArrayHasKey('fileTypes', $baseConfig); + static::assertArrayHasKey('referencedDocumentType', $baseConfig); + static::assertSame('invoice', $baseConfig['referencedDocumentType']); + } + + public function testGetBaseConfigFromCache(): void + { + $context = Context::createDefaultContext(); + + $globalDocumentBaseConfigLookup = $this->getMockedGlobalDocumentBaseConfigLookup(); + $baseConfigResult = $globalDocumentBaseConfigLookup->getBaseConfig('anyConfigId', $context); + + static::assertIsArray($baseConfigResult); + static::assertSame($this->getCachedConfig(), $baseConfigResult); } /** @@ -107,6 +143,48 @@ private function getMockedGlobalDocumentBaseConfigLookup(): GlobalDocumentBaseCo $reflectionProperty->setValue($globalDocumentBaseConfigLookup, $cache); + $configCache = ['anyConfigId' => $this->getCachedConfig()]; + $reflectionProperty = new \ReflectionProperty(GlobalDocumentBaseConfigLookup::class, 'configCache'); + $reflectionProperty->setAccessible(true); + $reflectionProperty->setValue($globalDocumentBaseConfigLookup, $configCache); + return $globalDocumentBaseConfigLookup; } + + /** + * @return array + */ + private function getCachedConfig(): array + { + return [ + 'vatId' => 'anyVatId', + 'bankBic' => 'anyBankBic', + 'bankIban' => 'anyBankIban', + 'bankName' => 'anyBankName', + 'pageSize' => 'a4', + 'fileTypes' => [ + 0 => 'html', + 1 => 'pdf', + ], + 'taxNumber' => 'anyTaxNumber', + 'taxOffice' => 'anyTaxOffice', + 'companyName' => 'Example Company', + 'itemsPerPage' => 10, + 'displayFooter' => true, + 'displayHeader' => true, + 'displayPrices' => true, + 'companyAddress' => 'anyAddress', + 'pageOrientation' => 'portrait', + 'displayLineItems' => true, + 'displayPageCount' => true, + 'executiveDirector' => 'anyExecutiveDirector', + 'placeOfFulfillment' => 'anyPlaceOfFulfillment', + 'placeOfJurisdiction' => 'anyPlaceOfJurisdiction', + 'displayReturnAddress' => true, + 'displayCompanyAddress' => true, + 'diplayLineItemPosition' => true, + 'referencedDocumentType' => 'anyReferencedDocumentType', + 'displayAdditionalNoteDelivery' => false, + ]; + } } diff --git a/tests/Migration/MessageQueue/Handler/Processor/MediaProcessingProcessorTest.php b/tests/Migration/MessageQueue/Handler/Processor/MediaProcessingProcessorTest.php index 7f7a1c929..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,122 +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( - $connection, - new Shopware55Profile(), - null, - null, - $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 + ); - $migrationContext = new MigrationContext($connection, new Shopware55Profile(), null, null, $run->getId()); + $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, + ], + ]; + + $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/Shopware54/Converter/OrderDocumentConverterTest.php b/tests/Profile/Shopware54/Converter/OrderDocumentConverterTest.php index c790ac098..4c019a115 100644 --- a/tests/Profile/Shopware54/Converter/OrderDocumentConverterTest.php +++ b/tests/Profile/Shopware54/Converter/OrderDocumentConverterTest.php @@ -18,6 +18,7 @@ use SwagMigrationAssistant\Migration\DataSelection\DefaultEntities; use SwagMigrationAssistant\Migration\Logging\LoggingServiceInterface; use SwagMigrationAssistant\Migration\Mapping\Lookup\DocumentTypeLookup; +use SwagMigrationAssistant\Migration\Mapping\Lookup\GlobalDocumentBaseConfigLookup; use SwagMigrationAssistant\Migration\Mapping\Lookup\MediaDefaultFolderLookup; use SwagMigrationAssistant\Migration\Mapping\MappingServiceInterface; use SwagMigrationAssistant\Migration\MigrationContext; @@ -63,7 +64,8 @@ protected function setUp(): void $this->loggingService, $mediaFileService, $this->createMock(MediaDefaultFolderLookup::class), - $this->createMock(DocumentTypeLookup::class) + $this->createMock(DocumentTypeLookup::class), + $this->createDocumentBaseConfigLookupMock(), ); $connectionId = Uuid::randomHex(); $this->runId = Uuid::randomHex(); @@ -113,10 +115,15 @@ public function testConvert(): void static::assertArrayHasKey('documentType', $converted); static::assertSame('pdf', $converted['fileType']); static::assertTrue($converted['static']); + static::assertTrue($converted['sent']); static::assertSame('Rechnung', $converted['documentType']['name']); static::assertSame('invoice', $converted['documentType']['technicalName']); - static::assertSame($orderDocumentData[0]['docID'], $converted['config']['documentNumber']); - static::assertSame($orderDocumentData[0]['docID'], $converted['config']['custom']['invoiceNumber']); + + $expectedConfig = $this->getDefaultConfig(); + $expectedConfig['documentNumber'] = $orderDocumentData[0]['docID']; + $expectedConfig['custom']['invoiceNumber'] = $orderDocumentData[0]['docID']; + + static::assertSame($expectedConfig, $converted['config']); } public function testConvertShouldLogUnknownType(): void @@ -239,15 +246,65 @@ private function createDocumentConverter(string $converterClass, MappingServiceI $loggingService = new DummyLoggingService(); } + $documentBaseConfigLookupMock = $this->createMock(GlobalDocumentBaseConfigLookup::class); + $documentBaseConfigLookupMock->method('getBaseConfig')->willReturn([]); + $instance = new $converterClass( $mappingService, $loggingService, new DummyMediaFileService(), $this->createMock(MediaDefaultFolderLookup::class), - $this->createMock(DocumentTypeLookup::class) + $this->createMock(DocumentTypeLookup::class), + $documentBaseConfigLookupMock, ); static::assertInstanceOf(ShopwareConverter::class, $instance); return $instance; } + + private function createDocumentBaseConfigLookupMock(): GlobalDocumentBaseConfigLookup + { + $documentBaseConfigLookupMock = $this->createMock(GlobalDocumentBaseConfigLookup::class); + $documentBaseConfigLookupMock->method('get')->willReturn(Uuid::randomHex()); + $documentBaseConfigLookupMock->method('getBaseConfig')->willReturn($this->getDefaultConfig()); + + return $documentBaseConfigLookupMock; + } + + /** + * @return array + */ + private function getDefaultConfig(): array + { + return [ + 'vatId' => '', + 'bankBic' => '', + 'bankIban' => '', + 'bankName' => '', + 'pageSize' => 'a4', + 'fileTypes' => [ + 'html', + 'pdf', + ], + 'taxNumber' => '', + 'taxOffice' => '', + 'companyName' => 'Example Company', + 'itemsPerPage' => 10, + 'displayFooter' => true, + 'displayHeader' => true, + 'displayPrices' => true, + 'companyAddress' => '', + 'pageOrientation' => 'portrait', + 'displayLineItems' => true, + 'displayPageCount' => true, + 'executiveDirector' => '', + 'placeOfFulfillment' => '', + 'placeOfJurisdiction' => '', + 'displayReturnAddress' => true, + 'displayCompanyAddress' => true, + 'displayLineItemPosition' => true, + 'referencedDocumentType' => 'invoice', + 'displayAdditionalNoteDelivery' => false, + ]; + } } diff --git a/tests/Profile/Shopware6/Converter/MailHeaderFooterConverterTest.php b/tests/Profile/Shopware6/Converter/MailHeaderFooterConverterTest.php new file mode 100644 index 000000000..f872ef45c --- /dev/null +++ b/tests/Profile/Shopware6/Converter/MailHeaderFooterConverterTest.php @@ -0,0 +1,43 @@ + + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SwagMigrationAssistant\Test\Profile\Shopware6\Converter; + +use Shopware\Core\Framework\Log\Package; +use SwagMigrationAssistant\Migration\Converter\ConverterInterface; +use SwagMigrationAssistant\Migration\DataSelection\DataSet\DataSet; +use SwagMigrationAssistant\Migration\Logging\LoggingServiceInterface; +use SwagMigrationAssistant\Migration\Mapping\MappingServiceInterface; +use SwagMigrationAssistant\Migration\Media\MediaFileServiceInterface; +use SwagMigrationAssistant\Profile\Shopware6\Converter\MailHeaderFooterConverter; +use SwagMigrationAssistant\Profile\Shopware6\DataSelection\DataSet\MailHeaderFooterDataSet; + +#[Package('fundamentals@after-sales')] +class MailHeaderFooterConverterTest extends ShopwareConverterTest +{ + protected function createConverter( + MappingServiceInterface $mappingService, + LoggingServiceInterface $loggingService, + MediaFileServiceInterface $mediaFileService, + ?array $mappingArray = [], + ): ConverterInterface { + return new MailHeaderFooterConverter( + $mappingService, + $loggingService, + ); + } + + protected function createDataSet(): DataSet + { + return new MailHeaderFooterDataSet(); + } + + protected static function getFixtureBasePath(): string + { + return __DIR__ . '/../../../_fixtures/Shopware6/MailHeaderFooter/'; + } +} diff --git a/tests/Profile/Shopware6/Converter/ShippingMethodConverterTest.php b/tests/Profile/Shopware6/Converter/ShippingMethodConverterTest.php index 6f356d693..4bbb14492 100644 --- a/tests/Profile/Shopware6/Converter/ShippingMethodConverterTest.php +++ b/tests/Profile/Shopware6/Converter/ShippingMethodConverterTest.php @@ -32,16 +32,18 @@ protected function createConverter( MediaFileServiceInterface $mediaFileService, ?array $mappingArray = [], ): ConverterInterface { - $primaryKey = Uuid::randomHex(); + $uuid = Uuid::randomHex(); /** @var StaticEntityRepository $shippingMethodRepository */ $shippingMethodRepository = new StaticEntityRepository([ new IdSearchResult( 1, // trigger already existing technical name check - [$primaryKey => [ - 'primaryKey' => $primaryKey, - 'data' => [], - ]], + [ + $uuid => [ + 'primaryKey' => $uuid, + 'data' => [], + ], + ], new Criteria(), Context::createDefaultContext() ), diff --git a/tests/_fixtures/Shopware6/Document/03-WithGeneratedDocumentAndSentIsTrue/input.php b/tests/_fixtures/Shopware6/Document/03-WithGeneratedDocumentAndSentIsTrue/input.php new file mode 100644 index 000000000..8b82e1a62 --- /dev/null +++ b/tests/_fixtures/Shopware6/Document/03-WithGeneratedDocumentAndSentIsTrue/input.php @@ -0,0 +1,76 @@ + + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return [ + 'orderId' => '32865ab671214a9fbaa215d1b22af44d', + 'fileType' => 'pdf', + 'config' => [ + 'displayPrices' => false, + 'logo' => null, + 'filenamePrefix' => 'delivery_note_', + 'filenameSuffix' => '', + 'documentNumber' => '1000', + 'pageOrientation' => 'portrait', + 'pageSize' => 'a4', + 'displayFooter' => true, + 'displayHeader' => true, + 'displayLineItems' => true, + 'displayLineItemPosition' => null, + 'itemsPerPage' => 10, + 'displayPageCount' => true, + 'displayCompanyAddress' => true, + 'title' => null, + 'companyAddress' => 'Muster AG - Ebbinghoff 10 - 48624 Schöppingen', + 'companyName' => 'Muster AG', + 'companyEmail' => null, + 'companyUrl' => null, + 'taxNumber' => '000111000', + 'taxOffice' => 'Coesfeld', + 'vatId' => 'XX 111 222 333', + 'bankName' => 'Kreissparkasse Münster', + 'bankIban' => 'DE11111222223333344444', + 'bankBic' => 'SWSKKEFF', + 'placeOfJurisdiction' => 'Coesfeld', + 'placeOfFulfillment' => 'Coesfeld', + 'executiveDirector' => 'Max Mustermann', + 'custom' => [ + 'deliveryDate' => '2020-12-03T00:00:00+00:00', + 'deliveryNoteDate' => '2020-12-02T11:13:58.636Z', + 'deliveryNoteNumber' => '1000', + ], + 'extensions' => [ + ], + 'name' => 'delivery_note', + 'global' => true, + 'documentTypeId' => '3292943c32e5499f9a54cc5ff1a16abe', + 'translated' => [ + ], + 'id' => 'be54fb58fe374cd3865a82796350e6d5', + 'diplayLineItemPosition' => true, + 'documentComment' => 'Comment here', + 'documentDate' => '2020-12-02T11:13:58.810Z', + ], + 'sent' => true, + 'static' => false, + 'deepLinkCode' => 'iFlpLFZam1KS8AAT4SV9Tn7nEsSnSQIY', + 'documentType' => [ + 'name' => 'Delivery note', + 'technicalName' => 'delivery_note', + 'id' => '3292943c32e5499f9a54cc5ff1a16abe', + ], + 'documentMediaFile' => [ + 'fileExtension' => 'pdf', + 'fileSize' => 211211, + 'uploadedAt' => '2021-01-06T07:01:48.896+00:00', + 'url' => 'http://nextsupport.local/api/v3/_action/document/2efbab03c3d84f968bcd5636f9aa4057/NMXl9wyGzE2aB2CAarYHYg2xqtx8XXLN', + 'fileName' => 'delivery_note_10001', + 'mediaFolderId' => 'e49eafe11fbe4396bf1102caeec1f67f', + 'private' => true, + 'id' => '7bcdcfa0af944bb6ab05d2f29811cbb6', + ], + 'id' => '439564d3fc194166bf1c2c9e21942469', +]; diff --git a/tests/_fixtures/Shopware6/Document/03-WithGeneratedDocumentAndSentIsTrue/mapping.php b/tests/_fixtures/Shopware6/Document/03-WithGeneratedDocumentAndSentIsTrue/mapping.php new file mode 100644 index 000000000..735a57e69 --- /dev/null +++ b/tests/_fixtures/Shopware6/Document/03-WithGeneratedDocumentAndSentIsTrue/mapping.php @@ -0,0 +1,16 @@ + + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use SwagMigrationAssistant\Migration\DataSelection\DefaultEntities; + +return [ + [ + 'entityName' => DefaultEntities::ORDER_DOCUMENT_TYPE, + 'oldIdentifier' => 'delivery_note', + 'newIdentifier' => '41248655c60c4dd58cde43f14cd4f149', + ], +]; diff --git a/tests/_fixtures/Shopware6/Document/03-WithGeneratedDocumentAndSentIsTrue/media.php b/tests/_fixtures/Shopware6/Document/03-WithGeneratedDocumentAndSentIsTrue/media.php new file mode 100644 index 000000000..86b97d3b1 --- /dev/null +++ b/tests/_fixtures/Shopware6/Document/03-WithGeneratedDocumentAndSentIsTrue/media.php @@ -0,0 +1,16 @@ + + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return [ + [ + 'entity' => 'order_document', + 'uri' => 'http://nextsupport.local/api/v3/_action/document/2efbab03c3d84f968bcd5636f9aa4057/NMXl9wyGzE2aB2CAarYHYg2xqtx8XXLN', + 'fileName' => 'delivery_note_10001', + 'fileSize' => 211211, + 'mediaId' => '7bcdcfa0af944bb6ab05d2f29811cbb6', + ], +]; diff --git a/tests/_fixtures/Shopware6/Document/03-WithGeneratedDocumentAndSentIsTrue/output.php b/tests/_fixtures/Shopware6/Document/03-WithGeneratedDocumentAndSentIsTrue/output.php new file mode 100644 index 000000000..f918e6f84 --- /dev/null +++ b/tests/_fixtures/Shopware6/Document/03-WithGeneratedDocumentAndSentIsTrue/output.php @@ -0,0 +1,70 @@ + + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return [ + 'orderId' => '32865ab671214a9fbaa215d1b22af44d', + 'fileType' => 'pdf', + 'config' => [ + 'displayPrices' => false, + 'logo' => null, + 'filenamePrefix' => 'delivery_note_', + 'filenameSuffix' => '', + 'documentNumber' => '1000', + 'pageOrientation' => 'portrait', + 'pageSize' => 'a4', + 'displayFooter' => true, + 'displayHeader' => true, + 'displayLineItems' => true, + 'displayLineItemPosition' => null, + 'itemsPerPage' => 10, + 'displayPageCount' => true, + 'displayCompanyAddress' => true, + 'title' => null, + 'companyAddress' => 'Muster AG - Ebbinghoff 10 - 48624 Schöppingen', + 'companyName' => 'Muster AG', + 'companyEmail' => null, + 'companyUrl' => null, + 'taxNumber' => '000111000', + 'taxOffice' => 'Coesfeld', + 'vatId' => 'XX 111 222 333', + 'bankName' => 'Kreissparkasse Münster', + 'bankIban' => 'DE11111222223333344444', + 'bankBic' => 'SWSKKEFF', + 'placeOfJurisdiction' => 'Coesfeld', + 'placeOfFulfillment' => 'Coesfeld', + 'executiveDirector' => 'Max Mustermann', + 'custom' => [ + 'deliveryDate' => '2020-12-03T00:00:00+00:00', + 'deliveryNoteDate' => '2020-12-02T11:13:58.636Z', + 'deliveryNoteNumber' => '1000', + ], + 'extensions' => [ + ], + 'name' => 'delivery_note', + 'global' => true, + 'documentTypeId' => '41248655c60c4dd58cde43f14cd4f149', + 'translated' => [ + ], + 'id' => 'be54fb58fe374cd3865a82796350e6d5', + 'diplayLineItemPosition' => true, + 'documentComment' => 'Comment here', + 'documentDate' => '2020-12-02T11:13:58.810Z', + ], + 'sent' => true, + 'static' => false, + 'deepLinkCode' => 'iFlpLFZam1KS8AAT4SV9Tn7nEsSnSQIY', + 'documentMediaFile' => [ + 'uploadedAt' => '2021-01-06T07:01:48.896+00:00', + 'fileName' => 'delivery_note_10001', + 'mediaFolderId' => 'e49eafe11fbe4396bf1102caeec1f67f', + 'private' => true, + 'id' => '7bcdcfa0af944bb6ab05d2f29811cbb6', + 'hasFile' => false, + ], + 'id' => '439564d3fc194166bf1c2c9e21942469', + 'documentTypeId' => '41248655c60c4dd58cde43f14cd4f149', +]; diff --git a/tests/_fixtures/Shopware6/MailHeaderFooter/01-HappyCase/input.php b/tests/_fixtures/Shopware6/MailHeaderFooter/01-HappyCase/input.php new file mode 100644 index 000000000..575af794f --- /dev/null +++ b/tests/_fixtures/Shopware6/MailHeaderFooter/01-HappyCase/input.php @@ -0,0 +1,22 @@ + + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return [ + 'id' => 'a1b2c3d4e5f64a7b8c9d0e1f2a3b4c5d', + 'systemDefault' => false, + 'translations' => [ + [ + 'languageId' => '2fbb5fe2e29a4d70aa5854ce7ce3e20b', + 'name' => 'Test Header Footer', + 'description' => 'Test description', + 'headerHtml' => '
Test HTML Header
', + 'headerPlain' => 'Test Plain Header', + 'footerHtml' => '
Test HTML Footer
', + 'footerPlain' => 'Test Plain Footer', + ], + ], +]; diff --git a/tests/_fixtures/Shopware6/MailHeaderFooter/01-HappyCase/mapping.php b/tests/_fixtures/Shopware6/MailHeaderFooter/01-HappyCase/mapping.php new file mode 100644 index 000000000..668e2d734 --- /dev/null +++ b/tests/_fixtures/Shopware6/MailHeaderFooter/01-HappyCase/mapping.php @@ -0,0 +1,16 @@ + + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use SwagMigrationAssistant\Migration\DataSelection\DefaultEntities; + +return [ + [ + 'entityName' => DefaultEntities::LANGUAGE, + 'oldIdentifier' => '2fbb5fe2e29a4d70aa5854ce7ce3e20b', + 'newIdentifier' => '5dd637353d044752ae6a8c6e7f53430b', + ], +]; diff --git a/tests/_fixtures/Shopware6/MailHeaderFooter/01-HappyCase/output.php b/tests/_fixtures/Shopware6/MailHeaderFooter/01-HappyCase/output.php new file mode 100644 index 000000000..38459e647 --- /dev/null +++ b/tests/_fixtures/Shopware6/MailHeaderFooter/01-HappyCase/output.php @@ -0,0 +1,22 @@ + + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return [ + 'id' => 'a1b2c3d4e5f64a7b8c9d0e1f2a3b4c5d', + 'systemDefault' => false, + 'translations' => [ + [ + 'languageId' => '5dd637353d044752ae6a8c6e7f53430b', + 'name' => 'Test Header Footer', + 'description' => 'Test description', + 'headerHtml' => '
Test HTML Header
', + 'headerPlain' => 'Test Plain Header', + 'footerHtml' => '
Test HTML Footer
', + 'footerPlain' => 'Test Plain Footer', + ], + ], +]; diff --git a/tests/acceptance/fixtures/AcceptanceTest.ts b/tests/acceptance/fixtures/AcceptanceTest.ts index 463044855..7f76fbf44 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,9 +11,13 @@ export interface MigrationFixtureTypes { MigrationUser: FixtureTypes['ShopAdmin']; DatabaseCredentials: DatabaseCredentialsStruct; EntityCounter: EntityCounterStruct; - MediaProcessObserver: MediaProcessObserverStruct; } export type FixtureTypes = MigrationFixtureTypes & BaseTypes; -export const test = mergeTests(ShopwareTestSuite, MigrationUser, DatabaseCredentials, EntityCounter, MediaProcessObserver); +export const test = mergeTests( + ShopwareTestSuite, + MigrationUser, + DatabaseCredentials, + EntityCounter, +); diff --git a/tests/acceptance/fixtures/MediaProcessObserver.ts b/tests/acceptance/fixtures/MediaProcessObserver.ts deleted file mode 100644 index 3b6e46331..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 f3ce5fdf5..d542ce2ad 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, }) => { // TODO: fix & update snapshots // eslint-disable-next-line playwright/no-skipped-test @@ -38,11 +37,8 @@ test('As a shop owner I want to migrate my data from my old SW5 shop to SW6 via await page.getByRole('button', { name: 'Start', exact: true }).click(); await page.getByRole('button', { name: 'Continue' }).click(); await page.getByPlaceholder('Enter name').fill('sw5local'); - await page - .locator('div') - .filter({ hasText: /^Shopware 5\.5 - shopware AG$/ }) - .click(); - await page.getByText('Shopware 5.5 - shopware AG').click(); + await page.locator('div').filter({ hasText: /^Shopware 5\.5 - shopware AG$/ }).click(); + await page.locator(':text("Shopware 5.5 - shopware AG"):visible').click(); await page.getByText('Select gateway').click(); await page.getByPlaceholder('Select gateway').fill('Local'); await page.getByText('Local database').click(); @@ -110,25 +106,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); @@ -143,8 +120,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);