@webex/webex-core 2.59.2 → 2.59.3-next.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/.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 +20 -19
- 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,302 +1,302 @@
|
|
|
1
|
-
/*!
|
|
2
|
-
* Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
/* eslint-disable camelcase */
|
|
6
|
-
import {assert} from '@webex/test-helper-chai';
|
|
7
|
-
import MockWebex from '@webex/test-helper-mock-webex';
|
|
8
|
-
import FakeTimers from '@sinonjs/fake-timers';
|
|
9
|
-
import {RateLimitInterceptor, config, Credentials, WebexHttpError, Token} from '@webex/webex-core';
|
|
10
|
-
import {cloneDeep} from 'lodash';
|
|
11
|
-
|
|
12
|
-
describe('webex-core', () => {
|
|
13
|
-
describe('Interceptors', () => {
|
|
14
|
-
describe('RateLimitInterceptor', () => {
|
|
15
|
-
const idBrokerURL = process.env.IDBROKER_BASE_URL || 'https://idbroker.webex.com';
|
|
16
|
-
const identityURL = process.env.IDENTITY_BASE_URL || 'https://identity.webex.com';
|
|
17
|
-
let clock, interceptor, webex;
|
|
18
|
-
|
|
19
|
-
beforeEach(() => {
|
|
20
|
-
webex = new MockWebex({
|
|
21
|
-
children: {
|
|
22
|
-
credentials: Credentials,
|
|
23
|
-
},
|
|
24
|
-
config: cloneDeep(config),
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
webex.credentials.supertoken = new Token(
|
|
28
|
-
{
|
|
29
|
-
access_token: 'ST1',
|
|
30
|
-
token_type: 'Bearer',
|
|
31
|
-
},
|
|
32
|
-
{parent: webex}
|
|
33
|
-
);
|
|
34
|
-
|
|
35
|
-
interceptor = Reflect.apply(RateLimitInterceptor.create, webex, []);
|
|
36
|
-
clock = FakeTimers.install({now: Date.now()});
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
afterEach(() => {
|
|
40
|
-
clock.uninstall();
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
describe('#isRateLimited', () => {
|
|
44
|
-
it('returns false when previously rate limited idbroker API expiry time has been met', () => {
|
|
45
|
-
interceptor.setRateLimitExpiry(`${idBrokerURL}/horse/v1/myID`, 1);
|
|
46
|
-
clock.tick(2000);
|
|
47
|
-
|
|
48
|
-
return assert.equal(interceptor.isRateLimited(`${idBrokerURL}/horse/v1/myID`), false);
|
|
49
|
-
});
|
|
50
|
-
it('returns false when previously rate limited identity API expiry time has been met', () => {
|
|
51
|
-
interceptor.setRateLimitExpiry(`${identityURL}/horse/v1/myID`, 1);
|
|
52
|
-
clock.tick(2000);
|
|
53
|
-
|
|
54
|
-
return assert.equal(interceptor.isRateLimited(`${identityURL}/horse/v1/myID`), false);
|
|
55
|
-
});
|
|
56
|
-
it('returns false when URI is not IDbroker URI', () =>
|
|
57
|
-
assert.equal(interceptor.isRateLimited('https://example.com/testFake'), false));
|
|
58
|
-
it('returns false if the URI is null', () =>
|
|
59
|
-
assert.equal(interceptor.isRateLimited(null), false));
|
|
60
|
-
it.skip('returns true when idbroker API is rate limited', () => {
|
|
61
|
-
interceptor.setRateLimitExpiry(`${idBrokerURL}/horse/v1/myID`, new Date().getTime() * 2);
|
|
62
|
-
|
|
63
|
-
return assert.equal(interceptor.isRateLimited(`${idBrokerURL}/horse/v1/myID`), true);
|
|
64
|
-
});
|
|
65
|
-
it.skip('returns true when identity API is rate limited', () => {
|
|
66
|
-
interceptor.setRateLimitExpiry(`${identityURL}/horse/v1/myID`, new Date().getTime() * 2);
|
|
67
|
-
|
|
68
|
-
return assert.equal(interceptor.isRateLimited(`${identityURL}/horse/v1/myID`), true);
|
|
69
|
-
});
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
describe('#getApiName', () => {
|
|
73
|
-
it('returns null when there is no regex match', () =>
|
|
74
|
-
assert.equal(interceptor.getApiName('https://example.com/testFake'), null));
|
|
75
|
-
it('returns null when the argument is null', () =>
|
|
76
|
-
assert.equal(interceptor.getApiName(null), null));
|
|
77
|
-
it.skip('returns idbroker API name if there is a match', () =>
|
|
78
|
-
assert.equal(interceptor.getApiName(`${idBrokerURL}/horse/v1/myID`), 'horse'));
|
|
79
|
-
it.skip('returns identity API name if there is a match', () =>
|
|
80
|
-
assert.equal(interceptor.getApiName(`${identityURL}/horse/v1/myID`), 'horse'));
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
describe('#getRateLimitStatus', () => {
|
|
84
|
-
it('returns false if API name is not in rate limit expiry map', () =>
|
|
85
|
-
assert.equal(interceptor.getRateLimitStatus('https://example.com/testFake'), false));
|
|
86
|
-
it('returns false if idbroker API name is not rate limited', () => {
|
|
87
|
-
interceptor.setRateLimitExpiry(`${idBrokerURL}/horse/v1/myID`, 1);
|
|
88
|
-
clock.tick(2000);
|
|
89
|
-
|
|
90
|
-
return assert.equal(
|
|
91
|
-
interceptor.getRateLimitStatus(`${idBrokerURL}/horse/v1/myID`),
|
|
92
|
-
false
|
|
93
|
-
);
|
|
94
|
-
});
|
|
95
|
-
it('returns false if identity API name is not rate limited', () => {
|
|
96
|
-
interceptor.setRateLimitExpiry(`${identityURL}/horse/v1/myID`, 1);
|
|
97
|
-
clock.tick(2000);
|
|
98
|
-
|
|
99
|
-
return assert.equal(
|
|
100
|
-
interceptor.getRateLimitStatus(`${identityURL}/horse/v1/myID`),
|
|
101
|
-
false
|
|
102
|
-
);
|
|
103
|
-
});
|
|
104
|
-
it.skip('returns true if idbroker the API name is rate limited', () => {
|
|
105
|
-
const future = new Date().getTime() * 2;
|
|
106
|
-
|
|
107
|
-
interceptor.setRateLimitExpiry(`${idBrokerURL}/horse/v1/myID`, future);
|
|
108
|
-
|
|
109
|
-
return assert.equal(interceptor.getRateLimitStatus(`${idBrokerURL}/horse/v1/myID`), true);
|
|
110
|
-
});
|
|
111
|
-
it.skip('returns true if the identity API name is rate limited', () => {
|
|
112
|
-
const future = new Date().getTime() * 2;
|
|
113
|
-
|
|
114
|
-
interceptor.setRateLimitExpiry(`${identityURL}/horse/v1/myID`, future);
|
|
115
|
-
|
|
116
|
-
return assert.equal(interceptor.getRateLimitStatus(`${identityURL}/horse/v1/myID`), true);
|
|
117
|
-
});
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
describe('#setRateLimitExpiry', () => {
|
|
121
|
-
it('returns false if URI results in API name that is null', () =>
|
|
122
|
-
assert.equal(interceptor.setRateLimitExpiry('https://example.com/testFake', 1), false));
|
|
123
|
-
it.skip('sets expiry if idroker URI results in API name that can be mapped', () => {
|
|
124
|
-
interceptor.setRateLimitExpiry(`${idBrokerURL}/horse/v1/myID`, new Date().getTime() * 2);
|
|
125
|
-
|
|
126
|
-
return assert.equal(interceptor.getRateLimitStatus(`${idBrokerURL}/horse/v1/myID`), true);
|
|
127
|
-
});
|
|
128
|
-
it.skip('sets expiry if identity URI results in API name that can be mapped', () => {
|
|
129
|
-
interceptor.setRateLimitExpiry(`${identityURL}/horse/v1/myID`, new Date().getTime() * 2);
|
|
130
|
-
|
|
131
|
-
return assert.equal(interceptor.getRateLimitStatus(`${identityURL}/horse/v1/myID`), true);
|
|
132
|
-
});
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
describe('#extractRetryAfterTime', () => {
|
|
136
|
-
const milliMultiplier = 1000;
|
|
137
|
-
|
|
138
|
-
it('returns 60K milliseconds when retry-after <= 0S', () =>
|
|
139
|
-
assert.equal(
|
|
140
|
-
interceptor.extractRetryAfterTime({headers: {'retry-after': -1}}),
|
|
141
|
-
60 * milliMultiplier
|
|
142
|
-
));
|
|
143
|
-
it('returns 60K milliseconds when retry-after is null', () =>
|
|
144
|
-
assert.equal(
|
|
145
|
-
interceptor.extractRetryAfterTime({headers: {'retry-after-missing': 10}}),
|
|
146
|
-
60 * milliMultiplier
|
|
147
|
-
));
|
|
148
|
-
it('returns 3600K milliseconds when retry-after is > 3600S', () =>
|
|
149
|
-
assert.equal(
|
|
150
|
-
interceptor.extractRetryAfterTime({headers: {'retry-after': 7200}}),
|
|
151
|
-
3600 * milliMultiplier
|
|
152
|
-
));
|
|
153
|
-
it('returns retry-after * 1000 (converts to milliseconds) if 0S < retry-after < 3600S', () =>
|
|
154
|
-
assert.equal(
|
|
155
|
-
interceptor.extractRetryAfterTime({headers: {'retry-after': 55}}),
|
|
156
|
-
55 * milliMultiplier
|
|
157
|
-
));
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
describe('#onRequest', () => {
|
|
161
|
-
it('Rejects idbroker request if API is rate limited', () => {
|
|
162
|
-
const future = (new Date().getTime() / 1000) * 2;
|
|
163
|
-
|
|
164
|
-
interceptor.setRateLimitExpiry(`${idBrokerURL}/horse/v1/myID`, future);
|
|
165
|
-
|
|
166
|
-
return interceptor.onRequest({uri: `${idBrokerURL}/horse/v1/myID`}).catch((err) => {
|
|
167
|
-
assert.notCalled(webex.request);
|
|
168
|
-
assert.equal(err.message, `API rate limited ${idBrokerURL}/horse/v1/myID`);
|
|
169
|
-
});
|
|
170
|
-
});
|
|
171
|
-
it('Rejects identity request if API is rate limited', () => {
|
|
172
|
-
const future = (new Date().getTime() / 1000) * 2;
|
|
173
|
-
|
|
174
|
-
interceptor.setRateLimitExpiry(`${identityURL}/horse/v1/myID`, future);
|
|
175
|
-
|
|
176
|
-
return interceptor.onRequest({uri: `${identityURL}/horse/v1/myID`}).catch((err) => {
|
|
177
|
-
assert.notCalled(webex.request);
|
|
178
|
-
assert.equal(err.message, `API rate limited ${identityURL}/horse/v1/myID`);
|
|
179
|
-
});
|
|
180
|
-
});
|
|
181
|
-
it('Does not reject idbroker request if API is not rate limited', () => {
|
|
182
|
-
const options = {uri: `${idBrokerURL}/horse/v1/myID`};
|
|
183
|
-
|
|
184
|
-
return interceptor.onRequest(options).then((result) => {
|
|
185
|
-
assert.equal(result, options);
|
|
186
|
-
});
|
|
187
|
-
});
|
|
188
|
-
it('Does not reject identity request if API is not rate limited', () => {
|
|
189
|
-
const options = {uri: `${identityURL}/horse/v1/myID`};
|
|
190
|
-
|
|
191
|
-
return interceptor.onRequest(options).then((result) => {
|
|
192
|
-
assert.equal(result, options);
|
|
193
|
-
});
|
|
194
|
-
});
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
describe('#onResponseError', () => {
|
|
198
|
-
const err429 = new WebexHttpError.TooManyRequests({
|
|
199
|
-
statusCode: 429,
|
|
200
|
-
options: {
|
|
201
|
-
headers: {
|
|
202
|
-
trackingid: 'test',
|
|
203
|
-
'retry-after': 60,
|
|
204
|
-
},
|
|
205
|
-
uri: `${idBrokerURL}/horse/v1/myID`,
|
|
206
|
-
},
|
|
207
|
-
body: {
|
|
208
|
-
error: 'Too Many Requests',
|
|
209
|
-
},
|
|
210
|
-
});
|
|
211
|
-
const err429identity = new WebexHttpError.TooManyRequests({
|
|
212
|
-
statusCode: 429,
|
|
213
|
-
options: {
|
|
214
|
-
headers: {
|
|
215
|
-
trackingid: 'test',
|
|
216
|
-
'retry-after': 60,
|
|
217
|
-
},
|
|
218
|
-
uri: `${identityURL}/horse/v1/myID`,
|
|
219
|
-
},
|
|
220
|
-
body: {
|
|
221
|
-
error: 'Too Many Requests',
|
|
222
|
-
},
|
|
223
|
-
});
|
|
224
|
-
const err429notIdBroker = new WebexHttpError.TooManyRequests({
|
|
225
|
-
statusCode: 429,
|
|
226
|
-
options: {
|
|
227
|
-
headers: {
|
|
228
|
-
trackingid: 'test',
|
|
229
|
-
'retry-after': 60,
|
|
230
|
-
},
|
|
231
|
-
uri: 'https://example.com/horse/v1/myID',
|
|
232
|
-
},
|
|
233
|
-
body: {
|
|
234
|
-
error: 'Too Many Requests',
|
|
235
|
-
},
|
|
236
|
-
});
|
|
237
|
-
const err404 = new WebexHttpError.BadRequest({
|
|
238
|
-
statusCode: 404,
|
|
239
|
-
options: {
|
|
240
|
-
headers: {
|
|
241
|
-
trackingid: 'test',
|
|
242
|
-
'retry-after': 60,
|
|
243
|
-
},
|
|
244
|
-
uri: `${idBrokerURL}/horse/v1/myID`,
|
|
245
|
-
},
|
|
246
|
-
body: {
|
|
247
|
-
error: 'Resource Not Found',
|
|
248
|
-
},
|
|
249
|
-
});
|
|
250
|
-
const optionsIdBroker = {
|
|
251
|
-
headers: {
|
|
252
|
-
trackingid: 'test',
|
|
253
|
-
'retry-after': 60,
|
|
254
|
-
},
|
|
255
|
-
uri: `${idBrokerURL}/horse/v1/myID`,
|
|
256
|
-
};
|
|
257
|
-
|
|
258
|
-
const optionsIdentity = {
|
|
259
|
-
headers: {
|
|
260
|
-
trackingid: 'test',
|
|
261
|
-
'retry-after': 60,
|
|
262
|
-
},
|
|
263
|
-
uri: `${identityURL}/horse/v1/myID`,
|
|
264
|
-
};
|
|
265
|
-
|
|
266
|
-
const optionsNotIdBroker = {
|
|
267
|
-
headers: {
|
|
268
|
-
trackingid: 'test',
|
|
269
|
-
'retry-after': 60,
|
|
270
|
-
},
|
|
271
|
-
uri: 'https://example.com/horse/v1/myID',
|
|
272
|
-
};
|
|
273
|
-
|
|
274
|
-
it.skip('Stores API name and retry-after when status code is 429 and URI is idbroker', () =>
|
|
275
|
-
interceptor.onResponseError(optionsIdBroker, err429).catch((resp) => {
|
|
276
|
-
assert.equal(interceptor.isRateLimited(`${idBrokerURL}/horse/v1/myID`), true);
|
|
277
|
-
assert.equal(resp, err429);
|
|
278
|
-
}));
|
|
279
|
-
it.skip('Stores API name and retry-after when status code is 429 and URI is identity', () =>
|
|
280
|
-
interceptor.onResponseError(optionsIdentity, err429identity).catch((resp) => {
|
|
281
|
-
assert.equal(interceptor.isRateLimited(`${identityURL}/horse/v1/myID`), true);
|
|
282
|
-
assert.equal(resp, err429identity);
|
|
283
|
-
}));
|
|
284
|
-
it('Does not store API name and retry-after when URI is idbroker and status code is not 429', () =>
|
|
285
|
-
interceptor.onResponseError(optionsIdBroker, err404).catch((resp) => {
|
|
286
|
-
assert.equal(interceptor.isRateLimited(`${idBrokerURL}/horse/v1/myID`), false);
|
|
287
|
-
assert.equal(resp, err404);
|
|
288
|
-
}));
|
|
289
|
-
it('Does not store API name and retry-after when URI is identity and status code is not 429', () =>
|
|
290
|
-
interceptor.onResponseError(optionsIdentity, err404).catch((resp) => {
|
|
291
|
-
assert.equal(interceptor.isRateLimited(`${identityURL}/horse/v1/myID`), false);
|
|
292
|
-
assert.equal(resp, err404);
|
|
293
|
-
}));
|
|
294
|
-
it('Does not store API name and retry-after when status is 429 and URI is not idbroker', () =>
|
|
295
|
-
interceptor.onResponseError(optionsNotIdBroker, err429notIdBroker).catch((resp) => {
|
|
296
|
-
assert.equal(interceptor.isRateLimited('https://example.com/horse/v1/myID'), false);
|
|
297
|
-
assert.equal(resp, err429notIdBroker);
|
|
298
|
-
}));
|
|
299
|
-
});
|
|
300
|
-
});
|
|
301
|
-
});
|
|
302
|
-
});
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/* eslint-disable camelcase */
|
|
6
|
+
import {assert} from '@webex/test-helper-chai';
|
|
7
|
+
import MockWebex from '@webex/test-helper-mock-webex';
|
|
8
|
+
import FakeTimers from '@sinonjs/fake-timers';
|
|
9
|
+
import {RateLimitInterceptor, config, Credentials, WebexHttpError, Token} from '@webex/webex-core';
|
|
10
|
+
import {cloneDeep} from 'lodash';
|
|
11
|
+
|
|
12
|
+
describe('webex-core', () => {
|
|
13
|
+
describe('Interceptors', () => {
|
|
14
|
+
describe('RateLimitInterceptor', () => {
|
|
15
|
+
const idBrokerURL = process.env.IDBROKER_BASE_URL || 'https://idbroker.webex.com';
|
|
16
|
+
const identityURL = process.env.IDENTITY_BASE_URL || 'https://identity.webex.com';
|
|
17
|
+
let clock, interceptor, webex;
|
|
18
|
+
|
|
19
|
+
beforeEach(() => {
|
|
20
|
+
webex = new MockWebex({
|
|
21
|
+
children: {
|
|
22
|
+
credentials: Credentials,
|
|
23
|
+
},
|
|
24
|
+
config: cloneDeep(config),
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
webex.credentials.supertoken = new Token(
|
|
28
|
+
{
|
|
29
|
+
access_token: 'ST1',
|
|
30
|
+
token_type: 'Bearer',
|
|
31
|
+
},
|
|
32
|
+
{parent: webex}
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
interceptor = Reflect.apply(RateLimitInterceptor.create, webex, []);
|
|
36
|
+
clock = FakeTimers.install({now: Date.now()});
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
afterEach(() => {
|
|
40
|
+
clock.uninstall();
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
describe('#isRateLimited', () => {
|
|
44
|
+
it('returns false when previously rate limited idbroker API expiry time has been met', () => {
|
|
45
|
+
interceptor.setRateLimitExpiry(`${idBrokerURL}/horse/v1/myID`, 1);
|
|
46
|
+
clock.tick(2000);
|
|
47
|
+
|
|
48
|
+
return assert.equal(interceptor.isRateLimited(`${idBrokerURL}/horse/v1/myID`), false);
|
|
49
|
+
});
|
|
50
|
+
it('returns false when previously rate limited identity API expiry time has been met', () => {
|
|
51
|
+
interceptor.setRateLimitExpiry(`${identityURL}/horse/v1/myID`, 1);
|
|
52
|
+
clock.tick(2000);
|
|
53
|
+
|
|
54
|
+
return assert.equal(interceptor.isRateLimited(`${identityURL}/horse/v1/myID`), false);
|
|
55
|
+
});
|
|
56
|
+
it('returns false when URI is not IDbroker URI', () =>
|
|
57
|
+
assert.equal(interceptor.isRateLimited('https://example.com/testFake'), false));
|
|
58
|
+
it('returns false if the URI is null', () =>
|
|
59
|
+
assert.equal(interceptor.isRateLimited(null), false));
|
|
60
|
+
it.skip('returns true when idbroker API is rate limited', () => {
|
|
61
|
+
interceptor.setRateLimitExpiry(`${idBrokerURL}/horse/v1/myID`, new Date().getTime() * 2);
|
|
62
|
+
|
|
63
|
+
return assert.equal(interceptor.isRateLimited(`${idBrokerURL}/horse/v1/myID`), true);
|
|
64
|
+
});
|
|
65
|
+
it.skip('returns true when identity API is rate limited', () => {
|
|
66
|
+
interceptor.setRateLimitExpiry(`${identityURL}/horse/v1/myID`, new Date().getTime() * 2);
|
|
67
|
+
|
|
68
|
+
return assert.equal(interceptor.isRateLimited(`${identityURL}/horse/v1/myID`), true);
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
describe('#getApiName', () => {
|
|
73
|
+
it('returns null when there is no regex match', () =>
|
|
74
|
+
assert.equal(interceptor.getApiName('https://example.com/testFake'), null));
|
|
75
|
+
it('returns null when the argument is null', () =>
|
|
76
|
+
assert.equal(interceptor.getApiName(null), null));
|
|
77
|
+
it.skip('returns idbroker API name if there is a match', () =>
|
|
78
|
+
assert.equal(interceptor.getApiName(`${idBrokerURL}/horse/v1/myID`), 'horse'));
|
|
79
|
+
it.skip('returns identity API name if there is a match', () =>
|
|
80
|
+
assert.equal(interceptor.getApiName(`${identityURL}/horse/v1/myID`), 'horse'));
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
describe('#getRateLimitStatus', () => {
|
|
84
|
+
it('returns false if API name is not in rate limit expiry map', () =>
|
|
85
|
+
assert.equal(interceptor.getRateLimitStatus('https://example.com/testFake'), false));
|
|
86
|
+
it('returns false if idbroker API name is not rate limited', () => {
|
|
87
|
+
interceptor.setRateLimitExpiry(`${idBrokerURL}/horse/v1/myID`, 1);
|
|
88
|
+
clock.tick(2000);
|
|
89
|
+
|
|
90
|
+
return assert.equal(
|
|
91
|
+
interceptor.getRateLimitStatus(`${idBrokerURL}/horse/v1/myID`),
|
|
92
|
+
false
|
|
93
|
+
);
|
|
94
|
+
});
|
|
95
|
+
it('returns false if identity API name is not rate limited', () => {
|
|
96
|
+
interceptor.setRateLimitExpiry(`${identityURL}/horse/v1/myID`, 1);
|
|
97
|
+
clock.tick(2000);
|
|
98
|
+
|
|
99
|
+
return assert.equal(
|
|
100
|
+
interceptor.getRateLimitStatus(`${identityURL}/horse/v1/myID`),
|
|
101
|
+
false
|
|
102
|
+
);
|
|
103
|
+
});
|
|
104
|
+
it.skip('returns true if idbroker the API name is rate limited', () => {
|
|
105
|
+
const future = new Date().getTime() * 2;
|
|
106
|
+
|
|
107
|
+
interceptor.setRateLimitExpiry(`${idBrokerURL}/horse/v1/myID`, future);
|
|
108
|
+
|
|
109
|
+
return assert.equal(interceptor.getRateLimitStatus(`${idBrokerURL}/horse/v1/myID`), true);
|
|
110
|
+
});
|
|
111
|
+
it.skip('returns true if the identity API name is rate limited', () => {
|
|
112
|
+
const future = new Date().getTime() * 2;
|
|
113
|
+
|
|
114
|
+
interceptor.setRateLimitExpiry(`${identityURL}/horse/v1/myID`, future);
|
|
115
|
+
|
|
116
|
+
return assert.equal(interceptor.getRateLimitStatus(`${identityURL}/horse/v1/myID`), true);
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
describe('#setRateLimitExpiry', () => {
|
|
121
|
+
it('returns false if URI results in API name that is null', () =>
|
|
122
|
+
assert.equal(interceptor.setRateLimitExpiry('https://example.com/testFake', 1), false));
|
|
123
|
+
it.skip('sets expiry if idroker URI results in API name that can be mapped', () => {
|
|
124
|
+
interceptor.setRateLimitExpiry(`${idBrokerURL}/horse/v1/myID`, new Date().getTime() * 2);
|
|
125
|
+
|
|
126
|
+
return assert.equal(interceptor.getRateLimitStatus(`${idBrokerURL}/horse/v1/myID`), true);
|
|
127
|
+
});
|
|
128
|
+
it.skip('sets expiry if identity URI results in API name that can be mapped', () => {
|
|
129
|
+
interceptor.setRateLimitExpiry(`${identityURL}/horse/v1/myID`, new Date().getTime() * 2);
|
|
130
|
+
|
|
131
|
+
return assert.equal(interceptor.getRateLimitStatus(`${identityURL}/horse/v1/myID`), true);
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
describe('#extractRetryAfterTime', () => {
|
|
136
|
+
const milliMultiplier = 1000;
|
|
137
|
+
|
|
138
|
+
it('returns 60K milliseconds when retry-after <= 0S', () =>
|
|
139
|
+
assert.equal(
|
|
140
|
+
interceptor.extractRetryAfterTime({headers: {'retry-after': -1}}),
|
|
141
|
+
60 * milliMultiplier
|
|
142
|
+
));
|
|
143
|
+
it('returns 60K milliseconds when retry-after is null', () =>
|
|
144
|
+
assert.equal(
|
|
145
|
+
interceptor.extractRetryAfterTime({headers: {'retry-after-missing': 10}}),
|
|
146
|
+
60 * milliMultiplier
|
|
147
|
+
));
|
|
148
|
+
it('returns 3600K milliseconds when retry-after is > 3600S', () =>
|
|
149
|
+
assert.equal(
|
|
150
|
+
interceptor.extractRetryAfterTime({headers: {'retry-after': 7200}}),
|
|
151
|
+
3600 * milliMultiplier
|
|
152
|
+
));
|
|
153
|
+
it('returns retry-after * 1000 (converts to milliseconds) if 0S < retry-after < 3600S', () =>
|
|
154
|
+
assert.equal(
|
|
155
|
+
interceptor.extractRetryAfterTime({headers: {'retry-after': 55}}),
|
|
156
|
+
55 * milliMultiplier
|
|
157
|
+
));
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
describe('#onRequest', () => {
|
|
161
|
+
it('Rejects idbroker request if API is rate limited', () => {
|
|
162
|
+
const future = (new Date().getTime() / 1000) * 2;
|
|
163
|
+
|
|
164
|
+
interceptor.setRateLimitExpiry(`${idBrokerURL}/horse/v1/myID`, future);
|
|
165
|
+
|
|
166
|
+
return interceptor.onRequest({uri: `${idBrokerURL}/horse/v1/myID`}).catch((err) => {
|
|
167
|
+
assert.notCalled(webex.request);
|
|
168
|
+
assert.equal(err.message, `API rate limited ${idBrokerURL}/horse/v1/myID`);
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
it('Rejects identity request if API is rate limited', () => {
|
|
172
|
+
const future = (new Date().getTime() / 1000) * 2;
|
|
173
|
+
|
|
174
|
+
interceptor.setRateLimitExpiry(`${identityURL}/horse/v1/myID`, future);
|
|
175
|
+
|
|
176
|
+
return interceptor.onRequest({uri: `${identityURL}/horse/v1/myID`}).catch((err) => {
|
|
177
|
+
assert.notCalled(webex.request);
|
|
178
|
+
assert.equal(err.message, `API rate limited ${identityURL}/horse/v1/myID`);
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
it('Does not reject idbroker request if API is not rate limited', () => {
|
|
182
|
+
const options = {uri: `${idBrokerURL}/horse/v1/myID`};
|
|
183
|
+
|
|
184
|
+
return interceptor.onRequest(options).then((result) => {
|
|
185
|
+
assert.equal(result, options);
|
|
186
|
+
});
|
|
187
|
+
});
|
|
188
|
+
it('Does not reject identity request if API is not rate limited', () => {
|
|
189
|
+
const options = {uri: `${identityURL}/horse/v1/myID`};
|
|
190
|
+
|
|
191
|
+
return interceptor.onRequest(options).then((result) => {
|
|
192
|
+
assert.equal(result, options);
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
describe('#onResponseError', () => {
|
|
198
|
+
const err429 = new WebexHttpError.TooManyRequests({
|
|
199
|
+
statusCode: 429,
|
|
200
|
+
options: {
|
|
201
|
+
headers: {
|
|
202
|
+
trackingid: 'test',
|
|
203
|
+
'retry-after': 60,
|
|
204
|
+
},
|
|
205
|
+
uri: `${idBrokerURL}/horse/v1/myID`,
|
|
206
|
+
},
|
|
207
|
+
body: {
|
|
208
|
+
error: 'Too Many Requests',
|
|
209
|
+
},
|
|
210
|
+
});
|
|
211
|
+
const err429identity = new WebexHttpError.TooManyRequests({
|
|
212
|
+
statusCode: 429,
|
|
213
|
+
options: {
|
|
214
|
+
headers: {
|
|
215
|
+
trackingid: 'test',
|
|
216
|
+
'retry-after': 60,
|
|
217
|
+
},
|
|
218
|
+
uri: `${identityURL}/horse/v1/myID`,
|
|
219
|
+
},
|
|
220
|
+
body: {
|
|
221
|
+
error: 'Too Many Requests',
|
|
222
|
+
},
|
|
223
|
+
});
|
|
224
|
+
const err429notIdBroker = new WebexHttpError.TooManyRequests({
|
|
225
|
+
statusCode: 429,
|
|
226
|
+
options: {
|
|
227
|
+
headers: {
|
|
228
|
+
trackingid: 'test',
|
|
229
|
+
'retry-after': 60,
|
|
230
|
+
},
|
|
231
|
+
uri: 'https://example.com/horse/v1/myID',
|
|
232
|
+
},
|
|
233
|
+
body: {
|
|
234
|
+
error: 'Too Many Requests',
|
|
235
|
+
},
|
|
236
|
+
});
|
|
237
|
+
const err404 = new WebexHttpError.BadRequest({
|
|
238
|
+
statusCode: 404,
|
|
239
|
+
options: {
|
|
240
|
+
headers: {
|
|
241
|
+
trackingid: 'test',
|
|
242
|
+
'retry-after': 60,
|
|
243
|
+
},
|
|
244
|
+
uri: `${idBrokerURL}/horse/v1/myID`,
|
|
245
|
+
},
|
|
246
|
+
body: {
|
|
247
|
+
error: 'Resource Not Found',
|
|
248
|
+
},
|
|
249
|
+
});
|
|
250
|
+
const optionsIdBroker = {
|
|
251
|
+
headers: {
|
|
252
|
+
trackingid: 'test',
|
|
253
|
+
'retry-after': 60,
|
|
254
|
+
},
|
|
255
|
+
uri: `${idBrokerURL}/horse/v1/myID`,
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
const optionsIdentity = {
|
|
259
|
+
headers: {
|
|
260
|
+
trackingid: 'test',
|
|
261
|
+
'retry-after': 60,
|
|
262
|
+
},
|
|
263
|
+
uri: `${identityURL}/horse/v1/myID`,
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
const optionsNotIdBroker = {
|
|
267
|
+
headers: {
|
|
268
|
+
trackingid: 'test',
|
|
269
|
+
'retry-after': 60,
|
|
270
|
+
},
|
|
271
|
+
uri: 'https://example.com/horse/v1/myID',
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
it.skip('Stores API name and retry-after when status code is 429 and URI is idbroker', () =>
|
|
275
|
+
interceptor.onResponseError(optionsIdBroker, err429).catch((resp) => {
|
|
276
|
+
assert.equal(interceptor.isRateLimited(`${idBrokerURL}/horse/v1/myID`), true);
|
|
277
|
+
assert.equal(resp, err429);
|
|
278
|
+
}));
|
|
279
|
+
it.skip('Stores API name and retry-after when status code is 429 and URI is identity', () =>
|
|
280
|
+
interceptor.onResponseError(optionsIdentity, err429identity).catch((resp) => {
|
|
281
|
+
assert.equal(interceptor.isRateLimited(`${identityURL}/horse/v1/myID`), true);
|
|
282
|
+
assert.equal(resp, err429identity);
|
|
283
|
+
}));
|
|
284
|
+
it('Does not store API name and retry-after when URI is idbroker and status code is not 429', () =>
|
|
285
|
+
interceptor.onResponseError(optionsIdBroker, err404).catch((resp) => {
|
|
286
|
+
assert.equal(interceptor.isRateLimited(`${idBrokerURL}/horse/v1/myID`), false);
|
|
287
|
+
assert.equal(resp, err404);
|
|
288
|
+
}));
|
|
289
|
+
it('Does not store API name and retry-after when URI is identity and status code is not 429', () =>
|
|
290
|
+
interceptor.onResponseError(optionsIdentity, err404).catch((resp) => {
|
|
291
|
+
assert.equal(interceptor.isRateLimited(`${identityURL}/horse/v1/myID`), false);
|
|
292
|
+
assert.equal(resp, err404);
|
|
293
|
+
}));
|
|
294
|
+
it('Does not store API name and retry-after when status is 429 and URI is not idbroker', () =>
|
|
295
|
+
interceptor.onResponseError(optionsNotIdBroker, err429notIdBroker).catch((resp) => {
|
|
296
|
+
assert.equal(interceptor.isRateLimited('https://example.com/horse/v1/myID'), false);
|
|
297
|
+
assert.equal(resp, err429notIdBroker);
|
|
298
|
+
}));
|
|
299
|
+
});
|
|
300
|
+
});
|
|
301
|
+
});
|
|
302
|
+
});
|