@opra/nestjs 0.25.4 → 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/factories/opra-api.factory.js +55 -47
- package/cjs/services/nest-explorer.js +12 -9
- package/cjs/services/opra-api-loader.js +1 -1
- package/esm/factories/opra-api.factory.js +56 -48
- package/esm/services/nest-explorer.js +12 -9
- package/esm/services/opra-api-loader.js +1 -1
- package/package.json +4 -4
- package/types/interfaces/opra-module-options.interface.d.ts +2 -2
- package/types/services/nest-explorer.d.ts +4 -2
|
@@ -25,67 +25,75 @@ let OpraApiFactory = exports.OpraApiFactory = class OpraApiFactory {
|
|
|
25
25
|
const info = { title: '', version: '', ...moduleOptions.info };
|
|
26
26
|
info.title = info.title || 'Untitled service';
|
|
27
27
|
info.version = info.version || '1';
|
|
28
|
-
const
|
|
28
|
+
const root = {
|
|
29
|
+
resources: []
|
|
30
|
+
};
|
|
29
31
|
const apiSchema = {
|
|
30
32
|
version: common_2.OpraSchema.SpecVersion,
|
|
31
33
|
info,
|
|
32
34
|
types: [],
|
|
33
|
-
|
|
35
|
+
root
|
|
34
36
|
};
|
|
35
37
|
/*
|
|
36
38
|
* Walk through modules and add Resource instances to the api schema
|
|
37
39
|
*/
|
|
38
|
-
|
|
39
|
-
for (const wrapper of wrappers) {
|
|
40
|
+
this.explorerService.exploreResources(rootModule, (wrapper, modulePath) => {
|
|
40
41
|
const instance = wrapper.instance;
|
|
41
42
|
const ctor = instance.constructor;
|
|
42
43
|
const metadata = Reflect.getMetadata(common_2.RESOURCE_METADATA, ctor);
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
for (const methodName of Object.keys(sourceDef.operations)) {
|
|
59
|
-
const endpointFunction = instance[methodName];
|
|
60
|
-
const nestHandlerName = methodName + '_nestjs';
|
|
61
|
-
// Skip patch if controller do not have function for endpoint or already patched before
|
|
62
|
-
if (typeof endpointFunction !== 'function')
|
|
63
|
-
continue;
|
|
64
|
-
// NestJs requires calling handler function in different order than Opra.
|
|
65
|
-
// In NestJS, handler functions must be called with these parameters (req, res, next)
|
|
66
|
-
// In Opra, handler functions must be called with these parameters (context)
|
|
67
|
-
// To work handlers properly we create new handlers that will work as a proxy to wrap parameters
|
|
68
|
-
// Opra request (context) -> Nest (req, res, next, context: QueryRequestContext) -> Opra response (context)
|
|
69
|
-
const paramArgsMetadata = Reflect.getMetadata(constants_js_1.PARAM_ARGS_METADATA, instance.constructor, methodName);
|
|
70
|
-
const hasParamsArgs = !!paramArgsMetadata;
|
|
71
|
-
const patchedFn = instance[nestHandlerName] = function (...args) {
|
|
72
|
-
if (hasParamsArgs)
|
|
73
|
-
return endpointFunction.apply(this, args);
|
|
74
|
-
return endpointFunction.call(this, args[3]);
|
|
75
|
-
};
|
|
76
|
-
if (paramArgsMetadata)
|
|
77
|
-
Reflect.defineMetadata(constants_js_1.PARAM_ARGS_METADATA, paramArgsMetadata, instance.constructor, nestHandlerName);
|
|
78
|
-
// Copy all metadata from old Function to new one
|
|
79
|
-
Reflect.getMetadataKeys(endpointFunction).forEach(k => {
|
|
80
|
-
const m = Reflect.getMetadata(k, endpointFunction);
|
|
81
|
-
Reflect.defineMetadata(k, m, patchedFn);
|
|
82
|
-
});
|
|
83
|
-
this._createContextCallback(instance, wrapper, rootModule, methodName, isRequestScoped, contextType);
|
|
44
|
+
let node = root;
|
|
45
|
+
modulePath.forEach(m => {
|
|
46
|
+
const mt = Reflect.getMetadata(common_2.RESOURCE_METADATA, m._metatype);
|
|
47
|
+
if (mt) {
|
|
48
|
+
let n = node.resources.find(x => x.controller === m.instance);
|
|
49
|
+
if (!n) {
|
|
50
|
+
n = {
|
|
51
|
+
...mt,
|
|
52
|
+
kind: 'Container',
|
|
53
|
+
resources: [...(mt.resources || [])],
|
|
54
|
+
controller: m.instance
|
|
55
|
+
};
|
|
56
|
+
node.resources.push(n);
|
|
57
|
+
}
|
|
58
|
+
node = n;
|
|
84
59
|
}
|
|
60
|
+
});
|
|
61
|
+
// Do not add Modules decorated with @Container
|
|
62
|
+
if (wrapper.metatype !== wrapper.host?.metatype)
|
|
63
|
+
node.resources.push(instance);
|
|
64
|
+
/* Wrap operation and action functions */
|
|
65
|
+
const isRequestScoped = !wrapper.isDependencyTreeStatic();
|
|
66
|
+
const methodNames = [...Object.keys(metadata.operations || []), ...Object.keys(metadata.actions || [])];
|
|
67
|
+
for (const methodName of methodNames) {
|
|
68
|
+
const endpointFunction = instance[methodName];
|
|
69
|
+
const nestHandlerName = methodName + '_nestjs';
|
|
70
|
+
// Skip patch if controller do not have function for endpoint or already patched before
|
|
71
|
+
if (typeof endpointFunction !== 'function')
|
|
72
|
+
continue;
|
|
73
|
+
// NestJs requires calling handler function in different order than Opra.
|
|
74
|
+
// In NestJS, handler functions must be called with these parameters (req, res, next)
|
|
75
|
+
// In Opra, handler functions must be called with these parameters (context)
|
|
76
|
+
// To work handlers properly we create new handlers that will work as a proxy to wrap parameters
|
|
77
|
+
// Opra request (context) -> Nest (req, res, next, context: QueryRequestContext) -> Opra response (context)
|
|
78
|
+
const paramArgsMetadata = Reflect.getMetadata(constants_js_1.PARAM_ARGS_METADATA, instance.constructor, methodName);
|
|
79
|
+
const hasParamsArgs = !!paramArgsMetadata;
|
|
80
|
+
const patchedFn = instance[nestHandlerName] = function (...args) {
|
|
81
|
+
if (hasParamsArgs)
|
|
82
|
+
return endpointFunction.apply(this, args);
|
|
83
|
+
return endpointFunction.call(this, args[3]);
|
|
84
|
+
};
|
|
85
|
+
if (paramArgsMetadata)
|
|
86
|
+
Reflect.defineMetadata(constants_js_1.PARAM_ARGS_METADATA, paramArgsMetadata, instance.constructor, nestHandlerName);
|
|
87
|
+
// Copy all metadata from old Function to new one
|
|
88
|
+
Reflect.getMetadataKeys(endpointFunction).forEach(k => {
|
|
89
|
+
const m = Reflect.getMetadata(k, endpointFunction);
|
|
90
|
+
Reflect.defineMetadata(k, m, patchedFn);
|
|
91
|
+
});
|
|
92
|
+
this._createContextCallback(instance, wrapper, rootModule, methodName, isRequestScoped, contextType);
|
|
85
93
|
}
|
|
86
|
-
}
|
|
94
|
+
});
|
|
87
95
|
// Create api document
|
|
88
|
-
return common_2.
|
|
96
|
+
return common_2.ApiDocumentFactory.createDocument(apiSchema);
|
|
89
97
|
}
|
|
90
98
|
_createHandler(callback) {
|
|
91
99
|
return function (ctx) {
|
|
@@ -3,32 +3,35 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.NestExplorer = void 0;
|
|
4
4
|
const common_1 = require("@opra/common");
|
|
5
5
|
class NestExplorer {
|
|
6
|
-
exploreProviders(rootModule,
|
|
6
|
+
exploreProviders(rootModule, callback) {
|
|
7
7
|
const modules = new Set();
|
|
8
|
-
const
|
|
8
|
+
const tree = [];
|
|
9
9
|
const scanModules = (m) => {
|
|
10
10
|
if (modules.has(m))
|
|
11
11
|
return;
|
|
12
12
|
modules.add(m);
|
|
13
|
+
tree.push(m);
|
|
13
14
|
for (const mm of m.imports.values()) {
|
|
14
15
|
scanModules(mm);
|
|
15
16
|
}
|
|
16
17
|
for (const wrapper of m.providers.values()) {
|
|
17
|
-
|
|
18
|
-
wrappers.add(wrapper);
|
|
18
|
+
callback(wrapper, tree);
|
|
19
19
|
if (wrapper.host)
|
|
20
20
|
scanModules(wrapper.host);
|
|
21
21
|
}
|
|
22
|
+
tree.pop();
|
|
22
23
|
};
|
|
23
24
|
scanModules(rootModule);
|
|
24
|
-
return Array.from(wrappers.values());
|
|
25
25
|
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
exploreResources(rootModule, callback) {
|
|
27
|
+
this.exploreProviders(rootModule, (wrapper, tree) => {
|
|
28
|
+
if (wrapper.instance
|
|
29
29
|
&& typeof wrapper.instance === 'object'
|
|
30
30
|
&& wrapper.instance.constructor
|
|
31
|
-
&& common_1.OpraSchema.
|
|
31
|
+
&& common_1.OpraSchema.isResource(Reflect.getMetadata(common_1.RESOURCE_METADATA, wrapper.instance.constructor))) {
|
|
32
|
+
callback(wrapper, tree);
|
|
33
|
+
}
|
|
34
|
+
return false;
|
|
32
35
|
});
|
|
33
36
|
}
|
|
34
37
|
}
|
|
@@ -30,7 +30,7 @@ class OpraApiLoader {
|
|
|
30
30
|
};
|
|
31
31
|
try {
|
|
32
32
|
const apiDocument = await this.opraFactory.generateService(rootModule, options, 'http');
|
|
33
|
-
if (!apiDocument.
|
|
33
|
+
if (!Object.keys(apiDocument.root).length) {
|
|
34
34
|
this.logger.warn(`No Sources found (${name})`);
|
|
35
35
|
return;
|
|
36
36
|
}
|
|
@@ -6,7 +6,7 @@ import { ExternalContextCreator } from '@nestjs/core/helpers/external-context-cr
|
|
|
6
6
|
import { Injector } from '@nestjs/core/injector/injector';
|
|
7
7
|
import { InternalCoreModule } from '@nestjs/core/injector/internal-core-module';
|
|
8
8
|
import { REQUEST_CONTEXT_ID } from '@nestjs/core/router/request/request-constants';
|
|
9
|
-
import {
|
|
9
|
+
import { ApiDocumentFactory, OpraSchema, RESOURCE_METADATA } from '@opra/common';
|
|
10
10
|
import { PARAM_ARGS_METADATA } from '../constants.js';
|
|
11
11
|
import { HandlerParamType } from '../enums/handler-paramtype.enum.js';
|
|
12
12
|
import { NestExplorer } from '../services/nest-explorer.js';
|
|
@@ -22,67 +22,75 @@ export let OpraApiFactory = class OpraApiFactory {
|
|
|
22
22
|
const info = { title: '', version: '', ...moduleOptions.info };
|
|
23
23
|
info.title = info.title || 'Untitled service';
|
|
24
24
|
info.version = info.version || '1';
|
|
25
|
-
const
|
|
25
|
+
const root = {
|
|
26
|
+
resources: []
|
|
27
|
+
};
|
|
26
28
|
const apiSchema = {
|
|
27
29
|
version: OpraSchema.SpecVersion,
|
|
28
30
|
info,
|
|
29
31
|
types: [],
|
|
30
|
-
|
|
32
|
+
root
|
|
31
33
|
};
|
|
32
34
|
/*
|
|
33
35
|
* Walk through modules and add Resource instances to the api schema
|
|
34
36
|
*/
|
|
35
|
-
|
|
36
|
-
for (const wrapper of wrappers) {
|
|
37
|
+
this.explorerService.exploreResources(rootModule, (wrapper, modulePath) => {
|
|
37
38
|
const instance = wrapper.instance;
|
|
38
39
|
const ctor = instance.constructor;
|
|
39
40
|
const metadata = Reflect.getMetadata(RESOURCE_METADATA, ctor);
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
for (const methodName of Object.keys(sourceDef.operations)) {
|
|
56
|
-
const endpointFunction = instance[methodName];
|
|
57
|
-
const nestHandlerName = methodName + '_nestjs';
|
|
58
|
-
// Skip patch if controller do not have function for endpoint or already patched before
|
|
59
|
-
if (typeof endpointFunction !== 'function')
|
|
60
|
-
continue;
|
|
61
|
-
// NestJs requires calling handler function in different order than Opra.
|
|
62
|
-
// In NestJS, handler functions must be called with these parameters (req, res, next)
|
|
63
|
-
// In Opra, handler functions must be called with these parameters (context)
|
|
64
|
-
// To work handlers properly we create new handlers that will work as a proxy to wrap parameters
|
|
65
|
-
// Opra request (context) -> Nest (req, res, next, context: QueryRequestContext) -> Opra response (context)
|
|
66
|
-
const paramArgsMetadata = Reflect.getMetadata(PARAM_ARGS_METADATA, instance.constructor, methodName);
|
|
67
|
-
const hasParamsArgs = !!paramArgsMetadata;
|
|
68
|
-
const patchedFn = instance[nestHandlerName] = function (...args) {
|
|
69
|
-
if (hasParamsArgs)
|
|
70
|
-
return endpointFunction.apply(this, args);
|
|
71
|
-
return endpointFunction.call(this, args[3]);
|
|
72
|
-
};
|
|
73
|
-
if (paramArgsMetadata)
|
|
74
|
-
Reflect.defineMetadata(PARAM_ARGS_METADATA, paramArgsMetadata, instance.constructor, nestHandlerName);
|
|
75
|
-
// Copy all metadata from old Function to new one
|
|
76
|
-
Reflect.getMetadataKeys(endpointFunction).forEach(k => {
|
|
77
|
-
const m = Reflect.getMetadata(k, endpointFunction);
|
|
78
|
-
Reflect.defineMetadata(k, m, patchedFn);
|
|
79
|
-
});
|
|
80
|
-
this._createContextCallback(instance, wrapper, rootModule, methodName, isRequestScoped, contextType);
|
|
41
|
+
let node = root;
|
|
42
|
+
modulePath.forEach(m => {
|
|
43
|
+
const mt = Reflect.getMetadata(RESOURCE_METADATA, m._metatype);
|
|
44
|
+
if (mt) {
|
|
45
|
+
let n = node.resources.find(x => x.controller === m.instance);
|
|
46
|
+
if (!n) {
|
|
47
|
+
n = {
|
|
48
|
+
...mt,
|
|
49
|
+
kind: 'Container',
|
|
50
|
+
resources: [...(mt.resources || [])],
|
|
51
|
+
controller: m.instance
|
|
52
|
+
};
|
|
53
|
+
node.resources.push(n);
|
|
54
|
+
}
|
|
55
|
+
node = n;
|
|
81
56
|
}
|
|
57
|
+
});
|
|
58
|
+
// Do not add Modules decorated with @Container
|
|
59
|
+
if (wrapper.metatype !== wrapper.host?.metatype)
|
|
60
|
+
node.resources.push(instance);
|
|
61
|
+
/* Wrap operation and action functions */
|
|
62
|
+
const isRequestScoped = !wrapper.isDependencyTreeStatic();
|
|
63
|
+
const methodNames = [...Object.keys(metadata.operations || []), ...Object.keys(metadata.actions || [])];
|
|
64
|
+
for (const methodName of methodNames) {
|
|
65
|
+
const endpointFunction = instance[methodName];
|
|
66
|
+
const nestHandlerName = methodName + '_nestjs';
|
|
67
|
+
// Skip patch if controller do not have function for endpoint or already patched before
|
|
68
|
+
if (typeof endpointFunction !== 'function')
|
|
69
|
+
continue;
|
|
70
|
+
// NestJs requires calling handler function in different order than Opra.
|
|
71
|
+
// In NestJS, handler functions must be called with these parameters (req, res, next)
|
|
72
|
+
// In Opra, handler functions must be called with these parameters (context)
|
|
73
|
+
// To work handlers properly we create new handlers that will work as a proxy to wrap parameters
|
|
74
|
+
// Opra request (context) -> Nest (req, res, next, context: QueryRequestContext) -> Opra response (context)
|
|
75
|
+
const paramArgsMetadata = Reflect.getMetadata(PARAM_ARGS_METADATA, instance.constructor, methodName);
|
|
76
|
+
const hasParamsArgs = !!paramArgsMetadata;
|
|
77
|
+
const patchedFn = instance[nestHandlerName] = function (...args) {
|
|
78
|
+
if (hasParamsArgs)
|
|
79
|
+
return endpointFunction.apply(this, args);
|
|
80
|
+
return endpointFunction.call(this, args[3]);
|
|
81
|
+
};
|
|
82
|
+
if (paramArgsMetadata)
|
|
83
|
+
Reflect.defineMetadata(PARAM_ARGS_METADATA, paramArgsMetadata, instance.constructor, nestHandlerName);
|
|
84
|
+
// Copy all metadata from old Function to new one
|
|
85
|
+
Reflect.getMetadataKeys(endpointFunction).forEach(k => {
|
|
86
|
+
const m = Reflect.getMetadata(k, endpointFunction);
|
|
87
|
+
Reflect.defineMetadata(k, m, patchedFn);
|
|
88
|
+
});
|
|
89
|
+
this._createContextCallback(instance, wrapper, rootModule, methodName, isRequestScoped, contextType);
|
|
82
90
|
}
|
|
83
|
-
}
|
|
91
|
+
});
|
|
84
92
|
// Create api document
|
|
85
|
-
return
|
|
93
|
+
return ApiDocumentFactory.createDocument(apiSchema);
|
|
86
94
|
}
|
|
87
95
|
_createHandler(callback) {
|
|
88
96
|
return function (ctx) {
|
|
@@ -1,31 +1,34 @@
|
|
|
1
1
|
import { OpraSchema, RESOURCE_METADATA } from '@opra/common';
|
|
2
2
|
export class NestExplorer {
|
|
3
|
-
exploreProviders(rootModule,
|
|
3
|
+
exploreProviders(rootModule, callback) {
|
|
4
4
|
const modules = new Set();
|
|
5
|
-
const
|
|
5
|
+
const tree = [];
|
|
6
6
|
const scanModules = (m) => {
|
|
7
7
|
if (modules.has(m))
|
|
8
8
|
return;
|
|
9
9
|
modules.add(m);
|
|
10
|
+
tree.push(m);
|
|
10
11
|
for (const mm of m.imports.values()) {
|
|
11
12
|
scanModules(mm);
|
|
12
13
|
}
|
|
13
14
|
for (const wrapper of m.providers.values()) {
|
|
14
|
-
|
|
15
|
-
wrappers.add(wrapper);
|
|
15
|
+
callback(wrapper, tree);
|
|
16
16
|
if (wrapper.host)
|
|
17
17
|
scanModules(wrapper.host);
|
|
18
18
|
}
|
|
19
|
+
tree.pop();
|
|
19
20
|
};
|
|
20
21
|
scanModules(rootModule);
|
|
21
|
-
return Array.from(wrappers.values());
|
|
22
22
|
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
exploreResources(rootModule, callback) {
|
|
24
|
+
this.exploreProviders(rootModule, (wrapper, tree) => {
|
|
25
|
+
if (wrapper.instance
|
|
26
26
|
&& typeof wrapper.instance === 'object'
|
|
27
27
|
&& wrapper.instance.constructor
|
|
28
|
-
&& OpraSchema.
|
|
28
|
+
&& OpraSchema.isResource(Reflect.getMetadata(RESOURCE_METADATA, wrapper.instance.constructor))) {
|
|
29
|
+
callback(wrapper, tree);
|
|
30
|
+
}
|
|
31
|
+
return false;
|
|
29
32
|
});
|
|
30
33
|
}
|
|
31
34
|
}
|
|
@@ -27,7 +27,7 @@ export class OpraApiLoader {
|
|
|
27
27
|
};
|
|
28
28
|
try {
|
|
29
29
|
const apiDocument = await this.opraFactory.generateService(rootModule, options, 'http');
|
|
30
|
-
if (!apiDocument.
|
|
30
|
+
if (!Object.keys(apiDocument.root).length) {
|
|
31
31
|
this.logger.warn(`No Sources found (${name})`);
|
|
32
32
|
return;
|
|
33
33
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opra/nestjs",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.26.0",
|
|
4
4
|
"description": "Opra NestJS module",
|
|
5
5
|
"author": "Panates",
|
|
6
6
|
"license": "MIT",
|
|
@@ -25,8 +25,8 @@
|
|
|
25
25
|
"clean:cover": "rimraf ../../coverage/nestjs"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@opra/common": "^0.
|
|
29
|
-
"@opra/core": "^0.
|
|
28
|
+
"@opra/common": "^0.26.0",
|
|
29
|
+
"@opra/core": "^0.26.0",
|
|
30
30
|
"fast-tokenizer": "^1.2.2",
|
|
31
31
|
"lodash.head": "^4.0.1",
|
|
32
32
|
"lodash.identity": "^3.0.0",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"@nestjs/testing": "^10.2.4",
|
|
41
41
|
"@types/lodash": "^4.14.197",
|
|
42
42
|
"filedirname": "^2.7.0",
|
|
43
|
-
"ts-gems": "^2.
|
|
43
|
+
"ts-gems": "^2.5.0"
|
|
44
44
|
},
|
|
45
45
|
"type": "module",
|
|
46
46
|
"module": "./esm/index.js",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ModuleMetadata, Type } from '@nestjs/common';
|
|
2
2
|
import { OpraSchema } from '@opra/common';
|
|
3
|
-
import {
|
|
4
|
-
export type OpraModuleOptions =
|
|
3
|
+
import { NodeHttpAdapter } from '@opra/core';
|
|
4
|
+
export type OpraModuleOptions = NodeHttpAdapter.Options & {
|
|
5
5
|
id?: any;
|
|
6
6
|
info?: OpraSchema.DocumentInfo;
|
|
7
7
|
/**
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { InstanceWrapper } from '@nestjs/core/injector/instance-wrapper';
|
|
2
2
|
import { Module } from '@nestjs/core/injector/module';
|
|
3
|
+
type WrapperCallback = (wrapper: InstanceWrapper, tree: Module[]) => void;
|
|
3
4
|
export declare class NestExplorer {
|
|
4
|
-
exploreProviders(rootModule: Module,
|
|
5
|
-
|
|
5
|
+
exploreProviders(rootModule: Module, callback: WrapperCallback): void;
|
|
6
|
+
exploreResources(rootModule: Module, callback: WrapperCallback): void;
|
|
6
7
|
}
|
|
8
|
+
export {};
|