@webex/webex-core 3.8.0-next.2 → 3.8.0-next.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/dist/index.js +43 -0
  2. package/dist/index.js.map +1 -1
  3. package/dist/lib/batcher.js +1 -1
  4. package/dist/lib/credentials/credentials.js +1 -1
  5. package/dist/lib/credentials/token.js +1 -1
  6. package/dist/lib/services/service-catalog.js +23 -68
  7. package/dist/lib/services/service-catalog.js.map +1 -1
  8. package/dist/lib/services/services.js +1 -1
  9. package/dist/lib/services-v2/constants.js +17 -0
  10. package/dist/lib/services-v2/constants.js.map +1 -0
  11. package/dist/lib/services-v2/index.js +58 -0
  12. package/dist/lib/services-v2/index.js.map +1 -0
  13. package/dist/lib/services-v2/interceptors/hostmap.js +64 -0
  14. package/dist/lib/services-v2/interceptors/hostmap.js.map +1 -0
  15. package/dist/lib/services-v2/interceptors/server-error.js +77 -0
  16. package/dist/lib/services-v2/interceptors/server-error.js.map +1 -0
  17. package/dist/lib/services-v2/interceptors/service.js +137 -0
  18. package/dist/lib/services-v2/interceptors/service.js.map +1 -0
  19. package/dist/lib/services-v2/metrics.js +12 -0
  20. package/dist/lib/services-v2/metrics.js.map +1 -0
  21. package/dist/lib/services-v2/service-catalog.js +433 -0
  22. package/dist/lib/services-v2/service-catalog.js.map +1 -0
  23. package/dist/lib/services-v2/service-fed-ramp.js +13 -0
  24. package/dist/lib/services-v2/service-fed-ramp.js.map +1 -0
  25. package/dist/lib/services-v2/service-url.js +119 -0
  26. package/dist/lib/services-v2/service-url.js.map +1 -0
  27. package/dist/lib/services-v2/services-v2.js +963 -0
  28. package/dist/lib/services-v2/services-v2.js.map +1 -0
  29. package/dist/plugins/logger.js +1 -1
  30. package/dist/webex-core.js +2 -2
  31. package/dist/webex-core.js.map +1 -1
  32. package/package.json +13 -13
  33. package/src/index.js +10 -0
  34. package/src/lib/services/service-catalog.js +14 -54
  35. package/src/lib/services-v2/README.md +3 -0
  36. package/src/lib/services-v2/constants.js +21 -0
  37. package/src/lib/services-v2/index.js +23 -0
  38. package/src/lib/services-v2/interceptors/hostmap.js +36 -0
  39. package/src/lib/services-v2/interceptors/server-error.js +48 -0
  40. package/src/lib/services-v2/interceptors/service.js +101 -0
  41. package/src/lib/services-v2/metrics.js +4 -0
  42. package/src/lib/services-v2/service-catalog.js +455 -0
  43. package/src/lib/services-v2/service-fed-ramp.js +5 -0
  44. package/src/lib/services-v2/service-url.js +124 -0
  45. package/src/lib/services-v2/services-v2.js +971 -0
  46. package/test/fixtures/host-catalog-v2.js +247 -0
  47. package/test/unit/spec/services/service-catalog.js +30 -90
  48. package/test/unit/spec/services-v2/services-v2.js +564 -0
