diff --git a/README.md b/README.md index e6f87c1f..3418c3a5 100644 --- a/README.md +++ b/README.md @@ -210,7 +210,7 @@ Example: ##### Download ```bash -docker pull commercetools/commercetools-project-sync:5.3.1 +docker pull commercetools/commercetools-project-sync:5.4.0 ``` ##### Run @@ -222,14 +222,14 @@ docker run \ -e TARGET_PROJECT_KEY=xxxx \ -e TARGET_CLIENT_ID=xxxx \ -e TARGET_CLIENT_SECRET=xxxx \ -commercetools/commercetools-project-sync:5.3.1 -s all +commercetools/commercetools-project-sync:5.4.0 -s all ``` ### Examples - To run the all sync modules from a source project to a target project ```bash - docker run commercetools/commercetools-project-sync:5.3.1 -s all + docker run commercetools/commercetools-project-sync:5.4.0 -s all ``` This will run the following sync modules in the given order: 1. `Type` Sync and `ProductType` Sync and `States` Sync and `TaxCategory` Sync and `CustomObject` Sync in parallel. @@ -239,70 +239,70 @@ commercetools/commercetools-project-sync:5.3.1 -s all - To run the type sync ```bash - docker run commercetools/commercetools-project-sync:5.3.1 -s types + docker run commercetools/commercetools-project-sync:5.4.0 -s types ``` - To run the productType sync ```bash - docker run commercetools/commercetools-project-sync:5.3.1 -s productTypes + docker run commercetools/commercetools-project-sync:5.4.0 -s productTypes ``` - To run the states sync ```bash - docker run commercetools/commercetools-project-sync:5.3.1 -s states + docker run commercetools/commercetools-project-sync:5.4.0 -s states ``` - To run the taxCategory sync ```bash - docker run commercetools/commercetools-project-sync:5.3.1 -s taxCategories + docker run commercetools/commercetools-project-sync:5.4.0 -s taxCategories ``` - To run the category sync ```bash - docker run commercetools/commercetools-project-sync:5.3.1 -s categories + docker run commercetools/commercetools-project-sync:5.4.0 -s categories ``` - To run the product sync ```bash - docker run commercetools/commercetools-project-sync:5.3.1 -s products + docker run commercetools/commercetools-project-sync:5.4.0 -s products ``` - To run the cartDiscount sync ```bash - docker run commercetools/commercetools-project-sync:5.3.1 -s cartDiscounts + docker run commercetools/commercetools-project-sync:5.4.0 -s cartDiscounts ``` - To run the inventoryEntry sync ```bash - docker run commercetools/commercetools-project-sync:5.3.1 -s inventoryEntries + docker run commercetools/commercetools-project-sync:5.4.0 -s inventoryEntries ``` - To run the customObject sync ```bash - docker run commercetools/commercetools-project-sync:5.3.1 -s customObjects + docker run commercetools/commercetools-project-sync:5.4.0 -s customObjects ``` - To run the customer sync ```bash - docker run commercetools/commercetools-project-sync:5.3.1 -s customers + docker run commercetools/commercetools-project-sync:5.4.0 -s customers ``` - To run the shoppingList sync ```bash - docker run commercetools/commercetools-project-sync:5.3.1 -s shoppingLists + docker run commercetools/commercetools-project-sync:5.4.0 -s shoppingLists ``` - To run both products and shoppingList sync ```bash - docker run commercetools/commercetools-project-sync:5.3.1 -s products shoppingLists + docker run commercetools/commercetools-project-sync:5.4.0 -s products shoppingLists ``` - To run type, productType and shoppingList sync ```bash - docker run commercetools/commercetools-project-sync:5.3.1 -s types productTypes shoppingLists + docker run commercetools/commercetools-project-sync:5.4.0 -s types productTypes shoppingLists ``` - To run all sync modules using a runner name ```bash - docker run commercetools/commercetools-project-sync:5.3.1 -s all -r myRunnerName + docker run commercetools/commercetools-project-sync:5.4.0 -s all -r myRunnerName ``` ## Scopes diff --git a/build.gradle b/build.gradle index 806c9889..a66014e4 100644 --- a/build.gradle +++ b/build.gradle @@ -34,10 +34,10 @@ ext { assertjVersion = '3.24.2' pmdVersion = '6.55.0' jacocoVersion = '0.8.11' - commercetoolsSyncJava = '9.2.3' + commercetoolsSyncJava = '10.0.0-beta.4' + httpClientVersion = '17.0.0' apacheCliVersion = '1.6.0' jupiterApiVersion = '5.10.1' - asyncHttpClientVersion = '2.12.3' logbackVersion = '1.4.11' logstashLogbackEncoderVersion= '7.4' } @@ -65,7 +65,7 @@ tasks.withType(SpotlessTask) { dependencies { implementation "com.commercetools:commercetools-sync-java:${commercetoolsSyncJava}" - implementation "org.asynchttpclient:async-http-client:${asyncHttpClientVersion}" + implementation "com.commercetools.sdk:commercetools-okhttp-client4:${httpClientVersion}" implementation "commons-cli:commons-cli:${apacheCliVersion}" implementation "ch.qos.logback:logback-classic:${logbackVersion}" implementation "ch.qos.logback:logback-core:${logbackVersion}" diff --git a/src/integration-test/java/com/commercetools/project/sync/CategorySyncWithReferenceResolutionIT.java b/src/integration-test/java/com/commercetools/project/sync/CategorySyncWithReferenceResolutionIT.java index fdab1105..36a155a4 100644 --- a/src/integration-test/java/com/commercetools/project/sync/CategorySyncWithReferenceResolutionIT.java +++ b/src/integration-test/java/com/commercetools/project/sync/CategorySyncWithReferenceResolutionIT.java @@ -1,36 +1,29 @@ package com.commercetools.project.sync; -import static com.commercetools.project.sync.util.IntegrationTestUtils.assertCategoryExists; -import static com.commercetools.project.sync.util.IntegrationTestUtils.cleanUpProjects; -import static com.commercetools.project.sync.util.IntegrationTestUtils.createITSyncerFactory; -import static com.commercetools.project.sync.util.SphereClientUtils.CTP_SOURCE_CLIENT; -import static com.commercetools.project.sync.util.SphereClientUtils.CTP_TARGET_CLIENT; -import static io.sphere.sdk.models.LocalizedString.ofEnglish; +import static com.commercetools.api.models.common.LocalizedString.ofEnglish; +import static com.commercetools.project.sync.util.CtpClientUtils.CTP_SOURCE_CLIENT; +import static com.commercetools.project.sync.util.CtpClientUtils.CTP_TARGET_CLIENT; +import static com.commercetools.project.sync.util.IntegrationTestUtils.*; import static java.util.Arrays.asList; -import static java.util.Collections.emptyList; -import static java.util.Collections.emptyMap; -import static java.util.Collections.singletonList; import static org.assertj.core.api.Assertions.assertThat; +import com.commercetools.api.client.ProjectApiRoot; +import com.commercetools.api.models.category.CategoryDraft; +import com.commercetools.api.models.category.CategoryDraftBuilder; +import com.commercetools.api.models.common.AssetDraft; +import com.commercetools.api.models.common.AssetDraftBuilder; +import com.commercetools.api.models.common.AssetSourceBuilder; +import com.commercetools.api.models.common.LocalizedStringBuilder; +import com.commercetools.api.models.type.CustomFieldsDraft; +import com.commercetools.api.models.type.CustomFieldsDraftBuilder; +import com.commercetools.api.models.type.FieldDefinition; +import com.commercetools.api.models.type.FieldDefinitionBuilder; +import com.commercetools.api.models.type.ResourceTypeId; +import com.commercetools.api.models.type.Type; +import com.commercetools.api.models.type.TypeDraft; +import com.commercetools.api.models.type.TypeDraftBuilder; +import com.commercetools.api.models.type.TypeTextInputHint; import com.commercetools.project.sync.category.CategorySyncer; -import io.sphere.sdk.categories.CategoryDraft; -import io.sphere.sdk.categories.CategoryDraftBuilder; -import io.sphere.sdk.categories.commands.CategoryCreateCommand; -import io.sphere.sdk.client.SphereClient; -import io.sphere.sdk.models.AssetDraft; -import io.sphere.sdk.models.AssetDraftBuilder; -import io.sphere.sdk.models.AssetSourceBuilder; -import io.sphere.sdk.models.LocalizedString; -import io.sphere.sdk.models.TextInputHint; -import io.sphere.sdk.types.CustomFieldsDraft; -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 java.util.Arrays; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -52,50 +45,51 @@ void setup() { setupSourceProjectData(CTP_SOURCE_CLIENT); } - private void setupSourceProjectData(SphereClient sourceProjectClient) { - + private void setupSourceProjectData(ProjectApiRoot sourceProjectClient) { final FieldDefinition FIELD_DEFINITION = - FieldDefinition.of( - StringFieldType.of(), - "field_name", - LocalizedString.ofEnglish("label_1"), - false, - TextInputHint.SINGLE_LINE); + FieldDefinitionBuilder.of() + .type(fieldTypeBuilder -> fieldTypeBuilder.stringBuilder()) + .name("field_name") + .label(LocalizedStringBuilder.of().addValue("en", "label_1").build()) + .required(false) + .inputHint(TypeTextInputHint.SINGLE_LINE) + .build(); final TypeDraft typeDraft = - TypeDraftBuilder.of( - TYPE_KEY, - LocalizedString.ofEnglish("name_1"), - ResourceTypeIdsSetBuilder.of().addCategories().addPrices().addAssets().build()) - .description(LocalizedString.ofEnglish("description_1")) - .fieldDefinitions(Arrays.asList(FIELD_DEFINITION)) + TypeDraftBuilder.of() + .key(TYPE_KEY) + .name(ofEnglish("name_1")) + .resourceTypeIds( + ResourceTypeId.CATEGORY, ResourceTypeId.PRODUCT_PRICE, ResourceTypeId.ASSET) + .description(ofEnglish("description_1")) + .fieldDefinitions(FIELD_DEFINITION) .build(); - final Type type = - sourceProjectClient.execute(TypeCreateCommand.of(typeDraft)).toCompletableFuture().join(); + final Type type = sourceProjectClient.types().create(typeDraft).executeBlocking().getBody(); - CTP_TARGET_CLIENT.execute(TypeCreateCommand.of(typeDraft)).toCompletableFuture().join(); + CTP_TARGET_CLIENT.types().create(typeDraft).executeBlocking(); - CustomFieldsDraft customFieldsDraft = - CustomFieldsDraft.ofTypeKeyAndJson(type.getKey(), emptyMap()); + final CustomFieldsDraft customFieldsDraft = + CustomFieldsDraftBuilder.of().type(type.toResourceIdentifier()).build(); final AssetDraft assetDraft = - AssetDraftBuilder.of(emptyList(), LocalizedString.ofEnglish("assetName")) + AssetDraftBuilder.of() + .name(ofEnglish("assetName")) .key("assetKey") - .sources(singletonList(AssetSourceBuilder.ofUri("sourceUri").build())) + .sources(AssetSourceBuilder.of().uri("sourceUri").build()) .custom(customFieldsDraft) .build(); final CategoryDraft categoryDraft = - CategoryDraftBuilder.of(ofEnglish("t-shirts"), ofEnglish("t-shirts")) + CategoryDraftBuilder.of() + .name(ofEnglish("t-shirts")) + .slug(ofEnglish("t-shirts")) .key(RESOURCE_KEY) .assets(asList(assetDraft)) .custom(customFieldsDraft) .build(); - sourceProjectClient - .execute(CategoryCreateCommand.of(categoryDraft)) - .toCompletableFuture() - .join(); + + sourceProjectClient.categories().create(categoryDraft).execute().toCompletableFuture().join(); } @AfterAll diff --git a/src/integration-test/java/com/commercetools/project/sync/CliRunnerIT.java b/src/integration-test/java/com/commercetools/project/sync/CliRunnerIT.java index 2ed2f8b7..679f941d 100644 --- a/src/integration-test/java/com/commercetools/project/sync/CliRunnerIT.java +++ b/src/integration-test/java/com/commercetools/project/sync/CliRunnerIT.java @@ -1,16 +1,15 @@ package com.commercetools.project.sync; +import static com.commercetools.api.models.common.LocalizedString.ofEnglish; import static com.commercetools.project.sync.service.impl.CustomObjectServiceImpl.TIMESTAMP_GENERATOR_KEY; +import static com.commercetools.project.sync.util.CtpClientUtils.CTP_SOURCE_CLIENT; +import static com.commercetools.project.sync.util.CtpClientUtils.CTP_TARGET_CLIENT; import static com.commercetools.project.sync.util.IntegrationTestUtils.assertCategoryExists; import static com.commercetools.project.sync.util.IntegrationTestUtils.assertProductExists; import static com.commercetools.project.sync.util.IntegrationTestUtils.assertProductTypeExists; import static com.commercetools.project.sync.util.IntegrationTestUtils.cleanUpProjects; import static com.commercetools.project.sync.util.IntegrationTestUtils.createITSyncerFactory; -import static com.commercetools.project.sync.util.QueryUtils.queryAndExecute; -import static com.commercetools.project.sync.util.SphereClientUtils.CTP_SOURCE_CLIENT; -import static com.commercetools.project.sync.util.SphereClientUtils.CTP_TARGET_CLIENT; import static com.commercetools.project.sync.util.SyncUtils.APPLICATION_DEFAULT_NAME; -import static com.commercetools.project.sync.util.SyncUtils.DEFAULT_RUNNER_NAME; import static com.commercetools.project.sync.util.TestUtils.assertCartDiscountSyncerLoggingEvents; import static com.commercetools.project.sync.util.TestUtils.assertCategorySyncerLoggingEvents; import static com.commercetools.project.sync.util.TestUtils.assertCustomObjectSyncerLoggingEvents; @@ -22,12 +21,57 @@ import static com.commercetools.project.sync.util.TestUtils.assertStateSyncerLoggingEvents; import static com.commercetools.project.sync.util.TestUtils.assertTaxCategorySyncerLoggingEvents; import static com.commercetools.project.sync.util.TestUtils.assertTypeSyncerLoggingEvents; -import static io.sphere.sdk.models.LocalizedString.ofEnglish; import static java.lang.String.format; -import static java.util.Collections.emptyList; -import static java.util.Collections.singletonList; import static org.assertj.core.api.Assertions.assertThat; +import com.commercetools.api.client.ProjectApiRoot; +import com.commercetools.api.client.QueryUtils; +import com.commercetools.api.models.cart_discount.CartDiscount; +import com.commercetools.api.models.cart_discount.CartDiscountDraft; +import com.commercetools.api.models.cart_discount.CartDiscountDraftBuilder; +import com.commercetools.api.models.category.Category; +import com.commercetools.api.models.category.CategoryDraft; +import com.commercetools.api.models.category.CategoryDraftBuilder; +import com.commercetools.api.models.custom_object.CustomObject; +import com.commercetools.api.models.custom_object.CustomObjectDraft; +import com.commercetools.api.models.custom_object.CustomObjectDraftBuilder; +import com.commercetools.api.models.custom_object.CustomObjectPagedQueryResponse; +import com.commercetools.api.models.customer.Customer; +import com.commercetools.api.models.customer.CustomerDraft; +import com.commercetools.api.models.customer.CustomerDraftBuilder; +import com.commercetools.api.models.customer.CustomerSetFirstNameActionBuilder; +import com.commercetools.api.models.customer.CustomerSetLastNameActionBuilder; +import com.commercetools.api.models.customer.CustomerSignInResult; +import com.commercetools.api.models.inventory.InventoryEntry; +import com.commercetools.api.models.inventory.InventoryEntryDraft; +import com.commercetools.api.models.inventory.InventoryEntryDraftBuilder; +import com.commercetools.api.models.inventory.InventoryPagedQueryResponse; +import com.commercetools.api.models.product.Product; +import com.commercetools.api.models.product.ProductDraft; +import com.commercetools.api.models.product.ProductDraftBuilder; +import com.commercetools.api.models.product_type.ProductType; +import com.commercetools.api.models.product_type.ProductTypeDraft; +import com.commercetools.api.models.product_type.ProductTypeDraftBuilder; +import com.commercetools.api.models.shopping_list.ShoppingList; +import com.commercetools.api.models.shopping_list.ShoppingListDraft; +import com.commercetools.api.models.shopping_list.ShoppingListDraftBuilder; +import com.commercetools.api.models.shopping_list.ShoppingListLineItemDraft; +import com.commercetools.api.models.shopping_list.ShoppingListLineItemDraftBuilder; +import com.commercetools.api.models.state.State; +import com.commercetools.api.models.state.StateDraft; +import com.commercetools.api.models.state.StateDraftBuilder; +import com.commercetools.api.models.state.StateTypeEnum; +import com.commercetools.api.models.tax_category.TaxCategory; +import com.commercetools.api.models.tax_category.TaxCategoryDraft; +import com.commercetools.api.models.tax_category.TaxCategoryDraftBuilder; +import com.commercetools.api.models.tax_category.TaxRateDraft; +import com.commercetools.api.models.tax_category.TaxRateDraftBuilder; +import com.commercetools.api.models.type.CustomFieldsDraft; +import com.commercetools.api.models.type.CustomFieldsDraftBuilder; +import com.commercetools.api.models.type.ResourceTypeId; +import com.commercetools.api.models.type.Type; +import com.commercetools.api.models.type.TypeDraft; +import com.commercetools.api.models.type.TypeDraftBuilder; import com.commercetools.project.sync.cartdiscount.CartDiscountSyncer; import com.commercetools.project.sync.category.CategorySyncer; import com.commercetools.project.sync.customer.CustomerSyncer; @@ -41,88 +85,15 @@ import com.commercetools.project.sync.taxcategory.TaxCategorySyncer; import com.commercetools.project.sync.type.TypeSyncer; import com.commercetools.sync.commons.helpers.BaseSyncStatistics; -import com.commercetools.sync.products.helpers.ProductSyncStatistics; -import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.JsonNodeFactory; import com.fasterxml.jackson.databind.node.ObjectNode; -import com.neovisionaries.i18n.CountryCode; -import io.sphere.sdk.cartdiscounts.CartDiscount; -import io.sphere.sdk.cartdiscounts.CartDiscountDraft; -import io.sphere.sdk.cartdiscounts.CartDiscountDraftBuilder; -import io.sphere.sdk.cartdiscounts.CartDiscountValue; -import io.sphere.sdk.cartdiscounts.ShippingCostTarget; -import io.sphere.sdk.cartdiscounts.commands.CartDiscountCreateCommand; -import io.sphere.sdk.cartdiscounts.queries.CartDiscountQuery; -import io.sphere.sdk.categories.Category; -import io.sphere.sdk.categories.CategoryDraft; -import io.sphere.sdk.categories.CategoryDraftBuilder; -import io.sphere.sdk.categories.commands.CategoryCreateCommand; -import io.sphere.sdk.categories.queries.CategoryQuery; -import io.sphere.sdk.client.SphereClient; -import io.sphere.sdk.customers.Customer; -import io.sphere.sdk.customers.CustomerDraft; -import io.sphere.sdk.customers.CustomerDraftBuilder; -import io.sphere.sdk.customers.CustomerName; -import io.sphere.sdk.customers.CustomerSignInResult; -import io.sphere.sdk.customers.commands.CustomerCreateCommand; -import io.sphere.sdk.customers.commands.CustomerUpdateCommand; -import io.sphere.sdk.customers.commands.updateactions.ChangeName; -import io.sphere.sdk.customers.queries.CustomerQuery; -import io.sphere.sdk.customobjects.CustomObject; -import io.sphere.sdk.customobjects.CustomObjectDraft; -import io.sphere.sdk.customobjects.commands.CustomObjectUpsertCommand; -import io.sphere.sdk.customobjects.queries.CustomObjectQuery; -import io.sphere.sdk.inventory.InventoryEntry; -import io.sphere.sdk.inventory.InventoryEntryDraft; -import io.sphere.sdk.inventory.InventoryEntryDraftBuilder; -import io.sphere.sdk.inventory.commands.InventoryEntryCreateCommand; -import io.sphere.sdk.inventory.queries.InventoryEntryQuery; -import io.sphere.sdk.models.ResourceIdentifier; -import io.sphere.sdk.products.Product; -import io.sphere.sdk.products.ProductDraft; -import io.sphere.sdk.products.ProductDraftBuilder; -import io.sphere.sdk.products.ProductVariantDraftBuilder; -import io.sphere.sdk.products.commands.ProductCreateCommand; -import io.sphere.sdk.products.queries.ProductQuery; -import io.sphere.sdk.producttypes.ProductType; -import io.sphere.sdk.producttypes.ProductTypeDraft; -import io.sphere.sdk.producttypes.ProductTypeDraftBuilder; -import io.sphere.sdk.producttypes.commands.ProductTypeCreateCommand; -import io.sphere.sdk.producttypes.queries.ProductTypeQuery; -import io.sphere.sdk.queries.PagedQueryResult; -import io.sphere.sdk.queries.QueryPredicate; -import io.sphere.sdk.shoppinglists.LineItemDraft; -import io.sphere.sdk.shoppinglists.LineItemDraftBuilder; -import io.sphere.sdk.shoppinglists.ShoppingList; -import io.sphere.sdk.shoppinglists.ShoppingListDraft; -import io.sphere.sdk.shoppinglists.ShoppingListDraftBuilder; -import io.sphere.sdk.shoppinglists.commands.ShoppingListCreateCommand; -import io.sphere.sdk.shoppinglists.commands.ShoppingListDeleteCommand; -import io.sphere.sdk.shoppinglists.queries.ShoppingListQuery; -import io.sphere.sdk.states.State; -import io.sphere.sdk.states.StateDraft; -import io.sphere.sdk.states.StateDraftBuilder; -import io.sphere.sdk.states.StateType; -import io.sphere.sdk.states.commands.StateCreateCommand; -import io.sphere.sdk.states.queries.StateQuery; -import io.sphere.sdk.taxcategories.TaxCategory; -import io.sphere.sdk.taxcategories.TaxCategoryDraft; -import io.sphere.sdk.taxcategories.TaxCategoryDraftBuilder; -import io.sphere.sdk.taxcategories.TaxRateDraft; -import io.sphere.sdk.taxcategories.TaxRateDraftBuilder; -import io.sphere.sdk.taxcategories.commands.TaxCategoryCreateCommand; -import io.sphere.sdk.taxcategories.queries.TaxCategoryQuery; -import io.sphere.sdk.types.CustomFieldsDraft; -import io.sphere.sdk.types.CustomFieldsDraftBuilder; -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.queries.TypeQuery; +import io.vrap.rmf.base.client.ApiHttpResponse; +import io.vrap.rmf.base.client.utils.json.JsonUtils; import java.time.ZonedDateTime; -import java.util.Collections; +import java.util.List; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionStage; import java.util.concurrent.ExecutionException; import javax.annotation.Nonnull; import org.junit.jupiter.api.AfterAll; @@ -183,149 +154,201 @@ void setup() throws ExecutionException, InterruptedException { setupSourceProjectData(CTP_SOURCE_CLIENT); } - static void setupSourceProjectData(@Nonnull final SphereClient sourceProjectClient) + static void setupSourceProjectData(@Nonnull final ProjectApiRoot sourceProjectClient) throws ExecutionException, InterruptedException { final ProductTypeDraft productTypeDraft = - ProductTypeDraftBuilder.of( - RESOURCE_KEY, "sample-product-type", "a productType for t-shirts", emptyList()) + ProductTypeDraftBuilder.of() + .key(RESOURCE_KEY) + .name("sample-product-type") + .description("a productType for t-shirts") .build(); final ProductType productType = - sourceProjectClient - .execute(ProductTypeCreateCommand.of(productTypeDraft)) - .toCompletableFuture() - .join(); + sourceProjectClient.productTypes().post(productTypeDraft).executeBlocking().getBody(); final TypeDraft typeDraft = - TypeDraftBuilder.of( - RESOURCE_KEY, - ofEnglish("category-custom-type"), - ResourceTypeIdsSetBuilder.of().addCategories().add("shopping-list")) + TypeDraftBuilder.of() + .key(RESOURCE_KEY) + .name(ofEnglish("category-custom-type")) + .resourceTypeIds(ResourceTypeId.CATEGORY, ResourceTypeId.SHOPPING_LIST) .build(); final StateDraft stateDraft = - StateDraftBuilder.of(RESOURCE_KEY, StateType.PRODUCT_STATE) - .roles(Collections.emptySet()) + StateDraftBuilder.of() + .key(RESOURCE_KEY) + .type(StateTypeEnum.PRODUCT_STATE) + .roles(List.of()) .description(ofEnglish("State 1")) .name(ofEnglish("State 1")) .initial(true) - .transitions(Collections.emptySet()) + .transitions(List.of()) .build(); - final State state = - sourceProjectClient.execute(StateCreateCommand.of(stateDraft)).toCompletableFuture().join(); + final State state = sourceProjectClient.states().post(stateDraft).executeBlocking().getBody(); final CompletableFuture typeFuture = - sourceProjectClient.execute(TypeCreateCommand.of(typeDraft)).toCompletableFuture(); + sourceProjectClient + .types() + .post(typeDraft) + .execute() + .thenApply(ApiHttpResponse::getBody) + .toCompletableFuture(); final TaxRateDraft taxRateDraft = - TaxRateDraftBuilder.of("Tax-Rate-Name-1", 0.3, false, CountryCode.DE).build(); + TaxRateDraftBuilder.of() + .name("Tax-Rate-Name-1") + .amount(0.3) + .includedInPrice(false) + .country("DE") + .build(); final TaxCategoryDraft taxCategoryDraft = - TaxCategoryDraftBuilder.of( - "Tax-Category-Name-1", singletonList(taxRateDraft), "Tax-Category-Description-1") + TaxCategoryDraftBuilder.of() + .name("Tax-Category-Name-1") + .rates(taxRateDraft) + .description("Tax-Category-Description-1") .key(RESOURCE_KEY) .build(); final TaxCategory taxCategory = - sourceProjectClient - .execute(TaxCategoryCreateCommand.of(taxCategoryDraft)) - .toCompletableFuture() - .join(); + sourceProjectClient.taxCategories().post(taxCategoryDraft).executeBlocking().getBody(); final ObjectNode customObjectValue = JsonNodeFactory.instance.objectNode().put("name", "value"); - final CustomObjectDraft customObjectDraft = - CustomObjectDraft.ofUnversionedUpsert(RESOURCE_KEY, RESOURCE_KEY, customObjectValue); + final CustomObjectDraft customObjectDraft = + CustomObjectDraftBuilder.of() + .key(RESOURCE_KEY) + .container(RESOURCE_KEY) + .value(customObjectValue) + .build(); // following custom object should not be synced as it's created by the project-sync itself - final CustomObjectDraft customObjectToIgnore = - CustomObjectDraft.ofUnversionedUpsert( - PROJECT_SYNC_CONTAINER_NAME, "timestampGenerator", customObjectValue); + final CustomObjectDraft customObjectToIgnore = + CustomObjectDraftBuilder.of() + .container(PROJECT_SYNC_CONTAINER_NAME) + .key("timestampGenerator") + .value(customObjectValue) + .build(); - final CompletableFuture> customObjectFuture1 = + final CompletableFuture customObjectFuture1 = sourceProjectClient - .execute(CustomObjectUpsertCommand.of(customObjectDraft)) + .customObjects() + .post(customObjectDraft) + .execute() + .thenApply(ApiHttpResponse::getBody) .toCompletableFuture(); - final CompletableFuture> customObjectFuture2 = + final CompletableFuture customObjectFuture2 = sourceProjectClient - .execute(CustomObjectUpsertCommand.of(customObjectToIgnore)) + .customObjects() + .post(customObjectToIgnore) + .execute() + .thenApply(ApiHttpResponse::getBody) .toCompletableFuture(); final CategoryDraft categoryDraft = - CategoryDraftBuilder.of(ofEnglish("t-shirts"), ofEnglish("t-shirts")) + CategoryDraftBuilder.of() + .name(ofEnglish("t-shirts")) + .slug(ofEnglish("t-shirts")) .key(RESOURCE_KEY) .build(); final CompletableFuture categoryFuture = - sourceProjectClient.execute(CategoryCreateCommand.of(categoryDraft)).toCompletableFuture(); + sourceProjectClient + .categories() + .post(categoryDraft) + .execute() + .thenApply(ApiHttpResponse::getBody) + .toCompletableFuture(); final CustomerDraft customerDraft = - CustomerDraftBuilder.of("test@email.com", "testPassword").key(RESOURCE_KEY).build(); + CustomerDraftBuilder.of() + .email("test@email.com") + .password("testPassword") + .key(RESOURCE_KEY) + .build(); final CompletableFuture customerFuture = - sourceProjectClient.execute(CustomerCreateCommand.of(customerDraft)).toCompletableFuture(); + sourceProjectClient + .customers() + .post(customerDraft) + .execute() + .thenApply(ApiHttpResponse::getBody) + .toCompletableFuture(); CompletableFuture.allOf( typeFuture, customObjectFuture1, customObjectFuture2, categoryFuture, customerFuture) .join(); final ProductDraft productDraft = - ProductDraftBuilder.of( - productType, - ofEnglish("V-neck Tee"), - ofEnglish("v-neck-tee"), - ProductVariantDraftBuilder.of().key(RESOURCE_KEY).sku(RESOURCE_KEY).build()) - .state(state) - .taxCategory(taxCategory) + ProductDraftBuilder.of() + .productType(productType.toResourceIdentifier()) + .name(ofEnglish("V-neck Tee")) + .slug(ofEnglish("v-neck-tee")) + .masterVariant( + productVariantDraftBuilder -> + productVariantDraftBuilder.key(RESOURCE_KEY).sku(RESOURCE_KEY)) + .state(state.toResourceIdentifier()) + .taxCategory(taxCategory.toResourceIdentifier()) .key(RESOURCE_KEY) .publish(true) .build(); final CompletableFuture productFuture = - sourceProjectClient.execute(ProductCreateCommand.of(productDraft)).toCompletableFuture(); + sourceProjectClient + .products() + .post(productDraft) + .execute() + .thenApply(ApiHttpResponse::getBody) + .toCompletableFuture(); final InventoryEntryDraft inventoryEntryDraft = - InventoryEntryDraftBuilder.of(RESOURCE_KEY, 1L).build(); + InventoryEntryDraftBuilder.of().sku(RESOURCE_KEY).quantityOnStock(1L).build(); final CompletableFuture inventoryFuture = sourceProjectClient - .execute(InventoryEntryCreateCommand.of(inventoryEntryDraft)) + .inventory() + .post(inventoryEntryDraft) + .execute() + .thenApply(ApiHttpResponse::getBody) .toCompletableFuture(); final CartDiscountDraft cartDiscountDraft = - CartDiscountDraftBuilder.of( - ofEnglish("my-cart-discount"), - "1 = 1", - CartDiscountValue.ofRelative(1), - ShippingCostTarget.of(), - "0.1", - true) + CartDiscountDraftBuilder.of() + .name(ofEnglish("my-cart-discount")) + .cartPredicate("1 = 1") + .value( + cartDiscountValueDraftBuilder -> + cartDiscountValueDraftBuilder.relativeBuilder().permyriad(1L)) + .target(cartDiscountTargetBuilder -> cartDiscountTargetBuilder.shippingBuilder()) + .sortOrder("0.1") + .isActive(true) .key(RESOURCE_KEY) .build(); final CompletableFuture cartDiscountFuture = sourceProjectClient - .execute(CartDiscountCreateCommand.of(cartDiscountDraft)) + .cartDiscounts() + .post(cartDiscountDraft) + .execute() + .thenApply(ApiHttpResponse::getBody) .toCompletableFuture(); CompletableFuture.allOf(productFuture, inventoryFuture, cartDiscountFuture); final CustomerSignInResult customerSignInResult = customerFuture.get(); final Product product = productFuture.get(); - final LineItemDraft lineItemDraft = LineItemDraftBuilder.of(product.getId()).build(); + final ShoppingListLineItemDraft lineItemDraft = + ShoppingListLineItemDraftBuilder.of().productId(product.getId()).build(); final CustomFieldsDraft customFieldsDraft = - CustomFieldsDraftBuilder.ofTypeId(typeFuture.get().getId()).build(); + CustomFieldsDraftBuilder.of().type(typeFuture.get().toResourceIdentifier()).build(); final ShoppingListDraft shoppingListDraft = - ShoppingListDraftBuilder.of(ofEnglish("shoppingList-name")) + ShoppingListDraftBuilder.of() + .name(ofEnglish("shoppingList-name")) .key(RESOURCE_KEY) - .customer(ResourceIdentifier.ofId(customerSignInResult.getCustomer().getId())) - .lineItems(Collections.singletonList(lineItemDraft)) + .customer(customerSignInResult.getCustomer().toResourceIdentifier()) + .lineItems(lineItemDraft) .custom(customFieldsDraft) .build(); - sourceProjectClient - .execute(ShoppingListCreateCommand.of(shoppingListDraft)) - .toCompletableFuture() - .join(); + sourceProjectClient.shoppingLists().post(shoppingListDraft).executeBlocking(); } @AfterAll @@ -337,10 +360,8 @@ static void tearDownSuite() { void run_WithSyncAsArgumentWithAllArg_ShouldExecuteAllSyncers() { // test CliRunner.of().run(new String[] {"-s", "all"}, createITSyncerFactory()); - // assertions assertAllSyncersLoggingEvents(1); - assertAllResourcesAreSyncedToTarget(CTP_TARGET_CLIENT); } @@ -385,7 +406,7 @@ void run_WithSyncAsArgumentWithCustomersAndShoppingLists_ShouldExecuteGivenSynce assertCustomersAreSyncedCorrectly(CTP_TARGET_CLIENT); assertShoppingListsAreSyncedCorrectly(CTP_TARGET_CLIENT); - final String sourceProjectKey = CTP_SOURCE_CLIENT.getConfig().getProjectKey(); + final String sourceProjectKey = CTP_SOURCE_CLIENT.getProjectKey(); assertLastSyncCustomObjectExists( CTP_TARGET_CLIENT, sourceProjectKey, "customerSync", "runnerName"); @@ -410,7 +431,7 @@ void run_WithSyncAsArgumentWithCustomersAndShoppingLists_ShouldExecuteGivenSynce assertTaxCategoriesAreSyncedCorrectly(CTP_TARGET_CLIENT); assertCartDiscountsAreSyncedCorrectly(CTP_TARGET_CLIENT); - final String sourceProjectKey = CTP_SOURCE_CLIENT.getConfig().getProjectKey(); + final String sourceProjectKey = CTP_SOURCE_CLIENT.getProjectKey(); assertLastSyncCustomObjectExists( CTP_TARGET_CLIENT, sourceProjectKey, "categorySync", "runnerName"); @@ -440,7 +461,7 @@ void run_WithSyncAsArgumentWithCustomersAndShoppingLists_ShouldExecuteGivenSynce assertCategoriesAreSyncedCorrectly(CTP_TARGET_CLIENT); assertShoppingListsAreSyncedCorrectly(CTP_TARGET_CLIENT); - final String sourceProjectKey = CTP_SOURCE_CLIENT.getConfig().getProjectKey(); + final String sourceProjectKey = CTP_SOURCE_CLIENT.getProjectKey(); assertLastSyncCustomObjectExists( CTP_TARGET_CLIENT, sourceProjectKey, "categorySync", "runnerName"); @@ -478,7 +499,7 @@ void run_WithSyncAsArgumentWithCustomersAndShoppingLists_ShouldExecuteGivenSynce assertCustomersAreSyncedCorrectly(CTP_TARGET_CLIENT); assertShoppingListsAreSyncedCorrectly(CTP_TARGET_CLIENT); - final String sourceProjectKey = CTP_SOURCE_CLIENT.getConfig().getProjectKey(); + final String sourceProjectKey = CTP_SOURCE_CLIENT.getProjectKey(); assertLastSyncCustomObjectExists(CTP_TARGET_CLIENT, sourceProjectKey, "typeSync", "runnerName"); assertLastSyncCustomObjectExists( @@ -507,7 +528,7 @@ void run_WithSyncAsArgumentWithCustomersAndShoppingLists_ShouldExecuteGivenSynce assertCustomersAreSyncedCorrectly(CTP_TARGET_CLIENT); - final String sourceProjectKey = CTP_SOURCE_CLIENT.getConfig().getProjectKey(); + final String sourceProjectKey = CTP_SOURCE_CLIENT.getProjectKey(); assertLastSyncCustomObjectExists( CTP_TARGET_CLIENT, sourceProjectKey, "customerSync", "runnerName"); @@ -529,6 +550,7 @@ void run_WithUpdatedCustomer_ShouldSyncCustomersAndStoreLastSyncTimestampsAsCust // test CliRunner.of().run(new String[] {"-s", "customers"}, syncerFactory); + // assertions assertUpdatedCustomersAreSyncedCorrectly(CTP_SOURCE_CLIENT, CTP_TARGET_CLIENT); assertUpdatedCustomObjectTimestampAfterSync( @@ -536,67 +558,72 @@ void run_WithUpdatedCustomer_ShouldSyncCustomersAndStoreLastSyncTimestampsAsCust } private void assertUpdatedCustomObjectTimestampAfterSync( - @Nonnull final SphereClient targetClient, + @Nonnull final ProjectApiRoot targetClient, @Nonnull final String syncModuleName, @Nonnull final String runnerName, @Nonnull final ZonedDateTime lastModifiedTime) { - final PagedQueryResult> lastSyncResult = + + CustomObjectPagedQueryResponse lastSyncCustomObject = fetchLastSyncCustomObject(targetClient, syncModuleName, runnerName); + assertThat(lastSyncCustomObject.getResults()).isNotEmpty(); assertThat(lastModifiedTime) - .isBefore(lastSyncResult.getResults().get(0).getValue().getLastSyncTimestamp()); + .isBefore(lastSyncCustomObject.getResults().get(0).getLastModifiedAt()); } private ZonedDateTime getCustomObjectLastModifiedTime( - @Nonnull final SphereClient targetClient, + @Nonnull final ProjectApiRoot targetClient, @Nonnull final String syncModuleName, @Nonnull final String runnerName) { - final PagedQueryResult> lastSyncResult = + final CustomObjectPagedQueryResponse lastSyncResult = fetchLastSyncCustomObject(targetClient, syncModuleName, runnerName); - return lastSyncResult.getResults().get(0).getValue().getLastSyncTimestamp(); + return lastSyncResult.getResults().isEmpty() + ? null + : lastSyncResult.getResults().get(0).getLastModifiedAt(); } - private void updateCustomerSourceObject(@Nonnull final SphereClient sourceProjectClient) { - final PagedQueryResult customerPagedQueryResult = - sourceProjectClient - .execute( - CustomerQuery.of() - .withPredicates(QueryPredicate.of(format("key=\"%s\"", RESOURCE_KEY)))) - .toCompletableFuture() - .join(); - final Customer customer = customerPagedQueryResult.getResults().get(0); + private void updateCustomerSourceObject(@Nonnull final ProjectApiRoot sourceProjectClient) { + final Customer customer = + sourceProjectClient.customers().withKey(RESOURCE_KEY).get().executeBlocking().getBody(); sourceProjectClient - .execute( - CustomerUpdateCommand.of( - customer, - ChangeName.of(CustomerName.ofFirstAndLastName("testFirstName", "testLastName")))) - .toCompletableFuture() - .join(); + .customers() + .withKey(RESOURCE_KEY) + .post( + customerUpdateBuilder -> + customerUpdateBuilder + .actions( + CustomerSetFirstNameActionBuilder.of().firstName("testFirstName").build(), + CustomerSetLastNameActionBuilder.of().lastName("testLastName").build()) + .version(customer.getVersion())) + .executeBlocking(); } private void assertUpdatedCustomersAreSyncedCorrectly( - @Nonnull final SphereClient cspClient, @Nonnull final SphereClient ctpClient) { - final PagedQueryResult sourceCustomerPagedQueryResult = - cspClient - .execute( - CustomerQuery.of() - .withPredicates(QueryPredicate.of(format("key=\"%s\"", RESOURCE_KEY)))) + @Nonnull final ProjectApiRoot sourceClient, @Nonnull final ProjectApiRoot targetClient) { + final Customer sourceCustomer = + sourceClient + .customers() + .withKey(RESOURCE_KEY) + .get() + .execute() + .thenApply(ApiHttpResponse::getBody) .toCompletableFuture() .join(); - Customer sourceCustomer = sourceCustomerPagedQueryResult.getResults().get(0); - final PagedQueryResult targetCustomerPagedQueryResult = - ctpClient - .execute( - CustomerQuery.of() - .withPredicates(QueryPredicate.of(format("key=\"%s\"", RESOURCE_KEY)))) + final Customer targetCustomer = + targetClient + .customers() + .withKey(RESOURCE_KEY) + .get() + .execute() + .thenApply(ApiHttpResponse::getBody) .toCompletableFuture() .join(); - Customer targetCustomer = targetCustomerPagedQueryResult.getResults().get(0); - assertThat(sourceCustomer.getName()).isEqualTo(targetCustomer.getName()); + assertThat(sourceCustomer.getFirstName()).isEqualTo(targetCustomer.getFirstName()); + assertThat(sourceCustomer.getLastName()).isEqualTo(targetCustomer.getLastName()); } @Test @@ -619,21 +646,39 @@ private void assertUpdatedCustomersAreSyncedCorrectly( assertShoppingListsAreSyncedCorrectly(CTP_TARGET_CLIENT); - final String sourceProjectKey = CTP_SOURCE_CLIENT.getConfig().getProjectKey(); + final String sourceProjectKey = CTP_SOURCE_CLIENT.getProjectKey(); assertLastSyncCustomObjectExists( CTP_TARGET_CLIENT, sourceProjectKey, "shoppingListSync", "runnerName"); } - private void prepareDataForShoppingListSync(SphereClient sourceClient) { - queryAndExecute(sourceClient, ShoppingListQuery.of(), ShoppingListDeleteCommand::of).join(); - final ShoppingListDraft shoppingListDraft = - ShoppingListDraftBuilder.of(ofEnglish("shoppingList-name")).key(RESOURCE_KEY).build(); - - sourceClient - .execute(ShoppingListCreateCommand.of(shoppingListDraft)) + private void prepareDataForShoppingListSync(ProjectApiRoot sourceClient) { + QueryUtils.queryAll( + sourceClient.shoppingLists().get(), + shoppingLists -> { + CompletableFuture.allOf( + shoppingLists.stream() + .map( + shoppingList -> + sourceClient + .shoppingLists() + .delete(shoppingList) + .execute() + .thenApply(ApiHttpResponse::getBody)) + .map(CompletionStage::toCompletableFuture) + .toArray(CompletableFuture[]::new)) + .join(); + }) .toCompletableFuture() .join(); + + final ShoppingListDraft shoppingListDraft = + ShoppingListDraftBuilder.of() + .name(ofEnglish("shoppingList-name")) + .key(RESOURCE_KEY) + .build(); + + sourceClient.shoppingLists().post(shoppingListDraft).executeBlocking(); } @Test @@ -641,10 +686,9 @@ private void prepareDataForShoppingListSync(SphereClient sourceClient) { run_WithCustomObjectSyncAsArgument_ShouldSyncCustomObjectsWithoutProjectSyncGeneratedCustomObjects() { // test CliRunner.of().run(new String[] {"-s", "customObjects"}, createITSyncerFactory()); - // assertions + // assertions assertThat(customObjectSyncerTestLogger.getAllLoggingEvents()).hasSize(2); - assertCustomObjectSyncerLoggingEvents(customObjectSyncerTestLogger, 1); } @@ -660,168 +704,189 @@ private void prepareDataForShoppingListSync(SphereClient sourceClient) { assertProductTypesAreSyncedCorrectly(CTP_TARGET_CLIENT); - final ZonedDateTime lastSyncTimestamp = - assertCurrentCtpTimestampGeneratorAndGetLastModifiedAt( - CTP_TARGET_CLIENT, DEFAULT_RUNNER_NAME, "ProductTypeSync"); + // todo: https://commercetools.atlassian.net/browse/DEVX-272 + // final ZonedDateTime lastSyncTimestamp = + // assertCurrentCtpTimestampGeneratorAndGetLastModifiedAt( + // CTP_TARGET_CLIENT, DEFAULT_RUNNER_NAME, "ProductTypeSync"); - final String sourceProjectKey = CTP_SOURCE_CLIENT.getConfig().getProjectKey(); + // final String sourceProjectKey = CTP_SOURCE_CLIENT.getProjectKey(); - assertLastSyncCustomObjectIsCorrect( - CTP_TARGET_CLIENT, - sourceProjectKey, - "productTypeSync", - DEFAULT_RUNNER_NAME, - ProductSyncStatistics.class, - lastSyncTimestamp); + // assertLastSyncCustomObjectIsCorrect( + // CTP_TARGET_CLIENT, + // sourceProjectKey, + // "productTypeSync", + // DEFAULT_RUNNER_NAME, + // ProductSyncStatistics.class, + // lastSyncTimestamp); } - private static void assertTypesAreSyncedCorrectly(@Nonnull final SphereClient ctpClient) { - final String queryPredicate = format("key=\"%s\"", RESOURCE_KEY); - - final PagedQueryResult typeQueryResult = + private static void assertTypesAreSyncedCorrectly(@Nonnull final ProjectApiRoot ctpClient) { + final Type type = ctpClient - .execute(TypeQuery.of().withPredicates(QueryPredicate.of(queryPredicate))) + .types() + .withKey(RESOURCE_KEY) + .get() + .execute() + .thenApply(ApiHttpResponse::getBody) .toCompletableFuture() .join(); - assertThat(typeQueryResult.getResults()) - .hasSize(1) - .singleElement() - .satisfies(productType -> assertThat(productType.getKey()).isEqualTo(RESOURCE_KEY)); + assertThat(type.getKey()).isEqualTo(RESOURCE_KEY); } - private static void assertProductTypesAreSyncedCorrectly(@Nonnull final SphereClient ctpClient) { - - final PagedQueryResult productTypeQueryResult = - ctpClient.execute(ProductTypeQuery.of().byKey(RESOURCE_KEY)).toCompletableFuture().join(); + private static void assertProductTypesAreSyncedCorrectly( + @Nonnull final ProjectApiRoot ctpClient) { + final ProductType productType = + ctpClient + .productTypes() + .withKey(RESOURCE_KEY) + .get() + .execute() + .thenApply(ApiHttpResponse::getBody) + .toCompletableFuture() + .join(); - assertThat(productTypeQueryResult.getResults()) - .hasSize(1) - .singleElement() - .satisfies(productType -> assertThat(productType.getKey()).isEqualTo(RESOURCE_KEY)); + assertThat(productType.getKey()).isEqualTo(RESOURCE_KEY); } - private static void assertProductsAreSyncedCorrectly(@Nonnull final SphereClient ctpClient) { - - final PagedQueryResult productQueryResult = - ctpClient.execute(ProductQuery.of()).toCompletableFuture().join(); + private static void assertProductsAreSyncedCorrectly(@Nonnull final ProjectApiRoot ctpClient) { + final Product product = + ctpClient + .products() + .withKey(RESOURCE_KEY) + .get() + .execute() + .thenApply(ApiHttpResponse::getBody) + .toCompletableFuture() + .join(); - assertThat(productQueryResult.getResults()) - .hasSize(1) - .singleElement() - .satisfies(product -> assertThat(product.getKey()).isEqualTo(RESOURCE_KEY)); + assertThat(product.getKey()).isEqualTo(RESOURCE_KEY); } - private static void assertCustomersAreSyncedCorrectly(@Nonnull final SphereClient ctpClient) { - final PagedQueryResult customerPagedQueryResult = + private static void assertCustomersAreSyncedCorrectly(@Nonnull final ProjectApiRoot ctpClient) { + final Customer customer = ctpClient - .execute( - CustomerQuery.of() - .withPredicates(QueryPredicate.of(format("key=\"%s\"", RESOURCE_KEY)))) + .customers() + .withKey(RESOURCE_KEY) + .get() + .execute() + .thenApply(ApiHttpResponse::getBody) .toCompletableFuture() .join(); - assertThat(customerPagedQueryResult.getResults()).hasSize(1); + + assertThat(customer.getKey()).isEqualTo(RESOURCE_KEY); } - private static void assertShoppingListsAreSyncedCorrectly(@Nonnull final SphereClient ctpClient) { - final PagedQueryResult shoppingListPagedQueryResult = + private static void assertShoppingListsAreSyncedCorrectly( + @Nonnull final ProjectApiRoot ctpClient) { + final ShoppingList shoppingList = ctpClient - .execute( - ShoppingListQuery.of() - .withPredicates(QueryPredicate.of(format("key=\"%s\"", RESOURCE_KEY)))) + .shoppingLists() + .withKey(RESOURCE_KEY) + .get() + .execute() + .thenApply(ApiHttpResponse::getBody) .toCompletableFuture() .join(); - assertThat(shoppingListPagedQueryResult.getResults()).hasSize(1); + + assertThat(shoppingList.getKey()).isEqualTo(RESOURCE_KEY); } - private static void assertCategoriesAreSyncedCorrectly(@Nonnull final SphereClient ctpClient) { - final PagedQueryResult categoryPagedQueryResult = + private static void assertCategoriesAreSyncedCorrectly(@Nonnull final ProjectApiRoot ctpClient) { + final Category category = ctpClient - .execute( - CategoryQuery.of() - .withPredicates(QueryPredicate.of(format("key=\"%s\"", RESOURCE_KEY)))) + .categories() + .withKey(RESOURCE_KEY) + .get() + .execute() + .thenApply(ApiHttpResponse::getBody) .toCompletableFuture() .join(); - assertThat(categoryPagedQueryResult.getResults()).hasSize(1); + + assertThat(category.getKey()).isEqualTo(RESOURCE_KEY); } - private static void assertStatesAreSyncedCorrectly(@Nonnull final SphereClient ctpClient) { - final PagedQueryResult statePagedQueryResult = + private static void assertStatesAreSyncedCorrectly(@Nonnull final ProjectApiRoot ctpClient) { + final State state = ctpClient - .execute( - StateQuery.of() - .withPredicates(QueryPredicate.of(format("key=\"%s\"", RESOURCE_KEY)))) + .states() + .withKey(RESOURCE_KEY) + .get() + .execute() + .thenApply(ApiHttpResponse::getBody) .toCompletableFuture() .join(); - assertThat(statePagedQueryResult.getResults()).hasSize(1); + + assertThat(state.getKey()).isEqualTo(RESOURCE_KEY); } - private static void assertTaxCategoriesAreSyncedCorrectly(@Nonnull final SphereClient ctpClient) { - final PagedQueryResult taxCategoryPagedQueryResult = + private static void assertTaxCategoriesAreSyncedCorrectly( + @Nonnull final ProjectApiRoot ctpClient) { + final TaxCategory taxCategory = ctpClient - .execute( - TaxCategoryQuery.of() - .withPredicates(QueryPredicate.of(format("key=\"%s\"", RESOURCE_KEY)))) + .taxCategories() + .withKey(RESOURCE_KEY) + .get() + .execute() + .thenApply(ApiHttpResponse::getBody) .toCompletableFuture() .join(); - assertThat(taxCategoryPagedQueryResult.getResults()).hasSize(1); + + assertThat(taxCategory.getKey()).isEqualTo(RESOURCE_KEY); } - private static void assertCartDiscountsAreSyncedCorrectly(@Nonnull final SphereClient ctpClient) { - final PagedQueryResult cartDiscountPagedQueryResult = + private static void assertCartDiscountsAreSyncedCorrectly( + @Nonnull final ProjectApiRoot ctpClient) { + final CartDiscount cartDiscount = ctpClient - .execute( - CartDiscountQuery.of() - .withPredicates(QueryPredicate.of(format("key=\"%s\"", RESOURCE_KEY)))) + .cartDiscounts() + .withKey(RESOURCE_KEY) + .get() + .execute() + .thenApply(ApiHttpResponse::getBody) .toCompletableFuture() .join(); - assertThat(cartDiscountPagedQueryResult.getResults()).hasSize(1); + + assertThat(cartDiscount.getKey()).isEqualTo(RESOURCE_KEY); } private void assertLastSyncCustomObjectIsCorrect( - @Nonnull final SphereClient targetClient, + @Nonnull final ProjectApiRoot targetClient, @Nonnull final String sourceProjectKey, @Nonnull final String syncModuleName, @Nonnull final String syncRunnerName, @Nonnull final Class statisticsClass, @Nonnull final ZonedDateTime lastSyncTimestamp) { - final PagedQueryResult> lastSyncResult = - fetchLastSyncCustomObject(targetClient, syncModuleName, syncRunnerName); + final LastSyncCustomObject lastSyncCustomObject = + assertLastSyncCustomObjectExists( + targetClient, sourceProjectKey, syncModuleName, syncRunnerName); - assertThat(lastSyncResult.getResults()) - .hasSize(1) - .singleElement() - .satisfies( - lastSyncCustomObject -> { - assertThat(lastSyncCustomObject.getKey()).isEqualTo(sourceProjectKey); - assertThat(lastSyncCustomObject.getValue()) - .satisfies( - value -> { - // Excluding til BaseStatisticsDeserializer works with correct type. - // assertThat(value.getLastSyncStatistics()).isInstanceOf(statisticsClass); - assertThat(value.getLastSyncStatistics().getProcessed().get()).isEqualTo(1); - assertThat(value.getLastSyncTimestamp()) - .isBeforeOrEqualTo(lastSyncTimestamp); - }); - }); + assertThat(lastSyncCustomObject.getLastSyncStatistics()).isInstanceOf(statisticsClass); + assertThat(lastSyncCustomObject.getLastSyncStatistics().getProcessed().get()).isEqualTo(1); + assertThat(lastSyncCustomObject.getLastSyncTimestamp()).isBeforeOrEqualTo(lastSyncTimestamp); } @Nonnull private ZonedDateTime assertCurrentCtpTimestampGeneratorAndGetLastModifiedAt( - @Nonnull final SphereClient targetClient, + @Nonnull final ProjectApiRoot targetClient, @Nonnull final String runnerName, @Nonnull final String syncModuleName) { - final CustomObjectQuery timestampGeneratorQuery = - CustomObjectQuery.of(String.class) - .byContainer( - format( - "%s.%s.%s.%s", - APPLICATION_DEFAULT_NAME, runnerName, syncModuleName, TIMESTAMP_GENERATOR_KEY)); + final String container = + format( + "%s.%s.%s.%s", + APPLICATION_DEFAULT_NAME, runnerName, syncModuleName, TIMESTAMP_GENERATOR_KEY); - final PagedQueryResult> currentCtpTimestampGeneratorResults = - targetClient.execute(timestampGeneratorQuery).toCompletableFuture().join(); + final CustomObjectPagedQueryResponse currentCtpTimestampGeneratorResults = + targetClient + .customObjects() + .withContainer(container) + .get() + .execute() + .thenApply(ApiHttpResponse::getBody) + .toCompletableFuture() + .join(); assertThat(currentCtpTimestampGeneratorResults.getResults()) .hasSize(1) @@ -829,7 +894,7 @@ private ZonedDateTime assertCurrentCtpTimestampGeneratorAndGetLastModifiedAt( .satisfies( currentCtpTimestamp -> { assertThat(currentCtpTimestamp.getKey()).isEqualTo(TIMESTAMP_GENERATOR_KEY); - assertThat(currentCtpTimestamp.getValue()) + assertThat((String) currentCtpTimestamp.getValue()) .matches( "[0-9a-fA-F]{8}\\-[0-9a-fA-F]{4}\\-[0-9a-fA-F]{4}\\-[0-9a-fA-F]{4}\\-[0-9a-fA-F]{12}"); }); @@ -848,7 +913,7 @@ private ZonedDateTime assertCurrentCtpTimestampGeneratorAndGetLastModifiedAt( assertCurrentCtpTimestampGeneratorAndGetLastModifiedAt( CTP_TARGET_CLIENT, "runnerName", "ProductTypeSync"); - final String sourceProjectKey = CTP_SOURCE_CLIENT.getConfig().getProjectKey(); + final String sourceProjectKey = CTP_SOURCE_CLIENT.getProjectKey(); assertLastSyncCustomObjectExists( CTP_TARGET_CLIENT, sourceProjectKey, "productSync", "runnerName"); @@ -924,107 +989,101 @@ void run_WithSyncAsArgumentWithAllArgAsFullSync_ShouldExecuteAllSyncers() { assertNoProjectSyncCustomObjectExists(CTP_TARGET_CLIENT); } - private void assertNoProjectSyncCustomObjectExists(@Nonnull final SphereClient targetClient) { - final CustomObjectQuery lastSyncQuery = - CustomObjectQuery.of(LastSyncCustomObject.class) - .plusPredicates( - customObjectQueryModel -> - customObjectQueryModel.container().is(PROJECT_SYNC_CONTAINER_NAME)); - final PagedQueryResult> lastSyncResult = - targetClient.execute(lastSyncQuery).toCompletableFuture().join(); + private void assertNoProjectSyncCustomObjectExists(@Nonnull final ProjectApiRoot targetClient) { + final CustomObjectPagedQueryResponse lastSyncResult = + targetClient + .customObjects() + .withContainer(PROJECT_SYNC_CONTAINER_NAME) + .get() + .execute() + .thenApply(ApiHttpResponse::getBody) + .toCompletableFuture() + .join(); assertThat(lastSyncResult.getResults()).isEmpty(); } private void assertCurrentCtpTimestampGeneratorDoesntExist( - @Nonnull final SphereClient targetClient, + @Nonnull final ProjectApiRoot targetClient, @Nonnull final String runnerName, @Nonnull final String syncModuleName) { + final String container = + format( + "commercetools-project-sync.%s.%s.%s", + runnerName, syncModuleName, TIMESTAMP_GENERATOR_KEY); - final CustomObjectQuery timestampGeneratorQuery = - CustomObjectQuery.of(String.class) - .byContainer( - format( - "commercetools-project-sync.%s.%s.%s", - runnerName, syncModuleName, TIMESTAMP_GENERATOR_KEY)); - - final PagedQueryResult> currentCtpTimestampGeneratorResults = - targetClient.execute(timestampGeneratorQuery).toCompletableFuture().join(); + final CustomObjectPagedQueryResponse currentCtpTimestampGeneratorResults = + targetClient + .customObjects() + .withContainer(container) + .get() + .execute() + .thenApply(ApiHttpResponse::getBody) + .toCompletableFuture() + .join(); assertThat(currentCtpTimestampGeneratorResults.getResults()).isEmpty(); } - private void assertLastSyncCustomObjectExists( - @Nonnull final SphereClient targetClient, + private LastSyncCustomObject assertLastSyncCustomObjectExists( + @Nonnull final ProjectApiRoot targetClient, @Nonnull final String sourceProjectKey, @Nonnull final String syncModuleName, @Nonnull final String runnerName) { - final PagedQueryResult> lastSyncResult = + final CustomObjectPagedQueryResponse lastSyncResult = fetchLastSyncCustomObject(targetClient, syncModuleName, runnerName); assertThat(lastSyncResult.getResults()) .hasSize(1) .singleElement() .satisfies( - lastSyncCustomObject -> { - assertThat(lastSyncCustomObject.getKey()).isEqualTo(sourceProjectKey); - assertThat(lastSyncCustomObject.getValue()) - .satisfies( - value -> { - assertThat(value.getLastSyncTimestamp()).isNotNull(); - assertThat(value.getLastSyncDurationInMillis()).isNotNull(); - assertThat(value.getApplicationVersion()).isNotNull(); - }); + customObject -> { + assertThat(customObject.getKey()).isEqualTo(sourceProjectKey); + assertThat(customObject.getValue()).isNotNull(); }); + final CustomObject customObject = lastSyncResult.getResults().get(0); + final ObjectMapper objectMapper = JsonUtils.getConfiguredObjectMapper(); + final LastSyncCustomObject lastSyncCustomObject = + objectMapper.convertValue(customObject.getValue(), LastSyncCustomObject.class); + + assertThat(lastSyncCustomObject.getLastSyncTimestamp()).isNotNull(); + assertThat(lastSyncCustomObject.getLastSyncDurationInMillis()).isNotNull(); + assertThat(lastSyncCustomObject.getApplicationVersion()).isNotNull(); + return lastSyncCustomObject; } - private PagedQueryResult> fetchLastSyncCustomObject( - @Nonnull SphereClient targetClient, + private CustomObjectPagedQueryResponse fetchLastSyncCustomObject( + @Nonnull ProjectApiRoot targetClient, @Nonnull String syncModuleName, @Nonnull String runnerName) { - final CustomObjectQuery lastSyncQuery = - CustomObjectQuery.of(LastSyncCustomObject.class) - .byContainer(format("%s.%s.%s", APPLICATION_DEFAULT_NAME, runnerName, syncModuleName)); - - return targetClient.execute(lastSyncQuery).toCompletableFuture().join(); + final String container = + format("%s.%s.%s", APPLICATION_DEFAULT_NAME, runnerName, syncModuleName); + return targetClient + .customObjects() + .withContainer(container) + .get() + .execute() + .thenApply(ApiHttpResponse::getBody) + .toCompletableFuture() + .join(); } private static void assertAllResourcesAreSyncedToTarget( - @Nonnull final SphereClient targetClient) { + @Nonnull final ProjectApiRoot targetClient) { assertProductTypeExists(targetClient, RESOURCE_KEY); assertCategoryExists(targetClient, RESOURCE_KEY); assertProductExists(targetClient, RESOURCE_KEY, RESOURCE_KEY, RESOURCE_KEY); + assertTypesAreSyncedCorrectly(targetClient); + assertTaxCategoriesAreSyncedCorrectly(targetClient); - final String queryPredicate = format("key=\"%s\"", RESOURCE_KEY); - - final PagedQueryResult typeQueryResult = + final InventoryPagedQueryResponse inventoryEntryQueryResult = targetClient - .execute(TypeQuery.of().withPredicates(QueryPredicate.of(queryPredicate))) - .toCompletableFuture() - .join(); - - assertThat(typeQueryResult.getResults()) - .hasSize(1) - .singleElement() - .satisfies(type -> assertThat(type.getKey()).isEqualTo(RESOURCE_KEY)); - - final PagedQueryResult taxCategoryQueryResult = - targetClient - .execute(TaxCategoryQuery.of().withPredicates(QueryPredicate.of(queryPredicate))) - .toCompletableFuture() - .join(); - - assertThat(taxCategoryQueryResult.getResults()) - .hasSize(1) - .singleElement() - .satisfies(taxCategory -> assertThat(taxCategory.getKey()).isEqualTo(RESOURCE_KEY)); - - final PagedQueryResult inventoryEntryQueryResult = - targetClient - .execute( - InventoryEntryQuery.of() - .withPredicates(QueryPredicate.of(format("sku=\"%s\"", RESOURCE_KEY)))) + .inventory() + .get() + .withWhere(format("sku=\"%s\"", RESOURCE_KEY)) + .execute() + .thenApply(ApiHttpResponse::getBody) .toCompletableFuture() .join(); @@ -1033,65 +1092,23 @@ private static void assertAllResourcesAreSyncedToTarget( .singleElement() .satisfies(inventoryEntry -> assertThat(inventoryEntry.getSku()).isEqualTo(RESOURCE_KEY)); - final PagedQueryResult cartDiscountPagedQueryResult = - targetClient - .execute( - CartDiscountQuery.of() - .withPredicates(queryModel -> queryModel.key().is(RESOURCE_KEY))) - .toCompletableFuture() - .join(); - - assertThat(cartDiscountPagedQueryResult.getResults()) - .hasSize(1) - .singleElement() - .satisfies(cartDiscount -> assertThat(cartDiscount.getKey()).isEqualTo(RESOURCE_KEY)); + assertCartDiscountsAreSyncedCorrectly(targetClient); + assertStatesAreSyncedCorrectly(targetClient); - final PagedQueryResult statePagedQueryResult = + final CustomObject customObject = targetClient - .execute( - StateQuery.of().withPredicates(queryModel -> queryModel.key().is(RESOURCE_KEY))) + .customObjects() + .withContainerAndKey(RESOURCE_KEY, RESOURCE_KEY) + .get() + .execute() + .thenApply(ApiHttpResponse::getBody) .toCompletableFuture() .join(); - assertThat(statePagedQueryResult.getResults()) - .hasSize(1) - .singleElement() - .satisfies(state -> assertThat(state.getKey()).isEqualTo(RESOURCE_KEY)); - - final PagedQueryResult> customObjectPagedQueryResult = - targetClient - .execute( - CustomObjectQuery.ofJsonNode() - .withPredicates( - queryModel -> - queryModel - .key() - .is(RESOURCE_KEY) - .and(queryModel.container().is(RESOURCE_KEY)))) - .toCompletableFuture() - .join(); - - assertThat(customObjectPagedQueryResult.getResults()) - .hasSize(1) - .singleElement() - .satisfies(customObject -> assertThat(customObject.getKey()).isEqualTo(RESOURCE_KEY)); + assertThat(customObject.getKey()).isEqualTo(RESOURCE_KEY); - final PagedQueryResult customerPagedQueryResult = - targetClient - .execute( - CustomerQuery.of().withPredicates(queryModel -> queryModel.key().is(RESOURCE_KEY))) - .toCompletableFuture() - .join(); - assertThat(customerPagedQueryResult.getResults()).hasSize(1); - - final PagedQueryResult shoppingListPagedQueryResult = - targetClient - .execute( - ShoppingListQuery.of() - .withPredicates(queryModel -> queryModel.key().is(RESOURCE_KEY))) - .toCompletableFuture() - .join(); - assertThat(shoppingListPagedQueryResult.getResults()).hasSize(1); + assertCustomersAreSyncedCorrectly(targetClient); + assertShoppingListsAreSyncedCorrectly(targetClient); } public static void assertAllSyncersLoggingEvents(final int numberOfResources) { diff --git a/src/integration-test/java/com/commercetools/project/sync/InventorySyncMultiChannelIT.java b/src/integration-test/java/com/commercetools/project/sync/InventorySyncMultiChannelIT.java index eaa1ea8c..4b7269b4 100644 --- a/src/integration-test/java/com/commercetools/project/sync/InventorySyncMultiChannelIT.java +++ b/src/integration-test/java/com/commercetools/project/sync/InventorySyncMultiChannelIT.java @@ -1,36 +1,32 @@ package com.commercetools.project.sync; +import static com.commercetools.project.sync.util.CtpClientUtils.CTP_SOURCE_CLIENT; +import static com.commercetools.project.sync.util.CtpClientUtils.CTP_TARGET_CLIENT; import static com.commercetools.project.sync.util.IntegrationTestUtils.cleanUpProjects; import static com.commercetools.project.sync.util.IntegrationTestUtils.createITSyncerFactory; -import static com.commercetools.project.sync.util.SphereClientUtils.CTP_SOURCE_CLIENT; -import static com.commercetools.project.sync.util.SphereClientUtils.CTP_TARGET_CLIENT; -import static java.util.Collections.singletonList; import static org.assertj.core.api.Assertions.assertThat; +import com.commercetools.api.client.ProjectApiRoot; +import com.commercetools.api.models.channel.Channel; +import com.commercetools.api.models.channel.ChannelDraft; +import com.commercetools.api.models.channel.ChannelDraftBuilder; +import com.commercetools.api.models.channel.ChannelRoleEnum; +import com.commercetools.api.models.common.LocalizedString; +import com.commercetools.api.models.inventory.InventoryEntry; +import com.commercetools.api.models.inventory.InventoryEntryDraft; +import com.commercetools.api.models.inventory.InventoryEntryDraftBuilder; +import com.commercetools.api.models.type.CustomFieldsDraftBuilder; +import com.commercetools.api.models.type.FieldContainer; +import com.commercetools.api.models.type.FieldContainerBuilder; +import com.commercetools.api.models.type.FieldDefinition; +import com.commercetools.api.models.type.FieldDefinitionBuilder; +import com.commercetools.api.models.type.FieldTypeBuilder; +import com.commercetools.api.models.type.ResourceTypeId; +import com.commercetools.api.models.type.TypeDraft; +import com.commercetools.api.models.type.TypeDraftBuilder; import com.commercetools.project.sync.inventoryentry.InventoryEntrySyncer; -import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.JsonNodeFactory; -import io.sphere.sdk.channels.Channel; -import io.sphere.sdk.channels.ChannelDraft; -import io.sphere.sdk.channels.ChannelRole; -import io.sphere.sdk.channels.commands.ChannelCreateCommand; -import io.sphere.sdk.client.SphereClient; -import io.sphere.sdk.inventory.InventoryEntry; -import io.sphere.sdk.inventory.InventoryEntryDraft; -import io.sphere.sdk.inventory.InventoryEntryDraftBuilder; -import io.sphere.sdk.inventory.commands.InventoryEntryCreateCommand; -import io.sphere.sdk.models.LocalizedString; -import io.sphere.sdk.models.Reference; -import io.sphere.sdk.types.CustomFieldsDraft; -import io.sphere.sdk.types.FieldDefinition; -import io.sphere.sdk.types.StringFieldType; -import io.sphere.sdk.types.TypeDraft; -import io.sphere.sdk.types.TypeDraftBuilder; -import io.sphere.sdk.types.commands.TypeCreateCommand; -import java.util.Collections; -import java.util.HashMap; -import java.util.Locale; -import java.util.Map; +import io.vrap.rmf.base.client.ApiHttpResponse; import java.util.concurrent.CompletableFuture; import java.util.stream.IntStream; import javax.annotation.Nonnull; @@ -67,127 +63,174 @@ void setup() { setupProjectData(CTP_TARGET_CLIENT); } - private void setupProjectData(SphereClient sphereClient) { + private void setupProjectData(ProjectApiRoot projectApiRoot) { final ChannelDraft channelDraft1 = - ChannelDraft.of(SUPPLY_CHANNEL_KEY_1).withRoles(ChannelRole.INVENTORY_SUPPLY); + ChannelDraftBuilder.of() + .key(SUPPLY_CHANNEL_KEY_1) + .roles(ChannelRoleEnum.INVENTORY_SUPPLY) + .build(); final ChannelDraft channelDraft2 = - ChannelDraft.of(SUPPLY_CHANNEL_KEY_2).withRoles(ChannelRole.INVENTORY_SUPPLY); + ChannelDraftBuilder.of() + .key(SUPPLY_CHANNEL_KEY_2) + .roles(ChannelRoleEnum.INVENTORY_SUPPLY) + .build(); - final String channelId1 = - sphereClient - .execute(ChannelCreateCommand.of(channelDraft1)) + final Channel channel1 = + projectApiRoot + .channels() + .create(channelDraft1) + .execute() + .thenApply(ApiHttpResponse::getBody) .toCompletableFuture() - .join() - .getId(); - final String channelId2 = - sphereClient - .execute(ChannelCreateCommand.of(channelDraft2)) + .join(); + final Channel channel2 = + projectApiRoot + .channels() + .create(channelDraft2) + .execute() + .thenApply(ApiHttpResponse::getBody) .toCompletableFuture() - .join() - .getId(); - - final Reference supplyChannelReference1 = Channel.referenceOfId(channelId1); - final Reference supplyChannelReference2 = Channel.referenceOfId(channelId2); + .join(); - createInventoriesCustomType(sphereClient); + createInventoriesCustomType(projectApiRoot); // NOTE: There can only be one inventory entry for the combination of sku and supplyChannel. - final InventoryEntryDraft draft1 = InventoryEntryDraftBuilder.of(SKU_1, 0L).build(); + final InventoryEntryDraft draft1 = + InventoryEntryDraftBuilder.of().sku(SKU_1).quantityOnStock(0L).build(); final InventoryEntryDraft draft2 = - InventoryEntryDraftBuilder.of(SKU_1, 0L) - .supplyChannel(supplyChannelReference1) - .custom(CustomFieldsDraft.ofTypeKeyAndJson(CUSTOM_TYPE, getMockCustomFieldsJsons())) + InventoryEntryDraftBuilder.of() + .sku(SKU_1) + .quantityOnStock(0L) + .supplyChannel(channel1.toResourceIdentifier()) + .custom( + CustomFieldsDraftBuilder.of() + .type(builder -> builder.key(CUSTOM_TYPE)) + .fields(getMockFieldContainer()) + .build()) .build(); final InventoryEntryDraft draft3 = - InventoryEntryDraftBuilder.of(SKU_1, 1L) - .supplyChannel(supplyChannelReference2) - .custom(CustomFieldsDraft.ofTypeKeyAndJson(CUSTOM_TYPE, getMockCustomFieldsJsons())) + InventoryEntryDraftBuilder.of() + .sku(SKU_1) + .quantityOnStock(1L) + .supplyChannel(channel2.toResourceIdentifier()) + .custom( + CustomFieldsDraftBuilder.of() + .type(builder -> builder.key(CUSTOM_TYPE)) + .fields(getMockFieldContainer()) + .build()) .build(); - final InventoryEntryDraft draft4 = InventoryEntryDraftBuilder.of(SKU_2, 0L).build(); + final InventoryEntryDraft draft4 = + InventoryEntryDraftBuilder.of().sku(SKU_2).quantityOnStock(0L).build(); final InventoryEntryDraft draft5 = - InventoryEntryDraftBuilder.of(SKU_2, 0L) - .supplyChannel(supplyChannelReference1) - .custom(CustomFieldsDraft.ofTypeKeyAndJson(CUSTOM_TYPE, getMockCustomFieldsJsons())) + InventoryEntryDraftBuilder.of() + .sku(SKU_2) + .quantityOnStock(0L) + .supplyChannel(channel1.toResourceIdentifier()) + .custom( + CustomFieldsDraftBuilder.of() + .type(builder -> builder.key(CUSTOM_TYPE)) + .fields(getMockFieldContainer()) + .build()) .build(); final InventoryEntryDraft draft6 = - InventoryEntryDraftBuilder.of(SKU_2, 1L) - .supplyChannel(supplyChannelReference2) - .custom(CustomFieldsDraft.ofTypeKeyAndJson(CUSTOM_TYPE, getMockCustomFieldsJsons())) + InventoryEntryDraftBuilder.of() + .sku(SKU_2) + .quantityOnStock(1L) + .supplyChannel(channel2.toResourceIdentifier()) + .custom( + CustomFieldsDraftBuilder.of() + .type(builder -> builder.key(CUSTOM_TYPE)) + .fields(getMockFieldContainer()) + .build()) .build(); CompletableFuture.allOf( - sphereClient.execute(InventoryEntryCreateCommand.of(draft1)).toCompletableFuture(), - sphereClient.execute(InventoryEntryCreateCommand.of(draft2)).toCompletableFuture(), - sphereClient.execute(InventoryEntryCreateCommand.of(draft3)).toCompletableFuture(), - sphereClient.execute(InventoryEntryCreateCommand.of(draft4)).toCompletableFuture(), - sphereClient.execute(InventoryEntryCreateCommand.of(draft5)).toCompletableFuture(), - sphereClient.execute(InventoryEntryCreateCommand.of(draft6)).toCompletableFuture()) + projectApiRoot.inventory().create(draft1).execute().toCompletableFuture(), + projectApiRoot.inventory().create(draft2).execute().toCompletableFuture(), + projectApiRoot.inventory().create(draft3).execute().toCompletableFuture(), + projectApiRoot.inventory().create(draft4).execute().toCompletableFuture(), + projectApiRoot.inventory().create(draft5).execute().toCompletableFuture(), + projectApiRoot.inventory().create(draft6).execute().toCompletableFuture()) .join(); } - private static void createInventoriesCustomType(@Nonnull final SphereClient ctpClient) { + private static void createInventoriesCustomType(@Nonnull final ProjectApiRoot ctpClient) { final FieldDefinition fieldDefinition = - FieldDefinition.of( - StringFieldType.of(), - CUSTOM_FIELD_NAME, - LocalizedString.of(Locale.ENGLISH, CUSTOM_FIELD_NAME), - false); + FieldDefinitionBuilder.of() + .name(CUSTOM_FIELD_NAME) + .label(LocalizedString.ofEnglish(CUSTOM_FIELD_NAME)) + .required(false) + .type(FieldTypeBuilder.of().stringBuilder().build()) + .build(); + final TypeDraft typeDraft = - TypeDraftBuilder.of( - CUSTOM_TYPE, - LocalizedString.of(Locale.ENGLISH, CUSTOM_TYPE), - Collections.singleton(InventoryEntry.resourceTypeId())) - .fieldDefinitions(singletonList(fieldDefinition)) + TypeDraftBuilder.of() + .key(CUSTOM_TYPE) + .name(LocalizedString.ofEnglish(CUSTOM_TYPE)) + .resourceTypeIds(ResourceTypeId.INVENTORY_ENTRY) + .fieldDefinitions(fieldDefinition) .build(); - ctpClient.execute(TypeCreateCommand.of(typeDraft)).toCompletableFuture().join(); + + ctpClient.types().create(typeDraft).execute().toCompletableFuture().join(); } - private static Map getMockCustomFieldsJsons() { - final Map customFieldsJsons = new HashMap<>(); - customFieldsJsons.put(CUSTOM_FIELD_NAME, JsonNodeFactory.instance.textNode("customValue")); - return customFieldsJsons; + private static FieldContainer getMockFieldContainer() { + return FieldContainerBuilder.of() + .addValue(CUSTOM_FIELD_NAME, JsonNodeFactory.instance.textNode("customValue")) + .build(); } - private void create249InventoryEntry(SphereClient sphereClient) { + private void create249InventoryEntry(ProjectApiRoot projectApiRoot) { CompletableFuture.allOf( IntStream.range(0, 10) .mapToObj( value -> - sphereClient - .execute( - InventoryEntryCreateCommand.of( - InventoryEntryDraftBuilder.of("SKU_" + value, 1L).build())) + projectApiRoot + .inventory() + .create( + InventoryEntryDraftBuilder.of() + .sku("SKU_" + value) + .quantityOnStock(1L) + .build()) + .execute() .toCompletableFuture()) .toArray(CompletableFuture[]::new)) .join(); CompletableFuture.allOf( IntStream.range(0, 251) - .mapToObj(value -> create(sphereClient, value)) + .mapToObj(value -> create(projectApiRoot, value)) .toArray(CompletableFuture[]::new)) .join(); } - private CompletableFuture create(SphereClient sphereClient, int value) { + private CompletableFuture create(ProjectApiRoot projectApiRoot, int value) { final ChannelDraft channelDraft1 = - ChannelDraft.of("other-channel-key_" + value).withRoles(ChannelRole.INVENTORY_SUPPLY); + ChannelDraftBuilder.of() + .key("other-channel-key_" + value) + .roles(ChannelRoleEnum.INVENTORY_SUPPLY) + .build(); - final String channelId = - sphereClient - .execute(ChannelCreateCommand.of(channelDraft1)) + final Channel channel = + projectApiRoot + .channels() + .create(channelDraft1) + .execute() + .thenApply(ApiHttpResponse::getBody) .toCompletableFuture() - .join() - .getId(); - - final Reference supplyChannelReference = Channel.referenceOfId(channelId); - - return sphereClient - .execute( - InventoryEntryCreateCommand.of( - InventoryEntryDraftBuilder.of("SKU_CHANNEL", 0L) - .supplyChannel(supplyChannelReference) - .build())) + .join(); + + return projectApiRoot + .inventory() + .create( + InventoryEntryDraftBuilder.of() + .supplyChannel(channel.toResourceIdentifier()) + .sku("SKU_CHANNEL") + .quantityOnStock(0L) + .build()) + .execute() + .thenApply(ApiHttpResponse::getBody) .toCompletableFuture(); } diff --git a/src/integration-test/java/com/commercetools/project/sync/ProductSyncWithDiscountedPrice.java b/src/integration-test/java/com/commercetools/project/sync/ProductSyncWithDiscountedPrice.java deleted file mode 100644 index 85516e6b..00000000 --- a/src/integration-test/java/com/commercetools/project/sync/ProductSyncWithDiscountedPrice.java +++ /dev/null @@ -1,177 +0,0 @@ -package com.commercetools.project.sync; - -import static com.commercetools.project.sync.util.IntegrationTestUtils.cleanUpProjects; -import static com.commercetools.project.sync.util.IntegrationTestUtils.createITSyncerFactory; -import static com.commercetools.project.sync.util.SphereClientUtils.CTP_SOURCE_CLIENT; -import static com.commercetools.project.sync.util.SphereClientUtils.CTP_TARGET_CLIENT; -import static com.neovisionaries.i18n.CountryCode.DE; -import static io.sphere.sdk.models.DefaultCurrencyUnits.EUR; -import static io.sphere.sdk.models.LocalizedString.ofEnglish; -import static java.util.Collections.emptyList; -import static org.assertj.core.api.Assertions.assertThat; - -import com.neovisionaries.i18n.CountryCode; -import io.sphere.sdk.client.SphereClient; -import io.sphere.sdk.models.Reference; -import io.sphere.sdk.productdiscounts.DiscountedPrice; -import io.sphere.sdk.productdiscounts.ProductDiscount; -import io.sphere.sdk.productdiscounts.ProductDiscountDraft; -import io.sphere.sdk.productdiscounts.ProductDiscountDraftBuilder; -import io.sphere.sdk.productdiscounts.ProductDiscountValue; -import io.sphere.sdk.productdiscounts.commands.ProductDiscountCreateCommand; -import io.sphere.sdk.products.Price; -import io.sphere.sdk.products.PriceDraft; -import io.sphere.sdk.products.PriceDraftBuilder; -import io.sphere.sdk.products.Product; -import io.sphere.sdk.products.ProductDraft; -import io.sphere.sdk.products.ProductDraftBuilder; -import io.sphere.sdk.products.ProductVariant; -import io.sphere.sdk.products.ProductVariantDraft; -import io.sphere.sdk.products.ProductVariantDraftBuilder; -import io.sphere.sdk.products.commands.ProductCreateCommand; -import io.sphere.sdk.products.queries.ProductQuery; -import io.sphere.sdk.producttypes.ProductType; -import io.sphere.sdk.producttypes.ProductTypeDraft; -import io.sphere.sdk.producttypes.ProductTypeDraftBuilder; -import io.sphere.sdk.producttypes.commands.ProductTypeCreateCommand; -import io.sphere.sdk.queries.PagedQueryResult; -import java.math.BigDecimal; -import java.time.ZonedDateTime; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import javax.money.CurrencyUnit; -import org.javamoney.moneta.FastMoney; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import uk.org.lidalia.slf4jext.Level; -import uk.org.lidalia.slf4jtest.TestLogger; -import uk.org.lidalia.slf4jtest.TestLoggerFactory; - -// This will suppress MoreThanOneLogger warnings in this class -@SuppressWarnings("PMD.MoreThanOneLogger") -class ProductSyncWithDiscountedPrice { - private static final TestLogger cliRunnerTestLogger = - TestLoggerFactory.getTestLogger(CliRunner.class); - - private static final String MAIN_PRODUCT_TYPE_KEY = "main-product-type"; - private static final String MAIN_PRODUCT_MASTER_VARIANT_KEY = "main-product-master-variant-key"; - private static final String MAIN_PRODUCT_KEY = "product-with-references"; - private static final FastMoney TEN_EUR = FastMoney.of(10, EUR); - - @BeforeEach - void setup() { - cliRunnerTestLogger.clearAll(); - - ProductDiscountDraft productDiscountDraft = - ProductDiscountDraftBuilder.of() - .value(ProductDiscountValue.ofExternal()) - .name(ofEnglish("testProductDiscount")) - .predicate("1=1") - .sortOrder("0.9") - .isActive(true) - .build(); - ProductDiscount productDiscount = - CTP_TARGET_CLIENT - .execute(ProductDiscountCreateCommand.of(productDiscountDraft)) - .toCompletableFuture() - .join(); - setupProjectData(CTP_SOURCE_CLIENT, null); - setupProjectData(CTP_TARGET_CLIENT, productDiscount.getId()); - } - - static void setupProjectData(@Nonnull final SphereClient sphereClient, String productDiscountId) { - final ProductTypeDraft productTypeDraft = - ProductTypeDraftBuilder.of( - MAIN_PRODUCT_TYPE_KEY, - MAIN_PRODUCT_TYPE_KEY, - "a productType for t-shirts", - emptyList()) - .build(); - - final ProductType productType = - sphereClient - .execute(ProductTypeCreateCommand.of(productTypeDraft)) - .toCompletableFuture() - .join(); - - final PriceDraft priceDraft = - PriceDraftBuilder.of( - getPriceDraft(BigDecimal.valueOf(222), EUR, DE, null, null, productDiscountId)) - .build(); - - final ProductVariantDraft masterVariant = - ProductVariantDraftBuilder.of() - .key(MAIN_PRODUCT_MASTER_VARIANT_KEY) - .sku(MAIN_PRODUCT_MASTER_VARIANT_KEY) - .prices(priceDraft) - .build(); - - final ProductDraft draft = - ProductDraftBuilder.of( - productType, - ofEnglish(MAIN_PRODUCT_KEY), - ofEnglish(MAIN_PRODUCT_KEY), - masterVariant) - .key(MAIN_PRODUCT_KEY) - .build(); - - sphereClient.execute(ProductCreateCommand.of(draft)).toCompletableFuture().join(); - } - - @AfterAll - static void tearDownSuite() { - cleanUpProjects(CTP_SOURCE_CLIENT, CTP_TARGET_CLIENT); - } - - @Test - void run_WhenTargetProductHasDiscountedPrice_ShouldNotRemoveIt() { - // test - CliRunner.of() - .run(new String[] {"-s", "products", "-r", "runnerName", "-f"}, createITSyncerFactory()); - - // assertions - assertThat(cliRunnerTestLogger.getAllLoggingEvents()) - .allMatch(loggingEvent -> !Level.ERROR.equals(loggingEvent.getLevel())); - - final PagedQueryResult productQueryResult = - CTP_TARGET_CLIENT.execute(ProductQuery.of()).toCompletableFuture().join(); - - assertThat(productQueryResult.getResults()) - .hasSize(1) - .singleElement() - .satisfies( - product -> { - final ProductVariant stagedMasterVariant = - product.getMasterData().getStaged().getMasterVariant(); - assertThat(stagedMasterVariant.getPrices()) - .satisfies( - prices -> { - Price price = prices.get(0); - assertThat(price.getDiscounted()).isNotNull(); - assertThat(price.getDiscounted().getValue()).isEqualTo(TEN_EUR); - }); - }); - } - - @Nonnull - public static PriceDraft getPriceDraft( - @Nonnull final BigDecimal value, - @Nonnull final CurrencyUnit currencyUnits, - @Nullable final CountryCode countryCode, - @Nullable final ZonedDateTime validFrom, - @Nullable final ZonedDateTime validUntil, - @Nullable final String productDiscountReferenceId) { - DiscountedPrice discounted = null; - if (productDiscountReferenceId != null) { - discounted = - DiscountedPrice.of(TEN_EUR, Reference.of("product-discount", productDiscountReferenceId)); - } - return PriceDraftBuilder.of(Price.of(value, currencyUnits)) - .country(countryCode) - .validFrom(validFrom) - .validUntil(validUntil) - .discounted(discounted) - .build(); - } -} diff --git a/src/integration-test/java/com/commercetools/project/sync/ProductSyncWithDiscountedPriceIT.java b/src/integration-test/java/com/commercetools/project/sync/ProductSyncWithDiscountedPriceIT.java new file mode 100644 index 00000000..36a0784a --- /dev/null +++ b/src/integration-test/java/com/commercetools/project/sync/ProductSyncWithDiscountedPriceIT.java @@ -0,0 +1,168 @@ +package com.commercetools.project.sync; + +import static com.commercetools.api.models.common.LocalizedString.ofEnglish; +import static com.commercetools.project.sync.util.CtpClientUtils.CTP_SOURCE_CLIENT; +import static com.commercetools.project.sync.util.CtpClientUtils.CTP_TARGET_CLIENT; +import static com.commercetools.project.sync.util.IntegrationTestUtils.cleanUpProjects; +import static com.commercetools.project.sync.util.IntegrationTestUtils.createITSyncerFactory; +import static org.assertj.core.api.Assertions.assertThat; + +import com.commercetools.api.client.ProjectApiRoot; +import com.commercetools.api.models.common.CentPrecisionMoneyBuilder; +import com.commercetools.api.models.common.DiscountedPriceDraft; +import com.commercetools.api.models.common.DiscountedPriceDraftBuilder; +import com.commercetools.api.models.common.Price; +import com.commercetools.api.models.common.PriceDraft; +import com.commercetools.api.models.common.PriceDraftBuilder; +import com.commercetools.api.models.common.TypedMoney; +import com.commercetools.api.models.product.ProductDraft; +import com.commercetools.api.models.product.ProductDraftBuilder; +import com.commercetools.api.models.product.ProductPagedQueryResponse; +import com.commercetools.api.models.product.ProductVariant; +import com.commercetools.api.models.product.ProductVariantDraft; +import com.commercetools.api.models.product.ProductVariantDraftBuilder; +import com.commercetools.api.models.product_discount.ProductDiscount; +import com.commercetools.api.models.product_discount.ProductDiscountDraft; +import com.commercetools.api.models.product_discount.ProductDiscountDraftBuilder; +import com.commercetools.api.models.product_discount.ProductDiscountValueExternalDraftBuilder; +import com.commercetools.api.models.product_type.ProductType; +import com.commercetools.api.models.product_type.ProductTypeDraft; +import com.commercetools.api.models.product_type.ProductTypeDraftBuilder; +import java.time.ZonedDateTime; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import uk.org.lidalia.slf4jext.Level; +import uk.org.lidalia.slf4jtest.TestLogger; +import uk.org.lidalia.slf4jtest.TestLoggerFactory; + +class ProductSyncWithDiscountedPriceIT { + private static final TestLogger cliRunnerTestLogger = + TestLoggerFactory.getTestLogger(CliRunner.class); + + private static final String MAIN_PRODUCT_TYPE_KEY = "main-product-type"; + private static final String MAIN_PRODUCT_MASTER_VARIANT_KEY = "main-product-master-variant-key"; + private static final String MAIN_PRODUCT_KEY = "product-with-references"; + private static final TypedMoney TEN_EUR = + CentPrecisionMoneyBuilder.of() + .centAmount(1000L) + .currencyCode("EUR") + .fractionDigits(2) + .build(); + + @BeforeEach + void setup() { + cliRunnerTestLogger.clearAll(); + ProductDiscountDraft productDiscountDraft = + ProductDiscountDraftBuilder.of() + .value(ProductDiscountValueExternalDraftBuilder.of().build()) + .name(ofEnglish("testProductDiscount")) + .predicate("1=1") + .sortOrder("0.9") + .isActive(true) + .build(); + ProductDiscount productDiscount = + CTP_TARGET_CLIENT.productDiscounts().post(productDiscountDraft).executeBlocking().getBody(); + setupProjectData(CTP_SOURCE_CLIENT, null); + setupProjectData(CTP_TARGET_CLIENT, productDiscount.getId()); + } + + static void setupProjectData( + @Nonnull final ProjectApiRoot ctpClient, final String productDiscountId) { + final ProductTypeDraft productTypeDraft = + ProductTypeDraftBuilder.of() + .key(MAIN_PRODUCT_TYPE_KEY) + .name(MAIN_PRODUCT_TYPE_KEY) + .description("a productType for t-shirts") + .build(); + + final ProductType productType = + ctpClient.productTypes().post(productTypeDraft).executeBlocking().getBody(); + + final PriceDraft priceDraft = + PriceDraftBuilder.of(getPriceDraft(22200L, "EUR", "DE", null, null, productDiscountId)) + .build(); + + final ProductVariantDraft masterVariant = + ProductVariantDraftBuilder.of() + .key(MAIN_PRODUCT_MASTER_VARIANT_KEY) + .sku(MAIN_PRODUCT_MASTER_VARIANT_KEY) + .prices(priceDraft) + .build(); + + final ProductDraft draft = + ProductDraftBuilder.of() + .productType(productType.toResourceIdentifier()) + .name(ofEnglish(MAIN_PRODUCT_KEY)) + .slug(ofEnglish(MAIN_PRODUCT_KEY)) + .masterVariant(masterVariant) + .key(MAIN_PRODUCT_KEY) + .build(); + + ctpClient.products().post(draft).executeBlocking(); + } + + @AfterAll + static void tearDownSuite() { + cleanUpProjects(CTP_SOURCE_CLIENT, CTP_TARGET_CLIENT); + } + + @Test + void run_WhenTargetProductHasDiscountedPrice_ShouldNotRemoveIt() { + // test + CliRunner.of() + .run(new String[] {"-s", "products", "-r", "runnerName", "-f"}, createITSyncerFactory()); + + // assertions + assertThat(cliRunnerTestLogger.getAllLoggingEvents()) + .allMatch(loggingEvent -> !Level.ERROR.equals(loggingEvent.getLevel())); + + final ProductPagedQueryResponse productPagedQueryResponse = + CTP_TARGET_CLIENT.products().get().execute().toCompletableFuture().join().getBody(); + + assertThat(productPagedQueryResponse.getResults()) + .hasSize(1) + .singleElement() + .satisfies( + product -> { + final ProductVariant stagedMasterVariant = + product.getMasterData().getStaged().getMasterVariant(); + assertThat(stagedMasterVariant.getPrices()) + .satisfies( + prices -> { + final Price price = prices.get(0); + assertThat(price.getDiscounted()).isNotNull(); + assertThat(price.getDiscounted().getValue()).isEqualTo(TEN_EUR); + }); + }); + } + + @Nonnull + public static PriceDraft getPriceDraft( + @Nonnull final Long value, + @Nonnull final String currencyCode, + @Nullable final String countryCode, + @Nullable final ZonedDateTime validFrom, + @Nullable final ZonedDateTime validUntil, + @Nullable final String productDiscountReferenceId) { + DiscountedPriceDraft discounted = null; + if (productDiscountReferenceId != null) { + discounted = + DiscountedPriceDraftBuilder.of() + .value(TEN_EUR) + .discount( + productDiscountReferenceBuilder -> + productDiscountReferenceBuilder.id(productDiscountReferenceId)) + .build(); + } + return PriceDraftBuilder.of() + .value(moneyBuilder -> moneyBuilder.centAmount(value).currencyCode(currencyCode)) + .country(countryCode) + .validFrom(validFrom) + .validUntil(validUntil) + .discounted(discounted) + .build(); + } +} diff --git a/src/integration-test/java/com/commercetools/project/sync/ProductSyncWithMasterVariantSwitchIT.java b/src/integration-test/java/com/commercetools/project/sync/ProductSyncWithMasterVariantSwitchIT.java index 7b3e2308..d51a497b 100644 --- a/src/integration-test/java/com/commercetools/project/sync/ProductSyncWithMasterVariantSwitchIT.java +++ b/src/integration-test/java/com/commercetools/project/sync/ProductSyncWithMasterVariantSwitchIT.java @@ -1,33 +1,24 @@ package com.commercetools.project.sync; +import static com.commercetools.api.models.common.LocalizedString.ofEnglish; +import static com.commercetools.project.sync.util.CtpClientUtils.CTP_SOURCE_CLIENT; +import static com.commercetools.project.sync.util.CtpClientUtils.CTP_TARGET_CLIENT; import static com.commercetools.project.sync.util.IntegrationTestUtils.cleanUpProjects; import static com.commercetools.project.sync.util.IntegrationTestUtils.createITSyncerFactory; -import static com.commercetools.project.sync.util.SphereClientUtils.CTP_SOURCE_CLIENT; -import static com.commercetools.project.sync.util.SphereClientUtils.CTP_TARGET_CLIENT; -import static io.sphere.sdk.models.LocalizedString.ofEnglish; import static org.assertj.core.api.Assertions.assertThat; -import io.sphere.sdk.client.SphereClient; -import io.sphere.sdk.models.LocalizedString; -import io.sphere.sdk.products.Product; -import io.sphere.sdk.products.ProductDraft; -import io.sphere.sdk.products.ProductDraftBuilder; -import io.sphere.sdk.products.ProductVariant; -import io.sphere.sdk.products.ProductVariantDraft; -import io.sphere.sdk.products.ProductVariantDraftBuilder; -import io.sphere.sdk.products.attributes.AttributeConstraint; -import io.sphere.sdk.products.attributes.AttributeDefinitionDraftBuilder; -import io.sphere.sdk.products.attributes.AttributeDraft; -import io.sphere.sdk.products.attributes.StringAttributeType; -import io.sphere.sdk.products.commands.ProductCreateCommand; -import io.sphere.sdk.products.queries.ProductQuery; -import io.sphere.sdk.producttypes.ProductType; -import io.sphere.sdk.producttypes.ProductTypeDraft; -import io.sphere.sdk.producttypes.ProductTypeDraftBuilder; -import io.sphere.sdk.producttypes.commands.ProductTypeCreateCommand; -import io.sphere.sdk.queries.PagedQueryResult; -import java.util.Collections; -import java.util.List; +import com.commercetools.api.client.ProjectApiRoot; +import com.commercetools.api.models.product.ProductDraft; +import com.commercetools.api.models.product.ProductDraftBuilder; +import com.commercetools.api.models.product.ProductPagedQueryResponse; +import com.commercetools.api.models.product.ProductVariant; +import com.commercetools.api.models.product.ProductVariantDraft; +import com.commercetools.api.models.product.ProductVariantDraftBuilder; +import com.commercetools.api.models.product_type.AttributeConstraintEnum; +import com.commercetools.api.models.product_type.AttributeTypeBuilder; +import com.commercetools.api.models.product_type.ProductType; +import com.commercetools.api.models.product_type.ProductTypeDraft; +import com.commercetools.api.models.product_type.ProductTypeDraftBuilder; import javax.annotation.Nonnull; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeEach; @@ -36,8 +27,6 @@ import uk.org.lidalia.slf4jtest.TestLogger; import uk.org.lidalia.slf4jtest.TestLoggerFactory; -// This will suppress MoreThanOneLogger warnings in this class -@SuppressWarnings("PMD.MoreThanOneLogger") class ProductSyncWithMasterVariantSwitchIT { private static final TestLogger cliRunnerTestLogger = TestLoggerFactory.getTestLogger(CliRunner.class); @@ -54,52 +43,56 @@ void setup() { } static void setupProjectData( - @Nonnull final SphereClient sphereClient, @Nonnull final String variantSuffix) { + @Nonnull final ProjectApiRoot ctpClient, @Nonnull final String variantSuffix) { final ProductTypeDraft productTypeDraft = - ProductTypeDraftBuilder.of( - MAIN_PRODUCT_TYPE_KEY, - MAIN_PRODUCT_TYPE_KEY, - "a productType for t-shirts", - Collections.singletonList( - AttributeDefinitionDraftBuilder.of( - StringAttributeType.of(), - "test", - LocalizedString.ofEnglish("test"), - false) - .attributeConstraint(AttributeConstraint.SAME_FOR_ALL) - .build())) + ProductTypeDraftBuilder.of() + .name(MAIN_PRODUCT_TYPE_KEY) + .key(MAIN_PRODUCT_TYPE_KEY) + .description("a productType for t-shirts") + .addAttributes( + attributeDefinitionDraftBuilder -> + attributeDefinitionDraftBuilder + .type(AttributeTypeBuilder::textBuilder) + .name("test") + .label(ofEnglish("test")) + .isSearchable(false) + .isRequired(false) + .attributeConstraint(AttributeConstraintEnum.SAME_FOR_ALL) + .build()) .build(); final ProductType productType = - sphereClient - .execute(ProductTypeCreateCommand.of(productTypeDraft)) - .toCompletableFuture() - .join(); + ctpClient.productTypes().post(productTypeDraft).executeBlocking().getBody(); final ProductVariantDraft masterVariant = ProductVariantDraftBuilder.of() .key("key" + variantSuffix) .sku("sku" + variantSuffix) - .attributes(AttributeDraft.of("test", "test" + variantSuffix)) + .addAttributes( + attributeBuilder -> + attributeBuilder.name("test").value("test" + variantSuffix).build()) .build(); final ProductVariantDraft variant1 = ProductVariantDraftBuilder.of() .key("key" + variantSuffix + "Variant1") .sku("sku" + variantSuffix + "Variant1") - .attributes(AttributeDraft.of("test", "test" + variantSuffix)) + .addAttributes( + attributeBuilder -> + attributeBuilder.name("test").value("test" + variantSuffix).build()) .build(); final ProductDraft draft = - ProductDraftBuilder.of( - productType, - ofEnglish(MAIN_PRODUCT_KEY), - ofEnglish(MAIN_PRODUCT_KEY), - List.of(masterVariant, variant1)) + ProductDraftBuilder.of() + .productType(productType.toResourceIdentifier()) + .name(ofEnglish(MAIN_PRODUCT_KEY)) + .slug(ofEnglish(MAIN_PRODUCT_KEY)) + .masterVariant(masterVariant) + .variants(variant1) .key(MAIN_PRODUCT_KEY) .build(); - sphereClient.execute(ProductCreateCommand.of(draft)).toCompletableFuture().join(); + ctpClient.products().post(draft).executeBlocking(); } @AfterAll @@ -117,10 +110,10 @@ void run_WhenTargetProductDifferentSkusAndKeys_ShouldSyncTargetCorrectly() { assertThat(cliRunnerTestLogger.getAllLoggingEvents()) .allMatch(loggingEvent -> !Level.ERROR.equals(loggingEvent.getLevel())); - final PagedQueryResult productQueryResult = - CTP_TARGET_CLIENT.execute(ProductQuery.of()).toCompletableFuture().join(); + final ProductPagedQueryResponse productPagedQueryResponse = + CTP_TARGET_CLIENT.products().get().execute().toCompletableFuture().join().getBody(); - assertThat(productQueryResult.getResults()) + assertThat(productPagedQueryResponse.getResults()) .hasSize(1) .singleElement() .satisfies( @@ -131,13 +124,12 @@ void run_WhenTargetProductDifferentSkusAndKeys_ShouldSyncTargetCorrectly() { product.getMasterData().getStaged().getVariants().get(0); assertThat(targetMasterVariant.getSku()).isEqualTo("skuSource"); assertThat(targetMasterVariant.getKey()).isEqualTo("keySource"); - assertThat(targetMasterVariant.getAttributes().get(0).getValueAsString()) + assertThat(targetMasterVariant.getAttributes().get(0).getValue()) .isEqualTo("testSource"); assertThat(targetVariant1.getSku()).isEqualTo("skuSourceVariant1"); assertThat(targetVariant1.getKey()).isEqualTo("keySourceVariant1"); - assertThat(targetVariant1.getAttributes().get(0).getValueAsString()) - .isEqualTo("testSource"); + assertThat(targetVariant1.getAttributes().get(0).getValue()).isEqualTo("testSource"); }); } } diff --git a/src/integration-test/java/com/commercetools/project/sync/ProductSyncWithNestedReferencesIT.java b/src/integration-test/java/com/commercetools/project/sync/ProductSyncWithNestedReferencesIT.java index 86bc1666..431c07ec 100644 --- a/src/integration-test/java/com/commercetools/project/sync/ProductSyncWithNestedReferencesIT.java +++ b/src/integration-test/java/com/commercetools/project/sync/ProductSyncWithNestedReferencesIT.java @@ -1,15 +1,17 @@ package com.commercetools.project.sync; +import static com.commercetools.api.models.common.LocalizedString.ofEnglish; +import static com.commercetools.project.sync.util.CtpClientUtils.CTP_SOURCE_CLIENT; +import static com.commercetools.project.sync.util.CtpClientUtils.CTP_TARGET_CLIENT; import static com.commercetools.project.sync.util.IntegrationTestUtils.assertCategoryExists; import static com.commercetools.project.sync.util.IntegrationTestUtils.assertCustomerExists; +import static com.commercetools.project.sync.util.IntegrationTestUtils.assertProductExists; import static com.commercetools.project.sync.util.IntegrationTestUtils.assertProductTypeExists; import static com.commercetools.project.sync.util.IntegrationTestUtils.assertStateExists; import static com.commercetools.project.sync.util.IntegrationTestUtils.cleanUpProjects; import static com.commercetools.project.sync.util.IntegrationTestUtils.createAttributeObject; import static com.commercetools.project.sync.util.IntegrationTestUtils.createITSyncerFactory; -import static com.commercetools.project.sync.util.IntegrationTestUtils.createReferenceObject; -import static com.commercetools.project.sync.util.SphereClientUtils.CTP_SOURCE_CLIENT; -import static com.commercetools.project.sync.util.SphereClientUtils.CTP_TARGET_CLIENT; +import static com.commercetools.project.sync.util.IntegrationTestUtils.createReferenceOfType; import static com.commercetools.project.sync.util.TestUtils.assertCartDiscountSyncerLoggingEvents; import static com.commercetools.project.sync.util.TestUtils.assertCategorySyncerLoggingEvents; import static com.commercetools.project.sync.util.TestUtils.assertCustomObjectSyncerLoggingEvents; @@ -20,13 +22,41 @@ import static com.commercetools.project.sync.util.TestUtils.assertShoppingListSyncerLoggingEvents; import static com.commercetools.project.sync.util.TestUtils.assertStateSyncerLoggingEvents; import static com.commercetools.project.sync.util.TestUtils.assertTaxCategorySyncerLoggingEvents; -import static com.commercetools.project.sync.util.TestUtils.assertTypeSyncerLoggingEvents; -import static io.sphere.sdk.models.LocalizedString.ofEnglish; -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 com.commercetools.api.client.ProjectApiRoot; +import com.commercetools.api.models.category.Category; +import com.commercetools.api.models.category.CategoryDraft; +import com.commercetools.api.models.category.CategoryDraftBuilder; +import com.commercetools.api.models.common.Reference; +import com.commercetools.api.models.common.ReferenceTypeId; +import com.commercetools.api.models.custom_object.CustomObject; +import com.commercetools.api.models.custom_object.CustomObjectDraft; +import com.commercetools.api.models.custom_object.CustomObjectDraftBuilder; +import com.commercetools.api.models.customer.Customer; +import com.commercetools.api.models.customer.CustomerDraft; +import com.commercetools.api.models.customer.CustomerDraftBuilder; +import com.commercetools.api.models.customer.CustomerSignInResult; +import com.commercetools.api.models.product.Attribute; +import com.commercetools.api.models.product.AttributeAccessor; +import com.commercetools.api.models.product.AttributeBuilder; +import com.commercetools.api.models.product.Product; +import com.commercetools.api.models.product.ProductDraft; +import com.commercetools.api.models.product.ProductDraftBuilder; +import com.commercetools.api.models.product.ProductVariant; +import com.commercetools.api.models.product.ProductVariantDraft; +import com.commercetools.api.models.product.ProductVariantDraftBuilder; +import com.commercetools.api.models.product_type.AttributeConstraintEnum; +import com.commercetools.api.models.product_type.AttributeDefinitionDraft; +import com.commercetools.api.models.product_type.AttributeDefinitionDraftBuilder; +import com.commercetools.api.models.product_type.AttributeReferenceTypeId; +import com.commercetools.api.models.product_type.ProductType; +import com.commercetools.api.models.product_type.ProductTypeDraft; +import com.commercetools.api.models.product_type.ProductTypeDraftBuilder; +import com.commercetools.api.models.state.State; +import com.commercetools.api.models.state.StateDraft; +import com.commercetools.api.models.state.StateDraftBuilder; +import com.commercetools.api.models.state.StateTypeEnum; import com.commercetools.project.sync.cartdiscount.CartDiscountSyncer; import com.commercetools.project.sync.category.CategorySyncer; import com.commercetools.project.sync.customer.CustomerSyncer; @@ -38,49 +68,15 @@ import com.commercetools.project.sync.state.StateSyncer; import com.commercetools.project.sync.taxcategory.TaxCategorySyncer; import com.commercetools.project.sync.type.TypeSyncer; -import com.fasterxml.jackson.databind.JsonNode; +import com.commercetools.project.sync.util.TestUtils; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.JsonNodeFactory; import com.fasterxml.jackson.databind.node.ObjectNode; -import io.sphere.sdk.categories.Category; -import io.sphere.sdk.categories.CategoryDraft; -import io.sphere.sdk.categories.CategoryDraftBuilder; -import io.sphere.sdk.categories.commands.CategoryCreateCommand; -import io.sphere.sdk.client.SphereClient; -import io.sphere.sdk.customers.Customer; -import io.sphere.sdk.customers.CustomerDraft; -import io.sphere.sdk.customers.CustomerDraftBuilder; -import io.sphere.sdk.customers.CustomerSignInResult; -import io.sphere.sdk.customers.commands.CustomerCreateCommand; -import io.sphere.sdk.customobjects.CustomObject; -import io.sphere.sdk.customobjects.CustomObjectDraft; -import io.sphere.sdk.customobjects.commands.CustomObjectUpsertCommand; -import io.sphere.sdk.products.Product; -import io.sphere.sdk.products.ProductDraft; -import io.sphere.sdk.products.ProductDraftBuilder; -import io.sphere.sdk.products.ProductVariant; -import io.sphere.sdk.products.ProductVariantDraft; -import io.sphere.sdk.products.ProductVariantDraftBuilder; -import io.sphere.sdk.products.attributes.AttributeConstraint; -import io.sphere.sdk.products.attributes.AttributeDefinitionDraft; -import io.sphere.sdk.products.attributes.AttributeDefinitionDraftBuilder; -import io.sphere.sdk.products.attributes.AttributeDraft; -import io.sphere.sdk.products.attributes.NestedAttributeType; -import io.sphere.sdk.products.attributes.ReferenceAttributeType; -import io.sphere.sdk.products.attributes.SetAttributeType; -import io.sphere.sdk.products.commands.ProductCreateCommand; -import io.sphere.sdk.products.queries.ProductQuery; -import io.sphere.sdk.producttypes.ProductType; -import io.sphere.sdk.producttypes.ProductTypeDraft; -import io.sphere.sdk.producttypes.ProductTypeDraftBuilder; -import io.sphere.sdk.producttypes.commands.ProductTypeCreateCommand; -import io.sphere.sdk.queries.PagedQueryResult; -import io.sphere.sdk.states.State; -import io.sphere.sdk.states.StateDraft; -import io.sphere.sdk.states.StateDraftBuilder; -import io.sphere.sdk.states.StateType; -import io.sphere.sdk.states.commands.StateCreateCommand; +import java.util.ArrayList; import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; import javax.annotation.Nonnull; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeEach; @@ -145,159 +141,134 @@ void setup() { setupSourceProjectData(CTP_SOURCE_CLIENT); } - static void setupSourceProjectData(@Nonnull final SphereClient sourceProjectClient) { - final AttributeDefinitionDraft setOfCategoriesAttributeDef = - AttributeDefinitionDraftBuilder.of( - SetAttributeType.of(ReferenceAttributeType.ofCategory()), - "categories", - ofEnglish("categories"), - false) - .searchable(false) - .attributeConstraint(AttributeConstraint.NONE) - .build(); - - final AttributeDefinitionDraft setOfProductTypeAttributeDef = - AttributeDefinitionDraftBuilder.of( - SetAttributeType.of(ReferenceAttributeType.ofProductType()), - "productTypes", - ofEnglish("productTypes"), - false) - .searchable(false) - .attributeConstraint(AttributeConstraint.NONE) - .build(); - - final AttributeDefinitionDraft setOfCustomObjectAttributeDef = - AttributeDefinitionDraftBuilder.of( - SetAttributeType.of(ReferenceAttributeType.of(CustomObject.referenceTypeId())), - "customObjects", - ofEnglish("customObjects"), - false) - .searchable(false) - .attributeConstraint(AttributeConstraint.NONE) - .build(); - - final AttributeDefinitionDraft setOfStateAttributeDef = - AttributeDefinitionDraftBuilder.of( - SetAttributeType.of(ReferenceAttributeType.of(State.referenceTypeId())), - "states", - ofEnglish("states"), - false) - .searchable(false) - .attributeConstraint(AttributeConstraint.NONE) - .build(); - - final AttributeDefinitionDraft setOfCustomerAttributeDef = - AttributeDefinitionDraftBuilder.of( - SetAttributeType.of(ReferenceAttributeType.of(Customer.referenceTypeId())), - "customers", - ofEnglish("customers"), - false) - .searchable(false) - .attributeConstraint(AttributeConstraint.NONE) - .build(); + static void setupSourceProjectData(@Nonnull final ProjectApiRoot sourceProjectClient) { + final List attributeList = + List.of( + AttributeReferenceTypeId.PRODUCT_TYPE, + AttributeReferenceTypeId.CATEGORY, + AttributeReferenceTypeId.KEY_VALUE_DOCUMENT, + AttributeReferenceTypeId.STATE, + AttributeReferenceTypeId.CUSTOMER); + final Map attributeDefinitionDrafts = + attributeList.stream() + .map( + typeId -> + AttributeDefinitionDraftBuilder.of() + .type( + attributeTypeBuilder -> + attributeTypeBuilder + .setBuilder() + .elementType( + builder -> + builder.referenceBuilder().referenceTypeId(typeId))) + .name(typeId.getJsonName()) + .label(ofEnglish(typeId.getJsonName())) + .isRequired(false) + .isSearchable(false) + .attributeConstraint(AttributeConstraintEnum.NONE) + .build()) + .collect( + Collectors.toMap( + attributeDefinitionDraft -> attributeDefinitionDraft.getName(), + attributeDefinitionDraft -> attributeDefinitionDraft)); final ProductTypeDraft nestedProductTypeDraft = - ProductTypeDraftBuilder.of( - INNER_PRODUCT_TYPE_KEY, - INNER_PRODUCT_TYPE_KEY, - "an inner productType for t-shirts", - emptyList()) - .attributes( - asList( - setOfCategoriesAttributeDef, - setOfProductTypeAttributeDef, - setOfCustomObjectAttributeDef, - setOfStateAttributeDef, - setOfCustomerAttributeDef)) + ProductTypeDraftBuilder.of() + .key(INNER_PRODUCT_TYPE_KEY) + .name(INNER_PRODUCT_TYPE_KEY) + .description("an inner productType for t-shirts") + .attributes(List.copyOf(attributeDefinitionDrafts.values())) .build(); final ProductType innerProductType = - sourceProjectClient - .execute(ProductTypeCreateCommand.of(nestedProductTypeDraft)) - .toCompletableFuture() - .join(); + sourceProjectClient.productTypes().post(nestedProductTypeDraft).executeBlocking().getBody(); final AttributeDefinitionDraft nestedAttribute = - AttributeDefinitionDraftBuilder.of( - SetAttributeType.of(NestedAttributeType.of(innerProductType)), - NESTED_ATTRIBUTE_NAME, - ofEnglish("nested attribute"), - false) - .searchable(false) - .attributeConstraint(AttributeConstraint.NONE) + AttributeDefinitionDraftBuilder.of() + .type( + attributeTypeBuilder -> + attributeTypeBuilder + .setBuilder() + .elementType( + nestedBuilder -> + nestedBuilder + .nestedBuilder() + .typeReference(innerProductType.toReference()))) + .name(NESTED_ATTRIBUTE_NAME) + .label(ofEnglish("nested attribute")) + .isRequired(false) + .isSearchable(false) + .attributeConstraint(AttributeConstraintEnum.NONE) .build(); final ProductTypeDraft productTypeDraft = - ProductTypeDraftBuilder.of( - MAIN_PRODUCT_TYPE_KEY, - MAIN_PRODUCT_TYPE_KEY, - "a productType for t-shirts", - emptyList()) - .attributes(singletonList(nestedAttribute)) + ProductTypeDraftBuilder.of() + .key(MAIN_PRODUCT_TYPE_KEY) + .name(MAIN_PRODUCT_TYPE_KEY) + .description("a productType for t-shirts") + .attributes(nestedAttribute) .build(); final ProductType mainProductType = - sourceProjectClient - .execute(ProductTypeCreateCommand.of(productTypeDraft)) - .toCompletableFuture() - .join(); + sourceProjectClient.productTypes().post(productTypeDraft).executeBlocking().getBody(); final CategoryDraft categoryDraft = - CategoryDraftBuilder.of(ofEnglish("t-shirts"), ofEnglish("t-shirts")) + CategoryDraftBuilder.of() + .name(ofEnglish("t-shirts")) + .slug(ofEnglish("t-shirts")) .key(CATEGORY_KEY) .build(); final Category category = - sourceProjectClient - .execute(CategoryCreateCommand.of(categoryDraft)) - .toCompletableFuture() - .join(); + sourceProjectClient.categories().post(categoryDraft).executeBlocking().getBody(); final StateDraft stateDraft = - StateDraftBuilder.of(STATE_KEY, StateType.PRODUCT_STATE) - .roles(Collections.emptySet()) + StateDraftBuilder.of() + .key(STATE_KEY) + .type(StateTypeEnum.PRODUCT_STATE) + .roles(Collections.emptyList()) .description(ofEnglish("states")) .name(ofEnglish("states")) .initial(true) - .transitions(Collections.emptySet()) + .transitions(Collections.emptyList()) .build(); - final State state = - sourceProjectClient.execute(StateCreateCommand.of(stateDraft)).toCompletableFuture().join(); + final State state = sourceProjectClient.states().post(stateDraft).executeBlocking().getBody(); final CustomerDraft customerDraft = - CustomerDraftBuilder.of("test@email.com", "testPassword").key(CUSTOMER_KEY).build(); + CustomerDraftBuilder.of() + .email("test@email.com") + .password("testPassword") + .key(CUSTOMER_KEY) + .build(); final CustomerSignInResult customerSignInResult = - sourceProjectClient - .execute(CustomerCreateCommand.of(customerDraft)) - .toCompletableFuture() - .join(); + sourceProjectClient.customers().post(customerDraft).executeBlocking().getBody(); + final Customer customer = customerSignInResult.getCustomer(); final ObjectNode customObjectValue = JsonNodeFactory.instance.objectNode().put("field", "value1"); - final CustomObjectDraft customObjectDraft = - CustomObjectDraft.ofUnversionedUpsert("container", "key1", customObjectValue); - - final ObjectNode customObjectValue2 = - JsonNodeFactory.instance.objectNode().put("field", "value2"); + final CustomObjectDraft customObjectDraft = + CustomObjectDraftBuilder.of() + .container("container") + .key("key1") + .value(customObjectValue) + .build(); - final CustomObjectDraft customObjectDraft2 = - CustomObjectDraft.ofUnversionedUpsert("container", "key2", customObjectValue2); + final CustomObjectDraft customObjectDraft2 = + CustomObjectDraftBuilder.of() + .container("container") + .key("key2") + .value("{\"field\": \"value2\"}") + .build(); - final CustomObject customObject1 = - sourceProjectClient - .execute(CustomObjectUpsertCommand.of(customObjectDraft)) - .toCompletableFuture() - .join(); + final CustomObject customObject1 = + sourceProjectClient.customObjects().post(customObjectDraft).executeBlocking().getBody(); - final CustomObject customObject2 = - sourceProjectClient - .execute(CustomObjectUpsertCommand.of(customObjectDraft2)) - .toCompletableFuture() - .join(); + final CustomObject customObject2 = + sourceProjectClient.customObjects().post(customObjectDraft2).executeBlocking().getBody(); final ArrayNode setAttributeValue = JsonNodeFactory.instance.arrayNode(); @@ -305,44 +276,58 @@ static void setupSourceProjectData(@Nonnull final SphereClient sourceProjectClie final ArrayNode categoriesReferencesAttributeValue = JsonNodeFactory.instance.arrayNode(); categoriesReferencesAttributeValue.add( - createReferenceObject(Category.referenceTypeId(), category.getId())); + createReferenceOfType(AttributeReferenceTypeId.CATEGORY.getJsonName(), category.getId())); final ArrayNode productTypesReferencesAttributeValue = JsonNodeFactory.instance.arrayNode(); productTypesReferencesAttributeValue.add( - createReferenceObject(ProductType.referenceTypeId(), mainProductType.getId())); + createReferenceOfType( + AttributeReferenceTypeId.PRODUCT_TYPE.getJsonName(), mainProductType.getId())); final ArrayNode customObjectReferencesAttributeValue = JsonNodeFactory.instance.arrayNode(); customObjectReferencesAttributeValue.add( - createReferenceObject(CustomObject.referenceTypeId(), customObject1.getId())); + createReferenceOfType( + AttributeReferenceTypeId.KEY_VALUE_DOCUMENT.getJsonName(), customObject1.getId())); customObjectReferencesAttributeValue.add( - createReferenceObject(CustomObject.referenceTypeId(), customObject2.getId())); + createReferenceOfType( + AttributeReferenceTypeId.KEY_VALUE_DOCUMENT.getJsonName(), customObject2.getId())); final ArrayNode stateReferenceAttributeValue = JsonNodeFactory.instance.arrayNode(); - stateReferenceAttributeValue.add(createReferenceObject(State.referenceTypeId(), state.getId())); + stateReferenceAttributeValue.add( + createReferenceOfType(AttributeReferenceTypeId.STATE.getJsonName(), state.getId())); final ArrayNode customerReferenceAttributeValue = JsonNodeFactory.instance.arrayNode(); customerReferenceAttributeValue.add( - createReferenceObject(Customer.referenceTypeId(), customer.getId())); + createReferenceOfType(AttributeReferenceTypeId.CUSTOMER.getJsonName(), customer.getId())); + final AttributeDefinitionDraft categoriesAttributeDef = + attributeDefinitionDrafts.get(AttributeReferenceTypeId.CATEGORY.getJsonName()); nestedAttributeValue.add( createAttributeObject( - setOfCategoriesAttributeDef.getName(), categoriesReferencesAttributeValue)); + categoriesAttributeDef.getName(), categoriesReferencesAttributeValue)); + final AttributeDefinitionDraft setOfProductTypeAttributeDef = + attributeDefinitionDrafts.get(ReferenceTypeId.PRODUCT_TYPE.getJsonName()); nestedAttributeValue.add( createAttributeObject( setOfProductTypeAttributeDef.getName(), productTypesReferencesAttributeValue)); + final AttributeDefinitionDraft setOfCustomObjectAttributeDef = + attributeDefinitionDrafts.get(ReferenceTypeId.KEY_VALUE_DOCUMENT.getJsonName()); nestedAttributeValue.add( createAttributeObject( setOfCustomObjectAttributeDef.getName(), customObjectReferencesAttributeValue)); + final AttributeDefinitionDraft setOfStateAttributeDef = + attributeDefinitionDrafts.get(ReferenceTypeId.STATE.getJsonName()); nestedAttributeValue.add( createAttributeObject(setOfStateAttributeDef.getName(), stateReferenceAttributeValue)); + final AttributeDefinitionDraft setOfCustomerAttributeDef = + attributeDefinitionDrafts.get(ReferenceTypeId.CUSTOMER.getJsonName()); nestedAttributeValue.add( createAttributeObject( setOfCustomerAttributeDef.getName(), customerReferenceAttributeValue)); setAttributeValue.add(nestedAttributeValue); - final AttributeDraft setOfNestedAttribute = - AttributeDraft.of(nestedAttribute.getName(), setAttributeValue); + final Attribute setOfNestedAttribute = + AttributeBuilder.of().name(nestedAttribute.getName()).value(setAttributeValue).build(); final ProductVariantDraft masterVariant = ProductVariantDraftBuilder.of() @@ -352,18 +337,15 @@ static void setupSourceProjectData(@Nonnull final SphereClient sourceProjectClie .build(); final ProductDraft productDraftWithNestedAttribute = - ProductDraftBuilder.of( - mainProductType, - ofEnglish(MAIN_PRODUCT_KEY), - ofEnglish(MAIN_PRODUCT_KEY), - masterVariant) + ProductDraftBuilder.of() + .productType(mainProductType.toResourceIdentifier()) + .name(ofEnglish(MAIN_PRODUCT_KEY)) + .slug(ofEnglish(MAIN_PRODUCT_KEY)) + .masterVariant(masterVariant) .key(MAIN_PRODUCT_KEY) .build(); - sourceProjectClient - .execute(ProductCreateCommand.of(productDraftWithNestedAttribute)) - .toCompletableFuture() - .join(); + sourceProjectClient.products().post(productDraftWithNestedAttribute).executeBlocking(); } @AfterAll @@ -380,7 +362,7 @@ void run_WithSyncAsArgumentWithAllArgAsFullSync_ShouldExecuteAllSyncers() { assertThat(cliRunnerTestLogger.getAllLoggingEvents()) .allMatch(loggingEvent -> !Level.ERROR.equals(loggingEvent.getLevel())); - assertTypeSyncerLoggingEvents(typeSyncerTestLogger, 0); + TestUtils.assertTypeSyncerLoggingEvents(typeSyncerTestLogger, 0); assertProductTypeSyncerLoggingEvents(productTypeSyncerTestLogger, 2); assertTaxCategorySyncerLoggingEvents(taxCategorySyncerTestLogger, 0); assertCategorySyncerLoggingEvents(categorySyncerTestLogger, 1); @@ -396,7 +378,7 @@ void run_WithSyncAsArgumentWithAllArgAsFullSync_ShouldExecuteAllSyncers() { } private static void assertAllResourcesAreSyncedToTarget( - @Nonnull final SphereClient targetClient) { + @Nonnull final ProjectApiRoot targetClient) { assertProductTypeExists(targetClient, INNER_PRODUCT_TYPE_KEY); final ProductType productType = assertProductTypeExists(targetClient, MAIN_PRODUCT_TYPE_KEY); @@ -404,92 +386,85 @@ private static void assertAllResourcesAreSyncedToTarget( final State state = assertStateExists(targetClient, STATE_KEY); final Customer customer = assertCustomerExists(targetClient, CUSTOMER_KEY); - final PagedQueryResult productQueryResult = - targetClient.execute(ProductQuery.of()).toCompletableFuture().join(); - - assertThat(productQueryResult.getResults()) - .hasSize(1) - .singleElement() + final Product mainProduct = + assertProductExists( + targetClient, + MAIN_PRODUCT_KEY, + MAIN_PRODUCT_MASTER_VARIANT_KEY, + MAIN_PRODUCT_MASTER_VARIANT_KEY); + + assertThat(mainProduct.getKey()).isEqualTo(MAIN_PRODUCT_KEY); + final ProductVariant stagedMasterVariant = + mainProduct.getMasterData().getStaged().getMasterVariant(); + assertThat(stagedMasterVariant.getKey()).isEqualTo(MAIN_PRODUCT_MASTER_VARIANT_KEY); + assertThat(stagedMasterVariant.getAttributes()).hasSize(1); + assertThat(stagedMasterVariant.getAttribute(NESTED_ATTRIBUTE_NAME)) .satisfies( - product -> { - assertThat(product.getKey()).isEqualTo(MAIN_PRODUCT_KEY); - final ProductVariant stagedMasterVariant = - product.getMasterData().getStaged().getMasterVariant(); - assertThat(stagedMasterVariant.getKey()).isEqualTo(MAIN_PRODUCT_MASTER_VARIANT_KEY); - assertThat(stagedMasterVariant.getAttributes()).hasSize(1); - assertThat(stagedMasterVariant.getAttribute(NESTED_ATTRIBUTE_NAME)) + attribute -> { + final List> attributeAsSetNested = + AttributeAccessor.asSetNested(attribute); + assertThat(attributeAsSetNested).isExactlyInstanceOf(ArrayList.class); + assertThat(attributeAsSetNested).hasSize(1); + + final List nestedAttributeElement = attributeAsSetNested.get(0); + assertThat(nestedAttributeElement).isExactlyInstanceOf(ArrayList.class); + assertThat(nestedAttributeElement).hasSize(5); + + assertThat(nestedAttributeElement.get(0).getName()) + .isEqualTo(AttributeReferenceTypeId.CATEGORY.getJsonName()); + assertThat(nestedAttributeElement.get(0).getValue()) + .isExactlyInstanceOf(ArrayList.class); + final List categoryReferences = + AttributeAccessor.asSetReference(nestedAttributeElement.get(0)); + assertThat(categoryReferences) + .singleElement() + .satisfies( + categoryReference -> + assertThat(categoryReference.getId()).isEqualTo(category.getId())); + + assertThat(nestedAttributeElement.get(1).getName()) + .isEqualTo(AttributeReferenceTypeId.PRODUCT_TYPE.getJsonName()); + assertThat(nestedAttributeElement.get(1).getValue()) + .isExactlyInstanceOf(ArrayList.class); + final List productTypeReferences = + AttributeAccessor.asSetReference(nestedAttributeElement.get(1)); + assertThat(productTypeReferences) + .singleElement() + .satisfies( + productTypeReference -> + assertThat(productTypeReference.getId()).isEqualTo(productType.getId())); + + assertThat(nestedAttributeElement.get(2).getName()) + .isEqualTo(AttributeReferenceTypeId.KEY_VALUE_DOCUMENT.getJsonName()); + assertThat(nestedAttributeElement.get(2).getValue()) + .isExactlyInstanceOf(ArrayList.class); + final List customObjectReferences = + AttributeAccessor.asSetReference(nestedAttributeElement.get(2)); + assertThat(customObjectReferences).hasSize(2); + + assertThat(nestedAttributeElement.get(3).getName()) + .isEqualTo(AttributeReferenceTypeId.STATE.getJsonName()); + assertThat(nestedAttributeElement.get(3).getValue()) + .isExactlyInstanceOf(ArrayList.class); + final List stateReferences = + AttributeAccessor.asSetReference(nestedAttributeElement.get(3)); + assertThat(stateReferences) + .singleElement() + .satisfies( + stateReference -> + assertThat(stateReference.getId()).isEqualTo(state.getId())); + + assertThat(nestedAttributeElement.get(4).getName()) + .isEqualTo(AttributeReferenceTypeId.CUSTOMER.getJsonName()); + assertThat(nestedAttributeElement.get(4).getValue()) + .isExactlyInstanceOf(ArrayList.class); + final List customerReferences = + AttributeAccessor.asSetReference(nestedAttributeElement.get(4)); + assertThat(customerReferences) + .singleElement() .satisfies( - attribute -> { - final JsonNode attributeValue = attribute.getValueAsJsonNode(); - assertThat(attributeValue).isExactlyInstanceOf(ArrayNode.class); - final ArrayNode attributeValueAsArray = (ArrayNode) attributeValue; - assertThat(attributeValueAsArray).hasSize(1); - - final JsonNode nestedAttributeElement = attributeValueAsArray.get(0); - assertThat(nestedAttributeElement).isExactlyInstanceOf(ArrayNode.class); - final ArrayNode nestedTypeAttributes = (ArrayNode) nestedAttributeElement; - assertThat(nestedTypeAttributes).hasSize(5); - - assertThat(nestedTypeAttributes.get(0).get("name").asText()) - .isEqualTo("categories"); - assertThat(nestedTypeAttributes.get(0).get("value")) - .isExactlyInstanceOf(ArrayNode.class); - final ArrayNode categoryReferences = - (ArrayNode) (nestedTypeAttributes.get(0).get("value")); - assertThat(categoryReferences) - .singleElement() - .satisfies( - categoryReference -> - assertThat(categoryReference.get("id").asText()) - .isEqualTo(category.getId())); - - assertThat(nestedTypeAttributes.get(1).get("name").asText()) - .isEqualTo("productTypes"); - assertThat(nestedTypeAttributes.get(1).get("value")) - .isExactlyInstanceOf(ArrayNode.class); - final ArrayNode productTypeReferences = - (ArrayNode) (nestedTypeAttributes.get(1).get("value")); - assertThat(productTypeReferences) - .singleElement() - .satisfies( - productTypeReference -> - assertThat(productTypeReference.get("id").asText()) - .isEqualTo(productType.getId())); - - assertThat(nestedTypeAttributes.get(2).get("name").asText()) - .isEqualTo("customObjects"); - assertThat(nestedTypeAttributes.get(2).get("value")) - .isExactlyInstanceOf(ArrayNode.class); - final ArrayNode customObjectReferences = - (ArrayNode) (nestedTypeAttributes.get(2).get("value")); - assertThat(customObjectReferences).hasSize(2); - - assertThat(nestedTypeAttributes.get(3).get("name").asText()) - .isEqualTo("states"); - assertThat(nestedTypeAttributes.get(3).get("value")) - .isExactlyInstanceOf(ArrayNode.class); - final ArrayNode stateReferences = - (ArrayNode) (nestedTypeAttributes.get(3).get("value")); - assertThat(stateReferences) - .singleElement() - .satisfies( - stateReference -> - assertThat(stateReference.get("id").asText()) - .isEqualTo(state.getId())); - - assertThat(nestedTypeAttributes.get(4).get("name").asText()) - .isEqualTo("customers"); - assertThat(nestedTypeAttributes.get(4).get("value")) - .isExactlyInstanceOf(ArrayNode.class); - final ArrayNode customerReferences = - (ArrayNode) (nestedTypeAttributes.get(4).get("value")); - assertThat(customerReferences) - .singleElement() - .satisfies( - customerReference -> - assertThat(customerReference.get("id").asText()) - .isEqualTo(customer.getId())); - }); + customerReference -> + assertThat(customerReference.getId()).isEqualTo(customer.getId())); }); } } diff --git a/src/integration-test/java/com/commercetools/project/sync/ProductSyncWithReferenceResolutionIT.java b/src/integration-test/java/com/commercetools/project/sync/ProductSyncWithReferenceResolutionIT.java index 5766fbc6..cbb3aee6 100644 --- a/src/integration-test/java/com/commercetools/project/sync/ProductSyncWithReferenceResolutionIT.java +++ b/src/integration-test/java/com/commercetools/project/sync/ProductSyncWithReferenceResolutionIT.java @@ -1,86 +1,62 @@ package com.commercetools.project.sync; +import static com.commercetools.api.models.common.LocalizedString.ofEnglish; +import static com.commercetools.project.sync.ProductSyncWithDiscountedPriceIT.getPriceDraft; +import static com.commercetools.project.sync.util.CtpClientUtils.CTP_SOURCE_CLIENT; +import static com.commercetools.project.sync.util.CtpClientUtils.CTP_TARGET_CLIENT; import static com.commercetools.project.sync.util.IntegrationTestUtils.assertCategoryExists; import static com.commercetools.project.sync.util.IntegrationTestUtils.assertProductExists; import static com.commercetools.project.sync.util.IntegrationTestUtils.assertProductTypeExists; import static com.commercetools.project.sync.util.IntegrationTestUtils.cleanUpProjects; import static com.commercetools.project.sync.util.IntegrationTestUtils.createITSyncerFactory; -import static com.commercetools.project.sync.util.SphereClientUtils.CTP_SOURCE_CLIENT; -import static com.commercetools.project.sync.util.SphereClientUtils.CTP_TARGET_CLIENT; -import static com.neovisionaries.i18n.CountryCode.DE; -import static io.sphere.sdk.models.DefaultCurrencyUnits.EUR; -import static io.sphere.sdk.models.LocalizedString.ofEnglish; -import static java.lang.String.format; import static java.util.Arrays.asList; -import static java.util.Collections.emptyList; -import static java.util.Collections.emptyMap; -import static java.util.Collections.singleton; -import static java.util.Collections.singletonList; -import static java.util.Optional.ofNullable; import static org.assertj.core.api.Assertions.assertThat; +import com.commercetools.api.client.ProjectApiRoot; +import com.commercetools.api.models.category.CategoryDraft; +import com.commercetools.api.models.category.CategoryDraftBuilder; +import com.commercetools.api.models.channel.ChannelDraft; +import com.commercetools.api.models.channel.ChannelDraftBuilder; +import com.commercetools.api.models.channel.ChannelRoleEnum; +import com.commercetools.api.models.common.AssetDraft; +import com.commercetools.api.models.common.AssetDraftBuilder; +import com.commercetools.api.models.common.AssetSourceBuilder; +import com.commercetools.api.models.common.PriceDraft; +import com.commercetools.api.models.common.PriceDraftBuilder; +import com.commercetools.api.models.customer_group.CustomerGroup; +import com.commercetools.api.models.customer_group.CustomerGroupDraft; +import com.commercetools.api.models.customer_group.CustomerGroupDraftBuilder; +import com.commercetools.api.models.product.ProductDraft; +import com.commercetools.api.models.product.ProductDraftBuilder; +import com.commercetools.api.models.product.ProductVariantDraft; +import com.commercetools.api.models.product.ProductVariantDraftBuilder; +import com.commercetools.api.models.product_type.ProductType; +import com.commercetools.api.models.product_type.ProductTypeDraft; +import com.commercetools.api.models.product_type.ProductTypeDraftBuilder; +import com.commercetools.api.models.state.State; +import com.commercetools.api.models.state.StateDraft; +import com.commercetools.api.models.state.StateDraftBuilder; +import com.commercetools.api.models.state.StateTypeEnum; +import com.commercetools.api.models.tax_category.TaxCategory; +import com.commercetools.api.models.tax_category.TaxCategoryDraft; +import com.commercetools.api.models.tax_category.TaxCategoryDraftBuilder; +import com.commercetools.api.models.tax_category.TaxRateDraft; +import com.commercetools.api.models.tax_category.TaxRateDraftBuilder; +import com.commercetools.api.models.type.CustomFieldsDraft; +import com.commercetools.api.models.type.CustomFieldsDraftBuilder; +import com.commercetools.api.models.type.FieldDefinition; +import com.commercetools.api.models.type.FieldDefinitionBuilder; +import com.commercetools.api.models.type.FieldTypeBuilder; +import com.commercetools.api.models.type.ResourceTypeId; +import com.commercetools.api.models.type.Type; +import com.commercetools.api.models.type.TypeDraft; +import com.commercetools.api.models.type.TypeDraftBuilder; +import com.commercetools.api.models.type.TypeTextInputHint; import com.commercetools.project.sync.product.ProductSyncer; -import com.neovisionaries.i18n.CountryCode; -import io.sphere.sdk.categories.CategoryDraft; -import io.sphere.sdk.categories.CategoryDraftBuilder; -import io.sphere.sdk.categories.commands.CategoryCreateCommand; -import io.sphere.sdk.channels.Channel; -import io.sphere.sdk.channels.ChannelDraft; -import io.sphere.sdk.channels.ChannelDraftBuilder; -import io.sphere.sdk.channels.ChannelRole; -import io.sphere.sdk.channels.commands.ChannelCreateCommand; -import io.sphere.sdk.client.SphereClient; -import io.sphere.sdk.customergroups.CustomerGroup; -import io.sphere.sdk.customergroups.CustomerGroupDraft; -import io.sphere.sdk.customergroups.CustomerGroupDraftBuilder; -import io.sphere.sdk.customergroups.commands.CustomerGroupCreateCommand; -import io.sphere.sdk.models.AssetDraft; -import io.sphere.sdk.models.AssetDraftBuilder; -import io.sphere.sdk.models.AssetSourceBuilder; -import io.sphere.sdk.models.LocalizedString; -import io.sphere.sdk.models.ResourceIdentifier; -import io.sphere.sdk.models.TextInputHint; -import io.sphere.sdk.products.Price; -import io.sphere.sdk.products.PriceDraft; -import io.sphere.sdk.products.PriceDraftBuilder; -import io.sphere.sdk.products.ProductDraft; -import io.sphere.sdk.products.ProductDraftBuilder; -import io.sphere.sdk.products.ProductVariantDraft; -import io.sphere.sdk.products.ProductVariantDraftBuilder; -import io.sphere.sdk.products.commands.ProductCreateCommand; -import io.sphere.sdk.producttypes.ProductType; -import io.sphere.sdk.producttypes.ProductTypeDraft; -import io.sphere.sdk.producttypes.ProductTypeDraftBuilder; -import io.sphere.sdk.producttypes.commands.ProductTypeCreateCommand; -import io.sphere.sdk.queries.PagedQueryResult; -import io.sphere.sdk.queries.QueryPredicate; -import io.sphere.sdk.states.State; -import io.sphere.sdk.states.StateDraft; -import io.sphere.sdk.states.StateDraftBuilder; -import io.sphere.sdk.states.StateType; -import io.sphere.sdk.states.commands.StateCreateCommand; -import io.sphere.sdk.taxcategories.TaxCategory; -import io.sphere.sdk.taxcategories.TaxCategoryDraft; -import io.sphere.sdk.taxcategories.TaxCategoryDraftBuilder; -import io.sphere.sdk.taxcategories.TaxRateDraft; -import io.sphere.sdk.taxcategories.TaxRateDraftBuilder; -import io.sphere.sdk.taxcategories.commands.TaxCategoryCreateCommand; -import io.sphere.sdk.taxcategories.queries.TaxCategoryQuery; -import io.sphere.sdk.types.CustomFieldsDraft; -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 java.math.BigDecimal; -import java.time.ZonedDateTime; -import java.util.Arrays; -import java.util.Collections; +import io.vrap.rmf.base.client.ApiHttpResponse; +import io.vrap.rmf.base.client.http.HttpStatusCode; +import java.util.List; import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import javax.money.CurrencyUnit; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -107,116 +83,111 @@ void setup() { setupSourceProjectData(CTP_SOURCE_CLIENT); } - private void setupSourceProjectData(SphereClient sourceProjectClient) { + private void setupSourceProjectData(final ProjectApiRoot sourceProjectClient) { final ProductTypeDraft productTypeDraft = - ProductTypeDraftBuilder.of( - RESOURCE_KEY, "sample-product-type", "a productType for t-shirts", emptyList()) + ProductTypeDraftBuilder.of() + .key(RESOURCE_KEY) + .name("sample-product-type") + .description("a productType for t-shirts") .build(); final ProductType productType = - sourceProjectClient - .execute(ProductTypeCreateCommand.of(productTypeDraft)) - .toCompletableFuture() - .join(); + sourceProjectClient.productTypes().post(productTypeDraft).executeBlocking().getBody(); final FieldDefinition FIELD_DEFINITION_1 = - FieldDefinition.of( - StringFieldType.of(), - "field_name_1", - LocalizedString.ofEnglish("label_1"), - false, - TextInputHint.SINGLE_LINE); + FieldDefinitionBuilder.of() + .type(FieldTypeBuilder::stringBuilder) + .name("field_name_1") + .label(ofEnglish("label_1")) + .required(false) + .inputHint(TypeTextInputHint.SINGLE_LINE) + .build(); final TypeDraft typeDraft = - TypeDraftBuilder.of( - TYPE_KEY, - LocalizedString.ofEnglish("name_1"), - ResourceTypeIdsSetBuilder.of().addCategories().addPrices().addAssets().build()) - .description(LocalizedString.ofEnglish("description_1")) - .fieldDefinitions(Arrays.asList(FIELD_DEFINITION_1)) + TypeDraftBuilder.of() + .key(TYPE_KEY) + .name(ofEnglish("name_1")) + .resourceTypeIds( + ResourceTypeId.CATEGORY, ResourceTypeId.PRODUCT_PRICE, ResourceTypeId.ASSET) + .description(ofEnglish("description_1")) + .fieldDefinitions(FIELD_DEFINITION_1) .build(); - final Type type = - sourceProjectClient.execute(TypeCreateCommand.of(typeDraft)).toCompletableFuture().join(); + final Type type = sourceProjectClient.types().post(typeDraft).executeBlocking().getBody(); final CategoryDraft categoryDraft = - CategoryDraftBuilder.of(ofEnglish("t-shirts"), ofEnglish("t-shirts")) + CategoryDraftBuilder.of() + .name(ofEnglish("t-shirts")) + .slug(ofEnglish("t-shirts")) .key(RESOURCE_KEY) .build(); - sourceProjectClient - .execute(CategoryCreateCommand.of(categoryDraft)) - .toCompletableFuture() - .join(); + sourceProjectClient.categories().post(categoryDraft).executeBlocking(); final StateDraft stateDraft = - StateDraftBuilder.of(RESOURCE_KEY, StateType.PRODUCT_STATE) - .roles(Collections.emptySet()) + StateDraftBuilder.of() + .key(RESOURCE_KEY) + .type(StateTypeEnum.PRODUCT_STATE) + .roles(List.of()) .description(ofEnglish("State 1")) .name(ofEnglish("State 1")) .initial(true) - .transitions(Collections.emptySet()) + .transitions(List.of()) .build(); - final State state = - sourceProjectClient.execute(StateCreateCommand.of(stateDraft)).toCompletableFuture().join(); + final State state = sourceProjectClient.states().post(stateDraft).executeBlocking().getBody(); final TaxRateDraft taxRateDraft = - TaxRateDraftBuilder.of("Tax-Rate-Name-1", 0.3, false, CountryCode.DE).build(); + TaxRateDraftBuilder.of() + .name("Tax-Rate-Name-1") + .amount(0.3) + .includedInPrice(false) + .country("DE") + .build(); final TaxCategoryDraft taxCategoryDraft = - TaxCategoryDraftBuilder.of( - "Tax-Category-Name-1", singletonList(taxRateDraft), "Tax-Category-Description-1") + TaxCategoryDraftBuilder.of() + .name("Tax-Category-Name-1") + .rates(taxRateDraft) + .description("Tax-Category-Description-1") .key(RESOURCE_KEY) .build(); final TaxCategory taxCategory = - sourceProjectClient - .execute(TaxCategoryCreateCommand.of(taxCategoryDraft)) - .toCompletableFuture() - .join(); + sourceProjectClient.taxCategories().post(taxCategoryDraft).executeBlocking().getBody(); final CustomerGroupDraft customerGroupDraft = - CustomerGroupDraftBuilder.of("customerGroupName").key("customerGroupKey").build(); + CustomerGroupDraftBuilder.of() + .groupName("customerGroupName") + .key("customerGroupKey") + .build(); - CustomerGroup customerGroup = - sourceProjectClient - .execute(CustomerGroupCreateCommand.of(customerGroupDraft)) - .toCompletableFuture() - .join(); + final CustomerGroup customerGroup = + sourceProjectClient.customerGroups().post(customerGroupDraft).executeBlocking().getBody(); - CTP_TARGET_CLIENT - .execute(CustomerGroupCreateCommand.of(customerGroupDraft)) - .toCompletableFuture() - .join(); + CTP_TARGET_CLIENT.customerGroups().post(customerGroupDraft).executeBlocking(); CustomFieldsDraft customFieldsDraft = - CustomFieldsDraft.ofTypeKeyAndJson(type.getKey(), emptyMap()); + CustomFieldsDraftBuilder.of().type(type.toResourceIdentifier()).build(); final ChannelDraft draft = - ChannelDraftBuilder.of("channelKey").roles(singleton(ChannelRole.INVENTORY_SUPPLY)).build(); - sourceProjectClient.execute(ChannelCreateCommand.of(draft)).toCompletableFuture().join(); - CTP_TARGET_CLIENT.execute(ChannelCreateCommand.of(draft)).toCompletableFuture().join(); - - final PriceDraft priceBuilder = - PriceDraftBuilder.of( - getPriceDraft( - BigDecimal.valueOf(222), - EUR, - DE, - customerGroup.getId(), - null, - null, - null, - null)) - .customerGroup(customerGroup) + ChannelDraftBuilder.of().key("channelKey").roles(ChannelRoleEnum.INVENTORY_SUPPLY).build(); + sourceProjectClient.channels().post(draft).executeBlocking(); + CTP_TARGET_CLIENT.channels().post(draft).executeBlocking(); + + final PriceDraft priceDraft = + PriceDraftBuilder.of(getPriceDraft(22200L, "EUR", "DE", null, null, null)) + .customerGroup(customerGroup.toResourceIdentifier()) .custom(customFieldsDraft) - .channel(ResourceIdentifier.ofKey("channelKey")) + .channel( + channelResourceIdentifierBuilder -> + channelResourceIdentifierBuilder.key("channelKey")) .build(); final AssetDraft assetDraft = - AssetDraftBuilder.of(emptyList(), LocalizedString.ofEnglish("assetName")) + AssetDraftBuilder.of() + .name(ofEnglish("assetName")) .key("assetKey") - .sources(singletonList(AssetSourceBuilder.ofUri("sourceUri").build())) + .sources(AssetSourceBuilder.of().uri("sourceUri").build()) .custom(customFieldsDraft) .build(); @@ -224,25 +195,26 @@ private void setupSourceProjectData(SphereClient sourceProjectClient) { ProductVariantDraftBuilder.of() .key("variantKey") .sku("sku1") - .prices(priceBuilder) - .assets(asList(assetDraft)) + .prices(priceDraft) + .assets(assetDraft) .build(); final ProductDraft productDraft = - ProductDraftBuilder.of( - productType, - ofEnglish("V-neck Tee"), - ofEnglish("v-neck-tee"), + ProductDraftBuilder.of() + .productType(productType.toResourceIdentifier()) + .name(ofEnglish("V-Neck Tee")) + .slug(ofEnglish("v-neck-tee")) + .masterVariant( ProductVariantDraftBuilder.of().key(RESOURCE_KEY).sku(RESOURCE_KEY).build()) - .state(State.referenceOfId(state.getId())) - .taxCategory(TaxCategory.referenceOfId(taxCategory.getId())) - .productType(ProductType.referenceOfId(productType.getId())) + .state(state.toResourceIdentifier()) + .taxCategory(taxCategory.toResourceIdentifier()) + .productType(productType.toResourceIdentifier()) .variants(asList(variantDraft1)) .key(RESOURCE_KEY) .publish(true) .build(); - sourceProjectClient.execute(ProductCreateCommand.of(productDraft)).toCompletableFuture().join(); + sourceProjectClient.products().post(productDraft).executeBlocking(); } @AfterAll @@ -265,43 +237,23 @@ void run_WithSyncAsArgumentWithAllArgAsFullSync_ShouldResolveReferencesAndExecut } private static void assertAllResourcesAreSyncedToTarget( - @Nonnull final SphereClient targetClient) { + @Nonnull final ProjectApiRoot targetClient) { assertProductTypeExists(targetClient, RESOURCE_KEY); assertCategoryExists(targetClient, RESOURCE_KEY); assertProductExists(targetClient, RESOURCE_KEY, RESOURCE_KEY, RESOURCE_KEY); - final String queryPredicate = format("key=\"%s\"", RESOURCE_KEY); - - final PagedQueryResult taxCategoryQueryResult = + final ApiHttpResponse taxCategoryApiHttpResponse = targetClient - .execute(TaxCategoryQuery.of().withPredicates(QueryPredicate.of(queryPredicate))) + .taxCategories() + .withKey(RESOURCE_KEY) + .get() + .execute() .toCompletableFuture() .join(); - assertThat(taxCategoryQueryResult.getResults()) - .hasSize(1) - .singleElement() - .satisfies(taxCategory -> assertThat(taxCategory.getKey()).isEqualTo(RESOURCE_KEY)); - } - @Nonnull - public static PriceDraft getPriceDraft( - @Nonnull final BigDecimal value, - @Nonnull final CurrencyUnit currencyUnits, - @Nullable final CountryCode countryCode, - @Nullable final String customerGroupId, - @Nullable final ZonedDateTime validFrom, - @Nullable final ZonedDateTime validUntil, - @Nullable final String channelId, - @Nullable final CustomFieldsDraft customFieldsDraft) { - return PriceDraftBuilder.of(Price.of(value, currencyUnits)) - .country(countryCode) - .customerGroup( - ofNullable(customerGroupId).map(ResourceIdentifier::ofId).orElse(null)) - .validFrom(validFrom) - .validUntil(validUntil) - .channel(ofNullable(channelId).map(ResourceIdentifier::ofId).orElse(null)) - .custom(customFieldsDraft) - .build(); + assertThat(taxCategoryApiHttpResponse.getStatusCode()).isEqualTo(HttpStatusCode.OK_200); + assertThat(taxCategoryApiHttpResponse.getBody()).isNotNull(); + assertThat(taxCategoryApiHttpResponse.getBody().getKey()).isEqualTo(RESOURCE_KEY); } } diff --git a/src/integration-test/java/com/commercetools/project/sync/ProductSyncWithReferencesIT.java b/src/integration-test/java/com/commercetools/project/sync/ProductSyncWithReferencesIT.java index 08015ba4..02eefe9b 100644 --- a/src/integration-test/java/com/commercetools/project/sync/ProductSyncWithReferencesIT.java +++ b/src/integration-test/java/com/commercetools/project/sync/ProductSyncWithReferencesIT.java @@ -1,11 +1,12 @@ package com.commercetools.project.sync; +import static com.commercetools.api.models.common.LocalizedString.ofEnglish; +import static com.commercetools.project.sync.util.CtpClientUtils.CTP_SOURCE_CLIENT; +import static com.commercetools.project.sync.util.CtpClientUtils.CTP_TARGET_CLIENT; import static com.commercetools.project.sync.util.IntegrationTestUtils.assertProductTypeExists; import static com.commercetools.project.sync.util.IntegrationTestUtils.cleanUpProjects; import static com.commercetools.project.sync.util.IntegrationTestUtils.createITSyncerFactory; -import static com.commercetools.project.sync.util.IntegrationTestUtils.createReferenceObject; -import static com.commercetools.project.sync.util.SphereClientUtils.CTP_SOURCE_CLIENT; -import static com.commercetools.project.sync.util.SphereClientUtils.CTP_TARGET_CLIENT; +import static com.commercetools.project.sync.util.IntegrationTestUtils.createReferenceOfType; import static com.commercetools.project.sync.util.TestUtils.assertCartDiscountSyncerLoggingEvents; import static com.commercetools.project.sync.util.TestUtils.assertCategorySyncerLoggingEvents; import static com.commercetools.project.sync.util.TestUtils.assertCustomObjectSyncerLoggingEvents; @@ -17,12 +18,26 @@ import static com.commercetools.project.sync.util.TestUtils.assertStateSyncerLoggingEvents; import static com.commercetools.project.sync.util.TestUtils.assertTaxCategorySyncerLoggingEvents; import static com.commercetools.project.sync.util.TestUtils.assertTypeSyncerLoggingEvents; -import static io.sphere.sdk.models.LocalizedString.ofEnglish; import static java.lang.String.format; -import static java.util.Collections.emptyList; -import static java.util.Collections.singletonList; import static org.assertj.core.api.Assertions.assertThat; +import com.commercetools.api.client.ProjectApiRoot; +import com.commercetools.api.client.QueryUtils; +import com.commercetools.api.models.common.ReferenceTypeId; +import com.commercetools.api.models.custom_object.CustomObjectPagedQueryResponse; +import com.commercetools.api.models.product.AttributeBuilder; +import com.commercetools.api.models.product.Product; +import com.commercetools.api.models.product.ProductDraft; +import com.commercetools.api.models.product.ProductDraftBuilder; +import com.commercetools.api.models.product.ProductVariantDraft; +import com.commercetools.api.models.product.ProductVariantDraftBuilder; +import com.commercetools.api.models.product_type.AttributeConstraintEnum; +import com.commercetools.api.models.product_type.AttributeDefinitionDraft; +import com.commercetools.api.models.product_type.AttributeDefinitionDraftBuilder; +import com.commercetools.api.models.product_type.AttributeReferenceTypeId; +import com.commercetools.api.models.product_type.ProductType; +import com.commercetools.api.models.product_type.ProductTypeDraft; +import com.commercetools.api.models.product_type.ProductTypeDraftBuilder; import com.commercetools.project.sync.cartdiscount.CartDiscountSyncer; import com.commercetools.project.sync.category.CategorySyncer; import com.commercetools.project.sync.customer.CustomerSyncer; @@ -34,32 +49,10 @@ import com.commercetools.project.sync.state.StateSyncer; import com.commercetools.project.sync.taxcategory.TaxCategorySyncer; import com.commercetools.project.sync.type.TypeSyncer; -import com.commercetools.sync.commons.models.WaitingToBeResolved; -import com.commercetools.sync.commons.utils.CtpQueryUtils; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.JsonNodeFactory; import com.fasterxml.jackson.databind.node.ObjectNode; -import io.sphere.sdk.client.SphereClient; -import io.sphere.sdk.customobjects.CustomObject; -import io.sphere.sdk.customobjects.queries.CustomObjectQuery; -import io.sphere.sdk.products.Product; -import io.sphere.sdk.products.ProductDraft; -import io.sphere.sdk.products.ProductDraftBuilder; -import io.sphere.sdk.products.ProductVariantDraft; -import io.sphere.sdk.products.ProductVariantDraftBuilder; -import io.sphere.sdk.products.attributes.AttributeConstraint; -import io.sphere.sdk.products.attributes.AttributeDefinitionDraft; -import io.sphere.sdk.products.attributes.AttributeDefinitionDraftBuilder; -import io.sphere.sdk.products.attributes.AttributeDraft; -import io.sphere.sdk.products.attributes.ReferenceAttributeType; -import io.sphere.sdk.products.attributes.SetAttributeType; -import io.sphere.sdk.products.commands.ProductCreateCommand; -import io.sphere.sdk.products.queries.ProductQuery; -import io.sphere.sdk.producttypes.ProductType; -import io.sphere.sdk.producttypes.ProductTypeDraft; -import io.sphere.sdk.producttypes.ProductTypeDraftBuilder; -import io.sphere.sdk.producttypes.commands.ProductTypeCreateCommand; -import io.sphere.sdk.queries.PagedQueryResult; +import io.vrap.rmf.base.client.ApiHttpResponse; import java.util.List; import java.util.Set; import java.util.concurrent.CompletableFuture; @@ -110,6 +103,7 @@ class ProductSyncWithReferencesIT { @BeforeEach void setup() { + cleanUpProjects(CTP_SOURCE_CLIENT, CTP_TARGET_CLIENT); cliRunnerTestLogger.clearAll(); productSyncerTestLogger.clearAll(); productTypeSyncerTestLogger.clearAll(); @@ -122,70 +116,53 @@ void setup() { categorySyncerTestLogger.clearAll(); cartDiscountSyncerTestLogger.clearAll(); taxCategorySyncerTestLogger.clearAll(); - cleanUpProjects(CTP_SOURCE_CLIENT, CTP_TARGET_CLIENT); setupSourceProjectData(CTP_SOURCE_CLIENT); } - static void setupSourceProjectData(@Nonnull final SphereClient sourceProjectClient) { + static void setupSourceProjectData(@Nonnull final ProjectApiRoot sourceProjectClient) { final AttributeDefinitionDraft setOfProductsAttributeDef = - AttributeDefinitionDraftBuilder.of( - SetAttributeType.of(ReferenceAttributeType.ofProduct()), - "products", - ofEnglish("products"), - false) - .searchable(false) - .attributeConstraint(AttributeConstraint.NONE) + AttributeDefinitionDraftBuilder.of() + .type( + attributeTypeBuilder -> + attributeTypeBuilder + .setBuilder() + .elementType( + elementBuilder -> + elementBuilder + .referenceBuilder() + .referenceTypeId(AttributeReferenceTypeId.PRODUCT))) + .name("products") + .label(ofEnglish("products")) + .isRequired(false) + .isSearchable(false) + .attributeConstraint(AttributeConstraintEnum.NONE) .build(); final ProductTypeDraft productTypeDraft = - ProductTypeDraftBuilder.of( - MAIN_PRODUCT_TYPE_KEY, - MAIN_PRODUCT_TYPE_KEY, - "a productType for t-shirts", - emptyList()) - .attributes(singletonList(setOfProductsAttributeDef)) + ProductTypeDraftBuilder.of() + .key(MAIN_PRODUCT_TYPE_KEY) + .name(MAIN_PRODUCT_TYPE_KEY) + .description("an inner productType for t-shirts") + .attributes(setOfProductsAttributeDef) .build(); final ProductType productType = - sourceProjectClient - .execute(ProductTypeCreateCommand.of(productTypeDraft)) - .toCompletableFuture() - .join(); + sourceProjectClient.productTypes().post(productTypeDraft).executeBlocking().getBody(); final ConcurrentHashMap.KeySetView productIds = ConcurrentHashMap.newKeySet(); - // create 500 products - final CompletableFuture[] creationFutures = - IntStream.range(0, 500) - .mapToObj( - index -> { - final ProductVariantDraft masterVariant = - ProductVariantDraftBuilder.of() - .key(format("%d-mv-key", index)) - .sku(format("%d-mv-sku", index)) - .build(); - - final ProductDraft draft = - ProductDraftBuilder.of( - productType, - ofEnglish(Integer.toString(index)), - ofEnglish(format("%d-slug", index)), - masterVariant) - .key(format("%d-key", index)) - .build(); - return CTP_SOURCE_CLIENT - .execute(ProductCreateCommand.of(draft)) - .thenAccept(createdProduct -> productIds.add(createdProduct.getId())) - .toCompletableFuture(); - }) - .toArray(CompletableFuture[]::new); - - CompletableFuture.allOf(creationFutures).join(); + CompletableFuture.allOf( + IntStream.range(0, 500) + .mapToObj(value -> createProduct(sourceProjectClient, productType, value)) + .toArray(CompletableFuture[]::new)) + .join(); final ArrayNode setAttributeValue = JsonNodeFactory.instance.arrayNode(); final Set productReferences = productIds.stream() - .map(productId -> createReferenceObject(Product.referenceTypeId(), productId)) + .map( + productId -> + createReferenceOfType(ReferenceTypeId.PRODUCT.getJsonName(), productId)) .collect(Collectors.toSet()); setAttributeValue.addAll(productReferences); @@ -193,19 +170,48 @@ static void setupSourceProjectData(@Nonnull final SphereClient sourceProjectClie ProductVariantDraftBuilder.of() .key(MAIN_PRODUCT_MASTER_VARIANT_KEY) .sku(MAIN_PRODUCT_MASTER_VARIANT_KEY) - .attributes(AttributeDraft.of(setOfProductsAttributeDef.getName(), setAttributeValue)) + .attributes( + AttributeBuilder.of() + .name(setOfProductsAttributeDef.getName()) + .value(setAttributeValue) + .build()) .build(); final ProductDraft draft = - ProductDraftBuilder.of( - productType, - ofEnglish(MAIN_PRODUCT_KEY), - ofEnglish(MAIN_PRODUCT_KEY), - masterVariant) + ProductDraftBuilder.of() + .productType(productType.toResourceIdentifier()) + .name(ofEnglish(MAIN_PRODUCT_KEY)) + .slug(ofEnglish(MAIN_PRODUCT_KEY)) + .masterVariant(masterVariant) .key(MAIN_PRODUCT_KEY) .build(); - sourceProjectClient.execute(ProductCreateCommand.of(draft)).toCompletableFuture().join(); + sourceProjectClient.products().post(draft).executeBlocking(); + } + + private static CompletableFuture createProduct( + @Nonnull final ProjectApiRoot sourceProjectClient, + @Nonnull final ProductType productType, + int index) { + final ProductVariantDraft masterVariant = + ProductVariantDraftBuilder.of() + .key(format("%d-mv-key", index)) + .sku(format("%d-mv-sku", index)) + .build(); + final ProductDraft draft = + ProductDraftBuilder.of() + .productType(productType.toResourceIdentifier()) + .name(ofEnglish(Integer.toString(index))) + .slug(ofEnglish(format("%d-slug", index))) + .masterVariant(masterVariant) + .key(format("%d-key", index)) + .build(); + return sourceProjectClient + .products() + .create(draft) + .execute() + .thenApply(ApiHttpResponse::getBody) + .toCompletableFuture(); } @AfterAll @@ -240,12 +246,12 @@ void run_WithAProductWith500DistinctReferences_ShouldSyncCorrectly() { } private static void assertAllResourcesAreSyncedToTarget( - @Nonnull final SphereClient targetClient) { + @Nonnull final ProjectApiRoot targetClient) { assertProductTypeExists(targetClient, MAIN_PRODUCT_TYPE_KEY); final List products = - CtpQueryUtils.queryAll(targetClient, ProductQuery.of(), Function.identity()) + QueryUtils.queryAll(targetClient.products().get(), Function.identity()) .thenApply( fetchedProducts -> fetchedProducts.stream().flatMap(List::stream).collect(Collectors.toList())) @@ -253,12 +259,13 @@ private static void assertAllResourcesAreSyncedToTarget( .join(); assertThat(products).hasSize(501); - final CustomObjectQuery customObjectQuery = - CustomObjectQuery.of(WaitingToBeResolved.class) - .byContainer("commercetools-sync-java.UnresolvedReferencesService.productDrafts"); - - final PagedQueryResult> queryResult = - targetClient.execute(customObjectQuery).toCompletableFuture().join(); + final CustomObjectPagedQueryResponse queryResult = + targetClient + .customObjects() + .withContainer("commercetools-sync-java.UnresolvedReferencesService" + ".productDrafts") + .get() + .executeBlocking() + .getBody(); assertThat(queryResult.getResults()).isEmpty(); } diff --git a/src/integration-test/java/com/commercetools/project/sync/util/IntegrationTestUtils.java b/src/integration-test/java/com/commercetools/project/sync/util/IntegrationTestUtils.java index c24cab53..621c42ef 100644 --- a/src/integration-test/java/com/commercetools/project/sync/util/IntegrationTestUtils.java +++ b/src/integration-test/java/com/commercetools/project/sync/util/IntegrationTestUtils.java @@ -1,80 +1,40 @@ package com.commercetools.project.sync.util; import static com.commercetools.project.sync.service.impl.CustomObjectServiceImpl.TIMESTAMP_GENERATOR_KEY; -import static com.commercetools.project.sync.util.QueryUtils.queryAndExecute; -import static com.commercetools.project.sync.util.SphereClientUtils.CTP_SOURCE_CLIENT; -import static com.commercetools.project.sync.util.SphereClientUtils.CTP_TARGET_CLIENT; +import static com.commercetools.project.sync.util.CtpClientUtils.CTP_SOURCE_CLIENT; +import static com.commercetools.project.sync.util.CtpClientUtils.CTP_TARGET_CLIENT; import static java.lang.String.format; import static org.assertj.core.api.Assertions.assertThat; +import com.commercetools.api.client.ByProjectKeyCustomObjectsGet; +import com.commercetools.api.client.ProjectApiRoot; +import com.commercetools.api.client.QueryUtils; +import com.commercetools.api.client.error.ConcurrentModificationException; +import com.commercetools.api.models.category.Category; +import com.commercetools.api.models.customer.Customer; +import com.commercetools.api.models.product.Product; +import com.commercetools.api.models.product.ProductUnpublishActionBuilder; +import com.commercetools.api.models.product.ProductVariant; +import com.commercetools.api.models.product_type.ProductType; +import com.commercetools.api.models.product_type.ProductTypeRemoveAttributeDefinitionActionBuilder; +import com.commercetools.api.models.state.State; import com.commercetools.project.sync.SyncerFactory; -import com.commercetools.project.sync.model.response.LastSyncCustomObject; -import com.commercetools.sync.commons.utils.CtpQueryUtils; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.JsonNodeFactory; import com.fasterxml.jackson.databind.node.ObjectNode; -import io.sphere.sdk.cartdiscounts.commands.CartDiscountDeleteCommand; -import io.sphere.sdk.cartdiscounts.queries.CartDiscountQuery; -import io.sphere.sdk.categories.Category; -import io.sphere.sdk.categories.commands.CategoryDeleteCommand; -import io.sphere.sdk.categories.queries.CategoryQuery; -import io.sphere.sdk.channels.commands.ChannelDeleteCommand; -import io.sphere.sdk.channels.queries.ChannelQuery; -import io.sphere.sdk.client.ConcurrentModificationException; -import io.sphere.sdk.client.SphereClient; -import io.sphere.sdk.client.SphereRequest; -import io.sphere.sdk.commands.UpdateAction; -import io.sphere.sdk.customergroups.commands.CustomerGroupDeleteCommand; -import io.sphere.sdk.customergroups.queries.CustomerGroupQuery; -import io.sphere.sdk.customers.Customer; -import io.sphere.sdk.customers.commands.CustomerDeleteCommand; -import io.sphere.sdk.customers.queries.CustomerQuery; -import io.sphere.sdk.customobjects.CustomObject; -import io.sphere.sdk.customobjects.commands.CustomObjectDeleteCommand; -import io.sphere.sdk.customobjects.queries.CustomObjectQuery; -import io.sphere.sdk.inventory.commands.InventoryEntryDeleteCommand; -import io.sphere.sdk.inventory.queries.InventoryEntryQuery; -import io.sphere.sdk.models.Versioned; -import io.sphere.sdk.productdiscounts.commands.ProductDiscountDeleteCommand; -import io.sphere.sdk.productdiscounts.queries.ProductDiscountQuery; -import io.sphere.sdk.products.Product; -import io.sphere.sdk.products.ProductVariant; -import io.sphere.sdk.products.commands.ProductDeleteCommand; -import io.sphere.sdk.products.commands.ProductUpdateCommand; -import io.sphere.sdk.products.commands.updateactions.Unpublish; -import io.sphere.sdk.products.queries.ProductQuery; -import io.sphere.sdk.producttypes.ProductType; -import io.sphere.sdk.producttypes.commands.ProductTypeDeleteCommand; -import io.sphere.sdk.producttypes.commands.ProductTypeUpdateCommand; -import io.sphere.sdk.producttypes.commands.updateactions.RemoveAttributeDefinition; -import io.sphere.sdk.producttypes.queries.ProductTypeQuery; -import io.sphere.sdk.queries.PagedQueryResult; -import io.sphere.sdk.queries.QueryPredicate; -import io.sphere.sdk.shippingmethods.commands.ShippingMethodDeleteCommand; -import io.sphere.sdk.shippingmethods.queries.ShippingMethodQuery; -import io.sphere.sdk.shoppinglists.commands.ShoppingListDeleteCommand; -import io.sphere.sdk.shoppinglists.queries.ShoppingListQuery; -import io.sphere.sdk.states.State; -import io.sphere.sdk.states.commands.StateDeleteCommand; -import io.sphere.sdk.states.queries.StateQuery; -import io.sphere.sdk.taxcategories.commands.TaxCategoryDeleteCommand; -import io.sphere.sdk.taxcategories.queries.TaxCategoryQuery; -import io.sphere.sdk.types.commands.TypeDeleteCommand; -import io.sphere.sdk.types.queries.TypeQuery; +import io.vrap.rmf.base.client.ApiHttpResponse; +import io.vrap.rmf.base.client.http.HttpStatusCode; import java.time.Clock; import java.util.ArrayList; import java.util.List; -import java.util.Map; -import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; -import java.util.concurrent.ConcurrentHashMap; -import java.util.function.Consumer; -import java.util.stream.Collectors; import javax.annotation.Nonnull; public final class IntegrationTestUtils { + private static final int MAX_RETRY = 10; + public static SyncerFactory createITSyncerFactory() { return SyncerFactory.of( () -> CTP_SOURCE_CLIENT, () -> CTP_TARGET_CLIENT, Clock.systemDefaultZone(), false); @@ -88,55 +48,59 @@ public static SyncerFactory createITSyncerFactory() { * @param ctpClient the client to delete the custom objects from. */ public static void deleteLastSyncCustomObjects( - @Nonnull final SphereClient ctpClient, @Nonnull final String sourceProjectKey) { + @Nonnull final ProjectApiRoot ctpClient, @Nonnull final String sourceProjectKey) { // 1. First query for the time generator custom object - final QueryPredicate> timeGeneratorPredicate = - QueryPredicate.of(format("key=\"%s\"", TIMESTAMP_GENERATOR_KEY)); - - final CustomObjectQuery timeGeneratorCustomObjectQuery = - CustomObjectQuery.of(String.class).plusPredicates(timeGeneratorPredicate); + final ByProjectKeyCustomObjectsGet timeGeneratorCustomObjectQuery = + ctpClient.customObjects().get().withWhere(format("key=\"%s\"", TIMESTAMP_GENERATOR_KEY)); final List deletionStages = new ArrayList<>(); final CompletableFuture timeGeneratorDeletionsStage = - ctpClient - .execute(timeGeneratorCustomObjectQuery) - .thenApply(PagedQueryResult::getResults) + timeGeneratorCustomObjectQuery + .execute() + .thenApply( + customObjectPagedQueryResponseApiHttpResponse -> + customObjectPagedQueryResponseApiHttpResponse.getBody().getResults()) .thenCompose( customObjects -> CompletableFuture.allOf( customObjects.stream() .map( customObject -> - ctpClient.execute( - CustomObjectDeleteCommand.of(customObject, String.class))) - .map(CompletionStage::toCompletableFuture) + ctpClient + .customObjects() + .withContainerAndKey( + customObject.getContainer(), customObject.getKey()) + .delete() + .execute()) .toArray(CompletableFuture[]::new))) .toCompletableFuture(); deletionStages.add(timeGeneratorDeletionsStage); // 2. Then query for the lastSync custom objects - final QueryPredicate> lastSyncPredicate = - QueryPredicate.of(format("key=\"%s\"", sourceProjectKey)); - - final CustomObjectQuery lastSyncCustomObjectQuery = - CustomObjectQuery.of(LastSyncCustomObject.class).plusPredicates(lastSyncPredicate); + final ByProjectKeyCustomObjectsGet lastSyncCustomObjectQuery = + ctpClient.customObjects().get().withWhere(format("key=\"%s\"", sourceProjectKey)); final CompletableFuture lastSyncCustomObjectDeletionFutures = - ctpClient - .execute(lastSyncCustomObjectQuery) - .thenApply(PagedQueryResult::getResults) + lastSyncCustomObjectQuery + .execute() + .thenApply( + customObjectPagedQueryResponseApiHttpResponse -> + customObjectPagedQueryResponseApiHttpResponse.getBody().getResults()) .thenCompose( customObjects -> CompletableFuture.allOf( customObjects.stream() .map( customObject -> - ctpClient.execute( - CustomObjectDeleteCommand.of( - customObject, LastSyncCustomObject.class))) + ctpClient + .customObjects() + .withContainerAndKey( + customObject.getContainer(), customObject.getKey()) + .delete() + .execute()) .map(CompletionStage::toCompletableFuture) .toArray(CompletableFuture[]::new))) .toCompletableFuture(); @@ -148,32 +112,150 @@ public static void deleteLastSyncCustomObjects( } public static void cleanUpProjects( - @Nonnull final SphereClient sourceClient, @Nonnull final SphereClient targetClient) { - + @Nonnull final ProjectApiRoot sourceClient, @Nonnull final ProjectApiRoot targetClient) { deleteProjectData(sourceClient); deleteProjectData(targetClient); - deleteLastSyncCustomObjects(targetClient, sourceClient.getConfig().getProjectKey()); + deleteLastSyncCustomObjects(targetClient, sourceClient.getProjectKey()); } - private static void deleteProjectData(@Nonnull final SphereClient client) { - queryAndExecute(client, CategoryQuery.of(), CategoryDeleteCommand::of).join(); - queryAndExecute( - client, - ProductQuery.of(), - versioned -> ProductUpdateCommand.of(versioned, Unpublish.of())) + private static void deleteProjectData(@Nonnull final ProjectApiRoot client) { + QueryUtils.queryAll( + client.categories().get(), + categories -> { + CompletableFuture.allOf( + categories.stream() + .map( + category -> + client + .categories() + .delete(category) + .execute() + .thenApply(ApiHttpResponse::getBody)) + .map(CompletionStage::toCompletableFuture) + .toArray(CompletableFuture[]::new)) + .join(); + }) + .toCompletableFuture() + .join(); + QueryUtils.queryAll( + client.products().get(), + products -> { + CompletableFuture.allOf( + products.stream() + .map( + product -> + client + .products() + .update(product) + .with( + actionBuilder -> + actionBuilder.plus( + ProductUnpublishActionBuilder.of())) + .execute() + .thenApply(ApiHttpResponse::getBody)) + .map(CompletionStage::toCompletableFuture) + .toArray(CompletableFuture[]::new)) + .join(); + }) + .toCompletableFuture() .join(); final CompletableFuture deleteProduct = - queryAndExecute(client, ProductQuery.of(), ProductDeleteCommand::of); + QueryUtils.queryAll( + client.products().get(), + products -> { + CompletableFuture.allOf( + products.stream() + .map( + product -> + client + .products() + .delete(product) + .execute() + .thenApply(ApiHttpResponse::getBody)) + .map(CompletionStage::toCompletableFuture) + .toArray(CompletableFuture[]::new)) + .join(); + }) + .toCompletableFuture(); + final CompletableFuture deleteInventory = - queryAndExecute(client, InventoryEntryQuery.of(), InventoryEntryDeleteCommand::of); + QueryUtils.queryAll( + client.inventory().get(), + inventoryEntries -> { + CompletableFuture.allOf( + inventoryEntries.stream() + .map( + inventoryEntry -> + client + .inventory() + .delete(inventoryEntry) + .execute() + .thenApply(ApiHttpResponse::getBody)) + .map(CompletionStage::toCompletableFuture) + .toArray(CompletableFuture[]::new)) + .join(); + }) + .toCompletableFuture(); + final CompletableFuture deleteCartDiscount = - queryAndExecute(client, CartDiscountQuery.of(), CartDiscountDeleteCommand::of); + QueryUtils.queryAll( + client.cartDiscounts().get(), + cartDiscounts -> { + CompletableFuture.allOf( + cartDiscounts.stream() + .map( + cartDiscount -> + client + .cartDiscounts() + .delete(cartDiscount) + .execute() + .thenApply(ApiHttpResponse::getBody)) + .map(CompletionStage::toCompletableFuture) + .toArray(CompletableFuture[]::new)) + .join(); + }) + .toCompletableFuture(); + final CompletableFuture deleteCustomObject = - queryAndExecute( - client, CustomObjectQuery.ofJsonNode(), CustomObjectDeleteCommand::ofJsonNode); + QueryUtils.queryAll( + client.customObjects().get(), + customObjects -> { + CompletableFuture.allOf( + customObjects.stream() + .map( + customObject -> + client + .customObjects() + .withContainerAndKey( + customObject.getContainer(), customObject.getKey()) + .delete() + .execute() + .thenApply(ApiHttpResponse::getBody)) + .map(CompletionStage::toCompletableFuture) + .toArray(CompletableFuture[]::new)) + .join(); + }) + .toCompletableFuture(); + final CompletableFuture deleteProductDiscount = - queryAndExecute(client, ProductDiscountQuery.of(), ProductDiscountDeleteCommand::of); + QueryUtils.queryAll( + client.productDiscounts().get(), + productDiscounts -> { + CompletableFuture.allOf( + productDiscounts.stream() + .map( + productDiscount -> + client + .productDiscounts() + .delete(productDiscount) + .execute() + .thenApply(ApiHttpResponse::getBody)) + .map(CompletionStage::toCompletableFuture) + .toArray(CompletableFuture[]::new)) + .join(); + }) + .toCompletableFuture(); CompletableFuture.allOf( deleteProduct, @@ -183,27 +265,157 @@ private static void deleteProjectData(@Nonnull final SphereClient client) { deleteProductDiscount) .join(); - queryAndExecute( - client, - // builtIn is excluded as it cannot be deleted - StateQuery.of().plusPredicates(QueryPredicate.of("builtIn=\"false\"")), - StateDeleteCommand::of) + QueryUtils.queryAll( + client.states().get().withWhere("builtIn=\"false\""), + states -> { + CompletableFuture.allOf( + states.stream() + .map( + state -> + client + .states() + .delete(state) + .execute() + .thenApply(ApiHttpResponse::getBody)) + .map(CompletionStage::toCompletableFuture) + .toArray(CompletableFuture[]::new)) + .join(); + }) + .toCompletableFuture() .join(); - queryAndExecute(client, ShoppingListQuery.of(), ShoppingListDeleteCommand::of).join(); + QueryUtils.queryAll( + client.shoppingLists().get(), + shoppingLists -> { + CompletableFuture.allOf( + shoppingLists.stream() + .map( + shoppingList -> + client + .shoppingLists() + .delete(shoppingList) + .execute() + .thenApply(ApiHttpResponse::getBody)) + .map(CompletionStage::toCompletableFuture) + .toArray(CompletableFuture[]::new)) + .join(); + }) + .toCompletableFuture() + .join(); final CompletableFuture deleteType = - queryAndExecute(client, TypeQuery.of(), TypeDeleteCommand::of); + QueryUtils.queryAll( + client.types().get(), + types -> { + CompletableFuture.allOf( + types.stream() + .map( + type -> + client + .types() + .delete(type) + .execute() + .thenApply(ApiHttpResponse::getBody)) + .map(CompletionStage::toCompletableFuture) + .toArray(CompletableFuture[]::new)) + .join(); + }) + .toCompletableFuture(); + final CompletableFuture deleteShippingMethod = - queryAndExecute(client, ShippingMethodQuery.of(), ShippingMethodDeleteCommand::of); + QueryUtils.queryAll( + client.shippingMethods().get(), + shippingMethods -> { + CompletableFuture.allOf( + shippingMethods.stream() + .map( + shippingMethod -> + client + .shippingMethods() + .delete(shippingMethod) + .execute() + .thenApply(ApiHttpResponse::getBody)) + .map(CompletionStage::toCompletableFuture) + .toArray(CompletableFuture[]::new)) + .join(); + }) + .toCompletableFuture(); + final CompletableFuture deleteTaxCategory = - queryAndExecute(client, TaxCategoryQuery.of(), TaxCategoryDeleteCommand::of); + QueryUtils.queryAll( + client.taxCategories().get(), + taxCategories -> { + CompletableFuture.allOf( + taxCategories.stream() + .map( + taxCategory -> + client + .taxCategories() + .delete(taxCategory) + .execute() + .thenApply(ApiHttpResponse::getBody)) + .map(CompletionStage::toCompletableFuture) + .toArray(CompletableFuture[]::new)) + .join(); + }) + .toCompletableFuture(); + final CompletableFuture deleteCustomer = - queryAndExecute(client, CustomerQuery.of(), CustomerDeleteCommand::of); + QueryUtils.queryAll( + client.customers().get(), + customers -> { + CompletableFuture.allOf( + customers.stream() + .map( + customer -> + client + .customers() + .delete(customer) + .execute() + .thenApply(ApiHttpResponse::getBody)) + .map(CompletionStage::toCompletableFuture) + .toArray(CompletableFuture[]::new)) + .join(); + }) + .toCompletableFuture(); + final CompletableFuture deleteCustomerGroup = - queryAndExecute(client, CustomerGroupQuery.of(), CustomerGroupDeleteCommand::of); + QueryUtils.queryAll( + client.customerGroups().get(), + customerGroups -> { + CompletableFuture.allOf( + customerGroups.stream() + .map( + customerGroup -> + client + .customerGroups() + .delete(customerGroup) + .execute() + .thenApply(ApiHttpResponse::getBody)) + .map(CompletionStage::toCompletableFuture) + .toArray(CompletableFuture[]::new)) + .join(); + }) + .toCompletableFuture(); + final CompletableFuture deleteChannel = - queryAndExecute(client, ChannelQuery.of(), ChannelDeleteCommand::of); + QueryUtils.queryAll( + client.channels().get(), + channels -> { + CompletableFuture.allOf( + channels.stream() + .map( + channel -> + client + .channels() + .delete(channel) + .execute() + .thenApply(ApiHttpResponse::getBody)) + .map(CompletionStage::toCompletableFuture) + .toArray(CompletableFuture[]::new)) + .join(); + }) + .toCompletableFuture(); CompletableFuture.allOf( deleteType, @@ -216,175 +428,147 @@ private static void deleteProjectData(@Nonnull final SphereClient client) { deleteProductTypes(client); } - private static void deleteProductTypes(@Nonnull final SphereClient ctpClient) { + private static void deleteProductTypes(@Nonnull final ProjectApiRoot ctpClient) { deleteProductTypeAttributes(ctpClient); deleteProductTypesWithRetry(ctpClient); } - private static void deleteProductTypesWithRetry(@Nonnull final SphereClient ctpClient) { - final Consumer> pageConsumer = - pageElements -> - CompletableFuture.allOf( - pageElements.stream() - .map(productType -> deleteProductTypeWithRetry(ctpClient, productType)) - .map(CompletionStage::toCompletableFuture) - .toArray(CompletableFuture[]::new)) - .join(); - - CtpQueryUtils.queryAll(ctpClient, ProductTypeQuery.of(), pageConsumer) - .toCompletableFuture() - .join(); - } - - private static CompletionStage deleteProductTypeWithRetry( - @Nonnull final SphereClient ctpClient, @Nonnull final ProductType productType) { - return ctpClient - .execute(ProductTypeDeleteCommand.of(productType)) - .handle( - (result, throwable) -> { - if (throwable instanceof ConcurrentModificationException) { - Long currentVersion = - ((ConcurrentModificationException) throwable).getCurrentVersion(); - SphereRequest retry = - ProductTypeDeleteCommand.of(Versioned.of(productType.getId(), currentVersion)); - ctpClient.execute(retry); - } - return result; - }); + private static void deleteProductTypesWithRetry(@Nonnull final ProjectApiRoot ctpClient) { + withRetry( + () -> + QueryUtils.queryAll( + ctpClient.productTypes().get(), + productTypes -> { + CompletableFuture.allOf( + productTypes.stream() + .map( + productType -> + ctpClient + .productTypes() + .delete(productType) + .execute() + .thenApply(ApiHttpResponse::getBody)) + .map(CompletionStage::toCompletableFuture) + .toArray(CompletableFuture[]::new)) + .join(); + }) + .toCompletableFuture() + .join()); } - private static void deleteProductTypeAttributes(@Nonnull final SphereClient ctpClient) { - final ConcurrentHashMap>> productTypesToUpdate = - new ConcurrentHashMap<>(); - - CtpQueryUtils.queryAll( - ctpClient, - ProductTypeQuery.of(), - page -> { - page.forEach( - productType -> { - final Set> removeActions = - productType.getAttributes().stream() - .map( - attributeDefinition -> - RemoveAttributeDefinition.of(attributeDefinition.getName())) - .collect(Collectors.toSet()); - productTypesToUpdate.put(productType, removeActions); - }); - }) - .thenCompose( - aVoid -> - CompletableFuture.allOf( - productTypesToUpdate.entrySet().stream() - .map(entry -> updateProductTypeWithRetry(ctpClient, entry)) - .toArray(CompletableFuture[]::new))) - .toCompletableFuture() - .join(); + private static void deleteProductTypeAttributes(@Nonnull final ProjectApiRoot ctpClient) { + withRetry( + () -> + QueryUtils.queryAll( + ctpClient.productTypes().get(), + productTypes -> { + CompletableFuture.allOf( + productTypes.stream() + .map( + productType -> + ctpClient + .productTypes() + .update(productType) + .with( + builder -> { + productType + .getAttributes() + .forEach( + attributeDefinition -> + builder.plus( + ProductTypeRemoveAttributeDefinitionActionBuilder + .of() + .name( + attributeDefinition + .getName()))); + return builder; + }) + .execute() + .thenApply(ApiHttpResponse::getBody)) + .map(CompletionStage::toCompletableFuture) + .toArray(CompletableFuture[]::new)) + .join(); + }) + .toCompletableFuture() + .join()); } - private static CompletionStage updateProductTypeWithRetry( - @Nonnull final SphereClient ctpClient, - @Nonnull final Map.Entry>> entry) { - return ctpClient - .execute(ProductTypeUpdateCommand.of(entry.getKey(), new ArrayList<>(entry.getValue()))) - .handle( - (result, throwable) -> { - if (throwable instanceof ConcurrentModificationException) { - Long currentVersion = - ((ConcurrentModificationException) throwable).getCurrentVersion(); - SphereRequest retry = - ProductTypeUpdateCommand.of( - Versioned.of(entry.getKey().getId(), currentVersion), - new ArrayList<>(entry.getValue())); - ctpClient.execute(retry); - } - return result; - }); + private static void withRetry(final Runnable action) { + for (int i = 0; i < MAX_RETRY; i++) { + try { + action.run(); + } catch (Exception e) { + if (!e.getCause().getClass().equals(ConcurrentModificationException.class)) { + throw e; + } + } + } } @Nonnull public static ProductType assertProductTypeExists( - @Nonnull final SphereClient ctpClient, @Nonnull final String productTypeKey) { - final PagedQueryResult productTypeQueryResult = - ctpClient.execute(ProductTypeQuery.of().byKey(productTypeKey)).toCompletableFuture().join(); + @Nonnull final ProjectApiRoot ctpClient, @Nonnull final String key) { + final ApiHttpResponse productTypeApiHttpResponse = + ctpClient.productTypes().withKey(key).get().execute().toCompletableFuture().join(); - assertThat(productTypeQueryResult.getResults()) - .hasSize(1) - .singleElement() - .satisfies(productType -> assertThat(productType.getKey()).isEqualTo(productTypeKey)); + assertThat(productTypeApiHttpResponse.getStatusCode()).isEqualTo(HttpStatusCode.OK_200); + assertThat(productTypeApiHttpResponse.getBody()).isNotNull(); + assertThat(productTypeApiHttpResponse.getBody().getKey()).isEqualTo(key); - return productTypeQueryResult.getResults().get(0); + return productTypeApiHttpResponse.getBody(); } @Nonnull public static Category assertCategoryExists( - @Nonnull final SphereClient ctpClient, @Nonnull final String key) { - final String queryPredicate = format("key=\"%s\"", key); + @Nonnull final ProjectApiRoot ctpClient, @Nonnull final String key) { + final ApiHttpResponse categoryApiHttpResponse = + ctpClient.categories().withKey(key).get().execute().toCompletableFuture().join(); - final PagedQueryResult categoryQueryResult = - ctpClient - .execute(CategoryQuery.of().withPredicates(QueryPredicate.of(queryPredicate))) - .toCompletableFuture() - .join(); + assertThat(categoryApiHttpResponse.getStatusCode()).isEqualTo(HttpStatusCode.OK_200); + assertThat(categoryApiHttpResponse.getBody()).isNotNull(); + assertThat(categoryApiHttpResponse.getBody().getKey()).isEqualTo(key); - assertThat(categoryQueryResult.getResults()) - .hasSize(1) - .singleElement() - .satisfies(category -> assertThat(category.getKey()).isEqualTo(key)); - - return categoryQueryResult.getResults().get(0); + return categoryApiHttpResponse.getBody(); } @Nonnull public static State assertStateExists( - @Nonnull final SphereClient ctpClient, @Nonnull final String key) { - final String queryPredicate = format("key=\"%s\"", key); - - final PagedQueryResult stateQueryResult = - ctpClient - .execute(StateQuery.of().withPredicates(QueryPredicate.of(queryPredicate))) - .toCompletableFuture() - .join(); + @Nonnull final ProjectApiRoot ctpClient, @Nonnull final String key) { + final ApiHttpResponse stateApiHttpResponse = + ctpClient.states().withKey(key).get().execute().toCompletableFuture().join(); - assertThat(stateQueryResult.getResults()) - .hasSize(1) - .singleElement() - .satisfies(state -> assertThat(state.getKey()).isEqualTo(key)); + assertThat(stateApiHttpResponse.getStatusCode()).isEqualTo(HttpStatusCode.OK_200); + assertThat(stateApiHttpResponse.getBody()).isNotNull(); + assertThat(stateApiHttpResponse.getBody().getKey()).isEqualTo(key); - return stateQueryResult.getResults().get(0); + return stateApiHttpResponse.getBody(); } @Nonnull public static Customer assertCustomerExists( - @Nonnull final SphereClient ctpClient, @Nonnull final String key) { - final String queryPredicate = format("key=\"%s\"", key); - - final PagedQueryResult customerQueryResult = - ctpClient - .execute(CustomerQuery.of().withPredicates(QueryPredicate.of(queryPredicate))) - .toCompletableFuture() - .join(); + @Nonnull final ProjectApiRoot ctpClient, @Nonnull final String key) { + final ApiHttpResponse customerApiHttpResponse = + ctpClient.customers().withKey(key).get().execute().toCompletableFuture().join(); - assertThat(customerQueryResult.getResults()) - .hasSize(1) - .singleElement() - .satisfies(customer -> assertThat(customer.getKey()).isEqualTo(key)); + assertThat(customerApiHttpResponse.getStatusCode()).isEqualTo(HttpStatusCode.OK_200); + assertThat(customerApiHttpResponse.getBody()).isNotNull(); + assertThat(customerApiHttpResponse.getBody().getKey()).isEqualTo(key); - return customerQueryResult.getResults().get(0); + return customerApiHttpResponse.getBody(); } @Nonnull public static Product assertProductExists( - @Nonnull final SphereClient targetClient, + @Nonnull final ProjectApiRoot ctpClient, @Nonnull final String productKey, @Nonnull final String masterVariantKey, @Nonnull final String masterVariantSku) { - final PagedQueryResult productQueryResult = - targetClient.execute(ProductQuery.of()).toCompletableFuture().join(); + final ApiHttpResponse productApiHttpResponse = + ctpClient.products().withKey(productKey).get().execute().toCompletableFuture().join(); - assertThat(productQueryResult.getResults()) - .hasSize(1) - .singleElement() + assertThat(productApiHttpResponse.getStatusCode()).isEqualTo(HttpStatusCode.OK_200); + assertThat(productApiHttpResponse.getBody()).isNotNull(); + assertThat(productApiHttpResponse.getBody()) .satisfies( product -> { assertThat(product.getKey()).isEqualTo(productKey); @@ -394,11 +578,11 @@ public static Product assertProductExists( assertThat(stagedMasterVariant.getSku()).isEqualTo(masterVariantSku); }); - return productQueryResult.getResults().get(0); + return productApiHttpResponse.getBody(); } @Nonnull - public static ObjectNode createReferenceObject( + public static ObjectNode createReferenceOfType( @Nonnull final String typeId, @Nonnull final String id) { final ObjectNode referenceObject = JsonNodeFactory.instance.objectNode(); referenceObject.set("typeId", JsonNodeFactory.instance.textNode(typeId)); diff --git a/src/integration-test/java/com/commercetools/project/sync/util/QueryUtils.java b/src/integration-test/java/com/commercetools/project/sync/util/QueryUtils.java index 7dacf2b5..5947b90b 100644 --- a/src/integration-test/java/com/commercetools/project/sync/util/QueryUtils.java +++ b/src/integration-test/java/com/commercetools/project/sync/util/QueryUtils.java @@ -1,67 +1,68 @@ -package com.commercetools.project.sync.util; - -import com.commercetools.sync.commons.utils.CtpQueryUtils; -import io.sphere.sdk.client.SphereClient; -import io.sphere.sdk.client.SphereRequest; -import io.sphere.sdk.models.ResourceView; -import io.sphere.sdk.queries.QueryDsl; -import java.util.List; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletionStage; -import java.util.function.Consumer; -import java.util.function.Function; -import javax.annotation.Nonnull; - -public final class QueryUtils { - /** - * Applies the {@code resourceToRequestMapper} function on each page, resulting from the {@code - * query} executed by the {@code ctpClient}, to map each resource to a {@link SphereRequest} and - * then executes these requests in parallel within each page. - * - * @param ctpClient defines the CTP project to apply the query on. - * @param query query that should be made on the CTP project. - * @param resourceToRequestMapper defines a mapper function that should be applied on each - * resource, in the fetched page from the query on the specified CTP project, to map it to a - * {@link SphereRequest}. - */ - public static > - CompletableFuture queryAndExecute( - @Nonnull final SphereClient ctpClient, - @Nonnull final QueryDsl query, - @Nonnull final Function> resourceToRequestMapper) { - - return queryAndCompose( - ctpClient, query, resource -> ctpClient.execute(resourceToRequestMapper.apply(resource))); - } - - /** - * Applies the {@code resourceToStageMapper} function on each page, resulting from the {@code - * query} executed by the {@code ctpClient}, to map each resource to a {@link CompletionStage} and - * then executes these stages in parallel within each page. - * - * @param ctpClient defines the CTP project to apply the query on. - * @param query query that should be made on the CTP project. - * @param resourceToStageMapper defines a mapper function that should be applied on each resource, - * in the fetched page from the query on the specified CTP project, to map it to a {@link - * CompletionStage} which will be executed (in a blocking fashion) after every page fetch. - */ - private static , S> - CompletableFuture queryAndCompose( - @Nonnull final SphereClient ctpClient, - @Nonnull final QueryDsl query, - @Nonnull final Function> resourceToStageMapper) { - - final Consumer> pageConsumer = - pageElements -> - CompletableFuture.allOf( - pageElements.stream() - .map(resourceToStageMapper) - .map(CompletionStage::toCompletableFuture) - .toArray(CompletableFuture[]::new)) - .join(); - - return CtpQueryUtils.queryAll(ctpClient, query, pageConsumer).toCompletableFuture(); - } - - private QueryUtils() {} -} +// package com.commercetools.project.sync.util; +// +// import com.commercetools.api.client.ProjectApiRoot; +// import com.commercetools.api.models.common.BaseResource; +// +// import java.util.List; +// import java.util.concurrent.CompletableFuture; +// import java.util.concurrent.CompletionStage; +// import java.util.function.Consumer; +// import java.util.function.Function; +// import javax.annotation.Nonnull; +// +// public final class QueryUtils { +// /** +// * Applies the {@code resourceToRequestMapper} function on each page, resulting from the {@code +// * query} executed by the {@code ctpClient}, to map each resource to a {@link SphereRequest} and +// * then executes these requests in parallel within each page. +// * +// * @param ctpClient defines the CTP project to apply the query on. +// * @param query query that should be made on the CTP project. +// * @param resourceToRequestMapper defines a mapper function that should be applied on each +// * resource, in the fetched page from the query on the specified CTP project, to map it to a +// * {@link SphereRequest}. +// */ +// public static > +// CompletableFuture queryAndExecute( +// @Nonnull final ProjectApiRoot ctpClient, +// @Nonnull final QueryDsl query, +// @Nonnull final Function resourceToRequestMapper) { +// +// return queryAndCompose( +// ctpClient, query, resource -> ctpClient.execute(resourceToRequestMapper.apply(resource))); +// } +// +// /** +// * Applies the {@code resourceToStageMapper} function on each page, resulting from the {@code +// * query} executed by the {@code ctpClient}, to map each resource to a {@link CompletionStage} +// and +// * then executes these stages in parallel within each page. +// * +// * @param ctpClient defines the CTP project to apply the query on. +// * @param query query that should be made on the CTP project. +// * @param resourceToStageMapper defines a mapper function that should be applied on each +// resource, +// * in the fetched page from the query on the specified CTP project, to map it to a {@link +// * CompletionStage} which will be executed (in a blocking fashion) after every page fetch. +// */ +// private static , S> +// CompletableFuture queryAndCompose( +// @Nonnull final ProjectApiRoot ctpClient, +// @Nonnull final QueryDsl query, +// @Nonnull final Function> resourceToStageMapper) { +// +// final Consumer> pageConsumer = +// pageElements -> +// CompletableFuture.allOf( +// pageElements.stream() +// .map(resourceToStageMapper) +// .map(CompletionStage::toCompletableFuture) +// .toArray(CompletableFuture[]::new)) +// .join(); +// +// return com.commercetools.api.client.QueryUtils.queryAll(ctpClient, query, +// pageConsumer).toCompletableFuture(); +// } +// +// private QueryUtils() {} +// } diff --git a/src/main/java/com/commercetools/project/sync/BaseSyncStatisticsDeserializer.java b/src/main/java/com/commercetools/project/sync/BaseSyncStatisticsDeserializer.java index 6f9330f7..645708c7 100644 --- a/src/main/java/com/commercetools/project/sync/BaseSyncStatisticsDeserializer.java +++ b/src/main/java/com/commercetools/project/sync/BaseSyncStatisticsDeserializer.java @@ -3,13 +3,14 @@ import com.commercetools.sync.commons.helpers.BaseSyncStatistics; import com.commercetools.sync.customers.helpers.CustomerSyncStatistics; import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.deser.std.StdDeserializer; import java.io.IOException; +// This class compiles but not tested yet +// TODO: Test class and adjust logic if needed public class BaseSyncStatisticsDeserializer extends StdDeserializer { public BaseSyncStatisticsDeserializer() { @@ -22,7 +23,7 @@ public BaseSyncStatisticsDeserializer(final Class vc) { @Override public BaseSyncStatistics deserialize(JsonParser jsonParser, DeserializationContext ctxt) - throws IOException, JsonProcessingException { + throws IOException { final ObjectMapper mapper = (ObjectMapper) jsonParser.getCodec(); final JsonNode syncStatisticsNode = mapper.readTree(jsonParser); // since BaseSyncStatistics is abstract and there's no difference in the Subclasses except diff --git a/src/main/java/com/commercetools/project/sync/CliRunner.java b/src/main/java/com/commercetools/project/sync/CliRunner.java index 14406af8..f5cb9aa0 100644 --- a/src/main/java/com/commercetools/project/sync/CliRunner.java +++ b/src/main/java/com/commercetools/project/sync/CliRunner.java @@ -3,7 +3,7 @@ import static com.commercetools.project.sync.model.ProductSyncCustomRequest.parseProductQueryParametersOption; import static com.commercetools.project.sync.util.SyncUtils.getApplicationName; import static com.commercetools.project.sync.util.SyncUtils.getApplicationVersion; -import static io.sphere.sdk.utils.CompletableFutureUtils.exceptionallyCompletedFuture; +import static io.vrap.rmf.base.client.utils.CompletableFutureUtils.exceptionallyCompletedFuture; import static java.lang.String.format; import com.commercetools.project.sync.exception.CliException; diff --git a/src/main/java/com/commercetools/project/sync/Syncer.java b/src/main/java/com/commercetools/project/sync/Syncer.java index a46f9ab6..94d62ca8 100644 --- a/src/main/java/com/commercetools/project/sync/Syncer.java +++ b/src/main/java/com/commercetools/project/sync/Syncer.java @@ -1,9 +1,20 @@ package com.commercetools.project.sync; +import static com.commercetools.api.client.QueryUtils.queryAll; import static com.commercetools.project.sync.util.SyncUtils.getSyncModuleName; -import static com.commercetools.sync.commons.utils.CtpQueryUtils.queryAll; import static java.lang.String.format; +import com.commercetools.api.client.PagedQueryResourceRequest; +import com.commercetools.api.client.ProjectApiRoot; +import com.commercetools.api.models.DomainResource; +import com.commercetools.api.models.ResourcePagedQueryResponse; +import com.commercetools.api.models.ResourceUpdateAction; +import com.commercetools.api.models.category.Category; +import com.commercetools.api.models.category.CategoryDraft; +import com.commercetools.api.models.common.BaseResource; +import com.commercetools.api.models.custom_object.CustomObject; +import com.commercetools.api.models.product.ProductDraft; +import com.commercetools.api.models.product.ProductProjection; import com.commercetools.project.sync.model.response.LastSyncCustomObject; import com.commercetools.project.sync.service.CustomObjectService; import com.commercetools.sync.commons.BaseSync; @@ -11,12 +22,7 @@ import com.commercetools.sync.commons.helpers.BaseSyncStatistics; import com.commercetools.sync.commons.utils.CaffeineReferenceIdToKeyCacheImpl; import com.commercetools.sync.commons.utils.ReferenceIdToKeyCache; -import io.sphere.sdk.client.SphereClient; -import io.sphere.sdk.customobjects.CustomObject; -import io.sphere.sdk.models.ResourceView; -import io.sphere.sdk.models.Versioned; -import io.sphere.sdk.queries.QueryDsl; -import io.sphere.sdk.queries.QueryPredicate; +import io.vrap.rmf.base.client.ApiHttpResponse; import java.time.Clock; import java.time.ZonedDateTime; import java.util.List; @@ -26,43 +32,44 @@ import javax.annotation.Nullable; import net.logstash.logback.marker.Markers; import org.slf4j.Logger; -import org.slf4j.LoggerFactory; /** * Base class of the syncer that handles syncing a resource from a source CTP project to a target * CTP project. * - * @param The type of the resource to update (e.g. {@link - * io.sphere.sdk.products.ProductProjection}, {@link io.sphere.sdk.categories.CategoryDraft}, - * etc..) - * @param The type of the resource to create (e.g. {@link io.sphere.sdk.products.Product}, - * {@link io.sphere.sdk.categories.Category}, etc..) - * @param The type of the resource draft (e.g. {@link io.sphere.sdk.products.ProductDraft}, - * {@link io.sphere.sdk.categories.CategoryDraft}, etc..) - * @param The type of the sync statistics resulting from the sync process (e.g. {@link - * com.commercetools.sync.products.helpers.ProductSyncStatistics}, {@link + * @param The type of the resource to update (e.g. {@link ProductProjection}, {@link + * Category}, etc..) + * @param The update actions to update the resource with (e.g. {@link + * com.commercetools.api.models.product.ProductUpdateAction}) + * @param The type of the resource draft (e.g. {@link ProductDraft}, {@link + * CategoryDraft}, etc..) + * @param The type of the query builder dsl class (e.g. {@link + * com.commercetools.api.predicates.query.state.StateQueryBuilderDsl}) + * @param The type of the sync statistics resulting from the sync process (e.g. + * {@link com.commercetools.sync.products.helpers.ProductSyncStatistics}, {@link * com.commercetools.sync.categories.helpers.CategorySyncStatistics}, etc..) - * @param The type of the sync options used for the sync (e.g. {@link + * @param The type of the sync options used for the sync (e.g. {@link * com.commercetools.sync.products.ProductSyncOptions}, {@link * com.commercetools.sync.categories.CategorySyncOptions}, etc..) - * @param The type of the query used to query for the source resources (e.g. {@link - * io.sphere.sdk.products.queries.ProductProjectionQuery}, {@link - * io.sphere.sdk.categories.queries.CategoryQuery}, etc..) - * @param The type of the sync instance used to execute the sync process (e.g. {@link + * @param The type of the query used to query for the source resources (e.g. {@link + * com.commercetools.api.client.ByProjectKeyProductProjectionsGet}, {@link + * com.commercetools.api.client.ByProjectKeyCategoriesGet}, etc..) + * @param The type of the sync instance used to execute the sync process (e.g. {@link * com.commercetools.sync.products.ProductSync}, {@link * com.commercetools.sync.categories.CategorySync}, etc..) */ public abstract class Syncer< - RU extends ResourceView, - RC extends ResourceView, - RD, - V extends Versioned, - S extends BaseSyncStatistics, - O extends BaseSyncOptions, - Q extends QueryDsl, - B extends BaseSync> { - - private static final Logger LOGGER = LoggerFactory.getLogger(Syncer.class); + ResourceT extends BaseResource & DomainResource, + ResourceUpdateActionT extends ResourceUpdateAction, + ResourceDraftT, + QueryBuilderDslT, + SyncStatisticsT extends BaseSyncStatistics, + SyncOptionsT extends BaseSyncOptions, + PagedQueryT extends + PagedQueryResourceRequest, + PagedQueryResponseT extends ResourcePagedQueryResponse, + BaseSyncT extends + BaseSync> { /* Using default Caffeine cache implementation from sync-java library for caching reference * IdToKey values. @@ -70,9 +77,9 @@ public abstract class Syncer< protected static final ReferenceIdToKeyCache referenceIdToKeyCache = new CaffeineReferenceIdToKeyCacheImpl(); - private final B sync; - private final SphereClient sourceClient; - private final SphereClient targetClient; + private final BaseSyncT sync; + private final ProjectApiRoot sourceClient; + private final ProjectApiRoot targetClient; private final CustomObjectService customObjectService; private final Clock clock; @@ -90,9 +97,9 @@ public abstract class Syncer< * @param clock the clock to record the time for calculating the sync duration. */ public Syncer( - @Nonnull final B sync, - @Nonnull final SphereClient sourceClient, - @Nonnull final SphereClient targetClient, + @Nonnull final BaseSyncT sync, + @Nonnull final ProjectApiRoot sourceClient, + @Nonnull final ProjectApiRoot targetClient, @Nonnull final CustomObjectService customObjectService, @Nonnull final Clock clock) { this.sync = sync; @@ -103,13 +110,12 @@ public Syncer( } /** - * Fetches the sourceClient's project resources of type {@code T} with all needed references - * expanded and treats each page as a batch to the sync process. Then executes the sync process of - * on every page fetched from the source project sequentially. It then returns a completion stage - * containing a {@link Void} result after the execution of the sync process and logging the - * result. + * Fetches the sourceClient's project resources of type {@code ResourceT} with all needed + * references expanded and treats each page as a batch to the sync process. Then executes the sync + * process on every page sequentially. It then returns a completion stage containing a {@link + * Void} result after the execution of the sync process and logging the result. * - *

Note: If {@code isFullSync} is {@code false}, i.e. a delta sync is required, the method + *

Note: If {@param isFullSync} is {@code false}, i.e. a delta sync is required, the method * checks if there was a last sync time stamp persisted as a custom object in the target project * for this specific source project and sync module. If there is, it will sync only the resources * which were modified after the last sync time stamp and before the start of this sync. @@ -122,10 +128,10 @@ public Syncer( */ public CompletionStage sync(@Nullable final String runnerName, final boolean isFullSync) { - final String sourceProjectKey = sourceClient.getConfig().getProjectKey(); + final String sourceProjectKey = sourceClient.getProjectKey(); final String syncModuleName = getSyncModuleName(sync.getClass()); if (getLoggerInstance().isInfoEnabled()) { - final String targetProjectKey = targetClient.getConfig().getProjectKey(); + final String targetProjectKey = targetClient.getProjectKey(); getLoggerInstance() .info( format( @@ -179,7 +185,7 @@ private CompletionStage syncResourcesSinceLastSync( } @Nonnull - private CompletionStage getQueryOfResourcesSinceLastSync( + private CompletionStage getQueryOfResourcesSinceLastSync( @Nonnull final String sourceProjectKey, @Nonnull final String syncModuleName, @Nullable final String runnerName, @@ -190,7 +196,6 @@ private CompletionStage getQueryOfResourcesSinceLastSync( .thenApply( customObjectOptional -> customObjectOptional - .map(CustomObject::getValue) .map(LastSyncCustomObject::getLastSyncTimestamp) .map( lastSyncTimestamp -> @@ -201,21 +206,20 @@ private CompletionStage getQueryOfResourcesSinceLastSync( } @Nonnull - private Q getQueryWithTimeBoundedPredicate( + private PagedQueryT getQueryWithTimeBoundedPredicate( @Nonnull final ZonedDateTime lowerBound, @Nonnull final ZonedDateTime upperBound) { - - final QueryPredicate queryPredicate = - QueryPredicate.of( - format( - "lastModifiedAt >= \"%s\" AND lastModifiedAt <= \"%s\"", lowerBound, upperBound)); - return getQuery().plusPredicates(queryPredicate); + return (PagedQueryT) + getQuery() + .withWhere("lastModifiedAt >= :lower AND lastModifiedAt <= :upper") + .withPredicateVar("lower", lowerBound) + .withPredicateVar("upper", upperBound); } @Nonnull - private CompletionStage sync(@Nonnull final Q queryResourcesSinceLastSync) { + private CompletionStage sync(@Nonnull final PagedQueryT queryResourcesSinceLastSync) { final long timeBeforeSync = clock.millis(); - return queryAll(sourceClient, queryResourcesSinceLastSync, this::syncPage) + return queryAll(queryResourcesSinceLastSync, this::syncPage) .thenApply( ignoredResult -> { final long timeAfterSync = clock.millis(); @@ -224,7 +228,7 @@ private CompletionStage sync(@Nonnull final Q queryResourcesSinceLastSync) } @Nonnull - private CompletionStage> createNewLastSyncCustomObject( + private CompletableFuture> createNewLastSyncCustomObject( @Nonnull final String sourceProjectKey, @Nonnull final String syncModuleName, @Nullable final String runnerName, @@ -240,7 +244,7 @@ private CompletionStage> createNewLastSyncCus final ZonedDateTime lastSyncTimestampMinusBuffer = newLastSyncTimestamp.minusMinutes(bufferInMinutes); - final LastSyncCustomObject lastSyncCustomObject = + final LastSyncCustomObject lastSyncCustomObject = LastSyncCustomObject.of( lastSyncTimestampMinusBuffer, sync.getStatistics(), syncDurationInMillis); @@ -253,25 +257,26 @@ private CompletionStage> createNewLastSyncCus * {@link CompletableFuture} of each sync process on the given page as a batch. */ @Nonnull - private S syncPage(@Nonnull final List page) { + private SyncStatisticsT syncPage(@Nonnull final List page) { return transform(page).thenCompose(sync::sync).toCompletableFuture().join(); } /** - * Given a {@link List} representing a page of resources of type {@code T}, this method creates a - * a list of drafts of type {@link S} where reference ids of the references are replaced with keys - * and are ready for reference resolution by the sync process. + * Given a {@link List} representing a page of resources of type {@link ResourceT}, this method + * creates a list of drafts of type {@link ResourceDraftT} where reference ids of the references + * are replaced with keys and are ready for reference resolution by the sync process. * - * @return a {@link CompletionStage} containing a list of drafts of type {@link S} after being - * transformed from type {@link RU}. + * @return a {@link CompletionStage} containing a list of drafts of type {@link ResourceDraftT} + * after being transformed from type {@link ResourceT}. */ @Nonnull - protected abstract CompletionStage> transform(@Nonnull final List page); + protected abstract CompletionStage> transform( + @Nonnull final List page); @Nonnull - protected abstract Q getQuery(); + protected abstract PagedQueryT getQuery(); - public B getSync() { + public BaseSyncT getSync() { return sync; } @@ -279,7 +284,7 @@ public B getSync() { protected abstract Logger getLoggerInstance(); @Nonnull - public SphereClient getSourceClient() { + public ProjectApiRoot getSourceClient() { return sourceClient; } } diff --git a/src/main/java/com/commercetools/project/sync/SyncerApplication.java b/src/main/java/com/commercetools/project/sync/SyncerApplication.java index 40284fa5..a424cd3d 100644 --- a/src/main/java/com/commercetools/project/sync/SyncerApplication.java +++ b/src/main/java/com/commercetools/project/sync/SyncerApplication.java @@ -1,7 +1,7 @@ package com.commercetools.project.sync; -import static com.commercetools.project.sync.util.SphereClientUtils.CTP_SOURCE_CLIENT; -import static com.commercetools.project.sync.util.SphereClientUtils.CTP_TARGET_CLIENT; +import static com.commercetools.project.sync.util.CtpClientUtils.CTP_SOURCE_CLIENT; +import static com.commercetools.project.sync.util.CtpClientUtils.CTP_TARGET_CLIENT; import java.time.Clock; diff --git a/src/main/java/com/commercetools/project/sync/SyncerFactory.java b/src/main/java/com/commercetools/project/sync/SyncerFactory.java index 6057e267..ee4367d9 100644 --- a/src/main/java/com/commercetools/project/sync/SyncerFactory.java +++ b/src/main/java/com/commercetools/project/sync/SyncerFactory.java @@ -4,10 +4,15 @@ import static com.commercetools.project.sync.CliRunner.SYNC_MODULE_OPTION_DESCRIPTION; import static com.commercetools.project.sync.CliRunner.SYNC_MODULE_OPTION_LONG; import static com.commercetools.project.sync.CliRunner.SYNC_MODULE_OPTION_SHORT; -import static io.sphere.sdk.utils.CompletableFutureUtils.exceptionallyCompletedFuture; +import static io.vrap.rmf.base.client.utils.CompletableFutureUtils.exceptionallyCompletedFuture; import static java.lang.String.format; import static org.apache.commons.lang3.StringUtils.isBlank; +import com.commercetools.api.client.PagedQueryResourceRequest; +import com.commercetools.api.client.ProjectApiRoot; +import com.commercetools.api.models.ResourcePagedQueryResponse; +import com.commercetools.api.models.ResourceUpdateAction; +import com.commercetools.api.models.common.BaseResource; import com.commercetools.project.sync.cartdiscount.CartDiscountSyncer; import com.commercetools.project.sync.category.CategorySyncer; import com.commercetools.project.sync.customer.CustomerSyncer; @@ -24,10 +29,6 @@ import com.commercetools.sync.commons.BaseSync; import com.commercetools.sync.commons.BaseSyncOptions; import com.commercetools.sync.commons.helpers.BaseSyncStatistics; -import io.sphere.sdk.client.SphereClient; -import io.sphere.sdk.models.ResourceView; -import io.sphere.sdk.models.Versioned; -import io.sphere.sdk.queries.QueryDsl; import java.time.Clock; import java.util.ArrayList; import java.util.Arrays; @@ -43,14 +44,14 @@ import javax.annotation.Nullable; public final class SyncerFactory { - private final Supplier targetClientSupplier; - private final Supplier sourceClientSupplier; + private final Supplier targetClientSupplier; + private final Supplier sourceClientSupplier; private final Clock clock; private final boolean shouldCloseClients; private SyncerFactory( - @Nonnull final Supplier sourceClient, - @Nonnull final Supplier targetClient, + @Nonnull final Supplier sourceClient, + @Nonnull final Supplier targetClient, @Nonnull final Clock clock, final boolean closeClients) { this.targetClientSupplier = targetClient; @@ -61,16 +62,16 @@ private SyncerFactory( @Nonnull public static SyncerFactory of( - @Nonnull final Supplier sourceClient, - @Nonnull final Supplier targetClient, + @Nonnull final Supplier sourceClient, + @Nonnull final Supplier targetClient, @Nonnull final Clock clock) { return new SyncerFactory(sourceClient, targetClient, clock, true); } @Nonnull public static SyncerFactory of( - @Nonnull final Supplier sourceClient, - @Nonnull final Supplier targetClient, + @Nonnull final Supplier sourceClient, + @Nonnull final Supplier targetClient, @Nonnull final Clock clock, final boolean closeClients) { return new SyncerFactory(sourceClient, targetClient, clock, closeClients); @@ -127,14 +128,15 @@ private CompletableFuture chainSyncExecution( for (SyncModuleOption syncOptionValue : syncOptions) { Syncer< - ? extends ResourceView, - ? extends ResourceView, + ? extends BaseResource, + ? extends ResourceUpdateAction, + ?, ?, - ? extends Versioned, ? extends BaseSyncStatistics, ? extends BaseSyncOptions, - ? extends QueryDsl, - ? extends BaseSync> + ? extends PagedQueryResourceRequest, + ? extends ResourcePagedQueryResponse, + ? extends BaseSync> syncer = buildSyncer( syncOptionValue, @@ -286,14 +288,15 @@ private void closeClients() { * @return The instance of the syncer corresponding to the passed option value. */ private Syncer< - ? extends ResourceView, - ? extends ResourceView, + ? extends BaseResource, + ? extends ResourceUpdateAction, + ?, ?, - ? extends Versioned, ? extends BaseSyncStatistics, ? extends BaseSyncOptions, - ? extends QueryDsl, - ? extends BaseSync> + ? extends PagedQueryResourceRequest, + ? extends ResourcePagedQueryResponse, + ? extends BaseSync> buildSyncer( @Nonnull final SyncModuleOption syncModuleOption, @Nonnull final String runnerNameOptionValue, @@ -301,14 +304,15 @@ private void closeClients() { @Nullable final ProductSyncCustomRequest productSyncCustomRequest) { Syncer< - ? extends ResourceView, - ? extends ResourceView, + ? extends BaseResource, + ? extends ResourceUpdateAction, + ?, ?, - ? extends Versioned, ? extends BaseSyncStatistics, ? extends BaseSyncOptions, - ? extends QueryDsl, - ? extends BaseSync> + ? extends PagedQueryResourceRequest, + ? extends ResourcePagedQueryResponse, + ? extends BaseSync> syncer = null; switch (syncModuleOption) { diff --git a/src/main/java/com/commercetools/project/sync/cartdiscount/CartDiscountSyncer.java b/src/main/java/com/commercetools/project/sync/cartdiscount/CartDiscountSyncer.java index d6fd5a7d..a254c243 100644 --- a/src/main/java/com/commercetools/project/sync/cartdiscount/CartDiscountSyncer.java +++ b/src/main/java/com/commercetools/project/sync/cartdiscount/CartDiscountSyncer.java @@ -4,6 +4,13 @@ import static com.commercetools.project.sync.util.SyncUtils.logWarningCallback; import static com.commercetools.sync.cartdiscounts.utils.CartDiscountTransformUtils.toCartDiscountDrafts; +import com.commercetools.api.client.ByProjectKeyCartDiscountsGet; +import com.commercetools.api.client.ProjectApiRoot; +import com.commercetools.api.models.cart_discount.CartDiscount; +import com.commercetools.api.models.cart_discount.CartDiscountDraft; +import com.commercetools.api.models.cart_discount.CartDiscountPagedQueryResponse; +import com.commercetools.api.models.cart_discount.CartDiscountUpdateAction; +import com.commercetools.api.predicates.query.cart_discount.CartDiscountQueryBuilderDsl; import com.commercetools.project.sync.Syncer; import com.commercetools.project.sync.service.CustomObjectService; import com.commercetools.project.sync.service.impl.CustomObjectServiceImpl; @@ -14,11 +21,6 @@ import com.commercetools.sync.commons.exceptions.SyncException; import com.commercetools.sync.commons.utils.QuadConsumer; import com.commercetools.sync.commons.utils.TriConsumer; -import io.sphere.sdk.cartdiscounts.CartDiscount; -import io.sphere.sdk.cartdiscounts.CartDiscountDraft; -import io.sphere.sdk.cartdiscounts.queries.CartDiscountQuery; -import io.sphere.sdk.client.SphereClient; -import io.sphere.sdk.commands.UpdateAction; import java.time.Clock; import java.util.List; import java.util.Optional; @@ -30,12 +32,13 @@ public final class CartDiscountSyncer extends Syncer< CartDiscount, - CartDiscount, + CartDiscountUpdateAction, CartDiscountDraft, - CartDiscount, + CartDiscountQueryBuilderDsl, CartDiscountSyncStatistics, CartDiscountSyncOptions, - CartDiscountQuery, + ByProjectKeyCartDiscountsGet, + CartDiscountPagedQueryResponse, CartDiscountSync> { private static final Logger LOGGER = LoggerFactory.getLogger(CartDiscountSyncer.class); @@ -43,8 +46,8 @@ public final class CartDiscountSyncer /** Instantiates a {@link Syncer} instance. */ private CartDiscountSyncer( @Nonnull final CartDiscountSync cartDiscountSync, - @Nonnull final SphereClient sourceClient, - @Nonnull final SphereClient targetClient, + @Nonnull final ProjectApiRoot sourceClient, + @Nonnull final ProjectApiRoot targetClient, @Nonnull final CustomObjectService customObjectService, @Nonnull final Clock clock) { super(cartDiscountSync, sourceClient, targetClient, customObjectService, clock); @@ -52,15 +55,15 @@ private CartDiscountSyncer( @Nonnull public static CartDiscountSyncer of( - @Nonnull final SphereClient sourceClient, - @Nonnull final SphereClient targetClient, + @Nonnull final ProjectApiRoot sourceClient, + @Nonnull final ProjectApiRoot targetClient, @Nonnull final Clock clock) { final QuadConsumer< SyncException, Optional, Optional, - List>> + List> logErrorCallback = (exception, newResourceDraft, oldResource, updateActions) -> logErrorCallback(LOGGER, "cart discount", exception, oldResource, updateActions); @@ -91,8 +94,8 @@ protected CompletionStage> transform( @Nonnull @Override - protected CartDiscountQuery getQuery() { - return CartDiscountQuery.of(); + protected ByProjectKeyCartDiscountsGet getQuery() { + return getSourceClient().cartDiscounts().get(); } @Nonnull diff --git a/src/main/java/com/commercetools/project/sync/category/CategorySyncer.java b/src/main/java/com/commercetools/project/sync/category/CategorySyncer.java index 2cfcc7c6..ce97ced8 100644 --- a/src/main/java/com/commercetools/project/sync/category/CategorySyncer.java +++ b/src/main/java/com/commercetools/project/sync/category/CategorySyncer.java @@ -4,6 +4,13 @@ import static com.commercetools.project.sync.util.SyncUtils.logWarningCallback; import static com.commercetools.sync.categories.utils.CategoryTransformUtils.toCategoryDrafts; +import com.commercetools.api.client.ByProjectKeyCategoriesGet; +import com.commercetools.api.client.ProjectApiRoot; +import com.commercetools.api.models.category.Category; +import com.commercetools.api.models.category.CategoryDraft; +import com.commercetools.api.models.category.CategoryPagedQueryResponse; +import com.commercetools.api.models.category.CategoryUpdateAction; +import com.commercetools.api.predicates.query.category.CategoryQueryBuilderDsl; import com.commercetools.project.sync.Syncer; import com.commercetools.project.sync.service.CustomObjectService; import com.commercetools.project.sync.service.impl.CustomObjectServiceImpl; @@ -14,11 +21,6 @@ import com.commercetools.sync.commons.exceptions.SyncException; import com.commercetools.sync.commons.utils.QuadConsumer; import com.commercetools.sync.commons.utils.TriConsumer; -import io.sphere.sdk.categories.Category; -import io.sphere.sdk.categories.CategoryDraft; -import io.sphere.sdk.categories.queries.CategoryQuery; -import io.sphere.sdk.client.SphereClient; -import io.sphere.sdk.commands.UpdateAction; import java.time.Clock; import java.util.List; import java.util.Optional; @@ -30,12 +32,13 @@ public final class CategorySyncer extends Syncer< Category, - Category, + CategoryUpdateAction, CategoryDraft, - Category, + CategoryQueryBuilderDsl, CategorySyncStatistics, CategorySyncOptions, - CategoryQuery, + ByProjectKeyCategoriesGet, + CategoryPagedQueryResponse, CategorySync> { private static final Logger LOGGER = LoggerFactory.getLogger(CategorySyncer.class); @@ -43,8 +46,8 @@ public final class CategorySyncer /** Instantiates a {@link Syncer} instance. */ private CategorySyncer( @Nonnull final CategorySync categorySync, - @Nonnull final SphereClient sourceClient, - @Nonnull final SphereClient targetClient, + @Nonnull final ProjectApiRoot sourceClient, + @Nonnull final ProjectApiRoot targetClient, @Nonnull final CustomObjectService customObjectService, @Nonnull final Clock clock) { super(categorySync, sourceClient, targetClient, customObjectService, clock); @@ -52,14 +55,11 @@ private CategorySyncer( @Nonnull public static CategorySyncer of( - @Nonnull final SphereClient sourceClient, - @Nonnull final SphereClient targetClient, + @Nonnull final ProjectApiRoot sourceClient, + @Nonnull final ProjectApiRoot targetClient, @Nonnull final Clock clock) { final QuadConsumer< - SyncException, - Optional, - Optional, - List>> + SyncException, Optional, Optional, List> logErrorCallback = (exception, newResourceDraft, oldResource, updateActions) -> logErrorCallback(LOGGER, "category", exception, oldResource, updateActions); @@ -88,8 +88,8 @@ protected CompletionStage> transform(@Nonnull final List { private static final Logger LOGGER = LoggerFactory.getLogger(CustomerSyncer.class); private CustomerSyncer( @Nonnull final CustomerSync customerSync, - @Nonnull final SphereClient sourceClient, - @Nonnull final SphereClient targetClient, + @Nonnull final ProjectApiRoot sourceClient, + @Nonnull final ProjectApiRoot targetClient, @Nonnull final CustomObjectService customObjectService, @Nonnull final Clock clock) { super(customerSync, sourceClient, targetClient, customObjectService, clock); } public static CustomerSyncer of( - @Nonnull final SphereClient sourceClient, - @Nonnull final SphereClient targetClient, + @Nonnull final ProjectApiRoot sourceClient, + @Nonnull final ProjectApiRoot targetClient, @Nonnull final Clock clock) { final QuadConsumer< - SyncException, - Optional, - Optional, - List>> + SyncException, Optional, Optional, List> logErrorCallback = (exception, newResourceDraft, oldResource, updateActions) -> logErrorCallback(LOGGER, "customer", exception, oldResource, updateActions); @@ -86,8 +86,8 @@ protected CompletionStage> transform(@Nonnull final List, - CustomObject, - CustomObjectDraft, - CustomObject, + CustomObject, + NoopResourceUpdateAction, + CustomObjectDraft, + CustomObjectQueryBuilderDsl, CustomObjectSyncStatistics, CustomObjectSyncOptions, - CustomObjectQuery, + ByProjectKeyCustomObjectsGet, + CustomObjectPagedQueryResponse, CustomObjectSync> { private static final Logger LOGGER = LoggerFactory.getLogger(CustomObjectSyncer.class); @@ -71,8 +73,8 @@ public final class CustomObjectSyncer */ private CustomObjectSyncer( @Nonnull CustomObjectSync sync, - @Nonnull SphereClient sourceClient, - @Nonnull SphereClient targetClient, + @Nonnull ProjectApiRoot sourceClient, + @Nonnull ProjectApiRoot targetClient, @Nonnull CustomObjectService customObjectService, @Nonnull Clock clock, @Nullable String runnerName, @@ -84,31 +86,31 @@ private CustomObjectSyncer( @Nonnull @Override - protected CompletionStage>> transform( - @Nonnull List> page) { - final List> collect = + protected CompletableFuture> transform(@Nonnull List page) { + final List collect = page.stream() .map( customObject -> - CustomObjectDraft.ofUnversionedUpsert( - customObject.getContainer(), - customObject.getKey(), - customObject.getValue())) + CustomObjectDraftBuilder.of() + .container(customObject.getContainer()) + .key(customObject.getKey()) + .value(customObject.getValue()) + .build()) .collect(Collectors.toList()); return CompletableFuture.completedFuture(collect); } @Nonnull @Override - protected CustomObjectQuery getQuery() { - final CustomObjectQuery customObjectQuery = CustomObjectQuery.ofJsonNode(); + protected ByProjectKeyCustomObjectsGet getQuery() { + final ByProjectKeyCustomObjectsGet customObjectQuery = getSourceClient().customObjects().get(); if (isSyncProjectSyncCustomObjects) { return customObjectQuery; } else { final List excludedContainerNames = getExcludedContainerNames(); - return customObjectQuery.plusPredicates( - customObjectQueryModel -> - customObjectQueryModel.container().isNotIn(excludedContainerNames)); + return customObjectQuery + .withWhere("container not in :excludedNames") + .withPredicateVar("excludedNames", excludedContainerNames); } } @@ -120,16 +122,16 @@ protected Logger getLoggerInstance() { @Nonnull public static CustomObjectSyncer of( - @Nonnull final SphereClient sourceClient, - @Nonnull final SphereClient targetClient, + @Nonnull final ProjectApiRoot sourceClient, + @Nonnull final ProjectApiRoot targetClient, @Nonnull final Clock clock, @Nullable final String runnerName, final boolean isSyncProjectSyncCustomObjects) { final QuadConsumer< SyncException, - Optional>, - Optional>, - List>>> + Optional, + Optional, + List> logErrorCallback = (exception, newResourceDraft, oldResource, updateActions) -> { final String resourceIdentifier = getCustomObjectResourceIdentifier(oldResource); @@ -137,8 +139,7 @@ public static CustomObjectSyncer of( logErrorCallback( LOGGER, "customObject", exception, resourceIdentifier, updateActions); }; - final TriConsumer< - SyncException, Optional>, Optional>> + final TriConsumer, Optional> logWarningCallback = (exception, newResourceDraft, oldResource) -> { final String resourceIdentifier = getCustomObjectResourceIdentifier(oldResource); @@ -165,7 +166,7 @@ public static CustomObjectSyncer of( } private static String getCustomObjectResourceIdentifier( - @Nonnull Optional> oldResource) { + @Nonnull Optional oldResource) { return oldResource .map(CustomObjectCompositeIdentifier::of) .map(CustomObjectCompositeIdentifier::toString) diff --git a/src/main/java/com/commercetools/project/sync/inventoryentry/InventoryEntrySyncer.java b/src/main/java/com/commercetools/project/sync/inventoryentry/InventoryEntrySyncer.java index f5b23a6b..3e7df1a5 100644 --- a/src/main/java/com/commercetools/project/sync/inventoryentry/InventoryEntrySyncer.java +++ b/src/main/java/com/commercetools/project/sync/inventoryentry/InventoryEntrySyncer.java @@ -5,6 +5,13 @@ import static com.commercetools.project.sync.util.SyncUtils.logWarningCallback; import static com.commercetools.sync.inventories.utils.InventoryTransformUtils.toInventoryEntryDrafts; +import com.commercetools.api.client.ByProjectKeyInventoryGet; +import com.commercetools.api.client.ProjectApiRoot; +import com.commercetools.api.models.inventory.InventoryEntry; +import com.commercetools.api.models.inventory.InventoryEntryDraft; +import com.commercetools.api.models.inventory.InventoryEntryUpdateAction; +import com.commercetools.api.models.inventory.InventoryPagedQueryResponse; +import com.commercetools.api.predicates.query.inventory.InventoryEntryQueryBuilderDsl; import com.commercetools.project.sync.Syncer; import com.commercetools.project.sync.service.CustomObjectService; import com.commercetools.project.sync.service.impl.CustomObjectServiceImpl; @@ -15,11 +22,6 @@ import com.commercetools.sync.inventories.InventorySyncOptions; import com.commercetools.sync.inventories.InventorySyncOptionsBuilder; import com.commercetools.sync.inventories.helpers.InventorySyncStatistics; -import io.sphere.sdk.client.SphereClient; -import io.sphere.sdk.commands.UpdateAction; -import io.sphere.sdk.inventory.InventoryEntry; -import io.sphere.sdk.inventory.InventoryEntryDraft; -import io.sphere.sdk.inventory.queries.InventoryEntryQuery; import java.time.Clock; import java.util.List; import java.util.Optional; @@ -31,12 +33,13 @@ public final class InventoryEntrySyncer extends Syncer< InventoryEntry, - InventoryEntry, + InventoryEntryUpdateAction, InventoryEntryDraft, - InventoryEntry, + InventoryEntryQueryBuilderDsl, InventorySyncStatistics, InventorySyncOptions, - InventoryEntryQuery, + ByProjectKeyInventoryGet, + InventoryPagedQueryResponse, InventorySync> { private static final Logger LOGGER = LoggerFactory.getLogger(InventoryEntrySyncer.class); @@ -44,23 +47,23 @@ public final class InventoryEntrySyncer /** Instantiates a {@link Syncer} instance. */ private InventoryEntrySyncer( @Nonnull final InventorySync inventorySync, - @Nonnull final SphereClient sourceClient, - @Nonnull final SphereClient targetClient, + @Nonnull final ProjectApiRoot sourceClient, + @Nonnull final ProjectApiRoot targetClient, @Nonnull final CustomObjectService customObjectService, @Nonnull final Clock clock) { super(inventorySync, sourceClient, targetClient, customObjectService, clock); } public static InventoryEntrySyncer of( - @Nonnull final SphereClient sourceClient, - @Nonnull final SphereClient targetClient, + @Nonnull final ProjectApiRoot sourceClient, + @Nonnull final ProjectApiRoot targetClient, @Nonnull final Clock clock) { final QuadConsumer< SyncException, Optional, Optional, - List>> + List> logErrorCallback = (exception, newResourceDraft, oldResource, updateActions) -> logErrorCallback( @@ -100,8 +103,8 @@ protected CompletionStage> transform( @Nonnull @Override - protected InventoryEntryQuery getQuery() { - return InventoryEntryQuery.of(); + protected ByProjectKeyInventoryGet getQuery() { + return getSourceClient().inventory().get(); } @Nonnull diff --git a/src/main/java/com/commercetools/project/sync/model/ProductSyncCustomRequest.java b/src/main/java/com/commercetools/project/sync/model/ProductSyncCustomRequest.java index 97db04ee..5e3902bb 100644 --- a/src/main/java/com/commercetools/project/sync/model/ProductSyncCustomRequest.java +++ b/src/main/java/com/commercetools/project/sync/model/ProductSyncCustomRequest.java @@ -6,6 +6,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import java.io.IOException; +// TODO: Migration needed public final class ProductSyncCustomRequest { private Long limit; diff --git a/src/main/java/com/commercetools/project/sync/model/response/LastSyncCustomObject.java b/src/main/java/com/commercetools/project/sync/model/response/LastSyncCustomObject.java index a4a69684..00db3af9 100644 --- a/src/main/java/com/commercetools/project/sync/model/response/LastSyncCustomObject.java +++ b/src/main/java/com/commercetools/project/sync/model/response/LastSyncCustomObject.java @@ -92,7 +92,6 @@ public long getLastSyncDurationInMillis() { // Setters are needed for the 'com.fasterxml.jackson' deserialization, for example, when fetching // from CTP custom objects. - public void setLastSyncTimestamp(@Nonnull final ZonedDateTime lastSyncTimestamp) { this.lastSyncTimestamp = lastSyncTimestamp; } diff --git a/src/main/java/com/commercetools/project/sync/product/ProductSyncer.java b/src/main/java/com/commercetools/project/sync/product/ProductSyncer.java index d25a552b..c56f134c 100644 --- a/src/main/java/com/commercetools/project/sync/product/ProductSyncer.java +++ b/src/main/java/com/commercetools/project/sync/product/ProductSyncer.java @@ -1,10 +1,25 @@ package com.commercetools.project.sync.product; import static com.commercetools.project.sync.util.SyncUtils.IDENTIFIER_NOT_PRESENT; +import static com.commercetools.project.sync.util.SyncUtils.getCompletionExceptionCause; import static com.commercetools.project.sync.util.SyncUtils.logErrorCallback; import static com.commercetools.project.sync.util.SyncUtils.logWarningCallback; import static com.commercetools.sync.products.utils.ProductTransformUtils.toProductDrafts; +import com.commercetools.api.client.ByProjectKeyProductProjectionsGet; +import com.commercetools.api.client.ProjectApiRoot; +import com.commercetools.api.models.WithKey; +import com.commercetools.api.models.common.DiscountedPriceDraft; +import com.commercetools.api.models.common.PriceDraft; +import com.commercetools.api.models.common.PriceDraftBuilder; +import com.commercetools.api.models.product.ProductDraft; +import com.commercetools.api.models.product.ProductDraftBuilder; +import com.commercetools.api.models.product.ProductProjection; +import com.commercetools.api.models.product.ProductProjectionPagedQueryResponse; +import com.commercetools.api.models.product.ProductUpdateAction; +import com.commercetools.api.models.product.ProductVariantDraft; +import com.commercetools.api.models.product.ProductVariantDraftBuilder; +import com.commercetools.api.predicates.query.product.ProductProjectionQueryBuilderDsl; import com.commercetools.project.sync.Syncer; import com.commercetools.project.sync.model.ProductSyncCustomRequest; import com.commercetools.project.sync.service.CustomObjectService; @@ -16,25 +31,10 @@ import com.commercetools.sync.products.ProductSyncOptions; import com.commercetools.sync.products.ProductSyncOptionsBuilder; import com.commercetools.sync.products.helpers.ProductSyncStatistics; -import io.sphere.sdk.client.SphereClient; -import io.sphere.sdk.commands.UpdateAction; -import io.sphere.sdk.models.WithKey; -import io.sphere.sdk.products.PriceDraft; -import io.sphere.sdk.products.PriceDraftBuilder; -import io.sphere.sdk.products.Product; -import io.sphere.sdk.products.ProductDraft; -import io.sphere.sdk.products.ProductDraftBuilder; -import io.sphere.sdk.products.ProductProjection; -import io.sphere.sdk.products.ProductVariantDraft; -import io.sphere.sdk.products.ProductVariantDraftBuilder; -import io.sphere.sdk.products.ProductVariantDraftDsl; -import io.sphere.sdk.products.queries.ProductProjectionQuery; -import io.sphere.sdk.queries.QueryPredicate; import java.time.Clock; import java.util.Collections; import java.util.List; import java.util.Optional; -import java.util.concurrent.CompletionException; import java.util.concurrent.CompletionStage; import java.util.stream.Collectors; import javax.annotation.Nonnull; @@ -45,12 +45,13 @@ public final class ProductSyncer extends Syncer< ProductProjection, - Product, + ProductUpdateAction, ProductDraft, - ProductProjection, + ProductProjectionQueryBuilderDsl, ProductSyncStatistics, ProductSyncOptions, - ProductProjectionQuery, + ByProjectKeyProductProjectionsGet, + ProductProjectionPagedQueryResponse, ProductSync> { private static final Logger LOGGER = LoggerFactory.getLogger(ProductSyncer.class); @@ -60,8 +61,8 @@ public final class ProductSyncer /** Instantiates a {@link Syncer} instance. */ private ProductSyncer( @Nonnull final ProductSync productSync, - @Nonnull final SphereClient sourceClient, - @Nonnull final SphereClient targetClient, + @Nonnull final ProjectApiRoot sourceClient, + @Nonnull final ProjectApiRoot targetClient, @Nonnull final CustomObjectService customObjectService, @Nonnull final Clock clock, @Nullable final ProductSyncCustomRequest productSyncCustomRequest) { @@ -71,8 +72,8 @@ private ProductSyncer( @Nonnull public static ProductSyncer of( - @Nonnull final SphereClient sourceClient, - @Nonnull final SphereClient targetClient, + @Nonnull final ProjectApiRoot sourceClient, + @Nonnull final ProjectApiRoot targetClient, @Nonnull final Clock clock, @Nullable final ProductSyncCustomRequest productSyncCustomRequest) { @@ -80,7 +81,7 @@ public static ProductSyncer of( SyncException, Optional, Optional, - List>> + List> logErrorCallback = (exception, newResourceDraft, oldResource, updateActions) -> { final String resourceKey = @@ -155,43 +156,39 @@ private List removeDiscountedFromPrices( .collect(Collectors.toList()); } - private ProductVariantDraftDsl createProductVariantDraftWithoutDiscounted( + private ProductVariantDraft createProductVariantDraftWithoutDiscounted( @Nonnull final ProductVariantDraft productVariantDraft) { final List prices = productVariantDraft.getPrices(); List priceDrafts = null; if (prices != null) { priceDrafts = prices.stream() - .map(priceDraft -> PriceDraftBuilder.of(priceDraft).discounted(null).build()) + .map( + priceDraft -> + PriceDraftBuilder.of(priceDraft) + .discounted((DiscountedPriceDraft) null) + .build()) .collect(Collectors.toList()); } return ProductVariantDraftBuilder.of(productVariantDraft).prices(priceDrafts).build(); } - @Nonnull - private static Throwable getCompletionExceptionCause(@Nonnull final Throwable exception) { - if (exception instanceof CompletionException) { - return getCompletionExceptionCause(exception.getCause()); - } - return exception; - } - @Nonnull @Override - protected ProductProjectionQuery getQuery() { - ProductProjectionQuery productQuery = ProductProjectionQuery.ofStaged(); + protected ByProjectKeyProductProjectionsGet getQuery() { + ByProjectKeyProductProjectionsGet productProjectionsGet = + getSourceClient().productProjections().get().addStaged(true); if (productSyncCustomRequest == null) { - return productQuery; + return productProjectionsGet; } if (null != productSyncCustomRequest.getLimit()) { - productQuery = productQuery.withLimit(productSyncCustomRequest.getLimit()); + productProjectionsGet = productProjectionsGet.withLimit(productSyncCustomRequest.getLimit()); } if (null != productSyncCustomRequest.getWhere()) { - productQuery = - productQuery.withPredicates(QueryPredicate.of(productSyncCustomRequest.getWhere())); + productProjectionsGet = productProjectionsGet.withWhere(productSyncCustomRequest.getWhere()); } - return productQuery; + return productProjectionsGet; } @Nonnull diff --git a/src/main/java/com/commercetools/project/sync/producttype/ProductTypeSyncer.java b/src/main/java/com/commercetools/project/sync/producttype/ProductTypeSyncer.java index 7578c541..7dacd012 100644 --- a/src/main/java/com/commercetools/project/sync/producttype/ProductTypeSyncer.java +++ b/src/main/java/com/commercetools/project/sync/producttype/ProductTypeSyncer.java @@ -3,6 +3,13 @@ import static com.commercetools.project.sync.util.SyncUtils.logErrorCallback; import static com.commercetools.project.sync.util.SyncUtils.logWarningCallback; +import com.commercetools.api.client.ByProjectKeyProductTypesGet; +import com.commercetools.api.client.ProjectApiRoot; +import com.commercetools.api.models.product_type.ProductType; +import com.commercetools.api.models.product_type.ProductTypeDraft; +import com.commercetools.api.models.product_type.ProductTypePagedQueryResponse; +import com.commercetools.api.models.product_type.ProductTypeUpdateAction; +import com.commercetools.api.predicates.query.product_type.ProductTypeQueryBuilderDsl; import com.commercetools.project.sync.Syncer; import com.commercetools.project.sync.service.CustomObjectService; import com.commercetools.project.sync.service.impl.CustomObjectServiceImpl; @@ -14,11 +21,6 @@ import com.commercetools.sync.producttypes.ProductTypeSyncOptionsBuilder; import com.commercetools.sync.producttypes.helpers.ProductTypeSyncStatistics; import com.commercetools.sync.producttypes.utils.ProductTypeTransformUtils; -import io.sphere.sdk.client.SphereClient; -import io.sphere.sdk.commands.UpdateAction; -import io.sphere.sdk.producttypes.ProductType; -import io.sphere.sdk.producttypes.ProductTypeDraft; -import io.sphere.sdk.producttypes.queries.ProductTypeQuery; import java.time.Clock; import java.util.List; import java.util.Optional; @@ -30,12 +32,13 @@ public final class ProductTypeSyncer extends Syncer< ProductType, - ProductType, + ProductTypeUpdateAction, ProductTypeDraft, - ProductType, + ProductTypeQueryBuilderDsl, ProductTypeSyncStatistics, ProductTypeSyncOptions, - ProductTypeQuery, + ByProjectKeyProductTypesGet, + ProductTypePagedQueryResponse, ProductTypeSync> { private static final Logger LOGGER = LoggerFactory.getLogger(ProductTypeSyncer.class); @@ -43,8 +46,8 @@ public final class ProductTypeSyncer /** Instantiates a {@link Syncer} instance. */ private ProductTypeSyncer( @Nonnull final ProductTypeSync productTypeSync, - @Nonnull final SphereClient sourceClient, - @Nonnull final SphereClient targetClient, + @Nonnull final ProjectApiRoot sourceClient, + @Nonnull final ProjectApiRoot targetClient, @Nonnull final CustomObjectService customObjectService, @Nonnull final Clock clock) { super(productTypeSync, sourceClient, targetClient, customObjectService, clock); @@ -52,15 +55,15 @@ private ProductTypeSyncer( @Nonnull public static ProductTypeSyncer of( - @Nonnull final SphereClient sourceClient, - @Nonnull final SphereClient targetClient, + @Nonnull final ProjectApiRoot sourceClient, + @Nonnull final ProjectApiRoot targetClient, @Nonnull final Clock clock) { final QuadConsumer< SyncException, Optional, Optional, - List>> + List> logErrorCallback = (exception, newResourceDraft, oldResource, updateActions) -> logErrorCallback(LOGGER, "product type", exception, oldResource, updateActions); @@ -92,8 +95,8 @@ protected CompletionStage> transform( @Nonnull @Override - protected ProductTypeQuery getQuery() { - return ProductTypeQuery.of(); + protected ByProjectKeyProductTypesGet getQuery() { + return getSourceClient().productTypes().get(); } @Nonnull diff --git a/src/main/java/com/commercetools/project/sync/service/CustomObjectService.java b/src/main/java/com/commercetools/project/sync/service/CustomObjectService.java index 769c2888..c5f2ffe1 100644 --- a/src/main/java/com/commercetools/project/sync/service/CustomObjectService.java +++ b/src/main/java/com/commercetools/project/sync/service/CustomObjectService.java @@ -1,26 +1,66 @@ package com.commercetools.project.sync.service; +import com.commercetools.api.models.custom_object.CustomObject; import com.commercetools.project.sync.model.response.LastSyncCustomObject; -import io.sphere.sdk.customobjects.CustomObject; +import io.vrap.rmf.base.client.ApiHttpResponse; import java.time.ZonedDateTime; import java.util.Optional; -import java.util.concurrent.CompletionStage; +import java.util.concurrent.CompletableFuture; import javax.annotation.Nonnull; import javax.annotation.Nullable; public interface CustomObjectService { + + /** + * Creates or updates a custom object with the container named 'commercetools-project-sync.{@param + * runnerName}.{@param syncModuleName}.timestampGenerator' and key equals 'timestampGenerator' and + * then reading the 'lastModifiedAt' field of the persisted custom object and returning it. + * + * @param syncModuleName the name of the resource being synced. E.g. productSync, categorySync, + * etc.. + * @param runnerName the name of this specific running sync instance defined by the user. + * @return a {@link CompletableFuture} containing the current CTP timestamp as {@link + * ZonedDateTime}. + */ @Nonnull - CompletionStage getCurrentCtpTimestamp( + CompletableFuture getCurrentCtpTimestamp( @Nullable final String runnerName, @Nonnull final String syncModuleName); + /** + * Get's a custom object which has a container named 'commercetools-project-sync.{@param + * runnerName}.{@param syncModuleName}' and key equals {@param sourceProjectKey}. The value of the + * fetched custom object is deserialized and wrapped in an {@link Optional}. + * + * @param sourceProjectKey the source project from which the data is coming. + * @param syncModuleName the name of the resource being synced. E.g. productSync, categorySync, + * etc.. + * @param runnerName the name of this specific running sync instance defined by the user. + * @return the custom object with container 'commercetools-project-sync.{@param + * runnerName}.{@param syncModuleName}' and key '{@param sourceProjectKey}', wrapped in an + * {@link Optional} as a result of a {@link CompletableFuture}. + */ @Nonnull - CompletionStage>> getLastSyncCustomObject( + CompletableFuture> getLastSyncCustomObject( @Nonnull final String sourceProjectKey, @Nonnull final String syncModuleName, @Nullable final String runnerName); + /** + * Creates (or updates an already existing) custom object, with the container named + * 'commercetools-project-sync.{@param runnerName}.{@param syncModuleName}' and key equals {@param + * sourceProjectKey}, enriched with the information in the passed {@link LastSyncCustomObject} + * param. + * + * @param sourceProjectKey the source project key from which the data is coming. + * @param syncModuleName the name of the resource being synced. E.g. productSync, categorySync, + * etc.. + * @param runnerName the name of this specific running sync instance defined by the user. + * @param lastSyncCustomObject contains information about the last sync instance. + * @return a {@link CompletableFuture} of {@link ApiHttpResponse} with the created/updated custom + * object resource. + */ @Nonnull - CompletionStage> createLastSyncCustomObject( + CompletableFuture> createLastSyncCustomObject( @Nonnull final String sourceProjectKey, @Nonnull final String syncModuleName, @Nullable final String runnerName, diff --git a/src/main/java/com/commercetools/project/sync/service/impl/CustomObjectServiceImpl.java b/src/main/java/com/commercetools/project/sync/service/impl/CustomObjectServiceImpl.java index b36bc382..d2b92e79 100644 --- a/src/main/java/com/commercetools/project/sync/service/impl/CustomObjectServiceImpl.java +++ b/src/main/java/com/commercetools/project/sync/service/impl/CustomObjectServiceImpl.java @@ -2,133 +2,116 @@ import static com.commercetools.project.sync.util.SyncUtils.buildCurrentCtpTimestampContainerName; import static com.commercetools.project.sync.util.SyncUtils.buildLastSyncTimestampContainerName; -import static java.lang.String.format; +import com.commercetools.api.client.ProjectApiRoot; +import com.commercetools.api.models.custom_object.CustomObject; +import com.commercetools.api.models.custom_object.CustomObjectDraft; +import com.commercetools.api.models.custom_object.CustomObjectDraftBuilder; import com.commercetools.project.sync.model.response.LastSyncCustomObject; import com.commercetools.project.sync.service.CustomObjectService; -import io.sphere.sdk.client.SphereClient; -import io.sphere.sdk.customobjects.CustomObject; -import io.sphere.sdk.customobjects.CustomObjectDraft; -import io.sphere.sdk.customobjects.commands.CustomObjectUpsertCommand; -import io.sphere.sdk.customobjects.queries.CustomObjectQuery; -import io.sphere.sdk.models.ResourceView; -import io.sphere.sdk.queries.PagedQueryResult; -import io.sphere.sdk.queries.QueryPredicate; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.vrap.rmf.base.client.ApiHttpResponse; +import io.vrap.rmf.base.client.error.NotFoundException; +import io.vrap.rmf.base.client.utils.json.JsonUtils; import java.time.ZonedDateTime; -import java.util.Collection; import java.util.Optional; import java.util.UUID; -import java.util.concurrent.CompletionStage; -import java.util.stream.Stream; +import java.util.concurrent.CompletableFuture; import javax.annotation.Nonnull; import javax.annotation.Nullable; public class CustomObjectServiceImpl implements CustomObjectService { public static final String TIMESTAMP_GENERATOR_KEY = "timestampGenerator"; - private final SphereClient sphereClient; + private final ProjectApiRoot ctpClient; - public CustomObjectServiceImpl(@Nonnull final SphereClient sphereClient) { - this.sphereClient = sphereClient; + public CustomObjectServiceImpl(@Nonnull final ProjectApiRoot ctpClient) { + this.ctpClient = ctpClient; } - /** - * Gets the current timestamp of CTP by creating/updating a custom object with the container: - * 'commercetools-project-sync.{@code runnerName}.{@code syncModuleName}.timestampGenerator' and - * key: 'timestampGenerator' and then returning the lastModifiedAt of this created/updated custom - * object. - * - * @param syncModuleName the name of the resource being synced. E.g. productSync, categorySync, - * etc.. - * @param runnerName the name of this specific running sync instance defined by the user. - * @return a {@link CompletionStage} containing the current CTP timestamp as {@link - * ZonedDateTime}. - */ @Nonnull @Override - public CompletionStage getCurrentCtpTimestamp( + public CompletableFuture getCurrentCtpTimestamp( @Nullable final String runnerName, @Nonnull final String syncModuleName) { final String container = buildCurrentCtpTimestampContainerName(syncModuleName, runnerName); - final CustomObjectDraft currentTimestampDraft = - CustomObjectDraft.ofUnversionedUpsert( - container, TIMESTAMP_GENERATOR_KEY, UUID.randomUUID().toString(), String.class); + final CustomObjectDraft currentTimestampDraft = + CustomObjectDraftBuilder.of() + .container(container) + .key(TIMESTAMP_GENERATOR_KEY) + .value(UUID.randomUUID().toString()) + .build(); - return createCustomObject(currentTimestampDraft).thenApply(ResourceView::getLastModifiedAt); - } - - @Nonnull - private CompletionStage> createCustomObject( - @Nonnull final CustomObjectDraft customObjectDraft) { - return this.sphereClient.execute(CustomObjectUpsertCommand.of(customObjectDraft)); + return createCustomObject(currentTimestampDraft) + .thenApply(ApiHttpResponse::getBody) + .thenApply(CustomObject::getLastModifiedAt); } /** - * Queries for custom objects, on the CTP project defined by the {@code sphereClient}, which have - * a container: 'commercetools-project-sync.{@code runnerName}.{@code syncModuleName}' and key: - * {@code sourceProjectKey}. The method then returns the first custom object returned in the - * result set if there is, wrapped in an {@link Optional} as a result of a {@link - * CompletionStage}. It will be, at most, one custom object since the key is unique per custom - * object container as per CTP documentation. + * Helper to create a custom object of {@param customObjectDraft} on the CTP project defined by + * the {@code ctpClient}. * - * @param sourceProjectKey the source project from which the data is coming. - * @param syncModuleName the name of the resource being synced. E.g. productSync, categorySync, - * etc.. - * @param runnerName the name of this specific running sync instance defined by the user. - * @return the first custom object returned in the result set if there is, wrapped in an {@link - * Optional} as a result of a {@link CompletionStage}. It will be, at most, one custom object - * since the key is unique per custom object container as per CTP documentation. + * @param customObjectDraft draft of custom object to create + * @return a {@link CompletableFuture} of {@link ApiHttpResponse} with the created custom object + * resource. */ + @Nonnull + private CompletableFuture> createCustomObject( + @Nonnull final CustomObjectDraft customObjectDraft) { + return this.ctpClient.customObjects().post(customObjectDraft).execute(); + } + @Nonnull @Override - public CompletionStage>> getLastSyncCustomObject( + public CompletableFuture> getLastSyncCustomObject( @Nonnull final String sourceProjectKey, @Nonnull final String syncModuleName, @Nullable final String runnerName) { - final QueryPredicate> queryPredicate = - QueryPredicate.of( - format( - "container=\"%s\" AND key=\"%s\"", - buildLastSyncTimestampContainerName(syncModuleName, runnerName), sourceProjectKey)); - - return this.sphereClient - .execute(CustomObjectQuery.of(LastSyncCustomObject.class).plusPredicates(queryPredicate)) - .thenApply(PagedQueryResult::getResults) - .thenApply(Collection::stream) - .thenApply(Stream::findFirst); + final String containerName = buildLastSyncTimestampContainerName(syncModuleName, runnerName); + + return this.ctpClient + .customObjects() + .withContainerAndKey(containerName, sourceProjectKey) + .get() + .execute() + .handle( + (customObjectApiHttpResponse, throwable) -> { + if (throwable != null) { + if (throwable.getCause() != null + && throwable.getCause().getClass().equals(NotFoundException.class)) { + return Optional.empty(); + } else { + throw new RuntimeException(throwable); + } + } else { + final CustomObject responseBody = customObjectApiHttpResponse.getBody(); + final ObjectMapper objectMapper = JsonUtils.getConfiguredObjectMapper(); + final LastSyncCustomObject lastSyncCustomObject = + responseBody == null + ? null + : objectMapper.convertValue( + responseBody.getValue(), LastSyncCustomObject.class); + return Optional.ofNullable(lastSyncCustomObject); + } + }); } - /** - * Creates (or updates an already existing) custom object, with the container: - * 'commercetools-project-sync.{@code runnerName}.{@code syncModuleName}' and key: {@code - * sourceProjectKey}, enriched with the information in the passed {@link LastSyncCustomObject} - * param. - * - * @param sourceProjectKey the source project from which the data is coming. - * @param syncModuleName the name of the resource being synced. E.g. productSync, categorySync, - * etc.. - * @param runnerName the name of this specific running sync instance defined by the user. - * @param lastSyncCustomObject contains information about the last sync instance. - * @return the first custom object returned in the result set if there is, wrapped in an {@link - * Optional} as a result of a {@link CompletionStage}. It will be, at most, one custom object - * since the key is unique per custom object container as per CTP documentation. - */ @Nonnull @Override - public CompletionStage> createLastSyncCustomObject( + public CompletableFuture> createLastSyncCustomObject( @Nonnull final String sourceProjectKey, @Nonnull final String syncModuleName, @Nullable final String runnerName, @Nonnull final LastSyncCustomObject lastSyncCustomObject) { - final CustomObjectDraft lastSyncCustomObjectDraft = - CustomObjectDraft.ofUnversionedUpsert( - buildLastSyncTimestampContainerName(syncModuleName, runnerName), - sourceProjectKey, - lastSyncCustomObject, - LastSyncCustomObject.class); + final CustomObjectDraft lastSyncCustomObjectDraft = + CustomObjectDraftBuilder.of() + .container(buildLastSyncTimestampContainerName(syncModuleName, runnerName)) + .key(sourceProjectKey) + .value(lastSyncCustomObject) + .build(); return createCustomObject(lastSyncCustomObjectDraft); } diff --git a/src/main/java/com/commercetools/project/sync/shoppinglist/ShoppingListSyncer.java b/src/main/java/com/commercetools/project/sync/shoppinglist/ShoppingListSyncer.java index eac772fa..0523a093 100644 --- a/src/main/java/com/commercetools/project/sync/shoppinglist/ShoppingListSyncer.java +++ b/src/main/java/com/commercetools/project/sync/shoppinglist/ShoppingListSyncer.java @@ -3,9 +3,15 @@ import static com.commercetools.project.sync.util.SyncUtils.IDENTIFIER_NOT_PRESENT; import static com.commercetools.project.sync.util.SyncUtils.logErrorCallback; import static com.commercetools.project.sync.util.SyncUtils.logWarningCallback; -import static com.commercetools.sync.shoppinglists.utils.ShoppingListReferenceResolutionUtils.buildShoppingListQuery; import static com.commercetools.sync.shoppinglists.utils.ShoppingListTransformUtils.toShoppingListDrafts; +import com.commercetools.api.client.ByProjectKeyShoppingListsGet; +import com.commercetools.api.client.ProjectApiRoot; +import com.commercetools.api.models.shopping_list.ShoppingList; +import com.commercetools.api.models.shopping_list.ShoppingListDraft; +import com.commercetools.api.models.shopping_list.ShoppingListPagedQueryResponse; +import com.commercetools.api.models.shopping_list.ShoppingListUpdateAction; +import com.commercetools.api.predicates.query.shopping_list.ShoppingListQueryBuilderDsl; import com.commercetools.project.sync.Syncer; import com.commercetools.project.sync.service.CustomObjectService; import com.commercetools.project.sync.service.impl.CustomObjectServiceImpl; @@ -16,11 +22,6 @@ import com.commercetools.sync.shoppinglists.ShoppingListSyncOptions; import com.commercetools.sync.shoppinglists.ShoppingListSyncOptionsBuilder; import com.commercetools.sync.shoppinglists.helpers.ShoppingListSyncStatistics; -import io.sphere.sdk.client.SphereClient; -import io.sphere.sdk.commands.UpdateAction; -import io.sphere.sdk.shoppinglists.ShoppingList; -import io.sphere.sdk.shoppinglists.ShoppingListDraft; -import io.sphere.sdk.shoppinglists.queries.ShoppingListQuery; import java.time.Clock; import java.util.List; import java.util.Optional; @@ -32,35 +33,36 @@ public final class ShoppingListSyncer extends Syncer< ShoppingList, - ShoppingList, + ShoppingListUpdateAction, ShoppingListDraft, - ShoppingList, + ShoppingListQueryBuilderDsl, ShoppingListSyncStatistics, ShoppingListSyncOptions, - ShoppingListQuery, + ByProjectKeyShoppingListsGet, + ShoppingListPagedQueryResponse, ShoppingListSync> { private static final Logger LOGGER = LoggerFactory.getLogger(ShoppingListSyncer.class); private ShoppingListSyncer( @Nonnull final ShoppingListSync sync, - @Nonnull final SphereClient sourceClient, - @Nonnull final SphereClient targetClient, + @Nonnull final ProjectApiRoot sourceClient, + @Nonnull final ProjectApiRoot targetClient, @Nonnull final CustomObjectService customObjectService, @Nonnull final Clock clock) { super(sync, sourceClient, targetClient, customObjectService, clock); } public static ShoppingListSyncer of( - @Nonnull final SphereClient sourceClient, - @Nonnull final SphereClient targetClient, + @Nonnull final ProjectApiRoot sourceClient, + @Nonnull final ProjectApiRoot targetClient, @Nonnull final Clock clock) { final QuadConsumer< SyncException, Optional, Optional, - List>> + List> logErrorCallback = (exception, newResourceDraft, oldResource, updateActions) -> logErrorCallback( @@ -101,8 +103,8 @@ protected CompletionStage> transform(@Nonnull List { private static final Logger LOGGER = LoggerFactory.getLogger(StateSyncer.class); private StateSyncer( @Nonnull StateSync sync, - @Nonnull SphereClient sourceClient, - @Nonnull SphereClient targetClient, + @Nonnull ProjectApiRoot sourceClient, + @Nonnull ProjectApiRoot targetClient, @Nonnull CustomObjectService customObjectService, @Nonnull Clock clock) { super(sync, sourceClient, targetClient, customObjectService, clock); } public static StateSyncer of( - @Nonnull final SphereClient sourceClient, - @Nonnull final SphereClient targetClient, + @Nonnull final ProjectApiRoot sourceClient, + @Nonnull final ProjectApiRoot targetClient, @Nonnull final Clock clock) { final QuadConsumer< - SyncException, Optional, Optional, List>> + SyncException, Optional, Optional, List> logErrorCallback = (exception, newResourceDraft, oldResource, updateActions) -> logErrorCallback(LOGGER, "state", exception, oldResource, updateActions); @@ -74,13 +78,23 @@ public static StateSyncer of( @Nonnull @Override protected CompletionStage> transform(@Nonnull List states) { - return toStateDrafts(getSourceClient(), referenceIdToKeyCache, states); + return toStateDrafts(getSourceClient(), referenceIdToKeyCache, states) + .handle( + (stateDrafts, throwable) -> { + if (throwable != null) { + if (LOGGER.isWarnEnabled()) { + LOGGER.warn(throwable.getMessage(), getCompletionExceptionCause(throwable)); + } + return List.of(); + } + return stateDrafts; + }); } @Nonnull @Override - protected StateQuery getQuery() { - return StateQuery.of(); + protected ByProjectKeyStatesGet getQuery() { + return getSourceClient().states().get(); } @Nonnull diff --git a/src/main/java/com/commercetools/project/sync/taxcategory/TaxCategorySyncer.java b/src/main/java/com/commercetools/project/sync/taxcategory/TaxCategorySyncer.java index 6dd8073c..f149b9e2 100644 --- a/src/main/java/com/commercetools/project/sync/taxcategory/TaxCategorySyncer.java +++ b/src/main/java/com/commercetools/project/sync/taxcategory/TaxCategorySyncer.java @@ -3,6 +3,17 @@ import static com.commercetools.project.sync.util.SyncUtils.logErrorCallback; import static com.commercetools.project.sync.util.SyncUtils.logWarningCallback; +import com.commercetools.api.client.ByProjectKeyTaxCategoriesGet; +import com.commercetools.api.client.ProjectApiRoot; +import com.commercetools.api.models.tax_category.TaxCategory; +import com.commercetools.api.models.tax_category.TaxCategoryDraft; +import com.commercetools.api.models.tax_category.TaxCategoryDraftBuilder; +import com.commercetools.api.models.tax_category.TaxCategoryPagedQueryResponse; +import com.commercetools.api.models.tax_category.TaxCategoryUpdateAction; +import com.commercetools.api.models.tax_category.TaxRate; +import com.commercetools.api.models.tax_category.TaxRateDraft; +import com.commercetools.api.models.tax_category.TaxRateDraftBuilder; +import com.commercetools.api.predicates.query.tax_category.TaxCategoryQueryBuilderDsl; import com.commercetools.project.sync.Syncer; import com.commercetools.project.sync.service.CustomObjectService; import com.commercetools.project.sync.service.impl.CustomObjectServiceImpl; @@ -13,15 +24,6 @@ import com.commercetools.sync.taxcategories.TaxCategorySyncOptions; import com.commercetools.sync.taxcategories.TaxCategorySyncOptionsBuilder; import com.commercetools.sync.taxcategories.helpers.TaxCategorySyncStatistics; -import io.sphere.sdk.client.SphereClient; -import io.sphere.sdk.commands.UpdateAction; -import io.sphere.sdk.taxcategories.TaxCategory; -import io.sphere.sdk.taxcategories.TaxCategoryDraft; -import io.sphere.sdk.taxcategories.TaxCategoryDraftBuilder; -import io.sphere.sdk.taxcategories.TaxRate; -import io.sphere.sdk.taxcategories.TaxRateDraft; -import io.sphere.sdk.taxcategories.TaxRateDraftBuilder; -import io.sphere.sdk.taxcategories.queries.TaxCategoryQuery; import java.time.Clock; import java.util.List; import java.util.Optional; @@ -35,12 +37,13 @@ public final class TaxCategorySyncer extends Syncer< TaxCategory, - TaxCategory, + TaxCategoryUpdateAction, TaxCategoryDraft, - TaxCategory, + TaxCategoryQueryBuilderDsl, TaxCategorySyncStatistics, TaxCategorySyncOptions, - TaxCategoryQuery, + ByProjectKeyTaxCategoriesGet, + TaxCategoryPagedQueryResponse, TaxCategorySync> { private static final Logger LOGGER = LoggerFactory.getLogger(TaxCategorySyncer.class); @@ -48,8 +51,8 @@ public final class TaxCategorySyncer /** Instantiates a {@link Syncer} instance. */ private TaxCategorySyncer( @Nonnull final TaxCategorySync taxCategorySync, - @Nonnull final SphereClient sourceClient, - @Nonnull final SphereClient targetClient, + @Nonnull final ProjectApiRoot sourceClient, + @Nonnull final ProjectApiRoot targetClient, @Nonnull final CustomObjectService customObjectService, @Nonnull final Clock clock) { super(taxCategorySync, sourceClient, targetClient, customObjectService, clock); @@ -57,14 +60,14 @@ private TaxCategorySyncer( @Nonnull public static TaxCategorySyncer of( - @Nonnull final SphereClient sourceClient, - @Nonnull final SphereClient targetClient, + @Nonnull final ProjectApiRoot sourceClient, + @Nonnull final ProjectApiRoot targetClient, @Nonnull final Clock clock) { final QuadConsumer< SyncException, Optional, Optional, - List>> + List> logErrorCallback = (exception, newResourceDraft, oldResource, updateActions) -> logErrorCallback(LOGGER, "tax category", exception, oldResource, updateActions); @@ -97,9 +100,11 @@ protected CompletionStage> transform( @Nonnull private static TaxCategoryDraft convertTaxCategoryToTaxCategoryDraft( @Nonnull final TaxCategory taxCategory) { - List taxRateDrafts = convertTaxRateToTaxRateDraft(taxCategory.getTaxRates()); - return TaxCategoryDraftBuilder.of( - taxCategory.getName(), taxRateDrafts, taxCategory.getDescription()) + List taxRateDrafts = convertTaxRateToTaxRateDraft(taxCategory.getRates()); + return TaxCategoryDraftBuilder.of() + .name(taxCategory.getName()) + .rates(taxRateDrafts) + .description(taxCategory.getDescription()) .key(taxCategory.getKey()) .build(); } @@ -109,14 +114,24 @@ private static List convertTaxRateToTaxRateDraft( @Nonnull final List taxRates) { return taxRates.stream() - .map(taxRate -> TaxRateDraftBuilder.of(taxRate).build()) + .map( + taxRate -> + TaxRateDraftBuilder.of() + .name(taxRate.getName()) + .country(taxRate.getCountry()) + .state(taxRate.getState()) + .includedInPrice(taxRate.getIncludedInPrice()) + .amount(taxRate.getAmount()) + .key(taxRate.getKey()) + .subRates(taxRate.getSubRates()) + .build()) .collect(Collectors.toList()); } @Nonnull @Override - protected TaxCategoryQuery getQuery() { - return TaxCategoryQuery.of(); + protected ByProjectKeyTaxCategoriesGet getQuery() { + return getSourceClient().taxCategories().get(); } @Nonnull diff --git a/src/main/java/com/commercetools/project/sync/type/TypeSyncer.java b/src/main/java/com/commercetools/project/sync/type/TypeSyncer.java index a1f61268..4e633c56 100644 --- a/src/main/java/com/commercetools/project/sync/type/TypeSyncer.java +++ b/src/main/java/com/commercetools/project/sync/type/TypeSyncer.java @@ -1,8 +1,17 @@ package com.commercetools.project.sync.type; +import static com.commercetools.project.sync.util.SyncUtils.getCompletionExceptionCause; import static com.commercetools.project.sync.util.SyncUtils.logErrorCallback; import static com.commercetools.project.sync.util.SyncUtils.logWarningCallback; +import com.commercetools.api.client.ByProjectKeyTypesGet; +import com.commercetools.api.client.ProjectApiRoot; +import com.commercetools.api.models.type.Type; +import com.commercetools.api.models.type.TypeDraft; +import com.commercetools.api.models.type.TypeDraftBuilder; +import com.commercetools.api.models.type.TypePagedQueryResponse; +import com.commercetools.api.models.type.TypeUpdateAction; +import com.commercetools.api.predicates.query.type.TypeQueryBuilderDsl; import com.commercetools.project.sync.Syncer; import com.commercetools.project.sync.service.CustomObjectService; import com.commercetools.project.sync.service.impl.CustomObjectServiceImpl; @@ -13,12 +22,6 @@ import com.commercetools.sync.types.TypeSyncOptions; import com.commercetools.sync.types.TypeSyncOptionsBuilder; import com.commercetools.sync.types.helpers.TypeSyncStatistics; -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.queries.TypeQuery; import java.time.Clock; import java.util.List; import java.util.Optional; @@ -26,20 +29,31 @@ import java.util.concurrent.CompletionStage; import java.util.stream.Collectors; import javax.annotation.Nonnull; +import javax.annotation.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +// This class compiles but not tested yet +// TODO: Test class and adjust logic if needed public final class TypeSyncer extends Syncer< - Type, Type, TypeDraft, Type, TypeSyncStatistics, TypeSyncOptions, TypeQuery, TypeSync> { + Type, + TypeUpdateAction, + TypeDraft, + TypeQueryBuilderDsl, + TypeSyncStatistics, + TypeSyncOptions, + ByProjectKeyTypesGet, + TypePagedQueryResponse, + TypeSync> { private static final Logger LOGGER = LoggerFactory.getLogger(TypeSyncer.class); /** Instantiates a {@link Syncer} instance. */ private TypeSyncer( @Nonnull final TypeSync typeSync, - @Nonnull final SphereClient sourceClient, - @Nonnull final SphereClient targetClient, + @Nonnull final ProjectApiRoot sourceClient, + @Nonnull final ProjectApiRoot targetClient, @Nonnull final CustomObjectService customObjectService, @Nonnull final Clock clock) { super(typeSync, sourceClient, targetClient, customObjectService, clock); @@ -47,11 +61,11 @@ private TypeSyncer( @Nonnull public static TypeSyncer of( - @Nonnull final SphereClient sourceClient, - @Nonnull final SphereClient targetClient, + @Nonnull final ProjectApiRoot sourceClient, + @Nonnull final ProjectApiRoot targetClient, @Nonnull final Clock clock) { - final QuadConsumer, Optional, List>> + final QuadConsumer, Optional, List> logErrorCallback = (exception, newResourceDraft, oldResource, updateActions) -> logErrorCallback(LOGGER, "type", exception, oldResource, updateActions); @@ -73,8 +87,8 @@ public static TypeSyncer of( @Nonnull @Override - protected TypeQuery getQuery() { - return TypeQuery.of(); + protected ByProjectKeyTypesGet getQuery() { + return getSourceClient().types().get(); } @Nonnull @@ -86,13 +100,26 @@ protected Logger getLoggerInstance() { @Nonnull @Override protected CompletionStage> transform(@Nonnull final List page) { - return CompletableFuture.completedFuture( - page.stream().map(TypeSyncer::typeToDraft).collect(Collectors.toList())); + return CompletableFuture.supplyAsync( + () -> page.stream().map(TypeSyncer::typeToDraft).collect(Collectors.toList())) + .handle( + ((typeDrafts, throwable) -> { + if (throwable != null) { + if (LOGGER.isWarnEnabled()) { + LOGGER.warn(throwable.getMessage(), getCompletionExceptionCause(throwable)); + } + return List.of(); + } + return typeDrafts; + })); } - @Nonnull + @Nullable private static TypeDraft typeToDraft(@Nonnull final Type type) { - return TypeDraftBuilder.of(type.getKey(), type.getName(), type.getResourceTypeIds()) + return TypeDraftBuilder.of() + .key(type.getKey()) + .name(type.getName()) + .resourceTypeIds(type.getResourceTypeIds()) .description(type.getDescription()) .fieldDefinitions(type.getFieldDefinitions()) .build(); diff --git a/src/main/java/com/commercetools/project/sync/util/CtpClientUtils.java b/src/main/java/com/commercetools/project/sync/util/CtpClientUtils.java new file mode 100644 index 00000000..fc66a9bf --- /dev/null +++ b/src/main/java/com/commercetools/project/sync/util/CtpClientUtils.java @@ -0,0 +1,170 @@ +package com.commercetools.project.sync.util; + +import static java.lang.String.format; + +import com.commercetools.api.client.ProjectApiRoot; +import com.commercetools.api.defaultconfig.ApiRootBuilder; +import com.commercetools.api.defaultconfig.ServiceRegion; +import com.commercetools.http.okhttp4.CtOkHttp4Client; +import io.vrap.rmf.base.client.oauth2.ClientCredentials; +import java.io.InputStream; +import java.util.Arrays; +import java.util.InvalidPropertiesFormatException; +import java.util.Properties; +import javax.annotation.Nonnull; + +public final class CtpClientUtils { + private static final String CTP_CREDENTIALS_PROPERTIES = "ctp.credentials.properties"; + public static final String PROPERTIES_KEY_API_URL_SUFFIX = "apiUrl"; + public static final String PROPERTIES_KEY_AUTH_URL_SUFFIX = "authUrl"; + public static final String PROPERTIES_KEY_PROJECT_KEY_SUFFIX = "projectKey"; + public static final String PROPERTIES_KEY_CLIENT_ID_SUFFIX = "clientId"; + public static final String PROPERTIES_KEY_CLIENT_SECRET_SUFFIX = "clientSecret"; + public static final String PROPERTIES_KEY_SCOPES_SUFFIX = "scopes"; + + public static final ProjectApiRoot CTP_SOURCE_CLIENT = getCtpSourceClient(); + public static final ProjectApiRoot CTP_TARGET_CLIENT = getCtpTargetClient(); + + private static ProjectApiRoot getCtpSourceClient() { + return getCtpClient("source."); + } + + private static ProjectApiRoot getCtpTargetClient() { + return getCtpClient("target."); + } + + private static ProjectApiRoot getCtpClient(@Nonnull final String propertiesPrefix) { + try { + InputStream propStream = + CtpClientUtils.class.getClassLoader().getResourceAsStream(CTP_CREDENTIALS_PROPERTIES); + Properties properties = new Properties(); + if (propStream != null) { + properties.load(propStream); + } + if (properties.isEmpty()) { + properties = loadFromEnvVars(propertiesPrefix); + } + if (properties.isEmpty()) { + throw new InvalidPropertiesFormatException( + "Please provide CTP credentials for running project sync."); + } + + final String projectKey = + extract(properties, propertiesPrefix, PROPERTIES_KEY_PROJECT_KEY_SUFFIX); + final String clientId = + extract(properties, propertiesPrefix, PROPERTIES_KEY_CLIENT_ID_SUFFIX); + final String clientSecret = + extract(properties, propertiesPrefix, PROPERTIES_KEY_CLIENT_SECRET_SUFFIX); + final String apiUrl = + extract( + properties, + propertiesPrefix, + PROPERTIES_KEY_API_URL_SUFFIX, + ServiceRegion.GCP_EUROPE_WEST1.getApiUrl()); + + final String authUrl = + extract( + properties, + propertiesPrefix, + PROPERTIES_KEY_AUTH_URL_SUFFIX, + ServiceRegion.GCP_EUROPE_WEST1.getOAuthTokenUrl()); + final String scopes = + extract( + properties, + propertiesPrefix, + PROPERTIES_KEY_SCOPES_SUFFIX, + "manage_project:" + projectKey); + + final ClientCredentials credentials = + ClientCredentials.of() + .withClientId(clientId) + .withClientSecret(clientSecret) + .withScopes(scopes) + .build(); + + return createCtpClient(authUrl, apiUrl, credentials, projectKey); + } catch (Exception exception) { + throw new IllegalStateException( + format( + "IT properties file \"%s\" found, but CTP properties" + + " for prefix \"%s\" can't be read", + CTP_CREDENTIALS_PROPERTIES, propertiesPrefix), + exception); + } + } + + private static ProjectApiRoot createCtpClient( + @Nonnull String authUrl, + @Nonnull String apiUrl, + @Nonnull ClientCredentials credentials, + @Nonnull String projectKey) { + return ApiRootBuilder.of(new CtOkHttp4Client(200, 200)) + .defaultClient(credentials, authUrl, apiUrl) + .withRetryMiddleware(5, Arrays.asList(500, 502, 503, 504)) + .build(projectKey); + } + + private static Properties loadFromEnvVars(String propertiesPrefix) { + String projectKeyKey = propertiesPrefix.toUpperCase().replace(".", "_") + "PROJECT_KEY"; + String projectKey = System.getenv(projectKeyKey); + String clientIdKey = propertiesPrefix.toUpperCase().replace(".", "_") + "CLIENT_ID"; + String clientId = System.getenv(clientIdKey); + String clientSecretKey = propertiesPrefix.toUpperCase().replace(".", "_") + "CLIENT_SECRET"; + String clientSecret = System.getenv(clientSecretKey); + Properties properties = new Properties(); + properties.put(propertiesPrefix + PROPERTIES_KEY_PROJECT_KEY_SUFFIX, projectKey); + properties.put(propertiesPrefix + PROPERTIES_KEY_CLIENT_ID_SUFFIX, clientId); + properties.put(propertiesPrefix + PROPERTIES_KEY_CLIENT_SECRET_SUFFIX, clientSecret); + return properties; + } + + private static String extract( + final Properties properties, + final String prefix, + final String suffix, + final String defaultValue) { + return properties.getProperty(buildPropKey(prefix, suffix), defaultValue); + } + + private static String extract( + final Properties properties, final String prefix, final String suffix) { + final String mapKey = buildPropKey(prefix, suffix); + return properties + .computeIfAbsent(mapKey, key -> throwPropertiesException(prefix, mapKey)) + .toString(); + } + + private static String buildPropKey(final String prefix, final String suffix) { + return prefix + suffix; + } + + private static String throwPropertiesException(final String prefix, final String missingKey) { + throw new IllegalArgumentException( + "Missing property value '" + + missingKey + + "'.\n" + + "Usage:\n" + + "" + + buildPropKey(prefix, PROPERTIES_KEY_PROJECT_KEY_SUFFIX) + + "=YOUR project key\n" + + "" + + buildPropKey(prefix, PROPERTIES_KEY_CLIENT_ID_SUFFIX) + + "=YOUR client id\n" + + "" + + buildPropKey(prefix, PROPERTIES_KEY_CLIENT_SECRET_SUFFIX) + + "=YOUR client secret\n" + + "#optional:\n" + + "" + + buildPropKey(prefix, PROPERTIES_KEY_API_URL_SUFFIX) + + "=https://api.europe-west1.gcp.commercetools.com\n" + + "" + + buildPropKey(prefix, PROPERTIES_KEY_AUTH_URL_SUFFIX) + + "=https://auth.europe-west1.gcp.commercetools.com\n" + + "" + + buildPropKey(prefix, PROPERTIES_KEY_SCOPES_SUFFIX) + + "=manage_project" + + "#don't use quotes for the property values\n"); + } + + private CtpClientUtils() {} +} diff --git a/src/main/java/com/commercetools/project/sync/util/ProjectSyncSolutionInfo.java b/src/main/java/com/commercetools/project/sync/util/ProjectSyncSolutionInfo.java index ae664fb3..bfffcaa2 100644 --- a/src/main/java/com/commercetools/project/sync/util/ProjectSyncSolutionInfo.java +++ b/src/main/java/com/commercetools/project/sync/util/ProjectSyncSolutionInfo.java @@ -1,6 +1,6 @@ package com.commercetools.project.sync.util; -import io.sphere.sdk.client.SolutionInfo; +import io.vrap.rmf.base.client.SolutionInfo; public final class ProjectSyncSolutionInfo extends SolutionInfo { private static final String LIB_NAME = "commercetools-project-sync"; diff --git a/src/main/java/com/commercetools/project/sync/util/SphereClientUtils.java b/src/main/java/com/commercetools/project/sync/util/SphereClientUtils.java deleted file mode 100644 index 220d8284..00000000 --- a/src/main/java/com/commercetools/project/sync/util/SphereClientUtils.java +++ /dev/null @@ -1,52 +0,0 @@ -package com.commercetools.project.sync.util; - -import static java.lang.String.format; - -import com.commercetools.sync.commons.utils.ClientConfigurationUtils; -import io.sphere.sdk.client.SphereClient; -import io.sphere.sdk.client.SphereClientConfig; -import java.io.InputStream; -import java.util.Properties; -import javax.annotation.Nonnull; - -public final class SphereClientUtils { - private static final String CTP_CREDENTIALS_PROPERTIES = "ctp.credentials.properties"; - public static final SphereClientConfig CTP_SOURCE_CLIENT_CONFIG = getCtpSourceClientConfig(); - public static final SphereClientConfig CTP_TARGET_CLIENT_CONFIG = getCtpTargetClientConfig(); - public static final SphereClient CTP_SOURCE_CLIENT = - ClientConfigurationUtils.createClient(CTP_SOURCE_CLIENT_CONFIG); - public static final SphereClient CTP_TARGET_CLIENT = - ClientConfigurationUtils.createClient(CTP_TARGET_CLIENT_CONFIG); - - private static SphereClientConfig getCtpSourceClientConfig() { - return getCtpClientConfig("source.", "SOURCE"); - } - - private static SphereClientConfig getCtpTargetClientConfig() { - return getCtpClientConfig("target.", "TARGET"); - } - - private static SphereClientConfig getCtpClientConfig( - @Nonnull final String propertiesPrefix, @Nonnull final String envVarPrefix) { - try { - final InputStream propStream = - SphereClientUtils.class.getClassLoader().getResourceAsStream(CTP_CREDENTIALS_PROPERTIES); - if (propStream != null) { - final Properties ctpCredsProperties = new Properties(); - ctpCredsProperties.load(propStream); - return SphereClientConfig.ofProperties(ctpCredsProperties, propertiesPrefix); - } - } catch (Exception exception) { - throw new IllegalStateException( - format( - "CTP credentials file \"%s\" found, but CTP properties" - + " for prefix \"%s\" can't be read", - CTP_CREDENTIALS_PROPERTIES, propertiesPrefix), - exception); - } - - return SphereClientConfig.ofEnvironmentVariables(envVarPrefix); - } - - private SphereClientUtils() {} -} diff --git a/src/main/java/com/commercetools/project/sync/util/SyncUtils.java b/src/main/java/com/commercetools/project/sync/util/SyncUtils.java index a4448fc1..bbcf918b 100644 --- a/src/main/java/com/commercetools/project/sync/util/SyncUtils.java +++ b/src/main/java/com/commercetools/project/sync/util/SyncUtils.java @@ -5,19 +5,21 @@ import static java.util.Optional.ofNullable; import static org.apache.commons.lang3.StringUtils.isBlank; +import com.commercetools.api.models.ResourceUpdateAction; +import com.commercetools.api.models.WithKey; import com.commercetools.sync.commons.BaseSync; import com.commercetools.sync.commons.exceptions.SyncException; -import io.sphere.sdk.commands.UpdateAction; -import io.sphere.sdk.models.ResourceView; -import io.sphere.sdk.models.WithKey; import java.util.List; import java.util.Optional; +import java.util.concurrent.CompletionException; import java.util.stream.Collectors; import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; +// This class compiles but not tested yet +// TODO: Test class and adjust logic if needed public final class SyncUtils { public static final String APPLICATION_DEFAULT_NAME = "commercetools-project-sync"; @@ -42,14 +44,14 @@ public static String getApplicationVersion() { return isBlank(implementationVersion) ? APPLICATION_DEFAULT_VERSION : implementationVersion; } - public static void logErrorCallback( + public static void logErrorCallback( @Nonnull final Logger logger, @Nonnull final String resourceName, @Nonnull final SyncException exception, @Nonnull final Optional resource, - @Nullable final List> updateActions) { + @Nullable final List updateActions) { String updateActionsString = "[]"; - if (updateActions != null) { + if (updateActions != null && !updateActions.isEmpty()) { updateActionsString = updateActions.stream().map(Object::toString).collect(Collectors.joining(",")); } @@ -64,14 +66,14 @@ public static void logErrorCallback( } } - public static void logErrorCallback( + public static void logErrorCallback( @Nonnull final Logger logger, @Nonnull final String resourceName, @Nonnull final SyncException exception, @Nonnull final String resourceIdentifier, - @Nullable final List> updateActions) { + @Nullable final List updateActions) { String updateActionsString = "[]"; - if (updateActions != null) { + if (updateActions != null && !updateActions.isEmpty()) { updateActionsString = updateActions.stream().map(Object::toString).collect(Collectors.joining(",")); } @@ -111,6 +113,14 @@ public static void logWarningCallback( } } + @Nonnull + public static Throwable getCompletionExceptionCause(@Nonnull final Throwable exception) { + if (exception instanceof CompletionException) { + return getCompletionExceptionCause(exception.getCause()); + } + return exception; + } + @Nonnull public static String buildLastSyncTimestampContainerName( @Nonnull final String syncModuleName, @Nullable final String runnerName) { diff --git a/src/main/resources/META-INF/services/io.sphere.sdk.client.SolutionInfo b/src/main/resources/META-INF/services/io.vrap.rmf.base.client.SolutionInfo similarity index 100% rename from src/main/resources/META-INF/services/io.sphere.sdk.client.SolutionInfo rename to src/main/resources/META-INF/services/io.vrap.rmf.base.client.SolutionInfo diff --git a/src/main/resources/ctp.credentials.properties.skeleton b/src/main/resources/ctp.credentials.properties.skeleton index 5f9dbdc9..6e426f69 100644 --- a/src/main/resources/ctp.credentials.properties.skeleton +++ b/src/main/resources/ctp.credentials.properties.skeleton @@ -1,5 +1,5 @@ # rename to ctp.credentials.properties and fill the values to run the job locally without env variables -# see SphereClientUtils for more info +# see CtpClientUtils for more info source.projectKey=<> source.clientId=YOUR client id without quotes source.clientSecret=YOUR client secret without quotes diff --git a/src/test/java/com/commercetools/project/sync/CliRunnerTest.java b/src/test/java/com/commercetools/project/sync/CliRunnerTest.java index 2f447ced..020a7326 100644 --- a/src/test/java/com/commercetools/project/sync/CliRunnerTest.java +++ b/src/test/java/com/commercetools/project/sync/CliRunnerTest.java @@ -16,39 +16,34 @@ import static com.commercetools.project.sync.util.SyncUtils.APPLICATION_DEFAULT_VERSION; import static com.commercetools.project.sync.util.TestUtils.getMockedClock; import static com.commercetools.project.sync.util.TestUtils.stubClientsCustomObjectService; +import static com.commercetools.project.sync.util.TestUtils.withTestClient; import static java.lang.String.format; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import com.commercetools.api.client.ByProjectKeyProductProjectionsGet; +import com.commercetools.api.client.ProjectApiRoot; import com.commercetools.project.sync.exception.CliException; import com.google.common.base.Optional; -import io.sphere.sdk.cartdiscounts.queries.CartDiscountQuery; -import io.sphere.sdk.categories.queries.CategoryQuery; -import io.sphere.sdk.client.SphereClient; -import io.sphere.sdk.client.SphereClientConfig; -import io.sphere.sdk.customers.queries.CustomerQuery; -import io.sphere.sdk.customobjects.queries.CustomObjectQuery; -import io.sphere.sdk.inventory.queries.InventoryEntryQuery; -import io.sphere.sdk.products.queries.ProductProjectionQuery; -import io.sphere.sdk.producttypes.queries.ProductTypeQuery; -import io.sphere.sdk.queries.PagedQueryResult; -import io.sphere.sdk.shoppinglists.queries.ShoppingListQuery; -import io.sphere.sdk.states.queries.StateQuery; -import io.sphere.sdk.taxcategories.queries.TaxCategoryQuery; -import io.sphere.sdk.types.queries.TypeQuery; +import io.vrap.rmf.base.client.ApiHttpResponse; import java.io.ByteArrayOutputStream; import java.io.PrintStream; import java.io.UnsupportedEncodingException; +import java.nio.charset.StandardCharsets; import java.time.ZonedDateTime; import java.util.concurrent.CompletableFuture; import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -62,6 +57,8 @@ class CliRunnerTest { private static final TestLogger testLogger = TestLoggerFactory.getTestLogger(CliRunner.class); private static ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); private static PrintStream originalSystemOut; + private ProjectApiRoot sourceClient; + private ProjectApiRoot targetClient; @BeforeAll static void setupSuite() throws UnsupportedEncodingException { @@ -76,16 +73,35 @@ static void tearDownSuite() { } @BeforeEach + void setupTest() { + final ProjectApiRoot sourceClientWithEmtpyResults = + mockClientResourceGetRequestsWithEmptyResult("testProjectKey"); + sourceClient = spy(sourceClientWithEmtpyResults); + targetClient = mock(ProjectApiRoot.class); + when(targetClient.getProjectKey()).thenReturn("testTargetProjectKey"); + } + + @AfterEach void tearDownTest() { testLogger.clearAll(); + reset(sourceClient, targetClient); + } + + private ProjectApiRoot mockClientResourceGetRequestsWithEmptyResult(final String projectKey) { + return withTestClient( + projectKey, + (uri, method, encodedRequetsBody) -> { + final String responseString = "{\"results\":[]}"; + return CompletableFuture.completedFuture( + new ApiHttpResponse<>(200, null, responseString.getBytes(StandardCharsets.UTF_8))); + }); } @Test void run_WithEmptyArgumentList_ShouldFailAndLogError() { // preparation final SyncerFactory syncerFactory = - SyncerFactory.of( - () -> mock(SphereClient.class), () -> mock(SphereClient.class), getMockedClock()); + SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock()); // test CliRunner.of().run(new String[] {}, syncerFactory); @@ -113,8 +129,7 @@ void run_WithHelpAsLongArgument_ShouldPrintUsageHelpToStandardOut() throws UnsupportedEncodingException { // preparation final SyncerFactory syncerFactory = - SyncerFactory.of( - () -> mock(SphereClient.class), () -> mock(SphereClient.class), getMockedClock()); + SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock()); // test CliRunner.of().run(new String[] {"-help"}, syncerFactory); @@ -136,8 +151,7 @@ void run_WithHelpAsShortArgument_ShouldPrintUsageHelpToStandardOut() throws UnsupportedEncodingException { // preparation final SyncerFactory syncerFactory = - SyncerFactory.of( - () -> mock(SphereClient.class), () -> mock(SphereClient.class), getMockedClock()); + SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock()); // test CliRunner.of().run(new String[] {"-h"}, syncerFactory); @@ -150,8 +164,7 @@ void run_WithVersionAsShortArgument_ShouldPrintApplicationVersionToStandardOut() throws UnsupportedEncodingException { // preparation final SyncerFactory syncerFactory = - SyncerFactory.of( - () -> mock(SphereClient.class), () -> mock(SphereClient.class), getMockedClock()); + SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock()); // test CliRunner.of().run(new String[] {"-v"}, syncerFactory); @@ -164,8 +177,7 @@ void run_WithVersionAsLongArgument_ShouldPrintApplicationVersionToStandardOut() throws UnsupportedEncodingException { // preparation final SyncerFactory syncerFactory = - SyncerFactory.of( - () -> mock(SphereClient.class), () -> mock(SphereClient.class), getMockedClock()); + SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock()); // test CliRunner.of().run(new String[] {"--version"}, syncerFactory); @@ -177,8 +189,7 @@ void run_WithVersionAsLongArgument_ShouldPrintApplicationVersionToStandardOut() void run_WithSyncAsArgumentWithNoArgs_ShouldFailAndLogError() { // preparation final SyncerFactory syncerFactory = - SyncerFactory.of( - () -> mock(SphereClient.class), () -> mock(SphereClient.class), getMockedClock()); + SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock()); // test CliRunner.of().run(new String[] {"-s"}, syncerFactory); @@ -204,8 +215,7 @@ void run_WithSyncAsArgumentWithNoArgs_ShouldFailAndLogError() { void run_WithFullSyncAsFirstArgument_ShouldFailAndLogError() { // preparation final SyncerFactory syncerFactory = - SyncerFactory.of( - () -> mock(SphereClient.class), () -> mock(SphereClient.class), getMockedClock()); + SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock()); // test CliRunner.of().run(new String[] {"-f"}, syncerFactory); @@ -231,76 +241,55 @@ void run_WithFullSyncAsFirstArgument_ShouldFailAndLogError() { @Test void run_AsProductDeltaSync_ShouldBuildSyncerAndExecuteSync() { // preparation - final SphereClient sourceClient = mock(SphereClient.class); - when(sourceClient.getConfig()).thenReturn(SphereClientConfig.of("foo", "foo", "foo")); - - final SphereClient targetClient = mock(SphereClient.class); - when(targetClient.getConfig()).thenReturn(SphereClientConfig.of("bar", "bar", "bar")); - - when(sourceClient.execute(any(ProductProjectionQuery.class))) - .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); + stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); final SyncerFactory syncerFactory = spy(SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock())); - stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); - // test CliRunner.of().run(new String[] {"-s", "products"}, syncerFactory); // assertions verify(syncerFactory, times(1)).sync(new String[] {"products"}, null, false, false, null); - verify(sourceClient, times(1)).execute(any(ProductProjectionQuery.class)); + verify(sourceClient, times(1)).productProjections(); } @Test void run_AsProductFullSync_ShouldBuildSyncerAndExecuteSync() { // preparation - final SphereClient sourceClient = mock(SphereClient.class); - when(sourceClient.getConfig()).thenReturn(SphereClientConfig.of("foo", "foo", "foo")); - - final SphereClient targetClient = mock(SphereClient.class); - when(targetClient.getConfig()).thenReturn(SphereClientConfig.of("bar", "bar", "bar")); - - when(sourceClient.execute(any(ProductProjectionQuery.class))) - .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); + stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); final SyncerFactory syncerFactory = spy(SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock())); - stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); - // test CliRunner.of().run(new String[] {"-s", "products", "-f"}, syncerFactory); // assertions verify(syncerFactory, times(1)).sync(new String[] {"products"}, null, true, false, null); - verify(sourceClient, times(1)).execute(any(ProductProjectionQuery.class)); + verify(sourceClient, times(1)).productProjections(); } @Test void run_AsProductSyncWithCustomProductQueriesAndLimit_ShouldBuildSyncerAndExecuteQuerySuccessfully() { // preparation - final SphereClient sourceClient = mock(SphereClient.class); - when(sourceClient.getConfig()).thenReturn(SphereClientConfig.of("foo", "foo", "foo")); - - final SphereClient targetClient = mock(SphereClient.class); - when(targetClient.getConfig()).thenReturn(SphereClientConfig.of("bar", "bar", "bar")); - - when(sourceClient.execute(any(ProductProjectionQuery.class))) - .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); + stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); + final ByProjectKeyProductProjectionsGet getMock = mock(ByProjectKeyProductProjectionsGet.class); + when(getMock.addStaged(anyBoolean())).thenReturn(getMock); + when(getMock.withLimit(anyLong())).thenReturn(getMock); + when(getMock.withWhere(anyString())).thenReturn(getMock); + when(sourceClient.productProjections()).thenReturn(mock()); + when(sourceClient.productProjections().get()).thenReturn(getMock); final Long limit = 100L; final String customQuery = - "\"published=true AND masterData(masterVariant(attributes(name= \\\"abc\\\" AND value=123)))\""; + "published=true AND masterData(masterVariant(attributes(name=abc AND value=123)))"; final String productQueryParametersValue = - "{\"limit\": " + limit + ", \"where\": " + customQuery + "}"; + "{\"limit\": " + limit + ", \"where\": \"" + customQuery + "\"}"; final SyncerFactory syncerFactory = spy(SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock())); - - stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); // test CliRunner.of() .run( @@ -310,21 +299,22 @@ void run_AsProductFullSync_ShouldBuildSyncerAndExecuteSync() { syncerFactory); // assertions - verify(sourceClient, times(1)).execute(any(ProductProjectionQuery.class)); + verify(sourceClient.productProjections(), times(1)).get(); + verify(getMock, times(1)).withLimit(limit); + verify(getMock, times(1)).withWhere(customQuery); } @Test void run_AsProductSyncWithProductQueryParametersAndOnlyLimit_ShouldBuildSyncerAndExecuteQuerySuccessfully() { // preparation - final SphereClient sourceClient = mock(SphereClient.class); - when(sourceClient.getConfig()).thenReturn(SphereClientConfig.of("foo", "foo", "foo")); - - final SphereClient targetClient = mock(SphereClient.class); - when(targetClient.getConfig()).thenReturn(SphereClientConfig.of("bar", "bar", "bar")); - - when(sourceClient.execute(any(ProductProjectionQuery.class))) - .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); + stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); + final ByProjectKeyProductProjectionsGet getMock = mock(ByProjectKeyProductProjectionsGet.class); + when(getMock.addStaged(anyBoolean())).thenReturn(getMock); + when(getMock.withLimit(anyLong())).thenReturn(getMock); + when(getMock.withWhere(anyString())).thenReturn(getMock); + when(sourceClient.productProjections()).thenReturn(mock()); + when(sourceClient.productProjections().get()).thenReturn(getMock); final Long limit = 100L; final String productQueryParametersValue = "{\"limit\": " + limit + "}"; @@ -332,7 +322,6 @@ void run_AsProductFullSync_ShouldBuildSyncerAndExecuteSync() { final SyncerFactory syncerFactory = spy(SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock())); - stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); // test CliRunner.of() .run( @@ -342,30 +331,31 @@ void run_AsProductFullSync_ShouldBuildSyncerAndExecuteSync() { syncerFactory); // assertions - verify(sourceClient, times(1)).execute(any(ProductProjectionQuery.class)); + verify(sourceClient.productProjections(), times(1)).get(); + verify(getMock, times(1)).withLimit(limit); + verify(getMock, times(0)).withWhere(""); } @Test void run_AsProductSyncWithProductQueryParametersAndOnlyWhere_ShouldBuildSyncerAndExecuteQuerySuccessfully() { // preparation - final SphereClient sourceClient = mock(SphereClient.class); - when(sourceClient.getConfig()).thenReturn(SphereClientConfig.of("foo", "foo", "foo")); - - final SphereClient targetClient = mock(SphereClient.class); - when(targetClient.getConfig()).thenReturn(SphereClientConfig.of("bar", "bar", "bar")); + stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); - when(sourceClient.execute(any(ProductProjectionQuery.class))) - .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); + final ByProjectKeyProductProjectionsGet getMock = mock(ByProjectKeyProductProjectionsGet.class); + when(getMock.addStaged(anyBoolean())).thenReturn(getMock); + when(getMock.withLimit(anyLong())).thenReturn(getMock); + when(getMock.withWhere(anyString())).thenReturn(getMock); + when(sourceClient.productProjections()).thenReturn(mock()); + when(sourceClient.productProjections().get()).thenReturn(getMock); final String customQuery = - "\"published=true AND masterVariant(attributes(name= \\\"abc\\\" AND value=123))\""; - final String productQueryParametersValue = "{\"where\": " + customQuery + "}"; + "published=true AND masterVariant(attributes(name= \\\"abc\\\" AND value=123))"; + final String productQueryParametersValue = "{\"where\": \"" + customQuery + "\"}"; final SyncerFactory syncerFactory = spy(SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock())); - stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); // test CliRunner.of() .run( @@ -375,18 +365,15 @@ void run_AsProductFullSync_ShouldBuildSyncerAndExecuteSync() { syncerFactory); // assertions - verify(sourceClient, times(1)).execute(any(ProductProjectionQuery.class)); + verify(sourceClient.productProjections(), times(1)).get(); + verify(getMock, times(0)).withLimit(1L); + verify(getMock, times(1)) + .withWhere("published=true AND masterVariant(attributes(name= \"abc\" AND value=123))"); } @Test void run_WithWrongFormatProductQueryParametersArgument_ShouldThrowCLIException() { // preparation - final SphereClient sourceClient = mock(SphereClient.class); - when(sourceClient.getConfig()).thenReturn(SphereClientConfig.of("foo", "foo", "foo")); - - final SphereClient targetClient = mock(SphereClient.class); - when(targetClient.getConfig()).thenReturn(SphereClientConfig.of("bar", "bar", "bar")); - final Long limit = 100L; final String customQuery = "\"published=true AND masterVariant(attributes(name= \"abc\\\" AND value=123))\""; @@ -423,12 +410,6 @@ void run_WithWrongFormatProductQueryParametersArgument_ShouldThrowCLIException() @Test void run_WithInvalidLimitInProductQueryParametersArgument_ShouldThrowCLIException() { // preparation - final SphereClient sourceClient = mock(SphereClient.class); - when(sourceClient.getConfig()).thenReturn(SphereClientConfig.of("foo", "foo", "foo")); - - final SphereClient targetClient = mock(SphereClient.class); - when(targetClient.getConfig()).thenReturn(SphereClientConfig.of("bar", "bar", "bar")); - final Long limit = -100L; final String customQuery = "\"published=true AND masterVariant(attributes(name= \"abc\\\" AND value=123))\""; @@ -467,209 +448,129 @@ void run_WithInvalidLimitInProductQueryParametersArgument_ShouldThrowCLIExceptio @Test void run_AsTaxCategoryDeltaSync_ShouldBuildSyncerAndExecuteSync() { // preparation - final SphereClient sourceClient = mock(SphereClient.class); - when(sourceClient.getConfig()).thenReturn(SphereClientConfig.of("foo", "foo", "foo")); - - final SphereClient targetClient = mock(SphereClient.class); - when(targetClient.getConfig()).thenReturn(SphereClientConfig.of("bar", "bar", "bar")); - - when(sourceClient.execute(any(ProductProjectionQuery.class))) - .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - + stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); final SyncerFactory syncerFactory = spy(SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock())); - stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); - // test CliRunner.of().run(new String[] {"-s", "taxCategories"}, syncerFactory); // assertions verify(syncerFactory, times(1)).sync(new String[] {"taxCategories"}, null, false, false, null); - verify(sourceClient, times(1)).execute(any(TaxCategoryQuery.class)); + verify(sourceClient, times(1)).taxCategories(); } @Test void run_AsTaxCategoryFullSync_ShouldBuildSyncerAndExecuteSync() { // preparation - final SphereClient sourceClient = mock(SphereClient.class); - when(sourceClient.getConfig()).thenReturn(SphereClientConfig.of("foo", "foo", "foo")); - - final SphereClient targetClient = mock(SphereClient.class); - when(targetClient.getConfig()).thenReturn(SphereClientConfig.of("bar", "bar", "bar")); - - when(sourceClient.execute(any(TaxCategoryQuery.class))) - .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); + stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); final SyncerFactory syncerFactory = spy(SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock())); - stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); - // test CliRunner.of().run(new String[] {"-s", "taxCategories", "-f"}, syncerFactory); // assertions verify(syncerFactory, times(1)).sync(new String[] {"taxCategories"}, null, true, false, null); - verify(sourceClient, times(1)).execute(any(TaxCategoryQuery.class)); + verify(sourceClient, times(1)).taxCategories(); } @Test void run_AsCustomerDeltaSync_ShouldBuildSyncerAndExecuteSync() { // preparation - final SphereClient sourceClient = mock(SphereClient.class); - when(sourceClient.getConfig()).thenReturn(SphereClientConfig.of("foo", "foo", "foo")); - - final SphereClient targetClient = mock(SphereClient.class); - when(targetClient.getConfig()).thenReturn(SphereClientConfig.of("bar", "bar", "bar")); - + stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); final SyncerFactory syncerFactory = spy(SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock())); - stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); - // test CliRunner.of().run(new String[] {"-s", "customers"}, syncerFactory); // assertions verify(syncerFactory, times(1)).sync(new String[] {"customers"}, null, false, false, null); - verify(sourceClient, times(1)).execute(any(CustomerQuery.class)); + verify(sourceClient, times(1)).customers(); } @Test void run_AsCustomerFullSync_ShouldBuildSyncerAndExecuteSync() { // preparation - final SphereClient sourceClient = mock(SphereClient.class); - when(sourceClient.getConfig()).thenReturn(SphereClientConfig.of("foo", "foo", "foo")); - - final SphereClient targetClient = mock(SphereClient.class); - when(targetClient.getConfig()).thenReturn(SphereClientConfig.of("bar", "bar", "bar")); - - when(sourceClient.execute(any(CustomerQuery.class))) - .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - + stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); final SyncerFactory syncerFactory = spy(SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock())); - stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); - // test CliRunner.of().run(new String[] {"-s", "customers", "-f"}, syncerFactory); // assertions verify(syncerFactory, times(1)).sync(new String[] {"customers"}, null, true, false, null); - verify(sourceClient, times(1)).execute(any(CustomerQuery.class)); + verify(sourceClient, times(1)).customers(); } @Test void run_AsShoppingListDeltaSync_ShouldBuildSyncerAndExecuteSync() { // preparation - final SphereClient sourceClient = mock(SphereClient.class); - when(sourceClient.getConfig()).thenReturn(SphereClientConfig.of("foo", "foo", "foo")); - - final SphereClient targetClient = mock(SphereClient.class); - when(targetClient.getConfig()).thenReturn(SphereClientConfig.of("bar", "bar", "bar")); - + stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); final SyncerFactory syncerFactory = spy(SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock())); - stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); - // test CliRunner.of().run(new String[] {"-s", "shoppingLists"}, syncerFactory); // assertions verify(syncerFactory, times(1)).sync(new String[] {"shoppingLists"}, null, false, false, null); - verify(sourceClient, times(1)).execute(any(ShoppingListQuery.class)); + verify(sourceClient, times(1)).shoppingLists(); } @Test void run_AsShoppingListFullSync_ShouldBuildSyncerAndExecuteSync() { // preparation - final SphereClient sourceClient = mock(SphereClient.class); - when(sourceClient.getConfig()).thenReturn(SphereClientConfig.of("foo", "foo", "foo")); - - final SphereClient targetClient = mock(SphereClient.class); - when(targetClient.getConfig()).thenReturn(SphereClientConfig.of("bar", "bar", "bar")); - - when(sourceClient.execute(any(ShoppingListQuery.class))) - .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); + stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); final SyncerFactory syncerFactory = spy(SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock())); - stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); - // test CliRunner.of().run(new String[] {"-s", "shoppingLists", "-f"}, syncerFactory); // assertions verify(syncerFactory, times(1)).sync(new String[] {"shoppingLists"}, null, true, false, null); - verify(sourceClient, times(1)).execute(any(ShoppingListQuery.class)); + verify(sourceClient, times(1)).shoppingLists(); } @Test void run_AsCustomObjectDeltaSync_ShouldBuildSyncerAndExecuteSync() { // preparation - final SphereClient sourceClient = mock(SphereClient.class); - when(sourceClient.getConfig()).thenReturn(SphereClientConfig.of("foo", "foo", "foo")); - - final SphereClient targetClient = mock(SphereClient.class); - when(targetClient.getConfig()).thenReturn(SphereClientConfig.of("bar", "bar", "bar")); - - when(sourceClient.execute(any(CustomObjectQuery.class))) - .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); + stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); final SyncerFactory syncerFactory = spy(SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock())); - stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); - // test CliRunner.of().run(new String[] {"-s", "customObjects"}, syncerFactory); // assertions verify(syncerFactory, times(1)).sync(new String[] {"customObjects"}, null, false, false, null); - verify(sourceClient, times(1)).execute(any(CustomObjectQuery.class)); + verify(sourceClient, times(1)).customObjects(); } @Test void run_AsCustomObjectFullSync_ShouldBuildSyncerAndExecuteSync() { // preparation - final SphereClient sourceClient = mock(SphereClient.class); - when(sourceClient.getConfig()).thenReturn(SphereClientConfig.of("foo", "foo", "foo")); - - final SphereClient targetClient = mock(SphereClient.class); - when(targetClient.getConfig()).thenReturn(SphereClientConfig.of("bar", "bar", "bar")); - - when(sourceClient.execute(any(CustomObjectQuery.class))) - .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - + stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); final SyncerFactory syncerFactory = spy(SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock())); - stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); - // test CliRunner.of().run(new String[] {"-s", "customObjects", "-f"}, syncerFactory); // assertions verify(syncerFactory, times(1)).sync(new String[] {"customObjects"}, null, true, false, null); - verify(sourceClient, times(1)).execute(any(CustomObjectQuery.class)); + verify(sourceClient, times(1)).customObjects(); } @Test void run_AsCartDiscountFullSync_ShouldBuildSyncerAndExecuteSync() { // preparation - final SphereClient sourceClient = mock(SphereClient.class); - when(sourceClient.getConfig()).thenReturn(SphereClientConfig.of("foo", "foo", "foo")); - - final SphereClient targetClient = mock(SphereClient.class); - when(targetClient.getConfig()).thenReturn(SphereClientConfig.of("bar", "bar", "bar")); - - when(sourceClient.execute(any(CartDiscountQuery.class))) - .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - final SyncerFactory syncerFactory = spy(SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock())); @@ -680,102 +581,64 @@ void run_AsCartDiscountFullSync_ShouldBuildSyncerAndExecuteSync() { // assertions verify(syncerFactory, times(1)).sync(new String[] {"cartDiscounts"}, null, true, false, null); - verify(sourceClient, times(1)).execute(any(CartDiscountQuery.class)); + verify(sourceClient, times(1)).cartDiscounts(); } @Test void run_AsStateFullSync_ShouldBuildSyncerAndExecuteSync() { // preparation - final SphereClient sourceClient = mock(SphereClient.class); - when(sourceClient.getConfig()).thenReturn(SphereClientConfig.of("foo", "foo", "foo")); - - final SphereClient targetClient = mock(SphereClient.class); - when(targetClient.getConfig()).thenReturn(SphereClientConfig.of("bar", "bar", "bar")); - - when(sourceClient.execute(any(StateQuery.class))) - .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - + stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); final SyncerFactory syncerFactory = spy(SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock())); - stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); - // test CliRunner.of().run(new String[] {"-s", "states", "-f"}, syncerFactory); // assertions verify(syncerFactory, times(1)).sync(new String[] {"states"}, null, true, false, null); - verify(sourceClient, times(1)).execute(any(StateQuery.class)); + verify(sourceClient, times(1)).states(); } @Test void run_WithSyncAsLongArgument_ShouldBuildSyncerAndExecuteSync() { // preparation - final SphereClient sourceClient = mock(SphereClient.class); - when(sourceClient.getConfig()).thenReturn(SphereClientConfig.of("foo", "foo", "foo")); - - final SphereClient targetClient = mock(SphereClient.class); - when(targetClient.getConfig()).thenReturn(SphereClientConfig.of("bar", "bar", "bar")); - - when(sourceClient.execute(any(ProductProjectionQuery.class))) - .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - + stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); final SyncerFactory syncerFactory = spy(SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock())); - stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); - // test CliRunner.of().run(new String[] {"--sync", "products"}, syncerFactory); // assertions verify(syncerFactory, times(1)).sync(new String[] {"products"}, null, false, false, null); - verify(sourceClient, times(1)).execute(any(ProductProjectionQuery.class)); + verify(sourceClient, times(1)).productProjections(); } @Test void run_WithRunnerName_ShouldProcessSyncOption() { // preparation - final SphereClient sourceClient = mock(SphereClient.class); - when(sourceClient.getConfig()).thenReturn(SphereClientConfig.of("foo", "foo", "foo")); - - final SphereClient targetClient = mock(SphereClient.class); - when(targetClient.getConfig()).thenReturn(SphereClientConfig.of("bar", "bar", "bar")); - - when(sourceClient.execute(any(ProductProjectionQuery.class))) - .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); + stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); final SyncerFactory syncerFactory = spy(SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock())); - stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); - // test CliRunner.of().run(new String[] {"--sync", "products", "-r", "Runner123"}, syncerFactory); // assertions verify(syncerFactory, times(1)) .sync(new String[] {"products"}, "Runner123", false, false, null); - verify(sourceClient, times(1)).execute(any(ProductProjectionQuery.class)); + verify(sourceClient, times(1)).productProjections(); } @Test void run_WithRunnerNameLong_ShouldProcessSyncOption() { // preparation - final SphereClient sourceClient = mock(SphereClient.class); - when(sourceClient.getConfig()).thenReturn(SphereClientConfig.of("foo", "foo", "foo")); - - final SphereClient targetClient = mock(SphereClient.class); - when(targetClient.getConfig()).thenReturn(SphereClientConfig.of("bar", "bar", "bar")); - - when(sourceClient.execute(any(ProductProjectionQuery.class))) - .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); + stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); final SyncerFactory syncerFactory = spy(SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock())); - stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); - // test CliRunner.of() .run( @@ -784,7 +647,7 @@ void run_WithRunnerNameLong_ShouldProcessSyncOption() { // assertions verify(syncerFactory, times(1)).sync(new String[] {"products"}, "Runner123", true, false, null); - verify(sourceClient, times(1)).execute(any(ProductProjectionQuery.class)); + verify(sourceClient, times(1)).productProjections(); } @Test @@ -793,7 +656,9 @@ void run_WithUnknownArgument_ShouldPrintAndLogError() { final SyncerFactory syncerFactory = spy( SyncerFactory.of( - () -> mock(SphereClient.class), () -> mock(SphereClient.class), getMockedClock())); + () -> mock(ProjectApiRoot.class), + () -> mock(ProjectApiRoot.class), + getMockedClock())); // test CliRunner.of().run(new String[] {"-u"}, syncerFactory); @@ -808,7 +673,9 @@ void run_WithHelpAsArgument_ShouldPrintThreeOptionsWithDescriptionsToSystemOut() final SyncerFactory syncerFactory = spy( SyncerFactory.of( - () -> mock(SphereClient.class), () -> mock(SphereClient.class), getMockedClock())); + () -> mock(ProjectApiRoot.class), + () -> mock(ProjectApiRoot.class), + getMockedClock())); // test CliRunner.of().run(new String[] {"-h"}, syncerFactory); @@ -844,35 +711,6 @@ void run_WithHelpAsArgument_ShouldPrintThreeOptionsWithDescriptionsToSystemOut() @Test void run_WithSyncAsArgumentWithAllArg_ShouldExecuteAllSyncers() { // preparation - final SphereClient sourceClient = mock(SphereClient.class); - when(sourceClient.getConfig()).thenReturn(SphereClientConfig.of("foo", "foo", "foo")); - - final SphereClient targetClient = mock(SphereClient.class); - when(targetClient.getConfig()).thenReturn(SphereClientConfig.of("bar", "bar", "bar")); - - when(sourceClient.execute(any(ProductTypeQuery.class))) - .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - when(sourceClient.execute(any(TypeQuery.class))) - .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - when(sourceClient.execute(any(CategoryQuery.class))) - .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - when(sourceClient.execute(any(InventoryEntryQuery.class))) - .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - when(sourceClient.execute(any(ProductProjectionQuery.class))) - .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - when(sourceClient.execute(any(CartDiscountQuery.class))) - .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - when(sourceClient.execute(any(StateQuery.class))) - .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - when(sourceClient.execute(any(TaxCategoryQuery.class))) - .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - when(sourceClient.execute(any(CustomObjectQuery.class))) - .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - when(sourceClient.execute(any(CustomerQuery.class))) - .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - when(sourceClient.execute(any(ShoppingListQuery.class))) - .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); final SyncerFactory syncerFactory = @@ -883,51 +721,22 @@ void run_WithSyncAsArgumentWithAllArg_ShouldExecuteAllSyncers() { // assertions verify(syncerFactory, times(1)).sync(new String[] {"all"}, null, false, false, null); - verify(sourceClient, times(1)).execute(any(ProductTypeQuery.class)); - verify(sourceClient, times(1)).execute(any(TypeQuery.class)); - verify(sourceClient, times(1)).execute(any(TaxCategoryQuery.class)); - verify(sourceClient, times(1)).execute(any(CategoryQuery.class)); - verify(sourceClient, times(1)).execute(any(ProductProjectionQuery.class)); - verify(sourceClient, times(1)).execute(any(InventoryEntryQuery.class)); - verify(sourceClient, times(1)).execute(any(CartDiscountQuery.class)); - verify(sourceClient, times(1)).execute(any(StateQuery.class)); - verify(sourceClient, times(1)).execute(any(CustomObjectQuery.class)); - verify(sourceClient, times(1)).execute(any(CustomerQuery.class)); - verify(sourceClient, times(1)).execute(any(ShoppingListQuery.class)); + verify(sourceClient, times(1)).productTypes(); + verify(sourceClient, times(1)).types(); + verify(sourceClient, times(1)).taxCategories(); + verify(sourceClient, times(1)).categories(); + verify(sourceClient, times(1)).productProjections(); + verify(sourceClient, times(1)).inventory(); + verify(sourceClient, times(1)).cartDiscounts(); + verify(sourceClient, times(1)).states(); + verify(sourceClient, times(1)).customObjects(); + verify(sourceClient, times(1)).customers(); + verify(sourceClient, times(1)).shoppingLists(); } @Test void run_WithSyncAsArgumentWithAllArgWithRunnerName_ShouldExecuteAllSyncers() { // preparation - final SphereClient sourceClient = mock(SphereClient.class); - when(sourceClient.getConfig()).thenReturn(SphereClientConfig.of("foo", "foo", "foo")); - - final SphereClient targetClient = mock(SphereClient.class); - when(targetClient.getConfig()).thenReturn(SphereClientConfig.of("bar", "bar", "bar")); - - when(sourceClient.execute(any(ProductTypeQuery.class))) - .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - when(sourceClient.execute(any(TypeQuery.class))) - .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - when(sourceClient.execute(any(CategoryQuery.class))) - .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - when(sourceClient.execute(any(InventoryEntryQuery.class))) - .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - when(sourceClient.execute(any(ProductProjectionQuery.class))) - .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - when(sourceClient.execute(any(CartDiscountQuery.class))) - .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - when(sourceClient.execute(any(StateQuery.class))) - .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - when(sourceClient.execute(any(TaxCategoryQuery.class))) - .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - when(sourceClient.execute(any(CustomObjectQuery.class))) - .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - when(sourceClient.execute(any(CustomerQuery.class))) - .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - when(sourceClient.execute(any(ShoppingListQuery.class))) - .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); final SyncerFactory syncerFactory = @@ -938,51 +747,22 @@ void run_WithSyncAsArgumentWithAllArgWithRunnerName_ShouldExecuteAllSyncers() { // assertions verify(syncerFactory, times(1)).sync(new String[] {"all"}, "myRunner", false, false, null); - verify(sourceClient, times(1)).execute(any(ProductTypeQuery.class)); - verify(sourceClient, times(1)).execute(any(TypeQuery.class)); - verify(sourceClient, times(1)).execute(any(TaxCategoryQuery.class)); - verify(sourceClient, times(1)).execute(any(CategoryQuery.class)); - verify(sourceClient, times(1)).execute(any(ProductProjectionQuery.class)); - verify(sourceClient, times(1)).execute(any(InventoryEntryQuery.class)); - verify(sourceClient, times(1)).execute(any(CartDiscountQuery.class)); - verify(sourceClient, times(1)).execute(any(StateQuery.class)); - verify(sourceClient, times(1)).execute(any(CustomObjectQuery.class)); - verify(sourceClient, times(1)).execute(any(CustomerQuery.class)); - verify(sourceClient, times(1)).execute(any(ShoppingListQuery.class)); + verify(sourceClient, times(1)).productTypes(); + verify(sourceClient, times(1)).types(); + verify(sourceClient, times(1)).taxCategories(); + verify(sourceClient, times(1)).categories(); + verify(sourceClient, times(1)).productProjections(); + verify(sourceClient, times(1)).inventory(); + verify(sourceClient, times(1)).cartDiscounts(); + verify(sourceClient, times(1)).states(); + verify(sourceClient, times(1)).customObjects(); + verify(sourceClient, times(1)).customers(); + verify(sourceClient, times(1)).shoppingLists(); } @Test void run_WithSyncAsArgumentWithAllArg_ShouldExecuteAllSyncersInCorrectOrder() { // preparation - final SphereClient sourceClient = mock(SphereClient.class); - when(sourceClient.getConfig()).thenReturn(SphereClientConfig.of("foo", "foo", "foo")); - - final SphereClient targetClient = mock(SphereClient.class); - when(targetClient.getConfig()).thenReturn(SphereClientConfig.of("bar", "bar", "bar")); - - when(sourceClient.execute(any(ProductTypeQuery.class))) - .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - when(sourceClient.execute(any(TypeQuery.class))) - .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - when(sourceClient.execute(any(CategoryQuery.class))) - .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - when(sourceClient.execute(any(InventoryEntryQuery.class))) - .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - when(sourceClient.execute(any(ProductProjectionQuery.class))) - .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - when(sourceClient.execute(any(CartDiscountQuery.class))) - .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - when(sourceClient.execute(any(StateQuery.class))) - .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - when(sourceClient.execute(any(TaxCategoryQuery.class))) - .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - when(sourceClient.execute(any(CustomObjectQuery.class))) - .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - when(sourceClient.execute(any(CustomerQuery.class))) - .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - when(sourceClient.execute(any(ShoppingListQuery.class))) - .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); final SyncerFactory syncerFactory = @@ -999,34 +779,28 @@ void run_WithSyncAsArgumentWithAllArg_ShouldExecuteAllSyncersInCorrectOrder() { // Resources are grouped based on their references count. // Each group will run sequentially but the sync within the group runs in parallel. // So verifying the order of one resource in each group. - inOrder.verify(sourceClient).execute(any(ProductTypeQuery.class)); - verify(sourceClient, times(1)).execute(any(TypeQuery.class)); - verify(sourceClient, times(1)).execute(any(CustomObjectQuery.class)); - verify(sourceClient, times(1)).execute(any(StateQuery.class)); - verify(sourceClient, times(1)).execute(any(TaxCategoryQuery.class)); + inOrder.verify(sourceClient).productTypes(); + verify(sourceClient, times(1)).types(); + verify(sourceClient, times(1)).customObjects(); + verify(sourceClient, times(1)).states(); + verify(sourceClient, times(1)).taxCategories(); - inOrder.verify(sourceClient).execute(any(InventoryEntryQuery.class)); - verify(sourceClient, times(1)).execute(any(CategoryQuery.class)); - verify(sourceClient, times(1)).execute(any(CartDiscountQuery.class)); - verify(sourceClient, times(1)).execute(any(CustomerQuery.class)); + inOrder.verify(sourceClient).inventory(); + verify(sourceClient, times(1)).categories(); + verify(sourceClient, times(1)).cartDiscounts(); + verify(sourceClient, times(1)).customers(); - inOrder.verify(sourceClient).execute(any(ProductProjectionQuery.class)); + inOrder.verify(sourceClient).productProjections(); - inOrder.verify(sourceClient).execute(any(ShoppingListQuery.class)); + inOrder.verify(sourceClient).shoppingLists(); verify(sourceClient, times(1)).close(); - verify(sourceClient, times(11)).getConfig(); + // verify(sourceClient, times(11)).getConfig } @Test void run_WithOnlySyncCustomObjectArgument_ShouldThrowException() { // preparation - final SphereClient sourceClient = mock(SphereClient.class); - when(sourceClient.getConfig()).thenReturn(SphereClientConfig.of("foo", "foo", "foo")); - - final SphereClient targetClient = mock(SphereClient.class); - when(targetClient.getConfig()).thenReturn(SphereClientConfig.of("bar", "bar", "bar")); - final SyncerFactory syncerFactory = spy(SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock())); diff --git a/src/test/java/com/commercetools/project/sync/SyncerFactoryTest.java b/src/test/java/com/commercetools/project/sync/SyncerFactoryTest.java index 6a0060cb..c2498a72 100644 --- a/src/test/java/com/commercetools/project/sync/SyncerFactoryTest.java +++ b/src/test/java/com/commercetools/project/sync/SyncerFactoryTest.java @@ -15,78 +15,75 @@ import static com.commercetools.project.sync.util.TestUtils.assertStateSyncerLoggingEvents; import static com.commercetools.project.sync.util.TestUtils.assertTaxCategorySyncerLoggingEvents; import static com.commercetools.project.sync.util.TestUtils.assertTypeSyncerLoggingEvents; +import static com.commercetools.project.sync.util.TestUtils.createBadGatewayException; import static com.commercetools.project.sync.util.TestUtils.getMockedClock; import static com.commercetools.project.sync.util.TestUtils.mockLastSyncCustomObject; +import static com.commercetools.project.sync.util.TestUtils.readObjectFromResource; +import static com.commercetools.project.sync.util.TestUtils.readStringFromFile; import static com.commercetools.project.sync.util.TestUtils.stubClientsCustomObjectService; -import static com.commercetools.project.sync.util.TestUtils.verifyInteractionsWithClientAfterSync; -import static io.sphere.sdk.products.ProductProjectionType.STAGED; +import static com.commercetools.project.sync.util.TestUtils.withTestClient; import static java.lang.String.format; -import static java.util.Arrays.asList; -import static java.util.Collections.emptyList; -import static java.util.Collections.singleton; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.atLeast; 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.verifyNoInteractions; import static org.mockito.Mockito.when; +import com.commercetools.api.client.ByProjectKeyCustomObjectsPost; +import com.commercetools.api.client.ByProjectKeyProductsPost; +import com.commercetools.api.client.ProjectApiRoot; +import com.commercetools.api.defaultconfig.ApiRootBuilder; +import com.commercetools.api.models.ResourcePagedQueryResponse; +import com.commercetools.api.models.custom_object.CustomObject; +import com.commercetools.api.models.custom_object.CustomObjectDraft; +import com.commercetools.api.models.graph_ql.GraphQLRequest; +import com.commercetools.api.models.graph_ql.GraphQLRequestBuilder; +import com.commercetools.api.models.product.Product; +import com.commercetools.api.models.product.ProductDraft; +import com.commercetools.api.models.product.ProductMixin; +import com.commercetools.api.models.product.ProductProjection; +import com.commercetools.api.models.product.ProductProjectionPagedQueryResponse; +import com.commercetools.api.models.product.ProductProjectionPagedQueryResponseBuilder; +import com.commercetools.api.models.product.ProductProjectionType; import com.commercetools.project.sync.cartdiscount.CartDiscountSyncer; import com.commercetools.project.sync.category.CategorySyncer; import com.commercetools.project.sync.customer.CustomerSyncer; import com.commercetools.project.sync.customobject.CustomObjectSyncer; import com.commercetools.project.sync.exception.CliException; import com.commercetools.project.sync.inventoryentry.InventoryEntrySyncer; -import com.commercetools.project.sync.model.response.LastSyncCustomObject; import com.commercetools.project.sync.product.ProductSyncer; import com.commercetools.project.sync.producttype.ProductTypeSyncer; import com.commercetools.project.sync.shoppinglist.ShoppingListSyncer; import com.commercetools.project.sync.state.StateSyncer; import com.commercetools.project.sync.taxcategory.TaxCategorySyncer; import com.commercetools.project.sync.type.TypeSyncer; -import com.commercetools.project.sync.util.MockPagedQueryResult; import com.commercetools.sync.commons.exceptions.ReferenceTransformException; -import com.commercetools.sync.commons.helpers.ResourceKeyIdGraphQlRequest; -import com.commercetools.sync.commons.models.ResourceIdsGraphQlRequest; -import com.commercetools.sync.commons.models.ResourceKeyId; -import com.commercetools.sync.commons.models.ResourceKeyIdGraphQlResult; -import com.commercetools.sync.products.helpers.ProductSyncStatistics; -import io.sphere.sdk.cartdiscounts.queries.CartDiscountQuery; -import io.sphere.sdk.categories.queries.CategoryQuery; -import io.sphere.sdk.client.BadGatewayException; -import io.sphere.sdk.client.SphereClient; -import io.sphere.sdk.client.SphereClientConfig; -import io.sphere.sdk.client.SphereRequest; -import io.sphere.sdk.customers.queries.CustomerQuery; -import io.sphere.sdk.customobjects.CustomObject; -import io.sphere.sdk.customobjects.CustomObjectDraft; -import io.sphere.sdk.customobjects.commands.CustomObjectUpsertCommand; -import io.sphere.sdk.customobjects.queries.CustomObjectQuery; -import io.sphere.sdk.inventory.queries.InventoryEntryQuery; -import io.sphere.sdk.json.SphereJsonUtils; -import io.sphere.sdk.products.Product; -import io.sphere.sdk.products.ProductProjection; -import io.sphere.sdk.products.commands.ProductCreateCommand; -import io.sphere.sdk.products.queries.ProductProjectionQuery; -import io.sphere.sdk.producttypes.queries.ProductTypeQuery; -import io.sphere.sdk.queries.PagedQueryResult; -import io.sphere.sdk.queries.QueryPredicate; -import io.sphere.sdk.shoppinglists.queries.ShoppingListQuery; -import io.sphere.sdk.states.queries.StateQuery; -import io.sphere.sdk.taxcategories.queries.TaxCategoryQuery; -import io.sphere.sdk.types.queries.TypeQuery; -import io.sphere.sdk.utils.CompletableFutureUtils; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.vrap.rmf.base.client.ApiHttpMethod; +import io.vrap.rmf.base.client.ApiHttpResponse; +import io.vrap.rmf.base.client.error.BadGatewayException; +import io.vrap.rmf.base.client.utils.CompletableFutureUtils; +import io.vrap.rmf.base.client.utils.json.JsonUtils; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.time.ZonedDateTime; import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; import java.util.stream.IntStream; import javax.annotation.Nonnull; import org.assertj.core.api.Condition; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; @@ -125,7 +122,19 @@ class SyncerFactoryTest { private static final TestLogger taxCategorySyncerTestLogger = TestLoggerFactory.getTestLogger(TaxCategorySyncer.class); + private ProjectApiRoot sourceClient; + private ProjectApiRoot targetClient; + @BeforeEach + void setupTest() { + final ProjectApiRoot clientWithEmptyResourceResults = + mockClientResourceRequests("testProjectKey"); + sourceClient = spy(clientWithEmptyResourceResults); + targetClient = mock(ProjectApiRoot.class); + when(targetClient.getProjectKey()).thenReturn("testTargetProjectKey"); + } + + @AfterEach void tearDownTest() { cliRunnerTestLogger.clearAll(); productSyncerTestLogger.clearAll(); @@ -141,13 +150,20 @@ void tearDownTest() { taxCategorySyncerTestLogger.clearAll(); } + private ProjectApiRoot mockClientResourceRequests(final String projectKey) { + return withTestClient( + projectKey, + (uri, method, encodedRequetsBody) -> { + final String responseString = "{\"results\":[]}"; + return CompletableFuture.completedFuture( + new ApiHttpResponse<>(200, null, responseString.getBytes(StandardCharsets.UTF_8))); + }); + } + @Test void sync_WithNullOptionValue_ShouldCompleteExceptionallyWithIllegalArgumentException() { assertThat( - SyncerFactory.of( - () -> mock(SphereClient.class), - () -> mock(SphereClient.class), - getMockedClock()) + SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock()) .sync(new String[] {null}, "myRunnerName", false, false, null)) .failsWithin(1, TimeUnit.SECONDS) .withThrowableOfType(ExecutionException.class) @@ -161,10 +177,7 @@ void sync_WithNullOptionValue_ShouldCompleteExceptionallyWithIllegalArgumentExce @Test void sync_WithEmptyOptionValue_ShouldCompleteExceptionallyWithIllegalArgumentException() { assertThat( - SyncerFactory.of( - () -> mock(SphereClient.class), - () -> mock(SphereClient.class), - getMockedClock()) + SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock()) .sync(new String[] {""}, "myRunnerName", false, false, null)) .failsWithin(1, TimeUnit.SECONDS) .withThrowableOfType(ExecutionException.class) @@ -180,10 +193,7 @@ void sync_WithUnknownOptionValue_ShouldCompleteExceptionallyWithIllegalArgumentE final String[] unknownOptionValue = {"anyOption"}; assertThat( - SyncerFactory.of( - () -> mock(SphereClient.class), - () -> mock(SphereClient.class), - getMockedClock()) + SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock()) .sync(unknownOptionValue, "myRunnerName", false, false, null)) .failsWithin(1, TimeUnit.SECONDS) .withThrowableOfType(ExecutionException.class) @@ -198,32 +208,24 @@ void sync_WithUnknownOptionValue_ShouldCompleteExceptionallyWithIllegalArgumentE @SuppressWarnings("unchecked") void sync_AsProductsDeltaSync_ShouldBuildSyncerAndExecuteSync() { // preparation - final SphereClient sourceClient = mock(SphereClient.class); - when(sourceClient.getConfig()).thenReturn(SphereClientConfig.of("foo", "foo", "foo")); - - final SphereClient targetClient = mock(SphereClient.class); - when(targetClient.getConfig()).thenReturn(SphereClientConfig.of("bar", "bar", "bar")); - - when(sourceClient.execute(any(ProductProjectionQuery.class))) - .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); + stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); final SyncerFactory syncerFactory = SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock()); - final ZonedDateTime currentCtpTimestamp = ZonedDateTime.now(); - stubClientsCustomObjectService(targetClient, currentCtpTimestamp); - // test syncerFactory.sync(new String[] {"products"}, "myRunnerName", false, false, null); // assertions - verify(sourceClient, times(1)).execute(any(ProductProjectionQuery.class)); - + // verify product-projections are queried once + verify(sourceClient, times(1)).productProjections(); + // assertThat(verifyProductProjectionsGetCounter.get()).isEqualTo(1); verifyTimestampGeneratorCustomObjectUpsertIsCalled(targetClient, "ProductSync", "myRunnerName"); - verifyLastSyncCustomObjectQuery(targetClient, "productSync", "myRunnerName", "foo", 1); + verifyLastSyncCustomObjectQuery( + targetClient, "productSync", "myRunnerName", "testProjectKey", 1); // verify two custom object upserts : 1. current ctp timestamp and 2. last sync timestamp // creation) - verify(targetClient, times(2)).execute(any(CustomObjectUpsertCommand.class)); + verify(targetClient.customObjects(), times(2)).post(any(CustomObjectDraft.class)); // TODO: Assert on actual last sync timestamp creation in detail after Statistics classes in // java-sync library // TODO: override #equals method: @@ -261,31 +263,21 @@ void sync_AsProductsDeltaSync_ShouldBuildSyncerAndExecuteSync() { @SuppressWarnings("unchecked") void sync_AsProductsFullSync_ShouldBuildSyncerAndExecuteSync() { // preparation - final SphereClient sourceClient = mock(SphereClient.class); - when(sourceClient.getConfig()).thenReturn(SphereClientConfig.of("foo", "foo", "foo")); - - final SphereClient targetClient = mock(SphereClient.class); - when(targetClient.getConfig()).thenReturn(SphereClientConfig.of("bar", "bar", "bar")); - - when(sourceClient.execute(any(ProductProjectionQuery.class))) - .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); + stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); final SyncerFactory syncerFactory = SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock()); - final ZonedDateTime currentCtpTimestamp = ZonedDateTime.now(); - stubClientsCustomObjectService(targetClient, currentCtpTimestamp); - // test syncerFactory.sync(new String[] {"products"}, "myRunnerName", true, false, null); // assertions - verify(sourceClient, times(1)).execute(any(ProductProjectionQuery.class)); - + verify(sourceClient, times(1)).productProjections(); + // assertThat(verifyProductProjectionsGetCounter.get()).isEqualTo(1); verifyTimestampGeneratorCustomObjectUpsertIsNotCalled( targetClient, "ProductSync", "myRunnerName"); verifyLastSyncCustomObjectQuery(targetClient, "productSync", "myRunnerName", "foo", 0); - verify(targetClient, times(0)).execute(any(CustomObjectUpsertCommand.class)); + verify(targetClient.customObjects(), times(0)).post(any(CustomObjectDraft.class)); verifyInteractionsWithClientAfterSync(sourceClient, 1); final Condition startLog = @@ -316,41 +308,64 @@ void sync_AsProductsFullSync_ShouldBuildSyncerAndExecuteSync() { void sync_AsProductsFullSyncWithExceptionDuringAttributeReferenceReplacement_ShouldBuildSyncerAndExecuteSync() { // preparation - final SphereClient sourceClient = mock(SphereClient.class); - when(sourceClient.getConfig()).thenReturn(SphereClientConfig.of("foo", "foo", "foo")); - - final SphereClient targetClient = mock(SphereClient.class); - when(targetClient.getConfig()).thenReturn(SphereClientConfig.of("bar", "bar", "bar")); - final ProductProjection product5 = - SphereJsonUtils.readObjectFromResource("product-key-5.json", Product.class) - .toProjection(STAGED); + ProductMixin.toProjection( + readObjectFromResource("product-key-5.json", Product.class), + ProductProjectionType.STAGED); final ProductProjection product6 = - SphereJsonUtils.readObjectFromResource("product-key-6.json", Product.class) - .toProjection(STAGED); - final PagedQueryResult twoProductResult = - MockPagedQueryResult.of(asList(product5, product6)); - - when(sourceClient.execute(any(ProductProjectionQuery.class))) - .thenReturn(CompletableFuture.completedFuture(twoProductResult)); + ProductMixin.toProjection( + readObjectFromResource("product-key-6.json", Product.class), + ProductProjectionType.STAGED); + final ProductProjectionPagedQueryResponse twoProductResult = + ProductProjectionPagedQueryResponseBuilder.of() + .results(product5, product6) + .limit(10L) + .count(2L) + .offset(0L) + .total(2L) + .build(); + + final AtomicInteger verifyProductProjectionsGetCounter = new AtomicInteger(0); + final BadGatewayException badGatewayException = createBadGatewayException(); + final ProjectApiRoot sourceClient = + withTestClient( + "testProjectKey", + (uri, method, encodedRequetsBody) -> { + if (uri.contains("graphql") && ApiHttpMethod.POST.equals(method)) { + return CompletableFutureUtils.failed(badGatewayException); + } + if (uri.contains("product-projections") && ApiHttpMethod.GET.equals(method)) { + if (verifyProductProjectionsGetCounter.get() == 0) { + verifyProductProjectionsGetCounter.incrementAndGet(); + final String jsonString = + createJsonStringFromPagedQueryResponse(twoProductResult); + return CompletableFuture.completedFuture( + new ApiHttpResponse<>( + 200, null, jsonString.getBytes(StandardCharsets.UTF_8))); + } + } + return null; + }); - when(targetClient.execute(any())).thenReturn(CompletableFuture.completedFuture(null)); - final BadGatewayException badGatewayException = new BadGatewayException("Error!"); - when(targetClient.execute(any(ProductCreateCommand.class))) - .thenReturn(CompletableFutureUtils.failed(badGatewayException)); - when(sourceClient.execute(any(ResourceIdsGraphQlRequest.class))) + stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); + final ByProjectKeyProductsPost byProjectKeyProductsPost = mock(ByProjectKeyProductsPost.class); + when(byProjectKeyProductsPost.execute()) .thenReturn(CompletableFutureUtils.failed(badGatewayException)); + when(targetClient.products()).thenReturn(mock()); + when(targetClient.products().post(any(ProductDraft.class))) + .thenReturn(byProjectKeyProductsPost); + final ProjectApiRoot sourceSpy = spy(sourceClient); final SyncerFactory syncerFactory = - SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock()); + SyncerFactory.of(() -> sourceSpy, () -> targetClient, getMockedClock()); // test syncerFactory.sync(new String[] {"products"}, "myRunnerName", true, false, null); // assertions - verify(sourceClient, times(1)).execute(any(ProductProjectionQuery.class)); - verify(sourceClient, times(3)).execute(any(ResourceIdsGraphQlRequest.class)); - verifyInteractionsWithClientAfterSync(sourceClient, 1); + verify(sourceSpy, times(1)).productProjections(); + verify(sourceSpy, times(3)).graphql(); + verifyInteractionsWithClientAfterSync(sourceSpy, 1); final Condition startLog = new Condition<>( @@ -394,91 +409,175 @@ void sync_AsProductsFullSync_ShouldBuildSyncerAndExecuteSync() { void sync_AsProductsFullSyncWithExceptionDuringAttributeReferenceReplacement_ShouldContinueWithPages() { // preparation - final SphereClient sourceClient = mock(SphereClient.class); - when(sourceClient.getConfig()).thenReturn(SphereClientConfig.of("foo", "foo", "foo")); - - final SphereClient targetClient = mock(SphereClient.class); - when(targetClient.getConfig()).thenReturn(SphereClientConfig.of("bar", "bar", "bar")); - + final AtomicInteger verifyProductProjectionsGetCounter = new AtomicInteger(0); + final AtomicInteger sourceGraphQLPostCounter = new AtomicInteger(0); final ProductProjection product1 = - SphereJsonUtils.readObjectFromResource("product-key-7.json", Product.class) - .toProjection(STAGED); + ProductMixin.toProjection( + readObjectFromResource("product-key-7.json", Product.class), + ProductProjectionType.STAGED); final ProductProjection product2 = - SphereJsonUtils.readObjectFromResource("product-key-8.json", Product.class) - .toProjection(STAGED); + ProductMixin.toProjection( + readObjectFromResource("product-key-8.json", Product.class), + ProductProjectionType.STAGED); final ProductProjection product3 = - SphereJsonUtils.readObjectFromResource("product-key-9.json", Product.class) - .toProjection(STAGED); + ProductMixin.toProjection( + readObjectFromResource("product-key-9.json", Product.class), + ProductProjectionType.STAGED); final List fullPageOfProducts = IntStream.range(0, 500).mapToObj(o -> product1).collect(Collectors.toList()); - when(sourceClient.execute(any(ProductProjectionQuery.class))) - .thenReturn(CompletableFuture.completedFuture(MockPagedQueryResult.of(fullPageOfProducts))) - .thenReturn( - CompletableFuture.completedFuture(MockPagedQueryResult.of(asList(product3, product2)))); - - when(targetClient.execute(any(ProductProjectionQuery.class))) - .thenReturn(CompletableFuture.completedFuture(MockPagedQueryResult.of(emptyList()))); - when(targetClient.execute(any(ProductTypeQuery.class))) - .thenReturn(CompletableFuture.completedFuture(MockPagedQueryResult.of(emptyList()))); - when(targetClient.execute(any(CategoryQuery.class))) - .thenReturn(CompletableFuture.completedFuture(MockPagedQueryResult.of(emptyList()))); - when(targetClient.execute(any(TaxCategoryQuery.class))) - .thenReturn(CompletableFuture.completedFuture(MockPagedQueryResult.of(emptyList()))); - when(targetClient.execute(any(StateQuery.class))) - .thenReturn(CompletableFuture.completedFuture(MockPagedQueryResult.of(emptyList()))); - when(targetClient.execute(any(CustomObjectQuery.class))) - .thenReturn(CompletableFuture.completedFuture(MockPagedQueryResult.of(emptyList()))); - - final Product product4 = - SphereJsonUtils.readObjectFromResource("product-key-8.json", Product.class); - - when(targetClient.execute(any(ProductCreateCommand.class))) - .thenReturn(CompletableFuture.completedFuture(product4)); - - String jsonAsString = - "{\"results\":[{\"id\":\"53c4a8b4-865f-4b95-b6f2-3e1e70e3d0c1\",\"key\":\"productKey3\"}]}"; - final ResourceKeyIdGraphQlResult productsResult = - SphereJsonUtils.readObject(jsonAsString, ResourceKeyIdGraphQlResult.class); - - String jsonStringProductTypes = - "{\"results\":[{\"id\":\"53c4a8b4-865f-4b95-b6f2-3e1e70e3d0c2\",\"key\":\"prodType1\"}]}"; - final ResourceKeyIdGraphQlResult productTypesResult = - SphereJsonUtils.readObject(jsonStringProductTypes, ResourceKeyIdGraphQlResult.class); - - String jsonStringCategories = - "{\"results\":[{\"id\":\"53c4a8b4-865f-4b95-b6f2-3e1e70e3d0c3\",\"key\":\"cat1\"}]}"; - final ResourceKeyIdGraphQlResult categoriesResult = - SphereJsonUtils.readObject(jsonStringCategories, ResourceKeyIdGraphQlResult.class); - - final BadGatewayException badGatewayException = new BadGatewayException("Error!"); - when(sourceClient.execute(any(ResourceIdsGraphQlRequest.class))) - .thenReturn(CompletableFutureUtils.failed(badGatewayException)) - .thenReturn(CompletableFutureUtils.failed(badGatewayException)) - .thenReturn(CompletableFutureUtils.failed(badGatewayException)) - .thenReturn(CompletableFuture.completedFuture(productsResult)) - .thenReturn(CompletableFuture.completedFuture(categoriesResult)) - .thenReturn(CompletableFuture.completedFuture(productTypesResult)); - - final ResourceKeyIdGraphQlResult resourceKeyIdGraphQlResult = - mock(ResourceKeyIdGraphQlResult.class); - when(resourceKeyIdGraphQlResult.getResults()) - .thenReturn( - singleton(new ResourceKeyId("productKey3", "53c4a8b4-865f-4b95-b6f2-3e1e70e3d0c1"))); - when(targetClient.execute(any(ResourceKeyIdGraphQlRequest.class))) - .thenReturn(CompletableFuture.completedFuture(resourceKeyIdGraphQlResult)); + final Long pageSize = Long.valueOf(fullPageOfProducts.size()); + + final ProductProjectionPagedQueryResponse fullPageResponse = + ProductProjectionPagedQueryResponseBuilder.of() + .results(fullPageOfProducts) + .limit(10L) + .count(pageSize) + .offset(0L) + .total(pageSize) + .build(); + final ProductProjectionPagedQueryResponse twoProductResult = + ProductProjectionPagedQueryResponseBuilder.of() + .results(product3, product2) + .limit(10L) + .count(2L) + .offset(0L) + .total(2L) + .build(); + + final ProjectApiRoot srcClient = + withTestClient( + "testProjectKey", + (uri, method, encodedRequestBody) -> { + final Charset charsetUTF8 = Charset.forName(StandardCharsets.UTF_8.name()); + if (uri.contains("graphql") && ApiHttpMethod.POST.equals(method)) { + final ObjectMapper objectMapper = JsonUtils.getConfiguredObjectMapper(); + GraphQLRequest graphQLRequest; + try { + graphQLRequest = objectMapper.readValue(encodedRequestBody, GraphQLRequest.class); + } catch (JsonProcessingException e) { + graphQLRequest = GraphQLRequestBuilder.of().build(); + } + final String graphQLRequestQuery = graphQLRequest.getQuery(); + final String bodyData = "{\"data\": %s}"; + String result = String.format(bodyData, "{}"); + if (graphQLRequestQuery != null) { + int graphQlPostCounter = sourceGraphQLPostCounter.getAndIncrement(); + if (graphQlPostCounter < 3) { + return CompletableFutureUtils.failed(createBadGatewayException()); + } + if (graphQLRequestQuery.contains("products")) { + final String jsonAsString = + "{\"results\":[{\"id\":\"53c4a8b4-865f-4b95-b6f2-3e1e70e3d0c1\",\"key\":\"productKey3\"}]}"; + result = String.format(bodyData, jsonAsString); + return CompletableFuture.completedFuture( + new ApiHttpResponse<>(200, null, result.getBytes(charsetUTF8))); + } else if (graphQLRequestQuery.contains("productTypes")) { + final String jsonAsString = + "{\"results\":[{\"id\":\"53c4a8b4-865f-4b95-b6f2-3e1e70e3d0c2\",\"key\":\"prodType1\"}]}"; + result = String.format(bodyData, jsonAsString); + return CompletableFuture.completedFuture( + new ApiHttpResponse<>(200, null, result.getBytes(charsetUTF8))); + } else if (graphQLRequestQuery.contains("categories")) { + final String jsonAsString = + "{\"results\":[{\"id\":\"53c4a8b4-865f-4b95-b6f2-3e1e70e3d0c3\",\"key\":\"cat1\"}]}"; + result = String.format(bodyData, jsonAsString); + return CompletableFuture.completedFuture( + new ApiHttpResponse<>(200, null, result.getBytes(charsetUTF8))); + } else { + return CompletableFuture.completedFuture( + new ApiHttpResponse<>(200, null, result.getBytes(charsetUTF8))); + } + } + } + if (uri.contains("product-projections") && ApiHttpMethod.GET.equals(method)) { + int sourceGetCounter = verifyProductProjectionsGetCounter.getAndIncrement(); + if (sourceGetCounter == 0) { + final String fullPageResponseAsString = + createJsonStringFromPagedQueryResponse(fullPageResponse); + return CompletableFuture.completedFuture( + new ApiHttpResponse<>( + 200, null, fullPageResponseAsString.getBytes(charsetUTF8))); + } else if (sourceGetCounter == 1) { + final String twoResultsResponseAsString = + createJsonStringFromPagedQueryResponse(twoProductResult); + return CompletableFuture.completedFuture( + new ApiHttpResponse<>( + 200, null, twoResultsResponseAsString.getBytes(charsetUTF8))); + + } else { + final String emptyResultsAsString = "{\"results\":[]}"; + return CompletableFuture.completedFuture( + new ApiHttpResponse<>(200, null, emptyResultsAsString.getBytes(charsetUTF8))); + } + } + return null; + }); + + final ProjectApiRoot trgClient = + ApiRootBuilder.of( + request -> { + final String uri = request.getUri() != null ? request.getUri().toString() : ""; + final ApiHttpMethod method = request.getMethod(); + final Charset charsetUTF8 = Charset.forName(StandardCharsets.UTF_8.name()); + if (uri.contains("graphql") && ApiHttpMethod.POST.equals(method)) { + final String encodedRequestBody = + new String(request.getBody(), StandardCharsets.UTF_8); + ObjectMapper objectMapper = JsonUtils.getConfiguredObjectMapper(); + GraphQLRequest graphQLRequest; + try { + graphQLRequest = + objectMapper.readValue(encodedRequestBody, GraphQLRequest.class); + } catch (JsonProcessingException e) { + graphQLRequest = GraphQLRequestBuilder.of().build(); + } + final String graphQLRequestQuery = graphQLRequest.getQuery(); + final String bodyData = "{\"data\": %s}"; + String result; + if (graphQLRequestQuery != null) { + if (graphQLRequestQuery.contains("products")) { + final String jsonAsString = + "{\"results\":[{\"id\":\"53c4a8b4-865f-4b95-b6f2-3e1e70e3d0c1\",\"key\":\"productKey3\"}]}"; + result = String.format(bodyData, jsonAsString); + return CompletableFuture.completedFuture( + new ApiHttpResponse<>(200, null, result.getBytes(charsetUTF8))); + } else { + result = String.format(bodyData, "{\"results\": []}"); + return CompletableFuture.completedFuture( + new ApiHttpResponse<>(200, null, result.getBytes(charsetUTF8))); + } + } + } + if (uri.contains("products") && ApiHttpMethod.POST.equals(method)) { + final String productsResultAsString = readStringFromFile("product-key-8.json"); + return CompletableFuture.completedFuture( + new ApiHttpResponse<>( + 200, null, productsResultAsString.getBytes(charsetUTF8))); + + } else { + final String emptyResultsAsString = "{\"results\":[]}"; + return CompletableFuture.completedFuture( + new ApiHttpResponse<>( + 200, null, emptyResultsAsString.getBytes(charsetUTF8))); + } + }) + .withApiBaseUrl("testBaseUrl") + .build("testProjectKey2"); + + final ProjectApiRoot sourceClientSpy = spy(srcClient); + final ProjectApiRoot targetClientSpy = spy(trgClient); final SyncerFactory syncerFactory = - SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock()); + SyncerFactory.of(() -> sourceClientSpy, () -> targetClientSpy, getMockedClock()); // test syncerFactory.sync(new String[] {"products"}, "myRunnerName", true, false, null); // assertions - verify(sourceClient, times(2)).execute(any(ProductProjectionQuery.class)); - verify(sourceClient, times(9)).execute(any(ResourceIdsGraphQlRequest.class)); - verifyInteractionsWithClientAfterSync(sourceClient, 1); + assertThat(verifyProductProjectionsGetCounter.get()).isEqualTo(2); + verify(sourceClientSpy, times(9)).graphql(); + verifyInteractionsWithClientAfterSync(sourceClientSpy, 1); final Condition startLog = new Condition<>( @@ -514,12 +613,12 @@ void sync_AsProductsFullSync_ShouldBuildSyncerAndExecuteSync() { + "This page will not be synced to the target project."); assertThat(loggingEvent.getThrowable().isPresent()).isTrue(); assertThat(loggingEvent.getThrowable().get().getCause().getCause()) - .isEqualTo(badGatewayException); + .isInstanceOf(BadGatewayException.class); }); } private static void verifyTimestampGeneratorCustomObjectUpsertIsCalled( - @Nonnull final SphereClient client, + @Nonnull final ProjectApiRoot client, @Nonnull final String syncMethodName, @Nonnull final String syncRunnerName) { final CustomObjectDraft customObjectDraft = @@ -531,7 +630,7 @@ private static void verifyTimestampGeneratorCustomObjectUpsertIsCalled( } private static void verifyTimestampGeneratorCustomObjectUpsertIsNotCalled( - @Nonnull final SphereClient client, + @Nonnull final ProjectApiRoot client, @Nonnull final String syncMethodName, @Nonnull final String syncRunnerName) { final CustomObjectDraft customObjectDraft = @@ -540,91 +639,80 @@ private static void verifyTimestampGeneratorCustomObjectUpsertIsNotCalled( } private static CustomObjectDraft findTimestampGeneratorCustomObjectUpsert( - @Nonnull SphereClient client, + @Nonnull ProjectApiRoot client, @Nonnull String syncMethodName, @Nonnull String syncRunnerName) { - // fact: SphereRequest is a very broad interface and we actually wanted to capture only - // CustomObjectUpsertCommand. - // I tried it but argumentcaptor captures also CustomObjectQueryImpl classes, because we call - // both query and upsert in the mocked SphereClient. - // This situation throws runtime NPE error later in the method as query doesnt contain a draft. - // I guess generics doesnt work here as type is not know on compile time. - // That's why we need to filter instanceof CustomObjectUpsertCommand in the streams. - final ArgumentCaptor sphereClientArgumentCaptor = - ArgumentCaptor.forClass(CustomObjectUpsertCommand.class); - - verify(client, atLeast(0)).execute(sphereClientArgumentCaptor.capture()); - final List allValues = sphereClientArgumentCaptor.getAllValues(); + final ArgumentCaptor customObjectDraftArgumentCaptor = + ArgumentCaptor.forClass(CustomObjectDraft.class); + + verify(client.customObjects(), atLeast(0)).post(customObjectDraftArgumentCaptor.capture()); + final List allValues = customObjectDraftArgumentCaptor.getAllValues(); final CustomObjectDraft customObjectDraft = allValues.stream() - .filter(sphereRequest -> sphereRequest instanceof CustomObjectUpsertCommand) - .map(sphereRequest -> (CustomObjectUpsertCommand) sphereRequest) - .map(command -> (CustomObjectDraft) command.getDraft()) .filter( - draft -> { - return draft - .getContainer() - .equals( - format( - "%s.%s.%s.%s", - getApplicationName(), - syncRunnerName, - syncMethodName, - TIMESTAMP_GENERATOR_KEY)) - && draft.getKey().equals(TIMESTAMP_GENERATOR_KEY); - }) + draft -> + draft + .getContainer() + .equals( + format( + "%s.%s.%s.%s", + getApplicationName(), + syncRunnerName, + syncMethodName, + TIMESTAMP_GENERATOR_KEY)) + && draft.getKey().equals(TIMESTAMP_GENERATOR_KEY)) .findAny() .orElse(null); return customObjectDraft; } private static void verifyLastSyncCustomObjectQuery( - @Nonnull final SphereClient client, + @Nonnull final ProjectApiRoot client, @Nonnull final String syncModuleName, @Nonnull final String syncRunnerName, @Nonnull final String sourceProjectKey, final int expectedInvocations) { - final QueryPredicate> queryPredicate = - QueryPredicate.of( - format( - "container=\"commercetools-project-sync.%s.%s\" AND key=\"%s\"", - syncRunnerName, syncModuleName, sourceProjectKey)); + final String container = + format("commercetools-project-sync.%s.%s", syncRunnerName, syncModuleName); + + if (expectedInvocations > 0) { + verify(client.customObjects(), times(expectedInvocations)) + .withContainerAndKey(container, sourceProjectKey); + } else { + verifyNoInteractions( + client.customObjects().withContainerAndKey(anyString(), anyString()).get()); + } + } - verify(client, times(expectedInvocations)) - .execute(CustomObjectQuery.of(LastSyncCustomObject.class).plusPredicates(queryPredicate)); + private static String createJsonStringFromPagedQueryResponse( + final ResourcePagedQueryResponse pagedQueryResponse) { + try { + return JsonUtils.toJsonString(pagedQueryResponse); + } catch (JsonProcessingException e) { + return "{\"results\": []}"; + } } @Test @SuppressWarnings("unchecked") void sync_AsCategoriesDeltaSync_ShouldBuildSyncerAndExecuteSync() { // preparation - final SphereClient sourceClient = mock(SphereClient.class); - when(sourceClient.getConfig()).thenReturn(SphereClientConfig.of("foo", "foo", "foo")); - - final SphereClient targetClient = mock(SphereClient.class); - when(targetClient.getConfig()).thenReturn(SphereClientConfig.of("bar", "bar", "bar")); - - when(sourceClient.execute(any(CategoryQuery.class))) - .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); + stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); final SyncerFactory syncerFactory = SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock()); - - final ZonedDateTime currentCtpTimestamp = ZonedDateTime.now(); - stubClientsCustomObjectService(targetClient, currentCtpTimestamp); - // test syncerFactory.sync(new String[] {"categories"}, null, false, false, null); // assertions - verify(sourceClient, times(1)).execute(any(CategoryQuery.class)); + verify(sourceClient, times(1)).categories(); verifyTimestampGeneratorCustomObjectUpsertIsCalled( targetClient, "CategorySync", DEFAULT_RUNNER_NAME); - verifyLastSyncCustomObjectQuery(targetClient, "categorySync", DEFAULT_RUNNER_NAME, "foo", 1); + verifyLastSyncCustomObjectQuery( + targetClient, "categorySync", DEFAULT_RUNNER_NAME, "testProjectKey", 1); // verify two custom object upserts : 1. current ctp timestamp and 2. last sync timestamp - // creation) - verify(targetClient, times(2)).execute(any(CustomObjectUpsertCommand.class)); + verify(targetClient.customObjects(), times(2)).post(any(CustomObjectDraft.class)); // TODO: Assert on actual last sync timestamp creation in detail after Statistics classes in // java-sync library // TODO: override #equals method: @@ -662,32 +750,23 @@ void sync_AsCategoriesDeltaSync_ShouldBuildSyncerAndExecuteSync() { @SuppressWarnings("unchecked") void sync_AsProductTypesDeltaSync_ShouldBuildSyncerAndExecuteSync() { // preparation - final SphereClient sourceClient = mock(SphereClient.class); - when(sourceClient.getConfig()).thenReturn(SphereClientConfig.of("foo", "foo", "foo")); - - final SphereClient targetClient = mock(SphereClient.class); - when(targetClient.getConfig()).thenReturn(SphereClientConfig.of("bar", "bar", "bar")); - - when(sourceClient.execute(any(ProductTypeQuery.class))) - .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); + stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); final SyncerFactory syncerFactory = SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock()); - final ZonedDateTime currentCtpTimestamp = ZonedDateTime.now(); - stubClientsCustomObjectService(targetClient, currentCtpTimestamp); - // test syncerFactory.sync(new String[] {"productTypes"}, "", false, false, null); // assertions - verify(sourceClient, times(1)).execute(any(ProductTypeQuery.class)); + verify(sourceClient, times(1)).productTypes(); verifyTimestampGeneratorCustomObjectUpsertIsCalled( targetClient, "ProductTypeSync", DEFAULT_RUNNER_NAME); - verifyLastSyncCustomObjectQuery(targetClient, "productTypeSync", DEFAULT_RUNNER_NAME, "foo", 1); + verifyLastSyncCustomObjectQuery( + targetClient, "productTypeSync", DEFAULT_RUNNER_NAME, "testProjectKey", 1); // verify two custom object upserts : 1. current ctp timestamp and 2. last sync timestamp // creation) - verify(targetClient, times(2)).execute(any(CustomObjectUpsertCommand.class)); + verify(targetClient.customObjects(), times(2)).post(any(CustomObjectDraft.class)); // TODO: Assert on actual last sync timestamp creation in detail after Statistics classes in // java-sync library // TODO: override #equals method: @@ -726,17 +805,7 @@ void sync_AsProductTypesDeltaSync_ShouldBuildSyncerAndExecuteSync() { @SuppressWarnings("unchecked") void sync_AsTypesDeltaSync_ShouldBuildSyncerAndExecuteSync() { // preparation - final SphereClient sourceClient = mock(SphereClient.class); - when(sourceClient.getConfig()).thenReturn(SphereClientConfig.of("foo", "foo", "foo")); - - final SphereClient targetClient = mock(SphereClient.class); - when(targetClient.getConfig()).thenReturn(SphereClientConfig.of("bar", "bar", "bar")); - - when(sourceClient.execute(any(TypeQuery.class))) - .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - - final ZonedDateTime currentCtpTimestamp = ZonedDateTime.now(); - stubClientsCustomObjectService(targetClient, currentCtpTimestamp); + stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); final SyncerFactory syncerFactory = SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock()); @@ -745,12 +814,12 @@ void sync_AsTypesDeltaSync_ShouldBuildSyncerAndExecuteSync() { syncerFactory.sync(new String[] {"types"}, "foo", false, false, null); // assertions - verify(sourceClient, times(1)).execute(any(TypeQuery.class)); + verify(sourceClient, times(1)).types(); verifyTimestampGeneratorCustomObjectUpsertIsCalled(targetClient, "TypeSync", "foo"); - verifyLastSyncCustomObjectQuery(targetClient, "typeSync", "foo", "foo", 1); + verifyLastSyncCustomObjectQuery(targetClient, "typeSync", "foo", "testProjectKey", 1); // verify two custom object upserts : 1. current ctp timestamp and 2. last sync timestamp // creation) - verify(targetClient, times(2)).execute(any(CustomObjectUpsertCommand.class)); + verify(targetClient.customObjects(), times(2)).post(any(CustomObjectDraft.class)); // TODO: Assert on actual last sync timestamp creation in detail after Statistics classes in // java-sync library // TODO: override #equals method: @@ -788,17 +857,7 @@ void sync_AsTypesDeltaSync_ShouldBuildSyncerAndExecuteSync() { @SuppressWarnings("unchecked") void sync_AsInventoryEntriesDeltaSync_ShouldBuildSyncerAndExecuteSync() { // preparation - final SphereClient sourceClient = mock(SphereClient.class); - when(sourceClient.getConfig()).thenReturn(SphereClientConfig.of("foo", "foo", "foo")); - - final SphereClient targetClient = mock(SphereClient.class); - when(targetClient.getConfig()).thenReturn(SphereClientConfig.of("bar", "bar", "bar")); - - when(sourceClient.execute(any(InventoryEntryQuery.class))) - .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - - final ZonedDateTime currentCtpTimestamp = ZonedDateTime.now(); - stubClientsCustomObjectService(targetClient, currentCtpTimestamp); + stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); final SyncerFactory syncerFactory = SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock()); @@ -807,13 +866,14 @@ void sync_AsInventoryEntriesDeltaSync_ShouldBuildSyncerAndExecuteSync() { syncerFactory.sync(new String[] {"inventoryEntries"}, null, false, false, null); // assertions - verify(sourceClient, times(1)).execute(any(InventoryEntryQuery.class)); + verify(sourceClient, times(1)).inventory(); verifyTimestampGeneratorCustomObjectUpsertIsCalled( targetClient, "InventorySync", DEFAULT_RUNNER_NAME); - verifyLastSyncCustomObjectQuery(targetClient, "inventorySync", DEFAULT_RUNNER_NAME, "foo", 1); + verifyLastSyncCustomObjectQuery( + targetClient, "inventorySync", DEFAULT_RUNNER_NAME, "testProjectKey", 1); // verify two custom object upserts : 1. current ctp timestamp and 2. last sync timestamp // creation) - verify(targetClient, times(2)).execute(any(CustomObjectUpsertCommand.class)); + verify(targetClient.customObjects(), times(2)).post(any(CustomObjectDraft.class)); // TODO: Assert on actual last sync timestamp creation in detail after Statistics classes in // java-sync library // TODO: override #equals method: @@ -850,18 +910,19 @@ void sync_AsInventoryEntriesDeltaSync_ShouldBuildSyncerAndExecuteSync() { @Test void sync_WithErrorOnFetch_ShouldCloseClientAndCompleteExceptionally() { // preparation - final SphereClient sourceClient = mock(SphereClient.class); - when(sourceClient.getConfig()).thenReturn(SphereClientConfig.of("foo", "foo", "foo")); - - final SphereClient targetClient = mock(SphereClient.class); - when(targetClient.getConfig()).thenReturn(SphereClientConfig.of("bar", "bar", "bar")); - - final BadGatewayException badGatewayException = new BadGatewayException(); - when(sourceClient.execute(any(InventoryEntryQuery.class))) - .thenReturn(CompletableFutureUtils.exceptionallyCompletedFuture(badGatewayException)); + final ProjectApiRoot mockSource = + withTestClient( + "testProjectKey", + (uri, method, encodedRequestBody) -> { + if (uri.contains("inventory") && ApiHttpMethod.GET.equals(method)) { + return CompletableFutureUtils.exceptionallyCompletedFuture( + createBadGatewayException()); + } + return null; + }); + sourceClient = spy(mockSource); - final ZonedDateTime currentCtpTimestamp = ZonedDateTime.now(); - stubClientsCustomObjectService(targetClient, currentCtpTimestamp); + stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); final SyncerFactory syncerFactory = SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock()); @@ -873,8 +934,9 @@ void sync_WithErrorOnFetch_ShouldCloseClientAndCompleteExceptionally() { // assertions verifyTimestampGeneratorCustomObjectUpsertIsCalled( targetClient, "InventorySync", DEFAULT_RUNNER_NAME); - verify(sourceClient, times(1)).execute(any(InventoryEntryQuery.class)); + verify(sourceClient, times(1)).inventory(); verifyInteractionsWithClientAfterSync(sourceClient, 1); + assertThat(result) .failsWithin(1, TimeUnit.SECONDS) .withThrowableOfType(ExecutionException.class) @@ -886,15 +948,14 @@ void sync_WithErrorOnFetch_ShouldCloseClientAndCompleteExceptionally() { void sync_WithErrorOnCurrentCtpTimestampUpsert_ShouldCloseClientAndCompleteExceptionallyWithoutSyncing() { // preparation - final SphereClient sourceClient = mock(SphereClient.class); - when(sourceClient.getConfig()).thenReturn(SphereClientConfig.of("foo", "foo", "foo")); - - final SphereClient targetClient = mock(SphereClient.class); - when(targetClient.getConfig()).thenReturn(SphereClientConfig.of("bar", "bar", "bar")); - - final BadGatewayException badGatewayException = new BadGatewayException(); - when(targetClient.execute(any(CustomObjectUpsertCommand.class))) - .thenReturn(CompletableFutureUtils.exceptionallyCompletedFuture(badGatewayException)); + final ByProjectKeyCustomObjectsPost customObjectsPost = + mock(ByProjectKeyCustomObjectsPost.class); + when(customObjectsPost.execute()) + .thenReturn( + CompletableFutureUtils.exceptionallyCompletedFuture(createBadGatewayException())); + when(targetClient.customObjects()).thenReturn(mock()); + when(targetClient.customObjects().post(any(CustomObjectDraft.class))) + .thenReturn(customObjectsPost); final SyncerFactory syncerFactory = SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock()); @@ -906,7 +967,7 @@ void sync_WithErrorOnFetch_ShouldCloseClientAndCompleteExceptionally() { // assertions verifyTimestampGeneratorCustomObjectUpsertIsCalled( targetClient, "InventorySync", DEFAULT_RUNNER_NAME); - verify(sourceClient, times(0)).execute(any(InventoryEntryQuery.class)); + verify(sourceClient, times(0)).inventory(); verifyInteractionsWithClientAfterSync(sourceClient, 1); assertThat(result) .failsWithin(1, TimeUnit.SECONDS) @@ -919,20 +980,24 @@ void sync_WithErrorOnFetch_ShouldCloseClientAndCompleteExceptionally() { void sync_WithErrorOnQueryLastSyncTimestamp_ShouldCloseClientAndCompleteExceptionallyWithoutSyncing() { // preparation - final SphereClient sourceClient = mock(SphereClient.class); - when(sourceClient.getConfig()).thenReturn(SphereClientConfig.of("foo", "foo", "foo")); - - final SphereClient targetClient = mock(SphereClient.class); - when(targetClient.getConfig()).thenReturn(SphereClientConfig.of("bar", "bar", "bar")); - - final BadGatewayException badGatewayException = new BadGatewayException(); - when(targetClient.execute(any(CustomObjectQuery.class))) - .thenReturn(CompletableFutureUtils.exceptionallyCompletedFuture(badGatewayException)); - - final CustomObject> - lastSyncCustomObjectCustomObject = mockLastSyncCustomObject(ZonedDateTime.now()); - when(targetClient.execute(any(CustomObjectUpsertCommand.class))) - .thenReturn(CompletableFuture.completedFuture(lastSyncCustomObjectCustomObject)); + final ByProjectKeyCustomObjectsPost customObjectsPost = + mock(ByProjectKeyCustomObjectsPost.class); + final CustomObject lastSyncCustomObjectCustomObject = + mockLastSyncCustomObject(ZonedDateTime.now()); + final ApiHttpResponse apiHttpResponse = mock(ApiHttpResponse.class); + when(apiHttpResponse.getBody()).thenReturn(lastSyncCustomObjectCustomObject); + when(customObjectsPost.execute()) + .thenReturn(CompletableFuture.completedFuture(apiHttpResponse)); + when(targetClient.customObjects()).thenReturn(mock()); + when(targetClient.customObjects().withContainerAndKey(anyString(), anyString())) + .thenReturn(mock()); + when(targetClient.customObjects().withContainerAndKey(anyString(), anyString()).get()) + .thenReturn(mock()); + when(targetClient.customObjects().withContainerAndKey(anyString(), anyString()).get().execute()) + .thenReturn( + CompletableFutureUtils.exceptionallyCompletedFuture(createBadGatewayException())); + when(targetClient.customObjects().post(any(CustomObjectDraft.class))) + .thenReturn(customObjectsPost); final SyncerFactory syncerFactory = SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock()); @@ -943,51 +1008,25 @@ void sync_WithErrorOnFetch_ShouldCloseClientAndCompleteExceptionally() { // assertions verifyTimestampGeneratorCustomObjectUpsertIsCalled(targetClient, "InventorySync", "bar"); - verifyLastSyncCustomObjectQuery(targetClient, "inventorySync", "bar", "foo", 1); - verify(sourceClient, times(0)).execute(any(InventoryEntryQuery.class)); + verifyLastSyncCustomObjectQuery(targetClient, "inventorySync", "bar", "testProjectKey", 1); + verify(sourceClient, times(0)).inventory(); verifyInteractionsWithClientAfterSync(sourceClient, 1); assertThat(result) .failsWithin(1, TimeUnit.SECONDS) .withThrowableOfType(ExecutionException.class) - .withCauseExactlyInstanceOf(BadGatewayException.class); + .withCauseExactlyInstanceOf(RuntimeException.class) + .satisfies( + exception -> + assertThat(exception.getCause().getCause()) + .isInstanceOf(BadGatewayException.class) + .hasMessageContaining("test")); } @Test @SuppressWarnings("unchecked") void syncAll_AsDelta_ShouldBuildSyncerAndExecuteSync() { // preparation - final SphereClient sourceClient = mock(SphereClient.class); - when(sourceClient.getConfig()).thenReturn(SphereClientConfig.of("foo", "foo", "foo")); - - final SphereClient targetClient = mock(SphereClient.class); - when(targetClient.getConfig()).thenReturn(SphereClientConfig.of("bar", "bar", "bar")); - - when(sourceClient.execute(any(ProductTypeQuery.class))) - .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - when(sourceClient.execute(any(TypeQuery.class))) - .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - when(sourceClient.execute(any(StateQuery.class))) - .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - when(sourceClient.execute(any(TaxCategoryQuery.class))) - .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - when(sourceClient.execute(any(CategoryQuery.class))) - .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - when(sourceClient.execute(any(InventoryEntryQuery.class))) - .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - when(sourceClient.execute(any(ProductProjectionQuery.class))) - .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - when(sourceClient.execute(any(CartDiscountQuery.class))) - .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - when(sourceClient.execute(any(CustomObjectQuery.class))) - .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - when(sourceClient.execute(any(CustomerQuery.class))) - .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - when(sourceClient.execute(any(ShoppingListQuery.class))) - .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - - final ZonedDateTime currentCtpTimestamp = ZonedDateTime.now(); - stubClientsCustomObjectService(targetClient, currentCtpTimestamp); - + stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); final SyncerFactory syncerFactory = SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock()); @@ -1017,32 +1056,40 @@ void syncAll_AsDelta_ShouldBuildSyncerAndExecuteSync() { targetClient, "CustomerSync", DEFAULT_RUNNER_NAME); verifyTimestampGeneratorCustomObjectUpsertIsCalled( targetClient, "ShoppingListSync", DEFAULT_RUNNER_NAME); - verify(targetClient, times(22)).execute(any(CustomObjectUpsertCommand.class)); - verifyLastSyncCustomObjectQuery(targetClient, "inventorySync", DEFAULT_RUNNER_NAME, "foo", 1); - verifyLastSyncCustomObjectQuery(targetClient, "productTypeSync", DEFAULT_RUNNER_NAME, "foo", 1); - verifyLastSyncCustomObjectQuery(targetClient, "productSync", DEFAULT_RUNNER_NAME, "foo", 1); - verifyLastSyncCustomObjectQuery(targetClient, "categorySync", DEFAULT_RUNNER_NAME, "foo", 1); - verifyLastSyncCustomObjectQuery(targetClient, "typeSync", DEFAULT_RUNNER_NAME, "foo", 1); + verify(targetClient.customObjects(), times(22)).post(any(CustomObjectDraft.class)); + verifyLastSyncCustomObjectQuery( + targetClient, "inventorySync", DEFAULT_RUNNER_NAME, "testProjectKey", 1); verifyLastSyncCustomObjectQuery( - targetClient, "cartDiscountSync", DEFAULT_RUNNER_NAME, "foo", 1); - verifyLastSyncCustomObjectQuery(targetClient, "stateSync", DEFAULT_RUNNER_NAME, "foo", 1); - verifyLastSyncCustomObjectQuery(targetClient, "taxCategorySync", DEFAULT_RUNNER_NAME, "foo", 1); + targetClient, "productTypeSync", DEFAULT_RUNNER_NAME, "testProjectKey", 1); verifyLastSyncCustomObjectQuery( - targetClient, "customObjectSync", DEFAULT_RUNNER_NAME, "foo", 1); - verifyLastSyncCustomObjectQuery(targetClient, "customerSync", DEFAULT_RUNNER_NAME, "foo", 1); + targetClient, "productSync", DEFAULT_RUNNER_NAME, "testProjectKey", 1); verifyLastSyncCustomObjectQuery( - targetClient, "shoppingListSync", DEFAULT_RUNNER_NAME, "foo", 1); - verify(sourceClient, times(1)).execute(any(ProductTypeQuery.class)); - verify(sourceClient, times(1)).execute(any(TypeQuery.class)); - verify(sourceClient, times(1)).execute(any(CategoryQuery.class)); - verify(sourceClient, times(1)).execute(any(ProductProjectionQuery.class)); - verify(sourceClient, times(1)).execute(any(InventoryEntryQuery.class)); - verify(sourceClient, times(1)).execute(any(CartDiscountQuery.class)); - verify(sourceClient, times(1)).execute(any(StateQuery.class)); - verify(sourceClient, times(1)).execute(any(TaxCategoryQuery.class)); - verify(sourceClient, times(1)).execute(any(CustomObjectQuery.class)); - verify(sourceClient, times(1)).execute(any(CustomerQuery.class)); - verify(sourceClient, times(1)).execute(any(ShoppingListQuery.class)); + targetClient, "categorySync", DEFAULT_RUNNER_NAME, "testProjectKey", 1); + verifyLastSyncCustomObjectQuery( + targetClient, "typeSync", DEFAULT_RUNNER_NAME, "testProjectKey", 1); + verifyLastSyncCustomObjectQuery( + targetClient, "cartDiscountSync", DEFAULT_RUNNER_NAME, "testProjectKey", 1); + verifyLastSyncCustomObjectQuery( + targetClient, "stateSync", DEFAULT_RUNNER_NAME, "testProjectKey", 1); + verifyLastSyncCustomObjectQuery( + targetClient, "taxCategorySync", DEFAULT_RUNNER_NAME, "testProjectKey", 1); + verifyLastSyncCustomObjectQuery( + targetClient, "customObjectSync", DEFAULT_RUNNER_NAME, "testProjectKey", 1); + verifyLastSyncCustomObjectQuery( + targetClient, "customerSync", DEFAULT_RUNNER_NAME, "testProjectKey", 1); + verifyLastSyncCustomObjectQuery( + targetClient, "shoppingListSync", DEFAULT_RUNNER_NAME, "testProjectKey", 1); + verify(sourceClient, times(1)).productTypes(); + verify(sourceClient, times(1)).types(); + verify(sourceClient, times(1)).categories(); + verify(sourceClient, times(1)).productProjections(); + verify(sourceClient, times(1)).inventory(); + verify(sourceClient, times(1)).cartDiscounts(); + verify(sourceClient, times(1)).states(); + verify(sourceClient, times(1)).taxCategories(); + verify(sourceClient, times(1)).customObjects(); + verify(sourceClient, times(1)).customers(); + verify(sourceClient, times(1)).shoppingLists(); verifyInteractionsWithClientAfterSync(sourceClient, 11); assertThat(cliRunnerTestLogger.getAllLoggingEvents()) @@ -1082,24 +1129,7 @@ void syncAll_AsDelta_ShouldBuildSyncerAndExecuteSync() { @SuppressWarnings("unchecked") void syncProductTypesProductsCustomersAndShoppingLists_AsDelta_ShouldBuildSyncerAndExecuteSync() { // preparation - final SphereClient sourceClient = mock(SphereClient.class); - when(sourceClient.getConfig()).thenReturn(SphereClientConfig.of("foo", "foo", "foo")); - - final SphereClient targetClient = mock(SphereClient.class); - when(targetClient.getConfig()).thenReturn(SphereClientConfig.of("bar", "bar", "bar")); - - when(sourceClient.execute(any(ProductTypeQuery.class))) - .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - when(sourceClient.execute(any(ProductProjectionQuery.class))) - .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - when(sourceClient.execute(any(CustomerQuery.class))) - .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - when(sourceClient.execute(any(ShoppingListQuery.class))) - .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - - final ZonedDateTime currentCtpTimestamp = ZonedDateTime.now(); - stubClientsCustomObjectService(targetClient, currentCtpTimestamp); - + stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); final SyncerFactory syncerFactory = SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock()); @@ -1116,12 +1146,15 @@ void syncProductTypesProductsCustomersAndShoppingLists_AsDelta_ShouldBuildSyncer targetClient, "CustomerSync", DEFAULT_RUNNER_NAME); verifyTimestampGeneratorCustomObjectUpsertIsCalled( targetClient, "ShoppingListSync", DEFAULT_RUNNER_NAME); - verify(targetClient, times(8)).execute(any(CustomObjectUpsertCommand.class)); - verifyLastSyncCustomObjectQuery(targetClient, "productTypeSync", DEFAULT_RUNNER_NAME, "foo", 1); - verifyLastSyncCustomObjectQuery(targetClient, "productSync", DEFAULT_RUNNER_NAME, "foo", 1); - verifyLastSyncCustomObjectQuery(targetClient, "customerSync", DEFAULT_RUNNER_NAME, "foo", 1); + verify(targetClient.customObjects(), times(8)).post(any(CustomObjectDraft.class)); + verifyLastSyncCustomObjectQuery( + targetClient, "productTypeSync", DEFAULT_RUNNER_NAME, "testProjectKey", 1); + verifyLastSyncCustomObjectQuery( + targetClient, "productSync", DEFAULT_RUNNER_NAME, "testProjectKey", 1); verifyLastSyncCustomObjectQuery( - targetClient, "shoppingListSync", DEFAULT_RUNNER_NAME, "foo", 1); + targetClient, "customerSync", DEFAULT_RUNNER_NAME, "testProjectKey", 1); + verifyLastSyncCustomObjectQuery( + targetClient, "shoppingListSync", DEFAULT_RUNNER_NAME, "testProjectKey", 1); final InOrder inOrder = Mockito.inOrder(sourceClient); @@ -1130,12 +1163,10 @@ void syncProductTypesProductsCustomersAndShoppingLists_AsDelta_ShouldBuildSyncer // Example: Given: ['productTypes', 'customers', 'products', 'shoppingLists'] // From the given arguments, algorithm will group the resources as below, // [productTypes, customers] [products] [shoppingLists] - inOrder.verify(sourceClient, times(1)).execute(any(ProductTypeQuery.class)); - verify(sourceClient, times(1)).execute(any(CustomerQuery.class)); - - inOrder.verify(sourceClient, times(1)).execute(any(ProductProjectionQuery.class)); - - inOrder.verify(sourceClient, times(1)).execute(any(ShoppingListQuery.class)); + inOrder.verify(sourceClient, times(1)).productTypes(); + verify(sourceClient, times(1)).customers(); + inOrder.verify(sourceClient, times(1)).productProjections(); + inOrder.verify(sourceClient, times(1)).shoppingLists(); verifyInteractionsWithClientAfterSync(sourceClient, 4); assertProductTypeSyncerLoggingEvents(productTypeSyncerTestLogger, 0); @@ -1148,22 +1179,7 @@ void syncProductTypesProductsCustomersAndShoppingLists_AsDelta_ShouldBuildSyncer @SuppressWarnings("unchecked") void syncStatesInventoryEntriesAndCustomObjects_AsDelta_ShouldBuildSyncerAndExecuteSync() { // preparation - final SphereClient sourceClient = mock(SphereClient.class); - when(sourceClient.getConfig()).thenReturn(SphereClientConfig.of("foo", "foo", "foo")); - - final SphereClient targetClient = mock(SphereClient.class); - when(targetClient.getConfig()).thenReturn(SphereClientConfig.of("bar", "bar", "bar")); - - when(sourceClient.execute(any(StateQuery.class))) - .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - when(sourceClient.execute(any(InventoryEntryQuery.class))) - .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - when(sourceClient.execute(any(CustomObjectQuery.class))) - .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - - final ZonedDateTime currentCtpTimestamp = ZonedDateTime.now(); - stubClientsCustomObjectService(targetClient, currentCtpTimestamp); - + stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); final SyncerFactory syncerFactory = SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock()); @@ -1178,14 +1194,17 @@ void syncStatesInventoryEntriesAndCustomObjects_AsDelta_ShouldBuildSyncerAndExec targetClient, "InventorySync", DEFAULT_RUNNER_NAME); verifyTimestampGeneratorCustomObjectUpsertIsCalled( targetClient, "CustomObjectSync", DEFAULT_RUNNER_NAME); - verify(targetClient, times(6)).execute(any(CustomObjectUpsertCommand.class)); - verifyLastSyncCustomObjectQuery(targetClient, "stateSync", DEFAULT_RUNNER_NAME, "foo", 1); - verifyLastSyncCustomObjectQuery(targetClient, "inventorySync", DEFAULT_RUNNER_NAME, "foo", 1); + verify(targetClient.customObjects(), times(6)).post(any(CustomObjectDraft.class)); verifyLastSyncCustomObjectQuery( - targetClient, "customObjectSync", DEFAULT_RUNNER_NAME, "foo", 1); - verify(sourceClient, times(1)).execute(any(StateQuery.class)); - verify(sourceClient, times(1)).execute(any(InventoryEntryQuery.class)); - verify(sourceClient, times(1)).execute(any(CustomObjectQuery.class)); + targetClient, "stateSync", DEFAULT_RUNNER_NAME, "testProjectKey", 1); + verifyLastSyncCustomObjectQuery( + targetClient, "inventorySync", DEFAULT_RUNNER_NAME, "testProjectKey", 1); + verifyLastSyncCustomObjectQuery( + targetClient, "customObjectSync", DEFAULT_RUNNER_NAME, "testProjectKey", 1); + verify(sourceClient, times(1)).states(); + verify(sourceClient, times(1)).inventory(); + verify(sourceClient, times(1)).customObjects(); + verifyInteractionsWithClientAfterSync(sourceClient, 3); assertStateSyncerLoggingEvents(stateSyncerTestLogger, 0); @@ -1197,19 +1216,7 @@ void syncStatesInventoryEntriesAndCustomObjects_AsDelta_ShouldBuildSyncerAndExec @SuppressWarnings("unchecked") void syncTypesAndCategories_AsDelta_ShouldBuildSyncerAndExecuteSync() { // preparation - final SphereClient sourceClient = mock(SphereClient.class); - when(sourceClient.getConfig()).thenReturn(SphereClientConfig.of("foo", "foo", "foo")); - - final SphereClient targetClient = mock(SphereClient.class); - when(targetClient.getConfig()).thenReturn(SphereClientConfig.of("bar", "bar", "bar")); - - when(sourceClient.execute(any(TypeQuery.class))) - .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - when(sourceClient.execute(any(CategoryQuery.class))) - .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - - final ZonedDateTime currentCtpTimestamp = ZonedDateTime.now(); - stubClientsCustomObjectService(targetClient, currentCtpTimestamp); + stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); final SyncerFactory syncerFactory = SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock()); @@ -1223,14 +1230,16 @@ void syncTypesAndCategories_AsDelta_ShouldBuildSyncerAndExecuteSync() { targetClient, "TypeSync", DEFAULT_RUNNER_NAME); verifyTimestampGeneratorCustomObjectUpsertIsCalled( targetClient, "CategorySync", DEFAULT_RUNNER_NAME); - verify(targetClient, times(4)).execute(any(CustomObjectUpsertCommand.class)); - verifyLastSyncCustomObjectQuery(targetClient, "typeSync", DEFAULT_RUNNER_NAME, "foo", 1); - verifyLastSyncCustomObjectQuery(targetClient, "categorySync", DEFAULT_RUNNER_NAME, "foo", 1); + verify(targetClient.customObjects(), times(4)).post(any(CustomObjectDraft.class)); + verifyLastSyncCustomObjectQuery( + targetClient, "typeSync", DEFAULT_RUNNER_NAME, "testProjectKey", 1); + verifyLastSyncCustomObjectQuery( + targetClient, "categorySync", DEFAULT_RUNNER_NAME, "testProjectKey", 1); final InOrder inOrder = Mockito.inOrder(sourceClient); - inOrder.verify(sourceClient, times(1)).execute(any(TypeQuery.class)); - inOrder.verify(sourceClient, times(1)).execute(any(CategoryQuery.class)); + inOrder.verify(sourceClient, times(1)).types(); + inOrder.verify(sourceClient, times(1)).categories(); verifyInteractionsWithClientAfterSync(sourceClient, 2); assertTypeSyncerLoggingEvents(typeSyncerTestLogger, 0); @@ -1243,19 +1252,7 @@ void syncTypesAndCategories_AsDelta_ShouldBuildSyncerAndExecuteSync() { @SuppressWarnings("unchecked") void syncProductsAndShoppingLists_AsDelta_ShouldBuildSyncerAndExecuteSync() { // preparation - final SphereClient sourceClient = mock(SphereClient.class); - when(sourceClient.getConfig()).thenReturn(SphereClientConfig.of("foo", "foo", "foo")); - - final SphereClient targetClient = mock(SphereClient.class); - when(targetClient.getConfig()).thenReturn(SphereClientConfig.of("bar", "bar", "bar")); - - when(sourceClient.execute(any(ProductProjectionQuery.class))) - .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - when(sourceClient.execute(any(ShoppingListQuery.class))) - .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - - final ZonedDateTime currentCtpTimestamp = ZonedDateTime.now(); - stubClientsCustomObjectService(targetClient, currentCtpTimestamp); + stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); final SyncerFactory syncerFactory = SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock()); @@ -1269,12 +1266,13 @@ void syncProductsAndShoppingLists_AsDelta_ShouldBuildSyncerAndExecuteSync() { targetClient, "ProductSync", DEFAULT_RUNNER_NAME); verifyTimestampGeneratorCustomObjectUpsertIsCalled( targetClient, "ShoppingListSync", DEFAULT_RUNNER_NAME); - verify(targetClient, times(4)).execute(any(CustomObjectUpsertCommand.class)); - verifyLastSyncCustomObjectQuery(targetClient, "productSync", DEFAULT_RUNNER_NAME, "foo", 1); + verify(targetClient.customObjects(), times(4)).post(any(CustomObjectDraft.class)); + verifyLastSyncCustomObjectQuery( + targetClient, "productSync", DEFAULT_RUNNER_NAME, "testProjectKey", 1); verifyLastSyncCustomObjectQuery( - targetClient, "shoppingListSync", DEFAULT_RUNNER_NAME, "foo", 1); - verify(sourceClient, times(1)).execute(any(ProductProjectionQuery.class)); - verify(sourceClient, times(1)).execute(any(ShoppingListQuery.class)); + targetClient, "shoppingListSync", DEFAULT_RUNNER_NAME, "testProjectKey", 1); + verify(sourceClient, times(1)).productProjections(); + verify(sourceClient, times(1)).shoppingLists(); verifyInteractionsWithClientAfterSync(sourceClient, 2); assertProductSyncerLoggingEvents(productSyncerTestLogger, 0); @@ -1285,12 +1283,6 @@ void syncProductsAndShoppingLists_AsDelta_ShouldBuildSyncerAndExecuteSync() { @Test void sync_AsDelta_WithOneUnmatchedSyncOptionValue_ShouldResultIllegalArgumentException() { - final SphereClient sourceClient = mock(SphereClient.class); - when(sourceClient.getConfig()).thenReturn(SphereClientConfig.of("foo", "foo", "foo")); - - final SphereClient targetClient = mock(SphereClient.class); - when(targetClient.getConfig()).thenReturn(SphereClientConfig.of("bar", "bar", "bar")); - final SyncerFactory syncerFactory = SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock()); @@ -1312,12 +1304,6 @@ void sync_AsDelta_WithOneUnmatchedSyncOptionValue_ShouldResultIllegalArgumentExc @Test void sync_AsDelta_WithSyncOptionValuesAndAll_ShouldResultIllegalArgumentException() { - final SphereClient sourceClient = mock(SphereClient.class); - when(sourceClient.getConfig()).thenReturn(SphereClientConfig.of("foo", "foo", "foo")); - - final SphereClient targetClient = mock(SphereClient.class); - when(targetClient.getConfig()).thenReturn(SphereClientConfig.of("bar", "bar", "bar")); - final SyncerFactory syncerFactory = SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock()); @@ -1337,4 +1323,13 @@ void sync_AsDelta_WithSyncOptionValuesAndAll_ShouldResultIllegalArgumentExceptio .withCauseExactlyInstanceOf(CliException.class) .withMessageContaining(errorMessage); } + + private void verifyInteractionsWithClientAfterSync( + @Nonnull final ProjectApiRoot client, final int numberOfGetConfigInvocations) { + + verify(client, times(1)).close(); + // Verify config is accessed for the success message after sync: + // " example: Syncing products from CTP project with key 'x' to project with key 'y' is done"," + verify(client, times(numberOfGetConfigInvocations)).getProjectKey(); + } } diff --git a/src/test/java/com/commercetools/project/sync/cartdiscount/CartDiscountSyncerTest.java b/src/test/java/com/commercetools/project/sync/cartdiscount/CartDiscountSyncerTest.java index 3139fd5d..97f349fc 100644 --- a/src/test/java/com/commercetools/project/sync/cartdiscount/CartDiscountSyncerTest.java +++ b/src/test/java/com/commercetools/project/sync/cartdiscount/CartDiscountSyncerTest.java @@ -1,24 +1,24 @@ package com.commercetools.project.sync.cartdiscount; -import static com.commercetools.project.sync.util.TestUtils.getMockedClock; -import static com.commercetools.project.sync.util.TestUtils.mockResourceIdsGraphQlRequest; +import static com.commercetools.project.sync.util.TestUtils.*; import static com.commercetools.sync.cartdiscounts.utils.CartDiscountTransformUtils.toCartDiscountDrafts; -import static io.sphere.sdk.json.SphereJsonUtils.readObjectFromResource; +import static java.lang.String.format; import static java.util.Arrays.asList; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import com.commercetools.api.client.ByProjectKeyCartDiscountsGet; +import com.commercetools.api.client.ByProjectKeyCartDiscountsRequestBuilder; +import com.commercetools.api.client.ProjectApiRoot; +import com.commercetools.api.models.cart_discount.CartDiscount; +import com.commercetools.api.models.cart_discount.CartDiscountDraft; +import com.commercetools.api.models.cart_discount.CartDiscountPagedQueryResponse; import com.commercetools.sync.cartdiscounts.CartDiscountSync; import com.commercetools.sync.commons.utils.CaffeineReferenceIdToKeyCacheImpl; import com.commercetools.sync.commons.utils.ReferenceIdToKeyCache; -import io.sphere.sdk.cartdiscounts.CartDiscount; -import io.sphere.sdk.cartdiscounts.CartDiscountDraft; -import io.sphere.sdk.cartdiscounts.queries.CartDiscountQuery; -import io.sphere.sdk.client.SphereApiConfig; -import io.sphere.sdk.client.SphereClient; -import io.sphere.sdk.queries.PagedQueryResult; +import io.vrap.rmf.base.client.ApiHttpResponse; import java.time.Clock; import java.util.Collections; import java.util.List; @@ -36,22 +36,28 @@ class CartDiscountSyncerTest { @Test void of_ShouldCreateCartDiscountSyncerInstance() { + final ProjectApiRoot sourceClient = mock(ProjectApiRoot.class); + final ByProjectKeyCartDiscountsRequestBuilder byProjectKeyCartDiscountsRequestBuilder = mock(); + when(sourceClient.cartDiscounts()).thenReturn(byProjectKeyCartDiscountsRequestBuilder); + final ByProjectKeyCartDiscountsGet byProjectKeyCartDiscountsGet = mock(); + when(byProjectKeyCartDiscountsRequestBuilder.get()).thenReturn(byProjectKeyCartDiscountsGet); + // test final CartDiscountSyncer cartDiscountSyncer = - CartDiscountSyncer.of(mock(SphereClient.class), mock(SphereClient.class), getMockedClock()); + CartDiscountSyncer.of(sourceClient, mock(ProjectApiRoot.class), getMockedClock()); // assertions assertThat(cartDiscountSyncer).isNotNull(); - assertThat(cartDiscountSyncer.getQuery()).isEqualTo(CartDiscountQuery.of()); + assertThat(cartDiscountSyncer.getQuery()).isEqualTo(byProjectKeyCartDiscountsGet); assertThat(cartDiscountSyncer.getSync()).isExactlyInstanceOf(CartDiscountSync.class); } @Test void transform_ShouldReplaceCartDiscountReferenceIdsWithKeys() { // preparation - final SphereClient sourceClient = mock(SphereClient.class); + final ProjectApiRoot sourceClient = mock(ProjectApiRoot.class); final CartDiscountSyncer cartDiscountSyncer = - CartDiscountSyncer.of(sourceClient, mock(SphereClient.class), getMockedClock()); + CartDiscountSyncer.of(sourceClient, mock(ProjectApiRoot.class), getMockedClock()); final List cartDiscountPage = asList( readObjectFromResource("cart-discount-key-1.json", CartDiscount.class), @@ -62,7 +68,10 @@ void transform_ShouldReplaceCartDiscountReferenceIdsWithKeys() { .map(cartDiscount -> cartDiscount.getCustom().getType().getId()) .collect(Collectors.toList()); mockResourceIdsGraphQlRequest( - sourceClient, "4db98ea6-38dc-4ccb-b20f-466e1566fd03", "test cart discount custom type"); + sourceClient, + "typeDefinitions", + "4db98ea6-38dc-4ccb-b20f-466e1566fd03", + "test cart discount custom type"); // test final CompletionStage> draftsFromPageStage = @@ -79,35 +88,34 @@ void transform_ShouldReplaceCartDiscountReferenceIdsWithKeys() { assertThat(draftsFromPageStage).isCompletedWithValue(expectedResult); } - @Test - void getQuery_ShouldBuildCartDiscountQuery() { - // preparation - final CartDiscountSyncer cartDiscountSyncer = - CartDiscountSyncer.of(mock(SphereClient.class), mock(SphereClient.class), getMockedClock()); - - // test - final CartDiscountQuery query = cartDiscountSyncer.getQuery(); - - // assertion - assertThat(query).isEqualTo(CartDiscountQuery.of()); - } - @Test void syncWithError_ShouldCallErrorCallback() { final TestLogger syncerTestLogger = TestLoggerFactory.getTestLogger(CartDiscountSyncer.class); // preparation: cart discount with no key is synced - final SphereClient sourceClient = mock(SphereClient.class); - final SphereClient targetClient = mock(SphereClient.class); - when(sourceClient.getConfig()).thenReturn(SphereApiConfig.of("source-project")); - when(targetClient.getConfig()).thenReturn(SphereApiConfig.of("target-project")); + final ProjectApiRoot sourceClient = mock(ProjectApiRoot.class); + final ProjectApiRoot targetClient = mock(ProjectApiRoot.class); final List cartDiscounts = Collections.singletonList( readObjectFromResource("cart-discount-no-key.json", CartDiscount.class)); - final PagedQueryResult pagedQueryResult = mock(PagedQueryResult.class); - when(pagedQueryResult.getResults()).thenReturn(cartDiscounts); - when(sourceClient.execute(any(CartDiscountQuery.class))) - .thenReturn(CompletableFuture.completedFuture(pagedQueryResult)); + final ApiHttpResponse apiHttpResponse = + mock(ApiHttpResponse.class); + final CartDiscountPagedQueryResponse cartDiscountPagedQueryResponse = + mock(CartDiscountPagedQueryResponse.class); + when(cartDiscountPagedQueryResponse.getResults()).thenReturn(cartDiscounts); + when(apiHttpResponse.getBody()).thenReturn(cartDiscountPagedQueryResponse); + + final ByProjectKeyCartDiscountsRequestBuilder byProjectKeyCartDiscountsRequestBuilder = mock(); + when(sourceClient.cartDiscounts()).thenReturn(byProjectKeyCartDiscountsRequestBuilder); + final ByProjectKeyCartDiscountsGet byProjectKeyCartDiscountsGet = mock(); + when(byProjectKeyCartDiscountsRequestBuilder.get()).thenReturn(byProjectKeyCartDiscountsGet); + when(byProjectKeyCartDiscountsGet.withLimit(anyInt())).thenReturn(byProjectKeyCartDiscountsGet); + when(byProjectKeyCartDiscountsGet.withSort(anyString())) + .thenReturn(byProjectKeyCartDiscountsGet); + when(byProjectKeyCartDiscountsGet.withWithTotal(anyBoolean())) + .thenReturn(byProjectKeyCartDiscountsGet); + when(byProjectKeyCartDiscountsGet.execute()) + .thenReturn(CompletableFuture.completedFuture(apiHttpResponse)); // test final CartDiscountSyncer cartDiscountSyncer = @@ -121,6 +129,8 @@ void syncWithError_ShouldCallErrorCallback() { "Error when trying to sync cart discount. Existing key: <>. Update actions: []"); assertThat(errorLog.getThrowable().get().getMessage()) .isEqualTo( - "CartDiscountDraft with name: LocalizedString(en -> 1-month prepay(Go Big)) doesn't have a key. Please make sure all cart discount drafts have keys."); + format( + "CartDiscountDraft with name: %s doesn't have a key. Please make sure all cart discount drafts have keys.", + cartDiscounts.get(0).getName().toString())); } } diff --git a/src/test/java/com/commercetools/project/sync/category/CategorySyncerTest.java b/src/test/java/com/commercetools/project/sync/category/CategorySyncerTest.java index ae16642c..0103eec3 100644 --- a/src/test/java/com/commercetools/project/sync/category/CategorySyncerTest.java +++ b/src/test/java/com/commercetools/project/sync/category/CategorySyncerTest.java @@ -1,34 +1,31 @@ package com.commercetools.project.sync.category; -import static com.commercetools.project.sync.util.TestUtils.getMockedClock; +import static com.commercetools.project.sync.util.TestUtils.*; import static com.commercetools.sync.categories.utils.CategoryTransformUtils.toCategoryDrafts; -import static io.sphere.sdk.json.SphereJsonUtils.readObjectFromResource; import static java.lang.String.format; import static java.util.Arrays.asList; -import static java.util.Collections.singleton; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import com.commercetools.api.client.ByProjectKeyCategoriesGet; +import com.commercetools.api.client.ByProjectKeyCategoriesKeyByKeyGet; +import com.commercetools.api.client.ByProjectKeyCategoriesKeyByKeyRequestBuilder; +import com.commercetools.api.client.ByProjectKeyCategoriesRequestBuilder; +import com.commercetools.api.client.ProjectApiRoot; +import com.commercetools.api.models.category.Category; +import com.commercetools.api.models.category.CategoryDraft; +import com.commercetools.api.models.category.CategoryPagedQueryResponse; +import com.commercetools.api.models.category.CategoryPagedQueryResponseBuilder; import com.commercetools.sync.categories.CategorySync; -import com.commercetools.sync.commons.helpers.ResourceKeyIdGraphQlRequest; -import com.commercetools.sync.commons.models.ResourceIdsGraphQlRequest; -import com.commercetools.sync.commons.models.ResourceKeyId; -import com.commercetools.sync.commons.models.ResourceKeyIdGraphQlResult; import com.commercetools.sync.commons.utils.CaffeineReferenceIdToKeyCacheImpl; import com.commercetools.sync.commons.utils.ReferenceIdToKeyCache; -import io.sphere.sdk.categories.Category; -import io.sphere.sdk.categories.CategoryDraft; -import io.sphere.sdk.categories.queries.CategoryQuery; -import io.sphere.sdk.client.SphereApiConfig; -import io.sphere.sdk.client.SphereClient; -import io.sphere.sdk.json.SphereJsonUtils; -import io.sphere.sdk.queries.PagedQueryResult; +import com.fasterxml.jackson.core.JsonProcessingException; +import io.vrap.rmf.base.client.ApiHttpResponse; import java.time.Clock; import java.util.Collections; import java.util.List; -import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; import java.util.stream.Collectors; @@ -52,22 +49,26 @@ void setup() { @Test void of_ShouldCreateCategorySyncerInstance() { // test + final ProjectApiRoot projectApiRoot = mock(ProjectApiRoot.class); + final ByProjectKeyCategoriesRequestBuilder byProjectKeyCategoriesRequestBuilder = mock(); + when(projectApiRoot.categories()).thenReturn(byProjectKeyCategoriesRequestBuilder); + final ByProjectKeyCategoriesGet byProjectKeyCategoriesGet = mock(); + when(byProjectKeyCategoriesRequestBuilder.get()).thenReturn(byProjectKeyCategoriesGet); final CategorySyncer categorySyncer = - CategorySyncer.of(mock(SphereClient.class), mock(SphereClient.class), getMockedClock()); + CategorySyncer.of(projectApiRoot, projectApiRoot, getMockedClock()); // assertions - CategoryQuery expectedQuery = CategoryQuery.of(); assertThat(categorySyncer).isNotNull(); - assertThat(categorySyncer.getQuery()).isEqualTo(expectedQuery); + assertThat(categorySyncer.getQuery()).isEqualTo(byProjectKeyCategoriesGet); assertThat(categorySyncer.getSync()).isExactlyInstanceOf(CategorySync.class); } @Test - void transform_ShouldReplaceCategoryReferenceIdsWithKeys() { + void transform_ShouldReplaceCategoryReferenceIdsWithKeys() throws JsonProcessingException { // preparation - final SphereClient sourceClient = mock(SphereClient.class); + final ProjectApiRoot sourceClient = mock(ProjectApiRoot.class); final CategorySyncer categorySyncer = - CategorySyncer.of(sourceClient, mock(SphereClient.class), getMockedClock()); + CategorySyncer.of(sourceClient, mock(ProjectApiRoot.class), getMockedClock()); final List categoryPage = asList( readObjectFromResource("category-key-1.json", Category.class), @@ -78,13 +79,8 @@ void transform_ShouldReplaceCategoryReferenceIdsWithKeys() { .map(category -> category.getCustom().getType().getId()) .collect(Collectors.toList()); - final String jsonStringCustomTypes = - "{\"results\":[{\"id\":\"53c4a8b4-754f-4b95-b6f2-3e1e70e3d0c3\"," + "\"key\":\"cat1\"} ]}"; - final ResourceKeyIdGraphQlResult customTypesResult = - SphereJsonUtils.readObject(jsonStringCustomTypes, ResourceKeyIdGraphQlResult.class); - - when(sourceClient.execute(any(ResourceIdsGraphQlRequest.class))) - .thenReturn(CompletableFuture.completedFuture(customTypesResult)); + mockResourceIdsGraphQlRequest( + sourceClient, "typeDefinitions", "53c4a8b4-754f-4b95-b6f2-3e1e70e3d0c3", "cat1"); // test final CompletionStage> draftsFromPageStage = @@ -102,38 +98,35 @@ void transform_ShouldReplaceCategoryReferenceIdsWithKeys() { assertThat(draftsFromPageStage).isCompletedWithValue(expectedResult); } - @Test - void getQuery_ShouldBuildCategoryQuery() { - // preparation - final CategorySyncer categorySyncer = - CategorySyncer.of(mock(SphereClient.class), mock(SphereClient.class), getMockedClock()); - - // test - final CategoryQuery query = categorySyncer.getQuery(); - - // assertion - CategoryQuery expectedQuery = CategoryQuery.of(); - assertThat(query).isEqualTo(expectedQuery); - } - @Test void syncWithError_ShouldCallErrorCallback() { // preparation: category with no key is synced - final SphereClient sourceClient = mock(SphereClient.class); - final SphereClient targetClient = mock(SphereClient.class); - when(sourceClient.getConfig()).thenReturn(SphereApiConfig.of("source-project")); - when(targetClient.getConfig()).thenReturn(SphereApiConfig.of("target-project")); + final ProjectApiRoot sourceClient = mock(ProjectApiRoot.class); + final ByProjectKeyCategoriesRequestBuilder byProjectKeyCategoriesRequestBuilder = mock(); + when(sourceClient.categories()).thenReturn(byProjectKeyCategoriesRequestBuilder); + final ByProjectKeyCategoriesGet byProjectKeyCategoriesGet = mock(); + when(byProjectKeyCategoriesRequestBuilder.get()).thenReturn(byProjectKeyCategoriesGet); + when(byProjectKeyCategoriesGet.withSort(anyString())).thenReturn(byProjectKeyCategoriesGet); + when(byProjectKeyCategoriesGet.withLimit(anyInt())).thenReturn(byProjectKeyCategoriesGet); + when(byProjectKeyCategoriesGet.withWithTotal(anyBoolean())) + .thenReturn(byProjectKeyCategoriesGet); + final ApiHttpResponse response = mock(ApiHttpResponse.class); final List categories = Collections.singletonList(readObjectFromResource("category-no-key.json", Category.class)); - - final PagedQueryResult pagedQueryResult = mock(PagedQueryResult.class); - when(pagedQueryResult.getResults()).thenReturn(categories); - when(sourceClient.execute(any(CategoryQuery.class))) - .thenReturn(CompletableFuture.completedFuture(pagedQueryResult)); + final CategoryPagedQueryResponse categoryPagedQueryResponse = + CategoryPagedQueryResponseBuilder.of() + .results(categories) + .limit(20L) + .offset(0L) + .count(1L) + .build(); + when(response.getBody()).thenReturn(categoryPagedQueryResponse); + when(byProjectKeyCategoriesGet.execute()) + .thenReturn(CompletableFuture.completedFuture(response)); // test final CategorySyncer categorySyncer = - CategorySyncer.of(sourceClient, targetClient, mock(Clock.class)); + CategorySyncer.of(sourceClient, mock(ProjectApiRoot.class), mock(Clock.class)); categorySyncer.sync(null, true).toCompletableFuture().join(); // assertion @@ -143,42 +136,83 @@ void syncWithError_ShouldCallErrorCallback() { "Error when trying to sync category. Existing key: <>. Update actions: []"); assertThat(errorLog.getThrowable().get().getMessage()) .isEqualTo( - "CategoryDraft with name: LocalizedString(en -> category-name-1) doesn't have a key. Please make sure all category drafts have keys."); + format( + "CategoryDraft with name: %s doesn't have a key. Please make sure all category drafts have keys.", + categories.get(0).getName().toString())); } @Test - void syncWithWarning_ShouldCallWarningCallback() { + void syncWithWarning_ShouldCallWarningCallback() throws JsonProcessingException { // preparation: old category has category order hint, // new category does not have category order hint - final SphereClient sourceClient = mock(SphereClient.class); - final SphereClient targetClient = mock(SphereClient.class); - when(sourceClient.getConfig()).thenReturn(SphereApiConfig.of("source-project")); - when(targetClient.getConfig()).thenReturn(SphereApiConfig.of("target-project")); - + final ProjectApiRoot sourceClient = mock(ProjectApiRoot.class); + final ByProjectKeyCategoriesRequestBuilder byProjectKeyCategoriesRequestBuilder = mock(); + when(sourceClient.categories()).thenReturn(byProjectKeyCategoriesRequestBuilder); + final ByProjectKeyCategoriesGet byProjectKeyCategoriesGetSource = mock(); + when(byProjectKeyCategoriesRequestBuilder.get()).thenReturn(byProjectKeyCategoriesGetSource); + final ByProjectKeyCategoriesKeyByKeyRequestBuilder + byProjectKeyCategoriesKeyByKeyRequestBuilder = mock(); + when(byProjectKeyCategoriesRequestBuilder.withKey(anyString())) + .thenReturn(byProjectKeyCategoriesKeyByKeyRequestBuilder); + final ByProjectKeyCategoriesKeyByKeyGet byProjectKeyCategoriesKeyByKeyGet = mock(); + when(byProjectKeyCategoriesKeyByKeyRequestBuilder.get()) + .thenReturn(byProjectKeyCategoriesKeyByKeyGet); + when(byProjectKeyCategoriesGetSource.withSort(anyString())) + .thenReturn(byProjectKeyCategoriesGetSource); + when(byProjectKeyCategoriesGetSource.withLimit(anyInt())) + .thenReturn(byProjectKeyCategoriesGetSource); + when(byProjectKeyCategoriesGetSource.withWithTotal(anyBoolean())) + .thenReturn(byProjectKeyCategoriesGetSource); final List sourceCategories = - Collections.singletonList(readObjectFromResource("category-key-2.json", Category.class)); - final List targetCategories = Collections.singletonList( readObjectFromResource("category-order-hint.json", Category.class)); - - final PagedQueryResult sourcePagedQueryResult = mock(PagedQueryResult.class); - when(sourcePagedQueryResult.getResults()).thenReturn(sourceCategories); - when(sourceClient.execute(any(CategoryQuery.class))) - .thenReturn(CompletableFuture.completedFuture(sourcePagedQueryResult)); - - final PagedQueryResult targetPagedQueryResult = mock(PagedQueryResult.class); - when(targetPagedQueryResult.getResults()).thenReturn(targetCategories); - when(targetClient.execute(any(CategoryQuery.class))) - .thenReturn(CompletableFuture.completedFuture(targetPagedQueryResult)); - when(targetPagedQueryResult.head()).thenReturn(Optional.of(targetCategories.get(0))); - - final ResourceKeyIdGraphQlResult resourceKeyIdGraphQlResult = - mock(ResourceKeyIdGraphQlResult.class); - when(resourceKeyIdGraphQlResult.getResults()) + final ApiHttpResponse sourceResponse = mock(ApiHttpResponse.class); + final CategoryPagedQueryResponse categoryPagedQueryResponse = + CategoryPagedQueryResponseBuilder.of() + .results(sourceCategories) + .limit(20L) + .offset(0L) + .count(1L) + .build(); + when(sourceResponse.getBody()).thenReturn(categoryPagedQueryResponse); + when(byProjectKeyCategoriesGetSource.execute()) + .thenReturn(CompletableFuture.completedFuture(sourceResponse)); + when(byProjectKeyCategoriesKeyByKeyGet.execute()) .thenReturn( - singleton(new ResourceKeyId("categoryKey2", "ba81a6da-cf83-435b-a89e-2afab579846f"))); - when(targetClient.execute(any(ResourceKeyIdGraphQlRequest.class))) - .thenReturn(CompletableFuture.completedFuture(resourceKeyIdGraphQlResult)); + CompletableFuture.completedFuture( + new ApiHttpResponse(200, null, sourceCategories.get(0)))); + + final ProjectApiRoot targetClient = mock(ProjectApiRoot.class); + final ByProjectKeyCategoriesGet byProjectKeyCategoriesGetTarget = mock(); + when(targetClient.categories()).thenReturn(byProjectKeyCategoriesRequestBuilder); + when(byProjectKeyCategoriesRequestBuilder.get()).thenReturn(byProjectKeyCategoriesGetTarget); + when(byProjectKeyCategoriesGetTarget.withWhere(anyString())) + .thenReturn(byProjectKeyCategoriesGetTarget); + when(byProjectKeyCategoriesGetTarget.withPredicateVar(anyString(), any())) + .thenReturn(byProjectKeyCategoriesGetTarget); + when(byProjectKeyCategoriesGetTarget.withLimit(anyInt())) + .thenReturn(byProjectKeyCategoriesGetTarget); + when(byProjectKeyCategoriesGetTarget.withWithTotal(anyBoolean())) + .thenReturn(byProjectKeyCategoriesGetTarget); + when(byProjectKeyCategoriesGetTarget.withSort(anyString())) + .thenReturn(byProjectKeyCategoriesGetTarget); + + final List targetCategories = + Collections.singletonList(readObjectFromResource("category-key-2.json", Category.class)); + final ApiHttpResponse targetResponse = mock(ApiHttpResponse.class); + final CategoryPagedQueryResponse categoryPagedQueryResponseTarget = + CategoryPagedQueryResponseBuilder.of() + .results(targetCategories) + .limit(20L) + .offset(0L) + .count(1L) + .build(); + when(targetResponse.getBody()).thenReturn(categoryPagedQueryResponseTarget); + when(byProjectKeyCategoriesGetTarget.execute()) + .thenReturn(CompletableFuture.completedFuture(targetResponse)); + + mockResourceIdsGraphQlRequest( + targetClient, "categories", "ba81a6da-cf83-435b-a89e-2afab579846f", "categoryKey2"); // test final CategorySyncer categorySyncer = diff --git a/src/test/java/com/commercetools/project/sync/customer/CustomerSyncerTest.java b/src/test/java/com/commercetools/project/sync/customer/CustomerSyncerTest.java index 3f310e4c..e2025fef 100644 --- a/src/test/java/com/commercetools/project/sync/customer/CustomerSyncerTest.java +++ b/src/test/java/com/commercetools/project/sync/customer/CustomerSyncerTest.java @@ -1,24 +1,24 @@ package com.commercetools.project.sync.customer; +import static com.commercetools.project.sync.util.TestUtils.mockResourceIdsGraphQlRequest; +import static com.commercetools.project.sync.util.TestUtils.readObjectFromResource; import static com.commercetools.sync.customers.utils.CustomerTransformUtils.toCustomerDrafts; -import static io.sphere.sdk.json.SphereJsonUtils.readObjectFromResource; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import com.commercetools.sync.commons.models.ResourceIdsGraphQlRequest; -import com.commercetools.sync.commons.models.ResourceKeyIdGraphQlResult; +import com.commercetools.api.client.ByProjectKeyCustomersGet; +import com.commercetools.api.client.ByProjectKeyCustomersRequestBuilder; +import com.commercetools.api.client.ProjectApiRoot; +import com.commercetools.api.models.customer.Customer; +import com.commercetools.api.models.customer.CustomerDraft; +import com.commercetools.api.models.customer.CustomerPagedQueryResponse; +import com.commercetools.api.models.customer.CustomerPagedQueryResponseBuilder; import com.commercetools.sync.commons.utils.CaffeineReferenceIdToKeyCacheImpl; import com.commercetools.sync.commons.utils.ReferenceIdToKeyCache; import com.commercetools.sync.customers.CustomerSync; -import io.sphere.sdk.client.SphereApiConfig; -import io.sphere.sdk.client.SphereClient; -import io.sphere.sdk.customers.Customer; -import io.sphere.sdk.customers.CustomerDraft; -import io.sphere.sdk.customers.queries.CustomerQuery; -import io.sphere.sdk.json.SphereJsonUtils; -import io.sphere.sdk.queries.PagedQueryResult; +import io.vrap.rmf.base.client.ApiHttpResponse; import java.time.Clock; import java.util.Collections; import java.util.List; @@ -45,7 +45,8 @@ void setup() { void of_ShouldCreateCustomerSyncerInstance() { // test final CustomerSyncer customerSyncer = - CustomerSyncer.of(mock(SphereClient.class), mock(SphereClient.class), mock(Clock.class)); + CustomerSyncer.of( + mock(ProjectApiRoot.class), mock(ProjectApiRoot.class), mock(Clock.class)); // assertion assertThat(customerSyncer).isNotNull(); @@ -55,20 +56,13 @@ void of_ShouldCreateCustomerSyncerInstance() { @Test void transform_ShouldReplaceCustomerReferenceIdsWithKeys() { // preparation - final SphereClient sourceClient = mock(SphereClient.class); + final ProjectApiRoot sourceClient = mock(ProjectApiRoot.class); final CustomerSyncer customerSyncer = - CustomerSyncer.of(sourceClient, mock(SphereClient.class), mock(Clock.class)); + CustomerSyncer.of(sourceClient, mock(ProjectApiRoot.class), mock(Clock.class)); final List customers = Collections.singletonList(readObjectFromResource("customer-key-1.json", Customer.class)); - - final String jsonStringCustomerGroups = - "{\"results\":[{\"id\":\"d1229e6f-2b79-441e-b419-180311e52754\"," - + "\"key\":\"customerGroupKey\"} ]}"; - final ResourceKeyIdGraphQlResult customerGroupsResult = - SphereJsonUtils.readObject(jsonStringCustomerGroups, ResourceKeyIdGraphQlResult.class); - - when(sourceClient.execute(any(ResourceIdsGraphQlRequest.class))) - .thenReturn(CompletableFuture.completedFuture(customerGroupsResult)); + mockResourceIdsGraphQlRequest( + sourceClient, "customerGroups", "d1229e6f-2b79-441e-b419-180311e52754", "customerGroupKey"); // test final CompletionStage> draftsFromPageStage = @@ -82,33 +76,31 @@ void transform_ShouldReplaceCustomerReferenceIdsWithKeys() { @Test void getQuery_ShouldBuildCustomerQuery() { + final ProjectApiRoot projectApiRoot = mock(ProjectApiRoot.class); + final ByProjectKeyCustomersRequestBuilder byProjectKeyCustomersRequestBuilder = mock(); + when(projectApiRoot.customers()).thenReturn(byProjectKeyCustomersRequestBuilder); + final ByProjectKeyCustomersGet byProjectKeyCustomersGet = mock(); + when(byProjectKeyCustomersRequestBuilder.get()).thenReturn(byProjectKeyCustomersGet); + // test final CustomerSyncer customerSyncer = - CustomerSyncer.of(mock(SphereClient.class), mock(SphereClient.class), mock(Clock.class)); + CustomerSyncer.of(projectApiRoot, mock(ProjectApiRoot.class), mock(Clock.class)); // assertion - final CustomerQuery query = customerSyncer.getQuery(); - assertThat(query).isEqualTo(CustomerQuery.of()); + assertThat(customerSyncer.getQuery()).isEqualTo(byProjectKeyCustomersGet); } @Test void syncWithError_ShouldCallErrorCallback() { // preparation: customer with no key is synced - final SphereClient sourceClient = mock(SphereClient.class); - final SphereClient targetClient = mock(SphereClient.class); - when(sourceClient.getConfig()).thenReturn(SphereApiConfig.of("source-project")); - when(targetClient.getConfig()).thenReturn(SphereApiConfig.of("target-project")); + final ProjectApiRoot sourceClient = mock(ProjectApiRoot.class); final List customers = Collections.singletonList(readObjectFromResource("customer-no-key.json", Customer.class)); - - final PagedQueryResult pagedQueryResult = mock(PagedQueryResult.class); - when(pagedQueryResult.getResults()).thenReturn(customers); - when(sourceClient.execute(any(CustomerQuery.class))) - .thenReturn(CompletableFuture.completedFuture(pagedQueryResult)); + mockProjectApiRootGetRequest(sourceClient, customers); // test final CustomerSyncer customerSyncer = - CustomerSyncer.of(sourceClient, targetClient, mock(Clock.class)); + CustomerSyncer.of(sourceClient, mock(ProjectApiRoot.class), mock(Clock.class)); customerSyncer.sync(null, true).toCompletableFuture().join(); // assertion @@ -124,24 +116,15 @@ void syncWithError_ShouldCallErrorCallback() { @Test void syncWithWarning_ShouldCallWarningCallback() { // preparation: source customer has a different customer number than target customer - final SphereClient sourceClient = mock(SphereClient.class); - final SphereClient targetClient = mock(SphereClient.class); - when(sourceClient.getConfig()).thenReturn(SphereApiConfig.of("source-project")); - when(targetClient.getConfig()).thenReturn(SphereApiConfig.of("target-project")); + final ProjectApiRoot sourceClient = mock(ProjectApiRoot.class); + final ProjectApiRoot targetClient = mock(ProjectApiRoot.class); final List sourceCustomers = Collections.singletonList(readObjectFromResource("customer-id-1.json", Customer.class)); final List targetCustomers = Collections.singletonList(readObjectFromResource("customer-id-2.json", Customer.class)); - final PagedQueryResult sourcePagedQueryResult = mock(PagedQueryResult.class); - when(sourcePagedQueryResult.getResults()).thenReturn(sourceCustomers); - when(sourceClient.execute(any(CustomerQuery.class))) - .thenReturn(CompletableFuture.completedFuture(sourcePagedQueryResult)); - - final PagedQueryResult targetPagedQueryResult = mock(PagedQueryResult.class); - when(targetPagedQueryResult.getResults()).thenReturn(targetCustomers); - when(targetClient.execute(any(CustomerQuery.class))) - .thenReturn(CompletableFuture.completedFuture(targetPagedQueryResult)); + mockProjectApiRootGetRequest(sourceClient, sourceCustomers); + mockProjectApiRootGetRequest(targetClient, targetCustomers); // test final CustomerSyncer customerSyncer = @@ -156,4 +139,30 @@ void syncWithWarning_ShouldCallWarningCallback() { .isEqualTo( "Customer with key: \"customerKey\" has already a customer number: \"2\", once it's set it cannot be changed. Hereby, the update action is not created."); } + + private void mockProjectApiRootGetRequest( + final ProjectApiRoot projectApiRoot, final List results) { + final ByProjectKeyCustomersRequestBuilder byProjectKeyCustomersRequestBuilder = mock(); + when(projectApiRoot.customers()).thenReturn(byProjectKeyCustomersRequestBuilder); + final ByProjectKeyCustomersGet byProjectKeyCustomersGet = mock(); + when(byProjectKeyCustomersRequestBuilder.get()).thenReturn(byProjectKeyCustomersGet); + when(byProjectKeyCustomersGet.withSort(anyString())).thenReturn(byProjectKeyCustomersGet); + when(byProjectKeyCustomersGet.withWithTotal(anyBoolean())).thenReturn(byProjectKeyCustomersGet); + when(byProjectKeyCustomersGet.withLimit(anyInt())).thenReturn(byProjectKeyCustomersGet); + when(byProjectKeyCustomersGet.withWhere(anyString())).thenReturn(byProjectKeyCustomersGet); + when(byProjectKeyCustomersGet.withPredicateVar(anyString(), any())) + .thenReturn(byProjectKeyCustomersGet); + + final ApiHttpResponse response = mock(ApiHttpResponse.class); + final CustomerPagedQueryResponse customerPagedQueryResponse = + CustomerPagedQueryResponseBuilder.of() + .results(results) + .limit(20L) + .offset(0L) + .count(1L) + .build(); + when(response.getBody()).thenReturn(customerPagedQueryResponse); + when(byProjectKeyCustomersGet.execute()) + .thenReturn(CompletableFuture.completedFuture(response)); + } } diff --git a/src/test/java/com/commercetools/project/sync/customobject/CustomObjectSyncerTest.java b/src/test/java/com/commercetools/project/sync/customobject/CustomObjectSyncerTest.java index 543940d2..325450ba 100644 --- a/src/test/java/com/commercetools/project/sync/customobject/CustomObjectSyncerTest.java +++ b/src/test/java/com/commercetools/project/sync/customobject/CustomObjectSyncerTest.java @@ -4,47 +4,63 @@ import static java.util.Arrays.asList; import static java.util.stream.Collectors.toList; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.*; + +import com.commercetools.api.client.ByProjectKeyCustomObjectsGet; +import com.commercetools.api.client.ByProjectKeyCustomObjectsRequestBuilder; +import com.commercetools.api.client.ProjectApiRoot; +import com.commercetools.api.models.custom_object.CustomObject; +import com.commercetools.api.models.custom_object.CustomObjectDraft; +import com.commercetools.api.models.custom_object.CustomObjectDraftBuilder; import com.commercetools.project.sync.SyncModuleOption; import com.commercetools.project.sync.util.SyncUtils; import com.commercetools.sync.customobjects.CustomObjectSync; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.JsonNodeFactory; -import io.sphere.sdk.client.SphereClient; -import io.sphere.sdk.customobjects.CustomObject; -import io.sphere.sdk.customobjects.CustomObjectDraft; -import io.sphere.sdk.customobjects.queries.CustomObjectQuery; import java.util.List; import java.util.concurrent.CompletionStage; -import java.util.stream.Collectors; import java.util.stream.Stream; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class CustomObjectSyncerTest { + private final ProjectApiRoot sourceClient = mock(ProjectApiRoot.class); + + private final ByProjectKeyCustomObjectsGet byProjectKeyCustomObjectsGet = mock(); + + @BeforeEach + void setup() { + final ByProjectKeyCustomObjectsRequestBuilder byProjectKeyCustomObjectsRequestBuilder = mock(); + when(sourceClient.customObjects()).thenReturn(byProjectKeyCustomObjectsRequestBuilder); + when(byProjectKeyCustomObjectsRequestBuilder.get()).thenReturn(byProjectKeyCustomObjectsGet); + when(byProjectKeyCustomObjectsGet.withWhere(anyString())) + .thenReturn(byProjectKeyCustomObjectsGet); + } + @Test void of_ShouldCreateCustomObjectSyncerInstance() { - // test final String runnerName = ""; + + final List[] argument = new List[1]; + when(byProjectKeyCustomObjectsGet.withPredicateVar(eq("excludedNames"), anyCollection())) + .then( + invocation -> { + argument[0] = invocation.getArgument(1); + return byProjectKeyCustomObjectsGet; + }); + + // test final CustomObjectSyncer customObjectSyncer = CustomObjectSyncer.of( - mock(SphereClient.class), - mock(SphereClient.class), - getMockedClock(), - runnerName, - false); + sourceClient, mock(ProjectApiRoot.class), getMockedClock(), runnerName, false); // assertions final List excludedContainerNames = getExcludedContainerNames(runnerName); + assertThat(customObjectSyncer).isNotNull(); - assertThat(customObjectSyncer.getQuery()) - .isEqualTo( - CustomObjectQuery.of(JsonNode.class) - .plusPredicates( - customObjectQueryModel -> - customObjectQueryModel.container().isNotIn(excludedContainerNames))); + assertThat(customObjectSyncer.getQuery()).isEqualTo(byProjectKeyCustomObjectsGet); + assertThat(argument[0]).isEqualTo(excludedContainerNames); assertThat(customObjectSyncer.getSync()).isExactlyInstanceOf(CustomObjectSync.class); } @@ -53,21 +69,21 @@ void transform_ShouldConvertResourcesToDrafts() { // preparation final CustomObjectSyncer customObjectSyncer = CustomObjectSyncer.of( - mock(SphereClient.class), mock(SphereClient.class), getMockedClock(), "", false); + mock(ProjectApiRoot.class), mock(ProjectApiRoot.class), getMockedClock(), "", false); - final CustomObject customObject1 = mock(CustomObject.class); + final CustomObject customObject1 = mock(CustomObject.class); when(customObject1.getContainer()).thenReturn("testContainer1"); when(customObject1.getKey()).thenReturn("testKey1"); - when(customObject1.getValue()).thenReturn(JsonNodeFactory.instance.textNode("testValue1")); + when(customObject1.getValue()).thenReturn("testValue1"); - final CustomObject customObject2 = mock(CustomObject.class); + final CustomObject customObject2 = mock(CustomObject.class); when(customObject2.getContainer()).thenReturn("testContainer2"); when(customObject2.getKey()).thenReturn("testKey2"); - when(customObject2.getValue()).thenReturn(JsonNodeFactory.instance.booleanNode(true)); - final List> customObjects = asList(customObject1, customObject2); + when(customObject2.getValue()).thenReturn(true); + final List customObjects = asList(customObject1, customObject2); // test - final CompletionStage>> draftsFromPageStage = + final CompletionStage> draftsFromPageStage = customObjectSyncer.transform(customObjects); // assertions @@ -76,51 +92,28 @@ void transform_ShouldConvertResourcesToDrafts() { customObjects.stream() .map( customObject -> - CustomObjectDraft.ofUnversionedUpsert( - customObject.getContainer(), - customObject.getKey(), - customObject.getValue())) + CustomObjectDraftBuilder.of() + .container(customObject.getContainer()) + .key(customObject.getKey()) + .value(customObject.getValue()) + .build()) .collect(toList())); } - @Test - void getQuery_doNotSyncProjectSyncCustomObjects_ShouldBuildCustomObjectQuery() { - // preparation - final String runnerName = "testRunnerName"; - final CustomObjectSyncer customObjectSyncer = - CustomObjectSyncer.of( - mock(SphereClient.class), - mock(SphereClient.class), - getMockedClock(), - runnerName, - false); - - // test - final CustomObjectQuery query = customObjectSyncer.getQuery(); - - // assertion - final List excludedContainerNames = getExcludedContainerNames(runnerName); - assertThat(query) - .isEqualTo( - CustomObjectQuery.ofJsonNode() - .plusPredicates( - customObjectQueryModel -> - customObjectQueryModel.container().isNotIn(excludedContainerNames))); - } - @Test void getQuery_syncProjectSyncCustomObjects_ShouldBuildCustomObjectQuery() { // preparation final String runnerName = "testRunnerName"; final CustomObjectSyncer customObjectSyncer = CustomObjectSyncer.of( - mock(SphereClient.class), mock(SphereClient.class), getMockedClock(), runnerName, true); + sourceClient, mock(ProjectApiRoot.class), getMockedClock(), runnerName, true); // test - final CustomObjectQuery query = customObjectSyncer.getQuery(); + final ByProjectKeyCustomObjectsGet query = customObjectSyncer.getQuery(); // assertion - assertThat(query).isEqualTo(CustomObjectQuery.ofJsonNode()); + assertThat(query).isEqualTo(byProjectKeyCustomObjectsGet); + verify(byProjectKeyCustomObjectsGet, never()).withWhere(anyString()); } private List getExcludedContainerNames(String runnerName) { @@ -131,7 +124,7 @@ private List getExcludedContainerNames(String runnerName) { final String moduleName = syncModuleOption.getSyncModuleName(); return SyncUtils.buildLastSyncTimestampContainerName(moduleName, runnerName); }) - .collect(Collectors.toList()); + .collect(toList()); final List currentCtpTimestampContainerNames = Stream.of(SyncModuleOption.values()) .map( @@ -139,12 +132,12 @@ private List getExcludedContainerNames(String runnerName) { final String moduleName = syncModuleOption.getSyncModuleName(); return SyncUtils.buildCurrentCtpTimestampContainerName(moduleName, runnerName); }) - .collect(Collectors.toList()); + .collect(toList()); final List excludedContainerNames = Stream.concat( lastSyncTimestampContainerNames.stream(), currentCtpTimestampContainerNames.stream()) - .collect(Collectors.toList()); + .collect(toList()); return excludedContainerNames; } } diff --git a/src/test/java/com/commercetools/project/sync/inventoryentry/InventoryEntrySyncerTest.java b/src/test/java/com/commercetools/project/sync/inventoryentry/InventoryEntrySyncerTest.java index f7f02ae4..ee831181 100644 --- a/src/test/java/com/commercetools/project/sync/inventoryentry/InventoryEntrySyncerTest.java +++ b/src/test/java/com/commercetools/project/sync/inventoryentry/InventoryEntrySyncerTest.java @@ -1,27 +1,30 @@ package com.commercetools.project.sync.inventoryentry; -import static com.commercetools.project.sync.util.TestUtils.getMockedClock; -import static com.commercetools.project.sync.util.TestUtils.mockResourceIdsGraphQlRequest; +import static com.commercetools.project.sync.util.TestUtils.*; import static com.commercetools.sync.inventories.utils.InventoryTransformUtils.toInventoryEntryDrafts; -import static io.sphere.sdk.json.SphereJsonUtils.readObjectFromResource; import static java.util.Arrays.asList; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import com.commercetools.sync.commons.models.ResourceIdsGraphQlRequest; -import com.commercetools.sync.commons.models.ResourceKeyIdGraphQlResult; +import com.commercetools.api.client.ByProjectKeyGraphqlPost; +import com.commercetools.api.client.ByProjectKeyGraphqlRequestBuilder; +import com.commercetools.api.client.ByProjectKeyInventoryGet; +import com.commercetools.api.client.ByProjectKeyInventoryRequestBuilder; +import com.commercetools.api.client.ProjectApiRoot; +import com.commercetools.api.models.graph_ql.GraphQLRequest; +import com.commercetools.api.models.graph_ql.GraphQLResponse; +import com.commercetools.api.models.inventory.InventoryEntry; +import com.commercetools.api.models.inventory.InventoryEntryDraft; +import com.commercetools.api.models.inventory.InventoryPagedQueryResponse; +import com.commercetools.api.models.inventory.InventoryPagedQueryResponseBuilder; import com.commercetools.sync.commons.utils.CaffeineReferenceIdToKeyCacheImpl; import com.commercetools.sync.commons.utils.ReferenceIdToKeyCache; import com.commercetools.sync.inventories.InventorySync; -import io.sphere.sdk.client.SphereApiConfig; -import io.sphere.sdk.client.SphereClient; -import io.sphere.sdk.inventory.InventoryEntry; -import io.sphere.sdk.inventory.InventoryEntryDraft; -import io.sphere.sdk.inventory.queries.InventoryEntryQuery; -import io.sphere.sdk.json.SphereJsonUtils; -import io.sphere.sdk.queries.PagedQueryResult; +import com.fasterxml.jackson.core.JsonProcessingException; +import io.vrap.rmf.base.client.ApiHttpResponse; import java.time.Clock; import java.util.Collections; import java.util.List; @@ -30,6 +33,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import uk.org.lidalia.slf4jtest.LoggingEvent; import uk.org.lidalia.slf4jtest.TestLogger; @@ -52,7 +56,7 @@ void of_ShouldCreateInventoryEntrySyncerInstance() { // test final InventoryEntrySyncer inventorySyncer = InventoryEntrySyncer.of( - mock(SphereClient.class), mock(SphereClient.class), getMockedClock()); + mock(ProjectApiRoot.class), mock(ProjectApiRoot.class), getMockedClock()); // assertions assertThat(inventorySyncer).isNotNull(); @@ -60,11 +64,11 @@ void of_ShouldCreateInventoryEntrySyncerInstance() { } @Test - void transform_ShouldReplaceInventoryEntryReferenceIdsWithKeys() { + void transform_ShouldReplaceInventoryEntryReferenceIdsWithKeys() throws JsonProcessingException { // preparation - final SphereClient sourceClient = mock(SphereClient.class); + final ProjectApiRoot sourceClient = mock(ProjectApiRoot.class); final InventoryEntrySyncer inventoryEntrySyncer = - InventoryEntrySyncer.of(sourceClient, mock(SphereClient.class), getMockedClock()); + InventoryEntrySyncer.of(sourceClient, mock(ProjectApiRoot.class), getMockedClock()); final List inventoryPage = asList( readObjectFromResource("inventory-sku-1.json", InventoryEntry.class), @@ -81,20 +85,27 @@ void transform_ShouldReplaceInventoryEntryReferenceIdsWithKeys() { .collect(Collectors.toList()); final String jsonStringCustomTypes = - "{\"results\":[{\"id\":\"02e915e7-7763-48d1-83bd-d4e940a1a368\"," - + "\"key\":\"test-custom-type-key\"} ]}"; - final ResourceKeyIdGraphQlResult customTypesResult = - SphereJsonUtils.readObject(jsonStringCustomTypes, ResourceKeyIdGraphQlResult.class); + "{\"data\":{\"typeDefinitions\":{\"results\":[{\"id\":\"02e915e7-7763-48d1-83bd-d4e940a1a368\"," + + "\"key\":\"test-custom-type-key\"}]}}}"; + final GraphQLResponse customTypesResult = + readObject(jsonStringCustomTypes, GraphQLResponse.class); final String jsonStringSupplyChannels = - "{\"results\":[{\"id\":\"5c0516b5-f506-4b6a-b4d1-c06ca29ab7e1\"," - + "\"key\":\"test-channel-key\"} ]}"; - final ResourceKeyIdGraphQlResult supplyChannelsResult = - SphereJsonUtils.readObject(jsonStringSupplyChannels, ResourceKeyIdGraphQlResult.class); - - when(sourceClient.execute(any(ResourceIdsGraphQlRequest.class))) - .thenReturn(CompletableFuture.completedFuture(customTypesResult)) - .thenReturn(CompletableFuture.completedFuture(supplyChannelsResult)); + "{\"data\":{\"channels\":{\"results\":[{\"id\":\"5c0516b5-f506-4b6a-b4d1-c06ca29ab7e1\"," + + "\"key\":\"test-channel-key\"}]}}}"; + final GraphQLResponse supplyChannelsResult = + readObject(jsonStringSupplyChannels, GraphQLResponse.class); + + final ApiHttpResponse graphQLResponse = mock(ApiHttpResponse.class); + + when(graphQLResponse.getBody()).thenReturn(customTypesResult).thenReturn(supplyChannelsResult); + final ByProjectKeyGraphqlRequestBuilder byProjectKeyGraphqlRequestBuilder = mock(); + when(sourceClient.graphql()).thenReturn(byProjectKeyGraphqlRequestBuilder); + final ByProjectKeyGraphqlPost byProjectKeyGraphqlPost = mock(); + when(byProjectKeyGraphqlRequestBuilder.post(any(GraphQLRequest.class))) + .thenReturn(byProjectKeyGraphqlPost); + when(byProjectKeyGraphqlPost.execute()) + .thenReturn(CompletableFuture.completedFuture(graphQLResponse)); // test final CompletionStage> draftsFromPageStage = @@ -118,25 +129,39 @@ void transform_ShouldReplaceInventoryEntryReferenceIdsWithKeys() { } @Test - void syncWithError_ShouldCallErrorCallback() { + @Disabled("https://commercetools.atlassian.net/browse/DEVX-275") + void syncWithError_ShouldCallErrorCallback() throws JsonProcessingException { // preparation: inventory entry with no key is synced - final SphereClient sourceClient = mock(SphereClient.class); - final SphereClient targetClient = mock(SphereClient.class); - when(sourceClient.getConfig()).thenReturn(SphereApiConfig.of("source-project")); - when(targetClient.getConfig()).thenReturn(SphereApiConfig.of("target-project")); + final ProjectApiRoot sourceClient = mock(ProjectApiRoot.class); + final ProjectApiRoot targetClient = mock(ProjectApiRoot.class); + final List inventoryEntries = Collections.singletonList( readObjectFromResource("inventory-no-sku.json", InventoryEntry.class)); - final PagedQueryResult pagedQueryResult = mock(PagedQueryResult.class); - when(pagedQueryResult.getResults()).thenReturn(inventoryEntries); - when(sourceClient.execute(any(InventoryEntryQuery.class))) - .thenReturn(CompletableFuture.completedFuture(pagedQueryResult)); + final ByProjectKeyInventoryRequestBuilder byProjectKeyInventoryRequestBuilder = mock(); + when(sourceClient.inventory()).thenReturn(byProjectKeyInventoryRequestBuilder); + final ByProjectKeyInventoryGet byProjectKeyInventoryGet = mock(); + when(byProjectKeyInventoryRequestBuilder.get()).thenReturn(byProjectKeyInventoryGet); + when(byProjectKeyInventoryGet.withSort(anyString())).thenReturn(byProjectKeyInventoryGet); + when(byProjectKeyInventoryGet.withLimit(anyInt())).thenReturn(byProjectKeyInventoryGet); + when(byProjectKeyInventoryGet.withWithTotal(anyBoolean())).thenReturn(byProjectKeyInventoryGet); + final ApiHttpResponse response = mock(ApiHttpResponse.class); + final InventoryPagedQueryResponse inventoryPagedQueryResponse = + InventoryPagedQueryResponseBuilder.of() + .results(inventoryEntries) + .limit(20L) + .offset(0L) + .count(1L) + .build(); + when(response.getBody()).thenReturn(inventoryPagedQueryResponse); + when(byProjectKeyInventoryGet.execute()) + .thenReturn(CompletableFuture.completedFuture(response)); mockResourceIdsGraphQlRequest( - sourceClient, "4db98ea6-38dc-4ccb-b20f-466e1566567h", "customTypeKey"); + sourceClient, "typeDefinitions", "4db98ea6-38dc-4ccb-b20f-466e1566567h", "customTypeKey"); mockResourceIdsGraphQlRequest( - sourceClient, "1489488b-f737-4a9e-ba49-2d42d84c4c6f", "channelKey"); + sourceClient, "channels", "1489488b-f737-4a9e-ba49-2d42d84c4c6f", "channelKey"); // test final InventoryEntrySyncer inventoryEntrySyncer = diff --git a/src/test/java/com/commercetools/project/sync/product/ProductSyncerTest.java b/src/test/java/com/commercetools/project/sync/product/ProductSyncerTest.java index 0eb8eb50..a0394ab7 100644 --- a/src/test/java/com/commercetools/project/sync/product/ProductSyncerTest.java +++ b/src/test/java/com/commercetools/project/sync/product/ProductSyncerTest.java @@ -1,29 +1,38 @@ package com.commercetools.project.sync.product; +import static com.commercetools.project.sync.util.TestUtils.createBadGatewayException; import static com.commercetools.project.sync.util.TestUtils.getMockedClock; -import static io.sphere.sdk.json.SphereJsonUtils.readObjectFromResource; -import static java.util.Arrays.asList; +import static com.commercetools.project.sync.util.TestUtils.mockResourceIdsGraphQlRequest; +import static com.commercetools.project.sync.util.TestUtils.readObjectFromResource; +import static com.commercetools.project.sync.util.TestUtils.withTestClient; +import static java.util.Collections.singletonList; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import com.commercetools.api.client.ByProjectKeyGraphqlPost; +import com.commercetools.api.client.ByProjectKeyProductProjectionsGet; +import com.commercetools.api.client.ProjectApiRoot; +import com.commercetools.api.defaultconfig.ApiRootBuilder; +import com.commercetools.api.models.graph_ql.GraphQLRequest; +import com.commercetools.api.models.product.Product; +import com.commercetools.api.models.product.ProductDraft; +import com.commercetools.api.models.product.ProductMixin; +import com.commercetools.api.models.product.ProductProjection; +import com.commercetools.api.models.product.ProductProjectionType; import com.commercetools.project.sync.model.ProductSyncCustomRequest; import com.commercetools.sync.commons.exceptions.ReferenceTransformException; -import com.commercetools.sync.commons.models.ResourceIdsGraphQlRequest; -import com.commercetools.sync.commons.models.ResourceKeyIdGraphQlResult; import com.commercetools.sync.products.ProductSync; +import com.commercetools.sync.products.utils.AttributeUtils; import com.fasterxml.jackson.databind.JsonNode; -import io.sphere.sdk.client.BadGatewayException; -import io.sphere.sdk.client.SphereClient; -import io.sphere.sdk.json.SphereJsonUtils; -import io.sphere.sdk.products.Product; -import io.sphere.sdk.products.ProductDraft; -import io.sphere.sdk.products.ProductProjection; -import io.sphere.sdk.products.ProductProjectionType; -import io.sphere.sdk.products.queries.ProductProjectionQuery; -import io.sphere.sdk.queries.QueryPredicate; -import io.sphere.sdk.utils.CompletableFutureUtils; +import io.vrap.rmf.base.client.ApiHttpMethod; +import io.vrap.rmf.base.client.ApiHttpResponse; +import io.vrap.rmf.base.client.error.BadGatewayException; +import io.vrap.rmf.base.client.utils.CompletableFutureUtils; +import io.vrap.rmf.base.client.utils.json.JsonUtils; +import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.List; import java.util.Optional; @@ -45,54 +54,76 @@ void tearDownTest() { @Test void of_ShouldCreateProductSyncerInstance() { + final ProjectApiRoot apiRoot = mock(ProjectApiRoot.class); + when(apiRoot.productProjections()).thenReturn(mock()); + final ByProjectKeyProductProjectionsGet getMock = mock(ByProjectKeyProductProjectionsGet.class); + when(getMock.addStaged(anyBoolean())).thenReturn(getMock); + when(apiRoot.productProjections().get()).thenReturn(getMock); // test - final ProductSyncer productSyncer = - ProductSyncer.of( - mock(SphereClient.class), mock(SphereClient.class), getMockedClock(), null); + final ProductSyncer productSyncer = ProductSyncer.of(apiRoot, apiRoot, getMockedClock(), null); // assertions assertThat(productSyncer).isNotNull(); - assertThat(productSyncer.getQuery()).isInstanceOf(ProductProjectionQuery.class); + assertThat(productSyncer.getQuery()).isEqualTo(getMock); assertThat(productSyncer.getSync()).isExactlyInstanceOf(ProductSync.class); } @Test void transform_WithAttributeReferences_ShouldReplaceProductReferenceIdsWithKeys() { // preparation - final SphereClient sourceClient = mock(SphereClient.class); - final ProductSyncer productSyncer = - ProductSyncer.of(sourceClient, mock(SphereClient.class), getMockedClock(), null); - final List productPage = - Collections.singletonList( - readObjectFromResource("product-key-4.json", Product.class) - .toProjection(ProductProjectionType.STAGED)); - - String jsonStringProducts = - "{\"results\":[{\"id\":\"53c4a8b4-754f-4b95-b6f2-3e1e70e3d0d2\",\"key\":\"prod1\"}," - + "{\"id\":\"53c4a8b4-754f-4b95-b6f2-3e1e70e3d0d6\",\"key\":\"prod2\"}]}"; - final ResourceKeyIdGraphQlResult productsResult = - SphereJsonUtils.readObject(jsonStringProducts, ResourceKeyIdGraphQlResult.class); - - String jsonStringProductTypes = - "{\"results\":[{\"id\":\"53c4a8b4-754f-4b95-b6f2-3e1e70e3d0d3\"," - + "\"key\":\"prodType1\"}]}"; - final ResourceKeyIdGraphQlResult productTypesResult = - SphereJsonUtils.readObject(jsonStringProductTypes, ResourceKeyIdGraphQlResult.class); - - String jsonStringCategories = - "{\"results\":[{\"id\":\"53c4a8b4-754f-4b95-b6f2-3e1e70e3d0d4\",\"key\":\"cat1\"}," - + "{\"id\":\"53c4a8b4-754f-4b95-b6f2-3e1e70e3d0d5\",\"key\":\"cat2\"}]}"; - final ResourceKeyIdGraphQlResult categoriesResult = - SphereJsonUtils.readObject(jsonStringCategories, ResourceKeyIdGraphQlResult.class); - - when(sourceClient.execute(any(ResourceIdsGraphQlRequest.class))) - .thenReturn(CompletableFuture.completedFuture(productsResult)) - .thenReturn(CompletableFuture.completedFuture(productTypesResult)) - .thenReturn(CompletableFuture.completedFuture(categoriesResult)); + final ProductProjection productProjection = + ProductMixin.toProjection( + readObjectFromResource("product-key-4.json", Product.class), + ProductProjectionType.STAGED); + + final String jsonStringProducts = + "{\"data\":{\"products\":{\"results\":[{\"id\":\"53c4a8b4-754f-4b95-b6f2-3e1e70e3d0d2\"," + + "\"key\":\"prod1\"}," + + "{\"id\":\"53c4a8b4-754f-4b95-b6f2-3e1e70e3d0d6\",\"key\":\"prod2\"}]}}}"; + + final String jsonStringProductTypes = + "{\"data\":{\"productTypes\":{\"results\":[{\"id\":\"53c4a8b4-754f-4b95-b6f2-3e1e70e3d0d3\"," + + "\"key\":\"prodType1\"}]}}}"; + + final String jsonStringCategories = + "{\"data\":{\"categories\":{\"results\":[{\"id\":\"53c4a8b4-754f-4b95-b6f2-3e1e70e3d0d4\",\"key\":\"cat1\"}," + + "{\"id\":\"53c4a8b4-754f-4b95-b6f2-3e1e70e3d0d5\",\"key\":\"cat2\"}]}}}"; + + final ProjectApiRoot sourceClient = + withTestClient( + "testProjectKey", + (uri, method, requestBody) -> { + if (uri.contains("graphql") && ApiHttpMethod.POST.equals(method)) { + final GraphQLRequest graphQLRequest = + JsonUtils.fromJsonString(requestBody, GraphQLRequest.class); + final String requestQuery = graphQLRequest.getQuery(); + if (requestQuery.contains("products")) { + return CompletableFuture.completedFuture( + new ApiHttpResponse<>( + 200, null, jsonStringProducts.getBytes(StandardCharsets.UTF_8))); + } + if (requestQuery.contains("productTypes")) { + return CompletableFuture.completedFuture( + new ApiHttpResponse<>( + 200, null, jsonStringProductTypes.getBytes(StandardCharsets.UTF_8))); + } + if (requestQuery.contains("categories")) { + return CompletableFuture.completedFuture( + new ApiHttpResponse<>( + 200, null, jsonStringCategories.getBytes(StandardCharsets.UTF_8))); + } + final String result = "{\"data\":{}}"; + return CompletableFuture.completedFuture( + new ApiHttpResponse<>(200, null, result.getBytes(StandardCharsets.UTF_8))); + } + return null; + }); + final ProductSyncer productSyncer = + ProductSyncer.of(sourceClient, mock(ProjectApiRoot.class), getMockedClock(), null); // test final List draftsFromPageStage = - productSyncer.transform(productPage).toCompletableFuture().join(); + productSyncer.transform(singletonList(productProjection)).toCompletableFuture().join(); // assertions @@ -108,7 +139,11 @@ void transform_WithAttributeReferences_ShouldReplaceProductReferenceIdsWithKeys( .anySatisfy( attributeDraft -> { assertThat(attributeDraft.getName()).isEqualTo("productReference"); - final JsonNode referenceSet = attributeDraft.getValue(); + final JsonNode attributeValue = + AttributeUtils.replaceAttributeValueWithJsonAndReturnValue( + attributeDraft); + final List referenceSet = + AttributeUtils.getAttributeReferences(attributeValue); assertThat(referenceSet) .anySatisfy( reference -> @@ -126,7 +161,11 @@ void transform_WithAttributeReferences_ShouldReplaceProductReferenceIdsWithKeys( .anySatisfy( attributeDraft -> { assertThat(attributeDraft.getName()).isEqualTo("categoryReference"); - final JsonNode referenceSet = attributeDraft.getValue(); + final JsonNode attributeValue = + AttributeUtils.replaceAttributeValueWithJsonAndReturnValue( + attributeDraft); + final List referenceSet = + AttributeUtils.getAttributeReferences(attributeValue); assertThat(referenceSet) .anySatisfy( reference -> @@ -144,8 +183,12 @@ void transform_WithAttributeReferences_ShouldReplaceProductReferenceIdsWithKeys( .anySatisfy( attributeDraft -> { assertThat(attributeDraft.getName()).isEqualTo("productTypeReference"); - assertThat(attributeDraft.getValue().get("id").asText()) - .isEqualTo("prodType1"); + final JsonNode attributeValue = + AttributeUtils.replaceAttributeValueWithJsonAndReturnValue( + attributeDraft); + final List referenceSet = + AttributeUtils.getAttributeReferences(attributeValue); + assertThat(referenceSet.get(0).get("id").asText()).isEqualTo("prodType1"); })); assertThat(testLogger.getAllLoggingEvents()).isEmpty(); @@ -154,26 +197,20 @@ void transform_WithAttributeReferences_ShouldReplaceProductReferenceIdsWithKeys( @Test void transform_WithDiscountedPrices_ShouldRemoveDiscountedPrices() { // preparation - final SphereClient sourceClient = mock(SphereClient.class); + final ProjectApiRoot sourceClient = mock(ProjectApiRoot.class); final ProductSyncer productSyncer = - ProductSyncer.of(sourceClient, mock(SphereClient.class), getMockedClock(), null); - final List productPage = - Collections.singletonList( - readObjectFromResource("product-key-10.json", Product.class) - .toProjection(ProductProjectionType.STAGED)); - - String jsonStringProductTypes = - "{\"results\":[{\"id\":\"53c4a8b4-754f-4b95-b6f2-3e1e70e3d0d3\"," - + "\"key\":\"prodType1\"}]}"; - final ResourceKeyIdGraphQlResult productTypesResult = - SphereJsonUtils.readObject(jsonStringProductTypes, ResourceKeyIdGraphQlResult.class); + ProductSyncer.of(sourceClient, mock(ProjectApiRoot.class), getMockedClock(), null); + final ProductProjection productProjection = + ProductMixin.toProjection( + readObjectFromResource("product-key-10.json", Product.class), + ProductProjectionType.STAGED); - when(sourceClient.execute(any(ResourceIdsGraphQlRequest.class))) - .thenReturn(CompletableFuture.completedFuture(productTypesResult)); + mockResourceIdsGraphQlRequest( + sourceClient, "productTypes", "53c4a8b4-754f-4b95-b6f2-3e1e70e3d0d3", "prodType1"); // test final List draftsFromPageStage = - productSyncer.transform(productPage).toCompletableFuture().join(); + productSyncer.transform(singletonList(productProjection)).toCompletableFuture().join(); final Optional productDraftKey1 = draftsFromPageStage.stream() @@ -191,24 +228,29 @@ void transform_WithDiscountedPrices_ShouldRemoveDiscountedPrices() { @Test void transform_WithErrorOnGraphQlRequest_ShouldContinueAndLogError() { // preparation - final SphereClient sourceClient = mock(SphereClient.class); + final ProjectApiRoot sourceClient = mock(ProjectApiRoot.class); final ProductSyncer productSyncer = - ProductSyncer.of(sourceClient, mock(SphereClient.class), getMockedClock(), null); - final List productPage = - asList( - readObjectFromResource("product-key-1.json", Product.class) - .toProjection(ProductProjectionType.STAGED), - readObjectFromResource("product-key-2.json", Product.class) - .toProjection(ProductProjectionType.STAGED)); - - final BadGatewayException badGatewayException = - new BadGatewayException("Failed Graphql request"); - when(sourceClient.execute(any(ResourceIdsGraphQlRequest.class))) + ProductSyncer.of(sourceClient, mock(ProjectApiRoot.class), getMockedClock(), null); + final ProductProjection productProjection1 = + ProductMixin.toProjection( + readObjectFromResource("product-key-1.json", Product.class), + ProductProjectionType.STAGED); + final ProductProjection productProjection2 = + ProductMixin.toProjection( + readObjectFromResource("product-key-2.json", Product.class), + ProductProjectionType.STAGED); + + final ByProjectKeyGraphqlPost byProjectKeyGraphqlPost = mock(ByProjectKeyGraphqlPost.class); + final BadGatewayException badGatewayException = createBadGatewayException(); + when(sourceClient.graphql()).thenReturn(mock()); + when(sourceClient.graphql().post(any(GraphQLRequest.class))) + .thenReturn(byProjectKeyGraphqlPost); + when(byProjectKeyGraphqlPost.execute()) .thenReturn(CompletableFutureUtils.failed(badGatewayException)); // test final CompletionStage> draftsFromPageStage = - productSyncer.transform(productPage); + productSyncer.transform(List.of(productProjection1, productProjection2)); // assertions assertThat(draftsFromPageStage).isCompletedWithValue(Collections.emptyList()); @@ -230,15 +272,17 @@ void transform_WithErrorOnGraphQlRequest_ShouldContinueAndLogError() { @Test void getQuery_ShouldBuildProductQueryWithoutAnyExpansionPaths() { // preparation - final ProductSyncer productSyncer = - ProductSyncer.of( - mock(SphereClient.class), mock(SphereClient.class), getMockedClock(), null); + ProjectApiRoot apiRoot = mock(ProjectApiRoot.class); + when(apiRoot.productProjections()).thenReturn(mock()); + when(apiRoot.productProjections().get()).thenReturn(mock()); + when(apiRoot.productProjections().get().addStaged(anyBoolean())).thenReturn(mock()); + final ProductSyncer productSyncer = ProductSyncer.of(apiRoot, apiRoot, getMockedClock(), null); // test - final ProductProjectionQuery query = productSyncer.getQuery(); + final ByProjectKeyProductProjectionsGet query = productSyncer.getQuery(); // assertion - assertThat(query.expansionPaths()).isEmpty(); + assertThat(query.getExpand()).isEmpty(); } @Test @@ -248,22 +292,21 @@ void getQuery_ShouldBuildProductQueryWithCustomQueryAndLimitSize() { final String customQuery = "published=true AND masterData(masterVariant(attributes(name= \"abc\" AND value=123)))"; + final ProjectApiRoot apiRoot = + ApiRootBuilder.of().withApiBaseUrl("apiBaseUrl").build("projectKey"); + final ProductSyncCustomRequest productSyncCustomRequest = new ProductSyncCustomRequest(); - productSyncCustomRequest.setWhere(customQuery); productSyncCustomRequest.setLimit(limit); + productSyncCustomRequest.setWhere(customQuery); final ProductSyncer productSyncer = - ProductSyncer.of( - mock(SphereClient.class), - mock(SphereClient.class), - getMockedClock(), - productSyncCustomRequest); + ProductSyncer.of(apiRoot, apiRoot, getMockedClock(), productSyncCustomRequest); // test - final ProductProjectionQuery query = productSyncer.getQuery(); + final ByProjectKeyProductProjectionsGet query = productSyncer.getQuery(); // assertion - assertThat(query.limit()).isEqualTo(100); - assertThat(query.predicates()).contains(QueryPredicate.of(customQuery)); + assertThat(query.getLimit().get(0)).isEqualTo("100"); + assertThat(query.getWhere()).contains(customQuery); } } diff --git a/src/test/java/com/commercetools/project/sync/producttype/ProductTypeSyncerTest.java b/src/test/java/com/commercetools/project/sync/producttype/ProductTypeSyncerTest.java index 43fbf320..65570d84 100644 --- a/src/test/java/com/commercetools/project/sync/producttype/ProductTypeSyncerTest.java +++ b/src/test/java/com/commercetools/project/sync/producttype/ProductTypeSyncerTest.java @@ -1,26 +1,27 @@ package com.commercetools.project.sync.producttype; import static com.commercetools.project.sync.util.TestUtils.getMockedClock; -import static io.sphere.sdk.json.SphereJsonUtils.readObjectFromResource; +import static com.commercetools.project.sync.util.TestUtils.readObjectFromResource; import static java.util.Arrays.asList; 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.ArgumentMatchers.*; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import com.commercetools.api.client.ByProjectKeyProductTypesGet; +import com.commercetools.api.client.ByProjectKeyProductTypesRequestBuilder; +import com.commercetools.api.client.ProjectApiRoot; +import com.commercetools.api.models.product_type.AttributeDefinitionDraftBuilder; +import com.commercetools.api.models.product_type.ProductType; +import com.commercetools.api.models.product_type.ProductTypeDraft; +import com.commercetools.api.models.product_type.ProductTypeDraftBuilder; +import com.commercetools.api.models.product_type.ProductTypePagedQueryResponse; import com.commercetools.sync.producttypes.ProductTypeSync; -import io.sphere.sdk.client.SphereApiConfig; -import io.sphere.sdk.client.SphereClient; -import io.sphere.sdk.producttypes.ProductType; -import io.sphere.sdk.producttypes.ProductTypeDraft; -import io.sphere.sdk.producttypes.ProductTypeDraftBuilder; -import io.sphere.sdk.producttypes.queries.ProductTypeQuery; -import io.sphere.sdk.queries.PagedQueryResult; +import io.vrap.rmf.base.client.ApiHttpResponse; import java.time.Clock; import java.util.List; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletionStage; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import uk.org.lidalia.slf4jtest.LoggingEvent; @@ -38,13 +39,19 @@ void setup() { @Test void of_ShouldCreateProductTypeSyncerInstance() { + final ProjectApiRoot projectApiRoot = mock(ProjectApiRoot.class); + final ByProjectKeyProductTypesRequestBuilder byProjectKeyProductTypesRequestBuilder = mock(); + when(projectApiRoot.productTypes()).thenReturn(byProjectKeyProductTypesRequestBuilder); + final ByProjectKeyProductTypesGet byProjectKeyProductTypesGet = mock(); + when(byProjectKeyProductTypesRequestBuilder.get()).thenReturn(byProjectKeyProductTypesGet); + // test final ProductTypeSyncer productTypeSyncer = - ProductTypeSyncer.of(mock(SphereClient.class), mock(SphereClient.class), getMockedClock()); + ProductTypeSyncer.of(projectApiRoot, projectApiRoot, getMockedClock()); // assertions assertThat(productTypeSyncer).isNotNull(); - assertThat(productTypeSyncer.getQuery()).isInstanceOf(ProductTypeQuery.class); + assertThat(productTypeSyncer.getQuery()).isEqualTo(byProjectKeyProductTypesGet); assertThat(productTypeSyncer.getSync()).isExactlyInstanceOf(ProductTypeSync.class); } @@ -52,53 +59,86 @@ void of_ShouldCreateProductTypeSyncerInstance() { void transform_ShouldConvertResourcesToDrafts() { // preparation final ProductTypeSyncer productTypeSyncer = - ProductTypeSyncer.of(mock(SphereClient.class), mock(SphereClient.class), getMockedClock()); - final List productTypePage = + ProductTypeSyncer.of( + mock(ProjectApiRoot.class), mock(ProjectApiRoot.class), getMockedClock()); + final List productTypes = asList( readObjectFromResource("product-type-key-1.json", ProductType.class), readObjectFromResource("product-type-key-2.json", ProductType.class)); // test - final CompletionStage> draftsFromPageStage = - productTypeSyncer.transform(productTypePage); + final List productDrafts = + productTypeSyncer.transform(productTypes).toCompletableFuture().join(); // assertions - assertThat(draftsFromPageStage) - .isCompletedWithValue( - productTypePage.stream() - .map(ProductTypeDraftBuilder::of) - .map(ProductTypeDraftBuilder::build) - .collect(toList())); + final List expectedProductTypeDraft = + productTypes.stream() + .map( + (p) -> + ProductTypeDraftBuilder.of() + .key(p.getKey()) + .name(p.getName()) + .description(p.getDescription()) + .attributes( + p.getAttributes().stream() + .map( + attributeDefinition -> + AttributeDefinitionDraftBuilder.of() + .name(attributeDefinition.getName()) + .label(attributeDefinition.getLabel()) + .type(attributeDefinition.getType()) + .isRequired(attributeDefinition.getIsRequired()) + .inputHint(attributeDefinition.getInputHint()) + .attributeConstraint( + attributeDefinition.getAttributeConstraint()) + .isSearchable(attributeDefinition.getIsSearchable()) + .inputTip(attributeDefinition.getInputTip()) + .build()) + .collect(toList())) + .build()) + .collect(toList()); + + assertProductTypes(productDrafts.get(0), expectedProductTypeDraft.get(0)); + assertProductTypes(productDrafts.get(1), expectedProductTypeDraft.get(1)); } - @Test - void getQuery_ShouldBuildProductTypeQuery() { - // preparation - final ProductTypeSyncer productTypeSyncer = - ProductTypeSyncer.of(mock(SphereClient.class), mock(SphereClient.class), getMockedClock()); - - // test - final ProductTypeQuery query = productTypeSyncer.getQuery(); - - // assertion - assertThat(query).isEqualTo(ProductTypeQuery.of()); + private static void assertProductTypes( + final ProductTypeDraft productTypeDraft, final ProductTypeDraft expectedProductTypeDraft) { + assertThat(productTypeDraft.getKey()).isEqualTo(expectedProductTypeDraft.getKey()); + assertThat(productTypeDraft.getName()).isEqualTo(expectedProductTypeDraft.getName()); + assertThat(productTypeDraft.getDescription()) + .isEqualTo(expectedProductTypeDraft.getDescription()); + assertThat(productTypeDraft.getAttributes().size()) + .isEqualTo(expectedProductTypeDraft.getAttributes().size()); } @Test void syncWithError_WhenNoKeyIsProvided_ShouldCallErrorCallback() { // preparation - final SphereClient sourceClient = mock(SphereClient.class); - final SphereClient targetClient = mock(SphereClient.class); - when(sourceClient.getConfig()).thenReturn(SphereApiConfig.of("source-project")); - when(targetClient.getConfig()).thenReturn(SphereApiConfig.of("target-project")); - - final List productTypePage = - asList(readObjectFromResource("product-type-without-key.json", ProductType.class)); - - final PagedQueryResult pagedQueryResult = mock(PagedQueryResult.class); - when(pagedQueryResult.getResults()).thenReturn(productTypePage); - when(sourceClient.execute(any(ProductTypeQuery.class))) - .thenReturn(CompletableFuture.completedFuture(pagedQueryResult)); + final ProjectApiRoot sourceClient = mock(ProjectApiRoot.class); + final ProjectApiRoot targetClient = mock(ProjectApiRoot.class); + + final List productTypes = + List.of(readObjectFromResource("product-type-without-key.json", ProductType.class)); + + final ByProjectKeyProductTypesRequestBuilder byProjectKeyProductTypesRequestBuilder = mock(); + when(sourceClient.productTypes()).thenReturn(byProjectKeyProductTypesRequestBuilder); + final ByProjectKeyProductTypesGet byProjectKeyProductTypesGet = mock(); + when(byProjectKeyProductTypesRequestBuilder.get()).thenReturn(byProjectKeyProductTypesGet); + when(byProjectKeyProductTypesGet.withSort(anyString())).thenReturn(byProjectKeyProductTypesGet); + when(byProjectKeyProductTypesGet.withLimit(anyInt())).thenReturn(byProjectKeyProductTypesGet); + when(byProjectKeyProductTypesGet.withWithTotal(anyBoolean())) + .thenReturn(byProjectKeyProductTypesGet); + + final ApiHttpResponse apiHttpResponse = + mock(ApiHttpResponse.class); + final ProductTypePagedQueryResponse productTypePagedQueryResponse = + mock(ProductTypePagedQueryResponse.class); + when(productTypePagedQueryResponse.getResults()).thenReturn(productTypes); + when(apiHttpResponse.getBody()).thenReturn(productTypePagedQueryResponse); + + when(byProjectKeyProductTypesGet.execute()) + .thenReturn(CompletableFuture.completedFuture(apiHttpResponse)); // test final ProductTypeSyncer productTypeSyncer = diff --git a/src/test/java/com/commercetools/project/sync/service/impl/CustomObjectServiceImplTest.java b/src/test/java/com/commercetools/project/sync/service/impl/CustomObjectServiceImplTest.java index 898b6334..a6f964f4 100644 --- a/src/test/java/com/commercetools/project/sync/service/impl/CustomObjectServiceImplTest.java +++ b/src/test/java/com/commercetools/project/sync/service/impl/CustomObjectServiceImplTest.java @@ -1,57 +1,97 @@ package com.commercetools.project.sync.service.impl; import static com.commercetools.project.sync.util.SyncUtils.DEFAULT_RUNNER_NAME; -import static java.util.Collections.singletonList; import static java.util.Optional.empty; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.reset; import static org.mockito.Mockito.when; +import com.commercetools.api.client.ByProjectKeyCustomObjectsByContainerByKeyGet; +import com.commercetools.api.client.ByProjectKeyCustomObjectsByContainerByKeyRequestBuilder; +import com.commercetools.api.client.ByProjectKeyCustomObjectsPost; +import com.commercetools.api.client.ByProjectKeyCustomObjectsRequestBuilder; +import com.commercetools.api.client.ProjectApiRoot; +import com.commercetools.api.models.custom_object.CustomObject; +import com.commercetools.api.models.custom_object.CustomObjectDraft; import com.commercetools.project.sync.model.response.LastSyncCustomObject; import com.commercetools.project.sync.service.CustomObjectService; +import com.commercetools.project.sync.util.TestUtils; import com.commercetools.sync.products.helpers.ProductSyncStatistics; -import io.sphere.sdk.client.BadGatewayException; -import io.sphere.sdk.client.SphereClient; -import io.sphere.sdk.customobjects.CustomObject; -import io.sphere.sdk.customobjects.CustomObjectDraft; -import io.sphere.sdk.customobjects.commands.CustomObjectUpsertCommand; -import io.sphere.sdk.customobjects.queries.CustomObjectQuery; -import io.sphere.sdk.queries.PagedQueryResult; -import io.sphere.sdk.utils.CompletableFutureUtils; +import io.vrap.rmf.base.client.ApiHttpResponse; +import io.vrap.rmf.base.client.error.BadGatewayException; +import io.vrap.rmf.base.client.utils.CompletableFutureUtils; +import java.time.Duration; +import java.time.ZoneOffset; import java.time.ZonedDateTime; +import java.time.temporal.ChronoUnit; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; +import java.util.concurrent.ExecutionException; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; class CustomObjectServiceImplTest { - private static final SphereClient CLIENT = mock(SphereClient.class); + private ProjectApiRoot ctpClient; - @SuppressWarnings("unchecked") - private static final CustomObject STRING_CUSTOM_OBJECT = mock(CustomObject.class); + private static final ZonedDateTime now = ZonedDateTime.now(); - @SuppressWarnings("unchecked") - private static final CustomObject LAST_SYNC_CUSTOM_OBJECT = - mock(CustomObject.class); + private static final CustomObject STRING_CUSTOM_OBJECT = mock(CustomObject.class); + + private static final LastSyncCustomObject LAST_SYNC_CUSTOM_OBJECT_VALUE = + LastSyncCustomObject.of(now, new ProductSyncStatistics(), 0); + + private final ByProjectKeyCustomObjectsRequestBuilder byProjectKeyCustomObjectsRequestBuilder = + mock(); + private final ByProjectKeyCustomObjectsPost byProjectKeyCustomObjectsPost = mock(); + private final ByProjectKeyCustomObjectsByContainerByKeyGet + byProjectKeyCustomObjectsByContainerByKeyGet = mock(); + + private ApiHttpResponse apiHttpResponse; @BeforeAll static void setup() { - when(STRING_CUSTOM_OBJECT.getLastModifiedAt()).thenReturn(ZonedDateTime.now()); - when(LAST_SYNC_CUSTOM_OBJECT.getLastModifiedAt()).thenReturn(ZonedDateTime.now()); + when(STRING_CUSTOM_OBJECT.getLastModifiedAt()).thenReturn(now); + when(STRING_CUSTOM_OBJECT.getValue()).thenReturn(LAST_SYNC_CUSTOM_OBJECT_VALUE); + } + + @BeforeEach + void init() { + apiHttpResponse = mock(ApiHttpResponse.class); + ctpClient = mock(ProjectApiRoot.class); + + when(ctpClient.customObjects()).thenReturn(byProjectKeyCustomObjectsRequestBuilder); + when(byProjectKeyCustomObjectsRequestBuilder.post(any(CustomObjectDraft.class))) + .thenReturn(byProjectKeyCustomObjectsPost); + + final ByProjectKeyCustomObjectsByContainerByKeyRequestBuilder + byProjectKeyCustomObjectsByContainerByKeyRequestBuilder = mock(); + when(byProjectKeyCustomObjectsRequestBuilder.withContainerAndKey(anyString(), anyString())) + .thenReturn(byProjectKeyCustomObjectsByContainerByKeyRequestBuilder); + when(byProjectKeyCustomObjectsByContainerByKeyRequestBuilder.get()) + .thenReturn(byProjectKeyCustomObjectsByContainerByKeyGet); + } + + @AfterEach + void cleanup() { + reset(ctpClient); } @Test - @SuppressWarnings("unchecked") void getCurrentCtpTimestamp_OnSuccessfulUpsert_ShouldCompleteWithCtpTimestamp() { // preparation - when(CLIENT.execute(any(CustomObjectUpsertCommand.class))) - .thenReturn(CompletableFuture.completedFuture(STRING_CUSTOM_OBJECT)); - final CustomObjectService customObjectService = new CustomObjectServiceImpl(CLIENT); + when(apiHttpResponse.getBody()).thenReturn(STRING_CUSTOM_OBJECT); + when(byProjectKeyCustomObjectsPost.execute()) + .thenReturn(CompletableFuture.completedFuture(apiHttpResponse)); + final CustomObjectService customObjectService = new CustomObjectServiceImpl(ctpClient); // test final CompletionStage ctpTimestamp = @@ -62,14 +102,13 @@ void getCurrentCtpTimestamp_OnSuccessfulUpsert_ShouldCompleteWithCtpTimestamp() } @Test - @SuppressWarnings("unchecked") void getCurrentCtpTimestamp_OnFailedUpsert_ShouldCompleteExceptionally() { // preparation - when(CLIENT.execute(any(CustomObjectUpsertCommand.class))) - .thenReturn( - CompletableFutureUtils.exceptionallyCompletedFuture( - new BadGatewayException("CTP error!"))); - final CustomObjectService customObjectService = new CustomObjectServiceImpl(CLIENT); + final BadGatewayException badGatewayException = TestUtils.createBadGatewayException(); + + when(byProjectKeyCustomObjectsPost.execute()) + .thenReturn(CompletableFutureUtils.exceptionallyCompletedFuture(badGatewayException)); + final CustomObjectService customObjectService = new CustomObjectServiceImpl(ctpClient); // test final CompletionStage ctpTimestamp = @@ -77,47 +116,59 @@ void getCurrentCtpTimestamp_OnFailedUpsert_ShouldCompleteExceptionally() { // assertions assertThat(ctpTimestamp) - .hasFailedWithThrowableThat() - .isInstanceOf(BadGatewayException.class) - .hasMessageContaining("CTP error!"); + .failsWithin(Duration.ZERO) + .withThrowableOfType(ExecutionException.class) + .withCauseExactlyInstanceOf(BadGatewayException.class) + .withMessageContaining("test"); } @Test - @SuppressWarnings("unchecked") + @Disabled("https://commercetools.atlassian.net/browse/DEVX-272") void getLastSyncCustomObject_OnSuccessfulQueryWithResults_ShouldCompleteWithLastSyncCustomObject() { // preparation - final PagedQueryResult> queriedCustomObjects = - spy(PagedQueryResult.empty()); - when(queriedCustomObjects.getResults()).thenReturn(singletonList(LAST_SYNC_CUSTOM_OBJECT)); + when(apiHttpResponse.getBody()).thenReturn(STRING_CUSTOM_OBJECT); - when(CLIENT.execute(any(CustomObjectQuery.class))) - .thenReturn(CompletableFuture.completedFuture(queriedCustomObjects)); + when(byProjectKeyCustomObjectsByContainerByKeyGet.execute()) + .thenReturn(CompletableFuture.completedFuture(apiHttpResponse)); - final CustomObjectService customObjectService = new CustomObjectServiceImpl(CLIENT); + final CustomObjectService customObjectService = new CustomObjectServiceImpl(ctpClient); // test - final CompletionStage>> lastSyncCustomObject = - customObjectService.getLastSyncCustomObject("foo", "bar", DEFAULT_RUNNER_NAME); + final LastSyncCustomObject lastSyncCustomObject = + customObjectService.getLastSyncCustomObject("foo", "bar", DEFAULT_RUNNER_NAME).join().get(); // assertions - assertThat(lastSyncCustomObject).isCompletedWithValue(Optional.of(LAST_SYNC_CUSTOM_OBJECT)); + assertThat(lastSyncCustomObject.getLastSyncDurationInMillis()) + .isEqualTo(LAST_SYNC_CUSTOM_OBJECT_VALUE.getLastSyncDurationInMillis()); + assertThat(lastSyncCustomObject.getLastSyncStatistics()) + .isEqualTo(LAST_SYNC_CUSTOM_OBJECT_VALUE.getLastSyncStatistics()); + assertThat(lastSyncCustomObject.getApplicationVersion()) + .isEqualTo(LAST_SYNC_CUSTOM_OBJECT_VALUE.getApplicationVersion()); + assertThat( + lastSyncCustomObject + .getLastSyncTimestamp() + .truncatedTo(ChronoUnit.SECONDS) + .withZoneSameInstant(ZoneOffset.UTC)) + .isEqualTo( + LAST_SYNC_CUSTOM_OBJECT_VALUE + .getLastSyncTimestamp() + .truncatedTo(ChronoUnit.SECONDS) + .withZoneSameInstant(ZoneOffset.UTC)); } @Test - @SuppressWarnings("unchecked") void getLastSyncCustomObject_OnSuccessfulQueryWithNoResults_ShouldCompleteWithEmptyOptional() { // preparation - final PagedQueryResult> queriedCustomObjects = - PagedQueryResult.empty(); + when(apiHttpResponse.getBody()).thenReturn(null); - when(CLIENT.execute(any(CustomObjectQuery.class))) - .thenReturn(CompletableFuture.completedFuture(queriedCustomObjects)); + when(byProjectKeyCustomObjectsByContainerByKeyGet.execute()) + .thenReturn(CompletableFuture.completedFuture(apiHttpResponse)); - final CustomObjectService customObjectService = new CustomObjectServiceImpl(CLIENT); + final CustomObjectService customObjectService = new CustomObjectServiceImpl(ctpClient); // test - final CompletionStage>> lastSyncCustomObject = + final CompletionStage> lastSyncCustomObject = customObjectService.getLastSyncCustomObject("foo", "bar", DEFAULT_RUNNER_NAME); // assertions @@ -125,90 +176,91 @@ void getLastSyncCustomObject_OnSuccessfulQueryWithNoResults_ShouldCompleteWithEm } @Test - @SuppressWarnings("unchecked") void getLastSyncCustomObject_OnFailedQuery_ShouldCompleteExceptionally() { // preparation - final PagedQueryResult> queriedCustomObjects = - spy(PagedQueryResult.empty()); - when(queriedCustomObjects.getResults()).thenReturn(singletonList(LAST_SYNC_CUSTOM_OBJECT)); - - when(CLIENT.execute(any(CustomObjectQuery.class))) + when(byProjectKeyCustomObjectsByContainerByKeyGet.execute()) .thenReturn( CompletableFutureUtils.exceptionallyCompletedFuture( - new BadGatewayException("CTP error!"))); + TestUtils.createBadGatewayException())); - final CustomObjectService customObjectService = new CustomObjectServiceImpl(CLIENT); + final CustomObjectService customObjectService = new CustomObjectServiceImpl(ctpClient); // test - final CompletionStage>> lastSyncCustomObject = + final CompletionStage> lastSyncCustomObject = customObjectService.getLastSyncCustomObject("foo", "bar", DEFAULT_RUNNER_NAME); // assertions assertThat(lastSyncCustomObject) - .hasFailedWithThrowableThat() - .isInstanceOf(BadGatewayException.class) - .hasMessageContaining("CTP error!"); + .failsWithin(Duration.ZERO) + .withThrowableOfType(ExecutionException.class) + .withCauseExactlyInstanceOf(RuntimeException.class) + .satisfies( + exception -> + assertThat(exception.getCause().getCause()) + .isInstanceOf(BadGatewayException.class) + .hasMessageContaining("test")); } @Test - @SuppressWarnings("unchecked") void createLastSyncCustomObject_OnSuccessfulCreation_ShouldCompleteWithLastSyncCustomObject() { // preparation - when(CLIENT.execute(any(CustomObjectUpsertCommand.class))) - .thenReturn(CompletableFuture.completedFuture(LAST_SYNC_CUSTOM_OBJECT)); + when(apiHttpResponse.getBody()).thenReturn(STRING_CUSTOM_OBJECT); + + when(byProjectKeyCustomObjectsPost.execute()) + .thenReturn(CompletableFuture.completedFuture(apiHttpResponse)); - final CustomObjectService customObjectService = new CustomObjectServiceImpl(CLIENT); + final CustomObjectService customObjectService = new CustomObjectServiceImpl(ctpClient); // test final LastSyncCustomObject lastSyncCustomObject = LastSyncCustomObject.of(ZonedDateTime.now(), new ProductSyncStatistics(), 100); - final CustomObject createdCustomObject = + final CustomObject createdCustomObject = customObjectService .createLastSyncCustomObject("foo", "bar", DEFAULT_RUNNER_NAME, lastSyncCustomObject) .toCompletableFuture() - .join(); + .join() + .getBody(); + + final LastSyncCustomObject customObjectValue = + (LastSyncCustomObject) createdCustomObject.getValue(); // assertions - assertThat(createdCustomObject).isEqualTo(LAST_SYNC_CUSTOM_OBJECT); + assertThat(customObjectValue).isEqualTo(LAST_SYNC_CUSTOM_OBJECT_VALUE); } @Test - @SuppressWarnings("unchecked") void createLastSyncCustomObject_OnFailedCreation_ShouldCompleteExceptionally() { // preparation - when(CLIENT.execute(any(CustomObjectUpsertCommand.class))) + when(byProjectKeyCustomObjectsPost.execute()) .thenReturn( CompletableFutureUtils.exceptionallyCompletedFuture( - new BadGatewayException("CTP error!"))); + TestUtils.createBadGatewayException())); - final CustomObjectService customObjectService = new CustomObjectServiceImpl(CLIENT); + final CustomObjectService customObjectService = new CustomObjectServiceImpl(ctpClient); // test - final LastSyncCustomObject lastSyncCustomObject = - LastSyncCustomObject.of(ZonedDateTime.now(), new ProductSyncStatistics(), 100); - - final CompletionStage> createdCustomObject = + final CompletableFuture> createdCustomObject = customObjectService.createLastSyncCustomObject( - "foo", "bar", DEFAULT_RUNNER_NAME, lastSyncCustomObject); + "foo", "bar", DEFAULT_RUNNER_NAME, LAST_SYNC_CUSTOM_OBJECT_VALUE); // assertions assertThat(createdCustomObject) - .hasFailedWithThrowableThat() - .isInstanceOf(BadGatewayException.class) - .hasMessageContaining("CTP error!"); + .failsWithin(Duration.ZERO) + .withThrowableOfType(ExecutionException.class) + .withCauseExactlyInstanceOf(BadGatewayException.class) + .withMessageContaining("test"); } @Test - @SuppressWarnings("unchecked") void createLastSyncCustomObject_WithValidTestRunnerName_ShouldCreateCorrectCustomObjectDraft() { // preparation - final SphereClient client = mock(SphereClient.class); - final ArgumentCaptor arg = - ArgumentCaptor.forClass(CustomObjectUpsertCommand.class); - when(client.execute(arg.capture())).thenReturn(null); + final ArgumentCaptor arg = ArgumentCaptor.forClass(CustomObjectDraft.class); + when(byProjectKeyCustomObjectsRequestBuilder.post(arg.capture())) + .thenReturn(byProjectKeyCustomObjectsPost); + when(byProjectKeyCustomObjectsPost.execute()).thenReturn(null); - final CustomObjectService customObjectService = new CustomObjectServiceImpl(client); + final CustomObjectService customObjectService = new CustomObjectServiceImpl(ctpClient); final LastSyncCustomObject lastSyncCustomObject = LastSyncCustomObject.of(ZonedDateTime.now(), new ProductSyncStatistics(), 100); @@ -218,22 +270,23 @@ void createLastSyncCustomObject_WithValidTestRunnerName_ShouldCreateCorrectCusto "foo", "bar", "testRunnerName", lastSyncCustomObject); // assertions - final CustomObjectDraft createdDraft = (CustomObjectDraft) arg.getValue().getDraft(); + final CustomObjectDraft createdDraft = arg.getValue(); assertThat(createdDraft.getContainer()) .isEqualTo("commercetools-project-sync.testRunnerName.bar"); assertThat(createdDraft.getKey()).isEqualTo("foo"); + assertThat(createdDraft.getValue()).isInstanceOf(LastSyncCustomObject.class); + assertThat((LastSyncCustomObject) createdDraft.getValue()).isEqualTo(lastSyncCustomObject); } @Test - @SuppressWarnings("unchecked") void createLastSyncCustomObject_WithEmptyRunnerName_ShouldCreateCorrectCustomObjectDraft() { // preparation - final SphereClient client = mock(SphereClient.class); - final ArgumentCaptor arg = - ArgumentCaptor.forClass(CustomObjectUpsertCommand.class); - when(client.execute(arg.capture())).thenReturn(null); + final ArgumentCaptor arg = ArgumentCaptor.forClass(CustomObjectDraft.class); + when(byProjectKeyCustomObjectsRequestBuilder.post(arg.capture())) + .thenReturn(byProjectKeyCustomObjectsPost); + when(byProjectKeyCustomObjectsPost.execute()).thenReturn(null); - final CustomObjectService customObjectService = new CustomObjectServiceImpl(client); + final CustomObjectService customObjectService = new CustomObjectServiceImpl(ctpClient); final LastSyncCustomObject lastSyncCustomObject = LastSyncCustomObject.of(ZonedDateTime.now(), new ProductSyncStatistics(), 100); @@ -242,21 +295,22 @@ void createLastSyncCustomObject_WithEmptyRunnerName_ShouldCreateCorrectCustomObj customObjectService.createLastSyncCustomObject("foo", "bar", "", lastSyncCustomObject); // assertions - final CustomObjectDraft createdDraft = (CustomObjectDraft) arg.getValue().getDraft(); + final CustomObjectDraft createdDraft = arg.getValue(); assertThat(createdDraft.getContainer()).isEqualTo("commercetools-project-sync.runnerName.bar"); assertThat(createdDraft.getKey()).isEqualTo("foo"); + assertThat(createdDraft.getValue()).isInstanceOf(LastSyncCustomObject.class); + assertThat((LastSyncCustomObject) createdDraft.getValue()).isEqualTo(lastSyncCustomObject); } @Test - @SuppressWarnings("unchecked") void createLastSyncCustomObject_WithNullRunnerName_ShouldCreateCorrectCustomObjectDraft() { // preparation - final SphereClient client = mock(SphereClient.class); - final ArgumentCaptor arg = - ArgumentCaptor.forClass(CustomObjectUpsertCommand.class); - when(client.execute(arg.capture())).thenReturn(null); + final ArgumentCaptor arg = ArgumentCaptor.forClass(CustomObjectDraft.class); + when(byProjectKeyCustomObjectsRequestBuilder.post(arg.capture())) + .thenReturn(byProjectKeyCustomObjectsPost); + when(byProjectKeyCustomObjectsPost.execute()).thenReturn(null); - final CustomObjectService customObjectService = new CustomObjectServiceImpl(client); + final CustomObjectService customObjectService = new CustomObjectServiceImpl(ctpClient); final LastSyncCustomObject lastSyncCustomObject = LastSyncCustomObject.of(ZonedDateTime.now(), new ProductSyncStatistics(), 100); @@ -265,8 +319,10 @@ void createLastSyncCustomObject_WithNullRunnerName_ShouldCreateCorrectCustomObje customObjectService.createLastSyncCustomObject("foo", "bar", null, lastSyncCustomObject); // assertions - final CustomObjectDraft createdDraft = (CustomObjectDraft) arg.getValue().getDraft(); + final CustomObjectDraft createdDraft = arg.getValue(); assertThat(createdDraft.getContainer()).isEqualTo("commercetools-project-sync.runnerName.bar"); assertThat(createdDraft.getKey()).isEqualTo("foo"); + assertThat(createdDraft.getValue()).isInstanceOf(LastSyncCustomObject.class); + assertThat((LastSyncCustomObject) createdDraft.getValue()).isEqualTo(lastSyncCustomObject); } } diff --git a/src/test/java/com/commercetools/project/sync/shoppinglist/ShoppingListSyncerTest.java b/src/test/java/com/commercetools/project/sync/shoppinglist/ShoppingListSyncerTest.java index 74f8f979..6fd21001 100644 --- a/src/test/java/com/commercetools/project/sync/shoppinglist/ShoppingListSyncerTest.java +++ b/src/test/java/com/commercetools/project/sync/shoppinglist/ShoppingListSyncerTest.java @@ -1,25 +1,29 @@ package com.commercetools.project.sync.shoppinglist; import static com.commercetools.project.sync.util.TestUtils.mockResourceIdsGraphQlRequest; -import static io.sphere.sdk.json.SphereJsonUtils.readObjectFromResource; +import static com.commercetools.project.sync.util.TestUtils.readObjectFromResource; +import static java.lang.String.format; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import com.commercetools.api.client.ByProjectKeyShoppingListsGet; +import com.commercetools.api.client.ByProjectKeyShoppingListsRequestBuilder; +import com.commercetools.api.client.ProjectApiRoot; +import com.commercetools.api.defaultconfig.ApiRootBuilder; +import com.commercetools.api.models.shopping_list.ShoppingList; +import com.commercetools.api.models.shopping_list.ShoppingListDraft; +import com.commercetools.api.models.shopping_list.ShoppingListPagedQueryResponse; +import com.commercetools.api.models.shopping_list.ShoppingListPagedQueryResponseBuilder; import com.commercetools.sync.commons.utils.CaffeineReferenceIdToKeyCacheImpl; import com.commercetools.sync.commons.utils.ReferenceIdToKeyCache; import com.commercetools.sync.shoppinglists.ShoppingListSync; import com.commercetools.sync.shoppinglists.utils.ShoppingListTransformUtils; -import io.sphere.sdk.client.SphereApiConfig; -import io.sphere.sdk.client.SphereClient; -import io.sphere.sdk.expansion.ExpansionPath; -import io.sphere.sdk.queries.PagedQueryResult; -import io.sphere.sdk.shoppinglists.ShoppingList; -import io.sphere.sdk.shoppinglists.ShoppingListDraft; -import io.sphere.sdk.shoppinglists.queries.ShoppingListQuery; +import io.vrap.rmf.base.client.ApiHttpResponse; import java.time.Clock; -import java.util.Collections; import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; @@ -45,7 +49,7 @@ void of_ShouldCreateShoppingListSyncerInstance() { // test final ShoppingListSyncer shoppingListSyncer = ShoppingListSyncer.of( - mock(SphereClient.class), mock(SphereClient.class), mock(Clock.class)); + mock(ProjectApiRoot.class), mock(ProjectApiRoot.class), mock(Clock.class)); // assertion assertThat(shoppingListSyncer).isNotNull(); @@ -55,14 +59,14 @@ void of_ShouldCreateShoppingListSyncerInstance() { @Test void transform_ShouldReplaceShoppingListReferenceIdsWithKeys() { // preparation - final SphereClient sourceClient = mock(SphereClient.class); + final ProjectApiRoot sourceClient = mock(ProjectApiRoot.class); final ShoppingListSyncer shoppingListSyncer = - ShoppingListSyncer.of(sourceClient, mock(SphereClient.class), mock(Clock.class)); + ShoppingListSyncer.of(sourceClient, mock(ProjectApiRoot.class), mock(Clock.class)); final List shoppingList = - Collections.singletonList(readObjectFromResource("shopping-list.json", ShoppingList.class)); + List.of(readObjectFromResource("shopping-list.json", ShoppingList.class)); mockResourceIdsGraphQlRequest( - sourceClient, "5ebfa80e-f4aa-4c0b-be64-e348e09a855a", "customTypeKey"); + sourceClient, "shoppingLists", "5ebfa80e-f4aa-4c0b-be64-e348e09a855a", "customTypeKey"); // test final CompletionStage> draftsFromPageStage = @@ -78,37 +82,49 @@ void transform_ShouldReplaceShoppingListReferenceIdsWithKeys() { @Test void getQuery_ShouldBuildShoppingListQuery() { + final ProjectApiRoot apiRoot = + ApiRootBuilder.of().withApiBaseUrl("baseUrl").build("testProjectKey"); final ShoppingListSyncer shoppingListSyncer = - ShoppingListSyncer.of( - mock(SphereClient.class), mock(SphereClient.class), mock(Clock.class)); + ShoppingListSyncer.of(apiRoot, apiRoot, mock(Clock.class)); // assertion - final ShoppingListQuery query = shoppingListSyncer.getQuery(); - assertThat(query.expansionPaths()).containsExactly(ExpansionPath.of("lineItems[*].variant")); + final ByProjectKeyShoppingListsGet shoppingListsGet = shoppingListSyncer.getQuery(); + + assertThat(shoppingListsGet.getExpand()).containsExactly("lineItems[*].variant"); } @Test void syncWithError_ShouldCallErrorCallback() { // preparation: shoppingList with no key is synced - final SphereClient sourceClient = mock(SphereClient.class); - final SphereClient targetClient = mock(SphereClient.class); - when(sourceClient.getConfig()).thenReturn(SphereApiConfig.of("source-project")); - when(targetClient.getConfig()).thenReturn(SphereApiConfig.of("target-project")); + final ProjectApiRoot sourceClient = mock(ProjectApiRoot.class); final List shoppingLists = - Collections.singletonList( - readObjectFromResource("shopping-list-no-key.json", ShoppingList.class)); - - final PagedQueryResult pagedQueryResult = mock(PagedQueryResult.class); - when(pagedQueryResult.getResults()).thenReturn(shoppingLists); - when(sourceClient.execute(any(ShoppingListQuery.class))) - .thenReturn(CompletableFuture.completedFuture(pagedQueryResult)); + List.of(readObjectFromResource("shopping-list-no-key.json", ShoppingList.class)); + final ByProjectKeyShoppingListsRequestBuilder projectKeyShoppingListsRequestBuilder = mock(); + when(sourceClient.shoppingLists()).thenReturn(projectKeyShoppingListsRequestBuilder); + final ByProjectKeyShoppingListsGet shoppingListsGet = mock(); + when(shoppingListsGet.addExpand(anyString())).thenReturn(shoppingListsGet); + when(shoppingListsGet.withLimit(anyInt())).thenReturn(shoppingListsGet); + when(shoppingListsGet.withWithTotal(anyBoolean())).thenReturn(shoppingListsGet); + when(shoppingListsGet.withSort(anyString())).thenReturn(shoppingListsGet); + + final ShoppingListPagedQueryResponse shoppingListPagedQueryResponse = + ShoppingListPagedQueryResponseBuilder.of() + .results(shoppingLists) + .limit(20L) + .offset(0L) + .count(1L) + .build(); + final ApiHttpResponse apiHttpResponse = mock(ApiHttpResponse.class); + when(apiHttpResponse.getBody()).thenReturn(shoppingListPagedQueryResponse); + when(shoppingListsGet.execute()).thenReturn(CompletableFuture.completedFuture(apiHttpResponse)); + when(sourceClient.shoppingLists().get()).thenReturn(shoppingListsGet); mockResourceIdsGraphQlRequest( - sourceClient, "5ebfa80e-f4aa-4c0b-be64-e348e09a855a", "customTypeKey"); + sourceClient, "shoppingLists", "5ebfa80e-f4aa-4c0b-be64-e348e09a855a", "customTypeKey"); // test final ShoppingListSyncer shoppingListSyncer = - ShoppingListSyncer.of(sourceClient, targetClient, mock(Clock.class)); + ShoppingListSyncer.of(sourceClient, mock(ProjectApiRoot.class), mock(Clock.class)); shoppingListSyncer.sync(null, true).toCompletableFuture().join(); // assertion @@ -118,6 +134,9 @@ void syncWithError_ShouldCallErrorCallback() { "Error when trying to sync shoppingList. Existing key: <>. Update actions: []"); assertThat(errorLog.getThrowable().get().getMessage()) .isEqualTo( - "ShoppingListDraft with name: LocalizedString(en -> shoppingList-name-1) doesn't have a key. Please make sure all shopping list drafts have keys."); + format( + "ShoppingListDraft with name: %s doesn't have a key. Please make sure all shopping list drafts have keys" + + ".", + shoppingLists.get(0).getName().toString())); } } diff --git a/src/test/java/com/commercetools/project/sync/state/StateSyncerTest.java b/src/test/java/com/commercetools/project/sync/state/StateSyncerTest.java index 3426ce59..b99b9922 100644 --- a/src/test/java/com/commercetools/project/sync/state/StateSyncerTest.java +++ b/src/test/java/com/commercetools/project/sync/state/StateSyncerTest.java @@ -1,33 +1,35 @@ package com.commercetools.project.sync.state; import static com.commercetools.project.sync.util.TestUtils.getMockedClock; -import static io.sphere.sdk.json.SphereJsonUtils.readObjectFromResource; -import static java.util.Arrays.asList; +import static com.commercetools.project.sync.util.TestUtils.mockResourceIdsGraphQlRequest; +import static com.commercetools.project.sync.util.TestUtils.readObjectFromResource; +import static java.lang.String.format; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import com.commercetools.sync.commons.models.ResourceIdsGraphQlRequest; -import com.commercetools.sync.commons.models.ResourceKeyIdGraphQlResult; +import com.commercetools.api.client.ByProjectKeyStatesGet; +import com.commercetools.api.client.ProjectApiRoot; +import com.commercetools.api.defaultconfig.ApiRootBuilder; +import com.commercetools.api.models.common.LocalizedString; +import com.commercetools.api.models.state.State; +import com.commercetools.api.models.state.StateDraft; +import com.commercetools.api.models.state.StateDraftBuilder; +import com.commercetools.api.models.state.StatePagedQueryResponse; +import com.commercetools.api.models.state.StatePagedQueryResponseBuilder; +import com.commercetools.api.models.state.StateResourceIdentifierBuilder; +import com.commercetools.api.models.state.StateTypeEnum; import com.commercetools.sync.states.StateSync; -import io.sphere.sdk.client.SphereApiConfig; -import io.sphere.sdk.client.SphereClient; -import io.sphere.sdk.json.SphereJsonUtils; -import io.sphere.sdk.models.LocalizedString; -import io.sphere.sdk.queries.PagedQueryResult; -import io.sphere.sdk.states.State; -import io.sphere.sdk.states.StateDraft; -import io.sphere.sdk.states.StateDraftBuilder; -import io.sphere.sdk.states.StateType; -import io.sphere.sdk.states.queries.StateQuery; -import java.time.Clock; -import java.util.Arrays; +import io.vrap.rmf.base.client.ApiHttpResponse; import java.util.Collections; import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import uk.org.lidalia.slf4jtest.LoggingEvent; import uk.org.lidalia.slf4jtest.TestLogger; @@ -45,7 +47,7 @@ void setup() { void of_ShouldCreateStateSyncerInstance() { // test final StateSyncer stateSyncer = - StateSyncer.of(mock(SphereClient.class), mock(SphereClient.class), getMockedClock()); + StateSyncer.of(mock(ProjectApiRoot.class), mock(ProjectApiRoot.class), getMockedClock()); // assertions assertThat(stateSyncer).isNotNull(); @@ -55,77 +57,114 @@ void of_ShouldCreateStateSyncerInstance() { @Test void transform_ShouldReplaceStateTransitionIdsWithKeys() { // preparation - final SphereClient sourceClient = mock(SphereClient.class); + final ProjectApiRoot sourceClient = mock(ProjectApiRoot.class); + mockResourceIdsGraphQlRequest( + sourceClient, "states", "ab949f1b-c441-4c70-9cf0-4182c36d6a6c", "Initial"); + final StateSyncer stateSyncer = - StateSyncer.of(sourceClient, mock(SphereClient.class), getMockedClock()); + StateSyncer.of(sourceClient, mock(ProjectApiRoot.class), getMockedClock()); final List states = - asList( + List.of( readObjectFromResource("state-1.json", State.class), readObjectFromResource("state-2.json", State.class)); - final String jsonStringTransitions = - "{\"results\":[{\"id\":\"ab949f1b-c441-4c70-9cf0-4182c36d6a6c\"," - + "\"key\":\"Initial\"} ]}"; - final ResourceKeyIdGraphQlResult transitionsResult = - SphereJsonUtils.readObject(jsonStringTransitions, ResourceKeyIdGraphQlResult.class); - - when(sourceClient.execute(any(ResourceIdsGraphQlRequest.class))) - .thenReturn(CompletableFuture.completedFuture(transitionsResult)); - // test final CompletionStage> stateDrafts = stateSyncer.transform(states); // assertions final StateDraft draft1 = - StateDraftBuilder.of("State 1", StateType.LINE_ITEM_STATE) - .roles(Collections.emptySet()) + StateDraftBuilder.of() + .key("State 1") + .type(StateTypeEnum.LINE_ITEM_STATE) + .roles(List.of()) .description(LocalizedString.ofEnglish("State 1")) .name(LocalizedString.ofEnglish("State 1")) .initial(true) .build(); final StateDraft draft2 = - StateDraftBuilder.of("State 2", StateType.LINE_ITEM_STATE) - .roles(Collections.emptySet()) + StateDraftBuilder.of() + .key("State 2") + .type(StateTypeEnum.LINE_ITEM_STATE) + .roles(List.of()) .description(LocalizedString.ofEnglish("State 2")) .name(LocalizedString.ofEnglish("State 2")) .initial(false) - .transitions(Collections.singleton(State.referenceOfId("Initial"))) + .transitions(StateResourceIdentifierBuilder.of().key("Initial").build()) .build(); - assertThat(stateDrafts).isCompletedWithValue(Arrays.asList(draft1, draft2)); + assertThat(stateDrafts).isCompletedWithValue(List.of(draft1, draft2)); } @Test void getQuery_ShouldBuildStateQuery() { // preparation + final ProjectApiRoot apiRoot = + ApiRootBuilder.of().withApiBaseUrl("apiBaseUrl").build("testProjectKey"); final StateSyncer stateSyncer = - StateSyncer.of(mock(SphereClient.class), mock(SphereClient.class), getMockedClock()); + StateSyncer.of(apiRoot, mock(ProjectApiRoot.class), getMockedClock()); + + // test + assertion + assertThat(stateSyncer.getQuery()).isInstanceOf(ByProjectKeyStatesGet.class); + } + + @Test + void transform_WhenNoKeyIsProvided_ShouldContinueAndLogError() { + // preparation + final ProjectApiRoot sourceClient = mock(ProjectApiRoot.class); + final List stateTypePage = + List.of(readObjectFromResource("state-without-key.json", State.class)); // test - final StateQuery query = stateSyncer.getQuery(); + final StateSyncer stateSyncer = + StateSyncer.of(sourceClient, mock(ProjectApiRoot.class), getMockedClock()); + final CompletableFuture> stateDrafts = + stateSyncer.transform(stateTypePage).toCompletableFuture(); // assertion - assertThat(query).isEqualTo(StateQuery.of()); + assertThat(stateDrafts).isCompletedWithValue(Collections.emptyList()); + assertThat(syncerTestLogger.getAllLoggingEvents()) + .anySatisfy( + loggingEvent -> { + assertThat(loggingEvent.getMessage()).contains("StateDraft: key is missing"); + assertThat(loggingEvent.getThrowable().isPresent()).isTrue(); + assertThat(loggingEvent.getThrowable().get()) + .isInstanceOf(NullPointerException.class); + }); } + /* + * Disabled as long as there's NPE when a key is null or empty and therefore add with PLACEHOLDER to cache. + * See: https://commercetools.atlassian.net/browse/DEVX-277 + */ + @Disabled @Test - void syncWithError_WhenNoKeyIsProvided_ShouldCallErrorCallback() { + void syncWithError_WhenTransistionReferencesAreInvalid_ShouldCallErrorCallback() { // preparation - final SphereClient sourceClient = mock(SphereClient.class); - final SphereClient targetClient = mock(SphereClient.class); - when(sourceClient.getConfig()).thenReturn(SphereApiConfig.of("source-project")); - when(targetClient.getConfig()).thenReturn(SphereApiConfig.of("target-project")); - - final List stateTypePage = - asList(readObjectFromResource("state-without-key.json", State.class)); - - final PagedQueryResult pagedQueryResult = mock(PagedQueryResult.class); - when(pagedQueryResult.getResults()).thenReturn(stateTypePage); - when(sourceClient.execute(any(StateQuery.class))) - .thenReturn(CompletableFuture.completedFuture(pagedQueryResult)); + final ProjectApiRoot sourceClient = mock(ProjectApiRoot.class); + mockResourceIdsGraphQlRequest( + sourceClient, "states", "ab949f1b-c441-4c70-9cf0-4182c36d6a6c", ""); + final List stateTypePage = List.of(readObjectFromResource("state-2.json", State.class)); + final StatePagedQueryResponse queryResponse = + StatePagedQueryResponseBuilder.of() + .results(stateTypePage) + .limit(20L) + .offset(0L) + .count(1L) + .build(); + final ApiHttpResponse apiHttpResponse = mock(ApiHttpResponse.class); + when(apiHttpResponse.getBody()).thenReturn(queryResponse); + final ByProjectKeyStatesGet byProjectKeyStatesGet = mock(ByProjectKeyStatesGet.class); + when(byProjectKeyStatesGet.withLimit(anyInt())).thenReturn(byProjectKeyStatesGet); + when(byProjectKeyStatesGet.withWithTotal(anyBoolean())).thenReturn(byProjectKeyStatesGet); + when(byProjectKeyStatesGet.withSort(anyString())).thenReturn(byProjectKeyStatesGet); + when(byProjectKeyStatesGet.execute()) + .thenReturn(CompletableFuture.completedFuture(apiHttpResponse)); + when(sourceClient.states()).thenReturn(mock()); + when(sourceClient.states().get()).thenReturn(byProjectKeyStatesGet); // test - final StateSyncer stateSyncer = StateSyncer.of(sourceClient, targetClient, mock(Clock.class)); + final StateSyncer stateSyncer = + StateSyncer.of(sourceClient, mock(ProjectApiRoot.class), getMockedClock()); stateSyncer.sync(null, true).toCompletableFuture().join(); // assertion @@ -135,6 +174,8 @@ void syncWithError_WhenNoKeyIsProvided_ShouldCallErrorCallback() { "Error when trying to sync state. Existing key: <>. Update actions: []"); assertThat(errorLog.getThrowable().get().getMessage()) .isEqualTo( - "StateDraft with name: LocalizedString(en -> State 1) doesn't have a key. Please make sure all state drafts have keys."); + format( + "StateDraft with key: %s has invalid state transitions", + stateTypePage.get(0).getKey())); } } diff --git a/src/test/java/com/commercetools/project/sync/taxcategory/TaxCategorySyncerTest.java b/src/test/java/com/commercetools/project/sync/taxcategory/TaxCategorySyncerTest.java index d8baf540..052777fd 100644 --- a/src/test/java/com/commercetools/project/sync/taxcategory/TaxCategorySyncerTest.java +++ b/src/test/java/com/commercetools/project/sync/taxcategory/TaxCategorySyncerTest.java @@ -1,29 +1,32 @@ package com.commercetools.project.sync.taxcategory; import static com.commercetools.project.sync.util.TestUtils.getMockedClock; -import static io.sphere.sdk.json.SphereJsonUtils.readObjectFromResource; -import static java.util.Arrays.asList; +import static com.commercetools.project.sync.util.TestUtils.readObjectFromResource; 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.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import com.commercetools.api.client.ByProjectKeyTaxCategoriesGet; +import com.commercetools.api.client.ProjectApiRoot; +import com.commercetools.api.models.tax_category.TaxCategory; +import com.commercetools.api.models.tax_category.TaxCategoryDraft; +import com.commercetools.api.models.tax_category.TaxCategoryDraftBuilder; +import com.commercetools.api.models.tax_category.TaxCategoryPagedQueryResponse; +import com.commercetools.api.models.tax_category.TaxCategoryPagedQueryResponseBuilder; +import com.commercetools.api.models.tax_category.TaxRate; +import com.commercetools.api.models.tax_category.TaxRateDraft; +import com.commercetools.api.models.tax_category.TaxRateDraftBuilder; import com.commercetools.sync.taxcategories.TaxCategorySync; -import io.sphere.sdk.client.SphereApiConfig; -import io.sphere.sdk.client.SphereClient; -import io.sphere.sdk.queries.PagedQueryResult; -import io.sphere.sdk.taxcategories.TaxCategory; -import io.sphere.sdk.taxcategories.TaxCategoryDraft; -import io.sphere.sdk.taxcategories.TaxCategoryDraftBuilder; -import io.sphere.sdk.taxcategories.TaxRateDraft; -import io.sphere.sdk.taxcategories.TaxRateDraftBuilder; -import io.sphere.sdk.taxcategories.queries.TaxCategoryQuery; -import java.time.Clock; +import io.vrap.rmf.base.client.ApiHttpResponse; import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; import java.util.stream.Collectors; +import javax.annotation.Nonnull; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import uk.org.lidalia.slf4jtest.LoggingEvent; @@ -41,13 +44,18 @@ void setup() { @Test void of_ShouldCreateTaxCategorySyncerInstance() { + // preparation + final ProjectApiRoot sourceClient = mock(ProjectApiRoot.class); + when(sourceClient.taxCategories()).thenReturn(mock()); + when(sourceClient.taxCategories().get()).thenReturn(mock()); + // test final TaxCategorySyncer taxCategorySyncer = - TaxCategorySyncer.of(mock(SphereClient.class), mock(SphereClient.class), getMockedClock()); + TaxCategorySyncer.of(sourceClient, mock(ProjectApiRoot.class), getMockedClock()); // assertions assertThat(taxCategorySyncer).isNotNull(); - assertThat(taxCategorySyncer.getQuery()).isEqualTo(TaxCategoryQuery.of()); + assertThat(taxCategorySyncer.getQuery()).isInstanceOf(ByProjectKeyTaxCategoriesGet.class); assertThat(taxCategorySyncer.getSync()).isInstanceOf(TaxCategorySync.class); } @@ -55,9 +63,10 @@ void of_ShouldCreateTaxCategorySyncerInstance() { void transform_ShouldConvertResourcesToDrafts() { // preparation final TaxCategorySyncer taxCategorySyncer = - TaxCategorySyncer.of(mock(SphereClient.class), mock(SphereClient.class), getMockedClock()); + TaxCategorySyncer.of( + mock(ProjectApiRoot.class), mock(ProjectApiRoot.class), getMockedClock()); final List taxCategoryPage = - asList( + List.of( readObjectFromResource("tax-category-key-1.json", TaxCategory.class), readObjectFromResource("tax-category-key-2.json", TaxCategory.class)); @@ -71,50 +80,66 @@ void transform_ShouldConvertResourcesToDrafts() { taxCategoryPage.stream() .map( taxCategory -> { - List taxRateDrafts = - taxCategory.getTaxRates().stream() - .map(taxRate -> TaxRateDraftBuilder.of(taxRate).build()) - .collect(Collectors.toList()); - return TaxCategoryDraftBuilder.of( - taxCategory.getName(), taxRateDrafts, taxCategory.getDescription()) + final List taxRateDrafts = + convertTaxRateToTaxRateDraft(taxCategory.getRates()); + return TaxCategoryDraftBuilder.of() + .name(taxCategory.getName()) + .rates(taxRateDrafts) + .description(taxCategory.getDescription()) .key(taxCategory.getKey()); }) .map(TaxCategoryDraftBuilder::build) .collect(toList())); } - @Test - void getQuery_ShouldBuildTaxCategoryQuery() { - // preparation - final TaxCategorySyncer taxCategorySyncer = - TaxCategorySyncer.of(mock(SphereClient.class), mock(SphereClient.class), getMockedClock()); - - // test - final TaxCategoryQuery query = taxCategorySyncer.getQuery(); - - // assertion - assertThat(query).isEqualTo(TaxCategoryQuery.of()); + private List convertTaxRateToTaxRateDraft(@Nonnull final List taxRates) { + + return taxRates.stream() + .map( + taxRate -> + TaxRateDraftBuilder.of() + .name(taxRate.getName()) + .country(taxRate.getCountry()) + .state(taxRate.getState()) + .includedInPrice(taxRate.getIncludedInPrice()) + .amount(taxRate.getAmount()) + .key(taxRate.getKey()) + .subRates(taxRate.getSubRates()) + .build()) + .collect(Collectors.toList()); } @Test void syncWithError_WhenNoKeyIsProvided_ShouldCallErrorCallback() { // preparation - final SphereClient sourceClient = mock(SphereClient.class); - final SphereClient targetClient = mock(SphereClient.class); - when(sourceClient.getConfig()).thenReturn(SphereApiConfig.of("source-project")); - when(targetClient.getConfig()).thenReturn(SphereApiConfig.of("target-project")); - + final ProjectApiRoot sourceClient = mock(ProjectApiRoot.class); final List taxCategoryPage = - asList(readObjectFromResource("tax-category-without-key.json", TaxCategory.class)); - - final PagedQueryResult pagedQueryResult = mock(PagedQueryResult.class); - when(pagedQueryResult.getResults()).thenReturn(taxCategoryPage); - when(sourceClient.execute(any(TaxCategoryQuery.class))) - .thenReturn(CompletableFuture.completedFuture(pagedQueryResult)); - + List.of(readObjectFromResource("tax-category-without-key.json", TaxCategory.class)); + + final TaxCategoryPagedQueryResponse queryResponse = + TaxCategoryPagedQueryResponseBuilder.of() + .results(taxCategoryPage) + .limit(20L) + .offset(0L) + .count(1L) + .build(); + + final ApiHttpResponse apiHttpResponse = mock(ApiHttpResponse.class); + when(apiHttpResponse.getBody()).thenReturn(queryResponse); + final ByProjectKeyTaxCategoriesGet byProjectKeyTaxCategoriesGet = + mock(ByProjectKeyTaxCategoriesGet.class); + when(sourceClient.taxCategories()).thenReturn(mock()); + when(sourceClient.taxCategories().get()).thenReturn(byProjectKeyTaxCategoriesGet); + when(byProjectKeyTaxCategoriesGet.withLimit(anyInt())).thenReturn(byProjectKeyTaxCategoriesGet); + when(byProjectKeyTaxCategoriesGet.withWithTotal(anyBoolean())) + .thenReturn(byProjectKeyTaxCategoriesGet); + when(byProjectKeyTaxCategoriesGet.withSort(anyString())) + .thenReturn(byProjectKeyTaxCategoriesGet); + when(byProjectKeyTaxCategoriesGet.execute()) + .thenReturn(CompletableFuture.completedFuture(apiHttpResponse)); // test final TaxCategorySyncer taxCategorySyncer = - TaxCategorySyncer.of(sourceClient, targetClient, mock(Clock.class)); + TaxCategorySyncer.of(sourceClient, mock(ProjectApiRoot.class), getMockedClock()); taxCategorySyncer.sync(null, true).toCompletableFuture().join(); // assertion diff --git a/src/test/java/com/commercetools/project/sync/type/TypeSyncerTest.java b/src/test/java/com/commercetools/project/sync/type/TypeSyncerTest.java index 82d7bc57..dfd79bdd 100644 --- a/src/test/java/com/commercetools/project/sync/type/TypeSyncerTest.java +++ b/src/test/java/com/commercetools/project/sync/type/TypeSyncerTest.java @@ -1,27 +1,29 @@ package com.commercetools.project.sync.type; import static com.commercetools.project.sync.util.TestUtils.getMockedClock; -import static io.sphere.sdk.json.SphereJsonUtils.readObjectFromResource; -import static java.util.Arrays.asList; +import static com.commercetools.project.sync.util.TestUtils.readObjectFromResource; +import static java.lang.String.format; 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.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import com.commercetools.api.client.ByProjectKeyTypesGet; +import com.commercetools.api.client.ProjectApiRoot; +import com.commercetools.api.defaultconfig.ApiRootBuilder; +import com.commercetools.api.models.type.Type; +import com.commercetools.api.models.type.TypeDraft; +import com.commercetools.api.models.type.TypeDraftBuilder; +import com.commercetools.api.models.type.TypePagedQueryResponse; +import com.commercetools.api.models.type.TypePagedQueryResponseBuilder; import com.commercetools.sync.types.TypeSync; -import io.sphere.sdk.client.SphereApiConfig; -import io.sphere.sdk.client.SphereClient; -import io.sphere.sdk.queries.PagedQueryResult; -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 java.time.Clock; +import io.vrap.rmf.base.client.ApiHttpResponse; import java.util.Collections; import java.util.List; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletionStage; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import uk.org.lidalia.slf4jtest.LoggingEvent; @@ -38,13 +40,17 @@ void setup() { @Test void of_ShouldCreateTypeSyncerInstance() { + // preparation + final ProjectApiRoot sourceClient = mock(ProjectApiRoot.class); + when(sourceClient.types()).thenReturn(mock()); + when(sourceClient.types().get()).thenReturn(mock()); + // test final TypeSyncer typeSyncer = - TypeSyncer.of(mock(SphereClient.class), mock(SphereClient.class), getMockedClock()); + TypeSyncer.of(sourceClient, mock(ProjectApiRoot.class), getMockedClock()); // assertions assertThat(typeSyncer).isNotNull(); - assertThat(typeSyncer.getQuery()).isEqualTo(TypeQuery.of()); assertThat(typeSyncer.getSync()).isInstanceOf(TypeSync.class); } @@ -52,23 +58,26 @@ void of_ShouldCreateTypeSyncerInstance() { void transform_ShouldConvertResourcesToDrafts() { // preparation final TypeSyncer typeSyncer = - TypeSyncer.of(mock(SphereClient.class), mock(SphereClient.class), getMockedClock()); + TypeSyncer.of(mock(ProjectApiRoot.class), mock(ProjectApiRoot.class), getMockedClock()); final List typePage = - asList( + List.of( readObjectFromResource("type-key-1.json", Type.class), readObjectFromResource("type-key-2.json", Type.class)); // test - final CompletionStage> draftsFromPageStage = typeSyncer.transform(typePage); + final List draftsFromPageStage = + typeSyncer.transform(typePage).toCompletableFuture().join(); // assertions assertThat(draftsFromPageStage) - .isCompletedWithValue( + .isEqualTo( typePage.stream() .map( type -> - TypeDraftBuilder.of( - type.getKey(), type.getName(), type.getResourceTypeIds()) + TypeDraftBuilder.of() + .key(type.getKey()) + .name(type.getName()) + .resourceTypeIds(type.getResourceTypeIds()) .fieldDefinitions(type.getFieldDefinitions()) .description(type.getDescription())) .map(TypeDraftBuilder::build) @@ -78,43 +87,79 @@ void transform_ShouldConvertResourcesToDrafts() { @Test void getQuery_ShouldBuildTypeQuery() { // preparation + final ProjectApiRoot apiRoot = + ApiRootBuilder.of().withApiBaseUrl("baseURl").build("testProjectKey"); final TypeSyncer typeSyncer = - TypeSyncer.of(mock(SphereClient.class), mock(SphereClient.class), getMockedClock()); + TypeSyncer.of(apiRoot, mock(ProjectApiRoot.class), getMockedClock()); + + // test + assertion + assertThat(typeSyncer.getQuery()).isInstanceOf(ByProjectKeyTypesGet.class); + } + + @Test + void transform_WhenNoKeyIsProvided_ShouldLogErrorAndContinue() { + // preparation + final ProjectApiRoot sourceClient = mock(ProjectApiRoot.class); + final List typePage = + List.of(readObjectFromResource("type-without-key.json", Type.class)); // test - final TypeQuery query = typeSyncer.getQuery(); + final TypeSyncer typeSyncer = + TypeSyncer.of(sourceClient, mock(ProjectApiRoot.class), getMockedClock()); + final List typeDrafts = typeSyncer.transform(typePage).toCompletableFuture().join(); // assertion - assertThat(query).isEqualTo(TypeQuery.of()); + assertThat(typeDrafts).isEqualTo(Collections.emptyList()); + assertThat(syncerTestLogger.getAllLoggingEvents()) + .anySatisfy( + loggingEvent -> { + assertThat(loggingEvent.getMessage()).contains("TypeDraft: key is missing"); + assertThat(loggingEvent.getThrowable().isPresent()).isTrue(); + assertThat(loggingEvent.getThrowable().get()) + .isInstanceOf(NullPointerException.class); + }); } @Test - void syncWithError_WhenNoKeyIsProvided_ShouldCallErrorCallback() { + void sync_whenKeyIsBlank_shouldCallErrorCallback() { // preparation - final SphereClient sourceClient = mock(SphereClient.class); - final SphereClient targetClient = mock(SphereClient.class); - when(sourceClient.getConfig()).thenReturn(SphereApiConfig.of("source-project")); - when(targetClient.getConfig()).thenReturn(SphereApiConfig.of("target-project")); - - final List typePage = - Collections.singletonList(readObjectFromResource("type-without-key.json", Type.class)); - - final PagedQueryResult pagedQueryResult = mock(PagedQueryResult.class); - when(pagedQueryResult.getResults()).thenReturn(typePage); - when(sourceClient.execute(any(TypeQuery.class))) - .thenReturn(CompletableFuture.completedFuture(pagedQueryResult)); + final ProjectApiRoot sourceClient = mock(ProjectApiRoot.class); + final Type type = readObjectFromResource("type-key-1.json", Type.class); + type.setKey(""); + + final TypePagedQueryResponse response = + TypePagedQueryResponseBuilder.of() + .results(List.of(type)) + .limit(20L) + .offset(0L) + .count(1L) + .build(); + + final ApiHttpResponse apiHttpResponse = mock(ApiHttpResponse.class); + when(apiHttpResponse.getBody()).thenReturn(response); + final ByProjectKeyTypesGet byProjectKeyTypesGet = mock(ByProjectKeyTypesGet.class); + when(byProjectKeyTypesGet.execute()) + .thenReturn(CompletableFuture.completedFuture(apiHttpResponse)); + when(byProjectKeyTypesGet.withLimit(anyInt())).thenReturn(byProjectKeyTypesGet); + when(byProjectKeyTypesGet.withWithTotal(anyBoolean())).thenReturn(byProjectKeyTypesGet); + when(byProjectKeyTypesGet.withSort(anyString())).thenReturn(byProjectKeyTypesGet); + when(sourceClient.types()).thenReturn(mock()); + when(sourceClient.types().get()).thenReturn(byProjectKeyTypesGet); // test - final TypeSyncer typeSyncer = TypeSyncer.of(sourceClient, targetClient, mock(Clock.class)); - typeSyncer.sync(null, true).toCompletableFuture().join(); + final TypeSyncer typeSyncer = + TypeSyncer.of(sourceClient, mock(ProjectApiRoot.class), getMockedClock()); + typeSyncer.sync(null, true); - // assertion + // assertions final LoggingEvent errorLog = syncerTestLogger.getAllLoggingEvents().get(1); assertThat(errorLog.getMessage()) .isEqualTo( "Error when trying to sync type. Existing key: <>. Update actions: []"); assertThat(errorLog.getThrowable().get().getMessage()) .isEqualTo( - "TypeDraft with name: LocalizedString(en -> typeName) doesn't have a key. Please make sure all type drafts have keys."); + format( + "TypeDraft with name: %s doesn't have a key. Please make sure all type drafts have keys.", + type.getName())); } } diff --git a/src/test/java/com/commercetools/project/sync/util/SyncUtilsTest.java b/src/test/java/com/commercetools/project/sync/util/SyncUtilsTest.java index f75154fc..3c320ebe 100644 --- a/src/test/java/com/commercetools/project/sync/util/SyncUtilsTest.java +++ b/src/test/java/com/commercetools/project/sync/util/SyncUtilsTest.java @@ -1,18 +1,20 @@ package com.commercetools.project.sync.util; import static com.commercetools.project.sync.util.SyncUtils.*; +import static com.commercetools.project.sync.util.TestUtils.createBadGatewayException; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import com.commercetools.api.models.ResourceUpdateAction; +import com.commercetools.api.models.WithKey; import com.commercetools.sync.commons.exceptions.SyncException; import com.commercetools.sync.products.ProductSync; import com.commercetools.sync.types.TypeSync; -import io.sphere.sdk.commands.UpdateAction; -import io.sphere.sdk.models.ResourceView; -import io.sphere.sdk.models.WithKey; +import io.vrap.rmf.base.client.error.BadGatewayException; import java.util.Arrays; import java.util.Optional; +import java.util.concurrent.CompletionException; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import uk.org.lidalia.slf4jtest.LoggingEvent; @@ -62,9 +64,9 @@ void getApplicationVersion_ShouldGetDefaultVersion() { void logErrorCallbackWithStringResourceIdentifier_ShouldLogErrorWithCorrectMessage() { final TestLogger testLogger = TestLoggerFactory.getTestLogger(SyncUtilsTest.class); SyncException exception = new SyncException("test sync exception"); - UpdateAction updateAction1 = mock(UpdateAction.class); + ResourceUpdateAction updateAction1 = mock(ResourceUpdateAction.class); when(updateAction1.toString()).thenReturn("updateAction1"); - UpdateAction updateAction2 = mock(UpdateAction.class); + ResourceUpdateAction updateAction2 = mock(ResourceUpdateAction.class); when(updateAction2.toString()).thenReturn("updateAction2"); logErrorCallback( @@ -104,9 +106,9 @@ void logErrorCallbackWithStringResourceIdentifier_ShouldLogErrorWithCorrectMessa void logErrorCallbackWithResource_ShouldLogErrorWithCorrectMessage() { final TestLogger testLogger = TestLoggerFactory.getTestLogger(SyncUtilsTest.class); final SyncException exception = new SyncException("test sync exception"); - final UpdateAction updateAction1 = mock(UpdateAction.class); + final ResourceUpdateAction updateAction1 = mock(ResourceUpdateAction.class); when(updateAction1.toString()).thenReturn("updateAction1"); - final UpdateAction updateAction2 = mock(UpdateAction.class); + final ResourceUpdateAction updateAction2 = mock(ResourceUpdateAction.class); when(updateAction2.toString()).thenReturn("updateAction2"); final WithKey resource = mock(WithKey.class); when(resource.getKey()).thenReturn("test identifier"); @@ -176,4 +178,19 @@ void logWarningCallbackWithResource_ShouldLogWarningWithCorrectMessage() { assertThat(loggingEvent.getThrowable().isPresent()).isTrue(); assertThat(loggingEvent.getThrowable().get()).isInstanceOf(SyncException.class); } + + @Test + void getCompletionExceptionCause_WithCompletionException_ShouldReturnDifferentException() { + final BadGatewayException wrappedException = createBadGatewayException(); + final CompletionException completionException = new CompletionException(wrappedException); + + assertThat(getCompletionExceptionCause(completionException)).isEqualTo(wrappedException); + } + + @Test + void getCompletionExceptionCause_WithBadGatewayException_ShouldReturnException() { + final BadGatewayException badGatewayException = createBadGatewayException(); + + assertThat(getCompletionExceptionCause(badGatewayException)).isEqualTo(badGatewayException); + } } diff --git a/src/test/java/com/commercetools/project/sync/util/TestUtils.java b/src/test/java/com/commercetools/project/sync/util/TestUtils.java index 8d44d0e2..51debda3 100644 --- a/src/test/java/com/commercetools/project/sync/util/TestUtils.java +++ b/src/test/java/com/commercetools/project/sync/util/TestUtils.java @@ -1,35 +1,53 @@ package com.commercetools.project.sync.util; +import static io.vrap.rmf.base.client.utils.json.JsonUtils.fromInputStream; import static java.lang.String.format; -import static java.util.Collections.singletonList; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; 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.verifyNoMoreInteractions; import static org.mockito.Mockito.when; +import com.commercetools.api.client.ByProjectKeyCustomObjectsPost; +import com.commercetools.api.client.ByProjectKeyGraphqlPost; +import com.commercetools.api.client.ByProjectKeyGraphqlRequestBuilder; +import com.commercetools.api.client.ProjectApiRoot; +import com.commercetools.api.defaultconfig.ApiRootBuilder; +import com.commercetools.api.models.custom_object.CustomObject; +import com.commercetools.api.models.custom_object.CustomObjectDraft; +import com.commercetools.api.models.error.ErrorResponse; +import com.commercetools.api.models.error.ErrorResponseBuilder; +import com.commercetools.api.models.graph_ql.GraphQLRequest; +import com.commercetools.api.models.graph_ql.GraphQLResponse; import com.commercetools.project.sync.model.response.LastSyncCustomObject; -import com.commercetools.sync.commons.models.ResourceIdsGraphQlRequest; -import com.commercetools.sync.commons.models.ResourceKeyIdGraphQlResult; import com.commercetools.sync.products.helpers.ProductSyncStatistics; -import io.sphere.sdk.client.SphereClient; -import io.sphere.sdk.customobjects.CustomObject; -import io.sphere.sdk.customobjects.commands.CustomObjectUpsertCommand; -import io.sphere.sdk.customobjects.queries.CustomObjectQuery; -import io.sphere.sdk.json.SphereJsonUtils; -import io.sphere.sdk.queries.PagedQueryResult; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ObjectWriter; +import io.vrap.rmf.base.client.ApiHttpMethod; +import io.vrap.rmf.base.client.ApiHttpResponse; +import io.vrap.rmf.base.client.error.BadGatewayException; +import io.vrap.rmf.base.client.utils.json.JsonUtils; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; import java.time.Clock; import java.time.ZonedDateTime; +import java.util.Collections; import java.util.concurrent.CompletableFuture; import javax.annotation.Nonnull; +import org.apache.commons.lang3.StringUtils; import org.assertj.core.api.Condition; +import org.assertj.core.util.TriFunction; import uk.org.lidalia.slf4jext.Level; import uk.org.lidalia.slf4jtest.LoggingEvent; import uk.org.lidalia.slf4jtest.TestLogger; +// This utility class compiles but not used yet +// TODO: Use the utility functions and adjust them public final class TestUtils { public static void assertTypeSyncerLoggingEvents( @@ -180,39 +198,84 @@ public static void assertSyncerLoggingEvents( } public static void verifyInteractionsWithClientAfterSync( - @Nonnull final SphereClient client, final int numberOfGetConfigInvocations) { + @Nonnull final ProjectApiRoot client, final int numberOfGetConfigInvocations) { verify(client, times(1)).close(); // Verify config is accessed for the success message after sync: // " example: Syncing products from CTP project with key 'x' to project with key 'y' is done"," - verify(client, times(numberOfGetConfigInvocations)).getConfig(); + verify(client, times(numberOfGetConfigInvocations)).getProjectKey(); verifyNoMoreInteractions(client); } - @SuppressWarnings("unchecked") - public static void stubClientsCustomObjectService( - @Nonnull final SphereClient client, @Nonnull final ZonedDateTime currentCtpTimestamp) { + public static T readObjectFromResource(final String resourcePath, final Class objectType) { + final InputStream resourceAsStream = + Thread.currentThread().getContextClassLoader().getResourceAsStream(resourcePath); + return fromInputStream(resourceAsStream, objectType); + } - final CustomObject> customObject = - mockLastSyncCustomObject(currentCtpTimestamp); + public static T readObject(final String jsonString, final Class objectType) + throws JsonProcessingException { + final ObjectMapper objectMapper = JsonUtils.getConfiguredObjectMapper(); + return objectMapper.readValue(jsonString, objectType); + } - when(client.execute(any(CustomObjectUpsertCommand.class))) - .thenReturn(CompletableFuture.completedFuture(customObject)); + public static String readStringFromFile(final String resourcePath) { + final InputStream resourceAsStream = + Thread.currentThread().getContextClassLoader().getResourceAsStream(resourcePath); + try { + return new String(resourceAsStream.readAllBytes(), StandardCharsets.UTF_8); + } catch (IOException e) { + return StringUtils.EMPTY; + } + } - final PagedQueryResult>> - queriedCustomObjects = spy(PagedQueryResult.empty()); - when(queriedCustomObjects.getResults()).thenReturn(singletonList(customObject)); + @SuppressWarnings("unchecked") + public static void stubClientsCustomObjectService( + @Nonnull final ProjectApiRoot client, @Nonnull final ZonedDateTime currentCtpTimestamp) { + + final CustomObject customObject = mockLastSyncCustomObject(currentCtpTimestamp); + final ApiHttpResponse apiHttpResponse = mock(ApiHttpResponse.class); + when(apiHttpResponse.getBody()).thenReturn(customObject); + final ByProjectKeyCustomObjectsPost customObjectsPost = + mock(ByProjectKeyCustomObjectsPost.class); + when(customObjectsPost.execute()) + .thenReturn(CompletableFuture.completedFuture(apiHttpResponse)); + when(client.customObjects()).thenReturn(mock()); + when(client.customObjects().post(any(CustomObjectDraft.class))).thenReturn(customObjectsPost); + when(client.customObjects().withContainerAndKey(anyString(), anyString())).thenReturn(mock()); + when(client.customObjects().withContainerAndKey(anyString(), anyString()).get()) + .thenReturn(mock()); + when(client.customObjects().withContainerAndKey(anyString(), anyString()).get().execute()) + .thenReturn(CompletableFuture.completedFuture(apiHttpResponse)); + } - when(client.execute(any(CustomObjectQuery.class))) - .thenReturn(CompletableFuture.completedFuture(queriedCustomObjects)); + public static ProjectApiRoot withTestClient( + final String projectKey, + final TriFunction>> + fn) { + return ApiRootBuilder.of( + request -> { + final String uri = request.getUri() != null ? request.getUri().toString() : ""; + final ApiHttpMethod method = request.getMethod(); + final String encodedRequestBody = + uri.contains("graphql") + ? new String(request.getBody(), StandardCharsets.UTF_8) + : ""; + final CompletableFuture> response = + fn.apply(uri, method, encodedRequestBody); + if (response != null) { + return response; + } + return null; + }) + .withApiBaseUrl("testBaseUri") + .build(projectKey); } @Nonnull @SuppressWarnings("unchecked") - public static CustomObject> mockLastSyncCustomObject( - @Nonnull ZonedDateTime currentCtpTimestamp) { - final CustomObject> customObject = - mock(CustomObject.class); + public static CustomObject mockLastSyncCustomObject(@Nonnull ZonedDateTime currentCtpTimestamp) { + final CustomObject customObject = mock(CustomObject.class); final LastSyncCustomObject lastSyncCustomObject = LastSyncCustomObject.of(ZonedDateTime.now(), new ProductSyncStatistics(), 100); @@ -229,14 +292,55 @@ public static Clock getMockedClock() { return clock; } - public static void mockResourceIdsGraphQlRequest(SphereClient client, String id, String key) { + public static void mockResourceIdsGraphQlRequest( + ProjectApiRoot client, String resource, String id, String key) { final String jsonResponseString = - "{\"results\":[{\"id\":\"" + id + "\"," + "\"key\":\"" + key + "\"}]}"; - final ResourceKeyIdGraphQlResult result = - SphereJsonUtils.readObject(jsonResponseString, ResourceKeyIdGraphQlResult.class); + "{\"data\":{\"" + + resource + + "\":{\"results\":[{\"id\":\"" + + id + + "\"," + + "\"key\":\"" + + key + + "\"}]}}}"; + final GraphQLResponse result = + JsonUtils.fromJsonString(jsonResponseString, GraphQLResponse.class); + + final ApiHttpResponse apiHttpResponse = mock(ApiHttpResponse.class); + + when(apiHttpResponse.getBody()).thenReturn(result); + final ByProjectKeyGraphqlRequestBuilder byProjectKeyGraphqlRequestBuilder = mock(); + when(client.graphql()).thenReturn(byProjectKeyGraphqlRequestBuilder); + final ByProjectKeyGraphqlPost byProjectKeyGraphqlPost = mock(); + when(byProjectKeyGraphqlRequestBuilder.post(any(GraphQLRequest.class))) + .thenReturn(byProjectKeyGraphqlPost); + when(byProjectKeyGraphqlPost.execute()) + .thenReturn(CompletableFuture.completedFuture(apiHttpResponse)); + } + + public static BadGatewayException createBadGatewayException() { + final String json = getErrorResponseJsonString(500); + return new BadGatewayException( + 500, "", null, "", new ApiHttpResponse<>(500, null, json.getBytes(StandardCharsets.UTF_8))); + } - when(client.execute(any(ResourceIdsGraphQlRequest.class))) - .thenReturn(CompletableFuture.completedFuture(result)); + private static String getErrorResponseJsonString(Integer errorCode) { + final ErrorResponse errorResponse = + ErrorResponseBuilder.of() + .statusCode(errorCode) + .errors(Collections.emptyList()) + .message("test") + .build(); + + final ObjectWriter ow = new ObjectMapper().writer().withDefaultPrettyPrinter(); + String json; + try { + json = ow.writeValueAsString(errorResponse); + } catch (JsonProcessingException e) { + // ignore the error + json = null; + } + return json; } private TestUtils() {}