Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve getTextDescriptionForCallevent & introduce frontend error for phonenumber conflict #128

Merged
merged 11 commits into from
Nov 18, 2024
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@sipgate/integration-bridge",
"version": "1.0.9",
"version": "1.0.10",
"description": "sipgate Integration Bridge Framework",
"main": "dist/index.js",
"types": "dist/index.d.ts",
Expand Down
1 change: 1 addition & 0 deletions src/models/integration-error.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export enum IntegrationErrorType {
CONTACT_CREATE_ERROR_EMAIL_CONFLICT = 'contact/create-error/email-conflict',
CONTACT_ERROR_TOO_MANY_NUMBERS = 'contact/error/too-many-numbers',
CONTACT_ERROR_INVALID_PHONE_TYPE = 'contact/error/invalid-phone-type',
CONTACT_ERROR_PHONENUMBER_EXISTS = 'contact/error/phonenumber-exists',
}

export const DELEGATE_TO_FRONTEND_CODE = 452;
180 changes: 180 additions & 0 deletions src/util/callEventHelper.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
import {
CallDirection,
CallEvent,
CallParticipantType,
CallState,
} from '../models';
import { getTextDescriptionForCallevent } from './callEventHelper';

const generateBaseCallEvent = (): CallEvent => ({
id: 'callEventId123',
startTime: 1705832625000,
endTime: 1705833276000,
direction: CallDirection.IN,
participants: [
{
type: CallParticipantType.LOCAL,
phoneNumber: '4921177722233',
},
{
type: CallParticipantType.REMOTE,
phoneNumber: '4922199911122',
},
],
note: 'testnote01',
state: CallState.CONNECTED,
});

