@opra/core 0.33.13 → 1.0.0-alpha.2
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 +17 -4
- package/cjs/augmentation/http-controller.augmentation.js +25 -0
- package/cjs/constants.js +5 -0
- package/cjs/execution-context.js +25 -12
- package/cjs/{services → helpers}/logger.js +1 -2
- package/cjs/{services/api-service.js → helpers/service-base.js} +8 -8
- package/cjs/http/express-adapter.js +164 -0
- package/cjs/http/http-adapter.js +27 -0
- package/cjs/http/http-context.js +116 -0
- package/cjs/http/impl/asset-cache.js +21 -0
- package/cjs/http/impl/http-handler.js +575 -0
- package/cjs/http/{http-server-request.js → impl/http-incoming.host.js} +21 -46
- package/cjs/http/{http-server-response.js → impl/http-outgoing.host.js} +7 -26
- package/cjs/http/{helpers/multipart-helper.js → impl/multipart-reader.js} +24 -22
- package/cjs/http/impl/{http-incoming-message.host.js → node-incoming-message.host.js} +13 -54
- package/cjs/http/impl/{http-outgoing-message.host.js → node-outgoing-message.host.js} +11 -14
- package/cjs/http/interfaces/http-incoming.interface.js +25 -0
- package/cjs/http/interfaces/http-outgoing.interface.js +22 -0
- package/cjs/http/interfaces/node-incoming-message.interface.js +63 -0
- package/cjs/http/interfaces/node-outgoing-message.interface.js +15 -0
- package/cjs/http/utils/body-reader.js +215 -0
- package/cjs/http/{helpers → utils}/convert-to-raw-headers.js +1 -2
- package/cjs/http/{helpers → utils}/match-known-fields.js +11 -9
- package/cjs/http/utils/wrap-exception.js +34 -0
- package/cjs/index.js +25 -25
- package/cjs/platform-adapter.js +21 -0
- package/cjs/type-guards.js +23 -0
- package/esm/augmentation/18n.augmentation.js +20 -7
- package/esm/augmentation/http-controller.augmentation.js +23 -0
- package/esm/constants.js +2 -0
- package/esm/execution-context.js +25 -13
- package/esm/{services → helpers}/logger.js +1 -2
- package/esm/{services/api-service.js → helpers/service-base.js} +6 -6
- package/esm/http/express-adapter.js +159 -0
- package/esm/http/http-adapter.js +23 -0
- package/esm/http/http-context.js +111 -0
- package/esm/http/impl/asset-cache.js +17 -0
- package/esm/http/impl/http-handler.js +570 -0
- package/esm/http/{http-server-request.js → impl/http-incoming.host.js} +17 -43
- package/esm/http/{http-server-response.js → impl/http-outgoing.host.js} +6 -26
- package/esm/http/{helpers/multipart-helper.js → impl/multipart-reader.js} +22 -20
- package/esm/http/impl/{http-incoming-message.host.js → node-incoming-message.host.js} +11 -52
- package/esm/http/impl/{http-outgoing-message.host.js → node-outgoing-message.host.js} +9 -12
- package/esm/http/interfaces/http-incoming.interface.js +22 -0
- package/esm/http/interfaces/http-outgoing.interface.js +19 -0
- package/esm/http/interfaces/node-incoming-message.interface.js +60 -0
- package/esm/http/interfaces/node-outgoing-message.interface.js +12 -0
- package/esm/http/utils/body-reader.js +210 -0
- package/esm/http/{helpers → utils}/convert-to-headers.js +1 -1
- package/esm/http/{helpers → utils}/convert-to-raw-headers.js +2 -3
- package/esm/http/{helpers → utils}/match-known-fields.js +11 -9
- package/esm/http/utils/wrap-exception.js +30 -0
- package/esm/index.js +25 -26
- package/esm/platform-adapter.js +19 -1
- package/esm/type-guards.js +16 -0
- package/package.json +22 -10
- package/types/augmentation/18n.augmentation.d.ts +32 -2
- package/types/augmentation/http-controller.augmentation.d.ts +21 -0
- package/types/constants.d.ts +2 -0
- package/types/execution-context.d.ts +24 -26
- package/types/helpers/service-base.d.ts +10 -0
- package/types/http/express-adapter.d.ts +13 -0
- package/types/http/http-adapter.d.ts +27 -0
- package/types/http/http-context.d.ts +44 -0
- package/types/http/impl/asset-cache.d.ts +5 -0
- package/types/http/impl/http-handler.d.ts +73 -0
- package/types/http/impl/http-incoming.host.d.ts +23 -0
- package/types/http/impl/http-outgoing.host.d.ts +17 -0
- package/types/http/{helpers/multipart-helper.d.ts → impl/multipart-reader.d.ts} +8 -6
- package/types/http/impl/{http-incoming-message.host.d.ts → node-incoming-message.host.d.ts} +9 -22
- package/types/http/impl/{http-outgoing-message.host.d.ts → node-outgoing-message.host.d.ts} +11 -27
- package/types/http/{http-server-request.d.ts → interfaces/http-incoming.interface.d.ts} +28 -17
- package/types/http/{http-server-response.d.ts → interfaces/http-outgoing.interface.d.ts} +17 -10
- package/types/http/interfaces/node-incoming-message.interface.d.ts +38 -0
- package/types/http/interfaces/node-outgoing-message.interface.d.ts +29 -0
- package/types/http/utils/body-reader.d.ts +41 -0
- package/types/http/utils/wrap-exception.d.ts +2 -0
- package/types/index.d.ts +24 -26
- package/types/platform-adapter.d.ts +20 -48
- package/types/type-guards.d.ts +8 -0
- package/cjs/augmentation/collection.augmentation.js +0 -2
- package/cjs/augmentation/container.augmentation.js +0 -2
- package/cjs/augmentation/resource.augmentation.js +0 -26
- package/cjs/augmentation/singleton.augmentation.js +0 -2
- package/cjs/augmentation/storage.augmentation.js +0 -2
- package/cjs/execution-context.host.js +0 -46
- package/cjs/http/adapters/express-adapter.host.js +0 -34
- package/cjs/http/adapters/express-adapter.js +0 -14
- package/cjs/http/adapters/node-http-adapter.host.js +0 -70
- package/cjs/http/adapters/node-http-adapter.js +0 -14
- package/cjs/http/helpers/json-body-loader.js +0 -29
- package/cjs/http/helpers/query-parsers.js +0 -16
- package/cjs/http/http-adapter-host.js +0 -715
- package/cjs/interfaces/interceptor.interface.js +0 -2
- package/cjs/interfaces/request-handler.interface.js +0 -2
- package/cjs/platform-adapter.host.js +0 -154
- package/cjs/request-context.js +0 -25
- package/cjs/request.host.js +0 -24
- package/cjs/request.js +0 -2
- package/cjs/response.host.js +0 -22
- package/cjs/response.js +0 -2
- package/esm/augmentation/collection.augmentation.js +0 -1
- package/esm/augmentation/container.augmentation.js +0 -1
- package/esm/augmentation/resource.augmentation.js +0 -24
- package/esm/augmentation/singleton.augmentation.js +0 -1
- package/esm/augmentation/storage.augmentation.js +0 -1
- package/esm/execution-context.host.js +0 -42
- package/esm/http/adapters/express-adapter.host.js +0 -30
- package/esm/http/adapters/express-adapter.js +0 -11
- package/esm/http/adapters/node-http-adapter.host.js +0 -65
- package/esm/http/adapters/node-http-adapter.js +0 -11
- package/esm/http/helpers/json-body-loader.js +0 -24
- package/esm/http/helpers/query-parsers.js +0 -12
- package/esm/http/http-adapter-host.js +0 -710
- package/esm/interfaces/interceptor.interface.js +0 -1
- package/esm/interfaces/request-handler.interface.js +0 -1
- package/esm/platform-adapter.host.js +0 -149
- package/esm/request-context.js +0 -22
- package/esm/request.host.js +0 -20
- package/esm/request.js +0 -1
- package/esm/response.host.js +0 -18
- package/esm/response.js +0 -1
- package/i18n/i18n/en/error.json +0 -21
- package/types/augmentation/collection.augmentation.d.ts +0 -146
- package/types/augmentation/container.augmentation.d.ts +0 -14
- package/types/augmentation/resource.augmentation.d.ts +0 -38
- package/types/augmentation/singleton.augmentation.d.ts +0 -83
- package/types/augmentation/storage.augmentation.d.ts +0 -50
- package/types/execution-context.host.d.ts +0 -25
- package/types/http/adapters/express-adapter.d.ts +0 -15
- package/types/http/adapters/express-adapter.host.d.ts +0 -12
- package/types/http/adapters/node-http-adapter.d.ts +0 -17
- package/types/http/adapters/node-http-adapter.host.d.ts +0 -19
- package/types/http/helpers/json-body-loader.d.ts +0 -5
- package/types/http/helpers/query-parsers.d.ts +0 -1
- package/types/http/http-adapter-host.d.ts +0 -34
- package/types/interfaces/interceptor.interface.d.ts +0 -2
- package/types/interfaces/request-handler.interface.d.ts +0 -4
- package/types/platform-adapter.host.d.ts +0 -43
- package/types/request-context.d.ts +0 -13
- package/types/request.d.ts +0 -14
- package/types/request.host.d.ts +0 -27
- package/types/response.d.ts +0 -22
- package/types/response.host.d.ts +0 -22
- package/types/services/api-service.d.ts +0 -10
- /package/cjs/http/{helpers → utils}/common.js +0 -0
- /package/cjs/http/{helpers → utils}/concat-readable.js +0 -0
- /package/cjs/http/{helpers → utils}/convert-to-headers.js +0 -0
- /package/esm/http/{helpers → utils}/common.js +0 -0
- /package/esm/http/{helpers → utils}/concat-readable.js +0 -0
- /package/types/{services → helpers}/logger.d.ts +0 -0
- /package/types/http/{helpers → utils}/common.d.ts +0 -0
- /package/types/http/{helpers → utils}/concat-readable.d.ts +0 -0
- /package/types/http/{helpers → utils}/convert-to-headers.d.ts +0 -0
- /package/types/http/{helpers → utils}/convert-to-raw-headers.d.ts +0 -0
- /package/types/http/{helpers → utils}/match-known-fields.d.ts +0 -0
|
@@ -4,9 +4,22 @@ const tslib_1 = require("tslib");
|
|
|
4
4
|
const fs_1 = tslib_1.__importDefault(require("fs"));
|
|
5
5
|
const path_1 = tslib_1.__importDefault(require("path"));
|
|
6
6
|
const common_1 = require("@opra/common");
|
|
7
|
+
common_1.I18n.load = async function (options) {
|
|
8
|
+
const opts = {
|
|
9
|
+
...options,
|
|
10
|
+
};
|
|
11
|
+
delete opts.resourceDirs;
|
|
12
|
+
const instance = common_1.I18n.createInstance(opts);
|
|
13
|
+
await instance.init();
|
|
14
|
+
await instance.loadResourceDir(path_1.default.resolve((0, common_1.getStackFileName)(), '../../../i18n'));
|
|
15
|
+
if (options?.resourceDirs)
|
|
16
|
+
for (const dir of options.resourceDirs)
|
|
17
|
+
await instance.loadResourceDir(dir);
|
|
18
|
+
return instance;
|
|
19
|
+
};
|
|
7
20
|
common_1.I18n.prototype.loadResourceBundle = async function (lang, ns, filePath, deep, overwrite) {
|
|
8
21
|
let obj;
|
|
9
|
-
if (
|
|
22
|
+
if (URL.canParse(filePath)) {
|
|
10
23
|
obj = (await fetch(filePath, { headers: { accept: 'application/json' } })).json();
|
|
11
24
|
}
|
|
12
25
|
else {
|
|
@@ -18,17 +31,17 @@ common_1.I18n.prototype.loadResourceBundle = async function (lang, ns, filePath,
|
|
|
18
31
|
common_1.I18n.prototype.loadResourceDir = async function (dirnames, deep, overwrite) {
|
|
19
32
|
for (const dirname of Array.isArray(dirnames) ? dirnames : [dirnames]) {
|
|
20
33
|
/* istanbul ignore next */
|
|
21
|
-
if (!
|
|
34
|
+
if (!fs_1.default.existsSync(dirname))
|
|
22
35
|
continue;
|
|
23
36
|
const languageDirs = fs_1.default.readdirSync(dirname);
|
|
24
37
|
for (const lang of languageDirs) {
|
|
25
38
|
const langDir = path_1.default.join(dirname, lang);
|
|
26
|
-
if (
|
|
39
|
+
if (fs_1.default.statSync(langDir).isDirectory()) {
|
|
27
40
|
const nsDirs = fs_1.default.readdirSync(langDir);
|
|
28
41
|
for (const nsfile of nsDirs) {
|
|
29
42
|
const nsFilePath = path_1.default.join(langDir, nsfile);
|
|
30
43
|
const ext = path_1.default.extname(nsfile);
|
|
31
|
-
if (ext === '.json' &&
|
|
44
|
+
if (ext === '.json' && fs_1.default.statSync(nsFilePath).isFile()) {
|
|
32
45
|
const ns = path_1.default.basename(nsfile, ext);
|
|
33
46
|
await this.loadResourceBundle(lang, ns, nsFilePath, deep, overwrite);
|
|
34
47
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const common_1 = require("@opra/common");
|
|
4
|
+
// @ts-ignore
|
|
5
|
+
const oldInitialize = common_1.HttpController.prototype._initialize;
|
|
6
|
+
// @ts-ignore
|
|
7
|
+
common_1.HttpController.prototype._initialize = function (initArgs) {
|
|
8
|
+
oldInitialize?.call(this, initArgs);
|
|
9
|
+
this.onInit = initArgs.onInit;
|
|
10
|
+
this.onShutdown = initArgs.onShutdown;
|
|
11
|
+
};
|
|
12
|
+
common_1.HttpController.OnInit = function () {
|
|
13
|
+
return (target, propertyKey) => {
|
|
14
|
+
const sourceMetadata = (Reflect.getOwnMetadata(common_1.HTTP_CONTROLLER_METADATA, target.constructor) || {});
|
|
15
|
+
sourceMetadata.onInit = target[propertyKey];
|
|
16
|
+
Reflect.defineMetadata(common_1.HTTP_CONTROLLER_METADATA, target.constructor, sourceMetadata);
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
common_1.HttpController.OnShutdown = function () {
|
|
20
|
+
return (target, propertyKey) => {
|
|
21
|
+
const sourceMetadata = (Reflect.getOwnMetadata(common_1.HTTP_CONTROLLER_METADATA, target.constructor) || {});
|
|
22
|
+
sourceMetadata.onShutdown = target[propertyKey];
|
|
23
|
+
Reflect.defineMetadata(common_1.HTTP_CONTROLLER_METADATA, target.constructor, sourceMetadata);
|
|
24
|
+
};
|
|
25
|
+
};
|
package/cjs/constants.js
ADDED
package/cjs/execution-context.js
CHANGED
|
@@ -1,16 +1,29 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.ExecutionContext = void 0;
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
4
|
+
const strict_typed_events_1 = require("strict-typed-events");
|
|
5
|
+
/**
|
|
6
|
+
* @class ExecutionContext
|
|
7
|
+
*/
|
|
8
|
+
class ExecutionContext extends strict_typed_events_1.AsyncEventEmitter {
|
|
9
|
+
constructor(init) {
|
|
10
|
+
super();
|
|
11
|
+
this.document = init.document;
|
|
12
|
+
this.protocol = init.protocol;
|
|
13
|
+
this.platform = init.platform;
|
|
14
|
+
this.platformArgs = init.platformArgs;
|
|
14
15
|
}
|
|
15
|
-
|
|
16
|
-
|
|
16
|
+
addListener(event, listener) {
|
|
17
|
+
return super.addListener(event, listener);
|
|
18
|
+
}
|
|
19
|
+
removeListener(event, listener) {
|
|
20
|
+
return super.removeListener(event, listener);
|
|
21
|
+
}
|
|
22
|
+
on(event, listener) {
|
|
23
|
+
return super.on(event, listener);
|
|
24
|
+
}
|
|
25
|
+
off(event, listener) {
|
|
26
|
+
return super.off(event, listener);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
exports.ExecutionContext = ExecutionContext;
|
|
@@ -3,8 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.Logger = void 0;
|
|
4
4
|
class Logger {
|
|
5
5
|
constructor(options = {}) {
|
|
6
|
-
this._instance = options.instance ||
|
|
7
|
-
(!(process.env.NODE_ENV || '').includes('test') ? globalThis.console : {});
|
|
6
|
+
this._instance = options.instance || (!(process.env.NODE_ENV || '').includes('test') ? globalThis.console : {});
|
|
8
7
|
}
|
|
9
8
|
info(message, ...optionalParams) {
|
|
10
9
|
(this._instance.info || this._instance.log)?.(message, ...optionalParams);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
4
|
-
class
|
|
3
|
+
exports.ServiceBase = void 0;
|
|
4
|
+
class ServiceBase {
|
|
5
5
|
get context() {
|
|
6
6
|
if (!this._context)
|
|
7
7
|
throw new Error(`No context assigned for ${Object.getPrototypeOf(this).constructor.name}`);
|
|
@@ -18,12 +18,12 @@ class ApiService {
|
|
|
18
18
|
Object.setPrototypeOf(instance, this);
|
|
19
19
|
if (overwriteProperties)
|
|
20
20
|
Object.assign(instance, overwriteProperties);
|
|
21
|
-
if (this[
|
|
22
|
-
this[
|
|
21
|
+
if (this[ServiceBase.extendSymbol])
|
|
22
|
+
this[ServiceBase.extendSymbol](instance);
|
|
23
23
|
return instance;
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
|
-
exports.
|
|
27
|
-
(function (
|
|
28
|
-
|
|
29
|
-
})(
|
|
26
|
+
exports.ServiceBase = ServiceBase;
|
|
27
|
+
(function (ServiceBase) {
|
|
28
|
+
ServiceBase.extendSymbol = Symbol('extend');
|
|
29
|
+
})(ServiceBase || (exports.ServiceBase = ServiceBase = {}));
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ExpressAdapter = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const express_1 = require("express");
|
|
6
|
+
const nodePath = tslib_1.__importStar(require("path"));
|
|
7
|
+
const common_1 = require("@opra/common");
|
|
8
|
+
const constants_js_1 = require("../constants.js");
|
|
9
|
+
const http_adapter_js_1 = require("./http-adapter.js");
|
|
10
|
+
const http_context_js_1 = require("./http-context.js");
|
|
11
|
+
const http_incoming_interface_js_1 = require("./interfaces/http-incoming.interface.js");
|
|
12
|
+
const http_outgoing_interface_js_1 = require("./interfaces/http-outgoing.interface.js");
|
|
13
|
+
class ExpressAdapter extends http_adapter_js_1.HttpAdapter {
|
|
14
|
+
constructor(app, document, options) {
|
|
15
|
+
super(document, options);
|
|
16
|
+
this._controllerInstances = new Map();
|
|
17
|
+
this.app = app;
|
|
18
|
+
if (!(this.document.api instanceof common_1.HttpApi))
|
|
19
|
+
throw new TypeError('document.api must be instance of HttpApi');
|
|
20
|
+
for (const c of this.api.controllers.values())
|
|
21
|
+
this._createControllers(c);
|
|
22
|
+
this._initRouter(options?.basePath);
|
|
23
|
+
}
|
|
24
|
+
get platform() {
|
|
25
|
+
return 'express';
|
|
26
|
+
}
|
|
27
|
+
async close() {
|
|
28
|
+
const processResource = async (resource) => {
|
|
29
|
+
if (resource.controllers.size) {
|
|
30
|
+
const subResources = Array.from(resource.controllers.values());
|
|
31
|
+
subResources.reverse();
|
|
32
|
+
for (const subResource of subResources) {
|
|
33
|
+
await processResource(subResource);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
if (resource.onShutdown) {
|
|
37
|
+
const instance = this._controllerInstances.get(resource) || resource.instance;
|
|
38
|
+
if (instance)
|
|
39
|
+
try {
|
|
40
|
+
await resource.onShutdown.call(instance, resource);
|
|
41
|
+
}
|
|
42
|
+
catch (e) {
|
|
43
|
+
this.logger.error(e);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
for (const c of this.api.controllers.values())
|
|
48
|
+
await processResource(c);
|
|
49
|
+
this._controllerInstances.clear();
|
|
50
|
+
}
|
|
51
|
+
getControllerInstance(controllerPath) {
|
|
52
|
+
const controller = this.api.findController(controllerPath);
|
|
53
|
+
return controller && this._controllerInstances.get(controller);
|
|
54
|
+
}
|
|
55
|
+
_initRouter(basePath) {
|
|
56
|
+
const router = (0, express_1.Router)();
|
|
57
|
+
if (basePath) {
|
|
58
|
+
if (!basePath.startsWith('/'))
|
|
59
|
+
basePath = '/' + basePath;
|
|
60
|
+
if (basePath)
|
|
61
|
+
this.app.use(basePath, router);
|
|
62
|
+
}
|
|
63
|
+
else
|
|
64
|
+
this.app.use(router);
|
|
65
|
+
const createContext = (_req, _res, args) => {
|
|
66
|
+
const request = http_incoming_interface_js_1.HttpIncoming.from(_req);
|
|
67
|
+
const response = http_outgoing_interface_js_1.HttpOutgoing.from(_res);
|
|
68
|
+
const platformArgs = {
|
|
69
|
+
request: _req,
|
|
70
|
+
response: _res,
|
|
71
|
+
};
|
|
72
|
+
return new http_context_js_1.HttpContext({
|
|
73
|
+
adapter: this,
|
|
74
|
+
platform: this.platform,
|
|
75
|
+
platformArgs,
|
|
76
|
+
request,
|
|
77
|
+
response,
|
|
78
|
+
controller: args?.controller,
|
|
79
|
+
controllerInstance: args?.controllerInstance,
|
|
80
|
+
operation: args?.operation,
|
|
81
|
+
operationHandler: args?.operationHandler,
|
|
82
|
+
});
|
|
83
|
+
};
|
|
84
|
+
/** Add an endpoint that returns document schema */
|
|
85
|
+
router.get('*', (_req, _res, next) => {
|
|
86
|
+
if (_req.url.includes('/$schema')) {
|
|
87
|
+
const url = (_req.url.includes('?') ? _req.url.substring(0, _req.url.indexOf('?')) : _req.url).toLowerCase();
|
|
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();
|
|
95
|
+
});
|
|
96
|
+
/** Add operation endpoints */
|
|
97
|
+
if (this.api.controllers.size) {
|
|
98
|
+
const processResource = (controller, currentPath) => {
|
|
99
|
+
currentPath = nodePath.join(currentPath, controller.path);
|
|
100
|
+
for (const operation of controller.operations.values()) {
|
|
101
|
+
const routePath = currentPath + (operation.path || '');
|
|
102
|
+
const controllerInstance = this._controllerInstances.get(controller);
|
|
103
|
+
const operationHandler = controllerInstance[operation.name];
|
|
104
|
+
if (!operationHandler)
|
|
105
|
+
continue;
|
|
106
|
+
/** Define router callback */
|
|
107
|
+
router[operation.method.toLowerCase()](routePath, (_req, _res, _next) => {
|
|
108
|
+
const context = createContext(_req, _res, {
|
|
109
|
+
controller,
|
|
110
|
+
controllerInstance,
|
|
111
|
+
operation,
|
|
112
|
+
operationHandler,
|
|
113
|
+
});
|
|
114
|
+
this[constants_js_1.kHandler]
|
|
115
|
+
.handleRequest(context)
|
|
116
|
+
.then(() => {
|
|
117
|
+
if (!_res.headersSent)
|
|
118
|
+
_next();
|
|
119
|
+
})
|
|
120
|
+
.catch((e) => this.logger.fatal(e));
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
if (controller.controllers.size) {
|
|
124
|
+
for (const child of controller.controllers.values())
|
|
125
|
+
processResource(child, currentPath);
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
for (const c of this.api.controllers.values())
|
|
129
|
+
processResource(c, '/');
|
|
130
|
+
}
|
|
131
|
+
/** Add an endpoint that returns 404 error at last */
|
|
132
|
+
router.use('*', (_req, _res, next) => {
|
|
133
|
+
const res = http_outgoing_interface_js_1.HttpOutgoing.from(_res);
|
|
134
|
+
// const url = new URL(_req.originalUrl, '')
|
|
135
|
+
this[constants_js_1.kHandler]
|
|
136
|
+
.sendErrorResponse(res, [
|
|
137
|
+
new common_1.NotFoundError({
|
|
138
|
+
message: `No endpoint found for [${_req.method}]${_req.baseUrl}`,
|
|
139
|
+
details: {
|
|
140
|
+
path: _req.baseUrl,
|
|
141
|
+
method: _req.method,
|
|
142
|
+
},
|
|
143
|
+
}),
|
|
144
|
+
])
|
|
145
|
+
.catch(next);
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
_createControllers(controller) {
|
|
149
|
+
let instance = controller.instance;
|
|
150
|
+
if (!instance && controller.ctor)
|
|
151
|
+
instance = new controller.ctor();
|
|
152
|
+
if (instance) {
|
|
153
|
+
if (typeof instance.onInit === 'function')
|
|
154
|
+
instance.onInit.call(instance, this);
|
|
155
|
+
this._controllerInstances.set(controller, instance);
|
|
156
|
+
// Initialize sub resources
|
|
157
|
+
for (const r of controller.controllers.values()) {
|
|
158
|
+
this._createControllers(r);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return instance;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
exports.ExpressAdapter = ExpressAdapter;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.HttpAdapter = void 0;
|
|
4
|
+
const common_1 = require("@opra/common");
|
|
5
|
+
const constants_js_1 = require("../constants.js");
|
|
6
|
+
const platform_adapter_js_1 = require("../platform-adapter.js");
|
|
7
|
+
const http_handler_js_1 = require("./impl/http-handler.js");
|
|
8
|
+
/**
|
|
9
|
+
*
|
|
10
|
+
* @class HttpAdapter
|
|
11
|
+
*/
|
|
12
|
+
class HttpAdapter extends platform_adapter_js_1.PlatformAdapter {
|
|
13
|
+
constructor(document, options) {
|
|
14
|
+
super(document, options);
|
|
15
|
+
this.protocol = 'http';
|
|
16
|
+
if (!(document.api instanceof common_1.HttpApi))
|
|
17
|
+
throw new TypeError(`The document does not expose an HTTP Api`);
|
|
18
|
+
this[constants_js_1.kHandler] = new http_handler_js_1.HttpHandler(this);
|
|
19
|
+
this.interceptors = [...(options?.interceptors || [])];
|
|
20
|
+
if (options?.onRequest)
|
|
21
|
+
this.on('request', options.onRequest);
|
|
22
|
+
}
|
|
23
|
+
get api() {
|
|
24
|
+
return this.document.api;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
exports.HttpAdapter = HttpAdapter;
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.HttpContext = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const valgen_1 = require("valgen");
|
|
6
|
+
const type_is_1 = tslib_1.__importDefault(require("@browsery/type-is"));
|
|
7
|
+
const common_1 = require("@opra/common");
|
|
8
|
+
const constants_js_1 = require("../constants.js");
|
|
9
|
+
const execution_context_js_1 = require("../execution-context.js");
|
|
10
|
+
const multipart_reader_js_1 = require("./impl/multipart-reader.js");
|
|
11
|
+
class HttpContext extends execution_context_js_1.ExecutionContext {
|
|
12
|
+
constructor(init) {
|
|
13
|
+
super({ ...init, document: init.adapter.document, protocol: 'http' });
|
|
14
|
+
this.adapter = init.adapter;
|
|
15
|
+
this.protocol = 'http';
|
|
16
|
+
if (init.controller)
|
|
17
|
+
this.controller = init.controller;
|
|
18
|
+
if (init.controllerInstance)
|
|
19
|
+
this.controllerInstance = init.controllerInstance;
|
|
20
|
+
if (init.operation)
|
|
21
|
+
this.operation = init.operation;
|
|
22
|
+
if (init.operationHandler)
|
|
23
|
+
this.operationHandler = init.operationHandler;
|
|
24
|
+
this.request = init.request;
|
|
25
|
+
this.response = init.response;
|
|
26
|
+
this.mediaType = init.mediaType;
|
|
27
|
+
this.cookies = init.cookies || {};
|
|
28
|
+
this.headers = init.headers || {};
|
|
29
|
+
this.pathParams = init.pathParams || {};
|
|
30
|
+
this.queryParams = init.queryParams || {};
|
|
31
|
+
this._body = init.body;
|
|
32
|
+
}
|
|
33
|
+
get isMultipart() {
|
|
34
|
+
return !!this.request.is('multipart');
|
|
35
|
+
}
|
|
36
|
+
async getMultipartReader() {
|
|
37
|
+
if (!this.isMultipart)
|
|
38
|
+
throw new common_1.InternalServerError('Request content is not a multipart content');
|
|
39
|
+
if (this._multipartReader)
|
|
40
|
+
return this._multipartReader;
|
|
41
|
+
const { request, mediaType } = this;
|
|
42
|
+
if (mediaType?.contentType) {
|
|
43
|
+
const arr = Array.isArray(mediaType.contentType) ? mediaType.contentType : [mediaType.contentType];
|
|
44
|
+
const contentType = arr.find(ct => type_is_1.default.is(ct, ['multipart']));
|
|
45
|
+
if (!contentType)
|
|
46
|
+
throw new common_1.NotAcceptableError('This endpoint does not accept multipart requests');
|
|
47
|
+
}
|
|
48
|
+
const reader = new multipart_reader_js_1.MultipartReader(request, {
|
|
49
|
+
maxFields: mediaType?.maxFields,
|
|
50
|
+
maxFieldsSize: mediaType?.maxFieldsSize,
|
|
51
|
+
maxFiles: mediaType?.maxFiles,
|
|
52
|
+
maxFileSize: mediaType?.maxFileSize,
|
|
53
|
+
maxTotalFileSize: mediaType?.maxTotalFileSize,
|
|
54
|
+
minFileSize: mediaType?.minFileSize,
|
|
55
|
+
});
|
|
56
|
+
this._multipartReader = reader;
|
|
57
|
+
return reader;
|
|
58
|
+
}
|
|
59
|
+
async getBody() {
|
|
60
|
+
if (this._body !== undefined)
|
|
61
|
+
return this._body;
|
|
62
|
+
const { request, operation, mediaType } = this;
|
|
63
|
+
if (this.isMultipart) {
|
|
64
|
+
const reader = await this.getMultipartReader();
|
|
65
|
+
/** Retrieve all fields */
|
|
66
|
+
const parts = await reader.getAll();
|
|
67
|
+
/** Filter fields according to configuration */
|
|
68
|
+
this._body = [];
|
|
69
|
+
const multipartFields = mediaType?.multipartFields;
|
|
70
|
+
if (mediaType && multipartFields?.length) {
|
|
71
|
+
const fieldsFound = new Map();
|
|
72
|
+
for (const item of parts) {
|
|
73
|
+
const field = mediaType.findMultipartField(item.fieldName, item.type);
|
|
74
|
+
if (field) {
|
|
75
|
+
fieldsFound.set(field, true);
|
|
76
|
+
this._body.push(item);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
/** Check required fields */
|
|
80
|
+
for (const field of multipartFields) {
|
|
81
|
+
if (field.required && !fieldsFound.get(field))
|
|
82
|
+
throw new common_1.BadRequestError({
|
|
83
|
+
message: `Multipart field (${field.fieldName}) is required`,
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return this._body;
|
|
88
|
+
}
|
|
89
|
+
this._body = await this.request.readBody({ limit: operation.requestBody?.maxContentSize });
|
|
90
|
+
if (this._body != null) {
|
|
91
|
+
// Convert Buffer to string if media is text
|
|
92
|
+
if (Buffer.isBuffer(this._body) && request.is(['json', 'xml', 'txt', 'text']))
|
|
93
|
+
this._body = this._body.toString('utf-8');
|
|
94
|
+
// Transform text to Object if media is JSON
|
|
95
|
+
if (typeof this._body === 'string' && request.is(['json']))
|
|
96
|
+
this._body = JSON.parse(this._body);
|
|
97
|
+
}
|
|
98
|
+
if (mediaType) {
|
|
99
|
+
// Decode/Validate the data object according to data model
|
|
100
|
+
if (this._body && mediaType.type) {
|
|
101
|
+
let decode = this.adapter[constants_js_1.kAssetCache].get(mediaType, 'decode');
|
|
102
|
+
if (!decode) {
|
|
103
|
+
decode =
|
|
104
|
+
mediaType.type?.generateCodec('decode', {
|
|
105
|
+
partial: operation.requestBody?.partial,
|
|
106
|
+
projection: '*',
|
|
107
|
+
}) || valgen_1.vg.isAny();
|
|
108
|
+
this.adapter[constants_js_1.kAssetCache].set(mediaType, 'decode', decode);
|
|
109
|
+
}
|
|
110
|
+
this._body = decode(this._body);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return this._body;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
exports.HttpContext = HttpContext;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AssetCache = void 0;
|
|
4
|
+
class AssetCache {
|
|
5
|
+
constructor() {
|
|
6
|
+
this._items = new WeakMap();
|
|
7
|
+
}
|
|
8
|
+
get(obj, name) {
|
|
9
|
+
const cache = this._items.get(obj);
|
|
10
|
+
return cache && cache[name];
|
|
11
|
+
}
|
|
12
|
+
set(obj, name, asset) {
|
|
13
|
+
let cache = this._items.get(obj);
|
|
14
|
+
if (!cache) {
|
|
15
|
+
cache = {};
|
|
16
|
+
this._items.set(obj, cache);
|
|
17
|
+
}
|
|
18
|
+
cache[name] = asset;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
exports.AssetCache = AssetCache;
|