use of com.commercetools.sync.commons.exceptions.BuildUpdateActionException in project commercetools-sync-java by commercetools.
the class AssetsUpdateActionUtils method buildAssetsUpdateActionsWithNewAssetDrafts.
/**
* Compares a list of {@link Asset}s with a list of {@link AssetDraft}s. The method serves as a
* generic implementation for assets syncing. The method takes in functions for building the
* required update actions ( AddAsset, RemoveAsset, ChangeAssetOrder and 1-1 update actions on
* assets (e.g. changeAssetName, setAssetDescription, etc..) for the required resource.
*
* @param oldAssets the old list of assets.
* @param newAssetDrafts the new list of asset drafts.
* @param assetActionFactory factory responsible for building asset update actions.
* @param <T> the type of the resource the asset update actions are built for.
* @param syncOptions responsible for supplying the sync options to the sync utility method. It is
* used for triggering the warn callback within the utility
* @return a list of asset update actions on the resource of type T if the list of assets is not
* identical. Otherwise, if the assets are identical, an empty list is returned.
* @throws BuildUpdateActionException in case there are asset drafts with duplicate keys.
*/
@SuppressWarnings("unchecked")
@Nonnull
private static <T extends Resource, D> List<UpdateAction<T>> buildAssetsUpdateActionsWithNewAssetDrafts(@Nonnull final D newResource, @Nonnull final List<Asset> oldAssets, @Nonnull final List<AssetDraft> newAssetDrafts, @Nonnull final AssetActionFactory<T, D> assetActionFactory, @Nonnull final BaseSyncOptions syncOptions) throws BuildUpdateActionException {
// Asset set that has only the keys of the assets which should be removed, this is used in the
// method
// #buildChangeAssetOrderUpdateAction in order to compare the state of the asset lists after the
// remove actions
// have already been applied.
final HashSet<String> removedAssetKeys = new HashSet<>();
final Map<String, Asset> oldAssetsKeyMap = new HashMap<>();
oldAssets.forEach(asset -> {
String assetKey = asset.getKey();
if (isNotBlank(assetKey)) {
oldAssetsKeyMap.put(assetKey, asset);
} else {
syncOptions.applyWarningCallback(new SyncException(format(ASSET_KEY_NOT_SET, "id: " + asset.getId())), asset, null);
}
});
final Map<String, AssetDraft> newAssetDraftsKeyMap = new HashMap<>();
try {
newAssetDrafts.forEach(newAsset -> {
String assetKey = newAsset.getKey();
if (isNotBlank(assetKey)) {
newAssetDraftsKeyMap.merge(assetKey, newAsset, (assetDraftA, assetDraftB) -> {
throw new DuplicateKeyException("Supplied asset drafts have duplicate keys. Asset keys are" + " expected to be unique inside their container (a product variant or a category).");
});
} else {
syncOptions.applyWarningCallback(new SyncException(format(ASSET_KEY_NOT_SET, "name: " + newAsset.getName())), null, newAsset);
}
});
} catch (final DuplicateKeyException exception) {
throw new BuildUpdateActionException(exception);
}
// It is important to have a changeAssetOrder action before an addAsset action, since
// changeAssetOrder requires
// asset ids for sorting them, and new assets don't have ids yet since they are generated
// by CTP after an asset is created. Therefore, the order of update actions must be:
// 1. Remove or compare if matching.
final List<UpdateAction<T>> updateActions = buildRemoveAssetOrAssetUpdateActions(newResource, oldAssets, removedAssetKeys, newAssetDraftsKeyMap, assetActionFactory);
// 2. Compare ordering of assets and add a ChangeAssetOrder action if needed.
buildChangeAssetOrderUpdateAction(oldAssets, newAssetDrafts, removedAssetKeys, assetActionFactory).ifPresent(updateActions::add);
// For every new asset draft, If it doesn't exist in the old assets, then add an AddAsset action
// to the list
// of update actions.
updateActions.addAll(buildAddAssetUpdateActions(newAssetDrafts, oldAssetsKeyMap, assetActionFactory));
return updateActions;
}
use of com.commercetools.sync.commons.exceptions.BuildUpdateActionException in project commercetools-sync-java by commercetools.
the class FieldDefinitionsUpdateActionUtils method buildUpdateActions.
/**
* Compares a list of {@link FieldDefinition}s with a list of {@link FieldDefinition}s. 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.
*
* @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.
* @throws BuildUpdateActionException in case there are field definitions with duplicate names or
* enums duplicate keys.
*/
@Nonnull
private static List<UpdateAction<Type>> buildUpdateActions(@Nonnull final List<FieldDefinition> oldFieldDefinitions, @Nonnull final List<FieldDefinition> newFieldDefinitions) throws BuildUpdateActionException {
try {
final List<UpdateAction<Type>> updateActions = buildRemoveFieldDefinitionOrFieldDefinitionUpdateActions(oldFieldDefinitions, newFieldDefinitions);
updateActions.addAll(buildAddFieldDefinitionUpdateActions(oldFieldDefinitions, newFieldDefinitions));
buildChangeFieldDefinitionOrderUpdateAction(oldFieldDefinitions, newFieldDefinitions).ifPresent(updateActions::add);
return updateActions;
} catch (final DuplicateNameException | DuplicateKeyException exception) {
throw new BuildUpdateActionException(exception);
}
}
use of com.commercetools.sync.commons.exceptions.BuildUpdateActionException in project commercetools-sync-java by commercetools.
the class ProductVariantAttributeUpdateActionUtils method buildProductVariantAttributeUpdateAction.
/**
* Compares the attributes of a {@link AttributeDraft} and a {@link Attribute} to build either a
* {@link io.sphere.sdk.products.commands.updateactions.SetAttribute} or a {@link
* io.sphere.sdk.products.commands.updateactions.SetAttributeInAllVariants}.
*
* <p>If the attribute is sameForAll a {@link
* io.sphere.sdk.products.commands.updateactions.SetAttributeInAllVariants} is built. Otherwise, a
* {@link io.sphere.sdk.products.commands.updateactions.SetAttribute} is built.
*
* <p>If both the {@link AttributeDraft} and the {@link Attribute} have identical values, then no
* update action is needed and hence an empty {@link List} is returned.
*
* @param variantId the id of the variant of that the attribute belong to. It is used only in the
* error messages if any.
* @param oldProductVariantAttribute the {@link Attribute} which should be updated.
* @param newProductVariantAttribute the {@link AttributeDraft} where we get the new value.
* @param attributesMetaData a map of attribute name -> {@link AttributeMetaData}; which
* defines attribute information: its name and whether it has the constraint "SameForAll" or
* not.
* @return A filled optional with the update action or an empty optional if the attributes are
* identical.
* @throws BuildUpdateActionException thrown if attribute as not found in the {@code
* attributeMetaData} or if the attribute is required and the new value is null.
*/
@Nonnull
public static Optional<UpdateAction<Product>> buildProductVariantAttributeUpdateAction(final int variantId, @Nullable final Attribute oldProductVariantAttribute, @Nonnull final AttributeDraft newProductVariantAttribute, @Nonnull final Map<String, AttributeMetaData> attributesMetaData) throws BuildUpdateActionException {
final String newProductVariantAttributeName = newProductVariantAttribute.getName();
final JsonNode newProductVariantAttributeValue = newProductVariantAttribute.getValue();
final JsonNode oldProductVariantAttributeValue = oldProductVariantAttribute != null ? oldProductVariantAttribute.getValueAsJsonNode() : null;
final AttributeMetaData attributeMetaData = attributesMetaData.get(newProductVariantAttributeName);
if (attributeMetaData == null) {
final String errorMessage = format(ATTRIBUTE_NOT_IN_ATTRIBUTE_METADATA, newProductVariantAttributeName);
throw new BuildUpdateActionException(errorMessage);
}
return attributeMetaData.isSameForAll() ? buildUpdateAction(oldProductVariantAttributeValue, newProductVariantAttributeValue, () -> SetAttributeInAllVariants.of(newProductVariantAttribute, true)) : buildUpdateAction(oldProductVariantAttributeValue, newProductVariantAttributeValue, () -> SetAttribute.of(variantId, newProductVariantAttribute, true));
}
use of com.commercetools.sync.commons.exceptions.BuildUpdateActionException in project commercetools-sync-java by commercetools.
the class ProductVariantUpdateActionUtils method buildProductVariantAttributesUpdateActions.
/**
* Compares the attributes of a {@link ProductVariantDraft} and a {@link ProductVariant} to build
* either {@link io.sphere.sdk.products.commands.updateactions.SetAttribute} or {@link
* io.sphere.sdk.products.commands.updateactions.SetAttributeInAllVariants} update actions. If
* both the {@link ProductVariantDraft} and the {@link ProductVariant} have identical list of
* attributes, then no update action is needed and hence an empty {@link List} is returned.
*
* @param oldProduct the product that the variants belong to. It is used only in the error
* messages if any.
* @param newProduct the new product draft.
* @param oldProductVariant the {@link ProductVariant} which should be updated.
* @param newProductVariant the {@link ProductVariantDraft} where we get the new list of
* attributes.
* @param attributesMetaData a map of attribute name -> {@link AttributeMetaData}; which
* defines attribute information: its name, whether a value is required or not and whether it
* has the constraint "SameForAll" or not.
* @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
* ProductSyncOptions} for more info).
* @return a list that contains all the update actions needed, otherwise an empty list if no
* update actions are needed.
*/
@Nonnull
public static List<UpdateAction<Product>> buildProductVariantAttributesUpdateActions(@Nonnull final ProductProjection oldProduct, @Nonnull final ProductDraft newProduct, @Nonnull final ProductVariant oldProductVariant, @Nonnull final ProductVariantDraft newProductVariant, @Nonnull final Map<String, AttributeMetaData> attributesMetaData, @Nonnull final ProductSyncOptions syncOptions) {
final String productKey = oldProduct.getKey();
final Integer oldProductVariantId = oldProductVariant.getId();
final List<AttributeDraft> newProductVariantAttributes = newProductVariant.getAttributes();
final List<Attribute> oldProductVariantAttributes = oldProductVariant.getAttributes();
final List<UpdateAction<Product>> updateActions = buildRemoveUpdateActions(oldProductVariantAttributes, newProductVariantAttributes, Attribute::getName, AttributeDraft::getName, attribute -> {
try {
return buildUnSetAttribute(oldProductVariantId, attribute.getName(), attributesMetaData);
} catch (final BuildUpdateActionException buildUpdateActionException) {
final String errorMessage = format(FAILED_TO_BUILD_ATTRIBUTE_UPDATE_ACTION, attribute.getName(), newProductVariant.getKey(), productKey, buildUpdateActionException.getMessage());
syncOptions.applyErrorCallback(new SyncException(errorMessage, new BuildUpdateActionException(errorMessage, buildUpdateActionException)), oldProduct, newProduct, null);
return null;
}
});
final Map<String, Attribute> oldAttributesMap = collectionToMap(oldProductVariantAttributes, Attribute::getName);
emptyIfNull(newProductVariantAttributes).forEach(newAttribute -> {
if (newAttribute == null) {
final String errorMessage = format(FAILED_TO_BUILD_ATTRIBUTE_UPDATE_ACTION, null, newProductVariant.getKey(), productKey, NULL_PRODUCT_VARIANT_ATTRIBUTE);
syncOptions.applyErrorCallback(new SyncException(errorMessage, new BuildUpdateActionException(errorMessage)), oldProduct, newProduct, updateActions);
} else {
final String newAttributeName = newAttribute.getName();
final Attribute matchingOldAttribute = oldAttributesMap.get(newAttributeName);
try {
buildProductVariantAttributeUpdateAction(oldProductVariantId, matchingOldAttribute, newAttribute, attributesMetaData).ifPresent(updateActions::add);
} catch (final BuildUpdateActionException buildUpdateActionException) {
final String errorMessage = format(FAILED_TO_BUILD_ATTRIBUTE_UPDATE_ACTION, newAttributeName, newProductVariant.getKey(), productKey, buildUpdateActionException.getMessage());
syncOptions.applyErrorCallback(new SyncException(errorMessage, new BuildUpdateActionException(errorMessage, buildUpdateActionException)));
}
}
});
return updateActions;
}
use of com.commercetools.sync.commons.exceptions.BuildUpdateActionException in project commercetools-sync-java by commercetools.
the class CustomUpdateActionUtils method buildCustomUpdateActions.
/**
* Compares the {@link CustomFields} of an old resource {@code T} (for example {@link Category},
* {@link io.sphere.sdk.products.Product}, etc..), to the {@link CustomFieldsDraft}, of a new
* resource draft {@code S} (for example {@link CategoryDraft}, {@link
* io.sphere.sdk.products.ProductVariantDraft}, etc..), and returns a {@link List}<{@link
* UpdateAction}> as a result. If no update action is needed, for example in the case where
* both the {@link CustomFields} and the {@link CustomFieldsDraft} are null, an empty {@link
* List}<{@link UpdateAction}> is returned. A {@link BaseSyncOptions} instance is injected
* into the method which is responsible for supplying the sync options to the sync utility method.
* For example, custom error callbacks for errors. The {@link TypeService} is injected also for
* fetching the key of the old resource type from it's cache (see {@link
* CustomUpdateActionUtils#buildNonNullCustomFieldsUpdateActions(CustomFields, CustomFieldsDraft,
* Custom, GenericCustomActionBuilder, Integer, Function, Function, Function, BaseSyncOptions)}).
*
* <p>An update action will be added to the result list in the following cases:-
*
* <ol>
* <li>If the new resources's custom type is set, but old resources's custom type is not. A
* "setCustomType" update actions is added, which sets the custom type (and all it's fields
* to the old resource).
* <li>If the new resource's custom type is not set, but the old resource's custom type is set.
* A "setCustomType" update action is added, which removes the type set on the old resource.
* <li>If both the resources custom types are the same and the custom fields are both set. The
* custom field values of both resources are then calculated. (see {@link
* CustomUpdateActionUtils#buildSetCustomFieldsUpdateActions(Map, Map, Custom,
* GenericCustomActionBuilder, Integer, Function)} )})
* <li>If the keys of both custom types are different, then a "setCustomType" update action is
* added, where the old resource's custom type is set to be as the new one's.
* <li>If both resources custom type keys are identical but the custom fields of the new
* resource's custom type is not set.
* </ol>
*
* <p>An update action will <b>not</b> be added to the result list in the following cases:-
*
* <ol>
* <li>If both the resources' custom types are not set.
* <li>If both the resources' custom type keys are not set.
* <li>Custom fields are both empty.
* <li>Custom field JSON values have different ordering.
* <li>Custom field values are identical.
* </ol>
*
* @param <D> the type of the new {@link Resource} which is the super {@link Resource} of the
* Resource to update.
* @param <T> the type of the old {@link Resource} which has the custom fields.
* @param <S> the type of the new resource {@link CustomDraft}.
* @param <U> the type of the resource in which the update actions will be applied on.
* @param newMainResourceDraft the main resource of the resource draft where we get the new custom
* fields.
* @param oldResource the resource which should be updated.
* @param newResourceDraft the resource draft where we get the new custom fields.
* @param customActionBuilder the builder instance responsible for building the custom update
* actions.
* @param variantId optional field representing the variant id in case the oldResource is an
* asset.
* @param resourceIdGetter a function used to get the id of the resource being updated.
* @param resourceTypeIdGetter a function used to get the Type id of the resource being updated.
* @param updateIdGetter a function used to get the id/key needed for updating the resource that
* has the custom fields.
* @param syncOptions responsible for supplying the sync options to the sync utility method.
* @return a list that contains all the update actions needed, otherwise an empty list if no
* update actions are needed.
*/
@SuppressWarnings("unchecked")
@Nonnull
public static <D, T extends Custom, S extends CustomDraft, U extends ResourceView> List<UpdateAction<U>> buildCustomUpdateActions(@Nullable final D newMainResourceDraft, @Nonnull final T oldResource, @Nonnull final S newResourceDraft, @Nonnull final GenericCustomActionBuilder<U> customActionBuilder, @Nullable final Integer variantId, @Nonnull final Function<T, String> resourceIdGetter, @Nonnull final Function<T, String> resourceTypeIdGetter, @Nonnull final Function<T, String> updateIdGetter, @Nonnull final BaseSyncOptions syncOptions) {
final CustomFields oldResourceCustomFields = oldResource.getCustom();
final CustomFieldsDraft newResourceCustomFields = newResourceDraft.getCustom();
if (oldResourceCustomFields != null && newResourceCustomFields != null) {
try {
return buildNonNullCustomFieldsUpdateActions(oldResourceCustomFields, newResourceCustomFields, oldResource, customActionBuilder, variantId, resourceIdGetter, resourceTypeIdGetter, updateIdGetter, syncOptions);
} catch (BuildUpdateActionException exception) {
final String errorMessage = format(CUSTOM_FIELDS_UPDATE_ACTIONS_BUILD_FAILED, resourceTypeIdGetter.apply(oldResource), resourceIdGetter.apply(oldResource), exception.getMessage());
syncOptions.applyErrorCallback(new SyncException(errorMessage, exception), oldResource, newMainResourceDraft != null ? newMainResourceDraft : newResourceDraft, null);
}
} else {
if (oldResourceCustomFields == null) {
if (newResourceCustomFields != null) {
// New resource's custom fields are set, but old resources's custom fields are not set. So
// we
// should set the custom type and fields of the new resource to the old one.
final String newCustomFieldsTypeId = newResourceCustomFields.getType().getId();
if (isBlank(newCustomFieldsTypeId)) {
final String errorMessage = format(CUSTOM_FIELDS_UPDATE_ACTIONS_BUILD_FAILED, resourceTypeIdGetter.apply(oldResource), resourceIdGetter.apply(oldResource), CUSTOM_TYPE_ID_IS_BLANK);
syncOptions.applyErrorCallback(new SyncException(errorMessage, null), oldResource, newMainResourceDraft != null ? newMainResourceDraft : newResourceDraft, null);
} else {
final Map<String, JsonNode> newCustomFieldsJsonMap = newResourceCustomFields.getFields();
final Optional<UpdateAction<U>> updateAction = buildTypedSetCustomTypeUpdateAction(newCustomFieldsTypeId, newCustomFieldsJsonMap, oldResource, customActionBuilder, variantId, resourceIdGetter, resourceTypeIdGetter, updateIdGetter, syncOptions);
return updateAction.map(Collections::singletonList).orElseGet(Collections::emptyList);
}
}
} else {
return singletonList(customActionBuilder.buildRemoveCustomTypeAction(variantId, updateIdGetter.apply(oldResource)));
}
}
return Collections.emptyList();
}
Aggregations