@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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,34 @@
1
1
  # @commercetools/sdk-client-v2
2
2
 
3
+ ## 1.4.1
4
+
5
+ ### Patch Changes
6
+
7
+ - [#317](https://github.com/commercetools/commercetools-sdk-typescript/pull/317) [`d921acd`](https://github.com/commercetools/commercetools-sdk-typescript/commit/d921acda35dadf135dffb53419b8825c915477b1) Thanks [@ajimae](https://github.com/ajimae)! - Allow `sdk-client` to accept request headers with `application/graphql` content-type.
8
+
9
+ * [#311](https://github.com/commercetools/commercetools-sdk-typescript/pull/311) [`588a0f9`](https://github.com/commercetools/commercetools-sdk-typescript/commit/588a0f9b981a538a16a23a449e810c56956f352c) Thanks [@ajimae](https://github.com/ajimae)! - Change token fetch window from 2 hours to 5 minutes
10
+
11
+ - [#320](https://github.com/commercetools/commercetools-sdk-typescript/pull/320) [`7510e0b`](https://github.com/commercetools/commercetools-sdk-typescript/commit/7510e0bf69cc4b63c43d0431d338502d048524aa) Thanks [@JohnMarsden24](https://github.com/JohnMarsden24)! - fix: make options for `withExistingTokenFlow` method optional
12
+
13
+ ## 1.4.0
14
+
15
+ ### Minor Changes
16
+
17
+ - [#257](https://github.com/commercetools/commercetools-sdk-typescript/pull/257) [`facc47b`](https://github.com/commercetools/commercetools-sdk-typescript/commit/facc47ba50b00056adc232d7c75a2849cdcc6689) Thanks [@ajimae](https://github.com/ajimae)! - release latest sdk
18
+
19
+ ### Patch Changes
20
+
21
+ - [#248](https://github.com/commercetools/commercetools-sdk-typescript/pull/248) [`7512c3f`](https://github.com/commercetools/commercetools-sdk-typescript/commit/7512c3f1f488645da3952f296d4f4fe3149b7fba) Thanks [@ajimae](https://github.com/ajimae)! - - add an option (`includeRequestInErrorResponse`) to include or exclude original request from error responses.
22
+
23
+ ## 1.3.0
24
+
25
+ ### Minor Changes
26
+
27
+ - [#241](https://github.com/commercetools/commercetools-sdk-typescript/pull/241) [`85f5be3`](https://github.com/commercetools/commercetools-sdk-typescript/commit/85f5be349a9b0fa46539259981bfd8d5fc2ffdc6) Thanks [@ajimae](https://github.com/ajimae)! - Releasing the TS SDK with the following changelogs
28
+
29
+ - added functionalities to extend client user agent
30
+ - custom field added to OrderFromCardDraft
31
+
3
32
  ## 1.2.0
4
33
 
5
34
  ### Minor Changes
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Commercetools TypeScript SDK client.
1
+ # Commercetools Composable Commerce TypeScript SDK client
2
2
 
3
3
  ## Usage examples
4
4
 
@@ -77,7 +77,7 @@ const client = new ClientBuilder()
77
77
 
78
78
  const apiRoot = createApiBuilderFromCtpClient(client)
79
79
 
80
- // calling the platform functions
80
+ // calling the Composable Commerce functions
81
81
  // get project details
82
82
  apiRoot
83
83
  .withProjectKey({
@@ -39,13 +39,104 @@ function validate(funcName, request, options = {
39
39
  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`);
40
40
  }
41
41
 
42
+ let _options;
43
+
42
44
  function compose(...funcs) {
43
45
  funcs = funcs.filter(func => typeof func === 'function');
44
46
  if (funcs.length === 1) return funcs[0];
45
47
  return funcs.reduce((a, b) => (...args) => a(b(...args)));
46
48
  }
47
49
 
50
+ function process$1(request, fn, processOpt) {
51
+ validate('process', request, {
52
+ allowedMethods: ['GET']
53
+ });
54
+ 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
55
+
56
+ const opt = {
57
+ total: Number.POSITIVE_INFINITY,
58
+ accumulate: true,
59
+ ...processOpt
60
+ };
61
+ return new Promise((resolve, reject) => {
62
+ let _path,
63
+ _queryString = '';
64
+
65
+ if (request && request.uri) {
66
+ const [path, queryString] = request.uri.split('?');
67
+ _path = path;
68
+ _queryString = queryString;
69
+ }
70
+
71
+ const requestQuery = { ...qs__default["default"].parse(_queryString)
72
+ };
73
+ const query = {
74
+ // defaults
75
+ limit: 20,
76
+ // merge given query params
77
+ ...requestQuery
78
+ };
79
+ let hasFirstPageBeenProcessed = false;
80
+ let itemsToGet = opt.total;
81
+
82
+ const processPage = async (lastId, acc = []) => {
83
+ // Use the lesser value between limit and itemsToGet in query
84
+ const limit = query.limit < itemsToGet ? query.limit : itemsToGet;
85
+ const originalQueryString = qs__default["default"].stringify({ ...query,
86
+ limit
87
+ });
88
+ const enhancedQuery = {
89
+ sort: 'id asc',
90
+ withTotal: false,
91
+ ...(lastId ? {
92
+ where: `id > "${lastId}"`
93
+ } : {})
94
+ };
95
+ const enhancedQueryString = qs__default["default"].stringify(enhancedQuery);
96
+ const enhancedRequest = { ...request,
97
+ uri: `${_path}?${enhancedQueryString}&${originalQueryString}`
98
+ };
99
+
100
+ try {
101
+ const payload = await createClient(_options).execute(enhancedRequest);
102
+ const {
103
+ results,
104
+ count: resultsLength
105
+ } = payload.body;
106
+
107
+ if (!resultsLength && hasFirstPageBeenProcessed) {
108
+ return resolve(acc || []);
109
+ }
110
+
111
+ const result = await Promise.resolve(fn(payload));
112
+ let accumulated;
113
+ hasFirstPageBeenProcessed = true;
114
+ if (opt.accumulate) accumulated = acc.concat(result || []);
115
+ itemsToGet -= resultsLength; // If there are no more items to get, it means the total number
116
+ // of items in the original request have been fetched so we
117
+ // resolve the promise.
118
+ // Also, if we get less results in a page then the limit set it
119
+ // means that there are no more pages and that we can finally
120
+ // resolve the promise.
121
+
122
+ if (resultsLength < query.limit || !itemsToGet) {
123
+ return resolve(accumulated || []);
124
+ }
125
+
126
+ const last = results[resultsLength - 1];
127
+ const newLastId = last && last.id;
128
+ processPage(newLastId, accumulated);
129
+ } catch (error) {
130
+ reject(error);
131
+ }
132
+ }; // Start iterating through pages
133
+
134
+
135
+ processPage();
136
+ });
137
+ }
48
138
  function createClient(options) {
139
+ _options = options;
49
140
  if (!options) throw new Error('Missing required options');
50
141
  if (options.middlewares && !Array.isArray(options.middlewares)) throw new Error('Middlewares should be an array');
51
142
  if (!options.middlewares || !Array.isArray(options.middlewares) || !options.middlewares.length) throw new Error('You need to provide at least one middleware');
@@ -53,6 +144,8 @@ function createClient(options) {
53
144
  /**
54
145
  * Given a request object,
55
146
  */
147
+ process: process$1,
148
+
56
149
  execute(request) {
57
150
  validate('exec', request);
58
151
  return new Promise((resolve, reject) => {
@@ -81,95 +174,6 @@ function createClient(options) {
81
174
  error: undefined
82
175
  });
83
176
  });
84
- },
85
-
86
- process(request, fn, processOpt) {
87
- validate('process', request, {
88
- allowedMethods: ['GET']
89
- });
90
- 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
91
-
92
- const opt = {
93
- total: Number.POSITIVE_INFINITY,
94
- accumulate: true,
95
- ...processOpt
96
- };
97
- return new Promise((resolve, reject) => {
98
- let _path,
99
- _queryString = '';
100
-
101
- if (request && request.uri) {
102
- const [path, queryString] = request.uri.split('?');
103
- _path = path;
104
- _queryString = queryString;
105
- }
106
-
107
- const requestQuery = { ...qs__default["default"].parse(_queryString)
108
- };
109
- const query = {
110
- // defaults
111
- limit: 20,
112
- // merge given query params
113
- ...requestQuery
114
- };
115
- let hasFirstPageBeenProcessed = false;
116
- let itemsToGet = opt.total;
117
-
118
- const processPage = async (lastId, acc = []) => {
119
- // Use the lesser value between limit and itemsToGet in query
120
- const limit = query.limit < itemsToGet ? query.limit : itemsToGet;
121
- const originalQueryString = qs__default["default"].stringify({ ...query,
122
- limit
123
- });
124
- const enhancedQuery = {
125
- sort: 'id asc',
126
- withTotal: false,
127
- ...(lastId ? {
128
- where: `id > "${lastId}"`
129
- } : {})
130
- };
131
- const enhancedQueryString = qs__default["default"].stringify(enhancedQuery);
132
- const enhancedRequest = { ...request,
133
- uri: `${_path}?${enhancedQueryString}&${originalQueryString}`
134
- };
135
-
136
- try {
137
- const payload = await this.execute(enhancedRequest);
138
- const {
139
- results,
140
- count: resultsLength
141
- } = payload.body;
142
-
143
- if (!resultsLength && hasFirstPageBeenProcessed) {
144
- return resolve(acc || []);
145
- }
146
-
147
- const result = await Promise.resolve(fn(payload));
148
- let accumulated;
149
- hasFirstPageBeenProcessed = true;
150
- if (opt.accumulate) accumulated = acc.concat(result || []);
151
- itemsToGet -= resultsLength; // If there are no more items to get, it means the total number
152
- // of items in the original request have been fetched so we
153
- // resolve the promise.
154
- // Also, if we get less results in a page then the limit set it
155
- // means that there are no more pages and that we can finally
156
- // resolve the promise.
157
-
158
- if (resultsLength < query.limit || !itemsToGet) {
159
- return resolve(accumulated || []);
160
- }
161
-
162
- const last = results[resultsLength - 1];
163
- const newLastId = last && last.id;
164
- processPage(newLastId, accumulated);
165
- } catch (error) {
166
- reject(error);
167
- }
168
- }; // Start iterating through pages
169
-
170
-
171
- processPage();
172
- });
173
177
  }
174
178
 
175
179
  };
@@ -228,7 +232,7 @@ function buildRequestForPasswordFlow(options) {
228
232
  */
229
233
 
230
234
  const oauthUri = options.oauthUri || `/oauth/${pKey}/customers/token`;
231
- const url = options.host.replace(/\/$/, '') + oauthUri; // encode username and password as requested by platform
235
+ const url = options.host.replace(/\/$/, '') + oauthUri; // encode username and password as requested by the system
232
236
 
233
237
  const body = `grant_type=password&username=${encodeURIComponent(username)}&password=${encodeURIComponent(password)}${scopeStr}`;
234
238
  return {
@@ -282,8 +286,8 @@ function mergeAuthHeader(token, req) {
282
286
  }
283
287
 
284
288
  function calculateExpirationTime(expiresIn) {
285
- return Date.now() + // Add a gap of 2 hours before expiration time.
286
- expiresIn * 1000 - 2 * 60 * 60 * 1000;
289
+ return Date.now() + // Add a gap of 5 minutes before expiration time.
290
+ expiresIn * 1000 - 5 * 60 * 1000;
287
291
  }
288
292
 
289
293
  async function executeRequest({
@@ -741,6 +745,7 @@ function createHttpMiddleware({
741
745
  credentialsMode,
742
746
  includeResponseHeaders,
743
747
  includeOriginalRequest,
748
+ includeRequestInErrorResponse = true,
744
749
  maskSensitiveHeaderData = true,
745
750
  enableRetry,
746
751
  timeout,
@@ -749,7 +754,8 @@ function createHttpMiddleware({
749
754
  maxRetries = 10,
750
755
  backoff = true,
751
756
  retryDelay = 200,
752
- maxDelay = Infinity
757
+ maxDelay = Infinity,
758
+ retryCodes = [503]
753
759
  } = {},
754
760
  fetch: fetcher,
755
761
  getAbortController
@@ -767,20 +773,29 @@ function createHttpMiddleware({
767
773
  fetchFunction = fetch;
768
774
  }
769
775
 
776
+ if (!Array.isArray(retryCodes)) {
777
+ throw new Error('`retryCodes` option must be an array of retry status (error) codes.');
778
+ }
779
+
770
780
  return next => (request, response) => {
771
781
  let abortController;
772
782
  if (timeout || getAbortController) abortController = (getAbortController ? getAbortController() : null) || new AbortController();
773
783
  const url = host.replace(/\/$/, '') + request.uri;
774
- const body = typeof request.body === 'string' || Buffer.isBuffer(request.body) ? request.body : // NOTE: `stringify` of `null` gives the String('null')
775
- JSON.stringify(request.body || undefined);
776
784
  const requestHeader = { ...request.headers
777
- };
785
+ }; // Unset the content-type header if explicitly asked to (passing `null` as value).
778
786
 
779
- if (!Object.prototype.hasOwnProperty.call(requestHeader, 'Content-Type')) {
780
- requestHeader['Content-Type'] = 'application/json';
787
+ if (requestHeader['Content-Type'] === null) {
788
+ delete requestHeader['Content-Type'];
781
789
  }
782
790
 
783
- if (body) {
791
+ if (!(Object.prototype.hasOwnProperty.call(requestHeader, 'Content-Type') || Object.prototype.hasOwnProperty.call(requestHeader, 'content-type'))) {
792
+ requestHeader['Content-Type'] = 'application/json';
793
+ } // Ensure body is a string if content type is application/json
794
+
795
+
796
+ 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);
797
+
798
+ if (body && (typeof body === 'string' || Buffer.isBuffer(body))) {
784
799
  requestHeader['Content-Length'] = Buffer.byteLength(body).toString();
785
800
  }
786
801
 
@@ -849,15 +864,25 @@ function createHttpMiddleware({
849
864
  next(request, parsedResponse);
850
865
  });
851
866
  return;
852
- }
853
-
854
- if (res.status === 503 && enableRetry) if (retryCount < maxRetries) {
855
- setTimeout(executeFetch, calcDelayDuration(retryCount, retryDelay, maxRetries, backoff, maxDelay));
856
- retryCount += 1;
857
- return;
858
- } // Server responded with an error. Try to parse it as JSON, then
867
+ } // if (res.status === 503 && enableRetry)
868
+ // if (retryCount < maxRetries) {
869
+ // setTimeout(
870
+ // executeFetch,
871
+ // calcDelayDuration(
872
+ // retryCount,
873
+ // retryDelay,
874
+ // maxRetries,
875
+ // backoff,
876
+ // maxDelay
877
+ // )
878
+ // )
879
+ // retryCount += 1
880
+ // return
881
+ // }
882
+ // Server responded with an error. Try to parse it as JSON, then
859
883
  // return a proper error type with all necessary meta information.
860
884
 
885
+
861
886
  res.text().then(text => {
862
887
  // Try to parse the error response as JSON
863
888
  let parsed;
@@ -870,7 +895,9 @@ function createHttpMiddleware({
870
895
 
871
896
  const error = createError({
872
897
  statusCode: res.status,
873
- originalRequest: request,
898
+ ...(includeRequestInErrorResponse ? {
899
+ originalRequest: request
900
+ } : {}),
874
901
  retryCount,
875
902
  headers: parseHeaders(res.headers),
876
903
  ...(typeof parsed === 'object' ? {
@@ -881,6 +908,15 @@ function createHttpMiddleware({
881
908
  body: parsed
882
909
  })
883
910
  });
911
+
912
+ if (enableRetry && (retryCodes.indexOf(error.statusCode) !== -1 || (retryCodes === null || retryCodes === void 0 ? void 0 : retryCodes.indexOf(error.message)) !== -1)) {
913
+ if (retryCount < maxRetries) {
914
+ setTimeout(executeFetch, calcDelayDuration(retryCount, retryDelay, maxRetries, backoff, maxDelay));
915
+ retryCount += 1;
916
+ return;
917
+ }
918
+ }
919
+
884
920
  maskAuthData(error.originalRequest, maskSensitiveHeaderData); // Let the final resolver to reject the promise
885
921
 
886
922
  const parsedResponse = { ...response,
@@ -896,8 +932,9 @@ function createHttpMiddleware({
896
932
  retryCount += 1;
897
933
  return;
898
934
  }
899
- const error = new NetworkError(e.message, {
900
- originalRequest: request,
935
+ const error = new NetworkError(e.message, { ...(includeRequestInErrorResponse ? {
936
+ originalRequest: request
937
+ } : {}),
901
938
  retryCount
902
939
  });
903
940
  maskAuthData(error.originalRequest, maskSensitiveHeaderData);
@@ -982,10 +1019,11 @@ function createQueueMiddleware({
982
1019
 
983
1020
  var packageJson = {
984
1021
  name: "@commercetools/sdk-client-v2",
985
- version: "1.2.0",
986
- description: "commercetools TypeScript SDK client.",
1022
+ version: "1.4.1",
1023
+ description: "commercetools Composable Commerce TypeScript SDK client.",
987
1024
  keywords: [
988
1025
  "commercetools",
1026
+ "composable commerce",
989
1027
  "sdk",
990
1028
  "typescript",
991
1029
  "client",
@@ -1029,10 +1067,10 @@ var packageJson = {
1029
1067
  devDependencies: {
1030
1068
  "abort-controller": "3.0.0",
1031
1069
  "common-tags": "1.8.2",
1032
- dotenv: "16.0.0",
1033
- jest: "27.4.7",
1070
+ dotenv: "16.0.1",
1071
+ jest: "28.1.1",
1034
1072
  nock: "12.0.3",
1035
- "organize-imports-cli": "0.9.0"
1073
+ "organize-imports-cli": "0.10.0"
1036
1074
  },
1037
1075
  scripts: {
1038
1076
  organize_imports: "find src -type f -name '*.ts' | xargs organize-imports-cli",
@@ -1073,12 +1111,14 @@ function createUserAgent(options) {
1073
1111
  let contactInfo = null;
1074
1112
  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
1075
1113
 
1076
- const systemInfo = getSystemInfo();
1077
- return [baseInfo, systemInfo, libraryInfo, contactInfo].filter(Boolean).join(' ');
1114
+ const systemInfo = getSystemInfo(); // customName
1115
+
1116
+ const customAgent = options.customAgent || '';
1117
+ return [baseInfo, systemInfo, libraryInfo, contactInfo, customAgent].filter(Boolean).join(' ');
1078
1118
  }
1079
1119
 
1080
- function createUserAgentMiddleware() {
1081
- const userAgent = createUserAgent({
1120
+ function createUserAgentMiddleware(options) {
1121
+ const userAgent = createUserAgent({ ...options,
1082
1122
  name: `commercetools-sdk-javascript-v2/${packageJson.version}`
1083
1123
  });
1084
1124
  return next => (request, response) => {
@@ -1219,8 +1259,8 @@ class ClientBuilder {
1219
1259
  return this;
1220
1260
  }
1221
1261
 
1222
- withUserAgentMiddleware() {
1223
- this.userAgentMiddleware = createUserAgentMiddleware();
1262
+ withUserAgentMiddleware(options) {
1263
+ this.userAgentMiddleware = createUserAgentMiddleware(options);
1224
1264
  return this;
1225
1265
  }
1226
1266
 
@@ -1250,9 +1290,9 @@ class ClientBuilder {
1250
1290
  if (this.correlationIdMiddleware) middlewares.push(this.correlationIdMiddleware);
1251
1291
  if (this.userAgentMiddleware) middlewares.push(this.userAgentMiddleware);
1252
1292
  if (this.authMiddleware) middlewares.push(this.authMiddleware);
1253
- if (this.loggerMiddleware) middlewares.push(this.loggerMiddleware);
1254
1293
  if (this.queueMiddleware) middlewares.push(this.queueMiddleware);
1255
1294
  if (this.httpMiddleware) middlewares.push(this.httpMiddleware);
1295
+ if (this.loggerMiddleware) middlewares.push(this.loggerMiddleware);
1256
1296
  return createClient({
1257
1297
  middlewares
1258
1298
  });
@@ -1261,6 +1301,7 @@ class ClientBuilder {
1261
1301
  }
1262
1302
 
1263
1303
  exports.ClientBuilder = ClientBuilder;
1304
+ exports.Process = process$1;
1264
1305
  exports.createAuthForAnonymousSessionFlow = createAuthMiddlewareForAnonymousSessionFlow$1;
1265
1306
  exports.createAuthForClientCredentialsFlow = createAuthMiddlewareForClientCredentialsFlow$1;
1266
1307
  exports.createAuthForPasswordFlow = createAuthMiddlewareForPasswordFlow$1;