Skip to content

Commit f437445

Browse files
AshRelateIQGitHub Enterprise
authored andcommitted
Merge pull request #46 from patricksullivan/W-9732906
W-9732906: differentiate system and user errors in sync flow
2 parents 02a02dc + 0305b48 commit f437445

File tree

9 files changed

+280
-272
lines changed

9 files changed

+280
-272
lines changed

examples/checkout-main/classes/B2BSyncCheckInventory.cls

Lines changed: 40 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ global with sharing class B2BSyncCheckInventory {
66
// Validate the input
77
if (cartIds == null || cartIds.size() != 1) {
88
String errorMessage = 'A cart id must be included to B2BSyncCheckInventory';
9-
saveCartValidationOutputError(errorMessage, '');
10-
throw new CalloutException (errorMessage);
9+
// Sync non-user errors skip saveCartValidationOutputError
10+
throw new IllegalArgumentException (errorMessage);
1111
}
1212

1313
// Extract cart id and start processing
@@ -16,57 +16,45 @@ global with sharing class B2BSyncCheckInventory {
1616
}
1717

1818
private static void startCartProcessSync(ID cartId) {
19-
try {
20-
// Get all SKUs and their quantities from cart items.
21-
Map<String, Decimal> quantitiesFromSalesforce = new Map<String, Decimal>();
22-
for (CartItem cartItem : [SELECT Sku, Quantity FROM CartItem WHERE CartId = :cartId AND Type = 'Product' WITH SECURITY_ENFORCED]) {
23-
if (String.isBlank(cartItem.Sku)) {
24-
String errorMessage = 'The SKUs for all products in your cart must be defined.';
25-
saveCartValidationOutputError(errorMessage, cartId);
26-
throw new CalloutException(errorMessage);
27-
}
28-
quantitiesFromSalesforce.put(cartItem.Sku, cartItem.Quantity);
19+
// Get all SKUs and their quantities from cart items.
20+
Map<String, Decimal> quantitiesFromSalesforce = new Map<String, Decimal>();
21+
for (CartItem cartItem : [SELECT Sku, Quantity FROM CartItem WHERE CartId = :cartId AND Type = 'Product' WITH SECURITY_ENFORCED]) {
22+
if (String.isBlank(cartItem.Sku)) {
23+
String errorMessage = 'The SKUs for all products in your cart must be defined.';
24+
saveCartValidationOutputError(errorMessage, cartId);
25+
throw new CalloutException(errorMessage);
2926
}
27+
quantitiesFromSalesforce.put(cartItem.Sku, cartItem.Quantity);
28+
}
3029

31-
// Stop checkout if there are no items in the cart
32-
if (quantitiesFromSalesforce.isEmpty()) {
33-
String errorMessage = 'Looks like your cart is empty.';
30+
// Stop checkout if there are no items in the cart
31+
if (quantitiesFromSalesforce.isEmpty()) {
32+
String errorMessage = 'Looks like your cart is empty.';
33+
saveCartValidationOutputError(errorMessage, cartId);
34+
throw new CalloutException(errorMessage);
35+
}
36+
37+
// Get all available quantities for products in the cart (cart items) from an external service.
38+
Map<String, Object> quantitiesFromExternalService = getQuantitiesFromExternalService(cartId, quantitiesFromSalesforce.keySet());
39+
40+
// For each cart item SKU, check that the quantity from the external service
41+
// is greater or equal to the quantity in the cart.
42+
// If that is not true, set the integration status to "Failed".
43+
for (String sku : quantitiesFromSalesforce.keySet()) {
44+
Decimal quantityFromSalesforce = quantitiesFromSalesforce.get(sku);
45+
Decimal quantityFromExternalService = (Decimal)quantitiesFromExternalService.get(sku);
46+
if (quantityFromExternalService == null){
47+
String errorMessage = 'The product with sku ' + sku + ' could not be found in the external system';
3448
saveCartValidationOutputError(errorMessage, cartId);
35-
throw new CalloutException(errorMessage);
36-
}
37-
38-
// Get all available quantities for products in the cart (cart items) from an external service.
39-
Map<String, Object> quantitiesFromExternalService = getQuantitiesFromExternalService(cartId, quantitiesFromSalesforce.keySet());
40-
41-
// For each cart item SKU, check that the quantity from the external service
42-
// is greater or equal to the quantity in the cart.
43-
// If that is not true, set the integration status to "Failed".
44-
for (String sku : quantitiesFromSalesforce.keySet()) {
45-
Decimal quantityFromSalesforce = quantitiesFromSalesforce.get(sku);
46-
Decimal quantityFromExternalService = (Decimal)quantitiesFromExternalService.get(sku);
47-
if (quantityFromExternalService == null){
48-
String errorMessage = 'The product with sku ' + sku + ' could not be found in the external system';
49-
saveCartValidationOutputError(errorMessage, cartId);
50-
throw new CalloutException(errorMessage);
51-
}
52-
else if (quantityFromExternalService < quantityFromSalesforce){
53-
String errorMessage = 'Insufficient quantity for the product with sku ' + sku + ': '
54-
+ quantityFromSalesforce + ' needed, but only '
55-
+ quantityFromExternalService + ' available.';
56-
saveCartValidationOutputError(errorMessage, cartId);
57-
throw new CalloutException(errorMessage);
58-
}
49+
throw new CalloutException(errorMessage);
50+
}
51+
else if (quantityFromExternalService < quantityFromSalesforce){
52+
String errorMessage = 'Insufficient quantity for the product with sku ' + sku + ': '
53+
+ quantityFromSalesforce + ' needed, but only '
54+
+ quantityFromExternalService + ' available.';
55+
saveCartValidationOutputError(errorMessage, cartId);
56+
throw new CalloutException(errorMessage);
5957
}
60-
} catch (CalloutException e) {
61-
throw e;
62-
} catch (Exception e) {
63-
// For testing purposes, this example treats exceptions as user errors, which means they are displayed to the buyer user.
64-
// In production you probably want this to be an admin-type error. In that case, throw the exception here
65-
// and make sure that a notification system is in place to let the admin know that the error occurred.
66-
// See the readme section about error handling for details about how to create that notification.
67-
String errorMessage = 'An exception of type ' + e.getTypeName() + ' has occurred: ' + e.getMessage();
68-
saveCartValidationOutputError(errorMessage, cartId);
69-
throw new CalloutException(errorMessage);
7058
}
7159
}
7260

@@ -98,7 +86,7 @@ global with sharing class B2BSyncCheckInventory {
9886
}
9987
else {
10088
String errorMessage = 'There was a problem with the request. Error: ' + response.getStatusCode();
101-
saveCartValidationOutputError(errorMessage, cartId);
89+
// Sync non-user errors skip saveCartValidationOutputError
10290
throw new CalloutException(errorMessage);
10391
}
10492
}
@@ -109,7 +97,7 @@ global with sharing class B2BSyncCheckInventory {
10997
// CartId: Foreign key to the WebCart that this validation line is for
11098
// Level (required): One of the following - Info, Error, or Warning
11199
// Message (optional): Message displyed to the user
112-
// Name (required): The name of this CartValidationOutput record. For example CartId:BackgroundOperationId
100+
// Name (required): The name of this CartValidationOutput record. For example CartId
113101
// RelatedEntityId (required): Foreign key to WebCart, CartItem, CartDeliveryGroup
114102
// Type (required): One of the following - SystemError, Inventory, Taxes, Pricing, Shipping, Entitlement, Other
115103
CartValidationOutput cartValidationError = new CartValidationOutput(
@@ -123,4 +111,4 @@ global with sharing class B2BSyncCheckInventory {
123111

124112
insert(cartValidationError);
125113
}
126-
}
114+
}

examples/checkout-main/classes/B2BSyncCheckInventoryTest.cls

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public class B2BSyncCheckInventoryTest {
3434
Test.stopTest();
3535
}
3636

37-
@isTest static void testWhenExternalServiceCallFailsAFailedStatusIsReturnedAndACartValidationOutputEntryIsCreated() {
37+
@isTest static void testWhenExternalServiceCallFailsAFailedStatusIsReturnedAndACartValidationOutputEntryIsNotCreated() {
3838
// Because test methods do not support Web service callouts, we create a mock response based on a static resource.
3939
// To create the static resource from the Developer Console, select File | New | Static Resource
4040
StaticResourceCalloutMock mock = new StaticResourceCalloutMock();
@@ -51,7 +51,7 @@ public class B2BSyncCheckInventoryTest {
5151
List<Id> webCarts = new List<Id>{webCart.Id};
5252

5353
String expectedErrorMessage = 'There was a problem with the request. Error: 404';
54-
executeAndEnsureFailure(expectedErrorMessage, webCarts);
54+
executeAndEnsureFailure(expectedErrorMessage, webCarts, false);
5555

5656
Test.stopTest();
5757
}
@@ -68,7 +68,7 @@ public class B2BSyncCheckInventoryTest {
6868
List<Id> webCarts = new List<Id>{webCart.Id};
6969

7070
String expectedErrorMessage = 'Looks like your cart is empty.';
71-
executeAndEnsureFailure(expectedErrorMessage, webCarts);
71+
executeAndEnsureFailure(expectedErrorMessage, webCarts, true);
7272

7373
Test.stopTest();
7474

@@ -94,7 +94,7 @@ public class B2BSyncCheckInventoryTest {
9494
insert cartItemWithNoSku;
9595

9696
String expectedErrorMessage = 'The SKUs for all products in your cart must be defined.';
97-
executeAndEnsureFailure(expectedErrorMessage, webCarts);
97+
executeAndEnsureFailure(expectedErrorMessage, webCarts, true);
9898

9999
Test.stopTest();
100100

@@ -103,7 +103,7 @@ public class B2BSyncCheckInventoryTest {
103103
}
104104

105105
// Executes the check inventory check and ensures an error is correctly triggered
106-
static void executeAndEnsureFailure(String expectedErrorMessage, List<Id> webCarts) {
106+
static void executeAndEnsureFailure(String expectedErrorMessage, List<Id> webCarts, Boolean userError) {
107107
try {
108108
B2BSyncCheckInventory.syncCheckInventory(webCarts);
109109

@@ -115,8 +115,12 @@ public class B2BSyncCheckInventoryTest {
115115

116116
// A new CartValidationOutput record with level 'Error' was created.
117117
List<CartValidationOutput> cartValidationOutputs = [SELECT Id, Message FROM CartValidationOutput WHERE Level = 'Error'];
118-
System.assertEquals(1, cartValidationOutputs.size());
119-
System.assertEquals(expectedErrorMessage, cartValidationOutputs.get(0).Message);
118+
if (userError) {
119+
System.assertEquals(1, cartValidationOutputs.size());
120+
System.assertEquals(expectedErrorMessage, cartValidationOutputs.get(0).Message);
121+
} else {
122+
System.assertEquals(0, cartValidationOutputs.size());
123+
}
120124
}
121125

122126
// Inserts a cart item when we only know the cart id
@@ -144,4 +148,4 @@ public class B2BSyncCheckInventoryTest {
144148
CartItem cartItem = [SELECT Id FROM CartItem WHERE Name = 'TestProduct' LIMIT 1];
145149
delete cartItem;
146150
}
147-
}
151+
}

examples/checkout-main/classes/B2BSyncDelivery.cls

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ public class B2BSyncDelivery {
77
// Validate the input
88
if (cartIds == null || cartIds.size() != 1) {
99
String errorMessage = 'A cart id must be included to B2BSyncDelivery';
10-
saveCartValidationOutputError(errorMessage, '');
10+
// Sync non-user errors skip saveCartValidationOutputError
1111
throw new CalloutException (errorMessage);
1212
}
1313

@@ -42,14 +42,9 @@ public class B2BSyncDelivery {
4242
cartId);
4343
i+=1;
4444
}
45-
} catch (CalloutException e) {
46-
throw e;
47-
// For testing purposes, this example treats exceptions as user errors, which means they are displayed to the buyer user.
48-
// In production you probably want this to be an admin-type error. In that case, throw the exception here
49-
// and make sure that a notification system is in place to let the admin know that the error occurred.
50-
// See the readme section about error handling for details about how to create that notification.
5145
} catch (DmlException de) {
52-
// Catch any exceptions thrown when trying to insert the shipping charge to the CartItems
46+
// To aid debugging catch any exceptions thrown when trying to insert the shipping charge to the CartItems
47+
// In production you might want to hide these details from the buyer user.
5348
Integer numErrors = de.getNumDml();
5449
String errorMessage = 'There were ' + numErrors + ' errors when trying to insert the charge in the CartItem: ';
5550
for(Integer errorIdx = 0; errorIdx < numErrors; errorIdx++) {
@@ -58,10 +53,6 @@ public class B2BSyncDelivery {
5853
errorMessage += ' , ';
5954
}
6055

61-
saveCartValidationOutputError(errorMessage, cartId);
62-
throw new CalloutException (errorMessage);
63-
} catch(Exception e) {
64-
String errorMessage = 'An exception of type ' + e.getTypeName() + ' has occurred: ' + e.getMessage();
6556
saveCartValidationOutputError(errorMessage, cartId);
6657
throw new CalloutException (errorMessage);
6758
}
@@ -129,7 +120,7 @@ public class B2BSyncDelivery {
129120
}
130121
else {
131122
String errorMessage = 'There was a problem with the request. Error: ' + response.getStatusCode();
132-
saveCartValidationOutputError(errorMessage, cartId);
123+
// Sync non-user errors skip saveCartValidationOutputError
133124
throw new CalloutException (errorMessage);
134125
}
135126
}
@@ -201,7 +192,7 @@ public class B2BSyncDelivery {
201192
// CartId: Foreign key to the WebCart that this validation line is for
202193
// Level (required): One of the following - Info, Error, or Warning
203194
// Message (optional): Message displayed to the user
204-
// Name (required): The name of this CartValidationOutput record. For example CartId:BackgroundOperationId
195+
// Name (required): The name of this CartValidationOutput record. For example CartId
205196
// RelatedEntityId (required): Foreign key to WebCart, CartItem, CartDeliveryGroup
206197
// Type (required): One of the following - SystemError, Inventory, Taxes, Pricing, Shipping, Entitlement, Other
207198
CartValidationOutput cartValidationError = new CartValidationOutput(

examples/checkout-main/classes/B2BSyncDeliveryTest.cls

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ public class B2BSyncDeliveryTest {
4242
}
4343

4444

45-
@isTest static void testWhenExternalServiceCallFailsAFailedStatusIsReturnedAndACartValidationOutputEntryIsCreated() {
45+
@isTest static void testWhenExternalServiceCallFailsAFailedStatusIsReturnedAndACartValidationOutputEntryIsNotCreated() {
4646
// Because test methods do not support Web service callouts, we create a mock response based on a static resource.
4747
// To create the static resource from the the Developer Console, select File | New | Static Resource
4848
StaticResourceCalloutMock mock = new StaticResourceCalloutMock();
@@ -70,9 +70,8 @@ public class B2BSyncDeliveryTest {
7070

7171
// A new CartValidationOutput record with level 'Error' was created.
7272
List<CartValidationOutput> cartValidationOutputs = [SELECT Id, Message FROM CartValidationOutput WHERE Level = 'Error'];
73-
System.assertEquals(1, cartValidationOutputs.size());
74-
System.assertEquals(expectedErrorMessage, cartValidationOutputs.get(0).Message);
73+
System.assertEquals(0, cartValidationOutputs.size());
7574

7675
Test.stopTest();
7776
}
78-
}
77+
}

0 commit comments

Comments
 (0)