Skip to content

Commit 3222700

Browse files
Merge pull request #241 from commercetools/207-improve-product-deletion
207 improve product deletion
2 parents b61de4b + 34e4470 commit 3222700

7 files changed

Lines changed: 106 additions & 74 deletions

File tree

Lines changed: 36 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
11
package com.commercetools.sync.integration.commons.utils;
22

3-
import com.commercetools.sync.commons.helpers.BaseSyncStatistics;
4-
import com.commercetools.sync.commons.utils.CtpQueryUtils;
5-
import com.fasterxml.jackson.core.JsonProcessingException;
6-
import com.fasterxml.jackson.databind.ObjectMapper;
73
import io.sphere.sdk.client.SphereClient;
84
import io.sphere.sdk.client.SphereRequest;
95
import io.sphere.sdk.models.Resource;
@@ -12,15 +8,15 @@
128
import io.sphere.sdk.types.queries.TypeQuery;
139

1410
import javax.annotation.Nonnull;
15-
import java.util.List;
11+
import java.util.Collection;
1612
import java.util.concurrent.CompletableFuture;
1713
import java.util.concurrent.CompletionStage;
1814
import java.util.function.Function;
19-
import java.util.function.Supplier;
2015
import java.util.stream.Stream;
2116

2217
import static com.commercetools.sync.integration.commons.utils.SphereClientUtils.CTP_SOURCE_CLIENT;
2318
import static com.commercetools.sync.integration.commons.utils.SphereClientUtils.CTP_TARGET_CLIENT;
19+
import static io.sphere.sdk.queries.QueryExecutionUtils.queryAll;
2420

