@flink-app/flink 0.12.1-alpha.3 → 0.12.1-alpha.35
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/dist/src/FlinkApp.d.ts +4 -1
- package/dist/src/FlinkApp.js +52 -12
- package/dist/src/FlinkHttpHandler.d.ts +14 -1
- package/dist/src/FlinkRepo.d.ts +23 -7
- package/dist/src/FlinkRepo.js +32 -10
- package/dist/src/TypeScriptCompiler.d.ts +10 -0
- package/dist/src/TypeScriptCompiler.js +91 -18
- package/package.json +7 -4
- package/spec/FlinkRepo.spec.ts +11 -0
- package/spec/TypeScriptCompiler.spec.ts +8 -0
- package/spec/mock-project/dist/src/handlers/GetCar.js +2 -0
- package/spec/mock-project/dist/src/handlers/GetCar2.js +2 -0
- package/spec/mock-project/dist/src/handlers/GetCarWithArraySchema.js +2 -0
- package/spec/mock-project/dist/src/handlers/GetCarWithArraySchema2.js +2 -0
- package/spec/mock-project/dist/src/handlers/GetCarWithArraySchema3.js +2 -0
- package/spec/mock-project/dist/src/handlers/GetCarWithLiteralSchema.js +2 -0
- package/spec/mock-project/dist/src/handlers/GetCarWithLiteralSchema2.js +2 -0
- package/spec/mock-project/dist/src/handlers/GetCarWithSchemaInFile.js +2 -0
- package/spec/mock-project/dist/src/handlers/GetCarWithSchemaInFile2.js +2 -0
- package/spec/mock-project/dist/src/handlers/ManuallyAddedHandler.js +2 -0
- package/spec/mock-project/dist/src/handlers/ManuallyAddedHandler2.js +2 -0
- package/spec/mock-project/dist/src/handlers/PostCar.js +2 -0
- package/spec/mock-project/dist/src/handlers/PostLogin.js +2 -0
- package/spec/mock-project/dist/src/handlers/PostLogout.js +57 -0
- package/spec/mock-project/dist/src/handlers/PutCar.js +2 -0
- package/spec/mock-project/src/handlers/PostLogout.ts +19 -0
- package/src/FlinkApp.ts +44 -6
- package/src/FlinkHttpHandler.ts +95 -96
- package/src/FlinkRepo.ts +24 -7
- package/src/TypeScriptCompiler.ts +81 -23
|
@@ -53,3 +53,5 @@ var GetCarWithLiteralSchema = function (_a) { return __awaiter(void 0, [_a], voi
|
|
|
53
53
|
exports.default = GetCarWithLiteralSchema;
|
|
54
54
|
exports.__assumedHttpMethod = "get", exports.__file = "GetCarWithLiteralSchema.ts", exports.__query = [], exports.__params = [];
|
|
55
55
|
exports.__schemas = { reqSchema: undefined, resSchema: { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "properties": { "car": { "type": "object", "properties": { "model": { "type": "string" }, "metadata": { "type": "object", "properties": { "created": { "type": "string", "format": "date-time" } }, "additionalProperties": false } }, "required": ["model"], "additionalProperties": false } }, "required": ["car"], "additionalProperties": false, "definitions": {} } };
|
|
56
|
+
exports.__assumedHttpMethod = "get", exports.__file = "GetCarWithLiteralSchema.ts", exports.__query = [], exports.__params = [];
|
|
57
|
+
exports.__schemas = { reqSchema: undefined, resSchema: { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "properties": { "car": { "type": "object", "properties": { "model": { "type": "string" }, "metadata": { "type": "object", "properties": { "created": { "type": "string", "format": "date-time" } }, "additionalProperties": false } }, "required": ["model"], "additionalProperties": false } }, "required": ["car"], "additionalProperties": false, "definitions": {} } };
|
|
@@ -53,3 +53,5 @@ var GetCarWithLiteralSchema2 = function (_a) { return __awaiter(void 0, [_a], vo
|
|
|
53
53
|
exports.default = GetCarWithLiteralSchema2;
|
|
54
54
|
exports.__assumedHttpMethod = "get", exports.__file = "GetCarWithLiteralSchema2.ts", exports.__query = [], exports.__params = [];
|
|
55
55
|
exports.__schemas = { reqSchema: undefined, resSchema: { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "properties": { "car": { "type": "object", "properties": { "nestedCar": { "type": "object", "properties": { "model": { "type": "string" }, "metadata": { "type": "object", "properties": { "created": { "type": "string", "format": "date-time" } }, "additionalProperties": false } }, "required": ["model"], "additionalProperties": false } }, "required": ["nestedCar"], "additionalProperties": false } }, "required": ["car"], "additionalProperties": false, "definitions": {} } };
|
|
56
|
+
exports.__assumedHttpMethod = "get", exports.__file = "GetCarWithLiteralSchema2.ts", exports.__query = [], exports.__params = [];
|
|
57
|
+
exports.__schemas = { reqSchema: undefined, resSchema: { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "properties": { "car": { "type": "object", "properties": { "nestedCar": { "type": "object", "properties": { "model": { "type": "string" }, "metadata": { "type": "object", "properties": { "created": { "type": "string", "format": "date-time" } }, "additionalProperties": false } }, "required": ["model"], "additionalProperties": false } }, "required": ["nestedCar"], "additionalProperties": false } }, "required": ["car"], "additionalProperties": false, "definitions": {} } };
|
|
@@ -56,3 +56,5 @@ var GetCarWithSchemaInFile = function (_a) { return __awaiter(void 0, [_a], void
|
|
|
56
56
|
exports.default = GetCarWithSchemaInFile;
|
|
57
57
|
exports.__assumedHttpMethod = "get", exports.__file = "GetCarWithSchemaInFile.ts", exports.__query = [], exports.__params = [];
|
|
58
58
|
exports.__schemas = { reqSchema: undefined, resSchema: { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "properties": { "model": { "type": "string" } }, "required": ["model"], "additionalProperties": false, "definitions": {} } };
|
|
59
|
+
exports.__assumedHttpMethod = "get", exports.__file = "GetCarWithSchemaInFile.ts", exports.__query = [], exports.__params = [];
|
|
60
|
+
exports.__schemas = { reqSchema: undefined, resSchema: { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "properties": { "model": { "type": "string" } }, "required": ["model"], "additionalProperties": false, "definitions": {} } };
|
|
@@ -56,3 +56,5 @@ var GetCarWithSchemaInFile2 = function (_a) { return __awaiter(void 0, [_a], voi
|
|
|
56
56
|
exports.default = GetCarWithSchemaInFile2;
|
|
57
57
|
exports.__assumedHttpMethod = "get", exports.__file = "GetCarWithSchemaInFile2.ts", exports.__query = [], exports.__params = [];
|
|
58
58
|
exports.__schemas = { reqSchema: undefined, resSchema: { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "properties": { "car": { "type": "object", "properties": { "model": { "type": "string" }, "metadata": { "type": "object", "properties": { "created": { "type": "string", "format": "date-time" } }, "additionalProperties": false } }, "required": ["model"], "additionalProperties": false } }, "required": ["car"], "additionalProperties": false, "definitions": {} } };
|
|
59
|
+
exports.__assumedHttpMethod = "get", exports.__file = "GetCarWithSchemaInFile2.ts", exports.__query = [], exports.__params = [];
|
|
60
|
+
exports.__schemas = { reqSchema: undefined, resSchema: { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "properties": { "car": { "type": "object", "properties": { "model": { "type": "string" }, "metadata": { "type": "object", "properties": { "created": { "type": "string", "format": "date-time" } }, "additionalProperties": false } }, "required": ["model"], "additionalProperties": false } }, "required": ["car"], "additionalProperties": false, "definitions": {} } };
|
|
@@ -52,3 +52,5 @@ var manuallyAddedHandler = function (_a) { return __awaiter(void 0, [_a], void 0
|
|
|
52
52
|
exports.default = manuallyAddedHandler;
|
|
53
53
|
exports.__assumedHttpMethod = "", exports.__file = "ManuallyAddedHandler.ts", exports.__query = [], exports.__params = [];
|
|
54
54
|
exports.__schemas = { reqSchema: { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "additionalProperties": false, "properties": { "model": { "type": "string" }, "metadata": { "type": "object", "properties": { "created": { "type": "string", "format": "date-time" } }, "additionalProperties": false } }, "required": ["model"], "definitions": {} }, resSchema: undefined };
|
|
55
|
+
exports.__assumedHttpMethod = "", exports.__file = "ManuallyAddedHandler.ts", exports.__query = [], exports.__params = [];
|
|
56
|
+
exports.__schemas = { reqSchema: { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "additionalProperties": false, "properties": { "model": { "type": "string" }, "metadata": { "type": "object", "properties": { "created": { "type": "string", "format": "date-time" } }, "additionalProperties": false } }, "required": ["model"], "definitions": {} }, resSchema: undefined };
|
|
@@ -54,3 +54,5 @@ var manuallyAddedHandler = function (_a) { return __awaiter(void 0, [_a], void 0
|
|
|
54
54
|
exports.default = manuallyAddedHandler;
|
|
55
55
|
exports.__assumedHttpMethod = "", exports.__file = "ManuallyAddedHandler2.ts", exports.__query = [], exports.__params = [];
|
|
56
56
|
exports.__schemas = { reqSchema: { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "additionalProperties": false, "properties": { "model": { "type": "string" }, "metadata": { "type": "object", "properties": { "created": { "type": "string", "format": "date-time" } }, "additionalProperties": false } }, "required": ["model"], "definitions": {} }, resSchema: undefined };
|
|
57
|
+
exports.__assumedHttpMethod = "", exports.__file = "ManuallyAddedHandler2.ts", exports.__query = [], exports.__params = [];
|
|
58
|
+
exports.__schemas = { reqSchema: { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "additionalProperties": false, "properties": { "model": { "type": "string" }, "metadata": { "type": "object", "properties": { "created": { "type": "string", "format": "date-time" } }, "additionalProperties": false } }, "required": ["model"], "definitions": {} }, resSchema: undefined };
|
|
@@ -53,3 +53,5 @@ var PostCar = function (_a) { return __awaiter(void 0, [_a], void 0, function (_
|
|
|
53
53
|
exports.default = PostCar;
|
|
54
54
|
exports.__assumedHttpMethod = "post", exports.__file = "PostCar.ts", exports.__query = [], exports.__params = [];
|
|
55
55
|
exports.__schemas = { reqSchema: { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "additionalProperties": false, "properties": { "model": { "type": "string" }, "metadata": { "type": "object", "properties": { "created": { "type": "string", "format": "date-time" } }, "additionalProperties": false } }, "required": ["model"], "definitions": {} }, resSchema: { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "additionalProperties": false, "properties": { "model": { "type": "string" }, "metadata": { "type": "object", "properties": { "created": { "type": "string", "format": "date-time" } }, "additionalProperties": false } }, "required": ["model"], "definitions": {} } };
|
|
56
|
+
exports.__assumedHttpMethod = "post", exports.__file = "PostCar.ts", exports.__query = [], exports.__params = [];
|
|
57
|
+
exports.__schemas = { reqSchema: { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "additionalProperties": false, "properties": { "model": { "type": "string" }, "metadata": { "type": "object", "properties": { "created": { "type": "string", "format": "date-time" } }, "additionalProperties": false } }, "required": ["model"], "definitions": {} }, resSchema: { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "additionalProperties": false, "properties": { "model": { "type": "string" }, "metadata": { "type": "object", "properties": { "created": { "type": "string", "format": "date-time" } }, "additionalProperties": false } }, "required": ["model"], "definitions": {} } };
|
|
@@ -54,3 +54,5 @@ var PostLogin = function (_a) { return __awaiter(void 0, [_a], void 0, function
|
|
|
54
54
|
exports.default = PostLogin;
|
|
55
55
|
exports.__assumedHttpMethod = "post", exports.__file = "PostLogin.ts", exports.__query = [], exports.__params = [];
|
|
56
56
|
exports.__schemas = { reqSchema: undefined, resSchema: undefined };
|
|
57
|
+
exports.__assumedHttpMethod = "post", exports.__file = "PostLogin.ts", exports.__query = [], exports.__params = [];
|
|
58
|
+
exports.__schemas = { reqSchema: undefined, resSchema: undefined };
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
12
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
|
13
|
+
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
14
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
15
|
+
function step(op) {
|
|
16
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
17
|
+
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
18
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
19
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
20
|
+
switch (op[0]) {
|
|
21
|
+
case 0: case 1: t = op; break;
|
|
22
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
23
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
24
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
25
|
+
default:
|
|
26
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
27
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
28
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
29
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
30
|
+
if (t[2]) _.ops.pop();
|
|
31
|
+
_.trys.pop(); continue;
|
|
32
|
+
}
|
|
33
|
+
op = body.call(thisArg, _);
|
|
34
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
35
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.__schemas = exports.__params = exports.__query = exports.__file = exports.__assumedHttpMethod = exports.Route = void 0;
|
|
40
|
+
exports.Route = {
|
|
41
|
+
path: "/logout",
|
|
42
|
+
};
|
|
43
|
+
var PostLogout = function (_a) { return __awaiter(void 0, [_a], void 0, function (_b) {
|
|
44
|
+
var ctx = _b.ctx, req = _b.req;
|
|
45
|
+
return __generator(this, function (_c) {
|
|
46
|
+
return [2 /*return*/, {
|
|
47
|
+
data: {
|
|
48
|
+
success: true,
|
|
49
|
+
},
|
|
50
|
+
}];
|
|
51
|
+
});
|
|
52
|
+
}); };
|
|
53
|
+
exports.default = PostLogout;
|
|
54
|
+
exports.__assumedHttpMethod = "post", exports.__file = "PostLogout.ts", exports.__query = [], exports.__params = [];
|
|
55
|
+
exports.__schemas = { reqSchema: { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "additionalProperties": false, "definitions": {} }, resSchema: { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "properties": { "success": { "type": "boolean" } }, "required": ["success"], "additionalProperties": false, "definitions": {} } };
|
|
56
|
+
exports.__assumedHttpMethod = "post", exports.__file = "PostLogout.ts", exports.__query = [], exports.__params = [];
|
|
57
|
+
exports.__schemas = { reqSchema: { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "additionalProperties": false, "definitions": {} }, resSchema: { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "properties": { "success": { "type": "boolean" } }, "required": ["success"], "additionalProperties": false, "definitions": {} } };
|
|
@@ -53,3 +53,5 @@ var PutCar = function (_a) { return __awaiter(void 0, [_a], void 0, function (_b
|
|
|
53
53
|
exports.default = PutCar;
|
|
54
54
|
exports.__assumedHttpMethod = "put", exports.__file = "PutCar.ts", exports.__query = [], exports.__params = [];
|
|
55
55
|
exports.__schemas = { reqSchema: { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "additionalProperties": false, "properties": { "model": { "type": "string" }, "metadata": { "type": "object", "properties": { "created": { "type": "string", "format": "date-time" } }, "additionalProperties": false } }, "required": ["model"], "definitions": {} }, resSchema: undefined };
|
|
56
|
+
exports.__assumedHttpMethod = "put", exports.__file = "PutCar.ts", exports.__query = [], exports.__params = [];
|
|
57
|
+
exports.__schemas = { reqSchema: { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "additionalProperties": false, "properties": { "model": { "type": "string" }, "metadata": { "type": "object", "properties": { "created": { "type": "string", "format": "date-time" } }, "additionalProperties": false } }, "required": ["model"], "definitions": {} }, resSchema: undefined };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Handler, RouteProps } from "@flink-app/flink";
|
|
2
|
+
|
|
3
|
+
interface LogoutResponse {
|
|
4
|
+
success: boolean;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export const Route: RouteProps = {
|
|
8
|
+
path: "/logout",
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const PostLogout: Handler<any, {}, LogoutResponse> = async ({ ctx, req }) => {
|
|
12
|
+
return {
|
|
13
|
+
data: {
|
|
14
|
+
success: true,
|
|
15
|
+
},
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export default PostLogout;
|
package/src/FlinkApp.ts
CHANGED
|
@@ -12,7 +12,7 @@ import { v4 } from "uuid";
|
|
|
12
12
|
import { FlinkAuthPlugin } from "./auth/FlinkAuthPlugin";
|
|
13
13
|
import { FlinkContext } from "./FlinkContext";
|
|
14
14
|
import { internalServerError, notFound, unauthorized } from "./FlinkErrors";
|
|
15
|
-
import { Handler, HandlerFile, HttpMethod, QueryParamMetadata, RouteProps } from "./FlinkHttpHandler";
|
|
15
|
+
import { FlinkRequest, Handler, HandlerFile, HttpMethod, QueryParamMetadata, RouteProps } from "./FlinkHttpHandler";
|
|
16
16
|
import { FlinkJobFile } from "./FlinkJob";
|
|
17
17
|
import { log } from "./FlinkLog";
|
|
18
18
|
import { FlinkPlugin } from "./FlinkPlugin";
|
|
@@ -217,6 +217,7 @@ export class FlinkApp<C extends FlinkContext> {
|
|
|
217
217
|
public name: string;
|
|
218
218
|
public expressApp?: Express;
|
|
219
219
|
public db?: Db;
|
|
220
|
+
public dbClient?: MongoClient;
|
|
220
221
|
public handlers: HandlerConfig[] = [];
|
|
221
222
|
public port?: number;
|
|
222
223
|
public started = false;
|
|
@@ -234,6 +235,7 @@ export class FlinkApp<C extends FlinkContext> {
|
|
|
234
235
|
private rawContentTypes?: string[];
|
|
235
236
|
private schedulingOptions?: FlinkOptions["scheduling"];
|
|
236
237
|
private disableHttpServer = false;
|
|
238
|
+
private expressServer: any; // for simplicity, we don't want to import types from express/node here
|
|
237
239
|
|
|
238
240
|
private repos: { [x: string]: FlinkRepo<C, any> } = {};
|
|
239
241
|
|
|
@@ -367,7 +369,7 @@ export class FlinkApp<C extends FlinkContext> {
|
|
|
367
369
|
log.info("🚧 HTTP server is disabled, but flink app is running");
|
|
368
370
|
this.started = true;
|
|
369
371
|
} else {
|
|
370
|
-
this.expressApp?.listen(this.port, () => {
|
|
372
|
+
this.expressServer = this.expressApp?.listen(this.port, () => {
|
|
371
373
|
log.fontColorLog("magenta", `⚡️ HTTP server '${this.name}' is running and waiting for connections on ${this.port}`);
|
|
372
374
|
this.started = true;
|
|
373
375
|
});
|
|
@@ -376,6 +378,28 @@ export class FlinkApp<C extends FlinkContext> {
|
|
|
376
378
|
return this;
|
|
377
379
|
}
|
|
378
380
|
|
|
381
|
+
async stop() {
|
|
382
|
+
log.info("🛑 Stopping Flink app...");
|
|
383
|
+
|
|
384
|
+
if (this.scheduler) {
|
|
385
|
+
await this.scheduler.stop();
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
if (this.expressServer) {
|
|
389
|
+
return new Promise<void>((resolve, reject) => {
|
|
390
|
+
const int = setTimeout(() => {
|
|
391
|
+
reject("Failed to stop HTTP server in time");
|
|
392
|
+
}, 2000);
|
|
393
|
+
|
|
394
|
+
this.expressServer.close(() => {
|
|
395
|
+
clearInterval(int);
|
|
396
|
+
log.info("HTTP server stopped");
|
|
397
|
+
resolve();
|
|
398
|
+
});
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
379
403
|
/**
|
|
380
404
|
* Manually registers a handler.
|
|
381
405
|
*
|
|
@@ -506,7 +530,7 @@ export class FlinkApp<C extends FlinkContext> {
|
|
|
506
530
|
try {
|
|
507
531
|
// 👇 This is where the actual handler gets invoked
|
|
508
532
|
handlerRes = await handler({
|
|
509
|
-
req,
|
|
533
|
+
req: req as FlinkRequest,
|
|
510
534
|
ctx: this.ctx,
|
|
511
535
|
origin: routeProps.origin,
|
|
512
536
|
});
|
|
@@ -570,7 +594,7 @@ export class FlinkApp<C extends FlinkContext> {
|
|
|
570
594
|
* Will not register any handlers added programmatically.
|
|
571
595
|
*/
|
|
572
596
|
private async registerAutoRegisterableHandlers() {
|
|
573
|
-
for (const { handler, assumedHttpMethod } of autoRegisteredHandlers) {
|
|
597
|
+
for (const { handler, assumedHttpMethod } of autoRegisteredHandlers.sort((a, b) => (a.handler.Route?.order || 0) - (b.handler.Route?.order || 0))) {
|
|
574
598
|
if (!handler.Route) {
|
|
575
599
|
log.error(`Missing Props in handler ${handler.__file}`);
|
|
576
600
|
continue;
|
|
@@ -717,7 +741,7 @@ export class FlinkApp<C extends FlinkContext> {
|
|
|
717
741
|
private async buildContext() {
|
|
718
742
|
if (this.dbOpts) {
|
|
719
743
|
for (const { collectionName, repoInstanceName, Repo } of autoRegisteredRepos) {
|
|
720
|
-
const repoInstance: FlinkRepo<C, any> = new Repo(collectionName, this.db);
|
|
744
|
+
const repoInstance: FlinkRepo<C, any> = new Repo(collectionName, this.db, this.dbClient);
|
|
721
745
|
|
|
722
746
|
this.repos[repoInstanceName] = repoInstance;
|
|
723
747
|
|
|
@@ -753,8 +777,10 @@ export class FlinkApp<C extends FlinkContext> {
|
|
|
753
777
|
if (this.dbOpts) {
|
|
754
778
|
try {
|
|
755
779
|
log.debug("Connecting to db");
|
|
780
|
+
|
|
756
781
|
const client = await MongoClient.connect(this.dbOpts.uri, this.getMongoConnectionOptions());
|
|
757
782
|
this.db = client.db();
|
|
783
|
+
this.dbClient = client;
|
|
758
784
|
} catch (err) {
|
|
759
785
|
log.error("Failed to connect to db: " + err);
|
|
760
786
|
process.exit(1);
|
|
@@ -797,7 +823,7 @@ export class FlinkApp<C extends FlinkContext> {
|
|
|
797
823
|
if (!this.auth) {
|
|
798
824
|
throw new Error(`Attempting to authenticate request (${req.method} ${req.path}) but no authPlugin is set`);
|
|
799
825
|
}
|
|
800
|
-
return await this.auth.authenticateRequest(req, permissions);
|
|
826
|
+
return await this.auth.authenticateRequest(req as FlinkRequest, permissions);
|
|
801
827
|
}
|
|
802
828
|
|
|
803
829
|
public getRegisteredRoutes() {
|
|
@@ -813,6 +839,18 @@ export class FlinkApp<C extends FlinkContext> {
|
|
|
813
839
|
throw new Error("No db configured");
|
|
814
840
|
}
|
|
815
841
|
|
|
842
|
+
const { version: driverVersion } = require("mongodb/package.json");
|
|
843
|
+
|
|
844
|
+
if (driverVersion.startsWith("3")) {
|
|
845
|
+
log.debug(`Using legacy mongodb connection options as mongo client is version ${driverVersion}`);
|
|
846
|
+
return {
|
|
847
|
+
useNewUrlParser: true,
|
|
848
|
+
useUnifiedTopology: true,
|
|
849
|
+
};
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
log.debug(`Using modern MongoDB client options (driver version ${driverVersion})`);
|
|
853
|
+
|
|
816
854
|
return {
|
|
817
855
|
serverApi: {
|
|
818
856
|
version: ServerApiVersion.v1,
|
package/src/FlinkHttpHandler.ts
CHANGED
|
@@ -5,24 +5,25 @@ import { FlinkError } from "./FlinkErrors";
|
|
|
5
5
|
import { FlinkResponse } from "./FlinkResponse";
|
|
6
6
|
|
|
7
7
|
export enum HttpMethod {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
get = "get",
|
|
9
|
+
post = "post",
|
|
10
|
+
put = "put",
|
|
11
|
+
delete = "delete",
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
type Params = Request["params"];
|
|
15
|
-
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Query type for request query parameters.
|
|
18
|
+
* Does currently not allow nested objects, although
|
|
19
|
+
* underlying express Request does allow it.
|
|
20
|
+
*/
|
|
21
|
+
type Query = Record<string, string | string[] | undefined>;
|
|
16
22
|
|
|
17
23
|
/**
|
|
18
24
|
* Flink request extends express Request but adds reqId and user object.
|
|
19
25
|
*/
|
|
20
|
-
export type FlinkRequest<T = any, P = Params, Q = Query> = Request<
|
|
21
|
-
P,
|
|
22
|
-
any,
|
|
23
|
-
T,
|
|
24
|
-
Q
|
|
25
|
-
> & { reqId: string; user?: any };
|
|
26
|
+
export type FlinkRequest<T = any, P = Params, Q = Query> = Request<P, any, T, Q> & { reqId: string; user?: any };
|
|
26
27
|
|
|
27
28
|
/**
|
|
28
29
|
* Route props to control routing.
|
|
@@ -31,66 +32,69 @@ export type FlinkRequest<T = any, P = Params, Q = Query> = Request<
|
|
|
31
32
|
* instructs express web server how to route traffic.
|
|
32
33
|
*/
|
|
33
34
|
export interface RouteProps {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
35
|
+
/**
|
|
36
|
+
* HTTP method which this handlers responds to.
|
|
37
|
+
*
|
|
38
|
+
* Will if not set attempt to extract HTTP method based
|
|
39
|
+
* on handler file name prefix, for example `GetFoo.ts` will assume
|
|
40
|
+
* HTTP method `GET`.
|
|
41
|
+
*/
|
|
42
|
+
method?: HttpMethod;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Route path including any path params.
|
|
46
|
+
* Example: `/user/:id`
|
|
47
|
+
*/
|
|
48
|
+
path: string;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Generates mock response based on handlers response schema.
|
|
52
|
+
*
|
|
53
|
+
* Will be ignored if handler does not have any response schema defined.
|
|
54
|
+
*
|
|
55
|
+
* This should only be used during development 💥
|
|
56
|
+
*/
|
|
57
|
+
mockApi?: boolean;
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Set permissions needed to access route if route requires authentication.
|
|
61
|
+
*/
|
|
62
|
+
permissions?: string | string[];
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Optional documentation of endpoint. Can be used for example in API docs.
|
|
66
|
+
* Supports markdown strings.
|
|
67
|
+
*/
|
|
68
|
+
docs?: string; // TODO
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* If handler should not be auto registered
|
|
72
|
+
*/
|
|
73
|
+
skipAutoRegister?: boolean;
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* I.e. filename or plugin name that describes where handler origins from
|
|
77
|
+
*/
|
|
78
|
+
origin?: string;
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Order handler should be registered in.
|
|
82
|
+
*
|
|
83
|
+
* By default all handlers has order 0 and in most cases this is fine,
|
|
84
|
+
* but if for example you want to register a handler before all others
|
|
85
|
+
* to avoid conflicts you can set a negative order.
|
|
86
|
+
*/
|
|
87
|
+
order?: number;
|
|
78
88
|
}
|
|
79
89
|
|
|
80
90
|
/**
|
|
81
91
|
* Http handler function that handlers implements in order to
|
|
82
92
|
* handle HTTP requests and return a JSON response.
|
|
83
93
|
*/
|
|
84
|
-
export type Handler<
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
P extends Params = Params,
|
|
89
|
-
Q extends Query = Query
|
|
90
|
-
> = (props: {
|
|
91
|
-
req: FlinkRequest<ReqSchema, P, Q>;
|
|
92
|
-
ctx: Ctx;
|
|
93
|
-
origin?: string;
|
|
94
|
+
export type Handler<Ctx extends FlinkContext, ReqSchema = any, ResSchema = any, P extends Params = Params, Q extends Query = Query> = (props: {
|
|
95
|
+
req: FlinkRequest<ReqSchema, P, Q>;
|
|
96
|
+
ctx: Ctx;
|
|
97
|
+
origin?: string;
|
|
94
98
|
}) => Promise<FlinkResponse<ResSchema | FlinkError>>;
|
|
95
99
|
|
|
96
100
|
/**
|
|
@@ -99,12 +103,7 @@ export type Handler<
|
|
|
99
103
|
*
|
|
100
104
|
* Just syntactic sugar on top op `HandlerFn`
|
|
101
105
|
*/
|
|
102
|
-
export type GetHandler<
|
|
103
|
-
Ctx extends FlinkContext,
|
|
104
|
-
ResSchema = any,
|
|
105
|
-
P extends Params = Params,
|
|
106
|
-
Q extends Query = Query
|
|
107
|
-
> = Handler<Ctx, any, ResSchema, P, Q>;
|
|
106
|
+
export type GetHandler<Ctx extends FlinkContext, ResSchema = any, P extends Params = Params, Q extends Query = Query> = Handler<Ctx, any, ResSchema, P, Q>;
|
|
108
107
|
|
|
109
108
|
/**
|
|
110
109
|
* Type for Handler file. Describes shape of exports when using
|
|
@@ -113,32 +112,32 @@ export type GetHandler<
|
|
|
113
112
|
* `import * as FooHandler from "./src/handlers/FooHandler"
|
|
114
113
|
*/
|
|
115
114
|
export type HandlerFile = {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
115
|
+
default: Handler<any, any, any, any, any>;
|
|
116
|
+
Route?: RouteProps;
|
|
117
|
+
/**
|
|
118
|
+
* Name of schemas, is set at compile time by Flink compiler.
|
|
119
|
+
*/
|
|
120
|
+
__schemas?: {
|
|
121
|
+
reqSchema?: JSONSchema;
|
|
122
|
+
resSchema?: JSONSchema;
|
|
123
|
+
};
|
|
124
|
+
/**
|
|
125
|
+
* Typescript source file name, is set at compile time by Flink compiler.
|
|
126
|
+
*/
|
|
127
|
+
__file?: string;
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Description of query params, is set at compile time by Flink compiler.
|
|
131
|
+
*/
|
|
132
|
+
__query?: QueryParamMetadata[];
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Description of path params, is set at compile time by Flink compiler.
|
|
136
|
+
*/
|
|
137
|
+
__params?: QueryParamMetadata[];
|
|
139
138
|
};
|
|
140
139
|
|
|
141
140
|
export type QueryParamMetadata = {
|
|
142
|
-
|
|
143
|
-
|
|
141
|
+
name: string;
|
|
142
|
+
description: string;
|
|
144
143
|
};
|
package/src/FlinkRepo.ts
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
|
-
import { Collection, Db, Document, InsertOneResult, ObjectId } from "mongodb";
|
|
1
|
+
import { Collection, Db, Document, InsertOneResult, MongoClient, ObjectId } from "mongodb";
|
|
2
2
|
import { FlinkContext } from "./FlinkContext";
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* Partial model to have intellisense for partial updates but
|
|
6
|
+
* also allow any other properties to be set such as nested objects etc.
|
|
7
|
+
*/
|
|
8
|
+
type PartialModel<Model> = Partial<Model> & { [x: string]: any };
|
|
9
|
+
|
|
4
10
|
export abstract class FlinkRepo<C extends FlinkContext, Model extends Document> {
|
|
5
11
|
collection: Collection;
|
|
6
12
|
|
|
@@ -15,7 +21,7 @@ export abstract class FlinkRepo<C extends FlinkContext, Model extends Document>
|
|
|
15
21
|
return this._ctx;
|
|
16
22
|
}
|
|
17
23
|
|
|
18
|
-
constructor(
|
|
24
|
+
constructor(public collectionName: string, public db: Db, public client?: MongoClient) {
|
|
19
25
|
this.collection = db.collection(this.collectionName);
|
|
20
26
|
}
|
|
21
27
|
|
|
@@ -45,10 +51,12 @@ export abstract class FlinkRepo<C extends FlinkContext, Model extends Document>
|
|
|
45
51
|
return { ...model, _id: result.insertedId.toString() };
|
|
46
52
|
}
|
|
47
53
|
|
|
48
|
-
async updateOne(id: string | ObjectId, model:
|
|
54
|
+
async updateOne(id: string | ObjectId, model: PartialModel<Model>): Promise<Model | null> {
|
|
49
55
|
const oid = this.buildId(id);
|
|
50
56
|
|
|
51
|
-
|
|
57
|
+
const { _id, ...modelWithoutId } = model;
|
|
58
|
+
|
|
59
|
+
await this.collection.updateOne({ _id: oid }, { $set: modelWithoutId });
|
|
52
60
|
|
|
53
61
|
const res = await this.collection.findOne<Model>({ _id: oid });
|
|
54
62
|
|
|
@@ -58,9 +66,11 @@ export abstract class FlinkRepo<C extends FlinkContext, Model extends Document>
|
|
|
58
66
|
return null;
|
|
59
67
|
}
|
|
60
68
|
|
|
61
|
-
async updateMany<U =
|
|
69
|
+
async updateMany<U = PartialModel<Model>>(query: any, model: U): Promise<number> {
|
|
70
|
+
const { _id, ...modelWithoutId } = model as any;
|
|
71
|
+
|
|
62
72
|
const { modifiedCount } = await this.collection.updateMany(query, {
|
|
63
|
-
$set:
|
|
73
|
+
$set: modelWithoutId as any,
|
|
64
74
|
});
|
|
65
75
|
return modifiedCount;
|
|
66
76
|
}
|
|
@@ -72,7 +82,14 @@ export abstract class FlinkRepo<C extends FlinkContext, Model extends Document>
|
|
|
72
82
|
return deletedCount || 0;
|
|
73
83
|
}
|
|
74
84
|
|
|
75
|
-
|
|
85
|
+
/**
|
|
86
|
+
* Helper to ensure the id is always an ObjectId.
|
|
87
|
+
* If a string is passed, it will be converted to an ObjectId.
|
|
88
|
+
* If an ObjectId is passed, it will be returned as is.
|
|
89
|
+
* @param id
|
|
90
|
+
* @returns
|
|
91
|
+
*/
|
|
92
|
+
buildId(id: string | ObjectId) {
|
|
76
93
|
let oid: ObjectId | string;
|
|
77
94
|
|
|
78
95
|
if (typeof id === "string") {
|