Skip to content

Commit

Permalink
feat(call-history): update the callhistory when the user reads missed…
Browse files Browse the repository at this point in the history
… calls (#3580)
  • Loading branch information
sokn-sys authored May 10, 2024
1 parent bd29ca8 commit e65ff8f
Show file tree
Hide file tree
Showing 7 changed files with 339 additions and 9 deletions.
137 changes: 134 additions & 3 deletions packages/calling/src/CallHistory/CallHistory.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,31 @@
/* eslint-disable @typescript-eslint/no-shadow */
import {LOGGER} from '../Logger/types';
import {getTestUtilsWebex} from '../common/testUtil';
import {SORT, SORT_BY, WebexRequestPayload} from '../common/types';
import {HTTP_METHODS, SORT, SORT_BY, WebexRequestPayload} from '../common/types';
import {CallHistory, createCallHistoryClient} from './CallHistory';
import {ICallHistory} from './types';
import {
sortedCallHistory,
mockCallHistoryBody,
MOCK_SESSION_EVENT,
MOCK_SESSION_EVENT_LEGACY,
MOCK_SESSION_EVENT_VIEWED,
MOCK_UPDATE_MISSED_CALL_RESPONSE,
janusSetReadStateUrl,
ERROR_DETAILS_401,
ERROR_DETAILS_400,
} from './callHistoryFixtures';
import {COMMON_EVENT_KEYS, CallSessionEvent, MOBIUS_EVENT_KEYS} from '../Events/types';
import {
COMMON_EVENT_KEYS,
CallSessionEvent,
CallSessionViewedEvent,
MOBIUS_EVENT_KEYS,
} from '../Events/types';
import {APPLICATION_JSON, CALL_HISTORY_FILE, CONTENT_TYPE} from './constants';
import * as utils from '../common/Utils';

const webex = getTestUtilsWebex();

let serviceErrorCodeHandlerSpy: jest.SpyInstance;
describe('Call history tests', () => {
let callHistory: ICallHistory;

Expand Down Expand Up @@ -115,5 +127,124 @@ describe('Call history tests', () => {

callSessionCallback(MOCK_SESSION_EVENT_LEGACY);
});

it('verify the user viewed session event for missed calls update', async () => {
await new Promise<void>((resolve) => {
callHistory.on(
COMMON_EVENT_KEYS.CALL_HISTORY_USER_VIEWED_SESSIONS,
(event: CallSessionViewedEvent) => {
expect(event.data).toEqual(MOCK_SESSION_EVENT_VIEWED.data);
resolve();
}
);

expect(mockOn.mock.calls[2][0]).toEqual(MOBIUS_EVENT_KEYS.CALL_SESSION_EVENT_VIEWED);
const callSessionCallback = mockOn.mock.calls[2][1];

callSessionCallback(MOCK_SESSION_EVENT_VIEWED);
});
});
});

describe('Update missed calls test', () => {
const methodDetails = {
file: CALL_HISTORY_FILE,
method: 'updateMissedCalls',
};
afterEach(() => {
jest.clearAllMocks();
});
beforeEach(async () => {
serviceErrorCodeHandlerSpy = jest.spyOn(utils, 'serviceErrorCodeHandler');
global.fetch = jest.fn(() =>
Promise.resolve({
status: 200,
ok: true,
json: () => Promise.resolve(MOCK_UPDATE_MISSED_CALL_RESPONSE),
})
) as jest.Mock;
});

it('successfully updates missed calls', async () => {
const endTimeSessionIds = [{endTime: '1234568', sessionId: '123'}];
const response = await callHistory.updateMissedCalls(endTimeSessionIds);
const convertedEndTimeSessionIds = endTimeSessionIds.map((session) => ({
...session,
endTime: new Date(session.endTime).getTime(),
}));
expect(response.statusCode).toEqual(200);
expect(response).toEqual(MOCK_UPDATE_MISSED_CALL_RESPONSE);
expect(global.fetch).toBeCalledOnceWith(janusSetReadStateUrl, {
method: HTTP_METHODS.POST,
headers: {
[CONTENT_TYPE]: APPLICATION_JSON,
Authorization: await webex.credentials.getUserToken(),
},
body: JSON.stringify({endTimeSessionIds: convertedEndTimeSessionIds}),
});
});

it('Error: updateMissedCalls throw 400 error', async () => {
const endTimeSessionIds = [];
global.fetch = jest.fn(() =>
Promise.resolve({
status: 400,
ok: false,
})
) as jest.Mock;
const response = await callHistory.updateMissedCalls(endTimeSessionIds);
const convertedEndTimeSessionIds = endTimeSessionIds.map((session) => ({
...session,
endTime: new Date(session.endTime).getTime(),
}));
expect(response).toStrictEqual(ERROR_DETAILS_400);
expect(response.statusCode).toBe(400);
expect(global.fetch).toBeCalledOnceWith(janusSetReadStateUrl, {
method: HTTP_METHODS.POST,
headers: {
[CONTENT_TYPE]: APPLICATION_JSON,
Authorization: await webex.credentials.getUserToken(),
},
body: JSON.stringify({endTimeSessionIds: convertedEndTimeSessionIds}),
});
expect(serviceErrorCodeHandlerSpy).toBeCalledOnceWith(
{
statusCode: 400,
},
methodDetails
);
});

it('Error: updateMissedCalls throw 401 error', async () => {
const endTimeSessionIds = [];
global.fetch = jest.fn(() =>
Promise.resolve({
status: 401,
ok: false,
})
) as jest.Mock;

const response = await callHistory.updateMissedCalls(endTimeSessionIds);
const convertedEndTimeSessionIds = endTimeSessionIds.map((session) => ({
...session,
endTime: new Date(session.endTime).getTime(),
}));
expect(response).toStrictEqual(ERROR_DETAILS_401);
expect(response.statusCode).toBe(401);
expect(global.fetch).toBeCalledOnceWith(janusSetReadStateUrl, {
method: HTTP_METHODS.POST,
headers: {
[CONTENT_TYPE]: APPLICATION_JSON,
Authorization: await webex.credentials.getUserToken(),
},
body: JSON.stringify({endTimeSessionIds: convertedEndTimeSessionIds}),
});
expect(serviceErrorCodeHandlerSpy).toBeCalledOnceWith(
{
statusCode: 401,
},
methodDetails
);
});
});
});
92 changes: 90 additions & 2 deletions packages/calling/src/CallHistory/CallHistory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,35 @@
import SDKConnector from '../SDKConnector';
import {ISDKConnector, WebexSDK} from '../SDKConnector/types';
import {ALLOWED_SERVICES, HTTP_METHODS, WebexRequestPayload, SORT, SORT_BY} from '../common/types';
import {ICallHistory, JanusResponseEvent, LoggerInterface} from './types';
import {
ICallHistory,
JanusResponseEvent,
LoggerInterface,
UpdateMissedCallsResponse,
} from './types';
import log from '../Logger';
import {serviceErrorCodeHandler} from '../common/Utils';
import {CALL_HISTORY_FILE, FROM_DATE, HISTORY, LIMIT, NUMBER_OF_DAYS} from './constants';
import {
APPLICATION_JSON,
CALL_HISTORY_FILE,
CONTENT_TYPE,
FROM_DATE,
HISTORY,
LIMIT,
NUMBER_OF_DAYS,
UPDATE_MISSED_CALLS_ENDPOINT,
SET_READ_STATE_SUCCESS_MESSAGE,
} from './constants';
import {STATUS_CODE, SUCCESS_MESSAGE, USER_SESSIONS} from '../common/constants';
import {
COMMON_EVENT_KEYS,
CallHistoryEventTypes,
CallSessionEvent,
MOBIUS_EVENT_KEYS,
UserSession,
EndTimeSessionId,
CallSessionViewedEvent,
SanitizedEndTimeAndSessionId,
} from '../Events/types';
import {Eventing} from '../Events/impl';
/**
Expand Down Expand Up @@ -127,12 +145,78 @@ export class CallHistory extends Eventing<CallHistoryEventTypes> implements ICal
}
}

/**
* Function to update the missed call status in the call history using sessionId and time.
* @param endTimeSessionIds - An array of objects containing endTime and sessionId of the missed call history records
* @returns {Promise} Resolves to an object of type {@link UpdateMissedCallsResponse}.Response details with success or error status.
*/
public async updateMissedCalls(
endTimeSessionIds: EndTimeSessionId[]
): Promise<UpdateMissedCallsResponse> {
const loggerContext = {
file: CALL_HISTORY_FILE,
method: 'updateMissedCalls',
};
// Convert endTime to milliseconds for each session
const santizedSessionIds: SanitizedEndTimeAndSessionId[] = endTimeSessionIds.map((session) => ({
...session,
endTime: new Date(session.endTime).getTime(),
}));
const requestBody = {
endTimeSessionIds: santizedSessionIds,
};
try {
const updateMissedCallContentUrl = `${this.janusUrl}/${HISTORY}/${USER_SESSIONS}/${UPDATE_MISSED_CALLS_ENDPOINT}`;
// Make a POST request to update missed calls
const response = await fetch(updateMissedCallContentUrl, {
method: HTTP_METHODS.POST,
headers: {
[CONTENT_TYPE]: APPLICATION_JSON,
Authorization: await this.webex.credentials.getUserToken(),
},
body: JSON.stringify(requestBody),
});
if (!response.ok) {
throw new Error(`${response.status}`);
}

const data: UpdateMissedCallsResponse = await response.json();
log.info(`Missed calls are succesfully read by the user`, loggerContext);
const responseDetails: UpdateMissedCallsResponse = {
statusCode: data.statusCode as number,
data: {
readStatusMessage: SET_READ_STATE_SUCCESS_MESSAGE,
},
message: SUCCESS_MESSAGE,
};

return responseDetails;
} catch (err: unknown) {
// Catch the 401 error from try block, return the error object to user
const errorInfo = {
statusCode: err instanceof Error ? Number(err.message) : '',
} 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);
}
};

