diff --git a/UPGRADE.md b/UPGRADE.md
index efde4f2d8..e3e041e18 100644
--- a/UPGRADE.md
+++ b/UPGRADE.md
@@ -67,6 +67,10 @@
- Added methods `setProfile()`, `getGateway()`, `setGateway()` and `setConnection()` to `SwagMigrationAssistant\Migration\MigrationContext`
- Added null checks to methods `getProfile()` and `getGateway()` in `SwagMigrationAssistant\Migration\MigrationContext` to ensure that a profile and gateway is set before usage
+- [BREAKING] [#57](https://github.com/shopware/SwagMigrationAssistant/pull/57) feat!: checksum and reset via mq
+ - [BREAKING] Renamed method `cleanupMappingChecksums()` to `startCleanupMappingChecksums()` in `SwagMigrationAssistant\Migration\Run\RunServiceInterface` and implementation `SwagMigrationAssistant\Migration\Run\RunService`
+ - [BREAKING] Renamed method `cleanupMigrationData()` to `startTruncateMigrationData()` in `SwagMigrationAssistant\Migration\Run\RunServiceInterface` and implementation `SwagMigrationAssistant\Migration\Run\RunService`
+
# 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.
- [BREAKING] MIG-1053 - Refactored both Shopware 5 and Shopware 6 EnvironmentReader classes to provide more information about exceptions.
diff --git a/src/Controller/StatusController.php b/src/Controller/StatusController.php
index cdd95de87..677f9678e 100644
--- a/src/Controller/StatusController.php
+++ b/src/Controller/StatusController.php
@@ -341,13 +341,7 @@ public function resetChecksums(Request $request, Context $context): Response
throw RoutingException::missingRequestParameter('connectionId');
}
- $connection = $this->migrationConnectionRepo->search(new Criteria([$connectionId]), $context)->getEntities()->first();
-
- if ($connection === null) {
- throw MigrationException::noConnectionFound();
- }
-
- $this->runService->cleanupMappingChecksums($connectionId, $context);
+ $this->runService->startCleanupMappingChecksums($connectionId, $context);
return new Response();
}
@@ -360,18 +354,18 @@ public function resetChecksums(Request $request, Context $context): Response
)]
public function cleanupMigrationData(Context $context): Response
{
- $this->runService->cleanupMigrationData($context);
+ $this->runService->startTruncateMigrationData($context);
return new Response();
}
#[Route(
- path: '/api/_action/migration/get-reset-status',
- name: 'api.admin.migration.get-reset-status',
+ path: '/api/_action/migration/is-truncating-migration-data',
+ name: 'api.admin.migration.is-truncating-migration-data',
defaults: ['_acl' => ['admin']],
methods: [Request::METHOD_GET]
)]
- public function getResetStatus(Context $context): JsonResponse
+ public function isTruncatingMigrationData(Context $context): JsonResponse
{
$settings = $this->generalSettingRepo->search(new Criteria(), $context)->getEntities()->first();
@@ -381,4 +375,26 @@ public function getResetStatus(Context $context): JsonResponse
return new JsonResponse($settings->isReset());
}
+
+ #[Route(
+ path: '/api/_action/migration/is-resetting-checksums',
+ name: 'api.admin.migration.is-resetting-checksums',
+ defaults: ['_acl' => ['admin']],
+ methods: [Request::METHOD_GET]
+ )]
+ public function isResettingChecksums(Context $context): JsonResponse
+ {
+ $settings = $this->generalSettingRepo
+ ->search(new Criteria(), $context)
+ ->getEntities()
+ ->first();
+
+ if ($settings === null) {
+ return new JsonResponse(false);
+ }
+
+ return new JsonResponse(
+ $settings->isResettingChecksums()
+ );
+ }
}
diff --git a/src/Core/Migration/Migration1759000000AddIsResettingChecksumsToSetting.php b/src/Core/Migration/Migration1759000000AddIsResettingChecksumsToSetting.php
new file mode 100644
index 000000000..f2ece4927
--- /dev/null
+++ b/src/Core/Migration/Migration1759000000AddIsResettingChecksumsToSetting.php
@@ -0,0 +1,37 @@
+
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace SwagMigrationAssistant\Core\Migration;
+
+use Doctrine\DBAL\Connection;
+use Shopware\Core\Framework\Log\Package;
+use Shopware\Core\Framework\Migration\MigrationStep;
+
+#[Package('fundamentals@after-sales')]
+class Migration1759000000AddIsResettingChecksumsToSetting extends MigrationStep
+{
+ public const TABLE = 'swag_migration_general_setting';
+
+ public const COLUMN = 'is_resetting_checksums';
+
+ public function getCreationTimestamp(): int
+ {
+ return 1759000000;
+ }
+
+ public function update(Connection $connection): void
+ {
+ $this->addColumn(
+ connection: $connection,
+ table: self::TABLE,
+ column: self::COLUMN,
+ type: 'TINYINT(1)',
+ nullable: false,
+ default: '0'
+ );
+ }
+}
diff --git a/src/DependencyInjection/migration.xml b/src/DependencyInjection/migration.xml
index 9b45b2d86..a41275f96 100644
--- a/src/DependencyInjection/migration.xml
+++ b/src/DependencyInjection/migration.xml
@@ -185,7 +185,6 @@
-
@@ -312,7 +311,7 @@
-
+
@@ -328,7 +327,14 @@
+
+
+
+
+
+
+
@@ -368,7 +374,6 @@
-
diff --git a/src/Exception/MigrationException.php b/src/Exception/MigrationException.php
index 2f13fa472..0ac3376af 100644
--- a/src/Exception/MigrationException.php
+++ b/src/Exception/MigrationException.php
@@ -32,6 +32,10 @@ class MigrationException extends HttpException
public const MIGRATION_IS_ALREADY_RUNNING = 'SWAG_MIGRATION__MIGRATION_IS_ALREADY_RUNNING';
+ public const MIGRATION_IS_RESETTING_CHECKSUMS = 'SWAG_MIGRATION__MIGRATION_IS_RESETTING_CHECKSUMS';
+
+ public const MIGRATION_IS_TRUNCATING_DATA = 'SWAG_MIGRATION__MIGRATION_IS_TRUNCATING_DATA';
+
public const NO_CONNECTION_IS_SELECTED = 'SWAG_MIGRATION__NO_CONNECTION_IS_SELECTED';
public const NO_CONNECTION_FOUND = 'SWAG_MIGRATION__NO_CONNECTION_FOUND';
@@ -294,6 +298,24 @@ public static function migrationIsAlreadyRunning(): self
);
}
+ public static function checksumResetRunning(): self
+ {
+ return new MigrationIsAlreadyRunningException(
+ Response::HTTP_BAD_REQUEST,
+ self::MIGRATION_IS_RESETTING_CHECKSUMS,
+ 'Checksum reset is running.',
+ );
+ }
+
+ public static function truncatingDataRunning(): self
+ {
+ return new MigrationIsAlreadyRunningException(
+ Response::HTTP_BAD_REQUEST,
+ self::MIGRATION_IS_TRUNCATING_DATA,
+ 'Data truncation is running.',
+ );
+ }
+
public static function noConnectionIsSelected(): self
{
return new self(
diff --git a/src/Migration/MessageQueue/Handler/Processor/AbortingProcessor.php b/src/Migration/MessageQueue/Handler/Processor/AbortingProcessor.php
index bf2a1773d..011df47bf 100644
--- a/src/Migration/MessageQueue/Handler/Processor/AbortingProcessor.php
+++ b/src/Migration/MessageQueue/Handler/Processor/AbortingProcessor.php
@@ -12,11 +12,10 @@
use Shopware\Core\Framework\Log\Package;
use SwagMigrationAssistant\Migration\Data\SwagMigrationDataCollection;
use SwagMigrationAssistant\Migration\Media\SwagMigrationMediaFileCollection;
-use SwagMigrationAssistant\Migration\MessageQueue\Message\MigrationProcessMessage;
+use SwagMigrationAssistant\Migration\MessageQueue\Message\ResetChecksumMessage;
use SwagMigrationAssistant\Migration\MigrationContextInterface;
use SwagMigrationAssistant\Migration\Run\MigrationProgress;
use SwagMigrationAssistant\Migration\Run\MigrationStep;
-use SwagMigrationAssistant\Migration\Run\RunServiceInterface;
use SwagMigrationAssistant\Migration\Run\RunTransitionServiceInterface;
use SwagMigrationAssistant\Migration\Run\SwagMigrationRunCollection;
use SwagMigrationAssistant\Migration\Run\SwagMigrationRunEntity;
@@ -35,7 +34,6 @@ public function __construct(
EntityRepository $migrationDataRepo,
EntityRepository $migrationMediaFileRepo,
RunTransitionServiceInterface $runTransitionService,
- private readonly RunServiceInterface $runService,
private readonly MessageBusInterface $bus,
) {
parent::__construct(
@@ -57,12 +55,14 @@ public function process(
SwagMigrationRunEntity $run,
MigrationProgress $progress,
): void {
- $connection = $migrationContext->getConnection();
- $this->runService->cleanupMappingChecksums($connection->getId(), $context);
-
- $this->runTransitionService->forceTransitionToRunStep($migrationContext->getRunUuid(), MigrationStep::CLEANUP);
- $progress->setIsAborted(true);
- $this->updateProgress($migrationContext->getRunUuid(), $progress, $context);
- $this->bus->dispatch(new MigrationProcessMessage($context, $migrationContext->getRunUuid()));
+ $this->bus->dispatch(new ResetChecksumMessage(
+ $migrationContext->getConnection()->getId(),
+ $context,
+ $run->getId(),
+ $progress->getCurrentEntity(),
+ null,
+ 0,
+ true // abort flow flag
+ ));
}
}
diff --git a/src/Migration/MessageQueue/Handler/Processor/CleanUpProcessor.php b/src/Migration/MessageQueue/Handler/Processor/CleanUpProcessor.php
index 5ebe6ac0e..f6c186d0f 100644
--- a/src/Migration/MessageQueue/Handler/Processor/CleanUpProcessor.php
+++ b/src/Migration/MessageQueue/Handler/Processor/CleanUpProcessor.php
@@ -9,11 +9,9 @@
use Doctrine\DBAL\Connection;
use Shopware\Core\Framework\Context;
-use Shopware\Core\Framework\DataAbstractionLayer\Dbal\QueryBuilder;
use Shopware\Core\Framework\DataAbstractionLayer\EntityRepository;
use Shopware\Core\Framework\Log\Package;
use SwagMigrationAssistant\Migration\Data\SwagMigrationDataCollection;
-use SwagMigrationAssistant\Migration\Data\SwagMigrationDataDefinition;
use SwagMigrationAssistant\Migration\Media\SwagMigrationMediaFileCollection;
use SwagMigrationAssistant\Migration\MessageQueue\Message\MigrationProcessMessage;
use SwagMigrationAssistant\Migration\MigrationContextInterface;
@@ -27,6 +25,8 @@
#[Package('fundamentals@after-sales')]
class CleanUpProcessor extends AbstractProcessor
{
+ public const BATCH_SIZE = 250;
+
/**
* @param EntityRepository $migrationRunRepo
* @param EntityRepository $migrationDataRepo
@@ -37,7 +37,7 @@ public function __construct(
EntityRepository $migrationDataRepo,
EntityRepository $migrationMediaFileRepo,
RunTransitionServiceInterface $runTransitionService,
- private readonly Connection $dbalConnection,
+ private readonly Connection $connection,
private readonly MessageBusInterface $bus,
) {
parent::__construct(
@@ -59,22 +59,51 @@ public function process(
SwagMigrationRunEntity $run,
MigrationProgress $progress,
): void {
- $deleteCount = (int) $this->removeMigrationData();
+ if ($progress->getTotal() === 0) {
+ $progress->setTotal($this->getMigrationDataTotal());
+ $progress->setProgress(0);
+ }
+
+ $deleteCount = $this->removeMigrationData();
+
+ if ($deleteCount > 0) {
+ $progress->setProgress(
+ $progress->getProgress() + $deleteCount
+ );
+ }
if ($deleteCount <= 0) {
- $this->runTransitionService->transitionToRunStep($migrationContext->getRunUuid(), MigrationStep::INDEXING);
+ $this->runTransitionService->transitionToRunStep(
+ $migrationContext->getRunUuid(),
+ MigrationStep::INDEXING
+ );
}
- $this->updateProgress($migrationContext->getRunUuid(), $progress, $context);
- $this->bus->dispatch(new MigrationProcessMessage($context, $migrationContext->getRunUuid()));
+ $this->updateProgress(
+ $migrationContext->getRunUuid(),
+ $progress,
+ $context
+ );
+
+ $this->bus->dispatch(new MigrationProcessMessage(
+ $context,
+ $migrationContext->getRunUuid()
+ ));
}
- private function removeMigrationData(): int|string
+ private function removeMigrationData(): int
{
- return (new QueryBuilder($this->dbalConnection))
- ->delete(SwagMigrationDataDefinition::ENTITY_NAME)
- ->andWhere('written = 1')
- ->setMaxResults(1000)
+ return (int) $this->connection->createQueryBuilder()
+ ->delete('swag_migration_data')
+ ->setMaxResults(self::BATCH_SIZE)
->executeStatement();
}
+
+ private function getMigrationDataTotal(): int
+ {
+ return (int) $this->connection->createQueryBuilder()
+ ->select('COUNT(id)')->from('swag_migration_data')
+ ->executeQuery()
+ ->fetchOne();
+ }
}
diff --git a/src/Migration/MessageQueue/Handler/ResetChecksumHandler.php b/src/Migration/MessageQueue/Handler/ResetChecksumHandler.php
new file mode 100644
index 000000000..76618154f
--- /dev/null
+++ b/src/Migration/MessageQueue/Handler/ResetChecksumHandler.php
@@ -0,0 +1,195 @@
+
+ * 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 Doctrine\DBAL\Connection;
+use Doctrine\DBAL\ParameterType;
+use Shopware\Core\Framework\Context;
+use Shopware\Core\Framework\DataAbstractionLayer\EntityRepository;
+use Shopware\Core\Framework\Log\Package;
+use Shopware\Core\Framework\Uuid\Uuid;
+use SwagMigrationAssistant\Migration\DataSelection\DefaultEntities;
+use SwagMigrationAssistant\Migration\MessageQueue\Message\MigrationProcessMessage;
+use SwagMigrationAssistant\Migration\MessageQueue\Message\ResetChecksumMessage;
+use SwagMigrationAssistant\Migration\Run\MigrationProgress;
+use SwagMigrationAssistant\Migration\Run\MigrationStep;
+use SwagMigrationAssistant\Migration\Run\ProgressDataSetCollection;
+use SwagMigrationAssistant\Migration\Run\RunTransitionServiceInterface;
+use SwagMigrationAssistant\Migration\Run\SwagMigrationRunCollection;
+use Symfony\Component\Messenger\Attribute\AsMessageHandler;
+use Symfony\Component\Messenger\MessageBusInterface;
+
+/**
+ * @internal
+ */
+#[AsMessageHandler]
+#[Package('fundamentals@after-sales')]
+final readonly class ResetChecksumHandler
+{
+ public const BATCH_SIZE = 250;
+
+ /**
+ * @param EntityRepository $migrationRunRepo
+ */
+ public function __construct(
+ private Connection $connection,
+ private MessageBusInterface $messageBus,
+ private EntityRepository $migrationRunRepo,
+ private RunTransitionServiceInterface $runTransitionService,
+ ) {
+ }
+
+ public function __invoke(ResetChecksumMessage $message): void
+ {
+ $connectionId = $message->getConnectionId();
+ $totalMappings = $message->getTotalMappings();
+ $progress = null;
+
+ if ($totalMappings === null) {
+ $totalMappings = $this->getTotalMappingsCount($connectionId);
+
+ if ($message->getRunId() !== null && $totalMappings > 0) {
+ $progress = $this->updateProgress(
+ $message,
+ 0,
+ $totalMappings,
+ $message->getContext()
+ );
+ }
+ }
+
+ $affectedRows = $this->resetChecksums($connectionId);
+
+ if ($affectedRows === 0) {
+ $this->handleCompletion($message, $progress);
+
+ return;
+ }
+
+ $newProcessedCount = $message->getProcessedMappings() + $affectedRows;
+
+ if ($message->getRunId() !== null) {
+ $progress = $this->updateProgress(
+ $message,
+ $newProcessedCount,
+ $totalMappings,
+ $message->getContext()
+ );
+ }
+
+ if ($affectedRows < self::BATCH_SIZE) {
+ $this->handleCompletion($message, $progress);
+
+ return;
+ }
+
+ $this->messageBus->dispatch(new ResetChecksumMessage(
+ $message->getConnectionId(),
+ $message->getContext(),
+ $message->getRunId(),
+ $message->getEntity(),
+ $totalMappings,
+ $newProcessedCount,
+ $message->isPartOfAbort()
+ ));
+ }
+
+ private function handleCompletion(ResetChecksumMessage $message, ?MigrationProgress $progress): void
+ {
+ $this->clearResettingChecksumsFlag();
+
+ if (!$message->isPartOfAbort() || $message->getRunId() === null) {
+ return;
+ }
+
+ $runId = $message->getRunId();
+ $context = $message->getContext();
+
+ $this->runTransitionService->forceTransitionToRunStep(
+ $runId,
+ MigrationStep::CLEANUP
+ );
+
+ $finalProgress = new MigrationProgress(
+ 0,
+ 0,
+ $progress?->getDataSets() ?? new ProgressDataSetCollection(),
+ $message->getEntity() ?? DefaultEntities::RULE,
+ $progress?->getCurrentEntityProgress() ?? 0
+ );
+ $finalProgress->setIsAborted(true);
+
+ $this->migrationRunRepo->upsert([
+ [
+ 'id' => $runId,
+ 'progress' => $finalProgress->jsonSerialize(),
+ ],
+ ], $context);
+
+ $this->messageBus->dispatch(new MigrationProcessMessage(
+ $context,
+ $runId
+ ));
+ }
+
+ private function resetChecksums(string $connectionId): int
+ {
+ return (int) $this->connection->executeStatement(
+ 'UPDATE swag_migration_mapping
+ SET checksum = NULL
+ WHERE checksum IS NOT NULL
+ AND connection_id = :connectionId
+ LIMIT :limit',
+ [
+ 'connectionId' => Uuid::fromHexToBytes($connectionId),
+ 'limit' => self::BATCH_SIZE,
+ ],
+ [
+ 'connectionId' => ParameterType::BINARY,
+ 'limit' => ParameterType::INTEGER,
+ ]
+ );
+ }
+
+ private function getTotalMappingsCount(string $connectionId): int
+ {
+ return (int) $this->connection->createQueryBuilder()
+ ->select('COUNT(m.id)')
+ ->from('swag_migration_mapping', 'm')
+ ->where('m.checksum IS NOT NULL')
+ ->andWhere('m.connection_id = :connectionId')
+ ->setParameter('connectionId', Uuid::fromHexToBytes($connectionId))
+ ->executeQuery()
+ ->fetchOne();
+ }
+
+ private function updateProgress(ResetChecksumMessage $message, int $processed, int $total, Context $context): MigrationProgress
+ {
+ $progress = new MigrationProgress(
+ $processed,
+ $total,
+ new ProgressDataSetCollection(),
+ $message->getEntity() ?? DefaultEntities::RULE,
+ $processed
+ );
+
+ $this->migrationRunRepo->update([[
+ 'id' => $message->getRunId(),
+ 'progress' => $progress->jsonSerialize(),
+ ]], $context);
+
+ return $progress;
+ }
+
+ private function clearResettingChecksumsFlag(): void
+ {
+ $this->connection->executeStatement(
+ 'UPDATE swag_migration_general_setting SET `is_resetting_checksums` = 0;'
+ );
+ }
+}
diff --git a/src/Migration/MessageQueue/Handler/CleanupMigrationHandler.php b/src/Migration/MessageQueue/Handler/TruncateMigrationHandler.php
similarity index 53%
rename from src/Migration/MessageQueue/Handler/CleanupMigrationHandler.php
rename to src/Migration/MessageQueue/Handler/TruncateMigrationHandler.php
index 536118b91..79cd8978b 100644
--- a/src/Migration/MessageQueue/Handler/CleanupMigrationHandler.php
+++ b/src/Migration/MessageQueue/Handler/TruncateMigrationHandler.php
@@ -9,7 +9,7 @@
use Doctrine\DBAL\Connection;
use Shopware\Core\Framework\Log\Package;
-use SwagMigrationAssistant\Migration\MessageQueue\Message\CleanupMigrationMessage;
+use SwagMigrationAssistant\Migration\MessageQueue\Message\TruncateMigrationMessage;
use Symfony\Component\Messenger\Attribute\AsMessageHandler;
use Symfony\Component\Messenger\MessageBusInterface;
@@ -18,15 +18,17 @@
/**
* @internal
*/
-final class CleanupMigrationHandler
+final class TruncateMigrationHandler
{
+ private const BATCH_SIZE = 250;
+
public function __construct(
private readonly Connection $connection,
private readonly MessageBusInterface $bus,
) {
}
- public function __invoke(CleanupMigrationMessage $message): void
+ public function __invoke(TruncateMigrationMessage $message): void
{
$currentStep = 0;
$tablesToReset = [
@@ -38,16 +40,40 @@ public function __invoke(CleanupMigrationMessage $message): void
'swag_migration_connection',
];
- $step = \array_search($message->getTableName(), $tablesToReset, true);
+ $step = \array_search(
+ $message->getTableName(),
+ $tablesToReset,
+ true
+ );
+
if ($step !== false) {
$currentStep = $step;
}
+ $affectedRows = (int) $this->connection->executeStatement(
+ 'DELETE FROM ' . $tablesToReset[$currentStep] . ' LIMIT ' . self::BATCH_SIZE
+ );
+
+ if ($affectedRows >= self::BATCH_SIZE) {
+ $this->bus->dispatch(new TruncateMigrationMessage(
+ $tablesToReset[$currentStep]
+ ));
+
+ return;
+ }
+
$nextStep = $currentStep + 1;
+
if (isset($tablesToReset[$nextStep])) {
- $nextMessage = new CleanupMigrationMessage($tablesToReset[$nextStep]);
- $this->bus->dispatch($nextMessage);
+ $this->bus->dispatch(new TruncateMigrationMessage(
+ $tablesToReset[$nextStep]
+ ));
+
+ return;
}
- $this->connection->executeStatement('DELETE FROM ' . $tablesToReset[$currentStep] . ';');
+
+ $this->connection->executeStatement(
+ 'UPDATE swag_migration_general_setting SET `is_reset` = 0;'
+ );
}
}
diff --git a/src/Migration/MessageQueue/Message/ResetChecksumMessage.php b/src/Migration/MessageQueue/Message/ResetChecksumMessage.php
new file mode 100644
index 000000000..94af5c3be
--- /dev/null
+++ b/src/Migration/MessageQueue/Message/ResetChecksumMessage.php
@@ -0,0 +1,62 @@
+
+ * 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')]
+readonly class ResetChecksumMessage implements AsyncMessageInterface
+{
+ public function __construct(
+ private string $connectionId,
+ private Context $context,
+ private ?string $runId = null,
+ private ?string $entity = null,
+ private ?int $totalMappings = null,
+ private int $processedMappings = 0,
+ private bool $isPartOfAbort = false,
+ ) {
+ }
+
+ public function getConnectionId(): string
+ {
+ return $this->connectionId;
+ }
+
+ public function getContext(): Context
+ {
+ return $this->context;
+ }
+
+ public function getRunId(): ?string
+ {
+ return $this->runId;
+ }
+
+ public function getEntity(): ?string
+ {
+ return $this->entity;
+ }
+
+ public function getTotalMappings(): ?int
+ {
+ return $this->totalMappings;
+ }
+
+ public function getProcessedMappings(): int
+ {
+ return $this->processedMappings;
+ }
+
+ public function isPartOfAbort(): bool
+ {
+ return $this->isPartOfAbort;
+ }
+}
diff --git a/src/Migration/MessageQueue/Message/CleanupMigrationMessage.php b/src/Migration/MessageQueue/Message/TruncateMigrationMessage.php
similarity index 90%
rename from src/Migration/MessageQueue/Message/CleanupMigrationMessage.php
rename to src/Migration/MessageQueue/Message/TruncateMigrationMessage.php
index db9025c46..864521af8 100644
--- a/src/Migration/MessageQueue/Message/CleanupMigrationMessage.php
+++ b/src/Migration/MessageQueue/Message/TruncateMigrationMessage.php
@@ -11,7 +11,7 @@
use Shopware\Core\Framework\MessageQueue\AsyncMessageInterface;
#[Package('fundamentals@after-sales')]
-class CleanupMigrationMessage implements AsyncMessageInterface
+class TruncateMigrationMessage implements AsyncMessageInterface
{
public function __construct(private readonly ?string $tableName = null)
{
diff --git a/src/Migration/Run/RunService.php b/src/Migration/Run/RunService.php
index bff24908e..617035e01 100644
--- a/src/Migration/Run/RunService.php
+++ b/src/Migration/Run/RunService.php
@@ -10,17 +10,13 @@
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\ParameterType;
use Shopware\Core\Framework\Context;
-use Shopware\Core\Framework\DataAbstractionLayer\Dbal\QueryBuilder;
-use Shopware\Core\Framework\DataAbstractionLayer\EntityDefinition;
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\DataAbstractionLayer\Search\Filter\NotFilter;
-use Shopware\Core\Framework\DataAbstractionLayer\Search\Sorting\FieldSorting;
use Shopware\Core\Framework\Log\Package;
use Shopware\Core\Framework\Store\Services\TrackingEventClient;
-use Shopware\Core\Framework\Uuid\Uuid;
use Shopware\Core\System\SalesChannel\SalesChannelCollection;
use Shopware\Core\System\SalesChannel\SalesChannelDefinition;
use Shopware\Storefront\Theme\ThemeCollection;
@@ -35,8 +31,9 @@
use SwagMigrationAssistant\Migration\Logging\Log\ThemeCompilingErrorRunLog;
use SwagMigrationAssistant\Migration\Logging\LoggingServiceInterface;
use SwagMigrationAssistant\Migration\Mapping\MappingServiceInterface;
-use SwagMigrationAssistant\Migration\MessageQueue\Message\CleanupMigrationMessage;
use SwagMigrationAssistant\Migration\MessageQueue\Message\MigrationProcessMessage;
+use SwagMigrationAssistant\Migration\MessageQueue\Message\ResetChecksumMessage;
+use SwagMigrationAssistant\Migration\MessageQueue\Message\TruncateMigrationMessage;
use SwagMigrationAssistant\Migration\MigrationContext;
use SwagMigrationAssistant\Migration\MigrationContextFactoryInterface;
use SwagMigrationAssistant\Migration\MigrationContextInterface;
@@ -69,7 +66,6 @@ public function __construct(
private readonly EntityRepository $generalSettingRepo,
private readonly ThemeService $themeService,
private readonly MappingServiceInterface $mappingService,
- private readonly EntityDefinition $migrationDataDefinition,
private readonly Connection $dbalConnection,
private readonly LoggingServiceInterface $loggingService,
private readonly TrackingEventClient $trackingEventClient,
@@ -86,6 +82,10 @@ public function startMigrationRun(array $dataSelectionIds, Context $context): vo
throw MigrationException::migrationIsAlreadyRunning();
}
+ if ($this->isResettingChecksums()) {
+ throw MigrationException::checksumResetRunning();
+ }
+
$connection = $this->getCurrentConnection($context);
if ($connection === null) {
@@ -97,8 +97,6 @@ public function startMigrationRun(array $dataSelectionIds, Context $context): vo
}
$connectionId = $connection->getId();
- $this->cleanupUnwrittenRunDataOfLastInactiveRun($context);
-
$runUuid = $this->createPlainMigrationRun($connectionId, $context);
if ($runUuid === null) {
@@ -177,30 +175,29 @@ public function abortMigration(Context $context): void
$this->fireTrackingInformation(self::TRACKING_EVENT_MIGRATION_ABORTED, $runId, $context);
}
- public function cleanupMappingChecksums(string $connectionUuid, Context $context, bool $resetAll = true): void
+ public function startCleanupMappingChecksums(string $connectionId, Context $context): void
{
- $sql = <<connectionRepo->search(
+ new Criteria([$connectionId]),
+ $context,
+ )->getEntities()->first();
+
+ if ($connection === null) {
+ throw MigrationException::noConnectionFound();
}
- $this->dbalConnection->executeStatement(
- $sql,
- [$connectionUuid],
- [ParameterType::STRING]
+ $affectedRows = $this->dbalConnection->executeStatement(
+ 'UPDATE swag_migration_general_setting SET `is_resetting_checksums` = 1 WHERE `is_resetting_checksums` = 0;'
);
+
+ if ($affectedRows === 0) {
+ throw MigrationException::checksumResetRunning();
+ }
+
+ $this->bus->dispatch(new ResetChecksumMessage(
+ $connectionId,
+ $context,
+ ));
}
public function approveFinishingMigration(Context $context): void
@@ -220,14 +217,21 @@ public function approveFinishingMigration(Context $context): void
$this->fireTrackingInformation(self::TRACKING_EVENT_MIGRATION_FINISHED, $run->getId(), $context);
}
- public function cleanupMigrationData(Context $context): void
+ public function startTruncateMigrationData(Context $context): void
{
if ($this->isMigrationRunning($context)) {
throw MigrationException::migrationIsAlreadyRunning();
}
- $this->dbalConnection->executeStatement('UPDATE swag_migration_general_setting SET selected_connection_id = NULL, `is_reset` = 1;');
- $this->bus->dispatch(new CleanupMigrationMessage());
+ $affectedRows = $this->dbalConnection->executeStatement(
+ 'UPDATE swag_migration_general_setting SET selected_connection_id = NULL, `is_reset` = 1 WHERE `is_reset` = 0;'
+ );
+
+ if ($affectedRows === 0) {
+ throw MigrationException::truncatingDataRunning();
+ }
+
+ $this->bus->dispatch(new TruncateMigrationMessage());
}
public function assignThemeToSalesChannel(string $runUuid, Context $context): void
@@ -308,17 +312,11 @@ private function isMigrationRunning(Context $context): bool
return $this->getActiveRun($context) !== null;
}
- private function getLastInactiveRun(Context $context): ?SwagMigrationRunEntity
+ private function isResettingChecksums(): bool
{
- $criteria = new Criteria();
- $criteria->addFilter(new MultiFilter(MultiFilter::CONNECTION_OR, [
- new EqualsFilter('step', MigrationStep::ABORTED->value),
- new EqualsFilter('step', MigrationStep::FINISHED->value),
- ]));
- $criteria->addSorting(new FieldSorting('createdAt', 'DESC'));
- $criteria->setLimit(1);
-
- return $this->migrationRunRepo->search($criteria, $context)->getEntities()->first();
+ return (bool) $this->dbalConnection->fetchOne(
+ 'SELECT is_resetting_checksums FROM swag_migration_general_setting LIMIT 1'
+ );
}
private function fireTrackingInformation(string $eventName, string $runUuid, Context $context): void
@@ -550,21 +548,6 @@ private function getDefaultTheme(Context $context): ?string
return \reset($ids);
}
- private function cleanupUnwrittenRunDataOfLastInactiveRun(Context $context): void
- {
- $lastInactiveRun = $this->getLastInactiveRun($context);
-
- if ($lastInactiveRun === null) {
- return;
- }
-
- $queryBuilder = new QueryBuilder($this->dbalConnection);
- $queryBuilder->delete($this->migrationDataDefinition->getEntityName())
- ->andWhere('run_id = :runId')
- ->setParameter('runId', Uuid::fromHexToBytes($lastInactiveRun->getId()))
- ->executeStatement();
- }
-
private function updateUnprocessedMediaFiles(string $connectionId, string $runUuid): void
{
$sql = << $dataSelectionIds
@@ -49,6 +51,4 @@ public function updateConnectionCredentials(Context $context, string $connection
public function approveFinishingMigration(Context $context): void;
public function assignThemeToSalesChannel(string $runUuid, Context $context): void;
-
- public function cleanupMigrationData(Context $context): void;
}
diff --git a/src/Migration/Setting/GeneralSettingDefinition.php b/src/Migration/Setting/GeneralSettingDefinition.php
index e96767518..d075d466d 100644
--- a/src/Migration/Setting/GeneralSettingDefinition.php
+++ b/src/Migration/Setting/GeneralSettingDefinition.php
@@ -46,6 +46,7 @@ protected function defineFields(): FieldCollection
(new IdField('id', 'id'))->addFlags(new PrimaryKey(), new Required()),
new FkField('selected_connection_id', 'selectedConnectionId', SwagMigrationConnectionDefinition::class),
new BoolField('is_reset', 'isReset'),
+ new BoolField('is_resetting_checksums', 'isResettingChecksums'),
new CreatedAtField(),
new UpdatedAtField(),
new ManyToOneAssociationField('selectedConnection', 'selected_connection_id', SwagMigrationConnectionDefinition::class, 'id', true),
diff --git a/src/Migration/Setting/GeneralSettingEntity.php b/src/Migration/Setting/GeneralSettingEntity.php
index 8497e7baa..43dc70884 100644
--- a/src/Migration/Setting/GeneralSettingEntity.php
+++ b/src/Migration/Setting/GeneralSettingEntity.php
@@ -23,6 +23,8 @@ class GeneralSettingEntity extends Entity
protected bool $isReset;
+ protected bool $isResettingChecksums = false;
+
public function getSelectedConnectionId(): ?string
{
return $this->selectedConnectionId;
@@ -52,4 +54,14 @@ public function setIsReset(bool $isReset): void
{
$this->isReset = $isReset;
}
+
+ public function isResettingChecksums(): bool
+ {
+ return $this->isResettingChecksums;
+ }
+
+ public function setIsResettingChecksums(bool $isResettingChecksums): void
+ {
+ $this->isResettingChecksums = $isResettingChecksums;
+ }
}
diff --git a/src/Resources/app/administration/src/core/service/api/swag-migration.api.service.ts b/src/Resources/app/administration/src/core/service/api/swag-migration.api.service.ts
index 79b955a02..fdc4f0d9a 100644
--- a/src/Resources/app/administration/src/core/service/api/swag-migration.api.service.ts
+++ b/src/Resources/app/administration/src/core/service/api/swag-migration.api.service.ts
@@ -398,4 +398,34 @@ export default class MigrationApiService extends ApiService {
headers,
});
}
+
+ async isResettingChecksums(): Promise {
+ // @ts-ignore
+ const headers = this.getBasicHeaders();
+
+ // @ts-ignore
+ return this.httpClient
+ .get(`_action/${this.getApiBasePath()}/is-resetting-checksums`, {
+ ...this.basicConfig,
+ headers,
+ })
+ .then((response: AxiosResponse) => {
+ return ApiService.handleResponse(response);
+ });
+ }
+
+ async isTruncatingMigrationData(): Promise {
+ // @ts-ignore
+ const headers = this.getBasicHeaders();
+
+ // @ts-ignore
+ return this.httpClient
+ .get(`_action/${this.getApiBasePath()}/is-truncating-migration-data`, {
+ ...this.basicConfig,
+ headers,
+ })
+ .then((response: AxiosResponse) => {
+ return ApiService.handleResponse(response);
+ });
+ }
}
diff --git a/src/Resources/app/administration/src/module/swag-migration/component/card/swag-migration-shop-information/index.ts b/src/Resources/app/administration/src/module/swag-migration/component/card/swag-migration-shop-information/index.ts
index fe6c3975b..865728fce 100644
--- a/src/Resources/app/administration/src/module/swag-migration/component/card/swag-migration-shop-information/index.ts
+++ b/src/Resources/app/administration/src/module/swag-migration/component/card/swag-migration-shop-information/index.ts
@@ -8,7 +8,7 @@ import type {
TRepository,
} from '../../../../../type/types';
import { MIGRATION_API_SERVICE } from '../../../../../core/service/api/swag-migration.api.service';
-import { MIGRATION_STORE_ID } from '../../../store/migration.store';
+import { MIGRATION_STORE_ID, type MigrationStore } from '../../../store/migration.store';
const { Mixin, Store } = Shopware;
const { mapState } = Shopware.Component.getComponentHelper();
@@ -23,10 +23,15 @@ export const BADGE_TYPE = {
DANGER: 'danger',
} as const;
+const MIGRATION_POLLING_INTERVAL = 2500 as const;
+
+type PollingType = 'checksum' | 'truncate';
+
/**
* @private
*/
export interface SwagMigrationShopInformationData {
+ migrationStore: MigrationStore;
confirmModalIsLoading: boolean;
showRemoveCredentialsConfirmModal: boolean;
showResetChecksumsConfirmModal: boolean;
@@ -34,6 +39,9 @@ export interface SwagMigrationShopInformationData {
lastMigrationDate: string;
connection: MigrationConnection | null;
context: unknown;
+ checksumPollingIntervalId: number | null;
+ truncatePollingIntervalId: number | null;
+ isLoading: boolean;
}
/**
@@ -52,14 +60,6 @@ export default Shopware.Component.wrapComponentConfig({
Mixin.getByName('notification'),
],
- filters: {
- localizedNumberFormat(value: number) {
- const locale = `${this.adminLocaleLanguage}-${this.adminLocaleRegion}`;
-
- return Intl.NumberFormat(locale).format(value);
- },
- },
-
props: {
connected: {
type: Boolean,
@@ -69,6 +69,9 @@ export default Shopware.Component.wrapComponentConfig({
data(): SwagMigrationShopInformationData {
return {
+ migrationStore: Store.get(MIGRATION_STORE_ID),
+ checksumPollingIntervalId: null,
+ truncatePollingIntervalId: null,
confirmModalIsLoading: false,
showRemoveCredentialsConfirmModal: false,
showResetChecksumsConfirmModal: false,
@@ -76,6 +79,7 @@ export default Shopware.Component.wrapComponentConfig({
lastMigrationDate: '-',
connection: null,
context: Shopware.Context.api,
+ isLoading: false,
};
},
@@ -83,6 +87,8 @@ export default Shopware.Component.wrapComponentConfig({
...mapState(
() => Store.get(MIGRATION_STORE_ID),
[
+ 'isResettingChecksum',
+ 'isTruncatingMigration',
'connectionId',
'currentConnection',
'environmentInformation',
@@ -92,10 +98,6 @@ export default Shopware.Component.wrapComponentConfig({
],
),
- displayEnvironmentInformation() {
- return this.environmentInformation === null ? {} : this.environmentInformation;
- },
-
migrationRunRepository(): TRepository<'swag_migration_run'> {
return this.repositoryFactory.create('swag_migration_run');
},
@@ -104,38 +106,46 @@ export default Shopware.Component.wrapComponentConfig({
return this.repositoryFactory.create('swag_migration_connection');
},
- connectionName() {
- return this.connection !== null
- ? this.connection?.name
- : this.$tc('swag-migration.index.shopInfoCard.noConnection');
+ displayEnvironmentInformation() {
+ return this.environmentInformation === null ? {} : this.environmentInformation;
},
- shopUrl() {
- return this.displayEnvironmentInformation.sourceSystemDomain === undefined
- ? ''
- : this.displayEnvironmentInformation.sourceSystemDomain.replace(/^\s*https?:\/\//, '');
+ isUpdating() {
+ return this.isResettingChecksum || this.isTruncatingMigration || this.isLoading;
},
- shopUrlPrefix() {
- if (this.displayEnvironmentInformation.sourceSystemDomain === undefined) {
- return '';
- }
+ showUpdateBanner() {
+ return this.isResettingChecksum || this.isTruncatingMigration;
+ },
- const match = this.displayEnvironmentInformation.sourceSystemDomain.match(/^\s*https?:\/\//);
+ updateBannerTitle() {
+ if (this.isResettingChecksum) {
+ return this.$tc('swag-migration.index.shopInfoCard.updateBanner.isResettingChecksums.title');
+ }
- if (match === null) {
- return '';
+ if (this.isTruncatingMigration) {
+ return this.$tc('swag-migration.index.shopInfoCard.updateBanner.isTruncatingMigration.title');
}
- return match[0];
+ return '';
},
- sslActive() {
- return this.shopUrlPrefix === 'https://';
+ updateBannerMessage() {
+ if (this.isResettingChecksum) {
+ return this.$tc('swag-migration.index.shopInfoCard.updateBanner.isResettingChecksums.message');
+ }
+
+ if (this.isTruncatingMigration) {
+ return this.$tc('swag-migration.index.shopInfoCard.updateBanner.isTruncatingMigration.message');
+ }
+
+ return '';
},
- shopUrlPrefixClass() {
- return this.sslActive ? 'swag-migration-shop-information__shop-domain-prefix--is-ssl' : '';
+ connectionName() {
+ return this.connection !== null
+ ? this.connection.name
+ : this.$tc('swag-migration.index.shopInfoCard.noConnection');
},
connectionBadgeLabel() {
@@ -158,6 +168,34 @@ export default Shopware.Component.wrapComponentConfig({
return BADGE_TYPE.DANGER;
},
+ shopUrl() {
+ return this.displayEnvironmentInformation.sourceSystemDomain === undefined
+ ? ''
+ : this.displayEnvironmentInformation.sourceSystemDomain.replace(/^\s*https?:\/\//, '');
+ },
+
+ shopUrlPrefix() {
+ if (this.displayEnvironmentInformation.sourceSystemDomain === undefined) {
+ return '';
+ }
+
+ const match = this.displayEnvironmentInformation.sourceSystemDomain.match(/^\s*https?:\/\//);
+
+ if (match === null) {
+ return '';
+ }
+
+ return match[0];
+ },
+
+ sslActive() {
+ return this.shopUrlPrefix === 'https://';
+ },
+
+ shopUrlPrefixClass() {
+ return this.sslActive ? 'swag-migration-shop-information__shop-domain-prefix--is-ssl' : '';
+ },
+
shopFirstLetter() {
return this.displayEnvironmentInformation.sourceSystemName?.charAt(0) ?? 'S';
},
@@ -194,7 +232,7 @@ export default Shopware.Component.wrapComponentConfig({
},
showMoreInformation() {
- return this.connection !== null && this.connection !== undefined;
+ return this.connection !== null;
},
},
@@ -219,22 +257,29 @@ export default Shopware.Component.wrapComponentConfig({
},
methods: {
- createdComponent() {
- this.updateLastMigrationDate();
- },
-
- openResetMigrationModal() {
- this.showResetMigrationConfirmModal = true;
- this.$router.push({
- name: 'swag.migration.index.resetMigration',
- });
- },
-
- async onCloseResetModal() {
- this.showResetMigrationConfirmModal = false;
- await this.$router.push({
- name: 'swag.migration.index.main',
- });
+ async createdComponent() {
+ this.isLoading = true;
+
+ try {
+ const [
+ isResettingChecksums,
+ isTruncatingMigration,
+ ] = await Promise.all([
+ this.migrationApiService.isResettingChecksums(),
+ this.migrationApiService.isTruncatingMigrationData(),
+ this.updateLastMigrationDate(),
+ ]);
+
+ if (isResettingChecksums) {
+ this.registerPolling('checksum');
+ }
+
+ if (isTruncatingMigration) {
+ this.registerPolling('truncate');
+ }
+ } finally {
+ this.isLoading = false;
+ }
},
async updateLastMigrationDate() {
@@ -292,6 +337,76 @@ export default Shopware.Component.wrapComponentConfig({
});
},
+ registerPolling(type: PollingType) {
+ this.unregisterPolling(type);
+
+ if (type === 'checksum') {
+ this.migrationStore.setIsResettingChecksum(true);
+ this.checksumPollingIntervalId = setInterval(() => this.poll(type), MIGRATION_POLLING_INTERVAL);
+ } else {
+ this.migrationStore.setIsTruncatingMigration(true);
+ this.truncatePollingIntervalId = setInterval(() => this.poll(type), MIGRATION_POLLING_INTERVAL);
+ }
+ },
+
+ unregisterPolling(type: PollingType) {
+ if (type === 'checksum') {
+ if (this.checksumPollingIntervalId) {
+ clearInterval(this.checksumPollingIntervalId);
+ }
+
+ this.checksumPollingIntervalId = null;
+ this.migrationStore.setIsResettingChecksum(false);
+ } else {
+ if (this.truncatePollingIntervalId) {
+ clearInterval(this.truncatePollingIntervalId);
+ }
+
+ this.migrationStore.setIsTruncatingMigration(false);
+ this.truncatePollingIntervalId = null;
+ }
+ },
+
+ async poll(type: PollingType) {
+ const isActive = type === 'checksum' ? this.isResettingChecksum : this.isTruncatingMigration;
+
+ if (!isActive) {
+ return;
+ }
+
+ try {
+ const isStillRunning =
+ type === 'checksum'
+ ? await this.migrationApiService.isResettingChecksums()
+ : await this.migrationApiService.isTruncatingMigrationData();
+
+ if (!isStillRunning) {
+ this.unregisterPolling(type);
+
+ if (type === 'truncate') {
+ this.migrationStore.init(true);
+ }
+ }
+ } catch {
+ this.unregisterPolling(type);
+ this.createNotificationError({
+ title: this.$tc('global.default.error'),
+ message: this.$tc('swag-migration.api-error.getState'),
+ });
+ }
+ },
+
+ openResetMigrationModal() {
+ this.showResetMigrationConfirmModal = true;
+ },
+
+ async onCloseResetModal() {
+ this.showResetMigrationConfirmModal = false;
+ await this.$router.push({
+ name: 'swag.migration.index.main',
+ });
+ },
+
onClickEditConnectionCredentials() {
this.$router.push({
name: 'swag.migration.wizard.credentials',
@@ -325,6 +440,10 @@ export default Shopware.Component.wrapComponentConfig({
});
},
+ onClickRefreshConnection() {
+ return this.migrationStore.init(true);
+ },
+
async onClickRemoveConnectionCredentials() {
this.confirmModalIsLoading = true;
@@ -336,41 +455,21 @@ export default Shopware.Component.wrapComponentConfig({
async onClickResetChecksums() {
this.confirmModalIsLoading = true;
- return this.migrationApiService.resetChecksums(this.connectionId).then(() => {
- this.showResetChecksumsConfirmModal = false;
- this.confirmModalIsLoading = false;
- });
+ await this.migrationApiService.resetChecksums(this.connectionId);
+ this.registerPolling('checksum');
+
+ this.showResetChecksumsConfirmModal = false;
+ this.confirmModalIsLoading = false;
},
async onClickResetMigration() {
this.confirmModalIsLoading = true;
- return this.migrationApiService
- .cleanupMigrationData()
- .catch(() => {
- this.showResetMigrationConfirmModal = false;
- this.confirmModalIsLoading = false;
-
- this.createNotificationError({
- title: this.$t(
- 'swag-migration.index.shopInfoCard.resetMigrationConfirmDialog.errorNotification.title',
- ),
- message: this.$t(
- 'swag-migration.index.shopInfoCard.resetMigrationConfirmDialog.errorNotification.message',
- ),
- variant: 'error',
- growl: true,
- });
- })
- .finally(async () => {
- this.confirmModalIsLoading = false;
- await this.onCloseResetModal();
- window.location.reload();
- });
- },
+ await this.migrationApiService.cleanupMigrationData();
+ this.registerPolling('truncate');
- onClickRefreshConnection() {
- return Store.get(MIGRATION_STORE_ID).init(true);
+ this.showResetMigrationConfirmModal = false;
+ this.confirmModalIsLoading = false;
},
},
});
diff --git a/src/Resources/app/administration/src/module/swag-migration/component/card/swag-migration-shop-information/swag-migration-shop-information.html.twig b/src/Resources/app/administration/src/module/swag-migration/component/card/swag-migration-shop-information/swag-migration-shop-information.html.twig
index 063371661..431cd8d57 100644
--- a/src/Resources/app/administration/src/module/swag-migration/component/card/swag-migration-shop-information/swag-migration-shop-information.html.twig
+++ b/src/Resources/app/administration/src/module/swag-migration/component/card/swag-migration-shop-information/swag-migration-shop-information.html.twig
@@ -1,8 +1,18 @@
{% block swag_migration_shop_information %}
+
+ {{ updateBannerMessage }}
+
+