@webex/internal-plugin-device 3.0.0-beta.3 → 3.0.0-beta.300

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 (44) hide show
  1. package/README.md +10 -6
  2. package/dist/config.js +0 -8
  3. package/dist/config.js.map +1 -1
  4. package/dist/constants.js +2 -3
  5. package/dist/constants.js.map +1 -1
  6. package/dist/device.js +102 -172
  7. package/dist/device.js.map +1 -1
  8. package/dist/features/feature-collection.js +1 -8
  9. package/dist/features/feature-collection.js.map +1 -1
  10. package/dist/features/feature-model.js +15 -42
  11. package/dist/features/feature-model.js.map +1 -1
  12. package/dist/features/features-model.js +9 -21
  13. package/dist/features/features-model.js.map +1 -1
  14. package/dist/features/index.js +0 -8
  15. package/dist/features/index.js.map +1 -1
  16. package/dist/index.js +2 -24
  17. package/dist/index.js.map +1 -1
  18. package/dist/interceptors/device-url.js +12 -33
  19. package/dist/interceptors/device-url.js.map +1 -1
  20. package/dist/ipNetworkDetector.js +200 -0
  21. package/dist/ipNetworkDetector.js.map +1 -0
  22. package/dist/metrics.js +0 -2
  23. package/dist/metrics.js.map +1 -1
  24. package/package.json +10 -10
  25. package/src/config.js +8 -9
  26. package/src/constants.js +3 -5
  27. package/src/device.js +149 -146
  28. package/src/features/feature-collection.js +1 -1
  29. package/src/features/feature-model.js +5 -11
  30. package/src/features/features-model.js +3 -9
  31. package/src/features/index.js +1 -5
  32. package/src/index.js +3 -11
  33. package/src/interceptors/device-url.js +5 -7
  34. package/src/ipNetworkDetector.ts +176 -0
  35. package/src/metrics.js +1 -2
  36. package/test/integration/spec/device.js +210 -239
  37. package/test/integration/spec/webex.js +9 -9
  38. package/test/unit/spec/device.js +44 -53
  39. package/test/unit/spec/features/feature-collection.js +2 -2
  40. package/test/unit/spec/features/feature-model.js +23 -39
  41. package/test/unit/spec/features/features-model.js +4 -12
  42. package/test/unit/spec/interceptors/device-url.js +69 -109
  43. package/test/unit/spec/ipNetworkDetector.js +410 -0
  44. package/test/unit/spec/wdm-dto.json +5 -13
package/src/device.js CHANGED
@@ -4,11 +4,9 @@ import {persist, waitForValue, WebexPlugin} from '@webex/webex-core';
4
4
  import {safeSetTimeout} from '@webex/common-timers';
5
5
 
6
6
  import METRICS from './metrics';
7
- import {
8
- FEATURE_COLLECTION_NAMES,
9
- DEVICE_EVENT_REGISTRATION_SUCCESS
10
- } from './constants';
7
+ import {FEATURE_COLLECTION_NAMES, DEVICE_EVENT_REGISTRATION_SUCCESS} from './constants';
11
8
  import FeaturesModel from './features/features-model';
9
+ import IpNetworkDetector from './ipNetworkDetector';
12
10
 
13
11
  /**
14
12
  * Determine if the plugin should be initialized based on cached storage.
@@ -20,7 +18,6 @@ function decider() {
20
18
  }
21
19
 
22
20
  const Device = WebexPlugin.extend({
23
-
24
21
  // Ampersand property members.
25
22
 
26
23
  namespace: 'Device',
@@ -37,7 +34,13 @@ const Device = WebexPlugin.extend({
37
34
  *
38
35
  * @type {FeaturesModel}
39
36
  */
40
- features: FeaturesModel
37
+ features: FeaturesModel,
38
+ /**
39
+ * Helper class for detecting what IP network version (ipv4, ipv6) we're on.
40
+ *
41
+ * @type {IpNetworkDetector}
42
+ */
43
+ ipNetworkDetector: IpNetworkDetector,
41
44
  },
42
45
 
43
46
  /**
@@ -121,7 +124,7 @@ const Device = WebexPlugin.extend({
121
124
  *
122
125
  * @returns {Array<string>}
123
126
  */
124
- ecmSupportedStorageProviders: ['array', false, (() => [])],
127
+ ecmSupportedStorageProviders: ['array', false, () => []],
125
128
 
