com.commercetools.sync.customers.helpers.CustomerReferenceResolver Maven / Gradle / Ivy
package com.commercetools.sync.customers.helpers;
import static com.commercetools.sync.commons.utils.CompletableFutureUtils.collectionOfFuturesToFutureOfCollection;
import static io.vrap.rmf.base.client.utils.CompletableFutureUtils.exceptionallyCompletedFuture;
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.common.ResourceIdentifier;
import com.commercetools.api.models.customer.CustomerDraft;
import com.commercetools.api.models.customer.CustomerDraftBuilder;
import com.commercetools.api.models.customer_group.CustomerGroupResourceIdentifierBuilder;
import com.commercetools.api.models.store.StoreResourceIdentifier;
import com.commercetools.api.models.store.StoreResourceIdentifierBuilder;
import com.commercetools.sync.commons.exceptions.ReferenceResolutionException;
import com.commercetools.sync.commons.helpers.CustomReferenceResolver;
import com.commercetools.sync.customers.CustomerSyncOptions;
import com.commercetools.sync.services.CustomerGroupService;
import com.commercetools.sync.services.TypeService;
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 CustomerReferenceResolver
extends CustomReferenceResolver {
public static final String FAILED_TO_RESOLVE_CUSTOMER_GROUP_REFERENCE =
"Failed to resolve customer group resource identifier on CustomerDraft with key:'%s'. Reason: %s";
public static final String FAILED_TO_RESOLVE_STORE_REFERENCE =
"Failed to resolve store resource identifier on CustomerDraft with key:'%s'. Reason: %s";
public static final String FAILED_TO_RESOLVE_CUSTOM_TYPE =
"Failed to resolve custom type reference on " + "CustomerDraft with key:'%s'.";
public static final String CUSTOMER_GROUP_DOES_NOT_EXIST =
"CustomerGroup with key '%s' doesn't exist.";
private final TypeService typeService;
private final CustomerGroupService customerGroupService;
/**
* Takes a {@link CustomerSyncOptions} instance, a {@link TypeService} and a {@link
* CustomerGroupService} to instantiate a {@link CustomerReferenceResolver} instance that could be
* used to resolve the customer drafts in the CTP project specified in the injected {@link
* CustomerSyncOptions} instance.
*
* @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 customerGroupService the service to fetch the customer groups for reference resolution.
*/
public CustomerReferenceResolver(
@Nonnull final CustomerSyncOptions options,
@Nonnull final TypeService typeService,
@Nonnull final CustomerGroupService customerGroupService) {
super(options, typeService);
this.typeService = typeService;
this.customerGroupService = customerGroupService;
}
/**
* Given a {@link CustomerDraft} this method attempts to resolve the stores, customer group and
* custom type references to return a {@link CompletionStage} which contains a new instance of the
* draft with the resolved references or, in case an error occurs during reference resolution, a
* {@link ReferenceResolutionException}.
*
* @param customerDraft the draft to resolve its references.
* @return a {@link CompletionStage} that contains as a result a new CustomerDraft instance with
* resolved custom type reference or, in case an error occurs during reference resolution, a
* {@link ReferenceResolutionException}.
*/
@Override
@Nonnull
public CompletionStage resolveReferences(
@Nonnull final CustomerDraft customerDraft) {
return resolveCustomTypeReference(CustomerDraftBuilder.of(customerDraft))
.thenCompose(this::resolveCustomerGroupReference)
.thenCompose(this::resolveStoreReferences)
.thenApply(CustomerDraftBuilder::build);
}
@Override
protected CompletionStage resolveCustomTypeReference(
@Nonnull final CustomerDraftBuilder draftBuilder) {
return resolveCustomTypeReference(
draftBuilder,
CustomerDraftBuilder::getCustom,
CustomerDraftBuilder::custom,
format(FAILED_TO_RESOLVE_CUSTOM_TYPE, draftBuilder.getKey()));
}
/**
* Given a {@link CustomerDraftBuilder} this method attempts to resolve the customer group to
* return a {@link CompletionStage} which contains a new instance of the builder with the resolved
* customer group reference.
*
* @param draftBuilder the customerDraft to resolve its customer group reference.
* @return a {@link CompletionStage} that contains as a result a new builder instance with
* resolved customer group reference or, in case an error occurs during reference resolution,
* a {@link ReferenceResolutionException}.
*/
@Nonnull
public CompletionStage resolveCustomerGroupReference(
@Nonnull final CustomerDraftBuilder draftBuilder) {
final ResourceIdentifier customerGroupResourceIdentifier = draftBuilder.getCustomerGroup();
if (customerGroupResourceIdentifier != null
&& customerGroupResourceIdentifier.getId() == null) {
String customerGroupKey;
try {
customerGroupKey = getKeyFromResourceIdentifier(customerGroupResourceIdentifier);
} catch (ReferenceResolutionException referenceResolutionException) {
return exceptionallyCompletedFuture(
new ReferenceResolutionException(
format(
FAILED_TO_RESOLVE_CUSTOMER_GROUP_REFERENCE,
draftBuilder.getKey(),
referenceResolutionException.getMessage())));
}
return fetchAndResolveCustomerGroupReference(draftBuilder, customerGroupKey);
}
return completedFuture(draftBuilder);
}
@Nonnull
private CompletionStage fetchAndResolveCustomerGroupReference(
@Nonnull final CustomerDraftBuilder draftBuilder, @Nonnull final String customerGroupKey) {
return customerGroupService
.fetchCachedCustomerGroupId(customerGroupKey)
.thenCompose(
resolvedCustomerGroupIdOptional ->
resolvedCustomerGroupIdOptional
.map(
resolvedCustomerGroupId ->
completedFuture(
draftBuilder.customerGroup(
CustomerGroupResourceIdentifierBuilder.of()
.id(resolvedCustomerGroupId)
.build())))
.orElseGet(
() -> {
final String errorMessage =
format(CUSTOMER_GROUP_DOES_NOT_EXIST, customerGroupKey);
return exceptionallyCompletedFuture(
new ReferenceResolutionException(
format(
FAILED_TO_RESOLVE_CUSTOMER_GROUP_REFERENCE,
draftBuilder.getKey(),
errorMessage)));
}));
}
/**
* Given a {@link CustomerDraftBuilder} this method attempts to resolve the stores and return a
* {@link CompletionStage} which contains a new instance of the builder with the resolved
* references.
*
* @param draftBuilder the customer draft to resolve its store references.
* @return a {@link CompletionStage} that contains as a result a new builder instance with
* resolved references or, in case an error occurs during reference resolution, a {@link
* ReferenceResolutionException}.
*/
@Nonnull
public CompletionStage resolveStoreReferences(
@Nonnull final CustomerDraftBuilder draftBuilder) {
final List storeResourceIdentifiers = draftBuilder.getStores();
if (storeResourceIdentifiers == null || storeResourceIdentifiers.isEmpty()) {
return completedFuture(draftBuilder);
}
final List resolvedReferences = new ArrayList<>();
for (StoreResourceIdentifier storeResourceIdentifier : storeResourceIdentifiers) {
if (storeResourceIdentifier != null) {
if (storeResourceIdentifier.getId() == null) {
try {
final String storeKey = getKeyFromResourceIdentifier(storeResourceIdentifier);
resolvedReferences.add(StoreResourceIdentifierBuilder.of().key(storeKey).build());
} catch (ReferenceResolutionException referenceResolutionException) {
return exceptionallyCompletedFuture(
new ReferenceResolutionException(
format(
FAILED_TO_RESOLVE_STORE_REFERENCE,
draftBuilder.getKey(),
referenceResolutionException.getMessage())));
}
} else {
resolvedReferences.add(
StoreResourceIdentifierBuilder.of().id(storeResourceIdentifier.getId()).build());
}
}
}
return completedFuture(draftBuilder.stores(resolvedReferences));
}
/**
* Calls the {@code cacheKeysToIds} service methods to fetch all the referenced keys (i.e custom
* type, customer group) 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 product references to fetch and cache the id's for.
* @return {@link CompletionStage}<{@link Map}<{@link String}>{@link String}>> in
* which the results of it's completions contains a map of requested references keys -> ids
* of customer references.
*/
@Nonnull
public CompletableFuture>> populateKeyToIdCachesForReferencedKeys(
@Nonnull final CustomerBatchValidator.ReferencedKeys referencedKeys) {
final List>> futures = new ArrayList<>();
final Set typeKeys = referencedKeys.getTypeKeys();
if (!typeKeys.isEmpty()) {
futures.add(typeService.cacheKeysToIds(typeKeys));
}
final Set customerGroupKeys = referencedKeys.getCustomerGroupKeys();
if (!customerGroupKeys.isEmpty()) {
futures.add(customerGroupService.cacheKeysToIds(customerGroupKeys));
}
return collectionOfFuturesToFutureOfCollection(futures, toList());
}
}