Skip to content
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
2 changes: 2 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

# [Unreleased](https://github.com/MyPureCloud/genesys-cloud-webrtc-sdk/compare/v13.0.0...HEAD)
### Added
* [STREAM-1180](https://inindca.atlassian.net/browse/STREAM-1180) - Allow for different media handling strategies. This supports alerting leader functionality, where one instance of the SDK needs to handle media, but other instances should not automatically handle media.

# [v13.0.0](https://github.com/MyPureCloud/genesys-cloud-webrtc-sdk/compare/v12.1.0...HEAD)
### Breaking Changes
Expand Down
49 changes: 48 additions & 1 deletion src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,14 @@
} from './client-private';
import { requestApi, createAndEmitSdkError, defaultConfigOption, requestApiWithRetry } from './utils';
import { setupLogging } from './logging';
import { SdkErrorTypes, SessionTypes } from './types/enums';
import { MediaHandling, SdkErrorTypes, SessionTypes } from './types/enums';
import { SessionManager } from './sessions/session-manager';
import { SdkMedia } from './media/media';
import { HeadsetProxyService } from './headsets/headset';
import { Constants } from 'stanza';
import { setupWebrtcForWindows11 } from './windows11-first-session-hack';
import { ISdkHeadsetService } from './headsets/headset-types';
import { SoftphoneSessionHandler } from '.';

Check warning on line 47 in src/client.ts

View workflow job for this annotation

GitHub Actions / test (22, ubuntu-latest)

'SoftphoneSessionHandler' is defined but never used

Check warning on line 47 in src/client.ts

View workflow job for this annotation

GitHub Actions / test (20, ubuntu-latest)

'SoftphoneSessionHandler' is defined but never used

Check warning on line 47 in src/client.ts

View workflow job for this annotation

GitHub Actions / test (18, ubuntu-latest)

'SoftphoneSessionHandler' is defined but never used

Check warning on line 47 in src/client.ts

View workflow job for this annotation

GitHub Actions / test (22, macos-latest)

'SoftphoneSessionHandler' is defined but never used

Check warning on line 47 in src/client.ts

View workflow job for this annotation

GitHub Actions / test (20, macos-latest)

'SoftphoneSessionHandler' is defined but never used

Check warning on line 47 in src/client.ts

View workflow job for this annotation

GitHub Actions / test (22, windows-latest)

'SoftphoneSessionHandler' is defined but never used

Check warning on line 47 in src/client.ts

View workflow job for this annotation

GitHub Actions / test (18, macos-latest)

'SoftphoneSessionHandler' is defined but never used

Check warning on line 47 in src/client.ts

View workflow job for this annotation

GitHub Actions / test (18, windows-latest)

'SoftphoneSessionHandler' is defined but never used

Check warning on line 47 in src/client.ts

View workflow job for this annotation

GitHub Actions / test (20, windows-latest)

'SoftphoneSessionHandler' is defined but never used

