@talkpilot/core-db 1.3.15 → 1.3.17
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 +20 -10
- package/dist/index.d.ts +0 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -1
- package/dist/index.js.map +1 -1
- package/dist/municipal/index.d.ts +1 -1
- package/dist/municipal/index.d.ts.map +1 -1
- package/dist/municipal/tickets/tickets.statistics.getters.d.ts.map +1 -1
- package/dist/municipal/tickets/tickets.statistics.getters.js +2 -6
- package/dist/municipal/tickets/tickets.statistics.getters.js.map +1 -1
- package/dist/talkpilot/calls/calls.constants.d.ts +2 -0
- package/dist/talkpilot/calls/calls.constants.d.ts.map +1 -1
- package/dist/talkpilot/calls/calls.constants.js +3 -1
- package/dist/talkpilot/calls/calls.constants.js.map +1 -1
- package/dist/talkpilot/calls/calls.getters.d.ts +2 -1
- package/dist/talkpilot/calls/calls.getters.d.ts.map +1 -1
- package/dist/talkpilot/calls/calls.getters.js +3 -1
- package/dist/talkpilot/calls/calls.getters.js.map +1 -1
- package/dist/talkpilot/calls/calls.types.d.ts +2 -1
- package/dist/talkpilot/calls/calls.types.d.ts.map +1 -1
- package/dist/talkpilot/calls/dashboard/calls.dashboard.d.ts.map +1 -1
- package/dist/talkpilot/calls/dashboard/calls.dashboard.js +16 -13
- package/dist/talkpilot/calls/dashboard/calls.dashboard.js.map +1 -1
- package/dist/talkpilot/calls/dashboard/calls.dashboard.types.d.ts +2 -2
- package/dist/talkpilot/calls/dashboard/calls.dashboard.types.d.ts.map +1 -1
- package/dist/talkpilot/clientsConfig/clientsConfig.getters.d.ts +0 -3
- package/dist/talkpilot/clientsConfig/clientsConfig.getters.d.ts.map +1 -1
- package/dist/talkpilot/clientsConfig/clientsConfig.getters.js +1 -17
- package/dist/talkpilot/clientsConfig/clientsConfig.getters.js.map +1 -1
- package/dist/talkpilot/clientsConfig/clientsConfig.types.d.ts +2 -1
- package/dist/talkpilot/clientsConfig/clientsConfig.types.d.ts.map +1 -1
- package/dist/test-utils/factories/index.d.ts +0 -1
- package/dist/test-utils/factories/index.d.ts.map +1 -1
- package/dist/test-utils/factories/index.js +0 -1
- package/dist/test-utils/factories/index.js.map +1 -1
- package/dist/test-utils/factories/websitalk/scans.d.ts.map +1 -1
- package/dist/test-utils/factories/websitalk/scans.js +1 -2
- package/dist/test-utils/factories/websitalk/scans.js.map +1 -1
- package/dist/websitalk/index.d.ts +0 -1
- package/dist/websitalk/index.d.ts.map +1 -1
- package/dist/websitalk/index.js +0 -1
- package/dist/websitalk/index.js.map +1 -1
- package/dist/websitalk/scans/index.d.ts +0 -1
- package/dist/websitalk/scans/index.d.ts.map +1 -1
- package/dist/websitalk/scans/index.js +0 -1
- package/dist/websitalk/scans/index.js.map +1 -1
- package/dist/websitalk/scans/scans.constants.d.ts +10 -4
- package/dist/websitalk/scans/scans.constants.d.ts.map +1 -1
- package/dist/websitalk/scans/scans.constants.js +13 -7
- package/dist/websitalk/scans/scans.constants.js.map +1 -1
- package/dist/websitalk/scans/scans.getters.d.ts +3 -10
- package/dist/websitalk/scans/scans.getters.d.ts.map +1 -1
- package/dist/websitalk/scans/scans.getters.js +9 -48
- package/dist/websitalk/scans/scans.getters.js.map +1 -1
- package/dist/websitalk/scans/scans.types.d.ts +8 -2
- package/dist/websitalk/scans/scans.types.d.ts.map +1 -1
- package/dist/websitalk/scans/scans.utils.d.ts +3 -0
- package/dist/websitalk/scans/scans.utils.d.ts.map +1 -0
- package/dist/websitalk/scans/scans.utils.js +7 -0
- package/dist/websitalk/scans/scans.utils.js.map +1 -0
- package/package.json +1 -1
- package/src/index.ts +0 -1
- package/src/municipal/index.ts +1 -1
- package/src/municipal/tickets/tickets.statistics.getters.ts +3 -16
- package/src/talkpilot/calls/__tests__/calls.dashboard.spec.ts +81 -1
- package/src/talkpilot/calls/__tests__/calls.spec.ts +13 -0
- package/src/talkpilot/calls/__tests__/calls.statistics.spec.ts +4 -6
- package/src/talkpilot/calls/calls.constants.ts +3 -0
- package/src/talkpilot/calls/calls.getters.ts +11 -1
- package/src/talkpilot/calls/calls.types.ts +12 -1
- package/src/talkpilot/calls/dashboard/calls.dashboard.ts +16 -13
- package/src/talkpilot/calls/dashboard/calls.dashboard.types.ts +2 -2
- package/src/talkpilot/clientsConfig/__tests__/clientsConfig.getters.spec.ts +5 -57
- package/src/talkpilot/clientsConfig/__tests__/clientsConfig.spec.ts +4 -0
- package/src/talkpilot/clientsConfig/clientsConfig.getters.ts +0 -24
- package/src/talkpilot/clientsConfig/clientsConfig.types.ts +2 -1
- package/src/test-utils/factories/index.ts +0 -1
- package/src/test-utils/factories/websitalk/scans.ts +1 -2
- package/src/websitalk/index.ts +0 -1
- package/src/websitalk/scans/__tests__/scans.spec.ts +26 -121
- package/src/websitalk/scans/index.ts +0 -1
- package/src/websitalk/scans/scans.getters.ts +17 -67
- package/src/websitalk/scans/scans.types.ts +6 -3
- package/tsconfig.json +4 -4
- package/src/test-utils/factories/websitalk/websiteUrls.ts +0 -17
- package/src/websitalk/scans/scans.constants.ts +0 -12
- package/src/websitalk/websiteUrls/__tests__/websiteUrls.spec.ts +0 -77
- package/src/websitalk/websiteUrls/index.ts +0 -2
- package/src/websitalk/websiteUrls/websiteUrls.getters.ts +0 -65
- package/src/websitalk/websiteUrls/websiteUrls.types.ts +0 -13
|
@@ -21,7 +21,7 @@ export type DashboardSummaryMetrics = {
|
|
|
21
21
|
avgDurationSeconds: number;
|
|
22
22
|
timeSavedMinutes: number;
|
|
23
23
|
completedCount: number;
|
|
24
|
-
|
|
24
|
+
inCallCount: number;
|
|
25
25
|
};
|
|
26
26
|
|
|
27
27
|
export type DashboardVisualData = {
|
|
@@ -42,7 +42,7 @@ export type DashboardReportResponse = {
|
|
|
42
42
|
};
|
|
43
43
|
type RawKpiResult = {
|
|
44
44
|
completedCount: number;
|
|
45
|
-
|
|
45
|
+
inCallCount: number;
|
|
46
46
|
answeredDuration: number | null;
|
|
47
47
|
};
|
|
48
48
|
|
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
import { describe, it, expect, beforeEach, afterEach } from "@jest/globals";
|
|
2
2
|
import {
|
|
3
3
|
getClientsConfigCollection,
|
|
4
|
-
getActiveScanId,
|
|
5
|
-
updateActiveScanId,
|
|
6
|
-
clearActiveScanId,
|
|
7
4
|
updateProductConfig,
|
|
8
5
|
} from "../clientsConfig.getters";
|
|
9
6
|
|
|
@@ -26,6 +23,8 @@ describe("updateProductConfig", () => {
|
|
|
26
23
|
websiteTalk: {
|
|
27
24
|
language: "English",
|
|
28
25
|
companyName: "OldCorp",
|
|
26
|
+
defaultBaseUrl: "https://old-url.com",
|
|
27
|
+
logoUrl: "",
|
|
29
28
|
timezone: "UTC",
|
|
30
29
|
},
|
|
31
30
|
},
|
|
@@ -39,7 +38,9 @@ describe("updateProductConfig", () => {
|
|
|
39
38
|
expect(updatedDoc).toBeDefined();
|
|
40
39
|
expect(updatedDoc?.products.websiteTalk?.language).toBe("Hebrew");
|
|
41
40
|
expect(updatedDoc?.products.websiteTalk?.companyName).toBe("TestCorp");
|
|
42
|
-
expect(updatedDoc?.products.websiteTalk?.
|
|
41
|
+
expect(updatedDoc?.products.websiteTalk?.defaultBaseUrl).toBe(
|
|
42
|
+
"https://old-url.com",
|
|
43
|
+
);
|
|
43
44
|
});
|
|
44
45
|
|
|
45
46
|
it("should throw an error if no client config is found", async () => {
|
|
@@ -50,56 +51,3 @@ describe("updateProductConfig", () => {
|
|
|
50
51
|
).rejects.toThrow("No client config found for clientId: invalid-client");
|
|
51
52
|
});
|
|
52
53
|
});
|
|
53
|
-
|
|
54
|
-
describe("activeScanId", () => {
|
|
55
|
-
beforeEach(async () => {
|
|
56
|
-
await getClientsConfigCollection().deleteMany({});
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
afterEach(async () => {
|
|
60
|
-
await getClientsConfigCollection().deleteMany({});
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
it("should return undefined when activeScanId is not set", async () => {
|
|
64
|
-
const clientId = "client-active";
|
|
65
|
-
await getClientsConfigCollection().insertOne({
|
|
66
|
-
clientId,
|
|
67
|
-
products: {
|
|
68
|
-
websiteTalk: { companyName: "X", timezone: "UTC", language: "he" },
|
|
69
|
-
},
|
|
70
|
-
} as any);
|
|
71
|
-
|
|
72
|
-
expect(await getActiveScanId(clientId)).toBeUndefined();
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
it("should persist activeScanId and return it from getActiveScanId", async () => {
|
|
76
|
-
const clientId = "client-active-2";
|
|
77
|
-
await getClientsConfigCollection().insertOne({
|
|
78
|
-
clientId,
|
|
79
|
-
products: {
|
|
80
|
-
websiteTalk: { companyName: "X", timezone: "UTC", language: "he" },
|
|
81
|
-
},
|
|
82
|
-
} as any);
|
|
83
|
-
|
|
84
|
-
await updateActiveScanId(clientId, "scan-abc");
|
|
85
|
-
expect(await getActiveScanId(clientId)).toBe("scan-abc");
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
it("should clear activeScanId and return undefined from getActiveScanId", async () => {
|
|
89
|
-
const clientId = "client-active-3";
|
|
90
|
-
await getClientsConfigCollection().insertOne({
|
|
91
|
-
clientId,
|
|
92
|
-
products: {
|
|
93
|
-
websiteTalk: {
|
|
94
|
-
companyName: "X",
|
|
95
|
-
timezone: "UTC",
|
|
96
|
-
language: "he",
|
|
97
|
-
activeScanId: "scan-old",
|
|
98
|
-
},
|
|
99
|
-
},
|
|
100
|
-
} as any);
|
|
101
|
-
|
|
102
|
-
await clearActiveScanId(clientId);
|
|
103
|
-
expect(await getActiveScanId(clientId)).toBeUndefined();
|
|
104
|
-
});
|
|
105
|
-
});
|
|
@@ -31,9 +31,11 @@ describe("db.clientsConfig", () => {
|
|
|
31
31
|
clientId,
|
|
32
32
|
products: {
|
|
33
33
|
websiteTalk: {
|
|
34
|
+
logoUrl: "https://cdn.example.com/branding/acme/logo.png",
|
|
34
35
|
companyName: "Acme Ltd",
|
|
35
36
|
timezone: "Asia/Jerusalem",
|
|
36
37
|
language: "he",
|
|
38
|
+
defaultBaseUrl: "https://acme.example",
|
|
37
39
|
},
|
|
38
40
|
},
|
|
39
41
|
});
|
|
@@ -46,9 +48,11 @@ describe("db.clientsConfig", () => {
|
|
|
46
48
|
clientId,
|
|
47
49
|
products: {
|
|
48
50
|
websiteTalk: {
|
|
51
|
+
logoUrl: "https://cdn.example.com/branding/acme/logo.png",
|
|
49
52
|
companyName: "Acme Ltd",
|
|
50
53
|
timezone: "Asia/Jerusalem",
|
|
51
54
|
language: "he",
|
|
55
|
+
defaultBaseUrl: "https://acme.example",
|
|
52
56
|
},
|
|
53
57
|
},
|
|
54
58
|
});
|
|
@@ -53,27 +53,3 @@ export async function updateProductConfig<K extends KnownProductKey>(
|
|
|
53
53
|
throw new Error(`No client config found for clientId: ${clientId}`);
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
|
-
|
|
57
|
-
export const getActiveScanId = async (
|
|
58
|
-
clientId: string,
|
|
59
|
-
): Promise<string | undefined> => {
|
|
60
|
-
const cfg = await getClientConfig(clientId);
|
|
61
|
-
return cfg?.products?.websiteTalk?.activeScanId;
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
export const updateActiveScanId = async (
|
|
65
|
-
clientId: string,
|
|
66
|
-
scanId: string,
|
|
67
|
-
): Promise<void> => {
|
|
68
|
-
await updateProductConfig(clientId, "websiteTalk", { activeScanId: scanId });
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
export const clearActiveScanId = async (clientId: string): Promise<void> => {
|
|
72
|
-
const result = await getClientsConfigCollection().updateOne(
|
|
73
|
-
{ clientId },
|
|
74
|
-
{ $unset: { "products.websiteTalk.activeScanId": "" } },
|
|
75
|
-
);
|
|
76
|
-
if (result.matchedCount === 0) {
|
|
77
|
-
throw new Error(`No client config found for clientId: ${clientId}`);
|
|
78
|
-
}
|
|
79
|
-
};
|
|
@@ -58,10 +58,11 @@ export type WebsiteTalkScheduler = {
|
|
|
58
58
|
|
|
59
59
|
/** Product config for the websiteTalk */
|
|
60
60
|
export type WebsiteTalkProduct = {
|
|
61
|
+
logoUrl: string;
|
|
61
62
|
companyName: string;
|
|
62
63
|
timezone: string;
|
|
63
64
|
language: string;
|
|
64
|
-
|
|
65
|
+
defaultBaseUrl: string;
|
|
65
66
|
};
|
|
66
67
|
|
|
67
68
|
// ---------------------------------------------------------------------------
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import { Factory } from "fishery";
|
|
2
2
|
import { faker } from "@faker-js/faker";
|
|
3
|
-
import { ObjectId } from "mongodb";
|
|
4
3
|
import type { Scan } from "../../../websitalk";
|
|
5
4
|
|
|
6
5
|
export const scanFactory = Factory.define<Scan>(() => ({
|
|
7
6
|
clientId: faker.string.uuid(),
|
|
8
|
-
|
|
7
|
+
baseUrl: faker.internet.url(),
|
|
9
8
|
status: "CHECKING",
|
|
10
9
|
activeTasksCount: 0,
|
|
11
10
|
pagesScanned: 0,
|
package/src/websitalk/index.ts
CHANGED
|
@@ -6,12 +6,7 @@ import {
|
|
|
6
6
|
countScans,
|
|
7
7
|
createScanDoc,
|
|
8
8
|
updateScanDoc,
|
|
9
|
-
|
|
10
|
-
getScansByWebsiteUrl,
|
|
11
|
-
getLatestScanByWebsiteUrl,
|
|
12
|
-
getInProgressScanByWebsiteUrl,
|
|
13
|
-
getLatestCompletedScanByWebsiteUrl,
|
|
14
|
-
deleteScansByWebsiteUrl,
|
|
9
|
+
getActiveScanStatus,
|
|
15
10
|
} from "../scans.getters";
|
|
16
11
|
import { createScanFixture } from "../../../test-utils/factories";
|
|
17
12
|
import { ObjectId } from "mongodb";
|
|
@@ -42,7 +37,7 @@ describe("db.websitalk.scans", () => {
|
|
|
42
37
|
expect(result.length).toBe(1);
|
|
43
38
|
expect(result[0]).toMatchObject({
|
|
44
39
|
clientId,
|
|
45
|
-
|
|
40
|
+
baseUrl: scanInside.baseUrl,
|
|
46
41
|
status: "COMPLETED",
|
|
47
42
|
});
|
|
48
43
|
expect(result[0]).toHaveProperty("siteChrome");
|
|
@@ -88,12 +83,12 @@ describe("db.websitalk.scans", () => {
|
|
|
88
83
|
clientId,
|
|
89
84
|
new Date("2023-05-01"),
|
|
90
85
|
new Date("2023-05-31"),
|
|
91
|
-
["clientId", "
|
|
86
|
+
["clientId", "baseUrl", "status"],
|
|
92
87
|
);
|
|
93
88
|
|
|
94
89
|
expect(result).toMatchObject({
|
|
95
90
|
clientId,
|
|
96
|
-
|
|
91
|
+
baseUrl: scan.baseUrl,
|
|
97
92
|
status: "COMPLETED",
|
|
98
93
|
});
|
|
99
94
|
expect(result).toHaveProperty("_id");
|
|
@@ -115,7 +110,7 @@ describe("db.websitalk.scans", () => {
|
|
|
115
110
|
const count = await countScans({ clientId });
|
|
116
111
|
|
|
117
112
|
expect(result.length).toBe(1);
|
|
118
|
-
expect(result[0].
|
|
113
|
+
expect(result[0].baseUrl).toBe(completed.baseUrl);
|
|
119
114
|
expect(count).toBe(2);
|
|
120
115
|
});
|
|
121
116
|
});
|
|
@@ -123,15 +118,15 @@ describe("db.websitalk.scans", () => {
|
|
|
123
118
|
describe("createScanDoc()", () => {
|
|
124
119
|
it("should insert a scan with default values for the rest of the fields", async () => {
|
|
125
120
|
const clientId = "createClient";
|
|
126
|
-
const
|
|
121
|
+
const baseUrl = "https://example.com";
|
|
127
122
|
|
|
128
|
-
const { insertedId } = await createScanDoc({ clientId,
|
|
123
|
+
const { insertedId } = await createScanDoc({ clientId, baseUrl });
|
|
129
124
|
|
|
130
125
|
const result = await getScansCollection().findOne({ _id: insertedId });
|
|
131
126
|
|
|
132
127
|
expect(result).toMatchObject({
|
|
133
128
|
clientId,
|
|
134
|
-
|
|
129
|
+
baseUrl,
|
|
135
130
|
status: "CHECKING",
|
|
136
131
|
activeTasksCount: 0,
|
|
137
132
|
pagesScanned: 0,
|
|
@@ -171,143 +166,53 @@ describe("db.websitalk.scans", () => {
|
|
|
171
166
|
});
|
|
172
167
|
});
|
|
173
168
|
|
|
174
|
-
describe("
|
|
175
|
-
|
|
169
|
+
describe("getActiveScanStatus()", () => {
|
|
170
|
+
it("should return isActive: true with the status when the latest scan is still in progress", async () => {
|
|
171
|
+
const clientId = "activeClient";
|
|
176
172
|
|
|
177
|
-
it("should return website scan history newest first", async () => {
|
|
178
173
|
await getScansCollection().insertMany([
|
|
179
174
|
createScanFixture({
|
|
180
|
-
|
|
181
|
-
status: "COMPLETED",
|
|
182
|
-
createdAt: new Date("2023-05-01"),
|
|
183
|
-
}),
|
|
184
|
-
createScanFixture({
|
|
185
|
-
websiteUrlId,
|
|
186
|
-
status: "COMPLETED",
|
|
187
|
-
createdAt: new Date("2023-05-15"),
|
|
188
|
-
}),
|
|
189
|
-
]);
|
|
190
|
-
|
|
191
|
-
const scans = await getScansByWebsiteUrl(websiteUrlId);
|
|
192
|
-
expect(scans).toHaveLength(2);
|
|
193
|
-
expect(scans[0].createdAt.getTime()).toBeGreaterThan(
|
|
194
|
-
scans[1].createdAt.getTime(),
|
|
195
|
-
);
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
it("should return the newest scan for a website", async () => {
|
|
199
|
-
await getScansCollection().insertMany([
|
|
200
|
-
createScanFixture({
|
|
201
|
-
websiteUrlId,
|
|
202
|
-
status: "COMPLETED",
|
|
203
|
-
createdAt: new Date("2023-05-01"),
|
|
204
|
-
}),
|
|
205
|
-
createScanFixture({
|
|
206
|
-
websiteUrlId,
|
|
207
|
-
status: "FAILED",
|
|
208
|
-
createdAt: new Date("2023-05-15"),
|
|
209
|
-
}),
|
|
210
|
-
]);
|
|
211
|
-
|
|
212
|
-
const latest = await getLatestScanByWebsiteUrl(websiteUrlId);
|
|
213
|
-
expect(latest?.status).toBe("FAILED");
|
|
214
|
-
});
|
|
215
|
-
|
|
216
|
-
it("should return in-progress scan and return null when only completed exists", async () => {
|
|
217
|
-
await getScansCollection().insertMany([
|
|
218
|
-
createScanFixture({
|
|
219
|
-
websiteUrlId,
|
|
175
|
+
clientId,
|
|
220
176
|
status: "COMPLETED",
|
|
221
177
|
createdAt: new Date("2023-05-01"),
|
|
222
178
|
}),
|
|
223
179
|
createScanFixture({
|
|
224
|
-
|
|
180
|
+
clientId,
|
|
225
181
|
status: "SCANNING",
|
|
226
182
|
createdAt: new Date("2023-05-15"),
|
|
227
183
|
}),
|
|
228
184
|
]);
|
|
229
185
|
|
|
230
|
-
const
|
|
231
|
-
expect(inProgressScan?.status).toBe("SCANNING");
|
|
186
|
+
const result = await getActiveScanStatus(clientId);
|
|
232
187
|
|
|
233
|
-
|
|
234
|
-
await getScansCollection().insertOne(
|
|
235
|
-
createScanFixture({
|
|
236
|
-
websiteUrlId,
|
|
237
|
-
status: "COMPLETED",
|
|
238
|
-
createdAt: new Date("2023-05-20"),
|
|
239
|
-
}),
|
|
240
|
-
);
|
|
241
|
-
|
|
242
|
-
expect(await getInProgressScanByWebsiteUrl(websiteUrlId)).toBeNull();
|
|
188
|
+
expect(result).toEqual({ isActive: true, status: "SCANNING" });
|
|
243
189
|
});
|
|
244
190
|
|
|
245
|
-
it("should return
|
|
246
|
-
const clientId = "
|
|
191
|
+
it("should return isActive: false when the latest scan finished (COMPLETED / FAILED)", async () => {
|
|
192
|
+
const clientId = "finishedClient";
|
|
193
|
+
|
|
247
194
|
await getScansCollection().insertMany([
|
|
248
195
|
createScanFixture({
|
|
249
196
|
clientId,
|
|
250
|
-
websiteUrlId: "website-a",
|
|
251
197
|
status: "SCANNING",
|
|
198
|
+
createdAt: new Date("2023-05-01"),
|
|
252
199
|
}),
|
|
253
200
|
createScanFixture({
|
|
254
201
|
clientId,
|
|
255
|
-
websiteUrlId: "website-b",
|
|
256
|
-
status: "PROCESSING",
|
|
257
|
-
}),
|
|
258
|
-
createScanFixture({
|
|
259
|
-
clientId,
|
|
260
|
-
websiteUrlId: "website-c",
|
|
261
|
-
status: "COMPLETED",
|
|
262
|
-
}),
|
|
263
|
-
]);
|
|
264
|
-
|
|
265
|
-
const scans = await findInProgressScansByClient(clientId);
|
|
266
|
-
expect(scans).toHaveLength(2);
|
|
267
|
-
expect(scans.map((s) => s.websiteUrlId).sort()).toEqual([
|
|
268
|
-
"website-a",
|
|
269
|
-
"website-b",
|
|
270
|
-
]);
|
|
271
|
-
});
|
|
272
|
-
});
|
|
273
|
-
|
|
274
|
-
describe("website delete helpers", () => {
|
|
275
|
-
it("should return the newest completed scan only", async () => {
|
|
276
|
-
const websiteUrlId = new ObjectId().toString();
|
|
277
|
-
await getScansCollection().insertMany([
|
|
278
|
-
createScanFixture({
|
|
279
|
-
websiteUrlId,
|
|
280
202
|
status: "FAILED",
|
|
281
|
-
createdAt: new Date("2023-05-
|
|
282
|
-
}),
|
|
283
|
-
createScanFixture({
|
|
284
|
-
websiteUrlId,
|
|
285
|
-
status: "COMPLETED",
|
|
286
|
-
createdAt: new Date("2023-05-20"),
|
|
287
|
-
}),
|
|
288
|
-
createScanFixture({
|
|
289
|
-
websiteUrlId,
|
|
290
|
-
status: "COMPLETED",
|
|
291
|
-
createdAt: new Date("2023-05-22"),
|
|
203
|
+
createdAt: new Date("2023-05-15"),
|
|
292
204
|
}),
|
|
293
205
|
]);
|
|
294
206
|
|
|
295
|
-
const
|
|
296
|
-
|
|
297
|
-
expect(
|
|
207
|
+
const result = await getActiveScanStatus(clientId);
|
|
208
|
+
|
|
209
|
+
expect(result).toEqual({ isActive: false });
|
|
298
210
|
});
|
|
299
211
|
|
|
300
|
-
it("should
|
|
301
|
-
const
|
|
302
|
-
await getScansCollection().insertMany([
|
|
303
|
-
createScanFixture({ websiteUrlId }),
|
|
304
|
-
createScanFixture({ websiteUrlId }),
|
|
305
|
-
createScanFixture({ websiteUrlId: new ObjectId().toString() }),
|
|
306
|
-
]);
|
|
212
|
+
it("should return isActive: false when the client has no scans", async () => {
|
|
213
|
+
const result = await getActiveScanStatus("noScansClient");
|
|
307
214
|
|
|
308
|
-
|
|
309
|
-
expect(deleted).toBe(2);
|
|
310
|
-
expect(await countScans({ websiteUrlId })).toBe(0);
|
|
215
|
+
expect(result).toEqual({ isActive: false });
|
|
311
216
|
});
|
|
312
217
|
});
|
|
313
218
|
});
|
|
@@ -4,12 +4,12 @@ import {
|
|
|
4
4
|
applyQueryOptions,
|
|
5
5
|
QueryOptions,
|
|
6
6
|
} from "../../talkpilot/utils/query.utils";
|
|
7
|
-
import type {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
} from "./scans.
|
|
7
|
+
import type {
|
|
8
|
+
ActiveScanStatus,
|
|
9
|
+
Scan,
|
|
10
|
+
ScanDoc,
|
|
11
|
+
ScanStatus,
|
|
12
|
+
} from "./scans.types";
|
|
13
13
|
|
|
14
14
|
export const getScansCollection = (): Collection<Scan> => {
|
|
15
15
|
return getDb().collection<Scan>("scans");
|
|
@@ -57,12 +57,11 @@ export const getScanFieldsByClientAndDateRange = <K extends keyof Scan>(
|
|
|
57
57
|
};
|
|
58
58
|
|
|
59
59
|
export const createScanDoc = (
|
|
60
|
-
scan: Pick<Scan, "clientId" | "
|
|
61
|
-
Partial<Pick<Scan, "notifyPhoneNumber">>,
|
|
60
|
+
scan: Pick<Scan, "clientId" | "baseUrl" | "notifyPhoneNumber">,
|
|
62
61
|
) => {
|
|
63
62
|
return getScansCollection().insertOne({
|
|
64
63
|
...scan,
|
|
65
|
-
status:
|
|
64
|
+
status: "CHECKING",
|
|
66
65
|
activeTasksCount: 0,
|
|
67
66
|
pagesScanned: 0,
|
|
68
67
|
pagesSkipped: 0,
|
|
@@ -73,48 +72,21 @@ export const createScanDoc = (
|
|
|
73
72
|
});
|
|
74
73
|
};
|
|
75
74
|
|
|
76
|
-
|
|
77
|
-
return getScansCollection().findOne({ _id: new ObjectId(id) });
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
export const getScansByWebsiteUrl = async (
|
|
81
|
-
websiteUrlId: string,
|
|
82
|
-
): Promise<ScanDoc[]> => {
|
|
83
|
-
return getScansCollection()
|
|
84
|
-
.find({ websiteUrlId })
|
|
85
|
-
.sort({ createdAt: -1 })
|
|
86
|
-
.toArray();
|
|
87
|
-
};
|
|
75
|
+
const FINISHED_SCAN_STATUSES: ScanStatus[] = ["COMPLETED", "FAILED"];
|
|
88
76
|
|
|
89
|
-
export const
|
|
90
|
-
|
|
91
|
-
): Promise<
|
|
92
|
-
|
|
93
|
-
{
|
|
77
|
+
export const getActiveScanStatus = async (
|
|
78
|
+
clientId: string,
|
|
79
|
+
): Promise<ActiveScanStatus> => {
|
|
80
|
+
const latestScan = await getScansCollection().findOne(
|
|
81
|
+
{ clientId },
|
|
94
82
|
{ sort: { createdAt: -1 } },
|
|
95
83
|
);
|
|
96
|
-
};
|
|
97
84
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
): Promise<ScanDoc | null> => {
|
|
101
|
-
const latestScan = await getLatestScanByWebsiteUrl(websiteUrlId);
|
|
102
|
-
if (!latestScan || !isInProgressScan(latestScan)) {
|
|
103
|
-
return null;
|
|
85
|
+
if (!latestScan || FINISHED_SCAN_STATUSES.includes(latestScan.status)) {
|
|
86
|
+
return { isActive: false };
|
|
104
87
|
}
|
|
105
|
-
return latestScan;
|
|
106
|
-
};
|
|
107
88
|
|
|
108
|
-
|
|
109
|
-
clientId: string,
|
|
110
|
-
): Promise<ScanDoc[]> => {
|
|
111
|
-
return getScansCollection()
|
|
112
|
-
.find({
|
|
113
|
-
clientId,
|
|
114
|
-
status: { $nin: FINISHED_SCAN_STATUSES },
|
|
115
|
-
})
|
|
116
|
-
.sort({ createdAt: -1 })
|
|
117
|
-
.toArray();
|
|
89
|
+
return { isActive: true, status: latestScan.status };
|
|
118
90
|
};
|
|
119
91
|
|
|
120
92
|
export const updateScanDoc = async (
|
|
@@ -139,25 +111,3 @@ export const findScansByQuery = async (
|
|
|
139
111
|
export const countScans = async (query: Filter<Scan>): Promise<number> => {
|
|
140
112
|
return getScansCollection().countDocuments(query);
|
|
141
113
|
};
|
|
142
|
-
|
|
143
|
-
export const hasScansForWebsiteUrl = async (
|
|
144
|
-
websiteUrlId: string,
|
|
145
|
-
): Promise<boolean> => {
|
|
146
|
-
return (await countScans({ websiteUrlId })) > 0;
|
|
147
|
-
};
|
|
148
|
-
|
|
149
|
-
export const getLatestCompletedScanByWebsiteUrl = async (
|
|
150
|
-
websiteUrlId: string,
|
|
151
|
-
): Promise<ScanDoc | null> => {
|
|
152
|
-
return getScansCollection().findOne(
|
|
153
|
-
{ websiteUrlId, status: "COMPLETED" },
|
|
154
|
-
{ sort: { createdAt: -1 } },
|
|
155
|
-
);
|
|
156
|
-
};
|
|
157
|
-
|
|
158
|
-
export const deleteScansByWebsiteUrl = async (
|
|
159
|
-
websiteUrlId: string,
|
|
160
|
-
): Promise<number> => {
|
|
161
|
-
const result = await getScansCollection().deleteMany({ websiteUrlId });
|
|
162
|
-
return result.deletedCount;
|
|
163
|
-
};
|
|
@@ -6,8 +6,11 @@ export type ScanStatus =
|
|
|
6
6
|
| "SCANNING"
|
|
7
7
|
| "PROCESSING"
|
|
8
8
|
| "COMPLETED"
|
|
9
|
-
| "FAILED"
|
|
10
|
-
|
|
9
|
+
| "FAILED";
|
|
10
|
+
|
|
11
|
+
export type ActiveScanStatus =
|
|
12
|
+
| { isActive: true; status: ScanStatus }
|
|
13
|
+
| { isActive: false };
|
|
11
14
|
|
|
12
15
|
export type SiteChromeEntry = {
|
|
13
16
|
url: string;
|
|
@@ -35,7 +38,7 @@ export type SkippedEntry = {
|
|
|
35
38
|
|
|
36
39
|
export type Scan = {
|
|
37
40
|
clientId: string;
|
|
38
|
-
|
|
41
|
+
baseUrl: string;
|
|
39
42
|
notifyPhoneNumber?: string | null;
|
|
40
43
|
status: ScanStatus;
|
|
41
44
|
activeTasksCount: number;
|
package/tsconfig.json
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"compilerOptions": {
|
|
3
3
|
"target": "ES2022",
|
|
4
|
-
"module": "
|
|
5
|
-
"moduleResolution": "
|
|
6
|
-
"isolatedModules": true,
|
|
4
|
+
"module": "CommonJS",
|
|
5
|
+
"moduleResolution": "node",
|
|
7
6
|
"lib": ["ES2022"],
|
|
8
7
|
"declaration": true,
|
|
9
8
|
"declarationMap": true,
|
|
@@ -16,7 +15,8 @@
|
|
|
16
15
|
"skipLibCheck": true,
|
|
17
16
|
"resolveJsonModule": true,
|
|
18
17
|
"experimentalDecorators": true,
|
|
19
|
-
"emitDecoratorMetadata": true
|
|
18
|
+
"emitDecoratorMetadata": true,
|
|
19
|
+
"baseUrl": "."
|
|
20
20
|
},
|
|
21
21
|
"include": ["src/**/*"],
|
|
22
22
|
"exclude": ["node_modules", "dist", "test", "**/*.test.ts", "**/__tests__/*", "**/*.spec.ts"]
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { Factory } from "fishery";
|
|
2
|
-
import { faker } from "@faker-js/faker";
|
|
3
|
-
import type { WebsiteUrl } from "../../../websitalk/websiteUrls";
|
|
4
|
-
|
|
5
|
-
export const websiteUrlFactory = Factory.define<WebsiteUrl>(() => ({
|
|
6
|
-
clientId: faker.string.uuid(),
|
|
7
|
-
name: faker.company.name(),
|
|
8
|
-
baseUrl: faker.internet.url(),
|
|
9
|
-
createdAt: new Date(),
|
|
10
|
-
updatedAt: new Date(),
|
|
11
|
-
}));
|
|
12
|
-
|
|
13
|
-
export function createWebsiteUrlFixture(
|
|
14
|
-
overrides?: Partial<WebsiteUrl>,
|
|
15
|
-
): WebsiteUrl {
|
|
16
|
-
return websiteUrlFactory.build(overrides);
|
|
17
|
-
}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import type { Scan, ScanStatus } from "./scans.types";
|
|
2
|
-
|
|
3
|
-
export const INITIAL_SCAN_STATUS = "CHECKING" as const satisfies ScanStatus;
|
|
4
|
-
|
|
5
|
-
export const FINISHED_SCAN_STATUSES: ScanStatus[] = [
|
|
6
|
-
"COMPLETED",
|
|
7
|
-
"FAILED",
|
|
8
|
-
"OVERWRITTEN",
|
|
9
|
-
];
|
|
10
|
-
|
|
11
|
-
export const isInProgressScan = (scan: Scan): boolean =>
|
|
12
|
-
!FINISHED_SCAN_STATUSES.includes(scan.status);
|