Skip to content

Commit 3c263b9

Browse files
committed
Extract the productSearch and facets o a service
1 parent 61220d2 commit 3c263b9

File tree

6 files changed

+532
-56
lines changed

6 files changed

+532
-56
lines changed

node/mocks/contextFactory.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@ export function createContext<Ctx = Context>({
5757
apps: {
5858
getAppSettings: jest.fn().mockReturnValue(appSettings ?? {}),
5959
},
60+
vbase: {
61+
getJSON: jest.fn().mockResolvedValue({}),
62+
}
6063
},
6164
vtex: {
6265
production: production ?? false,

node/resolvers/search/index.ts

Lines changed: 5 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ import {
4747
fetchCorrection,
4848
} from '../../services/autocomplete'
4949
import { fetchBanners } from '../../services/banners'
50+
import { fetchFacets } from '../../services/facets'
51+
import { fetchProductSearch } from '../../services/productSearch'
5052
import { AdvertisementOptions, FacetsInput, ProductSearchInput, ProductsInput, SuggestionProductsArgs } from '../../typings/Search'
5153

5254
enum CrossSellingInput {
@@ -347,28 +349,7 @@ export const queries = {
347349

348350
let { selectedFacets } = args
349351

350-
const {
351-
clients: { intelligentSearchApi },
352-
} = ctx
353-
354-
const biggyArgs: { [key: string]: any } = {
355-
...args,
356-
}
357-
358-
// unnecessary field. It's is an object and breaks the @vtex/api cache
359-
delete biggyArgs.selectedFacets
360-
361-
const result = await intelligentSearchApi.facets(
362-
{ ...biggyArgs, query: args.fullText },
363-
buildAttributePath(selectedFacets),
364-
shippingOptions
365-
)
366-
367-
if (ctx.vtex.tenant) {
368-
ctx.translated = result.translated
369-
}
370-
371-
return result
352+
return fetchFacets(ctx, args, selectedFacets, shippingOptions)
372353
},
373354

374355
product: async (_: any, rawArgs: ProductArgs, ctx: Context) => {
@@ -464,41 +445,9 @@ export const queries = {
464445
})
465446
}
466447

467-
const { intelligentSearchApi } = ctx.clients
468-
const {
469-
selectedFacets,
470-
fullText,
471-
advertisementOptions = defaultAdvertisementOptions,
472-
} = args
473-
474-
const workspaceSearchParams = await getWorkspaceSearchParamsFromStorage(ctx)
448+
const { selectedFacets } = args
475449

476-
const biggyArgs: { [key: string]: any } = {
477-
...advertisementOptions,
478-
...args,
479-
query: fullText,
480-
sort: convertOrderBy(args.orderBy),
481-
...args.options,
482-
...workspaceSearchParams,
483-
}
484-
485-
// unnecessary field. It's is an object and breaks the @vtex/api cache
486-
delete biggyArgs.selectedFacets
487-
488-
const result = await intelligentSearchApi.productSearch(
489-
{ ...biggyArgs },
490-
buildAttributePath(selectedFacets),
491-
shippingOptions
492-
)
493-
494-
if (ctx.vtex.tenant && !args.productOriginVtex) {
495-
ctx.translated = result.translated
496-
}
497-
498-
return {
499-
searchState: args.searchState,
500-
...result,
501-
}
450+
return fetchProductSearch(ctx, args, selectedFacets, shippingOptions)
502451
},
503452

504453
sponsoredProducts: async (_: any, args: ProductSearchInput, ctx: any) => {

node/services/facets.test.ts

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
import { fetchFacets } from './facets'
2+
import { createContext } from '../mocks/contextFactory'
3+
import type { FacetsInput } from '../typings/Search'
4+
5+
describe('fetchFacets service', () => {
6+
const mockFacetsResponse = {
7+
facets: [
8+
{
9+
name: 'Category',
10+
values: [
11+
{ name: 'Electronics', quantity: 10 },
12+
{ name: 'Clothing', quantity: 5 },
13+
],
14+
},
15+
],
16+
translated: false,
17+
}
18+
19+
const mockArgs: FacetsInput = {
20+
fullText: 'test query',
21+
query: 'test',
22+
map: 'ft',
23+
selectedFacets: [],
24+
removeHiddenFacets: false,
25+
hideUnavailableItems: false,
26+
categoryTreeBehavior: 'default',
27+
}
28+
29+
const mockSelectedFacets: SelectedFacet[] = []
30+
31+
beforeEach(() => {
32+
jest.clearAllMocks()
33+
})
34+
35+
it('should use intelligentSearchApi when shouldUseNewPLPEndpoint is false', async () => {
36+
const ctx = createContext({
37+
accountName: 'testaccount',
38+
appSettings: {
39+
shouldUseNewPLPEndpoint: false,
40+
},
41+
intelligentSearchApiSettings: {
42+
facets: mockFacetsResponse,
43+
},
44+
})
45+
46+
const result = await fetchFacets(ctx, mockArgs, mockSelectedFacets)
47+
48+
expect(ctx.clients.intelligentSearchApi.facets).toHaveBeenCalled()
49+
expect(ctx.clients.intsch.facets).not.toHaveBeenCalled()
50+
expect(result).toEqual(mockFacetsResponse)
51+
})
52+
53+
it('should use intsch when shouldUseNewPLPEndpoint is true', async () => {
54+
const ctx = createContext({
55+
accountName: 'testaccount',
56+
appSettings: {
57+
shouldUseNewPLPEndpoint: true,
58+
},
59+
intschSettings: {
60+
facets: mockFacetsResponse,
61+
},
62+
})
63+
64+
const result = await fetchFacets(ctx, mockArgs, mockSelectedFacets)
65+
66+
expect(ctx.clients.intsch.facets).toHaveBeenCalled()
67+
expect(ctx.clients.intelligentSearchApi.facets).not.toHaveBeenCalled()
68+
expect(result).toEqual(mockFacetsResponse)
69+
})
70+
71+
it('should handle shipping options correctly', async () => {
72+
const ctx = createContext({
73+
accountName: 'testaccount',
74+
appSettings: {
75+
shouldUseNewPLPEndpoint: false,
76+
},
77+
intelligentSearchApiSettings: {
78+
facets: mockFacetsResponse,
79+
},
80+
})
81+
82+
const shippingOptions = ['delivery', 'pickup']
83+
84+
await fetchFacets(ctx, mockArgs, mockSelectedFacets, shippingOptions)
85+
86+
expect(ctx.clients.intelligentSearchApi.facets).toHaveBeenCalledWith(
87+
expect.objectContaining({ query: 'test query' }),
88+
expect.any(String),
89+
shippingOptions
90+
)
91+
})
92+
93+
it('should set translated flag in context when tenant is present', async () => {
94+
const ctx = createContext({
95+
accountName: 'testaccount',
96+
appSettings: {
97+
shouldUseNewPLPEndpoint: false,
98+
},
99+
intelligentSearchApiSettings: {
100+
facets: { ...mockFacetsResponse, translated: true },
101+
},
102+
tenantLocale: 'en-US',
103+
})
104+
105+
await fetchFacets(ctx, mockArgs, mockSelectedFacets)
106+
107+
expect(ctx.translated).toBe(true)
108+
})
109+
})
110+

node/services/facets.ts

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import { buildAttributePath } from '../commons/compatibility-layer'
2+
import { fetchAppSettings } from './settings'
3+
import type { FacetsInput } from '../typings/Search'
4+
5+
/**
6+
* Fetches facets using the intelligentSearchApi client (Biggy)
7+
*/
8+
async function fetchFacetsFromBiggy(
9+
ctx: Context,
10+
args: FacetsInput,
11+
selectedFacets: SelectedFacet[],
12+
shippingOptions?: string[]
13+
) {
14+
const {
15+
clients: { intelligentSearchApi },
16+
} = ctx
17+
18+
const biggyArgs: { [key: string]: any } = {
19+
...args,
20+
}
21+
22+
// unnecessary field. It's is an object and breaks the @vtex/api cache
23+
delete biggyArgs.selectedFacets
24+
25+
const result: any = await intelligentSearchApi.facets(
26+
{ ...biggyArgs, query: args.fullText },
27+
buildAttributePath(selectedFacets),
28+
shippingOptions
29+
)
30+
31+
if (ctx.vtex.tenant) {
32+
ctx.translated = result.translated
33+
}
34+
35+
return result
36+
}
37+
38+
/**
39+
* Fetches facets using the intsch client (Intelligent Search)
40+
*/
41+
async function fetchFacetsFromIntsch(
42+
ctx: Context,
43+
args: FacetsInput,
44+
selectedFacets: SelectedFacet[],
45+
shippingOptions?: string[]
46+
) {
47+
const {
48+
clients: { intsch },
49+
} = ctx
50+
51+
const intschArgs: { [key: string]: any } = {
52+
...args,
53+
}
54+
55+
// unnecessary field. It's is an object and breaks the @vtex/api cache
56+
delete intschArgs.selectedFacets
57+
58+
const result: any = await intsch.facets(
59+
{ ...intschArgs, query: args.fullText },
60+
buildAttributePath(selectedFacets),
61+
shippingOptions
62+
)
63+
64+
if (ctx.vtex.tenant) {
65+
ctx.translated = result.translated
66+
}
67+
68+
return result
69+
}
70+
71+
/**
72+
* Facets service that extracts facets fetching logic and implements flag-based routing
73+
* using intelligentSearchApi as primary and intsch as alternative based on shouldUseNewPLPEndpoint flag
74+
*/
75+
export async function fetchFacets(
76+
ctx: Context,
77+
args: FacetsInput,
78+
selectedFacets: SelectedFacet[],
79+
shippingOptions?: string[]
80+
) {
81+
const { shouldUseNewPLPEndpoint } = await fetchAppSettings(ctx)
82+
83+
// Check if current account should use intsch directly
84+
if (shouldUseNewPLPEndpoint) {
85+
return fetchFacetsFromIntsch(ctx, args, selectedFacets, shippingOptions)
86+
}
87+
88+
return fetchFacetsFromBiggy(ctx, args, selectedFacets, shippingOptions)
89+
}
90+

0 commit comments

Comments
 (0)