@flink-app/flink 0.3.11 → 0.4.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/cli/run.ts CHANGED
@@ -2,10 +2,10 @@
2
2
  import TypeScriptCompiler from "../src/TypeScriptCompiler";
3
3
 
4
4
  module.exports = async function run(args: string[]) {
5
- const startTime = Date.now();
5
+ const startTime = Date.now();
6
6
 
7
- if (args.includes("--help")) {
8
- console.log(`
7
+ if (args.includes("--help")) {
8
+ console.log(`
9
9
  Description
10
10
  Compiles and starts the application.
11
11
 
@@ -20,37 +20,33 @@ module.exports = async function run(args: string[]) {
20
20
  --help Displays this message
21
21
  `);
22
22
 
23
- process.exit(0);
24
- }
23
+ process.exit(0);
24
+ }
25
25
 
26
- let dir = "./";
27
- if (args[0] && !args[0].startsWith("--")) {
28
- dir = args[0];
29
- }
26
+ let dir = "./";
27
+ if (args[0] && !args[0].startsWith("--")) {
28
+ dir = args[0];
29
+ }
30
30
 
31
- let entry = "/src/index.ts";
32
- if (args.includes("--entry")) {
33
- entry = args[args.indexOf("--entry") + 1];
34
- entry = entry.startsWith("/") ? entry : "/" + entry;
35
- }
31
+ let entry = "/src/index.ts";
32
+ if (args.includes("--entry")) {
33
+ entry = args[args.indexOf("--entry") + 1];
34
+ entry = entry.startsWith("/") ? entry : "/" + entry;
35
+ }
36
36
 
37
- await TypeScriptCompiler.clean(dir);
37
+ await TypeScriptCompiler.clean(dir);
38
38
 
39
- const compiler = new TypeScriptCompiler(dir);
39
+ const compiler = new TypeScriptCompiler(dir);
40
40
 
41
- if (!compiler.getPreEmitDiagnostics()) {
42
- process.exit(1);
43
- }
41
+ if (!compiler.getPreEmitDiagnostics()) {
42
+ process.exit(1);
43
+ }
44
44
 
45
- await Promise.all([
46
- compiler.parseRepos(),
47
- compiler.parseHandlers(),
48
- compiler.generateStartScript(entry),
49
- ]);
45
+ await Promise.all([compiler.parseRepos(), compiler.parseHandlers(), compiler.parseJobs(), compiler.generateStartScript(entry)]);
50
46
 
51
- console.log(`Compilation done, took ${Date.now() - startTime}ms`);
47
+ console.log(`Compilation done, took ${Date.now() - startTime}ms`);
52
48
 
53
- compiler.emit();
49
+ compiler.emit();
54
50
 
55
- require("child_process").fork(dir + "/dist/.flink/start.js");
51
+ require("child_process").fork(dir + "/dist/.flink/start.js");
56
52
  };
package/dist/cli/run.js CHANGED
@@ -68,11 +68,7 @@ module.exports = function run(args) {
68
68
  if (!compiler.getPreEmitDiagnostics()) {
69
69
  process.exit(1);
70
70
  }
71
- return [4 /*yield*/, Promise.all([
72
- compiler.parseRepos(),
73
- compiler.parseHandlers(),
74
- compiler.generateStartScript(entry),
75
- ])];
71
+ return [4 /*yield*/, Promise.all([compiler.parseRepos(), compiler.parseHandlers(), compiler.parseJobs(), compiler.generateStartScript(entry)])];
76
72
  case 2:
77
73
  _a.sent();
78
74
  console.log("Compilation done, took ".concat(Date.now() - startTime, "ms"));
@@ -1,15 +1,18 @@
1
+ import { OptionsJson } from "body-parser";
1
2
  import { Express } from "express";
2
3
  import { JSONSchema7 } from "json-schema";
3
4
  import { Db } from "mongodb";
5
+ import { ToadScheduler } from "toad-scheduler";
4
6
  import { FlinkAuthPlugin } from "./auth/FlinkAuthPlugin";
5
7
  import { FlinkContext } from "./FlinkContext";
6
8
  import { HandlerFile, HttpMethod, QueryParamMetadata, RouteProps } from "./FlinkHttpHandler";
9
+ import { FlinkJobFile } from "./FlinkJob";
7
10
  import { FlinkPlugin } from "./FlinkPlugin";
8
11
  import { FlinkRepo } from "./FlinkRepo";
9
12
  export type JSONSchema = JSONSchema7;
10
13
  /**
11
14
  * This will be populated at compile time when the apps handlers
12
- * are picked up by typescript compiler
15
+ * are picked up by TypeScript compiler
13
16
  */
14
17
  export declare const autoRegisteredHandlers: {
15
18
  handler: HandlerFile;
@@ -17,13 +20,18 @@ export declare const autoRegisteredHandlers: {
17
20
  }[];
18
21
  /**
19
22
  * This will be populated at compile time when the apps repos
20
- * are picked up by typescript compiler
23
+ * are picked up by TypeScript compiler
21
24
  */
22
25
  export declare const autoRegisteredRepos: {
23
26
  collectionName: string;
24
27
  repoInstanceName: string;
25
28
  Repo: any;
26
29
  }[];
30
+ /**
31
+ * This will be populated at compile time when the apps jobs
32
+ * are picked up by TypeScript compiler
33
+ */
34
+ export declare const autoRegisteredJobs: FlinkJobFile[];
27
35
  export interface FlinkOptions {
28
36
  /**
29
37
  * Name of application, will only show in logs and in HTTP header.
@@ -88,6 +96,17 @@ export interface FlinkOptions {
88
96
  * Optional root folder of app. Defaults to `./`
89
97
  */
90
98
  appRoot?: string;
99
+ /**
100
+ * Options for json body parser
101
+ */
102
+ jsonOptions?: OptionsJson;
103
+ scheduling?: {
104
+ /**
105
+ * If true, the scheduler will be enabled.
106
+ * Defaults to true.
107
+ */
108
+ enabled?: boolean;
109
+ };
91
110
  }
92
111
  export interface HandlerConfig {
93
112
  schema?: {
@@ -124,11 +143,14 @@ export declare class FlinkApp<C extends FlinkContext> {
124
143
  private auth?;
125
144
  private corsOpts;
126
145
  private routingConfigured;
146
+ private jsonOptions?;
147
+ private schedulingOptions?;
127
148
  private repos;
128
149
  /**
129
150
  * Internal cache used to track registered handlers and potentially any overlapping routes
130
151
  */
131
152
  private handlerRouteCache;
153
+ scheduler?: ToadScheduler;
132
154
  constructor(opts: FlinkOptions);
133
155
  get ctx(): C;
134
156
  start(): Promise<this>;
@@ -147,6 +169,7 @@ export declare class FlinkApp<C extends FlinkContext> {
147
169
  * Will not register any handlers added programmatically.
148
170
  */
149
171
  private registerAutoRegisterableHandlers;
172
+ private registerAutoRegisterableJobs;
150
173
  addRepo(instanceName: string, repoInstance: FlinkRepo<C>): void;
151
174
  /**
152
175
  * Constructs the app context. Will inject context in all components
@@ -163,4 +186,5 @@ export declare class FlinkApp<C extends FlinkContext> {
163
186
  private initPluginDb;
164
187
  private authenticate;
165
188
  getRegisteredRoutes(): string[];
189
+ private get isSchedulingEnabled();
166
190
  }
@@ -50,16 +50,18 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
50
50
  return (mod && mod.__esModule) ? mod : { "default": mod };
51
51
  };
52
52
  Object.defineProperty(exports, "__esModule", { value: true });
53
- exports.FlinkApp = exports.autoRegisteredRepos = exports.autoRegisteredHandlers = void 0;
53
+ exports.FlinkApp = exports.autoRegisteredJobs = exports.autoRegisteredRepos = exports.autoRegisteredHandlers = void 0;
54
54
  var ajv_1 = __importDefault(require("ajv"));
55
55
  var ajv_formats_1 = __importDefault(require("ajv-formats"));
56
56
  var body_parser_1 = __importDefault(require("body-parser"));
57
57
  var cors_1 = __importDefault(require("cors"));
58
58
  var express_1 = __importDefault(require("express"));
59
59
  var mongodb_1 = __importDefault(require("mongodb"));
60
- var node_color_log_1 = __importDefault(require("node-color-log"));
60
+ var ms_1 = __importDefault(require("ms"));
61
+ var toad_scheduler_1 = require("toad-scheduler");
61
62
  var uuid_1 = require("uuid");
62
63
  var FlinkErrors_1 = require("./FlinkErrors");
64
+ var FlinkLog_1 = require("./FlinkLog");
63
65
  var mock_data_generator_1 = __importDefault(require("./mock-data-generator"));
64
66
  var utils_1 = require("./utils");
65
67
  var ajv = new ajv_1.default();
@@ -71,14 +73,19 @@ var defaultCorsOptions = {
71
73
  };
72
74
  /**
73
75
  * This will be populated at compile time when the apps handlers
74
- * are picked up by typescript compiler
76
+ * are picked up by TypeScript compiler
75
77
  */
76
78
  exports.autoRegisteredHandlers = [];
77
79
  /**
78
80
  * This will be populated at compile time when the apps repos
79
- * are picked up by typescript compiler
81
+ * are picked up by TypeScript compiler
80
82
  */
81
83
  exports.autoRegisteredRepos = [];
84
+ /**
85
+ * This will be populated at compile time when the apps jobs
86
+ * are picked up by TypeScript compiler
87
+ */
88
+ exports.autoRegisteredJobs = [];
82
89
  var FlinkApp = /** @class */ (function () {
83
90
  function FlinkApp(opts) {
84
91
  this.handlers = [];
@@ -99,6 +106,8 @@ var FlinkApp = /** @class */ (function () {
99
106
  this.plugins = opts.plugins || [];
100
107
  this.corsOpts = __assign(__assign({}, defaultCorsOptions), opts.cors);
101
108
  this.auth = opts.auth;
109
+ this.jsonOptions = opts.jsonOptions || { limit: "1mb" };
110
+ this.schedulingOptions = opts.scheduling;
102
111
  }
103
112
  Object.defineProperty(FlinkApp.prototype, "ctx", {
104
113
  get: function () {
@@ -125,22 +134,21 @@ var FlinkApp = /** @class */ (function () {
125
134
  _c.sent();
126
135
  if (this.debug) {
127
136
  offsetTime = Date.now();
128
- node_color_log_1.default.bgColorLog("cyan", "Init db took ".concat(offsetTime - startTime, " ms"));
137
+ FlinkLog_1.log.bgColorLog("cyan", "Init db took ".concat(offsetTime - startTime, " ms"));
129
138
  }
130
139
  return [4 /*yield*/, this.buildContext()];
131
140
  case 2:
132
141
  _c.sent();
133
142
  if (this.debug) {
134
- node_color_log_1.default.bgColorLog("cyan", "Build context took ".concat(Date.now() - offsetTime, " ms"));
143
+ FlinkLog_1.log.bgColorLog("cyan", "Build context took ".concat(Date.now() - offsetTime, " ms"));
135
144
  offsetTime = Date.now();
136
145
  }
137
- if (this.debug) {
138
- node_color_log_1.default.bgColorLog("cyan", "Registered JSON schemas took ".concat(Date.now() - offsetTime, " ms"));
139
- offsetTime = Date.now();
146
+ if (this.isSchedulingEnabled) {
147
+ this.scheduler = new toad_scheduler_1.ToadScheduler();
140
148
  }
141
149
  this.expressApp = (0, express_1.default)();
142
150
  this.expressApp.use((0, cors_1.default)(this.corsOpts));
143
- this.expressApp.use(body_parser_1.default.json());
151
+ this.expressApp.use(body_parser_1.default.json(this.jsonOptions));
144
152
  this.expressApp.use(function (req, res, next) {
145
153
  req.reqId = (0, uuid_1.v4)();
146
154
  next();
@@ -163,7 +171,7 @@ var FlinkApp = /** @class */ (function () {
163
171
  _c.sent();
164
172
  _c.label = 7;
165
173
  case 7:
166
- node_color_log_1.default.info("Initialized plugin '".concat(plugin.id, "'"));
174
+ FlinkLog_1.log.info("Initialized plugin '".concat(plugin.id, "'"));
167
175
  _c.label = 8;
168
176
  case 8:
169
177
  _i++;
@@ -172,9 +180,19 @@ var FlinkApp = /** @class */ (function () {
172
180
  case 10:
173
181
  _c.sent();
174
182
  if (this.debug) {
175
- node_color_log_1.default.bgColorLog("cyan", "Register handlers took ".concat(Date.now() - offsetTime, " ms"));
183
+ FlinkLog_1.log.bgColorLog("cyan", "Register handlers took ".concat(Date.now() - offsetTime, " ms"));
176
184
  offsetTime = Date.now();
177
185
  }
186
+ if (!this.isSchedulingEnabled) return [3 /*break*/, 12];
187
+ return [4 /*yield*/, this.registerAutoRegisterableJobs()];
188
+ case 11:
189
+ _c.sent();
190
+ if (this.debug) {
191
+ FlinkLog_1.log.bgColorLog("cyan", "Register jobs took ".concat(Date.now() - offsetTime, " ms"));
192
+ offsetTime = Date.now();
193
+ }
194
+ _c.label = 12;
195
+ case 12:
178
196
  // Register 404 with slight delay to allow all manually added routes to be added
179
197
  // TODO: Is there a better solution to force this handler to always run last?
180
198
  setTimeout(function () {
@@ -184,7 +202,7 @@ var FlinkApp = /** @class */ (function () {
184
202
  _this.routingConfigured = true;
185
203
  });
186
204
  (_a = this.expressApp) === null || _a === void 0 ? void 0 : _a.listen(this.port, function () {
187
- node_color_log_1.default.fontColorLog("magenta", "\u26A1\uFE0F HTTP server '".concat(_this.name, "' is running and waiting for connections on ").concat(_this.port));
205
+ FlinkLog_1.log.fontColorLog("magenta", "\u26A1\uFE0F HTTP server '".concat(_this.name, "' is running and waiting for connections on ").concat(_this.port));
188
206
  _this.started = true;
189
207
  });
190
208
  return [2 /*return*/, this];
@@ -205,18 +223,18 @@ var FlinkApp = /** @class */ (function () {
205
223
  }
206
224
  var routeProps = __assign(__assign({}, (handler.Route || {})), routePropsOverride);
207
225
  if (!routeProps.method) {
208
- node_color_log_1.default.error("Failed to register handler '".concat(handler.__file, "': Missing 'method' in route props, either set it or name handler file with HTTP method as prefix"));
226
+ FlinkLog_1.log.error("Failed to register handler '".concat(handler.__file, "': Missing 'method' in route props, either set it or name handler file with HTTP method as prefix"));
209
227
  return;
210
228
  }
211
229
  if (!routeProps.path) {
212
- node_color_log_1.default.error("Failed to register handler '".concat(handler.__file, "': Missing 'path' in route props"));
230
+ FlinkLog_1.log.error("Failed to register handler '".concat(handler.__file, "': Missing 'path' in route props"));
213
231
  return;
214
232
  }
215
233
  var dup = this.handlers.find(function (h) { return h.routeProps.path === routeProps.path && h.routeProps.method === routeProps.method; });
216
234
  var methodAndPath = "".concat(routeProps.method.toUpperCase(), " ").concat(routeProps.path);
217
235
  if (dup) {
218
236
  // TODO: Not sure if there is a case where you'd want to overwrite a route?
219
- node_color_log_1.default.warn("".concat(methodAndPath, " overlaps existing route"));
237
+ FlinkLog_1.log.warn("".concat(methodAndPath, " overlaps existing route"));
220
238
  }
221
239
  var handlerConfig = {
222
240
  routeProps: __assign(__assign({}, routeProps), { method: routeProps.method, path: routeProps.path }),
@@ -228,10 +246,10 @@ var FlinkApp = /** @class */ (function () {
228
246
  paramsMetadata: handler.__params || [],
229
247
  };
230
248
  if (((_c = handler.__schemas) === null || _c === void 0 ? void 0 : _c.reqSchema) && !((_d = handlerConfig.schema) === null || _d === void 0 ? void 0 : _d.reqSchema)) {
231
- node_color_log_1.default.warn("Expected request schema ".concat(handler.__schemas.reqSchema, " for handler ").concat(methodAndPath, " but no such schema was found"));
249
+ FlinkLog_1.log.warn("Expected request schema ".concat(handler.__schemas.reqSchema, " for handler ").concat(methodAndPath, " but no such schema was found"));
232
250
  }
233
251
  if (((_e = handler.__schemas) === null || _e === void 0 ? void 0 : _e.resSchema) && !((_f = handlerConfig.schema) === null || _f === void 0 ? void 0 : _f.resSchema)) {
234
- node_color_log_1.default.warn("Expected response schema ".concat(handler.__schemas.resSchema, " for handler ").concat(methodAndPath, " but no such schema was found"));
252
+ FlinkLog_1.log.warn("Expected response schema ".concat(handler.__schemas.resSchema, " for handler ").concat(methodAndPath, " but no such schema was found"));
235
253
  }
236
254
  this.registerHandler(handlerConfig, handler.default);
237
255
  };
@@ -242,7 +260,7 @@ var FlinkApp = /** @class */ (function () {
242
260
  var method = routeProps.method;
243
261
  var app = this.expressApp;
244
262
  if (!method) {
245
- node_color_log_1.default.error("Route ".concat(routeProps.path, " is missing http method"));
263
+ FlinkLog_1.log.error("Route ".concat(routeProps.path, " is missing http method"));
246
264
  }
247
265
  if (method) {
248
266
  var methodAndRoute_1 = "".concat(method.toUpperCase(), " ").concat(routeProps.path);
@@ -263,8 +281,8 @@ var FlinkApp = /** @class */ (function () {
263
281
  validate = ajv.compile(schema.reqSchema);
264
282
  valid = validate(req.body);
265
283
  if (!valid) {
266
- node_color_log_1.default.warn("".concat(methodAndRoute_1, ": Bad request ").concat(JSON.stringify(validate.errors, null, 2)));
267
- node_color_log_1.default.debug("Invalid json: ".concat(JSON.stringify(req.body)));
284
+ FlinkLog_1.log.warn("".concat(methodAndRoute_1, ": Bad request ").concat(JSON.stringify(validate.errors, null, 2)));
285
+ FlinkLog_1.log.debug("Invalid json: ".concat(JSON.stringify(req.body)));
268
286
  return [2 /*return*/, res.status(400).json({
269
287
  status: 400,
270
288
  error: {
@@ -276,7 +294,7 @@ var FlinkApp = /** @class */ (function () {
276
294
  }
277
295
  }
278
296
  if (routeProps.mockApi && schema.resSchema) {
279
- node_color_log_1.default.warn("Mock response for ".concat(req.method.toUpperCase(), " ").concat(req.path));
297
+ FlinkLog_1.log.warn("Mock response for ".concat(req.method.toUpperCase(), " ").concat(req.path));
280
298
  data = (0, mock_data_generator_1.default)(schema.resSchema);
281
299
  res.status(200).json({
282
300
  status: 200,
@@ -298,7 +316,7 @@ var FlinkApp = /** @class */ (function () {
298
316
  return [3 /*break*/, 6];
299
317
  case 5:
300
318
  err_1 = _a.sent();
301
- node_color_log_1.default.warn("Handler '".concat(methodAndRoute_1, "' threw unhandled exception ").concat(err_1));
319
+ FlinkLog_1.log.warn("Handler '".concat(methodAndRoute_1, "' threw unhandled exception ").concat(err_1));
302
320
  console.error(err_1);
303
321
  return [2 /*return*/, res.status(500).json((0, FlinkErrors_1.internalServerError)(err_1))];
304
322
  case 6:
@@ -306,8 +324,8 @@ var FlinkApp = /** @class */ (function () {
306
324
  validate = ajv.compile(schema.resSchema);
307
325
  valid = validate(JSON.parse(JSON.stringify(handlerRes.data)));
308
326
  if (!valid) {
309
- node_color_log_1.default.warn("[".concat(req.reqId, "] ").concat(methodAndRoute_1, ": Bad response ").concat(JSON.stringify(validate.errors, null, 2)));
310
- node_color_log_1.default.debug("Invalid json: ".concat(JSON.stringify(handlerRes.data)));
327
+ FlinkLog_1.log.warn("[".concat(req.reqId, "] ").concat(methodAndRoute_1, ": Bad response ").concat(JSON.stringify(validate.errors, null, 2)));
328
+ FlinkLog_1.log.debug("Invalid json: ".concat(JSON.stringify(handlerRes.data)));
311
329
  // log.debug(JSON.stringify(schema, null, 2));
312
330
  return [2 /*return*/, res.status(500).json({
313
331
  status: 500,
@@ -326,12 +344,12 @@ var FlinkApp = /** @class */ (function () {
326
344
  });
327
345
  }); });
328
346
  if (this.handlerRouteCache.has(methodAndRoute_1)) {
329
- node_color_log_1.default.error("Cannot register handler ".concat(methodAndRoute_1, " - route already registered"));
347
+ FlinkLog_1.log.error("Cannot register handler ".concat(methodAndRoute_1, " - route already registered"));
330
348
  return process.exit(1); // TODO: Do we need to exit?
331
349
  }
332
350
  else {
333
351
  this.handlerRouteCache.set(methodAndRoute_1, JSON.stringify(routeProps));
334
- node_color_log_1.default.info("Registered route ".concat(methodAndRoute_1));
352
+ FlinkLog_1.log.info("Registered route ".concat(methodAndRoute_1));
335
353
  }
336
354
  }
337
355
  };
@@ -349,11 +367,11 @@ var FlinkApp = /** @class */ (function () {
349
367
  for (_i = 0, autoRegisteredHandlers_1 = exports.autoRegisteredHandlers; _i < autoRegisteredHandlers_1.length; _i++) {
350
368
  _c = autoRegisteredHandlers_1[_i], handler = _c.handler, assumedHttpMethod = _c.assumedHttpMethod;
351
369
  if (!handler.Route) {
352
- node_color_log_1.default.error("Missing Props in handler ".concat(handler.__file));
370
+ FlinkLog_1.log.error("Missing Props in handler ".concat(handler.__file));
353
371
  continue;
354
372
  }
355
373
  if (!handler.default) {
356
- node_color_log_1.default.error("Missing exported handler function in handler ".concat(handler.__file));
374
+ FlinkLog_1.log.error("Missing exported handler function in handler ".concat(handler.__file));
357
375
  continue;
358
376
  }
359
377
  this.registerHandler({
@@ -370,6 +388,91 @@ var FlinkApp = /** @class */ (function () {
370
388
  });
371
389
  });
372
390
  };
391
+ FlinkApp.prototype.registerAutoRegisterableJobs = function () {
392
+ return __awaiter(this, void 0, void 0, function () {
393
+ var _loop_1, this_1, _i, autoRegisteredJobs_1, _a, jobProps, jobFn, __file;
394
+ var _this = this;
395
+ return __generator(this, function (_b) {
396
+ if (!this.scheduler) {
397
+ throw new Error("Scheduler not initialized"); // should never happen
398
+ }
399
+ _loop_1 = function (jobProps, jobFn, __file) {
400
+ if (jobProps.cron && jobProps.interval) {
401
+ FlinkLog_1.log.error("Cannot register job ".concat(jobProps.id, " - both cron and interval are set in ").concat(__file));
402
+ return "continue";
403
+ }
404
+ if (jobProps.cron && jobProps.afterDelay) {
405
+ FlinkLog_1.log.error("Cannot register job ".concat(jobProps.id, " - both cron and afterDelay are set in ").concat(__file));
406
+ return "continue";
407
+ }
408
+ if (jobProps.interval && jobProps.afterDelay) {
409
+ FlinkLog_1.log.error("Cannot register job ".concat(jobProps.id, " - both interval and afterDelay are set in ").concat(__file));
410
+ return "continue";
411
+ }
412
+ if (this_1.scheduler.existsById(jobProps.id)) {
413
+ FlinkLog_1.log.error("Job with id ".concat(jobProps.id, " is already registered, found duplicate in ").concat(__file));
414
+ return "continue";
415
+ }
416
+ FlinkLog_1.log.debug("Registering job ".concat(jobProps.id, ": ").concat(JSON.stringify(jobProps), " from ").concat(__file));
417
+ var task = new toad_scheduler_1.AsyncTask(jobProps.id, function () { return __awaiter(_this, void 0, void 0, function () {
418
+ return __generator(this, function (_a) {
419
+ switch (_a.label) {
420
+ case 0: return [4 /*yield*/, jobFn({ ctx: this.ctx })];
421
+ case 1:
422
+ _a.sent();
423
+ FlinkLog_1.log.debug("Job ".concat(jobProps.id, " completed"));
424
+ if (jobProps.afterDelay) {
425
+ // afterDelay runs only once, so we remove the job
426
+ this.scheduler.removeById(jobProps.id);
427
+ }
428
+ return [2 /*return*/];
429
+ }
430
+ });
431
+ }); }, function (err) {
432
+ FlinkLog_1.log.error("Job ".concat(jobProps.id, " threw unhandled exception ").concat(err));
433
+ console.error(err);
434
+ });
435
+ if (jobProps.cron) {
436
+ var job = new toad_scheduler_1.CronJob({ timezone: jobProps.timezone, cronExpression: jobProps.cron }, task, {
437
+ id: jobProps.id,
438
+ preventOverrun: jobProps.singleton,
439
+ });
440
+ this_1.scheduler.addCronJob(job);
441
+ }
442
+ else if (jobProps.interval) {
443
+ var job = new toad_scheduler_1.SimpleIntervalJob({
444
+ milliseconds: (0, ms_1.default)(jobProps.interval),
445
+ runImmediately: false, // TODO: Expose to props?
446
+ }, task, {
447
+ id: jobProps.id,
448
+ preventOverrun: jobProps.singleton,
449
+ });
450
+ this_1.scheduler.addSimpleIntervalJob(job);
451
+ }
452
+ else if (jobProps.afterDelay !== undefined) {
453
+ var job = new toad_scheduler_1.SimpleIntervalJob({
454
+ milliseconds: (0, ms_1.default)(jobProps.afterDelay),
455
+ runImmediately: false,
456
+ }, task, {
457
+ id: jobProps.id,
458
+ preventOverrun: jobProps.singleton,
459
+ });
460
+ this_1.scheduler.addSimpleIntervalJob(job);
461
+ }
462
+ else {
463
+ FlinkLog_1.log.error("Cannot register job ".concat(jobProps.id, " - no cron, interval or once set in ").concat(__file));
464
+ return "continue";
465
+ }
466
+ };
467
+ this_1 = this;
468
+ for (_i = 0, autoRegisteredJobs_1 = exports.autoRegisteredJobs; _i < autoRegisteredJobs_1.length; _i++) {
469
+ _a = autoRegisteredJobs_1[_i], jobProps = _a.Job, jobFn = _a.default, __file = _a.__file;
470
+ _loop_1(jobProps, jobFn, __file);
471
+ }
472
+ return [2 /*return*/];
473
+ });
474
+ });
475
+ };
373
476
  FlinkApp.prototype.addRepo = function (instanceName, repoInstance) {
374
477
  this.repos[instanceName] = repoInstance;
375
478
  // TODO: Find out if we need to set ctx here or wanted not to if plugin has its own context
@@ -388,11 +491,11 @@ var FlinkApp = /** @class */ (function () {
388
491
  _a = autoRegisteredRepos_1[_i], collectionName = _a.collectionName, repoInstanceName = _a.repoInstanceName, Repo = _a.Repo;
389
492
  repoInstance = new Repo(collectionName, this.db);
390
493
  this.repos[repoInstanceName] = repoInstance;
391
- node_color_log_1.default.info("Registered repo ".concat(repoInstanceName));
494
+ FlinkLog_1.log.info("Registered repo ".concat(repoInstanceName));
392
495
  }
393
496
  }
394
497
  else if (exports.autoRegisteredRepos.length > 0) {
395
- node_color_log_1.default.warn("No db configured but found repo(s)");
498
+ FlinkLog_1.log.warn("No db configured but found repo(s)");
396
499
  }
397
500
  pluginCtx = this.plugins.reduce(function (out, plugin) {
398
501
  if (out[plugin.id]) {
@@ -427,7 +530,7 @@ var FlinkApp = /** @class */ (function () {
427
530
  _a.label = 1;
428
531
  case 1:
429
532
  _a.trys.push([1, 3, , 4]);
430
- node_color_log_1.default.debug("Connecting to db");
533
+ FlinkLog_1.log.debug("Connecting to db");
431
534
  return [4 /*yield*/, mongodb_1.default.connect(this.dbOpts.uri, {
432
535
  useUnifiedTopology: true,
433
536
  connectTimeoutMS: 4000,
@@ -438,7 +541,7 @@ var FlinkApp = /** @class */ (function () {
438
541
  return [3 /*break*/, 4];
439
542
  case 3:
440
543
  err_2 = _a.sent();
441
- node_color_log_1.default.error("Failed to connect to db: " + err_2);
544
+ FlinkLog_1.log.error("Failed to connect to db: " + err_2);
442
545
  process.exit(1);
443
546
  return [3 /*break*/, 4];
444
547
  case 4:
@@ -467,7 +570,7 @@ var FlinkApp = /** @class */ (function () {
467
570
  if (!plugin.db) return [3 /*break*/, 5];
468
571
  if (!plugin.db.useHostDb) return [3 /*break*/, 1];
469
572
  if (!this.db) {
470
- node_color_log_1.default.error("Plugin '".concat(this.name, " configured to use host app db, but no db exists in FlinkApp'"));
573
+ FlinkLog_1.log.error("Plugin '".concat(this.name, " configured to use host app db, but no db exists in FlinkApp'"));
471
574
  }
472
575
  else {
473
576
  return [2 /*return*/, this.db];
@@ -478,7 +581,7 @@ var FlinkApp = /** @class */ (function () {
478
581
  _a.label = 2;
479
582
  case 2:
480
583
  _a.trys.push([2, 4, , 5]);
481
- node_color_log_1.default.debug("Connecting to '".concat(plugin.id, "' db"));
584
+ FlinkLog_1.log.debug("Connecting to '".concat(plugin.id, "' db"));
482
585
  return [4 /*yield*/, mongodb_1.default.connect(plugin.db.uri, {
483
586
  useUnifiedTopology: true,
484
587
  })];
@@ -487,7 +590,7 @@ var FlinkApp = /** @class */ (function () {
487
590
  return [2 /*return*/, client.db()];
488
591
  case 4:
489
592
  err_3 = _a.sent();
490
- node_color_log_1.default.error("Failed to connect to db defined in plugin '".concat(plugin.id, "': ") + err_3);
593
+ FlinkLog_1.log.error("Failed to connect to db defined in plugin '".concat(plugin.id, "': ") + err_3);
491
594
  return [3 /*break*/, 5];
492
595
  case 5: return [2 /*return*/];
493
596
  }
@@ -511,6 +614,14 @@ var FlinkApp = /** @class */ (function () {
511
614
  FlinkApp.prototype.getRegisteredRoutes = function () {
512
615
  return Array.from(this.handlerRouteCache.values());
513
616
  };
617
+ Object.defineProperty(FlinkApp.prototype, "isSchedulingEnabled", {
618
+ get: function () {
619
+ var _a;
620
+ return ((_a = this.schedulingOptions) === null || _a === void 0 ? void 0 : _a.enabled) !== false;
621
+ },
622
+ enumerable: false,
623
+ configurable: true
624
+ });
514
625
  return FlinkApp;
515
626
  }());
516
627
  exports.FlinkApp = FlinkApp;
@@ -0,0 +1,58 @@
1
+ import { FlinkContext } from "./FlinkContext";
2
+ export type FlinkJobProps = {
3
+ /**
4
+ * Id of job, must be unique
5
+ */
6
+ id: string;
7
+ /**
8
+ * Cron expression for scheduling job with minute precision
9
+ * Example: * * * * *
10
+ */
11
+ cron?: string;
12
+ /**
13
+ * Timezone for cron expression
14
+ * Example: Europe/Stockholm
15
+ */
16
+ timezone?: string;
17
+ /**
18
+ * Interval for scheduling job
19
+ * Uses ms syntax, i.e. 1s, 1m, 1h, 1d
20
+ */
21
+ interval?: string;
22
+ /**
23
+ * If true, job will only run once at startup after delay.
24
+ * Uses ms syntax, i.e. 1s, 1m, 1h, 1d
25
+ */
26
+ afterDelay?: string;
27
+ /**
28
+ * If true, job will only one at a time.
29
+ *
30
+ * If a job is already running, the next invocation will be ignored and will be
31
+ * retried after the next interval.
32
+ */
33
+ singleton?: boolean;
34
+ };
35
+ /**
36
+ * Type for Flink job function. This function should be default exported from
37
+ * a Job file and the body is what will be executed when the job is invoked.
38
+ */
39
+ export interface FlinkJob<C extends FlinkContext> {
40
+ (params: {
41
+ ctx: C;
42
+ }): Promise<any>;
43
+ }
44
+ /**
45
+ * Type for Job file.
46
+ *
47
+ * Describes shape of exports when using syntax like:
48
+ *
49
+ * `import * as FooJob from "./src/jobs/FooJob"
50
+ */
51
+ export type FlinkJobFile = {
52
+ default: FlinkJob<any>;
53
+ Job: FlinkJobProps;
54
+ /**
55
+ * Typescript source file name, is set at compile time by Flink compiler.
56
+ */
57
+ __file?: string;
58
+ };
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -4,5 +4,21 @@ export declare const log: {
4
4
  warn: (...args: any[]) => void;
5
5
  error: (...args: any[]) => void;
6
6
  json: (...args: any) => void;
7
+ bgColorLog: (ticket: "black" | "red" | "green" | "yellow" | "blue" | "magenta" | "cyan" | "white", text: string, setting?: {
8
+ bold?: boolean | undefined;
9
+ italic?: boolean | undefined;
10
+ dim?: boolean | undefined;
11
+ underscore?: boolean | undefined;
12
+ reverse?: boolean | undefined;
13
+ strikethrough?: boolean | undefined;
14
+ } | undefined) => void;
15
+ fontColorLog: (ticket: "black" | "red" | "green" | "yellow" | "blue" | "magenta" | "cyan" | "white", text: string, setting?: {
16
+ bold?: boolean | undefined;
17
+ italic?: boolean | undefined;
18
+ dim?: boolean | undefined;
19
+ underscore?: boolean | undefined;
20
+ reverse?: boolean | undefined;
21
+ strikethrough?: boolean | undefined;
22
+ } | undefined) => void;
7
23
  setLevel: (level: "debug" | "info" | "warn" | "error") => void;
8
24
  };