From 6f282b44f092868fbca7f45b89bb0b26f1b12b48 Mon Sep 17 00:00:00 2001 From: Lorenzo Ruozzi Date: Mon, 11 Apr 2022 14:09:33 +0200 Subject: [PATCH 01/24] Replace QueueItem with ItemImport model (#123) --- src/Command/ConsumeCommand.php | 104 ------------------ src/Command/EnqueueCommand.php | 61 ++++------ src/Command/QueueCleanupCommand.php | 97 ---------------- src/Controller/ProductEnqueueController.php | 36 ++---- src/DependencyInjection/Configuration.php | 19 ---- .../WebgriffeSyliusAkeneoExtension.php | 3 - src/Doctrine/ORM/QueueItemRepository.php | 50 --------- src/Entity/QueueItem.php | 80 -------------- src/Entity/QueueItemInterface.php | 30 ----- src/Message/ItemImport.php | 24 ++++ .../CleanableQueueItemRepositoryInterface.php | 12 -- .../QueueItemRepositoryInterface.php | 18 --- src/Resources/config/admin_routing.yaml | 16 --- src/Resources/config/config.yaml | 4 + .../config/doctrine/QueueItem.orm.xml | 19 ---- src/Resources/config/services.xml | 29 +---- 16 files changed, 62 insertions(+), 540 deletions(-) delete mode 100644 src/Doctrine/ORM/QueueItemRepository.php delete mode 100644 src/Entity/QueueItemInterface.php create mode 100644 src/Message/ItemImport.php delete mode 100644 src/Repository/CleanableQueueItemRepositoryInterface.php delete mode 100644 src/Repository/QueueItemRepositoryInterface.php delete mode 100644 src/Resources/config/doctrine/QueueItem.orm.xml diff --git a/src/Command/ConsumeCommand.php b/src/Command/ConsumeCommand.php index 7cbb49c7..e69de29b 100644 --- a/src/Command/ConsumeCommand.php +++ b/src/Command/ConsumeCommand.php @@ -1,104 +0,0 @@ -setDescription('Process the Queue by calling the proper importer for each item'); - } - - /** - * @throws \Throwable - */ - protected function execute(InputInterface $input, OutputInterface $output): int - { - if (!$this->lock()) { - $output->writeln('The command is already running in another process.'); - - return 0; - } - - $queueItems = $this->queueItemRepository->findAllToImport(); - foreach ($queueItems as $queueItem) { - $akeneoIdentifier = $queueItem->getAkeneoIdentifier(); - - try { - $importer = $this->resolveImporter($queueItem->getAkeneoEntity()); - $importer->import($akeneoIdentifier); - $queueItem->setImportedAt(new \DateTime()); - $queueItem->setErrorMessage(null); - } catch (\Throwable $t) { - /** @var EntityManagerInterface $objectManager */ - $objectManager = $this->managerRegistry->getManager(); - if (!$objectManager->isOpen()) { - $this->release(); - - throw $t; - } - $queueItem->setErrorMessage($t->getMessage() . \PHP_EOL . $t->getTraceAsString()); - $output->writeln( - sprintf( - 'There has been an error importing %s entity with identifier %s. ' . - 'The error was: %s.', - $queueItem->getAkeneoEntity(), - $akeneoIdentifier, - $t->getMessage(), - ), - ); - if ($output->isVeryVerbose()) { - $output->writeln((string) $t); - } - } - - $this->queueItemRepository->add($queueItem); - $output->writeln( - sprintf( - '%s entity with identifier %s has been imported.', - $queueItem->getAkeneoEntity(), - $akeneoIdentifier, - ), - ); - } - - $this->release(); - - return 0; - } - - private function resolveImporter(string $akeneoEntity): ImporterInterface - { - foreach ($this->importerRegistry->all() as $importer) { - if ($importer->getAkeneoEntity() === $akeneoEntity) { - return $importer; - } - } - - throw new \RuntimeException(sprintf('Cannot find suitable importer for entity "%s".', $akeneoEntity)); - } -} diff --git a/src/Command/EnqueueCommand.php b/src/Command/EnqueueCommand.php index 76894567..7f74d82c 100644 --- a/src/Command/EnqueueCommand.php +++ b/src/Command/EnqueueCommand.php @@ -4,17 +4,19 @@ namespace Webgriffe\SyliusAkeneoPlugin\Command; -use Sylius\Component\Resource\Factory\FactoryInterface; +use DateTime; +use InvalidArgumentException; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Command\LockableTrait; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Messenger\MessageBusInterface; +use Throwable; use Webgriffe\SyliusAkeneoPlugin\DateTimeBuilderInterface; -use Webgriffe\SyliusAkeneoPlugin\Entity\QueueItemInterface; use Webgriffe\SyliusAkeneoPlugin\ImporterInterface; use Webgriffe\SyliusAkeneoPlugin\ImporterRegistryInterface; -use Webgriffe\SyliusAkeneoPlugin\Repository\QueueItemRepositoryInterface; +use Webgriffe\SyliusAkeneoPlugin\Message\ItemImport; use Webmozart\Assert\Assert; final class EnqueueCommand extends Command @@ -32,10 +34,9 @@ final class EnqueueCommand extends Command protected static $defaultName = 'webgriffe:akeneo:enqueue'; public function __construct( - private QueueItemRepositoryInterface $queueItemRepository, - private FactoryInterface $queueItemFactory, private DateTimeBuilderInterface $dateTimeBuilder, private ImporterRegistryInterface $importerRegistry, + private MessageBusInterface $messageBus, ) { parent::__construct(); } @@ -76,18 +77,18 @@ protected function execute(InputInterface $input, OutputInterface $output): int $sinceFilePath = null; if ('' !== $sinceOptionValue = (string) $input->getOption(self::SINCE_OPTION_NAME)) { try { - $sinceDate = new \DateTime($sinceOptionValue); - } catch (\Throwable) { - throw new \InvalidArgumentException( + $sinceDate = new DateTime($sinceOptionValue); + } catch (Throwable) { + throw new InvalidArgumentException( sprintf('The "%s" argument must be a valid date', self::SINCE_OPTION_NAME), ); } } elseif ('' !== $sinceFilePath = (string) $input->getOption(self::SINCE_FILE_OPTION_NAME)) { $sinceDate = $this->getSinceDateByFile($sinceFilePath); } elseif ($input->getOption(self::ALL_OPTION_NAME) === true) { - $sinceDate = (new \DateTime())->setTimestamp(0); + $sinceDate = (new DateTime())->setTimestamp(0); } else { - throw new \InvalidArgumentException( + throw new InvalidArgumentException( sprintf( 'One of "--%s", "--%s" or "--%s" option must be specified', self::SINCE_OPTION_NAME, @@ -118,15 +119,11 @@ protected function execute(InputInterface $input, OutputInterface $output): int continue; } foreach ($identifiers as $identifier) { - if ($this->isEntityAlreadyQueuedToImport($importer->getAkeneoEntity(), $identifier)) { - continue; - } - $queueItem = $this->queueItemFactory->createNew(); - Assert::isInstanceOf($queueItem, QueueItemInterface::class); - $queueItem->setAkeneoEntity($importer->getAkeneoEntity()); - $queueItem->setAkeneoIdentifier($identifier); - $queueItem->setCreatedAt(new \DateTime()); - $this->queueItemRepository->add($queueItem); + $itemImport = new ItemImport( + $importer->getAkeneoEntity(), + $identifier + ); + $this->messageBus->dispatch($itemImport); $output->writeln( sprintf( '%s entity with identifier %s enqueued.', @@ -146,20 +143,20 @@ protected function execute(InputInterface $input, OutputInterface $output): int return 0; } - private function getSinceDateByFile(string $filepath): \DateTime + private function getSinceDateByFile(string $filepath): DateTime { if (!file_exists($filepath)) { - throw new \InvalidArgumentException( + throw new InvalidArgumentException( sprintf('The file "%s" does not exists', $filepath), ); } if (!is_readable($filepath)) { - throw new \InvalidArgumentException( + throw new InvalidArgumentException( sprintf('The file "%s" is not readable', $filepath), ); } if (!is_writable($filepath)) { - throw new \InvalidArgumentException( + throw new InvalidArgumentException( sprintf('The file "%s" is not writable', $filepath), ); } @@ -167,29 +164,19 @@ private function getSinceDateByFile(string $filepath): \DateTime try { $content = file_get_contents($filepath); Assert::string($content); - $sinceDate = new \DateTime(trim($content)); - } catch (\Throwable $t) { + $sinceDate = new DateTime(trim($content)); + } catch (Throwable $t) { throw new \RuntimeException(sprintf('The file "%s" must contain a valid datetime', $filepath), 0, $t); } return $sinceDate; } - private function writeSinceDateFile(string $filepath, \DateTime $runDate): void + private function writeSinceDateFile(string $filepath, DateTime $runDate): void { file_put_contents($filepath, $runDate->format('c')); } - private function isEntityAlreadyQueuedToImport(string $akeneoEntity, string $akeneoIdentifier): bool - { - $queueItem = $this->queueItemRepository->findOneToImport($akeneoEntity, $akeneoIdentifier); - if ($queueItem !== null) { - return true; - } - - return false; - } - /** * @return ImporterInterface[] */ @@ -220,7 +207,7 @@ private function getImporters(InputInterface $input): array $importers = []; foreach ($importersToUse as $importerToUse) { if (!array_key_exists($importerToUse, $allImporters)) { - throw new \InvalidArgumentException(sprintf('Importer "%s" does not exists.', $importerToUse)); + throw new InvalidArgumentException(sprintf('Importer "%s" does not exists.', $importerToUse)); } $importers[] = $allImporters[$importerToUse]; } diff --git a/src/Command/QueueCleanupCommand.php b/src/Command/QueueCleanupCommand.php index fb22cf14..e69de29b 100644 --- a/src/Command/QueueCleanupCommand.php +++ b/src/Command/QueueCleanupCommand.php @@ -1,97 +0,0 @@ -setDescription('Clean the Akeneo\'s queue of items older than N days.') - ->setHelp('This command allows you to clean the Akeneo\'s queue of item older than a specificed numbers of days.') - ->addArgument( - self::DAYS_ARGUMENT_NAME, - InputArgument::OPTIONAL, - 'Number of days from which to purge the queue of previous items', - (string) (self::DEFAULT_DAYS), - ) - ; - } - - protected function execute(InputInterface $input, OutputInterface $output): int - { - $numberOfDays = self::DEFAULT_DAYS; - // get the number of days from user - $numberOfDaysEntered = $input->getArgument(self::DAYS_ARGUMENT_NAME); - if ($numberOfDaysEntered !== null) { - if (!is_string($numberOfDaysEntered) || (int) $numberOfDaysEntered < 0) { - $output->writeln('Sorry, the number of days entered is not valid!'); - - return self::FAILURE; - } - $numberOfDays = (int) $numberOfDaysEntered; - } - - // get the beginning date - $dateToDelete = $this->getPreviousDateNDays($numberOfDays); - - $queueItems = $this->queueItemRepository->findToCleanup($dateToDelete); - - if (count($queueItems) === 0) { - $output->writeln('There are no items to clean'); - - return self::SUCCESS; - } - - /** @var QueueItem $queueItem */ - foreach ($queueItems as $queueItem) { - $this->queueItemRepository->remove($queueItem); - } - - $output->writeln(sprintf('%s items imported before %s has been deleted.', count($queueItems), $dateToDelete->format('Y-m-d H:i:s'))); - - return self::SUCCESS; - } - - /** - * @throws \Exception - */ - private function getPreviousDateNDays(int $numberOfDays): DateTime - { - $dtBuilder = new DateTimeBuilder(); - $today = $dtBuilder->build(); - - return $today->sub(new DateInterval(sprintf('P%dD', $numberOfDays))); - } -} diff --git a/src/Controller/ProductEnqueueController.php b/src/Controller/ProductEnqueueController.php index 69375a19..e4af37bd 100644 --- a/src/Controller/ProductEnqueueController.php +++ b/src/Controller/ProductEnqueueController.php @@ -9,18 +9,16 @@ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; -use Symfony\Component\Routing\Generator\UrlGeneratorInterface; +use Symfony\Component\Messenger\MessageBusInterface; use Symfony\Contracts\Translation\TranslatorInterface; -use Webgriffe\SyliusAkeneoPlugin\Entity\QueueItem; -use Webgriffe\SyliusAkeneoPlugin\Repository\QueueItemRepositoryInterface; +use Webgriffe\SyliusAkeneoPlugin\Message\ItemImport; use Webmozart\Assert\Assert; final class ProductEnqueueController extends AbstractController { public function __construct( - private QueueItemRepositoryInterface $queueItemRepository, private ProductRepositoryInterface $productRepository, - private UrlGeneratorInterface $urlGenerator, + private MessageBusInterface $messageBus, private TranslatorInterface $translator, ) { } @@ -33,38 +31,20 @@ public function enqueueAction(int $productId): Response throw new NotFoundHttpException('Product not found'); } - $alreadyEnqueued = []; $enqueued = []; foreach ($product->getVariants() as $productVariant) { $productVariantCode = $productVariant->getCode(); Assert::notNull($productVariantCode); - $productEnqueued = $this->queueItemRepository->findBy([ - 'akeneoIdentifier' => $productVariantCode, - 'akeneoEntity' => 'Product', - 'importedAt' => null, - ]); - if (count($productEnqueued) > 0) { - $alreadyEnqueued[] = $productVariantCode; - - continue; - } - - $queueItem = new QueueItem(); - $queueItem->setAkeneoEntity('Product'); - $queueItem->setAkeneoIdentifier($productVariantCode); - $queueItem->setCreatedAt(new \DateTime()); - $this->queueItemRepository->add($queueItem); + $queueItem = new ItemImport( + 'Product', + $productVariantCode + ); + $this->messageBus->dispatch($queueItem); $enqueued[] = $productVariantCode; } - foreach ($alreadyEnqueued as $code) { - $this->addFlash( - 'error', - $this->translator->trans('webgriffe_sylius_akeneo.ui.product_already_enqueued', ['code' => $code]), // @phpstan-ignore-line - ); - } foreach ($enqueued as $code) { $this->addFlash( 'success', diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php index f4e653c3..d326eb90 100644 --- a/src/DependencyInjection/Configuration.php +++ b/src/DependencyInjection/Configuration.php @@ -56,25 +56,6 @@ public function getConfigTreeBuilder(): TreeBuilder ->end() ->end() - ->arrayNode('resources')->addDefaultsIfNotSet() - ->children() - ->arrayNode('queue_item')->addDefaultsIfNotSet() - ->children() - ->variableNode('options')->end() - ->arrayNode('classes')->addDefaultsIfNotSet() - ->children() - ->scalarNode('model')->defaultValue(QueueItem::class)->cannotBeEmpty()->end() - ->scalarNode('interface')->defaultValue(QueueItemInterface::class)->cannotBeEmpty()->end() - ->scalarNode('controller')->defaultValue(ResourceController::class)->cannotBeEmpty()->end() - ->scalarNode('factory')->defaultValue(Factory::class)->cannotBeEmpty()->end() - ->scalarNode('repository')->defaultValue(QueueItemRepository::class)->cannotBeEmpty()->end() - ->end() - ->end() - ->end() - ->end() - ->end() - ->end() - ->end() ; diff --git a/src/DependencyInjection/WebgriffeSyliusAkeneoExtension.php b/src/DependencyInjection/WebgriffeSyliusAkeneoExtension.php index 886d954a..223dc382 100644 --- a/src/DependencyInjection/WebgriffeSyliusAkeneoExtension.php +++ b/src/DependencyInjection/WebgriffeSyliusAkeneoExtension.php @@ -118,9 +118,6 @@ public function load(array $config, ContainerBuilder $container): void $config = $this->processConfiguration($this->getConfiguration([], $container), $config); $loader = new XmlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config')); - Assert::isArray($config['resources']); - - $this->registerResources('webgriffe_sylius_akeneo', 'doctrine/orm', $config['resources'], $container); $this->registerApiClientParameters($config['api_client'], $container); $loader->load('services.xml'); diff --git a/src/Doctrine/ORM/QueueItemRepository.php b/src/Doctrine/ORM/QueueItemRepository.php deleted file mode 100644 index 89fe9210..00000000 --- a/src/Doctrine/ORM/QueueItemRepository.php +++ /dev/null @@ -1,50 +0,0 @@ -createQueryBuilder('o') - ->andWhere('o.importedAt IS NULL') - ->getQuery() - ->getResult() - ; - } - - public function findOneToImport(string $akeneoEntity, string $akeneoIdentifier): ?QueueItemInterface - { - return $this->createQueryBuilder('o') - ->andWhere('o.importedAt IS NULL') - ->andWhere('o.akeneoEntity = :akeneoEntity') - ->andWhere('o.akeneoIdentifier = :akeneoIdentifier') - ->setParameter('akeneoEntity', $akeneoEntity) - ->setParameter('akeneoIdentifier', $akeneoIdentifier) - ->getQuery() - ->getOneOrNullResult() - ; - } - - public function findToCleanup(DateTime $dateLimit): array - { - return $this->createQueryBuilder('o') - ->where('o.importedAt IS NOT NULL') - ->andWhere('o.importedAt <= :dateLimit') - ->setParameter('dateLimit', $dateLimit) - ->getQuery() - ->getResult() - ; - } -} diff --git a/src/Entity/QueueItem.php b/src/Entity/QueueItem.php index 6da0e483..e69de29b 100644 --- a/src/Entity/QueueItem.php +++ b/src/Entity/QueueItem.php @@ -1,80 +0,0 @@ -id; - } - - public function getAkeneoEntity(): string - { - return $this->akeneoEntity; - } - - public function getAkeneoIdentifier(): string - { - return $this->akeneoIdentifier; - } - - public function getErrorMessage(): ?string - { - return $this->errorMessage; - } - - public function getCreatedAt(): DateTimeInterface - { - return $this->createdAt; - } - - public function getImportedAt(): ?DateTimeInterface - { - return $this->importedAt; - } - - public function setAkeneoIdentifier(string $identifier): void - { - $this->akeneoIdentifier = $identifier; - } - - public function setAkeneoEntity(string $entity): void - { - $this->akeneoEntity = $entity; - } - - public function setErrorMessage(?string $errorMessage): void - { - $this->errorMessage = $errorMessage; - } - - public function setCreatedAt(DateTimeInterface $createdAt): void - { - $this->createdAt = $createdAt; - } - - public function setImportedAt(?DateTimeInterface $importedAt): void - { - $this->importedAt = $importedAt; - } -} diff --git a/src/Entity/QueueItemInterface.php b/src/Entity/QueueItemInterface.php deleted file mode 100644 index 15239675..00000000 --- a/src/Entity/QueueItemInterface.php +++ /dev/null @@ -1,30 +0,0 @@ -akeneoEntity; + } + + public function getAkeneoIdentifier(): string + { + return $this->akeneoIdentifier; + } +} diff --git a/src/Repository/CleanableQueueItemRepositoryInterface.php b/src/Repository/CleanableQueueItemRepositoryInterface.php deleted file mode 100644 index 85e48287..00000000 --- a/src/Repository/CleanableQueueItemRepositoryInterface.php +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/src/Resources/config/services.xml b/src/Resources/config/services.xml index 89adc2c3..0631ab74 100644 --- a/src/Resources/config/services.xml +++ b/src/Resources/config/services.xml @@ -6,35 +6,11 @@ - - - - - - - - - - - - - - - - - - - - - - + @@ -72,9 +48,8 @@ - - + From 86fc8ac680aecfcd3565ee47ef88431a4a06909f Mon Sep 17 00:00:00 2001 From: Lorenzo Ruozzi Date: Mon, 13 Jun 2022 11:47:15 +0200 Subject: [PATCH 02/24] Add ItemImport handler (#123) --- ...ng_product_associations_from_queue.feature | 2 +- .../importing_products_from_queue.feature | 18 ++-- src/DependencyInjection/Configuration.php | 5 -- .../WebgriffeSyliusAkeneoExtension.php | 2 + .../CommandEventSubscriber.php | 31 ------- .../ProductEventSubscriber.php | 38 ++++++++ src/MessageHandler/ItemImportHandler.php | 44 +++++++++ src/Resources/config/config.yaml | 1 + src/Resources/config/services.xml | 12 ++- .../FileAttributeValueHandler.php | 13 ++- src/ValueHandler/ImageValueHandler.php | 21 +++-- .../config/packages/test/messenger.yaml | 4 + .../Context/Cli/ConsumeCommandContext.php | 30 ------- .../Context/Cli/EnqueueCommandContext.php | 37 ++++---- .../Cli/QueueCleanupCommandContext.php | 73 --------------- .../Context/Cli/ReconcileCommandContext.php | 8 +- tests/Behat/Context/Db/ProductContext.php | 2 +- .../MessengerContext.php} | 90 +++++++++++-------- tests/Behat/Context/Setup/QueueContext.php | 37 ++++---- .../Context/System/FilesystemContext.php | 2 +- .../Context/Transform/QueueItemContext.php | 2 +- tests/Behat/Resources/services.xml | 20 +---- tests/Behat/Resources/suites.yml | 17 ---- .../Integration/TemporaryFilesManagerTest.php | 20 ++--- 24 files changed, 241 insertions(+), 288 deletions(-) delete mode 100644 src/EventSubscriber/CommandEventSubscriber.php create mode 100644 src/EventSubscriber/ProductEventSubscriber.php create mode 100644 src/MessageHandler/ItemImportHandler.php create mode 100644 tests/Application/config/packages/test/messenger.yaml rename tests/Behat/Context/{Db/QueueContext.php => Messenger/MessengerContext.php} (60%) diff --git a/features/importing_product_associations_from_queue.feature b/features/importing_product_associations_from_queue.feature index da8618a1..c91b5eca 100644 --- a/features/importing_product_associations_from_queue.feature +++ b/features/importing_product_associations_from_queue.feature @@ -12,6 +12,6 @@ Feature: Importing product associations from queue And the store has a product "upsell-product-2" with code "upsell-product-2" And there is one product associations to import with identifier "10627329" in the Akeneo queue And the store has a product association type "Upsell" with a code "UPSELL" - When I import all items in queue + When I consume the messages Then the product "10627329" should be associated to product "upsell-product-1" for association with code "UPSELL" And the product "10627329" should be associated to product "upsell-product-2" for association with code "UPSELL" diff --git a/features/importing_products_from_queue.feature b/features/importing_products_from_queue.feature index f3d21b14..3eb3fbc8 100644 --- a/features/importing_products_from_queue.feature +++ b/features/importing_products_from_queue.feature @@ -10,7 +10,7 @@ Feature: Importing products from queue And the store is also available in "it_IT" And there is one item to import with identifier "braided-hat-m" for the "Product" importer in the Akeneo queue And there is one item to import with identifier "braided-hat-l" for the "Product" importer in the Akeneo queue - When I import all items in queue + When I consume the messages Then the product "model-braided-hat" should exists with the right data And the product variant "braided-hat-m" of product "model-braided-hat" should exists with the right data And the product variant "braided-hat-l" of product "model-braided-hat" should exists with the right data @@ -20,10 +20,9 @@ Feature: Importing products from queue Given the store operates on a single channel And the store is also available in "it_IT" And there is one item to import with identifier "NOT_EXISTS" for the "Product" importer in the Akeneo queue - When I import all items in queue - Then the product "NOT_EXISTS" should not exists - And the queue item with identifier "NOT_EXISTS" for the "Product" importer has not been marked as imported - And the queue item with identifier "NOT_EXISTS" for the "Product" importer has an error message + When I consume the messages + Then the item import message for "NOT_EXISTS" identifier and the "Product" importer should have failed + And the product "NOT_EXISTS" should not exists @cli Scenario: Going on with subsequent product imports when any fail @@ -31,11 +30,10 @@ Feature: Importing products from queue And the store is also available in "it_IT" And there is one item to import with identifier "NOT_EXISTS" for the "Product" importer in the Akeneo queue And there is one item to import with identifier "braided-hat-m" for the "Product" importer in the Akeneo queue - When I import all items in queue - Then the product "NOT_EXISTS" should not exists + When I consume the messages + Then the item import message for "NOT_EXISTS" identifier and the "Product" importer should have failed + And the product "NOT_EXISTS" should not exists And the product variant "braided-hat-m" of product "model-braided-hat" should exists with the right data - And the queue item with identifier "braided-hat-m" for the "Product" importer has been marked as imported - And the queue item with identifier "NOT_EXISTS" for the "Product" importer has not been marked as imported @cli Scenario: Importing products with images should not leave temporary files in temporary files directory @@ -43,5 +41,5 @@ Feature: Importing products from queue And the store is also available in "it_IT" And there is one item to import with identifier "braided-hat-m" for the "Product" importer in the Akeneo queue And there is one item to import with identifier "braided-hat-l" for the "Product" importer in the Akeneo queue - When I import all items in queue + When I consume the messages Then there should not be any temporary file in the temporary files directory diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php index d326eb90..6b16a2d4 100644 --- a/src/DependencyInjection/Configuration.php +++ b/src/DependencyInjection/Configuration.php @@ -4,13 +4,8 @@ namespace Webgriffe\SyliusAkeneoPlugin\DependencyInjection; -use Sylius\Bundle\ResourceBundle\Controller\ResourceController; -use Sylius\Component\Resource\Factory\Factory; use Symfony\Component\Config\Definition\Builder\TreeBuilder; use Symfony\Component\Config\Definition\ConfigurationInterface; -use Webgriffe\SyliusAkeneoPlugin\Doctrine\ORM\QueueItemRepository; -use Webgriffe\SyliusAkeneoPlugin\Entity\QueueItem; -use Webgriffe\SyliusAkeneoPlugin\Entity\QueueItemInterface; final class Configuration implements ConfigurationInterface { diff --git a/src/DependencyInjection/WebgriffeSyliusAkeneoExtension.php b/src/DependencyInjection/WebgriffeSyliusAkeneoExtension.php index 223dc382..52494efd 100644 --- a/src/DependencyInjection/WebgriffeSyliusAkeneoExtension.php +++ b/src/DependencyInjection/WebgriffeSyliusAkeneoExtension.php @@ -53,6 +53,7 @@ final class WebgriffeSyliusAkeneoExtension extends AbstractResourceExtension imp '$productImageFactory' => 'sylius.factory.product_image', '$productImageRepository' => 'sylius.repository.product_image', '$apiClient' => 'webgriffe_sylius_akeneo.api_client', + '$temporaryFilesManager' => 'webgriffe_sylius_akeneo.temporary_file_manager', ], ], 'immutable_slug' => [ @@ -99,6 +100,7 @@ final class WebgriffeSyliusAkeneoExtension extends AbstractResourceExtension imp 'arguments' => [ '$apiClient' => 'webgriffe_sylius_akeneo.api_client', '$filesystem' => 'filesystem', + '$temporaryFilesManager' => 'webgriffe_sylius_akeneo.temporary_file_manager', ], ], 'metric_property' => [ diff --git a/src/EventSubscriber/CommandEventSubscriber.php b/src/EventSubscriber/CommandEventSubscriber.php deleted file mode 100644 index 3fade6e5..00000000 --- a/src/EventSubscriber/CommandEventSubscriber.php +++ /dev/null @@ -1,31 +0,0 @@ - ['onTerminateCommand']]; - } - - public function onTerminateCommand(ConsoleTerminateEvent $event): void - { - $command = $event->getCommand(); - - if ($command !== null && $command->getName() === 'webgriffe:akeneo:consume') { - $this->temporaryFilesManager->deleteAllTemporaryFiles(); - } - } -} diff --git a/src/EventSubscriber/ProductEventSubscriber.php b/src/EventSubscriber/ProductEventSubscriber.php new file mode 100644 index 00000000..15c9d636 --- /dev/null +++ b/src/EventSubscriber/ProductEventSubscriber.php @@ -0,0 +1,38 @@ + ['removeImagesFileProperty', -50], + 'sylius.product.pre_update' => ['removeImagesFileProperty', -50], + ]; + } + + /** + * When more than two variants with different images are handled by the same instance of Messenger + * the file property should be removed after having uploaded with the Sylius\Bundle\CoreBundle\EventListener\ImagesUploadListener. + * Otherwise, the next pre create/update product event will throw an error by getting content from the first image that was removed by the TemporaryFilesManager. + * See features/importing_products_from_queue.feature for having a real case. + */ + public function removeImagesFileProperty(GenericEvent $event): void + { + /** @var ImagesAwareInterface|mixed $subject */ + $subject = $event->getSubject(); + Assert::isInstanceOf($subject, ImagesAwareInterface::class); + + foreach ($subject->getImages() as $image) { + $image->setFile(null); + } + } +} diff --git a/src/MessageHandler/ItemImportHandler.php b/src/MessageHandler/ItemImportHandler.php new file mode 100644 index 00000000..feea55ba --- /dev/null +++ b/src/MessageHandler/ItemImportHandler.php @@ -0,0 +1,44 @@ +getAkeneoIdentifier(); + $importer = $this->resolveImporter($message->getAkeneoEntity()); + $importer->import($akeneoIdentifier); + + $this->entityManager->flush(); + + $this->temporaryFilesManager->deleteAllTemporaryFiles(); + } + + private function resolveImporter(string $akeneoEntity): ImporterInterface + { + foreach ($this->importerRegistry->all() as $importer) { + if ($importer->getAkeneoEntity() === $akeneoEntity) { + return $importer; + } + } + + throw new RuntimeException(sprintf('Cannot find suitable importer for entity "%s".', $akeneoEntity)); + } +} diff --git a/src/Resources/config/config.yaml b/src/Resources/config/config.yaml index 2c716cf2..55a65871 100644 --- a/src/Resources/config/config.yaml +++ b/src/Resources/config/config.yaml @@ -73,4 +73,5 @@ sylius_grid: framework: messenger: routing: + 'Webgriffe\SyliusAkeneoPlugin\Message\ItemImport': main diff --git a/src/Resources/config/services.xml b/src/Resources/config/services.xml index 0631ab74..26170ad8 100644 --- a/src/Resources/config/services.xml +++ b/src/Resources/config/services.xml @@ -65,8 +65,7 @@ %webgriffe_sylius_akeneo.temporary_files_prefix% - - + @@ -139,5 +138,14 @@ + + + + + + + + diff --git a/src/ValueHandler/FileAttributeValueHandler.php b/src/ValueHandler/FileAttributeValueHandler.php index a10beb8e..4a659e6c 100644 --- a/src/ValueHandler/FileAttributeValueHandler.php +++ b/src/ValueHandler/FileAttributeValueHandler.php @@ -7,6 +7,7 @@ use Akeneo\Pim\ApiClient\AkeneoPimClientInterface; use Akeneo\Pim\ApiClient\Exception\HttpException; use InvalidArgumentException; +use const JSON_THROW_ON_ERROR; use SplFileInfo; use Sylius\Component\Channel\Model\ChannelInterface; use Sylius\Component\Core\Model\ProductInterface; @@ -14,6 +15,7 @@ use Symfony\Component\Filesystem\Filesystem; use Symfony\Component\HttpFoundation\File\File; use Symfony\Component\HttpKernel\Exception\HttpException as SymfonyHttpException; +use Webgriffe\SyliusAkeneoPlugin\TemporaryFilesManagerInterface; use Webgriffe\SyliusAkeneoPlugin\ValueHandlerInterface; use Webmozart\Assert\Assert; @@ -24,6 +26,7 @@ final class FileAttributeValueHandler implements ValueHandlerInterface public function __construct( private AkeneoPimClientInterface $apiClient, private Filesystem $filesystem, + private TemporaryFilesManagerInterface $temporaryFilesManager, private string $akeneoAttributeCode, private string $downloadPath, ) { @@ -137,14 +140,18 @@ private function downloadFile(string $mediaCode): SplFileInfo $bodyContents = $response->getBody()->getContents(); if ($statusClass !== 2) { /** @var array $responseResult */ - $responseResult = json_decode($bodyContents, true, 512, \JSON_THROW_ON_ERROR); + $responseResult = json_decode($bodyContents, true, 512, JSON_THROW_ON_ERROR); throw new SymfonyHttpException((int) $responseResult['code'], (string) $responseResult['message']); } - $tempName = tempnam(sys_get_temp_dir(), 'akeneo-'); - Assert::string($tempName); + $tempName = $this->generateTempFilePath(); file_put_contents($tempName, $bodyContents); return new File($tempName); } + + private function generateTempFilePath(): string + { + return $this->temporaryFilesManager->generateTemporaryFilePath(); + } } diff --git a/src/ValueHandler/ImageValueHandler.php b/src/ValueHandler/ImageValueHandler.php index b70a4412..cb5e30f4 100644 --- a/src/ValueHandler/ImageValueHandler.php +++ b/src/ValueHandler/ImageValueHandler.php @@ -5,6 +5,7 @@ namespace Webgriffe\SyliusAkeneoPlugin\ValueHandler; use Akeneo\Pim\ApiClient\AkeneoPimClientInterface; +use InvalidArgumentException; use SplFileInfo; use Sylius\Component\Channel\Model\ChannelInterface; use Sylius\Component\Core\Model\ProductImageInterface; @@ -14,6 +15,7 @@ use Sylius\Component\Resource\Repository\RepositoryInterface; use Symfony\Component\HttpFoundation\File\File; use Symfony\Component\HttpKernel\Exception\HttpException; +use Webgriffe\SyliusAkeneoPlugin\TemporaryFilesManager; use Webgriffe\SyliusAkeneoPlugin\ValueHandlerInterface; use Webmozart\Assert\Assert; @@ -23,6 +25,7 @@ public function __construct( private FactoryInterface $productImageFactory, private RepositoryInterface $productImageRepository, private AkeneoPimClientInterface $apiClient, + private TemporaryFilesManager $temporaryFilesManager, private string $akeneoAttributeCode, private string $syliusImageType, ) { @@ -42,7 +45,7 @@ public function supports($subject, string $attribute, array $value): bool public function handle($subject, string $attribute, array $value): void { if (!$subject instanceof ProductVariantInterface) { - throw new \InvalidArgumentException( + throw new InvalidArgumentException( sprintf( 'This image value handler only supports instances of %s, %s given.', ProductVariantInterface::class, @@ -137,7 +140,7 @@ private function getValue(array $value, ProductInterface $product): ?string $productChannelCodes = array_map(static fn (ChannelInterface $channel): ?string => $channel->getCode(), $product->getChannels()->toArray()); foreach ($value as $valueData) { if (!is_array($valueData)) { - throw new \InvalidArgumentException(sprintf('Invalid Akeneo value data: expected an array, "%s" given.', gettype($valueData))); + throw new InvalidArgumentException(sprintf('Invalid Akeneo value data: expected an array, "%s" given.', gettype($valueData))); } // todo: we should throw here? it seeme that API won't never return an empty array if (!array_key_exists('data', $valueData)) { @@ -145,7 +148,7 @@ private function getValue(array $value, ProductInterface $product): ?string } if (!array_key_exists('scope', $valueData)) { - throw new \InvalidArgumentException('Invalid Akeneo value data: required "scope" information was not found.'); + throw new InvalidArgumentException('Invalid Akeneo value data: required "scope" information was not found.'); } if ($valueData['scope'] !== null && !in_array($valueData['scope'], $productChannelCodes, true)) { continue; @@ -154,13 +157,13 @@ private function getValue(array $value, ProductInterface $product): ?string /** @psalm-suppress MixedAssignment */ $data = $valueData['data']; if (!is_string($data) && null !== $data) { - throw new \InvalidArgumentException(sprintf('Invalid Akeneo value data: expected a string or null value, got "%s".', gettype($data))); + throw new InvalidArgumentException(sprintf('Invalid Akeneo value data: expected a string or null value, got "%s".', gettype($data))); } return $data; } - throw new \InvalidArgumentException('Invalid Akeneo value data: cannot find the media code.'); + throw new InvalidArgumentException('Invalid Akeneo value data: cannot find the media code.'); } private function downloadFile(string $mediaCode): SplFileInfo @@ -174,10 +177,14 @@ private function downloadFile(string $mediaCode): SplFileInfo throw new HttpException((int) $responseResult['code'], (string) $responseResult['message']); } - $tempName = tempnam(sys_get_temp_dir(), 'akeneo-'); - Assert::string($tempName); + $tempName = $this->generateTempFilePath(); file_put_contents($tempName, $bodyContents); return new File($tempName); } + + private function generateTempFilePath(): string + { + return $this->temporaryFilesManager->generateTemporaryFilePath(); + } } diff --git a/tests/Application/config/packages/test/messenger.yaml b/tests/Application/config/packages/test/messenger.yaml new file mode 100644 index 00000000..febf1335 --- /dev/null +++ b/tests/Application/config/packages/test/messenger.yaml @@ -0,0 +1,4 @@ +framework: + messenger: + transports: + main: 'in-memory://' diff --git a/tests/Behat/Context/Cli/ConsumeCommandContext.php b/tests/Behat/Context/Cli/ConsumeCommandContext.php index 3d0fb005..e69de29b 100644 --- a/tests/Behat/Context/Cli/ConsumeCommandContext.php +++ b/tests/Behat/Context/Cli/ConsumeCommandContext.php @@ -1,30 +0,0 @@ -kernel); - $application->setAutoExit(false); - $application->add($this->consumeCommand); - $applicationTester = new ApplicationTester($application); - $applicationTester->run(['command' => 'webgriffe:akeneo:consume']); - } -} diff --git a/tests/Behat/Context/Cli/EnqueueCommandContext.php b/tests/Behat/Context/Cli/EnqueueCommandContext.php index 556b93ef..58ea6a7b 100644 --- a/tests/Behat/Context/Cli/EnqueueCommandContext.php +++ b/tests/Behat/Context/Cli/EnqueueCommandContext.php @@ -16,12 +16,15 @@ final class EnqueueCommandContext implements Context { - public function __construct(private KernelInterface $kernel, private EnqueueCommand $enqueueCommand, private SharedStorageInterface $sharedStorage) - { + public function __construct( + private KernelInterface $kernel, + private EnqueueCommand $enqueueCommand, + private SharedStorageInterface $sharedStorage + ) { } /** - * @When /^I enqueue items for all importers modified since date "([^"]+)"$/ + * @When I enqueue items for all importers modified since date :date */ public function iRunEnqueueCommandWithSinceDate(string $date): void { @@ -37,7 +40,7 @@ public function iRunEnqueueCommandWithSinceDate(string $date): void /** * @When I enqueue items for all importers with no since date */ - public function iRunEnqueueCommandWithNoSinceDate(): void + public function iEnqueueItemsForAllImportersWithNoSinceDate(): void { $commandTester = $this->getCommandTester(); @@ -49,9 +52,9 @@ public function iRunEnqueueCommandWithNoSinceDate(): void } /** - * @Then /^I should be notified that a since date is required$/ + * @Then I should be notified that a since date is required */ - public function theCommandShouldHaveThrownExceptionWithMessageContaining(): void + public function iShouldBeNotifiedThatASinceDateIsRequired(): void { /** @var Throwable|mixed $throwable */ $throwable = $this->sharedStorage->get('command_exception'); @@ -63,7 +66,7 @@ public function theCommandShouldHaveThrownExceptionWithMessageContaining(): void } /** - * @When /^I enqueue items for all importers with invalid since date$/ + * @When I enqueue items for all importers with invalid since date */ public function iEnqueueItemsForAllImportersWithInvalidSinceDate(): void { @@ -77,7 +80,7 @@ public function iEnqueueItemsForAllImportersWithInvalidSinceDate(): void } /** - * @Then /^I should be notified that the since date must be a valid date$/ + * @Then I should be notified that the since date must be a valid date */ public function iShouldBeNotifiedThatTheSinceDateMustBeAValidDate(): void { @@ -88,7 +91,7 @@ public function iShouldBeNotifiedThatTheSinceDateMustBeAValidDate(): void } /** - * @When /^I enqueue items with since date specified from a not existent file$/ + * @When I enqueue items with since date specified from a not existent file */ public function iEnqueueItemsWithSinceDateSpecifiedFromANotExistentFile(): void { @@ -103,7 +106,7 @@ public function iEnqueueItemsWithSinceDateSpecifiedFromANotExistentFile(): void } /** - * @Then /^I should be notified that the since date file does not exists$/ + * @Then I should be notified that the since date file does not exists */ public function iShouldBeNotifiedThatTheSinceDateFileDoesNotExists(): void { @@ -114,9 +117,9 @@ public function iShouldBeNotifiedThatTheSinceDateFileDoesNotExists(): void } /** - * @When /^I enqueue items for all importers modified since date specified from file "([^"]+)"$/ + * @When I enqueue items for all importers modified since date specified from file :file */ - public function iEnqueueItemsWithSinceDateSpecifiedFromFile(string $file): void + public function iEnqueueItemsForAllImportersModifiedSinceDateSpecifiedFromFile(string $file): void { $commandTester = $this->getCommandTester(); $filepath = vfsStream::url('root/' . $file); @@ -129,7 +132,7 @@ public function iEnqueueItemsWithSinceDateSpecifiedFromFile(string $file): void } /** - * @When /^I enqueue all items for all importers$/ + * @When I enqueue all items for all importers */ public function iEnqueueAllItemsForAllImporters(): void { @@ -139,9 +142,9 @@ public function iEnqueueAllItemsForAllImporters(): void } /** - * @When /^I enqueue all items for the "([^"]+)" importer$/ + * @When I enqueue all items for the :importer importer */ - public function iEnqueueItemsModifiedSinceDateForTheImporter(string $importer): void + public function iEnqueueAllItemsForTheImporter(string $importer): void { $commandTester = $this->getCommandTester(); @@ -151,7 +154,7 @@ public function iEnqueueItemsModifiedSinceDateForTheImporter(string $importer): } /** - * @When /^I enqueue all items for a not existent importer$/ + * @When I enqueue all items for a not existent importer */ public function iEnqueueAllItemsForANotExistentImporter(): void { @@ -167,7 +170,7 @@ public function iEnqueueAllItemsForANotExistentImporter(): void } /** - * @Then /^I should be notified that the importer does not exists$/ + * @Then I should be notified that the importer does not exists */ public function iShouldBeNotifiedThatTheImporterDoesNotExists(): void { diff --git a/tests/Behat/Context/Cli/QueueCleanupCommandContext.php b/tests/Behat/Context/Cli/QueueCleanupCommandContext.php index b15dd247..e69de29b 100644 --- a/tests/Behat/Context/Cli/QueueCleanupCommandContext.php +++ b/tests/Behat/Context/Cli/QueueCleanupCommandContext.php @@ -1,73 +0,0 @@ -sharedStorage->set('command_input_days', $days); - $commandTester = $this->getCommandTester(); - - $input = ['command' => 'webgriffe:akeneo:cleanup-queue']; - if ($days !== null) { - $input['days'] = (string) $days; - } - $commandTester->execute($input); - $this->sharedStorage->set('command_display', $commandTester->getDisplay()); - } - - /** - * @Then I should be notified that there are no items to clean - */ - public function iShouldBeNotifiedThatThereAreNoItemsToClean(): void - { - $output = $this->sharedStorage->get('command_display'); - Assert::contains($output, 'There are no items to clean'); - } - - /** - * @Then /^I should be notified that (\d+) item[s]? (has|have) been deleted$/ - */ - public function iShouldBeNotifiedThatItemHasBeenDeleted(int $count): void - { - $output = $this->sharedStorage->get('command_display'); - Assert::regex($output, "/$count items imported before \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} has been deleted/"); - } - - /** - * @Then /^there shouldn\'t be any more item[s]? to clean$/ - */ - public function thereShouldntBeAnyMoreItemToClean(): void - { - $this->iCleanTheQueue($this->sharedStorage->has('command_input_days') ? $this->sharedStorage->get('command_input_days') : null); - $this->iShouldBeNotifiedThatThereAreNoItemsToClean(); - } - - private function getCommandTester(): CommandTester - { - $application = new Application($this->kernel); - $application->add($this->queueCleanupCommand); - $command = $application->find('webgriffe:akeneo:cleanup-queue'); - - return new CommandTester($command); - } -} diff --git a/tests/Behat/Context/Cli/ReconcileCommandContext.php b/tests/Behat/Context/Cli/ReconcileCommandContext.php index b91b7713..a84d767e 100644 --- a/tests/Behat/Context/Cli/ReconcileCommandContext.php +++ b/tests/Behat/Context/Cli/ReconcileCommandContext.php @@ -12,12 +12,14 @@ final class ReconcileCommandContext implements Context { - public function __construct(private KernelInterface $kernel, private ReconcileCommand $reconcileCommand) - { + public function __construct( + private KernelInterface $kernel, + private ReconcileCommand $reconcileCommand + ) { } /** - * @When /^I reconcile items$/ + * @When I reconcile items */ public function iReconcileItems(): void { diff --git a/tests/Behat/Context/Db/ProductContext.php b/tests/Behat/Context/Db/ProductContext.php index df4dbf7c..1d2ab02e 100644 --- a/tests/Behat/Context/Db/ProductContext.php +++ b/tests/Behat/Context/Db/ProductContext.php @@ -40,7 +40,7 @@ public function theProductVariantShouldExistsWithTheRightData(string $code, stri } /** - * @Then /^the product "([^"]*)" should not exists$/ + * @Then the product :code should not exists */ public function theProductShouldNotExists(string $code): void { diff --git a/tests/Behat/Context/Db/QueueContext.php b/tests/Behat/Context/Messenger/MessengerContext.php similarity index 60% rename from tests/Behat/Context/Db/QueueContext.php rename to tests/Behat/Context/Messenger/MessengerContext.php index 0033613d..63e420e0 100644 --- a/tests/Behat/Context/Db/QueueContext.php +++ b/tests/Behat/Context/Messenger/MessengerContext.php @@ -2,44 +2,24 @@ declare(strict_types=1); -namespace Tests\Webgriffe\SyliusAkeneoPlugin\Behat\Context\Db; +namespace Tests\Webgriffe\SyliusAkeneoPlugin\Behat\Context\Messenger; use Behat\Behat\Context\Context; -use Webgriffe\SyliusAkeneoPlugin\Entity\QueueItemInterface; -use Webgriffe\SyliusAkeneoPlugin\Repository\QueueItemRepositoryInterface; +use InvalidArgumentException; +use Symfony\Component\Messenger\Transport\InMemoryTransport; +use Throwable; +use Webgriffe\SyliusAkeneoPlugin\Message\ItemImport; +use Webgriffe\SyliusAkeneoPlugin\MessageHandler\ItemImportHandler; use Webmozart\Assert\Assert; -final class QueueContext implements Context +final class MessengerContext implements Context { - public function __construct(private QueueItemRepositoryInterface $queueItemRepository) - { - } - - /** - * @Given /^the queue item with identifier "([^"]*)" for the "([^"]*)" importer has been marked as imported$/ - */ - public function theQueueItemForProductWithIdentifierHasBeenMarkedAsImported(string $identifier, string $importer): void - { - $queueItem = $this->getQueueItemByImporterAndIdentifier($importer, $identifier); - Assert::notNull($queueItem->getImportedAt()); - } + private array $failedMessages = []; - /** - * @Given /^the queue item with identifier "([^"]*)" for the "([^"]*)" importer has not been marked as imported$/ - */ - public function theQueueItemForProductWithIdentifierHasNotBeenMarkedAsImported(string $identifier, string $importer): void - { - $queueItem = $this->getQueueItemByImporterAndIdentifier($importer, $identifier); - Assert::null($queueItem->getImportedAt()); - } - - /** - * @Given /^the queue item with identifier "([^"]*)" for the "([^"]*)" importer has an error message$/ - */ - public function theQueueItemHasAnErrorMessage(string $identifier, string $importer): void - { - $queueItem = $this->getQueueItemByImporterAndIdentifier($importer, $identifier); - Assert::notNull($queueItem->getErrorMessage()); + public function __construct( + private InMemoryTransport $transport, + private ItemImportHandler $itemImportHandler, + ) { } /** @@ -119,12 +99,48 @@ public function thereShouldBeItemsForTheImporterOnlyInTheAkeneoQueue(string $imp Assert::count($this->queueItemRepository->findAll(), count($importerItems)); } - private function getQueueItemByImporterAndIdentifier(string $importer, string $identifier): QueueItemInterface + private function getQueueItemByImporterAndIdentifier(string $importer, string $identifier): ItemImport { - /** @var QueueItemInterface|null $item */ - $item = $this->queueItemRepository->findOneBy(['akeneoEntity' => $importer, 'akeneoIdentifier' => $identifier]); - Assert::isInstanceOf($item, QueueItemInterface::class); + $sentMessages = $this->transport->get(); + foreach ($sentMessages as $sentMessage) { + /** @var ItemImport|object $message */ + $message = $sentMessage->getMessage(); + if ($message instanceof ItemImport && $message->getAkeneoEntity() === $importer && $message->getAkeneoIdentifier() === $identifier) { + return $message; + } + } + + throw new InvalidArgumentException(sprintf('No message founded for importer "%s" and identifier "%s".', $importer, $identifier)); + } - return $item; + /** + * @When I consume the messages + */ + public function iConsumeTheMessages(): void + { + foreach ($this->transport->get() as $envelope) { + $message = $envelope->getMessage(); + if (!$message instanceof ItemImport) { + continue; + } + try { + $this->itemImportHandler->__invoke($message); + } catch (Throwable $throwable) { + $this->failedMessages[] = $message; + + continue; + } + } + } + + /** + * @Then the item import message for :identifier identifier and the :importer importer should have failed + */ + public function theItemImportMessageForProductShouldHaveFailed(string $identifier, string $importer): void + { + $failedMessages = array_filter($this->failedMessages, static function (ItemImport $failedMessage) use ($identifier, $importer): bool { + return $failedMessage->getAkeneoIdentifier() === $identifier && $failedMessage->getAkeneoEntity() === $importer; + }); + Assert::count($failedMessages, 1); } } diff --git a/tests/Behat/Context/Setup/QueueContext.php b/tests/Behat/Context/Setup/QueueContext.php index 5afbe59b..adc3c7e4 100644 --- a/tests/Behat/Context/Setup/QueueContext.php +++ b/tests/Behat/Context/Setup/QueueContext.php @@ -6,43 +6,40 @@ use Behat\Behat\Context\Context; use Sylius\Behat\Service\SharedStorageInterface; -use Sylius\Component\Resource\Factory\FactoryInterface; +use Symfony\Component\Messenger\MessageBusInterface; use Webgriffe\SyliusAkeneoPlugin\Entity\QueueItemInterface; +use Webgriffe\SyliusAkeneoPlugin\Message\ItemImport; use Webgriffe\SyliusAkeneoPlugin\Repository\QueueItemRepositoryInterface; final class QueueContext implements Context { - public function __construct(private FactoryInterface $queueItemFactory, private QueueItemRepositoryInterface $queueItemRepository, private SharedStorageInterface $sharedStorage) - { + public function __construct( + private SharedStorageInterface $sharedStorage, + private MessageBusInterface $messageBus, + ) { } /** - * @Given /^there is one item to import with identifier "([^"]*)" for the "([^"]*)" importer in the Akeneo queue$/ - * @Given /^there is a not imported item with identifier "([^"]*)" for the "([^"]*)" importer in the Akeneo queue$/ + * @Given there is one item to import with identifier :identifier for the :importer importer in the Akeneo queue + * @Given there is a not imported item with identifier :identifier for the :importer importer in the Akeneo queue */ public function thereIsOneProductToImportWithIdentifierInTheAkeneoQueue(string $identifier, string $importer): void { - /** @var QueueItemInterface $queueItem */ - $queueItem = $this->queueItemFactory->createNew(); - $queueItem->setAkeneoEntity($importer); - $queueItem->setAkeneoIdentifier($identifier); - $queueItem->setCreatedAt(new \DateTime()); - $this->queueItemRepository->add($queueItem); - $this->sharedStorage->set('item', $queueItem); + $itemImport = new ItemImport($importer, $identifier); + $this->messageBus->dispatch($itemImport); + + $this->sharedStorage->set('item_import', $itemImport); } /** - * @Given /^there is one product associations to import with identifier "([^"]*)" in the Akeneo queue$/ + * @Given there is one product associations to import with identifier :identifier in the Akeneo queue */ public function thereIsOneProductAssociationsToImportWithIdentifierInTheAkeneoQueue(string $identifier): void { - /** @var QueueItemInterface $queueItem */ - $queueItem = $this->queueItemFactory->createNew(); - $queueItem->setAkeneoEntity('ProductAssociations'); - $queueItem->setAkeneoIdentifier($identifier); - $queueItem->setCreatedAt(new \DateTime()); - $this->queueItemRepository->add($queueItem); - $this->sharedStorage->set('item', $queueItem); + $itemImport = new ItemImport('ProductAssociations', $identifier); + $this->messageBus->dispatch($itemImport); + + $this->sharedStorage->set('item', $itemImport); } /** diff --git a/tests/Behat/Context/System/FilesystemContext.php b/tests/Behat/Context/System/FilesystemContext.php index 6c4d9bf4..e99137de 100644 --- a/tests/Behat/Context/System/FilesystemContext.php +++ b/tests/Behat/Context/System/FilesystemContext.php @@ -45,7 +45,7 @@ public function thereIsAFileWithNameThatContains(string $filename, string $conte } /** - * @Then /^there should not be any temporary file in the temporary files directory$/ + * @Then there should not be any temporary file in the temporary files directory */ public function thereShouldNotBeAnyTemporaryFileInTheTemporaryFilesDirectory(): void { diff --git a/tests/Behat/Context/Transform/QueueItemContext.php b/tests/Behat/Context/Transform/QueueItemContext.php index 5e844713..db3de0f5 100644 --- a/tests/Behat/Context/Transform/QueueItemContext.php +++ b/tests/Behat/Context/Transform/QueueItemContext.php @@ -11,7 +11,7 @@ final class QueueItemContext implements Context { - public function __construct(private QueueItemRepositoryInterface $queueItemRepository) + public function __construct() { } diff --git a/tests/Behat/Resources/services.xml b/tests/Behat/Resources/services.xml index f82272a9..7c8b81f5 100644 --- a/tests/Behat/Resources/services.xml +++ b/tests/Behat/Resources/services.xml @@ -5,36 +5,23 @@ - - - + - - - - - - - - - - - @@ -47,8 +34,9 @@ - - + + + diff --git a/tests/Behat/Resources/suites.yml b/tests/Behat/Resources/suites.yml index 43b9c88a..1612e85f 100644 --- a/tests/Behat/Resources/suites.yml +++ b/tests/Behat/Resources/suites.yml @@ -9,8 +9,6 @@ default: - sylius.behat.context.setup.locale - webgriffe_sylius_akeneo.behat.context.setup.queue - - webgriffe_sylius_akeneo.behat.context.cli.consume_command - - webgriffe_sylius_akeneo.behat.context.db.product - webgriffe_sylius_akeneo.behat.context.db.queue - webgriffe_sylius_akeneo.behat.context.system.filesystem @@ -27,8 +25,6 @@ default: - sylius.behat.context.setup.channel - webgriffe_sylius_akeneo.behat.context.setup.queue - - webgriffe_sylius_akeneo.behat.context.cli.consume_command - - webgriffe_sylius_akeneo.behat.context.db.product - webgriffe_sylius_akeneo.behat.context.db.queue @@ -144,16 +140,3 @@ default: filters: tags: "@managing_queue_items && @ui" - - cli_cleaning_queue: - contexts: - - sylius.behat.context.hook.doctrine_orm - - - sylius.behat.context.transform.shared_storage - - - webgriffe_sylius_akeneo.behat.context.setup.queue - - - webgriffe_sylius_akeneo.behat.context.cli.queue_cleanup_command - - filters: - tags: "@cleaning_queue && @cli" diff --git a/tests/Integration/TemporaryFilesManagerTest.php b/tests/Integration/TemporaryFilesManagerTest.php index a700ed26..89c56150 100644 --- a/tests/Integration/TemporaryFilesManagerTest.php +++ b/tests/Integration/TemporaryFilesManagerTest.php @@ -12,7 +12,7 @@ final class TemporaryFilesManagerTest extends TestCase { - private $temporaryFileManager; + private TemporaryFilesManager $temporaryFileManager; protected function setUp(): void { @@ -25,10 +25,8 @@ protected function setUp(): void ); } - /** - * @test - */ - public function it_generates_temporary_file_path() + /** @test */ + public function it_generates_temporary_file_path(): void { $this->assertMatchesRegularExpression( '|' . vfsStream::url('root') . '/akeneo-.*|', @@ -36,10 +34,8 @@ public function it_generates_temporary_file_path() ); } - /** - * @test - */ - public function it_deletes_all_temporary_files() + /** @test */ + public function it_deletes_all_temporary_files(): void { touch(vfsStream::url('root') . '/akeneo-temp1'); touch(vfsStream::url('root') . '/akeneo-temp2'); @@ -52,10 +48,8 @@ public function it_deletes_all_temporary_files() $this->assertFileDoesNotExist(vfsStream::url('root') . '/akeneo-temp3'); } - /** - * @test - */ - public function it_does_not_delete_not_managed_temporary_files() + /** @test */ + public function it_does_not_delete_not_managed_temporary_files(): void { touch(vfsStream::url('root') . '/not-managed-temp-file'); From c578af39320dc3991ec2fed143f3597ae127d6cf Mon Sep 17 00:00:00 2001 From: Lorenzo Ruozzi Date: Mon, 13 Jun 2022 12:25:30 +0200 Subject: [PATCH 03/24] Adjust behat (#123) --- features/enqueuing_generic_items.feature | 7 --- .../Context/Cli/EnqueueCommandContext.php | 5 +- .../Context/Messenger/MessengerContext.php | 58 +++++++++++++------ 3 files changed, 43 insertions(+), 27 deletions(-) diff --git a/features/enqueuing_generic_items.feature b/features/enqueuing_generic_items.feature index dc2fe730..cf2297b0 100644 --- a/features/enqueuing_generic_items.feature +++ b/features/enqueuing_generic_items.feature @@ -25,13 +25,6 @@ Feature: Enqueuing items When I enqueue items with since date specified from a not existent file Then I should be notified that the since date file does not exists - @cli - Scenario: Avoiding to enqueue two times the same item if it has not been imported yet - Given there is a product "product-1" updated at "2020-01-20 22:23:13" on Akeneo - And there is one item to import with identifier "product-1" for the "Product" importer in the Akeneo queue - When I enqueue items for all importers modified since date "2020-01-20 01:00:00" - Then there should be only one queue item with identifier "product-1" for the "Product" importer in the Akeneo queue - @cli Scenario: Enqueuing all items regardless last modified date Given there are 3 products on Akeneo diff --git a/tests/Behat/Context/Cli/EnqueueCommandContext.php b/tests/Behat/Context/Cli/EnqueueCommandContext.php index 58ea6a7b..2ac0fff5 100644 --- a/tests/Behat/Context/Cli/EnqueueCommandContext.php +++ b/tests/Behat/Context/Cli/EnqueueCommandContext.php @@ -5,6 +5,7 @@ namespace Tests\Webgriffe\SyliusAkeneoPlugin\Behat\Context\Cli; use Behat\Behat\Context\Context; +use DateTime; use org\bovigo\vfs\vfsStream; use Sylius\Behat\Service\SharedStorageInterface; use Symfony\Bundle\FrameworkBundle\Console\Application; @@ -26,12 +27,12 @@ public function __construct( /** * @When I enqueue items for all importers modified since date :date */ - public function iRunEnqueueCommandWithSinceDate(string $date): void + public function iRunEnqueueCommandWithSinceDate(DateTime $date): void { $commandTester = $this->getCommandTester(); try { - $commandTester->execute(['command' => 'webgriffe:akeneo:enqueue', '--since' => $date]); + $commandTester->execute(['command' => 'webgriffe:akeneo:enqueue', '--since' => $date->format('Y-m-d H:i:s')]); } catch (Throwable $t) { $this->sharedStorage->set('command_exception', $t); } diff --git a/tests/Behat/Context/Messenger/MessengerContext.php b/tests/Behat/Context/Messenger/MessengerContext.php index 63e420e0..80d75fef 100644 --- a/tests/Behat/Context/Messenger/MessengerContext.php +++ b/tests/Behat/Context/Messenger/MessengerContext.php @@ -6,6 +6,7 @@ use Behat\Behat\Context\Context; use InvalidArgumentException; +use Symfony\Component\Messenger\Envelope; use Symfony\Component\Messenger\Transport\InMemoryTransport; use Throwable; use Webgriffe\SyliusAkeneoPlugin\Message\ItemImport; @@ -32,42 +33,39 @@ public function theQueueItemHasAnErrorMessageContaining(string $identifier, stri } /** - * @Then /^the queue item with identifier "([^"]*)" for the "([^"]*)" importer should not be in the Akeneo queue$/ + * @Then the queue item with identifier :identifier for the :importer importer should not be in the Akeneo queue */ - public function theProductShouldNotBeInTheAkeneoQueue(string $identifier, string $importer): void + public function theQueueItemWithIdentifierForTheImporterShouldNotBeInTheAkeneoQueue(string $identifier, string $importer): void { - Assert::null( - $this->queueItemRepository->findOneBy(['akeneoEntity' => $importer, 'akeneoIdentifier' => $identifier]), + Assert::null($this->getEnvelopeByImporterAndIdentifier($importer, $identifier), ); } /** - * @Then /^the queue item with identifier "([^"]*)" for the "([^"]*)" importer should be in the Akeneo queue$/ + * @Then the queue item with identifier :identifier for the :importer importer should be in the Akeneo queue */ - public function theProductShouldBeInTheAkeneoQueue(string $identifier, string $importer): void + public function theQueueItemWithIdentifierForTheImporterShouldBeInTheAkeneoQueue(string $identifier, string $importer): void { Assert::isInstanceOf( - $this->queueItemRepository->findOneBy( - ['akeneoEntity' => $importer, 'akeneoIdentifier' => $identifier], - ), - QueueItemInterface::class, + $this->getEnvelopeByImporterAndIdentifier($importer, $identifier)->getMessage(), + ItemImport::class ); } /** - * @Then /^there should be no item in the queue for the "([^"]*)" importer/ + * @Then there should be no item in the queue for the :importer importer */ public function thereShouldBeNoProductInTheAkeneoQueue(string $importer): void { - Assert::isEmpty($this->queueItemRepository->findBy(['akeneoEntity' => $importer])); + Assert::isEmpty($this->getEnvelopesByImporter($importer)); } /** - * @Then /^there should be no item in the Akeneo queue$/ + * @Then there should be no item in the Akeneo queue */ public function thereShouldBeNoItemInTheAkeneoQueue(): void { - Assert::isEmpty($this->queueItemRepository->findAll()); + Assert::isEmpty($this->transport->get()); } /** @@ -86,17 +84,17 @@ public function thereShouldBeOnlyOneProductQueueItemForInTheAkeneoQueue(string $ */ public function thereShouldBeItemsForTheImporterInTheAkeneoQueue(int $count, string $importer): void { - $items = $this->queueItemRepository->findBy(['akeneoEntity' => $importer]); + $items = $this->getEnvelopesByImporter($importer); Assert::count($items, $count); } /** - * @Then /^there should be items for the "([^"]*)" importer only in the Akeneo queue$/ + * @Then there should be items for the :importer importer only in the Akeneo queue */ public function thereShouldBeItemsForTheImporterOnlyInTheAkeneoQueue(string $importer): void { - $importerItems = $this->queueItemRepository->findBy(['akeneoEntity' => $importer]); - Assert::count($this->queueItemRepository->findAll(), count($importerItems)); + $importerItems = $this->getEnvelopesByImporter($importer); + Assert::count($this->transport->get(), count($importerItems)); } private function getQueueItemByImporterAndIdentifier(string $importer, string $identifier): ItemImport @@ -143,4 +141,28 @@ public function theItemImportMessageForProductShouldHaveFailed(string $identifie }); Assert::count($failedMessages, 1); } + + private function getEnvelopesByImporter(string $importer): array + { + return array_filter($this->transport->get(), static function (Envelope $envelope) use ($importer): bool { + /** @var ItemImport|mixed $message */ + $message = $envelope->getMessage(); + return $message instanceof ItemImport && $message->getAkeneoEntity() === $importer; + }); + } + + private function getEnvelopeByImporterAndIdentifier(string $importer, string $identifier): ?Envelope + { + $envelopes = array_filter($this->transport->get(), static function (Envelope $envelope) use ($importer, $identifier): bool { + /** @var ItemImport|mixed $message */ + $message = $envelope->getMessage(); + return $message instanceof ItemImport && $message->getAkeneoEntity() === $importer && $message->getAkeneoIdentifier() === $identifier; + }); + $envelope = reset($envelopes); + if (!$envelope instanceof Envelope) { + return null; + } + + return $envelope; + } } From 4f742962ebf70c1a06ece8d56521041235caa431 Mon Sep 17 00:00:00 2001 From: Lorenzo Ruozzi Date: Mon, 13 Jun 2022 12:47:15 +0200 Subject: [PATCH 04/24] Fix CS (#123) --- tests/Behat/Context/Messenger/MessengerContext.php | 3 +++ tests/Behat/Context/Setup/QueueContext.php | 1 - tests/Behat/Context/Transform/QueueItemContext.php | 1 - 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/Behat/Context/Messenger/MessengerContext.php b/tests/Behat/Context/Messenger/MessengerContext.php index 80d75fef..4fdaf2c4 100644 --- a/tests/Behat/Context/Messenger/MessengerContext.php +++ b/tests/Behat/Context/Messenger/MessengerContext.php @@ -121,6 +121,7 @@ public function iConsumeTheMessages(): void if (!$message instanceof ItemImport) { continue; } + try { $this->itemImportHandler->__invoke($message); } catch (Throwable $throwable) { @@ -147,6 +148,7 @@ private function getEnvelopesByImporter(string $importer): array return array_filter($this->transport->get(), static function (Envelope $envelope) use ($importer): bool { /** @var ItemImport|mixed $message */ $message = $envelope->getMessage(); + return $message instanceof ItemImport && $message->getAkeneoEntity() === $importer; }); } @@ -156,6 +158,7 @@ private function getEnvelopeByImporterAndIdentifier(string $importer, string $id $envelopes = array_filter($this->transport->get(), static function (Envelope $envelope) use ($importer, $identifier): bool { /** @var ItemImport|mixed $message */ $message = $envelope->getMessage(); + return $message instanceof ItemImport && $message->getAkeneoEntity() === $importer && $message->getAkeneoIdentifier() === $identifier; }); $envelope = reset($envelopes); diff --git a/tests/Behat/Context/Setup/QueueContext.php b/tests/Behat/Context/Setup/QueueContext.php index adc3c7e4..9695973e 100644 --- a/tests/Behat/Context/Setup/QueueContext.php +++ b/tests/Behat/Context/Setup/QueueContext.php @@ -9,7 +9,6 @@ use Symfony\Component\Messenger\MessageBusInterface; use Webgriffe\SyliusAkeneoPlugin\Entity\QueueItemInterface; use Webgriffe\SyliusAkeneoPlugin\Message\ItemImport; -use Webgriffe\SyliusAkeneoPlugin\Repository\QueueItemRepositoryInterface; final class QueueContext implements Context { diff --git a/tests/Behat/Context/Transform/QueueItemContext.php b/tests/Behat/Context/Transform/QueueItemContext.php index db3de0f5..e0ab263e 100644 --- a/tests/Behat/Context/Transform/QueueItemContext.php +++ b/tests/Behat/Context/Transform/QueueItemContext.php @@ -6,7 +6,6 @@ use Behat\Behat\Context\Context; use Webgriffe\SyliusAkeneoPlugin\Entity\QueueItemInterface; -use Webgriffe\SyliusAkeneoPlugin\Repository\QueueItemRepositoryInterface; use Webmozart\Assert\Assert; final class QueueItemContext implements Context From a2fb3a4023ea427962caf714866dbc786a0b0ecc Mon Sep 17 00:00:00 2001 From: Lorenzo Ruozzi Date: Mon, 13 Jun 2022 14:13:15 +0200 Subject: [PATCH 05/24] Remove admin queue tests (#123) --- features/enqueuing_products.feature | 3 -- .../browsing_queue_items.feature | 44 ------------------- .../deleting_queue_items.feature | 29 ------------ src/Menu/AdminMenuListener.php | 25 ----------- src/Resources/config/services.xml | 4 -- .../Ui/Admin/ManagingProductsContext.php | 11 +++-- .../Context/Ui/Admin/ManagingQueueItems.php | 4 +- .../Behat/Page/Admin/QueueItem/IndexPage.php | 37 ---------------- .../Admin/QueueItem/IndexPageInterface.php | 16 ------- tests/Behat/Resources/services.xml | 6 --- 10 files changed, 6 insertions(+), 173 deletions(-) delete mode 100644 features/managing_queue_items/browsing_queue_items.feature delete mode 100644 features/managing_queue_items/deleting_queue_items.feature delete mode 100644 src/Menu/AdminMenuListener.php delete mode 100644 tests/Behat/Page/Admin/QueueItem/IndexPageInterface.php diff --git a/features/enqueuing_products.feature b/features/enqueuing_products.feature index 54fc55cf..e0af0939 100644 --- a/features/enqueuing_products.feature +++ b/features/enqueuing_products.feature @@ -40,7 +40,6 @@ Feature: Enqueuing products When I browse products And I schedule an Akeneo PIM import for the "Braided hat m" product Then I should be notified that it has been successfully enqueued - And I should see 1, not imported, item in the Akeneo queue items list @ui Scenario: Enqueuing a product already enqueued @@ -50,7 +49,6 @@ Feature: Enqueuing products When I browse products And I schedule an Akeneo PIM import for the "Braided hat l" product Then I should be notified that it has been already enqueued - And I should see 1, not imported, item in the Akeneo queue items list @ui Scenario: Enqueuing a configurable product @@ -61,4 +59,3 @@ Feature: Enqueuing products When I browse products And I schedule an Akeneo PIM import for the "Braided hat" product Then I should be notified that it has been successfully enqueued - And I should see 3, not imported, items in the Akeneo queue items list diff --git a/features/managing_queue_items/browsing_queue_items.feature b/features/managing_queue_items/browsing_queue_items.feature deleted file mode 100644 index e746e623..00000000 --- a/features/managing_queue_items/browsing_queue_items.feature +++ /dev/null @@ -1,44 +0,0 @@ -@managing_queue_items -Feature: Browsing queue items - In order to see the status of imported and not imported items from Akeneo - As an Administrator - I want to browse the Akeneo items queue - - Background: - Given I am logged in as an administrator - And there is a not imported item with identifier "braided-hat-m" for the "Product" importer in the Akeneo queue - And there is a not imported item with identifier "braided-hat-l" for the "ProductAssociations" importer in the Akeneo queue - And there is an already imported item with identifier "braided-hat-s" for the "Product" importer in the Akeneo queue - - @ui - Scenario: Browsing all items - When I browse Akeneo queue items - Then I should see 3 queue items in the list - - @ui - Scenario: Browsing not imported items - When I browse Akeneo queue items - And I choose "No" as an imported filter - And I filter - Then I should see 2, not imported, queue items in the list - - @ui - Scenario: Browsing imported items - When I browse Akeneo queue items - And I choose "Yes" as an imported filter - And I filter - Then I should see 1, imported, queue item in the list - - @ui - Scenario: Filtering items by importer - When I browse Akeneo queue items - And I specify "Associations" as an importer filter - And I filter - Then I should see 1 queue item in the list - - @ui - Scenario: Filtering items by identifier - When I browse Akeneo queue items - And I specify "hat-l" as an identifier filter - And I filter - Then I should see 1 queue item in the list diff --git a/features/managing_queue_items/deleting_queue_items.feature b/features/managing_queue_items/deleting_queue_items.feature deleted file mode 100644 index 6e04523e..00000000 --- a/features/managing_queue_items/deleting_queue_items.feature +++ /dev/null @@ -1,29 +0,0 @@ -@managing_queue_items -Feature: Deleting queue items - In order to keep the Akeneo import queue clean - As an Administrator - I want to delete queue items that I know that will never be imported - - Background: - Given I am logged in as an administrator - - @ui - Scenario: Deleting single queue item - Given there is a not imported item with identifier "braided-hat-m" for the "Product" importer in the Akeneo queue - And I browse Akeneo queue items - When I delete the "braided-hat-m" queue item - Then I should be notified that it has been successfully deleted - And this queue item should no longer exist in the queue - - @ui @javascript - Scenario: Deleting multiple queue items at once - Given there is a not imported item with identifier "braided-hat-l" for the "Product" importer in the Akeneo queue - And there is a not imported item with identifier "braided-hat-m" for the "Product" importer in the Akeneo queue - And there is a not imported item with identifier "braided-hat-s" for the "Product" importer in the Akeneo queue - When I browse Akeneo queue items - And I check the "braided-hat-m" queue item - And I check also the "braided-hat-s" queue item - And I delete them - Then I should be notified that they have been successfully deleted - And I should see a single queue item in the list - And I should see the "braided-hat-l" queue item in the list diff --git a/src/Menu/AdminMenuListener.php b/src/Menu/AdminMenuListener.php deleted file mode 100644 index 96c8814e..00000000 --- a/src/Menu/AdminMenuListener.php +++ /dev/null @@ -1,25 +0,0 @@ -getMenu(); - $catalogMenu = $menu->getChild('catalog'); - if ($catalogMenu === null) { - return; - } - - $catalogMenu - ->addChild('webgriffe_sylius_akeneo.queue_item', ['route' => 'webgriffe_sylius_akeneo_admin_queue_item_index']) - ->setLabel('webgriffe_sylius_akeneo.ui.queue_items') - ->setLabelAttribute('icon', 'cloud download') - ; - } -} diff --git a/src/Resources/config/services.xml b/src/Resources/config/services.xml index 26170ad8..78bb3020 100644 --- a/src/Resources/config/services.xml +++ b/src/Resources/config/services.xml @@ -69,10 +69,6 @@ - - - - diff --git a/tests/Behat/Context/Ui/Admin/ManagingProductsContext.php b/tests/Behat/Context/Ui/Admin/ManagingProductsContext.php index 75907d9a..9b6b6a5d 100644 --- a/tests/Behat/Context/Ui/Admin/ManagingProductsContext.php +++ b/tests/Behat/Context/Ui/Admin/ManagingProductsContext.php @@ -11,18 +11,17 @@ use Sylius\Behat\Service\Helper\JavaScriptTestHelperInterface; use Sylius\Behat\Service\NotificationCheckerInterface; use Sylius\Component\Core\Model\ProductInterface; -use Tests\Webgriffe\SyliusAkeneoPlugin\Behat\Page\Admin\QueueItem\IndexPageInterface as QueueItemsIndexPageInterface; use Webmozart\Assert\Assert; final class ManagingProductsContext implements Context { private const SCHEDULE_AKENEO_PIM_IMPORT = 'Schedule Akeneo PIM import'; - /** - * ProductItems constructor. - */ - public function __construct(private IndexPageInterface $indexPage, private JavaScriptTestHelperInterface $testHelper, private NotificationCheckerInterface $notificationChecker, private QueueItemsIndexPageInterface $queueItemsIndexPage) - { + public function __construct( + private IndexPageInterface $indexPage, + private JavaScriptTestHelperInterface $testHelper, + private NotificationCheckerInterface $notificationChecker + ) { } /** diff --git a/tests/Behat/Context/Ui/Admin/ManagingQueueItems.php b/tests/Behat/Context/Ui/Admin/ManagingQueueItems.php index 0ab7fb9f..0dae6bc3 100644 --- a/tests/Behat/Context/Ui/Admin/ManagingQueueItems.php +++ b/tests/Behat/Context/Ui/Admin/ManagingQueueItems.php @@ -6,13 +6,11 @@ use Behat\Behat\Context\Context; use Sylius\Behat\Service\SharedStorageInterface; -use Tests\Webgriffe\SyliusAkeneoPlugin\Behat\Page\Admin\QueueItem\IndexPageInterface; -use Webgriffe\SyliusAkeneoPlugin\Entity\QueueItemInterface; use Webmozart\Assert\Assert; final class ManagingQueueItems implements Context { - public function __construct(private IndexPageInterface $indexPage, private SharedStorageInterface $sharedStorage) + public function __construct(private SharedStorageInterface $sharedStorage) { } diff --git a/tests/Behat/Page/Admin/QueueItem/IndexPage.php b/tests/Behat/Page/Admin/QueueItem/IndexPage.php index 07d7e4f6..e69de29b 100644 --- a/tests/Behat/Page/Admin/QueueItem/IndexPage.php +++ b/tests/Behat/Page/Admin/QueueItem/IndexPage.php @@ -1,37 +0,0 @@ -getElement('filter_imported')->selectOption($imported); - } - - public function specifyImporterFilter(string $importer): void - { - $this->getElement('filter_importer')->setValue($importer); - } - - public function specifyIdentifierFilter(string $identifier): void - { - $this->getElement('filter_identifier')->setValue($identifier); - } - - protected function getDefinedElements(): array - { - return array_merge( - parent::getDefinedElements(), - [ - 'filter_imported' => '#criteria_imported', - 'filter_importer' => '#criteria_akeneoEntity_value', - 'filter_identifier' => '#criteria_akeneoIdentifier_value', - ], - ); - } -} diff --git a/tests/Behat/Page/Admin/QueueItem/IndexPageInterface.php b/tests/Behat/Page/Admin/QueueItem/IndexPageInterface.php deleted file mode 100644 index bb625e15..00000000 --- a/tests/Behat/Page/Admin/QueueItem/IndexPageInterface.php +++ /dev/null @@ -1,16 +0,0 @@ - - @@ -55,11 +54,6 @@ - - - - - webgriffe_sylius_akeneo_admin_queue_item_index From a0ea51ee593fc6039f74691d8654b6e06d3d7fcf Mon Sep 17 00:00:00 2001 From: Lorenzo Ruozzi Date: Mon, 13 Jun 2022 14:39:00 +0200 Subject: [PATCH 06/24] FIx spec (#123) --- spec/ValueHandler/FileAttributeValueHandlerSpec.php | 7 +++++-- spec/ValueHandler/ImageValueHandlerSpec.php | 6 +++++- src/ValueHandler/ImageValueHandler.php | 4 ++-- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/spec/ValueHandler/FileAttributeValueHandlerSpec.php b/spec/ValueHandler/FileAttributeValueHandlerSpec.php index b5925d1c..c52e68d6 100644 --- a/spec/ValueHandler/FileAttributeValueHandlerSpec.php +++ b/spec/ValueHandler/FileAttributeValueHandlerSpec.php @@ -17,6 +17,7 @@ use Sylius\Component\Core\Model\ProductInterface; use Sylius\Component\Core\Model\ProductVariantInterface; use Symfony\Component\Filesystem\Filesystem; +use Webgriffe\SyliusAkeneoPlugin\TemporaryFilesManagerInterface; use Webgriffe\SyliusAkeneoPlugin\ValueHandler\FileAttributeValueHandler; use Webgriffe\SyliusAkeneoPlugin\ValueHandlerInterface; use Webmozart\Assert\InvalidArgumentException; @@ -31,7 +32,8 @@ public function let( MediaFileApiInterface $productMediaFileApi, Filesystem $filesystem, ProductVariantInterface $productVariant, - ProductInterface $product + ProductInterface $product, + TemporaryFilesManagerInterface $temporaryFilesManager, ): void { $commerceChannel = new Channel(); $commerceChannel->setCode('ecommerce'); @@ -43,7 +45,8 @@ public function let( $apiClient->getProductMediaFileApi()->willReturn($productMediaFileApi); $productMediaFileApi->download(Argument::type('string'))->willReturn(new Response(200, [], '__FILE_CONTENT__')); $attributeApi->get('allegato_1')->willReturn(['type' => 'pim_catalog_file']); - $this->beConstructedWith($apiClient, $filesystem, 'allegato_1', 'public/media/attachment/product/'); + $temporaryFilesManager->generateTemporaryFilePath()->willReturn('tempfile'); + $this->beConstructedWith($apiClient, $filesystem, $temporaryFilesManager, 'allegato_1', 'public/media/attachment/product/'); } public function it_is_initializable(): void diff --git a/spec/ValueHandler/ImageValueHandlerSpec.php b/spec/ValueHandler/ImageValueHandlerSpec.php index 587556d8..437e0303 100644 --- a/spec/ValueHandler/ImageValueHandlerSpec.php +++ b/spec/ValueHandler/ImageValueHandlerSpec.php @@ -19,6 +19,7 @@ use Sylius\Component\Resource\Factory\FactoryInterface; use Sylius\Component\Resource\Repository\RepositoryInterface; use Webgriffe\SyliusAkeneoPlugin\ApiClientInterface; +use Webgriffe\SyliusAkeneoPlugin\TemporaryFilesManagerInterface; use Webgriffe\SyliusAkeneoPlugin\ValueHandler\ImageValueHandler; class ImageValueHandlerSpec extends ObjectBehavior @@ -45,7 +46,8 @@ public function let( ResponseInterface $downloadResponse, StreamInterface $responseBody, ProductVariantInterface $productVariant, - ProductInterface $product + ProductInterface $product, + TemporaryFilesManagerInterface $temporaryFilesManager, ): void { $productImageFactory->createNew()->willReturn($productImage); $apiClient->getProductMediaFileApi()->willReturn($productMediaFileApi); @@ -66,10 +68,12 @@ public function let( $productImageRepository ->findBy(['owner' => $product, 'type' => self::SYLIUS_IMAGE_TYPE]) ->willReturn(new ArrayCollection([])); + $temporaryFilesManager->generateTemporaryFilePath()->willReturn('tempfile'); $this->beConstructedWith( $productImageFactory, $productImageRepository, $apiClient, + $temporaryFilesManager, self::AKENEO_ATTRIBUTE_CODE, self::SYLIUS_IMAGE_TYPE ); diff --git a/src/ValueHandler/ImageValueHandler.php b/src/ValueHandler/ImageValueHandler.php index cb5e30f4..fc2c360b 100644 --- a/src/ValueHandler/ImageValueHandler.php +++ b/src/ValueHandler/ImageValueHandler.php @@ -15,7 +15,7 @@ use Sylius\Component\Resource\Repository\RepositoryInterface; use Symfony\Component\HttpFoundation\File\File; use Symfony\Component\HttpKernel\Exception\HttpException; -use Webgriffe\SyliusAkeneoPlugin\TemporaryFilesManager; +use Webgriffe\SyliusAkeneoPlugin\TemporaryFilesManagerInterface; use Webgriffe\SyliusAkeneoPlugin\ValueHandlerInterface; use Webmozart\Assert\Assert; @@ -25,7 +25,7 @@ public function __construct( private FactoryInterface $productImageFactory, private RepositoryInterface $productImageRepository, private AkeneoPimClientInterface $apiClient, - private TemporaryFilesManager $temporaryFilesManager, + private TemporaryFilesManagerInterface $temporaryFilesManager, private string $akeneoAttributeCode, private string $syliusImageType, ) { From e913581968f5363d3c72b21a8f4421f1a05b9f09 Mon Sep 17 00:00:00 2001 From: Lorenzo Ruozzi Date: Mon, 20 Jun 2022 12:42:17 +0200 Subject: [PATCH 07/24] Fix CS (#123) --- src/Command/EnqueueCommand.php | 2 +- src/Controller/ProductEnqueueController.php | 2 +- src/Message/ItemImport.php | 2 +- tests/Behat/Context/Cli/EnqueueCommandContext.php | 2 +- tests/Behat/Context/Cli/ReconcileCommandContext.php | 2 +- tests/Behat/Context/Messenger/MessengerContext.php | 5 +++-- tests/Behat/Context/Ui/Admin/ManagingProductsContext.php | 2 +- 7 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/Command/EnqueueCommand.php b/src/Command/EnqueueCommand.php index 7f74d82c..4f718d00 100644 --- a/src/Command/EnqueueCommand.php +++ b/src/Command/EnqueueCommand.php @@ -121,7 +121,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int foreach ($identifiers as $identifier) { $itemImport = new ItemImport( $importer->getAkeneoEntity(), - $identifier + $identifier, ); $this->messageBus->dispatch($itemImport); $output->writeln( diff --git a/src/Controller/ProductEnqueueController.php b/src/Controller/ProductEnqueueController.php index e4af37bd..14d4fdfa 100644 --- a/src/Controller/ProductEnqueueController.php +++ b/src/Controller/ProductEnqueueController.php @@ -38,7 +38,7 @@ public function enqueueAction(int $productId): Response $queueItem = new ItemImport( 'Product', - $productVariantCode + $productVariantCode, ); $this->messageBus->dispatch($queueItem); diff --git a/src/Message/ItemImport.php b/src/Message/ItemImport.php index d4ffacd5..aa19a29e 100644 --- a/src/Message/ItemImport.php +++ b/src/Message/ItemImport.php @@ -8,7 +8,7 @@ final class ItemImport { public function __construct( private string $akeneoEntity, - private string $akeneoIdentifier + private string $akeneoIdentifier, ) { } diff --git a/tests/Behat/Context/Cli/EnqueueCommandContext.php b/tests/Behat/Context/Cli/EnqueueCommandContext.php index 2ac0fff5..d0d37cad 100644 --- a/tests/Behat/Context/Cli/EnqueueCommandContext.php +++ b/tests/Behat/Context/Cli/EnqueueCommandContext.php @@ -20,7 +20,7 @@ final class EnqueueCommandContext implements Context public function __construct( private KernelInterface $kernel, private EnqueueCommand $enqueueCommand, - private SharedStorageInterface $sharedStorage + private SharedStorageInterface $sharedStorage, ) { } diff --git a/tests/Behat/Context/Cli/ReconcileCommandContext.php b/tests/Behat/Context/Cli/ReconcileCommandContext.php index a84d767e..fd50ef97 100644 --- a/tests/Behat/Context/Cli/ReconcileCommandContext.php +++ b/tests/Behat/Context/Cli/ReconcileCommandContext.php @@ -14,7 +14,7 @@ final class ReconcileCommandContext implements Context { public function __construct( private KernelInterface $kernel, - private ReconcileCommand $reconcileCommand + private ReconcileCommand $reconcileCommand, ) { } diff --git a/tests/Behat/Context/Messenger/MessengerContext.php b/tests/Behat/Context/Messenger/MessengerContext.php index 4fdaf2c4..7f0dfe94 100644 --- a/tests/Behat/Context/Messenger/MessengerContext.php +++ b/tests/Behat/Context/Messenger/MessengerContext.php @@ -37,7 +37,8 @@ public function theQueueItemHasAnErrorMessageContaining(string $identifier, stri */ public function theQueueItemWithIdentifierForTheImporterShouldNotBeInTheAkeneoQueue(string $identifier, string $importer): void { - Assert::null($this->getEnvelopeByImporterAndIdentifier($importer, $identifier), + Assert::null( + $this->getEnvelopeByImporterAndIdentifier($importer, $identifier), ); } @@ -48,7 +49,7 @@ public function theQueueItemWithIdentifierForTheImporterShouldBeInTheAkeneoQueue { Assert::isInstanceOf( $this->getEnvelopeByImporterAndIdentifier($importer, $identifier)->getMessage(), - ItemImport::class + ItemImport::class, ); } diff --git a/tests/Behat/Context/Ui/Admin/ManagingProductsContext.php b/tests/Behat/Context/Ui/Admin/ManagingProductsContext.php index 9b6b6a5d..4eddf42f 100644 --- a/tests/Behat/Context/Ui/Admin/ManagingProductsContext.php +++ b/tests/Behat/Context/Ui/Admin/ManagingProductsContext.php @@ -20,7 +20,7 @@ final class ManagingProductsContext implements Context public function __construct( private IndexPageInterface $indexPage, private JavaScriptTestHelperInterface $testHelper, - private NotificationCheckerInterface $notificationChecker + private NotificationCheckerInterface $notificationChecker, ) { } From 65e73bbfd333b10fe103b7a2120fe93f04d906b8 Mon Sep 17 00:00:00 2001 From: Lorenzo Ruozzi Date: Mon, 20 Jun 2022 12:56:33 +0200 Subject: [PATCH 08/24] Remove old services and fix PHPStan (#123) --- .../Context/Messenger/MessengerContext.php | 37 ++--- tests/Behat/Context/Setup/QueueContext.php | 34 ----- .../Context/Transform/QueueItemContext.php | 32 ---- .../Ui/Admin/ManagingProductsContext.php | 13 -- .../Context/Ui/Admin/ManagingQueueItems.php | 144 ------------------ tests/Behat/Resources/services.xml | 7 - tests/Behat/Resources/suites.yml | 2 - 7 files changed, 13 insertions(+), 256 deletions(-) delete mode 100644 tests/Behat/Context/Transform/QueueItemContext.php delete mode 100644 tests/Behat/Context/Ui/Admin/ManagingQueueItems.php diff --git a/tests/Behat/Context/Messenger/MessengerContext.php b/tests/Behat/Context/Messenger/MessengerContext.php index 7f0dfe94..ec2b8087 100644 --- a/tests/Behat/Context/Messenger/MessengerContext.php +++ b/tests/Behat/Context/Messenger/MessengerContext.php @@ -23,15 +23,6 @@ public function __construct( ) { } - /** - * @Given /^the queue item with identifier "([^"]*)" for the "([^"]*)" importer has an error message containing "([^"]*)"$/ - */ - public function theQueueItemHasAnErrorMessageContaining(string $identifier, string $importer, string $message): void - { - $queueItem = $this->getQueueItemByImporterAndIdentifier($importer, $identifier); - Assert::contains((string) $queueItem->getErrorMessage(), $message); - } - /** * @Then the queue item with identifier :identifier for the :importer importer should not be in the Akeneo queue */ @@ -47,8 +38,10 @@ public function theQueueItemWithIdentifierForTheImporterShouldNotBeInTheAkeneoQu */ public function theQueueItemWithIdentifierForTheImporterShouldBeInTheAkeneoQueue(string $identifier, string $importer): void { + $envelope = $this->getEnvelopeByImporterAndIdentifier($importer, $identifier); + Assert::notNull($envelope); Assert::isInstanceOf( - $this->getEnvelopeByImporterAndIdentifier($importer, $identifier)->getMessage(), + $envelope->getMessage(), ItemImport::class, ); } @@ -69,17 +62,6 @@ public function thereShouldBeNoItemInTheAkeneoQueue(): void Assert::isEmpty($this->transport->get()); } - /** - * @Then /^there should be only one queue item with identifier "([^"]*)" for the "([^"]*)" importer in the Akeneo queue$/ - */ - public function thereShouldBeOnlyOneProductQueueItemForInTheAkeneoQueue(string $identifier, string $importer): void - { - $items = $this->queueItemRepository->findBy( - ['akeneoEntity' => $importer, 'akeneoIdentifier' => $identifier], - ); - Assert::count($items, 1); - } - /** * @Then /^there should be (\d+) items for the "([^"]*)" importer in the Akeneo queue$/ */ @@ -94,8 +76,10 @@ public function thereShouldBeItemsForTheImporterInTheAkeneoQueue(int $count, str */ public function thereShouldBeItemsForTheImporterOnlyInTheAkeneoQueue(string $importer): void { + /** @var Envelope[] $envelopes */ + $envelopes = $this->transport->get(); $importerItems = $this->getEnvelopesByImporter($importer); - Assert::count($this->transport->get(), count($importerItems)); + Assert::count($envelopes, count($importerItems)); } private function getQueueItemByImporterAndIdentifier(string $importer, string $identifier): ItemImport @@ -146,7 +130,10 @@ public function theItemImportMessageForProductShouldHaveFailed(string $identifie private function getEnvelopesByImporter(string $importer): array { - return array_filter($this->transport->get(), static function (Envelope $envelope) use ($importer): bool { + /** @var Envelope[] $envelopes */ + $envelopes = $this->transport->get(); + + return array_filter($envelopes, static function (Envelope $envelope) use ($importer): bool { /** @var ItemImport|mixed $message */ $message = $envelope->getMessage(); @@ -156,7 +143,9 @@ private function getEnvelopesByImporter(string $importer): array private function getEnvelopeByImporterAndIdentifier(string $importer, string $identifier): ?Envelope { - $envelopes = array_filter($this->transport->get(), static function (Envelope $envelope) use ($importer, $identifier): bool { + /** @var Envelope[] $envelopes */ + $envelopes = $this->transport->get(); + $envelopes = array_filter($envelopes, static function (Envelope $envelope) use ($importer, $identifier): bool { /** @var ItemImport|mixed $message */ $message = $envelope->getMessage(); diff --git a/tests/Behat/Context/Setup/QueueContext.php b/tests/Behat/Context/Setup/QueueContext.php index 9695973e..43bb0442 100644 --- a/tests/Behat/Context/Setup/QueueContext.php +++ b/tests/Behat/Context/Setup/QueueContext.php @@ -7,7 +7,6 @@ use Behat\Behat\Context\Context; use Sylius\Behat\Service\SharedStorageInterface; use Symfony\Component\Messenger\MessageBusInterface; -use Webgriffe\SyliusAkeneoPlugin\Entity\QueueItemInterface; use Webgriffe\SyliusAkeneoPlugin\Message\ItemImport; final class QueueContext implements Context @@ -40,37 +39,4 @@ public function thereIsOneProductAssociationsToImportWithIdentifierInTheAkeneoQu $this->sharedStorage->set('item', $itemImport); } - - /** - * @Given /^there is an already imported item with identifier "([^"]*)" for the "([^"]*)" importer in the Akeneo queue$/ - */ - public function thereIsAnAlreadyImportedItemWithIdentifierForTheImporterInTheAkeneoQueue(string $identifier, string $importer): void - { - /** @var QueueItemInterface $queueItem */ - $queueItem = $this->queueItemFactory->createNew(); - $queueItem->setAkeneoEntity($importer); - $queueItem->setAkeneoIdentifier($identifier); - $queueItem->setCreatedAt(new \DateTime()); - $queueItem->setImportedAt(new \DateTime()); - $this->queueItemRepository->add($queueItem); - $this->sharedStorage->set('item', $queueItem); - } - - /** - * @Given /^(this item) has been imported (\d+) days ago$/ - */ - public function thisItemHasBeenImportedDaysAgo(QueueItemInterface $queueItem, int $days): void - { - $queueItem->setImportedAt(new \DateTime("$days days ago")); - $this->queueItemRepository->add($queueItem); - } - - /** - * @Given /^(this item) has been imported now$/ - */ - public function thisItemHasBeenImportedNow(QueueItemInterface $queueItem): void - { - $queueItem->setImportedAt(new \DateTime()); - $this->queueItemRepository->add($queueItem); - } } diff --git a/tests/Behat/Context/Transform/QueueItemContext.php b/tests/Behat/Context/Transform/QueueItemContext.php deleted file mode 100644 index e0ab263e..00000000 --- a/tests/Behat/Context/Transform/QueueItemContext.php +++ /dev/null @@ -1,32 +0,0 @@ -queueItemRepository->findBy(['akeneoIdentifier' => $akeneoIdentifier]); - - Assert::count( - $queueItems, - 1, - sprintf('%d queue items has been found with identifier "%s".', count($queueItems), $akeneoIdentifier), - ); - - return $queueItems[0]; - } -} diff --git a/tests/Behat/Context/Ui/Admin/ManagingProductsContext.php b/tests/Behat/Context/Ui/Admin/ManagingProductsContext.php index 4eddf42f..7ab56e73 100644 --- a/tests/Behat/Context/Ui/Admin/ManagingProductsContext.php +++ b/tests/Behat/Context/Ui/Admin/ManagingProductsContext.php @@ -11,7 +11,6 @@ use Sylius\Behat\Service\Helper\JavaScriptTestHelperInterface; use Sylius\Behat\Service\NotificationCheckerInterface; use Sylius\Component\Core\Model\ProductInterface; -use Webmozart\Assert\Assert; final class ManagingProductsContext implements Context { @@ -58,16 +57,4 @@ public function iShouldBeNotifiedThatItHasBeenAlreadyEnqueued(): void 'Akeneo PIM import for this product has been already scheduled before', ); } - - /** - * @Then /^I should see (\d+), not imported, items? in the Akeneo queue items list$/ - */ - public function iShouldSeeNotImportedItemInTheAkeneoQueueItemsList(int $numberOfItems): void - { - $this->queueItemsIndexPage->open(); - Assert::same($this->queueItemsIndexPage->countItems(), $numberOfItems); - foreach ($this->indexPage->getColumnFields('importedAt') as $columnField) { - Assert::eq($columnField, 'No'); - } - } } diff --git a/tests/Behat/Context/Ui/Admin/ManagingQueueItems.php b/tests/Behat/Context/Ui/Admin/ManagingQueueItems.php deleted file mode 100644 index 0dae6bc3..00000000 --- a/tests/Behat/Context/Ui/Admin/ManagingQueueItems.php +++ /dev/null @@ -1,144 +0,0 @@ -indexPage->open(); - } - - /** - * @Then /^I should see (\d+), not imported, queue items in the list$/ - */ - public function iShouldSeeQueueItemsInTheList(int $numberOfItems): void - { - Assert::same($this->indexPage->countItems(), $numberOfItems); - foreach ($this->indexPage->getColumnFields('importedAt') as $columnField) { - Assert::eq($columnField, 'No'); - } - } - - /** - * @Given /^I choose "([^"]*)" as an imported filter$/ - */ - public function iChooseAsAnImportedFilter(string $imported): void - { - $this->indexPage->chooseImportedFilter($imported); - } - - /** - * @When /^I filter$/ - */ - public function iFilter(): void - { - $this->indexPage->filter(); - } - - /** - * @Then /^I should see (\d+), imported, queue items? in the list$/ - */ - public function iShouldSeeImportedQueueItemInTheList(int $numberOfItems): void - { - Assert::same($this->indexPage->countItems(), $numberOfItems); - foreach ($this->indexPage->getColumnFields('importedAt') as $columnField) { - Assert::contains($columnField, 'Yes'); - } - } - - /** - * @When /^I specify "([^"]*)" as an importer filter$/ - */ - public function iSpecifyAsAnImporterFilter(string $importer): void - { - $this->indexPage->specifyImporterFilter($importer); - } - - /** - * @Then /^I should see (\d+) queue items? in the list$/ - */ - public function iShouldSeeQueueItemInTheList(int $numberOfItems): void - { - Assert::same($this->indexPage->countItems(), $numberOfItems); - } - - /** - * @Given /^I specify "([^"]*)" as an identifier filter$/ - */ - public function iSpecifyAsAnIdentifierFilter(string $identifier): void - { - $this->indexPage->specifyIdentifierFilter($identifier); - } - - /** - * @When /^I delete the ("([^"]*)" queue item)$/ - */ - public function iDeleteTheQueueItem(QueueItemInterface $queueItem): void - { - $this->indexPage->deleteResourceOnPage(['akeneoIdentifier' => $queueItem->getAkeneoIdentifier()]); - - $this->sharedStorage->set('queue_item', $queueItem); - } - - /** - * @Given /^(this queue item) should no longer exist in the queue$/ - */ - public function thisQueueItemShouldNoLongerExistInTheQueue(QueueItemInterface $queueItem): void - { - Assert::false( - $this->indexPage->isSingleResourceOnPage( - [ - 'akeneoIdentifier' => $queueItem->getAkeneoIdentifier(), - 'akeneoEntity' => $queueItem->getAkeneoEntity(), - ], - ), - ); - } - - /** - * @Given /^I check the ("([^"]*)" queue item)$/ - * @Given /^I check also the ("([^"]*)" queue item)$/ - */ - public function iCheckAlsoTheQueueItem(QueueItemInterface $queueItem): void - { - $this->indexPage->checkResourceOnPage(['akeneoIdentifier' => $queueItem->getAkeneoIdentifier()]); - } - - /** - * @When I delete them - */ - public function iDeleteThem(): void - { - $this->indexPage->bulkDelete(); - } - - /** - * @Given /^I should see a single queue item in the list$/ - */ - public function iShouldSeeASingleQueueItemInTheList(): void - { - Assert::eq($this->indexPage->countItems(), 1); - } - - /** - * @Given /^I should see the ("([^"]*)" queue item) in the list$/ - */ - public function iShouldSeeTheQueueItemInTheList(QueueItemInterface $queueItem): void - { - Assert::true($this->indexPage->isSingleResourceOnPage(['akeneoIdentifier' => $queueItem->getAkeneoIdentifier()])); - } -} diff --git a/tests/Behat/Resources/services.xml b/tests/Behat/Resources/services.xml index 652f6f46..34678174 100644 --- a/tests/Behat/Resources/services.xml +++ b/tests/Behat/Resources/services.xml @@ -4,9 +4,6 @@ - - - @@ -46,10 +43,6 @@ - - - - diff --git a/tests/Behat/Resources/suites.yml b/tests/Behat/Resources/suites.yml index 1612e85f..f6ffe52b 100644 --- a/tests/Behat/Resources/suites.yml +++ b/tests/Behat/Resources/suites.yml @@ -130,12 +130,10 @@ default: - sylius.behat.context.hook.doctrine_orm - sylius.behat.context.transform.shared_storage - - webgriffe_sylius_akeneo.behat.context.transform.queue_item - sylius.behat.context.setup.admin_security - webgriffe_sylius_akeneo.behat.context.setup.queue - - webgriffe_sylius_akeneo.behat.context.ui.admin.managing_queue_items - sylius.behat.context.ui.admin.notification filters: From 6004e7f52bdcca5922341a98b27f8cf4aa88fc57 Mon Sep 17 00:00:00 2001 From: Lorenzo Ruozzi Date: Fri, 1 Jul 2022 17:11:52 +0200 Subject: [PATCH 09/24] Fix behat (#123) --- features/enqueuing_products.feature | 2 +- .../Context/Ui/Admin/ManagingProductsContext.php | 12 ------------ tests/Behat/Resources/services.xml | 2 +- tests/Behat/Resources/suites.yml | 11 ++++++----- 4 files changed, 8 insertions(+), 19 deletions(-) diff --git a/features/enqueuing_products.feature b/features/enqueuing_products.feature index e0af0939..2c48e05a 100644 --- a/features/enqueuing_products.feature +++ b/features/enqueuing_products.feature @@ -48,7 +48,7 @@ Feature: Enqueuing products And there is one item to import with identifier "BRAIDED_HAT_L" for the "Product" importer in the Akeneo queue When I browse products And I schedule an Akeneo PIM import for the "Braided hat l" product - Then I should be notified that it has been already enqueued + Then I should be notified that it has been successfully enqueued @ui Scenario: Enqueuing a configurable product diff --git a/tests/Behat/Context/Ui/Admin/ManagingProductsContext.php b/tests/Behat/Context/Ui/Admin/ManagingProductsContext.php index 7ab56e73..cf780a1d 100644 --- a/tests/Behat/Context/Ui/Admin/ManagingProductsContext.php +++ b/tests/Behat/Context/Ui/Admin/ManagingProductsContext.php @@ -45,16 +45,4 @@ public function iShouldBeNotifiedThatItHasBeenSuccessfullyEnqueued(): void 'Akeneo PIM product import has been successfully scheduled', ); } - - /** - * @Given /^I should be notified that it has been already enqueued$/ - */ - public function iShouldBeNotifiedThatItHasBeenAlreadyEnqueued(): void - { - $this->testHelper->waitUntilNotificationPopups( - $this->notificationChecker, - NotificationType::success(), - 'Akeneo PIM import for this product has been already scheduled before', - ); - } } diff --git a/tests/Behat/Resources/services.xml b/tests/Behat/Resources/services.xml index 34678174..6cc20b4c 100644 --- a/tests/Behat/Resources/services.xml +++ b/tests/Behat/Resources/services.xml @@ -31,7 +31,7 @@ - + diff --git a/tests/Behat/Resources/suites.yml b/tests/Behat/Resources/suites.yml index f6ffe52b..31c34198 100644 --- a/tests/Behat/Resources/suites.yml +++ b/tests/Behat/Resources/suites.yml @@ -10,7 +10,7 @@ default: - webgriffe_sylius_akeneo.behat.context.setup.queue - webgriffe_sylius_akeneo.behat.context.db.product - - webgriffe_sylius_akeneo.behat.context.db.queue + - webgriffe_sylius_akeneo.behat.context.messenger - webgriffe_sylius_akeneo.behat.context.system.filesystem filters: @@ -26,7 +26,7 @@ default: - webgriffe_sylius_akeneo.behat.context.setup.queue - webgriffe_sylius_akeneo.behat.context.db.product - - webgriffe_sylius_akeneo.behat.context.db.queue + - webgriffe_sylius_akeneo.behat.context.messenger filters: tags: "@importing_product_associations && @cli" @@ -42,7 +42,7 @@ default: - webgriffe_sylius_akeneo.behat.context.cli.enqueue_command - - webgriffe_sylius_akeneo.behat.context.db.queue + - webgriffe_sylius_akeneo.behat.context.messenger - webgriffe_sylius_akeneo.behat.context.system.filesystem - webgriffe_sylius_akeneo.behat.context.system.datetime @@ -61,7 +61,7 @@ default: - webgriffe_sylius_akeneo.behat.context.cli.enqueue_command - - webgriffe_sylius_akeneo.behat.context.db.queue + - webgriffe_sylius_akeneo.behat.context.messenger - webgriffe_sylius_akeneo.behat.context.system.filesystem - webgriffe_sylius_akeneo.behat.context.system.datetime @@ -82,6 +82,7 @@ default: - webgriffe_sylius_akeneo.behat.context.setup.queue - sylius.behat.context.ui.admin.managing_products + - webgriffe_sylius_akeneo.behat.context.messenger - webgriffe_sylius_akeneo.behat.context.ui.admin.managing_products filters: tags: "@enqueuing_products && @ui" @@ -97,7 +98,7 @@ default: - webgriffe_sylius_akeneo.behat.context.cli.enqueue_command - - webgriffe_sylius_akeneo.behat.context.db.queue + - webgriffe_sylius_akeneo.behat.context.messenger - webgriffe_sylius_akeneo.behat.context.system.filesystem - webgriffe_sylius_akeneo.behat.context.system.datetime From f37cb3a09f9773fcad553e71fc2addcd09e1b9f5 Mon Sep 17 00:00:00 2001 From: Lorenzo Ruozzi Date: Mon, 1 Aug 2022 10:50:53 +0200 Subject: [PATCH 10/24] Rename enqueue command into import command (#123) --- README.md | 34 +++++------ UPGRADE-2.0.md | 2 +- features/enqueuing_generic_items.feature | 16 +++--- features/enqueuing_products.feature | 6 +- .../enqueuing_products_associations.feature | 2 +- ...ng_product_associations_from_queue.feature | 2 +- .../importing_products_from_queue.feature | 8 +-- .../{EnqueueCommand.php => ImportCommand.php} | 12 ++-- ...roller.php => ProductImportController.php} | 4 +- src/Resources/config/admin_routing.yaml | 6 +- src/Resources/config/config.yaml | 10 ++-- src/Resources/config/services.xml | 4 +- src/Resources/translations/messages.en.yaml | 2 +- src/Resources/translations/messages.it.yaml | 2 +- .../{enqueue.html.twig => import.html.twig} | 0 ...ndContext.php => ImportCommandContext.php} | 56 +++++++++---------- .../Context/Messenger/MessengerContext.php | 4 +- tests/Behat/Resources/services.xml | 4 +- tests/Behat/Resources/suites.yml | 6 +- 19 files changed, 90 insertions(+), 90 deletions(-) rename src/Command/{EnqueueCommand.php => ImportCommand.php} (94%) rename src/Controller/{ProductEnqueueController.php => ProductImportController.php} (93%) rename src/Resources/views/Product/Grid/Action/{enqueue.html.twig => import.html.twig} (100%) rename tests/Behat/Context/Cli/{EnqueueCommandContext.php => ImportCommandContext.php} (71%) diff --git a/README.md b/README.md index 889fd072..a482a981 100644 --- a/README.md +++ b/README.md @@ -379,7 +379,7 @@ This plugin will also import product associations. It's a zero configuration imp ### Launch data import from CLI -To actually import data you must first create queue items with the **enqueue command** and then you can import them with the **consume command**. +To actually import data you must first create queue items with the **import command** and then you can import them with the **consume command**. #### Schedule Akeneo PIM import button @@ -387,41 +387,41 @@ This button allows you to queue a product directly from the admin index page. By ![Schedule Akeneo PIM import button](schedule-akeneo-import-button.png) -#### Enqueue command +#### Import command -To create queue items you can use the `webgriffe:akeneo:enqueue` console command: +To create queue items you can use the `webgriffe:akeneo:import` console command: ```bash -bin/console webgriffe:akeneo:enqueue --since="2020-01-30" +bin/console webgriffe:akeneo:import --since="2020-01-30" ``` -This will enqueue all Akeneo entities updated after the provided date. +This will import all Akeneo entities updated after the provided date. You can also use a "since file" where to read the since date: ```bash echo "2020-01-30" > var/storage/akeneo-sincefile.txt -bin/console webgriffe:akeneo:enqueue --since-file="var/storage/akeneo-sincefile.txt" +bin/console webgriffe:akeneo:import --since-file="var/storage/akeneo-sincefile.txt" ``` -When run with the since file, the enqueue command will write the current date/time to the since file after the enqueueing process is terminated. This is useful when you put the enqueue command in cron: +When run with the since file, the import command will write the current date/time to the since file after the importing process is terminated. This is useful when you put the import command in cron: ``` -* * * * * /usr/bin/php /path/to/sylius/bin/console -e prod -q webgriffe:akeneo:enqueue --since-file=/path/to/sylius/var/storage/akeneo-enqueue-sincefile.txt +* * * * * /usr/bin/php /path/to/sylius/bin/console -e prod -q webgriffe:akeneo:import --since-file=/path/to/sylius/var/storage/akeneo-import-sincefile.txt ``` -This way the enqueue command is run repeatedly enqueuing only producs modified since the last command execution. +This way the import command is run repeatedly importing only products modified since the last command execution. -You can also enqueue items only for specific importers: +You can also import items only for specific importers: ```bash -bin/console webgriffe:akeneno:enqueue --importer="Product" --importer="MyImporter" --since="2020-01-30" +bin/console webgriffe:akeneno:import --importer="Product" --importer="MyImporter" --since="2020-01-30" ``` -You can also enqueue items regardless of their last update date: +You can also import items regardless of their last update date: ```bash -bin/console webgriffe:akeneno:enqueue --all +bin/console webgriffe:akeneno:import --all ``` #### Consume command @@ -474,20 +474,20 @@ It could be useful to add also this command to your scheduler to run automatical To make all importers and other plugin features work automatically the following is the suggested crontab: ``` -0 * * * * /path/to/sylius/bin/console -e prod -q webgriffe:akeneo:enqueue --all --importer="AttributeOptions" -* * * * * /path/to/sylius/bin/console -e prod -q webgriffe:akeneo:enqueue --since-file=/path/to/sylius/var/storage/akeneo-enqueue-sincefile.txt --importer="Product" --importer="ProductAssociations" +0 * * * * /path/to/sylius/bin/console -e prod -q webgriffe:akeneo:import --all --importer="AttributeOptions" +* * * * * /path/to/sylius/bin/console -e prod -q webgriffe:akeneo:import --since-file=/path/to/sylius/var/storage/akeneo-import-sincefile.txt --importer="Product" --importer="ProductAssociations" * * * * * /path/to/sylius/bin/console -e prod -q webgriffe:akeneo:consume 0 0 * * * /path/to/sylius/bin/console -e prod -q webgriffe:akeneo:cleanup-queue 0 */6 * * * /path/to/sylius/bin/console -e prod -q webgriffe:akeneo:reconcile ``` This will: -* Enqueue the update of all attribute options every hour +* Import the update of all attribute options every hour * Import, every minute, all products that have been modified since the last execution, along with their associations * Clean the imported items queue older than 10 days, every day at midnight * Reconcile Akeneo deleted products every 6 hours -Enqueue, Consume and Reconcile commands uses a [lock mechanism](https://symfony.com/doc/current/console/lockable_trait.html) which prevents running them if another instance of the same command is already running. +Import, Consume and Reconcile commands uses a [lock mechanism](https://symfony.com/doc/current/console/lockable_trait.html) which prevents running them if another instance of the same command is already running. ## Architecture & customization diff --git a/UPGRADE-2.0.md b/UPGRADE-2.0.md index 8461cdd6..35dce359 100644 --- a/UPGRADE-2.0.md +++ b/UPGRADE-2.0.md @@ -64,7 +64,7 @@ Removed all deprecations of the v1.x releases. ##### Removed - [BC] Property Webgriffe\SyliusAkeneoPlugin\DependencyInjection\WebgriffeSyliusAkeneoExtension::$valueHandlersTypesDefinitions was removed - [BC] Removed the service `webgriffe_sylius_akeneo_plugin.repository.cleanable_queue_item`, use the `webgriffe_sylius_akeneo.repository.cleanable_queue_item` instead. - - [BC] Removed the service `webgriffe_sylius_akeneo_plugin.controller.product_enqueue_controller`, use the `webgriffe_sylius_akeneo.controller.product_enqueue_controller` instead. + - [BC] Removed the service `webgriffe_sylius_akeneo_plugin.controller.product_enqueue_controller`, use the `webgriffe_sylius_akeneo.controller.product_import_controller` instead. - [BC] Removed the resource `webgriffe_sylius_akeneo_plugin.queue_item` use the `webgriffe_sylius_akeneo.queue_item` instead. ### Test changes diff --git a/features/enqueuing_generic_items.feature b/features/enqueuing_generic_items.feature index cf2297b0..6713e3cc 100644 --- a/features/enqueuing_generic_items.feature +++ b/features/enqueuing_generic_items.feature @@ -2,44 +2,44 @@ Feature: Enqueuing items In order to import data from Akeneo As a Store Owner - I want to add enqueue items to the Akeneo PIM queue + I want to import items from the Akeneo PIM @cli Scenario: Enqueueing items when no item is modified since the given date - When I enqueue items for all importers modified since date "2020-01-20 01:00:00" + When I import items for all importers modified since date "2020-01-20 01:00:00" Then there should be no item in the Akeneo queue @cli Scenario: Enqueueing items without a since date - When I enqueue items for all importers with no since date + When I import items for all importers with no since date Then I should be notified that a since date is required And there should be no item in the Akeneo queue @cli Scenario: Enqueueing items with an invalid since date - When I enqueue items for all importers with invalid since date + When I import items for all importers with invalid since date Then I should be notified that the since date must be a valid date @cli Scenario: Enqueueing items with a since date specified from a not existent file - When I enqueue items with since date specified from a not existent file + When I import items with since date specified from a not existent file Then I should be notified that the since date file does not exists @cli Scenario: Enqueuing all items regardless last modified date Given there are 3 products on Akeneo - When I enqueue all items for all importers + When I import all items for all importers Then there should be 3 items for the "Product" importer in the Akeneo queue And there should be 3 items for the "ProductAssociations" importer in the Akeneo queue @cli Scenario: Enqueuing all items for one importer only Given there are 3 products on Akeneo - When I enqueue all items for the "Product" importer + When I import all items for the "Product" importer Then there should be 3 items for the "Product" importer in the Akeneo queue And there should be items for the "Product" importer only in the Akeneo queue @cli Scenario: Enqueuing all items for a not existent importer - When I enqueue all items for a not existent importer + When I import all items for a not existent importer Then I should be notified that the importer does not exists diff --git a/features/enqueuing_products.feature b/features/enqueuing_products.feature index 2c48e05a..8f7718ad 100644 --- a/features/enqueuing_products.feature +++ b/features/enqueuing_products.feature @@ -9,7 +9,7 @@ Feature: Enqueuing products Given there is a product "product-1" updated at "2020-01-10 22:23:13" on Akeneo And there is a product "product-2" updated at "2020-01-21 09:54:12" on Akeneo And there is a product "product-3" updated at "2020-01-22 08:15:08" on Akeneo - When I enqueue items for all importers modified since date "2020-01-20 01:00:00" + When I import items for all importers modified since date "2020-01-20 01:00:00" Then the queue item with identifier "product-1" for the "Product" importer should not be in the Akeneo queue And the queue item with identifier "product-2" for the "Product" importer should be in the Akeneo queue And the queue item with identifier "product-3" for the "Product" importer should be in the Akeneo queue @@ -18,7 +18,7 @@ Feature: Enqueuing products Scenario: There are no products modified since datetime read in file Given there is a file with name "last-date" and content "2020-01-20 01:00:00" And current date time is "2020-01-25T12:00:00+01:00" - When I enqueue items for all importers modified since date specified from file "last-date" + When I import items for all importers modified since date specified from file "last-date" Then there should be no item in the queue for the "Product" importer And there is a file with name "last-date" that contains "2020-01-25T12:00:00+01:00" @@ -28,7 +28,7 @@ Feature: Enqueuing products And there is a product "product-2" updated at "2020-01-21 09:54:12" on Akeneo And there is a file with name "last-date" and content "2020-01-20 01:00:00" And current date time is "2020-01-25T12:00:00+01:00" - When I enqueue items for all importers modified since date specified from file "last-date" + When I import items for all importers modified since date specified from file "last-date" Then the queue item with identifier "product-1" for the "Product" importer should not be in the Akeneo queue And the queue item with identifier "product-2" for the "Product" importer should be in the Akeneo queue And there is a file with name "last-date" that contains "2020-01-25T12:00:00+01:00" diff --git a/features/enqueuing_products_associations.feature b/features/enqueuing_products_associations.feature index f0367f1b..df84f23d 100644 --- a/features/enqueuing_products_associations.feature +++ b/features/enqueuing_products_associations.feature @@ -9,7 +9,7 @@ Feature: Enqueuing products associations Given there is a product "product-1" updated at "2020-01-10 22:23:13" on Akeneo And there is a product "product-2" updated at "2020-01-21 09:54:12" on Akeneo And there is a product "product-3" updated at "2020-01-22 08:15:08" on Akeneo - When I enqueue items for all importers modified since date "2020-01-20 01:00:00" + When I import items for all importers modified since date "2020-01-20 01:00:00" Then the queue item with identifier "product-1" for the "ProductAssociations" importer should not be in the Akeneo queue And the queue item with identifier "product-2" for the "ProductAssociations" importer should be in the Akeneo queue And the queue item with identifier "product-3" for the "ProductAssociations" importer should be in the Akeneo queue diff --git a/features/importing_product_associations_from_queue.feature b/features/importing_product_associations_from_queue.feature index c91b5eca..02907198 100644 --- a/features/importing_product_associations_from_queue.feature +++ b/features/importing_product_associations_from_queue.feature @@ -12,6 +12,6 @@ Feature: Importing product associations from queue And the store has a product "upsell-product-2" with code "upsell-product-2" And there is one product associations to import with identifier "10627329" in the Akeneo queue And the store has a product association type "Upsell" with a code "UPSELL" - When I consume the messages + When I import all from Akeneo Then the product "10627329" should be associated to product "upsell-product-1" for association with code "UPSELL" And the product "10627329" should be associated to product "upsell-product-2" for association with code "UPSELL" diff --git a/features/importing_products_from_queue.feature b/features/importing_products_from_queue.feature index 3eb3fbc8..e3986b09 100644 --- a/features/importing_products_from_queue.feature +++ b/features/importing_products_from_queue.feature @@ -10,7 +10,7 @@ Feature: Importing products from queue And the store is also available in "it_IT" And there is one item to import with identifier "braided-hat-m" for the "Product" importer in the Akeneo queue And there is one item to import with identifier "braided-hat-l" for the "Product" importer in the Akeneo queue - When I consume the messages + When I import all from Akeneo Then the product "model-braided-hat" should exists with the right data And the product variant "braided-hat-m" of product "model-braided-hat" should exists with the right data And the product variant "braided-hat-l" of product "model-braided-hat" should exists with the right data @@ -20,7 +20,7 @@ Feature: Importing products from queue Given the store operates on a single channel And the store is also available in "it_IT" And there is one item to import with identifier "NOT_EXISTS" for the "Product" importer in the Akeneo queue - When I consume the messages + When I import all from Akeneo Then the item import message for "NOT_EXISTS" identifier and the "Product" importer should have failed And the product "NOT_EXISTS" should not exists @@ -30,7 +30,7 @@ Feature: Importing products from queue And the store is also available in "it_IT" And there is one item to import with identifier "NOT_EXISTS" for the "Product" importer in the Akeneo queue And there is one item to import with identifier "braided-hat-m" for the "Product" importer in the Akeneo queue - When I consume the messages + When I import all from Akeneo Then the item import message for "NOT_EXISTS" identifier and the "Product" importer should have failed And the product "NOT_EXISTS" should not exists And the product variant "braided-hat-m" of product "model-braided-hat" should exists with the right data @@ -41,5 +41,5 @@ Feature: Importing products from queue And the store is also available in "it_IT" And there is one item to import with identifier "braided-hat-m" for the "Product" importer in the Akeneo queue And there is one item to import with identifier "braided-hat-l" for the "Product" importer in the Akeneo queue - When I consume the messages + When I import all from Akeneo Then there should not be any temporary file in the temporary files directory diff --git a/src/Command/EnqueueCommand.php b/src/Command/ImportCommand.php similarity index 94% rename from src/Command/EnqueueCommand.php rename to src/Command/ImportCommand.php index 4f718d00..792f220a 100644 --- a/src/Command/EnqueueCommand.php +++ b/src/Command/ImportCommand.php @@ -19,7 +19,7 @@ use Webgriffe\SyliusAkeneoPlugin\Message\ItemImport; use Webmozart\Assert\Assert; -final class EnqueueCommand extends Command +final class ImportCommand extends Command { use LockableTrait; @@ -31,7 +31,7 @@ final class EnqueueCommand extends Command private const IMPORTER_OPTION_NAME = 'importer'; - protected static $defaultName = 'webgriffe:akeneo:enqueue'; + protected static $defaultName = 'webgriffe:akeneo:import'; public function __construct( private DateTimeBuilderInterface $dateTimeBuilder, @@ -44,7 +44,7 @@ public function __construct( protected function configure(): void { $this->setDescription( - 'Populate the Queue with Akeneo\'s entities that has been modified since a specified date/datetime', + 'Import Akeneo\'s entities that has been modified since a specified date/datetime', ); $this->addOption( self::SINCE_OPTION_NAME, @@ -62,13 +62,13 @@ protected function configure(): void self::ALL_OPTION_NAME, 'a', InputOption::VALUE_NONE, - 'Enqueue all identifiers regardless their last modified date.', + 'Import all identifiers regardless their last modified date.', ); $this->addOption( self::IMPORTER_OPTION_NAME, 'i', InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, - 'Enqueue items only for specified importers', + 'Import items only for specified importers', ); } @@ -126,7 +126,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $this->messageBus->dispatch($itemImport); $output->writeln( sprintf( - '%s entity with identifier %s enqueued.', + '%s entity with identifier %s imported.', $importer->getAkeneoEntity(), $identifier, ), diff --git a/src/Controller/ProductEnqueueController.php b/src/Controller/ProductImportController.php similarity index 93% rename from src/Controller/ProductEnqueueController.php rename to src/Controller/ProductImportController.php index 14d4fdfa..057f416b 100644 --- a/src/Controller/ProductEnqueueController.php +++ b/src/Controller/ProductImportController.php @@ -14,7 +14,7 @@ use Webgriffe\SyliusAkeneoPlugin\Message\ItemImport; use Webmozart\Assert\Assert; -final class ProductEnqueueController extends AbstractController +final class ProductImportController extends AbstractController { public function __construct( private ProductRepositoryInterface $productRepository, @@ -23,7 +23,7 @@ public function __construct( ) { } - public function enqueueAction(int $productId): Response + public function importAction(int $productId): Response { /** @var ProductInterface|null $product */ $product = $this->productRepository->find($productId); diff --git a/src/Resources/config/admin_routing.yaml b/src/Resources/config/admin_routing.yaml index e19d9d1c..0e481145 100644 --- a/src/Resources/config/admin_routing.yaml +++ b/src/Resources/config/admin_routing.yaml @@ -1,3 +1,3 @@ -webgriffe_sylius_akeneo_product_enqueue: - controller: webgriffe_sylius_akeneo.controller.product_enqueue_controller:enqueueAction - path: product/{productId}/enqueue +webgriffe_sylius_akeneo_product_import: + controller: webgriffe_sylius_akeneo.controller.product_import_controller:importAction + path: product/{productId}/import diff --git a/src/Resources/config/config.yaml b/src/Resources/config/config.yaml index 55a65871..095b05c4 100644 --- a/src/Resources/config/config.yaml +++ b/src/Resources/config/config.yaml @@ -1,7 +1,7 @@ sylius_grid: templates: action: - enqueueProduct: '@WebgriffeSyliusAkeneoPlugin\Product\Grid\Action\enqueue.html.twig' + importProduct: '@WebgriffeSyliusAkeneoPlugin\Product\Grid\Action\import.html.twig' grids: webgriffe_sylius_akeneo_admin_queue_item: driver: @@ -59,12 +59,12 @@ sylius_grid: sylius_admin_product: actions: item: - enqueue: - type: enqueueProduct - label: webgriffe_sylius_akeneo.ui.enqueue + import: + type: importProduct + label: webgriffe_sylius_akeneo.ui.import options: link: - route: webgriffe_sylius_akeneo_product_enqueue + route: webgriffe_sylius_akeneo_product_import parameters: productId: resource.id icon: cloud download diff --git a/src/Resources/config/services.xml b/src/Resources/config/services.xml index 78bb3020..a9436850 100644 --- a/src/Resources/config/services.xml +++ b/src/Resources/config/services.xml @@ -7,7 +7,7 @@ - + @@ -46,7 +46,7 @@ - + diff --git a/src/Resources/translations/messages.en.yaml b/src/Resources/translations/messages.en.yaml index 01e1ad25..22a20d40 100644 --- a/src/Resources/translations/messages.en.yaml +++ b/src/Resources/translations/messages.en.yaml @@ -8,7 +8,7 @@ webgriffe_sylius_akeneo: imported_yes: Yes, at %date% imported_no: No error_message: Error message - enqueue: Schedule Akeneo PIM import + import: Schedule Akeneo PIM import enqueued_success: Akeneo PIM import for product "{code}" has been successfully scheduled product_not_exist: Product not found product_already_enqueued: Akeneo PIM import for product "{code}" has been already scheduled before diff --git a/src/Resources/translations/messages.it.yaml b/src/Resources/translations/messages.it.yaml index 2df00806..4c2d9ee6 100644 --- a/src/Resources/translations/messages.it.yaml +++ b/src/Resources/translations/messages.it.yaml @@ -8,7 +8,7 @@ webgriffe_sylius_akeneo: imported_yes: Si, il %date% imported_no: No error_message: Messaggio d'errore - enqueue: Programma importazione da Akeneo PIM + import: Programma importazione da Akeneo PIM enqueued_success: L'importazione del prodotto "{code}" da Akeneo PIM è stata correttamente programmata product_not_exist: Prodotto non trovato product_already_enqueued: L'importazione del prodotto "{code}" da Akeneo PIM è già stata programmata precedentemente diff --git a/src/Resources/views/Product/Grid/Action/enqueue.html.twig b/src/Resources/views/Product/Grid/Action/import.html.twig similarity index 100% rename from src/Resources/views/Product/Grid/Action/enqueue.html.twig rename to src/Resources/views/Product/Grid/Action/import.html.twig diff --git a/tests/Behat/Context/Cli/EnqueueCommandContext.php b/tests/Behat/Context/Cli/ImportCommandContext.php similarity index 71% rename from tests/Behat/Context/Cli/EnqueueCommandContext.php rename to tests/Behat/Context/Cli/ImportCommandContext.php index d0d37cad..9462c96b 100644 --- a/tests/Behat/Context/Cli/EnqueueCommandContext.php +++ b/tests/Behat/Context/Cli/ImportCommandContext.php @@ -12,41 +12,41 @@ use Symfony\Component\Console\Tester\CommandTester; use Symfony\Component\HttpKernel\KernelInterface; use Throwable; -use Webgriffe\SyliusAkeneoPlugin\Command\EnqueueCommand; +use Webgriffe\SyliusAkeneoPlugin\Command\ImportCommand; use Webmozart\Assert\Assert; -final class EnqueueCommandContext implements Context +final class ImportCommandContext implements Context { public function __construct( private KernelInterface $kernel, - private EnqueueCommand $enqueueCommand, + private ImportCommand $enqueueCommand, private SharedStorageInterface $sharedStorage, ) { } /** - * @When I enqueue items for all importers modified since date :date + * @When I import items for all importers modified since date :date */ - public function iRunEnqueueCommandWithSinceDate(DateTime $date): void + public function iImportItemsForAllImportersModifiedSinceDate(DateTime $date): void { $commandTester = $this->getCommandTester(); try { - $commandTester->execute(['command' => 'webgriffe:akeneo:enqueue', '--since' => $date->format('Y-m-d H:i:s')]); + $commandTester->execute(['command' => 'webgriffe:akeneo:import', '--since' => $date->format('Y-m-d H:i:s')]); } catch (Throwable $t) { $this->sharedStorage->set('command_exception', $t); } } /** - * @When I enqueue items for all importers with no since date + * @When I import items for all importers with no since date */ - public function iEnqueueItemsForAllImportersWithNoSinceDate(): void + public function iImportItemsForAllImportersWithNoSinceDate(): void { $commandTester = $this->getCommandTester(); try { - $commandTester->execute(['command' => 'webgriffe:akeneo:enqueue']); + $commandTester->execute(['command' => 'webgriffe:akeneo:import']); } catch (Throwable $t) { $this->sharedStorage->set('command_exception', $t); } @@ -67,14 +67,14 @@ public function iShouldBeNotifiedThatASinceDateIsRequired(): void } /** - * @When I enqueue items for all importers with invalid since date + * @When I import items for all importers with invalid since date */ - public function iEnqueueItemsForAllImportersWithInvalidSinceDate(): void + public function iImportItemsForAllImportersWithInvalidSinceDate(): void { $commandTester = $this->getCommandTester(); try { - $commandTester->execute(['command' => 'webgriffe:akeneo:enqueue', '--since' => 'bad date']); + $commandTester->execute(['command' => 'webgriffe:akeneo:import', '--since' => 'bad date']); } catch (Throwable $t) { $this->sharedStorage->set('command_exception', $t); } @@ -92,15 +92,15 @@ public function iShouldBeNotifiedThatTheSinceDateMustBeAValidDate(): void } /** - * @When I enqueue items with since date specified from a not existent file + * @When I import items with since date specified from a not existent file */ - public function iEnqueueItemsWithSinceDateSpecifiedFromANotExistentFile(): void + public function iImportItemsWithSinceDateSpecifiedFromANotExistentFile(): void { $commandTester = $this->getCommandTester(); $filepath = vfsStream::url('root/not-existent-file.txt'); try { - $commandTester->execute(['command' => 'webgriffe:akeneo:enqueue', '--since-file' => $filepath]); + $commandTester->execute(['command' => 'webgriffe:akeneo:import', '--since-file' => $filepath]); } catch (Throwable $t) { $this->sharedStorage->set('command_exception', $t); } @@ -118,52 +118,52 @@ public function iShouldBeNotifiedThatTheSinceDateFileDoesNotExists(): void } /** - * @When I enqueue items for all importers modified since date specified from file :file + * @When I import items for all importers modified since date specified from file :file */ - public function iEnqueueItemsForAllImportersModifiedSinceDateSpecifiedFromFile(string $file): void + public function iImportItemsForAllImportersModifiedSinceDateSpecifiedFromFile(string $file): void { $commandTester = $this->getCommandTester(); $filepath = vfsStream::url('root/' . $file); try { - $commandTester->execute(['command' => 'webgriffe:akeneo:enqueue', '--since-file' => $filepath]); + $commandTester->execute(['command' => 'webgriffe:akeneo:import', '--since-file' => $filepath]); } catch (Throwable $t) { $this->sharedStorage->set('command_exception', $t); } } /** - * @When I enqueue all items for all importers + * @When I import all items for all importers */ - public function iEnqueueAllItemsForAllImporters(): void + public function iImportAllItemsForAllImporters(): void { $commandTester = $this->getCommandTester(); - $commandTester->execute(['command' => 'webgriffe:akeneo:enqueue', '--all' => true]); + $commandTester->execute(['command' => 'webgriffe:akeneo:import', '--all' => true]); } /** - * @When I enqueue all items for the :importer importer + * @When I import all items for the :importer importer */ - public function iEnqueueAllItemsForTheImporter(string $importer): void + public function iImportAllItemsForTheImporter(string $importer): void { $commandTester = $this->getCommandTester(); $commandTester->execute( - ['command' => 'webgriffe:akeneo:enqueue', '--all' => true, '--importer' => [$importer]], + ['command' => 'webgriffe:akeneo:import', '--all' => true, '--importer' => [$importer]], ); } /** - * @When I enqueue all items for a not existent importer + * @When I import all items for a not existent importer */ - public function iEnqueueAllItemsForANotExistentImporter(): void + public function iImportAllItemsForANotExistentImporter(): void { $commandTester = $this->getCommandTester(); try { $commandTester->execute( - ['command' => 'webgriffe:akeneo:enqueue', '--all' => true, '--importer' => ['not_existent']], + ['command' => 'webgriffe:akeneo:import', '--all' => true, '--importer' => ['not_existent']], ); } catch (Throwable $t) { $this->sharedStorage->set('command_exception', $t); @@ -185,7 +185,7 @@ private function getCommandTester(): CommandTester { $application = new Application($this->kernel); $application->add($this->enqueueCommand); - $command = $application->find('webgriffe:akeneo:enqueue'); + $command = $application->find('webgriffe:akeneo:import'); return new CommandTester($command); } diff --git a/tests/Behat/Context/Messenger/MessengerContext.php b/tests/Behat/Context/Messenger/MessengerContext.php index ec2b8087..d451437a 100644 --- a/tests/Behat/Context/Messenger/MessengerContext.php +++ b/tests/Behat/Context/Messenger/MessengerContext.php @@ -97,9 +97,9 @@ private function getQueueItemByImporterAndIdentifier(string $importer, string $i } /** - * @When I consume the messages + * @When I import all from Akeneo */ - public function iConsumeTheMessages(): void + public function IImportAllFromAkeneo(): void { foreach ($this->transport->get() as $envelope) { $message = $envelope->getMessage(); diff --git a/tests/Behat/Resources/services.xml b/tests/Behat/Resources/services.xml index 6cc20b4c..061af732 100644 --- a/tests/Behat/Resources/services.xml +++ b/tests/Behat/Resources/services.xml @@ -13,9 +13,9 @@ - + - + diff --git a/tests/Behat/Resources/suites.yml b/tests/Behat/Resources/suites.yml index 31c34198..fc505897 100644 --- a/tests/Behat/Resources/suites.yml +++ b/tests/Behat/Resources/suites.yml @@ -40,7 +40,7 @@ default: - webgriffe_sylius_akeneo.behat.context.setup.akeneo - webgriffe_sylius_akeneo.behat.context.setup.queue - - webgriffe_sylius_akeneo.behat.context.cli.enqueue_command + - webgriffe_sylius_akeneo.behat.context.cli.import_command - webgriffe_sylius_akeneo.behat.context.messenger @@ -59,7 +59,7 @@ default: - webgriffe_sylius_akeneo.behat.context.setup.akeneo - webgriffe_sylius_akeneo.behat.context.setup.queue - - webgriffe_sylius_akeneo.behat.context.cli.enqueue_command + - webgriffe_sylius_akeneo.behat.context.cli.import_command - webgriffe_sylius_akeneo.behat.context.messenger @@ -96,7 +96,7 @@ default: - webgriffe_sylius_akeneo.behat.context.setup.akeneo - webgriffe_sylius_akeneo.behat.context.setup.queue - - webgriffe_sylius_akeneo.behat.context.cli.enqueue_command + - webgriffe_sylius_akeneo.behat.context.cli.import_command - webgriffe_sylius_akeneo.behat.context.messenger From 58c4c485e1eaccc7fb78f87a54bf390c332510f5 Mon Sep 17 00:00:00 2001 From: Lorenzo Ruozzi Date: Mon, 1 Aug 2022 11:25:20 +0200 Subject: [PATCH 11/24] Remove no more needed feature file (#123) --- features/cleaning_queue.feature | 39 --------------------------------- 1 file changed, 39 deletions(-) delete mode 100644 features/cleaning_queue.feature diff --git a/features/cleaning_queue.feature b/features/cleaning_queue.feature deleted file mode 100644 index 7c85e917..00000000 --- a/features/cleaning_queue.feature +++ /dev/null @@ -1,39 +0,0 @@ -@cleaning_queue -Feature: cleaning queue - In order to have better performances during the akeneo pim import - As a store owner - I want to clean old already imported queue items. - - @cli - Scenario: Cleaning the queue when there are no imported queue items - Given there is a not imported item with identifier "braided-hat-m" for the "Product" importer in the Akeneo queue - When I clean the queue - Then I should be notified that there are no items to clean - - @cli - Scenario: Cleaning the queue when there are some imported items to clean - Given there is an already imported item with identifier "braided-hat-m" for the "Product" importer in the Akeneo queue - And this item has been imported 15 days ago - When I clean the queue - Then I should be notified that 1 item has been deleted - And there shouldn't be any more item to clean - - @cli - Scenario: Cleaning the queue specifying the retention number of days - Given there is an already imported item with identifier "braided-hat-m" for the "Product" importer in the Akeneo queue - And this item has been imported 15 days ago - And there is an already imported item with identifier "braided-hat-s" for the "Product" importer in the Akeneo queue - And this item has been imported 20 days ago - When I clean the queue specifying 16 days of retention - Then I should be notified that 1 item has been deleted - And there shouldn't be any more item to clean - - @cli - Scenario: Cleaning the queue specifying zero as retention number of days - Given there is an already imported item with identifier "braided-hat-m" for the "Product" importer in the Akeneo queue - And this item has been imported now - And there is an already imported item with identifier "braided-hat-s" for the "Product" importer in the Akeneo queue - And this item has been imported 20 days ago - When I clean the queue specifying 0 days of retention - Then I should be notified that 2 items have been deleted - And there shouldn't be any more items to clean From 134408ff6fa941448e5755d933ef173df92285c5 Mon Sep 17 00:00:00 2001 From: Lorenzo Ruozzi Date: Mon, 1 Aug 2022 12:51:52 +0200 Subject: [PATCH 12/24] Fix test template file (#123) --- .../SyliusAdminBundle/Product/_showInShopButton.html.twig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Application/templates/bundles/SyliusAdminBundle/Product/_showInShopButton.html.twig b/tests/Application/templates/bundles/SyliusAdminBundle/Product/_showInShopButton.html.twig index 491033e8..6a443f0c 100644 --- a/tests/Application/templates/bundles/SyliusAdminBundle/Product/_showInShopButton.html.twig +++ b/tests/Application/templates/bundles/SyliusAdminBundle/Product/_showInShopButton.html.twig @@ -36,7 +36,7 @@ {% endif %} {% endif %} - + - {{ 'webgriffe_sylius_akeneo.ui.enqueue'|trans }} + {{ 'webgriffe_sylius_akeneo.ui.import'|trans }} From de9a478f1ada0970c4c21ee95524c4eae98abf61 Mon Sep 17 00:00:00 2001 From: Lorenzo Ruozzi Date: Mon, 1 Aug 2022 12:57:00 +0200 Subject: [PATCH 13/24] WIP: refactor behat enqueuing products test (#123) --- features/enqueuing_generic_items.feature | 45 ---------- features/importing_generic_items.feature | 63 +++++++++++++ ...cts.feature => importing_products.feature} | 2 +- ...> importing_products_associations.feature} | 2 +- src/Product/Importer.php | 6 +- src/ProductAssociations/Importer.php | 5 +- .../config/packages/test/messenger.yaml | 2 +- .../Context/Messenger/MessengerContext.php | 8 -- .../Ui/Admin/ManagingProductsContext.php | 48 ++++++++++ tests/Behat/Resources/services.xml | 2 + tests/Behat/Resources/suites.yml | 10 ++- .../ApiClientMock/Product/10597353.json | 82 +++++++++++++++++ .../ApiClientMock/Product/11164822.json | 84 ++++++++++++++++++ .../ApiClientMock/Product/1314976.json | 72 +++++++++++++++ ...829a75beedf374533b1ff8bc_10597353_1104.jpg | Bin 0 -> 7896 bytes ...dbeababd84389b31159f8_11164822_Philips.jpg | Bin 0 -> 8545 bytes ...a0e6e91d0c512bc37c8780c21_1314976_5566.jpg | Bin 0 -> 8660 bytes 17 files changed, 369 insertions(+), 62 deletions(-) delete mode 100644 features/enqueuing_generic_items.feature create mode 100644 features/importing_generic_items.feature rename features/{enqueuing_products.feature => importing_products.feature} (99%) rename features/{enqueuing_products_associations.feature => importing_products_associations.feature} (96%) create mode 100644 tests/Integration/DataFixtures/ApiClientMock/Product/10597353.json create mode 100644 tests/Integration/DataFixtures/ApiClientMock/Product/11164822.json create mode 100644 tests/Integration/DataFixtures/ApiClientMock/Product/1314976.json create mode 100644 tests/Integration/DataFixtures/ApiClientMock/media-files/6/5/8/8/65887ee600d9d66e829a75beedf374533b1ff8bc_10597353_1104.jpg create mode 100644 tests/Integration/DataFixtures/ApiClientMock/media-files/7/3/6/b/736bbc43fd1e384f4badbeababd84389b31159f8_11164822_Philips.jpg create mode 100644 tests/Integration/DataFixtures/ApiClientMock/media-files/8/e/2/d/8e2d8254e599201a0e6e91d0c512bc37c8780c21_1314976_5566.jpg diff --git a/features/enqueuing_generic_items.feature b/features/enqueuing_generic_items.feature deleted file mode 100644 index 6713e3cc..00000000 --- a/features/enqueuing_generic_items.feature +++ /dev/null @@ -1,45 +0,0 @@ -@enqueuing_generic_items -Feature: Enqueuing items - In order to import data from Akeneo - As a Store Owner - I want to import items from the Akeneo PIM - - @cli - Scenario: Enqueueing items when no item is modified since the given date - When I import items for all importers modified since date "2020-01-20 01:00:00" - Then there should be no item in the Akeneo queue - - @cli - Scenario: Enqueueing items without a since date - When I import items for all importers with no since date - Then I should be notified that a since date is required - And there should be no item in the Akeneo queue - - @cli - Scenario: Enqueueing items with an invalid since date - When I import items for all importers with invalid since date - Then I should be notified that the since date must be a valid date - - @cli - Scenario: Enqueueing items with a since date specified from a not existent file - When I import items with since date specified from a not existent file - Then I should be notified that the since date file does not exists - - @cli - Scenario: Enqueuing all items regardless last modified date - Given there are 3 products on Akeneo - When I import all items for all importers - Then there should be 3 items for the "Product" importer in the Akeneo queue - And there should be 3 items for the "ProductAssociations" importer in the Akeneo queue - - @cli - Scenario: Enqueuing all items for one importer only - Given there are 3 products on Akeneo - When I import all items for the "Product" importer - Then there should be 3 items for the "Product" importer in the Akeneo queue - And there should be items for the "Product" importer only in the Akeneo queue - - @cli - Scenario: Enqueuing all items for a not existent importer - When I import all items for a not existent importer - Then I should be notified that the importer does not exists diff --git a/features/importing_generic_items.feature b/features/importing_generic_items.feature new file mode 100644 index 00000000..b3f038e7 --- /dev/null +++ b/features/importing_generic_items.feature @@ -0,0 +1,63 @@ +@importing_generic_items +Feature: Importing items + In order to import data from Akeneo + As an Administrator + I want to import items from the Akeneo PIM + + Background: + Given I am logged in as an administrator + And the store has a product association type "Pack" with a code "PACK" + + @cli @ui + Scenario: Importing items when no item is modified since the given date + When I import items for all importers modified since date "2020-01-20 01:00:00" + And I browse products + Then I should see 0 products in the list + + @cli @ui + Scenario: Importing items without a since date + When I import items for all importers with no since date + Then I should be notified that a since date is required + When I browse products + Then I should see 0 products in the list + + @cli @ui + Scenario: Importing items with an invalid since date + When I import items for all importers with invalid since date + Then I should be notified that the since date must be a valid date + When I browse products + Then I should see 0 products in the list + + @cli @ui + Scenario: Importing items with a since date specified from a not existent file + When I import items with since date specified from a not existent file + Then I should be notified that the since date file does not exists + When I browse products + Then I should see 0 products in the list + + @cli @ui + Scenario: Importing all items regardless last modified date + Given there is a product "1314976" updated at "2022-06-15" on Akeneo + And there is a product "10597353" updated at "2022-07-23" on Akeneo + And there is a product "11164822" updated at "2022-08-01" on Akeneo + When I import all items for all importers + And I browse products + Then I should see 3 products in the list + And the product with code "11164822" should have an association "Pack" with product "10597353" + + @cli @ui + Scenario: Importing all items for one importer only + Given there is a product "1314976" updated at "2022-06-15" on Akeneo + And there is a product "10597353" updated at "2022-07-23" on Akeneo + And there is a product "11164822" updated at "2022-08-01" on Akeneo + When I import all items for the "Product" importer + And I browse products + Then I should see 3 products in the list + And the product with code "11164822" should not have an association "Pack" with product "10597353" + + @cli @ui + Scenario: Enqueuing all items for a not existent importer + When I import all items for a not existent importer + Then I should be notified that the importer does not exists + When I browse products + Then I should see 0 products in the list diff --git a/features/enqueuing_products.feature b/features/importing_products.feature similarity index 99% rename from features/enqueuing_products.feature rename to features/importing_products.feature index 8f7718ad..3a400457 100644 --- a/features/enqueuing_products.feature +++ b/features/importing_products.feature @@ -1,4 +1,4 @@ -@enqueuing_products +@importing_products Feature: Enqueuing products In order to import my products from Akeneo As a Store Owner diff --git a/features/enqueuing_products_associations.feature b/features/importing_products_associations.feature similarity index 96% rename from features/enqueuing_products_associations.feature rename to features/importing_products_associations.feature index df84f23d..7e05c0b3 100644 --- a/features/enqueuing_products_associations.feature +++ b/features/importing_products_associations.feature @@ -1,4 +1,4 @@ -@enqueuing_products_associations +@importing_products_associations Feature: Enqueuing products associations In order to import my products associations from Akeneo As a Store Owner diff --git a/src/Product/Importer.php b/src/Product/Importer.php index 991d1c64..fe7629e6 100644 --- a/src/Product/Importer.php +++ b/src/Product/Importer.php @@ -104,8 +104,10 @@ public function getIdentifiersModifiedSince(DateTime $sinceDate): array $products = $this->apiClient->getProductApi()->all(50, ['search' => $searchBuilder->getFilters()]); $identifiers = []; foreach ($products as $product) { - Assert::string($product['identifier']); - $identifiers[] = $product['identifier']; + Assert::keyExists($product, 'identifier'); + $productIdentifier = (string) $product['identifier']; + Assert::stringNotEmpty($productIdentifier); + $identifiers[] = $productIdentifier; } return $identifiers; diff --git a/src/ProductAssociations/Importer.php b/src/ProductAssociations/Importer.php index bfdd9200..3f08d3f4 100644 --- a/src/ProductAssociations/Importer.php +++ b/src/ProductAssociations/Importer.php @@ -131,7 +131,10 @@ public function getIdentifiersModifiedSince(DateTime $sinceDate): array $products = $this->apiClient->getProductApi()->all(50, ['search' => $searchBuilder->getFilters()]); $identifiers = []; foreach ($products as $product) { - $identifiers[] = $product['identifier']; + Assert::keyExists($product, 'identifier'); + $productIdentifier = (string) $product['identifier']; + Assert::stringNotEmpty($productIdentifier); + $identifiers[] = $productIdentifier; } return $identifiers; diff --git a/tests/Application/config/packages/test/messenger.yaml b/tests/Application/config/packages/test/messenger.yaml index febf1335..561f725d 100644 --- a/tests/Application/config/packages/test/messenger.yaml +++ b/tests/Application/config/packages/test/messenger.yaml @@ -1,4 +1,4 @@ framework: messenger: transports: - main: 'in-memory://' + main: 'sync://' diff --git a/tests/Behat/Context/Messenger/MessengerContext.php b/tests/Behat/Context/Messenger/MessengerContext.php index d451437a..51aa358e 100644 --- a/tests/Behat/Context/Messenger/MessengerContext.php +++ b/tests/Behat/Context/Messenger/MessengerContext.php @@ -7,22 +7,14 @@ use Behat\Behat\Context\Context; use InvalidArgumentException; use Symfony\Component\Messenger\Envelope; -use Symfony\Component\Messenger\Transport\InMemoryTransport; use Throwable; use Webgriffe\SyliusAkeneoPlugin\Message\ItemImport; -use Webgriffe\SyliusAkeneoPlugin\MessageHandler\ItemImportHandler; use Webmozart\Assert\Assert; final class MessengerContext implements Context { private array $failedMessages = []; - public function __construct( - private InMemoryTransport $transport, - private ItemImportHandler $itemImportHandler, - ) { - } - /** * @Then the queue item with identifier :identifier for the :importer importer should not be in the Akeneo queue */ diff --git a/tests/Behat/Context/Ui/Admin/ManagingProductsContext.php b/tests/Behat/Context/Ui/Admin/ManagingProductsContext.php index cf780a1d..e752fce8 100644 --- a/tests/Behat/Context/Ui/Admin/ManagingProductsContext.php +++ b/tests/Behat/Context/Ui/Admin/ManagingProductsContext.php @@ -8,9 +8,13 @@ use Behat\Mink\Element\NodeElement; use Sylius\Behat\NotificationType; use Sylius\Behat\Page\Admin\Product\IndexPageInterface; +use Sylius\Behat\Page\Admin\Product\UpdateSimpleProductPageInterface; use Sylius\Behat\Service\Helper\JavaScriptTestHelperInterface; use Sylius\Behat\Service\NotificationCheckerInterface; use Sylius\Component\Core\Model\ProductInterface; +use Sylius\Component\Core\Repository\ProductRepositoryInterface; +use Sylius\Component\Product\Model\ProductAssociationTypeInterface; +use Webmozart\Assert\Assert; final class ManagingProductsContext implements Context { @@ -20,6 +24,8 @@ public function __construct( private IndexPageInterface $indexPage, private JavaScriptTestHelperInterface $testHelper, private NotificationCheckerInterface $notificationChecker, + private UpdateSimpleProductPageInterface $updateSimpleProductPage, + private ProductRepositoryInterface $productRepository, ) { } @@ -45,4 +51,46 @@ public function iShouldBeNotifiedThatItHasBeenSuccessfullyEnqueued(): void 'Akeneo PIM product import has been successfully scheduled', ); } + + /** + * @Then the product with code :code should have an association :productAssociationType with product :productName + */ + public function theProductShouldHaveAnAssociationWithProducts( + string $code, + ProductAssociationTypeInterface $productAssociationType, + ...$productsNames, + ): void { + $product = $this->productRepository->findOneByCode($code); + $this->updateSimpleProductPage->open(['id' => $product->getId()]); + foreach ($productsNames as $productName) { + Assert::true( + $this->updateSimpleProductPage->hasAssociatedProduct($productName, $productAssociationType), + sprintf( + 'This product should have an association %s with product %s.', + $productAssociationType->getName(), + $productName, + ), + ); + } + } + + /** + * @Then the product with code :code should not have an association :productAssociationType with product :productName + */ + public function theProductShouldNotHaveAnAssociationWithProduct( + string $code, + ProductAssociationTypeInterface $productAssociationType, + string $productName, + ): void { + $product = $this->productRepository->findOneByCode($code); + $this->updateSimpleProductPage->open(['id' => $product->getId()]); + Assert::false( + $this->updateSimpleProductPage->hasAssociatedProduct($productName, $productAssociationType), + sprintf( + 'This product should have an association %s with product %s.', + $productAssociationType->getName(), + $productName, + ), + ); + } } diff --git a/tests/Behat/Resources/services.xml b/tests/Behat/Resources/services.xml index 061af732..0b09cbff 100644 --- a/tests/Behat/Resources/services.xml +++ b/tests/Behat/Resources/services.xml @@ -47,6 +47,8 @@ + + diff --git a/tests/Behat/Resources/suites.yml b/tests/Behat/Resources/suites.yml index fc505897..ab35510a 100644 --- a/tests/Behat/Resources/suites.yml +++ b/tests/Behat/Resources/suites.yml @@ -31,24 +31,28 @@ default: filters: tags: "@importing_product_associations && @cli" - cli_enqueuing_generic_items: + cli_ui_importing_generic_items: contexts: - sylius.behat.context.hook.doctrine_orm - sylius.behat.context.transform.date_time + - sylius.behat.context.transform.product_association_type + - sylius.behat.context.setup.admin_security + - sylius.behat.context.setup.product_association - webgriffe_sylius_akeneo.behat.context.setup.akeneo - webgriffe_sylius_akeneo.behat.context.setup.queue - webgriffe_sylius_akeneo.behat.context.cli.import_command - - webgriffe_sylius_akeneo.behat.context.messenger + - sylius.behat.context.ui.admin.managing_products + - webgriffe_sylius_akeneo.behat.context.ui.admin.managing_products - webgriffe_sylius_akeneo.behat.context.system.filesystem - webgriffe_sylius_akeneo.behat.context.system.datetime filters: - tags: "@enqueuing_generic_items && @cli" + tags: "@importing_generic_items && @cli && @ui" cli_enqueuing_products: contexts: diff --git a/tests/Integration/DataFixtures/ApiClientMock/Product/10597353.json b/tests/Integration/DataFixtures/ApiClientMock/Product/10597353.json new file mode 100644 index 00000000..0b986ea8 --- /dev/null +++ b/tests/Integration/DataFixtures/ApiClientMock/Product/10597353.json @@ -0,0 +1,82 @@ +{ + "identifier": "10597353", + "enabled": true, + "family": "loudspeakers", + "categories": [ + "audio_video_sales", + "loudspeakers", + "nec" + ], + "groups": [], + "parent": null, + "values": { + "picture": [ + { + "locale": null, + "scope": null, + "data": "6/5/8/8/65887ee600d9d66e829a75beedf374533b1ff8bc_10597353_1104.jpg", + "_links": { + "download": { + "href": "http://127.0.0.1:8081/api/rest/v1/media-files/6/5/8/8/65887ee600d9d66e829a75beedf374533b1ff8bc_10597353_1104.jpg/download" + } + } + } + ], + "name": [ + { + "locale": null, + "scope": null, + "data": "NEC SP-4046PV" + } + ], + "description": [ + { + "locale": "de_DE", + "scope": "print", + "data": "Dem edlen Design der Displays angepasst und mit wenigen Handgriffen montiert bzw. demontiert." + }, + { + "locale": "en_US", + "scope": "print", + "data": "Enhance your Large-Screen Display Speakers without breaking your budget with NEC's SP-4046PV." + }, + { + "locale": "fr_FR", + "scope": "print", + "data": "Adapté au design des moniteurs LCD NEC Public Display, montage et démontage faciles." + } + ], + "release_date": [ + { + "locale": null, + "scope": "ecommerce", + "data": "2011-08-07T00:00:00+00:00" + } + ] + }, + "created": "2022-04-14T13:14:05+00:00", + "updated": "2022-04-14T13:14:05+00:00", + "associations": { + "PACK": { + "products": [], + "product_models": [], + "groups": [] + }, + "UPSELL": { + "products": [], + "product_models": [], + "groups": [] + }, + "X_SELL": { + "products": [], + "product_models": [], + "groups": [] + }, + "SUBSTITUTION": { + "products": [], + "product_models": [], + "groups": [] + } + }, + "quantified_associations": {} +} diff --git a/tests/Integration/DataFixtures/ApiClientMock/Product/11164822.json b/tests/Integration/DataFixtures/ApiClientMock/Product/11164822.json new file mode 100644 index 00000000..97b29406 --- /dev/null +++ b/tests/Integration/DataFixtures/ApiClientMock/Product/11164822.json @@ -0,0 +1,84 @@ +{ + "identifier": "11164822", + "enabled": true, + "family": "loudspeakers", + "categories": [ + "audio_video_sales", + "loudspeakers", + "philips" + ], + "groups": [], + "parent": null, + "values": { + "picture": [ + { + "locale": null, + "scope": null, + "data": "7/3/6/b/736bbc43fd1e384f4badbeababd84389b31159f8_11164822_Philips.jpg", + "_links": { + "download": { + "href": "http://127.0.0.1:8081/api/rest/v1/media-files/7/3/6/b/736bbc43fd1e384f4badbeababd84389b31159f8_11164822_Philips.jpg/download" + } + } + } + ], + "name": [ + { + "locale": null, + "scope": null, + "data": "Philips AD7000W" + } + ], + "description": [ + { + "locale": "de_DE", + "scope": "print", + "data": "Eleganz und erstklassige Audio-Qualität, kabellos dank AirPlay\\nBegeistert von Sound\\n\\nDieser kabellose Philips Fidelio-Lautsprecher ermöglicht es Ihnen, Titel direkt von Ihrem iPod/iPhone/iPad und über iTunes auf Ihrem Computer abzuspielen, und das ganze kabellos dank AirPlay. Dieser elegante und kompakte Lautsprecher erfüllt Ihr Zuhause mit großartiger Musik ganz ohne Kabelgewirr.\\n\\nKristallklarer Klang\\n- SoundAvia für eine ausgewogene Klangleistung\\n- 10 W RMS Gesamtausgangsleistung\\n\\nElegant und kompakt\\n- Kompaktes Design mit minimalem Platzbedarf für jeden Lebensstil\\n\\nNoch vielseitiger\\n- Musikübertragung mit kabelloser AirPlay-Technologie\\n- Gleichzeitiges Wiedergeben und Aufladen Ihres iPods/iPhones/iPads\\n- AUX-Eingang für einfaches Anschließen an nahezu jedes elektronische Gerät\\n- Entdecken Sie Musik und weitere Funktionen, und teilen Sie diese über die kostenlose Fidelio\\nApp" + }, + { + "locale": "en_US", + "scope": "print", + "data": "Elegance and superb audio, wirelessly with AirPlay\\nObsessed with sound\\n\\nThis Philips Fidelio wireless speaker gives you the freedom to play songs directly from\\nyour iPod/iPhone/iPad, and from your computer’s iTunes - wirelessly via AirPlay. Elegant\\nand compact, it fills your home with great music, not clutter.\\n\\nCrystal clear sound\\n- SoundAvia for well-balanced sound performance\\n- 10 W RMS total output power\\n\\nElegant and compact\\n- Compact design for any space, any lifestyle\\n\\nAdvanced versatility\\n- Stream music with AirPlay wireless technology\\n- Play and charge your iPod/iPhone/iPad simultaneously\\n- AUX-in for easy connection to almost any electronic device\\n- Discover, share music and more features via free Fidelio app" + }, + { + "locale": "fr_FR", + "scope": "print", + "data": "Élégance et qualité audio exceptionnelle, sans fil avec AirPlay\\nL'obsession du son\\n\\nCette enceinte sans fil Philips Fidelio vous laisse la liberté d'écouter des morceaux directement à partir de votre iPod/iPhone/iPad ou depuis iTunes sur votre ordinateur, tout cela sans fil grâce à AirPlay. Élégante et compacte, elle emplit votre maison d'un son haute qualité, sans l'encombrer inutilement.\\n\\nSon limpide\\n- SoundAvia pour des performances sonores équilibrées\\n- Puissance de sortie totale de 10 W RMS\\n\\nÉlégante et compacte\\n- Design compact pour tous les intérieurs et tous les modes de vie\\n\\nPolyvalence accrue\\n- Technologie de diffusion de la musique sans fil AirPlay\\n- Écoutez et chargez simultanément votre iPod/iPhone/iPad\\n- Entrée AUX, pour une connexion facile à la plupart des périphériques électroniques\\n- Découvrez et partagez de la musique et bien plus via l'appli Fidelio gratuite" + } + ], + "release_date": [ + { + "locale": null, + "scope": "ecommerce", + "data": "2011-10-09T00:00:00+00:00" + } + ] + }, + "created": "2022-04-14T13:14:01+00:00", + "updated": "2022-04-14T13:14:01+00:00", + "associations": { + "PACK": { + "products": [ + "10597353" + ], + "product_models": [], + "groups": [] + }, + "UPSELL": { + "products": [], + "product_models": [], + "groups": [] + }, + "X_SELL": { + "products": [], + "product_models": [], + "groups": [] + }, + "SUBSTITUTION": { + "products": [], + "product_models": [], + "groups": [] + } + }, + "quantified_associations": {} +} diff --git a/tests/Integration/DataFixtures/ApiClientMock/Product/1314976.json b/tests/Integration/DataFixtures/ApiClientMock/Product/1314976.json new file mode 100644 index 00000000..07d2baf9 --- /dev/null +++ b/tests/Integration/DataFixtures/ApiClientMock/Product/1314976.json @@ -0,0 +1,72 @@ +{ + "identifier": "1314976", + "enabled": true, + "family": "loudspeakers", + "categories": [ + "audio_video_sales", + "loudspeakers", + "sony" + ], + "groups": [], + "parent": null, + "values": { + "picture": [ + { + "locale": null, + "scope": null, + "data": "8/e/2/d/8e2d8254e599201a0e6e91d0c512bc37c8780c21_1314976_5566.jpg", + "_links": { + "download": { + "href": "http://127.0.0.1:8081/api/rest/v1/media-files/8/e/2/d/8e2d8254e599201a0e6e91d0c512bc37c8780c21_1314976_5566.jpg/download" + } + } + } + ], + "name": [ + { + "locale": null, + "scope": null, + "data": "Sony SS-SP32FWB" + } + ], + "description": [ + { + "locale": "en_US", + "scope": "print", + "data": "When it comes to creating high-quality Hi-Fi sound in your home, selecting the proper speakers is even more important. Sony makes it easy with an extremely wide range of different speakers. Whether you're listening to a refined string quartet or watching a powerful action-packed movie, Sony's speakers are designed to deliver an intensive acoustic experience that is both powerful and emotional." + } + ], + "release_date": [ + { + "locale": null, + "scope": "ecommerce", + "data": "2007-10-13T00:00:00+00:00" + } + ] + }, + "created": "2022-04-14T13:14:11+00:00", + "updated": "2022-04-14T13:14:11+00:00", + "associations": { + "PACK": { + "products": [], + "product_models": [], + "groups": [] + }, + "UPSELL": { + "products": [], + "product_models": [], + "groups": [] + }, + "X_SELL": { + "products": [], + "product_models": [], + "groups": [] + }, + "SUBSTITUTION": { + "products": [], + "product_models": [], + "groups": [] + } + }, + "quantified_associations": {} +} diff --git a/tests/Integration/DataFixtures/ApiClientMock/media-files/6/5/8/8/65887ee600d9d66e829a75beedf374533b1ff8bc_10597353_1104.jpg b/tests/Integration/DataFixtures/ApiClientMock/media-files/6/5/8/8/65887ee600d9d66e829a75beedf374533b1ff8bc_10597353_1104.jpg new file mode 100644 index 0000000000000000000000000000000000000000..350bc04c3f2007ca60541228db2e1b394e263ff6 GIT binary patch literal 7896 zcmai21yCH_vR*6+B)A3*ZUM45*~J3{SZr}maCg@PcXti8xU;wf4<0-~fUvl`+sl3L z{_nr{=2g9(sXAwBditE{>aY9j^Z4WO7XU{_Qd$y#goFfm|N8Qb6d4r_3FWtfEDit(85I>7?HTIxrzmK@m5`880H|oVPo6%b#si|`(}*fz z5O999{~Vh`NXz9wFD4E(@QbUi={zDLrgO}#9amQA;(ll7eEi$k%YXjGUrYTSQBaYP z(SE<;z(oQeq5NJ64ehtme=8s%<5B}fQIzbDI6r>Q!J~<-9>*8!TsfwNIy}w;UZDKm zIW7t=Kp3!$KmvRcd4=-~6&dB1L94RJ*DGy2LmhRfMRed%AaZROkY4viV-4#HwEV?}E;w!qJmtxZn z9OR3qg_Ma=(G*&%7`(dMj{r3Rg=j~7oLpxoaO;hHUXcB{_ST?LuK_H~2D6V~&|A!V zz(Xfvt-CXcd}l7+ee#F3#q*8P>r-#?7$|Whk(9J`qRkGG}`6~sT7V7ZPXFumy*nIavIUAg= zb-B|iExw`*IVl~cZc$T<;nL$GE+Mif0$fmF1{_OL^qj~uhT^Aq zG|7|?mV+B(fwCFS7z%8i^~e1>Y5(i^zb8lp&Wjp0pK~L%r*)x zZ&FJJ@~)m@H%-gdC*&T2YmM@eJOQZC42VPb$c6z&`6RPk{pLdb!ZshT_cJ%jUAyM^ zI;D;6mhmQGpv3HG_)_6;2ShyW%fL(N0A&-ZLOviHU-VX7r7VO-+6;TJoWrgh9>`mj zOAv>dmuSwRGQ32Ti=}sZJ1nZchy1tq{u#)q$lLP8G>PL2Wzva7bb_%Y^C{>c`+=<-}3d_0hQaQPTOaZ+57M zL+iHd6e)E-QhIM|bj?W0GabyP7ZMUI3SXfsZXk_-+2id5WoQIcA4to;72*>wS{$t( z3b*zU`F#Cv?Eae*3{GKwb-hpX7*$WBb7moZSY2l>FU_sF8(^hPv3}dokz@)~>YB3& z{tK307{AcGXatpQ!_p5qct(m-GYJyM7Il9mTFbL4;+sGpj-Hj z>$5v0l9ZN0M_7v>ZDPu9IZU>W;myyrxJ;YE8D_XbFxS>T~ zN-od-T3Q{@)`WA^;peW2k)|0RKJ$Rk2il&n&C_y0uNo2*oNjh@X^$gXbts zTV@H1i8~IC8p!n7(Xy*dnSb7Jp*=wBc-dWRG)up%5&=v!P53j)o{%@Y?=Z)0jgF=o z<6s&Rt`)GMx9>tveV>jo)AsMIkqh;tTuMdpDuny`m73n0@zcj>Hlf&IEok`fkXb?W zG9Zo*F&UEuc~mN{Q{UcIC>k_8S+pI;dswQ6^Y|9ZVlC^$~>h%Y|*XSA1g@KBl~xQV_jqXaFq8e9%rD?&eVF$D= zYu7+39vE2gUI66#g$!LdgsVZ{S#6!%JA!Xqu%cmuL2*7|Dn4!&@12 z#n@(B&!3qWm(jbM#O{~U)(l0}CHJp~<82C|O{g2QYZ*T~2R8&3`oOclO7jzzt~cOw z;OHg&0^6I>0+O~wOW?we8uDGGagIa> zHvH~Q{^qpB@{!jA3c`*yo}VdMqmyO(lN++N_bw$Km`7l=O}911YYj3J+Wi#F&S~8i zcu+y|RDU)>ZW!ut9(Qr`P_3%u27Q-GaUffop^OaP{MF>^o%c=e)wF6O?clhUkg7;n zllCtGRc*(>eDy~7BLGddl+RWkm`uxuLIRY3mYS@OOuB+gzL5%SRGop_*&CY)~)C07}baYznx>@IjkYo<+ zagBP|QR+BIJ={Mt0S#Dn?G}BIPr~(OxAYNh{+Sev=k~!$s{eR zj}7K4AqGiLxhFZS8e&}iv#{f6?pL>5Fz@Xv11-%n=--aIw~UGe5-j&v*)Ci6SFoHM zT16-&jlGCWebT27Ix$}vvw1Pm#WMeMjMX7ZoflRSw+{3u%0zf4bvA5ew;$eyM__XB z|7R%SJj48OR}i|Xm@lT2yED0&HRUfQd)$0~RCxB4NO1S*c+49-@h2@DgjtGbP1>!lv&eR;(MCjYQI>{ z^&NJ-pYD4SeD>*ociiL0jQU+} ztAG-IgzeJP$*s$>s~(mKTC=;s<1#p85=1;`!O+!@B;)DQy&r#@r;jD1a~Z&3E2tX_ zvE935SRq?T{PapAGiY3n(%OBK?17>q2?Nt4N>yMUVavnWwh;T_ZEI&Y%fW5zVBvaJ z`~?jcS%00x?S(epa<0#9_5q8Kr}F5(dgY&dUtyUU8MZnfJgG`j=f~&Ao-Nr7F-~Cf`{^XHA{_AMrA=I|wXgG1U@~mI2sJa8HW5X=(>s3Z$o0 zfEw(XV8H#VNOCGn_1|o+@qtC48_i*`1r_s?6wz(g>GJ5s5;JelA;tM4K#vvyuQbcJ zoGryq`<1$TDihG#iPy+3qM;E!JXNBgse!%L@v&g?hMY=k za;~?jsmP)kt?7&Oc-6qca)Y==M>DnB+?VX3SBTRfd+M&&?%!Pd65WKH*37Pvr&2 zF1V?U$H_f)g|?0l`sZeABe{Czgpl8~$9vuS{e$U-EtO!w?O(0o-@${F=c}-9H02?t z6XRHy_C(D1wZEqy9-~F$Lq*-`0$kA7w}PWPU0KlgMXNj9esWfg91nr_O%Y}98y#7E zbp!BTPjFQ_+y@$(GK4eV=>^%O-IDv3)a~Y+;Ad;YyGg0oL}t^G;hxgY5xAWS-?uD- zo!B1L6fI&lyFQoefa7bf=poM_BxHH)^W)wh?|pj~=rP6=y<*`M%r^zZ z&5Vd8oXFTLYQ_9&KqT!rgg^DwLUqh8o!*BLE!S6YQ;Q$RCHhQ=KG>hm|DP2K|7>%r z^%0QY%}-AxSL8-aZ_Y#vk8`+@Y^u7Qp1$P^ALq^exgH(Kb(0h_lGh>0MD7*pth0yX zo*A?x1U%ed9vAd#IUFm+xk+;!9ec65?Dxql0c{U zmc~g~p1CS@u(`lm+%rCm>nW-F$bBx1hUcmPmPxI9AS+WqVm|W-*yamQJ4asBYaWSq zMMMXf!;a-ZkcC+Vy)!YBQz0941vdh?(c#z6^{e0CGi)dui(Ch(oI^QP=XTQ zk0C^vd}pIj{jg=YPli*}?YWrq&AqNP&3d=zC7l&p!?T4X1d}|CPM}QyJ!>@*8a({L zZ=$^4rwbdjv{Y($G?a5EAnHbTKBXnw@Ce|{e%*1Gw+R28@V4)QU!|#wtk|J@OP5b z_9nqa5i8|H=49AaQRU6NX`Do9xR!P$`md`>)Eiu_N~qrsiPd;fux;?XFlIqgNL$@o zOkiGv($~_G%KpIGee$3LOTA_e->10EQLCM%q9guccG8S*X>Ie3_(CEh{XF?cx*NJc zq62eMio@;FNELkl7WiZSw$+?#>~N{nmh=3O|En9j!R78z7OBUBpf#4L>mxwLYop=K zABh*~${oj@q8;SRk1>3Ht!>j*DQK+ny|OfAYQ}+kdUTz;)mSbfnfIi6rJo7+OH5Ep@ zbjPC>1D|dAMy;+B8fZ`ix}!B4ihhvH$+eRjaHj+?YR(ZG-D2k1JB7O~^#?Cjg@MPk zg#smplv((A?_MejQQZ=^gM*G7=CXxu6YHDA4)qf?EsprGKfEB=KlD6L(Ti(Y?)V3Q z4F2rnk2R~P+))s9AsDsJ!XfFE7LHK2NETEzXOztIH_~VEd}$ihJ?WH2{$}Hlk-XQ= z?;z8IkATsR{^T@}fRb_)b1x1K5oYC5jNE;T?=406eoJiq__FulGh;ZVA%Q6+QJ$=9 zivUkZEoG=oQsPJW+p~r940R5gsTMXm-F_VhVct~Uud*^)CGxl7b3Ba%I ztS0$=w-JCb;Hq-}at*Gvm4% zNXNhZewXmPq^ascvc&MgX&yJzbQ(##9@={e>HdnXu2scmYos+(2X1Q$7JfQ~)_1mj z54%torgE?$mpwzTZ0mBQ|Ljp@T!i^!REuhWm-q)_^nb-x_g~*Kg;1c zRQUjnj7G*@)y63y{WEpT7Ctui1qsqgDsB$!3W$R$=q6!NHk41iaLL1HmN@WwY?}l9WVL~ykAiilg6O9VXP|wVq(bj zW34svb0_0M_BvBpI-n|(eLs-X!I$s!4CR?I#+%>JzIuQFTomTC}?QFP|$AV-BErq zwTe4SrUpj{mN}>ByGzvclV~0{S z_j@!@#8&SP9o+%i?DFVrqz=dnH(lB0_JCGBob(H4hBe2Cy{jDL^3bPk;@Lw>Zi@i; zx+8zMi;$6fUa}8r&POzVGK#&7xzbysMdMmI9u%A1c60w`4S%-naK=~?Pvv8#vD*Zm zAOH4$R&Z3!SqkG;;oN&IHC<|^!tDl7HiDs(MZh*>UbO9EzcFRc61jqrlG!>xd!TBN zUHEE2#@g2YN-})EguCxylp>Ph$}Wgelcvib`s9LkmtIE%%y%zzOi^M#YVs^}bRcl; z4%(|g-t&ctP6x`n7s=vB)|0yhw#;r zJXxJ##Em!TVJ6whVwlA;Z&yO0%&(BUEsic0k%9D1>H!#4NvLajkZaQ)^PjW;<$DQq z8{X`+PgAWBTQX6?IXW=K3}=>hzeeAdnozteTT585w6M@MqhrIOmR96_74kyc^bsId zD8-PYu2`g2cP&2TrJ$tgJgggsU+b=_k<8Sh;H9z=u65@kIMU@|O7X(7@pmhhnJvAzw0u0g7%Vm&(-MJIRQ_^dAeE^%wog zsxYUDwFB-!p;%pw0mHZAPQkUre8(nEA0-!xCN1=3KadZV%1^x+eMKb&X*5Rdj4h^y z9tLfYxXAUqo$zE)8zoo@Igu>7E(n{OA{dsVl3?~epl3A-2e%vT@oiEbz<^#_LeZ`- z##vM9i$)lqjkEICmz@@ym*^4XO;z(q&l2V5Fi8mcv%Wb=?#qXD5;su-hfcdwdJHMH zH?OroEe+^cIB`3Np_A^tvNW%n<&NpZZiKh6-jzsk5?p~g99D`zNzR*0@-h;N}-(R zd(wex>S1g7smdiL(98%ryk50*_Q3X&m>@cOUMJ&q zuU%)baQ=(ZvZ)2{fmp$m2z4%{Upq_Mi$S%dwaUhIbd`b3vLbf*bpB@NJhQ^h-oOjj zffz7Z@=Nbzm1F6lJxu>2HfCmYIj$!wVU$EMV)!-$G*EwP_IW6Ph#VZ?)^;ioJMuUB Ig&*hs3)yj&YybcN literal 0 HcmV?d00001 diff --git a/tests/Integration/DataFixtures/ApiClientMock/media-files/7/3/6/b/736bbc43fd1e384f4badbeababd84389b31159f8_11164822_Philips.jpg b/tests/Integration/DataFixtures/ApiClientMock/media-files/7/3/6/b/736bbc43fd1e384f4badbeababd84389b31159f8_11164822_Philips.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ff7eb26930334d336900cd4b4dd62d57a69fb116 GIT binary patch literal 8545 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 OEE1t2S$=lCjO=CU8sDFV; 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 Date: Mon, 8 Aug 2022 10:14:45 +0200 Subject: [PATCH 14/24] Assert to import product associations (#123) --- features/importing_product_associations_from_queue.feature | 2 +- tests/Behat/Context/Cli/ImportCommandContext.php | 1 + tests/Behat/Context/Setup/AkeneoContext.php | 5 +++-- tests/Behat/Resources/suites.yml | 4 +++- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/features/importing_product_associations_from_queue.feature b/features/importing_product_associations_from_queue.feature index 02907198..28b5fce1 100644 --- a/features/importing_product_associations_from_queue.feature +++ b/features/importing_product_associations_from_queue.feature @@ -10,8 +10,8 @@ Feature: Importing product associations from queue And the store has a product "10627329" And the store has a product "upsell-product-1" with code "upsell-product-1" And the store has a product "upsell-product-2" with code "upsell-product-2" - And there is one product associations to import with identifier "10627329" in the Akeneo queue And the store has a product association type "Upsell" with a code "UPSELL" + And there is a product "10627329" on Akeneo When I import all from Akeneo Then the product "10627329" should be associated to product "upsell-product-1" for association with code "UPSELL" And the product "10627329" should be associated to product "upsell-product-2" for association with code "UPSELL" diff --git a/tests/Behat/Context/Cli/ImportCommandContext.php b/tests/Behat/Context/Cli/ImportCommandContext.php index 9462c96b..f169f3e1 100644 --- a/tests/Behat/Context/Cli/ImportCommandContext.php +++ b/tests/Behat/Context/Cli/ImportCommandContext.php @@ -134,6 +134,7 @@ public function iImportItemsForAllImportersModifiedSinceDateSpecifiedFromFile(st /** * @When I import all items for all importers + * @When I import all from Akeneo */ public function iImportAllItemsForAllImporters(): void { diff --git a/tests/Behat/Context/Setup/AkeneoContext.php b/tests/Behat/Context/Setup/AkeneoContext.php index 1fc47103..b53a9d02 100644 --- a/tests/Behat/Context/Setup/AkeneoContext.php +++ b/tests/Behat/Context/Setup/AkeneoContext.php @@ -21,12 +21,13 @@ public function __construct( } /** + * @Given there is a product :identifier on Akeneo * @Given there is a product :identifier updated at :date on Akeneo */ - public function thereIsAProductUpdatedAtOnAkeneo(string $identifier, DateTime $date): void + public function thereIsAProductUpdatedAtOnAkeneo(string $identifier, DateTime $date = null): void { Assert::isInstanceOf($this->apiClient, ApiClientMock::class); - $this->apiClient->addProductUpdatedAt($identifier, $date); + $this->apiClient->addProductUpdatedAt($identifier, $date ?? new DateTime()); } /** diff --git a/tests/Behat/Resources/suites.yml b/tests/Behat/Resources/suites.yml index ab35510a..505807b2 100644 --- a/tests/Behat/Resources/suites.yml +++ b/tests/Behat/Resources/suites.yml @@ -24,9 +24,11 @@ default: - sylius.behat.context.setup.product_association - sylius.behat.context.setup.channel - webgriffe_sylius_akeneo.behat.context.setup.queue + - webgriffe_sylius_akeneo.behat.context.setup.akeneo - webgriffe_sylius_akeneo.behat.context.db.product - - webgriffe_sylius_akeneo.behat.context.messenger + + - webgriffe_sylius_akeneo.behat.context.cli.import_command filters: tags: "@importing_product_associations && @cli" From cd174dcdf7f13fd941652b4c9b779e9d870719f3 Mon Sep 17 00:00:00 2001 From: Lorenzo Ruozzi Date: Mon, 8 Aug 2022 10:15:12 +0200 Subject: [PATCH 15/24] Remove old commands (#123) --- src/Command/ConsumeCommand.php | 0 src/Command/QueueCleanupCommand.php | 0 2 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 src/Command/ConsumeCommand.php delete mode 100644 src/Command/QueueCleanupCommand.php diff --git a/src/Command/ConsumeCommand.php b/src/Command/ConsumeCommand.php deleted file mode 100644 index e69de29b..00000000 diff --git a/src/Command/QueueCleanupCommand.php b/src/Command/QueueCleanupCommand.php deleted file mode 100644 index e69de29b..00000000 From 78e5fabe65abede14697de26795141adbc159bc4 Mon Sep 17 00:00:00 2001 From: Lorenzo Ruozzi Date: Mon, 8 Aug 2022 11:39:35 +0200 Subject: [PATCH 16/24] Fix behat tests (#123) --- ...=> importing_product_associations.feature} | 0 features/importing_products.feature | 77 ++++++++----------- .../importing_products_associations.feature | 15 ---- .../importing_products_from_queue.feature | 45 ----------- tests/Behat/Resources/suites.yml | 24 +++++- ...{braided-hat-l.json => BRAIDED_HAT_L.json} | 4 +- ...{braided-hat-m.json => BRAIDED_HAT_M.json} | 4 +- ...{braided-hat-s.json => BRAIDED_HAT_S.json} | 4 +- ...raided-hat.json => MODEL_BRAIDED_HAT.json} | 0 9 files changed, 62 insertions(+), 111 deletions(-) rename features/{importing_product_associations_from_queue.feature => importing_product_associations.feature} (100%) delete mode 100644 features/importing_products_associations.feature delete mode 100644 features/importing_products_from_queue.feature rename tests/Integration/DataFixtures/ApiClientMock/Product/{braided-hat-l.json => BRAIDED_HAT_L.json} (97%) rename tests/Integration/DataFixtures/ApiClientMock/Product/{braided-hat-m.json => BRAIDED_HAT_M.json} (98%) rename tests/Integration/DataFixtures/ApiClientMock/Product/{braided-hat-s.json => BRAIDED_HAT_S.json} (97%) rename tests/Integration/DataFixtures/ApiClientMock/ProductModel/{model-braided-hat.json => MODEL_BRAIDED_HAT.json} (100%) diff --git a/features/importing_product_associations_from_queue.feature b/features/importing_product_associations.feature similarity index 100% rename from features/importing_product_associations_from_queue.feature rename to features/importing_product_associations.feature diff --git a/features/importing_products.feature b/features/importing_products.feature index 3a400457..85449549 100644 --- a/features/importing_products.feature +++ b/features/importing_products.feature @@ -1,61 +1,50 @@ @importing_products -Feature: Enqueuing products - In order to import my products from Akeneo +Feature: Importing products + In order to show updated data about my products As a Store Owner - I want to add them to the Akeneo PIM queue + I want to import products from Akeneo PIM - @cli - Scenario: Enqueuing products modified since a given date - Given there is a product "product-1" updated at "2020-01-10 22:23:13" on Akeneo - And there is a product "product-2" updated at "2020-01-21 09:54:12" on Akeneo - And there is a product "product-3" updated at "2020-01-22 08:15:08" on Akeneo - When I import items for all importers modified since date "2020-01-20 01:00:00" - Then the queue item with identifier "product-1" for the "Product" importer should not be in the Akeneo queue - And the queue item with identifier "product-2" for the "Product" importer should be in the Akeneo queue - And the queue item with identifier "product-3" for the "Product" importer should be in the Akeneo queue + Background: + Given there is a product "BRAIDED_HAT_M" on Akeneo + And there is a product "BRAIDED_HAT_L" on Akeneo @cli - Scenario: There are no products modified since datetime read in file - Given there is a file with name "last-date" and content "2020-01-20 01:00:00" - And current date time is "2020-01-25T12:00:00+01:00" - When I import items for all importers modified since date specified from file "last-date" - Then there should be no item in the queue for the "Product" importer - And there is a file with name "last-date" that contains "2020-01-25T12:00:00+01:00" + Scenario: Importing single product model and its variants + Given the store operates on a single channel + And the store is also available in "it_IT" + When I import all from Akeneo + Then the product "MODEL_BRAIDED_HAT" should exists with the right data + And the product variant "BRAIDED_HAT_M" of product "MODEL_BRAIDED_HAT" should exists with the right data + And the product variant "BRAIDED_HAT_L" of product "MODEL_BRAIDED_HAT" should exists with the right data @cli - Scenario: Enqueuing products modified since datetime read in file - Given there is a product "product-1" updated at "2020-01-10 22:23:13" on Akeneo - And there is a product "product-2" updated at "2020-01-21 09:54:12" on Akeneo - And there is a file with name "last-date" and content "2020-01-20 01:00:00" - And current date time is "2020-01-25T12:00:00+01:00" - When I import items for all importers modified since date specified from file "last-date" - Then the queue item with identifier "product-1" for the "Product" importer should not be in the Akeneo queue - And the queue item with identifier "product-2" for the "Product" importer should be in the Akeneo queue - And there is a file with name "last-date" that contains "2020-01-25T12:00:00+01:00" - - @ui - Scenario: Enqueuing a simple product - Given I am logged in as an administrator - And the store has a product "Braided hat m" - When I browse products - And I schedule an Akeneo PIM import for the "Braided hat m" product - Then I should be notified that it has been successfully enqueued + Scenario: Importing products with images should not leave temporary files in temporary files directory + Given the store operates on a single channel + And the store is also available in "it_IT" + When I import all from Akeneo + Then there should not be any temporary file in the temporary files directory @ui - Scenario: Enqueuing a product already enqueued - Given I am logged in as an administrator - And the store has a product "Braided hat l" - And there is one item to import with identifier "BRAIDED_HAT_L" for the "Product" importer in the Akeneo queue + Scenario: Importing a simple product + Given there is a product "11164822" on Akeneo + And I am logged in as an administrator + And the store has a product "11164822" When I browse products - And I schedule an Akeneo PIM import for the "Braided hat l" product + And I schedule an Akeneo PIM import for the "11164822" product Then I should be notified that it has been successfully enqueued + And the product "11164822" should exists with the right data + And the product variant "11164822" of product "11164822" should exists with the right data @ui - Scenario: Enqueuing a configurable product + Scenario: Importing a configurable product Given the store operates on a single channel - And the store has a "Braided hat" configurable product - And this product has "Small", "Medium" and "Large" variants + And the store has a "Model Braided Hat" configurable product + And this product has "Braided Hat S", "Braided Hat M" and "Braided Hat L" variants And I am logged in as an administrator When I browse products - And I schedule an Akeneo PIM import for the "Braided hat" product + And I schedule an Akeneo PIM import for the "Model Braided Hat" product Then I should be notified that it has been successfully enqueued + Then the product "MODEL_BRAIDED_HAT" should exists with the right data + And the product variant "BRAIDED_HAT_S" of product "MODEL_BRAIDED_HAT" should exists with the right data + And the product variant "BRAIDED_HAT_M" of product "MODEL_BRAIDED_HAT" should exists with the right data + And the product variant "BRAIDED_HAT_L" of product "MODEL_BRAIDED_HAT" should exists with the right data diff --git a/features/importing_products_associations.feature b/features/importing_products_associations.feature deleted file mode 100644 index 7e05c0b3..00000000 --- a/features/importing_products_associations.feature +++ /dev/null @@ -1,15 +0,0 @@ -@importing_products_associations -Feature: Enqueuing products associations - In order to import my products associations from Akeneo - As a Store Owner - I want to add them to the Akeneo PIM queue - - @cli - Scenario: Enqueuing products associations for products modified since a given date - Given there is a product "product-1" updated at "2020-01-10 22:23:13" on Akeneo - And there is a product "product-2" updated at "2020-01-21 09:54:12" on Akeneo - And there is a product "product-3" updated at "2020-01-22 08:15:08" on Akeneo - When I import items for all importers modified since date "2020-01-20 01:00:00" - Then the queue item with identifier "product-1" for the "ProductAssociations" importer should not be in the Akeneo queue - And the queue item with identifier "product-2" for the "ProductAssociations" importer should be in the Akeneo queue - And the queue item with identifier "product-3" for the "ProductAssociations" importer should be in the Akeneo queue diff --git a/features/importing_products_from_queue.feature b/features/importing_products_from_queue.feature deleted file mode 100644 index e3986b09..00000000 --- a/features/importing_products_from_queue.feature +++ /dev/null @@ -1,45 +0,0 @@ -@importing_products -Feature: Importing products from queue - In order to show updated data about my products - As a Store Owner - I want to import products from Akeneo PIM queue - - @cli - Scenario: Importing single product model and its variants from queue - Given the store operates on a single channel - And the store is also available in "it_IT" - And there is one item to import with identifier "braided-hat-m" for the "Product" importer in the Akeneo queue - And there is one item to import with identifier "braided-hat-l" for the "Product" importer in the Akeneo queue - When I import all from Akeneo - Then the product "model-braided-hat" should exists with the right data - And the product variant "braided-hat-m" of product "model-braided-hat" should exists with the right data - And the product variant "braided-hat-l" of product "model-braided-hat" should exists with the right data - - @cli - Scenario: Keeping the queue item as not imported while importing non existent product model from queue - Given the store operates on a single channel - And the store is also available in "it_IT" - And there is one item to import with identifier "NOT_EXISTS" for the "Product" importer in the Akeneo queue - When I import all from Akeneo - Then the item import message for "NOT_EXISTS" identifier and the "Product" importer should have failed - And the product "NOT_EXISTS" should not exists - - @cli - Scenario: Going on with subsequent product imports when any fail - Given the store operates on a single channel - And the store is also available in "it_IT" - And there is one item to import with identifier "NOT_EXISTS" for the "Product" importer in the Akeneo queue - And there is one item to import with identifier "braided-hat-m" for the "Product" importer in the Akeneo queue - When I import all from Akeneo - Then the item import message for "NOT_EXISTS" identifier and the "Product" importer should have failed - And the product "NOT_EXISTS" should not exists - And the product variant "braided-hat-m" of product "model-braided-hat" should exists with the right data - - @cli - Scenario: Importing products with images should not leave temporary files in temporary files directory - Given the store operates on a single channel - And the store is also available in "it_IT" - And there is one item to import with identifier "braided-hat-m" for the "Product" importer in the Akeneo queue - And there is one item to import with identifier "braided-hat-l" for the "Product" importer in the Akeneo queue - When I import all from Akeneo - Then there should not be any temporary file in the temporary files directory diff --git a/tests/Behat/Resources/suites.yml b/tests/Behat/Resources/suites.yml index 505807b2..116705ce 100644 --- a/tests/Behat/Resources/suites.yml +++ b/tests/Behat/Resources/suites.yml @@ -8,14 +8,36 @@ default: - sylius.behat.context.setup.channel - sylius.behat.context.setup.locale - webgriffe_sylius_akeneo.behat.context.setup.queue + - webgriffe_sylius_akeneo.behat.context.setup.akeneo - webgriffe_sylius_akeneo.behat.context.db.product - - webgriffe_sylius_akeneo.behat.context.messenger + - webgriffe_sylius_akeneo.behat.context.cli.import_command - webgriffe_sylius_akeneo.behat.context.system.filesystem filters: tags: "@importing_products && @cli" + ui_importing_products: + contexts: + - sylius.behat.context.hook.doctrine_orm + + - sylius.behat.context.transform.shared_storage + - sylius.behat.context.transform.product + + - sylius.behat.context.setup.channel + - sylius.behat.context.setup.admin_security + - sylius.behat.context.setup.product + - webgriffe_sylius_akeneo.behat.context.setup.akeneo + - webgriffe_sylius_akeneo.behat.context.setup.queue + + - webgriffe_sylius_akeneo.behat.context.db.product + + - sylius.behat.context.ui.admin.managing_products + - webgriffe_sylius_akeneo.behat.context.ui.admin.managing_products + + filters: + tags: "@importing_products && @ui" + cli_importing_product_associations: contexts: - sylius.behat.context.hook.doctrine_orm diff --git a/tests/Integration/DataFixtures/ApiClientMock/Product/braided-hat-l.json b/tests/Integration/DataFixtures/ApiClientMock/Product/BRAIDED_HAT_L.json similarity index 97% rename from tests/Integration/DataFixtures/ApiClientMock/Product/braided-hat-l.json rename to tests/Integration/DataFixtures/ApiClientMock/Product/BRAIDED_HAT_L.json index 2728e686..e2d5ae52 100644 --- a/tests/Integration/DataFixtures/ApiClientMock/Product/braided-hat-l.json +++ b/tests/Integration/DataFixtures/ApiClientMock/Product/BRAIDED_HAT_L.json @@ -1,5 +1,5 @@ { - "identifier": "braided-hat-l", + "identifier": "BRAIDED_HAT_L", "enabled": true, "family": "accessories", "categories": [ @@ -7,7 +7,7 @@ "master_accessories_hats" ], "groups": [], - "parent": "model-braided-hat", + "parent": "MODEL_BRAIDED_HAT", "values": { "size": [ { diff --git a/tests/Integration/DataFixtures/ApiClientMock/Product/braided-hat-m.json b/tests/Integration/DataFixtures/ApiClientMock/Product/BRAIDED_HAT_M.json similarity index 98% rename from tests/Integration/DataFixtures/ApiClientMock/Product/braided-hat-m.json rename to tests/Integration/DataFixtures/ApiClientMock/Product/BRAIDED_HAT_M.json index 1e5d188a..a0b069e8 100644 --- a/tests/Integration/DataFixtures/ApiClientMock/Product/braided-hat-m.json +++ b/tests/Integration/DataFixtures/ApiClientMock/Product/BRAIDED_HAT_M.json @@ -1,5 +1,5 @@ { - "identifier": "braided-hat-m", + "identifier": "BRAIDED_HAT_M", "enabled": true, "family": "accessories", "categories": [ @@ -7,7 +7,7 @@ "master_accessories_hats" ], "groups": [], - "parent": "model-braided-hat", + "parent": "MODEL_BRAIDED_HAT", "values": { "size": [ { diff --git a/tests/Integration/DataFixtures/ApiClientMock/Product/braided-hat-s.json b/tests/Integration/DataFixtures/ApiClientMock/Product/BRAIDED_HAT_S.json similarity index 97% rename from tests/Integration/DataFixtures/ApiClientMock/Product/braided-hat-s.json rename to tests/Integration/DataFixtures/ApiClientMock/Product/BRAIDED_HAT_S.json index 41afc02e..5dfbc5ec 100644 --- a/tests/Integration/DataFixtures/ApiClientMock/Product/braided-hat-s.json +++ b/tests/Integration/DataFixtures/ApiClientMock/Product/BRAIDED_HAT_S.json @@ -1,5 +1,5 @@ { - "identifier": "braided-hat-s", + "identifier": "BRAIDED_HAT_S", "enabled": false, "family": "accessories", "categories": [ @@ -7,7 +7,7 @@ "master_accessories_hats" ], "groups": [], - "parent": "model-braided-hat", + "parent": "MODEL_BRAIDED_HAT", "values": { "size": [ { diff --git a/tests/Integration/DataFixtures/ApiClientMock/ProductModel/model-braided-hat.json b/tests/Integration/DataFixtures/ApiClientMock/ProductModel/MODEL_BRAIDED_HAT.json similarity index 100% rename from tests/Integration/DataFixtures/ApiClientMock/ProductModel/model-braided-hat.json rename to tests/Integration/DataFixtures/ApiClientMock/ProductModel/MODEL_BRAIDED_HAT.json From 2bcac5cf50dee22971a5d00a6c4efbb754e37921 Mon Sep 17 00:00:00 2001 From: Lorenzo Ruozzi Date: Mon, 8 Aug 2022 11:46:31 +0200 Subject: [PATCH 17/24] Remove old things (#123) --- src/Entity/QueueItem.php | 0 src/Resources/config/config.yaml | 53 ------------------- .../views/QueueItem/Grid/importedAt.html.twig | 11 ---- 3 files changed, 64 deletions(-) delete mode 100644 src/Entity/QueueItem.php delete mode 100644 src/Resources/views/QueueItem/Grid/importedAt.html.twig diff --git a/src/Entity/QueueItem.php b/src/Entity/QueueItem.php deleted file mode 100644 index e69de29b..00000000 diff --git a/src/Resources/config/config.yaml b/src/Resources/config/config.yaml index 095b05c4..034d956e 100644 --- a/src/Resources/config/config.yaml +++ b/src/Resources/config/config.yaml @@ -3,59 +3,6 @@ sylius_grid: action: importProduct: '@WebgriffeSyliusAkeneoPlugin\Product\Grid\Action\import.html.twig' grids: - webgriffe_sylius_akeneo_admin_queue_item: - driver: - name: doctrine/orm - options: - class: Webgriffe\SyliusAkeneoPlugin\Entity\QueueItem - sorting: - createdAt: desc - fields: - akeneoEntity: - type: string - label: webgriffe_sylius_akeneo.ui.importer - sortable: ~ - akeneoIdentifier: - type: string - label: webgriffe_sylius_akeneo.ui.identifier - sortable: ~ - createdAt: - type: datetime - label: sylius.ui.created_at - sortable: ~ - importedAt: - type: twig - label: webgriffe_sylius_akeneo.ui.imported - sortable: ~ - options: - template: '@WebgriffeSyliusAkeneoPlugin\QueueItem\Grid\importedAt.html.twig' - errorMessage: - type: string - label: webgriffe_sylius_akeneo.ui.error_message - filters: - akeneoEntity: - type: string - label: webgriffe_sylius_akeneo.ui.importer - akeneoIdentifier: - type: string - label: webgriffe_sylius_akeneo.ui.identifier - imported: - type: exists - label: webgriffe_sylius_akeneo.ui.imported - options: - field: importedAt - errorMessage: - type: string - label: webgriffe_sylius_akeneo.ui.error_message - actions: - main: ~ - item: - delete: - type: delete - bulk: - delete: - type: delete - sylius_admin_product: actions: item: diff --git a/src/Resources/views/QueueItem/Grid/importedAt.html.twig b/src/Resources/views/QueueItem/Grid/importedAt.html.twig deleted file mode 100644 index 66a2cd92..00000000 --- a/src/Resources/views/QueueItem/Grid/importedAt.html.twig +++ /dev/null @@ -1,11 +0,0 @@ -{% if data %} - - - {{ 'webgriffe_sylius_akeneo.ui.imported_yes'|trans({'%date%': data|date('Y-m-d H:i:s')}) }} - -{% else %} - - - {{ 'webgriffe_sylius_akeneo.ui.imported_no'|trans }} - -{% endif %} From b780885bb8e1991389273d4f0e4c86eb83491d2d Mon Sep 17 00:00:00 2001 From: Lorenzo Ruozzi Date: Mon, 8 Aug 2022 12:03:04 +0200 Subject: [PATCH 18/24] Fix phpstan (#123) --- .../Context/Messenger/MessengerContext.php | 153 ------------------ .../Ui/Admin/ManagingProductsContext.php | 4 +- tests/Behat/Resources/services.xml | 5 - tests/Behat/Resources/suites.yml | 5 - 4 files changed, 3 insertions(+), 164 deletions(-) delete mode 100644 tests/Behat/Context/Messenger/MessengerContext.php diff --git a/tests/Behat/Context/Messenger/MessengerContext.php b/tests/Behat/Context/Messenger/MessengerContext.php deleted file mode 100644 index 51aa358e..00000000 --- a/tests/Behat/Context/Messenger/MessengerContext.php +++ /dev/null @@ -1,153 +0,0 @@ -getEnvelopeByImporterAndIdentifier($importer, $identifier), - ); - } - - /** - * @Then the queue item with identifier :identifier for the :importer importer should be in the Akeneo queue - */ - public function theQueueItemWithIdentifierForTheImporterShouldBeInTheAkeneoQueue(string $identifier, string $importer): void - { - $envelope = $this->getEnvelopeByImporterAndIdentifier($importer, $identifier); - Assert::notNull($envelope); - Assert::isInstanceOf( - $envelope->getMessage(), - ItemImport::class, - ); - } - - /** - * @Then there should be no item in the queue for the :importer importer - */ - public function thereShouldBeNoProductInTheAkeneoQueue(string $importer): void - { - Assert::isEmpty($this->getEnvelopesByImporter($importer)); - } - - /** - * @Then there should be no item in the Akeneo queue - */ - public function thereShouldBeNoItemInTheAkeneoQueue(): void - { - Assert::isEmpty($this->transport->get()); - } - - /** - * @Then /^there should be (\d+) items for the "([^"]*)" importer in the Akeneo queue$/ - */ - public function thereShouldBeItemsForTheImporterInTheAkeneoQueue(int $count, string $importer): void - { - $items = $this->getEnvelopesByImporter($importer); - Assert::count($items, $count); - } - - /** - * @Then there should be items for the :importer importer only in the Akeneo queue - */ - public function thereShouldBeItemsForTheImporterOnlyInTheAkeneoQueue(string $importer): void - { - /** @var Envelope[] $envelopes */ - $envelopes = $this->transport->get(); - $importerItems = $this->getEnvelopesByImporter($importer); - Assert::count($envelopes, count($importerItems)); - } - - private function getQueueItemByImporterAndIdentifier(string $importer, string $identifier): ItemImport - { - $sentMessages = $this->transport->get(); - foreach ($sentMessages as $sentMessage) { - /** @var ItemImport|object $message */ - $message = $sentMessage->getMessage(); - if ($message instanceof ItemImport && $message->getAkeneoEntity() === $importer && $message->getAkeneoIdentifier() === $identifier) { - return $message; - } - } - - throw new InvalidArgumentException(sprintf('No message founded for importer "%s" and identifier "%s".', $importer, $identifier)); - } - - /** - * @When I import all from Akeneo - */ - public function IImportAllFromAkeneo(): void - { - foreach ($this->transport->get() as $envelope) { - $message = $envelope->getMessage(); - if (!$message instanceof ItemImport) { - continue; - } - - try { - $this->itemImportHandler->__invoke($message); - } catch (Throwable $throwable) { - $this->failedMessages[] = $message; - - continue; - } - } - } - - /** - * @Then the item import message for :identifier identifier and the :importer importer should have failed - */ - public function theItemImportMessageForProductShouldHaveFailed(string $identifier, string $importer): void - { - $failedMessages = array_filter($this->failedMessages, static function (ItemImport $failedMessage) use ($identifier, $importer): bool { - return $failedMessage->getAkeneoIdentifier() === $identifier && $failedMessage->getAkeneoEntity() === $importer; - }); - Assert::count($failedMessages, 1); - } - - private function getEnvelopesByImporter(string $importer): array - { - /** @var Envelope[] $envelopes */ - $envelopes = $this->transport->get(); - - return array_filter($envelopes, static function (Envelope $envelope) use ($importer): bool { - /** @var ItemImport|mixed $message */ - $message = $envelope->getMessage(); - - return $message instanceof ItemImport && $message->getAkeneoEntity() === $importer; - }); - } - - private function getEnvelopeByImporterAndIdentifier(string $importer, string $identifier): ?Envelope - { - /** @var Envelope[] $envelopes */ - $envelopes = $this->transport->get(); - $envelopes = array_filter($envelopes, static function (Envelope $envelope) use ($importer, $identifier): bool { - /** @var ItemImport|mixed $message */ - $message = $envelope->getMessage(); - - return $message instanceof ItemImport && $message->getAkeneoEntity() === $importer && $message->getAkeneoIdentifier() === $identifier; - }); - $envelope = reset($envelopes); - if (!$envelope instanceof Envelope) { - return null; - } - - return $envelope; - } -} diff --git a/tests/Behat/Context/Ui/Admin/ManagingProductsContext.php b/tests/Behat/Context/Ui/Admin/ManagingProductsContext.php index e752fce8..edd70750 100644 --- a/tests/Behat/Context/Ui/Admin/ManagingProductsContext.php +++ b/tests/Behat/Context/Ui/Admin/ManagingProductsContext.php @@ -58,9 +58,10 @@ public function iShouldBeNotifiedThatItHasBeenSuccessfullyEnqueued(): void public function theProductShouldHaveAnAssociationWithProducts( string $code, ProductAssociationTypeInterface $productAssociationType, - ...$productsNames, + string ...$productsNames, ): void { $product = $this->productRepository->findOneByCode($code); + Assert::isInstanceOf($product, ProductInterface::class); $this->updateSimpleProductPage->open(['id' => $product->getId()]); foreach ($productsNames as $productName) { Assert::true( @@ -83,6 +84,7 @@ public function theProductShouldNotHaveAnAssociationWithProduct( string $productName, ): void { $product = $this->productRepository->findOneByCode($code); + Assert::isInstanceOf($product, ProductInterface::class); $this->updateSimpleProductPage->open(['id' => $product->getId()]); Assert::false( $this->updateSimpleProductPage->hasAssociatedProduct($productName, $productAssociationType), diff --git a/tests/Behat/Resources/services.xml b/tests/Behat/Resources/services.xml index 0b09cbff..82b82145 100644 --- a/tests/Behat/Resources/services.xml +++ b/tests/Behat/Resources/services.xml @@ -31,11 +31,6 @@ - - - - - %webgriffe_sylius_akeneo.temporary_directory% %webgriffe_sylius_akeneo.temporary_files_prefix% diff --git a/tests/Behat/Resources/suites.yml b/tests/Behat/Resources/suites.yml index 116705ce..e7c4d102 100644 --- a/tests/Behat/Resources/suites.yml +++ b/tests/Behat/Resources/suites.yml @@ -89,8 +89,6 @@ default: - webgriffe_sylius_akeneo.behat.context.cli.import_command - - webgriffe_sylius_akeneo.behat.context.messenger - - webgriffe_sylius_akeneo.behat.context.system.filesystem - webgriffe_sylius_akeneo.behat.context.system.datetime @@ -110,7 +108,6 @@ default: - webgriffe_sylius_akeneo.behat.context.setup.queue - sylius.behat.context.ui.admin.managing_products - - webgriffe_sylius_akeneo.behat.context.messenger - webgriffe_sylius_akeneo.behat.context.ui.admin.managing_products filters: tags: "@enqueuing_products && @ui" @@ -126,8 +123,6 @@ default: - webgriffe_sylius_akeneo.behat.context.cli.import_command - - webgriffe_sylius_akeneo.behat.context.messenger - - webgriffe_sylius_akeneo.behat.context.system.filesystem - webgriffe_sylius_akeneo.behat.context.system.datetime From 2a880fd0ca599d6b53f2115b43a7b5d943628a87 Mon Sep 17 00:00:00 2001 From: Lorenzo Ruozzi Date: Mon, 8 Aug 2022 12:23:06 +0200 Subject: [PATCH 19/24] Fix phpunit (#123) --- ...odel-braided-hat-with-variation-image.yaml | 2 +- .../ProductVariant/braided-hat-l.yaml | 2 +- .../ProductVariant/braided-hat-m.yaml | 2 +- .../braided-hat-m/with-channel-pricings.yaml | 2 +- .../braided-hat-m/without-option-values.yaml | 2 +- tests/Integration/Product/ImporterTest.php | 50 +++++++++---------- 6 files changed, 30 insertions(+), 30 deletions(-) diff --git a/tests/Integration/DataFixtures/ORM/resources/Product/model-braided-hat-with-variation-image.yaml b/tests/Integration/DataFixtures/ORM/resources/Product/model-braided-hat-with-variation-image.yaml index 9c6fd159..6f1ddbfb 100644 --- a/tests/Integration/DataFixtures/ORM/resources/Product/model-braided-hat-with-variation-image.yaml +++ b/tests/Integration/DataFixtures/ORM/resources/Product/model-braided-hat-with-variation-image.yaml @@ -29,7 +29,7 @@ Sylius\Component\Core\Model\Product: model-braided-hat: fallbackLocale: "en_US" currentLocale: "en_US" - code: "model-braided-hat" + code: "MODEL_BRAIDED_HAT" translations: - "@model-braided-hat_en_US" - "@model-braided-hat_it_IT" diff --git a/tests/Integration/DataFixtures/ORM/resources/ProductVariant/braided-hat-l.yaml b/tests/Integration/DataFixtures/ORM/resources/ProductVariant/braided-hat-l.yaml index a8f2aa1a..cc9aef91 100644 --- a/tests/Integration/DataFixtures/ORM/resources/ProductVariant/braided-hat-l.yaml +++ b/tests/Integration/DataFixtures/ORM/resources/ProductVariant/braided-hat-l.yaml @@ -1,6 +1,6 @@ Sylius\Component\Core\Model\ProductVariant: braided-hat-l: - code: 'braided-hat-l' + code: 'BRAIDED_HAT_L' product: '@model-braided-hat' optionValues: - '@size_l' diff --git a/tests/Integration/DataFixtures/ORM/resources/ProductVariant/braided-hat-m.yaml b/tests/Integration/DataFixtures/ORM/resources/ProductVariant/braided-hat-m.yaml index 24d3be9e..8593b557 100644 --- a/tests/Integration/DataFixtures/ORM/resources/ProductVariant/braided-hat-m.yaml +++ b/tests/Integration/DataFixtures/ORM/resources/ProductVariant/braided-hat-m.yaml @@ -1,6 +1,6 @@ Sylius\Component\Core\Model\ProductVariant: braided-hat-m: - code: 'braided-hat-m' + code: 'BRAIDED_HAT_M' product: '@model-braided-hat' optionValues: - '@size_m' diff --git a/tests/Integration/DataFixtures/ORM/resources/ProductVariant/braided-hat-m/with-channel-pricings.yaml b/tests/Integration/DataFixtures/ORM/resources/ProductVariant/braided-hat-m/with-channel-pricings.yaml index bb83521c..c44a65e4 100644 --- a/tests/Integration/DataFixtures/ORM/resources/ProductVariant/braided-hat-m/with-channel-pricings.yaml +++ b/tests/Integration/DataFixtures/ORM/resources/ProductVariant/braided-hat-m/with-channel-pricings.yaml @@ -11,7 +11,7 @@ Sylius\Component\Core\Model\ChannelPricing: Sylius\Component\Core\Model\ProductVariant: braided-hat-m: - code: 'braided-hat-m' + code: 'BRAIDED_HAT_M' product: '@model-braided-hat' optionValues: - '@size_m' diff --git a/tests/Integration/DataFixtures/ORM/resources/ProductVariant/braided-hat-m/without-option-values.yaml b/tests/Integration/DataFixtures/ORM/resources/ProductVariant/braided-hat-m/without-option-values.yaml index b1380c26..be71bf80 100644 --- a/tests/Integration/DataFixtures/ORM/resources/ProductVariant/braided-hat-m/without-option-values.yaml +++ b/tests/Integration/DataFixtures/ORM/resources/ProductVariant/braided-hat-m/without-option-values.yaml @@ -1,4 +1,4 @@ Sylius\Component\Core\Model\ProductVariant: braided-hat-m: - code: 'braided-hat-m' + code: 'BRAIDED_HAT_M' product: '@model-braided-hat' diff --git a/tests/Integration/Product/ImporterTest.php b/tests/Integration/Product/ImporterTest.php index 90b76438..7e3b1832 100644 --- a/tests/Integration/Product/ImporterTest.php +++ b/tests/Integration/Product/ImporterTest.php @@ -57,14 +57,14 @@ public function it_creates_new_product_variant_and_its_product_when_importing_va PurgeMode::createDeleteMode(), ); - $this->importer->import('braided-hat-m'); + $this->importer->import('BRAIDED_HAT_M'); /** @var ProductVariantInterface[] $allVariants */ $allVariants = $this->productVariantRepository->findAll(); $this->assertCount(1, $allVariants); $this->assertInstanceOf(ProductVariantInterface::class, $allVariants[0]); $this->assertInstanceOf(ProductInterface::class, $allVariants[0]->getProduct()); - $this->assertEquals('model-braided-hat', $allVariants[0]->getProduct()->getCode()); + $this->assertEquals('MODEL_BRAIDED_HAT', $allVariants[0]->getProduct()->getCode()); } /** @@ -83,7 +83,7 @@ public function it_creates_proper_product_option_value_with_translations_if_miss PurgeMode::createDeleteMode(), ); - $this->importer->import('braided-hat-m'); + $this->importer->import('BRAIDED_HAT_M'); /** @var ProductVariantInterface $variant */ $variant = $this->productVariantRepository->findAll()[0]; @@ -114,7 +114,7 @@ public function it_updates_alredy_existent_product_option_value() PurgeMode::createDeleteMode(), ); - $this->importer->import('braided-hat-m'); + $this->importer->import('BRAIDED_HAT_M'); /** @var ProductVariantInterface $variant */ $variant = $this->productVariantRepository->findAll()[0]; @@ -143,7 +143,7 @@ public function it_creates_missing_translations_while_updating_alredy_existent_p PurgeMode::createDeleteMode(), ); - $this->importer->import('braided-hat-m'); + $this->importer->import('BRAIDED_HAT_M'); /** @var ProductVariantInterface $variant */ $variant = $this->productVariantRepository->findAll()[0]; @@ -171,7 +171,7 @@ public function it_creates_missing_option_value_while_updating_alredy_existent_p PurgeMode::createDeleteMode(), ); - $this->importer->import('braided-hat-m'); + $this->importer->import('BRAIDED_HAT_M'); /** @var ProductVariantInterface $variant */ $variant = $this->productVariantRepository->findAll()[0]; @@ -259,7 +259,7 @@ public function it_sets_channel_price_value_on_product_variant_according_to_chan /** @var ChannelInterface $europeChannel */ $europeChannel = $this->channelRepository->findOneByCode('europe'); - $this->importer->import('braided-hat-m'); + $this->importer->import('BRAIDED_HAT_M'); /** @var ProductVariantInterface $variant */ $variant = $this->productVariantRepository->findAll()[0]; @@ -303,7 +303,7 @@ public function it_updates_channel_price_value_on_product_variant_according_chan /** @var ChannelInterface $europeChannel */ $europeChannel = $this->channelRepository->findOneByCode('europe'); - $this->importer->import('braided-hat-m'); + $this->importer->import('BRAIDED_HAT_M'); /** @var ProductVariantInterface $variant */ $variant = $this->productVariantRepository->findAll()[0]; @@ -338,7 +338,7 @@ public function it_sets_all_channels_to_imported_products() PurgeMode::createDeleteMode(), ); - $this->importer->import('braided-hat-m'); + $this->importer->import('BRAIDED_HAT_M'); /** @var ProductInterface $product */ $product = $this->productRepository->findAll()[0]; @@ -359,8 +359,8 @@ public function it_imports_all_product_images_when_importing_variants_of_configu PurgeMode::createDeleteMode(), ); - $this->importer->import('braided-hat-m'); - $this->importer->import('braided-hat-l'); + $this->importer->import('BRAIDED_HAT_M'); + $this->importer->import('BRAIDED_HAT_L'); /** @var ProductVariantInterface[] $allVariants */ $allVariants = $this->productVariantRepository->findAll(); @@ -368,7 +368,7 @@ public function it_imports_all_product_images_when_importing_variants_of_configu $this->assertInstanceOf(ProductVariantInterface::class, $allVariants[0]); $product = $allVariants[0]->getProduct(); $this->assertInstanceOf(ProductInterface::class, $product); - $this->assertEquals('model-braided-hat', $product->getCode()); + $this->assertEquals('MODEL_BRAIDED_HAT', $product->getCode()); $this->assertCount(2, $product->getImages()); /** @var ProductImageInterface $image */ foreach ($product->getImages() as $image) { @@ -511,9 +511,9 @@ public function it_imports_product_as_enabled_even_if_is_disabled_on_akeneo_but_ PurgeMode::createDeleteMode(), ); - $this->importer->import('braided-hat-s'); + $this->importer->import('BRAIDED_HAT_S'); - $product = $this->productRepository->findOneByCode('model-braided-hat'); + $product = $this->productRepository->findOneByCode('MODEL_BRAIDED_HAT'); $this->assertTrue($product->isEnabled()); } @@ -534,9 +534,9 @@ public function it_imports_variant_of_a_configurable_product_as_disabled_if_it_i PurgeMode::createDeleteMode(), ); - $this->importer->import('braided-hat-s'); + $this->importer->import('BRAIDED_HAT_S'); - $productVariant = $this->productVariantRepository->findOneByCode('braided-hat-s'); + $productVariant = $this->productVariantRepository->findOneByCode('BRAIDED_HAT_S'); $this->assertFalse($productVariant->isEnabled()); } @@ -556,10 +556,10 @@ public function it_updates_existing_product_attribute_value() PurgeMode::createDeleteMode(), ); - $this->importer->import('braided-hat-m'); + $this->importer->import('BRAIDED_HAT_M'); /** @var ProductInterface $product */ - $product = $this->productRepository->findOneByCode('model-braided-hat'); + $product = $this->productRepository->findOneByCode('MODEL_BRAIDED_HAT'); $this->assertEquals('cotton', $product->getAttributeByCodeAndLocale('material', 'en_US')->getValue()); $this->assertEquals('cotone', $product->getAttributeByCodeAndLocale('material', 'it_IT')->getValue()); } @@ -580,7 +580,7 @@ public function it_downloads_file_from_akeneo() PurgeMode::createDeleteMode(), ); - $this->importer->import('braided-hat-m'); + $this->importer->import('BRAIDED_HAT_M'); $this->assertTrue( $this->filesystem->exists( self::$container->getParameter( @@ -636,9 +636,9 @@ public function it_removes_product_images_removed_on_akeneo() PurgeMode::createDeleteMode(), ); - $this->importer->import('braided-hat-m'); + $this->importer->import('BRAIDED_HAT_M'); - $product = $this->productRepository->findOneByCode('model-braided-hat'); + $product = $this->productRepository->findOneByCode('MODEL_BRAIDED_HAT'); $this->assertCount(1, $product->getImages()); } @@ -662,9 +662,9 @@ public function it_does_not_remove_product_images_of_other_variants_removed_on_a PurgeMode::createDeleteMode(), ); - $this->importer->import('braided-hat-l'); + $this->importer->import('BRAIDED_HAT_L'); - $product = $this->productRepository->findOneByCode('model-braided-hat'); + $product = $this->productRepository->findOneByCode('MODEL_BRAIDED_HAT'); $this->assertCount(2, $product->getImages()); } @@ -708,10 +708,10 @@ public function it_removes_existing_product_attributes_values_if_they_are_empty_ PurgeMode::createDeleteMode(), ); - $this->importer->import('braided-hat-m'); + $this->importer->import('BRAIDED_HAT_M'); /** @var ProductInterface $product */ - $product = $this->productRepository->findOneByCode('model-braided-hat'); + $product = $this->productRepository->findOneByCode('MODEL_BRAIDED_HAT'); $this->assertFalse($product->hasAttributeByCodeAndLocale('supplier', 'it_IT')); $this->assertFalse($product->hasAttributeByCodeAndLocale('supplier', 'en_US')); } From 49d9263837bffbd7bce212940533bb4d8f87f425 Mon Sep 17 00:00:00 2001 From: Lorenzo Ruozzi Date: Mon, 8 Aug 2022 12:34:02 +0200 Subject: [PATCH 20/24] Fix psalm (#123) --- src/Product/Importer.php | 1 + src/ProductAssociations/Importer.php | 1 + 2 files changed, 2 insertions(+) diff --git a/src/Product/Importer.php b/src/Product/Importer.php index fe7629e6..0b88d1f3 100644 --- a/src/Product/Importer.php +++ b/src/Product/Importer.php @@ -104,6 +104,7 @@ public function getIdentifiersModifiedSince(DateTime $sinceDate): array $products = $this->apiClient->getProductApi()->all(50, ['search' => $searchBuilder->getFilters()]); $identifiers = []; foreach ($products as $product) { + Assert::isArray($product); Assert::keyExists($product, 'identifier'); $productIdentifier = (string) $product['identifier']; Assert::stringNotEmpty($productIdentifier); diff --git a/src/ProductAssociations/Importer.php b/src/ProductAssociations/Importer.php index 3f08d3f4..ba1ff52c 100644 --- a/src/ProductAssociations/Importer.php +++ b/src/ProductAssociations/Importer.php @@ -131,6 +131,7 @@ public function getIdentifiersModifiedSince(DateTime $sinceDate): array $products = $this->apiClient->getProductApi()->all(50, ['search' => $searchBuilder->getFilters()]); $identifiers = []; foreach ($products as $product) { + Assert::isArray($product); Assert::keyExists($product, 'identifier'); $productIdentifier = (string) $product['identifier']; Assert::stringNotEmpty($productIdentifier); From f10a37a31c0e4ab103f79758e8a8359b61e3b0da Mon Sep 17 00:00:00 2001 From: Lorenzo Ruozzi Date: Mon, 8 Aug 2022 14:15:25 +0200 Subject: [PATCH 21/24] Update README.md Co-authored-by: Manuele Menozzi --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a482a981..0b05cf1d 100644 --- a/README.md +++ b/README.md @@ -379,7 +379,7 @@ This plugin will also import product associations. It's a zero configuration imp ### Launch data import from CLI -To actually import data you must first create queue items with the **import command** and then you can import them with the **consume command**. +To actually import data from CLI you can use the **import command** (see below). #### Schedule Akeneo PIM import button From 005113487dc83280d224beaacbee137377904600 Mon Sep 17 00:00:00 2001 From: Lorenzo Ruozzi Date: Fri, 12 Aug 2022 15:49:37 +0200 Subject: [PATCH 22/24] Apply suggestions from code review Co-authored-by: Manuele Menozzi --- README.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/README.md b/README.md index 0b05cf1d..74f6f963 100644 --- a/README.md +++ b/README.md @@ -476,8 +476,6 @@ To make all importers and other plugin features work automatically the following ``` 0 * * * * /path/to/sylius/bin/console -e prod -q webgriffe:akeneo:import --all --importer="AttributeOptions" * * * * * /path/to/sylius/bin/console -e prod -q webgriffe:akeneo:import --since-file=/path/to/sylius/var/storage/akeneo-import-sincefile.txt --importer="Product" --importer="ProductAssociations" -* * * * * /path/to/sylius/bin/console -e prod -q webgriffe:akeneo:consume -0 0 * * * /path/to/sylius/bin/console -e prod -q webgriffe:akeneo:cleanup-queue 0 */6 * * * /path/to/sylius/bin/console -e prod -q webgriffe:akeneo:reconcile ``` @@ -487,7 +485,7 @@ This will: * Clean the imported items queue older than 10 days, every day at midnight * Reconcile Akeneo deleted products every 6 hours -Import, Consume and Reconcile commands uses a [lock mechanism](https://symfony.com/doc/current/console/lockable_trait.html) which prevents running them if another instance of the same command is already running. +Import and Reconcile commands uses a [lock mechanism](https://symfony.com/doc/current/console/lockable_trait.html) which prevents running them if another instance of the same command is already running. ## Architecture & customization From 418d2e6553869afaba4737e9fb8362a15c2e1f1f Mon Sep 17 00:00:00 2001 From: Lorenzo Ruozzi Date: Fri, 12 Aug 2022 16:01:27 +0200 Subject: [PATCH 23/24] Remove consume and clear queue comamnds (#123) --- README.md | 36 +----------------------------------- 1 file changed, 1 insertion(+), 35 deletions(-) diff --git a/README.md b/README.md index 74f6f963..b91f2605 100644 --- a/README.md +++ b/README.md @@ -423,41 +423,7 @@ You can also import items regardless of their last update date: ```bash bin/console webgriffe:akeneno:import --all ``` - -#### Consume command - -To import the Akeneo entities that are in the queue you can use the `webgriffe:akeneo:consume` console command: - -```bash -bin/console webgriffe:akeneo:consume -``` - -This will consume all queue items which are not imported yet. - -Of course you can put this command in cron as well: - -``` -* * * * * /usr/bin/php /path/to/sylius/bin/console -e prod -q webgriffe:akeneo:consume -``` - -### Browsing queue items in the admin - -You can examine the Akeneo import queue from the admin panel at **Catalog -> Akeneo PIM import**. You can filter and sort items and see their error message: - -![Akeneo queue items grid](queue_items_grid.png) - -### Queue clear - -Sometimes it may be a good idea to clear the queue of imported items. This can also be useful in development to keep only items not imported due to errors or yet to be consumed. -You can clear the queue of elements by launching the following command: - -```bash -bin/console webgriffe:akeneo:cleanup-queue {days} -``` - -Where {days} should be replaced by the number of days back from which to start deleting the queue of items. -If the number is not entered, the default value 10 will be used. -So, if for example today is 2020-12-15 and you use the parameter days = 10, all the elements imported before 2020-12-05 will be deleted. +Import command assume that Symfony Messenger is working as required since Sylius v1.11. ### Products reconciliation From 17eb2e63928abf3861cdfcb12afb242428555c01 Mon Sep 17 00:00:00 2001 From: Lorenzo Ruozzi Date: Mon, 29 Aug 2022 12:19:28 +0200 Subject: [PATCH 24/24] Apply suggested changes (#123) --- features/importing_generic_items.feature | 2 +- .../Context/Cli/ImportCommandContext.php | 4 +- tests/Behat/Resources/suites.yml | 65 ------------------- 3 files changed, 3 insertions(+), 68 deletions(-) diff --git a/features/importing_generic_items.feature b/features/importing_generic_items.feature index b3f038e7..ff05f506 100644 --- a/features/importing_generic_items.feature +++ b/features/importing_generic_items.feature @@ -56,7 +56,7 @@ Feature: Importing items And the product with code "11164822" should not have an association "Pack" with product "10597353" @cli @ui - Scenario: Enqueuing all items for a not existent importer + Scenario: Importing all items for a not existent importer When I import all items for a not existent importer Then I should be notified that the importer does not exists When I browse products diff --git a/tests/Behat/Context/Cli/ImportCommandContext.php b/tests/Behat/Context/Cli/ImportCommandContext.php index f169f3e1..a5a2ead3 100644 --- a/tests/Behat/Context/Cli/ImportCommandContext.php +++ b/tests/Behat/Context/Cli/ImportCommandContext.php @@ -19,7 +19,7 @@ final class ImportCommandContext implements Context { public function __construct( private KernelInterface $kernel, - private ImportCommand $enqueueCommand, + private ImportCommand $importCommand, private SharedStorageInterface $sharedStorage, ) { } @@ -185,7 +185,7 @@ public function iShouldBeNotifiedThatTheImporterDoesNotExists(): void private function getCommandTester(): CommandTester { $application = new Application($this->kernel); - $application->add($this->enqueueCommand); + $application->add($this->importCommand); $command = $application->find('webgriffe:akeneo:import'); return new CommandTester($command); diff --git a/tests/Behat/Resources/suites.yml b/tests/Behat/Resources/suites.yml index e7c4d102..7652b896 100644 --- a/tests/Behat/Resources/suites.yml +++ b/tests/Behat/Resources/suites.yml @@ -78,57 +78,6 @@ default: filters: tags: "@importing_generic_items && @cli && @ui" - cli_enqueuing_products: - contexts: - - sylius.behat.context.hook.doctrine_orm - - - sylius.behat.context.transform.date_time - - - webgriffe_sylius_akeneo.behat.context.setup.akeneo - - webgriffe_sylius_akeneo.behat.context.setup.queue - - - webgriffe_sylius_akeneo.behat.context.cli.import_command - - - webgriffe_sylius_akeneo.behat.context.system.filesystem - - webgriffe_sylius_akeneo.behat.context.system.datetime - - filters: - tags: "@enqueuing_products && @cli" - - ui_enqueuing_products: - contexts: - - sylius.behat.context.hook.doctrine_orm - - - sylius.behat.context.transform.shared_storage - - sylius.behat.context.transform.product - - - sylius.behat.context.setup.channel - - sylius.behat.context.setup.admin_security - - sylius.behat.context.setup.product - - webgriffe_sylius_akeneo.behat.context.setup.queue - - - sylius.behat.context.ui.admin.managing_products - - webgriffe_sylius_akeneo.behat.context.ui.admin.managing_products - filters: - tags: "@enqueuing_products && @ui" - - cli_enqueuing_products_associations: - contexts: - - sylius.behat.context.hook.doctrine_orm - - - sylius.behat.context.transform.date_time - - - webgriffe_sylius_akeneo.behat.context.setup.akeneo - - webgriffe_sylius_akeneo.behat.context.setup.queue - - - webgriffe_sylius_akeneo.behat.context.cli.import_command - - - webgriffe_sylius_akeneo.behat.context.system.filesystem - - webgriffe_sylius_akeneo.behat.context.system.datetime - - filters: - tags: "@enqueuing_products_associations && @cli" - cli_reconcile_products: contexts: - sylius.behat.context.hook.doctrine_orm @@ -148,17 +97,3 @@ default: - webgriffe_sylius_akeneo.behat.context.cli.reconcile_command filters: tags: "@reconcile_products && @cli" - - ui_managing_queue_items: - contexts: - - sylius.behat.context.hook.doctrine_orm - - - sylius.behat.context.transform.shared_storage - - - sylius.behat.context.setup.admin_security - - webgriffe_sylius_akeneo.behat.context.setup.queue - - - sylius.behat.context.ui.admin.notification - - filters: - tags: "@managing_queue_items && @ui"