@webex/plugin-meetings 3.12.0-next.22 → 3.12.0-next.23

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.
@@ -178,7 +178,7 @@ var AIEnableRequest = _webexCore.WebexPlugin.extend({
178
178
  method: _constants.HTTP_VERBS.PUT
179
179
  });
180
180
  },
181
- version: "3.12.0-next.22"
181
+ version: "3.12.0-next.23"
182
182
  });
183
183
  var _default = exports.default = AIEnableRequest;
184
184
  //# sourceMappingURL=index.js.map
@@ -209,7 +209,7 @@ var Breakout = _webexCore.WebexPlugin.extend({
209
209
  sessionId: this.sessionId
210
210
  });
211
211
  },
212
- version: "3.12.0-next.22"
212
+ version: "3.12.0-next.23"
213
213
  });
214
214
  var _default = exports.default = Breakout;
215
215
  //# sourceMappingURL=breakout.js.map
@@ -1109,7 +1109,7 @@ var Breakouts = _webexCore.WebexPlugin.extend({
1109
1109
  this.trigger(_constants.BREAKOUTS.EVENTS.ASK_RETURN_TO_MAIN);
1110
1110
  }
1111
1111
  },
1112
- version: "3.12.0-next.22"
1112
+ version: "3.12.0-next.23"
1113
1113
  });
1114
1114
  var _default = exports.default = Breakouts;
1115
1115
  //# sourceMappingURL=index.js.map
