@webex/plugin-meetings 3.10.0-next.16 → 3.10.0-next.18

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.
@@ -448,7 +448,7 @@ var Webinar = _webexCore.WebexPlugin.extend({
448
448
  }, _callee7);
449
449
  }))();
450
450
  },
451
- version: "3.10.0-next.16"
451
+ version: "3.10.0-next.18"
452
452
  });
453
453
  var _default = exports.default = Webinar;
454
454
  //# sourceMappingURL=index.js.map
package/package.json CHANGED
@@ -43,7 +43,7 @@
43
43
  "@webex/eslint-config-legacy": "0.0.0",
44
44
  "@webex/jest-config-legacy": "0.0.0",
45
45
  "@webex/legacy-tools": "0.0.0",
46
- "@webex/plugin-rooms": "3.10.0-next.7",
46
+ "@webex/plugin-rooms": "3.10.0-next.8",
47
47
  "@webex/test-helper-chai": "3.10.0-next.1",
48
48
  "@webex/test-helper-mocha": "3.10.0-next.1",
49
49
  "@webex/test-helper-mock-webex": "3.10.0-next.1",
@@ -63,20 +63,20 @@
63
63
  "@webex/common": "3.10.0-next.1",
64
64
  "@webex/event-dictionary-ts": "^1.0.1930",
65
65
  "@webex/internal-media-core": "2.20.3",
66
- "@webex/internal-plugin-conversation": "3.10.0-next.7",
67
- "@webex/internal-plugin-device": "3.10.0-next.7",
68
- "@webex/internal-plugin-llm": "3.10.0-next.7",
69
- "@webex/internal-plugin-mercury": "3.10.0-next.7",
70
- "@webex/internal-plugin-metrics": "3.10.0-next.7",
71
- "@webex/internal-plugin-support": "3.10.0-next.7",
72
- "@webex/internal-plugin-user": "3.10.0-next.7",
73
- "@webex/internal-plugin-voicea": "3.10.0-next.9",
66
+ "@webex/internal-plugin-conversation": "3.10.0-next.8",
67
+ "@webex/internal-plugin-device": "3.10.0-next.8",
68
+ "@webex/internal-plugin-llm": "3.10.0-next.8",
69
+ "@webex/internal-plugin-mercury": "3.10.0-next.8",
70
+ "@webex/internal-plugin-metrics": "3.10.0-next.8",
71
+ "@webex/internal-plugin-support": "3.10.0-next.8",
72
+ "@webex/internal-plugin-user": "3.10.0-next.8",
73
+ "@webex/internal-plugin-voicea": "3.10.0-next.10",
74
74
  "@webex/media-helpers": "3.10.0-next.3",
75
- "@webex/plugin-people": "3.10.0-next.7",
76
- "@webex/plugin-rooms": "3.10.0-next.7",
75
+ "@webex/plugin-people": "3.10.0-next.8",
76
+ "@webex/plugin-rooms": "3.10.0-next.8",
77
77
  "@webex/ts-sdp": "^1.8.1",
78
78
  "@webex/web-capabilities": "^1.7.1",
79
- "@webex/webex-core": "3.10.0-next.7",
79
+ "@webex/webex-core": "3.10.0-next.8",
80
80
  "ampersand-collection": "^2.0.2",
81
81
  "bowser": "^2.11.0",
82
82
  "btoa": "^1.2.1",
@@ -93,5 +93,5 @@
93
93
  "//": [
94
94
  "TODO: upgrade jwt-decode when moving to node 18"
95
95
  ],
96
- "version": "3.10.0-next.16"
96
+ "version": "3.10.0-next.18"
97
97
  }
@@ -18,6 +18,7 @@ import {
18
18
  CALL_REMOVED_REASON,
19
19
  RECORDING_STATE,
20
20
  Enum,
21
+ SELF_ROLES,
21
22
  } from '../constants';
22
23
 
23
24
  import InfoUtils from './infoUtils';
