diff --git a/README.md b/README.md index 53299d1e..b4628208 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,7 @@ + [Enqueue command](#enqueue-command) + [Consume command](#consume-command) * [Automatically import data with cron jobs](#automatically-import-data-with-cron-jobs) + * [Browsing queue items in the admin](#browsing-queue-items-in-the-admin) - [Architecture & customization](#architecture---customization) * [Product Importer](#product-importer) + [Taxons resolver](#taxons-resolver) @@ -405,6 +406,12 @@ It will enqueue the update of all attribute options every hour and it will impor Both enqueue and consume commands uses a [lock mechanism](https://symfony.com/doc/current/console/lockable_trait.html) which prevents to run them multiple times. +### 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) + ## Architecture & customization This plugin has basically two main entry points: diff --git a/features/browsing_queue_items.feature b/features/browsing_queue_items.feature new file mode 100644 index 00000000..efa0f0ba --- /dev/null +++ b/features/browsing_queue_items.feature @@ -0,0 +1,44 @@ +@browsing_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/queue_items_grid.png b/queue_items_grid.png new file mode 100644 index 00000000..4cafe34b Binary files /dev/null and b/queue_items_grid.png differ diff --git a/src/Menu/AdminMenuListener.php b/src/Menu/AdminMenuListener.php new file mode 100644 index 00000000..96c8814e --- /dev/null +++ b/src/Menu/AdminMenuListener.php @@ -0,0 +1,25 @@ +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/admin_routing.yaml b/src/Resources/config/admin_routing.yaml new file mode 100644 index 00000000..7b71fafa --- /dev/null +++ b/src/Resources/config/admin_routing.yaml @@ -0,0 +1,15 @@ +webgriffe_sylius_akeneo_admin_queue_item: + resource: | + alias: webgriffe_sylius_akeneo.queue_item + section: admin + path: akeneo_queue_items + except: ['delete', 'update', 'create', 'show', 'bulkDelete'] + templates: "@SyliusAdmin\\Crud" + redirect: update + grid: webgriffe_sylius_akeneo_admin_queue_item + vars: + all: + subheader: webgriffe_sylius_akeneo.ui.manage_akeneo_queue_items + index: + icon: 'cloud download' + type: sylius.resource diff --git a/src/Resources/config/config.yaml b/src/Resources/config/config.yaml new file mode 100644 index 00000000..4478b488 --- /dev/null +++ b/src/Resources/config/config.yaml @@ -0,0 +1,56 @@ +sylius_resource: + resources: + webgriffe_sylius_akeneo.queue_item: + classes: + model: Webgriffe\SyliusAkeneoPlugin\Entity\QueueItem + repository: Webgriffe\SyliusAkeneoPlugin\Doctrine\ORM\QueueItemRepository + +sylius_grid: + 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: ~ diff --git a/src/Resources/config/services.xml b/src/Resources/config/services.xml index 300cea54..29954763 100644 --- a/src/Resources/config/services.xml +++ b/src/Resources/config/services.xml @@ -61,6 +61,10 @@ + + + + diff --git a/src/Resources/translations/messages.en.yaml b/src/Resources/translations/messages.en.yaml new file mode 100644 index 00000000..7b77e3f4 --- /dev/null +++ b/src/Resources/translations/messages.en.yaml @@ -0,0 +1,10 @@ +webgriffe_sylius_akeneo: + ui: + queue_items: Akeneo PIM import + manage_akeneo_queue_items: Manage Akeneo PIM import items + importer: Importer type + identifier: Identifier + imported: Imported + imported_yes: Yes, at %date% + imported_no: No + error_message: Error message diff --git a/src/Resources/translations/messages.it.yaml b/src/Resources/translations/messages.it.yaml new file mode 100644 index 00000000..00f66963 --- /dev/null +++ b/src/Resources/translations/messages.it.yaml @@ -0,0 +1,10 @@ +webgriffe_sylius_akeneo: + ui: + queue_items: Importazione da Akeneo PIM + manage_akeneo_queue_items: Gestisci gli elementi di importazione da Akeneo PIM + importer: Tipo di import + identifier: Identificatore + imported: Importato + imported_yes: Si, il %date% + imported_no: No + error_message: Messaggio d'errore diff --git a/src/Resources/views/QueueItem/Grid/importedAt.html.twig b/src/Resources/views/QueueItem/Grid/importedAt.html.twig new file mode 100644 index 00000000..66a2cd92 --- /dev/null +++ b/src/Resources/views/QueueItem/Grid/importedAt.html.twig @@ -0,0 +1,11 @@ +{% 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 %} diff --git a/tests/Application/config/packages/webgriffe_sylius_akeneo_plugin.yaml b/tests/Application/config/packages/webgriffe_sylius_akeneo_plugin.yaml index daffdc70..573f1eb4 100644 --- a/tests/Application/config/packages/webgriffe_sylius_akeneo_plugin.yaml +++ b/tests/Application/config/packages/webgriffe_sylius_akeneo_plugin.yaml @@ -1,3 +1,6 @@ +imports: + - { resource: "@WebgriffeSyliusAkeneoPlugin/Resources/config/config.yaml" } + webgriffe_sylius_akeneo: api_client: base_url: 'http://demo.akeneo.com/' diff --git a/tests/Application/config/routes/webgriffe_sylius_akeneo_plugin.yaml b/tests/Application/config/routes/webgriffe_sylius_akeneo_plugin.yaml new file mode 100644 index 00000000..4b6b9763 --- /dev/null +++ b/tests/Application/config/routes/webgriffe_sylius_akeneo_plugin.yaml @@ -0,0 +1,3 @@ +webgriffe_sylius_akeneo_plugin_admin: + resource: "@WebgriffeSyliusAkeneoPlugin/Resources/config/admin_routing.yaml" + prefix: /admin diff --git a/tests/Behat/Context/Setup/QueueContext.php b/tests/Behat/Context/Setup/QueueContext.php index 10258e1f..caeabc54 100644 --- a/tests/Behat/Context/Setup/QueueContext.php +++ b/tests/Behat/Context/Setup/QueueContext.php @@ -27,6 +27,7 @@ public function __construct( /** * @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$/ */ public function thereIsOneProductToImportWithIdentifierInTheAkeneoQueue(string $identifier, string $importer) { @@ -50,4 +51,18 @@ public function thereIsOneProductAssociationsToImportWithIdentifierInTheAkeneoQu $queueItem->setCreatedAt(new \DateTime()); $this->queueItemRepository->add($queueItem); } + + /** + * @Given /^there is an already imported item with identifier "([^"]*)" for the "([^"]*)" importer in the Akeneo queue$/ + */ + public function thereIsAnAlreadyImportedItemWithIdentifierForTheImporterInTheAkeneoQueue(string $identifier, string $importer) + { + /** @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); + } } diff --git a/tests/Behat/Context/Ui/Admin/ManagingQueueItems.php b/tests/Behat/Context/Ui/Admin/ManagingQueueItems.php new file mode 100644 index 00000000..c1f945da --- /dev/null +++ b/tests/Behat/Context/Ui/Admin/ManagingQueueItems.php @@ -0,0 +1,90 @@ +indexPage = $indexPage; + } + + /** + * @When /^I browse Akeneo queue items$/ + */ + public function iBrowseAkeneoQueueItems(): void + { + $this->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); + } +} diff --git a/tests/Behat/Page/Admin/QueueItem/IndexPage.php b/tests/Behat/Page/Admin/QueueItem/IndexPage.php new file mode 100644 index 00000000..c565adf3 --- /dev/null +++ b/tests/Behat/Page/Admin/QueueItem/IndexPage.php @@ -0,0 +1,37 @@ +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 new file mode 100644 index 00000000..bb625e15 --- /dev/null +++ b/tests/Behat/Page/Admin/QueueItem/IndexPageInterface.php @@ -0,0 +1,16 @@ + - + - + - + - + - + - + - + %webgriffe_sylius_akeneo.temporary_directory% %webgriffe_sylius_akeneo.temporary_files_prefix% - + + + + + + + + webgriffe_sylius_akeneo_admin_queue_item_index + diff --git a/tests/Behat/Resources/suites.yml b/tests/Behat/Resources/suites.yml index 8386a530..f2e8018b 100644 --- a/tests/Behat/Resources/suites.yml +++ b/tests/Behat/Resources/suites.yml @@ -7,13 +7,13 @@ default: - sylius.behat.context.setup.product - sylius.behat.context.setup.channel - sylius.behat.context.setup.locale - - webgriffe_sylius_akeneo.context.setup.queue + - webgriffe_sylius_akeneo.behat.context.setup.queue - - webgriffe_sylius_akeneo.context.cli.consume_command + - webgriffe_sylius_akeneo.behat.context.cli.consume_command - - webgriffe_sylius_akeneo.context.db.product - - webgriffe_sylius_akeneo.context.db.queue - - webgriffe_sylius_akeneo.context.system.filesystem + - webgriffe_sylius_akeneo.behat.context.db.product + - webgriffe_sylius_akeneo.behat.context.db.queue + - webgriffe_sylius_akeneo.behat.context.system.filesystem filters: tags: "@importing_products && @cli" @@ -25,12 +25,12 @@ default: - sylius.behat.context.setup.product - sylius.behat.context.setup.product_association - sylius.behat.context.setup.channel - - webgriffe_sylius_akeneo.context.setup.queue + - webgriffe_sylius_akeneo.behat.context.setup.queue - - webgriffe_sylius_akeneo.context.cli.consume_command + - webgriffe_sylius_akeneo.behat.context.cli.consume_command - - webgriffe_sylius_akeneo.context.db.product - - webgriffe_sylius_akeneo.context.db.queue + - webgriffe_sylius_akeneo.behat.context.db.product + - webgriffe_sylius_akeneo.behat.context.db.queue filters: tags: "@importing_product_associations && @cli" @@ -41,15 +41,15 @@ default: - sylius.behat.context.transform.date_time - - webgriffe_sylius_akeneo.context.setup.akeneo - - webgriffe_sylius_akeneo.context.setup.queue + - webgriffe_sylius_akeneo.behat.context.setup.akeneo + - webgriffe_sylius_akeneo.behat.context.setup.queue - - webgriffe_sylius_akeneo.context.cli.enqueue_command + - webgriffe_sylius_akeneo.behat.context.cli.enqueue_command - - webgriffe_sylius_akeneo.context.db.queue + - webgriffe_sylius_akeneo.behat.context.db.queue - - webgriffe_sylius_akeneo.context.system.filesystem - - webgriffe_sylius_akeneo.context.system.datetime + - webgriffe_sylius_akeneo.behat.context.system.filesystem + - webgriffe_sylius_akeneo.behat.context.system.datetime filters: tags: "@enqueuing_generic_items && @cli" @@ -60,15 +60,15 @@ default: - sylius.behat.context.transform.date_time - - webgriffe_sylius_akeneo.context.setup.akeneo - - webgriffe_sylius_akeneo.context.setup.queue + - webgriffe_sylius_akeneo.behat.context.setup.akeneo + - webgriffe_sylius_akeneo.behat.context.setup.queue - - webgriffe_sylius_akeneo.context.cli.enqueue_command + - webgriffe_sylius_akeneo.behat.context.cli.enqueue_command - - webgriffe_sylius_akeneo.context.db.queue + - webgriffe_sylius_akeneo.behat.context.db.queue - - webgriffe_sylius_akeneo.context.system.filesystem - - webgriffe_sylius_akeneo.context.system.datetime + - webgriffe_sylius_akeneo.behat.context.system.filesystem + - webgriffe_sylius_akeneo.behat.context.system.datetime filters: tags: "@enqueuing_products && @cli" @@ -79,16 +79,27 @@ default: - sylius.behat.context.transform.date_time - - webgriffe_sylius_akeneo.context.setup.akeneo - - webgriffe_sylius_akeneo.context.setup.queue + - webgriffe_sylius_akeneo.behat.context.setup.akeneo + - webgriffe_sylius_akeneo.behat.context.setup.queue - - webgriffe_sylius_akeneo.context.cli.enqueue_command + - webgriffe_sylius_akeneo.behat.context.cli.enqueue_command - - webgriffe_sylius_akeneo.context.db.queue + - webgriffe_sylius_akeneo.behat.context.db.queue - - webgriffe_sylius_akeneo.context.system.filesystem - - webgriffe_sylius_akeneo.context.system.datetime + - webgriffe_sylius_akeneo.behat.context.system.filesystem + - webgriffe_sylius_akeneo.behat.context.system.datetime filters: tags: "@enqueuing_products_associations && @cli" + ui_browsing_queue_items: + contexts: + - sylius.behat.context.hook.doctrine_orm + + - sylius.behat.context.setup.admin_security + - webgriffe_sylius_akeneo.behat.context.setup.queue + + - webgriffe_sylius_akeneo.behat.context.ui.admin.managing_queue_items + + filters: + tags: "@browsing_queue_items && @ui"