@loopback/rest 1.23.0 → 1.26.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 +41 -0
- package/dist/body-parsers/body-parser.helpers.js +5 -2
- package/dist/body-parsers/body-parser.helpers.js.map +1 -1
- package/dist/body-parsers/body-parser.js +8 -4
- package/dist/body-parsers/body-parser.js.map +1 -1
- package/dist/coercion/coerce-parameter.js +9 -4
- package/dist/coercion/coerce-parameter.js.map +1 -1
- package/dist/coercion/utils.js +5 -2
- package/dist/coercion/utils.js.map +1 -1
- package/dist/coercion/validator.js +4 -2
- package/dist/coercion/validator.js.map +1 -1
- package/dist/index.d.ts +10 -10
- package/dist/index.js +13 -10
- package/dist/index.js.map +1 -1
- package/dist/keys.d.ts +1 -1
- package/dist/parser.js +7 -3
- package/dist/parser.js.map +1 -1
- package/dist/providers/log-error.provider.js +2 -1
- package/dist/providers/log-error.provider.js.map +1 -1
- package/dist/request-context.js +17 -11
- package/dist/request-context.js.map +1 -1
- package/dist/rest-http-error.d.ts +1 -1
- package/dist/rest-http-error.js +11 -8
- package/dist/rest-http-error.js.map +1 -1
- package/dist/rest.component.js +2 -1
- package/dist/rest.component.js.map +1 -1
- package/dist/rest.server.d.ts +2 -2
- package/dist/rest.server.js +20 -15
- package/dist/rest.server.js.map +1 -1
- package/dist/router/base-route.d.ts +8 -1
- package/dist/router/base-route.js +13 -0
- package/dist/router/base-route.js.map +1 -1
- package/dist/router/controller-route.js +10 -6
- package/dist/router/controller-route.js.map +1 -1
- package/dist/router/external-express-routes.d.ts +1 -1
- package/dist/router/external-express-routes.js +11 -8
- package/dist/router/external-express-routes.js.map +1 -1
- package/dist/router/handler-route.js +1 -1
- package/dist/router/handler-route.js.map +1 -1
- package/dist/router/openapi-path.js +18 -21
- package/dist/router/openapi-path.js.map +1 -1
- package/dist/router/router-base.js +2 -1
- package/dist/router/router-base.js.map +1 -1
- package/dist/router/routing-table.js +12 -8
- package/dist/router/routing-table.js.map +1 -1
- package/dist/router/trie-router.js +2 -1
- package/dist/router/trie-router.js.map +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/validation/request-body.validator.js +19 -14
- package/dist/validation/request-body.validator.js.map +1 -1
- package/dist/writer.js +2 -1
- package/dist/writer.js.map +1 -1
- package/package.json +19 -19
- package/src/body-parsers/body-parser.helpers.ts +1 -1
- package/src/body-parsers/body-parser.ts +3 -3
- package/src/coercion/coerce-parameter.ts +3 -3
- package/src/coercion/utils.ts +1 -1
- package/src/coercion/validator.ts +2 -2
- package/src/index.ts +11 -14
- package/src/keys.ts +1 -1
- package/src/parser.ts +2 -2
- package/src/providers/log-error.provider.ts +1 -1
- package/src/request-context.ts +10 -10
- package/src/rest-http-error.ts +4 -5
- package/src/rest.component.ts +1 -1
- package/src/rest.server.ts +11 -11
- package/src/router/base-route.ts +13 -1
- package/src/router/controller-route.ts +7 -5
- package/src/router/external-express-routes.ts +3 -4
- package/src/router/handler-route.ts +8 -2
- package/src/router/openapi-path.ts +24 -29
- package/src/router/router-base.ts +1 -1
- package/src/router/routing-table.ts +5 -5
- package/src/router/trie-router.ts +1 -1
- package/src/types.ts +1 -1
- package/src/validation/request-body.validator.ts +7 -7
- package/src/writer.ts +1 -1
package/src/router/base-route.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
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 {Context} from '@loopback/context';
|
|
6
|
+
import {Context, InvocationSource} from '@loopback/context';
|
|
7
7
|
import {OperationObject} from '@loopback/openapi-v3';
|
|
8
8
|
import {OperationArgs, OperationRetval} from '../types';
|
|
9
9
|
import {RouteEntry} from './route-entry';
|
|
@@ -38,4 +38,16 @@ export abstract class BaseRoute implements RouteEntry {
|
|
|
38
38
|
describe(): string {
|
|
39
39
|
return `"${this.verb} ${this.path}"`;
|
|
40
40
|
}
|
|
41
|
+
|
|
42
|
+
toString() {
|
|
43
|
+
return `${this.constructor.name} - ${this.verb} ${this.path}`;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export class RouteSource implements InvocationSource<RouteEntry> {
|
|
48
|
+
type = 'route';
|
|
49
|
+
constructor(readonly value: RouteEntry) {}
|
|
50
|
+
toString() {
|
|
51
|
+
return `${this.value.verb} ${this.value.path}`;
|
|
52
|
+
}
|
|
41
53
|
}
|
|
@@ -13,10 +13,10 @@ import {
|
|
|
13
13
|
} from '@loopback/context';
|
|
14
14
|
import {CoreBindings} from '@loopback/core';
|
|
15
15
|
import {OperationObject} from '@loopback/openapi-v3';
|
|
16
|
-
import
|
|
16
|
+
import HttpErrors from 'http-errors';
|
|
17
17
|
import {RestBindings} from '../keys';
|
|
18
18
|
import {OperationArgs, OperationRetval} from '../types';
|
|
19
|
-
import {BaseRoute} from './base-route';
|
|
19
|
+
import {BaseRoute, RouteSource} from './base-route';
|
|
20
20
|
|
|
21
21
|
/*
|
|
22
22
|
* A controller instance with open properties/methods
|
|
@@ -64,7 +64,7 @@ export class ControllerRoute<T> extends BaseRoute {
|
|
|
64
64
|
methodName?: string,
|
|
65
65
|
) {
|
|
66
66
|
const controllerName = spec['x-controller-name'] || controllerCtor.name;
|
|
67
|
-
methodName = methodName
|
|
67
|
+
methodName = methodName ?? spec['x-operation-name'];
|
|
68
68
|
|
|
69
69
|
if (!methodName) {
|
|
70
70
|
throw new Error(
|
|
@@ -90,7 +90,7 @@ export class ControllerRoute<T> extends BaseRoute {
|
|
|
90
90
|
);
|
|
91
91
|
|
|
92
92
|
this._controllerFactory =
|
|
93
|
-
controllerFactory
|
|
93
|
+
controllerFactory ?? createControllerFactoryForClass(controllerCtor);
|
|
94
94
|
this._controllerCtor = controllerCtor;
|
|
95
95
|
this._controllerName = controllerName || controllerCtor.name;
|
|
96
96
|
this._methodName = methodName;
|
|
@@ -138,7 +138,9 @@ export class ControllerRoute<T> extends BaseRoute {
|
|
|
138
138
|
);
|
|
139
139
|
}
|
|
140
140
|
// Invoke the method with dependency injection
|
|
141
|
-
return invokeMethod(controller, this._methodName, requestContext, args
|
|
141
|
+
return invokeMethod(controller, this._methodName, requestContext, args, {
|
|
142
|
+
source: new RouteSource(this),
|
|
143
|
+
});
|
|
142
144
|
}
|
|
143
145
|
}
|
|
144
146
|
|
|
@@ -9,11 +9,10 @@ import {
|
|
|
9
9
|
OperationObject,
|
|
10
10
|
SchemasObject,
|
|
11
11
|
} from '@loopback/openapi-v3';
|
|
12
|
-
import
|
|
13
|
-
import {RequestHandler} from 'express';
|
|
12
|
+
import express, {RequestHandler} from 'express';
|
|
14
13
|
import {PathParams} from 'express-serve-static-core';
|
|
15
|
-
import
|
|
16
|
-
import
|
|
14
|
+
import HttpErrors from 'http-errors';
|
|
15
|
+
import onFinished from 'on-finished';
|
|
17
16
|
import {ServeStaticOptions} from 'serve-static';
|
|
18
17
|
import {promisify} from 'util';
|
|
19
18
|
import {RequestContext} from '../request-context';
|
|
@@ -7,7 +7,7 @@ import {Context, invokeMethodWithInterceptors} from '@loopback/context';
|
|
|
7
7
|
import {OperationObject} from '@loopback/openapi-v3';
|
|
8
8
|
import {RestBindings} from '../keys';
|
|
9
9
|
import {OperationArgs, OperationRetval} from '../types';
|
|
10
|
-
import {BaseRoute} from './base-route';
|
|
10
|
+
import {BaseRoute, RouteSource} from './base-route';
|
|
11
11
|
|
|
12
12
|
export class Route extends BaseRoute {
|
|
13
13
|
constructor(
|
|
@@ -33,6 +33,12 @@ export class Route extends BaseRoute {
|
|
|
33
33
|
): Promise<OperationRetval> {
|
|
34
34
|
// Use `invokeMethodWithInterceptors` to invoke the handler function so
|
|
35
35
|
// that global interceptors are applied
|
|
36
|
-
return invokeMethodWithInterceptors(
|
|
36
|
+
return invokeMethodWithInterceptors(
|
|
37
|
+
requestContext,
|
|
38
|
+
this,
|
|
39
|
+
'_handler',
|
|
40
|
+
args,
|
|
41
|
+
{source: new RouteSource(this)},
|
|
42
|
+
);
|
|
37
43
|
}
|
|
38
44
|
}
|
|
@@ -13,42 +13,37 @@ import {parse} from 'path-to-regexp';
|
|
|
13
13
|
* allows `[A-Za-z0-9_]`
|
|
14
14
|
*/
|
|
15
15
|
const POSSIBLE_VARNAME_PATTERN = /\{([^\}]+)\}/g;
|
|
16
|
-
const
|
|
16
|
+
const VALID_VARNAME_PATTERN = /^[A-Za-z0-9_]+$/;
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
19
|
* Validate the path to be compatible with OpenAPI path template. No parameter
|
|
20
20
|
* modifier, custom pattern, or unnamed parameter is allowed.
|
|
21
21
|
*/
|
|
22
22
|
export function validateApiPath(path = '/') {
|
|
23
|
-
|
|
24
|
-
if (tokens.some(t => typeof t === 'object')) {
|
|
25
|
-
throw new Error(
|
|
26
|
-
`Invalid path template: '${path}'. Please use {param} instead of ':param'`,
|
|
27
|
-
);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const invalid = path.match(INVALID_VARNAME_PATTERN);
|
|
31
|
-
if (invalid) {
|
|
32
|
-
throw new Error(
|
|
33
|
-
`Invalid parameter name '${invalid[1]}' found in path '${path}'`,
|
|
34
|
-
);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const regexpPath = toExpressPath(path);
|
|
38
|
-
tokens = parse(regexpPath);
|
|
23
|
+
const tokens = parse(path);
|
|
39
24
|
for (const token of tokens) {
|
|
40
25
|
if (typeof token === 'string') continue;
|
|
41
|
-
if (typeof token
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
26
|
+
if (typeof token === 'object') {
|
|
27
|
+
const name = token.name;
|
|
28
|
+
if (typeof name === 'string' && name !== '') {
|
|
29
|
+
throw new Error(
|
|
30
|
+
`Invalid path template: '${path}'. Please use {${name}} instead of ':${name}'`,
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
if (typeof name === 'number') {
|
|
34
|
+
throw new Error(`Unnamed parameter is not allowed in path '${path}'`);
|
|
35
|
+
}
|
|
36
|
+
const valid = token.prefix.match(VALID_VARNAME_PATTERN);
|
|
37
|
+
if (!valid) {
|
|
38
|
+
throw new Error(
|
|
39
|
+
`Invalid parameter name '${token.prefix}' found in path '${path}'`,
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
if (['?', '+', '*'].includes(token.modifier)) {
|
|
43
|
+
throw new Error(
|
|
44
|
+
`Parameter modifier '{${token.prefix}}${token.modifier}' is not allowed in path '${path}`,
|
|
45
|
+
);
|
|
46
|
+
}
|
|
52
47
|
}
|
|
53
48
|
}
|
|
54
49
|
return path;
|
|
@@ -68,5 +63,5 @@ export function getPathVariables(path: string) {
|
|
|
68
63
|
export function toExpressPath(path: string) {
|
|
69
64
|
// Convert `.` to `\\.` so that path-to-regexp will treat it as the plain
|
|
70
65
|
// `.` character
|
|
71
|
-
return path.replace(POSSIBLE_VARNAME_PATTERN, ':$1').replace('.', '\\.');
|
|
66
|
+
return path.replace(POSSIBLE_VARNAME_PATTERN, '{:$1}').replace('.', '\\.');
|
|
72
67
|
}
|
|
@@ -109,7 +109,7 @@ export abstract class BaseRouter implements RestRouter {
|
|
|
109
109
|
*/
|
|
110
110
|
function normalizeVerb(verb: string) {
|
|
111
111
|
// Use lower case, default to `get`
|
|
112
|
-
return
|
|
112
|
+
return verb?.toLowerCase() || 'get';
|
|
113
113
|
}
|
|
114
114
|
|
|
115
115
|
/**
|
|
@@ -9,9 +9,9 @@ import {
|
|
|
9
9
|
ParameterObject,
|
|
10
10
|
PathObject,
|
|
11
11
|
} from '@loopback/openapi-v3';
|
|
12
|
-
import
|
|
13
|
-
import
|
|
14
|
-
import
|
|
12
|
+
import assert from 'assert';
|
|
13
|
+
import debugFactory from 'debug';
|
|
14
|
+
import HttpErrors from 'http-errors';
|
|
15
15
|
import {inspect} from 'util';
|
|
16
16
|
import {Request} from '../types';
|
|
17
17
|
import {
|
|
@@ -57,7 +57,7 @@ export class RoutingTable {
|
|
|
57
57
|
|
|
58
58
|
debug('Registering Controller with API %s', inspect(spec, {depth: null}));
|
|
59
59
|
|
|
60
|
-
const basePath = spec.basePath
|
|
60
|
+
const basePath = spec.basePath ?? '/';
|
|
61
61
|
for (const p in spec.paths) {
|
|
62
62
|
for (const verb in spec.paths[p]) {
|
|
63
63
|
const opSpec: OperationObject = spec.paths[p][verb];
|
|
@@ -154,6 +154,6 @@ export class RoutingTable {
|
|
|
154
154
|
|
|
155
155
|
function describeOperationParameters(opSpec: OperationObject) {
|
|
156
156
|
return ((opSpec.parameters as ParameterObject[]) || [])
|
|
157
|
-
.map(p =>
|
|
157
|
+
.map(p => p?.name || '')
|
|
158
158
|
.join(', ');
|
|
159
159
|
}
|
|
@@ -42,7 +42,7 @@ export class TrieRouter extends BaseRouter {
|
|
|
42
42
|
const route = found.node.value!;
|
|
43
43
|
if (route) {
|
|
44
44
|
debug('Route found: %s', inspect(route, {depth: 5}));
|
|
45
|
-
return createResolvedRoute(route, found.params
|
|
45
|
+
return createResolvedRoute(route, found.params ?? {});
|
|
46
46
|
}
|
|
47
47
|
}
|
|
48
48
|
return undefined;
|
package/src/types.ts
CHANGED
|
@@ -10,10 +10,10 @@ import {
|
|
|
10
10
|
SchemaObject,
|
|
11
11
|
SchemasObject,
|
|
12
12
|
} from '@loopback/openapi-v3';
|
|
13
|
-
import
|
|
14
|
-
import
|
|
15
|
-
import
|
|
16
|
-
import
|
|
13
|
+
import AJV from 'ajv';
|
|
14
|
+
import debugModule from 'debug';
|
|
15
|
+
import _ from 'lodash';
|
|
16
|
+
import util from 'util';
|
|
17
17
|
import {HttpErrors, RequestBody, RestHttpErrors} from '..';
|
|
18
18
|
import {RequestBodyValidationOptions, SchemaValidatorCache} from '../types';
|
|
19
19
|
|
|
@@ -38,7 +38,7 @@ export function validateRequestBody(
|
|
|
38
38
|
globalSchemas: SchemasObject = {},
|
|
39
39
|
options: RequestBodyValidationOptions = {},
|
|
40
40
|
) {
|
|
41
|
-
const required = requestBodySpec
|
|
41
|
+
const required = requestBodySpec?.required;
|
|
42
42
|
|
|
43
43
|
if (required && body.value == null) {
|
|
44
44
|
const err = Object.assign(
|
|
@@ -125,7 +125,7 @@ function validateValueAgainstSchema(
|
|
|
125
125
|
) {
|
|
126
126
|
let validate: AJV.ValidateFunction | undefined;
|
|
127
127
|
|
|
128
|
-
const cache = options.compiledSchemaCache
|
|
128
|
+
const cache = options.compiledSchemaCache ?? DEFAULT_COMPILED_SCHEMA_CACHE;
|
|
129
129
|
const key = getKeyForOptions(options);
|
|
130
130
|
|
|
131
131
|
let validatorMap: Map<string, AJV.ValidateFunction> | undefined;
|
|
@@ -136,7 +136,7 @@ function validateValueAgainstSchema(
|
|
|
136
136
|
|
|
137
137
|
if (!validate) {
|
|
138
138
|
validate = createValidator(schema, globalSchemas, options);
|
|
139
|
-
validatorMap = validatorMap
|
|
139
|
+
validatorMap = validatorMap ?? new Map();
|
|
140
140
|
validatorMap.set(key, validate);
|
|
141
141
|
cache.set(schema, validatorMap);
|
|
142
142
|
}
|
package/src/writer.ts
CHANGED
|
@@ -31,7 +31,7 @@ export function writeResultToResponse(
|
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
const isStream =
|
|
34
|
-
result instanceof Readable || typeof
|
|
34
|
+
result instanceof Readable || typeof result?.pipe === 'function';
|
|
35
35
|
|
|
36
36
|
if (isStream) {
|
|
37
37
|
response.setHeader('Content-Type', 'application/octet-stream');
|