diff --git a/package-lock.json b/package-lock.json index 48c60da7..df7052c4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sipgate/integration-bridge", - "version": "1.0.9", + "version": "1.0.10", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sipgate/integration-bridge", - "version": "1.0.9", + "version": "1.0.10", "license": "UNLICENSED", "dependencies": { "@google-cloud/pubsub": "^3.7.1", diff --git a/package.json b/package.json index 304ca63f..9f6fd021 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/models/integration-error.model.ts b/src/models/integration-error.model.ts index 2de24c07..deca0294 100644 --- a/src/models/integration-error.model.ts +++ b/src/models/integration-error.model.ts @@ -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; diff --git a/src/util/callEventHelper.test.ts b/src/util/callEventHelper.test.ts new file mode 100644 index 00000000..7498d623 --- /dev/null +++ b/src/util/callEventHelper.test.ts @@ -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.', + ); + }); + }); +}); diff --git a/src/util/callEventHelper.ts b/src/util/callEventHelper.ts index cc11c760..962aff0f 100644 --- a/src/util/callEventHelper.ts +++ b/src/util/callEventHelper.ts @@ -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; @@ -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); };