Skip to content

Commit

Permalink
feat(callhistory): add-support-for-multiline-in-callhistory (webex#3816)
Browse files Browse the repository at this point in the history
  • Loading branch information
akulakum authored Sep 18, 2024
1 parent c04a29f commit e42b390
Show file tree
Hide file tree
Showing 7 changed files with 497 additions and 10 deletions.
115 changes: 114 additions & 1 deletion packages/calling/src/CallHistory/CallHistory.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
/* eslint-disable @typescript-eslint/no-shadow */
import {LOGGER} from '../Logger/types';
import {getTestUtilsWebex} from '../common/testUtil';
import {HTTP_METHODS, SORT, SORT_BY, WebexRequestPayload} from '../common/types';
import {CALLING_BACKEND, HTTP_METHODS, SORT, SORT_BY, WebexRequestPayload} from '../common/types';
import {CallHistory, createCallHistoryClient} from './CallHistory';
import {ICallHistory} from './types';
import {
Expand All @@ -16,6 +16,10 @@ import {
janusSetReadStateUrl,
ERROR_DETAILS_401,
ERROR_DETAILS_400,
MOCK_LINES_API_CALL_RESPONSE,
MOCK_LINES_API_CALL_RESPONSE_WITH_NO_LINEDATA,
MOCK_CALL_HISTORY_WITH_UCM_LINE_NUMBER,
MOCK_CALL_HISTORY_WITHOUT_UCM_LINE_NUMBER,
} from './callHistoryFixtures';
import {
COMMON_EVENT_KEYS,
Expand Down Expand Up @@ -247,4 +251,113 @@ describe('Call history tests', () => {
);
});
});

