@loopback/rest 5.1.2 → 5.2.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/CHANGELOG.md +17 -0
- package/dist/coercion/coerce-parameter.d.ts +3 -1
- package/dist/coercion/coerce-parameter.js +28 -12
- package/dist/coercion/coerce-parameter.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/parser.js +1 -1
- package/dist/parser.js.map +1 -1
- package/dist/types.d.ts +10 -0
- package/dist/validation/request-body.validator.d.ts +10 -2
- package/dist/validation/request-body.validator.js +25 -9
- package/dist/validation/request-body.validator.js.map +1 -1
- package/package.json +16 -16
- package/src/coercion/coerce-parameter.ts +55 -15
- package/src/index.ts +6 -0
- package/src/parser.ts +1 -1
- package/src/types.ts +11 -0
- package/src/validation/request-body.validator.ts +35 -12
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,23 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
# [5.2.0](https://github.com/strongloop/loopback-next/compare/@loopback/rest@5.1.2...@loopback/rest@5.2.0) (2020-06-30)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Bug Fixes
|
|
10
|
+
|
|
11
|
+
* **rest:** fix typing to be compatible with latest @types/js-yaml ([5ac39e4](https://github.com/strongloop/loopback-next/commit/5ac39e44827ef74c9d2bccb1797a9da125c249e3))
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
### Features
|
|
15
|
+
|
|
16
|
+
* coerce query object with schema ([ccea25f](https://github.com/strongloop/loopback-next/commit/ccea25fc382457f9436adfc0d8f6ce3a2d029c5e))
|
|
17
|
+
* **rest:** expose types from strong-error-handler ([b6f5595](https://github.com/strongloop/loopback-next/commit/b6f559549518c839a2898f1b968fc6a7901a6c6b))
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
6
23
|
## [5.1.2](https://github.com/strongloop/loopback-next/compare/@loopback/rest@5.1.1...@loopback/rest@5.1.2) (2020-06-23)
|
|
7
24
|
|
|
8
25
|
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { ParameterObject } from '@loopback/openapi-v3';
|
|
2
|
+
import { ValueValidationOptions } from '../';
|
|
2
3
|
/**
|
|
3
4
|
* Coerce the http raw data to a JavaScript type data of a parameter
|
|
4
5
|
* according to its OpenAPI schema specification.
|
|
5
6
|
*
|
|
6
7
|
* @param data - The raw data get from http request
|
|
7
8
|
* @param schema - The parameter's schema defined in OpenAPI specification
|
|
9
|
+
* @param options - The ajv validation options
|
|
8
10
|
*/
|
|
9
|
-
export declare function coerceParameter(data: string | undefined | object, spec: ParameterObject): string | number | boolean | object | undefined
|
|
11
|
+
export declare function coerceParameter(data: string | undefined | object, spec: ParameterObject, options?: ValueValidationOptions): Promise<string | number | boolean | object | undefined>;
|
|
@@ -20,14 +20,10 @@ const debug = debug_1.default('loopback:rest:coercion');
|
|
|
20
20
|
*
|
|
21
21
|
* @param data - The raw data get from http request
|
|
22
22
|
* @param schema - The parameter's schema defined in OpenAPI specification
|
|
23
|
+
* @param options - The ajv validation options
|
|
23
24
|
*/
|
|
24
|
-
function coerceParameter(data, spec) {
|
|
25
|
-
|
|
26
|
-
// If a query parameter is a url encoded Json object, the schema is defined under content['application/json']
|
|
27
|
-
if (!schema && spec.in === 'query' && spec.content) {
|
|
28
|
-
const jsonSpec = spec.content['application/json'];
|
|
29
|
-
schema = jsonSpec.schema;
|
|
30
|
-
}
|
|
25
|
+
async function coerceParameter(data, spec, options) {
|
|
26
|
+
const schema = extractSchemaFromSpec(spec);
|
|
31
27
|
if (!schema || openapi_v3_1.isReferenceObject(schema)) {
|
|
32
28
|
debug('The parameter with schema %s is not coerced since schema' +
|
|
33
29
|
'dereference is not supported yet.', schema);
|
|
@@ -56,7 +52,7 @@ function coerceParameter(data, spec) {
|
|
|
56
52
|
case 'boolean':
|
|
57
53
|
return coerceBoolean(data, spec);
|
|
58
54
|
case 'object':
|
|
59
|
-
return coerceObject(data, spec);
|
|
55
|
+
return coerceObject(data, spec, options);
|
|
60
56
|
case 'string':
|
|
61
57
|
case 'password':
|
|
62
58
|
return coerceString(data, spec);
|
|
@@ -127,17 +123,37 @@ function coerceBoolean(data, spec) {
|
|
|
127
123
|
return false;
|
|
128
124
|
throw __1.RestHttpErrors.invalidData(data, spec.name);
|
|
129
125
|
}
|
|
130
|
-
function coerceObject(input, spec) {
|
|
126
|
+
async function coerceObject(input, spec, options) {
|
|
131
127
|
const data = parseJsonIfNeeded(input, spec);
|
|
132
|
-
if (data
|
|
128
|
+
if (data == null) {
|
|
133
129
|
// Skip any further checks and coercions, nothing we can do with `undefined`
|
|
134
|
-
return
|
|
130
|
+
return data;
|
|
135
131
|
}
|
|
136
132
|
if (typeof data !== 'object' || Array.isArray(data))
|
|
137
133
|
throw __1.RestHttpErrors.invalidData(input, spec.name);
|
|
138
|
-
|
|
134
|
+
const schema = extractSchemaFromSpec(spec);
|
|
135
|
+
if (schema) {
|
|
136
|
+
// Apply coercion based on properties defined by spec.schema
|
|
137
|
+
await __1.validateValueAgainstSchema(data, schema, {}, { ...options, coerceTypes: true, source: 'parameter' });
|
|
138
|
+
}
|
|
139
139
|
return data;
|
|
140
140
|
}
|
|
141
|
+
/**
|
|
142
|
+
* Extract the schema from an OpenAPI parameter specification. If the root level
|
|
143
|
+
* one not found, search from media type 'application/json'.
|
|
144
|
+
*
|
|
145
|
+
* @param spec The parameter specification
|
|
146
|
+
*/
|
|
147
|
+
function extractSchemaFromSpec(spec) {
|
|
148
|
+
var _a, _b;
|
|
149
|
+
let schema = spec.schema;
|
|
150
|
+
// If a query parameter is a url encoded Json object,
|
|
151
|
+
// the schema is defined under content['application/json']
|
|
152
|
+
if (!schema && spec.in === 'query') {
|
|
153
|
+
schema = (_b = (_a = spec.content) === null || _a === void 0 ? void 0 : _a['application/json']) === null || _b === void 0 ? void 0 : _b.schema;
|
|
154
|
+
}
|
|
155
|
+
return schema;
|
|
156
|
+
}
|
|
141
157
|
function parseJsonIfNeeded(data, spec) {
|
|
142
158
|
if (typeof data !== 'string')
|
|
143
159
|
return data;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"coerce-parameter.js","sourceRoot":"","sources":["../../src/coercion/coerce-parameter.ts"],"names":[],"mappings":";AAAA,sDAAsD;AACtD,8BAA8B;AAC9B,+CAA+C;AAC/C,gEAAgE;;;;AAEhE,
|
|
1
|
+
{"version":3,"file":"coerce-parameter.js","sourceRoot":"","sources":["../../src/coercion/coerce-parameter.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
* @packageDocumentation
|
|
16
16
|
*/
|
|
17
17
|
export * from '@loopback/openapi-v3';
|
|
18
|
+
export { ErrorHandlerOptions, ErrorWriterOptions, StrongErrorHandler, writeErrorToResponse, } from 'strong-error-handler';
|
|
18
19
|
export * from './body-parsers';
|
|
19
20
|
export * from './http-handler';
|
|
20
21
|
export * from './keys';
|
package/dist/index.js
CHANGED
|
@@ -23,6 +23,8 @@ const tslib_1 = require("tslib");
|
|
|
23
23
|
* @packageDocumentation
|
|
24
24
|
*/
|
|
25
25
|
tslib_1.__exportStar(require("@loopback/openapi-v3"), exports);
|
|
26
|
+
var strong_error_handler_1 = require("strong-error-handler");
|
|
27
|
+
Object.defineProperty(exports, "writeErrorToResponse", { enumerable: true, get: function () { return strong_error_handler_1.writeErrorToResponse; } });
|
|
26
28
|
tslib_1.__exportStar(require("./body-parsers"), exports);
|
|
27
29
|
tslib_1.__exportStar(require("./http-handler"), exports);
|
|
28
30
|
tslib_1.__exportStar(require("./keys"), exports);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA,sDAAsD;AACtD,8BAA8B;AAC9B,+CAA+C;AAC/C,gEAAgE;;;;AAEhE;;;;;;;;;;;;;;;GAeG;AAEH,+DAAqC;AACrC,yDAA+B;AAC/B,yDAA+B;AAC/B,iDAAuB;AACvB,uDAA6B;AAC7B,mDAAyB;AACzB,sDAA4B;AAC5B,4DAAkC;AAClC,4DAAkC;AAClC,6DAAmC;AACnC,2DAAiC;AACjC,wDAA8B;AAC9B,mDAAyB;AACzB,qDAA2B;AAC3B,8EAAoD;AACpD,kDAAwB;AACxB,8EAAoD;AACpD,mDAAyB;AAGzB,sDAAsD;AACtD,sEAAqC;AAH7B,qBAGD,qBAAU,CAHC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA,sDAAsD;AACtD,8BAA8B;AAC9B,+CAA+C;AAC/C,gEAAgE;;;;AAEhE;;;;;;;;;;;;;;;GAeG;AAEH,+DAAqC;AACrC,6DAK8B;AAD5B,4HAAA,oBAAoB,OAAA;AAEtB,yDAA+B;AAC/B,yDAA+B;AAC/B,iDAAuB;AACvB,uDAA6B;AAC7B,mDAAyB;AACzB,sDAA4B;AAC5B,4DAAkC;AAClC,4DAAkC;AAClC,6DAAmC;AACnC,2DAAiC;AACjC,wDAA8B;AAC9B,mDAAyB;AACzB,qDAA2B;AAC3B,8EAAoD;AACpD,kDAAwB;AACxB,8EAAoD;AACpD,mDAAyB;AAGzB,sDAAsD;AACtD,sEAAqC;AAH7B,qBAGD,qBAAU,CAHC"}
|
package/dist/parser.js
CHANGED
|
@@ -49,7 +49,7 @@ async function buildOperationArguments(operationSpec, request, pathParams, body,
|
|
|
49
49
|
}
|
|
50
50
|
const spec = paramSpec;
|
|
51
51
|
const rawValue = getParamFromRequest(spec, request, pathParams);
|
|
52
|
-
const coercedValue = coerce_parameter_1.coerceParameter(rawValue, spec);
|
|
52
|
+
const coercedValue = await coerce_parameter_1.coerceParameter(rawValue, spec);
|
|
53
53
|
paramArgs.push(coercedValue);
|
|
54
54
|
}
|
|
55
55
|
debug('Validating request body - value %j', body);
|
package/dist/parser.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parser.js","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":";AAAA,sDAAsD;AACtD,8BAA8B;AAC9B,+CAA+C;AAC/C,gEAAgE;;;;AAEhE,qDAM8B;AAC9B,0DAAiC;AACjC,iDAA8D;AAC9D,kEAA4D;AAC5D,uDAAiD;AAQjD,gFAAwE;AACxE,MAAM,KAAK,GAAG,eAAY,CAAC,sBAAsB,CAAC,CAAC;AAEnD;;;;;;GAMG;AACI,KAAK,UAAU,kBAAkB,CACtC,OAAgB,EAChB,KAAoB,EACpB,oBAAuC,IAAI,gCAAiB,EAAE,EAC9D,UAAwC,EAAE;IAE1C,KAAK,CAAC,0CAA0C,EAAE,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC;IACpE,MAAM,aAAa,GAAG,KAAK,CAAC,IAAI,CAAC;IACjC,MAAM,UAAU,GAAG,KAAK,CAAC,UAAU,CAAC;IACpC,MAAM,IAAI,GAAG,MAAM,iBAAiB,CAAC,uBAAuB,CAC1D,aAAa,EACb,OAAO,CACR,CAAC;IACF,OAAO,uBAAuB,CAC5B,aAAa,EACb,OAAO,EACP,UAAU,EACV,IAAI,EACJ,KAAK,CAAC,OAAO,EACb,OAAO,CACR,CAAC;AACJ,CAAC;AArBD,gDAqBC;AAED,KAAK,UAAU,uBAAuB,CACpC,aAA8B,EAC9B,OAAgB,EAChB,UAA+B,EAC/B,IAAiB,EACjB,aAA4B,EAC5B,UAAwC,EAAE;;IAE1C,IAAI,gBAAgB,GAAG,CAAC,CAAC,CAAC;IAC1B,IAAI,aAAa,CAAC,WAAW,EAAE;QAC7B,uEAAuE;QACvE,uEAAuE;QACvE,IAAI,8BAAiB,CAAC,aAAa,CAAC,WAAW,CAAC,EAAE;YAChD,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;SAC3D;QACD,MAAM,CAAC,GAAG,aAAa,CAAC,WAAW,CAAC,+BAAkB,CAAC,CAAC;QACxD,gBAAgB,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;KAC9B;IAED,MAAM,SAAS,GAAkB,EAAE,CAAC;IAEpC,KAAK,MAAM,SAAS,UAAI,aAAa,CAAC,UAAU,mCAAI,EAAE,EAAE;QACtD,IAAI,8BAAiB,CAAC,SAAS,CAAC,EAAE;YAChC,yCAAyC;YACzC,6DAA6D;YAC7D,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;SAC3D;QACD,MAAM,IAAI,GAAG,SAA4B,CAAC;QAC1C,MAAM,QAAQ,GAAG,mBAAmB,CAAC,IAAI,EAAE,OAAO,EAAE,UAAU,CAAC,CAAC;QAChE,MAAM,YAAY,GAAG,kCAAe,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"parser.js","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
CHANGED
|
@@ -77,6 +77,16 @@ export declare type AjvKeyword = KeywordDefinition & {
|
|
|
77
77
|
export declare type AjvFormat = FormatDefinition & {
|
|
78
78
|
name: string;
|
|
79
79
|
};
|
|
80
|
+
/**
|
|
81
|
+
* Options for any value validation using AJV
|
|
82
|
+
*/
|
|
83
|
+
export interface ValueValidationOptions extends RequestBodyValidationOptions {
|
|
84
|
+
/**
|
|
85
|
+
* Where the data comes from. It can be 'body', 'path', 'header',
|
|
86
|
+
* 'query', 'cookie', etc...
|
|
87
|
+
*/
|
|
88
|
+
source?: string;
|
|
89
|
+
}
|
|
80
90
|
/**
|
|
81
91
|
* Options for request body validation using AJV
|
|
82
92
|
*/
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { RequestBodyObject, SchemasObject } from '@loopback/openapi-v3';
|
|
1
|
+
import { ReferenceObject, RequestBodyObject, SchemaObject, SchemasObject } from '@loopback/openapi-v3';
|
|
2
2
|
import { RequestBody } from '..';
|
|
3
|
-
import { RequestBodyValidationOptions } from '../types';
|
|
3
|
+
import { RequestBodyValidationOptions, ValueValidationOptions } from '../types';
|
|
4
4
|
/**
|
|
5
5
|
* Check whether the request body is valid according to the provided OpenAPI schema.
|
|
6
6
|
* The JSON schema is generated from the OpenAPI schema which is typically defined
|
|
@@ -12,3 +12,11 @@ import { RequestBodyValidationOptions } from '../types';
|
|
|
12
12
|
* @param options - Request body validation options for AJV
|
|
13
13
|
*/
|
|
14
14
|
export declare function validateRequestBody(body: RequestBody, requestBodySpec?: RequestBodyObject, globalSchemas?: SchemasObject, options?: RequestBodyValidationOptions): Promise<void>;
|
|
15
|
+
/**
|
|
16
|
+
* Validate the request body data against JSON schema.
|
|
17
|
+
* @param body - The request body data.
|
|
18
|
+
* @param schema - The JSON schema used to perform the validation.
|
|
19
|
+
* @param globalSchemas - Schema references.
|
|
20
|
+
* @param options - Request body validation options.
|
|
21
|
+
*/
|
|
22
|
+
export declare function validateValueAgainstSchema(value: any, schema: SchemaObject | ReferenceObject, globalSchemas?: SchemasObject, options?: ValueValidationOptions): Promise<void>;
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
// This file is licensed under the MIT License.
|
|
5
5
|
// License text available at https://opensource.org/licenses/MIT
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
exports.validateRequestBody = void 0;
|
|
7
|
+
exports.validateValueAgainstSchema = exports.validateRequestBody = void 0;
|
|
8
8
|
const tslib_1 = require("tslib");
|
|
9
9
|
const openapi_v3_1 = require("@loopback/openapi-v3");
|
|
10
10
|
const debug_1 = tslib_1.__importDefault(require("debug"));
|
|
@@ -47,7 +47,10 @@ async function validateRequestBody(body, requestBodySpec, globalSchemas = {}, op
|
|
|
47
47
|
if (!schema)
|
|
48
48
|
return;
|
|
49
49
|
options = { coerceTypes: !!body.coercionRequired, ...options };
|
|
50
|
-
await validateValueAgainstSchema(body.value, schema, globalSchemas,
|
|
50
|
+
await validateValueAgainstSchema(body.value, schema, globalSchemas, {
|
|
51
|
+
...options,
|
|
52
|
+
source: 'body',
|
|
53
|
+
});
|
|
51
54
|
}
|
|
52
55
|
exports.validateRequestBody = validateRequestBody;
|
|
53
56
|
/**
|
|
@@ -91,7 +94,7 @@ function getKeyForOptions(options) {
|
|
|
91
94
|
*/
|
|
92
95
|
async function validateValueAgainstSchema(
|
|
93
96
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
94
|
-
|
|
97
|
+
value, schema, globalSchemas = {}, options = {}) {
|
|
95
98
|
var _a, _b;
|
|
96
99
|
let validate;
|
|
97
100
|
const cache = (_a = options.compiledSchemaCache) !== null && _a !== void 0 ? _a : DEFAULT_COMPILED_SCHEMA_CACHE;
|
|
@@ -111,10 +114,10 @@ body, schema, globalSchemas = {}, options = {}) {
|
|
|
111
114
|
}
|
|
112
115
|
let validationErrors = [];
|
|
113
116
|
try {
|
|
114
|
-
const validationResult = await validate(
|
|
115
|
-
// When
|
|
117
|
+
const validationResult = await validate(value);
|
|
118
|
+
// When value is optional & values is empty / null, ajv returns null
|
|
116
119
|
if (validationResult || validationResult === null) {
|
|
117
|
-
debug(
|
|
120
|
+
debug(`Value from ${options.source} passed AJV validation.`);
|
|
118
121
|
return;
|
|
119
122
|
}
|
|
120
123
|
}
|
|
@@ -123,12 +126,26 @@ body, schema, globalSchemas = {}, options = {}) {
|
|
|
123
126
|
}
|
|
124
127
|
/* istanbul ignore if */
|
|
125
128
|
if (debug.enabled) {
|
|
126
|
-
debug('Invalid
|
|
129
|
+
debug('Invalid value: %s. Errors: %s', util_1.default.inspect(value, { depth: null }), util_1.default.inspect(validationErrors));
|
|
127
130
|
}
|
|
128
131
|
if (typeof options.ajvErrorTransformer === 'function') {
|
|
129
132
|
validationErrors = options.ajvErrorTransformer(validationErrors);
|
|
130
133
|
}
|
|
131
|
-
|
|
134
|
+
// Throw invalid request body error
|
|
135
|
+
if (options.source === 'body') {
|
|
136
|
+
const error = __1.RestHttpErrors.invalidRequestBody();
|
|
137
|
+
addErrorDetails(error, validationErrors);
|
|
138
|
+
throw error;
|
|
139
|
+
}
|
|
140
|
+
// Throw invalid value error
|
|
141
|
+
const error = new __1.HttpErrors.BadRequest('Invalid value.');
|
|
142
|
+
addErrorDetails(error, validationErrors);
|
|
143
|
+
throw error;
|
|
144
|
+
}
|
|
145
|
+
exports.validateValueAgainstSchema = validateValueAgainstSchema;
|
|
146
|
+
function addErrorDetails(
|
|
147
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
148
|
+
error, validationErrors) {
|
|
132
149
|
error.details = lodash_1.default.map(validationErrors, e => {
|
|
133
150
|
return {
|
|
134
151
|
path: e.dataPath,
|
|
@@ -137,7 +154,6 @@ body, schema, globalSchemas = {}, options = {}) {
|
|
|
137
154
|
info: e.params,
|
|
138
155
|
};
|
|
139
156
|
});
|
|
140
|
-
throw error;
|
|
141
157
|
}
|
|
142
158
|
/**
|
|
143
159
|
* Create a validate function for the given schema
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"request-body.validator.js","sourceRoot":"","sources":["../../src/validation/request-body.validator.ts"],"names":[],"mappings":";AAAA,sDAAsD;AACtD,8BAA8B;AAC9B,+CAA+C;AAC/C,gEAAgE;;;;AAEhE,qDAM8B;AAE9B,0DAAgC;AAChC,4DAAuB;AACvB,wDAAwB;AACxB,0BAA2D;
|
|
1
|
+
{"version":3,"file":"request-body.validator.js","sourceRoot":"","sources":["../../src/validation/request-body.validator.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@loopback/rest",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.2.0",
|
|
4
4
|
"description": "Expose controllers as REST endpoints and route REST API requests to controller methods",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -24,15 +24,15 @@
|
|
|
24
24
|
"access": "public"
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@loopback/core": "^2.9.
|
|
28
|
-
"@loopback/express": "^1.2.
|
|
29
|
-
"@loopback/http-server": "^2.1.
|
|
30
|
-
"@loopback/openapi-v3": "^3.4.
|
|
27
|
+
"@loopback/core": "^2.9.1",
|
|
28
|
+
"@loopback/express": "^1.2.5",
|
|
29
|
+
"@loopback/http-server": "^2.1.9",
|
|
30
|
+
"@loopback/openapi-v3": "^3.4.5",
|
|
31
31
|
"@openapi-contrib/openapi-schema-to-json-schema": "^3.0.0",
|
|
32
32
|
"@types/body-parser": "^1.19.0",
|
|
33
33
|
"@types/cors": "^2.8.6",
|
|
34
34
|
"@types/express": "^4.17.6",
|
|
35
|
-
"@types/express-serve-static-core": "^4.17.
|
|
35
|
+
"@types/express-serve-static-core": "^4.17.8",
|
|
36
36
|
"@types/http-errors": "^1.6.3",
|
|
37
37
|
"@types/on-finished": "^2.3.1",
|
|
38
38
|
"@types/serve-static": "1.13.4",
|
|
@@ -44,28 +44,28 @@
|
|
|
44
44
|
"cors": "^2.8.5",
|
|
45
45
|
"debug": "^4.1.1",
|
|
46
46
|
"express": "^4.17.1",
|
|
47
|
-
"http-errors": "^1.
|
|
47
|
+
"http-errors": "^1.8.0",
|
|
48
48
|
"js-yaml": "^3.14.0",
|
|
49
49
|
"json-schema-compare": "^0.2.2",
|
|
50
50
|
"lodash": "^4.17.15",
|
|
51
51
|
"on-finished": "^2.3.0",
|
|
52
52
|
"path-to-regexp": "^6.1.0",
|
|
53
53
|
"qs": "^6.9.4",
|
|
54
|
-
"strong-error-handler": "^3.
|
|
54
|
+
"strong-error-handler": "^3.5.0",
|
|
55
55
|
"tslib": "^2.0.0",
|
|
56
56
|
"type-is": "^1.6.18",
|
|
57
57
|
"validator": "^13.1.1"
|
|
58
58
|
},
|
|
59
59
|
"devDependencies": {
|
|
60
|
-
"@loopback/build": "^6.
|
|
61
|
-
"@loopback/eslint-config": "^8.0.
|
|
62
|
-
"@loopback/openapi-spec-builder": "^2.1.
|
|
63
|
-
"@loopback/repository": "^2.
|
|
64
|
-
"@loopback/testlab": "^3.
|
|
60
|
+
"@loopback/build": "^6.1.0",
|
|
61
|
+
"@loopback/eslint-config": "^8.0.3",
|
|
62
|
+
"@loopback/openapi-spec-builder": "^2.1.9",
|
|
63
|
+
"@loopback/repository": "^2.9.0",
|
|
64
|
+
"@loopback/testlab": "^3.2.0",
|
|
65
65
|
"@types/debug": "^4.1.5",
|
|
66
|
-
"@types/js-yaml": "^3.12.
|
|
66
|
+
"@types/js-yaml": "^3.12.5",
|
|
67
67
|
"@types/json-schema-compare": "^0.2.0",
|
|
68
|
-
"@types/lodash": "^4.14.
|
|
68
|
+
"@types/lodash": "^4.14.157",
|
|
69
69
|
"@types/multer": "^1.4.3",
|
|
70
70
|
"@types/node": "^10.17.26",
|
|
71
71
|
"@types/on-finished": "^2.3.1",
|
|
@@ -83,5 +83,5 @@
|
|
|
83
83
|
"url": "https://github.com/strongloop/loopback-next.git",
|
|
84
84
|
"directory": "packages/rest"
|
|
85
85
|
},
|
|
86
|
-
"gitHead": "
|
|
86
|
+
"gitHead": "b89db3d3b8be6a36e63e91c2331d217fda7538de"
|
|
87
87
|
}
|
|
@@ -3,9 +3,19 @@
|
|
|
3
3
|
// This file is licensed under the MIT License.
|
|
4
4
|
// License text available at https://opensource.org/licenses/MIT
|
|
5
5
|
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
isReferenceObject,
|
|
8
|
+
ParameterObject,
|
|
9
|
+
ReferenceObject,
|
|
10
|
+
SchemaObject,
|
|
11
|
+
} from '@loopback/openapi-v3';
|
|
7
12
|
import debugModule from 'debug';
|
|
8
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
RequestBodyValidationOptions,
|
|
15
|
+
RestHttpErrors,
|
|
16
|
+
validateValueAgainstSchema,
|
|
17
|
+
ValueValidationOptions,
|
|
18
|
+
} from '../';
|
|
9
19
|
import {parseJson} from '../parse-json';
|
|
10
20
|
import {
|
|
11
21
|
DateCoercionOptions,
|
|
@@ -27,18 +37,14 @@ const debug = debugModule('loopback:rest:coercion');
|
|
|
27
37
|
*
|
|
28
38
|
* @param data - The raw data get from http request
|
|
29
39
|
* @param schema - The parameter's schema defined in OpenAPI specification
|
|
40
|
+
* @param options - The ajv validation options
|
|
30
41
|
*/
|
|
31
|
-
export function coerceParameter(
|
|
42
|
+
export async function coerceParameter(
|
|
32
43
|
data: string | undefined | object,
|
|
33
44
|
spec: ParameterObject,
|
|
45
|
+
options?: ValueValidationOptions,
|
|
34
46
|
) {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
// If a query parameter is a url encoded Json object, the schema is defined under content['application/json']
|
|
38
|
-
if (!schema && spec.in === 'query' && spec.content) {
|
|
39
|
-
const jsonSpec = spec.content['application/json'];
|
|
40
|
-
schema = jsonSpec.schema;
|
|
41
|
-
}
|
|
47
|
+
const schema = extractSchemaFromSpec(spec);
|
|
42
48
|
|
|
43
49
|
if (!schema || isReferenceObject(schema)) {
|
|
44
50
|
debug(
|
|
@@ -72,7 +78,7 @@ export function coerceParameter(
|
|
|
72
78
|
case 'boolean':
|
|
73
79
|
return coerceBoolean(data, spec);
|
|
74
80
|
case 'object':
|
|
75
|
-
return coerceObject(data, spec);
|
|
81
|
+
return coerceObject(data, spec, options);
|
|
76
82
|
case 'string':
|
|
77
83
|
case 'password':
|
|
78
84
|
return coerceString(data, spec);
|
|
@@ -158,21 +164,55 @@ function coerceBoolean(data: string | object, spec: ParameterObject) {
|
|
|
158
164
|
throw RestHttpErrors.invalidData(data, spec.name);
|
|
159
165
|
}
|
|
160
166
|
|
|
161
|
-
function coerceObject(
|
|
167
|
+
async function coerceObject(
|
|
168
|
+
input: string | object,
|
|
169
|
+
spec: ParameterObject,
|
|
170
|
+
options?: RequestBodyValidationOptions,
|
|
171
|
+
) {
|
|
162
172
|
const data = parseJsonIfNeeded(input, spec);
|
|
163
173
|
|
|
164
|
-
if (data
|
|
174
|
+
if (data == null) {
|
|
165
175
|
// Skip any further checks and coercions, nothing we can do with `undefined`
|
|
166
|
-
return
|
|
176
|
+
return data;
|
|
167
177
|
}
|
|
168
178
|
|
|
169
179
|
if (typeof data !== 'object' || Array.isArray(data))
|
|
170
180
|
throw RestHttpErrors.invalidData(input, spec.name);
|
|
171
181
|
|
|
172
|
-
|
|
182
|
+
const schema = extractSchemaFromSpec(spec);
|
|
183
|
+
if (schema) {
|
|
184
|
+
// Apply coercion based on properties defined by spec.schema
|
|
185
|
+
await validateValueAgainstSchema(
|
|
186
|
+
data,
|
|
187
|
+
schema,
|
|
188
|
+
{},
|
|
189
|
+
{...options, coerceTypes: true, source: 'parameter'},
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
|
|
173
193
|
return data;
|
|
174
194
|
}
|
|
175
195
|
|
|
196
|
+
/**
|
|
197
|
+
* Extract the schema from an OpenAPI parameter specification. If the root level
|
|
198
|
+
* one not found, search from media type 'application/json'.
|
|
199
|
+
*
|
|
200
|
+
* @param spec The parameter specification
|
|
201
|
+
*/
|
|
202
|
+
function extractSchemaFromSpec(
|
|
203
|
+
spec: ParameterObject,
|
|
204
|
+
): SchemaObject | ReferenceObject | undefined {
|
|
205
|
+
let schema = spec.schema;
|
|
206
|
+
|
|
207
|
+
// If a query parameter is a url encoded Json object,
|
|
208
|
+
// the schema is defined under content['application/json']
|
|
209
|
+
if (!schema && spec.in === 'query') {
|
|
210
|
+
schema = spec.content?.['application/json']?.schema;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
return schema;
|
|
214
|
+
}
|
|
215
|
+
|
|
176
216
|
function parseJsonIfNeeded(
|
|
177
217
|
data: string | object,
|
|
178
218
|
spec: ParameterObject,
|
package/src/index.ts
CHANGED
|
@@ -21,6 +21,12 @@
|
|
|
21
21
|
*/
|
|
22
22
|
|
|
23
23
|
export * from '@loopback/openapi-v3';
|
|
24
|
+
export {
|
|
25
|
+
ErrorHandlerOptions,
|
|
26
|
+
ErrorWriterOptions,
|
|
27
|
+
StrongErrorHandler,
|
|
28
|
+
writeErrorToResponse,
|
|
29
|
+
} from 'strong-error-handler';
|
|
24
30
|
export * from './body-parsers';
|
|
25
31
|
export * from './http-handler';
|
|
26
32
|
export * from './keys';
|
package/src/parser.ts
CHANGED
|
@@ -83,7 +83,7 @@ async function buildOperationArguments(
|
|
|
83
83
|
}
|
|
84
84
|
const spec = paramSpec as ParameterObject;
|
|
85
85
|
const rawValue = getParamFromRequest(spec, request, pathParams);
|
|
86
|
-
const coercedValue = coerceParameter(rawValue, spec);
|
|
86
|
+
const coercedValue = await coerceParameter(rawValue, spec);
|
|
87
87
|
paramArgs.push(coercedValue);
|
|
88
88
|
}
|
|
89
89
|
|
package/src/types.ts
CHANGED
|
@@ -108,6 +108,17 @@ export type AjvKeyword = KeywordDefinition & {name: string};
|
|
|
108
108
|
*/
|
|
109
109
|
export type AjvFormat = FormatDefinition & {name: string};
|
|
110
110
|
|
|
111
|
+
/**
|
|
112
|
+
* Options for any value validation using AJV
|
|
113
|
+
*/
|
|
114
|
+
export interface ValueValidationOptions extends RequestBodyValidationOptions {
|
|
115
|
+
/**
|
|
116
|
+
* Where the data comes from. It can be 'body', 'path', 'header',
|
|
117
|
+
* 'query', 'cookie', etc...
|
|
118
|
+
*/
|
|
119
|
+
source?: string;
|
|
120
|
+
}
|
|
121
|
+
|
|
111
122
|
/**
|
|
112
123
|
* Options for request body validation using AJV
|
|
113
124
|
*/
|
|
@@ -15,7 +15,11 @@ import debugModule from 'debug';
|
|
|
15
15
|
import _ from 'lodash';
|
|
16
16
|
import util from 'util';
|
|
17
17
|
import {HttpErrors, RequestBody, RestHttpErrors} from '..';
|
|
18
|
-
import {
|
|
18
|
+
import {
|
|
19
|
+
RequestBodyValidationOptions,
|
|
20
|
+
SchemaValidatorCache,
|
|
21
|
+
ValueValidationOptions,
|
|
22
|
+
} from '../types';
|
|
19
23
|
import {AjvFactoryProvider} from './ajv-factory.provider';
|
|
20
24
|
|
|
21
25
|
const toJsonSchema = require('@openapi-contrib/openapi-schema-to-json-schema');
|
|
@@ -66,7 +70,10 @@ export async function validateRequestBody(
|
|
|
66
70
|
if (!schema) return;
|
|
67
71
|
|
|
68
72
|
options = {coerceTypes: !!body.coercionRequired, ...options};
|
|
69
|
-
await validateValueAgainstSchema(body.value, schema, globalSchemas,
|
|
73
|
+
await validateValueAgainstSchema(body.value, schema, globalSchemas, {
|
|
74
|
+
...options,
|
|
75
|
+
source: 'body',
|
|
76
|
+
});
|
|
70
77
|
}
|
|
71
78
|
|
|
72
79
|
/**
|
|
@@ -115,12 +122,12 @@ function getKeyForOptions(options: RequestBodyValidationOptions) {
|
|
|
115
122
|
* @param globalSchemas - Schema references.
|
|
116
123
|
* @param options - Request body validation options.
|
|
117
124
|
*/
|
|
118
|
-
async function validateValueAgainstSchema(
|
|
125
|
+
export async function validateValueAgainstSchema(
|
|
119
126
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
120
|
-
|
|
127
|
+
value: any,
|
|
121
128
|
schema: SchemaObject | ReferenceObject,
|
|
122
129
|
globalSchemas: SchemasObject = {},
|
|
123
|
-
options:
|
|
130
|
+
options: ValueValidationOptions = {},
|
|
124
131
|
) {
|
|
125
132
|
let validate: ajv.ValidateFunction | undefined;
|
|
126
133
|
|
|
@@ -145,10 +152,10 @@ async function validateValueAgainstSchema(
|
|
|
145
152
|
|
|
146
153
|
let validationErrors: ajv.ErrorObject[] = [];
|
|
147
154
|
try {
|
|
148
|
-
const validationResult = await validate(
|
|
149
|
-
// When
|
|
155
|
+
const validationResult = await validate(value);
|
|
156
|
+
// When value is optional & values is empty / null, ajv returns null
|
|
150
157
|
if (validationResult || validationResult === null) {
|
|
151
|
-
debug(
|
|
158
|
+
debug(`Value from ${options.source} passed AJV validation.`);
|
|
152
159
|
return;
|
|
153
160
|
}
|
|
154
161
|
} catch (error) {
|
|
@@ -158,8 +165,8 @@ async function validateValueAgainstSchema(
|
|
|
158
165
|
/* istanbul ignore if */
|
|
159
166
|
if (debug.enabled) {
|
|
160
167
|
debug(
|
|
161
|
-
'Invalid
|
|
162
|
-
util.inspect(
|
|
168
|
+
'Invalid value: %s. Errors: %s',
|
|
169
|
+
util.inspect(value, {depth: null}),
|
|
163
170
|
util.inspect(validationErrors),
|
|
164
171
|
);
|
|
165
172
|
}
|
|
@@ -168,7 +175,24 @@ async function validateValueAgainstSchema(
|
|
|
168
175
|
validationErrors = options.ajvErrorTransformer(validationErrors);
|
|
169
176
|
}
|
|
170
177
|
|
|
171
|
-
|
|
178
|
+
// Throw invalid request body error
|
|
179
|
+
if (options.source === 'body') {
|
|
180
|
+
const error = RestHttpErrors.invalidRequestBody();
|
|
181
|
+
addErrorDetails(error, validationErrors);
|
|
182
|
+
throw error;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Throw invalid value error
|
|
186
|
+
const error = new HttpErrors.BadRequest('Invalid value.');
|
|
187
|
+
addErrorDetails(error, validationErrors);
|
|
188
|
+
throw error;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function addErrorDetails(
|
|
192
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
193
|
+
error: any,
|
|
194
|
+
validationErrors: ajv.ErrorObject[],
|
|
195
|
+
) {
|
|
172
196
|
error.details = _.map(validationErrors, e => {
|
|
173
197
|
return {
|
|
174
198
|
path: e.dataPath,
|
|
@@ -177,7 +201,6 @@ async function validateValueAgainstSchema(
|
|
|
177
201
|
info: e.params,
|
|
178
202
|
};
|
|
179
203
|
});
|
|
180
|
-
throw error;
|
|
181
204
|
}
|
|
182
205
|
|
|
183
206
|
/**
|