@talkpilot/core-db 1.3.44 → 1.3.45

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 (57) hide show
  1. package/README.md +41 -0
  2. package/dist/index.d.ts +1 -0
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +1 -0
  5. package/dist/index.js.map +1 -1
  6. package/dist/talkpilot/calls/calls.types.d.ts +1 -0
  7. package/dist/talkpilot/calls/calls.types.d.ts.map +1 -1
  8. package/dist/talkpilot/clientsConfig/clientsConfig.getters.d.ts +39 -0
  9. package/dist/talkpilot/clientsConfig/clientsConfig.getters.d.ts.map +1 -1
  10. package/dist/talkpilot/clientsConfig/clientsConfig.getters.js +92 -1
  11. package/dist/talkpilot/clientsConfig/clientsConfig.getters.js.map +1 -1
  12. package/dist/talkpilot/clientsConfig/clientsConfig.types.d.ts +4 -0
  13. package/dist/talkpilot/clientsConfig/clientsConfig.types.d.ts.map +1 -1
  14. package/dist/talkpilot/clientsConfig/index.d.ts +1 -0
  15. package/dist/talkpilot/clientsConfig/index.d.ts.map +1 -1
  16. package/dist/talkpilot/clientsConfig/index.js +1 -0
  17. package/dist/talkpilot/clientsConfig/index.js.map +1 -1
  18. package/dist/test-utils/factories/index.d.ts +1 -0
  19. package/dist/test-utils/factories/index.d.ts.map +1 -1
  20. package/dist/test-utils/factories/index.js +1 -0
  21. package/dist/test-utils/factories/index.js.map +1 -1
  22. package/dist/test-utils/factories/websitalk/scans.d.ts.map +1 -1
  23. package/dist/test-utils/factories/websitalk/scans.js +5 -0
  24. package/dist/test-utils/factories/websitalk/scans.js.map +1 -1
  25. package/dist/websitalk/index.d.ts +1 -0
  26. package/dist/websitalk/index.d.ts.map +1 -1
  27. package/dist/websitalk/index.js +1 -0
  28. package/dist/websitalk/index.js.map +1 -1
  29. package/dist/websitalk/knowledgeDocuments/knowledgeDocuments.types.d.ts +2 -4
  30. package/dist/websitalk/knowledgeDocuments/knowledgeDocuments.types.d.ts.map +1 -1
  31. package/dist/websitalk/scans/scans.getters.d.ts.map +1 -1
  32. package/dist/websitalk/scans/scans.getters.js +8 -2
  33. package/dist/websitalk/scans/scans.getters.js.map +1 -1
  34. package/dist/websitalk/scans/scans.types.d.ts +5 -0
  35. package/dist/websitalk/scans/scans.types.d.ts.map +1 -1
  36. package/package.json +1 -1
  37. package/src/index.ts +1 -0
  38. package/src/talkpilot/calls/calls.types.ts +1 -0
  39. package/src/talkpilot/clientsConfig/__tests__/clientsConfig.getters.spec.ts +318 -0
  40. package/src/talkpilot/clientsConfig/clientsConfig.constants.ts +2 -0
  41. package/src/talkpilot/clientsConfig/clientsConfig.getters.ts +116 -2
  42. package/src/talkpilot/clientsConfig/clientsConfig.types.ts +4 -0
  43. package/src/talkpilot/clientsConfig/index.ts +1 -0
  44. package/src/test-utils/factories/index.ts +1 -0
  45. package/src/test-utils/factories/websitalk/scanResources.ts +26 -0
  46. package/src/test-utils/factories/websitalk/scans.ts +5 -0
  47. package/src/websitalk/index.ts +1 -0
  48. package/src/websitalk/knowledgeDocuments/__tests__/knowledgeDocuments.spec.ts +0 -19
  49. package/src/websitalk/knowledgeDocuments/knowledgeDocuments.types.ts +2 -2
  50. package/src/websitalk/scanResources/__tests__/scanResources.spec.ts +136 -0
  51. package/src/websitalk/scanResources/index.ts +4 -0
  52. package/src/websitalk/scanResources/scanResources.constants.ts +47 -0
  53. package/src/websitalk/scanResources/scanResources.getters.ts +94 -0
  54. package/src/websitalk/scanResources/scanResources.types.ts +90 -0
  55. package/src/websitalk/scanResources/scanResources.utils.ts +28 -0
  56. package/src/websitalk/scans/scans.getters.ts +8 -2
  57. package/src/websitalk/scans/scans.types.ts +9 -0
