@webex/plugin-meetings 3.3.1-next.3 → 3.3.1-next.31

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 (108) hide show
  1. package/dist/breakouts/breakout.js +1 -1
  2. package/dist/breakouts/index.js +7 -2
  3. package/dist/breakouts/index.js.map +1 -1
  4. package/dist/constants.js +12 -2
  5. package/dist/constants.js.map +1 -1
  6. package/dist/interpretation/index.js +1 -1
  7. package/dist/interpretation/siLanguage.js +1 -1
  8. package/dist/media/MediaConnectionAwaiter.js +68 -13
  9. package/dist/media/MediaConnectionAwaiter.js.map +1 -1
  10. package/dist/mediaQualityMetrics/config.js +16 -6
  11. package/dist/mediaQualityMetrics/config.js.map +1 -1
  12. package/dist/meeting/connectionStateHandler.js +67 -0
  13. package/dist/meeting/connectionStateHandler.js.map +1 -0
  14. package/dist/meeting/index.js +242 -133
  15. package/dist/meeting/index.js.map +1 -1
  16. package/dist/meeting/locusMediaRequest.js +7 -0
  17. package/dist/meeting/locusMediaRequest.js.map +1 -1
  18. package/dist/meeting/util.js +1 -0
  19. package/dist/meeting/util.js.map +1 -1
  20. package/dist/meeting-info/index.js +4 -4
  21. package/dist/meeting-info/index.js.map +1 -1
  22. package/dist/meeting-info/meeting-info-v2.js +2 -2
  23. package/dist/meeting-info/meeting-info-v2.js.map +1 -1
  24. package/dist/meeting-info/util.js +17 -17
  25. package/dist/meeting-info/util.js.map +1 -1
  26. package/dist/meeting-info/utilv2.js +16 -16
  27. package/dist/meeting-info/utilv2.js.map +1 -1
  28. package/dist/meetings/collection.js +1 -1
  29. package/dist/meetings/collection.js.map +1 -1
  30. package/dist/meetings/index.js +37 -33
  31. package/dist/meetings/index.js.map +1 -1
  32. package/dist/meetings/meetings.types.js +8 -0
  33. package/dist/meetings/meetings.types.js.map +1 -1
  34. package/dist/meetings/util.js +3 -2
  35. package/dist/meetings/util.js.map +1 -1
  36. package/dist/metrics/constants.js +2 -1
  37. package/dist/metrics/constants.js.map +1 -1
  38. package/dist/metrics/index.js +57 -0
  39. package/dist/metrics/index.js.map +1 -1
  40. package/dist/personal-meeting-room/index.js +1 -1
  41. package/dist/personal-meeting-room/index.js.map +1 -1
  42. package/dist/reachability/clusterReachability.js +108 -53
  43. package/dist/reachability/clusterReachability.js.map +1 -1
  44. package/dist/reachability/index.js +415 -56
  45. package/dist/reachability/index.js.map +1 -1
  46. package/dist/statsAnalyzer/index.js +81 -27
  47. package/dist/statsAnalyzer/index.js.map +1 -1
  48. package/dist/statsAnalyzer/mqaUtil.js +36 -10
  49. package/dist/statsAnalyzer/mqaUtil.js.map +1 -1
  50. package/dist/types/constants.d.ts +11 -0
  51. package/dist/types/media/MediaConnectionAwaiter.d.ts +24 -4
  52. package/dist/types/mediaQualityMetrics/config.d.ts +11 -0
  53. package/dist/types/meeting/connectionStateHandler.d.ts +30 -0
  54. package/dist/types/meeting/index.d.ts +13 -3
  55. package/dist/types/meeting/locusMediaRequest.d.ts +2 -0
  56. package/dist/types/meeting-info/index.d.ts +3 -2
  57. package/dist/types/meeting-info/meeting-info-v2.d.ts +3 -2
  58. package/dist/types/meeting-info/util.d.ts +5 -4
  59. package/dist/types/meeting-info/utilv2.d.ts +3 -2
  60. package/dist/types/meetings/collection.d.ts +3 -2
  61. package/dist/types/meetings/index.d.ts +4 -3
  62. package/dist/types/meetings/meetings.types.d.ts +9 -0
  63. package/dist/types/metrics/constants.d.ts +1 -0
  64. package/dist/types/metrics/index.d.ts +15 -0
  65. package/dist/types/reachability/clusterReachability.d.ts +31 -3
  66. package/dist/types/reachability/index.d.ts +93 -2
  67. package/dist/types/statsAnalyzer/index.d.ts +15 -6
  68. package/dist/types/statsAnalyzer/mqaUtil.d.ts +17 -4
  69. package/dist/webinar/index.js +1 -1
  70. package/package.json +23 -23
  71. package/src/breakouts/index.ts +7 -1
  72. package/src/constants.ts +13 -0
  73. package/src/media/MediaConnectionAwaiter.ts +86 -11
  74. package/src/mediaQualityMetrics/config.ts +14 -3
  75. package/src/meeting/connectionStateHandler.ts +65 -0
  76. package/src/meeting/index.ts +166 -63
  77. package/src/meeting/locusMediaRequest.ts +5 -0
  78. package/src/meeting/util.ts +1 -0
  79. package/src/meeting-info/index.ts +9 -6
  80. package/src/meeting-info/meeting-info-v2.ts +4 -4
  81. package/src/meeting-info/util.ts +23 -28
  82. package/src/meeting-info/utilv2.ts +18 -24
  83. package/src/meetings/collection.ts +3 -3
  84. package/src/meetings/index.ts +39 -40
  85. package/src/meetings/meetings.types.ts +11 -0
  86. package/src/meetings/util.ts +5 -4
  87. package/src/metrics/constants.ts +1 -0
  88. package/src/metrics/index.ts +44 -0
  89. package/src/personal-meeting-room/index.ts +2 -2
  90. package/src/reachability/clusterReachability.ts +86 -25
  91. package/src/reachability/index.ts +316 -27
  92. package/src/statsAnalyzer/index.ts +85 -24
  93. package/src/statsAnalyzer/mqaUtil.ts +55 -7
  94. package/test/unit/spec/breakouts/index.ts +51 -32
  95. package/test/unit/spec/media/MediaConnectionAwaiter.ts +131 -32
  96. package/test/unit/spec/meeting/connectionStateHandler.ts +102 -0
  97. package/test/unit/spec/meeting/index.js +323 -56
  98. package/test/unit/spec/meeting/locusMediaRequest.ts +7 -0
  99. package/test/unit/spec/meeting-info/index.js +4 -4
  100. package/test/unit/spec/meeting-info/meetinginfov2.js +24 -28
  101. package/test/unit/spec/meeting-info/request.js +2 -2
  102. package/test/unit/spec/meeting-info/utilv2.js +41 -49
  103. package/test/unit/spec/meetings/index.js +14 -0
  104. package/test/unit/spec/metrics/index.js +126 -0
  105. package/test/unit/spec/personal-meeting-room/personal-meeting-room.js +2 -2
  106. package/test/unit/spec/reachability/clusterReachability.ts +116 -22
  107. package/test/unit/spec/reachability/index.ts +1153 -84
  108. package/test/unit/spec/stats-analyzer/index.js +647 -319
