@talkpilot/core-db 1.0.13 → 1.0.15

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 (76) hide show
  1. package/dist/municipal/departmentsSubjects/departmentsSubjects.getters.d.ts +2 -2
  2. package/dist/municipal/departmentsSubjects/departmentsSubjects.getters.d.ts.map +1 -1
  3. package/dist/municipal/departmentsSubjects/departmentsSubjects.getters.js.map +1 -1
  4. package/dist/municipal/departmentsSubjects/departmentsSubjects.types.d.ts +1 -4
  5. package/dist/municipal/departmentsSubjects/departmentsSubjects.types.d.ts.map +1 -1
  6. package/dist/municipal/departmentsSubjects/index.d.ts +1 -1
  7. package/dist/municipal/departmentsSubjects/index.d.ts.map +1 -1
  8. package/dist/municipal/index.d.ts +2 -0
  9. package/dist/municipal/index.d.ts.map +1 -1
  10. package/dist/municipal/index.js +1 -0
  11. package/dist/municipal/index.js.map +1 -1
  12. package/dist/municipal/streets/streets.getters.d.ts.map +1 -1
  13. package/dist/municipal/streets/streets.getters.js.map +1 -1
  14. package/dist/municipal/systemInstructions/index.d.ts +4 -0
  15. package/dist/municipal/systemInstructions/index.d.ts.map +1 -0
  16. package/dist/municipal/systemInstructions/index.js +19 -0
  17. package/dist/municipal/systemInstructions/index.js.map +1 -0
  18. package/dist/municipal/systemInstructions/instructions.getters.d.ts +30 -0
  19. package/dist/municipal/systemInstructions/instructions.getters.d.ts.map +1 -0
  20. package/dist/municipal/systemInstructions/instructions.getters.js +46 -0
  21. package/dist/municipal/systemInstructions/instructions.getters.js.map +1 -0
  22. package/dist/municipal/systemInstructions/instructions.setters.d.ts +36 -0
  23. package/dist/municipal/systemInstructions/instructions.setters.d.ts.map +1 -0
  24. package/dist/municipal/systemInstructions/instructions.setters.js +64 -0
  25. package/dist/municipal/systemInstructions/instructions.setters.js.map +1 -0
  26. package/dist/municipal/systemInstructions/instructions.types.d.ts +14 -0
  27. package/dist/municipal/systemInstructions/instructions.types.d.ts.map +1 -0
  28. package/dist/municipal/systemInstructions/instructions.types.js +3 -0
  29. package/dist/municipal/systemInstructions/instructions.types.js.map +1 -0
  30. package/dist/municipal/tickets/tickets.getters.d.ts.map +1 -1
  31. package/dist/municipal/tickets/tickets.getters.js.map +1 -1
  32. package/dist/municipal/tickets/tickets.types.d.ts +1 -1
  33. package/dist/municipal/tickets/tickets.types.d.ts.map +1 -1
  34. package/dist/municipal/utils/types.d.ts +5 -0
  35. package/dist/municipal/utils/types.d.ts.map +1 -0
  36. package/dist/municipal/utils/types.js +3 -0
  37. package/dist/municipal/utils/types.js.map +1 -0
  38. package/dist/talkpilot/flows/flows.schema.d.ts +3 -0
  39. package/dist/talkpilot/flows/flows.schema.d.ts.map +1 -1
  40. package/dist/talkpilot/flows/flows.schema.js +1 -0
  41. package/dist/talkpilot/flows/flows.schema.js.map +1 -1
  42. package/dist/talkpilot/flows/flows.types.d.ts +1 -0
  43. package/dist/talkpilot/flows/flows.types.d.ts.map +1 -1
  44. package/dist/talkpilot/flows/index.d.ts +1 -1
  45. package/dist/talkpilot/flows/index.d.ts.map +1 -1
  46. package/dist/talkpilot/flows/index.js +2 -1
  47. package/dist/talkpilot/flows/index.js.map +1 -1
  48. package/dist/test-utils/factories/municipal/departmentsSubjects.d.ts.map +1 -1
  49. package/dist/test-utils/factories/municipal/departmentsSubjects.js.map +1 -1
  50. package/package.json +1 -1
  51. package/src/municipal/departmentsSubjects/departmentsSubjects.getters.ts +2 -2
  52. package/src/municipal/departmentsSubjects/departmentsSubjects.types.ts +4 -8
  53. package/src/municipal/departmentsSubjects/index.ts +0 -1
  54. package/src/municipal/index.ts +3 -1
  55. package/src/municipal/streets/streets.getters.ts +1 -1
  56. package/src/municipal/systemInstructions/__tests__/getters.spec.ts +106 -0
  57. package/src/municipal/systemInstructions/__tests__/setters.spec.ts +201 -0
  58. package/src/municipal/systemInstructions/index.ts +3 -0
  59. package/src/municipal/systemInstructions/instructions.getters.ts +45 -0
  60. package/src/municipal/systemInstructions/instructions.setters.ts +79 -0
  61. package/src/municipal/systemInstructions/instructions.types.ts +15 -0
  62. package/src/municipal/tickets/tickets.getters.ts +1 -1
  63. package/src/municipal/tickets/tickets.types.ts +2 -1
  64. package/src/municipal/utils/types.ts +6 -0
  65. package/src/talkpilot/flows/__tests__/flows.schema.spec.ts +13 -0
  66. package/src/talkpilot/flows/flows.schema.ts +1 -0
  67. package/src/talkpilot/flows/flows.types.ts +1 -0
  68. package/src/talkpilot/flows/index.ts +1 -1
  69. package/src/talkpilot/phone_numbers/__tests__/phone_numbers.spec.ts +2 -1
  70. package/src/talkpilot/sessions/__tests__/sessions.spec.ts +1 -1
  71. package/src/test-utils/factories/municipal/departmentsSubjects.ts +2 -1
  72. package/src/test-utils/factories/municipal/tickets.ts +1 -1
  73. package/dist/talkpilot/subscriptions/subscriptions.utils.d.ts +0 -4
  74. package/dist/talkpilot/subscriptions/subscriptions.utils.d.ts.map +0 -1
  75. package/dist/talkpilot/subscriptions/subscriptions.utils.js +0 -20
  76. package/dist/talkpilot/subscriptions/subscriptions.utils.js.map +0 -1
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getFlowsCollection = void 0;
3
+ exports.findFlowById = exports.getFlowsCollection = void 0;
4
4
  var flows_getter_1 = require("./flows.getter");
