Expand my Community achievements bar.

Guidelines for the Responsible Use of Generative AI in the Experience Cloud Community.
SOLVED

Wanted to understand data/logic flow of product component of CIF

Avatar

Level 8

 

Hi Team,

Can someone please explain me the logic of the CIF component: /apps/core/cif/components/commerce/product/v3/product
Foe testing purpose, I have deleted all the content from /apps/core/cif/components/commerce/product/v3/product/productFullDetail.html
Stil, in the logs I can see:
com.adobe.cq.commerce.core.components.client.MagentoGraphqlClient Cache hit for query
'{products(filter:{url_key:{eq:"carina-cardigan"}}){items{__typename,sku,url_key,name,description{html},image{label,url},thumbnail{l ....

That means, even if we are not rendering anything from Sightly component, still backend logic calls the GraphQL query to get the data.
Ofcourse, Sling model is called from: /apps/core/cif/components/commerce/product/v3/product/product.html

Can someone tell where exactly the logic is written to the GraphQL response:
I can see com.adobe.cq.commerce.core.components.models.retriever.AbstractProductRetriever
Here, method:

 

 @Override
    protected void populate() {
        // Get product list from response
        GraphqlResponse<Query, Error> response = executeQuery();
        errors = response.getErrors();
        if (CollectionUtils.isEmpty(errors)) {
            Query rootQuery = response.getData();
            List<ProductInterface> products = rootQuery.getProducts().getItems();

            // Return first product in list unless the identifier type is 'url_key',
            // then return the product whose 'url_key' matches the identifier
            if (products.size() > 0) {
                product = Optional.of(products.get(0));
            } else {
                product = Optional.empty();
            }
        } else {
            product = Optional.empty();
        }
    }

 

Is this, the logic to get the particular product details. If yes, which method calls this. I was traversing through : com.adobe.cq.commerce.core.components.internal.models.v3.product.ProductImpl and then its super classes to crack the logic. Still, I am bit confused here.

Can someone help to crack the logic of this component.

 

cc: @Hemalatha  @AMANATH_ULLAH  @MukeshYadav_ 

Thanks in advance.

1 Accepted Solution

Avatar

Correct answer by
Community Advisor

Hi @Mahesh_Gunaje 
Firstly 

com.adobe.cq.commerce.core.components.internal.servlets.
SpecificPageFilterFactory
Filter gets triggered then the 
com.adobe.cq.commerce.core.components.internal.servlets.
CatalogPageNotFoundFilter
triggers.
Which internally calls the populate method on the class com.adobe.cq.commerce.core.components.internal.models.v1.product.ProductImpl object.

However the Graphql request goes mutiple times to the GraphqlClient
It serves request from localCahe and does not exectue pre-executed queries all over again.

You can checkout code for the same in the executeCached method
com.adobe.cq.commerce.core.components.internal.client.
MagentoGraphqlClientImpl

 

private GraphqlResponse<Query, Error> executeCached(String query, RequestOptions options) {
        try {
            if (localResponseCache != null && localResponseCache.containsKey(query)) {
                return localResponseCache.get(query);
            }

            GraphqlRequest request = new GraphqlRequest(query);
            GraphqlResponse<Query, Error> response = graphqlClient.execute(request, Query.class, Error.class, options);

            if (localResponseCache != null) {
                localResponseCache.put(query, response);
            }

            return response;
        } catch (RuntimeException ex) {
            LOGGER.error("Failed to execute query: {}", query, ex);
            return newErrorResponse(ex);
        }
    }

Let me know if this was your query.


 

View solution in original post

5 Replies

Avatar

Community Advisor

Hi @Mahesh_Gunaje 
You are correct,
Inside com.adobe.cq.commerce.core.components.models.retriever.AbstractProductRetriever
The populate() method is used to fetch product info using graphql query.
The flow is ::
All the getter methods of 