126
129
  /**
127
130
  * This property stores the modification time value retrieved from the
@@ -242,7 +245,7 @@ const Device = WebexPlugin.extend({
242
245
  *
243
246
  * @type {'ALLOW' | 'BLOCK'}
244
247
  */
245
- whiteboardFileShareControl: 'string'
248
+ whiteboardFileShareControl: 'string',
246
249
  },
247
250
 
248
251
  /**
@@ -268,9 +271,9 @@ const Device = WebexPlugin.extend({
268
271
  * @returns {boolean}
269
272
  */
270
273
  fn() {
271
- return !!(this.url);
272
- }
273
- }
274
+ return !!this.url;
275
+ },
276
+ },
274
277
  },
275
278
 
276
279
  /**
@@ -318,7 +321,7 @@ const Device = WebexPlugin.extend({
318
321
  *
319
322
  * @type {boolean}
320
323
  */
321
- isInNetwork: 'boolean'
324
+ isInNetwork: 'boolean',
322
325
  },
323
326
 
324
327
  // Event method members.
@@ -355,61 +358,60 @@ const Device = WebexPlugin.extend({
355
358
  this.logger.info('device: refreshing');
356
359
 
357
360
  // Validate that the device can be registered.
358
- return this.canRegister()
359
- .then(() => {
360
- // Validate if the device is not registered and register instead.
361
- if (!this.registered) {
362
- this.logger.info('device: device not registered, registering');
363
-
364
- return this.register();
365
- }
366
-
367
- // Merge body configurations, overriding defaults.
368
- const body = {
369
- ...(this.serialize()),
370
- ...(this.config.body ? this.config.body : {})
371
- };
372
-
373
- // Remove unneeded properties from the body object.
374
- delete body.features;
375
- delete body.mediaCluster;
376
- delete body.etag;
377
-
378
- // Append a ttl value if the device is marked as ephemeral.
379
- if (this.config.ephemeral) {
380
- body.ttl = this.config.ephemeralDeviceTTL;
381
- }
382
-
383
- // Merge header configurations, overriding defaults.
384
- const headers = {
385
- ...(this.config.defaults.headers ? this.config.defaults.headers : {}),
386
- ...(this.config.headers ? this.config.headers : {}),
387
- // If etag is sent, WDM will not send developer feature toggles unless they have changed
388
- ...(this.etag ? {'If-None-Match': this.etag} : {})
389
- };
390
-
391
- return this.request({
392
- method: 'PUT',
393
- uri: this.url,
394
- body,
395
- headers
396
- })
397
- .then((response) => this.processRegistrationSuccess(response))
398
- .catch((reason) => {
399
- // Handle a 404 error, which indicates that the device is no longer
400
- // valid and needs to be registered as a new device.
401
- if (reason.statusCode === 404) {
402
- this.logger.info('device: refresh failed, device is not valid');
403
- this.logger.info('device: attempting to register a new device');
361
+ return this.canRegister().then(() => {
362
+ // Validate if the device is not registered and register instead.
363
+ if (!this.registered) {
364
+ this.logger.info('device: device not registered, registering');
404
365
 
405
- this.clear();
366
+ return this.register();
367
+ }
406
368
 
407
- return this.register();
408
- }
369
+ // Merge body configurations, overriding defaults.
370
+ const body = {
371
+ ...this.serialize(),
372
+ ...(this.config.body ? this.config.body : {}),
373
+ };
409
374
 
410
- return Promise.reject(reason);
411
- });
412
- });
375
+ // Remove unneeded properties from the body object.
376
+ delete body.features;
377
+ delete body.mediaCluster;
378
+ delete body.etag;
379
+
380
+ // Append a ttl value if the device is marked as ephemeral.
381
+ if (this.config.ephemeral) {
382
+ body.ttl = this.config.ephemeralDeviceTTL;
383
+ }
384
+
385
+ // Merge header configurations, overriding defaults.
386
+ const headers = {
387
+ ...(this.config.defaults.headers ? this.config.defaults.headers : {}),
388
+ ...(this.config.headers ? this.config.headers : {}),
389
+ // If etag is sent, WDM will not send developer feature toggles unless they have changed
390
+ ...(this.etag ? {'If-None-Match': this.etag} : {}),
391
+ };
392
+
393
+ return this.request({
394
+ method: 'PUT',
395
+ uri: this.url,
396
+ body,
397
+ headers,
398
+ })
399
+ .then((response) => this.processRegistrationSuccess(response))
400
+ .catch((reason) => {
401
+ // Handle a 404 error, which indicates that the device is no longer
402
+ // valid and needs to be registered as a new device.
403
+ if (reason.statusCode === 404) {
404
+ this.logger.info('device: refresh failed, device is not valid');
405
+ this.logger.info('device: attempting to register a new device');
406
+
407
+ this.clear();
408
+
409
+ return this.register();
410
+ }
411
+
412
+ return Promise.reject(reason);
413
+ });
414
+ });
413
415
  },
414
416
 
415
417
  /**
@@ -425,52 +427,53 @@ const Device = WebexPlugin.extend({
425
427
  this.logger.info('device: registering');
426
428
 
427
429
  // Validate that the device can be registered.
428
- return this.canRegister()
429
- .then(() => {
430
- // Validate if the device is already registered and refresh instead.
431
- if (this.registered) {
432
- this.logger.info('device: device already registered, refreshing');
433
-
434
- return this.refresh();
435
- }
436
-
437
- // Merge body configurations, overriding defaults.
438
- const body = {
439
- ...(this.config.defaults.body ? this.config.defaults.body : {}),
440
- ...(this.config.body ? this.config.body : {})
441
- };
442
-
443
- // Merge header configurations, overriding defaults.
444
- const headers = {
445
- ...(this.config.defaults.headers ? this.config.defaults.headers : {}),
446
- ...(this.config.headers ? this.config.headers : {})
447
- };
448
-
449
- // Append a ttl value if the device is marked as ephemeral
450
- if (this.config.ephemeral) {
451
- body.ttl = this.config.ephemeralDeviceTTL;
452
- }
453
-
454
- // This will be replaced by a `create()` method.
455
- return this.request({
456
- method: 'POST',
457
- service: 'wdm',
458
- resource: 'devices',
459
- body,
460
- headers
430
+ return this.canRegister().then(() => {
431
+ // Validate if the device is already registered and refresh instead.
432
+ if (this.registered) {
433
+ this.logger.info('device: device already registered, refreshing');
434
+
435
+ return this.refresh();
436
+ }
437
+
438
+ // Merge body configurations, overriding defaults.
439
+ const body = {
440
+ ...(this.config.defaults.body ? this.config.defaults.body : {}),
441
+ ...(this.config.body ? this.config.body : {}),
442
+ };
443
+
444
+ // Merge header configurations, overriding defaults.
445
+ const headers = {
446
+ ...(this.config.defaults.headers ? this.config.defaults.headers : {}),
447
+ ...(this.config.headers ? this.config.headers : {}),
448
+ };
449
+
450
+ // Append a ttl value if the device is marked as ephemeral
451
+ if (this.config.ephemeral) {
452
+ body.ttl = this.config.ephemeralDeviceTTL;
453
+ }
454
+
455
+ // This will be replaced by a `create()` method.
456
+ return this.request({
457
+ method: 'POST',
458
+ service: 'wdm',
459
+ resource: 'devices',
460
+ body,
461
+ headers,
462
+ })
463
+ .then((response) => {
464
+ this.webex.internal.metrics.submitClientMetrics(
465
+ METRICS.JS_SDK_WDM_REGISTRATION_SUCCESSFUL
466
+ );
467
+
468
+ return this.processRegistrationSuccess(response);
461
469
  })
462
- .then((response) => {
463
- this.webex.internal.metrics.submitClientMetrics(METRICS.JS_SDK_WDM_REGISTRATION_SUCCESSFUL);
464
-
465
- return this.processRegistrationSuccess(response);
466
- })
467
- .catch((error) => {
468
- this.webex.internal.metrics.submitClientMetrics(METRICS.JS_SDK_WDM_REGISTRATION_FAILED, {
469
- fields: {error}
470
- });
471
- throw error;
470
+ .catch((error) => {
471
+ this.webex.internal.metrics.submitClientMetrics(METRICS.JS_SDK_WDM_REGISTRATION_FAILED, {
472
+ fields: {error},
472
473
  });
473
- });
474
+ throw error;
475
+ });
476
+ });
474
477
  },
475
478
 
476
479
  /**
@@ -493,9 +496,8 @@ const Device = WebexPlugin.extend({
493
496
 
494
497
  return this.request({
495
498
  uri: this.url,
496
- method: 'DELETE'
497
- })
498
- .then(() => this.clear());
499
+ method: 'DELETE',
500
+ }).then(() => this.clear());
499
501
  },
500
502
  /* eslint-enable require-jsdoc */
501
503
 
@@ -515,19 +517,19 @@ const Device = WebexPlugin.extend({
515
517
  const {services} = this.webex.internal;
516
518
 
517
519
  // Wait for the postauth catalog to populate.
518
- return services.waitForCatalog(
519
- 'postauth',
520
- this.config.canRegisterWaitDuration
521
- )
522
- .then(() => (
523
- // Validate that the service exists after waiting for the catalog.
524
- services.get('wdm') ?
525
- Promise.resolve() :
526
- Promise.reject(new Error([
527
- 'device: cannot register,',
528
- '\'wdm\' service is not available from the postauth catalog'
529
- ].join(' ')))
530
- ));
520
+ return services.waitForCatalog('postauth', this.config.canRegisterWaitDuration).then(() =>
521
+ // Validate that the service exists after waiting for the catalog.
522
+ services.get('wdm')
523
+ ? Promise.resolve()
524
+ : Promise.reject(
525
+ new Error(
526
+ [
527
+ 'device: cannot register,',
528
+ "'wdm' service is not available from the postauth catalog",
529
+ ].join(' ')
530
+ )
531
+ )
532
+ );
531
533
  },
532
534
 
533
535
  /**
@@ -556,14 +558,14 @@ const Device = WebexPlugin.extend({
556
558
  const headers = {
557
559
  'cisco-no-http-redirect': null,
558
560
  'spark-user-agent': null,
559
- trackingid: null
561
+ trackingid: null,
560
562
  };
561
563
 
562
564
  // Send the network reachability request.
563
565
  return this.request({
564
566
  headers,
565
567
  method: 'GET',
566
- uri: this.intranetInactivityCheckUrl
568
+ uri: this.intranetInactivityCheckUrl,
567
569
  })
568
570
  .then(() => {
569
571
  this.isInNetwork = true;
@@ -612,17 +614,15 @@ const Device = WebexPlugin.extend({
612
614
  .catch((error) => {
613
615
  this.logger.warn(error.message);
614
616
 
615
- return Promise.reject(new Error(
616
- 'device: failed to get the current websocket url'
617
- ));
617
+ return Promise.reject(new Error('device: failed to get the current websocket url'));
618
618
  });
619
619
  }
620
620
 
621
621
  // Validate if the device is registered.
622
622
  if (!this.registered) {
623
- return Promise.reject(new Error(
624
- 'device: cannot get websocket url, device is not registered'
625
- ));
623
+ return Promise.reject(
624
+ new Error('device: cannot get websocket url, device is not registered')
625
+ );
626
626
  }
627
627
 
628
628
  // Attempt to collect the priority-host-mapped web socket URL.
@@ -633,9 +633,7 @@ const Device = WebexPlugin.extend({
633
633
  return Promise.resolve(wsUrl);
634
634
  }
635
635
 
636
- return Promise.reject(new Error(
637
- 'device: failed to get the current websocket url'
638
- ));
636
+ return Promise.reject(new Error('device: failed to get the current websocket url'));
639
637
  },
640
638
 
641
639
  /**
@@ -654,9 +652,9 @@ const Device = WebexPlugin.extend({
654
652
  delete body.services;
655
653
  delete body.serviceHostMap;
656
654
 
657
- const {etag} = response.headers;
655
+ const {etag} = response.headers || {};
658
656
 
659
- if (this.etag && this.etag === etag) {
657
+ if (this.etag && etag && this.etag === etag) {
660
658
  // If current etag matches the previous one and we have sent
661
659
  // If-None-Match header the developer and entitlement feature
662
660
  // toggles will not be returned
@@ -707,12 +705,14 @@ const Device = WebexPlugin.extend({
707
705
 
708
706
  // Validate if the device is currently in a meeting and is configured to
709
707
  // required inactivity enforcement.
710
- if (!this.isInMeeting && this.config.enableInactivityEnforcement &&
711
- this.isReachabilityChecked) {
708
+ if (
709
+ !this.isInMeeting &&
710
+ this.config.enableInactivityEnforcement &&
711
+ this.isReachabilityChecked
712
+ ) {
712
713
  if (this.isInNetwork) {
713
714
  this.setLogoutTimer(this.inNetworkInactivityDuration);
714
- }
715
- else {
715
+ } else {
716
716
  this.setLogoutTimer(this.intranetInactivityDuration);
717
717
  }
718
718
  }
@@ -732,7 +732,9 @@ const Device = WebexPlugin.extend({
732
732
  }
733
733
 
734
734
  // Setup user activity date event listener.
735
- this.on('change:lastUserActivityDate', () => { this.resetLogoutTimer(); });
735
+ this.on('change:lastUserActivityDate', () => {
736
+ this.resetLogoutTimer();
737
+ });
736
738
 
737
739
  // Initialize a new timer.
738
740
  this.logoutTimer = safeSetTimeout(() => {
@@ -754,9 +756,10 @@ const Device = WebexPlugin.extend({
754
756
  resolve();
755
757
  }
756
758
 
757
- const timeoutTimer = safeSetTimeout(() => reject(
758
- new Error('device: timeout occured while waiting for registration')
759
- ), timeout * 1000);
759
+ const timeoutTimer = safeSetTimeout(
760
+ () => reject(new Error('device: timeout occured while waiting for registration')),
761
+ timeout * 1000
762
+ );
760
763
 
761
764
  this.once(DEVICE_EVENT_REGISTRATION_SUCCESS, () => {
762
765
  clearTimeout(timeoutTimer);
@@ -832,7 +835,7 @@ const Device = WebexPlugin.extend({
832
835
  this.isInMeeting = false;
833
836
  this.resetLogoutTimer();
834
837
  });
835
- }
838
+ },
836
839
  /* eslint-enable require-jsdoc */
837
840
  });
