Skip to content

Commit b8ff9e2

Browse files
authored
Add feature flag to call the new Facets and Search endpoints (#507)
* Add new feature flag to PLP * Extract productsByIdentifier to use the same endpoint * Add product search endpoint ot intsch * Add facets to the intsch client * Extract the productSearch and facets o a service * Implement shadow traffic comparisson to facets and productSearch * Fix lint warnings * Update changelog * Add cache to intsch * update changelog
1 parent 0a5f9b9 commit b8ff9e2

File tree

16 files changed

+859
-113
lines changed

16 files changed

+859
-113
lines changed

CHANGELOG.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,18 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
77

88
## [Unreleased]
99

10+
### Added
11+
12+
- Feature flag to call the productSearch and facets using the intsch api.
13+
14+
### Changed
15+
16+
- Make productSearch and facets send a % of the traffic to our routes on intsch.
17+
18+
### Fixed
19+
20+
- Make the productIdentifier call the intsch when the feature flag is on.
21+
1022
## [1.86.0] - 2025-10-21
1123

1224
### Changed

manifest.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,11 @@
3030
"title": "Feature flag to use the new PDP endpoint",
3131
"type": "boolean",
3232
"default": false
33+
},
34+
"shouldUseNewPLPEndpoint": {
35+
"title": "Feature flag to use the new PLP endpoint",
36+
"type": "boolean",
37+
"default": false
3338
}
3439
}
3540
},

node/clients/intelligent-search-api.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import type {
1515
} from './intsch/types'
1616
import type { Options, SearchResultArgs } from '../typings/Search'
1717

18-
const isPathTraversal = (str: string) => str.indexOf('..') >= 0
18+
export const isPathTraversal = (str: string) => str.indexOf('..') >= 0
1919

