Skip to content

Commit

Permalink
feat: premise recording sdk support (#4021)
Browse files Browse the repository at this point in the history
  • Loading branch information
hmahant-webex authored Dec 16, 2024
1 parent 71f56bf commit 885e4bb
Show file tree
Hide file tree
Showing 10 changed files with 165 additions and 19 deletions.
4 changes: 4 additions & 0 deletions packages/@webex/plugin-meetings/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -904,6 +904,10 @@ export const DISPLAY_HINTS = {
RECORDING_CONTROL_PAUSE: 'RECORDING_CONTROL_PAUSE',
RECORDING_CONTROL_STOP: 'RECORDING_CONTROL_STOP',
RECORDING_CONTROL_RESUME: 'RECORDING_CONTROL_RESUME',
PREMISE_RECORDING_CONTROL_START: 'PREMISE_RECORDING_CONTROL_START',
PREMISE_RECORDING_CONTROL_PAUSE: 'PREMISE_RECORDING_CONTROL_PAUSE',
PREMISE_RECORDING_CONTROL_STOP: 'PREMISE_RECORDING_CONTROL_STOP',
PREMISE_RECORDING_CONTROL_RESUME: 'PREMISE_RECORDING_CONTROL_RESUME',
LOCK_CONTROL_UNLOCK: 'LOCK_CONTROL_UNLOCK',
LOCK_CONTROL_LOCK: 'LOCK_CONTROL_LOCK',
LOCK_STATUS_LOCKED: 'LOCK_STATUS_LOCKED',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ interface IInMeetingActions {
canStartRecording?: boolean;
canPauseRecording?: boolean;
canResumeRecording?: boolean;
isPremiseRecordingEnabled?: boolean;
canStopRecording?: boolean;
canRaiseHand?: boolean;
canLowerAllHands?: boolean;
Expand Down Expand Up @@ -121,6 +122,8 @@ export default class InMeetingActions implements IInMeetingActions {

canResumeRecording = null;

isPremiseRecordingEnabled = null;

canStopRecording = null;

canSetMuteOnEntry = null;
Expand Down Expand Up @@ -301,6 +304,7 @@ export default class InMeetingActions implements IInMeetingActions {
canPauseRecording: this.canPauseRecording,
canResumeRecording: this.canResumeRecording,
canStopRecording: this.canStopRecording,
isPremiseRecordingEnabled: this.isPremiseRecordingEnabled,
canRaiseHand: this.canRaiseHand,
canLowerAllHands: this.canLowerAllHands,
canLowerSomeoneElsesHand: this.canLowerSomeoneElsesHand,
Expand Down
4 changes: 4 additions & 0 deletions packages/@webex/plugin-meetings/src/meeting/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3774,6 +3774,10 @@ export default class Meeting extends StatelessWebexPlugin {
this.userDisplayHints,
this.selfUserPolicies
),
isPremiseRecordingEnabled: RecordingUtil.isPremiseRecordingEnabled(
this.userDisplayHints,
this.selfUserPolicies
),
canRaiseHand: MeetingUtil.canUserRaiseHand(this.userDisplayHints),
canLowerAllHands: MeetingUtil.canUserLowerAllHands(this.userDisplayHints),
canLowerSomeoneElsesHand: MeetingUtil.canUserLowerSomeoneElsesHand(this.userDisplayHints),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
enum RecordingAction {
export enum RecordingAction {
Start = 'Start',
Stop = 'Stop',
Pause = 'Pause',
Resume = 'Resume',
}

export default RecordingAction;
export enum RecordingType {
Premise = 'premise',
Cloud = 'cloud',
}
21 changes: 17 additions & 4 deletions packages/@webex/plugin-meetings/src/recording-controller/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import PermissionError from '../common/errors/permission';
import LoggerProxy from '../common/logs/logger-proxy';
import {CONTROLS, HTTP_VERBS, SELF_POLICY} from '../constants';
import MeetingRequest from '../meeting/request';
import RecordingAction from './enums';
import {RecordingAction, RecordingType} from './enums';
import Util from './util';
import LoggerProxy from '../common/logs/logger-proxy';

/**
* @description Recording manages the recording functionality of the meeting object, there should only be one instantation of recording per meeting
Expand Down Expand Up @@ -228,11 +228,12 @@ export default class RecordingController {

/**
* @param {RecordingAction} action
* @param {RecordingType} recordingType
* @private
* @memberof RecordingController
* @returns {Promise}
*/
private recordingService(action: RecordingAction): Promise<any> {
private recordingService(action: RecordingAction, recordingType: RecordingType): Promise<any> {
// @ts-ignore
return this.request.request({
body: {
Expand All @@ -242,6 +243,7 @@ export default class RecordingController {
recording: {
action: action.toLowerCase(),
},
recordingType,
},
uri: `${this.serviceUrl}/loci/${this.locusId}/recording`,
method: HTTP_VERBS.PUT,
Expand Down Expand Up @@ -276,14 +278,25 @@ export default class RecordingController {
* @returns {Promise}
*/
private recordingFacade(action: RecordingAction): Promise<any> {
const isPremiseRecordingEnabled = Util.isPremiseRecordingEnabled(
this.displayHints,
this.selfUserPolicies
);
LoggerProxy.logger.log(
`RecordingController:index#recordingFacade --> recording action [${action}]`
);

let recordingType: RecordingType;
if (isPremiseRecordingEnabled) {
recordingType = RecordingType.Premise;
} else {
recordingType = RecordingType.Cloud;
}

// assumes action is proper cased (i.e., Example)
if (Util?.[`canUser${action}`](this.displayHints, this.selfUserPolicies)) {
if (this.serviceUrl) {
return this.recordingService(action);
return this.recordingService(action, recordingType);
}

return this.recordingControls(action);
Expand Down
25 changes: 20 additions & 5 deletions packages/@webex/plugin-meetings/src/recording-controller/util.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,47 @@
import {DISPLAY_HINTS, SELF_POLICY} from '../constants';
import RecordingAction from './enums';
import {RecordingAction} from './enums';
import MeetingUtil from '../meeting/util';

const canUserStart = (
displayHints: Array<string>,
userPolicies: Record<SELF_POLICY, boolean>
): boolean =>
displayHints.includes(DISPLAY_HINTS.RECORDING_CONTROL_START) &&
(displayHints.includes(DISPLAY_HINTS.RECORDING_CONTROL_START) ||
displayHints.includes(DISPLAY_HINTS.PREMISE_RECORDING_CONTROL_START)) &&
MeetingUtil.selfSupportsFeature(SELF_POLICY.SUPPORT_NETWORK_BASED_RECORD, userPolicies);

const canUserPause = (
displayHints: Array<string>,
userPolicies: Record<SELF_POLICY, boolean>
): boolean =>
displayHints.includes(DISPLAY_HINTS.RECORDING_CONTROL_PAUSE) &&
(displayHints.includes(DISPLAY_HINTS.RECORDING_CONTROL_PAUSE) ||
displayHints.includes(DISPLAY_HINTS.PREMISE_RECORDING_CONTROL_PAUSE)) &&
MeetingUtil.selfSupportsFeature(SELF_POLICY.SUPPORT_NETWORK_BASED_RECORD, userPolicies);

const canUserResume = (
displayHints: Array<string>,
userPolicies: Record<SELF_POLICY, boolean>
): boolean =>
displayHints.includes(DISPLAY_HINTS.RECORDING_CONTROL_RESUME) &&
(displayHints.includes(DISPLAY_HINTS.RECORDING_CONTROL_RESUME) ||
displayHints.includes(DISPLAY_HINTS.PREMISE_RECORDING_CONTROL_RESUME)) &&
MeetingUtil.selfSupportsFeature(SELF_POLICY.SUPPORT_NETWORK_BASED_RECORD, userPolicies);

const canUserStop = (
displayHints: Array<string>,
userPolicies: Record<SELF_POLICY, boolean>
): boolean =>
displayHints.includes(DISPLAY_HINTS.RECORDING_CONTROL_STOP) &&
(displayHints.includes(DISPLAY_HINTS.RECORDING_CONTROL_STOP) ||
displayHints.includes(DISPLAY_HINTS.PREMISE_RECORDING_CONTROL_STOP)) &&
MeetingUtil.selfSupportsFeature(SELF_POLICY.SUPPORT_NETWORK_BASED_RECORD, userPolicies);

const isPremiseRecordingEnabled = (
displayHints: Array<string>,
userPolicies: Record<SELF_POLICY, boolean>
): boolean =>
(displayHints.includes(DISPLAY_HINTS.PREMISE_RECORDING_CONTROL_START) ||
displayHints.includes(DISPLAY_HINTS.PREMISE_RECORDING_CONTROL_PAUSE) ||
displayHints.includes(DISPLAY_HINTS.PREMISE_RECORDING_CONTROL_STOP) ||
displayHints.includes(DISPLAY_HINTS.PREMISE_RECORDING_CONTROL_RESUME)) &&
MeetingUtil.selfSupportsFeature(SELF_POLICY.SUPPORT_NETWORK_BASED_RECORD, userPolicies);

const extractLocusId = (url: string) => {
Expand Down Expand Up @@ -70,6 +84,7 @@ export default {
canUserPause,
canUserResume,
canUserStop,
isPremiseRecordingEnabled,
deriveRecordingStates,
extractLocusId,
};
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ describe('plugin-meetings', () => {
canStartManualCaption: null,
canStopManualCaption: null,
isManualCaptionActive: null,
isPremiseRecordingEnabled: null,
isSaveTranscriptsEnabled: null,
isWebexAssistantActive: null,
canViewCaptionPanel: null,
Expand Down Expand Up @@ -131,6 +132,7 @@ describe('plugin-meetings', () => {
'canStartManualCaption',
'canStopManualCaption',
'isManualCaptionActive',
'isPremiseRecordingEnabled',
'isSaveTranscriptsEnabled',
'isWebexAssistantActive',
'canViewCaptionPanel',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,21 @@ describe('plugin-meetings', () => {

assert.calledWith(request.request, {
uri: `test/loci/id/recording`,
body: {meetingInfo: {locusSessionId: 'testId'}, recording: {action: 'start'}},
body: {meetingInfo: {locusSessionId: 'testId'}, recording: {action: 'start'}, recordingType: 'cloud'},
method: HTTP_VERBS.PUT,
});

assert.deepEqual(result, request.request.firstCall.returnValue);
});

it('can start premise recording when the correct display hint is present', () => {
controller.setDisplayHints(['PREMISE_RECORDING_CONTROL_START']);

const result = controller.startRecording();

assert.calledWith(request.request, {
uri: `test/loci/id/recording`,
body: {meetingInfo: {locusSessionId: 'testId'}, recording: {action: 'start'}, recordingType: 'premise'},
method: HTTP_VERBS.PUT,
});

Expand All @@ -238,14 +252,28 @@ describe('plugin-meetings', () => {
assert.isRejected(result);
});

it('can start recording when the correct display hint is present', () => {
it('can stop recording when the correct display hint is present', () => {
controller.setDisplayHints(['RECORDING_CONTROL_STOP']);

const result = controller.stopRecording();

assert.calledWith(request.request, {
uri: `test/loci/id/recording`,
body: {meetingInfo: {locusSessionId: 'testId'}, recording: {action: 'stop'}},
body: {meetingInfo: {locusSessionId: 'testId'}, recording: {action: 'stop'}, recordingType: 'cloud'},
method: HTTP_VERBS.PUT,
});

assert.deepEqual(result, request.request.firstCall.returnValue);
});

it('can stop premise recording when the correct display hint is present', () => {
controller.setDisplayHints(['PREMISE_RECORDING_CONTROL_STOP']);

const result = controller.stopRecording();

assert.calledWith(request.request, {
uri: `test/loci/id/recording`,
body: {meetingInfo: {locusSessionId: 'testId'}, recording: {action: 'stop'}, recordingType: 'premise'},
method: HTTP_VERBS.PUT,
});

Expand All @@ -269,7 +297,21 @@ describe('plugin-meetings', () => {

assert.calledWith(request.request, {
uri: `test/loci/id/recording`,
body: {meetingInfo: {locusSessionId: 'testId'}, recording: {action: 'pause'}},
body: {meetingInfo: {locusSessionId: 'testId'}, recording: {action: 'pause'}, recordingType: 'cloud'},
method: HTTP_VERBS.PUT,
});

assert.deepEqual(result, request.request.firstCall.returnValue);
});

it('can pause premise recording when the correct display hint is present', () => {
controller.setDisplayHints(['PREMISE_RECORDING_CONTROL_PAUSE']);

const result = controller.pauseRecording();

assert.calledWith(request.request, {
uri: `test/loci/id/recording`,
body: {meetingInfo: {locusSessionId: 'testId'}, recording: {action: 'pause'}, recordingType: 'premise'},
method: HTTP_VERBS.PUT,
});

Expand All @@ -293,7 +335,21 @@ describe('plugin-meetings', () => {

assert.calledWith(request.request, {
uri: `test/loci/id/recording`,
body: {meetingInfo: {locusSessionId: 'testId'}, recording: {action: 'resume'}},
body: {meetingInfo: {locusSessionId: 'testId'}, recording: {action: 'resume'}, recordingType: 'cloud'},
method: HTTP_VERBS.PUT,
});

assert.deepEqual(result, request.request.firstCall.returnValue);
});

it('can resume premise recording when the correct display hint is present', () => {
controller.setDisplayHints(['PREMISE_RECORDING_CONTROL_RESUME']);

const result = controller.resumeRecording();

assert.calledWith(request.request, {
uri: `test/loci/id/recording`,
body: {meetingInfo: {locusSessionId: 'testId'}, recording: {action: 'resume'}, recordingType: 'premise'},
method: HTTP_VERBS.PUT,
});

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import RecordingUtil from '@webex/plugin-meetings/src/recording-controller/util';
import RecordingAction from '@webex/plugin-meetings/src/recording-controller/enums';
import { RecordingAction } from '@webex/plugin-meetings/src/recording-controller/enums';
import {SELF_POLICY} from '@webex/plugin-meetings/src/constants';

import {assert} from 'chai';
Expand Down Expand Up @@ -29,6 +29,15 @@ describe('plugin-meetings', () => {
);
});

it('can start premise recording when the correct display hint is present', () => {
locusInfo.parsedLocus.info.userDisplayHints.push('PREMISE_RECORDING_CONTROL_START');

assert.equal(
RecordingUtil.canUserStart(locusInfo.parsedLocus.info.userDisplayHints),
true
);
});

it('can start recording when the correct display hint is present and the policy is true', () => {
locusInfo.parsedLocus.info.userDisplayHints.push('RECORDING_CONTROL_START');

Expand Down Expand Up @@ -69,6 +78,15 @@ describe('plugin-meetings', () => {
);
});

it('can pause premise recording when the correct display hint is present', () => {
locusInfo.parsedLocus.info.userDisplayHints.push('PREMISE_RECORDING_CONTROL_PAUSE');

assert.equal(
RecordingUtil.canUserPause(locusInfo.parsedLocus.info.userDisplayHints),
true
);
});

it('can pause recording when the correct display hint is present and the policy is true', () => {
locusInfo.parsedLocus.info.userDisplayHints.push('RECORDING_CONTROL_PAUSE');

Expand Down Expand Up @@ -109,6 +127,15 @@ describe('plugin-meetings', () => {
);
});

it('can stop premise recording when the correct display hint is present', () => {
locusInfo.parsedLocus.info.userDisplayHints.push('PREMISE_RECORDING_CONTROL_STOP');

assert.equal(
RecordingUtil.canUserStop(locusInfo.parsedLocus.info.userDisplayHints),
true
);
});

it('can stop recording when the correct display hint is present and the policy is true', () => {
locusInfo.parsedLocus.info.userDisplayHints.push('RECORDING_CONTROL_STOP', {
[SELF_POLICY.SUPPORT_NETWORK_BASED_RECORD]: true,
Expand Down Expand Up @@ -142,7 +169,7 @@ describe('plugin-meetings', () => {
});

describe('canUserResume', () => {
it('can start recording when the correct display hint is present', () => {
it('can resume recording when the correct display hint is present', () => {
locusInfo.parsedLocus.info.userDisplayHints.push('RECORDING_CONTROL_RESUME');

assert.equal(
Expand All @@ -151,7 +178,16 @@ describe('plugin-meetings', () => {
);
});

it('can start recording when the correct display hint is present and the policy is true', () => {
it('can resume premise recording when the correct display hint is present', () => {
locusInfo.parsedLocus.info.userDisplayHints.push('PREMISE_RECORDING_CONTROL_RESUME');

assert.equal(
RecordingUtil.canUserResume(locusInfo.parsedLocus.info.userDisplayHints),
true
);
});

it('can resume recording when the correct display hint is present and the policy is true', () => {
locusInfo.parsedLocus.info.userDisplayHints.push('RECORDING_CONTROL_RESUME');

assert.equal(
Expand Down
9 changes: 9 additions & 0 deletions packages/@webex/webex-core/src/lib/webex-http-error.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,15 @@ export default class WebexHttpError extends HttpError {
value: res.options,
});

Reflect.defineProperty(this, 'body', {
enumerable: false,
value: res.body,
});

if (this.body && this.body.errorCode) {
message += `\nerrorCode : ${this.body.errorCode}`;
}

if (this.options.url) {
message += `\n${this.options.method} ${this.options.url}`;
} else if (this.options.uri) {
Expand Down

0 comments on commit 885e4bb

Please sign in to comment.