@webex/webex-core 2.59.3-next.1 → 2.59.4

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.
Files changed (189) hide show
  1. package/.eslintrc.js +6 -6
  2. package/README.md +79 -79
  3. package/babel.config.js +3 -3
  4. package/dist/config.js +24 -24
  5. package/dist/config.js.map +1 -1
  6. package/dist/credentials-config.js +56 -56
  7. package/dist/credentials-config.js.map +1 -1
  8. package/dist/index.js.map +1 -1
  9. package/dist/interceptors/auth.js +28 -28
  10. package/dist/interceptors/auth.js.map +1 -1
  11. package/dist/interceptors/default-options.js +24 -24
  12. package/dist/interceptors/default-options.js.map +1 -1
  13. package/dist/interceptors/embargo.js +9 -9
  14. package/dist/interceptors/embargo.js.map +1 -1
  15. package/dist/interceptors/network-timing.js +19 -19
  16. package/dist/interceptors/network-timing.js.map +1 -1
  17. package/dist/interceptors/payload-transformer.js +19 -19
  18. package/dist/interceptors/payload-transformer.js.map +1 -1
  19. package/dist/interceptors/rate-limit.js +40 -40
  20. package/dist/interceptors/rate-limit.js.map +1 -1
  21. package/dist/interceptors/redirect.js +13 -13
  22. package/dist/interceptors/redirect.js.map +1 -1
  23. package/dist/interceptors/request-event.js +23 -23
  24. package/dist/interceptors/request-event.js.map +1 -1
  25. package/dist/interceptors/request-logger.js +13 -13
  26. package/dist/interceptors/request-logger.js.map +1 -1
  27. package/dist/interceptors/request-timing.js +23 -23
  28. package/dist/interceptors/request-timing.js.map +1 -1
  29. package/dist/interceptors/response-logger.js +19 -19
  30. package/dist/interceptors/response-logger.js.map +1 -1
  31. package/dist/interceptors/user-agent.js +29 -29
  32. package/dist/interceptors/user-agent.js.map +1 -1
  33. package/dist/interceptors/webex-tracking-id.js +15 -15
  34. package/dist/interceptors/webex-tracking-id.js.map +1 -1
  35. package/dist/interceptors/webex-user-agent.js +13 -13
  36. package/dist/interceptors/webex-user-agent.js.map +1 -1
  37. package/dist/lib/batcher.js +83 -83
  38. package/dist/lib/batcher.js.map +1 -1
  39. package/dist/lib/credentials/credentials.js +103 -103
  40. package/dist/lib/credentials/credentials.js.map +1 -1
  41. package/dist/lib/credentials/grant-errors.js +17 -17
  42. package/dist/lib/credentials/grant-errors.js.map +1 -1
  43. package/dist/lib/credentials/index.js +2 -2
  44. package/dist/lib/credentials/index.js.map +1 -1
  45. package/dist/lib/credentials/scope.js +11 -11
  46. package/dist/lib/credentials/scope.js.map +1 -1
  47. package/dist/lib/credentials/token-collection.js +2 -2
  48. package/dist/lib/credentials/token-collection.js.map +1 -1
  49. package/dist/lib/credentials/token.js +145 -145
  50. package/dist/lib/credentials/token.js.map +1 -1
  51. package/dist/lib/page.js +49 -49
  52. package/dist/lib/page.js.map +1 -1
  53. package/dist/lib/services/constants.js.map +1 -1
  54. package/dist/lib/services/index.js +2 -2
  55. package/dist/lib/services/index.js.map +1 -1
  56. package/dist/lib/services/interceptors/server-error.js +9 -9
  57. package/dist/lib/services/interceptors/server-error.js.map +1 -1
  58. package/dist/lib/services/interceptors/service.js +24 -24
  59. package/dist/lib/services/interceptors/service.js.map +1 -1
  60. package/dist/lib/services/metrics.js.map +1 -1
  61. package/dist/lib/services/service-catalog.js +104 -104
  62. package/dist/lib/services/service-catalog.js.map +1 -1
  63. package/dist/lib/services/service-fed-ramp.js.map +1 -1
  64. package/dist/lib/services/service-host.js +134 -134
  65. package/dist/lib/services/service-host.js.map +1 -1
  66. package/dist/lib/services/service-registry.js +175 -175
  67. package/dist/lib/services/service-registry.js.map +1 -1
  68. package/dist/lib/services/service-state.js +38 -38
  69. package/dist/lib/services/service-state.js.map +1 -1
  70. package/dist/lib/services/service-url.js +31 -31
  71. package/dist/lib/services/service-url.js.map +1 -1
  72. package/dist/lib/services/services.js +245 -245
  73. package/dist/lib/services/services.js.map +1 -1
  74. package/dist/lib/stateless-webex-plugin.js +28 -28
  75. package/dist/lib/stateless-webex-plugin.js.map +1 -1
  76. package/dist/lib/storage/decorators.js +27 -27
  77. package/dist/lib/storage/decorators.js.map +1 -1
  78. package/dist/lib/storage/errors.js +4 -4
  79. package/dist/lib/storage/errors.js.map +1 -1
  80. package/dist/lib/storage/index.js.map +1 -1
  81. package/dist/lib/storage/make-webex-plugin-store.js +44 -44
  82. package/dist/lib/storage/make-webex-plugin-store.js.map +1 -1
  83. package/dist/lib/storage/make-webex-store.js +40 -40
  84. package/dist/lib/storage/make-webex-store.js.map +1 -1
  85. package/dist/lib/storage/memory-store-adapter.js +9 -9
  86. package/dist/lib/storage/memory-store-adapter.js.map +1 -1
  87. package/dist/lib/webex-core-plugin-mixin.js +13 -13
  88. package/dist/lib/webex-core-plugin-mixin.js.map +1 -1
  89. package/dist/lib/webex-http-error.js +9 -9
  90. package/dist/lib/webex-http-error.js.map +1 -1
  91. package/dist/lib/webex-internal-core-plugin-mixin.js +13 -13
  92. package/dist/lib/webex-internal-core-plugin-mixin.js.map +1 -1
  93. package/dist/lib/webex-plugin.js +36 -36
  94. package/dist/lib/webex-plugin.js.map +1 -1
  95. package/dist/plugins/logger.js +9 -9
  96. package/dist/plugins/logger.js.map +1 -1
  97. package/dist/webex-core.js +104 -104
  98. package/dist/webex-core.js.map +1 -1
  99. package/dist/webex-internal-core.js +12 -12
  100. package/dist/webex-internal-core.js.map +1 -1
  101. package/jest.config.js +3 -3
  102. package/package.json +19 -20
  103. package/process +1 -1
  104. package/src/config.js +90 -90
  105. package/src/credentials-config.js +212 -212
  106. package/src/index.js +62 -62
  107. package/src/interceptors/auth.js +186 -186
  108. package/src/interceptors/default-options.js +55 -55
  109. package/src/interceptors/embargo.js +43 -43
  110. package/src/interceptors/network-timing.js +54 -54
  111. package/src/interceptors/payload-transformer.js +55 -55
  112. package/src/interceptors/rate-limit.js +169 -169
  113. package/src/interceptors/redirect.js +106 -106
  114. package/src/interceptors/request-event.js +93 -93
  115. package/src/interceptors/request-logger.js +78 -78
  116. package/src/interceptors/request-timing.js +65 -65
  117. package/src/interceptors/response-logger.js +98 -98
  118. package/src/interceptors/user-agent.js +77 -77
  119. package/src/interceptors/webex-tracking-id.js +73 -73
  120. package/src/interceptors/webex-user-agent.js +79 -79
  121. package/src/lib/batcher.js +307 -307
  122. package/src/lib/credentials/credentials.js +552 -552
  123. package/src/lib/credentials/grant-errors.js +92 -92
  124. package/src/lib/credentials/index.js +16 -16
  125. package/src/lib/credentials/scope.js +34 -34
  126. package/src/lib/credentials/token-collection.js +17 -17
  127. package/src/lib/credentials/token.js +559 -559
  128. package/src/lib/page.js +159 -159
  129. package/src/lib/services/constants.js +9 -9
  130. package/src/lib/services/index.js +26 -26
  131. package/src/lib/services/interceptors/server-error.js +48 -48
  132. package/src/lib/services/interceptors/service.js +101 -101
  133. package/src/lib/services/metrics.js +4 -4
  134. package/src/lib/services/service-catalog.js +435 -435
  135. package/src/lib/services/service-fed-ramp.js +4 -4
  136. package/src/lib/services/service-host.js +267 -267
  137. package/src/lib/services/service-registry.js +465 -465
  138. package/src/lib/services/service-state.js +78 -78
  139. package/src/lib/services/service-url.js +124 -124
  140. package/src/lib/services/services.js +1018 -1018
  141. package/src/lib/stateless-webex-plugin.js +98 -98
  142. package/src/lib/storage/decorators.js +220 -220
  143. package/src/lib/storage/errors.js +15 -15
  144. package/src/lib/storage/index.js +10 -10
  145. package/src/lib/storage/make-webex-plugin-store.js +211 -211
  146. package/src/lib/storage/make-webex-store.js +140 -140
  147. package/src/lib/storage/memory-store-adapter.js +79 -79
  148. package/src/lib/webex-core-plugin-mixin.js +114 -114
  149. package/src/lib/webex-http-error.js +61 -61
  150. package/src/lib/webex-internal-core-plugin-mixin.js +107 -107
  151. package/src/lib/webex-plugin.js +222 -222
  152. package/src/plugins/logger.js +60 -60
  153. package/src/webex-core.js +745 -745
  154. package/src/webex-internal-core.js +46 -46
  155. package/test/integration/spec/credentials/credentials.js +139 -139
  156. package/test/integration/spec/credentials/token.js +102 -102
  157. package/test/integration/spec/services/service-catalog.js +838 -838
  158. package/test/integration/spec/services/services.js +1221 -1221
  159. package/test/integration/spec/webex-core.js +178 -178
  160. package/test/unit/spec/_setup.js +44 -44
  161. package/test/unit/spec/credentials/credentials.js +1017 -1017
  162. package/test/unit/spec/credentials/token.js +441 -441
  163. package/test/unit/spec/interceptors/auth.js +521 -521
  164. package/test/unit/spec/interceptors/default-options.js +84 -84
  165. package/test/unit/spec/interceptors/embargo.js +144 -144
  166. package/test/unit/spec/interceptors/network-timing.js +49 -49
  167. package/test/unit/spec/interceptors/payload-transformer.js +155 -155
  168. package/test/unit/spec/interceptors/rate-limit.js +302 -302
  169. package/test/unit/spec/interceptors/redirect.js +102 -102
  170. package/test/unit/spec/interceptors/request-timing.js +92 -92
  171. package/test/unit/spec/interceptors/user-agent.js +76 -76
  172. package/test/unit/spec/interceptors/webex-tracking-id.js +76 -76
  173. package/test/unit/spec/interceptors/webex-user-agent.js +159 -159
  174. package/test/unit/spec/lib/batcher.js +330 -330
  175. package/test/unit/spec/lib/page.js +148 -148
  176. package/test/unit/spec/lib/webex-plugin.js +48 -48
  177. package/test/unit/spec/services/interceptors/server-error.js +204 -204
  178. package/test/unit/spec/services/interceptors/service.js +188 -188
  179. package/test/unit/spec/services/service-catalog.js +194 -194
  180. package/test/unit/spec/services/service-host.js +260 -260
  181. package/test/unit/spec/services/service-registry.js +747 -747
  182. package/test/unit/spec/services/service-state.js +60 -60
  183. package/test/unit/spec/services/service-url.js +258 -258
  184. package/test/unit/spec/services/services.js +348 -348
  185. package/test/unit/spec/storage/persist.js +50 -50
  186. package/test/unit/spec/storage/storage-adapter.js +12 -12
  187. package/test/unit/spec/storage/wait-for-value.js +81 -81
  188. package/test/unit/spec/webex-core.js +253 -253
  189. package/test/unit/spec/webex-internal-core.js +91 -91
