@opra/core 1.0.0-alpha.23 → 1.0.0-alpha.25
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/constants.js +1 -2
- package/cjs/execution-context.js +1 -0
- package/cjs/http/express-adapter.js +15 -18
- package/cjs/http/http-adapter.js +2 -5
- package/cjs/http/http-context.js +14 -10
- package/cjs/http/{impl/http-handler.js → http-handler.js} +159 -163
- package/cjs/http/impl/multipart-reader.js +132 -44
- package/cjs/index.js +1 -1
- package/cjs/platform-adapter.js +0 -3
- package/esm/constants.js +0 -1
- package/esm/execution-context.js +1 -0
- package/esm/http/express-adapter.js +15 -18
- package/esm/http/http-adapter.js +2 -5
- package/esm/http/http-context.js +14 -10
- package/esm/http/{impl/http-handler.js → http-handler.js} +152 -156
- package/esm/http/impl/multipart-reader.js +133 -45
- package/esm/index.js +1 -1
- package/esm/platform-adapter.js +0 -3
- package/package.json +5 -5
- package/types/constants.d.ts +0 -1
- package/types/execution-context.d.ts +2 -1
- package/types/http/http-adapter.d.ts +23 -5
- package/types/http/{impl/http-handler.d.ts → http-handler.d.ts} +8 -8
- package/types/http/impl/multipart-reader.d.ts +38 -19
- package/types/index.d.ts +1 -1
- package/types/platform-adapter.d.ts +0 -4
- package/cjs/helpers/logger.js +0 -35
- package/esm/helpers/logger.js +0 -31
- package/types/helpers/logger.d.ts +0 -14
package/cjs/constants.js
CHANGED
package/cjs/execution-context.js
CHANGED
|
@@ -8,6 +8,7 @@ const strict_typed_events_1 = require("strict-typed-events");
|
|
|
8
8
|
class ExecutionContext extends strict_typed_events_1.AsyncEventEmitter {
|
|
9
9
|
constructor(init) {
|
|
10
10
|
super();
|
|
11
|
+
this.errors = [];
|
|
11
12
|
this.document = init.document;
|
|
12
13
|
this.protocol = init.protocol;
|
|
13
14
|
this.platform = init.platform;
|
|
@@ -5,11 +5,11 @@ const tslib_1 = require("tslib");
|
|
|
5
5
|
const common_1 = require("@opra/common");
|
|
6
6
|
const express_1 = require("express");
|
|
7
7
|
const nodePath = tslib_1.__importStar(require("path"));
|
|
8
|
-
const constants_js_1 = require("../constants.js");
|
|
9
8
|
const http_adapter_js_1 = require("./http-adapter.js");
|
|
10
9
|
const http_context_js_1 = require("./http-context.js");
|
|
11
10
|
const http_incoming_interface_js_1 = require("./interfaces/http-incoming.interface.js");
|
|
12
11
|
const http_outgoing_interface_js_1 = require("./interfaces/http-outgoing.interface.js");
|
|
12
|
+
const wrap_exception_1 = require("./utils/wrap-exception");
|
|
13
13
|
class ExpressAdapter extends http_adapter_js_1.HttpAdapter {
|
|
14
14
|
constructor(app, document, options) {
|
|
15
15
|
super(document, options);
|
|
@@ -40,7 +40,8 @@ class ExpressAdapter extends http_adapter_js_1.HttpAdapter {
|
|
|
40
40
|
await resource.onShutdown.call(instance, resource);
|
|
41
41
|
}
|
|
42
42
|
catch (e) {
|
|
43
|
-
this.
|
|
43
|
+
if (this.listenerCount('error'))
|
|
44
|
+
this.emit('error', (0, wrap_exception_1.wrapException)(e));
|
|
44
45
|
}
|
|
45
46
|
}
|
|
46
47
|
}
|
|
@@ -80,7 +81,7 @@ class ExpressAdapter extends http_adapter_js_1.HttpAdapter {
|
|
|
80
81
|
/** Add an endpoint that returns document schema */
|
|
81
82
|
router.get('/\\$schema', (_req, _res, next) => {
|
|
82
83
|
const context = createContext(_req, _res);
|
|
83
|
-
this
|
|
84
|
+
this.handler.sendDocumentSchema(context).catch(next);
|
|
84
85
|
});
|
|
85
86
|
/** Add operation endpoints */
|
|
86
87
|
if (this.api.controllers.size) {
|
|
@@ -100,13 +101,13 @@ class ExpressAdapter extends http_adapter_js_1.HttpAdapter {
|
|
|
100
101
|
operation,
|
|
101
102
|
operationHandler,
|
|
102
103
|
});
|
|
103
|
-
this
|
|
104
|
+
this.handler
|
|
104
105
|
.handleRequest(context)
|
|
105
106
|
.then(() => {
|
|
106
107
|
if (!_res.headersSent)
|
|
107
108
|
_next();
|
|
108
109
|
})
|
|
109
|
-
.catch((e) => this.
|
|
110
|
+
.catch((e) => this.emit('error', e));
|
|
110
111
|
});
|
|
111
112
|
}
|
|
112
113
|
if (controller.controllers.size) {
|
|
@@ -119,19 +120,15 @@ class ExpressAdapter extends http_adapter_js_1.HttpAdapter {
|
|
|
119
120
|
}
|
|
120
121
|
/** Add an endpoint that returns 404 error at last */
|
|
121
122
|
router.use('*', (_req, _res, next) => {
|
|
122
|
-
const
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
},
|
|
132
|
-
}),
|
|
133
|
-
])
|
|
134
|
-
.catch(next);
|
|
123
|
+
const context = createContext(_req, _res);
|
|
124
|
+
context.errors.push(new common_1.NotFoundError({
|
|
125
|
+
message: `No endpoint found at [${_req.method}]${_req.baseUrl}`,
|
|
126
|
+
details: {
|
|
127
|
+
path: _req.baseUrl,
|
|
128
|
+
method: _req.method,
|
|
129
|
+
},
|
|
130
|
+
}));
|
|
131
|
+
this.handler.sendResponse(context).catch(next);
|
|
135
132
|
});
|
|
136
133
|
}
|
|
137
134
|
_createControllers(controller) {
|
package/cjs/http/http-adapter.js
CHANGED
|
@@ -2,9 +2,8 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.HttpAdapter = void 0;
|
|
4
4
|
const common_1 = require("@opra/common");
|
|
5
|
-
const constants_js_1 = require("../constants.js");
|
|
6
5
|
const platform_adapter_js_1 = require("../platform-adapter.js");
|
|
7
|
-
const http_handler_js_1 = require("./
|
|
6
|
+
const http_handler_js_1 = require("./http-handler.js");
|
|
8
7
|
/**
|
|
9
8
|
*
|
|
10
9
|
* @class HttpAdapter
|
|
@@ -15,10 +14,8 @@ class HttpAdapter extends platform_adapter_js_1.PlatformAdapter {
|
|
|
15
14
|
this.protocol = 'http';
|
|
16
15
|
if (!(document.api instanceof common_1.HttpApi))
|
|
17
16
|
throw new TypeError(`The document does not expose an HTTP Api`);
|
|
18
|
-
this
|
|
17
|
+
this.handler = new http_handler_js_1.HttpHandler(this);
|
|
19
18
|
this.interceptors = [...(options?.interceptors || [])];
|
|
20
|
-
if (options?.onRequest)
|
|
21
|
-
this.on('request', options.onRequest);
|
|
22
19
|
}
|
|
23
20
|
get api() {
|
|
24
21
|
return this.document.api;
|
package/cjs/http/http-context.js
CHANGED
|
@@ -29,6 +29,10 @@ class HttpContext extends execution_context_js_1.ExecutionContext {
|
|
|
29
29
|
this.pathParams = init.pathParams || {};
|
|
30
30
|
this.queryParams = init.queryParams || {};
|
|
31
31
|
this._body = init.body;
|
|
32
|
+
this.on('finish', () => {
|
|
33
|
+
if (this._multipartReader)
|
|
34
|
+
this._multipartReader.purge().catch(() => undefined);
|
|
35
|
+
});
|
|
32
36
|
}
|
|
33
37
|
get isMultipart() {
|
|
34
38
|
return !!this.request.is('multipart');
|
|
@@ -38,21 +42,21 @@ class HttpContext extends execution_context_js_1.ExecutionContext {
|
|
|
38
42
|
throw new common_1.InternalServerError('Request content is not a multipart content');
|
|
39
43
|
if (this._multipartReader)
|
|
40
44
|
return this._multipartReader;
|
|
41
|
-
const {
|
|
45
|
+
const { mediaType } = this;
|
|
42
46
|
if (mediaType?.contentType) {
|
|
43
47
|
const arr = Array.isArray(mediaType.contentType) ? mediaType.contentType : [mediaType.contentType];
|
|
44
48
|
const contentType = arr.find(ct => type_is_1.default.is(ct, ['multipart']));
|
|
45
49
|
if (!contentType)
|
|
46
50
|
throw new common_1.NotAcceptableError('This endpoint does not accept multipart requests');
|
|
47
51
|
}
|
|
48
|
-
const reader = new multipart_reader_js_1.MultipartReader(
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
});
|
|
52
|
+
const reader = new multipart_reader_js_1.MultipartReader(this, {
|
|
53
|
+
limits: {
|
|
54
|
+
fields: mediaType?.maxFields,
|
|
55
|
+
fieldSize: mediaType?.maxFieldsSize,
|
|
56
|
+
files: mediaType?.maxFiles,
|
|
57
|
+
fileSize: mediaType?.maxFileSize,
|
|
58
|
+
},
|
|
59
|
+
}, mediaType);
|
|
56
60
|
this._multipartReader = reader;
|
|
57
61
|
return reader;
|
|
58
62
|
}
|
|
@@ -70,7 +74,7 @@ class HttpContext extends execution_context_js_1.ExecutionContext {
|
|
|
70
74
|
if (mediaType && multipartFields?.length) {
|
|
71
75
|
const fieldsFound = new Map();
|
|
72
76
|
for (const item of parts) {
|
|
73
|
-
const field = mediaType.findMultipartField(item.
|
|
77
|
+
const field = mediaType.findMultipartField(item.field, item.kind);
|
|
74
78
|
if (field) {
|
|
75
79
|
fieldsFound.set(field, true);
|
|
76
80
|
this._body.push(item);
|
|
@@ -9,15 +9,15 @@ const content_type_1 = require("content-type");
|
|
|
9
9
|
const fast_tokenizer_1 = require("fast-tokenizer");
|
|
10
10
|
const ts_gems_1 = require("ts-gems");
|
|
11
11
|
const valgen_1 = require("valgen");
|
|
12
|
-
const
|
|
13
|
-
const
|
|
12
|
+
const constants_1 = require("../constants");
|
|
13
|
+
const wrap_exception_1 = require("./utils/wrap-exception");
|
|
14
14
|
/**
|
|
15
15
|
* @class HttpHandler
|
|
16
16
|
*/
|
|
17
17
|
class HttpHandler {
|
|
18
18
|
constructor(adapter) {
|
|
19
19
|
this.adapter = adapter;
|
|
20
|
-
this[
|
|
20
|
+
this[constants_1.kAssetCache] = adapter[constants_1.kAssetCache];
|
|
21
21
|
}
|
|
22
22
|
/**
|
|
23
23
|
* Main http request handler
|
|
@@ -42,7 +42,7 @@ class HttpHandler {
|
|
|
42
42
|
throw e;
|
|
43
43
|
if (e instanceof valgen_1.ValidationError) {
|
|
44
44
|
throw new common_1.BadRequestError({
|
|
45
|
-
message:
|
|
45
|
+
message: 'Response validation failed',
|
|
46
46
|
code: 'RESPONSE_VALIDATION',
|
|
47
47
|
details: e.issues,
|
|
48
48
|
}, e);
|
|
@@ -68,19 +68,17 @@ class HttpHandler {
|
|
|
68
68
|
let e = error;
|
|
69
69
|
if (e instanceof valgen_1.ValidationError) {
|
|
70
70
|
e = new common_1.InternalServerError({
|
|
71
|
-
message:
|
|
71
|
+
message: 'Response validation failed',
|
|
72
72
|
code: 'RESPONSE_VALIDATION',
|
|
73
73
|
details: e.issues,
|
|
74
74
|
}, e);
|
|
75
75
|
}
|
|
76
76
|
else
|
|
77
|
-
e = (0,
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
response.end();
|
|
83
|
-
});
|
|
77
|
+
e = (0, wrap_exception_1.wrapException)(e);
|
|
78
|
+
if (this.onError)
|
|
79
|
+
await this.onError(context, error);
|
|
80
|
+
context.errors.push(e);
|
|
81
|
+
await this.sendResponse(context);
|
|
84
82
|
}
|
|
85
83
|
finally {
|
|
86
84
|
await context.emitAsync('finish');
|
|
@@ -121,10 +119,10 @@ class HttpHandler {
|
|
|
121
119
|
};
|
|
122
120
|
/** prepare decoders */
|
|
123
121
|
const getDecoder = (prm) => {
|
|
124
|
-
let decode = this[
|
|
122
|
+
let decode = this[constants_1.kAssetCache].get(prm, 'decode');
|
|
125
123
|
if (!decode) {
|
|
126
124
|
decode = prm.type?.generateCodec('decode', { ignoreReadonlyFields: true }) || valgen_1.vg.isAny();
|
|
127
|
-
this[
|
|
125
|
+
this[constants_1.kAssetCache].set(prm, 'decode', decode);
|
|
128
126
|
}
|
|
129
127
|
return decode;
|
|
130
128
|
};
|
|
@@ -214,6 +212,7 @@ class HttpHandler {
|
|
|
214
212
|
}
|
|
215
213
|
}
|
|
216
214
|
for (const prm of paramsLeft) {
|
|
215
|
+
key = String(prm.name);
|
|
217
216
|
// Throw error for required parameters
|
|
218
217
|
if (prm.required) {
|
|
219
218
|
const decode = getDecoder(prm);
|
|
@@ -265,7 +264,7 @@ class HttpHandler {
|
|
|
265
264
|
const responseValue = await context.operationHandler.call(context.controllerInstance, context);
|
|
266
265
|
const { response } = context;
|
|
267
266
|
if (!response.writableEnded) {
|
|
268
|
-
await this.
|
|
267
|
+
await this.sendResponse(context, responseValue).finally(() => {
|
|
269
268
|
if (!response.writableEnded)
|
|
270
269
|
response.end();
|
|
271
270
|
});
|
|
@@ -277,96 +276,156 @@ class HttpHandler {
|
|
|
277
276
|
* @param responseValue
|
|
278
277
|
* @protected
|
|
279
278
|
*/
|
|
280
|
-
async
|
|
279
|
+
async sendResponse(context, responseValue) {
|
|
280
|
+
if (context.errors.length)
|
|
281
|
+
return this._sendErrorResponse(context);
|
|
281
282
|
const { response } = context;
|
|
282
283
|
const { document } = this.adapter;
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
if (
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
284
|
+
try {
|
|
285
|
+
const responseArgs = this._determineResponseArgs(context, responseValue);
|
|
286
|
+
const { operationResponse, statusCode } = responseArgs;
|
|
287
|
+
let { contentType, body } = responseArgs;
|
|
288
|
+
const operationResultType = document.node.getDataType(common_1.OperationResult);
|
|
289
|
+
let operationResultEncoder = this[constants_1.kAssetCache].get(operationResultType, 'encode');
|
|
290
|
+
if (!operationResultEncoder) {
|
|
291
|
+
operationResultEncoder = operationResultType.generateCodec('encode', { ignoreWriteonlyFields: true });
|
|
292
|
+
this[constants_1.kAssetCache].set(operationResultType, 'encode', operationResultEncoder);
|
|
293
|
+
}
|
|
294
|
+
/** Validate response */
|
|
295
|
+
if (operationResponse?.type) {
|
|
296
|
+
if (!(body == null && statusCode === common_1.HttpStatusCode.NO_CONTENT)) {
|
|
297
|
+
/** Generate encoder */
|
|
298
|
+
let encode = this[constants_1.kAssetCache].get(operationResponse, 'encode');
|
|
299
|
+
if (!encode) {
|
|
300
|
+
encode = operationResponse.type.generateCodec('encode', {
|
|
301
|
+
partial: operationResponse.partial,
|
|
302
|
+
projection: '*',
|
|
303
|
+
ignoreWriteonlyFields: true,
|
|
304
|
+
onFail: issue => `Response body validation failed: ` + issue.message,
|
|
305
|
+
});
|
|
306
|
+
if (operationResponse) {
|
|
307
|
+
if (operationResponse.isArray)
|
|
308
|
+
encode = valgen_1.vg.isArray(encode);
|
|
309
|
+
this[constants_1.kAssetCache].set(operationResponse, 'encode', encode);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
/** Encode body */
|
|
313
|
+
if (operationResponse.type.extendsFrom(operationResultType)) {
|
|
314
|
+
if (body instanceof common_1.OperationResult)
|
|
315
|
+
body = encode(body);
|
|
316
|
+
else {
|
|
317
|
+
body.payload = encode(body.payload);
|
|
318
|
+
body = operationResultEncoder(body);
|
|
319
|
+
}
|
|
307
320
|
}
|
|
308
|
-
}
|
|
309
|
-
/** Encode body */
|
|
310
|
-
if (operationResponse.type.extendsFrom(operationResultType)) {
|
|
311
|
-
if (body instanceof common_1.OperationResult)
|
|
312
|
-
body = encode(body);
|
|
313
321
|
else {
|
|
314
|
-
body
|
|
315
|
-
|
|
322
|
+
if (body instanceof common_1.OperationResult &&
|
|
323
|
+
contentType &&
|
|
324
|
+
type_is_1.default.is(contentType, [common_1.MimeTypes.opra_response_json])) {
|
|
325
|
+
body.payload = encode(body.payload);
|
|
326
|
+
body = operationResultEncoder(body);
|
|
327
|
+
}
|
|
328
|
+
else {
|
|
329
|
+
body = encode(body);
|
|
330
|
+
}
|
|
316
331
|
}
|
|
317
|
-
}
|
|
318
|
-
else {
|
|
319
332
|
if (body instanceof common_1.OperationResult &&
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
body.
|
|
323
|
-
body = operationResultEncoder(body);
|
|
333
|
+
operationResponse.type &&
|
|
334
|
+
operationResponse.type !== document.node.getDataType(common_1.OperationResult)) {
|
|
335
|
+
body.type = operationResponse.type.name ? operationResponse.type.name : '#embedded';
|
|
324
336
|
}
|
|
325
|
-
else
|
|
326
|
-
body = encode(body);
|
|
327
|
-
}
|
|
328
|
-
if (body instanceof common_1.OperationResult &&
|
|
329
|
-
operationResponse.type &&
|
|
330
|
-
operationResponse.type !== document.node.getDataType(common_1.OperationResult)) {
|
|
331
|
-
body.type = operationResponse.type.name ? operationResponse.type.name : '#embedded';
|
|
332
337
|
}
|
|
333
338
|
}
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
339
|
+
else if (body != null) {
|
|
340
|
+
if (body instanceof common_1.OperationResult) {
|
|
341
|
+
body = operationResultEncoder(body);
|
|
342
|
+
contentType = common_1.MimeTypes.opra_response_json;
|
|
343
|
+
}
|
|
344
|
+
else if (Buffer.isBuffer(body))
|
|
345
|
+
contentType = common_1.MimeTypes.binary;
|
|
346
|
+
else if (typeof body === 'object') {
|
|
347
|
+
contentType = contentType || common_1.MimeTypes.json;
|
|
348
|
+
if (typeof body.toJSON === 'function')
|
|
349
|
+
body = body.toJSON();
|
|
350
|
+
}
|
|
351
|
+
else {
|
|
352
|
+
contentType = contentType || common_1.MimeTypes.text;
|
|
353
|
+
body = String(body);
|
|
354
|
+
}
|
|
346
355
|
}
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
356
|
+
/** Set content-type header value if not set */
|
|
357
|
+
if (contentType && contentType !== responseArgs.contentType)
|
|
358
|
+
response.setHeader('content-type', contentType);
|
|
359
|
+
response.status(statusCode);
|
|
360
|
+
if (body == null) {
|
|
361
|
+
response.end();
|
|
362
|
+
return;
|
|
350
363
|
}
|
|
364
|
+
let x;
|
|
365
|
+
if (Buffer.isBuffer(body) || (0, common_1.isReadableStream)(body))
|
|
366
|
+
x = body;
|
|
367
|
+
else if ((0, common_1.isBlob)(body))
|
|
368
|
+
x = body.stream();
|
|
369
|
+
else if (typeof body === 'object')
|
|
370
|
+
x = JSON.stringify(body);
|
|
371
|
+
else
|
|
372
|
+
x = String(body);
|
|
373
|
+
response.end(x);
|
|
374
|
+
}
|
|
375
|
+
catch (error) {
|
|
376
|
+
context.errors.push(error);
|
|
377
|
+
return this._sendErrorResponse(context);
|
|
351
378
|
}
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
response.
|
|
356
|
-
if (body == null) {
|
|
379
|
+
}
|
|
380
|
+
async _sendErrorResponse(context) {
|
|
381
|
+
const { response, errors } = context;
|
|
382
|
+
if (response.headersSent) {
|
|
357
383
|
response.end();
|
|
358
384
|
return;
|
|
359
385
|
}
|
|
360
|
-
|
|
361
|
-
if (
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
386
|
+
const wrappedErrors = errors.map(wrap_exception_1.wrapException);
|
|
387
|
+
if (!wrappedErrors.length)
|
|
388
|
+
wrappedErrors.push(new common_1.InternalServerError());
|
|
389
|
+
// Sort errors from fatal to info
|
|
390
|
+
wrappedErrors.sort((a, b) => {
|
|
391
|
+
const i = common_1.IssueSeverity.Keys.indexOf(a.severity) - common_1.IssueSeverity.Keys.indexOf(b.severity);
|
|
392
|
+
if (i === 0)
|
|
393
|
+
return b.status - a.status;
|
|
394
|
+
return i;
|
|
395
|
+
});
|
|
396
|
+
context.errors = wrappedErrors;
|
|
397
|
+
let status = response.statusCode || 0;
|
|
398
|
+
if (!status || status < Number(common_1.HttpStatusCode.BAD_REQUEST)) {
|
|
399
|
+
status = wrappedErrors[0].status;
|
|
400
|
+
if (status < Number(common_1.HttpStatusCode.BAD_REQUEST))
|
|
401
|
+
status = common_1.HttpStatusCode.INTERNAL_SERVER_ERROR;
|
|
402
|
+
}
|
|
403
|
+
response.statusCode = status;
|
|
404
|
+
this.adapter.emitAsync('error', wrappedErrors[0], context).catch(() => undefined);
|
|
405
|
+
const { document } = this.adapter;
|
|
406
|
+
const dt = document.node.getComplexType('OperationResult');
|
|
407
|
+
let encode = this[constants_1.kAssetCache].get(dt, 'encode');
|
|
408
|
+
if (!encode) {
|
|
409
|
+
encode = dt.generateCodec('encode', { ignoreWriteonlyFields: true });
|
|
410
|
+
this[constants_1.kAssetCache].set(dt, 'encode', encode);
|
|
411
|
+
}
|
|
412
|
+
const { i18n } = this.adapter;
|
|
413
|
+
const bodyObject = new common_1.OperationResult({
|
|
414
|
+
errors: wrappedErrors.map(x => {
|
|
415
|
+
const o = x.toJSON();
|
|
416
|
+
if (!(process.env.NODE_ENV === 'dev' || process.env.NODE_ENV === 'development'))
|
|
417
|
+
delete o.stack;
|
|
418
|
+
return i18n.deep(o);
|
|
419
|
+
}),
|
|
420
|
+
});
|
|
421
|
+
const body = encode(bodyObject);
|
|
422
|
+
response.setHeader(common_1.HttpHeaderCodes.Content_Type, common_1.MimeTypes.opra_response_json + '; charset=utf-8');
|
|
423
|
+
response.setHeader(common_1.HttpHeaderCodes.Cache_Control, 'no-cache');
|
|
424
|
+
response.setHeader(common_1.HttpHeaderCodes.Pragma, 'no-cache');
|
|
425
|
+
response.setHeader(common_1.HttpHeaderCodes.Expires, '-1');
|
|
426
|
+
response.setHeader(common_1.HttpHeaderCodes.X_Opra_Version, common_1.OpraSchema.SpecVersion);
|
|
427
|
+
response.send(JSON.stringify(body));
|
|
428
|
+
response.end();
|
|
370
429
|
}
|
|
371
430
|
/**
|
|
372
431
|
*
|
|
@@ -390,7 +449,7 @@ class HttpHandler {
|
|
|
390
449
|
}
|
|
391
450
|
let operationResponse;
|
|
392
451
|
const cacheKey = `HttpOperationResponse:${statusCode}${contentType ? ':' + contentType : ''}`;
|
|
393
|
-
let responseArgs = this[
|
|
452
|
+
let responseArgs = this[constants_1.kAssetCache].get(response, cacheKey);
|
|
394
453
|
if (!responseArgs) {
|
|
395
454
|
responseArgs = { statusCode, contentType };
|
|
396
455
|
if (operation.responses.length) {
|
|
@@ -425,6 +484,8 @@ class HttpHandler {
|
|
|
425
484
|
: operationResponse.contentType);
|
|
426
485
|
if (typeof ct === 'string')
|
|
427
486
|
responseArgs.contentType = contentType = ct;
|
|
487
|
+
else if (operationResponse.type)
|
|
488
|
+
responseArgs.contentType = common_1.MimeTypes.opra_response_json;
|
|
428
489
|
}
|
|
429
490
|
}
|
|
430
491
|
}
|
|
@@ -436,7 +497,7 @@ class HttpHandler {
|
|
|
436
497
|
}
|
|
437
498
|
if (!hasBody)
|
|
438
499
|
delete responseArgs.contentType;
|
|
439
|
-
this[
|
|
500
|
+
this[constants_1.kAssetCache].set(response, cacheKey, { ...responseArgs });
|
|
440
501
|
}
|
|
441
502
|
/** Fix response value according to composition */
|
|
442
503
|
const composition = operationResponse?.owner.composition;
|
|
@@ -502,85 +563,20 @@ class HttpHandler {
|
|
|
502
563
|
const documentId = searchParams.get('id');
|
|
503
564
|
const doc = documentId ? document.findDocument(documentId) : document;
|
|
504
565
|
if (!doc) {
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
]);
|
|
566
|
+
context.errors.push(new common_1.BadRequestError({
|
|
567
|
+
message: `Document with given id [${documentId}] does not exists`,
|
|
568
|
+
}));
|
|
569
|
+
return this.sendResponse(context);
|
|
510
570
|
}
|
|
511
571
|
/** Check if response cache exists */
|
|
512
|
-
let responseBody = this[
|
|
572
|
+
let responseBody = this[constants_1.kAssetCache].get(doc, `$schema`);
|
|
513
573
|
/** Create response if response cache does not exists */
|
|
514
574
|
if (!responseBody) {
|
|
515
575
|
const schema = doc.export();
|
|
516
576
|
responseBody = JSON.stringify(schema);
|
|
517
|
-
this[
|
|
577
|
+
this[constants_1.kAssetCache].set(doc, `$schema`, responseBody);
|
|
518
578
|
}
|
|
519
579
|
response.end(responseBody);
|
|
520
580
|
}
|
|
521
|
-
async sendErrorResponse(response, errors) {
|
|
522
|
-
if (response.headersSent) {
|
|
523
|
-
response.end();
|
|
524
|
-
return;
|
|
525
|
-
}
|
|
526
|
-
if (!errors.length)
|
|
527
|
-
errors.push((0, wrap_exception_js_1.wrapException)({ status: response.statusCode || 500 }));
|
|
528
|
-
const { logger } = this.adapter;
|
|
529
|
-
errors.forEach(x => {
|
|
530
|
-
if (x instanceof common_1.OpraException) {
|
|
531
|
-
switch (x.severity) {
|
|
532
|
-
case 'fatal':
|
|
533
|
-
logger.fatal(x);
|
|
534
|
-
break;
|
|
535
|
-
case 'warning':
|
|
536
|
-
logger.warn(x);
|
|
537
|
-
break;
|
|
538
|
-
default:
|
|
539
|
-
logger.error(x);
|
|
540
|
-
}
|
|
541
|
-
}
|
|
542
|
-
else
|
|
543
|
-
logger.fatal(x);
|
|
544
|
-
});
|
|
545
|
-
const wrappedErrors = errors.map(wrap_exception_js_1.wrapException);
|
|
546
|
-
// Sort errors from fatal to info
|
|
547
|
-
wrappedErrors.sort((a, b) => {
|
|
548
|
-
const i = common_1.IssueSeverity.Keys.indexOf(a.severity) - common_1.IssueSeverity.Keys.indexOf(b.severity);
|
|
549
|
-
if (i === 0)
|
|
550
|
-
return b.status - a.status;
|
|
551
|
-
return i;
|
|
552
|
-
});
|
|
553
|
-
let status = response.statusCode || 0;
|
|
554
|
-
if (!status || status < Number(common_1.HttpStatusCode.BAD_REQUEST)) {
|
|
555
|
-
status = wrappedErrors[0].status;
|
|
556
|
-
if (status < Number(common_1.HttpStatusCode.BAD_REQUEST))
|
|
557
|
-
status = common_1.HttpStatusCode.INTERNAL_SERVER_ERROR;
|
|
558
|
-
}
|
|
559
|
-
response.statusCode = status;
|
|
560
|
-
const { document } = this.adapter;
|
|
561
|
-
const dt = document.node.getComplexType('OperationResult');
|
|
562
|
-
let encode = this[constants_js_1.kAssetCache].get(dt, 'encode');
|
|
563
|
-
if (!encode) {
|
|
564
|
-
encode = dt.generateCodec('encode', { ignoreWriteonlyFields: true });
|
|
565
|
-
this[constants_js_1.kAssetCache].set(dt, 'encode', encode);
|
|
566
|
-
}
|
|
567
|
-
const { i18n } = this.adapter;
|
|
568
|
-
const bodyObject = new common_1.OperationResult({
|
|
569
|
-
errors: wrappedErrors.map(x => {
|
|
570
|
-
const o = x.toJSON();
|
|
571
|
-
if (!(process.env.NODE_ENV === 'dev' || process.env.NODE_ENV === 'development'))
|
|
572
|
-
delete o.stack;
|
|
573
|
-
return i18n.deep(o);
|
|
574
|
-
}),
|
|
575
|
-
});
|
|
576
|
-
const body = encode(bodyObject);
|
|
577
|
-
response.setHeader(common_1.HttpHeaderCodes.Content_Type, common_1.MimeTypes.opra_response_json + '; charset=utf-8');
|
|
578
|
-
response.setHeader(common_1.HttpHeaderCodes.Cache_Control, 'no-cache');
|
|
579
|
-
response.setHeader(common_1.HttpHeaderCodes.Pragma, 'no-cache');
|
|
580
|
-
response.setHeader(common_1.HttpHeaderCodes.Expires, '-1');
|
|
581
|
-
response.setHeader(common_1.HttpHeaderCodes.X_Opra_Version, common_1.OpraSchema.SpecVersion);
|
|
582
|
-
response.send(JSON.stringify(body));
|
|
583
|
-
response.end();
|
|
584
|
-
}
|
|
585
581
|
}
|
|
586
582
|
exports.HttpHandler = HttpHandler;
|