@loopback/rest 6.1.0 → 8.0.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 (78) hide show
  1. package/CHANGELOG.md +109 -0
  2. package/dist/body-parsers/body-parser.js +3 -0
  3. package/dist/body-parsers/body-parser.js.map +1 -1
  4. package/dist/coercion/coerce-parameter.d.ts +1 -1
  5. package/dist/coercion/coerce-parameter.js +36 -13
  6. package/dist/coercion/coerce-parameter.js.map +1 -1
  7. package/dist/coercion/utils.d.ts +1 -1
  8. package/dist/http-handler.js +5 -0
  9. package/dist/http-handler.js.map +1 -1
  10. package/dist/index.js +1 -1
  11. package/dist/providers/find-route.provider.d.ts +0 -2
  12. package/dist/providers/find-route.provider.js +11 -8
  13. package/dist/providers/find-route.provider.js.map +1 -1
  14. package/dist/providers/invoke-method.provider.d.ts +0 -2
  15. package/dist/providers/invoke-method.provider.js +21 -10
  16. package/dist/providers/invoke-method.provider.js.map +1 -1
  17. package/dist/providers/log-error.provider.js +7 -2
  18. package/dist/providers/log-error.provider.js.map +1 -1
  19. package/dist/providers/parse-params.provider.d.ts +0 -2
  20. package/dist/providers/parse-params.provider.js +16 -8
  21. package/dist/providers/parse-params.provider.js.map +1 -1
  22. package/dist/providers/reject.provider.d.ts +1 -1
  23. package/dist/providers/reject.provider.js +2 -1
  24. package/dist/providers/reject.provider.js.map +1 -1
  25. package/dist/providers/send.provider.d.ts +1 -4
  26. package/dist/providers/send.provider.js +11 -13
  27. package/dist/providers/send.provider.js.map +1 -1
  28. package/dist/request-context.js.map +1 -1
  29. package/dist/rest.application.js +1 -1
  30. package/dist/rest.application.js.map +1 -1
  31. package/dist/rest.server.d.ts +1 -0
  32. package/dist/rest.server.js +5 -4
  33. package/dist/rest.server.js.map +1 -1
  34. package/dist/router/base-route.js +3 -3
  35. package/dist/router/base-route.js.map +1 -1
  36. package/dist/router/controller-route.js +1 -1
  37. package/dist/router/controller-route.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/redirect-route.js +1 -1
  41. package/dist/router/redirect-route.js.map +1 -1
  42. package/dist/sequence.js +1 -1
  43. package/dist/sequence.js.map +1 -1
  44. package/dist/spec-enhancers/consolidate.spec-enhancer.js +1 -1
  45. package/dist/spec-enhancers/consolidate.spec-enhancer.js.map +1 -1
  46. package/dist/spec-enhancers/info.spec-enhancer.js +13 -5
  47. package/dist/spec-enhancers/info.spec-enhancer.js.map +1 -1
  48. package/dist/validation/ajv-factory.provider.js +5 -3
  49. package/dist/validation/ajv-factory.provider.js.map +1 -1
  50. package/dist/validation/openapi-formats.d.ts +26 -0
  51. package/dist/validation/openapi-formats.js +85 -0
  52. package/dist/validation/openapi-formats.js.map +1 -0
  53. package/dist/validation/request-body.validator.d.ts +4 -4
  54. package/dist/validation/request-body.validator.js +5 -8
  55. package/dist/validation/request-body.validator.js.map +1 -1
  56. package/package.json +25 -22
  57. package/src/body-parsers/body-parser.ts +3 -0
  58. package/src/coercion/coerce-parameter.ts +43 -19
  59. package/src/http-handler.ts +6 -0
  60. package/src/providers/find-route.provider.ts +24 -8
  61. package/src/providers/invoke-method.provider.ts +28 -10
  62. package/src/providers/log-error.provider.ts +2 -1
  63. package/src/providers/parse-params.provider.ts +28 -8
  64. package/src/providers/reject.provider.ts +4 -3
  65. package/src/providers/send.provider.ts +9 -12
  66. package/src/request-context.ts +2 -1
  67. package/src/rest.application.ts +1 -1
  68. package/src/rest.server.ts +8 -5
  69. package/src/router/base-route.ts +3 -3
  70. package/src/router/controller-route.ts +1 -1
  71. package/src/router/handler-route.ts +3 -1
  72. package/src/router/redirect-route.ts +1 -1
  73. package/src/sequence.ts +2 -2
  74. package/src/spec-enhancers/consolidate.spec-enhancer.ts +2 -2
  75. package/src/spec-enhancers/info.spec-enhancer.ts +15 -6
  76. package/src/validation/ajv-factory.provider.ts +8 -4
  77. package/src/validation/openapi-formats.ts +92 -0
  78. package/src/validation/request-body.validator.ts +8 -8