@@ -0,0 +1,23 @@
1
+ /*!
2
+ * Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.
3
+ */
4
+ // import {registerInternalPlugin} from '../../webex-core';
5
+
6
+ import * as constants from './constants';
7
+ // import ServerErrorInterceptor from './interceptors/server-error';
8
+ // import ServiceInterceptor from './interceptors/service';
9
+ export {default as ServicesV2} from './services-v2';
10
+
11
+ // registerInternalPlugin('services', ServicesV2, {
12
+ // interceptors: {
13
+ // ServiceInterceptor: ServiceInterceptor.create,
14
+ // ServerErrorInterceptor: ServerErrorInterceptor.create,
15
+ // },
16
+ // });
17
+
18
+ export {constants};
19
+ export {default as ServiceInterceptorV2} from './interceptors/service';
20
+ export {default as ServerErrorInterceptorV2} from './interceptors/server-error';
21
+ export {default as HostMapInterceptorV2} from './interceptors/hostmap';
22
+ export {default as ServiceCatalogV2} from './service-catalog';
23
+ export {default as ServiceUrlV2} from './service-url';
@@ -0,0 +1,36 @@
1
+ /*!
2
+ * Copyright (c) 2015-2024 Cisco Systems, Inc. See LICENSE file.
3
+ */
4
+
5
+ import {Interceptor} from '@webex/http-core';
6
+
7
+ /**
8
+ * This interceptor replaces the host in the request uri with the host from the hostmap
9
+ * It will attempt to do this for every request, but not all URIs will be in the hostmap
10
+ * URIs with hosts that are not in the hostmap will be left unchanged
11
+ */
12
+ export default class HostMapInterceptor extends Interceptor {
13
+ /**
14
+ * @returns {HostMapInterceptor}
15
+ */
16
+ static create() {
17
+ return new HostMapInterceptor({webex: this});
18
+ }
19
+
20
+ /**
21
+ * @see Interceptor#onRequest
22
+ * @param {Object} options
23
+ * @returns {Object}
24
+ */
25
+ onRequest(options) {
26
+ if (options.uri) {
27
+ try {
28
+ options.uri = this.webex.internal.services.replaceHostFromHostmap(options.uri);
29
+ } catch (error) {
30
+ /* empty */
31
+ }
32
+ }
33
+
34
+ return options;
35
+ }
36
+ }
@@ -0,0 +1,48 @@
1
+ /*!
2
+ * Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.
3
+ */
4
+
5
+ import {Interceptor} from '@webex/http-core';
6
+ import WebexHttpError from '../../webex-http-error';
7
+ /**
8
+ * Changes server url when it fails
9
+ */
10
+ export default class ServerErrorInterceptor extends Interceptor {
11
+ /**
12
+ * @returns {HAMessagingInterceptor}
13
+ */
14
+ static create() {
15
+ // eslint-disable-next-line no-invalid-this
16
+ return new ServerErrorInterceptor({webex: this});
17
+ }
18
+
19
+ /**
20
+ * @see Interceptor#onResponseError
21
+ * @param {Object} options
22
+ * @param {Object} reason
23
+ * @returns {Object}
24
+ */
25
+ onResponseError(options, reason) {
26
+ if (
27
+ (reason instanceof WebexHttpError.InternalServerError ||
28
+ reason instanceof WebexHttpError.BadGateway ||
29
+ reason instanceof WebexHttpError.ServiceUnavailable) &&
30
+ options.uri
31
+ ) {
32
+ const feature = this.webex.internal.device.features.developer.get('web-high-availability');
33
+
34
+ if (feature && feature.value) {
35
+ this.webex.internal.metrics.submitClientMetrics('web-ha', {
36
+ fields: {success: false},
37
+ tags: {action: 'failed', error: reason.message, url: options.uri},
38
+ });
39
+
40
+ return Promise.resolve(this.webex.internal.services.markFailedUrl(options.uri)).then(() =>
41
+ Promise.reject(reason)
42
+ );
43
+ }
44
+ }
45
+
46
+ return Promise.reject(reason);
47
+ }
48
+ }
@@ -0,0 +1,101 @@
1
+ /*!
2
+ * Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.
3
+ */
4
+
5
+ import {Interceptor} from '@webex/http-core';
6
+
7
+ const trailingSlashes = /(?:^\/)|(?:\/$)/;
8
+
9
+ /**
10
+ * @class
11
+ */
12
+ export default class ServiceInterceptor extends Interceptor {
13
+ /**
14
+ * @returns {ServiceInterceptor}
15
+ */
16
+ static create() {
17
+ /* eslint no-invalid-this: [0] */
18
+ return new ServiceInterceptor({webex: this});
19
+ }
20
+
21
+ /* eslint-disable no-param-reassign */
22
+ /**
23
+ * @see Interceptor#onRequest
24
+ * @param {Object} options - The request PTO.
25
+ * @returns {Object} - The mutated request PTO.
26
+ */
27
+ onRequest(options) {
28
+ // Validate that the PTO includes a uri property.
29
+ if (options.uri) {
30
+ return options;
31
+ }
32
+
33
+ // Normalize and validate the PTO.
34
+ this.normalizeOptions(options);
35
+ this.validateOptions(options);
36
+
37
+ // Destructure commonly referenced namespaces.
38
+ const {services} = this.webex.internal;
39
+ const {service, resource, waitForServiceTimeout} = options;
40
+
41
+ // Attempt to collect the service url.
42
+ return services
43
+ .waitForService({name: service, timeout: waitForServiceTimeout})
44
+ .then((serviceUrl) => {
45
+ // Generate the combined service url and resource.
46
+ options.uri = this.generateUri(serviceUrl, resource);
47
+
48
+ return options;
49
+ })
50
+ .catch(() =>
51
+ Promise.reject(new Error(`service-interceptor: '${service}' is not a known service`))
52
+ );
53
+ }
54
+
55
+ /* eslint-disable class-methods-use-this */
56
+ /**
57
+ * Generate a usable request uri string from a service url and a resouce.
58
+ *
59
+ * @param {string} serviceUrl - The service url.
60
+ * @param {string} [resource] - The resouce to be appended to the service url.
61
+ * @returns {string} - The combined service url and resource.
62
+ */
63
+ generateUri(serviceUrl, resource = '') {
64
+ const formattedService = serviceUrl.replace(trailingSlashes, '');
65
+ const formattedResource = resource.replace(trailingSlashes, '');
66
+
67
+ return `${formattedService}/${formattedResource}`;
68
+ }
69
+
70
+ /**
71
+ * Normalizes request options relative to service identification.
72
+ *
73
+ * @param {Object} options - The request PTO.
74
+ * @returns {Object} - The mutated request PTO.
75
+ */
76
+ normalizeOptions(options) {
77
+ // Validate if the api property is used.
78
+ if (options.api) {
79
+ // Assign the service property the value of the api property if necessary.
80
+ options.service = options.service || options.api;
81
+ delete options.api;
82
+ }
83
+ }
84
+
85
+ /**
86
+ * Validates that the appropriate options for this interceptor are present.
87
+ *
88
+ * @param {Object} options - The request PTO.
89
+ * @returns {Object} - The mutated request PTO.
90
+ */
91
+ validateOptions(options) {
92
+ if (!options.resource) {
93
+ throw new Error('a `resource` parameter is required');
94
+ }
95
+
96
+ if (!options.service) {
97
+ throw new Error("a valid 'service' parameter is required");
98
+ }
99
+ }
100
+ /* eslint-enable class-methods-use-this, no-param-reassign */
101
+ }
@@ -0,0 +1,4 @@
1
+ // Metrics for service catalog
2
+ export default {
3
+ JS_SDK_SERVICE_NOT_FOUND: 'JS_SDK_SERVICE_NOT_FOUND',
4
+ };
@@ -0,0 +1,455 @@
1
+ import Url from 'url';
2
+
3
+ import AmpState from 'ampersand-state';
4
+
5
+ import {union} from 'lodash';
6
+ import ServiceUrl from './service-url';
7
+
8
+ /* eslint-disable no-underscore-dangle */
9
+ /**
10
+ * @class
11
+ */
12
+ const ServiceCatalog = AmpState.extend({
13
+ namespace: 'ServiceCatalog',
14
+
15
+ props: {
16
+ serviceGroups: [
17
+ 'object',
18
+ true,
19
+ () => ({
20
+ discovery: [],
21
+ override: [],
22
+ preauth: [],
23
+ postauth: [],
24
+ signin: [],
25
+ }),
26
+ ],
27
+ status: [
28
+ 'object',
29
+ true,
30
+ () => ({
31
+ discovery: {
32
+ ready: false,
33
+ collecting: false,
34
+ },
35
+ override: {
36
+ ready: false,
37
+ collecting: false,
38
+ },
39
+ preauth: {
40
+ ready: false,
41
+ collecting: false,
42
+ },
43
+ postauth: {
44
+ ready: false,
45
+ collecting: false,
46
+ },
47
+ signin: {
48
+ ready: false,
49
+ collecting: false,
50
+ },
51
+ }),
52
+ ],
53
+ isReady: ['boolean', false, false],
54
+ allowedDomains: ['array', false, () => []],
55
+ },
56
+
57
+ /**
58
+ * @private
59
+ * Search the service url array to locate a `ServiceUrl`
60
+ * class object based on its name.
61
+ * @param {string} name
62
+ * @param {string} [serviceGroup]
63
+ * @returns {ServiceUrl}
64
+ */
65
+ _getUrl(name, serviceGroup) {
66
+ const serviceUrls =
67
+ typeof serviceGroup === 'string'
68
+ ? this.serviceGroups[serviceGroup] || []
69
+ : [
70
+ ...this.serviceGroups.override,
71
+ ...this.serviceGroups.postauth,
72
+ ...this.serviceGroups.signin,
73
+ ...this.serviceGroups.preauth,
74
+ ...this.serviceGroups.discovery,
75
+ ];
76
+
77
+ return serviceUrls.find((serviceUrl) => serviceUrl.name === name);
78
+ },
79
+
80
+ /**
81
+ * @private
82
+ * Generate an array of `ServiceUrl`s that is organized from highest auth
83
+ * level to lowest auth level.
84
+ * @returns {Array<ServiceUrl>} - array of `ServiceUrl`s
85
+ */
86
+ _listServiceUrls() {
87
+ return [
88
+ ...this.serviceGroups.override,
89
+ ...this.serviceGroups.postauth,
90
+ ...this.serviceGroups.signin,
91
+ ...this.serviceGroups.preauth,
92
+ ...this.serviceGroups.discovery,
93
+ ];
94
+ },
95
+
96
+ /**
97
+ * @private
98
+ * Safely load one or more `ServiceUrl`s into this `Services` instance.
99
+ * @param {string} serviceGroup
100
+ * @param {Array<ServiceUrl>} services
101
+ * @returns {Services}
102
+ */
103
+ _loadServiceUrls(serviceGroup, services) {
104
+ // declare namespaces outside of loop
105
+ let existingService;
106
+
107
+ services.forEach((service) => {
108
+ existingService = this._getUrl(service.name, serviceGroup);
109
+
110
+ if (!existingService) {
111
+ this.serviceGroups[serviceGroup].push(service);
112
+ }
113
+ });
114
+
115
+ return this;
116
+ },
117
+
118
+ /**
119
+ * @private
120
+ * Safely unload one or more `ServiceUrl`s into this `Services` instance
121
+ * @param {string} serviceGroup
122
+ * @param {Array<ServiceUrl>} services
123
+ * @returns {Services}
124
+ */
125
+ _unloadServiceUrls(serviceGroup, services) {
126
+ // declare namespaces outside of loop
127
+ let existingService;
128
+
129
+ services.forEach((service) => {
130
+ existingService = this._getUrl(service.name, serviceGroup);
131
+
132
+ if (existingService) {
133
+ this.serviceGroups[serviceGroup].splice(
134
+ this.serviceGroups[serviceGroup].indexOf(existingService),
135
+ 1
136
+ );
137
+ }
138
+ });
139
+
140
+ return this;
141
+ },
142
+
143
+ /**
144
+ * Clear all collected catalog data and reset catalog status.
145
+ *
146
+ * @returns {void}
147
+ */
148
+ clean() {
149
+ this.serviceGroups.preauth.length = 0;
150
+ this.serviceGroups.signin.length = 0;
151
+ this.serviceGroups.postauth.length = 0;
152
+ this.status.preauth = {ready: false};
153
+ this.status.signin = {ready: false};
154
+ this.status.postauth = {ready: false};
155
+ },
156
+
157
+ /**
158
+ * Search over all service groups to find a cluster id based
159
+ * on a given url.
160
+ * @param {string} url - Must be parsable by `Url`
161
+ * @returns {string} - ClusterId of a given url
162
+ */
163
+ findClusterId(url) {
164
+ const incomingUrlObj = Url.parse(url);
165
+ let serviceUrlObj;
166
+
167
+ for (const key of Object.keys(this.serviceGroups)) {
168
+ for (const service of this.serviceGroups[key]) {
169
+ serviceUrlObj = Url.parse(service.defaultUrl);
170
+
171
+ for (const host of service.hosts) {
172
+ if (incomingUrlObj.hostname === host.host && host.id) {
173
+ return host.id;
174
+ }
175
+ }
176
+
177
+ if (serviceUrlObj.hostname === incomingUrlObj.hostname && service.hosts.length > 0) {
178
+ // no exact match, so try to grab the first home cluster
179
+ for (const host of service.hosts) {
180
+ if (host.homeCluster) {
181
+ return host.id;
182
+ }
183
+ }
184
+
185
+ // no match found still, so return the first entry
186
+ return service.hosts[0].id;
187
+ }
188
+ }
189
+ }
190
+
191
+ return undefined;
192
+ },
193
+
194
+ /**
195
+ * Search over all service groups and return a service value from a provided
196
+ * clusterId. Currently, this method will return either a service name, or a
197
+ * service url depending on the `value` parameter. If the `value` parameter
198
+ * is set to `name`, it will return a service name to be utilized within the
199
+ * Services plugin methods.
200
+ * @param {object} params
201
+ * @param {string} params.clusterId - clusterId of found service
202
+ * @param {boolean} [params.priorityHost = true] - returns priority host url if true
203
+ * @param {string} [params.serviceGroup] - specify service group
204
+ * @returns {object} service
205
+ * @returns {string} service.name
206
+ * @returns {string} service.url
207
+ */
208
+ findServiceFromClusterId({clusterId, priorityHost = true, serviceGroup} = {}) {
209
+ const serviceUrls =
210
+ typeof serviceGroup === 'string'
211
+ ? this.serviceGroups[serviceGroup] || []
212
+ : [
213
+ ...this.serviceGroups.override,
214
+ ...this.serviceGroups.postauth,
215
+ ...this.serviceGroups.signin,
216
+ ...this.serviceGroups.preauth,
217
+ ...this.serviceGroups.discovery,
218
+ ];
219
+
220
+ const identifiedServiceUrl = serviceUrls.find((serviceUrl) =>
221
+ serviceUrl.hosts.find((host) => host.id === clusterId)
222
+ );
223
+
224
+ if (identifiedServiceUrl) {
225
+ return {
226
+ name: identifiedServiceUrl.name,
227
+ url: identifiedServiceUrl.get(priorityHost, clusterId),
228
+ };
229
+ }
230
+
231
+ return undefined;
232
+ },
233
+
234
+ /**
235
+ * Find a service based on the provided url.
236
+ * @param {string} url - Must be parsable by `Url`
237
+ * @returns {serviceUrl} - ServiceUrl assocated with provided url
238
+ */
239
+ findServiceUrlFromUrl(url) {
240
+ const serviceUrls = [
241
+ ...this.serviceGroups.discovery,
242
+ ...this.serviceGroups.preauth,
243
+ ...this.serviceGroups.signin,
244
+ ...this.serviceGroups.postauth,
245
+ ...this.serviceGroups.override,
246
+ ];
247
+
248
+ return serviceUrls.find((serviceUrl) => {
249
+ // Check to see if the URL we are checking starts with the default URL
250
+ if (url.startsWith(serviceUrl.defaultUrl)) {
251
+ return true;
252
+ }
253
+
254
+ // If not, we check to see if the alternate URLs match
255
+ // These are made by swapping the host of the default URL
256
+ // with that of an alternate host
257
+ for (const host of serviceUrl.hosts) {
258
+ const alternateUrl = new URL(serviceUrl.defaultUrl);
259
+ alternateUrl.host = host.host;
260
+
261
+ if (url.startsWith(alternateUrl.toString())) {
262
+ return true;
263
+ }
264
+ }
265
+
266
+ return false;
267
+ });
268
+ },
269
+
270
+ /**
271
+ * Finds an allowed domain that matches a specific url.
272
+ *
273
+ * @param {string} url - The url to match the allowed domains against.
274
+ * @returns {string} - The matching allowed domain.
275
+ */
276
+ findAllowedDomain(url) {
277
+ const urlObj = Url.parse(url);
278
+
279
+ if (!urlObj.host) {
280
+ return undefined;
281
+ }
282
+
283
+ return this.allowedDomains.find((allowedDomain) => urlObj.host.includes(allowedDomain));
284
+ },
285
+
286
+ /**
287
+ * Get a service url from the current services list by name.
288
+ * @param {string} name
289
+ * @param {boolean} priorityHost
290
+ * @param {string} serviceGroup
291
+ * @returns {string}
292
+ */
293
+ get(name, priorityHost, serviceGroup) {
294
+ const serviceUrl = this._getUrl(name, serviceGroup);
295
+
296
+ return serviceUrl ? serviceUrl.get(priorityHost) : undefined;
297
+ },
298
+
299
+ /**
300
+ * Get the current allowed domains list.
301
+ *
302
+ * @returns {Array<string>} - the current allowed domains list.
303
+ */
304
+ getAllowedDomains() {
305
+ return [...this.allowedDomains];
306
+ },
307
+
308
+ /**
309
+ * Creates an object where the keys are the service names
310
+ * and the values are the service urls.
311
+ * @param {boolean} priorityHost - use the highest priority if set to `true`
312
+ * @param {string} [serviceGroup]
313
+ * @returns {Record<string, string>}
314
+ */
315
+ list(priorityHost, serviceGroup) {
316
+ const output = {};
317
+
318
+ const serviceUrls =
319
+ typeof serviceGroup === 'string'
320
+ ? this.serviceGroups[serviceGroup] || []
321
+ : [
322
+ ...this.serviceGroups.discovery,
323
+ ...this.serviceGroups.preauth,
324
+ ...this.serviceGroups.signin,
325
+ ...this.serviceGroups.postauth,
326
+ ...this.serviceGroups.override,
327
+ ];
328
+
329
+ if (serviceUrls) {
330
+ serviceUrls.forEach((serviceUrl) => {
331
+ output[serviceUrl.name] = serviceUrl.get(priorityHost);
332
+ });
333
+ }
334
+
335
+ return output;
336
+ },
337
+
338
+ /**
339
+ * Mark a priority host service url as failed.
340
+ * This will mark the host associated with the
341
+ * `ServiceUrl` to be removed from the its
342
+ * respective host array, and then return the next
343
+ * viable host from the `ServiceUrls` host array,
344
+ * or the `ServiceUrls` default url if no other priority
345
+ * hosts are available, or if `noPriorityHosts` is set to
346
+ * `true`.
347
+ * @param {string} url
348
+ * @param {boolean} noPriorityHosts
349
+ * @returns {string}
350
+ */
351
+ markFailedUrl(url, noPriorityHosts) {
352
+ const serviceUrl = this._getUrl(
353
+ Object.keys(this.list()).find((key) => this._getUrl(key).failHost(url))
354
+ );
355
+
356
+ if (!serviceUrl) {
357
+ return undefined;
358
+ }
359
+
360
+ return noPriorityHosts ? serviceUrl.get(false) : serviceUrl.get(true);
361
+ },
362
+
363
+ /**
364
+ * Set the allowed domains for the catalog.
365
+ *
366
+ * @param {Array<string>} allowedDomains - allowed domains to be assigned.
367
+ * @returns {void}
368
+ */
369
+ setAllowedDomains(allowedDomains) {
370
+ this.allowedDomains = [...allowedDomains];
371
+ },
372
+
373
+ /**
374
+ *
375
+ * @param {Array<string>} newAllowedDomains - new allowed domains to add to existing set of allowed domains
376
+ * @returns {void}
377
+ */
378
+ addAllowedDomains(newAllowedDomains) {
379
+ this.allowedDomains = union(this.allowedDomains, newAllowedDomains);
380
+ },
381
+
382
+ /**
383
+ * Update the current list of `ServiceUrl`s against a provided
384
+ * service hostmap.
385
+ * @emits ServiceCatalog#preauthorized
386
+ * @emits ServiceCatalog#postauthorized
387
+ * @param {string} serviceGroup
388
+ * @param {object} serviceHostmap
389
+ * @returns {Services}
390
+ */
391
+ updateServiceUrls(serviceGroup, serviceHostmap) {
392
+ const currentServiceUrls = this.serviceGroups[serviceGroup];
393
+
394
+ const unusedUrls = currentServiceUrls.filter((serviceUrl) =>
395
+ serviceHostmap.every((item) => item.name !== serviceUrl.name)
396
+ );
397
+
398
+ this._unloadServiceUrls(serviceGroup, unusedUrls);
399
+
400
+ serviceHostmap.forEach((serviceObj) => {
401
+ const service = this._getUrl(serviceObj.name, serviceGroup);
402
+
403
+ if (service) {
404
+ service.defaultUrl = serviceObj.defaultUrl;
405
+ service.hosts = serviceObj.hosts || [];
406
+ } else {
407
+ this._loadServiceUrls(serviceGroup, [
408
+ new ServiceUrl({
409
+ ...serviceObj,
410
+ }),
411
+ ]);
412
+ }
413
+ });
414
+
415
+ this.status[serviceGroup].ready = true;
416
+ this.trigger(serviceGroup);
417
+
418
+ return this;
419
+ },
420
+
421
+ /**
422
+ * Wait until the service catalog is available,
423
+ * or reject after a timeout of 60 seconds.
424
+ * @param {string} serviceGroup
425
+ * @param {number} [timeout] - in seconds
426
+ * @returns {Promise<void>}
427
+ */
428
+ waitForCatalog(serviceGroup, timeout) {
429
+ return new Promise((resolve, reject) => {
430
+ if (this.status[serviceGroup].ready) {
431
+ resolve();
432
+ }
433
+
434
+ const validatedTimeout = typeof timeout === 'number' && timeout >= 0 ? timeout : 60;
435
+
436
+ const timeoutTimer = setTimeout(
437
+ () =>
438
+ reject(
439
+ new Error(
440
+ `services: timeout occured while waiting for '${serviceGroup}' catalog to populate`
441
+ )
442
+ ),
443
+ validatedTimeout * 1000
444
+ );
445
+
446
+ this.once(serviceGroup, () => {
447
+ clearTimeout(timeoutTimer);
448
+ resolve();
449
+ });
450
+ });
451
+ },
452
+ });
453
+ /* eslint-enable no-underscore-dangle */
454
+
455
+ export default ServiceCatalog;
@@ -0,0 +1,5 @@
1
+ export default {
2
+ hydra: 'https://api-usgov.webex.com/v1',
3
+ u2c: 'https://u2c.gov.ciscospark.com/u2c/api/v1',
4
+ sqdiscovery: 'https://ds.ciscospark.com/v1/region', // TODO: fedramp load balanced URL? this has been here for years as of now but now explicitly done
5
+ };