838
841
 
@@ -24,7 +24,7 @@ const FeatureCollection = AmpCollection.extend({
24
24
  *
25
25
  * @type {Class}
26
26
  */
27
- model: FeatureModel
27
+ model: FeatureModel,
28
28
  });
29
29
 
30
30
  export default FeatureCollection;
@@ -20,7 +20,6 @@ import {FEATURE_TYPES} from '../constants';
20
20
  * **WDM** service upon registration.
21
21
  */
22
22
  const FeatureModel = AmpState.extend({
23
-
24
23
  idAttribute: 'key', // needed by Ampersand to determine unique item
25
24
 
26
25
  // Ampersand property members.
@@ -67,7 +66,7 @@ const FeatureModel = AmpState.extend({
67
66
  *
68
67
  * @type {any}
69
68
  */
70
- value: 'any'
69
+ value: 'any',
71
70
  },
72
71
 
73
72
  /**
@@ -82,11 +81,7 @@ const FeatureModel = AmpState.extend({
82
81
  constructor(attrs, options = {}) {
83
82
  defaults(options, {parse: true});
84
83
 
85
- return Reflect.apply(
86
- AmpState.prototype.constructor,
87
- this,
88
- [attrs, options]
89
- );
84
+ return Reflect.apply(AmpState.prototype.constructor, this, [attrs, options]);
90
85
  },
91
86
 
92
87
  // Ampsersand method members.
@@ -150,7 +145,7 @@ const FeatureModel = AmpState.extend({
150
145
  // Validate that the overloaded class member returned an object with the
151
146
  // `lastModified` key-value pair and instance it as an ISO string.
152
147
  if (attrs.lastModified) {
153
- attrs.lastModified = (new Date(attrs.lastModified).toISOString());
148
+ attrs.lastModified = new Date(attrs.lastModified).toISOString();
154
149
  }
155
150
 
156
151
  return attrs;
@@ -179,8 +174,7 @@ const FeatureModel = AmpState.extend({
179
174
  if (isObject(key) || key === null) {
180
175
  attrs = key;
181
176
  optns = value;
182
- }
183
- else {
177
+ } else {
184
178
  attrs = {};
185
179
  attrs[key] = value;
186
180
  optns = options;
@@ -189,7 +183,7 @@ const FeatureModel = AmpState.extend({
189
183
  attrs = this.parse(attrs, optns);
190
184
 
191
185
  return Reflect.apply(AmpState.prototype.set, this, [attrs, optns]);
192
- }
186
+ },
193
187
  });
194
188
 
195
189
  export default FeatureModel;
@@ -14,7 +14,6 @@ import FeatureCollection from './feature-collection';
14
14
  * organize the data retrieved from the **wdm** service on device registration.
15
15
  */
16
16
  const FeaturesModel = AmpState.extend({
17
-
18
17
  // Ampersand property members.
19
18
 
20
19
  collections: {
@@ -37,7 +36,7 @@ const FeaturesModel = AmpState.extend({
37
36
  *
38
37
  * @type {FeatureCollection}
39
38
  */
40
- user: FeatureCollection
39
+ user: FeatureCollection,
41
40
  },
42
41
 
43
42
  // Helper method members.
@@ -87,16 +86,11 @@ const FeaturesModel = AmpState.extend({
87
86
  eventNames.forEach((eventName) => {
88
87
  FEATURE_COLLECTION_NAMES.forEach((collectionName) => {
89
88
  this[collectionName].on(eventName, (model, options) => {
90
- this.trigger(
91
- `change:${collectionName}`,
92
- this,
93
- this[collectionName],
94
- options
95
- );
89
+ this.trigger(`change:${collectionName}`, this, this[collectionName], options);
96
90
  });
97
91
  });
98
92
  });
99
- }
93
+ },
100
94
  });
101
95
 
102
96
  export default FeaturesModel;
@@ -2,8 +2,4 @@ import FeatureCollection from './feature-collection';
2
2
  import FeatureModel from './feature-model';
3
3
  import FeaturesModel from './features-model';
4
4
 
5
- export {
6
- FeatureCollection,
7
- FeatureModel,
8
- FeaturesModel
9
- };
5
+ export {FeatureCollection, FeatureModel, FeaturesModel};
package/src/index.js CHANGED
@@ -13,7 +13,7 @@ import config from './config';
13
13
  registerInternalPlugin('device', Device, {
14
14
  config,
15
15
  interceptors: {
16
- DeviceUrlInterceptor: DeviceUrlInterceptor.create
16
+ DeviceUrlInterceptor: DeviceUrlInterceptor.create,
17
17
  },
18
18
  /**
19
19
  * Unregister the device in the case that the webex instance has logged out.
@@ -22,16 +22,8 @@ registerInternalPlugin('device', Device, {
22
22
  */
23
23
  onBeforeLogout() {
24
24
  return this.unregister();
25
- }
25
+ },
26
26
  });
27
27
 
28
28
  export {default} from './device';
29
- export {
30
- config,
31
- constants,
32
- DeviceUrlInterceptor,
33
- FeatureCollection,
34
- FeatureModel,
35
- FeaturesModel
36
- };
37
-
29
+ export {config, constants, DeviceUrlInterceptor, FeatureCollection, FeatureModel, FeaturesModel};
@@ -8,8 +8,8 @@ import {set} from 'lodash';
8
8
  import {CISCO_DEVICE_URL} from '../constants';
9
9
 
10
10
  /**
11
- * Adds 'cisco-device-url' header, as appropriate, to requests
12
- */
11
+ * Adds 'cisco-device-url' header, as appropriate, to requests
12
+ */
13
13
  export default class DeviceUrlInterceptor extends Interceptor {
14
14
  /**
15
15
  * @returns {DeviceUrlInterceptor}
@@ -29,15 +29,13 @@ export default class DeviceUrlInterceptor extends Interceptor {
29
29
  const {device, services} = this.webex.internal;
30
30
 
31
31
  // Check if header is already set before moving forward
32
- if (
33
- !device.url ||
34
- (headers && CISCO_DEVICE_URL in headers && !!headers[CISCO_DEVICE_URL])
35
- ) {
32
+ if (!device.url || (headers && CISCO_DEVICE_URL in headers && !!headers[CISCO_DEVICE_URL])) {
36
33
  return Promise.resolve(options);
37
34
  }
38
35
 
39
36
  // Wait for catalog and service to be defined.
40
- return services.waitForService({service, url: uri})
37
+ return services
38
+ .waitForService({service, url: uri})
41
39
  .then((url) => {
42
40
  // Grab the service name with the url returned from waitForService
43
41
  const {name: serviceName} = services.getServiceFromUrl(url) || {};