All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.commercetools.sync.services.impl.BaseTransformServiceImpl Maven / Gradle / Ivy

package com.commercetools.sync.services.impl;

import static com.commercetools.sync.commons.utils.ResourceIdentifierUtils.REFERENCE_ID_FIELD;
import static com.commercetools.sync.commons.utils.ResourceIdentifierUtils.REFERENCE_TYPE_ID_FIELD;
import static java.lang.String.format;
import static java.util.stream.Collectors.toSet;
import static org.apache.commons.lang3.StringUtils.isBlank;

import com.commercetools.api.client.ProjectApiRoot;
import com.commercetools.api.models.category.CategoryReference;
import com.commercetools.api.models.channel.ChannelReference;
import com.commercetools.api.models.custom_object.CustomObjectReference;
import com.commercetools.api.models.customer.CustomerReference;
import com.commercetools.api.models.customer_group.CustomerGroupReference;
import com.commercetools.api.models.graph_ql.GraphQLRequest;
import com.commercetools.api.models.graph_ql.GraphQLRequestBuilder;
import com.commercetools.api.models.graph_ql.GraphQLResponse;
import com.commercetools.api.models.graph_ql.GraphQLVariablesMapBuilder;
import com.commercetools.api.models.product.ProductReference;
import com.commercetools.api.models.product_type.ProductTypeReference;
import com.commercetools.api.models.shopping_list.ShoppingListReference;
import com.commercetools.api.models.state.StateReference;
import com.commercetools.api.models.tax_category.TaxCategoryReference;
import com.commercetools.api.models.type.TypeReference;
import com.commercetools.sync.commons.models.GraphQlQueryResource;
import com.commercetools.sync.commons.utils.ChunkUtils;
import com.commercetools.sync.commons.utils.ReferenceIdToKeyCache;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.vrap.rmf.base.client.ApiHttpResponse;
import io.vrap.rmf.base.client.utils.json.JsonUtils;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.text.StringEscapeUtils;

public abstract class BaseTransformServiceImpl {
  /*
   * An id is a 36 characters long string. (i.e: 53c4a8b4-754f-4b95-b6f2-3e1e70e3d0c3) We
   * chunk them in 300 ids, we will have a query around 11.000 characters. Above this size it
   * could return - Error 413 (Request Entity Too Large)
   */
  public static final int CHUNK_SIZE = 300;
  public static final String KEY_IS_NOT_SET_PLACE_HOLDER = "KEY_IS_NOT_SET";
  protected final ReferenceIdToKeyCache referenceIdToKeyCache;

  private final ProjectApiRoot ctpClient;

  protected BaseTransformServiceImpl(
      @Nonnull final ProjectApiRoot ctpClient,
      @Nonnull final ReferenceIdToKeyCache referenceIdToKeyCache) {
    this.ctpClient = ctpClient;
    this.referenceIdToKeyCache = referenceIdToKeyCache;
  }

  protected ProjectApiRoot getCtpClient() {
    return ctpClient;
  }

  protected CompletableFuture fetchAndFillReferenceIdToKeyCache(
      @Nonnull final Set ids, @Nonnull final GraphQlQueryResource requestType) {

    final Set nonCachedReferenceIds = getNonCachedReferenceIds(ids);
    if (nonCachedReferenceIds.isEmpty()) {
      return CompletableFuture.completedFuture(null);
    }

    final List> chunkedIds = ChunkUtils.chunk(nonCachedReferenceIds, CHUNK_SIZE);

    final List graphQLRequests = createGraphQLRequests(chunkedIds, requestType);

    return ChunkUtils.executeChunks(getCtpClient(), graphQLRequests)
        .thenAccept(graphQLResult -> cacheResourceReferenceKeys(graphQLResult, requestType));
  }

  protected Map> buildMapOfRequestTypeToReferencedIds(
      final Set references) {
    Map> typedReferenceMap = new HashMap<>();
    typedReferenceMap.put(GraphQlQueryResource.CATEGORIES, new HashSet<>());
    typedReferenceMap.put(GraphQlQueryResource.CHANNELS, new HashSet<>());
    typedReferenceMap.put(GraphQlQueryResource.CUSTOMERS, new HashSet<>());
    typedReferenceMap.put(GraphQlQueryResource.CUSTOM_OBJECTS, new HashSet<>());
    typedReferenceMap.put(GraphQlQueryResource.CUSTOMER_GROUPS, new HashSet<>());
    typedReferenceMap.put(GraphQlQueryResource.PRODUCT_TYPES, new HashSet<>());
    typedReferenceMap.put(GraphQlQueryResource.PRODUCTS, new HashSet<>());
    typedReferenceMap.put(GraphQlQueryResource.SHOPPING_LISTS, new HashSet<>());
    typedReferenceMap.put(GraphQlQueryResource.STATES, new HashSet<>());
    typedReferenceMap.put(GraphQlQueryResource.TAX_CATEGORIES, new HashSet<>());
    typedReferenceMap.put(GraphQlQueryResource.TYPES, new HashSet<>());
    references.forEach(
        ref -> {
          final String refAsText = ref.get(REFERENCE_ID_FIELD).asText();
          switch (ref.get(REFERENCE_TYPE_ID_FIELD).asText()) {
            case (ProductReference.PRODUCT):
              typedReferenceMap.get(GraphQlQueryResource.PRODUCTS).add(refAsText);
              break;
            case (CategoryReference.CATEGORY):
              typedReferenceMap.get(GraphQlQueryResource.CATEGORIES).add(refAsText);
              break;
            case (ChannelReference.CHANNEL):
              typedReferenceMap.get(GraphQlQueryResource.CHANNELS).add(refAsText);
              break;
            case (CustomerReference.CUSTOMER):
              typedReferenceMap.get(GraphQlQueryResource.CUSTOMERS).add(refAsText);
              break;
            case (CustomObjectReference.KEY_VALUE_DOCUMENT):
              typedReferenceMap.get(GraphQlQueryResource.CUSTOM_OBJECTS).add(refAsText);
              break;
            case (CustomerGroupReference.CUSTOMER_GROUP):
              typedReferenceMap.get(GraphQlQueryResource.CUSTOMER_GROUPS).add(refAsText);
              break;
            case (ProductTypeReference.PRODUCT_TYPE):
              typedReferenceMap.get(GraphQlQueryResource.PRODUCT_TYPES).add(refAsText);
              break;
            case (ShoppingListReference.SHOPPING_LIST):
              typedReferenceMap.get(GraphQlQueryResource.SHOPPING_LISTS).add(refAsText);
              break;
            case (StateReference.STATE):
              typedReferenceMap.get(GraphQlQueryResource.STATES).add(refAsText);
              break;
            case (TaxCategoryReference.TAX_CATEGORY):
              typedReferenceMap.get(GraphQlQueryResource.TAX_CATEGORIES).add(refAsText);
              break;
            case (TypeReference.TYPE):
              typedReferenceMap.get(GraphQlQueryResource.TYPES).add(refAsText);
              break;
            default:
              // Nothing to do
              break;
          }
        });
    return typedReferenceMap;
  }

