From 41efa1606c54a89f27363880ed3814ae6aff864d Mon Sep 17 00:00:00 2001 From: aoz Date: Mon, 28 Sep 2020 10:34:01 +0200 Subject: [PATCH 1/6] use caffeine for the cache, as caffeine library supports an evictable cache by size. --- build.gradle | 4 +++- .../sync/services/impl/BaseService.java | 21 ++++++++++++------- .../services/impl/StateServiceImplTest.java | 6 +++--- .../impl/TaxCategoryServiceImplTest.java | 4 ++-- 4 files changed, 21 insertions(+), 14 deletions(-) diff --git a/build.gradle b/build.gradle index 58efa8e992..c818dbca8a 100644 --- a/build.gradle +++ b/build.gradle @@ -16,6 +16,7 @@ ext{ pmdVersion = '6.14.0' jacocoVersion = '0.8.4' findbugsVersion = '3.0.1' + caffeineVersion = '2.8.5' } apply from: "$rootDir/gradle-scripts/plugins.gradle" @@ -46,10 +47,11 @@ dependencies { implementation "com.commercetools.sdk.jvm.core:commercetools-java-client:${commercetoolsJvmSdkVersion}" implementation "com.commercetools.sdk.jvm.core:commercetools-convenience:${commercetoolsJvmSdkVersion}" implementation "com.google.code.findbugs:annotations:${findbugsVersion}" + implementation "com.github.ben-manes.caffeine:caffeine:${caffeineVersion}" testImplementation "org.mockito:mockito-junit-jupiter:${mockitoJunitJupiterVersion}" testImplementation "org.junit.jupiter:junit-jupiter-api:${jupiterApiVersion}" testImplementation "org.junit.jupiter:junit-jupiter-params:${jupiterApiVersion}" testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:${jupiterApiVersion}" testRuntimeOnly "org.junit.vintage:junit-vintage-engine:${jupiterApiVersion}" testImplementation "org.assertj:assertj-core:${assertjVersion}" -} \ No newline at end of file +} diff --git a/src/main/java/com/commercetools/sync/services/impl/BaseService.java b/src/main/java/com/commercetools/sync/services/impl/BaseService.java index ac4f8e85e5..fc46cbcd3d 100644 --- a/src/main/java/com/commercetools/sync/services/impl/BaseService.java +++ b/src/main/java/com/commercetools/sync/services/impl/BaseService.java @@ -3,6 +3,8 @@ import com.commercetools.sync.commons.BaseSyncOptions; import com.commercetools.sync.commons.exceptions.SyncException; import com.commercetools.sync.commons.utils.CtpQueryUtils; +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; import io.sphere.sdk.client.SphereClient; import io.sphere.sdk.commands.DraftBasedCreateCommand; import io.sphere.sdk.commands.UpdateAction; @@ -20,7 +22,6 @@ import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; -import java.util.concurrent.ConcurrentHashMap; import java.util.function.BiFunction; import java.util.function.Consumer; import java.util.function.Function; @@ -47,7 +48,9 @@ abstract class BaseService, S extends BaseSyncOp Q extends MetaModelQueryDsl, M, E> { final S syncOptions; - final Map keyToIdCache = new ConcurrentHashMap<>(); + protected final Cache keyToIdCache = Caffeine.newBuilder() + .maximumSize(100_000) + .build(); private static final int MAXIMUM_ALLOWED_UPDATE_ACTIONS = 500; private static final String CREATE_FAILED = "Failed to create draft with key: '%s'. Reason: %s"; @@ -170,8 +173,10 @@ CompletionStage> fetchCachedResourceId( if (isBlank(key)) { return CompletableFuture.completedFuture(Optional.empty()); } - if (keyToIdCache.containsKey(key)) { - return CompletableFuture.completedFuture(Optional.ofNullable(keyToIdCache.get(key))); + + final String id = keyToIdCache.getIfPresent(key); + if (id != null) { + return CompletableFuture.completedFuture(Optional.of(id)); } return fetchAndCache(key, keyMapper, querySupplier); } @@ -186,7 +191,7 @@ private CompletionStage> fetchAndCache( return CtpQueryUtils .queryAll(syncOptions.getCtpClient(), querySupplier.get(), pageConsumer) - .thenApply(result -> Optional.ofNullable(keyToIdCache.get(key))); + .thenApply(result -> Optional.ofNullable(keyToIdCache.getIfPresent(key))); } /** @@ -208,11 +213,11 @@ CompletionStage> cacheKeysToIds( final Set keysNotCached = keys .stream() .filter(StringUtils::isNotBlank) - .filter(key -> !keyToIdCache.containsKey(key)) + .filter(key -> !keyToIdCache.asMap().containsKey(key)) .collect(Collectors.toSet()); if (keysNotCached.isEmpty()) { - return CompletableFuture.completedFuture(keyToIdCache); + return CompletableFuture.completedFuture(keyToIdCache.asMap()); } final Consumer> pageConsumer = page -> page.forEach(resource -> @@ -220,7 +225,7 @@ CompletionStage> cacheKeysToIds( return CtpQueryUtils .queryAll(syncOptions.getCtpClient(), keysQueryMapper.apply(keysNotCached), pageConsumer) - .thenApply(result -> keyToIdCache); + .thenApply(result -> keyToIdCache.asMap()); } /** diff --git a/src/test/java/com/commercetools/sync/services/impl/StateServiceImplTest.java b/src/test/java/com/commercetools/sync/services/impl/StateServiceImplTest.java index b604698f9d..42f3589ea9 100644 --- a/src/test/java/com/commercetools/sync/services/impl/StateServiceImplTest.java +++ b/src/test/java/com/commercetools/sync/services/impl/StateServiceImplTest.java @@ -130,7 +130,7 @@ void fetchMatchingStatesByKeys_WithKeySet_ShouldFetchStates() { assertAll( () -> assertThat(states).isNotEmpty(), () -> assertThat(states).contains(mock1, mock2), - () -> assertThat(service.keyToIdCache).containsKeys(key1, key2) + () -> assertThat(service.keyToIdCache.asMap()).containsKeys(key1, key2) ); ArgumentCaptor captor = ArgumentCaptor.forClass(StateQuery.class); verify(client).execute(captor.capture()); @@ -164,7 +164,7 @@ void shouldFetchStatesByKeysWithExpandedTransitions() { assertAll( () -> assertThat(states).isNotEmpty(), () -> assertThat(states).contains(mock1, mock2), - () -> assertThat(service.keyToIdCache).containsKeys(key1, key2) + () -> assertThat(service.keyToIdCache.asMap()).containsKeys(key1, key2) ); ArgumentCaptor captor = ArgumentCaptor.forClass(StateQuery.class); @@ -189,7 +189,7 @@ void fetchState_WithKey_ShouldFetchState() { assertAll( () -> assertThat(stateOptional).containsSame(mock), - () -> assertThat(service.keyToIdCache.get(stateKey)).isEqualTo(stateId) + () -> assertThat(service.keyToIdCache.getIfPresent(stateKey)).isEqualTo(stateId) ); verify(client).execute(any(StateQuery.class)); } diff --git a/src/test/java/com/commercetools/sync/services/impl/TaxCategoryServiceImplTest.java b/src/test/java/com/commercetools/sync/services/impl/TaxCategoryServiceImplTest.java index ff4a77773d..7a8e28bd9e 100644 --- a/src/test/java/com/commercetools/sync/services/impl/TaxCategoryServiceImplTest.java +++ b/src/test/java/com/commercetools/sync/services/impl/TaxCategoryServiceImplTest.java @@ -119,7 +119,7 @@ void fetchMatchingTaxCategoriesByKeys_WithKeySet_ShouldFetchTaxCategories() { assertAll( () -> assertThat(taxCategories).contains(mock1, mock2), - () -> assertThat(service.keyToIdCache).containsKeys(key1, key2) + () -> assertThat(service.keyToIdCache.asMap()).containsKeys(key1, key2) ); verify(client).execute(any(TaxCategoryQuery.class)); } @@ -139,7 +139,7 @@ void fetchTaxCategory_WithKey_ShouldFetchTaxCategory() { assertAll( () -> assertThat(taxCategoryOptional).containsSame(mock), - () -> assertThat(service.keyToIdCache.get(taxCategoryKey)).isEqualTo(taxCategoryId) + () -> assertThat(service.keyToIdCache.asMap().get(taxCategoryKey)).isEqualTo(taxCategoryId) ); verify(client).execute(any(TaxCategoryQuery.class)); } From 8de68aff37d5420078ae2abc9569801fc90e65e3 Mon Sep 17 00:00:00 2001 From: aoz Date: Mon, 28 Sep 2020 11:56:57 +0200 Subject: [PATCH 2/6] fix compile issue --- .../sync/services/impl/CustomObjectServiceImplTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/java/com/commercetools/sync/services/impl/CustomObjectServiceImplTest.java b/src/test/java/com/commercetools/sync/services/impl/CustomObjectServiceImplTest.java index ba7010f71b..dc9bb62eac 100644 --- a/src/test/java/com/commercetools/sync/services/impl/CustomObjectServiceImplTest.java +++ b/src/test/java/com/commercetools/sync/services/impl/CustomObjectServiceImplTest.java @@ -128,7 +128,7 @@ void fetchMatchingCustomObjects_WithKeySet_ShouldFetchCustomObjects() { assertAll( () -> assertThat(customObjects).contains(mock1, mock2), - () -> assertThat(service.keyToIdCache).containsKeys( + () -> assertThat(service.keyToIdCache.asMap()).containsKeys( String.valueOf(customObjectCompositeIdlist.get(0)), String.valueOf(customObjectCompositeIdlist.get(1))) ); @@ -154,7 +154,7 @@ void fetchCustomObject_WithKeyAndContainer_ShouldFetchCustomObject() { assertAll( () -> assertThat(customObjectOptional).containsSame(mock), () -> assertThat( - service.keyToIdCache.get( + service.keyToIdCache.asMap().get( CustomObjectCompositeIdentifier.of(customObjectKey, customObjectContainer).toString()) ).isEqualTo(customObjectId) ); From 74441a7add054584de93f4ca777cd1e99fddf188 Mon Sep 17 00:00:00 2001 From: salander85 <70885646+salander85@users.noreply.github.com> Date: Wed, 18 Nov 2020 10:23:56 +0100 Subject: [PATCH 3/6] Use GraphQL API to fetch resource ids (#619) * Implementation of Graphql for fetching the ids * Query customer keys id using graphql * Optimize Pagination * Simplify code * Add UnitTests for new implementation and update existing UTs * Add ITs for GraphQL requests * Optimize performance by skipping fetch totals * Make checkstyle happy * Add UTs to cover old impl * Rename classes and move to package 'models' * Fix UTs by matching correct request type on client execution * Javadoc changes * Fix checkstyle violations * Test graphQL requesting 5 pages * Add suppression for findbugs violation * Get rid of getBytes of string to avoid encoding issues * Simplify UTs * Filter blank values while building graphql where query * Rename resource types * Fix StateSyncIT * Fix ProductTypeWithNestedAttributeSyncIT * Add UT to cover cases in GraphQlQueryAll * Make findbugs happy --- .../integration/commons/CtpQueryUtilsIT.java | 70 ++++++ .../ProductTypeWithNestedAttributeSyncIT.java | 3 +- .../externalsource/states/StateSyncIT.java | 6 +- .../services/impl/ProductServiceImplIT.java | 19 +- .../helpers/ResourceKeyIdGraphQlRequest.java | 132 +++++++++++ .../commons/models/GraphQlQueryResources.java | 24 ++ .../sync/commons/models/ResourceKeyId.java | 25 ++ .../models/ResourceKeyIdGraphQlResult.java | 20 ++ .../sync/commons/utils/CtpQueryUtils.java | 24 ++ .../sync/commons/utils/GraphQlQueryAll.java | 141 +++++++++++ .../sync/commons/utils/QueryAll.java | 2 +- .../sync/services/impl/BaseService.java | 53 ++++- .../services/impl/BaseServiceWithKey.java | 13 +- .../services/impl/CategoryServiceImpl.java | 20 +- .../services/impl/ChannelServiceImpl.java | 8 +- .../impl/CustomerGroupServiceImpl.java | 9 +- .../services/impl/CustomerServiceImpl.java | 8 +- .../services/impl/ProductServiceImpl.java | 6 +- .../services/impl/ProductTypeServiceImpl.java | 7 +- .../sync/services/impl/StateServiceImpl.java | 7 +- .../services/impl/TaxCategoryServiceImpl.java | 8 +- .../sync/services/impl/TypeServiceImpl.java | 7 +- .../sync/categories/CategorySyncTest.java | 27 ++- .../ResourceKeyIdGraphQlRequestTest.java | 219 ++++++++++++++++++ .../sync/commons/utils/CtpQueryUtilsTest.java | 60 ++++- .../commons/utils/GraphQlQueryAllTest.java | 84 +++++++ .../producttypes/ProductTypeSyncTest.java | 3 +- .../services/impl/BaseServiceImplTest.java | 90 +++++-- 28 files changed, 997 insertions(+), 98 deletions(-) create mode 100644 src/main/java/com/commercetools/sync/commons/helpers/ResourceKeyIdGraphQlRequest.java create mode 100644 src/main/java/com/commercetools/sync/commons/models/GraphQlQueryResources.java create mode 100644 src/main/java/com/commercetools/sync/commons/models/ResourceKeyId.java create mode 100644 src/main/java/com/commercetools/sync/commons/models/ResourceKeyIdGraphQlResult.java create mode 100644 src/main/java/com/commercetools/sync/commons/utils/GraphQlQueryAll.java create mode 100644 src/test/java/com/commercetools/sync/commons/helpers/ResourceKeyIdGraphQlRequestTest.java create mode 100644 src/test/java/com/commercetools/sync/commons/utils/GraphQlQueryAllTest.java diff --git a/src/integration-test/java/com/commercetools/sync/integration/commons/CtpQueryUtilsIT.java b/src/integration-test/java/com/commercetools/sync/integration/commons/CtpQueryUtilsIT.java index 93b28ad7cb..6fb2f31807 100644 --- a/src/integration-test/java/com/commercetools/sync/integration/commons/CtpQueryUtilsIT.java +++ b/src/integration-test/java/com/commercetools/sync/integration/commons/CtpQueryUtilsIT.java @@ -1,6 +1,9 @@ package com.commercetools.sync.integration.commons; +import com.commercetools.sync.commons.helpers.ResourceKeyIdGraphQlRequest; +import com.commercetools.sync.commons.models.GraphQlQueryResources; +import com.commercetools.sync.commons.models.ResourceKeyId; import io.sphere.sdk.categories.Category; import io.sphere.sdk.categories.CategoryDraft; import io.sphere.sdk.categories.queries.CategoryQuery; @@ -15,6 +18,7 @@ import java.util.Locale; import java.util.Set; import java.util.function.Consumer; +import java.util.stream.Collectors; import static com.commercetools.sync.commons.utils.CtpQueryUtils.queryAll; import static com.commercetools.sync.integration.commons.utils.CategoryITUtils.OLD_CATEGORY_CUSTOM_TYPE_KEY; @@ -24,6 +28,7 @@ import static com.commercetools.sync.integration.commons.utils.CategoryITUtils.getCategoryDraftsWithPrefix; import static com.commercetools.sync.integration.commons.utils.ITUtils.deleteTypes; import static com.commercetools.sync.integration.commons.utils.SphereClientUtils.CTP_TARGET_CLIENT; +import static java.util.Collections.singleton; import static org.assertj.core.api.Assertions.assertThat; class CtpQueryUtilsIT { @@ -140,4 +145,69 @@ void queryAll_WithCategoryCollectorCallbackOn600Categories_ShouldFetchAllCategor assertThat(categoryPages.get(1)).hasSize(100); } + + @Test + void queryAll_WithIdCollectorConsumerWithSinglePage_ShouldCollectIds() { + final int numberOfCategories = 100; + List categoryDrafts = + getCategoryDraftsWithPrefix(Locale.ENGLISH, "new", null, numberOfCategories); + List categories = createCategories(CTP_TARGET_CLIENT, categoryDrafts); + + Set allCategoryKeys = categories.stream() + .map(category -> category.getKey()) + .collect(Collectors.toSet()); + final List categoryIds = new ArrayList<>(); + + final Consumer> categoryPageConsumer = categoryPageResults -> + categoryPageResults.forEach(category -> categoryIds.add(category.getId())); + + ResourceKeyIdGraphQlRequest resourceKeyIdGraphQlRequest = new ResourceKeyIdGraphQlRequest(allCategoryKeys, + GraphQlQueryResources.CATEGORIES); + + queryAll(CTP_TARGET_CLIENT, resourceKeyIdGraphQlRequest, categoryPageConsumer).toCompletableFuture().join(); + assertThat(categoryIds).hasSize(numberOfCategories); + } + + @Test + void queryAll_WithIdCollectorConsumerWithMultiplePages_ShouldCollectIds() { + final int numberOfCategories = 600; + final List categoryDrafts = getCategoryDraftsWithPrefix(Locale.ENGLISH, "new", + null, numberOfCategories); + List categories = createCategories(CTP_TARGET_CLIENT, categoryDrafts); + Set allCategoryKeys = categories.stream() + .map(category -> category.getKey()) + .collect(Collectors.toSet()); + + final List categoryIds = new ArrayList<>(); + + final Consumer> categoryPageConsumer = categoryPageResults -> + categoryPageResults.forEach(category -> categoryIds.add(category.getId())); + + ResourceKeyIdGraphQlRequest resourceKeyIdGraphQlRequest = + new ResourceKeyIdGraphQlRequest(allCategoryKeys, GraphQlQueryResources.CATEGORIES); + + queryAll(CTP_TARGET_CLIENT, resourceKeyIdGraphQlRequest, categoryPageConsumer).toCompletableFuture().join(); + assertThat(categoryIds).hasSize(numberOfCategories); + } + + @Test + void queryAll_WithIdCollectorConsumerWithRequestedKeySubset_ShouldCollectIds() { + final List categoryDrafts = getCategoryDraftsWithPrefix(Locale.ENGLISH, "new", + null, 5); + List categories = createCategories(CTP_TARGET_CLIENT, categoryDrafts); + + final List categoryIds = new ArrayList<>(); + + final Consumer> categoryPageConsumer = categoryPageResults -> + categoryPageResults.forEach(category -> categoryIds.add(category.getId())); + + ResourceKeyIdGraphQlRequest resourceKeyIdGraphQlRequest = + new ResourceKeyIdGraphQlRequest(singleton(categories.get(0).getKey()), + GraphQlQueryResources.CATEGORIES); + + queryAll(CTP_TARGET_CLIENT, resourceKeyIdGraphQlRequest, categoryPageConsumer).toCompletableFuture().join(); + assertThat(categoryIds).hasSize(1); + assertThat(categoryIds).containsExactly(categories.get(0).getId()); + } + } diff --git a/src/integration-test/java/com/commercetools/sync/integration/externalsource/producttypes/ProductTypeWithNestedAttributeSyncIT.java b/src/integration-test/java/com/commercetools/sync/integration/externalsource/producttypes/ProductTypeWithNestedAttributeSyncIT.java index 65237a4fc2..48e0b22e9c 100644 --- a/src/integration-test/java/com/commercetools/sync/integration/externalsource/producttypes/ProductTypeWithNestedAttributeSyncIT.java +++ b/src/integration-test/java/com/commercetools/sync/integration/externalsource/producttypes/ProductTypeWithNestedAttributeSyncIT.java @@ -1,6 +1,7 @@ package com.commercetools.sync.integration.externalsource.producttypes; +import com.commercetools.sync.commons.helpers.ResourceKeyIdGraphQlRequest; import com.commercetools.sync.producttypes.ProductTypeSync; import com.commercetools.sync.producttypes.ProductTypeSyncOptions; import com.commercetools.sync.producttypes.ProductTypeSyncOptionsBuilder; @@ -282,8 +283,8 @@ void sync_WithNewProductTypeWithFailedFetchOnReferenceResolution_ShouldFail() { final SphereClient ctpClient = spy(CTP_TARGET_CLIENT); final BadGatewayException badGatewayException = new BadGatewayException(); + when(ctpClient.execute(any(ResourceKeyIdGraphQlRequest.class))).thenCallRealMethod(); // should work on caching when(ctpClient.execute(any(ProductTypeQuery.class))) - .thenCallRealMethod() // should work on caching .thenCallRealMethod() // should work when fetching matching product types .thenCallRealMethod() // should work when second fetching matching product types .thenReturn(exceptionallyCompletedFuture(badGatewayException)) // fail on fetching during resolution diff --git a/src/integration-test/java/com/commercetools/sync/integration/externalsource/states/StateSyncIT.java b/src/integration-test/java/com/commercetools/sync/integration/externalsource/states/StateSyncIT.java index e9254b13a1..77b3d0c139 100644 --- a/src/integration-test/java/com/commercetools/sync/integration/externalsource/states/StateSyncIT.java +++ b/src/integration-test/java/com/commercetools/sync/integration/externalsource/states/StateSyncIT.java @@ -1,5 +1,6 @@ package com.commercetools.sync.integration.externalsource.states; +import com.commercetools.sync.commons.helpers.ResourceKeyIdGraphQlRequest; import com.commercetools.sync.commons.models.WaitingToBeResolvedTransitions; import com.commercetools.sync.services.impl.StateServiceImpl; import com.commercetools.sync.services.impl.UnresolvedTransitionsServiceImpl; @@ -751,8 +752,7 @@ void sync_WithExceptionOnResolvingTransition_ShouldUpdateTransitions() { final StateDraft stateBDraft = createStateDraft(keyB, stateC); final State stateB = createStateInSource(stateBDraft); final SphereClient spyClient = spy(CTP_TARGET_CLIENT); - final StateQuery stateQuery = any(StateQuery.class); - when(spyClient.execute(stateQuery)) + when(spyClient.execute(any())) .thenCallRealMethod() .thenReturn(exceptionallyCompletedFuture(new BadGatewayException())); @@ -997,7 +997,7 @@ void sync_WithFailureInKeysToIdCreation_ShouldAddErrorMessage() { final SphereClient spyClient = spy(CTP_TARGET_CLIENT); - when(spyClient.execute(any(StateQuery.class))) + when(spyClient.execute(any(ResourceKeyIdGraphQlRequest.class))) .thenReturn(exceptionallyCompletedFuture(new BadRequestException("a test exception"))) .thenReturn(exceptionallyCompletedFuture(new ConcurrentModificationException())) .thenCallRealMethod(); diff --git a/src/integration-test/java/com/commercetools/sync/integration/services/impl/ProductServiceImplIT.java b/src/integration-test/java/com/commercetools/sync/integration/services/impl/ProductServiceImplIT.java index 829aeedda8..190d176c1b 100644 --- a/src/integration-test/java/com/commercetools/sync/integration/services/impl/ProductServiceImplIT.java +++ b/src/integration-test/java/com/commercetools/sync/integration/services/impl/ProductServiceImplIT.java @@ -34,6 +34,7 @@ import org.junit.jupiter.api.Test; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.List; @@ -223,15 +224,25 @@ void cacheKeysToIds_WithAlreadyCachedKeys_ShouldNotMakeRequestsAndReturnCurrentC .build(); final ProductService spyProductService = new ProductServiceImpl(productSyncOptions); + final ProductDraft productDraft1 = createProductDraftBuilder(PRODUCT_KEY_2_RESOURCE_PATH, + productType.toReference()) + .categories(emptyList()) + .taxCategory(null) + .state(null) + .categoryOrderHints(null) + .build(); + Product product2 = + CTP_TARGET_CLIENT.execute(ProductCreateCommand.of(productDraft1)).toCompletableFuture().join(); - Map cache = spyProductService.cacheKeysToIds(singleton(product.getKey())) - .toCompletableFuture().join(); - assertThat(cache).hasSize(1); + Set keys = Arrays.asList(product.getKey(), product2.getKey()).stream().collect(Collectors.toSet()); + Map cache = spyProductService.cacheKeysToIds(keys).toCompletableFuture().join(); + assertThat(cache).hasSize(2); // Attempt to cache same (already cached) key. cache = spyProductService.cacheKeysToIds(singleton(product.getKey())) .toCompletableFuture().join(); - assertThat(cache).hasSize(1); + assertThat(cache).hasSize(2); + assertThat(cache).containsKeys(product.getKey(), product2.getKey()); // verify only 1 request was made to fetch id the first time, but not second time since it's already in cache. verify(spyClient, times(1)).execute(any()); diff --git a/src/main/java/com/commercetools/sync/commons/helpers/ResourceKeyIdGraphQlRequest.java b/src/main/java/com/commercetools/sync/commons/helpers/ResourceKeyIdGraphQlRequest.java new file mode 100644 index 0000000000..d921ed9dea --- /dev/null +++ b/src/main/java/com/commercetools/sync/commons/helpers/ResourceKeyIdGraphQlRequest.java @@ -0,0 +1,132 @@ +package com.commercetools.sync.commons.helpers; + +import com.commercetools.sync.commons.models.GraphQlQueryResources; +import com.commercetools.sync.commons.models.ResourceKeyIdGraphQlResult; +import com.fasterxml.jackson.databind.JsonNode; +import io.sphere.sdk.client.HttpRequestIntent; +import io.sphere.sdk.client.SphereClient; +import io.sphere.sdk.client.SphereRequest; +import io.sphere.sdk.http.HttpMethod; +import io.sphere.sdk.http.HttpResponse; +import io.sphere.sdk.json.SphereJsonUtils; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.Set; + +import static java.lang.String.format; +import static java.util.Objects.requireNonNull; +import static java.util.stream.Collectors.joining; +import static org.apache.commons.lang3.StringUtils.isBlank; + +public class ResourceKeyIdGraphQlRequest implements SphereRequest { + protected final Set keysToSearch; + protected final GraphQlQueryResources resource; + private long limit = 500; + private String queryPredicate = null; + + /** + * A SphereRequest implementation to allow {@link SphereClient} to execute graphQL queries on CTP. It provides a + * POST request to the CTP graphql API containing body to fetch a set of ids matching given keys of a resource + * defined in endpoint parameter. + * + * @param keysToSearch - a set of keys to fetch matching ids for. + * @param resource - a string representing the name of the resource endpoint. + */ + public ResourceKeyIdGraphQlRequest(@Nonnull final Set keysToSearch, + @Nonnull final GraphQlQueryResources resource) { + + this.keysToSearch = requireNonNull(keysToSearch); + this.resource = resource; + } + + /** + * This method adds a predicate string to the request. + * + * @param predicate - a string representing a query predicate. + * @return - an instance of this class. + */ + @Nonnull + public ResourceKeyIdGraphQlRequest withPredicate(final String predicate) { + + this.queryPredicate = predicate; + return this; + } + + /** + * This method adds a limit to the request. + * + * @param limit - a number representing the query limit parameter + * @return - an instance of this class + */ + @Nonnull + public ResourceKeyIdGraphQlRequest withLimit(final long limit) { + + this.limit = limit; + return this; + } + + @Nullable + @Override + public ResourceKeyIdGraphQlResult deserialize(final HttpResponse httpResponse) { + + final JsonNode rootJsonNode = SphereJsonUtils.parse(httpResponse.getResponseBody()); + if (rootJsonNode.isNull()) { + return null; + } + JsonNode result = rootJsonNode.get("data").get(resource.getName()); + return SphereJsonUtils.readObject(result, ResourceKeyIdGraphQlResult.class); + } + + @Override + public HttpRequestIntent httpRequestIntent() { + + final String body = format("{\"query\": \"{%s}\"}", buildQueryString()); + return HttpRequestIntent.of(HttpMethod.POST, "/graphql", body); + } + + /** + * This method builds a string matching the required format to query a set of ids matching given keys of a + * resource using the CTP graphql API + * + * @return a string representing a graphql query + */ + @Nonnull + String buildQueryString() { + + return format( + "%s(limit: %d, where: \\\"%s\\\", sort: [\\\"id asc\\\"]) { results { id key } }", + this.resource.getName(), this.limit, createWhereQuery(keysToSearch)); + } + + @Nonnull + private String createWhereQuery(@Nonnull final Set keys) { + // The where in the graphql query should look like this in the end => `where: "key in (\"key1\", + // \"key2\")"` + // So we need an escaping backslash before the quote. So to add this: + // We need 1 backslash (2 in java) to escape the quote in the graphql query. + // We need 2 backslashes (4 in java) to escape the backslash in the JSON payload string. + // We need 1 extra backslash to escape the quote in the java string + // hence: 7 backslashes: + final String backslashQuote = "\\\\\\\""; + final String commaSeparatedKeys = + keys.stream() + .filter(key -> !isBlank(key)) + .collect( + joining( + format("%s, %s", backslashQuote, backslashQuote), + backslashQuote, + backslashQuote)); + + String whereQuery = createWhereQuery(commaSeparatedKeys); + return isBlank(this.queryPredicate) ? whereQuery : format("%s AND %s", whereQuery, queryPredicate); + } + + @Nonnull + private static String createWhereQuery(@Nonnull final String commaSeparatedKeys) { + + return format("key in (%s)", commaSeparatedKeys); + } + +} + diff --git a/src/main/java/com/commercetools/sync/commons/models/GraphQlQueryResources.java b/src/main/java/com/commercetools/sync/commons/models/GraphQlQueryResources.java new file mode 100644 index 0000000000..6d4d781b14 --- /dev/null +++ b/src/main/java/com/commercetools/sync/commons/models/GraphQlQueryResources.java @@ -0,0 +1,24 @@ +package com.commercetools.sync.commons.models; + +public enum GraphQlQueryResources { + CATEGORIES("categories"), + CHANNELS("channels"), + CUSTOMER_GROUPS("customerGroups"), + CUSTOMERS("customers"), + PRODUCTS("products"), + PRODUCT_TYPES("productTypes"), + STATES("states"), + TAX_CATEGORIES("taxCategories"), + TYPES("typeDefinitions"), + SHOPPING_LISTS("shoppingLists"); + + private final String name; + + GraphQlQueryResources(final String name) { + this.name = name; + } + + public String getName() { + return name; + } +} diff --git a/src/main/java/com/commercetools/sync/commons/models/ResourceKeyId.java b/src/main/java/com/commercetools/sync/commons/models/ResourceKeyId.java new file mode 100644 index 0000000000..b5e4542850 --- /dev/null +++ b/src/main/java/com/commercetools/sync/commons/models/ResourceKeyId.java @@ -0,0 +1,25 @@ +package com.commercetools.sync.commons.models; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import javax.annotation.Nonnull; + +public final class ResourceKeyId { + private final String key; + private final String id; + + @JsonCreator + public ResourceKeyId(@JsonProperty("key") @Nonnull final String key, @JsonProperty("id") @Nonnull final String id) { + this.key = key; + this.id = id; + } + + public String getKey() { + return key; + } + + public String getId() { + return id; + } +} diff --git a/src/main/java/com/commercetools/sync/commons/models/ResourceKeyIdGraphQlResult.java b/src/main/java/com/commercetools/sync/commons/models/ResourceKeyIdGraphQlResult.java new file mode 100644 index 0000000000..c9ad79d575 --- /dev/null +++ b/src/main/java/com/commercetools/sync/commons/models/ResourceKeyIdGraphQlResult.java @@ -0,0 +1,20 @@ +package com.commercetools.sync.commons.models; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.Set; + +public class ResourceKeyIdGraphQlResult { + private final Set results; + + @JsonCreator + protected ResourceKeyIdGraphQlResult(@JsonProperty("results") final Set results) { + this.results = results; + } + + public Set getResults() { + return results; + } + +} diff --git a/src/main/java/com/commercetools/sync/commons/utils/CtpQueryUtils.java b/src/main/java/com/commercetools/sync/commons/utils/CtpQueryUtils.java index ce5dbb36ec..14a3dddde4 100644 --- a/src/main/java/com/commercetools/sync/commons/utils/CtpQueryUtils.java +++ b/src/main/java/com/commercetools/sync/commons/utils/CtpQueryUtils.java @@ -1,11 +1,14 @@ package com.commercetools.sync.commons.utils; +import com.commercetools.sync.commons.helpers.ResourceKeyIdGraphQlRequest; +import com.commercetools.sync.commons.models.ResourceKeyId; import io.sphere.sdk.client.SphereClient; import io.sphere.sdk.models.ResourceView; import io.sphere.sdk.queries.QueryDsl; import javax.annotation.Nonnull; import java.util.List; +import java.util.Set; import java.util.concurrent.CompletionStage; import java.util.function.Consumer; import java.util.function.Function; @@ -114,6 +117,27 @@ public final class CtpQueryUtils { return queryAll.run(pageConsumer); } + /** + * Creates a graphQL query to fetch all elements where keys matching given set of keys with pagination using a + * combination of limit and id sorting. + * + *

The method takes a {@link Consumer} that is applied on every page of the queried elements. + * + *

NOTE: This method fetches all paged results sequentially as opposed to fetching the pages in parallel. + * + * @param client commercetools client + * @param resourceKeyIdGraphQlRequest graphql query containing predicates and pagination limits + * @param pageConsumer consumer applied on every page queried + * @return a completion stage containing void as a result after the consumer was applied on all pages. + */ + @Nonnull + public static CompletionStage queryAll(@Nonnull final SphereClient client, + @Nonnull final ResourceKeyIdGraphQlRequest resourceKeyIdGraphQlRequest, + @Nonnull final Consumer> pageConsumer) { + GraphQlQueryAll graphQlQueryAll = GraphQlQueryAll.of(client, resourceKeyIdGraphQlRequest, DEFAULT_PAGE_SIZE); + return graphQlQueryAll.run(pageConsumer); + } + private CtpQueryUtils() { } } diff --git a/src/main/java/com/commercetools/sync/commons/utils/GraphQlQueryAll.java b/src/main/java/com/commercetools/sync/commons/utils/GraphQlQueryAll.java new file mode 100644 index 0000000000..1cd6c5b43d --- /dev/null +++ b/src/main/java/com/commercetools/sync/commons/utils/GraphQlQueryAll.java @@ -0,0 +1,141 @@ +package com.commercetools.sync.commons.utils; + +import com.commercetools.sync.commons.helpers.ResourceKeyIdGraphQlRequest; +import com.commercetools.sync.commons.models.ResourceKeyIdGraphQlResult; +import com.commercetools.sync.commons.models.ResourceKeyId; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +import io.sphere.sdk.client.SphereClient; + +import javax.annotation.Nonnull; +import java.util.Iterator; +import java.util.Set; +import java.util.concurrent.CompletionStage; +import java.util.function.Consumer; + +import static java.lang.String.format; +import static java.util.concurrent.CompletableFuture.completedFuture; +import static org.apache.commons.lang3.StringUtils.EMPTY; +import static org.apache.commons.lang3.StringUtils.isBlank; + +final class GraphQlQueryAll { + private final SphereClient client; + private final ResourceKeyIdGraphQlRequest resourceKeyIdGraphQlRequest; + private final long pageSize; + + private Consumer> pageConsumer; + + private GraphQlQueryAll(@Nonnull final SphereClient client, + @Nonnull final ResourceKeyIdGraphQlRequest resourceKeyIdGraphQlRequest, + final long pageSize) { + + this.client = client; + this.resourceKeyIdGraphQlRequest = withDefaults(resourceKeyIdGraphQlRequest, pageSize); + this.pageSize = pageSize; + } + + @Nonnull + private static ResourceKeyIdGraphQlRequest withDefaults( + @Nonnull final ResourceKeyIdGraphQlRequest resourceKeyIdGraphQlRequest, + final long pageSize) { + + return resourceKeyIdGraphQlRequest.withLimit(pageSize); + } + + @Nonnull + static GraphQlQueryAll of(@Nonnull final SphereClient client, + @Nonnull final ResourceKeyIdGraphQlRequest resourceKeyIdGraphQlRequest, + final int pageSize) { + + return new GraphQlQueryAll(client, resourceKeyIdGraphQlRequest, pageSize); + } + + /** + * Given a {@link Consumer} to a page of results of type {@code BaseGraphQlResult}, this method sets this instance's + * {@code pageConsumer} to the supplied value, then it makes requests to fetch the entire result space of a + * graphql query request {@code BaseGraphQlRequest} to CTP, while accepting the consumer on each fetched page. + * + * @param pageConsumer the consumer to accept on each fetched page of the result space. + * @return a future containing void after the consumer accepted all the pages. + */ + @Nonnull + CompletionStage run(@Nonnull final Consumer> pageConsumer) { + + this.pageConsumer = pageConsumer; + final CompletionStage firstPage = client.execute(resourceKeyIdGraphQlRequest); + return queryNextPages(firstPage); + } + + /** + * Given a completion stage {@code currentPageStage} containing a current graphql result + * {@link ResourceKeyIdGraphQlResult}, this method composes the completion stage by first checking if the result + * is null or not. If it is not, then it recursively (by calling itself with the next page's completion stage + * result) composes to the supplied stage, stages of the all next pages' processing. If there is no next page, + * then the result of the {@code currentPageStage} would be null and this method would just return a completed + * future containing null result, which in turn signals the last page of processing. + * + * @param currentPageStage a future containing a graphql result {@link ResourceKeyIdGraphQlResult}. + */ + @Nonnull + private CompletionStage queryNextPages( + @Nonnull final CompletionStage currentPageStage) { + return currentPageStage.thenCompose(currentPage -> + currentPage != null ? queryNextPages(processPageAndGetNext(currentPage)) : completedFuture(null)); + } + + /** + * Given a graphql query result representing a page {@link ResourceKeyIdGraphQlResult}, this method checks if there + * are elements in the result (size > 0), then it consumes the resultant list using this instance's {@code + * pageConsumer}. Then it attempts to fetch the next page if it exists and returns a completion stage + * containing the result of the next page. If there is a next page, then a new future of the next page is returned. + * If there are no more results, the method returns a completed future containing null. + * + * @param page the current page result. + * @return If there is a next page, then a new future of the next page is returned. If there are no more results, + * the method returns a completed future containing null. + */ + @Nonnull + @SuppressFBWarnings("NP_NONNULL_PARAM_VIOLATION") // `https://github.com/findbugsproject/findbugs/issues/79 + private CompletionStage processPageAndGetNext( + @Nonnull final ResourceKeyIdGraphQlResult page) { + final Set currentPageElements = page.getResults(); + if (!currentPageElements.isEmpty()) { + consumePageElements(currentPageElements); + return getNextPageStage(currentPageElements); + } + return completedFuture(null); + } + + private void consumePageElements(@Nonnull final Set pageElements) { + pageConsumer.accept(pageElements); + } + + /** + * Given a list of page elements of type {@code ResourceKeyId}, this method checks if this page is the last page or + * not by checking if the result size is equal to this instance's {@code pageSize}). If it is, then it means + * there might be still more results. However, if not, then it means for sure there are no more results and this + * is the last page. If there is a next page, the id of the last element in the list is used and a future is + * created containing the fetched results which have an id greater than the id of the last element in the list + * and this future is returned. If there are no more results, the method returns a completed future containing null. + * + * @param pageElements set of page elements of type {@code ResourceKeyId}. + * @return a future containing the fetched results which have an id greater than the id of the last element + * in the set. + */ + @Nonnull + @SuppressFBWarnings("NP_NONNULL_PARAM_VIOLATION") // `https://github.com/findbugsproject/findbugs/issues/79 + private CompletionStage getNextPageStage( + @Nonnull final Set pageElements) { + if (pageElements.size() == pageSize) { + String lastElementId = EMPTY; + Iterator iterator = pageElements.iterator(); + while (iterator.hasNext()) { + lastElementId = iterator.next().getId(); + } + final String queryPredicate = isBlank(lastElementId) ? null : format("id > \\\\\\\"%s\\\\\\\"", + lastElementId); + + return client.execute(resourceKeyIdGraphQlRequest.withPredicate(queryPredicate)); + } + return completedFuture(null); + } +} diff --git a/src/main/java/com/commercetools/sync/commons/utils/QueryAll.java b/src/main/java/com/commercetools/sync/commons/utils/QueryAll.java index aefe19dd63..626ec3bb2b 100644 --- a/src/main/java/com/commercetools/sync/commons/utils/QueryAll.java +++ b/src/main/java/com/commercetools/sync/commons/utils/QueryAll.java @@ -42,7 +42,7 @@ private QueryAll(@Nonnull final SphereClient client, private static > QueryDsl withDefaults( @Nonnull final QueryDsl query, final long pageSize) { - final C withLimit = query.withLimit(pageSize); + final C withLimit = query.withLimit(pageSize).withFetchTotal(false); return !withLimit.sort().isEmpty() ? withLimit : withLimit.withSort(QuerySort.of("id asc")); } diff --git a/src/main/java/com/commercetools/sync/services/impl/BaseService.java b/src/main/java/com/commercetools/sync/services/impl/BaseService.java index a68180ecea..15cf5f6046 100644 --- a/src/main/java/com/commercetools/sync/services/impl/BaseService.java +++ b/src/main/java/com/commercetools/sync/services/impl/BaseService.java @@ -2,6 +2,8 @@ import com.commercetools.sync.commons.BaseSyncOptions; import com.commercetools.sync.commons.exceptions.SyncException; +import com.commercetools.sync.commons.helpers.ResourceKeyIdGraphQlRequest; +import com.commercetools.sync.commons.models.ResourceKeyId; import com.commercetools.sync.commons.utils.CtpQueryUtils; import com.github.benmanes.caffeine.cache.Cache; import com.github.benmanes.caffeine.cache.Caffeine; @@ -210,11 +212,7 @@ CompletionStage> cacheKeysToIds( @Nonnull final Function keyMapper, @Nonnull final Function, Q> keysQueryMapper) { - final Set keysNotCached = keys - .stream() - .filter(StringUtils::isNotBlank) - .filter(key -> !keyToIdCache.asMap().containsKey(key)) - .collect(Collectors.toSet()); + final Set keysNotCached = getKeysNotCached(keys); if (keysNotCached.isEmpty()) { return CompletableFuture.completedFuture(keyToIdCache.asMap()); @@ -228,6 +226,51 @@ CompletionStage> cacheKeysToIds( .thenApply(result -> keyToIdCache.asMap()); } + /** + * Given a set of keys this method collects all keys which aren't already contained in the cache + * {@code keyToIdCache} + * + * @param keys {@link Set} of keys + * @return a {@link Set} of keys which aren't already contained in the cache or empty + */ + @Nonnull + private Set getKeysNotCached(@Nonnull final Set keys) { + return keys + .stream() + .filter(StringUtils::isNotBlank) + .filter(key -> !keyToIdCache.asMap().containsKey(key)) + .collect(Collectors.toSet()); + } + + /** + * Given a set of keys this method caches a mapping of the keys to ids of such keys only for the keys which are + * not already in the cache. + * + * @param keys keys to cache. + * @param keyToIdMapper a function to get the key from the resource. + * @param keysRequestMapper function that accepts a set of keys which are not cached and maps it to a graphQL + * request object representing the graphQL query to CTP on such keys. + * @return a map of key to ids of the requested keys. + */ + @Nonnull + CompletionStage> cacheKeysToIdsUsingGraphQl( + @Nonnull final Set keys, + @Nonnull final Function> keyToIdMapper, + @Nonnull final Function, ResourceKeyIdGraphQlRequest> keysRequestMapper) { + + final Set keysNotCached = getKeysNotCached(keys); + + if (keysNotCached.isEmpty()) { + return CompletableFuture.completedFuture(keyToIdCache.asMap()); + } + + final Consumer> resultConsumer = results -> results.forEach(resource -> + keyToIdCache.putAll(keyToIdMapper.apply(resource))); + + return CtpQueryUtils.queryAll(syncOptions.getCtpClient(), keysRequestMapper.apply(keysNotCached), + resultConsumer).thenApply(result -> keyToIdCache.asMap()); + } + /** * Given a {@link Set} of resource keys, this method fetches a set of all the resources, matching this given set of * keys in the CTP project, defined in an injected {@link SphereClient}. A mapping of the key to the id diff --git a/src/main/java/com/commercetools/sync/services/impl/BaseServiceWithKey.java b/src/main/java/com/commercetools/sync/services/impl/BaseServiceWithKey.java index 47427a5cc7..a3ef50de94 100644 --- a/src/main/java/com/commercetools/sync/services/impl/BaseServiceWithKey.java +++ b/src/main/java/com/commercetools/sync/services/impl/BaseServiceWithKey.java @@ -1,6 +1,7 @@ package com.commercetools.sync.services.impl; import com.commercetools.sync.commons.BaseSyncOptions; +import com.commercetools.sync.commons.helpers.ResourceKeyIdGraphQlRequest; import io.sphere.sdk.client.SphereClient; import io.sphere.sdk.commands.DraftBasedCreateCommand; import io.sphere.sdk.models.Resource; @@ -9,6 +10,7 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; +import java.util.Collections; import java.util.Map; import java.util.Optional; import java.util.Set; @@ -27,6 +29,7 @@ * {@link io.sphere.sdk.categories.queries.CategoryQueryModel}, etc.. * @param Expansion Model (e.g. {@link io.sphere.sdk.products.expansion.ProductExpansionModel}, * {@link io.sphere.sdk.categories.expansion.CategoryExpansionModel}, etc.. + * */ abstract class BaseServiceWithKey & WithKey, S extends BaseSyncOptions, Q extends MetaModelQueryDsl, M, E> extends BaseService { @@ -93,18 +96,20 @@ CompletionStage> fetchCachedResourceId( * not already in the cache. * * @param keys keys to cache. - * @param keysQueryMapper function that accepts a set of keys which are not cached and maps it to a query object - * representing the query to CTP on such keys. + * @param keysRequestMapper function that accepts a set of keys which are not cached and maps it to a graphQL + * request object representing the graphql query to CTP on such keys. * @return a map of key to ids of the requested keys. */ @Nonnull CompletionStage> cacheKeysToIds( @Nonnull final Set keys, - @Nonnull final Function, Q> keysQueryMapper) { + @Nonnull final Function, ResourceKeyIdGraphQlRequest> keysRequestMapper) { // Why method reference is not used: // http://mail.openjdk.java.net/pipermail/compiler-dev/2015-November/009824.html - return super.cacheKeysToIds(keys, resource -> resource.getKey(), keysQueryMapper); + return super + .cacheKeysToIdsUsingGraphQl(keys, resource -> Collections.singletonMap(resource.getKey(), resource.getId()), + keysRequestMapper); } /** diff --git a/src/main/java/com/commercetools/sync/services/impl/CategoryServiceImpl.java b/src/main/java/com/commercetools/sync/services/impl/CategoryServiceImpl.java index 60deaff9cc..eca7beb7bf 100644 --- a/src/main/java/com/commercetools/sync/services/impl/CategoryServiceImpl.java +++ b/src/main/java/com/commercetools/sync/services/impl/CategoryServiceImpl.java @@ -2,6 +2,8 @@ import com.commercetools.sync.categories.CategorySyncOptions; +import com.commercetools.sync.commons.helpers.ResourceKeyIdGraphQlRequest; +import com.commercetools.sync.commons.models.GraphQlQueryResources; import com.commercetools.sync.services.CategoryService; import io.sphere.sdk.categories.Category; import io.sphere.sdk.categories.CategoryDraft; @@ -12,8 +14,6 @@ import io.sphere.sdk.categories.queries.CategoryQueryBuilder; import io.sphere.sdk.categories.queries.CategoryQueryModel; import io.sphere.sdk.commands.UpdateAction; -import io.sphere.sdk.queries.QueryPredicate; -import org.apache.commons.lang3.StringUtils; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -22,14 +22,11 @@ import java.util.Optional; import java.util.Set; import java.util.concurrent.CompletionStage; -import java.util.stream.Collectors; -import static java.lang.String.format; import static java.util.Collections.singleton; /** * Implementation of CategoryService interface. - * TODO: USE graphQL to get only keys. GITHUB ISSUE#84 */ public final class CategoryServiceImpl extends BaseServiceWithKey> implements CategoryService { @@ -42,17 +39,8 @@ public CategoryServiceImpl(@Nonnull final CategorySyncOptions syncOptions) { @Override public CompletionStage> cacheKeysToIds(@Nonnull final Set categoryKeys) { return cacheKeysToIds( - categoryKeys, keysNotCached -> CategoryQuery - .of() - .withPredicates(buildCategoryKeysQueryPredicate(keysNotCached))); - } - - QueryPredicate buildCategoryKeysQueryPredicate(@Nonnull final Set categoryKeys) { - final String keysQueryString = categoryKeys.stream() - .filter(StringUtils::isNotBlank) - .map(productKey -> format("\"%s\"", productKey)) - .collect(Collectors.joining(",")); - return QueryPredicate.of(format("key in (%s)", keysQueryString)); + categoryKeys, + keysNotCached -> new ResourceKeyIdGraphQlRequest(keysNotCached, GraphQlQueryResources.CATEGORIES)); } @Nonnull diff --git a/src/main/java/com/commercetools/sync/services/impl/ChannelServiceImpl.java b/src/main/java/com/commercetools/sync/services/impl/ChannelServiceImpl.java index ac019d9368..8a80e9b263 100644 --- a/src/main/java/com/commercetools/sync/services/impl/ChannelServiceImpl.java +++ b/src/main/java/com/commercetools/sync/services/impl/ChannelServiceImpl.java @@ -1,6 +1,8 @@ package com.commercetools.sync.services.impl; import com.commercetools.sync.commons.BaseSyncOptions; +import com.commercetools.sync.commons.helpers.ResourceKeyIdGraphQlRequest; +import com.commercetools.sync.commons.models.GraphQlQueryResources; import com.commercetools.sync.services.ChannelService; import io.sphere.sdk.channels.Channel; import io.sphere.sdk.channels.ChannelDraft; @@ -37,10 +39,8 @@ public ChannelServiceImpl( public CompletionStage> cacheKeysToIds(@Nonnull final Set channelKeys) { return cacheKeysToIds( - channelKeys, keysNotCached -> ChannelQueryBuilder - .of() - .plusPredicates(channelQueryModel -> channelQueryModel.key().isIn(keysNotCached)) - .build()); + channelKeys, + keysNotCached -> new ResourceKeyIdGraphQlRequest(keysNotCached, GraphQlQueryResources.CHANNELS)); } @Nonnull diff --git a/src/main/java/com/commercetools/sync/services/impl/CustomerGroupServiceImpl.java b/src/main/java/com/commercetools/sync/services/impl/CustomerGroupServiceImpl.java index 43006dbff8..342327120e 100644 --- a/src/main/java/com/commercetools/sync/services/impl/CustomerGroupServiceImpl.java +++ b/src/main/java/com/commercetools/sync/services/impl/CustomerGroupServiceImpl.java @@ -1,6 +1,8 @@ package com.commercetools.sync.services.impl; import com.commercetools.sync.commons.BaseSyncOptions; +import com.commercetools.sync.commons.helpers.ResourceKeyIdGraphQlRequest; +import com.commercetools.sync.commons.models.GraphQlQueryResources; import com.commercetools.sync.services.CustomerGroupService; import io.sphere.sdk.customergroups.CustomerGroup; import io.sphere.sdk.customergroups.CustomerGroupDraft; @@ -29,11 +31,8 @@ public CustomerGroupServiceImpl(@Nonnull final BaseSyncOptions syncOptions) { public CompletionStage> cacheKeysToIds(@Nonnull final Set customerGroupKeys) { return cacheKeysToIds( - customerGroupKeys, keysNotCached -> CustomerGroupQueryBuilder - .of() - .plusPredicates(customerGroupQueryModel -> - customerGroupQueryModel.key().isIn(keysNotCached)) - .build()); + customerGroupKeys, keysNotCached -> new ResourceKeyIdGraphQlRequest(keysNotCached, + GraphQlQueryResources.CUSTOMER_GROUPS)); } @Nonnull diff --git a/src/main/java/com/commercetools/sync/services/impl/CustomerServiceImpl.java b/src/main/java/com/commercetools/sync/services/impl/CustomerServiceImpl.java index 195cc5522f..a804477f9a 100644 --- a/src/main/java/com/commercetools/sync/services/impl/CustomerServiceImpl.java +++ b/src/main/java/com/commercetools/sync/services/impl/CustomerServiceImpl.java @@ -1,6 +1,8 @@ package com.commercetools.sync.services.impl; import com.commercetools.sync.commons.exceptions.SyncException; +import com.commercetools.sync.commons.helpers.ResourceKeyIdGraphQlRequest; +import com.commercetools.sync.commons.models.GraphQlQueryResources; import com.commercetools.sync.customers.CustomerSyncOptions; import com.commercetools.sync.services.CustomerService; import io.sphere.sdk.commands.UpdateAction; @@ -37,10 +39,8 @@ public CustomerServiceImpl(@Nonnull final CustomerSyncOptions syncOptions) { @Override public CompletionStage> cacheKeysToIds( @Nonnull final Set keysToCache) { - return cacheKeysToIds(keysToCache, keysNotCached -> CustomerQueryBuilder - .of() - .plusPredicates(customerQueryModel -> customerQueryModel.key().isIn(keysNotCached)) - .build()); + return cacheKeysToIds(keysToCache, keysNotCached -> new ResourceKeyIdGraphQlRequest(keysNotCached, + GraphQlQueryResources.CUSTOMERS)); } @Nonnull diff --git a/src/main/java/com/commercetools/sync/services/impl/ProductServiceImpl.java b/src/main/java/com/commercetools/sync/services/impl/ProductServiceImpl.java index 21974e8ee6..83abd9d208 100644 --- a/src/main/java/com/commercetools/sync/services/impl/ProductServiceImpl.java +++ b/src/main/java/com/commercetools/sync/services/impl/ProductServiceImpl.java @@ -1,5 +1,7 @@ package com.commercetools.sync.services.impl; +import com.commercetools.sync.commons.helpers.ResourceKeyIdGraphQlRequest; +import com.commercetools.sync.commons.models.GraphQlQueryResources; import com.commercetools.sync.products.ProductSyncOptions; import com.commercetools.sync.services.ProductService; import io.sphere.sdk.commands.UpdateAction; @@ -48,9 +50,7 @@ public CompletionStage> cacheKeysToIds(@Nonnull final Set ProductQuery - .of() - .withPredicates(buildProductKeysQueryPredicate(keysNotCached))); + keysNotCached -> new ResourceKeyIdGraphQlRequest(keysNotCached, GraphQlQueryResources.PRODUCTS)); } QueryPredicate buildProductKeysQueryPredicate(@Nonnull final Set productKeys) { diff --git a/src/main/java/com/commercetools/sync/services/impl/ProductTypeServiceImpl.java b/src/main/java/com/commercetools/sync/services/impl/ProductTypeServiceImpl.java index 6baa9f26e3..550a858c02 100644 --- a/src/main/java/com/commercetools/sync/services/impl/ProductTypeServiceImpl.java +++ b/src/main/java/com/commercetools/sync/services/impl/ProductTypeServiceImpl.java @@ -1,6 +1,8 @@ package com.commercetools.sync.services.impl; import com.commercetools.sync.commons.BaseSyncOptions; +import com.commercetools.sync.commons.helpers.ResourceKeyIdGraphQlRequest; +import com.commercetools.sync.commons.models.GraphQlQueryResources; import com.commercetools.sync.commons.utils.CtpQueryUtils; import com.commercetools.sync.products.AttributeMetaData; import com.commercetools.sync.services.ProductTypeService; @@ -43,10 +45,7 @@ public CompletionStage> cacheKeysToIds(@Nonnull final Set ProductTypeQueryBuilder - .of() - .plusPredicates(queryModel -> queryModel.key().isIn(keysNotCached)) - .build()); + keysNotCached -> new ResourceKeyIdGraphQlRequest(keysNotCached, GraphQlQueryResources.PRODUCT_TYPES)); } @Nonnull diff --git a/src/main/java/com/commercetools/sync/services/impl/StateServiceImpl.java b/src/main/java/com/commercetools/sync/services/impl/StateServiceImpl.java index 25d7ddb893..1f6cb9a770 100644 --- a/src/main/java/com/commercetools/sync/services/impl/StateServiceImpl.java +++ b/src/main/java/com/commercetools/sync/services/impl/StateServiceImpl.java @@ -1,5 +1,7 @@ package com.commercetools.sync.services.impl; +import com.commercetools.sync.commons.helpers.ResourceKeyIdGraphQlRequest; +import com.commercetools.sync.commons.models.GraphQlQueryResources; import com.commercetools.sync.services.StateService; import com.commercetools.sync.states.StateSyncOptions; import io.sphere.sdk.commands.UpdateAction; @@ -39,10 +41,7 @@ public CompletionStage> cacheKeysToIds(@Nonnull final Set StateQueryBuilder - .of() - .plusPredicates(queryModel -> queryModel.key().isIn(keysNotCached)) - .build()); + keysNotCached -> new ResourceKeyIdGraphQlRequest(keysNotCached, GraphQlQueryResources.STATES)); } @Nonnull diff --git a/src/main/java/com/commercetools/sync/services/impl/TaxCategoryServiceImpl.java b/src/main/java/com/commercetools/sync/services/impl/TaxCategoryServiceImpl.java index e121af2284..5d5d871d5c 100644 --- a/src/main/java/com/commercetools/sync/services/impl/TaxCategoryServiceImpl.java +++ b/src/main/java/com/commercetools/sync/services/impl/TaxCategoryServiceImpl.java @@ -1,5 +1,7 @@ package com.commercetools.sync.services.impl; +import com.commercetools.sync.commons.helpers.ResourceKeyIdGraphQlRequest; +import com.commercetools.sync.commons.models.GraphQlQueryResources; import com.commercetools.sync.services.TaxCategoryService; import com.commercetools.sync.taxcategories.TaxCategorySyncOptions; import io.sphere.sdk.commands.UpdateAction; @@ -35,10 +37,8 @@ public TaxCategoryServiceImpl(@Nonnull final TaxCategorySyncOptions syncOptions) public CompletionStage> cacheKeysToIds(@Nonnull final Set taxCategoryKeys) { return cacheKeysToIds( - taxCategoryKeys, keysNotCached -> TaxCategoryQueryBuilder - .of() - .plusPredicates(taxCategoryQueryModel -> taxCategoryQueryModel.key().isIn(keysNotCached)) - .build()); + taxCategoryKeys, + keysNotCached -> new ResourceKeyIdGraphQlRequest(keysNotCached, GraphQlQueryResources.TAX_CATEGORIES)); } @Nonnull diff --git a/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java b/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java index 683ed14532..dc6a650291 100644 --- a/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java +++ b/src/main/java/com/commercetools/sync/services/impl/TypeServiceImpl.java @@ -1,6 +1,8 @@ package com.commercetools.sync.services.impl; import com.commercetools.sync.commons.BaseSyncOptions; +import com.commercetools.sync.commons.helpers.ResourceKeyIdGraphQlRequest; +import com.commercetools.sync.commons.models.GraphQlQueryResources; import com.commercetools.sync.services.TypeService; import io.sphere.sdk.commands.UpdateAction; import io.sphere.sdk.types.Type; @@ -36,10 +38,7 @@ public TypeServiceImpl(@Nonnull final BaseSyncOptions syncOptions) { public CompletionStage> cacheKeysToIds(@Nonnull final Set typeKeys) { return cacheKeysToIds( - typeKeys, keysNotCached -> TypeQueryBuilder - .of() - .plusPredicates(typeQueryModel -> typeQueryModel.key().isIn(keysNotCached)) - .build()); + typeKeys, keysNotCached -> new ResourceKeyIdGraphQlRequest(keysNotCached, GraphQlQueryResources.TYPES)); } @Nonnull diff --git a/src/test/java/com/commercetools/sync/categories/CategorySyncTest.java b/src/test/java/com/commercetools/sync/categories/CategorySyncTest.java index 08035964b2..513f202061 100644 --- a/src/test/java/com/commercetools/sync/categories/CategorySyncTest.java +++ b/src/test/java/com/commercetools/sync/categories/CategorySyncTest.java @@ -2,6 +2,9 @@ import com.commercetools.sync.categories.helpers.CategorySyncStatistics; import com.commercetools.sync.commons.exceptions.ReferenceResolutionException; +import com.commercetools.sync.commons.helpers.ResourceKeyIdGraphQlRequest; +import com.commercetools.sync.commons.models.ResourceKeyId; +import com.commercetools.sync.commons.models.ResourceKeyIdGraphQlResult; import com.commercetools.sync.services.CategoryService; import com.commercetools.sync.services.TypeService; import com.commercetools.sync.services.impl.CategoryServiceImpl; @@ -14,7 +17,6 @@ import io.sphere.sdk.models.LocalizedString; import io.sphere.sdk.models.ResourceIdentifier; import io.sphere.sdk.models.SphereException; -import io.sphere.sdk.queries.PagedQueryResult; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -412,7 +414,7 @@ void sync_WithDefaultBatchSize_ShouldCallSyncOnEachBatch() { void sync_WithFailOnCachingKeysToIds_ShouldTriggerErrorCallbackAndReturnProperStats() { // preparation final SphereClient mockClient = mock(SphereClient.class); - when(mockClient.execute(any(CategoryQuery.class))) + when(mockClient.execute(any(ResourceKeyIdGraphQlRequest.class))) .thenReturn(supplyAsync(() -> { throw new SphereException(); })); @@ -459,18 +461,21 @@ void sync_WithFailOnFetchingCategories_ShouldTriggerErrorCallbackAndReturnProper final String categoryKey = "key"; final Category mockCategory = getMockCategory("foo", categoryKey); + ResourceKeyId resourceKeyId = new ResourceKeyId(categoryKey, "foo"); + + @SuppressWarnings("unchecked") final ResourceKeyIdGraphQlResult resourceKeyIdGraphQlResult = mock( + ResourceKeyIdGraphQlResult.class); + when(resourceKeyIdGraphQlResult.getResults()).thenReturn(singleton(resourceKeyId)); - // It is safe here to cast PagedQueryResult to PagedQueryResult since we are sure - // it can be safely casted but the compiler doesn't see the generic type because of erasure. - @SuppressWarnings("unchecked") final PagedQueryResult pagedQueryResult = mock(PagedQueryResult.class); - when(pagedQueryResult.getResults()).thenReturn(singletonList(mockCategory)); + // successful caching + when(mockClient.execute(any(ResourceKeyIdGraphQlRequest.class))) + .thenReturn(CompletableFuture.completedFuture(resourceKeyIdGraphQlResult)); - // successful caching but exception on fetch. + // exception on fetch. when(mockClient.execute(any(CategoryQuery.class))) - .thenReturn(CompletableFuture.completedFuture(pagedQueryResult)) - .thenReturn(supplyAsync(() -> { - throw new SphereException(); - })); + .thenReturn(supplyAsync(() -> { + throw new SphereException(); + })); when(mockClient.execute(any(CategoryCreateCommand.class))) .thenReturn(CompletableFuture.completedFuture(mockCategory)); diff --git a/src/test/java/com/commercetools/sync/commons/helpers/ResourceKeyIdGraphQlRequestTest.java b/src/test/java/com/commercetools/sync/commons/helpers/ResourceKeyIdGraphQlRequestTest.java new file mode 100644 index 0000000000..33256a8fae --- /dev/null +++ b/src/test/java/com/commercetools/sync/commons/helpers/ResourceKeyIdGraphQlRequestTest.java @@ -0,0 +1,219 @@ +package com.commercetools.sync.commons.helpers; + +import com.commercetools.sync.commons.models.GraphQlQueryResources; +import com.commercetools.sync.commons.models.ResourceKeyIdGraphQlResult; +import com.fasterxml.jackson.core.JsonProcessingException; +import io.sphere.sdk.client.HttpRequestIntent; +import io.sphere.sdk.http.HttpMethod; +import io.sphere.sdk.http.HttpResponse; +import io.sphere.sdk.http.StringHttpRequestBody; +import org.junit.jupiter.api.Test; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +import static java.util.Collections.singleton; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class ResourceKeyIdGraphQlRequestTest { + + @Test + void newGraphQlRequest_WithNullKeys_ShouldThrowNullPointerException() { + //test & assertion + assertThatThrownBy( + () -> new ResourceKeyIdGraphQlRequest(null, GraphQlQueryResources.CATEGORIES)) + .isExactlyInstanceOf(NullPointerException.class); + } + + @Test + void httpRequestIntent_WithEmptyKeys_ShouldReturnQueryStringWithEmptyKeysClause() { + //preparation + final ResourceKeyIdGraphQlRequest resourceKeyIdGraphQlRequest = + new ResourceKeyIdGraphQlRequest(Collections.emptySet(), GraphQlQueryResources.CATEGORIES); + + //test + final HttpRequestIntent httpRequestIntent = resourceKeyIdGraphQlRequest.httpRequestIntent(); + + //assertions + assertThat(httpRequestIntent.getBody()).isExactlyInstanceOf(StringHttpRequestBody.class); + assertThat(httpRequestIntent.getHttpMethod()).isEqualByComparingTo(HttpMethod.POST); + final StringHttpRequestBody requestBody = (StringHttpRequestBody) httpRequestIntent.getBody(); + assertThat(requestBody).isNotNull(); + assertThat(requestBody.getString()) + .isEqualTo("{\"query\": \"{categories(limit: 500, where: \\\"key" + + " in (\\\\\\\"\\\\\\\")\\\", sort: [\\\"id asc\\\"]) { results { id key } }}\"}"); + } + + @Test + void httpRequestIntent_WithKeys_ShouldReturnCorrectQueryString() { + //preparation + final Set keysToSearch = new HashSet<>(); + keysToSearch.add("key1"); + keysToSearch.add("key2"); + final ResourceKeyIdGraphQlRequest resourceKeyIdGraphQlRequest = + new ResourceKeyIdGraphQlRequest(keysToSearch, GraphQlQueryResources.CATEGORIES); + + //test + final HttpRequestIntent httpRequestIntent = resourceKeyIdGraphQlRequest.httpRequestIntent(); + + //assertions + assertThat(httpRequestIntent.getBody()).isExactlyInstanceOf(StringHttpRequestBody.class); + assertThat(httpRequestIntent.getHttpMethod()).isEqualByComparingTo(HttpMethod.POST); + final StringHttpRequestBody requestBody = (StringHttpRequestBody) httpRequestIntent.getBody(); + assertThat(requestBody).isNotNull(); + assertThat(requestBody.getString()) + .isEqualTo("{\"query\": \"{categories(limit: 500, where: \\\"key" + + " in (\\\\\\\"key1\\\\\\\", \\\\\\\"key2\\\\\\\")\\\", sort: [\\\"id asc\\\"]) { results { id key } " + + "}}\"}"); + } + + @Test + void httpRequestIntent_WithSomeEmptyAndNullKeys_ShouldReturnCorrectQueryString() { + //preparation + final Set keysToSearch = new HashSet<>(); + keysToSearch.add("key1"); + keysToSearch.add(""); + keysToSearch.add("key2"); + keysToSearch.add(null); + final ResourceKeyIdGraphQlRequest resourceKeyIdGraphQlRequest = + new ResourceKeyIdGraphQlRequest(keysToSearch, GraphQlQueryResources.CATEGORIES); + + //test + final HttpRequestIntent httpRequestIntent = resourceKeyIdGraphQlRequest.httpRequestIntent(); + + //assertions + assertThat(httpRequestIntent.getBody()).isExactlyInstanceOf(StringHttpRequestBody.class); + assertThat(httpRequestIntent.getHttpMethod()).isEqualByComparingTo(HttpMethod.POST); + final StringHttpRequestBody requestBody = (StringHttpRequestBody) httpRequestIntent.getBody(); + assertThat(requestBody).isNotNull(); + assertThat(requestBody.getString()) + .isEqualTo("{\"query\": \"{categories(limit: 500, where: \\\"key" + + " in (\\\\\\\"key1\\\\\\\", \\\\\\\"key2\\\\\\\")\\\", sort: [\\\"id asc\\\"]) { results { id key } " + + "}}\"}"); + } + + @Test + void httpRequestIntent_WithKeyAndExplicitLimit_ShouldReturnCorrectQueryString() { + //preparation + final ResourceKeyIdGraphQlRequest resourceKeyIdGraphQlRequest = + new ResourceKeyIdGraphQlRequest(singleton("key1"), GraphQlQueryResources.CATEGORIES).withLimit(10); + + //test + final HttpRequestIntent httpRequestIntent = resourceKeyIdGraphQlRequest.httpRequestIntent(); + + //assertions + assertThat(httpRequestIntent.getBody()).isExactlyInstanceOf(StringHttpRequestBody.class); + assertThat(httpRequestIntent.getHttpMethod()).isEqualByComparingTo(HttpMethod.POST); + final StringHttpRequestBody requestBody = (StringHttpRequestBody) httpRequestIntent.getBody(); + assertThat(requestBody).isNotNull(); + assertThat(requestBody.getString()) + .isEqualTo("{\"query\": \"{categories(limit: 10, where: \\\"key" + + " in (\\\\\\\"key1\\\\\\\")\\\", sort: [\\\"id asc\\\"]) { results { id key } " + + "}}\"}"); + } + + @Test + void httpRequestIntent_WithKeyAndPredicate_ShouldReturnCorrectQueryString() { + //preparation + final ResourceKeyIdGraphQlRequest resourceKeyIdGraphQlRequest = + new ResourceKeyIdGraphQlRequest(singleton("key1"), GraphQlQueryResources.CATEGORIES) + .withPredicate("id > \\\\\\\"id" + + "\\\\\\\""); + + //test + final HttpRequestIntent httpRequestIntent = resourceKeyIdGraphQlRequest.httpRequestIntent(); + + //assertions + assertThat(httpRequestIntent.getBody()).isExactlyInstanceOf(StringHttpRequestBody.class); + assertThat(httpRequestIntent.getHttpMethod()).isEqualByComparingTo(HttpMethod.POST); + final StringHttpRequestBody requestBody = (StringHttpRequestBody) httpRequestIntent.getBody(); + assertThat(requestBody).isNotNull(); + assertThat(requestBody.getString()) + .isEqualTo("{\"query\": \"{categories(limit: 500, where: \\\"key" + + " in (\\\\\\\"key1\\\\\\\") AND id > \\\\\\\"id\\\\\\\"\\\", sort: [\\\"id asc\\\"])" + + " { results { id key } }}\"}"); + } + + @Test + void deserialize_WithEmptyResult_ShouldReturnNull() throws JsonProcessingException { + //preparation + final HttpResponse httpResponse = HttpResponse.of(200, "null"); + final ResourceKeyIdGraphQlRequest resourceKeyIdGraphQlRequest = + new ResourceKeyIdGraphQlRequest(singleton("key-1"), GraphQlQueryResources.CATEGORIES); + + //test + final ResourceKeyIdGraphQlResult result = resourceKeyIdGraphQlRequest.deserialize(httpResponse); + + //assertions + assertThat(result).isNull(); + } + + @Test + void deserialize_WithEmptyResult_ShouldDeserializeCorrectly() throws JsonProcessingException { + //preparation + String jsonAsString = "{\"data\":{\"categories\":{\"results\":[]}}}"; + + final HttpResponse httpResponse = HttpResponse.of(200, jsonAsString); + + final ResourceKeyIdGraphQlRequest resourceKeyIdGraphQlRequest = + new ResourceKeyIdGraphQlRequest(singleton("key-1"), GraphQlQueryResources.CATEGORIES); + + //test + final ResourceKeyIdGraphQlResult result = resourceKeyIdGraphQlRequest.deserialize(httpResponse); + + //assertions + assertThat(result).isNotNull(); + assertThat(result.getResults()).isEmpty(); + } + + @Test + void deserialize_WithSingleResult_ShouldReturnSingletonMap() throws JsonProcessingException { + //preparation + String jsonAsString = "{\"data\":{\"categories\":{\"results\":[{\"id\":\"id-1\",\"key\":\"key-1\"}]}}}"; + + final HttpResponse httpResponse = HttpResponse.of(200, jsonAsString); + + final ResourceKeyIdGraphQlRequest resourceKeyIdGraphQlRequest = + new ResourceKeyIdGraphQlRequest(singleton("key-1"), GraphQlQueryResources.CATEGORIES); + + //test + final ResourceKeyIdGraphQlResult result = resourceKeyIdGraphQlRequest.deserialize(httpResponse); + + //assertions + assertThat(result).isNotNull(); + assertThat(result.getResults()).hasSize(1); + assertThat(result.getResults()) + .extracting("key") + .containsExactly("key-1"); + assertThat(result.getResults()) + .extracting("id") + .containsExactly("id-1"); + } + + @Test + void deserialize_WithMultipleResults_ShouldReturnCorrectResult() throws JsonProcessingException { + //preparation + String jsonAsString = "{\"data\":{\"categories\":{\"results\":[{\"id\":\"id-1\",\"key\":\"key-1\"}," + + "{\"id\":\"id-2\",\"key\":\"key-2\"},{\"id\":\"id-3\",\"key\":\"key-3\"}]}}}"; + + final HttpResponse httpResponse = HttpResponse.of(200, jsonAsString); + + final ResourceKeyIdGraphQlRequest resourceKeyIdGraphQlRequest = + new ResourceKeyIdGraphQlRequest(singleton("key-1"), GraphQlQueryResources.CATEGORIES); + + //test + final ResourceKeyIdGraphQlResult result = resourceKeyIdGraphQlRequest.deserialize(httpResponse); + + //assertions + assertThat(result).isNotNull(); + assertThat(result.getResults()).hasSize(3); + assertThat(result.getResults()) + .extracting("key") + .containsExactlyInAnyOrder("key-1", "key-2", "key-3"); + assertThat(result.getResults()) + .extracting("id") + .containsExactlyInAnyOrder("id-1", "id-2", "id-3"); + } +} diff --git a/src/test/java/com/commercetools/sync/commons/utils/CtpQueryUtilsTest.java b/src/test/java/com/commercetools/sync/commons/utils/CtpQueryUtilsTest.java index 1be3de8263..48f8999ee5 100644 --- a/src/test/java/com/commercetools/sync/commons/utils/CtpQueryUtilsTest.java +++ b/src/test/java/com/commercetools/sync/commons/utils/CtpQueryUtilsTest.java @@ -1,5 +1,9 @@ package com.commercetools.sync.commons.utils; +import com.commercetools.sync.commons.helpers.ResourceKeyIdGraphQlRequest; +import com.commercetools.sync.commons.models.ResourceKeyIdGraphQlResult; +import com.commercetools.sync.commons.models.GraphQlQueryResources; +import com.commercetools.sync.commons.models.ResourceKeyId; import io.sphere.sdk.categories.Category; import io.sphere.sdk.categories.queries.CategoryQuery; import io.sphere.sdk.categories.queries.CategoryQueryModel; @@ -17,6 +21,7 @@ import javax.annotation.Nonnull; import java.util.List; +import java.util.Set; import java.util.UUID; import java.util.function.Function; import java.util.stream.Collectors; @@ -37,6 +42,8 @@ class CtpQueryUtilsTest { @Captor private ArgumentCaptor sphereRequestArgumentCaptor; + @Captor + private ArgumentCaptor graphQlRequestArgumentCaptor; @BeforeEach void init() { @@ -104,6 +111,53 @@ void queryAll_WithCustomSort_ShouldNotSortByIdAsc() { getSecondPageQuery(keyPredicateFunction, categories, QuerySort.of("id desc"))); } + @Test + void queryAll_WithGraphQlRequest_ShouldFetchPagedResult() { + // preparation + final SphereClient sphereClient = mock(SphereClient.class); + final List resultPage = IntStream + .range(0, 500) + .mapToObj(i -> mock(ResourceKeyId.class)) + .collect(Collectors.toList()); + final ResourceKeyId lastCategory = resultPage.get(resultPage.size() - 1); + when(lastCategory.getId()).thenReturn(UUID.randomUUID().toString()); + + final Set mockPage = resultPage.stream().collect(Collectors.toSet()); + + final ResourceKeyIdGraphQlResult pagedResourceKeyIdGraphQlResult = mock(ResourceKeyIdGraphQlResult.class); + when(pagedResourceKeyIdGraphQlResult.getResults()) + .thenReturn(mockPage) + .thenReturn(mockPage) + .thenReturn(mockPage) + .thenReturn(mockPage) + .thenReturn(mockPage.stream().limit(10).collect(Collectors.toSet())); + + when(sphereClient.execute(any())).thenReturn(completedFuture(pagedResourceKeyIdGraphQlResult)); + + final Set keysToQuery = IntStream + .range(1, 2010) + .mapToObj(i -> "key" + i) + .collect(Collectors.toSet()); + + ResourceKeyIdGraphQlRequest resourceKeyIdGraphQlRequest = + new ResourceKeyIdGraphQlRequest(keysToQuery, GraphQlQueryResources.CATEGORIES); + + // test + queryAll(sphereClient, resourceKeyIdGraphQlRequest, results -> identity()) + .toCompletableFuture() + .join(); + + // assertions + verify(sphereClient, times(5)).execute(graphQlRequestArgumentCaptor.capture()); + assertThat(graphQlRequestArgumentCaptor.getAllValues()) + .containsExactly( + resourceKeyIdGraphQlRequest.withLimit(500), + resourceKeyIdGraphQlRequest.withLimit(500).withPredicate(format("id > \\\\\\\"%s\\\\\\\"", "id")), + resourceKeyIdGraphQlRequest.withLimit(500).withPredicate(format("id > \\\\\\\"%s\\\\\\\"", "id")), + resourceKeyIdGraphQlRequest.withLimit(500).withPredicate(format("id > \\\\\\\"%s\\\\\\\"", "id")), + resourceKeyIdGraphQlRequest.withLimit(500).withPredicate(format("id > \\\\\\\"%s\\\\\\\"", "id"))); + } + @Nonnull private List getMockCategoryPage() { final List categories = IntStream @@ -134,7 +188,8 @@ private CategoryQuery getFirstPageQuery( return CategoryQuery .of() .plusPredicates(keyPredicate).withSort(sort) - .withLimit(500); + .withLimit(500) + .withFetchTotal(false); } @Nonnull @@ -151,6 +206,7 @@ private CategoryQuery getSecondPageQuery( .of() .plusPredicates(keyPredicateFunction).withSort(sort) .plusPredicates(QueryPredicate.of(format("id > \"%s\"", lastCategoryIdInPage))) - .withLimit(500); + .withLimit(500) + .withFetchTotal(false); } } \ No newline at end of file diff --git a/src/test/java/com/commercetools/sync/commons/utils/GraphQlQueryAllTest.java b/src/test/java/com/commercetools/sync/commons/utils/GraphQlQueryAllTest.java new file mode 100644 index 0000000000..cf88c5f515 --- /dev/null +++ b/src/test/java/com/commercetools/sync/commons/utils/GraphQlQueryAllTest.java @@ -0,0 +1,84 @@ +package com.commercetools.sync.commons.utils; + + +import com.commercetools.sync.commons.helpers.ResourceKeyIdGraphQlRequest; +import com.commercetools.sync.commons.models.ResourceKeyId; +import com.commercetools.sync.commons.models.ResourceKeyIdGraphQlResult; +import io.sphere.sdk.client.SphereClient; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; + +import static io.sphere.sdk.queries.QueryExecutionUtils.DEFAULT_PAGE_SIZE; +import static java.util.Collections.emptySet; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +class GraphQlQueryAllTest { + + private static final ResourceKeyIdGraphQlResult pagedGraphQlQueryResult = mock(ResourceKeyIdGraphQlResult.class); + private static final SphereClient sphereClient = mock(SphereClient.class); + + @BeforeAll + static void setup() { + when(sphereClient.execute(any())).thenReturn(CompletableFuture.completedFuture(pagedGraphQlQueryResult)); + } + + @Test + void run_WithConsumer_ShouldApplyConsumer() { + //preparation + ResourceKeyId resource1 = new ResourceKeyId("key1", "id1"); + ResourceKeyId resource2 = new ResourceKeyId("key2", "id2"); + ResourceKeyId resource3 = new ResourceKeyId("key3", "id3"); + ResourceKeyId resource4 = new ResourceKeyId("key4", "id4"); + + Set results = new HashSet<>(); + results.add(resource1); + results.add(resource2); + results.add(resource3); + results.add(resource4); + when(pagedGraphQlQueryResult.getResults()).thenReturn(results); + + final GraphQlQueryAll query = + GraphQlQueryAll.of(sphereClient, mock(ResourceKeyIdGraphQlRequest.class), DEFAULT_PAGE_SIZE); + final List resourceIds = new ArrayList<>(); + + final Consumer> resourceIdCollector = page -> + page.forEach(resource -> resourceIds.add(resource.getId())); + + //test + query.run(resourceIdCollector).toCompletableFuture().join(); + + //assertions + assertThat(resourceIds).hasSize(4); + assertThat(resourceIds).containsExactlyInAnyOrder("id2", "id1", "id3", "id4"); + } + + @Test + void run_WithEmptyResults_ShouldSkipConsumer() { + //preparation + when(pagedGraphQlQueryResult.getResults()).thenReturn(emptySet()); + + final GraphQlQueryAll query = + GraphQlQueryAll.of(sphereClient, mock(ResourceKeyIdGraphQlRequest.class), DEFAULT_PAGE_SIZE); + final List resourceIds = new ArrayList<>(); + + final Consumer> resourceIdCollector = page -> + page.forEach(resource -> resourceIds.add(resource.getId())); + + //test + query.run(resourceIdCollector).toCompletableFuture().join(); + + //assertions + assertThat(resourceIds).isEmpty(); + } + +} diff --git a/src/test/java/com/commercetools/sync/producttypes/ProductTypeSyncTest.java b/src/test/java/com/commercetools/sync/producttypes/ProductTypeSyncTest.java index 891b42b773..ff0faa865f 100644 --- a/src/test/java/com/commercetools/sync/producttypes/ProductTypeSyncTest.java +++ b/src/test/java/com/commercetools/sync/producttypes/ProductTypeSyncTest.java @@ -1,6 +1,7 @@ package com.commercetools.sync.producttypes; import com.commercetools.sync.commons.exceptions.SyncException; +import com.commercetools.sync.commons.helpers.ResourceKeyIdGraphQlRequest; import com.commercetools.sync.producttypes.helpers.ProductTypeSyncStatistics; import com.commercetools.sync.services.ProductTypeService; import com.commercetools.sync.services.impl.ProductTypeServiceImpl; @@ -547,7 +548,7 @@ void sync_WithErrorCachingKeys_ShouldExecuteCallbackOnErrorAndIncreaseFailedCoun final ProductTypeService mockProductTypeService = new ProductTypeServiceImpl(syncOptions); - when(sphereClient.execute(any(ProductTypeQuery.class))) + when(sphereClient.execute(any(ResourceKeyIdGraphQlRequest.class))) .thenReturn(supplyAsync(() -> { throw new SphereException(); })); final ProductTypeSync productTypeSync = new ProductTypeSync(syncOptions, mockProductTypeService); diff --git a/src/test/java/com/commercetools/sync/services/impl/BaseServiceImplTest.java b/src/test/java/com/commercetools/sync/services/impl/BaseServiceImplTest.java index 290cc4d8e3..31a9cb8fbe 100644 --- a/src/test/java/com/commercetools/sync/services/impl/BaseServiceImplTest.java +++ b/src/test/java/com/commercetools/sync/services/impl/BaseServiceImplTest.java @@ -1,12 +1,20 @@ package com.commercetools.sync.services.impl; import com.commercetools.sync.commons.exceptions.SyncException; +import com.commercetools.sync.commons.helpers.ResourceKeyIdGraphQlRequest; +import com.commercetools.sync.commons.models.ResourceKeyIdGraphQlResult; +import com.commercetools.sync.commons.models.ResourceKeyId; import com.commercetools.sync.commons.utils.TriConsumer; +import com.commercetools.sync.customobjects.CustomObjectSyncOptions; +import com.commercetools.sync.customobjects.CustomObjectSyncOptionsBuilder; +import com.commercetools.sync.customobjects.helpers.CustomObjectCompositeIdentifier; import com.commercetools.sync.products.ProductSyncOptions; import com.commercetools.sync.products.ProductSyncOptionsBuilder; import com.commercetools.sync.services.ProductService; import io.sphere.sdk.client.BadGatewayException; 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.queries.ProductQuery; @@ -33,6 +41,7 @@ import static java.util.Collections.singletonList; import static java.util.concurrent.CompletableFuture.completedFuture; import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -253,7 +262,7 @@ void fetchResource_WithBadGateWayException_ShouldCompleteExceptionally() { } @Test - void cacheKeysToIds_WithEmptySetOfKeys_ShouldMakeNoRequestAndReturnEmptyOptional() { + void cacheKeysToIdsUsingGraphQl_WithEmptySetOfKeys_ShouldMakeNoRequestAndReturnEmptyOptional() { //test final Map optional = service.cacheKeysToIds(emptySet()).toCompletableFuture().join(); @@ -263,7 +272,7 @@ void cacheKeysToIds_WithEmptySetOfKeys_ShouldMakeNoRequestAndReturnEmptyOptional } @Test - void cacheKeysToIds_WithAllCachedKeys_ShouldMakeNoRequestAndReturnCachedEntry() { + void cacheKeysToIdsUsingGraphQl_WithAllCachedKeys_ShouldMakeNoRequestAndReturnCachedEntry() { //preparation final PagedQueryResult pagedQueryResult = mock(PagedQueryResult.class); final Product mockProductResult = mock(Product.class); @@ -284,36 +293,36 @@ void cacheKeysToIds_WithAllCachedKeys_ShouldMakeNoRequestAndReturnCachedEntry() } @Test - void cacheKeysToIds_WithNoCachedKeys_ShouldMakeRequestAndReturnCachedEntry() { + void cacheKeysToIdsUsingGraphQl_WithNoCachedKeys_ShouldMakeRequestAndReturnCachedEntry() { //preparation - final PagedQueryResult pagedQueryResult = mock(PagedQueryResult.class); - final Product mockProductResult = mock(Product.class); + final ResourceKeyIdGraphQlResult resourceKeyIdGraphQlResult = mock(ResourceKeyIdGraphQlResult.class); + final ResourceKeyId mockResourceKeyId = mock(ResourceKeyId.class); final String key = "testKey"; final String id = "testId"; - when(mockProductResult.getKey()).thenReturn(key); - when(mockProductResult.getId()).thenReturn(id); - when(pagedQueryResult.getResults()).thenReturn(singletonList(mockProductResult)); - when(client.execute(any())).thenReturn(completedFuture(pagedQueryResult)); + when(mockResourceKeyId.getKey()).thenReturn(key); + when(mockResourceKeyId.getId()).thenReturn(id); + when(resourceKeyIdGraphQlResult.getResults()).thenReturn(singleton(mockResourceKeyId)); + when(client.execute(any())).thenReturn(completedFuture(resourceKeyIdGraphQlResult)); //test final Map optional = service.cacheKeysToIds(singleton("testKey")).toCompletableFuture().join(); //assertions assertThat(optional).containsExactly(MapEntry.entry(key, id)); - verify(client, times(1)).execute(any(ProductQuery.class)); + verify(client, times(1)).execute(any(ResourceKeyIdGraphQlRequest.class)); } @Test - void cacheKeysToIds_WithBadGateWayException_ShouldCompleteExceptionally() { + void cacheKeysToIdsUsingGraphQl_WithBadGateWayException_ShouldCompleteExceptionally() { //preparation - final PagedQueryResult pagedQueryResult = mock(PagedQueryResult.class); - final Product mockProductResult = mock(Product.class); + final ResourceKeyIdGraphQlResult resourceKeyIdGraphQlResult = mock(ResourceKeyIdGraphQlResult.class); + final ResourceKeyId mockResourceKeyId = mock(ResourceKeyId.class); final String key = "testKey"; final String id = "testId"; - when(mockProductResult.getKey()).thenReturn(key); - when(mockProductResult.getId()).thenReturn(id); - when(pagedQueryResult.getResults()).thenReturn(singletonList(mockProductResult)); - when(client.execute(any(ProductQuery.class))) + when(mockResourceKeyId.getKey()).thenReturn(key); + when(mockResourceKeyId.getId()).thenReturn(id); + when(resourceKeyIdGraphQlResult.getResults()).thenReturn(singleton(mockResourceKeyId)); + when(client.execute(any(ResourceKeyIdGraphQlRequest.class))) .thenReturn(CompletableFutureUtils.exceptionallyCompletedFuture(new BadGatewayException())); //test @@ -321,6 +330,51 @@ void cacheKeysToIds_WithBadGateWayException_ShouldCompleteExceptionally() { //assertions assertThat(result).hasFailedWithThrowableThat().isExactlyInstanceOf(BadGatewayException.class); - verify(client, times(1)).execute(any(ProductQuery.class)); + verify(client, times(1)).execute(any(ResourceKeyIdGraphQlRequest.class)); + } + + @Test + void cacheKeysToIds_WithEmptySetOfKeys_ShouldNotMakeRequestAndReturnEmpty() { + //preparation + CustomObjectSyncOptions customObjectSyncOptions = CustomObjectSyncOptionsBuilder.of(client).build(); + CustomObjectServiceImpl serviceImpl = new CustomObjectServiceImpl(customObjectSyncOptions); + + //test + final Map optional = serviceImpl.cacheKeysToIds(emptySet()).toCompletableFuture().join(); + + //assertions + assertThat(optional).isEmpty(); + verify(client, never()).execute(any()); + } + + @Test + void cacheKeysToIds_WithEmptyCache_ShouldMakeRequestAndReturnCacheEntries() { + //preparation + final CustomObjectSyncOptions customObjectSyncOptions = CustomObjectSyncOptionsBuilder.of(client).build(); + final CustomObjectServiceImpl serviceImpl = new CustomObjectServiceImpl(customObjectSyncOptions); + final PagedQueryResult pagedQueryResult = mock(PagedQueryResult.class); + final CustomObject customObject = mock(CustomObject.class); + final String customObjectId = "customObjectId"; + final String customObjectContainer = "customObjectContainer"; + final String customObjectKey = "customObjectKey"; + + when(customObject.getId()).thenReturn(customObjectId); + when(customObject.getKey()).thenReturn(customObjectKey); + when(customObject.getContainer()).thenReturn(customObjectContainer); + when(pagedQueryResult.getResults()).thenReturn(singletonList(customObject)); + when(client.execute(any())).thenReturn(completedFuture(pagedQueryResult)); + + + final Map result = serviceImpl + .cacheKeysToIds(singleton(CustomObjectCompositeIdentifier.of(customObjectKey, customObjectContainer))) + .toCompletableFuture().join(); + + assertAll( + () -> assertThat(result).hasSize(1), + () -> assertThat(result.get( + CustomObjectCompositeIdentifier.of(customObjectKey, customObjectContainer).toString()) + ).isEqualTo(customObjectId) + ); + verify(client).execute(any(CustomObjectQuery.class)); } } From 9c022d73601936ad18bde9143954af87d2a66856 Mon Sep 17 00:00:00 2001 From: salander85 Date: Mon, 23 Nov 2020 11:15:53 +0100 Subject: [PATCH 4/6] Use graphQL to fetch shoppingLists IDs --- .../sync/services/impl/ShoppingListServiceImpl.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/commercetools/sync/services/impl/ShoppingListServiceImpl.java b/src/main/java/com/commercetools/sync/services/impl/ShoppingListServiceImpl.java index 0684cc0823..2465225fce 100644 --- a/src/main/java/com/commercetools/sync/services/impl/ShoppingListServiceImpl.java +++ b/src/main/java/com/commercetools/sync/services/impl/ShoppingListServiceImpl.java @@ -1,5 +1,7 @@ package com.commercetools.sync.services.impl; +import com.commercetools.sync.commons.helpers.ResourceKeyIdGraphQlRequest; +import com.commercetools.sync.commons.models.GraphQlQueryResources; import com.commercetools.sync.services.ShoppingListService; import com.commercetools.sync.shoppinglists.ShoppingListSyncOptions; import io.sphere.sdk.commands.UpdateAction; @@ -10,11 +12,11 @@ import io.sphere.sdk.shoppinglists.commands.ShoppingListUpdateCommand; import io.sphere.sdk.shoppinglists.expansion.ShoppingListExpansionModel; import io.sphere.sdk.shoppinglists.queries.ShoppingListQuery; -import io.sphere.sdk.shoppinglists.queries.ShoppingListQueryBuilder; import io.sphere.sdk.shoppinglists.queries.ShoppingListQueryModel; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Optional; @@ -36,11 +38,9 @@ public ShoppingListServiceImpl(@Nonnull final ShoppingListSyncOptions syncOption @Override public CompletionStage> cacheKeysToIds(@Nonnull final Set shoppingListKeys) { - return cacheKeysToIds( - shoppingListKeys, ShoppingList::getKey, keysNotCached -> ShoppingListQueryBuilder - .of() - .plusPredicates(shoppingListQueryModel -> shoppingListQueryModel.key().isIn(keysNotCached)) - .build()); + return cacheKeysToIdsUsingGraphQl( + shoppingListKeys, resource -> Collections.singletonMap(resource.getKey(), resource.getId()), + keysNotCached -> new ResourceKeyIdGraphQlRequest(keysNotCached, GraphQlQueryResources.SHOPPING_LISTS)); } @Nonnull From 056ab4155f6d19d0c3608e88378f30d6e2493119 Mon Sep 17 00:00:00 2001 From: salander85 <70885646+salander85@users.noreply.github.com> Date: Tue, 24 Nov 2020 09:56:26 +0100 Subject: [PATCH 5/6] Improve current caching solution (#628) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add option to configure cache size * Cleanup cache and add UTs for config cache size * Adjust UT after merge * Cleanup cache and correct javadoc * Adjust javadoc and correct indentation * Remove manual cache cleanup * Change deprecated method call * Revert "Remove manual cache cleanup" This reverts commit 120f43bb2d59c9bbc3f856c86d3a85bba651f21c. * Remove manual cache cleanup from BaseService * Revert "Remove manual cache cleanup from BaseService" This reverts commit 484ec7dc6e57fe627aa3f06df74bc3cb861377e5. * Execute eviction on current thread, remove manual cleanup * Address review comments * Add cache map size to shoppinglists sync options * Add release notes for the change * Remove release date from the notes Co-authored-by: Ahmet Öz Co-authored-by: King-Hin Leung Co-authored-by: Ahmet Öz --- docs/RELEASE_NOTES.md | 11 +++++ .../CartDiscountSyncOptions.java | 27 +++++++----- .../CartDiscountSyncOptionsBuilder.java | 3 +- .../sync/categories/CategorySyncOptions.java | 6 ++- .../CategorySyncOptionsBuilder.java | 3 +- .../sync/commons/BaseSyncOptions.java | 17 +++++++- .../sync/commons/BaseSyncOptionsBuilder.java | 20 +++++++++ .../sync/customers/CustomerSyncOptions.java | 4 +- .../customers/CustomerSyncOptionsBuilder.java | 3 +- .../CustomObjectSyncOptions.java | 6 ++- .../CustomObjectSyncOptionsBuilder.java | 3 +- .../inventories/InventorySyncOptions.java | 5 ++- .../InventorySyncOptionsBuilder.java | 3 +- .../sync/products/ProductSyncOptions.java | 4 +- .../products/ProductSyncOptionsBuilder.java | 1 + .../producttypes/ProductTypeSyncOptions.java | 5 ++- .../ProductTypeSyncOptionsBuilder.java | 3 +- .../sync/services/impl/BaseService.java | 8 ++-- .../ShoppingListSyncOptions.java | 6 ++- .../ShoppingListSyncOptionsBuilder.java | 3 +- .../sync/states/StateSyncOptions.java | 5 ++- .../sync/states/StateSyncOptionsBuilder.java | 3 +- .../taxcategories/TaxCategorySyncOptions.java | 5 ++- .../TaxCategorySyncOptionsBuilder.java | 3 +- .../sync/types/TypeSyncOptions.java | 5 ++- .../sync/types/TypeSyncOptionsBuilder.java | 3 +- .../CartDiscountSyncOptionsBuilderTest.java | 26 ++++++++++++ .../CategorySyncOptionsBuilderTest.java | 24 +++++++++++ .../CustomerSyncOptionsBuilderTest.java | 25 +++++++++++ .../CustomObjectSyncOptionsBuilderTest.java | 24 +++++++++++ .../ProductSyncOptionsBuilderTest.java | 23 +++++++++++ .../ProductTypeSyncOptionsBuilderTest.java | 25 +++++++++++ .../services/impl/BaseServiceImplTest.java | 41 ++++++++++++++++++- .../ShoppingListSyncOptionsBuilderTest.java | 28 +++++++++++++ .../states/StateSyncOptionsBuilderTest.java | 29 ++++++++++++- .../TaxCategorySyncOptionsBuilderTest.java | 31 +++++++++++++- .../types/TypeSyncOptionsBuilderTest.java | 24 +++++++++++ 37 files changed, 416 insertions(+), 49 deletions(-) diff --git a/docs/RELEASE_NOTES.md b/docs/RELEASE_NOTES.md index 589013897f..c9cdebe271 100644 --- a/docs/RELEASE_NOTES.md +++ b/docs/RELEASE_NOTES.md @@ -27,7 +27,18 @@ 7. Add Migration guide section which specifies explicitly if there are breaking changes and how to tackle them. --> + + ### 3.0.0 - Nov 18, 2020 [Commits](https://github.com/commercetools/commercetools-sync-java/compare/2.3.0...3.0.0) | [Javadoc](https://commercetools.github.io/commercetools-sync-java/v/3.0.0/) | diff --git a/src/main/java/com/commercetools/sync/cartdiscounts/CartDiscountSyncOptions.java b/src/main/java/com/commercetools/sync/cartdiscounts/CartDiscountSyncOptions.java index 916b5fa781..4508f04deb 100644 --- a/src/main/java/com/commercetools/sync/cartdiscounts/CartDiscountSyncOptions.java +++ b/src/main/java/com/commercetools/sync/cartdiscounts/CartDiscountSyncOptions.java @@ -19,16 +19,23 @@ public final class CartDiscountSyncOptions extends BaseSyncOptions { CartDiscountSyncOptions( - @Nonnull final SphereClient ctpClient, - @Nullable final QuadConsumer, Optional, - List>> errorCallback, - @Nullable final TriConsumer, Optional> - warningCallback, - final int batchSize, - @Nullable final TriFunction>, CartDiscountDraft, - CartDiscount, List>> beforeUpdateCallback, - @Nullable final Function beforeCreateCallback) { + @Nonnull final SphereClient ctpClient, + @Nullable final QuadConsumer, Optional, + List>> errorCallback, + @Nullable final TriConsumer, Optional> + warningCallback, + final int batchSize, + @Nullable final TriFunction>, CartDiscountDraft, + CartDiscount, List>> beforeUpdateCallback, + @Nullable final Function beforeCreateCallback, + final long cacheSize) { - super(ctpClient, errorCallback, warningCallback, batchSize, beforeUpdateCallback, beforeCreateCallback); + super(ctpClient, + errorCallback, + warningCallback, + batchSize, + beforeUpdateCallback, + beforeCreateCallback, + cacheSize); } } diff --git a/src/main/java/com/commercetools/sync/cartdiscounts/CartDiscountSyncOptionsBuilder.java b/src/main/java/com/commercetools/sync/cartdiscounts/CartDiscountSyncOptionsBuilder.java index 127a3f6154..aadcc14b47 100644 --- a/src/main/java/com/commercetools/sync/cartdiscounts/CartDiscountSyncOptionsBuilder.java +++ b/src/main/java/com/commercetools/sync/cartdiscounts/CartDiscountSyncOptionsBuilder.java @@ -41,7 +41,8 @@ public CartDiscountSyncOptions build() { warningCallback, batchSize, beforeUpdateCallback, - beforeCreateCallback + beforeCreateCallback, + cacheSize ); } diff --git a/src/main/java/com/commercetools/sync/categories/CategorySyncOptions.java b/src/main/java/com/commercetools/sync/categories/CategorySyncOptions.java index 25a08a9070..87fee0d784 100644 --- a/src/main/java/com/commercetools/sync/categories/CategorySyncOptions.java +++ b/src/main/java/com/commercetools/sync/categories/CategorySyncOptions.java @@ -27,12 +27,14 @@ public final class CategorySyncOptions extends BaseSyncOptions>, CategoryDraft, Category, List>> beforeUpdateCallback, - @Nullable final Function beforeCreateCallback) { + @Nullable final Function beforeCreateCallback, + final long cacheSize) { super(ctpClient, errorCallback, warningCallback, batchSize, beforeUpdateCallback, - beforeCreateCallback); + beforeCreateCallback, + cacheSize); } } diff --git a/src/main/java/com/commercetools/sync/categories/CategorySyncOptionsBuilder.java b/src/main/java/com/commercetools/sync/categories/CategorySyncOptionsBuilder.java index 2d83e70284..c55369d4f7 100644 --- a/src/main/java/com/commercetools/sync/categories/CategorySyncOptionsBuilder.java +++ b/src/main/java/com/commercetools/sync/categories/CategorySyncOptionsBuilder.java @@ -42,7 +42,8 @@ public CategorySyncOptions build() { warningCallback, batchSize, beforeUpdateCallback, - beforeCreateCallback); + beforeCreateCallback, + cacheSize); } /** diff --git a/src/main/java/com/commercetools/sync/commons/BaseSyncOptions.java b/src/main/java/com/commercetools/sync/commons/BaseSyncOptions.java index aa9370b8db..5b235194e4 100644 --- a/src/main/java/com/commercetools/sync/commons/BaseSyncOptions.java +++ b/src/main/java/com/commercetools/sync/commons/BaseSyncOptions.java @@ -29,6 +29,7 @@ public class BaseSyncOptions { private int batchSize; private final TriFunction>, V, U, List>> beforeUpdateCallback; private final Function beforeCreateCallback; + private long cacheSize; protected BaseSyncOptions( @Nonnull final SphereClient ctpClient, @@ -38,13 +39,14 @@ protected BaseSyncOptions( final int batchSize, @Nullable final TriFunction>, V, U, List>> beforeUpdateCallback, - @Nullable final Function beforeCreateCallback) { + @Nullable final Function beforeCreateCallback, final long cacheSize) { this.ctpClient = ctpClient; this.errorCallback = errorCallback; this.batchSize = batchSize; this.warningCallback = warningCallback; this.beforeUpdateCallback = beforeUpdateCallback; this.beforeCreateCallback = beforeCreateCallback; + this.cacheSize = cacheSize; } /** @@ -158,6 +160,19 @@ public int getBatchSize() { return batchSize; } + /** + * Gets the cache size used in the sync process. To increase performance during the sync some resource keys / ids + * are cached which are required for resolving references. To keep the cache performant old entries are evicted + * when a certain size is reached. + * + *

This cache size is set to 100.000 by default. + * + * @return option that indicates capacity of cache of resource keys + */ + public long getCacheSize() { + return cacheSize; + } + /** * Returns the {@code beforeUpdateCallback} {@link TriFunction}<{@link List}<{@link UpdateAction}< * {@code U}>>, {@code V}, {@code U}, {@link List}<{@link UpdateAction}<{@code U}>>> function diff --git a/src/main/java/com/commercetools/sync/commons/BaseSyncOptionsBuilder.java b/src/main/java/com/commercetools/sync/commons/BaseSyncOptionsBuilder.java index 398d6bb897..0a7a75bc8c 100644 --- a/src/main/java/com/commercetools/sync/commons/BaseSyncOptionsBuilder.java +++ b/src/main/java/com/commercetools/sync/commons/BaseSyncOptionsBuilder.java @@ -21,6 +21,7 @@ public abstract class BaseSyncOptionsBuilder>, V, U, List>> beforeUpdateCallback; protected Function beforeCreateCallback; + protected long cacheSize = 10_000; /** * Sets the {@code errorCallback} function of the sync module. This callback will be called whenever an event occurs @@ -68,6 +69,25 @@ public T batchSize(final int batchSize) { return getThis(); } + /** + * Sets the cache size that indicates the key to id cache size of the sync process. To increase performance + * during the sync some resource keys -> ids are cached which are required for resolving references. To keep the + * cache performant outdated entries are evicted when a certain size is reached. + * + *

Note: This cache size is set to 10.000 by default. + * + * @param cacheSize a long number value that indicates cache size of the key to id cache used for reference + * resolution. Has to be positive or else will be ignored and default value of 100.000 would be + * used. + * @return {@code this} instance of {@link BaseSyncOptionsBuilder} + */ + public T cacheSize(final long cacheSize) { + if (cacheSize > 0) { + this.cacheSize = cacheSize; + } + return getThis(); + } + /** * Sets the beforeUpdateCallback {@link TriFunction} which can be applied on the supplied list of update actions * generated from comparing an old resource of type {@code U} (e.g. {@link io.sphere.sdk.products.Product}) to a new diff --git a/src/main/java/com/commercetools/sync/customers/CustomerSyncOptions.java b/src/main/java/com/commercetools/sync/customers/CustomerSyncOptions.java index 526cce4b8b..2995c1eba3 100644 --- a/src/main/java/com/commercetools/sync/customers/CustomerSyncOptions.java +++ b/src/main/java/com/commercetools/sync/customers/CustomerSyncOptions.java @@ -27,12 +27,12 @@ public final class CustomerSyncOptions extends BaseSyncOptions>, CustomerDraft, Customer, List>> beforeUpdateCallback, - @Nullable final Function beforeCreateCallback) { + @Nullable final Function beforeCreateCallback, final long cacheSize) { super(ctpClient, errorCallback, warningCallback, batchSize, beforeUpdateCallback, - beforeCreateCallback); + beforeCreateCallback, cacheSize); } } diff --git a/src/main/java/com/commercetools/sync/customers/CustomerSyncOptionsBuilder.java b/src/main/java/com/commercetools/sync/customers/CustomerSyncOptionsBuilder.java index a957180f84..23c066fc4b 100644 --- a/src/main/java/com/commercetools/sync/customers/CustomerSyncOptionsBuilder.java +++ b/src/main/java/com/commercetools/sync/customers/CustomerSyncOptionsBuilder.java @@ -42,7 +42,8 @@ public CustomerSyncOptions build() { warningCallback, batchSize, beforeUpdateCallback, - beforeCreateCallback); + beforeCreateCallback, + cacheSize); } /** diff --git a/src/main/java/com/commercetools/sync/customobjects/CustomObjectSyncOptions.java b/src/main/java/com/commercetools/sync/customobjects/CustomObjectSyncOptions.java index 04b754fbab..bd13895fdb 100644 --- a/src/main/java/com/commercetools/sync/customobjects/CustomObjectSyncOptions.java +++ b/src/main/java/com/commercetools/sync/customobjects/CustomObjectSyncOptions.java @@ -31,13 +31,15 @@ public final class CustomObjectSyncOptions extends BaseSyncOptions>>, CustomObjectDraft, CustomObject, List>>> beforeUpdateCallback, - @Nullable final Function, CustomObjectDraft> beforeCreateCallback) { + @Nullable final Function, CustomObjectDraft> beforeCreateCallback, + final long cacheSize) { super( ctpClient, errorCallBack, warningCallBack, batchSize, beforeUpdateCallback, - beforeCreateCallback); + beforeCreateCallback, + cacheSize); } } diff --git a/src/main/java/com/commercetools/sync/customobjects/CustomObjectSyncOptionsBuilder.java b/src/main/java/com/commercetools/sync/customobjects/CustomObjectSyncOptionsBuilder.java index 04aaa4af8d..64c639350b 100644 --- a/src/main/java/com/commercetools/sync/customobjects/CustomObjectSyncOptionsBuilder.java +++ b/src/main/java/com/commercetools/sync/customobjects/CustomObjectSyncOptionsBuilder.java @@ -42,7 +42,8 @@ public CustomObjectSyncOptions build() { warningCallback, batchSize, beforeUpdateCallback, - beforeCreateCallback + beforeCreateCallback, + cacheSize ); } diff --git a/src/main/java/com/commercetools/sync/inventories/InventorySyncOptions.java b/src/main/java/com/commercetools/sync/inventories/InventorySyncOptions.java index 314a801bfb..f2d2a6f1d7 100644 --- a/src/main/java/com/commercetools/sync/inventories/InventorySyncOptions.java +++ b/src/main/java/com/commercetools/sync/inventories/InventorySyncOptions.java @@ -29,13 +29,14 @@ public final class InventorySyncOptions extends BaseSyncOptions>, InventoryEntryDraft, InventoryEntry, List>> beforeUpdateCallback, - @Nullable final Function beforeCreateCallback) { + @Nullable final Function beforeCreateCallback, final long cacheSize) { super(ctpClient, errorCallback, warningCallback, batchSize, beforeUpdateCallback, - beforeCreateCallback); + beforeCreateCallback, + cacheSize); this.ensureChannels = ensureChannels; } diff --git a/src/main/java/com/commercetools/sync/inventories/InventorySyncOptionsBuilder.java b/src/main/java/com/commercetools/sync/inventories/InventorySyncOptionsBuilder.java index c44bcb6e42..17c0c4cd63 100644 --- a/src/main/java/com/commercetools/sync/inventories/InventorySyncOptionsBuilder.java +++ b/src/main/java/com/commercetools/sync/inventories/InventorySyncOptionsBuilder.java @@ -63,7 +63,8 @@ public InventorySyncOptions build() { batchSize, ensureChannels, beforeUpdateCallback, - beforeCreateCallback); + beforeCreateCallback, + cacheSize); } /** diff --git a/src/main/java/com/commercetools/sync/products/ProductSyncOptions.java b/src/main/java/com/commercetools/sync/products/ProductSyncOptions.java index 8b0823c2ce..f97987fc8b 100644 --- a/src/main/java/com/commercetools/sync/products/ProductSyncOptions.java +++ b/src/main/java/com/commercetools/sync/products/ProductSyncOptions.java @@ -31,10 +31,10 @@ public final class ProductSyncOptions extends BaseSyncOptions>, ProductDraft, Product, List>> beforeUpdateCallback, - @Nullable final Function beforeCreateCallback, + @Nullable final Function beforeCreateCallback, final long cacheSize, boolean ensurePriceChannels) { super(ctpClient, errorCallBack, warningCallBack, batchSize, beforeUpdateCallback, - beforeCreateCallback); + beforeCreateCallback, cacheSize); this.syncFilter = ofNullable(syncFilter).orElseGet(SyncFilter::of); this.ensurePriceChannels = ensurePriceChannels; } diff --git a/src/main/java/com/commercetools/sync/products/ProductSyncOptionsBuilder.java b/src/main/java/com/commercetools/sync/products/ProductSyncOptionsBuilder.java index 561e0abc91..6ff1d746de 100644 --- a/src/main/java/com/commercetools/sync/products/ProductSyncOptionsBuilder.java +++ b/src/main/java/com/commercetools/sync/products/ProductSyncOptionsBuilder.java @@ -75,6 +75,7 @@ public ProductSyncOptions build() { syncFilter, beforeUpdateCallback, beforeCreateCallback, + cacheSize, ensurePriceChannels ); } diff --git a/src/main/java/com/commercetools/sync/producttypes/ProductTypeSyncOptions.java b/src/main/java/com/commercetools/sync/producttypes/ProductTypeSyncOptions.java index 24667d8be6..43d3102fcf 100644 --- a/src/main/java/com/commercetools/sync/producttypes/ProductTypeSyncOptions.java +++ b/src/main/java/com/commercetools/sync/producttypes/ProductTypeSyncOptions.java @@ -26,7 +26,7 @@ public final class ProductTypeSyncOptions extends BaseSyncOptions>, ProductTypeDraft, ProductType, List>> beforeUpdateCallback, - @Nullable final Function beforeCreateCallback + @Nullable final Function beforeCreateCallback, final long cacheSize ) { super( @@ -35,7 +35,8 @@ public final class ProductTypeSyncOptions extends BaseSyncOptions, S extends BaseSyncOp Q extends MetaModelQueryDsl, M, E> { final S syncOptions; - protected final Cache keyToIdCache = Caffeine.newBuilder() - .maximumSize(100_000) - .build(); + protected final Cache keyToIdCache; private static final int MAXIMUM_ALLOWED_UPDATE_ACTIONS = 500; static final String CREATE_FAILED = "Failed to create draft with key: '%s'. Reason: %s"; BaseService(@Nonnull final S syncOptions) { this.syncOptions = syncOptions; + this.keyToIdCache = Caffeine.newBuilder() + .maximumSize(syncOptions.getCacheSize()) + .executor(Runnable::run) + .build(); } /** diff --git a/src/main/java/com/commercetools/sync/shoppinglists/ShoppingListSyncOptions.java b/src/main/java/com/commercetools/sync/shoppinglists/ShoppingListSyncOptions.java index 8ca42d8a00..a2974da050 100644 --- a/src/main/java/com/commercetools/sync/shoppinglists/ShoppingListSyncOptions.java +++ b/src/main/java/com/commercetools/sync/shoppinglists/ShoppingListSyncOptions.java @@ -27,7 +27,8 @@ public final class ShoppingListSyncOptions extends BaseSyncOptions>, ShoppingListDraft, ShoppingList, List>> beforeUpdateCallback, - @Nullable final Function beforeCreateCallback + @Nullable final Function beforeCreateCallback, + final long cacheSize ) { super( ctpClient, @@ -35,7 +36,8 @@ public final class ShoppingListSyncOptions extends BaseSyncOptions { final int batchSize, @Nullable final TriFunction>, StateDraft, State, List>> beforeUpdateCallback, - @Nullable final Function beforeCreateCallback) { + @Nullable final Function beforeCreateCallback, final long cacheSize) { super( ctpClient, errorCallBack, warningCallBack, batchSize, beforeUpdateCallback, - beforeCreateCallback); + beforeCreateCallback, + cacheSize); } } diff --git a/src/main/java/com/commercetools/sync/states/StateSyncOptionsBuilder.java b/src/main/java/com/commercetools/sync/states/StateSyncOptionsBuilder.java index b4fd508a87..6b740c8b37 100644 --- a/src/main/java/com/commercetools/sync/states/StateSyncOptionsBuilder.java +++ b/src/main/java/com/commercetools/sync/states/StateSyncOptionsBuilder.java @@ -42,7 +42,8 @@ public StateSyncOptions build() { warningCallback, batchSize, beforeUpdateCallback, - beforeCreateCallback + beforeCreateCallback, + cacheSize ); } diff --git a/src/main/java/com/commercetools/sync/taxcategories/TaxCategorySyncOptions.java b/src/main/java/com/commercetools/sync/taxcategories/TaxCategorySyncOptions.java index d0821011d3..24bc25b373 100644 --- a/src/main/java/com/commercetools/sync/taxcategories/TaxCategorySyncOptions.java +++ b/src/main/java/com/commercetools/sync/taxcategories/TaxCategorySyncOptions.java @@ -27,14 +27,15 @@ public final class TaxCategorySyncOptions extends BaseSyncOptions>, TaxCategoryDraft, TaxCategory, List>> beforeUpdateCallback, - @Nullable final Function beforeCreateCallback) { + @Nullable final Function beforeCreateCallback, final long cacheSize) { super( ctpClient, errorCallBack, warningCallBack, batchSize, beforeUpdateCallback, - beforeCreateCallback); + beforeCreateCallback, + cacheSize); } } diff --git a/src/main/java/com/commercetools/sync/taxcategories/TaxCategorySyncOptionsBuilder.java b/src/main/java/com/commercetools/sync/taxcategories/TaxCategorySyncOptionsBuilder.java index d026223d03..07ddca2b65 100644 --- a/src/main/java/com/commercetools/sync/taxcategories/TaxCategorySyncOptionsBuilder.java +++ b/src/main/java/com/commercetools/sync/taxcategories/TaxCategorySyncOptionsBuilder.java @@ -41,7 +41,8 @@ public TaxCategorySyncOptions build() { warningCallback, batchSize, beforeUpdateCallback, - beforeCreateCallback + beforeCreateCallback, + cacheSize ); } diff --git a/src/main/java/com/commercetools/sync/types/TypeSyncOptions.java b/src/main/java/com/commercetools/sync/types/TypeSyncOptions.java index cb8549709a..9324ad62bb 100644 --- a/src/main/java/com/commercetools/sync/types/TypeSyncOptions.java +++ b/src/main/java/com/commercetools/sync/types/TypeSyncOptions.java @@ -27,7 +27,7 @@ public final class TypeSyncOptions extends BaseSyncOptions { final int batchSize, @Nullable final TriFunction>, TypeDraft, Type, List>> beforeUpdateCallback, - @Nullable final Function beforeCreateCallback + @Nullable final Function beforeCreateCallback, final long cacheSize ) { super( ctpClient, @@ -35,7 +35,8 @@ public final class TypeSyncOptions extends BaseSyncOptions { warningCallback, batchSize, beforeUpdateCallback, - beforeCreateCallback + beforeCreateCallback, + cacheSize ); } diff --git a/src/main/java/com/commercetools/sync/types/TypeSyncOptionsBuilder.java b/src/main/java/com/commercetools/sync/types/TypeSyncOptionsBuilder.java index a9a8963ff4..3d40a88cd3 100644 --- a/src/main/java/com/commercetools/sync/types/TypeSyncOptionsBuilder.java +++ b/src/main/java/com/commercetools/sync/types/TypeSyncOptionsBuilder.java @@ -40,7 +40,8 @@ public TypeSyncOptions build() { warningCallback, batchSize, beforeUpdateCallback, - beforeCreateCallback + beforeCreateCallback, + cacheSize ); } diff --git a/src/test/java/com/commercetools/sync/cartdiscounts/CartDiscountSyncOptionsBuilderTest.java b/src/test/java/com/commercetools/sync/cartdiscounts/CartDiscountSyncOptionsBuilderTest.java index 6f24ff3f50..26d39a46da 100644 --- a/src/test/java/com/commercetools/sync/cartdiscounts/CartDiscountSyncOptionsBuilderTest.java +++ b/src/test/java/com/commercetools/sync/cartdiscounts/CartDiscountSyncOptionsBuilderTest.java @@ -48,6 +48,7 @@ void build_WithClient_ShouldBuildSyncOptions() { assertThat(cartDiscountSyncOptions.getWarningCallback()).isNull(); assertThat(cartDiscountSyncOptions.getCtpClient()).isEqualTo(CTP_CLIENT); assertThat(cartDiscountSyncOptions.getBatchSize()).isEqualTo(CartDiscountSyncOptionsBuilder.BATCH_SIZE_DEFAULT); + assertThat(cartDiscountSyncOptions.getCacheSize()).isEqualTo(10_000); } @Test @@ -269,4 +270,29 @@ void applyBeforeCreateCallBack_WithCallbackReturningNull_ShouldReturnEmptyOption assertThat(filteredDraft).isEmpty(); } + + @Test + void cacheSize_WithPositiveValue_ShouldSetCacheSize() { + final CartDiscountSyncOptions cartDiscountSyncOptions = CartDiscountSyncOptionsBuilder.of(CTP_CLIENT) + .cacheSize(10) + .build(); + assertThat(cartDiscountSyncOptions.getCacheSize()).isEqualTo(10); + } + + @Test + void cacheSize_WithZeroOrNegativeValue_ShouldFallBackToDefaultValue() { + final CartDiscountSyncOptions cartDiscountSyncOptionsWithZeroCacheSize = CartDiscountSyncOptionsBuilder + .of(CTP_CLIENT) + .cacheSize(0) + .build(); + + assertThat(cartDiscountSyncOptionsWithZeroCacheSize.getCacheSize()).isEqualTo(10_000); + + final CartDiscountSyncOptions cartDiscountSyncOptionsWithNegativeCacheSize = CartDiscountSyncOptionsBuilder + .of(CTP_CLIENT) + .cacheSize(-100) + .build(); + + assertThat(cartDiscountSyncOptionsWithNegativeCacheSize.getCacheSize()).isEqualTo(10_000); + } } diff --git a/src/test/java/com/commercetools/sync/categories/CategorySyncOptionsBuilderTest.java b/src/test/java/com/commercetools/sync/categories/CategorySyncOptionsBuilderTest.java index 6681a04891..f892a9ae4b 100644 --- a/src/test/java/com/commercetools/sync/categories/CategorySyncOptionsBuilderTest.java +++ b/src/test/java/com/commercetools/sync/categories/CategorySyncOptionsBuilderTest.java @@ -47,6 +47,7 @@ void build_WithClient_ShouldBuildCategorySyncOptions() { assertThat(categorySyncOptions.getWarningCallback()).isNull(); assertThat(categorySyncOptions.getCtpClient()).isEqualTo(CTP_CLIENT); assertThat(categorySyncOptions.getBatchSize()).isEqualTo(CategorySyncOptionsBuilder.BATCH_SIZE_DEFAULT); + assertThat(categorySyncOptions.getCacheSize()).isEqualTo(10_000); } @Test @@ -250,4 +251,27 @@ void applyBeforeCreateCallBack_WithNullReturnCallback_ShouldReturnEmptyList() { assertThat(filteredDraft).isEmpty(); } + + @Test + void cacheSize_WithPositiveValue_ShouldSetCacheSize() { + final CategorySyncOptions categorySyncOptions = CategorySyncOptionsBuilder.of(CTP_CLIENT) + .cacheSize(10) + .build(); + assertThat(categorySyncOptions.getCacheSize()).isEqualTo(10); + } + + @Test + void cacheSize_WithZeroOrNegativeValue_ShouldFallBackToDefaultValue() { + final CategorySyncOptions categorySyncOptionsWithZeroCacheSize = CategorySyncOptionsBuilder.of(CTP_CLIENT) + .cacheSize(0) + .build(); + assertThat(categorySyncOptionsWithZeroCacheSize.getCacheSize()).isEqualTo(10_000); + + final CategorySyncOptions categorySyncOptionsWithNegativeCacheSize = CategorySyncOptionsBuilder + .of(CTP_CLIENT) + .cacheSize(-100) + .build(); + assertThat(categorySyncOptionsWithNegativeCacheSize.getCacheSize()).isEqualTo(10_000); + } + } diff --git a/src/test/java/com/commercetools/sync/customers/CustomerSyncOptionsBuilderTest.java b/src/test/java/com/commercetools/sync/customers/CustomerSyncOptionsBuilderTest.java index 264d95d04e..95a941c9f1 100644 --- a/src/test/java/com/commercetools/sync/customers/CustomerSyncOptionsBuilderTest.java +++ b/src/test/java/com/commercetools/sync/customers/CustomerSyncOptionsBuilderTest.java @@ -46,6 +46,7 @@ void build_WithClient_ShouldBuildSyncOptions() { assertThat(customerSyncOptions.getWarningCallback()).isNull(); assertThat(customerSyncOptions.getCtpClient()).isEqualTo(CTP_CLIENT); assertThat(customerSyncOptions.getBatchSize()).isEqualTo(CustomerSyncOptionsBuilder.BATCH_SIZE_DEFAULT); + assertThat(customerSyncOptions.getCacheSize()).isEqualTo(10_000); } @Test @@ -259,4 +260,28 @@ void applyBeforeCreateCallBack_WithCallbackReturningNull_ShouldReturnEmptyOption assertThat(filteredDraft).isEmpty(); } + @Test + void cacheSize_WithPositiveValue_ShouldSetCacheSize() { + CustomerSyncOptions customerSyncOptions = CustomerSyncOptionsBuilder + .of(CTP_CLIENT) + .cacheSize(10) + .build(); + assertThat(customerSyncOptions.getCacheSize()).isEqualTo(10); + } + + @Test + void cacheSize_WithZeroOrNegativeValue_ShouldFallBackToDefaultValue() { + final CustomerSyncOptions customerSyncOptionsWithZeroCacheSize = CustomerSyncOptionsBuilder + .of(CTP_CLIENT) + .cacheSize(0) + .build(); + assertThat(customerSyncOptionsWithZeroCacheSize.getCacheSize()).isEqualTo(10_000); + + final CustomerSyncOptions customerSyncOptionsWithNegativeCacheSize = CustomerSyncOptionsBuilder + .of(CTP_CLIENT) + .cacheSize(-100) + .build(); + assertThat(customerSyncOptionsWithNegativeCacheSize.getCacheSize()).isEqualTo(10_000); + } + } diff --git a/src/test/java/com/commercetools/sync/customobjects/CustomObjectSyncOptionsBuilderTest.java b/src/test/java/com/commercetools/sync/customobjects/CustomObjectSyncOptionsBuilderTest.java index 915e9cad65..9058f8de6a 100644 --- a/src/test/java/com/commercetools/sync/customobjects/CustomObjectSyncOptionsBuilderTest.java +++ b/src/test/java/com/commercetools/sync/customobjects/CustomObjectSyncOptionsBuilderTest.java @@ -44,6 +44,7 @@ void build_WithClient_ShouldBuildSyncOptions() { assertThat(customObjectSyncOptions.getWarningCallback()).isNull(); assertThat(customObjectSyncOptions.getCtpClient()).isEqualTo(CTP_CLIENT); assertThat(customObjectSyncOptions.getBatchSize()).isEqualTo(CustomObjectSyncOptionsBuilder.BATCH_SIZE_DEFAULT); + assertThat(customObjectSyncOptions.getCacheSize()).isEqualTo(10_000); } @Test @@ -186,4 +187,27 @@ void applyBeforeCreateCallBack_WithCallbackReturningNull_ShouldReturnEmptyOption assertThat(filteredDraft).isEmpty(); } + @Test + void cacheSize_WithPositiveValue_ShouldSetCacheSize() { + final CustomObjectSyncOptions customObjectSyncOptions = CustomObjectSyncOptionsBuilder.of(CTP_CLIENT) + .cacheSize(10) + .build(); + assertThat(customObjectSyncOptions.getCacheSize()).isEqualTo(10); + } + + @Test + void cacheSize_WithZeroOrNegativeValue_ShouldFallBackToDefaultValue() { + final CustomObjectSyncOptions customObjectSyncOptionsWithZeroCacheSize = + CustomObjectSyncOptionsBuilder.of(CTP_CLIENT) + .cacheSize(0) + .build(); + assertThat(customObjectSyncOptionsWithZeroCacheSize.getCacheSize()).isEqualTo(10_000); + + final CustomObjectSyncOptions customObjectSyncOptionsWithNegativeCacheSize = CustomObjectSyncOptionsBuilder + .of(CTP_CLIENT) + .cacheSize(-100) + .build(); + assertThat(customObjectSyncOptionsWithNegativeCacheSize.getCacheSize()).isEqualTo(10_000); + } + } diff --git a/src/test/java/com/commercetools/sync/products/ProductSyncOptionsBuilderTest.java b/src/test/java/com/commercetools/sync/products/ProductSyncOptionsBuilderTest.java index 285dcf3d90..a6852574a8 100644 --- a/src/test/java/com/commercetools/sync/products/ProductSyncOptionsBuilderTest.java +++ b/src/test/java/com/commercetools/sync/products/ProductSyncOptionsBuilderTest.java @@ -50,6 +50,7 @@ void build_WithClient_ShouldBuildProductSyncOptions() { assertThat(productSyncOptions.getWarningCallback()).isNull(); assertThat(productSyncOptions.getCtpClient()).isEqualTo(CTP_CLIENT); assertThat(productSyncOptions.getBatchSize()).isEqualTo(ProductSyncOptionsBuilder.BATCH_SIZE_DEFAULT); + assertThat(productSyncOptions.getCacheSize()).isEqualTo(10_000); } @Test @@ -264,4 +265,26 @@ void applyBeforeCreateCallBack_WithNullReturnCallback_ShouldReturnEmptyOptional( assertThat(filteredDraft).isEmpty(); } + + @Test + void cacheSize_WithPositiveValue_ShouldSetCacheSize() { + final ProductSyncOptions productSyncOptions = ProductSyncOptionsBuilder.of(CTP_CLIENT) + .cacheSize(10) + .build(); + assertThat(productSyncOptions.getCacheSize()).isEqualTo(10); + } + + @Test + void cacheSize_WithZeroOrNegativeValue_ShouldFallBackToDefaultValue() { + final ProductSyncOptions productSyncOptionsWithZeroCacheSize = ProductSyncOptionsBuilder.of(CTP_CLIENT) + .cacheSize(0) + .build(); + assertThat(productSyncOptionsWithZeroCacheSize.getCacheSize()).isEqualTo(10_000); + + final ProductSyncOptions productSyncOptionsWithNegativeCacheSize = ProductSyncOptionsBuilder + .of(CTP_CLIENT) + .cacheSize(-100) + .build(); + assertThat(productSyncOptionsWithNegativeCacheSize.getCacheSize()).isEqualTo(10_000); + } } diff --git a/src/test/java/com/commercetools/sync/producttypes/ProductTypeSyncOptionsBuilderTest.java b/src/test/java/com/commercetools/sync/producttypes/ProductTypeSyncOptionsBuilderTest.java index 63d89c6008..99bf2f95f5 100644 --- a/src/test/java/com/commercetools/sync/producttypes/ProductTypeSyncOptionsBuilderTest.java +++ b/src/test/java/com/commercetools/sync/producttypes/ProductTypeSyncOptionsBuilderTest.java @@ -46,6 +46,7 @@ void build_WithClient_ShouldBuildProductSyncOptions() { assertThat(productTypeSyncOptions.getWarningCallback()).isNull(); assertThat(productTypeSyncOptions.getCtpClient()).isEqualTo(CTP_CLIENT); assertThat(productTypeSyncOptions.getBatchSize()).isEqualTo(ProductTypeSyncOptionsBuilder.BATCH_SIZE_DEFAULT); + assertThat(productTypeSyncOptions.getCacheSize()).isEqualTo(10_000); } @Test @@ -259,6 +260,30 @@ void applyBeforeCreateCallBack_WithCallbackReturningNull_ShouldReturnEmptyOption assertThat(filteredDraft).isEmpty(); } + @Test + void cacheSize_WithPositiveValue_ShouldSetCacheSize() { + final ProductTypeSyncOptions productTypeSyncOptions = ProductTypeSyncOptionsBuilder.of(CTP_CLIENT) + .cacheSize(10) + .build(); + assertThat(productTypeSyncOptions.getCacheSize()).isEqualTo(10); + } + + @Test + void cacheSize_WithZeroOrNegativeValue_ShouldFallBackToDefaultValue() { + final ProductTypeSyncOptions productTypeSyncOptionsWithZeroCacheSize = + ProductTypeSyncOptionsBuilder.of(CTP_CLIENT) + .cacheSize(0) + .build(); + + assertThat(productTypeSyncOptionsWithZeroCacheSize.getCacheSize()).isEqualTo(10_000); + + final ProductTypeSyncOptions productTypeSyncOptionsWithNegativeCacheSize = ProductTypeSyncOptionsBuilder + .of(CTP_CLIENT) + .cacheSize(-100) + .build(); + + assertThat(productTypeSyncOptionsWithNegativeCacheSize.getCacheSize()).isEqualTo(10_000); + } } diff --git a/src/test/java/com/commercetools/sync/services/impl/BaseServiceImplTest.java b/src/test/java/com/commercetools/sync/services/impl/BaseServiceImplTest.java index 31a9cb8fbe..918f5fd51c 100644 --- a/src/test/java/com/commercetools/sync/services/impl/BaseServiceImplTest.java +++ b/src/test/java/com/commercetools/sync/services/impl/BaseServiceImplTest.java @@ -2,8 +2,8 @@ import com.commercetools.sync.commons.exceptions.SyncException; import com.commercetools.sync.commons.helpers.ResourceKeyIdGraphQlRequest; -import com.commercetools.sync.commons.models.ResourceKeyIdGraphQlResult; import com.commercetools.sync.commons.models.ResourceKeyId; +import com.commercetools.sync.commons.models.ResourceKeyIdGraphQlResult; import com.commercetools.sync.commons.utils.TriConsumer; import com.commercetools.sync.customobjects.CustomObjectSyncOptions; import com.commercetools.sync.customobjects.CustomObjectSyncOptionsBuilder; @@ -35,6 +35,9 @@ import java.util.Optional; import java.util.Set; import java.util.concurrent.CompletionStage; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; import static java.util.Collections.emptySet; import static java.util.Collections.singleton; @@ -65,6 +68,7 @@ void setup() { .of(client) .warningCallback(warningCallback) .batchSize(20) + .cacheSize(2) .build(); service = new ProductServiceImpl(syncOptions); } @@ -292,6 +296,37 @@ void cacheKeysToIdsUsingGraphQl_WithAllCachedKeys_ShouldMakeNoRequestAndReturnCa verify(client, times(1)).execute(any(ProductQuery.class)); } + @Test + void cacheKeysToIds_WithCachedKeysExceedingCacheSize_ShouldNotReturnLeastUsedKeys() { + //preparation + final PagedQueryResult pagedQueryResult = mock(PagedQueryResult.class); + final Product product1 = mock(Product.class); + when(product1.getKey()).thenReturn("key-1"); + when(product1.getId()).thenReturn("id-1"); + final Product product2 = mock(Product.class); + when(product2.getKey()).thenReturn("key-2"); + when(product2.getId()).thenReturn("id-2"); + when(pagedQueryResult.getResults()).thenReturn(Arrays.asList(product1, product2)); + final ResourceKeyIdGraphQlResult resourceKeyIdGraphQlResult = mock(ResourceKeyIdGraphQlResult.class); + final ResourceKeyId resourceKeyId = mock(ResourceKeyId.class); + when(resourceKeyId.getKey()).thenReturn("testKey"); + when(resourceKeyId.getId()).thenReturn("testId"); + when(resourceKeyIdGraphQlResult.getResults()).thenReturn(singleton(resourceKeyId)); + when(client.execute(any())) + .thenReturn(completedFuture(pagedQueryResult)) + .thenReturn(completedFuture(resourceKeyIdGraphQlResult)); + service.fetchMatchingProductsByKeys(Arrays.asList("key-1", "key-2").stream().collect(Collectors.toSet())); + service.getIdFromCacheOrFetch("key-1"); //access the first added cache entry + + //test + final Map optional = service.cacheKeysToIds(singleton("testKey")).toCompletableFuture().join(); + + //assertions + assertThat(optional).containsExactly(MapEntry.entry("key-1", "id-1"), MapEntry.entry("testKey", "testId")); + verify(client, times(1)).execute(any(ProductQuery.class)); + verify(client, times(1)).execute(any(ResourceKeyIdGraphQlRequest.class)); + } + @Test void cacheKeysToIdsUsingGraphQl_WithNoCachedKeys_ShouldMakeRequestAndReturnCachedEntry() { //preparation @@ -329,7 +364,9 @@ void cacheKeysToIdsUsingGraphQl_WithBadGateWayException_ShouldCompleteExceptiona final CompletionStage> result = service.cacheKeysToIds(singleton("testKey")); //assertions - assertThat(result).hasFailedWithThrowableThat().isExactlyInstanceOf(BadGatewayException.class); + assertThat(result).failsWithin(1, TimeUnit.SECONDS) + .withThrowableOfType(ExecutionException.class) + .withCauseExactlyInstanceOf(BadGatewayException.class); verify(client, times(1)).execute(any(ResourceKeyIdGraphQlRequest.class)); } diff --git a/src/test/java/com/commercetools/sync/shoppinglists/ShoppingListSyncOptionsBuilderTest.java b/src/test/java/com/commercetools/sync/shoppinglists/ShoppingListSyncOptionsBuilderTest.java index 1e385bc4a4..3807d2f51f 100644 --- a/src/test/java/com/commercetools/sync/shoppinglists/ShoppingListSyncOptionsBuilderTest.java +++ b/src/test/java/com/commercetools/sync/shoppinglists/ShoppingListSyncOptionsBuilderTest.java @@ -282,4 +282,32 @@ void applyBeforeCreateCallBack_WithCallbackReturningNull_ShouldReturnEmptyOption assertThat(filteredDraft).isEmpty(); } + + @Test + void cacheSize_WithPositiveValue_ShouldSetCacheSize() { + final ShoppingListSyncOptions shoppingListSyncOptions = + ShoppingListSyncOptionsBuilder + .of(CTP_CLIENT) + .cacheSize(10) + .build(); + + assertThat(shoppingListSyncOptions.getCacheSize()).isEqualTo(10); + } + + @Test + void cacheSize_WithZeroOrNegativeValue_ShouldFallBackToDefaultValue() { + final ShoppingListSyncOptions shoppingListSyncOptionsWithZeroCacheSize = + ShoppingListSyncOptionsBuilder.of(CTP_CLIENT) + .cacheSize(0) + .build(); + + assertThat(shoppingListSyncOptionsWithZeroCacheSize.getCacheSize()).isEqualTo(10_000); + + final ShoppingListSyncOptions shoppingListSyncOptionsWithNegativeCacheSize = + ShoppingListSyncOptionsBuilder.of(CTP_CLIENT) + .cacheSize(-100) + .build(); + + assertThat(shoppingListSyncOptionsWithNegativeCacheSize.getCacheSize()).isEqualTo(10_000); + } } diff --git a/src/test/java/com/commercetools/sync/states/StateSyncOptionsBuilderTest.java b/src/test/java/com/commercetools/sync/states/StateSyncOptionsBuilderTest.java index 818d5a08ec..c3f83152d0 100644 --- a/src/test/java/com/commercetools/sync/states/StateSyncOptionsBuilderTest.java +++ b/src/test/java/com/commercetools/sync/states/StateSyncOptionsBuilderTest.java @@ -49,7 +49,8 @@ void build_WithClient_ShouldBuildSyncOptions() { () -> assertThat(stateSyncOptions.getErrorCallback()).isNull(), () -> assertThat(stateSyncOptions.getWarningCallback()).isNull(), () -> assertThat(stateSyncOptions.getCtpClient()).isEqualTo(CTP_CLIENT), - () -> assertThat(stateSyncOptions.getBatchSize()).isEqualTo(StateSyncOptionsBuilder.BATCH_SIZE_DEFAULT) + () -> assertThat(stateSyncOptions.getBatchSize()).isEqualTo(StateSyncOptionsBuilder.BATCH_SIZE_DEFAULT), + () -> assertThat(stateSyncOptions.getCacheSize()).isEqualTo(10_000) ); } @@ -95,7 +96,6 @@ void build_WithWarningCallback_ShouldSetWarningCallback() { assertThat(stateSyncOptions.getWarningCallback()).isNotNull(); } - @Test void build_WithBatchSize_ShouldSetBatchSize() { StateSyncOptions stateSyncOptions = StateSyncOptionsBuilder.of(CTP_CLIENT) @@ -123,4 +123,29 @@ void build_WithInvalidBatchSize_ShouldBuildSyncOptions() { .isEqualTo(StateSyncOptionsBuilder.BATCH_SIZE_DEFAULT); } + @Test + void build_WithCacheSize_ShouldSetCacheSize() { + StateSyncOptions stateSyncOptions = StateSyncOptionsBuilder.of(CTP_CLIENT) + .cacheSize(10) + .build(); + + assertThat(stateSyncOptions.getCacheSize()).isEqualTo(10); + } + + @Test + void build_WithZeroOrNegativeCacheSize_ShouldBuildSyncOptions() { + StateSyncOptions stateSyncOptionsWithZeroCacheSize = StateSyncOptionsBuilder.of(CTP_CLIENT) + .cacheSize(0) + .build(); + + assertThat(stateSyncOptionsWithZeroCacheSize.getCacheSize()).isEqualTo(10_000); + + StateSyncOptions stateSyncOptionsWithNegativeCacheSize = StateSyncOptionsBuilder + .of(CTP_CLIENT) + .cacheSize(-100) + .build(); + + assertThat(stateSyncOptionsWithNegativeCacheSize.getCacheSize()).isEqualTo(10_000); + } + } diff --git a/src/test/java/com/commercetools/sync/taxcategories/TaxCategorySyncOptionsBuilderTest.java b/src/test/java/com/commercetools/sync/taxcategories/TaxCategorySyncOptionsBuilderTest.java index fd49b1c8e5..55b453e1d0 100644 --- a/src/test/java/com/commercetools/sync/taxcategories/TaxCategorySyncOptionsBuilderTest.java +++ b/src/test/java/com/commercetools/sync/taxcategories/TaxCategorySyncOptionsBuilderTest.java @@ -51,7 +51,8 @@ void build_WithClient_ShouldBuildSyncOptions() { () -> assertThat(taxCategorySyncOptions.getWarningCallback()).isNull(), () -> assertThat(taxCategorySyncOptions.getCtpClient()).isEqualTo(CTP_CLIENT), () -> assertThat(taxCategorySyncOptions.getBatchSize()) - .isEqualTo(TaxCategorySyncOptionsBuilder.BATCH_SIZE_DEFAULT) + .isEqualTo(TaxCategorySyncOptionsBuilder.BATCH_SIZE_DEFAULT), + () -> assertThat(taxCategorySyncOptions.getCacheSize()).isEqualTo(10_000) ); } @@ -128,4 +129,32 @@ void build_WithInvalidBatchSize_ShouldBuildSyncOptions() { .isEqualTo(TaxCategorySyncOptionsBuilder.BATCH_SIZE_DEFAULT); } + + @Test + void build_WithCacheSize_ShouldSetCacheSize() { + final TaxCategorySyncOptions taxCategorySyncOptions = TaxCategorySyncOptionsBuilder.of(CTP_CLIENT) + .cacheSize(10) + .build(); + + assertThat(taxCategorySyncOptions.getCacheSize()).isEqualTo(10); + } + + + @Test + void build_WithZeroOrNegativeCacheSize_ShouldBuildSyncOptions() { + final TaxCategorySyncOptions taxCategorySyncOptionsWithZeroCacheSize = TaxCategorySyncOptionsBuilder + .of(CTP_CLIENT) + .cacheSize(0) + .build(); + + assertThat(taxCategorySyncOptionsWithZeroCacheSize.getCacheSize()).isEqualTo(10_000); + + final TaxCategorySyncOptions taxCategorySyncOptionsWithNegativeCacheSize = TaxCategorySyncOptionsBuilder + .of(CTP_CLIENT) + .cacheSize(-100) + .build(); + + assertThat(taxCategorySyncOptionsWithNegativeCacheSize.getCacheSize()).isEqualTo(10_000); + } + } diff --git a/src/test/java/com/commercetools/sync/types/TypeSyncOptionsBuilderTest.java b/src/test/java/com/commercetools/sync/types/TypeSyncOptionsBuilderTest.java index afdaa47985..c502ee0f58 100644 --- a/src/test/java/com/commercetools/sync/types/TypeSyncOptionsBuilderTest.java +++ b/src/test/java/com/commercetools/sync/types/TypeSyncOptionsBuilderTest.java @@ -47,6 +47,7 @@ void build_WithClient_ShouldBuildSyncOptions() { assertThat(typeSyncOptions.getWarningCallback()).isNull(); assertThat(typeSyncOptions.getCtpClient()).isEqualTo(CTP_CLIENT); assertThat(typeSyncOptions.getBatchSize()).isEqualTo(TypeSyncOptionsBuilder.BATCH_SIZE_DEFAULT); + assertThat(typeSyncOptions.getCacheSize()).isEqualTo(10_000); } @Test @@ -251,4 +252,27 @@ void applyBeforeCreateCallBack_WithCallbackReturningNull_ShouldReturnEmptyOption assertThat(filteredDraft).isEmpty(); } + @Test + void cacheSize_WithPositiveValue_ShouldSetCacheSize() { + final TypeSyncOptions typeSyncOptions = TypeSyncOptionsBuilder.of(CTP_CLIENT) + .cacheSize(10) + .build(); + assertThat(typeSyncOptions.getCacheSize()).isEqualTo(10); + } + + @Test + void cacheSize_WithZeroOrNegativeValue_ShouldFallBackToDefaultValue() { + final TypeSyncOptions typeSyncOptionsWithZeroCacheSize = TypeSyncOptionsBuilder.of(CTP_CLIENT) + .cacheSize(0) + .build(); + assertThat(typeSyncOptionsWithZeroCacheSize.getCacheSize()).isEqualTo(10_000); + + final TypeSyncOptions typeSyncOptionsWithNegativeCacheSize = TypeSyncOptionsBuilder + .of(CTP_CLIENT) + .cacheSize(-100) + .build(); + assertThat(typeSyncOptionsWithNegativeCacheSize.getCacheSize()).isEqualTo(10_000); + } + + } From 93465f5a38444b5d7d4de39f244df4e574f16f3e Mon Sep 17 00:00:00 2001 From: aoz Date: Tue, 24 Nov 2020 11:41:11 +0100 Subject: [PATCH 6/6] bump versions to 3.0.1 --- README.md | 16 ++++++++-------- docs/README.md | 12 ++++++------ docs/RELEASE_NOTES.md | 8 +++++--- docs/usage/CART_DISCOUNT_SYNC.md | 4 ++-- docs/usage/CATEGORY_SYNC.md | 4 ++-- docs/usage/CUSTOMER_SYNC.md | 4 ++-- docs/usage/CUSTOM_OBJECT_SYNC.md | 2 +- docs/usage/IMPORTANT_USAGE_TIPS.md | 2 +- docs/usage/INVENTORY_SYNC.md | 4 ++-- docs/usage/PRODUCT_SYNC.md | 4 ++-- docs/usage/PRODUCT_TYPE_SYNC.md | 4 ++-- docs/usage/QUICK_START.md | 16 ++++++++-------- docs/usage/SHOPPING_LIST_SYNC.md | 2 +- docs/usage/STATE_SYNC.md | 4 ++-- docs/usage/TAX_CATEGORY_SYNC.md | 2 +- docs/usage/TYPE_SYNC.md | 2 +- mkdocs.yml | 2 +- .../utils/CustomerUpdateActionUtils.java | 1 - 18 files changed, 47 insertions(+), 46 deletions(-) diff --git a/README.md b/README.md index c29576fe24..c4b53b7a32 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,9 @@ # commercetools sync [![Build Status](https://travis-ci.org/commercetools/commercetools-sync-java.svg?branch=master)](https://travis-ci.org/commercetools/commercetools-sync-java) [![codecov](https://codecov.io/gh/commercetools/commercetools-sync-java/branch/master/graph/badge.svg)](https://codecov.io/gh/commercetools/commercetools-sync-java) -[![Benchmarks 3.0.0](https://img.shields.io/badge/Benchmarks-3.0.0-orange.svg)](https://commercetools.github.io/commercetools-sync-java/benchmarks/) +[![Benchmarks 3.0.1](https://img.shields.io/badge/Benchmarks-3.0.1-orange.svg)](https://commercetools.github.io/commercetools-sync-java/benchmarks/) [![Download](https://api.bintray.com/packages/commercetools/maven/commercetools-sync-java/images/download.svg) ](https://bintray.com/commercetools/maven/commercetools-sync-java/_latestVersion) -[![Javadoc](http://javadoc-badge.appspot.com/com.commercetools/commercetools-sync-java.svg?label=Javadoc)](https://commercetools.github.io/commercetools-sync-java/v/3.0.0/) +[![Javadoc](http://javadoc-badge.appspot.com/com.commercetools/commercetools-sync-java.svg?label=Javadoc)](https://commercetools.github.io/commercetools-sync-java/v/3.0.1/) [![Known Vulnerabilities](https://snyk.io/test/github/commercetools/commercetools-sync-java/4b2e26113d591bda158217c5dc1cf80a88665646/badge.svg)](https://snyk.io/test/github/commercetools/commercetools-sync-java/4b2e26113d591bda158217c5dc1cf80a88665646) More at https://commercetools.github.io/commercetools-sync-java @@ -40,7 +40,7 @@ The library supports synchronising the following entities in commercetools - [Ivy](#ivy) - [Roadmap](#roadmap) - [Release Notes](/docs/RELEASE_NOTES.md) -- [Javadoc](https://commercetools.github.io/commercetools-sync-java/v/3.0.0/) +- [Javadoc](https://commercetools.github.io/commercetools-sync-java/v/3.0.1/) - [Benchmarks](https://commercetools.github.io/commercetools-sync-java/benchmarks/) @@ -67,7 +67,7 @@ commercetools sync is a Java library that imports commercetools platform data in - Make sure you have `JDK 8` installed. - [commercetools-jvm-sdk](https://github.com/commercetools/commercetools-jvm-sdk) as a dependency in your JVM-based - application. (Make sure to use a version `>= 1.54.0`). + application. (Make sure to use a version `>= 1.55.0`). - a target commercetools project for syncing your source data to. @@ -82,24 +82,24 @@ Here are the most popular ones: com.commercetools commercetools-sync-java - 3.0.0 + 3.0.1 ```` #### Gradle ````groovy -implementation 'com.commercetools:commercetools-sync-java:3.0.0' +implementation 'com.commercetools:commercetools-sync-java:3.0.1' ```` #### SBT ```` -libraryDependencies += "com.commercetools" % "commercetools-sync-java" % "3.0.0" +libraryDependencies += "com.commercetools" % "commercetools-sync-java" % "3.0.1" ```` #### Ivy ````xml - + ```` diff --git a/docs/README.md b/docs/README.md index 4fd8b34105..01c07b47f6 100644 --- a/docs/README.md +++ b/docs/README.md @@ -2,9 +2,9 @@ # commercetools sync [![Build Status](https://travis-ci.org/commercetools/commercetools-sync-java.svg?branch=master)](https://travis-ci.org/commercetools/commercetools-sync-java) [![codecov](https://codecov.io/gh/commercetools/commercetools-sync-java/branch/master/graph/badge.svg)](https://codecov.io/gh/commercetools/commercetools-sync-java) -[![Benchmarks 3.0.0](https://img.shields.io/badge/Benchmarks-3.0.0-orange.svg)](https://commercetools.github.io/commercetools-sync-java/benchmarks/) +[![Benchmarks 3.0.1](https://img.shields.io/badge/Benchmarks-3.0.1-orange.svg)](https://commercetools.github.io/commercetools-sync-java/benchmarks/) [![Download](https://api.bintray.com/packages/commercetools/maven/commercetools-sync-java/images/download.svg) ](https://bintray.com/commercetools/maven/commercetools-sync-java/_latestVersion) -[![Javadoc](http://javadoc-badge.appspot.com/com.commercetools/commercetools-sync-java.svg?label=Javadoc)](https://commercetools.github.io/commercetools-sync-java/v/3.0.0/) +[![Javadoc](http://javadoc-badge.appspot.com/com.commercetools/commercetools-sync-java.svg?label=Javadoc)](https://commercetools.github.io/commercetools-sync-java/v/3.0.1/) [![Known Vulnerabilities](https://snyk.io/test/github/commercetools/commercetools-sync-java/4b2e26113d591bda158217c5dc1cf80a88665646/badge.svg)](https://snyk.io/test/github/commercetools/commercetools-sync-java/4b2e26113d591bda158217c5dc1cf80a88665646) @@ -58,18 +58,18 @@ Here are the most popular ones: com.commercetools commercetools-sync-java - 3.0.0 + 3.0.1 ```` #### Gradle ````groovy -implementation 'com.commercetools:commercetools-sync-java:3.0.0' +implementation 'com.commercetools:commercetools-sync-java:3.0.1' ```` #### SBT ```` -libraryDependencies += "com.commercetools" % "commercetools-sync-java" % "3.0.0" +libraryDependencies += "com.commercetools" % "commercetools-sync-java" % "3.0.1" ```` #### Ivy ````xml - + ```` diff --git a/docs/RELEASE_NOTES.md b/docs/RELEASE_NOTES.md index c9cdebe271..1e136668f8 100644 --- a/docs/RELEASE_NOTES.md +++ b/docs/RELEASE_NOTES.md @@ -27,8 +27,8 @@ 7. Add Migration guide section which specifies explicitly if there are breaking changes and how to tackle them. --> - +- 🛠️ **Dependency Updates** (1) + - `commercetools-jvm-sdk` `1.54.0` -> [`1.55.0`](http://commercetools.github.io/commercetools-jvm-sdk/apidocs/io/sphere/sdk/meta/ReleaseNotes.html#v1_55_0) + ### 3.0.0 - Nov 18, 2020 [Commits](https://github.com/commercetools/commercetools-sync-java/compare/2.3.0...3.0.0) | [Javadoc](https://commercetools.github.io/commercetools-sync-java/v/3.0.0/) | diff --git a/docs/usage/CART_DISCOUNT_SYNC.md b/docs/usage/CART_DISCOUNT_SYNC.md index 5428e83899..f192c9d28b 100644 --- a/docs/usage/CART_DISCOUNT_SYNC.md +++ b/docs/usage/CART_DISCOUNT_SYNC.md @@ -26,7 +26,7 @@ against a [CartDiscountDraft](https://docs.commercetools.com/http-api-projects-c #### Prerequisites 1. Create a `sphereClient`: -Use the [ClientConfigurationUtils](https://github.com/commercetools/commercetools-sync-java/blob/3.0.0/src/main/java/com/commercetools/sync/commons/utils/ClientConfigurationUtils.java#L45) which apply the best practices for `SphereClient` creation. +Use the [ClientConfigurationUtils](https://github.com/commercetools/commercetools-sync-java/blob/3.0.1/src/main/java/com/commercetools/sync/commons/utils/ClientConfigurationUtils.java#L45) which apply the best practices for `SphereClient` creation. If you have custom requirements for the sphere client creation, have a look into the [Important Usage Tips](IMPORTANT_USAGE_TIPS.md). 2. The sync expects a list of `CartDiscountDraft`s that have their `key` fields set to be matched with @@ -37,7 +37,7 @@ fields set, otherwise they won't be matched. Types are matched by their `key`s. Therefore, in order for the sync to resolve the actual ids of the type reference, the `key` of the `Type` has to be supplied. - - When syncing from a source commercetools project, you can use [`mapToCartDiscountDrafts`](https://commercetools.github.io/commercetools-sync-java/v/3.0.0/com/commercetools/sync/cartdiscounts/utils/CartDiscountReferenceResolutionUtils.html#mapToCartDiscountDrafts-java.util.List-) + - When syncing from a source commercetools project, you can use [`mapToCartDiscountDrafts`](https://commercetools.github.io/commercetools-sync-java/v/3.0.1/com/commercetools/sync/cartdiscounts/utils/CartDiscountReferenceResolutionUtils.html#mapToCartDiscountDrafts-java.util.List-) method that maps from a `CartDiscount` to `CartDiscountDraft` in order to make them ready for reference resolution by the sync: ````java diff --git a/docs/usage/CATEGORY_SYNC.md b/docs/usage/CATEGORY_SYNC.md index 28503b62cd..5e5b45de2e 100644 --- a/docs/usage/CATEGORY_SYNC.md +++ b/docs/usage/CATEGORY_SYNC.md @@ -27,7 +27,7 @@ against a [CategoryDraft](https://docs.commercetools.com/http-api-projects-categ #### Prerequisites 1. Create a `sphereClient`: -Use the [ClientConfigurationUtils](https://github.com/commercetools/commercetools-sync-java/blob/3.0.0/src/main/java/com/commercetools/sync/commons/utils/ClientConfigurationUtils.java#L45) which apply the best practices for `SphereClient` creation. +Use the [ClientConfigurationUtils](https://github.com/commercetools/commercetools-sync-java/blob/3.0.1/src/main/java/com/commercetools/sync/commons/utils/ClientConfigurationUtils.java#L45) which apply the best practices for `SphereClient` creation. If you have custom requirements for the sphere client creation, have a look into the [Important Usage Tips](IMPORTANT_USAGE_TIPS.md). 2. The sync expects a list of `CategoryDraft`s that have their `key` fields set to be matched with @@ -38,7 +38,7 @@ otherwise they won't be matched. These references are matched by their `key`s. Therefore, in order for the sync to resolve the actual ids of the references, their `key`s has to be supplied. - - When syncing from a source commercetools project, you can use [`mapToCategoryDrafts`](https://commercetools.github.io/commercetools-sync-java/v/3.0.0/com/commercetools/sync/categories/utils/CategoryReferenceResolutionUtils.html#mapToCategoryDrafts-java.util.List-) + - When syncing from a source commercetools project, you can use [`mapToCategoryDrafts`](https://commercetools.github.io/commercetools-sync-java/v/3.0.1/com/commercetools/sync/categories/utils/CategoryReferenceResolutionUtils.html#mapToCategoryDrafts-java.util.List-) method that maps from a `Category` to `CategoryDraft` in order to make them ready for reference resolution by the sync: ````java final List categoryDrafts = CategoryReferenceResolutionUtils.mapToCategoryDrafts(categories); diff --git a/docs/usage/CUSTOMER_SYNC.md b/docs/usage/CUSTOMER_SYNC.md index 405596042a..ff8ab2cc30 100644 --- a/docs/usage/CUSTOMER_SYNC.md +++ b/docs/usage/CUSTOMER_SYNC.md @@ -26,7 +26,7 @@ against a [CustomerDraft](https://docs.commercetools.com/api/projects/customers# #### Prerequisites 1. Create a `sphereClient`: -Use the [ClientConfigurationUtils](https://github.com/commercetools/commercetools-sync-java/blob/3.0.0/src/main/java/com/commercetools/sync/commons/utils/ClientConfigurationUtils.java#L45) which apply the best practices for `SphereClient` creation. +Use the [ClientConfigurationUtils](https://github.com/commercetools/commercetools-sync-java/blob/3.0.1/src/main/java/com/commercetools/sync/commons/utils/ClientConfigurationUtils.java#L45) which apply the best practices for `SphereClient` creation. If you have custom requirements for the sphere client creation, have a look into the [Important Usage Tips](IMPORTANT_USAGE_TIPS.md). 2. The sync expects a list of `CustomerDraft`s that have their `key` fields set to be matched with customers in the @@ -43,7 +43,7 @@ Any reference that is not expanded will have its id in place and not replaced by resources on the target commercetools project and the library will issue an update/create an API request without reference resolution. - - When syncing from a source commercetools project, you can use [`mapToCustomerDrafts`](https://commercetools.github.io/commercetools-sync-java/v/3.0.0/com/commercetools/sync/customers/utils/CustomerReferenceResolutionUtils.html#mapToCustomerDrafts-java.util.List-) + - When syncing from a source commercetools project, you can use [`mapToCustomerDrafts`](https://commercetools.github.io/commercetools-sync-java/v/3.0.1/com/commercetools/sync/customers/utils/CustomerReferenceResolutionUtils.html#mapToCustomerDrafts-java.util.List-) method that maps from a `Customer` to `CustomerDraft` to make them ready for reference resolution by the sync: ````java diff --git a/docs/usage/CUSTOM_OBJECT_SYNC.md b/docs/usage/CUSTOM_OBJECT_SYNC.md index dd576096a8..3dcf569a5f 100644 --- a/docs/usage/CUSTOM_OBJECT_SYNC.md +++ b/docs/usage/CUSTOM_OBJECT_SYNC.md @@ -24,7 +24,7 @@ against a [CustomObjectDraft](https://docs.commercetools.com/http-api-projects-c #### Prerequisites 1. Create a `sphereClient`: -Use the [ClientConfigurationUtils](https://github.com/commercetools/commercetools-sync-java/blob/3.0.0/src/main/java/com/commercetools/sync/commons/utils/ClientConfigurationUtils.java#L45) which apply the best practices for `SphereClient` creation. +Use the [ClientConfigurationUtils](https://github.com/commercetools/commercetools-sync-java/blob/3.0.1/src/main/java/com/commercetools/sync/commons/utils/ClientConfigurationUtils.java#L45) which apply the best practices for `SphereClient` creation. If you have custom requirements for the sphere client creation, have a look into the [Important Usage Tips](IMPORTANT_USAGE_TIPS.md). 2. The sync expects a list of `CustomObjectDraft`s that have their `key` and `container` fields set to be matched with diff --git a/docs/usage/IMPORTANT_USAGE_TIPS.md b/docs/usage/IMPORTANT_USAGE_TIPS.md index 0ba613aba1..3f70e275f3 100644 --- a/docs/usage/IMPORTANT_USAGE_TIPS.md +++ b/docs/usage/IMPORTANT_USAGE_TIPS.md @@ -31,7 +31,7 @@ productSync.sync(batch1) By design, scaling the sync process should **not** be done by executing the batches themselves in parallel. However, it can be done either by: - Changing the number of [max parallel requests](https://github.com/commercetools/commercetools-sync-java/tree/master/src/main/java/com/commercetools/sync/commons/utils/ClientConfigurationUtils.java#L116) within the `sphereClient` configuration. It defines how many requests the client can execute in parallel. - - or changing the draft [batch size](https://commercetools.github.io/commercetools-sync-java/v/3.0.0/com/commercetools/sync/commons/BaseSyncOptionsBuilder.html#batchSize-int-). It defines how many drafts can one batch contain. + - or changing the draft [batch size](https://commercetools.github.io/commercetools-sync-java/v/3.0.1/com/commercetools/sync/commons/BaseSyncOptionsBuilder.html#batchSize-int-). It defines how many drafts can one batch contain. The current overridable default [configuration](https://github.com/commercetools/commercetools-sync-java/tree/master/src/main/java/com/commercetools/sync/commons/utils/ClientConfigurationUtils.java#L45) of the `sphereClient` is the recommended good balance for stability and performance for the sync process. diff --git a/docs/usage/INVENTORY_SYNC.md b/docs/usage/INVENTORY_SYNC.md index fc5ed74d04..d7deefb855 100644 --- a/docs/usage/INVENTORY_SYNC.md +++ b/docs/usage/INVENTORY_SYNC.md @@ -27,7 +27,7 @@ against a [InventoryEntryDraft](https://docs.commercetools.com/http-api-projects #### Prerequisites 1. Create a `sphereClient`: -Use the [ClientConfigurationUtils](https://github.com/commercetools/commercetools-sync-java/blob/3.0.0/src/main/java/com/commercetools/sync/commons/utils/ClientConfigurationUtils.java#L45) which apply the best practices for `SphereClient` creation. +Use the [ClientConfigurationUtils](https://github.com/commercetools/commercetools-sync-java/blob/3.0.1/src/main/java/com/commercetools/sync/commons/utils/ClientConfigurationUtils.java#L45) which apply the best practices for `SphereClient` creation. If you have custom requirements for the sphere client creation, have a look into the [Important Usage Tips](IMPORTANT_USAGE_TIPS.md). 2. The sync expects a list of `InventoryEntryDraft`s that have their `sku` fields set, @@ -37,7 +37,7 @@ If you have custom requirements for the sphere client creation, have a look into references are matched by their `key`s. Therefore, in order for the sync to resolve the actual ids of those references, their `key`s has to be supplied. - - When syncing from a source commercetools project, you can use [`mapToInventoryEntryDrafts`](https://commercetools.github.io/commercetools-sync-java/v/3.0.0/com/commercetools/sync/inventories/utils/InventoryReferenceResolutionUtils.html#mapToInventoryEntryDrafts-java.util.List-) + - When syncing from a source commercetools project, you can use [`mapToInventoryEntryDrafts`](https://commercetools.github.io/commercetools-sync-java/v/3.0.1/com/commercetools/sync/inventories/utils/InventoryReferenceResolutionUtils.html#mapToInventoryEntryDrafts-java.util.List-) method that that maps from a `InventoryEntry` to `InventoryEntryDraft` in order to make them ready for reference resolution by the sync: ````java final List inventoryEntryDrafts = InventoryReferenceResolutionUtils.mapToInventoryEntryDrafts(inventoryEntries); diff --git a/docs/usage/PRODUCT_SYNC.md b/docs/usage/PRODUCT_SYNC.md index 51eb6d0b7c..c1c92a64cf 100644 --- a/docs/usage/PRODUCT_SYNC.md +++ b/docs/usage/PRODUCT_SYNC.md @@ -27,7 +27,7 @@ against a [ProductDraft](https://docs.commercetools.com/http-api-projects-produc #### Prerequisites 1. Create a `sphereClient`: -Use the [ClientConfigurationUtils](https://github.com/commercetools/commercetools-sync-java/blob/3.0.0/src/main/java/com/commercetools/sync/commons/utils/ClientConfigurationUtils.java#L45) which apply the best practices for `SphereClient` creation. +Use the [ClientConfigurationUtils](https://github.com/commercetools/commercetools-sync-java/blob/3.0.1/src/main/java/com/commercetools/sync/commons/utils/ClientConfigurationUtils.java#L45) which apply the best practices for `SphereClient` creation. If you have custom requirements for the sphere client creation, have a look into the [Important Usage Tips](IMPORTANT_USAGE_TIPS.md). 2. The sync expects a list of `ProductDraft`s that have their `key` fields set to be matched with @@ -41,7 +41,7 @@ all the variants in the target project are expected to have the `sku` fields set of the product also have prices, where each price also has some references including a reference to the `Type` of its custom fields and a reference to a `channel`. All these referenced resources are matched by their `key`s. Therefore, in order for the sync to resolve the actual ids of those references, those `key`s have to be supplied in the following way: - - When syncing from a source commercetools project, you can use [`mapToProductDrafts`](https://commercetools.github.io/commercetools-sync-java/v/3.0.0/com/commercetools/sync/products/utils/ProductReferenceResolutionUtils.html#mapToProductDrafts-java.util.List-) + - When syncing from a source commercetools project, you can use [`mapToProductDrafts`](https://commercetools.github.io/commercetools-sync-java/v/3.0.1/com/commercetools/sync/products/utils/ProductReferenceResolutionUtils.html#mapToProductDrafts-java.util.List-) method that maps from a `Product` to `ProductDraft` in order to make them ready for reference resolution by the sync: ````java final List productDrafts = ProductReferenceResolutionUtils.mapToProductDrafts(products); diff --git a/docs/usage/PRODUCT_TYPE_SYNC.md b/docs/usage/PRODUCT_TYPE_SYNC.md index 8a3e72ff2b..8608560433 100644 --- a/docs/usage/PRODUCT_TYPE_SYNC.md +++ b/docs/usage/PRODUCT_TYPE_SYNC.md @@ -29,7 +29,7 @@ against a [ProductTypeDraft](https://docs.commercetools.com/http-api-projects-pr #### Prerequisites 1. Create a `sphereClient`: -Use the [ClientConfigurationUtils](https://github.com/commercetools/commercetools-sync-java/blob/3.0.0/src/main/java/com/commercetools/sync/commons/utils/ClientConfigurationUtils.java#L45) which apply the best practices for `SphereClient` creation. +Use the [ClientConfigurationUtils](https://github.com/commercetools/commercetools-sync-java/blob/3.0.1/src/main/java/com/commercetools/sync/commons/utils/ClientConfigurationUtils.java#L45) which apply the best practices for `SphereClient` creation. If you have custom requirements for the sphere client creation, have a look into the [Important Usage Tips](IMPORTANT_USAGE_TIPS.md). 2. The sync expects a list of `ProductTypeDraft`s that have their `key` fields set to be matched with @@ -42,7 +42,7 @@ references, those `key`s have to be supplied in the following way: - Provide the `key` value on the `id` field of the reference. This means that calling `getId()` on the reference would return its `key`. - **Note**: When syncing from a source commercetools project, you can use [`mapToProductTypeDrafts`](https://commercetools.github.io/commercetools-sync-java/v/3.0.0 /com/commercetools/sync/producttypes/utils/ProductTypeReferenceResolutionUtils.html#mapToProductTypeDrafts-java.util.List-) + **Note**: When syncing from a source commercetools project, you can use [`mapToProductTypeDrafts`](https://commercetools.github.io/commercetools-sync-java/v/3.0.1 /com/commercetools/sync/producttypes/utils/ProductTypeReferenceResolutionUtils.html#mapToProductTypeDrafts-java.util.List-) that replaces the references id fields with keys, in order to make them ready for reference resolution by the sync: ````java diff --git a/docs/usage/QUICK_START.md b/docs/usage/QUICK_START.md index 90373626d4..1ac23f88cb 100644 --- a/docs/usage/QUICK_START.md +++ b/docs/usage/QUICK_START.md @@ -20,35 +20,35 @@ com.commercetools.sdk.jvm.core commercetools-models - 1.54.0 + 1.55.0 com.commercetools.sdk.jvm.core commercetools-java-client - 1.54.0 + 1.55.0 com.commercetools.sdk.jvm.core commercetools-convenience - 1.54.0 + 1.55.0 com.commercetools commercetools-sync-java - 3.0.0 + 3.0.1 ```` - For Gradle users: ````groovy // Add commercetools-jvm-sdk dependencies. -implementation 'com.commercetools.sdk.jvm.core:commercetools-models:1.54.0' -implementation 'com.commercetools.sdk.jvm.core:commercetools-java-client:1.54.0' -implementation 'com.commercetools.sdk.jvm.core:commercetools-convenience:1.54.0' +implementation 'com.commercetools.sdk.jvm.core:commercetools-models:1.55.0' +implementation 'com.commercetools.sdk.jvm.core:commercetools-java-client:1.55.0' +implementation 'com.commercetools.sdk.jvm.core:commercetools-convenience:1.55.0' // Add commercetools-sync-java dependency. -implementation 'com.commercetools:commercetools-sync-java:3.0.0' +implementation 'com.commercetools:commercetools-sync-java:3.0.1' ```` ### 2. Setup Syncing Options diff --git a/docs/usage/SHOPPING_LIST_SYNC.md b/docs/usage/SHOPPING_LIST_SYNC.md index 2fc0212df6..88689714b7 100644 --- a/docs/usage/SHOPPING_LIST_SYNC.md +++ b/docs/usage/SHOPPING_LIST_SYNC.md @@ -44,7 +44,7 @@ Any reference that is not expanded will have its id in place and not replaced by resources on the target commercetools project and the library will issue an update/create an API request without reference resolution. Therefore, in order for the sync to resolve the actual ids of those references, those `key`s have to be supplied in the following way: - - When syncing from a source commercetools project, you can use [`mapToShoppingListDraft`](https://commercetools.github.io/commercetools-sync-java/v/3.0.0/com/commercetools/sync/shoppinglists/utils/ShoppingListReferenceResolutionUtils.html#mapToShoppingListDrafts-java.util.List-) + - When syncing from a source commercetools project, you can use [`mapToShoppingListDraft`](https://commercetools.github.io/commercetools-sync-java/v/3.0.1/com/commercetools/sync/shoppinglists/utils/ShoppingListReferenceResolutionUtils.html#mapToShoppingListDrafts-java.util.List-) method that maps from a `ShoppingList` to `ShoppingListDraft` to make them ready for reference resolution by the shopping list sync: ````java diff --git a/docs/usage/STATE_SYNC.md b/docs/usage/STATE_SYNC.md index 99a404f60b..3693bccaaf 100644 --- a/docs/usage/STATE_SYNC.md +++ b/docs/usage/STATE_SYNC.md @@ -27,7 +27,7 @@ against a [StateDraft](https://docs.commercetools.com/http-api-projects-states#s #### Prerequisites 1. Create a `sphereClient`: -Use the [ClientConfigurationUtils](https://github.com/commercetools/commercetools-sync-java/blob/3.0.0/src/main/java/com/commercetools/sync/commons/utils/ClientConfigurationUtils.java#L45) which apply the best practices for `SphereClient` creation. +Use the [ClientConfigurationUtils](https://github.com/commercetools/commercetools-sync-java/blob/3.0.1/src/main/java/com/commercetools/sync/commons/utils/ClientConfigurationUtils.java#L45) which apply the best practices for `SphereClient` creation. If you have custom requirements for the sphere client creation, have a look into the [Important Usage Tips](IMPORTANT_USAGE_TIPS.md). 2. The sync expects a list of `StateDraft`s that have their `key` fields set to be matched with @@ -40,7 +40,7 @@ otherwise they won't be matched. transition would return its `key`. **Note**: When syncing from a source commercetools project, you can use this util which this library provides: - [`mapToStateDrafts`](https://commercetools.github.io/commercetools-sync-java/v/3.0.0/com/commercetools/sync/states/utils/StateReferenceResolutionUtils.html#mapToStateDrafts-java.util.List-) + [`mapToStateDrafts`](https://commercetools.github.io/commercetools-sync-java/v/3.0.1/com/commercetools/sync/states/utils/StateReferenceResolutionUtils.html#mapToStateDrafts-java.util.List-) that replaces the references id fields with keys, in order to make them ready for reference resolution by the sync: ````java // Puts the keys in the reference id fields to prepare for reference resolution diff --git a/docs/usage/TAX_CATEGORY_SYNC.md b/docs/usage/TAX_CATEGORY_SYNC.md index 6f3292e5e7..fd42ebf086 100644 --- a/docs/usage/TAX_CATEGORY_SYNC.md +++ b/docs/usage/TAX_CATEGORY_SYNC.md @@ -25,7 +25,7 @@ against a [TaxCategoryDraft](https://docs.commercetools.com/http-api-projects-ta #### Prerequisites 1. Create a `sphereClient`: -Use the [ClientConfigurationUtils](https://github.com/commercetools/commercetools-sync-java/blob/3.0.0/src/main/java/com/commercetools/sync/commons/utils/ClientConfigurationUtils.java#L45) which apply the best practices for `SphereClient` creation. +Use the [ClientConfigurationUtils](https://github.com/commercetools/commercetools-sync-java/blob/3.0.1/src/main/java/com/commercetools/sync/commons/utils/ClientConfigurationUtils.java#L45) which apply the best practices for `SphereClient` creation. If you have custom requirements for the sphere client creation, have a look into the [Important Usage Tips](IMPORTANT_USAGE_TIPS.md). 2. The sync expects a list of `TaxCategoryDraft`s that have their `key` fields set to be matched with diff --git a/docs/usage/TYPE_SYNC.md b/docs/usage/TYPE_SYNC.md index b0e9cfa177..f8a0e44860 100644 --- a/docs/usage/TYPE_SYNC.md +++ b/docs/usage/TYPE_SYNC.md @@ -27,7 +27,7 @@ against a [TypeDraft](https://docs.commercetools.com/http-api-projects-types.htm #### Prerequisites 1. Create a `sphereClient`: -Use the [ClientConfigurationUtils](https://github.com/commercetools/commercetools-sync-java/blob/3.0.0/src/main/java/com/commercetools/sync/commons/utils/ClientConfigurationUtils.java#L45) which apply the best practices for `SphereClient` creation. +Use the [ClientConfigurationUtils](https://github.com/commercetools/commercetools-sync-java/blob/3.0.1/src/main/java/com/commercetools/sync/commons/utils/ClientConfigurationUtils.java#L45) which apply the best practices for `SphereClient` creation. If you have custom requirements for the sphere client creation, have a look into the [Important Usage Tips](IMPORTANT_USAGE_TIPS.md). 2. The sync expects a list of `TypeDraft`s that have their `key` fields set to be matched with diff --git a/mkdocs.yml b/mkdocs.yml index 787c143a20..c0ea1cbba8 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -61,7 +61,7 @@ nav: - Advanced: - Sync Options: usage/SYNC_OPTIONS.md - Usage Tips: usage/IMPORTANT_USAGE_TIPS.md - - Javadoc: https://commercetools.github.io/commercetools-sync-java/v/3.0.0/ + - Javadoc: https://commercetools.github.io/commercetools-sync-java/v/3.0.1/ - Release notes: RELEASE_NOTES.md - Roadmap: https://github.com/commercetools/commercetools-sync-java/milestones - Issues: https://github.com/commercetools/commercetools-sync-java/issues diff --git a/src/main/java/com/commercetools/sync/customers/utils/CustomerUpdateActionUtils.java b/src/main/java/com/commercetools/sync/customers/utils/CustomerUpdateActionUtils.java index d5d58d0c8d..8a1144c9f9 100644 --- a/src/main/java/com/commercetools/sync/customers/utils/CustomerUpdateActionUtils.java +++ b/src/main/java/com/commercetools/sync/customers/utils/CustomerUpdateActionUtils.java @@ -54,7 +54,6 @@ import static com.commercetools.sync.commons.utils.CommonTypeUpdateActionUtils.buildUpdateActionForReferences; import static java.lang.String.format; import static java.util.Collections.emptyList; -import static java.util.Collections.singletonList; import static java.util.function.Function.identity; import static java.util.stream.Collectors.toList; import static java.util.stream.Collectors.toMap;