@@ -31,14 +31,12 @@ var LocusRetryStatusInterceptor = exports.default = /*#__PURE__*/function (_Inte
31
31
  (0, _inherits2.default)(LocusRetryStatusInterceptor, _Interceptor);
32
32
  return (0, _createClass2.default)(LocusRetryStatusInterceptor, [{
33
33
  key: "onResponseError",
34
- value:
35
- /**
36
- * Handle response errors
37
- * @param {Object} options
38
- * @param {WebexHttpError} reason
39
- * @returns {Promise<WebexHttpError>}
40
- */
41
- function onResponseError(options, reason) {
34
+ value: function onResponseError(options, reason) {
35
+ // Don't retry /hashtree or /sync calls for 429 or any 5xx — during a sync storm retries
36
+ // make things worse. The normal sync timers will handle recovery for these endpoints.
37
+ if ((reason.statusCode === 429 || reason.statusCode >= 500) && LocusRetryStatusInterceptor.isLocusHashtreeOrSync(options.uri)) {
38
+ return _promise.default.reject(reason);
39
+ }
42
40
  if ((reason.statusCode === 503 || reason.statusCode === 429) && options.uri.includes('locus')) {
43
41
  var hasRetriedLocusRequest = rateLimitExpiryTime.get(this);
44
42
  var retryAfterTime = options.headers['retry-after'] || 2000;
@@ -87,6 +85,23 @@ var LocusRetryStatusInterceptor = exports.default = /*#__PURE__*/function (_Inte
87
85
  webex: this
88
86
  });
89
87
  }
88
+
89
+ /**
90
+ * Check whether a URI is a Locus /hashtree or /sync endpoint.
91
+ * @param {string} uri
92
+ * @returns {boolean}
93
+ */
94
+ }, {
95
+ key: "isLocusHashtreeOrSync",
96
+ value: function isLocusHashtreeOrSync(uri) {
97
+ try {
98
+ var _URL = new URL(uri),
99
+ pathname = _URL.pathname;
100
+ return pathname.includes('/locus/') && (pathname.endsWith('/hashtree') || pathname.endsWith('/sync'));
101
+ } catch (_unused) {
102
+ return false;
103
+ }
104
+ }
90
105
  }]);
91
106
  }(_httpCore.Interceptor);
92
107
  //# sourceMappingURL=locusRetry.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["_httpCore","require","_callSuper","t","o","e","_getPrototypeOf2","default","_possibleConstructorReturn2","_isNativeReflectConstruct","_Reflect$construct","constructor","apply","Boolean","prototype","valueOf","call","rateLimitExpiryTime","_weakMap","LocusRetryStatusInterceptor","exports","_Interceptor","_classCallCheck2","arguments","_inherits2","_createClass2","key","value","onResponseError","options","reason","statusCode","uri","includes","hasRetriedLocusRequest","get","retryAfterTime","headers","set","_promise","reject","handleRetryRequestLocusServiceError","_this","resolve","timeout","setTimeout","clearTimeout","webex","request","method","body","then","catch","create","Interceptor"],"sources":["locusRetry.ts"],"sourcesContent":["/*!\n * Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.\n */\n\nimport {Interceptor} from '@webex/http-core';\n\nconst rateLimitExpiryTime = new WeakMap();\n/**\n * @class\n */\nexport default class LocusRetryStatusInterceptor extends Interceptor {\n /**\n * @returns {LocusRetryStatusInterceptor}\n */\n static create() {\n // @ts-ignore\n return new LocusRetryStatusInterceptor({webex: this});\n }\n\n /**\n * Handle response errors\n * @param {Object} options\n * @param {WebexHttpError} reason\n * @returns {Promise<WebexHttpError>}\n */\n onResponseError(options, reason) {\n if ((reason.statusCode === 503 || reason.statusCode === 429) && options.uri.includes('locus')) {\n const hasRetriedLocusRequest = rateLimitExpiryTime.get(this);\n const retryAfterTime = options.headers['retry-after'] || 2000;\n\n if (hasRetriedLocusRequest) {\n rateLimitExpiryTime.set(this, false);\n\n return Promise.reject(options);\n }\n rateLimitExpiryTime.set(this, true);\n\n return this.handleRetryRequestLocusServiceError(options, retryAfterTime);\n }\n\n return Promise.reject(reason);\n }\n\n /**\n * Handle retries for locus service unavailable errors\n * @param {Object} options associated with the request\n * @param {number} retryAfterTime retry after time in milliseconds\n * @returns {Promise}\n */\n handleRetryRequestLocusServiceError(options, retryAfterTime) {\n return new Promise((resolve, reject) => {\n const timeout = setTimeout(() => {\n clearTimeout(timeout);\n\n // @ts-ignore\n this.webex\n .request({\n method: options.method,\n uri: options.uri,\n body: options.body,\n })\n .then(resolve)\n .catch(reject);\n }, retryAfterTime);\n });\n }\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["_httpCore","require","_callSuper","t","o","e","_getPrototypeOf2","default","_possibleConstructorReturn2","_isNativeReflectConstruct","_Reflect$construct","constructor","apply","Boolean","prototype","valueOf","call","rateLimitExpiryTime","_weakMap","LocusRetryStatusInterceptor","exports","_Interceptor","_classCallCheck2","arguments","_inherits2","_createClass2","key","value","onResponseError","options","reason","statusCode","isLocusHashtreeOrSync","uri","_promise","reject","includes","hasRetriedLocusRequest","get","retryAfterTime","headers","set","handleRetryRequestLocusServiceError","_this","resolve","timeout","setTimeout","clearTimeout","webex","request","method","body","then","catch","create","_URL","URL","pathname","endsWith","_unused","Interceptor"],"sources":["locusRetry.ts"],"sourcesContent":["/*!\n * Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.\n */\n\nimport {Interceptor} from '@webex/http-core';\n\nconst rateLimitExpiryTime = new WeakMap();\n/**\n * @class\n */\nexport default class LocusRetryStatusInterceptor extends Interceptor {\n /**\n * @returns {LocusRetryStatusInterceptor}\n */\n static create() {\n // @ts-ignore\n return new LocusRetryStatusInterceptor({webex: this});\n }\n\n /**\n * Check whether a URI is a Locus /hashtree or /sync endpoint.\n * @param {string} uri\n * @returns {boolean}\n */\n private static isLocusHashtreeOrSync(uri: string): boolean {\n try {\n const {pathname} = new URL(uri);\n\n return (\n pathname.includes('/locus/') &&\n (pathname.endsWith('/hashtree') || pathname.endsWith('/sync'))\n );\n } catch {\n return false;\n }\n }\n\n onResponseError(options, reason) {\n // Don't retry /hashtree or /sync calls for 429 or any 5xx — during a sync storm retries\n // make things worse. The normal sync timers will handle recovery for these endpoints.\n if (\n (reason.statusCode === 429 || reason.statusCode >= 500) &&\n LocusRetryStatusInterceptor.isLocusHashtreeOrSync(options.uri)\n ) {\n return Promise.reject(reason);\n }\n\n if ((reason.statusCode === 503 || reason.statusCode === 429) && options.uri.includes('locus')) {\n const hasRetriedLocusRequest = rateLimitExpiryTime.get(this);\n const retryAfterTime = options.headers['retry-after'] || 2000;\n\n if (hasRetriedLocusRequest) {\n rateLimitExpiryTime.set(this, false);\n\n return Promise.reject(options);\n }\n rateLimitExpiryTime.set(this, true);\n\n return this.handleRetryRequestLocusServiceError(options, retryAfterTime);\n }\n\n return Promise.reject(reason);\n }\n\n /**\n * Handle retries for locus service unavailable errors\n * @param {Object} options associated with the request\n * @param {number} retryAfterTime retry after time in milliseconds\n * @returns {Promise}\n */\n handleRetryRequestLocusServiceError(options, retryAfterTime) {\n return new Promise((resolve, reject) => {\n const timeout = setTimeout(() => {\n clearTimeout(timeout);\n\n // @ts-ignore\n this.webex\n .request({\n method: options.method,\n uri: options.uri,\n body: options.body,\n })\n .then(resolve)\n .catch(reject);\n }, retryAfterTime);\n });\n }\n}\n"],"mappings":"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","ignoreList":[]}
@@ -372,7 +372,7 @@ var SimultaneousInterpretation = _webexCore.WebexPlugin.extend({
372
372
  throw error;
373
373
  });
374
374
  },