com.adobe.cq.commerce.core.components.internal.models.v1.product.
ProductImpl
call the method 
productRetriever.fetchProduct() which returns Product Object
Inside this method 

 

 

public ProductInterface fetchProduct() {
        if (this.product == null) {
            populate();
        }
        return this.product.orElse(null);
    }​

 

 

We have the logic to call populate() method, if the product is not already fetched using graphql query.

Let me know if you are looking for anything else or a solution around this.

Avatar

Level 8

Hi @pulkitvashisth 

1:So, if we call fetchProduct() method multiple times, still graphQL query will be called only once. Correct me if I am wrong.

2: Whats is the type of product object. Since, ProductInterface is an interface only. is product is object of ConfigurableProduct class?

Avatar

Level 8

One more observation from my side @pulkitvashisth 

 

com.adobe.cq.commerce.core.components.internal.models.v3.product.ProductImpl.java file


@PostConstruct
protected void initModel()

init method is callled several times if I load the page: http://localhost:4502/content/venia/us/en/products/product-page.html/venia-tops/venia-sweaters/carin...

I mean more than 10 times. Is this the normal behavior? I mean calling init method multiple times is the normal one?

 

Wanted to check, is there any personal blogs, Adobe articles related to implementation of AEM CIF framework?

 

Thanks in advance.

Avatar

Level 8

Hi @pulkitvashisth 

 

One more query. 

Which method calls the method: populate of AbstractProductRetriever.java for the first time, during the page load:
http://localhost:4502/content/venia/us/en/products/product-page.html/venia-tops/venia-sweaters/carin...

In debug mode, I came to know that Sling filter: com.adobe.cq.commerce.core.components.internal.servlets.CatalogPageNotFoundFilter.java
calls populate method as mentioned below:

 

 if (siteStructure.isProductPage(currentPage)) {
                removeSlingScriptHelperFromBindings = addSlingScriptHelperIfNeeded(slingRequest, slingResponse);
                Product product = commerceModelFinder.findProductComponentModel(slingRequest, currentPage.getContentResource());
                if (product != null && !product.getFound()) {
                    slingResponse.sendError(HttpServletResponse.SC_NOT_FOUND, "Product not found");
                    return;
                }
            }

 

But according to me, at the end of init method of com.adobe.cq.commerce.core.components.internal.models.v3.product.java
populate() method has to be called. Since, in this initModel method calls: super.initModel();
Then, this method sets the productRetriever.extendProductQueryWith.

So, I am bit consfused with: for the first time, during the page load(above mentioned URL), which method calls the populate method?

Avatar

Correct answer by
Community Advisor

Hi @Mahesh_Gunaje 
Firstly 

com.adobe.cq.commerce.core.components.internal.servlets.
SpecificPageFilterFactory
Filter gets triggered then the 
com.adobe.cq.commerce.core.components.internal.servlets.
CatalogPageNotFoundFilter
triggers.
Which internally calls the populate method on the class com.adobe.cq.commerce.core.components.internal.models.v1.product.ProductImpl object.

However the Graphql request goes mutiple times to the GraphqlClient
It serves request from localCahe and does not exectue pre-executed queries all over again.

You can checkout code for the same in the executeCached method
com.adobe.cq.commerce.core.components.internal.client.
MagentoGraphqlClientImpl

 

private GraphqlResponse<Query, Error> executeCached(String query, RequestOptions options) {
        try {
            if (localResponseCache != null && localResponseCache.containsKey(query)) {
                return localResponseCache.get(query);
            }

            GraphqlRequest request = new GraphqlRequest(query);
            GraphqlResponse<Query, Error> response = graphqlClient.execute(request, Query.class, Error.class, options);

            if (localResponseCache != null) {
                localResponseCache.put(query, response);
            }

            return response;
        } catch (RuntimeException ex) {
            LOGGER.error("Failed to execute query: {}", query, ex);
            return newErrorResponse(ex);
        }
    }

Let me know if this was your query.