@flink-app/flink 0.4.7 → 0.5.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/dist/src/FlinkApp.d.ts +16 -0
- package/dist/src/FlinkApp.js +41 -17
- package/dist/src/utils.d.ts +7 -0
- package/dist/src/utils.js +13 -1
- package/package.json +4 -2
- package/src/FlinkApp.ts +63 -14
- package/src/utils.ts +12 -0
package/dist/src/FlinkApp.d.ts
CHANGED
|
@@ -112,6 +112,21 @@ export interface FlinkOptions {
|
|
|
112
112
|
* Only useful when starting a Flink app for testing purposes.
|
|
113
113
|
*/
|
|
114
114
|
disableHttpServer?: boolean;
|
|
115
|
+
/**
|
|
116
|
+
* Configuration for access logs.
|
|
117
|
+
*/
|
|
118
|
+
accessLog?: {
|
|
119
|
+
/**
|
|
120
|
+
* Enables access logs for all requests.
|
|
121
|
+
* Defaults to true.
|
|
122
|
+
*/
|
|
123
|
+
enabled?: boolean;
|
|
124
|
+
/**
|
|
125
|
+
* Optional custom format.
|
|
126
|
+
* Uses `morgan` format and defaults to `dev`.
|
|
127
|
+
*/
|
|
128
|
+
format?: string;
|
|
129
|
+
};
|
|
115
130
|
}
|
|
116
131
|
export interface HandlerConfig {
|
|
117
132
|
schema?: {
|
|
@@ -157,6 +172,7 @@ export declare class FlinkApp<C extends FlinkContext> {
|
|
|
157
172
|
*/
|
|
158
173
|
private handlerRouteCache;
|
|
159
174
|
scheduler?: ToadScheduler;
|
|
175
|
+
private accessLog;
|
|
160
176
|
constructor(opts: FlinkOptions);
|
|
161
177
|
get ctx(): C;
|
|
162
178
|
start(): Promise<this>;
|
package/dist/src/FlinkApp.js
CHANGED
|
@@ -57,6 +57,7 @@ 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 morgan_1 = __importDefault(require("morgan"));
|
|
60
61
|
var ms_1 = __importDefault(require("ms"));
|
|
61
62
|
var toad_scheduler_1 = require("toad-scheduler");
|
|
62
63
|
var uuid_1 = require("uuid");
|
|
@@ -110,6 +111,7 @@ var FlinkApp = /** @class */ (function () {
|
|
|
110
111
|
this.jsonOptions = opts.jsonOptions || { limit: "1mb" };
|
|
111
112
|
this.schedulingOptions = opts.scheduling;
|
|
112
113
|
this.disableHttpServer = !!opts.disableHttpServer;
|
|
114
|
+
this.accessLog = __assign({ enabled: true, format: "dev" }, opts.accessLog);
|
|
113
115
|
}
|
|
114
116
|
Object.defineProperty(FlinkApp.prototype, "ctx", {
|
|
115
117
|
get: function () {
|
|
@@ -155,6 +157,9 @@ var FlinkApp = /** @class */ (function () {
|
|
|
155
157
|
this.expressApp = express_1.default();
|
|
156
158
|
this.expressApp.use(cors_1.default(this.corsOpts));
|
|
157
159
|
this.expressApp.use(body_parser_1.default.json(this.jsonOptions));
|
|
160
|
+
if (this.accessLog.enabled) {
|
|
161
|
+
this.expressApp.use(morgan_1.default(this.accessLog.format));
|
|
162
|
+
}
|
|
158
163
|
this.expressApp.use(function (req, res, next) {
|
|
159
164
|
req.reqId = uuid_1.v4();
|
|
160
165
|
next();
|
|
@@ -281,8 +286,16 @@ var FlinkApp = /** @class */ (function () {
|
|
|
281
286
|
if (this.disableHttpServer) {
|
|
282
287
|
return;
|
|
283
288
|
}
|
|
289
|
+
var validateReq_1;
|
|
290
|
+
var validateRes_1;
|
|
291
|
+
if (schema.reqSchema) {
|
|
292
|
+
validateReq_1 = ajv.compile(schema.reqSchema);
|
|
293
|
+
}
|
|
294
|
+
if (schema.resSchema) {
|
|
295
|
+
validateRes_1 = ajv.compile(schema.resSchema);
|
|
296
|
+
}
|
|
284
297
|
this.expressApp[method](routeProps.path, function (req, res) { return __awaiter(_this, void 0, void 0, function () {
|
|
285
|
-
var
|
|
298
|
+
var valid, data, handlerRes, err_1, valid;
|
|
286
299
|
return __generator(this, function (_a) {
|
|
287
300
|
switch (_a.label) {
|
|
288
301
|
case 0:
|
|
@@ -294,18 +307,17 @@ var FlinkApp = /** @class */ (function () {
|
|
|
294
307
|
}
|
|
295
308
|
_a.label = 2;
|
|
296
309
|
case 2:
|
|
297
|
-
if (
|
|
298
|
-
|
|
299
|
-
valid = validate(req.body);
|
|
310
|
+
if (validateReq_1) {
|
|
311
|
+
valid = validateReq_1(req.body);
|
|
300
312
|
if (!valid) {
|
|
301
|
-
FlinkLog_1.log.warn(methodAndRoute_1 + ": Bad request " + JSON.stringify(
|
|
313
|
+
FlinkLog_1.log.warn(methodAndRoute_1 + ": Bad request " + JSON.stringify(validateReq_1.errors, null, 2));
|
|
302
314
|
FlinkLog_1.log.debug("Invalid json: " + JSON.stringify(req.body));
|
|
303
315
|
return [2 /*return*/, res.status(400).json({
|
|
304
316
|
status: 400,
|
|
305
317
|
error: {
|
|
306
318
|
id: uuid_1.v4(),
|
|
307
319
|
title: "Bad request",
|
|
308
|
-
detail: "Schema did not validate " + JSON.stringify(
|
|
320
|
+
detail: "Schema did not validate " + JSON.stringify(validateReq_1.errors),
|
|
309
321
|
},
|
|
310
322
|
})];
|
|
311
323
|
}
|
|
@@ -337,11 +349,10 @@ var FlinkApp = /** @class */ (function () {
|
|
|
337
349
|
console.error(err_1);
|
|
338
350
|
return [2 /*return*/, res.status(500).json(FlinkErrors_1.internalServerError(err_1))];
|
|
339
351
|
case 6:
|
|
340
|
-
if (
|
|
341
|
-
|
|
342
|
-
valid = validate(JSON.parse(JSON.stringify(handlerRes.data)));
|
|
352
|
+
if (validateRes_1 && !utils_1.isError(handlerRes)) {
|
|
353
|
+
valid = validateRes_1(JSON.parse(JSON.stringify(handlerRes.data)));
|
|
343
354
|
if (!valid) {
|
|
344
|
-
FlinkLog_1.log.warn("[" + req.reqId + "] " + methodAndRoute_1 + ": Bad response " + JSON.stringify(
|
|
355
|
+
FlinkLog_1.log.warn("[" + req.reqId + "] " + methodAndRoute_1 + ": Bad response " + JSON.stringify(validateRes_1.errors, null, 2));
|
|
345
356
|
FlinkLog_1.log.debug("Invalid json: " + JSON.stringify(handlerRes.data));
|
|
346
357
|
// log.debug(JSON.stringify(schema, null, 2));
|
|
347
358
|
return [2 /*return*/, res.status(500).json({
|
|
@@ -349,7 +360,7 @@ var FlinkApp = /** @class */ (function () {
|
|
|
349
360
|
error: {
|
|
350
361
|
id: uuid_1.v4(),
|
|
351
362
|
title: "Bad response",
|
|
352
|
-
detail: "Schema did not validate " + JSON.stringify(
|
|
363
|
+
detail: "Schema did not validate " + JSON.stringify(validateRes_1.errors),
|
|
353
364
|
},
|
|
354
365
|
})];
|
|
355
366
|
}
|
|
@@ -377,12 +388,12 @@ var FlinkApp = /** @class */ (function () {
|
|
|
377
388
|
* Will not register any handlers added programmatically.
|
|
378
389
|
*/
|
|
379
390
|
FlinkApp.prototype.registerAutoRegisterableHandlers = function () {
|
|
380
|
-
var _a, _b;
|
|
391
|
+
var _a, _b, _c;
|
|
381
392
|
return __awaiter(this, void 0, void 0, function () {
|
|
382
|
-
var _i, autoRegisteredHandlers_1,
|
|
383
|
-
return __generator(this, function (
|
|
393
|
+
var _i, autoRegisteredHandlers_1, _d, handler, assumedHttpMethod, pathParams, _e, _f, param;
|
|
394
|
+
return __generator(this, function (_g) {
|
|
384
395
|
for (_i = 0, autoRegisteredHandlers_1 = exports.autoRegisteredHandlers; _i < autoRegisteredHandlers_1.length; _i++) {
|
|
385
|
-
|
|
396
|
+
_d = autoRegisteredHandlers_1[_i], handler = _d.handler, assumedHttpMethod = _d.assumedHttpMethod;
|
|
386
397
|
if (!handler.Route) {
|
|
387
398
|
FlinkLog_1.log.error("Missing Props in handler " + handler.__file);
|
|
388
399
|
continue;
|
|
@@ -391,11 +402,24 @@ var FlinkApp = /** @class */ (function () {
|
|
|
391
402
|
FlinkLog_1.log.error("Missing exported handler function in handler " + handler.__file);
|
|
392
403
|
continue;
|
|
393
404
|
}
|
|
405
|
+
if (!!((_a = handler.__params) === null || _a === void 0 ? void 0 : _a.length)) {
|
|
406
|
+
pathParams = utils_1.getPathParams(handler.Route.path);
|
|
407
|
+
for (_e = 0, _f = handler.__params; _e < _f.length; _e++) {
|
|
408
|
+
param = _f[_e];
|
|
409
|
+
if (!pathParams.includes(param.name)) {
|
|
410
|
+
FlinkLog_1.log.error("Handler " + handler.__file + " has param " + param.name + " but it is not present in the path '" + handler.Route.path + "'");
|
|
411
|
+
throw new Error("Invalid/missing handler path param");
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
if (pathParams.length !== handler.__params.length) {
|
|
415
|
+
FlinkLog_1.log.warn("Handler " + handler.__file + " has " + handler.__params.length + " typed params but the path '" + handler.Route.path + "' has " + pathParams.length + " params");
|
|
416
|
+
}
|
|
417
|
+
}
|
|
394
418
|
this.registerHandler({
|
|
395
419
|
routeProps: __assign(__assign({}, handler.Route), { method: handler.Route.method || assumedHttpMethod, origin: this.name }),
|
|
396
420
|
schema: {
|
|
397
|
-
reqSchema: (
|
|
398
|
-
resSchema: (
|
|
421
|
+
reqSchema: (_b = handler.__schemas) === null || _b === void 0 ? void 0 : _b.reqSchema,
|
|
422
|
+
resSchema: (_c = handler.__schemas) === null || _c === void 0 ? void 0 : _c.resSchema,
|
|
399
423
|
},
|
|
400
424
|
queryMetadata: handler.__query || [],
|
|
401
425
|
paramsMetadata: handler.__params || [],
|
package/dist/src/utils.d.ts
CHANGED
|
@@ -18,3 +18,10 @@ export declare function getRepoInstanceName(fn: string): string;
|
|
|
18
18
|
*/
|
|
19
19
|
export declare function getHttpMethodFromHandlerName(handlerFilename: string): HttpMethod | undefined;
|
|
20
20
|
export declare function getJsDocComment(comment: string): string;
|
|
21
|
+
/**
|
|
22
|
+
* Returns array of path params from path string.
|
|
23
|
+
* For example `/user/:id` will return `["id"]`
|
|
24
|
+
* @param path
|
|
25
|
+
* @returns
|
|
26
|
+
*/
|
|
27
|
+
export declare function getPathParams(path: string): string[];
|
package/dist/src/utils.js
CHANGED
|
@@ -39,7 +39,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
39
39
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
40
40
|
};
|
|
41
41
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
42
|
-
exports.getJsDocComment = exports.getHttpMethodFromHandlerName = exports.getRepoInstanceName = exports.getCollectionNameForRepo = exports.getSchemaFiles = exports.getHandlerFiles = exports.isError = exports.isRouteMatch = exports.schemasPath = exports.handlersPath = void 0;
|
|
42
|
+
exports.getPathParams = exports.getJsDocComment = exports.getHttpMethodFromHandlerName = exports.getRepoInstanceName = exports.getCollectionNameForRepo = exports.getSchemaFiles = exports.getHandlerFiles = exports.isError = exports.isRouteMatch = exports.schemasPath = exports.handlersPath = void 0;
|
|
43
43
|
var path_1 = require("path");
|
|
44
44
|
var tiny_glob_1 = __importDefault(require("tiny-glob"));
|
|
45
45
|
var FlinkHttpHandler_1 = require("./FlinkHttpHandler");
|
|
@@ -149,3 +149,15 @@ function getJsDocComment(comment) {
|
|
|
149
149
|
return rows.join("\n").trim();
|
|
150
150
|
}
|
|
151
151
|
exports.getJsDocComment = getJsDocComment;
|
|
152
|
+
var pathParamsRegex = /:([a-zA-Z0-9]+)/g;
|
|
153
|
+
/**
|
|
154
|
+
* Returns array of path params from path string.
|
|
155
|
+
* For example `/user/:id` will return `["id"]`
|
|
156
|
+
* @param path
|
|
157
|
+
* @returns
|
|
158
|
+
*/
|
|
159
|
+
function getPathParams(path) {
|
|
160
|
+
var _a;
|
|
161
|
+
return ((_a = path.match(pathParamsRegex)) === null || _a === void 0 ? void 0 : _a.map(function (match) { return match.slice(1); })) || [];
|
|
162
|
+
}
|
|
163
|
+
exports.getPathParams = getPathParams;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flink-app/flink",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Typescript only framework for creating REST-like APIs on top of Express and mongodb",
|
|
5
5
|
"types": "dist/src/index.d.ts",
|
|
6
6
|
"main": "dist/src/index.js",
|
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
"mkdirp": "^1.0.4",
|
|
41
41
|
"mock-json-schema": "^1.0.8",
|
|
42
42
|
"mongodb": "^3.6.6",
|
|
43
|
+
"morgan": "^1.10.0",
|
|
43
44
|
"ms": "^2.0.0",
|
|
44
45
|
"node-color-log": "^10.0.2",
|
|
45
46
|
"passport": "^0.4.1",
|
|
@@ -57,6 +58,7 @@
|
|
|
57
58
|
"@types/jasmine": "^3.7.1",
|
|
58
59
|
"@types/json-schema": "^7.0.7",
|
|
59
60
|
"@types/mkdirp": "^1.0.1",
|
|
61
|
+
"@types/morgan": "^1.9.4",
|
|
60
62
|
"@types/node": "^15.0.1",
|
|
61
63
|
"jasmine": "^3.7.0",
|
|
62
64
|
"jasmine-spec-reporter": "^7.0.0",
|
|
@@ -65,5 +67,5 @@
|
|
|
65
67
|
"rimraf": "^3.0.2",
|
|
66
68
|
"ts-node": "^9.1.1"
|
|
67
69
|
},
|
|
68
|
-
"gitHead": "
|
|
70
|
+
"gitHead": "3c877c210da19119c074c7482e97a485d2334e80"
|
|
69
71
|
}
|
package/src/FlinkApp.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import Ajv from "ajv";
|
|
1
|
+
import Ajv, { ValidateFunction } from "ajv";
|
|
2
2
|
import addFormats from "ajv-formats";
|
|
3
3
|
import bodyParser, { OptionsJson } from "body-parser";
|
|
4
4
|
import cors from "cors";
|
|
5
5
|
import express, { Express, Request } from "express";
|
|
6
6
|
import { JSONSchema7 } from "json-schema";
|
|
7
7
|
import mongodb, { Db } from "mongodb";
|
|
8
|
+
import morgan from "morgan";
|
|
8
9
|
import ms from "ms";
|
|
9
10
|
import { AsyncTask, CronJob, SimpleIntervalJob, ToadScheduler } from "toad-scheduler";
|
|
10
11
|
import { v4 } from "uuid";
|
|
@@ -18,7 +19,7 @@ import { FlinkPlugin } from "./FlinkPlugin";
|
|
|
18
19
|
import { FlinkRepo } from "./FlinkRepo";
|
|
19
20
|
import { FlinkResponse } from "./FlinkResponse";
|
|
20
21
|
import generateMockData from "./mock-data-generator";
|
|
21
|
-
import { isError } from "./utils";
|
|
22
|
+
import { getPathParams, isError } from "./utils";
|
|
22
23
|
|
|
23
24
|
const ajv = new Ajv();
|
|
24
25
|
addFormats(ajv);
|
|
@@ -168,6 +169,23 @@ export interface FlinkOptions {
|
|
|
168
169
|
* Only useful when starting a Flink app for testing purposes.
|
|
169
170
|
*/
|
|
170
171
|
disableHttpServer?: boolean;
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Configuration for access logs.
|
|
175
|
+
*/
|
|
176
|
+
accessLog?: {
|
|
177
|
+
/**
|
|
178
|
+
* Enables access logs for all requests.
|
|
179
|
+
* Defaults to true.
|
|
180
|
+
*/
|
|
181
|
+
enabled?: boolean;
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Optional custom format.
|
|
185
|
+
* Uses `morgan` format and defaults to `dev`.
|
|
186
|
+
*/
|
|
187
|
+
format?: string;
|
|
188
|
+
};
|
|
171
189
|
}
|
|
172
190
|
|
|
173
191
|
export interface HandlerConfig {
|
|
@@ -220,6 +238,8 @@ export class FlinkApp<C extends FlinkContext> {
|
|
|
220
238
|
|
|
221
239
|
public scheduler?: ToadScheduler;
|
|
222
240
|
|
|
241
|
+
private accessLog: { enabled: boolean; format: string };
|
|
242
|
+
|
|
223
243
|
constructor(opts: FlinkOptions) {
|
|
224
244
|
this.name = opts.name;
|
|
225
245
|
this.port = opts.port || 3333;
|
|
@@ -232,6 +252,7 @@ export class FlinkApp<C extends FlinkContext> {
|
|
|
232
252
|
this.jsonOptions = opts.jsonOptions || { limit: "1mb" };
|
|
233
253
|
this.schedulingOptions = opts.scheduling;
|
|
234
254
|
this.disableHttpServer = !!opts.disableHttpServer;
|
|
255
|
+
this.accessLog = { enabled: true, format: "dev", ...opts.accessLog };
|
|
235
256
|
}
|
|
236
257
|
|
|
237
258
|
get ctx() {
|
|
@@ -267,11 +288,13 @@ export class FlinkApp<C extends FlinkContext> {
|
|
|
267
288
|
|
|
268
289
|
if (!this.disableHttpServer) {
|
|
269
290
|
this.expressApp = express();
|
|
270
|
-
|
|
271
291
|
this.expressApp.use(cors(this.corsOpts));
|
|
272
|
-
|
|
273
292
|
this.expressApp.use(bodyParser.json(this.jsonOptions));
|
|
274
293
|
|
|
294
|
+
if (this.accessLog.enabled) {
|
|
295
|
+
this.expressApp.use(morgan(this.accessLog.format));
|
|
296
|
+
}
|
|
297
|
+
|
|
275
298
|
this.expressApp.use((req, res, next) => {
|
|
276
299
|
req.reqId = v4();
|
|
277
300
|
next();
|
|
@@ -411,6 +434,17 @@ export class FlinkApp<C extends FlinkContext> {
|
|
|
411
434
|
return;
|
|
412
435
|
}
|
|
413
436
|
|
|
437
|
+
let validateReq: ValidateFunction<any> | undefined;
|
|
438
|
+
let validateRes: ValidateFunction<any> | undefined;
|
|
439
|
+
|
|
440
|
+
if (schema.reqSchema) {
|
|
441
|
+
validateReq = ajv.compile(schema.reqSchema);
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
if (schema.resSchema) {
|
|
445
|
+
validateRes = ajv.compile(schema.resSchema);
|
|
446
|
+
}
|
|
447
|
+
|
|
414
448
|
this.expressApp => {
|
|
415
449
|
if (routeProps.permissions) {
|
|
416
450
|
if (!(await this.authenticate(req, routeProps.permissions))) {
|
|
@@ -418,12 +452,11 @@ export class FlinkApp<C extends FlinkContext> {
|
|
|
418
452
|
}
|
|
419
453
|
}
|
|
420
454
|
|
|
421
|
-
if (
|
|
422
|
-
const
|
|
423
|
-
const valid = validate(req.body);
|
|
455
|
+
if (validateReq) {
|
|
456
|
+
const valid = validateReq(req.body);
|
|
424
457
|
|
|
425
458
|
if (!valid) {
|
|
426
|
-
log.warn(`${methodAndRoute}: Bad request ${JSON.stringify(
|
|
459
|
+
log.warn(`${methodAndRoute}: Bad request ${JSON.stringify(validateReq.errors, null, 2)}`);
|
|
427
460
|
|
|
428
461
|
log.debug(`Invalid json: ${JSON.stringify(req.body)}`);
|
|
429
462
|
|
|
@@ -432,7 +465,7 @@ export class FlinkApp<C extends FlinkContext> {
|
|
|
432
465
|
error: {
|
|
433
466
|
id: v4(),
|
|
434
467
|
title: "Bad request",
|
|
435
|
-
detail: `Schema did not validate ${JSON.stringify(
|
|
468
|
+
detail: `Schema did not validate ${JSON.stringify(validateReq.errors)}`,
|
|
436
469
|
},
|
|
437
470
|
});
|
|
438
471
|
}
|
|
@@ -465,12 +498,11 @@ export class FlinkApp<C extends FlinkContext> {
|
|
|
465
498
|
return res.status(500).json(internalServerError(err as any));
|
|
466
499
|
}
|
|
467
500
|
|
|
468
|
-
if (
|
|
469
|
-
const
|
|
470
|
-
const valid = validate(JSON.parse(JSON.stringify(handlerRes.data)));
|
|
501
|
+
if (validateRes && !isError(handlerRes)) {
|
|
502
|
+
const valid = validateRes(JSON.parse(JSON.stringify(handlerRes.data)));
|
|
471
503
|
|
|
472
504
|
if (!valid) {
|
|
473
|
-
log.warn(`[${req.reqId}] ${methodAndRoute}: Bad response ${JSON.stringify(
|
|
505
|
+
log.warn(`[${req.reqId}] ${methodAndRoute}: Bad response ${JSON.stringify(validateRes.errors, null, 2)}`);
|
|
474
506
|
log.debug(`Invalid json: ${JSON.stringify(handlerRes.data)}`);
|
|
475
507
|
// log.debug(JSON.stringify(schema, null, 2));
|
|
476
508
|
|
|
@@ -479,7 +511,7 @@ export class FlinkApp<C extends FlinkContext> {
|
|
|
479
511
|
error: {
|
|
480
512
|
id: v4(),
|
|
481
513
|
title: "Bad response",
|
|
482
|
-
detail: `Schema did not validate ${JSON.stringify(
|
|
514
|
+
detail: `Schema did not validate ${JSON.stringify(validateRes.errors)}`,
|
|
483
515
|
},
|
|
484
516
|
});
|
|
485
517
|
}
|
|
@@ -518,6 +550,23 @@ export class FlinkApp<C extends FlinkContext> {
|
|
|
518
550
|
continue;
|
|
519
551
|
}
|
|
520
552
|
|
|
553
|
+
if (!!handler.__params?.length) {
|
|
554
|
+
const pathParams = getPathParams(handler.Route.path);
|
|
555
|
+
|
|
556
|
+
for (const param of handler.__params) {
|
|
557
|
+
if (!pathParams.includes(param.name)) {
|
|
558
|
+
log.error(`Handler ${handler.__file} has param ${param.name} but it is not present in the path '${handler.Route.path}'`);
|
|
559
|
+
throw new Error("Invalid/missing handler path param");
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
if (pathParams.length !== handler.__params.length) {
|
|
564
|
+
log.warn(
|
|
565
|
+
`Handler ${handler.__file} has ${handler.__params.length} typed params but the path '${handler.Route.path}' has ${pathParams.length} params`
|
|
566
|
+
);
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
|
|
521
570
|
this.registerHandler(
|
|
522
571
|
{
|
|
523
572
|
routeProps: {
|
package/src/utils.ts
CHANGED
|
@@ -88,3 +88,15 @@ export function getJsDocComment(comment: string) {
|
|
|
88
88
|
|
|
89
89
|
return rows.join("\n").trim();
|
|
90
90
|
}
|
|
91
|
+
|
|
92
|
+
const pathParamsRegex = /:([a-zA-Z0-9]+)/g;
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Returns array of path params from path string.
|
|
96
|
+
* For example `/user/:id` will return `["id"]`
|
|
97
|
+
* @param path
|
|
98
|
+
* @returns
|
|
99
|
+
*/
|
|
100
|
+
export function getPathParams(path: string) {
|
|
101
|
+
return path.match(pathParamsRegex)?.map((match) => match.slice(1)) || [];
|
|
102
|
+
}
|