From 6588e221a8a0c19989531365ecbeb3e8619b500a Mon Sep 17 00:00:00 2001 From: levente Date: Mon, 11 Dec 2023 17:34:32 +0200 Subject: [PATCH 1/5] SITES-17835 - Release CIF Components 2.12.4 * updated to CIF components 2.12.4 --- pom.xml | 2 +- ui.frontend/package-lock.json | 12 ++++++------ ui.frontend/package.json | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/pom.xml b/pom.xml index 4ef8239e..bb9604aa 100644 --- a/pom.xml +++ b/pom.xml @@ -87,7 +87,7 @@ admin admin 2.18.6 - 2.12.2 + 2.12.4 1.7.10 9.1.0-magento242ee 5.1.2 diff --git a/ui.frontend/package-lock.json b/ui.frontend/package-lock.json index 0c6d286e..893ee0b8 100644 --- a/ui.frontend/package-lock.json +++ b/ui.frontend/package-lock.json @@ -5,14 +5,14 @@ "requires": true, "dependencies": { "@adobe/aem-core-cif-product-recs-extension": { - "version": "2.12.0", - "resolved": "https://registry.npmjs.org/@adobe/aem-core-cif-product-recs-extension/-/aem-core-cif-product-recs-extension-2.12.0.tgz", - "integrity": "sha512-rj9QirxjcE6I+0x+uMnkt/wOLBtAz3tmoq3//JLnplASs300+Bsi0h65Ab0CaxLayTS/pSWj6LpcSZGiYSZI+Q==" + "version": "2.12.4", + "resolved": "https://artifactory-uw2.adobeitc.com/artifactory/api/npm/npmjs-remote/@adobe/aem-core-cif-product-recs-extension/-/aem-core-cif-product-recs-extension-2.12.4.tgz", + "integrity": "sha512-8PvQwHzFblwYyGaplQBk307faLyxkVaxv62tb6xVAiQxCWo1ZKClZzguZWZMahwXAe9TSbouTCpfy3G4jAQisQ==" }, "@adobe/aem-core-cif-react-components": { - "version": "2.12.0", - "resolved": "https://registry.npmjs.org/@adobe/aem-core-cif-react-components/-/aem-core-cif-react-components-2.12.0.tgz", - "integrity": "sha512-kq5REBCYad0b6xNf9+TqCpD/Iq1DNV5dP3+N28s7EkoHJydNKD7OG8oR9gUDZEUJDemp5je+FLIpAqWqBPmw7A==" + "version": "2.12.4", + "resolved": "https://artifactory-uw2.adobeitc.com/artifactory/api/npm/npmjs-remote/@adobe/aem-core-cif-react-components/-/aem-core-cif-react-components-2.12.4.tgz", + "integrity": "sha512-HD4NnJYmdIyr9qJQTgHi69PHFgjxuJD3xqhzR2/+bZLqKNXOfq8N7gX8ElXVBsYJ5x7AmItrHDm5Z7cw77sA6Q==" }, "@apollo/client": { "version": "3.5.6", diff --git a/ui.frontend/package.json b/ui.frontend/package.json index bd8c629e..57e9c5de 100644 --- a/ui.frontend/package.json +++ b/ui.frontend/package.json @@ -87,8 +87,8 @@ "webpack-merge": "^4.2.1" }, "dependencies": { - "@adobe/aem-core-cif-product-recs-extension": "2.12.0", - "@adobe/aem-core-cif-react-components": "2.12.0", + "@adobe/aem-core-cif-product-recs-extension": "2.12.4", + "@adobe/aem-core-cif-react-components": "2.12.4", "@apollo/client": "^3.5.5", "@babel/runtime": "^7.4.5", "@magento/peregrine": "11.0.0", From 620be128ab9197a44b49842b5e829611c7fe35b6 Mon Sep 17 00:00:00 2001 From: Alexandru Tudoran Date: Tue, 23 Jan 2024 14:07:53 +0200 Subject: [PATCH 2/5] SITES-19186 - Release CIF Components 2.12.6 --- pom.xml | 2 +- ui.frontend/package-lock.json | 12 ++++++------ ui.frontend/package.json | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/pom.xml b/pom.xml index bb9604aa..9b1d3bc9 100644 --- a/pom.xml +++ b/pom.xml @@ -87,7 +87,7 @@ admin admin 2.18.6 - 2.12.4 + 2.12.6 1.7.10 9.1.0-magento242ee 5.1.2 diff --git a/ui.frontend/package-lock.json b/ui.frontend/package-lock.json index 893ee0b8..2e050174 100644 --- a/ui.frontend/package-lock.json +++ b/ui.frontend/package-lock.json @@ -5,14 +5,14 @@ "requires": true, "dependencies": { "@adobe/aem-core-cif-product-recs-extension": { - "version": "2.12.4", - "resolved": "https://artifactory-uw2.adobeitc.com/artifactory/api/npm/npmjs-remote/@adobe/aem-core-cif-product-recs-extension/-/aem-core-cif-product-recs-extension-2.12.4.tgz", - "integrity": "sha512-8PvQwHzFblwYyGaplQBk307faLyxkVaxv62tb6xVAiQxCWo1ZKClZzguZWZMahwXAe9TSbouTCpfy3G4jAQisQ==" + "version": "2.12.6", + "resolved": "https://artifactory-uw2.adobeitc.com/artifactory/api/npm/npmjs-remote/@adobe/aem-core-cif-product-recs-extension/-/aem-core-cif-product-recs-extension-2.12.6.tgz", + "integrity": "sha512-OJ6VrX6dvoE8t+Z4J4WXXKUoqJtuxRSdnYajsH5/Ptm0ydmMpwkCi1vIdxmJswapZHJN7iRpKeyU5F/+RG7mfQ==" }, "@adobe/aem-core-cif-react-components": { - "version": "2.12.4", - "resolved": "https://artifactory-uw2.adobeitc.com/artifactory/api/npm/npmjs-remote/@adobe/aem-core-cif-react-components/-/aem-core-cif-react-components-2.12.4.tgz", - "integrity": "sha512-HD4NnJYmdIyr9qJQTgHi69PHFgjxuJD3xqhzR2/+bZLqKNXOfq8N7gX8ElXVBsYJ5x7AmItrHDm5Z7cw77sA6Q==" + "version": "2.12.6", + "resolved": "https://artifactory-uw2.adobeitc.com/artifactory/api/npm/npmjs-remote/@adobe/aem-core-cif-react-components/-/aem-core-cif-react-components-2.12.6.tgz", + "integrity": "sha512-81uzBKPWPmzcn7NZ5uCVhq6Mb5uccWqx9sFcy7NNIiUNA2VBpTXMZXzcPY+UussSTKEsQ9737InitrYLsGPsOw==" }, "@apollo/client": { "version": "3.5.6", diff --git a/ui.frontend/package.json b/ui.frontend/package.json index 57e9c5de..cdc4b6fd 100644 --- a/ui.frontend/package.json +++ b/ui.frontend/package.json @@ -87,8 +87,8 @@ "webpack-merge": "^4.2.1" }, "dependencies": { - "@adobe/aem-core-cif-product-recs-extension": "2.12.4", - "@adobe/aem-core-cif-react-components": "2.12.4", + "@adobe/aem-core-cif-product-recs-extension": "2.12.6", + "@adobe/aem-core-cif-react-components": "2.12.6", "@apollo/client": "^3.5.5", "@babel/runtime": "^7.4.5", "@magento/peregrine": "11.0.0", From 300b70ab27f417198d9593633c9530313303474e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Levente=20S=C3=A1ntha?= Date: Mon, 27 May 2024 07:50:17 +0300 Subject: [PATCH 3/5] @releng - fix pipeline issues and tests (#326) * fix ui & integration tests * improved unit test coverage --- .circleci/config.yml | 4 +- .../models/commerce/MySearchResultsImpl.java | 4 + .../commerce/MyProductTeaserImplTest.java | 6 + .../commerce/MySearchResultsImplTest.java | 48 +++++++- .../datalayer/grouped-product-65.json | 63 +++++----- .../resources/datalayer/grouped-product.json | 63 +++++----- .../datalayer/simple-product-65.json | 116 +++++++++--------- .../resources/datalayer/simple-product.json | 116 +++++++++--------- 8 files changed, 230 insertions(+), 190 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index c0a75fcc..8c94d6e0 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,14 +2,14 @@ version: 2.1 orbs: codecov: codecov/codecov@1.1.1 - browser-tools: circleci/browser-tools@1.2.4 + browser-tools: circleci/browser-tools@1.4.4 common: integration_test_steps: &integration_test_steps steps: - checkout - browser-tools/install-browser-tools: - chrome-version: 114.0.5735.90 # TODO: remove -> https://github.com/CircleCI-Public/browser-tools-orb/issues/75 + chrome-version: 116.0.5845.96 # TODO: remove -> https://github.com/CircleCI-Public/browser-tools-orb/issues/75 - restore_cache: keys: - maven-repo-{{ .Environment.CACHE_VERSION }}-its-{{ arch }}-{{ .Branch }}-{{ checksum "pom.xml" }} diff --git a/core/src/main/java/com/venia/core/models/commerce/MySearchResultsImpl.java b/core/src/main/java/com/venia/core/models/commerce/MySearchResultsImpl.java index 85f87f7e..ebb1ca65 100644 --- a/core/src/main/java/com/venia/core/models/commerce/MySearchResultsImpl.java +++ b/core/src/main/java/com/venia/core/models/commerce/MySearchResultsImpl.java @@ -121,6 +121,10 @@ public String getId() { @Override public ComponentData getData() { final ComponentData data = ((Component) searchResults).getData(); + if (data == null) { + return null; + } + final AtomicReference dataRef = new AtomicReference<>(); ComponentData componentData = (ComponentData) Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[]{ComponentData.class}, (proxy, method, args) -> { diff --git a/core/src/test/java/com/venia/core/models/commerce/MyProductTeaserImplTest.java b/core/src/test/java/com/venia/core/models/commerce/MyProductTeaserImplTest.java index 7a42319b..4568c5d5 100644 --- a/core/src/test/java/com/venia/core/models/commerce/MyProductTeaserImplTest.java +++ b/core/src/test/java/com/venia/core/models/commerce/MyProductTeaserImplTest.java @@ -282,4 +282,10 @@ public void testDataLayerFeature() throws Exception { Assertions.assertEquals(dataLayerJson, underTest.getData().getJson()); } + + @Test + void testGetProductRetriever() throws Exception { + setup(PRODUCTTEASER_NO_BADGE); + Assertions.assertNotNull(underTest.getProductRetriever()); + } } diff --git a/core/src/test/java/com/venia/core/models/commerce/MySearchResultsImplTest.java b/core/src/test/java/com/venia/core/models/commerce/MySearchResultsImplTest.java index 91c755ab..6a15f793 100644 --- a/core/src/test/java/com/venia/core/models/commerce/MySearchResultsImplTest.java +++ b/core/src/test/java/com/venia/core/models/commerce/MySearchResultsImplTest.java @@ -13,16 +13,20 @@ ******************************************************************************/ package com.venia.core.models.commerce; -import com.adobe.cq.commerce.core.components.models.searchresults.SearchResults; import com.adobe.cq.commerce.core.components.services.urls.UrlProvider; import com.adobe.cq.commerce.core.search.models.SearchResultsSet; import com.adobe.cq.commerce.core.search.models.Sorter; import com.adobe.cq.commerce.core.search.models.SorterKey; import com.adobe.cq.commerce.core.search.services.SearchResultsService; +import com.adobe.cq.commerce.magento.graphql.FilterMatchTypeInput; import com.adobe.cq.commerce.magento.graphql.ProductAttributeFilterInput; +import com.adobe.cq.wcm.core.components.models.datalayer.ComponentData; import com.day.cq.wcm.api.Page; import io.wcm.testing.mock.aem.junit5.AemContext; import io.wcm.testing.mock.aem.junit5.AemContextExtension; +import org.apache.sling.api.resource.Resource; +import org.apache.sling.api.wrappers.ValueMapDecorator; +import org.apache.sling.caconfig.ConfigurationBuilder; import org.apache.sling.testing.mock.sling.ResourceResolverType; import org.apache.sling.testing.mock.sling.servlet.MockSlingHttpServletRequest; import org.junit.jupiter.api.Assertions; @@ -38,8 +42,7 @@ import java.util.Map; import java.util.function.Function; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.*; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -49,7 +52,8 @@ public class MySearchResultsImplTest { private static final String PAGE = "/content/page"; private final AemContext context = new AemContext(ResourceResolverType.JCR_MOCK); - private SearchResults underTest; + private MySearchResultsImpl underTest; + private HashMap dataLayerConfigMap; @BeforeEach void beforeEach() { @@ -79,6 +83,12 @@ void beforeEach() { context.registerService(UrlProvider.class, mock(UrlProvider.class)); context.addModelsForClasses(MySearchResultsImpl.class); + ConfigurationBuilder configurationBuilder = mock(ConfigurationBuilder.class); + when(configurationBuilder.name("com.adobe.cq.wcm.core.components.internal.DataLayerConfig")).thenReturn(configurationBuilder); + dataLayerConfigMap = new HashMap<>(); + when(configurationBuilder.asValueMap()).thenReturn(new ValueMapDecorator(dataLayerConfigMap)); + context.registerAdapter(Resource.class, ConfigurationBuilder.class, configurationBuilder); + MockSlingHttpServletRequest request = context.request(); request.addRequestParameter("search_query", "test"); @@ -86,6 +96,36 @@ void beforeEach() { Assertions.assertNotNull(underTest); } + @Test + void testInstance() { + assertEquals("paginationbar", underTest.getPaginationType()); + assertFalse(underTest.isAddToCartEnabled()); + assertFalse(underTest.isAddToWishListEnabled()); + assertTrue(underTest.loadClientPrice()); + assertNotNull(underTest.getSearchResultsStorefrontContext()); + assertNotNull(underTest.getSearchStorefrontContext()); + underTest.getAppliedCssClasses(); + assertEquals("venia/components/commerce/searchresults", underTest.getExportedType()); + ComponentData componentData = underTest.getData(); + assertNull(componentData); + assertEquals("searchresults-50df7e8869", underTest.getId()); + + underTest.extendProductQueryWith(p-> p.color()); + underTest.extendProductFilterWith(f -> f.setName(new FilterMatchTypeInput().setMatch("winter"))); + assertNotNull(underTest.getProducts()); + } + + @Test + void testComponentData() { + dataLayerConfigMap.put("enabled", true); + + ComponentData componentData = underTest.getData(); + assertNotNull(componentData); + assertEquals("venia/components/commerce/searchresults", componentData.getType()); + assertEquals("searchresults-50df7e8869", componentData.getId()); + } + + @Test void testSorterKeys() { List keys = underTest.getSearchResultsSet().getSorter().getKeys(); diff --git a/it.tests/src/main/resources/datalayer/grouped-product-65.json b/it.tests/src/main/resources/datalayer/grouped-product-65.json index 2ecaa222..b044eedf 100644 --- a/it.tests/src/main/resources/datalayer/grouped-product-65.json +++ b/it.tests/src/main/resources/datalayer/grouped-product-65.json @@ -1,34 +1,33 @@ { - "product-3c02043652": { - "xdm:listPrice": 78.0, - "xdm:SKU": "VA23", - "xdm:assets": [ - { - - "repo:path": "https://mystage1-amspro120.amscommerce.cloud/media/catalog/product/cache/8735dd21982cf027014173d1affcf80c/v/a/va23_main.jpg", - "repo:id": "image-3d3553c5dc", - "@type": "image" - } - ], - "xdm:categories": [ - { - "repo:id": "category-8c01c593b9", - "xdm:name": "Accessories", - "xdm:asset": { - "repo:path": "https://mystage1-amspro120.amscommerce.cloud/media/catalog/category/carefree.jpg", - "repo:id": "image-6310f9e8a5", - "@type": "image" - } - }, - { - "repo:id": "category-f5a5c5aea0", - "xdm:name": "Jewelry", - "xdm:asset": null - } - ], - "xdm:currencyCode": "USD", - "dc:description": "This trio is designed for versatility. Brighten up a simple look or add another layer of “wow” to an already vibrant ensemble. Go as big and bold, or understated as you're in the mood to be.", - "dc:title": "Augusta Trio", - "@type": "venia/components/commerce/product" - } + "product-3c02043652": { + "xdm:listPrice": 78.0, + "xdm:SKU": "VA23", + "xdm:assets": [ + { + "repo:path": "https://mcprod.catalogservice4commerce.fun/media/catalog/product/cache/dbba4389ce8da2ab9399ca5f8b677aac/v/a/va23_main.jpg", + "repo:id": "image-daa709cd50", + "@type": "image" + } + ], + "xdm:categories": [ + { + "repo:id": "category-4c0609820d", + "xdm:name": "Accessories", + "xdm:asset": { + "repo:path": "https://mcprod.catalogservice4commerce.fun/media/catalog/category/carefree.jpg", + "repo:id": "image-11a31da9a5", + "@type": "image" + } + }, + { + "repo:id": "category-6e6f7c1718", + "xdm:name": "Jewelry", + "xdm:asset": null + } + ], + "xdm:currencyCode": "USD", + "dc:description": "This trio is designed for versatility. Brighten up a simple look or add another layer of “wow” to an already vibrant ensemble. Go as big and bold, or understated as you're in the mood to be.", + "dc:title": "Augusta Trio", + "@type": "venia/components/commerce/product" + } } \ No newline at end of file diff --git a/it.tests/src/main/resources/datalayer/grouped-product.json b/it.tests/src/main/resources/datalayer/grouped-product.json index 0c3827a6..17e4dd57 100644 --- a/it.tests/src/main/resources/datalayer/grouped-product.json +++ b/it.tests/src/main/resources/datalayer/grouped-product.json @@ -1,34 +1,33 @@ { - "product-3c02043652": { - "xdm:listPrice": 78.0, - "xdm:SKU": "VA23", - "xdm:assets": [ - { - - "repo:path": "https://mystage1-amspro120.amscommerce.cloud/media/catalog/product/cache/8735dd21982cf027014173d1affcf80c/v/a/va23_main.jpg", - "repo:id": "image-3d3553c5dc", - "@type": "image" - } - ], - "xdm:categories": [ - { - "repo:id": "category-8c01c593b9", - "xdm:name": "Accessories", - "xdm:asset": { - "repo:path": "https://mystage1-amspro120.amscommerce.cloud/media/catalog/category/carefree.jpg", - "repo:id": "image-6310f9e8a5", - "@type": "image" - } - }, - { - "repo:id": "category-f5a5c5aea0", - "xdm:name": "Jewelry", - "xdm:asset": null - } - ], - "xdm:currencyCode": "USD", - "dc:description": "This trio is designed for versatility. Brighten up a simple look or add another layer of “wow” to an already vibrant ensemble. Go as big and bold, or understated as you're in the mood to be.", - "dc:title": "Augusta Trio", - "@type": "venia/components/commerce/product" - } + "product-3c02043652": { + "xdm:listPrice": 78.0, + "xdm:SKU": "VA23", + "xdm:assets": [ + { + "repo:path": "https://mcprod.catalogservice4commerce.fun/media/catalog/product/cache/dbba4389ce8da2ab9399ca5f8b677aac/v/a/va23_main.jpg", + "repo:id": "image-daa709cd50", + "@type": "image" + } + ], + "xdm:categories": [ + { + "repo:id": "category-4c0609820d", + "xdm:name": "Accessories", + "xdm:asset": { + "repo:path": "https://mcprod.catalogservice4commerce.fun/media/catalog/category/carefree.jpg", + "repo:id": "image-11a31da9a5", + "@type": "image" + } + }, + { + "repo:id": "category-6e6f7c1718", + "xdm:name": "Jewelry", + "xdm:asset": null + } + ], + "xdm:currencyCode": "USD", + "dc:description": "This trio is designed for versatility. Brighten up a simple look or add another layer of “wow” to an already vibrant ensemble. Go as big and bold, or understated as you're in the mood to be.", + "dc:title": "Augusta Trio", + "@type": "venia/components/commerce/product" + } } \ No newline at end of file diff --git a/it.tests/src/main/resources/datalayer/simple-product-65.json b/it.tests/src/main/resources/datalayer/simple-product-65.json index f776e951..9244dad7 100644 --- a/it.tests/src/main/resources/datalayer/simple-product-65.json +++ b/it.tests/src/main/resources/datalayer/simple-product-65.json @@ -1,62 +1,58 @@ { - "product-dc59ad50df": { - "xdm:listPrice": 78.0, - "xdm:SKU": "VP05", - "xdm:assets": [ - { - "repo:path": "https://mystage1-amspro120.amscommerce.cloud/media/catalog/product/cache/8735dd21982cf027014173d1affcf80c/v/p/vp05-rn_main_4.jpg", - "repo:id": "image-73439b9cf3", - "@type": "image" - }, - { - "repo:path": "https://mystage1-amspro120.amscommerce.cloud/media/catalog/product/cache/8735dd21982cf027014173d1affcf80c/v/p/vp05-rn_alt_2.jpg", - "repo:id": "image-2e75bb2439", - "@type": "image" - }, - { - "repo:path": "https://mystage1-amspro120.amscommerce.cloud/media/catalog/product/cache/8735dd21982cf027014173d1affcf80c/v/p/vp05-rn_back_2.jpg", - "repo:id": "image-40bcf141f9", - "@type": "image" - }, - { - "repo:path": "https://mystage1-amspro120.amscommerce.cloud/media/catalog/product/cache/8735dd21982cf027014173d1affcf80c/v/p/vp05_look_2.jpg", - "repo:id": "image-e049cce860", - "@type": "image" - } - ], - "xdm:categories": [ - { - "repo:id": "category-cf9d757f29", - "xdm:name": "Bottoms", - "xdm:asset": { - "repo:path": "https://mystage1-amspro120.amscommerce.cloud/media/catalog/category/minimalist.jpg", - "repo:id": "image-74c855fbf3", - "@type": "image" - } - }, - { - "repo:id": "category-6e60cf4797", - "xdm:name": "Pants & Shorts", - "xdm:asset": null - }, - { - "repo:id": "category-b8a3caa888", - "xdm:name": "Shop The Look", - "xdm:asset": { - "repo:path": "https://mystage1-amspro120.amscommerce.cloud/media/catalog/category/beachy.jpg", - "repo:id": "image-8a0806aaab", - "@type": "image" - } - }, - { - "repo:id": "category-df146c27a3", - "xdm:name": "Minimalist Sensibility", - "xdm:asset": null - } - ], - "xdm:currencyCode": "USD", - "dc:description": "

The Honora Wide Leg Pants definitely hold their own when it comes to standing out from the crowd. These pants feature a unique design and a varying color palette to make pairing a snap.

Features:

  • Elastic waistband
  • Drawstring waist
  • Sits just below the waist
  • 31" inseam
  • Machine wash, line dry
", - "dc:title": "Honora Wide Leg Pants", - "@type": "venia/components/commerce/product" - } + "product-dc59ad50df": { + "xdm:listPrice": 78.0, + "xdm:SKU": "VP05", + "xdm:assets": [ + { + "repo:path": "https://mcprod.catalogservice4commerce.fun/media/catalog/product/cache/dbba4389ce8da2ab9399ca5f8b677aac/v/p/vp05-rn_main.jpg", + "repo:id": "image-2ea11abe0a", + "@type": "image" + }, + { + "repo:path": "https://mcprod.catalogservice4commerce.fun/media/catalog/product/cache/dbba4389ce8da2ab9399ca5f8b677aac/v/p/vp05-rn_alt.jpg", + "repo:id": "image-35c39a6a6b", + "@type": "image" + }, + { + "repo:path": "https://mcprod.catalogservice4commerce.fun/media/catalog/product/cache/dbba4389ce8da2ab9399ca5f8b677aac/v/p/vp05-rn_back.jpg", + "repo:id": "image-7ed6d7593b", + "@type": "image" + }, + { + "repo:path": "https://mcprod.catalogservice4commerce.fun/media/catalog/product/cache/dbba4389ce8da2ab9399ca5f8b677aac/v/p/vp05_look.jpg", + "repo:id": "image-3b4d560585", + "@type": "image" + } + ], + "xdm:categories": [ + { + "repo:id": "category-2212426126", + "xdm:name": "Bottoms", + "xdm:asset": { + "repo:path": "https://mcprod.catalogservice4commerce.fun/media/catalog/category/minimalist.jpg", + "repo:id": "image-9d427bd572", + "@type": "image" + } + }, + { + "repo:id": "category-3c1d5daf00", + "xdm:name": "Pants & Shorts", + "xdm:asset": null + }, + { + "repo:id": "category-77640b4209", + "xdm:name": "Shop The Look", + "xdm:asset": null + }, + { + "repo:id": "category-077469fda4", + "xdm:name": "Minimalist Sensibility", + "xdm:asset": null + } + ], + "xdm:currencyCode": "USD", + "dc:description": "

The Honora Wide Leg Pants definitely hold their own when it comes to standing out from the crowd. These pants feature a unique design and a varying color palette to make pairing a snap.

Features:

  • Elastic waistband
  • Drawstring waist
  • Sits just below the waist
  • 31" inseam
  • Machine wash, line dry
", + "dc:title": "Honora Wide Leg Pants", + "@type": "venia/components/commerce/product" + } } \ No newline at end of file diff --git a/it.tests/src/main/resources/datalayer/simple-product.json b/it.tests/src/main/resources/datalayer/simple-product.json index f7cd1ec6..91f3e749 100644 --- a/it.tests/src/main/resources/datalayer/simple-product.json +++ b/it.tests/src/main/resources/datalayer/simple-product.json @@ -1,62 +1,58 @@ { - "product-dc59ad50df": { - "xdm:listPrice": 78.0, - "xdm:SKU": "VP05", - "xdm:assets": [ - { - "repo:path": "https://mystage1-amspro120.amscommerce.cloud/media/catalog/product/cache/8735dd21982cf027014173d1affcf80c/v/p/vp05-rn_main_4.jpg", - "repo:id": "image-73439b9cf3", - "@type": "image" - }, - { - "repo:path": "https://mystage1-amspro120.amscommerce.cloud/media/catalog/product/cache/8735dd21982cf027014173d1affcf80c/v/p/vp05-rn_alt_2.jpg", - "repo:id": "image-2e75bb2439", - "@type": "image" - }, - { - "repo:path": "https://mystage1-amspro120.amscommerce.cloud/media/catalog/product/cache/8735dd21982cf027014173d1affcf80c/v/p/vp05-rn_back_2.jpg", - "repo:id": "image-40bcf141f9", - "@type": "image" - }, - { - "repo:path": "https://mystage1-amspro120.amscommerce.cloud/media/catalog/product/cache/8735dd21982cf027014173d1affcf80c/v/p/vp05_look_2.jpg", - "repo:id": "image-e049cce860", - "@type": "image" - } - ], - "xdm:categories": [ - { - "repo:id": "category-cf9d757f29", - "xdm:name": "Bottoms", - "xdm:asset": { - "repo:path": "https://mystage1-amspro120.amscommerce.cloud/media/catalog/category/minimalist.jpg", - "repo:id": "image-74c855fbf3", - "@type": "image" - } - }, - { - "repo:id": "category-6e60cf4797", - "xdm:name": "Pants & Shorts", - "xdm:asset": null - }, - { - "repo:id": "category-b8a3caa888", - "xdm:name": "Shop The Look", - "xdm:asset": { - "repo:path": "https://mystage1-amspro120.amscommerce.cloud/media/catalog/category/beachy.jpg", - "repo:id": "image-8a0806aaab", - "@type": "image" - } - }, - { - "repo:id": "category-df146c27a3", - "xdm:name": "Minimalist Sensibility", - "xdm:asset": null - } - ], - "xdm:currencyCode": "USD", - "dc:description": "

The Honora Wide Leg Pants definitely hold their own when it comes to standing out from the crowd. These pants feature a unique design and a varying color palette to make pairing a snap.

Features:

  • Elastic waistband
  • Drawstring waist
  • Sits just below the waist
  • 31" inseam
  • Machine wash, line dry
", - "dc:title": "Honora Wide Leg Pants", - "@type": "venia/components/commerce/product" - } + "product-dc59ad50df": { + "xdm:listPrice": 78.0, + "xdm:SKU": "VP05", + "xdm:assets": [ + { + "repo:path": "https://mcprod.catalogservice4commerce.fun/media/catalog/product/cache/dbba4389ce8da2ab9399ca5f8b677aac/v/p/vp05-rn_main.jpg", + "repo:id": "image-2ea11abe0a", + "@type": "image" + }, + { + "repo:path": "https://mcprod.catalogservice4commerce.fun/media/catalog/product/cache/dbba4389ce8da2ab9399ca5f8b677aac/v/p/vp05-rn_alt.jpg", + "repo:id": "image-35c39a6a6b", + "@type": "image" + }, + { + "repo:path": "https://mcprod.catalogservice4commerce.fun/media/catalog/product/cache/dbba4389ce8da2ab9399ca5f8b677aac/v/p/vp05-rn_back.jpg", + "repo:id": "image-7ed6d7593b", + "@type": "image" + }, + { + "repo:path": "https://mcprod.catalogservice4commerce.fun/media/catalog/product/cache/dbba4389ce8da2ab9399ca5f8b677aac/v/p/vp05_look.jpg", + "repo:id": "image-3b4d560585", + "@type": "image" + } + ], + "xdm:categories": [ + { + "repo:id": "category-2212426126", + "xdm:name": "Bottoms", + "xdm:asset": { + "repo:path": "https://mcprod.catalogservice4commerce.fun/media/catalog/category/minimalist.jpg", + "repo:id": "image-9d427bd572", + "@type": "image" + } + }, + { + "repo:id": "category-3c1d5daf00", + "xdm:name": "Pants & Shorts", + "xdm:asset": null + }, + { + "repo:id": "category-77640b4209", + "xdm:name": "Shop The Look", + "xdm:asset": null + }, + { + "repo:id": "category-077469fda4", + "xdm:name": "Minimalist Sensibility", + "xdm:asset": null + } + ], + "xdm:currencyCode": "USD", + "dc:description": "

The Honora Wide Leg Pants definitely hold their own when it comes to standing out from the crowd. These pants feature a unique design and a varying color palette to make pairing a snap.

Features:

  • Elastic waistband
  • Drawstring waist
  • Sits just below the waist
  • 31" inseam
  • Machine wash, line dry
", + "dc:title": "Honora Wide Leg Pants", + "@type": "venia/components/commerce/product" + } } \ No newline at end of file From 962a24968b6288e8da52b72c682bb5cb9329f5a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Levente=20S=C3=A1ntha?= Date: Fri, 21 Jun 2024 12:59:02 +0300 Subject: [PATCH 4/5] SITES-22114 - Release new CIF components version 2.13.0 (#327) * updated dependencies to CIF Components 2.13.0 --- pom.xml | 2 +- ui.frontend/package-lock.json | 10 ---------- ui.frontend/package.json | 4 ++-- 3 files changed, 3 insertions(+), 13 deletions(-) diff --git a/pom.xml b/pom.xml index 9b1d3bc9..34473c46 100644 --- a/pom.xml +++ b/pom.xml @@ -87,7 +87,7 @@ admin admin 2.18.6 - 2.12.6 + 2.13.2 1.7.10 9.1.0-magento242ee 5.1.2 diff --git a/ui.frontend/package-lock.json b/ui.frontend/package-lock.json index 2e050174..915e6a13 100644 --- a/ui.frontend/package-lock.json +++ b/ui.frontend/package-lock.json @@ -4,16 +4,6 @@ "lockfileVersion": 1, "requires": true, "dependencies": { - "@adobe/aem-core-cif-product-recs-extension": { - "version": "2.12.6", - "resolved": "https://artifactory-uw2.adobeitc.com/artifactory/api/npm/npmjs-remote/@adobe/aem-core-cif-product-recs-extension/-/aem-core-cif-product-recs-extension-2.12.6.tgz", - "integrity": "sha512-OJ6VrX6dvoE8t+Z4J4WXXKUoqJtuxRSdnYajsH5/Ptm0ydmMpwkCi1vIdxmJswapZHJN7iRpKeyU5F/+RG7mfQ==" - }, - "@adobe/aem-core-cif-react-components": { - "version": "2.12.6", - "resolved": "https://artifactory-uw2.adobeitc.com/artifactory/api/npm/npmjs-remote/@adobe/aem-core-cif-react-components/-/aem-core-cif-react-components-2.12.6.tgz", - "integrity": "sha512-81uzBKPWPmzcn7NZ5uCVhq6Mb5uccWqx9sFcy7NNIiUNA2VBpTXMZXzcPY+UussSTKEsQ9737InitrYLsGPsOw==" - }, "@apollo/client": { "version": "3.5.6", "resolved": "https://registry.npmjs.org/@apollo/client/-/client-3.5.6.tgz", diff --git a/ui.frontend/package.json b/ui.frontend/package.json index cdc4b6fd..a24f47de 100644 --- a/ui.frontend/package.json +++ b/ui.frontend/package.json @@ -87,8 +87,8 @@ "webpack-merge": "^4.2.1" }, "dependencies": { - "@adobe/aem-core-cif-product-recs-extension": "2.12.6", - "@adobe/aem-core-cif-react-components": "2.12.6", + "@adobe/aem-core-cif-product-recs-extension": "2.13.2", + "@adobe/aem-core-cif-react-components": "2.13.2", "@apollo/client": "^3.5.5", "@babel/runtime": "^7.4.5", "@magento/peregrine": "11.0.0", From 241831c5c47e87a0c138ce930c770bd615b6e821 Mon Sep 17 00:00:00 2001 From: Alwin Joseph Date: Thu, 27 Jun 2024 20:08:44 +0530 Subject: [PATCH 5/5] SITES-22611 - Internal usage pattern, document how to use and taxonomy of errors (#328) * adds filters for commerce api issues * resolves the sling attribute issue * fixed error checking conditions, * updates/add unit testcases * resolves the build issues * fixed POM dependencies to fix the build failure * resolves the integration test issues * resolves the searchresult issue * resolves the placeholder inetgration tests * adds unit test coverage for commercecomponentModelfinder * adds unit test coverage for catalogpageexceptionfilter * undo IT test data changes * resolves the pr comments * removes unwanted import & spaces * fixed page title --------- Co-authored-by: levente --- .../available_vhosts/venia_publish.vhost | 3 +- .../src/conf.d/variables/ams_default.vars | 1 + core/pom.xml | 69 +++++- .../CommerceComponentModelFinder.java | 96 ++++++++ .../servlets/CatalogPageErrorFilter.java | 144 ++++++++++++ .../commerce/MyProductTeaserImplTest.java | 15 +- .../CommerceComponentModelFinderTest.java | 101 +++++++++ .../servlets/CatalogPageErrorFilterTest.java | 213 ++++++++++++++++++ .../src/conf.d/available_vhosts/venia.vhost | 1 + dispatcher/src/conf.d/variables/custom.vars | 1 + pom.xml | 8 +- .../en/errors/503/.content.xml | 56 +++++ .../venia/us/en/errors/503/.content.xml | 56 +++++ 13 files changed, 749 insertions(+), 15 deletions(-) create mode 100644 core/src/main/java/com/venia/core/models/commerce/services/CommerceComponentModelFinder.java create mode 100644 core/src/main/java/com/venia/core/models/commerce/servlets/CatalogPageErrorFilter.java create mode 100644 core/src/test/java/com/venia/core/models/commerce/services/CommerceComponentModelFinderTest.java create mode 100644 core/src/test/java/com/venia/core/models/commerce/servlets/CatalogPageErrorFilterTest.java create mode 100644 ui.content/src/main/content/jcr_root/content/venia/language-masters/en/errors/503/.content.xml create mode 100644 ui.content/src/main/content/jcr_root/content/venia/us/en/errors/503/.content.xml diff --git a/classic/dispatcher/src/conf.d/available_vhosts/venia_publish.vhost b/classic/dispatcher/src/conf.d/available_vhosts/venia_publish.vhost index 7da338dd..5e278053 100644 --- a/classic/dispatcher/src/conf.d/available_vhosts/venia_publish.vhost +++ b/classic/dispatcher/src/conf.d/available_vhosts/venia_publish.vhost @@ -64,4 +64,5 @@ PassEnv DISP_ID ErrorDocument 404 ${404_PAGE} - \ No newline at end of file + ErrorDocument 503 ${503_PAGE} + diff --git a/classic/dispatcher/src/conf.d/variables/ams_default.vars b/classic/dispatcher/src/conf.d/variables/ams_default.vars index f2548c39..2041fc9a 100644 --- a/classic/dispatcher/src/conf.d/variables/ams_default.vars +++ b/classic/dispatcher/src/conf.d/variables/ams_default.vars @@ -22,6 +22,7 @@ Define ASSET_DOWNLOAD_RULE deny # Customer specific values Define CONTENT_FOLDER_NAME venia Define 404_PAGE /content/venia/us/en/errors/404.html +Define 503_PAGE /content/venia/us/en/errors/503.html #Enable/Disable 3DMime type. Enabling default by setting to 1 Define 3D_MIMETYPE_ENABLED 1 diff --git a/core/pom.xml b/core/pom.xml index 0e8e209e..70543cd3 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -135,6 +135,34 @@ Import-Package: javax.annotation;version=0.0.0,* + + javax.servlet + javax.servlet-api + 3.1.0 + provided + + + javax.servlet + jsp-api + 2.0 + provided + + + javax.jcr + jcr + 2.0 + provided + + + org.osgi + osgi.cmpn + 6.0.0 + provided + + + org.slf4j + slf4j-simple + javax.annotation javax.annotation-api @@ -163,6 +191,12 @@ Import-Package: javax.annotation;version=0.0.0,* magento-graphql provided + + org.osgi + osgi.core + 6.0.0 + provided + com.adobe.cq core.wcm.components.core @@ -172,7 +206,7 @@ Import-Package: javax.annotation;version=0.0.0,* org.apache.sling org.apache.sling.api - provided + test org.apache.sling @@ -197,6 +231,14 @@ Import-Package: javax.annotation;version=0.0.0,* org.junit.jupiter junit-jupiter + 5.10.2 + test + + + + org.mockito + mockito-core + 5.11.0 test @@ -213,13 +255,32 @@ Import-Package: javax.annotation;version=0.0.0,* com.adobe.aem uber-jar - 6.4.4 - apis - test + ${uber.jar.version} + provided + + + org.slf4j + slf4j-simple + + io.wcm io.wcm.testing.aem-mock.junit5 + 5.5.0 + test + + + + org.apache.sling + org.apache.sling.testing.sling-mock.junit5 + 3.5.0 + test + + + junit + junit + 4.13.2 test diff --git a/core/src/main/java/com/venia/core/models/commerce/services/CommerceComponentModelFinder.java b/core/src/main/java/com/venia/core/models/commerce/services/CommerceComponentModelFinder.java new file mode 100644 index 00000000..20626241 --- /dev/null +++ b/core/src/main/java/com/venia/core/models/commerce/services/CommerceComponentModelFinder.java @@ -0,0 +1,96 @@ +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ~ Copyright 2021 Adobe + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ +package com.venia.core.models.commerce.services; + +import com.adobe.cq.commerce.core.components.models.product.Product; +import com.adobe.cq.commerce.core.components.models.productlist.ProductList; +import com.drew.lang.annotations.Nullable; +import org.apache.sling.api.SlingHttpServletRequest; +import org.apache.sling.api.resource.Resource; +import org.apache.sling.models.factory.ModelFactory; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; + +/** + * This service allows to traverse a {@link Resource} tree looking for a {@link Resource} of a set of particular resource types and if + * found adapting them to given adapter type. This helps for example finding the product component on the page and return the Product model + * from it. + */ +@Component( + service = com.venia.core.models.commerce.services.CommerceComponentModelFinder.class) +public class CommerceComponentModelFinder { + + private static final Logger LOGGER = LoggerFactory.getLogger(com.venia.core.models.commerce.services.CommerceComponentModelFinder.class); + private static final Collection PRODUCT_RTS = Collections.singleton("core/cif/components/commerce/product/v1/product"); + private static final Collection PRODUCT_LIST_RTS = Arrays.asList( + "core/cif/components/commerce/productlist/v2/productlist", + "core/cif/components/commerce/productlist/v1/productlist"); + + @Reference + private ModelFactory modelFactory; + + @Nullable + public Product findProductComponentModel(SlingHttpServletRequest request, Resource root) { + return findComponentModel(request, root, PRODUCT_RTS, Product.class); + } + + @Nullable + public ProductList findProductListComponentModel(SlingHttpServletRequest request, Resource root) { + return findComponentModel(request, root, PRODUCT_LIST_RTS, ProductList.class); + } + + @Nullable + public T findComponentModel(SlingHttpServletRequest request, Resource root, Collection resourceTypes, + Class adapterType) { + Resource componentResource = findChildResourceWithType(root, resourceTypes); + if (componentResource != null) { + return modelFactory.getModelFromWrappedRequest(request, componentResource, adapterType); + } else { + return null; + } + } + + private Resource findChildResourceWithType(Resource fromResource, Collection resourceTypes) { + if (fromResource == null) { + return null; + } + + LOGGER.debug("Looking for child resource type '{}' from {}", resourceTypes, fromResource.getPath()); + + for (Resource child : fromResource.getChildren()) { + for (String resourceType : resourceTypes) { + if (child.isResourceType(resourceType)) { + LOGGER.debug("Found child resource type '{}' at {}", resourceType, child.getPath()); + return child; + } + } + + Resource resource = findChildResourceWithType(child, resourceTypes); + if (resource != null) { + return resource; + } + } + + return null; + } +} + diff --git a/core/src/main/java/com/venia/core/models/commerce/servlets/CatalogPageErrorFilter.java b/core/src/main/java/com/venia/core/models/commerce/servlets/CatalogPageErrorFilter.java new file mode 100644 index 00000000..9a9cc410 --- /dev/null +++ b/core/src/main/java/com/venia/core/models/commerce/servlets/CatalogPageErrorFilter.java @@ -0,0 +1,144 @@ +package com.venia.core.models.commerce.servlets; + +import com.adobe.cq.commerce.core.components.models.common.SiteStructure; +import com.adobe.cq.commerce.core.components.models.product.Product; +import com.adobe.cq.commerce.core.components.models.productlist.ProductList; +import com.adobe.cq.commerce.core.components.models.retriever.AbstractCategoryRetriever; +import com.adobe.cq.commerce.core.components.models.retriever.AbstractProductRetriever; +import com.day.cq.wcm.api.Page; +import com.day.cq.wcm.api.PageManager; +import com.day.cq.wcm.api.PageManagerFactory; +import com.venia.core.models.commerce.services.CommerceComponentModelFinder; +import org.apache.sling.api.SlingHttpServletRequest; +import org.apache.sling.api.SlingHttpServletResponse; +import org.apache.sling.api.resource.Resource; +import org.apache.sling.api.scripting.SlingBindings; +import org.apache.sling.scripting.core.ScriptHelper; +import org.osgi.framework.BundleContext; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.servlet.*; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +@Component( + service = {Filter.class}, + property = { + "sling.filter.scope=REQUEST", + "sling.filter.resourceTypes=cq:Page", + "sling.filter.resourceTypes=core/cif/components/structure/page/v1/page", + "sling.filter.resourceTypes=core/cif/components/structure/page/v2/page", + "sling.filter.resourceTypes=core/cif/components/structure/page/v3/page", + "sling.filter.extensions=html", + "sling.filter.extensions=json", + "sling.filter.resource.pattern=/content(/.+)?", + "service.ranking:Integer=-4000" + }) +public class CatalogPageErrorFilter implements Filter { + private static final Logger LOGGER = LoggerFactory.getLogger(CatalogPageErrorFilter.class); + + @Reference + private PageManagerFactory pageManagerFactory; + + @Reference + private CommerceComponentModelFinder commerceModelFinder; + + public CatalogPageErrorFilter() { + } + + private BundleContext bundleContext; + + @Activate + protected void activate(BundleContext bundleContext) { + this.bundleContext = bundleContext; + } + + public void init(FilterConfig filterConfig) throws ServletException { + } + + public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { + if (servletRequest instanceof SlingHttpServletRequest && servletResponse instanceof SlingHttpServletResponse) { + SlingHttpServletRequest slingRequest = (SlingHttpServletRequest) servletRequest; + SlingHttpServletResponse slingResponse = (SlingHttpServletResponse) servletResponse; + PageManager pageManager = pageManagerFactory.getPageManager(slingRequest.getResourceResolver()); + Page currentPage = pageManager.getContainingPage(slingRequest.getResource()); + boolean removeSlingScriptHelperFromBindings = false; + if (currentPage != null) { + // Get the SiteStructure model + SiteStructure siteStructure = slingRequest.adaptTo(SiteStructure.class); + if (siteStructure.isProductPage(currentPage)) { + // add the SlingScriptHelper to the bindings if it is not there yet + removeSlingScriptHelperFromBindings = addSlingScriptHelperIfNeeded(slingRequest, slingResponse); + Product product = commerceModelFinder.findProductComponentModel(slingRequest, currentPage.getContentResource()); + if (product != null) { + AbstractProductRetriever productRetriever = product.getProductRetriever(); + // force GraphQL query execution + if (productRetriever != null && !product.getFound() && productRetriever.hasErrors()) { + slingResponse.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE, "Commerce application not reachable"); + return; + } + } + } else if (siteStructure.isCategoryPage(currentPage)) { + // add the SlingScriptHelper to the bindings if it is not there yet + removeSlingScriptHelperFromBindings = addSlingScriptHelperIfNeeded(slingRequest, slingResponse); + ProductList productList = commerceModelFinder.findProductListComponentModel(slingRequest, currentPage.getContentResource()); + if (productList != null) { + // Get the AbstractCategoryRetriever model + AbstractCategoryRetriever categoryRetriever = productList.getCategoryRetriever(); + if ((categoryRetriever != null && + // force GraphQL query execution for category + categoryRetriever.fetchCategory() == null && categoryRetriever.hasErrors()) || + // force GraphQL query execution for products + productList.getSearchResultsSet().hasErrors()) { + slingResponse.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE, "Commerce application not reachable"); + return; + } + } + } + if (removeSlingScriptHelperFromBindings) { + // remove the ScriptHelper if we added it before + SlingBindings slingBindings = getSlingBindings(slingRequest); + if (slingBindings != null) { + slingBindings.remove("sling"); + } + } + } + } + filterChain.doFilter(servletRequest, servletResponse); + } + + public void destroy() { + } + + /** + * The {@link com.venia.core.models.commerce.services.CommerceComponentModelFinder} uses + * {@link org.apache.sling.models.factory.ModelFactory#getModelFromWrappedRequest(SlingHttpServletRequest, Resource, Class)} + * to obtain the model of either {@link Product} or {@link ProductList}. That method invokes all + * {@link org.apache.sling.scripting.api.BindingsValuesProvider} + * while creating the wrapped request. In AEM 6.5 they are not executed lazily and depend on some existing bindings on construction of + * which one requires the SlingScriptHelper. + * + * @param slingRequest + */ + private boolean addSlingScriptHelperIfNeeded(SlingHttpServletRequest slingRequest, SlingHttpServletResponse slingResponse) { + SlingBindings slingBindings = getSlingBindings(slingRequest); + if (slingBindings != null && slingBindings.getSling() == null) { + slingBindings.put("sling", new ScriptHelper(bundleContext, null, slingRequest, slingResponse)); + return true; + } + return false; + } + + private static SlingBindings getSlingBindings(SlingHttpServletRequest slingRequest) { + Object attr = slingRequest.getAttribute(SlingBindings.class.getName()); + if (attr == null) { + attr = new SlingBindings(); + slingRequest.setAttribute(SlingBindings.class.getName(), attr); + } + return attr instanceof SlingBindings ? (SlingBindings) attr : null; + } +} diff --git a/core/src/test/java/com/venia/core/models/commerce/MyProductTeaserImplTest.java b/core/src/test/java/com/venia/core/models/commerce/MyProductTeaserImplTest.java index 4568c5d5..54d6626f 100644 --- a/core/src/test/java/com/venia/core/models/commerce/MyProductTeaserImplTest.java +++ b/core/src/test/java/com/venia/core/models/commerce/MyProductTeaserImplTest.java @@ -41,8 +41,7 @@ import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; -import org.mockito.internal.util.reflection.FieldReader; -import org.mockito.internal.util.reflection.FieldSetter; +import org.apache.commons.lang3.reflect.FieldUtils; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; @@ -120,10 +119,14 @@ void setup(String resourceName) throws Exception { underTest = context.request().adaptTo(MyProductTeaser.class); Assertions.assertNotNull(underTest); - Class clazz = underTest.getClass(); - productTeaser = Mockito.spy((ProductTeaser)(new FieldReader(underTest, clazz.getDeclaredField("productTeaser")).read())); - FieldSetter.setField(underTest, clazz.getDeclaredField("productTeaser"), productTeaser); - FieldSetter.setField(underTest, clazz.getDeclaredField("productRetriever"), productRetriever); + // Use FieldUtils to get the private fields (if needed) + productTeaser = Mockito.spy((ProductTeaser) FieldUtils.readField( + underTest, + "productTeaser", + true + )); + FieldUtils.writeField(underTest, "productTeaser", productTeaser, true); + FieldUtils.writeField(underTest, "productRetriever", productRetriever, true); } @ParameterizedTest diff --git a/core/src/test/java/com/venia/core/models/commerce/services/CommerceComponentModelFinderTest.java b/core/src/test/java/com/venia/core/models/commerce/services/CommerceComponentModelFinderTest.java new file mode 100644 index 00000000..37e8b4ef --- /dev/null +++ b/core/src/test/java/com/venia/core/models/commerce/services/CommerceComponentModelFinderTest.java @@ -0,0 +1,101 @@ +package com.venia.core.models.commerce.services; + + +import com.adobe.cq.commerce.core.components.models.product.Product; +import com.adobe.cq.commerce.core.components.models.productlist.ProductList; +import io.wcm.testing.mock.aem.junit5.AemContext; +import io.wcm.testing.mock.aem.junit5.AemContextExtension; +import org.apache.sling.api.resource.Resource; +import org.apache.sling.models.factory.ModelFactory; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import java.util.Collections; +import java.util.Arrays; +import java.util.List; +import org.apache.sling.testing.mock.sling.servlet.MockSlingHttpServletRequest; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +@ExtendWith({AemContextExtension.class, MockitoExtension.class}) +class CommerceComponentModelFinderTest { + + @Mock + ModelFactory modelFactory; + + @Mock + private Resource resource; + + @Mock + private MockSlingHttpServletRequest request; + + + private final AemContext context = new AemContext(); + + @InjectMocks + private CommerceComponentModelFinder finder; + + @BeforeEach + void setUp() {; + } + + @Test + void findProductComponentModelWithResourceReturnsNullWhenNoProductComponent() { + Product product = finder.findProductComponentModel(request, resource); + assertNull(product); + } + + @Test + void findProductComponentModelWithResourceReturnsProductWhenProductComponentExists() { + Resource mockChildResource = mock(Resource.class); + Product mockProduct = mock(Product.class); + when(resource.getChildren()).thenReturn(Collections.singletonList(mockChildResource)); + when(mockChildResource.isResourceType(anyString())).thenReturn(true); + when(modelFactory.getModelFromWrappedRequest(any(), any(), eq(Product.class))).thenReturn(mockProduct); + Product product = finder.findProductComponentModel(request, resource); + assertNotNull(product); + assertEquals(mockProduct, product); + } + + @Test + void findProductListComponentModelWithResourceReturnsNullWhenNoProductListComponent() { + ProductList productList = finder.findProductListComponentModel(request, resource); + assertNull(productList); + } + + @Test + void findComponentModelWithResourceReturnsProductWhenProductAdapterType() { + Resource mockChildResource = mock(Resource.class); + Product mockProduct = mock(Product.class); + + List mockResourceTypes = Arrays.asList("mockResourceType1", "mockResourceType2"); + + when(resource.getChildren()).thenReturn(Collections.singletonList(mockChildResource)); + when(mockChildResource.isResourceType(anyString())).thenReturn(true); + when(modelFactory.getModelFromWrappedRequest(any(), any(), eq(Product.class))).thenReturn(mockProduct); + + Product product = finder.findComponentModel(request, resource, mockResourceTypes, Product.class); + assertNotNull(product); + assertEquals(mockProduct, product); + } + + @Test + void findComponentModelWithResourceReturnsProductListWhenProductListAdapterType() { + Resource mockChildResource = mock(Resource.class); + ProductList mockProductList = mock(ProductList.class); + + List mockResourceTypes = Arrays.asList("mockResourceType1", "mockResourceType2"); + + when(resource.getChildren()).thenReturn(Collections.singletonList(mockChildResource)); + when(mockChildResource.isResourceType(anyString())).thenReturn(true); + when(modelFactory.getModelFromWrappedRequest(any(), any(), eq(ProductList.class))).thenReturn(mockProductList); + + ProductList productList = finder.findComponentModel(request, resource, mockResourceTypes, ProductList.class); + assertNotNull(productList); + assertEquals(mockProductList, productList); + } +} diff --git a/core/src/test/java/com/venia/core/models/commerce/servlets/CatalogPageErrorFilterTest.java b/core/src/test/java/com/venia/core/models/commerce/servlets/CatalogPageErrorFilterTest.java new file mode 100644 index 00000000..e2db8c45 --- /dev/null +++ b/core/src/test/java/com/venia/core/models/commerce/servlets/CatalogPageErrorFilterTest.java @@ -0,0 +1,213 @@ +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ~ Copyright 2021 Adobe + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ +package com.venia.core.models.commerce.servlets; + +import com.adobe.cq.commerce.core.components.models.common.SiteStructure; +import com.adobe.cq.commerce.core.components.models.product.Product; +import com.adobe.cq.commerce.core.components.models.productlist.CategoryRetriever; +import com.adobe.cq.commerce.core.components.models.productlist.ProductList; +import com.adobe.cq.commerce.core.components.models.retriever.AbstractProductRetriever; +import com.adobe.cq.commerce.core.search.models.SearchResultsSet; +import com.day.cq.wcm.api.Page; +import com.day.cq.wcm.api.PageManager; +import com.day.cq.wcm.api.PageManagerFactory; +import com.venia.core.models.commerce.services.CommerceComponentModelFinder; +import io.wcm.testing.mock.aem.junit5.AemContext; +import io.wcm.testing.mock.aem.junit5.AemContextExtension; +import org.apache.sling.api.resource.Resource; +import org.apache.sling.api.resource.ResourceResolver; +import org.apache.sling.testing.mock.sling.servlet.MockSlingHttpServletRequest; +import org.apache.sling.testing.mock.sling.servlet.MockSlingHttpServletResponse; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.osgi.framework.BundleContext; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; + +@ExtendWith({AemContextExtension.class, MockitoExtension.class}) +public class CatalogPageErrorFilterTest { + + private final AemContext aemContext = new AemContext(); + + @Mock + private PageManagerFactory pageManagerFactory; + + @Mock + private PageManager pageManager; + + @Mock + private Page currentPage; + + @Mock + private Resource resource; + + @Mock + private CommerceComponentModelFinder commerceModelFinder; + + @Mock + private BundleContext bundleContext; + + @Mock + private MockSlingHttpServletRequest slingRequest; + + @Mock + private MockSlingHttpServletResponse slingResponse; + + @InjectMocks + private CatalogPageErrorFilter catalogPageErrorFilter; + + private FilterChain filterChain; + + @BeforeEach + void setUp() { + filterChain = mock(FilterChain.class); + when(pageManagerFactory.getPageManager(any())).thenReturn(pageManager); + when(pageManager.getContainingPage(slingRequest.getResource())).thenReturn(currentPage); + } + + @Test + void doFilterWhenCurrentPageNull() throws IOException, ServletException { + when(pageManager.getContainingPage(slingRequest.getResource())).thenReturn(null); + + catalogPageErrorFilter.doFilter(slingRequest, slingResponse, filterChain); + verify(filterChain).doFilter(slingRequest, slingResponse); + + } + + @Test + void doFilterWhenProductNull() throws IOException, ServletException { + ResourceResolver resourceResolver = mock(ResourceResolver.class); + SiteStructure siteStructure = mock(SiteStructure.class); + + when(slingRequest.getResourceResolver()).thenReturn(resourceResolver); + when(slingRequest.adaptTo(SiteStructure.class)).thenReturn(siteStructure); + when(siteStructure.isProductPage(any())).thenReturn(true); + when(commerceModelFinder.findProductComponentModel(any(), any())).thenReturn(null); + + catalogPageErrorFilter.doFilter(slingRequest, slingResponse, filterChain); + + verify(filterChain).doFilter(slingRequest, slingResponse); + } + + @Test + void doFilterWhenProductRetrieverNull() throws IOException, ServletException { + ResourceResolver resourceResolver = mock(ResourceResolver.class); + SiteStructure siteStructure = mock(SiteStructure.class); + Product product = mock(Product.class); + + + when(slingRequest.getResourceResolver()).thenReturn(resourceResolver); + when(slingRequest.adaptTo(SiteStructure.class)).thenReturn(siteStructure); + when(siteStructure.isProductPage(any())).thenReturn(true); + when(commerceModelFinder.findProductComponentModel(any(), any())).thenReturn(product); + when(product.getProductRetriever()).thenReturn(null); + catalogPageErrorFilter.doFilter(slingRequest, slingResponse, filterChain); + verify(filterChain).doFilter(slingRequest, slingResponse); + } + + @Test + void doFilterWhenProductRetrieverHasErrors() throws IOException, ServletException { + ResourceResolver resourceResolver = mock(ResourceResolver.class); + SiteStructure siteStructure = mock(SiteStructure.class); + Product product = mock(Product.class); + AbstractProductRetriever productRetriever = mock(AbstractProductRetriever.class); + + when(slingRequest.getResourceResolver()).thenReturn(resourceResolver); + when(slingRequest.adaptTo(SiteStructure.class)).thenReturn(siteStructure); + when(siteStructure.isProductPage(any())).thenReturn(true); + when(commerceModelFinder.findProductComponentModel(any(), any())).thenReturn(product); + when(product.getProductRetriever()).thenReturn(productRetriever); + when(productRetriever.hasErrors()).thenReturn(true); + + catalogPageErrorFilter.doFilter(slingRequest, slingResponse, filterChain); + verify(slingResponse).sendError( + HttpServletResponse.SC_SERVICE_UNAVAILABLE, + "Commerce application not reachable" + ); + verify(filterChain, never()).doFilter(slingRequest, slingResponse); + } + + @Test + void doFilterWithCategoryPageWhenProductListNull() throws IOException, ServletException { + ResourceResolver resourceResolver = mock(ResourceResolver.class); + SiteStructure siteStructure = mock(SiteStructure.class); + + when(slingRequest.getResourceResolver()).thenReturn(resourceResolver); + when(slingRequest.adaptTo(SiteStructure.class)).thenReturn(siteStructure); + when(siteStructure.isCategoryPage(any())).thenReturn(true); + when(commerceModelFinder.findProductListComponentModel(any(), any())).thenReturn(null); + catalogPageErrorFilter.doFilter(slingRequest, slingResponse, filterChain); + + verify(filterChain).doFilter(slingRequest, slingResponse); + } + + @Test + void doFilterWithCategoryPageWhenCategoryRetrieverNullAndSearchResultErrorEmpty() throws IOException, ServletException { + PageManager pageManager = mock(PageManager.class); + Page currentPage = mock(Page.class); + ResourceResolver resourceResolver = mock(ResourceResolver.class); + SiteStructure siteStructure = mock(SiteStructure.class); + ProductList productList = mock(ProductList.class); + SearchResultsSet searchResultsSet = mock(SearchResultsSet.class); + + when(slingRequest.getResourceResolver()).thenReturn(resourceResolver); + when(slingRequest.adaptTo(SiteStructure.class)).thenReturn(siteStructure); + when(siteStructure.isCategoryPage(any())).thenReturn(true); + when(commerceModelFinder.findProductListComponentModel(any(), any())).thenReturn(productList); + when(productList.getCategoryRetriever()).thenReturn(null); + when(productList.getSearchResultsSet()).thenReturn(searchResultsSet); + when(searchResultsSet.hasErrors()).thenReturn(false); + catalogPageErrorFilter.doFilter(slingRequest, slingResponse, filterChain); + verify(filterChain).doFilter(slingRequest, slingResponse); + } + + @Test + void doFilterWithCategoryPageHasErrors() throws IOException, ServletException { + PageManager pageManager = mock(PageManager.class); + Page currentPage = mock(Page.class); + ResourceResolver resourceResolver = mock(ResourceResolver.class); + SiteStructure siteStructure = mock(SiteStructure.class); + ProductList productList = mock(ProductList.class); + CategoryRetriever categoryRetriever = mock(CategoryRetriever.class); + + when(slingRequest.getResourceResolver()).thenReturn(resourceResolver); + when(slingRequest.adaptTo(SiteStructure.class)).thenReturn(siteStructure); + when(siteStructure.isCategoryPage(any())).thenReturn(true); + when(commerceModelFinder.findProductListComponentModel(any(), any())).thenReturn(productList); + when(productList.getCategoryRetriever()).thenReturn(categoryRetriever); + when(categoryRetriever.fetchCategory()).thenReturn(null); + when(categoryRetriever.hasErrors()).thenReturn(true); + + catalogPageErrorFilter.doFilter(slingRequest, slingResponse, filterChain); + + verify(slingResponse).sendError( + HttpServletResponse.SC_SERVICE_UNAVAILABLE, + "Commerce application not reachable" + ); + verify(filterChain, never()).doFilter(slingRequest, slingResponse); + } + +} diff --git a/dispatcher/src/conf.d/available_vhosts/venia.vhost b/dispatcher/src/conf.d/available_vhosts/venia.vhost index 99ac19d3..eb98f757 100644 --- a/dispatcher/src/conf.d/available_vhosts/venia.vhost +++ b/dispatcher/src/conf.d/available_vhosts/venia.vhost @@ -64,4 +64,5 @@ Include conf.d/variables/custom.vars # Customizations made to the vhost compared to default ErrorDocument 404 ${404_PAGE} + ErrorDocument 503 ${503_PAGE} diff --git a/dispatcher/src/conf.d/variables/custom.vars b/dispatcher/src/conf.d/variables/custom.vars index 743d81aa..ed9c72bb 100644 --- a/dispatcher/src/conf.d/variables/custom.vars +++ b/dispatcher/src/conf.d/variables/custom.vars @@ -5,3 +5,4 @@ # Define CONTENT_FOLDER_NAME venia Define 404_PAGE /content/venia/us/en/errors/404.html +Define 503_PAGE /content/venia/us/en/errors/503.html diff --git a/pom.xml b/pom.xml index 34473c46..ee7ab379 100644 --- a/pom.xml +++ b/pom.xml @@ -93,7 +93,7 @@ 5.1.2 UTF-8 UTF-8 - 6.5.0 + 6.5.8 0.8.7 2020.9.4277.20200929T030741Z-200924 2022.08.02.00 @@ -153,8 +153,8 @@ org.apache.maven.plugins maven-compiler-plugin - 1.8 - 1.8 + 11 + 11 @@ -807,7 +807,7 @@ Bundle-DocURL: org.apache.sling org.apache.sling.api - 2.16.4 + 2.27.0 org.apache.sling diff --git a/ui.content/src/main/content/jcr_root/content/venia/language-masters/en/errors/503/.content.xml b/ui.content/src/main/content/jcr_root/content/venia/language-masters/en/errors/503/.content.xml new file mode 100644 index 00000000..4f889dfc --- /dev/null +++ b/ui.content/src/main/content/jcr_root/content/venia/language-masters/en/errors/503/.content.xml @@ -0,0 +1,56 @@ + + + + + + + <container + cq:lastRolledout="{Date}2021-10-15T11:13:25.925+02:00" + cq:lastRolledoutBy="admin" + jcr:mixinTypes="[cq:LiveRelationship]" + jcr:primaryType="nt:unstructured" + sling:resourceType="venia/components/container" + layout="responsiveGrid"> + <text + jcr:lastModified="{Date}2024-06-04T18:29:41.763+05:30" + jcr:lastModifiedBy="admin" + jcr:primaryType="nt:unstructured" + sling:resourceType="venia/components/text" + text="<p>&nbsp;</p> <h1>Ruh-Roh! Service Unavailable</h1> <p>&nbsp;</p> <p>We're sorry, we are unable to fetch this page!</p> <p>&nbsp;</p> <p>&nbsp;</p> <p>&nbsp;</p> " + textIsRich="true"/> + </container> + </container> + </root> + </jcr:content> +</jcr:root> diff --git a/ui.content/src/main/content/jcr_root/content/venia/us/en/errors/503/.content.xml b/ui.content/src/main/content/jcr_root/content/venia/us/en/errors/503/.content.xml new file mode 100644 index 00000000..4f889dfc --- /dev/null +++ b/ui.content/src/main/content/jcr_root/content/venia/us/en/errors/503/.content.xml @@ -0,0 +1,56 @@ +<?xml version="1.0" encoding="UTF-8"?> +<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:mix="http://www.jcp.org/jcr/mix/1.0" xmlns:nt="http://www.jcp.org/jcr/nt/1.0" + jcr:primaryType="cq:Page"> + <jcr:content + cq:allowedTemplates="[/conf/venia/settings/wcm/templates/page-content]" + cq:lastModified="{Date}2024-06-04T18:29:41.772+05:30" + cq:lastModifiedBy="admin" + cq:lastRolledoutBy="admin" + cq:robotsTags="[noindex]" + cq:template="/conf/venia/settings/wcm/templates/page-content" + jcr:isCheckedOut="{Boolean}true" + jcr:mixinTypes="[cq:LiveRelationship,mix:versionable]" + jcr:primaryType="cq:PageContent" + jcr:title="Service Unavailable" + jcr:uuid="79995388-8a07-41de-aa5c-17b262989be0" + sling:resourceType="venia/components/page" + pwaCachestrategy="staleWhileRevalidate" + pwaDisplay="standalone" + pwaOrientation="any"> + <root + cq:lastRolledout="{Date}2021-10-15T11:13:25.907+02:00" + cq:lastRolledoutBy="admin" + jcr:mixinTypes="[cq:LiveRelationship]" + jcr:primaryType="nt:unstructured" + sling:resourceType="venia/components/container"> + <container + cq:lastRolledout="{Date}2021-10-15T11:13:25.915+02:00" + cq:lastRolledoutBy="admin" + jcr:mixinTypes="[cq:LiveRelationship]" + jcr:primaryType="nt:unstructured" + sling:resourceType="venia/components/container"> + <title + cq:lastRolledout="{Date}2021-10-15T11:13:25.920+02:00" + cq:lastRolledoutBy="admin" + jcr:mixinTypes="[cq:LiveRelationship]" + jcr:primaryType="nt:unstructured" + sling:resourceType="venia/components/title"/> + <container + cq:lastRolledout="{Date}2021-10-15T11:13:25.925+02:00" + cq:lastRolledoutBy="admin" + jcr:mixinTypes="[cq:LiveRelationship]" + jcr:primaryType="nt:unstructured" + sling:resourceType="venia/components/container" + layout="responsiveGrid"> + <text + jcr:lastModified="{Date}2024-06-04T18:29:41.763+05:30" + jcr:lastModifiedBy="admin" + jcr:primaryType="nt:unstructured" + sling:resourceType="venia/components/text" + text="<p>&nbsp;</p> <h1>Ruh-Roh! Service Unavailable</h1> <p>&nbsp;</p> <p>We're sorry, we are unable to fetch this page!</p> <p>&nbsp;</p> <p>&nbsp;</p> <p>&nbsp;</p> " + textIsRich="true"/> + </container> + </container> + </root> + </jcr:content> +</jcr:root>