diff --git a/bundles/core/src/main/java/com/adobe/cq/commerce/core/components/internal/datalayer/AssetDataImpl.java b/bundles/core/src/main/java/com/adobe/cq/commerce/core/components/internal/datalayer/AssetDataImpl.java index ba1eff4818..484cd60869 100644 --- a/bundles/core/src/main/java/com/adobe/cq/commerce/core/components/internal/datalayer/AssetDataImpl.java +++ b/bundles/core/src/main/java/com/adobe/cq/commerce/core/components/internal/datalayer/AssetDataImpl.java @@ -18,11 +18,9 @@ import java.util.Date; import java.util.Map; -import org.apache.commons.codec.digest.DigestUtils; -import org.apache.commons.lang3.StringUtils; - import com.adobe.cq.commerce.core.components.models.product.Asset; import com.adobe.cq.wcm.core.components.models.datalayer.AssetData; +import com.adobe.cq.wcm.core.components.util.ComponentUtils; public class AssetDataImpl implements AssetData { private final Asset asset; @@ -33,8 +31,7 @@ public AssetDataImpl(Asset asset) { @Override public String getId() { - return StringUtils.join(asset.getType(), DataLayerComponent.ID_SEPARATOR, StringUtils.substring(DigestUtils.sha256Hex(asset - .getPath()), 0, 10)); + return ComponentUtils.generateId(asset.getType(), asset.getPath()); } @Override diff --git a/bundles/core/src/main/java/com/adobe/cq/commerce/core/components/internal/datalayer/CategoryDataImpl.java b/bundles/core/src/main/java/com/adobe/cq/commerce/core/components/internal/datalayer/CategoryDataImpl.java index beff18984f..909951f86c 100644 --- a/bundles/core/src/main/java/com/adobe/cq/commerce/core/components/internal/datalayer/CategoryDataImpl.java +++ b/bundles/core/src/main/java/com/adobe/cq/commerce/core/components/internal/datalayer/CategoryDataImpl.java @@ -15,12 +15,10 @@ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ package com.adobe.cq.commerce.core.components.internal.datalayer; -import org.apache.commons.codec.digest.DigestUtils; -import org.apache.commons.lang3.StringUtils; - import com.adobe.cq.commerce.core.components.datalayer.CategoryData; import com.adobe.cq.commerce.core.components.internal.models.v1.product.AssetImpl; import com.adobe.cq.wcm.core.components.models.datalayer.AssetData; +import com.adobe.cq.wcm.core.components.util.ComponentUtils; public class CategoryDataImpl implements CategoryData { private String id; @@ -35,7 +33,7 @@ public CategoryDataImpl(String id, String name, String image) { @Override public String getId() { - return StringUtils.join("category", DataLayerComponent.ID_SEPARATOR, StringUtils.substring(DigestUtils.sha256Hex(id), 0, 10)); + return ComponentUtils.generateId("category", id); } @Override diff --git a/bundles/core/src/main/java/com/adobe/cq/commerce/core/components/internal/datalayer/DataLayerComponent.java b/bundles/core/src/main/java/com/adobe/cq/commerce/core/components/internal/datalayer/DataLayerComponent.java index f2f7ee9388..399ef458df 100644 --- a/bundles/core/src/main/java/com/adobe/cq/commerce/core/components/internal/datalayer/DataLayerComponent.java +++ b/bundles/core/src/main/java/com/adobe/cq/commerce/core/components/internal/datalayer/DataLayerComponent.java @@ -17,23 +17,27 @@ import javax.inject.Inject; -import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.lang3.StringUtils; import org.apache.sling.api.resource.Resource; import org.apache.sling.api.resource.ValueMap; import org.apache.sling.caconfig.ConfigurationBuilder; +import org.apache.sling.models.annotations.injectorspecific.Self; import com.adobe.cq.commerce.core.components.datalayer.CategoryData; +import com.adobe.cq.wcm.core.components.models.Component; import com.adobe.cq.wcm.core.components.models.datalayer.AssetData; import com.adobe.cq.wcm.core.components.models.datalayer.ComponentData; +import com.adobe.cq.wcm.core.components.util.ComponentUtils; import com.fasterxml.jackson.annotation.JsonIgnore; public abstract class DataLayerComponent { - public static final String ID_SEPARATOR = "-"; @Inject protected Resource resource; + @Self + private Component wcmComponent; + private String id; private Boolean dataLayerEnabled; private ComponentData componentData; @@ -69,10 +73,14 @@ protected ComponentData getComponentData() { } protected String generateId() { - String resourceType = resource.getResourceType(); - String prefix = StringUtils.substringAfterLast(resourceType, "/"); - String path = resource.getPath(); - return StringUtils.join(prefix, ID_SEPARATOR, StringUtils.substring(DigestUtils.sha256Hex(path), 0, 10)); + if (wcmComponent == null) { + String resourceType = resource.getResourceType(); + String prefix = StringUtils.substringAfterLast(resourceType, "/"); + String path = resource.getPath(); + return ComponentUtils.generateId(prefix, path); + } else { + return wcmComponent.getId(); + } } public String getId() { diff --git a/bundles/core/src/main/java/com/adobe/cq/commerce/core/components/internal/datalayer/DataLayerListItem.java b/bundles/core/src/main/java/com/adobe/cq/commerce/core/components/internal/datalayer/DataLayerListItem.java index 0035d3011e..ca7fe6c5c7 100644 --- a/bundles/core/src/main/java/com/adobe/cq/commerce/core/components/internal/datalayer/DataLayerListItem.java +++ b/bundles/core/src/main/java/com/adobe/cq/commerce/core/components/internal/datalayer/DataLayerListItem.java @@ -15,10 +15,13 @@ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ package com.adobe.cq.commerce.core.components.internal.datalayer; -import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.lang3.StringUtils; import org.apache.sling.api.resource.Resource; +import com.adobe.cq.wcm.core.components.util.ComponentUtils; + +import static com.adobe.cq.wcm.core.components.util.ComponentUtils.ID_SEPARATOR; + public abstract class DataLayerListItem extends DataLayerComponent { public static final String ITEM_ID_PREFIX = "item"; @@ -37,6 +40,6 @@ protected String getIdentifier() { @Override protected String generateId() { String prefix = StringUtils.join(parentId, ID_SEPARATOR, ITEM_ID_PREFIX); - return StringUtils.join(prefix, ID_SEPARATOR, StringUtils.substring(DigestUtils.sha256Hex(getIdentifier()), 0, 10)); + return ComponentUtils.generateId(prefix, getIdentifier()); } } diff --git a/bundles/core/src/main/java/com/adobe/cq/commerce/core/components/internal/models/v1/common/ProductListItemImpl.java b/bundles/core/src/main/java/com/adobe/cq/commerce/core/components/internal/models/v1/common/ProductListItemImpl.java index c46f69e5b2..f11d535183 100644 --- a/bundles/core/src/main/java/com/adobe/cq/commerce/core/components/internal/models/v1/common/ProductListItemImpl.java +++ b/bundles/core/src/main/java/com/adobe/cq/commerce/core/components/internal/models/v1/common/ProductListItemImpl.java @@ -19,7 +19,6 @@ import javax.annotation.Nullable; -import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.lang3.StringUtils; import org.apache.sling.api.SlingHttpServletRequest; @@ -31,9 +30,12 @@ import com.adobe.cq.commerce.core.components.services.urls.UrlProvider; import com.adobe.cq.commerce.core.components.services.urls.UrlProvider.ParamsBuilder; import com.adobe.cq.wcm.core.components.models.datalayer.ComponentData; +import com.adobe.cq.wcm.core.components.util.ComponentUtils; import com.day.cq.wcm.api.Page; import com.fasterxml.jackson.annotation.JsonIgnore; +import static com.adobe.cq.wcm.core.components.util.ComponentUtils.ID_SEPARATOR; + public class ProductListItemImpl extends DataLayerListItem implements ProductListItem { private String sku; @@ -151,7 +153,7 @@ protected ComponentData getComponentData() { @Override protected String generateId() { String prefix = StringUtils.join(parentId, ID_SEPARATOR, ITEM_ID_PREFIX); - return StringUtils.join(prefix, ID_SEPARATOR, StringUtils.substring(DigestUtils.sha256Hex(getSKU()), 0, 10)); + return ComponentUtils.generateId(prefix, getSKU()); } @Override diff --git a/bundles/core/src/main/java/com/adobe/cq/commerce/core/components/internal/models/v1/contentfragment/CommerceContentFragmentImpl.java b/bundles/core/src/main/java/com/adobe/cq/commerce/core/components/internal/models/v1/contentfragment/CommerceContentFragmentImpl.java index 6c07a0d1b2..8672fae6d9 100644 --- a/bundles/core/src/main/java/com/adobe/cq/commerce/core/components/internal/models/v1/contentfragment/CommerceContentFragmentImpl.java +++ b/bundles/core/src/main/java/com/adobe/cq/commerce/core/components/internal/models/v1/contentfragment/CommerceContentFragmentImpl.java @@ -313,6 +313,11 @@ public String getEditorJSON() { return contentFragment.getEditorJSON(); } + @Override + public String getId() { + return contentFragment.getId(); + } + static class EmptyContentFragment implements ContentFragment { /** * The empty return value means that the model does not contain a valid content fragment. diff --git a/bundles/core/src/main/java/com/adobe/cq/commerce/core/components/internal/models/v1/product/ProductImpl.java b/bundles/core/src/main/java/com/adobe/cq/commerce/core/components/internal/models/v1/product/ProductImpl.java index 8756793d51..070364937d 100644 --- a/bundles/core/src/main/java/com/adobe/cq/commerce/core/components/internal/models/v1/product/ProductImpl.java +++ b/bundles/core/src/main/java/com/adobe/cq/commerce/core/components/internal/models/v1/product/ProductImpl.java @@ -73,8 +73,10 @@ import com.adobe.cq.commerce.magento.graphql.SimpleProduct; import com.adobe.cq.commerce.magento.graphql.VirtualProduct; import com.adobe.cq.sightly.SightlyWCMMode; +import com.adobe.cq.wcm.core.components.models.Component; import com.adobe.cq.wcm.core.components.models.datalayer.AssetData; import com.adobe.cq.wcm.core.components.models.datalayer.ComponentData; +import com.adobe.cq.wcm.core.components.util.ComponentUtils; import com.adobe.cq.wcm.launches.utils.LaunchUtils; import com.day.cq.commons.Externalizer; import com.day.cq.wcm.api.Page; @@ -82,6 +84,8 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; +import static com.adobe.cq.wcm.core.components.util.ComponentUtils.ID_SEPARATOR; + @Model( adaptables = SlingHttpServletRequest.class, adapters = Product.class, @@ -429,7 +433,16 @@ public ComponentData getComponentData() { @Override protected String generateId() { - return StringUtils.join("product", ID_SEPARATOR, StringUtils.substring(DigestUtils.sha256Hex(getSku()), 0, 10)); + String id = super.generateId(); + if (StringUtils.isNotBlank(properties.get(Component.PN_ID, String.class))) { + // if available use the id provided by the user + return id; + } else { + // otherwise include the product SKU in the id + String prefix = StringUtils.substringBefore(id, ID_SEPARATOR); + String suffix = StringUtils.substringAfterLast(id, ID_SEPARATOR) + getSku(); + return ComponentUtils.generateId(prefix, suffix); + } } @Override diff --git a/bundles/core/src/main/java/com/adobe/cq/commerce/core/search/internal/converters/ProductToProductListItemConverter.java b/bundles/core/src/main/java/com/adobe/cq/commerce/core/search/internal/converters/ProductToProductListItemConverter.java index 0d6e3a82cb..280e8ef09c 100644 --- a/bundles/core/src/main/java/com/adobe/cq/commerce/core/search/internal/converters/ProductToProductListItemConverter.java +++ b/bundles/core/src/main/java/com/adobe/cq/commerce/core/search/internal/converters/ProductToProductListItemConverter.java @@ -18,14 +18,12 @@ import java.util.Locale; import java.util.function.Function; -import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.lang3.StringUtils; import org.apache.sling.api.SlingHttpServletRequest; import org.apache.sling.api.resource.Resource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.adobe.cq.commerce.core.components.internal.datalayer.DataLayerComponent; import com.adobe.cq.commerce.core.components.internal.models.v1.common.PriceImpl; import com.adobe.cq.commerce.core.components.internal.models.v1.common.ProductListItemImpl; import com.adobe.cq.commerce.core.components.models.common.Price; @@ -34,6 +32,7 @@ import com.adobe.cq.commerce.magento.graphql.GroupedProduct; import com.adobe.cq.commerce.magento.graphql.ProductImage; import com.adobe.cq.commerce.magento.graphql.ProductInterface; +import com.adobe.cq.wcm.core.components.util.ComponentUtils; import com.day.cq.wcm.api.Page; /** @@ -69,8 +68,7 @@ public ProductListItem apply(final ProductInterface product) { String resourceType = parentResource.getResourceType(); String prefix = StringUtils.substringAfterLast(resourceType, "/"); String path = parentResource.getPath(); - String parentId = StringUtils.join(prefix, DataLayerComponent.ID_SEPARATOR, StringUtils.substring(DigestUtils.sha256Hex(path), - 0, 10)); + String parentId = ComponentUtils.generateId(prefix, path); ProductListItem productListItem = new ProductListItemImpl(product.getSku(), product.getUrlKey(), diff --git a/bundles/core/src/test/java/com/adobe/cq/commerce/core/components/internal/models/v1/page/PageMetadataImplTest.java b/bundles/core/src/test/java/com/adobe/cq/commerce/core/components/internal/models/v1/page/PageMetadataImplTest.java index 430c51350d..0817d1ce71 100644 --- a/bundles/core/src/test/java/com/adobe/cq/commerce/core/components/internal/models/v1/page/PageMetadataImplTest.java +++ b/bundles/core/src/test/java/com/adobe/cq/commerce/core/components/internal/models/v1/page/PageMetadataImplTest.java @@ -164,7 +164,7 @@ public void testPageMetadataModelOnProductPage() throws Exception { // Asserts that the right product resource is used when PageMetadataImpl adapts the request to the Product component ComponentData data = productModel.getData(); assertEquals("venia/components/commerce/product", data.getType()); - assertEquals("product-8309e8957e", data.getId()); + assertEquals("product-3944cc709b", data.getId()); } @Test diff --git a/bundles/core/src/test/java/com/adobe/cq/commerce/core/components/internal/models/v1/product/ProductImplTest.java b/bundles/core/src/test/java/com/adobe/cq/commerce/core/components/internal/models/v1/product/ProductImplTest.java index 626fc7e1af..435493290b 100644 --- a/bundles/core/src/test/java/com/adobe/cq/commerce/core/components/internal/models/v1/product/ProductImplTest.java +++ b/bundles/core/src/test/java/com/adobe/cq/commerce/core/components/internal/models/v1/product/ProductImplTest.java @@ -76,6 +76,7 @@ import io.wcm.testing.mock.aem.junit.AemContextCallback; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; import static org.mockito.Matchers.any; import static org.mockito.Mockito.RETURNS_DEEP_STUBS; import static org.mockito.Mockito.mock; @@ -119,6 +120,7 @@ private static AemContext createContext(String contentPath) { private static final String PAGE = "/content/pageA"; private static final String PRODUCT = "/content/pageA/jcr:content/root/responsivegrid/product"; + private static final String PRODUCT_WITH_ID = "/content/pageA/jcr:content/root/responsivegrid/productwithid"; private Resource productResource; private Resource pageResource; @@ -326,7 +328,7 @@ public void testSafeDescriptionWithNull() { SimpleProduct product = mock(SimpleProduct.class, RETURNS_DEEP_STUBS); when(product.getDescription()).thenReturn(null); Whitebox.setInternalState(productModel.getProductRetriever(), "product", Optional.of(product)); - Assert.assertNull(productModel.getDescription()); + assertNull(productModel.getDescription()); } @Test @@ -339,7 +341,7 @@ public void testSafeDescriptionHtmlNull() { Whitebox.setInternalState(productModel.getProductRetriever(), "product", Optional.of(product)); - Assert.assertNull(productModel.getDescription()); + assertNull(productModel.getDescription()); } @Test @@ -487,7 +489,7 @@ public void testProductNoGraphqlClient() { Assert.assertFalse("Product is not configurable", productModel.isConfigurable()); Assert.assertFalse("Product is not a grouped product", productModel.isGroupedProduct()); Assert.assertFalse("Product is not virtual", productModel.isVirtualProduct()); - Assert.assertNull("The product retriever is not created", productModel.getProductRetriever()); + assertNull("The product retriever is not created", productModel.getProductRetriever()); } @Test @@ -553,4 +555,18 @@ public void testStorefrontContextRender() throws IOException { String jsonResult = productModel.getStorefrontContext().getJson(); assertEquals(mapper.readTree(expected), mapper.readTree(jsonResult)); } + + @Test + public void testManualHtmlId() throws IOException { + context.currentResource(PRODUCT_WITH_ID); + context.request().setServletPath(PRODUCT_WITH_ID + ".beaumont-summit-kit.html"); + productResource = context.resourceResolver().getResource(PRODUCT_WITH_ID); + SlingBindings slingBindings = (SlingBindings) context.request().getAttribute(SlingBindings.class.getName()); + slingBindings.setResource(productResource); + slingBindings.put(WCMBindingsConstants.NAME_PROPERTIES, productResource.getValueMap()); + + productModel = context.request().adaptTo(ProductImpl.class); + + assertEquals("custom-id", productModel.getId()); + } } diff --git a/bundles/core/src/test/resources/context/jcr-content.json b/bundles/core/src/test/resources/context/jcr-content.json index 85bc21717b..eb9165ea5e 100644 --- a/bundles/core/src/test/resources/context/jcr-content.json +++ b/bundles/core/src/test/resources/context/jcr-content.json @@ -55,6 +55,10 @@ "product": { "sling:resourceType": "venia/components/commerce/product" }, + "productwithid": { + "sling:resourceType": "venia/components/commerce/product", + "id": "custom-id" + }, "productlist": { "loadClientPrice": true, "showImage": true, diff --git a/bundles/core/src/test/resources/results/result-datalayer-product-component.json b/bundles/core/src/test/resources/results/result-datalayer-product-component.json index afa145e4b0..a741cb18bd 100644 --- a/bundles/core/src/test/resources/results/result-datalayer-product-component.json +++ b/bundles/core/src/test/resources/results/result-datalayer-product-component.json @@ -1,5 +1,5 @@ { - "product-8309e8957e": { + "product-a8f26bcdca": { "xdm:SKU": "MJ01", "xdm:listPrice": 58.0, "xdm:categories": [ diff --git a/it/http/src/test/resources/datalayer/chaz-kangeroo-hoodie-product.json b/it/http/src/test/resources/datalayer/chaz-kangeroo-hoodie-product.json index 34f57570b3..b92fc6d832 100644 --- a/it/http/src/test/resources/datalayer/chaz-kangeroo-hoodie-product.json +++ b/it/http/src/test/resources/datalayer/chaz-kangeroo-hoodie-product.json @@ -1,5 +1,5 @@ { - "product-500741b9f1": { + "product-4489fda6a6": { "xdm:SKU": "MH01", "xdm:listPrice": 52.0, "xdm:assets": [ diff --git a/ui.apps/src/main/content/jcr_root/apps/core/cif/components/commerce/carousel/v1/carousel/_cq_dialog/.content.xml b/ui.apps/src/main/content/jcr_root/apps/core/cif/components/commerce/carousel/v1/carousel/_cq_dialog/.content.xml index 626c92adf9..1a313b358a 100644 --- a/ui.apps/src/main/content/jcr_root/apps/core/cif/components/commerce/carousel/v1/carousel/_cq_dialog/.content.xml +++ b/ui.apps/src/main/content/jcr_root/apps/core/cif/components/commerce/carousel/v1/carousel/_cq_dialog/.content.xml @@ -17,7 +17,7 @@