Skip to content

Commit 62f3266

Browse files
Merge pull request #1238 from commercetools/BMU-2448_store
[BMU-2448] Store in Shipping lists
2 parents 47364d1 + 336fc1a commit 62f3266

File tree

7 files changed

+296
-5
lines changed

7 files changed

+296
-5
lines changed

src/integration-test/java/com/commercetools/sync/integration/externalsource/shoppinglists/ShoppingListSyncIT.java

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import static com.commercetools.api.models.common.LocalizedString.ofEnglish;
44
import static com.commercetools.sync.commons.asserts.statistics.AssertionsForStatistics.assertThat;
5+
import static com.commercetools.sync.integration.commons.utils.CustomerITUtils.ensureStore;
56
import static com.commercetools.sync.integration.commons.utils.ShoppingListITUtils.*;
67
import static com.commercetools.sync.integration.commons.utils.TestClientUtils.CTP_SOURCE_CLIENT;
78
import static com.commercetools.sync.integration.commons.utils.TestClientUtils.CTP_TARGET_CLIENT;
@@ -36,6 +37,7 @@
3637
import com.commercetools.api.models.shopping_list.TextLineItem;
3738
import com.commercetools.api.models.shopping_list.TextLineItemDraft;
3839
import com.commercetools.api.models.shopping_list.TextLineItemDraftBuilder;
40+
import com.commercetools.api.models.store.StoreResourceIdentifier;
3941
import com.commercetools.api.models.type.CustomFields;
4042
import com.commercetools.api.models.type.CustomFieldsDraftBuilder;
4143
import com.commercetools.api.models.type.TypeReference;
@@ -521,4 +523,62 @@ private List<ShoppingListLineItemDraft> setNullToAddedAtValuesForLineItems(
521523
ShoppingListLineItemDraftBuilder.of(lineItemDraft).addedAt(null).build())
522524
.collect(toList());
523525
}
526+
527+
@Test
528+
void sync_WithStoreChange_ShouldUpdateShoppingListStore() {
529+
// Create the store that will be referenced
530+
ensureStore(CTP_TARGET_CLIENT, "different-store-key");
531+
532+
// Create a shopping list draft with a different store
533+
final ShoppingListDraft modifiedDraft =
534+
ShoppingListDraftBuilder.of(shoppingListDraftSampleCarrotCake)
535+
.store(
536+
storeResourceIdentifierBuilder ->
537+
storeResourceIdentifierBuilder.key("different-store-key"))
538+
.build();
539+
540+
final ShoppingListSyncStatistics shoppingListSyncStatistics =
541+
shoppingListSync.sync(singletonList(modifiedDraft)).toCompletableFuture().join();
542+
543+
assertThat(errorMessages).isEmpty();
544+
assertThat(warningMessages).isEmpty();
545+
assertThat(exceptions).isEmpty();
546+
547+
// Verify that a setStore update action was generated
548+
assertThat(updateActionList)
549+
.anySatisfy(
550+
action -> {
551+
assertThat(action.getAction()).isEqualTo("setStore");
552+
});
553+
554+
assertThat(shoppingListSyncStatistics).hasValues(1, 0, 1, 0);
555+
}
556+
557+
@Test
558+
void sync_WithNullStore_ShouldUpdateShoppingListStoreToNull() {
559+
// Create a shopping list draft with null store
560+
final StoreResourceIdentifier nullStore = null;
561+
final ShoppingListDraft modifiedDraft =
562+
ShoppingListDraftBuilder.of(shoppingListDraftSampleCarrotCake).store(nullStore).build();
563+
564+
final ShoppingListSyncStatistics shoppingListSyncStatistics =
565+
shoppingListSync.sync(singletonList(modifiedDraft)).toCompletableFuture().join();
566+
567+
assertThat(errorMessages).isEmpty();
568+
assertThat(warningMessages).isEmpty();
569+
assertThat(exceptions).isEmpty();
570+
571+
// If the original had a store and we set it to null, we should see an update
572+
if (shoppingListSampleCarrotCake.getStore() != null) {
573+
assertThat(updateActionList)
574+
.anySatisfy(
575+
action -> {
576+
assertThat(action.getAction()).isEqualTo("setStore");
577+
});
578+
assertThat(shoppingListSyncStatistics).hasValues(1, 0, 1, 0);
579+
} else {
580+
// If both are null, no update should occur
581+
assertThat(shoppingListSyncStatistics).hasValues(1, 0, 0, 0);
582+
}
583+
}
524584
}