@@ -512,6 +513,14 @@ export default class LocusInfo extends EventsScope {
512
513
  updateLocusFromHashTreeObject(object: HashTreeObject, locus: LocusDTO): LocusDTO {
513
514
  const type = object.htMeta.elementId.type.toLowerCase();
514
515
 
516
+ const addParticipantObject = (obj: HashTreeObject) => {
517
+ if (!locus.participants) {
518
+ locus.participants = [];
519
+ }
520
+ locus.participants.push(obj.data);
521
+ this.hashTreeObjectId2ParticipantId.set(obj.htMeta.elementId.id, obj.data.id);
522
+ };
523
+
515
524
  switch (type) {
516
525
  case ObjectType.locus: {
517
526
  if (!object.data) {
@@ -577,13 +586,7 @@ export default class LocusInfo extends EventsScope {
577
586
  } ${object.data ? 'updated' : 'removed'} version=${object.htMeta.elementId.version}`
578
587
  );
579
588
  if (object.data) {
580
- if (!locus.participants) {
581
- locus.participants = [];
582
- }
583
- const participantObject = object.data;
584
- participantObject.htMeta = object.htMeta;
585
- locus.participants.push(participantObject);
586
- this.hashTreeObjectId2ParticipantId.set(object.htMeta.elementId.id, participantObject.id);
589
+ addParticipantObject(object);
587
590
  } else {
588
591
  const participantId = this.hashTreeObjectId2ParticipantId.get(object.htMeta.elementId.id);
589
592
 
@@ -610,6 +613,24 @@ export default class LocusInfo extends EventsScope {
610
613
  );
611
614
  const locusDtoKey = ObjectTypeToLocusKeyMap[type];
612
615
  locus[locusDtoKey] = object.data;
616
+
617
+ /* Hash tree based webinar attendees don't receive a Participant object for themselves from Locus,
618
+ but a lot of existing code in SDK and web app expects a member object for self to exist,
619
+ so whenever SELF changes for a webinar attendee, we copy it into a participant object.
620
+ We can do it, because SELF has always all the same properties as a participant object.
621
+ */
622
+ if (
623
+ type === ObjectType.self &&
624
+ locus.info?.isWebinar &&
625
+ object.data.controls?.role?.roles?.find(
626
+ (r) => r.type === SELF_ROLES.ATTENDEE && r.hasRole
627
+ )
628
+ ) {
629
+ LoggerProxy.logger.info(
630
+ `Locus-info:index#updateLocusFromHashTreeObject --> webinar attendee: creating participant object from self`
631
+ );
632
+ addParticipantObject(object);
633
+ }
613
634
  }
614
635
  break;
615
636
  default:
@@ -1,5 +1,5 @@
1
1
  import uuid from 'uuid';
2
- import {cloneDeep, isEqual, isEmpty} from 'lodash';
2
+ import {cloneDeep, isEqual, isEmpty, merge} from 'lodash';
3
3
  import jwtDecode from 'jwt-decode';
4
4
  // @ts-ignore - Fix this
5
5
  import {StatelessWebexPlugin} from '@webex/webex-core';
@@ -50,6 +50,11 @@ import {
50
50
  type MeetingTranscriptPayload,
51
51
  } from '@webex/internal-plugin-voicea';
52
52
 
53
+ import {
54
+ getBrowserMediaErrorCode,
55
+ isBrowserMediaError,
56
+ isBrowserMediaErrorName,
57
+ } from '@webex/internal-plugin-metrics/src/call-diagnostic/call-diagnostic-metrics.util';
53
58
  import {processNewCaptions} from './voicea-meeting';
54
59
 
55
60
  import {
@@ -5440,6 +5445,20 @@ export default class Meeting extends StatelessWebexPlugin {
5440
5445
  shouldRetry = false;
5441
5446
  }
5442
5447
 
5448
+ if (CallDiagnosticUtils.isBrowserMediaError(error)) {
5449
+ shouldRetry = false;
5450
+ // eslint-disable-next-line no-ex-assign
5451
+ error = merge({
5452
+ error: {
5453
+ body: {
5454
+ errorCode: CallDiagnosticUtils.getBrowserMediaErrorCode(error),
5455
+ message: error?.message,
5456
+ name: error?.name,
5457
+ },
5458
+ },
5459
+ });
5460
+ }
5461
+
5443
5462
  // we only want to call leave if join was successful and this was a retry or we won't be doing any more retries
5444
5463
  if (joined && (isRetry || !shouldRetry)) {
5445
5464
  try {
@@ -358,6 +358,76 @@ describe('plugin-meetings', () => {
358
358
  });
359
359
  });
360
360
 
361
+ it('should process locus update correctly when called with updated SELF (webinar non-attendee)', () => {
362
+ const newSelf = {
363
+ id: 'new-self',
364
+ visibleDataSets: ['dataset1', 'dataset2'],
365
+ controls: {
366
+ role: {
367
+ roles: [
368
+ {type: 'PANELIST', hasRole: true},
369
+ {type: 'ATTENDEE', hasRole: false},
370
+ ],
371
+ },
372
+ },
373
+ };
374
+ locusInfo.info.isWebinar = true;
375
+
376
+ // simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
377
+ locusInfoUpdateCallback(OBJECTS_UPDATED, {
378
+ updatedObjects: [{htMeta: {elementId: {type: 'self'}}, data: newSelf}],
379
+ });
380
+
381
+ // check onDeltaLocus() was called with correctly updated locus info
382
+ // without any participant generated
383
+ assert.calledOnceWithExactly(onDeltaLocusStub, {
384
+ ...expectedLocusInfo,
385
+ info: {
386
+ ...expectedLocusInfo.info,
387
+ isWebinar: true,
388
+ },
389
+ self: newSelf,
390
+ });
391
+ });
392
+
393
+ it('should generate a participant when called with updated SELF for webinar attendee', () => {
394
+ const newSelf = {
395
+ id: 'new-self',
396
+ visibleDataSets: ['dataset1', 'dataset2'],
397
+ controls: {
398
+ role: {
399
+ roles: [
400
+ {type: 'something else - should be ignored', hasRole: true},
401
+ {type: 'ATTENDEE', hasRole: true},
402
+ ],
403
+ },
404
+ },
405
+ };
406
+
407
+ locusInfo.info.isWebinar = true;
408
+
409
+ // simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
410
+ locusInfoUpdateCallback(OBJECTS_UPDATED, {
411
+ updatedObjects: [{htMeta: {elementId: {type: 'self'}}, data: newSelf}],
412
+ });
413
+
414
+ // check onDeltaLocus() was called with correctly updated locus info
415
+ // that contains a participant created from self
416
+ assert.calledOnceWithExactly(onDeltaLocusStub, {
417
+ ...expectedLocusInfo,
418
+ info: {
419
+ ...expectedLocusInfo.info,
420
+ isWebinar: true,
421
+ },
422
+ self: newSelf,
423
+ participants: [
424
+ {
425
+ ...newSelf,
426
+ },
427
+ ],
428
+ });
429
+ });
430
+
361
431
  it('should process locus update correctly when called with updated fullState', () => {
362
432
  const newFullState = {
363
433
  id: 'new-fullState',
@@ -1003,6 +1003,35 @@ describe('plugin-meetings', () => {
1003
1003
  );
1004
1004
  });
1005
1005
 
1006
+ it('should call leave() if addMediaInternal() fails ', async () => {
1007
+ const addMediaError = new Error('fake addMedia error');
1008
+ addMediaError.name = 'TypeError';
1009
+
1010
+ const rejectError = {
1011
+ error: {
1012
+ body: {
1013
+ errorCode: 2729,
1014
+ message: 'fake addMedia error',
1015
+ name: 'TypeError'
1016
+ }
1017
+ }
1018
+ };
1019
+ meeting.addMediaInternal.rejects(addMediaError);
1020
+ sinon.stub(meeting, 'leave').resolves();
1021
+
1022
+ await assert.isRejected(
1023
+ meeting.joinWithMedia({
1024
+ joinOptions,
1025
+ mediaOptions,
1026
+ }),
1027
+ rejectError
1028
+ );
1029
+
1030
+ assert.calledOnce(meeting.join);
1031
+ assert.calledOnce(meeting.addMediaInternal);
1032
+ assert.calledOnce(Metrics.sendBehavioralMetric);
1033
+ });
1034
+
1006
1035
  it('should not call leave() if addMediaInternal() fails the first time and succeeds the second time and should only call join() once', async () => {
1007
1036
  const addMediaError = new Error('fake addMedia error');
1008
1037
  const leaveStub = sinon.stub(meeting, 'leave');