@opra/core 0.25.5 → 0.26.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/augmentation/container.augmentation.js +2 -0
- package/cjs/http/adapters/express-adapter.host.js +34 -0
- package/cjs/http/{express-adapter.js → adapters/express-adapter.js} +1 -3
- package/cjs/http/{http-adapter.host.js → adapters/node-http-adapter.host.js} +30 -22
- package/cjs/http/adapters/node-http-adapter.js +14 -0
- package/cjs/http/helpers/json-body-loader.js +29 -0
- package/cjs/http/http-adapter-host.js +678 -0
- package/cjs/index.js +4 -3
- package/cjs/platform-adapter.host.js +74 -45
- package/cjs/{endpoint-context.js → request-context.js} +5 -5
- package/cjs/request.host.js +3 -0
- package/esm/augmentation/container.augmentation.js +1 -0
- package/esm/http/adapters/express-adapter.host.js +30 -0
- package/esm/http/{express-adapter.js → adapters/express-adapter.js} +1 -3
- package/esm/http/{http-adapter.host.js → adapters/node-http-adapter.host.js} +28 -20
- package/esm/http/adapters/node-http-adapter.js +11 -0
- package/esm/http/helpers/json-body-loader.js +24 -0
- package/esm/http/http-adapter-host.js +673 -0
- package/esm/index.js +4 -3
- package/esm/platform-adapter.host.js +75 -46
- package/esm/{endpoint-context.js → request-context.js} +4 -4
- package/esm/request.host.js +3 -0
- package/i18n/en/error.json +1 -2
- package/package.json +3 -3
- package/types/augmentation/collection.augmentation.d.ts +19 -16
- package/types/augmentation/container.augmentation.d.ts +13 -0
- package/types/augmentation/resource.augmentation.d.ts +2 -2
- package/types/augmentation/singleton.augmentation.d.ts +13 -9
- package/types/augmentation/storage.augmentation.d.ts +11 -14
- package/types/http/{express-adapter.d.ts → adapters/express-adapter.d.ts} +3 -3
- package/types/http/adapters/express-adapter.host.d.ts +12 -0
- package/types/http/{http-adapter.d.ts → adapters/node-http-adapter.d.ts} +5 -5
- package/types/http/adapters/node-http-adapter.host.d.ts +19 -0
- package/types/http/helpers/json-body-loader.d.ts +5 -0
- package/types/http/http-adapter-host.d.ts +34 -0
- package/types/index.d.ts +4 -3
- package/types/interfaces/request-handler.interface.d.ts +1 -1
- package/types/platform-adapter.d.ts +2 -2
- package/types/platform-adapter.host.d.ts +18 -14
- package/types/{endpoint-context.d.ts → request-context.d.ts} +3 -3
- package/types/request.d.ts +7 -2
- package/types/request.host.d.ts +5 -2
- package/cjs/http/express-adapter.host.js +0 -24
- package/cjs/http/http-adapter-base.js +0 -138
- package/cjs/http/http-adapter.js +0 -16
- package/cjs/http/request-handlers/entity-request-handler.js +0 -429
- package/cjs/http/request-handlers/parse-batch-request.js +0 -169
- package/cjs/http/request-handlers/request-handler-base.js +0 -37
- package/cjs/http/request-handlers/storage-request-handler.js +0 -139
- package/esm/http/express-adapter.host.js +0 -20
- package/esm/http/http-adapter-base.js +0 -134
- package/esm/http/http-adapter.js +0 -13
- package/esm/http/request-handlers/entity-request-handler.js +0 -424
- package/esm/http/request-handlers/parse-batch-request.js +0 -169
- package/esm/http/request-handlers/request-handler-base.js +0 -33
- package/esm/http/request-handlers/storage-request-handler.js +0 -134
- package/types/http/express-adapter.host.d.ts +0 -11
- package/types/http/http-adapter-base.d.ts +0 -23
- package/types/http/http-adapter.host.d.ts +0 -18
- package/types/http/request-handlers/entity-request-handler.d.ts +0 -24
- package/types/http/request-handlers/parse-batch-request.d.ts +0 -0
- package/types/http/request-handlers/request-handler-base.d.ts +0 -16
- package/types/http/request-handlers/storage-request-handler.d.ts +0 -23
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.StorageRequestHandler = void 0;
|
|
4
|
-
const tslib_1 = require("tslib");
|
|
5
|
-
const promises_1 = tslib_1.__importDefault(require("fs/promises"));
|
|
6
|
-
const os_1 = tslib_1.__importDefault(require("os"));
|
|
7
|
-
const common_1 = require("@opra/common");
|
|
8
|
-
const endpoint_context_js_1 = require("../../endpoint-context.js");
|
|
9
|
-
const request_host_js_1 = require("../../request.host.js");
|
|
10
|
-
const response_host_js_1 = require("../../response.host.js");
|
|
11
|
-
const multipart_helper_js_1 = require("../helpers/multipart-helper.js");
|
|
12
|
-
const request_handler_base_js_1 = require("./request-handler-base.js");
|
|
13
|
-
/**
|
|
14
|
-
* @class StorageRequestHandler
|
|
15
|
-
*/
|
|
16
|
-
class StorageRequestHandler extends request_handler_base_js_1.RequestHandlerBase {
|
|
17
|
-
constructor(adapter, options) {
|
|
18
|
-
super(adapter);
|
|
19
|
-
this.adapter = adapter;
|
|
20
|
-
this._uploadDir = options?.uploadDir || os_1.default.tmpdir();
|
|
21
|
-
}
|
|
22
|
-
async processRequest(executionContext) {
|
|
23
|
-
const { incoming, outgoing } = executionContext.switchToHttp();
|
|
24
|
-
// Parse incoming message and create Request object
|
|
25
|
-
const request = await this.parseRequest(executionContext, incoming);
|
|
26
|
-
if (!request)
|
|
27
|
-
return;
|
|
28
|
-
const response = new response_host_js_1.ResponseHost({ http: outgoing });
|
|
29
|
-
const context = endpoint_context_js_1.EndpointContext.from(executionContext, request, response);
|
|
30
|
-
await this.callEndpoint(context);
|
|
31
|
-
if (response.errors.length) {
|
|
32
|
-
context.errors.push(...response.errors);
|
|
33
|
-
return;
|
|
34
|
-
}
|
|
35
|
-
await this.sendResponse(context);
|
|
36
|
-
}
|
|
37
|
-
async parseRequest(executionContext, incoming) {
|
|
38
|
-
const contentId = incoming.headers['content-id'];
|
|
39
|
-
const p = incoming.parsedUrl.path[0];
|
|
40
|
-
const resource = this.adapter.api.getResource(p.resource);
|
|
41
|
-
try {
|
|
42
|
-
if (!(resource instanceof common_1.Storage))
|
|
43
|
-
return;
|
|
44
|
-
switch (incoming.method) {
|
|
45
|
-
case 'GET': {
|
|
46
|
-
const endpointMeta = await this.assertEndpoint(resource, 'get');
|
|
47
|
-
return new request_host_js_1.RequestHost({
|
|
48
|
-
controller: endpointMeta.controller,
|
|
49
|
-
http: incoming,
|
|
50
|
-
resource,
|
|
51
|
-
endpoint: 'get',
|
|
52
|
-
contentId,
|
|
53
|
-
path: incoming.parsedUrl.path.slice(1).toString().substring(1),
|
|
54
|
-
params: this.parseParameters(incoming.parsedUrl, endpointMeta)
|
|
55
|
-
});
|
|
56
|
-
}
|
|
57
|
-
case 'DELETE': {
|
|
58
|
-
const endpointMeta = await this.assertEndpoint(resource, 'delete');
|
|
59
|
-
return new request_host_js_1.RequestHost({
|
|
60
|
-
controller: endpointMeta.controller,
|
|
61
|
-
http: incoming,
|
|
62
|
-
resource,
|
|
63
|
-
endpoint: 'delete',
|
|
64
|
-
contentId,
|
|
65
|
-
path: incoming.parsedUrl.path.slice(1).toString().substring(1),
|
|
66
|
-
params: this.parseParameters(incoming.parsedUrl, endpointMeta)
|
|
67
|
-
});
|
|
68
|
-
}
|
|
69
|
-
case 'POST': {
|
|
70
|
-
const endpointMeta = await this.assertEndpoint(resource, 'post');
|
|
71
|
-
await promises_1.default.mkdir(this._uploadDir, { recursive: true });
|
|
72
|
-
const multipartIterator = new multipart_helper_js_1.MultipartIterator(incoming, {
|
|
73
|
-
...endpointMeta,
|
|
74
|
-
filename: () => this.adapter.serviceName + '_p' + process.pid +
|
|
75
|
-
't' + String(Date.now()).substring(8) + 'r' + (0, common_1.uid)(12)
|
|
76
|
-
});
|
|
77
|
-
multipartIterator.pause();
|
|
78
|
-
// Add an hook to clean up files after request finished
|
|
79
|
-
executionContext.on('finish', async () => {
|
|
80
|
-
multipartIterator.cancel();
|
|
81
|
-
await multipartIterator.deleteFiles().catch(() => void 0);
|
|
82
|
-
});
|
|
83
|
-
return new request_host_js_1.RequestHost({
|
|
84
|
-
controller: endpointMeta.controller,
|
|
85
|
-
http: incoming,
|
|
86
|
-
resource,
|
|
87
|
-
endpoint: 'post',
|
|
88
|
-
contentId,
|
|
89
|
-
parts: multipartIterator,
|
|
90
|
-
path: incoming.parsedUrl.path.slice(1).toString().substring(1),
|
|
91
|
-
params: this.parseParameters(incoming.parsedUrl, endpointMeta)
|
|
92
|
-
});
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
catch (e) {
|
|
97
|
-
if (e instanceof common_1.OpraException)
|
|
98
|
-
throw e;
|
|
99
|
-
throw new common_1.BadRequestError(e);
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
async callEndpoint(context) {
|
|
103
|
-
const request = context.request;
|
|
104
|
-
const { response } = context;
|
|
105
|
-
// Call endpoint handler method
|
|
106
|
-
let value;
|
|
107
|
-
try {
|
|
108
|
-
value = await request.controller[request.endpoint].call(request.controller, context);
|
|
109
|
-
if (response.value == null)
|
|
110
|
-
response.value = value;
|
|
111
|
-
}
|
|
112
|
-
catch (error) {
|
|
113
|
-
response.errors.push(error);
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
async sendResponse(context) {
|
|
117
|
-
const { response } = context;
|
|
118
|
-
const outgoing = response.switchToHttp();
|
|
119
|
-
outgoing.statusCode = outgoing.statusCode || common_1.HttpStatusCodes.OK;
|
|
120
|
-
if (response.value != null) {
|
|
121
|
-
if (typeof response.value === 'string') {
|
|
122
|
-
if (!outgoing.hasHeader('content-type'))
|
|
123
|
-
outgoing.setHeader('content-type', 'text/plain');
|
|
124
|
-
outgoing.send(response.value);
|
|
125
|
-
}
|
|
126
|
-
else if (Buffer.isBuffer(response.value) || (0, common_1.isReadable)(response.value)) {
|
|
127
|
-
if (!outgoing.hasHeader('content-type'))
|
|
128
|
-
outgoing.setHeader('content-type', 'application/octet-stream');
|
|
129
|
-
outgoing.send(response.value);
|
|
130
|
-
}
|
|
131
|
-
else {
|
|
132
|
-
outgoing.setHeader('content-type', 'application/json; charset=utf-8');
|
|
133
|
-
outgoing.send(JSON.stringify(response.value));
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
outgoing.end();
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
exports.StorageRequestHandler = StorageRequestHandler;
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { OpraURLPath } from '@opra/common';
|
|
2
|
-
import { HttpAdapterBase } from './http-adapter-base.js';
|
|
3
|
-
import { HttpServerRequest } from './http-server-request.js';
|
|
4
|
-
import { HttpServerResponse } from './http-server-response.js';
|
|
5
|
-
export class ExpressAdapterHost extends HttpAdapterBase {
|
|
6
|
-
constructor(app, api, options) {
|
|
7
|
-
super(api, options);
|
|
8
|
-
this._platform = 'express';
|
|
9
|
-
const basePath = new OpraURLPath(options?.basePath);
|
|
10
|
-
app.use(basePath.toString(), (_req, _res) => {
|
|
11
|
-
const req = HttpServerRequest.from(_req);
|
|
12
|
-
const res = HttpServerResponse.from(_res);
|
|
13
|
-
this.handleIncoming(req, res)
|
|
14
|
-
.catch(() => void 0);
|
|
15
|
-
});
|
|
16
|
-
}
|
|
17
|
-
get app() {
|
|
18
|
-
return this._app;
|
|
19
|
-
}
|
|
20
|
-
}
|
|
@@ -1,134 +0,0 @@
|
|
|
1
|
-
import { BadRequestError, HttpHeaderCodes, HttpStatusCodes, InternalServerError, IssueSeverity, OpraException, OpraSchema, wrapException } from '@opra/common';
|
|
2
|
-
import { ExecutionContextHost } from '../execution-context.host.js';
|
|
3
|
-
import { PlatformAdapterHost } from '../platform-adapter.host.js';
|
|
4
|
-
import { EntityRequestHandler } from './request-handlers/entity-request-handler.js';
|
|
5
|
-
import { StorageRequestHandler } from './request-handlers/storage-request-handler.js';
|
|
6
|
-
/**
|
|
7
|
-
*
|
|
8
|
-
* @class HttpAdapterBase
|
|
9
|
-
*/
|
|
10
|
-
export class HttpAdapterBase extends PlatformAdapterHost {
|
|
11
|
-
constructor() {
|
|
12
|
-
super(...arguments);
|
|
13
|
-
this._protocol = 'http';
|
|
14
|
-
this._requestHandlers = [
|
|
15
|
-
new EntityRequestHandler(this),
|
|
16
|
-
new StorageRequestHandler(this)
|
|
17
|
-
];
|
|
18
|
-
}
|
|
19
|
-
/**
|
|
20
|
-
* Main http request handler
|
|
21
|
-
* @param incoming
|
|
22
|
-
* @param outgoing
|
|
23
|
-
* @protected
|
|
24
|
-
*/
|
|
25
|
-
async handleIncoming(incoming, outgoing) {
|
|
26
|
-
const context = new ExecutionContextHost(this.api, this.platform, { http: { incoming, outgoing } });
|
|
27
|
-
try {
|
|
28
|
-
/* istanbul ignore next */
|
|
29
|
-
if (!this._initialized)
|
|
30
|
-
throw new InternalServerError(`${Object.getPrototypeOf(this).constructor.name} has not been initialized yet`);
|
|
31
|
-
outgoing.setHeader(HttpHeaderCodes.X_Opra_Version, OpraSchema.SpecVersion);
|
|
32
|
-
// Expose headers if cors enabled
|
|
33
|
-
if (outgoing.getHeader(HttpHeaderCodes.Access_Control_Allow_Origin)) {
|
|
34
|
-
// Expose X-Opra-* headers
|
|
35
|
-
outgoing.appendHeader(HttpHeaderCodes.Access_Control_Expose_Headers, Object.values(HttpHeaderCodes)
|
|
36
|
-
.filter(k => k.toLowerCase().startsWith('x-opra-')));
|
|
37
|
-
}
|
|
38
|
-
let i = 0;
|
|
39
|
-
let requestProcessed = false;
|
|
40
|
-
const next = async () => {
|
|
41
|
-
while (i < this._interceptors.length) {
|
|
42
|
-
const interceptor = this._interceptors[i++];
|
|
43
|
-
if (interceptor)
|
|
44
|
-
await interceptor(context, next);
|
|
45
|
-
}
|
|
46
|
-
if (!requestProcessed) {
|
|
47
|
-
requestProcessed = true;
|
|
48
|
-
await this.processRequest(context);
|
|
49
|
-
}
|
|
50
|
-
};
|
|
51
|
-
await next();
|
|
52
|
-
}
|
|
53
|
-
catch (error) {
|
|
54
|
-
context.errors.push(wrapException(error));
|
|
55
|
-
}
|
|
56
|
-
// If no response returned to the client we send an error
|
|
57
|
-
if (!outgoing.writableEnded) {
|
|
58
|
-
if (!context.errors.length)
|
|
59
|
-
context.errors.push(new BadRequestError(`Server can not process this request`));
|
|
60
|
-
await this.handleError(context);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
async processRequest(context) {
|
|
64
|
-
try {
|
|
65
|
-
const { incoming, outgoing } = context.switchToHttp();
|
|
66
|
-
if (incoming.method === 'GET' && !incoming.parsedUrl.path.length) {
|
|
67
|
-
outgoing.setHeader('content-type', 'application/json');
|
|
68
|
-
outgoing.end(JSON.stringify(this.api.exportSchema({ webSafe: true })));
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
const { parsedUrl } = incoming;
|
|
72
|
-
if (!parsedUrl.path.length) {
|
|
73
|
-
// Batch
|
|
74
|
-
if (incoming.headers['content-type'] === 'multipart/mixed') {
|
|
75
|
-
// todo
|
|
76
|
-
}
|
|
77
|
-
throw new BadRequestError();
|
|
78
|
-
}
|
|
79
|
-
// Iterate through request handlers until one of them sends response (end outgoing stream)
|
|
80
|
-
for (const requestHandler of this._requestHandlers) {
|
|
81
|
-
await requestHandler.processRequest(context);
|
|
82
|
-
if (outgoing.writableEnded)
|
|
83
|
-
return;
|
|
84
|
-
if (context.errors.length) {
|
|
85
|
-
await this.handleError(context);
|
|
86
|
-
return;
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
finally {
|
|
91
|
-
await context.emitAsync('finish');
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
async handleError(context) {
|
|
95
|
-
const { errors } = context;
|
|
96
|
-
const { outgoing } = context.switchToHttp();
|
|
97
|
-
if (outgoing.headersSent) {
|
|
98
|
-
outgoing.end();
|
|
99
|
-
return;
|
|
100
|
-
}
|
|
101
|
-
errors.forEach(x => {
|
|
102
|
-
// todo. implement a better log mechanism
|
|
103
|
-
if (x instanceof OpraException)
|
|
104
|
-
this._logger.fatal(x);
|
|
105
|
-
else
|
|
106
|
-
this._logger.error(x);
|
|
107
|
-
});
|
|
108
|
-
const wrappedErrors = errors.map(wrapException);
|
|
109
|
-
// Sort errors from fatal to info
|
|
110
|
-
wrappedErrors.sort((a, b) => {
|
|
111
|
-
const i = IssueSeverity.Keys.indexOf(a.severity) - IssueSeverity.Keys.indexOf(b.severity);
|
|
112
|
-
if (i === 0)
|
|
113
|
-
return b.status - a.status;
|
|
114
|
-
return i;
|
|
115
|
-
});
|
|
116
|
-
let status = outgoing.statusCode || 0;
|
|
117
|
-
if (!status || status < Number(HttpStatusCodes.BAD_REQUEST)) {
|
|
118
|
-
status = wrappedErrors[0].status;
|
|
119
|
-
if (status < Number(HttpStatusCodes.BAD_REQUEST))
|
|
120
|
-
status = HttpStatusCodes.INTERNAL_SERVER_ERROR;
|
|
121
|
-
}
|
|
122
|
-
outgoing.statusCode = status;
|
|
123
|
-
const body = {
|
|
124
|
-
errors: wrappedErrors.map(x => this._i18n.deep(x.toJSON()))
|
|
125
|
-
};
|
|
126
|
-
outgoing.setHeader(HttpHeaderCodes.Content_Type, 'application/opra+json; charset=utf-8');
|
|
127
|
-
outgoing.setHeader(HttpHeaderCodes.Cache_Control, 'no-cache');
|
|
128
|
-
outgoing.setHeader(HttpHeaderCodes.Pragma, 'no-cache');
|
|
129
|
-
outgoing.setHeader(HttpHeaderCodes.Expires, '-1');
|
|
130
|
-
outgoing.setHeader(HttpHeaderCodes.X_Opra_Version, OpraSchema.SpecVersion);
|
|
131
|
-
outgoing.send(JSON.stringify(body));
|
|
132
|
-
outgoing.end();
|
|
133
|
-
}
|
|
134
|
-
}
|
package/esm/http/http-adapter.js
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { HttpAdapterHost } from './http-adapter.host.js';
|
|
2
|
-
/**
|
|
3
|
-
* @namespace HttpAdapter
|
|
4
|
-
*/
|
|
5
|
-
export var HttpAdapter;
|
|
6
|
-
(function (HttpAdapter) {
|
|
7
|
-
async function create(api, options) {
|
|
8
|
-
const adapter = new HttpAdapterHost(api, options);
|
|
9
|
-
await adapter.init();
|
|
10
|
-
return adapter;
|
|
11
|
-
}
|
|
12
|
-
HttpAdapter.create = create;
|
|
13
|
-
})(HttpAdapter || (HttpAdapter = {}));
|