Skip to content

Commit

Permalink
CIF-2916 - Remove the queries done by the UrlProvider to get the cate…
Browse files Browse the repository at this point in the history
…gory uid / sku (#943)

* add UrlProvider product and category filter hooks
* use product attribute filter input hook from url provider
* format code, add more unit tests
* implement fuzzy matching for magento gql client local cache
* order products queries so that sku,url_key,... are always the first parameters
* use urlprovider hook in productlist and breadcrumb
* fix nodetype validation error
* disable buildtime coverage check in examples
* update pom.xml
  • Loading branch information
buuhuu authored Aug 2, 2022
1 parent 78f1845 commit dc2eae4
Show file tree
Hide file tree
Showing 33 changed files with 509 additions and 350 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -246,8 +246,27 @@ public GraphqlResponse<Query, Error> execute(String query, HttpMethod httpMethod

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

// fuzzy matching (a very simplified version of caching resolved graphql response objects)
// If a cache key (query) starts with the given query trimmed by any trailing curley brackets can assume
// that the cached response queried with the same filter the same fields. This only works if the queries
// we use define the queried fields always in the same order.
// Example: The query to resolve the sku from the url_key done by the UrlProvider can reuse the response
// from the query done by the product detail component. This helps any Commerce Content Fragment or
// Commerce Experience Fragment on a product detail page that is rendered after the product detail
// component to get the product identifier.
String fuzzyKey = StringUtils.removePattern(query, "\\}+$");
for (Map.Entry<String, GraphqlResponse<Query, Error>> entry : localResponseCache.entrySet()) {
if (entry.getKey().startsWith(fuzzyKey)) {
LOGGER.debug("Fuzzy cache hit for query '{}', return response of query '{}'", query, entry.getKey());
return entry.getValue();
}
}
}

GraphqlRequest request = new GraphqlRequest(query);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -161,20 +161,15 @@ private boolean populateItems(NavigationItem item) {
ProductInterface product = null;

if (siteStructure.isProductPage(page)) {
String productSku = urlProvider.getProductIdentifier(request);
if (StringUtils.isEmpty(productSku)) {
return false;
}
ProductUrlFormat.Params urlParams = urlProvider.parseProductUrlFormatParameters(request);
categoriesBreadcrumbs = fetchProductBreadcrumbs(productSku, urlParams, magentoGraphqlClient);
categoriesBreadcrumbs = fetchProductBreadcrumbs();
product = retriever.fetchProduct();
isProductPage = true;
} else if (siteStructure.isCategoryPage(page)) {
String categoryUid = urlProvider.getCategoryIdentifier(request);
if (StringUtils.isEmpty(categoryUid)) {

if (product == null) {
return false;
}
categoriesBreadcrumbs = fetchCategoryBreadcrumbs(categoryUid, magentoGraphqlClient);
} else if (siteStructure.isCategoryPage(page)) {
categoriesBreadcrumbs = fetchCategoryBreadcrumbs();
isCategoryPage = true;
} else {
// we reached a content page
Expand Down Expand Up @@ -292,12 +287,12 @@ public Comparator<CategoryInterface> getCategoryInterfaceComparator() {
.reversed();
}

private List<? extends CategoryInterface> fetchProductBreadcrumbs(String productSku, ProductUrlFormat.Params urlParams,
MagentoGraphqlClient magentoGraphqlClient) {
private List<? extends CategoryInterface> fetchProductBreadcrumbs() {
retriever = new BreadcrumbRetriever(magentoGraphqlClient);
retriever.setProductIdentifier(productSku);
retriever.setProductIdentifierHook(urlProvider.getProductFilterHook(request));

List<? extends CategoryInterface> categories = retriever.fetchCategoriesBreadcrumbs();
ProductUrlFormat.Params urlParams = urlProvider.parseProductUrlFormatParameters(request);
List<String> alternatives = categories.stream().map(CategoryInterface::getUrlPath).collect(Collectors.toList());
String contextUrlPath = UrlFormatBase.selectUrlPath(null, alternatives, null, urlParams.getCategoryUrlParams().getUrlKey(),
urlParams.getCategoryUrlParams().getUrlPath());
Expand All @@ -311,9 +306,9 @@ private List<? extends CategoryInterface> fetchProductBreadcrumbs(String product
.collect(Collectors.toList());
}

private List<? extends CategoryInterface> fetchCategoryBreadcrumbs(String categoryUid, MagentoGraphqlClient magentoGraphqlClient) {
private List<? extends CategoryInterface> fetchCategoryBreadcrumbs() {
retriever = new BreadcrumbRetriever(magentoGraphqlClient);
retriever.setCategoryIdentifier(categoryUid);
retriever.setCategoryIdentifierHook(urlProvider.getCategoryFilterHook(request));

return retriever.fetchCategoriesBreadcrumbs();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.function.UnaryOperator;

import org.apache.commons.collections4.CollectionUtils;

Expand All @@ -27,7 +28,6 @@
import com.adobe.cq.commerce.magento.graphql.CategoryFilterInput;
import com.adobe.cq.commerce.magento.graphql.CategoryInterface;
import com.adobe.cq.commerce.magento.graphql.CategoryTreeQueryDefinition;
import com.adobe.cq.commerce.magento.graphql.FilterEqualTypeInput;
import com.adobe.cq.commerce.magento.graphql.Operations;
import com.adobe.cq.commerce.magento.graphql.ProductAttributeFilterInput;
import com.adobe.cq.commerce.magento.graphql.ProductInterface;
Expand All @@ -41,10 +41,8 @@ class BreadcrumbRetriever extends AbstractRetriever {

private List<? extends CategoryInterface> categories;
private Optional<ProductInterface> product;

private String productIdentifier;

private String categoryIdentifier;
private UnaryOperator<ProductAttributeFilterInput> productIdentifierHook;
private UnaryOperator<CategoryFilterInput> categoryIdentifierHook;

BreadcrumbRetriever(MagentoGraphqlClient client) {
super(client);
Expand All @@ -65,7 +63,7 @@ protected List<? extends CategoryInterface> fetchCategoriesBreadcrumbs() {

/**
* Executes the GraphQL query and returns the name of the product.
* This assumes that {@link #setProductIdentifier(String)} has been called before.
* This assumes that {@link #setProductIdentifierHook(UnaryOperator)} has been called before.
* For subsequent calls of this method, a cached response is returned.
*
* @return The product name.
Expand All @@ -80,25 +78,25 @@ protected ProductInterface fetchProduct() {
/**
* Set the sku of the product that should be fetched. Setting the a new product, removes any cached data.
*
* @param productIdentifier The product sku.
* @param inputHook The product sku.
*/
protected void setProductIdentifier(String productIdentifier) {
this.productIdentifier = productIdentifier;
protected void setProductIdentifierHook(UnaryOperator<ProductAttributeFilterInput> inputHook) {
this.productIdentifierHook = inputHook;
}

/**
* Set the category uid of the category that should be fetched. Setting the a new category, removes any cached
* data.
*
* @param categoryIdentifier The category uid.
* @param inputHook The category uid.
*/
protected void setCategoryIdentifier(String categoryIdentifier) {
this.categoryIdentifier = categoryIdentifier;
protected void setCategoryIdentifierHook(UnaryOperator<CategoryFilterInput> inputHook) {
this.categoryIdentifierHook = inputHook;
}

@Override
protected void populate() {
if (productIdentifier == null && categoryIdentifier == null) {
if (productIdentifierHook == null && categoryIdentifierHook == null) {
categories = Collections.emptyList();
product = Optional.empty();
return;
Expand All @@ -114,24 +112,29 @@ protected void populate() {

Query rootQuery = response.getData();

if (productIdentifier != null) {
if (productIdentifierHook != null) {
List<ProductInterface> products = rootQuery
.getProducts()
.getItems();

if (products.size() > 0) {
product = Optional.of(products.get(0));
categories = product.get().getCategories();
ProductInterface p = products.get(0);
categories = p.getCategories();
product = Optional.of(p);
} else {
categories = Collections.emptyList();
product = Optional.empty();
}
} else {
categories = rootQuery.getCategoryList();
product = Optional.empty();
}
}

@Override
protected GraphqlResponse<Query, Error> executeQuery() {
if (query == null) {
if (productIdentifier != null) {
if (productIdentifierHook != null) {
query = generateProductQuery();
} else {
query = generateCategoryQuery();
Expand All @@ -146,8 +149,7 @@ protected GraphqlResponse<Query, Error> executeQuery() {
* @return GraphQL query as string
*/
protected String generateProductQuery() {
FilterEqualTypeInput identifierFilter = new FilterEqualTypeInput().setEq(productIdentifier);
ProductAttributeFilterInput filter = new ProductAttributeFilterInput().setSku(identifierFilter);
ProductAttributeFilterInput filter = productIdentifierHook.apply(new ProductAttributeFilterInput());

QueryQuery.ProductsArgumentsDefinition searchArgs = s -> s.filter(filter);

Expand Down Expand Up @@ -175,8 +177,7 @@ protected String generateProductQuery() {
* @return GraphQL query as string
*/
protected String generateCategoryQuery() {
FilterEqualTypeInput identifierFilter = new FilterEqualTypeInput().setEq(categoryIdentifier);
CategoryFilterInput filter = new CategoryFilterInput().setCategoryUid(identifierFilter);
CategoryFilterInput filter = categoryIdentifierHook.apply(new CategoryFilterInput());

CategoryListArgumentsDefinition searchArgs = s -> s.filters(filter);

Expand Down
Loading

0 comments on commit dc2eae4

Please sign in to comment.