@webex/internal-plugin-mercury 3.0.0-beta.4 → 3.0.0-beta.400

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/src/mercury.js CHANGED
@@ -15,16 +15,11 @@ import {
15
15
  Forbidden,
16
16
  NotAuthorized,
17
17
  UnknownResponse,
18
- ConnectionError
18
+ ConnectionError,
19
19
  // NotFound
20
20
  } from './errors';
21
21
 
22
- const normalReconnectReasons = [
23
- 'idle',
24
- 'done (forced)',
25
- 'pong not received',
26
- 'pong mismatch'
27
- ];
22
+ const normalReconnectReasons = ['idle', 'done (forced)', 'pong not received', 'pong mismatch'];
28
23
 
29
24
  const Mercury = WebexPlugin.extend({
30
25
  namespace: 'Mercury',
@@ -32,14 +27,18 @@ const Mercury = WebexPlugin.extend({
32
27
  session: {
33
28
  connected: {
34
29
  default: false,
35
- type: 'boolean'
30
+ type: 'boolean',
36
31
  },
37
32
  connecting: {
38
33
  default: false,
39
- type: 'boolean'
34
+ type: 'boolean',
35
+ },
36
+ hasEverConnected: {
37
+ default: false,
38
+ type: 'boolean',
40
39
  },
41
40
  socket: 'object',
42
- localClusterServiceUrls: 'object'
41
+ localClusterServiceUrls: 'object',
43
42
  },
44
43
 
45
44
  derived: {
@@ -47,42 +46,41 @@ const Mercury = WebexPlugin.extend({
47
46
  deps: ['connected'],
48
47
  fn() {
49
48
  return this.connected;
50
- }
51
- }
49
+ },
50
+ },
52
51
  },
53
52
 
54
53
  @oneFlight
55
54
  connect(webSocketUrl) {
56
55
  if (this.connected) {
57
- this.logger.info('mercury: already connected, will not connect again');
56
+ this.logger.info(`${this.namespace}: already connected, will not connect again`);
58
57
 
59
58
  return Promise.resolve();
60
59
  }
61
60
 
62
61
  this.connecting = true;
63
62
 
64
- return Promise.resolve(this.webex.internal.device.registered || this.webex.internal.device.register())
65
- .then(() => {
66
- this.logger.info('mercury: connecting');
63
+ return Promise.resolve(
64
+ this.webex.internal.device.registered || this.webex.internal.device.register()
65
+ ).then(() => {
66
+ this.logger.info(`${this.namespace}: connecting`);
67
67
 
68
- return this._connectWithBackoff(webSocketUrl);
69
- });
68
+ return this._connectWithBackoff(webSocketUrl);
69
+ });
70
70
  },
71
71
 
72
72
  @oneFlight
73
73
  disconnect() {
74
74
  return new Promise((resolve) => {
75
75
  if (this.backoffCall) {
76
- this.logger.info('mercury: aborting connection');
76
+ this.logger.info(`${this.namespace}: aborting connection`);
77
77
  this.backoffCall.abort();
78
78
  }
79
79
 
80
80
  if (this.socket) {
81
81
  this.socket.removeAllListeners('message');
82
82
  this.once('offline', resolve);
83
- this.socket.close();
84
-
85
- return;
83
+ resolve(this.socket.close());
86
84
  }
87
85
 
88
86
  resolve();
@@ -121,7 +119,8 @@ const Mercury = WebexPlugin.extend({
121
119
  webSocketUrl = this.webex.internal.device.webSocketUrl;
122
120
  }
123
121
 
124
- return this.webex.internal.feature.getFeature('developer', 'web-high-availability')
122
+ return this.webex.internal.feature
123
+ .getFeature('developer', 'web-high-availability')
125
124
  .then((haMessagingEnabled) => {
126
125
  if (haMessagingEnabled) {
127
126
  return this.webex.internal.services.convertUrlToPriorityHostUrl(webSocketUrl);
@@ -138,13 +137,13 @@ const Mercury = WebexPlugin.extend({
138
137
  Object.assign(webSocketUrl.query, {
139
138
  outboundWireFormat: 'text',
140
139
  bufferStates: true,
141
- aliasHttpStatus: true
140
+ aliasHttpStatus: true,
142
141
  });
143
142
 
144
143
  if (webSharedMercury) {
145
144
  Object.assign(webSocketUrl.query, {
146
145
  mercuryRegistrationStatus: true,
147
- isRegistrationRefreshEnabled: true
146
+ isRegistrationRefreshEnabled: true,
148
147
  });
149
148
  Reflect.deleteProperty(webSocketUrl.query, 'bufferStates');
150
149
  }
@@ -164,11 +163,12 @@ const Mercury = WebexPlugin.extend({
164
163
  socket.on('close', (...args) => this._onclose(...args));
165
164
  socket.on('message', (...args) => this._onmessage(...args));
166
165
  socket.on('sequence-mismatch', (...args) => this._emit('sequence-mismatch', ...args));
166
+ socket.on('ping-pong-latency', (...args) => this._emit('ping-pong-latency', ...args));
167
167
 
168
168
  Promise.all([this._prepareUrl(socketUrl), this.webex.credentials.getUserToken()])
169
169
  .then(([webSocketUrl, token]) => {
170
170
  if (!this.backoffCall) {
171
- const msg = 'mercury: prevent socket open when backoffCall no longer defined';
171
+ const msg = `${this.namespace}: prevent socket open when backoffCall no longer defined`;
172
172
 
173
173
  this.logger.info(msg);
174
174
 
@@ -183,12 +183,12 @@ const Mercury = WebexPlugin.extend({
183
183
  pongTimeout: this.config.pongTimeout,
184
184
  token: token.toString(),
185
185
  trackingId: `${this.webex.sessionId}_${Date.now()}`,
186
- logger: this.logger
186
+ logger: this.logger,
187
187
  };
188
188
 
189
189
  // if the consumer has supplied request options use them
190
190
  if (this.webex.config.defaultMercuryOptions) {
191
- this.logger.info('mercury: setting custom options');
191
+ this.logger.info(`${this.namespace}: setting custom options`);
192
192
  options = {...options, ...this.webex.config.defaultMercuryOptions};
193
193
  }
194
194
 
@@ -196,21 +196,18 @@ const Mercury = WebexPlugin.extend({
196
196
  // the socket if it is in the process of being opened.
197
197
  this.socket = socket;
198
198
 
199
+ this.logger.info(`${this.namespace} connection url: ${webSocketUrl}`);
200
+
199
201
  return socket.open(webSocketUrl, options);
200
202
  })
201
203
  .then(() => {
202
- this.webex.internal.metrics.submitClientMetrics('web-ha-mercury', {
203
- fields: {
204
- success: true
205
- },
206
- tags: {
207
- action: 'connected',
208
- url: attemptWSUrl
209
- }
210
- });
204
+ this.logger.info(
205
+ `${this.namespace}: connected to mercury, success, action: connected, url: ${attemptWSUrl}`
206
+ );
211
207
  callback();
212
208
 
213
- return this.webex.internal.feature.getFeature('developer', 'web-high-availability')
209
+ return this.webex.internal.feature
210
+ .getFeature('developer', 'web-high-availability')
214
211
  .then((haMessagingEnabled) => {
215
212
  if (haMessagingEnabled) {
216
213
  return this.webex.internal.device.refresh();
@@ -227,21 +224,21 @@ const Mercury = WebexPlugin.extend({
227
224
  if (reason.code !== 1006 && this.backoffCall && this.backoffCall.getNumRetries() > 0) {
228
225
  this._emit('connection_failed', reason, {retries: this.backoffCall.getNumRetries()});
229
226
  }
230
- this.logger.info('mercury: connection attempt failed', reason);
227
+ this.logger.info(`${this.namespace}: connection attempt failed`, reason);
231
228
  // UnknownResponse is produced by IE for any 4XXX; treated it like a bad
232
229
  // web socket url and let WDM handle the token checking
233
230
  if (reason instanceof UnknownResponse) {
234
- this.logger.info('mercury: received unknown response code, refreshing device registration');
231
+ this.logger.info(
232
+ `${this.namespace}: received unknown response code, refreshing device registration`
233
+ );
235
234
 
236
- return this.webex.internal.device.refresh()
237
- .then(() => callback(reason));
235
+ return this.webex.internal.device.refresh().then(() => callback(reason));
238
236
  }
239
237
  // NotAuthorized implies expired token
240
238
  if (reason instanceof NotAuthorized) {
241
- this.logger.info('mercury: received authorization error, reauthorizing');
239
+ this.logger.info(`${this.namespace}: received authorization error, reauthorizing`);
242
240
 
243
- return this.webex.credentials.refresh({force: true})
244
- .then(() => callback(reason));
241
+ return this.webex.credentials.refresh({force: true}).then(() => callback(reason));
245
242
  }
246
243
  // // NotFound implies expired web socket url
247
244
  // else if (reason instanceof NotFound) {
@@ -252,26 +249,19 @@ const Mercury = WebexPlugin.extend({
252
249
  // BadRequest implies current credentials are for a Service Account
253
250
  // Forbidden implies current user is not entitle for Webex
254
251
  if (reason instanceof BadRequest || reason instanceof Forbidden) {
255
- this.logger.warn('mercury: received unrecoverable response from mercury');
252
+ this.logger.warn(`${this.namespace}: received unrecoverable response from mercury`);
256
253
  this.backoffCall.abort();
257
254
 
258
255
  return callback(reason);
259
256
  }
260
257
  if (reason instanceof ConnectionError) {
261
- return this.webex.internal.feature.getFeature('developer', 'web-high-availability')
258
+ return this.webex.internal.feature
259
+ .getFeature('developer', 'web-high-availability')
262
260
  .then((haMessagingEnabled) => {
263
261
  if (haMessagingEnabled) {
264
- this.logger.info('mercury: received a generic connection error, will try to connect to another datacenter');
265
- this.webex.internal.metrics.submitClientMetrics('web-ha-mercury', {
266
- fields: {
267
- success: false
268
- },
269
- tags: {
270
- action: 'failed',
271
- error: reason.message,
272
- url: attemptWSUrl
273
- }
274
- });
262
+ this.logger.info(
263
+ `${this.namespace}: received a generic connection error, will try to connect to another datacenter. failed, action: 'failed', url: ${attemptWSUrl} error: ${reason.message}`
264
+ );
275
265
 
276
266
  return this.webex.internal.services.markFailedUrl(attemptWSUrl);
277
267
  }
@@ -284,7 +274,7 @@ const Mercury = WebexPlugin.extend({
284
274
  return callback(reason);
285
275
  })
286
276
  .catch((reason) => {
287
- this.logger.error('mercury: failed to handle connection failure', reason);
277
+ this.logger.error(`${this.namespace}: failed to handle connection failure`, reason);
288
278
  callback(reason);
289
279
  });
290
280
  },
@@ -299,11 +289,16 @@ const Mercury = WebexPlugin.extend({
299
289
 
300
290
  this.backoffCall = undefined;
301
291
  if (err) {
302
- this.logger.info(`mercury: failed to connect after ${call.getNumRetries()} retries; log statement about next retry was inaccurate; ${err}`);
292
+ this.logger.info(
293
+ `${
294
+ this.namespace
295
+ }: failed to connect after ${call.getNumRetries()} retries; log statement about next retry was inaccurate; ${err}`
296
+ );
303
297
 
304
298
  return reject(err);
305
299
  }
306
300
  this.connected = true;
301
+ this.hasEverConnected = true;
307
302
  this._emit('online');
308
303
 
309
304
  return resolve();
@@ -311,21 +306,25 @@ const Mercury = WebexPlugin.extend({
311
306
 
312
307
  // eslint-disable-next-line prefer-reflect
313
308
  call = backoff.call((callback) => {
314
- this.logger.info(`mercury: executing connection attempt ${call.getNumRetries()}`);
309
+ this.logger.info(`${this.namespace}: executing connection attempt ${call.getNumRetries()}`);
315
310
  this._attemptConnection(webSocketUrl, callback);
316
311
  }, onComplete);
317
312
 
318
- call.setStrategy(new backoff.ExponentialStrategy({
319
- initialDelay: this.config.backoffTimeReset,
320
- maxDelay: this.config.backoffTimeMax
321
- }));
313
+ call.setStrategy(
314
+ new backoff.ExponentialStrategy({
315
+ initialDelay: this.config.backoffTimeReset,
316
+ maxDelay: this.config.backoffTimeMax,
317
+ })
318
+ );
322
319
 
323
- if (this.config.maxRetries) {
320
+ if (this.config.initialConnectionMaxRetries && !this.hasEverConnected) {
321
+ call.failAfter(this.config.initialConnectionMaxRetries);
322
+ } else if (this.config.maxRetries) {
324
323
  call.failAfter(this.config.maxRetries);
325
324
  }
326
325
 
327
326
  call.on('abort', () => {
328
- this.logger.info('mercury: connection aborted');
327
+ this.logger.info(`${this.namespace}: connection aborted`);
329
328
  reject(new Error('Mercury Connection Aborted'));
330
329
  });
331
330
 
@@ -334,15 +333,17 @@ const Mercury = WebexPlugin.extend({
334
333
  const number = call.getNumRetries();
335
334
  const delay = Math.min(call.strategy_.nextBackoffDelay_, this.config.backoffTimeMax);
336
335
 
337
- this.logger.info(`mercury: failed to connect; attempting retry ${number + 1} in ${delay} ms`);
336
+ this.logger.info(
337
+ `${this.namespace}: failed to connect; attempting retry ${number + 1} in ${delay} ms`
338
+ );
338
339
  /* istanbul ignore if */
339
340
  if (process.env.NODE_ENV === 'development') {
340
- this.logger.debug('mercury: ', err, err.stack);
341
+ this.logger.debug(`${this.namespace}: `, err, err.stack);
341
342
  }
342
343
 
343
344
  return;
344
345
  }
345
- this.logger.info('mercury: connected');
346
+ this.logger.info(`${this.namespace}: connected`);
346
347
  });
347
348
 
348
349
  call.start();
@@ -354,9 +355,11 @@ const Mercury = WebexPlugin.extend({
354
355
  _emit(...args) {
355
356
  try {
356
357
  this.trigger(...args);
357
- }
358
- catch (error) {
359
- this.logger.error('mercury: error occurred in event handler', error);
358
+ } catch (error) {
359
+ this.logger.error(`${this.namespace}: error occurred in event handler`, {
360
+ error,
361
+ arguments: args,
362
+ });
360
363
  }
361
364
  },
362
365
 
@@ -373,7 +376,7 @@ const Mercury = WebexPlugin.extend({
373
376
  if ((this.webex[namespace] || this.webex.internal[namespace])[handlerName]) {
374
377
  handlers.push({
375
378
  name: handlerName,
376
- namespace
379
+ namespace,
377
380
  });
378
381
  }
379
382
 
@@ -395,20 +398,22 @@ const Mercury = WebexPlugin.extend({
395
398
 
396
399
  switch (event.code) {
397
400
  case 1003:
398
- // metric: disconnect
399
- this.logger.info(`mercury: Mercury service rejected last message; will not reconnect: ${event.reason}`);
401
+ // metric: disconnect
402
+ this.logger.info(
403
+ `${this.namespace}: Mercury service rejected last message; will not reconnect: ${event.reason}`
404
+ );
400
405
  this._emit('offline.permanent', event);
401
406
  break;
402
407
  case 4000:
403
408
  // metric: disconnect
404
- this.logger.info('mercury: socket replaced; will not reconnect');
409
+ this.logger.info(`${this.namespace}: socket replaced; will not reconnect`);
405
410
  this._emit('offline.replaced', event);
406
411
  break;
407
412
  case 1001:
408
413
  case 1005:
409
414
  case 1006:
410
415
  case 1011:
411
- this.logger.info('mercury: socket disconnected; reconnecting');
416
+ this.logger.info(`${this.namespace}: socket disconnected; reconnecting`);
412
417
  this._emit('offline.transient', event);
413
418
  this._reconnect(socketUrl);
414
419
  // metric: disconnect
@@ -416,25 +421,25 @@ const Mercury = WebexPlugin.extend({
416
421
  break;
417
422
  case 1000:
418
423
  if (normalReconnectReasons.includes(reason)) {
419
- this.logger.info('mercury: socket disconnected; reconnecting');
424
+ this.logger.info(`${this.namespace}: socket disconnected; reconnecting`);
420
425
  this._emit('offline.transient', event);
421
426
  this._reconnect(socketUrl);
422
427
  // metric: disconnect
423
428
  // if (reason === done forced) metric: force closure
424
- }
425
- else {
426
- this.logger.info('mercury: socket disconnected; will not reconnect');
429
+ } else {
430
+ this.logger.info(`${this.namespace}: socket disconnected; will not reconnect`);
427
431
  this._emit('offline.permanent', event);
428
432
  }
429
433
  break;
430
434
  default:
431
- this.logger.info('mercury: socket disconnected unexpectedly; will not reconnect');
435
+ this.logger.info(
436
+ `${this.namespace}: socket disconnected unexpectedly; will not reconnect`
437
+ );
432
438
  // unexpected disconnect
433
439
  this._emit('offline.permanent', event);
434
440
  }
435
- }
436
- catch (error) {
437
- this.logger.error('mercury: error occurred in close handler', error);
441
+ } catch (error) {
442
+ this.logger.error(`${this.namespace}: error occurred in close handler`, error);
438
443
  }
439
444
  },
440
445
 
@@ -442,7 +447,7 @@ const Mercury = WebexPlugin.extend({
442
447
  const envelope = event.data;
443
448
 
444
449
  if (process.env.ENABLE_MERCURY_LOGGING) {
445
- this.logger.debug('mercury: message envelope: ', envelope);
450
+ this.logger.debug(`${this.namespace}: message envelope: `, envelope);
446
451
  }
447
452
 
448
453
  const {data} = envelope;
@@ -450,34 +455,43 @@ const Mercury = WebexPlugin.extend({
450
455
  this._applyOverrides(data);
451
456
 
452
457
  return this._getEventHandlers(data.eventType)
453
- .reduce((promise, handler) => promise.then(() => {
454
- const {namespace, name} = handler;
455
-
456
- return new Promise((resolve) => resolve((this.webex[namespace] || this.webex.internal[namespace])[name](data)))
457
- .catch((reason) => this.logger.error(`mercury: error occurred in autowired event handler for ${data.eventType}`, reason));
458
- }), Promise.resolve())
458
+ .reduce(
459
+ (promise, handler) =>
460
+ promise.then(() => {
461
+ const {namespace, name} = handler;
462
+
463
+ return new Promise((resolve) =>
464
+ resolve((this.webex[namespace] || this.webex.internal[namespace])[name](data))
465
+ ).catch((reason) =>
466
+ this.logger.error(
467
+ `${this.namespace}: error occurred in autowired event handler for ${data.eventType}`,
468
+ reason
469
+ )
470
+ );
471
+ }),
472
+ Promise.resolve()
473
+ )
459
474
  .then(() => {
460
475
  this._emit('event', event.data);
461
476
  const [namespace] = data.eventType.split('.');
462
477
 
463
478
  if (namespace === data.eventType) {
464
479
  this._emit(`event:${namespace}`, envelope);
465
- }
466
- else {
480
+ } else {
467
481
  this._emit(`event:${namespace}`, envelope);
468
482
  this._emit(`event:${data.eventType}`, envelope);
469
483
  }
470
484
  })
471
485
  .catch((reason) => {
472
- this.logger.error('mercury: error occurred processing socket message', reason);
486
+ this.logger.error(`${this.namespace}: error occurred processing socket message`, reason);
473
487
  });
474
488
  },
475
489
 
476
490
  _reconnect(webSocketUrl) {
477
- this.logger.info('mercury: reconnecting');
491
+ this.logger.info(`${this.namespace}: reconnecting`);
478
492
 
479
493
  return this.connect(webSocketUrl);
480
- }
494
+ },
481
495
  });
482
496
 
483
497
  export default Mercury;