Skip to content

Commit e8811bc

Browse files
authored
fix(bug): custom comparison for lenum (#1207)
1 parent 3569fda commit e8811bc

File tree

6 files changed

+191
-12
lines changed

6 files changed

+191
-12
lines changed

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ plugins {
99
}
1010

1111
ext{
12-
commercetoolsJavaSdkV2Version = '17.18.0'
12+
commercetoolsJavaSdkV2Version = '17.28.0'
1313
mockitoJunitJupiterVersion = '5.16.1'
1414
jupiterApiVersion = '5.11.3'
1515
assertjVersion = '3.26.3'

src/integration-test/java/com/commercetools/sync/integration/externalsource/products/ProductSyncIT.java

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1015,6 +1015,121 @@ void sync_shouldSyncProductsWithoutAnyVariants() {
10151015
assertThat(syncStatistics).hasValues(2, 1, 1, 0, 0);
10161016
}
10171017

1018+
@Test
1019+
void sync_withNoChangesInIntegerAttribute_shouldNotUpdateTheProduct() {
1020+
// preparation
1021+
final List<ProductUpdateAction> updateActions = new ArrayList<>();
1022+
final TriConsumer<SyncException, Optional<ProductDraft>, Optional<ProductProjection>>
1023+
warningCallBack =
1024+
(exception, newResource, oldResource) ->
1025+
warningCallBackMessages.add(exception.getMessage());
1026+
1027+
final ProductSyncOptions customOptions =
1028+
ProductSyncOptionsBuilder.of(TestClientUtils.CTP_TARGET_CLIENT)
1029+
.errorCallback(
1030+
(exception, oldResource, newResource, actions) ->
1031+
collectErrors(exception.getMessage(), exception.getCause()))
1032+
.warningCallback(warningCallBack)
1033+
.beforeUpdateCallback(
1034+
(actions, draft, old) -> {
1035+
updateActions.addAll(actions);
1036+
return actions;
1037+
})
1038+
.build();
1039+
1040+
final ProductDraft productDraft =
1041+
createProductDraftBuilder(
1042+
PRODUCT_KEY_1_RESOURCE_PATH,
1043+
ProductTypeResourceIdentifierBuilder.of().key(productType.getKey()).build())
1044+
.categories(emptyList())
1045+
.taxCategory((TaxCategoryResourceIdentifier) null)
1046+
.state((StateResourceIdentifier) null)
1047+
.build();
1048+
1049+
// Creating the attribute draft with the changes
1050+
final Attribute sortAttrDraft = AttributeBuilder.of().name("sort").value(10).build();
1051+
1052+
// Creating the product variant draft with the product reference attribute
1053+
final List<Attribute> attributes = singletonList(sortAttrDraft);
1054+
1055+
final ProductVariantDraft masterVariant =
1056+
ProductVariantDraftBuilder.of(productDraft.getMasterVariant())
1057+
.attributes(attributes)
1058+
.build();
1059+
1060+
final ProductDraft productDraftWithChangedAttributes =
1061+
ProductDraftBuilder.of(productDraft).masterVariant(masterVariant).build();
1062+
1063+
// test
1064+
final ProductSync productSync = new ProductSync(customOptions);
1065+
productSync.sync(singletonList(productDraftWithChangedAttributes)).toCompletableFuture().join();
1066+
final ProductSyncStatistics syncStatistics =
1067+
productSync
1068+
.sync(singletonList(productDraftWithChangedAttributes))
1069+
.toCompletableFuture()
1070+
.join();
1071+
1072+
assertThat(syncStatistics).hasValues(2, 0, 1, 0, 0);
1073+
}
1074+
1075+
@Test
1076+
void sync_withNoChangesInLenumAttribute_shouldNotUpdateTheProduct() {
1077+
// preparation
1078+
final List<ProductUpdateAction> updateActions = new ArrayList<>();
1079+
final TriConsumer<SyncException, Optional<ProductDraft>, Optional<ProductProjection>>
1080+
warningCallBack =
1081+
(exception, newResource, oldResource) ->
1082+
warningCallBackMessages.add(exception.getMessage());
1083+
1084+
final ProductSyncOptions customOptions =
1085+
ProductSyncOptionsBuilder.of(TestClientUtils.CTP_TARGET_CLIENT)
1086+
.errorCallback(
1087+
(exception, oldResource, newResource, actions) ->
1088+
collectErrors(exception.getMessage(), exception.getCause()))
1089+
.warningCallback(warningCallBack)
1090+
.beforeUpdateCallback(
1091+
(actions, draft, old) -> {
1092+
updateActions.addAll(actions);
1093+
return actions;
1094+
})
1095+
.build();
1096+
1097+
final ProductDraft productDraft =
1098+
createProductDraftBuilder(
1099+
PRODUCT_KEY_1_RESOURCE_PATH,
1100+
ProductTypeResourceIdentifierBuilder.of().key(productType.getKey()).build())
1101+
.categories(emptyList())
1102+
.taxCategory((TaxCategoryResourceIdentifier) null)
1103+
.state((StateResourceIdentifier) null)
1104+
.build();
1105+
1106+
// Creating the attribute draft with the changes
1107+
final Attribute technologyAttrDraft =
1108+
AttributeBuilder.of().name("technology").value("laser").build();
1109+
1110+
// Creating the product variant draft with the product reference attribute
1111+
final List<Attribute> attributes = singletonList(technologyAttrDraft);
1112+
1113+
final ProductVariantDraft masterVariant =
1114+
ProductVariantDraftBuilder.of(productDraft.getMasterVariant())
1115+
.attributes(attributes)
1116+
.build();
1117+
1118+
final ProductDraft productDraftWithChangedAttributes =
1119+
ProductDraftBuilder.of(productDraft).masterVariant(masterVariant).build();
1120+
1121+
// test
1122+
final ProductSync productSync = new ProductSync(customOptions);
1123+
productSync.sync(singletonList(productDraftWithChangedAttributes)).toCompletableFuture().join();
1124+
final ProductSyncStatistics syncStatistics =
1125+
productSync
1126+
.sync(singletonList(productDraftWithChangedAttributes))
1127+
.toCompletableFuture()
1128+
.join();
1129+
1130+
assertThat(syncStatistics).hasValues(2, 0, 1, 0, 0);
1131+
}
1132+
10181133
@Test
10191134
void sync_withProductContainingAttributeChanges_shouldSyncProductCorrectly() {
10201135
// preparation

src/main/java/com/commercetools/sync/products/utils/AttributeUtils.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,7 @@ public final class AttributeUtils {
2020
* converted value in the attribute.
2121
*
2222
* @param attribute - Attribute to replace it's value with a JSON representation
23-
* @return - a {@link JsonNode} representing the attribute's value. extracted from the given
24-
* attribute or empty list if the attribute * doesn't contain reference types.
23+
* @return - a {@link JsonNode} representing the attribute's value.
2524
*/
2625
@Nonnull
2726
public static JsonNode replaceAttributeValueWithJsonAndReturnValue(

src/main/java/com/commercetools/sync/products/utils/ProductUpdateActionUtils.java

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
import com.commercetools.api.models.product.ProductUpdateAction;
5353
import com.commercetools.api.models.product.ProductVariant;
5454
import com.commercetools.api.models.product.ProductVariantDraft;
55+
import com.commercetools.api.models.product.SearchKeyword;
5556
import com.commercetools.api.models.product.SearchKeywords;
5657
import com.commercetools.api.models.state.State;
5758
import com.commercetools.api.models.state.StateResourceIdentifier;
@@ -337,14 +338,23 @@ public static Optional<ProductUpdateAction> buildSetSearchKeywordsUpdateAction(
337338
if (newSearchKeywords == null) {
338339
return Optional.empty();
339340
} else {
340-
return buildUpdateAction(
341-
oldSearchKeywords,
342-
newSearchKeywords,
343-
() ->
344-
ProductSetSearchKeywordsActionBuilder.of()
345-
.searchKeywords(newSearchKeywords)
346-
.staged(true)
347-
.build());
341+
// For some reasons, values for searchKeywords could be null or {} since Java SDK v17.28.0
342+
// Even though they mean the same thing, they are not equals and would trigger update action
343+
// Thus I had to do a manual comparison here.
344+
final Map<String, List<SearchKeyword>> newSearchValues = newSearchKeywords.values();
345+
final Map<String, List<SearchKeyword>> oldSearchValue = oldSearchKeywords.values();
346+
if (newSearchValues == null && (oldSearchValue == null || oldSearchValue.size() == 0)) {
347+
return Optional.empty();
348+
} else {
349+
return buildUpdateAction(
350+
oldSearchKeywords,
351+
newSearchKeywords,
352+
() ->
353+
ProductSetSearchKeywordsActionBuilder.of()
354+
.searchKeywords(newSearchKeywords)
355+
.staged(true)
356+
.build());
357+
}
348358
}
349359
}
350360

src/main/java/com/commercetools/sync/products/utils/ProductVariantAttributeUpdateActionUtils.java

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package com.commercetools.sync.products.utils;
22

3-
import static com.commercetools.sync.commons.utils.CommonTypeUpdateActionUtils.buildUpdateAction;
43
import static java.lang.String.format;
54

65
import com.commercetools.api.models.product.Attribute;
@@ -10,12 +9,17 @@
109
import com.commercetools.api.models.product.ProductSetAttributeInAllVariantsActionBuilder;
1110
import com.commercetools.api.models.product.ProductUpdateAction;
1211
import com.commercetools.sync.commons.exceptions.BuildUpdateActionException;
12+
import com.commercetools.sync.commons.utils.CommonTypeUpdateActionUtils;
1313
import com.commercetools.sync.products.AttributeMetaData;
1414
import com.fasterxml.jackson.databind.JsonNode;
15+
import com.fasterxml.jackson.databind.node.ObjectNode;
16+
import com.fasterxml.jackson.databind.node.TextNode;
1517
import io.vrap.rmf.base.client.utils.json.JsonUtils;
1618
import java.util.List;
1719
import java.util.Map;
20+
import java.util.Objects;
1821
import java.util.Optional;
22+
import java.util.function.Supplier;
1923
import javax.annotation.Nonnull;
2024
import javax.annotation.Nullable;
2125

@@ -93,5 +97,22 @@ public static Optional<ProductUpdateAction> buildProductVariantAttributeUpdateAc
9397
.build());
9498
}
9599

100+
@Nonnull
101+
private static Optional<ProductUpdateAction> buildUpdateAction(
102+
final JsonNode oldAttributeValueAsJson,
103+
final JsonNode newAttributeValueAsJson,
104+
final Supplier<ProductUpdateAction> actionSupplier) {
105+
if (oldAttributeValueAsJson instanceof ObjectNode
106+
&& newAttributeValueAsJson instanceof TextNode) {
107+
String oldKey = oldAttributeValueAsJson.get("key").asText();
108+
String newKey = newAttributeValueAsJson.asText();
109+
return !Objects.equals(oldKey, newKey)
110+
? Optional.ofNullable(actionSupplier.get())
111+
: Optional.empty();
112+
}
113+
return CommonTypeUpdateActionUtils.buildUpdateAction(
114+
oldAttributeValueAsJson, newAttributeValueAsJson, actionSupplier);
115+
}
116+
96117
private ProductVariantAttributeUpdateActionUtils() {}
97118
}

src/test/resources/product-type.json

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,40 @@
66
"description": "all fleisch",
77
"classifier": "Complex",
88
"attributes": [
9+
{
10+
"name": "technology",
11+
"label": {
12+
"en": "technology"
13+
},
14+
"isRequired": false,
15+
"type": {
16+
"name": "lenum",
17+
"values": [
18+
{
19+
"key": "laser",
20+
"label": {
21+
"en": "Laser"
22+
}
23+
},
24+
{
25+
"key": "plasma",
26+
"label": {
27+
"en": "Plasma"
28+
}
29+
},
30+
{
31+
"key": "waterjet",
32+
"label": {
33+
"en": "Waterjet"
34+
}
35+
}
36+
]
37+
},
38+
"attributeConstraint": "None",
39+
"isSearchable": false,
40+
"inputHint": "SingleLine",
41+
"displayGroup": "Other"
42+
},
943
{
1044
"name": "priceInfo",
1145
"label": {

0 commit comments

Comments
 (0)