@commercetools/sdk-client-v2 1.2.0 → 1.4.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.
@@ -30,13 +30,104 @@ function validate(funcName, request, options = {
30
30
  if (!options.allowedMethods.includes(request.method)) throw new Error(`The "${funcName}" Request object requires a valid method. See https://commercetools.github.io/nodejs/sdk/Glossary.html#clientrequest`);
31
31
  }
32
32
 
33
+ let _options;
34
+
33
35
  function compose(...funcs) {
34
36
  funcs = funcs.filter(func => typeof func === 'function');
35
37
  if (funcs.length === 1) return funcs[0];
36
38
  return funcs.reduce((a, b) => (...args) => a(b(...args)));
37
39
  }
38
40
 
41
+ function process$1(request, fn, processOpt) {
42
+ validate('process', request, {
43
+ allowedMethods: ['GET']
44
+ });
45
+ if (typeof fn !== 'function') throw new Error('The "process" function accepts a "Function" as a second argument that returns a Promise. See https://commercetools.github.io/nodejs/sdk/api/sdkClient.html#processrequest-processfn-options'); // Set default process options
46
+
47
+ const opt = {
48
+ total: Number.POSITIVE_INFINITY,
49
+ accumulate: true,
50
+ ...processOpt
51
+ };
52
+ return new Promise((resolve, reject) => {
53
+ let _path,
54
+ _queryString = '';
55
+
56
+ if (request && request.uri) {
57
+ const [path, queryString] = request.uri.split('?');
58
+ _path = path;
59
+ _queryString = queryString;
60
+ }
61
+
62
+ const requestQuery = { ...qs.parse(_queryString)
63
+ };
64
+ const query = {
65
+ // defaults
66
+ limit: 20,
67
+ // merge given query params
68
+ ...requestQuery
69
+ };
70
+ let hasFirstPageBeenProcessed = false;
71
+ let itemsToGet = opt.total;
72
+
73
+ const processPage = async (lastId, acc = []) => {
74
+ // Use the lesser value between limit and itemsToGet in query
75
+ const limit = query.limit < itemsToGet ? query.limit : itemsToGet;
76
+ const originalQueryString = qs.stringify({ ...query,
77
+ limit
78
+ });
79
+ const enhancedQuery = {
80
+ sort: 'id asc',
81
+ withTotal: false,
82
+ ...(lastId ? {
83
+ where: `id > "${lastId}"`
84
+ } : {})
85
+ };
86
+ const enhancedQueryString = qs.stringify(enhancedQuery);
87
+ const enhancedRequest = { ...request,
88
+ uri: `${_path}?${enhancedQueryString}&${originalQueryString}`
89
+ };
90
+
91
+ try {
92
+ const payload = await createClient(_options).execute(enhancedRequest);
93
+ const {
94
+ results,
95
+ count: resultsLength
96
+ } = payload.body;
97
+
98
+ if (!resultsLength && hasFirstPageBeenProcessed) {
99
+ return resolve(acc || []);
100
+ }
101
+
102
+ const result = await Promise.resolve(fn(payload));
103
+ let accumulated;
104
+ hasFirstPageBeenProcessed = true;
105
+ if (opt.accumulate) accumulated = acc.concat(result || []);
106
+ itemsToGet -= resultsLength; // If there are no more items to get, it means the total number
107
+ // of items in the original request have been fetched so we
108
+ // resolve the promise.
109
+ // Also, if we get less results in a page then the limit set it
110
+ // means that there are no more pages and that we can finally
111
+ // resolve the promise.
112
+
113
+ if (resultsLength < query.limit || !itemsToGet) {
114
+ return resolve(accumulated || []);
115
+ }
116
+
117
+ const last = results[resultsLength - 1];
118
+ const newLastId = last && last.id;
119
+ processPage(newLastId, accumulated);
120
+ } catch (error) {
121
+ reject(error);
122
+ }
123
+ }; // Start iterating through pages
124
+
125
+
126
+ processPage();
127
+ });
128
+ }
39
129
  function createClient(options) {
130
+ _options = options;
40
131
  if (!options) throw new Error('Missing required options');
41
132
  if (options.middlewares && !Array.isArray(options.middlewares)) throw new Error('Middlewares should be an array');
42
133
  if (!options.middlewares || !Array.isArray(options.middlewares) || !options.middlewares.length) throw new Error('You need to provide at least one middleware');
@@ -44,6 +135,8 @@ function createClient(options) {
44
135
  /**
45
136
  * Given a request object,
46
137
  */
138
+ process: process$1,
139
+
47
140
  execute(request) {
48
141
  validate('exec', request);
49
142
  return new Promise((resolve, reject) => {
@@ -72,95 +165,6 @@ function createClient(options) {
72
165
  error: undefined
73
166
  });
74
167
  });
75
- },
76
-
77
- process(request, fn, processOpt) {
78
- validate('process', request, {
79
- allowedMethods: ['GET']
80
- });
81
- if (typeof fn !== 'function') throw new Error('The "process" function accepts a "Function" as a second argument that returns a Promise. See https://commercetools.github.io/nodejs/sdk/api/sdkClient.html#processrequest-processfn-options'); // Set default process options
82
-
83
- const opt = {
84
- total: Number.POSITIVE_INFINITY,
85
- accumulate: true,
86
- ...processOpt
87
- };
88
- return new Promise((resolve, reject) => {
89
- let _path,
90
- _queryString = '';
91
-
92
- if (request && request.uri) {
93
- const [path, queryString] = request.uri.split('?');
94
- _path = path;
95
- _queryString = queryString;
96
- }
97
-
98
- const requestQuery = { ...qs.parse(_queryString)
99
- };
100
- const query = {
101
- // defaults
102
- limit: 20,
103
- // merge given query params
104
- ...requestQuery
105
- };
106
- let hasFirstPageBeenProcessed = false;
107
- let itemsToGet = opt.total;
108
-
109
- const processPage = async (lastId, acc = []) => {
110
- // Use the lesser value between limit and itemsToGet in query
111
- const limit = query.limit < itemsToGet ? query.limit : itemsToGet;
112
- const originalQueryString = qs.stringify({ ...query,
113
- limit
114
- });
115
- const enhancedQuery = {
116
- sort: 'id asc',
117
- withTotal: false,
118
- ...(lastId ? {
119
- where: `id > "${lastId}"`
120
- } : {})
121
- };
122
- const enhancedQueryString = qs.stringify(enhancedQuery);
123
- const enhancedRequest = { ...request,
124
- uri: `${_path}?${enhancedQueryString}&${originalQueryString}`
125
- };
126
-
127
- try {
128
- const payload = await this.execute(enhancedRequest);
129
- const {
130
- results,
131
- count: resultsLength
132
- } = payload.body;
133
-
134
- if (!resultsLength && hasFirstPageBeenProcessed) {
135
- return resolve(acc || []);
136
- }
137
-
138
- const result = await Promise.resolve(fn(payload));
139
- let accumulated;
140
- hasFirstPageBeenProcessed = true;
141
- if (opt.accumulate) accumulated = acc.concat(result || []);
142
- itemsToGet -= resultsLength; // If there are no more items to get, it means the total number
143
- // of items in the original request have been fetched so we
144
- // resolve the promise.
145
- // Also, if we get less results in a page then the limit set it
146
- // means that there are no more pages and that we can finally
147
- // resolve the promise.
148
-
149
- if (resultsLength < query.limit || !itemsToGet) {
150
- return resolve(accumulated || []);
151
- }
152
-
153
- const last = results[resultsLength - 1];
154
- const newLastId = last && last.id;
155
- processPage(newLastId, accumulated);
156
- } catch (error) {
157
- reject(error);
158
- }
159
- }; // Start iterating through pages
160
-
161
-
162
- processPage();
163
- });
164
168
  }
165
169
 
166
170
  };
@@ -219,7 +223,7 @@ function buildRequestForPasswordFlow(options) {
219
223
  */
220
224
 
221
225
  const oauthUri = options.oauthUri || `/oauth/${pKey}/customers/token`;
222
- const url = options.host.replace(/\/$/, '') + oauthUri; // encode username and password as requested by platform
226
+ const url = options.host.replace(/\/$/, '') + oauthUri; // encode username and password as requested by the system
223
227
 
224
228
  const body = `grant_type=password&username=${encodeURIComponent(username)}&password=${encodeURIComponent(password)}${scopeStr}`;
225
229
  return {
@@ -273,8 +277,8 @@ function mergeAuthHeader(token, req) {
273
277
  }
274
278
 
275
279
  function calculateExpirationTime(expiresIn) {
276
- return Date.now() + // Add a gap of 2 hours before expiration time.
277
- expiresIn * 1000 - 2 * 60 * 60 * 1000;
280
+ return Date.now() + // Add a gap of 5 minutes before expiration time.
281
+ expiresIn * 1000 - 5 * 60 * 1000;
278
282
  }
279
283
 
280
284
  async function executeRequest({
@@ -732,6 +736,7 @@ function createHttpMiddleware({
732
736
  credentialsMode,
733
737
  includeResponseHeaders,
734
738
  includeOriginalRequest,
739
+ includeRequestInErrorResponse = true,
735
740
  maskSensitiveHeaderData = true,
736
741
  enableRetry,
737
742
  timeout,
@@ -740,7 +745,8 @@ function createHttpMiddleware({
740
745
  maxRetries = 10,
741
746
  backoff = true,
742
747
  retryDelay = 200,
743
- maxDelay = Infinity
748
+ maxDelay = Infinity,
749
+ retryCodes = [503]
744
750
  } = {},
745
751
  fetch: fetcher,
746
752
  getAbortController
@@ -758,20 +764,29 @@ function createHttpMiddleware({
758
764
  fetchFunction = fetch;
759
765
  }
760
766
 
767
+ if (!Array.isArray(retryCodes)) {
768
+ throw new Error('`retryCodes` option must be an array of retry status (error) codes.');
769
+ }
770
+
761
771
  return next => (request, response) => {
762
772
  let abortController;
763
773
  if (timeout || getAbortController) abortController = (getAbortController ? getAbortController() : null) || new AbortController();
764
774
  const url = host.replace(/\/$/, '') + request.uri;
765
- const body = typeof request.body === 'string' || Buffer.isBuffer(request.body) ? request.body : // NOTE: `stringify` of `null` gives the String('null')
766
- JSON.stringify(request.body || undefined);
767
775
  const requestHeader = { ...request.headers
768
- };
776
+ }; // Unset the content-type header if explicitly asked to (passing `null` as value).
769
777
 
770
- if (!Object.prototype.hasOwnProperty.call(requestHeader, 'Content-Type')) {
771
- requestHeader['Content-Type'] = 'application/json';
778
+ if (requestHeader['Content-Type'] === null) {
779
+ delete requestHeader['Content-Type'];
772
780
  }
773
781
 
774
- if (body) {
782
+ if (!(Object.prototype.hasOwnProperty.call(requestHeader, 'Content-Type') || Object.prototype.hasOwnProperty.call(requestHeader, 'content-type'))) {
783
+ requestHeader['Content-Type'] = 'application/json';
784
+ } // Ensure body is a string if content type is application/json
785
+
786
+
787
+ const body = ['application/json', 'application/graphql'].indexOf(requestHeader['Content-Type']) > -1 && typeof request.body === 'string' || Buffer.isBuffer(request.body) ? request.body : JSON.stringify(request.body || undefined);
788
+
789
+ if (body && (typeof body === 'string' || Buffer.isBuffer(body))) {
775
790
  requestHeader['Content-Length'] = Buffer.byteLength(body).toString();
776
791
  }
777
792
 
@@ -840,15 +855,25 @@ function createHttpMiddleware({
840
855
  next(request, parsedResponse);
841
856
  });
842
857
  return;
843
- }
844
-
845
- if (res.status === 503 && enableRetry) if (retryCount < maxRetries) {
846
- setTimeout(executeFetch, calcDelayDuration(retryCount, retryDelay, maxRetries, backoff, maxDelay));
847
- retryCount += 1;
848
- return;
849
- } // Server responded with an error. Try to parse it as JSON, then
858
+ } // if (res.status === 503 && enableRetry)
859
+ // if (retryCount < maxRetries) {
860
+ // setTimeout(
861
+ // executeFetch,
862
+ // calcDelayDuration(
863
+ // retryCount,
864
+ // retryDelay,
865
+ // maxRetries,
866
+ // backoff,
867
+ // maxDelay
868
+ // )
869
+ // )
870
+ // retryCount += 1
871
+ // return
872
+ // }
873
+ // Server responded with an error. Try to parse it as JSON, then
850
874
  // return a proper error type with all necessary meta information.
851
875
 
876
+
852
877
  res.text().then(text => {
853
878
  // Try to parse the error response as JSON
854
879
  let parsed;
@@ -861,7 +886,9 @@ function createHttpMiddleware({
861
886
 
862
887
  const error = createError({
863
888
  statusCode: res.status,
864
- originalRequest: request,
889
+ ...(includeRequestInErrorResponse ? {
890
+ originalRequest: request
891
+ } : {}),
865
892
  retryCount,
866
893
  headers: parseHeaders(res.headers),
867
894
  ...(typeof parsed === 'object' ? {
@@ -872,6 +899,15 @@ function createHttpMiddleware({
872
899
  body: parsed
873
900
  })
874
901
  });
902
+
903
+ if (enableRetry && (retryCodes.indexOf(error.statusCode) !== -1 || (retryCodes === null || retryCodes === void 0 ? void 0 : retryCodes.indexOf(error.message)) !== -1)) {
904
+ if (retryCount < maxRetries) {
905
+ setTimeout(executeFetch, calcDelayDuration(retryCount, retryDelay, maxRetries, backoff, maxDelay));
906
+ retryCount += 1;
907
+ return;
908
+ }
909
+ }
910
+
875
911
  maskAuthData(error.originalRequest, maskSensitiveHeaderData); // Let the final resolver to reject the promise
876
912
 
877
913
  const parsedResponse = { ...response,
@@ -887,8 +923,9 @@ function createHttpMiddleware({
887
923
  retryCount += 1;
888
924
  return;
889
925
  }
890
- const error = new NetworkError(e.message, {
891
- originalRequest: request,
926
+ const error = new NetworkError(e.message, { ...(includeRequestInErrorResponse ? {
927
+ originalRequest: request
928
+ } : {}),
892
929
  retryCount
893
930
  });
894
931
  maskAuthData(error.originalRequest, maskSensitiveHeaderData);
@@ -973,10 +1010,11 @@ function createQueueMiddleware({
973
1010
 
974
1011
  var packageJson = {
975
1012
  name: "@commercetools/sdk-client-v2",
976
- version: "1.2.0",
977
- description: "commercetools TypeScript SDK client.",
1013
+ version: "1.4.1",
1014
+ description: "commercetools Composable Commerce TypeScript SDK client.",
978
1015
  keywords: [
979
1016
  "commercetools",
1017
+ "composable commerce",
980
1018
  "sdk",
981
1019
  "typescript",
982
1020
  "client",
@@ -1020,10 +1058,10 @@ var packageJson = {
1020
1058
  devDependencies: {
1021
1059
  "abort-controller": "3.0.0",
1022
1060
  "common-tags": "1.8.2",
1023
- dotenv: "16.0.0",
1024
- jest: "27.4.7",
1061
+ dotenv: "16.0.1",
1062
+ jest: "28.1.1",
1025
1063
  nock: "12.0.3",
1026
- "organize-imports-cli": "0.9.0"
1064
+ "organize-imports-cli": "0.10.0"
1027
1065
  },
1028
1066
  scripts: {
1029
1067
  organize_imports: "find src -type f -name '*.ts' | xargs organize-imports-cli",
@@ -1064,12 +1102,14 @@ function createUserAgent(options) {
1064
1102
  let contactInfo = null;
1065
1103
  if (options.contactUrl && !options.contactEmail) contactInfo = `(+${options.contactUrl})`;else if (!options.contactUrl && options.contactEmail) contactInfo = `(+${options.contactEmail})`;else if (options.contactUrl && options.contactEmail) contactInfo = `(+${options.contactUrl}; +${options.contactEmail})`; // System info
1066
1104
 
1067
- const systemInfo = getSystemInfo();
1068
- return [baseInfo, systemInfo, libraryInfo, contactInfo].filter(Boolean).join(' ');
1105
+ const systemInfo = getSystemInfo(); // customName
1106
+
1107
+ const customAgent = options.customAgent || '';
1108
+ return [baseInfo, systemInfo, libraryInfo, contactInfo, customAgent].filter(Boolean).join(' ');
1069
1109
  }
1070
1110
 
1071
- function createUserAgentMiddleware() {
1072
- const userAgent = createUserAgent({
1111
+ function createUserAgentMiddleware(options) {
1112
+ const userAgent = createUserAgent({ ...options,
1073
1113
  name: `commercetools-sdk-javascript-v2/${packageJson.version}`
1074
1114
  });
1075
1115
  return next => (request, response) => {
@@ -1210,8 +1250,8 @@ class ClientBuilder {
1210
1250
  return this;
1211
1251
  }
1212
1252
 
1213
- withUserAgentMiddleware() {
1214
- this.userAgentMiddleware = createUserAgentMiddleware();
1253
+ withUserAgentMiddleware(options) {
1254
+ this.userAgentMiddleware = createUserAgentMiddleware(options);
1215
1255
  return this;
1216
1256
  }
1217
1257
 
@@ -1241,9 +1281,9 @@ class ClientBuilder {
1241
1281
  if (this.correlationIdMiddleware) middlewares.push(this.correlationIdMiddleware);
1242
1282
  if (this.userAgentMiddleware) middlewares.push(this.userAgentMiddleware);
1243
1283
  if (this.authMiddleware) middlewares.push(this.authMiddleware);
1244
- if (this.loggerMiddleware) middlewares.push(this.loggerMiddleware);
1245
1284
  if (this.queueMiddleware) middlewares.push(this.queueMiddleware);
1246
1285
  if (this.httpMiddleware) middlewares.push(this.httpMiddleware);
1286
+ if (this.loggerMiddleware) middlewares.push(this.loggerMiddleware);
1247
1287
  return createClient({
1248
1288
  middlewares
1249
1289
  });
@@ -1251,4 +1291,4 @@ class ClientBuilder {
1251
1291
 
1252
1292
  }
1253
1293
 
1254
- export { ClientBuilder, createAuthMiddlewareForAnonymousSessionFlow$1 as createAuthForAnonymousSessionFlow, createAuthMiddlewareForClientCredentialsFlow$1 as createAuthForClientCredentialsFlow, createAuthMiddlewareForPasswordFlow$1 as createAuthForPasswordFlow, createAuthMiddlewareForRefreshTokenFlow$1 as createAuthForRefreshTokenFlow, createAuthMiddlewareWithExistingToken$1 as createAuthWithExistingToken, createClient, createCorrelationIdMiddleware, createHttpMiddleware as createHttpClient, createLoggerMiddleware, createQueueMiddleware, createUserAgentMiddleware, getErrorByCode };
1294
+ export { ClientBuilder, process$1 as Process, createAuthMiddlewareForAnonymousSessionFlow$1 as createAuthForAnonymousSessionFlow, createAuthMiddlewareForClientCredentialsFlow$1 as createAuthForClientCredentialsFlow, createAuthMiddlewareForPasswordFlow$1 as createAuthForPasswordFlow, createAuthMiddlewareForRefreshTokenFlow$1 as createAuthForRefreshTokenFlow, createAuthMiddlewareWithExistingToken$1 as createAuthWithExistingToken, createClient, createCorrelationIdMiddleware, createHttpMiddleware as createHttpClient, createLoggerMiddleware, createQueueMiddleware, createUserAgentMiddleware, getErrorByCode };