@webex/webex-core 3.11.0-webex-services-ready.1 → 3.12.0
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/interceptors/redirect.js +1 -1
- package/dist/interceptors/redirect.js.map +1 -1
- package/dist/lib/batcher.js +1 -1
- package/dist/lib/credentials/credentials.js +1 -1
- package/dist/lib/credentials/token.js +1 -1
- package/dist/lib/services/services.js +28 -39
- package/dist/lib/services/services.js.map +1 -1
- package/dist/lib/services-v2/services-v2.js +23 -35
- package/dist/lib/services-v2/services-v2.js.map +1 -1
- package/dist/plugins/logger.js +1 -1
- package/dist/webex-core.js +2 -2
- package/dist/webex-core.js.map +1 -1
- package/package.json +13 -13
- package/src/interceptors/redirect.js +4 -1
- package/src/lib/services/services.js +32 -48
- package/src/lib/services-v2/services-v2.ts +27 -44
- package/test/integration/spec/services/service-catalog.js +7 -6
- package/test/integration/spec/services/services.js +6 -17
- package/test/integration/spec/services-v2/services-v2.js +6 -17
- package/test/unit/spec/services/services.js +173 -342
- package/test/unit/spec/services-v2/services-v2.ts +114 -236
- package/test/unit/spec/webex-core.js +0 -2
- package/test/unit/spec/webex-internal-core.js +0 -2
|
@@ -34,53 +34,6 @@ describe('webex-core', () => {
|
|
|
34
34
|
});
|
|
35
35
|
|
|
36
36
|
describe('#initialize', () => {
|
|
37
|
-
it('listens for "loaded" event instead of "ready" to avoid deadlock', () => {
|
|
38
|
-
services.listenToOnce = sinon.stub();
|
|
39
|
-
services.initialize();
|
|
40
|
-
|
|
41
|
-
// Second listenToOnce call should be for 'loaded' event
|
|
42
|
-
assert.equal(services.listenToOnce.getCall(1).args[1], 'loaded');
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
it('services.ready starts as false', () => {
|
|
46
|
-
assert.isFalse(services.ready);
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
it('sets ready to true and triggers services:initialized when initialization succeeds with credentials', async () => {
|
|
50
|
-
services.listenToOnce = sinon.stub();
|
|
51
|
-
services.initServiceCatalogs = sinon.stub().returns(Promise.resolve());
|
|
52
|
-
services.trigger = sinon.stub();
|
|
53
|
-
services.webex.credentials = {
|
|
54
|
-
supertoken: {
|
|
55
|
-
access_token: 'token',
|
|
56
|
-
},
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
services.initialize();
|
|
60
|
-
|
|
61
|
-
// call the onLoaded callback
|
|
62
|
-
services.listenToOnce.getCall(1).args[2]();
|
|
63
|
-
await waitForAsync();
|
|
64
|
-
|
|
65
|
-
assert.isTrue(services.ready);
|
|
66
|
-
sinon.assert.calledWith(services.trigger, 'services:initialized');
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
it('sets ready to true and triggers services:initialized when initialization succeeds without credentials', async () => {
|
|
70
|
-
services.listenToOnce = sinon.stub();
|
|
71
|
-
services.collectPreauthCatalog = sinon.stub().returns(Promise.resolve());
|
|
72
|
-
services.trigger = sinon.stub();
|
|
73
|
-
|
|
74
|
-
services.initialize();
|
|
75
|
-
|
|
76
|
-
// call the onLoaded callback
|
|
77
|
-
services.listenToOnce.getCall(1).args[2]();
|
|
78
|
-
await waitForAsync();
|
|
79
|
-
|
|
80
|
-
assert.isTrue(services.ready);
|
|
81
|
-
sinon.assert.calledWith(services.trigger, 'services:initialized');
|
|
82
|
-
});
|
|
83
|
-
|
|
84
37
|
it('initFailed is false when initialization succeeds and credentials are available', async () => {
|
|
85
38
|
services.listenToOnce = sinon.stub();
|
|
86
39
|
services.initServiceCatalogs = sinon.stub().returns(Promise.resolve());
|
|
@@ -92,7 +45,7 @@ describe('webex-core', () => {
|
|
|
92
45
|
|
|
93
46
|
services.initialize();
|
|
94
47
|
|
|
95
|
-
// call the
|
|
48
|
+
// call the onReady callback
|
|
96
49
|
services.listenToOnce.getCall(1).args[2]();
|
|
97
50
|
await waitForAsync();
|
|
98
51
|
|
|
@@ -105,7 +58,7 @@ describe('webex-core', () => {
|
|
|
105
58
|
|
|
106
59
|
services.initialize();
|
|
107
60
|
|
|
108
|
-
// call the
|
|
61
|
+
// call the onReady callback
|
|
109
62
|
services.listenToOnce.getCall(1).args[2]();
|
|
110
63
|
await waitForAsync();
|
|
111
64
|
|
|
@@ -114,9 +67,9 @@ describe('webex-core', () => {
|
|
|
114
67
|
|
|
115
68
|
it.each([
|
|
116
69
|
{error: new Error('failed'), expectedMessage: 'failed'},
|
|
117
|
-
{error: undefined, expectedMessage: undefined}
|
|
70
|
+
{error: undefined, expectedMessage: undefined}
|
|
118
71
|
])(
|
|
119
|
-
'sets initFailed to true when collectPreauthCatalog errors
|
|
72
|
+
'sets initFailed to true when collectPreauthCatalog errors',
|
|
120
73
|
async ({error, expectedMessage}) => {
|
|
121
74
|
services.collectPreauthCatalog = sinon.stub().callsFake(() => {
|
|
122
75
|
return Promise.reject(error);
|
|
@@ -124,18 +77,15 @@ describe('webex-core', () => {
|
|
|
124
77
|
|
|
125
78
|
services.listenToOnce = sinon.stub();
|
|
126
79
|
services.logger.error = sinon.stub();
|
|
127
|
-
services.trigger = sinon.stub();
|
|
128
80
|
|
|
129
81
|
services.initialize();
|
|
130
82
|
|
|
131
|
-
// call the
|
|
83
|
+
// call the onReady callback
|
|
132
84
|
services.listenToOnce.getCall(1).args[2]();
|
|
133
85
|
|
|
134
86
|
await waitForAsync();
|
|
135
87
|
|
|
136
88
|
assert.isTrue(services.initFailed);
|
|
137
|
-
assert.isTrue(services.ready);
|
|
138
|
-
sinon.assert.calledWith(services.trigger, 'services:initialized');
|
|
139
89
|
sinon.assert.calledWith(
|
|
140
90
|
services.logger.error,
|
|
141
91
|
`services: failed to init initial services when no credentials available, ${expectedMessage}`
|
|
@@ -145,132 +95,32 @@ describe('webex-core', () => {
|
|
|
145
95
|
|
|
146
96
|
it.each([
|
|
147
97
|
{error: new Error('failed'), expectedMessage: 'failed'},
|
|
148
|
-
{error: undefined, expectedMessage: undefined}
|
|
149
|
-
])(
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
services.initServiceCatalogs = sinon.stub().callsFake(() => {
|
|
153
|
-
return Promise.reject(error);
|
|
154
|
-
});
|
|
155
|
-
services.webex.credentials = {
|
|
156
|
-
supertoken: {
|
|
157
|
-
access_token: 'token',
|
|
158
|
-
},
|
|
159
|
-
};
|
|
160
|
-
|
|
161
|
-
services.listenToOnce = sinon.stub();
|
|
162
|
-
services.logger.error = sinon.stub();
|
|
163
|
-
services.trigger = sinon.stub();
|
|
164
|
-
|
|
165
|
-
services.initialize();
|
|
166
|
-
|
|
167
|
-
// call the onLoaded callback
|
|
168
|
-
services.listenToOnce.getCall(1).args[2]();
|
|
169
|
-
|
|
170
|
-
await waitForAsync();
|
|
171
|
-
|
|
172
|
-
assert.isTrue(services.initFailed);
|
|
173
|
-
assert.isTrue(services.ready);
|
|
174
|
-
sinon.assert.calledWith(services.trigger, 'services:initialized');
|
|
175
|
-
sinon.assert.calledWith(
|
|
176
|
-
services.logger.error,
|
|
177
|
-
`services: failed to init initial services when credentials available, ${expectedMessage}`
|
|
178
|
-
);
|
|
179
|
-
}
|
|
180
|
-
);
|
|
181
|
-
|
|
182
|
-
describe('when change:canAuthorize fires', () => {
|
|
183
|
-
it('calls initServiceCatalogs when canAuthorize becomes true and postauth catalog is not ready', async () => {
|
|
184
|
-
services.listenToOnce = sinon.stub();
|
|
185
|
-
services._loadCatalogFromCache = sinon.stub().returns(Promise.resolve(false));
|
|
186
|
-
services.collectPreauthCatalog = sinon.stub().returns(Promise.resolve());
|
|
187
|
-
services.initServiceCatalogs = sinon.stub().returns(Promise.resolve());
|
|
188
|
-
services.trigger = sinon.stub();
|
|
189
|
-
// Ensure no credentials so we go to the preauth path
|
|
190
|
-
services.webex.credentials = {};
|
|
191
|
-
|
|
192
|
-
services.initialize();
|
|
193
|
-
|
|
194
|
-
// Get the catalog after initialize creates it
|
|
195
|
-
const testCatalog = services._getCatalog();
|
|
196
|
-
testCatalog.status.postauth.ready = false;
|
|
197
|
-
|
|
198
|
-
// call the onLoaded callback (preauth path)
|
|
199
|
-
services.listenToOnce.getCall(1).args[2]();
|
|
200
|
-
await waitForAsync();
|
|
201
|
-
|
|
202
|
-
// Now set canAuthorize to true to simulate auth completing
|
|
203
|
-
services.webex.canAuthorize = true;
|
|
204
|
-
|
|
205
|
-
// call the change:canAuthorize callback (should be call 2)
|
|
206
|
-
services.listenToOnce.getCall(2).args[2]();
|
|
207
|
-
await waitForAsync();
|
|
208
|
-
|
|
209
|
-
sinon.assert.calledOnce(services.initServiceCatalogs);
|
|
210
|
-
assert.isTrue(testCatalog.isReady);
|
|
211
|
-
});
|
|
212
|
-
|
|
213
|
-
it('does not call initServiceCatalogs when postauth catalog is already ready', async () => {
|
|
214
|
-
services.listenToOnce = sinon.stub();
|
|
215
|
-
services._loadCatalogFromCache = sinon.stub().returns(Promise.resolve(false));
|
|
216
|
-
services.collectPreauthCatalog = sinon.stub().returns(Promise.resolve());
|
|
217
|
-
services.initServiceCatalogs = sinon.stub().returns(Promise.resolve());
|
|
218
|
-
services.trigger = sinon.stub();
|
|
219
|
-
// Ensure no credentials so we go to the preauth path
|
|
220
|
-
services.webex.credentials = {};
|
|
221
|
-
|
|
222
|
-
services.initialize();
|
|
223
|
-
|
|
224
|
-
// Get the catalog after initialize creates it
|
|
225
|
-
const testCatalog = services._getCatalog();
|
|
226
|
-
|
|
227
|
-
// call the onLoaded callback (preauth path)
|
|
228
|
-
services.listenToOnce.getCall(1).args[2]();
|
|
229
|
-
await waitForAsync();
|
|
230
|
-
|
|
231
|
-
// Set canAuthorize to true and postauth as ready BEFORE calling the callback
|
|
232
|
-
services.webex.canAuthorize = true;
|
|
233
|
-
testCatalog.status.postauth.ready = true;
|
|
234
|
-
|
|
235
|
-
// call the change:canAuthorize callback (should be call 2)
|
|
236
|
-
services.listenToOnce.getCall(2).args[2]();
|
|
237
|
-
await waitForAsync();
|
|
238
|
-
|
|
239
|
-
sinon.assert.notCalled(services.initServiceCatalogs);
|
|
98
|
+
{error: undefined, expectedMessage: undefined}
|
|
99
|
+
])('sets initFailed to true when initServiceCatalogs errors', async ({error, expectedMessage}) => {
|
|
100
|
+
services.initServiceCatalogs = sinon.stub().callsFake(() => {
|
|
101
|
+
return Promise.reject(error);
|
|
240
102
|
});
|
|
103
|
+
services.webex.credentials = {
|
|
104
|
+
supertoken: {
|
|
105
|
+
access_token: 'token'
|
|
106
|
+
}
|
|
107
|
+
}
|
|
241
108
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
services._loadCatalogFromCache = sinon.stub().returns(Promise.resolve(false));
|
|
245
|
-
services.collectPreauthCatalog = sinon.stub().returns(Promise.resolve());
|
|
246
|
-
services.initServiceCatalogs = sinon.stub().rejects(new Error('auth failed'));
|
|
247
|
-
services.logger.error = sinon.stub();
|
|
248
|
-
services.trigger = sinon.stub();
|
|
249
|
-
// Ensure no credentials so we go to the preauth path
|
|
250
|
-
services.webex.credentials = {};
|
|
251
|
-
|
|
252
|
-
services.initialize();
|
|
253
|
-
|
|
254
|
-
// Get the catalog after initialize creates it
|
|
255
|
-
const testCatalog = services._getCatalog();
|
|
256
|
-
testCatalog.status.postauth.ready = false;
|
|
109
|
+
services.listenToOnce = sinon.stub();
|
|
110
|
+
services.logger.error = sinon.stub();
|
|
257
111
|
|
|
258
|
-
|
|
259
|
-
services.listenToOnce.getCall(1).args[2]();
|
|
260
|
-
await waitForAsync();
|
|
112
|
+
services.initialize();
|
|
261
113
|
|
|
262
|
-
|
|
263
|
-
|
|
114
|
+
// call the onReady callback
|
|
115
|
+
services.listenToOnce.getCall(1).args[2]();
|
|
264
116
|
|
|
265
|
-
|
|
266
|
-
services.listenToOnce.getCall(2).args[2]();
|
|
267
|
-
await waitForAsync();
|
|
117
|
+
await waitForAsync();
|
|
268
118
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
119
|
+
assert.isTrue(services.initFailed);
|
|
120
|
+
sinon.assert.calledWith(
|
|
121
|
+
services.logger.error,
|
|
122
|
+
`services: failed to init initial services when credentials available, ${expectedMessage}`
|
|
123
|
+
);
|
|
274
124
|
});
|
|
275
125
|
});
|
|
276
126
|
|
|
@@ -309,7 +159,7 @@ describe('webex-core', () => {
|
|
|
309
159
|
|
|
310
160
|
services.collectPreauthCatalog = sinon.stub().callsFake(() => {
|
|
311
161
|
return Promise.resolve();
|
|
312
|
-
})
|
|
162
|
+
})
|
|
313
163
|
|
|
314
164
|
services.updateServices = sinon.stub().callsFake(() => {
|
|
315
165
|
return Promise.reject(error);
|
|
@@ -404,7 +254,7 @@ describe('webex-core', () => {
|
|
|
404
254
|
discovery: {
|
|
405
255
|
sqdiscovery: 'https://test.ciscospark.com/v1/region',
|
|
406
256
|
},
|
|
407
|
-
}
|
|
257
|
+
}
|
|
408
258
|
};
|
|
409
259
|
});
|
|
410
260
|
|
|
@@ -424,8 +274,8 @@ describe('webex-core', () => {
|
|
|
424
274
|
assert.calledWith(webex.request, {
|
|
425
275
|
uri: 'https://test.ciscospark.com/v1/region',
|
|
426
276
|
addAuthHeader: false,
|
|
427
|
-
headers: {'spark-user-agent': null},
|
|
428
|
-
timeout: 5000
|
|
277
|
+
headers: { 'spark-user-agent': null },
|
|
278
|
+
timeout: 5000
|
|
429
279
|
});
|
|
430
280
|
});
|
|
431
281
|
});
|
|
@@ -482,6 +332,7 @@ describe('webex-core', () => {
|
|
|
482
332
|
});
|
|
483
333
|
|
|
484
334
|
describe('#_fetchNewServiceHostmap()', () => {
|
|
335
|
+
|
|
485
336
|
beforeEach(() => {
|
|
486
337
|
sinon.spy(webex.internal.newMetrics.callDiagnosticLatencies, 'measureLatency');
|
|
487
338
|
});
|
|
@@ -495,20 +346,17 @@ describe('webex-core', () => {
|
|
|
495
346
|
|
|
496
347
|
sinon.stub(services, '_formatReceivedHostmap').resolves(mapResponse);
|
|
497
348
|
sinon.stub(services, 'request').resolves({});
|
|
498
|
-
|
|
349
|
+
|
|
499
350
|
const mapResult = await services._fetchNewServiceHostmap({from: 'limited'});
|
|
500
351
|
|
|
501
352
|
assert.calledOnceWithExactly(services.request, {
|
|
502
353
|
method: 'GET',
|
|
503
354
|
service: 'u2c',
|
|
504
355
|
resource: '/limited/catalog',
|
|
505
|
-
qs: {format: 'hostmap'}
|
|
506
|
-
}
|
|
507
|
-
assert.calledOnceWithExactly(
|
|
508
|
-
webex.internal.newMetrics.callDiagnosticLatencies.measureLatency,
|
|
509
|
-
sinon.match.func,
|
|
510
|
-
'internal.get.u2c.time'
|
|
356
|
+
qs: {format: 'hostmap'}
|
|
357
|
+
}
|
|
511
358
|
);
|
|
359
|
+
assert.calledOnceWithExactly(webex.internal.newMetrics.callDiagnosticLatencies.measureLatency, sinon.match.func, 'internal.get.u2c.time');
|
|
512
360
|
});
|
|
513
361
|
|
|
514
362
|
it('checks service request rejects', async () => {
|
|
@@ -516,7 +364,7 @@ describe('webex-core', () => {
|
|
|
516
364
|
|
|
517
365
|
sinon.spy(services, '_formatReceivedHostmap');
|
|
518
366
|
sinon.stub(services, 'request').rejects(error);
|
|
519
|
-
|
|
367
|
+
|
|
520
368
|
const promise = services._fetchNewServiceHostmap({from: 'limited'});
|
|
521
369
|
const rejectedValue = await assert.isRejected(promise);
|
|
522
370
|
|
|
@@ -528,13 +376,10 @@ describe('webex-core', () => {
|
|
|
528
376
|
method: 'GET',
|
|
529
377
|
service: 'u2c',
|
|
530
378
|
resource: '/limited/catalog',
|
|
531
|
-
qs: {format: 'hostmap'}
|
|
532
|
-
}
|
|
533
|
-
assert.calledOnceWithExactly(
|
|
534
|
-
webex.internal.newMetrics.callDiagnosticLatencies.measureLatency,
|
|
535
|
-
sinon.match.func,
|
|
536
|
-
'internal.get.u2c.time'
|
|
379
|
+
qs: {format: 'hostmap'}
|
|
380
|
+
}
|
|
537
381
|
);
|
|
382
|
+
assert.calledOnceWithExactly(webex.internal.newMetrics.callDiagnosticLatencies.measureLatency, sinon.match.func, 'internal.get.u2c.time');
|
|
538
383
|
});
|
|
539
384
|
});
|
|
540
385
|
|
|
@@ -565,6 +410,7 @@ describe('webex-core', () => {
|
|
|
565
410
|
});
|
|
566
411
|
|
|
567
412
|
it('returns the original uri if the hostmap has no hosts for the host', () => {
|
|
413
|
+
|
|
568
414
|
services._hostCatalog = {
|
|
569
415
|
'example.com': [],
|
|
570
416
|
};
|
|
@@ -728,7 +574,8 @@ describe('webex-core', () => {
|
|
|
728
574
|
id: '0:0:0:different-e-x',
|
|
729
575
|
},
|
|
730
576
|
],
|
|
731
|
-
'example-f.com': [
|
|
577
|
+
'example-f.com': [
|
|
578
|
+
],
|
|
732
579
|
},
|
|
733
580
|
format: 'hostmap',
|
|
734
581
|
};
|
|
@@ -936,7 +783,7 @@ describe('webex-core', () => {
|
|
|
936
783
|
defaultUrl: 'https://example-g.com/api/v1',
|
|
937
784
|
hosts: [],
|
|
938
785
|
name: 'example-g',
|
|
939
|
-
}
|
|
786
|
+
}
|
|
940
787
|
]);
|
|
941
788
|
});
|
|
942
789
|
|
|
@@ -990,59 +837,34 @@ describe('webex-core', () => {
|
|
|
990
837
|
assert.equal(webex.config.credentials.authorizeUrl, authUrl);
|
|
991
838
|
});
|
|
992
839
|
});
|
|
993
|
-
|
|
840
|
+
|
|
994
841
|
describe('#getMobiusClusters', () => {
|
|
995
842
|
it('returns unique mobius host entries from hostCatalog', () => {
|
|
996
843
|
// Arrange: two hostCatalog keys, with duplicate mobius host across keys
|
|
997
844
|
services._hostCatalog = {
|
|
998
845
|
'mobius-us-east-2.prod.infra.webex.com': [
|
|
999
|
-
{
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
priority: 5,
|
|
1003
|
-
id: 'urn:TEAM:xyz:mobius',
|
|
1004
|
-
},
|
|
1005
|
-
{
|
|
1006
|
-
host: 'mobius-eu-central-1.prod.infra.webex.com',
|
|
1007
|
-
ttl: -1,
|
|
1008
|
-
priority: 10,
|
|
1009
|
-
id: 'urn:TEAM:xyz:mobius',
|
|
1010
|
-
},
|
|
1011
|
-
],
|
|
846
|
+
{host: 'mobius-us-east-2.prod.infra.webex.com', ttl: -1, priority: 5, id: 'urn:TEAM:xyz:mobius'},
|
|
847
|
+
{host: 'mobius-eu-central-1.prod.infra.webex.com', ttl: -1, priority: 10, id: 'urn:TEAM:xyz:mobius'},
|
|
848
|
+
],
|
|
1012
849
|
|
|
1013
850
|
'mobius-eu-central-1.prod.infra.webex.com': [
|
|
1014
|
-
{
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
priority: 7,
|
|
1018
|
-
id: 'urn:TEAM:xyz:mobius',
|
|
1019
|
-
}, // duplicate host
|
|
1020
|
-
],
|
|
1021
|
-
'wdm-a.webex.com': [
|
|
851
|
+
{host: 'mobius-us-east-2.prod.infra.webex.com', ttl: -1, priority: 7, id: 'urn:TEAM:xyz:mobius'}, // duplicate host
|
|
852
|
+
],
|
|
853
|
+
'wdm-a.webex.com' : [
|
|
1022
854
|
{host: 'wdm-a.webex.com', ttl: -1, priority: 5, id: 'urn:TEAM:xyz:wdm'},
|
|
1023
|
-
]
|
|
855
|
+
]
|
|
1024
856
|
};
|
|
1025
|
-
|
|
857
|
+
|
|
1026
858
|
// Act
|
|
1027
859
|
const clusters = services.getMobiusClusters();
|
|
1028
|
-
|
|
860
|
+
|
|
1029
861
|
// Assert
|
|
1030
862
|
// deduped; only mobius entries; keeps first seen mobius-a, then mobius-b
|
|
1031
863
|
assert.deepEqual(
|
|
1032
864
|
clusters.map(({host, id, ttl, priority}) => ({host, id, ttl, priority})),
|
|
1033
865
|
[
|
|
1034
|
-
{
|
|
1035
|
-
|
|
1036
|
-
id: 'urn:TEAM:xyz:mobius',
|
|
1037
|
-
ttl: -1,
|
|
1038
|
-
priority: 5,
|
|
1039
|
-
},
|
|
1040
|
-
{
|
|
1041
|
-
host: 'mobius-eu-central-1.prod.infra.webex.com',
|
|
1042
|
-
id: 'urn:TEAM:xyz:mobius',
|
|
1043
|
-
ttl: -1,
|
|
1044
|
-
priority: 10,
|
|
1045
|
-
},
|
|
866
|
+
{host: 'mobius-us-east-2.prod.infra.webex.com', id: 'urn:TEAM:xyz:mobius', ttl: -1, priority: 5},
|
|
867
|
+
{host: 'mobius-eu-central-1.prod.infra.webex.com', id: 'urn:TEAM:xyz:mobius', ttl: -1, priority: 10},
|
|
1046
868
|
]
|
|
1047
869
|
);
|
|
1048
870
|
});
|
|
@@ -1051,31 +873,31 @@ describe('webex-core', () => {
|
|
|
1051
873
|
describe('#isValidHost', () => {
|
|
1052
874
|
beforeEach(() => {
|
|
1053
875
|
// Setting up a mock host catalog
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
876
|
+
services._hostCatalog = {
|
|
877
|
+
"audit-ci-m.wbx2.com": [
|
|
878
|
+
{
|
|
879
|
+
"host": "audit-ci-m.wbx2.com",
|
|
880
|
+
"ttl": -1,
|
|
881
|
+
"priority": 5,
|
|
882
|
+
"id": "urn:IDENTITY:PA61:adminAudit"
|
|
883
|
+
},
|
|
884
|
+
{
|
|
885
|
+
"host": "audit-ci-m.wbx2.com",
|
|
886
|
+
"ttl": -1,
|
|
887
|
+
"priority": 5,
|
|
888
|
+
"id": "urn:IDENTITY:PA61:adminAuditV2"
|
|
889
|
+
}
|
|
890
|
+
],
|
|
891
|
+
"mercury-connection-partition0-r.wbx2.com": [
|
|
892
|
+
{
|
|
893
|
+
"host": "mercury-connection-partition0-r.wbx2.com",
|
|
894
|
+
"ttl": -1,
|
|
895
|
+
"priority": 5,
|
|
896
|
+
"id": "urn:TEAM:us-west-2_r:mercuryConnectionPartition0"
|
|
897
|
+
}
|
|
898
|
+
],
|
|
899
|
+
"empty.com": []
|
|
900
|
+
};
|
|
1079
901
|
});
|
|
1080
902
|
afterAll(() => {
|
|
1081
903
|
// Clean up the mock host catalog
|
|
@@ -1100,13 +922,64 @@ describe('webex-core', () => {
|
|
|
1100
922
|
});
|
|
1101
923
|
});
|
|
1102
924
|
|
|
925
|
+
describe('#isIntegrationEnvironment', () => {
|
|
926
|
+
it('returns true when u2c URL contains "intb"', () => {
|
|
927
|
+
services.webex.config = {
|
|
928
|
+
services: {
|
|
929
|
+
discovery: {
|
|
930
|
+
u2c: 'https://u2c-intb.ciscospark.com/u2c/api/v1',
|
|
931
|
+
},
|
|
932
|
+
},
|
|
933
|
+
};
|
|
934
|
+
assert.isTrue(services.isIntegrationEnvironment());
|
|
935
|
+
});
|
|
936
|
+
|
|
937
|
+
it('returns false when u2c URL does not contain "intb" (production)', () => {
|
|
938
|
+
services.webex.config = {
|
|
939
|
+
services: {
|
|
940
|
+
discovery: {
|
|
941
|
+
u2c: 'https://u2c.wbx2.com/u2c/api/v1',
|
|
942
|
+
},
|
|
943
|
+
},
|
|
944
|
+
};
|
|
945
|
+
assert.isFalse(services.isIntegrationEnvironment());
|
|
946
|
+
});
|
|
947
|
+
|
|
948
|
+
it('returns false when u2c URL is for FedRAMP', () => {
|
|
949
|
+
services.webex.config = {
|
|
950
|
+
services: {
|
|
951
|
+
discovery: {
|
|
952
|
+
u2c: 'https://u2c.gov.ciscospark.com/u2c/api/v1',
|
|
953
|
+
},
|
|
954
|
+
},
|
|
955
|
+
};
|
|
956
|
+
assert.isFalse(services.isIntegrationEnvironment());
|
|
957
|
+
});
|
|
958
|
+
|
|
959
|
+
it('returns false when u2c URL is undefined', () => {
|
|
960
|
+
services.webex.config = {
|
|
961
|
+
services: {
|
|
962
|
+
discovery: {
|
|
963
|
+
u2c: undefined,
|
|
964
|
+
},
|
|
965
|
+
},
|
|
966
|
+
};
|
|
967
|
+
assert.isFalse(services.isIntegrationEnvironment());
|
|
968
|
+
});
|
|
969
|
+
|
|
970
|
+
it('returns false when config is not available', () => {
|
|
971
|
+
services.webex.config = undefined;
|
|
972
|
+
assert.isFalse(services.isIntegrationEnvironment());
|
|
973
|
+
});
|
|
974
|
+
});
|
|
975
|
+
|
|
1103
976
|
describe('U2C catalog cache behavior', () => {
|
|
1104
977
|
let webex;
|
|
1105
978
|
let services;
|
|
1106
979
|
let catalog;
|
|
1107
980
|
let localStorageBackup;
|
|
1108
981
|
let windowBackup;
|
|
1109
|
-
|
|
982
|
+
|
|
1110
983
|
const makeLocalStorageShim = () => {
|
|
1111
984
|
const store = new Map();
|
|
1112
985
|
return {
|
|
@@ -1116,16 +989,13 @@ describe('webex-core', () => {
|
|
|
1116
989
|
_store: store,
|
|
1117
990
|
};
|
|
1118
991
|
};
|
|
1119
|
-
|
|
992
|
+
|
|
1120
993
|
beforeEach(() => {
|
|
1121
994
|
// Build a fresh webex instance
|
|
1122
|
-
webex = new MockWebex({
|
|
1123
|
-
children: {services: Services},
|
|
1124
|
-
config: {credentials: {federation: true}},
|
|
1125
|
-
});
|
|
995
|
+
webex = new MockWebex({children: {services: Services}, config: {credentials: {federation: true}}});
|
|
1126
996
|
services = webex.internal.services;
|
|
1127
997
|
catalog = services._getCatalog();
|
|
1128
|
-
|
|
998
|
+
|
|
1129
999
|
// enable U2C caching feature flag in tests that rely on localStorage writes/reads
|
|
1130
1000
|
services.webex.config = services.webex.config || {};
|
|
1131
1001
|
services.webex.config.calling = {...(services.webex.config.calling || {}), cacheU2C: true};
|
|
@@ -1137,15 +1007,13 @@ describe('webex-core', () => {
|
|
|
1137
1007
|
global.window.localStorage = makeLocalStorageShim();
|
|
1138
1008
|
// Ensure code under test uses our shim via util method
|
|
1139
1009
|
sinon.stub(services, '_getLocalStorageSafe').returns(global.window.localStorage);
|
|
1140
|
-
|
|
1010
|
+
|
|
1141
1011
|
// Stub the formatter so we don't need a full hostmap payload in tests
|
|
1142
|
-
sinon
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
{name: 'hydra', defaultUrl: 'https://api.ciscospark.com/v1', hosts: []},
|
|
1146
|
-
]);
|
|
1012
|
+
sinon.stub(services, '_formatReceivedHostmap').callsFake(() => [
|
|
1013
|
+
{name: 'hydra', defaultUrl: 'https://api.ciscospark.com/v1', hosts: []},
|
|
1014
|
+
]);
|
|
1147
1015
|
});
|
|
1148
|
-
|
|
1016
|
+
|
|
1149
1017
|
afterEach(() => {
|
|
1150
1018
|
global.window.localStorage = localStorageBackup || undefined;
|
|
1151
1019
|
if (!windowBackup) {
|
|
@@ -1158,7 +1026,7 @@ describe('webex-core', () => {
|
|
|
1158
1026
|
services._getLocalStorageSafe.restore();
|
|
1159
1027
|
}
|
|
1160
1028
|
});
|
|
1161
|
-
|
|
1029
|
+
|
|
1162
1030
|
it('invokes initServiceCatalogs on ready, caches catalog, and stores in localStorage', async () => {
|
|
1163
1031
|
// Arrange: authenticated credentials and spies
|
|
1164
1032
|
services.webex.credentials = {
|
|
@@ -1170,20 +1038,10 @@ describe('webex-core', () => {
|
|
|
1170
1038
|
const cacheSpy = sinon.spy(services, '_cacheCatalog');
|
|
1171
1039
|
const setItemSpy = sinon.spy(global.window.localStorage, 'setItem');
|
|
1172
1040
|
// Make fetch return a hostmap object and allow formatter to reduce it
|
|
1173
|
-
sinon
|
|
1174
|
-
|
|
1175
|
-
.resolves({
|
|
1176
|
-
body: {
|
|
1177
|
-
services: [],
|
|
1178
|
-
activeServices: {},
|
|
1179
|
-
timestamp: Date.now().toString(),
|
|
1180
|
-
orgId: 'urn:EXAMPLE:org',
|
|
1181
|
-
format: 'U2CV2',
|
|
1182
|
-
},
|
|
1183
|
-
});
|
|
1184
|
-
// Cause loaded callback to run immediately
|
|
1041
|
+
sinon.stub(services, 'request').resolves({body: {services: [], activeServices: {}, timestamp: Date.now().toString(), orgId: 'urn:EXAMPLE:org', format: 'U2CV2'}});
|
|
1042
|
+
// Cause ready callback to run immediately
|
|
1185
1043
|
services.listenToOnce = sinon.stub().callsFake((ctx, event, cb) => {
|
|
1186
|
-
if (event === '
|
|
1044
|
+
if (event === 'ready') cb();
|
|
1187
1045
|
});
|
|
1188
1046
|
|
|
1189
1047
|
// Act
|
|
@@ -1223,9 +1081,9 @@ describe('webex-core', () => {
|
|
|
1223
1081
|
|
|
1224
1082
|
const initSpy = sinon.spy(services, 'initServiceCatalogs');
|
|
1225
1083
|
const cacheSpy = sinon.spy(services, '_cacheCatalog');
|
|
1226
|
-
// Cause
|
|
1084
|
+
// Cause ready callback to run immediately
|
|
1227
1085
|
services.listenToOnce = sinon.stub().callsFake((ctx, event, cb) => {
|
|
1228
|
-
if (event === '
|
|
1086
|
+
if (event === 'ready') cb();
|
|
1229
1087
|
});
|
|
1230
1088
|
|
|
1231
1089
|
// Act
|
|
@@ -1233,18 +1091,9 @@ describe('webex-core', () => {
|
|
|
1233
1091
|
await waitForAsync();
|
|
1234
1092
|
|
|
1235
1093
|
// Assert: ready path found cache and skipped initServiceCatalogs
|
|
1236
|
-
assert.isFalse(
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
);
|
|
1240
|
-
assert.isTrue(
|
|
1241
|
-
services._getCatalog().status.preauth.ready,
|
|
1242
|
-
'preauth should be ready from cache'
|
|
1243
|
-
);
|
|
1244
|
-
assert.isTrue(
|
|
1245
|
-
services._getCatalog().status.postauth.ready,
|
|
1246
|
-
'postauth should be ready from cache'
|
|
1247
|
-
);
|
|
1094
|
+
assert.isFalse(initSpy.called, 'expected initServiceCatalogs to be skipped with cache present');
|
|
1095
|
+
assert.isTrue(services._getCatalog().status.preauth.ready, 'preauth should be ready from cache');
|
|
1096
|
+
assert.isTrue(services._getCatalog().status.postauth.ready, 'postauth should be ready from cache');
|
|
1248
1097
|
assert.isFalse(cacheSpy.called, 'should not write cache during warm-up-only path');
|
|
1249
1098
|
|
|
1250
1099
|
// Cleanup
|
|
@@ -1260,29 +1109,26 @@ describe('webex-core', () => {
|
|
|
1260
1109
|
preauth: {serviceLinks: {}, hostCatalog: {}},
|
|
1261
1110
|
postauth: {serviceLinks: {}, hostCatalog: {}},
|
|
1262
1111
|
};
|
|
1263
|
-
|
|
1112
|
+
|
|
1264
1113
|
window.localStorage.setItem(CATALOG_CACHE_KEY_V1, JSON.stringify(staleCached));
|
|
1265
|
-
|
|
1114
|
+
|
|
1266
1115
|
const warmed = await services._loadCatalogFromCache();
|
|
1267
|
-
|
|
1116
|
+
|
|
1268
1117
|
assert.isFalse(warmed, 'stale cache must not warm');
|
|
1269
|
-
assert.isNull(
|
|
1270
|
-
window.localStorage.getItem(CATALOG_CACHE_KEY_V1),
|
|
1271
|
-
'expired cache must be cleared'
|
|
1272
|
-
);
|
|
1118
|
+
assert.isNull(window.localStorage.getItem(CATALOG_CACHE_KEY_V1), 'expired cache must be cleared');
|
|
1273
1119
|
assert.isFalse(catalog.status.preauth.ready);
|
|
1274
1120
|
assert.isFalse(catalog.status.postauth.ready);
|
|
1275
1121
|
});
|
|
1276
|
-
|
|
1122
|
+
|
|
1277
1123
|
it('clearCatalogCache() removes the cached entry', async () => {
|
|
1278
1124
|
const CATALOG_CACHE_KEY_V1 = 'services.v1.u2cHostMap';
|
|
1279
1125
|
window.localStorage.setItem(CATALOG_CACHE_KEY_V1, JSON.stringify({cachedAt: Date.now()}));
|
|
1280
|
-
|
|
1126
|
+
|
|
1281
1127
|
await services.clearCatalogCache();
|
|
1282
|
-
|
|
1128
|
+
|
|
1283
1129
|
assert.isNull(window.localStorage.getItem(CATALOG_CACHE_KEY_V1), 'cache should be cleared');
|
|
1284
1130
|
});
|
|
1285
|
-
|
|
1131
|
+
|
|
1286
1132
|
it('still fetches when forceRefresh=true even if ready', async () => {
|
|
1287
1133
|
const CATALOG_CACHE_KEY_V1 = 'services.v1.u2cHostMap';
|
|
1288
1134
|
window.localStorage.setItem(
|
|
@@ -1294,24 +1140,20 @@ describe('webex-core', () => {
|
|
|
1294
1140
|
postauth: {serviceLinks: {}, hostCatalog: {}},
|
|
1295
1141
|
})
|
|
1296
1142
|
);
|
|
1297
|
-
|
|
1143
|
+
|
|
1298
1144
|
// warm from cache
|
|
1299
1145
|
const warmed = await services._loadCatalogFromCache();
|
|
1300
1146
|
assert.isTrue(warmed);
|
|
1301
1147
|
assert.isTrue(catalog.status.preauth.ready);
|
|
1302
1148
|
assert.isTrue(catalog.status.postauth.ready);
|
|
1303
|
-
|
|
1149
|
+
|
|
1304
1150
|
const fetchSpy = sinon.spy(services, '_fetchNewServiceHostmap');
|
|
1305
|
-
|
|
1151
|
+
|
|
1306
1152
|
// with forceRefresh we should fetch despite ready=true
|
|
1307
|
-
await services.updateServices({
|
|
1308
|
-
from: 'limited',
|
|
1309
|
-
query: {orgId: 'urn:EXAMPLE:org'},
|
|
1310
|
-
forceRefresh: true,
|
|
1311
|
-
});
|
|
1153
|
+
await services.updateServices({from: 'limited', query: {orgId: 'urn:EXAMPLE:org'}, forceRefresh: true});
|
|
1312
1154
|
// pass an empty query to avoid spreading undefined in qs construction
|
|
1313
1155
|
await services.updateServices({forceRefresh: true});
|
|
1314
|
-
|
|
1156
|
+
|
|
1315
1157
|
assert.isTrue(fetchSpy.called, 'forceRefresh should bypass cache short-circuit');
|
|
1316
1158
|
fetchSpy.restore();
|
|
1317
1159
|
});
|
|
@@ -1372,11 +1214,9 @@ describe('webex-core', () => {
|
|
|
1372
1214
|
);
|
|
1373
1215
|
// formatter returns at least one entry to mark ready
|
|
1374
1216
|
services._formatReceivedHostmap.restore && services._formatReceivedHostmap.restore();
|
|
1375
|
-
sinon
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
{name: 'hydra', defaultUrl: 'https://api.ciscospark.com/v1', hosts: []},
|
|
1379
|
-
]);
|
|
1217
|
+
sinon.stub(services, '_formatReceivedHostmap').callsFake(() => [
|
|
1218
|
+
{name: 'hydra', defaultUrl: 'https://api.ciscospark.com/v1', hosts: []},
|
|
1219
|
+
]);
|
|
1380
1220
|
|
|
1381
1221
|
const warmed = await services._loadCatalogFromCache();
|
|
1382
1222
|
assert.isTrue(warmed);
|
|
@@ -1398,11 +1238,9 @@ describe('webex-core', () => {
|
|
|
1398
1238
|
})
|
|
1399
1239
|
);
|
|
1400
1240
|
services._formatReceivedHostmap.restore && services._formatReceivedHostmap.restore();
|
|
1401
|
-
sinon
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
{name: 'hydra', defaultUrl: 'https://api.ciscospark.com/v1', hosts: []},
|
|
1405
|
-
]);
|
|
1241
|
+
sinon.stub(services, '_formatReceivedHostmap').callsFake(() => [
|
|
1242
|
+
{name: 'hydra', defaultUrl: 'https://api.ciscospark.com/v1', hosts: []},
|
|
1243
|
+
]);
|
|
1406
1244
|
|
|
1407
1245
|
const warmed = await services._loadCatalogFromCache();
|
|
1408
1246
|
// function returns true if overall cache path succeeded; we only verify group readiness
|
|
@@ -1429,17 +1267,12 @@ describe('webex-core', () => {
|
|
|
1429
1267
|
})
|
|
1430
1268
|
);
|
|
1431
1269
|
services._formatReceivedHostmap.restore && services._formatReceivedHostmap.restore();
|
|
1432
|
-
sinon
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
{name: 'hydra', defaultUrl: 'https://api.ciscospark.com/v1', hosts: []},
|
|
1436
|
-
]);
|
|
1270
|
+
sinon.stub(services, '_formatReceivedHostmap').callsFake(() => [
|
|
1271
|
+
{name: 'hydra', defaultUrl: 'https://api.ciscospark.com/v1', hosts: []},
|
|
1272
|
+
]);
|
|
1437
1273
|
|
|
1438
1274
|
await services._loadCatalogFromCache();
|
|
1439
|
-
assert.isFalse(
|
|
1440
|
-
catalog.status.preauth.ready,
|
|
1441
|
-
'preauth should not warm on selection mismatch'
|
|
1442
|
-
);
|
|
1275
|
+
assert.isFalse(catalog.status.preauth.ready, 'preauth should not warm on selection mismatch');
|
|
1443
1276
|
});
|
|
1444
1277
|
|
|
1445
1278
|
it('skips warming when environment fingerprint mismatches', async () => {
|
|
@@ -1450,10 +1283,7 @@ describe('webex-core', () => {
|
|
|
1450
1283
|
JSON.stringify({
|
|
1451
1284
|
cachedAt: Date.now(),
|
|
1452
1285
|
env: {fedramp: false, u2cDiscoveryUrl: 'https://u2c.other.com/u2c/api/v1'},
|
|
1453
|
-
preauth: {
|
|
1454
|
-
hostMap: {serviceLinks: {}, hostCatalog: {}},
|
|
1455
|
-
meta: {selectionType: 'mode', selectionValue: 'DEFAULT_BY_PROXIMITY'},
|
|
1456
|
-
},
|
|
1286
|
+
preauth: {hostMap: {serviceLinks: {}, hostCatalog: {}}, meta: {selectionType: 'mode', selectionValue: 'DEFAULT_BY_PROXIMITY'}},
|
|
1457
1287
|
})
|
|
1458
1288
|
);
|
|
1459
1289
|
// current env
|
|
@@ -1468,6 +1298,7 @@ describe('webex-core', () => {
|
|
|
1468
1298
|
assert.isFalse(catalog.status.postauth.ready);
|
|
1469
1299
|
});
|
|
1470
1300
|
});
|
|
1301
|
+
|
|
1471
1302
|
});
|
|
1472
1303
|
});
|
|
1473
1304
|
/* eslint-enable no-underscore-dangle */
|