1 | "use strict";
|
2 |
|
3 |
|
4 |
|
5 |
|
6 | Object.defineProperty(exports, "__esModule", { value: true });
|
7 | exports.RequestBodyParser = void 0;
|
8 | const tslib_1 = require("tslib");
|
9 | const core_1 = require("@loopback/core");
|
10 | const openapi_v3_1 = require("@loopback/openapi-v3");
|
11 | const debug_1 = tslib_1.__importDefault(require("debug"));
|
12 | const type_is_1 = require("type-is");
|
13 | const rest_http_error_1 = require("../rest-http-error");
|
14 | const body_parser_helpers_1 = require("./body-parser.helpers");
|
15 | const types_1 = require("./types");
|
16 | const debug = (0, debug_1.default)('loopback:rest:body-parser');
|
17 | let RequestBodyParser = class RequestBodyParser {
|
18 | constructor(parsers, ctx) {
|
19 | this.ctx = ctx;
|
20 | this.parsers = sortParsers(parsers !== null && parsers !== void 0 ? parsers : []);
|
21 | if (debug.enabled) {
|
22 | debug('Body parsers: ', this.parsers.map(p => p.name));
|
23 | }
|
24 | }
|
25 | async loadRequestBodyIfNeeded(operationSpec, request) {
|
26 | const { requestBody, customParser } = await this._matchRequestBodySpec(operationSpec, request);
|
27 | if (!operationSpec.requestBody)
|
28 | return requestBody;
|
29 | const matchedMediaType = requestBody.mediaType;
|
30 | try {
|
31 | if (customParser) {
|
32 |
|
33 | const body = await this._invokeCustomParser(customParser, request);
|
34 | debug('Parsed request body', body);
|
35 | return Object.assign(requestBody, body);
|
36 | }
|
37 | else {
|
38 | const parser = this._findParser(matchedMediaType);
|
39 | if (parser) {
|
40 | const body = await parser.parse(request);
|
41 | debug('Parsed request body', body);
|
42 | return Object.assign(requestBody, body);
|
43 | }
|
44 | }
|
45 | }
|
46 | catch (err) {
|
47 | debug('Request body parsing error', err);
|
48 | throw (0, body_parser_helpers_1.normalizeParsingError)(err);
|
49 | }
|
50 | throw rest_http_error_1.RestHttpErrors.unsupportedMediaType(matchedMediaType);
|
51 | }
|
52 | |
53 |
|
54 |
|
55 | async _matchRequestBodySpec(operationSpec, request) {
|
56 | var _a;
|
57 | const requestBody = {
|
58 | value: undefined,
|
59 | };
|
60 | if (!operationSpec.requestBody)
|
61 | return { requestBody };
|
62 | const contentType = (_a = (0, body_parser_helpers_1.getContentType)(request)) !== null && _a !== void 0 ? _a : 'application/json';
|
63 | debug('Loading request body with content type %j', contentType);
|
64 |
|
65 |
|
66 | if ((0, openapi_v3_1.isReferenceObject)(operationSpec.requestBody)) {
|
67 | throw new Error('$ref requestBody is not supported yet.');
|
68 | }
|
69 | let content = operationSpec.requestBody.content || {};
|
70 | if (!Object.keys(content).length) {
|
71 | content = {
|
72 |
|
73 | 'application/json': { schema: { type: 'object' } },
|
74 | 'application/x-www-form-urlencoded': { schema: { type: 'object' } },
|
75 | };
|
76 | }
|
77 |
|
78 |
|
79 | let matchedMediaType = false;
|
80 | let customParser = undefined;
|
81 | for (const type in content) {
|
82 | matchedMediaType = (0, type_is_1.is)(contentType, type);
|
83 | if (matchedMediaType) {
|
84 | debug('Matched media type: %s -> %s', type, contentType);
|
85 | requestBody.mediaType = contentType;
|
86 | requestBody.schema = content[type].schema;
|
87 | customParser = content[type]['x-parser'];
|
88 | break;
|
89 | }
|
90 | }
|
91 | if (!matchedMediaType) {
|
92 |
|
93 | throw rest_http_error_1.RestHttpErrors.unsupportedMediaType(contentType, Object.keys(content));
|
94 | }
|
95 | return { requestBody, customParser };
|
96 | }
|
97 | |
98 |
|
99 |
|
100 |
|
101 | _findParser(matchedMediaType) {
|
102 | for (const parser of this.parsers) {
|
103 | if (!parser.supports(matchedMediaType)) {
|
104 | debug('Body parser %s does not support %s', parser.name, matchedMediaType);
|
105 | continue;
|
106 | }
|
107 | debug('Body parser %s found for %s', parser.name, matchedMediaType);
|
108 | return parser;
|
109 | }
|
110 | }
|
111 | |
112 |
|
113 |
|
114 |
|
115 |
|
116 | async _invokeCustomParser(customParser, request) {
|
117 | if (typeof customParser === 'string') {
|
118 | const parser = this.parsers.find(p => p.name === customParser ||
|
119 | p.name === body_parser_helpers_1.builtinParsers.mapping[customParser]);
|
120 | if (parser) {
|
121 | debug('Using custom parser %s', customParser);
|
122 | return parser.parse(request);
|
123 | }
|
124 | }
|
125 | else if (typeof customParser === 'function') {
|
126 | if (isBodyParserClass(customParser)) {
|
127 | debug('Using custom parser class %s', customParser.name);
|
128 | const parser = await (0, core_1.instantiateClass)(customParser, this.ctx);
|
129 | return parser.parse(request);
|
130 | }
|
131 | else {
|
132 | debug('Using custom parser function %s', customParser.name);
|
133 | return customParser(request);
|
134 | }
|
135 | }
|
136 | throw new Error('Custom parser not found: ' + customParser);
|
137 | }
|
138 | };
|
139 | exports.RequestBodyParser = RequestBodyParser;
|
140 | exports.RequestBodyParser = RequestBodyParser = tslib_1.__decorate([
|
141 | tslib_1.__param(0, (0, core_1.inject)((0, core_1.filterByTag)(types_1.REQUEST_BODY_PARSER_TAG), { optional: true })),
|
142 | tslib_1.__param(1, core_1.inject.context()),
|
143 | tslib_1.__metadata("design:paramtypes", [Array, core_1.Context])
|
144 | ], RequestBodyParser);
|
145 |
|
146 |
|
147 |
|
148 |
|
149 | function isBodyParserClass(fn) {
|
150 | return fn.toString().startsWith('class ');
|
151 | }
|
152 |
|
153 |
|
154 |
|
155 |
|
156 | function sortParsers(parsers) {
|
157 | return parsers.sort((a, b) => (0, core_1.compareByOrder)(a.name, b.name, body_parser_helpers_1.builtinParsers.names));
|
158 | }
|
159 |
|
\ | No newline at end of file |