@faststore/api 1.10.4 → 1.10.17

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.
@@ -1,25 +1,27 @@
1
- import { NotFoundError, BadRequestError } from '../../errors'
2
- import { mutateChannelContext, mutateLocaleContext } from '../utils/contex'
3
- import { enhanceSku } from '../utils/enhanceSku'
1
+ import { FACET_CROSS_SELLING_MAP } from "./../utils/facets"
2
+ import { BadRequestError, NotFoundError } from "../../errors"
3
+ import { mutateChannelContext, mutateLocaleContext } from "../utils/contex"
4
+ import { enhanceSku } from "../utils/enhanceSku"
4
5
  import {
5
6
  findChannel,
7
+ findCrossSelling,
6
8
  findLocale,
7
9
  findSkuId,
8
10
  findSlug,
9
11
  transformSelectedFacet,
10
- } from '../utils/facets'
11
- import { SORT_MAP } from '../utils/sort'
12
- import { StoreCollection } from './collection'
12
+ } from "../utils/facets"
13
+ import { SORT_MAP } from "../utils/sort"
14
+ import { StoreCollection } from "./collection"
13
15
  import type {
14
- QueryProductArgs,
15
16
  QueryAllCollectionsArgs,
16
17
  QueryAllProductsArgs,
17
- QuerySearchArgs,
18
18
  QueryCollectionArgs,
19
- } from '../../../__generated__/schema'
20
- import type { CategoryTree } from '../clients/commerce/types/CategoryTree'
21
- import type { Context } from '../index'
22
- import { isValidSkuId, pickBestSku } from '../utils/sku'
19
+ QueryProductArgs,
20
+ QuerySearchArgs,
21
+ } from "../../../__generated__/schema"
22
+ import type { CategoryTree } from "../clients/commerce/types/CategoryTree"
23
+ import type { Context } from "../index"
24
+ import { isValidSkuId, pickBestSku } from "../utils/sku"
23
25
 
24
26
  export const Query = {
25
27
  product: async (_: unknown, { locator }: QueryProductArgs, ctx: Context) => {
@@ -95,6 +97,7 @@ export const Query = {
95
97
  // Insert channel in context for later usage
96
98
  const channel = findChannel(selectedFacets)
97
99
  const locale = findLocale(selectedFacets)
100
+ const crossSelling = findCrossSelling(selectedFacets)
98
101
 
99
102
  if (channel) {
100
103
  mutateChannelContext(ctx, channel)
@@ -104,11 +107,33 @@ export const Query = {
104
107
  mutateLocaleContext(ctx, locale)
105
108
  }
106
109
 
110
+ let query = term
111
+
112
+ /**
113
+ * In case we are using crossSelling, we need to modify the search
114
+ * we will be performing on our search engine. The idea in here
115
+ * is to use the cross selling API for fetching the productIds our
116
+ * search will return for us.
117
+ * Doing this two request workflow makes it possible to have cross
118
+ * selling with Search features, like pagination, internationalization
119
+ * etc
120
+ */
121
+ if (crossSelling) {
122
+ const products = await ctx.clients.commerce.catalog.products.crossselling({
123
+ type: FACET_CROSS_SELLING_MAP[crossSelling.key],
124
+ productId: crossSelling.value,
125
+ })
126
+
127
+ query = `product:${
128
+ products.map((x) => x.productId).slice(0, first).join(";")
129
+ }`
130
+ }
131
+
107
132
  const after = maybeAfter ? Number(maybeAfter) : 0
108
133
  const searchArgs = {
109
- page: Math.ceil(after / first!),
134
+ page: Math.ceil(after / first),
110
135
  count: first,
111
- query: term,
136
+ query,
112
137
  sort: SORT_MAP[sort ?? 'score_desc'],
113
138
  selectedFacets: selectedFacets?.flatMap(transformSelectedFacet) ?? [],
114
139
  }
@@ -1,11 +1,26 @@
1
1
  import ChannelMarshal from './channel'
2
2
  import type { Maybe } from '../../../__generated__/schema'
3
+ import { BadRequestError } from '../../errors'
3
4
 
4
5
  export interface SelectedFacet {
5
6
  key: string
6
7
  value: string
7
8
  }
8
9
 
10
+ export interface CrossSellingFacet {
11
+ key: keyof typeof FACET_CROSS_SELLING_MAP
12
+ value: string
13
+ }
14
+
15
+ export const FACET_CROSS_SELLING_MAP = {
16
+ buy: "whoboughtalsobought",
17
+ view: "whosawalsosaw",
18
+ similars: "similars",
19
+ viewAndBought: "whosawalsobought",
20
+ accessories: "accessories",
21
+ suggestions: "suggestions",
22
+ } as const
23
+
9
24
  /**
10
25
  * Transform facets from the store to VTEX platform facets.
11
26
  * For instance, the channel in Store becomes trade-policy and regionId in VTEX's realm
@@ -33,6 +48,15 @@ export const transformSelectedFacet = ({ key, value }: SelectedFacet) => {
33
48
  return { key, value: value.replace('-to-', ':') }
34
49
  }
35
50
 
51
+ case "buy":
52
+ case "view":
53
+ case "similars":
54
+ case "viewAndBought":
55
+ case "accessories":
56
+ case "suggestions": {
57
+ return [] // remove this facet from search
58
+ }
59
+
36
60
  default:
37
61
  return { key, value }
38
62
  }
@@ -52,6 +76,23 @@ export const parseRange = (range: string): [number, number] | null => {
52
76
  return splitted as [number, number]
53
77
  }
54
78
 
79
+ export const isCrossSelling = (
80
+ x: string,
81
+ ): x is CrossSellingFacet['key'] =>
82
+ typeof (FACET_CROSS_SELLING_MAP as Record<string, string>)[x] === "string"
83
+
84
+ export const findCrossSelling = (facets?: Maybe<SelectedFacet[]>) => {
85
+ const filtered = facets?.filter((x): x is CrossSellingFacet => isCrossSelling(x.key))
86
+
87
+ if (Array.isArray(filtered) && filtered.length > 1) {
88
+ throw new BadRequestError(
89
+ `You passed ${filtered.length} cross selling facets but only one is allowed. Please leave one of the following facet: ${filtered.map(x => x.key).join(',')}`
90
+ )
91
+ }
92
+
93
+ return filtered?.[0] ?? null
94
+ }
95
+
55
96
  export const findSlug = (facets?: Maybe<SelectedFacet[]>) =>
56
97
  facets?.find((x) => x.key === 'slug')?.value ?? null
57
98