1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 | import {
|
7 | isReferenceObject,
|
8 | OperationObject,
|
9 | ParameterObject,
|
10 | REQUEST_BODY_INDEX,
|
11 | SchemasObject,
|
12 | } from '@loopback/openapi-v3';
|
13 | import debugFactory from 'debug';
|
14 | import {RequestBody, RequestBodyParser} from './body-parsers';
|
15 | import {coerceParameter} from './coercion/coerce-parameter';
|
16 | import {RestHttpErrors} from './rest-http-error';
|
17 | import {ResolvedRoute} from './router';
|
18 | import {
|
19 | OperationArgs,
|
20 | PathParameterValues,
|
21 | Request,
|
22 | ValidationOptions,
|
23 | } from './types';
|
24 | import {DEFAULT_AJV_VALIDATION_OPTIONS} from './validation/ajv-factory.provider';
|
25 | import {validateRequestBody} from './validation/request-body.validator';
|
26 | const debug = debugFactory('loopback:rest:parser');
|
27 |
|
28 |
|
29 |
|
30 |
|
31 |
|
32 |
|
33 |
|
34 |
|
35 | export async function parseOperationArgs(
|
36 | request: Request,
|
37 | route: ResolvedRoute,
|
38 | requestBodyParser: RequestBodyParser = new RequestBodyParser(),
|
39 | options: ValidationOptions = DEFAULT_AJV_VALIDATION_OPTIONS,
|
40 | ): Promise<OperationArgs> {
|
41 | debug('Parsing operation arguments for route %s', route.describe());
|
42 | const operationSpec = route.spec;
|
43 | const pathParams = route.pathParams;
|
44 | const body = await requestBodyParser.loadRequestBodyIfNeeded(
|
45 | operationSpec,
|
46 | request,
|
47 | );
|
48 | return buildOperationArguments(
|
49 | operationSpec,
|
50 | request,
|
51 | pathParams,
|
52 | body,
|
53 | route.schemas,
|
54 | options,
|
55 | );
|
56 | }
|
57 |
|
58 | async function buildOperationArguments(
|
59 | operationSpec: OperationObject,
|
60 | request: Request,
|
61 | pathParams: PathParameterValues,
|
62 | body: RequestBody,
|
63 | globalSchemas: SchemasObject,
|
64 | options: ValidationOptions = DEFAULT_AJV_VALIDATION_OPTIONS,
|
65 | ): Promise<OperationArgs> {
|
66 | let requestBodyIndex = -1;
|
67 | if (operationSpec.requestBody) {
|
68 |
|
69 |
|
70 | if (isReferenceObject(operationSpec.requestBody)) {
|
71 | throw new Error('$ref requestBody is not supported yet.');
|
72 | }
|
73 | const i = operationSpec.requestBody[REQUEST_BODY_INDEX];
|
74 | requestBodyIndex = i ?? 0;
|
75 | }
|
76 |
|
77 | const paramArgs: OperationArgs = [];
|
78 |
|
79 | for (const paramSpec of operationSpec.parameters ?? []) {
|
80 | if (isReferenceObject(paramSpec)) {
|
81 |
|
82 |
|
83 | throw new Error('$ref parameters are not supported yet.');
|
84 | }
|
85 | const spec = paramSpec as ParameterObject;
|
86 | const rawValue = getParamFromRequest(spec, request, pathParams);
|
87 | const coercedValue = await coerceParameter(rawValue, spec, options);
|
88 | paramArgs.push(coercedValue);
|
89 | }
|
90 |
|
91 | debug('Validating request body - value %j', body);
|
92 | await validateRequestBody(
|
93 | body,
|
94 | operationSpec.requestBody,
|
95 | globalSchemas,
|
96 | options,
|
97 | );
|
98 |
|
99 | if (requestBodyIndex >= 0) {
|
100 | paramArgs.splice(requestBodyIndex, 0, body.value);
|
101 | }
|
102 | return paramArgs;
|
103 | }
|
104 |
|
105 | function getParamFromRequest(
|
106 | spec: ParameterObject,
|
107 | request: Request,
|
108 | pathParams: PathParameterValues,
|
109 | ) {
|
110 | switch (spec.in) {
|
111 | case 'query':
|
112 | return request.query[spec.name];
|
113 | case 'path':
|
114 | return pathParams[spec.name];
|
115 | case 'header':
|
116 |
|
117 | return request.headers[spec.name.toLowerCase()];
|
118 |
|
119 |
|
120 | default:
|
121 | throw RestHttpErrors.invalidParamLocation(spec.in);
|
122 | }
|
123 | }
|