Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/feature/VIH-11065--preload-confe…
Browse files Browse the repository at this point in the history
…rence-cache-with-days-hearings' into feature/VIH-11065--preload-conference-cache-with-days-hearings
  • Loading branch information
will-craig committed Nov 22, 2024
2 parents 81fb80b + db09707 commit e820dc9
Show file tree
Hide file tree
Showing 15 changed files with 220 additions and 285 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -83,15 +83,15 @@ describe('AudioRecordingService', () => {
describe('reconnectToWowza', () => {
it('should reconnect to Wowza', async () => {
const failedToConnectCallback = jasmine.createSpy('failedToConnectCallback');
service.conference = { id: 'conferenceId', audioRecordingIngestUrl: 'ingestUrl' } as any;
service.conference = { id: globalConference.id, audioRecordingIngestUrl: 'ingestUrl' } as any;
videoCallServiceSpy.connectWowzaAgent.and.callFake((url, callback) => {
callback({ status: 'success', result: ['newUUID'] });
});

await service.reconnectToWowza(failedToConnectCallback);
expect(service.restartActioned).toBeTrue();
expect(videoCallServiceSpy.connectWowzaAgent).toHaveBeenCalledWith('ingestUrl', jasmine.any(Function));
expect(eventServiceSpy.sendAudioRecordingPaused).toHaveBeenCalledWith('conferenceId', false);
expect(eventServiceSpy.sendAudioRecordingPaused).toHaveBeenCalledWith(globalConference.id, false);
expect(failedToConnectCallback).not.toHaveBeenCalled();
});

Expand All @@ -106,6 +106,21 @@ describe('AudioRecordingService', () => {
expect(failedToConnectCallback).toHaveBeenCalled();
});

it('should call push false to wowzaAgentConnection$ if reconnect to Wowza fails', async () => {
service.conference = { id: 'conferenceId', audioRecordingIngestUrl: 'ingestUrl' } as any;
videoCallServiceSpy.connectWowzaAgent.and.callFake((url, callback) => {
callback({ status: 'failure' });
});

let emittedValue: boolean | undefined;
service.getWowzaAgentConnectionState().subscribe(value => (emittedValue = value));

await service.reconnectToWowza();

// Assert that `false` was emitted by the observable
expect(emittedValue).toBe(false);
});

it('should clean up dial out connections', () => {
service.dialOutUUID = ['uuid1', 'uuid2'];
service.cleanupDialOutConnections();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,18 +43,7 @@ export class AudioRecordingService {
takeUntil(this.onDestroy$),
distinctUntilChanged((prev, curr) => prev?.uuid === curr?.uuid)
)
.subscribe(participant => {
if (participant) {
this.dialOutUUID = [...new Set([...this.dialOutUUID, participant?.uuid])];
}
this.wowzaAgent = participant;
if (participant?.isAudioOnlyCall) {
this.wowzaAgentConnection$.next(true);
this.restartActioned = false;
} else {
this.wowzaAgentConnection$.next(false);
}
});
.subscribe(wowzaParticipant => this.handleWowzaParticipantAdded(wowzaParticipant));

this.eventService.getAudioPaused().subscribe(async (message: AudioRecordingPauseStateMessage) => {
if (this.conference.id === message.conferenceId) {
Expand All @@ -77,28 +66,52 @@ export class AudioRecordingService {
this.dialOutUUID = this.dialOutUUID.filter(uuid => uuid !== this.wowzaAgent.uuid);
}

async reconnectToWowza(failedToConnectCallback: Function) {
async reconnectToWowza(callback: Function = null) {
this.restartActioned = true;
this.cleanupDialOutConnections();
this.videoCallService.connectWowzaAgent(this.conference.audioRecordingIngestUrl, async dialOutToWowzaResponse => {
if (dialOutToWowzaResponse.status === 'success') {
await this.eventService.sendAudioRecordingPaused(this.conference.id, false);
this.logger.debug(`${this.loggerPrefix} dial-out request successful`);
} else {
failedToConnectCallback();
this.restartActioned = false;
if (callback) {
callback();
} else {
this.wowzaAgentConnection$.next(false);
}
}
});
}

cleanupDialOutConnections() {
cleanupDialOutConnections(): void {
this.logger.debug(`${this.loggerPrefix} Cleaning up dial out connections, if any {dialOutUUID: ${this.dialOutUUID}}`);
this.dialOutUUID?.forEach(uuid => {
this.videoCallService.disconnectWowzaAgent(uuid);
});

if (this.dialOutUUID.length > 0) {
this.dialOutUUID?.forEach(uuid => {
if (uuid) {
this.videoCallService.disconnectWowzaAgent(uuid);
}
});
}

this.dialOutUUID = [];
}

cleanupSubscriptions() {
cleanupSubscriptions(): void {
this.onDestroy$.next();
this.onDestroy$.complete();
}

private async handleWowzaParticipantAdded(participant: VHPexipParticipant) {
if (participant) {
this.dialOutUUID = [...new Set([...this.dialOutUUID, participant?.uuid])];
}
this.wowzaAgent = participant;
if (participant?.isAudioOnlyCall) {
this.wowzaAgentConnection$.next(true);
this.restartActioned = false;
await this.eventService.sendAudioRecordingPaused(this.conference.id, false);
} else {
this.wowzaAgentConnection$.next(false);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { fakeAsync, flush, tick } from '@angular/core/testing';
import { Guid } from 'guid-typescript';
import { BehaviorSubject, Observable, of, Subject, Subscription } from 'rxjs';
import { BehaviorSubject, of, Subject } from 'rxjs';
import {
ClientSettingsResponse,
ConferenceResponse,
Expand Down Expand Up @@ -58,6 +58,8 @@ import { ConferenceActions } from '../store/actions/conference.actions';
import { take } from 'rxjs/operators';
import { NotificationToastrService } from '../services/notification-toastr.service';
import { audioRecordingServiceSpy } from '../../testing/mocks/mock-audio-recording.service';
import * as ConferenceSelectors from '../../waiting-space/store/selectors/conference.selectors';
import { mapParticipantToVHParticipant } from '../store/models/api-contract-to-state-model-mappers';

describe('HearingControlsBaseComponent', () => {
const participantOneId = Guid.create().toString();
Expand Down Expand Up @@ -115,6 +117,8 @@ describe('HearingControlsBaseComponent', () => {
const initialState = initialConferenceState;
mockStore = createMockStore({ initialState });

mockStore.overrideSelector(ConferenceSelectors.getLoggedInParticipant, mapParticipantToVHParticipant(globalParticipant));

clientSettingsResponse = new ClientSettingsResponse({
enable_dynamic_evidence_sharing: false
});
Expand Down Expand Up @@ -192,6 +196,7 @@ describe('HearingControlsBaseComponent', () => {

afterEach(() => {
component.ngOnDestroy();
mockStore.resetSelectors();
});
it('should return true for staff member', () => {
component.participant = conference.participants.find(x => x.role === Role.StaffMember);
Expand Down Expand Up @@ -255,73 +260,6 @@ describe('HearingControlsBaseComponent', () => {
});
});

describe('onLoggedInParticipantChanged', () => {
it('should unsubscribe from the existing subscription and resubscribe', fakeAsync(() => {
// Arrange
const firstSubscription = new Subscription();
spyOn(firstSubscription, 'unsubscribe');
const secondSubscription = new Subscription();
spyOn(secondSubscription, 'unsubscribe');
const observable = new Observable<ParticipantModel>();
getSpiedPropertyGetter(participantServiceSpy, 'onParticipantSpotlightStatusChanged$').and.returnValue(observable);
spyOn(observable, 'pipe').and.returnValue(observable);
spyOn(observable, 'subscribe').and.returnValues(firstSubscription, secondSubscription);

// Act
component.onLoggedInParticipantChanged(ParticipantModel.fromParticipantForUserResponse(participantOne));
flush();
component.onLoggedInParticipantChanged(ParticipantModel.fromParticipantForUserResponse(participantOne));
flush();

// Assert
expect(firstSubscription.unsubscribe).toHaveBeenCalledTimes(1);
expect(secondSubscription.unsubscribe).not.toHaveBeenCalledTimes(1);
expect(component['participantSpotlightUpdateSubscription']).toBe(secondSubscription);
}));

it('should update isSpotlighted when spotlight changed event is emitted', fakeAsync(() => {
// Arrange
const spotlightChangedSubject = new Subject<ParticipantModel>();
const spotlightChanged$ = spotlightChangedSubject.asObservable();
getSpiedPropertyGetter(participantServiceSpy, 'onParticipantSpotlightStatusChanged$').and.returnValue(spotlightChanged$);
spyOn(spotlightChanged$, 'pipe').and.returnValue(spotlightChanged$);

const participant = ParticipantModel.fromParticipantForUserResponse(participantOne);
participant.isSpotlighted = false;

// Act
component.onLoggedInParticipantChanged(participant);
flush();

participant.isSpotlighted = true;
spotlightChangedSubject.next(participant);

// Assert
expect(component.isSpotlighted).toBeTrue();
}));

it('should NOT update isSpotlighted when spotlight changed event is emitted if the participant IDs dont match', fakeAsync(() => {
// Arrange
const spotlightChangedSubject = new Subject<ParticipantModel>();
const spotlightChanged$ = spotlightChangedSubject.asObservable();
getSpiedPropertyGetter(participantServiceSpy, 'onParticipantSpotlightStatusChanged$').and.returnValue(spotlightChanged$);
spyOn(spotlightChanged$, 'pipe').and.returnValue(spotlightChanged$);

const participant = ParticipantModel.fromParticipantForUserResponse(participantOne);
participant.isSpotlighted = false;

// Act
component.onLoggedInParticipantChanged(participant);
flush();

participant.id = Guid.create().toString();
spotlightChangedSubject.next(participant);

// Assert
expect(component.isSpotlighted).toBeFalse();
}));
});

describe('onVideoEvidenceSharing', () => {
it('should set sharingDynamicEvidence to true when video evidence sharing has started', fakeAsync(() => {
component.sharingDynamicEvidence = false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { FocusService } from 'src/app/services/focus.service';
import { ConferenceState } from '../store/reducers/conference.reducer';
import { Store } from '@ngrx/store';
import { ConferenceActions } from '../store/actions/conference.actions';
import * as ConferenceSelectors from '../store/selectors/conference.selectors';

@Injectable()
export abstract class HearingControlsBaseComponent implements OnInit, OnDestroy {
Expand Down Expand Up @@ -167,26 +168,19 @@ export abstract class HearingControlsBaseComponent implements OnInit, OnDestroy
this.setupVideoCallSubscribers();
this.setupEventhubSubscribers();

this.participantService.loggedInParticipant$
this.conferenceStore
.select(ConferenceSelectors.getLoggedInParticipant)
.pipe(
takeUntil(this.destroyedSubject),
filter(participant => participant && participant.role === Role.Judge)
filter(x => !!x)
)
.subscribe(participant => this.onLoggedInParticipantChanged(participant));
.subscribe(loggedInParticipant => {
this.isSpotlighted = loggedInParticipant.pexipInfo?.isSpotlighted;
});

this.initialiseMuteStatus();
}

onLoggedInParticipantChanged(participant: ParticipantModel): void {
this.isSpotlighted = participant.isSpotlighted;
this.participantSpotlightUpdateSubscription?.unsubscribe();
this.participantSpotlightUpdateSubscription = this.participantService.onParticipantSpotlightStatusChanged$
.pipe(filter(updatedParticipant => updatedParticipant.id === participant.id))
.subscribe(updatedParticipant => {
this.isSpotlighted = updatedParticipant.isSpotlighted;
});
}

initialiseMuteStatus() {
if (this.isPrivateConsultation && this.audioMuted) {
this.resetMute();
Expand Down
Loading

0 comments on commit e820dc9

Please sign in to comment.