@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.
Files changed (77) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/dist/body-parsers/body-parser.helpers.js +5 -2
  3. package/dist/body-parsers/body-parser.helpers.js.map +1 -1
  4. package/dist/body-parsers/body-parser.js +8 -4
  5. package/dist/body-parsers/body-parser.js.map +1 -1
  6. package/dist/coercion/coerce-parameter.js +9 -4
  7. package/dist/coercion/coerce-parameter.js.map +1 -1
  8. package/dist/coercion/utils.js +5 -2
  9. package/dist/coercion/utils.js.map +1 -1
  10. package/dist/coercion/validator.js +4 -2
  11. package/dist/coercion/validator.js.map +1 -1
  12. package/dist/index.d.ts +10 -10
  13. package/dist/index.js +13 -10
  14. package/dist/index.js.map +1 -1
  15. package/dist/keys.d.ts +1 -1
  16. package/dist/parser.js +7 -3
  17. package/dist/parser.js.map +1 -1
  18. package/dist/providers/log-error.provider.js +2 -1
  19. package/dist/providers/log-error.provider.js.map +1 -1
  20. package/dist/request-context.js +17 -11
  21. package/dist/request-context.js.map +1 -1
  22. package/dist/rest-http-error.d.ts +1 -1
  23. package/dist/rest-http-error.js +11 -8
  24. package/dist/rest-http-error.js.map +1 -1
  25. package/dist/rest.component.js +2 -1
  26. package/dist/rest.component.js.map +1 -1
  27. package/dist/rest.server.d.ts +2 -2
  28. package/dist/rest.server.js +20 -15
  29. package/dist/rest.server.js.map +1 -1
  30. package/dist/router/base-route.d.ts +8 -1
  31. package/dist/router/base-route.js +13 -0
  32. package/dist/router/base-route.js.map +1 -1
  33. package/dist/router/controller-route.js +10 -6
  34. package/dist/router/controller-route.js.map +1 -1
  35. package/dist/router/external-express-routes.d.ts +1 -1
  36. package/dist/router/external-express-routes.js +11 -8
  37. package/dist/router/external-express-routes.js.map +1 -1
  38. package/dist/router/handler-route.js +1 -1
  39. package/dist/router/handler-route.js.map +1 -1
  40. package/dist/router/openapi-path.js +18 -21
  41. package/dist/router/openapi-path.js.map +1 -1
  42. package/dist/router/router-base.js +2 -1
  43. package/dist/router/router-base.js.map +1 -1
  44. package/dist/router/routing-table.js +12 -8
  45. package/dist/router/routing-table.js.map +1 -1
  46. package/dist/router/trie-router.js +2 -1
  47. package/dist/router/trie-router.js.map +1 -1
  48. package/dist/types.d.ts +1 -1
  49. package/dist/validation/request-body.validator.js +19 -14
  50. package/dist/validation/request-body.validator.js.map +1 -1
  51. package/dist/writer.js +2 -1
  52. package/dist/writer.js.map +1 -1
  53. package/package.json +19 -19
  54. package/src/body-parsers/body-parser.helpers.ts +1 -1
  55. package/src/body-parsers/body-parser.ts +3 -3
  56. package/src/coercion/coerce-parameter.ts +3 -3
  57. package/src/coercion/utils.ts +1 -1
  58. package/src/coercion/validator.ts +2 -2
  59. package/src/index.ts +11 -14
  60. package/src/keys.ts +1 -1
  61. package/src/parser.ts +2 -2
  62. package/src/providers/log-error.provider.ts +1 -1
  63. package/src/request-context.ts +10 -10
  64. package/src/rest-http-error.ts +4 -5
  65. package/src/rest.component.ts +1 -1
  66. package/src/rest.server.ts +11 -11
  67. package/src/router/base-route.ts +13 -1
  68. package/src/router/controller-route.ts +7 -5
  69. package/src/router/external-express-routes.ts +3 -4
  70. package/src/router/handler-route.ts +8 -2
  71. package/src/router/openapi-path.ts +24 -29
  72. package/src/router/router-base.ts +1 -1
  73. package/src/router/routing-table.ts +5 -5
  74. package/src/router/trie-router.ts +1 -1
  75. package/src/types.ts +1 -1
  76. package/src/validation/request-body.validator.ts +7 -7
  77. package/src/writer.ts +1 -1
@@ -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 * as HttpErrors from 'http-errors';
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 || spec['x-operation-name'];
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 || createControllerFactoryForClass(controllerCtor);
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 * as express from 'express';
13
- import {RequestHandler} from 'express';
12
+ import express, {RequestHandler} from 'express';
14
13
  import {PathParams} from 'express-serve-static-core';
15
- import * as HttpErrors from 'http-errors';
16
- import * as onFinished from 'on-finished';
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(requestContext, this, '_handler', args);
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 INVALID_VARNAME_PATTERN = /\{([^\}]*[^\w\}][^\}]*)\}/;
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
- let tokens = parse(path);
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.name === 'number') {
42
- // Such as /(.*)
43
- throw new Error(`Unnamed parameter is not allowed in path '${path}'`);
44
- }
45
- if (
46
- (token.optional || token.repeat || token.pattern !== '[^\\/]+?') &&
47
- // Required by path-to-regexp@3.x
48
- token.prefix === '/'
49
- ) {
50
- // Such as /:foo*, /:foo+, /:foo?, or /:foo(\\d+)
51
- throw new Error(`Parameter modifier is not allowed in path '${path}'`);
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 (verb && verb.toLowerCase()) || 'get';
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 * as assert from 'assert';
13
- import * as debugFactory from 'debug';
14
- import * as HttpErrors from 'http-errors';
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 => (p && p.name) || '')
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
@@ -5,7 +5,7 @@
5
5
 
6
6
  import {Binding, BoundValue} from '@loopback/context';
7
7
  import {ReferenceObject, SchemaObject} from '@loopback/openapi-v3';
8
- import * as ajv from 'ajv';
8
+ import ajv from 'ajv';
9
9
  import {
10
10
  Options,
11
11
  OptionsJson,
@@ -10,10 +10,10 @@ import {
10
10
  SchemaObject,
11
11
  SchemasObject,
12
12
  } from '@loopback/openapi-v3';
13
- import * as AJV from 'ajv';
14
- import * as debugModule from 'debug';
15
- import * as _ from 'lodash';
16
- import * as util from 'util';
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 && requestBodySpec.required;
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 || DEFAULT_COMPILED_SCHEMA_CACHE;
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 || new Map();
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 (result && result.pipe) === 'function';
34
+ result instanceof Readable || typeof result?.pipe === 'function';
35
35
 
36
36
  if (isStream) {
37
37
  response.setHeader('Content-Type', 'application/octet-stream');