@webex/webex-core 3.12.0-next.5 → 3.12.0-next.50
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/README.md +5 -2
- package/dist/config.js +38 -0
- package/dist/config.js.map +1 -1
- package/dist/credentials-config.js +12 -0
- package/dist/credentials-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 +23 -7
- package/dist/lib/batcher.js.map +1 -1
- package/dist/lib/credentials/credentials.js +103 -4
- package/dist/lib/credentials/credentials.js.map +1 -1
- package/dist/lib/credentials/token.js +1 -1
- package/dist/lib/domains.js +90 -0
- package/dist/lib/domains.js.map +1 -0
- package/dist/lib/services/service-catalog.js +108 -18
- package/dist/lib/services/service-catalog.js.map +1 -1
- package/dist/lib/services/services.js +232 -34
- package/dist/lib/services/services.js.map +1 -1
- package/dist/lib/services-v2/service-catalog.js +14 -13
- package/dist/lib/services-v2/service-catalog.js.map +1 -1
- package/dist/lib/services-v2/services-v2.js +241 -37
- 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 +13 -13
- package/src/config.js +42 -0
- package/src/credentials-config.js +13 -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/batcher.js +25 -10
- package/src/lib/credentials/credentials.js +93 -3
- package/src/lib/domains.ts +94 -0
- package/src/lib/services/service-catalog.js +112 -16
- package/src/lib/services/services.js +208 -20
- package/src/lib/services-v2/service-catalog.ts +16 -12
- package/src/lib/services-v2/services-v2.ts +218 -21
- package/src/webex-core.js +8 -0
- package/test/fixtures/activation-email.ts +22 -0
- package/test/integration/spec/services/services.js +183 -112
- package/test/integration/spec/services-v2/services-v2.js +169 -98
- package/test/unit/spec/credentials/credentials.js +247 -3
- package/test/unit/spec/interceptors/auth.js +56 -0
- package/test/unit/spec/interceptors/catalog-url.js +224 -0
- package/test/unit/spec/interceptors/payload-transformer.js +119 -1
- package/test/unit/spec/lib/batcher.js +56 -0
- package/test/unit/spec/services/service-catalog.js +368 -11
- package/test/unit/spec/services/services.js +440 -2
- package/test/unit/spec/services-v2/service-catalog.ts +134 -11
- package/test/unit/spec/services-v2/services-v2.ts +467 -0
- package/test/unit/spec/webex-core.js +77 -1
|
@@ -3,6 +3,8 @@ import AmpState from 'ampersand-state';
|
|
|
3
3
|
import {union} from 'lodash';
|
|
4
4
|
import ServiceDetail from './service-detail';
|
|
5
5
|
import {IServiceDetail, ServiceGroup} from './types';
|
|
6
|
+
import {matchAllowedDomain, normalizeAllowedDomains} from '../domains';
|
|
7
|
+
import {matchesParsedCatalogUrl, parseCatalogUrl} from '../services/service-catalog';
|
|
6
8
|
|
|
7
9
|
/**
|
|
8
10
|
* @class
|
|
@@ -198,9 +200,17 @@ const ServiceCatalog = AmpState.extend({
|
|
|
198
200
|
findServiceDetailFromUrl(url: string): IServiceDetail | undefined {
|
|
199
201
|
const serviceDetails = this._getAllServiceDetails();
|
|
200
202
|
|
|
203
|
+
let candidateUrl: URL;
|
|
204
|
+
|
|
205
|
+
try {
|
|
206
|
+
candidateUrl = new URL(url);
|
|
207
|
+
} catch {
|
|
208
|
+
return undefined;
|
|
209
|
+
}
|
|
210
|
+
|
|
201
211
|
return serviceDetails.find(({serviceUrls}) => {
|
|
202
212
|
for (const serviceUrl of serviceUrls) {
|
|
203
|
-
if (
|
|
213
|
+
if (matchesParsedCatalogUrl(candidateUrl, parseCatalogUrl(serviceUrl.baseUrl))) {
|
|
204
214
|
return true;
|
|
205
215
|
}
|
|
206
216
|
}
|
|
@@ -210,20 +220,14 @@ const ServiceCatalog = AmpState.extend({
|
|
|
210
220
|
},
|
|
211
221
|
|
|
212
222
|
/**
|
|
213
|
-
* Finds an allowed domain that matches a specific url.
|
|
223
|
+
* Finds an allowed domain that matches a specific url. The url's hostname
|
|
224
|
+
* must be the allowed domain itself or a subdomain of it.
|
|
214
225
|
*
|
|
215
226
|
* @param {string} url - The url to match the allowed domains against.
|
|
216
227
|
* @returns {string} - The matching allowed domain.
|
|
217
228
|
*/
|
|
218
229
|
findAllowedDomain(url: string): string {
|
|
219
|
-
|
|
220
|
-
const urlObj = new URL(url);
|
|
221
|
-
|
|
222
|
-
return this.allowedDomains.find((allowedDomain) => urlObj.host.includes(allowedDomain));
|
|
223
|
-
} catch {
|
|
224
|
-
// If the URL is invalid or can't be found, return undefined
|
|
225
|
-
return undefined;
|
|
226
|
-
}
|
|
230
|
+
return matchAllowedDomain(url, this.allowedDomains);
|
|
227
231
|
},
|
|
228
232
|
|
|
229
233
|
/**
|
|
@@ -282,7 +286,7 @@ const ServiceCatalog = AmpState.extend({
|
|
|
282
286
|
* @returns {void}
|
|
283
287
|
*/
|
|
284
288
|
setAllowedDomains(allowedDomains: Array<string>): void {
|
|
285
|
-
this.allowedDomains =
|
|
289
|
+
this.allowedDomains = normalizeAllowedDomains(allowedDomains);
|
|
286
290
|
},
|
|
287
291
|
|
|
288
292
|
/**
|
|
@@ -291,7 +295,7 @@ const ServiceCatalog = AmpState.extend({
|
|
|
291
295
|
* @returns {void}
|
|
292
296
|
*/
|
|
293
297
|
addAllowedDomains(newAllowedDomains: Array<string>): void {
|
|
294
|
-
this.allowedDomains = union(this.allowedDomains, newAllowedDomains);
|
|
298
|
+
this.allowedDomains = union(this.allowedDomains, normalizeAllowedDomains(newAllowedDomains));
|
|
295
299
|
},
|
|
296
300
|
|
|
297
301
|
/**
|
|
@@ -7,6 +7,7 @@ import METRICS from '../metrics';
|
|
|
7
7
|
import ServiceCatalog from './service-catalog';
|
|
8
8
|
import fedRampServices from './service-fed-ramp';
|
|
9
9
|
import {COMMERCIAL_ALLOWED_DOMAINS} from '../constants';
|
|
10
|
+
import {matchesCatalogUrl} from '../services/service-catalog';
|
|
10
11
|
import {
|
|
11
12
|
ActiveServices,
|
|
12
13
|
IServiceCatalog,
|
|
@@ -15,6 +16,7 @@ import {
|
|
|
15
16
|
ServiceHostmap,
|
|
16
17
|
ServiceGroup,
|
|
17
18
|
ServiceHost,
|
|
19
|
+
ServiceUrl,
|
|
18
20
|
SelectionMeta,
|
|
19
21
|
} from './types';
|
|
20
22
|
|
|
@@ -44,6 +46,22 @@ const Services = WebexPlugin.extend({
|
|
|
44
46
|
initFailed: ['boolean', false, false],
|
|
45
47
|
},
|
|
46
48
|
|
|
49
|
+
session: {
|
|
50
|
+
/**
|
|
51
|
+
* Becomes `true` once the initial catalog collection has completed
|
|
52
|
+
* (successfully or otherwise) and any in-flight credentials refresh has
|
|
53
|
+
* settled. Blocks `webex.ready` so consumers can rely on `webex.ready`
|
|
54
|
+
* implying "catalogs populated AND credential state stable".
|
|
55
|
+
* @instance
|
|
56
|
+
* @memberof Services
|
|
57
|
+
* @type {boolean}
|
|
58
|
+
*/
|
|
59
|
+
ready: {
|
|
60
|
+
default: false,
|
|
61
|
+
type: 'boolean',
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
|
|
47
65
|
_catalogs: new WeakMap(),
|
|
48
66
|
|
|
49
67
|
_activeServices: {},
|
|
@@ -972,7 +990,9 @@ const Services = WebexPlugin.extend({
|
|
|
972
990
|
|
|
973
991
|
const priorityUrl = service.get();
|
|
974
992
|
const defaultUrl = new URL(
|
|
975
|
-
service.serviceUrls.find((serviceUrl) =>
|
|
993
|
+
service.serviceUrls.find((serviceUrl: ServiceUrl) =>
|
|
994
|
+
matchesCatalogUrl(url, serviceUrl.baseUrl)
|
|
995
|
+
).baseUrl
|
|
976
996
|
).href;
|
|
977
997
|
|
|
978
998
|
return {
|
|
@@ -1219,7 +1239,11 @@ const Services = WebexPlugin.extend({
|
|
|
1219
1239
|
): Promise<object> {
|
|
1220
1240
|
const service = 'u2c';
|
|
1221
1241
|
const resource = from ? `/${from}/catalog` : '/catalog';
|
|
1222
|
-
const qs = {
|
|
1242
|
+
const qs = {
|
|
1243
|
+
...(query || {}),
|
|
1244
|
+
format: 'U2CV2',
|
|
1245
|
+
...(this.webex.config?.services?.useCatalogOverride && {useCatalogOverride: true}),
|
|
1246
|
+
};
|
|
1223
1247
|
|
|
1224
1248
|
if (forceRefresh) {
|
|
1225
1249
|
qs.timestamp = new Date().getTime();
|
|
@@ -1237,9 +1261,16 @@ const Services = WebexPlugin.extend({
|
|
|
1237
1261
|
requestObject.headers = {authorization: token};
|
|
1238
1262
|
}
|
|
1239
1263
|
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1264
|
+
const sendRequest = () => this.request(requestObject);
|
|
1265
|
+
|
|
1266
|
+
const responsePromise = this.webex.internal.newMetrics
|
|
1267
|
+
? this.webex.internal.newMetrics.callDiagnosticLatencies.measureLatency(
|
|
1268
|
+
sendRequest,
|
|
1269
|
+
'internal.get.u2c.time'
|
|
1270
|
+
)
|
|
1271
|
+
: sendRequest();
|
|
1272
|
+
|
|
1273
|
+
return responsePromise.then(({body}) => this._formatReceivedHostmap(body || {}));
|
|
1243
1274
|
},
|
|
1244
1275
|
|
|
1245
1276
|
/**
|
|
@@ -1312,6 +1343,7 @@ const Services = WebexPlugin.extend({
|
|
|
1312
1343
|
|
|
1313
1344
|
// Destructure the credentials plugin.
|
|
1314
1345
|
const {credentials} = this.webex;
|
|
1346
|
+
const catalog = this._getCatalog();
|
|
1315
1347
|
|
|
1316
1348
|
// Init a promise chain. Must be done as a Promise.resolve() to allow
|
|
1317
1349
|
// credentials#getOrgId() to properly throw.
|
|
@@ -1324,11 +1356,18 @@ const Services = WebexPlugin.extend({
|
|
|
1324
1356
|
.then(() => {
|
|
1325
1357
|
// Validate if the token is authorized.
|
|
1326
1358
|
if (credentials.canAuthorize) {
|
|
1327
|
-
// Attempt to collect the postauth catalog
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
})
|
|
1359
|
+
// Attempt to collect the postauth catalog, then mark the catalog
|
|
1360
|
+
// ready. Setting `isReady` here - rather than only in the init
|
|
1361
|
+
// callers - means a slow postauth fetch that loses the gated-init
|
|
1362
|
+
// timeout race still marks the catalog ready once it completes.
|
|
1363
|
+
return this.updateServices({forceRefresh: refresh})
|
|
1364
|
+
.then(() => {
|
|
1365
|
+
catalog.isReady = true;
|
|
1366
|
+
})
|
|
1367
|
+
.catch(() => {
|
|
1368
|
+
this.initFailed = true;
|
|
1369
|
+
this.logger.warn('services: cannot retrieve postauth catalog');
|
|
1370
|
+
});
|
|
1332
1371
|
}
|
|
1333
1372
|
|
|
1334
1373
|
// Return a resolved promise for consistent return value.
|
|
@@ -1337,6 +1376,51 @@ const Services = WebexPlugin.extend({
|
|
|
1337
1376
|
);
|
|
1338
1377
|
},
|
|
1339
1378
|
|
|
1379
|
+
/**
|
|
1380
|
+
* Await any in-flight credentials refresh, then flip `services.ready` so
|
|
1381
|
+
* `webex.ready` can fire. Closes the parallel-refresh window: if a credential
|
|
1382
|
+
* refresh is in flight when initial catalog collection settles, we must not
|
|
1383
|
+
* signal ready until the refresh has resolved - otherwise downstream
|
|
1384
|
+
* consumers may observe `canAuthorize`/token state that is about to change
|
|
1385
|
+
* under them.
|
|
1386
|
+
*
|
|
1387
|
+
* @private
|
|
1388
|
+
* @returns {Promise<void>}
|
|
1389
|
+
*/
|
|
1390
|
+
async _finalizeReady(): Promise<void> {
|
|
1391
|
+
const {credentials} = this.webex;
|
|
1392
|
+
|
|
1393
|
+
if (credentials && credentials.isRefreshing) {
|
|
1394
|
+
await new Promise<void>((resolve) => {
|
|
1395
|
+
credentials.once('change:isRefreshing', resolve);
|
|
1396
|
+
});
|
|
1397
|
+
}
|
|
1398
|
+
|
|
1399
|
+
this.ready = true;
|
|
1400
|
+
},
|
|
1401
|
+
|
|
1402
|
+
/**
|
|
1403
|
+
* Build a promise that rejects once the catalog init timeout elapses. Race
|
|
1404
|
+
* this against catalog collection so a hung request never leaves
|
|
1405
|
+
* `services.ready` false forever - that would stall `webex.ready` and leave
|
|
1406
|
+
* consumers waiting on it indefinitely. Timeout is configurable via
|
|
1407
|
+
* `config.services.catalogInitTimeout` (defaults to 15s in config). Created
|
|
1408
|
+
* lazily so paths that skip catalog collection never schedule a stray timer.
|
|
1409
|
+
*
|
|
1410
|
+
* @private
|
|
1411
|
+
* @returns {Promise<never>}
|
|
1412
|
+
*/
|
|
1413
|
+
_makeInitTimeout(): Promise<never> {
|
|
1414
|
+
const initTimeoutMs = this.webex.config?.services?.catalogInitTimeout;
|
|
1415
|
+
|
|
1416
|
+
return new Promise<never>((_, reject) => {
|
|
1417
|
+
setTimeout(
|
|
1418
|
+
() => reject(new Error(`services: init timed out after ${initTimeoutMs}ms`)),
|
|
1419
|
+
initTimeoutMs
|
|
1420
|
+
);
|
|
1421
|
+
});
|
|
1422
|
+
},
|
|
1423
|
+
|
|
1340
1424
|
/**
|
|
1341
1425
|
* Initializer
|
|
1342
1426
|
*
|
|
@@ -1348,11 +1432,39 @@ const Services = WebexPlugin.extend({
|
|
|
1348
1432
|
const catalog = new ServiceCatalog();
|
|
1349
1433
|
this._catalogs.set(this.webex, catalog);
|
|
1350
1434
|
|
|
1351
|
-
// Listen for configuration changes once.
|
|
1435
|
+
// Listen for configuration changes once. The config is not populated on the
|
|
1436
|
+
// webex instance until the `change:config` event fires, so any decision that
|
|
1437
|
+
// depends on config values (such as the gated-vs-ungated init below) must be
|
|
1438
|
+
// made from within this handler rather than synchronously in `initialize()`.
|
|
1352
1439
|
this.listenToOnce(this.webex, 'change:config', () => {
|
|
1353
1440
|
this.initConfig();
|
|
1441
|
+
|
|
1442
|
+
// Feature flag: when enabled, `webex.ready` is blocked until the initial
|
|
1443
|
+
// catalog collection has settled AND any in-flight credentials refresh has
|
|
1444
|
+
// completed. When disabled (the default), preserves the pre-existing
|
|
1445
|
+
// behavior where `webex.ready` fires as soon as `webex.loaded` does and
|
|
1446
|
+
// the catalog is collected out-of-band.
|
|
1447
|
+
const waitForCatalogInit = this.webex.config?.services?.waitForCatalogInit === true;
|
|
1448
|
+
|
|
1449
|
+
if (waitForCatalogInit) {
|
|
1450
|
+
this._initializeCatalogsGated(catalog);
|
|
1451
|
+
} else {
|
|
1452
|
+
// Not gating - immediately mark ready so we do not block webex.ready.
|
|
1453
|
+
this.ready = true;
|
|
1454
|
+
this._initializeCatalogsUngated(catalog);
|
|
1455
|
+
}
|
|
1354
1456
|
});
|
|
1457
|
+
},
|
|
1355
1458
|
|
|
1459
|
+
/**
|
|
1460
|
+
* Original (pre-verified-ready) initialization path. Runs on `webex.ready`
|
|
1461
|
+
* and collects catalogs opportunistically without blocking anything.
|
|
1462
|
+
*
|
|
1463
|
+
* @private
|
|
1464
|
+
* @param {ServiceCatalog} catalog
|
|
1465
|
+
* @returns {void}
|
|
1466
|
+
*/
|
|
1467
|
+
_initializeCatalogsUngated(catalog: ServiceCatalog): void {
|
|
1356
1468
|
// wait for webex instance to be ready before attempting
|
|
1357
1469
|
// to update the service catalogs
|
|
1358
1470
|
this.listenToOnce(this.webex, 'ready', async () => {
|
|
@@ -1365,19 +1477,25 @@ const Services = WebexPlugin.extend({
|
|
|
1365
1477
|
const {supertoken} = this.webex.credentials;
|
|
1366
1478
|
// Validate if the supertoken exists.
|
|
1367
1479
|
if (supertoken && supertoken.access_token) {
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
.
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
);
|
|
1377
|
-
});
|
|
1480
|
+
// `initServiceCatalogs` marks the catalog ready internally once the
|
|
1481
|
+
// postauth catalog is collected.
|
|
1482
|
+
this.initServiceCatalogs().catch((error) => {
|
|
1483
|
+
this.initFailed = true;
|
|
1484
|
+
this.logger.error(
|
|
1485
|
+
`services: failed to init initial services when credentials available, ${error?.message}`
|
|
1486
|
+
);
|
|
1487
|
+
});
|
|
1378
1488
|
} else {
|
|
1379
1489
|
const {email} = this.webex.config;
|
|
1380
1490
|
|
|
1491
|
+
if (this.webex.config?.services?.skipPreauthCatalogOnUnauthenticated === true) {
|
|
1492
|
+
this.logger.info(
|
|
1493
|
+
'services: skipping preauth catalog collection while unauthenticated as per the config'
|
|
1494
|
+
);
|
|
1495
|
+
|
|
1496
|
+
return;
|
|
1497
|
+
}
|
|
1498
|
+
|
|
1381
1499
|
this.collectPreauthCatalog(email ? {email} : undefined).catch((error) => {
|
|
1382
1500
|
this.initFailed = true;
|
|
1383
1501
|
this.logger.error(
|
|
@@ -1387,6 +1505,85 @@ const Services = WebexPlugin.extend({
|
|
|
1387
1505
|
}
|
|
1388
1506
|
});
|
|
1389
1507
|
},
|
|
1508
|
+
|
|
1509
|
+
/**
|
|
1510
|
+
* Verified-ready initialization path. Blocks `webex.ready` until the initial
|
|
1511
|
+
* catalog fetch has settled (or timed out) AND any in-flight credentials
|
|
1512
|
+
* refresh has completed. Also handles the fresh-login case where OAuth
|
|
1513
|
+
* completes after `loaded` fires.
|
|
1514
|
+
*
|
|
1515
|
+
* @private
|
|
1516
|
+
* @param {ServiceCatalog} catalog
|
|
1517
|
+
* @returns {void}
|
|
1518
|
+
*/
|
|
1519
|
+
_initializeCatalogsGated(catalog: ServiceCatalog): void {
|
|
1520
|
+
// Wait for storage to be loaded before attempting to update the service
|
|
1521
|
+
// catalogs. We listen for 'loaded' instead of 'ready' because `services.ready`
|
|
1522
|
+
// now blocks `webex.ready` - listening to 'ready' would deadlock.
|
|
1523
|
+
this.listenToOnce(this.webex, 'loaded', async () => {
|
|
1524
|
+
const warmed = await this._loadCatalogFromCache();
|
|
1525
|
+
if (warmed) {
|
|
1526
|
+
catalog.isReady = true;
|
|
1527
|
+
await this._finalizeReady();
|
|
1528
|
+
|
|
1529
|
+
return;
|
|
1530
|
+
}
|
|
1531
|
+
const {supertoken} = this.webex.credentials;
|
|
1532
|
+
|
|
1533
|
+
// Validate if the supertoken exists.
|
|
1534
|
+
if (supertoken && supertoken.access_token) {
|
|
1535
|
+
// `initServiceCatalogs` marks the catalog ready internally once the
|
|
1536
|
+
// postauth catalog is collected - even if it loses the timeout race
|
|
1537
|
+
// below, so a slow fetch still eventually flips `catalog.isReady`.
|
|
1538
|
+
Promise.race([this.initServiceCatalogs(), this._makeInitTimeout()])
|
|
1539
|
+
.catch((error) => {
|
|
1540
|
+
this.initFailed = true;
|
|
1541
|
+
this.logger.error(
|
|
1542
|
+
`services: failed to init initial services when credentials available, ${error?.message}`
|
|
1543
|
+
);
|
|
1544
|
+
})
|
|
1545
|
+
.finally(() => this._finalizeReady());
|
|
1546
|
+
} else {
|
|
1547
|
+
const {email} = this.webex.config;
|
|
1548
|
+
|
|
1549
|
+
// Handle fresh login: 'loaded' fires before OAuth completes, so listen
|
|
1550
|
+
// for `canAuthorize` flipping true and then collect the postauth catalog.
|
|
1551
|
+
this.listenToOnce(this.webex, 'change:canAuthorize', () => {
|
|
1552
|
+
if (this.webex.canAuthorize && !catalog.status.postauth.ready) {
|
|
1553
|
+
// `initServiceCatalogs` marks the catalog ready internally.
|
|
1554
|
+
this.initServiceCatalogs().catch((error) => {
|
|
1555
|
+
this.logger.error(
|
|
1556
|
+
`services: failed to init service catalogs after auth, ${error?.message}`
|
|
1557
|
+
);
|
|
1558
|
+
});
|
|
1559
|
+
}
|
|
1560
|
+
});
|
|
1561
|
+
|
|
1562
|
+
if (this.webex.config?.services?.skipPreauthCatalogOnUnauthenticated === true) {
|
|
1563
|
+
// Skip the preauth catalog fetch (it will be collected manually
|
|
1564
|
+
// later), but still finalize `services.ready` so `webex.ready` is not
|
|
1565
|
+
// stalled while unauthenticated. No timeout is created here so there
|
|
1566
|
+
// is no stray timer or unhandled rejection.
|
|
1567
|
+
this.logger.info(
|
|
1568
|
+
'services: skipping preauth catalog collection while unauthenticated as per the config'
|
|
1569
|
+
);
|
|
1570
|
+
this._finalizeReady();
|
|
1571
|
+
} else {
|
|
1572
|
+
Promise.race([
|
|
1573
|
+
this.collectPreauthCatalog(email ? {email} : undefined),
|
|
1574
|
+
this._makeInitTimeout(),
|
|
1575
|
+
])
|
|
1576
|
+
.catch((error) => {
|
|
1577
|
+
this.initFailed = true;
|
|
1578
|
+
this.logger.error(
|
|
1579
|
+
`services: failed to init initial services when no credentials available, ${error?.message}`
|
|
1580
|
+
);
|
|
1581
|
+
})
|
|
1582
|
+
.finally(() => this._finalizeReady());
|
|
1583
|
+
}
|
|
1584
|
+
}
|
|
1585
|
+
});
|
|
1586
|
+
},
|
|
1390
1587
|
});
|
|
1391
1588
|
/* eslint-enable no-underscore-dangle */
|
|
1392
1589
|
|
package/src/webex-core.js
CHANGED
|
@@ -17,6 +17,7 @@ import AmpState from 'ampersand-state';
|
|
|
17
17
|
import uuid from 'uuid';
|
|
18
18
|
|
|
19
19
|
import AuthInterceptor from './interceptors/auth';
|
|
20
|
+
import CatalogUrlInterceptor from './interceptors/catalog-url';
|
|
20
21
|
import NetworkTimingInterceptor from './interceptors/network-timing';
|
|
21
22
|
import PayloadTransformerInterceptor from './interceptors/payload-transformer';
|
|
22
23
|
import RedirectInterceptor from './interceptors/redirect';
|
|
@@ -58,6 +59,7 @@ const interceptors = {
|
|
|
58
59
|
/* eslint-enable no-extra-parens */
|
|
59
60
|
RequestTimingInterceptor: RequestTimingInterceptor.create,
|
|
60
61
|
ServiceInterceptor: undefined,
|
|
62
|
+
CatalogUrlInterceptor: CatalogUrlInterceptor.create,
|
|
61
63
|
UserAgentInterceptor: UserAgentInterceptor.create,
|
|
62
64
|
ProxyInterceptor: ProxyInterceptor.create,
|
|
63
65
|
WebexUserAgentInterceptor: WebexUserAgentInterceptor.create,
|
|
@@ -83,6 +85,7 @@ const preInterceptors = [
|
|
|
83
85
|
'RequestEventInterceptor',
|
|
84
86
|
'WebexTrackingIdInterceptor',
|
|
85
87
|
'RateLimitInterceptor',
|
|
88
|
+
'CatalogUrlInterceptor',
|
|
86
89
|
];
|
|
87
90
|
|
|
88
91
|
const postInterceptors = [
|
|
@@ -393,6 +396,11 @@ const WebexCore = AmpState.extend({
|
|
|
393
396
|
return ints;
|
|
394
397
|
}
|
|
395
398
|
|
|
399
|
+
// CatalogUrlInterceptor is opt-in via config.services.validateCatalogUrls
|
|
400
|
+
if (key === 'CatalogUrlInterceptor' && this.config.services?.validateCatalogUrls !== true) {
|
|
401
|
+
return ints;
|
|
402
|
+
}
|
|
403
|
+
|
|
396
404
|
ints.push(Reflect.apply(interceptor, this, []));
|
|
397
405
|
|
|
398
406
|
return ints;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import uuid from 'uuid';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Generates a unique test email for user-activation/validation specs.
|
|
9
|
+
*
|
|
10
|
+
* The local part is kept short on purpose: for a brand-new self-signup user the
|
|
11
|
+
* backend derives the "given name" from the email local part, and self-signup
|
|
12
|
+
* orgs cap the given name at 50 characters. A full UUID would push the local
|
|
13
|
+
* part to 59 chars and fail with errorCode 100018, so we use 20 hex characters
|
|
14
|
+
* of entropy (local part = 43 chars).
|
|
15
|
+
*
|
|
16
|
+
* @returns {string} e.g. `Collabctg+webex-js-sdk-1a2b3c4d5e6f7a8b9c0d@gmail.com`
|
|
17
|
+
*/
|
|
18
|
+
export function createActivationEmail(): string {
|
|
19
|
+
return `Collabctg+webex-js-sdk-${uuid.v4().replace(/-/g, '').slice(0, 20)}@gmail.com`;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export default createActivationEmail;
|