@naturalcycles/backend-lib 9.9.0 → 9.10.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/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/server/validation/ajvValidateRequest.d.ts +21 -0
- package/dist/server/validation/ajvValidateRequest.js +71 -0
- package/dist/server/validation/validateRequest.js +12 -12
- package/dist/server/validation/zodValidateRequest.d.ts +21 -0
- package/dist/server/validation/zodValidateRequest.js +68 -0
- package/package.json +1 -1
- package/src/index.ts +2 -2
- package/src/server/validation/ajvValidateRequest.ts +107 -0
- package/src/server/validation/validateRequest.ts +14 -14
- package/src/server/validation/zodValidateRequest.ts +107 -0
- package/dist/server/validation/validateMiddleware.d.ts +0 -9
- package/dist/server/validation/validateMiddleware.js +0 -54
- package/dist/server/validation/zodValidateMiddleware.d.ts +0 -10
- package/dist/server/validation/zodValidateMiddleware.js +0 -37
- package/src/server/validation/validateMiddleware.ts +0 -89
- package/src/server/validation/zodValidateMiddleware.ts +0 -54
package/dist/index.d.ts
CHANGED
|
@@ -28,8 +28,8 @@ export * from './server/serverStatusMiddleware.js';
|
|
|
28
28
|
export * from './server/simpleRequestLoggerMiddleware.js';
|
|
29
29
|
export * from './server/startServer.js';
|
|
30
30
|
export * from './server/startServer.model.js';
|
|
31
|
-
export * from './server/validation/
|
|
31
|
+
export * from './server/validation/ajvValidateRequest.js';
|
|
32
32
|
export * from './server/validation/validateRequest.js';
|
|
33
|
-
export * from './server/validation/
|
|
33
|
+
export * from './server/validation/zodValidateRequest.js';
|
|
34
34
|
export * from './util.js';
|
|
35
35
|
export { onFinished };
|
package/dist/index.js
CHANGED
|
@@ -28,8 +28,8 @@ export * from './server/serverStatusMiddleware.js';
|
|
|
28
28
|
export * from './server/simpleRequestLoggerMiddleware.js';
|
|
29
29
|
export * from './server/startServer.js';
|
|
30
30
|
export * from './server/startServer.model.js';
|
|
31
|
-
export * from './server/validation/
|
|
31
|
+
export * from './server/validation/ajvValidateRequest.js';
|
|
32
32
|
export * from './server/validation/validateRequest.js';
|
|
33
|
-
export * from './server/validation/
|
|
33
|
+
export * from './server/validation/zodValidateRequest.js';
|
|
34
34
|
export * from './util.js';
|
|
35
35
|
export { onFinished };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { AjvSchema, AjvValidationError } from '@naturalcycles/nodejs-lib';
|
|
2
|
+
import type { BackendRequest } from '../server.model.js';
|
|
3
|
+
import type { ReqValidationOptions } from './validateRequest.js';
|
|
4
|
+
declare class AjvValidateRequest {
|
|
5
|
+
body<T>(req: BackendRequest, schema: AjvSchema<T>, opt?: ReqValidationOptions<AjvValidationError>): T;
|
|
6
|
+
query<T>(req: BackendRequest, schema: AjvSchema<T>, opt?: ReqValidationOptions<AjvValidationError>): T;
|
|
7
|
+
params<T>(req: BackendRequest, schema: AjvSchema<T>, opt?: ReqValidationOptions<AjvValidationError>): T;
|
|
8
|
+
/**
|
|
9
|
+
* Validates `req.headers` against the provided schema.
|
|
10
|
+
*
|
|
11
|
+
* Note: as opposed to other methods, this method does not mutate `req.headers` in case of success,
|
|
12
|
+
* i.e. schemas that cast values will not have any effect.
|
|
13
|
+
*
|
|
14
|
+
* If you wish to mutate `req.headers` with the validated value, use `keepOriginal: false` option.
|
|
15
|
+
* Keep in mind that this will also remove all values that are not in the schema.
|
|
16
|
+
*/
|
|
17
|
+
headers<T>(req: BackendRequest, schema: AjvSchema<T>, opt?: ReqValidationOptions<AjvValidationError>): T;
|
|
18
|
+
private validate;
|
|
19
|
+
}
|
|
20
|
+
export declare const ajvValidateRequest: AjvValidateRequest;
|
|
21
|
+
export {};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { _get, AppError } from '@naturalcycles/js-lib';
|
|
2
|
+
class AjvValidateRequest {
|
|
3
|
+
body(req, schema, opt = {}) {
|
|
4
|
+
return this.validate(req, 'body', schema, opt);
|
|
5
|
+
}
|
|
6
|
+
query(req, schema, opt = {}) {
|
|
7
|
+
return this.validate(req, 'query', schema, opt);
|
|
8
|
+
}
|
|
9
|
+
params(req, schema, opt = {}) {
|
|
10
|
+
return this.validate(req, 'params', schema, opt);
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Validates `req.headers` against the provided schema.
|
|
14
|
+
*
|
|
15
|
+
* Note: as opposed to other methods, this method does not mutate `req.headers` in case of success,
|
|
16
|
+
* i.e. schemas that cast values will not have any effect.
|
|
17
|
+
*
|
|
18
|
+
* If you wish to mutate `req.headers` with the validated value, use `keepOriginal: false` option.
|
|
19
|
+
* Keep in mind that this will also remove all values that are not in the schema.
|
|
20
|
+
*/
|
|
21
|
+
headers(req, schema, opt = {}) {
|
|
22
|
+
const options = {
|
|
23
|
+
keepOriginal: true,
|
|
24
|
+
...opt,
|
|
25
|
+
};
|
|
26
|
+
return this.validate(req, 'headers', schema, options);
|
|
27
|
+
}
|
|
28
|
+
validate(req, reqProperty, schema, opt = {}) {
|
|
29
|
+
const value = { ...req[reqProperty] }; // destructure to avoid being mutated by Ajv
|
|
30
|
+
// It will mutate the `value`, but not the original object
|
|
31
|
+
const error = schema.getValidationError(value, {
|
|
32
|
+
objectName: `request ${reqProperty}`,
|
|
33
|
+
});
|
|
34
|
+
if (error) {
|
|
35
|
+
let report;
|
|
36
|
+
if (typeof opt.report === 'boolean') {
|
|
37
|
+
report = opt.report;
|
|
38
|
+
}
|
|
39
|
+
else if (typeof opt.report === 'function') {
|
|
40
|
+
report = opt.report(error);
|
|
41
|
+
}
|
|
42
|
+
if (opt.redactPaths) {
|
|
43
|
+
redact(opt.redactPaths, req[reqProperty], error);
|
|
44
|
+
}
|
|
45
|
+
throw new AppError(error.message, {
|
|
46
|
+
backendResponseStatusCode: 400,
|
|
47
|
+
report,
|
|
48
|
+
...error.data,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
// mutate req to replace the property with the value, converted by Joi
|
|
52
|
+
if (!opt.keepOriginal && reqProperty !== 'query') {
|
|
53
|
+
// query is read-only in Express 5
|
|
54
|
+
req[reqProperty] = value;
|
|
55
|
+
}
|
|
56
|
+
return value;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
export const ajvValidateRequest = new AjvValidateRequest();
|
|
60
|
+
const REDACTED = 'REDACTED';
|
|
61
|
+
/**
|
|
62
|
+
* Mutates error
|
|
63
|
+
*/
|
|
64
|
+
function redact(redactPaths, obj, error) {
|
|
65
|
+
redactPaths
|
|
66
|
+
.map(path => _get(obj, path))
|
|
67
|
+
.filter(Boolean)
|
|
68
|
+
.forEach(secret => {
|
|
69
|
+
error.message = error.message.replaceAll(secret, REDACTED);
|
|
70
|
+
});
|
|
71
|
+
}
|
|
@@ -1,17 +1,5 @@
|
|
|
1
1
|
import { _get, AppError } from '@naturalcycles/js-lib';
|
|
2
2
|
import { getValidationResult } from '@naturalcycles/nodejs-lib';
|
|
3
|
-
const REDACTED = 'REDACTED';
|
|
4
|
-
/**
|
|
5
|
-
* Mutates error
|
|
6
|
-
*/
|
|
7
|
-
function redact(redactPaths, obj, error) {
|
|
8
|
-
redactPaths
|
|
9
|
-
.map(path => _get(obj, path))
|
|
10
|
-
.filter(Boolean)
|
|
11
|
-
.forEach(secret => {
|
|
12
|
-
error.message = error.message.replaceAll(secret, REDACTED);
|
|
13
|
-
});
|
|
14
|
-
}
|
|
15
3
|
class ValidateRequest {
|
|
16
4
|
body(req, schema, opt = {}) {
|
|
17
5
|
return this.validate(req, 'body', schema, opt);
|
|
@@ -68,3 +56,15 @@ class ValidateRequest {
|
|
|
68
56
|
}
|
|
69
57
|
}
|
|
70
58
|
export const validateRequest = new ValidateRequest();
|
|
59
|
+
const REDACTED = 'REDACTED';
|
|
60
|
+
/**
|
|
61
|
+
* Mutates error
|
|
62
|
+
*/
|
|
63
|
+
function redact(redactPaths, obj, error) {
|
|
64
|
+
redactPaths
|
|
65
|
+
.map(path => _get(obj, path))
|
|
66
|
+
.filter(Boolean)
|
|
67
|
+
.forEach(secret => {
|
|
68
|
+
error.message = error.message.replaceAll(secret, REDACTED);
|
|
69
|
+
});
|
|
70
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type ZodType, type ZodValidationError } from '@naturalcycles/js-lib/zod';
|
|
2
|
+
import type { BackendRequest } from '../server.model.js';
|
|
3
|
+
import type { ReqValidationOptions } from './validateRequest.js';
|
|
4
|
+
declare class ZodValidateRequest {
|
|
5
|
+
body<T>(req: BackendRequest, schema: ZodType<T>, opt?: ReqValidationOptions<ZodValidationError>): T;
|
|
6
|
+
query<T>(req: BackendRequest, schema: ZodType<T>, opt?: ReqValidationOptions<ZodValidationError>): T;
|
|
7
|
+
params<T>(req: BackendRequest, schema: ZodType<T>, opt?: ReqValidationOptions<ZodValidationError>): T;
|
|
8
|
+
/**
|
|
9
|
+
* Validates `req.headers` against the provided schema.
|
|
10
|
+
*
|
|
11
|
+
* Note: as opposed to other methods, this method does not mutate `req.headers` in case of success,
|
|
12
|
+
* i.e. schemas that cast values will not have any effect.
|
|
13
|
+
*
|
|
14
|
+
* If you wish to mutate `req.headers` with the validated value, use `keepOriginal: false` option.
|
|
15
|
+
* Keep in mind that this will also remove all values that are not in the schema.
|
|
16
|
+
*/
|
|
17
|
+
headers<T>(req: BackendRequest, schema: ZodType<T>, opt?: ReqValidationOptions<ZodValidationError>): T;
|
|
18
|
+
private validate;
|
|
19
|
+
}
|
|
20
|
+
export declare const zodValidateRequest: ZodValidateRequest;
|
|
21
|
+
export {};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { _get, AppError } from '@naturalcycles/js-lib';
|
|
2
|
+
import { zSafeValidate } from '@naturalcycles/js-lib/zod';
|
|
3
|
+
class ZodValidateRequest {
|
|
4
|
+
body(req, schema, opt = {}) {
|
|
5
|
+
return this.validate(req, 'body', schema, opt);
|
|
6
|
+
}
|
|
7
|
+
query(req, schema, opt = {}) {
|
|
8
|
+
return this.validate(req, 'query', schema, opt);
|
|
9
|
+
}
|
|
10
|
+
params(req, schema, opt = {}) {
|
|
11
|
+
return this.validate(req, 'params', schema, opt);
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Validates `req.headers` against the provided schema.
|
|
15
|
+
*
|
|
16
|
+
* Note: as opposed to other methods, this method does not mutate `req.headers` in case of success,
|
|
17
|
+
* i.e. schemas that cast values will not have any effect.
|
|
18
|
+
*
|
|
19
|
+
* If you wish to mutate `req.headers` with the validated value, use `keepOriginal: false` option.
|
|
20
|
+
* Keep in mind that this will also remove all values that are not in the schema.
|
|
21
|
+
*/
|
|
22
|
+
headers(req, schema, opt = {}) {
|
|
23
|
+
const options = {
|
|
24
|
+
keepOriginal: true,
|
|
25
|
+
...opt,
|
|
26
|
+
};
|
|
27
|
+
return this.validate(req, 'headers', schema, options);
|
|
28
|
+
}
|
|
29
|
+
validate(req, reqProperty, schema, opt = {}) {
|
|
30
|
+
const { data, error } = zSafeValidate(req[reqProperty] || {}, schema);
|
|
31
|
+
if (error) {
|
|
32
|
+
let report;
|
|
33
|
+
if (typeof opt.report === 'boolean') {
|
|
34
|
+
report = opt.report;
|
|
35
|
+
}
|
|
36
|
+
else if (typeof opt.report === 'function') {
|
|
37
|
+
report = opt.report(error);
|
|
38
|
+
}
|
|
39
|
+
if (opt.redactPaths) {
|
|
40
|
+
redact(opt.redactPaths, req[reqProperty], error);
|
|
41
|
+
}
|
|
42
|
+
throw new AppError(error.message, {
|
|
43
|
+
backendResponseStatusCode: 400,
|
|
44
|
+
report,
|
|
45
|
+
...error.data,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
// mutate req to replace the property with the value, converted by Joi
|
|
49
|
+
if (!opt.keepOriginal && reqProperty !== 'query') {
|
|
50
|
+
// query is read-only in Express 5
|
|
51
|
+
req[reqProperty] = data;
|
|
52
|
+
}
|
|
53
|
+
return data;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
export const zodValidateRequest = new ZodValidateRequest();
|
|
57
|
+
const REDACTED = 'REDACTED';
|
|
58
|
+
/**
|
|
59
|
+
* Mutates error
|
|
60
|
+
*/
|
|
61
|
+
function redact(redactPaths, obj, error) {
|
|
62
|
+
redactPaths
|
|
63
|
+
.map(path => _get(obj, path))
|
|
64
|
+
.filter(Boolean)
|
|
65
|
+
.forEach(secret => {
|
|
66
|
+
error.message = error.message.replaceAll(secret, REDACTED);
|
|
67
|
+
});
|
|
68
|
+
}
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -28,9 +28,9 @@ export * from './server/serverStatusMiddleware.js'
|
|
|
28
28
|
export * from './server/simpleRequestLoggerMiddleware.js'
|
|
29
29
|
export * from './server/startServer.js'
|
|
30
30
|
export * from './server/startServer.model.js'
|
|
31
|
-
export * from './server/validation/
|
|
31
|
+
export * from './server/validation/ajvValidateRequest.js'
|
|
32
32
|
export * from './server/validation/validateRequest.js'
|
|
33
|
-
export * from './server/validation/
|
|
33
|
+
export * from './server/validation/zodValidateRequest.js'
|
|
34
34
|
export * from './util.js'
|
|
35
35
|
|
|
36
36
|
export { onFinished }
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { _get, AppError } from '@naturalcycles/js-lib'
|
|
2
|
+
import type { AjvSchema, AjvValidationError } from '@naturalcycles/nodejs-lib'
|
|
3
|
+
import type { BackendRequest } from '../server.model.js'
|
|
4
|
+
import type { ReqValidationOptions } from './validateRequest.js'
|
|
5
|
+
|
|
6
|
+
class AjvValidateRequest {
|
|
7
|
+
body<T>(
|
|
8
|
+
req: BackendRequest,
|
|
9
|
+
schema: AjvSchema<T>,
|
|
10
|
+
opt: ReqValidationOptions<AjvValidationError> = {},
|
|
11
|
+
): T {
|
|
12
|
+
return this.validate(req, 'body', schema, opt)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
query<T>(
|
|
16
|
+
req: BackendRequest,
|
|
17
|
+
schema: AjvSchema<T>,
|
|
18
|
+
opt: ReqValidationOptions<AjvValidationError> = {},
|
|
19
|
+
): T {
|
|
20
|
+
return this.validate(req, 'query', schema, opt)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
params<T>(
|
|
24
|
+
req: BackendRequest,
|
|
25
|
+
schema: AjvSchema<T>,
|
|
26
|
+
opt: ReqValidationOptions<AjvValidationError> = {},
|
|
27
|
+
): T {
|
|
28
|
+
return this.validate(req, 'params', schema, opt)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Validates `req.headers` against the provided schema.
|
|
33
|
+
*
|
|
34
|
+
* Note: as opposed to other methods, this method does not mutate `req.headers` in case of success,
|
|
35
|
+
* i.e. schemas that cast values will not have any effect.
|
|
36
|
+
*
|
|
37
|
+
* If you wish to mutate `req.headers` with the validated value, use `keepOriginal: false` option.
|
|
38
|
+
* Keep in mind that this will also remove all values that are not in the schema.
|
|
39
|
+
*/
|
|
40
|
+
headers<T>(
|
|
41
|
+
req: BackendRequest,
|
|
42
|
+
schema: AjvSchema<T>,
|
|
43
|
+
opt: ReqValidationOptions<AjvValidationError> = {},
|
|
44
|
+
): T {
|
|
45
|
+
const options: ReqValidationOptions<AjvValidationError> = {
|
|
46
|
+
keepOriginal: true,
|
|
47
|
+
...opt,
|
|
48
|
+
}
|
|
49
|
+
return this.validate(req, 'headers', schema, options)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
private validate<T>(
|
|
53
|
+
req: BackendRequest,
|
|
54
|
+
reqProperty: 'body' | 'params' | 'query' | 'headers',
|
|
55
|
+
schema: AjvSchema<T>,
|
|
56
|
+
opt: ReqValidationOptions<AjvValidationError> = {},
|
|
57
|
+
): T {
|
|
58
|
+
const value: T = { ...req[reqProperty] } // destructure to avoid being mutated by Ajv
|
|
59
|
+
// It will mutate the `value`, but not the original object
|
|
60
|
+
const error = schema.getValidationError(value, {
|
|
61
|
+
objectName: `request ${reqProperty}`,
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
if (error) {
|
|
65
|
+
let report: boolean | undefined
|
|
66
|
+
if (typeof opt.report === 'boolean') {
|
|
67
|
+
report = opt.report
|
|
68
|
+
} else if (typeof opt.report === 'function') {
|
|
69
|
+
report = opt.report(error)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (opt.redactPaths) {
|
|
73
|
+
redact(opt.redactPaths, req[reqProperty], error)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
throw new AppError(error.message, {
|
|
77
|
+
backendResponseStatusCode: 400,
|
|
78
|
+
report,
|
|
79
|
+
...error.data,
|
|
80
|
+
})
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// mutate req to replace the property with the value, converted by Joi
|
|
84
|
+
if (!opt.keepOriginal && reqProperty !== 'query') {
|
|
85
|
+
// query is read-only in Express 5
|
|
86
|
+
req[reqProperty] = value
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return value
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export const ajvValidateRequest = new AjvValidateRequest()
|
|
94
|
+
|
|
95
|
+
const REDACTED = 'REDACTED'
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Mutates error
|
|
99
|
+
*/
|
|
100
|
+
function redact(redactPaths: string[], obj: any, error: Error): void {
|
|
101
|
+
redactPaths
|
|
102
|
+
.map(path => _get(obj, path) as string)
|
|
103
|
+
.filter(Boolean)
|
|
104
|
+
.forEach(secret => {
|
|
105
|
+
error.message = error.message.replaceAll(secret, REDACTED)
|
|
106
|
+
})
|
|
107
|
+
}
|
|
@@ -3,8 +3,6 @@ import type { AnySchema, JoiValidationError } from '@naturalcycles/nodejs-lib'
|
|
|
3
3
|
import { getValidationResult } from '@naturalcycles/nodejs-lib'
|
|
4
4
|
import type { BackendRequest } from '../server.model.js'
|
|
5
5
|
|
|
6
|
-
const REDACTED = 'REDACTED'
|
|
7
|
-
|
|
8
6
|
export interface ReqValidationOptions<ERR extends Error> {
|
|
9
7
|
/**
|
|
10
8
|
* Pass a 'dot-paths' (e.g `pw`, or `input.pw`) that needs to be redacted from the output, in case of error.
|
|
@@ -26,18 +24,6 @@ export interface ReqValidationOptions<ERR extends Error> {
|
|
|
26
24
|
keepOriginal?: boolean
|
|
27
25
|
}
|
|
28
26
|
|
|
29
|
-
/**
|
|
30
|
-
* Mutates error
|
|
31
|
-
*/
|
|
32
|
-
function redact(redactPaths: string[], obj: any, error: Error): void {
|
|
33
|
-
redactPaths
|
|
34
|
-
.map(path => _get(obj, path) as string)
|
|
35
|
-
.filter(Boolean)
|
|
36
|
-
.forEach(secret => {
|
|
37
|
-
error.message = error.message.replaceAll(secret, REDACTED)
|
|
38
|
-
})
|
|
39
|
-
}
|
|
40
|
-
|
|
41
27
|
class ValidateRequest {
|
|
42
28
|
body<T>(
|
|
43
29
|
req: BackendRequest,
|
|
@@ -127,3 +113,17 @@ class ValidateRequest {
|
|
|
127
113
|
}
|
|
128
114
|
|
|
129
115
|
export const validateRequest = new ValidateRequest()
|
|
116
|
+
|
|
117
|
+
const REDACTED = 'REDACTED'
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Mutates error
|
|
121
|
+
*/
|
|
122
|
+
function redact(redactPaths: string[], obj: any, error: Error): void {
|
|
123
|
+
redactPaths
|
|
124
|
+
.map(path => _get(obj, path) as string)
|
|
125
|
+
.filter(Boolean)
|
|
126
|
+
.forEach(secret => {
|
|
127
|
+
error.message = error.message.replaceAll(secret, REDACTED)
|
|
128
|
+
})
|
|
129
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { _get, AppError } from '@naturalcycles/js-lib'
|
|
2
|
+
import { type ZodType, type ZodValidationError, zSafeValidate } from '@naturalcycles/js-lib/zod'
|
|
3
|
+
import type { BackendRequest } from '../server.model.js'
|
|
4
|
+
import type { ReqValidationOptions } from './validateRequest.js'
|
|
5
|
+
|
|
6
|
+
class ZodValidateRequest {
|
|
7
|
+
body<T>(
|
|
8
|
+
req: BackendRequest,
|
|
9
|
+
schema: ZodType<T>,
|
|
10
|
+
opt: ReqValidationOptions<ZodValidationError> = {},
|
|
11
|
+
): T {
|
|
12
|
+
return this.validate(req, 'body', schema, opt)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
query<T>(
|
|
16
|
+
req: BackendRequest,
|
|
17
|
+
schema: ZodType<T>,
|
|
18
|
+
opt: ReqValidationOptions<ZodValidationError> = {},
|
|
19
|
+
): T {
|
|
20
|
+
return this.validate(req, 'query', schema, opt)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
params<T>(
|
|
24
|
+
req: BackendRequest,
|
|
25
|
+
schema: ZodType<T>,
|
|
26
|
+
opt: ReqValidationOptions<ZodValidationError> = {},
|
|
27
|
+
): T {
|
|
28
|
+
return this.validate(req, 'params', schema, opt)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Validates `req.headers` against the provided schema.
|
|
33
|
+
*
|
|
34
|
+
* Note: as opposed to other methods, this method does not mutate `req.headers` in case of success,
|
|
35
|
+
* i.e. schemas that cast values will not have any effect.
|
|
36
|
+
*
|
|
37
|
+
* If you wish to mutate `req.headers` with the validated value, use `keepOriginal: false` option.
|
|
38
|
+
* Keep in mind that this will also remove all values that are not in the schema.
|
|
39
|
+
*/
|
|
40
|
+
headers<T>(
|
|
41
|
+
req: BackendRequest,
|
|
42
|
+
schema: ZodType<T>,
|
|
43
|
+
opt: ReqValidationOptions<ZodValidationError> = {},
|
|
44
|
+
): T {
|
|
45
|
+
const options: ReqValidationOptions<ZodValidationError> = {
|
|
46
|
+
keepOriginal: true,
|
|
47
|
+
...opt,
|
|
48
|
+
}
|
|
49
|
+
return this.validate(req, 'headers', schema, options)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
private validate<T>(
|
|
53
|
+
req: BackendRequest,
|
|
54
|
+
reqProperty: 'body' | 'params' | 'query' | 'headers',
|
|
55
|
+
schema: ZodType<T>,
|
|
56
|
+
opt: ReqValidationOptions<ZodValidationError> = {},
|
|
57
|
+
): T {
|
|
58
|
+
const { data, error } = zSafeValidate(
|
|
59
|
+
req[reqProperty] || {},
|
|
60
|
+
schema,
|
|
61
|
+
// `request ${reqProperty}`,
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
if (error) {
|
|
65
|
+
let report: boolean | undefined
|
|
66
|
+
if (typeof opt.report === 'boolean') {
|
|
67
|
+
report = opt.report
|
|
68
|
+
} else if (typeof opt.report === 'function') {
|
|
69
|
+
report = opt.report(error)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (opt.redactPaths) {
|
|
73
|
+
redact(opt.redactPaths, req[reqProperty], error)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
throw new AppError(error.message, {
|
|
77
|
+
backendResponseStatusCode: 400,
|
|
78
|
+
report,
|
|
79
|
+
...error.data,
|
|
80
|
+
})
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// mutate req to replace the property with the value, converted by Joi
|
|
84
|
+
if (!opt.keepOriginal && reqProperty !== 'query') {
|
|
85
|
+
// query is read-only in Express 5
|
|
86
|
+
req[reqProperty] = data
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return data
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export const zodValidateRequest = new ZodValidateRequest()
|
|
94
|
+
|
|
95
|
+
const REDACTED = 'REDACTED'
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Mutates error
|
|
99
|
+
*/
|
|
100
|
+
function redact(redactPaths: string[], obj: any, error: Error): void {
|
|
101
|
+
redactPaths
|
|
102
|
+
.map(path => _get(obj, path) as string)
|
|
103
|
+
.filter(Boolean)
|
|
104
|
+
.forEach(secret => {
|
|
105
|
+
error.message = error.message.replaceAll(secret, REDACTED)
|
|
106
|
+
})
|
|
107
|
+
}
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import type { JsonSchema, JsonSchemaBuilder } from '@naturalcycles/js-lib';
|
|
2
|
-
import type { AjvValidationError } from '@naturalcycles/nodejs-lib';
|
|
3
|
-
import { AjvSchema } from '@naturalcycles/nodejs-lib';
|
|
4
|
-
import type { BackendRequestHandler } from '../server.model.js';
|
|
5
|
-
import type { ReqValidationOptions } from './validateRequest.js';
|
|
6
|
-
export declare function validateBody(schema: JsonSchema | JsonSchemaBuilder | AjvSchema, opt?: ReqValidationOptions<AjvValidationError>): BackendRequestHandler;
|
|
7
|
-
export declare function validateParams(schema: JsonSchema | JsonSchemaBuilder | AjvSchema, opt?: ReqValidationOptions<AjvValidationError>): BackendRequestHandler;
|
|
8
|
-
export declare function validateQuery(schema: JsonSchema | JsonSchemaBuilder | AjvSchema, opt?: ReqValidationOptions<AjvValidationError>): BackendRequestHandler;
|
|
9
|
-
export declare function validateHeaders(schema: JsonSchema | JsonSchemaBuilder | AjvSchema, opt?: ReqValidationOptions<AjvValidationError>): BackendRequestHandler;
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import { _get, AppError } from '@naturalcycles/js-lib';
|
|
2
|
-
import { AjvSchema } from '@naturalcycles/nodejs-lib';
|
|
3
|
-
const REDACTED = 'REDACTED';
|
|
4
|
-
export function validateBody(schema, opt = {}) {
|
|
5
|
-
return validateObject('body', schema, opt);
|
|
6
|
-
}
|
|
7
|
-
export function validateParams(schema, opt = {}) {
|
|
8
|
-
return validateObject('params', schema, opt);
|
|
9
|
-
}
|
|
10
|
-
export function validateQuery(schema, opt = {}) {
|
|
11
|
-
return validateObject('query', schema, opt);
|
|
12
|
-
}
|
|
13
|
-
export function validateHeaders(schema, opt = {}) {
|
|
14
|
-
return validateObject('headers', schema, opt);
|
|
15
|
-
}
|
|
16
|
-
/**
|
|
17
|
-
* Validates req property (body, params or query).
|
|
18
|
-
* Supports Joi schema or AjvSchema (from nodejs-lib).
|
|
19
|
-
* Able to redact sensitive data from the error message.
|
|
20
|
-
* Throws http 400 on error.
|
|
21
|
-
*/
|
|
22
|
-
function validateObject(prop, schema, opt = {}) {
|
|
23
|
-
const ajvSchema = AjvSchema.create(schema, {
|
|
24
|
-
objectName: `request ${prop}`,
|
|
25
|
-
});
|
|
26
|
-
const reportPredicate = typeof opt.report === 'function' ? opt.report : () => opt.report;
|
|
27
|
-
return (req, _res, next) => {
|
|
28
|
-
const error = ajvSchema.getValidationError(req[prop] || {});
|
|
29
|
-
if (error) {
|
|
30
|
-
const report = reportPredicate(error);
|
|
31
|
-
if (opt.redactPaths) {
|
|
32
|
-
redact(opt.redactPaths, req[prop], error);
|
|
33
|
-
error.data.errors.length = 0; // clears the array
|
|
34
|
-
}
|
|
35
|
-
return next(new AppError(error.message, {
|
|
36
|
-
backendResponseStatusCode: 400,
|
|
37
|
-
report,
|
|
38
|
-
...error.data,
|
|
39
|
-
}));
|
|
40
|
-
}
|
|
41
|
-
next();
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
|
-
/**
|
|
45
|
-
* Mutates error
|
|
46
|
-
*/
|
|
47
|
-
function redact(redactPaths, obj, error) {
|
|
48
|
-
redactPaths
|
|
49
|
-
.map(path => _get(obj, path))
|
|
50
|
-
.filter(Boolean)
|
|
51
|
-
.forEach(secret => {
|
|
52
|
-
error.message = error.message.replaceAll(secret, REDACTED);
|
|
53
|
-
});
|
|
54
|
-
}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import type { ZodType, ZodValidationError } from '@naturalcycles/js-lib/zod';
|
|
2
|
-
import type { BackendRequestHandler } from '../server.model.js';
|
|
3
|
-
import type { ReqValidationOptions } from './validateRequest.js';
|
|
4
|
-
/**
|
|
5
|
-
* Validates req property (body, params or query).
|
|
6
|
-
* Supports Joi schema or AjvSchema (from nodejs-lib).
|
|
7
|
-
* Able to redact sensitive data from the error message.
|
|
8
|
-
* Throws http 400 on error.
|
|
9
|
-
*/
|
|
10
|
-
export declare function zodReqValidate(prop: 'body' | 'params' | 'query', schema: ZodType, opt?: ReqValidationOptions<ZodValidationError>): BackendRequestHandler;
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import { _get, AppError } from '@naturalcycles/js-lib';
|
|
2
|
-
import { zSafeValidate } from '@naturalcycles/js-lib/zod';
|
|
3
|
-
const REDACTED = 'REDACTED';
|
|
4
|
-
/**
|
|
5
|
-
* Validates req property (body, params or query).
|
|
6
|
-
* Supports Joi schema or AjvSchema (from nodejs-lib).
|
|
7
|
-
* Able to redact sensitive data from the error message.
|
|
8
|
-
* Throws http 400 on error.
|
|
9
|
-
*/
|
|
10
|
-
export function zodReqValidate(prop, schema, opt = {}) {
|
|
11
|
-
const reportPredicate = typeof opt.report === 'function' ? opt.report : () => opt.report;
|
|
12
|
-
return (req, _res, next) => {
|
|
13
|
-
const { error } = zSafeValidate(req[prop] || {}, schema);
|
|
14
|
-
if (!error) {
|
|
15
|
-
return next();
|
|
16
|
-
}
|
|
17
|
-
const report = reportPredicate(error);
|
|
18
|
-
if (opt.redactPaths) {
|
|
19
|
-
redact(opt.redactPaths, req[prop], error);
|
|
20
|
-
}
|
|
21
|
-
return next(new AppError(error.message, {
|
|
22
|
-
backendResponseStatusCode: 400,
|
|
23
|
-
report,
|
|
24
|
-
}));
|
|
25
|
-
};
|
|
26
|
-
}
|
|
27
|
-
/**
|
|
28
|
-
* Mutates error
|
|
29
|
-
*/
|
|
30
|
-
function redact(redactPaths, obj, error) {
|
|
31
|
-
redactPaths
|
|
32
|
-
.map(path => _get(obj, path))
|
|
33
|
-
.filter(Boolean)
|
|
34
|
-
.forEach(secret => {
|
|
35
|
-
error.message = error.message.replace(secret, REDACTED);
|
|
36
|
-
});
|
|
37
|
-
}
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
import type { JsonSchema, JsonSchemaBuilder } from '@naturalcycles/js-lib'
|
|
2
|
-
import { _get, AppError } from '@naturalcycles/js-lib'
|
|
3
|
-
import type { AjvValidationError } from '@naturalcycles/nodejs-lib'
|
|
4
|
-
import { AjvSchema } from '@naturalcycles/nodejs-lib'
|
|
5
|
-
import type { BackendRequestHandler } from '../server.model.js'
|
|
6
|
-
import type { ReqValidationOptions } from './validateRequest.js'
|
|
7
|
-
|
|
8
|
-
const REDACTED = 'REDACTED'
|
|
9
|
-
|
|
10
|
-
export function validateBody(
|
|
11
|
-
schema: JsonSchema | JsonSchemaBuilder | AjvSchema,
|
|
12
|
-
opt: ReqValidationOptions<AjvValidationError> = {},
|
|
13
|
-
): BackendRequestHandler {
|
|
14
|
-
return validateObject('body', schema, opt)
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export function validateParams(
|
|
18
|
-
schema: JsonSchema | JsonSchemaBuilder | AjvSchema,
|
|
19
|
-
opt: ReqValidationOptions<AjvValidationError> = {},
|
|
20
|
-
): BackendRequestHandler {
|
|
21
|
-
return validateObject('params', schema, opt)
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export function validateQuery(
|
|
25
|
-
schema: JsonSchema | JsonSchemaBuilder | AjvSchema,
|
|
26
|
-
opt: ReqValidationOptions<AjvValidationError> = {},
|
|
27
|
-
): BackendRequestHandler {
|
|
28
|
-
return validateObject('query', schema, opt)
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export function validateHeaders(
|
|
32
|
-
schema: JsonSchema | JsonSchemaBuilder | AjvSchema,
|
|
33
|
-
opt: ReqValidationOptions<AjvValidationError> = {},
|
|
34
|
-
): BackendRequestHandler {
|
|
35
|
-
return validateObject('headers', schema, opt)
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Validates req property (body, params or query).
|
|
40
|
-
* Supports Joi schema or AjvSchema (from nodejs-lib).
|
|
41
|
-
* Able to redact sensitive data from the error message.
|
|
42
|
-
* Throws http 400 on error.
|
|
43
|
-
*/
|
|
44
|
-
function validateObject(
|
|
45
|
-
prop: 'body' | 'params' | 'query' | 'headers',
|
|
46
|
-
schema: JsonSchema | JsonSchemaBuilder | AjvSchema,
|
|
47
|
-
opt: ReqValidationOptions<AjvValidationError> = {},
|
|
48
|
-
): BackendRequestHandler {
|
|
49
|
-
const ajvSchema = AjvSchema.create(schema, {
|
|
50
|
-
objectName: `request ${prop}`,
|
|
51
|
-
})
|
|
52
|
-
|
|
53
|
-
const reportPredicate =
|
|
54
|
-
typeof opt.report === 'function' ? opt.report : () => opt.report as boolean | undefined
|
|
55
|
-
|
|
56
|
-
return (req, _res, next) => {
|
|
57
|
-
const error = ajvSchema.getValidationError(req[prop] || {})
|
|
58
|
-
if (error) {
|
|
59
|
-
const report = reportPredicate(error)
|
|
60
|
-
|
|
61
|
-
if (opt.redactPaths) {
|
|
62
|
-
redact(opt.redactPaths, req[prop], error)
|
|
63
|
-
error.data.errors.length = 0 // clears the array
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
return next(
|
|
67
|
-
new AppError(error.message, {
|
|
68
|
-
backendResponseStatusCode: 400,
|
|
69
|
-
report,
|
|
70
|
-
...error.data,
|
|
71
|
-
}),
|
|
72
|
-
)
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
next()
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* Mutates error
|
|
81
|
-
*/
|
|
82
|
-
function redact(redactPaths: string[], obj: any, error: Error): void {
|
|
83
|
-
redactPaths
|
|
84
|
-
.map(path => _get(obj, path) as string)
|
|
85
|
-
.filter(Boolean)
|
|
86
|
-
.forEach(secret => {
|
|
87
|
-
error.message = error.message.replaceAll(secret, REDACTED)
|
|
88
|
-
})
|
|
89
|
-
}
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import { _get, AppError } from '@naturalcycles/js-lib'
|
|
2
|
-
import type { ZodType, ZodValidationError } from '@naturalcycles/js-lib/zod'
|
|
3
|
-
import { zSafeValidate } from '@naturalcycles/js-lib/zod'
|
|
4
|
-
import type { BackendRequestHandler } from '../server.model.js'
|
|
5
|
-
import type { ReqValidationOptions } from './validateRequest.js'
|
|
6
|
-
|
|
7
|
-
const REDACTED = 'REDACTED'
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Validates req property (body, params or query).
|
|
11
|
-
* Supports Joi schema or AjvSchema (from nodejs-lib).
|
|
12
|
-
* Able to redact sensitive data from the error message.
|
|
13
|
-
* Throws http 400 on error.
|
|
14
|
-
*/
|
|
15
|
-
export function zodReqValidate(
|
|
16
|
-
prop: 'body' | 'params' | 'query',
|
|
17
|
-
schema: ZodType,
|
|
18
|
-
opt: ReqValidationOptions<ZodValidationError> = {},
|
|
19
|
-
): BackendRequestHandler {
|
|
20
|
-
const reportPredicate =
|
|
21
|
-
typeof opt.report === 'function' ? opt.report : () => opt.report as boolean | undefined
|
|
22
|
-
|
|
23
|
-
return (req, _res, next) => {
|
|
24
|
-
const { error } = zSafeValidate(req[prop] || {}, schema)
|
|
25
|
-
if (!error) {
|
|
26
|
-
return next()
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const report = reportPredicate(error)
|
|
30
|
-
|
|
31
|
-
if (opt.redactPaths) {
|
|
32
|
-
redact(opt.redactPaths, req[prop], error)
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
return next(
|
|
36
|
-
new AppError(error.message, {
|
|
37
|
-
backendResponseStatusCode: 400,
|
|
38
|
-
report,
|
|
39
|
-
}),
|
|
40
|
-
)
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Mutates error
|
|
46
|
-
*/
|
|
47
|
-
function redact(redactPaths: string[], obj: any, error: Error): void {
|
|
48
|
-
redactPaths
|
|
49
|
-
.map(path => _get(obj, path) as string)
|
|
50
|
-
.filter(Boolean)
|
|
51
|
-
.forEach(secret => {
|
|
52
|
-
error.message = error.message.replace(secret, REDACTED)
|
|
53
|
-
})
|
|
54
|
-
}
|