@naturalcycles/backend-lib 5.1.0 → 5.3.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/db/httpDBRequestHandler.js +10 -10
- package/dist/index.d.ts +3 -3
- package/dist/index.js +3 -3
- package/dist/server/genericErrorMiddleware.js +0 -1
- package/dist/server/server.model.d.ts +1 -0
- package/dist/server/{validateMiddleware.d.ts → validation/validateMiddleware.d.ts} +2 -2
- package/dist/server/{validateMiddleware.js → validation/validateMiddleware.js} +1 -1
- package/dist/server/{reqValidationMiddleware.d.ts → validation/validateRequest.d.ts} +9 -2
- package/dist/server/{reqValidationMiddleware.js → validation/validateRequest.js} +35 -21
- package/dist/server/{zodValidateMiddleware.d.ts → validation/zodValidateMiddleware.d.ts} +2 -2
- package/dist/server/{zodValidateMiddleware.js → validation/zodValidateMiddleware.js} +1 -1
- package/package.json +2 -2
- package/src/db/httpDBRequestHandler.ts +11 -11
- package/src/index.ts +3 -3
- package/src/server/genericErrorMiddleware.ts +0 -1
- package/src/server/{validateMiddleware.ts → validation/validateMiddleware.ts} +4 -3
- package/src/server/{reqValidationMiddleware.ts → validation/validateRequest.ts} +56 -28
- package/src/server/{zodValidateMiddleware.ts → validation/zodValidateMiddleware.ts} +4 -3
|
@@ -49,25 +49,25 @@ function httpDBRequestHandler(db) {
|
|
|
49
49
|
//
|
|
50
50
|
// })
|
|
51
51
|
// getByIds
|
|
52
|
-
router.put('/getByIds',
|
|
53
|
-
const { table, ids, opt } =
|
|
52
|
+
router.put('/getByIds', async (req, res) => {
|
|
53
|
+
const { table, ids, opt } = __1.validateRequest.body(req, getByIdsInputSchema);
|
|
54
54
|
res.json(await db.getByIds(table, ids, opt));
|
|
55
55
|
});
|
|
56
56
|
// runQuery
|
|
57
|
-
router.put('/runQuery',
|
|
58
|
-
const { query, opt } =
|
|
57
|
+
router.put('/runQuery', async (req, res) => {
|
|
58
|
+
const { query, opt } = __1.validateRequest.body(req, runQueryInputSchema);
|
|
59
59
|
const q = db_lib_1.DBQuery.fromPlainObject(query);
|
|
60
60
|
res.json(await db.runQuery(q, opt));
|
|
61
61
|
});
|
|
62
62
|
// runQueryCount
|
|
63
|
-
router.put('/runQueryCount',
|
|
64
|
-
const { query, opt } =
|
|
63
|
+
router.put('/runQueryCount', async (req, res) => {
|
|
64
|
+
const { query, opt } = __1.validateRequest.body(req, runQueryInputSchema);
|
|
65
65
|
const q = db_lib_1.DBQuery.fromPlainObject(query);
|
|
66
66
|
res.json(await db.runQueryCount(q, opt));
|
|
67
67
|
});
|
|
68
68
|
// saveBatch
|
|
69
|
-
router.put('/saveBatch',
|
|
70
|
-
const { table, rows, opt } =
|
|
69
|
+
router.put('/saveBatch', async (req, res) => {
|
|
70
|
+
const { table, rows, opt } = __1.validateRequest.body(req, saveBatchInputSchema);
|
|
71
71
|
await db.saveBatch(table, rows, opt);
|
|
72
72
|
res.end();
|
|
73
73
|
});
|
|
@@ -77,8 +77,8 @@ function httpDBRequestHandler(db) {
|
|
|
77
77
|
// res.json(await db.deleteByIds(table, ids, opt))
|
|
78
78
|
// })
|
|
79
79
|
// deleteByQuery
|
|
80
|
-
router.put('/deleteByQuery',
|
|
81
|
-
const { query, opt } =
|
|
80
|
+
router.put('/deleteByQuery', async (req, res) => {
|
|
81
|
+
const { query, opt } = __1.validateRequest.body(req, runQueryInputSchema);
|
|
82
82
|
const q = db_lib_1.DBQuery.fromPlainObject(query);
|
|
83
83
|
res.json(await db.deleteByQuery(q, opt));
|
|
84
84
|
});
|
package/dist/index.d.ts
CHANGED
|
@@ -20,11 +20,11 @@ export * from './server/notFoundMiddleware';
|
|
|
20
20
|
export * from './server/okMiddleware';
|
|
21
21
|
export * from './server/basicAuthMiddleware';
|
|
22
22
|
export * from './server/requestTimeoutMiddleware';
|
|
23
|
-
export * from './server/
|
|
23
|
+
export * from './server/validation/validateRequest';
|
|
24
24
|
export * from './server/simpleRequestLoggerMiddleware';
|
|
25
25
|
export * from './server/serverStatusMiddleware';
|
|
26
|
-
export * from './server/validateMiddleware';
|
|
27
|
-
export * from './server/zodValidateMiddleware';
|
|
26
|
+
export * from './server/validation/validateMiddleware';
|
|
27
|
+
export * from './server/validation/zodValidateMiddleware';
|
|
28
28
|
export * from './server/request.log.util';
|
|
29
29
|
export * from './server/startServer';
|
|
30
30
|
export * from './server/startServer.model';
|
package/dist/index.js
CHANGED
|
@@ -25,11 +25,11 @@ tslib_1.__exportStar(require("./server/notFoundMiddleware"), exports);
|
|
|
25
25
|
tslib_1.__exportStar(require("./server/okMiddleware"), exports);
|
|
26
26
|
tslib_1.__exportStar(require("./server/basicAuthMiddleware"), exports);
|
|
27
27
|
tslib_1.__exportStar(require("./server/requestTimeoutMiddleware"), exports);
|
|
28
|
-
tslib_1.__exportStar(require("./server/
|
|
28
|
+
tslib_1.__exportStar(require("./server/validation/validateRequest"), exports);
|
|
29
29
|
tslib_1.__exportStar(require("./server/simpleRequestLoggerMiddleware"), exports);
|
|
30
30
|
tslib_1.__exportStar(require("./server/serverStatusMiddleware"), exports);
|
|
31
|
-
tslib_1.__exportStar(require("./server/validateMiddleware"), exports);
|
|
32
|
-
tslib_1.__exportStar(require("./server/zodValidateMiddleware"), exports);
|
|
31
|
+
tslib_1.__exportStar(require("./server/validation/validateMiddleware"), exports);
|
|
32
|
+
tslib_1.__exportStar(require("./server/validation/zodValidateMiddleware"), exports);
|
|
33
33
|
tslib_1.__exportStar(require("./server/request.log.util"), exports);
|
|
34
34
|
tslib_1.__exportStar(require("./server/startServer"), exports);
|
|
35
35
|
tslib_1.__exportStar(require("./server/startServer.model"), exports);
|
|
@@ -57,7 +57,6 @@ function respondWithError(req, res, err) {
|
|
|
57
57
|
// Otherwise, it breaks the _isHttpErrorResponse function check, and error get formatted/detected wrongly
|
|
58
58
|
httpError.data['httpStatusCode'] = httpError.data.backendResponseStatusCode;
|
|
59
59
|
httpError.data.headersSent = headersSent || undefined;
|
|
60
|
-
httpError.data.report ||= undefined; // set to undefined if false
|
|
61
60
|
(0, js_lib_1._filterUndefinedValues)(httpError.data, true);
|
|
62
61
|
formatError?.(httpError); // Mutates
|
|
63
62
|
res.status(httpError.data.backendResponseStatusCode).json({
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { JsonSchema, JsonSchemaBuilder } from '@naturalcycles/js-lib';
|
|
2
2
|
import { AjvSchema, AjvValidationError } from '@naturalcycles/nodejs-lib';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { BackendRequestHandler } from '../server.model';
|
|
4
|
+
import { ReqValidationOptions } from './validateRequest';
|
|
5
5
|
export declare function validateBody(schema: JsonSchema | JsonSchemaBuilder | AjvSchema, opt?: ReqValidationOptions<AjvValidationError>): BackendRequestHandler;
|
|
6
6
|
export declare function validateParams(schema: JsonSchema | JsonSchemaBuilder | AjvSchema, opt?: ReqValidationOptions<AjvValidationError>): BackendRequestHandler;
|
|
7
7
|
export declare function validateQuery(schema: JsonSchema | JsonSchemaBuilder | AjvSchema, opt?: ReqValidationOptions<AjvValidationError>): BackendRequestHandler;
|
|
@@ -26,7 +26,7 @@ function validateObject(prop, schema, opt = {}) {
|
|
|
26
26
|
const ajvSchema = nodejs_lib_1.AjvSchema.create(schema, {
|
|
27
27
|
objectName: `request ${prop}`,
|
|
28
28
|
});
|
|
29
|
-
const reportPredicate = typeof opt.report === 'function' ? opt.report : () =>
|
|
29
|
+
const reportPredicate = typeof opt.report === 'function' ? opt.report : () => opt.report;
|
|
30
30
|
return (req, res, next) => {
|
|
31
31
|
const error = ajvSchema.getValidationError(req[prop]);
|
|
32
32
|
if (error) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AnySchema, JoiValidationError } from '@naturalcycles/nodejs-lib';
|
|
2
|
-
import {
|
|
2
|
+
import { BackendRequest } from '../server.model';
|
|
3
3
|
export interface ReqValidationOptions<ERR extends Error> {
|
|
4
4
|
/**
|
|
5
5
|
* Pass a 'dot-paths' (e.g `pw`, or `input.pw`) that needs to be redacted from the output, in case of error.
|
|
@@ -12,4 +12,11 @@ export interface ReqValidationOptions<ERR extends Error> {
|
|
|
12
12
|
*/
|
|
13
13
|
report?: boolean | ((err: ERR) => boolean);
|
|
14
14
|
}
|
|
15
|
-
|
|
15
|
+
declare class ValidateRequest {
|
|
16
|
+
body<T>(req: BackendRequest, schema: AnySchema<T>, opt?: ReqValidationOptions<JoiValidationError>): T;
|
|
17
|
+
query<T>(req: BackendRequest, schema: AnySchema<T>, opt?: ReqValidationOptions<JoiValidationError>): T;
|
|
18
|
+
params<T>(req: BackendRequest, schema: AnySchema<T>, opt?: ReqValidationOptions<JoiValidationError>): T;
|
|
19
|
+
private validate;
|
|
20
|
+
}
|
|
21
|
+
export declare const validateRequest: ValidateRequest;
|
|
22
|
+
export {};
|
|
@@ -1,40 +1,54 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.validateRequest = void 0;
|
|
4
4
|
const js_lib_1 = require("@naturalcycles/js-lib");
|
|
5
5
|
const nodejs_lib_1 = require("@naturalcycles/nodejs-lib");
|
|
6
6
|
const REDACTED = 'REDACTED';
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
/**
|
|
8
|
+
* Mutates error
|
|
9
|
+
*/
|
|
10
|
+
function redact(redactPaths, obj, error) {
|
|
11
|
+
redactPaths
|
|
12
|
+
.map(path => (0, js_lib_1._get)(obj, path))
|
|
13
|
+
.filter(Boolean)
|
|
14
|
+
.forEach(secret => {
|
|
15
|
+
error.message = error.message.replace(secret, REDACTED);
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
class ValidateRequest {
|
|
19
|
+
body(req, schema, opt = {}) {
|
|
20
|
+
return this.validate(req, 'body', schema, opt);
|
|
21
|
+
}
|
|
22
|
+
query(req, schema, opt = {}) {
|
|
23
|
+
return this.validate(req, 'query', schema, opt);
|
|
24
|
+
}
|
|
25
|
+
params(req, schema, opt = {}) {
|
|
26
|
+
return this.validate(req, 'params', schema, opt);
|
|
27
|
+
}
|
|
28
|
+
validate(req, reqProperty, schema, opt = {}) {
|
|
10
29
|
const { value, error } = (0, nodejs_lib_1.getValidationResult)(req[reqProperty], schema, `request ${reqProperty}`);
|
|
11
30
|
if (error) {
|
|
12
|
-
|
|
31
|
+
let report;
|
|
32
|
+
if (typeof opt.report === 'boolean') {
|
|
33
|
+
report = opt.report;
|
|
34
|
+
}
|
|
35
|
+
else if (typeof opt.report === 'function') {
|
|
36
|
+
report = opt.report(error);
|
|
37
|
+
}
|
|
13
38
|
if (opt.redactPaths) {
|
|
14
39
|
redact(opt.redactPaths, req[reqProperty], error);
|
|
15
40
|
error.data.joiValidationErrorItems.length = 0; // clears the array
|
|
16
41
|
delete error.data.annotation;
|
|
17
42
|
}
|
|
18
|
-
|
|
43
|
+
throw new js_lib_1.AppError(error.message, {
|
|
19
44
|
backendResponseStatusCode: 400,
|
|
20
45
|
report,
|
|
21
46
|
...error.data,
|
|
22
|
-
})
|
|
47
|
+
});
|
|
23
48
|
}
|
|
24
49
|
// mutate req to replace the property with the value, converted by Joi
|
|
25
50
|
req[reqProperty] = value;
|
|
26
|
-
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
exports.reqValidation = reqValidation;
|
|
30
|
-
/**
|
|
31
|
-
* Mutates error
|
|
32
|
-
*/
|
|
33
|
-
function redact(redactPaths, obj, error) {
|
|
34
|
-
redactPaths
|
|
35
|
-
.map(path => (0, js_lib_1._get)(obj, path))
|
|
36
|
-
.filter(Boolean)
|
|
37
|
-
.forEach(secret => {
|
|
38
|
-
error.message = error.message.replace(secret, REDACTED);
|
|
39
|
-
});
|
|
51
|
+
return value;
|
|
52
|
+
}
|
|
40
53
|
}
|
|
54
|
+
exports.validateRequest = new ValidateRequest();
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ZodSchema, ZodValidationError } from '@naturalcycles/js-lib';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { BackendRequestHandler } from '../server.model';
|
|
3
|
+
import { ReqValidationOptions } from './validateRequest';
|
|
4
4
|
/**
|
|
5
5
|
* Validates req property (body, params or query).
|
|
6
6
|
* Supports Joi schema or AjvSchema (from nodejs-lib).
|
|
@@ -10,7 +10,7 @@ const REDACTED = 'REDACTED';
|
|
|
10
10
|
* Throws http 400 on error.
|
|
11
11
|
*/
|
|
12
12
|
function zodReqValidate(prop, schema, opt = {}) {
|
|
13
|
-
const reportPredicate = typeof opt.report === 'function' ? opt.report : () =>
|
|
13
|
+
const reportPredicate = typeof opt.report === 'function' ? opt.report : () => opt.report;
|
|
14
14
|
return (req, res, next) => {
|
|
15
15
|
const { error } = (0, js_lib_1.zSafeValidate)(req[prop], schema);
|
|
16
16
|
if (!error) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@naturalcycles/backend-lib",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.3.0",
|
|
4
4
|
"scripts": {
|
|
5
5
|
"prepare": "husky",
|
|
6
6
|
"dev": "APP_ENV=dev nodemon",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"yargs": "^17.0.0"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
|
-
"@naturalcycles/bench-lib": "^
|
|
39
|
+
"@naturalcycles/bench-lib": "^2.0.0",
|
|
40
40
|
"@naturalcycles/dev-lib": "^13.0.0",
|
|
41
41
|
"@sentry/node": "^7.0.0",
|
|
42
42
|
"@types/ejs": "^3.0.0",
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
} from '@naturalcycles/db-lib/dist/validation'
|
|
13
13
|
import { ObjectWithId } from '@naturalcycles/js-lib'
|
|
14
14
|
import { anyObjectSchema, arraySchema, objectSchema, stringSchema } from '@naturalcycles/nodejs-lib'
|
|
15
|
-
import { BackendRouter, getDefaultRouter,
|
|
15
|
+
import { BackendRouter, getDefaultRouter, validateRequest } from '..'
|
|
16
16
|
|
|
17
17
|
export interface GetByIdsInput {
|
|
18
18
|
table: string
|
|
@@ -84,28 +84,28 @@ export function httpDBRequestHandler(db: CommonDB): BackendRouter {
|
|
|
84
84
|
// })
|
|
85
85
|
|
|
86
86
|
// getByIds
|
|
87
|
-
router.put('/getByIds',
|
|
88
|
-
const { table, ids, opt } =
|
|
87
|
+
router.put('/getByIds', async (req, res) => {
|
|
88
|
+
const { table, ids, opt } = validateRequest.body(req, getByIdsInputSchema)
|
|
89
89
|
res.json(await db.getByIds(table, ids, opt))
|
|
90
90
|
})
|
|
91
91
|
|
|
92
92
|
// runQuery
|
|
93
|
-
router.put('/runQuery',
|
|
94
|
-
const { query, opt } =
|
|
93
|
+
router.put('/runQuery', async (req, res) => {
|
|
94
|
+
const { query, opt } = validateRequest.body(req, runQueryInputSchema)
|
|
95
95
|
const q = DBQuery.fromPlainObject(query)
|
|
96
96
|
res.json(await db.runQuery(q, opt))
|
|
97
97
|
})
|
|
98
98
|
|
|
99
99
|
// runQueryCount
|
|
100
|
-
router.put('/runQueryCount',
|
|
101
|
-
const { query, opt } =
|
|
100
|
+
router.put('/runQueryCount', async (req, res) => {
|
|
101
|
+
const { query, opt } = validateRequest.body(req, runQueryInputSchema)
|
|
102
102
|
const q = DBQuery.fromPlainObject(query)
|
|
103
103
|
res.json(await db.runQueryCount(q, opt))
|
|
104
104
|
})
|
|
105
105
|
|
|
106
106
|
// saveBatch
|
|
107
|
-
router.put('/saveBatch',
|
|
108
|
-
const { table, rows, opt } =
|
|
107
|
+
router.put('/saveBatch', async (req, res) => {
|
|
108
|
+
const { table, rows, opt } = validateRequest.body(req, saveBatchInputSchema)
|
|
109
109
|
await db.saveBatch(table, rows, opt)
|
|
110
110
|
res.end()
|
|
111
111
|
})
|
|
@@ -117,8 +117,8 @@ export function httpDBRequestHandler(db: CommonDB): BackendRouter {
|
|
|
117
117
|
// })
|
|
118
118
|
|
|
119
119
|
// deleteByQuery
|
|
120
|
-
router.put('/deleteByQuery',
|
|
121
|
-
const { query, opt } =
|
|
120
|
+
router.put('/deleteByQuery', async (req, res) => {
|
|
121
|
+
const { query, opt } = validateRequest.body(req, runQueryInputSchema)
|
|
122
122
|
const q = DBQuery.fromPlainObject(query)
|
|
123
123
|
res.json(await db.deleteByQuery(q, opt))
|
|
124
124
|
})
|
package/src/index.ts
CHANGED
|
@@ -20,11 +20,11 @@ export * from './server/notFoundMiddleware'
|
|
|
20
20
|
export * from './server/okMiddleware'
|
|
21
21
|
export * from './server/basicAuthMiddleware'
|
|
22
22
|
export * from './server/requestTimeoutMiddleware'
|
|
23
|
-
export * from './server/
|
|
23
|
+
export * from './server/validation/validateRequest'
|
|
24
24
|
export * from './server/simpleRequestLoggerMiddleware'
|
|
25
25
|
export * from './server/serverStatusMiddleware'
|
|
26
|
-
export * from './server/validateMiddleware'
|
|
27
|
-
export * from './server/zodValidateMiddleware'
|
|
26
|
+
export * from './server/validation/validateMiddleware'
|
|
27
|
+
export * from './server/validation/zodValidateMiddleware'
|
|
28
28
|
export * from './server/request.log.util'
|
|
29
29
|
export * from './server/startServer'
|
|
30
30
|
export * from './server/startServer.model'
|
|
@@ -90,7 +90,6 @@ export function respondWithError(req: BackendRequest, res: BackendResponse, err:
|
|
|
90
90
|
// Otherwise, it breaks the _isHttpErrorResponse function check, and error get formatted/detected wrongly
|
|
91
91
|
httpError.data['httpStatusCode'] = httpError.data.backendResponseStatusCode
|
|
92
92
|
httpError.data.headersSent = headersSent || undefined
|
|
93
|
-
httpError.data.report ||= undefined // set to undefined if false
|
|
94
93
|
_filterUndefinedValues(httpError.data, true)
|
|
95
94
|
|
|
96
95
|
formatError?.(httpError) // Mutates
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { JsonSchema, JsonSchemaBuilder, _get, AppError } from '@naturalcycles/js-lib'
|
|
2
2
|
import { AjvSchema, AjvValidationError } from '@naturalcycles/nodejs-lib'
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { BackendRequestHandler } from '../server.model'
|
|
4
|
+
import { ReqValidationOptions } from './validateRequest'
|
|
5
5
|
|
|
6
6
|
const REDACTED = 'REDACTED'
|
|
7
7
|
|
|
@@ -41,7 +41,8 @@ function validateObject(
|
|
|
41
41
|
objectName: `request ${prop}`,
|
|
42
42
|
})
|
|
43
43
|
|
|
44
|
-
const reportPredicate =
|
|
44
|
+
const reportPredicate =
|
|
45
|
+
typeof opt.report === 'function' ? opt.report : () => opt.report as boolean | undefined
|
|
45
46
|
|
|
46
47
|
return (req, res, next) => {
|
|
47
48
|
const error = ajvSchema.getValidationError(req[prop])
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { _get, AppError } from '@naturalcycles/js-lib'
|
|
2
2
|
import { AnySchema, getValidationResult, JoiValidationError } from '@naturalcycles/nodejs-lib'
|
|
3
|
-
import {
|
|
3
|
+
import { BackendRequest } from '../server.model'
|
|
4
4
|
|
|
5
5
|
const REDACTED = 'REDACTED'
|
|
6
6
|
|
|
@@ -18,17 +18,57 @@ export interface ReqValidationOptions<ERR extends Error> {
|
|
|
18
18
|
report?: boolean | ((err: ERR) => boolean)
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
21
|
+
/**
|
|
22
|
+
* Mutates error
|
|
23
|
+
*/
|
|
24
|
+
function redact(redactPaths: string[], obj: any, error: Error): void {
|
|
25
|
+
redactPaths
|
|
26
|
+
.map(path => _get(obj, path) as string)
|
|
27
|
+
.filter(Boolean)
|
|
28
|
+
.forEach(secret => {
|
|
29
|
+
error.message = error.message.replace(secret, REDACTED)
|
|
30
|
+
})
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
class ValidateRequest {
|
|
34
|
+
body<T>(
|
|
35
|
+
req: BackendRequest,
|
|
36
|
+
schema: AnySchema<T>,
|
|
37
|
+
opt: ReqValidationOptions<JoiValidationError> = {},
|
|
38
|
+
): T {
|
|
39
|
+
return this.validate(req, 'body', schema, opt)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
query<T>(
|
|
43
|
+
req: BackendRequest,
|
|
44
|
+
schema: AnySchema<T>,
|
|
45
|
+
opt: ReqValidationOptions<JoiValidationError> = {},
|
|
46
|
+
): T {
|
|
47
|
+
return this.validate(req, 'query', schema, opt)
|
|
48
|
+
}
|
|
27
49
|
|
|
28
|
-
|
|
50
|
+
params<T>(
|
|
51
|
+
req: BackendRequest,
|
|
52
|
+
schema: AnySchema<T>,
|
|
53
|
+
opt: ReqValidationOptions<JoiValidationError> = {},
|
|
54
|
+
): T {
|
|
55
|
+
return this.validate(req, 'params', schema, opt)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
private validate<T>(
|
|
59
|
+
req: BackendRequest,
|
|
60
|
+
reqProperty: 'body' | 'params' | 'query',
|
|
61
|
+
schema: AnySchema<T>,
|
|
62
|
+
opt: ReqValidationOptions<JoiValidationError> = {},
|
|
63
|
+
): T {
|
|
29
64
|
const { value, error } = getValidationResult(req[reqProperty], schema, `request ${reqProperty}`)
|
|
30
65
|
if (error) {
|
|
31
|
-
|
|
66
|
+
let report: boolean | undefined
|
|
67
|
+
if (typeof opt.report === 'boolean') {
|
|
68
|
+
report = opt.report
|
|
69
|
+
} else if (typeof opt.report === 'function') {
|
|
70
|
+
report = opt.report(error)
|
|
71
|
+
}
|
|
32
72
|
|
|
33
73
|
if (opt.redactPaths) {
|
|
34
74
|
redact(opt.redactPaths, req[reqProperty], error)
|
|
@@ -36,29 +76,17 @@ export function reqValidation(
|
|
|
36
76
|
delete error.data.annotation
|
|
37
77
|
}
|
|
38
78
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
}),
|
|
45
|
-
)
|
|
79
|
+
throw new AppError(error.message, {
|
|
80
|
+
backendResponseStatusCode: 400,
|
|
81
|
+
report,
|
|
82
|
+
...error.data,
|
|
83
|
+
})
|
|
46
84
|
}
|
|
47
85
|
|
|
48
86
|
// mutate req to replace the property with the value, converted by Joi
|
|
49
87
|
req[reqProperty] = value
|
|
50
|
-
|
|
88
|
+
return value
|
|
51
89
|
}
|
|
52
90
|
}
|
|
53
91
|
|
|
54
|
-
|
|
55
|
-
* Mutates error
|
|
56
|
-
*/
|
|
57
|
-
function redact(redactPaths: string[], obj: any, error: Error): void {
|
|
58
|
-
redactPaths
|
|
59
|
-
.map(path => _get(obj, path) as string)
|
|
60
|
-
.filter(Boolean)
|
|
61
|
-
.forEach(secret => {
|
|
62
|
-
error.message = error.message.replace(secret, REDACTED)
|
|
63
|
-
})
|
|
64
|
-
}
|
|
92
|
+
export const validateRequest = new ValidateRequest()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { _get, AppError, ZodSchema, ZodValidationError, zSafeValidate } from '@naturalcycles/js-lib'
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { BackendRequestHandler } from '../server.model'
|
|
3
|
+
import { ReqValidationOptions } from './validateRequest'
|
|
4
4
|
|
|
5
5
|
const REDACTED = 'REDACTED'
|
|
6
6
|
|
|
@@ -15,7 +15,8 @@ export function zodReqValidate(
|
|
|
15
15
|
schema: ZodSchema,
|
|
16
16
|
opt: ReqValidationOptions<ZodValidationError<any>> = {},
|
|
17
17
|
): BackendRequestHandler {
|
|
18
|
-
const reportPredicate =
|
|
18
|
+
const reportPredicate =
|
|
19
|
+
typeof opt.report === 'function' ? opt.report : () => opt.report as boolean | undefined
|
|
19
20
|
|
|
20
21
|
return (req, res, next) => {
|
|
21
22
|
const { error } = zSafeValidate(req[prop], schema)
|