@webex/webex-core 3.12.0-llmrefactor.1 → 3.12.0-llmrefactor.10
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/dist/config.js +23 -0
- package/dist/config.js.map +1 -1
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -1
- package/dist/interceptors/catalog-url.js +87 -0
- package/dist/interceptors/catalog-url.js.map +1 -0
- package/dist/interceptors/payload-transformer.js +25 -4
- package/dist/interceptors/payload-transformer.js.map +1 -1
- package/dist/lib/batcher.js +1 -1
- package/dist/lib/credentials/credentials.js +56 -1
- package/dist/lib/credentials/credentials.js.map +1 -1
- package/dist/lib/credentials/token.js +1 -1
- package/dist/lib/services/service-catalog.js +102 -8
- package/dist/lib/services/service-catalog.js.map +1 -1
- package/dist/lib/services/services.js +74 -44
- package/dist/lib/services/services.js.map +1 -1
- package/dist/lib/services-v2/service-catalog.js +8 -1
- package/dist/lib/services-v2/service-catalog.js.map +1 -1
- package/dist/lib/services-v2/services-v2.js +76 -45
- package/dist/lib/services-v2/services-v2.js.map +1 -1
- package/dist/plugins/logger.js +1 -1
- package/dist/webex-core.js +11 -3
- package/dist/webex-core.js.map +1 -1
- package/package.json +12 -12
- package/src/config.js +25 -0
- package/src/index.js +1 -0
- package/src/interceptors/catalog-url.js +66 -0
- package/src/interceptors/payload-transformer.js +23 -1
- package/src/lib/credentials/credentials.js +43 -0
- package/src/lib/services/service-catalog.js +106 -6
- package/src/lib/services/services.js +65 -30
- package/src/lib/services-v2/service-catalog.ts +10 -1
- package/src/lib/services-v2/services-v2.ts +70 -30
- package/src/webex-core.js +8 -0
- package/test/integration/spec/services/services.js +148 -113
- package/test/integration/spec/services-v2/services-v2.js +134 -99
- package/test/unit/spec/credentials/credentials.js +114 -1
- package/test/unit/spec/interceptors/catalog-url.js +224 -0
- package/test/unit/spec/interceptors/payload-transformer.js +119 -1
- package/test/unit/spec/services/service-catalog.js +275 -0
- package/test/unit/spec/services/services.js +135 -2
- package/test/unit/spec/services-v2/service-catalog.ts +41 -0
- package/test/unit/spec/services-v2/services-v2.ts +158 -2
- package/test/unit/spec/webex-core.js +77 -1
|
@@ -151,9 +151,55 @@ describe('webex-core', () => {
|
|
|
151
151
|
const [, event] = services.listenToOnce.getCall(1).args;
|
|
152
152
|
assert.equal(event, 'ready', 'default path listens on webex ready');
|
|
153
153
|
});
|
|
154
|
+
|
|
155
|
+
it('skips preauth catalog collection when skipPreauthCatalogOnUnauthenticated is true and no credentials', async () => {
|
|
156
|
+
services.webex.config = {
|
|
157
|
+
...(services.webex.config || {}),
|
|
158
|
+
services: {skipPreauthCatalogOnUnauthenticated: true},
|
|
159
|
+
};
|
|
160
|
+
services.listenToOnce = sinon.stub();
|
|
161
|
+
services.collectPreauthCatalog = sinon.stub().returns(Promise.resolve());
|
|
162
|
+
services.logger.info = sinon.stub();
|
|
163
|
+
|
|
164
|
+
services.initialize();
|
|
165
|
+
services.listenToOnce.getCall(0).args[2]();
|
|
166
|
+
services.listenToOnce.getCall(1).args[2]();
|
|
167
|
+
await waitForAsync();
|
|
168
|
+
|
|
169
|
+
assert.isFalse(
|
|
170
|
+
services.collectPreauthCatalog.called,
|
|
171
|
+
'must not collect preauth catalog while unauthenticated when option is enabled'
|
|
172
|
+
);
|
|
173
|
+
assert.isFalse(services.initFailed);
|
|
174
|
+
sinon.assert.calledWith(
|
|
175
|
+
services.logger.info,
|
|
176
|
+
'services: skipping preauth catalog collection while unauthenticated as per the config'
|
|
177
|
+
);
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
it('still collects preauth catalog when skipPreauthCatalogOnUnauthenticated is false (default)', async () => {
|
|
181
|
+
services.webex.config = {
|
|
182
|
+
...(services.webex.config || {}),
|
|
183
|
+
services: {skipPreauthCatalogOnUnauthenticated: false},
|
|
184
|
+
};
|
|
185
|
+
services.listenToOnce = sinon.stub();
|
|
186
|
+
services.collectPreauthCatalog = sinon.stub().returns(Promise.resolve());
|
|
187
|
+
|
|
188
|
+
services.initialize();
|
|
189
|
+
services.listenToOnce.getCall(0).args[2]();
|
|
190
|
+
services.listenToOnce.getCall(1).args[2]();
|
|
191
|
+
await waitForAsync();
|
|
192
|
+
|
|
193
|
+
assert.isTrue(
|
|
194
|
+
services.collectPreauthCatalog.called,
|
|
195
|
+
'must collect preauth catalog when option is disabled'
|
|
196
|
+
);
|
|
197
|
+
});
|
|
154
198
|
});
|
|
155
199
|
|
|
156
200
|
describe('#initialize (waitForCatalogInit=true)', () => {
|
|
201
|
+
let clock;
|
|
202
|
+
|
|
157
203
|
beforeEach(() => {
|
|
158
204
|
services.webex.config = {
|
|
159
205
|
...(services.webex.config || {}),
|
|
@@ -164,6 +210,15 @@ describe('webex-core', () => {
|
|
|
164
210
|
// true). We reset here so tests that call initialize() a second time
|
|
165
211
|
// with the flag enabled observe the gated behavior from a clean state.
|
|
166
212
|
services.ready = false;
|
|
213
|
+
// The gated path schedules a real 15s setTimeout via _makeInitTimeout()
|
|
214
|
+
// that is not cleared when the init race resolves. Fake only setTimeout/
|
|
215
|
+
// clearTimeout (leaving setImmediate real so waitForAsync still works)
|
|
216
|
+
// so restoring in afterEach clears the pending timer and Jest can exit.
|
|
217
|
+
clock = sinon.useFakeTimers({toFake: ['setTimeout', 'clearTimeout']});
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
afterEach(() => {
|
|
221
|
+
clock.restore();
|
|
167
222
|
});
|
|
168
223
|
|
|
169
224
|
it('leaves services.ready=false after initialize (does not flip until finalize)', () => {
|
|
@@ -243,7 +298,6 @@ describe('webex-core', () => {
|
|
|
243
298
|
});
|
|
244
299
|
|
|
245
300
|
it('flips services.ready=true when init times out', async () => {
|
|
246
|
-
const clock = sinon.useFakeTimers();
|
|
247
301
|
services.listenToOnce = sinon.stub();
|
|
248
302
|
services.initServiceCatalogs = sinon.stub().returns(new Promise(() => {}));
|
|
249
303
|
services.webex.credentials = {
|
|
@@ -256,7 +310,6 @@ describe('webex-core', () => {
|
|
|
256
310
|
services.listenToOnce.getCall(1).args[2]();
|
|
257
311
|
|
|
258
312
|
await clock.tickAsync(15_001);
|
|
259
|
-
clock.restore();
|
|
260
313
|
|
|
261
314
|
assert.isTrue(services.initFailed);
|
|
262
315
|
assert.isTrue(services.ready);
|
|
@@ -376,6 +429,89 @@ describe('webex-core', () => {
|
|
|
376
429
|
});
|
|
377
430
|
});
|
|
378
431
|
|
|
432
|
+
describe('#initialize (waitForCatalogInit=true, skipPreauthCatalogOnUnauthenticated=true)', () => {
|
|
433
|
+
beforeEach(() => {
|
|
434
|
+
services.webex.config = {
|
|
435
|
+
...(services.webex.config || {}),
|
|
436
|
+
services: {
|
|
437
|
+
waitForCatalogInit: true,
|
|
438
|
+
catalogInitTimeout: 15000,
|
|
439
|
+
skipPreauthCatalogOnUnauthenticated: true,
|
|
440
|
+
},
|
|
441
|
+
};
|
|
442
|
+
services.ready = false;
|
|
443
|
+
});
|
|
444
|
+
|
|
445
|
+
it('skips preauth catalog collection but still finalizes services.ready when unauthenticated', async () => {
|
|
446
|
+
services.listenToOnce = sinon.stub();
|
|
447
|
+
services.collectPreauthCatalog = sinon.stub().returns(Promise.resolve());
|
|
448
|
+
services.logger.info = sinon.stub();
|
|
449
|
+
|
|
450
|
+
services.initialize();
|
|
451
|
+
services.listenToOnce.getCall(0).args[2]();
|
|
452
|
+
services.listenToOnce.getCall(1).args[2]();
|
|
453
|
+
await waitForAsync();
|
|
454
|
+
|
|
455
|
+
assert.isFalse(
|
|
456
|
+
services.collectPreauthCatalog.called,
|
|
457
|
+
'must not collect preauth catalog while unauthenticated when option is enabled'
|
|
458
|
+
);
|
|
459
|
+
assert.isTrue(services.ready, 'must still finalize ready so webex.ready is not stalled');
|
|
460
|
+
sinon.assert.calledWith(
|
|
461
|
+
services.logger.info,
|
|
462
|
+
'services: skipping preauth catalog collection while unauthenticated as per the config'
|
|
463
|
+
);
|
|
464
|
+
});
|
|
465
|
+
|
|
466
|
+
it('still registers a change:canAuthorize listener when skipping preauth (fresh-login path)', async () => {
|
|
467
|
+
services.listenToOnce = sinon.stub();
|
|
468
|
+
services.collectPreauthCatalog = sinon.stub().returns(Promise.resolve());
|
|
469
|
+
services.initServiceCatalogs = sinon.stub().returns(Promise.resolve());
|
|
470
|
+
|
|
471
|
+
services.initialize();
|
|
472
|
+
const currentCatalog = services._getCatalog();
|
|
473
|
+
services.listenToOnce.getCall(0).args[2]();
|
|
474
|
+
services.listenToOnce.getCall(1).args[2]();
|
|
475
|
+
await waitForAsync();
|
|
476
|
+
|
|
477
|
+
const freshLoginCall = services.listenToOnce
|
|
478
|
+
.getCalls()
|
|
479
|
+
.find((call: any) => call.args[1] === 'change:canAuthorize');
|
|
480
|
+
assert.isDefined(freshLoginCall, 'expected change:canAuthorize listener to be registered');
|
|
481
|
+
|
|
482
|
+
services.webex.canAuthorize = true;
|
|
483
|
+
currentCatalog.status.postauth.ready = false;
|
|
484
|
+
freshLoginCall!.args[2]();
|
|
485
|
+
|
|
486
|
+
assert.isTrue(
|
|
487
|
+
services.initServiceCatalogs.called,
|
|
488
|
+
'expected postauth catalog init after canAuthorize flips'
|
|
489
|
+
);
|
|
490
|
+
await waitForAsync();
|
|
491
|
+
});
|
|
492
|
+
|
|
493
|
+
it('does not schedule an init timeout when skipping preauth (no stray timer/rejection)', async () => {
|
|
494
|
+
const clock = sinon.useFakeTimers();
|
|
495
|
+
services.listenToOnce = sinon.stub();
|
|
496
|
+
services.collectPreauthCatalog = sinon.stub().returns(Promise.resolve());
|
|
497
|
+
services.logger.error = sinon.stub();
|
|
498
|
+
|
|
499
|
+
services.initialize();
|
|
500
|
+
services.listenToOnce.getCall(0).args[2]();
|
|
501
|
+
services.listenToOnce.getCall(1).args[2]();
|
|
502
|
+
|
|
503
|
+
// Advance well past the configured init timeout; since no timeout was
|
|
504
|
+
// scheduled in skip mode, no timeout error should ever be logged.
|
|
505
|
+
await clock.tickAsync(30_000);
|
|
506
|
+
clock.restore();
|
|
507
|
+
|
|
508
|
+
sinon.assert.neverCalledWithMatch(
|
|
509
|
+
services.logger.error,
|
|
510
|
+
/services: init timed out/
|
|
511
|
+
);
|
|
512
|
+
});
|
|
513
|
+
});
|
|
514
|
+
|
|
379
515
|
describe('#_finalizeReady', () => {
|
|
380
516
|
beforeEach(() => {
|
|
381
517
|
// Outer beforeEach construction already ran initialize() (ungated,
|
|
@@ -760,6 +896,26 @@ describe('webex-core', () => {
|
|
|
760
896
|
});
|
|
761
897
|
});
|
|
762
898
|
|
|
899
|
+
describe('#getServiceFromUrl()', () => {
|
|
900
|
+
it('matches an exact service URL when the catalog URL has a trailing slash', () => {
|
|
901
|
+
const baseUrl = 'https://example.com/api/v1/';
|
|
902
|
+
|
|
903
|
+
catalog.updateServiceGroups('preauth', [
|
|
904
|
+
{
|
|
905
|
+
id: 'example',
|
|
906
|
+
serviceName: 'example',
|
|
907
|
+
serviceUrls: [{host: 'example.com', baseUrl, priority: 1}],
|
|
908
|
+
},
|
|
909
|
+
]);
|
|
910
|
+
|
|
911
|
+
assert.deepEqual(services.getServiceFromUrl('https://example.com/api/v1'), {
|
|
912
|
+
name: 'example',
|
|
913
|
+
priorityUrl: baseUrl,
|
|
914
|
+
defaultUrl: baseUrl,
|
|
915
|
+
});
|
|
916
|
+
});
|
|
917
|
+
});
|
|
918
|
+
|
|
763
919
|
describe('#_formatReceivedHostmap()', () => {
|
|
764
920
|
let serviceHostmap;
|
|
765
921
|
let formattedHM;
|
|
@@ -91,6 +91,30 @@ describe('Webex', () => {
|
|
|
91
91
|
assert.isFalse(webex.canAuthorize);
|
|
92
92
|
});
|
|
93
93
|
|
|
94
|
+
it('initializes network telemetry after applying the configured metrics options', async () => {
|
|
95
|
+
const configuredWebex = new WebexCore({
|
|
96
|
+
config: {
|
|
97
|
+
metrics: {
|
|
98
|
+
networkTelemetry: {
|
|
99
|
+
enabled: true,
|
|
100
|
+
intervalMs: 60 * 1_000,
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
});
|
|
105
|
+
const submitClientMetrics = sinon
|
|
106
|
+
.stub(configuredWebex.internal.metrics, 'submitClientMetrics')
|
|
107
|
+
.resolves();
|
|
108
|
+
|
|
109
|
+
assert.exists(configuredWebex.internal.metrics.networkTelemetry);
|
|
110
|
+
configuredWebex.trigger('request:start', {service: 'hydra', resource: 'rooms'});
|
|
111
|
+
await configuredWebex.internal.metrics.flushNetworkTelemetry();
|
|
112
|
+
|
|
113
|
+
assert.calledOnce(submitClientMetrics);
|
|
114
|
+
|
|
115
|
+
await configuredWebex.internal.metrics.stopNetworkTelemetry();
|
|
116
|
+
});
|
|
117
|
+
|
|
94
118
|
[
|
|
95
119
|
'data',
|
|
96
120
|
'data.access_token',
|
|
@@ -179,7 +203,7 @@ describe('Webex', () => {
|
|
|
179
203
|
|
|
180
204
|
describe('initializes with interceptors', () => {
|
|
181
205
|
[
|
|
182
|
-
// 4 pre, 4 post, 10 remaining default = 18
|
|
206
|
+
// 4 pre (CatalogUrlInterceptor is opt-in), 4 post, 10 remaining default = 18
|
|
183
207
|
[
|
|
184
208
|
'defaults to existing interceptors if undefined',
|
|
185
209
|
undefined,
|
|
@@ -205,6 +229,58 @@ describe('Webex', () => {
|
|
|
205
229
|
'RateLimitInterceptor',
|
|
206
230
|
],
|
|
207
231
|
],
|
|
232
|
+
[
|
|
233
|
+
'does not include CatalogUrlInterceptor for a truthy non-boolean value',
|
|
234
|
+
{services: {validateCatalogUrls: 'true'}},
|
|
235
|
+
18,
|
|
236
|
+
[
|
|
237
|
+
'RequestTimingInterceptor',
|
|
238
|
+
'RequestEventInterceptor',
|
|
239
|
+
'WebexTrackingIdInterceptor',
|
|
240
|
+
'RateLimitInterceptor',
|
|
241
|
+
'ServiceInterceptor',
|
|
242
|
+
'UserAgentInterceptor',
|
|
243
|
+
'ProxyInterceptor',
|
|
244
|
+
'WebexUserAgentInterceptor',
|
|
245
|
+
'AuthInterceptor',
|
|
246
|
+
'PayloadTransformerInterceptor',
|
|
247
|
+
'RedirectInterceptor',
|
|
248
|
+
'DefaultOptionsInterceptor',
|
|
249
|
+
'HostMapInterceptor',
|
|
250
|
+
'ServerErrorInterceptor',
|
|
251
|
+
'HttpStatusInterceptor',
|
|
252
|
+
'NetworkTimingInterceptor',
|
|
253
|
+
'EmbargoInterceptor',
|
|
254
|
+
'RateLimitInterceptor',
|
|
255
|
+
],
|
|
256
|
+
],
|
|
257
|
+
// CatalogUrlInterceptor is opt-in via services.validateCatalogUrls
|
|
258
|
+
[
|
|
259
|
+
'includes CatalogUrlInterceptor when validateCatalogUrls is enabled',
|
|
260
|
+
{services: {validateCatalogUrls: true}},
|
|
261
|
+
19,
|
|
262
|
+
[
|
|
263
|
+
'RequestTimingInterceptor',
|
|
264
|
+
'RequestEventInterceptor',
|
|
265
|
+
'WebexTrackingIdInterceptor',
|
|
266
|
+
'RateLimitInterceptor',
|
|
267
|
+
'CatalogUrlInterceptor',
|
|
268
|
+
'ServiceInterceptor',
|
|
269
|
+
'UserAgentInterceptor',
|
|
270
|
+
'ProxyInterceptor',
|
|
271
|
+
'WebexUserAgentInterceptor',
|
|
272
|
+
'AuthInterceptor',
|
|
273
|
+
'PayloadTransformerInterceptor',
|
|
274
|
+
'RedirectInterceptor',
|
|
275
|
+
'DefaultOptionsInterceptor',
|
|
276
|
+
'HostMapInterceptor',
|
|
277
|
+
'ServerErrorInterceptor',
|
|
278
|
+
'HttpStatusInterceptor',
|
|
279
|
+
'NetworkTimingInterceptor',
|
|
280
|
+
'EmbargoInterceptor',
|
|
281
|
+
'RateLimitInterceptor',
|
|
282
|
+
],
|
|
283
|
+
],
|
|
208
284
|
[
|
|
209
285
|
'only adds PayloadTransformerInterceptor',
|
|
210
286
|
{interceptors: {PayloadTransformerInterceptor: PayloadTransformerInterceptor.create}},
|