@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.
Files changed (33) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/dist/api.cjs.development.js +113 -34
  3. package/dist/api.cjs.development.js.map +1 -1
  4. package/dist/api.cjs.production.min.js +1 -1
  5. package/dist/api.cjs.production.min.js.map +1 -1
  6. package/dist/api.esm.js +113 -34
  7. package/dist/api.esm.js.map +1 -1
  8. package/dist/index.d.ts +5 -4
  9. package/dist/platforms/vtex/clients/commerce/types/Portal.d.ts +1 -1
  10. package/dist/platforms/vtex/index.d.ts +5 -4
  11. package/dist/platforms/vtex/loaders/index.d.ts +1 -1
  12. package/dist/platforms/vtex/loaders/sku.d.ts +1 -2
  13. package/dist/platforms/vtex/resolvers/mutation.d.ts +3 -3
  14. package/dist/platforms/vtex/resolvers/offer.d.ts +1 -1
  15. package/dist/platforms/vtex/resolvers/seo.d.ts +1 -0
  16. package/dist/platforms/vtex/resolvers/validateCart.d.ts +3 -3
  17. package/dist/platforms/vtex/utils/canonical.d.ts +2 -0
  18. package/dist/platforms/vtex/utils/facets.d.ts +2 -0
  19. package/dist/platforms/vtex/utils/orderStatistics.d.ts +4 -0
  20. package/dist/platforms/vtex/utils/sku.d.ts +8 -0
  21. package/package.json +2 -2
  22. package/src/platforms/vtex/clients/commerce/index.ts +1 -1
  23. package/src/platforms/vtex/clients/commerce/types/Portal.ts +1 -1
  24. package/src/platforms/vtex/loaders/sku.ts +3 -16
  25. package/src/platforms/vtex/resolvers/offer.ts +6 -3
  26. package/src/platforms/vtex/resolvers/product.ts +6 -4
  27. package/src/platforms/vtex/resolvers/query.ts +44 -1
  28. package/src/platforms/vtex/resolvers/seo.ts +2 -2
  29. package/src/platforms/vtex/resolvers/validateCart.ts +3 -3
  30. package/src/platforms/vtex/utils/canonical.ts +3 -0
  31. package/src/platforms/vtex/utils/facets.ts +6 -0
  32. package/src/platforms/vtex/utils/orderStatistics.ts +16 -0
  33. 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 facetsList => {
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: async root => {
1051
+ itemOffered: root => {
1066
1052
  if (isSearchItem(root)) {
1067
1053
  return root.product;
1068
1054
  }
1069
1055
 
1070
1056
  if (isOrderFormItem(root)) {
1071
- return { ...(await root.product),
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.flatMap(seller => enhanceCommercialOffer({
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 findLocale = facets => {
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 === 'locale')) == null ? void 0 : _facets$find.value) != null ? _facets$find$value : null;
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 findChannel = facets => {
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(facet => facet.key === 'channel')) == null ? void 0 : _facets$find2.value) != null ? _facets$find$value2 : null;
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
- return skuLoader.load(locator);
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
- titleTemplate: () => '',
1586
- canonical: () => ''
1662
+ canonical: ({
1663
+ canonical
1664
+ }) => canonical != null ? canonical : '',
1665
+ titleTemplate: () => ''
1587
1666
  };
1588
1667
 
1589
1668
  const ObjectOrString = /*#__PURE__*/new graphql.GraphQLScalarType({