@talkpilot/core-db 1.0.4 → 1.0.7

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.
Files changed (48) hide show
  1. package/dist/talkpilot/calls/calls.getters.d.ts +5 -2
  2. package/dist/talkpilot/calls/calls.getters.d.ts.map +1 -1
  3. package/dist/talkpilot/calls/calls.getters.js +25 -1
  4. package/dist/talkpilot/calls/calls.getters.js.map +1 -1
  5. package/dist/talkpilot/calls/calls.types.d.ts +31 -1
  6. package/dist/talkpilot/calls/calls.types.d.ts.map +1 -1
  7. package/dist/talkpilot/calls/calls.types.js +3 -0
  8. package/dist/talkpilot/calls/calls.types.js.map +1 -1
  9. package/dist/talkpilot/clientsConfig/clientsConfig.getters.d.ts +1 -1
  10. package/dist/talkpilot/clientsConfig/clientsConfig.getters.d.ts.map +1 -1
  11. package/dist/talkpilot/clientsConfig/clientsConfig.getters.js +5 -2
  12. package/dist/talkpilot/clientsConfig/clientsConfig.getters.js.map +1 -1
  13. package/dist/talkpilot/phone_numbers/phone_numbers.getter.d.ts +6 -3
  14. package/dist/talkpilot/phone_numbers/phone_numbers.getter.d.ts.map +1 -1
  15. package/dist/talkpilot/phone_numbers/phone_numbers.getter.js +35 -19
  16. package/dist/talkpilot/phone_numbers/phone_numbers.getter.js.map +1 -1
  17. package/dist/talkpilot/plans/plans.getters.d.ts +4 -2
  18. package/dist/talkpilot/plans/plans.getters.d.ts.map +1 -1
  19. package/dist/talkpilot/plans/plans.getters.js +14 -11
  20. package/dist/talkpilot/plans/plans.getters.js.map +1 -1
  21. package/dist/talkpilot/results/results.types.d.ts +2 -0
  22. package/dist/talkpilot/results/results.types.d.ts.map +1 -1
  23. package/dist/talkpilot/sessions/sessions.getter.d.ts.map +1 -1
  24. package/dist/talkpilot/sessions/sessions.getter.js +57 -6
  25. package/dist/talkpilot/sessions/sessions.getter.js.map +1 -1
  26. package/dist/talkpilot/subscriptions/subscriptions.getters.d.ts +2 -1
  27. package/dist/talkpilot/subscriptions/subscriptions.getters.d.ts.map +1 -1
  28. package/dist/talkpilot/subscriptions/subscriptions.getters.js +9 -11
  29. package/dist/talkpilot/subscriptions/subscriptions.getters.js.map +1 -1
  30. package/dist/talkpilot/utils/query.utils.d.ts +8 -0
  31. package/dist/talkpilot/utils/query.utils.d.ts.map +1 -0
  32. package/dist/talkpilot/utils/query.utils.js +17 -0
  33. package/dist/talkpilot/utils/query.utils.js.map +1 -0
  34. package/package.json +1 -1
  35. package/src/talkpilot/calls/__tests__/calls.spec.ts +58 -0
  36. package/src/talkpilot/calls/calls.getters.ts +29 -2
  37. package/src/talkpilot/calls/calls.types.ts +38 -1
  38. package/src/talkpilot/clientsConfig/clientsConfig.getters.ts +6 -6
  39. package/src/talkpilot/phone_numbers/__tests__/phone_numbers.spec.ts +57 -1
  40. package/src/talkpilot/phone_numbers/phone_numbers.getter.ts +49 -18
  41. package/src/talkpilot/plans/__tests__/plans.spec.ts +66 -0
  42. package/src/talkpilot/plans/plans.getters.ts +26 -22
  43. package/src/talkpilot/results/results.types.ts +2 -0
  44. package/src/talkpilot/sessions/__tests__/sessions.spec.ts +73 -10
  45. package/src/talkpilot/sessions/sessions.getter.ts +66 -7
  46. package/src/talkpilot/subscriptions/subscriptions.getters.ts +19 -23
  47. package/src/talkpilot/utils/__tests__/query.utils.spec.ts +49 -0
  48. package/src/talkpilot/utils/query.utils.ts +21 -0