2020
interface CorrectionParams {
2121
query: string
@@ -29,7 +29,7 @@ interface AutocompleteSearchSuggestionsParams {
2929
query: string
3030
}
3131

32-
interface FacetsArgs {
32+
export type FacetsArgs = {
3333
query?: string
3434
page?: number
3535
count?: number
@@ -47,7 +47,7 @@ interface FacetsArgs {
4747
regionId?: string | null
4848
}
4949

50-
const decodeQuery = (query: string) => {
50+
export const decodeQuery = (query: string) => {
5151
try {
5252
return decodeURIComponent(query)
5353
} catch (e) {

node/clients/intsch/index.ts

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,23 @@ import type {
88
CorrectionArgs,
99
CorrectionArgsV1,
1010
CorrectionResponse,
11+
FacetsResponse,
1112
FetchBannersArgs,
1213
FetchBannersArgsV1,
1314
FetchBannersResponse,
1415
FetchProductArgs,
1516
FetchProductResponse,
1617
IIntelligentSearchClient,
18+
ProductSearchResponse,
1719
SearchSuggestionsArgs,
1820
SearchSuggestionsArgsV1,
1921
SearchSuggestionsResponse,
2022
TopSearchesResponse,
2123
} from './types'
24+
import type { SearchResultArgs } from '../../typings/Search'
25+
import type { FacetsArgs } from '../intelligent-search-api'
26+
import { decodeQuery, isPathTraversal } from '../intelligent-search-api'
27+
import { parseState } from '../../utils/searchState'
2228

2329
export class Intsch extends JanusClient implements IIntelligentSearchClient {
2430
private locale: string | undefined
@@ -133,4 +139,56 @@ export class Intsch extends JanusClient implements IIntelligentSearchClient {
133139
metric: 'banners-new-v1',
134140
})
135141
}
142+
143+
public productSearch(
144+
params: SearchResultArgs,
145+
path: string,
146+
shippingHeader?: string[]
147+
): Promise<ProductSearchResponse> {
148+
const { query, leap, searchState } = params
149+
150+
if (isPathTraversal(path)) {
151+
throw new Error('Malformed URL')
152+
}
153+
154+
return this.http.get(`/api/intelligent-search/v0/product-search/${path}`, {
155+
params: {
156+
query: query && decodeQuery(query),
157+
locale: this.locale,
158+
bgy_leap: leap ? true : undefined,
159+
...parseState(searchState),
160+
...params,
161+
},
162+
metric: 'product-search-new',
163+
headers: {
164+
'x-vtex-shipping-options': shippingHeader ?? '',
165+
},
166+
})
167+
}
168+
169+
public facets(
170+
params: FacetsArgs,
171+
path: string,
172+
shippingHeader?: string[]
173+
): Promise<FacetsResponse> {
174+
if (isPathTraversal(path)) {
175+
throw new Error('Malformed URL')
176+
}
177+
178+
const { query, leap, searchState } = params
179+
180+
return this.http.get(`/api/intelligent-search/v0/facets/${path}`, {
181+
params: {
182+
...params,
183+
query: query && decodeQuery(query),
184+
locale: this.locale,
185+
bgy_leap: leap ? true : undefined,
186+
...parseState(searchState),
187+
},
188+
metric: 'facets-new',
189+
headers: {
190+
'x-vtex-shipping-options': shippingHeader ?? '',
191+
},
192+
})
193+
}
136194
}

node/clients/intsch/types.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
import type { SearchResultArgs } from '../../typings/Search'
2+
import type { FacetsArgs } from '../intelligent-search-api'
3+
14
export type AutocompleteSuggestionsArgs = {
25
query: string
36
}
@@ -91,6 +94,12 @@ export type FetchProductArgs = {
9194

9295
export type FetchProductResponse = SearchProduct
9396

97+
export type ProductSearchResponse = {
98+
products: SearchProduct[]
99+
}
100+
101+
export type FacetsResponse = any
102+
94103
export interface IIntelligentSearchClient {
95104
fetchAutocompleteSuggestions(
96105
args: AutocompleteSuggestionsArgs
@@ -111,4 +120,14 @@ export interface IIntelligentSearchClient {
111120
): Promise<SearchSuggestionsResponse>
112121
fetchCorrectionV1(args: CorrectionArgsV1): Promise<CorrectionResponse>
113122
fetchBannersV1(args: FetchBannersArgsV1): Promise<FetchBannersResponse>
123+
productSearch(
124+
args: SearchResultArgs,
125+
path: string,
126+
shippingHeader?: string[]
127+
): Promise<ProductSearchResponse>
128+
facets(
129+
params: FacetsArgs,
130+
path: string,
131+
shippingHeader?: string[]
132+
): Promise<FacetsResponse>
114133
}

node/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,14 @@ const searchCache = new LRUCache<string, Cached>({ max: 3000 })
2121
const messagesCache = new LRUCache<string, Cached>({ max: 3000 })
2222
const vbaseCache = new LRUCache<string, Cached>({ max: 3000 })
2323
const appsCache = new LRUCache<string, Cached>({ max: 1500 })
24+
const intschCache = new LRUCache<string, Cached>({ max: 3000 })
2425

2526
metrics.trackCache('segment', segmentCache)
2627
metrics.trackCache('search', searchCache)
2728
metrics.trackCache('messages', messagesCache)
2829
metrics.trackCache('vbase', vbaseCache)
2930
metrics.trackCache('apps', appsCache)
31+
metrics.trackCache('intsch', intschCache)
3032

3133
export default new Service<Clients, RecorderState, CustomContext>({
3234
clients: {

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/mocks/intsch.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import type {
66
CorrectionResponse,
77
FetchBannersResponse,
88
FetchProductResponse,
9+
ProductSearchResponse,
10+
FacetsResponse,
911
} from '../clients/intsch/types'
1012

1113
export class MockedIntschClient implements IIntelligentSearchClient {
@@ -91,6 +93,18 @@ export class MockedIntschClient implements IIntelligentSearchClient {
9193
} else {
9294
this.fetchBannersV1.mockResolvedValue(args?.fetchBannersV1 ?? null)
9395
}
96+
97+
if (args?.productSearch instanceof Error) {
98+
this.productSearch.mockRejectedValue(args.productSearch)
99+
} else {
100+
this.productSearch.mockResolvedValue(args?.productSearch ?? null)
101+
}
102+
103+
if (args?.facets instanceof Error) {
104+
this.facets.mockRejectedValue(args.facets)
105+
} else {
106+
this.facets.mockResolvedValue(args?.facets ?? null)
107+
}
94108
}
95109

96110
public fetchAutocompleteSuggestions = jest.fn()
@@ -104,6 +118,8 @@ export class MockedIntschClient implements IIntelligentSearchClient {
104118
public fetchSearchSuggestionsV1 = jest.fn()
105119
public fetchCorrectionV1 = jest.fn()
106120
public fetchBannersV1 = jest.fn()
121+
public productSearch = jest.fn()
122+
public facets = jest.fn()
107123
}
108124

109125
export type IntelligentSearchClientArgs = {
@@ -118,4 +134,6 @@ export type IntelligentSearchClientArgs = {
118134
fetchSearchSuggestionsV1?: SearchSuggestionsResponse | Error
119135
fetchCorrectionV1?: CorrectionResponse | Error
120136
fetchBannersV1?: FetchBannersResponse | Error
137+
productSearch?: ProductSearchResponse | Error
138+
facets?: FacetsResponse | Error
121139
}

0 commit comments

Comments
 (0)