describe('callEventHelper', () => {
describe('getTextDescriptionForCallevent for german locale', () => {
it('should generate sane description for incoming, connected callEvent', () => {
const callEvent = generateBaseCallEvent();

expect(getTextDescriptionForCallevent(callEvent)).toEqual(
'Angenommener eingehender Anruf von 4922199911122 auf 4921177722233 am 21.1.2024, 11:23:45 Uhr, Dauer: 10:51 Minuten.',
);
});

it('should generate sane description for outgoing, connected callEvent', () => {
const callEvent = generateBaseCallEvent();
callEvent.direction = CallDirection.OUT;

expect(getTextDescriptionForCallevent(callEvent)).toEqual(
'Angenommener ausgehender Anruf von 4921177722233 auf 4922199911122 am 21.1.2024, 11:23:45 Uhr, Dauer: 10:51 Minuten.',
);
});

it('should generate sane description for incoming, missed callEvent', () => {
const callEvent = generateBaseCallEvent();
callEvent.state = CallState.MISSED;

expect(getTextDescriptionForCallevent(callEvent)).toEqual(
'Nicht angenommener eingehender Anruf von 4922199911122 auf 4921177722233 am 21.1.2024, 11:23:45 Uhr.',
);
});

it('should generate sane description for outgoing, missed callEvent', () => {
const callEvent = generateBaseCallEvent();
callEvent.direction = CallDirection.OUT;
callEvent.state = CallState.MISSED;

expect(getTextDescriptionForCallevent(callEvent)).toEqual(
'Nicht angenommener ausgehender Anruf von 4921177722233 auf 4922199911122 am 21.1.2024, 11:23:45 Uhr.',
);
});

it('should generate sane description for incoming, busy callEvent', () => {
const callEvent = generateBaseCallEvent();
callEvent.state = CallState.BUSY;

expect(getTextDescriptionForCallevent(callEvent)).toEqual(
'Nicht angenommener eingehender Anruf von 4922199911122 auf 4921177722233 am 21.1.2024, 11:23:45 Uhr.',
);
});

it('should generate sane description for outgoing, busy callEvent', () => {
const callEvent = generateBaseCallEvent();
callEvent.direction = CallDirection.OUT;
callEvent.state = CallState.BUSY;

expect(getTextDescriptionForCallevent(callEvent)).toEqual(
'Nicht angenommener ausgehender Anruf von 4921177722233 auf 4922199911122 am 21.1.2024, 11:23:45 Uhr.',
);
});

it('should generate sane description for incoming, not_found callEvent', () => {
const callEvent = generateBaseCallEvent();
callEvent.state = CallState.NOT_FOUND;

expect(getTextDescriptionForCallevent(callEvent)).toEqual(
'Nicht angenommener eingehender Anruf von 4922199911122 auf 4921177722233 am 21.1.2024, 11:23:45 Uhr.',
);
});

it('should generate sane description for outgoing, not_found callEvent', () => {
const callEvent = generateBaseCallEvent();
callEvent.direction = CallDirection.OUT;
callEvent.state = CallState.NOT_FOUND;

expect(getTextDescriptionForCallevent(callEvent)).toEqual(
'Nicht angenommener ausgehender Anruf von 4921177722233 auf 4922199911122 am 21.1.2024, 11:23:45 Uhr.',
);
});
});

describe('getTextDescriptionForCallevent for english locale', () => {
it('should generate sane description for incoming, connected callEvent', () => {
const callEvent = generateBaseCallEvent();

expect(getTextDescriptionForCallevent(callEvent, 'en-US')).toEqual(
'Answered incoming call from 4922199911122 to 4921177722233 on 1/21/2024, 11:23:45 AM, duration: 10:51 minutes.',
);
});

it('should generate sane description for outgoing, connected callEvent', () => {
const callEvent = generateBaseCallEvent();
callEvent.direction = CallDirection.OUT;

expect(getTextDescriptionForCallevent(callEvent, 'en-US')).toEqual(
'Answered outgoing call from 4921177722233 to 4922199911122 on 1/21/2024, 11:23:45 AM, duration: 10:51 minutes.',
);
});

it('should generate sane description for incoming, missed callEvent', () => {
const callEvent = generateBaseCallEvent();
callEvent.state = CallState.MISSED;

expect(getTextDescriptionForCallevent(callEvent, 'en-US')).toEqual(
'Unanswered incoming call from 4922199911122 to 4921177722233 on 1/21/2024, 11:23:45 AM.',
);
});

it('should generate sane description for outgoing, missed callEvent', () => {
const callEvent = generateBaseCallEvent();
callEvent.direction = CallDirection.OUT;
callEvent.state = CallState.MISSED;

expect(getTextDescriptionForCallevent(callEvent, 'en-US')).toEqual(
'Unanswered outgoing call from 4921177722233 to 4922199911122 on 1/21/2024, 11:23:45 AM.',
);
});

it('should generate sane description for incoming, busy callEvent', () => {
const callEvent = generateBaseCallEvent();
callEvent.state = CallState.BUSY;

expect(getTextDescriptionForCallevent(callEvent, 'en-US')).toEqual(
'Unanswered incoming call from 4922199911122 to 4921177722233 on 1/21/2024, 11:23:45 AM.',
);
});

it('should generate sane description for outgoing, busy callEvent', () => {
const callEvent = generateBaseCallEvent();
callEvent.direction = CallDirection.OUT;
callEvent.state = CallState.BUSY;

expect(getTextDescriptionForCallevent(callEvent, 'en-US')).toEqual(
'Unanswered outgoing call from 4921177722233 to 4922199911122 on 1/21/2024, 11:23:45 AM.',
);
});

it('should generate sane description for incoming, not_found callEvent', () => {
const callEvent = generateBaseCallEvent();
callEvent.state = CallState.NOT_FOUND;

expect(getTextDescriptionForCallevent(callEvent, 'en-US')).toEqual(
'Unanswered incoming call from 4922199911122 to 4921177722233 on 1/21/2024, 11:23:45 AM.',
);
});

it('should generate sane description for outgoing, not_found callEvent', () => {
const callEvent = generateBaseCallEvent();
callEvent.direction = CallDirection.OUT;
callEvent.state = CallState.NOT_FOUND;

expect(getTextDescriptionForCallevent(callEvent, 'en-US')).toEqual(
'Unanswered outgoing call from 4921177722233 to 4922199911122 on 1/21/2024, 11:23:45 AM.',
);
});
});
});
104 changes: 83 additions & 21 deletions src/util/callEventHelper.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import { CallEvent, CallParticipantType, CallDirection } from '../models';
import { startsWith } from 'lodash';
import {
CallEvent,
CallParticipantType,
CallDirection,
CallState,
} from '../models';

