@j3r3mcdev/oast-server 1.0.0 → 1.1.0
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/.github/workflows/ci.yml +29 -29
- package/.github/workflows/publish.yml +31 -31
- package/README.md +192 -192
- package/dist/api/controllers/__tests__/tasks.controller.test.d.ts +1 -0
- package/dist/api/controllers/events.controller.d.ts +6 -0
- package/dist/api/controllers/health.controller.d.ts +4 -0
- package/dist/api/controllers/index.d.ts +0 -0
- package/dist/api/controllers/tasks.controller.d.ts +40 -0
- package/dist/api/dto/__tests__/create-task.dto.test.d.ts +1 -0
- package/dist/api/dto/__tests__/filter-tasks.dto.test.d.ts +1 -0
- package/dist/api/dto/create-task.dto.d.ts +9 -0
- package/dist/api/dto/filter-tasks.dto.d.ts +8 -0
- package/dist/api/services/__tests__/events.service.test.d.ts +1 -0
- package/dist/api/services/__tests__/tasks.service.test.d.ts +1 -0
- package/dist/api/services/events.service.d.ts +8 -0
- package/dist/api/services/tasks.service.d.ts +22 -0
- package/dist/api/sse/events.stream.d.ts +25 -0
- package/dist/config/constants.d.ts +0 -0
- package/dist/config/env.d.ts +0 -0
- package/dist/core/__tests__/core-router.test.d.ts +1 -0
- package/dist/core/__tests__/core-server.test.d.ts +1 -0
- package/dist/core/__tests__/event.normalizer.test.d.ts +1 -0
- package/dist/core/__tests__/event.router.test.d.ts +1 -0
- package/dist/core/__tests__/logger.test.d.ts +1 -0
- package/dist/core/__tests__/storage-manager.test.d.ts +1 -0
- package/dist/core/event.normalizer.d.ts +10 -0
- package/dist/core/event.router.d.ts +7 -0
- package/dist/core/http/__tests__/adapter-node.test.d.ts +1 -0
- package/dist/core/http/__tests__/body-parser-multipart.test.d.ts +1 -0
- package/dist/core/http/__tests__/body-parser-raw.test.d.ts +1 -0
- package/dist/core/http/__tests__/body-parser-text.test.d.ts +1 -0
- package/dist/core/http/__tests__/compile-path.test.d.ts +1 -0
- package/dist/core/http/__tests__/middleware-pipeline.test.d.ts +1 -0
- package/dist/core/http/__tests__/request.test.d.ts +1 -0
- package/dist/core/http/__tests__/response.test.d.ts +1 -0
- package/dist/core/http/__tests__/router-match.test.d.ts +1 -0
- package/dist/core/http/adapter-node.d.ts +8 -0
- package/dist/core/http/buildRequest.d.ts +2 -0
- package/dist/core/http/compile-path.d.ts +13 -0
- package/dist/core/http/errors.d.ts +9 -0
- package/dist/core/http/http-server.d.ts +7 -0
- package/dist/core/http/index.d.ts +0 -0
- package/dist/core/http/main.d.ts +0 -0
- package/dist/core/http/middleware.d.ts +12 -0
- package/dist/core/http/request.d.ts +35 -0
- package/dist/core/http/response.d.ts +17 -0
- package/dist/core/http/router.d.ts +28 -0
- package/dist/core/http/utils.d.ts +0 -0
- package/dist/core/id-generator.d.ts +3 -0
- package/dist/core/logger.d.ts +29 -0
- package/dist/core/router.d.ts +16 -0
- package/dist/core/server.d.ts +25 -0
- package/dist/core/storage.d.ts +24 -0
- package/dist/index.d.ts +1 -0
- package/dist/listeners/api/__tests__/api.controller.test.d.ts +1 -0
- package/dist/listeners/api/__tests__/api.extractor.test.d.ts +1 -0
- package/dist/listeners/api/__tests__/api.listener.test.d.ts +1 -0
- package/dist/listeners/api/__tests__/api.routes.test.d.ts +1 -0
- package/dist/listeners/api/__tests__/api.sse.test.d.ts +1 -0
- package/dist/listeners/api/api.controllers.d.ts +9 -0
- package/dist/listeners/api/api.extractor.d.ts +5 -0
- package/dist/listeners/api/api.listener.d.ts +17 -0
- package/dist/listeners/api/api.routes.d.ts +5 -0
- package/dist/listeners/api/api.sse.d.ts +10 -0
- package/dist/listeners/dns/__tests__/dns.test.d.ts +1 -0
- package/dist/listeners/dns/dns.extractor.d.ts +8 -0
- package/dist/listeners/dns/dns.listener.d.ts +15 -0
- package/dist/listeners/http/__tests__/http.extractor.test.d.ts +1 -0
- package/dist/listeners/http/__tests__/http.listener.test.d.ts +1 -0
- package/dist/listeners/http/http.extractor.d.ts +11 -0
- package/dist/listeners/http/http.listener.d.ts +17 -0
- package/dist/listeners/listener.interface.d.ts +4 -0
- package/dist/listeners/smtp/__tests__/smtp.extractor.test.d.ts +1 -0
- package/dist/listeners/smtp/__tests__/smtp.listener.test.d.ts +1 -0
- package/dist/listeners/smtp/smtp.extractor.d.ts +12 -0
- package/dist/listeners/smtp/smtp.listener.d.ts +13 -0
- package/dist/listeners/ssrf/__tests__/ssrf.extractor.test.d.ts +1 -0
- package/dist/listeners/ssrf/__tests__/ssrf.listener.test.d.ts +1 -0
- package/dist/listeners/ssrf/__tests__/ssrf.listener.test.js +4 -16
- package/dist/listeners/ssrf/ssrf.extractor.d.ts +10 -0
- package/dist/listeners/ssrf/ssrf.listener.d.ts +8 -0
- package/dist/listeners/tcp/tcp.extractor.d.ts +4 -0
- package/dist/listeners/tcp/tcp.listener.d.ts +18 -0
- package/dist/listeners/webhook/__tests__/webhook.extractor.test.d.ts +1 -0
- package/dist/listeners/webhook/__tests__/webhook.listener.test.d.ts +1 -0
- package/dist/listeners/webhook/webhook.extractor.d.ts +8 -0
- package/dist/listeners/webhook/webhook.listener.d.ts +8 -0
- package/dist/listeners/websocket/__tests__/websocket.extractor.test.d.ts +1 -0
- package/dist/listeners/websocket/__tests__/websocket.listener.test.d.ts +1 -0
- package/dist/listeners/websocket/websocket.extractor.d.ts +10 -0
- package/dist/listeners/websocket/websocket.listener.d.ts +8 -0
- package/dist/storage-adapters/adapters/__tests__/memory.storage.test.d.ts +1 -0
- package/dist/storage-adapters/adapters/memory.storage.d.ts +21 -0
- package/dist/storage-adapters/adapters/redis.storage.d.ts +0 -0
- package/dist/storage-adapters/adapters/sqlite.storage.d.ts +0 -0
- package/dist/storage-adapters/storage.interface.d.ts +18 -0
- package/dist/types/event.types.d.ts +118 -0
- package/dist/utils/token.d.ts +0 -0
- package/jest.config.js +14 -11
- package/package.json +45 -45
- package/sadmin list shadows +9 -9
- package/src/api/controllers/__tests__/tasks.controller.test.ts +74 -74
- package/src/api/controllers/events.controller.ts +10 -10
- package/src/api/controllers/health.controller.ts +7 -7
- package/src/api/controllers/tasks.controller.ts +41 -41
- package/src/api/dto/__tests__/create-task.dto.test.ts +41 -41
- package/src/api/dto/__tests__/filter-tasks.dto.test.ts +35 -35
- package/src/api/dto/create-task.dto.ts +33 -33
- package/src/api/dto/filter-tasks.dto.ts +33 -33
- package/src/api/services/__tests__/events.service.test.ts +41 -41
- package/src/api/services/__tests__/tasks.service.test.ts +41 -41
- package/src/api/services/events.service.ts +17 -17
- package/src/api/services/tasks.service.ts +79 -79
- package/src/api/sse/events.stream.ts +90 -90
- package/src/core/__tests__/core-router.test.ts +30 -30
- package/src/core/__tests__/core-server.test.ts +44 -44
- package/src/core/__tests__/event.normalizer.test.ts +56 -56
- package/src/core/__tests__/event.router.test.ts +89 -89
- package/src/core/__tests__/logger.test.ts +32 -32
- package/src/core/__tests__/storage-manager.test.ts +74 -74
- package/src/core/event.normalizer.ts +147 -147
- package/src/core/event.router.ts +13 -13
- package/src/core/http/__tests__/adapter-node.test.ts +52 -52
- package/src/core/http/__tests__/body-parser-multipart.test.ts +41 -41
- package/src/core/http/__tests__/body-parser-raw.test.ts +28 -28
- package/src/core/http/__tests__/body-parser-text.test.ts +28 -28
- package/src/core/http/__tests__/compile-path.test.ts +39 -39
- package/src/core/http/__tests__/middleware-pipeline.test.ts +51 -51
- package/src/core/http/__tests__/request.test.ts +34 -34
- package/src/core/http/__tests__/response.test.ts +35 -35
- package/src/core/http/__tests__/router-match.test.ts +171 -171
- package/src/core/http/adapter-node.ts +51 -51
- package/src/core/http/buildRequest.ts +18 -18
- package/src/core/http/compile-path.ts +32 -32
- package/src/core/http/errors.ts +37 -37
- package/src/core/http/http-server.ts +52 -52
- package/src/core/http/middleware.ts +160 -160
- package/src/core/http/request.ts +55 -55
- package/src/core/http/response.ts +93 -93
- package/src/core/http/router.ts +138 -138
- package/src/core/id-generator.ts +8 -8
- package/src/core/logger.ts +113 -113
- package/src/core/router.ts +44 -44
- package/src/core/server.ts +85 -85
- package/src/core/storage.ts +64 -64
- package/src/index.ts +89 -89
- package/src/listeners/api/__tests__/api.controller.test.ts +116 -116
- package/src/listeners/api/__tests__/api.extractor.test.ts +46 -46
- package/src/listeners/api/__tests__/api.listener.test.ts +82 -82
- package/src/listeners/api/__tests__/api.routes.test.ts +155 -155
- package/src/listeners/api/__tests__/api.sse.test.ts +105 -105
- package/src/listeners/api/api.controllers.ts +67 -67
- package/src/listeners/api/api.extractor.ts +43 -43
- package/src/listeners/api/api.listener.ts +50 -50
- package/src/listeners/api/api.routes.ts +76 -76
- package/src/listeners/api/api.sse.ts +38 -38
- package/src/listeners/dns/__tests__/dns.test.ts +118 -118
- package/src/listeners/dns/dns.extractor.ts +14 -14
- package/src/listeners/dns/dns.listener.ts +61 -61
- package/src/listeners/http/__tests__/http.extractor.test.ts +59 -59
- package/src/listeners/http/__tests__/http.listener.test.ts +133 -133
- package/src/listeners/http/http.extractor.ts +15 -15
- package/src/listeners/http/http.listener.ts +110 -110
- package/src/listeners/listener.interface.ts +4 -4
- package/src/listeners/smtp/__tests__/smtp.extractor.test.ts +69 -69
- package/src/listeners/smtp/__tests__/smtp.listener.test.ts +150 -150
- package/src/listeners/smtp/smtp.extractor.ts +18 -18
- package/src/listeners/smtp/smtp.listener.ts +60 -60
- package/src/listeners/ssrf/__tests__/ssrf.extractor.test.ts +41 -41
- package/src/listeners/ssrf/__tests__/ssrf.listener.test.ts +87 -98
- package/src/listeners/ssrf/ssrf.extractor.ts +14 -14
- package/src/listeners/ssrf/ssrf.listener.ts +37 -37
- package/src/listeners/tcp/tcp.extractor.ts +16 -16
- package/src/listeners/tcp/tcp.listener.ts +61 -61
- package/src/listeners/webhook/__tests__/webhook.extractor.test.ts +35 -35
- package/src/listeners/webhook/__tests__/webhook.listener.test.ts +122 -122
- package/src/listeners/webhook/webhook.extractor.ts +12 -12
- package/src/listeners/webhook/webhook.listener.ts +58 -58
- package/src/listeners/websocket/__tests__/websocket.extractor.test.ts +33 -33
- package/src/listeners/websocket/__tests__/websocket.listener.test.ts +90 -90
- package/src/listeners/websocket/websocket.extractor.ts +11 -11
- package/src/listeners/websocket/websocket.listener.ts +40 -40
- package/src/storage-adapters/adapters/__tests__/memory.storage.test.ts +75 -75
- package/src/storage-adapters/adapters/memory.storage.ts +64 -64
- package/src/storage-adapters/storage.interface.ts +26 -26
- package/src/types/event.types.ts +147 -147
- package/tsconfig.json +21 -15
|
@@ -1,35 +1,35 @@
|
|
|
1
|
-
import { describe, it, expect } from "@jest/globals";
|
|
2
|
-
import { FilterTasksDtoValidator } from "../filter-tasks.dto";
|
|
3
|
-
|
|
4
|
-
describe("FilterTasksDtoValidator", () => {
|
|
5
|
-
it("valide un query correct", () => {
|
|
6
|
-
const dto = FilterTasksDtoValidator.validate({
|
|
7
|
-
status: "running",
|
|
8
|
-
limit: "10",
|
|
9
|
-
type: "crawl:api",
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
expect(dto).toEqual({
|
|
13
|
-
status: "running",
|
|
14
|
-
limit: 10,
|
|
15
|
-
type: "crawl:api",
|
|
16
|
-
});
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
it("rejette un status invalide", () => {
|
|
20
|
-
expect(() =>
|
|
21
|
-
FilterTasksDtoValidator.validate({ status: "unknown" }),
|
|
22
|
-
).toThrow("Invalid status");
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
it("rejette un limit invalide", () => {
|
|
26
|
-
expect(() => FilterTasksDtoValidator.validate({ limit: "-5" })).toThrow(
|
|
27
|
-
"Invalid limit",
|
|
28
|
-
);
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
it("accepte un query vide", () => {
|
|
32
|
-
const dto = FilterTasksDtoValidator.validate({});
|
|
33
|
-
expect(dto).toEqual({});
|
|
34
|
-
});
|
|
35
|
-
});
|
|
1
|
+
import { describe, it, expect } from "@jest/globals";
|
|
2
|
+
import { FilterTasksDtoValidator } from "../filter-tasks.dto";
|
|
3
|
+
|
|
4
|
+
describe("FilterTasksDtoValidator", () => {
|
|
5
|
+
it("valide un query correct", () => {
|
|
6
|
+
const dto = FilterTasksDtoValidator.validate({
|
|
7
|
+
status: "running",
|
|
8
|
+
limit: "10",
|
|
9
|
+
type: "crawl:api",
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
expect(dto).toEqual({
|
|
13
|
+
status: "running",
|
|
14
|
+
limit: 10,
|
|
15
|
+
type: "crawl:api",
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it("rejette un status invalide", () => {
|
|
20
|
+
expect(() =>
|
|
21
|
+
FilterTasksDtoValidator.validate({ status: "unknown" }),
|
|
22
|
+
).toThrow("Invalid status");
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it("rejette un limit invalide", () => {
|
|
26
|
+
expect(() => FilterTasksDtoValidator.validate({ limit: "-5" })).toThrow(
|
|
27
|
+
"Invalid limit",
|
|
28
|
+
);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("accepte un query vide", () => {
|
|
32
|
+
const dto = FilterTasksDtoValidator.validate({});
|
|
33
|
+
expect(dto).toEqual({});
|
|
34
|
+
});
|
|
35
|
+
});
|
|
@@ -1,33 +1,33 @@
|
|
|
1
|
-
export interface CreateTaskDto {
|
|
2
|
-
type: string;
|
|
3
|
-
payload: Record<string, any>;
|
|
4
|
-
priority?: "low" | "normal" | "high";
|
|
5
|
-
metadata?: Record<string, any>;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
export class CreateTaskDtoValidator {
|
|
9
|
-
static validate(input: any): CreateTaskDto {
|
|
10
|
-
if (!input || typeof input !== "object") {
|
|
11
|
-
throw new Error("Invalid body: expected object");
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
if (!input.type || typeof input.type !== "string") {
|
|
15
|
-
throw new Error("Invalid type: expected non-empty string");
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
if (!input.payload || typeof input.payload !== "object") {
|
|
19
|
-
throw new Error("Invalid payload: expected object");
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
if (input.priority && !["low", "normal", "high"].includes(input.priority)) {
|
|
23
|
-
throw new Error("Invalid priority: must be low, normal or high");
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
return {
|
|
27
|
-
type: input.type,
|
|
28
|
-
payload: input.payload,
|
|
29
|
-
priority: input.priority ?? "normal",
|
|
30
|
-
metadata: input.metadata ?? {},
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
}
|
|
1
|
+
export interface CreateTaskDto {
|
|
2
|
+
type: string;
|
|
3
|
+
payload: Record<string, any>;
|
|
4
|
+
priority?: "low" | "normal" | "high";
|
|
5
|
+
metadata?: Record<string, any>;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export class CreateTaskDtoValidator {
|
|
9
|
+
static validate(input: any): CreateTaskDto {
|
|
10
|
+
if (!input || typeof input !== "object") {
|
|
11
|
+
throw new Error("Invalid body: expected object");
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
if (!input.type || typeof input.type !== "string") {
|
|
15
|
+
throw new Error("Invalid type: expected non-empty string");
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (!input.payload || typeof input.payload !== "object") {
|
|
19
|
+
throw new Error("Invalid payload: expected object");
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (input.priority && !["low", "normal", "high"].includes(input.priority)) {
|
|
23
|
+
throw new Error("Invalid priority: must be low, normal or high");
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return {
|
|
27
|
+
type: input.type,
|
|
28
|
+
payload: input.payload,
|
|
29
|
+
priority: input.priority ?? "normal",
|
|
30
|
+
metadata: input.metadata ?? {},
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -1,33 +1,33 @@
|
|
|
1
|
-
export interface FilterTasksDto {
|
|
2
|
-
status?: "pending" | "running" | "completed" | "failed";
|
|
3
|
-
limit?: number;
|
|
4
|
-
type?: string;
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
export class FilterTasksDtoValidator {
|
|
8
|
-
static validate(query: any): FilterTasksDto {
|
|
9
|
-
const dto: FilterTasksDto = {};
|
|
10
|
-
|
|
11
|
-
if (query.status) {
|
|
12
|
-
const allowed = ["pending", "running", "completed", "failed"];
|
|
13
|
-
if (!allowed.includes(query.status)) {
|
|
14
|
-
throw new Error("Invalid status");
|
|
15
|
-
}
|
|
16
|
-
dto.status = query.status;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
if (query.limit) {
|
|
20
|
-
const n = Number(query.limit);
|
|
21
|
-
if (isNaN(n) || n <= 0) {
|
|
22
|
-
throw new Error("Invalid limit");
|
|
23
|
-
}
|
|
24
|
-
dto.limit = n;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
if (query.type) {
|
|
28
|
-
dto.type = String(query.type);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
return dto;
|
|
32
|
-
}
|
|
33
|
-
}
|
|
1
|
+
export interface FilterTasksDto {
|
|
2
|
+
status?: "pending" | "running" | "completed" | "failed";
|
|
3
|
+
limit?: number;
|
|
4
|
+
type?: string;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export class FilterTasksDtoValidator {
|
|
8
|
+
static validate(query: any): FilterTasksDto {
|
|
9
|
+
const dto: FilterTasksDto = {};
|
|
10
|
+
|
|
11
|
+
if (query.status) {
|
|
12
|
+
const allowed = ["pending", "running", "completed", "failed"];
|
|
13
|
+
if (!allowed.includes(query.status)) {
|
|
14
|
+
throw new Error("Invalid status");
|
|
15
|
+
}
|
|
16
|
+
dto.status = query.status;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (query.limit) {
|
|
20
|
+
const n = Number(query.limit);
|
|
21
|
+
if (isNaN(n) || n <= 0) {
|
|
22
|
+
throw new Error("Invalid limit");
|
|
23
|
+
}
|
|
24
|
+
dto.limit = n;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (query.type) {
|
|
28
|
+
dto.type = String(query.type);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return dto;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -1,41 +1,41 @@
|
|
|
1
|
-
import { describe, it, expect, jest } from "@jest/globals";
|
|
2
|
-
import { TasksService } from "../tasks.service";
|
|
3
|
-
import { EventsService } from "../events.service";
|
|
4
|
-
import { EventStreamManager } from "../../sse/events.stream";
|
|
5
|
-
|
|
6
|
-
describe("TasksService", () => {
|
|
7
|
-
it("émet un événement lors de la création d'une tâche", () => {
|
|
8
|
-
const stream = new EventStreamManager({ heartbeatInterval: 999999 });
|
|
9
|
-
const events = new EventsService(stream);
|
|
10
|
-
|
|
11
|
-
const spy = jest.spyOn(stream, "broadcast");
|
|
12
|
-
|
|
13
|
-
const service = new TasksService(events);
|
|
14
|
-
|
|
15
|
-
const task = service.create({ type: "x", payload: {} });
|
|
16
|
-
|
|
17
|
-
expect(spy).toHaveBeenCalledWith(
|
|
18
|
-
"tasks",
|
|
19
|
-
"task.created",
|
|
20
|
-
expect.objectContaining({ id: task.id }),
|
|
21
|
-
);
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
it("émet un événement lors de l'annulation d'une tâche", () => {
|
|
25
|
-
const stream = new EventStreamManager({ heartbeatInterval: 999999 });
|
|
26
|
-
const events = new EventsService(stream);
|
|
27
|
-
|
|
28
|
-
const spy = jest.spyOn(stream, "broadcast");
|
|
29
|
-
|
|
30
|
-
const service = new TasksService(events);
|
|
31
|
-
|
|
32
|
-
const task = service.create({ type: "x", payload: {} });
|
|
33
|
-
service.cancel(task.id);
|
|
34
|
-
|
|
35
|
-
expect(spy).toHaveBeenCalledWith(
|
|
36
|
-
"tasks",
|
|
37
|
-
"task.cancelled",
|
|
38
|
-
expect.objectContaining({ id: task.id }),
|
|
39
|
-
);
|
|
40
|
-
});
|
|
41
|
-
});
|
|
1
|
+
import { describe, it, expect, jest } from "@jest/globals";
|
|
2
|
+
import { TasksService } from "../tasks.service";
|
|
3
|
+
import { EventsService } from "../events.service";
|
|
4
|
+
import { EventStreamManager } from "../../sse/events.stream";
|
|
5
|
+
|
|
6
|
+
describe("TasksService", () => {
|
|
7
|
+
it("émet un événement lors de la création d'une tâche", () => {
|
|
8
|
+
const stream = new EventStreamManager({ heartbeatInterval: 999999 });
|
|
9
|
+
const events = new EventsService(stream);
|
|
10
|
+
|
|
11
|
+
const spy = jest.spyOn(stream, "broadcast");
|
|
12
|
+
|
|
13
|
+
const service = new TasksService(events);
|
|
14
|
+
|
|
15
|
+
const task = service.create({ type: "x", payload: {} });
|
|
16
|
+
|
|
17
|
+
expect(spy).toHaveBeenCalledWith(
|
|
18
|
+
"tasks",
|
|
19
|
+
"task.created",
|
|
20
|
+
expect.objectContaining({ id: task.id }),
|
|
21
|
+
);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it("émet un événement lors de l'annulation d'une tâche", () => {
|
|
25
|
+
const stream = new EventStreamManager({ heartbeatInterval: 999999 });
|
|
26
|
+
const events = new EventsService(stream);
|
|
27
|
+
|
|
28
|
+
const spy = jest.spyOn(stream, "broadcast");
|
|
29
|
+
|
|
30
|
+
const service = new TasksService(events);
|
|
31
|
+
|
|
32
|
+
const task = service.create({ type: "x", payload: {} });
|
|
33
|
+
service.cancel(task.id);
|
|
34
|
+
|
|
35
|
+
expect(spy).toHaveBeenCalledWith(
|
|
36
|
+
"tasks",
|
|
37
|
+
"task.cancelled",
|
|
38
|
+
expect.objectContaining({ id: task.id }),
|
|
39
|
+
);
|
|
40
|
+
});
|
|
41
|
+
});
|
|
@@ -1,41 +1,41 @@
|
|
|
1
|
-
import { describe, it, expect, jest } from "@jest/globals";
|
|
2
|
-
import { TasksService } from "../tasks.service";
|
|
3
|
-
import { EventsService } from "../events.service";
|
|
4
|
-
import { EventStreamManager } from "../../sse/events.stream";
|
|
5
|
-
|
|
6
|
-
describe("TasksService", () => {
|
|
7
|
-
it("émet un événement lors de la création d'une tâche", () => {
|
|
8
|
-
const stream = new EventStreamManager({ heartbeatInterval: 999999 });
|
|
9
|
-
const events = new EventsService(stream);
|
|
10
|
-
|
|
11
|
-
const spy = jest.spyOn(stream, "broadcast");
|
|
12
|
-
|
|
13
|
-
const service = new TasksService(events);
|
|
14
|
-
|
|
15
|
-
const task = service.create({ type: "x", payload: {} });
|
|
16
|
-
|
|
17
|
-
expect(spy).toHaveBeenCalledWith(
|
|
18
|
-
"tasks",
|
|
19
|
-
"task.created",
|
|
20
|
-
expect.objectContaining({ id: task.id }),
|
|
21
|
-
);
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
it("émet un événement lors de l'annulation d'une tâche", () => {
|
|
25
|
-
const stream = new EventStreamManager({ heartbeatInterval: 999999 });
|
|
26
|
-
const events = new EventsService(stream);
|
|
27
|
-
|
|
28
|
-
const spy = jest.spyOn(stream, "broadcast");
|
|
29
|
-
|
|
30
|
-
const service = new TasksService(events);
|
|
31
|
-
|
|
32
|
-
const task = service.create({ type: "x", payload: {} });
|
|
33
|
-
service.cancel(task.id);
|
|
34
|
-
|
|
35
|
-
expect(spy).toHaveBeenCalledWith(
|
|
36
|
-
"tasks",
|
|
37
|
-
"task.cancelled",
|
|
38
|
-
expect.objectContaining({ id: task.id }),
|
|
39
|
-
);
|
|
40
|
-
});
|
|
41
|
-
});
|
|
1
|
+
import { describe, it, expect, jest } from "@jest/globals";
|
|
2
|
+
import { TasksService } from "../tasks.service";
|
|
3
|
+
import { EventsService } from "../events.service";
|
|
4
|
+
import { EventStreamManager } from "../../sse/events.stream";
|
|
5
|
+
|
|
6
|
+
describe("TasksService", () => {
|
|
7
|
+
it("émet un événement lors de la création d'une tâche", () => {
|
|
8
|
+
const stream = new EventStreamManager({ heartbeatInterval: 999999 });
|
|
9
|
+
const events = new EventsService(stream);
|
|
10
|
+
|
|
11
|
+
const spy = jest.spyOn(stream, "broadcast");
|
|
12
|
+
|
|
13
|
+
const service = new TasksService(events);
|
|
14
|
+
|
|
15
|
+
const task = service.create({ type: "x", payload: {} });
|
|
16
|
+
|
|
17
|
+
expect(spy).toHaveBeenCalledWith(
|
|
18
|
+
"tasks",
|
|
19
|
+
"task.created",
|
|
20
|
+
expect.objectContaining({ id: task.id }),
|
|
21
|
+
);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it("émet un événement lors de l'annulation d'une tâche", () => {
|
|
25
|
+
const stream = new EventStreamManager({ heartbeatInterval: 999999 });
|
|
26
|
+
const events = new EventsService(stream);
|
|
27
|
+
|
|
28
|
+
const spy = jest.spyOn(stream, "broadcast");
|
|
29
|
+
|
|
30
|
+
const service = new TasksService(events);
|
|
31
|
+
|
|
32
|
+
const task = service.create({ type: "x", payload: {} });
|
|
33
|
+
service.cancel(task.id);
|
|
34
|
+
|
|
35
|
+
expect(spy).toHaveBeenCalledWith(
|
|
36
|
+
"tasks",
|
|
37
|
+
"task.cancelled",
|
|
38
|
+
expect.objectContaining({ id: task.id }),
|
|
39
|
+
);
|
|
40
|
+
});
|
|
41
|
+
});
|
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
import { EventStreamManager } from "../sse/events.stream";
|
|
2
|
-
|
|
3
|
-
export class EventsService {
|
|
4
|
-
constructor(private stream: EventStreamManager) {}
|
|
5
|
-
|
|
6
|
-
connect(res: any, channels: string[] = []) {
|
|
7
|
-
return this.stream.addClient(res, channels);
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
emit(channel: string, event: string, data: any) {
|
|
11
|
-
this.stream.broadcast(channel, event, data);
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
emitAll(event: string, data: any) {
|
|
15
|
-
this.stream.broadcastAll(event, data);
|
|
16
|
-
}
|
|
17
|
-
}
|
|
1
|
+
import { EventStreamManager } from "../sse/events.stream";
|
|
2
|
+
|
|
3
|
+
export class EventsService {
|
|
4
|
+
constructor(private stream: EventStreamManager) {}
|
|
5
|
+
|
|
6
|
+
connect(res: any, channels: string[] = []) {
|
|
7
|
+
return this.stream.addClient(res, channels);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
emit(channel: string, event: string, data: any) {
|
|
11
|
+
this.stream.broadcast(channel, event, data);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
emitAll(event: string, data: any) {
|
|
15
|
+
this.stream.broadcastAll(event, data);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -1,79 +1,79 @@
|
|
|
1
|
-
import { CreateTaskDto } from "../dto/create-task.dto";
|
|
2
|
-
import { FilterTasksDto } from "../dto/filter-tasks.dto";
|
|
3
|
-
import { EventsService } from "./events.service";
|
|
4
|
-
|
|
5
|
-
export interface Task {
|
|
6
|
-
id: string;
|
|
7
|
-
type: string;
|
|
8
|
-
payload: Record<string, any>;
|
|
9
|
-
priority: "low" | "normal" | "high";
|
|
10
|
-
metadata: Record<string, any>;
|
|
11
|
-
status: "pending" | "running" | "completed" | "failed";
|
|
12
|
-
createdAt: number;
|
|
13
|
-
updatedAt: number;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export class TasksService {
|
|
17
|
-
private tasks = new Map<string, Task>();
|
|
18
|
-
private events: EventsService;
|
|
19
|
-
|
|
20
|
-
constructor(events: EventsService) {
|
|
21
|
-
this.events = events;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
create(dto: CreateTaskDto): Task {
|
|
25
|
-
const id = crypto.randomUUID();
|
|
26
|
-
const now = Date.now();
|
|
27
|
-
|
|
28
|
-
const task: Task = {
|
|
29
|
-
id,
|
|
30
|
-
type: dto.type,
|
|
31
|
-
payload: dto.payload,
|
|
32
|
-
priority: dto.priority ?? "normal",
|
|
33
|
-
metadata: dto.metadata ?? {},
|
|
34
|
-
status: "pending",
|
|
35
|
-
createdAt: now,
|
|
36
|
-
updatedAt: now,
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
this.tasks.set(id, task);
|
|
40
|
-
|
|
41
|
-
this.events.emit("tasks", "task.created", task);
|
|
42
|
-
|
|
43
|
-
return task;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
get(id: string): Task | undefined {
|
|
47
|
-
return this.tasks.get(id);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
list(filters: FilterTasksDto): Task[] {
|
|
51
|
-
let results = Array.from(this.tasks.values());
|
|
52
|
-
|
|
53
|
-
if (filters.status) {
|
|
54
|
-
results = results.filter((t) => t.status === filters.status);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
if (filters.type) {
|
|
58
|
-
results = results.filter((t) => t.type === filters.type);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
if (filters.limit) {
|
|
62
|
-
results = results.slice(0, filters.limit);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
return results;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
cancel(id: string): boolean {
|
|
69
|
-
const task = this.tasks.get(id);
|
|
70
|
-
if (!task) return false;
|
|
71
|
-
|
|
72
|
-
task.status = "failed";
|
|
73
|
-
task.updatedAt = Date.now();
|
|
74
|
-
|
|
75
|
-
this.events.emit("tasks", "task.cancelled", task);
|
|
76
|
-
|
|
77
|
-
return true;
|
|
78
|
-
}
|
|
79
|
-
}
|
|
1
|
+
import { CreateTaskDto } from "../dto/create-task.dto";
|
|
2
|
+
import { FilterTasksDto } from "../dto/filter-tasks.dto";
|
|
3
|
+
import { EventsService } from "./events.service";
|
|
4
|
+
|
|
5
|
+
export interface Task {
|
|
6
|
+
id: string;
|
|
7
|
+
type: string;
|
|
8
|
+
payload: Record<string, any>;
|
|
9
|
+
priority: "low" | "normal" | "high";
|
|
10
|
+
metadata: Record<string, any>;
|
|
11
|
+
status: "pending" | "running" | "completed" | "failed";
|
|
12
|
+
createdAt: number;
|
|
13
|
+
updatedAt: number;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export class TasksService {
|
|
17
|
+
private tasks = new Map<string, Task>();
|
|
18
|
+
private events: EventsService;
|
|
19
|
+
|
|
20
|
+
constructor(events: EventsService) {
|
|
21
|
+
this.events = events;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
create(dto: CreateTaskDto): Task {
|
|
25
|
+
const id = crypto.randomUUID();
|
|
26
|
+
const now = Date.now();
|
|
27
|
+
|
|
28
|
+
const task: Task = {
|
|
29
|
+
id,
|
|
30
|
+
type: dto.type,
|
|
31
|
+
payload: dto.payload,
|
|
32
|
+
priority: dto.priority ?? "normal",
|
|
33
|
+
metadata: dto.metadata ?? {},
|
|
34
|
+
status: "pending",
|
|
35
|
+
createdAt: now,
|
|
36
|
+
updatedAt: now,
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
this.tasks.set(id, task);
|
|
40
|
+
|
|
41
|
+
this.events.emit("tasks", "task.created", task);
|
|
42
|
+
|
|
43
|
+
return task;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
get(id: string): Task | undefined {
|
|
47
|
+
return this.tasks.get(id);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
list(filters: FilterTasksDto): Task[] {
|
|
51
|
+
let results = Array.from(this.tasks.values());
|
|
52
|
+
|
|
53
|
+
if (filters.status) {
|
|
54
|
+
results = results.filter((t) => t.status === filters.status);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (filters.type) {
|
|
58
|
+
results = results.filter((t) => t.type === filters.type);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (filters.limit) {
|
|
62
|
+
results = results.slice(0, filters.limit);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return results;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
cancel(id: string): boolean {
|
|
69
|
+
const task = this.tasks.get(id);
|
|
70
|
+
if (!task) return false;
|
|
71
|
+
|
|
72
|
+
task.status = "failed";
|
|
73
|
+
task.updatedAt = Date.now();
|
|
74
|
+
|
|
75
|
+
this.events.emit("tasks", "task.cancelled", task);
|
|
76
|
+
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
}
|