@constructor-io/constructorio-node 5.9.0 → 5.10.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@constructor-io/constructorio-node",
3
- "version": "5.9.0",
3
+ "version": "5.10.0",
4
4
  "description": "Constructor.io Node.js client",
5
5
  "main": "src/constructorio.js",
6
6
  "types": "src/types/constructorio.d.ts",
@@ -36,6 +36,7 @@
36
36
  "devDependencies": {
37
37
  "@cspell/eslint-plugin": "^6.8.2",
38
38
  "@types/events": "^3.0.0",
39
+ "@types/node": "^22.19.21",
39
40
  "@typescript-eslint/eslint-plugin": "^5.46.1",
40
41
  "@typescript-eslint/parser": "^5.46.1",
41
42
  "chai": "^4.3.4",
@@ -8,6 +8,7 @@ const Autocomplete = require('./modules/autocomplete');
8
8
  const Recommendations = require('./modules/recommendations');
9
9
  const Tasks = require('./modules/tasks');
10
10
  const Quizzes = require('./modules/quizzes');
11
+ const Searchandising = require('./modules/searchandising');
11
12
  const { version: packageVersion } = require('../package.json');
12
13
  const utils = require('./utils/helpers');
13
14
 
@@ -42,6 +43,7 @@ class ConstructorIO {
42
43
  * @property {object} catalog - Interface to {@link module:catalog}
43
44
  * @property {object} tasks - Interface to {@link module:tasks}
44
45
  * @property {object} quizzes - Interface to {@link module:quizzes}
46
+ * @property {object} searchandising - Interface to {@link module:searchandising}
45
47
  * @returns {class}
46
48
  */
47
49
  constructor(options = {}) {
@@ -83,6 +85,7 @@ class ConstructorIO {
83
85
  this.recommendations = new Recommendations(this.options);
84
86
  this.tasks = new Tasks(this.options);
85
87
  this.quizzes = new Quizzes(this.options);
88
+ this.searchandising = new Searchandising(this.options);
86
89
  }
87
90
  }
88
91
 
@@ -0,0 +1,428 @@
1
+ /* eslint-disable max-len */
2
+ /* eslint-disable camelcase, no-underscore-dangle, no-unneeded-ternary, brace-style */
3
+ const qs = require('qs');
4
+ const { AbortController } = require('node-abort-controller');
5
+ const helpers = require('../../utils/helpers');
6
+
7
+ // Create URL from supplied path and options
8
+ function createCampaignsUrl(path, options, additionalQueryParams = {}, apiVersion = 'v1') {
9
+ const {
10
+ apiKey,
11
+ serviceUrl,
12
+ version,
13
+ } = options;
14
+ let queryParams = {
15
+ c: version,
16
+ ...additionalQueryParams,
17
+ };
18
+
19
+ // Validate path is provided
20
+ if (!path || typeof path !== 'string') {
21
+ throw new Error('path is a required parameter of type string');
22
+ }
23
+
24
+ queryParams.key = apiKey;
25
+ queryParams = helpers.cleanParams(queryParams);
26
+
27
+ const queryString = qs.stringify(queryParams, { indices: false });
28
+
29
+ const encodedPath = path.split('/').map(encodeURIComponent).join('/');
30
+
31
+ return `${serviceUrl}/${encodeURIComponent(apiVersion)}/${encodedPath}?${queryString}`;
32
+ }
33
+
34
+ /**
35
+ * Interface to searchandising campaign related API calls
36
+ *
37
+ * @module searchandising/campaigns
38
+ * @inner
39
+ * @returns {object}
40
+ */
41
+ class Campaigns {
42
+ constructor(options) {
43
+ this.options = options || {};
44
+ }
45
+
46
+ /**
47
+ * Retrieve campaigns
48
+ *
49
+ * @function retrieveCampaigns
50
+ * @param {object} [parameters] - Additional parameters for retrieving campaigns
51
+ * @param {string} [parameters.section='Products'] - The section of the index to use
52
+ * @param {number|number[]} [parameters.id] - The ID(s) of campaigns to filter by
53
+ * @param {string[]} [parameters.refinedQueries] - A list of refined queries to filter by
54
+ * @param {object} [parameters.refinedFilters] - An object of refined filters to filter by
55
+ * @param {object} [parameters.refinedRecommendationContexts] - A filter for refined recommendation contexts
56
+ * @param {number} [parameters.numResultsPerPage=20] - The number of campaigns to return - maximum value 100
57
+ * @param {number} [parameters.page] - The page of results to return
58
+ * @param {number} [parameters.offset] - The number of results to skip from the beginning - cannot be used together with `page`
59
+ * @param {object} [networkParameters] - Parameters relevant to the network request
60
+ * @param {number} [networkParameters.timeout] - Request timeout (in milliseconds)
61
+ * @returns {Promise}
62
+ * @see https://docs.constructor.com/reference/v1-searchandising-retrieve-campaigns
63
+ * @example
64
+ * constructorio.searchandising.campaigns.retrieveCampaigns({
65
+ * section: 'Products',
66
+ * numResultsPerPage: 50,
67
+ * page: 1,
68
+ * });
69
+ */
70
+ retrieveCampaigns(parameters = {}, networkParameters = {}) {
71
+ const queryParams = {};
72
+ let requestUrl;
73
+ const { fetch } = this.options;
74
+ const controller = new AbortController();
75
+ const { signal } = controller;
76
+ const headers = {
77
+ 'Content-Type': 'application/json',
78
+ };
79
+
80
+ if (parameters) {
81
+ const {
82
+ section,
83
+ id,
84
+ refined_filters,
85
+ refinedFilters = refined_filters,
86
+ num_results_per_page,
87
+ numResultsPerPage = num_results_per_page,
88
+ page,
89
+ offset,
90
+ refined_recommendation_contexts,
91
+ refinedRecommendationContexts = refined_recommendation_contexts,
92
+ refined_queries,
93
+ refinedQueries = refined_queries,
94
+ } = parameters;
95
+
96
+ if (section) {
97
+ queryParams.section = section;
98
+ }
99
+
100
+ if (id) {
101
+ queryParams.id = id;
102
+ }
103
+
104
+ if (refinedFilters) {
105
+ queryParams.refined_filters = refinedFilters;
106
+ }
107
+
108
+ if (numResultsPerPage) {
109
+ queryParams.num_results_per_page = numResultsPerPage;
110
+ }
111
+
112
+ if (page) {
113
+ queryParams.page = page;
114
+ }
115
+
116
+ if (offset) {
117
+ queryParams.offset = offset;
118
+ }
119
+
120
+ if (refinedRecommendationContexts) {
121
+ queryParams.refined_recommendation_contexts = refinedRecommendationContexts;
122
+ }
123
+
124
+ if (refinedQueries) {
125
+ queryParams.refined_queries = refinedQueries;
126
+ }
127
+ }
128
+
129
+ try {
130
+ requestUrl = createCampaignsUrl('campaigns', this.options, queryParams);
131
+ } catch (e) {
132
+ return Promise.reject(e);
133
+ }
134
+
135
+ Object.assign(headers, helpers.combineCustomHeaders(this.options, networkParameters));
136
+
137
+ helpers.applyNetworkTimeout(this.options, networkParameters, controller);
138
+
139
+ return fetch(requestUrl, {
140
+ method: 'GET',
141
+ headers: {
142
+ ...headers,
143
+ ...helpers.createAuthHeader(this.options),
144
+ },
145
+ signal,
146
+ }).then((response) => {
147
+ if (response.ok) {
148
+ return response.json();
149
+ }
150
+
151
+ return helpers.throwHttpErrorFromResponse(new Error(), response);
152
+ }).then((json) => json);
153
+ }
154
+
155
+ /**
156
+ * Retrieve a campaign given a specific id
157
+ *
158
+ * @function retrieveCampaign
159
+ * @param {object} parameters - Additional parameters for retrieving a campaign
160
+ * @param {number} parameters.id - The ID of the campaign to be retrieved
161
+ * @param {string} [parameters.section='Products'] - The section of the index to use
162
+ * @param {object} [networkParameters] - Parameters relevant to the network request
163
+ * @param {number} [networkParameters.timeout] - Request timeout (in milliseconds)
164
+ * @returns {Promise}
165
+ * @see https://docs.constructor.com/reference/v1-searchandising-retrieve-campaign
166
+ * @example
167
+ * constructorio.searchandising.campaigns.retrieveCampaign({
168
+ * id: 42,
169
+ * section: 'Products',
170
+ * });
171
+ */
172
+ retrieveCampaign(parameters = {}, networkParameters = {}) {
173
+ const queryParams = {};
174
+ let requestUrl;
175
+ const { fetch } = this.options;
176
+ const controller = new AbortController();
177
+ const { signal } = controller;
178
+ const headers = {
179
+ 'Content-Type': 'application/json',
180
+ };
181
+ const { id, section } = parameters;
182
+
183
+ if (section) {
184
+ queryParams.section = section;
185
+ }
186
+
187
+ try {
188
+ requestUrl = createCampaignsUrl(`campaigns/${id}`, this.options, queryParams);
189
+ } catch (e) {
190
+ return Promise.reject(e);
191
+ }
192
+
193
+ Object.assign(headers, helpers.combineCustomHeaders(this.options, networkParameters));
194
+
195
+ helpers.applyNetworkTimeout(this.options, networkParameters, controller);
196
+
197
+ return fetch(requestUrl, {
198
+ method: 'GET',
199
+ headers: {
200
+ ...headers,
201
+ ...helpers.createAuthHeader(this.options),
202
+ },
203
+ signal,
204
+ }).then((response) => {
205
+ if (response.ok) {
206
+ return response.json();
207
+ }
208
+
209
+ return helpers.throwHttpErrorFromResponse(new Error(), response);
210
+ }).then((json) => json);
211
+ }
212
+
213
+ /**
214
+ * Create a campaign
215
+ *
216
+ * @function createCampaign
217
+ * @param {object} parameters - Additional parameters for the campaign to be created
218
+ * @param {string} parameters.name - The name of the campaign
219
+ * @param {string} [parameters.section='Products'] - The section of the index to use
220
+ * @param {string} [parameters.description] - The description of the campaign
221
+ * @param {string} [parameters.requestTagName] - Request tag name used to activate this campaign for, used only with `request_tag_value`
222
+ * @param {string} [parameters.requestTagValue] - Request tag value used to activate this campaign for, used only with `request_tag_name`
223
+ * @param {string} [parameters.startTime] - The start time of the campaign (ISO 8601 date-time)
224
+ * @param {string} [parameters.endTime] - The end time of the campaign (ISO 8601 date-time)
225
+ * @param {object[]} [parameters.refinedQueries] - A list of refined queries
226
+ * @param {object[]} [parameters.refinedFilters] - A list of refined filters
227
+ * @param {object[]} [parameters.refinedRecommendationContexts] - A list of refined recommendation contexts
228
+ * @param {object[]} [parameters.boostRules] - A list of boost rules
229
+ * @param {object[]} [parameters.blacklistRules] - A list of blacklist rules
230
+ * @param {object[]} [parameters.slotRules] - A list of slot rules
231
+ * @param {object[]} [parameters.contentRules] - A list of content rules
232
+ * @param {object[]} [parameters.filtersSlotRules] - A list of filters slot rules
233
+ * @param {object} [parameters.whitelistRule] - A whitelist rule
234
+ * @param {object} [parameters.variationSlicingRule] - A variation slicing rule
235
+ * @param {object} [parameters.metadataJson] - Metadata related to the campaign
236
+ * @param {object} [networkParameters] - Parameters relevant to the network request
237
+ * @param {number} [networkParameters.timeout] - Request timeout (in milliseconds)
238
+ * @returns {Promise}
239
+ * @see https://docs.constructor.com/reference/v1-searchandising-create-campaign
240
+ * @example
241
+ * constructorio.searchandising.campaigns.createCampaign({
242
+ * name: 'Spring Sale',
243
+ * section: 'Products',
244
+ * description: 'Seasonal promotion campaign',
245
+ * boostRules: [
246
+ * { rule_type: 'boost', rule: { boost: 5, filters: { brand: ['Nike'] } } },
247
+ * ],
248
+ * });
249
+ */
250
+ createCampaign(parameters = {}, networkParameters = {}) {
251
+ const queryParams = {};
252
+ let requestUrl;
253
+ const { fetch } = this.options;
254
+ const controller = new AbortController();
255
+ const { signal } = controller;
256
+ const headers = {
257
+ 'Content-Type': 'application/json',
258
+ };
259
+ const { section, ...body } = parameters;
260
+
261
+ if (section) {
262
+ queryParams.section = section;
263
+ }
264
+
265
+ try {
266
+ requestUrl = createCampaignsUrl('campaigns', this.options, queryParams);
267
+ } catch (e) {
268
+ return Promise.reject(e);
269
+ }
270
+
271
+ Object.assign(headers, helpers.combineCustomHeaders(this.options, networkParameters));
272
+
273
+ helpers.applyNetworkTimeout(this.options, networkParameters, controller);
274
+
275
+ return fetch(requestUrl, {
276
+ method: 'POST',
277
+ body: JSON.stringify(helpers.toSnakeCaseKeys(body, false)),
278
+ headers: {
279
+ ...headers,
280
+ ...helpers.createAuthHeader(this.options),
281
+ },
282
+ signal,
283
+ }).then((response) => {
284
+ if (response.ok) {
285
+ return response.json();
286
+ }
287
+
288
+ return helpers.throwHttpErrorFromResponse(new Error(), response);
289
+ }).then((json) => json);
290
+ }
291
+
292
+ /**
293
+ * Update a campaign - only the fields present in the request will be updated
294
+ *
295
+ * @function updateCampaign
296
+ * @param {object} parameters - Additional parameters for the campaign to be updated
297
+ * @param {number} parameters.id - The ID of the campaign to be updated
298
+ * @param {string} [parameters.section='Products'] - The section of the index to use
299
+ * @param {string} [parameters.name] - The name of the campaign
300
+ * @param {string} [parameters.description] - The description of the campaign
301
+ * @param {string} [parameters.requestTagName] - Request tag name used to activate this campaign for, used only with `request_tag_value`
302
+ * @param {string} [parameters.requestTagValue] - Request tag value used to activate this campaign for, used only with `request_tag_name`
303
+ * @param {string} [parameters.startTime] - The start time of the campaign (ISO 8601 date-time)
304
+ * @param {string} [parameters.endTime] - The end time of the campaign (ISO 8601 date-time)
305
+ * @param {object[]} [parameters.refinedQueries] - A list of refined queries
306
+ * @param {object[]} [parameters.refinedFilters] - A list of refined filters
307
+ * @param {object[]} [parameters.refinedRecommendationContexts] - A list of refined recommendation contexts
308
+ * @param {object[]} [parameters.boostRules] - A list of boost rules
309
+ * @param {object[]} [parameters.blacklistRules] - A list of blacklist rules
310
+ * @param {object[]} [parameters.slotRules] - A list of slot rules
311
+ * @param {object[]} [parameters.contentRules] - A list of content rules
312
+ * @param {object[]} [parameters.filtersSlotRules] - A list of filters slot rules
313
+ * @param {object} [parameters.whitelistRule] - A whitelist rule
314
+ * @param {object} [parameters.variationSlicingRule] - A variation slicing rule
315
+ * @param {object} [parameters.metadataJson] - Metadata related to the campaign
316
+ * @param {object} [networkParameters] - Parameters relevant to the network request
317
+ * @param {number} [networkParameters.timeout] - Request timeout (in milliseconds)
318
+ * @returns {Promise}
319
+ * @see https://docs.constructor.com/reference/v1-searchandising-update-campaign
320
+ * @example
321
+ * constructorio.searchandising.campaigns.updateCampaign({
322
+ * id: 42,
323
+ * name: 'Spring Sale - Updated',
324
+ * description: 'Updated seasonal promotion campaign',
325
+ * });
326
+ */
327
+ updateCampaign(parameters = {}, networkParameters = {}) {
328
+ const queryParams = {};
329
+ let requestUrl;
330
+ const { fetch } = this.options;
331
+ const controller = new AbortController();
332
+ const { signal } = controller;
333
+ const headers = {
334
+ 'Content-Type': 'application/json',
335
+ };
336
+ const { id, section, ...body } = parameters;
337
+
338
+ if (section) {
339
+ queryParams.section = section;
340
+ }
341
+
342
+ try {
343
+ requestUrl = createCampaignsUrl(`campaigns/${id}`, this.options, queryParams);
344
+ } catch (e) {
345
+ return Promise.reject(e);
346
+ }
347
+
348
+ Object.assign(headers, helpers.combineCustomHeaders(this.options, networkParameters));
349
+
350
+ helpers.applyNetworkTimeout(this.options, networkParameters, controller);
351
+
352
+ return fetch(requestUrl, {
353
+ method: 'PATCH',
354
+ body: JSON.stringify(helpers.toSnakeCaseKeys(body, false)),
355
+ headers: {
356
+ ...headers,
357
+ ...helpers.createAuthHeader(this.options),
358
+ },
359
+ signal,
360
+ }).then((response) => {
361
+ if (response.ok) {
362
+ return response.json();
363
+ }
364
+
365
+ return helpers.throwHttpErrorFromResponse(new Error(), response);
366
+ }).then((json) => json);
367
+ }
368
+
369
+ /**
370
+ * Delete a campaign given a specific id
371
+ *
372
+ * @function deleteCampaign
373
+ * @param {object} parameters - Additional parameters for the campaign to be deleted
374
+ * @param {number} parameters.id - The ID of the campaign to be deleted
375
+ * @param {string} [parameters.section='Products'] - The section of the index to use
376
+ * @param {object} [networkParameters] - Parameters relevant to the network request
377
+ * @param {number} [networkParameters.timeout] - Request timeout (in milliseconds)
378
+ * @returns {Promise}
379
+ * @see https://docs.constructor.com/reference/v1-searchandising-delete-campaign
380
+ * @example
381
+ * constructorio.searchandising.campaigns.deleteCampaign({
382
+ * id: 42,
383
+ * section: 'Products',
384
+ * });
385
+ */
386
+ deleteCampaign(parameters = {}, networkParameters = {}) {
387
+ const queryParams = {};
388
+ let requestUrl;
389
+ const { fetch } = this.options;
390
+ const controller = new AbortController();
391
+ const { signal } = controller;
392
+ const headers = {
393
+ 'Content-Type': 'application/json',
394
+ };
395
+ const { id, section } = parameters;
396
+
397
+ if (section) {
398
+ queryParams.section = section;
399
+ }
400
+
401
+ try {
402
+ requestUrl = createCampaignsUrl(`campaigns/${id}`, this.options, queryParams);
403
+ } catch (e) {
404
+ return Promise.reject(e);
405
+ }
406
+
407
+ Object.assign(headers, helpers.combineCustomHeaders(this.options, networkParameters));
408
+
409
+ helpers.applyNetworkTimeout(this.options, networkParameters, controller);
410
+
411
+ return fetch(requestUrl, {
412
+ method: 'DELETE',
413
+ headers: {
414
+ ...headers,
415
+ ...helpers.createAuthHeader(this.options),
416
+ },
417
+ signal,
418
+ }).then((response) => {
419
+ if (response.ok) {
420
+ return Promise.resolve();
421
+ }
422
+
423
+ return helpers.throwHttpErrorFromResponse(new Error(), response);
424
+ });
425
+ }
426
+ }
427
+
428
+ module.exports = Campaigns;
@@ -0,0 +1,18 @@
1
+ /* eslint-disable max-len */
2
+ const Campaigns = require('./campaigns');
3
+
4
+ /**
5
+ * Interface to searchandising related API calls
6
+ *
7
+ * @module searchandising
8
+ * @inner
9
+ * @returns {object}
10
+ */
11
+ class Searchandising {
12
+ constructor(options) {
13
+ this.options = options || {};
14
+ this.campaigns = new Campaigns(this.options);
15
+ }
16
+ }
17
+
18
+ module.exports = Searchandising;
@@ -6,6 +6,7 @@ import Tracker from './tracker';
6
6
  import Catalog from './catalog';
7
7
  import Tasks from './tasks';
8
8
  import Quizzes from './quizzes';
9
+ import Searchandising from './searchandising';
9
10
 
10
11
  import { ConstructorClientOptions } from '.';
11
12
 
@@ -31,4 +32,6 @@ declare class ConstructorIO {
31
32
  tasks: Tasks;
32
33
 
33
34
  quizzes: Quizzes;
35
+
36
+ searchandising: Searchandising;
34
37
  }
@@ -6,6 +6,7 @@ export * from './recommendations';
6
6
  export * from './search';
7
7
  export * from './tasks';
8
8
  export * from './tracker';
9
+ export * from './searchandising';
9
10
 
10
11
  export interface NetworkParameters extends Record<string, any> {
11
12
  timeout?: number;
@@ -0,0 +1,306 @@
1
+ import { ConstructorClientOptions, NetworkParameters } from '.';
2
+
3
+ export default Searchandising;
4
+
5
+ export interface RetrieveCampaignsParameters {
6
+ section?: string;
7
+ id?: number | number[];
8
+ refinedFilters?: Record<string, string | string[]>;
9
+ numResultsPerPage?: number;
10
+ page?: number;
11
+ offset?: number;
12
+ refinedRecommendationContexts?: RefinedRecommendationContext;
13
+ refinedQueries?: string[];
14
+ }
15
+
16
+ export interface RefinedRecommendationContext {
17
+ pod_id?: string[];
18
+ condition_type?: ('attribute' | 'item' | 'expression')[];
19
+ }
20
+
21
+ export interface RetrieveCampaignParameters {
22
+ id: number;
23
+ section?: string;
24
+ }
25
+
26
+ /* fields shared by create and update campaign requests */
27
+ export interface CampaignParametersBase {
28
+ section?: string;
29
+ name?: string;
30
+ description?: string;
31
+ requestTagName?: RequestTag;
32
+ requestTagValue?: string;
33
+ startTime?: string;
34
+ endTime?: string;
35
+ refinedQueries?: RefinedQuery[];
36
+ refinedFilters?: RefinedFilter[];
37
+ refinedRecommendationContexts?: RecommendationContext[];
38
+ boostRules?: CampaignRuleInput[];
39
+ blacklistRules?: CampaignRuleInput[];
40
+ slotRules?: CampaignRuleInput[];
41
+ contentRules?: CampaignRuleInput[];
42
+ filtersSlotRules?: CampaignRuleInput[];
43
+ whitelistRule?: CampaignRuleInput | null;
44
+ variationSlicingRule?: CampaignRuleInput | null;
45
+ metadataJson?: CampaignMetadata;
46
+ }
47
+
48
+ export interface CreateCampaignParameters extends CampaignParametersBase {
49
+ name: string;
50
+ }
51
+
52
+ export interface UpdateCampaignParameters extends CampaignParametersBase {
53
+ id: number;
54
+ }
55
+
56
+ export interface DeleteCampaignParameters {
57
+ id: number;
58
+ section?: string;
59
+ }
60
+
61
+ export interface Campaigns {
62
+ options: ConstructorClientOptions;
63
+
64
+ retrieveCampaigns(
65
+ parameters?: RetrieveCampaignsParameters,
66
+ networkParameters?: NetworkParameters
67
+ ): Promise<CampaignListGetResponse>;
68
+
69
+ retrieveCampaign(
70
+ parameters: RetrieveCampaignParameters,
71
+ networkParameters?: NetworkParameters
72
+ ): Promise<CampaignGetResponse>;
73
+
74
+ createCampaign(
75
+ parameters: CreateCampaignParameters,
76
+ networkParameters?: NetworkParameters
77
+ ): Promise<CampaignPostResponse>;
78
+
79
+ updateCampaign(
80
+ parameters: UpdateCampaignParameters,
81
+ networkParameters?: NetworkParameters
82
+ ): Promise<CampaignPatchResponse>;
83
+
84
+ deleteCampaign(
85
+ parameters: DeleteCampaignParameters,
86
+ networkParameters?: NetworkParameters
87
+ ): Promise<void>;
88
+ }
89
+
90
+ declare class Searchandising {
91
+ constructor(options: ConstructorClientOptions);
92
+
93
+ options: ConstructorClientOptions;
94
+
95
+ campaigns: Campaigns;
96
+ }
97
+
98
+ export type RequestTag =
99
+ | 'client_ip'
100
+ | 'client_version'
101
+ | 'geo_city'
102
+ | 'geo_country'
103
+ | 'geo_country_iso_code'
104
+ | 'geo_region'
105
+ | 'dt_weekday'
106
+ | 'dt_timeofday'
107
+ | 'user_segment'
108
+ | 'autogenerated_user_segment'
109
+ | 'dynamic_segment';
110
+
111
+ export interface RefinedQuery {
112
+ query: string;
113
+ }
114
+
115
+ export interface RefinedFilter {
116
+ filter_name: string;
117
+ filter_value: string;
118
+ }
119
+
120
+ export interface RecommendationContext {
121
+ pod_id: string;
122
+ condition: RecommendationContextCondition;
123
+ }
124
+
125
+ export type RecommendationContextCondition =
126
+ | RecommendationContextItemCondition
127
+ | RecommendationContextAttributeCondition
128
+ | RecommendationContextExpressionCondition;
129
+
130
+ export interface RecommendationContextItemCondition {
131
+ type: 'item';
132
+ item_id?: string;
133
+ }
134
+
135
+ export interface RecommendationContextAttributeCondition {
136
+ type: 'attribute';
137
+ filter_name?: string;
138
+ filter_value?: string;
139
+ }
140
+
141
+ export interface RecommendationContextExpressionCondition {
142
+ type: 'expression';
143
+ expression: Record<string, string[]>;
144
+ }
145
+
146
+ export type FiltersObj = Record<string, string[]>;
147
+
148
+ export interface PositionRange {
149
+ start: number;
150
+ end: number;
151
+ }
152
+
153
+ export interface BoostRuleParameters {
154
+ filters?: FiltersObj;
155
+ item_ids?: string[];
156
+ boost: number;
157
+ }
158
+
159
+ export interface BlacklistRuleParameters {
160
+ filters?: FiltersObj;
161
+ item_ids?: string[];
162
+ }
163
+
164
+ export interface SlotRuleParameters {
165
+ item_id: string;
166
+ position: number;
167
+ variation_slice?: Record<string, string[]>;
168
+ fuzzy_match?: boolean;
169
+ labels?: Record<string, any>;
170
+ }
171
+
172
+ export interface ContentRuleParameters {
173
+ data: Record<string, any>;
174
+ }
175
+
176
+ export interface FiltersSlotRuleParameters {
177
+ position_ranges: PositionRange[];
178
+ filters?: FiltersObj;
179
+ filter_expression?: Record<string, any> | string;
180
+ labels?: Record<string, any>;
181
+ }
182
+
183
+ export interface WhitelistRuleParameters {
184
+ filters: FiltersObj;
185
+ }
186
+
187
+ export interface VariationSlicingRuleParameters {
188
+ facet_names: string[];
189
+ filter_expression?: Record<string, any> | string;
190
+ }
191
+
192
+ /* shared fields present on every campaign rule */
193
+ export interface RuleObjBase {
194
+ id: number;
195
+ request_tag_name?: RequestTag;
196
+ request_tag_value?: string;
197
+ active?: boolean;
198
+ start_time?: string;
199
+ end_time?: string;
200
+ campaign_id?: number;
201
+ automatically_generated?: boolean;
202
+ created_at?: string;
203
+ updated_at?: string;
204
+ }
205
+
206
+ export interface BoostRule extends RuleObjBase {
207
+ rule_type: 'boost';
208
+ rule: BoostRuleParameters;
209
+ }
210
+
211
+ export interface BlacklistRule extends RuleObjBase {
212
+ rule_type: 'blacklist';
213
+ rule: BlacklistRuleParameters;
214
+ }
215
+
216
+ export interface SlotRule extends RuleObjBase {
217
+ rule_type: 'slot';
218
+ rule: SlotRuleParameters;
219
+ request_filters?: Record<string, string[]>;
220
+ }
221
+
222
+ export interface ContentRule extends RuleObjBase {
223
+ rule_type: 'content';
224
+ rule: ContentRuleParameters;
225
+ }
226
+
227
+ export interface FiltersSlotRule extends RuleObjBase {
228
+ rule_type: 'filters_slot';
229
+ rule: FiltersSlotRuleParameters;
230
+ }
231
+
232
+ export interface WhitelistRule extends RuleObjBase {
233
+ rule_type: 'whitelist';
234
+ rule: WhitelistRuleParameters;
235
+ }
236
+
237
+ export interface VariationSlicingRule extends RuleObjBase {
238
+ rule_type: 'variation_slicing';
239
+ rule: VariationSlicingRuleParameters;
240
+ }
241
+
242
+ export type RuleObj =
243
+ | BoostRule
244
+ | BlacklistRule
245
+ | SlotRule
246
+ | ContentRule
247
+ | FiltersSlotRule
248
+ | WhitelistRule
249
+ | VariationSlicingRule;
250
+
251
+ /*
252
+ * Omit the server-assigned fields from each rule.
253
+ * `Omit` does not distribute over unions - applied directly to RuleObj it would merge
254
+ * all members into one object and decouple rule_type from rule. The `T extends unknown`
255
+ * conditional forces distribution so each member is omitted individually and the
256
+ * rule_type/rule discrimination is preserved.
257
+ */
258
+ type OmitServerFields<T> = T extends unknown
259
+ ? Omit<T, 'id' | 'created_at' | 'updated_at'>
260
+ : never;
261
+
262
+ /* rule definition supplied when creating or modifying a campaign - server-assigned fields are omitted */
263
+ export type CampaignRuleInput = OmitServerFields<RuleObj>;
264
+
265
+ export interface CampaignMetadata {
266
+ goal?: string;
267
+ goal_description?: string;
268
+ }
269
+
270
+ export interface Campaign extends Record<string, any> {
271
+ id: number;
272
+ created_at: string;
273
+ updated_at: string;
274
+ name?: string;
275
+ description?: string;
276
+ request_tag_name?: RequestTag;
277
+ request_tag_value?: string;
278
+ start_time?: string;
279
+ end_time?: string;
280
+ refined_queries?: RefinedQuery[];
281
+ refined_filters?: RefinedFilter[];
282
+ refined_recommendation_contexts?: RecommendationContext[];
283
+ boost_rules?: BoostRule[];
284
+ blacklist_rules?: BlacklistRule[];
285
+ slot_rules?: SlotRule[];
286
+ content_rules?: ContentRule[];
287
+ filters_slot_rules?: FiltersSlotRule[];
288
+ whitelist_rule?: WhitelistRule;
289
+ variation_slicing_rule?: VariationSlicingRule;
290
+ metadata_json?: CampaignMetadata;
291
+ }
292
+
293
+ /* campaigns results returned from server */
294
+ export interface CampaignListGetResponse {
295
+ campaigns: Campaign[];
296
+ total_count: number;
297
+ }
298
+
299
+ /* single campaign result returned from server */
300
+ export type CampaignGetResponse = Campaign;
301
+
302
+ /* campaign result returned from server after creation */
303
+ export type CampaignPostResponse = Campaign;
304
+
305
+ /* campaign result returned from server after update */
306
+ export type CampaignPatchResponse = Campaign;
@@ -0,0 +1,60 @@
1
+ import { expectAssignable } from 'tsd';
2
+ import {
3
+ CampaignListGetResponse,
4
+ Campaign,
5
+ RetrieveCampaignsParameters,
6
+ CreateCampaignParameters,
7
+ } from '../searchandising';
8
+
9
+ expectAssignable<RetrieveCampaignsParameters>({
10
+ section: 'Products',
11
+ id: [1, 2],
12
+ refinedFilters: { group_id: ['123', '456'] },
13
+ numResultsPerPage: 50,
14
+ page: 1,
15
+ refinedRecommendationContexts: {
16
+ pod_id: ['pod-a'],
17
+ condition_type: ['item'],
18
+ },
19
+ refinedQueries: ['shoes', 'boots'],
20
+ });
21
+
22
+ expectAssignable<CreateCampaignParameters>({
23
+ name: 'Spring Sale',
24
+ section: 'Products',
25
+ description: 'Seasonal promotion campaign',
26
+ refinedQueries: [{ query: 'shoes' }],
27
+ boostRules: [
28
+ {
29
+ rule_type: 'boost',
30
+ rule: { boost: 5, filters: { brand: ['Nike'] } },
31
+ },
32
+ ],
33
+ });
34
+
35
+ expectAssignable<Campaign>({
36
+ id: 42,
37
+ created_at: '2026-01-01T00:00:00Z',
38
+ updated_at: '2026-01-02T00:00:00Z',
39
+ name: 'Spring Sale',
40
+ refined_queries: [{ query: 'shoes' }],
41
+ boost_rules: [
42
+ {
43
+ id: 1,
44
+ rule_type: 'boost',
45
+ rule: { boost: 5, filters: { brand: ['Nike'] } },
46
+ },
47
+ ],
48
+ });
49
+
50
+ expectAssignable<CampaignListGetResponse>({
51
+ campaigns: [
52
+ {
53
+ id: 42,
54
+ created_at: '2026-01-01T00:00:00Z',
55
+ updated_at: '2026-01-02T00:00:00Z',
56
+ name: 'Spring Sale',
57
+ },
58
+ ],
59
+ total_count: 1,
60
+ });