@naturalcycles/backend-lib 9.42.0 → 9.42.2
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/server/serverStatsMiddleware.js +1 -1
- package/dist/validation/ajv/ajvValidateRequest.d.ts +12 -0
- package/dist/validation/ajv/ajvValidateRequest.js +18 -4
- package/dist/validation/validateRequest.util.d.ts +9 -0
- package/dist/validation/validateRequest.util.js +7 -0
- package/package.json +1 -1
- package/src/server/serverStatsMiddleware.ts +1 -1
- package/src/validation/ajv/ajvValidateRequest.ts +19 -3
- package/src/validation/validateRequest.util.ts +19 -0
|
@@ -63,7 +63,7 @@ export const serverStatsHTMLHandler = (req, res) => {
|
|
|
63
63
|
'<tr>',
|
|
64
64
|
`<td><pre>${endpoint}</pre></td>`,
|
|
65
65
|
`<td align="right"><pre>${stat.total}</pre></td>`,
|
|
66
|
-
//
|
|
66
|
+
// oxlint-disable-next-line @typescript-eslint/no-base-to-string typescript/restrict-template-expressions
|
|
67
67
|
...families.map(f => `<td align="right"><pre>${stat[f]}</pre></td>`),
|
|
68
68
|
...percentiles.map(pc => `<td align="right"><pre>${stat.pc[pc]}</pre></td>`),
|
|
69
69
|
'</tr>',
|
|
@@ -3,7 +3,19 @@ import type { BackendRequest } from '../../server/server.model.js';
|
|
|
3
3
|
import { type ReqValidationOptions } from '../validateRequest.util.js';
|
|
4
4
|
declare class AjvValidateRequest {
|
|
5
5
|
body<IN, OUT>(req: BackendRequest, schema: SchemaHandledByAjv<IN, OUT>, opt?: ReqValidationOptions<AjvValidationError>): OUT;
|
|
6
|
+
/**
|
|
7
|
+
* Query validation uses type coercion (unlike body validation),
|
|
8
|
+
* so the passed in schemas do not need to specify only string values.
|
|
9
|
+
*
|
|
10
|
+
* Coercion mutates the input, even if the end result is that the input failed the validation.
|
|
11
|
+
*/
|
|
6
12
|
query<IN, OUT>(req: BackendRequest, schema: SchemaHandledByAjv<IN, OUT>, opt?: ReqValidationOptions<AjvValidationError>): OUT;
|
|
13
|
+
/**
|
|
14
|
+
* Params validation uses type coercion (unlike body validation),
|
|
15
|
+
* so the passed in schemas do not need to specify only string values.
|
|
16
|
+
*
|
|
17
|
+
* Coercion mutates the input, even if the end result is that the input failed the validation.
|
|
18
|
+
*/
|
|
7
19
|
params<IN, OUT>(req: BackendRequest, schema: SchemaHandledByAjv<IN, OUT>, opt?: ReqValidationOptions<AjvValidationError>): OUT;
|
|
8
20
|
/**
|
|
9
21
|
* Does NOT mutate `req.headers`,
|
|
@@ -1,15 +1,27 @@
|
|
|
1
1
|
import { _deepCopy } from '@naturalcycles/js-lib/object';
|
|
2
|
-
import { AjvSchema, } from '@naturalcycles/nodejs-lib/ajv';
|
|
2
|
+
import { AjvSchema, getCoercingAjv, } from '@naturalcycles/nodejs-lib/ajv';
|
|
3
3
|
import { handleValidationError } from '../validateRequest.util.js';
|
|
4
4
|
class AjvValidateRequest {
|
|
5
5
|
body(req, schema, opt = {}) {
|
|
6
6
|
return this.validate(req, 'body', schema, opt);
|
|
7
7
|
}
|
|
8
|
+
/**
|
|
9
|
+
* Query validation uses type coercion (unlike body validation),
|
|
10
|
+
* so the passed in schemas do not need to specify only string values.
|
|
11
|
+
*
|
|
12
|
+
* Coercion mutates the input, even if the end result is that the input failed the validation.
|
|
13
|
+
*/
|
|
8
14
|
query(req, schema, opt = {}) {
|
|
9
|
-
return this.validate(req, 'query', schema, opt);
|
|
15
|
+
return this.validate(req, 'query', schema, { coerceTypes: true, ...opt });
|
|
10
16
|
}
|
|
17
|
+
/**
|
|
18
|
+
* Params validation uses type coercion (unlike body validation),
|
|
19
|
+
* so the passed in schemas do not need to specify only string values.
|
|
20
|
+
*
|
|
21
|
+
* Coercion mutates the input, even if the end result is that the input failed the validation.
|
|
22
|
+
*/
|
|
11
23
|
params(req, schema, opt = {}) {
|
|
12
|
-
return this.validate(req, 'params', schema, opt);
|
|
24
|
+
return this.validate(req, 'params', schema, { coerceTypes: true, ...opt });
|
|
13
25
|
}
|
|
14
26
|
/**
|
|
15
27
|
* Does NOT mutate `req.headers`,
|
|
@@ -27,7 +39,9 @@ class AjvValidateRequest {
|
|
|
27
39
|
}
|
|
28
40
|
validate(req, reqProperty, schema, opt = {}) {
|
|
29
41
|
const input = req[reqProperty] || {};
|
|
30
|
-
const
|
|
42
|
+
const { coerceTypes } = opt;
|
|
43
|
+
const ajv = coerceTypes ? getCoercingAjv() : undefined;
|
|
44
|
+
const ajvSchema = AjvSchema.create(schema, { ajv });
|
|
31
45
|
const [error, output] = ajvSchema.getValidationResult(input, {
|
|
32
46
|
inputName: `request.${reqProperty}`,
|
|
33
47
|
});
|
|
@@ -11,4 +11,13 @@ export interface ReqValidationOptions<ERR extends AppError> {
|
|
|
11
11
|
* If true - `genericErrorHandler` will report it to errorReporter (aka Sentry).
|
|
12
12
|
*/
|
|
13
13
|
report?: boolean | ((err: ERR) => boolean);
|
|
14
|
+
/**
|
|
15
|
+
* Defaults to false, because it promotes type safe thinking.
|
|
16
|
+
*
|
|
17
|
+
* If set to true, AJV will try to coerce the types after the validation fails and retry the validation.
|
|
18
|
+
*
|
|
19
|
+
* To be used in places where we know that we are going to receive data with the wrong type,
|
|
20
|
+
* typically: request path params and request query params.
|
|
21
|
+
*/
|
|
22
|
+
coerceTypes?: boolean;
|
|
14
23
|
}
|
|
@@ -11,6 +11,7 @@ export function handleValidationError(error, originalProperty, opt = {}) {
|
|
|
11
11
|
if (opt.redactPaths) {
|
|
12
12
|
redact(opt.redactPaths, originalProperty, error);
|
|
13
13
|
}
|
|
14
|
+
makeErrorUserReadable(error);
|
|
14
15
|
throw new AppError(error.message, {
|
|
15
16
|
backendResponseStatusCode: 400,
|
|
16
17
|
report,
|
|
@@ -29,3 +30,9 @@ function redact(redactPaths, obj, error) {
|
|
|
29
30
|
error.message = error.message.replaceAll(secret, REDACTED);
|
|
30
31
|
});
|
|
31
32
|
}
|
|
33
|
+
/**
|
|
34
|
+
* Mutates error
|
|
35
|
+
*/
|
|
36
|
+
function makeErrorUserReadable(error) {
|
|
37
|
+
error.message = error.message.replaceAll('[Object: null prototype] ', '');
|
|
38
|
+
}
|
package/package.json
CHANGED
|
@@ -87,7 +87,7 @@ export const serverStatsHTMLHandler: BackendRequestHandler = (req, res) => {
|
|
|
87
87
|
'<tr>',
|
|
88
88
|
`<td><pre>${endpoint}</pre></td>`,
|
|
89
89
|
`<td align="right"><pre>${stat.total}</pre></td>`,
|
|
90
|
-
//
|
|
90
|
+
// oxlint-disable-next-line @typescript-eslint/no-base-to-string typescript/restrict-template-expressions
|
|
91
91
|
...families.map(f => `<td align="right"><pre>${stat[f]}</pre></td>`),
|
|
92
92
|
...percentiles.map(pc => `<td align="right"><pre>${stat.pc![pc]}</pre></td>`),
|
|
93
93
|
'</tr>',
|
|
@@ -2,6 +2,7 @@ import { _deepCopy } from '@naturalcycles/js-lib/object'
|
|
|
2
2
|
import {
|
|
3
3
|
AjvSchema,
|
|
4
4
|
type AjvValidationError,
|
|
5
|
+
getCoercingAjv,
|
|
5
6
|
type SchemaHandledByAjv,
|
|
6
7
|
} from '@naturalcycles/nodejs-lib/ajv'
|
|
7
8
|
import type { BackendRequest } from '../../server/server.model.js'
|
|
@@ -16,20 +17,32 @@ class AjvValidateRequest {
|
|
|
16
17
|
return this.validate(req, 'body', schema, opt)
|
|
17
18
|
}
|
|
18
19
|
|
|
20
|
+
/**
|
|
21
|
+
* Query validation uses type coercion (unlike body validation),
|
|
22
|
+
* so the passed in schemas do not need to specify only string values.
|
|
23
|
+
*
|
|
24
|
+
* Coercion mutates the input, even if the end result is that the input failed the validation.
|
|
25
|
+
*/
|
|
19
26
|
query<IN, OUT>(
|
|
20
27
|
req: BackendRequest,
|
|
21
28
|
schema: SchemaHandledByAjv<IN, OUT>,
|
|
22
29
|
opt: ReqValidationOptions<AjvValidationError> = {},
|
|
23
30
|
): OUT {
|
|
24
|
-
return this.validate(req, 'query', schema, opt)
|
|
31
|
+
return this.validate(req, 'query', schema, { coerceTypes: true, ...opt })
|
|
25
32
|
}
|
|
26
33
|
|
|
34
|
+
/**
|
|
35
|
+
* Params validation uses type coercion (unlike body validation),
|
|
36
|
+
* so the passed in schemas do not need to specify only string values.
|
|
37
|
+
*
|
|
38
|
+
* Coercion mutates the input, even if the end result is that the input failed the validation.
|
|
39
|
+
*/
|
|
27
40
|
params<IN, OUT>(
|
|
28
41
|
req: BackendRequest,
|
|
29
42
|
schema: SchemaHandledByAjv<IN, OUT>,
|
|
30
43
|
opt: ReqValidationOptions<AjvValidationError> = {},
|
|
31
44
|
): OUT {
|
|
32
|
-
return this.validate(req, 'params', schema, opt)
|
|
45
|
+
return this.validate(req, 'params', schema, { coerceTypes: true, ...opt })
|
|
33
46
|
}
|
|
34
47
|
|
|
35
48
|
/**
|
|
@@ -58,7 +71,10 @@ class AjvValidateRequest {
|
|
|
58
71
|
opt: ReqValidationOptions<AjvValidationError> = {},
|
|
59
72
|
): OUT {
|
|
60
73
|
const input: IN = req[reqProperty] || {}
|
|
61
|
-
|
|
74
|
+
|
|
75
|
+
const { coerceTypes } = opt
|
|
76
|
+
const ajv = coerceTypes ? getCoercingAjv() : undefined
|
|
77
|
+
const ajvSchema = AjvSchema.create(schema, { ajv })
|
|
62
78
|
|
|
63
79
|
const [error, output] = ajvSchema.getValidationResult(input, {
|
|
64
80
|
inputName: `request.${reqProperty}`,
|
|
@@ -17,6 +17,8 @@ export function handleValidationError<T, ERR extends AppError>(
|
|
|
17
17
|
redact(opt.redactPaths, originalProperty, error)
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
makeErrorUserReadable(error)
|
|
21
|
+
|
|
20
22
|
throw new AppError(error.message, {
|
|
21
23
|
backendResponseStatusCode: 400,
|
|
22
24
|
report,
|
|
@@ -38,6 +40,13 @@ function redact(redactPaths: string[], obj: any, error: Error): void {
|
|
|
38
40
|
})
|
|
39
41
|
}
|
|
40
42
|
|
|
43
|
+
/**
|
|
44
|
+
* Mutates error
|
|
45
|
+
*/
|
|
46
|
+
function makeErrorUserReadable<ERR extends AppError>(error: ERR): void {
|
|
47
|
+
error.message = error.message.replaceAll('[Object: null prototype] ', '')
|
|
48
|
+
}
|
|
49
|
+
|
|
41
50
|
export interface ReqValidationOptions<ERR extends AppError> {
|
|
42
51
|
/**
|
|
43
52
|
* Pass a 'dot-paths' (e.g `pw`, or `input.pw`) that needs to be redacted from the output, in case of error.
|
|
@@ -50,4 +59,14 @@ export interface ReqValidationOptions<ERR extends AppError> {
|
|
|
50
59
|
* If true - `genericErrorHandler` will report it to errorReporter (aka Sentry).
|
|
51
60
|
*/
|
|
52
61
|
report?: boolean | ((err: ERR) => boolean)
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Defaults to false, because it promotes type safe thinking.
|
|
65
|
+
*
|
|
66
|
+
* If set to true, AJV will try to coerce the types after the validation fails and retry the validation.
|
|
67
|
+
*
|
|
68
|
+
* To be used in places where we know that we are going to receive data with the wrong type,
|
|
69
|
+
* typically: request path params and request query params.
|
|
70
|
+
*/
|
|
71
|
+
coerceTypes?: boolean
|
|
53
72
|
}
|