@webex/webex-core 3.12.0-next.8 → 3.12.0-task-refactor.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/config.js +0 -7
- package/dist/config.js.map +1 -1
- 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/service-url.js +1 -11
- package/dist/lib/services/service-url.js.map +1 -1
- package/dist/lib/services/services.js +93 -418
- package/dist/lib/services/services.js.map +1 -1
- package/dist/lib/services-v2/services-v2.js +40 -342
- package/dist/lib/services-v2/services-v2.js.map +1 -1
- package/dist/lib/services-v2/types.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/config.js +0 -7
- package/src/interceptors/redirect.js +1 -4
- package/src/lib/services/service-url.js +1 -9
- package/src/lib/services/services.js +5 -285
- package/src/lib/services-v2/services-v2.ts +1 -269
- package/src/lib/services-v2/types.ts +0 -5
- package/test/integration/spec/services/service-catalog.js +4 -10
- package/test/integration/spec/services/services.js +9 -25
- package/test/integration/spec/services-v2/services-v2.js +6 -16
- package/test/unit/spec/services/service-url.js +0 -110
- package/test/unit/spec/services/services.js +12 -392
- package/test/unit/spec/services-v2/services-v2.ts +0 -249
|
@@ -20,8 +20,6 @@ export const DEFAULT_CLUSTER_SERVICE = 'identityLookup';
|
|
|
20
20
|
const CLUSTER_SERVICE = process.env.WEBEX_CONVERSATION_CLUSTER_SERVICE || DEFAULT_CLUSTER_SERVICE;
|
|
21
21
|
const DEFAULT_CLUSTER_IDENTIFIER =
|
|
22
22
|
process.env.WEBEX_CONVERSATION_DEFAULT_CLUSTER || `${DEFAULT_CLUSTER}:${CLUSTER_SERVICE}`;
|
|
23
|
-
const CATALOG_CACHE_KEY_V1 = 'services.v1.u2cHostMap';
|
|
24
|
-
const CATALOG_TTL_MS = 24 * 60 * 60 * 1000; // 24 hours
|
|
25
23
|
|
|
26
24
|
/* eslint-disable no-underscore-dangle */
|
|
27
25
|
/**
|
|
@@ -98,49 +96,6 @@ const Services = WebexPlugin.extend({
|
|
|
98
96
|
return this._catalogs.get(this.webex);
|
|
99
97
|
},
|
|
100
98
|
|
|
101
|
-
/**
|
|
102
|
-
* Safely access localStorage if available; returns the Storage or null.
|
|
103
|
-
* @returns {Storage|null}
|
|
104
|
-
*/
|
|
105
|
-
_getLocalStorageSafe() {
|
|
106
|
-
if (typeof window !== 'undefined' && window.localStorage) {
|
|
107
|
-
return window.localStorage;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
return null;
|
|
111
|
-
},
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* Determine the intended preauth selection based on the current context.
|
|
115
|
-
* @param {string|undefined} currentOrgId
|
|
116
|
-
* @returns {{selectionType: string, selectionValue: string}}
|
|
117
|
-
*/
|
|
118
|
-
getIntendedPreauthSelection(currentOrgId) {
|
|
119
|
-
if (this.webex.credentials?.canAuthorize) {
|
|
120
|
-
if (currentOrgId) {
|
|
121
|
-
return {
|
|
122
|
-
selectionType: 'orgId',
|
|
123
|
-
selectionValue: currentOrgId,
|
|
124
|
-
};
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
const emailConfig = this.webex.config && this.webex.config.email;
|
|
129
|
-
|
|
130
|
-
if (typeof emailConfig === 'string' && emailConfig.trim()) {
|
|
131
|
-
return {
|
|
132
|
-
selectionType: 'emailhash',
|
|
133
|
-
selectionValue: sha256(emailConfig.toLowerCase()).toString(),
|
|
134
|
-
};
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
// fall back to proximity mode when no orgId or email available
|
|
138
|
-
return {
|
|
139
|
-
selectionType: 'mode',
|
|
140
|
-
selectionValue: 'DEFAULT_BY_PROXIMITY',
|
|
141
|
-
};
|
|
142
|
-
},
|
|
143
|
-
|
|
144
99
|
/**
|
|
145
100
|
* Get a service url from the current services list by name
|
|
146
101
|
* from the associated instance catalog.
|
|
@@ -210,10 +165,9 @@ const Services = WebexPlugin.extend({
|
|
|
210
165
|
|
|
211
166
|
/**
|
|
212
167
|
* Get all Mobius cluster host entries from the legacy host catalog.
|
|
213
|
-
* @returns {Array<
|
|
168
|
+
* @returns {Array<Object>}
|
|
214
169
|
*/
|
|
215
170
|
getMobiusClusters() {
|
|
216
|
-
this.logger.info('services: fetching mobius clusters');
|
|
217
171
|
const clusters = [];
|
|
218
172
|
const hostCatalog = this._hostCatalog || {};
|
|
219
173
|
|
|
@@ -243,28 +197,6 @@ const Services = WebexPlugin.extend({
|
|
|
243
197
|
|
|
244
198
|
return !!hostCatalog[host]?.length;
|
|
245
199
|
},
|
|
246
|
-
|
|
247
|
-
/**
|
|
248
|
-
* Checks if the current environment is an integration (INT) environment
|
|
249
|
-
* by examining the u2c discovery URL from webex config.
|
|
250
|
-
* INT environments use discovery URLs containing 'intb' (e.g., u2c-intb.ciscospark.com).
|
|
251
|
-
* @returns {boolean} True if INT environment, false otherwise
|
|
252
|
-
*/
|
|
253
|
-
isIntegrationEnvironment() {
|
|
254
|
-
try {
|
|
255
|
-
const u2cUrl = this.webex?.config?.services?.discovery?.u2c || '';
|
|
256
|
-
const isInt = u2cUrl.includes('intb');
|
|
257
|
-
|
|
258
|
-
this.logger.info(`services: isIntegrationEnvironment: ${isInt}`);
|
|
259
|
-
|
|
260
|
-
return isInt;
|
|
261
|
-
} catch (error) {
|
|
262
|
-
this.logger.error('services: failed to determine integration environment', error);
|
|
263
|
-
|
|
264
|
-
return false;
|
|
265
|
-
}
|
|
266
|
-
},
|
|
267
|
-
|
|
268
200
|
/**
|
|
269
201
|
* Merge provided active cluster mappings into current state.
|
|
270
202
|
* @param {Record<string,string>} activeServices
|
|
@@ -305,7 +237,7 @@ const Services = WebexPlugin.extend({
|
|
|
305
237
|
* @param {string} [param.token] - used for signin catalog
|
|
306
238
|
* @returns {Promise<object>}
|
|
307
239
|
*/
|
|
308
|
-
|
|
240
|
+
updateServices({from, query, token, forceRefresh} = {}) {
|
|
309
241
|
const catalog = this._getCatalog();
|
|
310
242
|
let formattedQuery;
|
|
311
243
|
let serviceGroup;
|
|
@@ -359,20 +291,7 @@ const Services = WebexPlugin.extend({
|
|
|
359
291
|
forceRefresh,
|
|
360
292
|
})
|
|
361
293
|
.then((serviceHostMap) => {
|
|
362
|
-
|
|
363
|
-
// Build selection metadata for caching discrimination
|
|
364
|
-
let selectionMeta;
|
|
365
|
-
if (serviceGroup === 'preauth' || serviceGroup === 'signin') {
|
|
366
|
-
const key = formattedQuery && Object.keys(formattedQuery || {})[0];
|
|
367
|
-
if (key) {
|
|
368
|
-
selectionMeta = {
|
|
369
|
-
selectionType: key,
|
|
370
|
-
selectionValue: formattedQuery[key],
|
|
371
|
-
};
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
this._cacheCatalog(serviceGroup, serviceHostMap, selectionMeta);
|
|
375
|
-
catalog.updateServiceUrls(serviceGroup, formattedServiceHostMap);
|
|
294
|
+
catalog.updateServiceUrls(serviceGroup, serviceHostMap);
|
|
376
295
|
this.updateCredentialsConfig();
|
|
377
296
|
catalog.status[serviceGroup].collecting = false;
|
|
378
297
|
})
|
|
@@ -1089,197 +1008,7 @@ const Services = WebexPlugin.extend({
|
|
|
1089
1008
|
|
|
1090
1009
|
return this.webex.internal.newMetrics.callDiagnosticLatencies
|
|
1091
1010
|
.measureLatency(() => this.request(requestObject), 'internal.get.u2c.time')
|
|
1092
|
-
.then(({body}) => body);
|
|
1093
|
-
},
|
|
1094
|
-
|
|
1095
|
-
/**
|
|
1096
|
-
* Cache the catalog in the bounded storage.
|
|
1097
|
-
* @param {string} serviceGroup - preauth, signin, postauth
|
|
1098
|
-
* @param {object} hostMap - The hostmap to cache
|
|
1099
|
-
* @param {object} [meta] - Optional selection metadata used to validate cache reuse
|
|
1100
|
-
* @returns {Promise<void>}
|
|
1101
|
-
*
|
|
1102
|
-
*/
|
|
1103
|
-
async _cacheCatalog(serviceGroup, hostMap, meta) {
|
|
1104
|
-
let current = {};
|
|
1105
|
-
let orgId;
|
|
1106
|
-
try {
|
|
1107
|
-
// Respect calling.cacheU2C toggle; if disabled, skip writing cache
|
|
1108
|
-
if (!this.webex.config?.calling?.cacheU2C) {
|
|
1109
|
-
this.logger.info(`services: skipping cache write for ${serviceGroup} as per the config`);
|
|
1110
|
-
|
|
1111
|
-
return;
|
|
1112
|
-
}
|
|
1113
|
-
|
|
1114
|
-
// Persist to localStorage to survive browser refresh
|
|
1115
|
-
try {
|
|
1116
|
-
const ls = this._getLocalStorageSafe();
|
|
1117
|
-
const cachedJson = ls ? ls.getItem(CATALOG_CACHE_KEY_V1) : null;
|
|
1118
|
-
current = cachedJson ? JSON.parse(cachedJson) : {};
|
|
1119
|
-
} catch (e) {
|
|
1120
|
-
current = {};
|
|
1121
|
-
}
|
|
1122
|
-
|
|
1123
|
-
try {
|
|
1124
|
-
const {credentials} = this.webex;
|
|
1125
|
-
orgId = credentials.getOrgId();
|
|
1126
|
-
} catch (e) {
|
|
1127
|
-
orgId = current.orgId;
|
|
1128
|
-
}
|
|
1129
|
-
|
|
1130
|
-
// Capture environment fingerprint to invalidate cache across env changes
|
|
1131
|
-
let {env} = current;
|
|
1132
|
-
const fedramp = !!this.webex?.config?.fedramp;
|
|
1133
|
-
const u2cDiscoveryUrl = this.webex?.config?.services?.discovery?.u2c;
|
|
1134
|
-
env = {fedramp, u2cDiscoveryUrl};
|
|
1135
|
-
|
|
1136
|
-
const updated = {
|
|
1137
|
-
...current,
|
|
1138
|
-
orgId: orgId || current.orgId,
|
|
1139
|
-
env: env || current.env,
|
|
1140
|
-
// When selection meta is provided, store as an object; otherwise keep legacy shape
|
|
1141
|
-
[serviceGroup]: meta ? {hostMap, meta} : hostMap,
|
|
1142
|
-
cachedAt: Date.now(),
|
|
1143
|
-
};
|
|
1144
|
-
|
|
1145
|
-
const ls = this._getLocalStorageSafe();
|
|
1146
|
-
if (ls) {
|
|
1147
|
-
ls.setItem(CATALOG_CACHE_KEY_V1, JSON.stringify(updated));
|
|
1148
|
-
}
|
|
1149
|
-
} catch (error) {
|
|
1150
|
-
this.logger.warn('services: error caching catalog', error);
|
|
1151
|
-
}
|
|
1152
|
-
},
|
|
1153
|
-
|
|
1154
|
-
/**
|
|
1155
|
-
* Load the catalog from cache and hydrate the in-memory ServiceCatalog.
|
|
1156
|
-
* @returns {Promise<boolean>} true if cache was loaded, false otherwise
|
|
1157
|
-
*/
|
|
1158
|
-
async _loadCatalogFromCache() {
|
|
1159
|
-
let currentOrgId;
|
|
1160
|
-
try {
|
|
1161
|
-
// Respect calling.cacheU2C toggle; if disabled, skip using cache
|
|
1162
|
-
if (!this.webex.config?.calling?.cacheU2C) {
|
|
1163
|
-
this.logger.info('services: skipping cache warm-up as per the cache config');
|
|
1164
|
-
|
|
1165
|
-
return false;
|
|
1166
|
-
}
|
|
1167
|
-
|
|
1168
|
-
const ls = this._getLocalStorageSafe();
|
|
1169
|
-
if (!ls) {
|
|
1170
|
-
this.logger.info('services: skipping cache warm-up as no localStorage is available');
|
|
1171
|
-
|
|
1172
|
-
return false;
|
|
1173
|
-
}
|
|
1174
|
-
const cachedJson = ls.getItem(CATALOG_CACHE_KEY_V1);
|
|
1175
|
-
const cached = cachedJson ? JSON.parse(cachedJson) : undefined;
|
|
1176
|
-
if (!cached) {
|
|
1177
|
-
return false;
|
|
1178
|
-
}
|
|
1179
|
-
// TTL enforcement: clear if older than 24 hours
|
|
1180
|
-
const cachedAt = Number(cached.cachedAt) || 0;
|
|
1181
|
-
if (!cachedAt || Date.now() - cachedAt > CATALOG_TTL_MS) {
|
|
1182
|
-
this.clearCatalogCache();
|
|
1183
|
-
|
|
1184
|
-
return false;
|
|
1185
|
-
}
|
|
1186
|
-
|
|
1187
|
-
// If authorized, ensure cached org matches
|
|
1188
|
-
try {
|
|
1189
|
-
if (this.webex.credentials?.canAuthorize) {
|
|
1190
|
-
const {credentials} = this.webex;
|
|
1191
|
-
currentOrgId = credentials.getOrgId();
|
|
1192
|
-
if (cached.orgId && cached.orgId !== currentOrgId) {
|
|
1193
|
-
return false;
|
|
1194
|
-
}
|
|
1195
|
-
}
|
|
1196
|
-
} catch (e) {
|
|
1197
|
-
this.logger.warn('services: error checking orgId', e);
|
|
1198
|
-
}
|
|
1199
|
-
|
|
1200
|
-
// Ensure cached environment matches current environment
|
|
1201
|
-
|
|
1202
|
-
const fedramp = !!this.webex.config?.fedramp;
|
|
1203
|
-
const u2cDiscoveryUrl = this.webex.config?.services?.discovery?.u2c;
|
|
1204
|
-
const currentEnv = {fedramp, u2cDiscoveryUrl};
|
|
1205
|
-
if (cached.env) {
|
|
1206
|
-
const sameEnv =
|
|
1207
|
-
cached.env.fedramp === currentEnv.fedramp &&
|
|
1208
|
-
cached.env.u2cDiscoveryUrl === currentEnv.u2cDiscoveryUrl;
|
|
1209
|
-
if (!sameEnv) {
|
|
1210
|
-
this.logger.info('services: skipping cache warm due to environment mismatch');
|
|
1211
|
-
|
|
1212
|
-
return false;
|
|
1213
|
-
}
|
|
1214
|
-
}
|
|
1215
|
-
|
|
1216
|
-
const catalog = this._getCatalog();
|
|
1217
|
-
|
|
1218
|
-
// Apply any cached groups (with preauth selection validation if available)
|
|
1219
|
-
const groups = ['preauth', 'signin', 'postauth'];
|
|
1220
|
-
groups.forEach((serviceGroup) => {
|
|
1221
|
-
const cachedGroup = cached[serviceGroup];
|
|
1222
|
-
if (!cachedGroup) {
|
|
1223
|
-
return;
|
|
1224
|
-
}
|
|
1225
|
-
|
|
1226
|
-
// Support legacy (hostMap) and new ({hostMap, meta}) shapes
|
|
1227
|
-
const hostMap = cachedGroup && cachedGroup.hostMap ? cachedGroup.hostMap : cachedGroup;
|
|
1228
|
-
const meta = cachedGroup?.meta;
|
|
1229
|
-
|
|
1230
|
-
if (serviceGroup === 'preauth' && meta) {
|
|
1231
|
-
// For proximity-based selection, always fetch fresh to respect IP/region changes
|
|
1232
|
-
if (meta.selectionType === 'mode') {
|
|
1233
|
-
this.logger.info('services: skipping preauth cache warm for proximity mode');
|
|
1234
|
-
|
|
1235
|
-
return;
|
|
1236
|
-
}
|
|
1237
|
-
|
|
1238
|
-
const intended = this.getIntendedPreauthSelection(currentOrgId);
|
|
1239
|
-
const matches =
|
|
1240
|
-
intended &&
|
|
1241
|
-
intended.selectionType === meta.selectionType &&
|
|
1242
|
-
intended.selectionValue === meta.selectionValue;
|
|
1243
|
-
|
|
1244
|
-
if (!matches) {
|
|
1245
|
-
this.logger.info('services: skipping preauth cache warm due to selection mismatch');
|
|
1246
|
-
|
|
1247
|
-
return;
|
|
1248
|
-
}
|
|
1249
|
-
}
|
|
1250
|
-
|
|
1251
|
-
if (hostMap) {
|
|
1252
|
-
const formatted = this._formatReceivedHostmap(hostMap);
|
|
1253
|
-
catalog.updateServiceUrls(serviceGroup, formatted);
|
|
1254
|
-
}
|
|
1255
|
-
});
|
|
1256
|
-
|
|
1257
|
-
// Align credentials against warmed catalog
|
|
1258
|
-
this.updateCredentialsConfig();
|
|
1259
|
-
|
|
1260
|
-
return true;
|
|
1261
|
-
} catch (e) {
|
|
1262
|
-
this.logger.warn('services: error loading catalog from cache', e);
|
|
1263
|
-
|
|
1264
|
-
return false;
|
|
1265
|
-
}
|
|
1266
|
-
},
|
|
1267
|
-
|
|
1268
|
-
/**
|
|
1269
|
-
* Clear the catalog cache from the bounded storage.
|
|
1270
|
-
* @returns {Promise<void>}
|
|
1271
|
-
*/
|
|
1272
|
-
clearCatalogCache() {
|
|
1273
|
-
try {
|
|
1274
|
-
const ls = this._getLocalStorageSafe();
|
|
1275
|
-
if (ls) {
|
|
1276
|
-
ls.removeItem(CATALOG_CACHE_KEY_V1);
|
|
1277
|
-
}
|
|
1278
|
-
} catch (e) {
|
|
1279
|
-
this.logger.warn('services: error clearing catalog cache', e);
|
|
1280
|
-
}
|
|
1281
|
-
|
|
1282
|
-
return Promise.resolve();
|
|
1011
|
+
.then(({body}) => this._formatReceivedHostmap(body));
|
|
1283
1012
|
},
|
|
1284
1013
|
|
|
1285
1014
|
/**
|
|
@@ -1359,7 +1088,6 @@ const Services = WebexPlugin.extend({
|
|
|
1359
1088
|
// Validate if the token is authorized.
|
|
1360
1089
|
if (credentials.canAuthorize) {
|
|
1361
1090
|
// Attempt to collect the postauth catalog.
|
|
1362
|
-
|
|
1363
1091
|
return this.updateServices().catch(() => {
|
|
1364
1092
|
this.initFailed = true;
|
|
1365
1093
|
this.logger.warn('services: cannot retrieve postauth catalog');
|
|
@@ -1395,15 +1123,7 @@ const Services = WebexPlugin.extend({
|
|
|
1395
1123
|
|
|
1396
1124
|
// wait for webex instance to be ready before attempting
|
|
1397
1125
|
// to update the service catalogs
|
|
1398
|
-
|
|
1399
|
-
// not be valid when services is initialized
|
|
1400
|
-
this.listenToOnce(this.webex, 'ready', async () => {
|
|
1401
|
-
const cachedCatalog = await this._loadCatalogFromCache();
|
|
1402
|
-
if (cachedCatalog) {
|
|
1403
|
-
catalog.isReady = true;
|
|
1404
|
-
|
|
1405
|
-
return; // skip initServiceCatalogs() on reload when cache exists
|
|
1406
|
-
}
|
|
1126
|
+
this.listenToOnce(this.webex, 'ready', () => {
|
|
1407
1127
|
const {supertoken} = this.webex.credentials;
|
|
1408
1128
|
// Validate if the supertoken exists.
|
|
1409
1129
|
if (supertoken && supertoken.access_token) {
|
|
@@ -15,7 +15,6 @@ import {
|
|
|
15
15
|
ServiceHostmap,
|
|
16
16
|
ServiceGroup,
|
|
17
17
|
ServiceHost,
|
|
18
|
-
SelectionMeta,
|
|
19
18
|
} from './types';
|
|
20
19
|
|
|
21
20
|
const trailingSlashes = /(?:^\/)|(?:\/$)/;
|
|
@@ -29,9 +28,6 @@ const CLUSTER_SERVICE = process.env.WEBEX_CONVERSATION_CLUSTER_SERVICE || DEFAUL
|
|
|
29
28
|
const DEFAULT_CLUSTER_IDENTIFIER =
|
|
30
29
|
process.env.WEBEX_CONVERSATION_DEFAULT_CLUSTER || `${DEFAULT_CLUSTER}:${CLUSTER_SERVICE}`;
|
|
31
30
|
|
|
32
|
-
const CATALOG_CACHE_KEY_V2 = 'services.v2.u2cHostMap';
|
|
33
|
-
const CATALOG_TTL_MS = 24 * 60 * 60 * 1000; // 24 hours
|
|
34
|
-
|
|
35
31
|
/* eslint-disable no-underscore-dangle */
|
|
36
32
|
/**
|
|
37
33
|
* @class
|
|
@@ -60,46 +56,6 @@ const Services = WebexPlugin.extend({
|
|
|
60
56
|
return this._catalogs.get(this.webex);
|
|
61
57
|
},
|
|
62
58
|
|
|
63
|
-
/**
|
|
64
|
-
* Safely access localStorage if available; returns the Storage or null.
|
|
65
|
-
* @returns {Storage | null}
|
|
66
|
-
*/
|
|
67
|
-
_getLocalStorageSafe(): Storage | null {
|
|
68
|
-
if (typeof window !== 'undefined' && (window as any).localStorage) {
|
|
69
|
-
return (window as any).localStorage as Storage;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
return null;
|
|
73
|
-
},
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Determine the intended preauth selection based on the current context.
|
|
77
|
-
* @param {string} [currentOrgId]
|
|
78
|
-
* @returns {{selectionType: string, selectionValue: string}}
|
|
79
|
-
*/
|
|
80
|
-
getIntendedPreauthSelection(currentOrgId?: string): {
|
|
81
|
-
selectionType: string;
|
|
82
|
-
selectionValue: string;
|
|
83
|
-
} {
|
|
84
|
-
if (this.webex.credentials?.canAuthorize) {
|
|
85
|
-
if (currentOrgId) {
|
|
86
|
-
return {selectionType: 'orgId', selectionValue: currentOrgId};
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
const emailConfig = this.webex.config && this.webex.config.email;
|
|
91
|
-
|
|
92
|
-
if (typeof emailConfig === 'string' && emailConfig.trim()) {
|
|
93
|
-
return {
|
|
94
|
-
selectionType: 'emailhash',
|
|
95
|
-
selectionValue: sha256(emailConfig.toLowerCase()).toString(),
|
|
96
|
-
};
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// fall back to proximity mode when no orgId or email available
|
|
100
|
-
return {selectionType: 'mode', selectionValue: 'DEFAULT_BY_PROXIMITY'};
|
|
101
|
-
},
|
|
102
|
-
|
|
103
59
|
/**
|
|
104
60
|
* Get a service url from the current services list by name
|
|
105
61
|
* from the associated instance catalog.
|
|
@@ -154,7 +110,6 @@ const Services = WebexPlugin.extend({
|
|
|
154
110
|
* @returns {Array<ServiceHost>} - An array of `ServiceHost` objects.
|
|
155
111
|
*/
|
|
156
112
|
getMobiusClusters(): Array<ServiceHost> {
|
|
157
|
-
this.logger.info('services: fetching mobius clusters');
|
|
158
113
|
const clusters: Array<ServiceHost> = [];
|
|
159
114
|
const services: Array<Service> = this._services || [];
|
|
160
115
|
|
|
@@ -198,27 +153,6 @@ const Services = WebexPlugin.extend({
|
|
|
198
153
|
});
|
|
199
154
|
});
|
|
200
155
|
},
|
|
201
|
-
|
|
202
|
-
/**
|
|
203
|
-
* Checks if the current environment is an integration (INT) environment
|
|
204
|
-
* by examining the u2c discovery URL from webex config.
|
|
205
|
-
* INT environments use discovery URLs containing 'intb' (e.g., u2c-intb.ciscospark.com).
|
|
206
|
-
* @returns {boolean} True if INT environment, false otherwise
|
|
207
|
-
*/
|
|
208
|
-
isIntegrationEnvironment(): boolean {
|
|
209
|
-
try {
|
|
210
|
-
const u2cUrl = this.webex?.config?.services?.discovery?.u2c || '';
|
|
211
|
-
const isInt = u2cUrl.includes('intb');
|
|
212
|
-
|
|
213
|
-
this.logger.info(`services: isIntegrationEnvironment: ${isInt}`);
|
|
214
|
-
|
|
215
|
-
return isInt;
|
|
216
|
-
} catch (error) {
|
|
217
|
-
this.logger.error('services: failed to determine integration environment', error);
|
|
218
|
-
|
|
219
|
-
return false;
|
|
220
|
-
}
|
|
221
|
-
},
|
|
222
156
|
/**
|
|
223
157
|
* saves all the services from the pre and post catalog service
|
|
224
158
|
* @param {ActiveServices} activeServices
|
|
@@ -315,18 +249,6 @@ const Services = WebexPlugin.extend({
|
|
|
315
249
|
serviceHostMap?.services,
|
|
316
250
|
serviceHostMap?.timestamp
|
|
317
251
|
);
|
|
318
|
-
// Build selection metadata for caching discrimination (preauth/signin)
|
|
319
|
-
let selectionMeta: SelectionMeta | undefined;
|
|
320
|
-
if (serviceGroup === 'preauth' || serviceGroup === 'signin') {
|
|
321
|
-
const key = formattedQuery && Object.keys(formattedQuery || {})[0];
|
|
322
|
-
if (key) {
|
|
323
|
-
selectionMeta = {
|
|
324
|
-
selectionType: key,
|
|
325
|
-
selectionValue: formattedQuery[key],
|
|
326
|
-
};
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
this._cacheCatalog(serviceGroup, serviceHostMap, selectionMeta);
|
|
330
252
|
this.updateCredentialsConfig();
|
|
331
253
|
catalog.status[serviceGroup].collecting = false;
|
|
332
254
|
})
|
|
@@ -1012,190 +934,6 @@ const Services = WebexPlugin.extend({
|
|
|
1012
934
|
return url.replace(data.defaultUrl, data.priorityUrl);
|
|
1013
935
|
},
|
|
1014
936
|
|
|
1015
|
-
/**
|
|
1016
|
-
* @private
|
|
1017
|
-
* Cache the catalog in the bounded storage.
|
|
1018
|
-
* @param {ServiceGroup} serviceGroup - preauth, signin, postauth
|
|
1019
|
-
* @param {ServiceHostmap} hostMap - The hostmap to cache
|
|
1020
|
-
* @param {object} [meta] - Optional selection metadata for cache discrimination
|
|
1021
|
-
* @returns {Promise<void>}
|
|
1022
|
-
*/
|
|
1023
|
-
async _cacheCatalog(
|
|
1024
|
-
serviceGroup: ServiceGroup,
|
|
1025
|
-
hostMap: ServiceHostmap,
|
|
1026
|
-
meta?: SelectionMeta
|
|
1027
|
-
): Promise<void> {
|
|
1028
|
-
let current: {orgId?: string; env?: {fedramp?: boolean; u2cDiscoveryUrl?: string}} = {};
|
|
1029
|
-
let orgId: string | undefined;
|
|
1030
|
-
try {
|
|
1031
|
-
// Respect calling.cacheU2C toggle; if disabled, skip writing cache
|
|
1032
|
-
if (!this.webex.config?.calling?.cacheU2C) {
|
|
1033
|
-
this.logger.info(`services: skipping cache write for ${serviceGroup} as per the config`);
|
|
1034
|
-
|
|
1035
|
-
return;
|
|
1036
|
-
}
|
|
1037
|
-
|
|
1038
|
-
try {
|
|
1039
|
-
const ls = this._getLocalStorageSafe();
|
|
1040
|
-
const cachedJson = ls ? ls.getItem(CATALOG_CACHE_KEY_V2) : null;
|
|
1041
|
-
current = cachedJson ? JSON.parse(cachedJson) : {};
|
|
1042
|
-
} catch {
|
|
1043
|
-
current = {};
|
|
1044
|
-
}
|
|
1045
|
-
|
|
1046
|
-
try {
|
|
1047
|
-
const {credentials} = this.webex;
|
|
1048
|
-
orgId = credentials.getOrgId();
|
|
1049
|
-
} catch {
|
|
1050
|
-
orgId = current.orgId;
|
|
1051
|
-
}
|
|
1052
|
-
|
|
1053
|
-
// Capture environment fingerprint to invalidate cache across env changes
|
|
1054
|
-
let {env} = current;
|
|
1055
|
-
const fedramp = !!this.webex?.config?.fedramp;
|
|
1056
|
-
const u2cDiscoveryUrl = this.webex?.config?.services?.discovery?.u2c;
|
|
1057
|
-
env = {fedramp, u2cDiscoveryUrl};
|
|
1058
|
-
|
|
1059
|
-
const updated = {
|
|
1060
|
-
...current,
|
|
1061
|
-
orgId: orgId || current.orgId,
|
|
1062
|
-
env: env || current.env,
|
|
1063
|
-
// When selection meta is provided, store as an object; otherwise keep legacy shape
|
|
1064
|
-
[serviceGroup]: meta ? {hostMap, meta} : hostMap,
|
|
1065
|
-
cachedAt: Date.now(),
|
|
1066
|
-
};
|
|
1067
|
-
|
|
1068
|
-
const ls = this._getLocalStorageSafe();
|
|
1069
|
-
if (ls) {
|
|
1070
|
-
ls.setItem(CATALOG_CACHE_KEY_V2, JSON.stringify(updated));
|
|
1071
|
-
}
|
|
1072
|
-
} catch (e) {
|
|
1073
|
-
this.logger.warn('services: error caching catalog', e);
|
|
1074
|
-
}
|
|
1075
|
-
},
|
|
1076
|
-
|
|
1077
|
-
/**
|
|
1078
|
-
* @private
|
|
1079
|
-
* Load the catalog from cache and hydrate the in-memory ServiceCatalog.
|
|
1080
|
-
* @returns {Promise<boolean>} true if cache was loaded, false otherwise
|
|
1081
|
-
*/
|
|
1082
|
-
async _loadCatalogFromCache(): Promise<boolean> {
|
|
1083
|
-
let currentOrgId: string | undefined;
|
|
1084
|
-
try {
|
|
1085
|
-
// Respect calling.cacheU2C toggle; if disabled, skip using cache
|
|
1086
|
-
if (!this.webex.config?.calling?.cacheU2C) {
|
|
1087
|
-
this.logger.info('services: skipping cache warm-up as per the cache config');
|
|
1088
|
-
|
|
1089
|
-
return false;
|
|
1090
|
-
}
|
|
1091
|
-
|
|
1092
|
-
const ls = this._getLocalStorageSafe();
|
|
1093
|
-
if (!ls) {
|
|
1094
|
-
this.logger.info('services: skipping cache warm-up as no localStorage is available');
|
|
1095
|
-
|
|
1096
|
-
return false;
|
|
1097
|
-
}
|
|
1098
|
-
const cachedJson = ls.getItem(CATALOG_CACHE_KEY_V2);
|
|
1099
|
-
const cached = cachedJson ? JSON.parse(cachedJson) : undefined;
|
|
1100
|
-
if (!cached) {
|
|
1101
|
-
return false;
|
|
1102
|
-
}
|
|
1103
|
-
|
|
1104
|
-
// TTL enforcement
|
|
1105
|
-
const cachedAt = Number(cached.cachedAt) || 0;
|
|
1106
|
-
if (!cachedAt || Date.now() - cachedAt > CATALOG_TTL_MS) {
|
|
1107
|
-
this.clearCatalogCache();
|
|
1108
|
-
|
|
1109
|
-
return false;
|
|
1110
|
-
}
|
|
1111
|
-
|
|
1112
|
-
// If authorized, ensure cached org matches
|
|
1113
|
-
try {
|
|
1114
|
-
if (this.webex.credentials?.canAuthorize) {
|
|
1115
|
-
const {credentials} = this.webex;
|
|
1116
|
-
currentOrgId = credentials.getOrgId();
|
|
1117
|
-
if (cached.orgId && cached.orgId !== currentOrgId) {
|
|
1118
|
-
return false;
|
|
1119
|
-
}
|
|
1120
|
-
}
|
|
1121
|
-
} catch (e) {
|
|
1122
|
-
this.logger.warn('services: error checking orgId', e);
|
|
1123
|
-
}
|
|
1124
|
-
|
|
1125
|
-
// Ensure cached environment matches current environment
|
|
1126
|
-
|
|
1127
|
-
const fedramp = !!this.webex.config?.fedramp;
|
|
1128
|
-
const u2cDiscoveryUrl = this.webex.config?.services?.discovery?.u2c;
|
|
1129
|
-
const currentEnv = {fedramp, u2cDiscoveryUrl};
|
|
1130
|
-
if (cached.env) {
|
|
1131
|
-
const sameEnv =
|
|
1132
|
-
cached.env.fedramp === currentEnv.fedramp &&
|
|
1133
|
-
cached.env.u2cDiscoveryUrl === currentEnv.u2cDiscoveryUrl;
|
|
1134
|
-
if (!sameEnv) {
|
|
1135
|
-
return false;
|
|
1136
|
-
}
|
|
1137
|
-
}
|
|
1138
|
-
|
|
1139
|
-
const catalog = this._getCatalog();
|
|
1140
|
-
const groups: Array<ServiceGroup> = ['preauth', 'signin', 'postauth'];
|
|
1141
|
-
|
|
1142
|
-
groups.forEach((serviceGroup) => {
|
|
1143
|
-
const cachedGroup = cached[serviceGroup];
|
|
1144
|
-
if (!cachedGroup) {
|
|
1145
|
-
return;
|
|
1146
|
-
}
|
|
1147
|
-
|
|
1148
|
-
// Support legacy (hostMap) and new ({hostMap, meta}) shapes
|
|
1149
|
-
const hostMap: ServiceHostmap =
|
|
1150
|
-
cachedGroup && cachedGroup.hostMap ? cachedGroup.hostMap : cachedGroup;
|
|
1151
|
-
const meta: SelectionMeta | undefined = cachedGroup?.meta;
|
|
1152
|
-
|
|
1153
|
-
if (serviceGroup === 'preauth' && meta) {
|
|
1154
|
-
// For proximity-based selection, always fetch fresh to respect IP/region changes
|
|
1155
|
-
if (meta.selectionType === 'mode') {
|
|
1156
|
-
return;
|
|
1157
|
-
}
|
|
1158
|
-
|
|
1159
|
-
const intended = this.getIntendedPreauthSelection(currentOrgId);
|
|
1160
|
-
const matches =
|
|
1161
|
-
intended &&
|
|
1162
|
-
intended.selectionType === meta.selectionType &&
|
|
1163
|
-
intended.selectionValue === meta.selectionValue;
|
|
1164
|
-
if (!matches) {
|
|
1165
|
-
return;
|
|
1166
|
-
}
|
|
1167
|
-
}
|
|
1168
|
-
|
|
1169
|
-
if (hostMap) {
|
|
1170
|
-
catalog.updateServiceGroups(serviceGroup, hostMap?.services, hostMap?.timestamp);
|
|
1171
|
-
}
|
|
1172
|
-
});
|
|
1173
|
-
|
|
1174
|
-
this.updateCredentialsConfig();
|
|
1175
|
-
|
|
1176
|
-
return true;
|
|
1177
|
-
} catch (e) {
|
|
1178
|
-
return false;
|
|
1179
|
-
}
|
|
1180
|
-
},
|
|
1181
|
-
|
|
1182
|
-
/**
|
|
1183
|
-
* Clear the catalog cache from the bounded storage (v2).
|
|
1184
|
-
* @returns {Promise<void>}
|
|
1185
|
-
*/
|
|
1186
|
-
clearCatalogCache(): Promise<void> {
|
|
1187
|
-
try {
|
|
1188
|
-
const ls = this._getLocalStorageSafe();
|
|
1189
|
-
if (ls) {
|
|
1190
|
-
ls.removeItem(CATALOG_CACHE_KEY_V2);
|
|
1191
|
-
}
|
|
1192
|
-
} catch (e) {
|
|
1193
|
-
this.logger.warn('services: error clearing catalog cache', e);
|
|
1194
|
-
}
|
|
1195
|
-
|
|
1196
|
-
return Promise.resolve();
|
|
1197
|
-
},
|
|
1198
|
-
|
|
1199
937
|
/**
|
|
1200
938
|
* @private
|
|
1201
939
|
* Simplified method wrapper for sending a request to get
|
|
@@ -1355,13 +1093,7 @@ const Services = WebexPlugin.extend({
|
|
|
1355
1093
|
|
|
1356
1094
|
// wait for webex instance to be ready before attempting
|
|
1357
1095
|
// to update the service catalogs
|
|
1358
|
-
this.listenToOnce(this.webex, 'ready',
|
|
1359
|
-
const warmed = await this._loadCatalogFromCache();
|
|
1360
|
-
if (warmed) {
|
|
1361
|
-
catalog.isReady = true;
|
|
1362
|
-
|
|
1363
|
-
return;
|
|
1364
|
-
}
|
|
1096
|
+
this.listenToOnce(this.webex, 'ready', () => {
|
|
1365
1097
|
const {supertoken} = this.webex.credentials;
|
|
1366
1098
|
// Validate if the supertoken exists.
|
|
1367
1099
|
if (supertoken && supertoken.access_token) {
|
|
@@ -2,11 +2,6 @@ type ServiceName = string;
|
|
|
2
2
|
type ClusterId = string;
|
|
3
3
|
export type ServiceGroup = 'discovery' | 'override' | 'preauth' | 'postauth' | 'signin';
|
|
4
4
|
|
|
5
|
-
export type SelectionMeta = {
|
|
6
|
-
selectionType: string;
|
|
7
|
-
selectionValue: string;
|
|
8
|
-
};
|
|
9
|
-
|
|
10
5
|
export type ServiceHost = {
|
|
11
6
|
host: string;
|
|
12
7
|
ttl: number;
|