@@ -1 +1 @@
1
- {"version":3,"file":"subscriptions.getters.js","sourceRoot":"","sources":["../../../src/talkpilot/subscriptions/subscriptions.getters.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"subscriptions.getters.js","sourceRoot":"","sources":["../../../src/talkpilot/subscriptions/subscriptions.getters.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import { FindCursor } from 'mongodb';
2
+ export type QueryOptions = {
3
+ sort?: any;
4
+ skip?: number;
5
+ limit?: number;
6
+ };
7
+ export declare const applyQueryOptions: <T>(cursor: FindCursor<T>, options?: QueryOptions) => FindCursor<T>;
8
+ //# sourceMappingURL=query.utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"query.utils.d.ts","sourceRoot":"","sources":["../../../src/talkpilot/utils/query.utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAErC,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,CAAC,EAAE,GAAG,CAAC;IACX,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAAI,CAAC,EACjC,QAAQ,UAAU,CAAC,CAAC,CAAC,EACrB,UAAU,YAAY,KACrB,UAAU,CAAC,CAAC,CASd,CAAC"}
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.applyQueryOptions = void 0;
4
+ const applyQueryOptions = (cursor, options) => {
5
+ if (!options)
6
+ return cursor;
7
+ let result = cursor;
8
+ if (options.sort)
9
+ result = result.sort(options.sort);
10
+ if (options.skip)
11
+ result = result.skip(options.skip);
12
+ if (options.limit)
13
+ result = result.limit(options.limit);
14
+ return result;
15
+ };
16
+ exports.applyQueryOptions = applyQueryOptions;
17
+ //# sourceMappingURL=query.utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"query.utils.js","sourceRoot":"","sources":["../../../src/talkpilot/utils/query.utils.ts"],"names":[],"mappings":";;;AAQO,MAAM,iBAAiB,GAAG,CAC/B,MAAqB,EACrB,OAAsB,EACP,EAAE;IACjB,IAAI,CAAC,OAAO;QAAE,OAAO,MAAM,CAAC;IAE5B,IAAI,MAAM,GAAG,MAAM,CAAC;IACpB,IAAI,OAAO,CAAC,IAAI;QAAE,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACrD,IAAI,OAAO,CAAC,IAAI;QAAE,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACrD,IAAI,OAAO,CAAC,KAAK;QAAE,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IAExD,OAAO,MAAM,CAAC;AAChB,CAAC,CAAC;AAZW,QAAA,iBAAiB,qBAY5B"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@talkpilot/core-db",
3
- "version": "1.0.4",
3
+ "version": "1.0.7",
4
4
  "description": "Core database package for centralized connections and ORM integration.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -4,6 +4,10 @@ import {
4
4
  getCallsByClient,
5
5
  getCallsByPhoneNumber,
6
6
  getCallByCallSid,
7
+ findCallsByQuery,
8
+ countCalls,
9
+ getCallsByClientAndDateRange,
10
+ getCallsCollection,
7
11
  } from '../calls.getters';
8
12
  import { ObjectId } from 'mongodb';
9
13
  import { createOutGoingCallDoc } from '../../../test-utils/factories';
@@ -53,4 +57,58 @@ describe('db.calls', () => {
53
57
 
54
58
  expect(result.some(c => c.callSid === call.callSid)).toBe(true);
55
59
  });
60
+
61
+ describe('findCallsByQuery()', () => {
62
+ it('should find calls by status with limit and sort', async () => {
63
+ const clientId = 'client123';
64
+ const call1 = createOutGoingCallDoc({ clientId, status: 'completed' });
65
+ const call2 = createOutGoingCallDoc({ clientId, status: 'completed' });
66
+ const call3 = createOutGoingCallDoc({ clientId, status: 'busy' });
67
+
68
+ // We need to control createdAt to test sort. We'll insert directly.
69
+ await getCallsCollection().insertOne({ ...call1, createdAt: new Date('2023-01-01'), updatedAt: new Date(), env: 'test' });
70
+ await getCallsCollection().insertOne({ ...call2, createdAt: new Date('2023-01-02'), updatedAt: new Date(), env: 'test' });
71
+ await getCallsCollection().insertOne({ ...call3, createdAt: new Date(), updatedAt: new Date(), env: 'test' });
72
+
73
+ const result = await findCallsByQuery(
74
+ { clientId, status: 'completed' },
75
+ { sort: { createdAt: -1 }, limit: 1 }
76
+ );
77
+
78
+ expect(result.length).toBe(1);
79
+ expect(result[0].callSid).toBe(call2.callSid);
80
+ });
81
+ });
82
+
83
+ describe('countCalls()', () => {
84
+ it('should count calls matching query', async () => {
85
+ const clientId = 'countClient';
86
+ await createCallDoc(createOutGoingCallDoc({ clientId, status: 'completed' }));
87
+ await createCallDoc(createOutGoingCallDoc({ clientId, status: 'completed' }));
88
+ await createCallDoc(createOutGoingCallDoc({ clientId, status: 'failed' }));
89
+
90
+ const count = await countCalls({ clientId, status: 'completed' });
91
+ expect(count).toBe(2);
92
+ });
93
+ });
94
+
95
+ describe('getCallsByClientAndDateRange()', () => {
96
+ it('should return calls within date range', async () => {
97
+ const clientId = 'dateRangeClient';
98
+ const startDate = new Date('2023-05-01');
99
+ const endDate = new Date('2023-05-31');
100
+
101
+ const callInside = createOutGoingCallDoc({ clientId });
102
+ const callBefore = createOutGoingCallDoc({ clientId });
103
+ const callAfter = createOutGoingCallDoc({ clientId });
104
+
105
+ await getCallsCollection().insertOne({ ...callInside, createdAt: new Date('2023-05-15'), updatedAt: new Date(), env: 'test' });
106
+ await getCallsCollection().insertOne({ ...callBefore, createdAt: new Date('2023-04-30'), updatedAt: new Date(), env: 'test' });
107
+ await getCallsCollection().insertOne({ ...callAfter, createdAt: new Date('2023-06-01'), updatedAt: new Date(), env: 'test' });
108
+
109
+ const result = await getCallsByClientAndDateRange(clientId, startDate, endDate);
110
+ expect(result.length).toBe(1);
111
+ expect(result[0].callSid).toBe(callInside.callSid);
112
+ });
113
+ });
56
114
  });