@@ -0,0 +1,85 @@
1
+ "use strict";
2
+ // Copyright IBM Corp. 2020. All Rights Reserved.
3
+ // Node module: @loopback/rest
4
+ // This file is licensed under the MIT License.
5
+ // License text available at https://opensource.org/licenses/MIT
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.openapiFormats = exports.binaryFormat = exports.byteFormat = exports.doubleFormat = exports.floatFormat = exports.int64Format = exports.int32Format = void 0;
8
+ /**
9
+ * int32: [-2147483648, 21474836 47]
10
+ */
11
+ exports.int32Format = {
12
+ name: 'int32',
13
+ type: 'number',
14
+ validate: (value) => {
15
+ return (Number.isInteger(value) && value >= -2147483648 && value <= 2147483647);
16
+ },
17
+ async: false,
18
+ };
19
+ /**
20
+ * int64: [-9223372036854775808, 9223372036854775807]
21
+ */
22
+ exports.int64Format = {
23
+ name: 'int64',
24
+ type: 'number',
25
+ validate: (value) => {
26
+ const max = Number.MAX_SAFE_INTEGER; // 9007199254740991
27
+ const min = Number.MIN_SAFE_INTEGER; // -9007199254740991
28
+ return Number.isInteger(value) && value >= min && value <= max;
29
+ },
30
+ async: false,
31
+ };
32
+ /**
33
+ * float: [-2^128, 2^128]
34
+ */
35
+ exports.floatFormat = {
36
+ name: 'float',
37
+ type: 'number',
38
+ validate: (value) => {
39
+ return value >= -Math.pow(2, 128) && value <= Math.pow(2, 128);
40
+ },
41
+ async: false,
42
+ };
43
+ /**
44
+ * double: [-2^1024, 2^1024]
45
+ */
46
+ exports.doubleFormat = {
47
+ name: 'double',
48
+ type: 'number',
49
+ validate: (value) => {
50
+ const max = Number.MAX_VALUE; // 1.7976931348623157e+308
51
+ const min = -Number.MAX_VALUE; // -1.7976931348623157e+308
52
+ return value >= min && value <= max;
53
+ },
54
+ async: false,
55
+ };
56
+ /**
57
+ * Base64 encoded string
58
+ */
59
+ exports.byteFormat = {
60
+ name: 'byte',
61
+ type: 'string',
62
+ validate: (value) => {
63
+ const base64 = Buffer.from(value, 'base64').toString('base64');
64
+ return value === base64;
65
+ },
66
+ async: false,
67
+ };
68
+ /**
69
+ * Binary string
70
+ */
71
+ exports.binaryFormat = {
72
+ name: 'binary',
73
+ type: 'string',
74
+ validate: (value) => true,
75
+ async: false,
76
+ };
77
+ exports.openapiFormats = [
78
+ exports.int32Format,
79
+ exports.int64Format,
80
+ exports.floatFormat,
81
+ exports.doubleFormat,
82
+ exports.byteFormat,
83
+ exports.binaryFormat,
84
+ ];
85
+ //# sourceMappingURL=openapi-formats.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openapi-formats.js","sourceRoot":"","sources":["../../src/validation/openapi-formats.ts"],"names":[],"mappings":";AAAA,iDAAiD;AACjD,8BAA8B;AAC9B,+CAA+C;AAC/C,gEAAgE;;;AAIhE;;GAEG;AACU,QAAA,WAAW,GAAc;IACpC,IAAI,EAAE,OAAO;IACb,IAAI,EAAE,QAAQ;IACd,QAAQ,EAAE,CAAC,KAAa,EAAE,EAAE;QAC1B,OAAO,CACL,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,UAAU,IAAI,KAAK,IAAI,UAAU,CACvE,CAAC;IACJ,CAAC;IACD,KAAK,EAAE,KAAK;CACb,CAAC;AAEF;;GAEG;AACU,QAAA,WAAW,GAAc;IACpC,IAAI,EAAE,OAAO;IACb,IAAI,EAAE,QAAQ;IACd,QAAQ,EAAE,CAAC,KAAa,EAAE,EAAE;QAC1B,MAAM,GAAG,GAAG,MAAM,CAAC,gBAAgB,CAAC,CAAC,mBAAmB;QACxD,MAAM,GAAG,GAAG,MAAM,CAAC,gBAAgB,CAAC,CAAC,oBAAoB;QACzD,OAAO,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,GAAG,IAAI,KAAK,IAAI,GAAG,CAAC;IACjE,CAAC;IACD,KAAK,EAAE,KAAK;CACb,CAAC;AAEF;;GAEG;AACU,QAAA,WAAW,GAAc;IACpC,IAAI,EAAE,OAAO;IACb,IAAI,EAAE,QAAQ;IACd,QAAQ,EAAE,CAAC,KAAa,EAAE,EAAE;QAC1B,OAAO,KAAK,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC,IAAI,KAAK,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IACjE,CAAC;IACD,KAAK,EAAE,KAAK;CACb,CAAC;AAEF;;GAEG;AACU,QAAA,YAAY,GAAc;IACrC,IAAI,EAAE,QAAQ;IACd,IAAI,EAAE,QAAQ;IACd,QAAQ,EAAE,CAAC,KAAa,EAAE,EAAE;QAC1B,MAAM,GAAG,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC,0BAA0B;QACxD,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,2BAA2B;QAC1D,OAAO,KAAK,IAAI,GAAG,IAAI,KAAK,IAAI,GAAG,CAAC;IACtC,CAAC;IACD,KAAK,EAAE,KAAK;CACb,CAAC;AAEF;;GAEG;AACU,QAAA,UAAU,GAAc;IACnC,IAAI,EAAE,MAAM;IACZ,IAAI,EAAE,QAAQ;IACd,QAAQ,EAAE,CAAC,KAAa,EAAE,EAAE;QAC1B,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QAC/D,OAAO,KAAK,KAAK,MAAM,CAAC;IAC1B,CAAC;IACD,KAAK,EAAE,KAAK;CACb,CAAC;AAEF;;GAEG;AACU,QAAA,YAAY,GAAc;IACrC,IAAI,EAAE,QAAQ;IACd,IAAI,EAAE,QAAQ;IACd,QAAQ,EAAE,CAAC,KAAa,EAAE,EAAE,CAAC,IAAI;IACjC,KAAK,EAAE,KAAK;CACb,CAAC;AAEW,QAAA,cAAc,GAAgB;IACzC,mBAAW;IACX,mBAAW;IACX,mBAAW;IACX,oBAAY;IACZ,kBAAU;IACV,oBAAY;CACb,CAAC"}
@@ -13,10 +13,10 @@ import { ValidationOptions, ValueValidationOptions } from '../types';
13
13
  */
