@webex/plugin-meetings 3.5.0 → 3.6.0-next.10

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 (83) hide show
  1. package/dist/breakouts/breakout.js +1 -1
  2. package/dist/breakouts/index.js +1 -1
  3. package/dist/common/errors/webinar-registration-error.js +50 -0
  4. package/dist/common/errors/webinar-registration-error.js.map +1 -0
  5. package/dist/config.js +4 -1
  6. package/dist/config.js.map +1 -1
  7. package/dist/constants.js +8 -0
  8. package/dist/constants.js.map +1 -1
  9. package/dist/index.js +7 -0
  10. package/dist/index.js.map +1 -1
  11. package/dist/interpretation/index.js +1 -1
  12. package/dist/interpretation/siLanguage.js +1 -1
  13. package/dist/locus-info/parser.js +5 -1
  14. package/dist/locus-info/parser.js.map +1 -1
  15. package/dist/media/index.js +3 -1
  16. package/dist/media/index.js.map +1 -1
  17. package/dist/meeting/in-meeting-actions.js +3 -1
  18. package/dist/meeting/in-meeting-actions.js.map +1 -1
  19. package/dist/meeting/index.js +185 -103
  20. package/dist/meeting/index.js.map +1 -1
  21. package/dist/meeting/muteState.js +5 -2
  22. package/dist/meeting/muteState.js.map +1 -1
  23. package/dist/meeting/util.js +8 -10
  24. package/dist/meeting/util.js.map +1 -1
  25. package/dist/meeting-info/meeting-info-v2.js +68 -17
  26. package/dist/meeting-info/meeting-info-v2.js.map +1 -1
  27. package/dist/meetings/index.js +102 -27
  28. package/dist/meetings/index.js.map +1 -1
  29. package/dist/metrics/constants.js +2 -1
  30. package/dist/metrics/constants.js.map +1 -1
  31. package/dist/multistream/remoteMedia.js +4 -0
  32. package/dist/multistream/remoteMedia.js.map +1 -1
  33. package/dist/roap/request.js +1 -1
  34. package/dist/roap/request.js.map +1 -1
  35. package/dist/types/common/errors/webinar-registration-error.d.ts +14 -0
  36. package/dist/types/config.d.ts +2 -0
  37. package/dist/types/constants.d.ts +8 -1
  38. package/dist/types/index.d.ts +2 -1
  39. package/dist/types/meeting/in-meeting-actions.d.ts +2 -0
  40. package/dist/types/meeting/index.d.ts +11 -0
  41. package/dist/types/meeting/muteState.d.ts +2 -1
  42. package/dist/types/meeting-info/meeting-info-v2.d.ts +23 -0
  43. package/dist/types/meetings/index.d.ts +43 -2
  44. package/dist/types/metrics/constants.d.ts +1 -0
  45. package/dist/types/multistream/remoteMedia.d.ts +1 -0
  46. package/dist/webinar/index.js +1 -1
  47. package/package.json +22 -22
  48. package/src/common/errors/webinar-registration-error.ts +27 -0
  49. package/src/config.ts +3 -0
  50. package/src/constants.ts +7 -0
  51. package/src/index.ts +2 -0
  52. package/src/locus-info/parser.ts +8 -1
  53. package/src/media/index.ts +4 -1
  54. package/src/meeting/in-meeting-actions.ts +3 -0
  55. package/src/meeting/index.ts +82 -13
  56. package/src/meeting/muteState.ts +6 -2
  57. package/src/meeting/util.ts +27 -31
  58. package/src/meeting-info/meeting-info-v2.ts +51 -0
  59. package/src/meetings/index.ts +129 -38
  60. package/src/metrics/constants.ts +1 -0
  61. package/src/multistream/remoteMedia.ts +5 -0
  62. package/src/roap/request.ts +3 -1
  63. package/test/unit/spec/locus-info/index.js +29 -0
  64. package/test/unit/spec/media/index.ts +4 -0
  65. package/test/unit/spec/meeting/in-meeting-actions.ts +2 -0
  66. package/test/unit/spec/meeting/index.js +118 -18
  67. package/test/unit/spec/meeting/muteState.js +8 -4
  68. package/test/unit/spec/meeting/utils.js +50 -85
  69. package/test/unit/spec/meeting-info/meetinginfov2.js +37 -0
  70. package/test/unit/spec/meetings/index.js +128 -13
  71. package/test/unit/spec/multistream/remoteMedia.ts +16 -2
  72. package/dist/networkQualityMonitor/index.js +0 -227
  73. package/dist/networkQualityMonitor/index.js.map +0 -1
  74. package/dist/rtcMetrics/constants.js +0 -11
  75. package/dist/rtcMetrics/constants.js.map +0 -1
  76. package/dist/rtcMetrics/index.js +0 -197
  77. package/dist/rtcMetrics/index.js.map +0 -1
  78. package/dist/types/networkQualityMonitor/index.d.ts +0 -70
  79. package/dist/types/rtcMetrics/constants.d.ts +0 -4
  80. package/dist/types/rtcMetrics/index.d.ts +0 -71
  81. package/src/rtcMetrics/constants.ts +0 -3
  82. package/src/rtcMetrics/index.ts +0 -186
  83. package/test/unit/spec/rtcMetrics/index.ts +0 -154
@@ -1,11 +1,12 @@
1
1
  /* eslint no-shadow: ["error", { "allow": ["eventType"] }] */
2
- import {union} from 'lodash';
2
+ import {cloneDeep} from 'lodash';
3
3
  import '@webex/internal-plugin-mercury';
4
4
  import '@webex/internal-plugin-conversation';
5
5
  import '@webex/internal-plugin-metrics';
6
6
  // @ts-ignore
7
7
  import {WebexPlugin} from '@webex/webex-core';
8
8
  import {setLogger} from '@webex/internal-media-core';
9
+ import {DeviceRegistrationOptions} from '@webex/internal-plugin-device';
9
10
 
10
11
  import * as mediaHelpersModule from '@webex/media-helpers';
11
12
 
@@ -55,6 +56,7 @@ import MeetingCollection from './collection';
55
56
  import {MEETING_KEY, INoiseReductionEffect, IVirtualBackgroundEffect} from './meetings.types';
56
57
  import MeetingsUtil from './util';
57
58
  import PermissionError from '../common/errors/permission';
59
+ import WebinarRegistrationError from '../common/errors/webinar-registration-error';
58
60
  import {SpaceIDDeprecatedError} from '../common/errors/webex-errors';
59
61
  import NoMeetingInfoError from '../common/errors/no-meeting-info';
60
62
 