src/main/java/com/commercetools/sync/shoppinglists/helpers/ShoppingListReferenceResolver.java

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
import com.commercetools.api.models.customer.CustomerResourceIdentifierBuilder;
1212
import com.commercetools.api.models.shopping_list.ShoppingListDraft;
1313
import com.commercetools.api.models.shopping_list.ShoppingListDraftBuilder;
14+
import com.commercetools.api.models.store.StoreResourceIdentifier;
15+
import com.commercetools.api.models.store.StoreResourceIdentifierBuilder;
1416
import com.commercetools.sync.commons.exceptions.ReferenceResolutionException;
1517
import com.commercetools.sync.commons.helpers.CustomReferenceResolver;
1618
import com.commercetools.sync.services.CustomerService;
@@ -32,6 +34,9 @@ public final class ShoppingListReferenceResolver
3234
"Failed to resolve customer resource identifier on "
3335
+ "ShoppingListDraft with key:'%s'. Reason: %s";
3436
static final String CUSTOMER_DOES_NOT_EXIST = "Customer with key '%s' doesn't exist.";
37+
static final String FAILED_TO_RESOLVE_STORE_REFERENCE =
38+
"Failed to resolve store resource identifier on "
39+
+ "ShoppingListDraft with key:'%s'. Reason: %s";
3540
static final String FAILED_TO_RESOLVE_CUSTOM_TYPE =
3641
"Failed to resolve custom type reference on ShoppingListDraft with key:'%s'. ";
3742

@@ -66,9 +71,9 @@ public ShoppingListReferenceResolver(
6671
}
6772