  @Nonnull
  protected Set getNonCachedReferenceIds(@Nonnull final Set referenceIds) {
    return referenceIds.stream().filter(id -> filterNonCachedIds(id)).collect(toSet());
  }

  @Nonnull
  protected Set getNonCachedReferences(@Nonnull final List references) {
    return references.stream()
        .filter(ref -> filterNonCachedIds(ref.get(REFERENCE_ID_FIELD).asText()))
        .collect(toSet());
  }

  private boolean filterNonCachedIds(@Nonnull final String id) {
    return !referenceIdToKeyCache.containsKey(id)
        || KEY_IS_NOT_SET_PLACE_HOLDER.equals(referenceIdToKeyCache.get(id));
  }

  @Nonnull
  protected List createGraphQLRequests(
      @Nonnull final List> chunkedIds,
      @Nonnull final GraphQlQueryResource requestType) {

    final String query =
        "query fetchKeyToIdPairs($where: String, $limit: Int) {\n"
            + "  "
            + requestType.getName()
            + "(limit: $limit, where: $where) {\n"
            + "    results {\n"
            + "      id\n"
            + "      key\n"
            + "    }\n"
            + "  }\n"
            + "}";

    final List graphQLRequests =
        chunkedIds.stream()
            .map(
                keys ->
                    keys.stream()
                        .filter(id -> !isBlank(id))
                        .map(StringEscapeUtils::escapeJava)
                        .map(s -> "\"" + s + "\"")
                        .collect(Collectors.joining(", ")))
            .map(commaSeparatedIds -> format("id in (%s)", commaSeparatedIds))
            .map(
                whereQuery ->
                    GraphQLVariablesMapBuilder.of()
                        .addValue("where", whereQuery)
                        .addValue("limit", CHUNK_SIZE)
                        .build())
            .map(variables -> GraphQLRequestBuilder.of().query(query).variables(variables).build())
            .collect(Collectors.toList());
    return graphQLRequests;
  }

  protected void cacheResourceReferenceKeys(
      @Nonnull final List> graphQLResults) {
    GraphQlQueryResource[] queryResources = GraphQlQueryResource.values();
    for (int i = 0; i < queryResources.length; i++) {
      cacheResourceReferenceKeys(graphQLResults, queryResources[i]);
    }
  }

  protected void cacheResourceReferenceKeys(
      @Nonnull final List> graphQLResults,
      @Nonnull final GraphQlQueryResource requestType) {
    graphQLResults.stream()
        .map(ApiHttpResponse::getBody)
        .filter(Objects::nonNull)
        .map(GraphQLResponse::getData)
        .filter(Objects::nonNull)
        .forEach(
            data -> {
              final ObjectMapper objectMapper = JsonUtils.getConfiguredObjectMapper();
              final JsonNode jsonNode = objectMapper.convertValue(data, JsonNode.class);
              final String requestTypeName = requestType.getName();
              if (jsonNode.get(requestTypeName) != null
                  && jsonNode.get(requestTypeName).get("results") != null) {
                final Iterator elements =
                    jsonNode.get(requestTypeName).get("results").elements();
                while (elements.hasNext()) {
                  JsonNode idAndKey = elements.next();
                  fillReferenceIdToKeyCache(idAndKey.get("id"), idAndKey.get("key"));
                }
              }
            });
  }

  private void fillReferenceIdToKeyCache(@Nullable JsonNode id, @Nullable JsonNode key) {
    if (id != null && !StringUtils.isBlank(id.asText())) {
      final String idValue = id.asText();
      final String keyValue =
          key != null && !StringUtils.isBlank(key.asText())
              ? key.asText()
              : KEY_IS_NOT_SET_PLACE_HOLDER;
      referenceIdToKeyCache.add(idValue, keyValue);
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy