@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
@@ -82,6 +82,7 @@ interface IInMeetingActions {
82
82
  supportHDV?: boolean;
83
83
  canShareWhiteBoard?: boolean;
84
84
  enforceVirtualBackground?: boolean;
85
+ canPollingAndQA?: boolean;
85
86
  }
86
87
 
87
88
  /**
@@ -236,6 +237,7 @@ export default class InMeetingActions implements IInMeetingActions {
236
237
 
237
238
  canShareWhiteBoard = null;
238
239
 
240
+ canPollingAndQA = null;
239
241
  /**
240
242
  * Returns all meeting action options
241
243
  * @returns {Object}
@@ -314,6 +316,7 @@ export default class InMeetingActions implements IInMeetingActions {
314
316
  supportHQV: this.supportHQV,
315
317
  supportHDV: this.supportHDV,
316
318
  canShareWhiteBoard: this.canShareWhiteBoard,
319
+ canPollingAndQA: this.canPollingAndQA,
317
320
  });
318
321
 
319
322
  /**
@@ -10,6 +10,7 @@ import {
10
10
  ClientEventLeaveReason,
11
11
  CallDiagnosticUtils,
12
12
  CALL_DIAGNOSTIC_CONFIG,
13
+ RtcMetrics,
13
14
  } from '@webex/internal-plugin-metrics';
14
15
  import {ClientEvent as RawClientEvent} from '@webex/event-dictionary-ts';
15
16
 
@@ -127,6 +128,7 @@ import {
127
128
  MeetingInfoV2PasswordError,
128
129
  MeetingInfoV2CaptchaError,
129
130
  MeetingInfoV2PolicyError,
131
+ MeetingInfoV2WebinarRegistrationError,
130
132
  } from '../meeting-info/meeting-info-v2';
131
133
  import {CSI, ReceiveSlotManager} from '../multistream/receiveSlotManager';
132
134
  import SendSlotManager from '../multistream/sendSlotManager';
@@ -155,7 +157,7 @@ import ControlsOptionsManager from '../controls-options-manager';
155
157
  import PermissionError from '../common/errors/permission';
156
158
  import {LocusMediaRequest} from './locusMediaRequest';
157
159
  import {ConnectionStateHandler, ConnectionStateEvent} from './connectionStateHandler';
158
- import RtcMetrics from '../rtcMetrics';
160
+ import WebinarRegistrationError from '../common/errors/webinar-registration-error';
159
161
 
160
162
  // default callback so we don't call an undefined function, but in practice it should never be used
161
163
  const DEFAULT_ICE_PHASE_CALLBACK = () => 'JOIN_MEETING_FINAL';
@@ -227,6 +229,7 @@ export type AddMediaOptions = {
227
229
 
228
230
  export type CallStateForMetrics = {
229
231
  correlationId?: string;
232
+ sessionCorrelationId?: string;
230
233
  joinTrigger?: string;
231
234
  loginType?: string;
232
235
  };
@@ -742,12 +745,29 @@ export default class Meeting extends StatelessWebexPlugin {
742
745
  */
743
746
  this.callStateForMetrics = attrs.callStateForMetrics || {};
744
747
  const correlationId = attrs.correlationId || attrs.callStateForMetrics?.correlationId;
748
+ const sessionCorrelationId =
749
+ attrs.sessionCorrelationId || attrs.callStateForMetrics?.sessionCorrelationId;
750
+ if (sessionCorrelationId) {
751
+ LoggerProxy.logger.log(
752
+ `Meetings:index#constructor --> Initializing the meeting object with session correlation id from app ${correlationId}`
753
+ );
754
+ this.callStateForMetrics.sessionCorrelationId = sessionCorrelationId;
755
+ } else {
756
+ LoggerProxy.logger.log(
757
+ `Meetings:index#constructor --> No session correlation id supplied. None will be generated and this field will remain blank`
758
+ );
759
+ // TODO: supply a session from the meetings instance
760
+ this.callStateForMetrics.sessionCorrelationId = '';
761
+ }
745
762
  if (correlationId) {
746
763
  LoggerProxy.logger.log(
747
764
  `Meetings:index#constructor --> Initializing the meeting object with correlation id from app ${correlationId}`
748
765
  );
749
766
  this.callStateForMetrics.correlationId = correlationId;
750
767
  } else {
768
+ LoggerProxy.logger.log(
769
+ `Meetings:index#constructor --> Initializing the meeting object with generated correlation id from sdk ${this.id}`
770
+ );
751
771
  this.callStateForMetrics.correlationId = this.id;
752
772
  }
