@opra/core 1.0.0-alpha.2 → 1.0.0-alpha.20
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/augmentation/18n.augmentation.js +1 -1
- package/cjs/execution-context.js +0 -1
- package/cjs/http/express-adapter.js +6 -17
- package/cjs/http/http-context.js +6 -3
- package/cjs/http/impl/http-handler.js +73 -62
- package/cjs/http/impl/http-incoming.host.js +3 -3
- package/cjs/http/impl/http-outgoing.host.js +2 -2
- package/cjs/http/impl/multipart-reader.js +4 -10
- package/cjs/http/impl/node-incoming-message.host.js +5 -3
- package/cjs/http/interfaces/node-incoming-message.interface.js +3 -2
- package/cjs/http/utils/body-reader.js +6 -5
- package/cjs/http/utils/common.js +6 -5
- package/cjs/http/utils/concat-readable.js +1 -2
- package/cjs/http/utils/convert-to-headers.js +2 -3
- package/cjs/http/utils/convert-to-raw-headers.js +1 -2
- package/cjs/http/utils/match-known-fields.js +2 -2
- package/cjs/http/utils/wrap-exception.js +1 -2
- package/cjs/index.js +3 -3
- package/cjs/platform-adapter.js +1 -1
- package/cjs/type-guards.js +4 -5
- package/esm/augmentation/18n.augmentation.js +1 -1
- package/esm/execution-context.js +0 -1
- package/esm/http/express-adapter.js +6 -17
- package/esm/http/http-context.js +6 -3
- package/esm/http/impl/http-handler.js +73 -62
- package/esm/http/impl/http-incoming.host.js +3 -3
- package/esm/http/impl/http-outgoing.host.js +2 -2
- package/esm/http/impl/multipart-reader.js +4 -10
- package/esm/http/impl/node-incoming-message.host.js +5 -3
- package/esm/http/interfaces/node-incoming-message.interface.js +3 -2
- package/esm/http/utils/body-reader.js +6 -5
- package/esm/http/utils/common.js +2 -1
- package/esm/index.js +3 -3
- package/esm/platform-adapter.js +1 -1
- package/i18n/i18n/en/error.json +21 -0
- package/package.json +13 -7
- package/types/augmentation/18n.augmentation.d.ts +1 -1
- package/types/execution-context.d.ts +1 -3
- package/types/http/express-adapter.d.ts +1 -1
- package/types/http/http-context.d.ts +2 -2
- package/types/http/impl/http-incoming.host.d.ts +1 -2
- package/types/http/impl/http-outgoing.host.d.ts +1 -1
- package/types/http/impl/multipart-reader.d.ts +0 -1
- package/types/http/impl/node-incoming-message.host.d.ts +2 -6
- package/types/http/impl/node-outgoing-message.host.d.ts +2 -5
- package/types/http/interfaces/http-incoming.interface.d.ts +1 -2
- package/types/http/interfaces/http-outgoing.interface.d.ts +1 -1
- package/types/http/interfaces/node-incoming-message.interface.d.ts +0 -2
- package/types/http/interfaces/node-outgoing-message.interface.d.ts +0 -2
- package/types/http/utils/body-reader.d.ts +1 -4
- package/types/http/utils/concat-readable.d.ts +0 -1
- package/types/http/utils/convert-to-raw-headers.d.ts +0 -1
- package/types/index.d.ts +3 -3
- package/types/platform-adapter.d.ts +2 -2
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const tslib_1 = require("tslib");
|
|
4
|
+
const common_1 = require("@opra/common");
|
|
4
5
|
const fs_1 = tslib_1.__importDefault(require("fs"));
|
|
5
6
|
const path_1 = tslib_1.__importDefault(require("path"));
|
|
6
|
-
const common_1 = require("@opra/common");
|
|
7
7
|
common_1.I18n.load = async function (options) {
|
|
8
8
|
const opts = {
|
|
9
9
|
...options,
|
package/cjs/execution-context.js
CHANGED
|
@@ -11,7 +11,6 @@ class ExecutionContext extends strict_typed_events_1.AsyncEventEmitter {
|
|
|
11
11
|
this.document = init.document;
|
|
12
12
|
this.protocol = init.protocol;
|
|
13
13
|
this.platform = init.platform;
|
|
14
|
-
this.platformArgs = init.platformArgs;
|
|
15
14
|
}
|
|
16
15
|
addListener(event, listener) {
|
|
17
16
|
return super.addListener(event, listener);
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.ExpressAdapter = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
|
+
const common_1 = require("@opra/common");
|
|
5
6
|
const express_1 = require("express");
|
|
6
7
|
const nodePath = tslib_1.__importStar(require("path"));
|
|
7
|
-
const common_1 = require("@opra/common");
|
|
8
8
|
const constants_js_1 = require("../constants.js");
|
|
9
9
|
const http_adapter_js_1 = require("./http-adapter.js");
|
|
10
10
|
const http_context_js_1 = require("./http-context.js");
|
|
@@ -35,13 +35,14 @@ class ExpressAdapter extends http_adapter_js_1.HttpAdapter {
|
|
|
35
35
|
}
|
|
36
36
|
if (resource.onShutdown) {
|
|
37
37
|
const instance = this._controllerInstances.get(resource) || resource.instance;
|
|
38
|
-
if (instance)
|
|
38
|
+
if (instance) {
|
|
39
39
|
try {
|
|
40
40
|
await resource.onShutdown.call(instance, resource);
|
|
41
41
|
}
|
|
42
42
|
catch (e) {
|
|
43
43
|
this.logger.error(e);
|
|
44
44
|
}
|
|
45
|
+
}
|
|
45
46
|
}
|
|
46
47
|
};
|
|
47
48
|
for (const c of this.api.controllers.values())
|
|
@@ -65,14 +66,9 @@ class ExpressAdapter extends http_adapter_js_1.HttpAdapter {
|
|
|
65
66
|
const createContext = (_req, _res, args) => {
|
|
66
67
|
const request = http_incoming_interface_js_1.HttpIncoming.from(_req);
|
|
67
68
|
const response = http_outgoing_interface_js_1.HttpOutgoing.from(_res);
|
|
68
|
-
const platformArgs = {
|
|
69
|
-
request: _req,
|
|
70
|
-
response: _res,
|
|
71
|
-
};
|
|
72
69
|
return new http_context_js_1.HttpContext({
|
|
73
70
|
adapter: this,
|
|
74
71
|
platform: this.platform,
|
|
75
|
-
platformArgs,
|
|
76
72
|
request,
|
|
77
73
|
response,
|
|
78
74
|
controller: args?.controller,
|
|
@@ -82,16 +78,9 @@ class ExpressAdapter extends http_adapter_js_1.HttpAdapter {
|
|
|
82
78
|
});
|
|
83
79
|
};
|
|
84
80
|
/** Add an endpoint that returns document schema */
|
|
85
|
-
router.get('
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
if (url === '/$schema') {
|
|
89
|
-
const context = createContext(_req, _res);
|
|
90
|
-
this[constants_js_1.kHandler].sendDocumentSchema(context).catch(next);
|
|
91
|
-
return;
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
next();
|
|
81
|
+
router.get('/\\$schema', (_req, _res, next) => {
|
|
82
|
+
const context = createContext(_req, _res);
|
|
83
|
+
this[constants_js_1.kHandler].sendDocumentSchema(context).catch(next);
|
|
95
84
|
});
|
|
96
85
|
/** Add operation endpoints */
|
|
97
86
|
if (this.api.controllers.size) {
|
package/cjs/http/http-context.js
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.HttpContext = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
|
-
const valgen_1 = require("valgen");
|
|
6
5
|
const type_is_1 = tslib_1.__importDefault(require("@browsery/type-is"));
|
|
7
6
|
const common_1 = require("@opra/common");
|
|
7
|
+
const valgen_1 = require("valgen");
|
|
8
8
|
const constants_js_1 = require("../constants.js");
|
|
9
9
|
const execution_context_js_1 = require("../execution-context.js");
|
|
10
10
|
const multipart_reader_js_1 = require("./impl/multipart-reader.js");
|
|
@@ -78,10 +78,11 @@ class HttpContext extends execution_context_js_1.ExecutionContext {
|
|
|
78
78
|
}
|
|
79
79
|
/** Check required fields */
|
|
80
80
|
for (const field of multipartFields) {
|
|
81
|
-
if (field.required && !fieldsFound.get(field))
|
|
81
|
+
if (field.required && !fieldsFound.get(field)) {
|
|
82
82
|
throw new common_1.BadRequestError({
|
|
83
83
|
message: `Multipart field (${field.fieldName}) is required`,
|
|
84
84
|
});
|
|
85
|
+
}
|
|
85
86
|
}
|
|
86
87
|
}
|
|
87
88
|
return this._body;
|
|
@@ -89,8 +90,9 @@ class HttpContext extends execution_context_js_1.ExecutionContext {
|
|
|
89
90
|
this._body = await this.request.readBody({ limit: operation.requestBody?.maxContentSize });
|
|
90
91
|
if (this._body != null) {
|
|
91
92
|
// Convert Buffer to string if media is text
|
|
92
|
-
if (Buffer.isBuffer(this._body) && request.is(['json', 'xml', 'txt', 'text']))
|
|
93
|
+
if (Buffer.isBuffer(this._body) && request.is(['json', 'xml', 'txt', 'text'])) {
|
|
93
94
|
this._body = this._body.toString('utf-8');
|
|
95
|
+
}
|
|
94
96
|
// Transform text to Object if media is JSON
|
|
95
97
|
if (typeof this._body === 'string' && request.is(['json']))
|
|
96
98
|
this._body = JSON.parse(this._body);
|
|
@@ -104,6 +106,7 @@ class HttpContext extends execution_context_js_1.ExecutionContext {
|
|
|
104
106
|
mediaType.type?.generateCodec('decode', {
|
|
105
107
|
partial: operation.requestBody?.partial,
|
|
106
108
|
projection: '*',
|
|
109
|
+
ignoreReadonlyFields: true,
|
|
107
110
|
}) || valgen_1.vg.isAny();
|
|
108
111
|
this.adapter[constants_js_1.kAssetCache].set(mediaType, 'decode', decode);
|
|
109
112
|
}
|
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.HttpHandler = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
|
+
const process = tslib_1.__importStar(require("node:process"));
|
|
6
|
+
const type_is_1 = tslib_1.__importDefault(require("@browsery/type-is"));
|
|
7
|
+
const common_1 = require("@opra/common");
|
|
5
8
|
const content_type_1 = require("content-type");
|
|
6
9
|
const fast_tokenizer_1 = require("fast-tokenizer");
|
|
7
|
-
const process = tslib_1.__importStar(require("node:process"));
|
|
8
10
|
const ts_gems_1 = require("ts-gems");
|
|
9
11
|
const valgen_1 = require("valgen");
|
|
10
|
-
const type_is_1 = tslib_1.__importDefault(require("@browsery/type-is"));
|
|
11
|
-
const common_1 = require("@opra/common");
|
|
12
12
|
const constants_js_1 = require("../../constants.js");
|
|
13
13
|
const wrap_exception_js_1 = require("../utils/wrap-exception.js");
|
|
14
14
|
/**
|
|
@@ -64,7 +64,8 @@ class HttpHandler {
|
|
|
64
64
|
else
|
|
65
65
|
await this._executeRequest(context);
|
|
66
66
|
}
|
|
67
|
-
catch (
|
|
67
|
+
catch (error) {
|
|
68
|
+
let e = error;
|
|
68
69
|
if (e instanceof valgen_1.ValidationError) {
|
|
69
70
|
e = new common_1.InternalServerError({
|
|
70
71
|
message: (0, common_1.translate)('error:RESPONSE_VALIDATION,', 'Response validation failed'),
|
|
@@ -76,11 +77,10 @@ class HttpHandler {
|
|
|
76
77
|
e = (0, wrap_exception_js_1.wrapException)(e);
|
|
77
78
|
response.status(e.statusCode || e.status || common_1.HttpStatusCode.INTERNAL_SERVER_ERROR);
|
|
78
79
|
response.contentType(common_1.MimeTypes.opra_response_json);
|
|
79
|
-
await this._sendResponse(context, new common_1.OperationResult({ errors: [e] })).finally(() => {
|
|
80
|
+
await this._sendResponse(context, new common_1.OperationResult({ errors: [e.toJSON()] })).finally(() => {
|
|
80
81
|
if (!response.finished)
|
|
81
82
|
response.end();
|
|
82
83
|
});
|
|
83
|
-
// if (!outgoing.writableEnded) await this._sendErrorResponse(context.response, [error]);
|
|
84
84
|
}
|
|
85
85
|
finally {
|
|
86
86
|
await context.emitAsync('finish');
|
|
@@ -113,17 +113,17 @@ class HttpHandler {
|
|
|
113
113
|
*/
|
|
114
114
|
async _parseParameters(context) {
|
|
115
115
|
const { operation, request } = context;
|
|
116
|
-
let
|
|
116
|
+
let key = '';
|
|
117
117
|
try {
|
|
118
118
|
const onFail = (issue) => {
|
|
119
|
-
issue.location =
|
|
119
|
+
issue.location = key;
|
|
120
120
|
return issue;
|
|
121
121
|
};
|
|
122
122
|
/** prepare decoders */
|
|
123
123
|
const getDecoder = (prm) => {
|
|
124
124
|
let decode = this[constants_js_1.kAssetCache].get(prm, 'decode');
|
|
125
125
|
if (!decode) {
|
|
126
|
-
decode = prm.type?.generateCodec('decode') || valgen_1.vg.isAny();
|
|
126
|
+
decode = prm.type?.generateCodec('decode', { ignoreReadonlyFields: true }) || valgen_1.vg.isAny();
|
|
127
127
|
this[constants_js_1.kAssetCache].set(prm, 'decode', decode);
|
|
128
128
|
}
|
|
129
129
|
return decode;
|
|
@@ -131,9 +131,9 @@ class HttpHandler {
|
|
|
131
131
|
const paramsLeft = new Set([...operation.parameters, ...operation.owner.parameters]);
|
|
132
132
|
/** parse cookie parameters */
|
|
133
133
|
if (request.cookies) {
|
|
134
|
-
for (
|
|
135
|
-
const oprPrm = operation.findParameter(
|
|
136
|
-
const cntPrm = operation.owner.findParameter(
|
|
134
|
+
for (key of Object.keys(request.cookies)) {
|
|
135
|
+
const oprPrm = operation.findParameter(key, 'cookie');
|
|
136
|
+
const cntPrm = operation.owner.findParameter(key, 'cookie');
|
|
137
137
|
const prm = oprPrm || cntPrm;
|
|
138
138
|
if (!prm)
|
|
139
139
|
continue;
|
|
@@ -142,16 +142,17 @@ class HttpHandler {
|
|
|
142
142
|
if (cntPrm)
|
|
143
143
|
paramsLeft.delete(cntPrm);
|
|
144
144
|
const decode = getDecoder(prm);
|
|
145
|
-
const v = decode(request.cookies[
|
|
145
|
+
const v = decode(request.cookies[key], { coerce: true, label: key, onFail });
|
|
146
|
+
const prmName = typeof prm.name === 'string' ? prm.name : key;
|
|
146
147
|
if (v !== undefined)
|
|
147
148
|
context.cookies[prmName] = v;
|
|
148
149
|
}
|
|
149
150
|
}
|
|
150
151
|
/** parse headers */
|
|
151
152
|
if (request.headers) {
|
|
152
|
-
for (
|
|
153
|
-
const oprPrm = operation.findParameter(
|
|
154
|
-
const cntPrm = operation.owner.findParameter(
|
|
153
|
+
for (key of Object.keys(request.headers)) {
|
|
154
|
+
const oprPrm = operation.findParameter(key, 'header');
|
|
155
|
+
const cntPrm = operation.owner.findParameter(key, 'header');
|
|
155
156
|
const prm = oprPrm || cntPrm;
|
|
156
157
|
if (!prm)
|
|
157
158
|
continue;
|
|
@@ -160,16 +161,17 @@ class HttpHandler {
|
|
|
160
161
|
if (cntPrm)
|
|
161
162
|
paramsLeft.delete(cntPrm);
|
|
162
163
|
const decode = getDecoder(prm);
|
|
163
|
-
const v = decode(request.headers[
|
|
164
|
+
const v = decode(request.headers[key], { coerce: true, label: key, onFail });
|
|
165
|
+
const prmName = typeof prm.name === 'string' ? prm.name : key;
|
|
164
166
|
if (v !== undefined)
|
|
165
167
|
context.headers[prmName] = v;
|
|
166
168
|
}
|
|
167
169
|
}
|
|
168
170
|
/** parse path parameters */
|
|
169
171
|
if (request.params) {
|
|
170
|
-
for (
|
|
171
|
-
const oprPrm = operation.findParameter(
|
|
172
|
-
const cntPrm = operation.owner.findParameter(
|
|
172
|
+
for (key of Object.keys(request.params)) {
|
|
173
|
+
const oprPrm = operation.findParameter(key, 'path');
|
|
174
|
+
const cntPrm = operation.owner.findParameter(key, 'path');
|
|
173
175
|
const prm = oprPrm || cntPrm;
|
|
174
176
|
if (!prm)
|
|
175
177
|
continue;
|
|
@@ -178,17 +180,17 @@ class HttpHandler {
|
|
|
178
180
|
if (cntPrm)
|
|
179
181
|
paramsLeft.delete(cntPrm);
|
|
180
182
|
const decode = getDecoder(prm);
|
|
181
|
-
const v = decode(request.params[
|
|
183
|
+
const v = decode(request.params[key], { coerce: true, label: key, onFail });
|
|
182
184
|
if (v !== undefined)
|
|
183
|
-
context.pathParams[
|
|
185
|
+
context.pathParams[key] = v;
|
|
184
186
|
}
|
|
185
187
|
}
|
|
186
188
|
/** parse query parameters */
|
|
187
189
|
const url = new URL(request.originalUrl || request.url || '/', 'http://tempuri.org');
|
|
188
190
|
const { searchParams } = url;
|
|
189
|
-
for (
|
|
190
|
-
const oprPrm = operation.findParameter(
|
|
191
|
-
const cntPrm = operation.owner.findParameter(
|
|
191
|
+
for (key of searchParams.keys()) {
|
|
192
|
+
const oprPrm = operation.findParameter(key, 'query');
|
|
193
|
+
const cntPrm = operation.owner.findParameter(key, 'query');
|
|
192
194
|
const prm = oprPrm || cntPrm;
|
|
193
195
|
if (!prm)
|
|
194
196
|
continue;
|
|
@@ -197,15 +199,16 @@ class HttpHandler {
|
|
|
197
199
|
if (cntPrm)
|
|
198
200
|
paramsLeft.delete(cntPrm);
|
|
199
201
|
const decode = getDecoder(prm);
|
|
200
|
-
let values = searchParams?.getAll(
|
|
202
|
+
let values = searchParams?.getAll(key);
|
|
203
|
+
const prmName = typeof prm.name === 'string' ? prm.name : key;
|
|
201
204
|
if (values?.length && prm.isArray) {
|
|
202
205
|
values = values.map(v => (0, fast_tokenizer_1.splitString)(v, { delimiters: prm.arraySeparator, quotes: true })).flat();
|
|
203
|
-
values = values.map(v => decode(v, { coerce: true, label:
|
|
206
|
+
values = values.map(v => decode(v, { coerce: true, label: key, onFail }));
|
|
204
207
|
if (values.length)
|
|
205
208
|
context.queryParams[prmName] = values;
|
|
206
209
|
}
|
|
207
210
|
else {
|
|
208
|
-
const v = decode(values[0], { coerce: true, label:
|
|
211
|
+
const v = decode(values[0], { coerce: true, label: key, onFail });
|
|
209
212
|
if (values.length)
|
|
210
213
|
context.queryParams[prmName] = v;
|
|
211
214
|
}
|
|
@@ -220,8 +223,8 @@ class HttpHandler {
|
|
|
220
223
|
}
|
|
221
224
|
catch (e) {
|
|
222
225
|
if (e instanceof valgen_1.ValidationError) {
|
|
223
|
-
|
|
224
|
-
message: `Invalid parameter (${
|
|
226
|
+
throw new common_1.BadRequestError({
|
|
227
|
+
message: `Invalid parameter (${key}) value. ` + e.message,
|
|
225
228
|
code: 'REQUEST_VALIDATION',
|
|
226
229
|
details: e.issues,
|
|
227
230
|
}, e);
|
|
@@ -241,9 +244,8 @@ class HttpHandler {
|
|
|
241
244
|
let contentType = request.header('content-type');
|
|
242
245
|
if (contentType) {
|
|
243
246
|
contentType = (0, content_type_1.parse)(contentType).type;
|
|
244
|
-
mediaType = operation.requestBody.content.find(mc =>
|
|
245
|
-
|
|
246
|
-
});
|
|
247
|
+
mediaType = operation.requestBody.content.find(mc => mc.contentType &&
|
|
248
|
+
type_is_1.default.is(contentType, Array.isArray(mc.contentType) ? mc.contentType : [mc.contentType]));
|
|
247
249
|
}
|
|
248
250
|
if (!mediaType) {
|
|
249
251
|
const contentTypes = operation.requestBody.content.map(mc => mc.contentType).flat();
|
|
@@ -262,11 +264,12 @@ class HttpHandler {
|
|
|
262
264
|
throw new common_1.MethodNotAllowedError();
|
|
263
265
|
const responseValue = await context.operationHandler.call(context.controllerInstance, context);
|
|
264
266
|
const { response } = context;
|
|
265
|
-
if (!response.writableEnded)
|
|
267
|
+
if (!response.writableEnded) {
|
|
266
268
|
await this._sendResponse(context, responseValue).finally(() => {
|
|
267
269
|
if (!response.writableEnded)
|
|
268
270
|
response.end();
|
|
269
271
|
});
|
|
272
|
+
}
|
|
270
273
|
}
|
|
271
274
|
/**
|
|
272
275
|
*
|
|
@@ -283,7 +286,7 @@ class HttpHandler {
|
|
|
283
286
|
const operationResultType = document.node.getDataType(common_1.OperationResult);
|
|
284
287
|
let operationResultEncoder = this[constants_js_1.kAssetCache].get(operationResultType, 'encode');
|
|
285
288
|
if (!operationResultEncoder) {
|
|
286
|
-
operationResultEncoder = operationResultType.generateCodec('encode');
|
|
289
|
+
operationResultEncoder = operationResultType.generateCodec('encode', { ignoreWriteonlyFields: true });
|
|
287
290
|
this[constants_js_1.kAssetCache].set(operationResultType, 'encode', operationResultEncoder);
|
|
288
291
|
}
|
|
289
292
|
/** Validate response */
|
|
@@ -295,6 +298,7 @@ class HttpHandler {
|
|
|
295
298
|
encode = operationResponse.type.generateCodec('encode', {
|
|
296
299
|
partial: operationResponse.partial,
|
|
297
300
|
projection: '*',
|
|
301
|
+
ignoreWriteonlyFields: true,
|
|
298
302
|
});
|
|
299
303
|
if (operationResponse) {
|
|
300
304
|
if (operationResponse.isArray)
|
|
@@ -321,7 +325,9 @@ class HttpHandler {
|
|
|
321
325
|
else
|
|
322
326
|
body = encode(body);
|
|
323
327
|
}
|
|
324
|
-
if (body instanceof common_1.OperationResult &&
|
|
328
|
+
if (body instanceof common_1.OperationResult &&
|
|
329
|
+
operationResponse.type &&
|
|
330
|
+
operationResponse.type !== document.node.getDataType(common_1.OperationResult)) {
|
|
325
331
|
body.type = operationResponse.type.name ? operationResponse.type.name : '#embedded';
|
|
326
332
|
}
|
|
327
333
|
}
|
|
@@ -391,8 +397,9 @@ class HttpHandler {
|
|
|
391
397
|
/** Filter available HttpOperationResponse instances according to status code. */
|
|
392
398
|
const filteredResponses = operation.responses.filter(r => r.statusCode.find(sc => sc.start <= statusCode && sc.end >= statusCode));
|
|
393
399
|
/** Throw InternalServerError if controller returns non-configured status code */
|
|
394
|
-
if (!filteredResponses.length && statusCode < 400)
|
|
400
|
+
if (!filteredResponses.length && statusCode < 400) {
|
|
395
401
|
throw new common_1.InternalServerError(`No responses defined for status code ${statusCode} in operation "${operation.name}"`);
|
|
402
|
+
}
|
|
396
403
|
/** We search for content-type in filtered HttpOperationResponse array */
|
|
397
404
|
if (filteredResponses.length) {
|
|
398
405
|
/** If no response returned, and content-type has not been set (No response wants to be returned by operation) */
|
|
@@ -405,8 +412,9 @@ class HttpHandler {
|
|
|
405
412
|
if (contentType) {
|
|
406
413
|
// Find HttpEndpointResponse instance according to content-type header
|
|
407
414
|
operationResponse = filteredResponses.find(r => type_is_1.default.is(contentType, (0, valgen_1.toArray)(r.contentType)));
|
|
408
|
-
if (!operationResponse)
|
|
415
|
+
if (!operationResponse) {
|
|
409
416
|
throw new common_1.InternalServerError(`Operation didn't configured to return "${contentType}" content`);
|
|
417
|
+
}
|
|
410
418
|
}
|
|
411
419
|
else {
|
|
412
420
|
/** Select first HttpOperationResponse if content-type header has not been set */
|
|
@@ -438,23 +446,26 @@ class HttpHandler {
|
|
|
438
446
|
case 'Entity.Get':
|
|
439
447
|
case 'Entity.FindMany':
|
|
440
448
|
case 'Entity.Update': {
|
|
441
|
-
if (!(body instanceof common_1.OperationResult))
|
|
449
|
+
if (!(body instanceof common_1.OperationResult)) {
|
|
442
450
|
body = new common_1.OperationResult({
|
|
443
451
|
payload: body,
|
|
444
452
|
});
|
|
453
|
+
}
|
|
445
454
|
if ((composition === 'Entity.Create' || composition === 'Entity.Update') &&
|
|
446
455
|
composition &&
|
|
447
|
-
body.affected == null)
|
|
456
|
+
body.affected == null) {
|
|
448
457
|
body.affected = 1;
|
|
458
|
+
}
|
|
449
459
|
break;
|
|
450
460
|
}
|
|
451
461
|
case 'Entity.Delete':
|
|
452
462
|
case 'Entity.DeleteMany':
|
|
453
463
|
case 'Entity.UpdateMany': {
|
|
454
|
-
if (!(body instanceof common_1.OperationResult))
|
|
464
|
+
if (!(body instanceof common_1.OperationResult)) {
|
|
455
465
|
body = new common_1.OperationResult({
|
|
456
466
|
affected: body,
|
|
457
467
|
});
|
|
468
|
+
}
|
|
458
469
|
body.affected =
|
|
459
470
|
typeof body.affected === 'number'
|
|
460
471
|
? body.affected
|
|
@@ -465,15 +476,19 @@ class HttpHandler {
|
|
|
465
476
|
: undefined;
|
|
466
477
|
break;
|
|
467
478
|
}
|
|
479
|
+
default:
|
|
480
|
+
break;
|
|
468
481
|
}
|
|
469
482
|
}
|
|
470
|
-
if (responseArgs.contentType && responseArgs.contentType !== parsedContentType?.type)
|
|
483
|
+
if (responseArgs.contentType && responseArgs.contentType !== parsedContentType?.type) {
|
|
471
484
|
response.setHeader('content-type', responseArgs.contentType);
|
|
485
|
+
}
|
|
472
486
|
if (responseArgs.contentType &&
|
|
473
487
|
body != null &&
|
|
474
488
|
!(body instanceof common_1.OperationResult) &&
|
|
475
|
-
type_is_1.default.is(responseArgs.contentType, [common_1.MimeTypes.opra_response_json]))
|
|
489
|
+
type_is_1.default.is(responseArgs.contentType, [common_1.MimeTypes.opra_response_json])) {
|
|
476
490
|
body = new common_1.OperationResult({ payload: body });
|
|
491
|
+
}
|
|
477
492
|
if (hasBody)
|
|
478
493
|
responseArgs.body = body;
|
|
479
494
|
return responseArgs;
|
|
@@ -482,28 +497,24 @@ class HttpHandler {
|
|
|
482
497
|
const { request, response } = context;
|
|
483
498
|
const { document } = this.adapter;
|
|
484
499
|
response.setHeader('content-type', common_1.MimeTypes.json);
|
|
500
|
+
const url = new URL(request.originalUrl || request.url || '/', 'http://tempuri.org');
|
|
501
|
+
const { searchParams } = url;
|
|
502
|
+
const documentId = searchParams.get('id');
|
|
503
|
+
const doc = documentId ? document.findDocument(documentId) : document;
|
|
504
|
+
if (!doc) {
|
|
505
|
+
return this.sendErrorResponse(response, [
|
|
506
|
+
new common_1.BadRequestError({
|
|
507
|
+
message: `Document with given id [${documentId}] does not exists`,
|
|
508
|
+
}),
|
|
509
|
+
]);
|
|
510
|
+
}
|
|
485
511
|
/** Check if response cache exists */
|
|
486
|
-
let responseBody = this[constants_js_1.kAssetCache].get(
|
|
512
|
+
let responseBody = this[constants_js_1.kAssetCache].get(doc, `$schema`);
|
|
487
513
|
/** Create response if response cache does not exists */
|
|
488
514
|
if (!responseBody) {
|
|
489
|
-
const
|
|
490
|
-
const { searchParams } = url;
|
|
491
|
-
// const nsPath = searchParams.get('ns');
|
|
492
|
-
// if (nsPath) {
|
|
493
|
-
// const arr = nsPath.split('/');
|
|
494
|
-
// let doc = document;
|
|
495
|
-
// for (const a of arr) {
|
|
496
|
-
// }
|
|
497
|
-
// }
|
|
498
|
-
const schema = document.export({ references: searchParams.get('references') });
|
|
499
|
-
const dt = document.node.getComplexType('OperationResult');
|
|
500
|
-
let encode = this[constants_js_1.kAssetCache].get(dt, 'encode');
|
|
501
|
-
if (!encode) {
|
|
502
|
-
encode = dt.generateCodec('encode');
|
|
503
|
-
this[constants_js_1.kAssetCache].set(dt, 'encode', encode);
|
|
504
|
-
}
|
|
515
|
+
const schema = doc.export();
|
|
505
516
|
responseBody = JSON.stringify(schema);
|
|
506
|
-
this[constants_js_1.kAssetCache].set(
|
|
517
|
+
this[constants_js_1.kAssetCache].set(doc, `$schema`, responseBody);
|
|
507
518
|
}
|
|
508
519
|
response.end(responseBody);
|
|
509
520
|
}
|
|
@@ -550,7 +561,7 @@ class HttpHandler {
|
|
|
550
561
|
const dt = document.node.getComplexType('OperationResult');
|
|
551
562
|
let encode = this[constants_js_1.kAssetCache].get(dt, 'encode');
|
|
552
563
|
if (!encode) {
|
|
553
|
-
encode = dt.generateCodec('encode');
|
|
564
|
+
encode = dt.generateCodec('encode', { ignoreWriteonlyFields: true });
|
|
554
565
|
this[constants_js_1.kAssetCache].set(dt, 'encode', encode);
|
|
555
566
|
}
|
|
556
567
|
const { i18n } = this.adapter;
|
|
@@ -6,10 +6,10 @@ const tslib_1 = require("tslib");
|
|
|
6
6
|
Some parts of this file contains codes from open source express library
|
|
7
7
|
https://github.com/expressjs
|
|
8
8
|
*/
|
|
9
|
+
const type_is_1 = tslib_1.__importDefault(require("@browsery/type-is"));
|
|
9
10
|
const accepts_1 = tslib_1.__importDefault(require("accepts"));
|
|
10
11
|
const fresh_1 = tslib_1.__importDefault(require("fresh"));
|
|
11
12
|
const range_parser_1 = tslib_1.__importDefault(require("range-parser"));
|
|
12
|
-
const type_is_1 = tslib_1.__importDefault(require("@browsery/type-is"));
|
|
13
13
|
const body_reader_js_1 = require("../utils/body-reader.js");
|
|
14
14
|
class HttpIncomingHost {
|
|
15
15
|
get protocol() {
|
|
@@ -41,11 +41,11 @@ class HttpIncomingHost {
|
|
|
41
41
|
get fresh() {
|
|
42
42
|
const method = this.method;
|
|
43
43
|
// GET or HEAD for weak freshness validation only
|
|
44
|
-
if ('GET'
|
|
44
|
+
if (method !== 'GET' && method !== 'HEAD')
|
|
45
45
|
return false;
|
|
46
46
|
const status = this.res?.statusCode;
|
|
47
47
|
// 2xx or 304 as per rfc2616 14.26
|
|
48
|
-
if ((status >= 200 && status < 300) ||
|
|
48
|
+
if ((status >= 200 && status < 300) || status === 304) {
|
|
49
49
|
return (0, fresh_1.default)(this.headers, {
|
|
50
50
|
etag: this.res.getHeader('ETag'),
|
|
51
51
|
'last-modified': this.res.getHeader('Last-Modified'),
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
7
|
exports.HttpOutgoingHost = void 0;
|
|
8
8
|
const tslib_1 = require("tslib");
|
|
9
|
+
const common_1 = require("@opra/common");
|
|
9
10
|
const content_disposition_1 = tslib_1.__importDefault(require("content-disposition"));
|
|
10
11
|
const content_type_1 = tslib_1.__importDefault(require("content-type"));
|
|
11
12
|
const cookie_1 = tslib_1.__importDefault(require("cookie"));
|
|
@@ -15,7 +16,6 @@ const mime_types_1 = tslib_1.__importDefault(require("mime-types"));
|
|
|
15
16
|
const path_1 = tslib_1.__importDefault(require("path"));
|
|
16
17
|
const putil_varhelpers_1 = require("putil-varhelpers");
|
|
17
18
|
const vary_1 = tslib_1.__importDefault(require("vary"));
|
|
18
|
-
const common_1 = require("@opra/common");
|
|
19
19
|
const charsetRegExp = /;\s*charset\s*=/;
|
|
20
20
|
class HttpOutgoingHost {
|
|
21
21
|
attachment(filename) {
|
|
@@ -164,7 +164,7 @@ class HttpOutgoingHost {
|
|
|
164
164
|
if (req?.fresh)
|
|
165
165
|
this.statusCode = 304;
|
|
166
166
|
// strip irrelevant headers
|
|
167
|
-
if (
|
|
167
|
+
if (this.statusCode === 204 || this.statusCode === 304) {
|
|
168
168
|
this.removeHeader('Content-Type');
|
|
169
169
|
this.removeHeader('Content-Length');
|
|
170
170
|
this.removeHeader('Transfer-Encoding');
|
|
@@ -16,9 +16,7 @@ class MultipartReader extends events_1.EventEmitter {
|
|
|
16
16
|
this._incoming = incoming;
|
|
17
17
|
const form = (this._form = (0, formidable_1.default)({
|
|
18
18
|
...options,
|
|
19
|
-
filter: (part) =>
|
|
20
|
-
return !this._cancelled && (!options?.filter || options.filter(part));
|
|
21
|
-
},
|
|
19
|
+
filter: (part) => !this._cancelled && (!options?.filter || options.filter(part)),
|
|
22
20
|
}));
|
|
23
21
|
form.once('error', () => {
|
|
24
22
|
this._cancelled = true;
|
|
@@ -73,7 +71,7 @@ class MultipartReader extends events_1.EventEmitter {
|
|
|
73
71
|
}
|
|
74
72
|
resume() {
|
|
75
73
|
if (!this._started)
|
|
76
|
-
this._form.parse(this._incoming, () =>
|
|
74
|
+
this._form.parse(this._incoming, () => undefined);
|
|
77
75
|
if (this._form.req)
|
|
78
76
|
this._form.resume();
|
|
79
77
|
}
|
|
@@ -92,12 +90,8 @@ class MultipartReader extends events_1.EventEmitter {
|
|
|
92
90
|
return resolve();
|
|
93
91
|
file._writeStream.once('close', resolve);
|
|
94
92
|
})
|
|
95
|
-
.then(() =>
|
|
96
|
-
|
|
97
|
-
})
|
|
98
|
-
.then(() => {
|
|
99
|
-
return 0;
|
|
100
|
-
}));
|
|
93
|
+
.then(() => promises_1.default.unlink(file.filepath))
|
|
94
|
+
.then(() => 0));
|
|
101
95
|
});
|
|
102
96
|
return Promise.allSettled(promises);
|
|
103
97
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.NodeIncomingMessageHost = exports.kHttpParser = exports.kTrailersDistinct = exports.kTrailers = exports.kHeadersDistinct = exports.kHeaders = exports.CRLF = void 0;
|
|
4
|
-
const stream_1 = require("stream");
|
|
5
4
|
const common_1 = require("@opra/common");
|
|
5
|
+
const stream_1 = require("stream");
|
|
6
6
|
const convert_to_headers_js_1 = require("../utils/convert-to-headers.js");
|
|
7
7
|
const convert_to_raw_headers_js_1 = require("../utils/convert-to-raw-headers.js");
|
|
8
8
|
exports.CRLF = Buffer.from('\r\n');
|
|
@@ -36,10 +36,12 @@ class NodeIncomingMessageHost extends stream_1.Duplex {
|
|
|
36
36
|
else
|
|
37
37
|
this.body = Buffer.from(JSON.stringify(init.body), 'utf-8');
|
|
38
38
|
}
|
|
39
|
-
if (init.headers)
|
|
39
|
+
if (init.headers) {
|
|
40
40
|
this.rawHeaders = Array.isArray(init.headers) ? init.headers : (0, convert_to_raw_headers_js_1.convertToRawHeaders)(init.headers);
|
|
41
|
-
|
|
41
|
+
}
|
|
42
|
+
if (init.trailers) {
|
|
42
43
|
this.rawTrailers = Array.isArray(init.trailers) ? init.trailers : (0, convert_to_raw_headers_js_1.convertToRawHeaders)(init.trailers);
|
|
44
|
+
}
|
|
43
45
|
this.ip = init.ip || '';
|
|
44
46
|
this.ips = init.ips || (this.ip ? [this.ip] : []);
|
|
45
47
|
if (this.body && !this.headers['content-length'])
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.NodeIncomingMessage = void 0;
|
|
4
|
-
const stream_1 = require("stream");
|
|
5
4
|
const http_parser_1 = require("@browsery/http-parser");
|
|
6
5
|
const common_1 = require("@opra/common");
|
|
6
|
+
const stream_1 = require("stream");
|
|
7
7
|
const node_incoming_message_host_js_1 = require("../impl/node-incoming-message.host.js");
|
|
8
8
|
const concat_readable_js_1 = require("../utils/concat-readable.js");
|
|
9
9
|
/**
|
|
@@ -17,8 +17,9 @@ var NodeIncomingMessage;
|
|
|
17
17
|
* @param iterable
|
|
18
18
|
*/
|
|
19
19
|
function from(iterable) {
|
|
20
|
-
if (typeof iterable === 'object' && !((0, common_1.isIterable)(iterable) || (0, common_1.isAsyncIterable)(iterable)))
|
|
20
|
+
if (typeof iterable === 'object' && !((0, common_1.isIterable)(iterable) || (0, common_1.isAsyncIterable)(iterable))) {
|
|
21
21
|
return new node_incoming_message_host_js_1.NodeIncomingMessageHost(iterable);
|
|
22
|
+
}
|
|
22
23
|
const msg = new node_incoming_message_host_js_1.NodeIncomingMessageHost();
|
|
23
24
|
const parser = (msg[node_incoming_message_host_js_1.kHttpParser] = new http_parser_1.HTTPParser(http_parser_1.HTTPParser.REQUEST));
|
|
24
25
|
let bodyChunks;
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.BodyReader = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
|
+
const type_is_1 = tslib_1.__importDefault(require("@browsery/type-is"));
|
|
6
|
+
const common_1 = require("@opra/common");
|
|
5
7
|
const base64_stream_1 = require("base64-stream");
|
|
6
8
|
const bytes_1 = tslib_1.__importDefault(require("bytes"));
|
|
7
9
|
const content_type_1 = require("content-type");
|
|
@@ -9,8 +11,6 @@ const events_1 = require("events");
|
|
|
9
11
|
const iconv_lite_1 = tslib_1.__importDefault(require("iconv-lite"));
|
|
10
12
|
const stream_1 = require("stream");
|
|
11
13
|
const zlib = tslib_1.__importStar(require("zlib"));
|
|
12
|
-
const type_is_1 = tslib_1.__importDefault(require("@browsery/type-is"));
|
|
13
|
-
const common_1 = require("@opra/common");
|
|
14
14
|
/**
|
|
15
15
|
*
|
|
16
16
|
* @class BodyReader
|
|
@@ -33,11 +33,12 @@ class BodyReader extends events_1.EventEmitter {
|
|
|
33
33
|
}
|
|
34
34
|
async read() {
|
|
35
35
|
/* istanbul ignore next */
|
|
36
|
-
if (this._completed)
|
|
36
|
+
if (this._completed) {
|
|
37
37
|
throw new common_1.InternalServerError({
|
|
38
38
|
message: 'Stream already read',
|
|
39
39
|
code: 'STREAM_ALREADY_READ',
|
|
40
40
|
});
|
|
41
|
+
}
|
|
41
42
|
if (!this.req.readable) {
|
|
42
43
|
throw new common_1.InternalServerError({
|
|
43
44
|
message: 'Stream is not readable',
|
|
@@ -61,8 +62,9 @@ class BodyReader extends events_1.EventEmitter {
|
|
|
61
62
|
* http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.3
|
|
62
63
|
*/
|
|
63
64
|
const contentLength = parseInt(this.req.headers['content-length'] || '0', 10);
|
|
64
|
-
if (this.req.headers['transfer-encoding'] === undefined && !(contentLength && !isNaN(contentLength)))
|
|
65
|
+
if (this.req.headers['transfer-encoding'] === undefined && !(contentLength && !isNaN(contentLength))) {
|
|
65
66
|
return this.onEnd();
|
|
67
|
+
}
|
|
66
68
|
// check the length and limit options.
|
|
67
69
|
// note: we intentionally leave the stream paused,
|
|
68
70
|
// so users should handle the stream themselves.
|
|
@@ -147,7 +149,6 @@ class BodyReader extends events_1.EventEmitter {
|
|
|
147
149
|
message: 'request aborted',
|
|
148
150
|
code: 'ECONNABORTED',
|
|
149
151
|
details: {
|
|
150
|
-
length,
|
|
151
152
|
received: this._receivedSize,
|
|
152
153
|
},
|
|
153
154
|
}));
|