@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.
- package/dist/breakouts/breakout.js +2 -2
- package/dist/breakouts/breakout.js.map +1 -1
- package/dist/breakouts/index.js +195 -26
- package/dist/breakouts/index.js.map +1 -1
- package/dist/breakouts/utils.js +6 -2
- package/dist/breakouts/utils.js.map +1 -1
- package/dist/constants.js +11 -3
- package/dist/constants.js.map +1 -1
- package/dist/types/constants.d.ts +8 -0
- package/package.json +18 -18
- package/src/breakouts/README.md +13 -4
- package/src/breakouts/breakout.ts +1 -2
- package/src/breakouts/index.ts +164 -17
- package/src/breakouts/utils.ts +4 -4
- package/src/constants.ts +8 -0
- package/test/unit/spec/breakouts/breakout.ts +26 -27
- package/test/unit/spec/breakouts/index.ts +346 -88
package/src/breakouts/index.ts
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
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;
|
package/src/breakouts/utils.ts
CHANGED
|
@@ -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
|
|
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
|
|
108
|
-
|
|
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
|
});
|