@opra/http 1.19.7 → 1.21.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/cjs/express-adapter.js +5 -5
- package/cjs/http-adapter.js +2 -2
- package/cjs/http-context.js +25 -27
- package/cjs/http-handler.js +38 -39
- package/esm/express-adapter.js +5 -5
- package/esm/http-adapter.js +2 -2
- package/esm/http-context.js +25 -27
- package/esm/http-handler.js +38 -39
- package/package.json +7 -7
- package/types/http-adapter.d.ts +1 -1
- package/types/http-context.d.ts +24 -25
package/cjs/express-adapter.js
CHANGED
|
@@ -49,14 +49,14 @@ class ExpressAdapter extends http_adapter_js_1.HttpAdapter {
|
|
|
49
49
|
const request = http_incoming_interface_js_1.HttpIncoming.from(_req);
|
|
50
50
|
const response = http_outgoing_interface_js_1.HttpOutgoing.from(_res);
|
|
51
51
|
const ctx = new http_context_js_1.HttpContext({
|
|
52
|
-
|
|
52
|
+
__adapter: this,
|
|
53
53
|
platform: this.platform,
|
|
54
54
|
request,
|
|
55
55
|
response,
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
56
|
+
__contDef: args?.controller,
|
|
57
|
+
__controller: args?.controllerInstance,
|
|
58
|
+
__oprDef: args?.operation,
|
|
59
|
+
__handler: args?.operationHandler,
|
|
60
60
|
});
|
|
61
61
|
await this.emitAsync('createContext', ctx);
|
|
62
62
|
return ctx;
|
package/cjs/http-adapter.js
CHANGED
|
@@ -10,7 +10,7 @@ const http_handler_js_1 = require("./http-handler.js");
|
|
|
10
10
|
class HttpAdapter extends core_1.PlatformAdapter {
|
|
11
11
|
constructor(options) {
|
|
12
12
|
super(options);
|
|
13
|
-
this.
|
|
13
|
+
this.transform = 'http';
|
|
14
14
|
this.handler = new http_handler_js_1.HttpHandler(this);
|
|
15
15
|
this.interceptors = [...(options?.interceptors || [])];
|
|
16
16
|
this.basePath = options?.basePath || '/';
|
|
@@ -19,7 +19,7 @@ class HttpAdapter extends core_1.PlatformAdapter {
|
|
|
19
19
|
this.scope = options?.scope;
|
|
20
20
|
}
|
|
21
21
|
get api() {
|
|
22
|
-
return this.document.
|
|
22
|
+
return this.document.getHttpApi();
|
|
23
23
|
}
|
|
24
24
|
}
|
|
25
25
|
exports.HttpAdapter = HttpAdapter;
|
package/cjs/http-context.js
CHANGED
|
@@ -5,26 +5,25 @@ const tslib_1 = require("tslib");
|
|
|
5
5
|
const type_is_1 = tslib_1.__importDefault(require("@browsery/type-is"));
|
|
6
6
|
const common_1 = require("@opra/common");
|
|
7
7
|
const core_1 = require("@opra/core");
|
|
8
|
-
const valgen_1 = require("valgen");
|
|
9
8
|
const multipart_reader_js_1 = require("./impl/multipart-reader.js");
|
|
10
9
|
class HttpContext extends core_1.ExecutionContext {
|
|
11
10
|
constructor(init) {
|
|
12
11
|
super({
|
|
13
12
|
...init,
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
__docNode: init.__oprDef?.node ||
|
|
14
|
+
init.__contDef?.node ||
|
|
15
|
+
init.__adapter.document.node,
|
|
16
|
+
transport: 'http',
|
|
17
17
|
});
|
|
18
|
-
this.
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
this.operationHandler = init.operationHandler;
|
|
18
|
+
this.errors = [];
|
|
19
|
+
if (init.__contDef)
|
|
20
|
+
this.__contDef = init.__contDef;
|
|
21
|
+
if (init.__controller)
|
|
22
|
+
this.__controller = init.__controller;
|
|
23
|
+
if (init.__oprDef)
|
|
24
|
+
this.__oprDef = init.__oprDef;
|
|
25
|
+
if (init.__handler)
|
|
26
|
+
this.__handler = init.__handler;
|
|
28
27
|
this.request = init.request;
|
|
29
28
|
this.response = init.response;
|
|
30
29
|
this.mediaType = init.mediaType;
|
|
@@ -69,7 +68,7 @@ class HttpContext extends core_1.ExecutionContext {
|
|
|
69
68
|
async getBody() {
|
|
70
69
|
if (this._body !== undefined)
|
|
71
70
|
return this._body;
|
|
72
|
-
const { request,
|
|
71
|
+
const { request, __oprDef, mediaType } = this;
|
|
73
72
|
if (this.isMultipart) {
|
|
74
73
|
const reader = await this.getMultipartReader();
|
|
75
74
|
/** Retrieve all fields */
|
|
@@ -79,7 +78,7 @@ class HttpContext extends core_1.ExecutionContext {
|
|
|
79
78
|
return this._body;
|
|
80
79
|
}
|
|
81
80
|
this._body = await this.request.readBody({
|
|
82
|
-
limit:
|
|
81
|
+
limit: __oprDef?.requestBody?.maxContentSize,
|
|
83
82
|
});
|
|
84
83
|
if (this._body != null) {
|
|
85
84
|
// Convert Buffer to string if media is text
|
|
@@ -94,18 +93,17 @@ class HttpContext extends core_1.ExecutionContext {
|
|
|
94
93
|
if (mediaType) {
|
|
95
94
|
// Decode/Validate the data object according to data model
|
|
96
95
|
if (this._body && mediaType.type) {
|
|
97
|
-
let decode = this.
|
|
96
|
+
let decode = this.__adapter[core_1.kAssetCache].get(mediaType, 'decode');
|
|
98
97
|
if (!decode) {
|
|
99
|
-
decode =
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
this.adapter[core_1.kAssetCache].set(mediaType, 'decode', decode);
|
|
98
|
+
decode = mediaType.generateCodec('decode', {
|
|
99
|
+
scope: this.__adapter.scope,
|
|
100
|
+
partial: __oprDef?.requestBody?.partial,
|
|
101
|
+
projection: '*',
|
|
102
|
+
ignoreReadonlyFields: true,
|
|
103
|
+
allowPatchOperators: __oprDef?.requestBody?.allowPatchOperators,
|
|
104
|
+
keepKeyFields: __oprDef?.requestBody?.keepKeyFields,
|
|
105
|
+
});
|
|
106
|
+
this.__adapter[core_1.kAssetCache].set(mediaType, 'decode', decode);
|
|
109
107
|
}
|
|
110
108
|
this._body = decode(this._body);
|
|
111
109
|
}
|
package/cjs/http-handler.js
CHANGED
|
@@ -95,11 +95,11 @@ class HttpHandler {
|
|
|
95
95
|
async parseRequest(context) {
|
|
96
96
|
await this._parseParameters(context);
|
|
97
97
|
await this._parseContentType(context);
|
|
98
|
-
if (context.
|
|
98
|
+
if (context.__oprDef?.requestBody?.immediateFetch)
|
|
99
99
|
await context.getBody();
|
|
100
100
|
/** Set default status code as the first status code between 200 and 299 */
|
|
101
|
-
if (context.
|
|
102
|
-
for (const r of context.
|
|
101
|
+
if (context.__oprDef) {
|
|
102
|
+
for (const r of context.__oprDef.responses) {
|
|
103
103
|
const st = r.statusCode.find(sc => sc.start <= 299 && sc.end >= 200);
|
|
104
104
|
if (st) {
|
|
105
105
|
context.response.status(st.start);
|
|
@@ -114,8 +114,8 @@ class HttpHandler {
|
|
|
114
114
|
* @protected
|
|
115
115
|
*/
|
|
116
116
|
async _parseParameters(context) {
|
|
117
|
-
const {
|
|
118
|
-
if (!
|
|
117
|
+
const { __oprDef, request } = context;
|
|
118
|
+
if (!__oprDef)
|
|
119
119
|
return;
|
|
120
120
|
let key = '';
|
|
121
121
|
try {
|
|
@@ -127,24 +127,23 @@ class HttpHandler {
|
|
|
127
127
|
const getDecoder = (prm) => {
|
|
128
128
|
let decode = this[core_1.kAssetCache].get(prm, 'decode');
|
|
129
129
|
if (!decode) {
|
|
130
|
-
decode =
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
}) || valgen_1.vg.isAny();
|
|
130
|
+
decode = prm.generateCodec('decode', {
|
|
131
|
+
scope: this.adapter.scope,
|
|
132
|
+
ignoreReadonlyFields: true,
|
|
133
|
+
});
|
|
135
134
|
this[core_1.kAssetCache].set(prm, 'decode', decode);
|
|
136
135
|
}
|
|
137
136
|
return decode;
|
|
138
137
|
};
|
|
139
138
|
const paramsLeft = new Set([
|
|
140
|
-
...
|
|
141
|
-
...
|
|
139
|
+
...__oprDef.parameters,
|
|
140
|
+
...__oprDef.owner.parameters,
|
|
142
141
|
]);
|
|
143
142
|
/** parse cookie parameters */
|
|
144
143
|
if (request.cookies) {
|
|
145
144
|
for (key of Object.keys(request.cookies)) {
|
|
146
|
-
const oprPrm =
|
|
147
|
-
const cntPrm =
|
|
145
|
+
const oprPrm = __oprDef.findParameter(key, 'cookie');
|
|
146
|
+
const cntPrm = __oprDef.owner.findParameter(key, 'cookie');
|
|
148
147
|
const prm = oprPrm || cntPrm;
|
|
149
148
|
if (!prm)
|
|
150
149
|
continue;
|
|
@@ -166,8 +165,8 @@ class HttpHandler {
|
|
|
166
165
|
/** parse headers */
|
|
167
166
|
if (request.headers) {
|
|
168
167
|
for (key of Object.keys(request.headers)) {
|
|
169
|
-
const oprPrm =
|
|
170
|
-
const cntPrm =
|
|
168
|
+
const oprPrm = __oprDef.findParameter(key, 'header');
|
|
169
|
+
const cntPrm = __oprDef.owner.findParameter(key, 'header');
|
|
171
170
|
const prm = oprPrm || cntPrm;
|
|
172
171
|
if (!prm)
|
|
173
172
|
continue;
|
|
@@ -189,8 +188,8 @@ class HttpHandler {
|
|
|
189
188
|
/** parse path parameters */
|
|
190
189
|
if (request.params) {
|
|
191
190
|
for (key of Object.keys(request.params)) {
|
|
192
|
-
const oprPrm =
|
|
193
|
-
const cntPrm =
|
|
191
|
+
const oprPrm = __oprDef.findParameter(key, 'path');
|
|
192
|
+
const cntPrm = __oprDef.owner.findParameter(key, 'path');
|
|
194
193
|
const prm = oprPrm || cntPrm;
|
|
195
194
|
if (!prm)
|
|
196
195
|
continue;
|
|
@@ -212,8 +211,8 @@ class HttpHandler {
|
|
|
212
211
|
const url = new URL(request.originalUrl || request.url || '/', 'http://tempuri.org');
|
|
213
212
|
const { searchParams } = url;
|
|
214
213
|
for (key of searchParams.keys()) {
|
|
215
|
-
const oprPrm =
|
|
216
|
-
const cntPrm =
|
|
214
|
+
const oprPrm = __oprDef.findParameter(key, 'query');
|
|
215
|
+
const cntPrm = __oprDef.owner.findParameter(key, 'query');
|
|
217
216
|
const prm = oprPrm || cntPrm;
|
|
218
217
|
if (!prm)
|
|
219
218
|
continue;
|
|
@@ -271,19 +270,19 @@ class HttpHandler {
|
|
|
271
270
|
* @protected
|
|
272
271
|
*/
|
|
273
272
|
async _parseContentType(context) {
|
|
274
|
-
const { request,
|
|
275
|
-
if (!
|
|
273
|
+
const { request, __oprDef } = context;
|
|
274
|
+
if (!__oprDef)
|
|
276
275
|
return;
|
|
277
|
-
if (
|
|
276
|
+
if (__oprDef.requestBody?.content.length) {
|
|
278
277
|
let mediaType;
|
|
279
278
|
let contentType = request.header('content-type');
|
|
280
279
|
if (contentType) {
|
|
281
280
|
contentType = (0, content_type_1.parse)(contentType).type;
|
|
282
|
-
mediaType =
|
|
281
|
+
mediaType = __oprDef.requestBody.content.find(mc => mc.contentType &&
|
|
283
282
|
type_is_1.default.is(contentType, Array.isArray(mc.contentType) ? mc.contentType : [mc.contentType]));
|
|
284
283
|
}
|
|
285
284
|
if (!mediaType) {
|
|
286
|
-
const contentTypes =
|
|
285
|
+
const contentTypes = __oprDef.requestBody.content
|
|
287
286
|
.map(mc => mc.contentType)
|
|
288
287
|
.flat();
|
|
289
288
|
throw new common_1.BadRequestError(`Request body should be one of required content types (${contentTypes.join(', ')})`);
|
|
@@ -297,9 +296,9 @@ class HttpHandler {
|
|
|
297
296
|
* @protected
|
|
298
297
|
*/
|
|
299
298
|
async _executeRequest(context) {
|
|
300
|
-
if (!context.
|
|
299
|
+
if (!context.__handler)
|
|
301
300
|
throw new common_1.MethodNotAllowedError();
|
|
302
|
-
const responseValue = await context.
|
|
301
|
+
const responseValue = await context.__handler.call(context.__controller, context);
|
|
303
302
|
const { response } = context;
|
|
304
303
|
if (!response.writableEnded) {
|
|
305
304
|
await this.sendResponse(context, responseValue).finally(() => {
|
|
@@ -426,19 +425,19 @@ class HttpHandler {
|
|
|
426
425
|
}
|
|
427
426
|
}
|
|
428
427
|
async _sendErrorResponse(context) {
|
|
429
|
-
context.errors = this._wrapExceptions(context.errors);
|
|
428
|
+
let errors = (context.errors = this._wrapExceptions(context.errors));
|
|
430
429
|
try {
|
|
431
430
|
if (context.listenerCount('error')) {
|
|
432
|
-
await context.emitAsync('error',
|
|
433
|
-
context.errors = this._wrapExceptions(context.errors);
|
|
431
|
+
await context.emitAsync('error', errors[0], context);
|
|
432
|
+
errors = context.errors = this._wrapExceptions(context.errors);
|
|
434
433
|
}
|
|
435
434
|
if (this.adapter.listenerCount('error')) {
|
|
436
|
-
await this.adapter.emitAsync('error',
|
|
437
|
-
context.errors = this._wrapExceptions(
|
|
435
|
+
await this.adapter.emitAsync('error', errors[0], context);
|
|
436
|
+
errors = context.errors = this._wrapExceptions(errors);
|
|
438
437
|
}
|
|
439
438
|
if (this.adapter.logger?.error) {
|
|
440
439
|
const logger = this.adapter.logger;
|
|
441
|
-
|
|
440
|
+
errors.forEach(e => {
|
|
442
441
|
if (e.status >= 500 && e.status < 600)
|
|
443
442
|
logger.error(e);
|
|
444
443
|
});
|
|
@@ -447,7 +446,7 @@ class HttpHandler {
|
|
|
447
446
|
catch (e) {
|
|
448
447
|
context.errors = this._wrapExceptions([e, ...context.errors]);
|
|
449
448
|
}
|
|
450
|
-
const { response
|
|
449
|
+
const { response } = context;
|
|
451
450
|
if (response.headersSent) {
|
|
452
451
|
response.end();
|
|
453
452
|
return;
|
|
@@ -521,7 +520,7 @@ class HttpHandler {
|
|
|
521
520
|
* @protected
|
|
522
521
|
*/
|
|
523
522
|
_determineResponseArgs(context, body) {
|
|
524
|
-
const { response,
|
|
523
|
+
const { response, __oprDef } = context;
|
|
525
524
|
const hasBody = body != null;
|
|
526
525
|
const statusCode = !hasBody && response.statusCode === common_1.HttpStatusCode.OK
|
|
527
526
|
? common_1.HttpStatusCode.NO_CONTENT
|
|
@@ -543,12 +542,12 @@ class HttpHandler {
|
|
|
543
542
|
let responseArgs = this[core_1.kAssetCache].get(response, cacheKey);
|
|
544
543
|
if (!responseArgs) {
|
|
545
544
|
responseArgs = { statusCode, contentType };
|
|
546
|
-
if (
|
|
545
|
+
if (__oprDef?.responses.length) {
|
|
547
546
|
/** Filter available HttpOperationResponse instances according to status code. */
|
|
548
|
-
const filteredResponses =
|
|
547
|
+
const filteredResponses = __oprDef.responses.filter(r => r.statusCode.find(sc => sc.start <= statusCode && sc.end >= statusCode));
|
|
549
548
|
/** Throw InternalServerError if controller returns non-configured status code */
|
|
550
549
|
if (!filteredResponses.length && statusCode < 400) {
|
|
551
|
-
throw new common_1.InternalServerError(`No responses defined for status code ${statusCode} in operation "${
|
|
550
|
+
throw new common_1.InternalServerError(`No responses defined for status code ${statusCode} in operation "${__oprDef.name}"`);
|
|
552
551
|
}
|
|
553
552
|
/** We search for content-type in filtered HttpOperationResponse array */
|
|
554
553
|
if (filteredResponses.length) {
|
|
@@ -588,7 +587,7 @@ class HttpHandler {
|
|
|
588
587
|
}
|
|
589
588
|
if (!hasBody)
|
|
590
589
|
delete responseArgs.contentType;
|
|
591
|
-
if (
|
|
590
|
+
if (__oprDef?.composition?.startsWith('Entity.')) {
|
|
592
591
|
if (context.queryParams.projection)
|
|
593
592
|
responseArgs.projection = context.queryParams.projection;
|
|
594
593
|
}
|
package/esm/express-adapter.js
CHANGED
|
@@ -45,14 +45,14 @@ export class ExpressAdapter extends HttpAdapter {
|
|
|
45
45
|
const request = HttpIncoming.from(_req);
|
|
46
46
|
const response = HttpOutgoing.from(_res);
|
|
47
47
|
const ctx = new HttpContext({
|
|
48
|
-
|
|
48
|
+
__adapter: this,
|
|
49
49
|
platform: this.platform,
|
|
50
50
|
request,
|
|
51
51
|
response,
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
52
|
+
__contDef: args?.controller,
|
|
53
|
+
__controller: args?.controllerInstance,
|
|
54
|
+
__oprDef: args?.operation,
|
|
55
|
+
__handler: args?.operationHandler,
|
|
56
56
|
});
|
|
57
57
|
await this.emitAsync('createContext', ctx);
|
|
58
58
|
return ctx;
|
package/esm/http-adapter.js
CHANGED
|
@@ -7,7 +7,7 @@ import { HttpHandler } from './http-handler.js';
|
|
|
7
7
|
export class HttpAdapter extends PlatformAdapter {
|
|
8
8
|
constructor(options) {
|
|
9
9
|
super(options);
|
|
10
|
-
this.
|
|
10
|
+
this.transform = 'http';
|
|
11
11
|
this.handler = new HttpHandler(this);
|
|
12
12
|
this.interceptors = [...(options?.interceptors || [])];
|
|
13
13
|
this.basePath = options?.basePath || '/';
|
|
@@ -16,6 +16,6 @@ export class HttpAdapter extends PlatformAdapter {
|
|
|
16
16
|
this.scope = options?.scope;
|
|
17
17
|
}
|
|
18
18
|
get api() {
|
|
19
|
-
return this.document.
|
|
19
|
+
return this.document.getHttpApi();
|
|
20
20
|
}
|
|
21
21
|
}
|
package/esm/http-context.js
CHANGED
|
@@ -1,26 +1,25 @@
|
|
|
1
1
|
import typeIs from '@browsery/type-is';
|
|
2
2
|
import { InternalServerError, NotAcceptableError, } from '@opra/common';
|
|
3
3
|
import { ExecutionContext, kAssetCache } from '@opra/core';
|
|
4
|
-
import { vg } from 'valgen';
|
|
5
4
|
import { MultipartReader } from './impl/multipart-reader.js';
|
|
6
5
|
export class HttpContext extends ExecutionContext {
|
|
7
6
|
constructor(init) {
|
|
8
7
|
super({
|
|
9
8
|
...init,
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
__docNode: init.__oprDef?.node ||
|
|
10
|
+
init.__contDef?.node ||
|
|
11
|
+
init.__adapter.document.node,
|
|
12
|
+
transport: 'http',
|
|
13
13
|
});
|
|
14
|
-
this.
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
this.operationHandler = init.operationHandler;
|
|
14
|
+
this.errors = [];
|
|
15
|
+
if (init.__contDef)
|
|
16
|
+
this.__contDef = init.__contDef;
|
|
17
|
+
if (init.__controller)
|
|
18
|
+
this.__controller = init.__controller;
|
|
19
|
+
if (init.__oprDef)
|
|
20
|
+
this.__oprDef = init.__oprDef;
|
|
21
|
+
if (init.__handler)
|
|
22
|
+
this.__handler = init.__handler;
|
|
24
23
|
this.request = init.request;
|
|
25
24
|
this.response = init.response;
|
|
26
25
|
this.mediaType = init.mediaType;
|
|
@@ -65,7 +64,7 @@ export class HttpContext extends ExecutionContext {
|
|
|
65
64
|
async getBody() {
|
|
66
65
|
if (this._body !== undefined)
|
|
67
66
|
return this._body;
|
|
68
|
-
const { request,
|
|
67
|
+
const { request, __oprDef, mediaType } = this;
|
|
69
68
|
if (this.isMultipart) {
|
|
70
69
|
const reader = await this.getMultipartReader();
|
|
71
70
|
/** Retrieve all fields */
|
|
@@ -75,7 +74,7 @@ export class HttpContext extends ExecutionContext {
|
|
|
75
74
|
return this._body;
|
|
76
75
|
}
|
|
77
76
|
this._body = await this.request.readBody({
|
|
78
|
-
limit:
|
|
77
|
+
limit: __oprDef?.requestBody?.maxContentSize,
|
|
79
78
|
});
|
|
80
79
|
if (this._body != null) {
|
|
81
80
|
// Convert Buffer to string if media is text
|
|
@@ -90,18 +89,17 @@ export class HttpContext extends ExecutionContext {
|
|
|
90
89
|
if (mediaType) {
|
|
91
90
|
// Decode/Validate the data object according to data model
|
|
92
91
|
if (this._body && mediaType.type) {
|
|
93
|
-
let decode = this.
|
|
92
|
+
let decode = this.__adapter[kAssetCache].get(mediaType, 'decode');
|
|
94
93
|
if (!decode) {
|
|
95
|
-
decode =
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
this.adapter[kAssetCache].set(mediaType, 'decode', decode);
|
|
94
|
+
decode = mediaType.generateCodec('decode', {
|
|
95
|
+
scope: this.__adapter.scope,
|
|
96
|
+
partial: __oprDef?.requestBody?.partial,
|
|
97
|
+
projection: '*',
|
|
98
|
+
ignoreReadonlyFields: true,
|
|
99
|
+
allowPatchOperators: __oprDef?.requestBody?.allowPatchOperators,
|
|
100
|
+
keepKeyFields: __oprDef?.requestBody?.keepKeyFields,
|
|
101
|
+
});
|
|
102
|
+
this.__adapter[kAssetCache].set(mediaType, 'decode', decode);
|
|
105
103
|
}
|
|
106
104
|
this._body = decode(this._body);
|
|
107
105
|
}
|
package/esm/http-handler.js
CHANGED
|
@@ -91,11 +91,11 @@ export class HttpHandler {
|
|
|
91
91
|
async parseRequest(context) {
|
|
92
92
|
await this._parseParameters(context);
|
|
93
93
|
await this._parseContentType(context);
|
|
94
|
-
if (context.
|
|
94
|
+
if (context.__oprDef?.requestBody?.immediateFetch)
|
|
95
95
|
await context.getBody();
|
|
96
96
|
/** Set default status code as the first status code between 200 and 299 */
|
|
97
|
-
if (context.
|
|
98
|
-
for (const r of context.
|
|
97
|
+
if (context.__oprDef) {
|
|
98
|
+
for (const r of context.__oprDef.responses) {
|
|
99
99
|
const st = r.statusCode.find(sc => sc.start <= 299 && sc.end >= 200);
|
|
100
100
|
if (st) {
|
|
101
101
|
context.response.status(st.start);
|
|
@@ -110,8 +110,8 @@ export class HttpHandler {
|
|
|
110
110
|
* @protected
|
|
111
111
|
*/
|
|
112
112
|
async _parseParameters(context) {
|
|
113
|
-
const {
|
|
114
|
-
if (!
|
|
113
|
+
const { __oprDef, request } = context;
|
|
114
|
+
if (!__oprDef)
|
|
115
115
|
return;
|
|
116
116
|
let key = '';
|
|
117
117
|
try {
|
|
@@ -123,24 +123,23 @@ export class HttpHandler {
|
|
|
123
123
|
const getDecoder = (prm) => {
|
|
124
124
|
let decode = this[kAssetCache].get(prm, 'decode');
|
|
125
125
|
if (!decode) {
|
|
126
|
-
decode =
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
}) || vg.isAny();
|
|
126
|
+
decode = prm.generateCodec('decode', {
|
|
127
|
+
scope: this.adapter.scope,
|
|
128
|
+
ignoreReadonlyFields: true,
|
|
129
|
+
});
|
|
131
130
|
this[kAssetCache].set(prm, 'decode', decode);
|
|
132
131
|
}
|
|
133
132
|
return decode;
|
|
134
133
|
};
|
|
135
134
|
const paramsLeft = new Set([
|
|
136
|
-
...
|
|
137
|
-
...
|
|
135
|
+
...__oprDef.parameters,
|
|
136
|
+
...__oprDef.owner.parameters,
|
|
138
137
|
]);
|
|
139
138
|
/** parse cookie parameters */
|
|
140
139
|
if (request.cookies) {
|
|
141
140
|
for (key of Object.keys(request.cookies)) {
|
|
142
|
-
const oprPrm =
|
|
143
|
-
const cntPrm =
|
|
141
|
+
const oprPrm = __oprDef.findParameter(key, 'cookie');
|
|
142
|
+
const cntPrm = __oprDef.owner.findParameter(key, 'cookie');
|
|
144
143
|
const prm = oprPrm || cntPrm;
|
|
145
144
|
if (!prm)
|
|
146
145
|
continue;
|
|
@@ -162,8 +161,8 @@ export class HttpHandler {
|
|
|
162
161
|
/** parse headers */
|
|
163
162
|
if (request.headers) {
|
|
164
163
|
for (key of Object.keys(request.headers)) {
|
|
165
|
-
const oprPrm =
|
|
166
|
-
const cntPrm =
|
|
164
|
+
const oprPrm = __oprDef.findParameter(key, 'header');
|
|
165
|
+
const cntPrm = __oprDef.owner.findParameter(key, 'header');
|
|
167
166
|
const prm = oprPrm || cntPrm;
|
|
168
167
|
if (!prm)
|
|
169
168
|
continue;
|
|
@@ -185,8 +184,8 @@ export class HttpHandler {
|
|
|
185
184
|
/** parse path parameters */
|
|
186
185
|
if (request.params) {
|
|
187
186
|
for (key of Object.keys(request.params)) {
|
|
188
|
-
const oprPrm =
|
|
189
|
-
const cntPrm =
|
|
187
|
+
const oprPrm = __oprDef.findParameter(key, 'path');
|
|
188
|
+
const cntPrm = __oprDef.owner.findParameter(key, 'path');
|
|
190
189
|
const prm = oprPrm || cntPrm;
|
|
191
190
|
if (!prm)
|
|
192
191
|
continue;
|
|
@@ -208,8 +207,8 @@ export class HttpHandler {
|
|
|
208
207
|
const url = new URL(request.originalUrl || request.url || '/', 'http://tempuri.org');
|
|
209
208
|
const { searchParams } = url;
|
|
210
209
|
for (key of searchParams.keys()) {
|
|
211
|
-
const oprPrm =
|
|
212
|
-
const cntPrm =
|
|
210
|
+
const oprPrm = __oprDef.findParameter(key, 'query');
|
|
211
|
+
const cntPrm = __oprDef.owner.findParameter(key, 'query');
|
|
213
212
|
const prm = oprPrm || cntPrm;
|
|
214
213
|
if (!prm)
|
|
215
214
|
continue;
|
|
@@ -267,19 +266,19 @@ export class HttpHandler {
|
|
|
267
266
|
* @protected
|
|
268
267
|
*/
|
|
269
268
|
async _parseContentType(context) {
|
|
270
|
-
const { request,
|
|
271
|
-
if (!
|
|
269
|
+
const { request, __oprDef } = context;
|
|
270
|
+
if (!__oprDef)
|
|
272
271
|
return;
|
|
273
|
-
if (
|
|
272
|
+
if (__oprDef.requestBody?.content.length) {
|
|
274
273
|
let mediaType;
|
|
275
274
|
let contentType = request.header('content-type');
|
|
276
275
|
if (contentType) {
|
|
277
276
|
contentType = parseContentType(contentType).type;
|
|
278
|
-
mediaType =
|
|
277
|
+
mediaType = __oprDef.requestBody.content.find(mc => mc.contentType &&
|
|
279
278
|
typeIs.is(contentType, Array.isArray(mc.contentType) ? mc.contentType : [mc.contentType]));
|
|
280
279
|
}
|
|
281
280
|
if (!mediaType) {
|
|
282
|
-
const contentTypes =
|
|
281
|
+
const contentTypes = __oprDef.requestBody.content
|
|
283
282
|
.map(mc => mc.contentType)
|
|
284
283
|
.flat();
|
|
285
284
|
throw new BadRequestError(`Request body should be one of required content types (${contentTypes.join(', ')})`);
|
|
@@ -293,9 +292,9 @@ export class HttpHandler {
|
|
|
293
292
|
* @protected
|
|
294
293
|
*/
|
|
295
294
|
async _executeRequest(context) {
|
|
296
|
-
if (!context.
|
|
295
|
+
if (!context.__handler)
|
|
297
296
|
throw new MethodNotAllowedError();
|
|
298
|
-
const responseValue = await context.
|
|
297
|
+
const responseValue = await context.__handler.call(context.__controller, context);
|
|
299
298
|
const { response } = context;
|
|
300
299
|
if (!response.writableEnded) {
|
|
301
300
|
await this.sendResponse(context, responseValue).finally(() => {
|
|
@@ -422,19 +421,19 @@ export class HttpHandler {
|
|
|
422
421
|
}
|
|
423
422
|
}
|
|
424
423
|
async _sendErrorResponse(context) {
|
|
425
|
-
context.errors = this._wrapExceptions(context.errors);
|
|
424
|
+
let errors = (context.errors = this._wrapExceptions(context.errors));
|
|
426
425
|
try {
|
|
427
426
|
if (context.listenerCount('error')) {
|
|
428
|
-
await context.emitAsync('error',
|
|
429
|
-
context.errors = this._wrapExceptions(context.errors);
|
|
427
|
+
await context.emitAsync('error', errors[0], context);
|
|
428
|
+
errors = context.errors = this._wrapExceptions(context.errors);
|
|
430
429
|
}
|
|
431
430
|
if (this.adapter.listenerCount('error')) {
|
|
432
|
-
await this.adapter.emitAsync('error',
|
|
433
|
-
context.errors = this._wrapExceptions(
|
|
431
|
+
await this.adapter.emitAsync('error', errors[0], context);
|
|
432
|
+
errors = context.errors = this._wrapExceptions(errors);
|
|
434
433
|
}
|
|
435
434
|
if (this.adapter.logger?.error) {
|
|
436
435
|
const logger = this.adapter.logger;
|
|
437
|
-
|
|
436
|
+
errors.forEach(e => {
|
|
438
437
|
if (e.status >= 500 && e.status < 600)
|
|
439
438
|
logger.error(e);
|
|
440
439
|
});
|
|
@@ -443,7 +442,7 @@ export class HttpHandler {
|
|
|
443
442
|
catch (e) {
|
|
444
443
|
context.errors = this._wrapExceptions([e, ...context.errors]);
|
|
445
444
|
}
|
|
446
|
-
const { response
|
|
445
|
+
const { response } = context;
|
|
447
446
|
if (response.headersSent) {
|
|
448
447
|
response.end();
|
|
449
448
|
return;
|
|
@@ -517,7 +516,7 @@ export class HttpHandler {
|
|
|
517
516
|
* @protected
|
|
518
517
|
*/
|
|
519
518
|
_determineResponseArgs(context, body) {
|
|
520
|
-
const { response,
|
|
519
|
+
const { response, __oprDef } = context;
|
|
521
520
|
const hasBody = body != null;
|
|
522
521
|
const statusCode = !hasBody && response.statusCode === HttpStatusCode.OK
|
|
523
522
|
? HttpStatusCode.NO_CONTENT
|
|
@@ -539,12 +538,12 @@ export class HttpHandler {
|
|
|
539
538
|
let responseArgs = this[kAssetCache].get(response, cacheKey);
|
|
540
539
|
if (!responseArgs) {
|
|
541
540
|
responseArgs = { statusCode, contentType };
|
|
542
|
-
if (
|
|
541
|
+
if (__oprDef?.responses.length) {
|
|
543
542
|
/** Filter available HttpOperationResponse instances according to status code. */
|
|
544
|
-
const filteredResponses =
|
|
543
|
+
const filteredResponses = __oprDef.responses.filter(r => r.statusCode.find(sc => sc.start <= statusCode && sc.end >= statusCode));
|
|
545
544
|
/** Throw InternalServerError if controller returns non-configured status code */
|
|
546
545
|
if (!filteredResponses.length && statusCode < 400) {
|
|
547
|
-
throw new InternalServerError(`No responses defined for status code ${statusCode} in operation "${
|
|
546
|
+
throw new InternalServerError(`No responses defined for status code ${statusCode} in operation "${__oprDef.name}"`);
|
|
548
547
|
}
|
|
549
548
|
/** We search for content-type in filtered HttpOperationResponse array */
|
|
550
549
|
if (filteredResponses.length) {
|
|
@@ -584,7 +583,7 @@ export class HttpHandler {
|
|
|
584
583
|
}
|
|
585
584
|
if (!hasBody)
|
|
586
585
|
delete responseArgs.contentType;
|
|
587
|
-
if (
|
|
586
|
+
if (__oprDef?.composition?.startsWith('Entity.')) {
|
|
588
587
|
if (context.queryParams.projection)
|
|
589
588
|
responseArgs.projection = context.queryParams.projection;
|
|
590
589
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opra/http",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.21.0",
|
|
4
4
|
"description": "Opra Http Server Adapter",
|
|
5
5
|
"author": "Panates",
|
|
6
6
|
"license": "MIT",
|
|
@@ -13,21 +13,21 @@
|
|
|
13
13
|
"base64-stream": "^1.0.0",
|
|
14
14
|
"busboy": "^1.6.0",
|
|
15
15
|
"bytes": "^3.1.2",
|
|
16
|
-
"content-disposition": "^0.
|
|
16
|
+
"content-disposition": "^1.0.1",
|
|
17
17
|
"content-type": "^1.0.5",
|
|
18
|
-
"cookie": "^1.
|
|
18
|
+
"cookie": "^1.1.1",
|
|
19
19
|
"cookie-signature": "^1.2.2",
|
|
20
20
|
"encodeurl": "^2.0.0",
|
|
21
21
|
"expect": "^30.2.0",
|
|
22
22
|
"fast-tokenizer": "^1.7.0",
|
|
23
23
|
"fresh": "^0.5.2",
|
|
24
24
|
"iconv-lite": "^0.7.0",
|
|
25
|
-
"mime-types": "^3.0.
|
|
25
|
+
"mime-types": "^3.0.2",
|
|
26
26
|
"node-events-async": "^1.2.0",
|
|
27
27
|
"power-tasks": "^1.11.1",
|
|
28
28
|
"putil-varhelpers": "^1.6.5",
|
|
29
29
|
"range-parser": "^1.2.1",
|
|
30
|
-
"raw-body": "^3.0.
|
|
30
|
+
"raw-body": "^3.0.2",
|
|
31
31
|
"reflect-metadata": "^0.2.2",
|
|
32
32
|
"super-fast-md5": "^1.0.3",
|
|
33
33
|
"tslib": "^2.8.1",
|
|
@@ -35,8 +35,8 @@
|
|
|
35
35
|
"vary": "^1.1.2"
|
|
36
36
|
},
|
|
37
37
|
"peerDependencies": {
|
|
38
|
-
"@opra/common": "^1.
|
|
39
|
-
"@opra/core": "^1.
|
|
38
|
+
"@opra/common": "^1.21.0",
|
|
39
|
+
"@opra/core": "^1.21.0"
|
|
40
40
|
},
|
|
41
41
|
"optionalDependencies": {
|
|
42
42
|
"express": "^4.0.0 || ^5.0.0",
|
package/types/http-adapter.d.ts
CHANGED
|
@@ -9,7 +9,7 @@ import { HttpHandler } from './http-handler.js';
|
|
|
9
9
|
*/
|
|
10
10
|
export declare abstract class HttpAdapter<T extends HttpAdapter.Events = HttpAdapter.Events> extends PlatformAdapter<EventMap<T>> {
|
|
11
11
|
readonly handler: HttpHandler;
|
|
12
|
-
readonly
|
|
12
|
+
readonly transform: OpraSchema.Transport;
|
|
13
13
|
readonly basePath: string;
|
|
14
14
|
scope?: string;
|
|
15
15
|
interceptors: (HttpAdapter.InterceptorFunction | HttpAdapter.IHttpInterceptor)[];
|
package/types/http-context.d.ts
CHANGED
|
@@ -1,35 +1,17 @@
|
|
|
1
|
-
import { HttpController, HttpMediaType, HttpOperation
|
|
1
|
+
import { HttpController, HttpMediaType, HttpOperation } from '@opra/common';
|
|
2
2
|
import { ExecutionContext } from '@opra/core';
|
|
3
3
|
import type { HttpAdapter } from './http-adapter';
|
|
4
4
|
import { MultipartReader } from './impl/multipart-reader.js';
|
|
5
5
|
import type { HttpIncoming } from './interfaces/http-incoming.interface.js';
|
|
6
6
|
import type { HttpOutgoing } from './interfaces/http-outgoing.interface.js';
|
|
7
|
-
export declare namespace HttpContext {
|
|
8
|
-
interface Initiator extends Omit<ExecutionContext.Initiator, 'document' | 'protocol' | 'documentNode'> {
|
|
9
|
-
adapter: HttpAdapter;
|
|
10
|
-
request: HttpIncoming;
|
|
11
|
-
response: HttpOutgoing;
|
|
12
|
-
controller?: HttpController;
|
|
13
|
-
controllerInstance?: any;
|
|
14
|
-
operation?: HttpOperation;
|
|
15
|
-
operationHandler?: Function;
|
|
16
|
-
cookies?: Record<string, any>;
|
|
17
|
-
headers?: Record<string, any>;
|
|
18
|
-
pathParams?: Record<string, any>;
|
|
19
|
-
queryParams?: Record<string, any>;
|
|
20
|
-
mediaType?: HttpMediaType;
|
|
21
|
-
body?: any;
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
7
|
export declare class HttpContext extends ExecutionContext {
|
|
25
8
|
protected _body?: any;
|
|
26
9
|
protected _multipartReader?: MultipartReader;
|
|
27
|
-
readonly
|
|
28
|
-
readonly
|
|
29
|
-
readonly
|
|
30
|
-
readonly
|
|
31
|
-
readonly
|
|
32
|
-
readonly operationHandler?: Function;
|
|
10
|
+
readonly __contDef: HttpController;
|
|
11
|
+
readonly __oprDef: HttpOperation;
|
|
12
|
+
readonly __controller: any;
|
|
13
|
+
readonly __handler?: Function;
|
|
14
|
+
readonly __adapter: HttpAdapter;
|
|
33
15
|
readonly request: HttpIncoming;
|
|
34
16
|
readonly response: HttpOutgoing;
|
|
35
17
|
readonly mediaType?: HttpMediaType;
|
|
@@ -37,9 +19,26 @@ export declare class HttpContext extends ExecutionContext {
|
|
|
37
19
|
readonly headers: Record<string, any>;
|
|
38
20
|
readonly pathParams: Record<string, any>;
|
|
39
21
|
readonly queryParams: Record<string, any>;
|
|
40
|
-
errors:
|
|
22
|
+
errors: Error[];
|
|
41
23
|
constructor(init: HttpContext.Initiator);
|
|
42
24
|
get isMultipart(): boolean;
|
|
43
25
|
getMultipartReader(): Promise<MultipartReader>;
|
|
44
26
|
getBody<T>(): Promise<T>;
|
|
45
27
|
}
|
|
28
|
+
export declare namespace HttpContext {
|
|
29
|
+
interface Initiator extends Omit<ExecutionContext.Initiator, '__adapter' | 'transport'> {
|
|
30
|
+
__adapter: HttpAdapter;
|
|
31
|
+
__contDef?: HttpController;
|
|
32
|
+
__oprDef?: HttpOperation;
|
|
33
|
+
__controller?: any;
|
|
34
|
+
__handler?: Function;
|
|
35
|
+
request: HttpIncoming;
|
|
36
|
+
response: HttpOutgoing;
|
|
37
|
+
cookies?: Record<string, any>;
|
|
38
|
+
headers?: Record<string, any>;
|
|
39
|
+
pathParams?: Record<string, any>;
|
|
40
|
+
queryParams?: Record<string, any>;
|
|
41
|
+
mediaType?: HttpMediaType;
|
|
42
|
+
body?: any;
|
|
43
|
+
}
|
|
44
|
+
}
|