753
773
  /**
@@ -1581,6 +1601,22 @@ export default class Meeting extends StatelessWebexPlugin {
1581
1601
  this.callStateForMetrics.correlationId = correlationId;
1582
1602
  }
1583
1603
 
1604
+ /**
1605
+ * Getter - Returns callStateForMetrics.sessionCorrelationId
1606
+ * @returns {string}
1607
+ */
1608
+ get sessionCorrelationId() {
1609
+ return this.callStateForMetrics.sessionCorrelationId;
1610
+ }
1611
+
1612
+ /**
1613
+ * Setter - sets callStateForMetrics.sessionCorrelationId
1614
+ * @param {string} sessionCorrelationId
1615
+ */
1616
+ set sessionCorrelationId(sessionCorrelationId: string) {
1617
+ this.callStateForMetrics.sessionCorrelationId = sessionCorrelationId;
1618
+ }
1619
+
1584
1620
  /**
1585
1621
  * Getter - Returns isoLocalClientMeetingJoinTime
1586
1622
  * This will be set once on meeting join, and not updated again
@@ -1725,8 +1761,16 @@ export default class Meeting extends StatelessWebexPlugin {
1725
1761
  if (err.meetingInfo) {
1726
1762
  this.meetingInfo = err.meetingInfo;
1727
1763
  }
1728
-
1729
1764
  throw new PermissionError();
1765
+ } else if (err instanceof MeetingInfoV2WebinarRegistrationError) {
1766
+ this.meetingInfoFailureReason = MEETING_INFO_FAILURE_REASON.WEBINAR_REGISTRATION;
1767
+ this.meetingInfoFailureCode = err.wbxAppApiCode;
1768
+
1769
+ if (err.meetingInfo) {
1770
+ this.meetingInfo = err.meetingInfo;
1771
+ }
1772
+
1773
+ throw new WebinarRegistrationError();
1730
1774
  } else if (err instanceof MeetingInfoV2PasswordError) {
1731
1775
  LoggerProxy.logger.info(
1732
1776
  // @ts-ignore
@@ -2705,6 +2749,9 @@ export default class Meeting extends StatelessWebexPlugin {
2705
2749
  newShareStatus = SHARE_STATUS.NO_SHARE;
2706
2750
  }
2707
2751
 
2752
+ LoggerProxy.logger.info(
2753
+ `Meeting:index#setUpLocusInfoMediaInactiveListener --> this.shareStatus=${this.shareStatus} newShareStatus=${newShareStatus}`
2754
+ );
2708
2755
  if (newShareStatus !== this.shareStatus) {
2709
2756
  const oldShareStatus = this.shareStatus;
2710
2757
 
@@ -3057,7 +3104,7 @@ export default class Meeting extends StatelessWebexPlugin {
3057
3104
  private setUpLocusInfoSelfListener() {
3058
3105
  this.locusInfo.on(LOCUSINFO.EVENTS.LOCAL_UNMUTE_REQUIRED, (payload) => {
3059
3106
  if (this.audio) {
3060
- this.audio.handleServerLocalUnmuteRequired(this);
3107
+ this.audio.handleServerLocalUnmuteRequired(this, payload.unmuteAllowed);
3061
3108
  Trigger.trigger(
3062
3109
  this,
3063
3110
  {
@@ -3792,6 +3839,10 @@ export default class Meeting extends StatelessWebexPlugin {
3792
3839
  requiredPolicies: [SELF_POLICY.SUPPORT_CHAT],
3793
3840
  policies: this.selfUserPolicies,
3794
3841
  }),
3842
+ canPollingAndQA: ControlsOptionsUtil.hasPolicies({
3843
+ requiredPolicies: [SELF_POLICY.SUPPORT_POLLING_AND_QA],
3844
+ policies: this.selfUserPolicies,
3845
+ }),
3795
3846
  canShareApplication:
3796
3847
  (ControlsOptionsUtil.hasHints({
3797
3848
  requiredHints: [DISPLAY_HINTS.SHARE_APPLICATION],
@@ -6338,7 +6389,7 @@ export default class Meeting extends StatelessWebexPlugin {
6338
6389
  private async createMediaConnection(turnServerInfo, bundlePolicy?: BundlePolicy) {
6339
6390
  this.rtcMetrics = this.isMultistream
6340
6391
  ? // @ts-ignore
6341
- new RtcMetrics(this.webex, this.id, this.correlationId)
6392
+ new RtcMetrics(this.webex, {meetingId: this.id}, this.correlationId)
6342
6393
  : undefined;
6343
6394
 
6344
6395
  const mc = Media.createMediaConnection(
@@ -6355,6 +6406,8 @@ export default class Meeting extends StatelessWebexPlugin {
6355
6406
  enableExtmap: this.config.enableExtmap,
6356
6407
  turnServerInfo,
6357
6408
  bundlePolicy,
6409
+ // @ts-ignore - config coming from registerPlugin
6410
+ iceCandidatesTimeout: this.config.iceCandidatesGatheringTimeout,
6358
6411
  }
6359
6412
  );
6360
6413
 
@@ -8142,6 +8195,9 @@ export default class Meeting extends StatelessWebexPlugin {
8142
8195
  * @returns {undefined}
8143
8196
  */
