Skip to content

Commit 9fee94a

Browse files
authored
fix(plugin-meetings): small refactor of code handling Locus API responses (#4508)
1 parent d933d39 commit 9fee94a

File tree

6 files changed

+138
-48
lines changed

6 files changed

+138
-48
lines changed

packages/@webex/plugin-meetings/src/locus-info/index.ts

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,51 @@ import LocusDeltaParser from './parser';
3131
import Metrics from '../metrics';
3232
import BEHAVIORAL_METRICS from '../metrics/constants';
3333

34+
export type LocusDTO = {
35+
controls?: any;
36+
fullState?: {
37+
active: boolean;
38+
count: number;
39+
lastActive: string;
40+
locked: boolean;
41+
sessionId: string;
42+
seessionIds: string[];
43+
startTime: number;
44+
state: string;
45+
type: string;
46+
};
47+
host?: {
48+
id: string;
49+
incomingCallProtocols: any[];
50+
isExternal: boolean;
51+
name: string;
52+
orgId: string;
53+
};
54+
info?: any;
55+
links?: any;
56+
mediaShares?: any[];
57+
meetings?: any[];
58+
participants: any[];
59+
replaces?: any[];
60+
self?: any;
61+
sequence?: {
62+
dirtyParticipants: number;
63+
entries: number[];
64+
rangeEnd: number;
65+
rangeStart: number;
66+
sequenceHash: number;
67+
sessionToken: string;
68+
since: string;
69+
totalParticipants: number;
70+
};
71+
syncUrl?: string;
72+
url?: string;
73+
};
74+
75+
export type LocusApiResponseBody = {
76+
locus: LocusDTO; // this LocusDTO here might not be the full one (for example it won't have all the participants, but it should have self)
77+
};
78+
3479
/**
3580
* @description LocusInfo extends ChildEmitter to convert locusInfo info a private emitter to parent object
3681
* @export
@@ -323,6 +368,16 @@ export default class LocusInfo extends EventsScope {
323368
this.emitChange = true;
324369
}
325370

371+
/**
372+
* Handles HTTP response from Locus API call.
373+
* @param {Meeting} meeting meeting object
374+
* @param {LocusApiResponseBody} responseBody body of the http response from Locus API call
375+
* @returns {void}
376+
*/
377+
handleLocusAPIResponse(meeting, responseBody: LocusApiResponseBody): void {
378+
this.handleLocusDelta(responseBody.locus, meeting);
379+
}
380+
326381
/**
327382
* @param {Meeting} meeting
328383
* @param {Object} data

packages/@webex/plugin-meetings/src/meeting/muteState.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -291,18 +291,14 @@ export class MuteState {
291291
);
292292

293293
return MeetingUtil.remoteUpdateAudioVideo(meeting, audioMuted, videoMuted)
294-
.then((locus) => {
294+
.then((response) => {
295295
LoggerProxy.logger.info(
296296
`Meeting:muteState#sendLocalMuteRequestToServer --> ${this.type}: local mute (audio=${audioMuted}, video=${videoMuted}) applied to server`
297297
);
298298

299299
this.state.server.localMute = this.type === AUDIO ? audioMuted : videoMuted;
300300

301-
if (locus) {
302-
meeting.locusInfo.handleLocusDelta(locus, meeting);
303-
}
304-
305-
return locus;
301+
return MeetingUtil.updateLocusFromApiResponse(meeting, response);
306302
})
307303
.catch((remoteUpdateError) => {
308304
LoggerProxy.logger.warn(

packages/@webex/plugin-meetings/src/meeting/util.ts

Lines changed: 16 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -59,18 +59,16 @@ const MeetingUtil = {
5959
);
6060
}
6161

62-
return meeting.locusMediaRequest
63-
.send({
64-
type: 'LocalMute',
65-
selfUrl: meeting.selfUrl,
66-
mediaId: meeting.mediaId,
67-
sequence: meeting.locusInfo.sequence,
68-
muteOptions: {
69-
audioMuted,
70-
videoMuted,
71-
},
72-
})
73-
.then((response) => response?.body?.locus);
62+
return meeting.locusMediaRequest.send({
63+
type: 'LocalMute',
64+
selfUrl: meeting.selfUrl,
65+
mediaId: meeting.mediaId,
66+
sequence: meeting.locusInfo.sequence,
67+
muteOptions: {
68+
audioMuted,
69+
videoMuted,
70+
},
71+
});
7472
},
7573

7674
hasOwner: (info) => info && info.owner,
@@ -695,22 +693,20 @@ const MeetingUtil = {
695693
},
696694

697695
/**
698-
* Updates the locus info for the meeting with the delta locus
699-
* returned from requests that include the sequence information
696+
* Updates the locus info for the meeting with the locus
697+
* information returned from API requests made to Locus
700698
* Returns the original response object
701699
* @param {Object} meeting The meeting object
702700
* @param {Object} response The response of the http request
703701
* @returns {Object}
704702
*/
705-
updateLocusWithDelta: (meeting, response) => {
703+
updateLocusFromApiResponse: (meeting, response) => {
706704
if (!meeting) {
707705
return response;
708706
}
709707

710-
const locus = response?.body?.locus;
711-
712-
if (locus) {
713-
meeting.locusInfo.handleLocusDelta(locus, meeting);
708+
if (response?.body?.locus) {
709+
meeting.locusInfo.handleLocusAPIResponse(meeting, response.body);
714710
}
715711

716712
return response;
@@ -757,7 +753,7 @@ const MeetingUtil = {
757753

758754
return meeting
759755
.request(options)
760-
.then((response) => MeetingUtil.updateLocusWithDelta(meeting, response));
756+
.then((response) => MeetingUtil.updateLocusFromApiResponse(meeting, response));
761757
};
762758

763759
return locusDeltaRequest;

packages/@webex/plugin-meetings/test/unit/spec/locus-info/index.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2040,6 +2040,18 @@ describe('plugin-meetings', () => {
20402040
});
20412041
});
20422042

2043+
describe('#handleLocusAPIResponse', () => {
2044+
it('calls handleLocusDelta', () => {
2045+
const fakeLocus = {eventType: LOCUSEVENT.DIFFERENCE};
2046+
2047+
sinon.stub(locusInfo, 'handleLocusDelta');
2048+
2049+
locusInfo.handleLocusAPIResponse(mockMeeting, {locus: fakeLocus});
2050+
2051+
assert.calledWith(locusInfo.handleLocusDelta, fakeLocus, mockMeeting);
2052+
});
2053+
});
2054+
20432055
describe('#LocusDeltaEvents', () => {
20442056
const fakeMeeting = 'fakeMeeting';
20452057
let sandbox = null;

packages/@webex/plugin-meetings/test/unit/spec/meeting/muteState.js

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ describe('plugin-meetings', () => {
1212
let video;
1313
let originalRemoteUpdateAudioVideo;
1414

15-
const fakeLocus = {info: 'this is a fake locus'};
15+
const fakeLocusResponse = {body: {locus: {info: 'this is a fake locus'}}};
1616

1717
const createFakeLocalStream = (id, userMuted, systemMuted) => {
1818
return {
@@ -38,9 +38,6 @@ describe('plugin-meetings', () => {
3838
unmuteAllowed: true,
3939
remoteVideoMuted: false,
4040
unmuteVideoAllowed: true,
41-
locusInfo: {
42-
handleLocusDelta: sinon.stub(),
43-
},
4441
members: {
4542
selfId: 'fake self id',
4643
muteMember: sinon.stub().resolves(),
@@ -49,7 +46,8 @@ describe('plugin-meetings', () => {
4946

5047
originalRemoteUpdateAudioVideo = MeetingUtil.remoteUpdateAudioVideo;
5148

52-
MeetingUtil.remoteUpdateAudioVideo = sinon.stub().resolves(fakeLocus);
49+
MeetingUtil.remoteUpdateAudioVideo = sinon.stub().resolves(fakeLocusResponse);
50+
MeetingUtil.updateLocusFromApiResponse = sinon.stub();
5351

5452
audio = createMuteState(AUDIO, meeting, true);
5553
video = createMuteState(VIDEO, meeting, true);
@@ -141,6 +139,7 @@ describe('plugin-meetings', () => {
141139
// and local unmute was sent to server
142140
assert.calledOnce(MeetingUtil.remoteUpdateAudioVideo);
143141
assert.calledWith(MeetingUtil.remoteUpdateAudioVideo, meeting, false, undefined);
142+
assert.calledWith(MeetingUtil.updateLocusFromApiResponse, meeting, fakeLocusResponse);
144143

145144
assert.isFalse(audio.isMuted());
146145
});
@@ -173,6 +172,7 @@ describe('plugin-meetings', () => {
173172

174173
// system was muted so local unmute was not sent to server
175174
assert.notCalled(MeetingUtil.remoteUpdateAudioVideo);
175+
assert.notCalled(MeetingUtil.updateLocusFromApiResponse);
176176

177177
assert.isTrue(audio.isMuted());
178178
});
@@ -207,6 +207,7 @@ describe('plugin-meetings', () => {
207207
// and local unmute was sent to server
208208
assert.calledOnce(MeetingUtil.remoteUpdateAudioVideo);
209209
assert.calledWith(MeetingUtil.remoteUpdateAudioVideo, meeting, undefined, false);
210+
assert.calledWith(MeetingUtil.updateLocusFromApiResponse, meeting, fakeLocusResponse);
210211

211212
assert.isFalse(video.isMuted());
212213
});
@@ -219,7 +220,9 @@ describe('plugin-meetings', () => {
219220

220221
assert.isTrue(video.isMuted());
221222

223+
await testUtils.flushPromises();
222224
MeetingUtil.remoteUpdateAudioVideo.resetHistory();
225+
MeetingUtil.updateLocusFromApiResponse.resetHistory();
223226

224227
// now simulate server requiring us to locally unmute
225228
// assuming setServerMuted succeeds at updating userMuted
@@ -239,6 +242,7 @@ describe('plugin-meetings', () => {
239242

240243
// system was muted so local unmute was not sent to server
241244
assert.notCalled(MeetingUtil.remoteUpdateAudioVideo);
245+
assert.notCalled(MeetingUtil.updateLocusFromApiResponse);
242246

243247
assert.isTrue(video.isMuted());
244248
});
@@ -443,6 +447,7 @@ describe('plugin-meetings', () => {
443447

444448
assert.calledOnce(MeetingUtil.remoteUpdateAudioVideo);
445449
assert.calledWith(MeetingUtil.remoteUpdateAudioVideo, meeting, true, undefined);
450+
assert.calledWith(MeetingUtil.updateLocusFromApiResponse, meeting, fakeLocusResponse);
446451

447452
// now allow the first request to complete
448453
serverResponseResolve();
@@ -559,6 +564,7 @@ describe('plugin-meetings', () => {
559564
await testUtils.flushPromises();
560565

561566
MeetingUtil.remoteUpdateAudioVideo.resetHistory();
567+
MeetingUtil.updateLocusFromApiResponse.resetHistory();
562568
};
563569

564570
const setupSpies = (mediaType) => {
@@ -605,13 +611,15 @@ describe('plugin-meetings', () => {
605611
{mediaType: VIDEO, title: 'video'},
606612
];
607613

614+
const fakeLocusResponse = {body: {locus: {info: 'fake locus'}}};
615+
608616
tests.forEach(({mediaType, title}) =>
609617
describe(title, () => {
610618
let originalRemoteUpdateAudioVideo;
611619

612620
beforeEach(() => {
613621
originalRemoteUpdateAudioVideo = MeetingUtil.remoteUpdateAudioVideo;
614-
MeetingUtil.remoteUpdateAudioVideo = sinon.stub().resolves({info: 'fake locus'});
622+
MeetingUtil.remoteUpdateAudioVideo = sinon.stub().resolves(fakeLocusResponse);
615623
});
616624

617625
afterEach(() => {
@@ -660,6 +668,7 @@ describe('plugin-meetings', () => {
660668
assert.calledWith(setUnmuteAllowedSpy, muteState.state.server.unmuteAllowed);
661669
assert.notCalled(setServerMutedSpy);
662670
assert.notCalled(MeetingUtil.remoteUpdateAudioVideo);
671+
assert.notCalled(MeetingUtil.updateLocusFromApiResponse);
663672
assert.isTrue(muteState.state.client.localMute);
664673
});
665674

@@ -672,6 +681,7 @@ describe('plugin-meetings', () => {
672681
assert.calledWith(setUnmuteAllowedSpy, muteState.state.server.unmuteAllowed);
673682
assert.notCalled(setServerMutedSpy);
674683
assert.notCalled(MeetingUtil.remoteUpdateAudioVideo);
684+
assert.notCalled(MeetingUtil.updateLocusFromApiResponse);
675685
assert.isTrue(muteState.state.client.localMute);
676686
});
677687

@@ -681,9 +691,12 @@ describe('plugin-meetings', () => {
681691

682692
muteState.init(meeting);
683693

694+
await testUtils.flushPromises();
695+
684696
assert.calledWith(setUnmuteAllowedSpy, muteState.state.server.unmuteAllowed);
685697
assert.notCalled(setServerMutedSpy);
686698
assert.calledOnce(MeetingUtil.remoteUpdateAudioVideo);
699+
assert.calledOnceWithExactly(MeetingUtil.updateLocusFromApiResponse, meeting, fakeLocusResponse);
687700
assert.isFalse(muteState.state.client.localMute);
688701
});
689702

@@ -707,6 +720,7 @@ describe('plugin-meetings', () => {
707720
simulateUserMute(mediaType, true);
708721
muteState.handleLocalStreamMuteStateChange(meeting);
709722
assert.notCalled(MeetingUtil.remoteUpdateAudioVideo);
723+
assert.notCalled(MeetingUtil.updateLocusFromApiResponse);
710724

711725
assert.isFalse(muteState.state.client.localMute);
712726
});
@@ -716,35 +730,47 @@ describe('plugin-meetings', () => {
716730

717731
simulateUserMute(mediaType, false);
718732
muteState.handleLocalStreamMuteStateChange(meeting);
733+
await testUtils.flushPromises();
734+
719735
assert.equal(muteState.state.client.localMute, false);
720736
assert.called(MeetingUtil.remoteUpdateAudioVideo);
737+
assert.calledOnceWithExactly(MeetingUtil.updateLocusFromApiResponse, meeting, fakeLocusResponse);
721738
});
722739

723740
it('tests localMute - user mute from false to true', async () => {
724741
await setup(mediaType, false, false, false, true);
725742

726743
simulateUserMute(mediaType, true);
727744
muteState.handleLocalStreamMuteStateChange(meeting);
745+
await testUtils.flushPromises();
746+
728747
assert.equal(muteState.state.client.localMute, true);
729748
assert.called(MeetingUtil.remoteUpdateAudioVideo);
749+
assert.calledOnceWithExactly(MeetingUtil.updateLocusFromApiResponse, meeting, fakeLocusResponse);
730750
});
731751

732752
it('tests localMute - system mute from true to false', async () => {
733753
await setup(mediaType, false, false, true, true);
734754

735755
simulateSystemMute(mediaType, false);
736756
muteState.handleLocalStreamMuteStateChange(meeting);
757+
await testUtils.flushPromises();
758+
737759
assert.equal(muteState.state.client.localMute, false);
738760
assert.called(MeetingUtil.remoteUpdateAudioVideo);
761+
assert.calledOnceWithExactly(MeetingUtil.updateLocusFromApiResponse, meeting, fakeLocusResponse);
739762
});
740763

741764
it('tests localMute - system mute from false to true', async () => {
742765
await setup(mediaType, false, false, false, true);
743766

744767
simulateSystemMute(mediaType, true);
745768
muteState.handleLocalStreamMuteStateChange(meeting);
769+
await testUtils.flushPromises();
770+
746771
assert.equal(muteState.state.client.localMute, true);
747772
assert.called(MeetingUtil.remoteUpdateAudioVideo);
773+
assert.calledOnceWithExactly(MeetingUtil.updateLocusFromApiResponse, meeting, fakeLocusResponse);
748774
});
749775
});
750776

0 commit comments

Comments
 (0)