@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.
- package/.eslintrc.js +6 -6
- package/README.md +79 -79
- package/babel.config.js +3 -3
- package/dist/config.js +24 -24
- package/dist/config.js.map +1 -1
- package/dist/credentials-config.js +56 -56
- package/dist/credentials-config.js.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/interceptors/auth.js +28 -28
- package/dist/interceptors/auth.js.map +1 -1
- package/dist/interceptors/default-options.js +24 -24
- package/dist/interceptors/default-options.js.map +1 -1
- package/dist/interceptors/embargo.js +9 -9
- package/dist/interceptors/embargo.js.map +1 -1
- package/dist/interceptors/network-timing.js +19 -19
- package/dist/interceptors/network-timing.js.map +1 -1
- package/dist/interceptors/payload-transformer.js +19 -19
- package/dist/interceptors/payload-transformer.js.map +1 -1
- package/dist/interceptors/rate-limit.js +40 -40
- package/dist/interceptors/rate-limit.js.map +1 -1
- package/dist/interceptors/redirect.js +13 -13
- package/dist/interceptors/redirect.js.map +1 -1
- package/dist/interceptors/request-event.js +23 -23
- package/dist/interceptors/request-event.js.map +1 -1
- package/dist/interceptors/request-logger.js +13 -13
- package/dist/interceptors/request-logger.js.map +1 -1
- package/dist/interceptors/request-timing.js +23 -23
- package/dist/interceptors/request-timing.js.map +1 -1
- package/dist/interceptors/response-logger.js +19 -19
- package/dist/interceptors/response-logger.js.map +1 -1
- package/dist/interceptors/user-agent.js +29 -29
- package/dist/interceptors/user-agent.js.map +1 -1
- package/dist/interceptors/webex-tracking-id.js +15 -15
- package/dist/interceptors/webex-tracking-id.js.map +1 -1
- package/dist/interceptors/webex-user-agent.js +13 -13
- package/dist/interceptors/webex-user-agent.js.map +1 -1
- package/dist/lib/batcher.js +83 -83
- package/dist/lib/batcher.js.map +1 -1
- package/dist/lib/credentials/credentials.js +103 -103
- package/dist/lib/credentials/credentials.js.map +1 -1
- package/dist/lib/credentials/grant-errors.js +17 -17
- package/dist/lib/credentials/grant-errors.js.map +1 -1
- package/dist/lib/credentials/index.js +2 -2
- package/dist/lib/credentials/index.js.map +1 -1
- package/dist/lib/credentials/scope.js +11 -11
- package/dist/lib/credentials/scope.js.map +1 -1
- package/dist/lib/credentials/token-collection.js +2 -2
- package/dist/lib/credentials/token-collection.js.map +1 -1
- package/dist/lib/credentials/token.js +145 -145
- package/dist/lib/credentials/token.js.map +1 -1
- package/dist/lib/page.js +49 -49
- package/dist/lib/page.js.map +1 -1
- package/dist/lib/services/constants.js.map +1 -1
- package/dist/lib/services/index.js +2 -2
- package/dist/lib/services/index.js.map +1 -1
- package/dist/lib/services/interceptors/server-error.js +9 -9
- package/dist/lib/services/interceptors/server-error.js.map +1 -1
- package/dist/lib/services/interceptors/service.js +24 -24
- package/dist/lib/services/interceptors/service.js.map +1 -1
- package/dist/lib/services/metrics.js.map +1 -1
- package/dist/lib/services/service-catalog.js +104 -104
- package/dist/lib/services/service-catalog.js.map +1 -1
- package/dist/lib/services/service-fed-ramp.js.map +1 -1
- package/dist/lib/services/service-host.js +134 -134
- package/dist/lib/services/service-host.js.map +1 -1
- package/dist/lib/services/service-registry.js +175 -175
- package/dist/lib/services/service-registry.js.map +1 -1
- package/dist/lib/services/service-state.js +38 -38
- package/dist/lib/services/service-state.js.map +1 -1
- package/dist/lib/services/service-url.js +31 -31
- package/dist/lib/services/service-url.js.map +1 -1
- package/dist/lib/services/services.js +245 -245
- package/dist/lib/services/services.js.map +1 -1
- package/dist/lib/stateless-webex-plugin.js +28 -28
- package/dist/lib/stateless-webex-plugin.js.map +1 -1
- package/dist/lib/storage/decorators.js +27 -27
- package/dist/lib/storage/decorators.js.map +1 -1
- package/dist/lib/storage/errors.js +4 -4
- package/dist/lib/storage/errors.js.map +1 -1
- package/dist/lib/storage/index.js.map +1 -1
- package/dist/lib/storage/make-webex-plugin-store.js +44 -44
- package/dist/lib/storage/make-webex-plugin-store.js.map +1 -1
- package/dist/lib/storage/make-webex-store.js +40 -40
- package/dist/lib/storage/make-webex-store.js.map +1 -1
- package/dist/lib/storage/memory-store-adapter.js +9 -9
- package/dist/lib/storage/memory-store-adapter.js.map +1 -1
- package/dist/lib/webex-core-plugin-mixin.js +13 -13
- package/dist/lib/webex-core-plugin-mixin.js.map +1 -1
- package/dist/lib/webex-http-error.js +9 -9
- package/dist/lib/webex-http-error.js.map +1 -1
- package/dist/lib/webex-internal-core-plugin-mixin.js +13 -13
- package/dist/lib/webex-internal-core-plugin-mixin.js.map +1 -1
- package/dist/lib/webex-plugin.js +36 -36
- package/dist/lib/webex-plugin.js.map +1 -1
- package/dist/plugins/logger.js +9 -9
- package/dist/plugins/logger.js.map +1 -1
- package/dist/webex-core.js +104 -104
- package/dist/webex-core.js.map +1 -1
- package/dist/webex-internal-core.js +12 -12
- package/dist/webex-internal-core.js.map +1 -1
- package/jest.config.js +3 -3
- package/package.json +19 -20
- package/process +1 -1
- package/src/config.js +90 -90
- package/src/credentials-config.js +212 -212
- package/src/index.js +62 -62
- package/src/interceptors/auth.js +186 -186
- package/src/interceptors/default-options.js +55 -55
- package/src/interceptors/embargo.js +43 -43
- package/src/interceptors/network-timing.js +54 -54
- package/src/interceptors/payload-transformer.js +55 -55
- package/src/interceptors/rate-limit.js +169 -169
- package/src/interceptors/redirect.js +106 -106
- package/src/interceptors/request-event.js +93 -93
- package/src/interceptors/request-logger.js +78 -78
- package/src/interceptors/request-timing.js +65 -65
- package/src/interceptors/response-logger.js +98 -98
- package/src/interceptors/user-agent.js +77 -77
- package/src/interceptors/webex-tracking-id.js +73 -73
- package/src/interceptors/webex-user-agent.js +79 -79
- package/src/lib/batcher.js +307 -307
- package/src/lib/credentials/credentials.js +552 -552
- package/src/lib/credentials/grant-errors.js +92 -92
- package/src/lib/credentials/index.js +16 -16
- package/src/lib/credentials/scope.js +34 -34
- package/src/lib/credentials/token-collection.js +17 -17
- package/src/lib/credentials/token.js +559 -559
- package/src/lib/page.js +159 -159
- package/src/lib/services/constants.js +9 -9
- package/src/lib/services/index.js +26 -26
- package/src/lib/services/interceptors/server-error.js +48 -48
- package/src/lib/services/interceptors/service.js +101 -101
- package/src/lib/services/metrics.js +4 -4
- package/src/lib/services/service-catalog.js +435 -435
- package/src/lib/services/service-fed-ramp.js +4 -4
- package/src/lib/services/service-host.js +267 -267
- package/src/lib/services/service-registry.js +465 -465
- package/src/lib/services/service-state.js +78 -78
- package/src/lib/services/service-url.js +124 -124
- package/src/lib/services/services.js +1018 -1018
- package/src/lib/stateless-webex-plugin.js +98 -98
- package/src/lib/storage/decorators.js +220 -220
- package/src/lib/storage/errors.js +15 -15
- package/src/lib/storage/index.js +10 -10
- package/src/lib/storage/make-webex-plugin-store.js +211 -211
- package/src/lib/storage/make-webex-store.js +140 -140
- package/src/lib/storage/memory-store-adapter.js +79 -79
- package/src/lib/webex-core-plugin-mixin.js +114 -114
- package/src/lib/webex-http-error.js +61 -61
- package/src/lib/webex-internal-core-plugin-mixin.js +107 -107
- package/src/lib/webex-plugin.js +222 -222
- package/src/plugins/logger.js +60 -60
- package/src/webex-core.js +745 -745
- package/src/webex-internal-core.js +46 -46
- package/test/integration/spec/credentials/credentials.js +139 -139
- package/test/integration/spec/credentials/token.js +102 -102
- package/test/integration/spec/services/service-catalog.js +838 -838
- package/test/integration/spec/services/services.js +1221 -1221
- package/test/integration/spec/webex-core.js +178 -178
- package/test/unit/spec/_setup.js +44 -44
- package/test/unit/spec/credentials/credentials.js +1017 -1017
- package/test/unit/spec/credentials/token.js +441 -441
- package/test/unit/spec/interceptors/auth.js +521 -521
- package/test/unit/spec/interceptors/default-options.js +84 -84
- package/test/unit/spec/interceptors/embargo.js +144 -144
- package/test/unit/spec/interceptors/network-timing.js +49 -49
- package/test/unit/spec/interceptors/payload-transformer.js +155 -155
- package/test/unit/spec/interceptors/rate-limit.js +302 -302
- package/test/unit/spec/interceptors/redirect.js +102 -102
- package/test/unit/spec/interceptors/request-timing.js +92 -92
- package/test/unit/spec/interceptors/user-agent.js +76 -76
- package/test/unit/spec/interceptors/webex-tracking-id.js +76 -76
- package/test/unit/spec/interceptors/webex-user-agent.js +159 -159
- package/test/unit/spec/lib/batcher.js +330 -330
- package/test/unit/spec/lib/page.js +148 -148
- package/test/unit/spec/lib/webex-plugin.js +48 -48
- package/test/unit/spec/services/interceptors/server-error.js +204 -204
- package/test/unit/spec/services/interceptors/service.js +188 -188
- package/test/unit/spec/services/service-catalog.js +194 -194
- package/test/unit/spec/services/service-host.js +260 -260
- package/test/unit/spec/services/service-registry.js +747 -747
- package/test/unit/spec/services/service-state.js +60 -60
- package/test/unit/spec/services/service-url.js +258 -258
- package/test/unit/spec/services/services.js +348 -348
- package/test/unit/spec/storage/persist.js +50 -50
- package/test/unit/spec/storage/storage-adapter.js +12 -12
- package/test/unit/spec/storage/wait-for-value.js +81 -81
- package/test/unit/spec/webex-core.js +253 -253
- 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
|
+
});
|