14
14
  export declare function validateRequestBody(body: RequestBody, requestBodySpec?: RequestBodyObject, globalSchemas?: SchemasObject, options?: ValidationOptions): Promise<void>;
15
15
  /**
16
- * Validate the request body data against JSON schema.
17
- * @param body - The request body data.
16
+ * Validate the value against JSON schema.
17
+ * @param value - The data value.
18
18
  * @param schema - The JSON schema used to perform the validation.
19
19
  * @param globalSchemas - Schema references.
20
- * @param options - Request body validation options.
20
+ * @param options - Value validation options.
21
21
  */
22
- export declare function validateValueAgainstSchema(value: any, schema: SchemaObject | ReferenceObject, globalSchemas?: SchemasObject, options?: ValueValidationOptions): Promise<void>;
22
+ export declare function validateValueAgainstSchema(value: any, schema: SchemaObject | ReferenceObject, globalSchemas?: SchemasObject, options?: ValueValidationOptions): Promise<any>;
@@ -85,11 +85,11 @@ function getKeyForOptions(options = ajv_factory_provider_1.DEFAULT_AJV_VALIDATIO
85
85
  return JSON.stringify(ajvOptions);
86
86
  }
87
87
  /**
88
- * Validate the request body data against JSON schema.
89
- * @param body - The request body data.
88
+ * Validate the value against JSON schema.
89
+ * @param value - The data value.
90
90
  * @param schema - The JSON schema used to perform the validation.
91
91
  * @param globalSchemas - Schema references.
92
- * @param options - Request body validation options.
92
+ * @param options - Value validation options.
93
93
  */
