@webex/plugin-meetings 3.0.0-beta.50 → 3.0.0-beta.52

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.
@@ -34,9 +34,11 @@ const Breakouts = WebexPlugin.extend({
34
34
  status: 'string', // only present when in a breakout session
35
35
  url: 'string', // appears from the moment you enable breakouts
36
36
  locusUrl: 'string', // the current locus url
37
- breakoutServiceUrl: 'string', // the current breakout resouce url
37
+ breakoutServiceUrl: 'string', // the current breakout resource url
38
38
  mainLocusUrl: 'string', // the locus url of the main session
39
39
  groups: 'array', // appears when create breakouts
40
+ editLock: 'object', // appears when getBreakout info editlock = true
41
+ intervalID: 'number',
40
42
  },
41
43
 
42
44
  children: {
@@ -115,7 +117,7 @@ const Breakouts = WebexPlugin.extend({
115
117
  },
116
118
 
117
119
  /**
118
- * Update the current breakout resouce url
120
+ * Update the current breakout resource url
119
121
  * @param {string} breakoutServiceUrl
120
122
  * @returns {void}
121
123
  */
@@ -336,7 +338,7 @@ const Breakouts = WebexPlugin.extend({
336
338
  },
337
339
 
338
340
  /**
339
- * Make the meeting enbale or disable breakout session
341
+ * Make the meeting enable or disable breakout session
340
342
  * @param {boolean} enable
341
343
  * @returns {Promise}
342
344
  */
@@ -376,17 +378,30 @@ const Breakouts = WebexPlugin.extend({
376
378
  */
377
379
  async create(sessions) {
378
380
  // @ts-ignore
381
+ const bodyInfo =
382
+ this.editLock && !!this.editLock.token
383
+ ? {
384
+ groups: [
385
+ {
386
+ sessions,
387
+ },
388
+ ],
389
+ editlock: {
390
+ token: this.editLock.token,
391
+ },
392
+ }
393
+ : {
394
+ groups: [
395
+ {
396
+ sessions,
397
+ },
398
+ ],
399
+ };
379
400
  const breakInfo = await this.webex
380
401
  .request({
381
402
  method: HTTP_VERBS.PUT,
382
403
  uri: this.url,
383
- body: {
384
- groups: [
385
- {
386
- sessions,
387
- },
388
- ],
389
- },
404
+ body: bodyInfo,
390
405
  })
391
406
  .catch((error) => {
392
407
  return Promise.reject(
@@ -398,6 +413,9 @@ const Breakouts = WebexPlugin.extend({
398
413
  this.set('groups', breakInfo.body.groups);
399
414
  }
400
415
 
416
+ // clear edit lock info after save breakout session info
417
+ this._clearEditLockInfo();
418
+
401
419
  return Promise.resolve(breakInfo);
402
420
  },
403
421
 
@@ -407,17 +425,30 @@ const Breakouts = WebexPlugin.extend({
407
425
  */
408
426
  async clearSessions() {
409
427
  // @ts-ignore
428
+ const bodyInfo =
429
+ this.editLock && !!this.editLock.token
430
+ ? {
431
+ groups: [
432
+ {
433
+ action: BREAKOUTS.ACTION.DELETE,
434
+ },
435
+ ],
436
+ editlock: {
437
+ token: this.editLock.token,
438
+ },
439
+ }
440
+ : {
441
+ groups: [
442
+ {
443
+ action: BREAKOUTS.ACTION.DELETE,
444
+ },
445
+ ],
446
+ };
410
447
  const breakInfo = await this.webex
411
448
  .request({
412
449
  method: HTTP_VERBS.PUT,
413
450
  uri: this.url,
414
- body: {
415
- groups: [
416
- {
417
- action: BREAKOUTS.ACTION.DELETE,
418
- },
419
- ],
420
- },
451
+ body: bodyInfo,
421
452
  })
422
453
  .catch((error) => {
423
454
  return Promise.reject(
@@ -503,9 +534,125 @@ const Breakouts = WebexPlugin.extend({
503
534
  if (breakout.body?.groups) {
504
535
  this.set('groups', breakout.body.groups);
505
536
  }
537
+ if (breakout.body?.editlock && editlock) {
538
+ this.set('editLock', breakout.body.editlock);
539
+ }
506
540
 
507
541
  return breakout;
508
542
  },
543
+
544
+ /**
545
+ * enable and edit lock breakout
546
+ * @returns {void}
547
+ */
548
+ async enableAndLockBreakout() {
549
+ if (this.enableBreakoutSession) {
550
+ this.lockBreakout();
551
+ } else {
552
+ const info = await this.enableBreakouts();
553
+
554
+ if (info.body) {
555
+ this.lockBreakout();
556
+ }
557
+ }
558
+ },
559
+
560
+ /**
561
+ * send breakout edit lock
562
+ * @returns {void}
563
+ */
564
+ async lockBreakout() {
565
+ if (this.editLock && !!this.editLock.token) {
566
+ if (this.editLock.state === BREAKOUTS.EDIT_LOCK_STATUS.LOCKED) {
567
+ throw new Error('Breakout already locked');
568
+ } else {
569
+ this.keepEditLockAlive();
570
+ }
571
+ } else {
572
+ const breakout = await this.getBreakout(true);
573
+ if (breakout.body?.editlock) {
574
+ this.keepEditLockAlive();
575
+ }
576
+ }
577
+ },
578
+
579
+ /**
580
+ * keep edit lock alive
581
+ * @returns {void}
582
+ */
583
+ keepEditLockAlive() {
584
+ if (this.editLock && !!this.editLock.token) {
585
+ const ttl = this.editLock.ttl < 30 ? BREAKOUTS.DEFAULT_TTL : this.editLock.ttl;
586
+
587
+ this.intervalID = window.setInterval(() => {
588
+ this.request({
589
+ method: HTTP_VERBS.PUT,
590
+ uri: `${this.url}/editlock/${this.editLock.token}`,
591
+ }).catch((error) => {
592
+ this._clearEditLockInfo();
593
+
594
+ return Promise.reject(boServiceErrorHandler(error, 'Breakouts#keepEditLockAlive'));
595
+ });
596
+ }, (ttl / 2) * 1000);
597
+ }
598
+ },
599
+
600
+ /**
601
+ * unlock edit breakout
602
+ * @returns {void}
603
+ */
604
+ unLockEditBreakout() {
605
+ this.request({
606
+ method: HTTP_VERBS.DELETE,
607
+ uri: `${this.url}/editlock/${this.editLock.token}`,
608
+ })
609
+ .then(() => {
610
+ this._clearEditLockInfo();
611
+ })
612
+ .catch((error) => {
613
+ return Promise.reject(boServiceErrorHandler(error, 'Breakouts#unLockEditBreakout'));
614
+ });
615
+ },
616
+
617
+ /**
618
+ * clear interval and edit lock info
619
+ * @private
620
+ * @returns {void}
621
+ */
622
+ _clearEditLockInfo() {
623
+ if (this.intervalID) {
624
+ clearInterval(this.intervalID);
625
+ }
626
+ this.set('editLock', {});
627
+ },
628
+
629
+ /**
630
+ * assign participants to breakout session
631
+ * @param {Array} sessions
632
+ * @returns {void}
633
+ */
634
+ assign(sessions: any[]) {
635
+ const internalSessions = sessions.map((item) => {
636
+ return {
637
+ id: item.id,
638
+ assigned: item.memberIds,
639
+ assignedEmails: item.emails,
640
+ };
641
+ });
642
+
643
+ return this.request({
644
+ method: HTTP_VERBS.PUT,
645
+ uri: this.url,
646
+ body: {
647
+ groups: [
648
+ {
649
+ id: this.breakoutGroupId,
650
+ sessions: internalSessions,
651
+ },
652
+ ],
653
+ },
654
+ });
655
+ },
509
656
  });
510
657
 
511
658
  export default Breakouts;
@@ -26,13 +26,13 @@ export const getBroadcastRoles = (options): string[] => {
26
26
  */
27
27
  export const boServiceErrorHandler = (error: any, message: string): any => {
28
28
  const errorCode = error?.body?.errorCode;
29
- const {EDIT_LOCK_TOKEN_MISMATCH} = BREAKOUTS.ERROR_CODE;
29
+ const {EDIT_LOCK_TOKEN_MISMATCH, EDIT_NOT_AUTHORIZED} = BREAKOUTS.ERROR_CODE;
30
+ LoggerProxy.logger.info(message);
30
31
  switch (errorCode) {
31
32
  case EDIT_LOCK_TOKEN_MISMATCH:
32
- LoggerProxy.logger.info(message);
33
-
34
33
  return new BreakoutEditLockedError('Edit lock token mismatch', error);
35
-
34
+ case EDIT_NOT_AUTHORIZED:
35
+ return new BreakoutEditLockedError('Not authorized to interact with edit lock', error);
36
36
  default:
37
37
  return error;
38
38
  }
package/src/constants.ts CHANGED
@@ -480,6 +480,7 @@ export const HTTP_VERBS = {
480
480
  POST: 'POST',
481
481
  GET: 'GET',
482
482
  PATCH: 'PATCH',
483
+ DELETE: 'DELETE',
483
484
  };
484
485
 
485
486
  // https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/iceGatheringState
@@ -556,8 +557,15 @@ export const BREAKOUTS = {
556
557
  },
557
558
  ERROR_CODE: {
558
559
  EDIT_LOCK_TOKEN_MISMATCH: 201409024,
560
+ EDIT_NOT_AUTHORIZED: 201403007,
559
561
  },
560
562
  DEFAULT_DURATION: 60000,
563
+ EDIT_LOCK_STATUS: {
564
+ LOCKED: 'LOCKED',
565
+ NOT_LOCKED: 'NOT_LOCKED',
566
+ UNKNOWN: 'UNKNOWN',
567
+ },
568
+ DEFAULT_TTL: 30,
561
569
  };
562
570
 
563
571
  export const LOCUSINFO = {
@@ -3,12 +3,10 @@ import Breakout from '@webex/plugin-meetings/src/breakouts/breakout';
3
3
  import Breakouts from '@webex/plugin-meetings/src/breakouts';
4
4
  import Members from '@webex/plugin-meetings/src/members';
5
5
  import MockWebex from '@webex/test-helper-mock-webex';
6
- import sinon from "sinon";
7
-
6
+ import sinon from 'sinon';
8
7
 
9
8
  describe('plugin-meetings', () => {
10
9
  describe('breakout', () => {
11
-
12
10
  let webex;
13
11
  let breakout;
14
12
  let breakouts;
@@ -29,34 +27,33 @@ describe('plugin-meetings', () => {
29
27
  describe('initialize', () => {
30
28
  it('creates the object correctly', () => {
31
29
  assert.instanceOf(breakout.members, Members);
32
- })
30
+ });
33
31
  });
34
32
 
35
33
  describe('#join', () => {
36
34
  it('makes the request as expected', async () => {
37
- const result = await breakout.join()
35
+ const result = await breakout.join();
38
36
 
39
37
  assert.calledOnceWithExactly(webex.request, {
40
38
  method: 'POST',
41
39
  uri: 'url/move',
42
40
  body: {
43
41
  groupId: 'groupId',
44
- sessionId: 'sessionId'
45
- }
42
+ sessionId: 'sessionId',
43
+ },
46
44
  });
47
45
 
48
- assert.equal(result, 'REQUEST_RETURN_VALUE')
46
+ assert.equal(result, 'REQUEST_RETURN_VALUE');
49
47
  });
50
48
  });
51
49
 
52
50
  describe('#leave', () => {
53
51
  it('throws error if in main sesson', async () => {
54
-
55
52
  breakout.set('sessionType', 'MAIN');
56
53
 
57
54
  const fn = () => {
58
55
  breakout.leave();
59
- }
56
+ };
60
57
 
61
58
  expect(fn).to.throw(/Cannot leave the main session/);
62
59
  });
@@ -64,14 +61,14 @@ describe('plugin-meetings', () => {
64
61
  it('throws error if there is no main session', async () => {
65
62
  const fn = () => {
66
63
  breakout.leave();
67
- }
64
+ };
68
65
 
69
66
  expect(fn).to.throw(/Cannot leave, no main session found/);
70
67
  });
71
68
 
72
69
  it('joins the main session if in a breakout', async () => {
73
70
  breakout.parent.breakouts.add({
74
- sessionType: 'MAIN'
71
+ sessionType: 'MAIN',
75
72
  });
76
73
 
77
74
  const mainSession = breakouts.breakouts.models[0];
@@ -82,58 +79,60 @@ describe('plugin-meetings', () => {
82
79
 
83
80
  assert.calledOnceWithExactly(mainSession.join);
84
81
  assert.equal(result, 'JOIN_RETURN_VALUE');
85
- })
82
+ });
86
83
  });
87
84
 
88
85
  describe('#askForHelp', () => {
89
86
  it('makes the request as expected', async () => {
90
- const result = await breakout.askForHelp()
87
+ const result = await breakout.askForHelp();
91
88
 
92
89
  assert.calledOnceWithExactly(webex.request, {
93
90
  method: 'POST',
94
91
  uri: 'url/help',
95
92
  body: {
96
93
  groupId: 'groupId',
97
- sessionId: 'sessionId'
98
- }
94
+ sessionId: 'sessionId',
95
+ },
99
96
  });
100
97
 
101
- assert.equal(result, 'REQUEST_RETURN_VALUE')
98
+ assert.equal(result, 'REQUEST_RETURN_VALUE');
102
99
  });
103
100
  });
104
101
 
105
102
  describe('#broadcast', () => {
106
103
  it('makes the request as expected', async () => {
107
- breakout.breakoutRequest.broadcast = sinon.stub().returns(Promise.resolve('REQUEST_RETURN_VALUE'));
108
- let result = await breakout.broadcast('hello')
104
+ breakout.breakoutRequest.broadcast = sinon
105
+ .stub()
106
+ .returns(Promise.resolve('REQUEST_RETURN_VALUE'));
107
+ let result = await breakout.broadcast('hello');
109
108
  assert.calledWithExactly(breakout.breakoutRequest.broadcast, {
110
109
  url: 'url',
111
110
  message: 'hello',
112
111
  options: undefined,
113
112
  groupId: 'groupId',
114
- sessionId: 'sessionId'
113
+ sessionId: 'sessionId',
115
114
  });
116
115
 
117
- assert.equal(result, 'REQUEST_RETURN_VALUE')
116
+ assert.equal(result, 'REQUEST_RETURN_VALUE');
118
117
 
119
- result = await breakout.broadcast('hello', {presenters: true, cohosts: true})
118
+ result = await breakout.broadcast('hello', {presenters: true, cohosts: true});
120
119
 
121
120
  assert.calledWithExactly(breakout.breakoutRequest.broadcast, {
122
121
  url: 'url',
123
122
  message: 'hello',
124
123
  options: {presenters: true, cohosts: true},
125
124
  groupId: 'groupId',
126
- sessionId: 'sessionId'
125
+ sessionId: 'sessionId',
127
126
  });
128
127
 
129
- assert.equal(result, 'REQUEST_RETURN_VALUE')
128
+ assert.equal(result, 'REQUEST_RETURN_VALUE');
130
129
  });
131
130
  });
132
131
 
133
132
  describe('#parseRoster', () => {
134
133
  it('calls locusParticipantsUpdate', () => {
135
134
  breakout.members = {
136
- locusParticipantsUpdate: sinon.stub()
135
+ locusParticipantsUpdate: sinon.stub(),
137
136
  };
138
137
 
139
138
  const locusData = {some: 'data'};
@@ -141,7 +140,7 @@ describe('plugin-meetings', () => {
141
140
 
142
141
  assert.calledOnceWithExactly(breakout.members.locusParticipantsUpdate, locusData);
143
142
  assert.equal(result, undefined);
144
- })
145
- })
143
+ });
144
+ });
146
145
  });
147
146
  });