8144
8197
  private handleShareAudioStreamEnded = async () => {
8198
+ LoggerProxy.logger.info(
8199
+ `Meeting:index#handleShareAudioStreamEnded --> audio share stream ended`
8200
+ );
8145
8201
  // current share audio stream has ended, but there might be an active
8146
8202
  // share video stream. we only leave from wireless share if share has
8147
8203
  // completely ended, which means no share audio or video streams active
@@ -8184,6 +8240,9 @@ export default class Meeting extends StatelessWebexPlugin {
8184
8240
  * @returns {undefined}
8185
8241
  */
8186
8242
  private handleShareVideoStreamEnded = async () => {
8243
+ LoggerProxy.logger.info(
8244
+ `Meeting:index#handleShareVideoStreamEnded --> video share stream ended`
8245
+ );
8187
8246
  // current share video stream has ended, but there might be an active
8188
8247
  // share audio stream. we only leave from wireless share if share has
8189
8248
  // completely ended, which means no share audio or video streams active
@@ -8672,6 +8731,9 @@ export default class Meeting extends StatelessWebexPlugin {
8672
8731
  * @returns {Promise}
8673
8732
  */
8674
8733
  async publishStreams(streams: LocalStreams): Promise<void> {
8734
+ LoggerProxy.logger.info(
8735
+ `Meeting:index#publishStreams --> called with: ${JSON.stringify(streams)}`
8736
+ );
8675
8737
  this.checkMediaConnection();
8676
8738
  if (
8677
8739
  !streams.microphone &&
@@ -8683,15 +8745,19 @@ export default class Meeting extends StatelessWebexPlugin {
8683
8745
  return;
8684
8746
  }
8685
8747
 
8686
- if (
8687
- streams?.microphone?.readyState === 'ended' ||
8688
- streams?.camera?.readyState === 'ended' ||
8689
- streams?.screenShare?.audio?.readyState === 'ended' ||
8690
- streams?.screenShare?.video?.readyState === 'ended'
8691
- ) {
8692
- throw new Error(
8693
- `Attempted to publish stream with ended readyState, correlationId=${this.correlationId}`
8694
- );
8748
+ const streamChecks = [
8749
+ {stream: streams?.microphone, name: 'microphone'},
8750
+ {stream: streams?.camera, name: 'camera'},
8751
+ {stream: streams?.screenShare?.audio, name: 'screenShare audio'},
8752
+ {stream: streams?.screenShare?.video, name: 'screenShare video'},
8753
+ ];
8754
+
8755
+ for (const {stream, name} of streamChecks) {
8756
+ if (stream?.readyState === 'ended') {
8757
+ throw new Error(
8758
+ `Attempted to publish ${name} stream with ended readyState, correlationId=${this.correlationId}`
8759
+ );
8760
+ }
8695
8761
  }
8696
8762
 
8697
8763
  let floorRequestNeeded = false;
@@ -8753,6 +8819,9 @@ export default class Meeting extends StatelessWebexPlugin {
8753
8819
  * @returns {Promise}
8754
8820
  */
8755
8821
  async unpublishStreams(streams: LocalStream[]): Promise<void> {
8822
+ LoggerProxy.logger.info(
8823
+ `Meeting:index#unpublishStreams --> called with: ${JSON.stringify(streams)}`
8824
+ );
8756
8825
  this.checkMediaConnection();
8757
8826
 
8758
8827
  const promises = [];
@@ -394,21 +394,25 @@ export class MuteState {
394
394
  * @public
395
395
  * @memberof MuteState
396
396
  * @param {Object} [meeting] the meeting object
397
+ * @param {Boolean} [unmuteAllowed] whether the user is allowed to unmute self
397
398
  * @returns {undefined}
398
399
  */
399
- public handleServerLocalUnmuteRequired(meeting?: any) {
400
+ public handleServerLocalUnmuteRequired(meeting: any, unmuteAllowed: boolean) {
400
401
  if (!this.state.client.enabled) {
401
402
  LoggerProxy.logger.warn(
402
403
  `Meeting:muteState#handleServerLocalUnmuteRequired --> ${this.type}: localAudioUnmuteRequired received while ${this.type} is disabled -> local unmute will not result in ${this.type} being sent`
403
404
  );
404
405
  } else {
405
406
  LoggerProxy.logger.info(
406
- `Meeting:muteState#handleServerLocalUnmuteRequired --> ${this.type}: localAudioUnmuteRequired received -> doing local unmute`
407
+ `Meeting:muteState#handleServerLocalUnmuteRequired --> ${this.type}: localAudioUnmuteRequired received -> doing local unmute (unmuteAllowed=${unmuteAllowed})`
407
408
  );
408
409
  }
409
410
 
410
411
  // todo: I'm seeing "you can now unmute yourself " popup when this happens - but same thing happens on web.w.c so we can ignore for now
411
412
  this.state.server.remoteMute = false;
413
+ this.state.server.unmuteAllowed = unmuteAllowed;
414
+
415
+ this.applyUnmuteAllowedToStream(meeting);
412
416
 
413
417
  // change user mute state to false, but keep localMute true if overall mute state is still true
414
418
  this.muteLocalStream(meeting, false, 'localUnmuteRequired');
@@ -89,8 +89,12 @@ const MeetingUtil = {
89
89
  getIpVersion(webex: any): IP_VERSION | undefined {
90
90
  const {supportsIpV4, supportsIpV6} = webex.internal.device.ipNetworkDetector;
91
91
 
92
- if (BrowserDetection().isBrowser('firefox')) {
93
- // our ipv6 solution relies on FQDN ICE candidates, but Firefox doesn't support them,
92
+ if (
93
+ !webex.config.meetings.backendIpv6NativeSupport &&
94
+ BrowserDetection().isBrowser('firefox')
95
+ ) {
96
+ // when backend doesn't support native ipv6,
97
+ // then our NAT64/DNS64 based solution relies on FQDN ICE candidates, but Firefox doesn't support them,
94
98
  // see https://bugzilla.mozilla.org/show_bug.cgi?id=1713128
95
99
  // so for Firefox we don't want the backend to activate the "ipv6 feature"
96
100
  return undefined;
@@ -150,7 +154,9 @@ const MeetingUtil = {
150
154
  ipVersion: MeetingUtil.getIpVersion(meeting.getWebexObject()),
151
155
  })
152
156
  .then((res) => {
153
- // @ts-ignore
157
+ const parsed = MeetingUtil.parseLocusJoin(res);
158
+ meeting.setLocus(parsed);
159
+
154
160
  webex.internal.newMetrics.submitClientEvent({
155
161
  name: 'client.locus.join.response',
156
162
  payload: {
@@ -161,11 +167,11 @@ const MeetingUtil = {
161
167
  },
162
168
  options: {
163
169
  meetingId: meeting.id,
164
- mediaConnections: res.body.mediaConnections,
170
+ mediaConnections: parsed.mediaConnections,
165
171
  },
166
172
  });
167
173
 
168
- return MeetingUtil.parseLocusJoin(res);
174
+ return parsed;
169
175
  });
170
176
  },
171
177
 
@@ -313,34 +319,24 @@ const MeetingUtil = {
313
319
  }
314
320
 
315
321
  // normal join meeting, scenario A, D
316
- return MeetingUtil.joinMeeting(meeting, options)
317
- .then((response) => {
318
- meeting.setLocus(response);
319
-
320
- return Promise.resolve(response);
321
- })
322
- .catch((err) => {
323
- // joining a claimed PMR that is not my own, scenario B
324
- if (MeetingUtil.isPinOrGuest(err)) {
325
- // @ts-ignore
326
- webex.internal.newMetrics.submitClientEvent({
327
- name: 'client.pin.prompt',
328
- options: {
329
- meetingId: meeting.id,
330
- },
331
- });
322
+ return MeetingUtil.joinMeeting(meeting, options).catch((err) => {
323
+ // joining a claimed PMR that is not my own, scenario B
324
+ if (MeetingUtil.isPinOrGuest(err)) {
325
+ webex.internal.newMetrics.submitClientEvent({
326
+ name: 'client.pin.prompt',
327
+ options: {
328
+ meetingId: meeting.id,
329
+ },
330
+ });
332
331
 
333
- // request host pin or non host for unclaimed PMR, start of Scenario C
334
- // see https://sqbu-github.cisco.com/WebExSquared/locus/wiki/Locus-Lobby-and--IVR-Feature
335
- return Promise.reject(new IntentToJoinError('Error Joining Meeting', err));
336
- }
337
- LoggerProxy.logger.error(
338
- 'Meeting:util#joinMeetingOptions --> Error joining the call, ',
339
- err
340
- );
332
+ // request host pin or non host for unclaimed PMR, start of Scenario C
333
+ // see https://sqbu-github.cisco.com/WebExSquared/locus/wiki/Locus-Lobby-and--IVR-Feature
334
+ return Promise.reject(new IntentToJoinError('Error Joining Meeting', err));
335
+ }
336
+ LoggerProxy.logger.error('Meeting:util#joinMeetingOptions --> Error joining the call, ', err);
341
337
 
342
- return Promise.reject(new JoinMeetingError(options, 'Error Joining Meeting', err));
343
- });
338
+ return Promise.reject(new JoinMeetingError(options, 'Error Joining Meeting', err));
339
+ });
344
340
  },
345
341
 
346
342
  /**
@@ -18,6 +18,7 @@ const ADHOC_MEETING_DEFAULT_ERROR =
18
18
  'Failed starting the adhoc meeting, Please contact support team ';
19
19
  const CAPTCHA_ERROR_REQUIRES_PASSWORD_CODES = [423005, 423006];
20
20
  const POLICY_ERROR_CODES = [403049, 403104, 403103, 403048, 403102, 403101];
21
+ const WEBINAR_REGISTRATION_ERROR_CODES = [403021, 403022, 403024];
21
22
  /**
22
23
  * Error to indicate that wbxappapi requires a password
23
24
  */
@@ -124,6 +125,31 @@ export class MeetingInfoV2CaptchaError extends Error {
124
125
  }
125
126
  }
126
127
 
128
+ /**
129
+ * Error preventing join because of a webinar registration error
130
+ */
131
+ export class MeetingInfoV2WebinarRegistrationError extends Error {
132
+ meetingInfo: any;
133
+ sdkMessage: any;
134
+ wbxAppApiCode: any;
135
+ body: any;
136
+ /**
137
+ *
138
+ * @constructor
139
+ * @param {Number} [wbxAppApiErrorCode]
140
+ * @param {Object} [meetingInfo]
141
+ * @param {String} [message]
142
+ */
143
+ constructor(wbxAppApiErrorCode?: number, meetingInfo?: object, message?: string) {
144
+ super(`${message}, code=${wbxAppApiErrorCode}`);
145
+ this.name = 'MeetingInfoV2WebinarRegistrationError';
146
+ this.sdkMessage = message;
147
+ this.stack = new Error().stack;
148
+ this.wbxAppApiCode = wbxAppApiErrorCode;
149
+ this.meetingInfo = meetingInfo;
150
+ }
151
+ }
152
+
127
153
  /**
128
154
  * @class MeetingInfo
129
155
  */
@@ -177,6 +203,29 @@ export default class MeetingInfoV2 {
177
203
  }
178
204
  };
179
205
 
206
+ /**
207
+ * Raises a handleWebinarRegistrationError for webinar registration error codes
208
+ * @param {any} err the error from the request
209
+ * @returns {void}
210
+ */
211
+ handleWebinarRegistrationError = (err) => {
212
+ if (!err.body) {
213
+ return;
214
+ }
215
+
216
+ if (WEBINAR_REGISTRATION_ERROR_CODES.includes(err.body?.code)) {
217
+ Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.WEBINAR_REGISTRATION_ERROR, {
218
+ code: err.body?.code,
219
+ });
220
+
221
+ throw new MeetingInfoV2WebinarRegistrationError(
222
+ err.body?.code,
223
+ err.body?.data?.meetingInfo,
224
+ err.body?.message
225
+ );
226
+ }
227
+ };
228
+
180
229
  /**
181
230
  * Creates adhoc space meetings for a space by fetching the conversation infomation
182
231
  * @param {String} conversationUrl conversationUrl to start adhoc meeting on
@@ -237,6 +286,7 @@ export default class MeetingInfoV2 {
237
286
  })
238
287
  .catch((err) => {
239
288
  this.handlePolicyError(err);
289
+ this.handleWebinarRegistrationError(err);
240
290
 
241
291
  Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.ADHOC_MEETING_FAILURE, {
242
292
  reason: err.message,
@@ -391,6 +441,7 @@ export default class MeetingInfoV2 {
391
441
 
392
442
  if (err?.statusCode === 403) {
393
443
  this.handlePolicyError(err);
444
+ this.handleWebinarRegistrationError(err);
394
445
 
395
446
  Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.VERIFY_PASSWORD_ERROR, {
396
447
  reason: err.message,