Skip to content

Commit d558ad3

Browse files
committed
Merge branch 'main' of github.com:sipgate/integration-bridge-framework
2 parents 56fc608 + 144d609 commit d558ad3

File tree

9 files changed

+2584
-1100
lines changed

9 files changed

+2584
-1100
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,4 @@ Thumbs.db
1616

1717
#TypeScript
1818
dist
19+
coverage

package-lock.json

Lines changed: 2186 additions & 1083 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@sipgate/integration-bridge",
3-
"version": "0.13.21",
3+
"version": "0.13.22",
44
"description": "sipgate Integration Bridge Framework",
55
"main": "dist/index.js",
66
"types": "dist/index.d.ts",
@@ -60,17 +60,17 @@
6060
"@types/compression": "1.7.2",
6161
"@types/cors": "2.8.13",
6262
"@types/express": "4.17.17",
63-
"@types/jest": "^28.1.8",
63+
"@types/jest": "^29.4.0",
6464
"@types/lru-cache": "7.10.9",
65-
"@types/node": "18.13.0",
65+
"@types/node": "18.14.6",
6666
"@types/redis": "4.0.10",
6767
"husky": "8.0.3",
68-
"jest": "^28.1.3",
68+
"jest": "^29.4.0",
6969
"lint-staged": "13.1.2",
7070
"node-mocks-http": "1.12.1",
7171
"prettier": "2.8.4",
7272
"rimraf": "3.0.2",
73-
"ts-jest": "^28.0.8",
73+
"ts-jest": "^29.0.5",
7474
"typescript": "4.9.5"
7575
},
7676
"dependencies": {

src/cache/storage-cache.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,15 +36,17 @@ export class StorageCache implements ContactCache {
3636
): Promise<Contact[] | CacheItemState> {
3737
try {
3838
this.log(`[${anonymizeKey(key)}] Trying to get Contacts from cache…`);
39-
console.time(`${anonymizeKey(key)}-get-cache-item-state`);
39+
const start = performance.now();
4040
const cacheItemState = await this.storage.get<CacheItemState>(
4141
this.getCacheItemKey(key)
4242
);
43-
console.timeEnd(`${anonymizeKey(key)}-get-cache-item-state`);
4443

45-
console.time(`${anonymizeKey(key)}-get-cache-contacts`);
4644
const value = await this.storage.get<Contact[]>(key);
47-
console.timeEnd(`${anonymizeKey(key)}-get-cache-contacts`);
45+
console.log(
46+
`[${anonymizeKey(key)}] loading contacts took ${
47+
performance.now() - start
48+
}ms`
49+
);
4850

4951
if (
5052
cacheItemState &&

src/cache/storage/redis-storage-adapter.ts

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import {
66
InputType,
77
} from "zlib";
88
import { StorageAdapter } from "../../models/storage-adapter.model";
9-
import { anonymizeKey } from "../../util";
109

1110
const inflate = promisify<InputType, Buffer>(nodeInflate);
1211
const deflate = promisify<InputType, Buffer>(nodeDeflate);
@@ -49,14 +48,8 @@ export class RedisStorageAdapter implements StorageAdapter {
4948
if (!value) {
5049
return null;
5150
}
52-
console.time(`${anonymizeKey(key)}-inflate-buffer-cache`);
5351
const decompressed = await inflate(Buffer.from(value, "base64"));
54-
console.timeEnd(`${anonymizeKey(key)}-inflate-buffer-cache`);
55-
56-
console.time(`${anonymizeKey(key)}-json-parse-cache`);
5752
const result = JSON.parse(decompressed.toString());
58-
console.timeEnd(`${anonymizeKey(key)}-json-parse-cache`);
59-
6053
return result;
6154
} catch {
6255
return null;

src/models/controller.model.test.ts

Lines changed: 258 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,14 @@ import {
55
MockRequest,
66
MockResponse,
77
} from "node-mocks-http";
8-
import { CalendarEvent, Contact, Controller } from ".";
8+
import {
9+
CalendarEvent,
10+
CallDirection,
11+
CallParticipantType,
12+
CallState,
13+
Contact,
14+
Controller,
15+
} from ".";
916
import { StorageCache } from "../cache";
1017
import { MemoryStorageAdapter } from "../cache/storage";
1118
import { APIContact } from "./api-contact.model";
@@ -595,3 +602,253 @@ describe("getHealth", () => {
595602
expect(next).toBeCalled();
596603
});
597604
});
605+
606+
describe("handleCallEvent", () => {
607+
let request: MockRequest<BridgeRequest>;
608+
let response: MockResponse<Response>;
609+
let next: jest.Mock;
610+
611+
beforeEach(() => {
612+
response = createResponse();
613+
next = jest.fn();
614+
});
615+
616+
it("should ignore a call event with a remote direct dial", async () => {
617+
request = createRequest({
618+
providerConfig: {
619+
apiKey: "a1b2c3",
620+
apiUrl: "http://example.com",
621+
locale: "de_DE",
622+
},
623+
body: {
624+
participants: [
625+
{
626+
type: CallParticipantType.LOCAL,
627+
phoneNumber: "1234567890",
628+
},
629+
{
630+
type: CallParticipantType.REMOTE,
631+
phoneNumber: "13",
632+
},
633+
],
634+
id: "",
635+
startTime: 0,
636+
endTime: 0,
637+
direction: CallDirection.IN,
638+
note: "",
639+
state: CallState.BUSY,
640+
},
641+
});
642+
const controller: Controller = new Controller(
643+
{
644+
handleCallEvent: (config, event) => Promise.resolve(""),
645+
},
646+
new StorageCache(new MemoryStorageAdapter())
647+
);
648+
649+
await controller.handleCallEvent(request, response, next);
650+
651+
const data: string = response._getData();
652+
653+
expect(next).not.toBeCalled();
654+
expect(data).toEqual("Skipping call event");
655+
});
656+
657+
it("should handle a call event", async () => {
658+
request = createRequest({
659+
providerConfig: {
660+
apiKey: "a1b2c3",
661+
apiUrl: "http://example.com",
662+
locale: "de_DE",
663+
},
664+
body: {
665+
participants: [
666+
{
667+
type: CallParticipantType.LOCAL,
668+
phoneNumber: "1234567890",
669+
},
670+
{
671+
type: CallParticipantType.REMOTE,
672+
phoneNumber: "0123456789",
673+
},
674+
],
675+
id: "",
676+
startTime: 0,
677+
endTime: 0,
678+
direction: CallDirection.IN,
679+
note: "",
680+
state: CallState.BUSY,
681+
},
682+
});
683+
const controller: Controller = new Controller(
684+
{
685+
handleCallEvent: (config, event) => Promise.resolve("callRef"),
686+
},
687+
new StorageCache(new MemoryStorageAdapter())
688+
);
689+
690+
await controller.handleCallEvent(request, response, next);
691+
692+
const data: string = response._getData();
693+
694+
expect(next).not.toBeCalled();
695+
expect(data).toEqual("callRef");
696+
});
697+
698+
it("should handle adapter not implementing the feature", async () => {
699+
request = createRequest({
700+
providerConfig: {
701+
apiKey: "a1b2c3",
702+
apiUrl: "http://example.com",
703+
locale: "de_DE",
704+
},
705+
});
706+
const controller: Controller = new Controller(
707+
{},
708+
new StorageCache(new MemoryStorageAdapter())
709+
);
710+
711+
await controller.handleCallEvent(request, response, next);
712+
713+
expect(next).toBeCalled();
714+
});
715+
716+
it("should handle config being missing", async () => {
717+
request = createRequest({});
718+
const controller: Controller = new Controller(
719+
{
720+
handleCallEvent: (config, event) => Promise.resolve("callRef"),
721+
},
722+
new StorageCache(new MemoryStorageAdapter())
723+
);
724+
725+
await controller.handleCallEvent(request, response, next);
726+
727+
expect(next).toBeCalled();
728+
});
729+
});
730+
731+
describe("updateCallEvent", () => {
732+
let request: MockRequest<BridgeRequest>;
733+
let response: MockResponse<Response>;
734+
let next: jest.Mock;
735+
736+
beforeEach(() => {
737+
response = createResponse();
738+
next = jest.fn();
739+
});
740+
741+
it("should ignore a call event with a remote direct dial", async () => {
742+
request = createRequest({
743+
providerConfig: {
744+
apiKey: "a1b2c3",
745+
apiUrl: "http://example.com",
746+
locale: "de_DE",
747+
},
748+
body: {
749+
participants: [
750+
{
751+
type: CallParticipantType.LOCAL,
752+
phoneNumber: "1234567890",
753+
},
754+
{
755+
type: CallParticipantType.REMOTE,
756+
phoneNumber: "13",
757+
},
758+
],
759+
id: "",
760+
startTime: 0,
761+
endTime: 0,
762+
direction: CallDirection.IN,
763+
note: "",
764+
state: CallState.BUSY,
765+
},
766+
});
767+
const controller: Controller = new Controller(
768+
{
769+
updateCallEvent: (config, id, event) => Promise.resolve(),
770+
},
771+
new StorageCache(new MemoryStorageAdapter())
772+
);
773+
774+
await controller.updateCallEvent(request, response, next);
775+
776+
const data: string = response._getData();
777+
778+
expect(next).not.toBeCalled();
779+
expect(data).toEqual("Skipping call event");
780+
});
781+
782+
it("should handle a call event", async () => {
783+
request = createRequest({
784+
providerConfig: {
785+
apiKey: "a1b2c3",
786+
apiUrl: "http://example.com",
787+
locale: "de_DE",
788+
},
789+
body: {
790+
participants: [
791+
{
792+
type: CallParticipantType.LOCAL,
793+
phoneNumber: "1234567890",
794+
},
795+
{
796+
type: CallParticipantType.REMOTE,
797+
phoneNumber: "0123456789",
798+
},
799+
],
800+
id: "",
801+
startTime: 0,
802+
endTime: 0,
803+
direction: CallDirection.IN,
804+
note: "",
805+
state: CallState.BUSY,
806+
},
807+
});
808+
const controller: Controller = new Controller(
809+
{
810+
updateCallEvent: (config, id, event) => Promise.resolve(),
811+
},
812+
new StorageCache(new MemoryStorageAdapter())
813+
);
814+
815+
await controller.updateCallEvent(request, response, next);
816+
817+
const data: string = response._getData();
818+
819+
expect(next).not.toBeCalled();
820+
expect(data).toEqual("");
821+
});
822+
823+
it("should handle adapter not implementing the feature", async () => {
824+
request = createRequest({
825+
providerConfig: {
826+
apiKey: "a1b2c3",
827+
apiUrl: "http://example.com",
828+
locale: "de_DE",
829+
},
830+
});
831+
const controller: Controller = new Controller(
832+
{},
833+
new StorageCache(new MemoryStorageAdapter())
834+
);
835+
836+
await controller.updateCallEvent(request, response, next);
837+
838+
expect(next).toBeCalled();
839+
});
840+
841+
it("should handle config being missing", async () => {
842+
request = createRequest({});
843+
const controller: Controller = new Controller(
844+
{
845+
updateCallEvent: (config, id, event) => Promise.resolve(),
846+
},
847+
new StorageCache(new MemoryStorageAdapter())
848+
);
849+
850+
await controller.updateCallEvent(request, response, next);
851+
852+
expect(next).toBeCalled();
853+
});
854+
});

src/models/controller.model.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
} from ".";
1616
import { calendarEventsSchema, contactsSchema } from "../schemas";
1717
import { anonymizeKey } from "../util/anonymize-key";
18+
import { shouldSkipCallEvent } from "../util/call-event.util";
1819
import { parsePhoneNumber } from "../util/phone-number-utils";
1920
import { validate } from "../util/validate";
2021
import { APIContact } from "./api-contact.model";
@@ -459,6 +460,16 @@ export class Controller {
459460
throw new ServerError(400, "Missing config parameters");
460461
}
461462

463+
if (shouldSkipCallEvent(req.body as CallEvent)) {
464+
console.log(
465+
`[${anonymizeKey(apiKey)}] skipping call event for call id ${
466+
req.body.id
467+
}`
468+
);
469+
res.status(200).send("Skipping call event");
470+
return;
471+
}
472+
462473
console.log(`[${anonymizeKey(apiKey)}] Handling call event for key`);
463474

464475
const integrationCallEventRef = await this.adapter.handleCallEvent(
@@ -518,6 +529,16 @@ export class Controller {
518529
throw new ServerError(400, "Missing config parameters");
519530
}
520531

532+
if (shouldSkipCallEvent(req.body as CallEvent)) {
533+
console.log(
534+
`[${anonymizeKey(apiKey)}] skipping call event for call id ${
535+
req.body.id
536+
}`
537+
);
538+
res.status(200).send("Skipping call event");
539+
return;
540+
}
541+
521542
console.log(`[${anonymizeKey(apiKey)}] Updating call event`);
522543

523544
//maybe return updated state obj

0 commit comments

Comments
 (0)