2521
public final class ITUtils {
2622

@@ -30,7 +26,7 @@ public final class ITUtils {
3026
* @param ctpClient defines the CTP project to delete the Types from.
3127
*/
3228
public static void deleteTypes(@Nonnull final SphereClient ctpClient) {
33-
queryAndApply(ctpClient, TypeQuery::of, TypeDeleteCommand::of);
29+
queryAndExecute(ctpClient, TypeQuery.of(), TypeDeleteCommand::of);
3430
}
3531

3632
/**
@@ -42,44 +38,48 @@ public static void deleteTypesFromTargetAndSource() {
4238
}
4339

4440
/**
45-
* Builds a JSON String that represents the fields of the supplied instance of {@link BaseSyncStatistics}.
46-
* Note: The order of the fields in the built JSON String depends on the order of the instance variables in this
47-
* class.
41+
* Applies the {@code resourceToRequestMapper} function on each page, resulting from the {@code query} executed by
42+
* the {@code ctpClient}, to map each resource to a {@link SphereRequest} and then executes these requests in
43+
* parallel within each page.
4844
*
49-
* @param statistics the instance of {@link BaseSyncStatistics} from which to create a JSON String.
50-
* @return a JSON String representation of the statistics object.
45+
* @param ctpClient defines the CTP project to apply the query on.
46+
* @param query query that should be made on the CTP project.
47+
* @param resourceToRequestMapper defines a mapper function that should be applied on each resource, in the fetched
48+
* page from the query on the specified CTP project, to map it to a
49+
* {@link SphereRequest}.
5150
*/
52-
public static String getStatisticsAsJSONString(@Nonnull final BaseSyncStatistics statistics)
53-
throws JsonProcessingException {
54-
final ObjectMapper mapper = new ObjectMapper();
55-
return mapper.writeValueAsString(statistics);
51+
public static <T extends Resource, C extends QueryDsl<T, C>> void queryAndExecute(
52+
@Nonnull final SphereClient ctpClient,
53+
@Nonnull final QueryDsl<T, C> query,
54+
@Nonnull final Function<T, SphereRequest<T>> resourceToRequestMapper) {
55+
56+
queryAndCompose(ctpClient, query, resource -> ctpClient.execute(resourceToRequestMapper.apply(resource)));
5657
}
5758

5859
/**
59-
* Applies the {@code pageMapper} function on each page fetched from the supplied {@code queryRequestSupplier} on
60-
* the supplied {@code ctpClient}.
60+
* Applies the {@code resourceToRequestMapper} function on each page, resulting from the {@code query} executed by
61+
* the {@code ctpClient}, to map each resource to a {@link CompletionStage} and then executes these stages in
62+
* parallel within each page.
6163
*
62-
* @param ctpClient defines the CTP project to apply the query on.
63-
* @param queryRequestSupplier defines a supplier which, when executed, returns the query that should be made on
64-
* the CTP project.
65-
* @param resourceMapper defines a mapper function that should be applied on each resource in the fetched page
66-
* from the query on the specified CTP project.
64+
*
65+
* @param ctpClient defines the CTP project to apply the query on.
66+
* @param query query that should be made on the CTP project.
67+
* @param resourceToStageMapper defines a mapper function that should be applied on each resource, in the fetched
68+
* page from the query on the specified CTP project, to map it to a
69+
* {@link CompletionStage}.
6770
*/
68-
public static <T extends Resource, C extends QueryDsl<T, C>> void queryAndApply(
71+
public static <T extends Resource, C extends QueryDsl<T, C>, S> void queryAndCompose(
6972
@Nonnull final SphereClient ctpClient,
70-
@Nonnull final Supplier<QueryDsl<T, C>> queryRequestSupplier,
71-
@Nonnull final Function<T, SphereRequest<T>> resourceMapper) {
73+
@Nonnull final QueryDsl<T, C> query,
74+
@Nonnull final Function<T, CompletionStage<S>> resourceToStageMapper) {
7275

73-
final Function<List<T>, Stream<CompletableFuture<T>>> pageMapper =
74-
pageElements -> pageElements.stream()
75-
.map(resourceMapper)
76-
.map(ctpClient::execute)
77-
.map(CompletionStage::toCompletableFuture);
76+
queryAll(ctpClient, query, resourceToStageMapper)
77+
.thenApply(Collection::stream)
78+
.thenCompose(ITUtils::toAllOf)
79+
.toCompletableFuture().join();
80+
}
7881

79-
CtpQueryUtils.queryAll(ctpClient, queryRequestSupplier.get(), pageMapper)
80-
.thenApply(list -> list.stream().flatMap(Function.identity()))
81-
.thenApply(stream -> stream.toArray(CompletableFuture[]::new))
82-
.thenCompose(CompletableFuture::allOf)
83-
.toCompletableFuture().join();
82+
private static <T> CompletionStage<Void> toAllOf(@Nonnull final Stream<? extends CompletionStage<T>> stageStream) {
83+
return CompletableFuture.allOf(stageStream.toArray(CompletableFuture[]::new));
8484
}
8585
}

src/integration-test/java/com/commercetools/sync/integration/commons/utils/ProductITUtils.java

Lines changed: 61 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,11 @@
1919

2020
import javax.annotation.Nonnull;
2121
import java.util.List;
22-
import java.util.function.Function;
22+
import java.util.concurrent.CompletionStage;
2323

2424
import static com.commercetools.sync.integration.commons.utils.CategoryITUtils.deleteAllCategories;
2525
import static com.commercetools.sync.integration.commons.utils.ITUtils.deleteTypes;
26-
import static com.commercetools.sync.integration.commons.utils.ITUtils.queryAndApply;
26+
import static com.commercetools.sync.integration.commons.utils.ITUtils.queryAndCompose;
2727
import static com.commercetools.sync.integration.commons.utils.ProductTypeITUtils.deleteProductTypes;
2828
import static com.commercetools.sync.integration.commons.utils.StateITUtils.deleteStates;
2929
import static com.commercetools.sync.integration.commons.utils.TaxCategoryITUtils.deleteTaxCategories;
@@ -48,19 +48,68 @@ public static void deleteProductSyncTestData(@Nonnull final SphereClient ctpClie
4848
}
4949

5050
/**
51-
* Deletes all products from the CTP project defined by the {@code ctpClient}.
51+
* Unpublishes all published products, then deletes all products from the CTP project defined by the
52+
* {@code ctpClient}.
5253
*
53-
* @param ctpClient defines the CTP project to delete the categories from.
54+
* @param ctpClient defines the CTP project to delete the products from.
5455
*/
5556
public static void deleteAllProducts(@Nonnull final SphereClient ctpClient) {
56-
final Function<Product, SphereRequest<Product>> unpublishAndDelete = product -> {
57-
if (product.getMasterData().isPublished()) {
58-
product = ctpClient.execute(ProductUpdateCommand.of(product, Unpublish.of()))
59-
.toCompletableFuture().join();
60-
}
61-
return ProductDeleteCommand.of(product);
62-
};
63-
queryAndApply(ctpClient, ProductQuery::of, unpublishAndDelete);
57+
queryAndCompose(ctpClient, ProductQuery.of(), product -> safeDeleteProduct(ctpClient, product));
58+
}
59+
60+
/**
61+
* If the {@code product} is published, issues an unpublish request followed by a delete. Otherwise,
62+
* issues a delete request right away.
63+
*
64+
* @param ctpClient defines the CTP project to delete the product from.
65+
* @param product the product to be deleted.
66+
* @return a {@link CompletionStage} containing the deleted product.
67+
*/
68+
@Nonnull
69+
private static CompletionStage<Product> safeDeleteProduct(@Nonnull final SphereClient ctpClient,
70+
@Nonnull final Product product) {
71+
return product.getMasterData().isPublished()
72+
? unpublishAndDeleteProduct(ctpClient, product) : deleteProduct(ctpClient, product);
73+
}
74+
75+
/**
76+
* Issues an unpublish request followed by a delete.
77+
*
78+
* @param ctpClient defines the CTP project to delete the product from.
79+
* @param product the product to be unpublished and deleted.
80+
* @return a {@link CompletionStage} containing the deleted product.
81+
*/
82+
@Nonnull
83+
private static CompletionStage<Product> unpublishAndDeleteProduct(@Nonnull final SphereClient ctpClient,
84+
@Nonnull final Product product) {
85+
return ctpClient.execute(buildUnpublishRequest(product))
86+
.thenCompose(unpublishedProduct ->
87+
deleteProduct(ctpClient, unpublishedProduct));
88+
}
89+
90+
/**
91+
* Note: This method assumes the product is unpublished.
92+
*
93+
* @param ctpClient the client defining the CTP project to delete the product from.
94+
* @param product the product to be deleted.
95+
* @return a {@link CompletionStage} containing the deleted product. If the product supplied was already unpublished
96+
* the method will return a completion stage that completed exceptionally.
97+
*/
98+
@Nonnull
99+
private static CompletionStage<Product> deleteProduct(@Nonnull final SphereClient ctpClient,
100+
@Nonnull final Product product) {
101+
return ctpClient.execute(ProductDeleteCommand.of(product));
102+
}
103+
104+
/**
105+
* Builds an unpublish request for the supplied product.
106+
*
107+
* @param product defines the product to build an un publish request for.
108+
* @return an unpublish request for the supplied product.
109+
*/
110+
@Nonnull
111+
private static SphereRequest<Product> buildUnpublishRequest(@Nonnull final Product product) {
112+
return ProductUpdateCommand.of(product, Unpublish.of());
64113
}
65114

66115
/**

src/integration-test/java/com/commercetools/sync/integration/commons/utils/ProductTypeITUtils.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
import java.util.Locale;
1919
import java.util.Optional;
2020

21-
import static com.commercetools.sync.integration.commons.utils.ITUtils.queryAndApply;
21+
import static com.commercetools.sync.integration.commons.utils.ITUtils.queryAndExecute;
2222
import static com.commercetools.sync.integration.commons.utils.SphereClientUtils.CTP_SOURCE_CLIENT;
2323
import static com.commercetools.sync.integration.commons.utils.SphereClientUtils.CTP_TARGET_CLIENT;
2424
import static io.sphere.sdk.json.SphereJsonUtils.readObjectFromResource;
@@ -44,7 +44,7 @@ public static void deleteProductTypesFromTargetAndSource() {
4444
* @param ctpClient defines the CTP project to delete the categories from.
4545
*/
4646
public static void deleteProductTypes(@Nonnull final SphereClient ctpClient) {
47-
queryAndApply(ctpClient, ProductTypeQuery::of, ProductTypeDeleteCommand::of);
47+
queryAndExecute(ctpClient, ProductTypeQuery.of(), ProductTypeDeleteCommand::of);
4848
}
4949

5050
/**

src/integration-test/java/com/commercetools/sync/integration/commons/utils/StateITUtils.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
import javax.annotation.Nonnull;
1212

13-
import static com.commercetools.sync.integration.commons.utils.ITUtils.queryAndApply;
13+
import static com.commercetools.sync.integration.commons.utils.ITUtils.queryAndExecute;
1414
import static com.commercetools.sync.integration.commons.utils.SphereClientUtils.CTP_SOURCE_CLIENT;
1515
import static com.commercetools.sync.integration.commons.utils.SphereClientUtils.CTP_TARGET_CLIENT;
1616
import static com.commercetools.sync.services.impl.StateServiceImpl.buildStateQuery;
@@ -36,7 +36,7 @@ public static void deleteStatesFromTargetAndSourceByType(@Nonnull final StateTyp
3636
*/
3737
public static void deleteStates(@Nonnull final SphereClient ctpClient,
3838
@Nonnull final StateType stateType) {
39-
queryAndApply(ctpClient, () -> buildStateQuery(stateType), StateDeleteCommand::of);
39+
queryAndExecute(ctpClient, buildStateQuery(stateType), StateDeleteCommand::of);
4040
}
4141

4242
/**

src/integration-test/java/com/commercetools/sync/integration/commons/utils/TaxCategoryITUtils.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
import javax.annotation.Nonnull;
1515

16-
import static com.commercetools.sync.integration.commons.utils.ITUtils.queryAndApply;
16+
import static com.commercetools.sync.integration.commons.utils.ITUtils.queryAndExecute;
1717
import static com.commercetools.sync.integration.commons.utils.SphereClientUtils.CTP_SOURCE_CLIENT;
1818
import static com.commercetools.sync.integration.commons.utils.SphereClientUtils.CTP_TARGET_CLIENT;
1919
import static com.commercetools.tests.utils.CompletionStageUtil.executeBlocking;
@@ -41,7 +41,7 @@ public static void deleteTaxCategoriesFromTargetAndSource() {
4141
* @param ctpClient defines the CTP project to delete the tax categories from.
4242
*/
4343
public static void deleteTaxCategories(@Nonnull final SphereClient ctpClient) {
44-
queryAndApply(ctpClient, TaxCategoryQuery::of, TaxCategoryDeleteCommand::of);
44+
queryAndExecute(ctpClient, TaxCategoryQuery.of(), TaxCategoryDeleteCommand::of);
4545
}
4646

4747
/**

src/integration-test/java/com/commercetools/sync/integration/inventories/utils/InventoryITUtils.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
import java.util.Map;
4040
import java.util.Optional;
4141

42-
import static com.commercetools.sync.integration.commons.utils.ITUtils.queryAndApply;
42+
import static com.commercetools.sync.integration.commons.utils.ITUtils.queryAndExecute;
4343
import static com.commercetools.sync.integration.commons.utils.SphereClientUtils.CTP_SOURCE_CLIENT;
4444
import static com.commercetools.sync.integration.commons.utils.SphereClientUtils.CTP_TARGET_CLIENT;
4545
import static java.util.Collections.singletonList;
@@ -70,7 +70,7 @@ public class InventoryITUtils {
7070
* @param ctpClient represents the CTP project the inventory entries will be deleted from.
7171
*/
7272
public static void deleteInventoryEntries(@Nonnull final SphereClient ctpClient) {
73-
queryAndApply(ctpClient, InventoryEntryQuery::of, InventoryEntryDeleteCommand::of);
73+
queryAndExecute(ctpClient, InventoryEntryQuery.of(), InventoryEntryDeleteCommand::of);
7474
}
7575

7676
/**
@@ -79,7 +79,7 @@ public static void deleteInventoryEntries(@Nonnull final SphereClient ctpClient)
7979
* @param ctpClient represents the CTP project the channels will be deleted from.
8080
*/
8181
public static void deleteSupplyChannels(@Nonnull final SphereClient ctpClient) {
82-
queryAndApply(ctpClient, ChannelQuery::of, ChannelDeleteCommand::of);
82+
queryAndExecute(ctpClient, ChannelQuery.of(), ChannelDeleteCommand::of);
8383
}
8484

8585
/**

src/test/java/com/commercetools/sync/commons/MockUtils.java

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
11
package com.commercetools.sync.commons;
22

3-
import com.commercetools.sync.commons.helpers.BaseSyncStatistics;
43
import com.commercetools.sync.services.CategoryService;
54
import com.commercetools.sync.services.TypeService;
6-
import com.fasterxml.jackson.core.JsonProcessingException;
75
import com.fasterxml.jackson.databind.JsonNode;
8-
import com.fasterxml.jackson.databind.ObjectMapper;
96
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
107
import io.sphere.sdk.categories.Category;
118
import io.sphere.sdk.types.CustomFieldsDraft;
@@ -79,18 +76,4 @@ public static TypeService getMockTypeService() {
7976
.thenReturn(completedFuture(Optional.of("typeId")));
8077
return typeService;
8178
}
82-
83-
/**
84-
* Builds a JSON String that represents the fields of the supplied instance of {@link BaseSyncStatistics}.
85-
* Note: The order of the fields in the built JSON String depends on the order of the instance variables in this
86-
* class.
87-
*
88-
* @param statistics the instance of {@link BaseSyncStatistics} from which to create a JSON String.
89-
* @return a JSON representation of the given {@code statistics} as a String.
90-
*/
91-
public static String getStatisticsAsJsonString(@Nonnull final BaseSyncStatistics statistics)
92-
throws JsonProcessingException {
93-
final ObjectMapper mapper = new ObjectMapper();
94-
return mapper.writeValueAsString(statistics);
95-
}
9679
}

0 commit comments

Comments
 (0)