diff --git a/README.md b/README.md index 889fd072..b91f2605 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 from CLI you can use the **import command** (see below). #### Schedule Akeneo PIM import button @@ -387,77 +387,43 @@ 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 - -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 @@ -474,20 +440,18 @@ 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" -* * * * * /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 * * * * /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" 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 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/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 diff --git a/features/enqueuing_generic_items.feature b/features/enqueuing_generic_items.feature deleted file mode 100644 index dc2fe730..00000000 --- a/features/enqueuing_generic_items.feature +++ /dev/null @@ -1,52 +0,0 @@ -@enqueuing_generic_items -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 - - @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" - 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 - 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 - 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 - 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 - When I enqueue 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 - 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 - Then I should be notified that the importer does not exists diff --git a/features/enqueuing_products.feature b/features/enqueuing_products.feature deleted file mode 100644 index 54fc55cf..00000000 --- a/features/enqueuing_products.feature +++ /dev/null @@ -1,64 +0,0 @@ -@enqueuing_products -Feature: Enqueuing products - In order to import my products from Akeneo - As a Store Owner - I want to add them to the Akeneo PIM queue - - @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 enqueue 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 - - @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 enqueue 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" - - @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 enqueue 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 - And I should see 1, not imported, item in the Akeneo queue items list - - @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 - 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 - 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 I am logged in as an administrator - 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/enqueuing_products_associations.feature b/features/enqueuing_products_associations.feature deleted file mode 100644 index f0367f1b..00000000 --- a/features/enqueuing_products_associations.feature +++ /dev/null @@ -1,15 +0,0 @@ -@enqueuing_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 enqueue 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_generic_items.feature b/features/importing_generic_items.feature new file mode 100644 index 00000000..ff05f506 --- /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: 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 + Then I should see 0 products in the list diff --git a/features/importing_product_associations_from_queue.feature b/features/importing_product_associations.feature similarity index 86% rename from features/importing_product_associations_from_queue.feature rename to features/importing_product_associations.feature index da8618a1..28b5fce1 100644 --- a/features/importing_product_associations_from_queue.feature +++ b/features/importing_product_associations.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" - When I import all items in queue + 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/features/importing_products.feature b/features/importing_products.feature new file mode 100644 index 00000000..85449549 --- /dev/null +++ b/features/importing_products.feature @@ -0,0 +1,50 @@ +@importing_products +Feature: Importing products + In order to show updated data about my products + As a Store Owner + I want to import products from Akeneo PIM + + Background: + Given there is a product "BRAIDED_HAT_M" on Akeneo + And there is a product "BRAIDED_HAT_L" on Akeneo + + @cli + 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: 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: 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 "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: Importing a configurable product + Given the store operates on a single channel + 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 "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_from_queue.feature b/features/importing_products_from_queue.feature deleted file mode 100644 index f3d21b14..00000000 --- a/features/importing_products_from_queue.feature +++ /dev/null @@ -1,47 +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 items in queue - 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 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 - - @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 items in queue - Then 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 - 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 items in queue - Then there should not be any temporary file in the temporary files directory 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/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/Command/ConsumeCommand.php b/src/Command/ConsumeCommand.php deleted file mode 100644 index 7cbb49c7..00000000 --- a/src/Command/ConsumeCommand.php +++ /dev/null @@ -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/ImportCommand.php similarity index 71% rename from src/Command/EnqueueCommand.php rename to src/Command/ImportCommand.php index 76894567..792f220a 100644 --- a/src/Command/EnqueueCommand.php +++ b/src/Command/ImportCommand.php @@ -4,20 +4,22 @@ 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 +final class ImportCommand extends Command { use LockableTrait; @@ -29,13 +31,12 @@ 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 QueueItemRepositoryInterface $queueItemRepository, - private FactoryInterface $queueItemFactory, private DateTimeBuilderInterface $dateTimeBuilder, private ImporterRegistryInterface $importerRegistry, + private MessageBusInterface $messageBus, ) { parent::__construct(); } @@ -43,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, @@ -61,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', ); } @@ -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,18 +119,14 @@ 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.', + '%s entity with identifier %s imported.', $importer->getAkeneoEntity(), $identifier, ), @@ -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 deleted file mode 100644 index fb22cf14..00000000 --- a/src/Command/QueueCleanupCommand.php +++ /dev/null @@ -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/ProductImportController.php similarity index 51% rename from src/Controller/ProductEnqueueController.php rename to src/Controller/ProductImportController.php index 69375a19..057f416b 100644 --- a/src/Controller/ProductEnqueueController.php +++ b/src/Controller/ProductImportController.php @@ -9,23 +9,21 @@ 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 +final class ProductImportController extends AbstractController { public function __construct( - private QueueItemRepositoryInterface $queueItemRepository, private ProductRepositoryInterface $productRepository, - private UrlGeneratorInterface $urlGenerator, + private MessageBusInterface $messageBus, private TranslatorInterface $translator, ) { } - public function enqueueAction(int $productId): Response + public function importAction(int $productId): Response { /** @var ProductInterface|null $product */ $product = $this->productRepository->find($productId); @@ -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..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 { @@ -56,25 +51,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..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' => [ @@ -118,9 +120,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 deleted file mode 100644 index 6da0e483..00000000 --- a/src/Entity/QueueItem.php +++ /dev/null @@ -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 @@ - ['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/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/Message/ItemImport.php b/src/Message/ItemImport.php new file mode 100644 index 00000000..aa19a29e --- /dev/null +++ b/src/Message/ItemImport.php @@ -0,0 +1,24 @@ +akeneoEntity; + } + + public function getAkeneoIdentifier(): string + { + return $this->akeneoIdentifier; + } +} 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/Product/Importer.php b/src/Product/Importer.php index 991d1c64..0b88d1f3 100644 --- a/src/Product/Importer.php +++ b/src/Product/Importer.php @@ -104,8 +104,11 @@ 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::isArray($product); + 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..ba1ff52c 100644 --- a/src/ProductAssociations/Importer.php +++ b/src/ProductAssociations/Importer.php @@ -131,7 +131,11 @@ public function getIdentifiersModifiedSince(DateTime $sinceDate): array $products = $this->apiClient->getProductApi()->all(50, ['search' => $searchBuilder->getFilters()]); $identifiers = []; foreach ($products as $product) { - $identifiers[] = $product['identifier']; + Assert::isArray($product); + Assert::keyExists($product, 'identifier'); + $productIdentifier = (string) $product['identifier']; + Assert::stringNotEmpty($productIdentifier); + $identifiers[] = $productIdentifier; } return $identifiers; 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..a9436850 100644 --- a/src/Resources/config/services.xml +++ b/src/Resources/config/services.xml @@ -6,35 +6,11 @@ - - - - - - - - - - - - - - - - - - + - - - - - + @@ -70,11 +46,10 @@ - + - - + @@ -90,15 +65,10 @@ %webgriffe_sylius_akeneo.temporary_files_prefix% - - + - - - - @@ -164,5 +134,14 @@ + + + + + + + + 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/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 %} 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..fc2c360b 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\TemporaryFilesManagerInterface; 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 TemporaryFilesManagerInterface $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..561f725d --- /dev/null +++ b/tests/Application/config/packages/test/messenger.yaml @@ -0,0 +1,4 @@ +framework: + messenger: + transports: + main: 'sync://' 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 }} 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/ImportCommandContext.php similarity index 62% rename from tests/Behat/Context/Cli/EnqueueCommandContext.php rename to tests/Behat/Context/Cli/ImportCommandContext.php index 556b93ef..a5a2ead3 100644 --- a/tests/Behat/Context/Cli/EnqueueCommandContext.php +++ b/tests/Behat/Context/Cli/ImportCommandContext.php @@ -5,53 +5,57 @@ 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; 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 SharedStorageInterface $sharedStorage) - { + public function __construct( + private KernelInterface $kernel, + private ImportCommand $importCommand, + private SharedStorageInterface $sharedStorage, + ) { } /** - * @When /^I enqueue items for all importers modified since date "([^"]+)"$/ + * @When I import items for all importers modified since date :date */ - public function iRunEnqueueCommandWithSinceDate(string $date): void + public function iImportItemsForAllImportersModifiedSinceDate(DateTime $date): void { $commandTester = $this->getCommandTester(); try { - $commandTester->execute(['command' => 'webgriffe:akeneo:enqueue', '--since' => $date]); + $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 iRunEnqueueCommandWithNoSinceDate(): 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); } } /** - * @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,21 +67,21 @@ public function theCommandShouldHaveThrownExceptionWithMessageContaining(): 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); } } /** - * @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,22 +92,22 @@ 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); } } /** - * @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,52 +118,53 @@ public function iShouldBeNotifiedThatTheSinceDateFileDoesNotExists(): void } /** - * @When /^I enqueue items for all importers modified since date specified from file "([^"]+)"$/ + * @When I import items for all importers modified since date specified from file :file */ - public function iEnqueueItemsWithSinceDateSpecifiedFromFile(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 + * @When I import all from Akeneo */ - 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$/ + * @When I import all items for the :importer importer */ - public function iEnqueueItemsModifiedSinceDateForTheImporter(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); @@ -167,7 +172,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 { @@ -180,8 +185,8 @@ public function iShouldBeNotifiedThatTheImporterDoesNotExists(): void private function getCommandTester(): CommandTester { $application = new Application($this->kernel); - $application->add($this->enqueueCommand); - $command = $application->find('webgriffe:akeneo:enqueue'); + $application->add($this->importCommand); + $command = $application->find('webgriffe:akeneo:import'); return new CommandTester($command); } 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..fd50ef97 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/Db/QueueContext.php deleted file mode 100644 index 0033613d..00000000 --- a/tests/Behat/Context/Db/QueueContext.php +++ /dev/null @@ -1,130 +0,0 @@ -getQueueItemByImporterAndIdentifier($importer, $identifier); - Assert::notNull($queueItem->getImportedAt()); - } - - /** - * @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()); - } - - /** - * @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 "([^"]*)" for the "([^"]*)" importer should not be in the Akeneo queue$/ - */ - public function theProductShouldNotBeInTheAkeneoQueue(string $identifier, string $importer): void - { - Assert::null( - $this->queueItemRepository->findOneBy(['akeneoEntity' => $importer, 'akeneoIdentifier' => $identifier]), - ); - } - - /** - * @Then /^the queue item with identifier "([^"]*)" for the "([^"]*)" importer should be in the Akeneo queue$/ - */ - public function theProductShouldBeInTheAkeneoQueue(string $identifier, string $importer): void - { - Assert::isInstanceOf( - $this->queueItemRepository->findOneBy( - ['akeneoEntity' => $importer, 'akeneoIdentifier' => $identifier], - ), - QueueItemInterface::class, - ); - } - - /** - * @Then /^there should be no item in the queue for the "([^"]*)" importer/ - */ - public function thereShouldBeNoProductInTheAkeneoQueue(string $importer): void - { - Assert::isEmpty($this->queueItemRepository->findBy(['akeneoEntity' => $importer])); - } - - /** - * @Then /^there should be no item in the Akeneo queue$/ - */ - public function thereShouldBeNoItemInTheAkeneoQueue(): void - { - Assert::isEmpty($this->queueItemRepository->findAll()); - } - - /** - * @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$/ - */ - public function thereShouldBeItemsForTheImporterInTheAkeneoQueue(int $count, string $importer): void - { - $items = $this->queueItemRepository->findBy(['akeneoEntity' => $importer]); - Assert::count($items, $count); - } - - /** - * @Then /^there should be items for the "([^"]*)" 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)); - } - - private function getQueueItemByImporterAndIdentifier(string $importer, string $identifier): QueueItemInterface - { - /** @var QueueItemInterface|null $item */ - $item = $this->queueItemRepository->findOneBy(['akeneoEntity' => $importer, 'akeneoIdentifier' => $identifier]); - Assert::isInstanceOf($item, QueueItemInterface::class); - - return $item; - } -} 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/Context/Setup/QueueContext.php b/tests/Behat/Context/Setup/QueueContext.php index 5afbe59b..43bb0442 100644 --- a/tests/Behat/Context/Setup/QueueContext.php +++ b/tests/Behat/Context/Setup/QueueContext.php @@ -6,75 +6,37 @@ use Behat\Behat\Context\Context; use Sylius\Behat\Service\SharedStorageInterface; -use Sylius\Component\Resource\Factory\FactoryInterface; -use Webgriffe\SyliusAkeneoPlugin\Entity\QueueItemInterface; -use Webgriffe\SyliusAkeneoPlugin\Repository\QueueItemRepositoryInterface; +use Symfony\Component\Messenger\MessageBusInterface; +use Webgriffe\SyliusAkeneoPlugin\Message\ItemImport; 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); - } - - /** - * @Given /^there is one product associations to import with 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($importer, $identifier); + $this->messageBus->dispatch($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); + $this->sharedStorage->set('item_import', $itemImport); } /** - * @Given /^(this item) has been imported (\d+) days ago$/ + * @Given there is one product associations to import with identifier :identifier in the Akeneo queue */ - public function thisItemHasBeenImportedDaysAgo(QueueItemInterface $queueItem, int $days): void + public function thereIsOneProductAssociationsToImportWithIdentifierInTheAkeneoQueue(string $identifier): void { - $queueItem->setImportedAt(new \DateTime("$days days ago")); - $this->queueItemRepository->add($queueItem); - } + $itemImport = new ItemImport('ProductAssociations', $identifier); + $this->messageBus->dispatch($itemImport); - /** - * @Given /^(this item) has been imported now$/ - */ - public function thisItemHasBeenImportedNow(QueueItemInterface $queueItem): void - { - $queueItem->setImportedAt(new \DateTime()); - $this->queueItemRepository->add($queueItem); + $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 deleted file mode 100644 index 5e844713..00000000 --- a/tests/Behat/Context/Transform/QueueItemContext.php +++ /dev/null @@ -1,33 +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 75907d9a..edd70750 100644 --- a/tests/Behat/Context/Ui/Admin/ManagingProductsContext.php +++ b/tests/Behat/Context/Ui/Admin/ManagingProductsContext.php @@ -8,21 +8,25 @@ 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 Tests\Webgriffe\SyliusAkeneoPlugin\Behat\Page\Admin\QueueItem\IndexPageInterface as QueueItemsIndexPageInterface; +use Sylius\Component\Core\Repository\ProductRepositoryInterface; +use Sylius\Component\Product\Model\ProductAssociationTypeInterface; 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, + private UpdateSimpleProductPageInterface $updateSimpleProductPage, + private ProductRepositoryInterface $productRepository, + ) { } /** @@ -49,26 +53,46 @@ public function iShouldBeNotifiedThatItHasBeenSuccessfullyEnqueued(): void } /** - * @Given /^I should be notified that it has been already enqueued$/ + * @Then the product with code :code should have an association :productAssociationType with product :productName */ - public function iShouldBeNotifiedThatItHasBeenAlreadyEnqueued(): void - { - $this->testHelper->waitUntilNotificationPopups( - $this->notificationChecker, - NotificationType::success(), - 'Akeneo PIM import for this product has been already scheduled before', - ); + public function theProductShouldHaveAnAssociationWithProducts( + string $code, + ProductAssociationTypeInterface $productAssociationType, + 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( + $this->updateSimpleProductPage->hasAssociatedProduct($productName, $productAssociationType), + sprintf( + 'This product should have an association %s with product %s.', + $productAssociationType->getName(), + $productName, + ), + ); + } } /** - * @Then /^I should see (\d+), not imported, items? in the Akeneo queue items list$/ + * @Then the product with code :code should not have an association :productAssociationType with product :productName */ - 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'); - } + public function theProductShouldNotHaveAnAssociationWithProduct( + string $code, + ProductAssociationTypeInterface $productAssociationType, + 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), + sprintf( + 'This product should have an association %s with product %s.', + $productAssociationType->getName(), + $productName, + ), + ); } } diff --git a/tests/Behat/Context/Ui/Admin/ManagingQueueItems.php b/tests/Behat/Context/Ui/Admin/ManagingQueueItems.php deleted file mode 100644 index 0ab7fb9f..00000000 --- a/tests/Behat/Context/Ui/Admin/ManagingQueueItems.php +++ /dev/null @@ -1,146 +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/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 @@ - - - - - - - + - - - - - - - - - - - - + - + @@ -47,10 +31,6 @@ - - - - %webgriffe_sylius_akeneo.temporary_directory% %webgriffe_sylius_akeneo.temporary_files_prefix% @@ -58,20 +38,12 @@ - - - - - - - - - - webgriffe_sylius_akeneo_admin_queue_item_index + + diff --git a/tests/Behat/Resources/suites.yml b/tests/Behat/Resources/suites.yml index 43b9c88a..7652b896 100644 --- a/tests/Behat/Resources/suites.yml +++ b/tests/Behat/Resources/suites.yml @@ -8,106 +8,75 @@ default: - sylius.behat.context.setup.channel - 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.setup.akeneo - webgriffe_sylius_akeneo.behat.context.db.product - - webgriffe_sylius_akeneo.behat.context.db.queue + - webgriffe_sylius_akeneo.behat.context.cli.import_command - webgriffe_sylius_akeneo.behat.context.system.filesystem filters: tags: "@importing_products && @cli" - cli_importing_product_associations: + ui_importing_products: contexts: - sylius.behat.context.hook.doctrine_orm - - sylius.behat.context.setup.product - - sylius.behat.context.setup.product_association + - 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.cli.consume_command - - webgriffe_sylius_akeneo.behat.context.db.product - - webgriffe_sylius_akeneo.behat.context.db.queue + + - sylius.behat.context.ui.admin.managing_products + - webgriffe_sylius_akeneo.behat.context.ui.admin.managing_products filters: - tags: "@importing_product_associations && @cli" + tags: "@importing_products && @ui" - cli_enqueuing_generic_items: + cli_importing_product_associations: contexts: - sylius.behat.context.hook.doctrine_orm - - sylius.behat.context.transform.date_time - - - webgriffe_sylius_akeneo.behat.context.setup.akeneo + - sylius.behat.context.setup.product + - 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.cli.enqueue_command - - - webgriffe_sylius_akeneo.behat.context.db.queue + - webgriffe_sylius_akeneo.behat.context.db.product - - webgriffe_sylius_akeneo.behat.context.system.filesystem - - webgriffe_sylius_akeneo.behat.context.system.datetime + - webgriffe_sylius_akeneo.behat.context.cli.import_command filters: - tags: "@enqueuing_generic_items && @cli" + tags: "@importing_product_associations && @cli" - cli_enqueuing_products: + 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.enqueue_command - - - webgriffe_sylius_akeneo.behat.context.db.queue - - - 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 + - webgriffe_sylius_akeneo.behat.context.cli.import_command - 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.enqueue_command - - - webgriffe_sylius_akeneo.behat.context.db.queue - webgriffe_sylius_akeneo.behat.context.system.filesystem - webgriffe_sylius_akeneo.behat.context.system.datetime filters: - tags: "@enqueuing_products_associations && @cli" + tags: "@importing_generic_items && @cli && @ui" cli_reconcile_products: contexts: @@ -128,32 +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 - - 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: - 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/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/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 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 00000000..350bc04c Binary files /dev/null and b/tests/Integration/DataFixtures/ApiClientMock/media-files/6/5/8/8/65887ee600d9d66e829a75beedf374533b1ff8bc_10597353_1104.jpg differ 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 00000000..ff7eb269 Binary files /dev/null and b/tests/Integration/DataFixtures/ApiClientMock/media-files/7/3/6/b/736bbc43fd1e384f4badbeababd84389b31159f8_11164822_Philips.jpg differ diff --git a/tests/Integration/DataFixtures/ApiClientMock/media-files/8/e/2/d/8e2d8254e599201a0e6e91d0c512bc37c8780c21_1314976_5566.jpg b/tests/Integration/DataFixtures/ApiClientMock/media-files/8/e/2/d/8e2d8254e599201a0e6e91d0c512bc37c8780c21_1314976_5566.jpg new file mode 100644 index 00000000..2c6238f4 Binary files /dev/null and b/tests/Integration/DataFixtures/ApiClientMock/media-files/8/e/2/d/8e2d8254e599201a0e6e91d0c512bc37c8780c21_1314976_5566.jpg differ 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')); } 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');