@faststore/api 1.8.20 → 1.8.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +27 -0
- package/dist/api.cjs.development.js +102 -23
- package/dist/api.cjs.development.js.map +1 -1
- package/dist/api.cjs.production.min.js +1 -1
- package/dist/api.cjs.production.min.js.map +1 -1
- package/dist/api.esm.js +102 -23
- package/dist/api.esm.js.map +1 -1
- package/dist/platforms/vtex/clients/commerce/index.d.ts +6 -0
- package/dist/platforms/vtex/clients/commerce/types/OrderForm.d.ts +8 -1
- package/dist/platforms/vtex/clients/index.d.ts +6 -0
- package/dist/platforms/vtex/index.d.ts +6 -0
- package/dist/platforms/vtex/utils/md5.d.ts +1 -0
- package/package.json +2 -2
- package/src/platforms/vtex/clients/commerce/index.ts +20 -0
- package/src/platforms/vtex/clients/commerce/types/OrderForm.ts +9 -1
- package/src/platforms/vtex/index.ts +7 -0
- package/src/platforms/vtex/resolvers/validateCart.ts +93 -20
- package/src/platforms/vtex/utils/md5.ts +4 -0
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,33 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
## 1.8.23 (2022-05-17)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
* Sync Cart with Checkout ([#1299](https://github.com/vtex/faststore/issues/1299)) ([62e8f56](https://github.com/vtex/faststore/commit/62e8f56b31c6bb6cf7260749a90ebef8aba9982b))
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
## 1.8.22 (2022-05-17)
|
|
18
|
+
|
|
19
|
+
**Note:** Version bump only for package @faststore/api
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
## 1.8.21 (2022-05-16)
|
|
26
|
+
|
|
27
|
+
**Note:** Version bump only for package @faststore/api
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
|
|
6
33
|
## 1.8.20 (2022-05-16)
|
|
7
34
|
|
|
8
35
|
**Note:** Version bump only for package @faststore/api
|
|
@@ -9,6 +9,7 @@ var fetch = _interopDefault(require('isomorphic-unfetch'));
|
|
|
9
9
|
var DataLoader = _interopDefault(require('dataloader'));
|
|
10
10
|
var pLimit = _interopDefault(require('p-limit'));
|
|
11
11
|
var deepEquals = _interopDefault(require('fast-deep-equal'));
|
|
12
|
+
var crypto = _interopDefault(require('crypto'));
|
|
12
13
|
var graphql = require('graphql');
|
|
13
14
|
|
|
14
15
|
const fetchAPI = async (info, init) => {
|
|
@@ -88,6 +89,19 @@ const VtexCommerce = ({
|
|
|
88
89
|
method: 'PATCH'
|
|
89
90
|
});
|
|
90
91
|
},
|
|
92
|
+
setCustomData: ({
|
|
93
|
+
id,
|
|
94
|
+
appId,
|
|
95
|
+
key,
|
|
96
|
+
value
|
|
97
|
+
}) => {
|
|
98
|
+
return fetchAPI(`${base}/api/checkout/pub/orderForm/${id}/customData/${appId}/${key}`, { ...BASE_INIT,
|
|
99
|
+
body: JSON.stringify({
|
|
100
|
+
value
|
|
101
|
+
}),
|
|
102
|
+
method: 'PUT'
|
|
103
|
+
});
|
|
104
|
+
},
|
|
91
105
|
region: async ({
|
|
92
106
|
postalCode,
|
|
93
107
|
country,
|
|
@@ -616,6 +630,8 @@ const StoreFacetValue = {
|
|
|
616
630
|
}) => quantity
|
|
617
631
|
};
|
|
618
632
|
|
|
633
|
+
const md5 = payload => crypto.createHash('md5').update(payload).digest('hex');
|
|
634
|
+
|
|
619
635
|
const getId = item => [item.itemOffered.sku, item.seller.identifier, item.price].join('::');
|
|
620
636
|
|
|
621
637
|
const orderFormItemToOffer = (item, index) => ({
|
|
@@ -662,6 +678,66 @@ const equals = (storeOrder, orderForm) => {
|
|
|
662
678
|
const orderItemsAreSync = deepEquals(orderFormItems, storeOrderItems);
|
|
663
679
|
return isSameOrder && orderItemsAreSync;
|
|
664
680
|
};
|
|
681
|
+
|
|
682
|
+
const orderFormToCart = (form, skuLoader) => {
|
|
683
|
+
return {
|
|
684
|
+
order: {
|
|
685
|
+
orderNumber: form.orderFormId,
|
|
686
|
+
acceptedOffer: form.items.map(item => ({ ...item,
|
|
687
|
+
product: skuLoader.load([{
|
|
688
|
+
key: 'id',
|
|
689
|
+
value: item.id
|
|
690
|
+
}])
|
|
691
|
+
}))
|
|
692
|
+
},
|
|
693
|
+
messages: form.messages.map(({
|
|
694
|
+
text,
|
|
695
|
+
status
|
|
696
|
+
}) => ({
|
|
697
|
+
text,
|
|
698
|
+
status: status.toUpperCase()
|
|
699
|
+
}))
|
|
700
|
+
};
|
|
701
|
+
};
|
|
702
|
+
|
|
703
|
+
const getOrderFormEtag = ({
|
|
704
|
+
items
|
|
705
|
+
}) => md5(JSON.stringify(items));
|
|
706
|
+
|
|
707
|
+
const setOrderFormEtag = async (form, commerce) => {
|
|
708
|
+
try {
|
|
709
|
+
const orderForm = await commerce.checkout.setCustomData({
|
|
710
|
+
id: form.orderFormId,
|
|
711
|
+
appId: 'faststore',
|
|
712
|
+
key: 'cartEtag',
|
|
713
|
+
value: getOrderFormEtag(form)
|
|
714
|
+
});
|
|
715
|
+
return orderForm;
|
|
716
|
+
} catch (err) {
|
|
717
|
+
console.error('Error while setting custom data to orderForm.\n Make sure to add the following custom app to the orderForm: \n{"fields":["cartEtag"],"id":"faststore","major":1}.\n More info at: https://developers.vtex.com/vtex-rest-api/docs/customizable-fields-with-checkout-api');
|
|
718
|
+
throw err;
|
|
719
|
+
}
|
|
720
|
+
};
|
|
721
|
+
/**
|
|
722
|
+
* Checks if cartEtag stored on customData is up to date
|
|
723
|
+
* @description If cartEtag is not up to date, this means that
|
|
724
|
+
* another system changed the cart, like Checkout UI or Order Placed
|
|
725
|
+
*/
|
|
726
|
+
|
|
727
|
+
|
|
728
|
+
const isOrderFormStale = form => {
|
|
729
|
+
var _form$customData, _faststoreData$fields;
|
|
730
|
+
|
|
731
|
+
const faststoreData = (_form$customData = form.customData) == null ? void 0 : _form$customData.customApps.find(app => app.id === 'faststore');
|
|
732
|
+
const oldEtag = faststoreData == null ? void 0 : (_faststoreData$fields = faststoreData.fields) == null ? void 0 : _faststoreData$fields.cartEtag;
|
|
733
|
+
|
|
734
|
+
if (oldEtag == null) {
|
|
735
|
+
return true;
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
const newEtag = getOrderFormEtag(form);
|
|
739
|
+
return newEtag !== oldEtag;
|
|
740
|
+
};
|
|
665
741
|
/**
|
|
666
742
|
* This resolver implements the optimistic cart behavior. The main idea in here
|
|
667
743
|
* is that we receive a cart from the UI (as query params) and we validate it with
|
|
@@ -682,6 +758,9 @@ const validateCart = async (_, {
|
|
|
682
758
|
order
|
|
683
759
|
}
|
|
684
760
|
}, ctx) => {
|
|
761
|
+
const {
|
|
762
|
+
enableOrderFormSync
|
|
763
|
+
} = ctx.storage.flags;
|
|
685
764
|
const {
|
|
686
765
|
orderNumber,
|
|
687
766
|
acceptedOffer
|
|
@@ -697,7 +776,19 @@ const validateCart = async (_, {
|
|
|
697
776
|
|
|
698
777
|
const orderForm = await commerce.checkout.orderForm({
|
|
699
778
|
id: orderNumber
|
|
700
|
-
}); //
|
|
779
|
+
}); // Step1.5: Check if another system changed the orderForm with this orderNumber
|
|
780
|
+
// If so, this means the user interacted with this cart elsewhere and expects
|
|
781
|
+
// to see this new cart state instead of what's stored on the user's browser.
|
|
782
|
+
|
|
783
|
+
if (enableOrderFormSync === true) {
|
|
784
|
+
const isStale = isOrderFormStale(orderForm);
|
|
785
|
+
|
|
786
|
+
if (isStale === true && orderNumber) {
|
|
787
|
+
const newOrderForm = await setOrderFormEtag(orderForm, commerce);
|
|
788
|
+
return orderFormToCart(newOrderForm, skuLoader);
|
|
789
|
+
}
|
|
790
|
+
} // Step2: Process items from both browser and checkout so they have the same shape
|
|
791
|
+
|
|
701
792
|
|
|
702
793
|
const browserItemsById = groupById(acceptedOffer);
|
|
703
794
|
const originItemsById = groupById(orderForm.items.map(orderFormItemToOffer));
|
|
@@ -735,34 +826,19 @@ const validateCart = async (_, {
|
|
|
735
826
|
} // Step4: Apply delta changes to order form
|
|
736
827
|
|
|
737
828
|
|
|
738
|
-
const updatedOrderForm = await commerce.checkout
|
|
829
|
+
const updatedOrderForm = await commerce.checkout // update orderForm items
|
|
830
|
+
.updateOrderFormItems({
|
|
739
831
|
id: orderForm.orderFormId,
|
|
740
832
|
orderItems: changes
|
|
741
|
-
})
|
|
833
|
+
}) // update orderForm etag so we know last time we touched this orderForm
|
|
834
|
+
.then(form => enableOrderFormSync ? setOrderFormEtag(form, commerce) : form); // Step5: If no changes detected before/after updating orderForm, the order is validated
|
|
742
835
|
|
|
743
836
|
if (equals(order, updatedOrderForm)) {
|
|
744
837
|
return null;
|
|
745
|
-
} // Step6: There were changes, convert orderForm to
|
|
838
|
+
} // Step6: There were changes, convert orderForm to StoreCart
|
|
746
839
|
|
|
747
840
|
|
|
748
|
-
return
|
|
749
|
-
order: {
|
|
750
|
-
orderNumber: updatedOrderForm.orderFormId,
|
|
751
|
-
acceptedOffer: updatedOrderForm.items.map(item => ({ ...item,
|
|
752
|
-
product: skuLoader.load([{
|
|
753
|
-
key: 'id',
|
|
754
|
-
value: item.id
|
|
755
|
-
}])
|
|
756
|
-
}))
|
|
757
|
-
},
|
|
758
|
-
messages: updatedOrderForm.messages.map(({
|
|
759
|
-
text,
|
|
760
|
-
status
|
|
761
|
-
}) => ({
|
|
762
|
-
text,
|
|
763
|
-
status: status.toUpperCase()
|
|
764
|
-
}))
|
|
765
|
-
};
|
|
841
|
+
return orderFormToCart(updatedOrderForm, skuLoader);
|
|
766
842
|
};
|
|
767
843
|
|
|
768
844
|
class ChannelMarshal {
|
|
@@ -1403,8 +1479,11 @@ const Resolvers = {
|
|
|
1403
1479
|
Mutation
|
|
1404
1480
|
};
|
|
1405
1481
|
const getContextFactory = options => ctx => {
|
|
1482
|
+
var _options$flags;
|
|
1483
|
+
|
|
1406
1484
|
ctx.storage = {
|
|
1407
|
-
channel: ChannelMarshal.parse(options.channel)
|
|
1485
|
+
channel: ChannelMarshal.parse(options.channel),
|
|
1486
|
+
flags: (_options$flags = options.flags) != null ? _options$flags : {}
|
|
1408
1487
|
};
|
|
1409
1488
|
ctx.clients = getClients(options, ctx);
|
|
1410
1489
|
ctx.loaders = getLoaders(options, ctx);
|