@constructor-io/constructorio-node 4.1.0 → 4.2.1

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": "4.1.0",
3
+ "version": "4.2.1",
4
4
  "description": "Constructor.io Node.js client",
5
5
  "main": "src/constructorio.js",
6
6
  "scripts": {
@@ -153,6 +153,8 @@ class Autocomplete {
153
153
  return Promise.reject(e);
154
154
  }
155
155
 
156
+ Object.assign(headers, helpers.combineCustomHeaders(this.options, networkParameters));
157
+
156
158
  // Append security token as 'x-cnstrc-token' if available
157
159
  if (this.options.securityToken && typeof this.options.securityToken === 'string') {
158
160
  headers['x-cnstrc-token'] = this.options.securityToken;
@@ -193,9 +193,11 @@ function createBrowseUrlForFacetOptions(facetName, parameters, userParameters, o
193
193
  }
194
194
 
195
195
  // Create request headers using supplied options and user parameters
196
- function createHeaders(options, userParameters) {
196
+ function createHeaders(options, userParameters, networkParameters = {}) {
197
197
  const headers = {};
198
198
 
199
+ Object.assign(headers, helpers.combineCustomHeaders(options, networkParameters));
200
+
199
201
  // Append security token as 'x-cnstrc-token' if available
200
202
  if (options.securityToken && typeof options.securityToken === 'string') {
201
203
  headers['x-cnstrc-token'] = options.securityToken;
@@ -273,7 +275,7 @@ class Browse {
273
275
  const fetch = (this.options && this.options.fetch) || nodeFetch;
274
276
  const controller = new AbortController();
275
277
  const { signal } = controller;
276
- const headers = createHeaders(this.options, userParameters);
278
+ const headers = createHeaders(this.options, userParameters, networkParameters);
277
279
 
278
280
  try {
279
281
  requestUrl = createBrowseUrlFromFilter(filterName, filterValue, parameters, userParameters, this.options);
@@ -357,7 +359,7 @@ class Browse {
357
359
  const fetch = (this.options && this.options.fetch) || nodeFetch;
358
360
  const controller = new AbortController();
359
361
  const { signal } = controller;
360
- const headers = createHeaders(this.options, userParameters);
362
+ const headers = createHeaders(this.options, userParameters, networkParameters);
361
363
 
362
364
  try {
363
365
  requestUrl = createBrowseUrlFromIDs(itemIds, parameters, userParameters, this.options);
@@ -428,7 +430,7 @@ class Browse {
428
430
  const fetch = (this.options && this.options.fetch) || nodeFetch;
429
431
  const controller = new AbortController();
430
432
  const { signal } = controller;
431
- const headers = createHeaders(this.options, userParameters);
433
+ const headers = createHeaders(this.options, userParameters, networkParameters);
432
434
  const { serviceUrl } = this.options;
433
435
  const queryParams = createQueryParams(parameters, userParameters, this.options);
434
436
 
@@ -488,6 +490,7 @@ class Browse {
488
490
  const fetch = (this.options && this.options.fetch) || nodeFetch;
489
491
  const controller = new AbortController();
490
492
  const { signal } = controller;
493
+ const headers = createHeaders(this.options, userParameters, networkParameters);
491
494
 
492
495
  try {
493
496
  requestUrl = createBrowseUrlForFacets(parameters, userParameters, this.options);
@@ -499,7 +502,7 @@ class Browse {
499
502
  helpers.applyNetworkTimeout(this.options, networkParameters, controller);
500
503
 
501
504
  return fetch(requestUrl, {
502
- headers: helpers.createAuthHeader(this.options),
505
+ headers: { ...headers, ...helpers.createAuthHeader(this.options) },
503
506
  signal,
504
507
  }).then((response) => {
505
508
  if (response.ok) {
@@ -542,6 +545,7 @@ class Browse {
542
545
  const fetch = (this.options && this.options.fetch) || nodeFetch;
543
546
  const controller = new AbortController();
544
547
  const { signal } = controller;
548
+ const headers = createHeaders(this.options, userParameters, networkParameters);
545
549
 
546
550
  try {
547
551
  requestUrl = createBrowseUrlForFacetOptions(facetName, parameters, userParameters, this.options);
@@ -553,7 +557,7 @@ class Browse {
553
557
  helpers.applyNetworkTimeout(this.options, networkParameters, controller);
554
558
 
555
559
  return fetch(requestUrl, {
556
- headers: helpers.createAuthHeader(this.options),
560
+ headers: { ...headers, ...helpers.createAuthHeader(this.options) },
557
561
  signal,
558
562
  }).then((response) => {
559
563
  if (response.ok) {
@@ -34,14 +34,14 @@ function createCatalogUrl(path, options, additionalQueryParams = {}, apiVersion
34
34
  // Convert a read stream to buffer
35
35
  function convertToBuffer(stream) {
36
36
  return new Promise((resolve, reject) => {
37
- let buffer = '';
37
+ const chunks = [];
38
38
 
39
39
  stream.on('data', (chunk) => {
40
- buffer += chunk;
40
+ chunks.push(chunk);
41
41
  });
42
42
 
43
43
  stream.on('end', () => {
44
- resolve(buffer);
44
+ resolve(Buffer.concat(chunks));
45
45
  });
46
46
 
47
47
  stream.on('error', (err) => {
@@ -118,6 +118,37 @@ async function createQueryParamsAndFormData(parameters) {
118
118
  return { queryParams, formData };
119
119
  }
120
120
 
121
+ async function addTarArchiveToFormData(parameters, formData, operation, apiKey) {
122
+ try {
123
+ const { section } = parameters;
124
+ let { tarArchive } = parameters;
125
+
126
+ // Convert tarArchive to buffer if passed as stream
127
+ if (tarArchive instanceof fs.ReadStream || tarArchive instanceof Duplex) {
128
+ tarArchive = await convertToBuffer(tarArchive);
129
+ }
130
+
131
+ // Pull tarArchive from parameters
132
+ if (tarArchive && formData && operation && apiKey && section) {
133
+ // Convert timestamp to YYYY-MM-DD-HH-MM-SS format
134
+ const formattedDateTime = new Date()
135
+ .toISOString()
136
+ .replace('T', '-')
137
+ .replace(/:/g, '-')
138
+ .slice(0, 19);
139
+ const filename = `${apiKey}_${section}_${operation}_${formattedDateTime}.tar.gz`;
140
+
141
+ formData.append(filename, tarArchive, {
142
+ filename,
143
+ });
144
+ }
145
+ } catch (error) {
146
+ throw new Error(error);
147
+ }
148
+
149
+ return formData;
150
+ }
151
+
121
152
  /**
122
153
  * Interface to catalog related API calls
123
154
  *
@@ -2207,6 +2238,155 @@ class Catalog {
2207
2238
  }
2208
2239
  }
2209
2240
 
2241
+ /**
2242
+ * Send full catalog tar archive to replace the current catalog
2243
+ *
2244
+ * @function replaceCatalogUsingTarArchive
2245
+ * @param {object} parameters - Additional parameters for catalog details
2246
+ * @param {string} parameters.section - The section to update
2247
+ * @param {string} [parameters.notification_email] - An email address to receive an email notification if the task fails
2248
+ * @param {boolean} [parameters.force=false] - Process the catalog even if it will invalidate a large number of existing items
2249
+ * @param {file} [parameters.tarArchive] - The tar file that includes csv files
2250
+ * @param {object} [networkParameters] - Parameters relevant to the network request
2251
+ * @param {number} [networkParameters.timeout] - Request timeout (in milliseconds)
2252
+ * @returns {Promise}
2253
+ * @see https://docs.constructor.io/rest_api/full_catalog
2254
+ * @example
2255
+ * constructorio.catalog.replaceCatalogUsingTarArchive({
2256
+ * section: 'Products',
2257
+ * notification_email: 'notifications@example.com',
2258
+ * tarArchive: tarArchiveBufferOrStream,
2259
+ * });
2260
+ */
2261
+ async replaceCatalogUsingTarArchive(parameters = {}, networkParameters = {}) {
2262
+ try {
2263
+ const fetch = (this.options && this.options.fetch) || nodeFetch;
2264
+ const apiKey = this.options && this.options.apiKey;
2265
+ const controller = new AbortController();
2266
+ const { signal } = controller;
2267
+ const { queryParams, formData } = await createQueryParamsAndFormData(parameters);
2268
+ const formDataWithTarArchive = await addTarArchiveToFormData(parameters, formData, 'sync', apiKey);
2269
+ const requestUrl = createCatalogUrl('catalog', this.options, queryParams);
2270
+ // Handle network timeout if specified
2271
+ helpers.applyNetworkTimeout(this.options, networkParameters, controller);
2272
+
2273
+ const response = await fetch(requestUrl, {
2274
+ method: 'PUT',
2275
+ body: formDataWithTarArchive,
2276
+ headers: helpers.createAuthHeader(this.options),
2277
+ signal,
2278
+ });
2279
+
2280
+ if (response.ok) {
2281
+ return Promise.resolve(response.json());
2282
+ }
2283
+
2284
+ return helpers.throwHttpErrorFromResponse(new Error(), response);
2285
+ } catch (error) {
2286
+ return Promise.reject(error);
2287
+ }
2288
+ }
2289
+
2290
+ /**
2291
+ * Send delta catalog tar archive to update the current catalog
2292
+ *
2293
+ * @function updateCatalogUsingTarArchive
2294
+ * @param {object} parameters - Additional parameters for catalog details
2295
+ * @param {string} parameters.section - The section to update
2296
+ * @param {string} [parameters.notification_email] - An email address to receive an email notification if the task fails
2297
+ * @param {boolean} [parameters.force=false] - Process the catalog even if it will invalidate a large number of existing items
2298
+ * @param {file} [parameters.tarArchive] - The tar file that includes csv files
2299
+ * @param {object} [networkParameters] - Parameters relevant to the network request
2300
+ * @param {number} [networkParameters.timeout] - Request timeout (in milliseconds)
2301
+ * @returns {Promise}
2302
+ * @see https://docs.constructor.io/rest_api/full_catalog
2303
+ * @example
2304
+ * constructorio.catalog.updateCatalogUsingTarArchive({
2305
+ * section: 'Products',
2306
+ * notification_email: 'notifications@example.com',
2307
+ * tarArchive: tarArchiveBufferOrStream,
2308
+ * });
2309
+ */
2310
+ async updateCatalogUsingTarArchive(parameters = {}, networkParameters = {}) {
2311
+ try {
2312
+ const fetch = (this.options && this.options.fetch) || nodeFetch;
2313
+ const apiKey = this.options && this.options.apiKey;
2314
+ const controller = new AbortController();
2315
+ const { signal } = controller;
2316
+ const { queryParams, formData } = await createQueryParamsAndFormData(parameters);
2317
+ const formDataWithTarArchive = await addTarArchiveToFormData(parameters, formData, 'delta', apiKey);
2318
+ const requestUrl = createCatalogUrl('catalog', this.options, queryParams);
2319
+
2320
+ // Handle network timeout if specified
2321
+ helpers.applyNetworkTimeout(this.options, networkParameters, controller);
2322
+
2323
+ const response = await fetch(requestUrl, {
2324
+ method: 'PATCH',
2325
+ body: formDataWithTarArchive,
2326
+ headers: helpers.createAuthHeader(this.options),
2327
+ signal,
2328
+ });
2329
+
2330
+ if (response.ok) {
2331
+ return Promise.resolve(response.json());
2332
+ }
2333
+
2334
+ return helpers.throwHttpErrorFromResponse(new Error(), response);
2335
+ } catch (error) {
2336
+ return Promise.reject(error);
2337
+ }
2338
+ }
2339
+
2340
+ /**
2341
+ * Send patch delta tar archive to patch the current catalog
2342
+ *
2343
+ * @function patchCatalogUsingTarArchive
2344
+ * @param {object} parameters - Additional parameters for catalog details
2345
+ * @param {string} parameters.section - The section to update
2346
+ * @param {string} [parameters.notification_email] - An email address to receive an email notification if the task fails
2347
+ * @param {boolean} [parameters.force=false] - Process the catalog even if it will invalidate a large number of existing items
2348
+ * @param {file} [parameters.tarArchive] - The tar file that includes csv files
2349
+ * @param {object} [networkParameters] - Parameters relevant to the network request
2350
+ * @param {number} [networkParameters.timeout] - Request timeout (in milliseconds)
2351
+ * @returns {Promise}
2352
+ * @see https://docs.constructor.io/rest_api/full_catalog
2353
+ * @example
2354
+ * constructorio.catalog.patchCatalogUsingTarArchive({
2355
+ * section: 'Products',
2356
+ * notification_email: 'notifications@example.com',
2357
+ * tarArchive: tarArchiveBufferOrStream,
2358
+ * });
2359
+ */
2360
+ async patchCatalogUsingTarArchive(parameters = {}, networkParameters = {}) {
2361
+ try {
2362
+ const fetch = (this.options && this.options.fetch) || nodeFetch;
2363
+ const apiKey = this.options && this.options.apiKey;
2364
+ const controller = new AbortController();
2365
+ const { signal } = controller;
2366
+ const { queryParams, formData } = await createQueryParamsAndFormData(parameters);
2367
+ const formDataWithTarArchive = await addTarArchiveToFormData(parameters, formData, 'delta', apiKey);
2368
+ const requestUrl = createCatalogUrl('catalog', this.options, { ...queryParams, patch_delta: true });
2369
+
2370
+ // Handle network timeout if specified
2371
+ helpers.applyNetworkTimeout(this.options, networkParameters, controller);
2372
+
2373
+ const response = await fetch(requestUrl, {
2374
+ method: 'PATCH',
2375
+ body: formDataWithTarArchive,
2376
+ headers: helpers.createAuthHeader(this.options),
2377
+ signal,
2378
+ });
2379
+
2380
+ if (response.ok) {
2381
+ return Promise.resolve(response.json());
2382
+ }
2383
+
2384
+ return helpers.throwHttpErrorFromResponse(new Error(), response);
2385
+ } catch (error) {
2386
+ return Promise.reject(error);
2387
+ }
2388
+ }
2389
+
2210
2390
  /**
2211
2391
  * Create a facet configuration
2212
2392
  *
@@ -142,6 +142,8 @@ class Recommendations {
142
142
  return Promise.reject(e);
143
143
  }
144
144
 
145
+ Object.assign(headers, helpers.combineCustomHeaders(this.options, networkParameters));
146
+
145
147
  // Append security token as 'x-cnstrc-token' if available
146
148
  if (this.options.securityToken && typeof this.options.securityToken === 'string') {
147
149
  headers['x-cnstrc-token'] = this.options.securityToken;
@@ -209,6 +211,8 @@ class Recommendations {
209
211
  const headers = {};
210
212
  const requestUrl = `${serviceUrl}/v1/recommendation_pods?key=${apiKey}`;
211
213
 
214
+ Object.assign(headers, helpers.combineCustomHeaders(this.options, networkParameters));
215
+
212
216
  // Append security token as 'x-cnstrc-token' if available
213
217
  if (this.options.securityToken && typeof this.options.securityToken === 'string') {
214
218
  headers['x-cnstrc-token'] = this.options.securityToken;
@@ -202,6 +202,8 @@ class Search {
202
202
  return Promise.reject(e);
203
203
  }
204
204
 
205
+ Object.assign(headers, helpers.combineCustomHeaders(this.options, networkParameters));
206
+
205
207
  // Append security token as 'x-cnstrc-token' if available
206
208
  if (this.options.securityToken && typeof this.options.securityToken === 'string') {
207
209
  headers['x-cnstrc-token'] = this.options.securityToken;
@@ -61,6 +61,9 @@ class Tasks {
61
61
  const fetch = (this.options && this.options.fetch) || nodeFetch;
62
62
  const controller = new AbortController();
63
63
  const { signal } = controller;
64
+ const headers = {
65
+ 'Content-Type': 'application/json',
66
+ };
64
67
 
65
68
  if (parameters) {
66
69
  const { num_results_per_page: numResultsPerPageOld, numResultsPerPage, page, startDate, endDate, status, type } = parameters;
@@ -98,12 +101,14 @@ class Tasks {
98
101
  return Promise.reject(e);
99
102
  }
100
103
 
104
+ Object.assign(headers, helpers.combineCustomHeaders(this.options, networkParameters));
105
+
101
106
  helpers.applyNetworkTimeout(this.options, networkParameters, controller);
102
107
 
103
108
  return fetch(requestUrl, {
104
109
  method: 'GET',
105
110
  headers: {
106
- 'Content-Type': 'application/json',
111
+ ...headers,
107
112
  ...helpers.createAuthHeader(this.options),
108
113
  },
109
114
  signal,
@@ -132,6 +137,9 @@ class Tasks {
132
137
  const fetch = (this.options && this.options.fetch) || nodeFetch;
133
138
  const controller = new AbortController();
134
139
  const { signal } = controller;
140
+ const headers = {
141
+ 'Content-Type': 'application/json',
142
+ };
135
143
 
136
144
  try {
137
145
  requestUrl = createTaskUrl(`tasks/${parameters.id}`, this.options);
@@ -139,12 +147,14 @@ class Tasks {
139
147
  return Promise.reject(e);
140
148
  }
141
149
 
150
+ Object.assign(headers, helpers.combineCustomHeaders(this.options, networkParameters));
151
+
142
152
  helpers.applyNetworkTimeout(this.options, networkParameters, controller);
143
153
 
144
154
  return fetch(requestUrl, {
145
155
  method: 'GET',
146
156
  headers: {
147
- 'Content-Type': 'application/json',
157
+ ...headers,
148
158
  ...helpers.createAuthHeader(this.options),
149
159
  },
150
160
  signal,
@@ -84,6 +84,8 @@ function send(url, userParameters, networkParameters, method = 'GET', body = {})
84
84
  const { signal } = controller;
85
85
  const headers = {};
86
86
 
87
+ Object.assign(headers, helpers.combineCustomHeaders(this.options, networkParameters));
88
+
87
89
  // Append security token as 'x-cnstrc-token' if available
88
90
  if (this.options.securityToken && typeof this.options.securityToken === 'string') {
89
91
  headers['x-cnstrc-token'] = this.options.securityToken;
@@ -43,7 +43,7 @@ const utils = {
43
43
  },
44
44
 
45
45
  // Abort network request based on supplied timeout interval (in milliseconds)
46
- // - method call parameter takes precedence over global options parameter
46
+ // - Method call parameter takes precedence over global options parameter
47
47
  applyNetworkTimeout: (options = {}, networkParameters = {}, controller = undefined) => {
48
48
  const optionsTimeout = options && options.networkParameters && options.networkParameters.timeout;
49
49
  const networkParametersTimeout = networkParameters && networkParameters.timeout;
@@ -53,6 +53,15 @@ const utils = {
53
53
  setTimeout(() => controller.abort(), timeout);
54
54
  }
55
55
  },
56
+
57
+ // Combine headers from options and networkParameters
58
+ // - Method call parameter takes precedence over global options parameter
59
+ combineCustomHeaders: (options = {}, networkParameters = {}) => {
60
+ const optionsHeaders = options && options.networkParameters && options.networkParameters.headers;
61
+ const networkParametersHeaders = networkParameters && networkParameters.headers;
62
+
63
+ return { ...optionsHeaders, ...networkParametersHeaders };
64
+ },
56
65
  };
57
66
 
58
67
  module.exports = utils;