5
5
  Object.defineProperty(exports, "getFlowsCollection", { enumerable: true, get: function () { return flows_getter_1.getFlowsCollection; } });
6
+ Object.defineProperty(exports, "findFlowById", { enumerable: true, get: function () { return flows_getter_1.findFlowById; } });
6
7
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/talkpilot/flows/index.ts"],"names":[],"mappings":";;;AAAA,+CAAoD;AAA3C,kHAAA,kBAAkB,OAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/talkpilot/flows/index.ts"],"names":[],"mappings":";;;AAAA,+CAAkE;AAAzD,kHAAA,kBAAkB,OAAA;AAAE,4GAAA,YAAY,OAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"departmentsSubjects.d.ts","sourceRoot":"","sources":["../../../../src/test-utils/factories/municipal/departmentsSubjects.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAGlC,OAAO,KAAK,EACV,iBAAiB,EAElB,MAAM,kEAAkE,CAAC;AAI1E,eAAO,MAAM,wBAAwB,4GAclC,CAAC;AAEJ,wBAAgB,uBAAuB,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC,iBAAiB,CAAC,GAAG,iBAAiB,CAEjG"}
1
+ {"version":3,"file":"departmentsSubjects.d.ts","sourceRoot":"","sources":["../../../../src/test-utils/factories/municipal/departmentsSubjects.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAGlC,OAAO,KAAK,EACV,iBAAiB,EAElB,MAAM,kEAAkE,CAAC;AAK1E,eAAO,MAAM,wBAAwB,4GAclC,CAAC;AAEJ,wBAAgB,uBAAuB,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC,iBAAiB,CAAC,GAAG,iBAAiB,CAEjG"}
@@ -1 +1 @@
1
- {"version":3,"file":"departmentsSubjects.js","sourceRoot":"","sources":["../../../../src/test-utils/factories/municipal/departmentsSubjects.ts"],"names":[],"mappings":";;;AA0BA,0DAEC;AA5BD,qCAAkC;AAClC,qCAAmC;AACnC,2CAAwC;AAMxC,MAAM,YAAY,GAAe,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,WAAW,EAAE,OAAO,CAAC,CAAC;AAEzE,QAAA,wBAAwB,GAAG,iBAAO,CAAC,MAAM,CAAoB,GAAG,EAAE,CAAC,CAAC;IAC/E,GAAG,EAAE,IAAI,kBAAQ,EAAE;IACnB,SAAS,EAAE,IAAI,IAAI,EAAE;IACrB,SAAS,EAAE,IAAI,IAAI,EAAE;IACrB,WAAW,EAAE,aAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;IACjC,UAAU,EAAE,aAAK,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;IACnC,gBAAgB,EAAE,aAAK,CAAC,KAAK,CAAC,IAAI,EAAE;IACpC,cAAc,EAAE,aAAK,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;IACvC,YAAY,EAAE,CAAC,aAAK,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC;IACtC,QAAQ,EAAE,aAAK,CAAC,OAAO,CAAC,YAAY,CAAC,YAAY,CAAC;IAClD,UAAU,EAAE,IAAI;IAChB,YAAY,EAAE,EAAE;IAChB,cAAc,EAAE,EAAE;IAClB,GAAG,EAAE,aAAK,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;CAC7C,CAAC,CAAC,CAAC;AAEJ,SAAgB,uBAAuB,CAAC,SAAsC;IAC5E,OAAO,gCAAwB,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;AACnD,CAAC"}
1
+ {"version":3,"file":"departmentsSubjects.js","sourceRoot":"","sources":["../../../../src/test-utils/factories/municipal/departmentsSubjects.ts"],"names":[],"mappings":";;;AA2BA,0DAEC;AA7BD,qCAAkC;AAClC,qCAAmC;AACnC,2CAAwC;AAOxC,MAAM,YAAY,GAAe,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,WAAW,EAAE,OAAO,CAAC,CAAC;AAEzE,QAAA,wBAAwB,GAAG,iBAAO,CAAC,MAAM,CAAoB,GAAG,EAAE,CAAC,CAAC;IAC/E,GAAG,EAAE,IAAI,kBAAQ,EAAE;IACnB,SAAS,EAAE,IAAI,IAAI,EAAE;IACrB,SAAS,EAAE,IAAI,IAAI,EAAE;IACrB,WAAW,EAAE,aAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;IACjC,UAAU,EAAE,aAAK,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;IACnC,gBAAgB,EAAE,aAAK,CAAC,KAAK,CAAC,IAAI,EAAE;IACpC,cAAc,EAAE,aAAK,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;IACvC,YAAY,EAAE,CAAC,aAAK,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC;IACtC,QAAQ,EAAE,aAAK,CAAC,OAAO,CAAC,YAAY,CAAC,YAAY,CAAC;IAClD,UAAU,EAAE,IAAI;IAChB,YAAY,EAAE,EAAE;IAChB,cAAc,EAAE,EAAE;IAClB,GAAG,EAAE,aAAK,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;CAC7C,CAAC,CAAC,CAAC;AAEJ,SAAgB,uBAAuB,CAAC,SAAsC;IAC5E,OAAO,gCAAwB,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;AACnD,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@talkpilot/core-db",
3
- "version": "1.0.13",
3
+ "version": "1.0.15",
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",
@@ -1,6 +1,6 @@
1
- import { getDb, DepartmentSubject } from '../index';
1
+ import { getDb, DepartmentSubject, CityName } from '../index';
2
2
  import { Collection, Filter } from 'mongodb';
3
- import { CityName, VectorSearchResult } from './departmentsSubjects.types';
3
+ import { VectorSearchResult } from './departmentsSubjects.types';
4
4
 
5
5
  export const getDepartmentsSubjectsCollection = (): Collection<DepartmentSubject> => {
6
6
  return getDb().collection<DepartmentSubject>('departmentsSubjects');
@@ -1,6 +1,7 @@
1
- import { ObjectId, WithId } from 'mongodb';
2
- import { Request } from 'express';
3
- import { ClientConfigDoc, Products } from '../../talkpilot/clientsConfig/clientsConfig.types';
1
+ import {ObjectId, WithId} from 'mongodb';
2
+ import {Request} from 'express';
3
+ import {ClientConfigDoc, Products} from '../../talkpilot/clientsConfig/clientsConfig.types';
4
+ import {CityName} from "../utils/types";
4
5
 
5
6
  export type DepartmentSubject = {
6
7
  _id: ObjectId;
@@ -56,11 +57,6 @@ export type VectorSearchResult = {
56
57
  score?: number;
57
58
  };
58
59
 
59
- /**
60
- * City name type for municipal data
61
- */
62
- // eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents
63
- export type CityName = 'ashdod' | 'maltar' | 'billit' | 'hashkelon' | 'eilat' | 'tests' | string;
64
60
  export type CommunicationType = 'free_text_sms' | 'upload_url_sms';
65
61
 
66
62
  /**
@@ -3,7 +3,6 @@ export type {
3
3
  DepartmentSubject,
4
4
  DepartmentSubjectDoc,
5
5
  VectorSearchResult,
6
- CityName,
7
6
  RequestWithClientConfig,
8
7
  Communication,
9
8
  Instruction,
@@ -4,15 +4,17 @@ export * from './cities';
4
4
  export * from './streets';
5
5
  export * from './departmentsSubjects';
6
6
  export * from './tickets';
7
+ export * from './systemInstructions';
8
+ export {CityName} from "./utils/types";
7
9
  export { municipalDataMongodbClient } from './mongodb-client';
8
10
 
9
11
  let db: Db;
10
12
  export const setDb = (d: Db) => {
11
13
  db = d;
12
14
  };
15
+
13
16
  export const getDb = (): Db => {
14
17
  if (!db) throw new Error('Municipal Data DB not initialised');
15
18
  return db;
16
19
  };
17
-
18
20
  export const ObjectId = MongoObjectId;
@@ -1,4 +1,4 @@
1
- import { CityName, getDb, ObjectId, Street } from '../index';
1
+ import {CityName, getDb, ObjectId, Street} from '../index';
2
2
  import type { StreetHint } from './streets.types';
3
3
  import { Collection, Filter, ObjectId as MongoObjectId } from 'mongodb';
4
4
 
@@ -0,0 +1,106 @@
1
+ import { createSystemInstruction } from '../instructions.setters';
2
+ import {
3
+ getSystemInstructionById,
4
+ getActiveSystemInstructionsByCity,
5
+ getAllActiveSystemInstructions,
6
+ getSystemInstructionsByToolAndCity,
7
+ findSystemInstructions
8
+ } from '../instructions.getters';
9
+ import { ObjectId } from 'mongodb';
10
+
11
+ describe('System Instructions Getter Tests', () => {
12
+ const testCity = 'Ashdod';
13
+ const otherCity = 'Tel Aviv';
14
+
15
+ describe('getters', () => {
16
+ let instructionId: string;
17
+
18
+ beforeEach(async () => {
19
+ const id = await createSystemInstruction({
20
+ cityName: testCity,
21
+ instruction: 'Find Me',
22
+ isActive: true,
23
+ tool: "search tool",
24
+ queryType: "search tool",
25
+ tags: ['important', 'urgent']
26
+ });
27
+ instructionId = id.toString();
28
+
29
+ // for filtering tests
30
+ await createSystemInstruction({
31
+ cityName: testCity,
32
+ instruction: 'Inactive One',
33
+ isActive: false,
34
+ tool: "search tool",
35
+ queryType: "search tool",
36
+ });
37
+
38
+ await createSystemInstruction({
39
+ cityName: otherCity,
40
+ instruction: 'Other City Instruction',
41
+ isActive: true,
42
+ tool: "other tool",
43
+ queryType: "other type",
44
+ });
45
+ });
46
+
47
+ describe('getSystemInstructionById', () => {
48
+ it('given existing id when fetched then return the document', async () => {
49
+ const result = await getSystemInstructionById(instructionId);
50
+ expect(result?._id.toString()).toBe(instructionId);
51
+ });
52
+
53
+ it('given non-existent id when fetched then return null', async () => {
54
+ const result = await getSystemInstructionById(new ObjectId().toHexString());
55
+ expect(result).toBeNull();
56
+ });
57
+ });
58
+
59
+ describe('getActiveSystemInstructionsByCity', () => {
60
+ it('given city with active instructions when fetched then return only active documents for that city', async () => {
61
+ const result = await getActiveSystemInstructionsByCity(testCity);
62
+ expect(result.length).toBe(1);
63
+ expect(result[0].instruction).toBe('Find Me');
64
+ });
65
+
66
+ it('given city with no instructions when fetched then return empty array', async () => {
67
+ const result = await getActiveSystemInstructionsByCity('NonExistentCity');
68
+ expect(result).toEqual([]);
69
+ });
70
+ });
71
+
72
+ describe('getSystemInstructionsByToolAndCity', () => {
73
+ it('given valid city and tool when fetched then return matching instructions', async () => {
74
+ const result = await getSystemInstructionsByToolAndCity(testCity, 'search tool');
75
+ expect(result.length).toBe(2); // One active, one inactive
76
+ expect(result.every(r => r.cityName === testCity && r.tool === 'search tool')).toBe(true);
77
+ });
78
+
79
+ it('given valid city but wrong tool when fetched then return empty array', async () => {
80
+ const result = await getSystemInstructionsByToolAndCity(testCity, 'wrong tool');
81
+ expect(result).toEqual([]);
82
+ });
83
+ });
84
+
85
+ describe('findSystemInstructions', () => {
86
+ it('given filter by tags when searched then return matching documents', async () => {
87
+ const result = await findSystemInstructions({ tags: { $in: ['important'] } });
88
+ expect(result.length).toBe(1);
89
+ expect(result[0].instruction).toBe('Find Me');
90
+ });
91
+
92
+ it('given empty filter when searched then return all documents', async () => {
93
+ const result = await findSystemInstructions({});
94
+ expect(result.length).toBe(3);
95
+ });
96
+ });
97
+
98
+ describe('getAllActiveSystemInstructions', () => {
99
+ it('when fetched then return all active instructions from all cities', async () => {
100
+ const result = await getAllActiveSystemInstructions();
101
+ expect(result.length).toBe(2); // One from Ashdod, one from Tel Aviv
102
+ expect(result.every(r => r.isActive)).toBe(true);
103
+ });
104
+ });
105
+ });
106
+ });
@@ -0,0 +1,201 @@
1
+ import {
2
+ createSystemInstruction,
3
+ updateSystemInstruction,
4
+ deleteSystemInstruction,
5
+ deactivateSystemInstruction,
6
+ activateSystemInstruction
7
+ } from '../instructions.setters';
8
+ import { getSystemInstructionById } from '../instructions.getters';
9
+ import {SystemInstruction} from "../instructions.types";
10
+ import { ObjectId } from 'mongodb';
11
+
12
+
13
+ describe('System Instructions Setter Tests', () => {
14
+ const testCity = 'Ashdod';
15
+
16
+ describe('createSystemInstruction', () => {
17
+ it('given valid data when created then save document with timestamps', async () => {
18
+ // Given
19
+ const instructionData: Omit<SystemInstruction, '_id' | 'createdAt' | 'updatedAt'> = {
20
+ cityName: testCity,
21
+ instruction: 'Test Instruction',
22
+ isActive: true,
23
+ tool: "search tool",
24
+ queryType: "search tool",
25
+ tags: ['test', 'unit']
26
+ };
27
+
28
+ // When
29
+ const id = await createSystemInstruction(instructionData);
30
+
31
+ // Then
32
+ const saved = await getSystemInstructionById(id.toString());
33
+ expect(saved).toBeDefined();
34
+ expect(saved?._id).toBeDefined();
35
+ expect(saved).toMatchObject(instructionData);
36
+ expect(saved?.createdAt).toBeInstanceOf(Date);
37
+ expect(saved?.updatedAt).toBeInstanceOf(Date);
38
+ });
39
+
40
+ it('given data without tags when created then save document with default isActive true and no tags', async () => {
41
+ // Given
42
+ const instructionData: Omit<SystemInstruction, '_id' | 'createdAt' | 'updatedAt'> = {
43
+ cityName: testCity,
44
+ instruction: 'No Tags Instruction',
45
+ isActive: true,
46
+ tool: "minimal tool",
47
+ queryType: "minimal type",
48
+ };
49
+
50
+ // When
51
+ const id = await createSystemInstruction(instructionData);
52
+
53
+ // Then
54
+ const saved = await getSystemInstructionById(id.toString());
55
+ expect(saved).toBeDefined();
56
+ expect(saved?.tags).toBeUndefined();
57
+ expect(saved?.isActive).toBe(true);
58
+ });
59
+ });
60
+
61
+ describe('updateSystemInstruction', () => {
62
+ let instructionId: string;
63
+ //Given before each test
64
+ beforeEach(async () => {
65
+ const id = await createSystemInstruction({
66
+ cityName: testCity,
67
+ instruction: 'Find Me',
68
+ isActive: true,
69
+ tool: "search tool",
70
+ queryType: "search tool",
71
+ });
72
+ instructionId = id.toString();
73
+
74
+ await createSystemInstruction({
75
+ cityName: testCity,
76
+ instruction: 'Inactive One',
77
+ isActive: false,
78
+ tool: "search tool",
79
+ queryType: "search tool",
80
+ });
81
+ });
82
+ it('given existing doc when updated then change values and refresh updatedAt', async () => {
83
+
84
+ const originalDoc = await getSystemInstructionById(instructionId.toString());
85
+
86
+ // Wait a bit to ensure timestamp difference
87
+ await new Promise(resolve => setTimeout(resolve, 10));
88
+
89
+ // When
90
+ await updateSystemInstruction(instructionId.toString(), { instruction: 'New Text' });
91
+
92
+ // Then
93
+ const updatedDoc = await getSystemInstructionById(instructionId.toString());
94
+ expect(updatedDoc?.instruction).toBe('New Text');
95
+ expect(updatedDoc?.updatedAt.getTime()).toBeGreaterThan(originalDoc!.updatedAt.getTime());
96
+ });
97
+
98
+ it('given multiple fields when updated then change all provided values', async () => {
99
+ // When
100
+ await updateSystemInstruction(instructionId, {
101
+ isActive: false,
102
+ tool: 'updated tool',
103
+ tags: ['new-tag']
104
+ });
105
+
106
+ // Then
107
+ const updatedDoc = await getSystemInstructionById(instructionId);
108
+ expect(updatedDoc?.isActive).toBe(false);
109
+ expect(updatedDoc?.tool).toBe('updated tool');
110
+ expect(updatedDoc?.tags).toEqual(['new-tag']);
111
+ });
112
+
113
+ it('given system instructions, when deactivating it then its status updated to active', async () => {
114
+ //When
115
+ const success= await deactivateSystemInstruction(instructionId.toString());
116
+ const updatedDoc= await getSystemInstructionById(instructionId.toString());
117
+ //Then
118
+ expect(success).toBe(true);
119
+ expect(updatedDoc?.isActive).toBe(false);
120
+ })
121
+ it('given invalidId when deactivating it then return false', async () => {
122
+ //When
123
+ const success= await deactivateSystemInstruction(new ObjectId().toHexString());
124
+
125
+ //Then
126
+ expect(success).toBe(false);
127
+ })
128
+
129
+ it('given already inactive instruction when deactivating then still return true and remain inactive', async () => {
130
+ // Given
131
+ await deactivateSystemInstruction(instructionId);
132
+
133
+ // When
134
+ const success = await deactivateSystemInstruction(instructionId);
135
+
136
+ // Then
137
+ expect(success).toBe(true);
138
+ const doc = await getSystemInstructionById(instructionId);
139
+ expect(doc?.isActive).toBe(false);
140
+ });
141
+
142
+ });
143
+
144
+ describe('activateSystemInstruction', () => {
145
+ let instructionId: string;
146
+ beforeEach(async () => {
147
+ const id = await createSystemInstruction({
148
+ cityName: testCity,
149
+ instruction: 'Inactive Instruction',
150
+ isActive: false,
151
+ tool: "test",
152
+ queryType: "test",
153
+ });
154
+ instructionId = id.toString();
155
+ });
156
+
157
+ it('given inactive doc when activating then set isActive to true', async () => {
158
+ // When
159
+ const success = await activateSystemInstruction(instructionId);
160
+
161
+ // Then
162
+ expect(success).toBe(true);
163
+ const updatedDoc = await getSystemInstructionById(instructionId);
164
+ expect(updatedDoc?.isActive).toBe(true);
165
+ });
166
+
167
+ it('given invalidId when activating then return false', async () => {
168
+ // When
169
+ const success = await activateSystemInstruction(new ObjectId().toHexString());
170
+
171
+ // Then
172
+ expect(success).toBe(false);
173
+ });
174
+ });
175
+
176
+ describe('deleteSystemInstruction', () => {
177
+ it('given existing id when deleted then remove document from DB', async () => {
178
+ const id = await createSystemInstruction({
179
+ cityName: testCity,
180
+ instruction: 'To be deleted',
181
+ isActive: true,
182
+ tool: "search tool",
183
+ queryType: "search tool",
184
+ });
185
+
186
+ const deleteResult = await deleteSystemInstruction(id.toString());
187
+ expect(deleteResult).toBe(true);
188
+
189
+ const findResult = await getSystemInstructionById(id.toString());
190
+ expect(findResult).toBeNull();
191
+ });
192
+
193
+ it('given non-existent id when deleting then return false', async () => {
194
+ // When
195
+ const result = await deleteSystemInstruction(new ObjectId().toHexString());
196
+
197
+ // Then
198
+ expect(result).toBe(false);
199
+ });
200
+ });
201
+ });
@@ -0,0 +1,3 @@
1
+ export * from './instructions.getters';
2
+ export * from './instructions.setters';
3
+ export type { SystemInstruction } from './instructions.types';
@@ -0,0 +1,45 @@
1
+ import {CityName, getDb, ObjectId} from '../index';
2
+ import { Collection, Filter } from 'mongodb';
3
+ import { SystemInstruction } from './instructions.types';
4
+
5
+ export const getSystemInstructionsCollection = (): Collection<SystemInstruction> => {
6
+ return getDb().collection<SystemInstruction>('system_instructions');
7
+ };
8
+ /**
9
+ * Find system instructions by filter
10
+ * @param filter filter to apply
11
+ * @returns array of system instructions
12
+ */
13
+ export const findSystemInstructions = async (
14
+ filter: Filter<SystemInstruction> = {}
15
+ ): Promise<SystemInstruction[]> => {
16
+ return await getSystemInstructionsCollection().find(filter).toArray();
17
+ };
18
+
19
+ /**
20
+ * Get system instruction by id
21
+ * @param id system instruction id
22
+ * @returns system instruction or null if not found
23
+ */
24
+ export const getSystemInstructionById = async (id: string): Promise<SystemInstruction | null> => {
25
+ return await getSystemInstructionsCollection().findOne({ _id: new ObjectId(id) });
26
+ }
27
+ /**
28
+ * Get all active system instructions for a city
29
+ * @param cityName city name
30
+ * @returns array of active system instructions
31
+ */
32
+ export const getActiveSystemInstructionsByCity = async (cityName: CityName): Promise<SystemInstruction[]> => {
33
+ return await getSystemInstructionsCollection().find({cityName, isActive: true}).toArray();
34
+ }
35
+ /**
36
+ * Get system instructions by tool and city
37
+ * @param cityName city name
38
+ * @param toolName tool name
39
+ * @returns array of system instructions
40
+ */
41
+ export const getSystemInstructionsByToolAndCity = async (cityName: CityName, toolName: string): Promise<SystemInstruction[]> => {
42
+ return await getSystemInstructionsCollection().find({cityName: cityName, tool: toolName}).toArray();
43
+ }
44
+
45
+
@@ -0,0 +1,79 @@
1
+ import { ObjectId } from '../index';
2
+ import { ObjectId as MongoObjectId } from 'mongodb';
3
+ import { SystemInstruction } from './instructions.types';
4
+ import { getSystemInstructionsCollection } from './instructions.getters';
5
+
6
+ /**
7
+ * Create a new system instruction
8
+ * @param instructionData system instruction data
9
+ * @returns inserted system instruction id
10
+ */
11
+ export const createSystemInstruction = async (
12
+ instructionData: Omit<SystemInstruction, '_id' | 'createdAt' | 'updatedAt'>
13
+ ): Promise<MongoObjectId> => {
14
+ const instruction: Omit<SystemInstruction, '_id'> = { //append doc metadata to user provided data
15
+ ...instructionData,
16
+ createdAt: new Date(),
17
+ updatedAt: new Date(),
18
+ };
19
+ const { insertedId } = await getSystemInstructionsCollection().insertOne(
20
+ instruction as SystemInstruction
21
+ );
22
+ return insertedId;
23
+ };
24
+
25
+ /**
26
+ * Update a system instruction by id
27
+ * @param id system instruction id
28
+ * @param data partial system instruction data
29
+ * @returns updated system instruction or null if not found
30
+ */
31
+ export const updateSystemInstruction = async (
32
+ id: string,
33
+ data: Partial<Omit<SystemInstruction, '_id' | 'createdAt' | 'updatedAt'>>
34
+ ): Promise<SystemInstruction | null> => {
35
+ const result = await getSystemInstructionsCollection().findOneAndUpdate(
36
+ { _id: new ObjectId(id) },
37
+ { $set: { ...data, updatedAt: new Date() } },
38
+ { returnDocument: 'after' }
39
+ );
40
+ return result || null;
41
+ };
42
+
43
+ /**
44
+ * Delete a system instruction by id
45
+ * @param id system instruction id
46
+ * @returns true if deleted, false otherwise
47
+ */
48
+ export const deleteSystemInstruction = async (id: string): Promise<boolean> => {
49
+ const result = await getSystemInstructionsCollection().deleteOne({ _id: new ObjectId(id) });
50
+ return result.deletedCount > 0;
51
+ };
52
+ /**
53
+ * Activate a system instruction by id, sets isActive to true.
54
+ * Does not check the current instruction state, calling this on an active instruction will do nothing.
55
+ * @param id system instruction id
56
+ * @returns true on success, false otherwise
57
+ */
58
+ export const activateSystemInstruction = async (id: string): Promise<boolean> => {
59
+ const result = await getSystemInstructionsCollection().findOneAndUpdate(
60
+ { _id: new ObjectId(id) },
61
+ { $set: { isActive: true } },
62
+ { returnDocument: 'after' }
63
+ );
64
+ return Boolean(result);
65
+ };
66
+ /**
67
+ * Deactivate a system instruction by id, sets isActive to false.
68
+ * Does not check the current instruction state, calling this on a deactivated instruction will do nothing.
69
+ * @param id system instruction id
70
+ * @returns true on success, false otherwise
71
+ */
72
+ export const deactivateSystemInstruction = async (id: string): Promise<boolean> => {
73
+ const result = await getSystemInstructionsCollection().findOneAndUpdate(
74
+ { _id: new ObjectId(id) },
75
+ { $set: { isActive: false } },
76
+ {returnDocument: 'after'}
77
+ );
78
+ return Boolean(result);
79
+ }
@@ -0,0 +1,15 @@
1
+ import { ObjectId } from 'mongodb';
2
+
3
+ import {CityName} from "../utils/types";
4
+
5
+ export type SystemInstruction = {
6
+ _id: ObjectId;
7
+ createdAt: Date;
8
+ updatedAt: Date;
9
+ cityName: CityName;
10
+ instruction: string;
11
+ isActive: boolean;
12
+ tool: string;
13
+ queryType: string;
14
+ tags?: string[]; //no tags implies all wildcard (?)
15
+ };
@@ -1,4 +1,4 @@
1
- import { CityName, getDb, ObjectId, Ticket } from '../index';
1
+ import {CityName, getDb, ObjectId, Ticket} from '../index';
2
2
  import type { SubjectStatsItem } from './tickets.types';
3
3
  import { Collection, Filter, ObjectId as MongoObjectId } from 'mongodb';
4
4
 
@@ -1,5 +1,6 @@
1
1
  import { ObjectId, WithId } from 'mongodb';
2
- import { CityName } from '../departmentsSubjects/departmentsSubjects.types';
2
+
3
+ import {CityName} from "../utils/types";
3
4
 
4
5
  export type Ticket = {
5
6
  _id: ObjectId;
@@ -0,0 +1,6 @@
1
+
2
+
3
+ /**
4
+ * City name type for municipal data
5
+ */
6
+ export type CityName = 'ashdod' | 'maltar' | 'billit' | 'hashkelon' | 'eilat' | 'tests' | string;
@@ -0,0 +1,13 @@
1
+ import { flowMongoSchema } from '../flows.schema';
2
+
3
+ describe('flowMongoSchema', () => {
4
+ it('defines silenceTimeoutSeconds as optional number or null', () => {
5
+ const schema: any = flowMongoSchema;
6
+
7
+ expect(schema.properties).toHaveProperty('silenceTimeoutSeconds');
8
+ expect(schema.properties.silenceTimeoutSeconds.bsonType).toEqual(['number', 'null']);
9
+ expect(schema.required).not.toContain('silenceTimeoutSeconds');
10
+ expect(schema.additionalProperties).toBe(false);
11
+ });
12
+ });
13
+
@@ -8,6 +8,7 @@ export const flowMongoSchema = {
8
8
  systemInstructions: { bsonType: 'string' },
9
9
  initialSentence: { bsonType: 'string' },
10
10
  voice: { bsonType: ['string', 'null'] },
11
+ silenceTimeoutSeconds: { bsonType: ['number', 'null'] },
11
12
  interactions: {
12
13
  bsonType: 'array',
13
14
  items: {
@@ -139,6 +139,7 @@ export type Flow = {
139
139
  voice?: string;
140
140
 
141
141
  insights: string[];
142
+ silenceTimeoutSeconds?: number;
142
143
 
143
144
  interactions: Interaction[];
144
145
 
@@ -1,2 +1,2 @@
1
- export { getFlowsCollection } from './flows.getter';
1
+ export { getFlowsCollection, findFlowById } from './flows.getter';
2
2
  export type * from './flows.types';
@@ -12,7 +12,7 @@ import { ObjectId } from 'mongodb';
12
12
  describe('db.phoneNumbers', () => {
13
13
  describe('getPhoneDataByPhoneNumber', () => {
14
14
  it('return phone number data with flow', async () => {
15
- const flow = createFlow({ clientId: 'test-client-id' });
15
+ const flow = createFlow({ clientId: 'test-client-id', silenceTimeoutSeconds: 60 });
16
16
  const phoneData = createPhoneNumber({ flow_id: flow._id, client_id: 'test-client-id' });
17
17
 
18
18
  await getFlowsCollection().insertOne(flow);
@@ -28,6 +28,7 @@ describe('db.phoneNumbers', () => {
28
28
  });
29
29
  // Specifically check if the flow was joined correctly
30
30
  expect(result?.flow.flowName).toBe(flow.flowName);
31
+ expect(result?.flow.silenceTimeoutSeconds).toBe(60);
31
32
  });
32
33
  });
33
34
 
@@ -10,7 +10,7 @@ describe('sessions', () => {
10
10
  it('returns session by incoming phone number', async () => {
11
11
  const to = '+972500000000';
12
12
  const from = '+972540000000';
13
- const flow = createFlow();
13
+ const flow = createFlow({ silenceTimeoutSeconds: 60 });
14
14
  const phoneNumberData = createPhoneNumber({ phone_number: to, flow_id: flow._id });
15
15
  const json = { [faker.lorem.word()]: faker.lorem.sentence() };
16
16
 
@@ -3,8 +3,9 @@ import { ObjectId } from 'mongodb';
3
3
  import { faker } from '@faker-js/faker';
4
4
  import type {
5
5
  DepartmentSubject,
6
- CityName,
6
+
7
7
  } from '../../../municipal/departmentsSubjects/departmentsSubjects.types';
8
+ import {CityName} from "../../../municipal";
8
9
 
9
10
  const VALID_CITIES: CityName[] = ['ashdod', 'maltar', 'billit', 'hashkelon', 'tests'];
10
11