@constructor-io/constructorio-node 4.4.3 → 4.4.6
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/package.json +3 -2
- package/src/modules/search.js +103 -2
- package/src/types/browse.d.ts +2 -0
- package/src/types/index.d.ts +28 -0
- package/src/types/search.d.ts +10 -0
- package/src/types/tests/types.test-d.ts +33 -0
package/package.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@constructor-io/constructorio-node",
|
|
3
|
-
"version": "4.4.
|
|
3
|
+
"version": "4.4.6",
|
|
4
4
|
"description": "Constructor.io Node.js client",
|
|
5
5
|
"main": "src/constructorio.js",
|
|
6
6
|
"types": "src/types/constructorio.d.ts",
|
|
7
7
|
"scripts": {
|
|
8
|
-
"version": "chmod +x ./scripts/verify-node-version.sh && ./scripts/verify-node-version.sh
|
|
8
|
+
"verify-node-version": "chmod +x ./scripts/verify-node-version.sh && ./scripts/verify-node-version.sh",
|
|
9
|
+
"version": "npm run verify-node-version && npm run docs && git add ./docs/*",
|
|
9
10
|
"check-lisc": "license-checker --production --onlyAllow 'Apache-2.0;BSD-3-Clause;MIT'",
|
|
10
11
|
"lint": "eslint 'src/**/*.js' 'spec/**/*.js' 'src/**/*.d.ts'",
|
|
11
12
|
"test:parallel": "mkdir -p test && cp -rf src/* test && mocha --parallel ./spec/*",
|
package/src/modules/search.js
CHANGED
|
@@ -6,7 +6,7 @@ const helpers = require('../utils/helpers');
|
|
|
6
6
|
|
|
7
7
|
// Create URL from supplied query (term) and parameters
|
|
8
8
|
// eslint-disable-next-line complexity
|
|
9
|
-
function createSearchUrl(query, parameters, userParameters, options) {
|
|
9
|
+
function createSearchUrl(query, parameters, userParameters, options, isVoiceSearch = false) {
|
|
10
10
|
const {
|
|
11
11
|
apiKey,
|
|
12
12
|
version,
|
|
@@ -137,7 +137,9 @@ function createSearchUrl(query, parameters, userParameters, options) {
|
|
|
137
137
|
|
|
138
138
|
const queryString = qs.stringify(queryParams, { indices: false });
|
|
139
139
|
|
|
140
|
-
|
|
140
|
+
const searchUrl = isVoiceSearch ? 'search/natural_language' : 'search';
|
|
141
|
+
|
|
142
|
+
return `${serviceUrl}/${searchUrl}/${helpers.encodeURIComponentRFC3986(helpers.trimNonBreakingSpaces(query))}?${queryString}`;
|
|
141
143
|
}
|
|
142
144
|
|
|
143
145
|
/**
|
|
@@ -255,6 +257,105 @@ class Search {
|
|
|
255
257
|
throw new Error('getSearchResults response data is malformed');
|
|
256
258
|
});
|
|
257
259
|
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Retrieve voice search results from API
|
|
263
|
+
*
|
|
264
|
+
* @function getVoiceSearchResults
|
|
265
|
+
* @param {string} query - Term to use to perform a voice search
|
|
266
|
+
* @param {object} [parameters] - Additional parameters to refine result set
|
|
267
|
+
* @param {number} [parameters.page] - The page number of the results. Can't be used together with 'offset'
|
|
268
|
+
* @param {number} [parameters.offset] - The number of results to skip from the beginning. Can't be used together with 'page'
|
|
269
|
+
* @param {number} [parameters.resultsPerPage] - The number of results per page to return
|
|
270
|
+
* @param {string} [parameters.section='Products'] - The section name for results
|
|
271
|
+
* @param {object} [parameters.fmtOptions] - The format options used to refine result groups
|
|
272
|
+
* @param {string[]} [parameters.hiddenFields] - Hidden metadata fields to return
|
|
273
|
+
* @param {string[]} [parameters.hiddenFacets] - Hidden facet fields to return
|
|
274
|
+
* @param {object} [parameters.variationsMap] - The variations map object to aggregate variations. Please refer to https://docs.constructor.io/rest_api/variations_mapping for details
|
|
275
|
+
* @param {object} [parameters.preFilterExpression] - Faceting expression to scope search results. Please refer to https://docs.constructor.io/rest_api/collections/#add-items-dynamically for details
|
|
276
|
+
* @param {object} [userParameters] - Parameters relevant to the user request
|
|
277
|
+
* @param {number} [userParameters.sessionId] - Session ID, utilized to personalize results
|
|
278
|
+
* @param {number} [userParameters.clientId] - Client ID, utilized to personalize results
|
|
279
|
+
* @param {string} [userParameters.userId] - User ID, utilized to personalize results
|
|
280
|
+
* @param {string} [userParameters.segments] - User segments
|
|
281
|
+
* @param {object} [userParameters.testCells] - User test cells
|
|
282
|
+
* @param {string} [userParameters.userIp] - Origin user IP, from client
|
|
283
|
+
* @param {string} [userParameters.userAgent] - Origin user agent, from client
|
|
284
|
+
* @param {object} [networkParameters] - Parameters relevant to the network request
|
|
285
|
+
* @param {number} [networkParameters.timeout] - Request timeout (in milliseconds)
|
|
286
|
+
* @returns {Promise}
|
|
287
|
+
* @see https://docs.constructor.io/rest_api/search/natural_language_search/
|
|
288
|
+
* @example
|
|
289
|
+
* constructorio.search.getVoiceSearchResults('show me lipstick', {
|
|
290
|
+
* resultsPerPage: 40,
|
|
291
|
+
* }, {
|
|
292
|
+
* testCells: {
|
|
293
|
+
* testName: 'cellName',
|
|
294
|
+
* },
|
|
295
|
+
* });
|
|
296
|
+
*/
|
|
297
|
+
|
|
298
|
+
getVoiceSearchResults(query, parameters = {}, userParameters = {}, networkParameters = {}) {
|
|
299
|
+
let requestUrl;
|
|
300
|
+
const { fetch } = this.options;
|
|
301
|
+
const controller = new AbortController();
|
|
302
|
+
const { signal } = controller;
|
|
303
|
+
const headers = {};
|
|
304
|
+
|
|
305
|
+
try {
|
|
306
|
+
const isVoiceSearch = true;
|
|
307
|
+
requestUrl = createSearchUrl(query, parameters, userParameters, this.options, isVoiceSearch);
|
|
308
|
+
} catch (e) {
|
|
309
|
+
return Promise.reject(e);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
Object.assign(headers, helpers.combineCustomHeaders(this.options, networkParameters));
|
|
313
|
+
|
|
314
|
+
// Append security token as 'x-cnstrc-token' if available
|
|
315
|
+
if (this.options.securityToken && typeof this.options.securityToken === 'string') {
|
|
316
|
+
headers['x-cnstrc-token'] = this.options.securityToken;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// Append user IP as 'X-Forwarded-For' if available
|
|
320
|
+
if (userParameters.userIp && typeof userParameters.userIp === 'string') {
|
|
321
|
+
headers['X-Forwarded-For'] = userParameters.userIp;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// Append user agent as 'User-Agent' if available
|
|
325
|
+
if (userParameters.userAgent && typeof userParameters.userAgent === 'string') {
|
|
326
|
+
headers['User-Agent'] = userParameters.userAgent;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// Handle network timeout if specified
|
|
330
|
+
helpers.applyNetworkTimeout(this.options, networkParameters, controller);
|
|
331
|
+
|
|
332
|
+
return fetch(requestUrl, { headers, signal }).then((response) => {
|
|
333
|
+
if (response.ok) {
|
|
334
|
+
return response.json();
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
return helpers.throwHttpErrorFromResponse(new Error(), response);
|
|
338
|
+
}).then((json) => {
|
|
339
|
+
// Search results
|
|
340
|
+
if (json.response && json.response.results) {
|
|
341
|
+
if (json.result_id) {
|
|
342
|
+
json.response.results.forEach((result) => {
|
|
343
|
+
// eslint-disable-next-line no-param-reassign
|
|
344
|
+
result.result_id = json.result_id;
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
return json;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// Redirect rules
|
|
352
|
+
if (json.response && json.response.redirect) {
|
|
353
|
+
return json;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
throw new Error('getVoiceSearchResults response data is malformed');
|
|
357
|
+
});
|
|
358
|
+
}
|
|
258
359
|
}
|
|
259
360
|
|
|
260
361
|
module.exports = Search;
|
package/src/types/browse.d.ts
CHANGED
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
ResultSources,
|
|
12
12
|
SortOption,
|
|
13
13
|
FmtOptions,
|
|
14
|
+
FilterExpression,
|
|
14
15
|
} from '.';
|
|
15
16
|
|
|
16
17
|
export default Browse;
|
|
@@ -24,6 +25,7 @@ export interface BrowseParameters {
|
|
|
24
25
|
sortOrder?: string;
|
|
25
26
|
section?: string;
|
|
26
27
|
fmtOptions?: FmtOptions;
|
|
28
|
+
preFilterExpression?: FilterExpression;
|
|
27
29
|
hiddenFields?: string[];
|
|
28
30
|
hiddenFacets?: string[];
|
|
29
31
|
variationsMap?: Record<string, any>;
|
package/src/types/index.d.ts
CHANGED
|
@@ -275,3 +275,31 @@ export interface SynonymGroup extends Record<string, any> {
|
|
|
275
275
|
synonym_group_id: number;
|
|
276
276
|
synonyms: string[];
|
|
277
277
|
}
|
|
278
|
+
|
|
279
|
+
export type FilterExpression =
|
|
280
|
+
| FilterExpressionGroup
|
|
281
|
+
| FilterExpressionNot
|
|
282
|
+
| FilterExpressionValue
|
|
283
|
+
| FilterExpressionRange;
|
|
284
|
+
|
|
285
|
+
export type FilterExpressionGroup =
|
|
286
|
+
| FilterExpressionGroupOr
|
|
287
|
+
| FilterExpressionGroupAnd;
|
|
288
|
+
|
|
289
|
+
export type FilterExpressionGroupOr = { or: FilterExpression[] };
|
|
290
|
+
export type FilterExpressionGroupAnd = { and: FilterExpression[] };
|
|
291
|
+
export type FilterExpressionCondition = 'or' | 'and';
|
|
292
|
+
|
|
293
|
+
export type FilterExpressionNot = { not: FilterExpression };
|
|
294
|
+
|
|
295
|
+
export type FilterExpressionValue = {
|
|
296
|
+
name: string;
|
|
297
|
+
value: string;
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
export type FilterExpressionRange = {
|
|
301
|
+
name: string;
|
|
302
|
+
range: FilterExpressionRangeValue;
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
export type FilterExpressionRangeValue = ['-inf' | number, 'inf' | number];
|
package/src/types/search.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import {
|
|
|
2
2
|
ConstructorClientOptions,
|
|
3
3
|
Facet,
|
|
4
4
|
Feature,
|
|
5
|
+
FilterExpression,
|
|
5
6
|
FmtOption,
|
|
6
7
|
Group,
|
|
7
8
|
NetworkParameters,
|
|
@@ -22,6 +23,7 @@ export interface SearchParameters {
|
|
|
22
23
|
sortOrder?: string;
|
|
23
24
|
section?: string;
|
|
24
25
|
fmtOptions?: Record<string, any>;
|
|
26
|
+
preFilterExpression?: FilterExpression;
|
|
25
27
|
hiddenFields?: string[];
|
|
26
28
|
hiddenFacets?: string[];
|
|
27
29
|
variationsMap?: Record<string, any>;
|
|
@@ -38,6 +40,13 @@ declare class Search {
|
|
|
38
40
|
userParameters?: UserParameters,
|
|
39
41
|
networkParameters?: NetworkParameters
|
|
40
42
|
): Promise<SearchResponse>;
|
|
43
|
+
|
|
44
|
+
getVoiceSearchResults(
|
|
45
|
+
query: string,
|
|
46
|
+
parameters?: Omit<SearchParameters, 'filters' | 'sortBy' | 'sortOrder'>,
|
|
47
|
+
userParameters?: UserParameters,
|
|
48
|
+
networkParameters?: NetworkParameters
|
|
49
|
+
): Promise<SearchResponse>;
|
|
41
50
|
}
|
|
42
51
|
|
|
43
52
|
/* search results returned from server */
|
|
@@ -64,6 +73,7 @@ export interface SearchRequestType extends Record<string, any> {
|
|
|
64
73
|
section: string;
|
|
65
74
|
blacklist_rules: boolean;
|
|
66
75
|
term: string;
|
|
76
|
+
original_query?: string;
|
|
67
77
|
fmt_options: Partial<FmtOption>;
|
|
68
78
|
sort_by: string;
|
|
69
79
|
sort_order: string;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { expectAssignable } from 'tsd';
|
|
2
|
+
import { FilterExpression } from '../index';
|
|
3
|
+
|
|
4
|
+
expectAssignable<FilterExpression>({
|
|
5
|
+
or: [
|
|
6
|
+
{
|
|
7
|
+
and: [
|
|
8
|
+
{
|
|
9
|
+
name: 'group_id',
|
|
10
|
+
value: 'electronics-group-id',
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
name: 'Price',
|
|
14
|
+
range: ['-inf', 200],
|
|
15
|
+
},
|
|
16
|
+
],
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
and: [
|
|
20
|
+
{
|
|
21
|
+
name: 'Type',
|
|
22
|
+
value: 'Laptop',
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
not: {
|
|
26
|
+
name: 'Price',
|
|
27
|
+
range: [800, 'inf'],
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
],
|
|
31
|
+
},
|
|
32
|
+
],
|
|
33
|
+
});
|