const ENVIRONMENTS = [
'mypurecloud.com',
Expand Down Expand Up @@ -111,6 +112,7 @@
_customerData: ICustomerData;
_hasConnected: boolean;
_config: ISdkFullConfig;
_mediaHandling = MediaHandling.standardMedia;

get isInitialized (): boolean {
return !!this._streamingConnection;
Expand Down Expand Up @@ -225,7 +227,7 @@
* and other necessary async tasks are complete.
*/
async initialize (opts?: { securityCode: string } | ICustomerData): Promise<void> {
const httpRequests: Promise<any>[] = [];

Check warning on line 230 in src/client.ts

View workflow job for this annotation

GitHub Actions / test (22, ubuntu-latest)

Unexpected any. Specify a different type

Check warning on line 230 in src/client.ts

View workflow job for this annotation

GitHub Actions / test (20, ubuntu-latest)

Unexpected any. Specify a different type

Check warning on line 230 in src/client.ts

View workflow job for this annotation

GitHub Actions / test (18, ubuntu-latest)

Unexpected any. Specify a different type

Check warning on line 230 in src/client.ts

View workflow job for this annotation

GitHub Actions / test (22, macos-latest)

Unexpected any. Specify a different type

Check warning on line 230 in src/client.ts

View workflow job for this annotation

GitHub Actions / test (20, macos-latest)

Unexpected any. Specify a different type

Check warning on line 230 in src/client.ts

View workflow job for this annotation

GitHub Actions / test (22, windows-latest)

Unexpected any. Specify a different type

Check warning on line 230 in src/client.ts

View workflow job for this annotation

GitHub Actions / test (18, macos-latest)

Unexpected any. Specify a different type

Check warning on line 230 in src/client.ts

View workflow job for this annotation

GitHub Actions / test (18, windows-latest)

Unexpected any. Specify a different type

Check warning on line 230 in src/client.ts

View workflow job for this annotation

GitHub Actions / test (20, windows-latest)

Unexpected any. Specify a different type
if (this.isGuest) {
let guestPromise: Promise<void>;

Expand Down Expand Up @@ -494,7 +496,7 @@
* @returns a promise that fullfils once the default
* device values have been updated
*/
async updateDefaultDevices (options: IMediaDeviceIds & { updateActiveSessions?: boolean } = {}): Promise<any> {

Check warning on line 499 in src/client.ts

View workflow job for this annotation

GitHub Actions / test (22, ubuntu-latest)

Unexpected any. Specify a different type

Check warning on line 499 in src/client.ts

View workflow job for this annotation

GitHub Actions / test (20, ubuntu-latest)

Unexpected any. Specify a different type

Check warning on line 499 in src/client.ts

View workflow job for this annotation

GitHub Actions / test (18, ubuntu-latest)

Unexpected any. Specify a different type

Check warning on line 499 in src/client.ts

View workflow job for this annotation

GitHub Actions / test (22, macos-latest)

Unexpected any. Specify a different type

Check warning on line 499 in src/client.ts

View workflow job for this annotation

GitHub Actions / test (20, macos-latest)

Unexpected any. Specify a different type

Check warning on line 499 in src/client.ts

View workflow job for this annotation

GitHub Actions / test (22, windows-latest)

Unexpected any. Specify a different type

Check warning on line 499 in src/client.ts

View workflow job for this annotation

GitHub Actions / test (18, macos-latest)

Unexpected any. Specify a different type

Check warning on line 499 in src/client.ts

View workflow job for this annotation

GitHub Actions / test (18, windows-latest)

Unexpected any. Specify a different type

Check warning on line 499 in src/client.ts

View workflow job for this annotation

GitHub Actions / test (20, windows-latest)

Unexpected any. Specify a different type
const updateVideo = options.videoDeviceId !== undefined;
const updateAudio = options.audioDeviceId !== undefined;
const updateOutput = options.outputDeviceId !== undefined;
Expand Down Expand Up @@ -524,7 +526,7 @@
});

if ((updateVideo || updateAudio) && this.sessionManager) {
const updateOptions: any = {};

Check warning on line 529 in src/client.ts

View workflow job for this annotation

GitHub Actions / test (22, ubuntu-latest)

Unexpected any. Specify a different type

Check warning on line 529 in src/client.ts

View workflow job for this annotation

GitHub Actions / test (20, ubuntu-latest)

Unexpected any. Specify a different type

Check warning on line 529 in src/client.ts

View workflow job for this annotation

GitHub Actions / test (18, ubuntu-latest)

Unexpected any. Specify a different type

Check warning on line 529 in src/client.ts

View workflow job for this annotation

GitHub Actions / test (22, macos-latest)

Unexpected any. Specify a different type

Check warning on line 529 in src/client.ts

View workflow job for this annotation

GitHub Actions / test (20, macos-latest)

Unexpected any. Specify a different type

Check warning on line 529 in src/client.ts

View workflow job for this annotation

GitHub Actions / test (22, windows-latest)

Unexpected any. Specify a different type

Check warning on line 529 in src/client.ts

View workflow job for this annotation

GitHub Actions / test (18, macos-latest)

Unexpected any. Specify a different type

Check warning on line 529 in src/client.ts

View workflow job for this annotation

GitHub Actions / test (18, windows-latest)

Unexpected any. Specify a different type

Check warning on line 529 in src/client.ts

View workflow job for this annotation

GitHub Actions / test (20, windows-latest)

Unexpected any. Specify a different type
if (updateVideo) updateOptions.videoDeviceId = options.videoDeviceId;
if (updateAudio) updateOptions.audioDeviceId = options.audioDeviceId;
promises.push(
Expand Down Expand Up @@ -568,7 +570,7 @@
* @returns a promise that fullfils once the event has been emitted
* signaling that the resolution has updated
*/
async updateDefaultResolution(resolution: IVideoResolution | undefined, updateActiveSessions: boolean): Promise<any> {

Check warning on line 573 in src/client.ts

View workflow job for this annotation

GitHub Actions / test (22, ubuntu-latest)

Unexpected any. Specify a different type

Check warning on line 573 in src/client.ts

View workflow job for this annotation

GitHub Actions / test (20, ubuntu-latest)

Unexpected any. Specify a different type

Check warning on line 573 in src/client.ts

View workflow job for this annotation

GitHub Actions / test (18, ubuntu-latest)

Unexpected any. Specify a different type

Check warning on line 573 in src/client.ts

View workflow job for this annotation

GitHub Actions / test (22, macos-latest)

Unexpected any. Specify a different type

Check warning on line 573 in src/client.ts

View workflow job for this annotation

GitHub Actions / test (20, macos-latest)

Unexpected any. Specify a different type

Check warning on line 573 in src/client.ts

View workflow job for this annotation

GitHub Actions / test (22, windows-latest)

Unexpected any. Specify a different type

Check warning on line 573 in src/client.ts

View workflow job for this annotation

GitHub Actions / test (18, macos-latest)

Unexpected any. Specify a different type

Check warning on line 573 in src/client.ts

View workflow job for this annotation

GitHub Actions / test (18, windows-latest)

Unexpected any. Specify a different type

Check warning on line 573 in src/client.ts

View workflow job for this annotation

GitHub Actions / test (20, windows-latest)

Unexpected any. Specify a different type
this._config.defaults.videoResolution = resolution;
if (!updateActiveSessions) {
return;
Expand Down Expand Up @@ -629,7 +631,7 @@
* @returns a promise that fullfils once the default
* settings and sessions are updated (if specified)
*/
async updateDefaultMediaSettings (settings: IMediaSettings & { updateActiveSessions?: boolean }): Promise<any> {

Check warning on line 634 in src/client.ts

View workflow job for this annotation

GitHub Actions / test (22, ubuntu-latest)

Unexpected any. Specify a different type

Check warning on line 634 in src/client.ts

View workflow job for this annotation

GitHub Actions / test (20, ubuntu-latest)

Unexpected any. Specify a different type

Check warning on line 634 in src/client.ts

View workflow job for this annotation

GitHub Actions / test (18, ubuntu-latest)

Unexpected any. Specify a different type

Check warning on line 634 in src/client.ts

View workflow job for this annotation

GitHub Actions / test (22, macos-latest)

Unexpected any. Specify a different type

Check warning on line 634 in src/client.ts

View workflow job for this annotation

GitHub Actions / test (20, macos-latest)

Unexpected any. Specify a different type

Check warning on line 634 in src/client.ts

View workflow job for this annotation

GitHub Actions / test (22, windows-latest)

Unexpected any. Specify a different type

Check warning on line 634 in src/client.ts

View workflow job for this annotation

GitHub Actions / test (18, macos-latest)

Unexpected any. Specify a different type

Check warning on line 634 in src/client.ts

View workflow job for this annotation

GitHub Actions / test (18, windows-latest)

Unexpected any. Specify a different type

Check warning on line 634 in src/client.ts

View workflow job for this annotation

GitHub Actions / test (20, windows-latest)

Unexpected any. Specify a different type
const allowedSettings: Array<keyof IMediaSettings> = [
'micAutoGainControl',
'micEchoCancellation',
Expand All @@ -637,7 +639,7 @@
'monitorMicVolume'
];

const entries = (Object.entries(settings) as Array<[keyof IMediaSettings, any]>)

Check warning on line 642 in src/client.ts

View workflow job for this annotation

GitHub Actions / test (22, ubuntu-latest)

Unexpected any. Specify a different type

Check warning on line 642 in src/client.ts

View workflow job for this annotation

GitHub Actions / test (20, ubuntu-latest)

Unexpected any. Specify a different type

Check warning on line 642 in src/client.ts

View workflow job for this annotation

GitHub Actions / test (18, ubuntu-latest)

Unexpected any. Specify a different type

Check warning on line 642 in src/client.ts

View workflow job for this annotation

GitHub Actions / test (22, macos-latest)

Unexpected any. Specify a different type

Check warning on line 642 in src/client.ts

View workflow job for this annotation

GitHub Actions / test (20, macos-latest)

Unexpected any. Specify a different type

Check warning on line 642 in src/client.ts

View workflow job for this annotation

GitHub Actions / test (22, windows-latest)

Unexpected any. Specify a different type

Check warning on line 642 in src/client.ts

View workflow job for this annotation

GitHub Actions / test (18, macos-latest)

Unexpected any. Specify a different type

Check warning on line 642 in src/client.ts

View workflow job for this annotation

GitHub Actions / test (18, windows-latest)

Unexpected any. Specify a different type

Check warning on line 642 in src/client.ts

View workflow job for this annotation

GitHub Actions / test (20, windows-latest)

Unexpected any. Specify a different type
.filter(([setting]) => allowedSettings.includes(setting));

this.logger.info('updating media settings', entries);
Expand Down Expand Up @@ -862,6 +864,51 @@
this.media.setDefaultAudioStream(stream);
}

/**
* **Genesys internal use only** - non-Genesys apps may experience unexpected behavior.
*
* Change the media handling for softphone sessions, which should be
* be chosen based on the alerting leader status of the consuming client.
*
* If media handling is reduced, idle persistent connections will be
* disconnected.
*
* @param mediaHandling how softphone media should be handled
*/
setMediaHandling (mediaHandling: MediaHandling): void {
const useHeadsets = !(mediaHandling === MediaHandling.reducedMedia);

if (!this.sessionManager) {
this._mediaHandling = mediaHandling;
this.setUseHeadsets(useHeadsets);
return;
}

const activeConversations = this.sessionManager.getAllActiveConversations();

if (activeConversations.length !== 0 && !useHeadsets) {
this._mediaHandling = MediaHandling.reducedMediaHeadsets;
this.setUseHeadsets(true);
throw createAndEmitSdkError.call(this, SdkErrorTypes.not_supported, 'Cannot downgrade media handling to stop using headsets during an active call');
}

this._mediaHandling = mediaHandling;
this.setUseHeadsets(useHeadsets);

const reduceMediaHandling = mediaHandling === MediaHandling.reducedMediaHeadsets || mediaHandling === MediaHandling.reducedMedia;
if (reduceMediaHandling) {
const conversationSessionIds = activeConversations.map(conversation => conversation.sessionId);
// Disconnect connections that aren't associated with an active conversation.
// When a client is no longer the alerting leader, it needs to give up any
// persistent connections. Active calls should be maintained.
this.sessionManager.getAllSessions().forEach(session => {
if (session.sessionType === SessionTypes.softphone && !conversationSessionIds.includes(session.id)) {
this.sessionManager.forceTerminateSession(session.id);
}
});
}
}

/**
* Accept a pending session based on the passed in conversation ID.
*
Expand Down
42 changes: 35 additions & 7 deletions src/headsets/headset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { SdkHeadsetService } from './sdk-headset-service';
import { HeadsetRequestType } from '../types/interfaces';
import { ExpandedConsumedHeadsetEvents, ISdkHeadsetService, OrchestrationState } from './headset-types';
import { HeadsetChangesQueue } from './headset-utils';
import { MediaHandling } from '../types/enums';

const REQUEST_PRIORITY: {[key in HeadsetControlsRequestType]: number} = {
'mediaHelper': 30,
Expand Down Expand Up @@ -48,10 +49,15 @@ export class HeadsetProxyService implements ISdkHeadsetService {
// TODO: PCM-2060 - remove this
this.useHeadsetOrchestration = !this.sdk._config.disableHeadsetControlsOrchestration;

if (this.sdk._mediaHandling === MediaHandling.reducedMedia) {
this.sdk.logger.warn('setUseHeadsets was called with `true` but media handling is set to `reducedMedia`; headsets are not supported in this configuration - not handling media. Not activating headsets.');
useHeadsets = false;
}

// currently only softphone is supported
const headsetsIsSupported = this.sdk._config.allowedSessionTypes.includes(SessionTypes.softphone);
if (useHeadsets && !headsetsIsSupported) {
this.sdk.logger.warn('setUseHeadsets was called with `true` but headsets are not supported in this configuration. Not activating headsets.');
this.sdk.logger.warn('setUseHeadsets was called with `true` but headsets are not supported in this configuration - headset is not supported. Not activating headsets.');
useHeadsets = false;
}

Expand Down Expand Up @@ -141,14 +147,21 @@ export class HeadsetProxyService implements ISdkHeadsetService {

this.sdk.logger.info('Starting headsetCallControls orchestration');

let requestType: HeadsetControlsRequestType;
if (this.sdk._mediaHandling === MediaHandling.alertingLeaderMedia) {
requestType = 'prioritized';
} else {
requestType = this.sdk._config.headsetRequestType || 'standard';
}

const headsetControlsRequest: HeadsetControlsRequest = {
jsonrpc: '2.0',
method: 'headsetControlsRequest',
params: {
requestType: this.sdk._config.headsetRequestType || 'standard'
requestType
}
};

this.sdk._streamingConnection.messenger.broadcastMessage({
mediaMessage: headsetControlsRequest
});
Expand All @@ -163,7 +176,7 @@ export class HeadsetProxyService implements ISdkHeadsetService {
if (state === this.orchestrationState && !forceUpdate) {
return;
}

this.sdk.logger.debug('Headset Orchestration state change', { oldState: this.orchestrationState, newState: state });

if (state === 'alternativeClient') {
Expand All @@ -188,7 +201,7 @@ export class HeadsetProxyService implements ISdkHeadsetService {
if (msg.fromMyClient) {
return;
}

switch(msg.mediaMessage.method) {
case 'headsetControlsRequest':
this.handleHeadsetControlsRequest(msg);
Expand Down Expand Up @@ -217,6 +230,21 @@ export class HeadsetProxyService implements ISdkHeadsetService {
const mediaMessage = msg.mediaMessage as HeadsetControlsRequest;
this.sdk.logger.debug('Received headsetControlsRequest message', { requestType: mediaMessage.params.requestType });

if (this.sdk._mediaHandling === MediaHandling.alertingLeaderMedia) {
// we still yield to media-helper
if (this.getRequestPriority(mediaMessage.params.requestType) === this.getRequestPriority('mediaHelper')) {
this.sdk.logger.info('Handling alerting leader media, but yielding headset controls to media-helper', { requestType: mediaMessage.params.requestType });
this.setOrchestrationState('alternativeClient');
} else if (this.getRequestPriority(mediaMessage.params.requestType) === this.getRequestPriority('prioritized')) {
this.sdk.logger.info('Currently handling alerting leader media, but yielding headset controls to new alerting leader', { requestType: mediaMessage.params.requestType });
this.setOrchestrationState('alternativeClient');
} else {
this.sendControlsRejectionMessage(msg, 'priority');
}

return;
}

// if incoming request is lower priority, reject
if (this.getRequestPriority(mediaMessage.params.requestType) < this.getRequestPriority(this.sdk._config.headsetRequestType)) {
this.sendControlsRejectionMessage(msg, this.sdk._config.headsetRequestType === 'mediaHelper' ? 'mediaHelper' : 'priority');
Expand Down Expand Up @@ -311,7 +339,7 @@ export class HeadsetProxyService implements ISdkHeadsetService {
endAllCalls (): Promise<void> {
return this.currentHeadsetService.endAllCalls();
}

answerIncomingCall (conversationId: string, autoAnswer: boolean): Promise<void> {
return this.currentHeadsetService.answerIncomingCall(conversationId, autoAnswer);
}
Expand All @@ -331,4 +359,4 @@ export class HeadsetProxyService implements ISdkHeadsetService {
resetHeadsetStateForCall(conversationId: string): Promise<void> {
return this.currentHeadsetService.resetHeadsetStateForCall(conversationId);
}
}
}
52 changes: 36 additions & 16 deletions src/sessions/softphone-session-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import {
PersistentConnectionEvent,
HawkNotification
} from '../types/interfaces';
import { SessionTypes, SdkErrorTypes, JingleReasons, CommunicationStates } from '../types/enums';
import { SessionTypes, SdkErrorTypes, JingleReasons, CommunicationStates, MediaHandling } from '../types/enums';
import { attachAudioMedia, logDeviceChange, createUniqueAudioMediaElement } from '../media/media-utils';
import { requestApi, isSoftphoneJid, createAndEmitSdkError, isPeerConnectionDisconnected } from '../utils';
import { HeadsetChangesQueue } from '../headsets/headset-utils';
Expand Down Expand Up @@ -150,31 +150,51 @@ export class SoftphoneSessionHandler extends BaseSessionHandler {
const isPrivAnswerAuto = pendingSession.privAnswerMode === 'Auto';
const eagerConnectionEstablishmentMode = this.sdk._config.eagerPersistentConnectionEstablishment;
const logInfo = { sessionId: pendingSession?.id, conversationId: pendingSession.conversationId };
const reducedMediaHandling = this.sdk._mediaHandling === MediaHandling.reducedMediaHeadsets || this.sdk._mediaHandling === MediaHandling.reducedMedia;

if (isPrivAnswerAuto) {
this.log('info', 'received a propose with privAnswerMode=Auto', logInfo);
if (reducedMediaHandling) {
this.log('info', 'received a propose while the SDK is configured for reduced media handling', logInfo);

if (eagerConnectionEstablishmentMode === 'none') {
this.log('info', 'eagerPersistentConnectionEstablishment is "none" so propose with privAnswerMode=Auto will be ignored', logInfo);
return;
} else if (eagerConnectionEstablishmentMode === 'auto') {
// we don't need to emit a pendingSession event when we auto-answer eager persistent connections
return await this.proceedWithSession(pendingSession);
} else {
if (pendingSession.autoAnswer) {
// emit the pendingSession event
await super.handlePropose(pendingSession);
}
} else {
// we want to emit the pendingSession event in all other cases
await super.handlePropose(pendingSession);

// calls will can be marked as auto-answer or priv-answer-mode: Auto, but never both
if (pendingSession.autoAnswer) {
if (this.sdk._config.disableAutoAnswer) {
// It is possible that the consuming client has its own logic for auto-answering calls (e.g. web-dir).
this.log('info', 'received an autoAnswer tagged propose but the SDK was configured to not auto-answer, deferring to the consuming client.', logInfo);
} else {
await this.proceedWithSession(pendingSession);
}
} else {
this.log('info', 'media handling is reduced, but this propose is not marked as autoAnswer and will be ignored', logInfo);
return;
}
} else {
if (isPrivAnswerAuto) {
this.log('info', 'received a propose with privAnswerMode=Auto', logInfo);

if (eagerConnectionEstablishmentMode === 'none') {
this.log('info', 'eagerPersistentConnectionEstablishment is "none" so propose with privAnswerMode=Auto will be ignored', logInfo);
return;
} else if (eagerConnectionEstablishmentMode === 'auto') {
// we don't need to emit a pendingSession event when we auto-answer eager persistent connections
return await this.proceedWithSession(pendingSession);
} else {
await super.handlePropose(pendingSession);
}
} else {
// we want to emit the pendingSession event in all other cases
await super.handlePropose(pendingSession);

// calls will can be marked as auto-answer or priv-answer-mode: Auto, but never both
if (pendingSession.autoAnswer) {
if (this.sdk._config.disableAutoAnswer) {
// It is possible that the consuming client has its own logic for auto-answering calls (e.g. web-dir).
this.log('info', 'received an autoAnswer tagged propose but the SDK was configured to not auto-answer, deferring to the consuming client.', logInfo);
} else {
await this.proceedWithSession(pendingSession);
}
}
}
}
}
Expand Down
24 changes: 24 additions & 0 deletions src/types/enums.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,27 @@ export enum JingleReasons {
connectivityError = 'connectivity-error',
alternativeSession = 'alternative-session'
}

/** These currently only affect softphone media */
export enum MediaHandling {
/** Handle all media; headset controls use traditional orchestration */
standardMedia = 'standard-media',
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Should this be standard-media or legacy-media? Basically I'm asking if the alerting-leader behavior will/should become the new default when this is flipped on.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I guess this probably a question for a PM too. I don't really want to see us manage so many different states in the future if we can avoid it.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fair question. The alerting leader behavior will not be the new default because it only applies to the clients we've hardcoded. However, I think it is possible that it will become the default at some point in the future. I think it's far enough away and unknown enough to not consider it legacy yet. 🙂

/** Handle all media; headset controls follow alerting leader */
alertingLeaderMedia = 'alerting-leader-media',
/**
* Handle some media (see below); headset controls are not used
*
* - New eager persistent connections will be ignored.
* - Auto-answer calls will be handled, which could result in a
* persistent connection being established.
*/
reducedMedia = 'reduced-media',
/**
* SDK internal use only. Handle some media (see below); headset controls use traditional orchestration.
*
* - New eager persistent connections will be ignored.
* - Auto-answer calls will be handled, which could result in a
* persistent connection being established.
*/
reducedMediaHeadsets = 'reduced-media-headsets',
}
69 changes: 68 additions & 1 deletion test/unit/client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@ import {
IStation,
IPersonDetails,
ISessionIdAndConversationId,
VideoSessionHandler
VideoSessionHandler,
MediaHandling,
IActiveConversationDescription
} from '../../src';
import * as utils from '../../src/utils';
import { RetryPromise } from 'genesys-cloud-streaming-client/dist/es/utils';
Expand Down Expand Up @@ -1159,6 +1161,71 @@ describe('Client', () => {
});
});

describe('setMediaHandling()', () => {
it('should just set media handling and headsets if there is no sessionManager yet', () => {
const mockSdk = constructSdk();
(sdk as any).sessionManager = null;
const useHeadsetsSpy = jest.fn();
mockSdk.setUseHeadsets = useHeadsetsSpy;

sdk.setMediaHandling(MediaHandling.reducedMediaHeadsets);

expect(sdk._mediaHandling).toBe(MediaHandling.reducedMediaHeadsets);
expect(useHeadsetsSpy).toHaveBeenCalledWith(true);
});

it('should throw and reduce media handling if new media handling will not use headsets but there is an active conversation', () => {
sdk = constructSdk();
const conversations = [{ conversationId: 'test-conversation-id' }] as IActiveConversationDescription[];
sessionManagerMock.getAllActiveConversations.mockReturnValue(conversations);

expect(() => {
sdk.setMediaHandling(MediaHandling.reducedMedia);
}).toThrow();
expect(sdk._mediaHandling).toBe(MediaHandling.reducedMediaHeadsets);
});

it('should disconnect any sessions not connected to an active conversation when set to a reduced handling of media', () => {
sdk = constructSdk();
sdk.setUseHeadsets = jest.fn();

const mockSession = new MockSession(SessionTypes.softphone);
const conversations = [{ sessionId: mockSession.id }] as IActiveConversationDescription[];
sessionManagerMock.getAllActiveConversations.mockReturnValue(conversations);
const idleSession = new MockSession(SessionTypes.softphone);
const idleSessionId = idleSession.id;
const sessions = [mockSession, idleSession] as unknown as IExtendedMediaSession[];
sessionManagerMock.getAllSessions.mockReturnValue(sessions);
const forceTerminateSpy = jest.fn();
sessionManagerMock.forceTerminateSession = forceTerminateSpy;

sdk.setMediaHandling(MediaHandling.reducedMediaHeadsets);

expect(sdk._mediaHandling).toBe(MediaHandling.reducedMediaHeadsets);
expect(forceTerminateSpy).toHaveBeenCalledTimes(1);
expect(forceTerminateSpy).toHaveBeenCalledWith(idleSessionId);
});

it('should use headsets when handling any media', () => {
sdk = constructSdk();
sessionManagerMock.getAllActiveConversations.mockReturnValue([]);
const useHeadsetsSpy = jest.fn();
sdk.setUseHeadsets = useHeadsetsSpy;

sdk.setMediaHandling(MediaHandling.standardMedia);
expect(sdk._mediaHandling).toBe(MediaHandling.standardMedia);
expect(useHeadsetsSpy).toHaveBeenCalledWith(true);

sdk.setMediaHandling(MediaHandling.alertingLeaderMedia);
expect(sdk._mediaHandling).toBe(MediaHandling.alertingLeaderMedia);
expect(useHeadsetsSpy).toHaveBeenCalledWith(true);

sdk.setMediaHandling(MediaHandling.reducedMediaHeadsets);
expect(sdk._mediaHandling).toBe(MediaHandling.reducedMediaHeadsets);
expect(useHeadsetsSpy).toHaveBeenCalledWith(true);
});
});

describe('destroy()', () => {
it('should log, end all sessions, remove listeners, destory media, and disconnect ws', async () => {
sdk = constructSdk();
Expand Down
Loading
Loading