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

com.commercetools.sync.inventories.helpers.InventoryReferenceResolver Maven / Gradle / Ivy

package com.commercetools.sync.inventories.helpers;

import static com.commercetools.sync.commons.utils.CompletableFutureUtils.collectionOfFuturesToFutureOfCollection;
import static java.lang.String.format;
import static java.util.concurrent.CompletableFuture.completedFuture;
import static java.util.stream.Collectors.toList;

import com.commercetools.api.models.channel.Channel;
import com.commercetools.api.models.channel.ChannelResourceIdentifier;
import com.commercetools.api.models.channel.ChannelResourceIdentifierBuilder;
import com.commercetools.api.models.inventory.InventoryEntryDraft;
import com.commercetools.api.models.inventory.InventoryEntryDraftBuilder;
import com.commercetools.sync.commons.exceptions.ReferenceResolutionException;
import com.commercetools.sync.commons.helpers.CustomReferenceResolver;
import com.commercetools.sync.inventories.InventorySyncOptions;
import com.commercetools.sync.services.ChannelService;
import com.commercetools.sync.services.TypeService;
import io.vrap.rmf.base.client.utils.CompletableFutureUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import javax.annotation.Nonnull;

public final class InventoryReferenceResolver
    extends CustomReferenceResolver<
        InventoryEntryDraft, InventoryEntryDraftBuilder, InventorySyncOptions> {

  private static final String CHANNEL_DOES_NOT_EXIST = "Channel with key '%s' does not exist.";
  static final String FAILED_TO_RESOLVE_CUSTOM_TYPE =
      "Failed to resolve custom type resource identifier on "
          + "InventoryEntryDraft with SKU:'%s'.";
  private static final String FAILED_TO_RESOLVE_SUPPLY_CHANNEL =
      "Failed to resolve supply channel "
          + "resource identifier on InventoryEntryDraft with SKU:'%s'. Reason: %s";
  private static final String FAILED_TO_CREATE_SUPPLY_CHANNEL =
      "Failed to create supply channel with key: '%s'";
  private final ChannelService channelService;
  private final TypeService typeService;

  /**
   * Takes a {@link InventorySyncOptions} instance, a {@link TypeService} and a {@link
   * ChannelService} to instantiate a {@link InventoryReferenceResolver} instance that could be used
   * to resolve the type and supply channel references of inventory drafts.
   *
   * @param options the container of all the options of the sync process including the CTP project
   *     client and/or configuration and other sync-specific options.
   * @param typeService the service to fetch the custom types for reference resolution.
   * @param channelService the service to fetch the supply channels for reference resolution.
   */
  public InventoryReferenceResolver(
      @Nonnull final InventorySyncOptions options,
      @Nonnull final TypeService typeService,
      @Nonnull final ChannelService channelService) {
    super(options, typeService);
    this.channelService = channelService;
    this.typeService = typeService;
  }

  /**
   * Given a {@link InventoryEntryDraft} this method attempts to resolve the custom type and supply
   * channel resource identifiers to return a {@link CompletionStage} which contains a new instance
   * of the draft with the resolved resource identifiers.
   *
   * @param draft the inventoryEntryDraft to resolve its resource identifiers.
   * @return a {@link CompletionStage} that contains as a result a new inventoryEntryDraft instance
   *     with resolved resource identifiers or, in case an error occurs during reference resolution,
   *     a {@link ReferenceResolutionException}.
   */
  public CompletionStage resolveReferences(
      @Nonnull final InventoryEntryDraft draft) {
    return resolveCustomTypeReference(InventoryEntryDraftBuilder.of(draft))
        .thenCompose(this::resolveSupplyChannelReference)
        .thenApply(InventoryEntryDraftBuilder::build);
  }

  @Override
  @Nonnull
  protected CompletionStage resolveCustomTypeReference(
      @Nonnull final InventoryEntryDraftBuilder draftBuilder) {

    return resolveCustomTypeReference(
        draftBuilder,
        InventoryEntryDraftBuilder::getCustom,
        InventoryEntryDraftBuilder::custom,
        format(FAILED_TO_RESOLVE_CUSTOM_TYPE, draftBuilder.getSku()));
  }

  /**
   * Given a {@link InventoryEntryDraftBuilder} this method attempts to resolve the supply channel
   * resource identifier to return a {@link CompletionStage} which contains a new instance of the
   * draft builder with the resolved supply channel resource identifier.
   *
   * 

The method then tries to fetch the key of the supply channel, optimistically from a cache. * If the id is not found in cache nor the CTP project and {@code ensureChannel} option is set to * true, a new channel will be created with this key and the role {@code "InventorySupply"}. * However, if the {@code ensureChannel} is set to false, the future is completed exceptionally * with a {@link ReferenceResolutionException}. * * @param draftBuilder the inventory draft builder to read it's values (key, sku, channel) and * then to write resolved resource identifier. * @return a {@link CompletionStage} that contains as a result the same {@code draftBuilder} * inventory draft builder instance with resolved supply channel or, in case an error occurs * during reference resolution, a {@link ReferenceResolutionException}. */ @Nonnull CompletionStage resolveSupplyChannelReference( @Nonnull final InventoryEntryDraftBuilder draftBuilder) { final ChannelResourceIdentifier channelReference = draftBuilder.getSupplyChannel(); if (channelReference != null && channelReference.getId() == null) { try { final String channelKey = getKeyFromResourceIdentifier(channelReference); return fetchOrCreateAndResolveReference(draftBuilder, channelKey); } catch (ReferenceResolutionException exception) { return CompletableFutureUtils.exceptionallyCompletedFuture( new ReferenceResolutionException( format( FAILED_TO_RESOLVE_SUPPLY_CHANNEL, draftBuilder.getSku(), exception.getMessage()), exception)); } } return completedFuture(draftBuilder); } /** * Given an {@link InventoryEntryDraftBuilder} and a {@code channelKey} this method fetches the * actual id of the channel corresponding to this key, ideally from a cache. Then it sets this id * on the supply channel reference id of the inventory entry draft builder. If the id is not found * in cache nor the CTP project and {@code ensureChannel} option is set to true, a new channel * will be created with this key and the role {@code "InventorySupply"}. However, if the {@code * ensureChannel} is set to false, the future is completed exceptionally with a {@link * ReferenceResolutionException}. * * @param draftBuilder the inventory draft builder to read it's values (key, sku, channel) and * then to write resolved resource identifiers. * @param channelKey the key of the channel to resolve it's actual id on the draft. * @return a {@link CompletionStage} that contains as a result the same {@code draftBuilder} * inventory draft builder instance with resolved supply channel resource identifier or an * exception. */ @Nonnull private CompletionStage fetchOrCreateAndResolveReference( @Nonnull final InventoryEntryDraftBuilder draftBuilder, @Nonnull final String channelKey) { final CompletionStage inventoryEntryDraftCompletionStage = channelService .fetchCachedChannelId(channelKey) .thenCompose( resolvedChannelIdOptional -> resolvedChannelIdOptional .map( resolvedChannelId -> setChannelReference(resolvedChannelId, draftBuilder)) .orElseGet(() -> createChannelAndSetReference(channelKey, draftBuilder))); final CompletableFuture result = new CompletableFuture<>(); inventoryEntryDraftCompletionStage.whenComplete( (resolvedDraftBuilder, exception) -> { if (exception != null) { result.completeExceptionally( new ReferenceResolutionException( format( FAILED_TO_RESOLVE_SUPPLY_CHANNEL, draftBuilder.getSku(), exception.getCause().getMessage()), exception)); } else { result.complete(resolvedDraftBuilder); } }); return result; } /** * Helper method that returns a completed CompletionStage with a resolved channel resource * identifier {@link InventoryEntryDraftBuilder} object as a result of setting the passed {@code * channelId} as the id of channel resource identifier. * * @param channelId the channel id to set on the inventory entry supply channel resource * identifier id field. * @param draftBuilder the inventory draft builder where to write resolved resource identifier. * @return a completed CompletionStage with a resolved channel resource identifier with the same * {@link InventoryEntryDraftBuilder} instance as a result of setting the passed {@code * channelId} as the id of channel resource identifier. */ @Nonnull private static CompletionStage setChannelReference( @Nonnull final String channelId, @Nonnull final InventoryEntryDraftBuilder draftBuilder) { return completedFuture( draftBuilder.supplyChannel(ChannelResourceIdentifierBuilder.of().id(channelId).build())); } /** * Helper method that creates a new {@link Channel} on the CTP project with the specified {@code * channelKey} and of the role {@code "InventorySupply"}. Only if the {@code ensureChannels} * options is set to {@code true} on the {@code options} instance of {@code this} class. Then it * resolves the supply channel resource identifier on the supplied {@code inventoryEntryDraft} by * setting the id of its supply channel resource identifier with the newly created Channel. * *

If the {@code ensureChannels} options is set to {@code false} on the {@code options} * instance of {@code this} class, the future is completed exceptionally with a {@link * ReferenceResolutionException}. * *

The method then returns a CompletionStage with a resolved channel resource identifiers * {@link InventoryEntryDraftBuilder} object. * * @param channelKey the key to create the new channel with. * @param draftBuilder the inventory draft builder where to write resolved resource identifiers. * @return a CompletionStage with the same {@code draftBuilder} inventory draft builder instance * where channel channels are resolved. */ @Nonnull private CompletionStage createChannelAndSetReference( @Nonnull final String channelKey, @Nonnull final InventoryEntryDraftBuilder draftBuilder) { if (options.shouldEnsureChannels()) { return channelService .createAndCacheChannel(channelKey) .thenCompose( createdChannelOptional -> { if (createdChannelOptional.isPresent()) { return setChannelReference(createdChannelOptional.get().getId(), draftBuilder); } else { final ReferenceResolutionException referenceResolutionException = new ReferenceResolutionException( format(FAILED_TO_CREATE_SUPPLY_CHANNEL, channelKey)); return CompletableFutureUtils.exceptionallyCompletedFuture( referenceResolutionException); } }); } else { final ReferenceResolutionException referenceResolutionException = new ReferenceResolutionException(format(CHANNEL_DOES_NOT_EXIST, channelKey)); return CompletableFutureUtils.exceptionallyCompletedFuture(referenceResolutionException); } } /** * Calls the {@code cacheKeysToIds} service methods to fetch all the referenced keys (supply * channel and type) from the commercetools to populate caches for the reference resolution. * *

Note: This method is meant be only used internally by the library to improve performance. * * @param referencedKeys a wrapper for the inventory references to fetch and cache the id's for. * @return {@link CompletionStage}<{@link List}<{@link Map}<{@link String}>{@link * String}>>> in which the results of it's completions contains a map of requested * references keys -> ids of it's references. */ @Nonnull public CompletableFuture>> populateKeyToIdCachesForReferencedKeys( @Nonnull final InventoryBatchValidator.ReferencedKeys referencedKeys) { final List>> futures = new ArrayList<>(); final Set channelKeys = referencedKeys.getChannelKeys(); if (!channelKeys.isEmpty()) { futures.add(channelService.cacheKeysToIds(channelKeys)); } final Set typeKeys = referencedKeys.getTypeKeys(); if (!typeKeys.isEmpty()) { futures.add(typeService.cacheKeysToIds(typeKeys)); } return collectionOfFuturesToFutureOfCollection(futures, toList()); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy