@faststore/api 4.3.1-dev.0 → 4.4.0-dev.0

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.
@@ -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('Invalid SkuId')
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
- products: [product],
114
- } = await search.products({
115
- page: 0,
116
- count: 1,
117
- query: `product:${route.id}`,
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
- let query = term
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 need to modify the search
171
- * we will be performing on our search engine. The idea in here
172
- * is to use the cross selling API for fetching the productIds our
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 products = await ctx.clients.commerce.catalog.products.crossselling(
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
- query = `product:${products
201
+ const productIds = crossSellingProducts
187
202
  .map((x) => x.productId)
188
203
  .slice(0, first)
189
- .join(';')}`
190
- }
191
204
 
192
- const after = maybeAfter ? Number(maybeAfter) : 0
193
- const searchArgs: Omit<SearchArgs, 'type'> = {
194
- page: Math.ceil(after / first) || 0,
195
- count: first,
196
- query: query ?? undefined,
197
- sort: SORT_MAP[sort ?? 'score_desc'],
198
- selectedFacets: selectedFacets?.flatMap(transformSelectedFacet) ?? [],
199
- sponsoredCount: sponsoredCount ?? undefined,
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 query = `id:${productIds.join(';')}`
256
- const products = await search.products({
257
- page: 0,
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.products
293
+ return products
263
294
  .flatMap((product) =>
264
295
  product.items.map((sku) => enhanceSku(sku, product))
265
296
  )