@@ -1,7 +1,8 @@
1
- import { getDb, Call, CallDoc, CallUpdateParams } from '../index';
1
+ import { getDb, Call, CallDoc, CallUpdateParams, CallQueryOptions } from '../index';
2
2
  import type { CallsByHour } from './calls.types';
3
- import { ObjectId } from 'mongodb';
3
+ import { ObjectId, Filter } from 'mongodb';
4
4
  import * as process from 'node:process';
5
+ import { applyQueryOptions } from '../utils/query.utils';
5
6
 
6
7
  export const getCallsCollection = () => {
7
8
  return getDb().collection<Call>('calls');
@@ -50,6 +51,32 @@ export const updateCallByCallSid = async (
50
51
  return result;
51
52
  };
52
53
 
54
+ // get calls by client and date range
55
+ //client here is the user id
56
+ export const getCallsByClientAndDateRange = (clientId: string, startDate: Date, endDate: Date) => {
57
+ return getCallsCollection()
58
+ .find({
59
+ clientId,
60
+ createdAt: {
61
+ $gte: startDate,
62
+ $lte: endDate,
63
+ },
64
+ })
65
+ .toArray();
66
+ };
67
+
68
+ export const findCallsByQuery = async (
69
+ query: Filter<Call>,
70
+ options?: CallQueryOptions
71
+ ): Promise<CallDoc[]> => {
72
+ const cursor = getCallsCollection().find(query);
73
+ return await applyQueryOptions(cursor, options).toArray();
74
+ };
75
+
76
+ export const countCalls = async (query: Filter<Call>): Promise<number> => {
77
+ return getCallsCollection().countDocuments(query);
78
+ };
79
+
53
80
  /**
54
81
  * Aggregate calls stats for a date range (createdAt in [startStr, endStr] in timezone).
55
82
  * completed = agentHungUp=true or status='completed'.
@@ -1,6 +1,14 @@
1
- import { ObjectId, WithId } from 'mongodb';
1
+ import { ObjectId, Sort, WithId } from 'mongodb';
2
2
  import { TranscriptionSegment } from '../results/results.types';
3
3
 
4
+ export const CONFERENCE_ROLE_CUSTOMER = 'customer' as const;
5
+ export const CONFERENCE_ROLE_SUPERVISOR = 'supervisor' as const;
6
+
7
+ export type ConferenceRole =
8
+ | typeof CONFERENCE_ROLE_CUSTOMER
9
+ | typeof CONFERENCE_ROLE_SUPERVISOR
10
+ | null;
11
+
4
12
  export type Call = {
5
13
  callSid: string;
6
14
  flowId: ObjectId;
@@ -21,7 +29,36 @@ export type Call = {
21
29
  env: string;
22
30
  updatedAt: Date;
23
31
  createdAt: Date;
32
+ isAnsweredByAnsweringMachine?: boolean;
24
33
  agentHungUp?: boolean;
34
+ endReason?: string;
35
+ conferenceName?: string | null;
36
+ conferenceSid?: string | null;
37
+ conferenceParticipantSid?: string | null;
38
+ isConferenceCall?: boolean;
39
+ conferenceRole?: ConferenceRole;
40
+ redirectedCall?: boolean;
41
+ };
42
+
43
+ export type CallQueryOptions = {
44
+ sort?: Sort;
45
+ skip?: number;
46
+ limit?: number;
47
+ };
48
+
49
+ export type CallsFilterParams = {
50
+ clientId: string;
51
+ callSid?: string;
52
+ startDate?: Date;
53
+ endDate?: Date;
54
+ status?: string;
55
+ customerPhoneNumber?: string;
56
+ agentPhoneNumber?: string;
57
+ flowId?: string;
58
+ runId?: string;
59
+ sessionId?: string;
60
+ isIncoming?: boolean;
61
+ search?: string;
25
62
  };
26
63
 
27
64
  export type ImmutableCallFields =
@@ -1,18 +1,18 @@
1
- import { ClientConfigDoc, getDb } from '../index';
1
+ import { ClientConfigDoc, getDb, findClientByPhoneNumber } from '../index';
2
2
  import { Collection } from 'mongodb';
3
3
 
4
4
  export const getClientsConfigCollection = (): Collection<ClientConfigDoc> => {
5
- return getDb().collection('clientsConfig');
5
+ return getDb().collection<ClientConfigDoc>('clientsConfig');
6
6
  };
7
7
 
8
8
  export const getClientConfig = (clientId: string) => {
9
9
  return getClientsConfigCollection().findOne({ clientId });
10
10
  };
11
11
 
12
- export const getClientConfigByPhone = async (
13
- phoneNumber: string
14
- ): Promise<ClientConfigDoc | null> => {
15
- return getClientsConfigCollection().findOne({ phoneNumber });
12
+ export const getClientConfigByPhone = async (phone: string) => {
13
+ const client = await findClientByPhoneNumber(phone);
14
+ if (!client) return null;
15
+ return getClientConfig(client.clientId);
16
16
  };
17
17
 
18
18
  export const createClientConfigDoc = async (clientConfig: ClientConfigDoc): Promise<void> => {
@@ -2,9 +2,12 @@ import {
2
2
  getPhoneDataByPhoneNumber,
3
3
  getPhoneNumbersCollection,
4
4
  getClientPhoneNumber,
5
+ getClientPhoneData,
6
+ createPhoneNumberEntity,
5
7
  } from '../phone_numbers.getter';
6
8
  import { getFlowsCollection } from '../../flows/flows.getter';
7
9
  import { createFlow, createPhoneNumber } from '../../../test-utils/factories';
10
+ import { ObjectId } from 'mongodb';
8
11
 
9
12
  describe('db.phoneNumbers', () => {
10
13
  describe('getPhoneDataByPhoneNumber', () => {
@@ -31,7 +34,7 @@ describe('db.phoneNumbers', () => {
31
34
  describe('getClientPhoneNumber', () => {
32
35
  it('return phone number by client id', async () => {
33
36
  const flow = createFlow();
34
- const phoneData = createPhoneNumber({ flow_id: flow._id });
37
+ const phoneData = createPhoneNumber({ flow_id: flow._id, is_primary: true });
35
38
 
36
39
  await getFlowsCollection().insertOne(flow);
37
40
  await getPhoneNumbersCollection().insertOne(phoneData);
@@ -40,4 +43,57 @@ describe('db.phoneNumbers', () => {
40
43
  expect(result).toBe(phoneData.phone_number);
41
44
  });
42
45
  });
46
+
47
+ describe('getClientPhoneData', () => {
48
+ it('returns primary phone data by default', async () => {
49
+ const clientId = 'multiPhoneClient';
50
+ const phone1 = createPhoneNumber({ client_id: clientId, is_primary: false, phone_number: '1', createdAt: new Date('2023-01-01') });
51
+ const phone2 = createPhoneNumber({ client_id: clientId, is_primary: true, phone_number: '2', createdAt: new Date('2023-01-02') });
52
+
53
+ await getPhoneNumbersCollection().insertOne(phone1);
54
+ await getPhoneNumbersCollection().insertOne(phone2);
55
+
56
+ const result = await getClientPhoneData(clientId);
57
+ expect(result?.phone_number).toBe('2');
58
+ });
59
+
60
+ it('returns most recent non-primary phone data if isPrimary is false', async () => {
61
+ const clientId = 'multiPhoneClient2';
62
+ const phone1 = createPhoneNumber({ client_id: clientId, is_primary: false, phone_number: '1', createdAt: new Date('2023-01-01') });
63
+ const phone2 = createPhoneNumber({ client_id: clientId, is_primary: false, phone_number: '2', createdAt: new Date('2023-01-02') });
64
+ const phone3 = createPhoneNumber({ client_id: clientId, is_primary: true, phone_number: '3', createdAt: new Date('2023-01-03') });
65
+
66
+ await getPhoneNumbersCollection().insertOne(phone1);
67
+ await getPhoneNumbersCollection().insertOne(phone2);
68
+ await getPhoneNumbersCollection().insertOne(phone3);
69
+
70
+ const result = await getClientPhoneData(clientId, false);
71
+ expect(result?.phone_number).toBe('2');
72
+ });
73
+ });
74
+
75
+ describe('createPhoneNumberEntity', () => {
76
+ it('creates first phone number as primary', async () => {
77
+ const clientId = 'newClient';
78
+ const flowId = new ObjectId().toHexString();
79
+ const phoneNumber = '+123456789';
80
+
81
+ const result = await createPhoneNumberEntity(phoneNumber, flowId, clientId);
82
+ expect(result.phone_number).toBe(phoneNumber);
83
+ expect(result.is_primary).toBe(true);
84
+ });
85
+
86
+ it('creates subsequent phone number as non-primary', async () => {
87
+ const clientId = 'existingClient';
88
+ const flowId = new ObjectId().toHexString();
89
+
90
+ // Create first one
91
+ await createPhoneNumberEntity('+111', flowId, clientId);
92
+
93
+ // Create second one
94
+ const result = await createPhoneNumberEntity('+222', flowId, clientId);
95
+ expect(result.phone_number).toBe('+222');
96
+ expect(result.is_primary).toBe(false);
97
+ });
98
+ });
43
99
  });
@@ -1,7 +1,13 @@
1
1
  import type { Collection } from 'mongodb';
2
- import { getDb, ObjectId } from '../index';
3
- import { findFlowById } from '../flows/flows.getter';
4
- import type { PhoneNumber, PhoneNumberWithFlow } from './phone_numbers.types';
2
+ import {
3
+ Client,
4
+ getClientsCollection,
5
+ getDb,
6
+ getFlowsCollection,
7
+ ObjectId,
8
+ findSubscriptions,
9
+ } from '../index';
10
+ import { PhoneNumber, PhoneNumberWithFlow } from './phone_numbers.types';
5
11
 
6
12
  export const getPhoneNumbersCollection = (): Collection<PhoneNumber> =>
7
13
  getDb().collection<PhoneNumber>('phone_numbers');
@@ -9,30 +15,46 @@ export const getPhoneNumbersCollection = (): Collection<PhoneNumber> =>
9
15
  export const getPhoneDataByPhoneNumber = async (
10
16
  phoneNumber: string
11
17
  ): Promise<PhoneNumberWithFlow | null> => {
12
- const phoneData = await getPhoneNumbersCollection().findOne({ phone_number: phoneNumber });
13
- if (!phoneData) return null;
14
- const flow = await findFlowById(phoneData.flow_id, phoneData.client_id);
15
- if (!flow) return null;
16
- return { ...phoneData, flow };
18
+ const phoneCallData = await getPhoneNumbersCollection().findOne({ phone_number: phoneNumber });
19
+ if (!phoneCallData) {
20
+ throw new Error('PhoneNumber not found');
21
+ }
22
+ const flow = await getFlowsCollection().findOne({ _id: new ObjectId(phoneCallData.flow_id) });
23
+ if (!flow) {
24
+ throw new Error('Flow not found');
25
+ }
26
+ const [subscription] =
27
+ (await findSubscriptions({ clientId: phoneCallData.client_id, isActive: true })) || [];
28
+
29
+ return { ...phoneCallData, flow, subscriptionId: subscription?._id?.toString() ?? undefined };
17
30
  };
18
31
 
19
- export const getClientPhoneNumber = async (clientId: string): Promise<string | null> => {
20
- return (await getPhoneNumbersCollection().findOne({ client_id: clientId }))?.phone_number ?? null;
32
+ export const getClientPrimaryPhoneNumber = async (clientId: string): Promise<string | null> => {
33
+ return (
34
+ (await getPhoneNumbersCollection().findOne({ client_id: clientId, is_primary: true }))
35
+ ?.phone_number ?? null
36
+ );
21
37
  };
22
38
 
23
- export const getClientPhoneData = async (clientId: string): Promise<PhoneNumber | null> => {
24
- const phoneNumberData = await getPhoneNumbersCollection().findOne({
25
- client_id: clientId,
26
- });
27
- return phoneNumberData;
39
+ export const getClientPhoneNumber = getClientPrimaryPhoneNumber;
40
+
41
+ export const getClientPhoneData = async (
42
+ clientId: string,
43
+ isPrimary?: boolean
44
+ ): Promise<PhoneNumber | null> => {
45
+ const filter = { client_id: clientId, is_primary: isPrimary !== false };
46
+ const options = isPrimary === false ? { sort: { createdAt: -1 } as const } : {};
47
+ return getPhoneNumbersCollection().findOne(filter, options);
28
48
  };
29
49
 
30
50
  export const createPhoneNumberEntity = async (
31
51
  phoneNumber: string,
32
52
  flowId: string,
33
- clientId: string,
34
- isPrimary = true,
53
+ clientId: string
35
54
  ): Promise<PhoneNumber> => {
55
+ const existing = await getClientPhoneData(clientId);
56
+ const isPrimary = !existing;
57
+
36
58
  await getPhoneNumbersCollection().insertOne({
37
59
  phone_number: phoneNumber,
38
60
  flow_id: new ObjectId(flowId),
@@ -41,7 +63,16 @@ export const createPhoneNumberEntity = async (
41
63
  createdAt: new Date(),
42
64
  updatedAt: new Date(),
43
65
  });
44
- const phoneNumberData = await getClientPhoneData(clientId);
66
+ const phoneNumberData = await getClientPhoneData(clientId, isPrimary);
45
67
  if (!phoneNumberData) throw new Error('Failed to create phoneNumber');
46
68
  return phoneNumberData;
47
69
  };
70
+
71
+ export const findClientByPhoneNumber = async (phoneNumber: string): Promise<Client> => {
72
+ const phoneData = await getPhoneNumbersCollection().findOne({ phone_number: phoneNumber });
73
+ if (!phoneData) throw new Error('Failed to get phone data');
74
+ const clientId = phoneData.client_id;
75
+ const client = await getClientsCollection().findOne({ clientId });
76
+ if (!client) throw new Error('Failed to get client');
77
+ return client;
78
+ };
@@ -0,0 +1,66 @@
1
+ import {
2
+ createPlanDoc,
3
+ findPlans,
4
+ findPlansByQuery,
5
+ countPlans,
6
+ updatePlanDoc,
7
+ getPlansCollection,
8
+ } from '../plans.getters';
9
+ import { ObjectId } from 'mongodb';
10
+ import { Plan } from '../plans.types';
11
+
12
+ describe('db.plans', () => {
13
+ const createTestPlan = (overrides?: Partial<Plan>): Omit<Plan, 'createdAt' | 'updatedAt'> => ({
14
+ productKey: 'basic-500',
15
+ name: 'Basic 500',
16
+ monthlyCallQuota: 500,
17
+ currency: 'ILS',
18
+ priceMonthlyMinor: 10000,
19
+ isActive: true,
20
+ ...overrides,
21
+ });
22
+
23
+ it('should create and find a plan', async () => {
24
+ const planData = createTestPlan({ name: 'CreateTest' });
25
+ const created = await createPlanDoc(planData);
26
+
27
+ expect(created._id).toBeDefined();
28
+ expect(created.name).toBe('CreateTest');
29
+
30
+ const found = await findPlans({ _id: created._id });
31
+ expect(found.length).toBe(1);
32
+ expect(found[0].name).toBe('CreateTest');
33
+ });
34
+
35
+ it('should find plans by query with limit', async () => {
36
+ await getPlansCollection().deleteMany({});
37
+ await createPlanDoc(createTestPlan({ productKey: 'p1', isActive: true }));
38
+ await createPlanDoc(createTestPlan({ productKey: 'p2', isActive: true }));
39
+ await createPlanDoc(createTestPlan({ productKey: 'p3', isActive: false }));
40
+
41
+ const activePlans = await findPlansByQuery({ isActive: true }, { limit: 1 });
42
+ expect(activePlans.length).toBe(1);
43
+ expect(activePlans[0].isActive).toBe(true);
44
+ });
45
+
46
+ it('should count plans matching query', async () => {
47
+ await getPlansCollection().deleteMany({});
48
+ await createPlanDoc(createTestPlan({ currency: 'USD' }));
49
+ await createPlanDoc(createTestPlan({ currency: 'USD' }));
50
+ await createPlanDoc(createTestPlan({ currency: 'ILS' }));
51
+
52
+ const count = await countPlans({ currency: 'USD' });
53
+ expect(count).toBe(2);
54
+ });
55
+
56
+ it('should update a plan', async () => {
57
+ const plan = await createPlanDoc(createTestPlan({ isActive: true }));
58
+ const updated = await updatePlanDoc(plan._id, { isActive: false });
59
+
60
+ expect(updated).not.toBeNull();
61
+ expect(updated?.isActive).toBe(false);
62
+
63
+ const found = await findPlans({ _id: plan._id });
64
+ expect(found[0].isActive).toBe(false);
65
+ });
66
+ });
@@ -1,5 +1,6 @@
1
- import { Plan, getDb, PlanDoc, PlanFilter, SimplePlanFilter, PlanQueryOptions } from '../index';
2
- import { ObjectId, Filter } from 'mongodb';
1
+ import {getDb, Plan, PlanDoc, PlanFilter, PlanQueryOptions, SimplePlanFilter} from '../index';
2
+ import {Filter, ObjectId} from 'mongodb';
3
+ import {applyQueryOptions} from '../utils/query.utils';
3
4
 
4
5
  export const getPlansCollection = () => {
5
6
  return getDb().collection<Plan>('plans');
@@ -74,16 +75,20 @@ export const findPlans = async (
74
75
  }
75
76
  }
76
77
 
77
- let queryBuilder = getPlansCollection().find(query);
78
+ const cursor = getPlansCollection().find(query);
79
+ return await applyQueryOptions(cursor, options).toArray();
80
+ };
78
81
 
79
- if (options?.sort) {
80
- queryBuilder = queryBuilder.sort(options.sort);
81
- }
82
- if (options?.limit) {
83
- queryBuilder = queryBuilder.limit(options.limit);
84
- }
82
+ export const findPlansByQuery = async (
83
+ query: Filter<Plan>,
84
+ options?: PlanQueryOptions
85
+ ): Promise<PlanDoc[]> => {
86
+ const cursor = getPlansCollection().find(query);
87
+ return await applyQueryOptions(cursor, options).toArray();
88
+ };
85
89
 
86
- return await queryBuilder.toArray();
90
+ export const countPlans = async (query: Filter<Plan>): Promise<number> => {
91
+ return getPlansCollection().countDocuments(query);
87
92
  };
88
93
 
89
94
  export const createPlanDoc = async (
@@ -103,19 +108,18 @@ export const createPlanDoc = async (
103
108
 
104
109
  export const updatePlanDoc = async (
105
110
  planId: ObjectId,
106
- updates: Partial<Pick<Plan, 'isActive'>>
111
+ updates: Partial<Omit<Plan, 'createdAt' | 'updatedAt'>>
107
112
  ): Promise<PlanDoc | null> => {
108
- const result = await getPlansCollection().findOneAndUpdate(
109
- { _id: planId },
110
- {
111
- $set: {
112
- ...updates,
113
- updatedAt: new Date(),
113
+ return await getPlansCollection().findOneAndUpdate(
114
+ {_id: planId},
115
+ {
116
+ $set: {
117
+ ...updates,
118
+ updatedAt: new Date(),
119
+ },
114
120
  },
115
- },
116
- {
117
- returnDocument: 'after',
118
- }
121
+ {
122
+ returnDocument: 'after',
123
+ }
119
124
  );
120
- return result;
121
125
  };
@@ -25,6 +25,8 @@ export interface CallResult {
25
25
  transcription: TranscriptionSegment[];
26
26
  flowId: ObjectId | string;
27
27
  recordingUrl: string | null;
28
+ agentHungUp?: boolean;
29
+ endReason?: string;
28
30
  }
29
31
 
30
32
  export type TranscriptionSegment =