@webex/plugin-meetings 3.0.0-beta.169 → 3.0.0-beta.170
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 +1 -1
- package/dist/breakouts/index.js +1 -1
- package/dist/constants.js +24 -1
- package/dist/constants.js.map +1 -1
- package/dist/interpretation/index.js +1 -1
- package/dist/interpretation/siLanguage.js +1 -1
- package/dist/meeting/index.js +20 -4
- package/dist/meeting/index.js.map +1 -1
- package/dist/meeting/util.js +6 -0
- package/dist/meeting/util.js.map +1 -1
- package/dist/recording-controller/index.js +21 -1
- package/dist/recording-controller/index.js.map +1 -1
- package/dist/recording-controller/util.js +9 -8
- package/dist/recording-controller/util.js.map +1 -1
- package/dist/types/constants.d.ts +21 -0
- package/dist/types/meeting/index.d.ts +7 -0
- package/dist/types/meeting/util.d.ts +2 -0
- package/dist/types/recording-controller/index.d.ts +15 -0
- package/dist/types/recording-controller/util.d.ts +5 -4
- package/package.json +20 -19
- package/src/constants.ts +22 -0
- package/src/meeting/index.ts +29 -4
- package/src/meeting/util.ts +9 -0
- package/src/recording-controller/index.ts +20 -2
- package/src/recording-controller/util.ts +26 -9
- package/test/unit/spec/meeting/index.js +63 -2
- package/test/unit/spec/meeting/utils.js +29 -0
- package/test/unit/spec/recording-controller/index.js +294 -218
- package/test/unit/spec/recording-controller/util.js +223 -96
package/src/meeting/index.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import uuid from 'uuid';
|
|
2
2
|
import {cloneDeep, isEqual, defer, isEmpty} from 'lodash';
|
|
3
|
+
import jwt from 'jsonwebtoken';
|
|
3
4
|
// @ts-ignore - Fix this
|
|
4
5
|
import {StatelessWebexPlugin} from '@webex/webex-core';
|
|
5
6
|
import {ClientEvent, CALL_DIAGNOSTIC_CONFIG} from '@webex/internal-plugin-metrics';
|
|
@@ -508,6 +509,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
508
509
|
controlsOptionsManager: ControlsOptionsManager;
|
|
509
510
|
requiredCaptcha: any;
|
|
510
511
|
receiveSlotManager: ReceiveSlotManager;
|
|
512
|
+
selfUserPolicies: any;
|
|
511
513
|
shareStatus: string;
|
|
512
514
|
statsAnalyzer: StatsAnalyzer;
|
|
513
515
|
transcription: Transcription;
|
|
@@ -2426,10 +2428,22 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
2426
2428
|
),
|
|
2427
2429
|
canSetMuted: ControlsOptionsUtil.canSetMuted(payload.info.userDisplayHints),
|
|
2428
2430
|
canUnsetMuted: ControlsOptionsUtil.canUnsetMuted(payload.info.userDisplayHints),
|
|
2429
|
-
canStartRecording: RecordingUtil.canUserStart(
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2431
|
+
canStartRecording: RecordingUtil.canUserStart(
|
|
2432
|
+
payload.info.userDisplayHints,
|
|
2433
|
+
this.selfUserPolicies
|
|
2434
|
+
),
|
|
2435
|
+
canStopRecording: RecordingUtil.canUserStop(
|
|
2436
|
+
payload.info.userDisplayHints,
|
|
2437
|
+
this.selfUserPolicies
|
|
2438
|
+
),
|
|
2439
|
+
canPauseRecording: RecordingUtil.canUserPause(
|
|
2440
|
+
payload.info.userDisplayHints,
|
|
2441
|
+
this.selfUserPolicies
|
|
2442
|
+
),
|
|
2443
|
+
canResumeRecording: RecordingUtil.canUserResume(
|
|
2444
|
+
payload.info.userDisplayHints,
|
|
2445
|
+
this.selfUserPolicies
|
|
2446
|
+
),
|
|
2433
2447
|
canRaiseHand: MeetingUtil.canUserRaiseHand(payload.info.userDisplayHints),
|
|
2434
2448
|
canLowerAllHands: MeetingUtil.canUserLowerAllHands(payload.info.userDisplayHints),
|
|
2435
2449
|
canLowerSomeoneElsesHand: MeetingUtil.canUserLowerSomeoneElsesHand(
|
|
@@ -2564,6 +2578,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
2564
2578
|
});
|
|
2565
2579
|
|
|
2566
2580
|
this.recordingController.setDisplayHints(payload.info.userDisplayHints);
|
|
2581
|
+
this.recordingController.setUserPolicy(this.selfUserPolicies);
|
|
2567
2582
|
this.controlsOptionsManager.setDisplayHints(payload.info.userDisplayHints);
|
|
2568
2583
|
|
|
2569
2584
|
if (changed) {
|
|
@@ -3128,11 +3143,21 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3128
3143
|
webexMeetingInfo?.hostId ||
|
|
3129
3144
|
this.owner;
|
|
3130
3145
|
this.permissionToken = webexMeetingInfo?.permissionToken;
|
|
3146
|
+
this.setSelfUserPolicies(this.permissionToken);
|
|
3131
3147
|
// Need to populate environment when sending CA event
|
|
3132
3148
|
this.environment = locusMeetingObject?.info.channel || webexMeetingInfo?.channel;
|
|
3133
3149
|
}
|
|
3134
3150
|
}
|
|
3135
3151
|
|
|
3152
|
+
/**
|
|
3153
|
+
* Sets the self user policies based on the contents of the permission token
|
|
3154
|
+
* @param {String} permissionToken
|
|
3155
|
+
* @returns {void}
|
|
3156
|
+
*/
|
|
3157
|
+
setSelfUserPolicies(permissionToken: string) {
|
|
3158
|
+
this.selfUserPolicies = jwt.decode(permissionToken)?.permission?.userPolicies;
|
|
3159
|
+
}
|
|
3160
|
+
|
|
3136
3161
|
/**
|
|
3137
3162
|
* Sets the sip uri on the class instance
|
|
3138
3163
|
* uses meeting info as precedence
|
package/src/meeting/util.ts
CHANGED
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
PASSWORD_STATUS,
|
|
11
11
|
DISPLAY_HINTS,
|
|
12
12
|
FULL_STATE,
|
|
13
|
+
SELF_POLICY,
|
|
13
14
|
} from '../constants';
|
|
14
15
|
import IntentToJoinError from '../common/errors/intent-to-join';
|
|
15
16
|
import JoinMeetingError from '../common/errors/join-meeting';
|
|
@@ -557,6 +558,14 @@ const MeetingUtil = {
|
|
|
557
558
|
|
|
558
559
|
return locusDeltaRequest;
|
|
559
560
|
},
|
|
561
|
+
|
|
562
|
+
selfSupportsFeature: (feature: SELF_POLICY, userPolicies: Record<SELF_POLICY, boolean>) => {
|
|
563
|
+
if (!userPolicies) {
|
|
564
|
+
return true;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
return userPolicies[feature];
|
|
568
|
+
},
|
|
560
569
|
};
|
|
561
570
|
|
|
562
571
|
export default MeetingUtil;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import PermissionError from '../common/errors/permission';
|
|
2
|
-
import {CONTROLS, HTTP_VERBS} from '../constants';
|
|
2
|
+
import {CONTROLS, HTTP_VERBS, SELF_POLICY} from '../constants';
|
|
3
3
|
import MeetingRequest from '../meeting/request';
|
|
4
4
|
import RecordingAction from './enums';
|
|
5
5
|
import Util from './util';
|
|
@@ -28,6 +28,14 @@ export default class RecordingController {
|
|
|
28
28
|
*/
|
|
29
29
|
private displayHints: Array<string> = [];
|
|
30
30
|
|
|
31
|
+
/**
|
|
32
|
+
* @instance
|
|
33
|
+
* @type {Object}
|
|
34
|
+
* @private
|
|
35
|
+
* @memberof RecordingInfo
|
|
36
|
+
*/
|
|
37
|
+
private selfUserPolicies: Record<SELF_POLICY, boolean>;
|
|
38
|
+
|
|
31
39
|
/**
|
|
32
40
|
* @instance
|
|
33
41
|
* @type {string}
|
|
@@ -126,6 +134,16 @@ export default class RecordingController {
|
|
|
126
134
|
this.displayHints = hints;
|
|
127
135
|
}
|
|
128
136
|
|
|
137
|
+
/**
|
|
138
|
+
* @param {Object} selfUserPolicies
|
|
139
|
+
* @returns {void}
|
|
140
|
+
* @public
|
|
141
|
+
* @memberof RecordingController
|
|
142
|
+
*/
|
|
143
|
+
public setUserPolicy(selfUserPolicies: Record<SELF_POLICY, boolean>) {
|
|
144
|
+
this.selfUserPolicies = selfUserPolicies;
|
|
145
|
+
}
|
|
146
|
+
|
|
129
147
|
/**
|
|
130
148
|
* @param {string} id
|
|
131
149
|
* @returns {void}
|
|
@@ -264,7 +282,7 @@ export default class RecordingController {
|
|
|
264
282
|
);
|
|
265
283
|
|
|
266
284
|
// assumes action is proper cased (i.e., Example)
|
|
267
|
-
if (Util?.[`canUser${action}`](this.displayHints)) {
|
|
285
|
+
if (Util?.[`canUser${action}`](this.displayHints, this.selfUserPolicies)) {
|
|
268
286
|
if (this.serviceUrl) {
|
|
269
287
|
return this.recordingService(action);
|
|
270
288
|
}
|
|
@@ -1,17 +1,34 @@
|
|
|
1
|
-
import {DISPLAY_HINTS} from '../constants';
|
|
1
|
+
import {DISPLAY_HINTS, SELF_POLICY} from '../constants';
|
|
2
2
|
import RecordingAction from './enums';
|
|
3
|
+
import MeetingUtil from '../meeting/util';
|
|
3
4
|
|
|
4
|
-
const canUserStart = (
|
|
5
|
-
displayHints
|
|
5
|
+
const canUserStart = (
|
|
6
|
+
displayHints: Array<string>,
|
|
7
|
+
userPolicies: Record<SELF_POLICY, boolean>
|
|
8
|
+
): boolean =>
|
|
9
|
+
displayHints.includes(DISPLAY_HINTS.RECORDING_CONTROL_START) &&
|
|
10
|
+
MeetingUtil.selfSupportsFeature(SELF_POLICY.SUPPORT_NETWORK_BASED_RECORD, userPolicies);
|
|
6
11
|
|
|
7
|
-
const canUserPause = (
|
|
8
|
-
displayHints
|
|
12
|
+
const canUserPause = (
|
|
13
|
+
displayHints: Array<string>,
|
|
14
|
+
userPolicies: Record<SELF_POLICY, boolean>
|
|
15
|
+
): boolean =>
|
|
16
|
+
displayHints.includes(DISPLAY_HINTS.RECORDING_CONTROL_PAUSE) &&
|
|
17
|
+
MeetingUtil.selfSupportsFeature(SELF_POLICY.SUPPORT_NETWORK_BASED_RECORD, userPolicies);
|
|
9
18
|
|
|
10
|
-
const canUserResume = (
|
|
11
|
-
displayHints
|
|
19
|
+
const canUserResume = (
|
|
20
|
+
displayHints: Array<string>,
|
|
21
|
+
userPolicies: Record<SELF_POLICY, boolean>
|
|
22
|
+
): boolean =>
|
|
23
|
+
displayHints.includes(DISPLAY_HINTS.RECORDING_CONTROL_RESUME) &&
|
|
24
|
+
MeetingUtil.selfSupportsFeature(SELF_POLICY.SUPPORT_NETWORK_BASED_RECORD, userPolicies);
|
|
12
25
|
|
|
13
|
-
const canUserStop = (
|
|
14
|
-
displayHints
|
|
26
|
+
const canUserStop = (
|
|
27
|
+
displayHints: Array<string>,
|
|
28
|
+
userPolicies: Record<SELF_POLICY, boolean>
|
|
29
|
+
): boolean =>
|
|
30
|
+
displayHints.includes(DISPLAY_HINTS.RECORDING_CONTROL_STOP) &&
|
|
31
|
+
MeetingUtil.selfSupportsFeature(SELF_POLICY.SUPPORT_NETWORK_BASED_RECORD, userPolicies);
|
|
15
32
|
|
|
16
33
|
const extractLocusId = (url: string) => {
|
|
17
34
|
return url?.split('/').pop();
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.
|
|
3
3
|
*/
|
|
4
4
|
import 'jsdom-global/register';
|
|
5
|
+
import jwt from 'jsonwebtoken';
|
|
5
6
|
import {cloneDeep, forEach, isEqual} from 'lodash';
|
|
6
7
|
import sinon from 'sinon';
|
|
7
8
|
import * as internalMediaModule from '@webex/internal-media-core';
|
|
@@ -5107,6 +5108,56 @@ describe('plugin-meetings', () => {
|
|
|
5107
5108
|
assert.equal(meeting.sipUri, test1);
|
|
5108
5109
|
});
|
|
5109
5110
|
});
|
|
5111
|
+
|
|
5112
|
+
describe('#setSelfUserPolicies', () => {
|
|
5113
|
+
it('sets correctly when policy data is present in token', () => {
|
|
5114
|
+
assert.notOk(meeting.selfUserPolicies);
|
|
5115
|
+
|
|
5116
|
+
const dummyToken = 'some data';
|
|
5117
|
+
const policyData = {permission: {userPolicies: {a: true}}};
|
|
5118
|
+
|
|
5119
|
+
sinon.stub(jwt, 'decode').returns(policyData);
|
|
5120
|
+
|
|
5121
|
+
meeting.setSelfUserPolicies(dummyToken);
|
|
5122
|
+
|
|
5123
|
+
assert.deepEqual(meeting.selfUserPolicies, {a: true});
|
|
5124
|
+
});
|
|
5125
|
+
|
|
5126
|
+
it('handles missing permission data', () => {
|
|
5127
|
+
assert.notOk(meeting.selfUserPolicies);
|
|
5128
|
+
|
|
5129
|
+
const dummyToken = 'some data';
|
|
5130
|
+
const policyData = {};
|
|
5131
|
+
|
|
5132
|
+
sinon.stub(jwt, 'decode').returns(policyData);
|
|
5133
|
+
|
|
5134
|
+
meeting.setSelfUserPolicies(dummyToken);
|
|
5135
|
+
|
|
5136
|
+
assert.deepEqual(meeting.selfUserPolicies, undefined);
|
|
5137
|
+
});
|
|
5138
|
+
|
|
5139
|
+
it('handles missing policy data', () => {
|
|
5140
|
+
assert.notOk(meeting.selfUserPolicies);
|
|
5141
|
+
|
|
5142
|
+
const dummyToken = 'some data';
|
|
5143
|
+
const policyData = {permission: {}};
|
|
5144
|
+
|
|
5145
|
+
sinon.stub(jwt, 'decode').returns(policyData);
|
|
5146
|
+
|
|
5147
|
+
meeting.setSelfUserPolicies(dummyToken);
|
|
5148
|
+
|
|
5149
|
+
assert.deepEqual(meeting.selfUserPolicies, undefined);
|
|
5150
|
+
});
|
|
5151
|
+
|
|
5152
|
+
it('handles missing token', () => {
|
|
5153
|
+
assert.notOk(meeting.selfUserPolicies);
|
|
5154
|
+
|
|
5155
|
+
meeting.setSelfUserPolicies();
|
|
5156
|
+
|
|
5157
|
+
assert.deepEqual(meeting.selfUserPolicies, undefined);
|
|
5158
|
+
});
|
|
5159
|
+
});
|
|
5160
|
+
|
|
5110
5161
|
describe('#unsetRemoteTracks', () => {
|
|
5111
5162
|
it('should unset the remote tracks and return null', () => {
|
|
5112
5163
|
meeting.mediaProperties.unsetRemoteTracks = sinon.stub().returns(true);
|
|
@@ -5142,6 +5193,7 @@ describe('plugin-meetings', () => {
|
|
|
5142
5193
|
assert.calledOnce(meeting.mediaProperties.unsetPeerConnection);
|
|
5143
5194
|
});
|
|
5144
5195
|
});
|
|
5196
|
+
|
|
5145
5197
|
describe('#parseMeetingInfo', () => {
|
|
5146
5198
|
const checkParseMeetingInfo = (expectedInfoToParse) => {
|
|
5147
5199
|
assert.equal(meeting.conversationUrl, expectedInfoToParse.conversationUrl);
|
|
@@ -5151,6 +5203,7 @@ describe('plugin-meetings', () => {
|
|
|
5151
5203
|
assert.equal(meeting.meetingJoinUrl, expectedInfoToParse.meetingJoinUrl);
|
|
5152
5204
|
assert.equal(meeting.owner, expectedInfoToParse.owner);
|
|
5153
5205
|
assert.equal(meeting.permissionToken, expectedInfoToParse.permissionToken);
|
|
5206
|
+
assert.deepEqual(meeting.selfUserPolicies, expectedInfoToParse.selfUserPolicies);
|
|
5154
5207
|
};
|
|
5155
5208
|
|
|
5156
5209
|
it('should parse meeting info from api return when locus meeting object is not available, set values, and return null', () => {
|
|
@@ -5162,7 +5215,8 @@ describe('plugin-meetings', () => {
|
|
|
5162
5215
|
locusUrl: url1,
|
|
5163
5216
|
meetingJoinUrl: url2,
|
|
5164
5217
|
meetingNumber: '12345',
|
|
5165
|
-
permissionToken:
|
|
5218
|
+
permissionToken:
|
|
5219
|
+
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwZXJtaXNzaW9uIjp7InVzZXJQb2xpY2llcyI6eyJhIjp0cnVlfX0sImlhdCI6MTY4OTE2NDEwMn0.9uL_U7QUdYyMerrgHC_gCKOax2j_bz04u8Ikbv9KiXU',
|
|
5166
5220
|
sipMeetingUri: test1,
|
|
5167
5221
|
sipUrl: test1,
|
|
5168
5222
|
owner: test2,
|
|
@@ -5177,7 +5231,9 @@ describe('plugin-meetings', () => {
|
|
|
5177
5231
|
meetingNumber: '12345',
|
|
5178
5232
|
meetingJoinUrl: url2,
|
|
5179
5233
|
owner: test2,
|
|
5180
|
-
|
|
5234
|
+
selfUserPolicies: {a: true},
|
|
5235
|
+
permissionToken:
|
|
5236
|
+
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwZXJtaXNzaW9uIjp7InVzZXJQb2xpY2llcyI6eyJhIjp0cnVlfX0sImlhdCI6MTY4OTE2NDEwMn0.9uL_U7QUdYyMerrgHC_gCKOax2j_bz04u8Ikbv9KiXU',
|
|
5181
5237
|
};
|
|
5182
5238
|
|
|
5183
5239
|
checkParseMeetingInfo(expectedInfoToParse);
|
|
@@ -5398,6 +5454,9 @@ describe('plugin-meetings', () => {
|
|
|
5398
5454
|
const restorableHasHints = ControlsOptionsUtil.hasHints;
|
|
5399
5455
|
ControlsOptionsUtil.hasHints = sinon.stub().returns(true);
|
|
5400
5456
|
|
|
5457
|
+
const setUserPolicySpy = sinon.spy(meeting.recordingController, 'setUserPolicy');
|
|
5458
|
+
meeting.selfUserPolicies = {a: true};
|
|
5459
|
+
|
|
5401
5460
|
meeting.setUpLocusInfoMeetingInfoListener();
|
|
5402
5461
|
|
|
5403
5462
|
assert.calledThrice(locusInfoOnSpy);
|
|
@@ -5509,6 +5568,8 @@ describe('plugin-meetings', () => {
|
|
|
5509
5568
|
displayHints: payload.info.userDisplayHints,
|
|
5510
5569
|
});
|
|
5511
5570
|
|
|
5571
|
+
assert.calledWith(setUserPolicySpy, {a: true});
|
|
5572
|
+
|
|
5512
5573
|
assert.calledWith(
|
|
5513
5574
|
TriggerProxy.trigger,
|
|
5514
5575
|
meeting,
|
|
@@ -3,6 +3,8 @@ import {assert} from '@webex/test-helper-chai';
|
|
|
3
3
|
import MeetingUtil from '@webex/plugin-meetings/src/meeting/util';
|
|
4
4
|
import LoggerProxy from '@webex/plugin-meetings/src/common/logs/logger-proxy';
|
|
5
5
|
import LoggerConfig from '@webex/plugin-meetings/src/common/logs/logger-config';
|
|
6
|
+
import Metrics from '@webex/plugin-meetings/src/metrics/index';
|
|
7
|
+
import {SELF_POLICY} from '@webex/plugin-meetings/src/constants';
|
|
6
8
|
import {DISPLAY_HINTS} from '@webex/plugin-meetings/src/constants';
|
|
7
9
|
import MockWebex from '@webex/test-helper-mock-webex';
|
|
8
10
|
|
|
@@ -269,6 +271,33 @@ describe('plugin-meetings', () => {
|
|
|
269
271
|
|
|
270
272
|
});
|
|
271
273
|
|
|
274
|
+
describe('selfSupportsFeature', () => {
|
|
275
|
+
it('returns true if there are no user policies', () => {
|
|
276
|
+
assert.equal(
|
|
277
|
+
MeetingUtil.selfSupportsFeature(SELF_POLICY.SUPPORT_ANNOTATION, undefined),
|
|
278
|
+
true
|
|
279
|
+
);
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
it('returns true if policy is true', () => {
|
|
283
|
+
assert.equal(
|
|
284
|
+
MeetingUtil.selfSupportsFeature(SELF_POLICY.SUPPORT_ANNOTATION, {
|
|
285
|
+
[SELF_POLICY.SUPPORT_ANNOTATION]: true
|
|
286
|
+
}),
|
|
287
|
+
true
|
|
288
|
+
);
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
it('returns false if policy is false', () => {
|
|
292
|
+
assert.equal(
|
|
293
|
+
MeetingUtil.selfSupportsFeature(SELF_POLICY.SUPPORT_ANNOTATION, {
|
|
294
|
+
[SELF_POLICY.SUPPORT_ANNOTATION]: false,
|
|
295
|
+
}),
|
|
296
|
+
false
|
|
297
|
+
);
|
|
298
|
+
});
|
|
299
|
+
});
|
|
300
|
+
|
|
272
301
|
describe('remoteUpdateAudioVideo', () => {
|
|
273
302
|
it('#Should call meetingRequest.locusMediaRequest with correct parameters', async () => {
|
|
274
303
|
const meeting = {
|