handleUserReadSessionEvents = async (event?: CallSessionViewedEvent) => {
if (event && event.data.userReadSessions.userReadSessions) {
this.emit(
COMMON_EVENT_KEYS.CALL_HISTORY_USER_VIEWED_SESSIONS,
event as CallSessionViewedEvent
);
}
};

/**
*
*/
Expand All @@ -145,6 +229,10 @@ export class CallHistory extends Eventing<CallHistoryEventTypes> implements ICal
MOBIUS_EVENT_KEYS.CALL_SESSION_EVENT_LEGACY,
this.handleSessionEvents
);
this.sdkConnector.registerListener<CallSessionViewedEvent>(
MOBIUS_EVENT_KEYS.CALL_SESSION_EVENT_VIEWED,
this.handleUserReadSessionEvents
);
}
}
/**
Expand Down
51 changes: 50 additions & 1 deletion packages/calling/src/CallHistory/callHistoryFixtures.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
import {Disposition, MOBIUS_EVENT_KEYS, CallSessionEvent, SessionType} from '../Events/types';
import {
Disposition,
MOBIUS_EVENT_KEYS,
CallSessionEvent,
SessionType,
CallSessionViewedEvent,
} from '../Events/types';
import {UpdateMissedCallsResponse} from './types';

export const sortedCallHistory = {
body: {
Expand Down Expand Up @@ -365,6 +372,10 @@ const SPARK_CALL_SESSION = {
correlationIds: ['dfe83701-3e1f-4e1d-8aeb-6fe81f53b653'],
};

const SPARK_CALL_VIEWED_SESSION = {
sessionId: '2ce4b9e0-7ee7-12a1-4a94-df524443b520',
};

export const MOCK_SESSION_EVENT: CallSessionEvent = {
id: 'id',
data: {
Expand All @@ -390,3 +401,41 @@ export const MOCK_SESSION_EVENT_LEGACY: CallSessionEvent = {
timestamp: 12345,
trackingId: 'tracking-id',
};

export const MOCK_SESSION_EVENT_VIEWED: CallSessionViewedEvent = {
id: 'id',
data: {
userReadSessions: {
userReadSessions: [SPARK_CALL_VIEWED_SESSION],
statusCode: 0,
},
eventType: MOBIUS_EVENT_KEYS.CALL_SESSION_EVENT_VIEWED,
},
timestamp: 12345,
trackingId: 'tracking-id',
};

export const MOCK_UPDATE_MISSED_CALL_RESPONSE: UpdateMissedCallsResponse = {
statusCode: 200,
data: {
readStatusMessage: 'Missed calls are read by the user.',
},
message: 'SUCCESS',
};
export const janusSetReadStateUrl =
'https://janus-intb.ciscospark.com/janus/api/v1/history/userSessions/setReadState';

export const ERROR_DETAILS_401 = {
statusCode: 401,
data: {
error: 'User is unauthorised, possible token expiry',
},
message: 'FAILURE',
};
export const ERROR_DETAILS_400 = {
statusCode: 400,
data: {
error: '400 Bad request',
},
message: 'FAILURE',
};
4 changes: 4 additions & 0 deletions packages/calling/src/CallHistory/constants.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
export const APPLICATION_JSON = 'application/json';
export const CALL_HISTORY_FILE = 'CallHistory';
export const CONTENT_TYPE = 'Content-Type';
export const FROM_DATE = '?from';
export const HISTORY = 'history';
export const LIMIT = 50;
export const NUMBER_OF_DAYS = 10;
export const RESPONSE_MESSAGE = 'responseMessage';
export const UPDATE_MISSED_CALLS_ENDPOINT = 'setReadState';
export const SET_READ_STATE_SUCCESS_MESSAGE = 'Missed calls are read by the user.';
export const SUCCESS_MESSAGE = 'SUCCESS';
export const STATUS_CODE = 'statusCode';
export const USER_SESSIONS = 'userSessions';
Loading

0 comments on commit e65ff8f

Please sign in to comment.