@faststore/api 1.9.6 → 1.9.7
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 +11 -0
- package/dist/api.cjs.development.js +113 -34
- 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 +113 -34
- package/dist/api.esm.js.map +1 -1
- package/dist/index.d.ts +5 -4
- package/dist/platforms/vtex/clients/commerce/types/Portal.d.ts +1 -1
- package/dist/platforms/vtex/index.d.ts +5 -4
- package/dist/platforms/vtex/loaders/index.d.ts +1 -1
- package/dist/platforms/vtex/loaders/sku.d.ts +1 -2
- package/dist/platforms/vtex/resolvers/mutation.d.ts +3 -3
- package/dist/platforms/vtex/resolvers/offer.d.ts +1 -1
- package/dist/platforms/vtex/resolvers/seo.d.ts +1 -0
- package/dist/platforms/vtex/resolvers/validateCart.d.ts +3 -3
- package/dist/platforms/vtex/utils/canonical.d.ts +2 -0
- package/dist/platforms/vtex/utils/facets.d.ts +2 -0
- package/dist/platforms/vtex/utils/orderStatistics.d.ts +4 -0
- package/dist/platforms/vtex/utils/sku.d.ts +8 -0
- package/package.json +2 -2
- package/src/platforms/vtex/clients/commerce/index.ts +1 -1
- package/src/platforms/vtex/clients/commerce/types/Portal.ts +1 -1
- package/src/platforms/vtex/loaders/sku.ts +3 -16
- package/src/platforms/vtex/resolvers/offer.ts +6 -3
- package/src/platforms/vtex/resolvers/product.ts +6 -4
- package/src/platforms/vtex/resolvers/query.ts +44 -1
- package/src/platforms/vtex/resolvers/seo.ts +2 -2
- package/src/platforms/vtex/resolvers/validateCart.ts +3 -3
- package/src/platforms/vtex/utils/canonical.ts +3 -0
- package/src/platforms/vtex/utils/facets.ts +6 -0
- package/src/platforms/vtex/utils/orderStatistics.ts +16 -0
- package/src/platforms/vtex/utils/sku.ts +26 -0
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,17 @@
|
|
|
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.9.7](https://github.com/vtex/faststore/compare/v1.9.6...v1.9.7) (2022-06-13)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
* Canonical PDP slugs ([#1338](https://github.com/vtex/faststore/issues/1338)) ([ec807bb](https://github.com/vtex/faststore/commit/ec807bb94dafaa72cdec82d53933ca0809bf01df))
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
6
17
|
## [1.9.6](https://github.com/vtex/faststore/compare/v1.9.5...v1.9.6) (2022-06-13)
|
|
7
18
|
|
|
8
19
|
|
|
@@ -392,18 +392,7 @@ const isBadRequestError = error => {
|
|
|
392
392
|
};
|
|
393
393
|
|
|
394
394
|
const getSkuLoader = (_, clients) => {
|
|
395
|
-
const loader = async
|
|
396
|
-
const skuIds = facetsList.map(facets => {
|
|
397
|
-
const maybeFacet = facets.find(({
|
|
398
|
-
key
|
|
399
|
-
}) => key === 'id');
|
|
400
|
-
|
|
401
|
-
if (!maybeFacet) {
|
|
402
|
-
throw new BadRequestError('Error while loading SKU. Needs to pass an id to selected facets');
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
return maybeFacet.value;
|
|
406
|
-
});
|
|
395
|
+
const loader = async skuIds => {
|
|
407
396
|
const {
|
|
408
397
|
products
|
|
409
398
|
} = await clients.search.products({
|
|
@@ -746,15 +735,12 @@ const equals = (storeOrder, orderForm) => {
|
|
|
746
735
|
return isSameOrder && orderItemsAreSync;
|
|
747
736
|
};
|
|
748
737
|
|
|
749
|
-
const orderFormToCart = (form, skuLoader) => {
|
|
738
|
+
const orderFormToCart = async (form, skuLoader) => {
|
|
750
739
|
return {
|
|
751
740
|
order: {
|
|
752
741
|
orderNumber: form.orderFormId,
|
|
753
|
-
acceptedOffer: form.items.map(item => ({ ...item,
|
|
754
|
-
product: skuLoader.load(
|
|
755
|
-
key: 'id',
|
|
756
|
-
value: item.id
|
|
757
|
-
}])
|
|
742
|
+
acceptedOffer: form.items.map(async item => ({ ...item,
|
|
743
|
+
product: await skuLoader.load(item.id)
|
|
758
744
|
}))
|
|
759
745
|
},
|
|
760
746
|
messages: form.messages.map(({
|
|
@@ -1062,13 +1048,13 @@ const StoreOffer = {
|
|
|
1062
1048
|
|
|
1063
1049
|
return null;
|
|
1064
1050
|
},
|
|
1065
|
-
itemOffered:
|
|
1051
|
+
itemOffered: root => {
|
|
1066
1052
|
if (isSearchItem(root)) {
|
|
1067
1053
|
return root.product;
|
|
1068
1054
|
}
|
|
1069
1055
|
|
|
1070
1056
|
if (isOrderFormItem(root)) {
|
|
1071
|
-
return { ...
|
|
1057
|
+
return { ...root.product,
|
|
1072
1058
|
attachmentsValues: root.attachments
|
|
1073
1059
|
};
|
|
1074
1060
|
}
|
|
@@ -1090,6 +1076,10 @@ const StoreOffer = {
|
|
|
1090
1076
|
}
|
|
1091
1077
|
};
|
|
1092
1078
|
|
|
1079
|
+
const canonicalFromProduct = ({
|
|
1080
|
+
linkText
|
|
1081
|
+
}) => `/${linkText}/p`;
|
|
1082
|
+
|
|
1093
1083
|
const enhanceCommercialOffer = ({
|
|
1094
1084
|
offer,
|
|
1095
1085
|
seller,
|
|
@@ -1130,13 +1120,11 @@ const StoreProduct = {
|
|
|
1130
1120
|
}
|
|
1131
1121
|
}) => description,
|
|
1132
1122
|
seo: ({
|
|
1133
|
-
isVariantOf
|
|
1134
|
-
description,
|
|
1135
|
-
productName
|
|
1136
|
-
}
|
|
1123
|
+
isVariantOf
|
|
1137
1124
|
}) => ({
|
|
1138
|
-
title: productName,
|
|
1139
|
-
description
|
|
1125
|
+
title: isVariantOf.productName,
|
|
1126
|
+
description: isVariantOf.description,
|
|
1127
|
+
canonical: canonicalFromProduct(isVariantOf)
|
|
1140
1128
|
}),
|
|
1141
1129
|
brand: ({
|
|
1142
1130
|
isVariantOf: {
|
|
@@ -1196,7 +1184,7 @@ const StoreProduct = {
|
|
|
1196
1184
|
},
|
|
1197
1185
|
review: () => [],
|
|
1198
1186
|
aggregateRating: () => ({}),
|
|
1199
|
-
offers: root => root.sellers.
|
|
1187
|
+
offers: root => root.sellers.map(seller => enhanceCommercialOffer({
|
|
1200
1188
|
offer: seller.commertialOffer,
|
|
1201
1189
|
seller,
|
|
1202
1190
|
product: root
|
|
@@ -1294,15 +1282,25 @@ const transformSelectedFacet = ({
|
|
|
1294
1282
|
};
|
|
1295
1283
|
}
|
|
1296
1284
|
};
|
|
1297
|
-
const
|
|
1285
|
+
const findSlug = facets => {
|
|
1298
1286
|
var _facets$find$value, _facets$find;
|
|
1299
1287
|
|
|
1300
|
-
return (_facets$find$value = facets == null ? void 0 : (_facets$find = facets.find(x => x.key === '
|
|
1288
|
+
return (_facets$find$value = facets == null ? void 0 : (_facets$find = facets.find(x => x.key === 'slug')) == null ? void 0 : _facets$find.value) != null ? _facets$find$value : null;
|
|
1301
1289
|
};
|
|
1302
|
-
const
|
|
1290
|
+
const findSkuId = facets => {
|
|
1303
1291
|
var _facets$find$value2, _facets$find2;
|
|
1304
1292
|
|
|
1305
|
-
return (_facets$find$value2 = facets == null ? void 0 : (_facets$find2 = facets.find(
|
|
1293
|
+
return (_facets$find$value2 = facets == null ? void 0 : (_facets$find2 = facets.find(x => x.key === 'id')) == null ? void 0 : _facets$find2.value) != null ? _facets$find$value2 : null;
|
|
1294
|
+
};
|
|
1295
|
+
const findLocale = facets => {
|
|
1296
|
+
var _facets$find$value3, _facets$find3;
|
|
1297
|
+
|
|
1298
|
+
return (_facets$find$value3 = facets == null ? void 0 : (_facets$find3 = facets.find(x => x.key === 'locale')) == null ? void 0 : _facets$find3.value) != null ? _facets$find$value3 : null;
|
|
1299
|
+
};
|
|
1300
|
+
const findChannel = facets => {
|
|
1301
|
+
var _facets$find$value4, _facets$find4;
|
|
1302
|
+
|
|
1303
|
+
return (_facets$find$value4 = facets == null ? void 0 : (_facets$find4 = facets.find(facet => facet.key === 'channel')) == null ? void 0 : _facets$find4.value) != null ? _facets$find$value4 : null;
|
|
1306
1304
|
};
|
|
1307
1305
|
|
|
1308
1306
|
const SORT_MAP = {
|
|
@@ -1316,6 +1314,42 @@ const SORT_MAP = {
|
|
|
1316
1314
|
score_desc: ''
|
|
1317
1315
|
};
|
|
1318
1316
|
|
|
1317
|
+
/**
|
|
1318
|
+
* More info at: https://en.wikipedia.org/wiki/Order_statistic
|
|
1319
|
+
*/
|
|
1320
|
+
// O(n) search to find the max of an array
|
|
1321
|
+
const min = (array, cmp) => {
|
|
1322
|
+
let best = 0;
|
|
1323
|
+
|
|
1324
|
+
for (let curr = 1; curr < array.length; curr++) {
|
|
1325
|
+
if (cmp(array[best], array[curr]) > 0) {
|
|
1326
|
+
best = curr;
|
|
1327
|
+
}
|
|
1328
|
+
}
|
|
1329
|
+
|
|
1330
|
+
return array[best];
|
|
1331
|
+
};
|
|
1332
|
+
|
|
1333
|
+
/**
|
|
1334
|
+
* This function implements Portal heuristics for returning the best sku for a product.
|
|
1335
|
+
*
|
|
1336
|
+
* The best sku is the one with the best (cheapest available) offer
|
|
1337
|
+
* */
|
|
1338
|
+
|
|
1339
|
+
const pickBestSku = skus => {
|
|
1340
|
+
const offersBySku = skus.flatMap(sku => sku.sellers.map(seller => ({
|
|
1341
|
+
offer: seller.commertialOffer,
|
|
1342
|
+
sku
|
|
1343
|
+
})));
|
|
1344
|
+
const best = min(offersBySku, ({
|
|
1345
|
+
offer: o1
|
|
1346
|
+
}, {
|
|
1347
|
+
offer: o2
|
|
1348
|
+
}) => bestOfferFirst(o1, o2));
|
|
1349
|
+
return best.sku;
|
|
1350
|
+
};
|
|
1351
|
+
const isValidSkuId = skuId => skuId !== '' && !Number.isNaN(Number(skuId));
|
|
1352
|
+
|
|
1319
1353
|
const Query = {
|
|
1320
1354
|
product: async (_, {
|
|
1321
1355
|
locator
|
|
@@ -1323,6 +1357,8 @@ const Query = {
|
|
|
1323
1357
|
// Insert channel in context for later usage
|
|
1324
1358
|
const channel = findChannel(locator);
|
|
1325
1359
|
const locale = findLocale(locator);
|
|
1360
|
+
const id = findSkuId(locator);
|
|
1361
|
+
const slug = findSlug(locator);
|
|
1326
1362
|
|
|
1327
1363
|
if (channel) {
|
|
1328
1364
|
mutateChannelContext(ctx, channel);
|
|
@@ -1335,9 +1371,50 @@ const Query = {
|
|
|
1335
1371
|
const {
|
|
1336
1372
|
loaders: {
|
|
1337
1373
|
skuLoader
|
|
1374
|
+
},
|
|
1375
|
+
clients: {
|
|
1376
|
+
commerce,
|
|
1377
|
+
search
|
|
1338
1378
|
}
|
|
1339
1379
|
} = ctx;
|
|
1340
|
-
|
|
1380
|
+
|
|
1381
|
+
try {
|
|
1382
|
+
var _ref;
|
|
1383
|
+
|
|
1384
|
+
const skuId = (_ref = id != null ? id : slug == null ? void 0 : slug.split('-').pop()) != null ? _ref : '';
|
|
1385
|
+
|
|
1386
|
+
if (!isValidSkuId(skuId)) {
|
|
1387
|
+
throw new Error('Invalid SkuId');
|
|
1388
|
+
}
|
|
1389
|
+
|
|
1390
|
+
const sku = await skuLoader.load(skuId);
|
|
1391
|
+
return sku;
|
|
1392
|
+
} catch (err) {
|
|
1393
|
+
if (slug == null) {
|
|
1394
|
+
throw new BadRequestError('Missing slug or id');
|
|
1395
|
+
}
|
|
1396
|
+
|
|
1397
|
+
const route = await commerce.catalog.portal.pagetype(`${slug}/p`);
|
|
1398
|
+
|
|
1399
|
+
if (route.pageType !== 'Product' || !route.id) {
|
|
1400
|
+
throw new NotFoundError(`No product found for slug ${slug}`);
|
|
1401
|
+
}
|
|
1402
|
+
|
|
1403
|
+
const {
|
|
1404
|
+
products: [product]
|
|
1405
|
+
} = await search.products({
|
|
1406
|
+
page: 0,
|
|
1407
|
+
count: 1,
|
|
1408
|
+
query: `product:${route.id}`
|
|
1409
|
+
});
|
|
1410
|
+
|
|
1411
|
+
if (!product) {
|
|
1412
|
+
throw new NotFoundError(`No product found for id ${route.id}`);
|
|
1413
|
+
}
|
|
1414
|
+
|
|
1415
|
+
const sku = pickBestSku(product.items);
|
|
1416
|
+
return enhanceSku(sku, product);
|
|
1417
|
+
}
|
|
1341
1418
|
},
|
|
1342
1419
|
collection: (_, {
|
|
1343
1420
|
slug
|
|
@@ -1582,8 +1659,10 @@ const StoreSeo = {
|
|
|
1582
1659
|
description: ({
|
|
1583
1660
|
description
|
|
1584
1661
|
}) => description != null ? description : '',
|
|
1585
|
-
|
|
1586
|
-
|
|
1662
|
+
canonical: ({
|
|
1663
|
+
canonical
|
|
1664
|
+
}) => canonical != null ? canonical : '',
|
|
1665
|
+
titleTemplate: () => ''
|
|
1587
1666
|
};
|
|
1588
1667
|
|
|
1589
1668
|
const ObjectOrString = /*#__PURE__*/new graphql.GraphQLScalarType({
|