94
94
  async function validateValueAgainstSchema(
95
95
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -114,11 +114,8 @@ value, schema, globalSchemas = {}, options = {}) {
114
114
  let validationErrors = [];
115
115
  try {
116
116
  const validationResult = await validate(value);
117
- // When value is optional & values is empty / null, ajv returns null
118
- if (validationResult || validationResult === null) {
119
- debug(`Value from ${options.source} passed AJV validation.`);
120
- return;
121
- }
117
+ debug(`Value from ${options.source} passed AJV validation.`, validationResult);
118
+ return validationResult;
122
119
  }
123
120
  catch (error) {
124
121
  validationErrors = error.errors;
@@ -1 +1 @@
1
- {"version":3,"file":"request-body.validator.js","sourceRoot":"","sources":["../../src/validation/request-body.validator.ts"],"names":[],"mappings":"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"}
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,11 +1,11 @@
1
1
  {
2
2
  "name": "@loopback/rest",
3
- "version": "6.1.0",
3
+ "version": "8.0.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",
7
7
  "engines": {
8
- "node": ">=10.16"
8
+ "node": "^10.16 || 12 || 14"
9
9
  },
10
10
  "scripts": {
11
11
  "acceptance": "lb-mocha \"dist/__tests__/acceptance/**/*.js\"",
@@ -23,53 +23,56 @@
23
23
  "publishConfig": {
24
24
  "access": "public"
25
25
  },
26
+ "peerDependencies": {
27
+ "@loopback/core": "^2.11.0"
28
+ },
26
29
  "dependencies": {
27
- "@loopback/core": "^2.9.4",
28
- "@loopback/express": "^1.4.0",
29
- "@loopback/http-server": "^2.1.12",
30
- "@loopback/openapi-v3": "^3.4.8",
30
+ "@loopback/express": "^2.1.0",
31
+ "@loopback/http-server": "^2.3.0",
32
+ "@loopback/openapi-v3": "^5.0.0",
31
33
  "@openapi-contrib/openapi-schema-to-json-schema": "^3.0.4",
32
34
  "@types/body-parser": "^1.19.0",
33
35
  "@types/cors": "^2.8.7",
34
- "@types/express": "^4.17.7",
35
- "@types/express-serve-static-core": "^4.17.9",
36
+ "@types/express": "^4.17.8",
37
+ "@types/express-serve-static-core": "^4.17.13",
36
38
  "@types/http-errors": "^1.8.0",
37
39
  "@types/on-finished": "^2.3.1",
38
40
  "@types/serve-static": "1.13.5",
39
41
  "@types/type-is": "^1.6.3",
40
- "ajv": "^6.12.4",
42
+ "ajv": "^6.12.5",
41
43
  "ajv-errors": "^1.0.1",
42
44
  "ajv-keywords": "^3.5.2",
43
45
  "body-parser": "^1.19.0",
44
46
  "cors": "^2.8.5",
45
- "debug": "^4.1.1",
47
+ "debug": "^4.2.0",
46
48
  "express": "^4.17.1",
47
49
  "http-errors": "^1.8.0",
48
50
  "js-yaml": "^3.14.0",
49
51
  "json-schema-compare": "^0.2.2",
50
52
  "lodash": "^4.17.20",
51
53
  "on-finished": "^2.3.0",
52
- "path-to-regexp": "^6.1.0",
54
+ "path-to-regexp": "^6.2.0",
53
55
  "qs": "^6.9.4",
54
56
  "strong-error-handler": "^3.5.0",
55
- "tslib": "^2.0.1",
57
+ "tslib": "^2.0.2",
56
58
  "type-is": "^1.6.18",
57
- "validator": "^13.1.1"
59
+ "validator": "^13.1.17"
58
60
  },
59
61
  "devDependencies": {
60
- "@loopback/build": "^6.2.1",
61
- "@loopback/eslint-config": "^9.0.1",
62
- "@loopback/openapi-spec-builder": "^2.1.12",
63
- "@loopback/repository": "^2.11.1",
64
- "@loopback/testlab": "^3.2.3",
62
+ "@loopback/build": "^6.2.5",
63
+ "@loopback/core": "^2.11.0",
64
+ "@loopback/eslint-config": "^10.0.1",
65
+ "@loopback/openapi-spec-builder": "^3.0.0",
66
+ "@loopback/repository": "^3.1.0",
67
+ "@loopback/testlab": "^3.2.7",
65
68
  "@types/debug": "^4.1.5",
66
69
  "@types/js-yaml": "^3.12.5",
67
70
  "@types/json-schema-compare": "^0.2.0",
68
- "@types/lodash": "^4.14.159",
71
+ "@types/lodash": "^4.14.161",
69
72
  "@types/multer": "^1.4.4",
70
- "@types/node": "^10.17.28",
73
+ "@types/node": "^10.17.35",
71
74
  "@types/on-finished": "^2.3.1",
72
- "@types/qs": "^6.9.4",
75
+ "@types/qs": "^6.9.5",
73
76
  "multer": "^1.4.2"
74
77
  },
75
78
  "files": [
@@ -83,5 +86,5 @@
83
86
  "url": "https://github.com/strongloop/loopback-next.git",
84
87
  "directory": "packages/rest"
85
88
  },
86
- "gitHead": "a056555744367da9caccb6be64095e27d2fe2e28"
89
+ "gitHead": "390f2794d10eea3d969ae417963af815ce1bc417"
87
90
  }
@@ -61,15 +61,18 @@ export class RequestBodyParser {
61
61
  if (customParser) {
62
62
  // Invoke the custom parser
63
63
  const body = await this._invokeCustomParser(customParser, request);
64
+ debug('Parsed request body', body);
64
65
  return Object.assign(requestBody, body);
65
66
  } else {
66
67
  const parser = this._findParser(matchedMediaType);
67
68
  if (parser) {
68
69
  const body = await parser.parse(request);
70
+ debug('Parsed request body', body);
69
71
  return Object.assign(requestBody, body);
70
72
  }
71
73
  }
72
74
  } catch (err) {
75
+ debug('Request body parsing error', err);
73
76
  throw normalizeParsingError(err);
74
77
  }
75
78
 
@@ -13,10 +13,10 @@ import debugModule from 'debug';
13
13
  import {
14
14
  RestHttpErrors,
15
15
  validateValueAgainstSchema,
16
- ValidationOptions,
17
16
  ValueValidationOptions,
18
17
  } from '../';
19
18
  import {parseJson} from '../parse-json';
19
+ import {ValidationOptions} from '../types';
20
20
  import {DEFAULT_AJV_VALIDATION_OPTIONS} from '../validation/ajv-factory.provider';
21
21
  import {
22
22
  DateCoercionOptions,
@@ -61,31 +61,51 @@ export async function coerceParameter(
61
61
  validator.validateParamBeforeCoercion(data);
62
62
  if (data === undefined) return data;
63
63
 
64
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
65
+ let result: any = data;
66
+
64
67
  switch (OAIType) {
65
68
  case 'byte':
66
- return coerceBuffer(data, spec);
69
+ result = coerceBuffer(data, spec);
70
+ break;
67
71
  case 'date':
68
- return coerceDatetime(data, spec, {dateOnly: true});
72
+ result = coerceDatetime(data, spec, {dateOnly: true});
73
+ break;
69
74
  case 'date-time':
70
- return coerceDatetime(data, spec);
75
+ result = coerceDatetime(data, spec);
76
+ break;
71
77
  case 'float':
72
78
  case 'double':
73
79
  case 'number':
74
- return coerceNumber(data, spec);
80
+ result = coerceNumber(data, spec);
81
+ break;
75
82
  case 'long':
76
- return coerceInteger(data, spec, {isLong: true});
83
+ result = coerceInteger(data, spec, {isLong: true});
84
+ break;
77
85
  case 'integer':
78
- return coerceInteger(data, spec);
86
+ result = coerceInteger(data, spec);
87
+ break;
79
88
  case 'boolean':
80
- return coerceBoolean(data, spec);
89
+ result = coerceBoolean(data, spec);
90
+ break;
81
91
  case 'object':
82
- return coerceObject(data, spec, options);
92
+ result = await coerceObject(data, spec);
93
+ break;
83
94
  case 'string':
84
95
  case 'password':
85
- return coerceString(data, spec);
86
- default:
87
- return data;
96
+ result = coerceString(data, spec);
97
+ break;
98
+ }
99
+
100
+ if (result != null) {
101
+ // For date/date-time/byte, we need to pass the raw string value to AJV
102
+ if (OAIType === 'date' || OAIType === 'date-time' || OAIType === 'byte') {
103
+ await validateParam(spec, data, options);
104
+ return result;
105
+ }
106
+ result = await validateParam(spec, result, options);
88
107
  }
108
+ return result;
89
109
  }
90
110
 
91
111
  function coerceString(data: string | object, spec: ParameterObject) {
@@ -165,11 +185,7 @@ function coerceBoolean(data: string | object, spec: ParameterObject) {
165
185
  throw RestHttpErrors.invalidData(data, spec.name);
166
186
  }
167
187
 
168
- async function coerceObject(
169
- input: string | object,
170
- spec: ParameterObject,
171
- options: ValidationOptions = DEFAULT_AJV_VALIDATION_OPTIONS,
172
- ) {
188
+ async function coerceObject(input: string | object, spec: ParameterObject) {
173
189
  const data = parseJsonIfNeeded(input, spec);
174
190
 
175
191
  if (data == null) {
@@ -180,17 +196,25 @@ async function coerceObject(
180
196
  if (typeof data !== 'object' || Array.isArray(data))
181
197
  throw RestHttpErrors.invalidData(input, spec.name);
182
198
 
199
+ return data;
200
+ }
201
+
202
+ function validateParam(
203
+ spec: ParameterObject,
204
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
205
+ data: any,
206
+ options: ValidationOptions = DEFAULT_AJV_VALIDATION_OPTIONS,
207
+ ) {
183
208
  const schema = extractSchemaFromSpec(spec);
184
209
  if (schema) {
185
210
  // Apply coercion based on properties defined by spec.schema
186
- await validateValueAgainstSchema(
211
+ return validateValueAgainstSchema(
187
212
  data,
188
213
  schema,
189
214
  {},
190
215
  {...options, coerceTypes: true, source: 'parameter', name: spec.name},
191
216
  );
192
217
  }
193
-
194
218
  return data;
195
219
  }
196
220
 
@@ -4,6 +4,7 @@
4
4
  // License text available at https://opensource.org/licenses/MIT
5
5
 
6
6
  import {Context} from '@loopback/core';
7
+ import {MIDDLEWARE_CONTEXT} from '@loopback/express';
7
8
  import {
8
9
  ComponentsObject,
9
10
  ControllerSpec,
@@ -109,6 +110,11 @@ export class HttpHandler {
109
110
  this._serverConfig,
110
111
  );
111
112
 
113
+ // Set the request context as a property of Express request object so that
114
+ // downstream Express native integration can access `RequestContext`
115
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
116
+ (request as any)[MIDDLEWARE_CONTEXT] = requestContext;
117
+
112
118
  const sequence = await requestContext.get<SequenceHandler>(
113
119
  RestBindings.SEQUENCE,
114
120
  );
@@ -3,14 +3,23 @@
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 {bind, Context, inject, Provider} from '@loopback/core';
6
+ import {
7
+ BindingScope,
8
+ Context,
9
+ inject,
10
+ injectable,
11
+ Provider,
12
+ } from '@loopback/core';
7
13
  import {asMiddleware, Middleware} from '@loopback/express';
14
+ import debugFactory from 'debug';
8
15
  import {HttpHandler} from '../http-handler';
9
16
  import {RestBindings, RestTags} from '../keys';
10
17
  import {ResolvedRoute} from '../router';
11
18
  import {RestMiddlewareGroups} from '../sequence';
12
19
  import {FindRoute, Request} from '../types';
13
20
 
21
+ const debug = debugFactory('loopback:rest:find-route');
22
+
14
23
  export class FindRouteProvider implements Provider<FindRoute> {
15
24
  constructor(
16
25
  @inject(RestBindings.Http.CONTEXT) protected context: Context,
@@ -23,26 +32,33 @@ export class FindRouteProvider implements Provider<FindRoute> {
23
32
 
24
33
  action(request: Request): ResolvedRoute {
25
34
  const found = this.handler.findRoute(request);
35
+ debug('Route found for %s %s', request.method, request.originalUrl, found);
26
36
  found.updateBindings(this.context);
27
37
  return found;
28
38
  }
29
39
  }
30
40
 
31
- @bind(
41
+ @injectable(
32
42
  asMiddleware({
33
43
  group: RestMiddlewareGroups.FIND_ROUTE,
34
44
  chain: RestTags.REST_MIDDLEWARE_CHAIN,
35
45
  }),
46
+ {scope: BindingScope.SINGLETON},
36
47
  )
37
48
  export class FindRouteMiddlewareProvider implements Provider<Middleware> {
38
- constructor(
39
- @inject(RestBindings.SequenceActions.FIND_ROUTE)
40
- protected findRoute: FindRoute,
41
- ) {}
42
-
43
49
  value(): Middleware {
44
50
  return async (ctx, next) => {
45
- const route = this.findRoute(ctx.request);
51
+ const request = ctx.request;
52
+ debug('Finding route for %s %s', request.method, request.originalUrl);
53
+ const handler = await ctx.get(RestBindings.HANDLER);
54
+ const route = handler.findRoute(request);
55
+ debug(
56
+ 'Route found for %s %s',
57
+ request.method,
58
+ request.originalUrl,
59
+ route,
60
+ );
61
+ route.updateBindings(ctx);
46
62
  ctx.bind(RestBindings.Operation.ROUTE).to(route);
47
63
  return next();
48
64
  };
@@ -3,13 +3,22 @@
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 {bind, Context, inject, Provider} from '@loopback/core';
6
+ import {
7
+ BindingScope,
8
+ Context,
9
+ inject,
10
+ injectable,
11
+ Provider,
12
+ } from '@loopback/core';
7
13
  import {asMiddleware, Middleware} from '@loopback/express';
14
+ import debugFactory from 'debug';
8
15
  import {RestBindings, RestTags} from '../keys';
9
16
  import {RouteEntry} from '../router';
10
17
  import {RestMiddlewareGroups} from '../sequence';
11
18
  import {InvokeMethod, OperationArgs, OperationRetval} from '../types';
12
19
 
20
+ const debug = debugFactory('loopback:rest:invoke-method');
21
+
13
22
  export class InvokeMethodProvider implements Provider<InvokeMethod> {
14
23
  constructor(@inject(RestBindings.Http.CONTEXT) protected context: Context) {}
15
24
 
@@ -22,28 +31,37 @@ export class InvokeMethodProvider implements Provider<InvokeMethod> {
22
31
  }
23
32
  }
24
33
 
25
- @bind(
34
+ @injectable(
26
35
  asMiddleware({
27
36
  group: RestMiddlewareGroups.INVOKE_METHOD,
28
37
  upstreamGroups: RestMiddlewareGroups.PARSE_PARAMS,
29
38
  chain: RestTags.REST_MIDDLEWARE_CHAIN,
30
39
  }),
40
+ {scope: BindingScope.SINGLETON},
31
41
  )
32
42
  export class InvokeMethodMiddlewareProvider implements Provider<Middleware> {
33
- constructor(
34
- @inject(RestBindings.SequenceActions.INVOKE_METHOD)
35
- protected invokeMethod: InvokeMethod,
36
- ) {}
37
-
38
43
  value(): Middleware {
39
44
  return async (ctx, next) => {
40
45
  const route: RouteEntry = await ctx.get(RestBindings.Operation.ROUTE);
41
46
  const params: OperationArgs = await ctx.get(
42
47
  RestBindings.Operation.PARAMS,
43
48
  );
44
- const retVal = await this.invokeMethod(route, params);
45
- ctx.bind(RestBindings.Operation.RETURN_VALUE).to(retVal);
46
- return retVal;
49
+ if (debug.enabled) {
50
+ debug('Invoking method %s with', route.describe(), params);
51
+ }
52
+ try {
53
+ const retVal = await route.invokeHandler(ctx, params);
54
+ ctx.bind(RestBindings.Operation.RETURN_VALUE).to(retVal);
55
+ if (debug.enabled) {
56
+ debug('Return value from %s', route.describe(), retVal);
57
+ }
58
+ return retVal;
59
+ } catch (err) {
60
+ if (debug.enabled) {
61
+ debug('Error thrown from %s', route.describe(), err);
62
+ }
63
+ throw err;
64
+ }
47
65
  };
48
66
  }
49
67
  }
@@ -3,9 +3,10 @@
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 {Provider} from '@loopback/core';
6
+ import {BindingScope, injectable, Provider} from '@loopback/core';
7
7
  import {LogError, Request} from '../types';
8
8
 
9
+ @injectable({scope: BindingScope.SINGLETON})
9
10
  export class LogErrorProvider implements Provider<LogError> {
10
11
  value(): LogError {
11
12
  return (err, statusCode, req) => this.action(err, statusCode, req);
@@ -3,8 +3,9 @@
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 {bind, inject, Provider} from '@loopback/core';
6
+ import {BindingScope, inject, injectable, Provider} from '@loopback/core';
7
7
  import {asMiddleware, Middleware} from '@loopback/express';
8
+ import debugFactory from 'debug';
8
9
  import {RequestBodyParser} from '../body-parsers';
9
10
  import {RestBindings, RestTags} from '../keys';
10
11
  import {parseOperationArgs} from '../parser';
@@ -12,6 +13,9 @@ import {ResolvedRoute} from '../router';
12
13
  import {RestMiddlewareGroups} from '../sequence';
13
14
  import {AjvFactory, ParseParams, Request, ValidationOptions} from '../types';
14
15
  import {DEFAULT_AJV_VALIDATION_OPTIONS} from '../validation/ajv-factory.provider';
16
+
17
+ const debug = debugFactory('loopback:rest:parse-param');
18
+
15
19
  /**
16
20
  * Provides the function for parsing args in requests at runtime.
17
21
  *
@@ -39,24 +43,40 @@ export class ParseParamsProvider implements Provider<ParseParams> {
39
43
  }
40
44
  }
41
45
 
42
- @bind(
46
+ @injectable(
43
47
  asMiddleware({
44
48
  group: RestMiddlewareGroups.PARSE_PARAMS,
45
49
  upstreamGroups: RestMiddlewareGroups.FIND_ROUTE,
46
50
  chain: RestTags.REST_MIDDLEWARE_CHAIN,
47
51
  }),
52
+ {scope: BindingScope.SINGLETON},
48
53
  )
49
54
  export class ParseParamsMiddlewareProvider implements Provider<Middleware> {
50
- constructor(
51
- @inject(RestBindings.SequenceActions.PARSE_PARAMS)
52
- protected parseParams: ParseParams,
53
- ) {}
54
-
55
55
  value(): Middleware {
56
56
  return async (ctx, next) => {
57
+ const requestBodyParser = await ctx.get(RestBindings.REQUEST_BODY_PARSER);
58
+ const validationOptions: ValidationOptions =
59
+ (await ctx.get(
60
+ RestBindings.REQUEST_BODY_PARSER_OPTIONS.deepProperty('validation'),
61
+ {optional: true},
62
+ )) ?? DEFAULT_AJV_VALIDATION_OPTIONS;
63
+ const ajvFactory = await ctx.get(RestBindings.AJV_FACTORY, {
64
+ optional: true,
65
+ });
66
+
57
67
  const route: ResolvedRoute = await ctx.get(RestBindings.Operation.ROUTE);
58
- const params = await this.parseParams(ctx.request, route);
68
+ debug('Parsing parameters for %s %s', route.verb, route.path);
69
+ const params = await parseOperationArgs(
70
+ ctx.request,
71
+ route,
72
+ requestBodyParser,
73
+ {
74
+ ajvFactory: ajvFactory,
75
+ ...validationOptions,
76
+ },
77
+ );
59
78
  ctx.bind(RestBindings.Operation.PARAMS).to(params);
79
+ debug('Parameters', params);
60
80
  return next();
61
81
  };
62
82
  }
@@ -3,11 +3,11 @@
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 {LogError, Reject, HandlerContext} from '../types';
7
- import {inject, Provider} from '@loopback/core';
6
+ import {BindingScope, inject, injectable, Provider} from '@loopback/core';
8
7
  import {HttpError} from 'http-errors';
8
+ import {ErrorWriterOptions, writeErrorToResponse} from 'strong-error-handler';
9
9
  import {RestBindings} from '../keys';
10
- import {writeErrorToResponse, ErrorWriterOptions} from 'strong-error-handler';
10
+ import {HandlerContext, LogError, Reject} from '../types';
11
11
 
12
12
  // TODO(bajtos) Make this mapping configurable at RestServer level,
13
13
  // allow apps and extensions to contribute additional mappings.
@@ -15,6 +15,7 @@ const codeToStatusCodeMap: {[key: string]: number} = {
15
15
  ENTITY_NOT_FOUND: 404,
16
16
  };
17
17
 
18
+ @injectable({scope: BindingScope.SINGLETON})
18
19
  export class RejectProvider implements Provider<Reject> {
19
20
  constructor(
20
21
  @inject(RestBindings.SequenceActions.LOG_ERROR)