@flink-app/flink 2.0.0-alpha.62 → 2.0.0-alpha.63
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/CHANGELOG.md +18 -0
- package/dist/src/FlinkApp.js +63 -27
- package/package.json +1 -1
- package/spec/FlinkApp.undefinedResponse.spec.ts +123 -0
- package/spec/FlinkJob.spec.ts +95 -0
- package/src/FlinkApp.ts +50 -26
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# @flink-app/flink
|
|
2
2
|
|
|
3
|
+
## 2.0.0-alpha.63
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 810df2c: Fix FlinkJob afterDelay: "0ms" running in a tight loop instead of once. Zero-delay jobs now run immediately via setImmediate (exactly once) rather than being registered with a 0ms scheduler interval. Also adds specs covering uncaught exception handling for all job scheduling modes.
|
|
8
|
+
- 8dd0752: fix: prevent server crash when handler returns no data with a response schema defined
|
|
9
|
+
|
|
10
|
+
When a PATCH (or any) handler returned `{ status: 204 }` without a `data` field,
|
|
11
|
+
`JSON.stringify(undefined)` returned the JS value `undefined`, causing
|
|
12
|
+
`JSON.parse(undefined)` to throw a `SyntaxError` that crashed the entire server process.
|
|
13
|
+
|
|
14
|
+
New behaviour:
|
|
15
|
+
|
|
16
|
+
- Status 204 + no data → skip validation silently (intentional no-content response)
|
|
17
|
+
- Non-204 status + no data + response schema defined → return 500 bad response with a
|
|
18
|
+
descriptive error message (surfaces the developer mistake without crashing the server)
|
|
19
|
+
- Data present → validate against schema as before
|
|
20
|
+
|
|
3
21
|
## 2.0.0-alpha.62
|
|
4
22
|
|
|
5
23
|
### Minor Changes
|
package/dist/src/FlinkApp.js
CHANGED
|
@@ -423,7 +423,7 @@ var FlinkApp = /** @class */ (function () {
|
|
|
423
423
|
}
|
|
424
424
|
}
|
|
425
425
|
this.expressApp[method](routeProps.path, function (req, res) { return __awaiter(_this, void 0, void 0, function () {
|
|
426
|
-
var valid, formattedErrors, data, normalizedQuery, _i, _a, _b, key, value, stream, handlerRes, flinkReq_1, err_1, errorResponse, result, valid, formattedErrors;
|
|
426
|
+
var valid, formattedErrors, data, normalizedQuery, _i, _a, _b, key, value, stream, handlerRes, flinkReq_1, err_1, errorResponse, result, detail, valid, formattedErrors;
|
|
427
427
|
var _this = this;
|
|
428
428
|
return __generator(this, function (_c) {
|
|
429
429
|
switch (_c.label) {
|
|
@@ -570,18 +570,30 @@ var FlinkApp = /** @class */ (function () {
|
|
|
570
570
|
return [2 /*return*/, res.status(204).send()];
|
|
571
571
|
}
|
|
572
572
|
if (validateRes_1 && !(0, utils_1.isError)(handlerRes)) {
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
573
|
+
if (handlerRes.data === undefined) {
|
|
574
|
+
if (handlerRes.status !== 204) {
|
|
575
|
+
detail = "Response schema is defined but handler returned no data";
|
|
576
|
+
FlinkLog_1.log.warn("[".concat(req.reqId, "] ").concat(methodAndRoute_1, ": Bad response - ").concat(detail));
|
|
577
|
+
return [2 /*return*/, res.status(500).json({
|
|
578
|
+
status: 500,
|
|
579
|
+
error: { id: (0, uuid_1.v4)(), title: "Bad response", detail: detail },
|
|
580
|
+
})];
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
else {
|
|
584
|
+
valid = validateRes_1(JSON.parse(JSON.stringify(handlerRes.data)));
|
|
585
|
+
if (!valid) {
|
|
586
|
+
formattedErrors = (0, utils_1.formatValidationErrors)(validateRes_1.errors, handlerRes.data);
|
|
587
|
+
FlinkLog_1.log.warn("[".concat(req.reqId, "] ").concat(methodAndRoute_1, ": Bad response\n").concat(formattedErrors));
|
|
588
|
+
return [2 /*return*/, res.status(500).json({
|
|
589
|
+
status: 500,
|
|
590
|
+
error: {
|
|
591
|
+
id: (0, uuid_1.v4)(),
|
|
592
|
+
title: "Bad response",
|
|
593
|
+
detail: formattedErrors,
|
|
594
|
+
},
|
|
595
|
+
})];
|
|
596
|
+
}
|
|
585
597
|
}
|
|
586
598
|
}
|
|
587
599
|
res.set(handlerRes.headers);
|
|
@@ -901,14 +913,38 @@ var FlinkApp = /** @class */ (function () {
|
|
|
901
913
|
this_1.scheduler.addSimpleIntervalJob(job);
|
|
902
914
|
}
|
|
903
915
|
else if (jobProps.afterDelay !== undefined) {
|
|
904
|
-
var
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
916
|
+
var delayMs = (0, ms_1.default)(jobProps.afterDelay);
|
|
917
|
+
if (delayMs === 0) {
|
|
918
|
+
setImmediate(function () { return __awaiter(_this, void 0, void 0, function () {
|
|
919
|
+
var err_2;
|
|
920
|
+
return __generator(this, function (_a) {
|
|
921
|
+
switch (_a.label) {
|
|
922
|
+
case 0:
|
|
923
|
+
_a.trys.push([0, 2, , 3]);
|
|
924
|
+
return [4 /*yield*/, jobFn({ ctx: this.ctx })];
|
|
925
|
+
case 1:
|
|
926
|
+
_a.sent();
|
|
927
|
+
return [3 /*break*/, 3];
|
|
928
|
+
case 2:
|
|
929
|
+
err_2 = _a.sent();
|
|
930
|
+
FlinkLog_1.log.error("Job ".concat(jobProps.id, " threw unhandled exception ").concat(err_2));
|
|
931
|
+
console.error(err_2);
|
|
932
|
+
return [3 /*break*/, 3];
|
|
933
|
+
case 3: return [2 /*return*/];
|
|
934
|
+
}
|
|
935
|
+
});
|
|
936
|
+
}); });
|
|
937
|
+
}
|
|
938
|
+
else {
|
|
939
|
+
var job = new toad_scheduler_1.SimpleIntervalJob({
|
|
940
|
+
milliseconds: delayMs,
|
|
941
|
+
runImmediately: false,
|
|
942
|
+
}, task, {
|
|
943
|
+
id: jobProps.id,
|
|
944
|
+
preventOverrun: jobProps.singleton,
|
|
945
|
+
});
|
|
946
|
+
this_1.scheduler.addSimpleIntervalJob(job);
|
|
947
|
+
}
|
|
912
948
|
}
|
|
913
949
|
else {
|
|
914
950
|
FlinkLog_1.log.error("Cannot register job ".concat(jobProps.id, " - no cron, interval or once set in ").concat(__file));
|
|
@@ -1111,7 +1147,7 @@ var FlinkApp = /** @class */ (function () {
|
|
|
1111
1147
|
*/
|
|
1112
1148
|
FlinkApp.prototype.initDb = function () {
|
|
1113
1149
|
return __awaiter(this, void 0, void 0, function () {
|
|
1114
|
-
var client,
|
|
1150
|
+
var client, err_3;
|
|
1115
1151
|
return __generator(this, function (_a) {
|
|
1116
1152
|
switch (_a.label) {
|
|
1117
1153
|
case 0:
|
|
@@ -1127,8 +1163,8 @@ var FlinkApp = /** @class */ (function () {
|
|
|
1127
1163
|
this.dbClient = client;
|
|
1128
1164
|
return [3 /*break*/, 4];
|
|
1129
1165
|
case 3:
|
|
1130
|
-
|
|
1131
|
-
FlinkLog_1.log.error("Failed to connect to db: " +
|
|
1166
|
+
err_3 = _a.sent();
|
|
1167
|
+
FlinkLog_1.log.error("Failed to connect to db: " + err_3);
|
|
1132
1168
|
process.exit(1);
|
|
1133
1169
|
return [3 /*break*/, 4];
|
|
1134
1170
|
case 4:
|
|
@@ -1147,7 +1183,7 @@ var FlinkApp = /** @class */ (function () {
|
|
|
1147
1183
|
*/
|
|
1148
1184
|
FlinkApp.prototype.initPluginDb = function (plugin) {
|
|
1149
1185
|
return __awaiter(this, void 0, void 0, function () {
|
|
1150
|
-
var client,
|
|
1186
|
+
var client, err_4;
|
|
1151
1187
|
return __generator(this, function (_a) {
|
|
1152
1188
|
switch (_a.label) {
|
|
1153
1189
|
case 0:
|
|
@@ -1174,8 +1210,8 @@ var FlinkApp = /** @class */ (function () {
|
|
|
1174
1210
|
client = _a.sent();
|
|
1175
1211
|
return [2 /*return*/, client.db()];
|
|
1176
1212
|
case 4:
|
|
1177
|
-
|
|
1178
|
-
FlinkLog_1.log.error("Failed to connect to db defined in plugin '".concat(plugin.id, "': ") +
|
|
1213
|
+
err_4 = _a.sent();
|
|
1214
|
+
FlinkLog_1.log.error("Failed to connect to db defined in plugin '".concat(plugin.id, "': ") + err_4);
|
|
1179
1215
|
return [3 /*break*/, 5];
|
|
1180
1216
|
case 5: return [2 /*return*/];
|
|
1181
1217
|
}
|
package/package.json
CHANGED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { FlinkApp } from "../src/FlinkApp";
|
|
2
|
+
import { FlinkContext } from "../src/FlinkContext";
|
|
3
|
+
import { GetHandler, Handler, HttpMethod } from "../src/FlinkHttpHandler";
|
|
4
|
+
|
|
5
|
+
const request = require("supertest");
|
|
6
|
+
|
|
7
|
+
interface TestContext extends FlinkContext {}
|
|
8
|
+
|
|
9
|
+
const resSchema = {
|
|
10
|
+
type: "object",
|
|
11
|
+
properties: {
|
|
12
|
+
id: { type: "string" },
|
|
13
|
+
},
|
|
14
|
+
required: ["id"],
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
describe("FlinkApp response validation when handler returns no data", () => {
|
|
18
|
+
let app: FlinkApp<TestContext>;
|
|
19
|
+
|
|
20
|
+
afterEach(async () => {
|
|
21
|
+
if (app && app.started) {
|
|
22
|
+
await app.stop();
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it("should return 500 bad response when handler returns undefined data with a response schema", async () => {
|
|
27
|
+
const handler: GetHandler<TestContext, any> = async () => {
|
|
28
|
+
return { status: 200 } as any; // no data
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
app = new FlinkApp<TestContext>({ name: "test-undefined-data", port: 4200 });
|
|
32
|
+
await app.start();
|
|
33
|
+
|
|
34
|
+
app.addHandler({
|
|
35
|
+
default: handler,
|
|
36
|
+
Route: { method: HttpMethod.get, path: "/test", resSchema },
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const response = await request(app.expressApp).get("/test");
|
|
40
|
+
|
|
41
|
+
expect(response.status).toBe(500);
|
|
42
|
+
expect(response.body.error.title).toBe("Bad response");
|
|
43
|
+
expect(response.body.error.detail).toContain("no data");
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it("should NOT return 500 when handler returns status 204 with no data (even if schema is defined)", async () => {
|
|
47
|
+
const handler: Handler<TestContext, any, any> = async () => {
|
|
48
|
+
return { status: 204 } as any;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
app = new FlinkApp<TestContext>({ name: "test-204-no-data", port: 4201 });
|
|
52
|
+
await app.start();
|
|
53
|
+
|
|
54
|
+
app.addHandler({
|
|
55
|
+
default: handler,
|
|
56
|
+
Route: { method: HttpMethod.patch, path: "/test", resSchema },
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
const response = await request(app.expressApp).patch("/test");
|
|
60
|
+
|
|
61
|
+
expect(response.status).toBe(204);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it("should validate normally when handler returns valid data", async () => {
|
|
65
|
+
const handler: GetHandler<TestContext, any> = async () => {
|
|
66
|
+
return { data: { id: "abc-123" } };
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
app = new FlinkApp<TestContext>({ name: "test-valid-data", port: 4202 });
|
|
70
|
+
await app.start();
|
|
71
|
+
|
|
72
|
+
app.addHandler({
|
|
73
|
+
default: handler,
|
|
74
|
+
Route: { method: HttpMethod.get, path: "/test", resSchema },
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
const response = await request(app.expressApp).get("/test");
|
|
78
|
+
|
|
79
|
+
expect(response.status).toBe(200);
|
|
80
|
+
expect(response.body.data.id).toBe("abc-123");
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it("should return 500 bad response when handler returns invalid data against schema", async () => {
|
|
84
|
+
const handler: GetHandler<TestContext, any> = async () => {
|
|
85
|
+
return { data: { wrongField: 123 } }; // missing required "id"
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
app = new FlinkApp<TestContext>({ name: "test-invalid-data", port: 4203 });
|
|
89
|
+
await app.start();
|
|
90
|
+
|
|
91
|
+
app.addHandler({
|
|
92
|
+
default: handler,
|
|
93
|
+
Route: { method: HttpMethod.get, path: "/test", resSchema },
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
const response = await request(app.expressApp).get("/test");
|
|
97
|
+
|
|
98
|
+
expect(response.status).toBe(500);
|
|
99
|
+
expect(response.body.error.title).toBe("Bad response");
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it("should not crash the server when handler returns undefined data", async () => {
|
|
103
|
+
const handler: GetHandler<TestContext, any> = async () => {
|
|
104
|
+
return { status: 200 } as any;
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
app = new FlinkApp<TestContext>({ name: "test-no-crash", port: 4204 });
|
|
108
|
+
await app.start();
|
|
109
|
+
|
|
110
|
+
app.addHandler({
|
|
111
|
+
default: handler,
|
|
112
|
+
Route: { method: HttpMethod.get, path: "/test", resSchema },
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
// First request triggers the bad response
|
|
116
|
+
await request(app.expressApp).get("/test");
|
|
117
|
+
|
|
118
|
+
// Server should still be running and able to handle further requests
|
|
119
|
+
const second = await request(app.expressApp).get("/test");
|
|
120
|
+
expect(second.status).toBe(500); // still returns 500, but doesn't crash
|
|
121
|
+
expect(app.started).toBe(true);
|
|
122
|
+
});
|
|
123
|
+
});
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { FlinkApp, autoRegisteredJobs } from "../src/FlinkApp";
|
|
2
|
+
import { FlinkContext } from "../src/FlinkContext";
|
|
3
|
+
import { FlinkJobFile } from "../src/FlinkJob";
|
|
4
|
+
|
|
5
|
+
interface TestContext extends FlinkContext {}
|
|
6
|
+
|
|
7
|
+
describe("FlinkJob error handling", () => {
|
|
8
|
+
let app: FlinkApp<TestContext>;
|
|
9
|
+
let consoleErrorSpy: jasmine.Spy;
|
|
10
|
+
|
|
11
|
+
beforeEach(() => {
|
|
12
|
+
consoleErrorSpy = spyOn(console, "error");
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
afterEach(async () => {
|
|
16
|
+
autoRegisteredJobs.length = 0;
|
|
17
|
+
if (app?.started) {
|
|
18
|
+
await app.stop();
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it("should catch and log errors from afterDelay 0ms jobs without crashing", async () => {
|
|
23
|
+
const job: FlinkJobFile = {
|
|
24
|
+
Job: { id: "failing-job-0ms", afterDelay: "0ms" },
|
|
25
|
+
default: async () => {
|
|
26
|
+
throw new Error("Job error 0ms");
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
autoRegisteredJobs.push(job);
|
|
31
|
+
|
|
32
|
+
app = new FlinkApp<TestContext>({ name: "test-job-errors-0ms", disableHttpServer: true });
|
|
33
|
+
await app.start();
|
|
34
|
+
|
|
35
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
36
|
+
|
|
37
|
+
expect(consoleErrorSpy).toHaveBeenCalled();
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it("should catch and log errors from afterDelay jobs without crashing", async () => {
|
|
41
|
+
const job: FlinkJobFile = {
|
|
42
|
+
Job: { id: "failing-job-delay", afterDelay: "10ms" },
|
|
43
|
+
default: async () => {
|
|
44
|
+
throw new Error("Job error with delay");
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
autoRegisteredJobs.push(job);
|
|
49
|
+
|
|
50
|
+
app = new FlinkApp<TestContext>({ name: "test-job-errors-delay", disableHttpServer: true });
|
|
51
|
+
await app.start();
|
|
52
|
+
|
|
53
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
54
|
+
|
|
55
|
+
expect(consoleErrorSpy).toHaveBeenCalled();
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it("should catch and log errors from interval jobs without crashing", async () => {
|
|
59
|
+
const job: FlinkJobFile = {
|
|
60
|
+
Job: { id: "failing-interval-job", interval: "10ms" },
|
|
61
|
+
default: async () => {
|
|
62
|
+
throw new Error("Interval job error");
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
autoRegisteredJobs.push(job);
|
|
67
|
+
|
|
68
|
+
app = new FlinkApp<TestContext>({ name: "test-job-errors-interval", disableHttpServer: true });
|
|
69
|
+
await app.start();
|
|
70
|
+
|
|
71
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
72
|
+
|
|
73
|
+
expect(consoleErrorSpy).toHaveBeenCalled();
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it("should run afterDelay 0ms job exactly once", async () => {
|
|
77
|
+
let runCount = 0;
|
|
78
|
+
|
|
79
|
+
const job: FlinkJobFile = {
|
|
80
|
+
Job: { id: "once-job-0ms", afterDelay: "0ms" },
|
|
81
|
+
default: async () => {
|
|
82
|
+
runCount++;
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
autoRegisteredJobs.push(job);
|
|
87
|
+
|
|
88
|
+
app = new FlinkApp<TestContext>({ name: "test-job-once-0ms", disableHttpServer: true });
|
|
89
|
+
await app.start();
|
|
90
|
+
|
|
91
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
92
|
+
|
|
93
|
+
expect(runCount).toBe(1);
|
|
94
|
+
});
|
|
95
|
+
});
|
package/src/FlinkApp.ts
CHANGED
|
@@ -790,20 +790,32 @@ export class FlinkApp<C extends FlinkContext> {
|
|
|
790
790
|
}
|
|
791
791
|
|
|
792
792
|
if (validateRes && !isError(handlerRes)) {
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
793
|
+
if (handlerRes.data === undefined) {
|
|
794
|
+
if (handlerRes.status !== 204) {
|
|
795
|
+
const detail =
|
|
796
|
+
"Response schema is defined but handler returned no data";
|
|
797
|
+
log.warn(`[${req.reqId}] ${methodAndRoute}: Bad response - ${detail}`);
|
|
798
|
+
return res.status(500).json({
|
|
799
|
+
status: 500,
|
|
800
|
+
error: { id: v4(), title: "Bad response", detail },
|
|
801
|
+
});
|
|
802
|
+
}
|
|
803
|
+
} else {
|
|
804
|
+
const valid = validateRes(JSON.parse(JSON.stringify(handlerRes.data)));
|
|
805
|
+
|
|
806
|
+
if (!valid) {
|
|
807
|
+
const formattedErrors = formatValidationErrors(validateRes.errors, handlerRes.data);
|
|
808
|
+
log.warn(`[${req.reqId}] ${methodAndRoute}: Bad response\n${formattedErrors}`);
|
|
809
|
+
|
|
810
|
+
return res.status(500).json({
|
|
811
|
+
status: 500,
|
|
812
|
+
error: {
|
|
813
|
+
id: v4(),
|
|
814
|
+
title: "Bad response",
|
|
815
|
+
detail: formattedErrors,
|
|
816
|
+
},
|
|
817
|
+
});
|
|
818
|
+
}
|
|
807
819
|
}
|
|
808
820
|
}
|
|
809
821
|
|
|
@@ -1198,18 +1210,30 @@ export class FlinkApp<C extends FlinkContext> {
|
|
|
1198
1210
|
|
|
1199
1211
|
this.scheduler.addSimpleIntervalJob(job);
|
|
1200
1212
|
} else if (jobProps.afterDelay !== undefined) {
|
|
1201
|
-
const
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
}
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
+
const delayMs = ms(jobProps.afterDelay);
|
|
1214
|
+
if (delayMs === 0) {
|
|
1215
|
+
setImmediate(async () => {
|
|
1216
|
+
try {
|
|
1217
|
+
await jobFn({ ctx: this.ctx });
|
|
1218
|
+
} catch (err) {
|
|
1219
|
+
log.error(`Job ${jobProps.id} threw unhandled exception ${err}`);
|
|
1220
|
+
console.error(err);
|
|
1221
|
+
}
|
|
1222
|
+
});
|
|
1223
|
+
} else {
|
|
1224
|
+
const job = new SimpleIntervalJob(
|
|
1225
|
+
{
|
|
1226
|
+
milliseconds: delayMs,
|
|
1227
|
+
runImmediately: false,
|
|
1228
|
+
},
|
|
1229
|
+
task,
|
|
1230
|
+
{
|
|
1231
|
+
id: jobProps.id,
|
|
1232
|
+
preventOverrun: jobProps.singleton,
|
|
1233
|
+
}
|
|
1234
|
+
);
|
|
1235
|
+
this.scheduler.addSimpleIntervalJob(job);
|
|
1236
|
+
}
|
|
1213
1237
|
} else {
|
|
1214
1238
|
log.error(`Cannot register job ${jobProps.id} - no cron, interval or once set in ${__file}`);
|
|
1215
1239
|
continue;
|