Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SITES-22111 - Extend core CIF graphQL resolver with new function/API to expose Errors #1006

Merged
merged 4 commits into from
May 27, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,8 @@ protected void populate() {

GraphqlResponse<Query, Error> response = executeQuery();

if (CollectionUtils.isNotEmpty(response.getErrors())) {
errors = response.getErrors();
if (CollectionUtils.isNotEmpty(errors)) {
categories = Collections.emptyList();
product = Optional.empty();
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
@Version("5.0.0")
@Version("5.1.0")
package com.adobe.cq.commerce.core.components.models.productlist;

import org.osgi.annotation.versioning.Version;
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,8 @@ protected GraphqlResponse<Query, Error> executeQuery() {
@Override
protected void populate() {
GraphqlResponse<Query, Error> response = executeQuery();
if (CollectionUtils.isEmpty(response.getErrors())) {
errors = response.getErrors();
if (CollectionUtils.isEmpty(errors)) {
Query rootQuery = response.getData();
categories = rootQuery.getCategoryList();
categories.sort(Comparator.comparing(c -> identifiers.indexOf(c.getUid().toString())));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,8 @@ protected GraphqlResponse<Query, Error> executeQuery() {
@Override
protected void populate() {
GraphqlResponse<Query, Error> response = executeQuery();
if (CollectionUtils.isEmpty(response.getErrors())) {
errors = response.getErrors();
if (CollectionUtils.isEmpty(errors)) {
Query rootQuery = response.getData();
if (rootQuery.getCategoryList() != null && !rootQuery.getCategoryList().isEmpty()) {
category = Optional.of(rootQuery.getCategoryList().get(0));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,8 @@ protected ProductPriceQueryDefinition generatePriceQuery() {
protected void populate() {
// Get product list from response
GraphqlResponse<Query, Error> response = executeQuery();
if (CollectionUtils.isEmpty(response.getErrors())) {
errors = response.getErrors();
if (CollectionUtils.isEmpty(errors)) {
Query rootQuery = response.getData();
List<ProductInterface> products = rootQuery.getProducts().getItems();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,8 @@ protected GraphqlResponse<Query, Error> executeQuery() {
protected void populate() {
// Get product list from response
GraphqlResponse<Query, Error> response = executeQuery();
if (CollectionUtils.isEmpty(response.getErrors())) {
errors = response.getErrors();
if (CollectionUtils.isEmpty(errors)) {
Query rootQuery = response.getData();
products = rootQuery.getProducts().getItems();
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
package com.adobe.cq.commerce.core.components.models.retriever;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import com.adobe.cq.commerce.core.components.client.MagentoGraphqlClient;
import com.adobe.cq.commerce.graphql.client.GraphqlResponse;
import com.adobe.cq.commerce.magento.graphql.Query;
Expand All @@ -24,6 +28,7 @@
* Abstract implementation of retriever that fetches data using GraphQL.
*/
public abstract class AbstractRetriever {
private static final List<Error> INITIAL_ERRORS = Collections.unmodifiableList(new ArrayList<>());

/**
* Generated or fully customized query.
Expand All @@ -34,6 +39,7 @@ public abstract class AbstractRetriever {
* Instance of the Magento GraphQL client.
*/
protected MagentoGraphqlClient client;
protected List<Error> errors = INITIAL_ERRORS;

public AbstractRetriever(MagentoGraphqlClient client) {
if (client == null) {
Expand All @@ -51,8 +57,36 @@ public void setQuery(String query) {
this.query = query;
}

/**
* Returns the errors encountered during the retrieval of GraphQL results.
*
* @return list of errors
*
* @throws IllegalStateException if this method is invoked earlier than {@link #populate()} on this instance
*/
public final List<Error> getErrors() {
if (errors == INITIAL_ERRORS) {
throw new IllegalStateException("The populate() method must be called and it must populate 'errors' before getErrors().");
}

return errors == null ? Collections.emptyList() : errors;
}

/**
* Checks whether there were any errors encountered during the retrieval of GraphQL results.
*
* @return {@code true} if there where any errors, {@code false} otherwise
*
* @throws IllegalStateException if this method is invoked earlier than {@link #populate()} on this instance
*/
public final boolean hasErrors() {
return !getErrors().isEmpty();
}

/**
* Executes the query and parses the response.
* Implementors should initialize the {@link #errors} field with the errors
* in the GraphQL response returned by {@link #executeQuery()}.
*/
abstract protected void populate();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
@Version("2.1.0")
@Version("2.2.0")
package com.adobe.cq.commerce.core.components.models.retriever;

import org.osgi.annotation.versioning.Version;
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
package com.adobe.cq.commerce.core.search.internal.models;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
Expand All @@ -29,6 +30,7 @@
import com.adobe.cq.commerce.core.search.models.SearchOptions;
import com.adobe.cq.commerce.core.search.models.SearchResultsSet;
import com.adobe.cq.commerce.core.search.models.SorterKey;
import com.adobe.cq.commerce.magento.graphql.gson.Error;

public class SearchResultsSetImpl implements SearchResultsSet {

Expand All @@ -38,6 +40,7 @@ public class SearchResultsSetImpl implements SearchResultsSet {
private List<SearchAggregation> searchAggregations = new ArrayList<>();
private Pager pager;
private SorterImpl sorter = new SorterImpl();
private List<Error> errors;

@Nonnull
@Override
Expand Down Expand Up @@ -144,4 +147,13 @@ public boolean hasSorting() {
List<SorterKey> keys = getSorter().getKeys();
return keys != null && !keys.isEmpty();
}

@Override
public List<Error> getErrors() {
return errors == null ? Collections.emptyList() : errors;
}

public void setErrors(List<Error> errors) {
this.errors = errors;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,14 @@
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
package com.adobe.cq.commerce.core.search.internal.services;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.SyntheticResource;
Expand Down Expand Up @@ -85,41 +87,58 @@ public List<FilterAttributeMetadata> retrieveCurrentlyAvailableCommerceFilters(f
return Optional.ofNullable(page.adaptTo(Resource.class))
.map(r -> new SyntheticResource(r.getResourceResolver(), r.getPath(), SearchFilterService.class.getName()))
.map(r -> r.adaptTo(MagentoGraphqlClient.class))
.map(this::retrieveCurrentlyAvailableCommerceFilters)
.map(magentoGraphqlClient -> retrieveCurrentlyAvailableCommerceFiltersInfo(magentoGraphqlClient).getLeft())
.orElseGet(Collections::emptyList);
}

public List<FilterAttributeMetadata> retrieveCurrentlyAvailableCommerceFilters(final SlingHttpServletRequest request, Page page) {
public Pair<List<FilterAttributeMetadata>, List<Error>> retrieveCurrentlyAvailableCommerceFiltersInfo(
final SlingHttpServletRequest request, Page page) {
// This is used to configure the cache in the GraphqlClient with a cache name of
// --> com.adobe.cq.commerce.core.search.services.SearchFilterService
return Optional.ofNullable(page.adaptTo(Resource.class))
.map(r -> new SyntheticResource(r.getResourceResolver(), r.getPath(), SearchFilterService.class.getName()))
.map(r -> new SlingHttpServletRequestWrapper(request) {
Resource r = page.adaptTo(Resource.class);
if (r != null) {
SyntheticResource syntheticResource = new SyntheticResource(r.getResourceResolver(), r.getPath(), SearchFilterService.class
.getName());
SlingHttpServletRequestWrapper slingHttpServletRequestWrapper = new SlingHttpServletRequestWrapper(request) {
@Override
public Resource getResource() {
return r;
return syntheticResource;
}
})
.map(r -> r.adaptTo(MagentoGraphqlClient.class))
.map(this::retrieveCurrentlyAvailableCommerceFilters)
.orElseGet(Collections::emptyList);
};
MagentoGraphqlClient magentoGraphqlClient = slingHttpServletRequestWrapper.adaptTo(MagentoGraphqlClient.class);
if (magentoGraphqlClient != null) {
Pair<List<FilterAttributeMetadata>, List<Error>> filterAttributeMetadataInfo = retrieveCurrentlyAvailableCommerceFiltersInfo(
magentoGraphqlClient);
return filterAttributeMetadataInfo;
}
}
return Pair.of(Collections.emptyList(), Collections.emptyList());
}

private List<FilterAttributeMetadata> retrieveCurrentlyAvailableCommerceFilters(MagentoGraphqlClient magentoGraphqlClient) {
private Pair<List<FilterAttributeMetadata>, List<Error>> retrieveCurrentlyAvailableCommerceFiltersInfo(
MagentoGraphqlClient magentoGraphqlClient) {
// First we query Magento for the required attribute and filter information
final List<__InputValue> availableFilters = fetchAvailableSearchFilters(magentoGraphqlClient);
final List<Attribute> attributes = fetchAttributeMetadata(magentoGraphqlClient, availableFilters);
final Pair<List<__InputValue>, List<Error>> availableFiltersInfo = fetchAvailableSearchFilters(magentoGraphqlClient);
final Pair<List<Attribute>, List<Error>> attributesInfo = fetchAttributeMetadata(magentoGraphqlClient, availableFiltersInfo
.getLeft());
final List<Error> errors = new ArrayList<>();
if (availableFiltersInfo.getRight() != null) {
errors.addAll(availableFiltersInfo.getRight());
}
if (attributesInfo.getRight() != null) {
errors.addAll(attributesInfo.getRight());
}
// Then we combine this data into a useful set of data usable by other systems
FilterAttributeMetadataConverter converter = new FilterAttributeMetadataConverter(attributes);
return availableFilters.stream().map(converter).collect(Collectors.toList());
FilterAttributeMetadataConverter converter = new FilterAttributeMetadataConverter(attributesInfo.getLeft());
return Pair.of(availableFiltersInfo.getLeft().stream().map(converter).collect(Collectors.toList()), errors);
}

private List<Attribute> fetchAttributeMetadata(final MagentoGraphqlClient magentoGraphqlClient,
private Pair<List<Attribute>, List<Error>> fetchAttributeMetadata(final MagentoGraphqlClient magentoGraphqlClient,
final List<__InputValue> availableFilters) {

if (magentoGraphqlClient == null) {
LOGGER.error("MagentoGraphQL client is null, unable to make query to fetch attribute metadata.");
return Collections.emptyList();
return Pair.of(Collections.emptyList(), Collections.emptyList());
}

List<AttributeInput> attributeInputs = availableFilters.stream().map(inputField -> {
Expand All @@ -144,11 +163,12 @@ private List<Attribute> fetchAttributeMetadata(final MagentoGraphqlClient magent
if (CollectionUtils.isNotEmpty(response.getErrors())) {
response.getErrors()
.forEach(err -> LOGGER.error("An error has occurred: {} ({})", err.getMessage(), err.getCategory()));
return Collections.emptyList();
return Pair.of(Collections.emptyList(), response.getErrors());
}

CustomAttributeMetadata cam = response.getData().getCustomAttributeMetadata();
return cam != null ? response.getData().getCustomAttributeMetadata().getItems() : Collections.emptyList();
return Pair.of(cam != null ? response.getData().getCustomAttributeMetadata().getItems() : Collections.emptyList(),
Collections.emptyList());
}

/**
Expand All @@ -157,11 +177,11 @@ private List<Attribute> fetchAttributeMetadata(final MagentoGraphqlClient magent
* @param magentoGraphqlClient client for making Magento GraphQL requests
* @return key value pair of the attribute code or identifier and filter type for that attribute
*/
private List<__InputValue> fetchAvailableSearchFilters(final MagentoGraphqlClient magentoGraphqlClient) {
private Pair<List<__InputValue>, List<Error>> fetchAvailableSearchFilters(final MagentoGraphqlClient magentoGraphqlClient) {

if (magentoGraphqlClient == null) {
LOGGER.error("MagentoGraphQL client is null, unable to make introspection call to fetch available filter attributes.");
return Collections.emptyList();
return Pair.of(Collections.emptyList(), Collections.emptyList());
}

__TypeQueryDefinition typeQuery = q -> q
Expand All @@ -179,10 +199,10 @@ private List<__InputValue> fetchAvailableSearchFilters(final MagentoGraphqlClien
if (CollectionUtils.isNotEmpty(response.getErrors())) {
response.getErrors()
.forEach(err -> LOGGER.error("An error has occurred: {} ({})", err.getMessage(), err.getCategory()));
return Collections.emptyList();
return Pair.of(Collections.emptyList(), response.getErrors());
}

__Type type = response.getData().__getType();
return type != null ? type.getInputFields() : Collections.emptyList();
return Pair.of(type != null ? type.getInputFields() : Collections.emptyList(), Collections.emptyList());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -205,10 +205,16 @@ public Pair<QueryQuery.CategoryListArgumentsDefinition, CategoryTreeQueryDefinit
category = categoryRetriever.fetchCategory();
}

List<Error> errors = new ArrayList<>();
// We will use the search filter service to retrieve all of the potential available filters the commerce system
// has available for querying against
List<FilterAttributeMetadata> availableFilters = searchFilterService.retrieveCurrentlyAvailableCommerceFilters(request, page);
Pair<List<FilterAttributeMetadata>, List<Error>> filterAttributeInfo = searchFilterService
.retrieveCurrentlyAvailableCommerceFiltersInfo(request, page);
List<FilterAttributeMetadata> availableFilters = filterAttributeInfo.getLeft();
SorterKey currentSorterKey = findSortKey(mutableSearchOptions);
if (filterAttributeInfo.getRight() != null) {
errors.addAll(filterAttributeInfo.getRight());
}

String productsQueryString = generateProductsQueryString(mutableSearchOptions, availableFilters, productQueryHook,
productAttributeFilterHook,
Expand All @@ -221,6 +227,7 @@ public Pair<QueryQuery.CategoryListArgumentsDefinition, CategoryTreeQueryDefinit

// If we have any errors returned we'll log them and return an empty search result
if (CollectionUtils.isNotEmpty(response.getErrors())) {
errors.addAll(response.getErrors());
response.getErrors()
.forEach(err -> LOGGER.error("An error has occurred: {} ({})", err.getMessage(), err.getCategory()));

Expand All @@ -240,6 +247,7 @@ public Pair<QueryQuery.CategoryListArgumentsDefinition, CategoryTreeQueryDefinit
searchResultsSet.setTotalResults(products.getTotalCount());
searchResultsSet.setProductListItems(productListItems);
searchResultsSet.setSearchAggregations(searchAggregations);
searchResultsSet.setErrors(errors);

return new ImmutablePair<>(category, searchResultsSet);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
package com.adobe.cq.commerce.core.search.models;

import java.util.Collections;
import java.util.List;
import java.util.Map;

Expand All @@ -23,6 +24,7 @@
import org.osgi.annotation.versioning.ConsumerType;

import com.adobe.cq.commerce.core.components.models.common.ProductListItem;
import com.adobe.cq.commerce.magento.graphql.gson.Error;

/**
* Represents a set of search results from a backend service. This would generally contain the actual {@link ProductListItem}s
Expand Down Expand Up @@ -113,4 +115,22 @@ public interface SearchResultsSet {
* @return {@code true} if the result set provides support for sorting of the results, {@code false} otherwise
*/
boolean hasSorting();

/**
* Get the list of errors that occurred during the search.
*
* @return the list of errors
*/
default List<Error> getErrors() {
return Collections.emptyList();
}

/**
* Check if there wehre errors during the search.
*
* @return {@code true} if the result set contains errors, {@code false} otherwise
*/
default boolean hasErrors() {
return getErrors() != null && !getErrors().isEmpty();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
@Version("3.2.0")
@Version("3.3.0")
package com.adobe.cq.commerce.core.search.models;

import org.osgi.annotation.versioning.Version;
Loading