@loopback/rest 6.0.0 → 7.0.1
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/CHANGELOG.md +73 -0
- package/dist/body-parsers/body-parser.js +3 -0
- package/dist/body-parsers/body-parser.js.map +1 -1
- package/dist/coercion/coerce-parameter.d.ts +1 -1
- package/dist/coercion/coerce-parameter.js +36 -13
- package/dist/coercion/coerce-parameter.js.map +1 -1
- package/dist/coercion/utils.d.ts +1 -1
- package/dist/http-handler.js +5 -0
- package/dist/http-handler.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/providers/find-route.provider.d.ts +0 -2
- package/dist/providers/find-route.provider.js +11 -8
- package/dist/providers/find-route.provider.js.map +1 -1
- package/dist/providers/invoke-method.provider.d.ts +0 -2
- package/dist/providers/invoke-method.provider.js +21 -10
- package/dist/providers/invoke-method.provider.js.map +1 -1
- package/dist/providers/log-error.provider.js +7 -2
- package/dist/providers/log-error.provider.js.map +1 -1
- package/dist/providers/parse-params.provider.d.ts +0 -2
- package/dist/providers/parse-params.provider.js +16 -8
- package/dist/providers/parse-params.provider.js.map +1 -1
- package/dist/providers/reject.provider.d.ts +1 -1
- package/dist/providers/reject.provider.js +2 -1
- package/dist/providers/reject.provider.js.map +1 -1
- package/dist/providers/send.provider.d.ts +1 -4
- package/dist/providers/send.provider.js +11 -13
- package/dist/providers/send.provider.js.map +1 -1
- package/dist/request-context.js.map +1 -1
- package/dist/rest-http-error.d.ts +3 -1
- package/dist/rest-http-error.js +3 -2
- package/dist/rest-http-error.js.map +1 -1
- package/dist/rest.application.js +2 -2
- package/dist/rest.application.js.map +1 -1
- package/dist/rest.component.js +0 -2
- package/dist/rest.component.js.map +1 -1
- package/dist/rest.server.d.ts +3 -2
- package/dist/rest.server.js +14 -7
- package/dist/rest.server.js.map +1 -1
- package/dist/router/base-route.js +3 -3
- package/dist/router/base-route.js.map +1 -1
- package/dist/router/controller-route.js +1 -1
- package/dist/router/controller-route.js.map +1 -1
- package/dist/router/handler-route.js +1 -1
- package/dist/router/handler-route.js.map +1 -1
- package/dist/router/redirect-route.js +1 -1
- package/dist/router/redirect-route.js.map +1 -1
- package/dist/sequence.d.ts +3 -2
- package/dist/sequence.js +30 -7
- package/dist/sequence.js.map +1 -1
- package/dist/spec-enhancers/consolidate.spec-enhancer.js +1 -1
- package/dist/spec-enhancers/consolidate.spec-enhancer.js.map +1 -1
- package/dist/spec-enhancers/info.spec-enhancer.js +13 -5
- package/dist/spec-enhancers/info.spec-enhancer.js.map +1 -1
- package/dist/types.d.ts +4 -0
- package/dist/validation/ajv-factory.provider.js +5 -3
- package/dist/validation/ajv-factory.provider.js.map +1 -1
- package/dist/validation/openapi-formats.d.ts +26 -0
- package/dist/validation/openapi-formats.js +85 -0
- package/dist/validation/openapi-formats.js.map +1 -0
- package/dist/validation/request-body.validator.d.ts +4 -4
- package/dist/validation/request-body.validator.js +14 -19
- package/dist/validation/request-body.validator.js.map +1 -1
- package/package.json +24 -21
- package/src/body-parsers/body-parser.ts +3 -0
- package/src/coercion/coerce-parameter.ts +44 -20
- package/src/http-handler.ts +6 -0
- package/src/providers/find-route.provider.ts +24 -8
- package/src/providers/invoke-method.provider.ts +28 -10
- package/src/providers/log-error.provider.ts +2 -1
- package/src/providers/parse-params.provider.ts +28 -8
- package/src/providers/reject.provider.ts +4 -3
- package/src/providers/send.provider.ts +9 -12
- package/src/request-context.ts +2 -1
- package/src/rest-http-error.ts +6 -2
- package/src/rest.application.ts +2 -2
- package/src/rest.component.ts +0 -2
- package/src/rest.server.ts +17 -8
- package/src/router/base-route.ts +3 -3
- package/src/router/controller-route.ts +1 -1
- package/src/router/handler-route.ts +3 -1
- package/src/router/redirect-route.ts +1 -1
- package/src/sequence.ts +45 -3
- package/src/spec-enhancers/consolidate.spec-enhancer.ts +2 -2
- package/src/spec-enhancers/info.spec-enhancer.ts +15 -6
- package/src/types.ts +5 -0
- package/src/validation/ajv-factory.provider.ts +8 -4
- package/src/validation/openapi-formats.ts +92 -0
- package/src/validation/request-body.validator.ts +26 -25
|
@@ -4,16 +4,17 @@
|
|
|
4
4
|
// License text available at https://opensource.org/licenses/MIT
|
|
5
5
|
|
|
6
6
|
import {
|
|
7
|
-
bind,
|
|
8
7
|
BindingScope,
|
|
9
8
|
filterByTag,
|
|
10
9
|
inject,
|
|
10
|
+
injectable,
|
|
11
11
|
Provider,
|
|
12
12
|
} from '@loopback/core';
|
|
13
13
|
import AjvCtor from 'ajv';
|
|
14
14
|
import debugModule from 'debug';
|
|
15
15
|
import {RestBindings, RestTags} from '../keys';
|
|
16
16
|
import {AjvFactory, AjvFormat, AjvKeyword, ValidationOptions} from '../types';
|
|
17
|
+
import {openapiFormats} from './openapi-formats';
|
|
17
18
|
|
|
18
19
|
const debug = debugModule('loopback:rest:ajv');
|
|
19
20
|
|
|
@@ -29,7 +30,7 @@ export const DEFAULT_AJV_VALIDATION_OPTIONS: ValidationOptions = {
|
|
|
29
30
|
/**
|
|
30
31
|
* A provider class that instantiate an AJV instance
|
|
31
32
|
*/
|
|
32
|
-
@
|
|
33
|
+
@injectable({scope: BindingScope.SINGLETON})
|
|
33
34
|
export class AjvFactoryProvider implements Provider<AjvFactory> {
|
|
34
35
|
constructor(
|
|
35
36
|
@inject(
|
|
@@ -57,8 +58,6 @@ export class AjvFactoryProvider implements Provider<AjvFactory> {
|
|
|
57
58
|
jsonPointers: true,
|
|
58
59
|
// nullable: support keyword "nullable" from Open API 3 specification.
|
|
59
60
|
nullable: true,
|
|
60
|
-
// Allow OpenAPI spec binary format
|
|
61
|
-
unknownFormats: ['binary'],
|
|
62
61
|
...validationOptions,
|
|
63
62
|
};
|
|
64
63
|
|
|
@@ -84,12 +83,17 @@ export class AjvFactoryProvider implements Provider<AjvFactory> {
|
|
|
84
83
|
});
|
|
85
84
|
}
|
|
86
85
|
|
|
86
|
+
for (const format of openapiFormats) {
|
|
87
|
+
ajvInst.addFormat(format.name, format);
|
|
88
|
+
}
|
|
89
|
+
|
|
87
90
|
if (this.formats) {
|
|
88
91
|
this.formats.forEach(format => {
|
|
89
92
|
debug('Adding Ajv format %s', format.name);
|
|
90
93
|
ajvInst.addFormat(format.name, format);
|
|
91
94
|
});
|
|
92
95
|
}
|
|
96
|
+
|
|
93
97
|
return ajvInst;
|
|
94
98
|
};
|
|
95
99
|
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
// Copyright IBM Corp. 2020. All Rights Reserved.
|
|
2
|
+
// Node module: @loopback/rest
|
|
3
|
+
// This file is licensed under the MIT License.
|
|
4
|
+
// License text available at https://opensource.org/licenses/MIT
|
|
5
|
+
|
|
6
|
+
import {AjvFormat} from '../types';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* int32: [-2147483648, 21474836 47]
|
|
10
|
+
*/
|
|
11
|
+
export const int32Format: AjvFormat = {
|
|
12
|
+
name: 'int32',
|
|
13
|
+
type: 'number',
|
|
14
|
+
validate: (value: number) => {
|
|
15
|
+
return (
|
|
16
|
+
Number.isInteger(value) && value >= -2147483648 && value <= 2147483647
|
|
17
|
+
);
|
|
18
|
+
},
|
|
19
|
+
async: false,
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* int64: [-9223372036854775808, 9223372036854775807]
|
|
24
|
+
*/
|
|
25
|
+
export const int64Format: AjvFormat = {
|
|
26
|
+
name: 'int64',
|
|
27
|
+
type: 'number',
|
|
28
|
+
validate: (value: number) => {
|
|
29
|
+
const max = Number.MAX_SAFE_INTEGER; // 9007199254740991
|
|
30
|
+
const min = Number.MIN_SAFE_INTEGER; // -9007199254740991
|
|
31
|
+
return Number.isInteger(value) && value >= min && value <= max;
|
|
32
|
+
},
|
|
33
|
+
async: false,
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* float: [-2^128, 2^128]
|
|
38
|
+
*/
|
|
39
|
+
export const floatFormat: AjvFormat = {
|
|
40
|
+
name: 'float',
|
|
41
|
+
type: 'number',
|
|
42
|
+
validate: (value: number) => {
|
|
43
|
+
return value >= -Math.pow(2, 128) && value <= Math.pow(2, 128);
|
|
44
|
+
},
|
|
45
|
+
async: false,
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* double: [-2^1024, 2^1024]
|
|
50
|
+
*/
|
|
51
|
+
export const doubleFormat: AjvFormat = {
|
|
52
|
+
name: 'double',
|
|
53
|
+
type: 'number',
|
|
54
|
+
validate: (value: number) => {
|
|
55
|
+
const max = Number.MAX_VALUE; // 1.7976931348623157e+308
|
|
56
|
+
const min = -Number.MAX_VALUE; // -1.7976931348623157e+308
|
|
57
|
+
return value >= min && value <= max;
|
|
58
|
+
},
|
|
59
|
+
async: false,
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Base64 encoded string
|
|
64
|
+
*/
|
|
65
|
+
export const byteFormat: AjvFormat = {
|
|
66
|
+
name: 'byte',
|
|
67
|
+
type: 'string',
|
|
68
|
+
validate: (value: string) => {
|
|
69
|
+
const base64 = Buffer.from(value, 'base64').toString('base64');
|
|
70
|
+
return value === base64;
|
|
71
|
+
},
|
|
72
|
+
async: false,
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Binary string
|
|
77
|
+
*/
|
|
78
|
+
export const binaryFormat: AjvFormat = {
|
|
79
|
+
name: 'binary',
|
|
80
|
+
type: 'string',
|
|
81
|
+
validate: (value: string) => true,
|
|
82
|
+
async: false,
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
export const openapiFormats: AjvFormat[] = [
|
|
86
|
+
int32Format,
|
|
87
|
+
int64Format,
|
|
88
|
+
floatFormat,
|
|
89
|
+
doubleFormat,
|
|
90
|
+
byteFormat,
|
|
91
|
+
binaryFormat,
|
|
92
|
+
];
|
|
@@ -12,7 +12,6 @@ import {
|
|
|
12
12
|
} from '@loopback/openapi-v3';
|
|
13
13
|
import ajv, {Ajv} from 'ajv';
|
|
14
14
|
import debugModule from 'debug';
|
|
15
|
-
import _ from 'lodash';
|
|
16
15
|
import util from 'util';
|
|
17
16
|
import {HttpErrors, RequestBody, RestHttpErrors} from '..';
|
|
18
17
|
import {
|
|
@@ -119,11 +118,11 @@ function getKeyForOptions(
|
|
|
119
118
|
}
|
|
120
119
|
|
|
121
120
|
/**
|
|
122
|
-
* Validate the
|
|
123
|
-
* @param
|
|
121
|
+
* Validate the value against JSON schema.
|
|
122
|
+
* @param value - The data value.
|
|
124
123
|
* @param schema - The JSON schema used to perform the validation.
|
|
125
124
|
* @param globalSchemas - Schema references.
|
|
126
|
-
* @param options -
|
|
125
|
+
* @param options - Value validation options.
|
|
127
126
|
*/
|
|
128
127
|
export async function validateValueAgainstSchema(
|
|
129
128
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
@@ -156,11 +155,11 @@ export async function validateValueAgainstSchema(
|
|
|
156
155
|
let validationErrors: ajv.ErrorObject[] = [];
|
|
157
156
|
try {
|
|
158
157
|
const validationResult = await validate(value);
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
158
|
+
debug(
|
|
159
|
+
`Value from ${options.source} passed AJV validation.`,
|
|
160
|
+
validationResult,
|
|
161
|
+
);
|
|
162
|
+
return validationResult;
|
|
164
163
|
} catch (error) {
|
|
165
164
|
validationErrors = error.errors;
|
|
166
165
|
}
|
|
@@ -180,30 +179,32 @@ export async function validateValueAgainstSchema(
|
|
|
180
179
|
|
|
181
180
|
// Throw invalid request body error
|
|
182
181
|
if (options.source === 'body') {
|
|
183
|
-
const error = RestHttpErrors.invalidRequestBody(
|
|
184
|
-
|
|
182
|
+
const error = RestHttpErrors.invalidRequestBody(
|
|
183
|
+
buildErrorDetails(validationErrors),
|
|
184
|
+
);
|
|
185
185
|
throw error;
|
|
186
186
|
}
|
|
187
187
|
|
|
188
188
|
// Throw invalid value error
|
|
189
|
-
const error =
|
|
190
|
-
|
|
189
|
+
const error = RestHttpErrors.invalidData(value, options.name ?? '(unknown)', {
|
|
190
|
+
details: buildErrorDetails(validationErrors),
|
|
191
|
+
});
|
|
191
192
|
throw error;
|
|
192
193
|
}
|
|
193
194
|
|
|
194
|
-
function
|
|
195
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
196
|
-
error: any,
|
|
195
|
+
function buildErrorDetails(
|
|
197
196
|
validationErrors: ajv.ErrorObject[],
|
|
198
|
-
) {
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
197
|
+
): RestHttpErrors.ValidationErrorDetails[] {
|
|
198
|
+
return validationErrors.map(
|
|
199
|
+
(e: ajv.ErrorObject): RestHttpErrors.ValidationErrorDetails => {
|
|
200
|
+
return {
|
|
201
|
+
path: e.dataPath,
|
|
202
|
+
code: e.keyword,
|
|
203
|
+
message: e.message ?? `must pass validation rule ${e.keyword}`,
|
|
204
|
+
info: e.params,
|
|
205
|
+
};
|
|
206
|
+
},
|
|
207
|
+
);
|
|
207
208
|
}
|
|
208
209
|
|
|
209
210
|
/**
|