From 0dcb3941ae23c61794e56db22800e3601ae38a6b Mon Sep 17 00:00:00 2001 From: aoz Date: Thu, 13 Sep 2018 11:53:29 +0200 Subject: [PATCH 001/164] #300: Adds implementations for the type sync. - Adds the core type sync implementations - Adds some unit tests --- .../sync/services/TypeService.java | 36 +++ .../sync/services/impl/TypeServiceImpl.java | 35 +++ .../commercetools/sync/types/TypeSync.java | 275 ++++++++++++++++++ .../sync/types/TypeSyncOptions.java | 40 +++ .../sync/types/TypeSyncOptionsBuilder.java | 59 ++++ .../types/helpers/TypeSyncStatistics.java | 23 ++ .../FieldDefinitionUpdateActionUtils.java | 110 +++++++ .../sync/types/utils/TypeSyncUtils.java | 54 ++++ .../types/utils/TypeUpdateActionUtils.java | 96 ++++++ .../utils/TypeUpdateEnumActionsUtils.java | 167 +++++++++++ .../TypeUpdateFieldDefinitionActionUtils.java | 274 +++++++++++++++++ .../TypeUpdateLocalizedEnumActionUtils.java | 104 +++++++ .../utils/TypeUpdatePlainEnumActionUtils.java | 97 ++++++ .../sync/services/impl/TypeServiceTest.java | 100 +++++++ .../sync/types/FieldDefinitionTestHelper.java | 53 ++++ .../types/helpers/TypeSyncStatisticsTest.java | 28 ++ .../FieldDefinitionUpdateActionUtilsTest.java | 165 +++++++++++ .../utils/TypeUpdateActionUtilsTest.java | 98 +++++++ 18 files changed, 1814 insertions(+) create mode 100644 src/main/java/com/commercetools/sync/types/TypeSync.java create mode 100644 src/main/java/com/commercetools/sync/types/TypeSyncOptions.java create mode 100644 src/main/java/com/commercetools/sync/types/TypeSyncOptionsBuilder.java create mode 100644 src/main/java/com/commercetools/sync/types/helpers/TypeSyncStatistics.java create mode 100644 src/main/java/com/commercetools/sync/types/utils/FieldDefinitionUpdateActionUtils.java create mode 100644 src/main/java/com/commercetools/sync/types/utils/TypeSyncUtils.java create mode 100644 src/main/java/com/commercetools/sync/types/utils/TypeUpdateActionUtils.java create mode 100644 src/main/java/com/commercetools/sync/types/utils/TypeUpdateEnumActionsUtils.java create mode 100644 src/main/java/com/commercetools/sync/types/utils/TypeUpdateFieldDefinitionActionUtils.java create mode 100644 src/main/java/com/commercetools/sync/types/utils/TypeUpdateLocalizedEnumActionUtils.java create mode 100644 src/main/java/com/commercetools/sync/types/utils/TypeUpdatePlainEnumActionUtils.java create mode 100644 src/test/java/com/commercetools/sync/services/impl/TypeServiceTest.java create mode 100644 src/test/java/com/commercetools/sync/types/FieldDefinitionTestHelper.java create mode 100644 src/test/java/com/commercetools/sync/types/helpers/TypeSyncStatisticsTest.java create mode 100644 src/test/java/com/commercetools/sync/types/utils/FieldDefinitionUpdateActionUtilsTest.java create mode 100644 src/test/java/com/commercetools/sync/types/utils/TypeUpdateActionUtilsTest.java diff --git a/src/main/java/com/commercetools/sync/services/TypeService.java b/src/main/java/com/commercetools/sync/services/TypeService.java index 5e53f2826e..797c928842 100644 --- a/src/main/java/com/commercetools/sync/services/TypeService.java +++ b/src/main/java/com/commercetools/sync/services/TypeService.java @@ -2,9 +2,14 @@ import io.sphere.sdk.channels.Channel; +import io.sphere.sdk.commands.UpdateAction; +import io.sphere.sdk.types.Type; +import io.sphere.sdk.types.TypeDraft; import javax.annotation.Nonnull; +import java.util.List; import java.util.Optional; +import java.util.Set; import java.util.concurrent.CompletionStage; public interface TypeService { @@ -26,4 +31,35 @@ public interface TypeService { */ @Nonnull CompletionStage> fetchCachedTypeId(@Nonnull final String key); + + /** + * Queries existing {@link Type}'s against set of keys. + * + * @param keys {@link List} of sku values, used in search predicate + * @return {@link List} of matching types or empty list when there was not type of key matching to + * {@code keys}. + */ + @Nonnull + CompletionStage> fetchMatchingTypesByKeys(@Nonnull final Set keys); + + + /** + * Creates new type from {@code typeDraft}. + * + * @param typeDraft draft with data for new type + * @return {@link CompletionStage} with created {@link Type} or an exception + */ + @Nonnull + CompletionStage createType(@Nonnull final TypeDraft typeDraft); + + /** + * Updates existing product type with {@code updateActions}. + * + * @param type type that should be updated + * @param updateActions {@link List} of actions that should be applied to {@code type} + * @return {@link CompletionStage} with updated {@link Type} or an exception + */ + @Nonnull + CompletionStage updateType(@Nonnull final Type type, + @Nonnull final List> updateActions); } diff --git a/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java b/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java index 6eb948b043..ca6899e114 100644 --- a/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java +++ b/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java @@ -4,13 +4,21 @@ import com.commercetools.sync.commons.BaseSyncOptions; import com.commercetools.sync.commons.utils.CtpQueryUtils; import com.commercetools.sync.services.TypeService; +import com.commercetools.sync.types.TypeSyncOptions; +import io.sphere.sdk.commands.UpdateAction; +import io.sphere.sdk.queries.QueryExecutionUtils; import io.sphere.sdk.types.Type; +import io.sphere.sdk.types.TypeDraft; +import io.sphere.sdk.types.commands.TypeCreateCommand; +import io.sphere.sdk.types.commands.TypeUpdateCommand; import io.sphere.sdk.types.queries.TypeQuery; +import io.sphere.sdk.types.queries.TypeQueryBuilder; import javax.annotation.Nonnull; import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; import java.util.concurrent.ConcurrentHashMap; @@ -25,6 +33,10 @@ public final class TypeServiceImpl implements TypeService { private final Map keyToIdCache = new ConcurrentHashMap<>(); private boolean isCached = false; + public TypeServiceImpl(@Nonnull final TypeSyncOptions syncOptions) { + this.syncOptions = syncOptions; + } + public TypeServiceImpl(@Nonnull final BaseSyncOptions syncOptions) { this.syncOptions = syncOptions; } @@ -38,6 +50,29 @@ public CompletionStage> fetchCachedTypeId(@Nonnull final String return CompletableFuture.completedFuture(Optional.ofNullable(keyToIdCache.get(key))); } + @Nonnull + @Override + public CompletionStage> fetchMatchingTypesByKeys(@Nonnull final Set keys) { + final TypeQuery query = TypeQueryBuilder + .of() + .plusPredicates(queryModel -> queryModel.key().isIn(keys)) + .build(); + + return QueryExecutionUtils.queryAll(syncOptions.getCtpClient(), query); + } + + @Nonnull + @Override + public CompletionStage createType(@Nonnull TypeDraft typeDraft) { + return syncOptions.getCtpClient().execute(TypeCreateCommand.of(typeDraft)); + } + + @Nonnull + @Override + public CompletionStage updateType(@Nonnull Type type, @Nonnull List> updateActions) { + return syncOptions.getCtpClient().execute(TypeUpdateCommand.of(type, updateActions)); + } + @Nonnull private CompletionStage> fetchAndCache(@Nonnull final String key) { final Consumer> typePageConsumer = typesPage -> diff --git a/src/main/java/com/commercetools/sync/types/TypeSync.java b/src/main/java/com/commercetools/sync/types/TypeSync.java new file mode 100644 index 0000000000..1e4baacb31 --- /dev/null +++ b/src/main/java/com/commercetools/sync/types/TypeSync.java @@ -0,0 +1,275 @@ +package com.commercetools.sync.types; + +import com.commercetools.sync.commons.BaseSync; +import com.commercetools.sync.services.TypeService; +import com.commercetools.sync.services.impl.TypeServiceImpl; +import com.commercetools.sync.types.helpers.TypeSyncStatistics; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +import io.sphere.sdk.commands.UpdateAction; +import io.sphere.sdk.models.WithKey; +import io.sphere.sdk.types.Type; +import io.sphere.sdk.types.TypeDraft; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionStage; +import java.util.stream.Collectors; + +import static com.commercetools.sync.commons.utils.SyncUtils.batchElements; +import static com.commercetools.sync.types.utils.TypeSyncUtils.buildActions; +import static java.lang.String.format; +import static java.util.Collections.emptyList; +import static java.util.Optional.ofNullable; +import static java.util.concurrent.CompletableFuture.completedFuture; +import static java.util.stream.Collectors.toList; +import static java.util.stream.Collectors.toSet; +import static org.apache.commons.lang3.StringUtils.isBlank; + +/** + * This class syncs type drafts with the corresponding types in the CTP project. + */ +public class TypeSync extends BaseSync { + + private static final String CTP_TYPE_FETCH_FAILED = "Failed to fetch existing types of keys '%s'."; + private static final String CTP_TYPE_UPDATE_FAILED = "Failed to update type of key '%s'."; + private static final String CTP_TYPE_CREATE_FAILED = "Failed to create type of key '%s'."; + private static final String TYPE_DRAFT_HAS_NO_KEY = "Failed to process type draft without key."; + private static final String TYPE_DRAFT_IS_NULL = "Failed to process null type draft."; + + private final TypeService typeService; + + public TypeSync(@Nonnull final TypeSyncOptions typeSyncOptions) { + super(new TypeSyncStatistics(), typeSyncOptions); + this.typeService = new TypeServiceImpl(typeSyncOptions); + } + + public TypeSync(@Nonnull final TypeSyncOptions typeSyncOptions, + @Nonnull final TypeService typeService) { + super(new TypeSyncStatistics(), typeSyncOptions); + this.typeService = typeService; + } + + /** + * Iterates through the whole {@code typeDrafts} list and accumulates its valid drafts to batches. + * Every batch is then processed by {@link TypeSync#processBatch(List)}. + * + *

Inherited doc: + * {@inheritDoc} + * + * @param typeDrafts {@link List} of {@link TypeDraft}'s that would be synced into CTP project. + * @return {@link CompletionStage} with {@link TypeSyncStatistics} holding statistics of all sync + * processes performed by this sync instance. + */ + @Override + protected CompletionStage process( + @Nonnull final List typeDrafts) { + + final List> batches = batchElements(typeDrafts, syncOptions.getBatchSize()); + + return syncBatches(batches, CompletableFuture.completedFuture(statistics)); + } + + @Override + protected CompletionStage syncBatches( + @Nonnull final List> batches, + @Nonnull final CompletionStage result) { + + if (batches.isEmpty()) { + return result; + } + + final List firstBatch = batches.remove(0); + return syncBatches(batches, result.thenCompose(subResult -> processBatch(firstBatch))); + } + + /** + * Fetches existing {@link Type} objects from CTP project that correspond to passed {@code batch}. + * Having existing product types fetched, {@code batch} is compared and synced with fetched objects by + * {@link TypeSync#syncBatch(List, List)} function. When fetching existing types results in + * an empty optional then {@code batch} isn't processed. + * + * @param batch batch of drafts that need to be synced + * @return {@link CompletionStage} of {@link Void} that indicates method progress. + */ + @Override + protected CompletionStage processBatch(@Nonnull final List batch) { + final List validTypeDrafts = batch.stream() + .filter(this::validateDraft) + .collect(toList()); + + if (validTypeDrafts.isEmpty()) { + statistics.incrementProcessed(batch.size()); + return completedFuture(statistics); + } else { + final Set keys = validTypeDrafts.stream().map(TypeDraft::getKey).collect(toSet()); + + return fetchExistingTypes(keys) + .thenCompose(oldTypes -> syncBatch(oldTypes, validTypeDrafts)) + .thenApply(ignored -> { + statistics.incrementProcessed(batch.size()); + return statistics; + }); + } + } + + /** + * Checks if a draft is valid for further processing. If so, then returns {@code true}. Otherwise handles an error + * and returns {@code false}. A valid draft is a {@link TypeDraft} object that is not {@code null} and its + * key is not empty. + * + * @param draft nullable draft + * @return boolean that indicate if given {@code draft} is valid for sync + */ + private boolean validateDraft(@Nullable final TypeDraft draft) { + if (draft == null) { + handleError(TYPE_DRAFT_IS_NULL, null, 1); + } else if (isBlank(draft.getKey())) { + handleError(TYPE_DRAFT_HAS_NO_KEY, null, 1); + } else { + return true; + } + + return false; + } + + /** + * Given a set of type keys, fetches the corresponding types from CTP if they exist. + * + * @param keys the keys of the types that are wanted to be fetched. + * @return a future which contains the list of types corresponding to the keys. + */ + private CompletionStage> fetchExistingTypes(@Nonnull final Set keys) { + return typeService + .fetchMatchingTypesByKeys(keys) + .exceptionally(exception -> { + final String errorMessage = format(CTP_TYPE_FETCH_FAILED, keys); + handleError(errorMessage, exception, keys.size()); + + return emptyList(); + }); + } + + /** + * Given a list of {@link Type} or {@link TypeDraft}, returns a map of keys to the + * {@link Type}/{@link TypeDraft} instances. + * + * @param types list of {@link Type}/{@link TypeDraft} + * @param a type that extends of {@link WithKey}. + * @return the map of keys to {@link Type}/{@link TypeDraft} instances. + */ + private Map getKeysProductTypeMap(@Nonnull final List types) { + return types.stream().collect(Collectors.toMap(WithKey::getKey, p -> p, + (productTypeA, productTypeB) -> productTypeB)); + } + + /** + * Given a {@link String} {@code errorMessage} and a {@link Throwable} {@code exception}, this method calls the + * optional error callback specified in the {@code syncOptions} and updates the {@code statistics} instance by + * incrementing the total number of failed product types to sync. + * + * @param errorMessage The error message describing the reason(s) of failure. + * @param exception The exception that called caused the failure, if any. + * @param failedTimes The number of times that the failed product types counter is incremented. + */ + private void handleError(@Nonnull final String errorMessage, @Nullable final Throwable exception, + final int failedTimes) { + + syncOptions.applyErrorCallback(errorMessage, exception); + statistics.incrementFailed(failedTimes); + } + + /** + * Given a list of type drafts, attempts to sync the drafts with the existing types in the CTP + * project. The type and the draft are considered to match if they have the same key. + * + * @param oldTypes old types. + * @param newTypes drafts that need to be synced. + * @return a future which contains an empty result after execution of the update + */ + private CompletionStage syncBatch( + @Nonnull final List oldTypes, + @Nonnull final List newTypes) { + final Map oldTypeMap = getKeysProductTypeMap(oldTypes); + + return CompletableFuture.allOf(newTypes + .stream() + .map(newType -> { + final Type oldType = oldTypeMap.get(newType.getKey()); + + return ofNullable(oldType) + .map(type -> updateProductType(oldType, newType)) + .orElseGet(() -> createType(newType)); + }) + .map(CompletionStage::toCompletableFuture) + .toArray(CompletableFuture[]::new)).thenApply(result -> statistics); + } + + /** + * Given a type draft, issues a request to the CTP project to create a corresponding Type. + * + *

The {@code statistics} instance is updated accordingly to whether the CTP request was carried + * out successfully or not. If an exception was thrown on executing the request to CTP, the error handling method + * is called. + * + * @param typeDraft the type draft to create the product type from. + * @return a future which contains an empty result after execution of the create. + */ + private CompletionStage createType(@Nonnull final TypeDraft typeDraft) { + return syncOptions.applyBeforeCreateCallBack(typeDraft) + .map(typeService::createType) + .map(creationFuture -> creationFuture + .thenAccept(createdType -> statistics.incrementCreated()) + .exceptionally(exception -> { + final String errorMessage = format(CTP_TYPE_CREATE_FAILED, + typeDraft.getKey()); + handleError(errorMessage, exception, 1); + + return null; + })) + .orElseGet(() -> CompletableFuture.completedFuture(null)); + } + + /** + * Given an existing {@link Type} and a new {@link TypeDraft}, the method calculates all the + * update actions required to synchronize the existing product type to be the same as the new one. If there are + * update actions found, a request is made to CTP to update the existing product type, otherwise it doesn't issue a + * request. + * + *

The {@code statistics} instance is updated accordingly to whether the CTP request was carried + * out successfully or not. If an exception was thrown on executing the request to CTP, the error handling method + * is called. + * + * @param oldType existing type that could be updated. + * @param newType draft containing data that could differ from data in {@code oldType}. + * @return a future which contains an empty result after execution of the update. + */ + @SuppressFBWarnings("NP_NONNULL_PARAM_VIOLATION") // https://github.com/findbugsproject/findbugs/issues/79 + private CompletionStage updateProductType(@Nonnull final Type oldType, + @Nonnull final TypeDraft newType) { + + final List> updateActions = buildActions(oldType, newType, syncOptions); + + final List> updateActionsAfterCallback = syncOptions.applyBeforeUpdateCallBack( + updateActions, + newType, + oldType + ); + + if (!updateActionsAfterCallback.isEmpty()) { + return typeService.updateType(oldType, updateActionsAfterCallback) + .thenAccept(updatedProductType -> statistics.incrementUpdated()) + .exceptionally(exception -> { + final String errorMessage = format(CTP_TYPE_UPDATE_FAILED, newType.getKey()); + handleError(errorMessage, exception, 1); + + return null; + }); + } + + return completedFuture(null); + } +} diff --git a/src/main/java/com/commercetools/sync/types/TypeSyncOptions.java b/src/main/java/com/commercetools/sync/types/TypeSyncOptions.java new file mode 100644 index 0000000000..64d931ca26 --- /dev/null +++ b/src/main/java/com/commercetools/sync/types/TypeSyncOptions.java @@ -0,0 +1,40 @@ +package com.commercetools.sync.types; + +import com.commercetools.sync.commons.BaseSyncOptions; +import com.commercetools.sync.commons.utils.TriFunction; +import io.sphere.sdk.client.SphereClient; +import io.sphere.sdk.commands.UpdateAction; +import io.sphere.sdk.types.Type; +import io.sphere.sdk.types.TypeDraft; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.List; +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.function.Function; + +public final class TypeSyncOptions extends BaseSyncOptions { + + TypeSyncOptions( + @Nonnull final SphereClient ctpClient, + @Nullable final BiConsumer updateActionErrorCallBack, + @Nullable final Consumer updateActionWarningCallBack, + final int batchSize, + final boolean allowUuid, + @Nullable final TriFunction>, TypeDraft, Type, List>> beforeUpdateCallback, + @Nullable final Function beforeCreateCallback + ) { + + super( + ctpClient, + updateActionErrorCallBack, + updateActionWarningCallBack, + batchSize, + allowUuid, + beforeUpdateCallback, + beforeCreateCallback + ); + } + +} diff --git a/src/main/java/com/commercetools/sync/types/TypeSyncOptionsBuilder.java b/src/main/java/com/commercetools/sync/types/TypeSyncOptionsBuilder.java new file mode 100644 index 0000000000..8c1dfaff93 --- /dev/null +++ b/src/main/java/com/commercetools/sync/types/TypeSyncOptionsBuilder.java @@ -0,0 +1,59 @@ +package com.commercetools.sync.types; + +import com.commercetools.sync.commons.BaseSyncOptionsBuilder; +import io.sphere.sdk.client.SphereClient; +import io.sphere.sdk.types.Type; +import io.sphere.sdk.types.TypeDraft; + +import javax.annotation.Nonnull; + +public final class TypeSyncOptionsBuilder extends BaseSyncOptionsBuilder { + + public static final int BATCH_SIZE_DEFAULT = 50; + + private TypeSyncOptionsBuilder(@Nonnull final SphereClient ctpClient) { + this.ctpClient = ctpClient; + } + + /** + * Creates a new instance of {@link TypeSyncOptionsBuilder} given a {@link SphereClient} responsible for + * interaction with the target CTP project, with the default batch size ({@code BATCH_SIZE_DEFAULT} = 50). + * + * @param ctpClient instance of the {@link SphereClient} responsible for interaction with the target CTP project. + * @return new instance of {@link TypeSyncOptionsBuilder} + */ + public static TypeSyncOptionsBuilder of(@Nonnull final SphereClient ctpClient) { + return new TypeSyncOptionsBuilder(ctpClient).batchSize(BATCH_SIZE_DEFAULT); + } + + /** + * Creates new instance of {@link TypeSyncOptions} enriched with all attributes provided to {@code this} + * builder. + * + * @return new instance of {@link TypeSyncOptions} + */ + @Override + public TypeSyncOptions build() { + return new TypeSyncOptions( + ctpClient, + errorCallback, + warningCallback, + batchSize, + allowUuid, + beforeUpdateCallback, + beforeCreateCallback + ); + } + + /** + * Returns an instance of this class to be used in the super class generic methods. Please see the JavaDoc in the + * overridden method for further details. + * + * @return an instance of this class. + */ + @Override + protected TypeSyncOptionsBuilder getThis() { + return this; + } +} diff --git a/src/main/java/com/commercetools/sync/types/helpers/TypeSyncStatistics.java b/src/main/java/com/commercetools/sync/types/helpers/TypeSyncStatistics.java new file mode 100644 index 0000000000..b14d5fd1e7 --- /dev/null +++ b/src/main/java/com/commercetools/sync/types/helpers/TypeSyncStatistics.java @@ -0,0 +1,23 @@ +package com.commercetools.sync.types.helpers; + +import com.commercetools.sync.commons.helpers.BaseSyncStatistics; + +import static java.lang.String.format; + +public class TypeSyncStatistics extends BaseSyncStatistics { + /** + * Builds a summary of the type sync statistics instance that looks like the following example: + * + *

"Summary: 2 types were processed in total (0 created, 0 updated and 0 failed to sync)." + * + * @return a summary message of the types sync statistics instance. + */ + @Override + public String getReportMessage() { + reportMessage = format( + "Summary: %s types were processed in total (%s created, %s updated and %s failed to sync).", + getProcessed(), getCreated(), getUpdated(), getFailed()); + + return reportMessage; + } +} diff --git a/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionUpdateActionUtils.java b/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionUpdateActionUtils.java new file mode 100644 index 0000000000..8d83bdd59c --- /dev/null +++ b/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionUpdateActionUtils.java @@ -0,0 +1,110 @@ +package com.commercetools.sync.types.utils; + +import io.sphere.sdk.commands.UpdateAction; +import io.sphere.sdk.models.LocalizedString; +import io.sphere.sdk.types.EnumFieldType; +import io.sphere.sdk.types.FieldDefinition; +import io.sphere.sdk.types.LocalizedEnumFieldType; +import io.sphere.sdk.types.Type; +import io.sphere.sdk.types.commands.updateactions.ChangeFieldDefinitionLabel; + +import javax.annotation.Nonnull; +import java.util.List; +import java.util.Optional; +import java.util.stream.Stream; + +import static com.commercetools.sync.commons.utils.CommonTypeUpdateActionUtils.buildUpdateAction; +import static com.commercetools.sync.types.utils.TypeUpdateLocalizedEnumActionUtils.buildLocalizedEnumValuesUpdateActions; +import static com.commercetools.sync.types.utils.TypeUpdatePlainEnumActionUtils.buildEnumValuesUpdateActions; +import static java.util.stream.Collectors.toList; + + +public final class FieldDefinitionUpdateActionUtils { + + /** + * Compares all the fields of an {@link FieldDefinition} and an {@link FieldDefinition} and returns + * a list of {@link UpdateAction}<{@link Type}> as a result. If both the {@link FieldDefinition} + * and the {@link FieldDefinition} have identical fields, then no update action is needed and hence an + * empty {@link List} is returned. + * + * @param oldFieldDefinition the field definition which should be updated. + * @param newFieldDefinition the field definition draft where we get the new fields. + * @return A list with the update actions or an empty list if the field definition fields are identical. + */ + @Nonnull + public static List> buildActions( + @Nonnull final FieldDefinition oldFieldDefinition, + @Nonnull final FieldDefinition newFieldDefinition) { + + final List> updateActions; + + updateActions = Stream + .of( + buildChangeLabelUpdateAction(oldFieldDefinition, newFieldDefinition) + ) + .filter(Optional::isPresent) + .map(Optional::get) + .collect(toList()); + + + if (isPlainEnumField(oldFieldDefinition)){ + updateActions.addAll(buildEnumValuesUpdateActions( + oldFieldDefinition.getName(), + ((EnumFieldType) oldFieldDefinition.getType()).getValues(), + ((EnumFieldType) newFieldDefinition.getType()).getValues() + )); + } else if (isLocalizedEnumAttribute(oldFieldDefinition)){ + updateActions.addAll(buildLocalizedEnumValuesUpdateActions( + oldFieldDefinition.getName(), + ((LocalizedEnumFieldType) oldFieldDefinition.getType()).getValues(), + ((LocalizedEnumFieldType) newFieldDefinition.getType()).getValues() + )); + } + + + return updateActions; + } + + /** + * Indicates if the field is a plain enum value or not. + * + * @param fieldDefinition the field definition. + * @return true if the field definition is a plain enum value, false otherwise. + */ + private static boolean isPlainEnumField(@Nonnull final FieldDefinition fieldDefinition) { + return fieldDefinition.getType().getClass() == EnumFieldType.class; + } + + /** + * Indicates if the field definition is a localized enum value or not. + * + * @param fieldDefinition the field definition. + * @return true if the field definition is a localized enum value, false otherwise. + */ + private static boolean isLocalizedEnumAttribute(@Nonnull final FieldDefinition fieldDefinition) { + return fieldDefinition.getType().getClass() == LocalizedEnumFieldType.class; + } + + /** + * Compares the {@link LocalizedString} labels of an {@link FieldDefinition} and an + * {@link FieldDefinition} and returns an {@link UpdateAction}<{@link Type}> as a result in + * an {@link Optional}. If both the {@link FieldDefinition} and the {@link FieldDefinition} have the + * same label, then no update action is needed and hence an empty {@link Optional} is returned. + * + * @param oldFieldDefinition the field definition which should be updated. + * @param newFieldDefinition the field definition draft where we get the new label. + * @return A filled optional with the update action or an empty optional if the labels are identical. + */ + @Nonnull + public static Optional> buildChangeLabelUpdateAction( + @Nonnull final FieldDefinition oldFieldDefinition, + @Nonnull final FieldDefinition newFieldDefinition) { + + return buildUpdateAction(oldFieldDefinition.getLabel(), newFieldDefinition.getLabel(), + () -> ChangeFieldDefinitionLabel.of(oldFieldDefinition.getName(), newFieldDefinition.getLabel()) + ); + } + + private FieldDefinitionUpdateActionUtils() { + } +} diff --git a/src/main/java/com/commercetools/sync/types/utils/TypeSyncUtils.java b/src/main/java/com/commercetools/sync/types/utils/TypeSyncUtils.java new file mode 100644 index 0000000000..29d5cce4bc --- /dev/null +++ b/src/main/java/com/commercetools/sync/types/utils/TypeSyncUtils.java @@ -0,0 +1,54 @@ +package com.commercetools.sync.types.utils; + +import com.commercetools.sync.types.TypeSyncOptions; +import io.sphere.sdk.commands.UpdateAction; +import io.sphere.sdk.types.Type; +import io.sphere.sdk.types.TypeDraft; + +import javax.annotation.Nonnull; +import java.util.List; +import java.util.Optional; +import java.util.stream.Stream; + +import static com.commercetools.sync.types.utils.TypeUpdateActionUtils.*; +import static java.util.stream.Collectors.toList; + +public final class TypeSyncUtils { + + /** + * Compares all the fields (including the attributes see + * {@link TypeUpdateActionUtils#buildFieldUpdateActions(Type, TypeDraft, TypeSyncOptions)}) of a {@link Type} and a + * {@link TypeDraft}. It returns a {@link List} of {@link UpdateAction}<{@link Type}> as a + * result. If no update action is needed, for example in case where both the {@link Type} and the + * {@link TypeDraft} have the same fields, an empty {@link List} is returned. + * + * @param oldType the {@link Type} which should be updated. + * @param newType the {@link TypeDraft} where we get the new data. + * @param syncOptions the sync options wrapper which contains options related to the sync process supplied by + * the user. For example, custom callbacks to call in case of warnings or errors occurring + * on the build update action process. And other options (See {@link TypeSyncOptions} + * for more info. + * @return A list of productType-specific update actions. + */ + @Nonnull + public static List> buildActions( + @Nonnull final Type oldType, + @Nonnull final TypeDraft newType, + @Nonnull final TypeSyncOptions syncOptions) { + + final List> updateActions = Stream.of( + buildChangeNameAction(oldType, newType), + buildSetDescriptionUpdateAction(oldType, newType) + ) + .filter(Optional::isPresent) + .map(Optional::get) + .collect(toList()); + + updateActions.addAll(buildFieldUpdateActions(oldType, newType, syncOptions)); + + return updateActions; + } + + private TypeSyncUtils() { + } +} diff --git a/src/main/java/com/commercetools/sync/types/utils/TypeUpdateActionUtils.java b/src/main/java/com/commercetools/sync/types/utils/TypeUpdateActionUtils.java new file mode 100644 index 0000000000..6c3b967301 --- /dev/null +++ b/src/main/java/com/commercetools/sync/types/utils/TypeUpdateActionUtils.java @@ -0,0 +1,96 @@ +package com.commercetools.sync.types.utils; + + +import com.commercetools.sync.commons.exceptions.BuildUpdateActionException; +import com.commercetools.sync.types.TypeSyncOptions; +import io.sphere.sdk.commands.UpdateAction; +import io.sphere.sdk.models.LocalizedString; +import io.sphere.sdk.types.Type; +import io.sphere.sdk.types.TypeDraft; +import io.sphere.sdk.types.commands.updateactions.ChangeName; +import io.sphere.sdk.types.commands.updateactions.SetDescription; + +import javax.annotation.Nonnull; +import java.util.List; +import java.util.Optional; + +import static com.commercetools.sync.commons.utils.CommonTypeUpdateActionUtils.buildUpdateAction; +import static com.commercetools.sync.types.utils.TypeUpdateFieldDefinitionActionUtils.buildFieldDefinitionsUpdateActions; +import static java.lang.String.format; +import static java.util.Collections.emptyList; + +public final class TypeUpdateActionUtils { + + /** + * Compares the {@code name} values of a {@link Type} and a {@link Type} + * and returns an {@link Optional} of update action, which would contain the {@code "changeName"} + * {@link UpdateAction}. If both {@link Type} and {@link Type} have the same + * {@code name} values, then no update action is needed and empty optional will be returned. + * + * @param oldType the type that should be updated. + * @param newType the type draft which contains the new name. + * @return optional containing update action or empty optional if names are identical. + */ + @Nonnull + public static Optional> buildChangeNameAction( + @Nonnull final Type oldType, + @Nonnull final TypeDraft newType) { + + return buildUpdateAction(oldType.getName(), newType.getName(), + () -> ChangeName.of(newType.getName())); + } + + + /** + * Compares the {@link LocalizedString} descriptions of a {@link Type} and a {@link TypeDraft} and + * returns an {@link UpdateAction}<{@link Type}> as a result in an {@link Optional}. If both the + * {@link Type} and the {@link TypeDraft} have the same description, then no update action is needed and + * hence an empty {@link Optional} is returned. + * + * @param oldType the type which should be updated. + * @param newType the type draft where we get the new description. + * @return A filled optional with the update action or an empty optional if the descriptions are identical. + */ + @Nonnull + public static Optional> buildSetDescriptionUpdateAction( + @Nonnull final Type oldType, + @Nonnull final TypeDraft newType) { + return buildUpdateAction(oldType.getDescription(), newType.getDescription(), + () -> SetDescription.of(newType.getDescription())); + } + + /** + * Compares the attributes of a {@link Type} and a {@link TypeDraft} and returns a list of + * {@link UpdateAction}<{@link Type}> as a result. If both the {@link Type} and + * the {@link TypeDraft} have identical field definitions, then no update action is needed and hence an empty + * {@link List} is returned. In case, the new product type draft has a list of field definitions in which a + * duplicate name exists, the error callback is triggered and an empty list is returned. + * + * @param oldType the type which should be updated. + * @param newType the type draft where we get the key. + * @param syncOptions responsible for supplying the sync options to the sync utility method. + * It is used for triggering the error callback within the utility, in case of + * errors. + * @return A list with the update actions or an empty list if the field definitions are identical. + */ + @Nonnull + public static List> buildFieldUpdateActions( + @Nonnull final Type oldType, + @Nonnull final TypeDraft newType, + @Nonnull final TypeSyncOptions syncOptions) { + try { + return buildFieldDefinitionsUpdateActions( + oldType.getFieldDefinitions(), + newType.getFieldDefinitions() + ); + } catch (final BuildUpdateActionException exception) { + syncOptions.applyErrorCallback(format("Failed to build update actions for the field definitions " + + "of the type with the key '%s'. Reason: %s", newType.getKey(), exception), + exception); + return emptyList(); + } + } + + private TypeUpdateActionUtils() { + } +} diff --git a/src/main/java/com/commercetools/sync/types/utils/TypeUpdateEnumActionsUtils.java b/src/main/java/com/commercetools/sync/types/utils/TypeUpdateEnumActionsUtils.java new file mode 100644 index 0000000000..1ee1f44e44 --- /dev/null +++ b/src/main/java/com/commercetools/sync/types/utils/TypeUpdateEnumActionsUtils.java @@ -0,0 +1,167 @@ +package com.commercetools.sync.types.utils; + +import com.commercetools.sync.commons.exceptions.DuplicateKeyException; +import io.sphere.sdk.commands.UpdateAction; +import io.sphere.sdk.models.EnumValue; +import io.sphere.sdk.models.WithKey; +import io.sphere.sdk.producttypes.ProductType; +import io.sphere.sdk.producttypes.commands.updateactions.RemoveEnumValues; +import io.sphere.sdk.types.Type; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.function.BiFunction; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static com.commercetools.sync.commons.utils.CommonTypeUpdateActionUtils.buildUpdateAction; +import static java.lang.String.format; +import static java.util.Collections.emptyList; +import static java.util.Optional.*; +import static java.util.stream.Collectors.toMap; + +public final class TypeUpdateEnumActionsUtils { + + /** + * Given a list of new {@link EnumValue}s, gets a map where the keys are the enum value key, and the values + * are the enum instances. + * + * @param fieldDefinitionName the field definition name whose the enum values belong to. + * @param enumValues the list of enum values. + * @param the enum type of the elements of the list. + * @return a map with the enum value key as a key of the map, and the enum + * value as a value of the map. + */ + @Nonnull + public static Map getEnumValuesKeyMapWithKeyValidation( + @Nonnull final String fieldDefinitionName, + @Nonnull final List enumValues) { + + return enumValues.stream().collect( + toMap(WithKey::getKey, enumValue -> enumValue, + (enumValueA, enumValueB) -> { + throw new DuplicateKeyException(format("Enum Values have duplicated keys. " + + "Field definition name: '%s', Duplicated enum value: '%s'. " + + "Enum Values are expected to be unique inside their attribute definition.", + fieldDefinitionName, enumValueA.getKey())); + } + )); + } + + /** + * Checks if there are any old enum values which are not existing in the {@code newEnumValues}. + * If there are, then "remove" enum values update actions are built. + * Otherwise, if there are no old enum values, then an empty list is returned. + * + * @param attributeDefinitionName the attribute definition name whose enum values belong to. + * @param oldEnumValues the list of old enum values. + * @param newEnumValues the list of new enum values. + * @param the enum type of the elements of the list. + * @return a list of enum values update actions if there are old enum value + * that should be removed. + * Otherwise, if the enum values are identical, an empty optional is returned. + */ + @Nonnull + public static Optional> buildRemoveEnumValuesUpdateActions( + @Nonnull final String attributeDefinitionName, + @Nonnull final List oldEnumValues, + @Nullable final List newEnumValues) { + + final Map newEnumValuesKeyMap = getEnumValuesKeyMapWithKeyValidation( + attributeDefinitionName, + ofNullable(newEnumValues).orElse(emptyList()) + ); + + final List keysToRemove = oldEnumValues + .stream() + .map(WithKey::getKey) + .filter(oldEnumValueKey -> !newEnumValuesKeyMap.containsKey(oldEnumValueKey)) + .collect(Collectors.toList()); + + return keysToRemove.isEmpty() ? empty() : of(RemoveEnumValues.of(attributeDefinitionName, keysToRemove)); + } + + /** + * Compares the order of a list of old enum values and a list of new enum values. If there is a change in order, + * then a change of enum values order (with the new order) is built. If there are no changes in order an empty + * optional is returned. + * + * @param attributeDefinitionName the attribute definition name whose enum values belong to. + * @param oldEnumValues the list of old enum values. + * @param newEnumValues the list of new enum values. + * @param changeOrderEnumCallback the function that is called to apply the change in the order. + * @param the enum type of the elements to change the order for. + * @return an optional update action if the the order of the enum values is not identical. + * Otherwise, if the enum values order is identical, an empty optional is returned. + */ + @Nonnull + public static Optional> buildChangeEnumValuesOrderUpdateAction( + @Nonnull final String attributeDefinitionName, + @Nonnull final List oldEnumValues, + @Nonnull final List newEnumValues, + @Nonnull final BiFunction, UpdateAction> changeOrderEnumCallback) { + + final List newKeys = newEnumValues + .stream() + .map(WithKey::getKey) + .collect(Collectors.toList()); + + final List existingKeys = oldEnumValues + .stream() + .map(WithKey::getKey) + .filter(newKeys::contains) + .collect(Collectors.toList()); + + final List notExistingKeys = newKeys + .stream() + .filter(newKey -> !existingKeys.contains(newKey)) + .collect(Collectors.toList()); + + final List allKeys = Stream.concat(existingKeys.stream(), notExistingKeys.stream()) + .collect(Collectors.toList()); + + return buildUpdateAction( + allKeys, + newKeys, + () -> changeOrderEnumCallback.apply(attributeDefinitionName, newKeys) + ); + } + + /** + * Checks if there are any new enum values which are not existing in the {@code oldEnumValues}. + * If there are, then "add" enum values update actions are built. + * Otherwise, if there are no new enum values, then an empty list is returned. + * + * @param attributeDefinitionName the attribute definition name whose enum values belong to. + * @param oldEnumValues the list of olf enum values. + * @param newEnumValues the list of new enum values. + * @param addEnumCallback the function that is called in order to add the new enum instance + * @param the enum type of the element to add. + * @return a list of enum values update actions if there are new enum value that should be added. + * Otherwise, if the enum values are identical, an empty optional is returned. + */ + @Nonnull + public static List> buildAddEnumValuesUpdateActions( + @Nonnull final String attributeDefinitionName, + @Nonnull final List oldEnumValues, + @Nonnull final List newEnumValues, + @Nonnull final BiFunction> addEnumCallback) { + + final Map oldEnumValuesKeyMap = getEnumValuesKeyMapWithKeyValidation( + attributeDefinitionName, + oldEnumValues + ); + + return newEnumValues + .stream() + .filter(newEnumValue -> !oldEnumValuesKeyMap.containsKey(newEnumValue.getKey())) + .map(newEnumValue -> addEnumCallback.apply(attributeDefinitionName, newEnumValue)) + .collect(Collectors.toList()); + } + + private TypeUpdateEnumActionsUtils() { + } +} diff --git a/src/main/java/com/commercetools/sync/types/utils/TypeUpdateFieldDefinitionActionUtils.java b/src/main/java/com/commercetools/sync/types/utils/TypeUpdateFieldDefinitionActionUtils.java new file mode 100644 index 0000000000..315e0a4d76 --- /dev/null +++ b/src/main/java/com/commercetools/sync/types/utils/TypeUpdateFieldDefinitionActionUtils.java @@ -0,0 +1,274 @@ +package com.commercetools.sync.types.utils; + +import com.commercetools.sync.commons.exceptions.BuildUpdateActionException; +import com.commercetools.sync.commons.exceptions.DuplicateKeyException; +import com.commercetools.sync.commons.exceptions.DuplicateNameException; +import io.sphere.sdk.commands.UpdateAction; +import io.sphere.sdk.types.FieldDefinition; +import io.sphere.sdk.types.Type; +import io.sphere.sdk.types.commands.updateactions.AddFieldDefinition; +import io.sphere.sdk.types.commands.updateactions.ChangeFieldDefinitionOrder; +import io.sphere.sdk.types.commands.updateactions.RemoveFieldDefinition; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static com.commercetools.sync.commons.utils.CommonTypeUpdateActionUtils.buildUpdateAction; +import static com.commercetools.sync.types.utils.FieldDefinitionUpdateActionUtils.buildActions; +import static java.lang.String.format; +import static java.util.Collections.singletonList; +import static java.util.Optional.ofNullable; +import static java.util.stream.Collectors.toList; +import static java.util.stream.Collectors.toMap; + +public final class TypeUpdateFieldDefinitionActionUtils { + + /** + * Compares a list of {@link FieldDefinition}s with a list of {@link FieldDefinition}s. + * The method serves as a generic implementation for field definitions syncing. The method takes in functions + * for building the required update actions (AddFieldDefinition, RemoveFieldDefinition, ChangeFieldDefinitionOrder + * and 1-1 update actions on field definitions (e.g. changeFieldDefinitionLabel, etc..) for the required + * resource. + * + *

If the list of new {@link FieldDefinition}s is {@code null}, then remove actions are built for + * every existing field definition in the {@code oldAttributeDefinitions} list. + * + * @param oldFieldDefinitions the old list of field definitions. + * @param newFieldDefinitions the new list of field definitions. + * @return a list of field definitions update actions if the list of field definitions is not identical. + * Otherwise, if the field definitions are identical, an empty list is returned. + * @throws BuildUpdateActionException in case there are field definitions drafts with duplicate names. + */ + @Nonnull + public static List> buildFieldDefinitionsUpdateActions( + @Nonnull final List oldFieldDefinitions, + @Nullable final List newFieldDefinitions) + throws BuildUpdateActionException { + + if (newFieldDefinitions != null) { + return buildUpdateActions( + oldFieldDefinitions, + newFieldDefinitions + ); + } else { + return newFieldDefinitions + .stream() + .map(FieldDefinition::getName) + .map(RemoveFieldDefinition::of) + .collect(Collectors.toList()); + } + } + + /** + * Compares a list of {@link FieldDefinition}s with a list of {@link FieldDefinition}s. + * The method serves as an implementation for field definitions syncing. The method takes in functions + * for building the required update actions (AddFieldDefinition, RemoveFieldDefinition, ChangeFieldDefinitionOrder and 1-1 + * update actions on field definitions (e.g. changeFieldDefinitionLabel, etc..) for the required + * resource. + * + * @param oldFieldDefinitions the old list of field definitions. + * @param newFieldDefinitions the new list of field definitions drafts. + * @return a list of field definitions update actions if the list of attribute definitions is not identical. + * Otherwise, if the field definitions are identical, an empty list is returned. + * @throws BuildUpdateActionException in case there are field definitions drafts with duplicate names. + */ + @Nonnull + private static List> buildUpdateActions( + @Nonnull final List oldFieldDefinitions, + @Nonnull final List newFieldDefinitions) + throws BuildUpdateActionException { + + final Map oldFieldDefinitionsNameMap = + oldFieldDefinitions + .stream() + .collect(toMap(FieldDefinition::getName, fieldDefinition -> fieldDefinition)); + + try { + + final List> updateActions = + buildRemoveFieldDefinitionOrFieldDefinitionUpdateActions( + oldFieldDefinitions, + newFieldDefinitions + ); + + updateActions.addAll( + buildAddFieldDefinitionUpdateActions( + newFieldDefinitions, + oldFieldDefinitionsNameMap + ) + ); + + buildChangeFieldDefinitionOrderUpdateAction( + oldFieldDefinitions, + newFieldDefinitions + ) + .ifPresent(updateActions::add); + + return updateActions; + + } catch (final DuplicateNameException | DuplicateKeyException exception) { + throw new BuildUpdateActionException(exception); + } + } + + /** + * Checks if there are any field definitions which are not existing in the + * {@code newFieldDefinitions}. If there are, then "remove" field definition update actions are + * built. + * Otherwise, if the field definition still exists in the new field definition, then compare the field definition + * fields (label, etc..), and add the computed actions to the list of update actions. + * + *

Note: If the field type field changes, the old field definition is removed and the new attribute + * definition is added with the new field type. + * + * @param oldFieldDefinitions the list of old {@link FieldDefinition}s. + * @param newFieldDefinitions the list of new {@link FieldDefinition}s. + * @return a list of field definition update actions if there are field that are not existing + * in the new draft. If the field definition still exists in the new draft, then compare the field + * definition fields (name, label, etc..), and add the computed actions to the list of update actions. + * Otherwise, if the field definitions are identical, an empty optional is returned. + */ + @Nonnull + private static List> buildRemoveFieldDefinitionOrFieldDefinitionUpdateActions( + @Nonnull final List oldFieldDefinitions, + @Nonnull final List newFieldDefinitions) { + + final Map newFieldDefinitionsNameMap = + newFieldDefinitions + .stream().collect( + toMap(FieldDefinition::getName, fieldDefinition -> fieldDefinition, + (fieldDefinitionA, fieldDefinitionB) -> { + throw new DuplicateNameException(format("Field definitions have duplicated names. " + + "Duplicated field definition name: '%s'. " + + "Field definitions names are expected to be unique inside their type.", + fieldDefinitionA.getName())); + } + )); + + return oldFieldDefinitions + .stream() + .map(oldFieldDefinition -> { + final String oldFieldDefinitionName = oldFieldDefinition.getName(); + final FieldDefinition matchingNewFieldDefinition = + newFieldDefinitionsNameMap.get(oldFieldDefinitionName); + + return ofNullable(matchingNewFieldDefinition) + .map(newFieldDefinition -> { + if (newFieldDefinition.getType() != null){ + // field type is required so if null we let commercetools to throw exception + if (haveSameFieldType(oldFieldDefinition, newFieldDefinition)){ + return buildActions(oldFieldDefinition, newFieldDefinition); + } else { + return Arrays.asList( + RemoveFieldDefinition.of(oldFieldDefinitionName), + AddFieldDefinition.of(newFieldDefinition) + ); + } + } else { + return new ArrayList>(); + } + }) + .orElseGet(() -> singletonList(RemoveFieldDefinition.of(oldFieldDefinitionName))); + }) + .flatMap(Collection::stream) + .collect(Collectors.toList()); + + } + + /** + * Compares the field types of the {@code fieldDefinitionA} and the {@code fieldDefinitionB} and + * returns true if both field definitions have the same attribute type, false otherwise. + * + * @param fieldDefinitionA the first field to compare. + * @param fieldDefinitionB the second field definition to compare. + * @return true if both field definitions have the same field type, false otherwise. + */ + private static boolean haveSameFieldType( + @Nonnull final FieldDefinition fieldDefinitionA, + @Nonnull final FieldDefinition fieldDefinitionB) { + + return fieldDefinitionA.getType().getClass() == fieldDefinitionB.getType().getClass(); + } + + /** + * Compares the order of a list of old {@link FieldDefinition}s and a list of new + * {@link FieldDefinition}s. + * If there is a change in order, then a change field definition order + * (with the new order) is built. If there are no changes in order an empty optional is returned. + * + * @param oldFieldDefinitions the list of old {@link FieldDefinition}s + * @param newFieldDefinitions the list of new {@link FieldDefinition}s + * @return a list of field definition update actions if the the order of field definitions is not + * identical. Otherwise, if the field definitions order is identical, an empty optional is returned. + */ + @Nonnull + private static Optional> buildChangeFieldDefinitionOrderUpdateAction( + @Nonnull final List oldFieldDefinitions, + @Nonnull final List newFieldDefinitions) { + + + final List newNames = newFieldDefinitions + .stream() + .map(FieldDefinition::getName) + .collect(toList()); + + final List existingNames = oldFieldDefinitions + .stream() + .map(FieldDefinition::getName) + .filter(newNames::contains) + .collect(toList()); + + final List notExistingNames = newNames + .stream() + .filter(newName -> !existingNames.contains(newName)) + .collect(toList()); + + final List newFieldDefinitionsOrderNames = newFieldDefinitions + .stream() + .map(FieldDefinition::getName) + .collect(toList()); + + final List allNames = Stream.concat(existingNames.stream(), notExistingNames.stream()) + .collect(toList()); + + return buildUpdateAction( + allNames, + newNames, + () -> ChangeFieldDefinitionOrder.of(newFieldDefinitionsOrderNames) + ); + } + + /** + * Checks if there are any new field definition drafts which are not existing in the + * {@code oldFieldDefinitionNameMap}. If there are, then "add" field definition update actions are built. + * Otherwise, if there are no new attribute definitions, then an empty list is returned. + * + * @param newFieldDefinitions the list of new {@link FieldDefinition}s. + * @param oldFieldDefinitionNameMap a map of names to FieldDefinition of the old list + * of field definition. + * @return a list of field definition update actions if there are new field definition that should be added. + * Otherwise, if the field definitions are identical, an empty optional is returned. + */ + @Nonnull + private static List> buildAddFieldDefinitionUpdateActions( + @Nonnull final List newFieldDefinitions, + @Nonnull final Map oldFieldDefinitionNameMap) { + + return newFieldDefinitions + .stream() + .filter(fieldDefinition -> !oldFieldDefinitionNameMap.containsKey(fieldDefinition.getName())) + .map(fieldDefinition -> FieldDefinition.of(fieldDefinition.getType(), + fieldDefinition.getName(), + fieldDefinition.getLabel(), + fieldDefinition.isRequired(), + fieldDefinition.getInputHint())) + .map(AddFieldDefinition::of) + .collect(Collectors.toList()); + } + + private TypeUpdateFieldDefinitionActionUtils() { + } +} diff --git a/src/main/java/com/commercetools/sync/types/utils/TypeUpdateLocalizedEnumActionUtils.java b/src/main/java/com/commercetools/sync/types/utils/TypeUpdateLocalizedEnumActionUtils.java new file mode 100644 index 0000000000..b75de27ee0 --- /dev/null +++ b/src/main/java/com/commercetools/sync/types/utils/TypeUpdateLocalizedEnumActionUtils.java @@ -0,0 +1,104 @@ +package com.commercetools.sync.types.utils; + +import com.commercetools.sync.commons.exceptions.DuplicateKeyException; +import io.sphere.sdk.commands.UpdateAction; +import io.sphere.sdk.models.LocalizedEnumValue; +import io.sphere.sdk.types.Type; +import io.sphere.sdk.types.commands.updateactions.AddLocalizedEnumValue; +import io.sphere.sdk.types.commands.updateactions.ChangeLocalizedEnumValueOrder; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static com.commercetools.sync.types.utils.TypeUpdateEnumActionsUtils.buildAddEnumValuesUpdateActions; +import static com.commercetools.sync.types.utils.TypeUpdateEnumActionsUtils.buildChangeEnumValuesOrderUpdateAction; +import static java.util.Collections.emptyList; + +public final class TypeUpdateLocalizedEnumActionUtils { + /** + * Compares a list of old {@link LocalizedEnumValue}s with a list of new {@link LocalizedEnumValue}s for a given + * attribute definition. + * The method serves as a generic implementation for localized enum values syncing. The method takes in functions + * for building the required update actions (AddLocalizedEnumValue, RemoveEnumValue, ChangeLocalizedEnumValueOrder + * and 1-1 update actions on localized enum values (e.g. changeLabel) for the required resource. + * + *

If the list of new {@link LocalizedEnumValue}s is {@code null}, then remove actions are built for + * every existing localized enum value in the {@code oldEnumValues} list. + * + * @param fieldDefinitionName the attribute name whose localized enum values are going to be synced. + * @param oldEnumValues the old list of localized enum values. + * @param newEnumValues the new list of localized enum values. + * @return a list of localized enum values update actions if the list of localized enum values is not identical. + * Otherwise, if the localized enum values are identical, an empty list is returned. + * @throws DuplicateKeyException in case there are localized enum values with duplicate keys. + */ + @Nonnull + public static List> buildLocalizedEnumValuesUpdateActions( + @Nonnull final String fieldDefinitionName, + @Nonnull final List oldEnumValues, + @Nullable final List newEnumValues) { + + if (newEnumValues != null && !newEnumValues.isEmpty()) { + return buildUpdateActions( + fieldDefinitionName, + oldEnumValues, + newEnumValues + ); + } + + return emptyList(); + } + + /** + * Compares a list of old {@link LocalizedEnumValue}s with a list of new {@link LocalizedEnumValue}s for a given + * attribute definition. + * The method serves as a generic implementation for localized enum values syncing. The method takes in functions + * for building the required update actions (AddEnumValue, RemoveEnumValue, ChangeEnumValueOrder and 1-1 + * update actions on localized enum values (e.g. changeLabel) for the required resource. + * + * @param attributeDefinitionName the attribute name whose localized enum values are going to be synced. + * @param oldEnumValues the old list of localized enum values. + * @param newEnumValues the new list of localized enum values. + * @return a list of localized enum values update actions if the list of localized enum values is not identical. + * Otherwise, if the localized enum values are identical, an empty list is returned. + * @throws DuplicateKeyException in case there are localized enum values with duplicate keys. + */ + @Nonnull + private static List> buildUpdateActions( + @Nonnull final String attributeDefinitionName, + @Nonnull final List oldEnumValues, + @Nonnull final List newEnumValues) { + + + final List> addEnumValuesUpdateActions = buildAddEnumValuesUpdateActions( + attributeDefinitionName, + oldEnumValues, + newEnumValues, + AddLocalizedEnumValue::of + ); + + final List> changeEnumValuesOrderUpdateActions = + buildChangeEnumValuesOrderUpdateAction( + attributeDefinitionName, + oldEnumValues, + newEnumValues, + ChangeLocalizedEnumValueOrder::of + ) + .map(Collections::singletonList) + .orElse(emptyList()); + + return Stream.concat( + + addEnumValuesUpdateActions.stream(), + changeEnumValuesOrderUpdateActions.stream() + + ).collect(Collectors.toList()); + } + + private TypeUpdateLocalizedEnumActionUtils() { + } +} diff --git a/src/main/java/com/commercetools/sync/types/utils/TypeUpdatePlainEnumActionUtils.java b/src/main/java/com/commercetools/sync/types/utils/TypeUpdatePlainEnumActionUtils.java new file mode 100644 index 0000000000..21e3dd4a71 --- /dev/null +++ b/src/main/java/com/commercetools/sync/types/utils/TypeUpdatePlainEnumActionUtils.java @@ -0,0 +1,97 @@ +package com.commercetools.sync.types.utils; + +import io.sphere.sdk.commands.UpdateAction; +import io.sphere.sdk.models.EnumValue; +import io.sphere.sdk.types.Type; +import io.sphere.sdk.types.commands.updateactions.AddEnumValue; +import io.sphere.sdk.types.commands.updateactions.ChangeEnumValueOrder; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static com.commercetools.sync.types.utils.TypeUpdateEnumActionsUtils.buildAddEnumValuesUpdateActions; +import static com.commercetools.sync.types.utils.TypeUpdateEnumActionsUtils.buildChangeEnumValuesOrderUpdateAction; +import static java.util.Collections.emptyList; + +public final class TypeUpdatePlainEnumActionUtils { + /** + * Compares a list of old {@link EnumValue}s with a list of new {@link EnumValue}s for a given + * field definition. + * The method serves as a generic implementation for plain enum values syncing. The method takes in functions + * for building the required update actions (AddEnumValue, ChangeEnumValueOrder + * and 1-1 update actions on plain enum values (e.g. changeLabel) for the required resource. + * + *

If the list of new {@link EnumValue}s is {@code null}, then remove actions are built for + * every existing plain enum value in the {@code oldEnumValues} list. + * + * @param fieldDefinitionName the field name whose plain enum values are going to be synced. + * @param oldEnumValues the old list of plain enum values. + * @param newEnumValues the new list of plain enum values. + * @return a list of plain enum values update actions if the list of plain enum values is not identical. + * Otherwise, if the plain enum values are identical, an empty list is returned. + */ + @Nonnull + public static List> buildEnumValuesUpdateActions( + @Nonnull final String fieldDefinitionName, + @Nonnull final List oldEnumValues, + @Nullable final List newEnumValues) { + + if (newEnumValues != null && !newEnumValues.isEmpty()) { + return buildUpdateActions(fieldDefinitionName, oldEnumValues, newEnumValues); + } + + return emptyList(); + } + + + /** + * Compares a list of old {@link EnumValue}s with a list of new {@link EnumValue}s for a given attribute + * definition. + * The method serves as a implementation for plain enum values syncing. The method takes in functions + * for building the required update actions (AddEnumValue, RemoveEnumValue, ChangeEnumValueOrder and 1-1 + * update actions on plain enum values (e.g. changeLabel) for the required resource. + * + * @param fieldDefinitionName the field definition name whose plain enum values are going to be synced. + * @param oldEnumValues the old list of plain enum values. + * @param newEnumValues the new list of plain enum values. + * @return a list of plain enum values update actions if the list of plain enum values is not identical. + * Otherwise, if the plain enum values are identical, an empty list is returned. + */ + @Nonnull + private static List> buildUpdateActions( + @Nonnull final String fieldDefinitionName, + @Nonnull final List oldEnumValues, + @Nonnull final List newEnumValues) { + + + final List> addEnumValuesUpdateActions = buildAddEnumValuesUpdateActions( + fieldDefinitionName, + oldEnumValues, + newEnumValues, + AddEnumValue::of + ); + + final List> changeEnumValuesOrderUpdateActions= + buildChangeEnumValuesOrderUpdateAction( + fieldDefinitionName, + oldEnumValues, + newEnumValues, + ChangeEnumValueOrder::of + ) + .map(Collections::singletonList) + .orElse(emptyList()); + + return Stream.concat( + addEnumValuesUpdateActions.stream(), + changeEnumValuesOrderUpdateActions.stream()) + .collect(Collectors.toList()); + } + + + private TypeUpdatePlainEnumActionUtils() { + } +} diff --git a/src/test/java/com/commercetools/sync/services/impl/TypeServiceTest.java b/src/test/java/com/commercetools/sync/services/impl/TypeServiceTest.java new file mode 100644 index 0000000000..e3493af837 --- /dev/null +++ b/src/test/java/com/commercetools/sync/services/impl/TypeServiceTest.java @@ -0,0 +1,100 @@ +package com.commercetools.sync.services.impl; + +import com.commercetools.sync.services.TypeService; +import com.commercetools.sync.types.TypeSyncOptions; +import com.commercetools.sync.types.TypeSyncOptionsBuilder; +import io.sphere.sdk.client.BadRequestException; +import io.sphere.sdk.client.SphereClient; +import io.sphere.sdk.types.Type; +import io.sphere.sdk.types.TypeDraft; +import io.sphere.sdk.types.commands.TypeCreateCommand; +import io.sphere.sdk.utils.CompletableFutureUtils; +import org.junit.Before; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +import static java.util.concurrent.CompletableFuture.completedFuture; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.*; + +public class TypeServiceTest { + + private TypeService service; + private TypeSyncOptions typeSyncOptions; + private List errorMessages; + private List errorExceptions; + + @Before + public void setUp() { + errorMessages = new ArrayList<>(); + errorExceptions = new ArrayList<>(); + typeSyncOptions = TypeSyncOptionsBuilder.of(mock(SphereClient.class)) + .errorCallback((errorMessage, errorException) -> { + errorMessages.add(errorMessage); + errorExceptions.add(errorException); + }) + .build(); + service = new TypeServiceImpl(typeSyncOptions); + } + + + @Test + public void createType_WithSuccessfulMockCtpResponse_ShouldReturnMock() { + final Type mock = mock(Type.class); + when(mock.getId()).thenReturn("typeId"); + + when(typeSyncOptions.getCtpClient().execute(any())) + .thenReturn(completedFuture(mock)); + + final TypeDraft draft = mock(TypeDraft.class); + when(draft.getKey()).thenReturn("typeKey"); + + final Optional typeOptional = Optional.ofNullable(service.createType(draft) + .toCompletableFuture().join()); + + assertThat(typeOptional).isNotEmpty(); + assertThat(typeOptional).containsSame(mock); + + verify(typeSyncOptions.getCtpClient()).execute(eq(TypeCreateCommand.of(draft))); + } + + @Test + public void createType_WithUnSuccessfulMockCtpResponse_ShouldNotCreateType() { + final Type mock = mock(Type.class); + when(mock.getId()).thenReturn("typeId"); + + when(typeSyncOptions.getCtpClient().execute(any())) + .thenReturn(CompletableFutureUtils.failed(new BadRequestException("bad request"))); + + final TypeDraft draft = mock(TypeDraft.class); + when(draft.getKey()).thenReturn("typeKey"); + final Optional typeOptional = Optional.ofNullable(service.createType(draft).toCompletableFuture().join()); + + assertThat(typeOptional).isEmpty(); + assertThat(errorMessages).hasSize(1); + assertThat(errorExceptions).hasSize(1); + assertThat(errorExceptions.get(0)).isExactlyInstanceOf(BadRequestException.class); + assertThat(errorMessages.get(0)).contains("Failed to create draft with key: 'typeKey'."); + assertThat(errorMessages.get(0)).contains("BadRequestException"); + } + + @Test + public void createType_WithDraftWithoutKey_ShouldNotCreateType() { + final TypeDraft draft = mock(TypeDraft.class); + final Optional typeOptional = Optional.ofNullable(service.createType(draft).toCompletableFuture().join()); + + assertThat(typeOptional).isEmpty(); + assertThat(errorMessages).hasSize(1); + assertThat(errorExceptions).hasSize(1); + assertThat(errorMessages.get(0)) + .isEqualTo("Failed to create draft with key: 'null'. Reason: Draft key is blank!"); + } + + + +} \ No newline at end of file diff --git a/src/test/java/com/commercetools/sync/types/FieldDefinitionTestHelper.java b/src/test/java/com/commercetools/sync/types/FieldDefinitionTestHelper.java new file mode 100644 index 0000000000..9ce18a98bd --- /dev/null +++ b/src/test/java/com/commercetools/sync/types/FieldDefinitionTestHelper.java @@ -0,0 +1,53 @@ +package com.commercetools.sync.types; + +import io.sphere.sdk.categories.Category; +import io.sphere.sdk.models.EnumValue; +import io.sphere.sdk.models.LocalizedString; +import io.sphere.sdk.models.TextInputHint; +import io.sphere.sdk.types.*; + +import java.util.Arrays; +import java.util.List; + +public final class FieldDefinitionTestHelper { + + public static FieldDefinition stringFieldDefinition(String fieldName, + String labelEng, + boolean required, + TextInputHint hint){ + return FieldDefinition.of(StringFieldType.of(), + fieldName, + LocalizedString.ofEnglish(labelEng), + required, + hint); + } + + public static FieldDefinition stateFieldDefinition() { + final List values = Arrays.asList( + EnumValue.of("published", "the category is publicly visible"), + EnumValue.of("draft", "the category should not be displayed in the frontend") + ); + final boolean required = false; + final LocalizedString label = LocalizedString.ofEnglish("state of the category concerning to show it publicly"); + final String fieldName = "state"; + return FieldDefinition + .of(EnumFieldType.of(values), fieldName, label, required); + } + + public static FieldDefinition imageUrlFieldDefinition() { + final LocalizedString imageUrlLabel = + LocalizedString.ofEnglish("absolute url to an image to display for the category"); + return FieldDefinition + .of(StringFieldType.of(), "imageUrl", imageUrlLabel, false, TextInputHint.SINGLE_LINE); + } + + public static FieldDefinition relatedCategoriesFieldDefinition() { + final LocalizedString relatedCategoriesLabel = + LocalizedString.ofEnglish("categories to suggest products similar to the current category"); + //referenceTypeId is required to refere to categories + final String referenceTypeId = Category.referenceTypeId(); + final SetFieldType setType = SetFieldType.of(ReferenceFieldType.of(referenceTypeId)); + return FieldDefinition + .of(setType, "relatedCategories", relatedCategoriesLabel, false); + } +} diff --git a/src/test/java/com/commercetools/sync/types/helpers/TypeSyncStatisticsTest.java b/src/test/java/com/commercetools/sync/types/helpers/TypeSyncStatisticsTest.java new file mode 100644 index 0000000000..5a928bf6ed --- /dev/null +++ b/src/test/java/com/commercetools/sync/types/helpers/TypeSyncStatisticsTest.java @@ -0,0 +1,28 @@ +package com.commercetools.sync.types.helpers; + +import org.junit.Before; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + + +public class TypeSyncStatisticsTest { + private TypeSyncStatistics typeSyncStatistics; + + @Before + public void setup() { + typeSyncStatistics = new TypeSyncStatistics(); + } + + @Test + public void getReportMessage_WithIncrementedStats_ShouldGetCorrectMessage() { + typeSyncStatistics.incrementCreated(1); + typeSyncStatistics.incrementFailed(2); + typeSyncStatistics.incrementUpdated(3); + typeSyncStatistics.incrementProcessed(6); + + assertThat(typeSyncStatistics.getReportMessage()) + .isEqualTo("Summary: 6 types were processed in total " + + "(1 created, 3 updated and 2 failed to sync)."); + } +} diff --git a/src/test/java/com/commercetools/sync/types/utils/FieldDefinitionUpdateActionUtilsTest.java b/src/test/java/com/commercetools/sync/types/utils/FieldDefinitionUpdateActionUtilsTest.java new file mode 100644 index 0000000000..90de8a79e0 --- /dev/null +++ b/src/test/java/com/commercetools/sync/types/utils/FieldDefinitionUpdateActionUtilsTest.java @@ -0,0 +1,165 @@ +package com.commercetools.sync.types.utils; + +import io.sphere.sdk.commands.UpdateAction; +import io.sphere.sdk.models.EnumValue; +import io.sphere.sdk.models.LocalizedEnumValue; +import io.sphere.sdk.models.LocalizedString; +import io.sphere.sdk.models.TextInputHint; +import io.sphere.sdk.types.EnumFieldType; +import io.sphere.sdk.types.FieldDefinition; +import io.sphere.sdk.types.LocalizedEnumFieldType; +import io.sphere.sdk.types.Type; +import io.sphere.sdk.types.commands.updateactions.AddEnumValue; +import io.sphere.sdk.types.commands.updateactions.AddLocalizedEnumValue; +import io.sphere.sdk.types.commands.updateactions.ChangeFieldDefinitionLabel; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +import static com.commercetools.sync.types.FieldDefinitionTestHelper.stringFieldDefinition; +import static com.commercetools.sync.types.utils.FieldDefinitionUpdateActionUtils.buildActions; +import static com.commercetools.sync.types.utils.FieldDefinitionUpdateActionUtils.buildChangeLabelUpdateAction; +import static io.sphere.sdk.models.LocalizedString.ofEnglish; +import static org.assertj.core.api.Assertions.assertThat; + +public class FieldDefinitionUpdateActionUtilsTest { + private static final String FIELD_NAME_1 = "fieldName1"; + private static final String LABEL_1 = "label1"; + private static final String LABEL_2 = "label2"; + + + private static FieldDefinition old; + private static FieldDefinition oldNullValues; + private static FieldDefinition newSame; + private static FieldDefinition newDifferent; + private static FieldDefinition newNullValues; + + private static final EnumValue ENUM_VALUE_A = EnumValue.of("a", "label_a"); + private static final EnumValue ENUM_VALUE_B = EnumValue.of("b", "label_b"); + + private static final LocalizedEnumValue LOCALIZED_ENUM_VALUE_A = LocalizedEnumValue.of("a", ofEnglish("label_a")); + private static final LocalizedEnumValue LOCALIZED_ENUM_VALUE_B = LocalizedEnumValue.of("b", ofEnglish("label_b")); + + /** + * Initialises test data. + */ + @BeforeClass + public static void setup() { + + old = stringFieldDefinition(FIELD_NAME_1, LABEL_1, false, TextInputHint.SINGLE_LINE); + oldNullValues = stringFieldDefinition(FIELD_NAME_1, LABEL_1, false, null); + newSame = stringFieldDefinition(FIELD_NAME_1, LABEL_1, false, TextInputHint.SINGLE_LINE); + newDifferent = stringFieldDefinition(FIELD_NAME_1, LABEL_2, true, TextInputHint.MULTI_LINE); + newNullValues = stringFieldDefinition(FIELD_NAME_1, LABEL_2, true, null); + } + + @Test + public void buildChangeLabelAction_WithDifferentValues_ShouldReturnAction() { + final Optional> result = buildChangeLabelUpdateAction(old, newDifferent); + + assertThat(result).contains(ChangeFieldDefinitionLabel.of(old.getName(), newDifferent.getLabel())); + } + + @Test + public void buildChangeLabelAction_WithSameValues_ShouldReturnEmptyOptional() { + final Optional> result = buildChangeLabelUpdateAction(old, newSame); + + assertThat(result).isEmpty(); + } + + @Test + public void buildActions_WithNewDifferentValues_ShouldReturnActions() { + final List> result = buildActions(old, newDifferent); + + assertThat(result).containsExactlyInAnyOrder( + ChangeFieldDefinitionLabel.of(old.getName(), newDifferent.getLabel()) + ); + } + + @Test + public void buildActions_WithSameValues_ShouldReturnEmpty() { + final List> result = buildActions(old, newSame); + + assertThat(result).isEmpty(); + } + + @Test + public void buildActions_WithNewPlainEnum_ShouldReturnAddEnumValueAction() { + final FieldDefinition oldFieldDefinition = FieldDefinition.of( + EnumFieldType.of(Arrays.asList(ENUM_VALUE_A)), + FIELD_NAME_1, + LocalizedString.ofEnglish(LABEL_1), + false, + TextInputHint.SINGLE_LINE); + + + final FieldDefinition newFieldDefinition = FieldDefinition.of( + EnumFieldType.of(Arrays.asList(ENUM_VALUE_A, ENUM_VALUE_B)), + FIELD_NAME_1, + LocalizedString.ofEnglish(LABEL_1), + false, + TextInputHint.SINGLE_LINE); + + final List> result = + buildActions(oldFieldDefinition, newFieldDefinition); + + + assertThat(result).containsExactly(AddEnumValue.of(FIELD_NAME_1, ENUM_VALUE_A)); + } + + + @Test + public void buildActions_WithoutOldPlainEnum_ShouldNotReturnAnyValueAction() { + final FieldDefinition oldFieldDefinition = FieldDefinition.of( + EnumFieldType.of(Arrays.asList(ENUM_VALUE_A)), + FIELD_NAME_1, + LocalizedString.ofEnglish(LABEL_1), + false, + TextInputHint.SINGLE_LINE); + + final FieldDefinition newFieldDefinition = FieldDefinition.of( + EnumFieldType.of(Collections.emptyList()), + FIELD_NAME_1, + LocalizedString.ofEnglish(LABEL_1), + false, + TextInputHint.SINGLE_LINE); + + + //TODO: For type sync there is no remove enum values action for the field definitions. + final List> result = buildActions(oldFieldDefinition, newFieldDefinition); + + assertThat(result).isEmpty(); + } + + + @Test + public void buildActions_WithNewLocalizedEnum_ShouldReturnAddLocalizedEnumValueAction() { + + final FieldDefinition oldFieldDefinition = FieldDefinition.of( + LocalizedEnumFieldType.of(Arrays.asList(LOCALIZED_ENUM_VALUE_A)), + FIELD_NAME_1, + LocalizedString.ofEnglish(LABEL_1), + false, + TextInputHint.SINGLE_LINE); + + final FieldDefinition newFieldDefinition = FieldDefinition.of( + LocalizedEnumFieldType.of(Arrays.asList(LOCALIZED_ENUM_VALUE_A, LOCALIZED_ENUM_VALUE_B)), + FIELD_NAME_1, + LocalizedString.ofEnglish(LABEL_1), + false, + TextInputHint.SINGLE_LINE); + + + final List> result = buildActions(oldFieldDefinition, newFieldDefinition); + + assertThat(result).containsExactly(AddLocalizedEnumValue.of(FIELD_NAME_1, LOCALIZED_ENUM_VALUE_B)); + } + + + + +} diff --git a/src/test/java/com/commercetools/sync/types/utils/TypeUpdateActionUtilsTest.java b/src/test/java/com/commercetools/sync/types/utils/TypeUpdateActionUtilsTest.java new file mode 100644 index 0000000000..2cabec82d2 --- /dev/null +++ b/src/test/java/com/commercetools/sync/types/utils/TypeUpdateActionUtilsTest.java @@ -0,0 +1,98 @@ +package com.commercetools.sync.types.utils; + +import io.sphere.sdk.commands.UpdateAction; +import io.sphere.sdk.models.LocalizedString; +import io.sphere.sdk.types.*; +import io.sphere.sdk.types.commands.updateactions.ChangeName; +import io.sphere.sdk.types.commands.updateactions.SetDescription; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.util.Arrays; +import java.util.List; +import java.util.Optional; +import java.util.Set; + +import static com.commercetools.sync.types.FieldDefinitionTestHelper.*; +import static com.commercetools.sync.types.utils.TypeUpdateActionUtils.buildChangeNameAction; +import static com.commercetools.sync.types.utils.TypeUpdateActionUtils.buildSetDescriptionUpdateAction; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class TypeUpdateActionUtilsTest { + private static Type old; + private static TypeDraft newSame; + private static TypeDraft newDifferent; + + /** + * Initialises test data. + */ + @BeforeClass + public static void setup() { + final String key = "category-customtype-key"; + final LocalizedString name = LocalizedString.ofEnglish("type for standard categories"); + final LocalizedString desc = LocalizedString.ofEnglish("description for category custom type"); + + //this enables the type only to be used for categories + final Set resourceTypeIds = ResourceTypeIdsSetBuilder.of() + .addCategories() + //there are more methods for other objects which support custom + .build(); + + final List fieldDefinitions = + Arrays.asList(stateFieldDefinition(), + imageUrlFieldDefinition(), + relatedCategoriesFieldDefinition()); + + + old = mock(Type.class); + when(old.getKey()).thenReturn(key); + when(old.getName()).thenReturn(name); + when(old.getDescription()).thenReturn(desc); + when(old.getFieldDefinitions()).thenReturn(fieldDefinitions); + + newSame = TypeDraftBuilder.of(key, name, resourceTypeIds) + .description(desc) + .fieldDefinitions(fieldDefinitions) + .build(); + + + newDifferent = TypeDraftBuilder.of("222-category-customtype-key-2", + LocalizedString.ofEnglish("222type for standard categories 222"), + resourceTypeIds) + .description(LocalizedString.ofEnglish("222 description for category custom type 2222")) + .fieldDefinitions(fieldDefinitions) + .build(); + } + + @Test + public void buildChangeNameAction_WithDifferentValues_ShouldReturnAction() { + final Optional> result = buildChangeNameAction(old, newDifferent); + + assertThat(result).containsInstanceOf(ChangeName.class); + assertThat(result).contains(ChangeName.of(newDifferent.getName())); + } + + @Test + public void buildChangeNameAction_WithSameValues_ShouldReturnEmptyOptional() { + final Optional> result = buildChangeNameAction(old, newSame); + + assertThat(result).isEmpty(); + } + + @Test + public void buildSetDescriptionAction_WithDifferentValues_ShouldReturnAction() { + final Optional> result = buildSetDescriptionUpdateAction(old, newDifferent); + + assertThat(result).containsInstanceOf(SetDescription.class); + assertThat(result).contains(SetDescription.of(newDifferent.getDescription())); + } + + @Test + public void buildSetDescriptionAction_WithSameValues_ShouldReturnEmptyOptional() { + final Optional> result = buildSetDescriptionUpdateAction(old, newSame); + + assertThat(result).isEmpty(); + } +} From aedf0876a3598ca79746df07bfa77e4218ee38a7 Mon Sep 17 00:00:00 2001 From: aoz Date: Thu, 13 Sep 2018 12:29:48 +0200 Subject: [PATCH 002/164] #300: fix checkStyles --- .../sync/services/impl/TypeServiceImpl.java | 5 +++-- .../sync/types/TypeSyncOptions.java | 3 ++- .../FieldDefinitionUpdateActionUtils.java | 4 ++-- .../types/utils/TypeUpdateActionUtils.java | 2 +- .../TypeUpdateFieldDefinitionActionUtils.java | 18 ++++++++++++------ .../utils/TypeUpdatePlainEnumActionUtils.java | 2 +- .../sync/types/FieldDefinitionTestHelper.java | 12 ++++++++---- .../types/utils/TypeUpdateActionUtilsTest.java | 6 +++++- 8 files changed, 34 insertions(+), 18 deletions(-) diff --git a/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java b/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java index ca6899e114..19c2931db4 100644 --- a/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java +++ b/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java @@ -63,13 +63,14 @@ public CompletionStage> fetchMatchingTypesByKeys(@Nonnull final Set createType(@Nonnull TypeDraft typeDraft) { + public CompletionStage createType(@Nonnull final TypeDraft typeDraft) { return syncOptions.getCtpClient().execute(TypeCreateCommand.of(typeDraft)); } @Nonnull @Override - public CompletionStage updateType(@Nonnull Type type, @Nonnull List> updateActions) { + public CompletionStage updateType(@Nonnull final Type type, + @Nonnull final List> updateActions) { return syncOptions.getCtpClient().execute(TypeUpdateCommand.of(type, updateActions)); } diff --git a/src/main/java/com/commercetools/sync/types/TypeSyncOptions.java b/src/main/java/com/commercetools/sync/types/TypeSyncOptions.java index 64d931ca26..fe78762c9a 100644 --- a/src/main/java/com/commercetools/sync/types/TypeSyncOptions.java +++ b/src/main/java/com/commercetools/sync/types/TypeSyncOptions.java @@ -22,7 +22,8 @@ public final class TypeSyncOptions extends BaseSyncOptions { @Nullable final Consumer updateActionWarningCallBack, final int batchSize, final boolean allowUuid, - @Nullable final TriFunction>, TypeDraft, Type, List>> beforeUpdateCallback, + @Nullable final TriFunction>, TypeDraft, + Type, List>> beforeUpdateCallback, @Nullable final Function beforeCreateCallback ) { diff --git a/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionUpdateActionUtils.java b/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionUpdateActionUtils.java index 8d83bdd59c..9ac9617786 100644 --- a/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionUpdateActionUtils.java +++ b/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionUpdateActionUtils.java @@ -47,13 +47,13 @@ public static List> buildActions( .collect(toList()); - if (isPlainEnumField(oldFieldDefinition)){ + if (isPlainEnumField(oldFieldDefinition)) { updateActions.addAll(buildEnumValuesUpdateActions( oldFieldDefinition.getName(), ((EnumFieldType) oldFieldDefinition.getType()).getValues(), ((EnumFieldType) newFieldDefinition.getType()).getValues() )); - } else if (isLocalizedEnumAttribute(oldFieldDefinition)){ + } else if (isLocalizedEnumAttribute(oldFieldDefinition)) { updateActions.addAll(buildLocalizedEnumValuesUpdateActions( oldFieldDefinition.getName(), ((LocalizedEnumFieldType) oldFieldDefinition.getType()).getValues(), diff --git a/src/main/java/com/commercetools/sync/types/utils/TypeUpdateActionUtils.java b/src/main/java/com/commercetools/sync/types/utils/TypeUpdateActionUtils.java index 6c3b967301..1614ce1d92 100644 --- a/src/main/java/com/commercetools/sync/types/utils/TypeUpdateActionUtils.java +++ b/src/main/java/com/commercetools/sync/types/utils/TypeUpdateActionUtils.java @@ -56,7 +56,7 @@ public static Optional> buildSetDescriptionUpdateAction( @Nonnull final Type oldType, @Nonnull final TypeDraft newType) { return buildUpdateAction(oldType.getDescription(), newType.getDescription(), - () -> SetDescription.of(newType.getDescription())); + () -> SetDescription.of(newType.getDescription())); } /** diff --git a/src/main/java/com/commercetools/sync/types/utils/TypeUpdateFieldDefinitionActionUtils.java b/src/main/java/com/commercetools/sync/types/utils/TypeUpdateFieldDefinitionActionUtils.java index 315e0a4d76..e717565d0d 100644 --- a/src/main/java/com/commercetools/sync/types/utils/TypeUpdateFieldDefinitionActionUtils.java +++ b/src/main/java/com/commercetools/sync/types/utils/TypeUpdateFieldDefinitionActionUtils.java @@ -12,7 +12,12 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Optional; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -65,8 +70,8 @@ public static List> buildFieldDefinitionsUpdateActions( /** * Compares a list of {@link FieldDefinition}s with a list of {@link FieldDefinition}s. * The method serves as an implementation for field definitions syncing. The method takes in functions - * for building the required update actions (AddFieldDefinition, RemoveFieldDefinition, ChangeFieldDefinitionOrder and 1-1 - * update actions on field definitions (e.g. changeFieldDefinitionLabel, etc..) for the required + * for building the required update actions (AddFieldDefinition, RemoveFieldDefinition, ChangeFieldDefinitionOrder + * and 1-1 update actions on field definitions (e.g. changeFieldDefinitionLabel, etc..) for the required * resource. * * @param oldFieldDefinitions the old list of field definitions. @@ -143,7 +148,8 @@ private static List> buildRemoveFieldDefinitionOrFieldDefinit (fieldDefinitionA, fieldDefinitionB) -> { throw new DuplicateNameException(format("Field definitions have duplicated names. " + "Duplicated field definition name: '%s'. " - + "Field definitions names are expected to be unique inside their type.", + + "Field definitions names are expected " + + "to be unique inside their type.", fieldDefinitionA.getName())); } )); @@ -157,9 +163,9 @@ private static List> buildRemoveFieldDefinitionOrFieldDefinit return ofNullable(matchingNewFieldDefinition) .map(newFieldDefinition -> { - if (newFieldDefinition.getType() != null){ + if (newFieldDefinition.getType() != null) { // field type is required so if null we let commercetools to throw exception - if (haveSameFieldType(oldFieldDefinition, newFieldDefinition)){ + if (haveSameFieldType(oldFieldDefinition, newFieldDefinition)) { return buildActions(oldFieldDefinition, newFieldDefinition); } else { return Arrays.asList( diff --git a/src/main/java/com/commercetools/sync/types/utils/TypeUpdatePlainEnumActionUtils.java b/src/main/java/com/commercetools/sync/types/utils/TypeUpdatePlainEnumActionUtils.java index 21e3dd4a71..f191d46db8 100644 --- a/src/main/java/com/commercetools/sync/types/utils/TypeUpdatePlainEnumActionUtils.java +++ b/src/main/java/com/commercetools/sync/types/utils/TypeUpdatePlainEnumActionUtils.java @@ -75,7 +75,7 @@ private static List> buildUpdateActions( AddEnumValue::of ); - final List> changeEnumValuesOrderUpdateActions= + final List> changeEnumValuesOrderUpdateActions = buildChangeEnumValuesOrderUpdateAction( fieldDefinitionName, oldEnumValues, diff --git a/src/test/java/com/commercetools/sync/types/FieldDefinitionTestHelper.java b/src/test/java/com/commercetools/sync/types/FieldDefinitionTestHelper.java index 9ce18a98bd..6072cf85d9 100644 --- a/src/test/java/com/commercetools/sync/types/FieldDefinitionTestHelper.java +++ b/src/test/java/com/commercetools/sync/types/FieldDefinitionTestHelper.java @@ -4,17 +4,21 @@ import io.sphere.sdk.models.EnumValue; import io.sphere.sdk.models.LocalizedString; import io.sphere.sdk.models.TextInputHint; -import io.sphere.sdk.types.*; +import io.sphere.sdk.types.EnumFieldType; +import io.sphere.sdk.types.FieldDefinition; +import io.sphere.sdk.types.ReferenceFieldType; +import io.sphere.sdk.types.SetFieldType; +import io.sphere.sdk.types.StringFieldType; import java.util.Arrays; import java.util.List; public final class FieldDefinitionTestHelper { - public static FieldDefinition stringFieldDefinition(String fieldName, - String labelEng, + public static FieldDefinition stringFieldDefinition(final String fieldName, + final String labelEng, boolean required, - TextInputHint hint){ + final TextInputHint hint) { return FieldDefinition.of(StringFieldType.of(), fieldName, LocalizedString.ofEnglish(labelEng), diff --git a/src/test/java/com/commercetools/sync/types/utils/TypeUpdateActionUtilsTest.java b/src/test/java/com/commercetools/sync/types/utils/TypeUpdateActionUtilsTest.java index 2cabec82d2..6cc558eb21 100644 --- a/src/test/java/com/commercetools/sync/types/utils/TypeUpdateActionUtilsTest.java +++ b/src/test/java/com/commercetools/sync/types/utils/TypeUpdateActionUtilsTest.java @@ -2,7 +2,11 @@ import io.sphere.sdk.commands.UpdateAction; import io.sphere.sdk.models.LocalizedString; -import io.sphere.sdk.types.*; +import io.sphere.sdk.types.FieldDefinition; +import io.sphere.sdk.types.ResourceTypeIdsSetBuilder; +import io.sphere.sdk.types.Type; +import io.sphere.sdk.types.TypeDraft; +import io.sphere.sdk.types.TypeDraftBuilder; import io.sphere.sdk.types.commands.updateactions.ChangeName; import io.sphere.sdk.types.commands.updateactions.SetDescription; import org.junit.BeforeClass; From 878c733b8314175e32e7cb2f81e394f2bd36a1c1 Mon Sep 17 00:00:00 2001 From: aoz Date: Tue, 18 Sep 2018 17:49:39 +0200 Subject: [PATCH 003/164] #300: add integration test for type sync utilities. --- .../sync/integration/types/TypeSyncIT.java | 836 ++++++++++++++++++ .../integration/types/utils/TypeITUtils.java | 148 ++++ .../statistics/AssertionsForStatistics.java | 12 + .../statistics/TypeSyncStatisticsAssert.java | 13 + 4 files changed, 1009 insertions(+) create mode 100644 src/integration-test/java/com/commercetools/sync/integration/types/TypeSyncIT.java create mode 100644 src/integration-test/java/com/commercetools/sync/integration/types/utils/TypeITUtils.java create mode 100644 src/test/java/com/commercetools/sync/commons/asserts/statistics/TypeSyncStatisticsAssert.java diff --git a/src/integration-test/java/com/commercetools/sync/integration/types/TypeSyncIT.java b/src/integration-test/java/com/commercetools/sync/integration/types/TypeSyncIT.java new file mode 100644 index 0000000000..7c47c1597a --- /dev/null +++ b/src/integration-test/java/com/commercetools/sync/integration/types/TypeSyncIT.java @@ -0,0 +1,836 @@ +package com.commercetools.sync.integration.types; + + +import com.commercetools.sync.commons.asserts.statistics.AssertionsForStatistics; +import com.commercetools.sync.services.TypeService; +import com.commercetools.sync.services.impl.TypeServiceImpl; +import com.commercetools.sync.types.TypeSync; +import com.commercetools.sync.types.TypeSyncOptions; +import com.commercetools.sync.types.TypeSyncOptionsBuilder; +import com.commercetools.sync.types.helpers.TypeSyncStatistics; +import io.sphere.sdk.models.EnumValue; +import io.sphere.sdk.models.LocalizedEnumValue; +import io.sphere.sdk.models.LocalizedString; +import io.sphere.sdk.models.SphereException; +import io.sphere.sdk.models.TextInputHint; +import io.sphere.sdk.types.EnumFieldType; +import io.sphere.sdk.types.FieldDefinition; +import io.sphere.sdk.types.LocalizedEnumFieldType; +import io.sphere.sdk.types.ResourceTypeIdsSetBuilder; +import io.sphere.sdk.types.StringFieldType; +import io.sphere.sdk.types.Type; +import io.sphere.sdk.types.TypeDraft; +import io.sphere.sdk.types.TypeDraftBuilder; +import io.sphere.sdk.types.queries.TypeQuery; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; + +import javax.annotation.Nonnull; +import java.util.List; +import java.util.Locale; +import java.util.Optional; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +import static com.commercetools.sync.integration.commons.utils.ITUtils.deleteTypesFromTargetAndSource; +import static com.commercetools.sync.integration.commons.utils.SphereClientUtils.CTP_SOURCE_CLIENT; +import static com.commercetools.sync.integration.commons.utils.SphereClientUtils.CTP_TARGET_CLIENT; +import static com.commercetools.sync.integration.types.utils.TypeITUtils.FIELD_DEFINITION_1; +import static com.commercetools.sync.integration.types.utils.TypeITUtils.FIELD_DEFINITION_2; +import static com.commercetools.sync.integration.types.utils.TypeITUtils.FIELD_DEFINITION_3; +import static com.commercetools.sync.integration.types.utils.TypeITUtils.FIELD_DEFINITION_LABEL_1; +import static com.commercetools.sync.integration.types.utils.TypeITUtils.FIELD_DEFINITION_NAME_1; +import static com.commercetools.sync.integration.types.utils.TypeITUtils.TYPE_DESCRIPTION_1; +import static com.commercetools.sync.integration.types.utils.TypeITUtils.TYPE_DESCRIPTION_2; +import static com.commercetools.sync.integration.types.utils.TypeITUtils.TYPE_KEY_1; +import static com.commercetools.sync.integration.types.utils.TypeITUtils.TYPE_KEY_2; +import static com.commercetools.sync.integration.types.utils.TypeITUtils.TYPE_NAME_1; +import static com.commercetools.sync.integration.types.utils.TypeITUtils.TYPE_NAME_2; +import static com.commercetools.sync.integration.types.utils.TypeITUtils.getTypeByKey; +import static com.commercetools.sync.integration.types.utils.TypeITUtils.populateSourceProject; +import static com.commercetools.sync.integration.types.utils.TypeITUtils.populateTargetProject; +import static java.util.Arrays.asList; +import static java.util.Collections.singletonList; +import static java.util.concurrent.CompletableFuture.supplyAsync; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + + +public class TypeSyncIT { + + /** + * Deletes types from source and target CTP projects. + * Populates source and target CTP projects with test data. + */ + @Before + public void setup() { + deleteTypesFromTargetAndSource(); + populateSourceProject(); + populateTargetProject(); + } + + /** + * Deletes all the test data from the {@code CTP_SOURCE_CLIENT} and the {@code CTP_SOURCE_CLIENT} projects that + * were set up in this test class. + */ + @AfterClass + public static void tearDown() { + deleteTypesFromTargetAndSource(); + } + + + @Test + public void sync_WithUpdatedType_ShouldUpdateType() { + final Optional oldTypeBefore = getTypeByKey(CTP_TARGET_CLIENT, TYPE_KEY_1); + assertThat(oldTypeBefore).isNotEmpty(); + + final TypeDraft newTypeDraft = TypeDraftBuilder.of( + TYPE_KEY_1, + TYPE_NAME_2, + ResourceTypeIdsSetBuilder.of().addCategories().build()) + .description(TYPE_DESCRIPTION_2) + .fieldDefinitions(singletonList(FIELD_DEFINITION_1)) + .build(); + + final TypeSyncOptions typeSyncOptions = TypeSyncOptionsBuilder + .of(CTP_TARGET_CLIENT) + .build(); + + final TypeSync typeSync = new TypeSync(typeSyncOptions); + + final TypeSyncStatistics typeSyncStatistics = typeSync + .sync(singletonList(newTypeDraft)) + .toCompletableFuture().join(); + + + AssertionsForStatistics.assertThat(typeSyncStatistics).hasValues(1, 0, 1, 0); + + + final Optional oldTypeAfter = getTypeByKey(CTP_TARGET_CLIENT, TYPE_KEY_1); + + assertThat(oldTypeAfter).isNotEmpty(); + assertThat(oldTypeAfter).hasValueSatisfying(type -> { + assertThat(type.getName()).isEqualTo(TYPE_NAME_2); + assertThat(type.getDescription()).isEqualTo(TYPE_DESCRIPTION_2); + assertFieldDefinitionsAreEqual(type.getFieldDefinitions(), singletonList(FIELD_DEFINITION_1)); + }); + } + + @Test + public void sync_WithNewType_ShouldCreateType() { + final Optional oldTypeBefore = getTypeByKey(CTP_TARGET_CLIENT, TYPE_KEY_2); + assertThat(oldTypeBefore).isNotEmpty(); + + final TypeDraft newTypeDraft = TypeDraftBuilder.of( + TYPE_KEY_2, + TYPE_NAME_2, + ResourceTypeIdsSetBuilder.of().addCategories().build()) + .description(TYPE_DESCRIPTION_2) + .fieldDefinitions(singletonList(FIELD_DEFINITION_1)) + .build(); + + final TypeSyncOptions typeSyncOptions = TypeSyncOptionsBuilder + .of(CTP_TARGET_CLIENT) + .build(); + + final TypeSync typeSync = new TypeSync(typeSyncOptions); + + final TypeSyncStatistics typeSyncStatistics = typeSync + .sync(singletonList(newTypeDraft)) + .toCompletableFuture().join(); + + AssertionsForStatistics.assertThat(typeSyncStatistics).hasValues(1, 1, 0, 0); + + + final Optional oldTypeAfter = getTypeByKey(CTP_TARGET_CLIENT, TYPE_KEY_2); + + assertThat(oldTypeAfter).isNotEmpty(); + assertThat(oldTypeAfter).hasValueSatisfying(type -> { + assertThat(type.getName()).isEqualTo(TYPE_NAME_2); + assertThat(type.getDescription()).isEqualTo(TYPE_DESCRIPTION_2); + assertFieldDefinitionsAreEqual(type.getFieldDefinitions(), singletonList(FIELD_DEFINITION_1)); + }); + } + + @Test + public void sync_WithUpdatedType_WithNewFieldDefinitions_ShouldUpdateTypeAddingFieldDefinition() { + final Optional oldTypeBefore = getTypeByKey(CTP_TARGET_CLIENT, TYPE_KEY_1); + assertThat(oldTypeBefore).isNotEmpty(); + + final TypeDraft newTypeDraft = TypeDraftBuilder.of( + TYPE_KEY_1, + TYPE_NAME_1, + ResourceTypeIdsSetBuilder.of().addCategories().build()) + .description(TYPE_DESCRIPTION_1) + .fieldDefinitions(asList(FIELD_DEFINITION_1, FIELD_DEFINITION_2, FIELD_DEFINITION_3)) + .build(); + + + final TypeSyncOptions typeSyncOptions = TypeSyncOptionsBuilder + .of(CTP_TARGET_CLIENT) + .build(); + + final TypeSync typeSync = new TypeSync(typeSyncOptions); + + final TypeSyncStatistics typeSyncStatistics = typeSync + .sync(singletonList(newTypeDraft)) + .toCompletableFuture().join(); + + AssertionsForStatistics.assertThat(typeSyncStatistics).hasValues(1, 0, 1, 0); + + final Optional oldTypeAfter = getTypeByKey(CTP_TARGET_CLIENT, TYPE_KEY_2); + + assertThat(oldTypeAfter).isNotEmpty(); + assertFieldDefinitionsAreEqual(oldTypeAfter.get().getFieldDefinitions(), asList( + FIELD_DEFINITION_1, + FIELD_DEFINITION_2, + FIELD_DEFINITION_3) + ); + } + + @Test + public void sync_WithUpdatedType_WithoutOldFieldDefinition_ShouldUpdateTypeRemovingAttribute() { + final Optional oldTypeBefore = getTypeByKey(CTP_TARGET_CLIENT, TYPE_KEY_1); + assertThat(oldTypeBefore).isNotEmpty(); + + final TypeDraft newTypeDraft = TypeDraftBuilder.of( + TYPE_KEY_1, + TYPE_NAME_1, + ResourceTypeIdsSetBuilder.of().addCategories().build()) + .description(TYPE_DESCRIPTION_1) + .fieldDefinitions(singletonList(FIELD_DEFINITION_1)) + .build(); + + final TypeSyncOptions typeSyncOptions = TypeSyncOptionsBuilder + .of(CTP_TARGET_CLIENT) + .build(); + + final TypeSync typeSync = new TypeSync(typeSyncOptions); + + final TypeSyncStatistics typeSyncStatistics = typeSync + .sync(singletonList(newTypeDraft)) + .toCompletableFuture().join(); + + AssertionsForStatistics.assertThat(typeSyncStatistics).hasValues(1, 0, 1, 0); + + final Optional oldTypeAfter = getTypeByKey(CTP_TARGET_CLIENT, TYPE_KEY_1); + + assertThat(oldTypeAfter).isNotEmpty(); + assertFieldDefinitionsAreEqual(oldTypeAfter.get().getFieldDefinitions(), + singletonList(FIELD_DEFINITION_1)); + } + + @Test + public void sync_WithUpdatedType_ChangingFieldDefinitionOrder_ShouldUpdateTypeChangingFieldDefinitionOrder() { + final Optional oldTypeBefore = getTypeByKey(CTP_TARGET_CLIENT, TYPE_KEY_1); + assertThat(oldTypeBefore).isNotEmpty(); + + final TypeDraft newTypeDraft = TypeDraftBuilder.of( + TYPE_KEY_1, + TYPE_NAME_1, + ResourceTypeIdsSetBuilder.of().addCategories().build()) + .description(TYPE_DESCRIPTION_1) + .fieldDefinitions(asList(FIELD_DEFINITION_2, FIELD_DEFINITION_1)) + .build(); + + final TypeSyncOptions typeSyncOptions = TypeSyncOptionsBuilder + .of(CTP_TARGET_CLIENT) + .build(); + + final TypeSync typeSync = new TypeSync(typeSyncOptions); + + final TypeSyncStatistics typeSyncStatistics = typeSync + .sync(singletonList(newTypeDraft)) + .toCompletableFuture().join(); + + AssertionsForStatistics.assertThat(typeSyncStatistics).hasValues(1, 0, 1, 0); + + final Optional oldTypeAfter = getTypeByKey(CTP_TARGET_CLIENT, TYPE_KEY_1); + + assertThat(oldTypeAfter).isNotEmpty(); + assertFieldDefinitionsAreEqual(oldTypeAfter.get().getFieldDefinitions(), + asList(FIELD_DEFINITION_2, FIELD_DEFINITION_1)); + } + + @Test + public void sync_WithUpdatedType_WithUpdatedFieldDefinition_ShouldUpdateTypeUpdatingFieldDefinition() { + final Optional oldTypeBefore = getTypeByKey(CTP_TARGET_CLIENT, TYPE_KEY_1); + assertThat(oldTypeBefore).isNotEmpty(); + + final FieldDefinition fieldDefinitionUpdated = FieldDefinition.of( + StringFieldType.of(), + FIELD_DEFINITION_NAME_1, + LocalizedString.ofEnglish("label_1_updated"), + true, + TextInputHint.MULTI_LINE); + + final TypeDraft newTypeDraft = TypeDraftBuilder.of( + TYPE_KEY_1, + TYPE_NAME_1, + ResourceTypeIdsSetBuilder.of().addCategories().build()) + .description(TYPE_DESCRIPTION_1) + .fieldDefinitions(singletonList(fieldDefinitionUpdated)) + .build(); + + final TypeSyncOptions typeSyncOptions = TypeSyncOptionsBuilder + .of(CTP_TARGET_CLIENT) + .build(); + + final TypeSync typeSync = new TypeSync(typeSyncOptions); + + final TypeSyncStatistics typeSyncStatistics = typeSync + .sync(singletonList(newTypeDraft)) + .toCompletableFuture().join(); + + AssertionsForStatistics.assertThat(typeSyncStatistics).hasValues(1, 0, 1, 0); + + final Optional oldTypeAfter = getTypeByKey(CTP_TARGET_CLIENT, TYPE_KEY_1); + + assertThat(oldTypeAfter).isNotEmpty(); + assertFieldDefinitionsAreEqual(oldTypeAfter.get().getFieldDefinitions(), + singletonList(fieldDefinitionUpdated)); + } + + @Test + public void sync_FromSourceToTargetProjectWithoutUpdates_ShouldReturnProperStatistics() { + //Fetch new types from source project. Convert them to drafts. + final List types = CTP_SOURCE_CLIENT + .execute(TypeQuery.of()) + .toCompletableFuture().join().getResults(); + + final List typeDrafts = types + .stream() + .map(type -> { + List newFieldDefinitions = type + .getFieldDefinitions() + .stream() + .map(fieldDefinition -> + FieldDefinition.of( + fieldDefinition.getType(), + fieldDefinition.getName(), + fieldDefinition.getLabel(), + fieldDefinition.isRequired(), + fieldDefinition.getInputHint())) + .collect(Collectors.toList()); + + return TypeDraftBuilder + .of( + type.getKey(), + type.getName(), + type.getResourceTypeIds()) + .description(type.getDescription()) + .fieldDefinitions(newFieldDefinitions) + .build(); + }) + .collect(Collectors.toList()); + + final TypeSyncOptions typeSyncOptions = TypeSyncOptionsBuilder + .of(CTP_TARGET_CLIENT) + .build(); + + final TypeSync typeSync = new TypeSync(typeSyncOptions); + + final TypeSyncStatistics typeSyncStatistics = typeSync + .sync(typeDrafts) + .toCompletableFuture().join(); + + AssertionsForStatistics.assertThat(typeSyncStatistics).hasValues(2, 1, 0, 0); + } + + @Test + public void sync_FromSourceToTargetProjectWithUpdates_ShouldReturnProperStatistics() { + //Fetch new types from source project. Convert them to drafts. + final List types = CTP_SOURCE_CLIENT + .execute(TypeQuery.of()) + .toCompletableFuture().join().getResults(); + + final List typeDrafts = types + .stream() + .map(type -> { + List newFieldDefinitions = type + .getFieldDefinitions() + .stream() + .map(fieldDefinition -> + FieldDefinition.of( + fieldDefinition.getType(), + fieldDefinition.getName(), + fieldDefinition.getLabel(), + fieldDefinition.isRequired(), + fieldDefinition.getInputHint())) + .collect(Collectors.toList()); + + return TypeDraftBuilder + .of( + type.getKey(), + LocalizedString.ofEnglish(type.getName().get(Locale.ENGLISH) + "_updated"), + type.getResourceTypeIds()) + .description(type.getDescription()) + .fieldDefinitions(newFieldDefinitions) + .build(); + }) + .collect(Collectors.toList()); + + final TypeSyncOptions typeSyncOptions = TypeSyncOptionsBuilder + .of(CTP_TARGET_CLIENT) + .build(); + + final TypeSync typeSync = new TypeSync(typeSyncOptions); + + final TypeSyncStatistics typeSyncStatistics = typeSync + .sync(typeDrafts) + .toCompletableFuture().join(); + + // Field Definition with key "key_1" already exists in target, so it's updated + // Field Definition with key "key_2" doesn't exist in target, so it's created + AssertionsForStatistics.assertThat(typeSyncStatistics).hasValues(2, 1, 1, 0); + } + + @Test + public void sync_FromSourceToTargetProjectWithUpdates_ShouldReturnProperStatisticsMessage() { + //Fetch new types from source project. Convert them to drafts. + final List types = CTP_SOURCE_CLIENT + .execute(TypeQuery.of()) + .toCompletableFuture().join().getResults(); + + final List typeDrafts = types + .stream() + .map(type -> { + List newFieldDefinitions = type + .getFieldDefinitions() + .stream() + .map(fieldDefinition -> + FieldDefinition.of( + fieldDefinition.getType(), + fieldDefinition.getName(), + fieldDefinition.getLabel(), + fieldDefinition.isRequired(), + fieldDefinition.getInputHint())) + .collect(Collectors.toList()); + + return TypeDraftBuilder + .of( + type.getKey(), + LocalizedString.ofEnglish(type.getName().get(Locale.ENGLISH) + "_updated"), + type.getResourceTypeIds()) + .description(type.getDescription()) + .fieldDefinitions(newFieldDefinitions) + .build(); + }) + .collect(Collectors.toList()); + + final TypeSyncOptions typeSyncOptions = TypeSyncOptionsBuilder + .of(CTP_TARGET_CLIENT) + .build(); + + final TypeSync typeSync = new TypeSync(typeSyncOptions); + + final TypeSyncStatistics typeSyncStatistics = typeSync + .sync(typeDrafts) + .toCompletableFuture().join(); + + assertThat(typeSyncStatistics + .getReportMessage()) + .isEqualTo("Summary: 2 types were processed in total" + + " (1 created, 1 updated and 0 failed to sync)."); + } + + @Test + public void sync_WithoutKey_ShouldExecuteCallbackOnErrorAndIncreaseFailedCounter() { + final TypeDraft newTypeDraft = TypeDraftBuilder.of( + null, + TYPE_NAME_1, + ResourceTypeIdsSetBuilder.of().addCategories().build()) + .description(TYPE_DESCRIPTION_1) + .fieldDefinitions(asList(FIELD_DEFINITION_1, FIELD_DEFINITION_2)) + .build(); + + final TypeSyncOptions typeSyncOptions = TypeSyncOptionsBuilder + .of(CTP_TARGET_CLIENT) + .build(); + + TypeSyncOptions spyTypeSyncOptions = spy(typeSyncOptions); + + final TypeSync typeSync = new TypeSync(spyTypeSyncOptions); + + final TypeSyncStatistics typeSyncStatistics = typeSync + .sync(singletonList(newTypeDraft)) + .toCompletableFuture().join(); + + verify(spyTypeSyncOptions).applyErrorCallback("Failed to process type draft without key.", null); + + AssertionsForStatistics.assertThat(typeSyncStatistics).hasValues(1, 0, 0, 1); + } + + @Test + public void sync_WithNullDraft_ShouldExecuteCallbackOnErrorAndIncreaseFailedCounter() { + final TypeDraft newTypeDraft = null; + + final TypeSyncOptions typeSyncOptions = TypeSyncOptionsBuilder + .of(CTP_TARGET_CLIENT) + .build(); + + TypeSyncOptions spyTypeSyncOptions = spy(typeSyncOptions); + + final TypeSync typeSync = new TypeSync(spyTypeSyncOptions); + + final TypeSyncStatistics typeSyncStatistics = typeSync + .sync(singletonList(newTypeDraft)) + .toCompletableFuture().join(); + + verify(spyTypeSyncOptions).applyErrorCallback("Failed to process null type draft.", null); + + AssertionsForStatistics.assertThat(typeSyncStatistics).hasValues(1, 0, 0, 1); + } + + @Test + public void sync_WithErrorFetchingExistingKeysFromCT_ShouldExecuteCallbackOnErrorAndIncreaseFailedCounter() { + final TypeDraft newTypeDraft = TypeDraftBuilder.of( + TYPE_KEY_1, + TYPE_NAME_1, + ResourceTypeIdsSetBuilder.of().addCategories().build()) + .description(TYPE_DESCRIPTION_1) + .fieldDefinitions(asList(FIELD_DEFINITION_1, FIELD_DEFINITION_2)) + .build(); + + final TypeSyncOptions typeSyncOptions = TypeSyncOptionsBuilder + .of(CTP_TARGET_CLIENT) + .build(); + + TypeSyncOptions spyTypeSyncOptions = spy(typeSyncOptions); + + TypeService typeServiceMock = mock(TypeServiceImpl.class); + + when(typeServiceMock.fetchMatchingTypesByKeys(Mockito.any())).thenReturn(supplyAsync(() -> { + throw new SphereException(); + })); + + final TypeSync typeSync = new TypeSync(spyTypeSyncOptions, typeServiceMock); + + final TypeSyncStatistics typeSyncStatistics = typeSync + .sync(singletonList(newTypeDraft)) + .toCompletableFuture().join(); + + verify(spyTypeSyncOptions) + .applyErrorCallback(eq("Failed to fetch existing types of keys '[key_1]'."), any()); + + AssertionsForStatistics.assertThat(typeSyncStatistics).hasValues(1, 0, 0, 1); + } + + @Test + public void sync_WithErrorCreatingTheTypeInCT_ShouldExecuteCallbackOnErrorAndIncreaseFailedCounter() { + final TypeDraft newTypeDraft = TypeDraftBuilder.of( + TYPE_KEY_2, + TYPE_NAME_2, + ResourceTypeIdsSetBuilder.of().addCategories().build()) + .description(TYPE_DESCRIPTION_2) + .fieldDefinitions(singletonList(FIELD_DEFINITION_1)) + .build(); + + final TypeSyncOptions typeSyncOptions = TypeSyncOptionsBuilder + .of(CTP_TARGET_CLIENT) + .build(); + + TypeSyncOptions spyTypeSyncOptions = spy(typeSyncOptions); + + TypeService typeService = new TypeServiceImpl(spyTypeSyncOptions); + TypeService spyTypeService = spy(typeService); + + doReturn(supplyAsync(() -> { + throw new SphereException(); + })).when(spyTypeService).createType(Mockito.any()); + + final TypeSync typeSync = new TypeSync(spyTypeSyncOptions, spyTypeService); + + final TypeSyncStatistics typeSyncStatistics = typeSync + .sync(singletonList(newTypeDraft)) + .toCompletableFuture().join(); + + verify(spyTypeSyncOptions) + .applyErrorCallback(eq("Failed to create type of key 'key_2'."), any()); + + AssertionsForStatistics.assertThat(typeSyncStatistics).hasValues(1, 0, 0, 1); + } + + @Test + public void sync_WithErrorUpdatingTheProductTypeInCT_ShouldExecuteCallbackOnErrorAndIncreaseFailedCounter() { + final TypeDraft newTypeDraft = TypeDraftBuilder.of( + TYPE_KEY_1, + TYPE_NAME_1, + ResourceTypeIdsSetBuilder.of().addCategories().build()) + .description(TYPE_DESCRIPTION_1) + .fieldDefinitions(singletonList(FIELD_DEFINITION_1)) + .build(); + + final TypeSyncOptions typeSyncOptions = TypeSyncOptionsBuilder + .of(CTP_TARGET_CLIENT) + .build(); + + TypeSyncOptions spyTypeSyncOptions = spy(typeSyncOptions); + + TypeService typeService = new TypeServiceImpl(spyTypeSyncOptions); + TypeService spyTypeService = spy(typeService); + + doReturn(supplyAsync(() -> { + throw new SphereException(); + })).when(spyTypeService).updateType(Mockito.any(), Mockito.anyList()); + + final TypeSync typeSync = new TypeSync(spyTypeSyncOptions, spyTypeService); + + final TypeSyncStatistics typeSyncStatistics = typeSync + .sync(singletonList(newTypeDraft)) + .toCompletableFuture().join(); + + verify(spyTypeSyncOptions) + .applyErrorCallback(eq("Failed to update type of key 'key_1'."), any()); + + AssertionsForStatistics.assertThat(typeSyncStatistics).hasValues(1, 0, 0, 1); + } + + @Test + public void sync_WithoutName_ShouldExecuteCallbackOnErrorAndIncreaseFailedCounter() { + final TypeDraft newTypeDraft = TypeDraftBuilder.of( + TYPE_KEY_1, + null, + ResourceTypeIdsSetBuilder.of().addCategories().build()) + .description(TYPE_DESCRIPTION_1) + .fieldDefinitions(asList(FIELD_DEFINITION_1, FIELD_DEFINITION_2)) + .build(); + + final TypeSyncOptions typeSyncOptions = TypeSyncOptionsBuilder + .of(CTP_TARGET_CLIENT) + .build(); + + TypeSyncOptions spyTypeSyncOptions = spy(typeSyncOptions); + + final TypeSync typeSync = new TypeSync(spyTypeSyncOptions); + + final TypeSyncStatistics typeSyncStatistics = typeSync + .sync(singletonList(newTypeDraft)) + .toCompletableFuture().join(); + + // Since the error message and exception is coming from commercetools, we don't test the actual message and + // exception + verify(spyTypeSyncOptions).applyErrorCallback(any(), any()); + + AssertionsForStatistics.assertThat(typeSyncStatistics).hasValues(1, 0, 0, 1); + } + + @Test + public void sync_WithoutFieldDefinitionType_ShouldExecuteCallbackOnErrorAndIncreaseFailedCounter() { + final FieldDefinition fieldDefinition = FieldDefinition.of( + null, + FIELD_DEFINITION_NAME_1, + FIELD_DEFINITION_LABEL_1, + true, + TextInputHint.SINGLE_LINE); + + final TypeDraft newTypeDraft = TypeDraftBuilder.of( + TYPE_KEY_1, + null, + ResourceTypeIdsSetBuilder.of().addCategories().build()) + .description(TYPE_DESCRIPTION_1) + .fieldDefinitions(singletonList(fieldDefinition)) + .build(); + + final TypeSyncOptions typeSyncOptions = TypeSyncOptionsBuilder + .of(CTP_TARGET_CLIENT) + .build(); + + TypeSyncOptions spyTypeSyncOptions = spy(typeSyncOptions); + + final TypeSync typeSync = new TypeSync(spyTypeSyncOptions); + + final TypeSyncStatistics typeSyncStatistics = typeSync + .sync(singletonList(newTypeDraft)) + .toCompletableFuture().join(); + + // Since the error message and exception is coming from commercetools, we don't test the actual message and + // exception + verify(spyTypeSyncOptions).applyErrorCallback(any(), any()); + + AssertionsForStatistics.assertThat(typeSyncStatistics).hasValues(1, 0, 0, 1); + } + + @Test + public void sync_WithSeveralBatches_ShouldReturnProperStatistics() { + // Default batch size is 50 (check TypeSyncOptionsBuilder) so we have 2 batches of 50 + final List typeDrafts = IntStream + .range(0, 100) + .mapToObj(i -> TypeDraftBuilder.of( + "key__" + Integer.toString(i), + LocalizedString.ofEnglish("name__"+ Integer.toString(i)), + ResourceTypeIdsSetBuilder.of().addCategories().build()) + .description(LocalizedString.ofEnglish("description__"+ Integer.toString(i))) + .fieldDefinitions(singletonList(FIELD_DEFINITION_1)) + .build()) + .collect(Collectors.toList()); + + final TypeSyncOptions typeSyncOptions = TypeSyncOptionsBuilder + .of(CTP_TARGET_CLIENT) + .build(); + + final TypeSync typeSync = new TypeSync(typeSyncOptions); + + final TypeSyncStatistics typeSyncStatistics = typeSync + .sync(typeDrafts) + .toCompletableFuture().join(); + + AssertionsForStatistics.assertThat(typeSyncStatistics).hasValues(100, 100, 0, 0); + } + + @Test + public void sync_beforeCreate_ShouldCallBeforeCreateCallback() { + final TypeDraft newTypeDraft = TypeDraftBuilder.of( + TYPE_KEY_2, + TYPE_NAME_2, + ResourceTypeIdsSetBuilder.of().addCategories().build()) + .description(TYPE_DESCRIPTION_2) + .fieldDefinitions(singletonList(FIELD_DEFINITION_1)) + .build(); + + final TypeSyncOptions typeSyncOptions = TypeSyncOptionsBuilder + .of(CTP_TARGET_CLIENT) + .build(); + + TypeSyncOptions spyTypeSyncOptions = spy(typeSyncOptions); + + final TypeSync typeSync = new TypeSync(spyTypeSyncOptions); + + typeSync.sync(singletonList(newTypeDraft)).toCompletableFuture().join(); + + verify(spyTypeSyncOptions).applyBeforeCreateCallBack(newTypeDraft); + } + + @Test + public void sync_beforeCreate_ShouldNotCallBeforeUpdateCallback() { + final TypeDraft newTypeDraft = TypeDraftBuilder.of( + TYPE_KEY_2, + TYPE_NAME_2, + ResourceTypeIdsSetBuilder.of().addCategories().build()) + .description(TYPE_DESCRIPTION_2) + .fieldDefinitions(singletonList(FIELD_DEFINITION_1)) + .build(); + + final TypeSyncOptions typeSyncOptions = TypeSyncOptionsBuilder + .of(CTP_TARGET_CLIENT) + .build(); + + TypeSyncOptions spyTypeSyncOptions = spy(typeSyncOptions); + + final TypeSync typeSync = new TypeSync(spyTypeSyncOptions); + + typeSync.sync(singletonList(newTypeDraft)).toCompletableFuture().join(); + + verify(spyTypeSyncOptions, never()).applyBeforeUpdateCallBack(any(), any(), any()); + } + + @Test + public void sync_beforeUpdate_ShouldCallBeforeUpdateCallback() { + final TypeDraft newTypeDraft = TypeDraftBuilder.of( + TYPE_KEY_1, + TYPE_NAME_2, + ResourceTypeIdsSetBuilder.of().addCategories().build()) + .description(TYPE_DESCRIPTION_2) + .fieldDefinitions(singletonList(FIELD_DEFINITION_1)) + .build(); + + final TypeSyncOptions typeSyncOptions = TypeSyncOptionsBuilder + .of(CTP_TARGET_CLIENT) + .build(); + + TypeSyncOptions spyTypeSyncOptions = spy(typeSyncOptions); + + final TypeSync typeSync = new TypeSync(spyTypeSyncOptions); + + typeSync.sync(singletonList(newTypeDraft)).toCompletableFuture().join(); + + verify(spyTypeSyncOptions).applyBeforeUpdateCallBack(any(), any(), any()); + } + + @Test + public void sync_beforeUpdate_ShouldNotCallBeforeCreateCallback() { + final TypeDraft newTypeDraft = TypeDraftBuilder.of( + TYPE_KEY_1, + TYPE_NAME_2, + ResourceTypeIdsSetBuilder.of().addCategories().build()) + .description(TYPE_DESCRIPTION_2) + .fieldDefinitions(singletonList(FIELD_DEFINITION_1)) + .build(); + + final TypeSyncOptions typeSyncOptions = TypeSyncOptionsBuilder + .of(CTP_TARGET_CLIENT) + .build(); + + TypeSyncOptions spyTypeSyncOptions = spy(typeSyncOptions); + + final TypeSync typeSync = new TypeSync(spyTypeSyncOptions); + + typeSync.sync(singletonList(newTypeDraft)).toCompletableFuture().join(); + + verify(spyTypeSyncOptions, never()).applyBeforeCreateCallBack(newTypeDraft); + } + + private static void assertFieldDefinitionsAreEqual(@Nonnull final List oldFields, + @Nonnull final List newFields) { + + IntStream.range(0, newFields.size()) + .forEach(index -> { + final FieldDefinition oldFieldDefinition = oldFields.get(index); + final FieldDefinition newFieldDefinition = newFields.get(index); + + assertThat(oldFieldDefinition.getName()).isEqualTo(newFieldDefinition.getName()); + assertThat(oldFieldDefinition.getLabel()).isEqualTo(newFieldDefinition.getLabel()); + assertThat(oldFieldDefinition.getType()).isEqualTo(newFieldDefinition.getType()); + assertThat(oldFieldDefinition.getInputHint()).isEqualTo(newFieldDefinition.getInputHint()); + assertThat(oldFieldDefinition.isRequired()).isEqualTo(newFieldDefinition.isRequired()); + + if (oldFieldDefinition.getType().getClass() == EnumFieldType.class) { + assertPlainEnumsValuesAreEqual( + ((EnumFieldType) oldFieldDefinition.getType()).getValues(), + ((EnumFieldType) newFieldDefinition.getType()).getValues() + ); + } else if (oldFieldDefinition.getType().getClass() == LocalizedEnumFieldType.class) { + assertLocalizedEnumsValuesAreEqual( + ((LocalizedEnumFieldType) oldFieldDefinition.getType()).getValues(), + ((LocalizedEnumFieldType) newFieldDefinition.getType()).getValues() + ); + } + }); + } + + private static void assertPlainEnumsValuesAreEqual(@Nonnull final List enumValues, + @Nonnull final List enumValuesDrafts) { + + IntStream.range(0, enumValuesDrafts.size()) + .forEach(index -> { + final EnumValue enumValue = enumValues.get(index); + final EnumValue enumValueDraft = enumValuesDrafts.get(index); + + assertThat(enumValue.getKey()).isEqualTo(enumValueDraft.getKey()); + assertThat(enumValue.getLabel()).isEqualTo(enumValueDraft.getLabel()); + }); + } + + private static void assertLocalizedEnumsValuesAreEqual(@Nonnull final List enumValues, + @Nonnull final List enumValuesDrafts) { + + IntStream.range(0, enumValuesDrafts.size()) + .forEach(index -> { + final LocalizedEnumValue enumValue = enumValues.get(index); + final LocalizedEnumValue enumValueDraft = enumValuesDrafts.get(index); + + assertThat(enumValue.getKey()).isEqualTo(enumValueDraft.getKey()); + assertThat(enumValue.getLabel()).isEqualTo(enumValueDraft.getLabel()); + }); + } + +} diff --git a/src/integration-test/java/com/commercetools/sync/integration/types/utils/TypeITUtils.java b/src/integration-test/java/com/commercetools/sync/integration/types/utils/TypeITUtils.java new file mode 100644 index 0000000000..e55713c5e7 --- /dev/null +++ b/src/integration-test/java/com/commercetools/sync/integration/types/utils/TypeITUtils.java @@ -0,0 +1,148 @@ +package com.commercetools.sync.integration.types.utils; + +import io.sphere.sdk.client.SphereClient; +import io.sphere.sdk.models.LocalizedString; +import io.sphere.sdk.models.TextInputHint; +import io.sphere.sdk.types.FieldDefinition; +import io.sphere.sdk.types.ResourceTypeIdsSetBuilder; +import io.sphere.sdk.types.StringFieldType; +import io.sphere.sdk.types.Type; +import io.sphere.sdk.types.TypeDraft; +import io.sphere.sdk.types.TypeDraftBuilder; +import io.sphere.sdk.types.commands.TypeCreateCommand; +import io.sphere.sdk.types.commands.TypeDeleteCommand; +import io.sphere.sdk.types.queries.TypeQuery; +import io.sphere.sdk.types.queries.TypeQueryBuilder; + +import javax.annotation.Nonnull; +import java.util.Arrays; +import java.util.Optional; + +import static com.commercetools.sync.integration.commons.utils.ITUtils.queryAndExecute; +import static com.commercetools.sync.integration.commons.utils.SphereClientUtils.CTP_SOURCE_CLIENT; +import static com.commercetools.sync.integration.commons.utils.SphereClientUtils.CTP_TARGET_CLIENT; +import static java.util.Collections.singletonList; + +public final class TypeITUtils { + public static final String TYPE_KEY_1 = "key_1"; + public static final String TYPE_KEY_2 = "key_2"; + + public static final LocalizedString TYPE_NAME_1 = LocalizedString.ofEnglish("name_1"); + public static final LocalizedString TYPE_NAME_2 = LocalizedString.ofEnglish("name_2"); + + public static final String FIELD_DEFINITION_NAME_1 = "field_1"; + public static final String FIELD_DEFINITION_NAME_2 = "field_2"; + public static final String FIELD_DEFINITION_NAME_3 = "field_2"; + + + public static final LocalizedString FIELD_DEFINITION_LABEL_1 = LocalizedString.ofEnglish("label_1"); + public static final LocalizedString FIELD_DEFINITION_LABEL_2 = LocalizedString.ofEnglish("label_2"); + public static final LocalizedString FIELD_DEFINITION_LABEL_3 = LocalizedString.ofEnglish("label_3"); + + public static final LocalizedString TYPE_DESCRIPTION_1 = LocalizedString.ofEnglish("description_1"); + public static final LocalizedString TYPE_DESCRIPTION_2 = LocalizedString.ofEnglish("description_2"); + + public static final FieldDefinition FIELD_DEFINITION_1 = FieldDefinition.of( + StringFieldType.of(), + FIELD_DEFINITION_NAME_1, + FIELD_DEFINITION_LABEL_1, + true, + TextInputHint.SINGLE_LINE); + + public static final FieldDefinition FIELD_DEFINITION_2 = FieldDefinition.of( + StringFieldType.of(), + FIELD_DEFINITION_NAME_2, + FIELD_DEFINITION_LABEL_2, + true, + TextInputHint.SINGLE_LINE); + + public static final FieldDefinition FIELD_DEFINITION_3 = FieldDefinition.of( + StringFieldType.of(), + FIELD_DEFINITION_NAME_3, + FIELD_DEFINITION_LABEL_3, + true, + TextInputHint.SINGLE_LINE); + + + + public static final TypeDraft typeDraft1 = TypeDraftBuilder.of( + TYPE_KEY_1, + TYPE_NAME_1, + ResourceTypeIdsSetBuilder.of().addCategories().build()) + .description(TYPE_DESCRIPTION_1) + .fieldDefinitions(Arrays.asList(FIELD_DEFINITION_1, FIELD_DEFINITION_2)) + .build(); + + public static final TypeDraft typeDraft2 = TypeDraftBuilder.of( + TYPE_KEY_2, + TYPE_NAME_2, + ResourceTypeIdsSetBuilder.of().addCategories().build()) + .description(TYPE_DESCRIPTION_2) + .fieldDefinitions(singletonList(FIELD_DEFINITION_2)) + .build(); + + + + /** + * Deletes all product types from CTP project, represented by provided {@code ctpClient}. + * + * @param ctpClient represents the CTP project the types will be deleted from. + */ + public static void deleteTypes(@Nonnull final SphereClient ctpClient) { + queryAndExecute(ctpClient, TypeQuery.of(), TypeDeleteCommand::of); + } + + + /** + * Deletes all product types from CTP projects defined by {@code CTP_SOURCE_CLIENT} and + * {@code CTP_TARGET_CLIENT}. + */ + public static void deleteTypesFromTargetAndSource() { + deleteTypes(CTP_SOURCE_CLIENT); + deleteTypes(CTP_TARGET_CLIENT); + } + + /** + * Populate source CTP project. + * Creates product type with key TYPE_KEY_1, TYPE_NAME_1, TYPE_DESCRIPTION_1 and + * attributes attributeDefinitionDraft1, attributeDefinitionDraft2. + * Creates product type with key TYPE_KEY_2, TYPE_NAME_2, TYPE_DESCRIPTION_2 and + * attributes attributeDefinitionDraft1. + */ + public static void populateSourceProject() { + CTP_SOURCE_CLIENT.execute(TypeCreateCommand.of(typeDraft1)).toCompletableFuture().join(); + CTP_SOURCE_CLIENT.execute(TypeCreateCommand.of(typeDraft2)).toCompletableFuture().join(); + } + + /** + * Populate source CTP project. + * Creates product type with key TYPE_KEY_1, TYPE_NAME_1, TYPE_DESCRIPTION_1 and + * attributes attributeDefinitionDraft1, attributeDefinitionDraft2. + */ + public static void populateTargetProject() { + CTP_TARGET_CLIENT.execute(TypeCreateCommand.of(typeDraft1)).toCompletableFuture().join(); + } + + /** + * Tries to fetch type of {@code key} using {@code sphereClient}. + * + * @param sphereClient sphere client used to execute requests. + * @param key key of requested type. + * @return {@link Optional} which may contain type of {@code key}. + */ + public static Optional getTypeByKey( + @Nonnull final SphereClient sphereClient, + @Nonnull final String key) { + + final TypeQuery query = TypeQueryBuilder + .of() + .plusPredicates(queryModel -> queryModel.key().is(key)) + .build(); + + return sphereClient.execute(query).toCompletableFuture().join().head(); + } + + private TypeITUtils() { + + } +} diff --git a/src/test/java/com/commercetools/sync/commons/asserts/statistics/AssertionsForStatistics.java b/src/test/java/com/commercetools/sync/commons/asserts/statistics/AssertionsForStatistics.java index 58b577ef0e..c32eb8a6bd 100644 --- a/src/test/java/com/commercetools/sync/commons/asserts/statistics/AssertionsForStatistics.java +++ b/src/test/java/com/commercetools/sync/commons/asserts/statistics/AssertionsForStatistics.java @@ -4,6 +4,7 @@ import com.commercetools.sync.inventories.helpers.InventorySyncStatistics; import com.commercetools.sync.products.helpers.ProductSyncStatistics; import com.commercetools.sync.producttypes.helpers.ProductTypeSyncStatistics; +import com.commercetools.sync.types.helpers.TypeSyncStatistics; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -55,4 +56,15 @@ public static InventorySyncStatisticsAssert assertThat(@Nullable final Inventory public static ProductTypeSyncStatisticsAssert assertThat(@Nullable final ProductTypeSyncStatistics statistics) { return new ProductTypeSyncStatisticsAssert(statistics); } + + /** + * Create assertion for {@link TypeSyncStatistics}. + * + * @param statistics the actual value. + * @return the created assertion object. + */ + @Nonnull + public static TypeSyncStatisticsAssert assertThat(@Nullable final TypeSyncStatistics statistics) { + return new TypeSyncStatisticsAssert(statistics); + } } diff --git a/src/test/java/com/commercetools/sync/commons/asserts/statistics/TypeSyncStatisticsAssert.java b/src/test/java/com/commercetools/sync/commons/asserts/statistics/TypeSyncStatisticsAssert.java new file mode 100644 index 0000000000..1b607497a5 --- /dev/null +++ b/src/test/java/com/commercetools/sync/commons/asserts/statistics/TypeSyncStatisticsAssert.java @@ -0,0 +1,13 @@ +package com.commercetools.sync.commons.asserts.statistics; + +import com.commercetools.sync.types.helpers.TypeSyncStatistics; + +import javax.annotation.Nullable; + +public final class TypeSyncStatisticsAssert extends + AbstractSyncStatisticsAssert { + + TypeSyncStatisticsAssert(@Nullable final TypeSyncStatistics actual) { + super(actual, TypeSyncStatisticsAssert.class); + } +} From 7b129fd706e0fc0c967fe73bcef839de97e5e267 Mon Sep 17 00:00:00 2001 From: aoz Date: Wed, 19 Sep 2018 17:14:54 +0200 Subject: [PATCH 004/164] #300: add unit and integration tests for the type sync feature. --- .../sync/integration/types/TypeSyncIT.java | 15 +- .../integration/types/utils/TypeITUtils.java | 22 +- .../commercetools/sync/types/TypeSync.java | 26 +- .../sync/types/TypeSyncOptionsBuilder.java | 2 +- .../FieldDefinitionUpdateActionUtils.java | 4 +- .../sync/types/utils/TypeSyncUtils.java | 13 +- .../types/utils/TypeUpdateActionUtils.java | 6 +- .../utils/TypeUpdateEnumActionsUtils.java | 55 +-- .../TypeUpdateFieldDefinitionActionUtils.java | 12 +- .../TypeUpdateLocalizedEnumActionUtils.java | 14 +- .../utils/TypeUpdatePlainEnumActionUtils.java | 2 +- .../sync/services/impl/TypeServiceTest.java | 100 ----- .../sync/types/FieldDefinitionTestHelper.java | 12 + .../FieldDefinitionUpdateActionUtilsTest.java | 7 +- .../utils/TypeUpdateActionUtilsTest.java | 12 +- ...BuildFieldDefinitionUpdateActionsTest.java | 419 ++++++++++++++++++ .../BuildLocalizedEnumUpdateActionsTest.java | 205 +++++++++ .../BuildPlainEnumUpdateActionsTest.java | 202 +++++++++ .../type-with-field-definitions-ab.json | 37 ++ .../type-with-field-definitions-abb.json | 47 ++ ...th-field-definitions-abc-with-changes.json | 47 ++ ...d-definitions-abc-with-different-type.json | 47 ++ .../type-with-field-definitions-abc.json | 47 ++ .../type-with-field-definitions-abcd.json | 58 +++ .../type-with-field-definitions-abd.json | 47 ++ .../type-with-field-definitions-acbd.json | 58 +++ .../type-with-field-definitions-adbc.json | 58 +++ .../type-with-field-definitions-cab.json | 47 ++ .../type-with-field-definitions-cb.json | 36 ++ .../type-with-field-definitions-cbd.json | 47 ++ 30 files changed, 1490 insertions(+), 214 deletions(-) delete mode 100644 src/test/java/com/commercetools/sync/services/impl/TypeServiceTest.java create mode 100644 src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildFieldDefinitionUpdateActionsTest.java create mode 100644 src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildLocalizedEnumUpdateActionsTest.java create mode 100644 src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildPlainEnumUpdateActionsTest.java create mode 100644 src/test/resources/com/commercetools/sync/types/utils/updatefielddefinitions/fields/type-with-field-definitions-ab.json create mode 100644 src/test/resources/com/commercetools/sync/types/utils/updatefielddefinitions/fields/type-with-field-definitions-abb.json create mode 100644 src/test/resources/com/commercetools/sync/types/utils/updatefielddefinitions/fields/type-with-field-definitions-abc-with-changes.json create mode 100644 src/test/resources/com/commercetools/sync/types/utils/updatefielddefinitions/fields/type-with-field-definitions-abc-with-different-type.json create mode 100644 src/test/resources/com/commercetools/sync/types/utils/updatefielddefinitions/fields/type-with-field-definitions-abc.json create mode 100644 src/test/resources/com/commercetools/sync/types/utils/updatefielddefinitions/fields/type-with-field-definitions-abcd.json create mode 100644 src/test/resources/com/commercetools/sync/types/utils/updatefielddefinitions/fields/type-with-field-definitions-abd.json create mode 100644 src/test/resources/com/commercetools/sync/types/utils/updatefielddefinitions/fields/type-with-field-definitions-acbd.json create mode 100644 src/test/resources/com/commercetools/sync/types/utils/updatefielddefinitions/fields/type-with-field-definitions-adbc.json create mode 100644 src/test/resources/com/commercetools/sync/types/utils/updatefielddefinitions/fields/type-with-field-definitions-cab.json create mode 100644 src/test/resources/com/commercetools/sync/types/utils/updatefielddefinitions/fields/type-with-field-definitions-cb.json create mode 100644 src/test/resources/com/commercetools/sync/types/utils/updatefielddefinitions/fields/type-with-field-definitions-cbd.json diff --git a/src/integration-test/java/com/commercetools/sync/integration/types/TypeSyncIT.java b/src/integration-test/java/com/commercetools/sync/integration/types/TypeSyncIT.java index 7c47c1597a..6d0f994cd9 100644 --- a/src/integration-test/java/com/commercetools/sync/integration/types/TypeSyncIT.java +++ b/src/integration-test/java/com/commercetools/sync/integration/types/TypeSyncIT.java @@ -128,7 +128,7 @@ public void sync_WithUpdatedType_ShouldUpdateType() { @Test public void sync_WithNewType_ShouldCreateType() { final Optional oldTypeBefore = getTypeByKey(CTP_TARGET_CLIENT, TYPE_KEY_2); - assertThat(oldTypeBefore).isNotEmpty(); + assertThat(oldTypeBefore).isEmpty(); final TypeDraft newTypeDraft = TypeDraftBuilder.of( TYPE_KEY_2, @@ -187,7 +187,7 @@ public void sync_WithUpdatedType_WithNewFieldDefinitions_ShouldUpdateTypeAddingF AssertionsForStatistics.assertThat(typeSyncStatistics).hasValues(1, 0, 1, 0); - final Optional oldTypeAfter = getTypeByKey(CTP_TARGET_CLIENT, TYPE_KEY_2); + final Optional oldTypeAfter = getTypeByKey(CTP_TARGET_CLIENT, TYPE_KEY_1); assertThat(oldTypeAfter).isNotEmpty(); assertFieldDefinitionsAreEqual(oldTypeAfter.get().getFieldDefinitions(), asList( @@ -198,7 +198,7 @@ public void sync_WithUpdatedType_WithNewFieldDefinitions_ShouldUpdateTypeAddingF } @Test - public void sync_WithUpdatedType_WithoutOldFieldDefinition_ShouldUpdateTypeRemovingAttribute() { + public void sync_WithUpdatedType_WithoutOldFieldDefinition_ShouldUpdateTypeRemovingFieldDefinition() { final Optional oldTypeBefore = getTypeByKey(CTP_TARGET_CLIENT, TYPE_KEY_1); assertThat(oldTypeBefore).isNotEmpty(); @@ -561,7 +561,7 @@ public void sync_WithErrorCreatingTheTypeInCT_ShouldExecuteCallbackOnErrorAndInc } @Test - public void sync_WithErrorUpdatingTheProductTypeInCT_ShouldExecuteCallbackOnErrorAndIncreaseFailedCounter() { + public void sync_WithErrorUpdatingTheTypeInCT_ShouldExecuteCallbackOnErrorAndIncreaseFailedCounter() { final TypeDraft newTypeDraft = TypeDraftBuilder.of( TYPE_KEY_1, TYPE_NAME_1, @@ -667,9 +667,9 @@ public void sync_WithSeveralBatches_ShouldReturnProperStatistics() { .range(0, 100) .mapToObj(i -> TypeDraftBuilder.of( "key__" + Integer.toString(i), - LocalizedString.ofEnglish("name__"+ Integer.toString(i)), + LocalizedString.ofEnglish("name__" + Integer.toString(i)), ResourceTypeIdsSetBuilder.of().addCategories().build()) - .description(LocalizedString.ofEnglish("description__"+ Integer.toString(i))) + .description(LocalizedString.ofEnglish("description__" + Integer.toString(i))) .fieldDefinitions(singletonList(FIELD_DEFINITION_1)) .build()) .collect(Collectors.toList()); @@ -790,7 +790,8 @@ private static void assertFieldDefinitionsAreEqual(@Nonnull final List syncBatches( /** * Fetches existing {@link Type} objects from CTP project that correspond to passed {@code batch}. - * Having existing product types fetched, {@code batch} is compared and synced with fetched objects by + * Having existing types fetched, {@code batch} is compared and synced with fetched objects by * {@link TypeSync#syncBatch(List, List)} function. When fetching existing types results in * an empty optional then {@code batch} isn't processed. * @@ -161,19 +160,19 @@ private CompletionStage> fetchExistingTypes(@Nonnull final Set a type that extends of {@link WithKey}. * @return the map of keys to {@link Type}/{@link TypeDraft} instances. */ - private Map getKeysProductTypeMap(@Nonnull final List types) { + private Map getKeysTypeMap(@Nonnull final List types) { return types.stream().collect(Collectors.toMap(WithKey::getKey, p -> p, - (productTypeA, productTypeB) -> productTypeB)); + (typeA, typeB) -> typeB)); } /** * Given a {@link String} {@code errorMessage} and a {@link Throwable} {@code exception}, this method calls the * optional error callback specified in the {@code syncOptions} and updates the {@code statistics} instance by - * incrementing the total number of failed product types to sync. + * incrementing the total number of failed types to sync. * * @param errorMessage The error message describing the reason(s) of failure. * @param exception The exception that called caused the failure, if any. - * @param failedTimes The number of times that the failed product types counter is incremented. + * @param failedTimes The number of times that the failed types counter is incremented. */ private void handleError(@Nonnull final String errorMessage, @Nullable final Throwable exception, final int failedTimes) { @@ -193,7 +192,7 @@ private void handleError(@Nonnull final String errorMessage, @Nullable final Thr private CompletionStage syncBatch( @Nonnull final List oldTypes, @Nonnull final List newTypes) { - final Map oldTypeMap = getKeysProductTypeMap(oldTypes); + final Map oldTypeMap = getKeysTypeMap(oldTypes); return CompletableFuture.allOf(newTypes .stream() @@ -201,7 +200,7 @@ private CompletionStage syncBatch( final Type oldType = oldTypeMap.get(newType.getKey()); return ofNullable(oldType) - .map(type -> updateProductType(oldType, newType)) + .map(type -> updateType(oldType, newType)) .orElseGet(() -> createType(newType)); }) .map(CompletionStage::toCompletableFuture) @@ -215,7 +214,7 @@ private CompletionStage syncBatch( * out successfully or not. If an exception was thrown on executing the request to CTP, the error handling method * is called. * - * @param typeDraft the type draft to create the product type from. + * @param typeDraft the type draft to create the type from. * @return a future which contains an empty result after execution of the create. */ private CompletionStage createType(@Nonnull final TypeDraft typeDraft) { @@ -235,8 +234,8 @@ private CompletionStage createType(@Nonnull final TypeDraft typeDraft) { /** * Given an existing {@link Type} and a new {@link TypeDraft}, the method calculates all the - * update actions required to synchronize the existing product type to be the same as the new one. If there are - * update actions found, a request is made to CTP to update the existing product type, otherwise it doesn't issue a + * update actions required to synchronize the existing type to be the same as the new one. If there are + * update actions found, a request is made to CTP to update the existing type, otherwise it doesn't issue a * request. * *

The {@code statistics} instance is updated accordingly to whether the CTP request was carried @@ -247,8 +246,7 @@ private CompletionStage createType(@Nonnull final TypeDraft typeDraft) { * @param newType draft containing data that could differ from data in {@code oldType}. * @return a future which contains an empty result after execution of the update. */ - @SuppressFBWarnings("NP_NONNULL_PARAM_VIOLATION") // https://github.com/findbugsproject/findbugs/issues/79 - private CompletionStage updateProductType(@Nonnull final Type oldType, + private CompletionStage updateType(@Nonnull final Type oldType, @Nonnull final TypeDraft newType) { final List> updateActions = buildActions(oldType, newType, syncOptions); @@ -261,7 +259,7 @@ private CompletionStage updateProductType(@Nonnull final Type oldType, if (!updateActionsAfterCallback.isEmpty()) { return typeService.updateType(oldType, updateActionsAfterCallback) - .thenAccept(updatedProductType -> statistics.incrementUpdated()) + .thenAccept(updatedType -> statistics.incrementUpdated()) .exceptionally(exception -> { final String errorMessage = format(CTP_TYPE_UPDATE_FAILED, newType.getKey()); handleError(errorMessage, exception, 1); diff --git a/src/main/java/com/commercetools/sync/types/TypeSyncOptionsBuilder.java b/src/main/java/com/commercetools/sync/types/TypeSyncOptionsBuilder.java index 8c1dfaff93..9c3f0c6e83 100644 --- a/src/main/java/com/commercetools/sync/types/TypeSyncOptionsBuilder.java +++ b/src/main/java/com/commercetools/sync/types/TypeSyncOptionsBuilder.java @@ -28,7 +28,7 @@ public static TypeSyncOptionsBuilder of(@Nonnull final SphereClient ctpClient) { } /** - * Creates new instance of {@link TypeSyncOptions} enriched with all attributes provided to {@code this} + * Creates new instance of {@link TypeSyncOptions} enriched with all fields provided to {@code this} * builder. * * @return new instance of {@link TypeSyncOptions} diff --git a/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionUpdateActionUtils.java b/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionUpdateActionUtils.java index 9ac9617786..eff5458304 100644 --- a/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionUpdateActionUtils.java +++ b/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionUpdateActionUtils.java @@ -53,7 +53,7 @@ public static List> buildActions( ((EnumFieldType) oldFieldDefinition.getType()).getValues(), ((EnumFieldType) newFieldDefinition.getType()).getValues() )); - } else if (isLocalizedEnumAttribute(oldFieldDefinition)) { + } else if (isLocalizedEnumField(oldFieldDefinition)) { updateActions.addAll(buildLocalizedEnumValuesUpdateActions( oldFieldDefinition.getName(), ((LocalizedEnumFieldType) oldFieldDefinition.getType()).getValues(), @@ -81,7 +81,7 @@ private static boolean isPlainEnumField(@Nonnull final FieldDefinition fieldDefi * @param fieldDefinition the field definition. * @return true if the field definition is a localized enum value, false otherwise. */ - private static boolean isLocalizedEnumAttribute(@Nonnull final FieldDefinition fieldDefinition) { + private static boolean isLocalizedEnumField(@Nonnull final FieldDefinition fieldDefinition) { return fieldDefinition.getType().getClass() == LocalizedEnumFieldType.class; } diff --git a/src/main/java/com/commercetools/sync/types/utils/TypeSyncUtils.java b/src/main/java/com/commercetools/sync/types/utils/TypeSyncUtils.java index 29d5cce4bc..43e5385a3c 100644 --- a/src/main/java/com/commercetools/sync/types/utils/TypeSyncUtils.java +++ b/src/main/java/com/commercetools/sync/types/utils/TypeSyncUtils.java @@ -10,15 +10,18 @@ import java.util.Optional; import java.util.stream.Stream; -import static com.commercetools.sync.types.utils.TypeUpdateActionUtils.*; +import static com.commercetools.sync.types.utils.TypeUpdateActionUtils.buildChangeNameAction; +import static com.commercetools.sync.types.utils.TypeUpdateActionUtils.buildFieldDefinitionUpdateActions; +import static com.commercetools.sync.types.utils.TypeUpdateActionUtils.buildSetDescriptionUpdateAction; import static java.util.stream.Collectors.toList; public final class TypeSyncUtils { /** * Compares all the fields (including the attributes see - * {@link TypeUpdateActionUtils#buildFieldUpdateActions(Type, TypeDraft, TypeSyncOptions)}) of a {@link Type} and a - * {@link TypeDraft}. It returns a {@link List} of {@link UpdateAction}<{@link Type}> as a + * {@link TypeUpdateActionUtils#buildFieldDefinitionUpdateActions(Type, TypeDraft, TypeSyncOptions)}) + * of a {@link Type} and a {@link TypeDraft}. + * It returns a {@link List} of {@link UpdateAction}<{@link Type}> as a * result. If no update action is needed, for example in case where both the {@link Type} and the * {@link TypeDraft} have the same fields, an empty {@link List} is returned. * @@ -28,7 +31,7 @@ public final class TypeSyncUtils { * the user. For example, custom callbacks to call in case of warnings or errors occurring * on the build update action process. And other options (See {@link TypeSyncOptions} * for more info. - * @return A list of productType-specific update actions. + * @return A list of type-specific update actions. */ @Nonnull public static List> buildActions( @@ -44,7 +47,7 @@ public static List> buildActions( .map(Optional::get) .collect(toList()); - updateActions.addAll(buildFieldUpdateActions(oldType, newType, syncOptions)); + updateActions.addAll(buildFieldDefinitionUpdateActions(oldType, newType, syncOptions)); return updateActions; } diff --git a/src/main/java/com/commercetools/sync/types/utils/TypeUpdateActionUtils.java b/src/main/java/com/commercetools/sync/types/utils/TypeUpdateActionUtils.java index 1614ce1d92..a7538953c8 100644 --- a/src/main/java/com/commercetools/sync/types/utils/TypeUpdateActionUtils.java +++ b/src/main/java/com/commercetools/sync/types/utils/TypeUpdateActionUtils.java @@ -60,10 +60,10 @@ public static Optional> buildSetDescriptionUpdateAction( } /** - * Compares the attributes of a {@link Type} and a {@link TypeDraft} and returns a list of + * Compares the fields of a {@link Type} and a {@link TypeDraft} and returns a list of * {@link UpdateAction}<{@link Type}> as a result. If both the {@link Type} and * the {@link TypeDraft} have identical field definitions, then no update action is needed and hence an empty - * {@link List} is returned. In case, the new product type draft has a list of field definitions in which a + * {@link List} is returned. In case, the new type draft has a list of field definitions in which a * duplicate name exists, the error callback is triggered and an empty list is returned. * * @param oldType the type which should be updated. @@ -74,7 +74,7 @@ public static Optional> buildSetDescriptionUpdateAction( * @return A list with the update actions or an empty list if the field definitions are identical. */ @Nonnull - public static List> buildFieldUpdateActions( + public static List> buildFieldDefinitionUpdateActions( @Nonnull final Type oldType, @Nonnull final TypeDraft newType, @Nonnull final TypeSyncOptions syncOptions) { diff --git a/src/main/java/com/commercetools/sync/types/utils/TypeUpdateEnumActionsUtils.java b/src/main/java/com/commercetools/sync/types/utils/TypeUpdateEnumActionsUtils.java index 1ee1f44e44..d6d46c41cd 100644 --- a/src/main/java/com/commercetools/sync/types/utils/TypeUpdateEnumActionsUtils.java +++ b/src/main/java/com/commercetools/sync/types/utils/TypeUpdateEnumActionsUtils.java @@ -4,12 +4,9 @@ import io.sphere.sdk.commands.UpdateAction; import io.sphere.sdk.models.EnumValue; import io.sphere.sdk.models.WithKey; -import io.sphere.sdk.producttypes.ProductType; -import io.sphere.sdk.producttypes.commands.updateactions.RemoveEnumValues; import io.sphere.sdk.types.Type; import javax.annotation.Nonnull; -import javax.annotation.Nullable; import java.util.List; import java.util.Map; import java.util.Optional; @@ -19,8 +16,6 @@ import static com.commercetools.sync.commons.utils.CommonTypeUpdateActionUtils.buildUpdateAction; import static java.lang.String.format; -import static java.util.Collections.emptyList; -import static java.util.Optional.*; import static java.util.stream.Collectors.toMap; public final class TypeUpdateEnumActionsUtils { @@ -29,7 +24,7 @@ public final class TypeUpdateEnumActionsUtils { * Given a list of new {@link EnumValue}s, gets a map where the keys are the enum value key, and the values * are the enum instances. * - * @param fieldDefinitionName the field definition name whose the enum values belong to. + * @param fieldDefinitionName the field definition name whose the enum values belong to. * @param enumValues the list of enum values. * @param the enum type of the elements of the list. * @return a map with the enum value key as a key of the map, and the enum @@ -45,51 +40,19 @@ public static Map getEnumValuesKeyMapWithKeyValid (enumValueA, enumValueB) -> { throw new DuplicateKeyException(format("Enum Values have duplicated keys. " + "Field definition name: '%s', Duplicated enum value: '%s'. " - + "Enum Values are expected to be unique inside their attribute definition.", + + "Enum Values are expected to be unique inside their field definition.", fieldDefinitionName, enumValueA.getKey())); } )); } - /** - * Checks if there are any old enum values which are not existing in the {@code newEnumValues}. - * If there are, then "remove" enum values update actions are built. - * Otherwise, if there are no old enum values, then an empty list is returned. - * - * @param attributeDefinitionName the attribute definition name whose enum values belong to. - * @param oldEnumValues the list of old enum values. - * @param newEnumValues the list of new enum values. - * @param the enum type of the elements of the list. - * @return a list of enum values update actions if there are old enum value - * that should be removed. - * Otherwise, if the enum values are identical, an empty optional is returned. - */ - @Nonnull - public static Optional> buildRemoveEnumValuesUpdateActions( - @Nonnull final String attributeDefinitionName, - @Nonnull final List oldEnumValues, - @Nullable final List newEnumValues) { - - final Map newEnumValuesKeyMap = getEnumValuesKeyMapWithKeyValidation( - attributeDefinitionName, - ofNullable(newEnumValues).orElse(emptyList()) - ); - - final List keysToRemove = oldEnumValues - .stream() - .map(WithKey::getKey) - .filter(oldEnumValueKey -> !newEnumValuesKeyMap.containsKey(oldEnumValueKey)) - .collect(Collectors.toList()); - - return keysToRemove.isEmpty() ? empty() : of(RemoveEnumValues.of(attributeDefinitionName, keysToRemove)); - } /** * Compares the order of a list of old enum values and a list of new enum values. If there is a change in order, * then a change of enum values order (with the new order) is built. If there are no changes in order an empty * optional is returned. * - * @param attributeDefinitionName the attribute definition name whose enum values belong to. + * @param fieldDefinitionName the field definition name whose enum values belong to. * @param oldEnumValues the list of old enum values. * @param newEnumValues the list of new enum values. * @param changeOrderEnumCallback the function that is called to apply the change in the order. @@ -99,7 +62,7 @@ public static Optional> buildRemov */ @Nonnull public static Optional> buildChangeEnumValuesOrderUpdateAction( - @Nonnull final String attributeDefinitionName, + @Nonnull final String fieldDefinitionName, @Nonnull final List oldEnumValues, @Nonnull final List newEnumValues, @Nonnull final BiFunction, UpdateAction> changeOrderEnumCallback) { @@ -126,7 +89,7 @@ public static Optional> buildChangeEnumVa return buildUpdateAction( allKeys, newKeys, - () -> changeOrderEnumCallback.apply(attributeDefinitionName, newKeys) + () -> changeOrderEnumCallback.apply(fieldDefinitionName, newKeys) ); } @@ -135,7 +98,7 @@ public static Optional> buildChangeEnumVa * If there are, then "add" enum values update actions are built. * Otherwise, if there are no new enum values, then an empty list is returned. * - * @param attributeDefinitionName the attribute definition name whose enum values belong to. + * @param fieldDefinitionName the field definition name whose enum values belong to. * @param oldEnumValues the list of olf enum values. * @param newEnumValues the list of new enum values. * @param addEnumCallback the function that is called in order to add the new enum instance @@ -145,20 +108,20 @@ public static Optional> buildChangeEnumVa */ @Nonnull public static List> buildAddEnumValuesUpdateActions( - @Nonnull final String attributeDefinitionName, + @Nonnull final String fieldDefinitionName, @Nonnull final List oldEnumValues, @Nonnull final List newEnumValues, @Nonnull final BiFunction> addEnumCallback) { final Map oldEnumValuesKeyMap = getEnumValuesKeyMapWithKeyValidation( - attributeDefinitionName, + fieldDefinitionName, oldEnumValues ); return newEnumValues .stream() .filter(newEnumValue -> !oldEnumValuesKeyMap.containsKey(newEnumValue.getKey())) - .map(newEnumValue -> addEnumCallback.apply(attributeDefinitionName, newEnumValue)) + .map(newEnumValue -> addEnumCallback.apply(fieldDefinitionName, newEnumValue)) .collect(Collectors.toList()); } diff --git a/src/main/java/com/commercetools/sync/types/utils/TypeUpdateFieldDefinitionActionUtils.java b/src/main/java/com/commercetools/sync/types/utils/TypeUpdateFieldDefinitionActionUtils.java index e717565d0d..f292f4c0a1 100644 --- a/src/main/java/com/commercetools/sync/types/utils/TypeUpdateFieldDefinitionActionUtils.java +++ b/src/main/java/com/commercetools/sync/types/utils/TypeUpdateFieldDefinitionActionUtils.java @@ -39,7 +39,7 @@ public final class TypeUpdateFieldDefinitionActionUtils { * resource. * *

If the list of new {@link FieldDefinition}s is {@code null}, then remove actions are built for - * every existing field definition in the {@code oldAttributeDefinitions} list. + * every existing field definition in the {@code oldFieldDefinitions} list. * * @param oldFieldDefinitions the old list of field definitions. * @param newFieldDefinitions the new list of field definitions. @@ -59,7 +59,7 @@ public static List> buildFieldDefinitionsUpdateActions( newFieldDefinitions ); } else { - return newFieldDefinitions + return oldFieldDefinitions .stream() .map(FieldDefinition::getName) .map(RemoveFieldDefinition::of) @@ -76,7 +76,7 @@ public static List> buildFieldDefinitionsUpdateActions( * * @param oldFieldDefinitions the old list of field definitions. * @param newFieldDefinitions the new list of field definitions drafts. - * @return a list of field definitions update actions if the list of attribute definitions is not identical. + * @return a list of field definitions update actions if the list of field definitions is not identical. * Otherwise, if the field definitions are identical, an empty list is returned. * @throws BuildUpdateActionException in case there are field definitions drafts with duplicate names. */ @@ -126,7 +126,7 @@ private static List> buildUpdateActions( * Otherwise, if the field definition still exists in the new field definition, then compare the field definition * fields (label, etc..), and add the computed actions to the list of update actions. * - *

Note: If the field type field changes, the old field definition is removed and the new attribute + *

Note: If the field type field changes, the old field definition is removed and the new field * definition is added with the new field type. * * @param oldFieldDefinitions the list of old {@link FieldDefinition}s. @@ -186,7 +186,7 @@ private static List> buildRemoveFieldDefinitionOrFieldDefinit /** * Compares the field types of the {@code fieldDefinitionA} and the {@code fieldDefinitionB} and - * returns true if both field definitions have the same attribute type, false otherwise. + * returns true if both field definitions have the same field type, false otherwise. * * @param fieldDefinitionA the first field to compare. * @param fieldDefinitionB the second field definition to compare. @@ -250,7 +250,7 @@ private static Optional> buildChangeFieldDefinitionOrderUpdat /** * Checks if there are any new field definition drafts which are not existing in the * {@code oldFieldDefinitionNameMap}. If there are, then "add" field definition update actions are built. - * Otherwise, if there are no new attribute definitions, then an empty list is returned. + * Otherwise, if there are no new field definitions, then an empty list is returned. * * @param newFieldDefinitions the list of new {@link FieldDefinition}s. * @param oldFieldDefinitionNameMap a map of names to FieldDefinition of the old list diff --git a/src/main/java/com/commercetools/sync/types/utils/TypeUpdateLocalizedEnumActionUtils.java b/src/main/java/com/commercetools/sync/types/utils/TypeUpdateLocalizedEnumActionUtils.java index b75de27ee0..57c14ceb43 100644 --- a/src/main/java/com/commercetools/sync/types/utils/TypeUpdateLocalizedEnumActionUtils.java +++ b/src/main/java/com/commercetools/sync/types/utils/TypeUpdateLocalizedEnumActionUtils.java @@ -21,7 +21,7 @@ public final class TypeUpdateLocalizedEnumActionUtils { /** * Compares a list of old {@link LocalizedEnumValue}s with a list of new {@link LocalizedEnumValue}s for a given - * attribute definition. + * field definition. * The method serves as a generic implementation for localized enum values syncing. The method takes in functions * for building the required update actions (AddLocalizedEnumValue, RemoveEnumValue, ChangeLocalizedEnumValueOrder * and 1-1 update actions on localized enum values (e.g. changeLabel) for the required resource. @@ -29,7 +29,7 @@ public final class TypeUpdateLocalizedEnumActionUtils { *

If the list of new {@link LocalizedEnumValue}s is {@code null}, then remove actions are built for * every existing localized enum value in the {@code oldEnumValues} list. * - * @param fieldDefinitionName the attribute name whose localized enum values are going to be synced. + * @param fieldDefinitionName the field name whose localized enum values are going to be synced. * @param oldEnumValues the old list of localized enum values. * @param newEnumValues the new list of localized enum values. * @return a list of localized enum values update actions if the list of localized enum values is not identical. @@ -55,12 +55,12 @@ public static List> buildLocalizedEnumValuesUpdateActions( /** * Compares a list of old {@link LocalizedEnumValue}s with a list of new {@link LocalizedEnumValue}s for a given - * attribute definition. + * field definition. * The method serves as a generic implementation for localized enum values syncing. The method takes in functions * for building the required update actions (AddEnumValue, RemoveEnumValue, ChangeEnumValueOrder and 1-1 * update actions on localized enum values (e.g. changeLabel) for the required resource. * - * @param attributeDefinitionName the attribute name whose localized enum values are going to be synced. + * @param fieldDefinitionName the attribute name whose localized enum values are going to be synced. * @param oldEnumValues the old list of localized enum values. * @param newEnumValues the new list of localized enum values. * @return a list of localized enum values update actions if the list of localized enum values is not identical. @@ -69,13 +69,13 @@ public static List> buildLocalizedEnumValuesUpdateActions( */ @Nonnull private static List> buildUpdateActions( - @Nonnull final String attributeDefinitionName, + @Nonnull final String fieldDefinitionName, @Nonnull final List oldEnumValues, @Nonnull final List newEnumValues) { final List> addEnumValuesUpdateActions = buildAddEnumValuesUpdateActions( - attributeDefinitionName, + fieldDefinitionName, oldEnumValues, newEnumValues, AddLocalizedEnumValue::of @@ -83,7 +83,7 @@ private static List> buildUpdateActions( final List> changeEnumValuesOrderUpdateActions = buildChangeEnumValuesOrderUpdateAction( - attributeDefinitionName, + fieldDefinitionName, oldEnumValues, newEnumValues, ChangeLocalizedEnumValueOrder::of diff --git a/src/main/java/com/commercetools/sync/types/utils/TypeUpdatePlainEnumActionUtils.java b/src/main/java/com/commercetools/sync/types/utils/TypeUpdatePlainEnumActionUtils.java index f191d46db8..923adcea29 100644 --- a/src/main/java/com/commercetools/sync/types/utils/TypeUpdatePlainEnumActionUtils.java +++ b/src/main/java/com/commercetools/sync/types/utils/TypeUpdatePlainEnumActionUtils.java @@ -49,7 +49,7 @@ public static List> buildEnumValuesUpdateActions( /** - * Compares a list of old {@link EnumValue}s with a list of new {@link EnumValue}s for a given attribute + * Compares a list of old {@link EnumValue}s with a list of new {@link EnumValue}s for a given field * definition. * The method serves as a implementation for plain enum values syncing. The method takes in functions * for building the required update actions (AddEnumValue, RemoveEnumValue, ChangeEnumValueOrder and 1-1 diff --git a/src/test/java/com/commercetools/sync/services/impl/TypeServiceTest.java b/src/test/java/com/commercetools/sync/services/impl/TypeServiceTest.java deleted file mode 100644 index e3493af837..0000000000 --- a/src/test/java/com/commercetools/sync/services/impl/TypeServiceTest.java +++ /dev/null @@ -1,100 +0,0 @@ -package com.commercetools.sync.services.impl; - -import com.commercetools.sync.services.TypeService; -import com.commercetools.sync.types.TypeSyncOptions; -import com.commercetools.sync.types.TypeSyncOptionsBuilder; -import io.sphere.sdk.client.BadRequestException; -import io.sphere.sdk.client.SphereClient; -import io.sphere.sdk.types.Type; -import io.sphere.sdk.types.TypeDraft; -import io.sphere.sdk.types.commands.TypeCreateCommand; -import io.sphere.sdk.utils.CompletableFutureUtils; -import org.junit.Before; -import org.junit.Test; - -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; - -import static java.util.concurrent.CompletableFuture.completedFuture; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.*; - -public class TypeServiceTest { - - private TypeService service; - private TypeSyncOptions typeSyncOptions; - private List errorMessages; - private List errorExceptions; - - @Before - public void setUp() { - errorMessages = new ArrayList<>(); - errorExceptions = new ArrayList<>(); - typeSyncOptions = TypeSyncOptionsBuilder.of(mock(SphereClient.class)) - .errorCallback((errorMessage, errorException) -> { - errorMessages.add(errorMessage); - errorExceptions.add(errorException); - }) - .build(); - service = new TypeServiceImpl(typeSyncOptions); - } - - - @Test - public void createType_WithSuccessfulMockCtpResponse_ShouldReturnMock() { - final Type mock = mock(Type.class); - when(mock.getId()).thenReturn("typeId"); - - when(typeSyncOptions.getCtpClient().execute(any())) - .thenReturn(completedFuture(mock)); - - final TypeDraft draft = mock(TypeDraft.class); - when(draft.getKey()).thenReturn("typeKey"); - - final Optional typeOptional = Optional.ofNullable(service.createType(draft) - .toCompletableFuture().join()); - - assertThat(typeOptional).isNotEmpty(); - assertThat(typeOptional).containsSame(mock); - - verify(typeSyncOptions.getCtpClient()).execute(eq(TypeCreateCommand.of(draft))); - } - - @Test - public void createType_WithUnSuccessfulMockCtpResponse_ShouldNotCreateType() { - final Type mock = mock(Type.class); - when(mock.getId()).thenReturn("typeId"); - - when(typeSyncOptions.getCtpClient().execute(any())) - .thenReturn(CompletableFutureUtils.failed(new BadRequestException("bad request"))); - - final TypeDraft draft = mock(TypeDraft.class); - when(draft.getKey()).thenReturn("typeKey"); - final Optional typeOptional = Optional.ofNullable(service.createType(draft).toCompletableFuture().join()); - - assertThat(typeOptional).isEmpty(); - assertThat(errorMessages).hasSize(1); - assertThat(errorExceptions).hasSize(1); - assertThat(errorExceptions.get(0)).isExactlyInstanceOf(BadRequestException.class); - assertThat(errorMessages.get(0)).contains("Failed to create draft with key: 'typeKey'."); - assertThat(errorMessages.get(0)).contains("BadRequestException"); - } - - @Test - public void createType_WithDraftWithoutKey_ShouldNotCreateType() { - final TypeDraft draft = mock(TypeDraft.class); - final Optional typeOptional = Optional.ofNullable(service.createType(draft).toCompletableFuture().join()); - - assertThat(typeOptional).isEmpty(); - assertThat(errorMessages).hasSize(1); - assertThat(errorExceptions).hasSize(1); - assertThat(errorMessages.get(0)) - .isEqualTo("Failed to create draft with key: 'null'. Reason: Draft key is blank!"); - } - - - -} \ No newline at end of file diff --git a/src/test/java/com/commercetools/sync/types/FieldDefinitionTestHelper.java b/src/test/java/com/commercetools/sync/types/FieldDefinitionTestHelper.java index 6072cf85d9..c2c2b730e6 100644 --- a/src/test/java/com/commercetools/sync/types/FieldDefinitionTestHelper.java +++ b/src/test/java/com/commercetools/sync/types/FieldDefinitionTestHelper.java @@ -6,6 +6,7 @@ import io.sphere.sdk.models.TextInputHint; import io.sphere.sdk.types.EnumFieldType; import io.sphere.sdk.types.FieldDefinition; +import io.sphere.sdk.types.LocalizedStringFieldType; import io.sphere.sdk.types.ReferenceFieldType; import io.sphere.sdk.types.SetFieldType; import io.sphere.sdk.types.StringFieldType; @@ -26,6 +27,17 @@ public static FieldDefinition stringFieldDefinition(final String fieldName, hint); } + public static FieldDefinition localizedStringFieldDefinition(final String fieldName, + final String labelEng, + boolean required, + final TextInputHint hint) { + return FieldDefinition.of(LocalizedStringFieldType.of(), + fieldName, + LocalizedString.ofEnglish(labelEng), + required, + hint); + } + public static FieldDefinition stateFieldDefinition() { final List values = Arrays.asList( EnumValue.of("published", "the category is publicly visible"), diff --git a/src/test/java/com/commercetools/sync/types/utils/FieldDefinitionUpdateActionUtilsTest.java b/src/test/java/com/commercetools/sync/types/utils/FieldDefinitionUpdateActionUtilsTest.java index 90de8a79e0..ed3165d393 100644 --- a/src/test/java/com/commercetools/sync/types/utils/FieldDefinitionUpdateActionUtilsTest.java +++ b/src/test/java/com/commercetools/sync/types/utils/FieldDefinitionUpdateActionUtilsTest.java @@ -33,10 +33,8 @@ public class FieldDefinitionUpdateActionUtilsTest { private static FieldDefinition old; - private static FieldDefinition oldNullValues; private static FieldDefinition newSame; private static FieldDefinition newDifferent; - private static FieldDefinition newNullValues; private static final EnumValue ENUM_VALUE_A = EnumValue.of("a", "label_a"); private static final EnumValue ENUM_VALUE_B = EnumValue.of("b", "label_b"); @@ -49,12 +47,9 @@ public class FieldDefinitionUpdateActionUtilsTest { */ @BeforeClass public static void setup() { - old = stringFieldDefinition(FIELD_NAME_1, LABEL_1, false, TextInputHint.SINGLE_LINE); - oldNullValues = stringFieldDefinition(FIELD_NAME_1, LABEL_1, false, null); newSame = stringFieldDefinition(FIELD_NAME_1, LABEL_1, false, TextInputHint.SINGLE_LINE); newDifferent = stringFieldDefinition(FIELD_NAME_1, LABEL_2, true, TextInputHint.MULTI_LINE); - newNullValues = stringFieldDefinition(FIELD_NAME_1, LABEL_2, true, null); } @Test @@ -108,7 +103,7 @@ public void buildActions_WithNewPlainEnum_ShouldReturnAddEnumValueAction() { buildActions(oldFieldDefinition, newFieldDefinition); - assertThat(result).containsExactly(AddEnumValue.of(FIELD_NAME_1, ENUM_VALUE_A)); + assertThat(result).containsExactly(AddEnumValue.of(FIELD_NAME_1, ENUM_VALUE_B)); } diff --git a/src/test/java/com/commercetools/sync/types/utils/TypeUpdateActionUtilsTest.java b/src/test/java/com/commercetools/sync/types/utils/TypeUpdateActionUtilsTest.java index 6cc558eb21..84e9d4e792 100644 --- a/src/test/java/com/commercetools/sync/types/utils/TypeUpdateActionUtilsTest.java +++ b/src/test/java/com/commercetools/sync/types/utils/TypeUpdateActionUtilsTest.java @@ -17,7 +17,9 @@ import java.util.Optional; import java.util.Set; -import static com.commercetools.sync.types.FieldDefinitionTestHelper.*; +import static com.commercetools.sync.types.FieldDefinitionTestHelper.imageUrlFieldDefinition; +import static com.commercetools.sync.types.FieldDefinitionTestHelper.relatedCategoriesFieldDefinition; +import static com.commercetools.sync.types.FieldDefinitionTestHelper.stateFieldDefinition; import static com.commercetools.sync.types.utils.TypeUpdateActionUtils.buildChangeNameAction; import static com.commercetools.sync.types.utils.TypeUpdateActionUtils.buildSetDescriptionUpdateAction; import static org.assertj.core.api.Assertions.assertThat; @@ -34,7 +36,7 @@ public class TypeUpdateActionUtilsTest { */ @BeforeClass public static void setup() { - final String key = "category-customtype-key"; + final String key = "category-custom-type-key"; final LocalizedString name = LocalizedString.ofEnglish("type for standard categories"); final LocalizedString desc = LocalizedString.ofEnglish("description for category custom type"); @@ -62,10 +64,10 @@ public static void setup() { .build(); - newDifferent = TypeDraftBuilder.of("222-category-customtype-key-2", - LocalizedString.ofEnglish("222type for standard categories 222"), + newDifferent = TypeDraftBuilder.of("category-custom-type-key-2", + LocalizedString.ofEnglish("type for standard categories 2"), resourceTypeIds) - .description(LocalizedString.ofEnglish("222 description for category custom type 2222")) + .description(LocalizedString.ofEnglish("description for category custom type 2")) .fieldDefinitions(fieldDefinitions) .build(); } diff --git a/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildFieldDefinitionUpdateActionsTest.java b/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildFieldDefinitionUpdateActionsTest.java new file mode 100644 index 0000000000..c9e434f331 --- /dev/null +++ b/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildFieldDefinitionUpdateActionsTest.java @@ -0,0 +1,419 @@ +package com.commercetools.sync.types.utils.typeactionutils; + +import com.commercetools.sync.commons.exceptions.BuildUpdateActionException; +import com.commercetools.sync.commons.exceptions.DuplicateNameException; +import com.commercetools.sync.types.TypeSyncOptions; +import com.commercetools.sync.types.TypeSyncOptionsBuilder; +import io.sphere.sdk.client.SphereClient; +import io.sphere.sdk.commands.UpdateAction; +import io.sphere.sdk.models.LocalizedString; +import io.sphere.sdk.models.TextInputHint; +import io.sphere.sdk.types.FieldDefinition; +import io.sphere.sdk.types.ResourceTypeIdsSetBuilder; +import io.sphere.sdk.types.Type; +import io.sphere.sdk.types.TypeDraft; +import io.sphere.sdk.types.TypeDraftBuilder; +import io.sphere.sdk.types.commands.updateactions.AddFieldDefinition; +import io.sphere.sdk.types.commands.updateactions.ChangeFieldDefinitionOrder; +import io.sphere.sdk.types.commands.updateactions.RemoveFieldDefinition; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; + +import static com.commercetools.sync.types.FieldDefinitionTestHelper.localizedStringFieldDefinition; +import static com.commercetools.sync.types.FieldDefinitionTestHelper.stringFieldDefinition; +import static com.commercetools.sync.types.utils.TypeUpdateActionUtils.buildFieldDefinitionUpdateActions; +import static io.sphere.sdk.json.SphereJsonUtils.readObjectFromResource; +import static java.util.Arrays.asList; +import static java.util.Collections.emptyList; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class BuildFieldDefinitionUpdateActionsTest { + private static final String RES_ROOT = + "com/commercetools/sync/types/utils/updatefielddefinitions/fields/"; + + private static final String TYPE_WITH_FIELDS_AB = + RES_ROOT + "type-with-field-definitions-ab.json"; + private static final String TYPE_WITH_FIELDS_ABB = + RES_ROOT + "type-with-field-definitions-abb.json"; + private static final String TYPE_WITH_FIELDS_ABC = + RES_ROOT + "type-with-field-definitions-abc.json"; + private static final String TYPE_WITH_FIELDS_ABCD = + RES_ROOT + "type-with-field-definitions-abcd.json"; + private static final String TYPE_WITH_FIELDS_ABD = + RES_ROOT + "type-with-field-definitions-abd.json"; + private static final String TYPE_WITH_FIELDS_CAB = + RES_ROOT + "type-with-field-definitions-cab.json"; + private static final String TYPE_WITH_FIELDS_CB = + RES_ROOT + "type-with-field-definitions-cb.json"; + private static final String TYPE_WITH_FIELDS_ACBD = + RES_ROOT + "type-with-field-definitions-acbd.json"; + private static final String TYPE_WITH_FIELDS_ADBC = + RES_ROOT + "type-with-field-definitions-adbc.json"; + private static final String TYPE_WITH_FIELDS_CBD = + RES_ROOT + "type-with-field-definitions-cbd.json"; + private static final String TYPE_WITH_FIELDS_ABC_WITH_DIFFERENT_TYPE = + RES_ROOT + "type-with-field-definitions-abc-with-different-type.json"; + + + private static final TypeSyncOptions SYNC_OPTIONS = TypeSyncOptionsBuilder + .of(mock(SphereClient.class)) + .build(); + + private static final String FIELD_A = "a"; + private static final String FIELD_B = "b"; + private static final String FIELD_C = "c"; + private static final String FIELD_D = "d"; + private static final String LABEL_EN = "label_en"; + + private static final FieldDefinition FIELD_DEFINITION_A = + stringFieldDefinition(FIELD_A, LABEL_EN, false, TextInputHint.SINGLE_LINE); + private static final FieldDefinition FIELD_DEFINITION_A_LOCALIZED_TYPE = + localizedStringFieldDefinition(FIELD_A, LABEL_EN, false, TextInputHint.SINGLE_LINE); + private static final FieldDefinition FIELD_DEFINITION_B = + stringFieldDefinition(FIELD_B, LABEL_EN, false, TextInputHint.SINGLE_LINE); + private static final FieldDefinition FIELD_DEFINITION_C = + stringFieldDefinition(FIELD_C, LABEL_EN, false, TextInputHint.SINGLE_LINE); + private static final FieldDefinition FIELD_DEFINITION_D = + stringFieldDefinition(FIELD_D, LABEL_EN, false, TextInputHint.SINGLE_LINE); + + private static final String TYPE_KEY = "key"; + private static final LocalizedString TYPE_NAME = LocalizedString.ofEnglish("name"); + private static final LocalizedString TYPE_DESCRIPTION = LocalizedString.ofEnglish("description"); + + private static final TypeDraft TYPE_DRAFT = TypeDraftBuilder.of( + TYPE_KEY, + TYPE_NAME, + ResourceTypeIdsSetBuilder.of().addCategories().build()) + .description(TYPE_DESCRIPTION) + .build(); + + @Test + public void buildUpdateActions_WithNullNewFieldDefinitionsAndExistingFieldDefinitions_ShouldBuild3RemoveActions() { + final Type oldType = readObjectFromResource(TYPE_WITH_FIELDS_ABC, Type.class); + + final List> updateActions = buildFieldDefinitionUpdateActions( + oldType, + TYPE_DRAFT, + SYNC_OPTIONS + ); + + assertThat(updateActions).containsExactly( + RemoveFieldDefinition.of(FIELD_A), + RemoveFieldDefinition.of(FIELD_B), + RemoveFieldDefinition.of(FIELD_C) + ); + } + + @Test + public void buildUpdateActions_WithNullNewAFieldDefinitionsAndNoOldFieldDefinitions_ShouldNotBuildActions() { + final Type oldType = mock(Type.class); + when(oldType.getFieldDefinitions()).thenReturn(emptyList()); + when(oldType.getKey()).thenReturn("type_key_1"); + + final List> updateActions = buildFieldDefinitionUpdateActions( + oldType, + TYPE_DRAFT, + SYNC_OPTIONS + ); + + assertThat(updateActions).isEmpty(); + } + + @Test + public void buildUpdateActions_WithNewFieldDefinitionsAndNoOldFieldDefinitions_ShouldBuild3AddActions() { + final Type oldType = mock(Type.class); + when(oldType.getFieldDefinitions()).thenReturn(emptyList()); + when(oldType.getKey()).thenReturn("type_key_1"); + + final TypeDraft newTypeDraft = readObjectFromResource( + TYPE_WITH_FIELDS_ABC, + TypeDraft.class + ); + + final List> updateActions = buildFieldDefinitionUpdateActions( + oldType, + newTypeDraft, + SYNC_OPTIONS + ); + + assertThat(updateActions).containsExactly( + AddFieldDefinition.of(FIELD_DEFINITION_A), + AddFieldDefinition.of(FIELD_DEFINITION_B), + AddFieldDefinition.of(FIELD_DEFINITION_C) + ); + } + + @Test + public void buildUpdateActions_WithIdenticalFieldDefinitions_ShouldNotBuildUpdateActions() { + final Type oldType = readObjectFromResource(TYPE_WITH_FIELDS_ABC, Type.class); + + final TypeDraft newTypeDraft = readObjectFromResource( + TYPE_WITH_FIELDS_ABC, + TypeDraft.class + ); + + final List> updateActions = buildFieldDefinitionUpdateActions( + oldType, + newTypeDraft, + SYNC_OPTIONS + ); + + assertThat(updateActions).isEmpty(); + } + + @Test + public void buildUpdateActions_WithDuplicateFieldDefinitionNames_ShouldNotBuildActionsAndTriggerErrorCb() { + final Type oldType = readObjectFromResource(TYPE_WITH_FIELDS_ABC, Type.class); + + final TypeDraft newTypeDraft = readObjectFromResource( + TYPE_WITH_FIELDS_ABB, + TypeDraft.class + ); + + final List errorMessages = new ArrayList<>(); + final List exceptions = new ArrayList<>(); + final TypeSyncOptions syncOptions = TypeSyncOptionsBuilder.of(mock(SphereClient.class)) + .errorCallback((errorMessage, exception) -> { + errorMessages.add(errorMessage); + exceptions.add(exception); + }) + .build(); + + final List> updateActions = buildFieldDefinitionUpdateActions( + oldType, + newTypeDraft, + syncOptions + ); + + assertThat(updateActions).isEmpty(); + assertThat(errorMessages).hasSize(1); + assertThat(errorMessages.get(0)).matches("Failed to build update actions for the field definitions of the " + + "type with the key 'key'. Reason: .*DuplicateNameException: Field definitions " + + "have duplicated names. Duplicated field definition name: 'b'. Field definitions names are " + + "expected to be unique inside their type."); + assertThat(exceptions).hasSize(1); + assertThat(exceptions.get(0)).isExactlyInstanceOf(BuildUpdateActionException.class); + assertThat(exceptions.get(0).getMessage()).contains("Field definitions have duplicated names. " + + "Duplicated field definition name: 'b'. Field definitions names are expected to be unique " + + "inside their type."); + assertThat(exceptions.get(0).getCause()).isExactlyInstanceOf(DuplicateNameException.class); + } + + @Test + public void buildUpdateActions_WithOneMissingFieldDefinition_ShouldBuildRemoveFieldDefinitionAction() { + final Type oldType = readObjectFromResource(TYPE_WITH_FIELDS_ABC, Type.class); + + final TypeDraft newTypeDraft = readObjectFromResource( + TYPE_WITH_FIELDS_AB, + TypeDraft.class + ); + + final List> updateActions = buildFieldDefinitionUpdateActions( + oldType, + newTypeDraft, + SYNC_OPTIONS + ); + + assertThat(updateActions).containsExactly(RemoveFieldDefinition.of("c")); + } + + @Test + public void buildUpdateActions_WithOneExtraFieldDefinition_ShouldBuildAddFieldDefinitionAction() { + final Type oldType = readObjectFromResource(TYPE_WITH_FIELDS_ABC, Type.class); + + final TypeDraft newTypeDraft = readObjectFromResource( + TYPE_WITH_FIELDS_ABCD, + TypeDraft.class + ); + + final List> updateActions = buildFieldDefinitionUpdateActions( + oldType, + newTypeDraft, + SYNC_OPTIONS + ); + + assertThat(updateActions).containsExactly( + AddFieldDefinition.of(FIELD_DEFINITION_D) + ); + } + + @Test + public void buildUpdateActions_WithOneFieldDefinitionSwitch_ShouldBuildRemoveAndAddAFieldDefinitionsActions() { + final Type oldType = readObjectFromResource(TYPE_WITH_FIELDS_ABC, Type.class); + + final TypeDraft newTypeDraft = readObjectFromResource( + TYPE_WITH_FIELDS_ABD, + TypeDraft.class + ); + + final List> updateActions = buildFieldDefinitionUpdateActions( + oldType, + newTypeDraft, + SYNC_OPTIONS + ); + + assertThat(updateActions).containsExactly( + RemoveFieldDefinition.of(FIELD_C), + AddFieldDefinition.of(FIELD_DEFINITION_D) + ); + } + + @Test + public void buildUpdateActions_WithDifferent_ShouldBuildChangeFieldDefinitionOrderAction() { + final Type oldType = readObjectFromResource(TYPE_WITH_FIELDS_ABC, Type.class); + + final TypeDraft newTypeDraft = readObjectFromResource( + TYPE_WITH_FIELDS_CAB, + TypeDraft.class + ); + + final List> updateActions = buildFieldDefinitionUpdateActions( + oldType, + newTypeDraft, + SYNC_OPTIONS + ); + + + assertThat(updateActions).containsExactly( + ChangeFieldDefinitionOrder + .of(asList( + FIELD_DEFINITION_C.getName(), + FIELD_DEFINITION_A.getName(), + FIELD_DEFINITION_B.getName() + )) + ); + } + + @Test + public void buildUpdateActions_WithRemovedAndDifferentOrder_ShouldBuildChangeOrderAndRemoveActions() { + final Type oldType = readObjectFromResource(TYPE_WITH_FIELDS_ABC, Type.class); + + final TypeDraft newTypeDraft = readObjectFromResource( + TYPE_WITH_FIELDS_CB, + TypeDraft.class + ); + + final List> updateActions = buildFieldDefinitionUpdateActions( + oldType, + newTypeDraft, + SYNC_OPTIONS + ); + + assertThat(updateActions).containsExactly( + RemoveFieldDefinition.of(FIELD_A), + ChangeFieldDefinitionOrder + .of(asList( + FIELD_DEFINITION_C.getName(), + FIELD_DEFINITION_B.getName() + ) + ) + ); + } + + @Test + public void buildUpdateActions_WithAddedAndDifferentOrder_ShouldBuildChangeOrderAndAddActions() { + final Type oldType = readObjectFromResource(TYPE_WITH_FIELDS_ABC, Type.class); + + final TypeDraft newTypeDraft = readObjectFromResource( + TYPE_WITH_FIELDS_ACBD, + TypeDraft.class + ); + + final List> updateActions = buildFieldDefinitionUpdateActions( + oldType, + newTypeDraft, + SYNC_OPTIONS + ); + + assertThat(updateActions).containsExactly( + AddFieldDefinition.of(FIELD_DEFINITION_D), + ChangeFieldDefinitionOrder + .of(asList( + FIELD_DEFINITION_A.getName(), + FIELD_DEFINITION_C.getName(), + FIELD_DEFINITION_B.getName(), + FIELD_DEFINITION_D.getName() + ) + ) + ); + } + + @Test + public void buildUpdateActions_WithAddedFieldDefinitionInBetween_ShouldBuildChangeOrderAndAddActions() { + final Type oldType = readObjectFromResource(TYPE_WITH_FIELDS_ABC, Type.class); + + final TypeDraft newTypeDraft = readObjectFromResource( + TYPE_WITH_FIELDS_ADBC, + TypeDraft.class + ); + + final List> updateActions = buildFieldDefinitionUpdateActions( + oldType, + newTypeDraft, + SYNC_OPTIONS + ); + + assertThat(updateActions).containsExactly( + AddFieldDefinition.of(FIELD_DEFINITION_D), + ChangeFieldDefinitionOrder + .of(asList( + FIELD_DEFINITION_A.getName(), + FIELD_DEFINITION_D.getName(), + FIELD_DEFINITION_B.getName(), + FIELD_DEFINITION_C.getName() + ) + ) + ); + } + + @Test + public void buildUpdateActions_WithAddedRemovedAndDifOrder_ShouldBuildAllThreeMoveFieldDefinitionActions() { + final Type oldType = readObjectFromResource(TYPE_WITH_FIELDS_ABC, Type.class); + + final TypeDraft newTypeDraft = readObjectFromResource( + TYPE_WITH_FIELDS_CBD, + TypeDraft.class + ); + + final List> updateActions = buildFieldDefinitionUpdateActions( + oldType, + newTypeDraft, + SYNC_OPTIONS + ); + assertThat(updateActions).containsExactly( + RemoveFieldDefinition.of(FIELD_A), + AddFieldDefinition.of(FIELD_DEFINITION_D), + ChangeFieldDefinitionOrder + .of(asList( + FIELD_DEFINITION_C.getName(), + FIELD_DEFINITION_B.getName(), + FIELD_DEFINITION_D.getName() + ) + ) + ); + } + + @Test + public void buildUpdateActions_WithDifferentType_ShouldRemoveOldFieldDefinitionAndAddNewFieldDefinition() { + final Type oldType = readObjectFromResource(TYPE_WITH_FIELDS_ABC, Type.class); + + final TypeDraft newTypeDraft = readObjectFromResource( + TYPE_WITH_FIELDS_ABC_WITH_DIFFERENT_TYPE, + TypeDraft.class + ); + final List> updateActions = buildFieldDefinitionUpdateActions( + oldType, + newTypeDraft, + SYNC_OPTIONS + ); + + assertThat(updateActions).containsExactly( + RemoveFieldDefinition.of(FIELD_A), + AddFieldDefinition.of(FIELD_DEFINITION_A_LOCALIZED_TYPE) + ); + } +} diff --git a/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildLocalizedEnumUpdateActionsTest.java b/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildLocalizedEnumUpdateActionsTest.java new file mode 100644 index 0000000000..4fb1648c9d --- /dev/null +++ b/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildLocalizedEnumUpdateActionsTest.java @@ -0,0 +1,205 @@ +package com.commercetools.sync.types.utils.typeactionutils; + +import io.sphere.sdk.commands.UpdateAction; +import io.sphere.sdk.models.LocalizedEnumValue; +import io.sphere.sdk.types.Type; +import io.sphere.sdk.types.commands.updateactions.AddLocalizedEnumValue; +import io.sphere.sdk.types.commands.updateactions.ChangeLocalizedEnumValueOrder; +import org.junit.Test; + +import java.util.Collections; +import java.util.List; + +import static com.commercetools.sync.types.utils.TypeUpdateLocalizedEnumActionUtils.buildLocalizedEnumValuesUpdateActions; +import static io.sphere.sdk.models.LocalizedString.ofEnglish; +import static java.util.Arrays.asList; +import static org.assertj.core.api.Assertions.assertThat; + +public class BuildLocalizedEnumUpdateActionsTest { + private static final String FIELD_NAME_1 = "field1"; + + private static final LocalizedEnumValue ENUM_VALUE_A = LocalizedEnumValue.of("a", ofEnglish("label_a")); + private static final LocalizedEnumValue ENUM_VALUE_B = LocalizedEnumValue.of("b", ofEnglish("label_b")); + private static final LocalizedEnumValue ENUM_VALUE_C = LocalizedEnumValue.of("c", ofEnglish("label_c")); + private static final LocalizedEnumValue ENUM_VALUE_D = LocalizedEnumValue.of("d", ofEnglish("label_d")); + + private static final List ENUM_VALUES_ABC = asList(ENUM_VALUE_A, ENUM_VALUE_B, ENUM_VALUE_C); + private static final List ENUM_VALUES_ABD = asList(ENUM_VALUE_A, ENUM_VALUE_B, ENUM_VALUE_D); + private static final List ENUM_VALUES_ABCD = asList( + ENUM_VALUE_A, + ENUM_VALUE_B, + ENUM_VALUE_C, + ENUM_VALUE_D + ); + private static final List ENUM_VALUES_CAB = asList(ENUM_VALUE_C, ENUM_VALUE_A, ENUM_VALUE_B); + private static final List ENUM_VALUES_CB = asList(ENUM_VALUE_C, ENUM_VALUE_B); + private static final List ENUM_VALUES_ACBD = asList( + ENUM_VALUE_A, + ENUM_VALUE_C, + ENUM_VALUE_B, + ENUM_VALUE_D + ); + private static final List ENUM_VALUES_ADBC = asList( + ENUM_VALUE_A, + ENUM_VALUE_D, + ENUM_VALUE_B, + ENUM_VALUE_C + ); + private static final List ENUM_VALUES_CBD = asList(ENUM_VALUE_C, ENUM_VALUE_B, ENUM_VALUE_D); + + @Test + public void buildLocalizedEnumUpdateActions_WithEmptyPlainEnumValuesAndNoOlEnumValues_ShouldNotBuildActions() { + final List> updateActions = buildLocalizedEnumValuesUpdateActions( + FIELD_NAME_1, + Collections.emptyList(), + Collections.emptyList() + ); + + assertThat(updateActions).isEmpty(); + } + + @Test + public void buildLocalizedEnumUpdateActions_WithNewPlainEnumValuesAndNoOldPlainEnumValues_ShouldBuild3AddActions() { + final List> updateActions = buildLocalizedEnumValuesUpdateActions( + FIELD_NAME_1, + Collections.emptyList(), + ENUM_VALUES_ABC + ); + + assertThat(updateActions).containsExactly( + AddLocalizedEnumValue.of(FIELD_NAME_1, ENUM_VALUE_A), + AddLocalizedEnumValue.of(FIELD_NAME_1, ENUM_VALUE_B), + AddLocalizedEnumValue.of(FIELD_NAME_1, ENUM_VALUE_C) + ); + } + + @Test + public void buildLocalizedEnumUpdateActions_WithIdenticalPlainEnum_ShouldNotBuildUpdateActions() { + final List> updateActions = buildLocalizedEnumValuesUpdateActions( + FIELD_NAME_1, + ENUM_VALUES_ABC, + ENUM_VALUES_ABC + ); + + assertThat(updateActions).isEmpty(); + } + + + @Test + public void buildLocalizedEnumUpdateActions_WithOnePlainEnumValue_ShouldBuildAddEnumValueAction() { + final List> updateActions = buildLocalizedEnumValuesUpdateActions( + FIELD_NAME_1, + ENUM_VALUES_ABC, + ENUM_VALUES_ABCD + ); + + assertThat(updateActions).containsExactly( + AddLocalizedEnumValue.of(FIELD_NAME_1, ENUM_VALUE_D) + ); + } + + @Test + public void buildLocalizedEnumUpdateActions_WithOneEnumValueSwitch_ShouldBuildRemoveAndAddEnumValueActions() { + final List> updateActions = buildLocalizedEnumValuesUpdateActions( + FIELD_NAME_1, + ENUM_VALUES_ABC, + ENUM_VALUES_ABD + ); + + // remove enum value actions not exists for type resources + assertThat(updateActions).containsExactly( + AddLocalizedEnumValue.of(FIELD_NAME_1, ENUM_VALUE_D) + ); + } + + @Test + public void buildLocalizedEnumUpdateActions_WithDifferent_ShouldBuildChangeEnumValueOrderAction() { + final List> updateActions = buildLocalizedEnumValuesUpdateActions( + FIELD_NAME_1, + ENUM_VALUES_ABC, + ENUM_VALUES_CAB + ); + + assertThat(updateActions).containsExactly( + ChangeLocalizedEnumValueOrder.of(FIELD_NAME_1, asList( + ENUM_VALUE_C.getKey(), + ENUM_VALUE_A.getKey(), + ENUM_VALUE_B.getKey() + )) + ); + } + + @Test + public void buildLocalizedEnumUpdateActions_WithRemovedAndDifferentOrder_ShouldBuildChangeOrderAndRemoveActions() { + final List> updateActions = buildLocalizedEnumValuesUpdateActions( + FIELD_NAME_1, + ENUM_VALUES_ABC, + ENUM_VALUES_CB + ); + + // remove enum value actions not exists for type resources + assertThat(updateActions).containsExactly( + ChangeLocalizedEnumValueOrder.of(FIELD_NAME_1, asList( + ENUM_VALUE_C.getKey(), + ENUM_VALUE_B.getKey() + )) + ); + } + + @Test + public void buildLocalizedEnumUpdateActions_WithAddedAndDifferentOrder_ShouldBuildChangeOrderAndAddActions() { + final List> updateActions = buildLocalizedEnumValuesUpdateActions( + FIELD_NAME_1, + ENUM_VALUES_ABC, + ENUM_VALUES_ACBD + ); + + assertThat(updateActions).containsExactly( + AddLocalizedEnumValue.of(FIELD_NAME_1, ENUM_VALUE_D), + ChangeLocalizedEnumValueOrder.of(FIELD_NAME_1, asList( + ENUM_VALUE_A.getKey(), + ENUM_VALUE_C.getKey(), + ENUM_VALUE_B.getKey(), + ENUM_VALUE_D.getKey() + )) + ); + } + + @Test + public void buildLocalizedEnumUpdateActions_WithAddedEnumValueInBetween_ShouldBuildChangeOrderAndAddActions() { + final List> updateActions = buildLocalizedEnumValuesUpdateActions( + FIELD_NAME_1, + ENUM_VALUES_ABC, + ENUM_VALUES_ADBC + ); + + assertThat(updateActions).containsExactly( + AddLocalizedEnumValue.of(FIELD_NAME_1, ENUM_VALUE_D), + ChangeLocalizedEnumValueOrder.of(FIELD_NAME_1, asList( + ENUM_VALUE_A.getKey(), + ENUM_VALUE_D.getKey(), + ENUM_VALUE_B.getKey(), + ENUM_VALUE_C.getKey() + )) + ); + } + + @Test + public void buildLocalizedEnumUpdateActions_WithAddedRemovedAndDifOrder_ShouldBuildAllThreeMoveEnumValueActions() { + final List> updateActions = buildLocalizedEnumValuesUpdateActions( + FIELD_NAME_1, + ENUM_VALUES_ABC, + ENUM_VALUES_CBD + ); + + // remove enum value actions not exists for type resources + assertThat(updateActions).containsExactly( + AddLocalizedEnumValue.of(FIELD_NAME_1, ENUM_VALUE_D), + ChangeLocalizedEnumValueOrder.of(FIELD_NAME_1, asList( + ENUM_VALUE_C.getKey(), + ENUM_VALUE_B.getKey(), + ENUM_VALUE_D.getKey() + )) + ); + } +} diff --git a/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildPlainEnumUpdateActionsTest.java b/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildPlainEnumUpdateActionsTest.java new file mode 100644 index 0000000000..5b3ce74640 --- /dev/null +++ b/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildPlainEnumUpdateActionsTest.java @@ -0,0 +1,202 @@ +package com.commercetools.sync.types.utils.typeactionutils; + +import io.sphere.sdk.commands.UpdateAction; +import io.sphere.sdk.models.EnumValue; +import io.sphere.sdk.types.Type; +import io.sphere.sdk.types.commands.updateactions.AddEnumValue; +import io.sphere.sdk.types.commands.updateactions.ChangeEnumValueOrder; +import org.junit.Test; + +import java.util.Collections; +import java.util.List; + +import static com.commercetools.sync.types.utils.TypeUpdatePlainEnumActionUtils.buildEnumValuesUpdateActions; +import static java.util.Arrays.asList; +import static org.assertj.core.api.Assertions.assertThat; + +public class BuildPlainEnumUpdateActionsTest { + private static final String FIELD_NAME_1 = "field1"; + + private static final EnumValue ENUM_VALUE_A = EnumValue.of("a", "label_a"); + private static final EnumValue ENUM_VALUE_B = EnumValue.of("b", "label_b"); + private static final EnumValue ENUM_VALUE_C = EnumValue.of("c", "label_c"); + private static final EnumValue ENUM_VALUE_D = EnumValue.of("d", "label_d"); + + private static final List ENUM_VALUES_ABC = asList(ENUM_VALUE_A, ENUM_VALUE_B, ENUM_VALUE_C); + private static final List ENUM_VALUES_ABD = asList(ENUM_VALUE_A, ENUM_VALUE_B, ENUM_VALUE_D); + private static final List ENUM_VALUES_ABCD = asList( + ENUM_VALUE_A, + ENUM_VALUE_B, + ENUM_VALUE_C, + ENUM_VALUE_D + ); + private static final List ENUM_VALUES_CAB = asList(ENUM_VALUE_C, ENUM_VALUE_A, ENUM_VALUE_B); + private static final List ENUM_VALUES_CB = asList(ENUM_VALUE_C, ENUM_VALUE_B); + private static final List ENUM_VALUES_ACBD = asList( + ENUM_VALUE_A, + ENUM_VALUE_C, + ENUM_VALUE_B, + ENUM_VALUE_D + ); + private static final List ENUM_VALUES_ADBC = asList( + ENUM_VALUE_A, + ENUM_VALUE_D, + ENUM_VALUE_B, + ENUM_VALUE_C + ); + private static final List ENUM_VALUES_CBD = asList(ENUM_VALUE_C, ENUM_VALUE_B, ENUM_VALUE_D); + + + + @Test + public void buildPlainEnumUpdateActions_WithEmptyPlainEnumValuesAndNoOlEnumValues_ShouldNotBuildActions() { + final List> updateActions = buildEnumValuesUpdateActions( + FIELD_NAME_1, + Collections.emptyList(), + Collections.emptyList() + ); + + assertThat(updateActions).isEmpty(); + } + + @Test + public void buildPlainEnumUpdateActions_WithNewPlainEnumValuesAndNoOldPlainEnumValues_ShouldBuild3AddActions() { + final List> updateActions = buildEnumValuesUpdateActions( + FIELD_NAME_1, + Collections.emptyList(), + ENUM_VALUES_ABC + ); + + assertThat(updateActions).containsExactly( + AddEnumValue.of(FIELD_NAME_1, ENUM_VALUE_A), + AddEnumValue.of(FIELD_NAME_1, ENUM_VALUE_B), + AddEnumValue.of(FIELD_NAME_1, ENUM_VALUE_C) + ); + } + + @Test + public void buildPlainEnumUpdateActions_WithIdenticalPlainEnum_ShouldNotBuildUpdateActions() { + final List> updateActions = buildEnumValuesUpdateActions( + FIELD_NAME_1, + ENUM_VALUES_ABC, + ENUM_VALUES_ABC + ); + + assertThat(updateActions).isEmpty(); + } + + @Test + public void buildPlainEnumUpdateActions_WithOnePlainEnumValue_ShouldBuildAddEnumValueAction() { + final List> updateActions = buildEnumValuesUpdateActions( + FIELD_NAME_1, + ENUM_VALUES_ABC, + ENUM_VALUES_ABCD + ); + + assertThat(updateActions).containsExactly( + AddEnumValue.of(FIELD_NAME_1, ENUM_VALUE_D) + ); + } + + @Test + public void buildPlainEnumUpdateActions_WithOneEnumValueSwitch_ShouldBuildRemoveAndAddEnumValueActions() { + final List> updateActions = buildEnumValuesUpdateActions( + FIELD_NAME_1, + ENUM_VALUES_ABC, + ENUM_VALUES_ABD + ); + + assertThat(updateActions).containsExactly( + AddEnumValue.of(FIELD_NAME_1, ENUM_VALUE_D) + ); + } + + @Test + public void buildPlainEnumUpdateActions_WithDifferent_ShouldBuildChangeEnumValueOrderAction() { + final List> updateActions = buildEnumValuesUpdateActions( + FIELD_NAME_1, + ENUM_VALUES_ABC, + ENUM_VALUES_CAB + ); + + assertThat(updateActions).containsExactly( + ChangeEnumValueOrder.of(FIELD_NAME_1, asList( + ENUM_VALUE_C.getKey(), + ENUM_VALUE_A.getKey(), + ENUM_VALUE_B.getKey() + )) + ); + } + + @Test + public void buildPlainEnumUpdateActions_WithRemovedAndDifferentOrder_ShouldBuildChangeOrderAndRemoveActions() { + final List> updateActions = buildEnumValuesUpdateActions( + FIELD_NAME_1, + ENUM_VALUES_ABC, + ENUM_VALUES_CB + ); + + assertThat(updateActions).containsExactly( + ChangeEnumValueOrder.of(FIELD_NAME_1, asList( + ENUM_VALUE_C.getKey(), + ENUM_VALUE_B.getKey() + )) + ); + } + + @Test + public void buildPlainEnumUpdateActions_WithAddedAndDifferentOrder_ShouldBuildChangeOrderAndAddActions() { + final List> updateActions = buildEnumValuesUpdateActions( + FIELD_NAME_1, + ENUM_VALUES_ABC, + ENUM_VALUES_ACBD + ); + + assertThat(updateActions).containsExactly( + AddEnumValue.of(FIELD_NAME_1, ENUM_VALUE_D), + ChangeEnumValueOrder.of(FIELD_NAME_1, asList( + ENUM_VALUE_A.getKey(), + ENUM_VALUE_C.getKey(), + ENUM_VALUE_B.getKey(), + ENUM_VALUE_D.getKey() + )) + ); + } + + @Test + public void buildPlainEnumUpdateActions_WithAddedEnumValueInBetween_ShouldBuildChangeOrderAndAddActions() { + final List> updateActions = buildEnumValuesUpdateActions( + FIELD_NAME_1, + ENUM_VALUES_ABC, + ENUM_VALUES_ADBC + ); + + assertThat(updateActions).containsExactly( + AddEnumValue.of(FIELD_NAME_1, ENUM_VALUE_D), + ChangeEnumValueOrder.of(FIELD_NAME_1, asList( + ENUM_VALUE_A.getKey(), + ENUM_VALUE_D.getKey(), + ENUM_VALUE_B.getKey(), + ENUM_VALUE_C.getKey() + )) + ); + } + + @Test + public void buildPlainEnumUpdateActions_WithAddedRemovedAndDifOrder_ShouldBuildAllThreeMoveEnumValueActions() { + final List> updateActions = buildEnumValuesUpdateActions( + FIELD_NAME_1, + ENUM_VALUES_ABC, + ENUM_VALUES_CBD + ); + + assertThat(updateActions).containsExactly( + AddEnumValue.of(FIELD_NAME_1, ENUM_VALUE_D), + ChangeEnumValueOrder.of(FIELD_NAME_1, asList( + ENUM_VALUE_C.getKey(), + ENUM_VALUE_B.getKey(), + ENUM_VALUE_D.getKey() + )) + ); + } +} diff --git a/src/test/resources/com/commercetools/sync/types/utils/updatefielddefinitions/fields/type-with-field-definitions-ab.json b/src/test/resources/com/commercetools/sync/types/utils/updatefielddefinitions/fields/type-with-field-definitions-ab.json new file mode 100644 index 0000000000..e1067e299f --- /dev/null +++ b/src/test/resources/com/commercetools/sync/types/utils/updatefielddefinitions/fields/type-with-field-definitions-ab.json @@ -0,0 +1,37 @@ +{ + "key": "key", + "name": { + "en": "name" + }, + "description": { + "en": "description" + }, + "resourceTypeIds": [ + "category" + ], + "fieldDefinitions": [ + { + "name": "a", + "label": { + "en": "label_en" + }, + "required": false, + "type": { + "name": "String" + }, + "inputHint": "SingleLine" + }, + { + "name": "b", + "label": { + "en": "label_en" + }, + "required": false, + "type": { + "name": "String" + }, + "inputHint": "SingleLine" + } + ] +} + diff --git a/src/test/resources/com/commercetools/sync/types/utils/updatefielddefinitions/fields/type-with-field-definitions-abb.json b/src/test/resources/com/commercetools/sync/types/utils/updatefielddefinitions/fields/type-with-field-definitions-abb.json new file mode 100644 index 0000000000..901d9a08e1 --- /dev/null +++ b/src/test/resources/com/commercetools/sync/types/utils/updatefielddefinitions/fields/type-with-field-definitions-abb.json @@ -0,0 +1,47 @@ +{ + "key": "key", + "name": { + "en": "name" + }, + "description": { + "en": "description" + }, + "resourceTypeIds": [ + "category" + ], + "fieldDefinitions": [ + { + "name": "a", + "label": { + "en": "label_en" + }, + "required": false, + "type": { + "name": "String" + }, + "inputHint": "SingleLine" + }, + { + "name": "b", + "label": { + "en": "label_en" + }, + "required": false, + "type": { + "name": "String" + }, + "inputHint": "SingleLine" + }, + { + "name": "b", + "label": { + "en": "label_en" + }, + "required": false, + "type": { + "name": "String" + }, + "inputHint": "SingleLine" + } + ] +} \ No newline at end of file diff --git a/src/test/resources/com/commercetools/sync/types/utils/updatefielddefinitions/fields/type-with-field-definitions-abc-with-changes.json b/src/test/resources/com/commercetools/sync/types/utils/updatefielddefinitions/fields/type-with-field-definitions-abc-with-changes.json new file mode 100644 index 0000000000..145155b14b --- /dev/null +++ b/src/test/resources/com/commercetools/sync/types/utils/updatefielddefinitions/fields/type-with-field-definitions-abc-with-changes.json @@ -0,0 +1,47 @@ +{ + "key": "key", + "name": { + "en": "name" + }, + "description": { + "en": "description" + }, + "resourceTypeIds": [ + "category" + ], + "fieldDefinitions": [ + { + "name": "a", + "label": { + "en": "label_en_edited" + }, + "required": false, + "type": { + "name": "String" + }, + "inputHint": "SingleLine" + }, + { + "name": "b", + "label": { + "en": "label_en_edited" + }, + "required": false, + "type": { + "name": "String" + }, + "inputHint": "SingleLine" + }, + { + "name": "c", + "label": { + "en": "label_en_edited" + }, + "required": false, + "type": { + "name": "String" + }, + "inputHint": "SingleLine" + } + ] +} \ No newline at end of file diff --git a/src/test/resources/com/commercetools/sync/types/utils/updatefielddefinitions/fields/type-with-field-definitions-abc-with-different-type.json b/src/test/resources/com/commercetools/sync/types/utils/updatefielddefinitions/fields/type-with-field-definitions-abc-with-different-type.json new file mode 100644 index 0000000000..2c36364f42 --- /dev/null +++ b/src/test/resources/com/commercetools/sync/types/utils/updatefielddefinitions/fields/type-with-field-definitions-abc-with-different-type.json @@ -0,0 +1,47 @@ +{ + "key": "key", + "name": { + "en": "name" + }, + "description": { + "en": "description" + }, + "resourceTypeIds": [ + "category" + ], + "fieldDefinitions": [ + { + "name": "a", + "label": { + "en": "label_en" + }, + "required": false, + "type": { + "name": "LocalizedString" + }, + "inputHint": "SingleLine" + }, + { + "name": "b", + "label": { + "en": "label_en" + }, + "required": false, + "type": { + "name": "String" + }, + "inputHint": "SingleLine" + }, + { + "name": "c", + "label": { + "en": "label_en" + }, + "required": false, + "type": { + "name": "String" + }, + "inputHint": "SingleLine" + } + ] +} \ No newline at end of file diff --git a/src/test/resources/com/commercetools/sync/types/utils/updatefielddefinitions/fields/type-with-field-definitions-abc.json b/src/test/resources/com/commercetools/sync/types/utils/updatefielddefinitions/fields/type-with-field-definitions-abc.json new file mode 100644 index 0000000000..71dc64f077 --- /dev/null +++ b/src/test/resources/com/commercetools/sync/types/utils/updatefielddefinitions/fields/type-with-field-definitions-abc.json @@ -0,0 +1,47 @@ +{ + "key": "key", + "name": { + "en": "name" + }, + "description": { + "en": "description" + }, + "resourceTypeIds": [ + "category" + ], + "fieldDefinitions": [ + { + "name": "a", + "label": { + "en": "label_en" + }, + "required": false, + "type": { + "name": "String" + }, + "inputHint": "SingleLine" + }, + { + "name": "b", + "label": { + "en": "label_en" + }, + "required": false, + "type": { + "name": "String" + }, + "inputHint": "SingleLine" + }, + { + "name": "c", + "label": { + "en": "label_en" + }, + "required": false, + "type": { + "name": "String" + }, + "inputHint": "SingleLine" + } + ] +} \ No newline at end of file diff --git a/src/test/resources/com/commercetools/sync/types/utils/updatefielddefinitions/fields/type-with-field-definitions-abcd.json b/src/test/resources/com/commercetools/sync/types/utils/updatefielddefinitions/fields/type-with-field-definitions-abcd.json new file mode 100644 index 0000000000..fa34f65324 --- /dev/null +++ b/src/test/resources/com/commercetools/sync/types/utils/updatefielddefinitions/fields/type-with-field-definitions-abcd.json @@ -0,0 +1,58 @@ +{ + "key": "key", + "name": { + "en": "name" + }, + "description": { + "en": "description" + }, + "resourceTypeIds": [ + "category" + ], + "fieldDefinitions": [ + { + "name": "a", + "label": { + "en": "label_en" + }, + "required": false, + "type": { + "name": "String" + }, + "inputHint": "SingleLine" + }, + { + "name": "b", + "label": { + "en": "label_en" + }, + "required": false, + "type": { + "name": "String" + }, + "inputHint": "SingleLine" + }, + { + "name": "c", + "label": { + "en": "label_en" + }, + "required": false, + "type": { + "name": "String" + }, + "inputHint": "SingleLine" + }, + { + "name": "d", + "label": { + "en": "label_en" + }, + "required": false, + "type": { + "name": "String" + }, + "inputHint": "SingleLine" + } + ] +} \ No newline at end of file diff --git a/src/test/resources/com/commercetools/sync/types/utils/updatefielddefinitions/fields/type-with-field-definitions-abd.json b/src/test/resources/com/commercetools/sync/types/utils/updatefielddefinitions/fields/type-with-field-definitions-abd.json new file mode 100644 index 0000000000..01be09fcfc --- /dev/null +++ b/src/test/resources/com/commercetools/sync/types/utils/updatefielddefinitions/fields/type-with-field-definitions-abd.json @@ -0,0 +1,47 @@ +{ + "key": "key", + "name": { + "en": "name" + }, + "description": { + "en": "description" + }, + "resourceTypeIds": [ + "category" + ], + "fieldDefinitions": [ + { + "name": "a", + "label": { + "en": "label_en" + }, + "required": false, + "type": { + "name": "String" + }, + "inputHint": "SingleLine" + }, + { + "name": "b", + "label": { + "en": "label_en" + }, + "required": false, + "type": { + "name": "String" + }, + "inputHint": "SingleLine" + }, + { + "name": "d", + "label": { + "en": "label_en" + }, + "required": false, + "type": { + "name": "String" + }, + "inputHint": "SingleLine" + } + ] +} \ No newline at end of file diff --git a/src/test/resources/com/commercetools/sync/types/utils/updatefielddefinitions/fields/type-with-field-definitions-acbd.json b/src/test/resources/com/commercetools/sync/types/utils/updatefielddefinitions/fields/type-with-field-definitions-acbd.json new file mode 100644 index 0000000000..bb64b6c90f --- /dev/null +++ b/src/test/resources/com/commercetools/sync/types/utils/updatefielddefinitions/fields/type-with-field-definitions-acbd.json @@ -0,0 +1,58 @@ +{ + "key": "key", + "name": { + "en": "name" + }, + "description": { + "en": "description" + }, + "resourceTypeIds": [ + "category" + ], + "fieldDefinitions": [ + { + "name": "a", + "label": { + "en": "label_en" + }, + "required": false, + "type": { + "name": "String" + }, + "inputHint": "SingleLine" + }, + { + "name": "c", + "label": { + "en": "label_en" + }, + "required": false, + "type": { + "name": "String" + }, + "inputHint": "SingleLine" + }, + { + "name": "b", + "label": { + "en": "label_en" + }, + "required": false, + "type": { + "name": "String" + }, + "inputHint": "SingleLine" + }, + { + "name": "d", + "label": { + "en": "label_en" + }, + "required": false, + "type": { + "name": "String" + }, + "inputHint": "SingleLine" + } + ] +} \ No newline at end of file diff --git a/src/test/resources/com/commercetools/sync/types/utils/updatefielddefinitions/fields/type-with-field-definitions-adbc.json b/src/test/resources/com/commercetools/sync/types/utils/updatefielddefinitions/fields/type-with-field-definitions-adbc.json new file mode 100644 index 0000000000..cf9c2aa80d --- /dev/null +++ b/src/test/resources/com/commercetools/sync/types/utils/updatefielddefinitions/fields/type-with-field-definitions-adbc.json @@ -0,0 +1,58 @@ +{ + "key": "key", + "name": { + "en": "name" + }, + "description": { + "en": "description" + }, + "resourceTypeIds": [ + "category" + ], + "fieldDefinitions": [ + { + "name": "a", + "label": { + "en": "label_en" + }, + "required": false, + "type": { + "name": "String" + }, + "inputHint": "SingleLine" + }, + { + "name": "d", + "label": { + "en": "label_en" + }, + "required": false, + "type": { + "name": "String" + }, + "inputHint": "SingleLine" + }, + { + "name": "b", + "label": { + "en": "label_en" + }, + "required": false, + "type": { + "name": "String" + }, + "inputHint": "SingleLine" + }, + { + "name": "c", + "label": { + "en": "label_en" + }, + "required": false, + "type": { + "name": "String" + }, + "inputHint": "SingleLine" + } + ] +} \ No newline at end of file diff --git a/src/test/resources/com/commercetools/sync/types/utils/updatefielddefinitions/fields/type-with-field-definitions-cab.json b/src/test/resources/com/commercetools/sync/types/utils/updatefielddefinitions/fields/type-with-field-definitions-cab.json new file mode 100644 index 0000000000..982b8461d9 --- /dev/null +++ b/src/test/resources/com/commercetools/sync/types/utils/updatefielddefinitions/fields/type-with-field-definitions-cab.json @@ -0,0 +1,47 @@ +{ + "key": "key", + "name": { + "en": "name" + }, + "description": { + "en": "description" + }, + "resourceTypeIds": [ + "category" + ], + "fieldDefinitions": [ + { + "name": "c", + "label": { + "en": "label_en" + }, + "required": false, + "type": { + "name": "String" + }, + "inputHint": "SingleLine" + }, + { + "name": "a", + "label": { + "en": "label_en" + }, + "required": false, + "type": { + "name": "String" + }, + "inputHint": "SingleLine" + }, + { + "name": "b", + "label": { + "en": "label_en" + }, + "required": false, + "type": { + "name": "String" + }, + "inputHint": "SingleLine" + } + ] +} \ No newline at end of file diff --git a/src/test/resources/com/commercetools/sync/types/utils/updatefielddefinitions/fields/type-with-field-definitions-cb.json b/src/test/resources/com/commercetools/sync/types/utils/updatefielddefinitions/fields/type-with-field-definitions-cb.json new file mode 100644 index 0000000000..4551281012 --- /dev/null +++ b/src/test/resources/com/commercetools/sync/types/utils/updatefielddefinitions/fields/type-with-field-definitions-cb.json @@ -0,0 +1,36 @@ +{ + "key": "key", + "name": { + "en": "name" + }, + "description": { + "en": "description" + }, + "resourceTypeIds": [ + "category" + ], + "fieldDefinitions": [ + { + "name": "c", + "label": { + "en": "label_en" + }, + "required": false, + "type": { + "name": "String" + }, + "inputHint": "SingleLine" + }, + { + "name": "b", + "label": { + "en": "label_en" + }, + "required": false, + "type": { + "name": "String" + }, + "inputHint": "SingleLine" + } + ] +} \ No newline at end of file diff --git a/src/test/resources/com/commercetools/sync/types/utils/updatefielddefinitions/fields/type-with-field-definitions-cbd.json b/src/test/resources/com/commercetools/sync/types/utils/updatefielddefinitions/fields/type-with-field-definitions-cbd.json new file mode 100644 index 0000000000..de7de34d5c --- /dev/null +++ b/src/test/resources/com/commercetools/sync/types/utils/updatefielddefinitions/fields/type-with-field-definitions-cbd.json @@ -0,0 +1,47 @@ +{ + "key": "key", + "name": { + "en": "name" + }, + "description": { + "en": "description" + }, + "resourceTypeIds": [ + "category" + ], + "fieldDefinitions": [ + { + "name": "c", + "label": { + "en": "label_en" + }, + "required": false, + "type": { + "name": "String" + }, + "inputHint": "SingleLine" + }, + { + "name": "b", + "label": { + "en": "label_en" + }, + "required": false, + "type": { + "name": "String" + }, + "inputHint": "SingleLine" + }, + { + "name": "d", + "label": { + "en": "label_en" + }, + "required": false, + "type": { + "name": "String" + }, + "inputHint": "SingleLine" + } + ] +} \ No newline at end of file From f6e2bc78e54d99195d3d18af070be6ec8fd8da33 Mon Sep 17 00:00:00 2001 From: aoz Date: Thu, 20 Sep 2018 13:53:16 +0200 Subject: [PATCH 005/164] #300: suppress warning coming from findBugs --- src/main/java/com/commercetools/sync/types/TypeSync.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/com/commercetools/sync/types/TypeSync.java b/src/main/java/com/commercetools/sync/types/TypeSync.java index 0bfe3964b7..e6b2256190 100644 --- a/src/main/java/com/commercetools/sync/types/TypeSync.java +++ b/src/main/java/com/commercetools/sync/types/TypeSync.java @@ -4,6 +4,7 @@ import com.commercetools.sync.services.TypeService; import com.commercetools.sync.services.impl.TypeServiceImpl; import com.commercetools.sync.types.helpers.TypeSyncStatistics; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import io.sphere.sdk.commands.UpdateAction; import io.sphere.sdk.models.WithKey; import io.sphere.sdk.types.Type; @@ -246,6 +247,7 @@ private CompletionStage createType(@Nonnull final TypeDraft typeDraft) { * @param newType draft containing data that could differ from data in {@code oldType}. * @return a future which contains an empty result after execution of the update. */ + @SuppressFBWarnings("NP_NONNULL_PARAM_VIOLATION") // https://github.com/findbugsproject/findbugs/issues/79 private CompletionStage updateType(@Nonnull final Type oldType, @Nonnull final TypeDraft newType) { From c2bb617104471a8269fdc2b66916e3085f6c85d9 Mon Sep 17 00:00:00 2001 From: aoz Date: Fri, 21 Sep 2018 14:46:55 +0200 Subject: [PATCH 006/164] #300: documentation and benchmark for type sync. --- README.md | 1 + docs/usage/TYPE_SYNC.md | 120 ++++++++ .../sync/benchmark/TypeSyncBenchmark.java | 277 ++++++++++++++++++ 3 files changed, 398 insertions(+) create mode 100644 docs/usage/TYPE_SYNC.md create mode 100644 src/benchmark/java/com/commercetools/sync/benchmark/TypeSyncBenchmark.java diff --git a/README.md b/README.md index 3c3b7e0fa2..cf28600b2d 100644 --- a/README.md +++ b/README.md @@ -44,6 +44,7 @@ Currently this library supports synchronising - [Products](/docs/usage/PRODUCT_SYNC.md) (_Beta_: Not recommended for production use yet.) - [InventoryEntries](/docs/usage/INVENTORY_SYNC.md) (_Beta_: Not recommended for production use yet.) - [ProductTypes](/docs/usage/PRODUCT_TYPE_SYNC.md) (_Beta_: Not recommended for production use yet.) + - [Types](/docs/usage/TYPE_SYNC.md) (_Beta_: Not recommended for production use yet.) ### Prerequisites diff --git a/docs/usage/TYPE_SYNC.md b/docs/usage/TYPE_SYNC.md new file mode 100644 index 0000000000..47e63aef0a --- /dev/null +++ b/docs/usage/TYPE_SYNC.md @@ -0,0 +1,120 @@ +# commercetools type sync + +Utility which provides API for building CTP type update actions and type synchronisation. + + + + + +- [Usage](#usage) + - [Sync list of type drafts](#sync-list-of-type-drafts) + - [Prerequisites](#prerequisites) + - [Running the sync](#running-the-sync) + - [Build all update actions](#build-all-update-actions) + - [Build particular update action(s)](#build-particular-update-actions) +- [Caveats](#caveats) + + + +## Usage + +### Sync list of type drafts + +#### Prerequisites +1. The sync expects a list of non-null `TypeDrafts` objects that have their `key` fields set to match the +types from the source to the target. Also the target project is expected to have the `key` fields set, otherwise they won't be +matched. +2. It is an important responsibility of the user of the library to instantiate a `sphereClient` that has the following properties: + - Limits the amount of concurrent requests done to CTP. This can be done by decorating the `sphereClient` with + [QueueSphereClientDecorator](http://commercetools.github.io/commercetools-jvm-sdk/apidocs/io/sphere/sdk/client/QueueSphereClientDecorator.html) + - Retries on 5xx errors with a retry strategy. This can be achieved by decorating the `sphereClient` with the + [RetrySphereClientDecorator](http://commercetools.github.io/commercetools-jvm-sdk/apidocs/io/sphere/sdk/client/RetrySphereClientDecorator.html) + + You can use the same client instantiating used in the integration tests for this library found + [here](/src/main/java/com/commercetools/sync/commons/utils/ClientConfigurationUtils.java#L45). + +4. After the `sphereClient` is setup, a `TypeSyncOptions` should be be built as follows: +````java +// instantiating a TypeSyncOptions +final TypeSyncOptions typeSyncOptions = TypeSyncOptionsBuilder.of(sphereClient).build(); +```` + +The options can be used to provide additional optional configuration for the sync as well: +- `errorCallBack` +a callback that is called whenever an event occurs during the sync process that represents an error. Currently, these +events. + +- `warningCallBack` +a callback that is called whenever an event occurs during the sync process that represents a warning. Currently, these +events. + +- `beforeUpdateCallback` +a filter function which can be applied on a generated list of update actions. It allows the user to intercept type +update and modify (add/remove) update actions just before they are send to CTP API. + +- `beforeCreateCallback` +a filter function which can be applied on a type draft before a request to create it on CTP is issued. It allows the +user to intercept type create requests modify the draft before the create request is sent to CTP API. + +- `allowUuid` +a flag, if set to `true`, enables the user to use keys with UUID format for references. By default, it is set to `false`. + +Example of options usage, that sets the error and warning callbacks to output the message to the log error and warning +streams, would look as follows: +```java +final Logger logger = LoggerFactory.getLogger(MySync.class); +final TypeSyncOptions typeSyncOptions = TypeSyncOptionsBuilder.of(sphereClient) + .errorCallBack(logger::error) + .warningCallBack(logger::warn) + .build(); +``` + +#### Running the sync +After all the aforementioned points in the previous section have been fulfilled, to run the sync: +````java +// instantiating a type sync +final TypeSync typeSync = new TypeSync(typeSyncOptions); + +// execute the sync on your list of types +CompletionStage syncStatisticsStage = typeSync.sync(typeDrafts); +```` +The result of the completing the `syncStatisticsStage` in the previous code snippet contains a `TypeSyncStatistics` +which contains all the stats of the sync process; which includes a report message, the total number of updated, created, +failed, processed types and the processing time of the last sync batch in different time units and in a +human readable format. + +````java +final TypeSyncStatistics stats = syncStatisticsStage.toCompletebleFuture().join(); +stats.getReportMessage(); +/*"Summary: 2000 types were processed in total (1000 created, 995 updated, 5 failed to sync)."*/ +```` + +__Note__ The statistics object contains the processing time of the last batch only. This is due to two reasons: + 1. The sync processing time should not take into account the time between supplying batches to the sync. + 2. It is not not known by the sync which batch is going to be the last one supplied. + +More examples of how to use the sync can be found [here](/src/integration-test/java/com/commercetools/sync/integration/types/TypeSyncIT.java). + +### Build all update actions + +A utility method provided by the library to compare a `Type` with a new `TypeDraft` and results in a list of type update actions. +```java +List> updateActions = TypeSyncUtils.buildActions(type, typeDraft, typeSyncOptions); +``` + +### Build particular update action(s) + +Utility methods provided by the library to compare the specific fields of a `Type` and a new `TypeDraft`, and in turn builds + the update action. One example is the `buildChangeNameUpdateAction` which compares names: +````java +Optional> updateAction = TypeUpdateActionUtils.buildChangeNameAction(oldType, typeDraft); +```` +More examples of those utils for different fields can be found [here](/src/test/java/com/commercetools/sync/types/utils/TypeUpdateActionUtilsTest.java). + + +## Caveats + +1. Types are either created or updated. Currently the tool does not support type deletion. +2. Updating the label of enum values and localized enum values of field definition is not supported yet. +3. Removing the enum values from the field definition is not supported yet. +4. Updating the input hint of field definition is not supported yet. \ No newline at end of file diff --git a/src/benchmark/java/com/commercetools/sync/benchmark/TypeSyncBenchmark.java b/src/benchmark/java/com/commercetools/sync/benchmark/TypeSyncBenchmark.java new file mode 100644 index 0000000000..538070d158 --- /dev/null +++ b/src/benchmark/java/com/commercetools/sync/benchmark/TypeSyncBenchmark.java @@ -0,0 +1,277 @@ +package com.commercetools.sync.benchmark; + +import com.commercetools.sync.commons.asserts.statistics.AssertionsForStatistics; +import com.commercetools.sync.commons.utils.SyncSolutionInfo; +import com.commercetools.sync.types.TypeSync; +import com.commercetools.sync.types.TypeSyncOptions; +import com.commercetools.sync.types.TypeSyncOptionsBuilder; +import com.commercetools.sync.types.helpers.TypeSyncStatistics; +import io.sphere.sdk.models.LocalizedString; +import io.sphere.sdk.queries.PagedQueryResult; +import io.sphere.sdk.types.FieldDefinition; +import io.sphere.sdk.types.ResourceTypeIdsSetBuilder; +import io.sphere.sdk.types.TypeDraft; +import io.sphere.sdk.types.TypeDraftBuilder; +import io.sphere.sdk.types.commands.TypeCreateCommand; +import io.sphere.sdk.types.queries.TypeQuery; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionStage; +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +import static com.commercetools.sync.benchmark.BenchmarkUtils.CREATES_ONLY; +import static com.commercetools.sync.benchmark.BenchmarkUtils.NUMBER_OF_RESOURCE_UNDER_TEST; +import static com.commercetools.sync.benchmark.BenchmarkUtils.THRESHOLD; +import static com.commercetools.sync.benchmark.BenchmarkUtils.TYPE_SYNC; +import static com.commercetools.sync.benchmark.BenchmarkUtils.UPDATES_ONLY; +import static com.commercetools.sync.benchmark.BenchmarkUtils.calculateDiff; +import static com.commercetools.sync.benchmark.BenchmarkUtils.saveNewResult; +import static com.commercetools.sync.integration.commons.utils.ITUtils.deleteTypesFromTargetAndSource; +import static com.commercetools.sync.integration.commons.utils.SphereClientUtils.CTP_TARGET_CLIENT; +import static com.commercetools.sync.integration.types.utils.TypeITUtils.FIELD_DEFINITION_1; +import static com.commercetools.sync.integration.types.utils.TypeITUtils.FIELD_DEFINITION_NAME_1; +import static com.commercetools.sync.integration.types.utils.TypeITUtils.deleteTypes; +import static com.commercetools.tests.utils.CompletionStageUtil.executeBlocking; +import static java.lang.String.format; +import static java.util.Collections.singletonList; +import static org.assertj.core.api.Assertions.assertThat; + +public class TypeSyncBenchmark { + + private TypeSyncOptions typeSyncOptions; + private List errorCallBackMessages; + private List warningCallBackMessages; + private List errorCallBackExceptions; + + @BeforeClass + public static void setup() { + deleteTypesFromTargetAndSource(); + } + + @AfterClass + public static void tearDown() { + deleteTypes(CTP_TARGET_CLIENT); + } + + @Before + public void setupTest() { + clearSyncTestCollections(); + deleteTypes(CTP_TARGET_CLIENT); + typeSyncOptions = buildSyncOptions(); + } + + private TypeSyncOptions buildSyncOptions() { + final BiConsumer errorCallBack = (errorMessage, exception) -> { + errorCallBackMessages.add(errorMessage); + errorCallBackExceptions.add(exception); + }; + final Consumer warningCallBack = warningMessage -> warningCallBackMessages.add(warningMessage); + + return TypeSyncOptionsBuilder.of(CTP_TARGET_CLIENT) + .errorCallback(errorCallBack) + .warningCallback(warningCallBack) + .build(); + } + + private void clearSyncTestCollections() { + errorCallBackMessages = new ArrayList<>(); + errorCallBackExceptions = new ArrayList<>(); + warningCallBackMessages = new ArrayList<>(); + } + + @Test + public void sync_NewTypes_ShouldCreateTypes() throws IOException { + final List typeDrafts = buildTypeDrafts(NUMBER_OF_RESOURCE_UNDER_TEST); + + // Sync drafts + final TypeSync typeSync = new TypeSync(typeSyncOptions); + + final long beforeSyncTime = System.currentTimeMillis(); + final TypeSyncStatistics syncStatistics = executeBlocking(typeSync.sync(typeDrafts)); + final long totalTime = System.currentTimeMillis() - beforeSyncTime; + + final double diff = calculateDiff(SyncSolutionInfo.LIB_VERSION, TYPE_SYNC, CREATES_ONLY, totalTime); + assertThat(diff) + .withFailMessage(format("Diff of benchmark '%e' is longer than expected threshold of '%d'.", + diff, THRESHOLD)) + .isLessThanOrEqualTo(THRESHOLD); + + // Assert actual state of CTP project (total number of existing types) + final CompletableFuture totalNumberOfTypes = + CTP_TARGET_CLIENT.execute(TypeQuery.of()) + .thenApply(PagedQueryResult::getTotal) + .thenApply(Long::intValue) + .toCompletableFuture(); + + executeBlocking(totalNumberOfTypes); + assertThat(totalNumberOfTypes).isCompletedWithValue(NUMBER_OF_RESOURCE_UNDER_TEST); + + + AssertionsForStatistics + .assertThat(syncStatistics) + .hasValues(NUMBER_OF_RESOURCE_UNDER_TEST, NUMBER_OF_RESOURCE_UNDER_TEST, 0, 0); + assertThat(errorCallBackExceptions).isEmpty(); + assertThat(errorCallBackMessages).isEmpty(); + assertThat(warningCallBackMessages).isEmpty(); + + saveNewResult(SyncSolutionInfo.LIB_VERSION, TYPE_SYNC, CREATES_ONLY, totalTime); + } + + @Test + public void sync_ExistingTypes_ShouldUpdateTypes() throws IOException { + final List typeDrafts = buildTypeDrafts(NUMBER_OF_RESOURCE_UNDER_TEST); + + // Create drafts to target project with different field type name + CompletableFuture.allOf(typeDrafts.stream() + .map(TypeDraftBuilder::of) + .map(TypeSyncBenchmark::applyFieldDefinitionNameChange) + .map(TypeDraftBuilder::build) + .map(draft -> CTP_TARGET_CLIENT.execute(TypeCreateCommand.of(draft))) + .map(CompletionStage::toCompletableFuture) + .toArray(CompletableFuture[]::new)) + .join(); + + // Sync new drafts + final TypeSync typeSync = new TypeSync(typeSyncOptions); + + final long beforeSyncTime = System.currentTimeMillis(); + final TypeSyncStatistics syncStatistics = executeBlocking(typeSync.sync(typeDrafts)); + final long totalTime = System.currentTimeMillis() - beforeSyncTime; + + // Calculate time taken for benchmark and assert it lies within threshold + final double diff = calculateDiff(SyncSolutionInfo.LIB_VERSION, TYPE_SYNC, CREATES_ONLY, totalTime); + assertThat(diff) + .withFailMessage(format("Diff of benchmark '%e' is longer than expected threshold of '%d'.", + diff, THRESHOLD)) + .isLessThanOrEqualTo(THRESHOLD); + + // Assert actual state of CTP project (number of updated types) + final CompletableFuture totalNumberOfUpdatedTypes = + CTP_TARGET_CLIENT.execute(TypeQuery.of() + .withPredicates(p -> p.fieldDefinitions().name().is(FIELD_DEFINITION_NAME_1))) + .thenApply(PagedQueryResult::getTotal) + .thenApply(Long::intValue) + .toCompletableFuture(); + + executeBlocking(totalNumberOfUpdatedTypes); + assertThat(totalNumberOfUpdatedTypes).isCompletedWithValue(NUMBER_OF_RESOURCE_UNDER_TEST); + + // Assert actual state of CTP project (total number of existing types) + final CompletableFuture totalNumberOfTypes = + CTP_TARGET_CLIENT.execute(TypeQuery.of()) + .thenApply(PagedQueryResult::getTotal) + .thenApply(Long::intValue) + .toCompletableFuture(); + executeBlocking(totalNumberOfTypes); + assertThat(totalNumberOfTypes).isCompletedWithValue(NUMBER_OF_RESOURCE_UNDER_TEST); + + // Assert statistics + AssertionsForStatistics + .assertThat(syncStatistics) + .hasValues(NUMBER_OF_RESOURCE_UNDER_TEST, 0, NUMBER_OF_RESOURCE_UNDER_TEST, 0); + + assertThat(errorCallBackExceptions).isEmpty(); + assertThat(errorCallBackMessages).isEmpty(); + assertThat(warningCallBackMessages).isEmpty(); + + saveNewResult(SyncSolutionInfo.LIB_VERSION, TYPE_SYNC, UPDATES_ONLY, totalTime); + } + + @Test + public void sync_WithSomeExistingTypes_ShouldSyncTypes() throws IOException { + final List typeDrafts = buildTypeDrafts(NUMBER_OF_RESOURCE_UNDER_TEST); + final int halfNumberOfDrafts = typeDrafts.size() / 2; + final List firstHalf = typeDrafts.subList(0, halfNumberOfDrafts); + + // Create first half of drafts to target project with different field definition name + CompletableFuture.allOf(firstHalf.stream() + .map(TypeDraftBuilder::of) + .map(TypeSyncBenchmark::applyFieldDefinitionNameChange) + .map(TypeDraftBuilder::build) + .map(draft -> CTP_TARGET_CLIENT.execute(TypeCreateCommand.of(draft))) + .map(CompletionStage::toCompletableFuture) + .toArray(CompletableFuture[]::new)) + .join(); + + // Sync new drafts + final TypeSync typeSync = new TypeSync(typeSyncOptions); + + final long beforeSyncTime = System.currentTimeMillis(); + final TypeSyncStatistics syncStatistics = executeBlocking(typeSync.sync(typeDrafts)); + final long totalTime = System.currentTimeMillis() - beforeSyncTime; + + // Calculate time taken for benchmark and assert it lies within threshold + final double diff = calculateDiff(SyncSolutionInfo.LIB_VERSION, TYPE_SYNC, CREATES_ONLY, totalTime); + assertThat(diff) + .withFailMessage(format("Diff of benchmark '%e' is longer than expected threshold of '%d'.", + diff, THRESHOLD)) + .isLessThanOrEqualTo(THRESHOLD); + + // Assert actual state of CTP project (number of updated types) + final CompletableFuture totalNumberOfUpdatedTypes = + CTP_TARGET_CLIENT.execute(TypeQuery.of() + .withPredicates(p -> p.fieldDefinitions().name().is(FIELD_DEFINITION_NAME_1 + "_old"))) + .thenApply(PagedQueryResult::getTotal) + .thenApply(Long::intValue) + .toCompletableFuture(); + + executeBlocking(totalNumberOfUpdatedTypes); + assertThat(totalNumberOfUpdatedTypes).isCompletedWithValue(0); + + // Assert actual state of CTP project (total number of existing types) + final CompletableFuture totalNumberOfTypes = + CTP_TARGET_CLIENT.execute(TypeQuery.of()) + .thenApply(PagedQueryResult::getTotal) + .thenApply(Long::intValue) + .toCompletableFuture(); + executeBlocking(totalNumberOfTypes); + assertThat(totalNumberOfTypes).isCompletedWithValue(NUMBER_OF_RESOURCE_UNDER_TEST); + + // Assert statistics + AssertionsForStatistics + .assertThat(syncStatistics) + .hasValues(NUMBER_OF_RESOURCE_UNDER_TEST, halfNumberOfDrafts, halfNumberOfDrafts, 0); + + assertThat(errorCallBackExceptions).isEmpty(); + assertThat(errorCallBackMessages).isEmpty(); + assertThat(warningCallBackMessages).isEmpty(); + + saveNewResult(SyncSolutionInfo.LIB_VERSION, TYPE_SYNC, UPDATES_ONLY, totalTime); + } + + private List buildTypeDrafts(final int numberOfTypes) { + return IntStream + .range(0, numberOfTypes) + .mapToObj(i -> TypeDraftBuilder.of( + "key__" + Integer.toString(i), + LocalizedString.ofEnglish("name__" + Integer.toString(i)), + ResourceTypeIdsSetBuilder.of().addCategories().build()) + .description(LocalizedString.ofEnglish("newDescription")) + .fieldDefinitions(singletonList(FIELD_DEFINITION_1)) + .build()) + .collect(Collectors.toList()); + } + + private static TypeDraftBuilder applyFieldDefinitionNameChange(final TypeDraftBuilder builder) { + final List list = builder.getFieldDefinitions() + .stream() + .map(fieldDefinition -> FieldDefinition.of(fieldDefinition.getType(), + fieldDefinition.getName() + "_old", + fieldDefinition.getLabel(), + fieldDefinition.isRequired(), + fieldDefinition.getInputHint())) + .collect(Collectors.toList()); + + return builder.fieldDefinitions(list); + } +} From 5d1fc58f2a87330ac40662208f26db0a441b420a Mon Sep 17 00:00:00 2001 From: aoz Date: Mon, 24 Sep 2018 12:07:49 +0200 Subject: [PATCH 007/164] #300: add constant to type sync benchmark util --- .../java/com/commercetools/sync/benchmark/BenchmarkUtils.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/benchmark/java/com/commercetools/sync/benchmark/BenchmarkUtils.java b/src/benchmark/java/com/commercetools/sync/benchmark/BenchmarkUtils.java index 6bd43806f6..d7089689dc 100644 --- a/src/benchmark/java/com/commercetools/sync/benchmark/BenchmarkUtils.java +++ b/src/benchmark/java/com/commercetools/sync/benchmark/BenchmarkUtils.java @@ -35,6 +35,7 @@ public class BenchmarkUtils { static final String PRODUCT_SYNC = "productSync"; static final String INVENTORY_SYNC = "inventorySync"; static final String CATEGORY_SYNC = "categorySync"; + static final String TYPE_SYNC = "typeSync"; static final String CREATES_ONLY = "createsOnly"; static final String UPDATES_ONLY = "updatesOnly"; static final String CREATES_AND_UPDATES = "mix"; From 62923b5d52e00df92aabd70ec4a7ee1eb3c6d922 Mon Sep 17 00:00:00 2001 From: aoz Date: Mon, 24 Sep 2018 12:12:13 +0200 Subject: [PATCH 008/164] #300: fix checkstyleBenchmark --- .../com/commercetools/sync/benchmark/TypeSyncBenchmark.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/benchmark/java/com/commercetools/sync/benchmark/TypeSyncBenchmark.java b/src/benchmark/java/com/commercetools/sync/benchmark/TypeSyncBenchmark.java index 538070d158..ef06330a83 100644 --- a/src/benchmark/java/com/commercetools/sync/benchmark/TypeSyncBenchmark.java +++ b/src/benchmark/java/com/commercetools/sync/benchmark/TypeSyncBenchmark.java @@ -60,7 +60,7 @@ public static void setup() { @AfterClass public static void tearDown() { - deleteTypes(CTP_TARGET_CLIENT); + deleteTypes(CTP_TARGET_CLIENT); } @Before From 973cb093d238427d79b6b3cfd01bf92475b5593e Mon Sep 17 00:00:00 2001 From: aoz Date: Mon, 24 Sep 2018 14:29:40 +0200 Subject: [PATCH 009/164] #300: updates releases notes. --- docs/RELEASE_NOTES.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/RELEASE_NOTES.md b/docs/RELEASE_NOTES.md index a76c72540c..370ea1eb03 100644 --- a/docs/RELEASE_NOTES.md +++ b/docs/RELEASE_NOTES.md @@ -56,6 +56,8 @@ [Javadoc](https://commercetools.github.io/commercetools-sync-java/v/v1.0.0-M14/) | [Jar](https://bintray.com/commercetools/maven/commercetools-sync-java/v1.0.0-M14) +**New Features** (1) +- **Type Sync** - Support for syncing types. [#300](https://github.com/commercetools/commercetools-sync-java/issues/300) For more info how to use it please refer to [Type usage doc](/docs/usage/TYPE_SYNC.md). **Enhancements** (6) - **Product Sync** - Products create and update requests are now issued in parallel. This should lead to a performance improvement. [#238](https://github.com/commercetools/commercetools-sync-java/issues/238) From f4fc41525cbd101ff25de148431849084815c98a Mon Sep 17 00:00:00 2001 From: aoz Date: Tue, 25 Sep 2018 15:46:03 +0200 Subject: [PATCH 010/164] #300: PR review changes, mostly documentation and indentions. --- docs/usage/TYPE_SYNC.md | 11 +++--- .../sync/integration/types/TypeSyncIT.java | 8 +++-- .../sync/services/TypeService.java | 12 +++---- .../sync/services/impl/TypeServiceImpl.java | 4 +-- .../commercetools/sync/types/TypeSync.java | 21 +++++------ .../FieldDefinitionUpdateActionUtils.java | 20 +++++------ .../types/utils/TypeUpdateActionUtils.java | 17 ++++----- .../utils/TypeUpdateEnumActionsUtils.java | 2 +- .../TypeUpdateFieldDefinitionActionUtils.java | 35 +++++++++---------- .../TypeUpdateLocalizedEnumActionUtils.java | 15 +------- .../utils/TypeUpdatePlainEnumActionUtils.java | 13 ------- .../BuildLocalizedEnumUpdateActionsTest.java | 6 ++-- 12 files changed, 64 insertions(+), 100 deletions(-) diff --git a/docs/usage/TYPE_SYNC.md b/docs/usage/TYPE_SYNC.md index 47e63aef0a..4173ece7c3 100644 --- a/docs/usage/TYPE_SYNC.md +++ b/docs/usage/TYPE_SYNC.md @@ -1,6 +1,6 @@ # commercetools type sync -Utility which provides API for building CTP type update actions and type synchronisation. +Utility which provides an API for building CTP type update actions and type synchronisation. @@ -30,7 +30,7 @@ matched. - Retries on 5xx errors with a retry strategy. This can be achieved by decorating the `sphereClient` with the [RetrySphereClientDecorator](http://commercetools.github.io/commercetools-jvm-sdk/apidocs/io/sphere/sdk/client/RetrySphereClientDecorator.html) - You can use the same client instantiating used in the integration tests for this library found + You can use the same client instance in the integration tests for this library found [here](/src/main/java/com/commercetools/sync/commons/utils/ClientConfigurationUtils.java#L45). 4. After the `sphereClient` is setup, a `TypeSyncOptions` should be be built as follows: @@ -109,12 +109,13 @@ Utility methods provided by the library to compare the specific fields of a `Typ ````java Optional> updateAction = TypeUpdateActionUtils.buildChangeNameAction(oldType, typeDraft); ```` -More examples of those utils for different fields can be found [here](/src/test/java/com/commercetools/sync/types/utils/TypeUpdateActionUtilsTest.java). - +More examples of those utils for different types can be found [here](/src/test/java/com/commercetools/sync/types/utils/TypeUpdateActionUtilsTest.java). +and field definitions can be found [here](/src/test/java/com/commercetools/sync/types/utils/FieldDefinitionUpdateActionUtilsTest.java). ## Caveats 1. Types are either created or updated. Currently the tool does not support type deletion. 2. Updating the label of enum values and localized enum values of field definition is not supported yet. 3. Removing the enum values from the field definition is not supported yet. -4. Updating the input hint of field definition is not supported yet. \ No newline at end of file +4. Updating the input hint of field definition is not supported yet. +5. Updating the reference and field definition type is not supported yet. \ No newline at end of file diff --git a/src/integration-test/java/com/commercetools/sync/integration/types/TypeSyncIT.java b/src/integration-test/java/com/commercetools/sync/integration/types/TypeSyncIT.java index 6d0f994cd9..89c28698b5 100644 --- a/src/integration-test/java/com/commercetools/sync/integration/types/TypeSyncIT.java +++ b/src/integration-test/java/com/commercetools/sync/integration/types/TypeSyncIT.java @@ -465,7 +465,8 @@ public void sync_WithoutKey_ShouldExecuteCallbackOnErrorAndIncreaseFailedCounter .sync(singletonList(newTypeDraft)) .toCompletableFuture().join(); - verify(spyTypeSyncOptions).applyErrorCallback("Failed to process type draft without key.", null); + verify(spyTypeSyncOptions) + .applyErrorCallback("Failed to process type draft without key.", null); AssertionsForStatistics.assertThat(typeSyncStatistics).hasValues(1, 0, 0, 1); } @@ -684,7 +685,8 @@ public void sync_WithSeveralBatches_ShouldReturnProperStatistics() { .sync(typeDrafts) .toCompletableFuture().join(); - AssertionsForStatistics.assertThat(typeSyncStatistics).hasValues(100, 100, 0, 0); + AssertionsForStatistics.assertThat(typeSyncStatistics) + .hasValues(100, 100, 0, 0); } @Test @@ -780,7 +782,7 @@ public void sync_beforeUpdate_ShouldNotCallBeforeCreateCallback() { } private static void assertFieldDefinitionsAreEqual(@Nonnull final List oldFields, - @Nonnull final List newFields) { + @Nonnull final List newFields) { IntStream.range(0, newFields.size()) .forEach(index -> { diff --git a/src/main/java/com/commercetools/sync/services/TypeService.java b/src/main/java/com/commercetools/sync/services/TypeService.java index 797c928842..4395920c86 100644 --- a/src/main/java/com/commercetools/sync/services/TypeService.java +++ b/src/main/java/com/commercetools/sync/services/TypeService.java @@ -35,8 +35,8 @@ public interface TypeService { /** * Queries existing {@link Type}'s against set of keys. * - * @param keys {@link List} of sku values, used in search predicate - * @return {@link List} of matching types or empty list when there was not type of key matching to + * @param keys {@link Set} of sku values, used in search predicate + * @return {@link CompletionStage} of matching types or empty list when there is no type with corresponding * {@code keys}. */ @Nonnull @@ -47,19 +47,19 @@ public interface TypeService { * Creates new type from {@code typeDraft}. * * @param typeDraft draft with data for new type - * @return {@link CompletionStage} with created {@link Type} or an exception + * @return {@link CompletionStage} with created {@link Type}. */ @Nonnull CompletionStage createType(@Nonnull final TypeDraft typeDraft); /** - * Updates existing product type with {@code updateActions}. + * Updates existing type with {@code updateActions}. * * @param type type that should be updated * @param updateActions {@link List} of actions that should be applied to {@code type} - * @return {@link CompletionStage} with updated {@link Type} or an exception + * @return {@link CompletionStage} with updated {@link Type}. */ @Nonnull CompletionStage updateType(@Nonnull final Type type, - @Nonnull final List> updateActions); + @Nonnull final List> updateActions); } diff --git a/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java b/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java index 19c2931db4..007688d28e 100644 --- a/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java +++ b/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java @@ -45,7 +45,7 @@ public TypeServiceImpl(@Nonnull final BaseSyncOptions syncOptions) { @Override public CompletionStage> fetchCachedTypeId(@Nonnull final String key) { if (!isCached) { - return fetchAndCache(key); + return cacheAndFetch(key); } return CompletableFuture.completedFuture(Optional.ofNullable(keyToIdCache.get(key))); } @@ -75,7 +75,7 @@ public CompletionStage updateType(@Nonnull final Type type, } @Nonnull - private CompletionStage> fetchAndCache(@Nonnull final String key) { + private CompletionStage> cacheAndFetch(@Nonnull final String key) { final Consumer> typePageConsumer = typesPage -> typesPage.forEach(type -> keyToIdCache.put(type.getKey(), type.getId())); diff --git a/src/main/java/com/commercetools/sync/types/TypeSync.java b/src/main/java/com/commercetools/sync/types/TypeSync.java index e6b2256190..90e643161d 100644 --- a/src/main/java/com/commercetools/sync/types/TypeSync.java +++ b/src/main/java/com/commercetools/sync/types/TypeSync.java @@ -65,11 +65,8 @@ public TypeSync(@Nonnull final TypeSyncOptions typeSyncOptions, * processes performed by this sync instance. */ @Override - protected CompletionStage process( - @Nonnull final List typeDrafts) { - + protected CompletionStage process(@Nonnull final List typeDrafts) { final List> batches = batchElements(typeDrafts, syncOptions.getBatchSize()); - return syncBatches(batches, CompletableFuture.completedFuture(statistics)); } @@ -90,16 +87,14 @@ protected CompletionStage syncBatches( * Fetches existing {@link Type} objects from CTP project that correspond to passed {@code batch}. * Having existing types fetched, {@code batch} is compared and synced with fetched objects by * {@link TypeSync#syncBatch(List, List)} function. When fetching existing types results in - * an empty optional then {@code batch} isn't processed. + * an empty updated/created statistics then {@code batch} isn't processed. * * @param batch batch of drafts that need to be synced - * @return {@link CompletionStage} of {@link Void} that indicates method progress. + * @return {@link CompletionStage} of {@link TypeSyncStatistics} that indicates method progress. */ @Override protected CompletionStage processBatch(@Nonnull final List batch) { - final List validTypeDrafts = batch.stream() - .filter(this::validateDraft) - .collect(toList()); + final List validTypeDrafts = batch.stream().filter(this::validateDraft).collect(toList()); if (validTypeDrafts.isEmpty()) { statistics.incrementProcessed(batch.size()); @@ -140,7 +135,7 @@ private boolean validateDraft(@Nullable final TypeDraft draft) { * Given a set of type keys, fetches the corresponding types from CTP if they exist. * * @param keys the keys of the types that are wanted to be fetched. - * @return a future which contains the list of types corresponding to the keys. + * @return a {@link CompletionStage} which contains the list of types corresponding to the keys. */ private CompletionStage> fetchExistingTypes(@Nonnull final Set keys) { return typeService @@ -188,7 +183,7 @@ private void handleError(@Nonnull final String errorMessage, @Nullable final Thr * * @param oldTypes old types. * @param newTypes drafts that need to be synced. - * @return a future which contains an empty result after execution of the update + * @return a {@link CompletionStage} which contains an empty result after execution of the update */ private CompletionStage syncBatch( @Nonnull final List oldTypes, @@ -216,7 +211,7 @@ private CompletionStage syncBatch( * is called. * * @param typeDraft the type draft to create the type from. - * @return a future which contains an empty result after execution of the create. + * @return a {@link CompletionStage} which contains an empty result after execution of the create. */ private CompletionStage createType(@Nonnull final TypeDraft typeDraft) { return syncOptions.applyBeforeCreateCallBack(typeDraft) @@ -245,7 +240,7 @@ private CompletionStage createType(@Nonnull final TypeDraft typeDraft) { * * @param oldType existing type that could be updated. * @param newType draft containing data that could differ from data in {@code oldType}. - * @return a future which contains an empty result after execution of the update. + * @return a {@link CompletionStage} which contains an empty result after execution of the update. */ @SuppressFBWarnings("NP_NONNULL_PARAM_VIOLATION") // https://github.com/findbugsproject/findbugs/issues/79 private CompletionStage updateType(@Nonnull final Type oldType, diff --git a/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionUpdateActionUtils.java b/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionUpdateActionUtils.java index eff5458304..4161a010f7 100644 --- a/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionUpdateActionUtils.java +++ b/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionUpdateActionUtils.java @@ -22,13 +22,13 @@ public final class FieldDefinitionUpdateActionUtils { /** - * Compares all the fields of an {@link FieldDefinition} and an {@link FieldDefinition} and returns + * Compares all the fields of old {@link FieldDefinition} with new {@link FieldDefinition} and returns * a list of {@link UpdateAction}<{@link Type}> as a result. If both the {@link FieldDefinition} * and the {@link FieldDefinition} have identical fields, then no update action is needed and hence an * empty {@link List} is returned. * - * @param oldFieldDefinition the field definition which should be updated. - * @param newFieldDefinition the field definition draft where we get the new fields. + * @param oldFieldDefinition the old field definition which should be updated. + * @param newFieldDefinition the new field definition where we get the new fields. * @return A list with the update actions or an empty list if the field definition fields are identical. */ @Nonnull @@ -36,12 +36,8 @@ public static List> buildActions( @Nonnull final FieldDefinition oldFieldDefinition, @Nonnull final FieldDefinition newFieldDefinition) { - final List> updateActions; - - updateActions = Stream - .of( - buildChangeLabelUpdateAction(oldFieldDefinition, newFieldDefinition) - ) + final List> updateActions = Stream.of( + buildChangeLabelUpdateAction(oldFieldDefinition, newFieldDefinition)) .filter(Optional::isPresent) .map(Optional::get) .collect(toList()); @@ -86,13 +82,13 @@ private static boolean isLocalizedEnumField(@Nonnull final FieldDefinition field } /** - * Compares the {@link LocalizedString} labels of an {@link FieldDefinition} and an + * Compares the {@link LocalizedString} labels of old {@link FieldDefinition} with new * {@link FieldDefinition} and returns an {@link UpdateAction}<{@link Type}> as a result in * an {@link Optional}. If both the {@link FieldDefinition} and the {@link FieldDefinition} have the * same label, then no update action is needed and hence an empty {@link Optional} is returned. * - * @param oldFieldDefinition the field definition which should be updated. - * @param newFieldDefinition the field definition draft where we get the new label. + * @param oldFieldDefinition the old field definition which should be updated. + * @param newFieldDefinition the new field definition draft where we get the new label. * @return A filled optional with the update action or an empty optional if the labels are identical. */ @Nonnull diff --git a/src/main/java/com/commercetools/sync/types/utils/TypeUpdateActionUtils.java b/src/main/java/com/commercetools/sync/types/utils/TypeUpdateActionUtils.java index a7538953c8..38bc76150d 100644 --- a/src/main/java/com/commercetools/sync/types/utils/TypeUpdateActionUtils.java +++ b/src/main/java/com/commercetools/sync/types/utils/TypeUpdateActionUtils.java @@ -22,10 +22,9 @@ public final class TypeUpdateActionUtils { /** - * Compares the {@code name} values of a {@link Type} and a {@link Type} + * Compares the {@code name} values of a {@link Type} and a {@link TypeDraft} * and returns an {@link Optional} of update action, which would contain the {@code "changeName"} - * {@link UpdateAction}. If both {@link Type} and {@link Type} have the same - * {@code name} values, then no update action is needed and empty optional will be returned. + * {@link UpdateAction} if values are different. * * @param oldType the type that should be updated. * @param newType the type draft which contains the new name. @@ -43,9 +42,8 @@ public static Optional> buildChangeNameAction( /** * Compares the {@link LocalizedString} descriptions of a {@link Type} and a {@link TypeDraft} and - * returns an {@link UpdateAction}<{@link Type}> as a result in an {@link Optional}. If both the - * {@link Type} and the {@link TypeDraft} have the same description, then no update action is needed and - * hence an empty {@link Optional} is returned. + * returns an {@link UpdateAction}<{@link Type}> as a result in an {@link Optional} + * of update action if values are different. * * @param oldType the type which should be updated. * @param newType the type draft where we get the new description. @@ -60,10 +58,9 @@ public static Optional> buildSetDescriptionUpdateAction( } /** - * Compares the fields of a {@link Type} and a {@link TypeDraft} and returns a list of - * {@link UpdateAction}<{@link Type}> as a result. If both the {@link Type} and - * the {@link TypeDraft} have identical field definitions, then no update action is needed and hence an empty - * {@link List} is returned. In case, the new type draft has a list of field definitions in which a + * Compares the field definitions of a {@link Type} and a {@link TypeDraft} and returns a list of + * {@link UpdateAction}<{@link Type}> as a result in an {@link Optional} of update action + * if values are different. In case, the new type draft has a list of field definitions in which a * duplicate name exists, the error callback is triggered and an empty list is returned. * * @param oldType the type which should be updated. diff --git a/src/main/java/com/commercetools/sync/types/utils/TypeUpdateEnumActionsUtils.java b/src/main/java/com/commercetools/sync/types/utils/TypeUpdateEnumActionsUtils.java index d6d46c41cd..15d669036d 100644 --- a/src/main/java/com/commercetools/sync/types/utils/TypeUpdateEnumActionsUtils.java +++ b/src/main/java/com/commercetools/sync/types/utils/TypeUpdateEnumActionsUtils.java @@ -99,7 +99,7 @@ public static Optional> buildChangeEnumVa * Otherwise, if there are no new enum values, then an empty list is returned. * * @param fieldDefinitionName the field definition name whose enum values belong to. - * @param oldEnumValues the list of olf enum values. + * @param oldEnumValues the list of old enum values. * @param newEnumValues the list of new enum values. * @param addEnumCallback the function that is called in order to add the new enum instance * @param the enum type of the element to add. diff --git a/src/main/java/com/commercetools/sync/types/utils/TypeUpdateFieldDefinitionActionUtils.java b/src/main/java/com/commercetools/sync/types/utils/TypeUpdateFieldDefinitionActionUtils.java index f292f4c0a1..1529d2e2ae 100644 --- a/src/main/java/com/commercetools/sync/types/utils/TypeUpdateFieldDefinitionActionUtils.java +++ b/src/main/java/com/commercetools/sync/types/utils/TypeUpdateFieldDefinitionActionUtils.java @@ -33,8 +33,8 @@ public final class TypeUpdateFieldDefinitionActionUtils { /** * Compares a list of {@link FieldDefinition}s with a list of {@link FieldDefinition}s. - * The method serves as a generic implementation for field definitions syncing. The method takes in functions - * for building the required update actions (AddFieldDefinition, RemoveFieldDefinition, ChangeFieldDefinitionOrder + * The method serves as a generic implementation for field definitions syncing and building the required + * update actions (AddFieldDefinition, RemoveFieldDefinition, ChangeFieldDefinitionOrder) * and 1-1 update actions on field definitions (e.g. changeFieldDefinitionLabel, etc..) for the required * resource. * @@ -69,8 +69,8 @@ public static List> buildFieldDefinitionsUpdateActions( /** * Compares a list of {@link FieldDefinition}s with a list of {@link FieldDefinition}s. - * The method serves as an implementation for field definitions syncing. The method takes in functions - * for building the required update actions (AddFieldDefinition, RemoveFieldDefinition, ChangeFieldDefinitionOrder + * The method serves as an implementation for field definitions syncing and building the required + * update actions (AddFieldDefinition, RemoveFieldDefinition, ChangeFieldDefinitionOrder) * and 1-1 update actions on field definitions (e.g. changeFieldDefinitionLabel, etc..) for the required * resource. * @@ -86,11 +86,6 @@ private static List> buildUpdateActions( @Nonnull final List newFieldDefinitions) throws BuildUpdateActionException { - final Map oldFieldDefinitionsNameMap = - oldFieldDefinitions - .stream() - .collect(toMap(FieldDefinition::getName, fieldDefinition -> fieldDefinition)); - try { final List> updateActions = @@ -101,8 +96,8 @@ private static List> buildUpdateActions( updateActions.addAll( buildAddFieldDefinitionUpdateActions( - newFieldDefinitions, - oldFieldDefinitionsNameMap + oldFieldDefinitions, + newFieldDefinitions ) ); @@ -215,7 +210,6 @@ private static Optional> buildChangeFieldDefinitionOrderUpdat @Nonnull final List oldFieldDefinitions, @Nonnull final List newFieldDefinitions) { - final List newNames = newFieldDefinitions .stream() .map(FieldDefinition::getName) @@ -252,20 +246,25 @@ private static Optional> buildChangeFieldDefinitionOrderUpdat * {@code oldFieldDefinitionNameMap}. If there are, then "add" field definition update actions are built. * Otherwise, if there are no new field definitions, then an empty list is returned. * - * @param newFieldDefinitions the list of new {@link FieldDefinition}s. - * @param oldFieldDefinitionNameMap a map of names to FieldDefinition of the old list - * of field definition. + * @param oldFieldDefinitions the list of old {@link FieldDefinition}s + * @param newFieldDefinitions the list of new {@link FieldDefinition}s + * * @return a list of field definition update actions if there are new field definition that should be added. * Otherwise, if the field definitions are identical, an empty optional is returned. */ @Nonnull private static List> buildAddFieldDefinitionUpdateActions( - @Nonnull final List newFieldDefinitions, - @Nonnull final Map oldFieldDefinitionNameMap) { + @Nonnull final List oldFieldDefinitions, + @Nonnull final List newFieldDefinitions) { + + final Map oldFieldDefinitionsNameMap = + oldFieldDefinitions + .stream() + .collect(toMap(FieldDefinition::getName, fieldDefinition -> fieldDefinition)); return newFieldDefinitions .stream() - .filter(fieldDefinition -> !oldFieldDefinitionNameMap.containsKey(fieldDefinition.getName())) + .filter(fieldDefinition -> !oldFieldDefinitionsNameMap.containsKey(fieldDefinition.getName())) .map(fieldDefinition -> FieldDefinition.of(fieldDefinition.getType(), fieldDefinition.getName(), fieldDefinition.getLabel(), diff --git a/src/main/java/com/commercetools/sync/types/utils/TypeUpdateLocalizedEnumActionUtils.java b/src/main/java/com/commercetools/sync/types/utils/TypeUpdateLocalizedEnumActionUtils.java index 57c14ceb43..99b57c1115 100644 --- a/src/main/java/com/commercetools/sync/types/utils/TypeUpdateLocalizedEnumActionUtils.java +++ b/src/main/java/com/commercetools/sync/types/utils/TypeUpdateLocalizedEnumActionUtils.java @@ -53,20 +53,7 @@ public static List> buildLocalizedEnumValuesUpdateActions( return emptyList(); } - /** - * Compares a list of old {@link LocalizedEnumValue}s with a list of new {@link LocalizedEnumValue}s for a given - * field definition. - * The method serves as a generic implementation for localized enum values syncing. The method takes in functions - * for building the required update actions (AddEnumValue, RemoveEnumValue, ChangeEnumValueOrder and 1-1 - * update actions on localized enum values (e.g. changeLabel) for the required resource. - * - * @param fieldDefinitionName the attribute name whose localized enum values are going to be synced. - * @param oldEnumValues the old list of localized enum values. - * @param newEnumValues the new list of localized enum values. - * @return a list of localized enum values update actions if the list of localized enum values is not identical. - * Otherwise, if the localized enum values are identical, an empty list is returned. - * @throws DuplicateKeyException in case there are localized enum values with duplicate keys. - */ + @Nonnull private static List> buildUpdateActions( @Nonnull final String fieldDefinitionName, diff --git a/src/main/java/com/commercetools/sync/types/utils/TypeUpdatePlainEnumActionUtils.java b/src/main/java/com/commercetools/sync/types/utils/TypeUpdatePlainEnumActionUtils.java index 923adcea29..f6307d03ee 100644 --- a/src/main/java/com/commercetools/sync/types/utils/TypeUpdatePlainEnumActionUtils.java +++ b/src/main/java/com/commercetools/sync/types/utils/TypeUpdatePlainEnumActionUtils.java @@ -48,19 +48,6 @@ public static List> buildEnumValuesUpdateActions( } - /** - * Compares a list of old {@link EnumValue}s with a list of new {@link EnumValue}s for a given field - * definition. - * The method serves as a implementation for plain enum values syncing. The method takes in functions - * for building the required update actions (AddEnumValue, RemoveEnumValue, ChangeEnumValueOrder and 1-1 - * update actions on plain enum values (e.g. changeLabel) for the required resource. - * - * @param fieldDefinitionName the field definition name whose plain enum values are going to be synced. - * @param oldEnumValues the old list of plain enum values. - * @param newEnumValues the new list of plain enum values. - * @return a list of plain enum values update actions if the list of plain enum values is not identical. - * Otherwise, if the plain enum values are identical, an empty list is returned. - */ @Nonnull private static List> buildUpdateActions( @Nonnull final String fieldDefinitionName, diff --git a/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildLocalizedEnumUpdateActionsTest.java b/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildLocalizedEnumUpdateActionsTest.java index 4fb1648c9d..06a9946f0e 100644 --- a/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildLocalizedEnumUpdateActionsTest.java +++ b/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildLocalizedEnumUpdateActionsTest.java @@ -99,7 +99,7 @@ public void buildLocalizedEnumUpdateActions_WithOnePlainEnumValue_ShouldBuildAdd } @Test - public void buildLocalizedEnumUpdateActions_WithOneEnumValueSwitch_ShouldBuildRemoveAndAddEnumValueActions() { + public void buildLocalizedEnumUpdateActions_WithOneEnumValueSwitch_ShouldBuildAddEnumValueActions() { final List> updateActions = buildLocalizedEnumValuesUpdateActions( FIELD_NAME_1, ENUM_VALUES_ABC, @@ -130,7 +130,7 @@ public void buildLocalizedEnumUpdateActions_WithDifferent_ShouldBuildChangeEnumV } @Test - public void buildLocalizedEnumUpdateActions_WithRemovedAndDifferentOrder_ShouldBuildChangeOrderAndRemoveActions() { + public void buildLocalizedEnumUpdateActions_WithRemovedAndDifferentOrder_ShouldBuildChangeOrderActions() { final List> updateActions = buildLocalizedEnumValuesUpdateActions( FIELD_NAME_1, ENUM_VALUES_ABC, @@ -185,7 +185,7 @@ public void buildLocalizedEnumUpdateActions_WithAddedEnumValueInBetween_ShouldBu } @Test - public void buildLocalizedEnumUpdateActions_WithAddedRemovedAndDifOrder_ShouldBuildAllThreeMoveEnumValueActions() { + public void buildLocalizedEnumUpdateActions_WithAddedRemovedAndDifOrder_ShouldBuildAddAndOrderEnumValueActions() { final List> updateActions = buildLocalizedEnumValuesUpdateActions( FIELD_NAME_1, ENUM_VALUES_ABC, From 2ddf05b211f1b471e4b8a377cc20b73c8d53823b Mon Sep 17 00:00:00 2001 From: aoz Date: Wed, 26 Sep 2018 16:14:42 +0200 Subject: [PATCH 011/164] #300: PR changes --- docs/usage/TYPE_SYNC.md | 6 +--- .../commercetools/sync/types/TypeSync.java | 2 +- .../sync/types/TypeSyncOptions.java | 3 +- .../sync/types/TypeSyncOptionsBuilder.java | 1 - .../utils/TypeUpdateEnumActionsUtils.java | 8 +++-- .../TypeUpdateFieldDefinitionActionUtils.java | 32 +++++++++---------- .../BuildLocalizedEnumUpdateActionsTest.java | 22 +++++++++++++ 7 files changed, 46 insertions(+), 28 deletions(-) diff --git a/docs/usage/TYPE_SYNC.md b/docs/usage/TYPE_SYNC.md index 4173ece7c3..3b9a9fe771 100644 --- a/docs/usage/TYPE_SYNC.md +++ b/docs/usage/TYPE_SYNC.md @@ -56,9 +56,6 @@ update and modify (add/remove) update actions just before they are send to CTP A a filter function which can be applied on a type draft before a request to create it on CTP is issued. It allows the user to intercept type create requests modify the draft before the create request is sent to CTP API. -- `allowUuid` -a flag, if set to `true`, enables the user to use keys with UUID format for references. By default, it is set to `false`. - Example of options usage, that sets the error and warning callbacks to output the message to the log error and warning streams, would look as follows: ```java @@ -117,5 +114,4 @@ and field definitions can be found [here](/src/test/java/com/commercetools/sync/ 1. Types are either created or updated. Currently the tool does not support type deletion. 2. Updating the label of enum values and localized enum values of field definition is not supported yet. 3. Removing the enum values from the field definition is not supported yet. -4. Updating the input hint of field definition is not supported yet. -5. Updating the reference and field definition type is not supported yet. \ No newline at end of file +4. Updating the input hint of field definition is not supported yet. \ No newline at end of file diff --git a/src/main/java/com/commercetools/sync/types/TypeSync.java b/src/main/java/com/commercetools/sync/types/TypeSync.java index 90e643161d..a6644e1804 100644 --- a/src/main/java/com/commercetools/sync/types/TypeSync.java +++ b/src/main/java/com/commercetools/sync/types/TypeSync.java @@ -244,7 +244,7 @@ private CompletionStage createType(@Nonnull final TypeDraft typeDraft) { */ @SuppressFBWarnings("NP_NONNULL_PARAM_VIOLATION") // https://github.com/findbugsproject/findbugs/issues/79 private CompletionStage updateType(@Nonnull final Type oldType, - @Nonnull final TypeDraft newType) { + @Nonnull final TypeDraft newType) { final List> updateActions = buildActions(oldType, newType, syncOptions); diff --git a/src/main/java/com/commercetools/sync/types/TypeSyncOptions.java b/src/main/java/com/commercetools/sync/types/TypeSyncOptions.java index fe78762c9a..51b28dbaf2 100644 --- a/src/main/java/com/commercetools/sync/types/TypeSyncOptions.java +++ b/src/main/java/com/commercetools/sync/types/TypeSyncOptions.java @@ -21,7 +21,6 @@ public final class TypeSyncOptions extends BaseSyncOptions { @Nullable final BiConsumer updateActionErrorCallBack, @Nullable final Consumer updateActionWarningCallBack, final int batchSize, - final boolean allowUuid, @Nullable final TriFunction>, TypeDraft, Type, List>> beforeUpdateCallback, @Nullable final Function beforeCreateCallback @@ -32,7 +31,7 @@ public final class TypeSyncOptions extends BaseSyncOptions { updateActionErrorCallBack, updateActionWarningCallBack, batchSize, - allowUuid, + false, beforeUpdateCallback, beforeCreateCallback ); diff --git a/src/main/java/com/commercetools/sync/types/TypeSyncOptionsBuilder.java b/src/main/java/com/commercetools/sync/types/TypeSyncOptionsBuilder.java index 9c3f0c6e83..0e8254a744 100644 --- a/src/main/java/com/commercetools/sync/types/TypeSyncOptionsBuilder.java +++ b/src/main/java/com/commercetools/sync/types/TypeSyncOptionsBuilder.java @@ -40,7 +40,6 @@ public TypeSyncOptions build() { errorCallback, warningCallback, batchSize, - allowUuid, beforeUpdateCallback, beforeCreateCallback ); diff --git a/src/main/java/com/commercetools/sync/types/utils/TypeUpdateEnumActionsUtils.java b/src/main/java/com/commercetools/sync/types/utils/TypeUpdateEnumActionsUtils.java index 15d669036d..93d75f87ad 100644 --- a/src/main/java/com/commercetools/sync/types/utils/TypeUpdateEnumActionsUtils.java +++ b/src/main/java/com/commercetools/sync/types/utils/TypeUpdateEnumActionsUtils.java @@ -46,7 +46,6 @@ public static Map getEnumValuesKeyMapWithKeyValid )); } - /** * Compares the order of a list of old enum values and a list of new enum values. If there is a change in order, * then a change of enum values order (with the new order) is built. If there are no changes in order an empty @@ -118,7 +117,12 @@ public static List> buildAddEnumValuesUpd oldEnumValues ); - return newEnumValues + final Map newEnumValuesKeyMap = getEnumValuesKeyMapWithKeyValidation( + fieldDefinitionName, + newEnumValues + ); + + return newEnumValuesKeyMap.values() .stream() .filter(newEnumValue -> !oldEnumValuesKeyMap.containsKey(newEnumValue.getKey())) .map(newEnumValue -> addEnumCallback.apply(fieldDefinitionName, newEnumValue)) diff --git a/src/main/java/com/commercetools/sync/types/utils/TypeUpdateFieldDefinitionActionUtils.java b/src/main/java/com/commercetools/sync/types/utils/TypeUpdateFieldDefinitionActionUtils.java index 1529d2e2ae..f921491116 100644 --- a/src/main/java/com/commercetools/sync/types/utils/TypeUpdateFieldDefinitionActionUtils.java +++ b/src/main/java/com/commercetools/sync/types/utils/TypeUpdateFieldDefinitionActionUtils.java @@ -25,7 +25,6 @@ import static com.commercetools.sync.types.utils.FieldDefinitionUpdateActionUtils.buildActions; import static java.lang.String.format; import static java.util.Collections.singletonList; -import static java.util.Optional.ofNullable; import static java.util.stream.Collectors.toList; import static java.util.stream.Collectors.toMap; @@ -156,27 +155,26 @@ private static List> buildRemoveFieldDefinitionOrFieldDefinit final FieldDefinition matchingNewFieldDefinition = newFieldDefinitionsNameMap.get(oldFieldDefinitionName); - return ofNullable(matchingNewFieldDefinition) - .map(newFieldDefinition -> { - if (newFieldDefinition.getType() != null) { - // field type is required so if null we let commercetools to throw exception - if (haveSameFieldType(oldFieldDefinition, newFieldDefinition)) { - return buildActions(oldFieldDefinition, newFieldDefinition); - } else { - return Arrays.asList( - RemoveFieldDefinition.of(oldFieldDefinitionName), - AddFieldDefinition.of(newFieldDefinition) - ); - } + if (matchingNewFieldDefinition == null) { + return singletonList(RemoveFieldDefinition.of(oldFieldDefinitionName)); + } else { + if (matchingNewFieldDefinition.getType() != null) { + // field type is required so if null we let commercetools to throw exception + if (haveSameFieldType(oldFieldDefinition, matchingNewFieldDefinition)) { + return buildActions(oldFieldDefinition, matchingNewFieldDefinition); } else { - return new ArrayList>(); + return Arrays.asList( + RemoveFieldDefinition.of(oldFieldDefinitionName), + AddFieldDefinition.of(matchingNewFieldDefinition) + ); } - }) - .orElseGet(() -> singletonList(RemoveFieldDefinition.of(oldFieldDefinitionName))); + } else { + return new ArrayList>(); + } + } }) .flatMap(Collection::stream) .collect(Collectors.toList()); - } /** diff --git a/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildLocalizedEnumUpdateActionsTest.java b/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildLocalizedEnumUpdateActionsTest.java index 06a9946f0e..7432f482be 100644 --- a/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildLocalizedEnumUpdateActionsTest.java +++ b/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildLocalizedEnumUpdateActionsTest.java @@ -1,11 +1,15 @@ package com.commercetools.sync.types.utils.typeactionutils; +import com.commercetools.sync.commons.exceptions.DuplicateKeyException; +import com.commercetools.sync.types.utils.TypeUpdateLocalizedEnumActionUtils; import io.sphere.sdk.commands.UpdateAction; import io.sphere.sdk.models.LocalizedEnumValue; import io.sphere.sdk.types.Type; import io.sphere.sdk.types.commands.updateactions.AddLocalizedEnumValue; import io.sphere.sdk.types.commands.updateactions.ChangeLocalizedEnumValueOrder; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.ExpectedException; import java.util.Collections; import java.util.List; @@ -25,6 +29,7 @@ public class BuildLocalizedEnumUpdateActionsTest { private static final List ENUM_VALUES_ABC = asList(ENUM_VALUE_A, ENUM_VALUE_B, ENUM_VALUE_C); private static final List ENUM_VALUES_ABD = asList(ENUM_VALUE_A, ENUM_VALUE_B, ENUM_VALUE_D); + private static final List ENUM_VALUES_ABB = asList(ENUM_VALUE_A, ENUM_VALUE_B, ENUM_VALUE_B); private static final List ENUM_VALUES_ABCD = asList( ENUM_VALUE_A, ENUM_VALUE_B, @@ -47,6 +52,9 @@ public class BuildLocalizedEnumUpdateActionsTest { ); private static final List ENUM_VALUES_CBD = asList(ENUM_VALUE_C, ENUM_VALUE_B, ENUM_VALUE_D); + @Rule + public ExpectedException expectedException = ExpectedException.none(); + @Test public void buildLocalizedEnumUpdateActions_WithEmptyPlainEnumValuesAndNoOlEnumValues_ShouldNotBuildActions() { final List> updateActions = buildLocalizedEnumValuesUpdateActions( @@ -202,4 +210,18 @@ public void buildLocalizedEnumUpdateActions_WithAddedRemovedAndDifOrder_ShouldBu )) ); } + + @Test + public void buildLocalizedEnumUpdateActions_WithDuplicatePlainEnumValues_ShouldTriggerDuplicateKeyError() { + expectedException.expect(DuplicateKeyException.class); + expectedException.expectMessage("Enum Values have duplicated keys. Field definition name: " + + "'field_definition_name', Duplicated enum value: 'b'. Enum Values are expected to be unique inside " + + "their field definition."); + + TypeUpdateLocalizedEnumActionUtils.buildLocalizedEnumValuesUpdateActions( + "field_definition_name", + ENUM_VALUES_ABC, + ENUM_VALUES_ABB + ); + } } From 6f1484541de42487b69083d4556201bc2cb40080 Mon Sep 17 00:00:00 2001 From: aoz Date: Fri, 5 Oct 2018 11:27:09 +0200 Subject: [PATCH 012/164] #300 - update type sync docs. --- docs/RELEASE_NOTES.md | 8 +++++++- docs/usage/TYPE_SYNC.md | 2 +- ...eEnumActionsUtils.java => EnumUpdateActionsUtils.java} | 4 ++-- .../types/utils/FieldDefinitionUpdateActionUtils.java | 4 ++-- ...nUtils.java => FieldDefinitionsUpdateActionUtils.java} | 4 ++-- ...tionUtils.java => LocalizedEnumUpdateActionUtils.java} | 8 ++++---- ...umActionUtils.java => PlainEnumUpdateActionUtils.java} | 8 ++++---- .../sync/types/utils/TypeUpdateActionUtils.java | 2 +- .../BuildLocalizedEnumUpdateActionsTest.java | 6 +++--- .../typeactionutils/BuildPlainEnumUpdateActionsTest.java | 2 +- 10 files changed, 27 insertions(+), 21 deletions(-) rename src/main/java/com/commercetools/sync/types/utils/{TypeUpdateEnumActionsUtils.java => EnumUpdateActionsUtils.java} (98%) rename src/main/java/com/commercetools/sync/types/utils/{TypeUpdateFieldDefinitionActionUtils.java => FieldDefinitionsUpdateActionUtils.java} (99%) rename src/main/java/com/commercetools/sync/types/utils/{TypeUpdateLocalizedEnumActionUtils.java => LocalizedEnumUpdateActionUtils.java} (91%) rename src/main/java/com/commercetools/sync/types/utils/{TypeUpdatePlainEnumActionUtils.java => PlainEnumUpdateActionUtils.java} (90%) diff --git a/docs/RELEASE_NOTES.md b/docs/RELEASE_NOTES.md index 370ea1eb03..6de38b819c 100644 --- a/docs/RELEASE_NOTES.md +++ b/docs/RELEASE_NOTES.md @@ -56,8 +56,14 @@ [Javadoc](https://commercetools.github.io/commercetools-sync-java/v/v1.0.0-M14/) | [Jar](https://bintray.com/commercetools/maven/commercetools-sync-java/v1.0.0-M14) -**New Features** (1) +**New Features** (7) - **Type Sync** - Support for syncing types. [#300](https://github.com/commercetools/commercetools-sync-java/issues/300) For more info how to use it please refer to [Type usage doc](/docs/usage/TYPE_SYNC.md). +- **Type Sync** - Exposed `TypeSyncUtils#buildActions` which calculates all needed update actions after comparing a `Type` and a `TypeDraft`. [#300](https://github.com/commercetools/commercetools-sync-java/issues/300) +- **Type Sync** - Exposed `TypeUpdateActionUtils` which contains utils for calculating needed update actions after comparing individual fields of a `Type` and a `TypeDraft`. [#300](https://github.com/commercetools/commercetools-sync-java/issues/300) +- **Type Sync** - Exposed `LocalizedEnumUpdateActionUtils` which contains utils for calculating needed update actions after comparing two lists of `LocalizedEnumValue`s. [#300](https://github.com/commercetools/commercetools-sync-java/issues/300) +- **Type Sync** - Exposed `PlainEnumUpdateActionUtils` which contains utils for calculating needed update actions after comparing two lists of `EnumValue`s. [#300](https://github.com/commercetools/commercetools-sync-java/issues/300) +- **Type Sync** - Exposed `FieldDefinitionsUpdateActionUtils` which contains utils for calculating needed update actions after comparing a list of `FieldDefinition`s and a list of `FieldDefinition`s. [#300](https://github.com/commercetools/commercetools-sync-java/issues/300) +- **Type Sync** - Exposed `FieldDefinitionUpdateActionUtils` which contains utils for calculating needed update actions after comparing a `FieldDefinition` and a `FieldDefinition`. [#300](https://github.com/commercetools/commercetools-sync-java/issues/300) **Enhancements** (6) - **Product Sync** - Products create and update requests are now issued in parallel. This should lead to a performance improvement. [#238](https://github.com/commercetools/commercetools-sync-java/issues/238) diff --git a/docs/usage/TYPE_SYNC.md b/docs/usage/TYPE_SYNC.md index 3b9a9fe771..1f42ab02bc 100644 --- a/docs/usage/TYPE_SYNC.md +++ b/docs/usage/TYPE_SYNC.md @@ -88,7 +88,7 @@ stats.getReportMessage(); __Note__ The statistics object contains the processing time of the last batch only. This is due to two reasons: 1. The sync processing time should not take into account the time between supplying batches to the sync. - 2. It is not not known by the sync which batch is going to be the last one supplied. + 2. It is not known by the sync which batch is going to be the last one supplied. More examples of how to use the sync can be found [here](/src/integration-test/java/com/commercetools/sync/integration/types/TypeSyncIT.java). diff --git a/src/main/java/com/commercetools/sync/types/utils/TypeUpdateEnumActionsUtils.java b/src/main/java/com/commercetools/sync/types/utils/EnumUpdateActionsUtils.java similarity index 98% rename from src/main/java/com/commercetools/sync/types/utils/TypeUpdateEnumActionsUtils.java rename to src/main/java/com/commercetools/sync/types/utils/EnumUpdateActionsUtils.java index 93d75f87ad..f2ffbc5f8c 100644 --- a/src/main/java/com/commercetools/sync/types/utils/TypeUpdateEnumActionsUtils.java +++ b/src/main/java/com/commercetools/sync/types/utils/EnumUpdateActionsUtils.java @@ -18,7 +18,7 @@ import static java.lang.String.format; import static java.util.stream.Collectors.toMap; -public final class TypeUpdateEnumActionsUtils { +public final class EnumUpdateActionsUtils { /** * Given a list of new {@link EnumValue}s, gets a map where the keys are the enum value key, and the values @@ -129,6 +129,6 @@ public static List> buildAddEnumValuesUpd .collect(Collectors.toList()); } - private TypeUpdateEnumActionsUtils() { + private EnumUpdateActionsUtils() { } } diff --git a/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionUpdateActionUtils.java b/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionUpdateActionUtils.java index 4161a010f7..84643f3482 100644 --- a/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionUpdateActionUtils.java +++ b/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionUpdateActionUtils.java @@ -14,8 +14,8 @@ import java.util.stream.Stream; import static com.commercetools.sync.commons.utils.CommonTypeUpdateActionUtils.buildUpdateAction; -import static com.commercetools.sync.types.utils.TypeUpdateLocalizedEnumActionUtils.buildLocalizedEnumValuesUpdateActions; -import static com.commercetools.sync.types.utils.TypeUpdatePlainEnumActionUtils.buildEnumValuesUpdateActions; +import static com.commercetools.sync.types.utils.LocalizedEnumUpdateActionUtils.buildLocalizedEnumValuesUpdateActions; +import static com.commercetools.sync.types.utils.PlainEnumUpdateActionUtils.buildEnumValuesUpdateActions; import static java.util.stream.Collectors.toList; diff --git a/src/main/java/com/commercetools/sync/types/utils/TypeUpdateFieldDefinitionActionUtils.java b/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionsUpdateActionUtils.java similarity index 99% rename from src/main/java/com/commercetools/sync/types/utils/TypeUpdateFieldDefinitionActionUtils.java rename to src/main/java/com/commercetools/sync/types/utils/FieldDefinitionsUpdateActionUtils.java index f921491116..67dbd2c2fa 100644 --- a/src/main/java/com/commercetools/sync/types/utils/TypeUpdateFieldDefinitionActionUtils.java +++ b/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionsUpdateActionUtils.java @@ -28,7 +28,7 @@ import static java.util.stream.Collectors.toList; import static java.util.stream.Collectors.toMap; -public final class TypeUpdateFieldDefinitionActionUtils { +public final class FieldDefinitionsUpdateActionUtils { /** * Compares a list of {@link FieldDefinition}s with a list of {@link FieldDefinition}s. @@ -272,6 +272,6 @@ private static List> buildAddFieldDefinitionUpdateActions( .collect(Collectors.toList()); } - private TypeUpdateFieldDefinitionActionUtils() { + private FieldDefinitionsUpdateActionUtils() { } } diff --git a/src/main/java/com/commercetools/sync/types/utils/TypeUpdateLocalizedEnumActionUtils.java b/src/main/java/com/commercetools/sync/types/utils/LocalizedEnumUpdateActionUtils.java similarity index 91% rename from src/main/java/com/commercetools/sync/types/utils/TypeUpdateLocalizedEnumActionUtils.java rename to src/main/java/com/commercetools/sync/types/utils/LocalizedEnumUpdateActionUtils.java index 99b57c1115..42d5f86968 100644 --- a/src/main/java/com/commercetools/sync/types/utils/TypeUpdateLocalizedEnumActionUtils.java +++ b/src/main/java/com/commercetools/sync/types/utils/LocalizedEnumUpdateActionUtils.java @@ -14,11 +14,11 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import static com.commercetools.sync.types.utils.TypeUpdateEnumActionsUtils.buildAddEnumValuesUpdateActions; -import static com.commercetools.sync.types.utils.TypeUpdateEnumActionsUtils.buildChangeEnumValuesOrderUpdateAction; +import static com.commercetools.sync.types.utils.EnumUpdateActionsUtils.buildAddEnumValuesUpdateActions; +import static com.commercetools.sync.types.utils.EnumUpdateActionsUtils.buildChangeEnumValuesOrderUpdateAction; import static java.util.Collections.emptyList; -public final class TypeUpdateLocalizedEnumActionUtils { +public final class LocalizedEnumUpdateActionUtils { /** * Compares a list of old {@link LocalizedEnumValue}s with a list of new {@link LocalizedEnumValue}s for a given * field definition. @@ -86,6 +86,6 @@ private static List> buildUpdateActions( ).collect(Collectors.toList()); } - private TypeUpdateLocalizedEnumActionUtils() { + private LocalizedEnumUpdateActionUtils() { } } diff --git a/src/main/java/com/commercetools/sync/types/utils/TypeUpdatePlainEnumActionUtils.java b/src/main/java/com/commercetools/sync/types/utils/PlainEnumUpdateActionUtils.java similarity index 90% rename from src/main/java/com/commercetools/sync/types/utils/TypeUpdatePlainEnumActionUtils.java rename to src/main/java/com/commercetools/sync/types/utils/PlainEnumUpdateActionUtils.java index f6307d03ee..6047aef9a3 100644 --- a/src/main/java/com/commercetools/sync/types/utils/TypeUpdatePlainEnumActionUtils.java +++ b/src/main/java/com/commercetools/sync/types/utils/PlainEnumUpdateActionUtils.java @@ -13,11 +13,11 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import static com.commercetools.sync.types.utils.TypeUpdateEnumActionsUtils.buildAddEnumValuesUpdateActions; -import static com.commercetools.sync.types.utils.TypeUpdateEnumActionsUtils.buildChangeEnumValuesOrderUpdateAction; +import static com.commercetools.sync.types.utils.EnumUpdateActionsUtils.buildAddEnumValuesUpdateActions; +import static com.commercetools.sync.types.utils.EnumUpdateActionsUtils.buildChangeEnumValuesOrderUpdateAction; import static java.util.Collections.emptyList; -public final class TypeUpdatePlainEnumActionUtils { +public final class PlainEnumUpdateActionUtils { /** * Compares a list of old {@link EnumValue}s with a list of new {@link EnumValue}s for a given * field definition. @@ -79,6 +79,6 @@ private static List> buildUpdateActions( } - private TypeUpdatePlainEnumActionUtils() { + private PlainEnumUpdateActionUtils() { } } diff --git a/src/main/java/com/commercetools/sync/types/utils/TypeUpdateActionUtils.java b/src/main/java/com/commercetools/sync/types/utils/TypeUpdateActionUtils.java index 38bc76150d..a15ed78177 100644 --- a/src/main/java/com/commercetools/sync/types/utils/TypeUpdateActionUtils.java +++ b/src/main/java/com/commercetools/sync/types/utils/TypeUpdateActionUtils.java @@ -15,7 +15,7 @@ import java.util.Optional; import static com.commercetools.sync.commons.utils.CommonTypeUpdateActionUtils.buildUpdateAction; -import static com.commercetools.sync.types.utils.TypeUpdateFieldDefinitionActionUtils.buildFieldDefinitionsUpdateActions; +import static com.commercetools.sync.types.utils.FieldDefinitionsUpdateActionUtils.buildFieldDefinitionsUpdateActions; import static java.lang.String.format; import static java.util.Collections.emptyList; diff --git a/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildLocalizedEnumUpdateActionsTest.java b/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildLocalizedEnumUpdateActionsTest.java index 7432f482be..0cd7c29859 100644 --- a/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildLocalizedEnumUpdateActionsTest.java +++ b/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildLocalizedEnumUpdateActionsTest.java @@ -1,7 +1,7 @@ package com.commercetools.sync.types.utils.typeactionutils; import com.commercetools.sync.commons.exceptions.DuplicateKeyException; -import com.commercetools.sync.types.utils.TypeUpdateLocalizedEnumActionUtils; +import com.commercetools.sync.types.utils.LocalizedEnumUpdateActionUtils; import io.sphere.sdk.commands.UpdateAction; import io.sphere.sdk.models.LocalizedEnumValue; import io.sphere.sdk.types.Type; @@ -14,7 +14,7 @@ import java.util.Collections; import java.util.List; -import static com.commercetools.sync.types.utils.TypeUpdateLocalizedEnumActionUtils.buildLocalizedEnumValuesUpdateActions; +import static com.commercetools.sync.types.utils.LocalizedEnumUpdateActionUtils.buildLocalizedEnumValuesUpdateActions; import static io.sphere.sdk.models.LocalizedString.ofEnglish; import static java.util.Arrays.asList; import static org.assertj.core.api.Assertions.assertThat; @@ -218,7 +218,7 @@ public void buildLocalizedEnumUpdateActions_WithDuplicatePlainEnumValues_ShouldT + "'field_definition_name', Duplicated enum value: 'b'. Enum Values are expected to be unique inside " + "their field definition."); - TypeUpdateLocalizedEnumActionUtils.buildLocalizedEnumValuesUpdateActions( + LocalizedEnumUpdateActionUtils.buildLocalizedEnumValuesUpdateActions( "field_definition_name", ENUM_VALUES_ABC, ENUM_VALUES_ABB diff --git a/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildPlainEnumUpdateActionsTest.java b/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildPlainEnumUpdateActionsTest.java index 5b3ce74640..d956090acc 100644 --- a/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildPlainEnumUpdateActionsTest.java +++ b/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildPlainEnumUpdateActionsTest.java @@ -10,7 +10,7 @@ import java.util.Collections; import java.util.List; -import static com.commercetools.sync.types.utils.TypeUpdatePlainEnumActionUtils.buildEnumValuesUpdateActions; +import static com.commercetools.sync.types.utils.PlainEnumUpdateActionUtils.buildEnumValuesUpdateActions; import static java.util.Arrays.asList; import static org.assertj.core.api.Assertions.assertThat; From 9801dbd87fa39ffda80ead4f33a0fac5f7103070 Mon Sep 17 00:00:00 2001 From: aoz Date: Fri, 5 Oct 2018 12:01:25 +0200 Subject: [PATCH 013/164] #300 - update type sync java docs and change some indention. --- .../commercetools/sync/types/TypeSync.java | 20 ++++++------ .../sync/types/TypeSyncOptionsBuilder.java | 7 ++-- .../FieldDefinitionsUpdateActionUtils.java | 32 ++++++------------- .../utils/LocalizedEnumUpdateActionUtils.java | 16 ++++------ .../utils/PlainEnumUpdateActionUtils.java | 14 ++++---- .../sync/types/utils/TypeSyncUtils.java | 20 ++++++------ .../types/utils/TypeUpdateActionUtils.java | 12 +++---- .../utils/TypeUpdateActionUtilsTest.java | 6 ++-- 8 files changed, 55 insertions(+), 72 deletions(-) diff --git a/src/main/java/com/commercetools/sync/types/TypeSync.java b/src/main/java/com/commercetools/sync/types/TypeSync.java index a6644e1804..a6770b94cf 100644 --- a/src/main/java/com/commercetools/sync/types/TypeSync.java +++ b/src/main/java/com/commercetools/sync/types/TypeSync.java @@ -191,16 +191,16 @@ private CompletionStage syncBatch( final Map oldTypeMap = getKeysTypeMap(oldTypes); return CompletableFuture.allOf(newTypes - .stream() - .map(newType -> { - final Type oldType = oldTypeMap.get(newType.getKey()); - - return ofNullable(oldType) - .map(type -> updateType(oldType, newType)) - .orElseGet(() -> createType(newType)); - }) - .map(CompletionStage::toCompletableFuture) - .toArray(CompletableFuture[]::new)).thenApply(result -> statistics); + .stream() + .map(newType -> { + final Type oldType = oldTypeMap.get(newType.getKey()); + + return ofNullable(oldType) + .map(type -> updateType(oldType, newType)) + .orElseGet(() -> createType(newType)); + }) + .map(CompletionStage::toCompletableFuture) + .toArray(CompletableFuture[]::new)).thenApply(result -> statistics); } /** diff --git a/src/main/java/com/commercetools/sync/types/TypeSyncOptionsBuilder.java b/src/main/java/com/commercetools/sync/types/TypeSyncOptionsBuilder.java index 0e8254a744..6f87ea7764 100644 --- a/src/main/java/com/commercetools/sync/types/TypeSyncOptionsBuilder.java +++ b/src/main/java/com/commercetools/sync/types/TypeSyncOptionsBuilder.java @@ -7,8 +7,8 @@ import javax.annotation.Nonnull; -public final class TypeSyncOptionsBuilder extends BaseSyncOptionsBuilder { +public final class TypeSyncOptionsBuilder + extends BaseSyncOptionsBuilder { public static final int BATCH_SIZE_DEFAULT = 50; @@ -28,8 +28,7 @@ public static TypeSyncOptionsBuilder of(@Nonnull final SphereClient ctpClient) { } /** - * Creates new instance of {@link TypeSyncOptions} enriched with all fields provided to {@code this} - * builder. + * Creates new instance of {@link TypeSyncOptions} enriched with all fields provided to {@code this} builder. * * @return new instance of {@link TypeSyncOptions} */ diff --git a/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionsUpdateActionUtils.java b/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionsUpdateActionUtils.java index 67dbd2c2fa..e08c0c30fb 100644 --- a/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionsUpdateActionUtils.java +++ b/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionsUpdateActionUtils.java @@ -32,7 +32,7 @@ public final class FieldDefinitionsUpdateActionUtils { /** * Compares a list of {@link FieldDefinition}s with a list of {@link FieldDefinition}s. - * The method serves as a generic implementation for field definitions syncing and building the required + * The method serves as an implementation for field definitions syncing and building the required * update actions (AddFieldDefinition, RemoveFieldDefinition, ChangeFieldDefinitionOrder) * and 1-1 update actions on field definitions (e.g. changeFieldDefinitionLabel, etc..) for the required * resource. @@ -40,7 +40,7 @@ public final class FieldDefinitionsUpdateActionUtils { *

If the list of new {@link FieldDefinition}s is {@code null}, then remove actions are built for * every existing field definition in the {@code oldFieldDefinitions} list. * - * @param oldFieldDefinitions the old list of field definitions. + * @param oldFieldDefinitions the old list of field definitions. * @param newFieldDefinitions the new list of field definitions. * @return a list of field definitions update actions if the list of field definitions is not identical. * Otherwise, if the field definitions are identical, an empty list is returned. @@ -53,10 +53,7 @@ public static List> buildFieldDefinitionsUpdateActions( throws BuildUpdateActionException { if (newFieldDefinitions != null) { - return buildUpdateActions( - oldFieldDefinitions, - newFieldDefinitions - ); + return buildUpdateActions(oldFieldDefinitions, newFieldDefinitions); } else { return oldFieldDefinitions .stream() @@ -73,7 +70,7 @@ public static List> buildFieldDefinitionsUpdateActions( * and 1-1 update actions on field definitions (e.g. changeFieldDefinitionLabel, etc..) for the required * resource. * - * @param oldFieldDefinitions the old list of field definitions. + * @param oldFieldDefinitions the old list of field definitions. * @param newFieldDefinitions the new list of field definitions drafts. * @return a list of field definitions update actions if the list of field definitions is not identical. * Otherwise, if the field definitions are identical, an empty list is returned. @@ -88,22 +85,11 @@ private static List> buildUpdateActions( try { final List> updateActions = - buildRemoveFieldDefinitionOrFieldDefinitionUpdateActions( - oldFieldDefinitions, - newFieldDefinitions - ); + buildRemoveFieldDefinitionOrFieldDefinitionUpdateActions(oldFieldDefinitions, newFieldDefinitions); - updateActions.addAll( - buildAddFieldDefinitionUpdateActions( - oldFieldDefinitions, - newFieldDefinitions - ) - ); + updateActions.addAll(buildAddFieldDefinitionUpdateActions(oldFieldDefinitions, newFieldDefinitions)); - buildChangeFieldDefinitionOrderUpdateAction( - oldFieldDefinitions, - newFieldDefinitions - ) + buildChangeFieldDefinitionOrderUpdateAction(oldFieldDefinitions, newFieldDefinitions) .ifPresent(updateActions::add); return updateActions; @@ -123,8 +109,8 @@ private static List> buildUpdateActions( *

Note: If the field type field changes, the old field definition is removed and the new field * definition is added with the new field type. * - * @param oldFieldDefinitions the list of old {@link FieldDefinition}s. - * @param newFieldDefinitions the list of new {@link FieldDefinition}s. + * @param oldFieldDefinitions the list of old {@link FieldDefinition}s. + * @param newFieldDefinitions the list of new {@link FieldDefinition}s. * @return a list of field definition update actions if there are field that are not existing * in the new draft. If the field definition still exists in the new draft, then compare the field * definition fields (name, label, etc..), and add the computed actions to the list of update actions. diff --git a/src/main/java/com/commercetools/sync/types/utils/LocalizedEnumUpdateActionUtils.java b/src/main/java/com/commercetools/sync/types/utils/LocalizedEnumUpdateActionUtils.java index 42d5f86968..dff43cb95d 100644 --- a/src/main/java/com/commercetools/sync/types/utils/LocalizedEnumUpdateActionUtils.java +++ b/src/main/java/com/commercetools/sync/types/utils/LocalizedEnumUpdateActionUtils.java @@ -22,13 +22,10 @@ public final class LocalizedEnumUpdateActionUtils { /** * Compares a list of old {@link LocalizedEnumValue}s with a list of new {@link LocalizedEnumValue}s for a given * field definition. - * The method serves as a generic implementation for localized enum values syncing. The method takes in functions + * The method serves as an implementation for localized enum values syncing. The method takes in functions * for building the required update actions (AddLocalizedEnumValue, RemoveEnumValue, ChangeLocalizedEnumValueOrder * and 1-1 update actions on localized enum values (e.g. changeLabel) for the required resource. * - *

If the list of new {@link LocalizedEnumValue}s is {@code null}, then remove actions are built for - * every existing localized enum value in the {@code oldEnumValues} list. - * * @param fieldDefinitionName the field name whose localized enum values are going to be synced. * @param oldEnumValues the old list of localized enum values. * @param newEnumValues the new list of localized enum values. @@ -43,13 +40,14 @@ public static List> buildLocalizedEnumValuesUpdateActions( @Nullable final List newEnumValues) { if (newEnumValues != null && !newEnumValues.isEmpty()) { - return buildUpdateActions( - fieldDefinitionName, - oldEnumValues, - newEnumValues - ); + return buildUpdateActions(fieldDefinitionName, oldEnumValues, newEnumValues); } + /* + TODO: If the list of newEnumValues is null, then remove actions are built + for every existing localized enum value in the oldEnumValues list. + */ + return emptyList(); } diff --git a/src/main/java/com/commercetools/sync/types/utils/PlainEnumUpdateActionUtils.java b/src/main/java/com/commercetools/sync/types/utils/PlainEnumUpdateActionUtils.java index 6047aef9a3..fc9a15f5d3 100644 --- a/src/main/java/com/commercetools/sync/types/utils/PlainEnumUpdateActionUtils.java +++ b/src/main/java/com/commercetools/sync/types/utils/PlainEnumUpdateActionUtils.java @@ -21,13 +21,10 @@ public final class PlainEnumUpdateActionUtils { /** * Compares a list of old {@link EnumValue}s with a list of new {@link EnumValue}s for a given * field definition. - * The method serves as a generic implementation for plain enum values syncing. The method takes in functions + * The method serves as an implementation for plain enum values syncing. The method takes in functions * for building the required update actions (AddEnumValue, ChangeEnumValueOrder * and 1-1 update actions on plain enum values (e.g. changeLabel) for the required resource. * - *

If the list of new {@link EnumValue}s is {@code null}, then remove actions are built for - * every existing plain enum value in the {@code oldEnumValues} list. - * * @param fieldDefinitionName the field name whose plain enum values are going to be synced. * @param oldEnumValues the old list of plain enum values. * @param newEnumValues the new list of plain enum values. @@ -44,6 +41,11 @@ public static List> buildEnumValuesUpdateActions( return buildUpdateActions(fieldDefinitionName, oldEnumValues, newEnumValues); } + /* + TODO: If the list of newEnumValues is null, then remove actions are built + for every existing plain enum value in the oldEnumValues list. + */ + return emptyList(); } @@ -72,9 +74,7 @@ private static List> buildUpdateActions( .map(Collections::singletonList) .orElse(emptyList()); - return Stream.concat( - addEnumValuesUpdateActions.stream(), - changeEnumValuesOrderUpdateActions.stream()) + return Stream.concat(addEnumValuesUpdateActions.stream(), changeEnumValuesOrderUpdateActions.stream()) .collect(Collectors.toList()); } diff --git a/src/main/java/com/commercetools/sync/types/utils/TypeSyncUtils.java b/src/main/java/com/commercetools/sync/types/utils/TypeSyncUtils.java index 43e5385a3c..1a332fa1cf 100644 --- a/src/main/java/com/commercetools/sync/types/utils/TypeSyncUtils.java +++ b/src/main/java/com/commercetools/sync/types/utils/TypeSyncUtils.java @@ -10,7 +10,7 @@ import java.util.Optional; import java.util.stream.Stream; -import static com.commercetools.sync.types.utils.TypeUpdateActionUtils.buildChangeNameAction; +import static com.commercetools.sync.types.utils.TypeUpdateActionUtils.buildChangeNameUpdateAction; import static com.commercetools.sync.types.utils.TypeUpdateActionUtils.buildFieldDefinitionUpdateActions; import static com.commercetools.sync.types.utils.TypeUpdateActionUtils.buildSetDescriptionUpdateAction; import static java.util.stream.Collectors.toList; @@ -18,19 +18,19 @@ public final class TypeSyncUtils { /** - * Compares all the fields (including the attributes see + * Compares all the fields (including the field definitions see * {@link TypeUpdateActionUtils#buildFieldDefinitionUpdateActions(Type, TypeDraft, TypeSyncOptions)}) * of a {@link Type} and a {@link TypeDraft}. * It returns a {@link List} of {@link UpdateAction}<{@link Type}> as a - * result. If no update action is needed, for example in case where both the {@link Type} and the + * result. If no update action are needed, for example in case where both the {@link Type} and the * {@link TypeDraft} have the same fields, an empty {@link List} is returned. * - * @param oldType the {@link Type} which should be updated. - * @param newType the {@link TypeDraft} where we get the new data. - * @param syncOptions the sync options wrapper which contains options related to the sync process supplied by - * the user. For example, custom callbacks to call in case of warnings or errors occurring - * on the build update action process. And other options (See {@link TypeSyncOptions} - * for more info. + * @param oldType the {@link Type} which should be updated. + * @param newType the {@link TypeDraft} where we get the new data. + * @param syncOptions the sync options wrapper which contains options related to the sync process supplied by + * the user. For example, custom callbacks to call in case of warnings or errors occurring + * on the build update action process. And other options (See {@link TypeSyncOptions} + * for more info. * @return A list of type-specific update actions. */ @Nonnull @@ -40,7 +40,7 @@ public static List> buildActions( @Nonnull final TypeSyncOptions syncOptions) { final List> updateActions = Stream.of( - buildChangeNameAction(oldType, newType), + buildChangeNameUpdateAction(oldType, newType), buildSetDescriptionUpdateAction(oldType, newType) ) .filter(Optional::isPresent) diff --git a/src/main/java/com/commercetools/sync/types/utils/TypeUpdateActionUtils.java b/src/main/java/com/commercetools/sync/types/utils/TypeUpdateActionUtils.java index a15ed78177..599f662e54 100644 --- a/src/main/java/com/commercetools/sync/types/utils/TypeUpdateActionUtils.java +++ b/src/main/java/com/commercetools/sync/types/utils/TypeUpdateActionUtils.java @@ -22,7 +22,7 @@ public final class TypeUpdateActionUtils { /** - * Compares the {@code name} values of a {@link Type} and a {@link TypeDraft} + * Compares the {@link LocalizedString} name values of a {@link Type} and a {@link TypeDraft} * and returns an {@link Optional} of update action, which would contain the {@code "changeName"} * {@link UpdateAction} if values are different. * @@ -31,7 +31,7 @@ public final class TypeUpdateActionUtils { * @return optional containing update action or empty optional if names are identical. */ @Nonnull - public static Optional> buildChangeNameAction( + public static Optional> buildChangeNameUpdateAction( @Nonnull final Type oldType, @Nonnull final TypeDraft newType) { @@ -51,8 +51,8 @@ public static Optional> buildChangeNameAction( */ @Nonnull public static Optional> buildSetDescriptionUpdateAction( - @Nonnull final Type oldType, - @Nonnull final TypeDraft newType) { + @Nonnull final Type oldType, + @Nonnull final TypeDraft newType) { return buildUpdateAction(oldType.getDescription(), newType.getDescription(), () -> SetDescription.of(newType.getDescription())); } @@ -63,8 +63,8 @@ public static Optional> buildSetDescriptionUpdateAction( * if values are different. In case, the new type draft has a list of field definitions in which a * duplicate name exists, the error callback is triggered and an empty list is returned. * - * @param oldType the type which should be updated. - * @param newType the type draft where we get the key. + * @param oldType the type which should be updated. + * @param newType the type draft where we get the key. * @param syncOptions responsible for supplying the sync options to the sync utility method. * It is used for triggering the error callback within the utility, in case of * errors. diff --git a/src/test/java/com/commercetools/sync/types/utils/TypeUpdateActionUtilsTest.java b/src/test/java/com/commercetools/sync/types/utils/TypeUpdateActionUtilsTest.java index 84e9d4e792..446c154033 100644 --- a/src/test/java/com/commercetools/sync/types/utils/TypeUpdateActionUtilsTest.java +++ b/src/test/java/com/commercetools/sync/types/utils/TypeUpdateActionUtilsTest.java @@ -20,7 +20,7 @@ import static com.commercetools.sync.types.FieldDefinitionTestHelper.imageUrlFieldDefinition; import static com.commercetools.sync.types.FieldDefinitionTestHelper.relatedCategoriesFieldDefinition; import static com.commercetools.sync.types.FieldDefinitionTestHelper.stateFieldDefinition; -import static com.commercetools.sync.types.utils.TypeUpdateActionUtils.buildChangeNameAction; +import static com.commercetools.sync.types.utils.TypeUpdateActionUtils.buildChangeNameUpdateAction; import static com.commercetools.sync.types.utils.TypeUpdateActionUtils.buildSetDescriptionUpdateAction; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; @@ -74,7 +74,7 @@ public static void setup() { @Test public void buildChangeNameAction_WithDifferentValues_ShouldReturnAction() { - final Optional> result = buildChangeNameAction(old, newDifferent); + final Optional> result = buildChangeNameUpdateAction(old, newDifferent); assertThat(result).containsInstanceOf(ChangeName.class); assertThat(result).contains(ChangeName.of(newDifferent.getName())); @@ -82,7 +82,7 @@ public void buildChangeNameAction_WithDifferentValues_ShouldReturnAction() { @Test public void buildChangeNameAction_WithSameValues_ShouldReturnEmptyOptional() { - final Optional> result = buildChangeNameAction(old, newSame); + final Optional> result = buildChangeNameUpdateAction(old, newSame); assertThat(result).isEmpty(); } From de42db0d104aff6c65057f94a1099286855abe3c Mon Sep 17 00:00:00 2001 From: aoz Date: Fri, 5 Oct 2018 13:57:31 +0200 Subject: [PATCH 014/164] #300 - update type sync java doc. --- docs/usage/TYPE_SYNC.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/usage/TYPE_SYNC.md b/docs/usage/TYPE_SYNC.md index 1f42ab02bc..98baffde04 100644 --- a/docs/usage/TYPE_SYNC.md +++ b/docs/usage/TYPE_SYNC.md @@ -30,7 +30,7 @@ matched. - Retries on 5xx errors with a retry strategy. This can be achieved by decorating the `sphereClient` with the [RetrySphereClientDecorator](http://commercetools.github.io/commercetools-jvm-sdk/apidocs/io/sphere/sdk/client/RetrySphereClientDecorator.html) - You can use the same client instance in the integration tests for this library found + You can instantiate the client the same way it is instantiated in the integration tests for this library found [here](/src/main/java/com/commercetools/sync/commons/utils/ClientConfigurationUtils.java#L45). 4. After the `sphereClient` is setup, a `TypeSyncOptions` should be be built as follows: From 5a357ce7426d638c2fef8670a9c34d640e27e686 Mon Sep 17 00:00:00 2001 From: aoz Date: Fri, 5 Oct 2018 14:02:14 +0200 Subject: [PATCH 015/164] #300 - typo --- .../java/com/commercetools/sync/types/utils/TypeSyncUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/commercetools/sync/types/utils/TypeSyncUtils.java b/src/main/java/com/commercetools/sync/types/utils/TypeSyncUtils.java index 1a332fa1cf..63d883262c 100644 --- a/src/main/java/com/commercetools/sync/types/utils/TypeSyncUtils.java +++ b/src/main/java/com/commercetools/sync/types/utils/TypeSyncUtils.java @@ -22,7 +22,7 @@ public final class TypeSyncUtils { * {@link TypeUpdateActionUtils#buildFieldDefinitionUpdateActions(Type, TypeDraft, TypeSyncOptions)}) * of a {@link Type} and a {@link TypeDraft}. * It returns a {@link List} of {@link UpdateAction}<{@link Type}> as a - * result. If no update action are needed, for example in case where both the {@link Type} and the + * result. If no update actions are needed, for example in case where both the {@link Type} and the * {@link TypeDraft} have the same fields, an empty {@link List} is returned. * * @param oldType the {@link Type} which should be updated. From 18970e91045e6ba4cc5756013fb24b5c1e9ecb34 Mon Sep 17 00:00:00 2001 From: aoz Date: Fri, 5 Oct 2018 14:11:56 +0200 Subject: [PATCH 016/164] #300 - add comment for updating field definition's type --- .../sync/types/utils/FieldDefinitionsUpdateActionUtils.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionsUpdateActionUtils.java b/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionsUpdateActionUtils.java index e08c0c30fb..f6398cb4c9 100644 --- a/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionsUpdateActionUtils.java +++ b/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionsUpdateActionUtils.java @@ -149,6 +149,8 @@ private static List> buildRemoveFieldDefinitionOrFieldDefinit if (haveSameFieldType(oldFieldDefinition, matchingNewFieldDefinition)) { return buildActions(oldFieldDefinition, matchingNewFieldDefinition); } else { + // this is a work around for changing the type of the definition. + // since there is no action, so we remove then add again. return Arrays.asList( RemoveFieldDefinition.of(oldFieldDefinitionName), AddFieldDefinition.of(matchingNewFieldDefinition) From 079b1dfefc661f7775385689c845fe438e753f8f Mon Sep 17 00:00:00 2001 From: aoz Date: Mon, 8 Oct 2018 10:20:35 +0200 Subject: [PATCH 017/164] #300 - change order --- docs/RELEASE_NOTES.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/RELEASE_NOTES.md b/docs/RELEASE_NOTES.md index 8e9addb2da..18b0033fa7 100644 --- a/docs/RELEASE_NOTES.md +++ b/docs/RELEASE_NOTES.md @@ -80,8 +80,6 @@ optionals. [#255](https://github.com/commercetools/commercetools-sync-java/issue - **Type Sync** - Exposed `PlainEnumUpdateActionUtils` which contains utils for calculating needed update actions after comparing two lists of `EnumValue`s. [#300](https://github.com/commercetools/commercetools-sync-java/issues/300) - **Type Sync** - Exposed `FieldDefinitionsUpdateActionUtils` which contains utils for calculating needed update actions after comparing a list of `FieldDefinition`s and a list of `FieldDefinition`s. [#300](https://github.com/commercetools/commercetools-sync-java/issues/300) - **Type Sync** - Exposed `FieldDefinitionUpdateActionUtils` which contains utils for calculating needed update actions after comparing a `FieldDefinition` and a `FieldDefinition`. [#300](https://github.com/commercetools/commercetools-sync-java/issues/300) -**Bug Fixes** (1) -- **Product Sync** - Fixed a bug where the removed attributes in the source product variant draft were not being removed from the target variant. [#238](https://github.com/commercetools/commercetools-sync-java/issues/308) **Enhancements** (8) - **Product Sync** - Products create and update requests are now issued in parallel. This should lead to a performance improvement. [#238](https://github.com/commercetools/commercetools-sync-java/issues/238) @@ -97,6 +95,8 @@ optionals. [#255](https://github.com/commercetools/commercetools-sync-java/issue - **Product Sync** - `AttributeMetaData#isRequired` is now removed. [#308](https://github.com/commercetools/commercetools-sync-java/issues/308) - **Product Sync** - `ProductVariantAttributeUpdateActionUtils#buildProductVariantAttributeUpdateAction` now takes a map of all meta data instead of the specific metadata entry. [#308](https://github.com/commercetools/commercetools-sync-java/issues/308) +**Bug Fixes** (1) +- **Product Sync** - Fixed a bug where the removed attributes in the source product variant draft were not being removed from the target variant. [#238](https://github.com/commercetools/commercetools-sync-java/issues/308) ### v1.0.0-M13 - Sept 5, 2018 [Commits](https://github.com/commercetools/commercetools-sync-java/compare/v1.0.0-M12...v1.0.0-M13) | From f7a4c768dd77bdb7892f0d9aa92cd604330a2aa7 Mon Sep 17 00:00:00 2001 From: aoz Date: Mon, 8 Oct 2018 10:26:28 +0200 Subject: [PATCH 018/164] #300 - refactor with using filterEmptyOptionals util --- .../types/utils/FieldDefinitionUpdateActionUtils.java | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionUpdateActionUtils.java b/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionUpdateActionUtils.java index 84643f3482..d1e43994a5 100644 --- a/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionUpdateActionUtils.java +++ b/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionUpdateActionUtils.java @@ -11,12 +11,11 @@ import javax.annotation.Nonnull; import java.util.List; import java.util.Optional; -import java.util.stream.Stream; import static com.commercetools.sync.commons.utils.CommonTypeUpdateActionUtils.buildUpdateAction; +import static com.commercetools.sync.commons.utils.OptionalUtils.filterEmptyOptionals; import static com.commercetools.sync.types.utils.LocalizedEnumUpdateActionUtils.buildLocalizedEnumValuesUpdateActions; import static com.commercetools.sync.types.utils.PlainEnumUpdateActionUtils.buildEnumValuesUpdateActions; -import static java.util.stream.Collectors.toList; public final class FieldDefinitionUpdateActionUtils { @@ -36,12 +35,8 @@ public static List> buildActions( @Nonnull final FieldDefinition oldFieldDefinition, @Nonnull final FieldDefinition newFieldDefinition) { - final List> updateActions = Stream.of( - buildChangeLabelUpdateAction(oldFieldDefinition, newFieldDefinition)) - .filter(Optional::isPresent) - .map(Optional::get) - .collect(toList()); - + final List> updateActions = + filterEmptyOptionals(buildChangeLabelUpdateAction(oldFieldDefinition, newFieldDefinition)); if (isPlainEnumField(oldFieldDefinition)) { updateActions.addAll(buildEnumValuesUpdateActions( From 850246de2d1bafcfdfb033fd6e5c13fd85bd51e7 Mon Sep 17 00:00:00 2001 From: aoz Date: Mon, 8 Oct 2018 11:17:46 +0200 Subject: [PATCH 019/164] #300 - refactor, do not block error callback --- .../FieldDefinitionsUpdateActionUtils.java | 25 ++++++++----------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionsUpdateActionUtils.java b/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionsUpdateActionUtils.java index f6398cb4c9..f113f6bafa 100644 --- a/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionsUpdateActionUtils.java +++ b/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionsUpdateActionUtils.java @@ -12,7 +12,6 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; @@ -144,20 +143,15 @@ private static List> buildRemoveFieldDefinitionOrFieldDefinit if (matchingNewFieldDefinition == null) { return singletonList(RemoveFieldDefinition.of(oldFieldDefinitionName)); } else { - if (matchingNewFieldDefinition.getType() != null) { - // field type is required so if null we let commercetools to throw exception - if (haveSameFieldType(oldFieldDefinition, matchingNewFieldDefinition)) { - return buildActions(oldFieldDefinition, matchingNewFieldDefinition); - } else { - // this is a work around for changing the type of the definition. - // since there is no action, so we remove then add again. - return Arrays.asList( - RemoveFieldDefinition.of(oldFieldDefinitionName), - AddFieldDefinition.of(matchingNewFieldDefinition) - ); - } + if (haveSameFieldType(oldFieldDefinition, matchingNewFieldDefinition)) { + return buildActions(oldFieldDefinition, matchingNewFieldDefinition); } else { - return new ArrayList>(); + // this is a work around for changing the type of the definition. + // since there is no action, so we remove then add again. + return Arrays.asList( + RemoveFieldDefinition.of(oldFieldDefinitionName), + AddFieldDefinition.of(matchingNewFieldDefinition) + ); } } }) @@ -177,7 +171,8 @@ private static boolean haveSameFieldType( @Nonnull final FieldDefinition fieldDefinitionA, @Nonnull final FieldDefinition fieldDefinitionB) { - return fieldDefinitionA.getType().getClass() == fieldDefinitionB.getType().getClass(); + return fieldDefinitionA.getType() != null && fieldDefinitionB.getType() != null && + fieldDefinitionA.getType().getClass() == fieldDefinitionB.getType().getClass(); } /** From 4807bf58ed60a395151c2bbfd54be512e65314a2 Mon Sep 17 00:00:00 2001 From: aoz Date: Mon, 8 Oct 2018 11:30:47 +0200 Subject: [PATCH 020/164] #300 - refactor, comment --- src/main/java/com/commercetools/sync/types/TypeSync.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/commercetools/sync/types/TypeSync.java b/src/main/java/com/commercetools/sync/types/TypeSync.java index a6770b94cf..ab5b6bffcd 100644 --- a/src/main/java/com/commercetools/sync/types/TypeSync.java +++ b/src/main/java/com/commercetools/sync/types/TypeSync.java @@ -87,7 +87,7 @@ protected CompletionStage syncBatches( * Fetches existing {@link Type} objects from CTP project that correspond to passed {@code batch}. * Having existing types fetched, {@code batch} is compared and synced with fetched objects by * {@link TypeSync#syncBatch(List, List)} function. When fetching existing types results in - * an empty updated/created statistics then {@code batch} isn't processed. + * an empty {@link TypeSyncStatistics} object then {@code batch} isn't processed. * * @param batch batch of drafts that need to be synced * @return {@link CompletionStage} of {@link TypeSyncStatistics} that indicates method progress. From c6b88d1b90e21b0b6d6fe273d67730f25154f1d4 Mon Sep 17 00:00:00 2001 From: aoz Date: Mon, 8 Oct 2018 11:51:26 +0200 Subject: [PATCH 021/164] #300 - checkStyle fix --- .../sync/types/utils/FieldDefinitionsUpdateActionUtils.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionsUpdateActionUtils.java b/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionsUpdateActionUtils.java index f113f6bafa..4d501f4b60 100644 --- a/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionsUpdateActionUtils.java +++ b/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionsUpdateActionUtils.java @@ -171,8 +171,8 @@ private static boolean haveSameFieldType( @Nonnull final FieldDefinition fieldDefinitionA, @Nonnull final FieldDefinition fieldDefinitionB) { - return fieldDefinitionA.getType() != null && fieldDefinitionB.getType() != null && - fieldDefinitionA.getType().getClass() == fieldDefinitionB.getType().getClass(); + return fieldDefinitionA.getType() != null && fieldDefinitionB.getType() != null + && fieldDefinitionA.getType().getClass() == fieldDefinitionB.getType().getClass(); } /** From 2dc8da912803fd607ac617649665927fa749e81d Mon Sep 17 00:00:00 2001 From: aoz Date: Mon, 8 Oct 2018 14:07:42 +0200 Subject: [PATCH 022/164] #300 - adds tests for TypeServiceImpl --- .../services/impl/TypeServiceImplIT.java | 149 +++++++++++++++++- 1 file changed, 144 insertions(+), 5 deletions(-) diff --git a/src/integration-test/java/com/commercetools/sync/integration/services/impl/TypeServiceImplIT.java b/src/integration-test/java/com/commercetools/sync/integration/services/impl/TypeServiceImplIT.java index 74b326dded..f5b35f6466 100644 --- a/src/integration-test/java/com/commercetools/sync/integration/services/impl/TypeServiceImplIT.java +++ b/src/integration-test/java/com/commercetools/sync/integration/services/impl/TypeServiceImplIT.java @@ -1,25 +1,36 @@ package com.commercetools.sync.integration.services.impl; -import com.commercetools.sync.categories.CategorySyncOptions; -import com.commercetools.sync.categories.CategorySyncOptionsBuilder; import com.commercetools.sync.services.TypeService; import com.commercetools.sync.services.impl.TypeServiceImpl; +import com.commercetools.sync.types.TypeSyncOptions; +import com.commercetools.sync.types.TypeSyncOptionsBuilder; import io.sphere.sdk.models.LocalizedString; import io.sphere.sdk.types.ResourceTypeIdsSetBuilder; +import io.sphere.sdk.types.Type; import io.sphere.sdk.types.TypeDraft; import io.sphere.sdk.types.TypeDraftBuilder; import io.sphere.sdk.types.commands.TypeCreateCommand; +import io.sphere.sdk.types.commands.updateactions.ChangeName; +import io.sphere.sdk.types.queries.TypeQuery; import org.junit.AfterClass; import org.junit.Before; import org.junit.Test; +import java.util.HashSet; +import java.util.List; import java.util.Locale; import java.util.Optional; +import java.util.Set; import static com.commercetools.sync.integration.commons.utils.CategoryITUtils.createCategoriesCustomType; import static com.commercetools.sync.integration.commons.utils.ITUtils.deleteTypesFromTargetAndSource; import static com.commercetools.sync.integration.commons.utils.SphereClientUtils.CTP_TARGET_CLIENT; +import static com.commercetools.sync.integration.types.utils.TypeITUtils.FIELD_DEFINITION_1; +import static com.commercetools.sync.integration.types.utils.TypeITUtils.TYPE_DESCRIPTION_1; +import static com.commercetools.sync.integration.types.utils.TypeITUtils.TYPE_KEY_1; +import static com.commercetools.sync.integration.types.utils.TypeITUtils.TYPE_NAME_1; +import static java.util.Collections.singletonList; import static org.assertj.core.api.Assertions.assertThat; public class TypeServiceImplIT { @@ -35,10 +46,9 @@ public class TypeServiceImplIT { public void setup() { deleteTypesFromTargetAndSource(); createCategoriesCustomType(OLD_TYPE_KEY, OLD_TYPE_LOCALE, OLD_TYPE_NAME, CTP_TARGET_CLIENT); - final CategorySyncOptions categorySyncOptions = CategorySyncOptionsBuilder.of(CTP_TARGET_CLIENT) - .build(); - typeService = new TypeServiceImpl(categorySyncOptions); + final TypeSyncOptions typeSyncOptions = TypeSyncOptionsBuilder.of(CTP_TARGET_CLIENT).build(); + typeService = new TypeServiceImpl(typeSyncOptions); } /** @@ -83,4 +93,133 @@ public void fetchCachedTypeId_WithNewlyCreatedTypeAfterCaching_ShouldNotFetchNew assertThat(newTypeId).isEmpty(); } + + @Test + public void fetchMatchingTypesByKeys_WithEmptySetOfKeys_ShouldReturnEmptyList() { + final Set typeKeys = new HashSet<>(); + final List matchingTypes = typeService.fetchMatchingTypesByKeys(typeKeys) + .toCompletableFuture() + .join(); + + assertThat(matchingTypes).isEmpty(); + } + + @Test + public void fetchMatchingTypesByKeys_WithNonExistingKeys_ShouldReturnEmptyList() { + final Set typeKeys = new HashSet<>(); + typeKeys.add("type_key_1"); + typeKeys.add("type_key_2"); + + final List matchingTypes = typeService.fetchMatchingTypesByKeys(typeKeys) + .toCompletableFuture() + .join(); + + assertThat(matchingTypes).isEmpty(); + } + + @Test + public void fetchMatchingTypesByKeys_WithAnyExistingKeys_ShouldReturnAListOfTypes() { + final Set typeKeys = new HashSet<>(); + typeKeys.add(OLD_TYPE_KEY); + + final List matchingTypes = typeService.fetchMatchingTypesByKeys(typeKeys) + .toCompletableFuture() + .join(); + + assertThat(matchingTypes).isNotEmpty(); + assertThat(matchingTypes).hasSize(1); + } + + @Test + public void createType_WithValidType_ShouldCreateType() { + final TypeDraft newTypeDraft = TypeDraftBuilder.of( + TYPE_KEY_1, + TYPE_NAME_1, + ResourceTypeIdsSetBuilder.of().addCategories().build()) + .description(TYPE_DESCRIPTION_1) + .fieldDefinitions(singletonList(FIELD_DEFINITION_1)) + .build(); + + final Type createdType = typeService.createType(newTypeDraft) + .toCompletableFuture().join(); + + assertThat(createdType).isNotNull(); + + final Optional typeOptional = CTP_TARGET_CLIENT + .execute(TypeQuery.of() + .withPredicates(typeQueryModel -> typeQueryModel.key().is(createdType.getKey()))) + .toCompletableFuture().join().head(); + + assertThat(typeOptional).isNotEmpty(); + final Type fetchedType = typeOptional.get(); + assertThat(fetchedType.getKey()).isEqualTo(newTypeDraft.getKey()); + assertThat(fetchedType.getDescription()).isEqualTo(createdType.getDescription()); + assertThat(fetchedType.getName()).isEqualTo(createdType.getName()); + assertThat(fetchedType.getFieldDefinitions()).isEqualTo(createdType.getFieldDefinitions()); + } + + @Test + public void createType_WithInvalidType_ShouldNotCreateType() { + final TypeDraft newTypeDraft = TypeDraftBuilder.of( + TYPE_KEY_1, + null, + ResourceTypeIdsSetBuilder.of().addCategories().build()) + .description(TYPE_DESCRIPTION_1) + .fieldDefinitions(singletonList(FIELD_DEFINITION_1)) + .build(); + + typeService.createType(newTypeDraft) + .exceptionally(exception -> { + assertThat(exception).isNotNull(); + assertThat(exception.getMessage()).contains("Request body does not contain valid JSON."); + return null; + }) + .toCompletableFuture().join(); + } + + @Test + public void updateType_WithValidChanges_ShouldUpdateTypeCorrectly() { + final Optional typeOptional = CTP_TARGET_CLIENT + .execute(TypeQuery.of() + .withPredicates(typeQueryModel -> typeQueryModel.key().is(OLD_TYPE_KEY))) + .toCompletableFuture().join().head(); + assertThat(typeOptional).isNotNull(); + + final ChangeName changeNameUpdateAction = ChangeName.of(LocalizedString.ofEnglish("new_type_name")); + + final Type updatedType = typeService.updateType(typeOptional.get(), singletonList(changeNameUpdateAction)) + .toCompletableFuture().join(); + assertThat(updatedType).isNotNull(); + + final Optional updatedTypeOptional = CTP_TARGET_CLIENT + .execute(TypeQuery.of() + .withPredicates(typeQueryModel -> typeQueryModel.key().is(OLD_TYPE_KEY))) + .toCompletableFuture().join().head(); + + assertThat(typeOptional).isNotEmpty(); + final Type fetchedType = updatedTypeOptional.get(); + assertThat(fetchedType.getKey()).isEqualTo(updatedType.getKey()); + assertThat(fetchedType.getDescription()).isEqualTo(updatedType.getDescription()); + assertThat(fetchedType.getName()).isEqualTo(updatedType.getName()); + assertThat(fetchedType.getFieldDefinitions()).isEqualTo(updatedType.getFieldDefinitions()); + } + + @Test + public void updateType_WithInvalidChanges_ShouldNotUpdateType() { + final Optional typeOptional = CTP_TARGET_CLIENT + .execute(TypeQuery.of() + .withPredicates(typeQueryModel -> typeQueryModel.key().is(OLD_TYPE_KEY))) + .toCompletableFuture().join().head(); + assertThat(typeOptional).isNotNull(); + + final ChangeName changeNameUpdateAction = ChangeName.of(null); + typeService.updateType(typeOptional.get(), singletonList(changeNameUpdateAction)) + .exceptionally(exception -> { + assertThat(exception).isNotNull(); + assertThat(exception.getMessage()).contains("Request body does not contain valid JSON."); + return null; + }) + .toCompletableFuture().join(); + } + } From 1d882cb74ad75c7888719c0d9b6f12839561680d Mon Sep 17 00:00:00 2001 From: aoz Date: Mon, 8 Oct 2018 14:57:39 +0200 Subject: [PATCH 023/164] #300 - refactor tests. --- .../sync/integration/types/TypeSyncIT.java | 93 ------- .../sync/services/impl/TypeServiceImpl.java | 5 + .../types/TypeSyncOptionsBuilderTest.java | 236 ++++++++++++++++++ 3 files changed, 241 insertions(+), 93 deletions(-) create mode 100644 src/test/java/com/commercetools/sync/types/TypeSyncOptionsBuilderTest.java diff --git a/src/integration-test/java/com/commercetools/sync/integration/types/TypeSyncIT.java b/src/integration-test/java/com/commercetools/sync/integration/types/TypeSyncIT.java index 89c28698b5..d4febe878b 100644 --- a/src/integration-test/java/com/commercetools/sync/integration/types/TypeSyncIT.java +++ b/src/integration-test/java/com/commercetools/sync/integration/types/TypeSyncIT.java @@ -59,7 +59,6 @@ import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -689,98 +688,6 @@ public void sync_WithSeveralBatches_ShouldReturnProperStatistics() { .hasValues(100, 100, 0, 0); } - @Test - public void sync_beforeCreate_ShouldCallBeforeCreateCallback() { - final TypeDraft newTypeDraft = TypeDraftBuilder.of( - TYPE_KEY_2, - TYPE_NAME_2, - ResourceTypeIdsSetBuilder.of().addCategories().build()) - .description(TYPE_DESCRIPTION_2) - .fieldDefinitions(singletonList(FIELD_DEFINITION_1)) - .build(); - - final TypeSyncOptions typeSyncOptions = TypeSyncOptionsBuilder - .of(CTP_TARGET_CLIENT) - .build(); - - TypeSyncOptions spyTypeSyncOptions = spy(typeSyncOptions); - - final TypeSync typeSync = new TypeSync(spyTypeSyncOptions); - - typeSync.sync(singletonList(newTypeDraft)).toCompletableFuture().join(); - - verify(spyTypeSyncOptions).applyBeforeCreateCallBack(newTypeDraft); - } - - @Test - public void sync_beforeCreate_ShouldNotCallBeforeUpdateCallback() { - final TypeDraft newTypeDraft = TypeDraftBuilder.of( - TYPE_KEY_2, - TYPE_NAME_2, - ResourceTypeIdsSetBuilder.of().addCategories().build()) - .description(TYPE_DESCRIPTION_2) - .fieldDefinitions(singletonList(FIELD_DEFINITION_1)) - .build(); - - final TypeSyncOptions typeSyncOptions = TypeSyncOptionsBuilder - .of(CTP_TARGET_CLIENT) - .build(); - - TypeSyncOptions spyTypeSyncOptions = spy(typeSyncOptions); - - final TypeSync typeSync = new TypeSync(spyTypeSyncOptions); - - typeSync.sync(singletonList(newTypeDraft)).toCompletableFuture().join(); - - verify(spyTypeSyncOptions, never()).applyBeforeUpdateCallBack(any(), any(), any()); - } - - @Test - public void sync_beforeUpdate_ShouldCallBeforeUpdateCallback() { - final TypeDraft newTypeDraft = TypeDraftBuilder.of( - TYPE_KEY_1, - TYPE_NAME_2, - ResourceTypeIdsSetBuilder.of().addCategories().build()) - .description(TYPE_DESCRIPTION_2) - .fieldDefinitions(singletonList(FIELD_DEFINITION_1)) - .build(); - - final TypeSyncOptions typeSyncOptions = TypeSyncOptionsBuilder - .of(CTP_TARGET_CLIENT) - .build(); - - TypeSyncOptions spyTypeSyncOptions = spy(typeSyncOptions); - - final TypeSync typeSync = new TypeSync(spyTypeSyncOptions); - - typeSync.sync(singletonList(newTypeDraft)).toCompletableFuture().join(); - - verify(spyTypeSyncOptions).applyBeforeUpdateCallBack(any(), any(), any()); - } - - @Test - public void sync_beforeUpdate_ShouldNotCallBeforeCreateCallback() { - final TypeDraft newTypeDraft = TypeDraftBuilder.of( - TYPE_KEY_1, - TYPE_NAME_2, - ResourceTypeIdsSetBuilder.of().addCategories().build()) - .description(TYPE_DESCRIPTION_2) - .fieldDefinitions(singletonList(FIELD_DEFINITION_1)) - .build(); - - final TypeSyncOptions typeSyncOptions = TypeSyncOptionsBuilder - .of(CTP_TARGET_CLIENT) - .build(); - - TypeSyncOptions spyTypeSyncOptions = spy(typeSyncOptions); - - final TypeSync typeSync = new TypeSync(spyTypeSyncOptions); - - typeSync.sync(singletonList(newTypeDraft)).toCompletableFuture().join(); - - verify(spyTypeSyncOptions, never()).applyBeforeCreateCallBack(newTypeDraft); - } - private static void assertFieldDefinitionsAreEqual(@Nonnull final List oldFields, @Nonnull final List newFields) { diff --git a/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java b/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java index 007688d28e..322df8266d 100644 --- a/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java +++ b/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java @@ -15,6 +15,7 @@ import io.sphere.sdk.types.queries.TypeQueryBuilder; import javax.annotation.Nonnull; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Optional; @@ -53,6 +54,10 @@ public CompletionStage> fetchCachedTypeId(@Nonnull final String @Nonnull @Override public CompletionStage> fetchMatchingTypesByKeys(@Nonnull final Set keys) { + if (keys.isEmpty()) { + return CompletableFuture.completedFuture(Collections.emptyList()); + } + final TypeQuery query = TypeQueryBuilder .of() .plusPredicates(queryModel -> queryModel.key().isIn(keys)) diff --git a/src/test/java/com/commercetools/sync/types/TypeSyncOptionsBuilderTest.java b/src/test/java/com/commercetools/sync/types/TypeSyncOptionsBuilderTest.java new file mode 100644 index 0000000000..eace8f32d5 --- /dev/null +++ b/src/test/java/com/commercetools/sync/types/TypeSyncOptionsBuilderTest.java @@ -0,0 +1,236 @@ +package com.commercetools.sync.types; + +import com.commercetools.sync.commons.utils.TriFunction; +import io.sphere.sdk.client.SphereClient; +import io.sphere.sdk.commands.UpdateAction; +import io.sphere.sdk.types.Type; +import io.sphere.sdk.types.TypeDraft; +import io.sphere.sdk.types.TypeDraftBuilder; +import io.sphere.sdk.types.commands.updateactions.ChangeName; +import org.junit.Test; + +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.function.Function; + +import static io.sphere.sdk.models.LocalizedString.ofEnglish; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class TypeSyncOptionsBuilderTest { + + private static final SphereClient CTP_CLIENT = mock(SphereClient.class); + private TypeSyncOptionsBuilder typeSyncOptionsBuilder = TypeSyncOptionsBuilder.of(CTP_CLIENT); + + @Test + public void of_WithClient_ShouldCreateTypeSyncOptionsBuilder() { + final TypeSyncOptionsBuilder builder = TypeSyncOptionsBuilder.of(CTP_CLIENT); + assertThat(builder).isNotNull(); + } + + @Test + public void build_WithClient_ShouldBuildSyncOptions() { + final TypeSyncOptions typeSyncOptions = typeSyncOptionsBuilder.build(); + assertThat(typeSyncOptions).isNotNull(); + assertThat(typeSyncOptions.shouldAllowUuidKeys()).isFalse(); + assertThat(typeSyncOptions.getBeforeUpdateCallback()).isNull(); + assertThat(typeSyncOptions.getBeforeCreateCallback()).isNull(); + assertThat(typeSyncOptions.getErrorCallBack()).isNull(); + assertThat(typeSyncOptions.getWarningCallBack()).isNull(); + assertThat(typeSyncOptions.getCtpClient()).isEqualTo(CTP_CLIENT); + assertThat(typeSyncOptions.getBatchSize()).isEqualTo(TypeSyncOptionsBuilder.BATCH_SIZE_DEFAULT); + } + + @Test + public void beforeUpdateCallback_WithFilterAsCallback_ShouldSetCallback() { + final TriFunction>, TypeDraft, Type, List>> + beforeUpdateCallback = (updateActions, newType, oldType) -> Collections.emptyList(); + + typeSyncOptionsBuilder.beforeUpdateCallback(beforeUpdateCallback); + + final TypeSyncOptions typeSyncOptions = typeSyncOptionsBuilder.build(); + assertThat(typeSyncOptions.getBeforeUpdateCallback()).isNotNull(); + } + + @Test + public void beforeCreateCallback_WithFilterAsCallback_ShouldSetCallback() { + typeSyncOptionsBuilder.beforeCreateCallback((newType) -> null); + + final TypeSyncOptions typeSyncOptions = typeSyncOptionsBuilder.build(); + assertThat(typeSyncOptions.getBeforeCreateCallback()).isNotNull(); + } + + + @Test + public void allowUuid_WithFalse_ShouldSetFlag() { + typeSyncOptionsBuilder.allowUuidKeys(true); + + final TypeSyncOptions typeSyncOptions = typeSyncOptionsBuilder.build(); + assertThat(typeSyncOptions.shouldAllowUuidKeys()).isNotNull(); + assertThat(typeSyncOptions.shouldAllowUuidKeys()).isFalse(); + } + + @Test + public void errorCallBack_WithCallBack_ShouldSetCallBack() { + final BiConsumer mockErrorCallBack = (errorMessage, errorException) -> { + }; + typeSyncOptionsBuilder.errorCallback(mockErrorCallBack); + + final TypeSyncOptions typeSyncOptions = typeSyncOptionsBuilder.build(); + assertThat(typeSyncOptions.getErrorCallBack()).isNotNull(); + } + + @Test + public void warningCallBack_WithCallBack_ShouldSetCallBack() { + final Consumer mockWarningCallBack = (warningMessage) -> { + }; + typeSyncOptionsBuilder.warningCallback(mockWarningCallBack); + + final TypeSyncOptions typeSyncOptions = typeSyncOptionsBuilder.build(); + assertThat(typeSyncOptions.getWarningCallBack()).isNotNull(); + } + + @Test + public void getThis_ShouldReturnCorrectInstance() { + final TypeSyncOptionsBuilder instance = typeSyncOptionsBuilder.getThis(); + assertThat(instance).isNotNull(); + assertThat(instance).isInstanceOf(TypeSyncOptionsBuilder.class); + assertThat(instance).isEqualTo(typeSyncOptionsBuilder); + } + + @Test + public void typeSyncOptionsBuilderSetters_ShouldBeCallableAfterBaseSyncOptionsBuildSetters() { + final TypeSyncOptions typeSyncOptions = TypeSyncOptionsBuilder + .of(CTP_CLIENT) + .allowUuidKeys(true) + .batchSize(30) + .beforeCreateCallback((newType) -> null) + .beforeUpdateCallback((updateActions, newType, oldType) -> Collections.emptyList()) + .build(); + assertThat(typeSyncOptions).isNotNull(); + } + + @Test + public void batchSize_WithPositiveValue_ShouldSetBatchSize() { + final TypeSyncOptions typeSyncOptions = TypeSyncOptionsBuilder.of(CTP_CLIENT) + .batchSize(10) + .build(); + assertThat(typeSyncOptions.getBatchSize()).isEqualTo(10); + } + + @Test + public void batchSize_WithZeroOrNegativeValue_ShouldFallBackToDefaultValue() { + final TypeSyncOptions typeSyncOptionsWithZeroBatchSize = TypeSyncOptionsBuilder.of(CTP_CLIENT) + .batchSize(0) + .build(); + assertThat(typeSyncOptionsWithZeroBatchSize.getBatchSize()) + .isEqualTo(TypeSyncOptionsBuilder.BATCH_SIZE_DEFAULT); + + final TypeSyncOptions typeSyncOptionsWithNegativeBatchSize = TypeSyncOptionsBuilder + .of(CTP_CLIENT) + .batchSize(-100) + .build(); + assertThat(typeSyncOptionsWithNegativeBatchSize.getBatchSize()) + .isEqualTo(TypeSyncOptionsBuilder.BATCH_SIZE_DEFAULT); + } + + @Test + public void applyBeforeUpdateCallBack_WithNullCallback_ShouldReturnIdenticalList() { + final TypeSyncOptions typeSyncOptions = TypeSyncOptionsBuilder.of(CTP_CLIENT) + .build(); + assertThat(typeSyncOptions.getBeforeUpdateCallback()).isNull(); + + final List> updateActions = Collections.singletonList(ChangeName.of(ofEnglish("name"))); + + final List> filteredList = + typeSyncOptions.applyBeforeUpdateCallBack(updateActions, mock(TypeDraft.class), mock(Type.class)); + + assertThat(filteredList).isSameAs(updateActions); + } + + @Test + public void applyBeforeUpdateCallBack_WithNullReturnCallback_ShouldReturnEmptyList() { + final TriFunction>, TypeDraft, Type, List>> + beforeUpdateCallback = (updateActions, newType, oldType) -> null; + final TypeSyncOptions typeSyncOptions = TypeSyncOptionsBuilder.of(CTP_CLIENT) + .beforeUpdateCallback( + beforeUpdateCallback) + .build(); + assertThat(typeSyncOptions.getBeforeUpdateCallback()).isNotNull(); + + final List> updateActions = Collections.singletonList(ChangeName.of(ofEnglish("name"))); + final List> filteredList = + typeSyncOptions.applyBeforeUpdateCallBack(updateActions, mock(TypeDraft.class), mock(Type.class)); + assertThat(filteredList).isNotEqualTo(updateActions); + assertThat(filteredList).isEmpty(); + } + + @Test + public void applyBeforeUpdateCallBack_WithCallback_ShouldReturnFilteredList() { + final TriFunction>, TypeDraft, Type, List>> + beforeUpdateCallback = (updateActions, newType, oldType) -> Collections.emptyList(); + + final TypeSyncOptions typeSyncOptions = TypeSyncOptionsBuilder.of(CTP_CLIENT) + .beforeUpdateCallback( + beforeUpdateCallback) + .build(); + assertThat(typeSyncOptions.getBeforeUpdateCallback()).isNotNull(); + + final List> updateActions = Collections.singletonList(ChangeName.of(ofEnglish("name"))); + final List> filteredList = + typeSyncOptions.applyBeforeUpdateCallBack(updateActions, mock(TypeDraft.class), mock(Type.class)); + assertThat(filteredList).isNotEqualTo(updateActions); + assertThat(filteredList).isEmpty(); + } + + @Test + public void applyBeforeCreateCallBack_WithCallback_ShouldReturnFilteredDraft() { + final Function draftFunction = + typeDraft -> TypeDraftBuilder.of(typeDraft).key(typeDraft.getKey() + "_filteredKey").build(); + + final TypeSyncOptions typeSyncOptions = TypeSyncOptionsBuilder.of(CTP_CLIENT) + .beforeCreateCallback(draftFunction) + .build(); + + assertThat(typeSyncOptions.getBeforeCreateCallback()).isNotNull(); + + final TypeDraft resourceDraft = mock(TypeDraft.class); + when(resourceDraft.getKey()).thenReturn("myKey"); + + + final Optional filteredDraft = typeSyncOptions.applyBeforeCreateCallBack(resourceDraft); + + assertThat(filteredDraft).isNotEmpty(); + assertThat(filteredDraft.get().getKey()).isEqualTo("myKey_filteredKey"); + } + + @Test + public void applyBeforeCreateCallBack_WithNullCallback_ShouldReturnIdenticalDraftInOptional() { + final TypeSyncOptions typeSyncOptions = TypeSyncOptionsBuilder.of(CTP_CLIENT).build(); + assertThat(typeSyncOptions.getBeforeCreateCallback()).isNull(); + + final TypeDraft resourceDraft = mock(TypeDraft.class); + final Optional filteredDraft = typeSyncOptions.applyBeforeCreateCallBack(resourceDraft); + + assertThat(filteredDraft).containsSame(resourceDraft); + } + + @Test + public void applyBeforeCreateCallBack_WithNullReturnCallback_ShouldReturnEmptyOptional() { + final Function draftFunction = typeDraft -> null; + final TypeSyncOptions typeSyncOptions = TypeSyncOptionsBuilder.of(CTP_CLIENT) + .beforeCreateCallback(draftFunction) + .build(); + assertThat(typeSyncOptions.getBeforeCreateCallback()).isNotNull(); + + final TypeDraft resourceDraft = mock(TypeDraft.class); + final Optional filteredDraft = typeSyncOptions.applyBeforeCreateCallBack(resourceDraft); + + assertThat(filteredDraft).isEmpty(); + } + +} From 8f45ac2ddd3fe62f71a6029ba1e1cb88962bd22e Mon Sep 17 00:00:00 2001 From: aoz Date: Tue, 9 Oct 2018 17:01:04 +0200 Subject: [PATCH 024/164] #300 - fix some typos and grammar in type document. --- docs/usage/TYPE_SYNC.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/usage/TYPE_SYNC.md b/docs/usage/TYPE_SYNC.md index 98baffde04..41b20cbde0 100644 --- a/docs/usage/TYPE_SYNC.md +++ b/docs/usage/TYPE_SYNC.md @@ -1,6 +1,6 @@ # commercetools type sync -Utility which provides an API for building CTP type update actions and type synchronisation. +A utility which provides an API for building CTP type update actions and type synchronization. @@ -22,10 +22,10 @@ Utility which provides an API for building CTP type update actions and type sync #### Prerequisites 1. The sync expects a list of non-null `TypeDrafts` objects that have their `key` fields set to match the -types from the source to the target. Also the target project is expected to have the `key` fields set, otherwise they won't be +types from the source to the target. Also, the target project is expected to have the `key` fields set, otherwise they won't be matched. 2. It is an important responsibility of the user of the library to instantiate a `sphereClient` that has the following properties: - - Limits the amount of concurrent requests done to CTP. This can be done by decorating the `sphereClient` with + - Limits the number of concurrent requests done to CTP. This can be done by decorating the `sphereClient` with [QueueSphereClientDecorator](http://commercetools.github.io/commercetools-jvm-sdk/apidocs/io/sphere/sdk/client/QueueSphereClientDecorator.html) - Retries on 5xx errors with a retry strategy. This can be achieved by decorating the `sphereClient` with the [RetrySphereClientDecorator](http://commercetools.github.io/commercetools-jvm-sdk/apidocs/io/sphere/sdk/client/RetrySphereClientDecorator.html) @@ -50,14 +50,14 @@ events. - `beforeUpdateCallback` a filter function which can be applied on a generated list of update actions. It allows the user to intercept type -update and modify (add/remove) update actions just before they are send to CTP API. +update and modify (add/remove) update actions just before they are sent to CTP API. - `beforeCreateCallback` a filter function which can be applied on a type draft before a request to create it on CTP is issued. It allows the -user to intercept type create requests modify the draft before the create request is sent to CTP API. +user to intercept type create request to modify the draft before the create request is sent to CTP API. Example of options usage, that sets the error and warning callbacks to output the message to the log error and warning -streams, would look as follows: +streams would look as follows: ```java final Logger logger = LoggerFactory.getLogger(MySync.class); final TypeSyncOptions typeSyncOptions = TypeSyncOptionsBuilder.of(sphereClient) @@ -78,7 +78,7 @@ CompletionStage syncStatisticsStage = typeSync.sync(typeDraf The result of the completing the `syncStatisticsStage` in the previous code snippet contains a `TypeSyncStatistics` which contains all the stats of the sync process; which includes a report message, the total number of updated, created, failed, processed types and the processing time of the last sync batch in different time units and in a -human readable format. +human-readable format. ````java final TypeSyncStatistics stats = syncStatisticsStage.toCompletebleFuture().join(); @@ -111,7 +111,7 @@ and field definitions can be found [here](/src/test/java/com/commercetools/sync/ ## Caveats -1. Types are either created or updated. Currently the tool does not support type deletion. +1. Types are either created or updated. Currently, the tool does not support type deletion. 2. Updating the label of enum values and localized enum values of field definition is not supported yet. 3. Removing the enum values from the field definition is not supported yet. 4. Updating the input hint of field definition is not supported yet. \ No newline at end of file From 4f5a08e98cb7c00d493042b0cad6311ca81f6d97 Mon Sep 17 00:00:00 2001 From: aoz Date: Mon, 15 Oct 2018 16:35:30 +0200 Subject: [PATCH 025/164] #300 - Review changes. --- .../services/impl/TypeServiceImplIT.java | 4 ++-- .../com/commercetools/sync/types/TypeSync.java | 4 ++-- .../sync/types/TypeSyncOptionsBuilderTest.java | 14 +------------- 3 files changed, 5 insertions(+), 17 deletions(-) diff --git a/src/integration-test/java/com/commercetools/sync/integration/services/impl/TypeServiceImplIT.java b/src/integration-test/java/com/commercetools/sync/integration/services/impl/TypeServiceImplIT.java index f5b35f6466..c43103199d 100644 --- a/src/integration-test/java/com/commercetools/sync/integration/services/impl/TypeServiceImplIT.java +++ b/src/integration-test/java/com/commercetools/sync/integration/services/impl/TypeServiceImplIT.java @@ -159,7 +159,7 @@ public void createType_WithValidType_ShouldCreateType() { } @Test - public void createType_WithInvalidType_ShouldNotCreateType() { + public void createType_WithInvalidType_ShouldCompleteExceptionally() { final TypeDraft newTypeDraft = TypeDraftBuilder.of( TYPE_KEY_1, null, @@ -205,7 +205,7 @@ public void updateType_WithValidChanges_ShouldUpdateTypeCorrectly() { } @Test - public void updateType_WithInvalidChanges_ShouldNotUpdateType() { + public void updateType_WithInvalidChanges_ShouldCompleteExceptionally() { final Optional typeOptional = CTP_TARGET_CLIENT .execute(TypeQuery.of() .withPredicates(typeQueryModel -> typeQueryModel.key().is(OLD_TYPE_KEY))) diff --git a/src/main/java/com/commercetools/sync/types/TypeSync.java b/src/main/java/com/commercetools/sync/types/TypeSync.java index ab5b6bffcd..377cfd0b4f 100644 --- a/src/main/java/com/commercetools/sync/types/TypeSync.java +++ b/src/main/java/com/commercetools/sync/types/TypeSync.java @@ -156,7 +156,7 @@ private CompletionStage> fetchExistingTypes(@Nonnull final Set a type that extends of {@link WithKey}. * @return the map of keys to {@link Type}/{@link TypeDraft} instances. */ - private Map getKeysTypeMap(@Nonnull final List types) { + private Map getTypeKeysMap(@Nonnull final List types) { return types.stream().collect(Collectors.toMap(WithKey::getKey, p -> p, (typeA, typeB) -> typeB)); } @@ -188,7 +188,7 @@ private void handleError(@Nonnull final String errorMessage, @Nullable final Thr private CompletionStage syncBatch( @Nonnull final List oldTypes, @Nonnull final List newTypes) { - final Map oldTypeMap = getKeysTypeMap(oldTypes); + final Map oldTypeMap = getTypeKeysMap(oldTypes); return CompletableFuture.allOf(newTypes .stream() diff --git a/src/test/java/com/commercetools/sync/types/TypeSyncOptionsBuilderTest.java b/src/test/java/com/commercetools/sync/types/TypeSyncOptionsBuilderTest.java index eace8f32d5..eb70c0ecd6 100644 --- a/src/test/java/com/commercetools/sync/types/TypeSyncOptionsBuilderTest.java +++ b/src/test/java/com/commercetools/sync/types/TypeSyncOptionsBuilderTest.java @@ -36,7 +36,6 @@ public void of_WithClient_ShouldCreateTypeSyncOptionsBuilder() { public void build_WithClient_ShouldBuildSyncOptions() { final TypeSyncOptions typeSyncOptions = typeSyncOptionsBuilder.build(); assertThat(typeSyncOptions).isNotNull(); - assertThat(typeSyncOptions.shouldAllowUuidKeys()).isFalse(); assertThat(typeSyncOptions.getBeforeUpdateCallback()).isNull(); assertThat(typeSyncOptions.getBeforeCreateCallback()).isNull(); assertThat(typeSyncOptions.getErrorCallBack()).isNull(); @@ -64,16 +63,6 @@ public void beforeCreateCallback_WithFilterAsCallback_ShouldSetCallback() { assertThat(typeSyncOptions.getBeforeCreateCallback()).isNotNull(); } - - @Test - public void allowUuid_WithFalse_ShouldSetFlag() { - typeSyncOptionsBuilder.allowUuidKeys(true); - - final TypeSyncOptions typeSyncOptions = typeSyncOptionsBuilder.build(); - assertThat(typeSyncOptions.shouldAllowUuidKeys()).isNotNull(); - assertThat(typeSyncOptions.shouldAllowUuidKeys()).isFalse(); - } - @Test public void errorCallBack_WithCallBack_ShouldSetCallBack() { final BiConsumer mockErrorCallBack = (errorMessage, errorException) -> { @@ -106,7 +95,6 @@ public void getThis_ShouldReturnCorrectInstance() { public void typeSyncOptionsBuilderSetters_ShouldBeCallableAfterBaseSyncOptionsBuildSetters() { final TypeSyncOptions typeSyncOptions = TypeSyncOptionsBuilder .of(CTP_CLIENT) - .allowUuidKeys(true) .batchSize(30) .beforeCreateCallback((newType) -> null) .beforeUpdateCallback((updateActions, newType, oldType) -> Collections.emptyList()) @@ -220,7 +208,7 @@ public void applyBeforeCreateCallBack_WithNullCallback_ShouldReturnIdenticalDraf } @Test - public void applyBeforeCreateCallBack_WithNullReturnCallback_ShouldReturnEmptyOptional() { + public void applyBeforeCreateCallBack_WithCallbackReturningNull_ShouldReturnEmptyOptional() { final Function draftFunction = typeDraft -> null; final TypeSyncOptions typeSyncOptions = TypeSyncOptionsBuilder.of(CTP_CLIENT) .beforeCreateCallback(draftFunction) From 19a5800c86a18954b7fe49d0e23778b5903c82a2 Mon Sep 17 00:00:00 2001 From: aoz Date: Mon, 15 Oct 2018 16:46:45 +0200 Subject: [PATCH 026/164] #300 - Review changes for benchmark. --- .../sync/benchmark/TypeSyncBenchmark.java | 109 ++++++++++-------- 1 file changed, 58 insertions(+), 51 deletions(-) diff --git a/src/benchmark/java/com/commercetools/sync/benchmark/TypeSyncBenchmark.java b/src/benchmark/java/com/commercetools/sync/benchmark/TypeSyncBenchmark.java index ef06330a83..c4d574a533 100644 --- a/src/benchmark/java/com/commercetools/sync/benchmark/TypeSyncBenchmark.java +++ b/src/benchmark/java/com/commercetools/sync/benchmark/TypeSyncBenchmark.java @@ -6,6 +6,7 @@ import com.commercetools.sync.types.TypeSyncOptions; import com.commercetools.sync.types.TypeSyncOptionsBuilder; import com.commercetools.sync.types.helpers.TypeSyncStatistics; +import com.sun.istack.internal.NotNull; import io.sphere.sdk.models.LocalizedString; import io.sphere.sdk.queries.PagedQueryResult; import io.sphere.sdk.types.FieldDefinition; @@ -19,6 +20,7 @@ import org.junit.BeforeClass; import org.junit.Test; +import javax.annotation.Nonnull; import java.io.IOException; import java.util.ArrayList; import java.util.List; @@ -70,6 +72,7 @@ public void setupTest() { typeSyncOptions = buildSyncOptions(); } + @Nonnull private TypeSyncOptions buildSyncOptions() { final BiConsumer errorCallBack = (errorMessage, exception) -> { errorCallBackMessages.add(errorMessage); @@ -78,9 +81,9 @@ private TypeSyncOptions buildSyncOptions() { final Consumer warningCallBack = warningMessage -> warningCallBackMessages.add(warningMessage); return TypeSyncOptionsBuilder.of(CTP_TARGET_CLIENT) - .errorCallback(errorCallBack) - .warningCallback(warningCallBack) - .build(); + .errorCallback(errorCallBack) + .warningCallback(warningCallBack) + .build(); } private void clearSyncTestCollections() { @@ -109,9 +112,9 @@ public void sync_NewTypes_ShouldCreateTypes() throws IOException { // Assert actual state of CTP project (total number of existing types) final CompletableFuture totalNumberOfTypes = CTP_TARGET_CLIENT.execute(TypeQuery.of()) - .thenApply(PagedQueryResult::getTotal) - .thenApply(Long::intValue) - .toCompletableFuture(); + .thenApply(PagedQueryResult::getTotal) + .thenApply(Long::intValue) + .toCompletableFuture(); executeBlocking(totalNumberOfTypes); assertThat(totalNumberOfTypes).isCompletedWithValue(NUMBER_OF_RESOURCE_UNDER_TEST); @@ -133,13 +136,13 @@ public void sync_ExistingTypes_ShouldUpdateTypes() throws IOException { // Create drafts to target project with different field type name CompletableFuture.allOf(typeDrafts.stream() - .map(TypeDraftBuilder::of) - .map(TypeSyncBenchmark::applyFieldDefinitionNameChange) - .map(TypeDraftBuilder::build) - .map(draft -> CTP_TARGET_CLIENT.execute(TypeCreateCommand.of(draft))) - .map(CompletionStage::toCompletableFuture) - .toArray(CompletableFuture[]::new)) - .join(); + .map(TypeDraftBuilder::of) + .map(TypeSyncBenchmark::applyFieldDefinitionNameChange) + .map(TypeDraftBuilder::build) + .map(draft -> CTP_TARGET_CLIENT.execute(TypeCreateCommand.of(draft))) + .map(CompletionStage::toCompletableFuture) + .toArray(CompletableFuture[]::new)) + .join(); // Sync new drafts final TypeSync typeSync = new TypeSync(typeSyncOptions); @@ -158,10 +161,11 @@ public void sync_ExistingTypes_ShouldUpdateTypes() throws IOException { // Assert actual state of CTP project (number of updated types) final CompletableFuture totalNumberOfUpdatedTypes = CTP_TARGET_CLIENT.execute(TypeQuery.of() - .withPredicates(p -> p.fieldDefinitions().name().is(FIELD_DEFINITION_NAME_1))) - .thenApply(PagedQueryResult::getTotal) - .thenApply(Long::intValue) - .toCompletableFuture(); + .withPredicates(p -> p.fieldDefinitions().name().is( + FIELD_DEFINITION_NAME_1))) + .thenApply(PagedQueryResult::getTotal) + .thenApply(Long::intValue) + .toCompletableFuture(); executeBlocking(totalNumberOfUpdatedTypes); assertThat(totalNumberOfUpdatedTypes).isCompletedWithValue(NUMBER_OF_RESOURCE_UNDER_TEST); @@ -169,9 +173,9 @@ public void sync_ExistingTypes_ShouldUpdateTypes() throws IOException { // Assert actual state of CTP project (total number of existing types) final CompletableFuture totalNumberOfTypes = CTP_TARGET_CLIENT.execute(TypeQuery.of()) - .thenApply(PagedQueryResult::getTotal) - .thenApply(Long::intValue) - .toCompletableFuture(); + .thenApply(PagedQueryResult::getTotal) + .thenApply(Long::intValue) + .toCompletableFuture(); executeBlocking(totalNumberOfTypes); assertThat(totalNumberOfTypes).isCompletedWithValue(NUMBER_OF_RESOURCE_UNDER_TEST); @@ -195,13 +199,13 @@ public void sync_WithSomeExistingTypes_ShouldSyncTypes() throws IOException { // Create first half of drafts to target project with different field definition name CompletableFuture.allOf(firstHalf.stream() - .map(TypeDraftBuilder::of) - .map(TypeSyncBenchmark::applyFieldDefinitionNameChange) - .map(TypeDraftBuilder::build) - .map(draft -> CTP_TARGET_CLIENT.execute(TypeCreateCommand.of(draft))) - .map(CompletionStage::toCompletableFuture) - .toArray(CompletableFuture[]::new)) - .join(); + .map(TypeDraftBuilder::of) + .map(TypeSyncBenchmark::applyFieldDefinitionNameChange) + .map(TypeDraftBuilder::build) + .map(draft -> CTP_TARGET_CLIENT.execute(TypeCreateCommand.of(draft))) + .map(CompletionStage::toCompletableFuture) + .toArray(CompletableFuture[]::new)) + .join(); // Sync new drafts final TypeSync typeSync = new TypeSync(typeSyncOptions); @@ -218,22 +222,23 @@ public void sync_WithSomeExistingTypes_ShouldSyncTypes() throws IOException { .isLessThanOrEqualTo(THRESHOLD); // Assert actual state of CTP project (number of updated types) - final CompletableFuture totalNumberOfUpdatedTypes = + final CompletableFuture totalNumberOfUpdatedTypesWithOldFielDefinitionName = CTP_TARGET_CLIENT.execute(TypeQuery.of() - .withPredicates(p -> p.fieldDefinitions().name().is(FIELD_DEFINITION_NAME_1 + "_old"))) - .thenApply(PagedQueryResult::getTotal) - .thenApply(Long::intValue) - .toCompletableFuture(); + .withPredicates(p -> p.fieldDefinitions().name().is( + FIELD_DEFINITION_NAME_1 + "_old"))) + .thenApply(PagedQueryResult::getTotal) + .thenApply(Long::intValue) + .toCompletableFuture(); - executeBlocking(totalNumberOfUpdatedTypes); - assertThat(totalNumberOfUpdatedTypes).isCompletedWithValue(0); + executeBlocking(totalNumberOfUpdatedTypesWithOldFielDefinitionName); + assertThat(totalNumberOfUpdatedTypesWithOldFielDefinitionName).isCompletedWithValue(0); // Assert actual state of CTP project (total number of existing types) final CompletableFuture totalNumberOfTypes = CTP_TARGET_CLIENT.execute(TypeQuery.of()) - .thenApply(PagedQueryResult::getTotal) - .thenApply(Long::intValue) - .toCompletableFuture(); + .thenApply(PagedQueryResult::getTotal) + .thenApply(Long::intValue) + .toCompletableFuture(); executeBlocking(totalNumberOfTypes); assertThat(totalNumberOfTypes).isCompletedWithValue(NUMBER_OF_RESOURCE_UNDER_TEST); @@ -249,28 +254,30 @@ public void sync_WithSomeExistingTypes_ShouldSyncTypes() throws IOException { saveNewResult(SyncSolutionInfo.LIB_VERSION, TYPE_SYNC, UPDATES_ONLY, totalTime); } - private List buildTypeDrafts(final int numberOfTypes) { + @Nonnull + private static List buildTypeDrafts(final int numberOfTypes) { return IntStream .range(0, numberOfTypes) .mapToObj(i -> TypeDraftBuilder.of( - "key__" + Integer.toString(i), - LocalizedString.ofEnglish("name__" + Integer.toString(i)), + format("key__%d", i), + LocalizedString.ofEnglish(format("name__%d", i)), ResourceTypeIdsSetBuilder.of().addCategories().build()) - .description(LocalizedString.ofEnglish("newDescription")) - .fieldDefinitions(singletonList(FIELD_DEFINITION_1)) - .build()) + .description(LocalizedString.ofEnglish(format("description__%d", i))) + .fieldDefinitions(singletonList(FIELD_DEFINITION_1)) + .build()) .collect(Collectors.toList()); } - private static TypeDraftBuilder applyFieldDefinitionNameChange(final TypeDraftBuilder builder) { + @NotNull + private static TypeDraftBuilder applyFieldDefinitionNameChange(@Nonnull final TypeDraftBuilder builder) { final List list = builder.getFieldDefinitions() - .stream() - .map(fieldDefinition -> FieldDefinition.of(fieldDefinition.getType(), - fieldDefinition.getName() + "_old", - fieldDefinition.getLabel(), - fieldDefinition.isRequired(), - fieldDefinition.getInputHint())) - .collect(Collectors.toList()); + .stream() + .map(fieldDefinition -> FieldDefinition.of(fieldDefinition.getType(), + fieldDefinition.getName() + "_old", + fieldDefinition.getLabel(), + fieldDefinition.isRequired(), + fieldDefinition.getInputHint())) + .collect(Collectors.toList()); return builder.fieldDefinitions(list); } From 026071b1b61aa32967a43a5d9c8d8d5120f14906 Mon Sep 17 00:00:00 2001 From: aoz Date: Mon, 15 Oct 2018 17:55:07 +0200 Subject: [PATCH 027/164] #301 - update release notes for type sync --- docs/RELEASE_NOTES.md | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/docs/RELEASE_NOTES.md b/docs/RELEASE_NOTES.md index 18b0033fa7..d13cd298ff 100644 --- a/docs/RELEASE_NOTES.md +++ b/docs/RELEASE_NOTES.md @@ -58,12 +58,20 @@ [Javadoc](https://commercetools.github.io/commercetools-sync-java/v/v1.0.0-M15/) | [Jar](https://bintray.com/commercetools/maven/commercetools-sync-java/v1.0.0-M15) -**New Features** (1) +**New Features** (8) - **Commons** - Added `OptionalUtils#filterEmptyOptionals` which are utility methods that filter out the empty optionals in a supplied list (with a varargs variation) returning a list of the contents of the non-empty optionals. [#255](https://github.com/commercetools/commercetools-sync-java/issues/255) +- **Type Sync** - Support for syncing types. [#300](https://github.com/commercetools/commercetools-sync-java/issues/300) For more info how to use it please refer to [Type usage doc](/docs/usage/TYPE_SYNC.md). +- **Type Sync** - Exposed `TypeSyncUtils#buildActions` which calculates all needed update actions after comparing a `Type` and a `TypeDraft`. [#300](https://github.com/commercetools/commercetools-sync-java/issues/300) +- **Type Sync** - Exposed `TypeUpdateActionUtils` which contains utils for calculating needed update actions after comparing individual fields of a `Type` and a `TypeDraft`. [#300](https://github.com/commercetools/commercetools-sync-java/issues/300) +- **Type Sync** - Exposed `LocalizedEnumUpdateActionUtils` which contains utils for calculating needed update actions after comparing two lists of `LocalizedEnumValue`s. [#300](https://github.com/commercetools/commercetools-sync-java/issues/300) +- **Type Sync** - Exposed `PlainEnumUpdateActionUtils` which contains utils for calculating needed update actions after comparing two lists of `EnumValue`s. [#300](https://github.com/commercetools/commercetools-sync-java/issues/300) +- **Type Sync** - Exposed `FieldDefinitionsUpdateActionUtils` which contains utils for calculating needed update actions after comparing a list of `FieldDefinition`s and a list of `FieldDefinition`s. [#300](https://github.com/commercetools/commercetools-sync-java/issues/300) +- **Type Sync** - Exposed `FieldDefinitionUpdateActionUtils` which contains utils for calculating needed update actions after comparing a `FieldDefinition` and a `FieldDefinition`. [#300](https://github.com/commercetools/commercetools-sync-java/issues/300) - +**Enhancements** (1) +- **Type Sync** - Added `TypeSyncBenchmark` to benchmark the type sync, to be able to compare the performance of the sync with the future releases. [#300](https://github.com/commercetools/commercetools-sync-java/issues/300) --> @@ -72,15 +80,6 @@ optionals. [#255](https://github.com/commercetools/commercetools-sync-java/issue [Javadoc](https://commercetools.github.io/commercetools-sync-java/v/v1.0.0-M14/) | [Jar](https://bintray.com/commercetools/maven/commercetools-sync-java/v1.0.0-M14) -**New Features** (7) -- **Type Sync** - Support for syncing types. [#300](https://github.com/commercetools/commercetools-sync-java/issues/300) For more info how to use it please refer to [Type usage doc](/docs/usage/TYPE_SYNC.md). -- **Type Sync** - Exposed `TypeSyncUtils#buildActions` which calculates all needed update actions after comparing a `Type` and a `TypeDraft`. [#300](https://github.com/commercetools/commercetools-sync-java/issues/300) -- **Type Sync** - Exposed `TypeUpdateActionUtils` which contains utils for calculating needed update actions after comparing individual fields of a `Type` and a `TypeDraft`. [#300](https://github.com/commercetools/commercetools-sync-java/issues/300) -- **Type Sync** - Exposed `LocalizedEnumUpdateActionUtils` which contains utils for calculating needed update actions after comparing two lists of `LocalizedEnumValue`s. [#300](https://github.com/commercetools/commercetools-sync-java/issues/300) -- **Type Sync** - Exposed `PlainEnumUpdateActionUtils` which contains utils for calculating needed update actions after comparing two lists of `EnumValue`s. [#300](https://github.com/commercetools/commercetools-sync-java/issues/300) -- **Type Sync** - Exposed `FieldDefinitionsUpdateActionUtils` which contains utils for calculating needed update actions after comparing a list of `FieldDefinition`s and a list of `FieldDefinition`s. [#300](https://github.com/commercetools/commercetools-sync-java/issues/300) -- **Type Sync** - Exposed `FieldDefinitionUpdateActionUtils` which contains utils for calculating needed update actions after comparing a `FieldDefinition` and a `FieldDefinition`. [#300](https://github.com/commercetools/commercetools-sync-java/issues/300) - **Enhancements** (8) - **Product Sync** - Products create and update requests are now issued in parallel. This should lead to a performance improvement. [#238](https://github.com/commercetools/commercetools-sync-java/issues/238) - **Commons** - Bumped `com.adarshr.test-logger` to 1.5.0. From f448662f2c8bb5ae314bdf66f1542d66cdf1870d Mon Sep 17 00:00:00 2001 From: aoz Date: Mon, 15 Oct 2018 17:55:40 +0200 Subject: [PATCH 028/164] #301 - fix import. --- .../com/commercetools/sync/benchmark/TypeSyncBenchmark.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/benchmark/java/com/commercetools/sync/benchmark/TypeSyncBenchmark.java b/src/benchmark/java/com/commercetools/sync/benchmark/TypeSyncBenchmark.java index c4d574a533..388a1c309a 100644 --- a/src/benchmark/java/com/commercetools/sync/benchmark/TypeSyncBenchmark.java +++ b/src/benchmark/java/com/commercetools/sync/benchmark/TypeSyncBenchmark.java @@ -6,7 +6,6 @@ import com.commercetools.sync.types.TypeSyncOptions; import com.commercetools.sync.types.TypeSyncOptionsBuilder; import com.commercetools.sync.types.helpers.TypeSyncStatistics; -import com.sun.istack.internal.NotNull; import io.sphere.sdk.models.LocalizedString; import io.sphere.sdk.queries.PagedQueryResult; import io.sphere.sdk.types.FieldDefinition; @@ -268,7 +267,7 @@ private static List buildTypeDrafts(final int numberOfTypes) { .collect(Collectors.toList()); } - @NotNull + @Nonnull private static TypeDraftBuilder applyFieldDefinitionNameChange(@Nonnull final TypeDraftBuilder builder) { final List list = builder.getFieldDefinitions() .stream() From 39132903439700783b1e2c0fdfc7287f64578d60 Mon Sep 17 00:00:00 2001 From: aoz Date: Tue, 16 Oct 2018 17:33:26 +0200 Subject: [PATCH 029/164] #300 - remove uuid flag from type sync options --- src/main/java/com/commercetools/sync/types/TypeSyncOptions.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/java/com/commercetools/sync/types/TypeSyncOptions.java b/src/main/java/com/commercetools/sync/types/TypeSyncOptions.java index 51b28dbaf2..10a33ef333 100644 --- a/src/main/java/com/commercetools/sync/types/TypeSyncOptions.java +++ b/src/main/java/com/commercetools/sync/types/TypeSyncOptions.java @@ -25,13 +25,11 @@ public final class TypeSyncOptions extends BaseSyncOptions { Type, List>> beforeUpdateCallback, @Nullable final Function beforeCreateCallback ) { - super( ctpClient, updateActionErrorCallBack, updateActionWarningCallBack, batchSize, - false, beforeUpdateCallback, beforeCreateCallback ); From 256554e04c5198c28a36378d85fcd0a666b703b9 Mon Sep 17 00:00:00 2001 From: aoz Date: Thu, 18 Oct 2018 14:27:53 +0200 Subject: [PATCH 030/164] #300- refactor type service and add test. --- .../services/impl/TypeServiceImplIT.java | 181 +++++++++++++----- .../sync/services/TypeService.java | 4 +- .../sync/services/impl/TypeServiceImpl.java | 32 +++- .../commercetools/sync/types/TypeSync.java | 25 ++- 4 files changed, 177 insertions(+), 65 deletions(-) diff --git a/src/integration-test/java/com/commercetools/sync/integration/services/impl/TypeServiceImplIT.java b/src/integration-test/java/com/commercetools/sync/integration/services/impl/TypeServiceImplIT.java index c43103199d..c0aa12c990 100644 --- a/src/integration-test/java/com/commercetools/sync/integration/services/impl/TypeServiceImplIT.java +++ b/src/integration-test/java/com/commercetools/sync/integration/services/impl/TypeServiceImplIT.java @@ -5,18 +5,24 @@ import com.commercetools.sync.services.impl.TypeServiceImpl; import com.commercetools.sync.types.TypeSyncOptions; import com.commercetools.sync.types.TypeSyncOptionsBuilder; +import io.sphere.sdk.client.BadGatewayException; +import io.sphere.sdk.client.SphereClient; import io.sphere.sdk.models.LocalizedString; import io.sphere.sdk.types.ResourceTypeIdsSetBuilder; import io.sphere.sdk.types.Type; import io.sphere.sdk.types.TypeDraft; import io.sphere.sdk.types.TypeDraftBuilder; import io.sphere.sdk.types.commands.TypeCreateCommand; +import io.sphere.sdk.types.commands.updateactions.ChangeKey; import io.sphere.sdk.types.commands.updateactions.ChangeName; import io.sphere.sdk.types.queries.TypeQuery; +import io.sphere.sdk.utils.CompletableFutureUtils; import org.junit.AfterClass; import org.junit.Before; import org.junit.Test; +import java.util.ArrayList; +import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Locale; @@ -30,8 +36,13 @@ import static com.commercetools.sync.integration.types.utils.TypeITUtils.TYPE_DESCRIPTION_1; import static com.commercetools.sync.integration.types.utils.TypeITUtils.TYPE_KEY_1; import static com.commercetools.sync.integration.types.utils.TypeITUtils.TYPE_NAME_1; +import static java.lang.String.format; +import static java.util.Collections.singleton; import static java.util.Collections.singletonList; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; public class TypeServiceImplIT { private TypeService typeService; @@ -39,15 +50,26 @@ public class TypeServiceImplIT { private static final String OLD_TYPE_NAME = "old_type_name"; private static final Locale OLD_TYPE_LOCALE = Locale.ENGLISH; + private List errorCallBackMessages; + private List errorCallBackExceptions; + /** * Deletes types from source and target CTP projects, then it populates target CTP project with test data. */ @Before public void setup() { + errorCallBackMessages = new ArrayList<>(); + errorCallBackExceptions = new ArrayList<>(); + deleteTypesFromTargetAndSource(); createCategoriesCustomType(OLD_TYPE_KEY, OLD_TYPE_LOCALE, OLD_TYPE_NAME, CTP_TARGET_CLIENT); - final TypeSyncOptions typeSyncOptions = TypeSyncOptionsBuilder.of(CTP_TARGET_CLIENT).build(); + final TypeSyncOptions typeSyncOptions = TypeSyncOptionsBuilder.of(CTP_TARGET_CLIENT) + .errorCallback((errorMessage, exception) -> { + errorCallBackMessages.add(errorMessage); + errorCallBackExceptions.add(exception); + }) + .build(); typeService = new TypeServiceImpl(typeSyncOptions); } @@ -65,14 +87,18 @@ public void fetchCachedTypeId_WithNonExistingType_ShouldNotFetchAType() { .toCompletableFuture() .join(); assertThat(typeId).isEmpty(); + assertThat(errorCallBackExceptions).isEmpty(); + assertThat(errorCallBackMessages).isEmpty(); } @Test public void fetchCachedTypeId_WithExistingType_ShouldFetchTypeAndCache() { final Optional typeId = typeService.fetchCachedTypeId(OLD_TYPE_KEY) - .toCompletableFuture() - .join(); + .toCompletableFuture() + .join(); assertThat(typeId).isNotEmpty(); + assertThat(errorCallBackExceptions).isEmpty(); + assertThat(errorCallBackMessages).isEmpty(); } @Test @@ -83,51 +109,120 @@ public void fetchCachedTypeId_WithNewlyCreatedTypeAfterCaching_ShouldNotFetchNew // Create new type final String newTypeKey = "new_type_key"; final TypeDraft draft = TypeDraftBuilder - .of(newTypeKey, LocalizedString.of(Locale.ENGLISH, "typeName"), - ResourceTypeIdsSetBuilder.of().addChannels()) - .build(); + .of(newTypeKey, LocalizedString.of(Locale.ENGLISH, "typeName"), + ResourceTypeIdsSetBuilder.of().addChannels()) + .build(); CTP_TARGET_CLIENT.execute(TypeCreateCommand.of(draft)).toCompletableFuture().join(); final Optional newTypeId = - typeService.fetchCachedTypeId(newTypeKey).toCompletableFuture().join(); + typeService.fetchCachedTypeId(newTypeKey).toCompletableFuture().join(); assertThat(newTypeId).isEmpty(); } @Test - public void fetchMatchingTypesByKeys_WithEmptySetOfKeys_ShouldReturnEmptyList() { + public void fetchMatchingTypesByKeys_WithEmptySetOfKeys_ShouldReturnEmptySet() { final Set typeKeys = new HashSet<>(); - final List matchingTypes = typeService.fetchMatchingTypesByKeys(typeKeys) - .toCompletableFuture() - .join(); + final Set matchingTypes = typeService.fetchMatchingTypesByKeys(typeKeys) + .toCompletableFuture() + .join(); assertThat(matchingTypes).isEmpty(); + assertThat(errorCallBackExceptions).isEmpty(); + assertThat(errorCallBackMessages).isEmpty(); } @Test - public void fetchMatchingTypesByKeys_WithNonExistingKeys_ShouldReturnEmptyList() { + public void fetchMatchingTypesByKeys_WithNonExistingKeys_ShouldReturnEmptySet() { final Set typeKeys = new HashSet<>(); typeKeys.add("type_key_1"); typeKeys.add("type_key_2"); - final List matchingTypes = typeService.fetchMatchingTypesByKeys(typeKeys) - .toCompletableFuture() - .join(); + final Set matchingTypes = typeService.fetchMatchingTypesByKeys(typeKeys) + .toCompletableFuture() + .join(); assertThat(matchingTypes).isEmpty(); + assertThat(errorCallBackExceptions).isEmpty(); + assertThat(errorCallBackMessages).isEmpty(); } @Test - public void fetchMatchingTypesByKeys_WithAnyExistingKeys_ShouldReturnAListOfTypes() { + public void fetchMatchingTypesByKeys_WithAnyExistingKeys_ShouldReturnASetOfTypes() { final Set typeKeys = new HashSet<>(); typeKeys.add(OLD_TYPE_KEY); - final List matchingTypes = typeService.fetchMatchingTypesByKeys(typeKeys) - .toCompletableFuture() - .join(); + final Set matchingTypes = typeService.fetchMatchingTypesByKeys(typeKeys) + .toCompletableFuture() + .join(); assertThat(matchingTypes).isNotEmpty(); assertThat(matchingTypes).hasSize(1); + assertThat(errorCallBackExceptions).isEmpty(); + assertThat(errorCallBackMessages).isEmpty(); + } + + @Test + public void fetchMatchingTypesByKeys_WithBadGateWayExceptionAlways_ShouldFail() { + // Mock sphere client to return BadeGatewayException on any request. + final SphereClient spyClient = spy(CTP_TARGET_CLIENT); + when(spyClient.execute(any(TypeQuery.class))) + .thenReturn(CompletableFutureUtils.exceptionallyCompletedFuture(new BadGatewayException())) + .thenCallRealMethod(); + + final TypeSyncOptions spyOptions = + TypeSyncOptionsBuilder.of(spyClient) + .errorCallback((errorMessage, exception) -> { + errorCallBackMessages.add(errorMessage); + errorCallBackExceptions.add(exception); + }) + .build(); + + final TypeService spyTypeService = new TypeServiceImpl(spyOptions); + + + final Set keys = new HashSet<>(); + keys.add(OLD_TYPE_KEY); + final Set fetchedTypes = spyTypeService.fetchMatchingTypesByKeys(keys) + .toCompletableFuture().join(); + assertThat(fetchedTypes).hasSize(0); + assertThat(errorCallBackExceptions).isNotEmpty(); + assertThat(errorCallBackExceptions.get(0).getCause()).isExactlyInstanceOf(BadGatewayException.class); + assertThat(errorCallBackMessages).isNotEmpty(); + assertThat(errorCallBackMessages.get(0)) + .isEqualToIgnoringCase(format("Failed to fetch types with keys: '%s'. Reason: %s", + keys.toString(), errorCallBackExceptions.get(0))); + } + + @Test + public void fetchMatchingTypesByKeys_WithAllExistingSetOfKeys_ShouldCacheFetchedTypeIds() { + final Set fetchedProductTypes = typeService.fetchMatchingTypesByKeys(singleton(OLD_TYPE_KEY)) + .toCompletableFuture().join(); + assertThat(fetchedProductTypes).hasSize(1); + + final Optional typeOptional = CTP_TARGET_CLIENT + .execute(TypeQuery.of().withPredicates(queryModel -> queryModel.key().is(OLD_TYPE_KEY))) + .toCompletableFuture() + .join() + .head(); + + assertThat(typeOptional).isNotNull(); + + // Change type old_type_key on ctp + final String newKey = "new_type_key"; + typeService.updateType(typeOptional.get(), Collections.singletonList(ChangeKey.of(newKey))) + .toCompletableFuture() + .join(); + + // Fetch cached id by old key + final Optional cachedTypeId = typeService.fetchCachedTypeId(OLD_TYPE_KEY) + .toCompletableFuture() + .join(); + + assertThat(cachedTypeId).isNotEmpty(); + assertThat(cachedTypeId).contains(typeOptional.get().getId()); + assertThat(errorCallBackExceptions).isEmpty(); + assertThat(errorCallBackMessages).isEmpty(); } @Test @@ -136,18 +231,18 @@ public void createType_WithValidType_ShouldCreateType() { TYPE_KEY_1, TYPE_NAME_1, ResourceTypeIdsSetBuilder.of().addCategories().build()) - .description(TYPE_DESCRIPTION_1) - .fieldDefinitions(singletonList(FIELD_DEFINITION_1)) - .build(); + .description(TYPE_DESCRIPTION_1) + .fieldDefinitions(singletonList(FIELD_DEFINITION_1)) + .build(); final Type createdType = typeService.createType(newTypeDraft) - .toCompletableFuture().join(); + .toCompletableFuture().join(); assertThat(createdType).isNotNull(); final Optional typeOptional = CTP_TARGET_CLIENT .execute(TypeQuery.of() - .withPredicates(typeQueryModel -> typeQueryModel.key().is(createdType.getKey()))) + .withPredicates(typeQueryModel -> typeQueryModel.key().is(createdType.getKey()))) .toCompletableFuture().join().head(); assertThat(typeOptional).isNotEmpty(); @@ -164,36 +259,36 @@ public void createType_WithInvalidType_ShouldCompleteExceptionally() { TYPE_KEY_1, null, ResourceTypeIdsSetBuilder.of().addCategories().build()) - .description(TYPE_DESCRIPTION_1) - .fieldDefinitions(singletonList(FIELD_DEFINITION_1)) - .build(); + .description(TYPE_DESCRIPTION_1) + .fieldDefinitions(singletonList(FIELD_DEFINITION_1)) + .build(); typeService.createType(newTypeDraft) - .exceptionally(exception -> { - assertThat(exception).isNotNull(); - assertThat(exception.getMessage()).contains("Request body does not contain valid JSON."); - return null; - }) - .toCompletableFuture().join(); + .exceptionally(exception -> { + assertThat(exception).isNotNull(); + assertThat(exception.getMessage()).contains("Request body does not contain valid JSON."); + return null; + }) + .toCompletableFuture().join(); } @Test public void updateType_WithValidChanges_ShouldUpdateTypeCorrectly() { final Optional typeOptional = CTP_TARGET_CLIENT .execute(TypeQuery.of() - .withPredicates(typeQueryModel -> typeQueryModel.key().is(OLD_TYPE_KEY))) + .withPredicates(typeQueryModel -> typeQueryModel.key().is(OLD_TYPE_KEY))) .toCompletableFuture().join().head(); assertThat(typeOptional).isNotNull(); final ChangeName changeNameUpdateAction = ChangeName.of(LocalizedString.ofEnglish("new_type_name")); final Type updatedType = typeService.updateType(typeOptional.get(), singletonList(changeNameUpdateAction)) - .toCompletableFuture().join(); + .toCompletableFuture().join(); assertThat(updatedType).isNotNull(); final Optional updatedTypeOptional = CTP_TARGET_CLIENT .execute(TypeQuery.of() - .withPredicates(typeQueryModel -> typeQueryModel.key().is(OLD_TYPE_KEY))) + .withPredicates(typeQueryModel -> typeQueryModel.key().is(OLD_TYPE_KEY))) .toCompletableFuture().join().head(); assertThat(typeOptional).isNotEmpty(); @@ -208,18 +303,18 @@ public void updateType_WithValidChanges_ShouldUpdateTypeCorrectly() { public void updateType_WithInvalidChanges_ShouldCompleteExceptionally() { final Optional typeOptional = CTP_TARGET_CLIENT .execute(TypeQuery.of() - .withPredicates(typeQueryModel -> typeQueryModel.key().is(OLD_TYPE_KEY))) + .withPredicates(typeQueryModel -> typeQueryModel.key().is(OLD_TYPE_KEY))) .toCompletableFuture().join().head(); assertThat(typeOptional).isNotNull(); final ChangeName changeNameUpdateAction = ChangeName.of(null); typeService.updateType(typeOptional.get(), singletonList(changeNameUpdateAction)) - .exceptionally(exception -> { - assertThat(exception).isNotNull(); - assertThat(exception.getMessage()).contains("Request body does not contain valid JSON."); - return null; - }) - .toCompletableFuture().join(); + .exceptionally(exception -> { + assertThat(exception).isNotNull(); + assertThat(exception.getMessage()).contains("Request body does not contain valid JSON."); + return null; + }) + .toCompletableFuture().join(); } } diff --git a/src/main/java/com/commercetools/sync/services/TypeService.java b/src/main/java/com/commercetools/sync/services/TypeService.java index 4395920c86..2336fd8652 100644 --- a/src/main/java/com/commercetools/sync/services/TypeService.java +++ b/src/main/java/com/commercetools/sync/services/TypeService.java @@ -36,11 +36,11 @@ public interface TypeService { * Queries existing {@link Type}'s against set of keys. * * @param keys {@link Set} of sku values, used in search predicate - * @return {@link CompletionStage} of matching types or empty list when there is no type with corresponding + * @return {@link CompletionStage} of matching types or empty set when there is no type with corresponding * {@code keys}. */ @Nonnull - CompletionStage> fetchMatchingTypesByKeys(@Nonnull final Set keys); + CompletionStage> fetchMatchingTypesByKeys(@Nonnull final Set keys); /** diff --git a/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java b/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java index 322df8266d..104b9396c8 100644 --- a/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java +++ b/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java @@ -24,12 +24,17 @@ import java.util.concurrent.CompletionStage; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Consumer; +import java.util.function.Function; +import java.util.stream.Collectors; + +import static java.lang.String.format; /** * Implementation of TypeService interface. * TODO: USE graphQL to get only keys. GITHUB ISSUE#84 */ public final class TypeServiceImpl implements TypeService { + private static final String FETCH_FAILED = "Failed to fetch types with keys: '%s'. Reason: %s"; private final BaseSyncOptions syncOptions; private final Map keyToIdCache = new ConcurrentHashMap<>(); private boolean isCached = false; @@ -53,17 +58,30 @@ public CompletionStage> fetchCachedTypeId(@Nonnull final String @Nonnull @Override - public CompletionStage> fetchMatchingTypesByKeys(@Nonnull final Set keys) { + public CompletionStage> fetchMatchingTypesByKeys(@Nonnull final Set keys) { if (keys.isEmpty()) { - return CompletableFuture.completedFuture(Collections.emptyList()); + return CompletableFuture.completedFuture(Collections.emptySet()); } - final TypeQuery query = TypeQueryBuilder - .of() - .plusPredicates(queryModel -> queryModel.key().isIn(keys)) - .build(); + final Function, List> typePageCallback + = typePage -> typePage; - return QueryExecutionUtils.queryAll(syncOptions.getCtpClient(), query); + return CtpQueryUtils.queryAll(syncOptions.getCtpClient(), + TypeQuery.of().withPredicates(queryModel -> queryModel.key().isIn(keys)), + typePageCallback) + .handle((fetchedTypes, sphereException) -> { + if (sphereException != null) { + syncOptions + .applyErrorCallback(format(FETCH_FAILED, keys, sphereException), + sphereException); + return Collections.emptySet(); + } + return fetchedTypes.stream() + .flatMap(List::stream) + .peek(type -> + keyToIdCache.put(type.getKey(), type.getId())) + .collect(Collectors.toSet()); + }); } @Nonnull diff --git a/src/main/java/com/commercetools/sync/types/TypeSync.java b/src/main/java/com/commercetools/sync/types/TypeSync.java index 377cfd0b4f..061c080e48 100644 --- a/src/main/java/com/commercetools/sync/types/TypeSync.java +++ b/src/main/java/com/commercetools/sync/types/TypeSync.java @@ -22,10 +22,9 @@ import static com.commercetools.sync.commons.utils.SyncUtils.batchElements; import static com.commercetools.sync.types.utils.TypeSyncUtils.buildActions; import static java.lang.String.format; -import static java.util.Collections.emptyList; +import static java.util.Collections.emptySet; import static java.util.Optional.ofNullable; import static java.util.concurrent.CompletableFuture.completedFuture; -import static java.util.stream.Collectors.toList; import static java.util.stream.Collectors.toSet; import static org.apache.commons.lang3.StringUtils.isBlank; @@ -86,7 +85,7 @@ protected CompletionStage syncBatches( /** * Fetches existing {@link Type} objects from CTP project that correspond to passed {@code batch}. * Having existing types fetched, {@code batch} is compared and synced with fetched objects by - * {@link TypeSync#syncBatch(List, List)} function. When fetching existing types results in + * {@link TypeSync#syncBatch(Set, Set)} function. When fetching existing types results in * an empty {@link TypeSyncStatistics} object then {@code batch} isn't processed. * * @param batch batch of drafts that need to be synced @@ -94,7 +93,7 @@ protected CompletionStage syncBatches( */ @Override protected CompletionStage processBatch(@Nonnull final List batch) { - final List validTypeDrafts = batch.stream().filter(this::validateDraft).collect(toList()); + final Set validTypeDrafts = batch.stream().filter(this::validateDraft).collect(toSet()); if (validTypeDrafts.isEmpty()) { statistics.incrementProcessed(batch.size()); @@ -135,28 +134,28 @@ private boolean validateDraft(@Nullable final TypeDraft draft) { * Given a set of type keys, fetches the corresponding types from CTP if they exist. * * @param keys the keys of the types that are wanted to be fetched. - * @return a {@link CompletionStage} which contains the list of types corresponding to the keys. + * @return a {@link CompletionStage} which contains the set of types corresponding to the keys. */ - private CompletionStage> fetchExistingTypes(@Nonnull final Set keys) { + private CompletionStage> fetchExistingTypes(@Nonnull final Set keys) { return typeService .fetchMatchingTypesByKeys(keys) .exceptionally(exception -> { final String errorMessage = format(CTP_TYPE_FETCH_FAILED, keys); handleError(errorMessage, exception, keys.size()); - return emptyList(); + return emptySet(); }); } /** - * Given a list of {@link Type} or {@link TypeDraft}, returns a map of keys to the + * Given a set of {@link Type} or {@link TypeDraft}, returns a map of keys to the * {@link Type}/{@link TypeDraft} instances. * * @param types list of {@link Type}/{@link TypeDraft} - * @param a type that extends of {@link WithKey}. + * @param a type that extends of {@link WithKey}. * @return the map of keys to {@link Type}/{@link TypeDraft} instances. */ - private Map getTypeKeysMap(@Nonnull final List types) { + private Map getTypeKeysMap(@Nonnull final Set types) { return types.stream().collect(Collectors.toMap(WithKey::getKey, p -> p, (typeA, typeB) -> typeB)); } @@ -178,7 +177,7 @@ private void handleError(@Nonnull final String errorMessage, @Nullable final Thr } /** - * Given a list of type drafts, attempts to sync the drafts with the existing types in the CTP + * Given a set of type drafts, attempts to sync the drafts with the existing types in the CTP * project. The type and the draft are considered to match if they have the same key. * * @param oldTypes old types. @@ -186,8 +185,8 @@ private void handleError(@Nonnull final String errorMessage, @Nullable final Thr * @return a {@link CompletionStage} which contains an empty result after execution of the update */ private CompletionStage syncBatch( - @Nonnull final List oldTypes, - @Nonnull final List newTypes) { + @Nonnull final Set oldTypes, + @Nonnull final Set newTypes) { final Map oldTypeMap = getTypeKeysMap(oldTypes); return CompletableFuture.allOf(newTypes From 8f1362fc7f021ff202c08af7663c6632cd0e5156 Mon Sep 17 00:00:00 2001 From: aoz Date: Thu, 18 Oct 2018 14:35:56 +0200 Subject: [PATCH 031/164] #300- refactor docs --- docs/usage/TYPE_SYNC.md | 4 ++-- .../com/commercetools/sync/types/TypeSyncOptionsBuilder.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/usage/TYPE_SYNC.md b/docs/usage/TYPE_SYNC.md index 41b20cbde0..49f85677d8 100644 --- a/docs/usage/TYPE_SYNC.md +++ b/docs/usage/TYPE_SYNC.md @@ -50,11 +50,11 @@ events. - `beforeUpdateCallback` a filter function which can be applied on a generated list of update actions. It allows the user to intercept type -update and modify (add/remove) update actions just before they are sent to CTP API. + **_update_** actions just before they are sent to CTP API. - `beforeCreateCallback` a filter function which can be applied on a type draft before a request to create it on CTP is issued. It allows the -user to intercept type create request to modify the draft before the create request is sent to CTP API. +user to intercept type **_create_** request to modify the draft before the create request is sent to CTP API. Example of options usage, that sets the error and warning callbacks to output the message to the log error and warning streams would look as follows: diff --git a/src/main/java/com/commercetools/sync/types/TypeSyncOptionsBuilder.java b/src/main/java/com/commercetools/sync/types/TypeSyncOptionsBuilder.java index 6f87ea7764..a9a8963ff4 100644 --- a/src/main/java/com/commercetools/sync/types/TypeSyncOptionsBuilder.java +++ b/src/main/java/com/commercetools/sync/types/TypeSyncOptionsBuilder.java @@ -45,7 +45,7 @@ public TypeSyncOptions build() { } /** - * Returns an instance of this class to be used in the super class generic methods. Please see the JavaDoc in the + * Returns an instance of this class to be used in the superclass's generic methods. Please see the JavaDoc in the * overridden method for further details. * * @return an instance of this class. From cb2dabd9d07d2663919d71abbd6337085ca2c2f6 Mon Sep 17 00:00:00 2001 From: aoz Date: Thu, 18 Oct 2018 14:37:33 +0200 Subject: [PATCH 032/164] #300- refactor make internal --- .../types/utils/EnumUpdateActionsUtils.java | 58 +++++++++---------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/src/main/java/com/commercetools/sync/types/utils/EnumUpdateActionsUtils.java b/src/main/java/com/commercetools/sync/types/utils/EnumUpdateActionsUtils.java index f2ffbc5f8c..7c619cafe2 100644 --- a/src/main/java/com/commercetools/sync/types/utils/EnumUpdateActionsUtils.java +++ b/src/main/java/com/commercetools/sync/types/utils/EnumUpdateActionsUtils.java @@ -18,33 +18,7 @@ import static java.lang.String.format; import static java.util.stream.Collectors.toMap; -public final class EnumUpdateActionsUtils { - - /** - * Given a list of new {@link EnumValue}s, gets a map where the keys are the enum value key, and the values - * are the enum instances. - * - * @param fieldDefinitionName the field definition name whose the enum values belong to. - * @param enumValues the list of enum values. - * @param the enum type of the elements of the list. - * @return a map with the enum value key as a key of the map, and the enum - * value as a value of the map. - */ - @Nonnull - public static Map getEnumValuesKeyMapWithKeyValidation( - @Nonnull final String fieldDefinitionName, - @Nonnull final List enumValues) { - - return enumValues.stream().collect( - toMap(WithKey::getKey, enumValue -> enumValue, - (enumValueA, enumValueB) -> { - throw new DuplicateKeyException(format("Enum Values have duplicated keys. " - + "Field definition name: '%s', Duplicated enum value: '%s'. " - + "Enum Values are expected to be unique inside their field definition.", - fieldDefinitionName, enumValueA.getKey())); - } - )); - } +final class EnumUpdateActionsUtils { /** * Compares the order of a list of old enum values and a list of new enum values. If there is a change in order, @@ -60,7 +34,7 @@ public static Map getEnumValuesKeyMapWithKeyValid * Otherwise, if the enum values order is identical, an empty optional is returned. */ @Nonnull - public static Optional> buildChangeEnumValuesOrderUpdateAction( + static Optional> buildChangeEnumValuesOrderUpdateAction( @Nonnull final String fieldDefinitionName, @Nonnull final List oldEnumValues, @Nonnull final List newEnumValues, @@ -106,7 +80,7 @@ public static Optional> buildChangeEnumVa * Otherwise, if the enum values are identical, an empty optional is returned. */ @Nonnull - public static List> buildAddEnumValuesUpdateActions( + static List> buildAddEnumValuesUpdateActions( @Nonnull final String fieldDefinitionName, @Nonnull final List oldEnumValues, @Nonnull final List newEnumValues, @@ -129,6 +103,32 @@ public static List> buildAddEnumValuesUpd .collect(Collectors.toList()); } + /** + * Given a list of new {@link EnumValue}s, gets a map where the keys are the enum value key, and the values + * are the enum instances. + * + * @param fieldDefinitionName the field definition name whose the enum values belong to. + * @param enumValues the list of enum values. + * @param the enum type of the elements of the list. + * @return a map with the enum value key as a key of the map, and the enum + * value as a value of the map. + */ + @Nonnull + private static Map getEnumValuesKeyMapWithKeyValidation( + @Nonnull final String fieldDefinitionName, + @Nonnull final List enumValues) { + + return enumValues.stream().collect( + toMap(WithKey::getKey, enumValue -> enumValue, + (enumValueA, enumValueB) -> { + throw new DuplicateKeyException(format("Enum Values have duplicated keys. " + + "Field definition name: '%s', Duplicated enum value: '%s'. " + + "Enum Values are expected to be unique inside their field definition.", + fieldDefinitionName, enumValueA.getKey())); + } + )); + } + private EnumUpdateActionsUtils() { } } From 8879163e8c573a67286ebf112b46d0e36763da0e Mon Sep 17 00:00:00 2001 From: aoz Date: Thu, 18 Oct 2018 14:41:57 +0200 Subject: [PATCH 033/164] #300- update release notes - add benchmark test as an enhancement entity --- docs/RELEASE_NOTES.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/RELEASE_NOTES.md b/docs/RELEASE_NOTES.md index 3b02b781b2..e6a9d181b0 100644 --- a/docs/RELEASE_NOTES.md +++ b/docs/RELEASE_NOTES.md @@ -70,9 +70,10 @@ optionals. [#255](https://github.com/commercetools/commercetools-sync-java/issue - **Type Sync** - Exposed `FieldDefinitionsUpdateActionUtils` which contains utils for calculating needed update actions after comparing a list of `FieldDefinition`s and a list of `FieldDefinition`s. [#300](https://github.com/commercetools/commercetools-sync-java/issues/300) - **Type Sync** - Exposed `FieldDefinitionUpdateActionUtils` which contains utils for calculating needed update actions after comparing a `FieldDefinition` and a `FieldDefinition`. [#300](https://github.com/commercetools/commercetools-sync-java/issues/300) -**Enhancements** (2) +**Enhancements** (3) - **Commons** - Bumped commercetools-jvm-sdk to version [1.35.0](http://commercetools.github.io/commercetools-jvm-sdk/apidocs/io/sphere/sdk/meta/ReleaseNotes.html#v1_35_0). - **Commons** - Bumped `mockito` to 2.23.0. +- **Type Sync** - Added `TypeSyncBenchmark` to benchmark the type sync, to be able to compare the performance of the sync with the future releases. [#300](https://github.com/commercetools/commercetools-sync-java/issues/300) **Changes** (3) - **Product Sync** - Reference keys are not validated if they are in UUID format anymore. [#166](https://github.com/commercetools/commercetools-sync-java/issues/166) From d608feaee23b6e32b7ac838a779baee45901bdc2 Mon Sep 17 00:00:00 2001 From: aoz Date: Thu, 18 Oct 2018 15:24:34 +0200 Subject: [PATCH 034/164] #300 - check controls for the field type on update actions --- .../sync/types/helpers/FieldTypeAssert.java | 55 ++++++++++++ .../FieldDefinitionUpdateActionUtils.java | 7 +- .../FieldDefinitionsUpdateActionUtils.java | 85 ++++++++++--------- .../FieldDefinitionUpdateActionUtilsTest.java | 83 ++++++++++++++++-- ...BuildFieldDefinitionUpdateActionsTest.java | 37 +++++++- ...ld-definitions-abc-without-field-type.json | 38 +++++++++ 6 files changed, 256 insertions(+), 49 deletions(-) create mode 100644 src/main/java/com/commercetools/sync/types/helpers/FieldTypeAssert.java create mode 100644 src/test/resources/com/commercetools/sync/types/utils/updatefielddefinitions/fields/type-with-field-definitions-abc-without-field-type.json diff --git a/src/main/java/com/commercetools/sync/types/helpers/FieldTypeAssert.java b/src/main/java/com/commercetools/sync/types/helpers/FieldTypeAssert.java new file mode 100644 index 0000000000..777efc1ba7 --- /dev/null +++ b/src/main/java/com/commercetools/sync/types/helpers/FieldTypeAssert.java @@ -0,0 +1,55 @@ +package com.commercetools.sync.types.helpers; + +import com.commercetools.sync.commons.exceptions.BuildUpdateActionException; +import io.sphere.sdk.types.FieldType; + + +public final class FieldTypeAssert { + + /** + * A utility method validates an old {@link FieldType} and a new {@link FieldType} + * and if the value of types {@code null}, {@link BuildUpdateActionException} will be thrown. + * + * @param oldFieldType the old field type. + * @param newFieldType the new field type. + */ + public static void assertTypesAreNull(final FieldType oldFieldType, + final FieldType newFieldType) + throws BuildUpdateActionException { + + if (oldFieldType == null && newFieldType == null) { + throw new BuildUpdateActionException("Field types are not set " + + "for both the old and new field definitions."); + } + + assertOldFieldType(oldFieldType); + assertNewFieldType(newFieldType); + } + + /** + * A utility method validates old {@link FieldType} and + * if the value of type {@code null}, {@link BuildUpdateActionException} will be thrown. + * + * @param oldFieldType the old field type. + */ + public static void assertOldFieldType(final FieldType oldFieldType) throws BuildUpdateActionException { + if (oldFieldType == null) { + throw new BuildUpdateActionException("Field type is not set for the old field definition."); + } + } + + /** + * A utility method validates new {@link FieldType} and + * if the value of type {@code null}, {@link BuildUpdateActionException} will be thrown. + * + * @param newFieldType the new field type. + */ + public static void assertNewFieldType(final FieldType newFieldType) throws BuildUpdateActionException { + if (newFieldType == null) { + throw new BuildUpdateActionException("Field type is not set for the new field definition."); + } + } + + private FieldTypeAssert() { + } +} \ No newline at end of file diff --git a/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionUpdateActionUtils.java b/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionUpdateActionUtils.java index d1e43994a5..b9ecd06e4e 100644 --- a/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionUpdateActionUtils.java +++ b/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionUpdateActionUtils.java @@ -1,5 +1,7 @@ package com.commercetools.sync.types.utils; +import com.commercetools.sync.commons.exceptions.BuildUpdateActionException; +import com.commercetools.sync.types.helpers.FieldTypeAssert; import io.sphere.sdk.commands.UpdateAction; import io.sphere.sdk.models.LocalizedString; import io.sphere.sdk.types.EnumFieldType; @@ -29,11 +31,14 @@ public final class FieldDefinitionUpdateActionUtils { * @param oldFieldDefinition the old field definition which should be updated. * @param newFieldDefinition the new field definition where we get the new fields. * @return A list with the update actions or an empty list if the field definition fields are identical. + * @throws BuildUpdateActionException in case there are field definitions with the null field type. */ @Nonnull public static List> buildActions( @Nonnull final FieldDefinition oldFieldDefinition, - @Nonnull final FieldDefinition newFieldDefinition) { + @Nonnull final FieldDefinition newFieldDefinition) throws BuildUpdateActionException { + + FieldTypeAssert.assertTypesAreNull(oldFieldDefinition.getType(), newFieldDefinition.getType()); final List> updateActions = filterEmptyOptionals(buildChangeLabelUpdateAction(oldFieldDefinition, newFieldDefinition)); diff --git a/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionsUpdateActionUtils.java b/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionsUpdateActionUtils.java index 4d501f4b60..02158e0491 100644 --- a/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionsUpdateActionUtils.java +++ b/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionsUpdateActionUtils.java @@ -3,8 +3,10 @@ import com.commercetools.sync.commons.exceptions.BuildUpdateActionException; import com.commercetools.sync.commons.exceptions.DuplicateKeyException; import com.commercetools.sync.commons.exceptions.DuplicateNameException; +import com.commercetools.sync.types.helpers.FieldTypeAssert; import io.sphere.sdk.commands.UpdateAction; import io.sphere.sdk.types.FieldDefinition; +import io.sphere.sdk.types.FieldType; import io.sphere.sdk.types.Type; import io.sphere.sdk.types.commands.updateactions.AddFieldDefinition; import io.sphere.sdk.types.commands.updateactions.ChangeFieldDefinitionOrder; @@ -12,6 +14,7 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; @@ -43,7 +46,8 @@ public final class FieldDefinitionsUpdateActionUtils { * @param newFieldDefinitions the new list of field definitions. * @return a list of field definitions update actions if the list of field definitions is not identical. * Otherwise, if the field definitions are identical, an empty list is returned. - * @throws BuildUpdateActionException in case there are field definitions drafts with duplicate names. + * @throws BuildUpdateActionException in case there are field definitions drafts with duplicate names or + * there are field definitions with the null field type. */ @Nonnull public static List> buildFieldDefinitionsUpdateActions( @@ -73,7 +77,8 @@ public static List> buildFieldDefinitionsUpdateActions( * @param newFieldDefinitions the new list of field definitions drafts. * @return a list of field definitions update actions if the list of field definitions is not identical. * Otherwise, if the field definitions are identical, an empty list is returned. - * @throws BuildUpdateActionException in case there are field definitions drafts with duplicate names. + * @throws BuildUpdateActionException in case there are field definitions with duplicate names or + * there are field definitions with the null field type. */ @Nonnull private static List> buildUpdateActions( @@ -114,11 +119,13 @@ private static List> buildUpdateActions( * in the new draft. If the field definition still exists in the new draft, then compare the field * definition fields (name, label, etc..), and add the computed actions to the list of update actions. * Otherwise, if the field definitions are identical, an empty optional is returned. + * @throws BuildUpdateActionException in case there are field definitions with duplicate names or + * there are field definitions with the null field type. */ @Nonnull private static List> buildRemoveFieldDefinitionOrFieldDefinitionUpdateActions( @Nonnull final List oldFieldDefinitions, - @Nonnull final List newFieldDefinitions) { + @Nonnull final List newFieldDefinitions) throws BuildUpdateActionException { final Map newFieldDefinitionsNameMap = newFieldDefinitions @@ -133,46 +140,48 @@ private static List> buildRemoveFieldDefinitionOrFieldDefinit } )); - return oldFieldDefinitions - .stream() - .map(oldFieldDefinition -> { - final String oldFieldDefinitionName = oldFieldDefinition.getName(); - final FieldDefinition matchingNewFieldDefinition = - newFieldDefinitionsNameMap.get(oldFieldDefinitionName); - - if (matchingNewFieldDefinition == null) { - return singletonList(RemoveFieldDefinition.of(oldFieldDefinitionName)); - } else { - if (haveSameFieldType(oldFieldDefinition, matchingNewFieldDefinition)) { - return buildActions(oldFieldDefinition, matchingNewFieldDefinition); - } else { - // this is a work around for changing the type of the definition. - // since there is no action, so we remove then add again. - return Arrays.asList( - RemoveFieldDefinition.of(oldFieldDefinitionName), - AddFieldDefinition.of(matchingNewFieldDefinition) - ); - } - } - }) - .flatMap(Collection::stream) - .collect(Collectors.toList()); + final List> updateActions = new ArrayList<>(); + + for (FieldDefinition oldFieldDefinition : oldFieldDefinitions) { + + FieldTypeAssert.assertOldFieldType(oldFieldDefinition.getType()); + + final String oldFieldDefinitionName = oldFieldDefinition.getName(); + final FieldDefinition matchingNewFieldDefinition = + newFieldDefinitionsNameMap.get(oldFieldDefinitionName); + + if (matchingNewFieldDefinition != null) { + + FieldTypeAssert.assertNewFieldType(matchingNewFieldDefinition.getType()); + + if (haveSameFieldType(oldFieldDefinition.getType(), matchingNewFieldDefinition.getType())) { + updateActions.addAll(buildActions(oldFieldDefinition, matchingNewFieldDefinition)); + } else { + // since there is no way to change a field type on CTP, + // we remove the field definition and add a new one with a new field type + updateActions.add(RemoveFieldDefinition.of(oldFieldDefinitionName)); + updateActions.add(AddFieldDefinition.of(matchingNewFieldDefinition)); + } + } else { + updateActions.add(RemoveFieldDefinition.of(oldFieldDefinitionName)); + } + } + + return updateActions; } /** - * Compares the field types of the {@code fieldDefinitionA} and the {@code fieldDefinitionB} and - * returns true if both field definitions have the same field type, false otherwise. + * Compares the field types of the {@code fieldTypeA} and the {@code fieldTypeB}. * - * @param fieldDefinitionA the first field to compare. - * @param fieldDefinitionB the second field definition to compare. - * @return true if both field definitions have the same field type, false otherwise. + * @param fieldTypeA the first type to compare. + * @param fieldTypeB the second field type to compare. + * @return true if both field types equal, false otherwise. */ private static boolean haveSameFieldType( - @Nonnull final FieldDefinition fieldDefinitionA, - @Nonnull final FieldDefinition fieldDefinitionB) { + @Nonnull final FieldType fieldTypeA, + @Nonnull final FieldType fieldTypeB) { - return fieldDefinitionA.getType() != null && fieldDefinitionB.getType() != null - && fieldDefinitionA.getType().getClass() == fieldDefinitionB.getType().getClass(); + return fieldTypeA.getClass() == fieldTypeB.getClass(); } /** @@ -227,8 +236,8 @@ private static Optional> buildChangeFieldDefinitionOrderUpdat * {@code oldFieldDefinitionNameMap}. If there are, then "add" field definition update actions are built. * Otherwise, if there are no new field definitions, then an empty list is returned. * - * @param oldFieldDefinitions the list of old {@link FieldDefinition}s - * @param newFieldDefinitions the list of new {@link FieldDefinition}s + * @param oldFieldDefinitions the list of old {@link FieldDefinition}s. + * @param newFieldDefinitions the list of new {@link FieldDefinition}s. * * @return a list of field definition update actions if there are new field definition that should be added. * Otherwise, if the field definitions are identical, an empty optional is returned. diff --git a/src/test/java/com/commercetools/sync/types/utils/FieldDefinitionUpdateActionUtilsTest.java b/src/test/java/com/commercetools/sync/types/utils/FieldDefinitionUpdateActionUtilsTest.java index ed3165d393..1276c2da48 100644 --- a/src/test/java/com/commercetools/sync/types/utils/FieldDefinitionUpdateActionUtilsTest.java +++ b/src/test/java/com/commercetools/sync/types/utils/FieldDefinitionUpdateActionUtilsTest.java @@ -1,5 +1,6 @@ package com.commercetools.sync.types.utils; +import com.commercetools.sync.commons.exceptions.BuildUpdateActionException; import io.sphere.sdk.commands.UpdateAction; import io.sphere.sdk.models.EnumValue; import io.sphere.sdk.models.LocalizedEnumValue; @@ -25,6 +26,7 @@ import static com.commercetools.sync.types.utils.FieldDefinitionUpdateActionUtils.buildChangeLabelUpdateAction; import static io.sphere.sdk.models.LocalizedString.ofEnglish; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; public class FieldDefinitionUpdateActionUtilsTest { private static final String FIELD_NAME_1 = "fieldName1"; @@ -67,7 +69,7 @@ public void buildChangeLabelAction_WithSameValues_ShouldReturnEmptyOptional() { } @Test - public void buildActions_WithNewDifferentValues_ShouldReturnActions() { + public void buildActions_WithNewDifferentValues_ShouldReturnActions() throws BuildUpdateActionException { final List> result = buildActions(old, newDifferent); assertThat(result).containsExactlyInAnyOrder( @@ -76,14 +78,14 @@ public void buildActions_WithNewDifferentValues_ShouldReturnActions() { } @Test - public void buildActions_WithSameValues_ShouldReturnEmpty() { + public void buildActions_WithSameValues_ShouldReturnEmpty() throws BuildUpdateActionException { final List> result = buildActions(old, newSame); assertThat(result).isEmpty(); } @Test - public void buildActions_WithNewPlainEnum_ShouldReturnAddEnumValueAction() { + public void buildActions_WithNewPlainEnum_ShouldReturnAddEnumValueAction() throws BuildUpdateActionException { final FieldDefinition oldFieldDefinition = FieldDefinition.of( EnumFieldType.of(Arrays.asList(ENUM_VALUE_A)), FIELD_NAME_1, @@ -99,16 +101,16 @@ public void buildActions_WithNewPlainEnum_ShouldReturnAddEnumValueAction() { false, TextInputHint.SINGLE_LINE); - final List> result = - buildActions(oldFieldDefinition, newFieldDefinition); - + final List> result = buildActions(oldFieldDefinition, newFieldDefinition); assertThat(result).containsExactly(AddEnumValue.of(FIELD_NAME_1, ENUM_VALUE_B)); } @Test - public void buildActions_WithoutOldPlainEnum_ShouldNotReturnAnyValueAction() { + public void buildActions_WithoutOldPlainEnum_ShouldNotReturnAnyValueAction() + throws BuildUpdateActionException { + final FieldDefinition oldFieldDefinition = FieldDefinition.of( EnumFieldType.of(Arrays.asList(ENUM_VALUE_A)), FIELD_NAME_1, @@ -124,7 +126,6 @@ public void buildActions_WithoutOldPlainEnum_ShouldNotReturnAnyValueAction() { TextInputHint.SINGLE_LINE); - //TODO: For type sync there is no remove enum values action for the field definitions. final List> result = buildActions(oldFieldDefinition, newFieldDefinition); assertThat(result).isEmpty(); @@ -132,7 +133,8 @@ public void buildActions_WithoutOldPlainEnum_ShouldNotReturnAnyValueAction() { @Test - public void buildActions_WithNewLocalizedEnum_ShouldReturnAddLocalizedEnumValueAction() { + public void buildActions_WithNewLocalizedEnum_ShouldReturnAddLocalizedEnumValueAction() + throws BuildUpdateActionException { final FieldDefinition oldFieldDefinition = FieldDefinition.of( LocalizedEnumFieldType.of(Arrays.asList(LOCALIZED_ENUM_VALUE_A)), @@ -156,5 +158,68 @@ public void buildActions_WithNewLocalizedEnum_ShouldReturnAddLocalizedEnumValueA + @Test + public void buildActions_WithFieldDefinitionsWithoutType_ShouldThrowBuildUpdateActionException() { + final FieldDefinition oldFieldDefinitionWithoutType = FieldDefinition.of( + null, + FIELD_NAME_1, + LocalizedString.ofEnglish(LABEL_1), + false, + TextInputHint.SINGLE_LINE); + + final FieldDefinition newFieldDefinitionWithoutType = FieldDefinition.of( + null, + FIELD_NAME_1, + LocalizedString.ofEnglish(LABEL_1), + false, + TextInputHint.SINGLE_LINE); + + assertThatThrownBy(() -> buildActions(oldFieldDefinitionWithoutType, newFieldDefinitionWithoutType)) + .hasMessage("Field types are not set for both the old and new field definitions.") + .isExactlyInstanceOf(BuildUpdateActionException.class); + } + + @Test + public void buildActions_WithOldFieldDefinitionWithoutType_ShouldThrowBuildUpdateActionException() { + final FieldDefinition oldFieldDefinitionWithoutType = FieldDefinition.of( + null, + FIELD_NAME_1, + LocalizedString.ofEnglish(LABEL_1), + false, + TextInputHint.SINGLE_LINE); + + final FieldDefinition newFieldDefinition = FieldDefinition.of( + LocalizedEnumFieldType.of(Arrays.asList(LOCALIZED_ENUM_VALUE_A, LOCALIZED_ENUM_VALUE_B)), + FIELD_NAME_1, + LocalizedString.ofEnglish(LABEL_1), + false, + TextInputHint.SINGLE_LINE); + + assertThatThrownBy(() -> buildActions(oldFieldDefinitionWithoutType, newFieldDefinition)) + .hasMessage("Field type is not set for the old field definition.") + .isExactlyInstanceOf(BuildUpdateActionException.class); + } + + @Test + public void buildActions_WithNewFieldDefinitionWithoutType_ShouldThrowBuildUpdateActionException() { + final FieldDefinition oldFieldDefinition = FieldDefinition.of( + LocalizedEnumFieldType.of(Arrays.asList(LOCALIZED_ENUM_VALUE_A)), + FIELD_NAME_1, + LocalizedString.ofEnglish(LABEL_1), + false, + TextInputHint.SINGLE_LINE); + + final FieldDefinition newFieldDefinitionWithoutType = FieldDefinition.of( + null, + FIELD_NAME_1, + LocalizedString.ofEnglish(LABEL_1), + false, + TextInputHint.SINGLE_LINE); + + assertThatThrownBy(() -> buildActions(oldFieldDefinition, newFieldDefinitionWithoutType)) + .hasMessage("Field type is not set for the new field definition.") + .isExactlyInstanceOf(BuildUpdateActionException.class); + } + } diff --git a/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildFieldDefinitionUpdateActionsTest.java b/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildFieldDefinitionUpdateActionsTest.java index c9e434f331..b31331014d 100644 --- a/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildFieldDefinitionUpdateActionsTest.java +++ b/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildFieldDefinitionUpdateActionsTest.java @@ -57,7 +57,8 @@ public class BuildFieldDefinitionUpdateActionsTest { RES_ROOT + "type-with-field-definitions-cbd.json"; private static final String TYPE_WITH_FIELDS_ABC_WITH_DIFFERENT_TYPE = RES_ROOT + "type-with-field-definitions-abc-with-different-type.json"; - + private static final String TYPE_WITH_FIELDS_ABC_WITHOUT_FIELD_TYPE = + RES_ROOT + "type-with-field-definitions-abc-without-field-type.json"; private static final TypeSyncOptions SYNC_OPTIONS = TypeSyncOptionsBuilder .of(mock(SphereClient.class)) @@ -416,4 +417,38 @@ public void buildUpdateActions_WithDifferentType_ShouldRemoveOldFieldDefinitionA AddFieldDefinition.of(FIELD_DEFINITION_A_LOCALIZED_TYPE) ); } + + @Test + public void buildUpdateActions_WithoutFieldType_ShouldNotBuildActionsAndTriggerErrorCallback() { + final Type oldType = readObjectFromResource(TYPE_WITH_FIELDS_ABC, Type.class); + + final TypeDraft newTypeDraft = readObjectFromResource( + TYPE_WITH_FIELDS_ABC_WITHOUT_FIELD_TYPE, + TypeDraft.class + ); + + final List errorMessages = new ArrayList<>(); + final List exceptions = new ArrayList<>(); + final TypeSyncOptions syncOptions = + TypeSyncOptionsBuilder.of(mock(SphereClient.class)) + .errorCallback((errorMessage, exception) -> { + errorMessages.add(errorMessage); + exceptions.add(exception); + }) + .build(); + + final List> updateActions = buildFieldDefinitionUpdateActions( + oldType, + newTypeDraft, + syncOptions + ); + + assertThat(updateActions).isEmpty(); + assertThat(errorMessages).hasSize(1); + assertThat(errorMessages.get(0)).matches("Failed to build update actions for the field definitions of the " + + "type with the key 'key'. Reason: .*BuildUpdateActionException: " + + "Field type is not set for the new field definition."); + assertThat(exceptions).hasSize(1); + assertThat(exceptions.get(0)).isExactlyInstanceOf(BuildUpdateActionException.class); + } } diff --git a/src/test/resources/com/commercetools/sync/types/utils/updatefielddefinitions/fields/type-with-field-definitions-abc-without-field-type.json b/src/test/resources/com/commercetools/sync/types/utils/updatefielddefinitions/fields/type-with-field-definitions-abc-without-field-type.json new file mode 100644 index 0000000000..003bdedde9 --- /dev/null +++ b/src/test/resources/com/commercetools/sync/types/utils/updatefielddefinitions/fields/type-with-field-definitions-abc-without-field-type.json @@ -0,0 +1,38 @@ +{ + "key": "key", + "name": { + "en": "name" + }, + "description": { + "en": "description" + }, + "resourceTypeIds": [ + "category" + ], + "fieldDefinitions": [ + { + "name": "a", + "label": { + "en": "label_en_edited" + }, + "required": false, + "inputHint": "SingleLine" + }, + { + "name": "b", + "label": { + "en": "label_en_edited" + }, + "required": false, + "inputHint": "SingleLine" + }, + { + "name": "c", + "label": { + "en": "label_en_edited" + }, + "required": false, + "inputHint": "SingleLine" + } + ] +} \ No newline at end of file From ea28dfb073a8e4bf3390db07cbeec2e89730bb31 Mon Sep 17 00:00:00 2001 From: aoz Date: Thu, 18 Oct 2018 15:41:22 +0200 Subject: [PATCH 035/164] #300 - fix checkStyle --- .../sync/services/impl/TypeServiceImpl.java | 2 -- .../sync/types/utils/EnumUpdateActionsUtils.java | 14 ++++++-------- .../utils/FieldDefinitionsUpdateActionUtils.java | 3 --- 3 files changed, 6 insertions(+), 13 deletions(-) diff --git a/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java b/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java index 104b9396c8..a2a41d3407 100644 --- a/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java +++ b/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java @@ -6,13 +6,11 @@ import com.commercetools.sync.services.TypeService; import com.commercetools.sync.types.TypeSyncOptions; import io.sphere.sdk.commands.UpdateAction; -import io.sphere.sdk.queries.QueryExecutionUtils; import io.sphere.sdk.types.Type; import io.sphere.sdk.types.TypeDraft; import io.sphere.sdk.types.commands.TypeCreateCommand; import io.sphere.sdk.types.commands.TypeUpdateCommand; import io.sphere.sdk.types.queries.TypeQuery; -import io.sphere.sdk.types.queries.TypeQueryBuilder; import javax.annotation.Nonnull; import java.util.Collections; diff --git a/src/main/java/com/commercetools/sync/types/utils/EnumUpdateActionsUtils.java b/src/main/java/com/commercetools/sync/types/utils/EnumUpdateActionsUtils.java index 7c619cafe2..6d9d2e7f08 100644 --- a/src/main/java/com/commercetools/sync/types/utils/EnumUpdateActionsUtils.java +++ b/src/main/java/com/commercetools/sync/types/utils/EnumUpdateActionsUtils.java @@ -119,14 +119,12 @@ private static Map getEnumValuesKeyMapWithKeyVali @Nonnull final List enumValues) { return enumValues.stream().collect( - toMap(WithKey::getKey, enumValue -> enumValue, - (enumValueA, enumValueB) -> { - throw new DuplicateKeyException(format("Enum Values have duplicated keys. " - + "Field definition name: '%s', Duplicated enum value: '%s'. " - + "Enum Values are expected to be unique inside their field definition.", - fieldDefinitionName, enumValueA.getKey())); - } - )); + toMap(WithKey::getKey, enumValue -> enumValue, (enumValueA, enumValueB) -> { + throw new DuplicateKeyException(format("Enum Values have duplicated keys. " + + "Field definition name: '%s', Duplicated enum value: '%s'. " + + "Enum Values are expected to be unique inside their field definition.", + fieldDefinitionName, enumValueA.getKey())); + })); } private EnumUpdateActionsUtils() { diff --git a/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionsUpdateActionUtils.java b/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionsUpdateActionUtils.java index 02158e0491..c725f1ecd2 100644 --- a/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionsUpdateActionUtils.java +++ b/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionsUpdateActionUtils.java @@ -15,8 +15,6 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Optional; @@ -26,7 +24,6 @@ import static com.commercetools.sync.commons.utils.CommonTypeUpdateActionUtils.buildUpdateAction; import static com.commercetools.sync.types.utils.FieldDefinitionUpdateActionUtils.buildActions; import static java.lang.String.format; -import static java.util.Collections.singletonList; import static java.util.stream.Collectors.toList; import static java.util.stream.Collectors.toMap; From ac55840e99f97de9ff41ad1c9b86d286f61ada39 Mon Sep 17 00:00:00 2001 From: aoz Date: Thu, 18 Oct 2018 16:01:47 +0200 Subject: [PATCH 036/164] #300 - fix IT. --- .../com/commercetools/sync/integration/types/TypeSyncIT.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/integration-test/java/com/commercetools/sync/integration/types/TypeSyncIT.java b/src/integration-test/java/com/commercetools/sync/integration/types/TypeSyncIT.java index d4febe878b..a8be442734 100644 --- a/src/integration-test/java/com/commercetools/sync/integration/types/TypeSyncIT.java +++ b/src/integration-test/java/com/commercetools/sync/integration/types/TypeSyncIT.java @@ -60,6 +60,7 @@ import static org.mockito.Mockito.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -655,7 +656,7 @@ public void sync_WithoutFieldDefinitionType_ShouldExecuteCallbackOnErrorAndIncre // Since the error message and exception is coming from commercetools, we don't test the actual message and // exception - verify(spyTypeSyncOptions).applyErrorCallback(any(), any()); + verify(spyTypeSyncOptions, times(2)).applyErrorCallback(any(), any()); AssertionsForStatistics.assertThat(typeSyncStatistics).hasValues(1, 0, 0, 1); } From 27ef3dbb3c846a320bd260ffa03abc72b8a39bf6 Mon Sep 17 00:00:00 2001 From: aoz Date: Mon, 22 Oct 2018 16:56:39 +0200 Subject: [PATCH 037/164] #318, #300 - refactoring the enum value usages. --- .../enums/EnumValuesUpdateActionUtils.java | 435 ++++++++++++++++++ .../LocalizedEnumValueUpdateActionUtils.java | 38 ++ .../PlainEnumValueUpdateActionUtils.java | 38 ++ .../AttributeDefinitionUpdateActionUtils.java | 56 ++- .../utils/EnumsUpdateActionUtils.java | 210 --------- .../utils/LocalizedEnumUpdateActionUtils.java | 62 --- .../LocalizedEnumValueUpdateActionUtils.java | 85 ++++ .../LocalizedEnumsUpdateActionUtils.java | 131 ------ .../utils/PlainEnumUpdateActionUtils.java | 62 --- .../PlainEnumValueUpdateActionUtils.java | 82 ++++ .../utils/PlainEnumsUpdateActionUtils.java | 123 ----- .../types/utils/EnumUpdateActionsUtils.java | 132 ------ .../FieldDefinitionUpdateActionUtils.java | 45 +- .../utils/LocalizedEnumUpdateActionUtils.java | 49 +- ...a => PlainEnumValueUpdateActionUtils.java} | 50 +- .../EnumValuesUpdateActionUtilsTest.java | 325 +++++++++++++ .../enums/LocalizedEnumValueTestObjects.java | 45 ++ ...calizedEnumValueUpdateActionUtilsTest.java | 11 +- .../enums/PlainEnumValueTestObjects.java | 45 ++ .../PlainEnumValueUpdateActionUtilsTest.java | 10 +- .../BuildLocalizedEnumUpdateActionsTest.java | 68 ++- .../BuildPlainEnumUpdateActionsTest.java | 66 +-- .../BuildLocalizedEnumUpdateActionsTest.java | 37 +- .../BuildPlainEnumUpdateActionsTest.java | 35 +- 24 files changed, 1267 insertions(+), 973 deletions(-) create mode 100644 src/main/java/com/commercetools/sync/commons/utils/enums/EnumValuesUpdateActionUtils.java create mode 100644 src/main/java/com/commercetools/sync/commons/utils/enums/LocalizedEnumValueUpdateActionUtils.java create mode 100644 src/main/java/com/commercetools/sync/commons/utils/enums/PlainEnumValueUpdateActionUtils.java delete mode 100644 src/main/java/com/commercetools/sync/producttypes/utils/EnumsUpdateActionUtils.java delete mode 100644 src/main/java/com/commercetools/sync/producttypes/utils/LocalizedEnumUpdateActionUtils.java create mode 100644 src/main/java/com/commercetools/sync/producttypes/utils/LocalizedEnumValueUpdateActionUtils.java delete mode 100644 src/main/java/com/commercetools/sync/producttypes/utils/LocalizedEnumsUpdateActionUtils.java delete mode 100644 src/main/java/com/commercetools/sync/producttypes/utils/PlainEnumUpdateActionUtils.java create mode 100644 src/main/java/com/commercetools/sync/producttypes/utils/PlainEnumValueUpdateActionUtils.java delete mode 100644 src/main/java/com/commercetools/sync/producttypes/utils/PlainEnumsUpdateActionUtils.java delete mode 100644 src/main/java/com/commercetools/sync/types/utils/EnumUpdateActionsUtils.java rename src/main/java/com/commercetools/sync/types/utils/{PlainEnumUpdateActionUtils.java => PlainEnumValueUpdateActionUtils.java} (53%) create mode 100644 src/test/java/com/commercetools/sync/commons/utils/enums/EnumValuesUpdateActionUtilsTest.java create mode 100644 src/test/java/com/commercetools/sync/commons/utils/enums/LocalizedEnumValueTestObjects.java rename src/test/java/com/commercetools/sync/{producttypes/utils => commons/utils/enums}/LocalizedEnumValueUpdateActionUtilsTest.java (82%) create mode 100644 src/test/java/com/commercetools/sync/commons/utils/enums/PlainEnumValueTestObjects.java rename src/test/java/com/commercetools/sync/{producttypes/utils => commons/utils/enums}/PlainEnumValueUpdateActionUtilsTest.java (83%) diff --git a/src/main/java/com/commercetools/sync/commons/utils/enums/EnumValuesUpdateActionUtils.java b/src/main/java/com/commercetools/sync/commons/utils/enums/EnumValuesUpdateActionUtils.java new file mode 100644 index 0000000000..1e5fca24c0 --- /dev/null +++ b/src/main/java/com/commercetools/sync/commons/utils/enums/EnumValuesUpdateActionUtils.java @@ -0,0 +1,435 @@ +package com.commercetools.sync.commons.utils.enums; + +import com.commercetools.sync.commons.exceptions.DuplicateKeyException; +import com.commercetools.sync.commons.utils.TriFunction; +import io.sphere.sdk.commands.UpdateAction; +import io.sphere.sdk.models.EnumValue; +import io.sphere.sdk.models.WithKey; +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.apache.commons.lang3.tuple.Pair; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.function.BiFunction; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static com.commercetools.sync.commons.utils.CommonTypeUpdateActionUtils.buildUpdateAction; +import static java.lang.String.format; +import static java.util.Collections.emptyList; +import static java.util.Optional.empty; +import static java.util.Optional.of; +import static java.util.Optional.ofNullable; +import static java.util.stream.Collectors.toMap; + +public final class EnumValuesUpdateActionUtils { + + /** + * Compares a list of old {@code oldEnumValues} with a list of new {@code newEnumValues} for a given definition. + * The method serves as an implementation for enum values syncing. The method takes in functions + * for building the required update actions (AddEnumValue, ChangeEnumValueOrder + * and 1-1 update actions on enum values (e.g. changeLabel) for the required resource. + * + * @param definitionName the definition name whose enum values are going to be synced. + * @param oldEnumValues the old list of plain enum values. + * @param newEnumValues the new list of plain enum values. + * @param removeEnumCallback the function that is called in order to remove the new enum instance. + * @param matchingEnumCallback the function that is called to get the update action resulting from + * comparing the enum value fields one by one. + * @param addEnumCallback the function that is called in order to add the new enum instance. + * @param changeOrderEnumCallback the function that is called to apply the change in the order. + * @param changeOrderWithKeysEnumCallback the function that is called to apply the change in the order with keys. + * @param the enum type of the elements to change the order for. + * @param the type of the resource in which the update actions will be applied on. + * @return a list of enum values update actions if the list of plain enum values is not identical. + * Otherwise, if the plain enum values are identical, an empty list is returned. + */ + @Nonnull + public static List> buildActions( + @Nonnull final String definitionName, + @Nonnull final List oldEnumValues, + @Nullable final List newEnumValues, + @Nullable final BiFunction, UpdateAction> removeEnumCallback, + @Nullable final TriFunction>> matchingEnumCallback, + @Nullable final BiFunction> addEnumCallback, + @Nullable final BiFunction, UpdateAction> changeOrderEnumCallback, + @Nullable final BiFunction, UpdateAction> changeOrderWithKeysEnumCallback) { + + if (newEnumValues != null && !newEnumValues.isEmpty()) { + return buildUpdateActions(definitionName, + oldEnumValues, + newEnumValues, + removeEnumCallback, + matchingEnumCallback, + addEnumCallback, + changeOrderEnumCallback, + changeOrderWithKeysEnumCallback + ); + } else if (removeEnumCallback != null) { + return buildRemoveEnumValuesUpdateAction( + definitionName, + oldEnumValues, + newEnumValues, + removeEnumCallback) + .map(Collections::singletonList) + .orElse(emptyList()); + + } + + return emptyList(); + } + + @Nonnull + private static List> buildUpdateActions( + @Nonnull final String definitionName, + @Nonnull final List oldEnumValues, + @Nonnull final List newEnumValues, + @Nullable final BiFunction, UpdateAction> removeEnumValuesUpdateActionCallback, + @Nullable final TriFunction>> matchingEnumCallback, + @Nullable final BiFunction> addEnumCallback, + @Nullable final BiFunction, UpdateAction> changeOrderEnumCallback, + @Nullable final BiFunction, UpdateAction> changeOrderWithKeysEnumCallback) { + + final List> removeEnumValuesUpdateActions = + getRemoveEnumValuesUpdateActions(definitionName, oldEnumValues, newEnumValues, + removeEnumValuesUpdateActionCallback); + + final List> matchingEnumValuesUpdateActions = getMatchingEnumValuesUpdateActions( + definitionName, oldEnumValues, newEnumValues, matchingEnumCallback); + + final List> addEnumValuesUpdateActions = getAddEnumValuesUpdateActions(definitionName, + oldEnumValues, newEnumValues, addEnumCallback); + + final List> changeEnumValuesOrderUpdateActions = getChangeEnumValuesOrderUpdateActions( + definitionName, oldEnumValues, newEnumValues, changeOrderEnumCallback); + + final List> changeEnumValuesWithKeysOrderUpdateActions = + getChangeEnumValuesWithKeysOrderUpdateActions( + definitionName, oldEnumValues, newEnumValues, + changeOrderWithKeysEnumCallback); + + return Stream.of(removeEnumValuesUpdateActions, + matchingEnumValuesUpdateActions, + addEnumValuesUpdateActions, + changeEnumValuesOrderUpdateActions, + changeEnumValuesWithKeysOrderUpdateActions) + .flatMap(Collection::stream) + .collect(Collectors.toList()); + } + + @Nonnull + private static List> getChangeEnumValuesWithKeysOrderUpdateActions( + @Nonnull final String definitionName, + @Nonnull final List oldEnumValues, + @Nonnull final List newEnumValues, + @Nullable final BiFunction, UpdateAction> changeOrderWithKeysEnumCallback) { + + return changeOrderWithKeysEnumCallback == null ? emptyList() : + buildChangeEnumValuesWithKeysOrderUpdateAction( + definitionName, + oldEnumValues, + newEnumValues, + changeOrderWithKeysEnumCallback + ).map(Collections::singletonList) + .orElse(emptyList()); + } + + @Nonnull + private static List> getChangeEnumValuesOrderUpdateActions( + @Nonnull final String definitionName, + @Nonnull final List oldEnumValues, + @Nonnull final List newEnumValues, + @Nullable final BiFunction, UpdateAction> changeOrderEnumCallback) { + + return changeOrderEnumCallback == null ? emptyList() : + buildChangeEnumValuesOrderUpdateAction( + definitionName, + oldEnumValues, + newEnumValues, + changeOrderEnumCallback + ) + .map(Collections::singletonList) + .orElse(emptyList()); + } + + @Nonnull + private static List> getAddEnumValuesUpdateActions( + @Nonnull final String definitionName, + @Nonnull final List oldEnumValues, + @Nonnull final List newEnumValues, + @Nullable final BiFunction> addEnumCallback) { + + return addEnumCallback == null ? emptyList() : buildAddEnumValuesUpdateActions( + definitionName, + oldEnumValues, + newEnumValues, + addEnumCallback + ); + } + + @Nonnull + private static List> getMatchingEnumValuesUpdateActions( + @Nonnull final String definitionName, + @Nonnull final List oldEnumValues, + @Nonnull final List newEnumValues, + @Nullable final TriFunction>> matchingEnumCallback) { + + return matchingEnumCallback == null ? emptyList() : + buildMatchingEnumValuesUpdateActions( + definitionName, + oldEnumValues, + newEnumValues, + matchingEnumCallback + ); + } + + @Nonnull + private static List> getRemoveEnumValuesUpdateActions( + @Nonnull final String definitionName, + @Nonnull final List oldEnumValues, + @Nonnull final List newEnumValues, + @Nullable final BiFunction, UpdateAction> removeEnumValuesUpdateActionCallback) { + + return removeEnumValuesUpdateActionCallback == null ? emptyList() : + buildRemoveEnumValuesUpdateAction( + definitionName, + oldEnumValues, + newEnumValues, + removeEnumValuesUpdateActionCallback) + .map(Collections::singletonList) + .orElse(emptyList()); + } + + /** + * Compares the order of a list of old enum values and a list of new enum values. If there is a change in order, + * then a change of enum values order (with the new order) is built. If there are no changes in order an empty + * optional is returned. + * + * @param definitionName the definition name whose enum values belong to. + * @param oldEnumValues the list of old enum values. + * @param newEnumValues the list of new enum values. + * @param changeOrderEnumCallback the function that is called to apply the change in the order. + * @param the enum type of the elements to change the order for. + * @param the type of the resource in which the update actions will be applied on. + * @return an optional update action if the the order of the enum values is not identical. + * Otherwise, if the enum values order is identical, an empty optional is returned. + */ + @Nonnull + public static Optional> buildChangeEnumValuesOrderUpdateAction( + @Nonnull final String definitionName, + @Nonnull final List oldEnumValues, + @Nonnull final List newEnumValues, + @Nonnull final BiFunction, UpdateAction> changeOrderEnumCallback) { + + final Pair, List> keysPair = getAllKeysAndNewKeysPair(oldEnumValues, newEnumValues); + return buildUpdateAction( + keysPair.getLeft(), //all keys + keysPair.getRight(), // new keys + () -> changeOrderEnumCallback.apply(definitionName, newEnumValues) + ); + } + + /** + * Compares the order of a list of old enum values and a list of new enum values. If there is a change in order, + * then a change of enum values order (with the new order) is built. If there are no changes in order an empty + * optional is returned. + * + * @param definitionName the definition name whose enum values belong to. + * @param oldEnumValues the list of old enum values. + * @param newEnumValues the list of new enum values. + * @param changeOrderEnumCallback the function that is called to apply the change in the order. + * @param the enum type of the elements to change the order for. + * @return an optional update action if the the order of the enum values is not identical. + * Otherwise, if the enum values order is identical, an empty optional is returned. + */ + @Nonnull + public static Optional> buildChangeEnumValuesWithKeysOrderUpdateAction( + @Nonnull final String definitionName, + @Nonnull final List oldEnumValues, + @Nonnull final List newEnumValues, + @Nonnull final BiFunction, UpdateAction> changeOrderEnumCallback) { + + final Pair, List> keysPair = getAllKeysAndNewKeysPair(oldEnumValues, newEnumValues); + return buildUpdateAction( + keysPair.getLeft(), //all keys + keysPair.getRight(), // new keys + () -> changeOrderEnumCallback.apply(definitionName, keysPair.getLeft()) + ); + } + + @Nonnull + private static Pair, List> getAllKeysAndNewKeysPair( + @Nonnull final List oldEnumValues, + @Nonnull final List newEnumValues) { + + final List newKeys = newEnumValues + .stream() + .map(WithKey::getKey) + .collect(Collectors.toList()); + + final List existingKeys = oldEnumValues + .stream() + .map(WithKey::getKey) + .filter(newKeys::contains) + .collect(Collectors.toList()); + + final List notExistingKeys = newKeys + .stream() + .filter(newKey -> !existingKeys.contains(newKey)) + .collect(Collectors.toList()); + + final List allKeys = Stream.concat(existingKeys.stream(), notExistingKeys.stream()) + .collect(Collectors.toList()); + + return ImmutablePair.of(allKeys, newKeys); + } + + /** + * Checks if there are any new enum values which are not existing in the {@code oldEnumValues}. + * If there are, then "add" enum values update actions are built. + * Otherwise, if there are no new enum values, then an empty list is returned. + * + * @param definitionName the definition name whose enum values belong to. + * @param oldEnumValues the list of olf enum values. + * @param newEnumValues the list of new enum values. + * @param addEnumCallback the function that is called in order to add the new enum instance. + * @param the enum type of the element to add. + * @param the type of the resource in which the update actions will be applied on. + * @return a list of enum values update actions if there are new enum value that should be added. + * Otherwise, if the enum values are identical, an empty optional is returned. + */ + @Nonnull + public static List> buildAddEnumValuesUpdateActions( + @Nonnull final String definitionName, + @Nonnull final List oldEnumValues, + @Nonnull final List newEnumValues, + @Nonnull final BiFunction> addEnumCallback) { + + final Map oldEnumValuesKeyMap = getEnumValuesKeyMapWithKeyValidation( + definitionName, + oldEnumValues + ); + + final Map newEnumValuesKeyMap = getEnumValuesKeyMapWithKeyValidation( + definitionName, + newEnumValues + ); + + return newEnumValuesKeyMap.values() + .stream() + .filter(newEnumValue -> !oldEnumValuesKeyMap.containsKey(newEnumValue.getKey())) + .map(newEnumValue -> addEnumCallback.apply(definitionName, newEnumValue)) + .collect(Collectors.toList()); + } + + /** + * Checks if there are any old enum values which are not existing in the {@code newEnumValues}. + * If there are, then "remove" enum values update actions are built. + * Otherwise, if there are no old enum values, then an empty list is returned. + * + * @param definitionName the definition name whose enum values belong to. + * @param oldEnumValues the list of old enum values. + * @param newEnumValues the list of new enum values. + * @param removeEnumCallback the function that is called in order to remove the new enum instance. + * @param the enum type of the elements of the list. + * @param the type of the resource in which the update actions will be applied on. + * @return a list of enum values update actions if there are any old enum value that should be removed. + * Otherwise, if the enum values are identical, an empty optional is returned. + */ + @Nonnull + public static Optional> buildRemoveEnumValuesUpdateAction( + @Nonnull final String definitionName, + @Nonnull final List oldEnumValues, + @Nullable final List newEnumValues, + @Nonnull final BiFunction, UpdateAction> removeEnumCallback) { + + final Map newEnumValuesKeyMap = getEnumValuesKeyMapWithKeyValidation( + definitionName, + ofNullable(newEnumValues).orElse(emptyList()) + ); + + final List keysToRemove = oldEnumValues + .stream() + .map(WithKey::getKey) + .filter(oldEnumValueKey -> !newEnumValuesKeyMap.containsKey(oldEnumValueKey)) + .collect(Collectors.toList()); + + return keysToRemove.isEmpty() ? empty() : + of(removeEnumCallback.apply(definitionName, keysToRemove)); + } + + /** + * Checks if there are any enum values which are existing in the {@code newEnumValues}. + * If there are, then compare the enum value fields, and add the computed actions to the list of + * update actions. + * + * @param definitionName the definition name whose enum values belong to. + * @param oldEnumValues the list of old enum values. + * @param newEnumValues the list of new enum values. + * @param matchingEnumCallback the function that is called to get the update action resulting from comparing + * the enum value fields one by one. + * @param the enum type of the elements of the list. + * @param the type of the resource in which the update actions will be applied on. + * @return a list of enum update actions if there are enum values that are existing in the map of new enum values. + * If the enum value still exists, then compare the enum value fields (label), and add the computed + * actions to the list of update actions. Otherwise, if the enum values are identical, an empty optional + * is returned. + */ + @Nonnull + public static List> buildMatchingEnumValuesUpdateActions( + @Nonnull final String definitionName, + @Nonnull final List oldEnumValues, + @Nonnull final List newEnumValues, + @Nonnull final TriFunction>> matchingEnumCallback) { + + final Map newEnumValuesKeyMap = getEnumValuesKeyMapWithKeyValidation( + definitionName, + newEnumValues + ); + + return oldEnumValues + .stream() + .filter(oldEnumValue -> newEnumValuesKeyMap.containsKey(oldEnumValue.getKey())) + .map(oldEnumValue -> matchingEnumCallback.apply( + definitionName, + oldEnumValue, + newEnumValuesKeyMap.get(oldEnumValue.getKey()) + )) + .flatMap(Collection::stream) + .collect(Collectors.toList()); + } + + /** + * Given a list of new {@link EnumValue}s, gets a map where the keys are the enum value key, and the values + * are the enum instances. + * + * @param definitionName the definition name whose the enum values belong to. + * @param enumValues the list of enum values. + * @param the enum type of the elements of the list. + * @return a map with the enum value key as a key of the map, and the enum + * value as a value of the map. + */ + @Nonnull + private static Map getEnumValuesKeyMapWithKeyValidation( + @Nonnull final String definitionName, + @Nonnull final List enumValues) { + + return enumValues.stream().collect( + toMap(WithKey::getKey, enumValue -> enumValue, + (enumValueA, enumValueB) -> { + throw new DuplicateKeyException(format("Enum Values have duplicated keys. " + + "Definition name: '%s', Duplicated enum value: '%s'. " + + "Enum Values are expected to be unique inside their definition.", + definitionName, enumValueA.getKey())); + } + )); + } + + private EnumValuesUpdateActionUtils() { + } +} diff --git a/src/main/java/com/commercetools/sync/commons/utils/enums/LocalizedEnumValueUpdateActionUtils.java b/src/main/java/com/commercetools/sync/commons/utils/enums/LocalizedEnumValueUpdateActionUtils.java new file mode 100644 index 0000000000..a42f608fbb --- /dev/null +++ b/src/main/java/com/commercetools/sync/commons/utils/enums/LocalizedEnumValueUpdateActionUtils.java @@ -0,0 +1,38 @@ +package com.commercetools.sync.commons.utils.enums; + +import io.sphere.sdk.commands.UpdateAction; +import io.sphere.sdk.models.LocalizedEnumValue; + +import javax.annotation.Nonnull; +import java.util.Optional; +import java.util.function.BiFunction; + +import static com.commercetools.sync.commons.utils.CommonTypeUpdateActionUtils.buildUpdateAction; + +public final class LocalizedEnumValueUpdateActionUtils { + + /** + * Compares the {@code label} values of an old {@link LocalizedEnumValue} and a new {@link LocalizedEnumValue} + * and returns an {@link Optional} of update action, which would contain the {@code "changeLabel"} + * {@link UpdateAction}. If both, old and new {@link LocalizedEnumValue} have the same {@code label} values, + * then no update action is needed and empty optional will be returned. + * + * @param attributeDefinitionName the attribute definition name whose localized enum values belong to. + * @param oldEnumValue the old localized enum value. + * @param newEnumValue the new localized enum value which contains the new description. + * @param the type of the resource in which the update actions will be applied on. + * @return optional containing update action or empty optional if labels + * are identical. + */ + @Nonnull + public static Optional> buildChangeLabelAction( + @Nonnull final String attributeDefinitionName, + @Nonnull final LocalizedEnumValue oldEnumValue, + @Nonnull final LocalizedEnumValue newEnumValue, + @Nonnull final BiFunction> changeLocalizedEnumValueLabelAction) { + + return buildUpdateAction(oldEnumValue.getLabel(), newEnumValue.getLabel(), + () -> changeLocalizedEnumValueLabelAction.apply(attributeDefinitionName, newEnumValue)); + } + +} diff --git a/src/main/java/com/commercetools/sync/commons/utils/enums/PlainEnumValueUpdateActionUtils.java b/src/main/java/com/commercetools/sync/commons/utils/enums/PlainEnumValueUpdateActionUtils.java new file mode 100644 index 0000000000..dab13cbe76 --- /dev/null +++ b/src/main/java/com/commercetools/sync/commons/utils/enums/PlainEnumValueUpdateActionUtils.java @@ -0,0 +1,38 @@ +package com.commercetools.sync.commons.utils.enums; + +import io.sphere.sdk.commands.UpdateAction; +import io.sphere.sdk.models.EnumValue; + +import javax.annotation.Nonnull; +import java.util.Optional; +import java.util.function.BiFunction; + +import static com.commercetools.sync.commons.utils.CommonTypeUpdateActionUtils.buildUpdateAction; + +public final class PlainEnumValueUpdateActionUtils { + + /** + * Compares the {@code label} values of an old {@link EnumValue} and a new {@link EnumValue} + * and returns an {@link Optional} of update action, which would contain the {@code "changeLabel"} + * {@link UpdateAction}. If both, old and new {@link EnumValue} have the same {@code label} values, + * then no update action is needed and empty optional will be returned. + * + * @param attributeDefinitionName the attribute definition name whose plain enum values belong to. + * @param oldEnumValue the old plain enum value. + * @param newEnumValue the new plain enum value which contains the new description. + * @param the type of the resource in which the update actions will be applied on. + * @return optional containing update action or empty optional if labels + * are identical. + */ + @Nonnull + public static Optional> buildChangeLabelAction( + @Nonnull final String attributeDefinitionName, + @Nonnull final EnumValue oldEnumValue, + @Nonnull final EnumValue newEnumValue, + @Nonnull final BiFunction> changePlainEnumValueLabelAction) { + + return buildUpdateAction(oldEnumValue.getLabel(), newEnumValue.getLabel(), + () -> changePlainEnumValueLabelAction.apply(attributeDefinitionName, newEnumValue)); + } + +} diff --git a/src/main/java/com/commercetools/sync/producttypes/utils/AttributeDefinitionUpdateActionUtils.java b/src/main/java/com/commercetools/sync/producttypes/utils/AttributeDefinitionUpdateActionUtils.java index 05f6fc46c1..dd1c382473 100644 --- a/src/main/java/com/commercetools/sync/producttypes/utils/AttributeDefinitionUpdateActionUtils.java +++ b/src/main/java/com/commercetools/sync/producttypes/utils/AttributeDefinitionUpdateActionUtils.java @@ -3,6 +3,8 @@ import com.commercetools.sync.commons.exceptions.BuildUpdateActionException; import com.commercetools.sync.producttypes.helpers.AttributeTypeAssert; import io.sphere.sdk.commands.UpdateAction; +import io.sphere.sdk.models.EnumValue; +import io.sphere.sdk.models.LocalizedEnumValue; import io.sphere.sdk.models.LocalizedString; import io.sphere.sdk.products.attributes.AttributeDefinition; import io.sphere.sdk.products.attributes.AttributeDefinitionDraft; @@ -16,13 +18,14 @@ import io.sphere.sdk.producttypes.commands.updateactions.SetInputTip; import javax.annotation.Nonnull; +import java.util.ArrayList; import java.util.List; import java.util.Optional; import static com.commercetools.sync.commons.utils.CommonTypeUpdateActionUtils.buildUpdateAction; import static com.commercetools.sync.commons.utils.OptionalUtils.filterEmptyOptionals; -import static com.commercetools.sync.producttypes.utils.LocalizedEnumsUpdateActionUtils.buildLocalizedEnumValuesUpdateActions; -import static com.commercetools.sync.producttypes.utils.PlainEnumsUpdateActionUtils.buildEnumValuesUpdateActions; +import static com.commercetools.sync.producttypes.utils.LocalizedEnumValueUpdateActionUtils.buildLocalizedEnumValuesUpdateActions; +import static com.commercetools.sync.producttypes.utils.PlainEnumValueUpdateActionUtils.buildEnumValuesUpdateActions; public final class AttributeDefinitionUpdateActionUtils { @@ -32,39 +35,60 @@ public final class AttributeDefinitionUpdateActionUtils { * and the {@link AttributeDefinitionDraft} have identical fields, then no update action is needed and hence an * empty {@link List} is returned. * - * @param oldAttributeDefinition the old attribute definition which should be updated. - * @param newAttributeDefinitionDraft the new attribute definition draft where we get the new fields. + * @param oldAttributeDefinition the old attribute definition which should be updated. + * @param newAttributeDefinition the new attribute definition draft where we get the new fields. * @return A list with the update actions or an empty list if the attribute definition fields are identical. * @throws BuildUpdateActionException in case there are attribute definitions with the null attribute type. */ @Nonnull public static List> buildActions( @Nonnull final AttributeDefinition oldAttributeDefinition, - @Nonnull final AttributeDefinitionDraft newAttributeDefinitionDraft) throws BuildUpdateActionException { - - AttributeTypeAssert.assertTypesAreNull( - oldAttributeDefinition.getAttributeType(), - newAttributeDefinitionDraft.getAttributeType()); + @Nonnull final AttributeDefinitionDraft newAttributeDefinition) throws BuildUpdateActionException { final List> updateActions = filterEmptyOptionals( - buildChangeLabelUpdateAction(oldAttributeDefinition, newAttributeDefinitionDraft), - buildSetInputTipUpdateAction(oldAttributeDefinition, newAttributeDefinitionDraft), - buildChangeIsSearchableUpdateAction(oldAttributeDefinition, newAttributeDefinitionDraft), - buildChangeInputHintUpdateAction(oldAttributeDefinition, newAttributeDefinitionDraft), - buildChangeAttributeConstraintUpdateAction(oldAttributeDefinition, newAttributeDefinitionDraft) + buildChangeLabelUpdateAction(oldAttributeDefinition, newAttributeDefinition), + buildSetInputTipUpdateAction(oldAttributeDefinition, newAttributeDefinition), + buildChangeIsSearchableUpdateAction(oldAttributeDefinition, newAttributeDefinition), + buildChangeInputHintUpdateAction(oldAttributeDefinition, newAttributeDefinition), + buildChangeAttributeConstraintUpdateAction(oldAttributeDefinition, newAttributeDefinition) ); + updateActions.addAll(buildEnumUpdateActions(oldAttributeDefinition, newAttributeDefinition)); + return updateActions; + } + + /** + * Compares all the {@link EnumValue} and {@link LocalizedEnumValue} values of an {@link AttributeDefinition} and + * an {@link AttributeDefinitionDraft} and returns a list of {@link UpdateAction}<{@link ProductType}> as a + * result. If both the {@link AttributeDefinition} and the {@link AttributeDefinitionDraft} have identical + * enum values, then no update action is needed and hence an empty {@link List} is returned. + * + * @param oldAttributeDefinition the attribute definition which should be updated. + * @param newAttributeDefinition the new attribute definition draft where we get the new fields. + * @return A list with the update actions or an empty list if the attribute definition enums are identical. + * @throws BuildUpdateActionException in case there are attribute definitions with the null attribute type. + */ + @Nonnull + public static List> buildEnumUpdateActions( + @Nonnull final AttributeDefinition oldAttributeDefinition, + @Nonnull final AttributeDefinitionDraft newAttributeDefinition) throws BuildUpdateActionException { + + AttributeTypeAssert.assertTypesAreNull( + oldAttributeDefinition.getAttributeType(), + newAttributeDefinition.getAttributeType()); + + final List> updateActions = new ArrayList<>(); if (isPlainEnumAttribute(oldAttributeDefinition)) { updateActions.addAll(buildEnumValuesUpdateActions( oldAttributeDefinition.getName(), ((EnumAttributeType) oldAttributeDefinition.getAttributeType()).getValues(), - ((EnumAttributeType) newAttributeDefinitionDraft.getAttributeType()).getValues() + ((EnumAttributeType) newAttributeDefinition.getAttributeType()).getValues() )); } else if (isLocalizedEnumAttribute(oldAttributeDefinition)) { updateActions.addAll(buildLocalizedEnumValuesUpdateActions( oldAttributeDefinition.getName(), ((LocalizedEnumAttributeType) oldAttributeDefinition.getAttributeType()).getValues(), - ((LocalizedEnumAttributeType) newAttributeDefinitionDraft.getAttributeType()).getValues() + ((LocalizedEnumAttributeType) newAttributeDefinition.getAttributeType()).getValues() )); } diff --git a/src/main/java/com/commercetools/sync/producttypes/utils/EnumsUpdateActionUtils.java b/src/main/java/com/commercetools/sync/producttypes/utils/EnumsUpdateActionUtils.java deleted file mode 100644 index 5155a893ca..0000000000 --- a/src/main/java/com/commercetools/sync/producttypes/utils/EnumsUpdateActionUtils.java +++ /dev/null @@ -1,210 +0,0 @@ -package com.commercetools.sync.producttypes.utils; - -import com.commercetools.sync.commons.exceptions.DuplicateKeyException; -import com.commercetools.sync.commons.utils.TriFunction; -import io.sphere.sdk.commands.UpdateAction; -import io.sphere.sdk.models.EnumValue; -import io.sphere.sdk.models.WithKey; -import io.sphere.sdk.producttypes.ProductType; -import io.sphere.sdk.producttypes.commands.updateactions.RemoveEnumValues; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.function.BiFunction; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import static com.commercetools.sync.commons.utils.CommonTypeUpdateActionUtils.buildUpdateAction; -import static java.lang.String.format; -import static java.util.Collections.emptyList; -import static java.util.Optional.empty; -import static java.util.Optional.of; -import static java.util.Optional.ofNullable; -import static java.util.stream.Collectors.toMap; - -final class EnumsUpdateActionUtils { - - /** - * Checks if there are any old enum values which are not existing in the {@code newEnumValues}. - * If there are, then "remove" enum values update actions are built. - * Otherwise, if there are no old enum values, then an empty list is returned. - * - * @param attributeDefinitionName the attribute definition name whose enum values belong to. - * @param oldEnumValues the list of old enum values. - * @param newEnumValues the list of new enum values. - * @param the enum type of the elements of the list. - * @return a list of enum values update actions if there are old enum value - * that should be removed. - * Otherwise, if the enum values are identical, an empty optional is returned. - */ - @Nonnull - static Optional> buildRemoveEnumValuesUpdateActions( - @Nonnull final String attributeDefinitionName, - @Nonnull final List oldEnumValues, - @Nullable final List newEnumValues) { - - final Map newEnumValuesKeyMap = getEnumValuesKeyMapWithKeyValidation( - attributeDefinitionName, - ofNullable(newEnumValues).orElse(emptyList()) - ); - - final List keysToRemove = oldEnumValues - .stream() - .map(WithKey::getKey) - .filter(oldEnumValueKey -> !newEnumValuesKeyMap.containsKey(oldEnumValueKey)) - .collect(Collectors.toList()); - - return keysToRemove.isEmpty() ? empty() : of(RemoveEnumValues.of(attributeDefinitionName, keysToRemove)); - } - - /** - * Compares the order of a list of old enum values and a list of new enum values. If there is a change in order, - * then a change of enum values order (with the new order) is built. If there are no changes in order an empty - * optional is returned. - * - * @param attributeDefinitionName the attribute definition name whose enum values belong to. - * @param oldEnumValues the list of old enum values. - * @param newEnumValues the list of new enum values. - * @param changeOrderEnumCallback the function that is called to apply the change in the order. - * @param the enum type of the elements to change the order for. - * @return an optional update action if the the order of the enum values is not identical. - * Otherwise, if the enum values order is identical, an empty optional is returned. - */ - @Nonnull - static Optional> buildChangeEnumValuesOrderUpdateAction( - @Nonnull final String attributeDefinitionName, - @Nonnull final List oldEnumValues, - @Nonnull final List newEnumValues, - @Nonnull final BiFunction, UpdateAction> changeOrderEnumCallback) { - - final List newKeys = newEnumValues - .stream() - .map(WithKey::getKey) - .collect(Collectors.toList()); - - final List existingKeys = oldEnumValues - .stream() - .map(WithKey::getKey) - .filter(newKeys::contains) - .collect(Collectors.toList()); - - final List notExistingKeys = newKeys - .stream() - .filter(newKey -> !existingKeys.contains(newKey)) - .collect(Collectors.toList()); - - final List allKeys = Stream.concat(existingKeys.stream(), notExistingKeys.stream()) - .collect(Collectors.toList()); - - - return buildUpdateAction( - allKeys, - newKeys, - () -> changeOrderEnumCallback.apply(attributeDefinitionName, newEnumValues) - ); - } - - /** - * Checks if there are any new enum values which are not existing in the {@code oldEnumValues}. - * If there are, then "add" enum values update actions are built. - * Otherwise, if there are no new enum values, then an empty list is returned. - * - * @param attributeDefinitionName the attribute definition name whose enum values belong to. - * @param oldEnumValues the list of olf enum values. - * @param newEnumValues the list of new enum values. - * @param addEnumCallback the function that is called in order to add the new enum instance - * @param the enum type of the element to add. - * @return a list of enum values update actions if there are new enum value that should be added. - * Otherwise, if the enum values are identical, an empty optional is returned. - */ - @Nonnull - static List> buildAddEnumValuesUpdateActions( - @Nonnull final String attributeDefinitionName, - @Nonnull final List oldEnumValues, - @Nonnull final List newEnumValues, - @Nonnull final BiFunction> addEnumCallback) { - - final Map oldEnumValuesKeyMap = getEnumValuesKeyMapWithKeyValidation( - attributeDefinitionName, - oldEnumValues - ); - - return newEnumValues - .stream() - .filter(newEnumValue -> !oldEnumValuesKeyMap.containsKey(newEnumValue.getKey())) - .map(newEnumValue -> addEnumCallback.apply(attributeDefinitionName, newEnumValue)) - .collect(Collectors.toList()); - } - - /** - * Checks if there are any enum values which are existing in the {@code newEnumValues}. - * If there are, then compare the enum value fields, and add the computed actions to the list of - * update actions. - * - * @param attributeDefinitionName the attribute definition name whose enum values belong to. - * @param oldEnumValues the list of old enum values. - * @param newEnumValues the list of new enum values. - * @param matchingEnumCallback the function that is called to get the update action resulting from comparing - * the enum value fields one by one. - * @param the enum type of the elements of the list. - * @return a list of enum update actions if there are enum values that are existing in the map of new enum values. - * If the enum value still exists, then compare the enum value fields (label), and add the computed - * actions to the list of update actions. - * Otherwise, if the enum values are identical, an empty optional is returned. - */ - @Nonnull - static List> buildMatchingEnumValuesUpdateActions( - @Nonnull final String attributeDefinitionName, - @Nonnull final List oldEnumValues, - @Nonnull final List newEnumValues, - @Nonnull final TriFunction>> matchingEnumCallback) { - - final Map newEnumValuesKeyMap = getEnumValuesKeyMapWithKeyValidation( - attributeDefinitionName, - newEnumValues - ); - - return oldEnumValues - .stream() - .filter(oldEnumValue -> newEnumValuesKeyMap.containsKey(oldEnumValue.getKey())) - .map(oldEnumValue -> matchingEnumCallback.apply( - attributeDefinitionName, - oldEnumValue, - newEnumValuesKeyMap.get(oldEnumValue.getKey()) - )) - .flatMap(Collection::stream) - .collect(Collectors.toList()); - } - - /** - * Given a list of new {@link EnumValue}s, gets a map where the keys are the enum value key, and the values - * are the enum instances. - * - * @param attributeDefinitionName the attribute definition name whose the enum values belong to. - * @param enumValues the list of enum values. - * @param the enum type of the elements of the list. - * @return a map with the enum value key as a key of the map, and the enum - * value as a value of the map. - */ - @Nonnull - private static Map getEnumValuesKeyMapWithKeyValidation( - @Nonnull final String attributeDefinitionName, - @Nonnull final List enumValues) { - - return enumValues.stream().collect( - toMap(WithKey::getKey, enumValue -> enumValue, (enumValueA, enumValueB) -> { - throw new DuplicateKeyException(format("Enum Values have duplicated keys. " - + "Attribute definition name: '%s', Duplicated enum value: '%s'. " - + "Enum Values are expected to be unique inside their attribute definition.", - attributeDefinitionName, enumValueA.getKey())); - } - )); - } - - private EnumsUpdateActionUtils() { - } -} diff --git a/src/main/java/com/commercetools/sync/producttypes/utils/LocalizedEnumUpdateActionUtils.java b/src/main/java/com/commercetools/sync/producttypes/utils/LocalizedEnumUpdateActionUtils.java deleted file mode 100644 index c0b3ad3f95..0000000000 --- a/src/main/java/com/commercetools/sync/producttypes/utils/LocalizedEnumUpdateActionUtils.java +++ /dev/null @@ -1,62 +0,0 @@ -package com.commercetools.sync.producttypes.utils; - -import io.sphere.sdk.commands.UpdateAction; -import io.sphere.sdk.models.LocalizedEnumValue; -import io.sphere.sdk.producttypes.ProductType; -import io.sphere.sdk.producttypes.commands.updateactions.ChangeLocalizedEnumValueLabel; - -import javax.annotation.Nonnull; -import java.util.List; -import java.util.Optional; - -import static com.commercetools.sync.commons.utils.CommonTypeUpdateActionUtils.buildUpdateAction; -import static com.commercetools.sync.commons.utils.OptionalUtils.filterEmptyOptionals; - -public final class LocalizedEnumUpdateActionUtils { - /** - * Compares all the fields of an old {@link LocalizedEnumValue} and a new {@link LocalizedEnumValue} and returns a - * list of {@link UpdateAction}<{@link ProductType}> as a result. If both {@link LocalizedEnumValue} have - * identical fields then no update action is needed and hence an empty {@link List} is returned. - * - * @param attributeDefinitionName the attribute definition name whose localized enum values belong to. - * @param oldEnumValue the localized enum value which should be updated. - * @param newEnumValue the localized enum value where we get the new fields. - * @return A list with the update actions or an empty list if the localized enum values are - * identical. - */ - @Nonnull - public static List> buildActions( - @Nonnull final String attributeDefinitionName, - @Nonnull final LocalizedEnumValue oldEnumValue, - @Nonnull final LocalizedEnumValue newEnumValue) { - - return filterEmptyOptionals( - buildChangeLabelAction(attributeDefinitionName, oldEnumValue, newEnumValue) - ); - } - - /** - * Compares the {@code label} values of an old {@link LocalizedEnumValue} and a new {@link LocalizedEnumValue} - * and returns an {@link Optional} of update action, which would contain the {@code "changeLabel"} - * {@link UpdateAction}. If both, old and new {@link LocalizedEnumValue} have the same {@code label} values, - * then no update action is needed and empty optional will be returned. - * - * @param attributeDefinitionName the attribute definition name whose localized enum values belong to. - * @param oldEnumValue the old localized enum value. - * @param newEnumValue the new localized enum value which contains the new description. - * @return optional containing update action or empty optional if labels - * are identical. - */ - @Nonnull - public static Optional> buildChangeLabelAction( - @Nonnull final String attributeDefinitionName, - @Nonnull final LocalizedEnumValue oldEnumValue, - @Nonnull final LocalizedEnumValue newEnumValue) { - - return buildUpdateAction(oldEnumValue.getLabel(), newEnumValue.getLabel(), - () -> ChangeLocalizedEnumValueLabel.of(attributeDefinitionName, newEnumValue)); - } - - private LocalizedEnumUpdateActionUtils() { - } -} diff --git a/src/main/java/com/commercetools/sync/producttypes/utils/LocalizedEnumValueUpdateActionUtils.java b/src/main/java/com/commercetools/sync/producttypes/utils/LocalizedEnumValueUpdateActionUtils.java new file mode 100644 index 0000000000..a95e7c4140 --- /dev/null +++ b/src/main/java/com/commercetools/sync/producttypes/utils/LocalizedEnumValueUpdateActionUtils.java @@ -0,0 +1,85 @@ +package com.commercetools.sync.producttypes.utils; + +import com.commercetools.sync.commons.exceptions.DuplicateKeyException; +import io.sphere.sdk.commands.UpdateAction; +import io.sphere.sdk.models.LocalizedEnumValue; +import io.sphere.sdk.producttypes.ProductType; +import io.sphere.sdk.producttypes.commands.updateactions.AddLocalizedEnumValue; +import io.sphere.sdk.producttypes.commands.updateactions.ChangeLocalizedEnumValueLabel; +import io.sphere.sdk.producttypes.commands.updateactions.ChangeLocalizedEnumValueOrder; +import io.sphere.sdk.producttypes.commands.updateactions.RemoveEnumValues; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.List; + +import static com.commercetools.sync.commons.utils.OptionalUtils.filterEmptyOptionals; +import static com.commercetools.sync.commons.utils.enums.EnumValuesUpdateActionUtils.buildActions; +import static com.commercetools.sync.commons.utils.enums.LocalizedEnumValueUpdateActionUtils.buildChangeLabelAction; + +public final class LocalizedEnumValueUpdateActionUtils { + + /** + * Compares a list of old {@link LocalizedEnumValue}s with a list of new {@link LocalizedEnumValue}s for a given + * attribute definition. + * The method serves as a generic implementation for localized enum values syncing. The method takes in functions + * for building the required update actions (AddLocalizedEnumValue, RemoveEnumValue, ChangeLocalizedEnumValueOrder + * and 1-1 update actions on localized enum values (e.g. changeLabel) for the required resource. + * + *

If the list of new {@link LocalizedEnumValue}s is {@code null}, then remove actions are built for + * every existing localized enum value in the {@code oldEnumValues} list. + * + * @param attributeDefinitionName the attribute name whose localized enum values are going to be synced. + * @param oldEnumValues the old list of localized enum values. + * @param newEnumValues the new list of localized enum values. + * @return a list of localized enum values update actions if the list of localized enum values is not identical. + * Otherwise, if the localized enum values are identical, an empty list is returned. + * @throws DuplicateKeyException in case there are localized enum values with duplicate keys. + */ + @Nonnull + public static List> buildLocalizedEnumValuesUpdateActions( + @Nonnull final String attributeDefinitionName, + @Nonnull final List oldEnumValues, + @Nullable final List newEnumValues) { + + return buildActions( + attributeDefinitionName, + oldEnumValues, + newEnumValues, + RemoveEnumValues::of, + LocalizedEnumValueUpdateActionUtils::buildLocalizedEnumValueUpdateActions, + AddLocalizedEnumValue::of, + ChangeLocalizedEnumValueOrder::of, + null + ); + } + + + /** + * Compares all the fields of an old {@link LocalizedEnumValue} and a new {@link LocalizedEnumValue} and returns a + * list of {@link UpdateAction}<{@link ProductType}> as a result. If both {@link LocalizedEnumValue} have + * identical fields then no update action is needed and hence an empty {@link List} is returned. + * + * @param attributeDefinitionName the attribute definition name whose localized enum values belong to. + * @param oldEnumValue the localized enum value which should be updated. + * @param newEnumValue the localized enum value where we get the new fields. + * @return A list with the update actions or an empty list if the localized enum values are + * identical. + */ + @Nonnull + public static List> buildLocalizedEnumValueUpdateActions( + @Nonnull final String attributeDefinitionName, + @Nonnull final LocalizedEnumValue oldEnumValue, + @Nonnull final LocalizedEnumValue newEnumValue) { + + return filterEmptyOptionals( + buildChangeLabelAction(attributeDefinitionName, + oldEnumValue, + newEnumValue, + ChangeLocalizedEnumValueLabel::of) + ); + } + + private LocalizedEnumValueUpdateActionUtils() { + } +} diff --git a/src/main/java/com/commercetools/sync/producttypes/utils/LocalizedEnumsUpdateActionUtils.java b/src/main/java/com/commercetools/sync/producttypes/utils/LocalizedEnumsUpdateActionUtils.java deleted file mode 100644 index b95db74a04..0000000000 --- a/src/main/java/com/commercetools/sync/producttypes/utils/LocalizedEnumsUpdateActionUtils.java +++ /dev/null @@ -1,131 +0,0 @@ -package com.commercetools.sync.producttypes.utils; - -import com.commercetools.sync.commons.exceptions.DuplicateKeyException; -import io.sphere.sdk.commands.UpdateAction; -import io.sphere.sdk.models.LocalizedEnumValue; -import io.sphere.sdk.producttypes.ProductType; -import io.sphere.sdk.producttypes.commands.updateactions.AddLocalizedEnumValue; -import io.sphere.sdk.producttypes.commands.updateactions.ChangeLocalizedEnumValueOrder; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import java.util.Collections; -import java.util.List; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import static com.commercetools.sync.producttypes.utils.EnumsUpdateActionUtils.buildAddEnumValuesUpdateActions; -import static com.commercetools.sync.producttypes.utils.EnumsUpdateActionUtils.buildChangeEnumValuesOrderUpdateAction; -import static com.commercetools.sync.producttypes.utils.EnumsUpdateActionUtils.buildMatchingEnumValuesUpdateActions; -import static com.commercetools.sync.producttypes.utils.EnumsUpdateActionUtils.buildRemoveEnumValuesUpdateActions; -import static java.util.Collections.emptyList; - -public final class LocalizedEnumsUpdateActionUtils { - /** - * Compares a list of old {@link LocalizedEnumValue}s with a list of new {@link LocalizedEnumValue}s for a given - * attribute definition. - * The method serves as a generic implementation for localized enum values syncing. The method takes in functions - * for building the required update actions (AddLocalizedEnumValue, RemoveEnumValue, ChangeLocalizedEnumValueOrder - * and 1-1 update actions on localized enum values (e.g. changeLabel) for the required resource. - * - *

If the list of new {@link LocalizedEnumValue}s is {@code null}, then remove actions are built for - * every existing localized enum value in the {@code oldEnumValues} list. - * - * @param attributeDefinitionName the attribute name whose localized enum values are going to be synced. - * @param oldEnumValues the old list of localized enum values. - * @param newEnumValues the new list of localized enum values. - * @return a list of localized enum values update actions if the list of localized enum values is not identical. - * Otherwise, if the localized enum values are identical, an empty list is returned. - * @throws DuplicateKeyException in case there are localized enum values with duplicate keys. - */ - @Nonnull - public static List> buildLocalizedEnumValuesUpdateActions( - @Nonnull final String attributeDefinitionName, - @Nonnull final List oldEnumValues, - @Nullable final List newEnumValues) { - - if (newEnumValues != null && !newEnumValues.isEmpty()) { - return buildUpdateActions( - attributeDefinitionName, - oldEnumValues, - newEnumValues - ); - } else { - return buildRemoveEnumValuesUpdateActions( - attributeDefinitionName, - oldEnumValues, - newEnumValues - ) - .map(Collections::singletonList) - .orElse(emptyList()); - } - } - - /** - * Compares a list of old {@link LocalizedEnumValue}s with a list of new {@link LocalizedEnumValue}s for a given - * attribute definition. - * The method serves as a generic implementation for localized enum values syncing. The method takes in functions - * for building the required update actions (AddEnumValue, RemoveEnumValue, ChangeEnumValueOrder and 1-1 - * update actions on localized enum values (e.g. changeLabel) for the required resource. - * - * @param attributeDefinitionName the attribute name whose localized enum values are going to be synced. - * @param oldEnumValues the old list of localized enum values. - * @param newEnumValues the new list of localized enum values. - * @return a list of localized enum values update actions if the list of localized enum values is not identical. - * Otherwise, if the localized enum values are identical, an empty list is returned. - * @throws DuplicateKeyException in case there are localized enum values with duplicate keys. - */ - @Nonnull - private static List> buildUpdateActions( - @Nonnull final String attributeDefinitionName, - @Nonnull final List oldEnumValues, - @Nonnull final List newEnumValues) { - - final List> removeEnumValuesUpdateActions = buildRemoveEnumValuesUpdateActions( - attributeDefinitionName, - oldEnumValues, - newEnumValues - ) - .map(Collections::singletonList) - .orElse(emptyList()); - - final List> matchingEnumValuesUpdateActions = - buildMatchingEnumValuesUpdateActions( - attributeDefinitionName, - oldEnumValues, - newEnumValues, - LocalizedEnumUpdateActionUtils::buildActions - ); - - final List> addEnumValuesUpdateActions = buildAddEnumValuesUpdateActions( - attributeDefinitionName, - oldEnumValues, - newEnumValues, - AddLocalizedEnumValue::of - ); - - final List> changeEnumValuesOrderUpdateActions = - buildChangeEnumValuesOrderUpdateAction( - attributeDefinitionName, - oldEnumValues, - newEnumValues, - ChangeLocalizedEnumValueOrder::of - ) - .map(Collections::singletonList) - .orElse(emptyList()); - - return Stream.concat( - Stream.concat( - removeEnumValuesUpdateActions.stream(), - matchingEnumValuesUpdateActions.stream() - ), - Stream.concat( - addEnumValuesUpdateActions.stream(), - changeEnumValuesOrderUpdateActions.stream() - ) - ).collect(Collectors.toList()); - } - - private LocalizedEnumsUpdateActionUtils() { - } -} diff --git a/src/main/java/com/commercetools/sync/producttypes/utils/PlainEnumUpdateActionUtils.java b/src/main/java/com/commercetools/sync/producttypes/utils/PlainEnumUpdateActionUtils.java deleted file mode 100644 index 728ec788bc..0000000000 --- a/src/main/java/com/commercetools/sync/producttypes/utils/PlainEnumUpdateActionUtils.java +++ /dev/null @@ -1,62 +0,0 @@ -package com.commercetools.sync.producttypes.utils; - -import io.sphere.sdk.commands.UpdateAction; -import io.sphere.sdk.models.EnumValue; -import io.sphere.sdk.producttypes.ProductType; -import io.sphere.sdk.producttypes.commands.updateactions.ChangePlainEnumValueLabel; - -import javax.annotation.Nonnull; -import java.util.List; -import java.util.Optional; - -import static com.commercetools.sync.commons.utils.CommonTypeUpdateActionUtils.buildUpdateAction; -import static com.commercetools.sync.commons.utils.OptionalUtils.filterEmptyOptionals; - -public final class PlainEnumUpdateActionUtils { - /** - * Compares all the fields of an old {@link EnumValue} and a new {@link EnumValue} and returns a list of - * {@link UpdateAction}<{@link ProductType}> as a result. If both {@link EnumValue} have identical fields, - * then no update action is needed and hence an empty {@link List} is returned. - * - * @param attributeDefinitionName the attribute definition name whose plain enum values belong to. - * @param oldEnumValue the enum value which should be updated. - * @param newEnumValue the enum value where we get the new fields. - * @return A list with the update actions or an empty list if the enum values are - * identical. - */ - @Nonnull - public static List> buildActions( - @Nonnull final String attributeDefinitionName, - @Nonnull final EnumValue oldEnumValue, - @Nonnull final EnumValue newEnumValue) { - - return filterEmptyOptionals( - buildChangeLabelAction(attributeDefinitionName, oldEnumValue, newEnumValue) - ); - } - - /** - * Compares the {@code label} values of an old {@link EnumValue} and a new {@link EnumValue} - * and returns an {@link Optional} of update action, which would contain the {@code "changeLabel"} - * {@link UpdateAction}. If both, old and new {@link EnumValue} have the same {@code label} values, - * then no update action is needed and empty optional will be returned. - * - * @param attributeDefinitionName the attribute definition name whose plain enum values belong to. - * @param oldEnumValue the old plain enum value. - * @param newEnumValue the new plain enum value which contains the new description. - * @return optional containing update action or empty optional if labels - * are identical. - */ - @Nonnull - public static Optional> buildChangeLabelAction( - @Nonnull final String attributeDefinitionName, - @Nonnull final EnumValue oldEnumValue, - @Nonnull final EnumValue newEnumValue) { - - return buildUpdateAction(oldEnumValue.getLabel(), newEnumValue.getLabel(), - () -> ChangePlainEnumValueLabel.of(attributeDefinitionName, newEnumValue)); - } - - private PlainEnumUpdateActionUtils() { - } -} diff --git a/src/main/java/com/commercetools/sync/producttypes/utils/PlainEnumValueUpdateActionUtils.java b/src/main/java/com/commercetools/sync/producttypes/utils/PlainEnumValueUpdateActionUtils.java new file mode 100644 index 0000000000..99eca6e58b --- /dev/null +++ b/src/main/java/com/commercetools/sync/producttypes/utils/PlainEnumValueUpdateActionUtils.java @@ -0,0 +1,82 @@ +package com.commercetools.sync.producttypes.utils; + +import com.commercetools.sync.commons.utils.enums.EnumValuesUpdateActionUtils; +import io.sphere.sdk.commands.UpdateAction; +import io.sphere.sdk.models.EnumValue; +import io.sphere.sdk.producttypes.ProductType; +import io.sphere.sdk.producttypes.commands.updateactions.AddEnumValue; +import io.sphere.sdk.producttypes.commands.updateactions.ChangeEnumValueOrder; +import io.sphere.sdk.producttypes.commands.updateactions.ChangePlainEnumValueLabel; +import io.sphere.sdk.producttypes.commands.updateactions.RemoveEnumValues; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.List; + +import static com.commercetools.sync.commons.utils.OptionalUtils.filterEmptyOptionals; +import static com.commercetools.sync.commons.utils.enums.PlainEnumValueUpdateActionUtils.buildChangeLabelAction; + +public final class PlainEnumValueUpdateActionUtils { + + /** + * Compares a list of old {@link EnumValue}s with a list of new {@link EnumValue}s for a given + * attribute definition. + * The method serves as a generic implementation for plain enum values syncing. The method takes in functions + * for building the required update actions (AddEnumValue, RemoveEnumValue, ChangeEnumValueOrder + * and 1-1 update actions on plain enum values (e.g. changeLabel) for the required resource. + * + *

If the list of new {@link EnumValue}s is {@code null}, then remove actions are built for + * every existing plain enum value in the {@code oldEnumValues} list. + * + * @param attributeDefinitionName the attribute name whose plain enum values are going to be synced. + * @param oldEnumValues the old list of plain enum values. + * @param newEnumValues the new list of plain enum values. + * @return a list of plain enum values update actions if the list of plain enum values is not identical. + * Otherwise, if the plain enum values are identical, an empty list is returned. + */ + @Nonnull + public static List> buildEnumValuesUpdateActions( + @Nonnull final String attributeDefinitionName, + @Nonnull final List oldEnumValues, + @Nullable final List newEnumValues) { + + return EnumValuesUpdateActionUtils.buildActions(attributeDefinitionName, + oldEnumValues, + newEnumValues, + RemoveEnumValues::of, + PlainEnumValueUpdateActionUtils::buildEnumValueUpdateActions, + AddEnumValue::of, + ChangeEnumValueOrder::of, + null); + } + + /** + * Compares all the fields of an old {@link EnumValue} and a new {@link EnumValue} and returns a list of + * {@link UpdateAction}<{@link ProductType}> as a result. If both {@link EnumValue} have identical fields, + * then no update action is needed and hence an empty {@link List} is returned. + * + * @param attributeDefinitionName the attribute definition name whose plain enum values belong to. + * @param oldEnumValue the enum value which should be updated. + * @param newEnumValue the enum value where we get the new fields. + * @return A list with the update actions or an empty list if the enum values are + * identical. + */ + @Nonnull + public static List> buildEnumValueUpdateActions( + @Nonnull final String attributeDefinitionName, + @Nonnull final EnumValue oldEnumValue, + @Nonnull final EnumValue newEnumValue) { + + return filterEmptyOptionals( + buildChangeLabelAction(attributeDefinitionName, + oldEnumValue, + newEnumValue, + ChangePlainEnumValueLabel::of + ) + ); + } + + + private PlainEnumValueUpdateActionUtils() { + } +} diff --git a/src/main/java/com/commercetools/sync/producttypes/utils/PlainEnumsUpdateActionUtils.java b/src/main/java/com/commercetools/sync/producttypes/utils/PlainEnumsUpdateActionUtils.java deleted file mode 100644 index 1eafa1079b..0000000000 --- a/src/main/java/com/commercetools/sync/producttypes/utils/PlainEnumsUpdateActionUtils.java +++ /dev/null @@ -1,123 +0,0 @@ -package com.commercetools.sync.producttypes.utils; - -import io.sphere.sdk.commands.UpdateAction; -import io.sphere.sdk.models.EnumValue; -import io.sphere.sdk.producttypes.ProductType; -import io.sphere.sdk.producttypes.commands.updateactions.AddEnumValue; -import io.sphere.sdk.producttypes.commands.updateactions.ChangeEnumValueOrder; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import java.util.Collections; -import java.util.List; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import static com.commercetools.sync.producttypes.utils.EnumsUpdateActionUtils.buildAddEnumValuesUpdateActions; -import static com.commercetools.sync.producttypes.utils.EnumsUpdateActionUtils.buildChangeEnumValuesOrderUpdateAction; -import static com.commercetools.sync.producttypes.utils.EnumsUpdateActionUtils.buildMatchingEnumValuesUpdateActions; -import static com.commercetools.sync.producttypes.utils.EnumsUpdateActionUtils.buildRemoveEnumValuesUpdateActions; -import static java.util.Collections.emptyList; - -public final class PlainEnumsUpdateActionUtils { - /** - * Compares a list of old {@link EnumValue}s with a list of new {@link EnumValue}s for a given - * attribute definition. - * The method serves as a generic implementation for plain enum values syncing. The method takes in functions - * for building the required update actions (AddEnumValue, RemoveEnumValue, ChangeEnumValueOrder - * and 1-1 update actions on plain enum values (e.g. changeLabel) for the required resource. - * - *

If the list of new {@link EnumValue}s is {@code null}, then remove actions are built for - * every existing plain enum value in the {@code oldEnumValues} list. - * - * @param attributeDefinitionName the attribute name whose plain enum values are going to be synced. - * @param oldEnumValues the old list of plain enum values. - * @param newEnumValues the new list of plain enum values. - * @return a list of plain enum values update actions if the list of plain enum values is not identical. - * Otherwise, if the plain enum values are identical, an empty list is returned. - */ - @Nonnull - public static List> buildEnumValuesUpdateActions( - @Nonnull final String attributeDefinitionName, - @Nonnull final List oldEnumValues, - @Nullable final List newEnumValues) { - - if (newEnumValues != null && !newEnumValues.isEmpty()) { - return buildUpdateActions(attributeDefinitionName, oldEnumValues, newEnumValues); - } else { - return buildRemoveEnumValuesUpdateActions(attributeDefinitionName, oldEnumValues, newEnumValues) - .map(Collections::singletonList) - .orElse(emptyList()); - } - } - - - /** - * Compares a list of old {@link EnumValue}s with a list of new {@link EnumValue}s for a given attribute - * definition. - * The method serves as a implementation for plain enum values syncing. The method takes in functions - * for building the required update actions (AddEnumValue, RemoveEnumValue, ChangeEnumValueOrder and 1-1 - * update actions on plain enum values (e.g. changeLabel) for the required resource. - * - * @param attributeDefinitionName the attribute name whose plain enum values are going to be synced. - * @param oldEnumValues the old list of plain enum values. - * @param newEnumValues the new list of plain enum values. - * @return a list of plain enum values update actions if the list of plain enum values is not identical. - * Otherwise, if the plain enum values are identical, an empty list is returned. - */ - @Nonnull - private static List> buildUpdateActions( - @Nonnull final String attributeDefinitionName, - @Nonnull final List oldEnumValues, - @Nonnull final List newEnumValues) { - - final List> removeEnumValuesUpdateActions = buildRemoveEnumValuesUpdateActions( - attributeDefinitionName, - oldEnumValues, - newEnumValues - ) - .map(Collections::singletonList) - .orElse(emptyList()); - - final List> matchingEnumValuesUpdateActions = - buildMatchingEnumValuesUpdateActions( - attributeDefinitionName, - oldEnumValues, - newEnumValues, - PlainEnumUpdateActionUtils::buildActions - ); - - final List> addEnumValuesUpdateActions = buildAddEnumValuesUpdateActions( - attributeDefinitionName, - oldEnumValues, - newEnumValues, - AddEnumValue::of - ); - - final List> changeEnumValuesOrderUpdateActions = - buildChangeEnumValuesOrderUpdateAction( - attributeDefinitionName, - oldEnumValues, - newEnumValues, - ChangeEnumValueOrder::of - ) - .map(Collections::singletonList) - .orElse(emptyList()); - - return Stream.concat( - Stream.concat( - removeEnumValuesUpdateActions.stream(), - matchingEnumValuesUpdateActions.stream() - ), - Stream.concat( - addEnumValuesUpdateActions.stream(), - changeEnumValuesOrderUpdateActions.stream() - ) - - ).collect(Collectors.toList()); - } - - - private PlainEnumsUpdateActionUtils() { - } -} diff --git a/src/main/java/com/commercetools/sync/types/utils/EnumUpdateActionsUtils.java b/src/main/java/com/commercetools/sync/types/utils/EnumUpdateActionsUtils.java deleted file mode 100644 index 6d9d2e7f08..0000000000 --- a/src/main/java/com/commercetools/sync/types/utils/EnumUpdateActionsUtils.java +++ /dev/null @@ -1,132 +0,0 @@ -package com.commercetools.sync.types.utils; - -import com.commercetools.sync.commons.exceptions.DuplicateKeyException; -import io.sphere.sdk.commands.UpdateAction; -import io.sphere.sdk.models.EnumValue; -import io.sphere.sdk.models.WithKey; -import io.sphere.sdk.types.Type; - -import javax.annotation.Nonnull; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.function.BiFunction; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import static com.commercetools.sync.commons.utils.CommonTypeUpdateActionUtils.buildUpdateAction; -import static java.lang.String.format; -import static java.util.stream.Collectors.toMap; - -final class EnumUpdateActionsUtils { - - /** - * Compares the order of a list of old enum values and a list of new enum values. If there is a change in order, - * then a change of enum values order (with the new order) is built. If there are no changes in order an empty - * optional is returned. - * - * @param fieldDefinitionName the field definition name whose enum values belong to. - * @param oldEnumValues the list of old enum values. - * @param newEnumValues the list of new enum values. - * @param changeOrderEnumCallback the function that is called to apply the change in the order. - * @param the enum type of the elements to change the order for. - * @return an optional update action if the the order of the enum values is not identical. - * Otherwise, if the enum values order is identical, an empty optional is returned. - */ - @Nonnull - static Optional> buildChangeEnumValuesOrderUpdateAction( - @Nonnull final String fieldDefinitionName, - @Nonnull final List oldEnumValues, - @Nonnull final List newEnumValues, - @Nonnull final BiFunction, UpdateAction> changeOrderEnumCallback) { - - final List newKeys = newEnumValues - .stream() - .map(WithKey::getKey) - .collect(Collectors.toList()); - - final List existingKeys = oldEnumValues - .stream() - .map(WithKey::getKey) - .filter(newKeys::contains) - .collect(Collectors.toList()); - - final List notExistingKeys = newKeys - .stream() - .filter(newKey -> !existingKeys.contains(newKey)) - .collect(Collectors.toList()); - - final List allKeys = Stream.concat(existingKeys.stream(), notExistingKeys.stream()) - .collect(Collectors.toList()); - - return buildUpdateAction( - allKeys, - newKeys, - () -> changeOrderEnumCallback.apply(fieldDefinitionName, newKeys) - ); - } - - /** - * Checks if there are any new enum values which are not existing in the {@code oldEnumValues}. - * If there are, then "add" enum values update actions are built. - * Otherwise, if there are no new enum values, then an empty list is returned. - * - * @param fieldDefinitionName the field definition name whose enum values belong to. - * @param oldEnumValues the list of old enum values. - * @param newEnumValues the list of new enum values. - * @param addEnumCallback the function that is called in order to add the new enum instance - * @param the enum type of the element to add. - * @return a list of enum values update actions if there are new enum value that should be added. - * Otherwise, if the enum values are identical, an empty optional is returned. - */ - @Nonnull - static List> buildAddEnumValuesUpdateActions( - @Nonnull final String fieldDefinitionName, - @Nonnull final List oldEnumValues, - @Nonnull final List newEnumValues, - @Nonnull final BiFunction> addEnumCallback) { - - final Map oldEnumValuesKeyMap = getEnumValuesKeyMapWithKeyValidation( - fieldDefinitionName, - oldEnumValues - ); - - final Map newEnumValuesKeyMap = getEnumValuesKeyMapWithKeyValidation( - fieldDefinitionName, - newEnumValues - ); - - return newEnumValuesKeyMap.values() - .stream() - .filter(newEnumValue -> !oldEnumValuesKeyMap.containsKey(newEnumValue.getKey())) - .map(newEnumValue -> addEnumCallback.apply(fieldDefinitionName, newEnumValue)) - .collect(Collectors.toList()); - } - - /** - * Given a list of new {@link EnumValue}s, gets a map where the keys are the enum value key, and the values - * are the enum instances. - * - * @param fieldDefinitionName the field definition name whose the enum values belong to. - * @param enumValues the list of enum values. - * @param the enum type of the elements of the list. - * @return a map with the enum value key as a key of the map, and the enum - * value as a value of the map. - */ - @Nonnull - private static Map getEnumValuesKeyMapWithKeyValidation( - @Nonnull final String fieldDefinitionName, - @Nonnull final List enumValues) { - - return enumValues.stream().collect( - toMap(WithKey::getKey, enumValue -> enumValue, (enumValueA, enumValueB) -> { - throw new DuplicateKeyException(format("Enum Values have duplicated keys. " - + "Field definition name: '%s', Duplicated enum value: '%s'. " - + "Enum Values are expected to be unique inside their field definition.", - fieldDefinitionName, enumValueA.getKey())); - })); - } - - private EnumUpdateActionsUtils() { - } -} diff --git a/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionUpdateActionUtils.java b/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionUpdateActionUtils.java index b9ecd06e4e..834e642b1d 100644 --- a/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionUpdateActionUtils.java +++ b/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionUpdateActionUtils.java @@ -3,6 +3,8 @@ import com.commercetools.sync.commons.exceptions.BuildUpdateActionException; import com.commercetools.sync.types.helpers.FieldTypeAssert; import io.sphere.sdk.commands.UpdateAction; +import io.sphere.sdk.models.EnumValue; +import io.sphere.sdk.models.LocalizedEnumValue; import io.sphere.sdk.models.LocalizedString; import io.sphere.sdk.types.EnumFieldType; import io.sphere.sdk.types.FieldDefinition; @@ -11,13 +13,14 @@ import io.sphere.sdk.types.commands.updateactions.ChangeFieldDefinitionLabel; import javax.annotation.Nonnull; +import java.util.ArrayList; import java.util.List; import java.util.Optional; import static com.commercetools.sync.commons.utils.CommonTypeUpdateActionUtils.buildUpdateAction; import static com.commercetools.sync.commons.utils.OptionalUtils.filterEmptyOptionals; import static com.commercetools.sync.types.utils.LocalizedEnumUpdateActionUtils.buildLocalizedEnumValuesUpdateActions; -import static com.commercetools.sync.types.utils.PlainEnumUpdateActionUtils.buildEnumValuesUpdateActions; +import static com.commercetools.sync.types.utils.PlainEnumValueUpdateActionUtils.buildEnumValuesUpdateActions; public final class FieldDefinitionUpdateActionUtils { @@ -38,26 +41,44 @@ public static List> buildActions( @Nonnull final FieldDefinition oldFieldDefinition, @Nonnull final FieldDefinition newFieldDefinition) throws BuildUpdateActionException { - FieldTypeAssert.assertTypesAreNull(oldFieldDefinition.getType(), newFieldDefinition.getType()); - final List> updateActions = filterEmptyOptionals(buildChangeLabelUpdateAction(oldFieldDefinition, newFieldDefinition)); + updateActions.addAll(buildEnumUpdateActions(oldFieldDefinition, newFieldDefinition)); + + return updateActions; + } + + /** + * Compares all the {@link EnumValue} and {@link LocalizedEnumValue} values of {@link FieldDefinition}s and returns + * a list of {@link UpdateAction}<{@link Type}> as a result. If both {@link FieldDefinition}s have identical + * enum values, then no update action is needed and hence an empty {@link List} is returned. + * + * @param oldFieldDefinition the old field definition which should be updated. + * @param newFieldDefinition the new field definition where we get the new fields. + * @return A list with the update actions or an empty list if the field definition enums are identical. + * @throws BuildUpdateActionException in case there are attribute definitions with the null attribute type. + */ + @Nonnull + public static List> buildEnumUpdateActions( + @Nonnull final FieldDefinition oldFieldDefinition, + @Nonnull final FieldDefinition newFieldDefinition) throws BuildUpdateActionException { + + FieldTypeAssert.assertTypesAreNull(oldFieldDefinition.getType(), newFieldDefinition.getType()); + final List> updateActions = new ArrayList<>(); if (isPlainEnumField(oldFieldDefinition)) { updateActions.addAll(buildEnumValuesUpdateActions( - oldFieldDefinition.getName(), - ((EnumFieldType) oldFieldDefinition.getType()).getValues(), - ((EnumFieldType) newFieldDefinition.getType()).getValues() + oldFieldDefinition.getName(), + ((EnumFieldType) oldFieldDefinition.getType()).getValues(), + ((EnumFieldType) newFieldDefinition.getType()).getValues() )); } else if (isLocalizedEnumField(oldFieldDefinition)) { updateActions.addAll(buildLocalizedEnumValuesUpdateActions( - oldFieldDefinition.getName(), - ((LocalizedEnumFieldType) oldFieldDefinition.getType()).getValues(), - ((LocalizedEnumFieldType) newFieldDefinition.getType()).getValues() + oldFieldDefinition.getName(), + ((LocalizedEnumFieldType) oldFieldDefinition.getType()).getValues(), + ((LocalizedEnumFieldType) newFieldDefinition.getType()).getValues() )); } - - return updateActions; } @@ -101,6 +122,8 @@ public static Optional> buildChangeLabelUpdateAction( ); } + + private FieldDefinitionUpdateActionUtils() { } } diff --git a/src/main/java/com/commercetools/sync/types/utils/LocalizedEnumUpdateActionUtils.java b/src/main/java/com/commercetools/sync/types/utils/LocalizedEnumUpdateActionUtils.java index dff43cb95d..4eac897c1c 100644 --- a/src/main/java/com/commercetools/sync/types/utils/LocalizedEnumUpdateActionUtils.java +++ b/src/main/java/com/commercetools/sync/types/utils/LocalizedEnumUpdateActionUtils.java @@ -9,14 +9,9 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; -import java.util.Collections; import java.util.List; -import java.util.stream.Collectors; -import java.util.stream.Stream; -import static com.commercetools.sync.types.utils.EnumUpdateActionsUtils.buildAddEnumValuesUpdateActions; -import static com.commercetools.sync.types.utils.EnumUpdateActionsUtils.buildChangeEnumValuesOrderUpdateAction; -import static java.util.Collections.emptyList; +import static com.commercetools.sync.commons.utils.enums.EnumValuesUpdateActionUtils.buildActions; public final class LocalizedEnumUpdateActionUtils { /** @@ -39,49 +34,19 @@ public static List> buildLocalizedEnumValuesUpdateActions( @Nonnull final List oldEnumValues, @Nullable final List newEnumValues) { - if (newEnumValues != null && !newEnumValues.isEmpty()) { - return buildUpdateActions(fieldDefinitionName, oldEnumValues, newEnumValues); - } - /* TODO: If the list of newEnumValues is null, then remove actions are built for every existing localized enum value in the oldEnumValues list. */ - return emptyList(); - } - - - @Nonnull - private static List> buildUpdateActions( - @Nonnull final String fieldDefinitionName, - @Nonnull final List oldEnumValues, - @Nonnull final List newEnumValues) { - - - final List> addEnumValuesUpdateActions = buildAddEnumValuesUpdateActions( - fieldDefinitionName, - oldEnumValues, - newEnumValues, - AddLocalizedEnumValue::of - ); - - final List> changeEnumValuesOrderUpdateActions = - buildChangeEnumValuesOrderUpdateAction( - fieldDefinitionName, + return buildActions(fieldDefinitionName, oldEnumValues, newEnumValues, - ChangeLocalizedEnumValueOrder::of - ) - .map(Collections::singletonList) - .orElse(emptyList()); - - return Stream.concat( - - addEnumValuesUpdateActions.stream(), - changeEnumValuesOrderUpdateActions.stream() - - ).collect(Collectors.toList()); + null, + null, + AddLocalizedEnumValue::of, + null, + ChangeLocalizedEnumValueOrder::of); } private LocalizedEnumUpdateActionUtils() { diff --git a/src/main/java/com/commercetools/sync/types/utils/PlainEnumUpdateActionUtils.java b/src/main/java/com/commercetools/sync/types/utils/PlainEnumValueUpdateActionUtils.java similarity index 53% rename from src/main/java/com/commercetools/sync/types/utils/PlainEnumUpdateActionUtils.java rename to src/main/java/com/commercetools/sync/types/utils/PlainEnumValueUpdateActionUtils.java index fc9a15f5d3..2baa7f437c 100644 --- a/src/main/java/com/commercetools/sync/types/utils/PlainEnumUpdateActionUtils.java +++ b/src/main/java/com/commercetools/sync/types/utils/PlainEnumValueUpdateActionUtils.java @@ -1,5 +1,6 @@ package com.commercetools.sync.types.utils; +import com.commercetools.sync.commons.utils.enums.EnumValuesUpdateActionUtils; import io.sphere.sdk.commands.UpdateAction; import io.sphere.sdk.models.EnumValue; import io.sphere.sdk.types.Type; @@ -8,16 +9,10 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; -import java.util.Collections; import java.util.List; -import java.util.stream.Collectors; -import java.util.stream.Stream; -import static com.commercetools.sync.types.utils.EnumUpdateActionsUtils.buildAddEnumValuesUpdateActions; -import static com.commercetools.sync.types.utils.EnumUpdateActionsUtils.buildChangeEnumValuesOrderUpdateAction; -import static java.util.Collections.emptyList; +public final class PlainEnumValueUpdateActionUtils { -public final class PlainEnumUpdateActionUtils { /** * Compares a list of old {@link EnumValue}s with a list of new {@link EnumValue}s for a given * field definition. @@ -37,48 +32,21 @@ public static List> buildEnumValuesUpdateActions( @Nonnull final List oldEnumValues, @Nullable final List newEnumValues) { - if (newEnumValues != null && !newEnumValues.isEmpty()) { - return buildUpdateActions(fieldDefinitionName, oldEnumValues, newEnumValues); - } - /* TODO: If the list of newEnumValues is null, then remove actions are built for every existing plain enum value in the oldEnumValues list. */ - return emptyList(); - } - - - @Nonnull - private static List> buildUpdateActions( - @Nonnull final String fieldDefinitionName, - @Nonnull final List oldEnumValues, - @Nonnull final List newEnumValues) { - - - final List> addEnumValuesUpdateActions = buildAddEnumValuesUpdateActions( - fieldDefinitionName, - oldEnumValues, - newEnumValues, - AddEnumValue::of - ); - - final List> changeEnumValuesOrderUpdateActions = - buildChangeEnumValuesOrderUpdateAction( - fieldDefinitionName, + return EnumValuesUpdateActionUtils.buildActions(fieldDefinitionName, oldEnumValues, newEnumValues, - ChangeEnumValueOrder::of - ) - .map(Collections::singletonList) - .orElse(emptyList()); - - return Stream.concat(addEnumValuesUpdateActions.stream(), changeEnumValuesOrderUpdateActions.stream()) - .collect(Collectors.toList()); + null, + null, + AddEnumValue::of, + null, + ChangeEnumValueOrder::of); } - - private PlainEnumUpdateActionUtils() { + private PlainEnumValueUpdateActionUtils() { } } diff --git a/src/test/java/com/commercetools/sync/commons/utils/enums/EnumValuesUpdateActionUtilsTest.java b/src/test/java/com/commercetools/sync/commons/utils/enums/EnumValuesUpdateActionUtilsTest.java new file mode 100644 index 0000000000..c3e51613ed --- /dev/null +++ b/src/test/java/com/commercetools/sync/commons/utils/enums/EnumValuesUpdateActionUtilsTest.java @@ -0,0 +1,325 @@ +package com.commercetools.sync.commons.utils.enums; + +import com.commercetools.sync.commons.utils.TriFunction; +import io.sphere.sdk.commands.UpdateAction; +import io.sphere.sdk.models.EnumValue; +import io.sphere.sdk.producttypes.ProductType; +import io.sphere.sdk.producttypes.commands.updateactions.AddEnumValue; +import io.sphere.sdk.producttypes.commands.updateactions.ChangeEnumValueOrder; +import io.sphere.sdk.producttypes.commands.updateactions.ChangePlainEnumValueLabel; +import io.sphere.sdk.producttypes.commands.updateactions.RemoveEnumValues; +import org.junit.Test; + +import javax.annotation.Nonnull; +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +import static com.commercetools.sync.commons.utils.OptionalUtils.filterEmptyOptionals; +import static com.commercetools.sync.commons.utils.enums.EnumValuesUpdateActionUtils.buildActions; +import static com.commercetools.sync.commons.utils.enums.EnumValuesUpdateActionUtils.buildAddEnumValuesUpdateActions; +import static com.commercetools.sync.commons.utils.enums.EnumValuesUpdateActionUtils.buildChangeEnumValuesOrderUpdateAction; +import static com.commercetools.sync.commons.utils.enums.EnumValuesUpdateActionUtils.buildMatchingEnumValuesUpdateActions; +import static com.commercetools.sync.commons.utils.enums.EnumValuesUpdateActionUtils.buildRemoveEnumValuesUpdateAction; +import static com.commercetools.sync.commons.utils.enums.PlainEnumValueTestObjects.*; +import static com.commercetools.sync.commons.utils.enums.PlainEnumValueUpdateActionUtils.buildChangeLabelAction; +import static java.util.Arrays.asList; +import static org.assertj.core.api.Assertions.assertThat; + +public class EnumValuesUpdateActionUtilsTest { + + private static final String attributeDefinitionName = "attribute_definition_name_1"; + + @Test + public void buildActions_WithoutCallbacks_ShouldNotBuildActions() { + final List> updateActions = buildActions(attributeDefinitionName, + ENUM_VALUES_ABC, + ENUM_VALUES_ABCD, + null, + null, + null, + null, + null); + + assertThat(updateActions).isEmpty(); + } + + @Test + public void buildActions_WithoutRemoveCallback_ShouldNotBuildRemoveAction() { + final List> updateActions = buildActions(attributeDefinitionName, + ENUM_VALUES_ABC, + null, + null, + null, + null, + null, + null); + + assertThat(updateActions).doesNotContain( + RemoveEnumValues.of(attributeDefinitionName, asList("a", "b", "c")) + ); + } + + @Test + public void buildActions_WithNullNewEnumValues_ShouldJustBuildRemoveActions() { + final List> updateActions = buildActions(attributeDefinitionName, + ENUM_VALUES_ABC, + null, + RemoveEnumValues::of, + null, + AddEnumValue::of, + null, + null); + + assertThat(updateActions).containsAnyOf( + RemoveEnumValues.of(attributeDefinitionName, asList("a", "b", "c")) + ); + + assertThat(updateActions).doesNotContain( + AddEnumValue.of(attributeDefinitionName, ENUM_VALUE_A) + ); + } + + @Test + public void buildActions_WithEmptyNewEnumValues_ShouldJustBuildRemoveActions() { + final List> updateActions = buildActions(attributeDefinitionName, + ENUM_VALUES_ABC, + Collections.emptyList(), + RemoveEnumValues::of, + null, + AddEnumValue::of, + null, + null); + + assertThat(updateActions).containsAnyOf( + RemoveEnumValues.of(attributeDefinitionName, asList("a", "b", "c")) + ); + + assertThat(updateActions).doesNotContain( + AddEnumValue.of(attributeDefinitionName, ENUM_VALUE_A) + ); + } + + @Test + public void buildActions_WithRemoveCallback_ShouldBuildRemoveAction() { + final List> updateActions = buildActions(attributeDefinitionName, + ENUM_VALUES_ABC, + null, + RemoveEnumValues::of, + null, + null, + null, + null); + + assertThat(updateActions).containsAnyOf( + RemoveEnumValues.of(attributeDefinitionName, asList("a", "b", "c")) + ); + } + + @Test + public void buildActions_WithoutMatchingEnumCallback_ShouldNotBuildMatchingActions() { + final List> updateActions = buildActions(attributeDefinitionName, + ENUM_VALUES_AB, + null, + null, + null, + null, + null, + null); + + assertThat(updateActions).doesNotContain( + ChangePlainEnumValueLabel.of(attributeDefinitionName, ENUM_VALUE_A), + ChangePlainEnumValueLabel.of(attributeDefinitionName, ENUM_VALUE_B) + ); + } + + @Test + public void buildActions_WithMatchingEnumCallback_ShouldBuildMatchingActions() { + final List> updateActions = buildActions(attributeDefinitionName, + ENUM_VALUES_AB, + ENUM_VALUES_AB_WITH_DIFFERENT_LABEL, + null, + getMatchingValueFunction(), + null, + null, + null); + + assertThat(updateActions).containsAnyOf( + ChangePlainEnumValueLabel.of(attributeDefinitionName, ENUM_VALUE_A_DIFFERENT_LABEL) + ); + } + + @Test + public void buildActions_WithoutAddEnumCallback_ShouldNotBuildAddEnumActions() { + final List> updateActions = buildActions(attributeDefinitionName, + ENUM_VALUES_AB, + ENUM_VALUES_ABC, + null, + null, + null, + null, + null); + + assertThat(updateActions).doesNotContain( + AddEnumValue.of(attributeDefinitionName, ENUM_VALUE_C) + ); + } + + @Test + public void buildActions_WithAddEnumCallback_ShouldBuildAddEnumActions() { + final List> updateActions = buildActions(attributeDefinitionName, + ENUM_VALUES_AB, + ENUM_VALUES_ABC, + null, + null, + AddEnumValue::of, + null, + null); + + assertThat(updateActions).containsAnyOf( + AddEnumValue.of(attributeDefinitionName, ENUM_VALUE_C) + ); + } + + @Test + public void buildActions_WithoutChangeOrderEnumCallback_ShouldNotBuildChangeOrderEnumActions() { + final List> updateActions = buildActions(attributeDefinitionName, + ENUM_VALUES_ABC, + ENUM_VALUES_CAB, + null, + null, + null, + null, + null); + + assertThat(updateActions).doesNotContain( + ChangeEnumValueOrder.of(attributeDefinitionName, asList( + ENUM_VALUE_C, + ENUM_VALUE_A, + ENUM_VALUE_B + )) + ); + } + + @Test + public void buildActions_WithChangeOrderEnumCallback_ShouldBuildChangeOrderEnumActions() { + final List> updateActions = buildActions(attributeDefinitionName, + ENUM_VALUES_ABC, + ENUM_VALUES_CAB, + null, + null, + null, + ChangeEnumValueOrder::of, + null); + + assertThat(updateActions).containsAnyOf( + ChangeEnumValueOrder.of(attributeDefinitionName, asList( + ENUM_VALUE_C, + ENUM_VALUE_A, + ENUM_VALUE_B + )) + ); + } + + @Test + public void buildRemoveEnumValuesUpdateAction_WithNullNewEnumValues_ShouldBuildRemoveActions() { + final Optional> updateAction = buildRemoveEnumValuesUpdateAction( + attributeDefinitionName, + ENUM_VALUES_ABC, + null, + RemoveEnumValues::of); + + assertThat(updateAction).isNotEmpty(); + assertThat(updateAction).isEqualTo( + Optional.of(RemoveEnumValues.of(attributeDefinitionName, asList("a", "b", "c")))); + } + + @Test + public void buildMatchingEnumValuesUpdateActions_WithDifferentEnumValues_ShouldBuildChangeLabelActions() { + final List> updateActions = buildMatchingEnumValuesUpdateActions( + attributeDefinitionName, + ENUM_VALUES_AB, + ENUM_VALUES_AB_WITH_DIFFERENT_LABEL, + getMatchingValueFunction()); + + assertThat(updateActions).containsAnyOf( + ChangePlainEnumValueLabel.of(attributeDefinitionName, ENUM_VALUE_A_DIFFERENT_LABEL) + ); + } + + @Test + public void buildMatchingEnumValuesUpdateActions_WithSameNewEnumValues_ShouldNotBuildChangeLabelActions() { + final List> updateActions = buildMatchingEnumValuesUpdateActions( + attributeDefinitionName, + ENUM_VALUES_AB, + ENUM_VALUES_AB, + getMatchingValueFunction()); + + assertThat(updateActions).isEmpty(); + } + + @Test + public void buildAddEnumValuesUpdateActions_WithNewEnumValues_ShouldBuildAddActions() { + final List> updateActions = buildAddEnumValuesUpdateActions(attributeDefinitionName, + ENUM_VALUES_AB, + ENUM_VALUES_ABC, + AddEnumValue::of); + + assertThat(updateActions).containsAnyOf( + AddEnumValue.of(attributeDefinitionName, ENUM_VALUE_C) + ); + } + + @Test + public void buildAddEnumValuesUpdateActions_WithSameEnumValues_ShouldNotBuildAddActions() { + final List> updateActions = buildAddEnumValuesUpdateActions(attributeDefinitionName, + ENUM_VALUES_AB, + ENUM_VALUES_AB, + AddEnumValue::of); + + assertThat(updateActions).isEmpty(); + } + + @Test + public void buildChangeEnumValuesOrderUpdateAction_WithNewEnumValues_ShouldBuildAddActions() { + final Optional> updateAction = + buildChangeEnumValuesOrderUpdateAction(attributeDefinitionName, + ENUM_VALUES_ABC, + ENUM_VALUES_CAB, + ChangeEnumValueOrder::of); + + assertThat(updateAction).isNotEmpty(); + assertThat(updateAction).isEqualTo( + Optional.of(ChangeEnumValueOrder.of(attributeDefinitionName, asList( + ENUM_VALUE_C, + ENUM_VALUE_A, + ENUM_VALUE_B)))); + } + + @Test + public void buildChangeEnumValuesOrderUpdateAction_WithSameEnumValues_ShouldNotBuildAddActions() { + final List> updateActions = buildAddEnumValuesUpdateActions(attributeDefinitionName, + ENUM_VALUES_AB, + ENUM_VALUES_AB, + AddEnumValue::of); + + assertThat(updateActions).isEmpty(); + } + + @Nonnull + private TriFunction>> getMatchingValueFunction() { + return (definitionName, oldEnumValue, newEnumValue) -> { + + if (oldEnumValue == null || newEnumValue == null) { + return Collections.emptyList(); + } + + return filterEmptyOptionals( + buildChangeLabelAction(attributeDefinitionName, + oldEnumValue, + newEnumValue, + ChangePlainEnumValueLabel::of + )); + }; + } +} diff --git a/src/test/java/com/commercetools/sync/commons/utils/enums/LocalizedEnumValueTestObjects.java b/src/test/java/com/commercetools/sync/commons/utils/enums/LocalizedEnumValueTestObjects.java new file mode 100644 index 0000000000..fb8b234226 --- /dev/null +++ b/src/test/java/com/commercetools/sync/commons/utils/enums/LocalizedEnumValueTestObjects.java @@ -0,0 +1,45 @@ +package com.commercetools.sync.commons.utils.enums; + +import io.sphere.sdk.models.LocalizedEnumValue; + +import java.util.List; + +import static io.sphere.sdk.models.LocalizedString.ofEnglish; +import static java.util.Arrays.asList; + +public final class LocalizedEnumValueTestObjects { + + public static final LocalizedEnumValue ENUM_VALUE_A = LocalizedEnumValue.of("a", ofEnglish("label_a")); + public static final LocalizedEnumValue ENUM_VALUE_A_DIFFERENT_LABEL = + LocalizedEnumValue.of("a", ofEnglish("label_a_diff")); + public static final LocalizedEnumValue ENUM_VALUE_B = LocalizedEnumValue.of("b", ofEnglish("label_b")); + public static final LocalizedEnumValue ENUM_VALUE_C = LocalizedEnumValue.of("c", ofEnglish("label_c")); + public static final LocalizedEnumValue ENUM_VALUE_D = LocalizedEnumValue.of("d", ofEnglish("label_d")); + + public static final List ENUM_VALUES_ABC = asList(ENUM_VALUE_A, ENUM_VALUE_B, ENUM_VALUE_C); + public static final List ENUM_VALUES_AB = asList(ENUM_VALUE_A, ENUM_VALUE_B); + public static final List ENUM_VALUES_ABB = asList(ENUM_VALUE_A, ENUM_VALUE_B, ENUM_VALUE_B); + public static final List ENUM_VALUES_ABD = asList(ENUM_VALUE_A, ENUM_VALUE_B, ENUM_VALUE_D); + public static final List ENUM_VALUES_ABCD = asList( + ENUM_VALUE_A, + ENUM_VALUE_B, + ENUM_VALUE_C, + ENUM_VALUE_D + ); + public static final List ENUM_VALUES_CAB = asList(ENUM_VALUE_C, ENUM_VALUE_A, ENUM_VALUE_B); + public static final List ENUM_VALUES_CB = asList(ENUM_VALUE_C, ENUM_VALUE_B); + public static final List ENUM_VALUES_ACBD = asList( + ENUM_VALUE_A, + ENUM_VALUE_C, + ENUM_VALUE_B, + ENUM_VALUE_D + ); + public static final List ENUM_VALUES_ADBC = asList( + ENUM_VALUE_A, + ENUM_VALUE_D, + ENUM_VALUE_B, + ENUM_VALUE_C + ); + public static final List ENUM_VALUES_CBD = asList(ENUM_VALUE_C, ENUM_VALUE_B, ENUM_VALUE_D); + +} diff --git a/src/test/java/com/commercetools/sync/producttypes/utils/LocalizedEnumValueUpdateActionUtilsTest.java b/src/test/java/com/commercetools/sync/commons/utils/enums/LocalizedEnumValueUpdateActionUtilsTest.java similarity index 82% rename from src/test/java/com/commercetools/sync/producttypes/utils/LocalizedEnumValueUpdateActionUtilsTest.java rename to src/test/java/com/commercetools/sync/commons/utils/enums/LocalizedEnumValueUpdateActionUtilsTest.java index 779653b0e1..3614055e76 100644 --- a/src/test/java/com/commercetools/sync/producttypes/utils/LocalizedEnumValueUpdateActionUtilsTest.java +++ b/src/test/java/com/commercetools/sync/commons/utils/enums/LocalizedEnumValueUpdateActionUtilsTest.java @@ -1,4 +1,4 @@ -package com.commercetools.sync.producttypes.utils; +package com.commercetools.sync.commons.utils.enums; import io.sphere.sdk.commands.UpdateAction; import io.sphere.sdk.models.LocalizedEnumValue; @@ -9,7 +9,8 @@ import java.util.Optional; -import static com.commercetools.sync.producttypes.utils.LocalizedEnumUpdateActionUtils.buildChangeLabelAction; + +import static com.commercetools.sync.commons.utils.enums.LocalizedEnumValueUpdateActionUtils.buildChangeLabelAction; import static org.assertj.core.api.Assertions.assertThat; public class LocalizedEnumValueUpdateActionUtilsTest { @@ -22,7 +23,8 @@ public void buildChangeLabelAction_WithDifferentValues_ShouldReturnAction() { final Optional> result = buildChangeLabelAction( "attribute_definition_name_1", old, - newDifferent + newDifferent, + ChangeLocalizedEnumValueLabel::of ); assertThat(result).contains(ChangeLocalizedEnumValueLabel.of("attribute_definition_name_1", newDifferent)); @@ -33,7 +35,8 @@ public void buildChangeLabelAction_WithSameValues_ShouldReturnEmptyOptional() { final Optional> result = buildChangeLabelAction( "attribute_definition_name_1", old, - newSame + newSame, + ChangeLocalizedEnumValueLabel::of ); assertThat(result).isEmpty(); diff --git a/src/test/java/com/commercetools/sync/commons/utils/enums/PlainEnumValueTestObjects.java b/src/test/java/com/commercetools/sync/commons/utils/enums/PlainEnumValueTestObjects.java new file mode 100644 index 0000000000..5b99e05433 --- /dev/null +++ b/src/test/java/com/commercetools/sync/commons/utils/enums/PlainEnumValueTestObjects.java @@ -0,0 +1,45 @@ +package com.commercetools.sync.commons.utils.enums; + +import io.sphere.sdk.models.EnumValue; + +import java.util.List; + +import static java.util.Arrays.asList; + +public final class PlainEnumValueTestObjects { + + public static final EnumValue ENUM_VALUE_A = EnumValue.of("a", "label_a"); + public static final EnumValue ENUM_VALUE_A_DIFFERENT_LABEL = EnumValue.of("a", "label_a_diff"); + public static final EnumValue ENUM_VALUE_B = EnumValue.of("b", "label_b"); + public static final EnumValue ENUM_VALUE_C = EnumValue.of("c", "label_c"); + public static final EnumValue ENUM_VALUE_D = EnumValue.of("d", "label_d"); + + public static final List ENUM_VALUES_ABC = asList(ENUM_VALUE_A, ENUM_VALUE_B, ENUM_VALUE_C); + public static final List ENUM_VALUES_AB = asList(ENUM_VALUE_A, ENUM_VALUE_B); + public static final List ENUM_VALUES_AB_WITH_DIFFERENT_LABEL = + asList(ENUM_VALUE_A_DIFFERENT_LABEL, ENUM_VALUE_B); + public static final List ENUM_VALUES_ABB = asList(ENUM_VALUE_A, ENUM_VALUE_B, ENUM_VALUE_B); + public static final List ENUM_VALUES_ABD = asList(ENUM_VALUE_A, ENUM_VALUE_B, ENUM_VALUE_D); + public static final List ENUM_VALUES_ABCD = asList( + ENUM_VALUE_A, + ENUM_VALUE_B, + ENUM_VALUE_C, + ENUM_VALUE_D + ); + public static final List ENUM_VALUES_CAB = asList(ENUM_VALUE_C, ENUM_VALUE_A, ENUM_VALUE_B); + public static final List ENUM_VALUES_CB = asList(ENUM_VALUE_C, ENUM_VALUE_B); + public static final List ENUM_VALUES_ACBD = asList( + ENUM_VALUE_A, + ENUM_VALUE_C, + ENUM_VALUE_B, + ENUM_VALUE_D + ); + public static final List ENUM_VALUES_ADBC = asList( + ENUM_VALUE_A, + ENUM_VALUE_D, + ENUM_VALUE_B, + ENUM_VALUE_C + ); + public static final List ENUM_VALUES_CBD = asList(ENUM_VALUE_C, ENUM_VALUE_B, ENUM_VALUE_D); + +} diff --git a/src/test/java/com/commercetools/sync/producttypes/utils/PlainEnumValueUpdateActionUtilsTest.java b/src/test/java/com/commercetools/sync/commons/utils/enums/PlainEnumValueUpdateActionUtilsTest.java similarity index 83% rename from src/test/java/com/commercetools/sync/producttypes/utils/PlainEnumValueUpdateActionUtilsTest.java rename to src/test/java/com/commercetools/sync/commons/utils/enums/PlainEnumValueUpdateActionUtilsTest.java index 2a265d7980..70f9f0d338 100644 --- a/src/test/java/com/commercetools/sync/producttypes/utils/PlainEnumValueUpdateActionUtilsTest.java +++ b/src/test/java/com/commercetools/sync/commons/utils/enums/PlainEnumValueUpdateActionUtilsTest.java @@ -1,4 +1,4 @@ -package com.commercetools.sync.producttypes.utils; +package com.commercetools.sync.commons.utils.enums; import io.sphere.sdk.commands.UpdateAction; import io.sphere.sdk.models.EnumValue; @@ -9,7 +9,7 @@ import java.util.Optional; -import static com.commercetools.sync.producttypes.utils.PlainEnumUpdateActionUtils.buildChangeLabelAction; +import static com.commercetools.sync.commons.utils.enums.PlainEnumValueUpdateActionUtils.buildChangeLabelAction; import static org.assertj.core.api.Assertions.assertThat; public class PlainEnumValueUpdateActionUtilsTest { @@ -32,7 +32,8 @@ public void buildChangeLabelAction_WithDifferentValues_ShouldReturnAction() { final Optional> result = buildChangeLabelAction( "attribute_definition_name_1", old, - newDifferent + newDifferent, + ChangePlainEnumValueLabel::of ); assertThat(result).containsInstanceOf(ChangePlainEnumValueLabel.class); @@ -44,7 +45,8 @@ public void buildChangeLabelAction_WithSameValues_ShouldReturnEmptyOptional() { final Optional> result = buildChangeLabelAction( "attribute_definition_name_1", old, - newSame + newSame, + ChangePlainEnumValueLabel::of ); assertThat(result).isEmpty(); diff --git a/src/test/java/com/commercetools/sync/producttypes/utils/producttypeactionutils/BuildLocalizedEnumUpdateActionsTest.java b/src/test/java/com/commercetools/sync/producttypes/utils/producttypeactionutils/BuildLocalizedEnumUpdateActionsTest.java index 398e672fec..a4695bd1ee 100644 --- a/src/test/java/com/commercetools/sync/producttypes/utils/producttypeactionutils/BuildLocalizedEnumUpdateActionsTest.java +++ b/src/test/java/com/commercetools/sync/producttypes/utils/producttypeactionutils/BuildLocalizedEnumUpdateActionsTest.java @@ -2,9 +2,9 @@ import com.commercetools.sync.commons.exceptions.DuplicateKeyException; import io.sphere.sdk.commands.UpdateAction; -import io.sphere.sdk.models.LocalizedEnumValue; import io.sphere.sdk.producttypes.ProductType; import io.sphere.sdk.producttypes.commands.updateactions.AddLocalizedEnumValue; +import io.sphere.sdk.producttypes.commands.updateactions.ChangeLocalizedEnumValueLabel; import io.sphere.sdk.producttypes.commands.updateactions.ChangeLocalizedEnumValueOrder; import io.sphere.sdk.producttypes.commands.updateactions.RemoveEnumValues; import org.junit.Rule; @@ -14,42 +14,13 @@ import java.util.Collections; import java.util.List; -import static com.commercetools.sync.producttypes.utils.LocalizedEnumsUpdateActionUtils.buildLocalizedEnumValuesUpdateActions; -import static io.sphere.sdk.models.LocalizedString.ofEnglish; +import static com.commercetools.sync.commons.utils.enums.LocalizedEnumValueTestObjects.*; +import static com.commercetools.sync.producttypes.utils.LocalizedEnumValueUpdateActionUtils.buildLocalizedEnumValueUpdateActions; +import static com.commercetools.sync.producttypes.utils.LocalizedEnumValueUpdateActionUtils.buildLocalizedEnumValuesUpdateActions; import static java.util.Arrays.asList; import static org.assertj.core.api.Assertions.assertThat; public class BuildLocalizedEnumUpdateActionsTest { - private static final LocalizedEnumValue ENUM_VALUE_A = LocalizedEnumValue.of("a", ofEnglish("label_a")); - private static final LocalizedEnumValue ENUM_VALUE_B = LocalizedEnumValue.of("b", ofEnglish("label_b")); - private static final LocalizedEnumValue ENUM_VALUE_C = LocalizedEnumValue.of("c", ofEnglish("label_c")); - private static final LocalizedEnumValue ENUM_VALUE_D = LocalizedEnumValue.of("d", ofEnglish("label_d")); - - private static final List ENUM_VALUES_ABC = asList(ENUM_VALUE_A, ENUM_VALUE_B, ENUM_VALUE_C); - private static final List ENUM_VALUES_AB = asList(ENUM_VALUE_A, ENUM_VALUE_B); - private static final List ENUM_VALUES_ABB = asList(ENUM_VALUE_A, ENUM_VALUE_B, ENUM_VALUE_B); - private static final List ENUM_VALUES_ABD = asList(ENUM_VALUE_A, ENUM_VALUE_B, ENUM_VALUE_D); - private static final List ENUM_VALUES_ABCD = asList( - ENUM_VALUE_A, - ENUM_VALUE_B, - ENUM_VALUE_C, - ENUM_VALUE_D - ); - private static final List ENUM_VALUES_CAB = asList(ENUM_VALUE_C, ENUM_VALUE_A, ENUM_VALUE_B); - private static final List ENUM_VALUES_CB = asList(ENUM_VALUE_C, ENUM_VALUE_B); - private static final List ENUM_VALUES_ACBD = asList( - ENUM_VALUE_A, - ENUM_VALUE_C, - ENUM_VALUE_B, - ENUM_VALUE_D - ); - private static final List ENUM_VALUES_ADBC = asList( - ENUM_VALUE_A, - ENUM_VALUE_D, - ENUM_VALUE_B, - ENUM_VALUE_C - ); - private static final List ENUM_VALUES_CBD = asList(ENUM_VALUE_C, ENUM_VALUE_B, ENUM_VALUE_D); @Test public void buildLocalizedEnumUpdateActions_WithNullNewEnumValuesAndExistingEnumValues_ShouldBuildRemoveAction() { @@ -121,9 +92,9 @@ public void buildLocalizedEnumUpdateActions_WithIdenticalPlainEnum_ShouldNotBuil @Test public void buildLocalizedEnumUpdateActions_WithDuplicatePlainEnumValues_ShouldTriggerDuplicateKeyError() { expectedException.expect(DuplicateKeyException.class); - expectedException.expectMessage("Enum Values have duplicated keys. Attribute definition name: " + expectedException.expectMessage("Enum Values have duplicated keys. Definition name: " + "'attribute_definition_name_1', Duplicated enum value: 'b'. Enum Values are expected to be unique inside " - + "their attribute definition."); + + "their definition."); buildLocalizedEnumValuesUpdateActions( "attribute_definition_name_1", @@ -263,4 +234,31 @@ public void buildLocalizedEnumUpdateActions_WithAddedRemovedAndDifOrder_ShouldBu )) ); } + + @Test + public void buildLocalizedEnumUpdateActions_WithDifferentLabels_ShouldReturnChangeLabelAction() { + final List> updateActions = buildLocalizedEnumValueUpdateActions( + "attribute_definition_name_1", + ENUM_VALUE_A, + ENUM_VALUE_A_DIFFERENT_LABEL + ); + + assertThat(updateActions).containsAnyOf( + ChangeLocalizedEnumValueLabel.of("attribute_definition_name_1", ENUM_VALUE_A_DIFFERENT_LABEL) + ); + } + + + @Test + public void buildLocalizedEnumUpdateActions_WithSameLabels_ShouldNotReturnChangeLabelAction() { + final List> updateActions = buildLocalizedEnumValueUpdateActions( + "attribute_definition_name_1", + ENUM_VALUE_A, + ENUM_VALUE_A + ); + + assertThat(updateActions).doesNotContain( + ChangeLocalizedEnumValueLabel.of("attribute_definition_name_1", ENUM_VALUE_A) + ); + } } diff --git a/src/test/java/com/commercetools/sync/producttypes/utils/producttypeactionutils/BuildPlainEnumUpdateActionsTest.java b/src/test/java/com/commercetools/sync/producttypes/utils/producttypeactionutils/BuildPlainEnumUpdateActionsTest.java index b60ddfa271..aed3b5e475 100644 --- a/src/test/java/com/commercetools/sync/producttypes/utils/producttypeactionutils/BuildPlainEnumUpdateActionsTest.java +++ b/src/test/java/com/commercetools/sync/producttypes/utils/producttypeactionutils/BuildPlainEnumUpdateActionsTest.java @@ -2,10 +2,10 @@ import com.commercetools.sync.commons.exceptions.DuplicateKeyException; import io.sphere.sdk.commands.UpdateAction; -import io.sphere.sdk.models.EnumValue; import io.sphere.sdk.producttypes.ProductType; import io.sphere.sdk.producttypes.commands.updateactions.AddEnumValue; import io.sphere.sdk.producttypes.commands.updateactions.ChangeEnumValueOrder; +import io.sphere.sdk.producttypes.commands.updateactions.ChangePlainEnumValueLabel; import io.sphere.sdk.producttypes.commands.updateactions.RemoveEnumValues; import org.junit.Rule; import org.junit.Test; @@ -14,43 +14,14 @@ import java.util.Collections; import java.util.List; -import static com.commercetools.sync.producttypes.utils.PlainEnumsUpdateActionUtils.buildEnumValuesUpdateActions; +import static com.commercetools.sync.commons.utils.enums.PlainEnumValueTestObjects.*; +import static com.commercetools.sync.producttypes.utils.PlainEnumValueUpdateActionUtils.buildEnumValueUpdateActions; +import static com.commercetools.sync.producttypes.utils.PlainEnumValueUpdateActionUtils.buildEnumValuesUpdateActions; import static java.util.Arrays.asList; import static org.assertj.core.api.Assertions.assertThat; public class BuildPlainEnumUpdateActionsTest { - private static final EnumValue ENUM_VALUE_A = EnumValue.of("a", "label_a"); - private static final EnumValue ENUM_VALUE_B = EnumValue.of("b", "label_b"); - private static final EnumValue ENUM_VALUE_C = EnumValue.of("c", "label_c"); - private static final EnumValue ENUM_VALUE_D = EnumValue.of("d", "label_d"); - - private static final List ENUM_VALUES_ABC = asList(ENUM_VALUE_A, ENUM_VALUE_B, ENUM_VALUE_C); - private static final List ENUM_VALUES_AB = asList(ENUM_VALUE_A, ENUM_VALUE_B); - private static final List ENUM_VALUES_ABB = asList(ENUM_VALUE_A, ENUM_VALUE_B, ENUM_VALUE_B); - private static final List ENUM_VALUES_ABD = asList(ENUM_VALUE_A, ENUM_VALUE_B, ENUM_VALUE_D); - private static final List ENUM_VALUES_ABCD = asList( - ENUM_VALUE_A, - ENUM_VALUE_B, - ENUM_VALUE_C, - ENUM_VALUE_D - ); - private static final List ENUM_VALUES_CAB = asList(ENUM_VALUE_C, ENUM_VALUE_A, ENUM_VALUE_B); - private static final List ENUM_VALUES_CB = asList(ENUM_VALUE_C, ENUM_VALUE_B); - private static final List ENUM_VALUES_ACBD = asList( - ENUM_VALUE_A, - ENUM_VALUE_C, - ENUM_VALUE_B, - ENUM_VALUE_D - ); - private static final List ENUM_VALUES_ADBC = asList( - ENUM_VALUE_A, - ENUM_VALUE_D, - ENUM_VALUE_B, - ENUM_VALUE_C - ); - private static final List ENUM_VALUES_CBD = asList(ENUM_VALUE_C, ENUM_VALUE_B, ENUM_VALUE_D); - @Test public void buildPlainEnumUpdateActions_WithNullNewEnumValuesAndExistingEnumValues_ShouldBuildRemoveAction() { final List> updateActions = buildEnumValuesUpdateActions( @@ -263,4 +234,33 @@ public void buildPlainEnumUpdateActions_WithAddedRemovedAndDifOrder_ShouldBuildA )) ); } + + @Test + public void buildPlainEnumUpdateActions_WithDifferentLabels_ShouldReturnChangeLabelAction() { + final List> updateActions = buildEnumValueUpdateActions( + "attribute_definition_name_1", + ENUM_VALUE_A, + ENUM_VALUE_A_DIFFERENT_LABEL + ); + + assertThat(updateActions).containsAnyOf( + ChangePlainEnumValueLabel.of("attribute_definition_name_1", ENUM_VALUE_A_DIFFERENT_LABEL) + ); + } + + + @Test + public void buildPlainEnumUpdateActions_WithSameLabels_ShouldNotReturnChangeLabelAction() { + final List> updateActions = buildEnumValueUpdateActions( + "attribute_definition_name_1", + ENUM_VALUE_A, + ENUM_VALUE_A + ); + + assertThat(updateActions).doesNotContain( + ChangePlainEnumValueLabel.of("attribute_definition_name_1", ENUM_VALUE_A) + ); + } + + } diff --git a/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildLocalizedEnumUpdateActionsTest.java b/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildLocalizedEnumUpdateActionsTest.java index 0cd7c29859..b232e27709 100644 --- a/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildLocalizedEnumUpdateActionsTest.java +++ b/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildLocalizedEnumUpdateActionsTest.java @@ -3,7 +3,6 @@ import com.commercetools.sync.commons.exceptions.DuplicateKeyException; import com.commercetools.sync.types.utils.LocalizedEnumUpdateActionUtils; import io.sphere.sdk.commands.UpdateAction; -import io.sphere.sdk.models.LocalizedEnumValue; import io.sphere.sdk.types.Type; import io.sphere.sdk.types.commands.updateactions.AddLocalizedEnumValue; import io.sphere.sdk.types.commands.updateactions.ChangeLocalizedEnumValueOrder; @@ -14,44 +13,14 @@ import java.util.Collections; import java.util.List; +import static com.commercetools.sync.commons.utils.enums.LocalizedEnumValueTestObjects.*; import static com.commercetools.sync.types.utils.LocalizedEnumUpdateActionUtils.buildLocalizedEnumValuesUpdateActions; -import static io.sphere.sdk.models.LocalizedString.ofEnglish; import static java.util.Arrays.asList; import static org.assertj.core.api.Assertions.assertThat; public class BuildLocalizedEnumUpdateActionsTest { private static final String FIELD_NAME_1 = "field1"; - private static final LocalizedEnumValue ENUM_VALUE_A = LocalizedEnumValue.of("a", ofEnglish("label_a")); - private static final LocalizedEnumValue ENUM_VALUE_B = LocalizedEnumValue.of("b", ofEnglish("label_b")); - private static final LocalizedEnumValue ENUM_VALUE_C = LocalizedEnumValue.of("c", ofEnglish("label_c")); - private static final LocalizedEnumValue ENUM_VALUE_D = LocalizedEnumValue.of("d", ofEnglish("label_d")); - - private static final List ENUM_VALUES_ABC = asList(ENUM_VALUE_A, ENUM_VALUE_B, ENUM_VALUE_C); - private static final List ENUM_VALUES_ABD = asList(ENUM_VALUE_A, ENUM_VALUE_B, ENUM_VALUE_D); - private static final List ENUM_VALUES_ABB = asList(ENUM_VALUE_A, ENUM_VALUE_B, ENUM_VALUE_B); - private static final List ENUM_VALUES_ABCD = asList( - ENUM_VALUE_A, - ENUM_VALUE_B, - ENUM_VALUE_C, - ENUM_VALUE_D - ); - private static final List ENUM_VALUES_CAB = asList(ENUM_VALUE_C, ENUM_VALUE_A, ENUM_VALUE_B); - private static final List ENUM_VALUES_CB = asList(ENUM_VALUE_C, ENUM_VALUE_B); - private static final List ENUM_VALUES_ACBD = asList( - ENUM_VALUE_A, - ENUM_VALUE_C, - ENUM_VALUE_B, - ENUM_VALUE_D - ); - private static final List ENUM_VALUES_ADBC = asList( - ENUM_VALUE_A, - ENUM_VALUE_D, - ENUM_VALUE_B, - ENUM_VALUE_C - ); - private static final List ENUM_VALUES_CBD = asList(ENUM_VALUE_C, ENUM_VALUE_B, ENUM_VALUE_D); - @Rule public ExpectedException expectedException = ExpectedException.none(); @@ -214,9 +183,9 @@ public void buildLocalizedEnumUpdateActions_WithAddedRemovedAndDifOrder_ShouldBu @Test public void buildLocalizedEnumUpdateActions_WithDuplicatePlainEnumValues_ShouldTriggerDuplicateKeyError() { expectedException.expect(DuplicateKeyException.class); - expectedException.expectMessage("Enum Values have duplicated keys. Field definition name: " + expectedException.expectMessage("Enum Values have duplicated keys. Definition name: " + "'field_definition_name', Duplicated enum value: 'b'. Enum Values are expected to be unique inside " - + "their field definition."); + + "their definition."); LocalizedEnumUpdateActionUtils.buildLocalizedEnumValuesUpdateActions( "field_definition_name", diff --git a/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildPlainEnumUpdateActionsTest.java b/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildPlainEnumUpdateActionsTest.java index d956090acc..dd4f0a2b74 100644 --- a/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildPlainEnumUpdateActionsTest.java +++ b/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildPlainEnumUpdateActionsTest.java @@ -1,7 +1,6 @@ package com.commercetools.sync.types.utils.typeactionutils; import io.sphere.sdk.commands.UpdateAction; -import io.sphere.sdk.models.EnumValue; import io.sphere.sdk.types.Type; import io.sphere.sdk.types.commands.updateactions.AddEnumValue; import io.sphere.sdk.types.commands.updateactions.ChangeEnumValueOrder; @@ -10,44 +9,14 @@ import java.util.Collections; import java.util.List; -import static com.commercetools.sync.types.utils.PlainEnumUpdateActionUtils.buildEnumValuesUpdateActions; +import static com.commercetools.sync.commons.utils.enums.PlainEnumValueTestObjects.*; +import static com.commercetools.sync.types.utils.PlainEnumValueUpdateActionUtils.buildEnumValuesUpdateActions; import static java.util.Arrays.asList; import static org.assertj.core.api.Assertions.assertThat; public class BuildPlainEnumUpdateActionsTest { private static final String FIELD_NAME_1 = "field1"; - private static final EnumValue ENUM_VALUE_A = EnumValue.of("a", "label_a"); - private static final EnumValue ENUM_VALUE_B = EnumValue.of("b", "label_b"); - private static final EnumValue ENUM_VALUE_C = EnumValue.of("c", "label_c"); - private static final EnumValue ENUM_VALUE_D = EnumValue.of("d", "label_d"); - - private static final List ENUM_VALUES_ABC = asList(ENUM_VALUE_A, ENUM_VALUE_B, ENUM_VALUE_C); - private static final List ENUM_VALUES_ABD = asList(ENUM_VALUE_A, ENUM_VALUE_B, ENUM_VALUE_D); - private static final List ENUM_VALUES_ABCD = asList( - ENUM_VALUE_A, - ENUM_VALUE_B, - ENUM_VALUE_C, - ENUM_VALUE_D - ); - private static final List ENUM_VALUES_CAB = asList(ENUM_VALUE_C, ENUM_VALUE_A, ENUM_VALUE_B); - private static final List ENUM_VALUES_CB = asList(ENUM_VALUE_C, ENUM_VALUE_B); - private static final List ENUM_VALUES_ACBD = asList( - ENUM_VALUE_A, - ENUM_VALUE_C, - ENUM_VALUE_B, - ENUM_VALUE_D - ); - private static final List ENUM_VALUES_ADBC = asList( - ENUM_VALUE_A, - ENUM_VALUE_D, - ENUM_VALUE_B, - ENUM_VALUE_C - ); - private static final List ENUM_VALUES_CBD = asList(ENUM_VALUE_C, ENUM_VALUE_B, ENUM_VALUE_D); - - - @Test public void buildPlainEnumUpdateActions_WithEmptyPlainEnumValuesAndNoOlEnumValues_ShouldNotBuildActions() { final List> updateActions = buildEnumValuesUpdateActions( From 33b303969a7535245485bba3d4b6f7da94606c09 Mon Sep 17 00:00:00 2001 From: aoz Date: Thu, 8 Nov 2018 10:12:03 +0100 Subject: [PATCH 038/164] #300 - refactor, move enum update actions on buildActions to buildEnumUpdateActions --- .../AttributeDefinitionUpdateActionUtils.java | 34 +++++++------------ 1 file changed, 13 insertions(+), 21 deletions(-) diff --git a/src/main/java/com/commercetools/sync/producttypes/utils/AttributeDefinitionUpdateActionUtils.java b/src/main/java/com/commercetools/sync/producttypes/utils/AttributeDefinitionUpdateActionUtils.java index 341bd596b3..eb7e8f2d11 100644 --- a/src/main/java/com/commercetools/sync/producttypes/utils/AttributeDefinitionUpdateActionUtils.java +++ b/src/main/java/com/commercetools/sync/producttypes/utils/AttributeDefinitionUpdateActionUtils.java @@ -1,7 +1,5 @@ package com.commercetools.sync.producttypes.utils; -import com.commercetools.sync.commons.exceptions.BuildUpdateActionException; -import com.commercetools.sync.producttypes.helpers.AttributeTypeAssert; import io.sphere.sdk.commands.UpdateAction; import io.sphere.sdk.models.EnumValue; import io.sphere.sdk.models.LocalizedEnumValue; @@ -40,22 +38,21 @@ final class AttributeDefinitionUpdateActionUtils { * @param oldAttributeDefinition the old attribute definition which should be updated. * @param newAttributeDefinitionDraft the new attribute definition draft where we get the new fields. * @return A list with the update actions or an empty list if the attribute definition fields are identical. - * @throws BuildUpdateActionException in case there are attribute definitions with the null attribute type. */ @Nonnull static List> buildActions( @Nonnull final AttributeDefinition oldAttributeDefinition, - @Nonnull final AttributeDefinitionDraft newAttributeDefinition) throws BuildUpdateActionException { + @Nonnull final AttributeDefinitionDraft newAttributeDefinitionDraft) { final List> updateActions = filterEmptyOptionals( - buildChangeLabelUpdateAction(oldAttributeDefinition, newAttributeDefinition), - buildSetInputTipUpdateAction(oldAttributeDefinition, newAttributeDefinition), - buildChangeIsSearchableUpdateAction(oldAttributeDefinition, newAttributeDefinition), - buildChangeInputHintUpdateAction(oldAttributeDefinition, newAttributeDefinition), - buildChangeAttributeConstraintUpdateAction(oldAttributeDefinition, newAttributeDefinition) + buildChangeLabelUpdateAction(oldAttributeDefinition, newAttributeDefinitionDraft), + buildSetInputTipUpdateAction(oldAttributeDefinition, newAttributeDefinitionDraft), + buildChangeIsSearchableUpdateAction(oldAttributeDefinition, newAttributeDefinitionDraft), + buildChangeInputHintUpdateAction(oldAttributeDefinition, newAttributeDefinitionDraft), + buildChangeAttributeConstraintUpdateAction(oldAttributeDefinition, newAttributeDefinitionDraft) ); - updateActions.addAll(buildEnumUpdateActions(oldAttributeDefinition, newAttributeDefinition)); + updateActions.addAll(buildEnumUpdateActions(oldAttributeDefinition, newAttributeDefinitionDraft)); return updateActions; } @@ -65,32 +62,27 @@ static List> buildActions( * result. If both the {@link AttributeDefinition} and the {@link AttributeDefinitionDraft} have identical * enum values, then no update action is needed and hence an empty {@link List} is returned. * - * @param oldAttributeDefinition the attribute definition which should be updated. - * @param newAttributeDefinition the new attribute definition draft where we get the new fields. + * @param oldAttributeDefinition the attribute definition which should be updated. + * @param newAttributeDefinitionDraft the new attribute definition draft where we get the new fields. * @return A list with the update actions or an empty list if the attribute definition enums are identical. - * @throws BuildUpdateActionException in case there are attribute definitions with the null attribute type. */ @Nonnull - public static List> buildEnumUpdateActions( + static List> buildEnumUpdateActions( @Nonnull final AttributeDefinition oldAttributeDefinition, - @Nonnull final AttributeDefinitionDraft newAttributeDefinition) throws BuildUpdateActionException { - - AttributeTypeAssert.assertTypesAreNull( - oldAttributeDefinition.getAttributeType(), - newAttributeDefinition.getAttributeType()); + @Nonnull final AttributeDefinitionDraft newAttributeDefinitionDraft) { final List> updateActions = new ArrayList<>(); if (isPlainEnumAttribute(oldAttributeDefinition)) { updateActions.addAll(buildEnumValuesUpdateActions( oldAttributeDefinition.getName(), ((EnumAttributeType) oldAttributeDefinition.getAttributeType()).getValues(), - ((EnumAttributeType) newAttributeDefinition.getAttributeType()).getValues() + ((EnumAttributeType) newAttributeDefinitionDraft.getAttributeType()).getValues() )); } else if (isLocalizedEnumAttribute(oldAttributeDefinition)) { updateActions.addAll(buildLocalizedEnumValuesUpdateActions( oldAttributeDefinition.getName(), ((LocalizedEnumAttributeType) oldAttributeDefinition.getAttributeType()).getValues(), - ((LocalizedEnumAttributeType) newAttributeDefinition.getAttributeType()).getValues() + ((LocalizedEnumAttributeType) newAttributeDefinitionDraft.getAttributeType()).getValues() )); } From 11a32b1a4eacd8b313b89e3061592945a027d1e1 Mon Sep 17 00:00:00 2001 From: aoz Date: Fri, 9 Nov 2018 13:59:21 +0100 Subject: [PATCH 039/164] #300 - make field definition build actions internal --- .../enums/EnumValuesUpdateActionUtils.java | 2 +- .../sync/types/helpers/FieldTypeAssert.java | 55 ------------- .../FieldDefinitionUpdateActionUtils.java | 23 +++--- .../FieldDefinitionsUpdateActionUtils.java | 72 +++++++++-------- ... LocalizedEnumValueUpdateActionUtils.java} | 4 +- .../BuildLocalizedEnumUpdateActionsTest.java | 3 - .../FieldDefinitionUpdateActionUtilsTest.java | 78 ++----------------- ...BuildFieldDefinitionUpdateActionsTest.java | 36 --------- .../BuildLocalizedEnumUpdateActionsTest.java | 7 +- ...ld-definitions-abc-without-field-type.json | 38 --------- 10 files changed, 60 insertions(+), 258 deletions(-) delete mode 100644 src/main/java/com/commercetools/sync/types/helpers/FieldTypeAssert.java rename src/main/java/com/commercetools/sync/types/utils/{LocalizedEnumUpdateActionUtils.java => LocalizedEnumValueUpdateActionUtils.java} (95%) delete mode 100644 src/test/resources/com/commercetools/sync/types/utils/updatefielddefinitions/fields/type-with-field-definitions-abc-without-field-type.json diff --git a/src/main/java/com/commercetools/sync/commons/utils/enums/EnumValuesUpdateActionUtils.java b/src/main/java/com/commercetools/sync/commons/utils/enums/EnumValuesUpdateActionUtils.java index 1e5fca24c0..a827aac909 100644 --- a/src/main/java/com/commercetools/sync/commons/utils/enums/EnumValuesUpdateActionUtils.java +++ b/src/main/java/com/commercetools/sync/commons/utils/enums/EnumValuesUpdateActionUtils.java @@ -258,7 +258,7 @@ public static Optional> buildChangeEnumVa return buildUpdateAction( keysPair.getLeft(), //all keys keysPair.getRight(), // new keys - () -> changeOrderEnumCallback.apply(definitionName, keysPair.getLeft()) + () -> changeOrderEnumCallback.apply(definitionName, keysPair.getRight()) ); } diff --git a/src/main/java/com/commercetools/sync/types/helpers/FieldTypeAssert.java b/src/main/java/com/commercetools/sync/types/helpers/FieldTypeAssert.java deleted file mode 100644 index 777efc1ba7..0000000000 --- a/src/main/java/com/commercetools/sync/types/helpers/FieldTypeAssert.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.commercetools.sync.types.helpers; - -import com.commercetools.sync.commons.exceptions.BuildUpdateActionException; -import io.sphere.sdk.types.FieldType; - - -public final class FieldTypeAssert { - - /** - * A utility method validates an old {@link FieldType} and a new {@link FieldType} - * and if the value of types {@code null}, {@link BuildUpdateActionException} will be thrown. - * - * @param oldFieldType the old field type. - * @param newFieldType the new field type. - */ - public static void assertTypesAreNull(final FieldType oldFieldType, - final FieldType newFieldType) - throws BuildUpdateActionException { - - if (oldFieldType == null && newFieldType == null) { - throw new BuildUpdateActionException("Field types are not set " - + "for both the old and new field definitions."); - } - - assertOldFieldType(oldFieldType); - assertNewFieldType(newFieldType); - } - - /** - * A utility method validates old {@link FieldType} and - * if the value of type {@code null}, {@link BuildUpdateActionException} will be thrown. - * - * @param oldFieldType the old field type. - */ - public static void assertOldFieldType(final FieldType oldFieldType) throws BuildUpdateActionException { - if (oldFieldType == null) { - throw new BuildUpdateActionException("Field type is not set for the old field definition."); - } - } - - /** - * A utility method validates new {@link FieldType} and - * if the value of type {@code null}, {@link BuildUpdateActionException} will be thrown. - * - * @param newFieldType the new field type. - */ - public static void assertNewFieldType(final FieldType newFieldType) throws BuildUpdateActionException { - if (newFieldType == null) { - throw new BuildUpdateActionException("Field type is not set for the new field definition."); - } - } - - private FieldTypeAssert() { - } -} \ No newline at end of file diff --git a/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionUpdateActionUtils.java b/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionUpdateActionUtils.java index 834e642b1d..59fafc312b 100644 --- a/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionUpdateActionUtils.java +++ b/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionUpdateActionUtils.java @@ -1,7 +1,5 @@ package com.commercetools.sync.types.utils; -import com.commercetools.sync.commons.exceptions.BuildUpdateActionException; -import com.commercetools.sync.types.helpers.FieldTypeAssert; import io.sphere.sdk.commands.UpdateAction; import io.sphere.sdk.models.EnumValue; import io.sphere.sdk.models.LocalizedEnumValue; @@ -19,11 +17,13 @@ import static com.commercetools.sync.commons.utils.CommonTypeUpdateActionUtils.buildUpdateAction; import static com.commercetools.sync.commons.utils.OptionalUtils.filterEmptyOptionals; -import static com.commercetools.sync.types.utils.LocalizedEnumUpdateActionUtils.buildLocalizedEnumValuesUpdateActions; +import static com.commercetools.sync.types.utils.LocalizedEnumValueUpdateActionUtils.buildLocalizedEnumValuesUpdateActions; import static com.commercetools.sync.types.utils.PlainEnumValueUpdateActionUtils.buildEnumValuesUpdateActions; - -public final class FieldDefinitionUpdateActionUtils { +/** + * This class is only meant for the internal use of the commercetools-sync-java library. + */ +final class FieldDefinitionUpdateActionUtils { /** * Compares all the fields of old {@link FieldDefinition} with new {@link FieldDefinition} and returns @@ -34,12 +34,11 @@ public final class FieldDefinitionUpdateActionUtils { * @param oldFieldDefinition the old field definition which should be updated. * @param newFieldDefinition the new field definition where we get the new fields. * @return A list with the update actions or an empty list if the field definition fields are identical. - * @throws BuildUpdateActionException in case there are field definitions with the null field type. */ @Nonnull - public static List> buildActions( + static List> buildActions( @Nonnull final FieldDefinition oldFieldDefinition, - @Nonnull final FieldDefinition newFieldDefinition) throws BuildUpdateActionException { + @Nonnull final FieldDefinition newFieldDefinition) { final List> updateActions = filterEmptyOptionals(buildChangeLabelUpdateAction(oldFieldDefinition, newFieldDefinition)); @@ -57,14 +56,12 @@ public static List> buildActions( * @param oldFieldDefinition the old field definition which should be updated. * @param newFieldDefinition the new field definition where we get the new fields. * @return A list with the update actions or an empty list if the field definition enums are identical. - * @throws BuildUpdateActionException in case there are attribute definitions with the null attribute type. */ @Nonnull - public static List> buildEnumUpdateActions( + static List> buildEnumUpdateActions( @Nonnull final FieldDefinition oldFieldDefinition, - @Nonnull final FieldDefinition newFieldDefinition) throws BuildUpdateActionException { + @Nonnull final FieldDefinition newFieldDefinition) { - FieldTypeAssert.assertTypesAreNull(oldFieldDefinition.getType(), newFieldDefinition.getType()); final List> updateActions = new ArrayList<>(); if (isPlainEnumField(oldFieldDefinition)) { updateActions.addAll(buildEnumValuesUpdateActions( @@ -113,7 +110,7 @@ private static boolean isLocalizedEnumField(@Nonnull final FieldDefinition field * @return A filled optional with the update action or an empty optional if the labels are identical. */ @Nonnull - public static Optional> buildChangeLabelUpdateAction( + static Optional> buildChangeLabelUpdateAction( @Nonnull final FieldDefinition oldFieldDefinition, @Nonnull final FieldDefinition newFieldDefinition) { diff --git a/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionsUpdateActionUtils.java b/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionsUpdateActionUtils.java index c725f1ecd2..a6064828ac 100644 --- a/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionsUpdateActionUtils.java +++ b/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionsUpdateActionUtils.java @@ -3,7 +3,6 @@ import com.commercetools.sync.commons.exceptions.BuildUpdateActionException; import com.commercetools.sync.commons.exceptions.DuplicateKeyException; import com.commercetools.sync.commons.exceptions.DuplicateNameException; -import com.commercetools.sync.types.helpers.FieldTypeAssert; import io.sphere.sdk.commands.UpdateAction; import io.sphere.sdk.types.FieldDefinition; import io.sphere.sdk.types.FieldType; @@ -15,6 +14,8 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Optional; @@ -24,10 +25,15 @@ import static com.commercetools.sync.commons.utils.CommonTypeUpdateActionUtils.buildUpdateAction; import static com.commercetools.sync.types.utils.FieldDefinitionUpdateActionUtils.buildActions; import static java.lang.String.format; +import static java.util.Collections.singletonList; +import static java.util.Optional.ofNullable; import static java.util.stream.Collectors.toList; import static java.util.stream.Collectors.toMap; -public final class FieldDefinitionsUpdateActionUtils { +/** + * This class is only meant for the internal use of the commercetools-sync-java library. + */ +final class FieldDefinitionsUpdateActionUtils { /** * Compares a list of {@link FieldDefinition}s with a list of {@link FieldDefinition}s. @@ -43,11 +49,10 @@ public final class FieldDefinitionsUpdateActionUtils { * @param newFieldDefinitions the new list of field definitions. * @return a list of field definitions update actions if the list of field definitions is not identical. * Otherwise, if the field definitions are identical, an empty list is returned. - * @throws BuildUpdateActionException in case there are field definitions drafts with duplicate names or - * there are field definitions with the null field type. + * @throws DuplicateNameException in case there are field definitions with duplicate names. */ @Nonnull - public static List> buildFieldDefinitionsUpdateActions( + static List> buildFieldDefinitionsUpdateActions( @Nonnull final List oldFieldDefinitions, @Nullable final List newFieldDefinitions) throws BuildUpdateActionException { @@ -74,8 +79,8 @@ public static List> buildFieldDefinitionsUpdateActions( * @param newFieldDefinitions the new list of field definitions drafts. * @return a list of field definitions update actions if the list of field definitions is not identical. * Otherwise, if the field definitions are identical, an empty list is returned. - * @throws BuildUpdateActionException in case there are field definitions with duplicate names or - * there are field definitions with the null field type. + * @throws BuildUpdateActionException in case there are field definitions with duplicate names or enums + * duplicate keys. */ @Nonnull private static List> buildUpdateActions( @@ -137,34 +142,35 @@ private static List> buildRemoveFieldDefinitionOrFieldDefinit } )); - final List> updateActions = new ArrayList<>(); - - for (FieldDefinition oldFieldDefinition : oldFieldDefinitions) { - - FieldTypeAssert.assertOldFieldType(oldFieldDefinition.getType()); - - final String oldFieldDefinitionName = oldFieldDefinition.getName(); - final FieldDefinition matchingNewFieldDefinition = + return oldFieldDefinitions + .stream() + .map(oldFieldDefinition -> { + final String oldFieldDefinitionName = oldFieldDefinition.getName(); + final FieldDefinition matchingNewFieldDefinition = newFieldDefinitionsNameMap.get(oldFieldDefinitionName); - if (matchingNewFieldDefinition != null) { - - FieldTypeAssert.assertNewFieldType(matchingNewFieldDefinition.getType()); - - if (haveSameFieldType(oldFieldDefinition.getType(), matchingNewFieldDefinition.getType())) { - updateActions.addAll(buildActions(oldFieldDefinition, matchingNewFieldDefinition)); - } else { - // since there is no way to change a field type on CTP, - // we remove the field definition and add a new one with a new field type - updateActions.add(RemoveFieldDefinition.of(oldFieldDefinitionName)); - updateActions.add(AddFieldDefinition.of(matchingNewFieldDefinition)); - } - } else { - updateActions.add(RemoveFieldDefinition.of(oldFieldDefinitionName)); - } - } - - return updateActions; + return ofNullable(matchingNewFieldDefinition) + .map(newFieldDefinition -> { + if (newFieldDefinition.getType() != null) { + // field type is required so if null we let commercetools to throw exception + if (haveSameFieldType(oldFieldDefinition.getType(), newFieldDefinition.getType())) { + return buildActions(oldFieldDefinition, newFieldDefinition); + } else { + // since there is no way to change an field type on CTP, + // we remove the field definition and add a new one with a new field type + return Arrays.asList( + RemoveFieldDefinition.of(oldFieldDefinitionName), + AddFieldDefinition.of(newFieldDefinition) + ); + } + } else { + return new ArrayList>(); + } + }) + .orElseGet(() -> singletonList(RemoveFieldDefinition.of(oldFieldDefinitionName))); + }) + .flatMap(Collection::stream) + .collect(Collectors.toList()); } /** diff --git a/src/main/java/com/commercetools/sync/types/utils/LocalizedEnumUpdateActionUtils.java b/src/main/java/com/commercetools/sync/types/utils/LocalizedEnumValueUpdateActionUtils.java similarity index 95% rename from src/main/java/com/commercetools/sync/types/utils/LocalizedEnumUpdateActionUtils.java rename to src/main/java/com/commercetools/sync/types/utils/LocalizedEnumValueUpdateActionUtils.java index 4eac897c1c..9c3dad0a60 100644 --- a/src/main/java/com/commercetools/sync/types/utils/LocalizedEnumUpdateActionUtils.java +++ b/src/main/java/com/commercetools/sync/types/utils/LocalizedEnumValueUpdateActionUtils.java @@ -13,7 +13,7 @@ import static com.commercetools.sync.commons.utils.enums.EnumValuesUpdateActionUtils.buildActions; -public final class LocalizedEnumUpdateActionUtils { +public final class LocalizedEnumValueUpdateActionUtils { /** * Compares a list of old {@link LocalizedEnumValue}s with a list of new {@link LocalizedEnumValue}s for a given * field definition. @@ -49,6 +49,6 @@ public static List> buildLocalizedEnumValuesUpdateActions( ChangeLocalizedEnumValueOrder::of); } - private LocalizedEnumUpdateActionUtils() { + private LocalizedEnumValueUpdateActionUtils() { } } diff --git a/src/test/java/com/commercetools/sync/producttypes/utils/producttypeactionutils/BuildLocalizedEnumUpdateActionsTest.java b/src/test/java/com/commercetools/sync/producttypes/utils/producttypeactionutils/BuildLocalizedEnumUpdateActionsTest.java index c40baf35fc..dfaac07f19 100644 --- a/src/test/java/com/commercetools/sync/producttypes/utils/producttypeactionutils/BuildLocalizedEnumUpdateActionsTest.java +++ b/src/test/java/com/commercetools/sync/producttypes/utils/producttypeactionutils/BuildLocalizedEnumUpdateActionsTest.java @@ -89,7 +89,6 @@ public void buildLocalizedEnumUpdateActions_WithIdenticalPlainEnum_ShouldNotBuil @Rule public ExpectedException expectedException = ExpectedException.none(); - @Test public void buildLocalizedEnumUpdateActions_WithDuplicatePlainEnumValues_ShouldTriggerDuplicateKeyError() { expectedException.expect(DuplicateKeyException.class); @@ -104,7 +103,6 @@ public void buildLocalizedEnumUpdateActions_WithDuplicatePlainEnumValues_ShouldT ); } - @Test public void buildLocalizedEnumUpdateActions_WithOneMissingPlainEnumValue_ShouldBuildRemoveEnumValueAction() { final List> updateActions = buildLocalizedEnumValuesUpdateActions( @@ -249,7 +247,6 @@ public void buildLocalizedEnumUpdateActions_WithDifferentLabels_ShouldReturnChan ); } - @Test public void buildLocalizedEnumUpdateActions_WithSameLabels_ShouldNotReturnChangeLabelAction() { final List> updateActions = buildLocalizedEnumValueUpdateActions( diff --git a/src/test/java/com/commercetools/sync/types/utils/FieldDefinitionUpdateActionUtilsTest.java b/src/test/java/com/commercetools/sync/types/utils/FieldDefinitionUpdateActionUtilsTest.java index 1276c2da48..db7a402b72 100644 --- a/src/test/java/com/commercetools/sync/types/utils/FieldDefinitionUpdateActionUtilsTest.java +++ b/src/test/java/com/commercetools/sync/types/utils/FieldDefinitionUpdateActionUtilsTest.java @@ -69,7 +69,7 @@ public void buildChangeLabelAction_WithSameValues_ShouldReturnEmptyOptional() { } @Test - public void buildActions_WithNewDifferentValues_ShouldReturnActions() throws BuildUpdateActionException { + public void buildActions_WithNewDifferentValues_ShouldReturnActions() { final List> result = buildActions(old, newDifferent); assertThat(result).containsExactlyInAnyOrder( @@ -78,14 +78,14 @@ public void buildActions_WithNewDifferentValues_ShouldReturnActions() throws Bui } @Test - public void buildActions_WithSameValues_ShouldReturnEmpty() throws BuildUpdateActionException { + public void buildActions_WithSameValues_ShouldReturnEmpty() { final List> result = buildActions(old, newSame); assertThat(result).isEmpty(); } @Test - public void buildActions_WithNewPlainEnum_ShouldReturnAddEnumValueAction() throws BuildUpdateActionException { + public void buildActions_WithNewPlainEnum_ShouldReturnAddEnumValueAction() { final FieldDefinition oldFieldDefinition = FieldDefinition.of( EnumFieldType.of(Arrays.asList(ENUM_VALUE_A)), FIELD_NAME_1, @@ -108,8 +108,7 @@ public void buildActions_WithNewPlainEnum_ShouldReturnAddEnumValueAction() throw @Test - public void buildActions_WithoutOldPlainEnum_ShouldNotReturnAnyValueAction() - throws BuildUpdateActionException { + public void buildActions_WithoutOldPlainEnum_ShouldNotReturnAnyValueAction() { final FieldDefinition oldFieldDefinition = FieldDefinition.of( EnumFieldType.of(Arrays.asList(ENUM_VALUE_A)), @@ -133,8 +132,7 @@ public void buildActions_WithoutOldPlainEnum_ShouldNotReturnAnyValueAction() @Test - public void buildActions_WithNewLocalizedEnum_ShouldReturnAddLocalizedEnumValueAction() - throws BuildUpdateActionException { + public void buildActions_WithNewLocalizedEnum_ShouldReturnAddLocalizedEnumValueAction() { final FieldDefinition oldFieldDefinition = FieldDefinition.of( LocalizedEnumFieldType.of(Arrays.asList(LOCALIZED_ENUM_VALUE_A)), @@ -156,70 +154,4 @@ public void buildActions_WithNewLocalizedEnum_ShouldReturnAddLocalizedEnumValueA assertThat(result).containsExactly(AddLocalizedEnumValue.of(FIELD_NAME_1, LOCALIZED_ENUM_VALUE_B)); } - - - @Test - public void buildActions_WithFieldDefinitionsWithoutType_ShouldThrowBuildUpdateActionException() { - final FieldDefinition oldFieldDefinitionWithoutType = FieldDefinition.of( - null, - FIELD_NAME_1, - LocalizedString.ofEnglish(LABEL_1), - false, - TextInputHint.SINGLE_LINE); - - final FieldDefinition newFieldDefinitionWithoutType = FieldDefinition.of( - null, - FIELD_NAME_1, - LocalizedString.ofEnglish(LABEL_1), - false, - TextInputHint.SINGLE_LINE); - - assertThatThrownBy(() -> buildActions(oldFieldDefinitionWithoutType, newFieldDefinitionWithoutType)) - .hasMessage("Field types are not set for both the old and new field definitions.") - .isExactlyInstanceOf(BuildUpdateActionException.class); - } - - @Test - public void buildActions_WithOldFieldDefinitionWithoutType_ShouldThrowBuildUpdateActionException() { - final FieldDefinition oldFieldDefinitionWithoutType = FieldDefinition.of( - null, - FIELD_NAME_1, - LocalizedString.ofEnglish(LABEL_1), - false, - TextInputHint.SINGLE_LINE); - - final FieldDefinition newFieldDefinition = FieldDefinition.of( - LocalizedEnumFieldType.of(Arrays.asList(LOCALIZED_ENUM_VALUE_A, LOCALIZED_ENUM_VALUE_B)), - FIELD_NAME_1, - LocalizedString.ofEnglish(LABEL_1), - false, - TextInputHint.SINGLE_LINE); - - assertThatThrownBy(() -> buildActions(oldFieldDefinitionWithoutType, newFieldDefinition)) - .hasMessage("Field type is not set for the old field definition.") - .isExactlyInstanceOf(BuildUpdateActionException.class); - } - - @Test - public void buildActions_WithNewFieldDefinitionWithoutType_ShouldThrowBuildUpdateActionException() { - final FieldDefinition oldFieldDefinition = FieldDefinition.of( - LocalizedEnumFieldType.of(Arrays.asList(LOCALIZED_ENUM_VALUE_A)), - FIELD_NAME_1, - LocalizedString.ofEnglish(LABEL_1), - false, - TextInputHint.SINGLE_LINE); - - final FieldDefinition newFieldDefinitionWithoutType = FieldDefinition.of( - null, - FIELD_NAME_1, - LocalizedString.ofEnglish(LABEL_1), - false, - TextInputHint.SINGLE_LINE); - - assertThatThrownBy(() -> buildActions(oldFieldDefinition, newFieldDefinitionWithoutType)) - .hasMessage("Field type is not set for the new field definition.") - .isExactlyInstanceOf(BuildUpdateActionException.class); - } - - } diff --git a/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildFieldDefinitionUpdateActionsTest.java b/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildFieldDefinitionUpdateActionsTest.java index b31331014d..3a5f516af0 100644 --- a/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildFieldDefinitionUpdateActionsTest.java +++ b/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildFieldDefinitionUpdateActionsTest.java @@ -57,8 +57,6 @@ public class BuildFieldDefinitionUpdateActionsTest { RES_ROOT + "type-with-field-definitions-cbd.json"; private static final String TYPE_WITH_FIELDS_ABC_WITH_DIFFERENT_TYPE = RES_ROOT + "type-with-field-definitions-abc-with-different-type.json"; - private static final String TYPE_WITH_FIELDS_ABC_WITHOUT_FIELD_TYPE = - RES_ROOT + "type-with-field-definitions-abc-without-field-type.json"; private static final TypeSyncOptions SYNC_OPTIONS = TypeSyncOptionsBuilder .of(mock(SphereClient.class)) @@ -417,38 +415,4 @@ public void buildUpdateActions_WithDifferentType_ShouldRemoveOldFieldDefinitionA AddFieldDefinition.of(FIELD_DEFINITION_A_LOCALIZED_TYPE) ); } - - @Test - public void buildUpdateActions_WithoutFieldType_ShouldNotBuildActionsAndTriggerErrorCallback() { - final Type oldType = readObjectFromResource(TYPE_WITH_FIELDS_ABC, Type.class); - - final TypeDraft newTypeDraft = readObjectFromResource( - TYPE_WITH_FIELDS_ABC_WITHOUT_FIELD_TYPE, - TypeDraft.class - ); - - final List errorMessages = new ArrayList<>(); - final List exceptions = new ArrayList<>(); - final TypeSyncOptions syncOptions = - TypeSyncOptionsBuilder.of(mock(SphereClient.class)) - .errorCallback((errorMessage, exception) -> { - errorMessages.add(errorMessage); - exceptions.add(exception); - }) - .build(); - - final List> updateActions = buildFieldDefinitionUpdateActions( - oldType, - newTypeDraft, - syncOptions - ); - - assertThat(updateActions).isEmpty(); - assertThat(errorMessages).hasSize(1); - assertThat(errorMessages.get(0)).matches("Failed to build update actions for the field definitions of the " - + "type with the key 'key'. Reason: .*BuildUpdateActionException: " - + "Field type is not set for the new field definition."); - assertThat(exceptions).hasSize(1); - assertThat(exceptions.get(0)).isExactlyInstanceOf(BuildUpdateActionException.class); - } } diff --git a/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildLocalizedEnumUpdateActionsTest.java b/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildLocalizedEnumUpdateActionsTest.java index b232e27709..a429f125b3 100644 --- a/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildLocalizedEnumUpdateActionsTest.java +++ b/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildLocalizedEnumUpdateActionsTest.java @@ -1,7 +1,7 @@ package com.commercetools.sync.types.utils.typeactionutils; import com.commercetools.sync.commons.exceptions.DuplicateKeyException; -import com.commercetools.sync.types.utils.LocalizedEnumUpdateActionUtils; +import com.commercetools.sync.types.utils.LocalizedEnumValueUpdateActionUtils; import io.sphere.sdk.commands.UpdateAction; import io.sphere.sdk.types.Type; import io.sphere.sdk.types.commands.updateactions.AddLocalizedEnumValue; @@ -14,7 +14,7 @@ import java.util.List; import static com.commercetools.sync.commons.utils.enums.LocalizedEnumValueTestObjects.*; -import static com.commercetools.sync.types.utils.LocalizedEnumUpdateActionUtils.buildLocalizedEnumValuesUpdateActions; +import static com.commercetools.sync.types.utils.LocalizedEnumValueUpdateActionUtils.buildLocalizedEnumValuesUpdateActions; import static java.util.Arrays.asList; import static org.assertj.core.api.Assertions.assertThat; @@ -61,7 +61,6 @@ public void buildLocalizedEnumUpdateActions_WithIdenticalPlainEnum_ShouldNotBuil assertThat(updateActions).isEmpty(); } - @Test public void buildLocalizedEnumUpdateActions_WithOnePlainEnumValue_ShouldBuildAddEnumValueAction() { final List> updateActions = buildLocalizedEnumValuesUpdateActions( @@ -187,7 +186,7 @@ public void buildLocalizedEnumUpdateActions_WithDuplicatePlainEnumValues_ShouldT + "'field_definition_name', Duplicated enum value: 'b'. Enum Values are expected to be unique inside " + "their definition."); - LocalizedEnumUpdateActionUtils.buildLocalizedEnumValuesUpdateActions( + LocalizedEnumValueUpdateActionUtils.buildLocalizedEnumValuesUpdateActions( "field_definition_name", ENUM_VALUES_ABC, ENUM_VALUES_ABB diff --git a/src/test/resources/com/commercetools/sync/types/utils/updatefielddefinitions/fields/type-with-field-definitions-abc-without-field-type.json b/src/test/resources/com/commercetools/sync/types/utils/updatefielddefinitions/fields/type-with-field-definitions-abc-without-field-type.json deleted file mode 100644 index 003bdedde9..0000000000 --- a/src/test/resources/com/commercetools/sync/types/utils/updatefielddefinitions/fields/type-with-field-definitions-abc-without-field-type.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "key": "key", - "name": { - "en": "name" - }, - "description": { - "en": "description" - }, - "resourceTypeIds": [ - "category" - ], - "fieldDefinitions": [ - { - "name": "a", - "label": { - "en": "label_en_edited" - }, - "required": false, - "inputHint": "SingleLine" - }, - { - "name": "b", - "label": { - "en": "label_en_edited" - }, - "required": false, - "inputHint": "SingleLine" - }, - { - "name": "c", - "label": { - "en": "label_en_edited" - }, - "required": false, - "inputHint": "SingleLine" - } - ] -} \ No newline at end of file From 251be4bd36cdac669af3077a4d503ddb0cbd96b4 Mon Sep 17 00:00:00 2001 From: aoz Date: Fri, 9 Nov 2018 14:08:51 +0100 Subject: [PATCH 040/164] #300 - revert changes on fetchMatchingTypesByKeys. --- .../services/impl/TypeServiceImplIT.java | 3 ++ .../sync/services/impl/TypeServiceImpl.java | 33 +++++++------------ 2 files changed, 15 insertions(+), 21 deletions(-) diff --git a/src/integration-test/java/com/commercetools/sync/integration/services/impl/TypeServiceImplIT.java b/src/integration-test/java/com/commercetools/sync/integration/services/impl/TypeServiceImplIT.java index c0aa12c990..7366b063b2 100644 --- a/src/integration-test/java/com/commercetools/sync/integration/services/impl/TypeServiceImplIT.java +++ b/src/integration-test/java/com/commercetools/sync/integration/services/impl/TypeServiceImplIT.java @@ -19,6 +19,7 @@ import io.sphere.sdk.utils.CompletableFutureUtils; import org.junit.AfterClass; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; import java.util.ArrayList; @@ -162,6 +163,8 @@ public void fetchMatchingTypesByKeys_WithAnyExistingKeys_ShouldReturnASetOfTypes assertThat(errorCallBackMessages).isEmpty(); } + // TODO: GITHUB ISSUE#331 + @Ignore @Test public void fetchMatchingTypesByKeys_WithBadGateWayExceptionAlways_ShouldFail() { // Mock sphere client to return BadeGatewayException on any request. diff --git a/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java b/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java index a2a41d3407..f9480e0286 100644 --- a/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java +++ b/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java @@ -6,11 +6,13 @@ import com.commercetools.sync.services.TypeService; import com.commercetools.sync.types.TypeSyncOptions; import io.sphere.sdk.commands.UpdateAction; +import io.sphere.sdk.queries.QueryExecutionUtils; import io.sphere.sdk.types.Type; import io.sphere.sdk.types.TypeDraft; import io.sphere.sdk.types.commands.TypeCreateCommand; import io.sphere.sdk.types.commands.TypeUpdateCommand; import io.sphere.sdk.types.queries.TypeQuery; +import io.sphere.sdk.types.queries.TypeQueryBuilder; import javax.annotation.Nonnull; import java.util.Collections; @@ -22,10 +24,8 @@ import java.util.concurrent.CompletionStage; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Consumer; -import java.util.function.Function; -import java.util.stream.Collectors; -import static java.lang.String.format; +import static java.util.stream.Collectors.toSet; /** * Implementation of TypeService interface. @@ -61,25 +61,16 @@ public CompletionStage> fetchMatchingTypesByKeys(@Nonnull final Set, List> typePageCallback - = typePage -> typePage; + final TypeQuery typeQuery = TypeQueryBuilder + .of() + .plusPredicates(queryModel -> queryModel.key().isIn(keys)) + .build(); - return CtpQueryUtils.queryAll(syncOptions.getCtpClient(), - TypeQuery.of().withPredicates(queryModel -> queryModel.key().isIn(keys)), - typePageCallback) - .handle((fetchedTypes, sphereException) -> { - if (sphereException != null) { - syncOptions - .applyErrorCallback(format(FETCH_FAILED, keys, sphereException), - sphereException); - return Collections.emptySet(); - } - return fetchedTypes.stream() - .flatMap(List::stream) - .peek(type -> - keyToIdCache.put(type.getKey(), type.getId())) - .collect(Collectors.toSet()); - }); + return QueryExecutionUtils.queryAll(syncOptions.getCtpClient(), typeQuery) + .thenApply(types -> types + .stream() + .peek(type -> keyToIdCache.put(type.getKey(), type.getId())) + .collect(toSet())); } @Nonnull From dee188b7d17777a0c8dacc10ad50b723deafad75 Mon Sep 17 00:00:00 2001 From: aoz Date: Fri, 9 Nov 2018 15:32:41 +0100 Subject: [PATCH 041/164] #300 - filter null values on buildFieldDefinitionsUpdateActions --- .../FieldDefinitionsUpdateActionUtils.java | 8 +++- ...dAttributeDefinitionUpdateActionsTest.java | 2 +- ...BuildFieldDefinitionUpdateActionsTest.java | 42 +++++++++++++++++++ 3 files changed, 50 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionsUpdateActionUtils.java b/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionsUpdateActionUtils.java index a6064828ac..b1f6411dfb 100644 --- a/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionsUpdateActionUtils.java +++ b/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionsUpdateActionUtils.java @@ -18,6 +18,7 @@ import java.util.Collection; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -45,6 +46,9 @@ final class FieldDefinitionsUpdateActionUtils { *

If the list of new {@link FieldDefinition}s is {@code null}, then remove actions are built for * every existing field definition in the {@code oldFieldDefinitions} list. * + *

Note: The method will ignore/filter out {@code null} field definitions from the passed + * {@code newFieldDefinitions}.

+ * * @param oldFieldDefinitions the old list of field definitions. * @param newFieldDefinitions the new list of field definitions. * @return a list of field definitions update actions if the list of field definitions is not identical. @@ -58,7 +62,9 @@ static List> buildFieldDefinitionsUpdateActions( throws BuildUpdateActionException { if (newFieldDefinitions != null) { - return buildUpdateActions(oldFieldDefinitions, newFieldDefinitions); + return buildUpdateActions( + oldFieldDefinitions, + newFieldDefinitions.stream().filter(Objects::nonNull).collect(toList())); } else { return oldFieldDefinitions .stream() diff --git a/src/test/java/com/commercetools/sync/producttypes/utils/producttypeactionutils/BuildAttributeDefinitionUpdateActionsTest.java b/src/test/java/com/commercetools/sync/producttypes/utils/producttypeactionutils/BuildAttributeDefinitionUpdateActionsTest.java index 96b17c06b9..0dcb803b53 100644 --- a/src/test/java/com/commercetools/sync/producttypes/utils/producttypeactionutils/BuildAttributeDefinitionUpdateActionsTest.java +++ b/src/test/java/com/commercetools/sync/producttypes/utils/producttypeactionutils/BuildAttributeDefinitionUpdateActionsTest.java @@ -451,7 +451,7 @@ public void buildAttributesUpdateActions_WithDifferentAttributeType_ShouldRemove } @Test - public void buildAttributesUpdateActions_WithANullAttributeDefinitonDraft_ShouldSkipNullAttributes() { + public void buildAttributesUpdateActions_WithANullAttributeDefinitionDraft_ShouldSkipNullAttributes() { // preparation final ProductType oldProductType = mock(ProductType.class); final AttributeDefinition attributeDefinition = AttributeDefinitionBuilder diff --git a/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildFieldDefinitionUpdateActionsTest.java b/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildFieldDefinitionUpdateActionsTest.java index 3a5f516af0..4e235ba3a7 100644 --- a/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildFieldDefinitionUpdateActionsTest.java +++ b/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildFieldDefinitionUpdateActionsTest.java @@ -9,11 +9,13 @@ import io.sphere.sdk.models.LocalizedString; import io.sphere.sdk.models.TextInputHint; import io.sphere.sdk.types.FieldDefinition; +import io.sphere.sdk.types.LocalizedEnumFieldType; import io.sphere.sdk.types.ResourceTypeIdsSetBuilder; import io.sphere.sdk.types.Type; import io.sphere.sdk.types.TypeDraft; import io.sphere.sdk.types.TypeDraftBuilder; import io.sphere.sdk.types.commands.updateactions.AddFieldDefinition; +import io.sphere.sdk.types.commands.updateactions.ChangeFieldDefinitionLabel; import io.sphere.sdk.types.commands.updateactions.ChangeFieldDefinitionOrder; import io.sphere.sdk.types.commands.updateactions.RemoveFieldDefinition; import org.junit.Test; @@ -27,6 +29,8 @@ import static io.sphere.sdk.json.SphereJsonUtils.readObjectFromResource; import static java.util.Arrays.asList; import static java.util.Collections.emptyList; +import static java.util.Collections.emptySet; +import static java.util.Collections.singletonList; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -415,4 +419,42 @@ public void buildUpdateActions_WithDifferentType_ShouldRemoveOldFieldDefinitionA AddFieldDefinition.of(FIELD_DEFINITION_A_LOCALIZED_TYPE) ); } + + @Test + public void buildUpdateActions_WithANullNewFieldDefinition_ShouldSkipNullFieldDefinitions() { + // preparation + final Type oldType = mock(Type.class); + final FieldDefinition oldFieldDefinition = FieldDefinition.of( + LocalizedEnumFieldType.of(emptyList()), + "field_1", + LocalizedString.ofEnglish("label1"), + false, + TextInputHint.SINGLE_LINE); + + + when(oldType.getFieldDefinitions()).thenReturn(singletonList(oldFieldDefinition)); + + final FieldDefinition newFieldDefinition = FieldDefinition.of( + LocalizedEnumFieldType.of(emptyList()), + "field_1", + LocalizedString.ofEnglish("label2"), + false, + TextInputHint.SINGLE_LINE); + + final TypeDraft typeDraft = TypeDraftBuilder + .of("key", LocalizedString.ofEnglish("label"), emptySet()) + .fieldDefinitions(asList(null, newFieldDefinition)) + .build(); + + // test + final List> updateActions = buildFieldDefinitionUpdateActions( + oldType, + typeDraft, + SYNC_OPTIONS + ); + + // assertion + assertThat(updateActions).containsExactly( + ChangeFieldDefinitionLabel.of(newFieldDefinition.getName(), newFieldDefinition.getLabel())); + } } From 4cea407b2f5333d5093060ae5c4fc7694d3ea429 Mon Sep 17 00:00:00 2001 From: aoz Date: Sun, 11 Nov 2018 21:31:34 +0100 Subject: [PATCH 042/164] #300 - refactor to use static helpers on collections --- .../sync/types/TypeSyncOptionsBuilderTest.java | 14 ++++++++------ .../FieldDefinitionUpdateActionUtilsTest.java | 14 ++++++++------ .../BuildLocalizedEnumUpdateActionsTest.java | 7 ++++--- .../BuildPlainEnumUpdateActionsTest.java | 7 ++++--- 4 files changed, 24 insertions(+), 18 deletions(-) diff --git a/src/test/java/com/commercetools/sync/types/TypeSyncOptionsBuilderTest.java b/src/test/java/com/commercetools/sync/types/TypeSyncOptionsBuilderTest.java index eb70c0ecd6..9e55eb4123 100644 --- a/src/test/java/com/commercetools/sync/types/TypeSyncOptionsBuilderTest.java +++ b/src/test/java/com/commercetools/sync/types/TypeSyncOptionsBuilderTest.java @@ -17,6 +17,8 @@ import java.util.function.Function; import static io.sphere.sdk.models.LocalizedString.ofEnglish; +import static java.util.Collections.emptyList; +import static java.util.Collections.singletonList; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -47,7 +49,7 @@ public void build_WithClient_ShouldBuildSyncOptions() { @Test public void beforeUpdateCallback_WithFilterAsCallback_ShouldSetCallback() { final TriFunction>, TypeDraft, Type, List>> - beforeUpdateCallback = (updateActions, newType, oldType) -> Collections.emptyList(); + beforeUpdateCallback = (updateActions, newType, oldType) -> emptyList(); typeSyncOptionsBuilder.beforeUpdateCallback(beforeUpdateCallback); @@ -97,7 +99,7 @@ public void typeSyncOptionsBuilderSetters_ShouldBeCallableAfterBaseSyncOptionsBu .of(CTP_CLIENT) .batchSize(30) .beforeCreateCallback((newType) -> null) - .beforeUpdateCallback((updateActions, newType, oldType) -> Collections.emptyList()) + .beforeUpdateCallback((updateActions, newType, oldType) -> emptyList()) .build(); assertThat(typeSyncOptions).isNotNull(); } @@ -132,7 +134,7 @@ public void applyBeforeUpdateCallBack_WithNullCallback_ShouldReturnIdenticalList .build(); assertThat(typeSyncOptions.getBeforeUpdateCallback()).isNull(); - final List> updateActions = Collections.singletonList(ChangeName.of(ofEnglish("name"))); + final List> updateActions = singletonList(ChangeName.of(ofEnglish("name"))); final List> filteredList = typeSyncOptions.applyBeforeUpdateCallBack(updateActions, mock(TypeDraft.class), mock(Type.class)); @@ -150,7 +152,7 @@ public void applyBeforeUpdateCallBack_WithNullReturnCallback_ShouldReturnEmptyLi .build(); assertThat(typeSyncOptions.getBeforeUpdateCallback()).isNotNull(); - final List> updateActions = Collections.singletonList(ChangeName.of(ofEnglish("name"))); + final List> updateActions = singletonList(ChangeName.of(ofEnglish("name"))); final List> filteredList = typeSyncOptions.applyBeforeUpdateCallBack(updateActions, mock(TypeDraft.class), mock(Type.class)); assertThat(filteredList).isNotEqualTo(updateActions); @@ -160,7 +162,7 @@ public void applyBeforeUpdateCallBack_WithNullReturnCallback_ShouldReturnEmptyLi @Test public void applyBeforeUpdateCallBack_WithCallback_ShouldReturnFilteredList() { final TriFunction>, TypeDraft, Type, List>> - beforeUpdateCallback = (updateActions, newType, oldType) -> Collections.emptyList(); + beforeUpdateCallback = (updateActions, newType, oldType) -> emptyList(); final TypeSyncOptions typeSyncOptions = TypeSyncOptionsBuilder.of(CTP_CLIENT) .beforeUpdateCallback( @@ -168,7 +170,7 @@ public void applyBeforeUpdateCallBack_WithCallback_ShouldReturnFilteredList() { .build(); assertThat(typeSyncOptions.getBeforeUpdateCallback()).isNotNull(); - final List> updateActions = Collections.singletonList(ChangeName.of(ofEnglish("name"))); + final List> updateActions = singletonList(ChangeName.of(ofEnglish("name"))); final List> filteredList = typeSyncOptions.applyBeforeUpdateCallBack(updateActions, mock(TypeDraft.class), mock(Type.class)); assertThat(filteredList).isNotEqualTo(updateActions); diff --git a/src/test/java/com/commercetools/sync/types/utils/FieldDefinitionUpdateActionUtilsTest.java b/src/test/java/com/commercetools/sync/types/utils/FieldDefinitionUpdateActionUtilsTest.java index db7a402b72..14448fab1e 100644 --- a/src/test/java/com/commercetools/sync/types/utils/FieldDefinitionUpdateActionUtilsTest.java +++ b/src/test/java/com/commercetools/sync/types/utils/FieldDefinitionUpdateActionUtilsTest.java @@ -25,6 +25,8 @@ import static com.commercetools.sync.types.utils.FieldDefinitionUpdateActionUtils.buildActions; import static com.commercetools.sync.types.utils.FieldDefinitionUpdateActionUtils.buildChangeLabelUpdateAction; import static io.sphere.sdk.models.LocalizedString.ofEnglish; +import static java.util.Arrays.*; +import static java.util.Collections.emptyList; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; @@ -87,7 +89,7 @@ public void buildActions_WithSameValues_ShouldReturnEmpty() { @Test public void buildActions_WithNewPlainEnum_ShouldReturnAddEnumValueAction() { final FieldDefinition oldFieldDefinition = FieldDefinition.of( - EnumFieldType.of(Arrays.asList(ENUM_VALUE_A)), + EnumFieldType.of(asList(ENUM_VALUE_A)), FIELD_NAME_1, LocalizedString.ofEnglish(LABEL_1), false, @@ -95,7 +97,7 @@ public void buildActions_WithNewPlainEnum_ShouldReturnAddEnumValueAction() { final FieldDefinition newFieldDefinition = FieldDefinition.of( - EnumFieldType.of(Arrays.asList(ENUM_VALUE_A, ENUM_VALUE_B)), + EnumFieldType.of(asList(ENUM_VALUE_A, ENUM_VALUE_B)), FIELD_NAME_1, LocalizedString.ofEnglish(LABEL_1), false, @@ -111,14 +113,14 @@ public void buildActions_WithNewPlainEnum_ShouldReturnAddEnumValueAction() { public void buildActions_WithoutOldPlainEnum_ShouldNotReturnAnyValueAction() { final FieldDefinition oldFieldDefinition = FieldDefinition.of( - EnumFieldType.of(Arrays.asList(ENUM_VALUE_A)), + EnumFieldType.of(asList(ENUM_VALUE_A)), FIELD_NAME_1, LocalizedString.ofEnglish(LABEL_1), false, TextInputHint.SINGLE_LINE); final FieldDefinition newFieldDefinition = FieldDefinition.of( - EnumFieldType.of(Collections.emptyList()), + EnumFieldType.of(emptyList()), FIELD_NAME_1, LocalizedString.ofEnglish(LABEL_1), false, @@ -135,14 +137,14 @@ public void buildActions_WithoutOldPlainEnum_ShouldNotReturnAnyValueAction() { public void buildActions_WithNewLocalizedEnum_ShouldReturnAddLocalizedEnumValueAction() { final FieldDefinition oldFieldDefinition = FieldDefinition.of( - LocalizedEnumFieldType.of(Arrays.asList(LOCALIZED_ENUM_VALUE_A)), + LocalizedEnumFieldType.of(asList(LOCALIZED_ENUM_VALUE_A)), FIELD_NAME_1, LocalizedString.ofEnglish(LABEL_1), false, TextInputHint.SINGLE_LINE); final FieldDefinition newFieldDefinition = FieldDefinition.of( - LocalizedEnumFieldType.of(Arrays.asList(LOCALIZED_ENUM_VALUE_A, LOCALIZED_ENUM_VALUE_B)), + LocalizedEnumFieldType.of(asList(LOCALIZED_ENUM_VALUE_A, LOCALIZED_ENUM_VALUE_B)), FIELD_NAME_1, LocalizedString.ofEnglish(LABEL_1), false, diff --git a/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildLocalizedEnumUpdateActionsTest.java b/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildLocalizedEnumUpdateActionsTest.java index a429f125b3..5afc254d9f 100644 --- a/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildLocalizedEnumUpdateActionsTest.java +++ b/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildLocalizedEnumUpdateActionsTest.java @@ -16,6 +16,7 @@ import static com.commercetools.sync.commons.utils.enums.LocalizedEnumValueTestObjects.*; import static com.commercetools.sync.types.utils.LocalizedEnumValueUpdateActionUtils.buildLocalizedEnumValuesUpdateActions; import static java.util.Arrays.asList; +import static java.util.Collections.emptyList; import static org.assertj.core.api.Assertions.assertThat; public class BuildLocalizedEnumUpdateActionsTest { @@ -28,8 +29,8 @@ public class BuildLocalizedEnumUpdateActionsTest { public void buildLocalizedEnumUpdateActions_WithEmptyPlainEnumValuesAndNoOlEnumValues_ShouldNotBuildActions() { final List> updateActions = buildLocalizedEnumValuesUpdateActions( FIELD_NAME_1, - Collections.emptyList(), - Collections.emptyList() + emptyList(), + emptyList() ); assertThat(updateActions).isEmpty(); @@ -39,7 +40,7 @@ public void buildLocalizedEnumUpdateActions_WithEmptyPlainEnumValuesAndNoOlEnumV public void buildLocalizedEnumUpdateActions_WithNewPlainEnumValuesAndNoOldPlainEnumValues_ShouldBuild3AddActions() { final List> updateActions = buildLocalizedEnumValuesUpdateActions( FIELD_NAME_1, - Collections.emptyList(), + emptyList(), ENUM_VALUES_ABC ); diff --git a/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildPlainEnumUpdateActionsTest.java b/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildPlainEnumUpdateActionsTest.java index dd4f0a2b74..114ebce19d 100644 --- a/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildPlainEnumUpdateActionsTest.java +++ b/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildPlainEnumUpdateActionsTest.java @@ -12,6 +12,7 @@ import static com.commercetools.sync.commons.utils.enums.PlainEnumValueTestObjects.*; import static com.commercetools.sync.types.utils.PlainEnumValueUpdateActionUtils.buildEnumValuesUpdateActions; import static java.util.Arrays.asList; +import static java.util.Collections.emptyList; import static org.assertj.core.api.Assertions.assertThat; public class BuildPlainEnumUpdateActionsTest { @@ -21,8 +22,8 @@ public class BuildPlainEnumUpdateActionsTest { public void buildPlainEnumUpdateActions_WithEmptyPlainEnumValuesAndNoOlEnumValues_ShouldNotBuildActions() { final List> updateActions = buildEnumValuesUpdateActions( FIELD_NAME_1, - Collections.emptyList(), - Collections.emptyList() + emptyList(), + emptyList() ); assertThat(updateActions).isEmpty(); @@ -32,7 +33,7 @@ public void buildPlainEnumUpdateActions_WithEmptyPlainEnumValuesAndNoOlEnumValue public void buildPlainEnumUpdateActions_WithNewPlainEnumValuesAndNoOldPlainEnumValues_ShouldBuild3AddActions() { final List> updateActions = buildEnumValuesUpdateActions( FIELD_NAME_1, - Collections.emptyList(), + emptyList(), ENUM_VALUES_ABC ); From 03d1832c3ef8efd70c1d2639c3d2a5bbb2c102c6 Mon Sep 17 00:00:00 2001 From: aoz Date: Sun, 11 Nov 2018 21:35:55 +0100 Subject: [PATCH 043/164] #300 - fix test --- .../com/commercetools/sync/integration/types/TypeSyncIT.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/integration-test/java/com/commercetools/sync/integration/types/TypeSyncIT.java b/src/integration-test/java/com/commercetools/sync/integration/types/TypeSyncIT.java index a8be442734..d4febe878b 100644 --- a/src/integration-test/java/com/commercetools/sync/integration/types/TypeSyncIT.java +++ b/src/integration-test/java/com/commercetools/sync/integration/types/TypeSyncIT.java @@ -60,7 +60,6 @@ import static org.mockito.Mockito.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -656,7 +655,7 @@ public void sync_WithoutFieldDefinitionType_ShouldExecuteCallbackOnErrorAndIncre // Since the error message and exception is coming from commercetools, we don't test the actual message and // exception - verify(spyTypeSyncOptions, times(2)).applyErrorCallback(any(), any()); + verify(spyTypeSyncOptions).applyErrorCallback(any(), any()); AssertionsForStatistics.assertThat(typeSyncStatistics).hasValues(1, 0, 0, 1); } From 84f4e6bd6f686b721dd581ce1fc69c60ea63d756 Mon Sep 17 00:00:00 2001 From: aoz Date: Sun, 11 Nov 2018 22:18:33 +0100 Subject: [PATCH 044/164] #300 - move commons.utils.enums package to commons.utils package --- .../EnumValuesUpdateActionUtils.java | 3 +- .../LocalizedEnumValueUpdateActionUtils.java | 38 ------------------- .../PlainEnumValueUpdateActionUtils.java | 38 ------------------- .../LocalizedEnumValueUpdateActionUtils.java | 32 +++++++++++++--- .../PlainEnumValueUpdateActionUtils.java | 33 ++++++++++++---- .../LocalizedEnumValueUpdateActionUtils.java | 2 +- .../PlainEnumValueUpdateActionUtils.java | 2 +- .../EnumValuesUpdateActionUtilsTest.java | 20 +++++----- .../LocalizedEnumValueTestObjects.java | 2 +- .../PlainEnumValueTestObjects.java | 2 +- ...calizedEnumValueUpdateActionUtilsTest.java | 10 ++--- .../PlainEnumValueUpdateActionUtilsTest.java | 10 ++--- .../BuildLocalizedEnumUpdateActionsTest.java | 2 +- .../BuildPlainEnumUpdateActionsTest.java | 2 +- .../BuildLocalizedEnumUpdateActionsTest.java | 3 +- .../BuildPlainEnumUpdateActionsTest.java | 3 +- 16 files changed, 78 insertions(+), 124 deletions(-) rename src/main/java/com/commercetools/sync/commons/utils/{enums => }/EnumValuesUpdateActionUtils.java (99%) delete mode 100644 src/main/java/com/commercetools/sync/commons/utils/enums/LocalizedEnumValueUpdateActionUtils.java delete mode 100644 src/main/java/com/commercetools/sync/commons/utils/enums/PlainEnumValueUpdateActionUtils.java rename src/test/java/com/commercetools/sync/commons/utils/{enums => }/EnumValuesUpdateActionUtilsTest.java (91%) rename src/test/java/com/commercetools/sync/commons/utils/{enums => }/LocalizedEnumValueTestObjects.java (97%) rename src/test/java/com/commercetools/sync/commons/utils/{enums => }/PlainEnumValueTestObjects.java (97%) rename src/test/java/com/commercetools/sync/{commons/utils/enums => producttypes/utils}/LocalizedEnumValueUpdateActionUtilsTest.java (82%) rename src/test/java/com/commercetools/sync/{commons/utils/enums => producttypes/utils}/PlainEnumValueUpdateActionUtilsTest.java (83%) diff --git a/src/main/java/com/commercetools/sync/commons/utils/enums/EnumValuesUpdateActionUtils.java b/src/main/java/com/commercetools/sync/commons/utils/EnumValuesUpdateActionUtils.java similarity index 99% rename from src/main/java/com/commercetools/sync/commons/utils/enums/EnumValuesUpdateActionUtils.java rename to src/main/java/com/commercetools/sync/commons/utils/EnumValuesUpdateActionUtils.java index a827aac909..2e42900691 100644 --- a/src/main/java/com/commercetools/sync/commons/utils/enums/EnumValuesUpdateActionUtils.java +++ b/src/main/java/com/commercetools/sync/commons/utils/EnumValuesUpdateActionUtils.java @@ -1,7 +1,6 @@ -package com.commercetools.sync.commons.utils.enums; +package com.commercetools.sync.commons.utils; import com.commercetools.sync.commons.exceptions.DuplicateKeyException; -import com.commercetools.sync.commons.utils.TriFunction; import io.sphere.sdk.commands.UpdateAction; import io.sphere.sdk.models.EnumValue; import io.sphere.sdk.models.WithKey; diff --git a/src/main/java/com/commercetools/sync/commons/utils/enums/LocalizedEnumValueUpdateActionUtils.java b/src/main/java/com/commercetools/sync/commons/utils/enums/LocalizedEnumValueUpdateActionUtils.java deleted file mode 100644 index a42f608fbb..0000000000 --- a/src/main/java/com/commercetools/sync/commons/utils/enums/LocalizedEnumValueUpdateActionUtils.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.commercetools.sync.commons.utils.enums; - -import io.sphere.sdk.commands.UpdateAction; -import io.sphere.sdk.models.LocalizedEnumValue; - -import javax.annotation.Nonnull; -import java.util.Optional; -import java.util.function.BiFunction; - -import static com.commercetools.sync.commons.utils.CommonTypeUpdateActionUtils.buildUpdateAction; - -public final class LocalizedEnumValueUpdateActionUtils { - - /** - * Compares the {@code label} values of an old {@link LocalizedEnumValue} and a new {@link LocalizedEnumValue} - * and returns an {@link Optional} of update action, which would contain the {@code "changeLabel"} - * {@link UpdateAction}. If both, old and new {@link LocalizedEnumValue} have the same {@code label} values, - * then no update action is needed and empty optional will be returned. - * - * @param attributeDefinitionName the attribute definition name whose localized enum values belong to. - * @param oldEnumValue the old localized enum value. - * @param newEnumValue the new localized enum value which contains the new description. - * @param the type of the resource in which the update actions will be applied on. - * @return optional containing update action or empty optional if labels - * are identical. - */ - @Nonnull - public static Optional> buildChangeLabelAction( - @Nonnull final String attributeDefinitionName, - @Nonnull final LocalizedEnumValue oldEnumValue, - @Nonnull final LocalizedEnumValue newEnumValue, - @Nonnull final BiFunction> changeLocalizedEnumValueLabelAction) { - - return buildUpdateAction(oldEnumValue.getLabel(), newEnumValue.getLabel(), - () -> changeLocalizedEnumValueLabelAction.apply(attributeDefinitionName, newEnumValue)); - } - -} diff --git a/src/main/java/com/commercetools/sync/commons/utils/enums/PlainEnumValueUpdateActionUtils.java b/src/main/java/com/commercetools/sync/commons/utils/enums/PlainEnumValueUpdateActionUtils.java deleted file mode 100644 index dab13cbe76..0000000000 --- a/src/main/java/com/commercetools/sync/commons/utils/enums/PlainEnumValueUpdateActionUtils.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.commercetools.sync.commons.utils.enums; - -import io.sphere.sdk.commands.UpdateAction; -import io.sphere.sdk.models.EnumValue; - -import javax.annotation.Nonnull; -import java.util.Optional; -import java.util.function.BiFunction; - -import static com.commercetools.sync.commons.utils.CommonTypeUpdateActionUtils.buildUpdateAction; - -public final class PlainEnumValueUpdateActionUtils { - - /** - * Compares the {@code label} values of an old {@link EnumValue} and a new {@link EnumValue} - * and returns an {@link Optional} of update action, which would contain the {@code "changeLabel"} - * {@link UpdateAction}. If both, old and new {@link EnumValue} have the same {@code label} values, - * then no update action is needed and empty optional will be returned. - * - * @param attributeDefinitionName the attribute definition name whose plain enum values belong to. - * @param oldEnumValue the old plain enum value. - * @param newEnumValue the new plain enum value which contains the new description. - * @param the type of the resource in which the update actions will be applied on. - * @return optional containing update action or empty optional if labels - * are identical. - */ - @Nonnull - public static Optional> buildChangeLabelAction( - @Nonnull final String attributeDefinitionName, - @Nonnull final EnumValue oldEnumValue, - @Nonnull final EnumValue newEnumValue, - @Nonnull final BiFunction> changePlainEnumValueLabelAction) { - - return buildUpdateAction(oldEnumValue.getLabel(), newEnumValue.getLabel(), - () -> changePlainEnumValueLabelAction.apply(attributeDefinitionName, newEnumValue)); - } - -} diff --git a/src/main/java/com/commercetools/sync/producttypes/utils/LocalizedEnumValueUpdateActionUtils.java b/src/main/java/com/commercetools/sync/producttypes/utils/LocalizedEnumValueUpdateActionUtils.java index a95e7c4140..d232d5d6c5 100644 --- a/src/main/java/com/commercetools/sync/producttypes/utils/LocalizedEnumValueUpdateActionUtils.java +++ b/src/main/java/com/commercetools/sync/producttypes/utils/LocalizedEnumValueUpdateActionUtils.java @@ -12,10 +12,11 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.util.List; +import java.util.Optional; +import static com.commercetools.sync.commons.utils.CommonTypeUpdateActionUtils.buildUpdateAction; import static com.commercetools.sync.commons.utils.OptionalUtils.filterEmptyOptionals; -import static com.commercetools.sync.commons.utils.enums.EnumValuesUpdateActionUtils.buildActions; -import static com.commercetools.sync.commons.utils.enums.LocalizedEnumValueUpdateActionUtils.buildChangeLabelAction; +import static com.commercetools.sync.commons.utils.EnumValuesUpdateActionUtils.buildActions; public final class LocalizedEnumValueUpdateActionUtils { @@ -73,13 +74,32 @@ public static List> buildLocalizedEnumValueUpdateActio @Nonnull final LocalizedEnumValue newEnumValue) { return filterEmptyOptionals( - buildChangeLabelAction(attributeDefinitionName, - oldEnumValue, - newEnumValue, - ChangeLocalizedEnumValueLabel::of) + buildChangeLabelAction(attributeDefinitionName, oldEnumValue, newEnumValue) ); } + /** + * Compares the {@code label} values of an old {@link LocalizedEnumValue} and a new {@link LocalizedEnumValue} + * and returns an {@link Optional} of update action, which would contain the {@code "changeLabel"} + * {@link UpdateAction}. If both, old and new {@link LocalizedEnumValue} have the same {@code label} values, + * then no update action is needed and empty optional will be returned. + * + * @param attributeDefinitionName the attribute definition name whose localized enum values belong to. + * @param oldEnumValue the old localized enum value. + * @param newEnumValue the new localized enum value which contains the new description. + * @return optional containing update action or empty optional if labels + * are identical. + */ + @Nonnull + public static Optional> buildChangeLabelAction( + @Nonnull final String attributeDefinitionName, + @Nonnull final LocalizedEnumValue oldEnumValue, + @Nonnull final LocalizedEnumValue newEnumValue) { + + return buildUpdateAction(oldEnumValue.getLabel(), newEnumValue.getLabel(), + () -> ChangeLocalizedEnumValueLabel.of(attributeDefinitionName, newEnumValue)); + } + private LocalizedEnumValueUpdateActionUtils() { } } diff --git a/src/main/java/com/commercetools/sync/producttypes/utils/PlainEnumValueUpdateActionUtils.java b/src/main/java/com/commercetools/sync/producttypes/utils/PlainEnumValueUpdateActionUtils.java index 99eca6e58b..3756710abb 100644 --- a/src/main/java/com/commercetools/sync/producttypes/utils/PlainEnumValueUpdateActionUtils.java +++ b/src/main/java/com/commercetools/sync/producttypes/utils/PlainEnumValueUpdateActionUtils.java @@ -1,6 +1,6 @@ package com.commercetools.sync.producttypes.utils; -import com.commercetools.sync.commons.utils.enums.EnumValuesUpdateActionUtils; +import com.commercetools.sync.commons.utils.EnumValuesUpdateActionUtils; import io.sphere.sdk.commands.UpdateAction; import io.sphere.sdk.models.EnumValue; import io.sphere.sdk.producttypes.ProductType; @@ -12,9 +12,10 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.util.List; +import java.util.Optional; +import static com.commercetools.sync.commons.utils.CommonTypeUpdateActionUtils.buildUpdateAction; import static com.commercetools.sync.commons.utils.OptionalUtils.filterEmptyOptionals; -import static com.commercetools.sync.commons.utils.enums.PlainEnumValueUpdateActionUtils.buildChangeLabelAction; public final class PlainEnumValueUpdateActionUtils { @@ -68,14 +69,32 @@ public static List> buildEnumValueUpdateActions( @Nonnull final EnumValue newEnumValue) { return filterEmptyOptionals( - buildChangeLabelAction(attributeDefinitionName, - oldEnumValue, - newEnumValue, - ChangePlainEnumValueLabel::of - ) + buildChangeLabelAction(attributeDefinitionName, oldEnumValue, newEnumValue) ); } + /** + * Compares the {@code label} values of an old {@link EnumValue} and a new {@link EnumValue} + * and returns an {@link Optional} of update action, which would contain the {@code "changeLabel"} + * {@link UpdateAction}. If both, old and new {@link EnumValue} have the same {@code label} values, + * then no update action is needed and empty optional will be returned. + * + * @param attributeDefinitionName the attribute definition name whose plain enum values belong to. + * @param oldEnumValue the old plain enum value. + * @param newEnumValue the new plain enum value which contains the new description. + * @return optional containing update action or empty optional if labels + * are identical. + */ + @Nonnull + public static Optional> buildChangeLabelAction( + @Nonnull final String attributeDefinitionName, + @Nonnull final EnumValue oldEnumValue, + @Nonnull final EnumValue newEnumValue) { + + return buildUpdateAction(oldEnumValue.getLabel(), newEnumValue.getLabel(), + () -> ChangePlainEnumValueLabel.of(attributeDefinitionName, newEnumValue)); + } + private PlainEnumValueUpdateActionUtils() { } diff --git a/src/main/java/com/commercetools/sync/types/utils/LocalizedEnumValueUpdateActionUtils.java b/src/main/java/com/commercetools/sync/types/utils/LocalizedEnumValueUpdateActionUtils.java index 9c3dad0a60..2d7b2d6f11 100644 --- a/src/main/java/com/commercetools/sync/types/utils/LocalizedEnumValueUpdateActionUtils.java +++ b/src/main/java/com/commercetools/sync/types/utils/LocalizedEnumValueUpdateActionUtils.java @@ -11,7 +11,7 @@ import javax.annotation.Nullable; import java.util.List; -import static com.commercetools.sync.commons.utils.enums.EnumValuesUpdateActionUtils.buildActions; +import static com.commercetools.sync.commons.utils.EnumValuesUpdateActionUtils.buildActions; public final class LocalizedEnumValueUpdateActionUtils { /** diff --git a/src/main/java/com/commercetools/sync/types/utils/PlainEnumValueUpdateActionUtils.java b/src/main/java/com/commercetools/sync/types/utils/PlainEnumValueUpdateActionUtils.java index 2baa7f437c..b07089b8a3 100644 --- a/src/main/java/com/commercetools/sync/types/utils/PlainEnumValueUpdateActionUtils.java +++ b/src/main/java/com/commercetools/sync/types/utils/PlainEnumValueUpdateActionUtils.java @@ -1,6 +1,6 @@ package com.commercetools.sync.types.utils; -import com.commercetools.sync.commons.utils.enums.EnumValuesUpdateActionUtils; +import com.commercetools.sync.commons.utils.EnumValuesUpdateActionUtils; import io.sphere.sdk.commands.UpdateAction; import io.sphere.sdk.models.EnumValue; import io.sphere.sdk.types.Type; diff --git a/src/test/java/com/commercetools/sync/commons/utils/enums/EnumValuesUpdateActionUtilsTest.java b/src/test/java/com/commercetools/sync/commons/utils/EnumValuesUpdateActionUtilsTest.java similarity index 91% rename from src/test/java/com/commercetools/sync/commons/utils/enums/EnumValuesUpdateActionUtilsTest.java rename to src/test/java/com/commercetools/sync/commons/utils/EnumValuesUpdateActionUtilsTest.java index c3e51613ed..9c10d925e9 100644 --- a/src/test/java/com/commercetools/sync/commons/utils/enums/EnumValuesUpdateActionUtilsTest.java +++ b/src/test/java/com/commercetools/sync/commons/utils/EnumValuesUpdateActionUtilsTest.java @@ -1,6 +1,5 @@ -package com.commercetools.sync.commons.utils.enums; +package com.commercetools.sync.commons.utils; -import com.commercetools.sync.commons.utils.TriFunction; import io.sphere.sdk.commands.UpdateAction; import io.sphere.sdk.models.EnumValue; import io.sphere.sdk.producttypes.ProductType; @@ -16,13 +15,13 @@ import java.util.Optional; import static com.commercetools.sync.commons.utils.OptionalUtils.filterEmptyOptionals; -import static com.commercetools.sync.commons.utils.enums.EnumValuesUpdateActionUtils.buildActions; -import static com.commercetools.sync.commons.utils.enums.EnumValuesUpdateActionUtils.buildAddEnumValuesUpdateActions; -import static com.commercetools.sync.commons.utils.enums.EnumValuesUpdateActionUtils.buildChangeEnumValuesOrderUpdateAction; -import static com.commercetools.sync.commons.utils.enums.EnumValuesUpdateActionUtils.buildMatchingEnumValuesUpdateActions; -import static com.commercetools.sync.commons.utils.enums.EnumValuesUpdateActionUtils.buildRemoveEnumValuesUpdateAction; -import static com.commercetools.sync.commons.utils.enums.PlainEnumValueTestObjects.*; -import static com.commercetools.sync.commons.utils.enums.PlainEnumValueUpdateActionUtils.buildChangeLabelAction; +import static com.commercetools.sync.commons.utils.EnumValuesUpdateActionUtils.buildActions; +import static com.commercetools.sync.commons.utils.EnumValuesUpdateActionUtils.buildAddEnumValuesUpdateActions; +import static com.commercetools.sync.commons.utils.EnumValuesUpdateActionUtils.buildChangeEnumValuesOrderUpdateAction; +import static com.commercetools.sync.commons.utils.EnumValuesUpdateActionUtils.buildMatchingEnumValuesUpdateActions; +import static com.commercetools.sync.commons.utils.EnumValuesUpdateActionUtils.buildRemoveEnumValuesUpdateAction; +import static com.commercetools.sync.commons.utils.PlainEnumValueTestObjects.*; +import static com.commercetools.sync.producttypes.utils.PlainEnumValueUpdateActionUtils.buildChangeLabelAction; import static java.util.Arrays.asList; import static org.assertj.core.api.Assertions.assertThat; @@ -317,8 +316,7 @@ private TriFunction return filterEmptyOptionals( buildChangeLabelAction(attributeDefinitionName, oldEnumValue, - newEnumValue, - ChangePlainEnumValueLabel::of + newEnumValue )); }; } diff --git a/src/test/java/com/commercetools/sync/commons/utils/enums/LocalizedEnumValueTestObjects.java b/src/test/java/com/commercetools/sync/commons/utils/LocalizedEnumValueTestObjects.java similarity index 97% rename from src/test/java/com/commercetools/sync/commons/utils/enums/LocalizedEnumValueTestObjects.java rename to src/test/java/com/commercetools/sync/commons/utils/LocalizedEnumValueTestObjects.java index fb8b234226..1b61d4ac4f 100644 --- a/src/test/java/com/commercetools/sync/commons/utils/enums/LocalizedEnumValueTestObjects.java +++ b/src/test/java/com/commercetools/sync/commons/utils/LocalizedEnumValueTestObjects.java @@ -1,4 +1,4 @@ -package com.commercetools.sync.commons.utils.enums; +package com.commercetools.sync.commons.utils; import io.sphere.sdk.models.LocalizedEnumValue; diff --git a/src/test/java/com/commercetools/sync/commons/utils/enums/PlainEnumValueTestObjects.java b/src/test/java/com/commercetools/sync/commons/utils/PlainEnumValueTestObjects.java similarity index 97% rename from src/test/java/com/commercetools/sync/commons/utils/enums/PlainEnumValueTestObjects.java rename to src/test/java/com/commercetools/sync/commons/utils/PlainEnumValueTestObjects.java index 5b99e05433..efab50622f 100644 --- a/src/test/java/com/commercetools/sync/commons/utils/enums/PlainEnumValueTestObjects.java +++ b/src/test/java/com/commercetools/sync/commons/utils/PlainEnumValueTestObjects.java @@ -1,4 +1,4 @@ -package com.commercetools.sync.commons.utils.enums; +package com.commercetools.sync.commons.utils; import io.sphere.sdk.models.EnumValue; diff --git a/src/test/java/com/commercetools/sync/commons/utils/enums/LocalizedEnumValueUpdateActionUtilsTest.java b/src/test/java/com/commercetools/sync/producttypes/utils/LocalizedEnumValueUpdateActionUtilsTest.java similarity index 82% rename from src/test/java/com/commercetools/sync/commons/utils/enums/LocalizedEnumValueUpdateActionUtilsTest.java rename to src/test/java/com/commercetools/sync/producttypes/utils/LocalizedEnumValueUpdateActionUtilsTest.java index 3614055e76..201d5a3bab 100644 --- a/src/test/java/com/commercetools/sync/commons/utils/enums/LocalizedEnumValueUpdateActionUtilsTest.java +++ b/src/test/java/com/commercetools/sync/producttypes/utils/LocalizedEnumValueUpdateActionUtilsTest.java @@ -1,4 +1,4 @@ -package com.commercetools.sync.commons.utils.enums; +package com.commercetools.sync.producttypes.utils; import io.sphere.sdk.commands.UpdateAction; import io.sphere.sdk.models.LocalizedEnumValue; @@ -10,7 +10,7 @@ import java.util.Optional; -import static com.commercetools.sync.commons.utils.enums.LocalizedEnumValueUpdateActionUtils.buildChangeLabelAction; +import static com.commercetools.sync.producttypes.utils.LocalizedEnumValueUpdateActionUtils.buildChangeLabelAction; import static org.assertj.core.api.Assertions.assertThat; public class LocalizedEnumValueUpdateActionUtilsTest { @@ -23,8 +23,7 @@ public void buildChangeLabelAction_WithDifferentValues_ShouldReturnAction() { final Optional> result = buildChangeLabelAction( "attribute_definition_name_1", old, - newDifferent, - ChangeLocalizedEnumValueLabel::of + newDifferent ); assertThat(result).contains(ChangeLocalizedEnumValueLabel.of("attribute_definition_name_1", newDifferent)); @@ -35,8 +34,7 @@ public void buildChangeLabelAction_WithSameValues_ShouldReturnEmptyOptional() { final Optional> result = buildChangeLabelAction( "attribute_definition_name_1", old, - newSame, - ChangeLocalizedEnumValueLabel::of + newSame ); assertThat(result).isEmpty(); diff --git a/src/test/java/com/commercetools/sync/commons/utils/enums/PlainEnumValueUpdateActionUtilsTest.java b/src/test/java/com/commercetools/sync/producttypes/utils/PlainEnumValueUpdateActionUtilsTest.java similarity index 83% rename from src/test/java/com/commercetools/sync/commons/utils/enums/PlainEnumValueUpdateActionUtilsTest.java rename to src/test/java/com/commercetools/sync/producttypes/utils/PlainEnumValueUpdateActionUtilsTest.java index 70f9f0d338..5e1ffcca0f 100644 --- a/src/test/java/com/commercetools/sync/commons/utils/enums/PlainEnumValueUpdateActionUtilsTest.java +++ b/src/test/java/com/commercetools/sync/producttypes/utils/PlainEnumValueUpdateActionUtilsTest.java @@ -1,4 +1,4 @@ -package com.commercetools.sync.commons.utils.enums; +package com.commercetools.sync.producttypes.utils; import io.sphere.sdk.commands.UpdateAction; import io.sphere.sdk.models.EnumValue; @@ -9,7 +9,7 @@ import java.util.Optional; -import static com.commercetools.sync.commons.utils.enums.PlainEnumValueUpdateActionUtils.buildChangeLabelAction; +import static com.commercetools.sync.producttypes.utils.PlainEnumValueUpdateActionUtils.buildChangeLabelAction; import static org.assertj.core.api.Assertions.assertThat; public class PlainEnumValueUpdateActionUtilsTest { @@ -32,8 +32,7 @@ public void buildChangeLabelAction_WithDifferentValues_ShouldReturnAction() { final Optional> result = buildChangeLabelAction( "attribute_definition_name_1", old, - newDifferent, - ChangePlainEnumValueLabel::of + newDifferent ); assertThat(result).containsInstanceOf(ChangePlainEnumValueLabel.class); @@ -45,8 +44,7 @@ public void buildChangeLabelAction_WithSameValues_ShouldReturnEmptyOptional() { final Optional> result = buildChangeLabelAction( "attribute_definition_name_1", old, - newSame, - ChangePlainEnumValueLabel::of + newSame ); assertThat(result).isEmpty(); diff --git a/src/test/java/com/commercetools/sync/producttypes/utils/producttypeactionutils/BuildLocalizedEnumUpdateActionsTest.java b/src/test/java/com/commercetools/sync/producttypes/utils/producttypeactionutils/BuildLocalizedEnumUpdateActionsTest.java index dfaac07f19..9b473e5b0a 100644 --- a/src/test/java/com/commercetools/sync/producttypes/utils/producttypeactionutils/BuildLocalizedEnumUpdateActionsTest.java +++ b/src/test/java/com/commercetools/sync/producttypes/utils/producttypeactionutils/BuildLocalizedEnumUpdateActionsTest.java @@ -14,7 +14,7 @@ import java.util.Collections; import java.util.List; -import static com.commercetools.sync.commons.utils.enums.LocalizedEnumValueTestObjects.*; +import static com.commercetools.sync.commons.utils.LocalizedEnumValueTestObjects.*; import static com.commercetools.sync.producttypes.utils.LocalizedEnumValueUpdateActionUtils.buildLocalizedEnumValueUpdateActions; import static com.commercetools.sync.producttypes.utils.LocalizedEnumValueUpdateActionUtils.buildLocalizedEnumValuesUpdateActions; import static java.util.Arrays.asList; diff --git a/src/test/java/com/commercetools/sync/producttypes/utils/producttypeactionutils/BuildPlainEnumUpdateActionsTest.java b/src/test/java/com/commercetools/sync/producttypes/utils/producttypeactionutils/BuildPlainEnumUpdateActionsTest.java index 5854c385f4..fe104f4da4 100644 --- a/src/test/java/com/commercetools/sync/producttypes/utils/producttypeactionutils/BuildPlainEnumUpdateActionsTest.java +++ b/src/test/java/com/commercetools/sync/producttypes/utils/producttypeactionutils/BuildPlainEnumUpdateActionsTest.java @@ -14,7 +14,7 @@ import java.util.Collections; import java.util.List; -import static com.commercetools.sync.commons.utils.enums.PlainEnumValueTestObjects.*; +import static com.commercetools.sync.commons.utils.PlainEnumValueTestObjects.*; import static com.commercetools.sync.producttypes.utils.PlainEnumValueUpdateActionUtils.buildEnumValueUpdateActions; import static com.commercetools.sync.producttypes.utils.PlainEnumValueUpdateActionUtils.buildEnumValuesUpdateActions; import static java.util.Arrays.asList; diff --git a/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildLocalizedEnumUpdateActionsTest.java b/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildLocalizedEnumUpdateActionsTest.java index 5afc254d9f..ec8ddbeff9 100644 --- a/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildLocalizedEnumUpdateActionsTest.java +++ b/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildLocalizedEnumUpdateActionsTest.java @@ -10,10 +10,9 @@ import org.junit.Test; import org.junit.rules.ExpectedException; -import java.util.Collections; import java.util.List; -import static com.commercetools.sync.commons.utils.enums.LocalizedEnumValueTestObjects.*; +import static com.commercetools.sync.commons.utils.LocalizedEnumValueTestObjects.*; import static com.commercetools.sync.types.utils.LocalizedEnumValueUpdateActionUtils.buildLocalizedEnumValuesUpdateActions; import static java.util.Arrays.asList; import static java.util.Collections.emptyList; diff --git a/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildPlainEnumUpdateActionsTest.java b/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildPlainEnumUpdateActionsTest.java index 114ebce19d..b13f62a6c4 100644 --- a/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildPlainEnumUpdateActionsTest.java +++ b/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildPlainEnumUpdateActionsTest.java @@ -6,10 +6,9 @@ import io.sphere.sdk.types.commands.updateactions.ChangeEnumValueOrder; import org.junit.Test; -import java.util.Collections; import java.util.List; -import static com.commercetools.sync.commons.utils.enums.PlainEnumValueTestObjects.*; +import static com.commercetools.sync.commons.utils.PlainEnumValueTestObjects.*; import static com.commercetools.sync.types.utils.PlainEnumValueUpdateActionUtils.buildEnumValuesUpdateActions; import static java.util.Arrays.asList; import static java.util.Collections.emptyList; From 9e7aefae702ef22ca020cfa3df7f8dd5ffe2989d Mon Sep 17 00:00:00 2001 From: aoz Date: Tue, 13 Nov 2018 10:42:28 +0100 Subject: [PATCH 045/164] #300 - cme (without max retry handler ) --- .../services/impl/TypeServiceImplIT.java | 58 ++++++++++++ .../sync/integration/types/TypeSyncIT.java | 89 +++++++++++++++++++ .../sync/services/TypeService.java | 13 +++ .../sync/services/impl/TypeServiceImpl.java | 20 +++++ .../commercetools/sync/types/TypeSync.java | 49 +++++++--- 5 files changed, 219 insertions(+), 10 deletions(-) diff --git a/src/integration-test/java/com/commercetools/sync/integration/services/impl/TypeServiceImplIT.java b/src/integration-test/java/com/commercetools/sync/integration/services/impl/TypeServiceImplIT.java index 7366b063b2..555674ef12 100644 --- a/src/integration-test/java/com/commercetools/sync/integration/services/impl/TypeServiceImplIT.java +++ b/src/integration-test/java/com/commercetools/sync/integration/services/impl/TypeServiceImplIT.java @@ -17,6 +17,7 @@ import io.sphere.sdk.types.commands.updateactions.ChangeName; import io.sphere.sdk.types.queries.TypeQuery; import io.sphere.sdk.utils.CompletableFutureUtils; +import org.apache.commons.lang3.StringUtils; import org.junit.AfterClass; import org.junit.Before; import org.junit.Ignore; @@ -37,6 +38,7 @@ import static com.commercetools.sync.integration.types.utils.TypeITUtils.TYPE_DESCRIPTION_1; import static com.commercetools.sync.integration.types.utils.TypeITUtils.TYPE_KEY_1; import static com.commercetools.sync.integration.types.utils.TypeITUtils.TYPE_NAME_1; +import static com.commercetools.tests.utils.CompletionStageUtil.executeBlocking; import static java.lang.String.format; import static java.util.Collections.singleton; import static java.util.Collections.singletonList; @@ -320,4 +322,60 @@ public void updateType_WithInvalidChanges_ShouldCompleteExceptionally() { .toCompletableFuture().join(); } + @Test + public void fetchType_WithExistingTypeKey_ShouldFetchType() { + final Optional typeOptional = CTP_TARGET_CLIENT + .execute(TypeQuery.of() + .withPredicates(typeQueryModel -> typeQueryModel.key().is(OLD_TYPE_KEY))) + .toCompletableFuture().join().head(); + assertThat(typeOptional).isNotNull(); + + final Optional fetchedTypeOptional = + executeBlocking(typeService.fetchType(OLD_TYPE_KEY)); + assertThat(fetchedTypeOptional).isEqualTo(typeOptional); + } + + @Test + public void fetchType_WithBlankKey_ShouldNotFetchType() { + final Optional fetchedTypeOptional = + executeBlocking(typeService.fetchType(StringUtils.EMPTY)); + assertThat(fetchedTypeOptional).isEmpty(); + } + + @Test + public void fetchType_WithNullKey_ShouldNotFetchType() { + final Optional fetchedTypeOptional = + executeBlocking(typeService.fetchType(null)); + assertThat(fetchedTypeOptional).isEmpty(); + } + + @Test + public void fetchType_WithBadGateWayExceptionAlways_ShouldFail() { + // Mock sphere client to return BadeGatewayException on any request. + final SphereClient spyClient = spy(CTP_TARGET_CLIENT); + when(spyClient.execute(any(TypeQuery.class))) + .thenReturn(CompletableFutureUtils.exceptionallyCompletedFuture(new BadGatewayException())) + .thenCallRealMethod(); + final TypeSyncOptions spyOptions = TypeSyncOptionsBuilder.of(spyClient) + .errorCallback( + (errorMessage, exception) -> { + errorCallBackMessages + .add(errorMessage); + errorCallBackExceptions + .add(exception); + }) + .build(); + final TypeService spyTypeService = new TypeServiceImpl(spyOptions); + + final Optional fetchedTypeOptional = + executeBlocking(spyTypeService.fetchType(OLD_TYPE_KEY)); + assertThat(fetchedTypeOptional).isEmpty(); + assertThat(errorCallBackExceptions).hasSize(1); + assertThat(errorCallBackExceptions.get(0).getCause()).isExactlyInstanceOf(BadGatewayException.class); + assertThat(errorCallBackMessages).hasSize(1); + assertThat(errorCallBackMessages.get(0)) + .isEqualToIgnoringCase(format("Failed to fetch types with keys: '%s'. Reason: %s", + OLD_TYPE_KEY, errorCallBackExceptions.get(0))); + } + } diff --git a/src/integration-test/java/com/commercetools/sync/integration/types/TypeSyncIT.java b/src/integration-test/java/com/commercetools/sync/integration/types/TypeSyncIT.java index d4febe878b..81d199352a 100644 --- a/src/integration-test/java/com/commercetools/sync/integration/types/TypeSyncIT.java +++ b/src/integration-test/java/com/commercetools/sync/integration/types/TypeSyncIT.java @@ -8,6 +8,9 @@ import com.commercetools.sync.types.TypeSyncOptions; import com.commercetools.sync.types.TypeSyncOptionsBuilder; import com.commercetools.sync.types.helpers.TypeSyncStatistics; +import io.sphere.sdk.client.BadGatewayException; +import io.sphere.sdk.client.ConcurrentModificationException; +import io.sphere.sdk.client.SphereClient; import io.sphere.sdk.models.EnumValue; import io.sphere.sdk.models.LocalizedEnumValue; import io.sphere.sdk.models.LocalizedString; @@ -21,13 +24,17 @@ import io.sphere.sdk.types.Type; import io.sphere.sdk.types.TypeDraft; import io.sphere.sdk.types.TypeDraftBuilder; +import io.sphere.sdk.types.commands.TypeUpdateCommand; import io.sphere.sdk.types.queries.TypeQuery; +import io.sphere.sdk.utils.CompletableFutureUtils; import org.junit.AfterClass; import org.junit.Before; import org.junit.Test; import org.mockito.Mockito; import javax.annotation.Nonnull; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Locale; import java.util.Optional; @@ -51,6 +58,7 @@ import static com.commercetools.sync.integration.types.utils.TypeITUtils.getTypeByKey; import static com.commercetools.sync.integration.types.utils.TypeITUtils.populateSourceProject; import static com.commercetools.sync.integration.types.utils.TypeITUtils.populateTargetProject; +import static java.lang.String.format; import static java.util.Arrays.asList; import static java.util.Collections.singletonList; import static java.util.concurrent.CompletableFuture.supplyAsync; @@ -743,4 +751,85 @@ private static void assertLocalizedEnumsValuesAreEqual(@Nonnull final List errorMessages = new ArrayList<>(); + final List errors = new ArrayList<>(); + + final TypeSyncOptions typeSyncOptions = + TypeSyncOptionsBuilder.of(spyClient) + .errorCallback((errorMessage, error) -> { + errorMessages.add(errorMessage); + errors.add(error); + }) + .build(); + + final TypeDraft typeDraft = TypeDraftBuilder.of( + TYPE_KEY_1, + TYPE_NAME_2, + ResourceTypeIdsSetBuilder.of().addCategories().build()) + .description(TYPE_DESCRIPTION_2) + .fieldDefinitions(singletonList(FIELD_DEFINITION_1)) + .build(); + + final TypeSync typeSync = new TypeSync(typeSyncOptions); + final TypeSyncStatistics statistics = typeSync.sync(Collections.singletonList(typeDraft)) + .toCompletableFuture() + .join(); + + // Test and assertion + AssertionsForStatistics.assertThat(statistics).hasValues(1, 0, 0, 1); + assertThat(errorMessages).hasSize(1); + assertThat(errors).hasSize(1); + + assertThat(errors.get(0)).isExactlyInstanceOf(BadGatewayException.class); + assertThat(errorMessages.get(0)) + .contains(format("Failed to update type of key '%s'.", + typeDraft.getKey(), errors.get(0))); + } + + @Test + public void sync_WithChangedTypeButConcurrentModificationException_ShouldRetryToUpdateNewTypeWithSuccess() { + + // Mock sphere client to return ConcurrentModification on the first update request. + final SphereClient spyClient = spy(CTP_TARGET_CLIENT); + when(spyClient.execute(any(TypeUpdateCommand.class))) + .thenReturn(CompletableFutureUtils.exceptionallyCompletedFuture(new ConcurrentModificationException())) + .thenCallRealMethod(); + + final List errorMessages = new ArrayList<>(); + final List errors = new ArrayList<>(); + + final TypeSyncOptions typeSyncOptions = + TypeSyncOptionsBuilder.of(spyClient) + .errorCallback((errorMessage, error) -> { + errorMessages.add(errorMessage); + errors.add(error); + }) + .build(); + + final TypeDraft typeDraft = TypeDraftBuilder.of( + TYPE_KEY_1, + TYPE_NAME_2, + ResourceTypeIdsSetBuilder.of().addCategories().build()) + .description(TYPE_DESCRIPTION_2) + .fieldDefinitions(singletonList(FIELD_DEFINITION_1)) + .build(); + + final TypeSync typeSync = new TypeSync(typeSyncOptions); + final TypeSyncStatistics statistics = typeSync.sync(Collections.singletonList(typeDraft)) + .toCompletableFuture() + .join(); + + // Test and assertion + AssertionsForStatistics.assertThat(statistics).hasValues(1, 0, 1, 0); + assertThat(errorMessages).isEmpty(); + assertThat(errors).isEmpty(); + } + } diff --git a/src/main/java/com/commercetools/sync/services/TypeService.java b/src/main/java/com/commercetools/sync/services/TypeService.java index 2336fd8652..cf226ab3ad 100644 --- a/src/main/java/com/commercetools/sync/services/TypeService.java +++ b/src/main/java/com/commercetools/sync/services/TypeService.java @@ -2,11 +2,13 @@ import io.sphere.sdk.channels.Channel; +import io.sphere.sdk.client.SphereClient; import io.sphere.sdk.commands.UpdateAction; import io.sphere.sdk.types.Type; import io.sphere.sdk.types.TypeDraft; import javax.annotation.Nonnull; +import javax.annotation.Nullable; import java.util.List; import java.util.Optional; import java.util.Set; @@ -42,6 +44,17 @@ public interface TypeService { @Nonnull CompletionStage> fetchMatchingTypesByKeys(@Nonnull final Set keys); + /** + * Given a type key, this method fetches a type that matches this given key in the CTP project defined in a + * potentially injected {@link SphereClient}. If there is no matching type an empty {@link Optional} will be + * returned in the returned future. + * + * @param key the key of the type to fetch. + * @return {@link CompletionStage}<{@link Optional}> in which the result of it's completion contains an + * {@link Optional} that contains the matching {@link Type} if exists, otherwise empty. + */ + @Nonnull + CompletionStage> fetchType(@Nullable final String key); /** * Creates new type from {@code typeDraft}. diff --git a/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java b/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java index f9480e0286..da4a3b5b6c 100644 --- a/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java +++ b/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java @@ -6,6 +6,7 @@ import com.commercetools.sync.services.TypeService; import com.commercetools.sync.types.TypeSyncOptions; import io.sphere.sdk.commands.UpdateAction; +import io.sphere.sdk.queries.PagedResult; import io.sphere.sdk.queries.QueryExecutionUtils; import io.sphere.sdk.types.Type; import io.sphere.sdk.types.TypeDraft; @@ -15,6 +16,7 @@ import io.sphere.sdk.types.queries.TypeQueryBuilder; import javax.annotation.Nonnull; +import javax.annotation.Nullable; import java.util.Collections; import java.util.List; import java.util.Map; @@ -25,7 +27,9 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.function.Consumer; +import static java.lang.String.format; import static java.util.stream.Collectors.toSet; +import static org.apache.http.util.TextUtils.isBlank; /** * Implementation of TypeService interface. @@ -73,6 +77,22 @@ public CompletionStage> fetchMatchingTypesByKeys(@Nonnull final Set> fetchType(@Nullable String key) { + if (isBlank(key)) { + return CompletableFuture.completedFuture(Optional.empty()); + } + + return syncOptions.getCtpClient() + .execute(TypeQuery.of().plusPredicates(typeQueryModel -> typeQueryModel.key().is(key))) + .thenApply(PagedResult::head) + .exceptionally(sphereException -> { + syncOptions.applyErrorCallback(format(FETCH_FAILED, key, sphereException), sphereException); + return Optional.empty(); + }); + } + @Nonnull @Override public CompletionStage createType(@Nonnull final TypeDraft typeDraft) { diff --git a/src/main/java/com/commercetools/sync/types/TypeSync.java b/src/main/java/com/commercetools/sync/types/TypeSync.java index 061c080e48..aa0bef9a21 100644 --- a/src/main/java/com/commercetools/sync/types/TypeSync.java +++ b/src/main/java/com/commercetools/sync/types/TypeSync.java @@ -5,6 +5,7 @@ import com.commercetools.sync.services.impl.TypeServiceImpl; import com.commercetools.sync.types.helpers.TypeSyncStatistics; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +import io.sphere.sdk.client.ConcurrentModificationException; import io.sphere.sdk.commands.UpdateAction; import io.sphere.sdk.models.WithKey; import io.sphere.sdk.types.Type; @@ -38,6 +39,7 @@ public class TypeSync extends BaseSync createType(@Nonnull final TypeDraft typeDraft) { * Given an existing {@link Type} and a new {@link TypeDraft}, the method calculates all the * update actions required to synchronize the existing type to be the same as the new one. If there are * update actions found, a request is made to CTP to update the existing type, otherwise it doesn't issue a - * request. + * request. If the update request failed due to a {@link ConcurrentModificationException}, the method recalculates + * the update actions required for syncing the {@link Type} and reissues the update request. * *

The {@code statistics} instance is updated accordingly to whether the CTP request was carried - * out successfully or not. If an exception was thrown on executing the request to CTP, the error handling method - * is called. + * out successfully or not. If an exception was thrown on executing the request to CTP, + * the optional error callback specified in the {@code syncOptions} is called. * * @param oldType existing type that could be updated. * @param newType draft containing data that could differ from data in {@code oldType}. @@ -255,15 +258,41 @@ private CompletionStage updateType(@Nonnull final Type oldType, if (!updateActionsAfterCallback.isEmpty()) { return typeService.updateType(oldType, updateActionsAfterCallback) - .thenAccept(updatedType -> statistics.incrementUpdated()) - .exceptionally(exception -> { - final String errorMessage = format(CTP_TYPE_UPDATE_FAILED, newType.getKey()); - handleError(errorMessage, exception, 1); - - return null; - }); + .handle((updatedType, sphereException) -> sphereException) + .thenCompose(sphereException -> { + if (sphereException != null) { + return executeSupplierIfConcurrentModificationException( + sphereException, + () -> fetchAndUpdate(oldType, newType), + () -> { + final String errorMessage = format(CTP_TYPE_UPDATE_FAILED, + newType.getKey()); + handleError(errorMessage, sphereException, 1); + + return completedFuture(null); + }); + } else { + statistics.incrementUpdated(); + return completedFuture(null); + } + }); } return completedFuture(null); } + + @SuppressFBWarnings("NP_NONNULL_PARAM_VIOLATION") + private CompletionStage fetchAndUpdate(@Nonnull final Type oldType, + @Nonnull final TypeDraft newType) { + + final String key = oldType.getKey(); + return typeService.fetchType(key) + .thenCompose(typeOptional -> + typeOptional + .map(fetchedType -> updateType(fetchedType, newType)) + .orElseGet(() -> { + handleError(format(CTP_TYPE_UPDATE_FAILED, key, FETCH_ON_RETRY), null, 1); + return CompletableFuture.completedFuture(null); + })); + } } From c9cf9c91ae930477fcf476d46ccb462f8ad47ce2 Mon Sep 17 00:00:00 2001 From: aoz Date: Tue, 13 Nov 2018 17:02:14 +0100 Subject: [PATCH 046/164] #300 - fix check style errors --- config/checkstyle/checkstyle.xml | 6 ++- .../sync/integration/types/TypeSyncIT.java | 12 ++--- .../sync/services/impl/TypeServiceImpl.java | 5 +- .../EnumValuesUpdateActionUtilsTest.java | 2 +- ...s.java => LocalizedEnumValueFixtures.java} | 50 +++++++++---------- .../commons/utils/PlainEnumValueFixtures.java | 43 ++++++++++++++++ .../utils/PlainEnumValueTestObjects.java | 45 ----------------- .../BuildLocalizedEnumUpdateActionsTest.java | 2 +- .../BuildPlainEnumUpdateActionsTest.java | 6 +-- .../types/TypeSyncOptionsBuilderTest.java | 1 - .../FieldDefinitionUpdateActionUtilsTest.java | 13 ++--- .../BuildLocalizedEnumUpdateActionsTest.java | 2 +- .../BuildPlainEnumUpdateActionsTest.java | 2 +- 13 files changed, 92 insertions(+), 97 deletions(-) rename src/test/java/com/commercetools/sync/commons/utils/{LocalizedEnumValueTestObjects.java => LocalizedEnumValueFixtures.java} (60%) create mode 100644 src/test/java/com/commercetools/sync/commons/utils/PlainEnumValueFixtures.java delete mode 100644 src/test/java/com/commercetools/sync/commons/utils/PlainEnumValueTestObjects.java diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml index c67a8025ea..27cd1caf37 100644 --- a/config/checkstyle/checkstyle.xml +++ b/config/checkstyle/checkstyle.xml @@ -70,7 +70,9 @@ + com.commercetools.sync.products.utils.productvariantupdateactionutils.prices.PriceFixtures, + com.commercetools.sync.commons.utils.LocalizedEnumValueFixtures, + com.commercetools.sync.commons.utils.PlainEnumValueFixtures"/> @@ -206,4 +208,4 @@ - \ No newline at end of file + diff --git a/src/integration-test/java/com/commercetools/sync/integration/types/TypeSyncIT.java b/src/integration-test/java/com/commercetools/sync/integration/types/TypeSyncIT.java index 81d199352a..3426684bad 100644 --- a/src/integration-test/java/com/commercetools/sync/integration/types/TypeSyncIT.java +++ b/src/integration-test/java/com/commercetools/sync/integration/types/TypeSyncIT.java @@ -756,7 +756,8 @@ public void sync_WithChangedTypeButBadGatewayException_ShouldFailUpdateType() { // Mock sphere client to return BadGatewayException on the first update request. final SphereClient spyClient = spy(CTP_TARGET_CLIENT); when(spyClient.execute(any(TypeUpdateCommand.class))) - .thenReturn(CompletableFutureUtils.exceptionallyCompletedFuture(new BadGatewayException())); + .thenReturn(CompletableFutureUtils.exceptionallyCompletedFuture( + new Exception("bad gateway issue on test", new BadGatewayException()))); final List errorMessages = new ArrayList<>(); final List errors = new ArrayList<>(); @@ -787,10 +788,8 @@ public void sync_WithChangedTypeButBadGatewayException_ShouldFailUpdateType() { assertThat(errorMessages).hasSize(1); assertThat(errors).hasSize(1); - assertThat(errors.get(0)).isExactlyInstanceOf(BadGatewayException.class); - assertThat(errorMessages.get(0)) - .contains(format("Failed to update type of key '%s'.", - typeDraft.getKey(), errors.get(0))); + assertThat(errors.get(0).getCause()).isExactlyInstanceOf(BadGatewayException.class); + assertThat(errorMessages.get(0)).contains(format("Failed to update type of key '%s'.", typeDraft.getKey())); } @Test @@ -799,7 +798,8 @@ public void sync_WithChangedTypeButConcurrentModificationException_ShouldRetryTo // Mock sphere client to return ConcurrentModification on the first update request. final SphereClient spyClient = spy(CTP_TARGET_CLIENT); when(spyClient.execute(any(TypeUpdateCommand.class))) - .thenReturn(CompletableFutureUtils.exceptionallyCompletedFuture(new ConcurrentModificationException())) + .thenReturn(CompletableFutureUtils.exceptionallyCompletedFuture( + new Exception("concurrency modification issue on test", new ConcurrentModificationException()))) .thenCallRealMethod(); final List errorMessages = new ArrayList<>(); diff --git a/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java b/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java index da4a3b5b6c..f963af4acd 100644 --- a/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java +++ b/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java @@ -79,7 +79,7 @@ public CompletionStage> fetchMatchingTypesByKeys(@Nonnull final Set> fetchType(@Nullable String key) { + public CompletionStage> fetchType(@Nullable final String key) { if (isBlank(key)) { return CompletableFuture.completedFuture(Optional.empty()); } @@ -88,7 +88,8 @@ public CompletionStage> fetchType(@Nullable String key) { .execute(TypeQuery.of().plusPredicates(typeQueryModel -> typeQueryModel.key().is(key))) .thenApply(PagedResult::head) .exceptionally(sphereException -> { - syncOptions.applyErrorCallback(format(FETCH_FAILED, key, sphereException), sphereException); + syncOptions.applyErrorCallback(format(FETCH_FAILED, key, sphereException), + sphereException); return Optional.empty(); }); } diff --git a/src/test/java/com/commercetools/sync/commons/utils/EnumValuesUpdateActionUtilsTest.java b/src/test/java/com/commercetools/sync/commons/utils/EnumValuesUpdateActionUtilsTest.java index 9c10d925e9..ff78e239df 100644 --- a/src/test/java/com/commercetools/sync/commons/utils/EnumValuesUpdateActionUtilsTest.java +++ b/src/test/java/com/commercetools/sync/commons/utils/EnumValuesUpdateActionUtilsTest.java @@ -20,7 +20,7 @@ import static com.commercetools.sync.commons.utils.EnumValuesUpdateActionUtils.buildChangeEnumValuesOrderUpdateAction; import static com.commercetools.sync.commons.utils.EnumValuesUpdateActionUtils.buildMatchingEnumValuesUpdateActions; import static com.commercetools.sync.commons.utils.EnumValuesUpdateActionUtils.buildRemoveEnumValuesUpdateAction; -import static com.commercetools.sync.commons.utils.PlainEnumValueTestObjects.*; +import static com.commercetools.sync.commons.utils.PlainEnumValueFixtures.*; import static com.commercetools.sync.producttypes.utils.PlainEnumValueUpdateActionUtils.buildChangeLabelAction; import static java.util.Arrays.asList; import static org.assertj.core.api.Assertions.assertThat; diff --git a/src/test/java/com/commercetools/sync/commons/utils/LocalizedEnumValueTestObjects.java b/src/test/java/com/commercetools/sync/commons/utils/LocalizedEnumValueFixtures.java similarity index 60% rename from src/test/java/com/commercetools/sync/commons/utils/LocalizedEnumValueTestObjects.java rename to src/test/java/com/commercetools/sync/commons/utils/LocalizedEnumValueFixtures.java index 1b61d4ac4f..02daedaeed 100644 --- a/src/test/java/com/commercetools/sync/commons/utils/LocalizedEnumValueTestObjects.java +++ b/src/test/java/com/commercetools/sync/commons/utils/LocalizedEnumValueFixtures.java @@ -6,8 +6,9 @@ import static io.sphere.sdk.models.LocalizedString.ofEnglish; import static java.util.Arrays.asList; +import static java.util.Collections.unmodifiableList; -public final class LocalizedEnumValueTestObjects { +public final class LocalizedEnumValueFixtures { public static final LocalizedEnumValue ENUM_VALUE_A = LocalizedEnumValue.of("a", ofEnglish("label_a")); public static final LocalizedEnumValue ENUM_VALUE_A_DIFFERENT_LABEL = @@ -16,30 +17,27 @@ public final class LocalizedEnumValueTestObjects { public static final LocalizedEnumValue ENUM_VALUE_C = LocalizedEnumValue.of("c", ofEnglish("label_c")); public static final LocalizedEnumValue ENUM_VALUE_D = LocalizedEnumValue.of("d", ofEnglish("label_d")); - public static final List ENUM_VALUES_ABC = asList(ENUM_VALUE_A, ENUM_VALUE_B, ENUM_VALUE_C); - public static final List ENUM_VALUES_AB = asList(ENUM_VALUE_A, ENUM_VALUE_B); - public static final List ENUM_VALUES_ABB = asList(ENUM_VALUE_A, ENUM_VALUE_B, ENUM_VALUE_B); - public static final List ENUM_VALUES_ABD = asList(ENUM_VALUE_A, ENUM_VALUE_B, ENUM_VALUE_D); - public static final List ENUM_VALUES_ABCD = asList( - ENUM_VALUE_A, - ENUM_VALUE_B, - ENUM_VALUE_C, - ENUM_VALUE_D - ); - public static final List ENUM_VALUES_CAB = asList(ENUM_VALUE_C, ENUM_VALUE_A, ENUM_VALUE_B); - public static final List ENUM_VALUES_CB = asList(ENUM_VALUE_C, ENUM_VALUE_B); - public static final List ENUM_VALUES_ACBD = asList( - ENUM_VALUE_A, - ENUM_VALUE_C, - ENUM_VALUE_B, - ENUM_VALUE_D - ); - public static final List ENUM_VALUES_ADBC = asList( - ENUM_VALUE_A, - ENUM_VALUE_D, - ENUM_VALUE_B, - ENUM_VALUE_C - ); - public static final List ENUM_VALUES_CBD = asList(ENUM_VALUE_C, ENUM_VALUE_B, ENUM_VALUE_D); + public static final List ENUM_VALUES_ABC = + unmodifiableList(asList(ENUM_VALUE_A, ENUM_VALUE_B, ENUM_VALUE_C)); + public static final List ENUM_VALUES_AB = + unmodifiableList(asList(ENUM_VALUE_A, ENUM_VALUE_B)); + public static final List ENUM_VALUES_ABB = + unmodifiableList(asList(ENUM_VALUE_A, ENUM_VALUE_B, ENUM_VALUE_B)); + public static final List ENUM_VALUES_ABD = + unmodifiableList(asList(ENUM_VALUE_A, ENUM_VALUE_B, ENUM_VALUE_D)); + public static final List ENUM_VALUES_ABCD = + unmodifiableList(asList(ENUM_VALUE_A, ENUM_VALUE_B, ENUM_VALUE_C, ENUM_VALUE_D)); + public static final List ENUM_VALUES_CAB = + unmodifiableList(asList(ENUM_VALUE_C, ENUM_VALUE_A, ENUM_VALUE_B)); + public static final List ENUM_VALUES_CB = + unmodifiableList(asList(ENUM_VALUE_C, ENUM_VALUE_B)); + public static final List ENUM_VALUES_ACBD = + unmodifiableList(asList(ENUM_VALUE_A, ENUM_VALUE_C, ENUM_VALUE_B, ENUM_VALUE_D)); + public static final List ENUM_VALUES_ADBC = + unmodifiableList(asList(ENUM_VALUE_A, ENUM_VALUE_D, ENUM_VALUE_B, ENUM_VALUE_C)); + public static final List ENUM_VALUES_CBD = + unmodifiableList(asList(ENUM_VALUE_C, ENUM_VALUE_B, ENUM_VALUE_D)); + private LocalizedEnumValueFixtures() { + } } diff --git a/src/test/java/com/commercetools/sync/commons/utils/PlainEnumValueFixtures.java b/src/test/java/com/commercetools/sync/commons/utils/PlainEnumValueFixtures.java new file mode 100644 index 0000000000..297062a83c --- /dev/null +++ b/src/test/java/com/commercetools/sync/commons/utils/PlainEnumValueFixtures.java @@ -0,0 +1,43 @@ +package com.commercetools.sync.commons.utils; + +import io.sphere.sdk.models.EnumValue; + +import java.util.List; + +import static java.util.Arrays.asList; +import static java.util.Collections.unmodifiableList; + +public final class PlainEnumValueFixtures { + + public static final EnumValue ENUM_VALUE_A = EnumValue.of("a", "label_a"); + public static final EnumValue ENUM_VALUE_A_DIFFERENT_LABEL = EnumValue.of("a", "label_a_diff"); + public static final EnumValue ENUM_VALUE_B = EnumValue.of("b", "label_b"); + public static final EnumValue ENUM_VALUE_C = EnumValue.of("c", "label_c"); + public static final EnumValue ENUM_VALUE_D = EnumValue.of("d", "label_d"); + + public static final List ENUM_VALUES_ABC = + unmodifiableList(asList(ENUM_VALUE_A, ENUM_VALUE_B, ENUM_VALUE_C)); + public static final List ENUM_VALUES_AB = + unmodifiableList(asList(ENUM_VALUE_A, ENUM_VALUE_B)); + public static final List ENUM_VALUES_AB_WITH_DIFFERENT_LABEL = + unmodifiableList(asList(ENUM_VALUE_A_DIFFERENT_LABEL, ENUM_VALUE_B)); + public static final List ENUM_VALUES_ABB = + unmodifiableList(asList(ENUM_VALUE_A, ENUM_VALUE_B, ENUM_VALUE_B)); + public static final List ENUM_VALUES_ABD = + unmodifiableList(asList(ENUM_VALUE_A, ENUM_VALUE_B, ENUM_VALUE_D)); + public static final List ENUM_VALUES_ABCD = + unmodifiableList(asList(ENUM_VALUE_A, ENUM_VALUE_B, ENUM_VALUE_C, ENUM_VALUE_D)); + public static final List ENUM_VALUES_CAB = + unmodifiableList(asList(ENUM_VALUE_C, ENUM_VALUE_A, ENUM_VALUE_B)); + public static final List ENUM_VALUES_CB = + unmodifiableList(asList(ENUM_VALUE_C, ENUM_VALUE_B)); + public static final List ENUM_VALUES_ACBD = + unmodifiableList(asList(ENUM_VALUE_A, ENUM_VALUE_C, ENUM_VALUE_B, ENUM_VALUE_D)); + public static final List ENUM_VALUES_ADBC = + unmodifiableList(asList(ENUM_VALUE_A, ENUM_VALUE_D, ENUM_VALUE_B, ENUM_VALUE_C)); + public static final List ENUM_VALUES_CBD = + unmodifiableList(asList(ENUM_VALUE_C, ENUM_VALUE_B, ENUM_VALUE_D)); + + private PlainEnumValueFixtures() { + } +} diff --git a/src/test/java/com/commercetools/sync/commons/utils/PlainEnumValueTestObjects.java b/src/test/java/com/commercetools/sync/commons/utils/PlainEnumValueTestObjects.java deleted file mode 100644 index efab50622f..0000000000 --- a/src/test/java/com/commercetools/sync/commons/utils/PlainEnumValueTestObjects.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.commercetools.sync.commons.utils; - -import io.sphere.sdk.models.EnumValue; - -import java.util.List; - -import static java.util.Arrays.asList; - -public final class PlainEnumValueTestObjects { - - public static final EnumValue ENUM_VALUE_A = EnumValue.of("a", "label_a"); - public static final EnumValue ENUM_VALUE_A_DIFFERENT_LABEL = EnumValue.of("a", "label_a_diff"); - public static final EnumValue ENUM_VALUE_B = EnumValue.of("b", "label_b"); - public static final EnumValue ENUM_VALUE_C = EnumValue.of("c", "label_c"); - public static final EnumValue ENUM_VALUE_D = EnumValue.of("d", "label_d"); - - public static final List ENUM_VALUES_ABC = asList(ENUM_VALUE_A, ENUM_VALUE_B, ENUM_VALUE_C); - public static final List ENUM_VALUES_AB = asList(ENUM_VALUE_A, ENUM_VALUE_B); - public static final List ENUM_VALUES_AB_WITH_DIFFERENT_LABEL = - asList(ENUM_VALUE_A_DIFFERENT_LABEL, ENUM_VALUE_B); - public static final List ENUM_VALUES_ABB = asList(ENUM_VALUE_A, ENUM_VALUE_B, ENUM_VALUE_B); - public static final List ENUM_VALUES_ABD = asList(ENUM_VALUE_A, ENUM_VALUE_B, ENUM_VALUE_D); - public static final List ENUM_VALUES_ABCD = asList( - ENUM_VALUE_A, - ENUM_VALUE_B, - ENUM_VALUE_C, - ENUM_VALUE_D - ); - public static final List ENUM_VALUES_CAB = asList(ENUM_VALUE_C, ENUM_VALUE_A, ENUM_VALUE_B); - public static final List ENUM_VALUES_CB = asList(ENUM_VALUE_C, ENUM_VALUE_B); - public static final List ENUM_VALUES_ACBD = asList( - ENUM_VALUE_A, - ENUM_VALUE_C, - ENUM_VALUE_B, - ENUM_VALUE_D - ); - public static final List ENUM_VALUES_ADBC = asList( - ENUM_VALUE_A, - ENUM_VALUE_D, - ENUM_VALUE_B, - ENUM_VALUE_C - ); - public static final List ENUM_VALUES_CBD = asList(ENUM_VALUE_C, ENUM_VALUE_B, ENUM_VALUE_D); - -} diff --git a/src/test/java/com/commercetools/sync/producttypes/utils/producttypeactionutils/BuildLocalizedEnumUpdateActionsTest.java b/src/test/java/com/commercetools/sync/producttypes/utils/producttypeactionutils/BuildLocalizedEnumUpdateActionsTest.java index 9b473e5b0a..0538364130 100644 --- a/src/test/java/com/commercetools/sync/producttypes/utils/producttypeactionutils/BuildLocalizedEnumUpdateActionsTest.java +++ b/src/test/java/com/commercetools/sync/producttypes/utils/producttypeactionutils/BuildLocalizedEnumUpdateActionsTest.java @@ -14,7 +14,7 @@ import java.util.Collections; import java.util.List; -import static com.commercetools.sync.commons.utils.LocalizedEnumValueTestObjects.*; +import static com.commercetools.sync.commons.utils.LocalizedEnumValueFixtures.*; import static com.commercetools.sync.producttypes.utils.LocalizedEnumValueUpdateActionUtils.buildLocalizedEnumValueUpdateActions; import static com.commercetools.sync.producttypes.utils.LocalizedEnumValueUpdateActionUtils.buildLocalizedEnumValuesUpdateActions; import static java.util.Arrays.asList; diff --git a/src/test/java/com/commercetools/sync/producttypes/utils/producttypeactionutils/BuildPlainEnumUpdateActionsTest.java b/src/test/java/com/commercetools/sync/producttypes/utils/producttypeactionutils/BuildPlainEnumUpdateActionsTest.java index fe104f4da4..df54878965 100644 --- a/src/test/java/com/commercetools/sync/producttypes/utils/producttypeactionutils/BuildPlainEnumUpdateActionsTest.java +++ b/src/test/java/com/commercetools/sync/producttypes/utils/producttypeactionutils/BuildPlainEnumUpdateActionsTest.java @@ -14,7 +14,7 @@ import java.util.Collections; import java.util.List; -import static com.commercetools.sync.commons.utils.PlainEnumValueTestObjects.*; +import static com.commercetools.sync.commons.utils.PlainEnumValueFixtures.*; import static com.commercetools.sync.producttypes.utils.PlainEnumValueUpdateActionUtils.buildEnumValueUpdateActions; import static com.commercetools.sync.producttypes.utils.PlainEnumValueUpdateActionUtils.buildEnumValuesUpdateActions; import static java.util.Arrays.asList; @@ -93,9 +93,9 @@ public void buildPlainEnumUpdateActions_WithIdenticalPlainEnum_ShouldNotBuildUpd @Test public void buildPlainEnumUpdateActions_WithDuplicatePlainEnumValues_ShouldTriggerDuplicateKeyError() { expectedException.expect(DuplicateKeyException.class); - expectedException.expectMessage("Enum Values have duplicated keys. Attribute definition name: " + expectedException.expectMessage("Enum Values have duplicated keys. Definition name: " + "'attribute_definition_name_1', Duplicated enum value: 'b'. Enum Values are expected to be unique inside " - + "their attribute definition."); + + "their definition."); buildEnumValuesUpdateActions( "attribute_definition_name_1", diff --git a/src/test/java/com/commercetools/sync/types/TypeSyncOptionsBuilderTest.java b/src/test/java/com/commercetools/sync/types/TypeSyncOptionsBuilderTest.java index 9e55eb4123..7eeb3fe00c 100644 --- a/src/test/java/com/commercetools/sync/types/TypeSyncOptionsBuilderTest.java +++ b/src/test/java/com/commercetools/sync/types/TypeSyncOptionsBuilderTest.java @@ -9,7 +9,6 @@ import io.sphere.sdk.types.commands.updateactions.ChangeName; import org.junit.Test; -import java.util.Collections; import java.util.List; import java.util.Optional; import java.util.function.BiConsumer; diff --git a/src/test/java/com/commercetools/sync/types/utils/FieldDefinitionUpdateActionUtilsTest.java b/src/test/java/com/commercetools/sync/types/utils/FieldDefinitionUpdateActionUtilsTest.java index 14448fab1e..584b679b8e 100644 --- a/src/test/java/com/commercetools/sync/types/utils/FieldDefinitionUpdateActionUtilsTest.java +++ b/src/test/java/com/commercetools/sync/types/utils/FieldDefinitionUpdateActionUtilsTest.java @@ -1,6 +1,5 @@ package com.commercetools.sync.types.utils; -import com.commercetools.sync.commons.exceptions.BuildUpdateActionException; import io.sphere.sdk.commands.UpdateAction; import io.sphere.sdk.models.EnumValue; import io.sphere.sdk.models.LocalizedEnumValue; @@ -16,8 +15,6 @@ import org.junit.BeforeClass; import org.junit.Test; -import java.util.Arrays; -import java.util.Collections; import java.util.List; import java.util.Optional; @@ -25,10 +22,10 @@ import static com.commercetools.sync.types.utils.FieldDefinitionUpdateActionUtils.buildActions; import static com.commercetools.sync.types.utils.FieldDefinitionUpdateActionUtils.buildChangeLabelUpdateAction; import static io.sphere.sdk.models.LocalizedString.ofEnglish; -import static java.util.Arrays.*; +import static java.util.Arrays.asList; import static java.util.Collections.emptyList; +import static java.util.Collections.singletonList; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; public class FieldDefinitionUpdateActionUtilsTest { private static final String FIELD_NAME_1 = "fieldName1"; @@ -89,7 +86,7 @@ public void buildActions_WithSameValues_ShouldReturnEmpty() { @Test public void buildActions_WithNewPlainEnum_ShouldReturnAddEnumValueAction() { final FieldDefinition oldFieldDefinition = FieldDefinition.of( - EnumFieldType.of(asList(ENUM_VALUE_A)), + EnumFieldType.of(singletonList(ENUM_VALUE_A)), FIELD_NAME_1, LocalizedString.ofEnglish(LABEL_1), false, @@ -113,7 +110,7 @@ public void buildActions_WithNewPlainEnum_ShouldReturnAddEnumValueAction() { public void buildActions_WithoutOldPlainEnum_ShouldNotReturnAnyValueAction() { final FieldDefinition oldFieldDefinition = FieldDefinition.of( - EnumFieldType.of(asList(ENUM_VALUE_A)), + EnumFieldType.of(singletonList(ENUM_VALUE_A)), FIELD_NAME_1, LocalizedString.ofEnglish(LABEL_1), false, @@ -137,7 +134,7 @@ public void buildActions_WithoutOldPlainEnum_ShouldNotReturnAnyValueAction() { public void buildActions_WithNewLocalizedEnum_ShouldReturnAddLocalizedEnumValueAction() { final FieldDefinition oldFieldDefinition = FieldDefinition.of( - LocalizedEnumFieldType.of(asList(LOCALIZED_ENUM_VALUE_A)), + LocalizedEnumFieldType.of(singletonList(LOCALIZED_ENUM_VALUE_A)), FIELD_NAME_1, LocalizedString.ofEnglish(LABEL_1), false, diff --git a/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildLocalizedEnumUpdateActionsTest.java b/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildLocalizedEnumUpdateActionsTest.java index ec8ddbeff9..7534c6315f 100644 --- a/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildLocalizedEnumUpdateActionsTest.java +++ b/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildLocalizedEnumUpdateActionsTest.java @@ -12,7 +12,7 @@ import java.util.List; -import static com.commercetools.sync.commons.utils.LocalizedEnumValueTestObjects.*; +import static com.commercetools.sync.commons.utils.LocalizedEnumValueFixtures.*; import static com.commercetools.sync.types.utils.LocalizedEnumValueUpdateActionUtils.buildLocalizedEnumValuesUpdateActions; import static java.util.Arrays.asList; import static java.util.Collections.emptyList; diff --git a/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildPlainEnumUpdateActionsTest.java b/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildPlainEnumUpdateActionsTest.java index b13f62a6c4..2821ecb2b9 100644 --- a/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildPlainEnumUpdateActionsTest.java +++ b/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildPlainEnumUpdateActionsTest.java @@ -8,7 +8,7 @@ import java.util.List; -import static com.commercetools.sync.commons.utils.PlainEnumValueTestObjects.*; +import static com.commercetools.sync.commons.utils.PlainEnumValueFixtures.*; import static com.commercetools.sync.types.utils.PlainEnumValueUpdateActionUtils.buildEnumValuesUpdateActions; import static java.util.Arrays.asList; import static java.util.Collections.emptyList; From 793405078b8faf109cd230f5143588c01abc1429 Mon Sep 17 00:00:00 2001 From: aoz Date: Tue, 13 Nov 2018 18:37:34 +0100 Subject: [PATCH 047/164] #300 - remove method, use simple map function, it is not required to use a new method for this case. --- .../commercetools/sync/types/TypeSync.java | 20 ++++--------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/src/main/java/com/commercetools/sync/types/TypeSync.java b/src/main/java/com/commercetools/sync/types/TypeSync.java index aa0bef9a21..d8133de6db 100644 --- a/src/main/java/com/commercetools/sync/types/TypeSync.java +++ b/src/main/java/com/commercetools/sync/types/TypeSync.java @@ -7,7 +7,6 @@ import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import io.sphere.sdk.client.ConcurrentModificationException; import io.sphere.sdk.commands.UpdateAction; -import io.sphere.sdk.models.WithKey; import io.sphere.sdk.types.Type; import io.sphere.sdk.types.TypeDraft; @@ -18,7 +17,6 @@ import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; -import java.util.stream.Collectors; import static com.commercetools.sync.commons.utils.SyncUtils.batchElements; import static com.commercetools.sync.types.utils.TypeSyncUtils.buildActions; @@ -26,6 +24,8 @@ import static java.util.Collections.emptySet; import static java.util.Optional.ofNullable; import static java.util.concurrent.CompletableFuture.completedFuture; +import static java.util.function.Function.identity; +import static java.util.stream.Collectors.toMap; import static java.util.stream.Collectors.toSet; import static org.apache.commons.lang3.StringUtils.isBlank; @@ -149,19 +149,6 @@ private CompletionStage> fetchExistingTypes(@Nonnull final Set }); } - /** - * Given a set of {@link Type} or {@link TypeDraft}, returns a map of keys to the - * {@link Type}/{@link TypeDraft} instances. - * - * @param types list of {@link Type}/{@link TypeDraft} - * @param a type that extends of {@link WithKey}. - * @return the map of keys to {@link Type}/{@link TypeDraft} instances. - */ - private Map getTypeKeysMap(@Nonnull final Set types) { - return types.stream().collect(Collectors.toMap(WithKey::getKey, p -> p, - (typeA, typeB) -> typeB)); - } - /** * Given a {@link String} {@code errorMessage} and a {@link Throwable} {@code exception}, this method calls the * optional error callback specified in the {@code syncOptions} and updates the {@code statistics} instance by @@ -189,7 +176,8 @@ private void handleError(@Nonnull final String errorMessage, @Nullable final Thr private CompletionStage syncBatch( @Nonnull final Set oldTypes, @Nonnull final Set newTypes) { - final Map oldTypeMap = getTypeKeysMap(oldTypes); + + final Map oldTypeMap = oldTypes.stream().collect(toMap(Type::getKey, identity())); return CompletableFuture.allOf(newTypes .stream() From 7b6cf52e190075fb6bb206ae60ce40b4664a9fe2 Mon Sep 17 00:00:00 2001 From: aoz Date: Fri, 23 Nov 2018 10:04:58 +0100 Subject: [PATCH 048/164] #300 - update release notes for type changes --- docs/RELEASE_NOTES.md | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/docs/RELEASE_NOTES.md b/docs/RELEASE_NOTES.md index 7eb4e2cc59..c42c378e9f 100644 --- a/docs/RELEASE_NOTES.md +++ b/docs/RELEASE_NOTES.md @@ -51,24 +51,24 @@ - +- **ProductType Sync** - `EnumsUpdateActionUtils` is now removed. [#300](https://github.com/commercetools/commercetools-sync-java/issues/300) +- **ProductType Sync** - `ProductTypeUpdateLocalizedEnumActionUtils` is now removed. [#300](https://github.com/commercetools/commercetools-sync-java/issues/300) +- **ProductType Sync** - `ProductTypeUpdatePlainEnumActionUtils` is now removed. [#300](https://github.com/commercetools/commercetools-sync-java/issues/300) +- **ProductType Sync** - Renamed `LocalizedEnumUpdateActionsUtils` to `LocalizedEnumValueUpdateActionUtils`. [#300](https://github.com/commercetools/commercetools-sync-java/issues/300) +- **ProductType Sync** - Renamed `PlainEnumUpdateActionsUtils` to `PlainEnumValueUpdateActionUtils`. [#300](https://github.com/commercetools/commercetools-sync-java/issues/300) ### v1.0.0-M14 - Oct 5, 2018 From 7cf453a280be2485bc759480df8f187c017fb33e Mon Sep 17 00:00:00 2001 From: aoz Date: Fri, 23 Nov 2018 10:05:44 +0100 Subject: [PATCH 049/164] #300 - add retry fetch test case --- .../sync/integration/types/TypeSyncIT.java | 61 ++++++++++++++++++- .../commercetools/sync/types/TypeSync.java | 4 +- 2 files changed, 61 insertions(+), 4 deletions(-) diff --git a/src/integration-test/java/com/commercetools/sync/integration/types/TypeSyncIT.java b/src/integration-test/java/com/commercetools/sync/integration/types/TypeSyncIT.java index 3426684bad..8904d1155d 100644 --- a/src/integration-test/java/com/commercetools/sync/integration/types/TypeSyncIT.java +++ b/src/integration-test/java/com/commercetools/sync/integration/types/TypeSyncIT.java @@ -63,6 +63,7 @@ import static java.util.Collections.singletonList; import static java.util.concurrent.CompletableFuture.supplyAsync; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.startsWith; import static org.mockito.Mockito.any; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.eq; @@ -598,7 +599,7 @@ public void sync_WithErrorUpdatingTheTypeInCT_ShouldExecuteCallbackOnErrorAndInc .toCompletableFuture().join(); verify(spyTypeSyncOptions) - .applyErrorCallback(eq("Failed to update type of key 'key_1'."), any()); + .applyErrorCallback(startsWith("Failed to update type with key: 'key_1'."), any()); AssertionsForStatistics.assertThat(typeSyncStatistics).hasValues(1, 0, 0, 1); } @@ -789,7 +790,10 @@ public void sync_WithChangedTypeButBadGatewayException_ShouldFailUpdateType() { assertThat(errors).hasSize(1); assertThat(errors.get(0).getCause()).isExactlyInstanceOf(BadGatewayException.class); - assertThat(errorMessages.get(0)).contains(format("Failed to update type of key '%s'.", typeDraft.getKey())); + + assertThat(errorMessages.get(0)) + .contains(format("Failed to update type with key: '%s'. Reason: %s", + typeDraft.getKey(), errors.get(0))); } @Test @@ -832,4 +836,57 @@ public void sync_WithChangedTypeButConcurrentModificationException_ShouldRetryTo assertThat(errors).isEmpty(); } + @Test + public void sync_WithChangedTypeButBadGatewayException_ShouldFailToReFetchAndUpdate() { + // Mock sphere client to return ConcurrentModification on the first update request. + final SphereClient spyClient = spy(CTP_TARGET_CLIENT); + when(spyClient.execute(any(TypeUpdateCommand.class))) + .thenReturn(CompletableFutureUtils.exceptionallyCompletedFuture( + new Exception("concurrency modification issue on test", new ConcurrentModificationException()))) + .thenCallRealMethod(); + + when(spyClient.execute(any(TypeQuery.class))) + .thenCallRealMethod() // fetchMatchingTypesByKeys + .thenReturn(CompletableFutureUtils.exceptionallyCompletedFuture(new BadGatewayException())); + + + final List errorMessages = new ArrayList<>(); + final List errors = new ArrayList<>(); + + final TypeSyncOptions typeSyncOptions = + TypeSyncOptionsBuilder.of(spyClient) + .errorCallback((errorMessage, error) -> { + errorMessages.add(errorMessage); + errors.add(error); + }) + .build(); + + final TypeDraft typeDraft = TypeDraftBuilder.of( + TYPE_KEY_1, + TYPE_NAME_2, + ResourceTypeIdsSetBuilder.of().addCategories().build()) + .description(TYPE_DESCRIPTION_2) + .fieldDefinitions(singletonList(FIELD_DEFINITION_1)) + .build(); + + final TypeSync typeSync = new TypeSync(typeSyncOptions); + final TypeSyncStatistics statistics = typeSync.sync(Collections.singletonList(typeDraft)) + .toCompletableFuture() + .join(); + + // Test and assertion + AssertionsForStatistics.assertThat(statistics).hasValues(1, 0, 0, 1); + assertThat(errorMessages).hasSize(2); + assertThat(errors).hasSize(2); + + assertThat(errors.get(0).getCause()).isExactlyInstanceOf(BadGatewayException.class); + assertThat(errorMessages.get(0)) + .contains(format("Failed to fetch types with keys: '%s'. Reason: %s", + typeDraft.getKey(), errors.get(0))); + + assertThat(errorMessages.get(1)) + .contains(format("Failed to update type with key: '%s'. Reason: Failed to fetch type on retry.", + typeDraft.getKey())); + } + } diff --git a/src/main/java/com/commercetools/sync/types/TypeSync.java b/src/main/java/com/commercetools/sync/types/TypeSync.java index d8133de6db..d8204e6c42 100644 --- a/src/main/java/com/commercetools/sync/types/TypeSync.java +++ b/src/main/java/com/commercetools/sync/types/TypeSync.java @@ -35,7 +35,7 @@ public class TypeSync extends BaseSync { private static final String CTP_TYPE_FETCH_FAILED = "Failed to fetch existing types of keys '%s'."; - private static final String CTP_TYPE_UPDATE_FAILED = "Failed to update type of key '%s'."; + private static final String CTP_TYPE_UPDATE_FAILED = "Failed to update type with key: '%s'. Reason: %s" ; private static final String CTP_TYPE_CREATE_FAILED = "Failed to create type of key '%s'."; private static final String TYPE_DRAFT_HAS_NO_KEY = "Failed to process type draft without key."; private static final String TYPE_DRAFT_IS_NULL = "Failed to process null type draft."; @@ -254,7 +254,7 @@ private CompletionStage updateType(@Nonnull final Type oldType, () -> fetchAndUpdate(oldType, newType), () -> { final String errorMessage = format(CTP_TYPE_UPDATE_FAILED, - newType.getKey()); + newType.getKey(), sphereException); handleError(errorMessage, sphereException, 1); return completedFuture(null); From 5f095213746a6150164d1226d5fd59010315e7af Mon Sep 17 00:00:00 2001 From: aoz Date: Fri, 23 Nov 2018 10:09:32 +0100 Subject: [PATCH 050/164] #300 - refactor, use syncBatches from BaseSync --- .../com/commercetools/sync/commons/BaseSync.java | 3 ++- .../java/com/commercetools/sync/types/TypeSync.java | 13 ------------- 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/src/main/java/com/commercetools/sync/commons/BaseSync.java b/src/main/java/com/commercetools/sync/commons/BaseSync.java index f0843234da..7e91105ea0 100644 --- a/src/main/java/com/commercetools/sync/commons/BaseSync.java +++ b/src/main/java/com/commercetools/sync/commons/BaseSync.java @@ -84,7 +84,8 @@ public V getSyncOptions() { * given list of batches. */ protected CompletionStage syncBatches(@Nonnull final List> batches, - @Nonnull final CompletionStage result) { + @Nonnull final CompletionStage result) { + if (batches.isEmpty()) { return result; } diff --git a/src/main/java/com/commercetools/sync/types/TypeSync.java b/src/main/java/com/commercetools/sync/types/TypeSync.java index d8204e6c42..4a3b2ae8cb 100644 --- a/src/main/java/com/commercetools/sync/types/TypeSync.java +++ b/src/main/java/com/commercetools/sync/types/TypeSync.java @@ -71,19 +71,6 @@ protected CompletionStage process(@Nonnull final List syncBatches( - @Nonnull final List> batches, - @Nonnull final CompletionStage result) { - - if (batches.isEmpty()) { - return result; - } - - final List firstBatch = batches.remove(0); - return syncBatches(batches, result.thenCompose(subResult -> processBatch(firstBatch))); - } - /** * Fetches existing {@link Type} objects from CTP project that correspond to passed {@code batch}. * Having existing types fetched, {@code batch} is compared and synced with fetched objects by From a787872ed9c9c5d27f6b0770225f7f3f968264de Mon Sep 17 00:00:00 2001 From: aoz Date: Mon, 26 Nov 2018 09:56:46 +0100 Subject: [PATCH 051/164] #300 - update README.md for type. --- README.md | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/README.md b/README.md index 63f6d1c948..a0576051ed 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ Currently this library supports synchronising the following entities in commerce - [Products](/docs/usage/PRODUCT_SYNC.md) - [InventoryEntries](/docs/usage/INVENTORY_SYNC.md) - [ProductTypes](/docs/usage/PRODUCT_TYPE_SYNC.md) + - [Types](/docs/usage/TYPE_SYNC.md) ![commercetools-java-sync-final 001](https://user-images.githubusercontent.com/9512131/31230702-0f2255a6-a9e5-11e7-9412-04ed52641dde.png) @@ -46,13 +47,6 @@ commercetools sync is a Java library that could be used to synchronise CTP data (e.g. [CategoryDraft](https://github.com/commercetools/commercetools-jvm-sdk/blob/master/commercetools-models/src/main/java/io/sphere/sdk/categories/CategoryDraft.java)). -Currently this library supports synchronising - - [Categories](/docs/usage/CATEGORY_SYNC.md) - - [Products](/docs/usage/PRODUCT_SYNC.md) (_Beta_: Not recommended for production use yet.) - - [InventoryEntries](/docs/usage/INVENTORY_SYNC.md) (_Beta_: Not recommended for production use yet.) - - [ProductTypes](/docs/usage/PRODUCT_TYPE_SYNC.md) (_Beta_: Not recommended for production use yet.) - - [Types](/docs/usage/TYPE_SYNC.md) (_Beta_: Not recommended for production use yet.) - ### Prerequisites - install Java 8 From fbb2d52fe2921d358c5775d453a331de27851971 Mon Sep 17 00:00:00 2001 From: aoz Date: Mon, 26 Nov 2018 10:00:19 +0100 Subject: [PATCH 052/164] #300 - update type sync caveats --- docs/usage/TYPE_SYNC.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/usage/TYPE_SYNC.md b/docs/usage/TYPE_SYNC.md index 49f85677d8..50ae04dfa3 100644 --- a/docs/usage/TYPE_SYNC.md +++ b/docs/usage/TYPE_SYNC.md @@ -111,7 +111,7 @@ and field definitions can be found [here](/src/test/java/com/commercetools/sync/ ## Caveats -1. Types are either created or updated. Currently, the tool does not support type deletion. -2. Updating the label of enum values and localized enum values of field definition is not supported yet. -3. Removing the enum values from the field definition is not supported yet. -4. Updating the input hint of field definition is not supported yet. \ No newline at end of file +1. Updating the label of enum values and localized enum values of field definition is not supported yet. +2. Removing the enum values from the field definition is not supported yet. +3. Updating the input hint of field definition is not supported yet. +4. Syncing types with an field of type [SetType](https://docs.commercetools.com/http-api-projects-types.html#settype) is not supported yet. From 76c5a44038422f2c25179c48cd5e082f45b66c18 Mon Sep 17 00:00:00 2001 From: aoz Date: Mon, 26 Nov 2018 10:01:47 +0100 Subject: [PATCH 053/164] #300 - rename method name cacheAndFetch to fetchAndCache with correct order. --- .../com/commercetools/sync/services/impl/TypeServiceImpl.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java b/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java index f963af4acd..aea679943e 100644 --- a/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java +++ b/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java @@ -53,7 +53,7 @@ public TypeServiceImpl(@Nonnull final BaseSyncOptions syncOptions) { @Override public CompletionStage> fetchCachedTypeId(@Nonnull final String key) { if (!isCached) { - return cacheAndFetch(key); + return fetchAndCache(key); } return CompletableFuture.completedFuture(Optional.ofNullable(keyToIdCache.get(key))); } @@ -108,7 +108,7 @@ public CompletionStage updateType(@Nonnull final Type type, } @Nonnull - private CompletionStage> cacheAndFetch(@Nonnull final String key) { + private CompletionStage> fetchAndCache(@Nonnull final String key) { final Consumer> typePageConsumer = typesPage -> typesPage.forEach(type -> keyToIdCache.put(type.getKey(), type.getId())); From 0c449e0dd18fafee9854b59e39f78818b6972518 Mon Sep 17 00:00:00 2001 From: aoz Date: Mon, 26 Nov 2018 11:27:48 +0100 Subject: [PATCH 054/164] #300 - refactor TypeService using BaseService. --- .../services/impl/TypeServiceImplIT.java | 24 +++++++++-------- .../sync/services/TypeService.java | 26 ++++++++++++------- .../sync/services/impl/TypeServiceImpl.java | 19 +++++++------- 3 files changed, 39 insertions(+), 30 deletions(-) diff --git a/src/integration-test/java/com/commercetools/sync/integration/services/impl/TypeServiceImplIT.java b/src/integration-test/java/com/commercetools/sync/integration/services/impl/TypeServiceImplIT.java index 555674ef12..46f13d9e1a 100644 --- a/src/integration-test/java/com/commercetools/sync/integration/services/impl/TypeServiceImplIT.java +++ b/src/integration-test/java/com/commercetools/sync/integration/services/impl/TypeServiceImplIT.java @@ -240,22 +240,24 @@ public void createType_WithValidType_ShouldCreateType() { .fieldDefinitions(singletonList(FIELD_DEFINITION_1)) .build(); - final Type createdType = typeService.createType(newTypeDraft) + final Optional createdType = typeService.createType(newTypeDraft) .toCompletableFuture().join(); assertThat(createdType).isNotNull(); - final Optional typeOptional = CTP_TARGET_CLIENT - .execute(TypeQuery.of() - .withPredicates(typeQueryModel -> typeQueryModel.key().is(createdType.getKey()))) - .toCompletableFuture().join().head(); + final Optional fetchedType = CTP_TARGET_CLIENT + .execute(TypeQuery.of() + .withPredicates(typeQueryModel -> + typeQueryModel.key().is(createdType.get().getKey()))) + .toCompletableFuture().join().head(); - assertThat(typeOptional).isNotEmpty(); - final Type fetchedType = typeOptional.get(); - assertThat(fetchedType.getKey()).isEqualTo(newTypeDraft.getKey()); - assertThat(fetchedType.getDescription()).isEqualTo(createdType.getDescription()); - assertThat(fetchedType.getName()).isEqualTo(createdType.getName()); - assertThat(fetchedType.getFieldDefinitions()).isEqualTo(createdType.getFieldDefinitions()); + assertThat(fetchedType).hasValueSatisfying(oldType -> + assertThat(createdType).hasValueSatisfying(newType -> { + assertThat(newType.getKey()).isEqualTo(newTypeDraft.getKey()); + assertThat(newType.getDescription()).isEqualTo(oldType.getDescription()); + assertThat(newType.getName()).isEqualTo(oldType.getName()); + assertThat(newType.getFieldDefinitions()).isEqualTo(oldType.getFieldDefinitions()); + })); } @Test diff --git a/src/main/java/com/commercetools/sync/services/TypeService.java b/src/main/java/com/commercetools/sync/services/TypeService.java index cf226ab3ad..24b389667e 100644 --- a/src/main/java/com/commercetools/sync/services/TypeService.java +++ b/src/main/java/com/commercetools/sync/services/TypeService.java @@ -1,6 +1,5 @@ package com.commercetools.sync.services; - import io.sphere.sdk.channels.Channel; import io.sphere.sdk.client.SphereClient; import io.sphere.sdk.commands.UpdateAction; @@ -57,20 +56,29 @@ public interface TypeService { CompletionStage> fetchType(@Nullable final String key); /** - * Creates new type from {@code typeDraft}. + * Given a {@link TypeDraft}, this method creates a {@link Type} based on it in the CTP project defined in + * a potentially injected {@link io.sphere.sdk.client.SphereClient}. The created type's id and key are also + * cached. This method returns {@link CompletionStage}<{@link Type}> in which the result of it's + * completion contains an instance of the {@link Type} which was created in the CTP project. * - * @param typeDraft draft with data for new type - * @return {@link CompletionStage} with created {@link Type}. + * @param typeDraft the {@link TypeDraft} to create a {@link Type} based off of. + * @return {@link CompletionStage}<{@link Optional}> in which the result of it's completion contains an + * {@link Optional} that contains the matching {@link Type} if exists, otherwise empty. */ @Nonnull - CompletionStage createType(@Nonnull final TypeDraft typeDraft); + CompletionStage> createType(@Nonnull final TypeDraft typeDraft); /** - * Updates existing type with {@code updateActions}. + * Given a {@link Type} and a {@link List}<{@link UpdateAction}<{@link Type}>>, this method + * issues an update request with these update actions on this {@link Type} in the CTP project defined in a + * potentially injected {@link io.sphere.sdk.client.SphereClient}. This method returns + * {@link CompletionStage}<{@link Type}> in which the result of it's completion contains an instance of + * the {@link Type} which was updated in the CTP project. * - * @param type type that should be updated - * @param updateActions {@link List} of actions that should be applied to {@code type} - * @return {@link CompletionStage} with updated {@link Type}. + * @param type the {@link Type} to update. + * @param updateActions the update actions to update the {@link Type} with. + * @return {@link CompletionStage}<{@link Type}> containing as a result of it's completion an instance of + * the {@link Type} which was updated in the CTP project or a {@link io.sphere.sdk.models.SphereException}. */ @Nonnull CompletionStage updateType(@Nonnull final Type type, diff --git a/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java b/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java index aea679943e..25b1d763cd 100644 --- a/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java +++ b/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java @@ -1,6 +1,5 @@ package com.commercetools.sync.services.impl; - import com.commercetools.sync.commons.BaseSyncOptions; import com.commercetools.sync.commons.utils.CtpQueryUtils; import com.commercetools.sync.services.TypeService; @@ -35,18 +34,17 @@ * Implementation of TypeService interface. * TODO: USE graphQL to get only keys. GITHUB ISSUE#84 */ -public final class TypeServiceImpl implements TypeService { +public final class TypeServiceImpl extends BaseService implements TypeService { private static final String FETCH_FAILED = "Failed to fetch types with keys: '%s'. Reason: %s"; - private final BaseSyncOptions syncOptions; private final Map keyToIdCache = new ConcurrentHashMap<>(); private boolean isCached = false; - public TypeServiceImpl(@Nonnull final TypeSyncOptions syncOptions) { - this.syncOptions = syncOptions; + public TypeServiceImpl(@Nonnull final BaseSyncOptions syncOptions) { + super(syncOptions); } - public TypeServiceImpl(@Nonnull final BaseSyncOptions syncOptions) { - this.syncOptions = syncOptions; + public TypeServiceImpl(@Nonnull final TypeSyncOptions syncOptions) { + super(syncOptions); } @Nonnull @@ -96,15 +94,16 @@ public CompletionStage> fetchType(@Nullable final String key) { @Nonnull @Override - public CompletionStage createType(@Nonnull final TypeDraft typeDraft) { - return syncOptions.getCtpClient().execute(TypeCreateCommand.of(typeDraft)); + public CompletionStage> createType(@Nonnull final TypeDraft typeDraft) { + return applyCallbackAndCreate(typeDraft, typeDraft.getKey(), TypeCreateCommand::of); } @Nonnull @Override public CompletionStage updateType(@Nonnull final Type type, @Nonnull final List> updateActions) { - return syncOptions.getCtpClient().execute(TypeUpdateCommand.of(type, updateActions)); + + return updateResource(type, TypeUpdateCommand::of, updateActions); } @Nonnull From eed705591e3a1fced3ffbbf307e08d3e934549ee Mon Sep 17 00:00:00 2001 From: aoz Date: Mon, 26 Nov 2018 11:37:53 +0100 Subject: [PATCH 055/164] #300 - update caveats for removing localized enum values. --- docs/usage/TYPE_SYNC.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/usage/TYPE_SYNC.md b/docs/usage/TYPE_SYNC.md index 50ae04dfa3..6ef874393e 100644 --- a/docs/usage/TYPE_SYNC.md +++ b/docs/usage/TYPE_SYNC.md @@ -112,6 +112,6 @@ and field definitions can be found [here](/src/test/java/com/commercetools/sync/ ## Caveats 1. Updating the label of enum values and localized enum values of field definition is not supported yet. -2. Removing the enum values from the field definition is not supported yet. +2. Removing the enum values and localized enum values from the field definition is not supported yet. 3. Updating the input hint of field definition is not supported yet. 4. Syncing types with an field of type [SetType](https://docs.commercetools.com/http-api-projects-types.html#settype) is not supported yet. From 08fae3c0f9e77c388c6085dcfba906a640265264 Mon Sep 17 00:00:00 2001 From: aoz Date: Mon, 26 Nov 2018 12:03:47 +0100 Subject: [PATCH 056/164] #300 - remove unnecessary comments. --- .../sync/types/utils/TypeUpdateActionUtilsTest.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/test/java/com/commercetools/sync/types/utils/TypeUpdateActionUtilsTest.java b/src/test/java/com/commercetools/sync/types/utils/TypeUpdateActionUtilsTest.java index 446c154033..2b1f9243fd 100644 --- a/src/test/java/com/commercetools/sync/types/utils/TypeUpdateActionUtilsTest.java +++ b/src/test/java/com/commercetools/sync/types/utils/TypeUpdateActionUtilsTest.java @@ -40,10 +40,8 @@ public static void setup() { final LocalizedString name = LocalizedString.ofEnglish("type for standard categories"); final LocalizedString desc = LocalizedString.ofEnglish("description for category custom type"); - //this enables the type only to be used for categories final Set resourceTypeIds = ResourceTypeIdsSetBuilder.of() .addCategories() - //there are more methods for other objects which support custom .build(); final List fieldDefinitions = From cdb6d17d29b75e74a72d0340506505255a7f8cc6 Mon Sep 17 00:00:00 2001 From: aoz Date: Mon, 26 Nov 2018 13:01:41 +0100 Subject: [PATCH 057/164] #300 - update product type caveats - set type --- docs/usage/PRODUCT_TYPE_SYNC.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/usage/PRODUCT_TYPE_SYNC.md b/docs/usage/PRODUCT_TYPE_SYNC.md index d2b1953a22..f62a4bf4a1 100644 --- a/docs/usage/PRODUCT_TYPE_SYNC.md +++ b/docs/usage/PRODUCT_TYPE_SYNC.md @@ -133,3 +133,4 @@ More examples of those utils for different fields can be found [here](/src/test/ In order to exploit the number of `max parallel requests`, the `batch size` should have a value set which is equal or higher. 3. Syncing product types with an attribute of type [NestedType](https://docs.commercetools.com/http-api-projects-productTypes.html#nestedtype) is not supported yet. +4. Syncing product types with an attribute of type [SetType](https://docs.commercetools.com/http-api-projects-productTypes.html#settype) is not supported yet. From cb0f24e7369ab16eb8916f2d29a8f55b4949b2d7 Mon Sep 17 00:00:00 2001 From: aoz Date: Mon, 26 Nov 2018 16:09:04 +0100 Subject: [PATCH 058/164] #300 - update and add todo's in java docs for missing update actions. --- docs/usage/TYPE_SYNC.md | 6 +++--- .../types/utils/LocalizedEnumValueUpdateActionUtils.java | 9 ++++----- .../types/utils/PlainEnumValueUpdateActionUtils.java | 9 ++++----- .../commercetools/sync/types/utils/TypeSyncUtils.java | 4 ++++ .../sync/types/utils/TypeUpdateActionUtils.java | 4 ++++ .../BuildLocalizedEnumUpdateActionsTest.java | 1 - 6 files changed, 19 insertions(+), 14 deletions(-) diff --git a/docs/usage/TYPE_SYNC.md b/docs/usage/TYPE_SYNC.md index 6ef874393e..d56b9e3e22 100644 --- a/docs/usage/TYPE_SYNC.md +++ b/docs/usage/TYPE_SYNC.md @@ -111,7 +111,7 @@ and field definitions can be found [here](/src/test/java/com/commercetools/sync/ ## Caveats -1. Updating the label of enum values and localized enum values of field definition is not supported yet. -2. Removing the enum values and localized enum values from the field definition is not supported yet. -3. Updating the input hint of field definition is not supported yet. +1. Updating the label of enum values and localized enum values of field definition is not supported yet. [#339](https://github.com/commercetools/commercetools-sync-java/issues/339) +2. Removing the enum values and localized enum values from the field definition is not supported yet. [#339](https://github.com/commercetools/commercetools-sync-java/issues/339) +3. Updating the input hint of field definition is not supported yet. [#339](https://github.com/commercetools/commercetools-sync-java/issues/339) 4. Syncing types with an field of type [SetType](https://docs.commercetools.com/http-api-projects-types.html#settype) is not supported yet. diff --git a/src/main/java/com/commercetools/sync/types/utils/LocalizedEnumValueUpdateActionUtils.java b/src/main/java/com/commercetools/sync/types/utils/LocalizedEnumValueUpdateActionUtils.java index 2d7b2d6f11..ec82f19e19 100644 --- a/src/main/java/com/commercetools/sync/types/utils/LocalizedEnumValueUpdateActionUtils.java +++ b/src/main/java/com/commercetools/sync/types/utils/LocalizedEnumValueUpdateActionUtils.java @@ -21,6 +21,10 @@ public final class LocalizedEnumValueUpdateActionUtils { * for building the required update actions (AddLocalizedEnumValue, RemoveEnumValue, ChangeLocalizedEnumValueOrder * and 1-1 update actions on localized enum values (e.g. changeLabel) for the required resource. * + *

+ * TODO: Check GITHUB ISSUE#339 for missing FieldDefinition update actions. + *

+ * * @param fieldDefinitionName the field name whose localized enum values are going to be synced. * @param oldEnumValues the old list of localized enum values. * @param newEnumValues the new list of localized enum values. @@ -34,11 +38,6 @@ public static List> buildLocalizedEnumValuesUpdateActions( @Nonnull final List oldEnumValues, @Nullable final List newEnumValues) { - /* - TODO: If the list of newEnumValues is null, then remove actions are built - for every existing localized enum value in the oldEnumValues list. - */ - return buildActions(fieldDefinitionName, oldEnumValues, newEnumValues, diff --git a/src/main/java/com/commercetools/sync/types/utils/PlainEnumValueUpdateActionUtils.java b/src/main/java/com/commercetools/sync/types/utils/PlainEnumValueUpdateActionUtils.java index b07089b8a3..2928b06784 100644 --- a/src/main/java/com/commercetools/sync/types/utils/PlainEnumValueUpdateActionUtils.java +++ b/src/main/java/com/commercetools/sync/types/utils/PlainEnumValueUpdateActionUtils.java @@ -20,6 +20,10 @@ public final class PlainEnumValueUpdateActionUtils { * for building the required update actions (AddEnumValue, ChangeEnumValueOrder * and 1-1 update actions on plain enum values (e.g. changeLabel) for the required resource. * + *

+ * TODO: Check GITHUB ISSUE#339 for missing FieldDefinition update actions. + *

+ * * @param fieldDefinitionName the field name whose plain enum values are going to be synced. * @param oldEnumValues the old list of plain enum values. * @param newEnumValues the new list of plain enum values. @@ -32,11 +36,6 @@ public static List> buildEnumValuesUpdateActions( @Nonnull final List oldEnumValues, @Nullable final List newEnumValues) { - /* - TODO: If the list of newEnumValues is null, then remove actions are built - for every existing plain enum value in the oldEnumValues list. - */ - return EnumValuesUpdateActionUtils.buildActions(fieldDefinitionName, oldEnumValues, newEnumValues, diff --git a/src/main/java/com/commercetools/sync/types/utils/TypeSyncUtils.java b/src/main/java/com/commercetools/sync/types/utils/TypeSyncUtils.java index 63d883262c..2d67248522 100644 --- a/src/main/java/com/commercetools/sync/types/utils/TypeSyncUtils.java +++ b/src/main/java/com/commercetools/sync/types/utils/TypeSyncUtils.java @@ -25,6 +25,10 @@ public final class TypeSyncUtils { * result. If no update actions are needed, for example in case where both the {@link Type} and the * {@link TypeDraft} have the same fields, an empty {@link List} is returned. * + *

+ * TODO: Check GITHUB ISSUE#339 for missing FieldDefinition update actions. + *

+ * * @param oldType the {@link Type} which should be updated. * @param newType the {@link TypeDraft} where we get the new data. * @param syncOptions the sync options wrapper which contains options related to the sync process supplied by diff --git a/src/main/java/com/commercetools/sync/types/utils/TypeUpdateActionUtils.java b/src/main/java/com/commercetools/sync/types/utils/TypeUpdateActionUtils.java index 599f662e54..05750410b6 100644 --- a/src/main/java/com/commercetools/sync/types/utils/TypeUpdateActionUtils.java +++ b/src/main/java/com/commercetools/sync/types/utils/TypeUpdateActionUtils.java @@ -63,6 +63,10 @@ public static Optional> buildSetDescriptionUpdateAction( * if values are different. In case, the new type draft has a list of field definitions in which a * duplicate name exists, the error callback is triggered and an empty list is returned. * + *

+ * TODO: Check GITHUB ISSUE#339 for missing FieldDefinition update actions. + *

+ * * @param oldType the type which should be updated. * @param newType the type draft where we get the key. * @param syncOptions responsible for supplying the sync options to the sync utility method. diff --git a/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildLocalizedEnumUpdateActionsTest.java b/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildLocalizedEnumUpdateActionsTest.java index 7534c6315f..bd4093d422 100644 --- a/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildLocalizedEnumUpdateActionsTest.java +++ b/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildLocalizedEnumUpdateActionsTest.java @@ -168,7 +168,6 @@ public void buildLocalizedEnumUpdateActions_WithAddedRemovedAndDifOrder_ShouldBu ENUM_VALUES_CBD ); - // remove enum value actions not exists for type resources assertThat(updateActions).containsExactly( AddLocalizedEnumValue.of(FIELD_NAME_1, ENUM_VALUE_D), ChangeLocalizedEnumValueOrder.of(FIELD_NAME_1, asList( From 3151401474c5b52b80bedb4642f29a310dddb8cb Mon Sep 17 00:00:00 2001 From: aoz Date: Mon, 26 Nov 2018 16:37:28 +0100 Subject: [PATCH 059/164] #300 - refactor Type IT packages --- .../sync/benchmark/TypeSyncBenchmark.java | 6 ++-- .../{types => commons}/utils/TypeITUtils.java | 4 +-- .../types/TypeSyncIT.java | 30 +++++++++---------- .../services/impl/TypeServiceImplIT.java | 8 ++--- 4 files changed, 24 insertions(+), 24 deletions(-) rename src/integration-test/java/com/commercetools/sync/integration/{types => commons}/utils/TypeITUtils.java (98%) rename src/integration-test/java/com/commercetools/sync/integration/{ => externalsource}/types/TypeSyncIT.java (96%) diff --git a/src/benchmark/java/com/commercetools/sync/benchmark/TypeSyncBenchmark.java b/src/benchmark/java/com/commercetools/sync/benchmark/TypeSyncBenchmark.java index 388a1c309a..f9f9d956e0 100644 --- a/src/benchmark/java/com/commercetools/sync/benchmark/TypeSyncBenchmark.java +++ b/src/benchmark/java/com/commercetools/sync/benchmark/TypeSyncBenchmark.java @@ -39,9 +39,9 @@ import static com.commercetools.sync.benchmark.BenchmarkUtils.saveNewResult; import static com.commercetools.sync.integration.commons.utils.ITUtils.deleteTypesFromTargetAndSource; import static com.commercetools.sync.integration.commons.utils.SphereClientUtils.CTP_TARGET_CLIENT; -import static com.commercetools.sync.integration.types.utils.TypeITUtils.FIELD_DEFINITION_1; -import static com.commercetools.sync.integration.types.utils.TypeITUtils.FIELD_DEFINITION_NAME_1; -import static com.commercetools.sync.integration.types.utils.TypeITUtils.deleteTypes; +import static com.commercetools.sync.integration.commons.utils.TypeITUtils.FIELD_DEFINITION_1; +import static com.commercetools.sync.integration.commons.utils.TypeITUtils.FIELD_DEFINITION_NAME_1; +import static com.commercetools.sync.integration.commons.utils.TypeITUtils.deleteTypes; import static com.commercetools.tests.utils.CompletionStageUtil.executeBlocking; import static java.lang.String.format; import static java.util.Collections.singletonList; diff --git a/src/integration-test/java/com/commercetools/sync/integration/types/utils/TypeITUtils.java b/src/integration-test/java/com/commercetools/sync/integration/commons/utils/TypeITUtils.java similarity index 98% rename from src/integration-test/java/com/commercetools/sync/integration/types/utils/TypeITUtils.java rename to src/integration-test/java/com/commercetools/sync/integration/commons/utils/TypeITUtils.java index f75f4a69b3..294d0af38c 100644 --- a/src/integration-test/java/com/commercetools/sync/integration/types/utils/TypeITUtils.java +++ b/src/integration-test/java/com/commercetools/sync/integration/commons/utils/TypeITUtils.java @@ -1,4 +1,4 @@ -package com.commercetools.sync.integration.types.utils; +package com.commercetools.sync.integration.commons.utils; import io.sphere.sdk.client.SphereClient; import io.sphere.sdk.models.LocalizedString; @@ -143,6 +143,6 @@ public static Optional getTypeByKey( } private TypeITUtils() { - + } } diff --git a/src/integration-test/java/com/commercetools/sync/integration/types/TypeSyncIT.java b/src/integration-test/java/com/commercetools/sync/integration/externalsource/types/TypeSyncIT.java similarity index 96% rename from src/integration-test/java/com/commercetools/sync/integration/types/TypeSyncIT.java rename to src/integration-test/java/com/commercetools/sync/integration/externalsource/types/TypeSyncIT.java index 8904d1155d..7273567f64 100644 --- a/src/integration-test/java/com/commercetools/sync/integration/types/TypeSyncIT.java +++ b/src/integration-test/java/com/commercetools/sync/integration/externalsource/types/TypeSyncIT.java @@ -1,4 +1,4 @@ -package com.commercetools.sync.integration.types; +package com.commercetools.sync.integration.externalsource.types; import com.commercetools.sync.commons.asserts.statistics.AssertionsForStatistics; @@ -44,20 +44,20 @@ import static com.commercetools.sync.integration.commons.utils.ITUtils.deleteTypesFromTargetAndSource; import static com.commercetools.sync.integration.commons.utils.SphereClientUtils.CTP_SOURCE_CLIENT; import static com.commercetools.sync.integration.commons.utils.SphereClientUtils.CTP_TARGET_CLIENT; -import static com.commercetools.sync.integration.types.utils.TypeITUtils.FIELD_DEFINITION_1; -import static com.commercetools.sync.integration.types.utils.TypeITUtils.FIELD_DEFINITION_2; -import static com.commercetools.sync.integration.types.utils.TypeITUtils.FIELD_DEFINITION_3; -import static com.commercetools.sync.integration.types.utils.TypeITUtils.FIELD_DEFINITION_LABEL_1; -import static com.commercetools.sync.integration.types.utils.TypeITUtils.FIELD_DEFINITION_NAME_1; -import static com.commercetools.sync.integration.types.utils.TypeITUtils.TYPE_DESCRIPTION_1; -import static com.commercetools.sync.integration.types.utils.TypeITUtils.TYPE_DESCRIPTION_2; -import static com.commercetools.sync.integration.types.utils.TypeITUtils.TYPE_KEY_1; -import static com.commercetools.sync.integration.types.utils.TypeITUtils.TYPE_KEY_2; -import static com.commercetools.sync.integration.types.utils.TypeITUtils.TYPE_NAME_1; -import static com.commercetools.sync.integration.types.utils.TypeITUtils.TYPE_NAME_2; -import static com.commercetools.sync.integration.types.utils.TypeITUtils.getTypeByKey; -import static com.commercetools.sync.integration.types.utils.TypeITUtils.populateSourceProject; -import static com.commercetools.sync.integration.types.utils.TypeITUtils.populateTargetProject; +import static com.commercetools.sync.integration.commons.utils.TypeITUtils.FIELD_DEFINITION_1; +import static com.commercetools.sync.integration.commons.utils.TypeITUtils.FIELD_DEFINITION_2; +import static com.commercetools.sync.integration.commons.utils.TypeITUtils.FIELD_DEFINITION_3; +import static com.commercetools.sync.integration.commons.utils.TypeITUtils.FIELD_DEFINITION_LABEL_1; +import static com.commercetools.sync.integration.commons.utils.TypeITUtils.FIELD_DEFINITION_NAME_1; +import static com.commercetools.sync.integration.commons.utils.TypeITUtils.TYPE_DESCRIPTION_1; +import static com.commercetools.sync.integration.commons.utils.TypeITUtils.TYPE_DESCRIPTION_2; +import static com.commercetools.sync.integration.commons.utils.TypeITUtils.TYPE_KEY_1; +import static com.commercetools.sync.integration.commons.utils.TypeITUtils.TYPE_KEY_2; +import static com.commercetools.sync.integration.commons.utils.TypeITUtils.TYPE_NAME_1; +import static com.commercetools.sync.integration.commons.utils.TypeITUtils.TYPE_NAME_2; +import static com.commercetools.sync.integration.commons.utils.TypeITUtils.getTypeByKey; +import static com.commercetools.sync.integration.commons.utils.TypeITUtils.populateSourceProject; +import static com.commercetools.sync.integration.commons.utils.TypeITUtils.populateTargetProject; import static java.lang.String.format; import static java.util.Arrays.asList; import static java.util.Collections.singletonList; diff --git a/src/integration-test/java/com/commercetools/sync/integration/services/impl/TypeServiceImplIT.java b/src/integration-test/java/com/commercetools/sync/integration/services/impl/TypeServiceImplIT.java index 46f13d9e1a..959e5eb646 100644 --- a/src/integration-test/java/com/commercetools/sync/integration/services/impl/TypeServiceImplIT.java +++ b/src/integration-test/java/com/commercetools/sync/integration/services/impl/TypeServiceImplIT.java @@ -34,10 +34,10 @@ import static com.commercetools.sync.integration.commons.utils.CategoryITUtils.createCategoriesCustomType; import static com.commercetools.sync.integration.commons.utils.ITUtils.deleteTypesFromTargetAndSource; import static com.commercetools.sync.integration.commons.utils.SphereClientUtils.CTP_TARGET_CLIENT; -import static com.commercetools.sync.integration.types.utils.TypeITUtils.FIELD_DEFINITION_1; -import static com.commercetools.sync.integration.types.utils.TypeITUtils.TYPE_DESCRIPTION_1; -import static com.commercetools.sync.integration.types.utils.TypeITUtils.TYPE_KEY_1; -import static com.commercetools.sync.integration.types.utils.TypeITUtils.TYPE_NAME_1; +import static com.commercetools.sync.integration.commons.utils.TypeITUtils.FIELD_DEFINITION_1; +import static com.commercetools.sync.integration.commons.utils.TypeITUtils.TYPE_DESCRIPTION_1; +import static com.commercetools.sync.integration.commons.utils.TypeITUtils.TYPE_KEY_1; +import static com.commercetools.sync.integration.commons.utils.TypeITUtils.TYPE_NAME_1; import static com.commercetools.tests.utils.CompletionStageUtil.executeBlocking; import static java.lang.String.format; import static java.util.Collections.singleton; From 72293e4ae08d4b0a8353ff91d5b0eed246904b3d Mon Sep 17 00:00:00 2001 From: aoz Date: Mon, 26 Nov 2018 16:41:54 +0100 Subject: [PATCH 060/164] #300 - update getCause wrapper on test, this change occurred because of type impl change. --- .../integration/externalsource/types/TypeSyncIT.java | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/integration-test/java/com/commercetools/sync/integration/externalsource/types/TypeSyncIT.java b/src/integration-test/java/com/commercetools/sync/integration/externalsource/types/TypeSyncIT.java index 7273567f64..f42ae70908 100644 --- a/src/integration-test/java/com/commercetools/sync/integration/externalsource/types/TypeSyncIT.java +++ b/src/integration-test/java/com/commercetools/sync/integration/externalsource/types/TypeSyncIT.java @@ -757,8 +757,7 @@ public void sync_WithChangedTypeButBadGatewayException_ShouldFailUpdateType() { // Mock sphere client to return BadGatewayException on the first update request. final SphereClient spyClient = spy(CTP_TARGET_CLIENT); when(spyClient.execute(any(TypeUpdateCommand.class))) - .thenReturn(CompletableFutureUtils.exceptionallyCompletedFuture( - new Exception("bad gateway issue on test", new BadGatewayException()))); + .thenReturn(CompletableFutureUtils.exceptionallyCompletedFuture( new BadGatewayException())); final List errorMessages = new ArrayList<>(); final List errors = new ArrayList<>(); @@ -802,8 +801,7 @@ public void sync_WithChangedTypeButConcurrentModificationException_ShouldRetryTo // Mock sphere client to return ConcurrentModification on the first update request. final SphereClient spyClient = spy(CTP_TARGET_CLIENT); when(spyClient.execute(any(TypeUpdateCommand.class))) - .thenReturn(CompletableFutureUtils.exceptionallyCompletedFuture( - new Exception("concurrency modification issue on test", new ConcurrentModificationException()))) + .thenReturn(CompletableFutureUtils.exceptionallyCompletedFuture(new ConcurrentModificationException())) .thenCallRealMethod(); final List errorMessages = new ArrayList<>(); @@ -841,8 +839,7 @@ public void sync_WithChangedTypeButBadGatewayException_ShouldFailToReFetchAndUpd // Mock sphere client to return ConcurrentModification on the first update request. final SphereClient spyClient = spy(CTP_TARGET_CLIENT); when(spyClient.execute(any(TypeUpdateCommand.class))) - .thenReturn(CompletableFutureUtils.exceptionallyCompletedFuture( - new Exception("concurrency modification issue on test", new ConcurrentModificationException()))) + .thenReturn(CompletableFutureUtils.exceptionallyCompletedFuture( new ConcurrentModificationException())) .thenCallRealMethod(); when(spyClient.execute(any(TypeQuery.class))) From 65314d13ca2cc1eb0def951ed7a03e2bfd0af525 Mon Sep 17 00:00:00 2001 From: Hesham Massoud Date: Mon, 26 Nov 2018 16:48:30 +0100 Subject: [PATCH 061/164] #300 grammar fix Co-Authored-By: ahmetoz --- .../sync/types/utils/FieldDefinitionsUpdateActionUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionsUpdateActionUtils.java b/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionsUpdateActionUtils.java index b1f6411dfb..65cffddfe9 100644 --- a/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionsUpdateActionUtils.java +++ b/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionsUpdateActionUtils.java @@ -162,7 +162,7 @@ private static List> buildRemoveFieldDefinitionOrFieldDefinit if (haveSameFieldType(oldFieldDefinition.getType(), newFieldDefinition.getType())) { return buildActions(oldFieldDefinition, newFieldDefinition); } else { - // since there is no way to change an field type on CTP, + // since there is no way to change a field type on CTP, // we remove the field definition and add a new one with a new field type return Arrays.asList( RemoveFieldDefinition.of(oldFieldDefinitionName), From ccd2a3f1e4629effe181d1d02802ae317eb312bf Mon Sep 17 00:00:00 2001 From: Lam Tran Date: Wed, 28 Nov 2018 14:39:37 +0100 Subject: [PATCH 062/164] Update docs/usage/TYPE_SYNC.md #300 fix grammar Co-Authored-By: ahmetoz --- docs/usage/TYPE_SYNC.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/usage/TYPE_SYNC.md b/docs/usage/TYPE_SYNC.md index d56b9e3e22..4a15a0d560 100644 --- a/docs/usage/TYPE_SYNC.md +++ b/docs/usage/TYPE_SYNC.md @@ -33,7 +33,7 @@ matched. You can instantiate the client the same way it is instantiated in the integration tests for this library found [here](/src/main/java/com/commercetools/sync/commons/utils/ClientConfigurationUtils.java#L45). -4. After the `sphereClient` is setup, a `TypeSyncOptions` should be be built as follows: +4. After the `sphereClient` is set up, a `TypeSyncOptions` should be be built as follows: ````java // instantiating a TypeSyncOptions final TypeSyncOptions typeSyncOptions = TypeSyncOptionsBuilder.of(sphereClient).build(); From 18d52d875e9ef0d56ecd8095aa6beca41bf1016b Mon Sep 17 00:00:00 2001 From: Lam Tran Date: Wed, 28 Nov 2018 14:41:54 +0100 Subject: [PATCH 063/164] Update src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildFieldDefinitionUpdateActionsTest.java #300 refactor naming in test Co-Authored-By: ahmetoz --- .../typeactionutils/BuildFieldDefinitionUpdateActionsTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildFieldDefinitionUpdateActionsTest.java b/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildFieldDefinitionUpdateActionsTest.java index 4e235ba3a7..7f3f375452 100644 --- a/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildFieldDefinitionUpdateActionsTest.java +++ b/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildFieldDefinitionUpdateActionsTest.java @@ -421,7 +421,7 @@ public void buildUpdateActions_WithDifferentType_ShouldRemoveOldFieldDefinitionA } @Test - public void buildUpdateActions_WithANullNewFieldDefinition_ShouldSkipNullFieldDefinitions() { + public void buildUpdateActions_WithNullNewFieldDefinition_ShouldSkipNullFieldDefinitions() { // preparation final Type oldType = mock(Type.class); final FieldDefinition oldFieldDefinition = FieldDefinition.of( From 9f34e58d4adf78cf5174ac84ed4ce01085a1634d Mon Sep 17 00:00:00 2001 From: Lam Tran Date: Wed, 28 Nov 2018 14:42:12 +0100 Subject: [PATCH 064/164] Update src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildFieldDefinitionUpdateActionsTest.java #300 refactor naming in test Co-Authored-By: ahmetoz --- .../typeactionutils/BuildFieldDefinitionUpdateActionsTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildFieldDefinitionUpdateActionsTest.java b/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildFieldDefinitionUpdateActionsTest.java index 7f3f375452..8f9f4263df 100644 --- a/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildFieldDefinitionUpdateActionsTest.java +++ b/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildFieldDefinitionUpdateActionsTest.java @@ -245,7 +245,7 @@ public void buildUpdateActions_WithOneExtraFieldDefinition_ShouldBuildAddFieldDe } @Test - public void buildUpdateActions_WithOneFieldDefinitionSwitch_ShouldBuildRemoveAndAddAFieldDefinitionsActions() { + public void buildUpdateActions_WithOneFieldDefinitionSwitch_ShouldBuildRemoveAndAddFieldDefinitionsActions() { final Type oldType = readObjectFromResource(TYPE_WITH_FIELDS_ABC, Type.class); final TypeDraft newTypeDraft = readObjectFromResource( From a98d0e9bcc5963a1b4602070f08c99b728d5f0a9 Mon Sep 17 00:00:00 2001 From: Lam Tran Date: Wed, 28 Nov 2018 14:42:33 +0100 Subject: [PATCH 065/164] Update src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildFieldDefinitionUpdateActionsTest.java #300 refactor naming in test Co-Authored-By: ahmetoz --- .../typeactionutils/BuildFieldDefinitionUpdateActionsTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildFieldDefinitionUpdateActionsTest.java b/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildFieldDefinitionUpdateActionsTest.java index 8f9f4263df..c60b5b9c36 100644 --- a/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildFieldDefinitionUpdateActionsTest.java +++ b/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildFieldDefinitionUpdateActionsTest.java @@ -112,7 +112,7 @@ public void buildUpdateActions_WithNullNewFieldDefinitionsAndExistingFieldDefini } @Test - public void buildUpdateActions_WithNullNewAFieldDefinitionsAndNoOldFieldDefinitions_ShouldNotBuildActions() { + public void buildUpdateActions_WithNullNewFieldDefinitionsAndNoOldFieldDefinitions_ShouldNotBuildActions() { final Type oldType = mock(Type.class); when(oldType.getFieldDefinitions()).thenReturn(emptyList()); when(oldType.getKey()).thenReturn("type_key_1"); From 1d7a357375ae069c738fc34d59fc4227016125f6 Mon Sep 17 00:00:00 2001 From: Lam Tran Date: Wed, 28 Nov 2018 14:43:02 +0100 Subject: [PATCH 066/164] Update src/main/java/com/commercetools/sync/types/utils/FieldDefinitionsUpdateActionUtils.java #300 rename commercetools to CTP Co-Authored-By: ahmetoz --- .../sync/types/utils/FieldDefinitionsUpdateActionUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionsUpdateActionUtils.java b/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionsUpdateActionUtils.java index 65cffddfe9..405eb42671 100644 --- a/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionsUpdateActionUtils.java +++ b/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionsUpdateActionUtils.java @@ -158,7 +158,7 @@ private static List> buildRemoveFieldDefinitionOrFieldDefinit return ofNullable(matchingNewFieldDefinition) .map(newFieldDefinition -> { if (newFieldDefinition.getType() != null) { - // field type is required so if null we let commercetools to throw exception + // field type is required so if null we let CTP to throw exception if (haveSameFieldType(oldFieldDefinition.getType(), newFieldDefinition.getType())) { return buildActions(oldFieldDefinition, newFieldDefinition); } else { From 6c40713ebd939916aac8f60649a5624514586e6b Mon Sep 17 00:00:00 2001 From: Lam Tran Date: Wed, 28 Nov 2018 14:44:07 +0100 Subject: [PATCH 067/164] Update src/main/java/com/commercetools/sync/producttypes/utils/AttributeDefinitionUpdateActionUtils.java #300 encapsulate method as private Co-Authored-By: ahmetoz --- .../utils/AttributeDefinitionUpdateActionUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/commercetools/sync/producttypes/utils/AttributeDefinitionUpdateActionUtils.java b/src/main/java/com/commercetools/sync/producttypes/utils/AttributeDefinitionUpdateActionUtils.java index eb7e8f2d11..3e95532455 100644 --- a/src/main/java/com/commercetools/sync/producttypes/utils/AttributeDefinitionUpdateActionUtils.java +++ b/src/main/java/com/commercetools/sync/producttypes/utils/AttributeDefinitionUpdateActionUtils.java @@ -67,7 +67,7 @@ static List> buildActions( * @return A list with the update actions or an empty list if the attribute definition enums are identical. */ @Nonnull - static List> buildEnumUpdateActions( + private static List> buildEnumUpdateActions( @Nonnull final AttributeDefinition oldAttributeDefinition, @Nonnull final AttributeDefinitionDraft newAttributeDefinitionDraft) { From fd5e63ca0b5274795f16fea4b7402fe0540049c0 Mon Sep 17 00:00:00 2001 From: aoz Date: Wed, 28 Nov 2018 14:53:36 +0100 Subject: [PATCH 068/164] #300 - close brackets in java docs --- .../producttypes/utils/LocalizedEnumValueUpdateActionUtils.java | 2 +- .../sync/types/utils/LocalizedEnumValueUpdateActionUtils.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/commercetools/sync/producttypes/utils/LocalizedEnumValueUpdateActionUtils.java b/src/main/java/com/commercetools/sync/producttypes/utils/LocalizedEnumValueUpdateActionUtils.java index d232d5d6c5..0f7d38aa97 100644 --- a/src/main/java/com/commercetools/sync/producttypes/utils/LocalizedEnumValueUpdateActionUtils.java +++ b/src/main/java/com/commercetools/sync/producttypes/utils/LocalizedEnumValueUpdateActionUtils.java @@ -24,7 +24,7 @@ public final class LocalizedEnumValueUpdateActionUtils { * Compares a list of old {@link LocalizedEnumValue}s with a list of new {@link LocalizedEnumValue}s for a given * attribute definition. * The method serves as a generic implementation for localized enum values syncing. The method takes in functions - * for building the required update actions (AddLocalizedEnumValue, RemoveEnumValue, ChangeLocalizedEnumValueOrder + * for building the required update actions (AddLocalizedEnumValue, RemoveEnumValue, ChangeLocalizedEnumValueOrder) * and 1-1 update actions on localized enum values (e.g. changeLabel) for the required resource. * *

If the list of new {@link LocalizedEnumValue}s is {@code null}, then remove actions are built for diff --git a/src/main/java/com/commercetools/sync/types/utils/LocalizedEnumValueUpdateActionUtils.java b/src/main/java/com/commercetools/sync/types/utils/LocalizedEnumValueUpdateActionUtils.java index ec82f19e19..f6b23e8fb5 100644 --- a/src/main/java/com/commercetools/sync/types/utils/LocalizedEnumValueUpdateActionUtils.java +++ b/src/main/java/com/commercetools/sync/types/utils/LocalizedEnumValueUpdateActionUtils.java @@ -18,7 +18,7 @@ public final class LocalizedEnumValueUpdateActionUtils { * Compares a list of old {@link LocalizedEnumValue}s with a list of new {@link LocalizedEnumValue}s for a given * field definition. * The method serves as an implementation for localized enum values syncing. The method takes in functions - * for building the required update actions (AddLocalizedEnumValue, RemoveEnumValue, ChangeLocalizedEnumValueOrder + * for building the required update actions (AddLocalizedEnumValue, RemoveEnumValue, ChangeLocalizedEnumValueOrder) * and 1-1 update actions on localized enum values (e.g. changeLabel) for the required resource. * *

From f7b3fde96bbb5808ac91bbbb4e1f0ee165a90aa0 Mon Sep 17 00:00:00 2001 From: aoz Date: Wed, 28 Nov 2018 14:56:16 +0100 Subject: [PATCH 069/164] #300 - update throws java doc for buildRemoveFieldDefinitionOrFieldDefinitionUpdateActions --- .../sync/types/utils/FieldDefinitionsUpdateActionUtils.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionsUpdateActionUtils.java b/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionsUpdateActionUtils.java index b1f6411dfb..193cb48b73 100644 --- a/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionsUpdateActionUtils.java +++ b/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionsUpdateActionUtils.java @@ -127,13 +127,12 @@ private static List> buildUpdateActions( * in the new draft. If the field definition still exists in the new draft, then compare the field * definition fields (name, label, etc..), and add the computed actions to the list of update actions. * Otherwise, if the field definitions are identical, an empty optional is returned. - * @throws BuildUpdateActionException in case there are field definitions with duplicate names or - * there are field definitions with the null field type. + * @throws DuplicateNameException in case there are field definitions drafts with duplicate names. */ @Nonnull private static List> buildRemoveFieldDefinitionOrFieldDefinitionUpdateActions( @Nonnull final List oldFieldDefinitions, - @Nonnull final List newFieldDefinitions) throws BuildUpdateActionException { + @Nonnull final List newFieldDefinitions) { final Map newFieldDefinitionsNameMap = newFieldDefinitions From 02a5fff450381baaf1050ab2dad599aa9cb91998 Mon Sep 17 00:00:00 2001 From: aoz Date: Wed, 28 Nov 2018 15:30:12 +0100 Subject: [PATCH 070/164] #300 - update java docs for buildActions for enumValues. --- .../utils/LocalizedEnumValueUpdateActionUtils.java | 8 ++++---- .../utils/PlainEnumValueUpdateActionUtils.java | 8 ++++---- .../types/utils/LocalizedEnumValueUpdateActionUtils.java | 7 +++---- .../sync/types/utils/PlainEnumValueUpdateActionUtils.java | 7 +++---- 4 files changed, 14 insertions(+), 16 deletions(-) diff --git a/src/main/java/com/commercetools/sync/producttypes/utils/LocalizedEnumValueUpdateActionUtils.java b/src/main/java/com/commercetools/sync/producttypes/utils/LocalizedEnumValueUpdateActionUtils.java index 0f7d38aa97..f5c80e1e1e 100644 --- a/src/main/java/com/commercetools/sync/producttypes/utils/LocalizedEnumValueUpdateActionUtils.java +++ b/src/main/java/com/commercetools/sync/producttypes/utils/LocalizedEnumValueUpdateActionUtils.java @@ -22,10 +22,10 @@ public final class LocalizedEnumValueUpdateActionUtils { /** * Compares a list of old {@link LocalizedEnumValue}s with a list of new {@link LocalizedEnumValue}s for a given - * attribute definition. - * The method serves as a generic implementation for localized enum values syncing. The method takes in functions - * for building the required update actions (AddLocalizedEnumValue, RemoveEnumValue, ChangeLocalizedEnumValueOrder) - * and 1-1 update actions on localized enum values (e.g. changeLabel) for the required resource. + * attribute definition and builds required update actions (e.g addLocalizedEnumValue, removeLocalizedEnumValue, + * changeLocalizedEnumValueOrder) and 1-1 update actions on localized enum values + * (e.g. changeLocalizedEnumValueLabel) for the required resource. If both the {@link LocalizedEnumValue}'s are + * identical, then no update action is needed and hence an empty {@link List} is returned. * *

If the list of new {@link LocalizedEnumValue}s is {@code null}, then remove actions are built for * every existing localized enum value in the {@code oldEnumValues} list. diff --git a/src/main/java/com/commercetools/sync/producttypes/utils/PlainEnumValueUpdateActionUtils.java b/src/main/java/com/commercetools/sync/producttypes/utils/PlainEnumValueUpdateActionUtils.java index 3756710abb..375985b1e3 100644 --- a/src/main/java/com/commercetools/sync/producttypes/utils/PlainEnumValueUpdateActionUtils.java +++ b/src/main/java/com/commercetools/sync/producttypes/utils/PlainEnumValueUpdateActionUtils.java @@ -21,10 +21,10 @@ public final class PlainEnumValueUpdateActionUtils { /** * Compares a list of old {@link EnumValue}s with a list of new {@link EnumValue}s for a given - * attribute definition. - * The method serves as a generic implementation for plain enum values syncing. The method takes in functions - * for building the required update actions (AddEnumValue, RemoveEnumValue, ChangeEnumValueOrder - * and 1-1 update actions on plain enum values (e.g. changeLabel) for the required resource. + * attribute definition and builds required update actions (e.g addEnumValue, removeEnumValue, + * changeEnumValueOrder) and 1-1 update actions on enum values (e.g. changeEnumValueLabel) for the required + * resource. If both the {@link EnumValue}'s are identical, then no update action is needed and hence + * an empty {@link List} is returned. * *

If the list of new {@link EnumValue}s is {@code null}, then remove actions are built for * every existing plain enum value in the {@code oldEnumValues} list. diff --git a/src/main/java/com/commercetools/sync/types/utils/LocalizedEnumValueUpdateActionUtils.java b/src/main/java/com/commercetools/sync/types/utils/LocalizedEnumValueUpdateActionUtils.java index f6b23e8fb5..be4651e740 100644 --- a/src/main/java/com/commercetools/sync/types/utils/LocalizedEnumValueUpdateActionUtils.java +++ b/src/main/java/com/commercetools/sync/types/utils/LocalizedEnumValueUpdateActionUtils.java @@ -16,10 +16,9 @@ public final class LocalizedEnumValueUpdateActionUtils { /** * Compares a list of old {@link LocalizedEnumValue}s with a list of new {@link LocalizedEnumValue}s for a given - * field definition. - * The method serves as an implementation for localized enum values syncing. The method takes in functions - * for building the required update actions (AddLocalizedEnumValue, RemoveEnumValue, ChangeLocalizedEnumValueOrder) - * and 1-1 update actions on localized enum values (e.g. changeLabel) for the required resource. + * field definition and builds required update actions (e.g addLocalizedEnumValue, changeLocalizedEnumValueOrder). + * If both the {@link LocalizedEnumValue}'s are identical, then no update action is needed and hence + * an empty {@link List} is returned. * *

* TODO: Check GITHUB ISSUE#339 for missing FieldDefinition update actions. diff --git a/src/main/java/com/commercetools/sync/types/utils/PlainEnumValueUpdateActionUtils.java b/src/main/java/com/commercetools/sync/types/utils/PlainEnumValueUpdateActionUtils.java index 2928b06784..a17e206f14 100644 --- a/src/main/java/com/commercetools/sync/types/utils/PlainEnumValueUpdateActionUtils.java +++ b/src/main/java/com/commercetools/sync/types/utils/PlainEnumValueUpdateActionUtils.java @@ -15,10 +15,9 @@ public final class PlainEnumValueUpdateActionUtils { /** * Compares a list of old {@link EnumValue}s with a list of new {@link EnumValue}s for a given - * field definition. - * The method serves as an implementation for plain enum values syncing. The method takes in functions - * for building the required update actions (AddEnumValue, ChangeEnumValueOrder - * and 1-1 update actions on plain enum values (e.g. changeLabel) for the required resource. + * field definition and builds required update actions (e.g addEnumValue, changeEnumValueOrder). + * If both the {@link EnumValue}'s are identical, then no update action is needed and hence + * an empty {@link List} is returned. * *

* TODO: Check GITHUB ISSUE#339 for missing FieldDefinition update actions. From 5b74a01f2aa33a7e2b321620532e1ccfce73d728 Mon Sep 17 00:00:00 2001 From: aoz Date: Tue, 4 Dec 2018 16:40:29 +0100 Subject: [PATCH 071/164] #300 fix java doc --- .../commercetools/sync/services/ProductTypeService.java | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/main/java/com/commercetools/sync/services/ProductTypeService.java b/src/main/java/com/commercetools/sync/services/ProductTypeService.java index 823e3c49e5..13e53ab8fe 100644 --- a/src/main/java/com/commercetools/sync/services/ProductTypeService.java +++ b/src/main/java/com/commercetools/sync/services/ProductTypeService.java @@ -64,13 +64,6 @@ CompletionStage>> fetchCachedProductAttr * @return {@link CompletionStage}<{@link Map}> in which the result of it's completion contains a {@link Set} * of all matching ProductType. */ - /** - * Queries existing {@link ProductType}'s against set of keys. - * - * @param keys {@link Set} of sku values, used in search predicate - * @return {@link CompletionStage} of matching {@link ProductType}s or empty set when there is no product type with - * corresponding {@code keys}. - */ @Nonnull CompletionStage> fetchMatchingProductTypesByKeys(@Nonnull final Set keys); From a6c5536b3dadcb6b20f1115c23965f58f19a5b06 Mon Sep 17 00:00:00 2001 From: aoz Date: Tue, 4 Dec 2018 16:48:12 +0100 Subject: [PATCH 072/164] #300 handle fetchMatchingTypesByKeys using CtpQueryUtils. --- .../services/impl/TypeServiceImplIT.java | 19 +++++++------------ .../sync/services/TypeService.java | 11 +++++++---- .../sync/services/impl/TypeServiceImpl.java | 6 ++++-- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/integration-test/java/com/commercetools/sync/integration/services/impl/TypeServiceImplIT.java b/src/integration-test/java/com/commercetools/sync/integration/services/impl/TypeServiceImplIT.java index 959e5eb646..ffd439b187 100644 --- a/src/integration-test/java/com/commercetools/sync/integration/services/impl/TypeServiceImplIT.java +++ b/src/integration-test/java/com/commercetools/sync/integration/services/impl/TypeServiceImplIT.java @@ -165,11 +165,9 @@ public void fetchMatchingTypesByKeys_WithAnyExistingKeys_ShouldReturnASetOfTypes assertThat(errorCallBackMessages).isEmpty(); } - // TODO: GITHUB ISSUE#331 - @Ignore @Test public void fetchMatchingTypesByKeys_WithBadGateWayExceptionAlways_ShouldFail() { - // Mock sphere client to return BadeGatewayException on any request. + // Mock sphere client to return BadGatewayException on any request. final SphereClient spyClient = spy(CTP_TARGET_CLIENT); when(spyClient.execute(any(TypeQuery.class))) .thenReturn(CompletableFutureUtils.exceptionallyCompletedFuture(new BadGatewayException())) @@ -188,15 +186,12 @@ public void fetchMatchingTypesByKeys_WithBadGateWayExceptionAlways_ShouldFail() final Set keys = new HashSet<>(); keys.add(OLD_TYPE_KEY); - final Set fetchedTypes = spyTypeService.fetchMatchingTypesByKeys(keys) - .toCompletableFuture().join(); - assertThat(fetchedTypes).hasSize(0); - assertThat(errorCallBackExceptions).isNotEmpty(); - assertThat(errorCallBackExceptions.get(0).getCause()).isExactlyInstanceOf(BadGatewayException.class); - assertThat(errorCallBackMessages).isNotEmpty(); - assertThat(errorCallBackMessages.get(0)) - .isEqualToIgnoringCase(format("Failed to fetch types with keys: '%s'. Reason: %s", - keys.toString(), errorCallBackExceptions.get(0))); + // test and assert + assertThat(errorCallBackExceptions).isEmpty(); + assertThat(errorCallBackMessages).isEmpty(); + assertThat(spyTypeService.fetchMatchingTypesByKeys(keys)) + .hasFailedWithThrowableThat() + .isExactlyInstanceOf(BadGatewayException.class); } @Test diff --git a/src/main/java/com/commercetools/sync/services/TypeService.java b/src/main/java/com/commercetools/sync/services/TypeService.java index 24b389667e..50c7ce31a3 100644 --- a/src/main/java/com/commercetools/sync/services/TypeService.java +++ b/src/main/java/com/commercetools/sync/services/TypeService.java @@ -9,6 +9,7 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.concurrent.CompletionStage; @@ -34,11 +35,13 @@ public interface TypeService { CompletionStage> fetchCachedTypeId(@Nonnull final String key); /** - * Queries existing {@link Type}'s against set of keys. + * Given a {@link Set} of Type keys, this method fetches a set of all the ProductTypes, matching this given + * set of keys in the CTP project, defined in a potentially injected {@link io.sphere.sdk.client.SphereClient}. A + * mapping of the key to the id of the fetched Type is persisted in an in-memory map. * - * @param keys {@link Set} of sku values, used in search predicate - * @return {@link CompletionStage} of matching types or empty set when there is no type with corresponding - * {@code keys}. + * @param keys set of Type keys to fetch matching Type by. + * @return {@link CompletionStage}<{@link Map}> in which the result of it's completion contains a {@link Set} + * of all matching Type. */ @Nonnull CompletionStage> fetchMatchingTypesByKeys(@Nonnull final Set keys); diff --git a/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java b/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java index 25b1d763cd..8c79c0d9c2 100644 --- a/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java +++ b/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java @@ -6,13 +6,13 @@ import com.commercetools.sync.types.TypeSyncOptions; import io.sphere.sdk.commands.UpdateAction; import io.sphere.sdk.queries.PagedResult; -import io.sphere.sdk.queries.QueryExecutionUtils; import io.sphere.sdk.types.Type; import io.sphere.sdk.types.TypeDraft; import io.sphere.sdk.types.commands.TypeCreateCommand; import io.sphere.sdk.types.commands.TypeUpdateCommand; import io.sphere.sdk.types.queries.TypeQuery; import io.sphere.sdk.types.queries.TypeQueryBuilder; +import org.apache.commons.lang3.StringUtils; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -25,6 +25,7 @@ import java.util.concurrent.CompletionStage; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Consumer; +import java.util.function.Function; import static java.lang.String.format; import static java.util.stream.Collectors.toSet; @@ -68,9 +69,10 @@ public CompletionStage> fetchMatchingTypesByKeys(@Nonnull final Set queryModel.key().isIn(keys)) .build(); - return QueryExecutionUtils.queryAll(syncOptions.getCtpClient(), typeQuery) + return CtpQueryUtils.queryAll(syncOptions.getCtpClient(), typeQuery, Function.identity()) .thenApply(types -> types .stream() + .flatMap(List::stream) .peek(type -> keyToIdCache.put(type.getKey(), type.getId())) .collect(toSet())); } From 73115354f672d282aaefce3823d2548c91ce4c70 Mon Sep 17 00:00:00 2001 From: aoz Date: Tue, 4 Dec 2018 17:07:59 +0100 Subject: [PATCH 073/164] #300 handle failed fetch exception in type sync. --- .../commercetools/sync/types/TypeSync.java | 66 +++++++++++-------- 1 file changed, 40 insertions(+), 26 deletions(-) diff --git a/src/main/java/com/commercetools/sync/types/TypeSync.java b/src/main/java/com/commercetools/sync/types/TypeSync.java index 4a3b2ae8cb..11235dc46e 100644 --- a/src/main/java/com/commercetools/sync/types/TypeSync.java +++ b/src/main/java/com/commercetools/sync/types/TypeSync.java @@ -9,6 +9,7 @@ import io.sphere.sdk.commands.UpdateAction; import io.sphere.sdk.types.Type; import io.sphere.sdk.types.TypeDraft; +import org.apache.commons.lang3.tuple.ImmutablePair; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -21,7 +22,6 @@ import static com.commercetools.sync.commons.utils.SyncUtils.batchElements; import static com.commercetools.sync.types.utils.TypeSyncUtils.buildActions; import static java.lang.String.format; -import static java.util.Collections.emptySet; import static java.util.Optional.ofNullable; import static java.util.concurrent.CompletableFuture.completedFuture; import static java.util.function.Function.identity; @@ -48,6 +48,18 @@ public TypeSync(@Nonnull final TypeSyncOptions typeSyncOptions) { this.typeService = new TypeServiceImpl(typeSyncOptions); } + /** + * Takes a {@link TypeSyncOptions} and a {@link TypeService} instances to instantiate + * a new {@link TypeSync} instance that could be used to sync type drafts in the CTP project specified + * in the injected {@link TypeSyncOptions} instance. + * + *

NOTE: This constructor is mainly to be used for tests where the services can be mocked and passed to. + * + * @param typeSyncOptions 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 type service which is responsible for fetching/caching the Types from the CTP + * project. + */ public TypeSync(@Nonnull final TypeSyncOptions typeSyncOptions, @Nonnull final TypeService typeService) { super(new TypeSyncStatistics(), typeSyncOptions); @@ -72,16 +84,24 @@ protected CompletionStage process(@Nonnull final Listupdate or create + * requests accordingly) on the target project. + * + *

In case of error during of fetching of existing types, the error callback will be triggered. + * And the sync process would stop for the given batch. + *

* * @param batch batch of drafts that need to be synced - * @return {@link CompletionStage} of {@link TypeSyncStatistics} that indicates method progress. + * @return a {@link CompletionStage} containing an instance + * of {@link TypeSyncStatistics} which contains information about the result of syncing the supplied + * batch to the target project. */ @Override protected CompletionStage processBatch(@Nonnull final List batch) { + final Set validTypeDrafts = batch.stream().filter(this::validateDraft).collect(toSet()); if (validTypeDrafts.isEmpty()) { @@ -90,9 +110,20 @@ protected CompletionStage processBatch(@Nonnull final List keys = validTypeDrafts.stream().map(TypeDraft::getKey).collect(toSet()); - return fetchExistingTypes(keys) - .thenCompose(oldTypes -> syncBatch(oldTypes, validTypeDrafts)) - .thenApply(ignored -> { + return typeService.fetchMatchingTypesByKeys(keys) + .handle(ImmutablePair::new) + .thenCompose(fetchResponse -> { + final Set fetchedTypes = fetchResponse.getKey(); + final Throwable exception = fetchResponse.getValue(); + + if (exception != null) { + final String errorMessage = format(CTP_TYPE_FETCH_FAILED, keys); + handleError(errorMessage, exception, keys.size()); + return CompletableFuture.completedFuture(null); + } else { + return syncBatch(fetchedTypes, validTypeDrafts); + } + }).thenApply(ignored -> { statistics.incrementProcessed(batch.size()); return statistics; }); @@ -119,23 +150,6 @@ private boolean validateDraft(@Nullable final TypeDraft draft) { return false; } - /** - * Given a set of type keys, fetches the corresponding types from CTP if they exist. - * - * @param keys the keys of the types that are wanted to be fetched. - * @return a {@link CompletionStage} which contains the set of types corresponding to the keys. - */ - private CompletionStage> fetchExistingTypes(@Nonnull final Set keys) { - return typeService - .fetchMatchingTypesByKeys(keys) - .exceptionally(exception -> { - final String errorMessage = format(CTP_TYPE_FETCH_FAILED, keys); - handleError(errorMessage, exception, keys.size()); - - return emptySet(); - }); - } - /** * Given a {@link String} {@code errorMessage} and a {@link Throwable} {@code exception}, this method calls the * optional error callback specified in the {@code syncOptions} and updates the {@code statistics} instance by From af1cb493ceb5a136e798f42e62d78ce60031c595 Mon Sep 17 00:00:00 2001 From: aoz Date: Tue, 4 Dec 2018 17:09:31 +0100 Subject: [PATCH 074/164] #300 remove redundant return --- src/main/java/com/commercetools/sync/types/TypeSync.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/commercetools/sync/types/TypeSync.java b/src/main/java/com/commercetools/sync/types/TypeSync.java index 11235dc46e..14b12cb2fe 100644 --- a/src/main/java/com/commercetools/sync/types/TypeSync.java +++ b/src/main/java/com/commercetools/sync/types/TypeSync.java @@ -174,7 +174,7 @@ private void handleError(@Nonnull final String errorMessage, @Nullable final Thr * @param newTypes drafts that need to be synced. * @return a {@link CompletionStage} which contains an empty result after execution of the update */ - private CompletionStage syncBatch( + private CompletionStage syncBatch( @Nonnull final Set oldTypes, @Nonnull final Set newTypes) { @@ -190,7 +190,7 @@ private CompletionStage syncBatch( .orElseGet(() -> createType(newType)); }) .map(CompletionStage::toCompletableFuture) - .toArray(CompletableFuture[]::new)).thenApply(result -> statistics); + .toArray(CompletableFuture[]::new)); } /** From b5a1a702e7a9597f832d0dbe678a9404a6f242ee Mon Sep 17 00:00:00 2001 From: aoz Date: Tue, 4 Dec 2018 17:20:25 +0100 Subject: [PATCH 075/164] #300 add unit test for type failed fetch scenario --- .../sync/types/TypeSyncTest.java | 79 +++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 src/test/java/com/commercetools/sync/types/TypeSyncTest.java diff --git a/src/test/java/com/commercetools/sync/types/TypeSyncTest.java b/src/test/java/com/commercetools/sync/types/TypeSyncTest.java new file mode 100644 index 0000000000..acdb72491f --- /dev/null +++ b/src/test/java/com/commercetools/sync/types/TypeSyncTest.java @@ -0,0 +1,79 @@ +package com.commercetools.sync.types; + +import com.commercetools.sync.services.TypeService; +import com.commercetools.sync.types.helpers.TypeSyncStatistics; +import io.sphere.sdk.client.SphereClient; +import io.sphere.sdk.models.SphereException; +import io.sphere.sdk.types.ResourceTypeIdsSetBuilder; +import io.sphere.sdk.types.TypeDraft; +import io.sphere.sdk.types.TypeDraftBuilder; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CompletionException; + +import static com.commercetools.sync.commons.asserts.statistics.AssertionsForStatistics.assertThat; +import static io.sphere.sdk.models.LocalizedString.ofEnglish; +import static java.util.Collections.emptyList; +import static java.util.Collections.singleton; +import static java.util.Collections.singletonList; +import static java.util.concurrent.CompletableFuture.supplyAsync; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class TypeSyncTest { + @Test + public void sync_WithErrorFetchingExistingKeys_ShouldExecuteCallbackOnErrorAndIncreaseFailedCounter() { + // preparation + + final TypeDraft newTypeDraft = TypeDraftBuilder.of( + "foo", + ofEnglish("name"), + ResourceTypeIdsSetBuilder.of().addCategories().build()) + .description(ofEnglish("desc")) + .fieldDefinitions(emptyList()) + .build(); + final List errorMessages = new ArrayList<>(); + final List exceptions = new ArrayList<>(); + + final TypeSyncOptions syncOptions = TypeSyncOptionsBuilder + .of(mock(SphereClient.class)) + .errorCallback((errorMessage, exception) -> { + errorMessages.add(errorMessage); + exceptions.add(exception); + }) + .build(); + + + final TypeService mockTypeService = mock(TypeService.class); + + when(mockTypeService.fetchMatchingTypesByKeys(singleton(newTypeDraft.getKey()))) + .thenReturn(supplyAsync(() -> { throw new SphereException(); })); + + final TypeSync typeSync = new TypeSync(syncOptions, mockTypeService); + + // test + final TypeSyncStatistics typeSyncStatistics = typeSync + .sync(singletonList(newTypeDraft)) + .toCompletableFuture().join(); + + // assertions + assertThat(errorMessages) + .hasSize(1) + .hasOnlyOneElementSatisfying(message -> + assertThat(message).isEqualTo("Failed to fetch existing types of keys '[foo]'.") + ); + + assertThat(exceptions) + .hasSize(1) + .hasOnlyOneElementSatisfying(throwable -> { + assertThat(throwable).isExactlyInstanceOf(CompletionException.class); + assertThat(throwable).hasCauseExactlyInstanceOf(SphereException.class); + }); + + assertThat(typeSyncStatistics).hasValues(1, 0, 0, 1); + } + +} From eb9e21611a2f377a1b247324a3789a2bba48587a Mon Sep 17 00:00:00 2001 From: aoz Date: Tue, 4 Dec 2018 17:36:58 +0100 Subject: [PATCH 076/164] #300 update type sync documentation --- docs/usage/PRODUCT_TYPE_SYNC.md | 4 +-- docs/usage/QUICK_START.md | 4 ++- docs/usage/TYPE_SYNC.md | 51 ++++++++------------------------- 3 files changed, 17 insertions(+), 42 deletions(-) diff --git a/docs/usage/PRODUCT_TYPE_SYNC.md b/docs/usage/PRODUCT_TYPE_SYNC.md index 66fd66a2c2..3f4bfa3a93 100644 --- a/docs/usage/PRODUCT_TYPE_SYNC.md +++ b/docs/usage/PRODUCT_TYPE_SYNC.md @@ -26,8 +26,8 @@ against a [ProductTypeDraft](https://docs.commercetools.com/http-api-projects-pr #### Prerequisites -1. The sync expects a list of `ProductTypeDrafts`s that have their `key` fields set to be matched with -product types in the target CTP project. Also, the product types in the target project are expected to have the `key` +1. The sync expects a list of `ProductTypeDraft`s that have their `key` fields set to be matched with +product types in the target CTP project. Also, the product types in the target project are expected to have the `key` fields set, otherwise they won't be matched. 2. Create a `sphereClient` [as described here](IMPORTANT_USAGE_TIPS.md#sphereclient-creation). diff --git a/docs/usage/QUICK_START.md b/docs/usage/QUICK_START.md index 64827c8615..41767a1d2a 100644 --- a/docs/usage/QUICK_START.md +++ b/docs/usage/QUICK_START.md @@ -83,4 +83,6 @@ implementation 'com.commercetools:commercetools-sync-java:v1.0.0-M14' #### More Details *[Product Sync](PRODUCT_SYNC.md), [ProductType Sync](PRODUCT_TYPE_SYNC.md), -[Category Sync](CATEGORY_SYNC.md), [Inventory Sync](INVENTORY_SYNC.md)* +[Category Sync](CATEGORY_SYNC.md), [Inventory Sync](INVENTORY_SYNC.md), +[Type Sync](TYPE_SYNC.md), +* diff --git a/docs/usage/TYPE_SYNC.md b/docs/usage/TYPE_SYNC.md index 4a15a0d560..b0ef9afd0d 100644 --- a/docs/usage/TYPE_SYNC.md +++ b/docs/usage/TYPE_SYNC.md @@ -1,6 +1,8 @@ -# commercetools type sync +# Type Sync -A utility which provides an API for building CTP type update actions and type synchronization. +Module used for importing/syncing Types into a commercetools project. +It also provides utilities for generating update actions based on the comparison of a [Type](https://docs.commercetools.com/http-api-projects-types.html#type) +against a [TypeDraft](https://docs.commercetools.com/http-api-projects-types.html#typedraft). @@ -21,17 +23,11 @@ A utility which provides an API for building CTP type update actions and type sy ### Sync list of type drafts #### Prerequisites -1. The sync expects a list of non-null `TypeDrafts` objects that have their `key` fields set to match the -types from the source to the target. Also, the target project is expected to have the `key` fields set, otherwise they won't be -matched. -2. It is an important responsibility of the user of the library to instantiate a `sphereClient` that has the following properties: - - Limits the number of concurrent requests done to CTP. This can be done by decorating the `sphereClient` with - [QueueSphereClientDecorator](http://commercetools.github.io/commercetools-jvm-sdk/apidocs/io/sphere/sdk/client/QueueSphereClientDecorator.html) - - Retries on 5xx errors with a retry strategy. This can be achieved by decorating the `sphereClient` with the - [RetrySphereClientDecorator](http://commercetools.github.io/commercetools-jvm-sdk/apidocs/io/sphere/sdk/client/RetrySphereClientDecorator.html) - - You can instantiate the client the same way it is instantiated in the integration tests for this library found - [here](/src/main/java/com/commercetools/sync/commons/utils/ClientConfigurationUtils.java#L45). +1. The sync expects a list of `TypeDraft`s that have their `key` fields set to be matched with +types in the target CTP project. Also, the types in the target project are expected to have the `key` +fields set, otherwise they won't be matched. + +2. Create a `sphereClient` [as described here](IMPORTANT_USAGE_TIPS.md#sphereclient-creation). 4. After the `sphereClient` is set up, a `TypeSyncOptions` should be be built as follows: ````java @@ -39,32 +35,7 @@ matched. final TypeSyncOptions typeSyncOptions = TypeSyncOptionsBuilder.of(sphereClient).build(); ```` -The options can be used to provide additional optional configuration for the sync as well: -- `errorCallBack` -a callback that is called whenever an event occurs during the sync process that represents an error. Currently, these -events. - -- `warningCallBack` -a callback that is called whenever an event occurs during the sync process that represents a warning. Currently, these -events. - -- `beforeUpdateCallback` -a filter function which can be applied on a generated list of update actions. It allows the user to intercept type - **_update_** actions just before they are sent to CTP API. - -- `beforeCreateCallback` -a filter function which can be applied on a type draft before a request to create it on CTP is issued. It allows the -user to intercept type **_create_** request to modify the draft before the create request is sent to CTP API. - -Example of options usage, that sets the error and warning callbacks to output the message to the log error and warning -streams would look as follows: -```java -final Logger logger = LoggerFactory.getLogger(MySync.class); -final TypeSyncOptions typeSyncOptions = TypeSyncOptionsBuilder.of(sphereClient) - .errorCallBack(logger::error) - .warningCallBack(logger::warn) - .build(); -``` +[More information about Sync Options](SYNC_OPTIONS.md). #### Running the sync After all the aforementioned points in the previous section have been fulfilled, to run the sync: @@ -92,6 +63,8 @@ __Note__ The statistics object contains the processing time of the last batch on More examples of how to use the sync can be found [here](/src/integration-test/java/com/commercetools/sync/integration/types/TypeSyncIT.java). +*Make sure to read the [Important Usage Tips](IMPORTANT_USAGE_TIPS.md) for optimal performance.* + ### Build all update actions A utility method provided by the library to compare a `Type` with a new `TypeDraft` and results in a list of type update actions. From de9c4ee5ea96eb3fc569d4c444c580291989ad6b Mon Sep 17 00:00:00 2001 From: aoz Date: Tue, 4 Dec 2018 18:13:30 +0100 Subject: [PATCH 077/164] #300 add ctpprojectsource IT for type resource. --- .../ctpprojectsource/type/TypeSyncIT.java | 138 ++++++++++++++++++ 1 file changed, 138 insertions(+) create mode 100644 src/integration-test/java/com/commercetools/sync/integration/ctpprojectsource/type/TypeSyncIT.java diff --git a/src/integration-test/java/com/commercetools/sync/integration/ctpprojectsource/type/TypeSyncIT.java b/src/integration-test/java/com/commercetools/sync/integration/ctpprojectsource/type/TypeSyncIT.java new file mode 100644 index 0000000000..48d8ca1934 --- /dev/null +++ b/src/integration-test/java/com/commercetools/sync/integration/ctpprojectsource/type/TypeSyncIT.java @@ -0,0 +1,138 @@ +package com.commercetools.sync.integration.ctpprojectsource.type; + +import com.commercetools.sync.types.TypeSync; +import com.commercetools.sync.types.TypeSyncOptions; +import com.commercetools.sync.types.TypeSyncOptionsBuilder; +import com.commercetools.sync.types.helpers.TypeSyncStatistics; +import io.sphere.sdk.models.LocalizedString; +import io.sphere.sdk.types.Type; +import io.sphere.sdk.types.TypeDraft; +import io.sphere.sdk.types.TypeDraftBuilder; +import io.sphere.sdk.types.queries.TypeQuery; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +import static com.commercetools.sync.commons.asserts.statistics.AssertionsForStatistics.assertThat; +import static com.commercetools.sync.integration.commons.utils.ITUtils.deleteTypesFromTargetAndSource; +import static com.commercetools.sync.integration.commons.utils.SphereClientUtils.CTP_SOURCE_CLIENT; +import static com.commercetools.sync.integration.commons.utils.SphereClientUtils.CTP_TARGET_CLIENT; +import static com.commercetools.sync.integration.commons.utils.TypeITUtils.populateSourceProject; +import static com.commercetools.sync.integration.commons.utils.TypeITUtils.populateTargetProject; +import static org.assertj.core.api.Assertions.assertThat; + +public class TypeSyncIT { + + /** + * Deletes types from source and target CTP projects. + * Populates source and target CTP projects with test data. + */ + @Before + public void setup() { + deleteTypesFromTargetAndSource(); + populateSourceProject(); + populateTargetProject(); + } + + /** + * Deletes all the test data from the {@code CTP_SOURCE_CLIENT} and the {@code CTP_SOURCE_CLIENT} projects that + * were set up in this test class. + */ + @AfterClass + public static void tearDown() { + deleteTypesFromTargetAndSource(); + } + + @Test + public void sync_WithoutUpdates_ShouldReturnProperStatistics() { + // preparation + final List types = CTP_SOURCE_CLIENT + .execute(TypeQuery.of()) + .toCompletableFuture().join().getResults(); + + final List typeDrafts = types + .stream() + .map(type -> TypeDraftBuilder.of(type.getKey(), type.getName(), type.getResourceTypeIds()) + .description(type.getDescription()) + .fieldDefinitions(type.getFieldDefinitions())) + .map(TypeDraftBuilder::build) + .collect(Collectors.toList()); + + final List errorMessages = new ArrayList<>(); + final List exceptions = new ArrayList<>(); + + final TypeSyncOptions typeSyncOptions = TypeSyncOptionsBuilder + .of(CTP_TARGET_CLIENT) + .errorCallback((error, throwable) -> { + errorMessages.add(error); + exceptions.add(throwable); + }) + .build(); + + final TypeSync typeSync = new TypeSync(typeSyncOptions); + + // test + final TypeSyncStatistics typeSyncStatistics = typeSync + .sync(typeDrafts) + .toCompletableFuture().join(); + + // assertion + assertThat(errorMessages).isEmpty(); + assertThat(exceptions).isEmpty(); + assertThat(typeSyncStatistics).hasValues(2, 1, 0, 0); + assertThat(typeSyncStatistics + .getReportMessage()) + .isEqualTo("Summary: 2 types were processed in total" + + " (1 created, 0 updated and 0 failed to sync)."); + + } + + @Test + public void sync_WithUpdates_ShouldReturnProperStatistics() { + // preparation + final List types = CTP_SOURCE_CLIENT + .execute(TypeQuery.of()) + .toCompletableFuture().join().getResults(); + + final List typeDrafts = types + .stream() + .map(type -> TypeDraftBuilder.of(type.getKey(), + LocalizedString.ofEnglish("updated_name"), type.getResourceTypeIds()) + .description(type.getDescription()) + .fieldDefinitions(type.getFieldDefinitions())) + .map(TypeDraftBuilder::build) + .collect(Collectors.toList()); + + + final List errorMessages = new ArrayList<>(); + final List exceptions = new ArrayList<>(); + + final TypeSyncOptions typeSyncOptions = TypeSyncOptionsBuilder + .of(CTP_TARGET_CLIENT) + .errorCallback((error, throwable) -> { + errorMessages.add(error); + exceptions.add(throwable); + }) + .build(); + + final TypeSync typeSync = new TypeSync(typeSyncOptions); + + // test + final TypeSyncStatistics typeSyncStatistics = typeSync + .sync(typeDrafts) + .toCompletableFuture().join(); + + // assertion + assertThat(errorMessages).isEmpty(); + assertThat(exceptions).isEmpty(); + assertThat(typeSyncStatistics).hasValues(2, 1, 1, 0); + assertThat(typeSyncStatistics + .getReportMessage()) + .isEqualTo("Summary: 2 types were processed in total" + + " (1 created, 1 updated and 0 failed to sync)."); + } +} From fb161647fe68aabbe8acc3be7197265478916ca1 Mon Sep 17 00:00:00 2001 From: aoz Date: Tue, 4 Dec 2018 18:50:24 +0100 Subject: [PATCH 078/164] #300 refactor type ITs. --- .../externalsource/types/TypeSyncIT.java | 459 ++++++------------ 1 file changed, 141 insertions(+), 318 deletions(-) diff --git a/src/integration-test/java/com/commercetools/sync/integration/externalsource/types/TypeSyncIT.java b/src/integration-test/java/com/commercetools/sync/integration/externalsource/types/TypeSyncIT.java index f42ae70908..c5ce846ff7 100644 --- a/src/integration-test/java/com/commercetools/sync/integration/externalsource/types/TypeSyncIT.java +++ b/src/integration-test/java/com/commercetools/sync/integration/externalsource/types/TypeSyncIT.java @@ -2,19 +2,17 @@ import com.commercetools.sync.commons.asserts.statistics.AssertionsForStatistics; -import com.commercetools.sync.services.TypeService; -import com.commercetools.sync.services.impl.TypeServiceImpl; import com.commercetools.sync.types.TypeSync; import com.commercetools.sync.types.TypeSyncOptions; import com.commercetools.sync.types.TypeSyncOptionsBuilder; import com.commercetools.sync.types.helpers.TypeSyncStatistics; import io.sphere.sdk.client.BadGatewayException; import io.sphere.sdk.client.ConcurrentModificationException; +import io.sphere.sdk.client.ErrorResponseException; import io.sphere.sdk.client.SphereClient; import io.sphere.sdk.models.EnumValue; import io.sphere.sdk.models.LocalizedEnumValue; import io.sphere.sdk.models.LocalizedString; -import io.sphere.sdk.models.SphereException; import io.sphere.sdk.models.TextInputHint; import io.sphere.sdk.types.EnumFieldType; import io.sphere.sdk.types.FieldDefinition; @@ -30,19 +28,17 @@ import org.junit.AfterClass; import org.junit.Before; import org.junit.Test; -import org.mockito.Mockito; import javax.annotation.Nonnull; import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.Locale; import java.util.Optional; +import java.util.concurrent.CompletionException; import java.util.stream.Collectors; import java.util.stream.IntStream; -import static com.commercetools.sync.integration.commons.utils.ITUtils.deleteTypesFromTargetAndSource; -import static com.commercetools.sync.integration.commons.utils.SphereClientUtils.CTP_SOURCE_CLIENT; +import static com.commercetools.sync.integration.commons.utils.ITUtils.deleteTypes; import static com.commercetools.sync.integration.commons.utils.SphereClientUtils.CTP_TARGET_CLIENT; import static com.commercetools.sync.integration.commons.utils.TypeITUtils.FIELD_DEFINITION_1; import static com.commercetools.sync.integration.commons.utils.TypeITUtils.FIELD_DEFINITION_2; @@ -61,15 +57,9 @@ import static java.lang.String.format; import static java.util.Arrays.asList; import static java.util.Collections.singletonList; -import static java.util.concurrent.CompletableFuture.supplyAsync; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.startsWith; import static org.mockito.Mockito.any; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.eq; -import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -81,7 +71,7 @@ public class TypeSyncIT { */ @Before public void setup() { - deleteTypesFromTargetAndSource(); + deleteTypes(CTP_TARGET_CLIENT); populateSourceProject(); populateTargetProject(); } @@ -92,12 +82,13 @@ public void setup() { */ @AfterClass public static void tearDown() { - deleteTypesFromTargetAndSource(); + deleteTypes(CTP_TARGET_CLIENT); } @Test public void sync_WithUpdatedType_ShouldUpdateType() { + // preparation final Optional oldTypeBefore = getTypeByKey(CTP_TARGET_CLIENT, TYPE_KEY_1); assertThat(oldTypeBefore).isNotEmpty(); @@ -115,11 +106,13 @@ public void sync_WithUpdatedType_ShouldUpdateType() { final TypeSync typeSync = new TypeSync(typeSyncOptions); + // test final TypeSyncStatistics typeSyncStatistics = typeSync .sync(singletonList(newTypeDraft)) - .toCompletableFuture().join(); - + .toCompletableFuture() + .join(); + //assertions AssertionsForStatistics.assertThat(typeSyncStatistics).hasValues(1, 0, 1, 0); @@ -135,9 +128,7 @@ public void sync_WithUpdatedType_ShouldUpdateType() { @Test public void sync_WithNewType_ShouldCreateType() { - final Optional oldTypeBefore = getTypeByKey(CTP_TARGET_CLIENT, TYPE_KEY_2); - assertThat(oldTypeBefore).isEmpty(); - + //preparation final TypeDraft newTypeDraft = TypeDraftBuilder.of( TYPE_KEY_2, TYPE_NAME_2, @@ -152,13 +143,15 @@ public void sync_WithNewType_ShouldCreateType() { final TypeSync typeSync = new TypeSync(typeSyncOptions); + //test final TypeSyncStatistics typeSyncStatistics = typeSync .sync(singletonList(newTypeDraft)) - .toCompletableFuture().join(); + .toCompletableFuture() + .join(); + //assertions AssertionsForStatistics.assertThat(typeSyncStatistics).hasValues(1, 1, 0, 0); - final Optional oldTypeAfter = getTypeByKey(CTP_TARGET_CLIENT, TYPE_KEY_2); assertThat(oldTypeAfter).isNotEmpty(); @@ -171,9 +164,7 @@ public void sync_WithNewType_ShouldCreateType() { @Test public void sync_WithUpdatedType_WithNewFieldDefinitions_ShouldUpdateTypeAddingFieldDefinition() { - final Optional oldTypeBefore = getTypeByKey(CTP_TARGET_CLIENT, TYPE_KEY_1); - assertThat(oldTypeBefore).isNotEmpty(); - + //preparation final TypeDraft newTypeDraft = TypeDraftBuilder.of( TYPE_KEY_1, TYPE_NAME_1, @@ -189,20 +180,25 @@ public void sync_WithUpdatedType_WithNewFieldDefinitions_ShouldUpdateTypeAddingF final TypeSync typeSync = new TypeSync(typeSyncOptions); + //test final TypeSyncStatistics typeSyncStatistics = typeSync .sync(singletonList(newTypeDraft)) - .toCompletableFuture().join(); + .toCompletableFuture() + .join(); + //assertions AssertionsForStatistics.assertThat(typeSyncStatistics).hasValues(1, 0, 1, 0); final Optional oldTypeAfter = getTypeByKey(CTP_TARGET_CLIENT, TYPE_KEY_1); - assertThat(oldTypeAfter).isNotEmpty(); - assertFieldDefinitionsAreEqual(oldTypeAfter.get().getFieldDefinitions(), asList( - FIELD_DEFINITION_1, - FIELD_DEFINITION_2, - FIELD_DEFINITION_3) - ); + assertThat(oldTypeAfter).hasValueSatisfying(type -> { + assertFieldDefinitionsAreEqual(type.getFieldDefinitions(), + asList( + FIELD_DEFINITION_1, + FIELD_DEFINITION_2, + FIELD_DEFINITION_3 + )); + }); } @Test @@ -239,9 +235,7 @@ public void sync_WithUpdatedType_WithoutOldFieldDefinition_ShouldUpdateTypeRemov @Test public void sync_WithUpdatedType_ChangingFieldDefinitionOrder_ShouldUpdateTypeChangingFieldDefinitionOrder() { - final Optional oldTypeBefore = getTypeByKey(CTP_TARGET_CLIENT, TYPE_KEY_1); - assertThat(oldTypeBefore).isNotEmpty(); - + //preparation final TypeDraft newTypeDraft = TypeDraftBuilder.of( TYPE_KEY_1, TYPE_NAME_1, @@ -256,24 +250,24 @@ public void sync_WithUpdatedType_ChangingFieldDefinitionOrder_ShouldUpdateTypeCh final TypeSync typeSync = new TypeSync(typeSyncOptions); + //test final TypeSyncStatistics typeSyncStatistics = typeSync .sync(singletonList(newTypeDraft)) .toCompletableFuture().join(); + //assertions AssertionsForStatistics.assertThat(typeSyncStatistics).hasValues(1, 0, 1, 0); final Optional oldTypeAfter = getTypeByKey(CTP_TARGET_CLIENT, TYPE_KEY_1); - assertThat(oldTypeAfter).isNotEmpty(); - assertFieldDefinitionsAreEqual(oldTypeAfter.get().getFieldDefinitions(), - asList(FIELD_DEFINITION_2, FIELD_DEFINITION_1)); + assertThat(oldTypeAfter).hasValueSatisfying(type -> + assertFieldDefinitionsAreEqual(type.getFieldDefinitions(), + asList(FIELD_DEFINITION_2, FIELD_DEFINITION_1))); } @Test public void sync_WithUpdatedType_WithUpdatedFieldDefinition_ShouldUpdateTypeUpdatingFieldDefinition() { - final Optional oldTypeBefore = getTypeByKey(CTP_TARGET_CLIENT, TYPE_KEY_1); - assertThat(oldTypeBefore).isNotEmpty(); - + //preparation final FieldDefinition fieldDefinitionUpdated = FieldDefinition.of( StringFieldType.of(), FIELD_DEFINITION_NAME_1, @@ -295,160 +289,19 @@ public void sync_WithUpdatedType_WithUpdatedFieldDefinition_ShouldUpdateTypeUpda final TypeSync typeSync = new TypeSync(typeSyncOptions); + //test final TypeSyncStatistics typeSyncStatistics = typeSync .sync(singletonList(newTypeDraft)) .toCompletableFuture().join(); + //assertions AssertionsForStatistics.assertThat(typeSyncStatistics).hasValues(1, 0, 1, 0); final Optional oldTypeAfter = getTypeByKey(CTP_TARGET_CLIENT, TYPE_KEY_1); - assertThat(oldTypeAfter).isNotEmpty(); - assertFieldDefinitionsAreEqual(oldTypeAfter.get().getFieldDefinitions(), - singletonList(fieldDefinitionUpdated)); - } - - @Test - public void sync_FromSourceToTargetProjectWithoutUpdates_ShouldReturnProperStatistics() { - //Fetch new types from source project. Convert them to drafts. - final List types = CTP_SOURCE_CLIENT - .execute(TypeQuery.of()) - .toCompletableFuture().join().getResults(); - - final List typeDrafts = types - .stream() - .map(type -> { - List newFieldDefinitions = type - .getFieldDefinitions() - .stream() - .map(fieldDefinition -> - FieldDefinition.of( - fieldDefinition.getType(), - fieldDefinition.getName(), - fieldDefinition.getLabel(), - fieldDefinition.isRequired(), - fieldDefinition.getInputHint())) - .collect(Collectors.toList()); - - return TypeDraftBuilder - .of( - type.getKey(), - type.getName(), - type.getResourceTypeIds()) - .description(type.getDescription()) - .fieldDefinitions(newFieldDefinitions) - .build(); - }) - .collect(Collectors.toList()); - - final TypeSyncOptions typeSyncOptions = TypeSyncOptionsBuilder - .of(CTP_TARGET_CLIENT) - .build(); - - final TypeSync typeSync = new TypeSync(typeSyncOptions); - - final TypeSyncStatistics typeSyncStatistics = typeSync - .sync(typeDrafts) - .toCompletableFuture().join(); - - AssertionsForStatistics.assertThat(typeSyncStatistics).hasValues(2, 1, 0, 0); - } - - @Test - public void sync_FromSourceToTargetProjectWithUpdates_ShouldReturnProperStatistics() { - //Fetch new types from source project. Convert them to drafts. - final List types = CTP_SOURCE_CLIENT - .execute(TypeQuery.of()) - .toCompletableFuture().join().getResults(); - - final List typeDrafts = types - .stream() - .map(type -> { - List newFieldDefinitions = type - .getFieldDefinitions() - .stream() - .map(fieldDefinition -> - FieldDefinition.of( - fieldDefinition.getType(), - fieldDefinition.getName(), - fieldDefinition.getLabel(), - fieldDefinition.isRequired(), - fieldDefinition.getInputHint())) - .collect(Collectors.toList()); - - return TypeDraftBuilder - .of( - type.getKey(), - LocalizedString.ofEnglish(type.getName().get(Locale.ENGLISH) + "_updated"), - type.getResourceTypeIds()) - .description(type.getDescription()) - .fieldDefinitions(newFieldDefinitions) - .build(); - }) - .collect(Collectors.toList()); - - final TypeSyncOptions typeSyncOptions = TypeSyncOptionsBuilder - .of(CTP_TARGET_CLIENT) - .build(); - - final TypeSync typeSync = new TypeSync(typeSyncOptions); - - final TypeSyncStatistics typeSyncStatistics = typeSync - .sync(typeDrafts) - .toCompletableFuture().join(); - - // Field Definition with key "key_1" already exists in target, so it's updated - // Field Definition with key "key_2" doesn't exist in target, so it's created - AssertionsForStatistics.assertThat(typeSyncStatistics).hasValues(2, 1, 1, 0); - } - - @Test - public void sync_FromSourceToTargetProjectWithUpdates_ShouldReturnProperStatisticsMessage() { - //Fetch new types from source project. Convert them to drafts. - final List types = CTP_SOURCE_CLIENT - .execute(TypeQuery.of()) - .toCompletableFuture().join().getResults(); - - final List typeDrafts = types - .stream() - .map(type -> { - List newFieldDefinitions = type - .getFieldDefinitions() - .stream() - .map(fieldDefinition -> - FieldDefinition.of( - fieldDefinition.getType(), - fieldDefinition.getName(), - fieldDefinition.getLabel(), - fieldDefinition.isRequired(), - fieldDefinition.getInputHint())) - .collect(Collectors.toList()); - - return TypeDraftBuilder - .of( - type.getKey(), - LocalizedString.ofEnglish(type.getName().get(Locale.ENGLISH) + "_updated"), - type.getResourceTypeIds()) - .description(type.getDescription()) - .fieldDefinitions(newFieldDefinitions) - .build(); - }) - .collect(Collectors.toList()); - - final TypeSyncOptions typeSyncOptions = TypeSyncOptionsBuilder - .of(CTP_TARGET_CLIENT) - .build(); - - final TypeSync typeSync = new TypeSync(typeSyncOptions); - - final TypeSyncStatistics typeSyncStatistics = typeSync - .sync(typeDrafts) - .toCompletableFuture().join(); - - assertThat(typeSyncStatistics - .getReportMessage()) - .isEqualTo("Summary: 2 types were processed in total" - + " (1 created, 1 updated and 0 failed to sync)."); + assertThat(oldTypeAfter).hasValueSatisfying(type -> + assertFieldDefinitionsAreEqual(type.getFieldDefinitions(), + singletonList(fieldDefinitionUpdated))); } @Test @@ -461,151 +314,80 @@ public void sync_WithoutKey_ShouldExecuteCallbackOnErrorAndIncreaseFailedCounter .fieldDefinitions(asList(FIELD_DEFINITION_1, FIELD_DEFINITION_2)) .build(); - final TypeSyncOptions typeSyncOptions = TypeSyncOptionsBuilder - .of(CTP_TARGET_CLIENT) - .build(); - - TypeSyncOptions spyTypeSyncOptions = spy(typeSyncOptions); - - final TypeSync typeSync = new TypeSync(spyTypeSyncOptions); - - final TypeSyncStatistics typeSyncStatistics = typeSync - .sync(singletonList(newTypeDraft)) - .toCompletableFuture().join(); - - verify(spyTypeSyncOptions) - .applyErrorCallback("Failed to process type draft without key.", null); - - AssertionsForStatistics.assertThat(typeSyncStatistics).hasValues(1, 0, 0, 1); - } - - @Test - public void sync_WithNullDraft_ShouldExecuteCallbackOnErrorAndIncreaseFailedCounter() { - final TypeDraft newTypeDraft = null; + final List errorMessages = new ArrayList<>(); + final List exceptions = new ArrayList<>(); final TypeSyncOptions typeSyncOptions = TypeSyncOptionsBuilder .of(CTP_TARGET_CLIENT) + .errorCallback((errorMessage, exception) -> { + errorMessages.add(errorMessage); + exceptions.add(exception); + }) .build(); - TypeSyncOptions spyTypeSyncOptions = spy(typeSyncOptions); - - final TypeSync typeSync = new TypeSync(spyTypeSyncOptions); + final TypeSync typeSync = new TypeSync(typeSyncOptions); + //test final TypeSyncStatistics typeSyncStatistics = typeSync .sync(singletonList(newTypeDraft)) - .toCompletableFuture().join(); - - verify(spyTypeSyncOptions).applyErrorCallback("Failed to process null type draft.", null); - - AssertionsForStatistics.assertThat(typeSyncStatistics).hasValues(1, 0, 0, 1); - } - - @Test - public void sync_WithErrorFetchingExistingKeysFromCT_ShouldExecuteCallbackOnErrorAndIncreaseFailedCounter() { - final TypeDraft newTypeDraft = TypeDraftBuilder.of( - TYPE_KEY_1, - TYPE_NAME_1, - ResourceTypeIdsSetBuilder.of().addCategories().build()) - .description(TYPE_DESCRIPTION_1) - .fieldDefinitions(asList(FIELD_DEFINITION_1, FIELD_DEFINITION_2)) - .build(); + .toCompletableFuture() + .join(); - final TypeSyncOptions typeSyncOptions = TypeSyncOptionsBuilder - .of(CTP_TARGET_CLIENT) - .build(); + // assertions + assertThat(errorMessages) + .hasSize(1) + .hasOnlyOneElementSatisfying(message -> + assertThat(message).isEqualTo("Failed to process type draft without key.") + ); - TypeSyncOptions spyTypeSyncOptions = spy(typeSyncOptions); - - TypeService typeServiceMock = mock(TypeServiceImpl.class); - - when(typeServiceMock.fetchMatchingTypesByKeys(Mockito.any())).thenReturn(supplyAsync(() -> { - throw new SphereException(); - })); - - final TypeSync typeSync = new TypeSync(spyTypeSyncOptions, typeServiceMock); - - final TypeSyncStatistics typeSyncStatistics = typeSync - .sync(singletonList(newTypeDraft)) - .toCompletableFuture().join(); - - verify(spyTypeSyncOptions) - .applyErrorCallback(eq("Failed to fetch existing types of keys '[key_1]'."), any()); + assertThat(exceptions) + .hasSize(1) + .hasOnlyOneElementSatisfying(throwable -> assertThat(throwable).isNull()); AssertionsForStatistics.assertThat(typeSyncStatistics).hasValues(1, 0, 0, 1); } @Test - public void sync_WithErrorCreatingTheTypeInCT_ShouldExecuteCallbackOnErrorAndIncreaseFailedCounter() { - final TypeDraft newTypeDraft = TypeDraftBuilder.of( - TYPE_KEY_2, - TYPE_NAME_2, - ResourceTypeIdsSetBuilder.of().addCategories().build()) - .description(TYPE_DESCRIPTION_2) - .fieldDefinitions(singletonList(FIELD_DEFINITION_1)) - .build(); + public void sync_WithNullDraft_ShouldExecuteCallbackOnErrorAndIncreaseFailedCounter() { + //preparation + final TypeDraft newTypeDraft = null; + final List errorMessages = new ArrayList<>(); + final List exceptions = new ArrayList<>(); final TypeSyncOptions typeSyncOptions = TypeSyncOptionsBuilder .of(CTP_TARGET_CLIENT) + .errorCallback((errorMessage, exception) -> { + errorMessages.add(errorMessage); + exceptions.add(exception); + }) .build(); - TypeSyncOptions spyTypeSyncOptions = spy(typeSyncOptions); - - TypeService typeService = new TypeServiceImpl(spyTypeSyncOptions); - TypeService spyTypeService = spy(typeService); - - doReturn(supplyAsync(() -> { - throw new SphereException(); - })).when(spyTypeService).createType(Mockito.any()); - - final TypeSync typeSync = new TypeSync(spyTypeSyncOptions, spyTypeService); + final TypeSync typeSync = new TypeSync(typeSyncOptions); + //test final TypeSyncStatistics typeSyncStatistics = typeSync .sync(singletonList(newTypeDraft)) - .toCompletableFuture().join(); - - verify(spyTypeSyncOptions) - .applyErrorCallback(eq("Failed to create type of key 'key_2'."), any()); - - AssertionsForStatistics.assertThat(typeSyncStatistics).hasValues(1, 0, 0, 1); - } - - @Test - public void sync_WithErrorUpdatingTheTypeInCT_ShouldExecuteCallbackOnErrorAndIncreaseFailedCounter() { - final TypeDraft newTypeDraft = TypeDraftBuilder.of( - TYPE_KEY_1, - TYPE_NAME_1, - ResourceTypeIdsSetBuilder.of().addCategories().build()) - .description(TYPE_DESCRIPTION_1) - .fieldDefinitions(singletonList(FIELD_DEFINITION_1)) - .build(); - - final TypeSyncOptions typeSyncOptions = TypeSyncOptionsBuilder - .of(CTP_TARGET_CLIENT) - .build(); - - TypeSyncOptions spyTypeSyncOptions = spy(typeSyncOptions); + .toCompletableFuture() + .join(); - TypeService typeService = new TypeServiceImpl(spyTypeSyncOptions); - TypeService spyTypeService = spy(typeService); + //assertions + assertThat(errorMessages) + .hasSize(1) + .hasOnlyOneElementSatisfying(message -> + assertThat(message).isEqualTo("Failed to process null type draft.") + ); - doReturn(supplyAsync(() -> { - throw new SphereException(); - })).when(spyTypeService).updateType(Mockito.any(), Mockito.anyList()); - - final TypeSync typeSync = new TypeSync(spyTypeSyncOptions, spyTypeService); - - final TypeSyncStatistics typeSyncStatistics = typeSync - .sync(singletonList(newTypeDraft)) - .toCompletableFuture().join(); - - verify(spyTypeSyncOptions) - .applyErrorCallback(startsWith("Failed to update type with key: 'key_1'."), any()); + assertThat(exceptions) + .hasSize(1) + .hasOnlyOneElementSatisfying(throwable -> assertThat(throwable).isNull()); AssertionsForStatistics.assertThat(typeSyncStatistics).hasValues(1, 0, 0, 1); } @Test public void sync_WithoutName_ShouldExecuteCallbackOnErrorAndIncreaseFailedCounter() { + // preparation + // Draft without "name" throws a commercetools exception because "name" is a required value final TypeDraft newTypeDraft = TypeDraftBuilder.of( TYPE_KEY_1, null, @@ -614,27 +396,46 @@ public void sync_WithoutName_ShouldExecuteCallbackOnErrorAndIncreaseFailedCounte .fieldDefinitions(asList(FIELD_DEFINITION_1, FIELD_DEFINITION_2)) .build(); + final List errorMessages = new ArrayList<>(); + final List exceptions = new ArrayList<>(); + final TypeSyncOptions typeSyncOptions = TypeSyncOptionsBuilder .of(CTP_TARGET_CLIENT) + .errorCallback((errorMessage, exception) -> { + errorMessages.add(errorMessage); + exceptions.add(exception); + }) .build(); - TypeSyncOptions spyTypeSyncOptions = spy(typeSyncOptions); - - final TypeSync typeSync = new TypeSync(spyTypeSyncOptions); + final TypeSync typeSync = new TypeSync(typeSyncOptions); + // test final TypeSyncStatistics typeSyncStatistics = typeSync .sync(singletonList(newTypeDraft)) - .toCompletableFuture().join(); - - // Since the error message and exception is coming from commercetools, we don't test the actual message and - // exception - verify(spyTypeSyncOptions).applyErrorCallback(any(), any()); + .toCompletableFuture() + .join(); + + // assertions + assertThat(errorMessages) + .hasSize(1) + .hasOnlyOneElementSatisfying(message -> + assertThat(message).contains("Failed to update type with key 'key_1'.") + ); + + assertThat(exceptions) + .hasSize(1) + .hasOnlyOneElementSatisfying(throwable -> { + assertThat(throwable).isExactlyInstanceOf(CompletionException.class); + assertThat(throwable).hasCauseExactlyInstanceOf(ErrorResponseException.class); + assertThat(throwable).hasMessageContaining("Missing required value"); + }); AssertionsForStatistics.assertThat(typeSyncStatistics).hasValues(1, 0, 0, 1); } @Test public void sync_WithoutFieldDefinitionType_ShouldExecuteCallbackOnErrorAndIncreaseFailedCounter() { + //preparation final FieldDefinition fieldDefinition = FieldDefinition.of( null, FIELD_DEFINITION_NAME_1, @@ -650,27 +451,46 @@ public void sync_WithoutFieldDefinitionType_ShouldExecuteCallbackOnErrorAndIncre .fieldDefinitions(singletonList(fieldDefinition)) .build(); + final List errorMessages = new ArrayList<>(); + final List exceptions = new ArrayList<>(); + final TypeSyncOptions typeSyncOptions = TypeSyncOptionsBuilder .of(CTP_TARGET_CLIENT) + .errorCallback((errorMessage, exception) -> { + errorMessages.add(errorMessage); + exceptions.add(exception); + }) .build(); - TypeSyncOptions spyTypeSyncOptions = spy(typeSyncOptions); - - final TypeSync typeSync = new TypeSync(spyTypeSyncOptions); + final TypeSync typeSync = new TypeSync(typeSyncOptions); + //test final TypeSyncStatistics typeSyncStatistics = typeSync .sync(singletonList(newTypeDraft)) - .toCompletableFuture().join(); - - // Since the error message and exception is coming from commercetools, we don't test the actual message and - // exception - verify(spyTypeSyncOptions).applyErrorCallback(any(), any()); + .toCompletableFuture() + .join(); + + // assertions + assertThat(errorMessages) + .hasSize(1) + .hasOnlyOneElementSatisfying(message -> + assertThat(message).contains("Failed to update type with key 'key_1'.") + ); + + assertThat(exceptions) + .hasSize(1) + .hasOnlyOneElementSatisfying(throwable -> { + assertThat(throwable).isExactlyInstanceOf(CompletionException.class); + assertThat(throwable).hasCauseExactlyInstanceOf(ErrorResponseException.class); + assertThat(throwable).hasMessageContaining("Missing required value"); + }); AssertionsForStatistics.assertThat(typeSyncStatistics).hasValues(1, 0, 0, 1); } @Test public void sync_WithSeveralBatches_ShouldReturnProperStatistics() { + // preparation // Default batch size is 50 (check TypeSyncOptionsBuilder) so we have 2 batches of 50 final List typeDrafts = IntStream .range(0, 100) @@ -689,10 +509,13 @@ public void sync_WithSeveralBatches_ShouldReturnProperStatistics() { final TypeSync typeSync = new TypeSync(typeSyncOptions); + //test final TypeSyncStatistics typeSyncStatistics = typeSync .sync(typeDrafts) - .toCompletableFuture().join(); + .toCompletableFuture() + .join(); + //assertion AssertionsForStatistics.assertThat(typeSyncStatistics) .hasValues(100, 100, 0, 0); } From 60963f968b41720e1494642b996273d04a2cb16a Mon Sep 17 00:00:00 2001 From: Hesham Massoud Date: Tue, 4 Dec 2018 18:51:59 +0100 Subject: [PATCH 079/164] #300 documentation empty char fix. Co-Authored-By: ahmetoz --- docs/usage/TYPE_SYNC.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/usage/TYPE_SYNC.md b/docs/usage/TYPE_SYNC.md index b0ef9afd0d..a9e560da37 100644 --- a/docs/usage/TYPE_SYNC.md +++ b/docs/usage/TYPE_SYNC.md @@ -24,7 +24,7 @@ against a [TypeDraft](https://docs.commercetools.com/http-api-projects-types.htm #### Prerequisites 1. The sync expects a list of `TypeDraft`s that have their `key` fields set to be matched with -types in the target CTP project. Also, the types in the target project are expected to have the `key` +types in the target CTP project. Also, the types in the target project are expected to have the `key` fields set, otherwise they won't be matched. 2. Create a `sphereClient` [as described here](IMPORTANT_USAGE_TIPS.md#sphereclient-creation). From fef7fefaaa4c630dede107613d2bc1b714822661 Mon Sep 17 00:00:00 2001 From: Hesham Massoud Date: Tue, 4 Dec 2018 18:52:24 +0100 Subject: [PATCH 080/164] #300 fix order Co-Authored-By: ahmetoz --- docs/usage/TYPE_SYNC.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/usage/TYPE_SYNC.md b/docs/usage/TYPE_SYNC.md index a9e560da37..2174f01db3 100644 --- a/docs/usage/TYPE_SYNC.md +++ b/docs/usage/TYPE_SYNC.md @@ -29,7 +29,7 @@ fields set, otherwise they won't be matched. 2. Create a `sphereClient` [as described here](IMPORTANT_USAGE_TIPS.md#sphereclient-creation). -4. After the `sphereClient` is set up, a `TypeSyncOptions` should be be built as follows: + 3. After the `sphereClient` is set up, a `TypeSyncOptions` should be be built as follows: ````java // instantiating a TypeSyncOptions final TypeSyncOptions typeSyncOptions = TypeSyncOptionsBuilder.of(sphereClient).build(); From 778eb3efb29df85b45e98c765a1aa8b49e04c7d3 Mon Sep 17 00:00:00 2001 From: aoz Date: Tue, 4 Dec 2018 18:54:57 +0100 Subject: [PATCH 081/164] #300 add type doc to docs/readme.. --- docs/README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/README.md b/docs/README.md index 2b25513f4b..671fe7ce45 100644 --- a/docs/README.md +++ b/docs/README.md @@ -29,7 +29,8 @@ Currently this library supports synchronising the following entities in commerce - [Products](usage/PRODUCT_SYNC.md) - [InventoryEntries](usage/INVENTORY_SYNC.md) - [ProductTypes](usage/PRODUCT_TYPE_SYNC.md) - + - [Types](usage/TYPE_SYNC.md) + ![commercetools-java-sync-final 001](https://user-images.githubusercontent.com/9512131/31230702-0f2255a6-a9e5-11e7-9412-04ed52641dde.png) From f3e95f37ba58aa3b422127533df35df277b4ed52 Mon Sep 17 00:00:00 2001 From: aoz Date: Tue, 4 Dec 2018 18:58:00 +0100 Subject: [PATCH 082/164] #300 update grammar for type caveats and add issue link for settype. --- docs/usage/TYPE_SYNC.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/usage/TYPE_SYNC.md b/docs/usage/TYPE_SYNC.md index 2174f01db3..7b8b9dac8f 100644 --- a/docs/usage/TYPE_SYNC.md +++ b/docs/usage/TYPE_SYNC.md @@ -87,4 +87,4 @@ and field definitions can be found [here](/src/test/java/com/commercetools/sync/ 1. Updating the label of enum values and localized enum values of field definition is not supported yet. [#339](https://github.com/commercetools/commercetools-sync-java/issues/339) 2. Removing the enum values and localized enum values from the field definition is not supported yet. [#339](https://github.com/commercetools/commercetools-sync-java/issues/339) 3. Updating the input hint of field definition is not supported yet. [#339](https://github.com/commercetools/commercetools-sync-java/issues/339) -4. Syncing types with an field of type [SetType](https://docs.commercetools.com/http-api-projects-types.html#settype) is not supported yet. +4. Syncing types with a field definition with type [SetType](https://docs.commercetools.com/http-api-projects-types.html#settype) is not supported yet. [#313](https://github.com/commercetools/commercetools-sync-java/issues/313) From e2d9cd84d03a3bf6a6674495cc0b13976f8021a3 Mon Sep 17 00:00:00 2001 From: aoz Date: Tue, 4 Dec 2018 19:01:43 +0100 Subject: [PATCH 083/164] #300 add type sync to documentation. --- mkdocs.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/mkdocs.yml b/mkdocs.yml index 44222aefe2..64634d917b 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -49,6 +49,7 @@ nav: - Quick Start: usage/QUICK_START.md - Product Sync: usage/PRODUCT_SYNC.md - ProductType Sync: usage/PRODUCT_TYPE_SYNC.md + - Type Sync: usage/TYPE_SYNC.md - Category Sync: usage/CATEGORY_SYNC.md - InventoryEntry Sync: usage/INVENTORY_SYNC.md - Advanced: From 6d868327bb774722a70ceade44388728f6d9d3a6 Mon Sep 17 00:00:00 2001 From: aoz Date: Tue, 4 Dec 2018 19:04:42 +0100 Subject: [PATCH 084/164] #300 fix checkstyle error. --- .../integration/services/impl/TypeServiceImplIT.java | 1 - .../sync/services/impl/TypeServiceImpl.java | 1 - src/main/java/com/commercetools/sync/types/TypeSync.java | 9 +++++---- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/integration-test/java/com/commercetools/sync/integration/services/impl/TypeServiceImplIT.java b/src/integration-test/java/com/commercetools/sync/integration/services/impl/TypeServiceImplIT.java index ffd439b187..0f1ce5d6b2 100644 --- a/src/integration-test/java/com/commercetools/sync/integration/services/impl/TypeServiceImplIT.java +++ b/src/integration-test/java/com/commercetools/sync/integration/services/impl/TypeServiceImplIT.java @@ -20,7 +20,6 @@ import org.apache.commons.lang3.StringUtils; import org.junit.AfterClass; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import java.util.ArrayList; diff --git a/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java b/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java index 8c79c0d9c2..5f39e7d34b 100644 --- a/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java +++ b/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java @@ -12,7 +12,6 @@ import io.sphere.sdk.types.commands.TypeUpdateCommand; import io.sphere.sdk.types.queries.TypeQuery; import io.sphere.sdk.types.queries.TypeQueryBuilder; -import org.apache.commons.lang3.StringUtils; import javax.annotation.Nonnull; import javax.annotation.Nullable; diff --git a/src/main/java/com/commercetools/sync/types/TypeSync.java b/src/main/java/com/commercetools/sync/types/TypeSync.java index 14b12cb2fe..f087332696 100644 --- a/src/main/java/com/commercetools/sync/types/TypeSync.java +++ b/src/main/java/com/commercetools/sync/types/TypeSync.java @@ -123,10 +123,11 @@ protected CompletionStage processBatch(@Nonnull final List { - statistics.incrementProcessed(batch.size()); - return statistics; - }); + }) + .thenApply(ignored -> { + statistics.incrementProcessed(batch.size()); + return statistics; + }); } } From 64235a3b884e29be6775c62f73945a95b6028458 Mon Sep 17 00:00:00 2001 From: Hesham Massoud Date: Wed, 5 Dec 2018 08:57:00 +0100 Subject: [PATCH 085/164] #300: Fix links. --- docs/usage/PRODUCT_TYPE_SYNC.md | 5 ++++- docs/usage/TYPE_SYNC.md | 8 ++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/docs/usage/PRODUCT_TYPE_SYNC.md b/docs/usage/PRODUCT_TYPE_SYNC.md index 3f4bfa3a93..889fb23919 100644 --- a/docs/usage/PRODUCT_TYPE_SYNC.md +++ b/docs/usage/PRODUCT_TYPE_SYNC.md @@ -65,7 +65,10 @@ __Note__ The statistics object contains the processing time of the last batch on 1. The sync processing time should not take into account the time between supplying batches to the sync. 2. It is not known by the sync which batch is going to be the last one supplied. -More examples of how to use the sync can be found [here](https://github.com/commercetools/commercetools-sync-java/tree/master/src/integration-test/java/com/commercetools/sync/integration/producttypes/ProductTypeSyncIT.java). +##### More examples of how to use the sync + + 1. [Sync from another CTP project as a source](https://github.com/commercetools/commercetools-sync-java/tree/master/src/integration-test/java/com/commercetools/sync/integration/ctpprojectsource/producttypes/ProductTypeSyncIT.java). + 2. [Sync from an external source](https://github.com/commercetools/commercetools-sync-java/tree/master/src/integration-test/java/com/commercetools/sync/integration/externalsource/producttypes/ProductTypeSyncIT.java). *Make sure to read the [Important Usage Tips](IMPORTANT_USAGE_TIPS.md) for optimal performance.* diff --git a/docs/usage/TYPE_SYNC.md b/docs/usage/TYPE_SYNC.md index 7b8b9dac8f..fab37559da 100644 --- a/docs/usage/TYPE_SYNC.md +++ b/docs/usage/TYPE_SYNC.md @@ -58,10 +58,14 @@ stats.getReportMessage(); ```` __Note__ The statistics object contains the processing time of the last batch only. This is due to two reasons: + 1. The sync processing time should not take into account the time between supplying batches to the sync. 2. It is not known by the sync which batch is going to be the last one supplied. - -More examples of how to use the sync can be found [here](/src/integration-test/java/com/commercetools/sync/integration/types/TypeSyncIT.java). + +##### More examples of how to use the sync + + 1. [Sync from another CTP project as a source](https://github.com/commercetools/commercetools-sync-java/tree/master/src/integration-test/java/com/commercetools/sync/integration/ctpprojectsource/types/TypeSyncIT.java). + 2. [Sync from an external source](https://github.com/commercetools/commercetools-sync-java/tree/master/src/integration-test/java/com/commercetools/sync/integration/externalsource/types/TypeSyncIT.java). *Make sure to read the [Important Usage Tips](IMPORTANT_USAGE_TIPS.md) for optimal performance.* From ba4340fb7f5709ef4111881ca74fe82f151b3a76 Mon Sep 17 00:00:00 2001 From: Hesham Massoud Date: Wed, 5 Dec 2018 08:57:22 +0100 Subject: [PATCH 086/164] #300: Remove unneeded link. --- docs/usage/TYPE_SYNC.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/usage/TYPE_SYNC.md b/docs/usage/TYPE_SYNC.md index fab37559da..8a4746039e 100644 --- a/docs/usage/TYPE_SYNC.md +++ b/docs/usage/TYPE_SYNC.md @@ -83,8 +83,7 @@ Utility methods provided by the library to compare the specific fields of a `Typ ````java Optional> updateAction = TypeUpdateActionUtils.buildChangeNameAction(oldType, typeDraft); ```` -More examples of those utils for different types can be found [here](/src/test/java/com/commercetools/sync/types/utils/TypeUpdateActionUtilsTest.java). -and field definitions can be found [here](/src/test/java/com/commercetools/sync/types/utils/FieldDefinitionUpdateActionUtilsTest.java). +More examples of those utils for different types can be found [here](https://github.com/commercetools/commercetools-sync-java/tree/master/src/test/java/com/commercetools/sync/types/utils/TypeUpdateActionUtilsTest.java). ## Caveats From ba9353071198f55c45a222ec71a986cbb4922f2e Mon Sep 17 00:00:00 2001 From: Hesham Massoud Date: Wed, 5 Dec 2018 08:57:40 +0100 Subject: [PATCH 087/164] #300: Remove comma in the end. --- docs/usage/QUICK_START.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/usage/QUICK_START.md b/docs/usage/QUICK_START.md index 41767a1d2a..ae7778c602 100644 --- a/docs/usage/QUICK_START.md +++ b/docs/usage/QUICK_START.md @@ -84,5 +84,4 @@ implementation 'com.commercetools:commercetools-sync-java:v1.0.0-M14' #### More Details *[Product Sync](PRODUCT_SYNC.md), [ProductType Sync](PRODUCT_TYPE_SYNC.md), [Category Sync](CATEGORY_SYNC.md), [Inventory Sync](INVENTORY_SYNC.md), -[Type Sync](TYPE_SYNC.md), -* +[Type Sync](TYPE_SYNC.md)* From c52f909343cf0c5d7dd7cabd05d29830985984f3 Mon Sep 17 00:00:00 2001 From: Hesham Massoud Date: Wed, 5 Dec 2018 08:58:11 +0100 Subject: [PATCH 088/164] #300: Add missing article. --- docs/usage/TYPE_SYNC.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/usage/TYPE_SYNC.md b/docs/usage/TYPE_SYNC.md index 8a4746039e..61dd97b90b 100644 --- a/docs/usage/TYPE_SYNC.md +++ b/docs/usage/TYPE_SYNC.md @@ -89,5 +89,5 @@ More examples of those utils for different types can be found [here](https://git 1. Updating the label of enum values and localized enum values of field definition is not supported yet. [#339](https://github.com/commercetools/commercetools-sync-java/issues/339) 2. Removing the enum values and localized enum values from the field definition is not supported yet. [#339](https://github.com/commercetools/commercetools-sync-java/issues/339) -3. Updating the input hint of field definition is not supported yet. [#339](https://github.com/commercetools/commercetools-sync-java/issues/339) +3. Updating the input hint of a field definition is not supported yet. [#339](https://github.com/commercetools/commercetools-sync-java/issues/339) 4. Syncing types with a field definition with type [SetType](https://docs.commercetools.com/http-api-projects-types.html#settype) is not supported yet. [#313](https://github.com/commercetools/commercetools-sync-java/issues/313) From 6ee9d0083072ba6b1ab73f54fd54bcc5d92e8cb5 Mon Sep 17 00:00:00 2001 From: Hesham Massoud Date: Wed, 5 Dec 2018 09:31:18 +0100 Subject: [PATCH 089/164] #300 fix wrong resource in javadoc Co-Authored-By: ahmetoz --- src/main/java/com/commercetools/sync/services/TypeService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/commercetools/sync/services/TypeService.java b/src/main/java/com/commercetools/sync/services/TypeService.java index 50c7ce31a3..e8ab52c275 100644 --- a/src/main/java/com/commercetools/sync/services/TypeService.java +++ b/src/main/java/com/commercetools/sync/services/TypeService.java @@ -35,7 +35,7 @@ public interface TypeService { CompletionStage> fetchCachedTypeId(@Nonnull final String key); /** - * Given a {@link Set} of Type keys, this method fetches a set of all the ProductTypes, matching this given + * Given a {@link Set} of Type keys, this method fetches a set of all the Types, matching this given * set of keys in the CTP project, defined in a potentially injected {@link io.sphere.sdk.client.SphereClient}. A * mapping of the key to the id of the fetched Type is persisted in an in-memory map. * From 3a4cd16a3b3dccd8fd783b0cc0dc5b1c7b4f92d7 Mon Sep 17 00:00:00 2001 From: Hesham Massoud Date: Wed, 5 Dec 2018 09:31:39 +0100 Subject: [PATCH 090/164] #300 update java doc Co-Authored-By: ahmetoz --- src/main/java/com/commercetools/sync/services/TypeService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/commercetools/sync/services/TypeService.java b/src/main/java/com/commercetools/sync/services/TypeService.java index e8ab52c275..f29cda2be9 100644 --- a/src/main/java/com/commercetools/sync/services/TypeService.java +++ b/src/main/java/com/commercetools/sync/services/TypeService.java @@ -36,7 +36,7 @@ public interface TypeService { /** * Given a {@link Set} of Type keys, this method fetches a set of all the Types, matching this given - * set of keys in the CTP project, defined in a potentially injected {@link io.sphere.sdk.client.SphereClient}. A + * set of keys in the CTP project, defined in an injected {@link io.sphere.sdk.client.SphereClient}. A * mapping of the key to the id of the fetched Type is persisted in an in-memory map. * * @param keys set of Type keys to fetch matching Type by. From e565aac9c9033804c14f58ec0af45bdac3d966d6 Mon Sep 17 00:00:00 2001 From: Hesham Massoud Date: Wed, 5 Dec 2018 09:31:59 +0100 Subject: [PATCH 091/164] #300 fix grammar in javadoc Co-Authored-By: ahmetoz --- src/main/java/com/commercetools/sync/services/TypeService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/commercetools/sync/services/TypeService.java b/src/main/java/com/commercetools/sync/services/TypeService.java index f29cda2be9..2bd0c87f75 100644 --- a/src/main/java/com/commercetools/sync/services/TypeService.java +++ b/src/main/java/com/commercetools/sync/services/TypeService.java @@ -40,7 +40,7 @@ public interface TypeService { * mapping of the key to the id of the fetched Type is persisted in an in-memory map. * * @param keys set of Type keys to fetch matching Type by. - * @return {@link CompletionStage}<{@link Map}> in which the result of it's completion contains a {@link Set} + * @return {@link CompletionStage}<{@link Map}> in which the result of its completion contains a {@link Set} * of all matching Type. */ @Nonnull From f23ebfb19a0cc86d49d2738384a41a6d2f12012e Mon Sep 17 00:00:00 2001 From: Hesham Massoud Date: Wed, 5 Dec 2018 09:32:18 +0100 Subject: [PATCH 092/164] #300 fix grammar in javadoc Co-Authored-By: ahmetoz --- src/main/java/com/commercetools/sync/services/TypeService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/commercetools/sync/services/TypeService.java b/src/main/java/com/commercetools/sync/services/TypeService.java index 2bd0c87f75..d0fea77020 100644 --- a/src/main/java/com/commercetools/sync/services/TypeService.java +++ b/src/main/java/com/commercetools/sync/services/TypeService.java @@ -41,7 +41,7 @@ public interface TypeService { * * @param keys set of Type keys to fetch matching Type by. * @return {@link CompletionStage}<{@link Map}> in which the result of its completion contains a {@link Set} - * of all matching Type. + * of all matching Types. */ @Nonnull CompletionStage> fetchMatchingTypesByKeys(@Nonnull final Set keys); From 8a5dd4d91f29b3d01b62e84db6ca2b109c408029 Mon Sep 17 00:00:00 2001 From: aoz Date: Wed, 5 Dec 2018 09:35:04 +0100 Subject: [PATCH 093/164] #300 make `TypeSync` constructor protected --- src/main/java/com/commercetools/sync/types/TypeSync.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/commercetools/sync/types/TypeSync.java b/src/main/java/com/commercetools/sync/types/TypeSync.java index f087332696..55f4397e3b 100644 --- a/src/main/java/com/commercetools/sync/types/TypeSync.java +++ b/src/main/java/com/commercetools/sync/types/TypeSync.java @@ -60,7 +60,7 @@ public TypeSync(@Nonnull final TypeSyncOptions typeSyncOptions) { * @param typeService the type service which is responsible for fetching/caching the Types from the CTP * project. */ - public TypeSync(@Nonnull final TypeSyncOptions typeSyncOptions, + TypeSync(@Nonnull final TypeSyncOptions typeSyncOptions, @Nonnull final TypeService typeService) { super(new TypeSyncStatistics(), typeSyncOptions); this.typeService = typeService; From 1f7c285ea82d11846c84a081dff46a6b2129f0de Mon Sep 17 00:00:00 2001 From: aoz Date: Wed, 5 Dec 2018 09:49:58 +0100 Subject: [PATCH 094/164] #300 revert product type doc, fix some changes from merge conflict. --- docs/usage/PRODUCT_TYPE_SYNC.md | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/docs/usage/PRODUCT_TYPE_SYNC.md b/docs/usage/PRODUCT_TYPE_SYNC.md index 66fd66a2c2..205cacb06e 100644 --- a/docs/usage/PRODUCT_TYPE_SYNC.md +++ b/docs/usage/PRODUCT_TYPE_SYNC.md @@ -86,17 +86,5 @@ Optional> updateAction = ProductTypeUpdateActionUtils. More examples of those utils for different fields can be found [here](https://github.com/commercetools/commercetools-sync-java/tree/master/src/test/java/com/commercetools/sync/producttypes/utils/ProductTypeUpdateActionUtilsTest.java). -## Caveats - -1. Product types are either created or updated. Currently the tool does not support product type deletion. -2. The sync library is not meant to be executed in a parallel fashion. Check the example in [point #2 here](/docs/usage/PRODUCT_SYNC.md#caveats). - By design, scaling the sync process should **not** be done by executing the batches themselves in parallel. However, it can be done either by: - - Changing the number of [max parallel requests](/src/main/java/com/commercetools/sync/commons/utils/ClientConfigurationUtils.java#L116) within the `sphereClient` configuration. It defines how many requests the client can execute in parallel. - - or changing the draft [batch size](https://commercetools.github.io/commercetools-sync-java/v/v1.0.0-M14/com/commercetools/sync/commons/BaseSyncOptionsBuilder.html#batchSize-int-). It defines how many drafts can one batch contain. - - The current overridable default [configuration](/src/main/java/com/commercetools/sync/commons/utils/ClientConfigurationUtils.java#L45) of the `sphereClient` - is the recommended good balance for stability and performance for the sync process. - - In order to exploit the number of `max parallel requests`, the `batch size` should have a value set which is equal or higher. - -3. Syncing product types with an attribute of type [NestedType](https://docs.commercetools.com/http-api-projects-productTypes.html#nestedtype) is not supported yet. +## Caveats +1. Syncing product types with an attribute of type [NestedType](https://docs.commercetools.com/http-api-projects-productTypes.html#nestedtype) is not supported yet. From 8b0a195b04ab114462492128d22e95fb6b4def20 Mon Sep 17 00:00:00 2001 From: Hesham Massoud Date: Wed, 5 Dec 2018 10:21:23 +0100 Subject: [PATCH 095/164] #300: Fix caveat in Type sync doc and add same caveat in productType sync doc. --- docs/usage/PRODUCT_TYPE_SYNC.md | 1 + docs/usage/TYPE_SYNC.md | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/usage/PRODUCT_TYPE_SYNC.md b/docs/usage/PRODUCT_TYPE_SYNC.md index 6468fea42d..19b45c953d 100644 --- a/docs/usage/PRODUCT_TYPE_SYNC.md +++ b/docs/usage/PRODUCT_TYPE_SYNC.md @@ -91,3 +91,4 @@ More examples of those utils for different fields can be found [here](https://gi ## Caveats 1. Syncing product types with an attribute of type [NestedType](https://docs.commercetools.com/http-api-projects-productTypes.html#nestedtype) is not supported yet. +2. Currently the sync would handle changes to Enum or LocalizedEnum Types but not [Set](https://docs.commercetools.com/http-api-projects-types.html#settype) of either. [#313](https://github.com/commercetools/commercetools-sync-java/issues/313) diff --git a/docs/usage/TYPE_SYNC.md b/docs/usage/TYPE_SYNC.md index 61dd97b90b..a4bafb484d 100644 --- a/docs/usage/TYPE_SYNC.md +++ b/docs/usage/TYPE_SYNC.md @@ -29,7 +29,7 @@ fields set, otherwise they won't be matched. 2. Create a `sphereClient` [as described here](IMPORTANT_USAGE_TIPS.md#sphereclient-creation). - 3. After the `sphereClient` is set up, a `TypeSyncOptions` should be be built as follows: +3. After the `sphereClient` is set up, a `TypeSyncOptions` should be be built as follows: ````java // instantiating a TypeSyncOptions final TypeSyncOptions typeSyncOptions = TypeSyncOptionsBuilder.of(sphereClient).build(); @@ -90,4 +90,4 @@ More examples of those utils for different types can be found [here](https://git 1. Updating the label of enum values and localized enum values of field definition is not supported yet. [#339](https://github.com/commercetools/commercetools-sync-java/issues/339) 2. Removing the enum values and localized enum values from the field definition is not supported yet. [#339](https://github.com/commercetools/commercetools-sync-java/issues/339) 3. Updating the input hint of a field definition is not supported yet. [#339](https://github.com/commercetools/commercetools-sync-java/issues/339) -4. Syncing types with a field definition with type [SetType](https://docs.commercetools.com/http-api-projects-types.html#settype) is not supported yet. [#313](https://github.com/commercetools/commercetools-sync-java/issues/313) +4. Currently the sync would handle changes to Enum or LocalizedEnum Types but not [Set](https://docs.commercetools.com/http-api-projects-types.html#settype) of either. [#313](https://github.com/commercetools/commercetools-sync-java/issues/313) From db311a6a9dde58ee109c03736e72939a8a973ca4 Mon Sep 17 00:00:00 2001 From: Hesham Massoud Date: Wed, 5 Dec 2018 10:23:56 +0100 Subject: [PATCH 096/164] #300: Remove extra space. --- docs/usage/PRODUCT_TYPE_SYNC.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/usage/PRODUCT_TYPE_SYNC.md b/docs/usage/PRODUCT_TYPE_SYNC.md index 19b45c953d..3bf1604ab7 100644 --- a/docs/usage/PRODUCT_TYPE_SYNC.md +++ b/docs/usage/PRODUCT_TYPE_SYNC.md @@ -27,7 +27,7 @@ against a [ProductTypeDraft](https://docs.commercetools.com/http-api-projects-pr #### Prerequisites 1. The sync expects a list of `ProductTypeDraft`s that have their `key` fields set to be matched with -product types in the target CTP project. Also, the product types in the target project are expected to have the `key` +product types in the target CTP project. Also, the product types in the target project are expected to have the `key` fields set, otherwise they won't be matched. 2. Create a `sphereClient` [as described here](IMPORTANT_USAGE_TIPS.md#sphereclient-creation). From 226b1658aa79f98855db724a09e1e60909565e91 Mon Sep 17 00:00:00 2001 From: Hesham Massoud Date: Wed, 5 Dec 2018 10:33:21 +0100 Subject: [PATCH 097/164] #300: Remove potentially in Javadoc. --- .../commercetools/sync/services/TypeService.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/commercetools/sync/services/TypeService.java b/src/main/java/com/commercetools/sync/services/TypeService.java index d0fea77020..0c05ef313e 100644 --- a/src/main/java/com/commercetools/sync/services/TypeService.java +++ b/src/main/java/com/commercetools/sync/services/TypeService.java @@ -47,8 +47,8 @@ public interface TypeService { CompletionStage> fetchMatchingTypesByKeys(@Nonnull final Set keys); /** - * Given a type key, this method fetches a type that matches this given key in the CTP project defined in a - * potentially injected {@link SphereClient}. If there is no matching type an empty {@link Optional} will be + * Given a type key, this method fetches a type that matches this given key in the CTP project defined in an + * injected {@link SphereClient}. If there is no matching type an empty {@link Optional} will be * returned in the returned future. * * @param key the key of the type to fetch. @@ -60,7 +60,7 @@ public interface TypeService { /** * Given a {@link TypeDraft}, this method creates a {@link Type} based on it in the CTP project defined in - * a potentially injected {@link io.sphere.sdk.client.SphereClient}. The created type's id and key are also + * an injected {@link io.sphere.sdk.client.SphereClient}. The created type's id and key are also * cached. This method returns {@link CompletionStage}<{@link Type}> in which the result of it's * completion contains an instance of the {@link Type} which was created in the CTP project. * @@ -73,10 +73,10 @@ public interface TypeService { /** * Given a {@link Type} and a {@link List}<{@link UpdateAction}<{@link Type}>>, this method - * issues an update request with these update actions on this {@link Type} in the CTP project defined in a - * potentially injected {@link io.sphere.sdk.client.SphereClient}. This method returns - * {@link CompletionStage}<{@link Type}> in which the result of it's completion contains an instance of - * the {@link Type} which was updated in the CTP project. + * issues an update request with these update actions on this {@link Type} in the CTP project defined in an injected + * {@link io.sphere.sdk.client.SphereClient}. This method returns {@link CompletionStage}<{@link Type}> in + * which the result of it's completion contains an instance of the {@link Type} which was updated in the CTP + * project. * * @param type the {@link Type} to update. * @param updateActions the update actions to update the {@link Type} with. From 98ec5406a541724091951395afa2b2cfedf47074 Mon Sep 17 00:00:00 2001 From: Hesham Massoud Date: Wed, 5 Dec 2018 10:43:14 +0100 Subject: [PATCH 098/164] #300: Remove shadowed fields in TypeService. --- .../sync/services/impl/TypeServiceImpl.java | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java b/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java index 5f39e7d34b..5bc55492ed 100644 --- a/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java +++ b/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java @@ -1,6 +1,5 @@ package com.commercetools.sync.services.impl; -import com.commercetools.sync.commons.BaseSyncOptions; import com.commercetools.sync.commons.utils.CtpQueryUtils; import com.commercetools.sync.services.TypeService; import com.commercetools.sync.types.TypeSyncOptions; @@ -17,12 +16,10 @@ import javax.annotation.Nullable; import java.util.Collections; import java.util.List; -import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; -import java.util.concurrent.ConcurrentHashMap; import java.util.function.Consumer; import java.util.function.Function; @@ -36,12 +33,6 @@ */ public final class TypeServiceImpl extends BaseService implements TypeService { private static final String FETCH_FAILED = "Failed to fetch types with keys: '%s'. Reason: %s"; - private final Map keyToIdCache = new ConcurrentHashMap<>(); - private boolean isCached = false; - - public TypeServiceImpl(@Nonnull final BaseSyncOptions syncOptions) { - super(syncOptions); - } public TypeServiceImpl(@Nonnull final TypeSyncOptions syncOptions) { super(syncOptions); From a50052ecc9cd566db0c6290c38a9a41be7dabbc4 Mon Sep 17 00:00:00 2001 From: Hesham Massoud Date: Wed, 5 Dec 2018 10:43:28 +0100 Subject: [PATCH 099/164] #300: Formatting. --- .../commercetools/sync/types/TypeSync.java | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/main/java/com/commercetools/sync/types/TypeSync.java b/src/main/java/com/commercetools/sync/types/TypeSync.java index 55f4397e3b..29dcfc28db 100644 --- a/src/main/java/com/commercetools/sync/types/TypeSync.java +++ b/src/main/java/com/commercetools/sync/types/TypeSync.java @@ -60,8 +60,7 @@ public TypeSync(@Nonnull final TypeSyncOptions typeSyncOptions) { * @param typeService the type service which is responsible for fetching/caching the Types from the CTP * project. */ - TypeSync(@Nonnull final TypeSyncOptions typeSyncOptions, - @Nonnull final TypeService typeService) { + TypeSync(@Nonnull final TypeSyncOptions typeSyncOptions, @Nonnull final TypeService typeService) { super(new TypeSyncStatistics(), typeSyncOptions); this.typeService = typeService; } @@ -110,24 +109,25 @@ protected CompletionStage processBatch(@Nonnull final List keys = validTypeDrafts.stream().map(TypeDraft::getKey).collect(toSet()); - return typeService.fetchMatchingTypesByKeys(keys) - .handle(ImmutablePair::new) - .thenCompose(fetchResponse -> { - final Set fetchedTypes = fetchResponse.getKey(); - final Throwable exception = fetchResponse.getValue(); - - if (exception != null) { - final String errorMessage = format(CTP_TYPE_FETCH_FAILED, keys); - handleError(errorMessage, exception, keys.size()); - return CompletableFuture.completedFuture(null); - } else { - return syncBatch(fetchedTypes, validTypeDrafts); - } - }) - .thenApply(ignored -> { - statistics.incrementProcessed(batch.size()); - return statistics; - }); + return typeService + .fetchMatchingTypesByKeys(keys) + .handle(ImmutablePair::new) + .thenCompose(fetchResponse -> { + final Set fetchedTypes = fetchResponse.getKey(); + final Throwable exception = fetchResponse.getValue(); + + if (exception != null) { + final String errorMessage = format(CTP_TYPE_FETCH_FAILED, keys); + handleError(errorMessage, exception, keys.size()); + return CompletableFuture.completedFuture(null); + } else { + return syncBatch(fetchedTypes, validTypeDrafts); + } + }) + .thenApply(ignored -> { + statistics.incrementProcessed(batch.size()); + return statistics; + }); } } From 4208b33d3b2b00cc920816b4c0c63d45ac67fcea Mon Sep 17 00:00:00 2001 From: aoz Date: Wed, 5 Dec 2018 10:48:18 +0100 Subject: [PATCH 100/164] #300 add static import for AssertionsForStatistics --- .../externalsource/types/TypeSyncIT.java | 31 +++++++++---------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/src/integration-test/java/com/commercetools/sync/integration/externalsource/types/TypeSyncIT.java b/src/integration-test/java/com/commercetools/sync/integration/externalsource/types/TypeSyncIT.java index c5ce846ff7..6dc50fa20b 100644 --- a/src/integration-test/java/com/commercetools/sync/integration/externalsource/types/TypeSyncIT.java +++ b/src/integration-test/java/com/commercetools/sync/integration/externalsource/types/TypeSyncIT.java @@ -1,7 +1,5 @@ package com.commercetools.sync.integration.externalsource.types; - -import com.commercetools.sync.commons.asserts.statistics.AssertionsForStatistics; import com.commercetools.sync.types.TypeSync; import com.commercetools.sync.types.TypeSyncOptions; import com.commercetools.sync.types.TypeSyncOptionsBuilder; @@ -38,6 +36,7 @@ import java.util.stream.Collectors; import java.util.stream.IntStream; +import static com.commercetools.sync.commons.asserts.statistics.AssertionsForStatistics.assertThat; import static com.commercetools.sync.integration.commons.utils.ITUtils.deleteTypes; import static com.commercetools.sync.integration.commons.utils.SphereClientUtils.CTP_TARGET_CLIENT; import static com.commercetools.sync.integration.commons.utils.TypeITUtils.FIELD_DEFINITION_1; @@ -113,7 +112,7 @@ public void sync_WithUpdatedType_ShouldUpdateType() { .join(); //assertions - AssertionsForStatistics.assertThat(typeSyncStatistics).hasValues(1, 0, 1, 0); + assertThat(typeSyncStatistics).hasValues(1, 0, 1, 0); final Optional oldTypeAfter = getTypeByKey(CTP_TARGET_CLIENT, TYPE_KEY_1); @@ -150,7 +149,7 @@ public void sync_WithNewType_ShouldCreateType() { .join(); //assertions - AssertionsForStatistics.assertThat(typeSyncStatistics).hasValues(1, 1, 0, 0); + assertThat(typeSyncStatistics).hasValues(1, 1, 0, 0); final Optional oldTypeAfter = getTypeByKey(CTP_TARGET_CLIENT, TYPE_KEY_2); @@ -187,7 +186,7 @@ public void sync_WithUpdatedType_WithNewFieldDefinitions_ShouldUpdateTypeAddingF .join(); //assertions - AssertionsForStatistics.assertThat(typeSyncStatistics).hasValues(1, 0, 1, 0); + assertThat(typeSyncStatistics).hasValues(1, 0, 1, 0); final Optional oldTypeAfter = getTypeByKey(CTP_TARGET_CLIENT, TYPE_KEY_1); @@ -224,7 +223,7 @@ public void sync_WithUpdatedType_WithoutOldFieldDefinition_ShouldUpdateTypeRemov .sync(singletonList(newTypeDraft)) .toCompletableFuture().join(); - AssertionsForStatistics.assertThat(typeSyncStatistics).hasValues(1, 0, 1, 0); + assertThat(typeSyncStatistics).hasValues(1, 0, 1, 0); final Optional oldTypeAfter = getTypeByKey(CTP_TARGET_CLIENT, TYPE_KEY_1); @@ -256,7 +255,7 @@ public void sync_WithUpdatedType_ChangingFieldDefinitionOrder_ShouldUpdateTypeCh .toCompletableFuture().join(); //assertions - AssertionsForStatistics.assertThat(typeSyncStatistics).hasValues(1, 0, 1, 0); + assertThat(typeSyncStatistics).hasValues(1, 0, 1, 0); final Optional oldTypeAfter = getTypeByKey(CTP_TARGET_CLIENT, TYPE_KEY_1); @@ -295,7 +294,7 @@ public void sync_WithUpdatedType_WithUpdatedFieldDefinition_ShouldUpdateTypeUpda .toCompletableFuture().join(); //assertions - AssertionsForStatistics.assertThat(typeSyncStatistics).hasValues(1, 0, 1, 0); + assertThat(typeSyncStatistics).hasValues(1, 0, 1, 0); final Optional oldTypeAfter = getTypeByKey(CTP_TARGET_CLIENT, TYPE_KEY_1); @@ -344,7 +343,7 @@ public void sync_WithoutKey_ShouldExecuteCallbackOnErrorAndIncreaseFailedCounter .hasSize(1) .hasOnlyOneElementSatisfying(throwable -> assertThat(throwable).isNull()); - AssertionsForStatistics.assertThat(typeSyncStatistics).hasValues(1, 0, 0, 1); + assertThat(typeSyncStatistics).hasValues(1, 0, 0, 1); } @Test @@ -381,7 +380,7 @@ public void sync_WithNullDraft_ShouldExecuteCallbackOnErrorAndIncreaseFailedCoun .hasSize(1) .hasOnlyOneElementSatisfying(throwable -> assertThat(throwable).isNull()); - AssertionsForStatistics.assertThat(typeSyncStatistics).hasValues(1, 0, 0, 1); + assertThat(typeSyncStatistics).hasValues(1, 0, 0, 1); } @Test @@ -430,7 +429,7 @@ public void sync_WithoutName_ShouldExecuteCallbackOnErrorAndIncreaseFailedCounte assertThat(throwable).hasMessageContaining("Missing required value"); }); - AssertionsForStatistics.assertThat(typeSyncStatistics).hasValues(1, 0, 0, 1); + assertThat(typeSyncStatistics).hasValues(1, 0, 0, 1); } @Test @@ -485,7 +484,7 @@ public void sync_WithoutFieldDefinitionType_ShouldExecuteCallbackOnErrorAndIncre assertThat(throwable).hasMessageContaining("Missing required value"); }); - AssertionsForStatistics.assertThat(typeSyncStatistics).hasValues(1, 0, 0, 1); + assertThat(typeSyncStatistics).hasValues(1, 0, 0, 1); } @Test @@ -516,7 +515,7 @@ public void sync_WithSeveralBatches_ShouldReturnProperStatistics() { .join(); //assertion - AssertionsForStatistics.assertThat(typeSyncStatistics) + assertThat(typeSyncStatistics) .hasValues(100, 100, 0, 0); } @@ -607,7 +606,7 @@ public void sync_WithChangedTypeButBadGatewayException_ShouldFailUpdateType() { .join(); // Test and assertion - AssertionsForStatistics.assertThat(statistics).hasValues(1, 0, 0, 1); + assertThat(statistics).hasValues(1, 0, 0, 1); assertThat(errorMessages).hasSize(1); assertThat(errors).hasSize(1); @@ -652,7 +651,7 @@ public void sync_WithChangedTypeButConcurrentModificationException_ShouldRetryTo .join(); // Test and assertion - AssertionsForStatistics.assertThat(statistics).hasValues(1, 0, 1, 0); + assertThat(statistics).hasValues(1, 0, 1, 0); assertThat(errorMessages).isEmpty(); assertThat(errors).isEmpty(); } @@ -695,7 +694,7 @@ public void sync_WithChangedTypeButBadGatewayException_ShouldFailToReFetchAndUpd .join(); // Test and assertion - AssertionsForStatistics.assertThat(statistics).hasValues(1, 0, 0, 1); + assertThat(statistics).hasValues(1, 0, 0, 1); assertThat(errorMessages).hasSize(2); assertThat(errors).hasSize(2); From d237b072b7760c8cb28658cd39e8fd3e4ea26eb4 Mon Sep 17 00:00:00 2001 From: aoz Date: Wed, 5 Dec 2018 11:06:48 +0100 Subject: [PATCH 101/164] #300 remove populateSourceProject because it's not needed. --- .../sync/integration/externalsource/types/TypeSyncIT.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/integration-test/java/com/commercetools/sync/integration/externalsource/types/TypeSyncIT.java b/src/integration-test/java/com/commercetools/sync/integration/externalsource/types/TypeSyncIT.java index 6dc50fa20b..000e253222 100644 --- a/src/integration-test/java/com/commercetools/sync/integration/externalsource/types/TypeSyncIT.java +++ b/src/integration-test/java/com/commercetools/sync/integration/externalsource/types/TypeSyncIT.java @@ -51,7 +51,6 @@ import static com.commercetools.sync.integration.commons.utils.TypeITUtils.TYPE_NAME_1; import static com.commercetools.sync.integration.commons.utils.TypeITUtils.TYPE_NAME_2; import static com.commercetools.sync.integration.commons.utils.TypeITUtils.getTypeByKey; -import static com.commercetools.sync.integration.commons.utils.TypeITUtils.populateSourceProject; import static com.commercetools.sync.integration.commons.utils.TypeITUtils.populateTargetProject; import static java.lang.String.format; import static java.util.Arrays.asList; @@ -71,7 +70,6 @@ public class TypeSyncIT { @Before public void setup() { deleteTypes(CTP_TARGET_CLIENT); - populateSourceProject(); populateTargetProject(); } From 5843e5089df0a3fa9bfb5dfa426f95b4d68b7c13 Mon Sep 17 00:00:00 2001 From: aoz Date: Wed, 5 Dec 2018 11:12:56 +0100 Subject: [PATCH 102/164] #300 remove confusing commented out code from assertFieldDefinitionsAreEqual, add this case a todo. --- .../sync/integration/externalsource/types/TypeSyncIT.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/integration-test/java/com/commercetools/sync/integration/externalsource/types/TypeSyncIT.java b/src/integration-test/java/com/commercetools/sync/integration/externalsource/types/TypeSyncIT.java index 000e253222..96cbcacb13 100644 --- a/src/integration-test/java/com/commercetools/sync/integration/externalsource/types/TypeSyncIT.java +++ b/src/integration-test/java/com/commercetools/sync/integration/externalsource/types/TypeSyncIT.java @@ -528,8 +528,7 @@ private static void assertFieldDefinitionsAreEqual(@Nonnull final List Date: Wed, 5 Dec 2018 11:20:30 +0100 Subject: [PATCH 103/164] #300 remove confusing **potentially** injected from java docs. --- .../com/commercetools/sync/services/TypeService.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/commercetools/sync/services/TypeService.java b/src/main/java/com/commercetools/sync/services/TypeService.java index 24b389667e..fbab46db4f 100644 --- a/src/main/java/com/commercetools/sync/services/TypeService.java +++ b/src/main/java/com/commercetools/sync/services/TypeService.java @@ -44,8 +44,8 @@ public interface TypeService { CompletionStage> fetchMatchingTypesByKeys(@Nonnull final Set keys); /** - * Given a type key, this method fetches a type that matches this given key in the CTP project defined in a - * potentially injected {@link SphereClient}. If there is no matching type an empty {@link Optional} will be + * Given a type key, this method fetches a type that matches this given key in the CTP project defined in an + * injected {@link SphereClient}. If there is no matching type an empty {@link Optional} will be * returned in the returned future. * * @param key the key of the type to fetch. @@ -56,8 +56,8 @@ public interface TypeService { CompletionStage> fetchType(@Nullable final String key); /** - * Given a {@link TypeDraft}, this method creates a {@link Type} based on it in the CTP project defined in - * a potentially injected {@link io.sphere.sdk.client.SphereClient}. The created type's id and key are also + * Given a {@link TypeDraft}, this method creates a {@link Type} based on it in the CTP project defined in an + * injected {@link io.sphere.sdk.client.SphereClient}. The created type's id and key are also * cached. This method returns {@link CompletionStage}<{@link Type}> in which the result of it's * completion contains an instance of the {@link Type} which was created in the CTP project. * @@ -70,8 +70,8 @@ public interface TypeService { /** * Given a {@link Type} and a {@link List}<{@link UpdateAction}<{@link Type}>>, this method - * issues an update request with these update actions on this {@link Type} in the CTP project defined in a - * potentially injected {@link io.sphere.sdk.client.SphereClient}. This method returns + * issues an update request with these update actions on this {@link Type} in the CTP project defined in an + * injected {@link io.sphere.sdk.client.SphereClient}. This method returns * {@link CompletionStage}<{@link Type}> in which the result of it's completion contains an instance of * the {@link Type} which was updated in the CTP project. * From 624259817a45eeaa19356c62a452a671fd92c7a3 Mon Sep 17 00:00:00 2001 From: aoz Date: Wed, 5 Dec 2018 11:22:07 +0100 Subject: [PATCH 104/164] #300 do not shadow BaseService --- .../com/commercetools/sync/services/impl/TypeServiceImpl.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java b/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java index 25b1d763cd..f536ef0eb1 100644 --- a/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java +++ b/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java @@ -18,12 +18,10 @@ import javax.annotation.Nullable; import java.util.Collections; import java.util.List; -import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; -import java.util.concurrent.ConcurrentHashMap; import java.util.function.Consumer; import static java.lang.String.format; @@ -36,8 +34,6 @@ */ public final class TypeServiceImpl extends BaseService implements TypeService { private static final String FETCH_FAILED = "Failed to fetch types with keys: '%s'. Reason: %s"; - private final Map keyToIdCache = new ConcurrentHashMap<>(); - private boolean isCached = false; public TypeServiceImpl(@Nonnull final BaseSyncOptions syncOptions) { super(syncOptions); From 4c2f4361724538a7dec990c1219b9eb74b65b3cd Mon Sep 17 00:00:00 2001 From: aoz Date: Wed, 5 Dec 2018 16:26:25 +0100 Subject: [PATCH 105/164] #300 update type service to fix compile errors --- .../com/commercetools/sync/services/impl/TypeServiceImpl.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java b/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java index f536ef0eb1..7fa511070e 100644 --- a/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java +++ b/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java @@ -32,7 +32,7 @@ * Implementation of TypeService interface. * TODO: USE graphQL to get only keys. GITHUB ISSUE#84 */ -public final class TypeServiceImpl extends BaseService implements TypeService { +public final class TypeServiceImpl extends BaseService implements TypeService { private static final String FETCH_FAILED = "Failed to fetch types with keys: '%s'. Reason: %s"; public TypeServiceImpl(@Nonnull final BaseSyncOptions syncOptions) { @@ -91,7 +91,7 @@ public CompletionStage> fetchType(@Nullable final String key) { @Nonnull @Override public CompletionStage> createType(@Nonnull final TypeDraft typeDraft) { - return applyCallbackAndCreate(typeDraft, typeDraft.getKey(), TypeCreateCommand::of); + return createResource(typeDraft, TypeDraft::getKey, TypeCreateCommand::of); } @Nonnull From f59ebb7387729c898e32893463bb93b9177900c8 Mon Sep 17 00:00:00 2001 From: aoz Date: Wed, 5 Dec 2018 16:28:35 +0100 Subject: [PATCH 106/164] #300 remove constructor --- .../commercetools/sync/services/impl/TypeServiceImpl.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java b/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java index 7fa511070e..37e7d57bf9 100644 --- a/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java +++ b/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java @@ -3,7 +3,6 @@ import com.commercetools.sync.commons.BaseSyncOptions; import com.commercetools.sync.commons.utils.CtpQueryUtils; import com.commercetools.sync.services.TypeService; -import com.commercetools.sync.types.TypeSyncOptions; import io.sphere.sdk.commands.UpdateAction; import io.sphere.sdk.queries.PagedResult; import io.sphere.sdk.queries.QueryExecutionUtils; @@ -39,10 +38,6 @@ public TypeServiceImpl(@Nonnull final BaseSyncOptions syncOptions) { super(syncOptions); } - public TypeServiceImpl(@Nonnull final TypeSyncOptions syncOptions) { - super(syncOptions); - } - @Nonnull @Override public CompletionStage> fetchCachedTypeId(@Nonnull final String key) { From dd9d5cbaa1e710f718e5954f0f988ba4e443469c Mon Sep 17 00:00:00 2001 From: aoz Date: Wed, 5 Dec 2018 16:36:02 +0100 Subject: [PATCH 107/164] #300 refactor fetchCachedTypeId --- .../commercetools/sync/services/impl/TypeServiceImpl.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java b/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java index 37e7d57bf9..9d10f09aa2 100644 --- a/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java +++ b/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java @@ -41,10 +41,11 @@ public TypeServiceImpl(@Nonnull final BaseSyncOptions syncOptions) { @Nonnull @Override public CompletionStage> fetchCachedTypeId(@Nonnull final String key) { - if (!isCached) { - return fetchAndCache(key); + + if (keyToIdCache.containsKey(key)) { + return CompletableFuture.completedFuture(Optional.ofNullable(keyToIdCache.get(key))); } - return CompletableFuture.completedFuture(Optional.ofNullable(keyToIdCache.get(key))); + return fetchAndCache(key); } @Nonnull From 76bb31b0cb65b0d32c90e846c97a1bd888086359 Mon Sep 17 00:00:00 2001 From: aoz Date: Wed, 5 Dec 2018 16:42:56 +0100 Subject: [PATCH 108/164] #300 refactor fetchMatchingTypesByKeys and fix ignored test --- .../services/impl/TypeServiceImplIT.java | 29 ++++++++----------- .../sync/services/impl/TypeServiceImpl.java | 6 ++-- 2 files changed, 16 insertions(+), 19 deletions(-) diff --git a/src/integration-test/java/com/commercetools/sync/integration/services/impl/TypeServiceImplIT.java b/src/integration-test/java/com/commercetools/sync/integration/services/impl/TypeServiceImplIT.java index 959e5eb646..5d5decb430 100644 --- a/src/integration-test/java/com/commercetools/sync/integration/services/impl/TypeServiceImplIT.java +++ b/src/integration-test/java/com/commercetools/sync/integration/services/impl/TypeServiceImplIT.java @@ -20,7 +20,6 @@ import org.apache.commons.lang3.StringUtils; import org.junit.AfterClass; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import java.util.ArrayList; @@ -32,12 +31,12 @@ import java.util.Set; import static com.commercetools.sync.integration.commons.utils.CategoryITUtils.createCategoriesCustomType; -import static com.commercetools.sync.integration.commons.utils.ITUtils.deleteTypesFromTargetAndSource; import static com.commercetools.sync.integration.commons.utils.SphereClientUtils.CTP_TARGET_CLIENT; import static com.commercetools.sync.integration.commons.utils.TypeITUtils.FIELD_DEFINITION_1; import static com.commercetools.sync.integration.commons.utils.TypeITUtils.TYPE_DESCRIPTION_1; import static com.commercetools.sync.integration.commons.utils.TypeITUtils.TYPE_KEY_1; import static com.commercetools.sync.integration.commons.utils.TypeITUtils.TYPE_NAME_1; +import static com.commercetools.sync.integration.commons.utils.TypeITUtils.deleteTypes; import static com.commercetools.tests.utils.CompletionStageUtil.executeBlocking; import static java.lang.String.format; import static java.util.Collections.singleton; @@ -57,14 +56,14 @@ public class TypeServiceImplIT { private List errorCallBackExceptions; /** - * Deletes types from source and target CTP projects, then it populates target CTP project with test data. + * Deletes types from the target CTP project, then it populates the project with test data. */ @Before public void setup() { errorCallBackMessages = new ArrayList<>(); errorCallBackExceptions = new ArrayList<>(); - deleteTypesFromTargetAndSource(); + deleteTypes(CTP_TARGET_CLIENT); createCategoriesCustomType(OLD_TYPE_KEY, OLD_TYPE_LOCALE, OLD_TYPE_NAME, CTP_TARGET_CLIENT); final TypeSyncOptions typeSyncOptions = TypeSyncOptionsBuilder.of(CTP_TARGET_CLIENT) @@ -77,11 +76,11 @@ public void setup() { } /** - * Cleans up the target and source test data that were built in this test class. + * Cleans up the target test data that were built in this test class. */ @AfterClass public static void tearDown() { - deleteTypesFromTargetAndSource(); + deleteTypes(CTP_TARGET_CLIENT); } @Test @@ -165,8 +164,6 @@ public void fetchMatchingTypesByKeys_WithAnyExistingKeys_ShouldReturnASetOfTypes assertThat(errorCallBackMessages).isEmpty(); } - // TODO: GITHUB ISSUE#331 - @Ignore @Test public void fetchMatchingTypesByKeys_WithBadGateWayExceptionAlways_ShouldFail() { // Mock sphere client to return BadeGatewayException on any request. @@ -188,15 +185,13 @@ public void fetchMatchingTypesByKeys_WithBadGateWayExceptionAlways_ShouldFail() final Set keys = new HashSet<>(); keys.add(OLD_TYPE_KEY); - final Set fetchedTypes = spyTypeService.fetchMatchingTypesByKeys(keys) - .toCompletableFuture().join(); - assertThat(fetchedTypes).hasSize(0); - assertThat(errorCallBackExceptions).isNotEmpty(); - assertThat(errorCallBackExceptions.get(0).getCause()).isExactlyInstanceOf(BadGatewayException.class); - assertThat(errorCallBackMessages).isNotEmpty(); - assertThat(errorCallBackMessages.get(0)) - .isEqualToIgnoringCase(format("Failed to fetch types with keys: '%s'. Reason: %s", - keys.toString(), errorCallBackExceptions.get(0))); + + // test and assert + assertThat(errorCallBackExceptions).isEmpty(); + assertThat(errorCallBackMessages).isEmpty(); + assertThat(spyTypeService.fetchMatchingTypesByKeys(keys)) + .hasFailedWithThrowableThat() + .isExactlyInstanceOf(BadGatewayException.class); } @Test diff --git a/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java b/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java index 9d10f09aa2..6b956a750e 100644 --- a/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java +++ b/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java @@ -5,7 +5,6 @@ import com.commercetools.sync.services.TypeService; import io.sphere.sdk.commands.UpdateAction; import io.sphere.sdk.queries.PagedResult; -import io.sphere.sdk.queries.QueryExecutionUtils; import io.sphere.sdk.types.Type; import io.sphere.sdk.types.TypeDraft; import io.sphere.sdk.types.commands.TypeCreateCommand; @@ -22,6 +21,7 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; import java.util.function.Consumer; +import java.util.function.Function; import static java.lang.String.format; import static java.util.stream.Collectors.toSet; @@ -51,6 +51,7 @@ public CompletionStage> fetchCachedTypeId(@Nonnull final String @Nonnull @Override public CompletionStage> fetchMatchingTypesByKeys(@Nonnull final Set keys) { + if (keys.isEmpty()) { return CompletableFuture.completedFuture(Collections.emptySet()); } @@ -60,9 +61,10 @@ public CompletionStage> fetchMatchingTypesByKeys(@Nonnull final Set queryModel.key().isIn(keys)) .build(); - return QueryExecutionUtils.queryAll(syncOptions.getCtpClient(), typeQuery) + return CtpQueryUtils.queryAll(syncOptions.getCtpClient(), typeQuery, Function.identity()) .thenApply(types -> types .stream() + .flatMap(List::stream) .peek(type -> keyToIdCache.put(type.getKey(), type.getId())) .collect(toSet())); } From 8ce297f886c9291ff15eb4ffadc4d3b15fdc8805 Mon Sep 17 00:00:00 2001 From: aoz Date: Wed, 5 Dec 2018 18:50:22 +0100 Subject: [PATCH 109/164] #300 update type sync and type services - includes updates for IT messages and concurrency modification tests - includes updates for type refetching - included test changes in type service IT --- .../externalsource/types/TypeSyncIT.java | 209 +++++++++++------- .../services/impl/TypeServiceImplIT.java | 181 ++++++++------- .../sync/services/TypeService.java | 22 +- .../sync/services/impl/TypeServiceImpl.java | 39 +++- .../commercetools/sync/types/TypeSync.java | 191 +++++++++------- .../sync/types/TypeSyncTest.java | 68 +++++- 6 files changed, 455 insertions(+), 255 deletions(-) diff --git a/src/integration-test/java/com/commercetools/sync/integration/externalsource/types/TypeSyncIT.java b/src/integration-test/java/com/commercetools/sync/integration/externalsource/types/TypeSyncIT.java index 96cbcacb13..de18fd858b 100644 --- a/src/integration-test/java/com/commercetools/sync/integration/externalsource/types/TypeSyncIT.java +++ b/src/integration-test/java/com/commercetools/sync/integration/externalsource/types/TypeSyncIT.java @@ -12,6 +12,7 @@ import io.sphere.sdk.models.LocalizedEnumValue; import io.sphere.sdk.models.LocalizedString; import io.sphere.sdk.models.TextInputHint; +import io.sphere.sdk.queries.PagedQueryResult; import io.sphere.sdk.types.EnumFieldType; import io.sphere.sdk.types.FieldDefinition; import io.sphere.sdk.types.LocalizedEnumFieldType; @@ -20,9 +21,9 @@ import io.sphere.sdk.types.Type; import io.sphere.sdk.types.TypeDraft; import io.sphere.sdk.types.TypeDraftBuilder; +import io.sphere.sdk.types.commands.TypeCreateCommand; import io.sphere.sdk.types.commands.TypeUpdateCommand; import io.sphere.sdk.types.queries.TypeQuery; -import io.sphere.sdk.utils.CompletableFutureUtils; import org.junit.AfterClass; import org.junit.Before; import org.junit.Test; @@ -32,6 +33,7 @@ import java.util.Collections; import java.util.List; import java.util.Optional; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; import java.util.stream.Collectors; import java.util.stream.IntStream; @@ -52,6 +54,7 @@ import static com.commercetools.sync.integration.commons.utils.TypeITUtils.TYPE_NAME_2; import static com.commercetools.sync.integration.commons.utils.TypeITUtils.getTypeByKey; import static com.commercetools.sync.integration.commons.utils.TypeITUtils.populateTargetProject; +import static io.sphere.sdk.utils.CompletableFutureUtils.exceptionallyCompletedFuture; import static java.lang.String.format; import static java.util.Arrays.asList; import static java.util.Collections.singletonList; @@ -416,7 +419,7 @@ public void sync_WithoutName_ShouldExecuteCallbackOnErrorAndIncreaseFailedCounte assertThat(errorMessages) .hasSize(1) .hasOnlyOneElementSatisfying(message -> - assertThat(message).contains("Failed to update type with key 'key_1'.") + assertThat(message).contains("Failed to update type with key: 'key_1'.") ); assertThat(exceptions) @@ -471,7 +474,7 @@ public void sync_WithoutFieldDefinitionType_ShouldExecuteCallbackOnErrorAndIncre assertThat(errorMessages) .hasSize(1) .hasOnlyOneElementSatisfying(message -> - assertThat(message).contains("Failed to update type with key 'key_1'.") + assertThat(message).contains("Failed to update type with key: 'key_1'.") ); assertThat(exceptions) @@ -572,11 +575,72 @@ private static void assertLocalizedEnumsValuesAreEqual(@Nonnull final List queryResult = + CTP_TARGET_CLIENT.execute(TypeQuery.of().plusPredicates(queryModel -> + queryModel.key().is(typeDraft.getKey()))) + .toCompletableFuture() + .join(); + + assertThat(queryResult.head()).hasValueSatisfying(type -> + assertThat(type.getName()).isEqualTo(newTypeName)); + } + + @Nonnull + private SphereClient buildClientWithConcurrentModificationUpdate() { + final SphereClient spyClient = spy(CTP_TARGET_CLIENT); - when(spyClient.execute(any(TypeUpdateCommand.class))) - .thenReturn(CompletableFutureUtils.exceptionallyCompletedFuture( new BadGatewayException())); + + final TypeUpdateCommand anyTypeUpdate = any(TypeUpdateCommand.class); + + when(spyClient.execute(anyTypeUpdate)) + .thenReturn(exceptionallyCompletedFuture(new ConcurrentModificationException())) + .thenCallRealMethod(); + + return spyClient; + } + + @Test + public void sync_WithConcurrentModificationExceptionAndFailedFetch_ShouldFailToReFetchAndUpdate() { + //preparation + final SphereClient spyClient = buildClientWithConcurrentModificationUpdateAndFailedFetchOnRetry(); + + final TypeDraft typeDraft = TypeDraftBuilder + .of("typeKey", LocalizedString.ofEnglish( "typeName"), ResourceTypeIdsSetBuilder.of().addChannels()) + .build(); + + CTP_TARGET_CLIENT.execute(TypeCreateCommand.of(typeDraft)) + .toCompletableFuture() + .join(); + + final LocalizedString newTypeName = LocalizedString.ofEnglish( "typeName_updated"); + final TypeDraft updatedDraft = TypeDraftBuilder.of(typeDraft).name(newTypeName).build(); final List errorMessages = new ArrayList<>(); final List errors = new ArrayList<>(); @@ -589,82 +653,58 @@ public void sync_WithChangedTypeButBadGatewayException_ShouldFailUpdateType() { }) .build(); - final TypeDraft typeDraft = TypeDraftBuilder.of( - TYPE_KEY_1, - TYPE_NAME_2, - ResourceTypeIdsSetBuilder.of().addCategories().build()) - .description(TYPE_DESCRIPTION_2) - .fieldDefinitions(singletonList(FIELD_DEFINITION_1)) - .build(); - final TypeSync typeSync = new TypeSync(typeSyncOptions); - final TypeSyncStatistics statistics = typeSync.sync(Collections.singletonList(typeDraft)) + + //test + final TypeSyncStatistics statistics = typeSync.sync(Collections.singletonList(updatedDraft)) .toCompletableFuture() .join(); - // Test and assertion + //assertion assertThat(statistics).hasValues(1, 0, 0, 1); + assertThat(errorMessages).hasSize(1); assertThat(errors).hasSize(1); assertThat(errors.get(0).getCause()).isExactlyInstanceOf(BadGatewayException.class); + assertThat(errorMessages.get(0)).contains( + format("Failed to update type with key: '%s'. Reason: Failed to fetch from CTP while retrying " + + "after concurrency modification.", typeDraft.getKey())); - assertThat(errorMessages.get(0)) - .contains(format("Failed to update type with key: '%s'. Reason: %s", - typeDraft.getKey(), errors.get(0))); } - @Test - public void sync_WithChangedTypeButConcurrentModificationException_ShouldRetryToUpdateNewTypeWithSuccess() { + @Nonnull + private SphereClient buildClientWithConcurrentModificationUpdateAndFailedFetchOnRetry() { - // Mock sphere client to return ConcurrentModification on the first update request. final SphereClient spyClient = spy(CTP_TARGET_CLIENT); - when(spyClient.execute(any(TypeUpdateCommand.class))) - .thenReturn(CompletableFutureUtils.exceptionallyCompletedFuture(new ConcurrentModificationException())) - .thenCallRealMethod(); - - final List errorMessages = new ArrayList<>(); - final List errors = new ArrayList<>(); + when(spyClient.execute(any(TypeQuery.class))) + .thenCallRealMethod() // Call real fetch on fetching matching types + .thenReturn(exceptionallyCompletedFuture(new BadGatewayException())); - final TypeSyncOptions typeSyncOptions = - TypeSyncOptionsBuilder.of(spyClient) - .errorCallback((errorMessage, error) -> { - errorMessages.add(errorMessage); - errors.add(error); - }) - .build(); - - final TypeDraft typeDraft = TypeDraftBuilder.of( - TYPE_KEY_1, - TYPE_NAME_2, - ResourceTypeIdsSetBuilder.of().addCategories().build()) - .description(TYPE_DESCRIPTION_2) - .fieldDefinitions(singletonList(FIELD_DEFINITION_1)) - .build(); + final TypeUpdateCommand anyTypeUpdate = any(TypeUpdateCommand.class); - final TypeSync typeSync = new TypeSync(typeSyncOptions); - final TypeSyncStatistics statistics = typeSync.sync(Collections.singletonList(typeDraft)) - .toCompletableFuture() - .join(); + when(spyClient.execute(anyTypeUpdate)) + .thenReturn(exceptionallyCompletedFuture(new ConcurrentModificationException())) + .thenCallRealMethod(); - // Test and assertion - assertThat(statistics).hasValues(1, 0, 1, 0); - assertThat(errorMessages).isEmpty(); - assertThat(errors).isEmpty(); + return spyClient; } @Test - public void sync_WithChangedTypeButBadGatewayException_ShouldFailToReFetchAndUpdate() { - // Mock sphere client to return ConcurrentModification on the first update request. - final SphereClient spyClient = spy(CTP_TARGET_CLIENT); - when(spyClient.execute(any(TypeUpdateCommand.class))) - .thenReturn(CompletableFutureUtils.exceptionallyCompletedFuture( new ConcurrentModificationException())) - .thenCallRealMethod(); + public void sync__WithConcurrentModificationExceptionAndUnexpectedDelete_ShouldFailToReFetchAndUpdate() { + //preparation + final SphereClient spyClient = buildClientWithConcurrentModificationUpdateAndNotFoundFetchOnRetry(); - when(spyClient.execute(any(TypeQuery.class))) - .thenCallRealMethod() // fetchMatchingTypesByKeys - .thenReturn(CompletableFutureUtils.exceptionallyCompletedFuture(new BadGatewayException())); + final TypeDraft typeDraft = TypeDraftBuilder + .of("typeKey", LocalizedString.ofEnglish( "typeName"), ResourceTypeIdsSetBuilder.of().addChannels()) + .build(); + CTP_TARGET_CLIENT.execute(TypeCreateCommand.of(typeDraft)) + .toCompletableFuture() + .join(); + + final LocalizedString newTypeName = LocalizedString.ofEnglish( "typeName_updated"); + final TypeDraft updatedDraft = TypeDraftBuilder.of(typeDraft).name(newTypeName).build(); final List errorMessages = new ArrayList<>(); final List errors = new ArrayList<>(); @@ -677,32 +717,41 @@ public void sync_WithChangedTypeButBadGatewayException_ShouldFailToReFetchAndUpd }) .build(); - final TypeDraft typeDraft = TypeDraftBuilder.of( - TYPE_KEY_1, - TYPE_NAME_2, - ResourceTypeIdsSetBuilder.of().addCategories().build()) - .description(TYPE_DESCRIPTION_2) - .fieldDefinitions(singletonList(FIELD_DEFINITION_1)) - .build(); - final TypeSync typeSync = new TypeSync(typeSyncOptions); - final TypeSyncStatistics statistics = typeSync.sync(Collections.singletonList(typeDraft)) + //test + final TypeSyncStatistics statistics = typeSync.sync(Collections.singletonList(updatedDraft)) .toCompletableFuture() .join(); - // Test and assertion + // Assertion assertThat(statistics).hasValues(1, 0, 0, 1); - assertThat(errorMessages).hasSize(2); - assertThat(errors).hasSize(2); - assertThat(errors.get(0).getCause()).isExactlyInstanceOf(BadGatewayException.class); - assertThat(errorMessages.get(0)) - .contains(format("Failed to fetch types with keys: '%s'. Reason: %s", - typeDraft.getKey(), errors.get(0))); + assertThat(errorMessages).hasSize(1); + assertThat(errors).hasSize(1); + assertThat(errorMessages.get(0)).contains( + format("Failed to update type with key: '%s'. Reason: Not found when attempting to fetch while " + + "retrying after concurrency modification.", typeDraft.getKey())); + + } + + @Nonnull + private SphereClient buildClientWithConcurrentModificationUpdateAndNotFoundFetchOnRetry() { + final SphereClient spyClient = spy(CTP_TARGET_CLIENT); + final TypeQuery anyTypeQuery = any(TypeQuery.class); + + when(spyClient.execute(anyTypeQuery)) + .thenCallRealMethod() // Call real fetch on fetching matching types + .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); + + + final TypeUpdateCommand anyTypeUpdate = any(TypeUpdateCommand.class); + + when(spyClient.execute(anyTypeUpdate)) + .thenReturn(exceptionallyCompletedFuture(new ConcurrentModificationException())) + .thenCallRealMethod(); + - assertThat(errorMessages.get(1)) - .contains(format("Failed to update type with key: '%s'. Reason: Failed to fetch type on retry.", - typeDraft.getKey())); + return spyClient; } } diff --git a/src/integration-test/java/com/commercetools/sync/integration/services/impl/TypeServiceImplIT.java b/src/integration-test/java/com/commercetools/sync/integration/services/impl/TypeServiceImplIT.java index 8e320314d3..e2572c9f7f 100644 --- a/src/integration-test/java/com/commercetools/sync/integration/services/impl/TypeServiceImplIT.java +++ b/src/integration-test/java/com/commercetools/sync/integration/services/impl/TypeServiceImplIT.java @@ -6,13 +6,14 @@ import com.commercetools.sync.types.TypeSyncOptions; import com.commercetools.sync.types.TypeSyncOptionsBuilder; import io.sphere.sdk.client.BadGatewayException; +import io.sphere.sdk.client.ErrorResponseException; import io.sphere.sdk.client.SphereClient; import io.sphere.sdk.models.LocalizedString; +import io.sphere.sdk.models.errors.DuplicateFieldError; import io.sphere.sdk.types.ResourceTypeIdsSetBuilder; import io.sphere.sdk.types.Type; import io.sphere.sdk.types.TypeDraft; import io.sphere.sdk.types.TypeDraftBuilder; -import io.sphere.sdk.types.commands.TypeCreateCommand; import io.sphere.sdk.types.commands.updateactions.ChangeKey; import io.sphere.sdk.types.commands.updateactions.ChangeName; import io.sphere.sdk.types.queries.TypeQuery; @@ -38,12 +39,14 @@ import static com.commercetools.sync.integration.commons.utils.TypeITUtils.TYPE_NAME_1; import static com.commercetools.sync.integration.commons.utils.TypeITUtils.deleteTypes; import static com.commercetools.tests.utils.CompletionStageUtil.executeBlocking; -import static java.lang.String.format; import static java.util.Collections.singleton; import static java.util.Collections.singletonList; +import static java.util.stream.Collectors.toList; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; public class TypeServiceImplIT { @@ -103,25 +106,6 @@ public void fetchCachedTypeId_WithExistingType_ShouldFetchTypeAndCache() { assertThat(errorCallBackMessages).isEmpty(); } - @Test - public void fetchCachedTypeId_WithNewlyCreatedTypeAfterCaching_ShouldNotFetchNewType() { - // Fetch any type to populate cache - typeService.fetchCachedTypeId("anyTypeKey").toCompletableFuture().join(); - - // Create new type - final String newTypeKey = "new_type_key"; - final TypeDraft draft = TypeDraftBuilder - .of(newTypeKey, LocalizedString.of(Locale.ENGLISH, "typeName"), - ResourceTypeIdsSetBuilder.of().addChannels()) - .build(); - CTP_TARGET_CLIENT.execute(TypeCreateCommand.of(draft)).toCompletableFuture().join(); - - final Optional newTypeId = - typeService.fetchCachedTypeId(newTypeKey).toCompletableFuture().join(); - - assertThat(newTypeId).isEmpty(); - } - @Test public void fetchMatchingTypesByKeys_WithEmptySetOfKeys_ShouldReturnEmptySet() { final Set typeKeys = new HashSet<>(); @@ -226,7 +210,7 @@ public void fetchMatchingTypesByKeys_WithAllExistingSetOfKeys_ShouldCacheFetched } @Test - public void createType_WithValidType_ShouldCreateType() { + public void createType_WithValidType_ShouldCreateTypeAndCacheId() { final TypeDraft newTypeDraft = TypeDraftBuilder.of( TYPE_KEY_1, TYPE_NAME_1, @@ -235,43 +219,121 @@ public void createType_WithValidType_ShouldCreateType() { .fieldDefinitions(singletonList(FIELD_DEFINITION_1)) .build(); - final Optional createdType = typeService.createType(newTypeDraft) - .toCompletableFuture().join(); + final SphereClient spyClient = spy(CTP_TARGET_CLIENT); + final TypeSyncOptions spyOptions = TypeSyncOptionsBuilder + .of(spyClient) + .errorCallback((errorMessage, exception) -> { + errorCallBackMessages.add(errorMessage); + errorCallBackExceptions.add(exception); + }) + .build(); - assertThat(createdType).isNotNull(); + final TypeService spyTypeService = new TypeServiceImpl(spyOptions); - final Optional fetchedType = CTP_TARGET_CLIENT - .execute(TypeQuery.of() - .withPredicates(typeQueryModel -> - typeQueryModel.key().is(createdType.get().getKey()))) + //test + final Optional createdType = spyTypeService + .createType(newTypeDraft) + .toCompletableFuture().join(); + + final Optional queriedOptional = CTP_TARGET_CLIENT + .execute(TypeQuery.of().withPredicates(typeQueryModel -> + typeQueryModel.key().is(TYPE_KEY_1))) .toCompletableFuture().join().head(); - assertThat(fetchedType).hasValueSatisfying(oldType -> - assertThat(createdType).hasValueSatisfying(newType -> { - assertThat(newType.getKey()).isEqualTo(newTypeDraft.getKey()); - assertThat(newType.getDescription()).isEqualTo(oldType.getDescription()); - assertThat(newType.getName()).isEqualTo(oldType.getName()); - assertThat(newType.getFieldDefinitions()).isEqualTo(oldType.getFieldDefinitions()); + assertThat(queriedOptional).hasValueSatisfying(queried -> + assertThat(createdType).hasValueSatisfying(created -> { + assertThat(created.getKey()).isEqualTo(queried.getKey()); + assertThat(created.getDescription()).isEqualTo(queried.getDescription()); + assertThat(created.getName()).isEqualTo(queried.getName()); + assertThat(created.getFieldDefinitions()).isEqualTo(queried.getFieldDefinitions()); })); + + // Assert that the created type is cached + final Optional typeId = + spyTypeService.fetchCachedTypeId(TYPE_KEY_1).toCompletableFuture().join(); + assertThat(typeId).isPresent(); + verify(spyClient, times(0)).execute(any(TypeQuery.class)); } @Test - public void createType_WithInvalidType_ShouldCompleteExceptionally() { + public void createType_WithInvalidType_ShouldHaveEmptyOptionalAsAResult() { + //preparation final TypeDraft newTypeDraft = TypeDraftBuilder.of( - TYPE_KEY_1, - null, + "", + TYPE_NAME_1, ResourceTypeIdsSetBuilder.of().addCategories().build()) .description(TYPE_DESCRIPTION_1) .fieldDefinitions(singletonList(FIELD_DEFINITION_1)) .build(); - typeService.createType(newTypeDraft) - .exceptionally(exception -> { - assertThat(exception).isNotNull(); - assertThat(exception.getMessage()).contains("Request body does not contain valid JSON."); - return null; - }) - .toCompletableFuture().join(); + final TypeSyncOptions options = TypeSyncOptionsBuilder + .of(CTP_TARGET_CLIENT) + .errorCallback((errorMessage, exception) -> { + errorCallBackMessages.add(errorMessage); + errorCallBackExceptions.add(exception); + }) + .build(); + + final TypeService typeService = new TypeServiceImpl(options); + + // test + final Optional result = + typeService.createType(newTypeDraft).toCompletableFuture().join(); + + // assertion + assertThat(result).isEmpty(); + assertThat(errorCallBackMessages) + .containsExactly("Failed to create draft with key: ''. Reason: Draft key is blank!"); + + } + + @Test + public void createProductType_WithDuplicateKey_ShouldHaveEmptyOptionalAsAResult() { + //preparation + final TypeDraft newTypeDraft = TypeDraftBuilder.of( + OLD_TYPE_KEY, + TYPE_NAME_1, + ResourceTypeIdsSetBuilder.of().addCategories().build()) + .description(TYPE_DESCRIPTION_1) + .fieldDefinitions(singletonList(FIELD_DEFINITION_1)) + .build(); + + final TypeSyncOptions options = TypeSyncOptionsBuilder + .of(CTP_TARGET_CLIENT) + .errorCallback((errorMessage, exception) -> { + errorCallBackMessages.add(errorMessage); + errorCallBackExceptions.add(exception); + }) + .build(); + + final TypeService typeService = new TypeServiceImpl(options); + + // test + final Optional result = + typeService.createType(newTypeDraft).toCompletableFuture().join(); + + // assertion + assertThat(result).isEmpty(); + assertThat(errorCallBackMessages) + .hasSize(1) + .hasOnlyOneElementSatisfying(msg -> assertThat(msg).contains("A duplicate value")); + + assertThat(errorCallBackExceptions) + .hasSize(1) + .hasOnlyOneElementSatisfying(exception -> { + assertThat(exception).isExactlyInstanceOf(ErrorResponseException.class); + final ErrorResponseException errorResponseException = (ErrorResponseException) exception; + + final List fieldErrors = errorResponseException + .getErrors() + .stream() + .map(sphereError -> { + assertThat(sphereError.getCode()).isEqualTo(DuplicateFieldError.CODE); + return sphereError.as(DuplicateFieldError.class); + }) + .collect(toList()); + assertThat(fieldErrors).hasSize(1); + }); } @Test @@ -346,33 +408,4 @@ public void fetchType_WithNullKey_ShouldNotFetchType() { assertThat(fetchedTypeOptional).isEmpty(); } - @Test - public void fetchType_WithBadGateWayExceptionAlways_ShouldFail() { - // Mock sphere client to return BadeGatewayException on any request. - final SphereClient spyClient = spy(CTP_TARGET_CLIENT); - when(spyClient.execute(any(TypeQuery.class))) - .thenReturn(CompletableFutureUtils.exceptionallyCompletedFuture(new BadGatewayException())) - .thenCallRealMethod(); - final TypeSyncOptions spyOptions = TypeSyncOptionsBuilder.of(spyClient) - .errorCallback( - (errorMessage, exception) -> { - errorCallBackMessages - .add(errorMessage); - errorCallBackExceptions - .add(exception); - }) - .build(); - final TypeService spyTypeService = new TypeServiceImpl(spyOptions); - - final Optional fetchedTypeOptional = - executeBlocking(spyTypeService.fetchType(OLD_TYPE_KEY)); - assertThat(fetchedTypeOptional).isEmpty(); - assertThat(errorCallBackExceptions).hasSize(1); - assertThat(errorCallBackExceptions.get(0).getCause()).isExactlyInstanceOf(BadGatewayException.class); - assertThat(errorCallBackMessages).hasSize(1); - assertThat(errorCallBackMessages.get(0)) - .isEqualToIgnoringCase(format("Failed to fetch types with keys: '%s'. Reason: %s", - OLD_TYPE_KEY, errorCallBackExceptions.get(0))); - } - } diff --git a/src/main/java/com/commercetools/sync/services/TypeService.java b/src/main/java/com/commercetools/sync/services/TypeService.java index a2a0273aaa..956e894b0c 100644 --- a/src/main/java/com/commercetools/sync/services/TypeService.java +++ b/src/main/java/com/commercetools/sync/services/TypeService.java @@ -59,14 +59,22 @@ public interface TypeService { CompletionStage> fetchType(@Nullable final String key); /** - * Given a {@link TypeDraft}, this method creates a {@link Type} based on it in the CTP project defined in an - * injected {@link io.sphere.sdk.client.SphereClient}. The created type's id and key are also - * cached. This method returns {@link CompletionStage}<{@link Type}> in which the result of it's - * completion contains an instance of the {@link Type} which was created in the CTP project. + * Given a resource draft of type {@link TypeDraft}, this method attempts to create a resource + * {@link Type} based on it in the CTP project defined by the sync options. * - * @param typeDraft the {@link TypeDraft} to create a {@link Type} based off of. - * @return {@link CompletionStage}<{@link Optional}> in which the result of it's completion contains an - * {@link Optional} that contains the matching {@link Type} if exists, otherwise empty. + *

A completion stage containing an empty option and the error callback will be triggered in those cases: + *

    + *
  • the draft has a blank key
  • + *
  • the create request fails on CTP
  • + *
+ * + *

On the other hand, if the resource gets created successfully on CTP, then the created resource's id and + * key are cached and the method returns a {@link CompletionStage} in which the result of it's completion + * contains an instance {@link Optional} of the resource which was created. + * + * @param typeDraft the resource draft to create a resource based off of. + * @return a {@link CompletionStage} containing an optional with the created resource if successful otherwise an + * empty optional. */ @Nonnull CompletionStage> createType(@Nonnull final TypeDraft typeDraft); diff --git a/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java b/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java index 1361039b6e..978e373398 100644 --- a/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java +++ b/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java @@ -4,7 +4,6 @@ import com.commercetools.sync.commons.utils.CtpQueryUtils; import com.commercetools.sync.services.TypeService; import io.sphere.sdk.commands.UpdateAction; -import io.sphere.sdk.queries.PagedResult; import io.sphere.sdk.types.Type; import io.sphere.sdk.types.TypeDraft; import io.sphere.sdk.types.commands.TypeCreateCommand; @@ -25,6 +24,7 @@ import static java.lang.String.format; import static java.util.stream.Collectors.toSet; +import static org.apache.commons.lang3.StringUtils.isNotBlank; import static org.apache.http.util.TextUtils.isBlank; /** @@ -71,18 +71,24 @@ public CompletionStage> fetchMatchingTypesByKeys(@Nonnull final Set> fetchType(@Nullable final String key) { + if (isBlank(key)) { return CompletableFuture.completedFuture(Optional.empty()); } - return syncOptions.getCtpClient() - .execute(TypeQuery.of().plusPredicates(typeQueryModel -> typeQueryModel.key().is(key))) - .thenApply(PagedResult::head) - .exceptionally(sphereException -> { - syncOptions.applyErrorCallback(format(FETCH_FAILED, key, sphereException), - sphereException); - return Optional.empty(); - }); + final TypeQuery typeQuery = + TypeQuery.of().plusPredicates(queryModel -> queryModel.key().is(key)); + + return syncOptions + .getCtpClient() + .execute(typeQuery) + .thenApply(typePagedQueryResult -> + typePagedQueryResult + .head() + .map(type -> { + keyToIdCache.put(type.getKey(), type.getId()); + return type; + })); } @Nonnull @@ -101,11 +107,20 @@ public CompletionStage updateType(@Nonnull final Type type, @Nonnull private CompletionStage> fetchAndCache(@Nonnull final String key) { - final Consumer> typePageConsumer = typesPage -> - typesPage.forEach(type -> keyToIdCache.put(type.getKey(), type.getId())); + + final Consumer> typePageConsumer = typePage -> + typePage.forEach(type -> { + final String fetchedTypeKey = type.getKey(); + final String id = type.getId(); + if (isNotBlank(fetchedTypeKey)) { + keyToIdCache.put(fetchedTypeKey, id); + } else { + syncOptions.applyWarningCallback(format("Type with id: '%s' has no key set. Keys are" + + " required for type matching.", id)); + } + }); return CtpQueryUtils.queryAll(syncOptions.getCtpClient(), TypeQuery.of(), typePageConsumer) - .thenAccept(result -> isCached = true) .thenApply(result -> Optional.ofNullable(keyToIdCache.get(key))); } } diff --git a/src/main/java/com/commercetools/sync/types/TypeSync.java b/src/main/java/com/commercetools/sync/types/TypeSync.java index 29dcfc28db..34a2cf2be6 100644 --- a/src/main/java/com/commercetools/sync/types/TypeSync.java +++ b/src/main/java/com/commercetools/sync/types/TypeSync.java @@ -4,8 +4,8 @@ import com.commercetools.sync.services.TypeService; import com.commercetools.sync.services.impl.TypeServiceImpl; import com.commercetools.sync.types.helpers.TypeSyncStatistics; +import com.commercetools.sync.types.utils.TypeSyncUtils; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; -import io.sphere.sdk.client.ConcurrentModificationException; import io.sphere.sdk.commands.UpdateAction; import io.sphere.sdk.types.Type; import io.sphere.sdk.types.TypeDraft; @@ -15,12 +15,12 @@ import javax.annotation.Nullable; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; import static com.commercetools.sync.commons.utils.SyncUtils.batchElements; -import static com.commercetools.sync.types.utils.TypeSyncUtils.buildActions; import static java.lang.String.format; import static java.util.Optional.ofNullable; import static java.util.concurrent.CompletableFuture.completedFuture; @@ -34,18 +34,15 @@ */ public class TypeSync extends BaseSync { - private static final String CTP_TYPE_FETCH_FAILED = "Failed to fetch existing types of keys '%s'."; + private static final String CTP_TYPE_FETCH_FAILED = "Failed to fetch existing types with keys: '%s'."; private static final String CTP_TYPE_UPDATE_FAILED = "Failed to update type with key: '%s'. Reason: %s" ; - private static final String CTP_TYPE_CREATE_FAILED = "Failed to create type of key '%s'."; private static final String TYPE_DRAFT_HAS_NO_KEY = "Failed to process type draft without key."; private static final String TYPE_DRAFT_IS_NULL = "Failed to process null type draft."; - private static final String FETCH_ON_RETRY = "Failed to fetch type on retry."; private final TypeService typeService; public TypeSync(@Nonnull final TypeSyncOptions typeSyncOptions) { - super(new TypeSyncStatistics(), typeSyncOptions); - this.typeService = new TypeServiceImpl(typeSyncOptions); + this(typeSyncOptions, new TypeServiceImpl(typeSyncOptions)); } /** @@ -60,7 +57,7 @@ public TypeSync(@Nonnull final TypeSyncOptions typeSyncOptions) { * @param typeService the type service which is responsible for fetching/caching the Types from the CTP * project. */ - TypeSync(@Nonnull final TypeSyncOptions typeSyncOptions, @Nonnull final TypeService typeService) { + TypeSync(@Nonnull final TypeSyncOptions typeSyncOptions, @Nonnull final TypeService typeService) { super(new TypeSyncStatistics(), typeSyncOptions); this.typeService = typeService; } @@ -120,7 +117,7 @@ protected CompletionStage processBatch(@Nonnull final List syncBatch( @Nonnull final Set oldTypes, @Nonnull final Set newTypes) { @@ -187,102 +185,133 @@ private CompletionStage syncBatch( final Type oldType = oldTypeMap.get(newType.getKey()); return ofNullable(oldType) - .map(type -> updateType(oldType, newType)) - .orElseGet(() -> createType(newType)); + .map(type -> buildActionsAndUpdate(oldType, newType)) + .orElseGet(() -> applyCallbackAndCreate(newType)); }) .map(CompletionStage::toCompletableFuture) .toArray(CompletableFuture[]::new)); } /** - * Given a type draft, issues a request to the CTP project to create a corresponding Type. - * - *

The {@code statistics} instance is updated accordingly to whether the CTP request was carried - * out successfully or not. If an exception was thrown on executing the request to CTP, the error handling method - * is called. + * Given a type draft, this method applies the beforeCreateCallback and then issues a create request to the + * CTP project to create the corresponding Type. * * @param typeDraft the type draft to create the type from. * @return a {@link CompletionStage} which contains an empty result after execution of the create. */ - private CompletionStage createType(@Nonnull final TypeDraft typeDraft) { - return syncOptions.applyBeforeCreateCallBack(typeDraft) - .map(typeService::createType) - .map(creationFuture -> creationFuture - .thenAccept(createdType -> statistics.incrementCreated()) - .exceptionally(exception -> { - final String errorMessage = format(CTP_TYPE_CREATE_FAILED, - typeDraft.getKey()); - handleError(errorMessage, exception, 1); - - return null; - })) - .orElseGet(() -> CompletableFuture.completedFuture(null)); + @SuppressFBWarnings("NP_NONNULL_PARAM_VIOLATION") // https://github.com/findbugsproject/findbugs/issues/79 + @Nonnull + private CompletionStage> applyCallbackAndCreate( + @Nonnull final TypeDraft typeDraft) { + + return syncOptions + .applyBeforeCreateCallBack(typeDraft) + .map(draft -> typeService + .createType(draft) + .thenApply(typeOptional -> { + if (typeOptional.isPresent()) { + statistics.incrementCreated(); + } else { + statistics.incrementFailed(); + } + return typeOptional; + }) + ) + .orElse(CompletableFuture.completedFuture(Optional.empty())); + } + + @SuppressFBWarnings("NP_NONNULL_PARAM_VIOLATION") // https://github.com/findbugsproject/findbugs/issues/79 + @Nonnull + private CompletionStage> buildActionsAndUpdate( + @Nonnull final Type oldType, + @Nonnull final TypeDraft newType) { + + final List> updateActions = TypeSyncUtils.buildActions(oldType, newType, syncOptions); + + final List> updateActionsAfterCallback = + syncOptions.applyBeforeUpdateCallBack(updateActions, newType, oldType); + + if (!updateActionsAfterCallback.isEmpty()) { + return updateType(oldType, newType, updateActionsAfterCallback); + } + + return completedFuture(null); } /** * Given an existing {@link Type} and a new {@link TypeDraft}, the method calculates all the * update actions required to synchronize the existing type to be the same as the new one. If there are * update actions found, a request is made to CTP to update the existing type, otherwise it doesn't issue a - * request. If the update request failed due to a {@link ConcurrentModificationException}, the method recalculates - * the update actions required for syncing the {@link Type} and reissues the update request. + * request. * *

The {@code statistics} instance is updated accordingly to whether the CTP request was carried - * out successfully or not. If an exception was thrown on executing the request to CTP, - * the optional error callback specified in the {@code syncOptions} is called. + * out successfully or not. If an exception was thrown on executing the request to CTP,the error handling method + * is called. * - * @param oldType existing type that could be updated. - * @param newType draft containing data that could differ from data in {@code oldType}. + * @param oldType existing type that could be updated. + * @param newType draft containing data that could differ from data in {@code oldType}. + * @param updateActions the update actions to update the {@link Type} with. * @return a {@link CompletionStage} which contains an empty result after execution of the update. */ - @SuppressFBWarnings("NP_NONNULL_PARAM_VIOLATION") // https://github.com/findbugsproject/findbugs/issues/79 - private CompletionStage updateType(@Nonnull final Type oldType, - @Nonnull final TypeDraft newType) { - - final List> updateActions = buildActions(oldType, newType, syncOptions); - - final List> updateActionsAfterCallback = syncOptions.applyBeforeUpdateCallBack( - updateActions, - newType, - oldType - ); - - if (!updateActionsAfterCallback.isEmpty()) { - return typeService.updateType(oldType, updateActionsAfterCallback) - .handle((updatedType, sphereException) -> sphereException) - .thenCompose(sphereException -> { - if (sphereException != null) { - return executeSupplierIfConcurrentModificationException( - sphereException, - () -> fetchAndUpdate(oldType, newType), - () -> { - final String errorMessage = format(CTP_TYPE_UPDATE_FAILED, - newType.getKey(), sphereException); - handleError(errorMessage, sphereException, 1); - - return completedFuture(null); - }); - } else { - statistics.incrementUpdated(); - return completedFuture(null); - } - }); - } - - return completedFuture(null); + @Nonnull + private CompletionStage> updateType( + @Nonnull final Type oldType, + @Nonnull final TypeDraft newType, + @Nonnull final List> updateActions) { + + return typeService + .updateType(oldType, updateActions) + .handle(ImmutablePair::new) + .thenCompose(updateResponse -> { + final Type updatedType = updateResponse.getKey(); + final Throwable sphereException = updateResponse.getValue(); + if (sphereException != null) { + return executeSupplierIfConcurrentModificationException( + sphereException, + () -> fetchAndUpdate(oldType, newType), + () -> { + final String errorMessage = + format(CTP_TYPE_UPDATE_FAILED, newType.getKey(), + sphereException.getMessage()); + handleError(errorMessage, sphereException, 1); + return CompletableFuture.completedFuture(Optional.empty()); + }); + } else { + statistics.incrementUpdated(); + return CompletableFuture.completedFuture(Optional.of(updatedType)); + } + }); } - @SuppressFBWarnings("NP_NONNULL_PARAM_VIOLATION") - private CompletionStage fetchAndUpdate(@Nonnull final Type oldType, - @Nonnull final TypeDraft newType) { + private CompletionStage> fetchAndUpdate( + @Nonnull final Type oldType, + @Nonnull final TypeDraft newType) { final String key = oldType.getKey(); - return typeService.fetchType(key) - .thenCompose(typeOptional -> - typeOptional - .map(fetchedType -> updateType(fetchedType, newType)) - .orElseGet(() -> { - handleError(format(CTP_TYPE_UPDATE_FAILED, key, FETCH_ON_RETRY), null, 1); - return CompletableFuture.completedFuture(null); - })); + return typeService + .fetchType(key) + .handle(ImmutablePair::new) + .thenCompose(fetchResponse -> { + final Optional fetchedTypeOptional = fetchResponse.getKey(); + final Throwable exception = fetchResponse.getValue(); + + if (exception != null) { + final String errorMessage = format(CTP_TYPE_UPDATE_FAILED, key, + "Failed to fetch from CTP while retrying after concurrency modification."); + handleError(errorMessage, exception, 1); + return CompletableFuture.completedFuture(null); + } + + return fetchedTypeOptional + .map(fetchedType -> buildActionsAndUpdate(fetchedType, newType)) + .orElseGet(() -> { + final String errorMessage = + format(CTP_TYPE_UPDATE_FAILED, key, + "Not found when attempting to fetch while retrying " + + "after concurrency modification."); + handleError(errorMessage, null, 1); + return CompletableFuture.completedFuture(null); + }); + }); } } diff --git a/src/test/java/com/commercetools/sync/types/TypeSyncTest.java b/src/test/java/com/commercetools/sync/types/TypeSyncTest.java index acdb72491f..40c0fb36ed 100644 --- a/src/test/java/com/commercetools/sync/types/TypeSyncTest.java +++ b/src/test/java/com/commercetools/sync/types/TypeSyncTest.java @@ -5,22 +5,31 @@ import io.sphere.sdk.client.SphereClient; import io.sphere.sdk.models.SphereException; import io.sphere.sdk.types.ResourceTypeIdsSetBuilder; +import io.sphere.sdk.types.Type; import io.sphere.sdk.types.TypeDraft; import io.sphere.sdk.types.TypeDraftBuilder; import org.junit.Test; import java.util.ArrayList; import java.util.List; +import java.util.Optional; import java.util.concurrent.CompletionException; import static com.commercetools.sync.commons.asserts.statistics.AssertionsForStatistics.assertThat; import static io.sphere.sdk.models.LocalizedString.ofEnglish; import static java.util.Collections.emptyList; +import static java.util.Collections.emptySet; import static java.util.Collections.singleton; import static java.util.Collections.singletonList; +import static java.util.concurrent.CompletableFuture.completedFuture; import static java.util.concurrent.CompletableFuture.supplyAsync; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anySet; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; public class TypeSyncTest { @@ -63,7 +72,7 @@ public void sync_WithErrorFetchingExistingKeys_ShouldExecuteCallbackOnErrorAndIn assertThat(errorMessages) .hasSize(1) .hasOnlyOneElementSatisfying(message -> - assertThat(message).isEqualTo("Failed to fetch existing types of keys '[foo]'.") + assertThat(message).isEqualTo("Failed to fetch existing types with keys: '[foo]'.") ); assertThat(exceptions) @@ -76,4 +85,61 @@ public void sync_WithErrorFetchingExistingKeys_ShouldExecuteCallbackOnErrorAndIn assertThat(typeSyncStatistics).hasValues(1, 0, 0, 1); } + @Test + public void sync_WithOnlyDraftsToCreate_ShouldCallBeforeCreateCallback() { + // preparation + final TypeDraft newTypeDraft = TypeDraftBuilder + .of("newType", ofEnglish( "typeName"), ResourceTypeIdsSetBuilder.of().addChannels()) + .build(); + + final TypeSyncOptions typeSyncOptions = TypeSyncOptionsBuilder + .of(mock(SphereClient.class)) + .build(); + + final TypeService typeService = mock(TypeService.class); + when(typeService.fetchMatchingTypesByKeys(anySet())).thenReturn(completedFuture(emptySet())); + when(typeService.createType(any())).thenReturn(completedFuture(Optional.empty())); + + final TypeSyncOptions spyTypeSyncOptions = spy(typeSyncOptions); + + // test + new TypeSync(spyTypeSyncOptions, typeService) + .sync(singletonList(newTypeDraft)).toCompletableFuture().join(); + + // assertion + verify(spyTypeSyncOptions).applyBeforeCreateCallBack(newTypeDraft); + verify(spyTypeSyncOptions, never()).applyBeforeUpdateCallBack(any(), any(), any()); + } + + @Test + public void sync_WithOnlyDraftsToUpdate_ShouldOnlyCallBeforeUpdateCallback() { + // preparation + final TypeDraft newTypeDraft = TypeDraftBuilder + .of("newType", ofEnglish( "typeName"), ResourceTypeIdsSetBuilder.of().addChannels()) + .build(); + + final TypeSyncOptions typeSyncOptions = TypeSyncOptionsBuilder + .of(mock(SphereClient.class)) + .build(); + + + + final Type mockedExistingType = mock(Type.class); + when(mockedExistingType.getKey()).thenReturn(newTypeDraft.getKey()); + + final TypeService typeService = mock(TypeService.class); + when(typeService.fetchMatchingTypesByKeys(anySet())).thenReturn(completedFuture(singleton(mockedExistingType))); + when(typeService.updateType(any(), any())).thenReturn(completedFuture(mockedExistingType)); + + final TypeSyncOptions spyTypeSyncOptions = spy(typeSyncOptions); + + // test + new TypeSync(spyTypeSyncOptions, typeService) + .sync(singletonList(newTypeDraft)).toCompletableFuture().join(); + + // assertion + verify(spyTypeSyncOptions).applyBeforeUpdateCallBack(any(), any(), any()); + verify(spyTypeSyncOptions, never()).applyBeforeCreateCallBack(newTypeDraft); + } + } From ff6d6f70b7af0cbb8b1f55a41b922bbaa18b6cab Mon Sep 17 00:00:00 2001 From: aoz Date: Wed, 5 Dec 2018 20:08:53 +0100 Subject: [PATCH 110/164] #300 fix benchmarks for type and product type --- .../com/commercetools/sync/benchmark/BenchmarkUtils.java | 4 ++-- .../sync/benchmark/ProductTypeSyncBenchmark.java | 5 +++-- .../com/commercetools/sync/benchmark/TypeSyncBenchmark.java | 3 ++- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/benchmark/java/com/commercetools/sync/benchmark/BenchmarkUtils.java b/src/benchmark/java/com/commercetools/sync/benchmark/BenchmarkUtils.java index 30a1ca6c40..027686ca48 100644 --- a/src/benchmark/java/com/commercetools/sync/benchmark/BenchmarkUtils.java +++ b/src/benchmark/java/com/commercetools/sync/benchmark/BenchmarkUtils.java @@ -108,9 +108,9 @@ private static double calculateAvg(@Nonnull final List results) { } private static ObjectNode createVersionNode(@Nonnull final ObjectNode rootNode, @Nonnull final String version) { - final ObjectNode newVersionNode = createSyncNode( + final ObjectNode newVersionNode = createSyncNode(createSyncNode(createSyncNode( createSyncNode(createSyncNode(JsonNodeFactory.instance.objectNode(), - PRODUCT_SYNC), INVENTORY_SYNC), CATEGORY_SYNC); + PRODUCT_SYNC), INVENTORY_SYNC), CATEGORY_SYNC), PRODUCT_TYPE_SYNC), TYPE_SYNC); return (ObjectNode) rootNode.set(version, newVersionNode); } diff --git a/src/benchmark/java/com/commercetools/sync/benchmark/ProductTypeSyncBenchmark.java b/src/benchmark/java/com/commercetools/sync/benchmark/ProductTypeSyncBenchmark.java index 2a4e55d586..b951974cf9 100644 --- a/src/benchmark/java/com/commercetools/sync/benchmark/ProductTypeSyncBenchmark.java +++ b/src/benchmark/java/com/commercetools/sync/benchmark/ProductTypeSyncBenchmark.java @@ -28,6 +28,7 @@ import java.util.stream.Collectors; import java.util.stream.IntStream; +import static com.commercetools.sync.benchmark.BenchmarkUtils.CREATES_AND_UPDATES; import static com.commercetools.sync.benchmark.BenchmarkUtils.CREATES_ONLY; import static com.commercetools.sync.benchmark.BenchmarkUtils.NUMBER_OF_RESOURCE_UNDER_TEST; import static com.commercetools.sync.benchmark.BenchmarkUtils.PRODUCT_TYPE_SYNC; @@ -242,7 +243,7 @@ public void sync_WithSomeExistingProductTypes_ShouldSyncProductTypes() throws IO assertThat(errorCallBackMessages).isEmpty(); assertThat(warningCallBackMessages).isEmpty(); - saveNewResult(SyncSolutionInfo.LIB_VERSION, PRODUCT_TYPE_SYNC, UPDATES_ONLY, totalTime); + saveNewResult(SyncSolutionInfo.LIB_VERSION, PRODUCT_TYPE_SYNC, CREATES_AND_UPDATES, totalTime); } @@ -275,4 +276,4 @@ private static ProductTypeDraftBuilder applyAttributeDefinitionNameChange( return builder.attributes(list); } -} \ No newline at end of file +} diff --git a/src/benchmark/java/com/commercetools/sync/benchmark/TypeSyncBenchmark.java b/src/benchmark/java/com/commercetools/sync/benchmark/TypeSyncBenchmark.java index f9f9d956e0..be2dbadf5c 100644 --- a/src/benchmark/java/com/commercetools/sync/benchmark/TypeSyncBenchmark.java +++ b/src/benchmark/java/com/commercetools/sync/benchmark/TypeSyncBenchmark.java @@ -30,6 +30,7 @@ import java.util.stream.Collectors; import java.util.stream.IntStream; +import static com.commercetools.sync.benchmark.BenchmarkUtils.CREATES_AND_UPDATES; import static com.commercetools.sync.benchmark.BenchmarkUtils.CREATES_ONLY; import static com.commercetools.sync.benchmark.BenchmarkUtils.NUMBER_OF_RESOURCE_UNDER_TEST; import static com.commercetools.sync.benchmark.BenchmarkUtils.THRESHOLD; @@ -250,7 +251,7 @@ public void sync_WithSomeExistingTypes_ShouldSyncTypes() throws IOException { assertThat(errorCallBackMessages).isEmpty(); assertThat(warningCallBackMessages).isEmpty(); - saveNewResult(SyncSolutionInfo.LIB_VERSION, TYPE_SYNC, UPDATES_ONLY, totalTime); + saveNewResult(SyncSolutionInfo.LIB_VERSION, TYPE_SYNC, CREATES_AND_UPDATES, totalTime); } @Nonnull From 25f9a5f0d0e13dae4ff7c7a8bc6fb238dc7d5b60 Mon Sep 17 00:00:00 2001 From: Hesham Massoud Date: Wed, 5 Dec 2018 22:13:36 +0100 Subject: [PATCH 111/164] #300: Remove empty space. --- docs/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/README.md b/docs/README.md index 671fe7ce45..ce8c24b878 100644 --- a/docs/README.md +++ b/docs/README.md @@ -30,7 +30,7 @@ Currently this library supports synchronising the following entities in commerce - [InventoryEntries](usage/INVENTORY_SYNC.md) - [ProductTypes](usage/PRODUCT_TYPE_SYNC.md) - [Types](usage/TYPE_SYNC.md) - + ![commercetools-java-sync-final 001](https://user-images.githubusercontent.com/9512131/31230702-0f2255a6-a9e5-11e7-9412-04ed52641dde.png) From 76e44736532e4083b4ad7e04bdcf769694dcf568 Mon Sep 17 00:00:00 2001 From: Hesham Massoud Date: Wed, 5 Dec 2018 22:14:14 +0100 Subject: [PATCH 112/164] #300: Statically import assertion utils. --- .../benchmark/ProductTypeSyncBenchmark.java | 14 ++++--------- .../sync/benchmark/TypeSyncBenchmark.java | 20 +++++++------------ 2 files changed, 11 insertions(+), 23 deletions(-) diff --git a/src/benchmark/java/com/commercetools/sync/benchmark/ProductTypeSyncBenchmark.java b/src/benchmark/java/com/commercetools/sync/benchmark/ProductTypeSyncBenchmark.java index b951974cf9..65d363b308 100644 --- a/src/benchmark/java/com/commercetools/sync/benchmark/ProductTypeSyncBenchmark.java +++ b/src/benchmark/java/com/commercetools/sync/benchmark/ProductTypeSyncBenchmark.java @@ -1,6 +1,5 @@ package com.commercetools.sync.benchmark; -import com.commercetools.sync.commons.asserts.statistics.AssertionsForStatistics; import com.commercetools.sync.commons.utils.SyncSolutionInfo; import com.commercetools.sync.producttypes.ProductTypeSync; import com.commercetools.sync.producttypes.ProductTypeSyncOptions; @@ -36,6 +35,7 @@ import static com.commercetools.sync.benchmark.BenchmarkUtils.UPDATES_ONLY; import static com.commercetools.sync.benchmark.BenchmarkUtils.calculateDiff; import static com.commercetools.sync.benchmark.BenchmarkUtils.saveNewResult; +import static com.commercetools.sync.commons.asserts.statistics.AssertionsForStatistics.assertThat; import static com.commercetools.sync.integration.commons.utils.ProductTypeITUtils.ATTRIBUTE_DEFINITION_DRAFT_1; import static com.commercetools.sync.integration.commons.utils.ProductTypeITUtils.deleteProductTypes; import static com.commercetools.sync.integration.commons.utils.SphereClientUtils.CTP_TARGET_CLIENT; @@ -111,9 +111,7 @@ public void sync_NewProductTypes_ShouldCreateProductTypes() throws IOException { assertThat(totalNumberOfProductTypes).isCompletedWithValue(NUMBER_OF_RESOURCE_UNDER_TEST); - AssertionsForStatistics - .assertThat(syncStatistics) - .hasValues(NUMBER_OF_RESOURCE_UNDER_TEST, NUMBER_OF_RESOURCE_UNDER_TEST, 0, 0); + assertThat(syncStatistics).hasValues(NUMBER_OF_RESOURCE_UNDER_TEST, NUMBER_OF_RESOURCE_UNDER_TEST, 0, 0); assertThat(errorCallBackExceptions).isEmpty(); assertThat(errorCallBackMessages).isEmpty(); assertThat(warningCallBackMessages).isEmpty(); @@ -172,9 +170,7 @@ public void sync_ExistingProductTypes_ShouldUpdateProductTypes() throws IOExcept assertThat(totalNumberOfProductTypes).isCompletedWithValue(NUMBER_OF_RESOURCE_UNDER_TEST); // Assert statistics - AssertionsForStatistics - .assertThat(syncStatistics) - .hasValues(NUMBER_OF_RESOURCE_UNDER_TEST, 0, NUMBER_OF_RESOURCE_UNDER_TEST, 0); + assertThat(syncStatistics).hasValues(NUMBER_OF_RESOURCE_UNDER_TEST, 0, NUMBER_OF_RESOURCE_UNDER_TEST, 0); assertThat(errorCallBackExceptions).isEmpty(); assertThat(errorCallBackMessages).isEmpty(); @@ -235,9 +231,7 @@ public void sync_WithSomeExistingProductTypes_ShouldSyncProductTypes() throws IO assertThat(totalNumberOfProductTypes).isCompletedWithValue(NUMBER_OF_RESOURCE_UNDER_TEST); // Assert statistics - AssertionsForStatistics - .assertThat(syncStatistics) - .hasValues(NUMBER_OF_RESOURCE_UNDER_TEST, halfNumberOfDrafts, halfNumberOfDrafts, 0); + assertThat(syncStatistics).hasValues(NUMBER_OF_RESOURCE_UNDER_TEST, halfNumberOfDrafts, halfNumberOfDrafts, 0); assertThat(errorCallBackExceptions).isEmpty(); assertThat(errorCallBackMessages).isEmpty(); diff --git a/src/benchmark/java/com/commercetools/sync/benchmark/TypeSyncBenchmark.java b/src/benchmark/java/com/commercetools/sync/benchmark/TypeSyncBenchmark.java index be2dbadf5c..23400b64d7 100644 --- a/src/benchmark/java/com/commercetools/sync/benchmark/TypeSyncBenchmark.java +++ b/src/benchmark/java/com/commercetools/sync/benchmark/TypeSyncBenchmark.java @@ -1,6 +1,5 @@ package com.commercetools.sync.benchmark; -import com.commercetools.sync.commons.asserts.statistics.AssertionsForStatistics; import com.commercetools.sync.commons.utils.SyncSolutionInfo; import com.commercetools.sync.types.TypeSync; import com.commercetools.sync.types.TypeSyncOptions; @@ -38,6 +37,7 @@ import static com.commercetools.sync.benchmark.BenchmarkUtils.UPDATES_ONLY; import static com.commercetools.sync.benchmark.BenchmarkUtils.calculateDiff; import static com.commercetools.sync.benchmark.BenchmarkUtils.saveNewResult; +import static com.commercetools.sync.commons.asserts.statistics.AssertionsForStatistics.assertThat; import static com.commercetools.sync.integration.commons.utils.ITUtils.deleteTypesFromTargetAndSource; import static com.commercetools.sync.integration.commons.utils.SphereClientUtils.CTP_TARGET_CLIENT; import static com.commercetools.sync.integration.commons.utils.TypeITUtils.FIELD_DEFINITION_1; @@ -120,9 +120,7 @@ public void sync_NewTypes_ShouldCreateTypes() throws IOException { assertThat(totalNumberOfTypes).isCompletedWithValue(NUMBER_OF_RESOURCE_UNDER_TEST); - AssertionsForStatistics - .assertThat(syncStatistics) - .hasValues(NUMBER_OF_RESOURCE_UNDER_TEST, NUMBER_OF_RESOURCE_UNDER_TEST, 0, 0); + assertThat(syncStatistics).hasValues(NUMBER_OF_RESOURCE_UNDER_TEST, NUMBER_OF_RESOURCE_UNDER_TEST, 0, 0); assertThat(errorCallBackExceptions).isEmpty(); assertThat(errorCallBackMessages).isEmpty(); assertThat(warningCallBackMessages).isEmpty(); @@ -180,9 +178,7 @@ public void sync_ExistingTypes_ShouldUpdateTypes() throws IOException { assertThat(totalNumberOfTypes).isCompletedWithValue(NUMBER_OF_RESOURCE_UNDER_TEST); // Assert statistics - AssertionsForStatistics - .assertThat(syncStatistics) - .hasValues(NUMBER_OF_RESOURCE_UNDER_TEST, 0, NUMBER_OF_RESOURCE_UNDER_TEST, 0); + assertThat(syncStatistics).hasValues(NUMBER_OF_RESOURCE_UNDER_TEST, 0, NUMBER_OF_RESOURCE_UNDER_TEST, 0); assertThat(errorCallBackExceptions).isEmpty(); assertThat(errorCallBackMessages).isEmpty(); @@ -222,7 +218,7 @@ public void sync_WithSomeExistingTypes_ShouldSyncTypes() throws IOException { .isLessThanOrEqualTo(THRESHOLD); // Assert actual state of CTP project (number of updated types) - final CompletableFuture totalNumberOfUpdatedTypesWithOldFielDefinitionName = + final CompletableFuture totalNumberOfUpdatedTypesWithOldFieldDefinitionName = CTP_TARGET_CLIENT.execute(TypeQuery.of() .withPredicates(p -> p.fieldDefinitions().name().is( FIELD_DEFINITION_NAME_1 + "_old"))) @@ -230,8 +226,8 @@ public void sync_WithSomeExistingTypes_ShouldSyncTypes() throws IOException { .thenApply(Long::intValue) .toCompletableFuture(); - executeBlocking(totalNumberOfUpdatedTypesWithOldFielDefinitionName); - assertThat(totalNumberOfUpdatedTypesWithOldFielDefinitionName).isCompletedWithValue(0); + executeBlocking(totalNumberOfUpdatedTypesWithOldFieldDefinitionName); + assertThat(totalNumberOfUpdatedTypesWithOldFieldDefinitionName).isCompletedWithValue(0); // Assert actual state of CTP project (total number of existing types) final CompletableFuture totalNumberOfTypes = @@ -243,9 +239,7 @@ public void sync_WithSomeExistingTypes_ShouldSyncTypes() throws IOException { assertThat(totalNumberOfTypes).isCompletedWithValue(NUMBER_OF_RESOURCE_UNDER_TEST); // Assert statistics - AssertionsForStatistics - .assertThat(syncStatistics) - .hasValues(NUMBER_OF_RESOURCE_UNDER_TEST, halfNumberOfDrafts, halfNumberOfDrafts, 0); + assertThat(syncStatistics).hasValues(NUMBER_OF_RESOURCE_UNDER_TEST, halfNumberOfDrafts, halfNumberOfDrafts, 0); assertThat(errorCallBackExceptions).isEmpty(); assertThat(errorCallBackMessages).isEmpty(); From ddf7e89255e8f9140ac14721d8a4fc591c449a78 Mon Sep 17 00:00:00 2001 From: Hesham Massoud Date: Wed, 5 Dec 2018 22:17:56 +0100 Subject: [PATCH 113/164] #300: Remove release notes for non exposed methods. --- docs/RELEASE_NOTES.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/RELEASE_NOTES.md b/docs/RELEASE_NOTES.md index ced7211dd6..4ebf20b402 100644 --- a/docs/RELEASE_NOTES.md +++ b/docs/RELEASE_NOTES.md @@ -43,8 +43,6 @@ - **Type Sync** - Exposed `TypeUpdateActionUtils` which contains utils for calculating needed update actions after comparing individual fields of a `Type` and a `TypeDraft`. [#300](https://github.com/commercetools/commercetools-sync-java/issues/300) - **Type Sync** - Exposed `LocalizedEnumValueUpdateActionUtils` which contains utils for calculating needed update actions after comparing two lists of `LocalizedEnumValue`s. [#300](https://github.com/commercetools/commercetools-sync-java/issues/300) - **Type Sync** - Exposed `PlainEnumValueUpdateActionUtils` which contains utils for calculating needed update actions after comparing two lists of `EnumValue`s. [#300](https://github.com/commercetools/commercetools-sync-java/issues/300) - - **Type Sync** - Exposed `FieldDefinitionsUpdateActionUtils` which contains utils for calculating needed update actions after comparing a list of `FieldDefinition`s and a list of `FieldDefinition`s. [#300](https://github.com/commercetools/commercetools-sync-java/issues/300) - - **Type Sync** - Exposed `FieldDefinitionUpdateActionUtils` which contains utils for calculating needed update actions after comparing a `FieldDefinition` and a `FieldDefinition`. [#300](https://github.com/commercetools/commercetools-sync-java/issues/300) - **Commons** - Exposed `EnumValuesUpdateActionUtils` which contains generic utilities for calculating needed update actions after comparing two lists of `EnumValue`s or `LocalizedEnumValue`s. [#300](https://github.com/commercetools/commercetools-sync-java/issues/300) - 📋 **Documentation** (4) From 291c5dcac0e540b0f48510092023278452dd9566 Mon Sep 17 00:00:00 2001 From: Hesham Massoud Date: Wed, 5 Dec 2018 22:18:56 +0100 Subject: [PATCH 114/164] #300: Reword release note entries. --- docs/RELEASE_NOTES.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/RELEASE_NOTES.md b/docs/RELEASE_NOTES.md index 4ebf20b402..06436efe16 100644 --- a/docs/RELEASE_NOTES.md +++ b/docs/RELEASE_NOTES.md @@ -58,7 +58,7 @@ - **Product Sync** - Reference keys are not validated if they are in UUID format anymore. [#166](https://github.com/commercetools/commercetools-sync-java/issues/166) - **Category Sync** - Reference keys are not validated if they are in UUID format anymore. [#166](https://github.com/commercetools/commercetools-sync-java/issues/166) - **Inventory Sync** - Reference keys are not validated if they are in UUID format anymore. [#166](https://github.com/commercetools/commercetools-sync-java/issues/166) - - **ProductType Sync** - Added `ProductTypeSyncBenchmark` to benchmark the product type sync, to be able to compare the performance of the sync with the future releases. [#301](https://github.com/commercetools/commercetools-sync-java/issues/301) + - **ProductType Sync** - Added benchmarks for the `productType` sync to be able to compare the performance of the sync with the future releases. [#301](https://github.com/commercetools/commercetools-sync-java/issues/301) - **Commons** - Bumped commercetools-jvm-sdk to version [1.37.0](http://commercetools.github.io/commercetools-jvm-sdk/apidocs/io/sphere/sdk/meta/ReleaseNotes.html#v1_37_0). - **Commons** - Bumped `mockito` to 2.23.4. - **Commons** - Bumped `com.adarshr.test-logger` to 1.6.0. @@ -69,7 +69,7 @@ - **Commons** - Bumped `com.adarshr.test-logger` to 1.6.0. - **Commons** - Bumped `org.ajoberstar.grgit` to 3.0.0. - **Commons** - Bumped gradle to version [gradle-5.0](https://docs.gradle.org/5.0/release-notes.html) - - **Type Sync** - Added `TypeSyncBenchmark` to benchmark the type sync, to be able to compare the performance of the sync with the future releases. [#300](https://github.com/commercetools/commercetools-sync-java/issues/300) + - **Type Sync** - Added benchmarks for the `type` sync to be able to compare the performance of the sync with the future releases. [#300](https://github.com/commercetools/commercetools-sync-java/issues/300) - 🚧 **Breaking Changes** (11) - **Product Sync** - `allowUuid` option is now removed. [#166](https://github.com/commercetools/commercetools-sync-java/issues/166) From c87687bd3ffac25ddf45514801e1f11ed4d3342c Mon Sep 17 00:00:00 2001 From: Hesham Massoud Date: Wed, 5 Dec 2018 22:34:23 +0100 Subject: [PATCH 115/164] #300: Fix bug in calculating diff of avg in benchmarks. --- .../commercetools/sync/benchmark/ProductSyncBenchmark.java | 4 ++-- .../sync/benchmark/ProductTypeSyncBenchmark.java | 6 ++++-- .../com/commercetools/sync/benchmark/TypeSyncBenchmark.java | 4 ++-- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/benchmark/java/com/commercetools/sync/benchmark/ProductSyncBenchmark.java b/src/benchmark/java/com/commercetools/sync/benchmark/ProductSyncBenchmark.java index 3c25cc6b3a..d58b154363 100644 --- a/src/benchmark/java/com/commercetools/sync/benchmark/ProductSyncBenchmark.java +++ b/src/benchmark/java/com/commercetools/sync/benchmark/ProductSyncBenchmark.java @@ -158,7 +158,7 @@ public void sync_ExistingProducts_ShouldUpdateProducts() throws IOException { // Calculate time taken for benchmark and assert it lies within threshold - final double diff = calculateDiff(SyncSolutionInfo.LIB_VERSION, PRODUCT_SYNC, CREATES_ONLY, totalTime); + final double diff = calculateDiff(SyncSolutionInfo.LIB_VERSION, PRODUCT_SYNC, UPDATES_ONLY, totalTime); assertThat(diff) .withFailMessage(format("Diff of benchmark '%e' is longer than expected threshold of '%d'.", diff, THRESHOLD)) @@ -222,7 +222,7 @@ public void sync_WithSomeExistingProducts_ShouldSyncProducts() throws IOExceptio // Calculate time taken for benchmark and assert it lies within threshold - final double diff = calculateDiff(SyncSolutionInfo.LIB_VERSION, PRODUCT_SYNC, CREATES_ONLY, totalTime); + final double diff = calculateDiff(SyncSolutionInfo.LIB_VERSION, PRODUCT_SYNC, CREATES_AND_UPDATES, totalTime); assertThat(diff) .withFailMessage(format("Diff of benchmark '%e' is longer than expected threshold of '%d'.", diff, THRESHOLD)) diff --git a/src/benchmark/java/com/commercetools/sync/benchmark/ProductTypeSyncBenchmark.java b/src/benchmark/java/com/commercetools/sync/benchmark/ProductTypeSyncBenchmark.java index 65d363b308..66bc9d61b3 100644 --- a/src/benchmark/java/com/commercetools/sync/benchmark/ProductTypeSyncBenchmark.java +++ b/src/benchmark/java/com/commercetools/sync/benchmark/ProductTypeSyncBenchmark.java @@ -142,7 +142,7 @@ public void sync_ExistingProductTypes_ShouldUpdateProductTypes() throws IOExcept final long totalTime = System.currentTimeMillis() - beforeSyncTime; // Calculate time taken for benchmark and assert it lies within threshold - final double diff = calculateDiff(SyncSolutionInfo.LIB_VERSION, PRODUCT_TYPE_SYNC, CREATES_ONLY, totalTime); + final double diff = calculateDiff(SyncSolutionInfo.LIB_VERSION, PRODUCT_TYPE_SYNC, UPDATES_ONLY, totalTime); assertThat(diff) .withFailMessage(format("Diff of benchmark '%e' is longer than expected threshold of '%d'.", diff, THRESHOLD)) @@ -203,7 +203,9 @@ public void sync_WithSomeExistingProductTypes_ShouldSyncProductTypes() throws IO final long totalTime = System.currentTimeMillis() - beforeSyncTime; // Calculate time taken for benchmark and assert it lies within threshold - final double diff = calculateDiff(SyncSolutionInfo.LIB_VERSION, PRODUCT_TYPE_SYNC, CREATES_ONLY, totalTime); + final double diff = + calculateDiff(SyncSolutionInfo.LIB_VERSION, PRODUCT_TYPE_SYNC, CREATES_AND_UPDATES, totalTime); + assertThat(diff) .withFailMessage(format("Diff of benchmark '%e' is longer than expected threshold of '%d'.", diff, THRESHOLD)) diff --git a/src/benchmark/java/com/commercetools/sync/benchmark/TypeSyncBenchmark.java b/src/benchmark/java/com/commercetools/sync/benchmark/TypeSyncBenchmark.java index 23400b64d7..425424a380 100644 --- a/src/benchmark/java/com/commercetools/sync/benchmark/TypeSyncBenchmark.java +++ b/src/benchmark/java/com/commercetools/sync/benchmark/TypeSyncBenchmark.java @@ -150,7 +150,7 @@ public void sync_ExistingTypes_ShouldUpdateTypes() throws IOException { final long totalTime = System.currentTimeMillis() - beforeSyncTime; // Calculate time taken for benchmark and assert it lies within threshold - final double diff = calculateDiff(SyncSolutionInfo.LIB_VERSION, TYPE_SYNC, CREATES_ONLY, totalTime); + final double diff = calculateDiff(SyncSolutionInfo.LIB_VERSION, TYPE_SYNC, UPDATES_ONLY, totalTime); assertThat(diff) .withFailMessage(format("Diff of benchmark '%e' is longer than expected threshold of '%d'.", diff, THRESHOLD)) @@ -211,7 +211,7 @@ public void sync_WithSomeExistingTypes_ShouldSyncTypes() throws IOException { final long totalTime = System.currentTimeMillis() - beforeSyncTime; // Calculate time taken for benchmark and assert it lies within threshold - final double diff = calculateDiff(SyncSolutionInfo.LIB_VERSION, TYPE_SYNC, CREATES_ONLY, totalTime); + final double diff = calculateDiff(SyncSolutionInfo.LIB_VERSION, TYPE_SYNC, CREATES_AND_UPDATES, totalTime); assertThat(diff) .withFailMessage(format("Diff of benchmark '%e' is longer than expected threshold of '%d'.", diff, THRESHOLD)) From 1dd8d7ece45d4081e98b131c62cc28f5d8ccb94f Mon Sep 17 00:00:00 2001 From: Hesham Massoud Date: Wed, 5 Dec 2018 22:35:17 +0100 Subject: [PATCH 116/164] #300: Delete types only from target. --- .../com/commercetools/sync/benchmark/TypeSyncBenchmark.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/benchmark/java/com/commercetools/sync/benchmark/TypeSyncBenchmark.java b/src/benchmark/java/com/commercetools/sync/benchmark/TypeSyncBenchmark.java index 425424a380..719838d1e0 100644 --- a/src/benchmark/java/com/commercetools/sync/benchmark/TypeSyncBenchmark.java +++ b/src/benchmark/java/com/commercetools/sync/benchmark/TypeSyncBenchmark.java @@ -38,7 +38,6 @@ import static com.commercetools.sync.benchmark.BenchmarkUtils.calculateDiff; import static com.commercetools.sync.benchmark.BenchmarkUtils.saveNewResult; import static com.commercetools.sync.commons.asserts.statistics.AssertionsForStatistics.assertThat; -import static com.commercetools.sync.integration.commons.utils.ITUtils.deleteTypesFromTargetAndSource; import static com.commercetools.sync.integration.commons.utils.SphereClientUtils.CTP_TARGET_CLIENT; import static com.commercetools.sync.integration.commons.utils.TypeITUtils.FIELD_DEFINITION_1; import static com.commercetools.sync.integration.commons.utils.TypeITUtils.FIELD_DEFINITION_NAME_1; @@ -57,7 +56,7 @@ public class TypeSyncBenchmark { @BeforeClass public static void setup() { - deleteTypesFromTargetAndSource(); + deleteTypes(CTP_TARGET_CLIENT); } @AfterClass From d29bfcd99cb691058bce4ae162c621fccbad90e6 Mon Sep 17 00:00:00 2001 From: Hesham Massoud Date: Wed, 5 Dec 2018 22:38:22 +0100 Subject: [PATCH 117/164] #300: Making unneeded public fields private. --- .../commons/utils/TypeITUtils.java | 35 +++++++++---------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/src/integration-test/java/com/commercetools/sync/integration/commons/utils/TypeITUtils.java b/src/integration-test/java/com/commercetools/sync/integration/commons/utils/TypeITUtils.java index 294d0af38c..b057380f71 100644 --- a/src/integration-test/java/com/commercetools/sync/integration/commons/utils/TypeITUtils.java +++ b/src/integration-test/java/com/commercetools/sync/integration/commons/utils/TypeITUtils.java @@ -31,13 +31,13 @@ public final class TypeITUtils { public static final LocalizedString TYPE_NAME_2 = LocalizedString.ofEnglish("name_2"); public static final String FIELD_DEFINITION_NAME_1 = "field_name_1"; - public static final String FIELD_DEFINITION_NAME_2 = "field_name_2"; - public static final String FIELD_DEFINITION_NAME_3 = "field_name_3"; + private static final String FIELD_DEFINITION_NAME_2 = "field_name_2"; + private static final String FIELD_DEFINITION_NAME_3 = "field_name_3"; public static final LocalizedString FIELD_DEFINITION_LABEL_1 = LocalizedString.ofEnglish("label_1"); - public static final LocalizedString FIELD_DEFINITION_LABEL_2 = LocalizedString.ofEnglish("label_2"); - public static final LocalizedString FIELD_DEFINITION_LABEL_3 = LocalizedString.ofEnglish("label_3"); + private static final LocalizedString FIELD_DEFINITION_LABEL_2 = LocalizedString.ofEnglish("label_2"); + private static final LocalizedString FIELD_DEFINITION_LABEL_3 = LocalizedString.ofEnglish("label_3"); public static final LocalizedString TYPE_DESCRIPTION_1 = LocalizedString.ofEnglish("description_1"); public static final LocalizedString TYPE_DESCRIPTION_2 = LocalizedString.ofEnglish("description_2"); @@ -65,23 +65,21 @@ public final class TypeITUtils { - public static final TypeDraft typeDraft1 = TypeDraftBuilder.of( - TYPE_KEY_1, - TYPE_NAME_1, - ResourceTypeIdsSetBuilder.of().addCategories().build()) - .description(TYPE_DESCRIPTION_1) - .fieldDefinitions(Arrays.asList(FIELD_DEFINITION_1, FIELD_DEFINITION_2)) - .build(); + private static final TypeDraft typeDraft1 = TypeDraftBuilder + .of(TYPE_KEY_1, + TYPE_NAME_1, + ResourceTypeIdsSetBuilder.of().addCategories().build()) + .description(TYPE_DESCRIPTION_1) + .fieldDefinitions(Arrays.asList(FIELD_DEFINITION_1, FIELD_DEFINITION_2)) + .build(); - public static final TypeDraft typeDraft2 = TypeDraftBuilder.of( - TYPE_KEY_2, + private static final TypeDraft typeDraft2 = TypeDraftBuilder + .of(TYPE_KEY_2, TYPE_NAME_2, ResourceTypeIdsSetBuilder.of().addCategories().build()) - .description(TYPE_DESCRIPTION_2) - .fieldDefinitions(singletonList(FIELD_DEFINITION_2)) - .build(); - - + .description(TYPE_DESCRIPTION_2) + .fieldDefinitions(singletonList(FIELD_DEFINITION_2)) + .build(); /** * Deletes all types from CTP project, represented by provided {@code ctpClient}. @@ -92,7 +90,6 @@ public static void deleteTypes(@Nonnull final SphereClient ctpClient) { queryAndExecute(ctpClient, TypeQuery.of(), TypeDeleteCommand::of); } - /** * Deletes all types from CTP projects defined by {@code CTP_SOURCE_CLIENT} and * {@code CTP_TARGET_CLIENT}. From c13e21a3b893f4b4f9ad3a6ed29503c395932f0b Mon Sep 17 00:00:00 2001 From: Hesham Massoud Date: Wed, 5 Dec 2018 22:39:21 +0100 Subject: [PATCH 118/164] #300: Adjust line spacing. --- .../sync/integration/commons/utils/TypeITUtils.java | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/integration-test/java/com/commercetools/sync/integration/commons/utils/TypeITUtils.java b/src/integration-test/java/com/commercetools/sync/integration/commons/utils/TypeITUtils.java index b057380f71..77f1c9b767 100644 --- a/src/integration-test/java/com/commercetools/sync/integration/commons/utils/TypeITUtils.java +++ b/src/integration-test/java/com/commercetools/sync/integration/commons/utils/TypeITUtils.java @@ -34,7 +34,6 @@ public final class TypeITUtils { private static final String FIELD_DEFINITION_NAME_2 = "field_name_2"; private static final String FIELD_DEFINITION_NAME_3 = "field_name_3"; - public static final LocalizedString FIELD_DEFINITION_LABEL_1 = LocalizedString.ofEnglish("label_1"); private static final LocalizedString FIELD_DEFINITION_LABEL_2 = LocalizedString.ofEnglish("label_2"); private static final LocalizedString FIELD_DEFINITION_LABEL_3 = LocalizedString.ofEnglish("label_3"); @@ -48,14 +47,12 @@ public final class TypeITUtils { FIELD_DEFINITION_LABEL_1, true, TextInputHint.SINGLE_LINE); - public static final FieldDefinition FIELD_DEFINITION_2 = FieldDefinition.of( StringFieldType.of(), FIELD_DEFINITION_NAME_2, FIELD_DEFINITION_LABEL_2, true, TextInputHint.SINGLE_LINE); - public static final FieldDefinition FIELD_DEFINITION_3 = FieldDefinition.of( StringFieldType.of(), FIELD_DEFINITION_NAME_3, @@ -63,8 +60,6 @@ public final class TypeITUtils { true, TextInputHint.SINGLE_LINE); - - private static final TypeDraft typeDraft1 = TypeDraftBuilder .of(TYPE_KEY_1, TYPE_NAME_1, @@ -72,7 +67,6 @@ public final class TypeITUtils { .description(TYPE_DESCRIPTION_1) .fieldDefinitions(Arrays.asList(FIELD_DEFINITION_1, FIELD_DEFINITION_2)) .build(); - private static final TypeDraft typeDraft2 = TypeDraftBuilder .of(TYPE_KEY_2, TYPE_NAME_2, From 903c0ca67eabefadc5d7a90598bd4c20f01863ee Mon Sep 17 00:00:00 2001 From: Hesham Massoud Date: Wed, 5 Dec 2018 22:45:40 +0100 Subject: [PATCH 119/164] #300: Use simpler name update. --- .../ctpprojectsource/producttypes/ProductTypeSyncIT.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/integration-test/java/com/commercetools/sync/integration/ctpprojectsource/producttypes/ProductTypeSyncIT.java b/src/integration-test/java/com/commercetools/sync/integration/ctpprojectsource/producttypes/ProductTypeSyncIT.java index 5df4fcadf7..650e4e7db7 100644 --- a/src/integration-test/java/com/commercetools/sync/integration/ctpprojectsource/producttypes/ProductTypeSyncIT.java +++ b/src/integration-test/java/com/commercetools/sync/integration/ctpprojectsource/producttypes/ProductTypeSyncIT.java @@ -108,7 +108,7 @@ public void sync_WithUpdates_ShouldReturnProperStatistics() { return ProductTypeDraftBuilder .of( productType.getKey(), - productType.getName() + "_updated", // name updated + "newName", productType.getDescription(), attributeDefinitionDrafts) .build(); From 7efd69d1508658509d36ee146d627e3210424a77 Mon Sep 17 00:00:00 2001 From: Hesham Massoud Date: Wed, 5 Dec 2018 23:09:45 +0100 Subject: [PATCH 120/164] #300: Fix Javadoc. --- .../sync/integration/externalsource/types/TypeSyncIT.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/integration-test/java/com/commercetools/sync/integration/externalsource/types/TypeSyncIT.java b/src/integration-test/java/com/commercetools/sync/integration/externalsource/types/TypeSyncIT.java index de18fd858b..ed6bd91a38 100644 --- a/src/integration-test/java/com/commercetools/sync/integration/externalsource/types/TypeSyncIT.java +++ b/src/integration-test/java/com/commercetools/sync/integration/externalsource/types/TypeSyncIT.java @@ -67,8 +67,8 @@ public class TypeSyncIT { /** - * Deletes types from source and target CTP projects. - * Populates source and target CTP projects with test data. + * Deletes types from the target CTP projects. + * Populates the target CTP project with test data. */ @Before public void setup() { From 9fdb6652cf1165ec7e01ef278c62b75e0774166a Mon Sep 17 00:00:00 2001 From: Hesham Massoud Date: Wed, 5 Dec 2018 23:10:05 +0100 Subject: [PATCH 121/164] #300: Fix line spacing. --- .../sync/integration/externalsource/types/TypeSyncIT.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/integration-test/java/com/commercetools/sync/integration/externalsource/types/TypeSyncIT.java b/src/integration-test/java/com/commercetools/sync/integration/externalsource/types/TypeSyncIT.java index ed6bd91a38..6d4f46058a 100644 --- a/src/integration-test/java/com/commercetools/sync/integration/externalsource/types/TypeSyncIT.java +++ b/src/integration-test/java/com/commercetools/sync/integration/externalsource/types/TypeSyncIT.java @@ -731,11 +731,11 @@ public void sync__WithConcurrentModificationExceptionAndUnexpectedDelete_ShouldF assertThat(errorMessages.get(0)).contains( format("Failed to update type with key: '%s'. Reason: Not found when attempting to fetch while " + "retrying after concurrency modification.", typeDraft.getKey())); - } @Nonnull private SphereClient buildClientWithConcurrentModificationUpdateAndNotFoundFetchOnRetry() { + final SphereClient spyClient = spy(CTP_TARGET_CLIENT); final TypeQuery anyTypeQuery = any(TypeQuery.class); @@ -743,14 +743,12 @@ private SphereClient buildClientWithConcurrentModificationUpdateAndNotFoundFetch .thenCallRealMethod() // Call real fetch on fetching matching types .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - final TypeUpdateCommand anyTypeUpdate = any(TypeUpdateCommand.class); when(spyClient.execute(anyTypeUpdate)) .thenReturn(exceptionallyCompletedFuture(new ConcurrentModificationException())) .thenCallRealMethod(); - return spyClient; } From f7f8cc1d79663e45d6cb10093cfcd66666e038c1 Mon Sep 17 00:00:00 2001 From: Hesham Massoud Date: Wed, 5 Dec 2018 23:10:13 +0100 Subject: [PATCH 122/164] #300: Fix Javadoc. --- .../externalsource/producttypes/ProductTypeSyncIT.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/integration-test/java/com/commercetools/sync/integration/externalsource/producttypes/ProductTypeSyncIT.java b/src/integration-test/java/com/commercetools/sync/integration/externalsource/producttypes/ProductTypeSyncIT.java index 2a682031be..ad26d1c08c 100644 --- a/src/integration-test/java/com/commercetools/sync/integration/externalsource/producttypes/ProductTypeSyncIT.java +++ b/src/integration-test/java/com/commercetools/sync/integration/externalsource/producttypes/ProductTypeSyncIT.java @@ -69,7 +69,7 @@ public class ProductTypeSyncIT { /** - * Deletes product types from target CTP project. + * Deletes product types from the target CTP project. * Populates target CTP project with test data. */ @Before From 9d66f49596a6aa30b6135e9eedc3548120274935 Mon Sep 17 00:00:00 2001 From: Hesham Massoud Date: Wed, 5 Dec 2018 23:10:44 +0100 Subject: [PATCH 123/164] #300: Remove braces for 1-liner. --- .../integration/externalsource/types/TypeSyncIT.java | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/integration-test/java/com/commercetools/sync/integration/externalsource/types/TypeSyncIT.java b/src/integration-test/java/com/commercetools/sync/integration/externalsource/types/TypeSyncIT.java index 6d4f46058a..178c226df4 100644 --- a/src/integration-test/java/com/commercetools/sync/integration/externalsource/types/TypeSyncIT.java +++ b/src/integration-test/java/com/commercetools/sync/integration/externalsource/types/TypeSyncIT.java @@ -191,14 +191,9 @@ public void sync_WithUpdatedType_WithNewFieldDefinitions_ShouldUpdateTypeAddingF final Optional oldTypeAfter = getTypeByKey(CTP_TARGET_CLIENT, TYPE_KEY_1); - assertThat(oldTypeAfter).hasValueSatisfying(type -> { + assertThat(oldTypeAfter).hasValueSatisfying(type -> assertFieldDefinitionsAreEqual(type.getFieldDefinitions(), - asList( - FIELD_DEFINITION_1, - FIELD_DEFINITION_2, - FIELD_DEFINITION_3 - )); - }); + asList(FIELD_DEFINITION_1, FIELD_DEFINITION_2, FIELD_DEFINITION_3))); } @Test From 1154d69002e9ccbc099e76c4047d549c116955a8 Mon Sep 17 00:00:00 2001 From: Hesham Massoud Date: Wed, 5 Dec 2018 23:10:59 +0100 Subject: [PATCH 124/164] #300: Use optional assertion. --- .../sync/integration/externalsource/types/TypeSyncIT.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/integration-test/java/com/commercetools/sync/integration/externalsource/types/TypeSyncIT.java b/src/integration-test/java/com/commercetools/sync/integration/externalsource/types/TypeSyncIT.java index 178c226df4..2edc8a52f3 100644 --- a/src/integration-test/java/com/commercetools/sync/integration/externalsource/types/TypeSyncIT.java +++ b/src/integration-test/java/com/commercetools/sync/integration/externalsource/types/TypeSyncIT.java @@ -223,9 +223,8 @@ public void sync_WithUpdatedType_WithoutOldFieldDefinition_ShouldUpdateTypeRemov final Optional oldTypeAfter = getTypeByKey(CTP_TARGET_CLIENT, TYPE_KEY_1); - assertThat(oldTypeAfter).isNotEmpty(); - assertFieldDefinitionsAreEqual(oldTypeAfter.get().getFieldDefinitions(), - singletonList(FIELD_DEFINITION_1)); + assertThat(oldTypeAfter).hasValueSatisfying(type -> + assertFieldDefinitionsAreEqual(type.getFieldDefinitions(), singletonList(FIELD_DEFINITION_1))); } @Test From 32d00af95638292cb81fca9517a0056c0f4e6367 Mon Sep 17 00:00:00 2001 From: Hesham Massoud Date: Wed, 5 Dec 2018 23:11:31 +0100 Subject: [PATCH 125/164] #300: Remove unneeded variable reassignment. --- .../sync/integration/externalsource/types/TypeSyncIT.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/integration-test/java/com/commercetools/sync/integration/externalsource/types/TypeSyncIT.java b/src/integration-test/java/com/commercetools/sync/integration/externalsource/types/TypeSyncIT.java index 2edc8a52f3..e7d23fb7a5 100644 --- a/src/integration-test/java/com/commercetools/sync/integration/externalsource/types/TypeSyncIT.java +++ b/src/integration-test/java/com/commercetools/sync/integration/externalsource/types/TypeSyncIT.java @@ -574,15 +574,14 @@ public void sync_WithConcurrentModificationException_ShouldRetryToUpdateNewTypeW final SphereClient spyClient = buildClientWithConcurrentModificationUpdate(); final TypeDraft typeDraft = TypeDraftBuilder - .of("typeKey", LocalizedString.ofEnglish( "typeName"), ResourceTypeIdsSetBuilder.of().addChannels()) + .of(TYPE_KEY_2, TYPE_NAME_2, ResourceTypeIdsSetBuilder.of().addChannels()) .build(); CTP_TARGET_CLIENT.execute(TypeCreateCommand.of(typeDraft)) .toCompletableFuture() .join(); - final LocalizedString newTypeName = TYPE_NAME_2; - final TypeDraft updatedDraft = TypeDraftBuilder.of(typeDraft).name(newTypeName).build(); + final TypeDraft updatedDraft = TypeDraftBuilder.of(typeDraft).name(TYPE_NAME_1).build(); final TypeSyncOptions typeSyncOptions = TypeSyncOptionsBuilder.of(spyClient).build(); final TypeSync typeSync = new TypeSync(typeSyncOptions); @@ -603,7 +602,7 @@ public void sync_WithConcurrentModificationException_ShouldRetryToUpdateNewTypeW .join(); assertThat(queryResult.head()).hasValueSatisfying(type -> - assertThat(type.getName()).isEqualTo(newTypeName)); + assertThat(type.getName()).isEqualTo(TYPE_NAME_1)); } @Nonnull From 7fe0b60fd37c24295d4019f0573977c19254e50c Mon Sep 17 00:00:00 2001 From: Hesham Massoud Date: Wed, 5 Dec 2018 23:47:16 +0100 Subject: [PATCH 126/164] #300: Move method and remove key check since it's required by CTP. --- .../sync/services/impl/TypeServiceImpl.java | 31 ++++++------------- 1 file changed, 10 insertions(+), 21 deletions(-) diff --git a/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java b/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java index 978e373398..21c16f678b 100644 --- a/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java +++ b/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java @@ -22,9 +22,7 @@ import java.util.function.Consumer; import java.util.function.Function; -import static java.lang.String.format; import static java.util.stream.Collectors.toSet; -import static org.apache.commons.lang3.StringUtils.isNotBlank; import static org.apache.http.util.TextUtils.isBlank; /** @@ -48,6 +46,16 @@ public CompletionStage> fetchCachedTypeId(@Nonnull final String return fetchAndCache(key); } + @Nonnull + private CompletionStage> fetchAndCache(@Nonnull final String key) { + + final Consumer> typePageConsumer = typePage -> + typePage.forEach(type -> keyToIdCache.put(type.getKey(), type.getId())); + + return CtpQueryUtils.queryAll(syncOptions.getCtpClient(), TypeQuery.of(), typePageConsumer) + .thenApply(result -> Optional.ofNullable(keyToIdCache.get(key))); + } + @Nonnull @Override public CompletionStage> fetchMatchingTypesByKeys(@Nonnull final Set keys) { @@ -104,23 +112,4 @@ public CompletionStage updateType(@Nonnull final Type type, return updateResource(type, TypeUpdateCommand::of, updateActions); } - - @Nonnull - private CompletionStage> fetchAndCache(@Nonnull final String key) { - - final Consumer> typePageConsumer = typePage -> - typePage.forEach(type -> { - final String fetchedTypeKey = type.getKey(); - final String id = type.getId(); - if (isNotBlank(fetchedTypeKey)) { - keyToIdCache.put(fetchedTypeKey, id); - } else { - syncOptions.applyWarningCallback(format("Type with id: '%s' has no key set. Keys are" - + " required for type matching.", id)); - } - }); - - return CtpQueryUtils.queryAll(syncOptions.getCtpClient(), TypeQuery.of(), typePageConsumer) - .thenApply(result -> Optional.ofNullable(keyToIdCache.get(key))); - } } From 28ed07adccde222d963eb719306e3523759b62da Mon Sep 17 00:00:00 2001 From: Hesham Massoud Date: Wed, 5 Dec 2018 23:47:34 +0100 Subject: [PATCH 127/164] #300: Remove unneeded variable and format method sig. --- .../commercetools/sync/services/impl/TypeServiceImpl.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java b/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java index 21c16f678b..77d96a7901 100644 --- a/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java +++ b/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java @@ -30,7 +30,6 @@ * TODO: USE graphQL to get only keys. GITHUB ISSUE#84 */ public final class TypeServiceImpl extends BaseService implements TypeService { - private static final String FETCH_FAILED = "Failed to fetch types with keys: '%s'. Reason: %s"; public TypeServiceImpl(@Nonnull final BaseSyncOptions syncOptions) { super(syncOptions); @@ -107,9 +106,9 @@ public CompletionStage> createType(@Nonnull final TypeDraft typeD @Nonnull @Override - public CompletionStage updateType(@Nonnull final Type type, - @Nonnull final List> updateActions) { - + public CompletionStage updateType( + @Nonnull final Type type, + @Nonnull final List> updateActions) { return updateResource(type, TypeUpdateCommand::of, updateActions); } } From 94e90198eef1f900decbb8862fa3a8b1e1853110 Mon Sep 17 00:00:00 2001 From: Hesham Massoud Date: Wed, 5 Dec 2018 23:47:53 +0100 Subject: [PATCH 128/164] #300: Fix method names. --- .../sync/services/impl/ProductTypeServiceImplTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/java/com/commercetools/sync/services/impl/ProductTypeServiceImplTest.java b/src/test/java/com/commercetools/sync/services/impl/ProductTypeServiceImplTest.java index b727aa35ed..8b007be167 100644 --- a/src/test/java/com/commercetools/sync/services/impl/ProductTypeServiceImplTest.java +++ b/src/test/java/com/commercetools/sync/services/impl/ProductTypeServiceImplTest.java @@ -23,7 +23,7 @@ public class ProductTypeServiceImplTest { @Test - public void fetchProductType_WithEmptyKey_ShouldNotFetchCategory() { + public void fetchProductType_WithEmptyKey_ShouldNotFetchProductType() { // preparation final SphereClient sphereClient = mock(SphereClient.class); final ProductTypeSyncOptions syncOptions = ProductTypeSyncOptionsBuilder @@ -40,7 +40,7 @@ public void fetchProductType_WithEmptyKey_ShouldNotFetchCategory() { } @Test - public void fetchProductType_WithNullKey_ShouldNotFetchCategory() { + public void fetchProductType_WithNullKey_ShouldNotFetchProductType() { // preparation final SphereClient sphereClient = mock(SphereClient.class); final ProductTypeSyncOptions syncOptions = ProductTypeSyncOptionsBuilder From f5895cdc7d92be74b13d510a81135f0872e71e0b Mon Sep 17 00:00:00 2001 From: Hesham Massoud Date: Wed, 5 Dec 2018 23:48:34 +0100 Subject: [PATCH 129/164] #300: Statically import, improve indentation and remove unneeded assertion. --- .../services/impl/TypeServiceImplIT.java | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/integration-test/java/com/commercetools/sync/integration/services/impl/TypeServiceImplIT.java b/src/integration-test/java/com/commercetools/sync/integration/services/impl/TypeServiceImplIT.java index e2572c9f7f..94f6d6bb53 100644 --- a/src/integration-test/java/com/commercetools/sync/integration/services/impl/TypeServiceImplIT.java +++ b/src/integration-test/java/com/commercetools/sync/integration/services/impl/TypeServiceImplIT.java @@ -8,7 +8,6 @@ import io.sphere.sdk.client.BadGatewayException; import io.sphere.sdk.client.ErrorResponseException; import io.sphere.sdk.client.SphereClient; -import io.sphere.sdk.models.LocalizedString; import io.sphere.sdk.models.errors.DuplicateFieldError; import io.sphere.sdk.types.ResourceTypeIdsSetBuilder; import io.sphere.sdk.types.Type; @@ -39,6 +38,7 @@ import static com.commercetools.sync.integration.commons.utils.TypeITUtils.TYPE_NAME_1; import static com.commercetools.sync.integration.commons.utils.TypeITUtils.deleteTypes; import static com.commercetools.tests.utils.CompletionStageUtil.executeBlocking; +import static io.sphere.sdk.models.LocalizedString.ofEnglish; import static java.util.Collections.singleton; import static java.util.Collections.singletonList; import static java.util.stream.Collectors.toList; @@ -69,12 +69,13 @@ public void setup() { deleteTypes(CTP_TARGET_CLIENT); createCategoriesCustomType(OLD_TYPE_KEY, OLD_TYPE_LOCALE, OLD_TYPE_NAME, CTP_TARGET_CLIENT); - final TypeSyncOptions typeSyncOptions = TypeSyncOptionsBuilder.of(CTP_TARGET_CLIENT) - .errorCallback((errorMessage, exception) -> { - errorCallBackMessages.add(errorMessage); - errorCallBackExceptions.add(exception); - }) - .build(); + final TypeSyncOptions typeSyncOptions = TypeSyncOptionsBuilder + .of(CTP_TARGET_CLIENT) + .errorCallback((errorMessage, exception) -> { + errorCallBackMessages.add(errorMessage); + errorCallBackExceptions.add(exception); + }) + .build(); typeService = new TypeServiceImpl(typeSyncOptions); } @@ -142,7 +143,6 @@ public void fetchMatchingTypesByKeys_WithAnyExistingKeys_ShouldReturnASetOfTypes .toCompletableFuture() .join(); - assertThat(matchingTypes).isNotEmpty(); assertThat(matchingTypes).hasSize(1); assertThat(errorCallBackExceptions).isEmpty(); assertThat(errorCallBackMessages).isEmpty(); @@ -344,7 +344,7 @@ public void updateType_WithValidChanges_ShouldUpdateTypeCorrectly() { .toCompletableFuture().join().head(); assertThat(typeOptional).isNotNull(); - final ChangeName changeNameUpdateAction = ChangeName.of(LocalizedString.ofEnglish("new_type_name")); + final ChangeName changeNameUpdateAction = ChangeName.of(ofEnglish("new_type_name")); final Type updatedType = typeService.updateType(typeOptional.get(), singletonList(changeNameUpdateAction)) .toCompletableFuture().join(); From 98c7b509c398b826c2a4fb524b0ea86dc5e38a12 Mon Sep 17 00:00:00 2001 From: Hesham Massoud Date: Wed, 5 Dec 2018 23:50:09 +0100 Subject: [PATCH 130/164] #300: Fix typos. --- .../sync/integration/services/impl/TypeServiceImplIT.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/integration-test/java/com/commercetools/sync/integration/services/impl/TypeServiceImplIT.java b/src/integration-test/java/com/commercetools/sync/integration/services/impl/TypeServiceImplIT.java index 94f6d6bb53..f129354267 100644 --- a/src/integration-test/java/com/commercetools/sync/integration/services/impl/TypeServiceImplIT.java +++ b/src/integration-test/java/com/commercetools/sync/integration/services/impl/TypeServiceImplIT.java @@ -180,9 +180,9 @@ public void fetchMatchingTypesByKeys_WithBadGateWayExceptionAlways_ShouldFail() @Test public void fetchMatchingTypesByKeys_WithAllExistingSetOfKeys_ShouldCacheFetchedTypeIds() { - final Set fetchedProductTypes = typeService.fetchMatchingTypesByKeys(singleton(OLD_TYPE_KEY)) - .toCompletableFuture().join(); - assertThat(fetchedProductTypes).hasSize(1); + final Set fetchedTypes = typeService.fetchMatchingTypesByKeys(singleton(OLD_TYPE_KEY)) + .toCompletableFuture().join(); + assertThat(fetchedTypes).hasSize(1); final Optional typeOptional = CTP_TARGET_CLIENT .execute(TypeQuery.of().withPredicates(queryModel -> queryModel.key().is(OLD_TYPE_KEY))) @@ -288,7 +288,7 @@ public void createType_WithInvalidType_ShouldHaveEmptyOptionalAsAResult() { } @Test - public void createProductType_WithDuplicateKey_ShouldHaveEmptyOptionalAsAResult() { + public void createType_WithDuplicateKey_ShouldHaveEmptyOptionalAsAResult() { //preparation final TypeDraft newTypeDraft = TypeDraftBuilder.of( OLD_TYPE_KEY, From f5da17e837d85e34b1fabdde4d6232361141daf7 Mon Sep 17 00:00:00 2001 From: Hesham Massoud Date: Thu, 6 Dec 2018 09:11:44 +0100 Subject: [PATCH 131/164] #300: Remove redundant supressions. --- .../com/commercetools/sync/producttypes/ProductTypeSync.java | 1 - src/main/java/com/commercetools/sync/types/TypeSync.java | 1 - 2 files changed, 2 deletions(-) diff --git a/src/main/java/com/commercetools/sync/producttypes/ProductTypeSync.java b/src/main/java/com/commercetools/sync/producttypes/ProductTypeSync.java index 608124d674..549ded85aa 100644 --- a/src/main/java/com/commercetools/sync/producttypes/ProductTypeSync.java +++ b/src/main/java/com/commercetools/sync/producttypes/ProductTypeSync.java @@ -208,7 +208,6 @@ private CompletionStage syncBatch( * @param productTypeDraft the product type draft to create the product type from. * @return a {@link CompletionStage} which contains an empty result after execution of the create. */ - @SuppressFBWarnings("NP_NONNULL_PARAM_VIOLATION") // https://github.com/findbugsproject/findbugs/issues/79 @Nonnull private CompletionStage> applyCallbackAndCreate( @Nonnull final ProductTypeDraft productTypeDraft) { diff --git a/src/main/java/com/commercetools/sync/types/TypeSync.java b/src/main/java/com/commercetools/sync/types/TypeSync.java index 34a2cf2be6..25f9153c3c 100644 --- a/src/main/java/com/commercetools/sync/types/TypeSync.java +++ b/src/main/java/com/commercetools/sync/types/TypeSync.java @@ -199,7 +199,6 @@ private CompletionStage syncBatch( * @param typeDraft the type draft to create the type from. * @return a {@link CompletionStage} which contains an empty result after execution of the create. */ - @SuppressFBWarnings("NP_NONNULL_PARAM_VIOLATION") // https://github.com/findbugsproject/findbugs/issues/79 @Nonnull private CompletionStage> applyCallbackAndCreate( @Nonnull final TypeDraft typeDraft) { From 7d7706788c5da862d89309e8880967adf4cbd37d Mon Sep 17 00:00:00 2001 From: Hesham Massoud Date: Thu, 6 Dec 2018 09:12:06 +0100 Subject: [PATCH 132/164] #300: Add @nonnull annotation. --- src/main/java/com/commercetools/sync/types/TypeSync.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/com/commercetools/sync/types/TypeSync.java b/src/main/java/com/commercetools/sync/types/TypeSync.java index 25f9153c3c..7bb0edd11b 100644 --- a/src/main/java/com/commercetools/sync/types/TypeSync.java +++ b/src/main/java/com/commercetools/sync/types/TypeSync.java @@ -282,6 +282,7 @@ private CompletionStage> updateType( }); } + @Nonnull private CompletionStage> fetchAndUpdate( @Nonnull final Type oldType, @Nonnull final TypeDraft newType) { From 694042f1f2e0fece9854def4e014be50b79c4be2 Mon Sep 17 00:00:00 2001 From: Hesham Massoud Date: Thu, 6 Dec 2018 09:12:18 +0100 Subject: [PATCH 133/164] #300: Statically import util. --- src/main/java/com/commercetools/sync/types/TypeSync.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/commercetools/sync/types/TypeSync.java b/src/main/java/com/commercetools/sync/types/TypeSync.java index 7bb0edd11b..b4af838414 100644 --- a/src/main/java/com/commercetools/sync/types/TypeSync.java +++ b/src/main/java/com/commercetools/sync/types/TypeSync.java @@ -4,7 +4,6 @@ import com.commercetools.sync.services.TypeService; import com.commercetools.sync.services.impl.TypeServiceImpl; import com.commercetools.sync.types.helpers.TypeSyncStatistics; -import com.commercetools.sync.types.utils.TypeSyncUtils; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import io.sphere.sdk.commands.UpdateAction; import io.sphere.sdk.types.Type; @@ -21,6 +20,7 @@ import java.util.concurrent.CompletionStage; import static com.commercetools.sync.commons.utils.SyncUtils.batchElements; +import static com.commercetools.sync.types.utils.TypeSyncUtils.buildActions; import static java.lang.String.format; import static java.util.Optional.ofNullable; import static java.util.concurrent.CompletableFuture.completedFuture; @@ -225,7 +225,7 @@ private CompletionStage> buildActionsAndUpdate( @Nonnull final Type oldType, @Nonnull final TypeDraft newType) { - final List> updateActions = TypeSyncUtils.buildActions(oldType, newType, syncOptions); + final List> updateActions = buildActions(oldType, newType, syncOptions); final List> updateActionsAfterCallback = syncOptions.applyBeforeUpdateCallBack(updateActions, newType, oldType); From 9057af91bc0f53b3d0fd494ec77442658a7e012d Mon Sep 17 00:00:00 2001 From: Hesham Massoud Date: Thu, 6 Dec 2018 09:15:24 +0100 Subject: [PATCH 134/164] #300: Remove excessive line spacing. --- src/test/java/com/commercetools/sync/types/TypeSyncTest.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/test/java/com/commercetools/sync/types/TypeSyncTest.java b/src/test/java/com/commercetools/sync/types/TypeSyncTest.java index 40c0fb36ed..455da3671c 100644 --- a/src/test/java/com/commercetools/sync/types/TypeSyncTest.java +++ b/src/test/java/com/commercetools/sync/types/TypeSyncTest.java @@ -122,8 +122,6 @@ public void sync_WithOnlyDraftsToUpdate_ShouldOnlyCallBeforeUpdateCallback() { .of(mock(SphereClient.class)) .build(); - - final Type mockedExistingType = mock(Type.class); when(mockedExistingType.getKey()).thenReturn(newTypeDraft.getKey()); From a08da1f01d14ec734ad67ff2bad9bdd1728e2b0c Mon Sep 17 00:00:00 2001 From: Hesham Massoud Date: Thu, 6 Dec 2018 09:25:48 +0100 Subject: [PATCH 135/164] #300: Refactor TypeSyncUtils. --- .../sync/types/utils/TypeSyncUtils.java | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/commercetools/sync/types/utils/TypeSyncUtils.java b/src/main/java/com/commercetools/sync/types/utils/TypeSyncUtils.java index 2d67248522..272c663ebd 100644 --- a/src/main/java/com/commercetools/sync/types/utils/TypeSyncUtils.java +++ b/src/main/java/com/commercetools/sync/types/utils/TypeSyncUtils.java @@ -7,13 +7,11 @@ import javax.annotation.Nonnull; import java.util.List; -import java.util.Optional; -import java.util.stream.Stream; +import static com.commercetools.sync.commons.utils.OptionalUtils.filterEmptyOptionals; import static com.commercetools.sync.types.utils.TypeUpdateActionUtils.buildChangeNameUpdateAction; import static com.commercetools.sync.types.utils.TypeUpdateActionUtils.buildFieldDefinitionUpdateActions; import static com.commercetools.sync.types.utils.TypeUpdateActionUtils.buildSetDescriptionUpdateAction; -import static java.util.stream.Collectors.toList; public final class TypeSyncUtils { @@ -26,7 +24,12 @@ public final class TypeSyncUtils { * {@link TypeDraft} have the same fields, an empty {@link List} is returned. * *

- * TODO: Check GITHUB ISSUE#339 for missing FieldDefinition update actions. + * Note: Currently this util doesn't support the following: + *

    + *
  • updating the inputHint of a FieldDefinition
  • + *
  • removing the EnumValue/LocalizedEnumValue of a FieldDefinition
  • + *
  • updating the label of a EnumValue/LocalizedEnumValue of a FieldDefinition
  • + *
*

* * @param oldType the {@link Type} which should be updated. @@ -43,13 +46,11 @@ public static List> buildActions( @Nonnull final TypeDraft newType, @Nonnull final TypeSyncOptions syncOptions) { - final List> updateActions = Stream.of( + final List> updateActions = + filterEmptyOptionals( buildChangeNameUpdateAction(oldType, newType), buildSetDescriptionUpdateAction(oldType, newType) - ) - .filter(Optional::isPresent) - .map(Optional::get) - .collect(toList()); + ); updateActions.addAll(buildFieldDefinitionUpdateActions(oldType, newType, syncOptions)); From f9d086f8dc9ca9bd5ec410849c84c1374030b55d Mon Sep 17 00:00:00 2001 From: Hesham Massoud Date: Thu, 6 Dec 2018 09:31:41 +0100 Subject: [PATCH 136/164] #300: Refactor util name. --- .../sync/types/utils/TypeSyncUtils.java | 6 ++-- .../types/utils/TypeUpdateActionUtils.java | 7 ++-- ...BuildFieldDefinitionUpdateActionsTest.java | 32 +++++++++---------- 3 files changed, 23 insertions(+), 22 deletions(-) diff --git a/src/main/java/com/commercetools/sync/types/utils/TypeSyncUtils.java b/src/main/java/com/commercetools/sync/types/utils/TypeSyncUtils.java index 272c663ebd..e5a9361a85 100644 --- a/src/main/java/com/commercetools/sync/types/utils/TypeSyncUtils.java +++ b/src/main/java/com/commercetools/sync/types/utils/TypeSyncUtils.java @@ -10,14 +10,14 @@ import static com.commercetools.sync.commons.utils.OptionalUtils.filterEmptyOptionals; import static com.commercetools.sync.types.utils.TypeUpdateActionUtils.buildChangeNameUpdateAction; -import static com.commercetools.sync.types.utils.TypeUpdateActionUtils.buildFieldDefinitionUpdateActions; +import static com.commercetools.sync.types.utils.TypeUpdateActionUtils.buildFieldDefinitionsUpdateActions; import static com.commercetools.sync.types.utils.TypeUpdateActionUtils.buildSetDescriptionUpdateAction; public final class TypeSyncUtils { /** * Compares all the fields (including the field definitions see - * {@link TypeUpdateActionUtils#buildFieldDefinitionUpdateActions(Type, TypeDraft, TypeSyncOptions)}) + * {@link TypeUpdateActionUtils#buildFieldDefinitionsUpdateActions(Type, TypeDraft, TypeSyncOptions)}) * of a {@link Type} and a {@link TypeDraft}. * It returns a {@link List} of {@link UpdateAction}<{@link Type}> as a * result. If no update actions are needed, for example in case where both the {@link Type} and the @@ -52,7 +52,7 @@ public static List> buildActions( buildSetDescriptionUpdateAction(oldType, newType) ); - updateActions.addAll(buildFieldDefinitionUpdateActions(oldType, newType, syncOptions)); + updateActions.addAll(buildFieldDefinitionsUpdateActions(oldType, newType, syncOptions)); return updateActions; } diff --git a/src/main/java/com/commercetools/sync/types/utils/TypeUpdateActionUtils.java b/src/main/java/com/commercetools/sync/types/utils/TypeUpdateActionUtils.java index 05750410b6..7897420c7d 100644 --- a/src/main/java/com/commercetools/sync/types/utils/TypeUpdateActionUtils.java +++ b/src/main/java/com/commercetools/sync/types/utils/TypeUpdateActionUtils.java @@ -15,7 +15,6 @@ import java.util.Optional; import static com.commercetools.sync.commons.utils.CommonTypeUpdateActionUtils.buildUpdateAction; -import static com.commercetools.sync.types.utils.FieldDefinitionsUpdateActionUtils.buildFieldDefinitionsUpdateActions; import static java.lang.String.format; import static java.util.Collections.emptyList; @@ -53,6 +52,7 @@ public static Optional> buildChangeNameUpdateAction( public static Optional> buildSetDescriptionUpdateAction( @Nonnull final Type oldType, @Nonnull final TypeDraft newType) { + return buildUpdateAction(oldType.getDescription(), newType.getDescription(), () -> SetDescription.of(newType.getDescription())); } @@ -75,12 +75,13 @@ public static Optional> buildSetDescriptionUpdateAction( * @return A list with the update actions or an empty list if the field definitions are identical. */ @Nonnull - public static List> buildFieldDefinitionUpdateActions( + public static List> buildFieldDefinitionsUpdateActions( @Nonnull final Type oldType, @Nonnull final TypeDraft newType, @Nonnull final TypeSyncOptions syncOptions) { + try { - return buildFieldDefinitionsUpdateActions( + return FieldDefinitionsUpdateActionUtils.buildFieldDefinitionsUpdateActions( oldType.getFieldDefinitions(), newType.getFieldDefinitions() ); diff --git a/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildFieldDefinitionUpdateActionsTest.java b/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildFieldDefinitionUpdateActionsTest.java index c60b5b9c36..26a7622710 100644 --- a/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildFieldDefinitionUpdateActionsTest.java +++ b/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildFieldDefinitionUpdateActionsTest.java @@ -25,7 +25,7 @@ import static com.commercetools.sync.types.FieldDefinitionTestHelper.localizedStringFieldDefinition; import static com.commercetools.sync.types.FieldDefinitionTestHelper.stringFieldDefinition; -import static com.commercetools.sync.types.utils.TypeUpdateActionUtils.buildFieldDefinitionUpdateActions; +import static com.commercetools.sync.types.utils.TypeUpdateActionUtils.buildFieldDefinitionsUpdateActions; import static io.sphere.sdk.json.SphereJsonUtils.readObjectFromResource; import static java.util.Arrays.asList; import static java.util.Collections.emptyList; @@ -98,7 +98,7 @@ public class BuildFieldDefinitionUpdateActionsTest { public void buildUpdateActions_WithNullNewFieldDefinitionsAndExistingFieldDefinitions_ShouldBuild3RemoveActions() { final Type oldType = readObjectFromResource(TYPE_WITH_FIELDS_ABC, Type.class); - final List> updateActions = buildFieldDefinitionUpdateActions( + final List> updateActions = buildFieldDefinitionsUpdateActions( oldType, TYPE_DRAFT, SYNC_OPTIONS @@ -117,7 +117,7 @@ public void buildUpdateActions_WithNullNewFieldDefinitionsAndNoOldFieldDefinitio when(oldType.getFieldDefinitions()).thenReturn(emptyList()); when(oldType.getKey()).thenReturn("type_key_1"); - final List> updateActions = buildFieldDefinitionUpdateActions( + final List> updateActions = buildFieldDefinitionsUpdateActions( oldType, TYPE_DRAFT, SYNC_OPTIONS @@ -137,7 +137,7 @@ public void buildUpdateActions_WithNewFieldDefinitionsAndNoOldFieldDefinitions_S TypeDraft.class ); - final List> updateActions = buildFieldDefinitionUpdateActions( + final List> updateActions = buildFieldDefinitionsUpdateActions( oldType, newTypeDraft, SYNC_OPTIONS @@ -159,7 +159,7 @@ public void buildUpdateActions_WithIdenticalFieldDefinitions_ShouldNotBuildUpdat TypeDraft.class ); - final List> updateActions = buildFieldDefinitionUpdateActions( + final List> updateActions = buildFieldDefinitionsUpdateActions( oldType, newTypeDraft, SYNC_OPTIONS @@ -186,7 +186,7 @@ public void buildUpdateActions_WithDuplicateFieldDefinitionNames_ShouldNotBuildA }) .build(); - final List> updateActions = buildFieldDefinitionUpdateActions( + final List> updateActions = buildFieldDefinitionsUpdateActions( oldType, newTypeDraft, syncOptions @@ -215,7 +215,7 @@ public void buildUpdateActions_WithOneMissingFieldDefinition_ShouldBuildRemoveFi TypeDraft.class ); - final List> updateActions = buildFieldDefinitionUpdateActions( + final List> updateActions = buildFieldDefinitionsUpdateActions( oldType, newTypeDraft, SYNC_OPTIONS @@ -233,7 +233,7 @@ public void buildUpdateActions_WithOneExtraFieldDefinition_ShouldBuildAddFieldDe TypeDraft.class ); - final List> updateActions = buildFieldDefinitionUpdateActions( + final List> updateActions = buildFieldDefinitionsUpdateActions( oldType, newTypeDraft, SYNC_OPTIONS @@ -253,7 +253,7 @@ public void buildUpdateActions_WithOneFieldDefinitionSwitch_ShouldBuildRemoveAnd TypeDraft.class ); - final List> updateActions = buildFieldDefinitionUpdateActions( + final List> updateActions = buildFieldDefinitionsUpdateActions( oldType, newTypeDraft, SYNC_OPTIONS @@ -274,7 +274,7 @@ public void buildUpdateActions_WithDifferent_ShouldBuildChangeFieldDefinitionOrd TypeDraft.class ); - final List> updateActions = buildFieldDefinitionUpdateActions( + final List> updateActions = buildFieldDefinitionsUpdateActions( oldType, newTypeDraft, SYNC_OPTIONS @@ -300,7 +300,7 @@ public void buildUpdateActions_WithRemovedAndDifferentOrder_ShouldBuildChangeOrd TypeDraft.class ); - final List> updateActions = buildFieldDefinitionUpdateActions( + final List> updateActions = buildFieldDefinitionsUpdateActions( oldType, newTypeDraft, SYNC_OPTIONS @@ -326,7 +326,7 @@ public void buildUpdateActions_WithAddedAndDifferentOrder_ShouldBuildChangeOrder TypeDraft.class ); - final List> updateActions = buildFieldDefinitionUpdateActions( + final List> updateActions = buildFieldDefinitionsUpdateActions( oldType, newTypeDraft, SYNC_OPTIONS @@ -354,7 +354,7 @@ public void buildUpdateActions_WithAddedFieldDefinitionInBetween_ShouldBuildChan TypeDraft.class ); - final List> updateActions = buildFieldDefinitionUpdateActions( + final List> updateActions = buildFieldDefinitionsUpdateActions( oldType, newTypeDraft, SYNC_OPTIONS @@ -382,7 +382,7 @@ public void buildUpdateActions_WithAddedRemovedAndDifOrder_ShouldBuildAllThreeMo TypeDraft.class ); - final List> updateActions = buildFieldDefinitionUpdateActions( + final List> updateActions = buildFieldDefinitionsUpdateActions( oldType, newTypeDraft, SYNC_OPTIONS @@ -408,7 +408,7 @@ public void buildUpdateActions_WithDifferentType_ShouldRemoveOldFieldDefinitionA TYPE_WITH_FIELDS_ABC_WITH_DIFFERENT_TYPE, TypeDraft.class ); - final List> updateActions = buildFieldDefinitionUpdateActions( + final List> updateActions = buildFieldDefinitionsUpdateActions( oldType, newTypeDraft, SYNC_OPTIONS @@ -447,7 +447,7 @@ public void buildUpdateActions_WithNullNewFieldDefinition_ShouldSkipNullFieldDef .build(); // test - final List> updateActions = buildFieldDefinitionUpdateActions( + final List> updateActions = buildFieldDefinitionsUpdateActions( oldType, typeDraft, SYNC_OPTIONS From cfc15828ed3c79990d2c4bcda8e03d61cd1c5c98 Mon Sep 17 00:00:00 2001 From: Hesham Massoud Date: Thu, 6 Dec 2018 09:31:59 +0100 Subject: [PATCH 137/164] #300: Fix Javadoc. --- .../sync/types/utils/TypeUpdateActionUtils.java | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/commercetools/sync/types/utils/TypeUpdateActionUtils.java b/src/main/java/com/commercetools/sync/types/utils/TypeUpdateActionUtils.java index 7897420c7d..ad64083f75 100644 --- a/src/main/java/com/commercetools/sync/types/utils/TypeUpdateActionUtils.java +++ b/src/main/java/com/commercetools/sync/types/utils/TypeUpdateActionUtils.java @@ -59,11 +59,17 @@ public static Optional> buildSetDescriptionUpdateAction( /** * Compares the field definitions of a {@link Type} and a {@link TypeDraft} and returns a list of - * {@link UpdateAction}<{@link Type}> as a result in an {@link Optional} of update action - * if values are different. In case, the new type draft has a list of field definitions in which a - * duplicate name exists, the error callback is triggered and an empty list is returned. + * {@link UpdateAction}<{@link Type}> as a result if the values are different. In case, the new type draft has + * a list of field definitions in which a duplicate name exists, the error callback is triggered and an empty list + * is returned. * *

+ * Note: Currently this util doesn't support the following: + *

    + *
  • updating the inputHint of a FieldDefinition
  • + *
  • removing the EnumValue/LocalizedEnumValue of a FieldDefinition
  • + *
  • updating the label of a EnumValue/LocalizedEnumValue of a FieldDefinition
  • + *
* TODO: Check GITHUB ISSUE#339 for missing FieldDefinition update actions. *

* From 577a8500fca1336e5360503c6a5ec42c9646959f Mon Sep 17 00:00:00 2001 From: Hesham Massoud Date: Thu, 6 Dec 2018 12:16:47 +0100 Subject: [PATCH 138/164] #300: Fix indentation. --- .../FieldDefinitionsUpdateActionUtils.java | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionsUpdateActionUtils.java b/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionsUpdateActionUtils.java index c1306f28a9..b8caf4e249 100644 --- a/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionsUpdateActionUtils.java +++ b/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionsUpdateActionUtils.java @@ -135,17 +135,14 @@ private static List> buildRemoveFieldDefinitionOrFieldDefinit @Nonnull final List newFieldDefinitions) { final Map newFieldDefinitionsNameMap = - newFieldDefinitions - .stream().collect( - toMap(FieldDefinition::getName, fieldDefinition -> fieldDefinition, - (fieldDefinitionA, fieldDefinitionB) -> { - throw new DuplicateNameException(format("Field definitions have duplicated names. " - + "Duplicated field definition name: '%s'. " - + "Field definitions names are expected " - + "to be unique inside their type.", - fieldDefinitionA.getName())); - } - )); + newFieldDefinitions + .stream().collect( + toMap(FieldDefinition::getName, fieldDefinition -> fieldDefinition, + (fieldDefinitionA, fieldDefinitionB) -> { + throw new DuplicateNameException(format("Field definitions have duplicated names. " + + "Duplicated field definition name: '%s'. Field definitions names are " + + "expected to be unique inside their type.", fieldDefinitionA.getName())); + })); return oldFieldDefinitions .stream() From fd5cff6c826875f913ed4ceb1cf10d991afad893 Mon Sep 17 00:00:00 2001 From: Hesham Massoud Date: Thu, 6 Dec 2018 12:17:14 +0100 Subject: [PATCH 139/164] #300: Fix Javadoc. --- .../sync/types/utils/FieldDefinitionsUpdateActionUtils.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionsUpdateActionUtils.java b/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionsUpdateActionUtils.java index b8caf4e249..8c8bb259a9 100644 --- a/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionsUpdateActionUtils.java +++ b/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionsUpdateActionUtils.java @@ -118,7 +118,7 @@ private static List> buildUpdateActions( * Otherwise, if the field definition still exists in the new field definition, then compare the field definition * fields (label, etc..), and add the computed actions to the list of update actions. * - *

Note: If the field type field changes, the old field definition is removed and the new field + *

Note: If the field type field is different, the old field definition is removed and the new field * definition is added with the new field type. * * @param oldFieldDefinitions the list of old {@link FieldDefinition}s. @@ -238,7 +238,7 @@ private static Optional> buildChangeFieldDefinitionOrderUpdat /** * Checks if there are any new field definition drafts which are not existing in the - * {@code oldFieldDefinitionNameMap}. If there are, then "add" field definition update actions are built. + * {@code oldFieldDefinitions}. If there are, then "add" field definition update actions are built. * Otherwise, if there are no new field definitions, then an empty list is returned. * * @param oldFieldDefinitions the list of old {@link FieldDefinition}s. From 85dab403ca49418722cea3420d3c7956ea0faad8 Mon Sep 17 00:00:00 2001 From: Hesham Massoud Date: Thu, 6 Dec 2018 12:17:35 +0100 Subject: [PATCH 140/164] #300: Add note about FieldType and AttributeType changes. --- docs/usage/PRODUCT_TYPE_SYNC.md | 8 +++++++- docs/usage/TYPE_SYNC.md | 8 +++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/docs/usage/PRODUCT_TYPE_SYNC.md b/docs/usage/PRODUCT_TYPE_SYNC.md index 3bf1604ab7..3f0c69bdda 100644 --- a/docs/usage/PRODUCT_TYPE_SYNC.md +++ b/docs/usage/PRODUCT_TYPE_SYNC.md @@ -12,6 +12,8 @@ against a [ProductTypeDraft](https://docs.commercetools.com/http-api-projects-pr - [Sync list of product type drafts](#sync-list-of-product-type-drafts) - [Prerequisites](#prerequisites) - [Running the sync](#running-the-sync) + - [Important to Note](#important-to-note) + - [More examples of how to use the sync](#more-examples-of-how-to-use-the-sync) - [Build all update actions](#build-all-update-actions) - [Build particular update action(s)](#build-particular-update-actions) - [Caveats](#caveats) @@ -64,8 +66,12 @@ __Note__ The statistics object contains the processing time of the last batch on 1. The sync processing time should not take into account the time between supplying batches to the sync. 2. It is not known by the sync which batch is going to be the last one supplied. + +#### Important to Note +1. If two matching `attributeDefinition`s on the matching `productType`s have a different `AttributeType`, the sync will +**remove** the existing `attributeDefinition` and then **add** a new `attributeDefinition` with the new `AttributeType`. -##### More examples of how to use the sync +#### More examples of how to use the sync 1. [Sync from another CTP project as a source](https://github.com/commercetools/commercetools-sync-java/tree/master/src/integration-test/java/com/commercetools/sync/integration/ctpprojectsource/producttypes/ProductTypeSyncIT.java). 2. [Sync from an external source](https://github.com/commercetools/commercetools-sync-java/tree/master/src/integration-test/java/com/commercetools/sync/integration/externalsource/producttypes/ProductTypeSyncIT.java). diff --git a/docs/usage/TYPE_SYNC.md b/docs/usage/TYPE_SYNC.md index a4bafb484d..6574e8881e 100644 --- a/docs/usage/TYPE_SYNC.md +++ b/docs/usage/TYPE_SYNC.md @@ -12,6 +12,8 @@ against a [TypeDraft](https://docs.commercetools.com/http-api-projects-types.htm - [Sync list of type drafts](#sync-list-of-type-drafts) - [Prerequisites](#prerequisites) - [Running the sync](#running-the-sync) + - [Important to Note](#important-to-note) + - [More examples of how to use the sync](#more-examples-of-how-to-use-the-sync) - [Build all update actions](#build-all-update-actions) - [Build particular update action(s)](#build-particular-update-actions) - [Caveats](#caveats) @@ -62,7 +64,11 @@ __Note__ The statistics object contains the processing time of the last batch on 1. The sync processing time should not take into account the time between supplying batches to the sync. 2. It is not known by the sync which batch is going to be the last one supplied. -##### More examples of how to use the sync +#### Important to Note +1. If two matching `fieldDefinition`s on the matching `type`s have a different `FieldType`, the sync will +**remove** the existing `fieldDefinition` and then **add** a new `fieldDefinition` with the new `FieldType`. + +#### More examples of how to use the sync 1. [Sync from another CTP project as a source](https://github.com/commercetools/commercetools-sync-java/tree/master/src/integration-test/java/com/commercetools/sync/integration/ctpprojectsource/types/TypeSyncIT.java). 2. [Sync from an external source](https://github.com/commercetools/commercetools-sync-java/tree/master/src/integration-test/java/com/commercetools/sync/integration/externalsource/types/TypeSyncIT.java). From bd01fdb63e3ecb317bd42a69926ae82b65a185cd Mon Sep 17 00:00:00 2001 From: Hesham Massoud Date: Thu, 6 Dec 2018 12:17:55 +0100 Subject: [PATCH 141/164] #300: Add a test case for a new draft with different order. --- .../sync/integration/externalsource/types/TypeSyncIT.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/integration-test/java/com/commercetools/sync/integration/externalsource/types/TypeSyncIT.java b/src/integration-test/java/com/commercetools/sync/integration/externalsource/types/TypeSyncIT.java index e7d23fb7a5..dcba42a4e5 100644 --- a/src/integration-test/java/com/commercetools/sync/integration/externalsource/types/TypeSyncIT.java +++ b/src/integration-test/java/com/commercetools/sync/integration/externalsource/types/TypeSyncIT.java @@ -235,7 +235,7 @@ public void sync_WithUpdatedType_ChangingFieldDefinitionOrder_ShouldUpdateTypeCh TYPE_NAME_1, ResourceTypeIdsSetBuilder.of().addCategories().build()) .description(TYPE_DESCRIPTION_1) - .fieldDefinitions(asList(FIELD_DEFINITION_2, FIELD_DEFINITION_1)) + .fieldDefinitions(asList(FIELD_DEFINITION_2, FIELD_DEFINITION_3, FIELD_DEFINITION_1)) .build(); final TypeSyncOptions typeSyncOptions = TypeSyncOptionsBuilder @@ -256,7 +256,7 @@ public void sync_WithUpdatedType_ChangingFieldDefinitionOrder_ShouldUpdateTypeCh assertThat(oldTypeAfter).hasValueSatisfying(type -> assertFieldDefinitionsAreEqual(type.getFieldDefinitions(), - asList(FIELD_DEFINITION_2, FIELD_DEFINITION_1))); + asList(FIELD_DEFINITION_2, FIELD_DEFINITION_3, FIELD_DEFINITION_1))); } @Test From 2128a66ca65f4d568c28a24b44158aad5f567073 Mon Sep 17 00:00:00 2001 From: Hesham Massoud Date: Thu, 6 Dec 2018 12:18:10 +0100 Subject: [PATCH 142/164] #300: Remove unneeded asserts. --- .../sync/types/utils/TypeUpdateActionUtilsTest.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/test/java/com/commercetools/sync/types/utils/TypeUpdateActionUtilsTest.java b/src/test/java/com/commercetools/sync/types/utils/TypeUpdateActionUtilsTest.java index 2b1f9243fd..63a24d6bb6 100644 --- a/src/test/java/com/commercetools/sync/types/utils/TypeUpdateActionUtilsTest.java +++ b/src/test/java/com/commercetools/sync/types/utils/TypeUpdateActionUtilsTest.java @@ -74,7 +74,6 @@ public static void setup() { public void buildChangeNameAction_WithDifferentValues_ShouldReturnAction() { final Optional> result = buildChangeNameUpdateAction(old, newDifferent); - assertThat(result).containsInstanceOf(ChangeName.class); assertThat(result).contains(ChangeName.of(newDifferent.getName())); } @@ -89,7 +88,6 @@ public void buildChangeNameAction_WithSameValues_ShouldReturnEmptyOptional() { public void buildSetDescriptionAction_WithDifferentValues_ShouldReturnAction() { final Optional> result = buildSetDescriptionUpdateAction(old, newDifferent); - assertThat(result).containsInstanceOf(SetDescription.class); assertThat(result).contains(SetDescription.of(newDifferent.getDescription())); } From 658a005906b0bd55846c64e802b0af917d8d99a1 Mon Sep 17 00:00:00 2001 From: Hesham Massoud Date: Thu, 6 Dec 2018 12:18:45 +0100 Subject: [PATCH 143/164] #300: Add a unit test with a definition with a null name. --- ...BuildFieldDefinitionUpdateActionsTest.java | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildFieldDefinitionUpdateActionsTest.java b/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildFieldDefinitionUpdateActionsTest.java index 26a7622710..68baa51b2f 100644 --- a/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildFieldDefinitionUpdateActionsTest.java +++ b/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildFieldDefinitionUpdateActionsTest.java @@ -457,4 +457,43 @@ public void buildUpdateActions_WithNullNewFieldDefinition_ShouldSkipNullFieldDef assertThat(updateActions).containsExactly( ChangeFieldDefinitionLabel.of(newFieldDefinition.getName(), newFieldDefinition.getLabel())); } + + @Test + public void buildUpdateActions_WithDefinitionWithNullName_ShouldBuildChangeFieldDefinitionOrderAction() { + // preparation + final Type oldType = mock(Type.class); + final FieldDefinition oldFieldDefinition = FieldDefinition.of( + LocalizedEnumFieldType.of(emptyList()), + "field_1", + LocalizedString.ofEnglish("label1"), + false, + TextInputHint.SINGLE_LINE); + + + when(oldType.getFieldDefinitions()).thenReturn(singletonList(oldFieldDefinition)); + + final FieldDefinition newFieldDefinition = FieldDefinition.of( + LocalizedEnumFieldType.of(emptyList()), + null, + LocalizedString.ofEnglish("label2"), + false, + TextInputHint.SINGLE_LINE); + + final TypeDraft typeDraft = TypeDraftBuilder + .of("key", LocalizedString.ofEnglish("label"), emptySet()) + .fieldDefinitions(asList(null, newFieldDefinition)) + .build(); + + // test + final List> updateActions = buildFieldDefinitionsUpdateActions( + oldType, + typeDraft, + SYNC_OPTIONS + ); + + // assertion + assertThat(updateActions).containsExactly( + RemoveFieldDefinition.of(oldFieldDefinition.getName()), + AddFieldDefinition.of(newFieldDefinition)); + } } From d3f8dd8af23c4137ae9da5d817bfc6a710a525d7 Mon Sep 17 00:00:00 2001 From: Hesham Massoud Date: Thu, 6 Dec 2018 12:21:25 +0100 Subject: [PATCH 144/164] #300: Fix unit test names. --- ...BuildFieldDefinitionUpdateActionsTest.java | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildFieldDefinitionUpdateActionsTest.java b/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildFieldDefinitionUpdateActionsTest.java index 68baa51b2f..2d54000da7 100644 --- a/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildFieldDefinitionUpdateActionsTest.java +++ b/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildFieldDefinitionUpdateActionsTest.java @@ -95,7 +95,7 @@ public class BuildFieldDefinitionUpdateActionsTest { .build(); @Test - public void buildUpdateActions_WithNullNewFieldDefinitionsAndExistingFieldDefinitions_ShouldBuild3RemoveActions() { + public void buildFieldDefinitionsUpdateActions_WithNullNewFieldDefinitionsAndExistingFieldDefinitions_ShouldBuild3RemoveActions() { final Type oldType = readObjectFromResource(TYPE_WITH_FIELDS_ABC, Type.class); final List> updateActions = buildFieldDefinitionsUpdateActions( @@ -112,7 +112,7 @@ public void buildUpdateActions_WithNullNewFieldDefinitionsAndExistingFieldDefini } @Test - public void buildUpdateActions_WithNullNewFieldDefinitionsAndNoOldFieldDefinitions_ShouldNotBuildActions() { + public void buildFieldDefinitionsUpdateActions_WithNullNewFieldDefinitionsAndNoOldFieldDefinitions_ShouldNotBuildActions() { final Type oldType = mock(Type.class); when(oldType.getFieldDefinitions()).thenReturn(emptyList()); when(oldType.getKey()).thenReturn("type_key_1"); @@ -127,7 +127,7 @@ public void buildUpdateActions_WithNullNewFieldDefinitionsAndNoOldFieldDefinitio } @Test - public void buildUpdateActions_WithNewFieldDefinitionsAndNoOldFieldDefinitions_ShouldBuild3AddActions() { + public void buildFieldDefinitionsUpdateActions_WithNewFieldDefinitionsAndNoOldFieldDefinitions_ShouldBuild3AddActions() { final Type oldType = mock(Type.class); when(oldType.getFieldDefinitions()).thenReturn(emptyList()); when(oldType.getKey()).thenReturn("type_key_1"); @@ -151,7 +151,7 @@ public void buildUpdateActions_WithNewFieldDefinitionsAndNoOldFieldDefinitions_S } @Test - public void buildUpdateActions_WithIdenticalFieldDefinitions_ShouldNotBuildUpdateActions() { + public void buildFieldDefinitionsUpdateActions_WithIdenticalFieldDefinitions_ShouldNotBuildUpdateActions() { final Type oldType = readObjectFromResource(TYPE_WITH_FIELDS_ABC, Type.class); final TypeDraft newTypeDraft = readObjectFromResource( @@ -169,7 +169,7 @@ public void buildUpdateActions_WithIdenticalFieldDefinitions_ShouldNotBuildUpdat } @Test - public void buildUpdateActions_WithDuplicateFieldDefinitionNames_ShouldNotBuildActionsAndTriggerErrorCb() { + public void buildFieldDefinitionsUpdateActions_WithDuplicateFieldDefinitionNames_ShouldNotBuildActionsAndTriggerErrorCb() { final Type oldType = readObjectFromResource(TYPE_WITH_FIELDS_ABC, Type.class); final TypeDraft newTypeDraft = readObjectFromResource( @@ -207,7 +207,7 @@ public void buildUpdateActions_WithDuplicateFieldDefinitionNames_ShouldNotBuildA } @Test - public void buildUpdateActions_WithOneMissingFieldDefinition_ShouldBuildRemoveFieldDefinitionAction() { + public void buildFieldDefinitionsUpdateActions_WithOneMissingFieldDefinition_ShouldBuildRemoveFieldDefinitionAction() { final Type oldType = readObjectFromResource(TYPE_WITH_FIELDS_ABC, Type.class); final TypeDraft newTypeDraft = readObjectFromResource( @@ -225,7 +225,7 @@ public void buildUpdateActions_WithOneMissingFieldDefinition_ShouldBuildRemoveFi } @Test - public void buildUpdateActions_WithOneExtraFieldDefinition_ShouldBuildAddFieldDefinitionAction() { + public void buildFieldDefinitionsUpdateActions_WithOneExtraFieldDefinition_ShouldBuildAddFieldDefinitionAction() { final Type oldType = readObjectFromResource(TYPE_WITH_FIELDS_ABC, Type.class); final TypeDraft newTypeDraft = readObjectFromResource( @@ -245,7 +245,7 @@ public void buildUpdateActions_WithOneExtraFieldDefinition_ShouldBuildAddFieldDe } @Test - public void buildUpdateActions_WithOneFieldDefinitionSwitch_ShouldBuildRemoveAndAddFieldDefinitionsActions() { + public void buildFieldDefinitionsUpdateActions_WithOneFieldDefinitionSwitch_ShouldBuildRemoveAndAddFieldDefinitionsActions() { final Type oldType = readObjectFromResource(TYPE_WITH_FIELDS_ABC, Type.class); final TypeDraft newTypeDraft = readObjectFromResource( @@ -266,7 +266,7 @@ public void buildUpdateActions_WithOneFieldDefinitionSwitch_ShouldBuildRemoveAnd } @Test - public void buildUpdateActions_WithDifferent_ShouldBuildChangeFieldDefinitionOrderAction() { + public void buildFieldDefinitionsUpdateActions_WithDifferent_ShouldBuildChangeFieldDefinitionOrderAction() { final Type oldType = readObjectFromResource(TYPE_WITH_FIELDS_ABC, Type.class); final TypeDraft newTypeDraft = readObjectFromResource( @@ -292,7 +292,7 @@ public void buildUpdateActions_WithDifferent_ShouldBuildChangeFieldDefinitionOrd } @Test - public void buildUpdateActions_WithRemovedAndDifferentOrder_ShouldBuildChangeOrderAndRemoveActions() { + public void buildFieldDefinitionsUpdateActions_WithRemovedAndDifferentOrder_ShouldBuildChangeOrderAndRemoveActions() { final Type oldType = readObjectFromResource(TYPE_WITH_FIELDS_ABC, Type.class); final TypeDraft newTypeDraft = readObjectFromResource( @@ -318,7 +318,7 @@ public void buildUpdateActions_WithRemovedAndDifferentOrder_ShouldBuildChangeOrd } @Test - public void buildUpdateActions_WithAddedAndDifferentOrder_ShouldBuildChangeOrderAndAddActions() { + public void buildFieldDefinitionsUpdateActions_WithAddedAndDifferentOrder_ShouldBuildChangeOrderAndAddActions() { final Type oldType = readObjectFromResource(TYPE_WITH_FIELDS_ABC, Type.class); final TypeDraft newTypeDraft = readObjectFromResource( @@ -346,7 +346,7 @@ public void buildUpdateActions_WithAddedAndDifferentOrder_ShouldBuildChangeOrder } @Test - public void buildUpdateActions_WithAddedFieldDefinitionInBetween_ShouldBuildChangeOrderAndAddActions() { + public void buildFieldDefinitionsUpdateActions_WithAddedFieldDefinitionInBetween_ShouldBuildChangeOrderAndAddActions() { final Type oldType = readObjectFromResource(TYPE_WITH_FIELDS_ABC, Type.class); final TypeDraft newTypeDraft = readObjectFromResource( @@ -374,7 +374,7 @@ public void buildUpdateActions_WithAddedFieldDefinitionInBetween_ShouldBuildChan } @Test - public void buildUpdateActions_WithAddedRemovedAndDifOrder_ShouldBuildAllThreeMoveFieldDefinitionActions() { + public void buildFieldDefinitionsUpdateActions_WithAddedRemovedAndDifOrder_ShouldBuildAllThreeMoveFieldDefinitionActions() { final Type oldType = readObjectFromResource(TYPE_WITH_FIELDS_ABC, Type.class); final TypeDraft newTypeDraft = readObjectFromResource( @@ -401,7 +401,7 @@ public void buildUpdateActions_WithAddedRemovedAndDifOrder_ShouldBuildAllThreeMo } @Test - public void buildUpdateActions_WithDifferentType_ShouldRemoveOldFieldDefinitionAndAddNewFieldDefinition() { + public void buildFieldDefinitionsUpdateActions_WithDifferentType_ShouldRemoveOldFieldDefinitionAndAddNewFieldDefinition() { final Type oldType = readObjectFromResource(TYPE_WITH_FIELDS_ABC, Type.class); final TypeDraft newTypeDraft = readObjectFromResource( @@ -421,7 +421,7 @@ public void buildUpdateActions_WithDifferentType_ShouldRemoveOldFieldDefinitionA } @Test - public void buildUpdateActions_WithNullNewFieldDefinition_ShouldSkipNullFieldDefinitions() { + public void buildFieldDefinitionsUpdateActions_WithNullNewFieldDefinition_ShouldSkipNullFieldDefinitions() { // preparation final Type oldType = mock(Type.class); final FieldDefinition oldFieldDefinition = FieldDefinition.of( @@ -459,7 +459,7 @@ public void buildUpdateActions_WithNullNewFieldDefinition_ShouldSkipNullFieldDef } @Test - public void buildUpdateActions_WithDefinitionWithNullName_ShouldBuildChangeFieldDefinitionOrderAction() { + public void buildFieldDefinitionsUpdateActions_WithDefinitionWithNullName_ShouldBuildChangeFieldDefinitionOrderAction() { // preparation final Type oldType = mock(Type.class); final FieldDefinition oldFieldDefinition = FieldDefinition.of( From db1835ebaae778f9f97863976ae9ff436174cd3d Mon Sep 17 00:00:00 2001 From: Hesham Massoud Date: Thu, 6 Dec 2018 12:33:01 +0100 Subject: [PATCH 145/164] #300: Add unit test for fieldDefinition with null FieldType. --- ...BuildFieldDefinitionUpdateActionsTest.java | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildFieldDefinitionUpdateActionsTest.java b/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildFieldDefinitionUpdateActionsTest.java index 2d54000da7..5649d07b57 100644 --- a/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildFieldDefinitionUpdateActionsTest.java +++ b/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildFieldDefinitionUpdateActionsTest.java @@ -496,4 +496,41 @@ public void buildFieldDefinitionsUpdateActions_WithDefinitionWithNullName_Should RemoveFieldDefinition.of(oldFieldDefinition.getName()), AddFieldDefinition.of(newFieldDefinition)); } + + @Test + public void buildFieldDefinitionsUpdateActions_WithDefinitionWithNullType_ShouldBuildChangeFieldDefinitionOrderAction() { + // preparation + final Type oldType = mock(Type.class); + final FieldDefinition oldFieldDefinition = FieldDefinition.of( + LocalizedEnumFieldType.of(emptyList()), + "field_1", + LocalizedString.ofEnglish("label1"), + false, + TextInputHint.SINGLE_LINE); + + + when(oldType.getFieldDefinitions()).thenReturn(singletonList(oldFieldDefinition)); + + final FieldDefinition newFieldDefinition = FieldDefinition.of( + null, + "field_1", + LocalizedString.ofEnglish("label2"), + false, + TextInputHint.SINGLE_LINE); + + final TypeDraft typeDraft = TypeDraftBuilder + .of("key", LocalizedString.ofEnglish("label"), emptySet()) + .fieldDefinitions(asList(null, newFieldDefinition)) + .build(); + + // test + final List> updateActions = buildFieldDefinitionsUpdateActions( + oldType, + typeDraft, + SYNC_OPTIONS + ); + + // assertion + assertThat(updateActions).isEmpty(); + } } From dcb95f4bcd97248e05f6a9a256774c220637bfc6 Mon Sep 17 00:00:00 2001 From: Hesham Massoud Date: Thu, 6 Dec 2018 13:12:59 +0100 Subject: [PATCH 146/164] #300: Unexpose enum differs. --- docs/RELEASE_NOTES.md | 3 --- .../commons/utils/EnumValuesUpdateActionUtils.java | 3 +++ .../utils/LocalizedEnumValueUpdateActionUtils.java | 12 ++++++++++-- .../utils/PlainEnumValueUpdateActionUtils.java | 14 ++++++++++++-- 4 files changed, 25 insertions(+), 7 deletions(-) diff --git a/docs/RELEASE_NOTES.md b/docs/RELEASE_NOTES.md index 06436efe16..37d0761917 100644 --- a/docs/RELEASE_NOTES.md +++ b/docs/RELEASE_NOTES.md @@ -41,9 +41,6 @@ - **Type Sync** - Support for syncing types. [#300](https://github.com/commercetools/commercetools-sync-java/issues/300) For more info how to use it please refer to [Type usage doc](/docs/usage/TYPE_SYNC.md). - **Type Sync** - Exposed `TypeSyncUtils#buildActions` which calculates all needed update actions after comparing a `Type` and a `TypeDraft`. [#300](https://github.com/commercetools/commercetools-sync-java/issues/300) - **Type Sync** - Exposed `TypeUpdateActionUtils` which contains utils for calculating needed update actions after comparing individual fields of a `Type` and a `TypeDraft`. [#300](https://github.com/commercetools/commercetools-sync-java/issues/300) - - **Type Sync** - Exposed `LocalizedEnumValueUpdateActionUtils` which contains utils for calculating needed update actions after comparing two lists of `LocalizedEnumValue`s. [#300](https://github.com/commercetools/commercetools-sync-java/issues/300) - - **Type Sync** - Exposed `PlainEnumValueUpdateActionUtils` which contains utils for calculating needed update actions after comparing two lists of `EnumValue`s. [#300](https://github.com/commercetools/commercetools-sync-java/issues/300) - - **Commons** - Exposed `EnumValuesUpdateActionUtils` which contains generic utilities for calculating needed update actions after comparing two lists of `EnumValue`s or `LocalizedEnumValue`s. [#300](https://github.com/commercetools/commercetools-sync-java/issues/300) - 📋 **Documentation** (4) - **Commons** - Added the documentation github pages. https://commercetools.github.io/commercetools-sync-java diff --git a/src/main/java/com/commercetools/sync/commons/utils/EnumValuesUpdateActionUtils.java b/src/main/java/com/commercetools/sync/commons/utils/EnumValuesUpdateActionUtils.java index 2e42900691..5b8ec9b808 100644 --- a/src/main/java/com/commercetools/sync/commons/utils/EnumValuesUpdateActionUtils.java +++ b/src/main/java/com/commercetools/sync/commons/utils/EnumValuesUpdateActionUtils.java @@ -26,6 +26,9 @@ import static java.util.Optional.ofNullable; import static java.util.stream.Collectors.toMap; +/** + * This class is only meant for the internal use of the commercetools-sync-java library. + */ public final class EnumValuesUpdateActionUtils { /** diff --git a/src/main/java/com/commercetools/sync/types/utils/LocalizedEnumValueUpdateActionUtils.java b/src/main/java/com/commercetools/sync/types/utils/LocalizedEnumValueUpdateActionUtils.java index be4651e740..c27577c3f2 100644 --- a/src/main/java/com/commercetools/sync/types/utils/LocalizedEnumValueUpdateActionUtils.java +++ b/src/main/java/com/commercetools/sync/types/utils/LocalizedEnumValueUpdateActionUtils.java @@ -13,7 +13,10 @@ import static com.commercetools.sync.commons.utils.EnumValuesUpdateActionUtils.buildActions; -public final class LocalizedEnumValueUpdateActionUtils { +/** + * This class is only meant for the internal use of the commercetools-sync-java library. + */ +final class LocalizedEnumValueUpdateActionUtils { /** * Compares a list of old {@link LocalizedEnumValue}s with a list of new {@link LocalizedEnumValue}s for a given * field definition and builds required update actions (e.g addLocalizedEnumValue, changeLocalizedEnumValueOrder). @@ -21,6 +24,11 @@ public final class LocalizedEnumValueUpdateActionUtils { * an empty {@link List} is returned. * *

+ * Note: Currently this util doesn't support the following: + *

    + *
  • removing the EnumValue/LocalizedEnumValue of a FieldDefinition
  • + *
  • updating the label of a EnumValue/LocalizedEnumValue of a FieldDefinition
  • + *
* TODO: Check GITHUB ISSUE#339 for missing FieldDefinition update actions. *

* @@ -32,7 +40,7 @@ public final class LocalizedEnumValueUpdateActionUtils { * @throws DuplicateKeyException in case there are localized enum values with duplicate keys. */ @Nonnull - public static List> buildLocalizedEnumValuesUpdateActions( + static List> buildLocalizedEnumValuesUpdateActions( @Nonnull final String fieldDefinitionName, @Nonnull final List oldEnumValues, @Nullable final List newEnumValues) { diff --git a/src/main/java/com/commercetools/sync/types/utils/PlainEnumValueUpdateActionUtils.java b/src/main/java/com/commercetools/sync/types/utils/PlainEnumValueUpdateActionUtils.java index a17e206f14..4745530846 100644 --- a/src/main/java/com/commercetools/sync/types/utils/PlainEnumValueUpdateActionUtils.java +++ b/src/main/java/com/commercetools/sync/types/utils/PlainEnumValueUpdateActionUtils.java @@ -1,5 +1,6 @@ package com.commercetools.sync.types.utils; +import com.commercetools.sync.commons.exceptions.DuplicateKeyException; import com.commercetools.sync.commons.utils.EnumValuesUpdateActionUtils; import io.sphere.sdk.commands.UpdateAction; import io.sphere.sdk.models.EnumValue; @@ -11,7 +12,10 @@ import javax.annotation.Nullable; import java.util.List; -public final class PlainEnumValueUpdateActionUtils { +/** + * This class is only meant for the internal use of the commercetools-sync-java library. + */ +final class PlainEnumValueUpdateActionUtils { /** * Compares a list of old {@link EnumValue}s with a list of new {@link EnumValue}s for a given @@ -20,6 +24,11 @@ public final class PlainEnumValueUpdateActionUtils { * an empty {@link List} is returned. * *

+ * Note: Currently this util doesn't support the following: + *

    + *
  • removing the EnumValue/LocalizedEnumValue of a FieldDefinition
  • + *
  • updating the label of a EnumValue/LocalizedEnumValue of a FieldDefinition
  • + *
* TODO: Check GITHUB ISSUE#339 for missing FieldDefinition update actions. *

* @@ -28,9 +37,10 @@ public final class PlainEnumValueUpdateActionUtils { * @param newEnumValues the new list of plain enum values. * @return a list of plain enum values update actions if the list of plain enum values is not identical. * Otherwise, if the plain enum values are identical, an empty list is returned. + * @throws DuplicateKeyException in case there are localized enum values with duplicate keys. */ @Nonnull - public static List> buildEnumValuesUpdateActions( + static List> buildEnumValuesUpdateActions( @Nonnull final String fieldDefinitionName, @Nonnull final List oldEnumValues, @Nullable final List newEnumValues) { From b5bfa670c3c842b0c02cc994182048611468fd88 Mon Sep 17 00:00:00 2001 From: Hesham Massoud Date: Thu, 6 Dec 2018 13:16:35 +0100 Subject: [PATCH 147/164] #300: Fix Javadocs. --- .../utils/AttributeDefinitionUpdateActionUtils.java | 3 +++ .../utils/AttributeDefinitionsUpdateActionUtils.java | 3 ++- .../utils/PlainEnumValueUpdateActionUtils.java | 2 ++ .../sync/types/utils/FieldDefinitionUpdateActionUtils.java | 7 ++++++- .../types/utils/FieldDefinitionsUpdateActionUtils.java | 4 +++- 5 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/commercetools/sync/producttypes/utils/AttributeDefinitionUpdateActionUtils.java b/src/main/java/com/commercetools/sync/producttypes/utils/AttributeDefinitionUpdateActionUtils.java index 3e95532455..59c83244c8 100644 --- a/src/main/java/com/commercetools/sync/producttypes/utils/AttributeDefinitionUpdateActionUtils.java +++ b/src/main/java/com/commercetools/sync/producttypes/utils/AttributeDefinitionUpdateActionUtils.java @@ -1,5 +1,6 @@ package com.commercetools.sync.producttypes.utils; +import com.commercetools.sync.commons.exceptions.DuplicateKeyException; import io.sphere.sdk.commands.UpdateAction; import io.sphere.sdk.models.EnumValue; import io.sphere.sdk.models.LocalizedEnumValue; @@ -38,6 +39,8 @@ final class AttributeDefinitionUpdateActionUtils { * @param oldAttributeDefinition the old attribute definition which should be updated. * @param newAttributeDefinitionDraft the new attribute definition draft where we get the new fields. * @return A list with the update actions or an empty list if the attribute definition fields are identical. + * + * @throws DuplicateKeyException in case there are localized enum values with duplicate keys. */ @Nonnull static List> buildActions( diff --git a/src/main/java/com/commercetools/sync/producttypes/utils/AttributeDefinitionsUpdateActionUtils.java b/src/main/java/com/commercetools/sync/producttypes/utils/AttributeDefinitionsUpdateActionUtils.java index 5c3710eed9..f93ad00719 100644 --- a/src/main/java/com/commercetools/sync/producttypes/utils/AttributeDefinitionsUpdateActionUtils.java +++ b/src/main/java/com/commercetools/sync/producttypes/utils/AttributeDefinitionsUpdateActionUtils.java @@ -53,7 +53,8 @@ final class AttributeDefinitionsUpdateActionUtils { * @param newAttributeDefinitionsDrafts the new list of attribute definitions drafts. * @return a list of attribute definitions update actions if the list of attribute definitions are not identical. * Otherwise, if the attribute definitions are identical, an empty list is returned. - * @throws DuplicateNameException in case there are attribute definitions drafts with duplicate names. + * @throws BuildUpdateActionException in case there are attribute definitions drafts with duplicate names or enums + * duplicate keys. */ @Nonnull static List> buildAttributeDefinitionsUpdateActions( diff --git a/src/main/java/com/commercetools/sync/producttypes/utils/PlainEnumValueUpdateActionUtils.java b/src/main/java/com/commercetools/sync/producttypes/utils/PlainEnumValueUpdateActionUtils.java index 375985b1e3..6fd0ce0838 100644 --- a/src/main/java/com/commercetools/sync/producttypes/utils/PlainEnumValueUpdateActionUtils.java +++ b/src/main/java/com/commercetools/sync/producttypes/utils/PlainEnumValueUpdateActionUtils.java @@ -1,5 +1,6 @@ package com.commercetools.sync.producttypes.utils; +import com.commercetools.sync.commons.exceptions.DuplicateKeyException; import com.commercetools.sync.commons.utils.EnumValuesUpdateActionUtils; import io.sphere.sdk.commands.UpdateAction; import io.sphere.sdk.models.EnumValue; @@ -34,6 +35,7 @@ public final class PlainEnumValueUpdateActionUtils { * @param newEnumValues the new list of plain enum values. * @return a list of plain enum values update actions if the list of plain enum values is not identical. * Otherwise, if the plain enum values are identical, an empty list is returned. + * @throws DuplicateKeyException in case there are localized enum values with duplicate keys. */ @Nonnull public static List> buildEnumValuesUpdateActions( diff --git a/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionUpdateActionUtils.java b/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionUpdateActionUtils.java index 59fafc312b..a558724285 100644 --- a/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionUpdateActionUtils.java +++ b/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionUpdateActionUtils.java @@ -1,5 +1,6 @@ package com.commercetools.sync.types.utils; +import com.commercetools.sync.commons.exceptions.DuplicateKeyException; import io.sphere.sdk.commands.UpdateAction; import io.sphere.sdk.models.EnumValue; import io.sphere.sdk.models.LocalizedEnumValue; @@ -26,7 +27,7 @@ final class FieldDefinitionUpdateActionUtils { /** - * Compares all the fields of old {@link FieldDefinition} with new {@link FieldDefinition} and returns + * Compares all the fields of an old {@link FieldDefinition} with a new {@link FieldDefinition} and returns * a list of {@link UpdateAction}<{@link Type}> as a result. If both the {@link FieldDefinition} * and the {@link FieldDefinition} have identical fields, then no update action is needed and hence an * empty {@link List} is returned. @@ -34,6 +35,8 @@ final class FieldDefinitionUpdateActionUtils { * @param oldFieldDefinition the old field definition which should be updated. * @param newFieldDefinition the new field definition where we get the new fields. * @return A list with the update actions or an empty list if the field definition fields are identical. + * + * @throws DuplicateKeyException in case there are localized enum values with duplicate keys. */ @Nonnull static List> buildActions( @@ -56,6 +59,8 @@ static List> buildActions( * @param oldFieldDefinition the old field definition which should be updated. * @param newFieldDefinition the new field definition where we get the new fields. * @return A list with the update actions or an empty list if the field definition enums are identical. + * + * @throws DuplicateKeyException in case there are localized enum values with duplicate keys. */ @Nonnull static List> buildEnumUpdateActions( diff --git a/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionsUpdateActionUtils.java b/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionsUpdateActionUtils.java index 8c8bb259a9..5146813cd6 100644 --- a/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionsUpdateActionUtils.java +++ b/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionsUpdateActionUtils.java @@ -53,7 +53,8 @@ final class FieldDefinitionsUpdateActionUtils { * @param newFieldDefinitions the new list of field definitions. * @return a list of field definitions update actions if the list of field definitions is not identical. * Otherwise, if the field definitions are identical, an empty list is returned. - * @throws DuplicateNameException in case there are field definitions with duplicate names. + * @throws BuildUpdateActionException in case there are field definitions with duplicate names or enums + * duplicate keys. */ @Nonnull static List> buildFieldDefinitionsUpdateActions( @@ -128,6 +129,7 @@ private static List> buildUpdateActions( * definition fields (name, label, etc..), and add the computed actions to the list of update actions. * Otherwise, if the field definitions are identical, an empty optional is returned. * @throws DuplicateNameException in case there are field definitions drafts with duplicate names. + * @throws DuplicateKeyException in case there are enum values with duplicate keys. */ @Nonnull private static List> buildRemoveFieldDefinitionOrFieldDefinitionUpdateActions( From bbaab0114164f8af20a8f3f8e4e49090b8fe36c8 Mon Sep 17 00:00:00 2001 From: Hesham Massoud Date: Thu, 6 Dec 2018 13:16:47 +0100 Subject: [PATCH 148/164] #300: Add more readable line spacing. --- .../utils/AttributeDefinitionUpdateActionUtils.java | 1 + .../sync/types/utils/FieldDefinitionUpdateActionUtils.java | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/src/main/java/com/commercetools/sync/producttypes/utils/AttributeDefinitionUpdateActionUtils.java b/src/main/java/com/commercetools/sync/producttypes/utils/AttributeDefinitionUpdateActionUtils.java index 59c83244c8..7eb47b7daf 100644 --- a/src/main/java/com/commercetools/sync/producttypes/utils/AttributeDefinitionUpdateActionUtils.java +++ b/src/main/java/com/commercetools/sync/producttypes/utils/AttributeDefinitionUpdateActionUtils.java @@ -75,6 +75,7 @@ private static List> buildEnumUpdateActions( @Nonnull final AttributeDefinitionDraft newAttributeDefinitionDraft) { final List> updateActions = new ArrayList<>(); + if (isPlainEnumAttribute(oldAttributeDefinition)) { updateActions.addAll(buildEnumValuesUpdateActions( oldAttributeDefinition.getName(), diff --git a/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionUpdateActionUtils.java b/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionUpdateActionUtils.java index a558724285..50ec8f6f7b 100644 --- a/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionUpdateActionUtils.java +++ b/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionUpdateActionUtils.java @@ -68,18 +68,23 @@ static List> buildEnumUpdateActions( @Nonnull final FieldDefinition newFieldDefinition) { final List> updateActions = new ArrayList<>(); + if (isPlainEnumField(oldFieldDefinition)) { + updateActions.addAll(buildEnumValuesUpdateActions( oldFieldDefinition.getName(), ((EnumFieldType) oldFieldDefinition.getType()).getValues(), ((EnumFieldType) newFieldDefinition.getType()).getValues() )); + } else if (isLocalizedEnumField(oldFieldDefinition)) { + updateActions.addAll(buildLocalizedEnumValuesUpdateActions( oldFieldDefinition.getName(), ((LocalizedEnumFieldType) oldFieldDefinition.getType()).getValues(), ((LocalizedEnumFieldType) newFieldDefinition.getType()).getValues() )); + } return updateActions; } From a87995df787317bfa2dd1d7c770efba24da30721 Mon Sep 17 00:00:00 2001 From: Hesham Massoud Date: Thu, 6 Dec 2018 13:18:41 +0100 Subject: [PATCH 149/164] #300: Move tests to accessible package. --- .../BuildFieldDefinitionUpdateActionsTest.java | 2 +- .../BuildLocalizedEnumUpdateActionsTest.java | 2 +- .../{typeactionutils => }/BuildPlainEnumUpdateActionsTest.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) rename src/test/java/com/commercetools/sync/types/utils/{typeactionutils => }/BuildFieldDefinitionUpdateActionsTest.java (99%) rename src/test/java/com/commercetools/sync/types/utils/{typeactionutils => }/BuildLocalizedEnumUpdateActionsTest.java (99%) rename src/test/java/com/commercetools/sync/types/utils/{typeactionutils => }/BuildPlainEnumUpdateActionsTest.java (98%) diff --git a/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildFieldDefinitionUpdateActionsTest.java b/src/test/java/com/commercetools/sync/types/utils/BuildFieldDefinitionUpdateActionsTest.java similarity index 99% rename from src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildFieldDefinitionUpdateActionsTest.java rename to src/test/java/com/commercetools/sync/types/utils/BuildFieldDefinitionUpdateActionsTest.java index 5649d07b57..1a72f9c408 100644 --- a/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildFieldDefinitionUpdateActionsTest.java +++ b/src/test/java/com/commercetools/sync/types/utils/BuildFieldDefinitionUpdateActionsTest.java @@ -1,4 +1,4 @@ -package com.commercetools.sync.types.utils.typeactionutils; +package com.commercetools.sync.types.utils; import com.commercetools.sync.commons.exceptions.BuildUpdateActionException; import com.commercetools.sync.commons.exceptions.DuplicateNameException; diff --git a/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildLocalizedEnumUpdateActionsTest.java b/src/test/java/com/commercetools/sync/types/utils/BuildLocalizedEnumUpdateActionsTest.java similarity index 99% rename from src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildLocalizedEnumUpdateActionsTest.java rename to src/test/java/com/commercetools/sync/types/utils/BuildLocalizedEnumUpdateActionsTest.java index bd4093d422..7b97c8538b 100644 --- a/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildLocalizedEnumUpdateActionsTest.java +++ b/src/test/java/com/commercetools/sync/types/utils/BuildLocalizedEnumUpdateActionsTest.java @@ -1,4 +1,4 @@ -package com.commercetools.sync.types.utils.typeactionutils; +package com.commercetools.sync.types.utils; import com.commercetools.sync.commons.exceptions.DuplicateKeyException; import com.commercetools.sync.types.utils.LocalizedEnumValueUpdateActionUtils; diff --git a/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildPlainEnumUpdateActionsTest.java b/src/test/java/com/commercetools/sync/types/utils/BuildPlainEnumUpdateActionsTest.java similarity index 98% rename from src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildPlainEnumUpdateActionsTest.java rename to src/test/java/com/commercetools/sync/types/utils/BuildPlainEnumUpdateActionsTest.java index 2821ecb2b9..d474c591b9 100644 --- a/src/test/java/com/commercetools/sync/types/utils/typeactionutils/BuildPlainEnumUpdateActionsTest.java +++ b/src/test/java/com/commercetools/sync/types/utils/BuildPlainEnumUpdateActionsTest.java @@ -1,4 +1,4 @@ -package com.commercetools.sync.types.utils.typeactionutils; +package com.commercetools.sync.types.utils; import io.sphere.sdk.commands.UpdateAction; import io.sphere.sdk.types.Type; From 5bbf06d9ec049db12f418de016fe90f522985e79 Mon Sep 17 00:00:00 2001 From: Hesham Massoud Date: Thu, 6 Dec 2018 14:53:58 +0100 Subject: [PATCH 150/164] #300: Unexpose generic enum differ. --- .../commons/utils/EnumValuesUpdateActionUtils.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/commercetools/sync/commons/utils/EnumValuesUpdateActionUtils.java b/src/main/java/com/commercetools/sync/commons/utils/EnumValuesUpdateActionUtils.java index 5b8ec9b808..62821b64da 100644 --- a/src/main/java/com/commercetools/sync/commons/utils/EnumValuesUpdateActionUtils.java +++ b/src/main/java/com/commercetools/sync/commons/utils/EnumValuesUpdateActionUtils.java @@ -27,7 +27,7 @@ import static java.util.stream.Collectors.toMap; /** - * This class is only meant for the internal use of the commercetools-sync-java library. + * The utils in this class are only meant for the internal use of the commercetools-sync-java library. */ public final class EnumValuesUpdateActionUtils { @@ -222,7 +222,7 @@ private static List> getRemoveEnumValuesU * Otherwise, if the enum values order is identical, an empty optional is returned. */ @Nonnull - public static Optional> buildChangeEnumValuesOrderUpdateAction( + static Optional> buildChangeEnumValuesOrderUpdateAction( @Nonnull final String definitionName, @Nonnull final List oldEnumValues, @Nonnull final List newEnumValues, @@ -250,7 +250,7 @@ public static Optional> buildChangeEnumVa * Otherwise, if the enum values order is identical, an empty optional is returned. */ @Nonnull - public static Optional> buildChangeEnumValuesWithKeysOrderUpdateAction( + private static Optional> buildChangeEnumValuesWithKeysOrderUpdateAction( @Nonnull final String definitionName, @Nonnull final List oldEnumValues, @Nonnull final List newEnumValues, @@ -306,7 +306,7 @@ private static Pair, List> getAllKeysAn * Otherwise, if the enum values are identical, an empty optional is returned. */ @Nonnull - public static List> buildAddEnumValuesUpdateActions( + static List> buildAddEnumValuesUpdateActions( @Nonnull final String definitionName, @Nonnull final List oldEnumValues, @Nonnull final List newEnumValues, @@ -344,7 +344,7 @@ public static List> buildAddEnumValuesUpd * Otherwise, if the enum values are identical, an empty optional is returned. */ @Nonnull - public static Optional> buildRemoveEnumValuesUpdateAction( + static Optional> buildRemoveEnumValuesUpdateAction( @Nonnull final String definitionName, @Nonnull final List oldEnumValues, @Nullable final List newEnumValues, @@ -383,7 +383,7 @@ public static Optional> buildRemoveEnumVa * is returned. */ @Nonnull - public static List> buildMatchingEnumValuesUpdateActions( + static List> buildMatchingEnumValuesUpdateActions( @Nonnull final String definitionName, @Nonnull final List oldEnumValues, @Nonnull final List newEnumValues, From 2459b38f49fa9a997304e2badfa08a26f90413f4 Mon Sep 17 00:00:00 2001 From: Hesham Massoud Date: Thu, 6 Dec 2018 14:55:47 +0100 Subject: [PATCH 151/164] #300: Remove redundant empty check. --- .../sync/commons/utils/EnumValuesUpdateActionUtils.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/commercetools/sync/commons/utils/EnumValuesUpdateActionUtils.java b/src/main/java/com/commercetools/sync/commons/utils/EnumValuesUpdateActionUtils.java index 62821b64da..53d2a7bbfd 100644 --- a/src/main/java/com/commercetools/sync/commons/utils/EnumValuesUpdateActionUtils.java +++ b/src/main/java/com/commercetools/sync/commons/utils/EnumValuesUpdateActionUtils.java @@ -62,7 +62,7 @@ public static List> buildActions( @Nullable final BiFunction, UpdateAction> changeOrderEnumCallback, @Nullable final BiFunction, UpdateAction> changeOrderWithKeysEnumCallback) { - if (newEnumValues != null && !newEnumValues.isEmpty()) { + if (newEnumValues != null) { return buildUpdateActions(definitionName, oldEnumValues, newEnumValues, @@ -76,11 +76,10 @@ public static List> buildActions( return buildRemoveEnumValuesUpdateAction( definitionName, oldEnumValues, - newEnumValues, + null, removeEnumCallback) .map(Collections::singletonList) .orElse(emptyList()); - } return emptyList(); From f6e34d2140d9b37781e32703b1ae661a16f0b5ce Mon Sep 17 00:00:00 2001 From: Hesham Massoud Date: Thu, 6 Dec 2018 14:56:16 +0100 Subject: [PATCH 152/164] #300: Replace singleton lists with optionals. --- .../utils/EnumValuesUpdateActionUtils.java | 53 +++++++++---------- 1 file changed, 25 insertions(+), 28 deletions(-) diff --git a/src/main/java/com/commercetools/sync/commons/utils/EnumValuesUpdateActionUtils.java b/src/main/java/com/commercetools/sync/commons/utils/EnumValuesUpdateActionUtils.java index 53d2a7bbfd..f3c92457af 100644 --- a/src/main/java/com/commercetools/sync/commons/utils/EnumValuesUpdateActionUtils.java +++ b/src/main/java/com/commercetools/sync/commons/utils/EnumValuesUpdateActionUtils.java @@ -96,8 +96,8 @@ private static List> buildUpdateActions( @Nullable final BiFunction, UpdateAction> changeOrderEnumCallback, @Nullable final BiFunction, UpdateAction> changeOrderWithKeysEnumCallback) { - final List> removeEnumValuesUpdateActions = - getRemoveEnumValuesUpdateActions(definitionName, oldEnumValues, newEnumValues, + final Optional> removeEnumValuesUpdateAction = + getRemoveEnumValuesUpdateAction(definitionName, oldEnumValues, newEnumValues, removeEnumValuesUpdateActionCallback); final List> matchingEnumValuesUpdateActions = getMatchingEnumValuesUpdateActions( @@ -106,56 +106,55 @@ private static List> buildUpdateActions( final List> addEnumValuesUpdateActions = getAddEnumValuesUpdateActions(definitionName, oldEnumValues, newEnumValues, addEnumCallback); - final List> changeEnumValuesOrderUpdateActions = getChangeEnumValuesOrderUpdateActions( + final Optional> changeEnumValuesOrderUpdateAction = getChangeEnumValuesOrderUpdateAction( definitionName, oldEnumValues, newEnumValues, changeOrderEnumCallback); - final List> changeEnumValuesWithKeysOrderUpdateActions = - getChangeEnumValuesWithKeysOrderUpdateActions( + final Optional> changeEnumValuesWithKeysOrderUpdateActions = + getChangeEnumValuesWithKeysOrderUpdateAction( definitionName, oldEnumValues, newEnumValues, changeOrderWithKeysEnumCallback); - return Stream.of(removeEnumValuesUpdateActions, + return Stream + .of( + removeEnumValuesUpdateAction.map(Collections::singletonList).orElse(emptyList()), matchingEnumValuesUpdateActions, addEnumValuesUpdateActions, - changeEnumValuesOrderUpdateActions, - changeEnumValuesWithKeysOrderUpdateActions) - .flatMap(Collection::stream) - .collect(Collectors.toList()); + changeEnumValuesOrderUpdateAction.map(Collections::singletonList).orElse(emptyList()), + changeEnumValuesWithKeysOrderUpdateActions.map(Collections::singletonList).orElse(emptyList())) + .flatMap(Collection::stream) + .collect(Collectors.toList()); } @Nonnull - private static List> getChangeEnumValuesWithKeysOrderUpdateActions( + private static Optional> getChangeEnumValuesWithKeysOrderUpdateAction( @Nonnull final String definitionName, @Nonnull final List oldEnumValues, @Nonnull final List newEnumValues, @Nullable final BiFunction, UpdateAction> changeOrderWithKeysEnumCallback) { - return changeOrderWithKeysEnumCallback == null ? emptyList() : + return changeOrderWithKeysEnumCallback == null ? Optional.empty() : buildChangeEnumValuesWithKeysOrderUpdateAction( definitionName, oldEnumValues, newEnumValues, changeOrderWithKeysEnumCallback - ).map(Collections::singletonList) - .orElse(emptyList()); + ); } @Nonnull - private static List> getChangeEnumValuesOrderUpdateActions( + private static Optional> getChangeEnumValuesOrderUpdateAction( @Nonnull final String definitionName, @Nonnull final List oldEnumValues, @Nonnull final List newEnumValues, @Nullable final BiFunction, UpdateAction> changeOrderEnumCallback) { - return changeOrderEnumCallback == null ? emptyList() : + return changeOrderEnumCallback == null ? Optional.empty() : buildChangeEnumValuesOrderUpdateAction( - definitionName, - oldEnumValues, - newEnumValues, - changeOrderEnumCallback - ) - .map(Collections::singletonList) - .orElse(emptyList()); + definitionName, + oldEnumValues, + newEnumValues, + changeOrderEnumCallback + ); } @Nonnull @@ -190,20 +189,18 @@ private static List> getMatchingEnumValue } @Nonnull - private static List> getRemoveEnumValuesUpdateActions( + private static Optional> getRemoveEnumValuesUpdateAction( @Nonnull final String definitionName, @Nonnull final List oldEnumValues, @Nonnull final List newEnumValues, @Nullable final BiFunction, UpdateAction> removeEnumValuesUpdateActionCallback) { - return removeEnumValuesUpdateActionCallback == null ? emptyList() : + return removeEnumValuesUpdateActionCallback == null ? Optional.empty() : buildRemoveEnumValuesUpdateAction( definitionName, oldEnumValues, newEnumValues, - removeEnumValuesUpdateActionCallback) - .map(Collections::singletonList) - .orElse(emptyList()); + removeEnumValuesUpdateActionCallback); } /** From 4ab3f1d9f79a61b908b00fce6fe24f1419ef1b69 Mon Sep 17 00:00:00 2001 From: Hesham Massoud Date: Thu, 6 Dec 2018 14:59:04 +0100 Subject: [PATCH 153/164] #300: Change test names. --- .../types/utils/BuildLocalizedEnumUpdateActionsTest.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/test/java/com/commercetools/sync/types/utils/BuildLocalizedEnumUpdateActionsTest.java b/src/test/java/com/commercetools/sync/types/utils/BuildLocalizedEnumUpdateActionsTest.java index 7b97c8538b..292d7036ad 100644 --- a/src/test/java/com/commercetools/sync/types/utils/BuildLocalizedEnumUpdateActionsTest.java +++ b/src/test/java/com/commercetools/sync/types/utils/BuildLocalizedEnumUpdateActionsTest.java @@ -1,7 +1,6 @@ package com.commercetools.sync.types.utils; import com.commercetools.sync.commons.exceptions.DuplicateKeyException; -import com.commercetools.sync.types.utils.LocalizedEnumValueUpdateActionUtils; import io.sphere.sdk.commands.UpdateAction; import io.sphere.sdk.types.Type; import io.sphere.sdk.types.commands.updateactions.AddLocalizedEnumValue; @@ -25,7 +24,7 @@ public class BuildLocalizedEnumUpdateActionsTest { public ExpectedException expectedException = ExpectedException.none(); @Test - public void buildLocalizedEnumUpdateActions_WithEmptyPlainEnumValuesAndNoOlEnumValues_ShouldNotBuildActions() { + public void buildLocalizedEnumUpdateActions_WithEmptyPlainEnumValuesAndNoOldEnumValues_ShouldNotBuildActions() { final List> updateActions = buildLocalizedEnumValuesUpdateActions( FIELD_NAME_1, emptyList(), @@ -179,7 +178,7 @@ public void buildLocalizedEnumUpdateActions_WithAddedRemovedAndDifOrder_ShouldBu } @Test - public void buildLocalizedEnumUpdateActions_WithDuplicatePlainEnumValues_ShouldTriggerDuplicateKeyError() { + public void buildLocalizedEnumUpdateActions_WithDuplicateEnumValues_ShouldTriggerDuplicateKeyError() { expectedException.expect(DuplicateKeyException.class); expectedException.expectMessage("Enum Values have duplicated keys. Definition name: " + "'field_definition_name', Duplicated enum value: 'b'. Enum Values are expected to be unique inside " From f84764fd0f495917622cec1afc20267fc14bf14f Mon Sep 17 00:00:00 2001 From: Hesham Massoud Date: Thu, 6 Dec 2018 14:59:42 +0100 Subject: [PATCH 154/164] #300: Fix unit tests. --- .../BuildPlainEnumUpdateActionsTest.java | 30 +++++++++++++++---- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/src/test/java/com/commercetools/sync/types/utils/BuildPlainEnumUpdateActionsTest.java b/src/test/java/com/commercetools/sync/types/utils/BuildPlainEnumUpdateActionsTest.java index d474c591b9..51417c0011 100644 --- a/src/test/java/com/commercetools/sync/types/utils/BuildPlainEnumUpdateActionsTest.java +++ b/src/test/java/com/commercetools/sync/types/utils/BuildPlainEnumUpdateActionsTest.java @@ -1,10 +1,13 @@ package com.commercetools.sync.types.utils; +import com.commercetools.sync.commons.exceptions.DuplicateKeyException; import io.sphere.sdk.commands.UpdateAction; import io.sphere.sdk.types.Type; import io.sphere.sdk.types.commands.updateactions.AddEnumValue; import io.sphere.sdk.types.commands.updateactions.ChangeEnumValueOrder; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.ExpectedException; import java.util.List; @@ -17,8 +20,11 @@ public class BuildPlainEnumUpdateActionsTest { private static final String FIELD_NAME_1 = "field1"; + @Rule + public ExpectedException expectedException = ExpectedException.none(); + @Test - public void buildPlainEnumUpdateActions_WithEmptyPlainEnumValuesAndNoOlEnumValues_ShouldNotBuildActions() { + public void buildPlainEnumUpdateActions_WithEmptyPlainEnumValuesAndNoOldEnumValues_ShouldNotBuildActions() { final List> updateActions = buildEnumValuesUpdateActions( FIELD_NAME_1, emptyList(), @@ -68,7 +74,7 @@ public void buildPlainEnumUpdateActions_WithOnePlainEnumValue_ShouldBuildAddEnum } @Test - public void buildPlainEnumUpdateActions_WithOneEnumValueSwitch_ShouldBuildRemoveAndAddEnumValueActions() { + public void buildPlainEnumUpdateActions_WithOneEnumValueSwitch_ShouldBuildAddEnumValueActions() { final List> updateActions = buildEnumValuesUpdateActions( FIELD_NAME_1, ENUM_VALUES_ABC, @@ -81,7 +87,7 @@ public void buildPlainEnumUpdateActions_WithOneEnumValueSwitch_ShouldBuildRemove } @Test - public void buildPlainEnumUpdateActions_WithDifferent_ShouldBuildChangeEnumValueOrderAction() { + public void buildPlainEnumUpdateActions_WithDifferentOrder_ShouldBuildChangeEnumValueOrderAction() { final List> updateActions = buildEnumValuesUpdateActions( FIELD_NAME_1, ENUM_VALUES_ABC, @@ -98,7 +104,7 @@ public void buildPlainEnumUpdateActions_WithDifferent_ShouldBuildChangeEnumValue } @Test - public void buildPlainEnumUpdateActions_WithRemovedAndDifferentOrder_ShouldBuildChangeOrderAndRemoveActions() { + public void buildPlainEnumUpdateActions_WithRemovedAndDifferentOrder_ShouldBuildChangeOrderAction() { final List> updateActions = buildEnumValuesUpdateActions( FIELD_NAME_1, ENUM_VALUES_ABC, @@ -152,7 +158,7 @@ public void buildPlainEnumUpdateActions_WithAddedEnumValueInBetween_ShouldBuildC } @Test - public void buildPlainEnumUpdateActions_WithAddedRemovedAndDifOrder_ShouldBuildAllThreeMoveEnumValueActions() { + public void buildPlainEnumUpdateActions_WithAddedRemovedAndDifOrder_ShouldBuildAddAndChangeOrderActions() { final List> updateActions = buildEnumValuesUpdateActions( FIELD_NAME_1, ENUM_VALUES_ABC, @@ -168,4 +174,18 @@ public void buildPlainEnumUpdateActions_WithAddedRemovedAndDifOrder_ShouldBuildA )) ); } + + @Test + public void buildLocalizedEnumUpdateActions_WithDuplicateEnumValues_ShouldTriggerDuplicateKeyError() { + expectedException.expect(DuplicateKeyException.class); + expectedException.expectMessage("Enum Values have duplicated keys. Definition name: " + + "'field_definition_name', Duplicated enum value: 'b'. Enum Values are expected to be unique inside " + + "their definition."); + + buildEnumValuesUpdateActions( + "field_definition_name", + ENUM_VALUES_ABC, + ENUM_VALUES_ABB + ); + } } From a4ecf2d73ef17ceecb024009d071429cc25f0505 Mon Sep 17 00:00:00 2001 From: Hesham Massoud Date: Thu, 6 Dec 2018 17:02:11 +0100 Subject: [PATCH 155/164] #300: Use query builder. --- .../sync/services/impl/ProductTypeServiceImpl.java | 2 +- .../com/commercetools/sync/services/impl/TypeServiceImpl.java | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/commercetools/sync/services/impl/ProductTypeServiceImpl.java b/src/main/java/com/commercetools/sync/services/impl/ProductTypeServiceImpl.java index 010b33751e..b310fbb319 100644 --- a/src/main/java/com/commercetools/sync/services/impl/ProductTypeServiceImpl.java +++ b/src/main/java/com/commercetools/sync/services/impl/ProductTypeServiceImpl.java @@ -154,7 +154,7 @@ public CompletionStage> fetchProductType(@Nullable final S } final ProductTypeQuery productTypeQuery = - ProductTypeQuery.of().plusPredicates(queryModel -> queryModel.key().is(key)); + ProductTypeQueryBuilder.of().plusPredicates(queryModel -> queryModel.key().is(key)).build(); return syncOptions .getCtpClient() diff --git a/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java b/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java index 77d96a7901..a7e58ad768 100644 --- a/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java +++ b/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java @@ -58,6 +58,7 @@ private CompletionStage> fetchAndCache(@Nonnull final String ke @Nonnull @Override public CompletionStage> fetchMatchingTypesByKeys(@Nonnull final Set keys) { + if (keys.isEmpty()) { return CompletableFuture.completedFuture(Collections.emptySet()); } @@ -84,7 +85,7 @@ public CompletionStage> fetchType(@Nullable final String key) { } final TypeQuery typeQuery = - TypeQuery.of().plusPredicates(queryModel -> queryModel.key().is(key)); + TypeQueryBuilder.of().plusPredicates(queryModel -> queryModel.key().is(key)).build(); return syncOptions .getCtpClient() From 0c1e47f24b5eb8a230931d68ca1ba037545e443b Mon Sep 17 00:00:00 2001 From: Hesham Massoud Date: Thu, 6 Dec 2018 17:02:21 +0100 Subject: [PATCH 156/164] #300: Fix Javadocs. --- .../commercetools/sync/services/ProductTypeService.java | 2 +- .../java/com/commercetools/sync/services/TypeService.java | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/commercetools/sync/services/ProductTypeService.java b/src/main/java/com/commercetools/sync/services/ProductTypeService.java index d394fe6f5f..2e4b669913 100644 --- a/src/main/java/com/commercetools/sync/services/ProductTypeService.java +++ b/src/main/java/com/commercetools/sync/services/ProductTypeService.java @@ -73,7 +73,7 @@ CompletionStage>> fetchCachedProductAttr * Given a resource draft of type {@link ProductTypeDraft}, this method attempts to create a resource * {@link ProductType} based on it in the CTP project defined by the sync options. * - *

A completion stage containing an empty option and the error callback will be triggered in those cases: + *

A completion stage containing an empty optional and the error callback will be triggered in those cases: *

    *
  • the draft has a blank key
  • *
  • the create request fails on CTP
  • diff --git a/src/main/java/com/commercetools/sync/services/TypeService.java b/src/main/java/com/commercetools/sync/services/TypeService.java index 956e894b0c..2ebce77ad7 100644 --- a/src/main/java/com/commercetools/sync/services/TypeService.java +++ b/src/main/java/com/commercetools/sync/services/TypeService.java @@ -52,7 +52,7 @@ public interface TypeService { * returned in the returned future. * * @param key the key of the type to fetch. - * @return {@link CompletionStage}<{@link Optional}> in which the result of it's completion contains an + * @return {@link CompletionStage}<{@link Optional}> in which the result of its completion contains an * {@link Optional} that contains the matching {@link Type} if exists, otherwise empty. */ @Nonnull @@ -62,14 +62,14 @@ public interface TypeService { * Given a resource draft of type {@link TypeDraft}, this method attempts to create a resource * {@link Type} based on it in the CTP project defined by the sync options. * - *

    A completion stage containing an empty option and the error callback will be triggered in those cases: + *

    A completion stage containing an empty optional and the error callback will be triggered in those cases: *

      *
    • the draft has a blank key
    • *
    • the create request fails on CTP
    • *
    * *

    On the other hand, if the resource gets created successfully on CTP, then the created resource's id and - * key are cached and the method returns a {@link CompletionStage} in which the result of it's completion + * key are cached and the method returns a {@link CompletionStage} in which the result of its completion * contains an instance {@link Optional} of the resource which was created. * * @param typeDraft the resource draft to create a resource based off of. @@ -83,7 +83,7 @@ public interface TypeService { * Given a {@link Type} and a {@link List}<{@link UpdateAction}<{@link Type}>>, this method * issues an update request with these update actions on this {@link Type} in the CTP project defined in an * injected {@link io.sphere.sdk.client.SphereClient}. This method returns - * {@link CompletionStage}<{@link Type}> in which the result of it's completion contains an instance of + * {@link CompletionStage}<{@link Type}> in which the result of its completion contains an instance of * the {@link Type} which was updated in the CTP project. * * @param type the {@link Type} to update. From 24dca2c729b045832f51f788586f6a59177fbcd3 Mon Sep 17 00:00:00 2001 From: Hesham Massoud Date: Thu, 6 Dec 2018 17:47:22 +0100 Subject: [PATCH 157/164] #300: Change fixtures class name, package and contents. --- .../sync/types/FieldDefinitionTestHelper.java | 69 ------------------- .../types/utils/FieldDefinitionFixtures.java | 35 ++++++++++ 2 files changed, 35 insertions(+), 69 deletions(-) delete mode 100644 src/test/java/com/commercetools/sync/types/FieldDefinitionTestHelper.java create mode 100644 src/test/java/com/commercetools/sync/types/utils/FieldDefinitionFixtures.java diff --git a/src/test/java/com/commercetools/sync/types/FieldDefinitionTestHelper.java b/src/test/java/com/commercetools/sync/types/FieldDefinitionTestHelper.java deleted file mode 100644 index c2c2b730e6..0000000000 --- a/src/test/java/com/commercetools/sync/types/FieldDefinitionTestHelper.java +++ /dev/null @@ -1,69 +0,0 @@ -package com.commercetools.sync.types; - -import io.sphere.sdk.categories.Category; -import io.sphere.sdk.models.EnumValue; -import io.sphere.sdk.models.LocalizedString; -import io.sphere.sdk.models.TextInputHint; -import io.sphere.sdk.types.EnumFieldType; -import io.sphere.sdk.types.FieldDefinition; -import io.sphere.sdk.types.LocalizedStringFieldType; -import io.sphere.sdk.types.ReferenceFieldType; -import io.sphere.sdk.types.SetFieldType; -import io.sphere.sdk.types.StringFieldType; - -import java.util.Arrays; -import java.util.List; - -public final class FieldDefinitionTestHelper { - - public static FieldDefinition stringFieldDefinition(final String fieldName, - final String labelEng, - boolean required, - final TextInputHint hint) { - return FieldDefinition.of(StringFieldType.of(), - fieldName, - LocalizedString.ofEnglish(labelEng), - required, - hint); - } - - public static FieldDefinition localizedStringFieldDefinition(final String fieldName, - final String labelEng, - boolean required, - final TextInputHint hint) { - return FieldDefinition.of(LocalizedStringFieldType.of(), - fieldName, - LocalizedString.ofEnglish(labelEng), - required, - hint); - } - - public static FieldDefinition stateFieldDefinition() { - final List values = Arrays.asList( - EnumValue.of("published", "the category is publicly visible"), - EnumValue.of("draft", "the category should not be displayed in the frontend") - ); - final boolean required = false; - final LocalizedString label = LocalizedString.ofEnglish("state of the category concerning to show it publicly"); - final String fieldName = "state"; - return FieldDefinition - .of(EnumFieldType.of(values), fieldName, label, required); - } - - public static FieldDefinition imageUrlFieldDefinition() { - final LocalizedString imageUrlLabel = - LocalizedString.ofEnglish("absolute url to an image to display for the category"); - return FieldDefinition - .of(StringFieldType.of(), "imageUrl", imageUrlLabel, false, TextInputHint.SINGLE_LINE); - } - - public static FieldDefinition relatedCategoriesFieldDefinition() { - final LocalizedString relatedCategoriesLabel = - LocalizedString.ofEnglish("categories to suggest products similar to the current category"); - //referenceTypeId is required to refere to categories - final String referenceTypeId = Category.referenceTypeId(); - final SetFieldType setType = SetFieldType.of(ReferenceFieldType.of(referenceTypeId)); - return FieldDefinition - .of(setType, "relatedCategories", relatedCategoriesLabel, false); - } -} diff --git a/src/test/java/com/commercetools/sync/types/utils/FieldDefinitionFixtures.java b/src/test/java/com/commercetools/sync/types/utils/FieldDefinitionFixtures.java new file mode 100644 index 0000000000..da3189bd6b --- /dev/null +++ b/src/test/java/com/commercetools/sync/types/utils/FieldDefinitionFixtures.java @@ -0,0 +1,35 @@ +package com.commercetools.sync.types; + +import io.sphere.sdk.models.LocalizedString; +import io.sphere.sdk.models.TextInputHint; +import io.sphere.sdk.types.FieldDefinition; +import io.sphere.sdk.types.LocalizedStringFieldType; +import io.sphere.sdk.types.StringFieldType; + +public final class FieldDefinitionTestHelper { + + public static FieldDefinition stringFieldDefinition(final String fieldName, + final String labelEng, + boolean required, + final TextInputHint hint) { + return FieldDefinition.of(StringFieldType.of(), + fieldName, + LocalizedString.ofEnglish(labelEng), + required, + hint); + } + + public static FieldDefinition localizedStringFieldDefinition(final String fieldName, + final String labelEng, + boolean required, + final TextInputHint hint) { + return FieldDefinition.of(LocalizedStringFieldType.of(), + fieldName, + LocalizedString.ofEnglish(labelEng), + required, + hint); + } + + private FieldDefinitionTestHelper() { + } +} From 7f6912c3effa0ff0a9c247c25a7d4b1c0a2cb53c Mon Sep 17 00:00:00 2001 From: Hesham Massoud Date: Thu, 6 Dec 2018 17:47:40 +0100 Subject: [PATCH 158/164] #300: Avoid star import for FieldDefinitionFixtures. --- config/checkstyle/checkstyle.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml index 27cd1caf37..f1b87f28f3 100644 --- a/config/checkstyle/checkstyle.xml +++ b/config/checkstyle/checkstyle.xml @@ -72,7 +72,8 @@ + com.commercetools.sync.commons.utils.PlainEnumValueFixtures, + com.commercetools.sync.types.utils.FieldDefinitionFixtures"/> From 9c96839e9b4021dfe7fe5b1bc26fbbe79e222e5a Mon Sep 17 00:00:00 2001 From: Hesham Massoud Date: Thu, 6 Dec 2018 17:48:17 +0100 Subject: [PATCH 159/164] #300: Add unit tests for mixed cases. --- .../utils/LocalizedEnumValueFixtures.java | 4 ++++ .../commons/utils/PlainEnumValueFixtures.java | 2 ++ .../BuildLocalizedEnumUpdateActionsTest.java | 19 +++++++++++++++ .../BuildPlainEnumUpdateActionsTest.java | 24 +++++++++++++++---- 4 files changed, 44 insertions(+), 5 deletions(-) diff --git a/src/test/java/com/commercetools/sync/commons/utils/LocalizedEnumValueFixtures.java b/src/test/java/com/commercetools/sync/commons/utils/LocalizedEnumValueFixtures.java index 02daedaeed..8615a5d8f3 100644 --- a/src/test/java/com/commercetools/sync/commons/utils/LocalizedEnumValueFixtures.java +++ b/src/test/java/com/commercetools/sync/commons/utils/LocalizedEnumValueFixtures.java @@ -19,6 +19,10 @@ public final class LocalizedEnumValueFixtures { public static final List ENUM_VALUES_ABC = unmodifiableList(asList(ENUM_VALUE_A, ENUM_VALUE_B, ENUM_VALUE_C)); + public static final List ENUM_VALUES_BAC = + unmodifiableList(asList(ENUM_VALUE_B, ENUM_VALUE_A, ENUM_VALUE_C)); + public static final List ENUM_VALUES_AB_WITH_DIFFERENT_LABEL = + unmodifiableList(asList(ENUM_VALUE_A_DIFFERENT_LABEL, ENUM_VALUE_B)); public static final List ENUM_VALUES_AB = unmodifiableList(asList(ENUM_VALUE_A, ENUM_VALUE_B)); public static final List ENUM_VALUES_ABB = diff --git a/src/test/java/com/commercetools/sync/commons/utils/PlainEnumValueFixtures.java b/src/test/java/com/commercetools/sync/commons/utils/PlainEnumValueFixtures.java index 297062a83c..a9c8c07c68 100644 --- a/src/test/java/com/commercetools/sync/commons/utils/PlainEnumValueFixtures.java +++ b/src/test/java/com/commercetools/sync/commons/utils/PlainEnumValueFixtures.java @@ -19,6 +19,8 @@ public final class PlainEnumValueFixtures { unmodifiableList(asList(ENUM_VALUE_A, ENUM_VALUE_B, ENUM_VALUE_C)); public static final List ENUM_VALUES_AB = unmodifiableList(asList(ENUM_VALUE_A, ENUM_VALUE_B)); + public static final List ENUM_VALUES_BAC = + unmodifiableList(asList(ENUM_VALUE_B, ENUM_VALUE_A, ENUM_VALUE_C)); public static final List ENUM_VALUES_AB_WITH_DIFFERENT_LABEL = unmodifiableList(asList(ENUM_VALUE_A_DIFFERENT_LABEL, ENUM_VALUE_B)); public static final List ENUM_VALUES_ABB = diff --git a/src/test/java/com/commercetools/sync/producttypes/utils/producttypeactionutils/BuildLocalizedEnumUpdateActionsTest.java b/src/test/java/com/commercetools/sync/producttypes/utils/producttypeactionutils/BuildLocalizedEnumUpdateActionsTest.java index 0538364130..4fe65ae4dd 100644 --- a/src/test/java/com/commercetools/sync/producttypes/utils/producttypeactionutils/BuildLocalizedEnumUpdateActionsTest.java +++ b/src/test/java/com/commercetools/sync/producttypes/utils/producttypeactionutils/BuildLocalizedEnumUpdateActionsTest.java @@ -160,6 +160,25 @@ public void buildLocalizedEnumUpdateActions_WithDifferent_ShouldBuildChangeEnumV ); } + @Test + public void buildPlainEnumUpdateActions_WithMixedCase_ShouldBuildChangeEnumValueOrderAction() { + final String attributeDefinitionName = "attribute_definition_name_1"; + final List> updateActions = buildLocalizedEnumValuesUpdateActions( + attributeDefinitionName, + ENUM_VALUES_BAC, + ENUM_VALUES_AB_WITH_DIFFERENT_LABEL + ); + + assertThat(updateActions).containsExactly( + RemoveEnumValues.of(attributeDefinitionName, ENUM_VALUE_C.getKey()), + ChangeLocalizedEnumValueLabel.of(attributeDefinitionName, ENUM_VALUE_A_DIFFERENT_LABEL), + ChangeLocalizedEnumValueOrder.of(attributeDefinitionName, asList( + ENUM_VALUE_A_DIFFERENT_LABEL, + ENUM_VALUE_B + )) + ); + } + @Test public void buildLocalizedEnumUpdateActions_WithRemovedAndDifferentOrder_ShouldBuildChangeOrderAndRemoveActions() { final List> updateActions = buildLocalizedEnumValuesUpdateActions( diff --git a/src/test/java/com/commercetools/sync/producttypes/utils/producttypeactionutils/BuildPlainEnumUpdateActionsTest.java b/src/test/java/com/commercetools/sync/producttypes/utils/producttypeactionutils/BuildPlainEnumUpdateActionsTest.java index df54878965..79bdb75e70 100644 --- a/src/test/java/com/commercetools/sync/producttypes/utils/producttypeactionutils/BuildPlainEnumUpdateActionsTest.java +++ b/src/test/java/com/commercetools/sync/producttypes/utils/producttypeactionutils/BuildPlainEnumUpdateActionsTest.java @@ -89,7 +89,6 @@ public void buildPlainEnumUpdateActions_WithIdenticalPlainEnum_ShouldNotBuildUpd @Rule public ExpectedException expectedException = ExpectedException.none(); - @Test public void buildPlainEnumUpdateActions_WithDuplicatePlainEnumValues_ShouldTriggerDuplicateKeyError() { expectedException.expect(DuplicateKeyException.class); @@ -104,7 +103,6 @@ public void buildPlainEnumUpdateActions_WithDuplicatePlainEnumValues_ShouldTrigg ); } - @Test public void buildPlainEnumUpdateActions_WithOneMissingPlainEnumValue_ShouldBuildRemoveEnumValueAction() { final List> updateActions = buildEnumValuesUpdateActions( @@ -162,6 +160,25 @@ public void buildPlainEnumUpdateActions_WithDifferent_ShouldBuildChangeEnumValue ); } + @Test + public void buildPlainEnumUpdateActions_WithMixedCase_ShouldBuildChangeEnumValueOrderAction() { + final String attributeDefinitionName = "attribute_definition_name_1"; + final List> updateActions = buildEnumValuesUpdateActions( + attributeDefinitionName, + ENUM_VALUES_BAC, + ENUM_VALUES_AB_WITH_DIFFERENT_LABEL + ); + + assertThat(updateActions).containsExactly( + RemoveEnumValues.of(attributeDefinitionName, ENUM_VALUE_C.getKey()), + ChangePlainEnumValueLabel.of(attributeDefinitionName, ENUM_VALUE_A_DIFFERENT_LABEL), + ChangeEnumValueOrder.of(attributeDefinitionName, asList( + ENUM_VALUE_A_DIFFERENT_LABEL, + ENUM_VALUE_B + )) + ); + } + @Test public void buildPlainEnumUpdateActions_WithRemovedAndDifferentOrder_ShouldBuildChangeOrderAndRemoveActions() { final List> updateActions = buildEnumValuesUpdateActions( @@ -249,7 +266,6 @@ public void buildPlainEnumUpdateActions_WithDifferentLabels_ShouldReturnChangeLa ); } - @Test public void buildPlainEnumUpdateActions_WithSameLabels_ShouldNotReturnChangeLabelAction() { final List> updateActions = buildEnumValueUpdateActions( @@ -262,6 +278,4 @@ public void buildPlainEnumUpdateActions_WithSameLabels_ShouldNotReturnChangeLabe ChangePlainEnumValueLabel.of("attribute_definition_name_1", ENUM_VALUE_A) ); } - - } From b7d4e54fc33e57a403fdc88650f4631cc8aac8ef Mon Sep 17 00:00:00 2001 From: Hesham Massoud Date: Thu, 6 Dec 2018 17:48:43 +0100 Subject: [PATCH 160/164] #300: Move fixtures into fixtures class. --- ...BuildFieldDefinitionUpdateActionsTest.java | 54 ++---------- .../types/utils/FieldDefinitionFixtures.java | 84 ++++++++++++++----- .../FieldDefinitionUpdateActionUtilsTest.java | 2 +- 3 files changed, 71 insertions(+), 69 deletions(-) diff --git a/src/test/java/com/commercetools/sync/types/utils/BuildFieldDefinitionUpdateActionsTest.java b/src/test/java/com/commercetools/sync/types/utils/BuildFieldDefinitionUpdateActionsTest.java index 1a72f9c408..bed9af119c 100644 --- a/src/test/java/com/commercetools/sync/types/utils/BuildFieldDefinitionUpdateActionsTest.java +++ b/src/test/java/com/commercetools/sync/types/utils/BuildFieldDefinitionUpdateActionsTest.java @@ -23,8 +23,7 @@ import java.util.ArrayList; import java.util.List; -import static com.commercetools.sync.types.FieldDefinitionTestHelper.localizedStringFieldDefinition; -import static com.commercetools.sync.types.FieldDefinitionTestHelper.stringFieldDefinition; +import static com.commercetools.sync.types.utils.FieldDefinitionFixtures.*; import static com.commercetools.sync.types.utils.TypeUpdateActionUtils.buildFieldDefinitionsUpdateActions; import static io.sphere.sdk.json.SphereJsonUtils.readObjectFromResource; import static java.util.Arrays.asList; @@ -36,53 +35,6 @@ import static org.mockito.Mockito.when; public class BuildFieldDefinitionUpdateActionsTest { - private static final String RES_ROOT = - "com/commercetools/sync/types/utils/updatefielddefinitions/fields/"; - - private static final String TYPE_WITH_FIELDS_AB = - RES_ROOT + "type-with-field-definitions-ab.json"; - private static final String TYPE_WITH_FIELDS_ABB = - RES_ROOT + "type-with-field-definitions-abb.json"; - private static final String TYPE_WITH_FIELDS_ABC = - RES_ROOT + "type-with-field-definitions-abc.json"; - private static final String TYPE_WITH_FIELDS_ABCD = - RES_ROOT + "type-with-field-definitions-abcd.json"; - private static final String TYPE_WITH_FIELDS_ABD = - RES_ROOT + "type-with-field-definitions-abd.json"; - private static final String TYPE_WITH_FIELDS_CAB = - RES_ROOT + "type-with-field-definitions-cab.json"; - private static final String TYPE_WITH_FIELDS_CB = - RES_ROOT + "type-with-field-definitions-cb.json"; - private static final String TYPE_WITH_FIELDS_ACBD = - RES_ROOT + "type-with-field-definitions-acbd.json"; - private static final String TYPE_WITH_FIELDS_ADBC = - RES_ROOT + "type-with-field-definitions-adbc.json"; - private static final String TYPE_WITH_FIELDS_CBD = - RES_ROOT + "type-with-field-definitions-cbd.json"; - private static final String TYPE_WITH_FIELDS_ABC_WITH_DIFFERENT_TYPE = - RES_ROOT + "type-with-field-definitions-abc-with-different-type.json"; - - private static final TypeSyncOptions SYNC_OPTIONS = TypeSyncOptionsBuilder - .of(mock(SphereClient.class)) - .build(); - - private static final String FIELD_A = "a"; - private static final String FIELD_B = "b"; - private static final String FIELD_C = "c"; - private static final String FIELD_D = "d"; - private static final String LABEL_EN = "label_en"; - - private static final FieldDefinition FIELD_DEFINITION_A = - stringFieldDefinition(FIELD_A, LABEL_EN, false, TextInputHint.SINGLE_LINE); - private static final FieldDefinition FIELD_DEFINITION_A_LOCALIZED_TYPE = - localizedStringFieldDefinition(FIELD_A, LABEL_EN, false, TextInputHint.SINGLE_LINE); - private static final FieldDefinition FIELD_DEFINITION_B = - stringFieldDefinition(FIELD_B, LABEL_EN, false, TextInputHint.SINGLE_LINE); - private static final FieldDefinition FIELD_DEFINITION_C = - stringFieldDefinition(FIELD_C, LABEL_EN, false, TextInputHint.SINGLE_LINE); - private static final FieldDefinition FIELD_DEFINITION_D = - stringFieldDefinition(FIELD_D, LABEL_EN, false, TextInputHint.SINGLE_LINE); - private static final String TYPE_KEY = "key"; private static final LocalizedString TYPE_NAME = LocalizedString.ofEnglish("name"); private static final LocalizedString TYPE_DESCRIPTION = LocalizedString.ofEnglish("description"); @@ -94,6 +46,10 @@ public class BuildFieldDefinitionUpdateActionsTest { .description(TYPE_DESCRIPTION) .build(); + private static final TypeSyncOptions SYNC_OPTIONS = TypeSyncOptionsBuilder + .of(mock(SphereClient.class)) + .build(); + @Test public void buildFieldDefinitionsUpdateActions_WithNullNewFieldDefinitionsAndExistingFieldDefinitions_ShouldBuild3RemoveActions() { final Type oldType = readObjectFromResource(TYPE_WITH_FIELDS_ABC, Type.class); diff --git a/src/test/java/com/commercetools/sync/types/utils/FieldDefinitionFixtures.java b/src/test/java/com/commercetools/sync/types/utils/FieldDefinitionFixtures.java index da3189bd6b..6644124a47 100644 --- a/src/test/java/com/commercetools/sync/types/utils/FieldDefinitionFixtures.java +++ b/src/test/java/com/commercetools/sync/types/utils/FieldDefinitionFixtures.java @@ -1,4 +1,4 @@ -package com.commercetools.sync.types; +package com.commercetools.sync.types.utils; import io.sphere.sdk.models.LocalizedString; import io.sphere.sdk.models.TextInputHint; @@ -6,30 +6,76 @@ import io.sphere.sdk.types.LocalizedStringFieldType; import io.sphere.sdk.types.StringFieldType; -public final class FieldDefinitionTestHelper { +import javax.annotation.Nonnull; +import javax.annotation.Nullable; - public static FieldDefinition stringFieldDefinition(final String fieldName, - final String labelEng, - boolean required, - final TextInputHint hint) { +public final class FieldDefinitionFixtures { + private static final String RES_ROOT = + "com/commercetools/sync/types/utils/updatefielddefinitions/fields/"; + + static final String TYPE_WITH_FIELDS_AB = + RES_ROOT + "type-with-field-definitions-ab.json"; + static final String TYPE_WITH_FIELDS_ABB = + RES_ROOT + "type-with-field-definitions-abb.json"; + static final String TYPE_WITH_FIELDS_ABC = + RES_ROOT + "type-with-field-definitions-abc.json"; + static final String TYPE_WITH_FIELDS_ABCD = + RES_ROOT + "type-with-field-definitions-abcd.json"; + static final String TYPE_WITH_FIELDS_ABD = + RES_ROOT + "type-with-field-definitions-abd.json"; + static final String TYPE_WITH_FIELDS_CAB = + RES_ROOT + "type-with-field-definitions-cab.json"; + static final String TYPE_WITH_FIELDS_CB = + RES_ROOT + "type-with-field-definitions-cb.json"; + static final String TYPE_WITH_FIELDS_ACBD = + RES_ROOT + "type-with-field-definitions-acbd.json"; + static final String TYPE_WITH_FIELDS_ADBC = + RES_ROOT + "type-with-field-definitions-adbc.json"; + static final String TYPE_WITH_FIELDS_CBD = + RES_ROOT + "type-with-field-definitions-cbd.json"; + static final String TYPE_WITH_FIELDS_ABC_WITH_DIFFERENT_TYPE = + RES_ROOT + "type-with-field-definitions-abc-with-different-type.json"; + + static final String FIELD_A = "a"; + static final String FIELD_B = "b"; + static final String FIELD_C = "c"; + static final String FIELD_D = "d"; + static final String LABEL_EN = "label_en"; + + static final FieldDefinition FIELD_DEFINITION_A = + stringFieldDefinition(FIELD_A, LABEL_EN, false, TextInputHint.SINGLE_LINE); + static final FieldDefinition FIELD_DEFINITION_A_LOCALIZED_TYPE = + localizedStringFieldDefinition(FIELD_A, LABEL_EN, false, TextInputHint.SINGLE_LINE); + static final FieldDefinition FIELD_DEFINITION_B = + stringFieldDefinition(FIELD_B, LABEL_EN, false, TextInputHint.SINGLE_LINE); + static final FieldDefinition FIELD_DEFINITION_C = + stringFieldDefinition(FIELD_C, LABEL_EN, false, TextInputHint.SINGLE_LINE); + static final FieldDefinition FIELD_DEFINITION_D = + stringFieldDefinition(FIELD_D, LABEL_EN, false, TextInputHint.SINGLE_LINE); + + static FieldDefinition stringFieldDefinition(@Nonnull final String fieldName, + @Nonnull final String labelEng, + boolean required, + @Nullable final TextInputHint hint) { return FieldDefinition.of(StringFieldType.of(), - fieldName, - LocalizedString.ofEnglish(labelEng), - required, - hint); + fieldName, + LocalizedString.ofEnglish(labelEng), + required, + hint); } - public static FieldDefinition localizedStringFieldDefinition(final String fieldName, - final String labelEng, - boolean required, - final TextInputHint hint) { + + private static FieldDefinition localizedStringFieldDefinition(@Nonnull final String fieldName, + @Nonnull final String labelEng, + boolean required, + @Nullable final TextInputHint hint) { return FieldDefinition.of(LocalizedStringFieldType.of(), - fieldName, - LocalizedString.ofEnglish(labelEng), - required, - hint); + fieldName, + LocalizedString.ofEnglish(labelEng), + required, + hint); } - private FieldDefinitionTestHelper() { + private FieldDefinitionFixtures() { } } diff --git a/src/test/java/com/commercetools/sync/types/utils/FieldDefinitionUpdateActionUtilsTest.java b/src/test/java/com/commercetools/sync/types/utils/FieldDefinitionUpdateActionUtilsTest.java index 584b679b8e..da7bff7e18 100644 --- a/src/test/java/com/commercetools/sync/types/utils/FieldDefinitionUpdateActionUtilsTest.java +++ b/src/test/java/com/commercetools/sync/types/utils/FieldDefinitionUpdateActionUtilsTest.java @@ -18,7 +18,7 @@ import java.util.List; import java.util.Optional; -import static com.commercetools.sync.types.FieldDefinitionTestHelper.stringFieldDefinition; +import static com.commercetools.sync.types.utils.FieldDefinitionFixtures.stringFieldDefinition; import static com.commercetools.sync.types.utils.FieldDefinitionUpdateActionUtils.buildActions; import static com.commercetools.sync.types.utils.FieldDefinitionUpdateActionUtils.buildChangeLabelUpdateAction; import static io.sphere.sdk.models.LocalizedString.ofEnglish; From 767825f32c6af27692703a1660365b43b6a7d135 Mon Sep 17 00:00:00 2001 From: Hesham Massoud Date: Thu, 6 Dec 2018 17:48:56 +0100 Subject: [PATCH 161/164] #300: Remove unneeded field definitions from test. --- .../types/utils/TypeUpdateActionUtilsTest.java | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/src/test/java/com/commercetools/sync/types/utils/TypeUpdateActionUtilsTest.java b/src/test/java/com/commercetools/sync/types/utils/TypeUpdateActionUtilsTest.java index 63a24d6bb6..62cb5d4b66 100644 --- a/src/test/java/com/commercetools/sync/types/utils/TypeUpdateActionUtilsTest.java +++ b/src/test/java/com/commercetools/sync/types/utils/TypeUpdateActionUtilsTest.java @@ -2,7 +2,6 @@ import io.sphere.sdk.commands.UpdateAction; import io.sphere.sdk.models.LocalizedString; -import io.sphere.sdk.types.FieldDefinition; import io.sphere.sdk.types.ResourceTypeIdsSetBuilder; import io.sphere.sdk.types.Type; import io.sphere.sdk.types.TypeDraft; @@ -12,14 +11,9 @@ import org.junit.BeforeClass; import org.junit.Test; -import java.util.Arrays; -import java.util.List; import java.util.Optional; import java.util.Set; -import static com.commercetools.sync.types.FieldDefinitionTestHelper.imageUrlFieldDefinition; -import static com.commercetools.sync.types.FieldDefinitionTestHelper.relatedCategoriesFieldDefinition; -import static com.commercetools.sync.types.FieldDefinitionTestHelper.stateFieldDefinition; import static com.commercetools.sync.types.utils.TypeUpdateActionUtils.buildChangeNameUpdateAction; import static com.commercetools.sync.types.utils.TypeUpdateActionUtils.buildSetDescriptionUpdateAction; import static org.assertj.core.api.Assertions.assertThat; @@ -44,21 +38,13 @@ public static void setup() { .addCategories() .build(); - final List fieldDefinitions = - Arrays.asList(stateFieldDefinition(), - imageUrlFieldDefinition(), - relatedCategoriesFieldDefinition()); - - old = mock(Type.class); when(old.getKey()).thenReturn(key); when(old.getName()).thenReturn(name); when(old.getDescription()).thenReturn(desc); - when(old.getFieldDefinitions()).thenReturn(fieldDefinitions); newSame = TypeDraftBuilder.of(key, name, resourceTypeIds) .description(desc) - .fieldDefinitions(fieldDefinitions) .build(); @@ -66,7 +52,6 @@ public static void setup() { LocalizedString.ofEnglish("type for standard categories 2"), resourceTypeIds) .description(LocalizedString.ofEnglish("description for category custom type 2")) - .fieldDefinitions(fieldDefinitions) .build(); } From 98abc39c99ae8432c99813af559a59dc84c97ab0 Mon Sep 17 00:00:00 2001 From: Hesham Massoud Date: Thu, 6 Dec 2018 18:26:37 +0100 Subject: [PATCH 162/164] #300: Reword notes. --- docs/usage/PRODUCT_TYPE_SYNC.md | 7 +++++-- docs/usage/TYPE_SYNC.md | 4 +++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/docs/usage/PRODUCT_TYPE_SYNC.md b/docs/usage/PRODUCT_TYPE_SYNC.md index 3f0c69bdda..b0c03d6c34 100644 --- a/docs/usage/PRODUCT_TYPE_SYNC.md +++ b/docs/usage/PRODUCT_TYPE_SYNC.md @@ -68,8 +68,11 @@ __Note__ The statistics object contains the processing time of the last batch on 2. It is not known by the sync which batch is going to be the last one supplied. #### Important to Note -1. If two matching `attributeDefinition`s on the matching `productType`s have a different `AttributeType`, the sync will -**remove** the existing `attributeDefinition` and then **add** a new `attributeDefinition` with the new `AttributeType`. + +1. If two matching `attributeDefinition`s (old and new) on the matching `productType`s (old and new) have a different `AttributeType`, the sync will +**remove** the existing `attributeDefinition` and then **add** a new `attributeDefinition` with the new `AttributeType`. + +2. The `attributeDefinition` for which the `AttributeType` is not defined (`null`) will not be synced. #### More examples of how to use the sync diff --git a/docs/usage/TYPE_SYNC.md b/docs/usage/TYPE_SYNC.md index 6574e8881e..2b572c2a3b 100644 --- a/docs/usage/TYPE_SYNC.md +++ b/docs/usage/TYPE_SYNC.md @@ -65,8 +65,10 @@ __Note__ The statistics object contains the processing time of the last batch on 2. It is not known by the sync which batch is going to be the last one supplied. #### Important to Note -1. If two matching `fieldDefinition`s on the matching `type`s have a different `FieldType`, the sync will +1. If two matching `fieldDefinition`s (old and new) on the matching `type`s (old and new) have a different `FieldType`, the sync will **remove** the existing `fieldDefinition` and then **add** a new `fieldDefinition` with the new `FieldType`. + +2. The `fieldDefinition` for which the `fieldType` is not defined (`null`) will not be synced. #### More examples of how to use the sync From 7d205287d6f5a7d07794f23a9f16ec2cbcbbbe03 Mon Sep 17 00:00:00 2001 From: aoz Date: Thu, 6 Dec 2018 18:31:20 +0100 Subject: [PATCH 163/164] #300 fix checksStyles --- .../FieldDefinitionsUpdateActionUtils.java | 4 +-- ...BuildFieldDefinitionUpdateActionsTest.java | 28 +++++++++---------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionsUpdateActionUtils.java b/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionsUpdateActionUtils.java index 5146813cd6..2f352c539b 100644 --- a/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionsUpdateActionUtils.java +++ b/src/main/java/com/commercetools/sync/types/utils/FieldDefinitionsUpdateActionUtils.java @@ -139,8 +139,8 @@ private static List> buildRemoveFieldDefinitionOrFieldDefinit final Map newFieldDefinitionsNameMap = newFieldDefinitions .stream().collect( - toMap(FieldDefinition::getName, fieldDefinition -> fieldDefinition, - (fieldDefinitionA, fieldDefinitionB) -> { + toMap(FieldDefinition::getName, fieldDefinition -> fieldDefinition, + (fieldDefinitionA, fieldDefinitionB) -> { throw new DuplicateNameException(format("Field definitions have duplicated names. " + "Duplicated field definition name: '%s'. Field definitions names are " + "expected to be unique inside their type.", fieldDefinitionA.getName())); diff --git a/src/test/java/com/commercetools/sync/types/utils/BuildFieldDefinitionUpdateActionsTest.java b/src/test/java/com/commercetools/sync/types/utils/BuildFieldDefinitionUpdateActionsTest.java index bed9af119c..17e8e8f92c 100644 --- a/src/test/java/com/commercetools/sync/types/utils/BuildFieldDefinitionUpdateActionsTest.java +++ b/src/test/java/com/commercetools/sync/types/utils/BuildFieldDefinitionUpdateActionsTest.java @@ -51,7 +51,7 @@ public class BuildFieldDefinitionUpdateActionsTest { .build(); @Test - public void buildFieldDefinitionsUpdateActions_WithNullNewFieldDefinitionsAndExistingFieldDefinitions_ShouldBuild3RemoveActions() { + public void buildFieldDefinitionsUpdateActions_WithNullNewFieldDefAndExistingFieldDefs_ShouldBuild3RemoveActions() { final Type oldType = readObjectFromResource(TYPE_WITH_FIELDS_ABC, Type.class); final List> updateActions = buildFieldDefinitionsUpdateActions( @@ -68,7 +68,7 @@ public void buildFieldDefinitionsUpdateActions_WithNullNewFieldDefinitionsAndExi } @Test - public void buildFieldDefinitionsUpdateActions_WithNullNewFieldDefinitionsAndNoOldFieldDefinitions_ShouldNotBuildActions() { + public void buildFieldDefinitionsUpdateActions_WithNullNewFieldDefsAndNoOldFieldDefs_ShouldNotBuildActions() { final Type oldType = mock(Type.class); when(oldType.getFieldDefinitions()).thenReturn(emptyList()); when(oldType.getKey()).thenReturn("type_key_1"); @@ -83,7 +83,7 @@ public void buildFieldDefinitionsUpdateActions_WithNullNewFieldDefinitionsAndNoO } @Test - public void buildFieldDefinitionsUpdateActions_WithNewFieldDefinitionsAndNoOldFieldDefinitions_ShouldBuild3AddActions() { + public void buildFieldDefinitionsUpdateActions_WithNewFieldDefsAndNoOldFieldDefinitions_ShouldBuild3AddActions() { final Type oldType = mock(Type.class); when(oldType.getFieldDefinitions()).thenReturn(emptyList()); when(oldType.getKey()).thenReturn("type_key_1"); @@ -125,7 +125,7 @@ public void buildFieldDefinitionsUpdateActions_WithIdenticalFieldDefinitions_Sho } @Test - public void buildFieldDefinitionsUpdateActions_WithDuplicateFieldDefinitionNames_ShouldNotBuildActionsAndTriggerErrorCb() { + public void buildFieldDefinitionsUpdateActions_WithDuplicateFieldDefNames_ShouldNotBuildActionsAndTriggerErrorCb() { final Type oldType = readObjectFromResource(TYPE_WITH_FIELDS_ABC, Type.class); final TypeDraft newTypeDraft = readObjectFromResource( @@ -163,7 +163,7 @@ public void buildFieldDefinitionsUpdateActions_WithDuplicateFieldDefinitionNames } @Test - public void buildFieldDefinitionsUpdateActions_WithOneMissingFieldDefinition_ShouldBuildRemoveFieldDefinitionAction() { + public void buildFieldDefinitionsUpdateActions_WithOneMissingFieldDefinition_ShouldBuildRemoveFieldDefAction() { final Type oldType = readObjectFromResource(TYPE_WITH_FIELDS_ABC, Type.class); final TypeDraft newTypeDraft = readObjectFromResource( @@ -181,7 +181,7 @@ public void buildFieldDefinitionsUpdateActions_WithOneMissingFieldDefinition_Sho } @Test - public void buildFieldDefinitionsUpdateActions_WithOneExtraFieldDefinition_ShouldBuildAddFieldDefinitionAction() { + public void buildFieldDefinitionsUpdateActions_WithOneExtraFieldDef_ShouldBuildAddFieldDefinitionAction() { final Type oldType = readObjectFromResource(TYPE_WITH_FIELDS_ABC, Type.class); final TypeDraft newTypeDraft = readObjectFromResource( @@ -201,7 +201,7 @@ public void buildFieldDefinitionsUpdateActions_WithOneExtraFieldDefinition_Shoul } @Test - public void buildFieldDefinitionsUpdateActions_WithOneFieldDefinitionSwitch_ShouldBuildRemoveAndAddFieldDefinitionsActions() { + public void buildFieldDefinitionsUpdateActions_WithOneFieldDefSwitch_ShouldBuildRemoveAndAddFieldDefActions() { final Type oldType = readObjectFromResource(TYPE_WITH_FIELDS_ABC, Type.class); final TypeDraft newTypeDraft = readObjectFromResource( @@ -248,7 +248,7 @@ public void buildFieldDefinitionsUpdateActions_WithDifferent_ShouldBuildChangeFi } @Test - public void buildFieldDefinitionsUpdateActions_WithRemovedAndDifferentOrder_ShouldBuildChangeOrderAndRemoveActions() { + public void buildFieldDefinitionsUpdateActions_WithRemovedAndDiffOrder_ShouldBuildChangeOrderAndRemoveActions() { final Type oldType = readObjectFromResource(TYPE_WITH_FIELDS_ABC, Type.class); final TypeDraft newTypeDraft = readObjectFromResource( @@ -302,7 +302,7 @@ public void buildFieldDefinitionsUpdateActions_WithAddedAndDifferentOrder_Should } @Test - public void buildFieldDefinitionsUpdateActions_WithAddedFieldDefinitionInBetween_ShouldBuildChangeOrderAndAddActions() { + public void buildFieldDefinitionsUpdateActions_WithAddedFieldDefInBetween_ShouldBuildChangeOrderAndAddActions() { final Type oldType = readObjectFromResource(TYPE_WITH_FIELDS_ABC, Type.class); final TypeDraft newTypeDraft = readObjectFromResource( @@ -330,7 +330,7 @@ public void buildFieldDefinitionsUpdateActions_WithAddedFieldDefinitionInBetween } @Test - public void buildFieldDefinitionsUpdateActions_WithAddedRemovedAndDifOrder_ShouldBuildAllThreeMoveFieldDefinitionActions() { + public void buildFieldDefinitionsUpdateActions_WithMixedFields_ShouldBuildAllThreeMoveFieldDefActions() { final Type oldType = readObjectFromResource(TYPE_WITH_FIELDS_ABC, Type.class); final TypeDraft newTypeDraft = readObjectFromResource( @@ -357,7 +357,7 @@ public void buildFieldDefinitionsUpdateActions_WithAddedRemovedAndDifOrder_Shoul } @Test - public void buildFieldDefinitionsUpdateActions_WithDifferentType_ShouldRemoveOldFieldDefinitionAndAddNewFieldDefinition() { + public void buildFieldDefinitionsUpdateActions_WithDifferentType_ShouldRemoveOldFieldDefAndAddNewFieldDef() { final Type oldType = readObjectFromResource(TYPE_WITH_FIELDS_ABC, Type.class); final TypeDraft newTypeDraft = readObjectFromResource( @@ -377,7 +377,7 @@ public void buildFieldDefinitionsUpdateActions_WithDifferentType_ShouldRemoveOld } @Test - public void buildFieldDefinitionsUpdateActions_WithNullNewFieldDefinition_ShouldSkipNullFieldDefinitions() { + public void buildFieldDefinitionsUpdateActions_WithNullNewFieldDef_ShouldSkipNullFieldDefs() { // preparation final Type oldType = mock(Type.class); final FieldDefinition oldFieldDefinition = FieldDefinition.of( @@ -415,7 +415,7 @@ public void buildFieldDefinitionsUpdateActions_WithNullNewFieldDefinition_Should } @Test - public void buildFieldDefinitionsUpdateActions_WithDefinitionWithNullName_ShouldBuildChangeFieldDefinitionOrderAction() { + public void buildFieldDefinitionsUpdateActions_WithDefWithNullName_ShouldBuildChangeFieldDefOrderAction() { // preparation final Type oldType = mock(Type.class); final FieldDefinition oldFieldDefinition = FieldDefinition.of( @@ -454,7 +454,7 @@ public void buildFieldDefinitionsUpdateActions_WithDefinitionWithNullName_Should } @Test - public void buildFieldDefinitionsUpdateActions_WithDefinitionWithNullType_ShouldBuildChangeFieldDefinitionOrderAction() { + public void buildFieldDefinitionsUpdateActions_WithDefWithNullType_ShouldBuildChangeFieldDefOrderAction() { // preparation final Type oldType = mock(Type.class); final FieldDefinition oldFieldDefinition = FieldDefinition.of( From edf0981635928980f65788aa52d5701d574ec507 Mon Sep 17 00:00:00 2001 From: Hesham Massoud Date: Thu, 6 Dec 2018 18:37:11 +0100 Subject: [PATCH 164/164] #300: Fix Checkstyle issues. --- .../com/commercetools/sync/types/utils/TypeSyncUtils.java | 6 +++--- .../sync/types/utils/TypeUpdateActionUtils.java | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/commercetools/sync/types/utils/TypeSyncUtils.java b/src/main/java/com/commercetools/sync/types/utils/TypeSyncUtils.java index e5a9361a85..c55fd32300 100644 --- a/src/main/java/com/commercetools/sync/types/utils/TypeSyncUtils.java +++ b/src/main/java/com/commercetools/sync/types/utils/TypeSyncUtils.java @@ -23,14 +23,14 @@ public final class TypeSyncUtils { * result. If no update actions are needed, for example in case where both the {@link Type} and the * {@link TypeDraft} have the same fields, an empty {@link List} is returned. * - *

    - * Note: Currently this util doesn't support the following: + * + *

    Note: Currently this util doesn't support the following: *

      *
    • updating the inputHint of a FieldDefinition
    • *
    • removing the EnumValue/LocalizedEnumValue of a FieldDefinition
    • *
    • updating the label of a EnumValue/LocalizedEnumValue of a FieldDefinition
    • *
    - *

    + * * * @param oldType the {@link Type} which should be updated. * @param newType the {@link TypeDraft} where we get the new data. diff --git a/src/main/java/com/commercetools/sync/types/utils/TypeUpdateActionUtils.java b/src/main/java/com/commercetools/sync/types/utils/TypeUpdateActionUtils.java index ad64083f75..7a512fd8c9 100644 --- a/src/main/java/com/commercetools/sync/types/utils/TypeUpdateActionUtils.java +++ b/src/main/java/com/commercetools/sync/types/utils/TypeUpdateActionUtils.java @@ -63,15 +63,15 @@ public static Optional> buildSetDescriptionUpdateAction( * a list of field definitions in which a duplicate name exists, the error callback is triggered and an empty list * is returned. * - *

    - * Note: Currently this util doesn't support the following: + * + *

    Note: Currently this util doesn't support the following: *

      *
    • updating the inputHint of a FieldDefinition
    • *
    • removing the EnumValue/LocalizedEnumValue of a FieldDefinition
    • *
    • updating the label of a EnumValue/LocalizedEnumValue of a FieldDefinition
    • *
    * TODO: Check GITHUB ISSUE#339 for missing FieldDefinition update actions. - *

    + * * * @param oldType the type which should be updated. * @param newType the type draft where we get the key.