6873
/**
69-
* Given a {@link ShoppingListDraft} this method attempts to resolve the customer and custom type
70-
* references to return a {@link java.util.concurrent.CompletionStage} which contains a new
71-
* instance of the draft with the resolved references.
74+
* Given a {@link ShoppingListDraft} this method attempts to resolve the customer, store and
75+
* custom type references to return a {@link java.util.concurrent.CompletionStage} which contains
76+
* a new instance of the draft with the resolved references.
7277
*
7378
* @param shoppingListDraft the shoppingListDraft to resolve its references.
7479
* @return a {@link java.util.concurrent.CompletionStage} that contains as a result a new
@@ -79,6 +84,7 @@ public ShoppingListReferenceResolver(
7984
public CompletionStage<ShoppingListDraft> resolveReferences(
8085
@Nonnull final ShoppingListDraft shoppingListDraft) {
8186
return resolveCustomerReference(ShoppingListDraftBuilder.of(shoppingListDraft))
87+
.thenCompose(this::resolveStoreReference)
8288
.thenCompose(this::resolveCustomTypeReference)
8389
.thenCompose(this::resolveLineItemReferences)
8490
.thenCompose(this::resolveTextLineItemReferences)
@@ -136,6 +142,32 @@ private CompletionStage<ShoppingListDraftBuilder> fetchAndResolveCustomerReferen
136142
}));
137143
}
138144

145+
@Nonnull
146+
protected CompletionStage<ShoppingListDraftBuilder> resolveStoreReference(
147+
@Nonnull final ShoppingListDraftBuilder draftBuilder) {
148+
149+
final StoreResourceIdentifier storeResourceIdentifier = draftBuilder.getStore();
150+
if (storeResourceIdentifier != null && storeResourceIdentifier.getId() == null) {
151+
try {
152+
final String storeKey = getKeyFromResourceIdentifier(storeResourceIdentifier);
153+
return completedFuture(
154+
draftBuilder.store(StoreResourceIdentifierBuilder.of().key(storeKey).build()));
155+
} catch (ReferenceResolutionException referenceResolutionException) {
156+
return exceptionallyCompletedFuture(
157+
new ReferenceResolutionException(
158+
format(
159+
FAILED_TO_RESOLVE_STORE_REFERENCE,
160+
draftBuilder.getKey(),
161+
referenceResolutionException.getMessage())));
162+
}
163+
} else if (storeResourceIdentifier != null && storeResourceIdentifier.getId() != null) {
164+
return completedFuture(
165+
draftBuilder.store(
166+
StoreResourceIdentifierBuilder.of().id(storeResourceIdentifier.getId()).build()));
167+
}
168+
return completedFuture(draftBuilder);
169+
}
170+
139171
@Nonnull
140172
protected CompletionStage<ShoppingListDraftBuilder> resolveCustomTypeReference(
141173
@Nonnull final ShoppingListDraftBuilder draftBuilder) {

src/main/java/com/commercetools/sync/shoppinglists/utils/ShoppingListReferenceResolutionUtils.java

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
import com.commercetools.api.models.shopping_list.TextLineItem;
1616
import com.commercetools.api.models.shopping_list.TextLineItemDraft;
1717
import com.commercetools.api.models.shopping_list.TextLineItemDraftBuilder;
18+
import com.commercetools.api.models.store.StoreResourceIdentifier;
19+
import com.commercetools.api.models.store.StoreResourceIdentifierBuilder;
1820
import com.commercetools.sync.commons.utils.ReferenceIdToKeyCache;
1921
import java.util.List;
2022
import java.util.Objects;
@@ -48,6 +50,11 @@ public final class ShoppingListReferenceResolutionUtils {
4850
* <td>{@link com.commercetools.api.models.customer.CustomerResourceIdentifier}</td>
4951
* </tr>
5052
* <tr>
53+
* <td>store</td>
54+
* <td>{@link com.commercetools.api.models.store.StoreKeyReference}</td>
55+
* <td>{@link com.commercetools.api.models.store.StoreResourceIdentifier}</td>
56+
* </tr>
57+
* <tr>
5158
* <td>custom.type</td>
5259
* <td>{@link com.commercetools.api.models.type.TypeReference}</td>
5360
* <td>{@link com.commercetools.api.models.type.TypeResourceIdentifier}</td>
@@ -107,6 +114,11 @@ public static List<ShoppingListDraft> mapToShoppingListDrafts(
107114
* <td>{@link com.commercetools.api.models.customer.CustomerResourceIdentifier}</td>
108115
* </tr>
109116
* <tr>
117+
* <td>store</td>
118+
* <td>{@link com.commercetools.api.models.store.StoreKeyReference}</td>
119+
* <td>{@link com.commercetools.api.models.store.StoreResourceIdentifier}</td>
120+
* </tr>
121+
* <tr>
110122
* <td>custom.type</td>
111123
* <td>{@link com.commercetools.api.models.type.TypeReference}</td>
112124
* <td>{@link com.commercetools.api.models.type.TypeResourceIdentifier}</td>
@@ -139,16 +151,18 @@ public static ShoppingListDraft mapToShoppingListDraft(
139151
@Nonnull final ShoppingList shoppingList,
140152
@Nonnull final ReferenceIdToKeyCache referenceIdToKeyCache) {
141153

142-
final CustomerResourceIdentifier resourceIdentifierWithKey =
154+
final CustomerResourceIdentifier customerResourceIdentifierWithKey =
143155
getResourceIdentifierWithKey(
144156
shoppingList.getCustomer(),
145157
referenceIdToKeyCache,
146158
(id, key) -> CustomerResourceIdentifierBuilder.of().id(id).key(key).build());
159+
147160
return ShoppingListDraftBuilder.of()
148161
.name(shoppingList.getName())
149162
.description(shoppingList.getDescription())
150163
.key(shoppingList.getKey())
151-
.customer(resourceIdentifierWithKey)
164+
.customer(customerResourceIdentifierWithKey)
165+
.store(mapToStoreResourceIdentifier(shoppingList))
152166
.slug(shoppingList.getSlug())
153167
.lineItems(mapToLineItemDrafts(shoppingList.getLineItems(), referenceIdToKeyCache))
154168
.textLineItems(
@@ -217,5 +231,14 @@ private static TextLineItemDraft mapToTextLineItemDraft(
217231
.build();
218232
}
219233

234+
@Nullable
235+
private static StoreResourceIdentifier mapToStoreResourceIdentifier(
236+
@Nonnull final ShoppingList shoppingList) {
237+
if (shoppingList.getStore() != null) {
238+
return StoreResourceIdentifierBuilder.of().key(shoppingList.getStore().getKey()).build();
239+
}
240+
return null;
241+
}
242+
220243
private ShoppingListReferenceResolutionUtils() {}
221244
}

src/main/java/com/commercetools/sync/shoppinglists/utils/ShoppingListSyncUtils.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ public static List<ShoppingListUpdateAction> buildActions(
4646
buildSetDescriptionUpdateAction(oldShoppingList, newShoppingList),
4747
buildSetAnonymousIdUpdateAction(oldShoppingList, newShoppingList),
4848
buildSetCustomerUpdateAction(oldShoppingList, newShoppingList),
49+
buildSetStoreUpdateAction(oldShoppingList, newShoppingList),
4950
buildSetDeleteDaysAfterLastModificationUpdateAction(oldShoppingList, newShoppingList));
5051

5152
updateActions.addAll(

src/main/java/com/commercetools/sync/shoppinglists/utils/ShoppingListUpdateActionUtils.java

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import com.commercetools.api.models.shopping_list.ShoppingListSetDeleteDaysAfterLastModificationActionBuilder;
1212
import com.commercetools.api.models.shopping_list.ShoppingListSetDescriptionActionBuilder;
1313
import com.commercetools.api.models.shopping_list.ShoppingListSetSlugActionBuilder;
14+
import com.commercetools.api.models.shopping_list.ShoppingListSetStoreActionBuilder;
1415
import com.commercetools.api.models.shopping_list.ShoppingListUpdateAction;
1516
import java.util.Optional;
1617
import javax.annotation.Nonnull;
@@ -116,6 +117,38 @@ public static Optional<ShoppingListUpdateAction> buildSetCustomerUpdateAction(
116117
.build());
117118
}
118119

120+
/**
121+
* Compares the store references of a {@link ShoppingList} and a {@link ShoppingListDraft} and
122+
* returns an {@link ShoppingListUpdateAction} as a result in an {@link java.util.Optional}. If
123+
* both the {@link ShoppingList} and the {@link ShoppingListDraft} have the same store, then no
124+
* update action is needed and hence an empty {@link java.util.Optional} is returned.
125+
*
126+
* <p>Note: This method compares store keys since {@link
127+
* com.commercetools.api.models.store.StoreKeyReference} contains the key directly.
128+
*
129+
* @param oldShoppingList the shopping list which should be updated.
130+
* @param newShoppingList the shopping list draft which holds the new store.
131+
* @return A filled optional with the update action or an empty optional if the stores are
132+
* identical.
133+
*/
134+
@Nonnull
135+
public static Optional<ShoppingListUpdateAction> buildSetStoreUpdateAction(
136+
@Nonnull final ShoppingList oldShoppingList,
137+
@Nonnull final ShoppingListDraft newShoppingList) {
138+
139+
final String oldStoreKey =
140+
oldShoppingList.getStore() != null ? oldShoppingList.getStore().getKey() : null;
141+
final String newStoreKey =
142+
newShoppingList.getStore() != null && newShoppingList.getStore().getKey() != null
143+
? newShoppingList.getStore().getKey()
144+
: (newShoppingList.getStore() != null ? newShoppingList.getStore().getId() : null);
145+
146+
return buildUpdateAction(
147+
oldStoreKey,
148+
newStoreKey,
149+
() -> ShoppingListSetStoreActionBuilder.of().store(newShoppingList.getStore()).build());
150+
}
151+
119152
/**
120153
* Compares the anonymousIds of {@link ShoppingList} and a {@link ShoppingListDraft} and returns
121154
* an {@link ShoppingListUpdateAction} as a result in an {@link java.util.Optional}. If both the

src/test/java/com/commercetools/sync/shoppinglists/utils/ShoppingListUpdateActionUtilsTest.java

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,12 @@
1717
import com.commercetools.api.models.shopping_list.ShoppingListSetDeleteDaysAfterLastModificationAction;
1818
import com.commercetools.api.models.shopping_list.ShoppingListSetDescriptionAction;
1919
import com.commercetools.api.models.shopping_list.ShoppingListSetSlugAction;
20+
import com.commercetools.api.models.shopping_list.ShoppingListSetStoreAction;
2021
import com.commercetools.api.models.shopping_list.ShoppingListUpdateAction;
22+
import com.commercetools.api.models.store.StoreKeyReference;
23+
import com.commercetools.api.models.store.StoreKeyReferenceBuilder;
24+
import com.commercetools.api.models.store.StoreResourceIdentifier;
25+
import com.commercetools.api.models.store.StoreResourceIdentifierBuilder;
2126
import java.util.Locale;
2227
import java.util.Optional;
2328
import java.util.UUID;
@@ -351,4 +356,94 @@ void buildSetAnonymousId_WithSameValues_ShouldNotBuildUpdateActions() {
351356
assertThat(setDeleteDaysUpdateAction)
352357
.containsInstanceOf(ShoppingListSetDeleteDaysAfterLastModificationAction.class);
353358
}
359+
360+
@Test
361+
void buildSetStoreUpdateAction_WithDifferentStoreKeys_ShouldBuildUpdateAction() {
362+
final StoreKeyReference oldStoreReference =
363+
StoreKeyReferenceBuilder.of().key("old-store-key").build();
364+
final ShoppingList oldShoppingList = mock(ShoppingList.class);
365+
when(oldShoppingList.getStore()).thenReturn(oldStoreReference);
366+
367+
final StoreResourceIdentifier newStoreIdentifier =
368+
StoreResourceIdentifierBuilder.of().key("new-store-key").build();
369+
final ShoppingListDraft newShoppingList = mock(ShoppingListDraft.class);
370+
when(newShoppingList.getStore()).thenReturn(newStoreIdentifier);
371+
372+
final ShoppingListUpdateAction setStoreUpdateAction =
373+
ShoppingListUpdateActionUtils.buildSetStoreUpdateAction(oldShoppingList, newShoppingList)
374+
.orElse(null);
375+
376+
assertThat(setStoreUpdateAction).isNotNull();
377+
assertThat(setStoreUpdateAction.getAction()).isEqualTo("setStore");
378+
assertThat(((ShoppingListSetStoreAction) setStoreUpdateAction).getStore())
379+
.isEqualTo(newStoreIdentifier);
380+
}
381+
382+
@Test
383+
void buildSetStoreUpdateAction_WithSameStoreKeys_ShouldNotBuildUpdateAction() {
384+
final StoreKeyReference oldStoreReference =
385+
StoreKeyReferenceBuilder.of().key("same-store-key").build();
386+
final ShoppingList oldShoppingList = mock(ShoppingList.class);
387+
when(oldShoppingList.getStore()).thenReturn(oldStoreReference);
388+
389+
final StoreResourceIdentifier newStoreIdentifier =
390+
StoreResourceIdentifierBuilder.of().key("same-store-key").build();
391+
final ShoppingListDraft newShoppingList = mock(ShoppingListDraft.class);
392+
when(newShoppingList.getStore()).thenReturn(newStoreIdentifier);
393+
394+
final Optional<ShoppingListUpdateAction> setStoreUpdateAction =
395+
ShoppingListUpdateActionUtils.buildSetStoreUpdateAction(oldShoppingList, newShoppingList);
396+
397+
assertThat(setStoreUpdateAction).isNotPresent();
398+
}
399+
400+
@Test
401+
void buildSetStoreUpdateAction_WithNullOldStore_ShouldBuildUpdateAction() {
402+
final ShoppingList oldShoppingList = mock(ShoppingList.class);
403+
when(oldShoppingList.getStore()).thenReturn(null);
404+
405+
final StoreResourceIdentifier newStoreIdentifier =
406+
StoreResourceIdentifierBuilder.of().key("new-store-key").build();
407+
final ShoppingListDraft newShoppingList = mock(ShoppingListDraft.class);
408+
when(newShoppingList.getStore()).thenReturn(newStoreIdentifier);
409+
410+
final ShoppingListUpdateAction setStoreUpdateAction =
411+
ShoppingListUpdateActionUtils.buildSetStoreUpdateAction(oldShoppingList, newShoppingList)
412+
.orElse(null);
413+
414+
assertThat(setStoreUpdateAction).isNotNull();
415+
assertThat(setStoreUpdateAction.getAction()).isEqualTo("setStore");
416+
}
417+
418+
@Test
419+
void buildSetStoreUpdateAction_WithNullNewStore_ShouldBuildUpdateAction() {
420+
final StoreKeyReference oldStoreReference =
421+
StoreKeyReferenceBuilder.of().key("old-store-key").build();
422+
final ShoppingList oldShoppingList = mock(ShoppingList.class);
423+
when(oldShoppingList.getStore()).thenReturn(oldStoreReference);
424+
425+
final ShoppingListDraft newShoppingList = mock(ShoppingListDraft.class);
426+
when(newShoppingList.getStore()).thenReturn(null);
427+
428+
final ShoppingListUpdateAction setStoreUpdateAction =
429+
ShoppingListUpdateActionUtils.buildSetStoreUpdateAction(oldShoppingList, newShoppingList)
430+
.orElse(null);
431+
432+
assertThat(setStoreUpdateAction).isNotNull();
433+
assertThat(setStoreUpdateAction.getAction()).isEqualTo("setStore");
434+
}
435+
436+
@Test
437+
void buildSetStoreUpdateAction_WithBothNull_ShouldNotBuildUpdateAction() {
438+
final ShoppingList oldShoppingList = mock(ShoppingList.class);
439+
when(oldShoppingList.getStore()).thenReturn(null);
440+
441+
final ShoppingListDraft newShoppingList = mock(ShoppingListDraft.class);
442+
when(newShoppingList.getStore()).thenReturn(null);
443+
444+
final Optional<ShoppingListUpdateAction> setStoreUpdateAction =
445+
ShoppingListUpdateActionUtils.buildSetStoreUpdateAction(oldShoppingList, newShoppingList);
446+
447+
assertThat(setStoreUpdateAction).isNotPresent();
448+
}
354449
}

0 commit comments

Comments
 (0)