@@ -1,330 +1,330 @@
1
- /*!
2
- * Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.
3
- */
4
-
5
- import FakeTimers from '@sinonjs/fake-timers';
6
- import {assert} from '@webex/test-helper-chai';
7
- import MockWebex from '@webex/test-helper-mock-webex';
8
- import sinon from 'sinon';
9
- import {Batcher} from '@webex/webex-core';
10
-
11
- function promiseTick(count) {
12
- let promise = Promise.resolve();
13
-
14
- while (count > 1) {
15
- promise = promise.then(() => promiseTick(1));
16
- count -= 1;
17
- }
18
-
19
- return promise;
20
- }
21
-
22
- describe('webex-core', () => {
23
- describe('Batcher', () => {
24
- let webex;
25
- const MockBatcher = Batcher.extend({
26
- namespace: 'mock',
27
- submitHttpRequest(payload) {
28
- return webex.request({
29
- service: 'mock',
30
- resource: '/batch',
31
- body: payload,
32
- });
33
- },
34
- fingerprintRequest(req) {
35
- return Promise.resolve(req);
36
- },
37
- fingerprintResponse(res) {
38
- return Promise.resolve(res);
39
- },
40
- });
41
-
42
- const OutOfBandBatcher = MockBatcher.extend({
43
- handleHttpSuccess() {
44
- return Promise.resolve();
45
- },
46
-
47
- fingerprintRequest(req) {
48
- return Promise.resolve(req.id);
49
- },
50
- fingerprintResponse(res) {
51
- return Promise.resolve(res.id);
52
- },
53
- });
54
-
55
- const BATCHER_MAX_CALLS = 10;
56
- const BATCHER_MAX_WAIT = 5;
57
- const BATCHER_WAIT = 2;
58
-
59
- beforeEach(() => {
60
- webex = new MockWebex({
61
- children: {
62
- batcher: MockBatcher,
63
- outOfBandBatcher: OutOfBandBatcher,
64
- },
65
- });
66
-
67
- webex.config.mock = {
68
- batcherMaxCalls: BATCHER_MAX_CALLS,
69
- batcherWait: BATCHER_WAIT,
70
- batcherMaxWait: BATCHER_MAX_WAIT,
71
- };
72
- });
73
-
74
- let clock;
75
-
76
- beforeEach(() => {
77
- clock = FakeTimers.install({now: Date.now()});
78
- });
79
-
80
- afterEach(() => {
81
- clock.uninstall();
82
- });
83
-
84
- describe('#request()', () => {
85
- it('coalesces requests made in a short time period into a single request', () => {
86
- const promises = [];
87
-
88
- webex.request.returns(Promise.resolve({body: [0, 1, 2]}));
89
-
90
- promises.push(webex.internal.batcher.request(0));
91
- assert.notCalled(webex.request);
92
-
93
- promises.push(webex.internal.batcher.request(1));
94
- assert.notCalled(webex.request);
95
-
96
- promises.push(webex.internal.batcher.request(2));
97
- assert.notCalled(webex.request);
98
-
99
- return promiseTick(50)
100
- .then(() => {
101
- clock.tick(1);
102
- assert.notCalled(webex.request);
103
- clock.tick(1);
104
-
105
- return promiseTick(50);
106
- })
107
- .then(() => {
108
- assert.calledOnce(webex.request);
109
-
110
- return Promise.all(promises);
111
- })
112
- .then((results) => {
113
- assert.lengthOf(results, 3);
114
- for (let i = 0; i < 3; i += 1) {
115
- assert.equal(results[i], i);
116
- }
117
- clock.tick(250);
118
-
119
- return promiseTick(50);
120
- })
121
- .then(() => {
122
- assert.calledOnce(webex.request);
123
- });
124
- });
125
-
126
- // This test is mostly here to make sure that unexpected failures (e.g.,
127
- // those cause by forgetting to wrap a return value in a Promise) don't
128
- // get squelched.
129
- it('propagates error from inside the call chain', () => {
130
- // This is way easier to prove if we don't need to control the clock
131
- clock.uninstall();
132
- sinon
133
- .stub(webex.internal.batcher, 'fingerprintResponse')
134
- .throws(new Error('simulated failure'));
135
- webex.request.returns(Promise.resolve({body: [{id: 1}]}));
136
-
137
- return assert.isRejected(webex.internal.batcher.request({id: 1}), /simulated failure/);
138
- });
139
-
140
- describe('when the http request fails', () => {
141
- it('fails the whole batch', () => {
142
- const p1 = webex.internal.batcher.request(1);
143
- const p2 = webex.internal.batcher.request(2);
144
-
145
- // eslint-disable-next-line prefer-promise-reject-errors
146
- webex.request.returns(Promise.reject({statusCode: 0}));
147
-
148
- return promiseTick(50)
149
- .then(() => clock.tick(2))
150
- .then(() => promiseTick(50))
151
- .then(() => {
152
- assert.calledOnce(webex.request);
153
-
154
- return Promise.all([assert.isRejected(p1), assert.isRejected(p2)]);
155
- });
156
- });
157
- });
158
-
159
- describe('when the number of request attempts exceeds a given threshold', () => {
160
- it('executes the batch request, regardless of the time passed', () => {
161
- const result = [];
162
-
163
- webex.request.returns(Promise.resolve({body: result}));
164
-
165
- const promises = [];
166
-
167
- // eslint-disable-next-line no-unmodified-loop-condition
168
- for (let i = 0; i < BATCHER_MAX_CALLS; i += 1) {
169
- result.push(i);
170
- promises.push(webex.internal.batcher.request(i));
171
- assert.notCalled(webex.request);
172
- }
173
-
174
- return promiseTick(50)
175
- .then(() => {
176
- assert.calledOnce(webex.request);
177
-
178
- return Promise.all(promises);
179
- })
180
- .then((results) => {
181
- assert.lengthOf(results, BATCHER_MAX_CALLS);
182
- // eslint-disable-next-line no-unmodified-loop-condition
183
- for (let i = 0; i < BATCHER_MAX_CALLS; i += 1) {
184
- assert.equal(results[i], i);
185
- }
186
- clock.tick(250);
187
-
188
- return promiseTick(50);
189
- })
190
- .then(() => {
191
- assert.calledOnce(webex.request);
192
- });
193
- });
194
- });
195
-
196
- describe('when the requests are requested continuously', () => {
197
- describe('when a configured time period is exceeded', () => {
198
- it('executes the batch request', () => {
199
- const promises = [];
200
- const result = [];
201
-
202
- // eslint-disable-next-line no-unmodified-loop-condition
203
- for (let i = 0; i < BATCHER_MAX_WAIT + 1; i += 1) {
204
- result.push(i);
205
- }
206
- webex.request.returns(Promise.resolve({body: result}));
207
- webex.request.onCall(1).returns(Promise.resolve({body: []}));
208
-
209
- return result
210
- .reduce(
211
- (promise, i) =>
212
- promise.then(() => {
213
- promises.push(webex.internal.batcher.request(i));
214
- clock.tick(1);
215
-
216
- return promiseTick(50);
217
- }),
218
- Promise.resolve()
219
- )
220
- .then(() => {
221
- assert.calledOnce(webex.request);
222
-
223
- return Promise.all(promises);
224
- })
225
- .then((results) => {
226
- assert.lengthOf(results, BATCHER_MAX_WAIT + 1);
227
-
228
- // eslint-disable-next-line no-unmodified-loop-condition
229
- for (let i = 0; i < BATCHER_MAX_WAIT + 1; i += 1) {
230
- assert.equal(results[i], i);
231
- }
232
- clock.tick(250);
233
-
234
- return promiseTick(50);
235
- })
236
- .then(() => {
237
- // This assertion is different than the other tests because
238
- // we've requestd enough requests that the normal debounce will
239
- // take over
240
- assert.calledTwice(webex.request);
241
- });
242
- });
243
- });
244
-
245
- describe('when the request attempts exceeds a given threshold', () => {
246
- it('executes the requests for the amount of max calls', () => {
247
- const result = [];
248
-
249
- webex.request.returns(Promise.resolve({body: result}));
250
-
251
- const promises = [];
252
-
253
- // eslint-disable-next-line no-unmodified-loop-condition
254
- for (let i = 0; i < BATCHER_MAX_CALLS * 2; i += 1) {
255
- result.push(i);
256
- promises.push(webex.internal.batcher.request(i));
257
- }
258
-
259
- return Promise.all(promises).then((results) => {
260
- assert.calledTwice(webex.request);
261
- assert.lengthOf(webex.request.args[0][0].body, BATCHER_MAX_CALLS);
262
- assert.lengthOf(webex.request.args[1][0].body, BATCHER_MAX_CALLS);
263
- assert.lengthOf(results, BATCHER_MAX_CALLS * 2);
264
- });
265
- });
266
- });
267
- });
268
-
269
- describe('when the same request is made twice before the first one completes', () => {
270
- it('returns the same result', () => {
271
- webex.request.returns(Promise.resolve({body: [1]}));
272
-
273
- const p1 = webex.internal.batcher.request(1);
274
- const p2 = webex.internal.batcher.request(1);
275
-
276
- const promise = Promise.all([p1, p2]);
277
-
278
- return promiseTick(50)
279
- .then(() => {
280
- clock.tick(2);
281
-
282
- return promiseTick(50);
283
- })
284
- .then(() => {
285
- assert.calledOnce(webex.request);
286
- assert.deepEqual(webex.request.args[0][0], {
287
- service: 'mock',
288
- resource: '/batch',
289
- body: [1],
290
- });
291
-
292
- return promise.then((result) => assert.deepEqual(result, [1, 1]));
293
- });
294
- });
295
- });
296
-
297
- describe("when it's overridden to handle out-of-band responses", () => {
298
- it('resolves as expected', () => {
299
- sinon.spy(webex.internal.outOfBandBatcher, 'fingerprintResponse');
300
- const promise = webex.internal.outOfBandBatcher.request({id: 1});
301
-
302
- return promiseTick(50)
303
- .then(() => clock.tick(2))
304
- .then(() => {
305
- assert.called(webex.request);
306
- assert.notCalled(webex.internal.outOfBandBatcher.fingerprintResponse);
307
- webex.internal.outOfBandBatcher.acceptItem({id: 1, data: 2});
308
-
309
- return promiseTick(50);
310
- })
311
- .then(() => promise)
312
- .then((res) => {
313
- assert.deepEqual(res, {
314
- id: 1,
315
- data: 2,
316
- });
317
- });
318
- });
319
- });
320
- });
321
-
322
- describe('#handleHttpError()', () => {
323
- it('handles a non WebexHttpError object passed', () =>
324
- assert.isRejected(
325
- webex.internal.batcher.handleHttpError('simulated failure'),
326
- /simulated failure/
327
- ));
328
- });
329
- });
330
- });
1
+ /*!
2
+ * Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.
3
+ */
4
+
5
+ import FakeTimers from '@sinonjs/fake-timers';
6
+ import {assert} from '@webex/test-helper-chai';
7
+ import MockWebex from '@webex/test-helper-mock-webex';
8
+ import sinon from 'sinon';
9
+ import {Batcher} from '@webex/webex-core';
10
+
11
+ function promiseTick(count) {
12
+ let promise = Promise.resolve();
13
+
14
+ while (count > 1) {
15
+ promise = promise.then(() => promiseTick(1));
16
+ count -= 1;
17
+ }
18
+
19
+ return promise;
20
+ }
21
+
22
+ describe('webex-core', () => {
23
+ describe('Batcher', () => {
24
+ let webex;
25
+ const MockBatcher = Batcher.extend({
26
+ namespace: 'mock',
27
+ submitHttpRequest(payload) {
28
+ return webex.request({
29
+ service: 'mock',
30
+ resource: '/batch',
31
+ body: payload,
32
+ });
33
+ },
34
+ fingerprintRequest(req) {
35
+ return Promise.resolve(req);
36
+ },
37
+ fingerprintResponse(res) {
38
+ return Promise.resolve(res);
39
+ },
40
+ });
41
+
42
+ const OutOfBandBatcher = MockBatcher.extend({
43
+ handleHttpSuccess() {
44
+ return Promise.resolve();
45
+ },
46
+
47
+ fingerprintRequest(req) {
48
+ return Promise.resolve(req.id);
49
+ },
50
+ fingerprintResponse(res) {
51
+ return Promise.resolve(res.id);
52
+ },
53
+ });
54
+
55
+ const BATCHER_MAX_CALLS = 10;
56
+ const BATCHER_MAX_WAIT = 5;
57
+ const BATCHER_WAIT = 2;
58
+
59
+ beforeEach(() => {
60
+ webex = new MockWebex({
61
+ children: {
62
+ batcher: MockBatcher,
63
+ outOfBandBatcher: OutOfBandBatcher,
64
+ },
65
+ });
66
+
67
+ webex.config.mock = {
68
+ batcherMaxCalls: BATCHER_MAX_CALLS,
69
+ batcherWait: BATCHER_WAIT,
70
+ batcherMaxWait: BATCHER_MAX_WAIT,
71
+ };
72
+ });
73
+
74
+ let clock;
75
+
76
+ beforeEach(() => {
77
+ clock = FakeTimers.install({now: Date.now()});
78
+ });
79
+
80
+ afterEach(() => {
81
+ clock.uninstall();
82
+ });
83
+
84
+ describe('#request()', () => {
85
+ it('coalesces requests made in a short time period into a single request', () => {
86
+ const promises = [];
87
+
88
+ webex.request.returns(Promise.resolve({body: [0, 1, 2]}));
89
+
90
+ promises.push(webex.internal.batcher.request(0));
91
+ assert.notCalled(webex.request);
92
+
93
+ promises.push(webex.internal.batcher.request(1));
94
+ assert.notCalled(webex.request);
95
+
96
+ promises.push(webex.internal.batcher.request(2));
97
+ assert.notCalled(webex.request);
98
+
99
+ return promiseTick(50)
100
+ .then(() => {
101
+ clock.tick(1);
102
+ assert.notCalled(webex.request);
103
+ clock.tick(1);
104
+
105
+ return promiseTick(50);
106
+ })
107
+ .then(() => {
108
+ assert.calledOnce(webex.request);
109
+
110
+ return Promise.all(promises);
111
+ })
112
+ .then((results) => {
113
+ assert.lengthOf(results, 3);
114
+ for (let i = 0; i < 3; i += 1) {
115
+ assert.equal(results[i], i);
116
+ }
117
+ clock.tick(250);
118
+
119
+ return promiseTick(50);
120
+ })
121
+ .then(() => {
122
+ assert.calledOnce(webex.request);
123
+ });
124
+ });
125
+
126
+ // This test is mostly here to make sure that unexpected failures (e.g.,
127
+ // those cause by forgetting to wrap a return value in a Promise) don't
128
+ // get squelched.
129
+ it('propagates error from inside the call chain', () => {
130
+ // This is way easier to prove if we don't need to control the clock
131
+ clock.uninstall();
132
+ sinon
133
+ .stub(webex.internal.batcher, 'fingerprintResponse')
134
+ .throws(new Error('simulated failure'));
135
+ webex.request.returns(Promise.resolve({body: [{id: 1}]}));
136
+
137
+ return assert.isRejected(webex.internal.batcher.request({id: 1}), /simulated failure/);
138
+ });
139
+
140
+ describe('when the http request fails', () => {
141
+ it('fails the whole batch', () => {
142
+ const p1 = webex.internal.batcher.request(1);
143
+ const p2 = webex.internal.batcher.request(2);
144
+
145
+ // eslint-disable-next-line prefer-promise-reject-errors
146
+ webex.request.returns(Promise.reject({statusCode: 0}));
147
+
148
+ return promiseTick(50)
149
+ .then(() => clock.tick(2))
150
+ .then(() => promiseTick(50))
151
+ .then(() => {
152
+ assert.calledOnce(webex.request);
153
+
154
+ return Promise.all([assert.isRejected(p1), assert.isRejected(p2)]);
155
+ });
156
+ });
157
+ });
158
+
159
+ describe('when the number of request attempts exceeds a given threshold', () => {
160
+ it('executes the batch request, regardless of the time passed', () => {
161
+ const result = [];
162
+
163
+ webex.request.returns(Promise.resolve({body: result}));
164
+
165
+ const promises = [];
166
+
167
+ // eslint-disable-next-line no-unmodified-loop-condition
168
+ for (let i = 0; i < BATCHER_MAX_CALLS; i += 1) {
169
+ result.push(i);
170
+ promises.push(webex.internal.batcher.request(i));
171
+ assert.notCalled(webex.request);
172
+ }
173
+
174
+ return promiseTick(50)
175
+ .then(() => {
176
+ assert.calledOnce(webex.request);
177
+
178
+ return Promise.all(promises);
179
+ })
180
+ .then((results) => {
181
+ assert.lengthOf(results, BATCHER_MAX_CALLS);
182
+ // eslint-disable-next-line no-unmodified-loop-condition
183
+ for (let i = 0; i < BATCHER_MAX_CALLS; i += 1) {
184
+ assert.equal(results[i], i);
185
+ }
186
+ clock.tick(250);
187
+
188
+ return promiseTick(50);
189
+ })
190
+ .then(() => {
191
+ assert.calledOnce(webex.request);
192
+ });
193
+ });
194
+ });
195
+
196
+ describe('when the requests are requested continuously', () => {
197
+ describe('when a configured time period is exceeded', () => {
198
+ it('executes the batch request', () => {
199
+ const promises = [];
200
+ const result = [];
201
+
202
+ // eslint-disable-next-line no-unmodified-loop-condition
203
+ for (let i = 0; i < BATCHER_MAX_WAIT + 1; i += 1) {
204
+ result.push(i);
205
+ }
206
+ webex.request.returns(Promise.resolve({body: result}));
207
+ webex.request.onCall(1).returns(Promise.resolve({body: []}));
208
+
209
+ return result
210
+ .reduce(
211
+ (promise, i) =>
212
+ promise.then(() => {
213
+ promises.push(webex.internal.batcher.request(i));
214
+ clock.tick(1);
215
+
216
+ return promiseTick(50);
217
+ }),
218
+ Promise.resolve()
219
+ )
220
+ .then(() => {
221
+ assert.calledOnce(webex.request);
222
+
223
+ return Promise.all(promises);
224
+ })
225
+ .then((results) => {
226
+ assert.lengthOf(results, BATCHER_MAX_WAIT + 1);
227
+
228
+ // eslint-disable-next-line no-unmodified-loop-condition
229
+ for (let i = 0; i < BATCHER_MAX_WAIT + 1; i += 1) {
230
+ assert.equal(results[i], i);
231
+ }
232
+ clock.tick(250);
233
+
234
+ return promiseTick(50);
235
+ })
236
+ .then(() => {
237
+ // This assertion is different than the other tests because
238
+ // we've requestd enough requests that the normal debounce will
239
+ // take over
240
+ assert.calledTwice(webex.request);
241
+ });
242
+ });
243
+ });
244
+
245
+ describe('when the request attempts exceeds a given threshold', () => {
246
+ it('executes the requests for the amount of max calls', () => {
247
+ const result = [];
248
+
249
+ webex.request.returns(Promise.resolve({body: result}));
250
+
251
+ const promises = [];
252
+
253
+ // eslint-disable-next-line no-unmodified-loop-condition
254
+ for (let i = 0; i < BATCHER_MAX_CALLS * 2; i += 1) {
255
+ result.push(i);
256
+ promises.push(webex.internal.batcher.request(i));
257
+ }
258
+
259
+ return Promise.all(promises).then((results) => {
260
+ assert.calledTwice(webex.request);
261
+ assert.lengthOf(webex.request.args[0][0].body, BATCHER_MAX_CALLS);
262
+ assert.lengthOf(webex.request.args[1][0].body, BATCHER_MAX_CALLS);
263
+ assert.lengthOf(results, BATCHER_MAX_CALLS * 2);
264
+ });
265
+ });
266
+ });
267
+ });
268
+
269
+ describe('when the same request is made twice before the first one completes', () => {
270
+ it('returns the same result', () => {
271
+ webex.request.returns(Promise.resolve({body: [1]}));
272
+
273
+ const p1 = webex.internal.batcher.request(1);
274
+ const p2 = webex.internal.batcher.request(1);
275
+
276
+ const promise = Promise.all([p1, p2]);
277
+
278
+ return promiseTick(50)
279
+ .then(() => {
280
+ clock.tick(2);
281
+
282
+ return promiseTick(50);
283
+ })
284
+ .then(() => {
285
+ assert.calledOnce(webex.request);
286
+ assert.deepEqual(webex.request.args[0][0], {
287
+ service: 'mock',
288
+ resource: '/batch',
289
+ body: [1],
290
+ });
291
+
292
+ return promise.then((result) => assert.deepEqual(result, [1, 1]));
293
+ });
294
+ });
295
+ });
296
+
297
+ describe("when it's overridden to handle out-of-band responses", () => {
298
+ it('resolves as expected', () => {
299
+ sinon.spy(webex.internal.outOfBandBatcher, 'fingerprintResponse');
300
+ const promise = webex.internal.outOfBandBatcher.request({id: 1});
301
+
302
+ return promiseTick(50)
303
+ .then(() => clock.tick(2))
304
+ .then(() => {
305
+ assert.called(webex.request);
306
+ assert.notCalled(webex.internal.outOfBandBatcher.fingerprintResponse);
307
+ webex.internal.outOfBandBatcher.acceptItem({id: 1, data: 2});
308
+
309
+ return promiseTick(50);
310
+ })
311
+ .then(() => promise)
312
+ .then((res) => {
313
+ assert.deepEqual(res, {
314
+ id: 1,
315
+ data: 2,
316
+ });
317
+ });
318
+ });
319
+ });
320
+ });
321
+
322
+ describe('#handleHttpError()', () => {
323
+ it('handles a non WebexHttpError object passed', () =>
324
+ assert.isRejected(
325
+ webex.internal.batcher.handleHttpError('simulated failure'),
326
+ /simulated failure/
327
+ ));
328
+ });
329
+ });
330
+ });