@@ -133,6 +135,28 @@ class MediaLogger {
133
135
  * @memberof Meetings
134
136
  */
135
137
 
138
+ /**
139
+ * Object containing only the most basic information about a meeting.
140
+ * This is the information that is kept even after the meeting is deleted from the MeetingCollection
141
+ */
142
+ export type BasicMeetingInformation = {
143
+ allowMediaInLobby: boolean;
144
+ correlationId: string;
145
+ environment: string;
146
+ id: string;
147
+ locusUrl: string;
148
+ locusInfo: {
149
+ // it's only a very small subset of the locus info, to avoid using much memory
150
+ url: string;
151
+ fullState: {
152
+ lastActive: string;
153
+ sessionId: string;
154
+ };
155
+ };
156
+ meetingInfo: any;
157
+ sessionCorrelationId: string;
158
+ };
159
+
136
160
  /**
137
161
  * Maintain a cache of meetings and sync with services.
138
162
  * @class
@@ -141,6 +165,7 @@ export default class Meetings extends WebexPlugin {
141
165
  loggerRequest: any;
142
166
  media: any;
143
167
  meetingCollection: any;
168
+ deletedMeetings: Map<string, BasicMeetingInformation>;
144
169
  personalMeetingRoom: any;
145
170
  preferredWebexSite: any;
146
171
  reachability: Reachability;
@@ -191,6 +216,8 @@ export default class Meetings extends WebexPlugin {
191
216
  // @ts-ignore
192
217
  this.loggerRequest = new LoggerRequest({webex: this.webex});
193
218
  this.meetingCollection = new MeetingCollection();
219
+ this.deletedMeetings = new Map();
220
+
194
221
  /**
195
222
  * The PersonalMeetingRoom object to interact with server
196
223
  * @instance
@@ -736,15 +763,35 @@ export default class Meetings extends WebexPlugin {
736
763
  }
737
764
  }
738
765
 
766
+ /**
767
+ * API to toggle backend ipv6 native support config, needs to be called before webex.meetings.register()
768
+ *
769
+ * @param {Boolean} newValue
770
+ * @private
771
+ * @memberof Meetings
772
+ * @returns {undefined}
773
+ */
774
+ private _toggleIpv6BackendNativeSupport(newValue: boolean) {
775
+ if (typeof newValue !== 'boolean') {
776
+ return;
777
+ }
778
+ // @ts-ignore
779
+ if (this.config.backendIpv6NativeSupport !== newValue) {
780
+ // @ts-ignore
781
+ this.config.backendIpv6NativeSupport = newValue;
782
+ }
783
+ }
784
+
739
785
  /**
740
786
  * Explicitly sets up the meetings plugin by registering
741
787
  * the device, connecting to mercury, and listening for locus events.
742
788
  *
789
+ * @param {DeviceRegistrationOptions} [deviceRegistrationOptions] - The options for registering the device (optional)
743
790
  * @returns {Promise}
744
791
  * @public
745
792
  * @memberof Meetings
746
793
  */
747
- public register() {
794
+ public register(deviceRegistrationOptions?: DeviceRegistrationOptions): Promise<any> {
748
795
  // @ts-ignore
749
796
  if (!this.webex.canAuthorize) {
750
797
  LoggerProxy.logger.error(
@@ -770,7 +817,7 @@ export default class Meetings extends WebexPlugin {
770
817
  }),
771
818
  // @ts-ignore
772
819
  this.webex.internal.device
773
- .register()
820
+ .register(deviceRegistrationOptions)
774
821
  // @ts-ignore
775
822
  .then(() =>
776
823
  LoggerProxy.logger.info(
@@ -996,35 +1043,45 @@ export default class Meetings extends WebexPlugin {
996
1043
  * @memberof Meetings
997
1044
  */
998
1045
  fetchUserPreferredWebexSite() {
999
- return this.request.getMeetingPreferences().then((res) => {
1000
- if (res) {
1001
- const preferredWebexSite = MeetingsUtil.parseDefaultSiteFromMeetingPreferences(res);
1002
- this.preferredWebexSite = preferredWebexSite;
1003
- // @ts-ignore
1004
- this.webex.internal.services._getCatalog().addAllowedDomains([preferredWebexSite]);
1005
- }
1046
+ // @ts-ignore
1047
+ return this.webex.people._getMe().then((me) => {
1048
+ const isGuestUser = me.type === 'appuser';
1049
+ if (!isGuestUser) {
1050
+ return this.request.getMeetingPreferences().then((res) => {
1051
+ if (res) {
1052
+ const preferredWebexSite = MeetingsUtil.parseDefaultSiteFromMeetingPreferences(res);
1053
+ this.preferredWebexSite = preferredWebexSite;
1054
+ // @ts-ignore
1055
+ this.webex.internal.services._getCatalog().addAllowedDomains([preferredWebexSite]);
1056
+ }
1006
1057
 
1007
- // fall back to getting the preferred site from the user information
1008
- if (!this.preferredWebexSite) {
1009
- // @ts-ignore
1010
- return this.webex.internal.user
1011
- .get()
1012
- .then((user) => {
1013
- const preferredWebexSite =
1014
- user?.userPreferences?.userPreferencesItems?.preferredWebExSite;
1015
- if (preferredWebexSite) {
1016
- this.preferredWebexSite = preferredWebexSite;
1017
- // @ts-ignore
1018
- this.webex.internal.services._getCatalog().addAllowedDomains([preferredWebexSite]);
1019
- } else {
1020
- throw new Error('site not found');
1021
- }
1022
- })
1023
- .catch(() => {
1024
- LoggerProxy.logger.error(
1025
- 'Failed to fetch preferred site from user - no site will be set'
1026
- );
1027
- });
1058
+ // fall back to getting the preferred site from the user information
1059
+ if (!this.preferredWebexSite) {
1060
+ // @ts-ignore
1061
+ return this.webex.internal.user
1062
+ .get()
1063
+ .then((user) => {
1064
+ const preferredWebexSite =
1065
+ user?.userPreferences?.userPreferencesItems?.preferredWebExSite;
1066
+ if (preferredWebexSite) {
1067
+ this.preferredWebexSite = preferredWebexSite;
1068
+ // @ts-ignore
1069
+ this.webex.internal.services
1070
+ ._getCatalog()
1071
+ .addAllowedDomains([preferredWebexSite]);
1072
+ } else {
1073
+ throw new Error('site not found');
1074
+ }
1075
+ })
1076
+ .catch(() => {
1077
+ LoggerProxy.logger.error(
1078
+ 'Failed to fetch preferred site from user - no site will be set'
1079
+ );
1080
+ });
1081
+ }
1082
+
1083
+ return Promise.resolve();
1084
+ });
1028
1085
  }
1029
1086
 
1030
1087
  return Promise.resolve();
@@ -1037,11 +1094,21 @@ export default class Meetings extends WebexPlugin {
1037
1094
  * @public
1038
1095
  * @memberof Meetings
1039
1096
  */
1040
-
1041
1097
  getPersonalMeetingRoom() {
1042
1098
  return this.personalMeetingRoom;
1043
1099
  }
1044
1100
 
1101
+ /**
1102
+ * Returns basic information about a meeting that exists or
1103
+ * used to exist in the MeetingCollection
1104
+ *
1105
+ * @param {string} meetingId
1106
+ * @returns {BasicMeetingInformation|undefined}
1107
+ */
1108
+ public getBasicMeetingInformation(meetingId: string): BasicMeetingInformation {
1109
+ return this.meetingCollection.get(meetingId) || this.deletedMeetings.get(meetingId);
1110
+ }
1111
+
1045
1112
  /**
1046
1113
  * @param {Meeting} meeting
1047
1114
  * @param {Object} reason
@@ -1052,6 +1119,24 @@ export default class Meetings extends WebexPlugin {
1052
1119
  */
1053
1120
  private destroy(meeting: Meeting, reason: object) {
1054
1121
  MeetingUtil.cleanUp(meeting);
1122
+ // keep some basic info about the deleted meeting forever
1123
+ this.deletedMeetings.set(meeting.id, {
1124
+ id: meeting.id,
1125
+ allowMediaInLobby: meeting.allowMediaInLobby,
1126
+ correlationId: meeting.correlationId,
1127
+ sessionCorrelationId: meeting.sessionCorrelationId,
1128
+ environment: meeting.environment,
1129
+ locusUrl: meeting.locusUrl,
1130
+ meetingInfo: cloneDeep(meeting.meetingInfo),
1131
+ locusInfo: {
1132
+ // locusInfo can be quite big, so keep just the minimal info
1133
+ url: meeting.locusInfo?.url,
1134
+ fullState: {
1135
+ lastActive: meeting.locusInfo?.fullState?.lastActive,
1136
+ sessionId: meeting.locusInfo?.fullState?.sessionId,
1137
+ },
1138
+ },
1139
+ });
1055
1140
  this.meetingCollection.delete(meeting.id);
1056
1141
  Trigger.trigger(
1057
1142
  this,
@@ -1081,6 +1166,7 @@ export default class Meetings extends WebexPlugin {
1081
1166
  * @param {CallStateForMetrics} callStateForMetrics - information about call state for metrics
1082
1167
  * @param {Object} [meetingInfo] - Pre-fetched complete meeting info
1083
1168
  * @param {String} [meetingLookupUrl] - meeting info prefetch url
1169
+ * @param {string} sessionCorrelationId - the optional specified sessionCorrelationId (callStateForMetrics.sessionCorrelationId) can be provided instead
1084
1170
  * @returns {Promise<Meeting>} A new Meeting.
1085
1171
  * @public
1086
1172
  * @memberof Meetings
@@ -1094,7 +1180,8 @@ export default class Meetings extends WebexPlugin {
1094
1180
  failOnMissingMeetingInfo = false,
1095
1181
  callStateForMetrics: CallStateForMetrics = undefined,
1096
1182
  meetingInfo = undefined,
1097
- meetingLookupUrl = undefined
1183
+ meetingLookupUrl = undefined,
1184
+ sessionCorrelationId: string = undefined
1098
1185
  ) {
1099
1186
  // Validate meeting information based on the provided destination and
1100
1187
  // type. This must be performed prior to determining if the meeting is
@@ -1105,6 +1192,10 @@ export default class Meetings extends WebexPlugin {
1105
1192
  callStateForMetrics = {...(callStateForMetrics || {}), correlationId};
1106
1193
  }
1107
1194
 
1195
+ if (sessionCorrelationId) {
1196
+ callStateForMetrics = {...(callStateForMetrics || {}), sessionCorrelationId};
1197
+ }
1198
+
1108
1199
  return (
1109
1200
  this.meetingInfo
1110
1201
  .fetchInfoOptions(destination, type)
@@ -1176,10 +1267,9 @@ export default class Meetings extends WebexPlugin {
1176
1267
  locusId: createdMeeting.locusId,
1177
1268
  meetingId: createdMeeting.locusInfo?.info?.webExMeetingId,
1178
1269
  autoupload: true,
1179
- }).then(() => this.destroy(createdMeeting, payload.reason));
1180
- } else {
1181
- this.destroy(createdMeeting, payload.reason);
1270
+ });
1182
1271
  }
1272
+ this.destroy(createdMeeting, payload.reason);
1183
1273
  });
1184
1274
 
1185
1275
  createdMeeting.on(EVENTS.REQUEST_UPLOAD_LOGS, (meetingInstance) => {
@@ -1206,7 +1296,7 @@ export default class Meetings extends WebexPlugin {
1206
1296
  return Promise.resolve(createdMeeting);
1207
1297
  });
1208
1298
  }
1209
- meeting.setCallStateForMetrics(callStateForMetrics);
1299
+ meeting.updateCallStateForMetrics(callStateForMetrics);
1210
1300
 
1211
1301
  // Return the existing meeting.
1212
1302
  return Promise.resolve(meeting);
@@ -1308,7 +1398,8 @@ export default class Meetings extends WebexPlugin {
1308
1398
  if (
1309
1399
  !(err instanceof CaptchaError) &&
1310
1400
  !(err instanceof PasswordError) &&
1311
- !(err instanceof PermissionError)
1401
+ !(err instanceof PermissionError) &&
1402
+ !(err instanceof WebinarRegistrationError)
1312
1403
  ) {
1313
1404
  LoggerProxy.logger.info(
1314
1405
  `Meetings:index#createMeeting --> Info Unable to fetch meeting info for ${destination}.`
@@ -70,6 +70,7 @@ const BEHAVIORAL_METRICS = {
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
72
  REACHABILITY_COMPLETED: 'js_sdk_reachability_completed',
73
+ WEBINAR_REGISTRATION_ERROR: 'js_sdk_webinar_registration_error',
73
74
  };
74
75
 
75
76
  export {BEHAVIORAL_METRICS as default};
@@ -107,11 +107,16 @@ export class RemoteMedia extends EventsScope {
107
107
  * to restrict the requested resolution to this size
108
108
  * @param width width of the video element
109
109
  * @param height height of the video element
110
+ * @note width/height of 0 will be ignored
110
111
  */
111
112
  public setSizeHint(width, height) {
112
113
  // only base on height for now
113
114
  let fs: number;
114
115
 
116
+ if (width === 0 || height === 0) {
117
+ return;
118
+ }
119
+
115
120
  if (height < 135) {
116
121
  fs = 60;
117
122
  } else if (height < 270) {
@@ -79,7 +79,9 @@ export default class RoapRequest extends StatelessWebexPlugin {
79
79
  });
80
80
 
81
81
  LoggerProxy.logger.info(
82
- `Roap:request#sendRoap --> ${locusSelfUrl} \n ${roapMessage.messageType} \n seq:${roapMessage.seq}`
82
+ `Roap:request#sendRoap --> ${roapMessage.messageType} seq:${roapMessage.seq} ${
83
+ ipVersion ? `ipver=${ipVersion} ` : ''
84
+ } ${locusSelfUrl}`
83
85
  );
84
86
 
85
87
  return locusMediaRequest
@@ -1807,6 +1807,35 @@ describe('plugin-meetings', () => {
1807
1807
  assert.calledWith(locusInfo.applyLocusDeltaData, action, parsedLoci, fakeMeeting);
1808
1808
  });
1809
1809
 
1810
+ it('catches errors thrown by onDeltaAction and is able to process next Locus delta', () => {
1811
+ const fakeLocusDelta = {
1812
+ sequence: {
1813
+ rangeStart: 0,
1814
+ rangeEnd: 0,
1815
+ },
1816
+ };
1817
+ locusInfo.locusParser.workingCopy = {
1818
+ sequence: {
1819
+ rangeStart: 0,
1820
+ rangeEnd: 0,
1821
+ },
1822
+ };
1823
+ const testMeeting = {locusInfo: {onDeltaLocus: sinon.stub()}};
1824
+
1825
+ locusParser.onDeltaAction = sandbox
1826
+ .stub()
1827
+ .onCall(0)
1828
+ .callsFake(() => {
1829
+ throw new Error('fake error');
1830
+ });
1831
+
1832
+ // simulate first locus delta coming - it will trigger an error thrown by onDeltaAction
1833
+ locusInfo.handleLocusDelta(fakeLocusDelta, testMeeting);
1834
+
1835
+ // simulate a second locus delta coming - it should be processed without errors
1836
+ locusInfo.handleLocusDelta(fakeLocusDelta, testMeeting);
1837
+ });
1838
+
1810
1839
  it('applyLocusDeltaData handles USE_INCOMING action correctly', () => {
1811
1840
  const {USE_INCOMING} = LocusDeltaParser.loci;
1812
1841
  const meeting = {
@@ -83,6 +83,7 @@ describe('createMediaConnection', () => {
83
83
  username: 'turn username',
84
84
  password: 'turn password',
85
85
  },
86
+ iceCandidatesTimeout: undefined,
86
87
  });
87
88
  assert.calledOnce(roapMediaConnectionConstructorStub);
88
89
  assert.calledWith(
@@ -100,6 +101,7 @@ describe('createMediaConnection', () => {
100
101
  credential: 'turn password',
101
102
  },
102
103
  ],
104
+ iceCandidatesTimeout: undefined,
103
105
  skipInactiveTransceivers: false,
104
106
  requireH264: true,
105
107
  sdpMunging: {
@@ -306,12 +308,14 @@ describe('createMediaConnection', () => {
306
308
  enableRtx: ENABLE_RTX,
307
309
  enableExtmap: ENABLE_EXTMAP,
308
310
  turnServerInfo,
311
+ iceCandidatesTimeout: undefined,
309
312
  });
310
313
  assert.calledOnce(roapMediaConnectionConstructorStub);
311
314
  assert.calledWith(
312
315
  roapMediaConnectionConstructorStub,
313
316
  {
314
317
  iceServers: [],
318
+ iceCandidatesTimeout: undefined,
315
319
  skipInactiveTransceivers: false,
316
320
  requireH264: true,
317
321
  sdpMunging: {
@@ -78,6 +78,7 @@ describe('plugin-meetings', () => {
78
78
  supportHDV: null,
79
79
  canShareWhiteBoard: null,
80
80
  enforceVirtualBackground: null,
81
+ canPollingAndQA: null,
81
82
  ...expected,
82
83
  };
83
84
 
@@ -161,6 +162,7 @@ describe('plugin-meetings', () => {
161
162
  'supportHDV',
162
163
  'canShareWhiteBoard',
163
164
  'enforceVirtualBackground',
165
+ 'canPollingAndQA',
164
166
  ].forEach((key) => {
165
167
  it(`get and set for ${key} work as expected`, () => {
166
168
  const inMeetingActions = new InMeetingActions();