@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.
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 +20 -19
  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,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
+ });