export interface CallMembers {
from: string | undefined;
Expand All @@ -24,35 +30,91 @@ export const getCallMembers = (event: CallEvent): CallMembers => {

return { from, to };
};
function formatDuration(durationInMilliSeconds: number): string {
const minutes = Math.floor(durationInMilliSeconds / (60 * 1000));
const seconds = Math.floor((durationInMilliSeconds - minutes * 60) / 1000)

const formatDuration = (
durationInMilliSeconds: number,
locale: string,
): string => {
const minutes = Math.floor(durationInMilliSeconds / 1000 / 60);
const seconds = (durationInMilliSeconds / 1000) % 60;
const unit = startsWith(locale, 'de') ? 'Minuten' : 'minutes';

return `${minutes}:${seconds
.toString()
.padStart(2, '0')
.substring(0, 2);
return `${minutes}:${seconds} min`;
}
.substring(0, 2)} ${unit}`;
};

export const getTextDescriptionForCallevent = (
const getGermanTextDescriptionForCallEvent = (
callEvent: CallEvent,
locale: string,
): string => {
const date = new Date(callEvent.startTime);
const duration = callEvent.endTime
? formatDuration(callEvent.endTime - callEvent.startTime)
: 0;

const duration = formatDuration(
callEvent.endTime ? callEvent.endTime - callEvent.startTime : 0,
locale,
);

const directionInfo =
callEvent.direction === CallDirection.IN ? 'eingehender' : 'ausgehender';

const { from, to } = getCallMembers(callEvent);
const fromDescription = from ? ` von ${from}` : '';
const toDescription = to ? ` auf ${to}` : '';
const callDescription = `${fromDescription}${toDescription}`;
const fromDescription = from ? `von ${from}` : '';
const toDescription = to ? `auf ${to}` : '';

const callDescription = `${fromDescription}${
fromDescription && toDescription ? ' ' : ''
}${toDescription}`;

const callState =
callEvent.state === 'MISSED' ? 'Nicht angenommener ' : 'Angenommener';
callEvent.state === CallState.CONNECTED
? 'Angenommener'
: 'Nicht angenommener';
const durationInfo =
callEvent.state === 'MISSED' ? '' : ` ,Dauer: ${duration}`;
const result = `${callState} ${directionInfo} Anruf ${callDescription} am ${date.toLocaleString(
'de',
{ timeZone: 'Europe/Berlin' },
)} ${durationInfo}`;
return result;
callEvent.state === CallState.CONNECTED ? `, Dauer: ${duration}` : '';
const callDate = date.toLocaleString('de', { timeZone: 'Europe/Berlin' });
const description = `${callState} ${directionInfo} Anruf ${callDescription} am ${callDate} Uhr${durationInfo}.`;

return description;
};

const getEnglishTextDescriptionForCallEvent = (
callEvent: CallEvent,
locale: string,
): string => {
const date = new Date(callEvent.startTime);
const duration = formatDuration(
callEvent.endTime ? callEvent.endTime - callEvent.startTime : 0,
locale,
);

const directionInfo =
callEvent.direction === CallDirection.IN ? 'incoming' : 'outgoing';

const { from, to } = getCallMembers(callEvent);
const fromDescription = from ? `from ${from}` : '';
const toDescription = to ? `to ${to}` : '';

const callDescription = `${fromDescription}${
fromDescription && toDescription ? ' ' : ''
}${toDescription}`;

const callState =
callEvent.state === CallState.CONNECTED ? 'Answered' : 'Unanswered';
const durationInfo =
callEvent.state === CallState.CONNECTED ? `, duration: ${duration}` : '';
const callDate = date.toLocaleString('en', { timeZone: 'Europe/Berlin' });
const description = `${callState} ${directionInfo} call ${callDescription} on ${callDate}${durationInfo}.`;

return description;
};

export const getTextDescriptionForCallevent = (
callEvent: CallEvent,
locale: string = 'de-DE',
): string => {
return startsWith(locale, 'de')
? getGermanTextDescriptionForCallEvent(callEvent, locale)
: getEnglishTextDescriptionForCallEvent(callEvent, locale);
};
Loading