@talkpilot/core-db 1.3.51 → 1.3.52
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -2
- package/dist/talkpilot/auditLogs/auditLogs.getters.d.ts +8 -0
- package/dist/talkpilot/auditLogs/auditLogs.getters.d.ts.map +1 -0
- package/dist/talkpilot/auditLogs/auditLogs.getters.js +26 -0
- package/dist/talkpilot/auditLogs/auditLogs.getters.js.map +1 -0
- package/dist/talkpilot/auditLogs/auditLogs.setters.d.ts +3 -0
- package/dist/talkpilot/auditLogs/auditLogs.setters.d.ts.map +1 -0
- package/dist/talkpilot/auditLogs/auditLogs.setters.js +9 -0
- package/dist/talkpilot/auditLogs/auditLogs.setters.js.map +1 -0
- package/dist/talkpilot/auditLogs/auditLogs.types.d.ts +12 -0
- package/dist/talkpilot/auditLogs/auditLogs.types.d.ts.map +1 -0
- package/dist/talkpilot/auditLogs/auditLogs.types.js +3 -0
- package/dist/talkpilot/auditLogs/auditLogs.types.js.map +1 -0
- package/dist/talkpilot/auditLogs/index.d.ts +3 -0
- package/dist/talkpilot/auditLogs/index.d.ts.map +1 -0
- package/dist/talkpilot/auditLogs/index.js +19 -0
- package/dist/talkpilot/auditLogs/index.js.map +1 -0
- package/dist/talkpilot/calls/calls.constants.d.ts +1 -1
- package/dist/talkpilot/clientDeletionLogs/clientDeletionLogs.getters.d.ts +4 -0
- package/dist/talkpilot/clientDeletionLogs/clientDeletionLogs.getters.d.ts.map +1 -0
- package/dist/talkpilot/clientDeletionLogs/clientDeletionLogs.getters.js +9 -0
- package/dist/talkpilot/clientDeletionLogs/clientDeletionLogs.getters.js.map +1 -0
- package/dist/talkpilot/clientDeletionLogs/clientDeletionLogs.setters.d.ts +3 -0
- package/dist/talkpilot/clientDeletionLogs/clientDeletionLogs.setters.d.ts.map +1 -0
- package/dist/talkpilot/clientDeletionLogs/clientDeletionLogs.setters.js +9 -0
- package/dist/talkpilot/clientDeletionLogs/clientDeletionLogs.setters.js.map +1 -0
- package/dist/talkpilot/clientDeletionLogs/clientDeletionLogs.types.d.ts +10 -0
- package/dist/talkpilot/clientDeletionLogs/clientDeletionLogs.types.d.ts.map +1 -0
- package/dist/talkpilot/clientDeletionLogs/clientDeletionLogs.types.js +3 -0
- package/dist/talkpilot/clientDeletionLogs/clientDeletionLogs.types.js.map +1 -0
- package/dist/talkpilot/clientDeletionLogs/index.d.ts +4 -0
- package/dist/talkpilot/clientDeletionLogs/index.d.ts.map +1 -0
- package/dist/talkpilot/clientDeletionLogs/index.js +20 -0
- package/dist/talkpilot/clientDeletionLogs/index.js.map +1 -0
- package/dist/talkpilot/clients/clients.constants.d.ts +3 -0
- package/dist/talkpilot/clients/clients.constants.d.ts.map +1 -0
- package/dist/talkpilot/clients/clients.constants.js +6 -0
- package/dist/talkpilot/clients/clients.constants.js.map +1 -0
- package/dist/talkpilot/clients/clients.getters.d.ts +1 -0
- package/dist/talkpilot/clients/clients.getters.d.ts.map +1 -1
- package/dist/talkpilot/clients/clients.getters.js +8 -1
- package/dist/talkpilot/clients/clients.getters.js.map +1 -1
- package/dist/talkpilot/clients/clients.lifecycle.d.ts +10 -0
- package/dist/talkpilot/clients/clients.lifecycle.d.ts.map +1 -0
- package/dist/talkpilot/clients/clients.lifecycle.js +95 -0
- package/dist/talkpilot/clients/clients.lifecycle.js.map +1 -0
- package/dist/talkpilot/clients/clients.types.d.ts +21 -0
- package/dist/talkpilot/clients/clients.types.d.ts.map +1 -1
- package/dist/talkpilot/clients/index.d.ts +2 -0
- package/dist/talkpilot/clients/index.d.ts.map +1 -1
- package/dist/talkpilot/clients/index.js +2 -0
- package/dist/talkpilot/clients/index.js.map +1 -1
- package/dist/talkpilot/flows/flows.getter.d.ts +1 -4
- package/dist/talkpilot/flows/flows.getter.d.ts.map +1 -1
- package/dist/talkpilot/flows/flows.getter.js +1 -12
- package/dist/talkpilot/flows/flows.getter.js.map +1 -1
- package/dist/talkpilot/flows/flows.schema.d.ts +0 -31
- package/dist/talkpilot/flows/flows.schema.d.ts.map +1 -1
- package/dist/talkpilot/flows/flows.schema.js +0 -25
- package/dist/talkpilot/flows/flows.schema.js.map +1 -1
- package/dist/talkpilot/flows/flows.types.d.ts +0 -14
- package/dist/talkpilot/flows/flows.types.d.ts.map +1 -1
- package/dist/talkpilot/flows/index.d.ts +1 -1
- package/dist/talkpilot/flows/index.d.ts.map +1 -1
- package/dist/talkpilot/flows/index.js +1 -3
- package/dist/talkpilot/flows/index.js.map +1 -1
- package/dist/talkpilot/index.d.ts +1 -0
- package/dist/talkpilot/index.d.ts.map +1 -1
- package/dist/talkpilot/index.js +1 -0
- package/dist/talkpilot/index.js.map +1 -1
- package/dist/talkpilot/phone_numbers/phone_numbers.getter.d.ts +11 -0
- package/dist/talkpilot/phone_numbers/phone_numbers.getter.d.ts.map +1 -1
- package/dist/talkpilot/phone_numbers/phone_numbers.getter.js +60 -1
- package/dist/talkpilot/phone_numbers/phone_numbers.getter.js.map +1 -1
- package/dist/talkpilot/results/index.d.ts +3 -0
- package/dist/talkpilot/results/index.d.ts.map +1 -1
- package/dist/talkpilot/results/index.js +22 -1
- package/dist/talkpilot/results/index.js.map +1 -1
- package/dist/talkpilot/results/results.constants.d.ts +9 -0
- package/dist/talkpilot/results/results.constants.d.ts.map +1 -0
- package/dist/talkpilot/results/results.constants.js +12 -0
- package/dist/talkpilot/results/results.constants.js.map +1 -0
- package/dist/talkpilot/results/results.getter.d.ts.map +1 -1
- package/dist/talkpilot/results/results.getter.js +3 -2
- package/dist/talkpilot/results/results.getter.js.map +1 -1
- package/dist/talkpilot/results/results.statistics.getters.d.ts +7 -0
- package/dist/talkpilot/results/results.statistics.getters.d.ts.map +1 -0
- package/dist/talkpilot/results/results.statistics.getters.js +244 -0
- package/dist/talkpilot/results/results.statistics.getters.js.map +1 -0
- package/dist/talkpilot/results/results.statistics.types.d.ts +42 -0
- package/dist/talkpilot/results/results.statistics.types.d.ts.map +1 -0
- package/dist/talkpilot/results/results.statistics.types.js +3 -0
- package/dist/talkpilot/results/results.statistics.types.js.map +1 -0
- package/dist/test-utils/factories/index.d.ts +2 -0
- package/dist/test-utils/factories/index.d.ts.map +1 -1
- package/dist/test-utils/factories/index.js +2 -0
- package/dist/test-utils/factories/index.js.map +1 -1
- package/dist/test-utils/factories/talkpilot/clients.d.ts +14 -0
- package/dist/test-utils/factories/talkpilot/clients.d.ts.map +1 -0
- package/dist/test-utils/factories/talkpilot/clients.js +19 -0
- package/dist/test-utils/factories/talkpilot/clients.js.map +1 -0
- package/dist/test-utils/factories/talkpilot/results.d.ts +16 -0
- package/dist/test-utils/factories/talkpilot/results.d.ts.map +1 -0
- package/dist/test-utils/factories/talkpilot/results.js +40 -0
- package/dist/test-utils/factories/talkpilot/results.js.map +1 -0
- package/dist/websitalk/scanResources/scanResources.getters.d.ts +0 -1
- package/dist/websitalk/scanResources/scanResources.getters.d.ts.map +1 -1
- package/dist/websitalk/scanResources/scanResources.getters.js +1 -6
- package/dist/websitalk/scanResources/scanResources.getters.js.map +1 -1
- package/dist/websitalk/scans/scans.constants.d.ts +0 -1
- package/dist/websitalk/scans/scans.constants.d.ts.map +1 -1
- package/dist/websitalk/scans/scans.constants.js +0 -1
- package/dist/websitalk/scans/scans.constants.js.map +1 -1
- package/dist/websitalk/scans/scans.getters.d.ts +0 -1
- package/dist/websitalk/scans/scans.getters.d.ts.map +1 -1
- package/dist/websitalk/scans/scans.getters.js +1 -6
- package/dist/websitalk/scans/scans.getters.js.map +1 -1
- package/package.json +1 -1
- package/src/talkpilot/auditLogs/__tests__/auditLogs.spec.ts +92 -0
- package/src/talkpilot/auditLogs/auditLogs.getters.ts +26 -0
- package/src/talkpilot/auditLogs/auditLogs.types.ts +13 -0
- package/src/talkpilot/auditLogs/index.ts +2 -0
- package/src/talkpilot/clients/__tests__/clients.getters.spec.ts +33 -0
- package/src/talkpilot/clients/__tests__/clients.lifecycle.spec.ts +166 -0
- package/src/talkpilot/clients/clients.constants.ts +2 -0
- package/src/talkpilot/clients/clients.getters.ts +7 -0
- package/src/talkpilot/clients/clients.lifecycle.ts +116 -0
- package/src/talkpilot/clients/clients.types.ts +19 -0
- package/src/talkpilot/clients/index.ts +2 -0
- package/src/talkpilot/flows/__tests__/flows.schema.spec.ts +0 -25
- package/src/talkpilot/flows/flows.getter.ts +2 -22
- package/src/talkpilot/flows/flows.schema.ts +0 -25
- package/src/talkpilot/flows/flows.types.ts +0 -22
- package/src/talkpilot/flows/index.ts +1 -6
- package/src/talkpilot/index.ts +1 -0
- package/src/talkpilot/phone_numbers/__tests__/phone_numbers.spec.ts +227 -13
- package/src/talkpilot/phone_numbers/phone_numbers.getter.ts +76 -1
- package/src/talkpilot/results/__tests__/results.statistics.spec.ts +226 -0
- package/src/talkpilot/results/index.ts +9 -0
- package/src/talkpilot/results/results.constants.ts +11 -0
- package/src/talkpilot/results/results.getter.ts +3 -2
- package/src/talkpilot/results/results.statistics.getters.ts +329 -0
- package/src/talkpilot/results/results.statistics.types.ts +45 -0
- package/src/test-utils/factories/index.ts +2 -0
- package/src/test-utils/factories/talkpilot/clients.ts +21 -0
- package/src/test-utils/factories/talkpilot/results.ts +43 -0
- package/src/websitalk/scanResources/__tests__/scanResources.spec.ts +0 -19
- package/src/websitalk/scanResources/scanResources.getters.ts +0 -7
- package/src/websitalk/scans/__tests__/scans.spec.ts +0 -17
- package/src/websitalk/scans/scans.constants.ts +0 -1
- package/src/websitalk/scans/scans.getters.ts +0 -5
|
@@ -7,6 +7,11 @@ import {
|
|
|
7
7
|
createPhoneNumberEntity,
|
|
8
8
|
createPurchasedPhoneNumber,
|
|
9
9
|
getPrimaryPhoneFlowId,
|
|
10
|
+
getClientPhones,
|
|
11
|
+
getAllClientPhoneMappings,
|
|
12
|
+
getDistinctClientPhonePairs,
|
|
13
|
+
findClientsByPhoneNumbers,
|
|
14
|
+
deletePhoneNumberRecords,
|
|
10
15
|
} from "../phone_numbers.getter";
|
|
11
16
|
import { getFlowsCollection } from "../../flows/flows.getter";
|
|
12
17
|
import { createFlow, createPhoneNumber } from "../../../test-utils/factories";
|
|
@@ -14,7 +19,7 @@ import { ObjectId } from "mongodb";
|
|
|
14
19
|
|
|
15
20
|
describe("db.phoneNumbers", () => {
|
|
16
21
|
describe("getPhoneDataByPhoneNumber", () => {
|
|
17
|
-
it("return phone number data with flow", async () => {
|
|
22
|
+
it("should return phone number data with flow", async () => {
|
|
18
23
|
const flow = createFlow({
|
|
19
24
|
clientId: "test-client-id",
|
|
20
25
|
conversationSettings: {
|
|
@@ -62,7 +67,7 @@ describe("db.phoneNumbers", () => {
|
|
|
62
67
|
});
|
|
63
68
|
|
|
64
69
|
describe("getClientPhoneNumber", () => {
|
|
65
|
-
it("return phone number by client id", async () => {
|
|
70
|
+
it("should return phone number by client id", async () => {
|
|
66
71
|
const flow = createFlow();
|
|
67
72
|
const phoneData = createPhoneNumber({
|
|
68
73
|
flow_id: flow._id,
|
|
@@ -78,7 +83,7 @@ describe("db.phoneNumbers", () => {
|
|
|
78
83
|
});
|
|
79
84
|
|
|
80
85
|
describe("getClientPhoneData", () => {
|
|
81
|
-
it("
|
|
86
|
+
it("should return primary phone data by default", async () => {
|
|
82
87
|
const clientId = "multiPhoneClient";
|
|
83
88
|
const phone1 = createPhoneNumber({
|
|
84
89
|
client_id: clientId,
|
|
@@ -100,7 +105,7 @@ describe("db.phoneNumbers", () => {
|
|
|
100
105
|
expect(result?.phone_number).toBe("2");
|
|
101
106
|
});
|
|
102
107
|
|
|
103
|
-
it("
|
|
108
|
+
it("should return most recent non-primary phone data if isPrimary is false", async () => {
|
|
104
109
|
const clientId = "multiPhoneClient2";
|
|
105
110
|
const phone1 = createPhoneNumber({
|
|
106
111
|
client_id: clientId,
|
|
@@ -131,7 +136,7 @@ describe("db.phoneNumbers", () => {
|
|
|
131
136
|
});
|
|
132
137
|
|
|
133
138
|
describe("getPhoneNumbersForFlows", () => {
|
|
134
|
-
it("
|
|
139
|
+
it("should return all phone numbers for the client with flow ObjectIds, newest first", async () => {
|
|
135
140
|
const clientId = "flowsClient";
|
|
136
141
|
const flow1 = createFlow({ clientId });
|
|
137
142
|
const flow2 = createFlow({ clientId });
|
|
@@ -169,12 +174,12 @@ describe("db.phoneNumbers", () => {
|
|
|
169
174
|
});
|
|
170
175
|
});
|
|
171
176
|
|
|
172
|
-
it("
|
|
177
|
+
it("should return an empty array when the client has no phone numbers", async () => {
|
|
173
178
|
const result = await getPhoneNumbersForFlows("noSuchClient");
|
|
174
179
|
expect(result).toEqual([]);
|
|
175
180
|
});
|
|
176
181
|
|
|
177
|
-
it("
|
|
182
|
+
it("should not include phone numbers for other clients", async () => {
|
|
178
183
|
const clientA = "clientA";
|
|
179
184
|
const clientB = "clientB";
|
|
180
185
|
const flow = createFlow();
|
|
@@ -191,7 +196,7 @@ describe("db.phoneNumbers", () => {
|
|
|
191
196
|
});
|
|
192
197
|
|
|
193
198
|
describe("createPhoneNumberEntity", () => {
|
|
194
|
-
it("
|
|
199
|
+
it("should create first phone number as primary", async () => {
|
|
195
200
|
const clientId = "newClient";
|
|
196
201
|
const flowId = new ObjectId().toHexString();
|
|
197
202
|
const phoneNumber = "+123456789";
|
|
@@ -205,7 +210,7 @@ describe("db.phoneNumbers", () => {
|
|
|
205
210
|
expect(result.is_primary).toBe(true);
|
|
206
211
|
});
|
|
207
212
|
|
|
208
|
-
it("
|
|
213
|
+
it("should create subsequent phone number as non-primary", async () => {
|
|
209
214
|
const clientId = "existingClient";
|
|
210
215
|
const flowId = new ObjectId().toHexString();
|
|
211
216
|
|
|
@@ -220,7 +225,7 @@ describe("db.phoneNumbers", () => {
|
|
|
220
225
|
});
|
|
221
226
|
|
|
222
227
|
describe("createPurchasedPhoneNumber", () => {
|
|
223
|
-
it("
|
|
228
|
+
it("should persist a phone_numbers document with provider payload and flow", async () => {
|
|
224
229
|
const clientId = "purchasedClientDb";
|
|
225
230
|
const flowId = new ObjectId().toHexString();
|
|
226
231
|
const phoneNumber = "+15551234567";
|
|
@@ -252,7 +257,7 @@ describe("db.phoneNumbers", () => {
|
|
|
252
257
|
});
|
|
253
258
|
|
|
254
259
|
describe("getPrimaryPhoneFlowId", () => {
|
|
255
|
-
it("
|
|
260
|
+
it("should return flow_id from primary phone as string", async () => {
|
|
256
261
|
const clientId = "primaryFlowClient";
|
|
257
262
|
const flowId = new ObjectId();
|
|
258
263
|
await getPhoneNumbersCollection().insertOne(
|
|
@@ -266,11 +271,11 @@ describe("db.phoneNumbers", () => {
|
|
|
266
271
|
expect(await getPrimaryPhoneFlowId(clientId)).toBe(flowId.toHexString());
|
|
267
272
|
});
|
|
268
273
|
|
|
269
|
-
it("
|
|
274
|
+
it("should return null when client has no primary phone", async () => {
|
|
270
275
|
expect(await getPrimaryPhoneFlowId("no-phone-client")).toBeNull();
|
|
271
276
|
});
|
|
272
277
|
|
|
273
|
-
it("
|
|
278
|
+
it("should return null when primary phone has no flow_id", async () => {
|
|
274
279
|
const clientId = "phoneNoFlow";
|
|
275
280
|
const phone = createPhoneNumber({ client_id: clientId, is_primary: true });
|
|
276
281
|
delete phone.flow_id;
|
|
@@ -279,4 +284,213 @@ describe("db.phoneNumbers", () => {
|
|
|
279
284
|
expect(await getPrimaryPhoneFlowId(clientId)).toBeNull();
|
|
280
285
|
});
|
|
281
286
|
});
|
|
287
|
+
|
|
288
|
+
describe("getClientPhones", () => {
|
|
289
|
+
it("should return all phone numbers for the given client, projected to phoneNumber only", async () => {
|
|
290
|
+
const clientId = "phonesClientA";
|
|
291
|
+
await getPhoneNumbersCollection().insertMany([
|
|
292
|
+
createPhoneNumber({ client_id: clientId, phone_number: "+100" }),
|
|
293
|
+
createPhoneNumber({ client_id: clientId, phone_number: "+200" }),
|
|
294
|
+
]);
|
|
295
|
+
|
|
296
|
+
const result = await getClientPhones(clientId);
|
|
297
|
+
|
|
298
|
+
expect(result).toHaveLength(2);
|
|
299
|
+
expect(result).toEqual(
|
|
300
|
+
expect.arrayContaining([
|
|
301
|
+
{ phoneNumber: "+100" },
|
|
302
|
+
{ phoneNumber: "+200" },
|
|
303
|
+
]),
|
|
304
|
+
);
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
it("should not include phone numbers belonging to other clients", async () => {
|
|
308
|
+
await getPhoneNumbersCollection().insertOne(
|
|
309
|
+
createPhoneNumber({ client_id: "someOtherClient" }),
|
|
310
|
+
);
|
|
311
|
+
|
|
312
|
+
const result = await getClientPhones("phonesClientB");
|
|
313
|
+
expect(result).toEqual([]);
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
it("should remove exact duplicate phone records for the same client", async () => {
|
|
317
|
+
const clientId = "phonesClientDup";
|
|
318
|
+
await getPhoneNumbersCollection().insertMany([
|
|
319
|
+
createPhoneNumber({ client_id: clientId, phone_number: "+300" }),
|
|
320
|
+
createPhoneNumber({ client_id: clientId, phone_number: "+300" }),
|
|
321
|
+
createPhoneNumber({ client_id: clientId, phone_number: "+400" }),
|
|
322
|
+
]);
|
|
323
|
+
|
|
324
|
+
const result = await getClientPhones(clientId);
|
|
325
|
+
|
|
326
|
+
expect(result).toHaveLength(2);
|
|
327
|
+
expect(result).toEqual(
|
|
328
|
+
expect.arrayContaining([
|
|
329
|
+
{ phoneNumber: "+300" },
|
|
330
|
+
{ phoneNumber: "+400" },
|
|
331
|
+
]),
|
|
332
|
+
);
|
|
333
|
+
});
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
describe("getAllClientPhoneMappings", () => {
|
|
337
|
+
it("should return a Map of clientId to phone number across all clients", async () => {
|
|
338
|
+
await getPhoneNumbersCollection().insertMany([
|
|
339
|
+
createPhoneNumber({ client_id: "mapClientA", phone_number: "+111" }),
|
|
340
|
+
createPhoneNumber({ client_id: "mapClientB", phone_number: "+222" }),
|
|
341
|
+
]);
|
|
342
|
+
|
|
343
|
+
const result = await getAllClientPhoneMappings();
|
|
344
|
+
|
|
345
|
+
expect(result).toBeInstanceOf(Map);
|
|
346
|
+
expect(result.get("mapClientA")).toBe("+111");
|
|
347
|
+
expect(result.get("mapClientB")).toBe("+222");
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
it("should return an empty Map when there are no phone numbers", async () => {
|
|
351
|
+
const result = await getAllClientPhoneMappings();
|
|
352
|
+
expect(result.size).toBe(0);
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
it("should return the most recently updated phone number when a client has several", async () => {
|
|
356
|
+
const clientId = "mapClientRecent";
|
|
357
|
+
await getPhoneNumbersCollection().insertMany([
|
|
358
|
+
createPhoneNumber({
|
|
359
|
+
client_id: clientId,
|
|
360
|
+
phone_number: "+old",
|
|
361
|
+
updatedAt: new Date("2023-01-01"),
|
|
362
|
+
}),
|
|
363
|
+
createPhoneNumber({
|
|
364
|
+
client_id: clientId,
|
|
365
|
+
phone_number: "+new",
|
|
366
|
+
updatedAt: new Date("2023-06-01"),
|
|
367
|
+
}),
|
|
368
|
+
]);
|
|
369
|
+
|
|
370
|
+
const result = await getAllClientPhoneMappings();
|
|
371
|
+
|
|
372
|
+
expect(result.get(clientId)).toBe("+new");
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
it("should prefer the is_primary number over a more recently updated non-primary one", async () => {
|
|
376
|
+
const clientId = "mapClientPrimary";
|
|
377
|
+
await getPhoneNumbersCollection().insertMany([
|
|
378
|
+
createPhoneNumber({
|
|
379
|
+
client_id: clientId,
|
|
380
|
+
phone_number: "+primary",
|
|
381
|
+
is_primary: true,
|
|
382
|
+
updatedAt: new Date("2023-01-01"),
|
|
383
|
+
}),
|
|
384
|
+
createPhoneNumber({
|
|
385
|
+
client_id: clientId,
|
|
386
|
+
phone_number: "+secondary",
|
|
387
|
+
is_primary: false,
|
|
388
|
+
updatedAt: new Date("2023-06-01"),
|
|
389
|
+
}),
|
|
390
|
+
]);
|
|
391
|
+
|
|
392
|
+
const result = await getAllClientPhoneMappings();
|
|
393
|
+
|
|
394
|
+
expect(result.get(clientId)).toBe("+primary");
|
|
395
|
+
});
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
describe("getDistinctClientPhonePairs", () => {
|
|
399
|
+
it("should return unique client+phone pairs, collapsing exact duplicates", async () => {
|
|
400
|
+
await getPhoneNumbersCollection().insertMany([
|
|
401
|
+
createPhoneNumber({ client_id: "pairClientA", phone_number: "+500" }),
|
|
402
|
+
createPhoneNumber({ client_id: "pairClientA", phone_number: "+500" }),
|
|
403
|
+
createPhoneNumber({ client_id: "pairClientB", phone_number: "+600" }),
|
|
404
|
+
]);
|
|
405
|
+
|
|
406
|
+
const result = await getDistinctClientPhonePairs();
|
|
407
|
+
|
|
408
|
+
expect(result).toEqual(
|
|
409
|
+
expect.arrayContaining([
|
|
410
|
+
{ clientId: "pairClientA", phoneNumber: "+500" },
|
|
411
|
+
{ clientId: "pairClientB", phoneNumber: "+600" },
|
|
412
|
+
]),
|
|
413
|
+
);
|
|
414
|
+
expect(
|
|
415
|
+
result.filter(
|
|
416
|
+
(p) => p.clientId === "pairClientA" && p.phoneNumber === "+500",
|
|
417
|
+
),
|
|
418
|
+
).toHaveLength(1);
|
|
419
|
+
});
|
|
420
|
+
|
|
421
|
+
it("should keep separate pairs when the same number is shared by different clients", async () => {
|
|
422
|
+
await getPhoneNumbersCollection().insertMany([
|
|
423
|
+
createPhoneNumber({ client_id: "sharedClientA", phone_number: "+700" }),
|
|
424
|
+
createPhoneNumber({ client_id: "sharedClientB", phone_number: "+700" }),
|
|
425
|
+
]);
|
|
426
|
+
|
|
427
|
+
const result = await getDistinctClientPhonePairs();
|
|
428
|
+
|
|
429
|
+
expect(result).toEqual(
|
|
430
|
+
expect.arrayContaining([
|
|
431
|
+
{ clientId: "sharedClientA", phoneNumber: "+700" },
|
|
432
|
+
{ clientId: "sharedClientB", phoneNumber: "+700" },
|
|
433
|
+
]),
|
|
434
|
+
);
|
|
435
|
+
});
|
|
436
|
+
});
|
|
437
|
+
|
|
438
|
+
describe("findClientsByPhoneNumbers", () => {
|
|
439
|
+
it("should return other clients sharing each phone number, excluding the given client", async () => {
|
|
440
|
+
await getPhoneNumbersCollection().insertMany([
|
|
441
|
+
createPhoneNumber({ client_id: "excludeMe", phone_number: "+800" }),
|
|
442
|
+
createPhoneNumber({ client_id: "otherClient1", phone_number: "+800" }),
|
|
443
|
+
createPhoneNumber({ client_id: "otherClient2", phone_number: "+800" }),
|
|
444
|
+
]);
|
|
445
|
+
|
|
446
|
+
const result = await findClientsByPhoneNumbers(["+800"], "excludeMe");
|
|
447
|
+
|
|
448
|
+
expect(result.get("+800")).toEqual(
|
|
449
|
+
expect.arrayContaining(["otherClient1", "otherClient2"]),
|
|
450
|
+
);
|
|
451
|
+
expect(result.get("+800")).not.toContain("excludeMe");
|
|
452
|
+
});
|
|
453
|
+
|
|
454
|
+
it("should return an empty array for a phone number with no other clients", async () => {
|
|
455
|
+
await getPhoneNumbersCollection().insertOne(
|
|
456
|
+
createPhoneNumber({ client_id: "soleOwner", phone_number: "+900" }),
|
|
457
|
+
);
|
|
458
|
+
|
|
459
|
+
const result = await findClientsByPhoneNumbers(["+900"], "soleOwner");
|
|
460
|
+
|
|
461
|
+
expect(result.get("+900")).toEqual([]);
|
|
462
|
+
});
|
|
463
|
+
|
|
464
|
+
it("should return an empty map immediately when given no phone numbers", async () => {
|
|
465
|
+
const result = await findClientsByPhoneNumbers([]);
|
|
466
|
+
expect(result.size).toBe(0);
|
|
467
|
+
});
|
|
468
|
+
});
|
|
469
|
+
|
|
470
|
+
describe("deletePhoneNumberRecords", () => {
|
|
471
|
+
it("should delete every record for the given phone number across all clients", async () => {
|
|
472
|
+
await getPhoneNumbersCollection().insertMany([
|
|
473
|
+
createPhoneNumber({ client_id: "delClientA", phone_number: "+1000" }),
|
|
474
|
+
createPhoneNumber({ client_id: "delClientB", phone_number: "+1000" }),
|
|
475
|
+
createPhoneNumber({ client_id: "delClientC", phone_number: "+2000" }),
|
|
476
|
+
]);
|
|
477
|
+
|
|
478
|
+
const deletedCount = await deletePhoneNumberRecords("+1000");
|
|
479
|
+
|
|
480
|
+
expect(deletedCount).toBe(2);
|
|
481
|
+
const remaining = await getPhoneNumbersCollection()
|
|
482
|
+
.find({ phone_number: "+1000" })
|
|
483
|
+
.toArray();
|
|
484
|
+
expect(remaining).toHaveLength(0);
|
|
485
|
+
const untouched = await getPhoneNumbersCollection().findOne({
|
|
486
|
+
phone_number: "+2000",
|
|
487
|
+
});
|
|
488
|
+
expect(untouched?.client_id).toBe("delClientC");
|
|
489
|
+
});
|
|
490
|
+
|
|
491
|
+
it("should return 0 when no records match the phone number", async () => {
|
|
492
|
+
const deletedCount = await deletePhoneNumberRecords("+9999999");
|
|
493
|
+
expect(deletedCount).toBe(0);
|
|
494
|
+
});
|
|
495
|
+
});
|
|
282
496
|
});
|
|
@@ -157,7 +157,9 @@ export const findClientByPhoneNumber = async (
|
|
|
157
157
|
return client;
|
|
158
158
|
};
|
|
159
159
|
|
|
160
|
-
export const getPrimaryPhoneFlowId = async (
|
|
160
|
+
export const getPrimaryPhoneFlowId = async (
|
|
161
|
+
clientId: string,
|
|
162
|
+
): Promise<string | null> => {
|
|
161
163
|
const phone = await getClientPhoneData(clientId);
|
|
162
164
|
return phone?.flow_id ? String(phone.flow_id) : null;
|
|
163
165
|
};
|
|
@@ -174,3 +176,76 @@ export const bindClientPhoneToFlow = async (
|
|
|
174
176
|
throw new Error(`No primary phone number found for client: ${clientId}`);
|
|
175
177
|
}
|
|
176
178
|
};
|
|
179
|
+
|
|
180
|
+
export const getClientPhones = async (
|
|
181
|
+
clientId: string,
|
|
182
|
+
): Promise<{ phoneNumber: string }[]> => {
|
|
183
|
+
const docs = await getPhoneNumbersCollection()
|
|
184
|
+
.aggregate<{ _id: string }>([
|
|
185
|
+
{ $match: { client_id: clientId } },
|
|
186
|
+
{ $group: { _id: "$phone_number" } },
|
|
187
|
+
])
|
|
188
|
+
.toArray();
|
|
189
|
+
return docs.map((d) => ({ phoneNumber: d._id }));
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
export const getAllClientPhoneMappings = async (): Promise<
|
|
193
|
+
Map<string, string>
|
|
194
|
+
> => {
|
|
195
|
+
const docs = await getPhoneNumbersCollection()
|
|
196
|
+
.aggregate<{ _id: string; phone_number: string }>([
|
|
197
|
+
{ $sort: { is_primary: -1, updatedAt: -1 } },
|
|
198
|
+
{ $group: { _id: "$client_id", phone_number: { $first: "$phone_number" } } },
|
|
199
|
+
])
|
|
200
|
+
.toArray();
|
|
201
|
+
return new Map(docs.map((d) => [d._id, d.phone_number]));
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
export const getDistinctClientPhonePairs = async (): Promise<
|
|
205
|
+
{ clientId: string; phoneNumber: string }[]
|
|
206
|
+
> => {
|
|
207
|
+
const docs = await getPhoneNumbersCollection()
|
|
208
|
+
.aggregate<{ _id: { client_id: string; phone_number: string } }>([
|
|
209
|
+
{ $group: { _id: { client_id: "$client_id", phone_number: "$phone_number" } } },
|
|
210
|
+
])
|
|
211
|
+
.toArray();
|
|
212
|
+
return docs.map((d) => ({
|
|
213
|
+
clientId: d._id.client_id,
|
|
214
|
+
phoneNumber: d._id.phone_number,
|
|
215
|
+
}));
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
export const findClientsByPhoneNumbers = async (
|
|
219
|
+
phoneNumbers: string[],
|
|
220
|
+
excludeClientId?: string,
|
|
221
|
+
): Promise<Map<string, string[]>> => {
|
|
222
|
+
const result = new Map<string, string[]>(
|
|
223
|
+
phoneNumbers.map((p) => [p, [] as string[]]),
|
|
224
|
+
);
|
|
225
|
+
if (phoneNumbers.length === 0) return result;
|
|
226
|
+
|
|
227
|
+
const docs = await getPhoneNumbersCollection()
|
|
228
|
+
.aggregate<{ _id: string; clientIds: string[] }>([
|
|
229
|
+
{
|
|
230
|
+
$match: {
|
|
231
|
+
phone_number: { $in: phoneNumbers },
|
|
232
|
+
...(excludeClientId ? { client_id: { $ne: excludeClientId } } : {}),
|
|
233
|
+
},
|
|
234
|
+
},
|
|
235
|
+
{ $group: { _id: "$phone_number", clientIds: { $addToSet: "$client_id" } } },
|
|
236
|
+
])
|
|
237
|
+
.toArray();
|
|
238
|
+
|
|
239
|
+
docs.forEach((d) => result.set(d._id, d.clientIds));
|
|
240
|
+
return result;
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
/** Deletes this number's record for every client that references it. Check `findClientsByPhoneNumbers` first to see who's affected. */
|
|
244
|
+
export const deletePhoneNumberRecords = async (
|
|
245
|
+
phoneNumber: string,
|
|
246
|
+
): Promise<number> => {
|
|
247
|
+
const result = await getPhoneNumbersCollection().deleteMany({
|
|
248
|
+
phone_number: phoneNumber,
|
|
249
|
+
});
|
|
250
|
+
return result.deletedCount;
|
|
251
|
+
};
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import {
|
|
2
|
+
aggregateActiveCallsSummary,
|
|
3
|
+
aggregateTodayCallsSummary,
|
|
4
|
+
aggregateClientCallStats,
|
|
5
|
+
aggregateSessionCallStats,
|
|
6
|
+
findClientIdsByCallSids,
|
|
7
|
+
} from "../results.statistics.getters";
|
|
8
|
+
import { getResultsCollection } from "../results.getter";
|
|
9
|
+
import { createResult, createCallResult } from "../../../test-utils/factories";
|
|
10
|
+
|
|
11
|
+
describe("results statistics getters", () => {
|
|
12
|
+
describe("aggregateActiveCallsSummary", () => {
|
|
13
|
+
it("should count distinct active calls within the last 2 hours, split incoming/outgoing", async () => {
|
|
14
|
+
const now = new Date();
|
|
15
|
+
await getResultsCollection().insertMany([
|
|
16
|
+
createResult({
|
|
17
|
+
clientId: "clientA",
|
|
18
|
+
sessionId: "incomingCall",
|
|
19
|
+
calls: [createCallResult({ status: "in-progress", date: now })],
|
|
20
|
+
}),
|
|
21
|
+
createResult({
|
|
22
|
+
clientId: "clientB",
|
|
23
|
+
sessionId: "someSessionId",
|
|
24
|
+
calls: [createCallResult({ status: "in-progress", date: now })],
|
|
25
|
+
}),
|
|
26
|
+
createResult({
|
|
27
|
+
clientId: "clientC",
|
|
28
|
+
sessionId: "incomingCall",
|
|
29
|
+
calls: [createCallResult({ status: "completed", date: now })],
|
|
30
|
+
}),
|
|
31
|
+
]);
|
|
32
|
+
|
|
33
|
+
const summary = await aggregateActiveCallsSummary();
|
|
34
|
+
|
|
35
|
+
expect(summary.total).toBe(2);
|
|
36
|
+
expect(summary.incoming).toBe(1);
|
|
37
|
+
expect(summary.outgoing).toBe(1);
|
|
38
|
+
expect(summary.activeClientIds.sort()).toEqual(["clientA", "clientB"]);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("should filter by clientId when provided", async () => {
|
|
42
|
+
const now = new Date();
|
|
43
|
+
await getResultsCollection().insertMany([
|
|
44
|
+
createResult({
|
|
45
|
+
clientId: "onlyThisClient",
|
|
46
|
+
calls: [createCallResult({ status: "in-progress", date: now })],
|
|
47
|
+
}),
|
|
48
|
+
createResult({
|
|
49
|
+
clientId: "otherClient",
|
|
50
|
+
calls: [createCallResult({ status: "in-progress", date: now })],
|
|
51
|
+
}),
|
|
52
|
+
]);
|
|
53
|
+
|
|
54
|
+
const summary = await aggregateActiveCallsSummary("onlyThisClient");
|
|
55
|
+
expect(summary.total).toBe(1);
|
|
56
|
+
expect(summary.activeClientIds).toEqual(["onlyThisClient"]);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("should exclude calls older than 2 hours", async () => {
|
|
60
|
+
const threeHoursAgo = new Date(Date.now() - 3 * 60 * 60 * 1000);
|
|
61
|
+
await getResultsCollection().insertOne(
|
|
62
|
+
createResult({
|
|
63
|
+
calls: [
|
|
64
|
+
createCallResult({ status: "in-progress", date: threeHoursAgo }),
|
|
65
|
+
],
|
|
66
|
+
}),
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
const summary = await aggregateActiveCallsSummary();
|
|
70
|
+
expect(summary.total).toBe(0);
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
describe("aggregateTodayCallsSummary", () => {
|
|
75
|
+
it("should aggregate status/type counts and total minutes within the date range", async () => {
|
|
76
|
+
const today = new Date().toISOString().slice(0, 10);
|
|
77
|
+
await getResultsCollection().insertMany([
|
|
78
|
+
createResult({
|
|
79
|
+
sessionId: "incomingCall",
|
|
80
|
+
calls: [
|
|
81
|
+
createCallResult({
|
|
82
|
+
status: "completed",
|
|
83
|
+
callLength: "120 seconds",
|
|
84
|
+
date: new Date(),
|
|
85
|
+
}),
|
|
86
|
+
],
|
|
87
|
+
}),
|
|
88
|
+
createResult({
|
|
89
|
+
sessionId: "someSession",
|
|
90
|
+
calls: [
|
|
91
|
+
createCallResult({
|
|
92
|
+
status: "failed",
|
|
93
|
+
callLength: "30 seconds",
|
|
94
|
+
date: new Date(),
|
|
95
|
+
}),
|
|
96
|
+
],
|
|
97
|
+
}),
|
|
98
|
+
]);
|
|
99
|
+
|
|
100
|
+
const summary = await aggregateTodayCallsSummary({ dateFrom: today, dateTo: today });
|
|
101
|
+
|
|
102
|
+
expect(summary.total).toBe(2);
|
|
103
|
+
expect(summary.incoming).toBe(1);
|
|
104
|
+
expect(summary.outgoing).toBe(1);
|
|
105
|
+
expect(summary.completed).toBe(1);
|
|
106
|
+
expect(summary.failed).toBe(1);
|
|
107
|
+
expect(summary.totalMinutes).toBe(3); // ceil(150/60)
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it("should respect the given timezone when resolving the default (today) range", async () => {
|
|
111
|
+
// 23:30 UTC on 2026-01-01 is already 2026-01-02 in Asia/Jerusalem (+2).
|
|
112
|
+
const lateUtc = new Date("2026-01-01T23:30:00.000Z");
|
|
113
|
+
await getResultsCollection().insertOne(
|
|
114
|
+
createResult({
|
|
115
|
+
calls: [createCallResult({ status: "completed", date: lateUtc })],
|
|
116
|
+
}),
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
const summaryUtc = await aggregateTodayCallsSummary({
|
|
120
|
+
dateFrom: "2026-01-01",
|
|
121
|
+
dateTo: "2026-01-01",
|
|
122
|
+
timezone: "UTC",
|
|
123
|
+
});
|
|
124
|
+
expect(summaryUtc.total).toBe(1);
|
|
125
|
+
|
|
126
|
+
const summaryJerusalem = await aggregateTodayCallsSummary({
|
|
127
|
+
dateFrom: "2026-01-01",
|
|
128
|
+
dateTo: "2026-01-01",
|
|
129
|
+
timezone: "Asia/Jerusalem",
|
|
130
|
+
});
|
|
131
|
+
expect(summaryJerusalem.total).toBe(0);
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
describe("aggregateClientCallStats", () => {
|
|
136
|
+
it("should group call counts and minutes by clientId", async () => {
|
|
137
|
+
const today = new Date().toISOString().slice(0, 10);
|
|
138
|
+
await getResultsCollection().insertOne(
|
|
139
|
+
createResult({
|
|
140
|
+
clientId: "clientX",
|
|
141
|
+
calls: [
|
|
142
|
+
createCallResult({
|
|
143
|
+
status: "completed",
|
|
144
|
+
callLength: "60 seconds",
|
|
145
|
+
date: new Date(),
|
|
146
|
+
}),
|
|
147
|
+
createCallResult({
|
|
148
|
+
status: "failed",
|
|
149
|
+
callLength: "60 seconds",
|
|
150
|
+
date: new Date(),
|
|
151
|
+
}),
|
|
152
|
+
],
|
|
153
|
+
}),
|
|
154
|
+
);
|
|
155
|
+
|
|
156
|
+
const rows = await aggregateClientCallStats({ dateFrom: today, dateTo: today });
|
|
157
|
+
const row = rows.find((r) => r.clientId === "clientX");
|
|
158
|
+
|
|
159
|
+
expect(row).toMatchObject({ totalCalls: 2, completed: 1, failed: 1 });
|
|
160
|
+
expect(row?.totalMinutes).toBe(2);
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it("should not include other clients when filtered by clientId", async () => {
|
|
164
|
+
const today = new Date().toISOString().slice(0, 10);
|
|
165
|
+
await getResultsCollection().insertMany([
|
|
166
|
+
createResult({
|
|
167
|
+
clientId: "wantedClient",
|
|
168
|
+
calls: [createCallResult({ status: "completed", date: new Date() })],
|
|
169
|
+
}),
|
|
170
|
+
createResult({
|
|
171
|
+
clientId: "unwantedClient",
|
|
172
|
+
calls: [createCallResult({ status: "completed", date: new Date() })],
|
|
173
|
+
}),
|
|
174
|
+
]);
|
|
175
|
+
|
|
176
|
+
const rows = await aggregateClientCallStats({
|
|
177
|
+
dateFrom: today,
|
|
178
|
+
dateTo: today,
|
|
179
|
+
clientId: "wantedClient",
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
expect(rows).toHaveLength(1);
|
|
183
|
+
expect(rows[0].clientId).toBe("wantedClient");
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
describe("aggregateSessionCallStats", () => {
|
|
188
|
+
it("should group call counts by sessionName", async () => {
|
|
189
|
+
const today = new Date().toISOString().slice(0, 10);
|
|
190
|
+
await getResultsCollection().insertOne(
|
|
191
|
+
createResult({
|
|
192
|
+
sessionName: "My Session",
|
|
193
|
+
calls: [createCallResult({ status: "completed", date: new Date() })],
|
|
194
|
+
}),
|
|
195
|
+
);
|
|
196
|
+
|
|
197
|
+
const rows = await aggregateSessionCallStats({ dateFrom: today, dateTo: today });
|
|
198
|
+
expect(rows.find((r) => r.sessionName === "My Session")).toMatchObject({
|
|
199
|
+
totalCalls: 1,
|
|
200
|
+
completed: 1,
|
|
201
|
+
});
|
|
202
|
+
});
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
describe("findClientIdsByCallSids", () => {
|
|
206
|
+
it("should map each callSid to the clientId that owns it", async () => {
|
|
207
|
+
const callA = createCallResult({ callSid: "CAaaa" });
|
|
208
|
+
const callB = createCallResult({ callSid: "CAbbb" });
|
|
209
|
+
await getResultsCollection().insertMany([
|
|
210
|
+
createResult({ clientId: "clientA", calls: [callA] }),
|
|
211
|
+
createResult({ clientId: "clientB", calls: [callB] }),
|
|
212
|
+
]);
|
|
213
|
+
|
|
214
|
+
const map = await findClientIdsByCallSids(["CAaaa", "CAbbb", "CAunknown"]);
|
|
215
|
+
|
|
216
|
+
expect(map.get("CAaaa")).toBe("clientA");
|
|
217
|
+
expect(map.get("CAbbb")).toBe("clientB");
|
|
218
|
+
expect(map.has("CAunknown")).toBe(false);
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
it("should return an empty map when given no callSids", async () => {
|
|
222
|
+
const map = await findClientIdsByCallSids([]);
|
|
223
|
+
expect(map.size).toBe(0);
|
|
224
|
+
});
|
|
225
|
+
});
|
|
226
|
+
});
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export * from "./results.constants";
|
|
1
2
|
export {
|
|
2
3
|
getResultsCollection,
|
|
3
4
|
initializeResultDocument,
|
|
@@ -5,3 +6,11 @@ export {
|
|
|
5
6
|
findResultByCallSid,
|
|
6
7
|
} from "./results.getter";
|
|
7
8
|
export type * from "./results.types";
|
|
9
|
+
export {
|
|
10
|
+
aggregateActiveCallsSummary,
|
|
11
|
+
aggregateTodayCallsSummary,
|
|
12
|
+
aggregateClientCallStats,
|
|
13
|
+
aggregateSessionCallStats,
|
|
14
|
+
findClientIdsByCallSids,
|
|
15
|
+
} from "./results.statistics.getters";
|
|
16
|
+
export type * from "./results.statistics.types";
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** sessionId used for calls that arrived without an existing session (incoming calls). */
|
|
2
|
+
export const INCOMING_SESSION_ID = "incomingCall";
|
|
3
|
+
|
|
4
|
+
/** Twilio call statuses considered "currently active" for the live calls summary. */
|
|
5
|
+
export const ACTIVE_STATUSES = ["in-progress"];
|
|
6
|
+
|
|
7
|
+
/** Lookback window for the "active calls right now" summary. */
|
|
8
|
+
export const ACTIVE_CALLS_WINDOW_MS = 2 * 60 * 60 * 1000;
|
|
9
|
+
|
|
10
|
+
/** Max rows returned by the per-session call stats breakdown. */
|
|
11
|
+
export const SESSION_STATS_LIMIT = 20;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Collection, ObjectId } from "mongodb";
|
|
2
2
|
import { getDb, Session } from "../index";
|
|
3
|
+
import { INCOMING_SESSION_ID } from "./results.constants";
|
|
3
4
|
import type { Result } from "./results.types";
|
|
4
5
|
|
|
5
6
|
export const getResultsCollection = (): Collection<Result> =>
|
|
@@ -17,8 +18,8 @@ export const initializeResultDocument = async (
|
|
|
17
18
|
runId: createRunId(),
|
|
18
19
|
date: new Date(),
|
|
19
20
|
calls: [],
|
|
20
|
-
sessionId: session ? session._id!.toString() :
|
|
21
|
-
sessionName: session ? session.session_name :
|
|
21
|
+
sessionId: session ? session._id!.toString() : INCOMING_SESSION_ID,
|
|
22
|
+
sessionName: session ? session.session_name : INCOMING_SESSION_ID,
|
|
22
23
|
clientId,
|
|
23
24
|
});
|
|
24
25
|
const result = await getResultsCollection().findOne({ _id: insertedId });
|