Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: premise recording sdk support #4021

Merged
merged 3 commits into from
Dec 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -117,6 +118,8 @@ export default class InMeetingActions implements IInMeetingActions {

canResumeRecording = null;

isPremiseRecordingEnabled = null;

canStopRecording = null;

canSetMuteOnEntry = null;
Expand Down Expand Up @@ -288,6 +291,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 @@ -3766,6 +3766,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',
}
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) {
chrisadubois marked this conversation as resolved.
Show resolved Hide resolved
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
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 @@ -126,6 +127,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');
hmahant-webex marked this conversation as resolved.
Show resolved Hide resolved

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', {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is this change made for?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change is added to get the error code if available from the failed API response so that we can log it for better troubleshooting.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm kind of concerned about this change. Not sure why we have not needed this before. But I have not tested it and if it's needed, it does seem fine.

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
Loading