@@ -4,15 +4,9 @@ import url from 'url';
4
4
  import {deconstructHydraId} from '@webex/common';
5
5
 
6
6
  import {
7
- _SIP_URI_,
8
- _PERSONAL_ROOM_,
9
- _MEETING_ID_,
10
- _CONVERSATION_URL_,
11
- _LOCUS_ID_,
12
- _MEETING_LINK_,
7
+ DESTINATION_TYPE,
13
8
  _PEOPLE_,
14
9
  _ROOM_,
15
- _MEETING_UUID_,
16
10
  DIALER_REGEX,
17
11
  WEBEX_DOT_COM,
18
12
  CONVERSATION_SERVICE,
@@ -144,7 +138,7 @@ export default class MeetingInfoUtil {
144
138
  const {type, webex} = from;
145
139
  let {destination} = from;
146
140
 
147
- if (type === _PERSONAL_ROOM_) {
141
+ if (type === DESTINATION_TYPE.PERSONAL_ROOM) {
148
142
  // this case checks if your type is personal room
149
143
  if (!destination) {
150
144
  // if we are not getting anything in desination we fetch org and user ids from webex instance
@@ -185,19 +179,19 @@ export default class MeetingInfoUtil {
185
179
  'Meeting-info:util#generateOptions --> WARN, use of Meeting Link is deprecated, please use a SIP URI instead'
186
180
  );
187
181
 
188
- options.type = _MEETING_LINK_;
182
+ options.type = DESTINATION_TYPE.MEETING_LINK;
189
183
  options.destination = destination;
190
184
  } else if (this.isSipUri(destination)) {
191
- options.type = _SIP_URI_;
185
+ options.type = DESTINATION_TYPE.SIP_URI;
192
186
  options.destination = destination;
193
187
  } else if (this.isPhoneNumber(destination)) {
194
- options.type = _SIP_URI_;
188
+ options.type = DESTINATION_TYPE.SIP_URI;
195
189
  options.destination = destination;
196
190
  } else if (this.isConversationUrl(destination, webex)) {
197
- options.type = _CONVERSATION_URL_;
191
+ options.type = DESTINATION_TYPE.CONVERSATION_URL;
198
192
  options.destination = destination;
199
193
  } else if (hydraId && hydraId.people) {
200
- options.type = _SIP_URI_;
194
+ options.type = DESTINATION_TYPE.SIP_URI;
201
195
 
202
196
  return this.getSipUriFromHydraPersonId(hydraId && hydraId.destination, webex).then((res) => {
203
197
  options.destination = res;
@@ -226,12 +220,12 @@ export default class MeetingInfoUtil {
226
220
  /**
227
221
  * Helper function to build up a correct locus url depending on the value passed
228
222
  * @param {Object} options type and value to fetch meeting info
229
- * @param {String} options.type One of [SIP_URI, PERSONAL_ROOM, MEETING_ID, CONVERSATION_URL, LOCUS_ID, MEETING_LINK]
223
+ * @param {DESTINATION_TYPE} options.type One of [SIP_URI, PERSONAL_ROOM, MEETING_ID, CONVERSATION_URL, LOCUS_ID, MEETING_LINK]
230
224
  * @param {String} options.installedOrgID org ID of user's machine
231
225
  * @param {Object} options.destination ?? value.value
232
226
  * @returns {Object} returns an object with {resource, method}
233
227
  */
234
- static getRequestBody(options: {type: string; destination: object} | any) {
228
+ static getRequestBody(options: {type: DESTINATION_TYPE; destination: object} | any) {
235
229
  const {type, destination, password, captchaInfo, installedOrgID, locusId, extraParams} =
236
230
  options;
237
231
  const body: any = {
@@ -240,20 +234,20 @@ export default class MeetingInfoUtil {
240
234
  };
241
235
 
242
236
  switch (type) {
243
- case _SIP_URI_:
237
+ case DESTINATION_TYPE.SIP_URI:
244
238
  body.sipUrl = destination;
245
239
  break;
246
- case _PERSONAL_ROOM_:
240
+ case DESTINATION_TYPE.PERSONAL_ROOM:
247
241
  body.userId = destination.userId;
248
242
  body.orgId = destination.orgId;
249
243
  break;
250
- case _MEETING_ID_:
244
+ case DESTINATION_TYPE.MEETING_ID:
251
245
  body.meetingKey = destination;
252
246
  break;
253
- case _CONVERSATION_URL_:
247
+ case DESTINATION_TYPE.CONVERSATION_URL:
254
248
  body.conversationUrl = destination;
255
249
  break;
256
- case _LOCUS_ID_:
250
+ case DESTINATION_TYPE.LOCUS_ID:
257
251
  // use meetingID for the completer meeting info for the already started meeting
258
252
  if (destination.info?.webExMeetingId) {
259
253
  body.meetingKey = destination.info.webExMeetingId;
@@ -261,10 +255,10 @@ export default class MeetingInfoUtil {
261
255
  body.sipUrl = destination.info.sipUri;
262
256
  }
263
257
  break;
264
- case _MEETING_LINK_:
258
+ case DESTINATION_TYPE.MEETING_LINK:
265
259
  body.meetingUrl = destination;
266
260
  break;
267
- case _MEETING_UUID_: {
261
+ case DESTINATION_TYPE.MEETING_UUID: {
268
262
  body.meetingUUID = destination;
269
263
  break;
270
264
  }
@@ -317,10 +311,10 @@ export default class MeetingInfoUtil {
317
311
  let preferredWebexSite = null;
318
312
 
319
313
  switch (type) {
320
- case _SIP_URI_:
314
+ case DESTINATION_TYPE.SIP_URI:
321
315
  preferredWebexSite = this.getWebexSite(destination);
322
316
  break;
323
- case _LOCUS_ID_:
317
+ case DESTINATION_TYPE.LOCUS_ID:
324
318
  preferredWebexSite = destination.info?.webExSite;
325
319
  break;
326
320
  default:
@@ -1,7 +1,7 @@
1
1
  import {find} from 'lodash';
2
2
 
3
3
  import Collection from '../common/collection';
4
-
4
+ import {MEETING_KEY} from './meetings.types';
5
5
  /**
6
6
  * @export
7
7
  * @class MeetingCollection
@@ -26,13 +26,13 @@ export default class MeetingCollection extends Collection {
26
26
 
27
27
  /**
28
28
  * get a specific meeting searching for key
29
- * @param {String} key
29
+ * @param {MEETING_KEY} key
30
30
  * @param {Any} value
31
31
  * @returns {Meeting} if found, else returns null
32
32
  * @public
33
33
  * @memberof MeetingCollection
34
34
  */
35
- public getByKey(key: string, value: any) {
35
+ public getByKey(key: MEETING_KEY, value: any) {
36
36
  if (key && value) {
37
37
  // @ts-ignore
38
38
  return find(this.meetings, (meeting) => meeting[key] === value);
@@ -25,28 +25,22 @@ import {
25
25
  EVENT_TRIGGERS,
26
26
  READY,
27
27
  LOCUSEVENT,
28
- LOCUS_URL,
29
28
  MAX_RANDOM_DELAY_FOR_MEETING_INFO,
30
29
  ROAP,
31
30
  ONLINE,
32
31
  OFFLINE,
33
32
  _MEETING_,
34
33
  _JOIN_,
35
- _LOCUS_ID_,
36
34
  _INCOMING_,
37
35
  LOCUS,
38
- CORRELATION_ID,
39
- SIP_URI,
40
36
  _LEFT_,
41
37
  _ID_,
42
38
  MEETING_REMOVED_REASON,
43
- _CONVERSATION_URL_,
44
- CONVERSATION_URL,
45
- MEETINGNUMBER,
46
39
  _JOINED_,
47
40
  _MOVED_,
48
41
  _ON_HOLD_LOBBY_,
49
42
  _WAIT_,
43
+ DESTINATION_TYPE,
50
44
  } from '../constants';
51
45
  import BEHAVIORAL_METRICS from '../metrics/constants';
52
46
  import MeetingInfo from '../meeting-info';
@@ -57,11 +51,10 @@ import Reachability from '../reachability';
57
51
  import Request from './request';
58
52
  import PasswordError from '../common/errors/password-error';
59
53
  import CaptchaError from '../common/errors/captcha-error';
60
-
61
54
  import MeetingCollection from './collection';
55
+ import {MEETING_KEY, INoiseReductionEffect, IVirtualBackgroundEffect} from './meetings.types';
62
56
  import MeetingsUtil from './util';
63
57
  import PermissionError from '../common/errors/permission';
64
- import {INoiseReductionEffect, IVirtualBackgroundEffect} from './meetings.types';
65
58
  import {SpaceIDDeprecatedError} from '../common/errors/webex-errors';
66
59
  import NoMeetingInfoError from '../common/errors/no-meeting-info';
67
60
 
@@ -380,23 +373,26 @@ export default class Meetings extends WebexPlugin {
380
373
  // Either the locus
381
374
  // TODO : Add check for the callBack Address
382
375
  return (
383
- this.meetingCollection.getByKey(LOCUS_URL, data.locusUrl) ||
376
+ this.meetingCollection.getByKey(MEETING_KEY.LOCUS_URL, data.locusUrl) ||
384
377
  // @ts-ignore
385
378
  this.meetingCollection.getByKey(
386
- CORRELATION_ID,
379
+ MEETING_KEY.CORRELATION_ID,
387
380
  // @ts-ignore
388
381
  MeetingsUtil.checkForCorrelationId(this.webex.internal.device.url, data.locus)
389
382
  ) ||
390
383
  this.meetingCollection.getByKey(
391
- SIP_URI,
384
+ MEETING_KEY.SIP_URI,
392
385
  data.locus.self &&
393
386
  data.locus.self.callbackInfo &&
394
387
  data.locus.self.callbackInfo.callbackAddress
395
388
  ) ||
396
389
  (data.locus.info?.isUnifiedSpaceMeeting
397
390
  ? undefined
398
- : this.meetingCollection.getByKey(CONVERSATION_URL, data.locus.conversationUrl)) ||
399
- this.meetingCollection.getByKey(MEETINGNUMBER, data.locus?.info?.webExMeetingId)
391
+ : this.meetingCollection.getByKey(
392
+ MEETING_KEY.CONVERSATION_URL,
393
+ data.locus.conversationUrl
394
+ )) ||
395
+ this.meetingCollection.getByKey(MEETING_KEY.MEETINGNUMBER, data.locus?.info?.webExMeetingId)
400
396
  );
401
397
  }
402
398
 
@@ -420,7 +416,7 @@ export default class Meetings extends WebexPlugin {
420
416
  if (!meeting && data.locus?.replaces?.length > 0) {
421
417
  // Always the last element in the replace is the active one
422
418
  meeting = this.meetingCollection.getByKey(
423
- LOCUS_URL,
419
+ MEETING_KEY.LOCUS_URL,
424
420
  data.locus.replaces[data.locus.replaces.length - 1].locusUrl
425
421
  );
426
422
  }
@@ -485,7 +481,7 @@ export default class Meetings extends WebexPlugin {
485
481
  return;
486
482
  }
487
483
 
488
- this.create(data.locus, _LOCUS_ID_, useRandomDelayForInfo)
484
+ this.create(data.locus, DESTINATION_TYPE.LOCUS_ID, useRandomDelayForInfo)
489
485
  .then((newMeeting) => {
490
486
  meeting = newMeeting;
491
487
 
@@ -1076,7 +1072,7 @@ export default class Meetings extends WebexPlugin {
1076
1072
  * When meeting info passed it should be complete, e.g.: fetched after password or captcha provided
1077
1073
  *
1078
1074
  * @param {string} destination - sipURL, phonenumber, or locus object}
1079
- * @param {string} [type] - the optional specified type, such as locusId
1075
+ * @param {DESTINATION_TYPE} [type] - the optional specified type, such as locusId
1080
1076
  * @param {Boolean} useRandomDelayForInfo - whether a random delay should be added to fetching meeting info
1081
1077
  * @param {Object} infoExtraParams extra parameters to be provided when fetching meeting info
1082
1078
  * @param {string} correlationId - the optional specified correlationId (callStateForMetrics.correlationId can be provided instead)
@@ -1090,7 +1086,7 @@ export default class Meetings extends WebexPlugin {
1090
1086
  */
1091
1087
  public create(
1092
1088
  destination: string,
1093
- type: string = null,
1089
+ type: DESTINATION_TYPE = null,
1094
1090
  useRandomDelayForInfo = false,
1095
1091
  infoExtraParams = {},
1096
1092
  correlationId: string = undefined,
@@ -1099,12 +1095,11 @@ export default class Meetings extends WebexPlugin {
1099
1095
  meetingInfo = undefined,
1100
1096
  meetingLookupUrl = undefined
1101
1097
  ) {
1102
- // TODO: type should be from a dictionary
1103
-
1104
1098
  // Validate meeting information based on the provided destination and
1105
1099
  // type. This must be performed prior to determining if the meeting is
1106
1100
  // found in the collection, as we mutate the destination for hydra person
1107
1101
  // id values.
1102
+
1108
1103
  if (correlationId) {
1109
1104
  callStateForMetrics = {...(callStateForMetrics || {}), correlationId};
1110
1105
  }
@@ -1128,8 +1123,14 @@ export default class Meetings extends WebexPlugin {
1128
1123
  // check for the conversation URL then sip Url
1129
1124
  let meeting = null;
1130
1125
 
1131
- if (type === _CONVERSATION_URL_ || options.type === _CONVERSATION_URL_) {
1132
- const foundMeeting = this.meetingCollection.getByKey(CONVERSATION_URL, targetDest);
1126
+ if (
1127
+ type === DESTINATION_TYPE.CONVERSATION_URL ||
1128
+ options.type === DESTINATION_TYPE.CONVERSATION_URL
1129
+ ) {
1130
+ const foundMeeting = this.meetingCollection.getByKey(
1131
+ MEETING_KEY.CONVERSATION_URL,
1132
+ targetDest
1133
+ );
1133
1134
 
1134
1135
  if (foundMeeting) {
1135
1136
  const foundMeetingIsNotCalendarMeeting = !foundMeeting.locusInfo.scheduledMeeting;
@@ -1144,7 +1145,7 @@ export default class Meetings extends WebexPlugin {
1144
1145
 
1145
1146
  // Attempt to collect the meeting if it exists.
1146
1147
  if (!meeting) {
1147
- meeting = this.meetingCollection.getByKey(SIP_URI, targetDest);
1148
+ meeting = this.meetingCollection.getByKey(MEETING_KEY.SIP_URI, targetDest);
1148
1149
  }
1149
1150
 
1150
1151
  // Validate if a meeting was found.
@@ -1218,7 +1219,7 @@ export default class Meetings extends WebexPlugin {
1218
1219
  * When meeting info passed it should be complete, e.g.: fetched after password or captcha provided
1219
1220
  *
1220
1221
  * @param {String} destination see create()
1221
- * @param {String} type see create()
1222
+ * @param {DESTINATION_TYPE} type see create()
1222
1223
  * @param {Boolean} useRandomDelayForInfo whether a random delay should be added to fetching meeting info
1223
1224
  * @param {Object} infoExtraParams extra parameters to be provided when fetching meeting info
1224
1225
  * @param {CallStateForMetrics} callStateForMetrics - information about call state for metrics
@@ -1231,7 +1232,7 @@ export default class Meetings extends WebexPlugin {
1231
1232
  */
1232
1233
  private async createMeeting(
1233
1234
  destination: any,
1234
- type: string = null,
1235
+ type: DESTINATION_TYPE = null,
1235
1236
  useRandomDelayForInfo = false,
1236
1237
  infoExtraParams = {},
1237
1238
  callStateForMetrics: CallStateForMetrics = undefined,
@@ -1247,7 +1248,7 @@ export default class Meetings extends WebexPlugin {
1247
1248
  deviceUrl: this.webex.internal.device.url,
1248
1249
  // @ts-ignore
1249
1250
  orgId: this.webex.internal.device.orgId,
1250
- locus: type === _LOCUS_ID_ ? destination : null, // pass the locus object if present
1251
+ locus: type === DESTINATION_TYPE.LOCUS_ID ? destination : null, // pass the locus object if present
1251
1252
  meetingInfoProvider: this.meetingInfo,
1252
1253
  destination,
1253
1254
  destinationType: type,
@@ -1290,19 +1291,17 @@ export default class Meetings extends WebexPlugin {
1290
1291
 
1291
1292
  if (meetingInfo) {
1292
1293
  meeting.injectMeetingInfo(meetingInfo, meetingInfoOptions, meetingLookupUrl);
1293
- } else if (
1294
- enableUnifiedMeetings &&
1295
- !isMeetingActive &&
1296
- useRandomDelayForInfo &&
1297
- waitingTime > 0
1298
- ) {
1299
- meeting.fetchMeetingInfoTimeoutId = setTimeout(
1300
- () => meeting.fetchMeetingInfo(meetingInfoOptions),
1301
- waitingTime
1302
- );
1303
- meeting.parseMeetingInfo(undefined, destination);
1304
- } else {
1305
- await meeting.fetchMeetingInfo(meetingInfoOptions);
1294
+ } else if (type !== DESTINATION_TYPE.ONE_ON_ONE_CALL) {
1295
+ // ignore fetchMeetingInfo for 1:1 meetings
1296
+ if (enableUnifiedMeetings && !isMeetingActive && useRandomDelayForInfo && waitingTime > 0) {
1297
+ meeting.fetchMeetingInfoTimeoutId = setTimeout(
1298
+ () => meeting.fetchMeetingInfo(meetingInfoOptions),
1299
+ waitingTime
1300
+ );
1301
+ meeting.parseMeetingInfo(undefined, destination);
1302
+ } else {
1303
+ await meeting.fetchMeetingInfo(meetingInfoOptions);
1304
+ }
1306
1305
  }
1307
1306
  } catch (err) {
1308
1307
  if (
@@ -1333,7 +1332,7 @@ export default class Meetings extends WebexPlugin {
1333
1332
  // For type LOCUS_ID we need to parse the locus object to get the information
1334
1333
  // about the caller and callee
1335
1334
  // Meeting Added event will be created in `handleLocusEvent`
1336
- if (type !== _LOCUS_ID_) {
1335
+ if (type !== DESTINATION_TYPE.LOCUS_ID) {
1337
1336
  if (!meeting.sipUri) {
1338
1337
  meeting.setSipUri(destination);
1339
1338
  }
@@ -2,6 +2,7 @@ import type {
2
2
  NoiseReductionEffectOptions,
3
3
  VirtualBackgroundEffectOptions,
4
4
  } from '@webex/media-helpers';
5
+ import {Enum} from '../constants';
5
6
 
6
7
  type INoiseReductionEffect = Omit<
7
8
  NoiseReductionEffectOptions,
@@ -10,3 +11,13 @@ type INoiseReductionEffect = Omit<
10
11
  type IVirtualBackgroundEffect = Omit<VirtualBackgroundEffectOptions, 'authToken'>;
11
12
 
12
13
  export type {INoiseReductionEffect, IVirtualBackgroundEffect};
14
+
15
+ export const MEETING_KEY = {
16
+ CONVERSATION_URL: 'conversationUrl',
17
+ SIP_URI: 'sipUri',
18
+ LOCUS_URL: 'locusUrl',
19
+ MEETINGNUMBER: 'meetingNumber',
20
+ CORRELATION_ID: 'correlationId',
21
+ } as const;
22
+
23
+ export type MEETING_KEY = Enum<typeof MEETING_KEY>;
@@ -5,10 +5,9 @@ import {
5
5
  _INCOMING_,
6
6
  _JOINED_,
7
7
  _LEFT_,
8
- _LOCUS_ID_,
8
+ DESTINATION_TYPE,
9
9
  _MOVED_,
10
10
  BREAKOUTS,
11
- CORRELATION_ID,
12
11
  EVENT_TRIGGERS,
13
12
  LOCUS,
14
13
  LOCUSEVENT,
@@ -18,6 +17,7 @@ import LoggerProxy from '../common/logs/logger-proxy';
18
17
  import Trigger from '../common/events/trigger-proxy';
19
18
  import BEHAVIORAL_METRICS from '../metrics/constants';
20
19
  import Metrics from '../metrics';
20
+ import {MEETING_KEY} from './meetings.types';
21
21
 
22
22
  /**
23
23
  * Meetings Media Codec Missing Event
@@ -39,14 +39,15 @@ import Metrics from '../metrics';
39
39
 
40
40
  const MeetingsUtil: any = {};
41
41
 
42
- MeetingsUtil.getMeetingAddedType = (type) => (type === _LOCUS_ID_ ? _INCOMING_ : _CREATED_);
42
+ MeetingsUtil.getMeetingAddedType = (type: DESTINATION_TYPE) =>
43
+ type === DESTINATION_TYPE.LOCUS_ID ? _INCOMING_ : _CREATED_;
43
44
 
44
45
  MeetingsUtil.handleRoapMercury = (envelope, meetingCollection) => {
45
46
  const {data} = envelope;
46
47
  const {eventType} = data;
47
48
 
48
49
  if (eventType === LOCUSEVENT.MESSAGE_ROAP) {
49
- const meeting = meetingCollection.getByKey(CORRELATION_ID, data.correlationId);
50
+ const meeting = meetingCollection.getByKey(MEETING_KEY.CORRELATION_ID, data.correlationId);
50
51
 
51
52
  if (meeting) {
52
53
  const {seq, messageType, tieBreaker, errorType, errorCause} = data.message;
@@ -69,6 +69,7 @@ const BEHAVIORAL_METRICS = {
69
69
  ROAP_OFFER_TO_ANSWER_LATENCY: 'js_sdk_roap_offer_to_answer_latency',
70
70
  ROAP_HTTP_RESPONSE_MISSING: 'js_sdk_roap_http_response_missing',
71
71
  TURN_DISCOVERY_REQUIRES_OK: 'js_sdk_turn_discovery_requires_ok',
72
+ REACHABILITY_COMPLETED: 'js_sdk_reachability_completed',
72
73
  };
73
74
 
74
75
  export {BEHAVIORAL_METRICS as default};
@@ -65,6 +65,50 @@ class Metrics {
65
65
  tags: metricTags,
66
66
  });
67
67
  }
68
+
69
+ /**
70
+ * Flattens an object into one that has no nested properties. Each level of nesting is represented
71
+ * by "_" in the flattened object property names.
72
+ * This function is needed, because Amplitude doesn't allow passing nested objects as metricFields.
73
+ * Use this function for metricFields before calling sendBehavioralMetric() if you want to send
74
+ * nested objects in your metrics.
75
+ *
76
+ * If the function is called with a literal, it returns an object with a single property "value"
77
+ * and the literal value in it.
78
+ *
79
+ * @param {any} payload object you want to flatten
80
+ * @param {string} prefix string prefix prepended to any property names in flatten object
81
+ * @returns {Object}
82
+ */
83
+ prepareMetricFields(payload: any = {}, prefix = '') {
84
+ let output = {};
85
+
86
+ if (Array.isArray(payload)) {
87
+ payload.forEach((item, index) => {
88
+ const propName = prefix.length > 0 ? `${prefix}_${index}` : `${index}`;
89
+
90
+ output = {...output, ...this.prepareMetricFields(item, propName)};
91
+ });
92
+
93
+ return output;
94
+ }
95
+
96
+ if (typeof payload !== 'object' || payload === null) {
97
+ if (prefix.length > 0) {
98
+ return {[prefix]: payload};
99
+ }
100
+
101
+ return {value: payload};
102
+ }
103
+
104
+ Object.entries(payload).forEach(([key, value]) => {
105
+ const propName = prefix.length > 0 ? `${prefix}_${key}` : key;
106
+
107
+ output = {...output, ...this.prepareMetricFields(value, propName)};
108
+ });
109
+
110
+ return output;
111
+ }
68
112
  }
69
113
 
70
114
  // Export Metrics singleton ---------------------------------------------------
@@ -1,7 +1,7 @@
1
1
  // @ts-ignore
2
2
  import {StatelessWebexPlugin} from '@webex/webex-core';
3
3
 
4
- import {MEETINGS, _PERSONAL_ROOM_} from '../constants';
4
+ import {MEETINGS, DESTINATION_TYPE} from '../constants';
5
5
  import ParameterError from '../common/errors/parameter';
6
6
 
7
7
  import PersonalMeetingRoomRequest from './request';
@@ -141,7 +141,7 @@ export default class PersonalMeetingRoom extends StatelessWebexPlugin {
141
141
  */
142
142
  public get() {
143
143
  const options = {
144
- type: _PERSONAL_ROOM_,
144
+ type: DESTINATION_TYPE.PERSONAL_ROOM,
145
145
  };
146
146
 
147
147
  return this.meetingInfo.fetchMeetingInfo(options).then((pmr) => {