@@ -45,6 +45,7 @@ export type Call = {
45
45
  conferenceRole?: ConferenceRole;
46
46
  redirectedCall?: boolean;
47
47
  toolExecutions?: ToolExecution[];
48
+ transcriptionComparison?: Record<string, TranscriptionSegment[]>;
48
49
  };
49
50
 
50
51
  export type ToolExecution = {
@@ -5,7 +5,15 @@ import {
5
5
  updateActiveScanId,
6
6
  clearActiveScanId,
7
7
  updateProductConfig,
8
+ getActiveScanIds,
9
+ addActiveScanId,
10
+ removeActiveScanId,
11
+ getPrimaryScanId,
12
+ setPrimaryScanId,
13
+ clearPrimaryScanId,
8
14
  } from "../clientsConfig.getters";
15
+ import { MAX_ACTIVE_SCANS } from "../clientsConfig.constants";
16
+ import { createClientConfig } from "../../../test-utils/factories/talkpilot/clientsConfig";
9
17
 
10
18
  describe("updateProductConfig", () => {
11
19
  beforeEach(async () => {
@@ -103,3 +111,313 @@ describe("activeScanId", () => {
103
111
  expect(await getActiveScanId(clientId)).toBeUndefined();
104
112
  });
105
113
  });
114
+
115
+ describe("activeScanIds (CP-1628)", () => {
116
+ beforeEach(async () => {
117
+ await getClientsConfigCollection().deleteMany({});
118
+ });
119
+
120
+ afterEach(async () => {
121
+ await getClientsConfigCollection().deleteMany({});
122
+ });
123
+
124
+ it("returns an empty list when nothing is set", async () => {
125
+ const clientId = "client-list-1";
126
+ await getClientsConfigCollection().insertOne(
127
+ createClientConfig({
128
+ clientId,
129
+ products: {
130
+ websiteTalk: { companyName: "X", timezone: "UTC", language: "he" },
131
+ },
132
+ }),
133
+ );
134
+
135
+ expect(await getActiveScanIds(clientId)).toEqual([]);
136
+ });
137
+
138
+ it("falls back to wrapping the legacy activeScanId when activeScanIds is absent", async () => {
139
+ const clientId = "client-list-2";
140
+ await getClientsConfigCollection().insertOne(
141
+ createClientConfig({
142
+ clientId,
143
+ products: {
144
+ websiteTalk: {
145
+ companyName: "X",
146
+ timezone: "UTC",
147
+ language: "he",
148
+ activeScanId: "scan-legacy",
149
+ },
150
+ },
151
+ }),
152
+ );
153
+
154
+ expect(await getActiveScanIds(clientId)).toEqual(["scan-legacy"]);
155
+ });
156
+
157
+ it("adds a scan id and returns it from getActiveScanIds", async () => {
158
+ const clientId = "client-list-3";
159
+ await getClientsConfigCollection().insertOne(
160
+ createClientConfig({
161
+ clientId,
162
+ products: {
163
+ websiteTalk: { companyName: "X", timezone: "UTC", language: "he" },
164
+ },
165
+ }),
166
+ );
167
+
168
+ await addActiveScanId(clientId, "scan-a");
169
+ await addActiveScanId(clientId, "scan-b");
170
+
171
+ expect(await getActiveScanIds(clientId)).toEqual(["scan-a", "scan-b"]);
172
+ });
173
+
174
+ it("does not duplicate an already-present scan id, and does not count it against the cap", async () => {
175
+ const clientId = "client-list-4";
176
+ await getClientsConfigCollection().insertOne(
177
+ createClientConfig({
178
+ clientId,
179
+ products: {
180
+ websiteTalk: {
181
+ companyName: "X",
182
+ timezone: "UTC",
183
+ language: "he",
184
+ activeScanIds: ["scan-a", "scan-b", "scan-c"],
185
+ },
186
+ },
187
+ }),
188
+ );
189
+
190
+ await expect(addActiveScanId(clientId, "scan-a")).resolves.toBeUndefined();
191
+ expect(await getActiveScanIds(clientId)).toEqual([
192
+ "scan-a",
193
+ "scan-b",
194
+ "scan-c",
195
+ ]);
196
+ });
197
+
198
+ it(`throws when adding a new scan id beyond MAX_ACTIVE_SCANS (${MAX_ACTIVE_SCANS})`, async () => {
199
+ const clientId = "client-list-5";
200
+ await getClientsConfigCollection().insertOne(
201
+ createClientConfig({
202
+ clientId,
203
+ products: {
204
+ websiteTalk: {
205
+ companyName: "X",
206
+ timezone: "UTC",
207
+ language: "he",
208
+ activeScanIds: ["scan-a", "scan-b", "scan-c"],
209
+ },
210
+ },
211
+ }),
212
+ );
213
+
214
+ await expect(addActiveScanId(clientId, "scan-d")).rejects.toThrow(
215
+ `Client ${clientId} already has the maximum of ${MAX_ACTIVE_SCANS} active scans`,
216
+ );
217
+ expect(await getActiveScanIds(clientId)).toEqual([
218
+ "scan-a",
219
+ "scan-b",
220
+ "scan-c",
221
+ ]);
222
+ });
223
+
224
+ it("removes a scan id without affecting the others", async () => {
225
+ const clientId = "client-list-6";
226
+ await getClientsConfigCollection().insertOne(
227
+ createClientConfig({
228
+ clientId,
229
+ products: {
230
+ websiteTalk: {
231
+ companyName: "X",
232
+ timezone: "UTC",
233
+ language: "he",
234
+ activeScanIds: ["scan-a", "scan-b"],
235
+ },
236
+ },
237
+ }),
238
+ );
239
+
240
+ await removeActiveScanId(clientId, "scan-a");
241
+ expect(await getActiveScanIds(clientId)).toEqual(["scan-b"]);
242
+ });
243
+
244
+ it("removing a scan id that isn't present is a harmless no-op", async () => {
245
+ const clientId = "client-list-7";
246
+ await getClientsConfigCollection().insertOne(
247
+ createClientConfig({
248
+ clientId,
249
+ products: {
250
+ websiteTalk: {
251
+ companyName: "X",
252
+ timezone: "UTC",
253
+ language: "he",
254
+ activeScanIds: ["scan-a"],
255
+ },
256
+ },
257
+ }),
258
+ );
259
+
260
+ await expect(
261
+ removeActiveScanId(clientId, "scan-x"),
262
+ ).resolves.toBeUndefined();
263
+ expect(await getActiveScanIds(clientId)).toEqual(["scan-a"]);
264
+ });
265
+
266
+ it("throws when removing from a client that doesn't exist", async () => {
267
+ await expect(
268
+ removeActiveScanId("no-such-client", "scan-a"),
269
+ ).rejects.toThrow("No client config found for clientId: no-such-client");
270
+ });
271
+
272
+ it("migrates a legacy-only activeScanId into activeScanIds instead of dropping it when adding a new one", async () => {
273
+ const clientId = "client-list-8";
274
+ await getClientsConfigCollection().insertOne(
275
+ createClientConfig({
276
+ clientId,
277
+ products: {
278
+ websiteTalk: {
279
+ companyName: "X",
280
+ timezone: "UTC",
281
+ language: "he",
282
+ activeScanId: "scan-legacy",
283
+ },
284
+ },
285
+ }),
286
+ );
287
+
288
+ await addActiveScanId(clientId, "scan-new");
289
+
290
+ expect(await getActiveScanIds(clientId)).toEqual(["scan-legacy", "scan-new"]);
291
+ const doc = await getClientsConfigCollection().findOne({ clientId });
292
+ expect(doc?.products?.websiteTalk?.activeScanId).toBeUndefined();
293
+ });
294
+
295
+ it("throws when adding to a client that doesn't exist", async () => {
296
+ await expect(
297
+ addActiveScanId("no-such-client", "scan-a"),
298
+ ).rejects.toThrow("No client config found for clientId: no-such-client");
299
+ });
300
+
301
+ it("unsets a legacy-only activeScanId when it is the one being removed", async () => {
302
+ const clientId = "client-list-9";
303
+ await getClientsConfigCollection().insertOne(
304
+ createClientConfig({
305
+ clientId,
306
+ products: {
307
+ websiteTalk: {
308
+ companyName: "X",
309
+ timezone: "UTC",
310
+ language: "he",
311
+ activeScanId: "scan-legacy",
312
+ },
313
+ },
314
+ }),
315
+ );
316
+
317
+ await removeActiveScanId(clientId, "scan-legacy");
318
+
319
+ expect(await getActiveScanIds(clientId)).toEqual([]);
320
+ const doc = await getClientsConfigCollection().findOne({ clientId });
321
+ expect(doc?.products?.websiteTalk?.activeScanId).toBeUndefined();
322
+ });
323
+ });
324
+
325
+ describe("primaryScanId (CP-1628 follow-up)", () => {
326
+ beforeEach(async () => {
327
+ await getClientsConfigCollection().deleteMany({});
328
+ });
329
+
330
+ afterEach(async () => {
331
+ await getClientsConfigCollection().deleteMany({});
332
+ });
333
+
334
+ it("returns undefined when nothing is set", async () => {
335
+ const clientId = "client-primary-1";
336
+ await getClientsConfigCollection().insertOne(
337
+ createClientConfig({
338
+ clientId,
339
+ products: {
340
+ websiteTalk: { companyName: "X", timezone: "UTC", language: "he" },
341
+ },
342
+ }),
343
+ );
344
+
345
+ expect(await getPrimaryScanId(clientId)).toBeUndefined();
346
+ });
347
+
348
+ it("sets and returns the primary scan id, independent of activeScanIds", async () => {
349
+ const clientId = "client-primary-2";
350
+ await getClientsConfigCollection().insertOne(
351
+ createClientConfig({
352
+ clientId,
353
+ products: {
354
+ websiteTalk: {
355
+ companyName: "X",
356
+ timezone: "UTC",
357
+ language: "he",
358
+ activeScanIds: ["scan-a", "scan-b"],
359
+ },
360
+ },
361
+ }),
362
+ );
363
+
364
+ await setPrimaryScanId(clientId, "scan-c");
365
+
366
+ expect(await getPrimaryScanId(clientId)).toBe("scan-c");
367
+ expect(await getActiveScanIds(clientId)).toEqual(["scan-a", "scan-b"]);
368
+ });
369
+
370
+ it("replaces the previous primary scan id when set again", async () => {
371
+ const clientId = "client-primary-3";
372
+ await getClientsConfigCollection().insertOne(
373
+ createClientConfig({
374
+ clientId,
375
+ products: {
376
+ websiteTalk: {
377
+ companyName: "X",
378
+ timezone: "UTC",
379
+ language: "he",
380
+ primaryScanId: "scan-a",
381
+ },
382
+ },
383
+ }),
384
+ );
385
+
386
+ await setPrimaryScanId(clientId, "scan-b");
387
+
388
+ expect(await getPrimaryScanId(clientId)).toBe("scan-b");
389
+ });
390
+
391
+ it("clears the primary scan id", async () => {
392
+ const clientId = "client-primary-4";
393
+ await getClientsConfigCollection().insertOne(
394
+ createClientConfig({
395
+ clientId,
396
+ products: {
397
+ websiteTalk: {
398
+ companyName: "X",
399
+ timezone: "UTC",
400
+ language: "he",
401
+ primaryScanId: "scan-a",
402
+ },
403
+ },
404
+ }),
405
+ );
406
+
407
+ await clearPrimaryScanId(clientId);
408
+
409
+ expect(await getPrimaryScanId(clientId)).toBeUndefined();
410
+ });
411
+
412
+ it("throws when setting the primary scan for a client that doesn't exist", async () => {
413
+ await expect(
414
+ setPrimaryScanId("no-such-client", "scan-a"),
415
+ ).rejects.toThrow("No client config found for clientId: no-such-client");
416
+ });
417
+
418
+ it("throws when clearing the primary scan for a client that doesn't exist", async () => {
419
+ await expect(
420
+ clearPrimaryScanId("no-such-client"),
421
+ ).rejects.toThrow("No client config found for clientId: no-such-client");
422
+ });
423
+ });
@@ -0,0 +1,2 @@
1
+ /** Maximum number of scans a client may have active at once. */
2
+ export const MAX_ACTIVE_SCANS = 3;
@@ -6,8 +6,9 @@ import {
6
6
  KNOWN_PRODUCT_KEYS,
7
7
  KnownProductKey,
8
8
  } from "../index";
9
- import { Collection } from "mongodb";
9
+ import { Collection, UpdateFilter } from "mongodb";
10
10
  import { resetClientQuotaSettings } from "../clients/clients.quota.getters";
11
+ import { MAX_ACTIVE_SCANS } from "./clientsConfig.constants";
11
12
 
12
13
  export const getClientsConfigCollection = (): Collection<ClientConfigDoc> => {
13
14
  return getDb().collection<ClientConfigDoc>("clientsConfig");
@@ -17,7 +18,9 @@ export const getClientConfig = (clientId: string) => {
17
18
  return getClientsConfigCollection().findOne({ clientId });
18
19
  };
19
20
 
20
- export const getClientDisplayName = async (clientId: string): Promise<string> => {
21
+ export const getClientDisplayName = async (
22
+ clientId: string,
23
+ ): Promise<string> => {
21
24
  const doc = await getClientsConfigCollection().findOne(
22
25
  { clientId },
23
26
  { projection: { "quota.clientDisplayName": 1, _id: 0 } },
@@ -71,6 +74,24 @@ export const getActiveScanId = async (
71
74
  return cfg?.products?.websiteTalk?.activeScanId;
72
75
  };
73
76
 
77
+ /**
78
+ * Returns the client's active scan ids as a list.
79
+ * Falls back to wrapping the legacy singular `activeScanId` field when
80
+ * `activeScanIds` is absent — lets clients on the old field keep working
81
+ * without a data migration.
82
+ */
83
+ export const getActiveScanIds = async (clientId: string): Promise<string[]> => {
84
+ const cfg = await getClientConfig(clientId);
85
+ const list = cfg?.products?.websiteTalk?.activeScanIds;
86
+ if (list && list.length > 0) return list;
87
+ const legacy = cfg?.products?.websiteTalk?.activeScanId;
88
+ return legacy ? [legacy] : [];
89
+ };
90
+
91
+ /**
92
+ * @deprecated Use `addActiveScanId` instead — supports multiple simultaneously
93
+ * active scans. This function overwrites the entire single-value field.
94
+ */
74
95
  export const updateActiveScanId = async (
75
96
  clientId: string,
76
97
  scanId: string,
@@ -78,6 +99,38 @@ export const updateActiveScanId = async (
78
99
  await updateProductConfig(clientId, "websiteTalk", { activeScanId: scanId });
79
100
  };
80
101
 
102
+ /**
103
+ * Adds a scan id to the client's active list. Writes the full list back
104
+ * (rather than a bare array append) so a client still on the legacy
105
+ * singular field gets migrated instead of silently losing it.
106
+ */
107
+ export const addActiveScanId = async (
108
+ clientId: string,
109
+ scanId: string,
110
+ ): Promise<void> => {
111
+ const current = await getActiveScanIds(clientId);
112
+ if (!current.includes(scanId) && current.length >= MAX_ACTIVE_SCANS) {
113
+ throw new Error(
114
+ `Client ${clientId} already has the maximum of ${MAX_ACTIVE_SCANS} active scans`,
115
+ );
116
+ }
117
+ const next = current.includes(scanId) ? current : [...current, scanId];
118
+ const result = await getClientsConfigCollection().updateOne(
119
+ { clientId },
120
+ {
121
+ $set: { "products.websiteTalk.activeScanIds": next },
122
+ $unset: { "products.websiteTalk.activeScanId": "" },
123
+ },
124
+ );
125
+ if (result.matchedCount === 0) {
126
+ throw new Error(`No client config found for clientId: ${clientId}`);
127
+ }
128
+ };
129
+
130
+ /**
131
+ * @deprecated Use `removeActiveScanId` instead — removes a single scan from
132
+ * the active list without clearing the others.
133
+ */
81
134
  export const clearActiveScanId = async (clientId: string): Promise<void> => {
82
135
  const result = await getClientsConfigCollection().updateOne(
83
136
  { clientId },
@@ -88,6 +141,67 @@ export const clearActiveScanId = async (clientId: string): Promise<void> => {
88
141
  }
89
142
  };
90
143
 
144
+ /**
145
+ * Removes a single scan id from the active list. Also clears the legacy
146
+ * singular field when it matches, so a not-yet-migrated client's removal
147
+ * actually takes effect instead of silently no-op'ing.
148
+ */
149
+ export const removeActiveScanId = async (
150
+ clientId: string,
151
+ scanId: string,
152
+ ): Promise<void> => {
153
+ const cfg = await getClientConfig(clientId);
154
+ if (!cfg) {
155
+ throw new Error(`No client config found for clientId: ${clientId}`);
156
+ }
157
+ const updateOps: UpdateFilter<ClientConfigDoc> = {
158
+ $pull: { "products.websiteTalk.activeScanIds": scanId },
159
+ };
160
+ if (cfg.products?.websiteTalk?.activeScanId === scanId) {
161
+ updateOps.$unset = { "products.websiteTalk.activeScanId": "" };
162
+ }
163
+ await getClientsConfigCollection().updateOne({ clientId }, updateOps);
164
+ };
165
+
166
+ /** Returns the scan whose content seeds the phone call's opening description, if one is set. */
167
+ export const getPrimaryScanId = async (
168
+ clientId: string,
169
+ ): Promise<string | undefined> => {
170
+ const cfg = await getClientConfig(clientId);
171
+ return cfg?.products?.websiteTalk?.primaryScanId;
172
+ };
173
+
174
+ /**
175
+ * Sets the primary scan, replacing whatever was set before — exactly one
176
+ * primary scan at a time. Throws if the client doesn't exist.
177
+ */
178
+ export const setPrimaryScanId = async (
179
+ clientId: string,
180
+ scanId: string,
181
+ ): Promise<void> => {
182
+ const result = await getClientsConfigCollection().updateOne(
183
+ { clientId },
184
+ { $set: { "products.websiteTalk.primaryScanId": scanId } },
185
+ );
186
+ if (result.matchedCount === 0) {
187
+ throw new Error(`No client config found for clientId: ${clientId}`);
188
+ }
189
+ };
190
+
191
+ /**
192
+ * Clears the primary scan. Callers that need the opening description fall
193
+ * back to the first active scan. Throws if the client doesn't exist.
194
+ */
195
+ export const clearPrimaryScanId = async (clientId: string): Promise<void> => {
196
+ const result = await getClientsConfigCollection().updateOne(
197
+ { clientId },
198
+ { $unset: { "products.websiteTalk.primaryScanId": "" } },
199
+ );
200
+ if (result.matchedCount === 0) {
201
+ throw new Error(`No client config found for clientId: ${clientId}`);
202
+ }
203
+ };
204
+
91
205
  export async function listClientIdsWithConfiguredQuota(): Promise<string[]> {
92
206
  const docs = await getClientsConfigCollection()
93
207
  .find({ "quota.totalQuota": { $exists: true, $ne: null } })
@@ -64,6 +64,10 @@ export type WebsiteTalkProduct = {
64
64
  timezone: string;
65
65
  language: string;
66
66
  activeScanId?: string;
67
+ /** Up to MAX_ACTIVE_SCANS scan ids simultaneously serving RAG for phone calls. */
68
+ activeScanIds?: string[];
69
+ /** The single scan whose content seeds the phone call's opening description. */
70
+ primaryScanId?: string;
67
71
  };
68
72
 
69
73
  // ---------------------------------------------------------------------------
@@ -1,2 +1,3 @@
1
1
  export * from "./clientsConfig.types";
2
2
  export * from "./clientsConfig.getters";
3
+ export * from "./clientsConfig.constants";
@@ -13,5 +13,6 @@ export * from "./municipal/muniIssues";
13
13
  export * from "./municipal/streets";
14
14
  export * from "./municipal/tickets";
15
15
  export * from "./websitalk/scans";
16
+ export * from "./websitalk/scanResources";
16
17
  export * from "./websitalk/websiteUrls";
17
18
  export * from "./websitalk/knowledgeDocuments";
@@ -0,0 +1,26 @@
1
+ import { Factory } from "fishery";
2
+ import { faker } from "@faker-js/faker";
3
+ import { ObjectId } from "mongodb";
4
+ import {
5
+ URL_TASK_STATUSES,
6
+ type ScanResource,
7
+ } from "../../../websitalk/scanResources";
8
+
9
+ export const scanResourceFactory = Factory.define<ScanResource>(() => ({
10
+ urlHashId: faker.string.uuid(),
11
+ clientId: faker.string.uuid(),
12
+ scanId: new ObjectId().toString(),
13
+ websiteUrlId: new ObjectId().toString(),
14
+ url: faker.internet.url(),
15
+ status: URL_TASK_STATUSES.URL_PENDING,
16
+ depth: 0,
17
+ retryCount: 0,
18
+ createdAt: new Date(),
19
+ updatedAt: new Date(),
20
+ }));
21
+
22
+ export function createScanResourceFixture(
23
+ overrides?: Partial<ScanResource>,
24
+ ): ScanResource {
25
+ return scanResourceFactory.build(overrides);
26
+ }
@@ -17,6 +17,11 @@ export const scanFactory = Factory.define<Scan>(() => ({
17
17
  },
18
18
  createdAt: new Date(),
19
19
  updatedAt: new Date(),
20
+ startedAt: new Date(),
21
+ completedAt: null,
22
+ pagesCompleted: 0,
23
+ pagesFailedPermanent: 0,
24
+ pagesDuplicate: 0,
20
25
  }));
21
26
 
22
27
  export function createScanFixture(overrides?: Partial<Scan>): Scan {
@@ -1,6 +1,7 @@
1
1
  import { Db, ObjectId as MongoObjectId } from "mongodb";
2
2
 
3
3
  export * from "./scans";
4
+ export * from "./scanResources";
4
5
  export * from "./websiteUrls";
5
6
  export * from "./knowledgeDocuments";
6
7
  export { websitalkMongodbClient } from "./mongodb-client";
@@ -40,21 +40,6 @@ describe("db.websitalk.knowledgeDocuments", () => {
40
40
  expect(saved?.createdAt).toBeInstanceOf(Date);
41
41
  expect(saved?.updatedAt).toBeInstanceOf(Date);
42
42
  });
43
-
44
- it("should create a knowledge document with a preset _id", async () => {
45
- const presetId = new (await import("mongodb")).ObjectId();
46
- const id = await createKnowledgeDocument({
47
- _id: presetId,
48
- clientId,
49
- filename: "preset.pdf",
50
- contentType: KNOWLEDGE_CONTENT_TYPES.PDF,
51
- fileType: KNOWLEDGE_FILE_TYPES.PDF,
52
- storagePath: `websitalk/knowledge_files/test/${presetId.toString()}/original.pdf`,
53
- byteSize: 512,
54
- });
55
-
56
- expect(id.toString()).toBe(presetId.toString());
57
- });
58
43
  });
59
44
 
60
45
  describe("getKnowledgeDocumentById", () => {
@@ -160,15 +145,11 @@ describe("db.websitalk.knowledgeDocuments", () => {
160
145
  status: KNOWLEDGE_DOCUMENT_STATUSES.READY,
161
146
  progressPercent: 100,
162
147
  pageCount: 12,
163
- byteSize: 2048,
164
- storagePath: "websitalk/knowledge_files/test/doc-updated.pdf",
165
148
  });
166
149
 
167
150
  expect(updated?.status).toBe(KNOWLEDGE_DOCUMENT_STATUSES.READY);
168
151
  expect(updated?.progressPercent).toBe(100);
169
152
  expect(updated?.pageCount).toBe(12);
170
- expect(updated?.byteSize).toBe(2048);
171
- expect(updated?.storagePath).toBe("websitalk/knowledge_files/test/doc-updated.pdf");
172
153
  expect(updated?.updatedAt.getTime()).toBeGreaterThanOrEqual(
173
154
  before!.updatedAt.getTime(),
174
155
  );
@@ -1,4 +1,4 @@
1
- import { ObjectId as MongoObjectId } from "mongodb";
1
+ import { WithId, ObjectId as MongoObjectId } from "mongodb";
2
2
 
3
3
  import type {
4
4
  KNOWLEDGE_CONTENT_TYPES,
@@ -34,7 +34,7 @@ export type KnowledgeDocument = {
34
34
  updatedAt: Date;
35
35
  };
36
36
 
37
- export type KnowledgeDocumentDoc = KnowledgeDocument & { _id: MongoObjectId };
37
+ export type KnowledgeDocumentDoc = WithId<KnowledgeDocument>;
38
38
 
39
39
  export type CreateKnowledgeDocumentInput = Omit<
40
40
  KnowledgeDocument,