diff --git a/src/DependencyInjection/migration.xml b/src/DependencyInjection/migration.xml
index b008b1b85..c375ecf6a 100644
--- a/src/DependencyInjection/migration.xml
+++ b/src/DependencyInjection/migration.xml
@@ -425,7 +425,9 @@
-
+
+
+
diff --git a/src/Exception/MigrationException.php b/src/Exception/MigrationException.php
index ff1c28442..4b5cffb2f 100644
--- a/src/Exception/MigrationException.php
+++ b/src/Exception/MigrationException.php
@@ -87,15 +87,11 @@ class MigrationException extends HttpException
final public const API_CONNECTION_ERROR = 'SWAG_MIGRATION__API_CONNECTION_ERROR';
- final public const UNEXPECTED_NULL_VALUE = 'SWAG_MIGRATION__UNEXPECTED_NULL_VALUE';
-
final public const COULD_NOT_CONVERT_FIX = 'SWAG_MIGRATION__COULD_NOT_CONVERT_FIX';
final public const MIGRATION_NOT_IN_STEP = 'SWAG_MIGRATION__MIGRATION_NOT_IN_STEP';
- final public const INVALID_ID = 'SWAG_MIGRATION__INVALID_ID';
-
- public const DUPLICATE_SOURCE_CONNECTION = 'SWAG_MIGRATION__DUPLICATE_SOURCE_CONNECTION';
+ final public const DUPLICATE_SOURCE_CONNECTION = 'SWAG_MIGRATION__DUPLICATE_SOURCE_CONNECTION';
public static function associationEntityRequiredMissing(string $entity, string $missingEntity): self
{
@@ -452,16 +448,6 @@ public static function invalidWriteContext(Context $invalidContext): self
);
}
- public static function unexpectedNullValue(string $fieldName): self
- {
- return new self(
- Response::HTTP_INTERNAL_SERVER_ERROR,
- self::UNEXPECTED_NULL_VALUE,
- 'Unexpected null value for field "{{ fieldName }}".',
- ['fieldName' => $fieldName]
- );
- }
-
public static function couldNotConvertFix(string $missingKey): self
{
return new self(
@@ -482,16 +468,6 @@ public static function migrationNotInStep(string $runUuid, string $step): self
);
}
- public static function invalidId(string $entityId, string $entityName): self
- {
- return new self(
- Response::HTTP_INTERNAL_SERVER_ERROR,
- self::INVALID_ID,
- 'The id "{{ entityId }}" for entity "{{ entityName }}" is not a valid Uuid',
- ['entityId' => $entityId, 'entityName' => $entityName]
- );
- }
-
public static function duplicateSourceConnection(): self
{
return new self(
diff --git a/src/Migration/Mapping/MappingService.php b/src/Migration/Mapping/MappingService.php
index d84ab7b04..54c8f05ab 100644
--- a/src/Migration/Mapping/MappingService.php
+++ b/src/Migration/Mapping/MappingService.php
@@ -17,8 +17,6 @@
use Shopware\Core\Framework\DataAbstractionLayer\Search\EntitySearchResult;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsAnyFilter;
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\Write\EntityWriterInterface;
use Shopware\Core\Framework\Log\Package;
use Shopware\Core\Framework\Uuid\Uuid;
@@ -214,22 +212,6 @@ public function getMappings(string $connectionId, string $entityName, array $ids
return $this->migrationMappingRepo->search($criteria, $context);
}
- public function hasValidMappingByEntityId(string $connectionId, string $entityName, string $entityId, Context $context): bool
- {
- $criteria = new Criteria();
- $criteria->addFilter(
- new EqualsFilter('connectionId', $connectionId),
- new EqualsFilter('entity', $entityName),
- new EqualsFilter('entityId', $entityId),
- new NotFilter(MultiFilter::CONNECTION_AND, [
- new EqualsFilter('oldIdentifier', null),
- ]),
- );
- $criteria->setLimit(1);
-
- return $this->migrationMappingRepo->searchIds($criteria, $context)->getTotal() > 0;
- }
-
public function preloadMappings(array $mappingIds, Context $context): void
{
if (empty($mappingIds)) {
diff --git a/src/Migration/Mapping/MappingServiceInterface.php b/src/Migration/Mapping/MappingServiceInterface.php
index 20dd09660..a2e9467d4 100644
--- a/src/Migration/Mapping/MappingServiceInterface.php
+++ b/src/Migration/Mapping/MappingServiceInterface.php
@@ -84,7 +84,5 @@ public function writeMapping(): void;
*/
public function getMappings(string $connectionId, string $entityName, array $ids, Context $context): EntitySearchResult;
- public function hasValidMappingByEntityId(string $connectionId, string $entityName, string $entityId, Context $context): bool;
-
public function preloadMappings(array $mappingIds, Context $context): void;
}
diff --git a/src/Migration/Service/MediaFileProcessorService.php b/src/Migration/Service/MediaFileProcessorService.php
index d4c8c2c56..ce3983fcc 100644
--- a/src/Migration/Service/MediaFileProcessorService.php
+++ b/src/Migration/Service/MediaFileProcessorService.php
@@ -108,6 +108,7 @@ private function getMediaFiles(MigrationContextInterface $migrationContext): arr
->from('swag_migration_media_file')
->where('run_id = :runId')
->andWhere('written = 1')
+ ->andWhere('processed = 0')
->orderBy('entity, file_size')
->setFirstResult($migrationContext->getOffset())
->setMaxResults($migrationContext->getLimit())
diff --git a/src/Migration/Service/MigrationDataWriter.php b/src/Migration/Service/MigrationDataWriter.php
index c62982dd0..40c9e5cf4 100644
--- a/src/Migration/Service/MigrationDataWriter.php
+++ b/src/Migration/Service/MigrationDataWriter.php
@@ -73,6 +73,7 @@ public function writeData(MigrationContextInterface $migrationContext, Context $
$criteria->addFilter(new EqualsFilter('entity', $dataSet::getEntity()));
$criteria->addFilter(new EqualsFilter('runId', $migrationContext->getRunUuid()));
$criteria->addFilter(new EqualsFilter('convertFailure', false));
+ $criteria->addFilter(new EqualsFilter('written', false));
$criteria->setOffset($migrationContext->getOffset());
$criteria->setLimit($migrationContext->getLimit());
$criteria->addSorting(new FieldSorting('autoIncrement', FieldSorting::ASCENDING));
@@ -133,7 +134,7 @@ public function writeData(MigrationContextInterface $migrationContext, Context $
}
unset($data);
- return $migrationData->getTotal();
+ return $migrationData->count();
} catch (WriteException $exception) {
$this->handleWriteException(
$exception,
@@ -165,7 +166,7 @@ public function writeData(MigrationContextInterface $migrationContext, Context $
$context
);
- return $migrationData->getTotal();
+ return $migrationData->count();
}
/**
diff --git a/src/Migration/Validation/Exception/MigrationValidationException.php b/src/Migration/Validation/Exception/MigrationValidationException.php
new file mode 100644
index 000000000..6e557120a
--- /dev/null
+++ b/src/Migration/Validation/Exception/MigrationValidationException.php
@@ -0,0 +1,91 @@
+
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace SwagMigrationAssistant\Migration\Validation\Exception;
+
+use Shopware\Core\Framework\Log\Package;
+use Shopware\Core\Framework\Migration\MigrationException;
+use Symfony\Component\HttpFoundation\Response;
+
+/**
+ * @codeCoverageIgnore
+ */
+#[Package('fundamentals@after-sales')]
+class MigrationValidationException extends MigrationException
+{
+ final public const VALIDATION_UNEXPECTED_NULL_VALUE = 'SWAG_MIGRATION_VALIDATION__UNEXPECTED_NULL_VALUE';
+
+ final public const VALIDATION_INVALID_ID = 'SWAG_MIGRATION_VALIDATION__INVALID_ID';
+
+ final public const VALIDATION_INVALID_REQUIRED_FIELD_VALUE = 'SWAG_MIGRATION_VALIDATION__INVALID_REQUIRED_FIELD_VALUE';
+
+ final public const VALIDATION_INVALID_OPTIONAL_FIELD_VALUE = 'SWAG_MIGRATION_VALIDATION__INVALID_OPTIONAL_FIELD_VALUE';
+
+ final public const VALIDATION_INVALID_TRANSLATION = 'SWAG_MIGRATION_VALIDATION__INVALID_TRANSLATION';
+
+ final public const VALIDATION_INVALID_ASSOCIATION = 'SWAG_MIGRATION_VALIDATION__INVALID_ASSOCIATION';
+
+ public static function unexpectedNullValue(string $fieldName): self
+ {
+ return new self(
+ Response::HTTP_INTERNAL_SERVER_ERROR,
+ self::VALIDATION_UNEXPECTED_NULL_VALUE,
+ 'Unexpected null value for field "{{ fieldName }}".',
+ ['fieldName' => $fieldName]
+ );
+ }
+
+ public static function invalidId(string $entityId, string $entityName): self
+ {
+ return new self(
+ Response::HTTP_INTERNAL_SERVER_ERROR,
+ self::VALIDATION_INVALID_ID,
+ 'The id "{{ entityId }}" for entity "{{ entityName }}" is not a valid Uuid',
+ ['entityId' => $entityId, 'entityName' => $entityName]
+ );
+ }
+
+ public static function invalidRequiredFieldValue(string $entityName, string $fieldName, string $message): self
+ {
+ return new self(
+ Response::HTTP_BAD_REQUEST,
+ self::VALIDATION_INVALID_REQUIRED_FIELD_VALUE,
+ 'Invalid value for required field "{{ fieldName }}" in entity "{{ entityName }}": {{ message }}',
+ ['fieldName' => $fieldName, 'entityName' => $entityName, 'message' => $message]
+ );
+ }
+
+ public static function invalidOptionalFieldValue(string $entityName, string $fieldName, string $message): self
+ {
+ return new self(
+ Response::HTTP_BAD_REQUEST,
+ self::VALIDATION_INVALID_OPTIONAL_FIELD_VALUE,
+ 'Invalid value for optional field "{{ fieldName }}" in entity "{{ entityName }}": {{ message }}',
+ ['fieldName' => $fieldName, 'entityName' => $entityName, 'message' => $message]
+ );
+ }
+
+ public static function invalidTranslation(string $entityName, string $fieldName, string $message): self
+ {
+ return new self(
+ Response::HTTP_BAD_REQUEST,
+ self::VALIDATION_INVALID_TRANSLATION,
+ 'Invalid translation for field "{{ fieldName }}" in entity "{{ entityName }}": {{ message }}',
+ ['fieldName' => $fieldName, 'entityName' => $entityName, 'message' => $message]
+ );
+ }
+
+ public static function invalidAssociation(string $entityName, string $fieldName, string $message): self
+ {
+ return new self(
+ Response::HTTP_BAD_REQUEST,
+ self::VALIDATION_INVALID_ASSOCIATION,
+ 'Invalid association "{{ fieldName }}" in entity "{{ entityName }}": {{ message }}',
+ ['fieldName' => $fieldName, 'entityName' => $entityName, 'message' => $message]
+ );
+ }
+}
diff --git a/src/Migration/Validation/Log/MigrationValidationInvalidFieldValueLog.php b/src/Migration/Validation/Log/MigrationValidationInvalidAssociationLog.php
similarity index 80%
rename from src/Migration/Validation/Log/MigrationValidationInvalidFieldValueLog.php
rename to src/Migration/Validation/Log/MigrationValidationInvalidAssociationLog.php
index 5b5340165..ce1ffd980 100644
--- a/src/Migration/Validation/Log/MigrationValidationInvalidFieldValueLog.php
+++ b/src/Migration/Validation/Log/MigrationValidationInvalidAssociationLog.php
@@ -11,7 +11,7 @@
use SwagMigrationAssistant\Migration\Logging\Log\Builder\AbstractMigrationLogEntry;
#[Package('fundamentals@after-sales')]
-readonly class MigrationValidationInvalidFieldValueLog extends AbstractMigrationLogEntry
+readonly class MigrationValidationInvalidAssociationLog extends AbstractMigrationLogEntry
{
public function isUserFixable(): bool
{
@@ -25,6 +25,6 @@ public function getLevel(): string
public function getCode(): string
{
- return 'SWAG_MIGRATION_VALIDATION_INVALID_FIELD_VALUE';
+ return 'SWAG_MIGRATION_VALIDATION_INVALID_ASSOCIATION';
}
}
diff --git a/src/Migration/Validation/Log/MigrationValidationInvalidForeignKeyLog.php b/src/Migration/Validation/Log/MigrationValidationInvalidOptionalFieldValueLog.php
similarity index 79%
rename from src/Migration/Validation/Log/MigrationValidationInvalidForeignKeyLog.php
rename to src/Migration/Validation/Log/MigrationValidationInvalidOptionalFieldValueLog.php
index bdd2a0ae3..71c295243 100644
--- a/src/Migration/Validation/Log/MigrationValidationInvalidForeignKeyLog.php
+++ b/src/Migration/Validation/Log/MigrationValidationInvalidOptionalFieldValueLog.php
@@ -11,7 +11,7 @@
use SwagMigrationAssistant\Migration\Logging\Log\Builder\AbstractMigrationLogEntry;
#[Package('fundamentals@after-sales')]
-readonly class MigrationValidationInvalidForeignKeyLog extends AbstractMigrationLogEntry
+readonly class MigrationValidationInvalidOptionalFieldValueLog extends AbstractMigrationLogEntry
{
public function isUserFixable(): bool
{
@@ -25,6 +25,6 @@ public function getLevel(): string
public function getCode(): string
{
- return 'SWAG_MIGRATION_VALIDATION_INVALID_FOREIGN_KEY';
+ return 'SWAG_MIGRATION_VALIDATION_INVALID_OPTIONAL_FIELD_VALUE';
}
}
diff --git a/src/Migration/Validation/Log/MigrationValidationUnexpectedFieldLog.php b/src/Migration/Validation/Log/MigrationValidationInvalidRequiredFieldValueLog.php
similarity index 74%
rename from src/Migration/Validation/Log/MigrationValidationUnexpectedFieldLog.php
rename to src/Migration/Validation/Log/MigrationValidationInvalidRequiredFieldValueLog.php
index d363bf846..9a4cc1b38 100644
--- a/src/Migration/Validation/Log/MigrationValidationUnexpectedFieldLog.php
+++ b/src/Migration/Validation/Log/MigrationValidationInvalidRequiredFieldValueLog.php
@@ -11,7 +11,7 @@
use SwagMigrationAssistant\Migration\Logging\Log\Builder\AbstractMigrationLogEntry;
#[Package('fundamentals@after-sales')]
-readonly class MigrationValidationUnexpectedFieldLog extends AbstractMigrationLogEntry
+readonly class MigrationValidationInvalidRequiredFieldValueLog extends AbstractMigrationLogEntry
{
public function isUserFixable(): bool
{
@@ -20,11 +20,11 @@ public function isUserFixable(): bool
public function getLevel(): string
{
- return self::LOG_LEVEL_WARNING;
+ return self::LOG_LEVEL_ERROR;
}
public function getCode(): string
{
- return 'SWAG_MIGRATION_VALIDATION_UNEXPECTED_FIELD';
+ return 'SWAG_MIGRATION_VALIDATION_INVALID_REQUIRED_FIELD_VALUE';
}
}
diff --git a/src/Migration/Validation/Log/MigrationValidationInvalidRequiredTranslation.php b/src/Migration/Validation/Log/MigrationValidationInvalidRequiredTranslation.php
new file mode 100644
index 000000000..9633c1f90
--- /dev/null
+++ b/src/Migration/Validation/Log/MigrationValidationInvalidRequiredTranslation.php
@@ -0,0 +1,30 @@
+
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace SwagMigrationAssistant\Migration\Validation\Log;
+
+use Shopware\Core\Framework\Log\Package;
+use SwagMigrationAssistant\Migration\Logging\Log\Builder\AbstractMigrationLogEntry;
+
+#[Package('fundamentals@after-sales')]
+readonly class MigrationValidationInvalidRequiredTranslation extends AbstractMigrationLogEntry
+{
+ public function isUserFixable(): bool
+ {
+ return false;
+ }
+
+ public function getLevel(): string
+ {
+ return self::LOG_LEVEL_ERROR;
+ }
+
+ public function getCode(): string
+ {
+ return 'SWAG_MIGRATION_VALIDATION_INVALID_REQUIRED_TRANSLATION';
+ }
+}
diff --git a/src/Migration/Validation/Log/MigrationValidationMissingRequiredFieldLog.php b/src/Migration/Validation/Log/MigrationValidationMissingRequiredFieldLog.php
index fb39b8810..ff5909673 100644
--- a/src/Migration/Validation/Log/MigrationValidationMissingRequiredFieldLog.php
+++ b/src/Migration/Validation/Log/MigrationValidationMissingRequiredFieldLog.php
@@ -15,7 +15,7 @@
{
public function isUserFixable(): bool
{
- return false;
+ return true;
}
public function getLevel(): string
diff --git a/src/Migration/Validation/MigrationValidationService.php b/src/Migration/Validation/MigrationValidationService.php
index e1d939c2a..79b3ba14c 100644
--- a/src/Migration/Validation/MigrationValidationService.php
+++ b/src/Migration/Validation/MigrationValidationService.php
@@ -7,11 +7,24 @@
namespace SwagMigrationAssistant\Migration\Validation;
+use Doctrine\DBAL\Connection;
+use Doctrine\DBAL\Exception;
use Shopware\Core\Framework\Context;
+use Shopware\Core\Framework\DataAbstractionLayer\CompiledFieldCollection;
use Shopware\Core\Framework\DataAbstractionLayer\DefinitionInstanceRegistry;
+use Shopware\Core\Framework\DataAbstractionLayer\Field\AssociationField;
+use Shopware\Core\Framework\DataAbstractionLayer\Field\CreatedAtField;
use Shopware\Core\Framework\DataAbstractionLayer\Field\Field;
-use Shopware\Core\Framework\DataAbstractionLayer\Field\FkField;
use Shopware\Core\Framework\DataAbstractionLayer\Field\Flag\Required;
+use Shopware\Core\Framework\DataAbstractionLayer\Field\ManyToManyAssociationField;
+use Shopware\Core\Framework\DataAbstractionLayer\Field\ManyToOneAssociationField;
+use Shopware\Core\Framework\DataAbstractionLayer\Field\OneToManyAssociationField;
+use Shopware\Core\Framework\DataAbstractionLayer\Field\OneToOneAssociationField;
+use Shopware\Core\Framework\DataAbstractionLayer\Field\ReferenceVersionField;
+use Shopware\Core\Framework\DataAbstractionLayer\Field\StorageAware;
+use Shopware\Core\Framework\DataAbstractionLayer\Field\TranslationsAssociationField;
+use Shopware\Core\Framework\DataAbstractionLayer\Field\UpdatedAtField;
+use Shopware\Core\Framework\DataAbstractionLayer\Field\VersionField;
use Shopware\Core\Framework\DataAbstractionLayer\Write\Command\WriteCommandQueue;
use Shopware\Core\Framework\DataAbstractionLayer\Write\DataStack\KeyValuePair;
use Shopware\Core\Framework\DataAbstractionLayer\Write\EntityExistence;
@@ -19,37 +32,70 @@
use Shopware\Core\Framework\DataAbstractionLayer\Write\WriteParameterBag;
use Shopware\Core\Framework\Log\Package;
use Shopware\Core\Framework\Uuid\Uuid;
-use SwagMigrationAssistant\Exception\MigrationException;
use SwagMigrationAssistant\Migration\Logging\Log\Builder\MigrationLogBuilder;
use SwagMigrationAssistant\Migration\Logging\LoggingServiceInterface;
-use SwagMigrationAssistant\Migration\Mapping\MappingServiceInterface;
use SwagMigrationAssistant\Migration\MigrationContextInterface;
use SwagMigrationAssistant\Migration\Validation\Event\MigrationPostValidationEvent;
use SwagMigrationAssistant\Migration\Validation\Event\MigrationPreValidationEvent;
+use SwagMigrationAssistant\Migration\Validation\Exception\MigrationValidationException;
use SwagMigrationAssistant\Migration\Validation\Log\MigrationValidationExceptionLog;
-use SwagMigrationAssistant\Migration\Validation\Log\MigrationValidationInvalidFieldValueLog;
-use SwagMigrationAssistant\Migration\Validation\Log\MigrationValidationInvalidForeignKeyLog;
+use SwagMigrationAssistant\Migration\Validation\Log\MigrationValidationInvalidAssociationLog;
+use SwagMigrationAssistant\Migration\Validation\Log\MigrationValidationInvalidOptionalFieldValueLog;
+use SwagMigrationAssistant\Migration\Validation\Log\MigrationValidationInvalidRequiredFieldValueLog;
+use SwagMigrationAssistant\Migration\Validation\Log\MigrationValidationInvalidRequiredTranslation;
use SwagMigrationAssistant\Migration\Validation\Log\MigrationValidationMissingRequiredFieldLog;
-use SwagMigrationAssistant\Migration\Validation\Log\MigrationValidationUnexpectedFieldLog;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
+use Symfony\Contracts\Service\ResetInterface;
/**
* @internal
*/
#[Package('fundamentals@after-sales')]
-readonly class MigrationValidationService
+class MigrationValidationService implements ResetInterface
{
+ /**
+ * @var list>
+ */
+ private const SYSTEM_MANAGED_FIELDS = [
+ CreatedAtField::class,
+ UpdatedAtField::class,
+ VersionField::class,
+ ReferenceVersionField::class,
+ TranslationsAssociationField::class,
+ ];
+
+ /**
+ * Maps entity name to an associative array of required field property names.
+ *
+ * Example:
+ * [
+ * 'entity_name' => [
+ * 'required_field_name' => true,
+ * ],
+ * ]
+ *
+ * @var array>
+ */
+ private array $requiredDefinitionFieldsCache = [];
+
public function __construct(
- private DefinitionInstanceRegistry $definitionRegistry,
- private EventDispatcherInterface $eventDispatcher,
- private LoggingServiceInterface $loggingService,
- private MappingServiceInterface $mappingService,
+ private readonly DefinitionInstanceRegistry $definitionRegistry,
+ private readonly EventDispatcherInterface $eventDispatcher,
+ private readonly LoggingServiceInterface $loggingService,
+ private readonly Connection $connection,
) {
}
+ public function reset(): void
+ {
+ $this->requiredDefinitionFieldsCache = [];
+ }
+
/**
* @param array|null $convertedEntity
* @param array $sourceData
+ *
+ * @throws \Exception|Exception
*/
public function validate(
MigrationContextInterface $migrationContext,
@@ -62,6 +108,10 @@ public function validate(
return null;
}
+ if (!$this->definitionRegistry->has($entityName)) {
+ return null;
+ }
+
$entityDefinition = $this->definitionRegistry->getByEntityName($entityName);
$validationContext = new MigrationValidationContext(
@@ -76,22 +126,8 @@ public function validate(
new MigrationPreValidationEvent($validationContext),
);
- try {
- $this->validateEntityStructure($validationContext);
- $this->validateFields($validationContext);
- $this->validateAssociations($validationContext);
- } catch (\Throwable $exception) {
- $validationContext->getValidationResult()->addLog(
- MigrationLogBuilder::fromMigrationContext($validationContext->getMigrationContext())
- ->withEntityName($validationContext->getEntityDefinition()->getEntityName())
- ->withSourceData($validationContext->getSourceData())
- ->withConvertedData($validationContext->getConvertedData())
- ->withExceptionMessage($exception->getMessage())
- ->withExceptionTrace($exception->getTrace())
- ->withEntityId($convertedEntity['id'] ?? null)
- ->build(MigrationValidationExceptionLog::class)
- );
- }
+ $this->validateEntityStructure($validationContext);
+ $this->validateFieldValues($validationContext);
$this->eventDispatcher->dispatch(
new MigrationPostValidationEvent($validationContext),
@@ -106,150 +142,363 @@ public function validate(
return $validationContext->getValidationResult();
}
+ /**
+ * @throws \Exception|Exception
+ */
private function validateEntityStructure(MigrationValidationContext $validationContext): void
{
- $fields = $validationContext->getEntityDefinition()->getFields();
+ $entityDefinition = $validationContext->getEntityDefinition();
- $requiredFields = array_values(array_map(
- static fn (Field $field) => $field->getPropertyName(),
- $fields->filterByFlag(Required::class)->getElements()
- ));
+ $requiredFields = $this->getRequiredFields(
+ $entityDefinition->getFields(),
+ $entityDefinition->getEntityName()
+ );
- $convertedFieldNames = array_keys($validationContext->getConvertedData());
- $missingRequiredFields = array_diff($requiredFields, $convertedFieldNames);
+ $missingRequiredFields = array_diff(
+ array_keys($requiredFields),
+ array_keys($validationContext->getConvertedData())
+ );
foreach ($missingRequiredFields as $missingField) {
- $validationContext->getValidationResult()->addLog(
- MigrationLogBuilder::fromMigrationContext($validationContext->getMigrationContext())
- ->withEntityName($validationContext->getEntityDefinition()->getEntityName())
- ->withFieldName($missingField)
- ->withConvertedData($validationContext->getConvertedData())
- ->withEntityId($validationContext->getConvertedData()['id'] ?? null)
- ->build(MigrationValidationMissingRequiredFieldLog::class)
- );
+ $this->addMissingRequiredFieldLog($validationContext, $missingField);
+ }
+ }
+
+ /**
+ * @throws \Exception|Exception
+ */
+ private function validateFieldValues(MigrationValidationContext $validationContext): void
+ {
+ $convertedData = $validationContext->getConvertedData();
+ $id = $convertedData['id'] ?? null;
+
+ if (!$this->validateId($validationContext, $id)) {
+ return;
}
- $unexpectedFields = array_diff($convertedFieldNames, array_keys($fields->getElements()));
+ $entityDefinition = $validationContext->getEntityDefinition();
+ $entityName = $entityDefinition->getEntityName();
+ $fields = $entityDefinition->getFields();
- foreach ($unexpectedFields as $unexpectedField) {
- $validationContext->getValidationResult()->addLog(
- MigrationLogBuilder::fromMigrationContext($validationContext->getMigrationContext())
- ->withEntityName($validationContext->getEntityDefinition()->getEntityName())
- ->withFieldName($unexpectedField)
- ->withConvertedData($validationContext->getConvertedData())
- ->withEntityId($validationContext->getConvertedData()['id'] ?? null)
- ->build(MigrationValidationUnexpectedFieldLog::class)
+ $entityExistence = EntityExistence::createForEntity($entityName, ['id' => $id]);
+ $parameters = new WriteParameterBag(
+ $entityDefinition,
+ WriteContext::createFromContext($validationContext->getContext()),
+ '',
+ new WriteCommandQueue(),
+ );
+
+ $requiredFields = $this->getRequiredFields($fields, $entityName);
+
+ foreach ($convertedData as $fieldName => $value) {
+ $this->validateField(
+ $validationContext,
+ $fields,
+ $fieldName,
+ $value,
+ $id,
+ $entityExistence,
+ $parameters,
+ isset($requiredFields[$fieldName])
);
}
}
- private function validateFields(MigrationValidationContext $validationContext): void
+ private function validateId(MigrationValidationContext $validationContext, mixed $id): bool
{
- $fields = $validationContext->getEntityDefinition()->getFields();
+ if ($id === null) {
+ $this->addExceptionLog(
+ $validationContext,
+ MigrationValidationException::unexpectedNullValue('id')
+ );
- if (!isset($validationContext->getConvertedData()['id'])) {
- throw MigrationException::unexpectedNullValue('id');
+ return false;
}
- if (!Uuid::isValid($validationContext->getConvertedData()['id'])) {
- throw MigrationException::invalidId($validationContext->getConvertedData()['id'], $validationContext->getEntityDefinition()->getEntityName());
+ if (!\is_string($id) || !Uuid::isValid($id)) {
+ $this->addExceptionLog(
+ $validationContext,
+ MigrationValidationException::invalidId((string) $id, $validationContext->getEntityDefinition()->getEntityName())
+ );
+
+ return false;
}
- $entityExistence = EntityExistence::createForEntity(
- $validationContext->getEntityDefinition()->getEntityName(),
- ['id' => $validationContext->getConvertedData()['id']],
- );
+ return true;
+ }
- $parameters = new WriteParameterBag(
- $validationContext->getEntityDefinition(),
- WriteContext::createFromContext($validationContext->getContext()),
- '',
- new WriteCommandQueue(),
+ private function validateField(
+ MigrationValidationContext $validationContext,
+ CompiledFieldCollection $fields,
+ string $fieldName,
+ mixed $value,
+ string $id,
+ EntityExistence $existence,
+ WriteParameterBag $parameters,
+ bool $isRequired,
+ ): void {
+ if (!$fields->has($fieldName)) {
+ return;
+ }
+
+ $field = clone $fields->get($fieldName);
+
+ try {
+ if ($field instanceof TranslationsAssociationField) {
+ $this->validateFieldByFieldSerializer($field, $value, $existence, $parameters, $isRequired);
+
+ return;
+ }
+
+ if ($field instanceof ManyToManyAssociationField || $field instanceof OneToManyAssociationField) {
+ $this->validateToManyAssociationStructure($validationContext, $fieldName, $value);
+
+ return;
+ }
+
+ if ($field instanceof ManyToOneAssociationField || $field instanceof OneToOneAssociationField) {
+ $this->validateToOneAssociationStructure($validationContext, $fieldName, $value);
+
+ return;
+ }
+
+ if ($field instanceof AssociationField) {
+ return;
+ }
+
+ $this->validateFieldByFieldSerializer($field, $value, $existence, $parameters, $isRequired);
+ } catch (MigrationValidationException $exception) {
+ $this->addValidationExceptionLog($validationContext, $exception, $fieldName, $value, $id);
+ } catch (\Throwable $exception) {
+ $this->addExceptionLog($validationContext, $exception);
+ }
+ }
+
+ /**
+ * @throws MigrationValidationException|\Exception
+ */
+ private function validateFieldByFieldSerializer(
+ Field $field,
+ mixed $value,
+ EntityExistence $entityExistence,
+ WriteParameterBag $parameters,
+ bool $isRequired,
+ ): void {
+ /**
+ * Replace all flags with Required to force the serializer to validate this field.
+ * AbstractFieldSerializer::requiresValidation() skips validation for fields without Required flag.
+ * The field is cloned before this method is called to avoid mutating the original definition.
+ */
+ $field->setFlags(new Required());
+
+ $keyValue = new KeyValuePair(
+ $field->getPropertyName(),
+ $value,
+ true
);
- foreach ($validationContext->getConvertedData() as $fieldName => $value) {
- if (!$fields->has($fieldName)) {
- continue;
+ try {
+ $serializer = $field->getSerializer();
+
+ // Consume the generator to trigger validation. Keys are not needed
+ \iterator_to_array($serializer->encode(
+ $field,
+ $entityExistence,
+ $keyValue,
+ $parameters
+ ), false);
+ } catch (\Throwable $e) {
+ $entityName = $parameters->getDefinition()->getEntityName();
+ $propertyName = $field->getPropertyName();
+
+ if ($field instanceof TranslationsAssociationField) {
+ throw MigrationValidationException::invalidTranslation($entityName, $propertyName, $e->getMessage());
}
- $field = clone $fields->get($fieldName);
- $field->setFlags(new Required());
+ if ($isRequired) {
+ throw MigrationValidationException::invalidRequiredFieldValue($entityName, $propertyName, $e->getMessage());
+ }
- $keyValue = new KeyValuePair(
- $field->getPropertyName(),
- $value,
- true
+ throw MigrationValidationException::invalidOptionalFieldValue($entityName, $propertyName, $e->getMessage());
+ }
+ }
+
+ /**
+ * @throws MigrationValidationException
+ */
+ private function validateToManyAssociationStructure(
+ MigrationValidationContext $validationContext,
+ string $fieldName,
+ mixed $value,
+ ): void {
+ $entityName = $validationContext->getEntityDefinition()->getEntityName();
+
+ if (!\is_array($value)) {
+ throw MigrationValidationException::invalidAssociation(
+ $entityName,
+ $fieldName,
+ \sprintf('must be an array, got %s', \get_debug_type($value))
);
+ }
+
+ foreach ($value as $index => $entry) {
+ if (!\is_array($entry)) {
+ throw MigrationValidationException::invalidAssociation(
+ $entityName,
+ $fieldName . '/' . $index,
+ \sprintf('entry at index %s must be an array, got %s', $index, \get_debug_type($entry))
+ );
+ }
- try {
- $serializer = $field->getSerializer();
- \iterator_to_array($serializer->encode($field, $entityExistence, $keyValue, $parameters), false);
- } catch (\Throwable $e) {
- $validationContext->getValidationResult()->addLog(
- MigrationLogBuilder::fromMigrationContext($validationContext->getMigrationContext())
- ->withEntityName($validationContext->getEntityDefinition()->getEntityName())
- ->withFieldName($fieldName)
- ->withConvertedData([$fieldName => $value])
- ->withSourceData($validationContext->getSourceData())
- ->withExceptionMessage($e->getMessage())
- ->withExceptionTrace($e->getTrace())
- ->withEntityId($validationContext->getConvertedData()['id'] ?? null)
- ->build(MigrationValidationInvalidFieldValueLog::class)
+ if (isset($entry['id']) && !Uuid::isValid($entry['id'])) {
+ throw MigrationValidationException::invalidAssociation(
+ $entityName,
+ $fieldName . '/' . $index . '/id',
+ \sprintf('invalid UUID "%s" at index %s', $entry['id'], $index)
);
}
}
}
- private function validateAssociations(MigrationValidationContext $validationContext): void
+ /**
+ * @throws MigrationValidationException
+ */
+ private function validateToOneAssociationStructure(
+ MigrationValidationContext $validationContext,
+ string $fieldName,
+ mixed $value,
+ ): void {
+ $entityName = $validationContext->getEntityDefinition()->getEntityName();
+
+ if (!\is_array($value)) {
+ throw MigrationValidationException::invalidAssociation(
+ $entityName,
+ $fieldName,
+ \sprintf('must be an array, got %s', \get_debug_type($value))
+ );
+ }
+
+ if (isset($value['id']) && !Uuid::isValid($value['id'])) {
+ throw MigrationValidationException::invalidAssociation(
+ $entityName,
+ $fieldName . '/id',
+ \sprintf('invalid UUID "%s"', $value['id'])
+ );
+ }
+ }
+
+ /**
+ * @throws Exception
+ *
+ * @return array
+ */
+ private function getRequiredFields(CompiledFieldCollection $fields, string $entityName): array
{
- $fields = $validationContext->getEntityDefinition()->getFields();
+ if (isset($this->requiredDefinitionFieldsCache[$entityName])) {
+ return $this->requiredDefinitionFieldsCache[$entityName];
+ }
- $fkFields = array_values(array_map(
- static fn (Field $field) => $field->getPropertyName(),
- $fields->filterInstance(FkField::class)->getElements()
- ));
+ $requiredDbColumns = $this->getRequiredDatabaseColumns($entityName);
+ $requiredFields = [];
- foreach ($fkFields as $fkFieldName) {
- if (!isset($validationContext->getConvertedData()[$fkFieldName])) {
+ foreach ($fields->filterByFlag(Required::class) as $field) {
+ if (\in_array($field::class, self::SYSTEM_MANAGED_FIELDS, true)) {
continue;
}
- $fkValue = $validationContext->getConvertedData()[$fkFieldName];
+ if (!($field instanceof StorageAware)) {
+ $requiredFields[$field->getPropertyName()] = true;
- if ($fkValue === '') {
continue;
}
- $fkField = $fields->get($fkFieldName);
-
- if (!$fkField instanceof FkField) {
- throw MigrationException::unexpectedNullValue($fkFieldName);
+ if (isset($requiredDbColumns[$field->getStorageName()])) {
+ $requiredFields[$field->getPropertyName()] = true;
}
+ }
- $referenceEntity = $fkField->getReferenceEntity();
+ return $this->requiredDefinitionFieldsCache[$entityName] = $requiredFields;
+ }
- if (!$referenceEntity) {
- throw MigrationException::unexpectedNullValue($fkFieldName);
- }
+ /**
+ * @throws Exception
+ *
+ * @return array
+ */
+ private function getRequiredDatabaseColumns(string $entityName): array
+ {
+ $requiredColumns = [];
- $hasMapping = $this->mappingService->hasValidMappingByEntityId(
- $validationContext->getMigrationContext()->getConnection()->getId(),
- $referenceEntity,
- $fkValue,
- $validationContext->getContext()
- );
+ $columns = $this->connection
+ ->createSchemaManager()
+ ->listTableColumns($entityName);
- if (!$hasMapping) {
- $validationContext->getValidationResult()->addLog(
- MigrationLogBuilder::fromMigrationContext($validationContext->getMigrationContext())
- ->withEntityName($validationContext->getEntityDefinition()->getEntityName())
- ->withFieldName($fkFieldName)
- ->withConvertedData([$fkFieldName => $fkValue])
- ->withSourceData($validationContext->getSourceData())
- ->withEntityId($validationContext->getConvertedData()['id'] ?? null)
- ->build(MigrationValidationInvalidForeignKeyLog::class)
- );
+ foreach ($columns as $column) {
+ if ($column->getNotnull() && $column->getDefault() === null && !$column->getAutoincrement()) {
+ $requiredColumns[$column->getName()] = true;
}
}
+
+ return $requiredColumns;
+ }
+
+ private function addMissingRequiredFieldLog(MigrationValidationContext $validationContext, string $fieldName): void
+ {
+ $convertedData = $validationContext->getConvertedData();
+ $entityId = isset($convertedData['id']) ? (string) $convertedData['id'] : null;
+
+ $validationContext->getValidationResult()->addLog(
+ MigrationLogBuilder::fromMigrationContext($validationContext->getMigrationContext())
+ ->withEntityName($validationContext->getEntityDefinition()->getEntityName())
+ ->withFieldName($fieldName)
+ ->withConvertedData($convertedData)
+ ->withEntityId($entityId)
+ ->build(MigrationValidationMissingRequiredFieldLog::class)
+ );
+ }
+
+ private function addValidationExceptionLog(
+ MigrationValidationContext $validationContext,
+ MigrationValidationException $exception,
+ string $fieldName,
+ mixed $value,
+ string $entityId,
+ ): void {
+ $logClass = match ($exception->getErrorCode()) {
+ MigrationValidationException::VALIDATION_INVALID_ASSOCIATION => MigrationValidationInvalidAssociationLog::class,
+ MigrationValidationException::VALIDATION_INVALID_REQUIRED_FIELD_VALUE => MigrationValidationInvalidRequiredFieldValueLog::class,
+ MigrationValidationException::VALIDATION_INVALID_OPTIONAL_FIELD_VALUE => MigrationValidationInvalidOptionalFieldValueLog::class,
+ MigrationValidationException::VALIDATION_INVALID_TRANSLATION => MigrationValidationInvalidRequiredTranslation::class,
+ default => MigrationValidationExceptionLog::class,
+ };
+
+ $validationContext->getValidationResult()->addLog(
+ MigrationLogBuilder::fromMigrationContext($validationContext->getMigrationContext())
+ ->withEntityName($validationContext->getEntityDefinition()->getEntityName())
+ ->withFieldName($fieldName)
+ ->withConvertedData([$fieldName => $value])
+ ->withSourceData($validationContext->getSourceData())
+ ->withExceptionMessage($exception->getMessage())
+ ->withExceptionTrace($exception->getTrace())
+ ->withEntityId($entityId)
+ ->build($logClass)
+ );
+ }
+
+ private function addExceptionLog(MigrationValidationContext $validationContext, \Throwable $exception): void
+ {
+ $convertedData = $validationContext->getConvertedData();
+ $entityId = isset($convertedData['id']) ? (string) $convertedData['id'] : null;
+
+ $validationContext->getValidationResult()->addLog(
+ MigrationLogBuilder::fromMigrationContext($validationContext->getMigrationContext())
+ ->withEntityName($validationContext->getEntityDefinition()->getEntityName())
+ ->withSourceData($validationContext->getSourceData())
+ ->withConvertedData($convertedData)
+ ->withExceptionMessage($exception->getMessage())
+ ->withExceptionTrace($exception->getTrace())
+ ->withEntityId($entityId)
+ ->build(MigrationValidationExceptionLog::class)
+ );
}
}
diff --git a/src/Profile/Shopware/Converter/OrderConverter.php b/src/Profile/Shopware/Converter/OrderConverter.php
index bbe5014eb..59c44568d 100644
--- a/src/Profile/Shopware/Converter/OrderConverter.php
+++ b/src/Profile/Shopware/Converter/OrderConverter.php
@@ -305,7 +305,11 @@ public function convert(
}
if (isset($data['attributes'])) {
- $converted['customFields'] = $this->getAttributes($data['attributes'], DefaultEntities::ORDER, $this->connectionName, ['id', 'orderID'], $this->context);
+ $customField = $this->getAttributes($data['attributes'], DefaultEntities::ORDER, $this->connectionName, ['id', 'orderID'], $this->context);
+
+ if ($customField !== null) {
+ $converted['customFields'] = $customField;
+ }
}
unset($data['attributes']);
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 afac4e53f..eaeb1607a 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
@@ -72,15 +72,16 @@
v-if="sslActive"
class="swag-migration-shop-information__shop-domain-prefix-icon"
name="regular-lock"
- size="12px"
+ size="14px"
/>
+
{{ shopUrlPrefix }}{{ shopUrl }}
{% endblock %}
diff --git a/src/Resources/app/administration/src/module/swag-migration/component/card/swag-migration-shop-information/swag-migration-shop-information.scss b/src/Resources/app/administration/src/module/swag-migration/component/card/swag-migration-shop-information/swag-migration-shop-information.scss
index 514735cc9..0977488f0 100644
--- a/src/Resources/app/administration/src/module/swag-migration/component/card/swag-migration-shop-information/swag-migration-shop-information.scss
+++ b/src/Resources/app/administration/src/module/swag-migration/component/card/swag-migration-shop-information/swag-migration-shop-information.scss
@@ -38,7 +38,7 @@
align-items: center;
font-size: var(--font-size-xs);
white-space: nowrap;
- gap: var(--scale-size-4);
+ gap: var(--scale-size-2);
}
.swag-migration-shop-information__profile-avatar {
@@ -46,11 +46,14 @@
}
.swag-migration-shop-information__shop-domain-prefix {
+ display: flex;
+ align-items: center;
+ gap: var(--scale-size-2);
color: var(--color-icon-attention-default);
}
.swag-migration-shop-information__shop-domain-prefix--is-ssl {
- color: var(--color-icon-attention-default);
+ color: var(--color-icon-positive-default);
}
.swag-migration-shop-information__connection-info {
diff --git a/src/Resources/app/administration/src/module/swag-migration/component/swag-migration-error-resolution/swag-migration-error-resolution-modal/index.ts b/src/Resources/app/administration/src/module/swag-migration/component/swag-migration-error-resolution/swag-migration-error-resolution-modal/index.ts
index 8f0ef08cd..1a703364a 100644
--- a/src/Resources/app/administration/src/module/swag-migration/component/swag-migration-error-resolution/swag-migration-error-resolution-modal/index.ts
+++ b/src/Resources/app/administration/src/module/swag-migration/component/swag-migration-error-resolution/swag-migration-error-resolution-modal/index.ts
@@ -17,8 +17,8 @@ const { Criteria } = Shopware.Data;
* null will render an unresolvable message.
*/
export const ERROR_CODE_COMPONENT_MAPPING: Record = {
- SWAG_MIGRATION_VALIDATION_INVALID_FIELD_VALUE: 'DEFAULT',
- SWAG_MIGRATION_VALIDATION_INVALID_FOREIGN_KEY: 'DEFAULT',
+ SWAG_MIGRATION_VALIDATION_INVALID_OPTIONAL_FIELD_VALUE: 'DEFAULT',
+ SWAG_MIGRATION_VALIDATION_INVALID_REQUIRED_FIELD_VALUE: 'DEFAULT',
SWAG_MIGRATION_VALIDATION_MISSING_REQUIRED_FIELD: 'DEFAULT',
} as const;
diff --git a/src/Resources/app/administration/src/module/swag-migration/component/swag-migration-error-resolution/swag-migration-error-resolution-step/swag-migration-error-resolution-step.scss b/src/Resources/app/administration/src/module/swag-migration/component/swag-migration-error-resolution/swag-migration-error-resolution-step/swag-migration-error-resolution-step.scss
index 3b4c3574c..e6e22fa24 100644
--- a/src/Resources/app/administration/src/module/swag-migration/component/swag-migration-error-resolution/swag-migration-error-resolution-step/swag-migration-error-resolution-step.scss
+++ b/src/Resources/app/administration/src/module/swag-migration/component/swag-migration-error-resolution/swag-migration-error-resolution-step/swag-migration-error-resolution-step.scss
@@ -58,7 +58,7 @@
}
.mt-card__content {
- padding: 0;
+ padding: 0 !important;
}
.mt-tabs__item:first-of-type {
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..194bb245d 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
@@ -624,8 +624,9 @@
"SWAG_MIGRATION__SHOPWARE_UNSUPPORTED_OBJECT_TYPE": "Nicht unterstützter Objekttyp",
"SWAG_MIGRATION__WRITE_EXCEPTION_OCCURRED": "Ein Schreibfehler ist aufgetreten",
"SWAG_MIGRATION_VALIDATION_EXCEPTION": "Validierungsfehler aufgetreten",
- "SWAG_MIGRATION_VALIDATION_INVALID_FIELD_VALUE": "Feld hat einen ungültigen Wert",
- "SWAG_MIGRATION_VALIDATION_INVALID_FOREIGN_KEY": "Ungültige Fremdschlüssel-Referenz",
+ "SWAG_MIGRATION_VALIDATION_INVALID_REQUIRED_FIELD_VALUE": "Pflichtfeld hat einen ungültigen Wert",
+ "SWAG_MIGRATION_VALIDATION_INVALID_OPTIONAL_FIELD_VALUE": "Optionales Feld hat einen ungültigen Wert",
+ "SWAG_MIGRATION_VALIDATION_INVALID_REQUIRED_TRANSLATION": "Erforderliche Übersetzung ist ungültig",
"SWAG_MIGRATION_VALIDATION_MISSING_REQUIRED_FIELD": "Erforderliches Feld fehlt",
"SWAG_MIGRATION_VALIDATION_UNEXPECTED_FIELD": "Unerwartetes Feld in Daten gefunden",
"SWAG_MIGRATION__DEACTIVATED_PACK_LANGUAGE": "Pack-Sprache ist deaktiviert",
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..d3259cf71 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
@@ -475,8 +475,9 @@
"SWAG_MIGRATION__SHOPWARE_UNSUPPORTED_OBJECT_TYPE": "Unsupported object type",
"SWAG_MIGRATION__WRITE_EXCEPTION_OCCURRED": "A write exception has occurred",
"SWAG_MIGRATION_VALIDATION_EXCEPTION": "Validation exception occurred",
- "SWAG_MIGRATION_VALIDATION_INVALID_FIELD_VALUE": "Field has an invalid value",
- "SWAG_MIGRATION_VALIDATION_INVALID_FOREIGN_KEY": "Invalid foreign key reference",
+ "SWAG_MIGRATION_VALIDATION_INVALID_REQUIRED_FIELD_VALUE": "Required field has an invalid value",
+ "SWAG_MIGRATION_VALIDATION_INVALID_OPTIONAL_FIELD_VALUE": "Optional field has an invalid value",
+ "SWAG_MIGRATION_VALIDATION_INVALID_REQUIRED_TRANSLATION": "Required translation is invalid",
"SWAG_MIGRATION_VALIDATION_MISSING_REQUIRED_FIELD": "Required field is missing",
"SWAG_MIGRATION_VALIDATION_UNEXPECTED_FIELD": "Unexpected field found in data",
"SWAG_MIGRATION__DEACTIVATED_PACK_LANGUAGE": "Pack language is deactivated",
diff --git a/tests/Jest/src/fixture.js b/tests/Jest/src/fixture.js
index cee9c18c6..95aabee19 100644
--- a/tests/Jest/src/fixture.js
+++ b/tests/Jest/src/fixture.js
@@ -112,7 +112,7 @@ export const fixtureLogGroups = Object.freeze([
},
{
// relation
- code: 'SWAG_MIGRATION_VALIDATION_INVALID_FIELD_VALUE',
+ code: 'SWAG_MIGRATION_VALIDATION_INVALID_REQUIRED_FIELD_VALUE',
count: 161,
entityName: 'product',
fieldName: 'options',
diff --git a/tests/Jest/src/module/swag-migration/component/swag-migration-error-resolution/swag-migration-error-resolution-modal.spec.js b/tests/Jest/src/module/swag-migration/component/swag-migration-error-resolution/swag-migration-error-resolution-modal.spec.js
index 24fa7d714..a59bd915e 100644
--- a/tests/Jest/src/module/swag-migration/component/swag-migration-error-resolution/swag-migration-error-resolution-modal.spec.js
+++ b/tests/Jest/src/module/swag-migration/component/swag-migration-error-resolution/swag-migration-error-resolution-modal.spec.js
@@ -182,8 +182,8 @@ describe('module/swag-migration/component/swag-migration-error-resolution/swag-m
describe('constants', () => {
it('should provide error code to component mapping', () => {
expect(ERROR_CODE_COMPONENT_MAPPING).toStrictEqual({
- SWAG_MIGRATION_VALIDATION_INVALID_FIELD_VALUE: 'DEFAULT',
- SWAG_MIGRATION_VALIDATION_INVALID_FOREIGN_KEY: 'DEFAULT',
+ SWAG_MIGRATION_VALIDATION_INVALID_OPTIONAL_FIELD_VALUE: 'DEFAULT',
+ SWAG_MIGRATION_VALIDATION_INVALID_REQUIRED_FIELD_VALUE: 'DEFAULT',
SWAG_MIGRATION_VALIDATION_MISSING_REQUIRED_FIELD: 'DEFAULT',
});
});
diff --git a/tests/Jest/src/module/swag-migration/component/swag-migration-error-resolution/swag-migration-error-resolution-step.spec.js b/tests/Jest/src/module/swag-migration/component/swag-migration-error-resolution/swag-migration-error-resolution-step.spec.js
index 3eb9ea981..252b91ed7 100644
--- a/tests/Jest/src/module/swag-migration/component/swag-migration-error-resolution/swag-migration-error-resolution-step.spec.js
+++ b/tests/Jest/src/module/swag-migration/component/swag-migration-error-resolution/swag-migration-error-resolution-step.spec.js
@@ -161,7 +161,7 @@ describe('src/module/swag-migration/component/swag-migration-error-resolution/sw
resolved: true,
}),
expect.objectContaining({
- name: 'swag-migration.index.error-resolution.codes.SWAG_MIGRATION_VALIDATION_INVALID_FIELD_VALUE',
+ name: 'swag-migration.index.error-resolution.codes.SWAG_MIGRATION_VALIDATION_INVALID_REQUIRED_FIELD_VALUE',
resolved: false,
}),
]),
diff --git a/tests/MigrationServicesTrait.php b/tests/MigrationServicesTrait.php
index 9135d0557..92d228953 100644
--- a/tests/MigrationServicesTrait.php
+++ b/tests/MigrationServicesTrait.php
@@ -7,6 +7,7 @@
namespace SwagMigrationAssistant\Test;
+use Doctrine\DBAL\Connection;
use Psr\Log\NullLogger;
use Shopware\Core\Checkout\Cart\Tax\TaxCalculator;
use Shopware\Core\Checkout\Order\Aggregate\OrderDelivery\OrderDeliveryStates;
@@ -211,7 +212,7 @@ protected function getMigrationDataConverter(
$this->getContainer()->get(DefinitionInstanceRegistry::class),
$this->getContainer()->get('event_dispatcher'),
$loggingService,
- $mappingService,
+ $this->getContainer()->get(Connection::class),
);
return new MigrationDataConverter(
@@ -221,7 +222,7 @@ protected function getMigrationDataConverter(
$loggingService,
$dataDefinition,
new DummyMappingService(),
- $validationService
+ $validationService,
);
}
diff --git a/tests/integration/Migration/Validation/MigrationValidationServiceTest.php b/tests/integration/Migration/Validation/MigrationValidationServiceTest.php
index fa4748df4..3ea51f83b 100644
--- a/tests/integration/Migration/Validation/MigrationValidationServiceTest.php
+++ b/tests/integration/Migration/Validation/MigrationValidationServiceTest.php
@@ -11,6 +11,7 @@
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\TestCase;
use Shopware\Core\Content\Product\ProductDefinition;
+use Shopware\Core\Defaults;
use Shopware\Core\Framework\Context;
use Shopware\Core\Framework\DataAbstractionLayer\EntityRepository;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
@@ -26,11 +27,13 @@
use SwagMigrationAssistant\Migration\Run\MigrationStep;
use SwagMigrationAssistant\Migration\Run\SwagMigrationRunCollection;
use SwagMigrationAssistant\Migration\Run\SwagMigrationRunDefinition;
+use SwagMigrationAssistant\Migration\Validation\Exception\MigrationValidationException;
use SwagMigrationAssistant\Migration\Validation\Log\MigrationValidationExceptionLog;
-use SwagMigrationAssistant\Migration\Validation\Log\MigrationValidationInvalidFieldValueLog;
-use SwagMigrationAssistant\Migration\Validation\Log\MigrationValidationInvalidForeignKeyLog;
+use SwagMigrationAssistant\Migration\Validation\Log\MigrationValidationInvalidAssociationLog;
+use SwagMigrationAssistant\Migration\Validation\Log\MigrationValidationInvalidOptionalFieldValueLog;
+use SwagMigrationAssistant\Migration\Validation\Log\MigrationValidationInvalidRequiredFieldValueLog;
+use SwagMigrationAssistant\Migration\Validation\Log\MigrationValidationInvalidRequiredTranslation;
use SwagMigrationAssistant\Migration\Validation\Log\MigrationValidationMissingRequiredFieldLog;
-use SwagMigrationAssistant\Migration\Validation\Log\MigrationValidationUnexpectedFieldLog;
use SwagMigrationAssistant\Migration\Validation\MigrationValidationResult;
use SwagMigrationAssistant\Migration\Validation\MigrationValidationService;
use SwagMigrationAssistant\Profile\Shopware54\Shopware54Profile;
@@ -47,6 +50,8 @@ class MigrationValidationServiceTest extends TestCase
private const CONNECTION_ID = '01991554142d73348ea58793d98f1989';
+ private MigrationContext $migrationContext;
+
private MigrationValidationService $validationService;
/**
@@ -76,7 +81,21 @@ protected function setUp(): void
$this->mappingRepo = static::getContainer()->get(SwagMigrationMappingDefinition::ENTITY_NAME . '.repository');
$this->context = Context::createDefaultContext();
+ $connection = new SwagMigrationConnectionEntity();
+ $connection->setId(self::CONNECTION_ID);
+ $connection->setProfileName(Shopware54Profile::PROFILE_NAME);
+ $connection->setGatewayName(DummyLocalGateway::GATEWAY_NAME);
+
$this->runId = Uuid::randomHex();
+
+ $this->migrationContext = new MigrationContext(
+ $connection,
+ new Shopware54Profile(),
+ new DummyLocalGateway(),
+ null,
+ $this->runId,
+ );
+
static::getContainer()->get('swag_migration_connection.repository')->create(
[
[
@@ -134,21 +153,8 @@ public function testShouldEarlyReturnNullWhenConvertedDataIsEmpty(): void
#[DataProvider('entityStructureAndFieldProvider')]
public function testShouldValidateStructureAndFieldsValues(array $convertedData, array $expectedLogs): void
{
- $connection = new SwagMigrationConnectionEntity();
- $connection->setId(self::CONNECTION_ID);
- $connection->setProfileName(Shopware54Profile::PROFILE_NAME);
- $connection->setGatewayName(DummyLocalGateway::GATEWAY_NAME);
-
- $migrationContext = new MigrationContext(
- $connection,
- new Shopware54Profile(),
- new DummyLocalGateway(),
- null,
- $this->runId,
- );
-
$result = $this->validationService->validate(
- $migrationContext,
+ $this->migrationContext,
$this->context,
$convertedData,
SwagMigrationLoggingDefinition::ENTITY_NAME,
@@ -166,10 +172,89 @@ public function testShouldValidateStructureAndFieldsValues(array $convertedData,
static::assertCount(\count($expectedLogs), $logs);
static::assertCount(\count($expectedLogs), $result->getLogs());
- $logCodes = array_map(fn ($log) => $log::class, $result->getLogs());
+ $logCodes = \array_map(fn ($log) => $log::class, $result->getLogs());
static::assertSame($expectedLogs, $logCodes);
}
+ public function testShouldFilterNullableFields(): void
+ {
+ $result = $this->validationService->validate(
+ $this->migrationContext,
+ $this->context,
+ [
+ 'id' => Uuid::randomHex(),
+ ],
+ ProductDefinition::ENTITY_NAME,
+ []
+ );
+
+ static::assertInstanceOf(MigrationValidationResult::class, $result);
+
+ $missingFields = \array_map(fn ($log) => $log->getFieldName(), $result->getLogs());
+
+ // Only 'stock' should be required as its not nullable in db and has no default
+ static::assertCount(1, $missingFields);
+ static::assertContains('stock', $missingFields);
+ }
+
+ /**
+ * @param array $convertedData
+ */
+ #[DataProvider('invalidIdProvider')]
+ public function testShouldLogWhenEntityHasInvalidOrMissingId(array $convertedData, string $expectedExceptionMessage): void
+ {
+ $result = $this->validationService->validate(
+ $this->migrationContext,
+ $this->context,
+ $convertedData,
+ SwagMigrationLoggingDefinition::ENTITY_NAME,
+ []
+ );
+
+ static::assertInstanceOf(MigrationValidationResult::class, $result);
+
+ $logs = \array_filter($result->getLogs(), fn ($log) => $log instanceof MigrationValidationExceptionLog);
+ static::assertCount(1, $logs);
+
+ $exceptionLog = array_values($logs)[0];
+ static::assertInstanceOf(MigrationValidationExceptionLog::class, $exceptionLog);
+
+ static::assertSame($expectedExceptionMessage, $exceptionLog->getExceptionMessage());
+ }
+
+ /**
+ * @return \Generator, string}>
+ */
+ public static function invalidIdProvider(): \Generator
+ {
+ $baseData = [
+ 'level' => 'error',
+ 'code' => 'some_code',
+ 'userFixable' => true,
+ 'createdAt' => (new \DateTime())->format(\DATE_ATOM),
+ ];
+
+ yield 'missing id (null)' => [
+ $baseData,
+ MigrationValidationException::unexpectedNullValue('id')->getMessage(),
+ ];
+
+ yield 'invalid uuid string' => [
+ [...$baseData, 'id' => 'invalid-uuid'],
+ MigrationValidationException::invalidId('invalid-uuid', SwagMigrationLoggingDefinition::ENTITY_NAME)->getMessage(),
+ ];
+
+ yield 'integer id instead of uuid string' => [
+ [...$baseData, 'id' => 12345],
+ MigrationValidationException::invalidId('12345', SwagMigrationLoggingDefinition::ENTITY_NAME)->getMessage(),
+ ];
+
+ yield 'empty string id' => [
+ [...$baseData, 'id' => ''],
+ MigrationValidationException::invalidId('', SwagMigrationLoggingDefinition::ENTITY_NAME)->getMessage(),
+ ];
+ }
+
/**
* @param array $convertedData
* @param array> $mappings
@@ -178,25 +263,12 @@ public function testShouldValidateStructureAndFieldsValues(array $convertedData,
#[DataProvider('associationProvider')]
public function testValidateAssociations(array $convertedData, array $mappings, array $expectedLogs): void
{
- $connection = new SwagMigrationConnectionEntity();
- $connection->setId(self::CONNECTION_ID);
- $connection->setProfileName(Shopware54Profile::PROFILE_NAME);
- $connection->setGatewayName(DummyLocalGateway::GATEWAY_NAME);
-
- $migrationContext = new MigrationContext(
- $connection,
- new Shopware54Profile(),
- new DummyLocalGateway(),
- null,
- $this->runId,
- );
-
if (!empty($mappings)) {
$this->mappingRepo->create($mappings, $this->context);
}
$result = $this->validationService->validate(
- $migrationContext,
+ $this->migrationContext,
$this->context,
$convertedData,
SwagMigrationLoggingDefinition::ENTITY_NAME,
@@ -205,10 +277,61 @@ public function testValidateAssociations(array $convertedData, array $mappings,
static::assertInstanceOf(MigrationValidationResult::class, $result);
- $logClasses = array_map(static fn ($log) => $log::class, $result->getLogs());
+ $logClasses = \array_map(static fn ($log) => $log::class, $result->getLogs());
static::assertEquals($expectedLogs, $logClasses);
}
+ public function testMissingTranslationAssociation(): void
+ {
+ $convertedData = [
+ 'id' => Uuid::randomHex(),
+ 'versionId' => Uuid::randomHex(),
+ 'stock' => 10,
+ 'translations' => ['lel'],
+ ];
+
+ $result = $this->validationService->validate(
+ $this->migrationContext,
+ $this->context,
+ $convertedData,
+ ProductDefinition::ENTITY_NAME,
+ []
+ );
+
+ static::assertInstanceOf(MigrationValidationResult::class, $result);
+
+ $logClasses = \array_map(static fn ($log) => $log::class, $result->getLogs());
+ static::assertCount(1, $logClasses);
+ static::assertEquals([MigrationValidationInvalidRequiredTranslation::class], $logClasses);
+ }
+
+ public function testValidTranslationAssociation(): void
+ {
+ $convertedData = [
+ 'id' => Uuid::randomHex(),
+ 'versionId' => Uuid::randomHex(),
+ 'stock' => 10,
+ 'translations' => [
+ Defaults::LANGUAGE_SYSTEM => [
+ 'name' => 'Valid name',
+ ],
+ ],
+ ];
+
+ $result = $this->validationService->validate(
+ $this->migrationContext,
+ $this->context,
+ $convertedData,
+ ProductDefinition::ENTITY_NAME,
+ []
+ );
+
+ static::assertInstanceOf(MigrationValidationResult::class, $result);
+
+ $logClasses = \array_map(static fn ($log) => $log::class, $result->getLogs());
+ static::assertCount(0, $logClasses);
+ }
+
public static function entityStructureAndFieldProvider(): \Generator
{
$log = [
@@ -243,25 +366,13 @@ public static function entityStructureAndFieldProvider(): \Generator
],
];
- yield 'structure - unexpected fields' => [
- [
- ...$log,
- 'unexpectedField1' => 'value',
- 'unexpectedField2' => 'value',
- ],
- [
- MigrationValidationUnexpectedFieldLog::class,
- MigrationValidationUnexpectedFieldLog::class,
- ],
- ];
-
yield 'fields - invalid type' => [
[
...$log,
'userFixable' => 'not_a_boolean',
],
[
- MigrationValidationInvalidFieldValueLog::class,
+ MigrationValidationInvalidOptionalFieldValueLog::class,
],
];
@@ -271,7 +382,7 @@ public static function entityStructureAndFieldProvider(): \Generator
'code' => str_repeat('sw', 128),
],
[
- MigrationValidationInvalidFieldValueLog::class,
+ MigrationValidationInvalidRequiredFieldValueLog::class,
],
];
@@ -291,7 +402,7 @@ public static function entityStructureAndFieldProvider(): \Generator
'sourceData' => "\xB1\x31",
],
[
- MigrationValidationInvalidFieldValueLog::class,
+ MigrationValidationInvalidOptionalFieldValueLog::class,
],
];
@@ -303,14 +414,12 @@ public static function entityStructureAndFieldProvider(): \Generator
'code' => ['sw'],
'userFixable' => true,
'createdAt' => (new \DateTime())->format(\DATE_ATOM),
- 'unexpectedField' => 'value',
],
[
MigrationValidationMissingRequiredFieldLog::class,
- MigrationValidationUnexpectedFieldLog::class,
- MigrationValidationInvalidFieldValueLog::class,
- MigrationValidationInvalidFieldValueLog::class,
- MigrationValidationInvalidFieldValueLog::class,
+ MigrationValidationInvalidRequiredFieldValueLog::class,
+ MigrationValidationInvalidRequiredFieldValueLog::class,
+ MigrationValidationInvalidRequiredFieldValueLog::class,
],
];
}
@@ -348,15 +457,6 @@ public static function associationProvider(): \Generator
[],
];
- yield 'invalid fk' => [
- [
- ...$log,
- 'runId' => Uuid::randomHex(),
- ],
- [$mapping],
- [MigrationValidationInvalidForeignKeyLog::class],
- ];
-
yield 'fk field not in converted data' => [
$log,
[],
@@ -370,7 +470,7 @@ public static function associationProvider(): \Generator
],
[],
[
- MigrationValidationInvalidFieldValueLog::class,
+ MigrationValidationInvalidOptionalFieldValueLog::class,
],
];
@@ -381,8 +481,299 @@ public static function associationProvider(): \Generator
],
[],
[
- MigrationValidationInvalidFieldValueLog::class,
+ MigrationValidationInvalidOptionalFieldValueLog::class,
+ ],
+ ];
+ }
+
+ /**
+ * Tests for ManyToMany and OneToMany association validation.
+ *
+ * @return \Generator, array}>
+ */
+ public static function toManyAssociationProvider(): \Generator
+ {
+ $baseProduct = [
+ 'id' => Uuid::randomHex(),
+ 'versionId' => Uuid::randomHex(),
+ 'stock' => 10,
+ 'translations' => [
+ Defaults::LANGUAGE_SYSTEM => [
+ 'name' => 'Test Product',
+ ],
+ ],
+ ];
+
+ yield 'valid categories association (empty array)' => [
+ [
+ ...$baseProduct,
+ 'categories' => [],
+ ],
+ [],
+ ];
+
+ yield 'valid categories association (with valid entries)' => [
+ [
+ ...$baseProduct,
+ 'categories' => [
+ ['id' => Uuid::randomHex()],
+ ['id' => Uuid::randomHex()],
+ ],
],
+ [],
];
+
+ yield 'invalid categories association (non-array value)' => [
+ [
+ ...$baseProduct,
+ 'categories' => 'not-an-array',
+ ],
+ [
+ MigrationValidationInvalidAssociationLog::class,
+ ],
+ ];
+
+ yield 'invalid categories association (entry is not array)' => [
+ [
+ ...$baseProduct,
+ 'categories' => [
+ 'not-an-array-entry',
+ ],
+ ],
+ [
+ MigrationValidationInvalidAssociationLog::class,
+ ],
+ ];
+
+ yield 'invalid categories association (invalid UUID in entry)' => [
+ [
+ ...$baseProduct,
+ 'categories' => [
+ ['id' => 'invalid-uuid'],
+ ],
+ ],
+ [
+ MigrationValidationInvalidAssociationLog::class,
+ ],
+ ];
+
+ yield 'invalid categories association (multiple errors)' => [
+ [
+ ...$baseProduct,
+ 'categories' => [
+ ['id' => Uuid::randomHex()],
+ 'invalid-entry',
+ ['id' => 'invalid-uuid'],
+ ],
+ ],
+ [
+ // Only first error is logged since validation throws on first failure
+ MigrationValidationInvalidAssociationLog::class,
+ ],
+ ];
+ }
+
+ /**
+ * @param array $convertedData
+ * @param array $expectedLogs
+ */
+ #[DataProvider('toManyAssociationProvider')]
+ public function testValidateToManyAssociations(array $convertedData, array $expectedLogs): void
+ {
+ $result = $this->validationService->validate(
+ $this->migrationContext,
+ $this->context,
+ $convertedData,
+ ProductDefinition::ENTITY_NAME,
+ []
+ );
+
+ static::assertInstanceOf(MigrationValidationResult::class, $result);
+
+ $logClasses = \array_map(static fn ($log) => $log::class, $result->getLogs());
+ static::assertEquals($expectedLogs, $logClasses);
+ }
+
+ /**
+ * Tests for ManyToOne and OneToOne association validation.
+ *
+ * @return \Generator, array}>
+ */
+ public static function toOneAssociationProvider(): \Generator
+ {
+ $baseProduct = [
+ 'id' => Uuid::randomHex(),
+ 'versionId' => Uuid::randomHex(),
+ 'stock' => 10,
+ 'translations' => [
+ Defaults::LANGUAGE_SYSTEM => [
+ 'name' => 'Test Product',
+ ],
+ ],
+ ];
+
+ yield 'valid manufacturer association (null value)' => [
+ $baseProduct,
+ [],
+ ];
+
+ yield 'valid manufacturer association (with valid id)' => [
+ [
+ ...$baseProduct,
+ 'manufacturer' => ['id' => Uuid::randomHex(), 'name' => 'Test Manufacturer'],
+ ],
+ [],
+ ];
+
+ yield 'invalid manufacturer association (non-array value)' => [
+ [
+ ...$baseProduct,
+ 'manufacturer' => 'not-an-array',
+ ],
+ [
+ MigrationValidationInvalidAssociationLog::class,
+ ],
+ ];
+
+ yield 'invalid manufacturer association (invalid UUID)' => [
+ [
+ ...$baseProduct,
+ 'manufacturer' => ['id' => 'invalid-uuid'],
+ ],
+ [
+ MigrationValidationInvalidAssociationLog::class,
+ ],
+ ];
+ }
+
+ /**
+ * @param array $convertedData
+ * @param array $expectedLogs
+ */
+ #[DataProvider('toOneAssociationProvider')]
+ public function testValidateToOneAssociations(array $convertedData, array $expectedLogs): void
+ {
+ $result = $this->validationService->validate(
+ $this->migrationContext,
+ $this->context,
+ $convertedData,
+ ProductDefinition::ENTITY_NAME,
+ []
+ );
+
+ static::assertInstanceOf(MigrationValidationResult::class, $result);
+
+ $logClasses = \array_map(static fn ($log) => $log::class, $result->getLogs());
+ static::assertEquals($expectedLogs, $logClasses);
+ }
+
+ public function testShouldReturnNullWhenEntityDefinitionDoesNotExist(): void
+ {
+ $result = $this->validationService->validate(
+ $this->migrationContext,
+ $this->context,
+ ['id' => Uuid::randomHex()],
+ 'non_existent_entity_definition',
+ []
+ );
+
+ static::assertNull($result);
+ }
+
+ public function testResetShouldClearRequiredFieldsCache(): void
+ {
+ $result1 = $this->validationService->validate(
+ $this->migrationContext,
+ $this->context,
+ [
+ 'id' => Uuid::randomHex(),
+ 'profileName' => 'profile',
+ 'gatewayName' => 'gateway',
+ 'level' => 'error',
+ 'code' => 'some_code',
+ 'userFixable' => true,
+ ],
+ SwagMigrationLoggingDefinition::ENTITY_NAME,
+ []
+ );
+
+ static::assertInstanceOf(MigrationValidationResult::class, $result1);
+
+ $this->validationService->reset();
+
+ $result2 = $this->validationService->validate(
+ $this->migrationContext,
+ $this->context,
+ [
+ 'id' => Uuid::randomHex(),
+ 'profileName' => 'profile',
+ 'gatewayName' => 'gateway',
+ 'level' => 'error',
+ 'code' => 'some_code',
+ 'userFixable' => true,
+ ],
+ SwagMigrationLoggingDefinition::ENTITY_NAME,
+ []
+ );
+
+ static::assertInstanceOf(MigrationValidationResult::class, $result2);
+
+ $this->clearCacheData();
+
+ static::assertCount(\count($result1->getLogs()), $result2->getLogs());
+ }
+
+ public function testValidNestedAssociationWithValidUuids(): void
+ {
+ $categoryId1 = Uuid::randomHex();
+ $categoryId2 = Uuid::randomHex();
+
+ $convertedData = [
+ 'id' => Uuid::randomHex(),
+ 'versionId' => Uuid::randomHex(),
+ 'stock' => 10,
+ 'translations' => [
+ Defaults::LANGUAGE_SYSTEM => [
+ 'name' => 'Test Product',
+ ],
+ ],
+ 'categories' => [
+ ['id' => $categoryId1],
+ ['id' => $categoryId2],
+ ],
+ ];
+
+ $result = $this->validationService->validate(
+ $this->migrationContext,
+ $this->context,
+ $convertedData,
+ ProductDefinition::ENTITY_NAME,
+ []
+ );
+
+ static::assertInstanceOf(MigrationValidationResult::class, $result);
+ static::assertCount(0, $result->getLogs());
+ }
+
+ public function testValidationLogsAreSavedToDatabase(): void
+ {
+ $this->validationService->validate(
+ $this->migrationContext,
+ $this->context,
+ [
+ 'id' => Uuid::randomHex(),
+ 'level' => 'error',
+ 'code' => 'some_code',
+ 'userFixable' => true,
+ ],
+ SwagMigrationLoggingDefinition::ENTITY_NAME,
+ []
+ );
+
+ $this->clearCacheData();
+
+ $logs = $this->loggingRepo->search(new Criteria(), $this->context)->getEntities();
+ static::assertInstanceOf(SwagMigrationLoggingCollection::class, $logs);
+ static::assertGreaterThan(0, $logs->count());
}
}
diff --git a/tests/unit/Migration/Logging/Log/MigrationLogTest.php b/tests/unit/Migration/Logging/Log/MigrationLogTest.php
index cdf99f432..c2ac1943c 100644
--- a/tests/unit/Migration/Logging/Log/MigrationLogTest.php
+++ b/tests/unit/Migration/Logging/Log/MigrationLogTest.php
@@ -39,10 +39,11 @@
use SwagMigrationAssistant\Migration\Logging\Log\WriteExceptionRunLog;
use SwagMigrationAssistant\Migration\MigrationContext;
use SwagMigrationAssistant\Migration\Validation\Log\MigrationValidationExceptionLog;
-use SwagMigrationAssistant\Migration\Validation\Log\MigrationValidationInvalidFieldValueLog;
-use SwagMigrationAssistant\Migration\Validation\Log\MigrationValidationInvalidForeignKeyLog;
+use SwagMigrationAssistant\Migration\Validation\Log\MigrationValidationInvalidAssociationLog;
+use SwagMigrationAssistant\Migration\Validation\Log\MigrationValidationInvalidOptionalFieldValueLog;
+use SwagMigrationAssistant\Migration\Validation\Log\MigrationValidationInvalidRequiredFieldValueLog;
+use SwagMigrationAssistant\Migration\Validation\Log\MigrationValidationInvalidRequiredTranslation;
use SwagMigrationAssistant\Migration\Validation\Log\MigrationValidationMissingRequiredFieldLog;
-use SwagMigrationAssistant\Migration\Validation\Log\MigrationValidationUnexpectedFieldLog;
use SwagMigrationAssistant\Profile\Shopware54\Shopware54Profile;
use SwagMigrationAssistant\Test\Mock\Gateway\Dummy\Local\DummyLocalGateway;
@@ -107,32 +108,39 @@ public static function logProvider(): \Generator
'userFixable' => false,
];
- yield MigrationValidationInvalidFieldValueLog::class => [
- 'logClass' => MigrationValidationInvalidFieldValueLog::class,
- 'code' => 'SWAG_MIGRATION_VALIDATION_INVALID_FIELD_VALUE',
+ yield MigrationValidationInvalidAssociationLog::class => [
+ 'logClass' => MigrationValidationInvalidAssociationLog::class,
+ 'code' => 'SWAG_MIGRATION_VALIDATION_INVALID_ASSOCIATION',
'level' => AbstractMigrationLogEntry::LOG_LEVEL_ERROR,
'userFixable' => true,
];
- yield MigrationValidationInvalidForeignKeyLog::class => [
- 'logClass' => MigrationValidationInvalidForeignKeyLog::class,
- 'code' => 'SWAG_MIGRATION_VALIDATION_INVALID_FOREIGN_KEY',
+ yield MigrationValidationInvalidOptionalFieldValueLog::class => [
+ 'logClass' => MigrationValidationInvalidOptionalFieldValueLog::class,
+ 'code' => 'SWAG_MIGRATION_VALIDATION_INVALID_OPTIONAL_FIELD_VALUE',
'level' => AbstractMigrationLogEntry::LOG_LEVEL_WARNING,
'userFixable' => true,
];
+ yield MigrationValidationInvalidRequiredFieldValueLog::class => [
+ 'logClass' => MigrationValidationInvalidRequiredFieldValueLog::class,
+ 'code' => 'SWAG_MIGRATION_VALIDATION_INVALID_REQUIRED_FIELD_VALUE',
+ 'level' => AbstractMigrationLogEntry::LOG_LEVEL_ERROR,
+ 'userFixable' => true,
+ ];
+
yield MigrationValidationMissingRequiredFieldLog::class => [
'logClass' => MigrationValidationMissingRequiredFieldLog::class,
'code' => 'SWAG_MIGRATION_VALIDATION_MISSING_REQUIRED_FIELD',
'level' => AbstractMigrationLogEntry::LOG_LEVEL_ERROR,
- 'userFixable' => false,
+ 'userFixable' => true,
];
- yield MigrationValidationUnexpectedFieldLog::class => [
- 'logClass' => MigrationValidationUnexpectedFieldLog::class,
- 'code' => 'SWAG_MIGRATION_VALIDATION_UNEXPECTED_FIELD',
- 'level' => AbstractMigrationLogEntry::LOG_LEVEL_WARNING,
- 'userFixable' => true,
+ yield MigrationValidationInvalidRequiredTranslation::class => [
+ 'logClass' => MigrationValidationInvalidRequiredTranslation::class,
+ 'code' => 'SWAG_MIGRATION_VALIDATION_INVALID_REQUIRED_TRANSLATION',
+ 'level' => AbstractMigrationLogEntry::LOG_LEVEL_ERROR,
+ 'userFixable' => false,
];
yield AssociationRequiredMissingLog::class => [