describe('fetchUCMLinesData test', () => {
it('verify successful UCM lines API case', async () => {
const ucmLinesAPIPayload = <WebexRequestPayload>(<unknown>MOCK_LINES_API_CALL_RESPONSE);

webex.request.mockResolvedValue(ucmLinesAPIPayload);
const response = await callHistory['fetchUCMLinesData']();

expect(response.statusCode).toBe(200);
expect(response.message).toBe('SUCCESS');
});

it('verify bad request failed UCM lines API case', async () => {
const failurePayload = {
statusCode: 400,
};
const ucmLinesAPIPayload = <WebexRequestPayload>(<unknown>failurePayload);

webex.request.mockRejectedValue(ucmLinesAPIPayload);
const response = await callHistory['fetchUCMLinesData']();

expect(response).toStrictEqual(ERROR_DETAILS_400);
expect(response.data.error).toEqual(ERROR_DETAILS_400.data.error);
expect(response.statusCode).toBe(400);
expect(response.message).toBe('FAILURE');
expect(serviceErrorCodeHandlerSpy).toHaveBeenCalledWith(
{statusCode: 400},
{file: 'CallHistory', method: 'fetchLinesData'}
);
});

it('should call fetchUCMLinesData when calling backend is UCM and userSessions contain valid cucmDN', async () => {
jest.spyOn(utils, 'getCallingBackEnd').mockReturnValue(CALLING_BACKEND.UCM);
// Since fetchUCMLinesData is a private method, TypeScript restricts direct access to it.
// To bypass this restriction, we are using 'as any' to access and invoke the method for testing purposes.
const fetchUCMLinesDataSpy = jest
.spyOn(callHistory as any, 'fetchUCMLinesData')
.mockResolvedValue(MOCK_LINES_API_CALL_RESPONSE);

const mockCallHistoryPayload = <WebexRequestPayload>(
(<unknown>MOCK_CALL_HISTORY_WITH_UCM_LINE_NUMBER)
);
webex.request.mockResolvedValue(mockCallHistoryPayload);

const response = await callHistory.getCallHistoryData(7, 10, SORT.DEFAULT, SORT_BY.DEFAULT);

expect(fetchUCMLinesDataSpy).toHaveBeenCalledTimes(1);

expect(response.statusCode).toBe(200);
expect(
response.data.userSessions && response.data.userSessions[0].self.ucmLineNumber
).toEqual(1);
});

it('should fetchUCMLinesData but not assign ucmLineNumber when UCM backend has no line data', async () => {
jest.spyOn(utils, 'getCallingBackEnd').mockReturnValue(CALLING_BACKEND.UCM);

// Since fetchUCMLinesData is a private method, TypeScript restricts direct access to it.
// To bypass this restriction, we are using 'as any' to access and invoke the method for testing purposes.
const fetchUCMLinesDataSpy = jest
.spyOn(callHistory as any, 'fetchUCMLinesData')
.mockResolvedValue(MOCK_LINES_API_CALL_RESPONSE_WITH_NO_LINEDATA);

const mockCallHistoryPayload = <WebexRequestPayload>(
(<unknown>MOCK_CALL_HISTORY_WITHOUT_UCM_LINE_NUMBER)
);
webex.request.mockResolvedValue(mockCallHistoryPayload);

const response = await callHistory.getCallHistoryData(7, 10, SORT.DEFAULT, SORT_BY.DEFAULT);

expect(fetchUCMLinesDataSpy).toHaveBeenCalledTimes(1);

expect(response.statusCode).toBe(200);
expect(response.data.userSessions && response.data.userSessions[0].self.cucmDN).toBeDefined();
expect(
response.data.userSessions && response.data.userSessions[0].self.ucmLineNumber
).toEqual(undefined);
});

it('should not call fetchUCMLinesData when calling backend is UCM but no valid cucmDN is present', async () => {
jest.spyOn(utils, 'getCallingBackEnd').mockReturnValue(CALLING_BACKEND.UCM);
// Since fetchUCMLinesData is a private method, TypeScript restricts direct access to it.
// To bypass this restriction, we are using 'as any' to access and invoke the method for testing purposes.
const fetchUCMLinesDataSpy = jest
.spyOn(callHistory as any, 'fetchUCMLinesData')
.mockResolvedValue({});

const callHistoryPayload = <WebexRequestPayload>(<unknown>mockCallHistoryBody);
webex.request.mockResolvedValue(callHistoryPayload);

await callHistory.getCallHistoryData(7, 10, SORT.DEFAULT, SORT_BY.DEFAULT);

expect(fetchUCMLinesDataSpy).not.toHaveBeenCalled();
});

it('should not call fetchUCMLinesData when calling backend is not UCM', async () => {
jest.spyOn(utils, 'getCallingBackEnd').mockReturnValue(CALLING_BACKEND.WXC);
// Since fetchUCMLinesData is a private method, TypeScript restricts direct access to it.
// To bypass this restriction, we are using 'as any' to access and invoke the method for testing purposes.
const fetchUCMLinesDataSpy = jest
.spyOn(callHistory as any, 'fetchUCMLinesData')
.mockResolvedValue({});

const callHistoryPayload = <WebexRequestPayload>(<unknown>mockCallHistoryBody);
webex.request.mockResolvedValue(callHistoryPayload);
await callHistory.getCallHistoryData(7, 10, SORT.DEFAULT, SORT_BY.DEFAULT);
expect(fetchUCMLinesDataSpy).not.toHaveBeenCalled(); // Check that fetchUCMLinesData was not called
});
});
});
95 changes: 93 additions & 2 deletions packages/calling/src/CallHistory/CallHistory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,23 @@
/* eslint-disable no-underscore-dangle */
import SDKConnector from '../SDKConnector';
import {ISDKConnector, WebexSDK} from '../SDKConnector/types';
import {ALLOWED_SERVICES, HTTP_METHODS, WebexRequestPayload, SORT, SORT_BY} from '../common/types';
import {
ALLOWED_SERVICES,
HTTP_METHODS,
WebexRequestPayload,
SORT,
SORT_BY,
CALLING_BACKEND,
} from '../common/types';
import {
ICallHistory,
JanusResponseEvent,
LoggerInterface,
UpdateMissedCallsResponse,
UCMLinesResponse,
} from './types';
import log from '../Logger';
import {serviceErrorCodeHandler} from '../common/Utils';
import {serviceErrorCodeHandler, getVgActionEndpoint, getCallingBackEnd} from '../common/Utils';
import {
APPLICATION_JSON,
CALL_HISTORY_FILE,
Expand All @@ -21,6 +29,12 @@ import {
NUMBER_OF_DAYS,
UPDATE_MISSED_CALLS_ENDPOINT,
SET_READ_STATE_SUCCESS_MESSAGE,
VERSION_1,
UNIFIED_COMMUNICATIONS,
CONFIG,
PEOPLE,
LINES,
ORG_ID,
} from './constants';
import {STATUS_CODE, SUCCESS_MESSAGE, USER_SESSIONS} from '../common/constants';
import {
Expand All @@ -32,6 +46,7 @@ import {
EndTimeSessionId,
CallSessionViewedEvent,
SanitizedEndTimeAndSessionId,
UCMLinesApiResponse,
} from '../Events/types';
import {Eventing} from '../Events/impl';
/**
Expand Down Expand Up @@ -128,6 +143,43 @@ export class CallHistory extends Eventing<CallHistoryEventTypes> implements ICal
);
}
}
// Check the calling backend
const callingBackend = getCallingBackEnd(this.webex);
if (callingBackend === CALLING_BACKEND.UCM) {
// Check if userSessions exist and the length is greater than 0
if (this.userSessions[USER_SESSIONS] && this.userSessions[USER_SESSIONS].length > 0) {
// Check if cucmDN exists and is valid in any of the userSessions
const hasCucmDN = this.userSessions[USER_SESSIONS].some(
(session: UserSession) => session.self.cucmDN && session.self.cucmDN.length > 0
);
// If any user session has cucmDN, proceed to fetch line data
if (hasCucmDN) {
// Fetch the Lines data
const ucmLinesResponse = await this.fetchUCMLinesData();

// Check if the Lines API response was successful
if (ucmLinesResponse.statusCode === 200 && ucmLinesResponse.data.lines?.devices) {
const ucmLinesData = ucmLinesResponse.data.lines.devices;

// Iterate over user sessions and match with Lines data
this.userSessions[USER_SESSIONS].forEach((session: UserSession) => {
const cucmDN = session.self.cucmDN;

if (cucmDN) {
ucmLinesData.forEach((device) => {
device.lines.forEach((line) => {
if (line.dnorpattern === cucmDN) {
session.self.ucmLineNumber = line.index; // Assign the ucmLineNumber
}
});
});
}
});
}
}
}
}

const responseDetails = {
statusCode: this.userSessions[STATUS_CODE],
data: {
Expand Down Expand Up @@ -202,6 +254,45 @@ export class CallHistory extends Eventing<CallHistoryEventTypes> implements ICal
}
}

/**
* Function to display the UCM Lines API response.
* @returns {Promise} Resolves to an object of type {@link UCMLinesResponse}.Response details with success or error status.
*/
private async fetchUCMLinesData(): Promise<UCMLinesResponse> {
const loggerContext = {
file: CALL_HISTORY_FILE,
method: 'fetchLinesData',
};
const vgEndpoint = getVgActionEndpoint(this.webex, CALLING_BACKEND.UCM);
const userId = this.webex.internal.device.userId;
const orgId = this.webex.internal.device.orgId;
const linesURIForUCM = `${vgEndpoint}/${VERSION_1}/${UNIFIED_COMMUNICATIONS}/${CONFIG}/${PEOPLE}/${userId}/${LINES}?${ORG_ID}=${orgId}`;

try {
const response = <WebexRequestPayload>await this.webex.request({
uri: `${linesURIForUCM}`,
method: HTTP_METHODS.GET,
});

const ucmLineDetails: UCMLinesResponse = {
statusCode: Number(response.statusCode),
data: {
lines: response.body as UCMLinesApiResponse,
},
message: SUCCESS_MESSAGE,
};

log.info(`Line details fetched successfully`, loggerContext);

return ucmLineDetails;
} catch (err: unknown) {
const errorInfo = err as WebexRequestPayload;
const errorStatus = serviceErrorCodeHandler(errorInfo, loggerContext);

return errorStatus;
}
}

handleSessionEvents = async (event?: CallSessionEvent) => {
if (event && event.data.userSessions.userSessions) {
this.emit(COMMON_EVENT_KEYS.CALL_HISTORY_USER_SESSION_INFO, event as CallSessionEvent);
Expand Down
Loading

0 comments on commit e42b390

Please sign in to comment.