@faststore/api 4.3.1-dev.0 → 4.4.0-dev.1
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/dist/cjs/index.js +41 -41
- package/dist/cjs/index.js.map +1 -1
- package/dist/es/index.mjs +1705 -1429
- package/dist/es/index.mjs.map +1 -1
- package/dist/src/platforms/vtex/clients/index.d.ts +3 -1
- package/dist/src/platforms/vtex/clients/index.d.ts.map +1 -1
- package/dist/src/platforms/vtex/clients/search/index.d.ts +18 -3
- package/dist/src/platforms/vtex/clients/search/index.d.ts.map +1 -1
- package/dist/src/platforms/vtex/loaders/sku.d.ts.map +1 -1
- package/dist/src/platforms/vtex/resolvers/query.d.ts +2 -1
- package/dist/src/platforms/vtex/resolvers/query.d.ts.map +1 -1
- package/dist/src/platforms/vtex/utils/intelligentSearchRequest.d.ts +52 -0
- package/dist/src/platforms/vtex/utils/intelligentSearchRequest.d.ts.map +1 -0
- package/package.json +3 -3
- package/src/platforms/vtex/clients/search/index.ts +132 -205
- package/src/platforms/vtex/loaders/sku.ts +3 -4
- package/src/platforms/vtex/resolvers/query.ts +70 -39
- package/src/platforms/vtex/utils/intelligentSearchRequest.ts +642 -0
|
@@ -28,6 +28,7 @@ import {
|
|
|
28
28
|
import type { CategoryTree } from '../clients/commerce/types/CategoryTree'
|
|
29
29
|
import type { ProfileAddress } from '../clients/commerce/types/Profile'
|
|
30
30
|
import type { SearchArgs } from '../clients/search'
|
|
31
|
+
import type { ProductSearchResult } from '../clients/search/types/ProductSearchResult'
|
|
31
32
|
import type { GraphqlContext } from '../index'
|
|
32
33
|
import { extractRuleForAuthorization } from '../utils/commercialAuth'
|
|
33
34
|
import { mutateChannelContext, mutateLocaleContext } from '../utils/contex'
|
|
@@ -46,6 +47,16 @@ import { SORT_MAP } from '../utils/sort'
|
|
|
46
47
|
import { FACET_CROSS_SELLING_MAP } from './../utils/facets'
|
|
47
48
|
import { StoreCollection } from './collection'
|
|
48
49
|
|
|
50
|
+
const INVALID_SKU_ID_ERROR = 'Invalid SkuId'
|
|
51
|
+
const SLUG_MISMATCH_ERROR =
|
|
52
|
+
'Slug was set but the fetched sku does not satisfy the slug condition.'
|
|
53
|
+
|
|
54
|
+
const shouldFallbackToProductRoute = (error: unknown) =>
|
|
55
|
+
isNotFoundError(error) ||
|
|
56
|
+
(error instanceof Error &&
|
|
57
|
+
(error.message === INVALID_SKU_ID_ERROR ||
|
|
58
|
+
error.message.startsWith(SLUG_MISMATCH_ERROR)))
|
|
59
|
+
|
|
49
60
|
export const Query = {
|
|
50
61
|
product: async (
|
|
51
62
|
_: unknown,
|
|
@@ -75,7 +86,7 @@ export const Query = {
|
|
|
75
86
|
const skuId = id ?? slug?.split('-').pop() ?? ''
|
|
76
87
|
|
|
77
88
|
if (!isValidSkuId(skuId)) {
|
|
78
|
-
throw new Error(
|
|
89
|
+
throw new Error(INVALID_SKU_ID_ERROR)
|
|
79
90
|
}
|
|
80
91
|
|
|
81
92
|
const sku = await skuLoader.load(skuId)
|
|
@@ -99,6 +110,10 @@ export const Query = {
|
|
|
99
110
|
|
|
100
111
|
return sku
|
|
101
112
|
} catch (err) {
|
|
113
|
+
if (!shouldFallbackToProductRoute(err)) {
|
|
114
|
+
throw err
|
|
115
|
+
}
|
|
116
|
+
|
|
102
117
|
if (slug == null) {
|
|
103
118
|
throw new BadRequestError('Missing slug or id')
|
|
104
119
|
}
|
|
@@ -109,15 +124,12 @@ export const Query = {
|
|
|
109
124
|
throw new NotFoundError(`No product found for slug ${slug}`)
|
|
110
125
|
}
|
|
111
126
|
|
|
112
|
-
const
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
// Manually disabling this flag to prevent regionalization issues
|
|
119
|
-
hideUnavailableItems: false,
|
|
120
|
-
})
|
|
127
|
+
const product = await search
|
|
128
|
+
.fetchProduct({
|
|
129
|
+
field: 'id',
|
|
130
|
+
value: String(route.id),
|
|
131
|
+
})
|
|
132
|
+
.catch(() => null)
|
|
121
133
|
|
|
122
134
|
if (!product) {
|
|
123
135
|
throw new NotFoundError(`No product found for id ${route.id}`)
|
|
@@ -164,39 +176,60 @@ export const Query = {
|
|
|
164
176
|
mutateLocaleContext(ctx, locale)
|
|
165
177
|
}
|
|
166
178
|
|
|
167
|
-
|
|
179
|
+
const after = maybeAfter ? Number(maybeAfter) : 0
|
|
180
|
+
const searchArgs: Omit<SearchArgs, 'type'> = {
|
|
181
|
+
page: Math.ceil(after / first) || 0,
|
|
182
|
+
count: first,
|
|
183
|
+
query: term ?? undefined,
|
|
184
|
+
sort: SORT_MAP[sort ?? 'score_desc'] ?? SORT_MAP.score_desc,
|
|
185
|
+
selectedFacets: selectedFacets?.flatMap(transformSelectedFacet) ?? [],
|
|
186
|
+
sponsoredCount: sponsoredCount ?? undefined,
|
|
187
|
+
}
|
|
168
188
|
|
|
169
189
|
/**
|
|
170
|
-
* In case we are using crossSelling, we
|
|
171
|
-
*
|
|
172
|
-
*
|
|
173
|
-
* search will return for us.
|
|
174
|
-
* Doing this two request workflow makes it possible to have cross
|
|
175
|
-
* selling with Search features, like pagination, internationalization
|
|
176
|
-
* etc
|
|
190
|
+
* In case we are using crossSelling, we fetch product IDs from the
|
|
191
|
+
* cross selling API and then hydrate them using the PDP endpoint
|
|
192
|
+
* via productsByIdentifier.
|
|
177
193
|
*/
|
|
178
194
|
if (crossSelling) {
|
|
179
|
-
const
|
|
180
|
-
{
|
|
195
|
+
const crossSellingProducts =
|
|
196
|
+
await ctx.clients.commerce.catalog.products.crossselling({
|
|
181
197
|
type: FACET_CROSS_SELLING_MAP[crossSelling.key],
|
|
182
198
|
productId: crossSelling.value,
|
|
183
|
-
}
|
|
184
|
-
)
|
|
199
|
+
})
|
|
185
200
|
|
|
186
|
-
|
|
201
|
+
const productIds = crossSellingProducts
|
|
187
202
|
.map((x) => x.productId)
|
|
188
203
|
.slice(0, first)
|
|
189
|
-
.join(';')}`
|
|
190
|
-
}
|
|
191
204
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
205
|
+
const productSearchPromise: Promise<ProductSearchResult> =
|
|
206
|
+
ctx.clients.search
|
|
207
|
+
.productsByIdentifier({ field: 'id', values: productIds })
|
|
208
|
+
.then((products) => ({
|
|
209
|
+
products,
|
|
210
|
+
recordsFiltered: products.length,
|
|
211
|
+
pagination: {
|
|
212
|
+
count: products.length,
|
|
213
|
+
current: { index: 0, proxyURL: '' },
|
|
214
|
+
before: [],
|
|
215
|
+
after: [],
|
|
216
|
+
perPage: first,
|
|
217
|
+
next: { index: 0, proxyURL: '' },
|
|
218
|
+
previous: { index: 0, proxyURL: '' },
|
|
219
|
+
first: { index: 0, proxyURL: '' },
|
|
220
|
+
last: { index: 0, proxyURL: '' },
|
|
221
|
+
},
|
|
222
|
+
sampling: false,
|
|
223
|
+
options: { sorts: [], counts: [] },
|
|
224
|
+
translated: false,
|
|
225
|
+
locale: '',
|
|
226
|
+
query: '',
|
|
227
|
+
operator: 'and',
|
|
228
|
+
fuzzy: '0',
|
|
229
|
+
searchId: '',
|
|
230
|
+
}))
|
|
231
|
+
|
|
232
|
+
return { searchArgs, productSearchPromise }
|
|
200
233
|
}
|
|
201
234
|
|
|
202
235
|
const productSearchPromise = ctx.clients.search.products(searchArgs)
|
|
@@ -252,14 +285,12 @@ export const Query = {
|
|
|
252
285
|
return []
|
|
253
286
|
}
|
|
254
287
|
|
|
255
|
-
const
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
count: productIds.length,
|
|
259
|
-
query,
|
|
288
|
+
const products = await search.productsByIdentifier({
|
|
289
|
+
field: 'id',
|
|
290
|
+
values: productIds,
|
|
260
291
|
})
|
|
261
292
|
|
|
262
|
-
return products
|
|
293
|
+
return products
|
|
263
294
|
.flatMap((product) =>
|
|
264
295
|
product.items.map((sku) => enhanceSku(sku, product))
|
|
265
296
|
)
|