375
- version: "3.12.0-next.22"
375
+ version: "3.12.0-next.23"
376
376
  });
377
377
  var _default = exports.default = SimultaneousInterpretation;
378
378
  //# sourceMappingURL=index.js.map
@@ -18,7 +18,7 @@ var SILanguage = _webexCore.WebexPlugin.extend({
18
18
  languageCode: 'number',
19
19
  languageName: 'string'
20
20
  },
21
- version: "3.12.0-next.22"
21
+ version: "3.12.0-next.23"
22
22
  });
23
23
  var _default = exports.default = SILanguage;
24
24
  //# sourceMappingURL=siLanguage.js.map
@@ -11,11 +11,11 @@ export default class LocusRetryStatusInterceptor extends Interceptor {
11
11
  */
12
12
  static create(): LocusRetryStatusInterceptor;
13
13
  /**
14
- * Handle response errors
15
- * @param {Object} options
16
- * @param {WebexHttpError} reason
17
- * @returns {Promise<WebexHttpError>}
14
+ * Check whether a URI is a Locus /hashtree or /sync endpoint.
15
+ * @param {string} uri
16
+ * @returns {boolean}
18
17
  */
18
+ private static isLocusHashtreeOrSync;
19
19
  onResponseError(options: any, reason: any): Promise<unknown>;
20
20
  /**
21
21
  * Handle retries for locus service unavailable errors
@@ -723,7 +723,7 @@ var Webinar = _webexCore.WebexPlugin.extend({
723
723
  }, _callee1);
724
724
  }))();
725
725
  },
726
- version: "3.12.0-next.22"
726
+ version: "3.12.0-next.23"
727
727
  });
728
728
  var _default = exports.default = Webinar;
729
729
  //# sourceMappingURL=index.js.map
package/package.json CHANGED
@@ -94,5 +94,5 @@
94
94
  "//": [
95
95
  "TODO: upgrade jwt-decode when moving to node 18"
96
96
  ],
97
- "version": "3.12.0-next.22"
97
+ "version": "3.12.0-next.23"
98
98
  }
@@ -18,12 +18,33 @@ export default class LocusRetryStatusInterceptor extends Interceptor {
18
18
  }
19
19
 
20
20
  /**
21
- * Handle response errors
22
- * @param {Object} options
23
- * @param {WebexHttpError} reason
24
- * @returns {Promise<WebexHttpError>}
21
+ * Check whether a URI is a Locus /hashtree or /sync endpoint.
22
+ * @param {string} uri
23
+ * @returns {boolean}
25
24
  */
25
+ private static isLocusHashtreeOrSync(uri: string): boolean {
26
+ try {
27
+ const {pathname} = new URL(uri);
28
+
29
+ return (
30
+ pathname.includes('/locus/') &&
31
+ (pathname.endsWith('/hashtree') || pathname.endsWith('/sync'))
32
+ );
33
+ } catch {
34
+ return false;
35
+ }
36
+ }
37
+
26
38
  onResponseError(options, reason) {
39
+ // Don't retry /hashtree or /sync calls for 429 or any 5xx — during a sync storm retries
40
+ // make things worse. The normal sync timers will handle recovery for these endpoints.
41
+ if (
42
+ (reason.statusCode === 429 || reason.statusCode >= 500) &&
43
+ LocusRetryStatusInterceptor.isLocusHashtreeOrSync(options.uri)
44
+ ) {
45
+ return Promise.reject(reason);
46
+ }
47
+
27
48
  if ((reason.statusCode === 503 || reason.statusCode === 429) && options.uri.includes('locus')) {
28
49
  const hasRetriedLocusRequest = rateLimitExpiryTime.get(this);
29
50
  const retryAfterTime = options.headers['retry-after'] || 2000;
@@ -36,6 +36,27 @@ describe('plugin-meetings', () => {
36
36
  uri: `https://locus-test.webex.com/locus/api/v1/loci/call`,
37
37
  body: 'foo'
38
38
  };
39
+
40
+ const hashTreeOptions = {
41
+ method: 'GET',
42
+ headers: {
43
+ trackingid: 'test',
44
+ 'retry-after': 1000,
45
+ },
46
+ uri: `https://locus-test.webex.com/locus/api/v1/loci/12345/session/abc/datasets/main/hashtree`,
47
+ body: undefined,
48
+ };
49
+
50
+ const syncOptions = {
51
+ method: 'POST',
52
+ headers: {
53
+ trackingid: 'test',
54
+ 'retry-after': 1000,
55
+ },
56
+ uri: `https://locus-test.webex.com/locus/api/v1/loci/12345/session/abc/datasets/main/sync`,
57
+ body: 'foo',
58
+ };
59
+
39
60
  const reason1 = new WebexHttpError.MethodNotAllowed({
40
61
  statusCode: 403,
41
62
  options: {
@@ -68,14 +89,194 @@ describe('plugin-meetings', () => {
68
89
  });
69
90
 
70
91
  it('calls handleRetryRequestLocusServiceError with correct retry time when locus service unavailable error', () => {
71
- interceptor.webex.request = sinon.stub().returns(Promise.resolve());
72
- const handleRetryStub = sinon.stub(interceptor, 'handleRetryRequestLocusServiceError');
92
+ interceptor.webex.request = sinon.stub().returns(Promise.resolve());
93
+ const handleRetryStub = sinon.stub(
94
+ interceptor,
95
+ 'handleRetryRequestLocusServiceError'
96
+ );
97
+ handleRetryStub.returns(Promise.resolve());
98
+
99
+ return interceptor.onResponseError(options, reason2).then(() => {
100
+ expect(handleRetryStub.calledWith(options, 1000)).to.be.true;
101
+ });
102
+ });
103
+
104
+ [429, 500, 502, 503, 504].forEach((statusCode) => {
105
+ it(`does not retry /hashtree requests on ${statusCode}`, () => {
106
+ const reason = new WebexHttpError.MethodNotAllowed({
107
+ statusCode,
108
+ options: {
109
+ headers: {trackingid: 'test', 'retry-after': 1000},
110
+ uri: hashTreeOptions.uri,
111
+ },
112
+ body: {error: `Fake ${statusCode}`},
113
+ });
114
+
115
+ const handleRetryStub = sinon.stub(
116
+ interceptor,
117
+ 'handleRetryRequestLocusServiceError'
118
+ );
119
+ handleRetryStub.returns(Promise.resolve());
120
+
121
+ return interceptor.onResponseError(hashTreeOptions, reason).then(
122
+ () => assert.fail('Expected promise to be rejected'),
123
+ (err) => {
124
+ expect(err).to.equal(reason);
125
+ expect(handleRetryStub.called).to.be.false;
126
+ handleRetryStub.restore();
127
+ }
128
+ );
129
+ });
130
+
131
+ it(`does not retry /sync requests on ${statusCode}`, () => {
132
+ const reason = new WebexHttpError.MethodNotAllowed({
133
+ statusCode,
134
+ options: {
135
+ headers: {trackingid: 'test', 'retry-after': 1000},
136
+ uri: syncOptions.uri,
137
+ },
138
+ body: {error: `Fake ${statusCode}`},
139
+ });
140
+
141
+ const handleRetryStub = sinon.stub(
142
+ interceptor,
143
+ 'handleRetryRequestLocusServiceError'
144
+ );
73
145
  handleRetryStub.returns(Promise.resolve());
74
146
 
75
- return interceptor.onResponseError(options, reason2).then(() => {
76
- expect(handleRetryStub.calledWith(options, 1000)).to.be.true;
147
+ return interceptor.onResponseError(syncOptions, reason).then(
148
+ () => assert.fail('Expected promise to be rejected'),
149
+ (err) => {
150
+ expect(err).to.equal(reason);
151
+ expect(handleRetryStub.called).to.be.false;
152
+ handleRetryStub.restore();
153
+ }
154
+ );
155
+ });
156
+ });
157
+
158
+ it('still retries other locus requests on 429', () => {
159
+ const reason429 = new WebexHttpError.MethodNotAllowed({
160
+ statusCode: 429,
161
+ options: {
162
+ headers: {trackingid: 'test', 'retry-after': 1000},
163
+ uri: options.uri,
164
+ },
165
+ body: {error: 'Too Many Requests'},
166
+ });
167
+
168
+ interceptor.webex.request = sinon.stub().returns(Promise.resolve());
169
+ const handleRetryStub = sinon.stub(
170
+ interceptor,
171
+ 'handleRetryRequestLocusServiceError'
172
+ );
173
+ handleRetryStub.returns(Promise.resolve());
77
174
 
175
+ return interceptor.onResponseError(options, reason429).then(() => {
176
+ expect(handleRetryStub.calledOnce).to.be.true;
177
+ handleRetryStub.restore();
178
+ });
179
+ });
180
+
181
+ it('still retries other locus requests on 503', () => {
182
+ interceptor.webex.request = sinon.stub().returns(Promise.resolve());
183
+ const handleRetryStub = sinon.stub(
184
+ interceptor,
185
+ 'handleRetryRequestLocusServiceError'
186
+ );
187
+ handleRetryStub.returns(Promise.resolve());
188
+
189
+ return interceptor.onResponseError(options, reason2).then(() => {
190
+ expect(handleRetryStub.calledOnce).to.be.true;
191
+ handleRetryStub.restore();
192
+ });
193
+ });
194
+
195
+ describe('URI parsing edge cases', () => {
196
+ const make503Reason = (uri) =>
197
+ new WebexHttpError.MethodNotAllowed({
198
+ statusCode: 503,
199
+ options: {headers: {trackingid: 'test', 'retry-after': 1000}, uri},
200
+ body: {error: 'Service Unavailable'},
201
+ });
202
+
203
+ const makeOptions = (uri) => ({
204
+ method: 'GET',
205
+ headers: {trackingid: 'test', 'retry-after': 1000},
206
+ uri,
207
+ body: undefined,
208
+ });
209
+
210
+ [
211
+ 'https://locus.webex.com/locus/api/v1/loci/123/session/abc/datasets/main/hashtree?rootHash=xyz',
212
+ 'https://locus.webex.com/locus/api/v1/loci/123/session/abc/datasets/main/sync?seq=5',
213
+ ].forEach((uri) => {
214
+ it(`skips retry even with query params: ${uri.split('/').pop()}`, () => {
215
+ const opts = makeOptions(uri);
216
+ const reason = make503Reason(uri);
217
+ const stub = sinon
218
+ .stub(interceptor, 'handleRetryRequestLocusServiceError')
219
+ .returns(Promise.resolve());
220
+
221
+ return interceptor.onResponseError(opts, reason).then(
222
+ () => assert.fail('Expected promise to be rejected'),
223
+ (err) => {
224
+ expect(err).to.equal(reason);
225
+ expect(stub.called).to.be.false;
226
+ stub.restore();
227
+ }
228
+ );
78
229
  });
230
+ });
231
+
232
+ [
233
+ 'https://locus.webex.com/locus/api/v1/loci/123/hashtree-v2',
234
+ 'https://locus.webex.com/locus/api/v1/loci/123/syncData',
235
+ 'https://locus.webex.com/locus/api/v1/loci/123/async',
236
+ 'https://locus.webex.com/locus/api/v1/loci/123/hashtree/metadata',
237
+ ].forEach((uri) => {
238
+ it(`still retries when path only partially matches: ${uri
239
+ .split('/')
240
+ .pop()}`, () => {
241
+ const opts = makeOptions(uri);
242
+ const reason = make503Reason(uri);
243
+ interceptor.webex.request = sinon.stub().returns(Promise.resolve());
244
+ const stub = sinon
245
+ .stub(interceptor, 'handleRetryRequestLocusServiceError')
246
+ .returns(Promise.resolve());
247
+
248
+ return interceptor.onResponseError(opts, reason).then(() => {
249
+ expect(stub.calledOnce).to.be.true;
250
+ stub.restore();
251
+ });
252
+ });
253
+ });
254
+
255
+ it('still retries when /hashtree is on a non-locus host', () => {
256
+ const uri = 'https://other-service.webex.com/api/v1/hashtree';
257
+ const opts = makeOptions(uri);
258
+ const reason = make503Reason(uri);
259
+
260
+ return interceptor.onResponseError(opts, reason).then(
261
+ () => assert.fail('Expected promise to be rejected'),
262
+ (err) => {
263
+ expect(err).to.equal(reason);
264
+ }
265
+ );
266
+ });
267
+
268
+ it('still retries when URI is malformed', () => {
269
+ const uri = 'not-a-valid-url';
270
+ const opts = makeOptions(uri);
271
+ const reason = make503Reason(uri);
272
+
273
+ return interceptor.onResponseError(opts, reason).then(
274
+ () => assert.fail('Expected promise to be rejected'),
275
+ (err) => {
276
+ expect(err).to.equal(reason);
277
+ }
278
+ );
279
+ });
79
280
  });
80
281
  });
81
282