@opra/nestjs 0.13.0 → 0.15.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/decorators/context.decorator.js +2 -12
- package/cjs/enums/handler-paramtype.enum.js +2 -3
- package/cjs/factories/opra-api.factory.js +56 -65
- package/cjs/factories/params.factory.js +4 -6
- package/cjs/services/nest-explorer.js +1 -1
- package/cjs/services/opra-api-loader.js +6 -2
- package/cjs/utils/param.utils.js +2 -0
- package/esm/decorators/context.decorator.d.ts +1 -11
- package/esm/decorators/context.decorator.js +1 -11
- package/esm/enums/handler-paramtype.enum.d.ts +3 -4
- package/esm/enums/handler-paramtype.enum.js +2 -3
- package/esm/factories/opra-api.factory.d.ts +2 -2
- package/esm/factories/opra-api.factory.js +57 -66
- package/esm/factories/params.factory.js +4 -6
- package/esm/services/nest-explorer.js +2 -2
- package/esm/services/opra-api-loader.d.ts +2 -2
- package/esm/services/opra-api-loader.js +6 -2
- package/esm/utils/param.utils.js +2 -0
- package/package.json +10 -10
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.ApiDoc = exports.Context = void 0;
|
|
4
4
|
const handler_paramtype_enum_js_1 = require("../enums/handler-paramtype.enum.js");
|
|
5
5
|
const param_utils_js_1 = require("../utils/param.utils.js");
|
|
6
6
|
/**
|
|
@@ -8,18 +8,8 @@ const param_utils_js_1 = require("../utils/param.utils.js");
|
|
|
8
8
|
* parameter with the value of `RequestContext`.
|
|
9
9
|
*/
|
|
10
10
|
exports.Context = (0, param_utils_js_1.createOpraParamDecorator)(handler_paramtype_enum_js_1.HandlerParamType.CONTEXT);
|
|
11
|
-
/**
|
|
12
|
-
* Handler method parameter decorator. Populates the decorated
|
|
13
|
-
* parameter with the value of `QueryResponse`.
|
|
14
|
-
*/
|
|
15
|
-
exports.Response = (0, param_utils_js_1.createOpraParamDecorator)(handler_paramtype_enum_js_1.HandlerParamType.RESPONSE);
|
|
16
|
-
/**
|
|
17
|
-
* Handler method parameter decorator. Populates the decorated
|
|
18
|
-
* parameter with the value of `OpraService`.
|
|
19
|
-
*/
|
|
20
|
-
exports.Service = (0, param_utils_js_1.createOpraParamDecorator)(handler_paramtype_enum_js_1.HandlerParamType.SERVICE);
|
|
21
11
|
/**
|
|
22
12
|
* Handler method parameter decorator. Populates the decorated
|
|
23
13
|
* parameter with the value of `OpraService`.
|
|
24
14
|
*/
|
|
25
|
-
exports.
|
|
15
|
+
exports.ApiDoc = (0, param_utils_js_1.createOpraParamDecorator)(handler_paramtype_enum_js_1.HandlerParamType.API);
|
|
@@ -4,8 +4,7 @@ exports.HandlerParamType = void 0;
|
|
|
4
4
|
var HandlerParamType;
|
|
5
5
|
(function (HandlerParamType) {
|
|
6
6
|
HandlerParamType[HandlerParamType["CONTEXT"] = 0] = "CONTEXT";
|
|
7
|
-
HandlerParamType[HandlerParamType["
|
|
8
|
-
HandlerParamType[HandlerParamType["
|
|
7
|
+
HandlerParamType[HandlerParamType["API"] = 1] = "API";
|
|
8
|
+
HandlerParamType[HandlerParamType["REQUEST"] = 2] = "REQUEST";
|
|
9
9
|
HandlerParamType[HandlerParamType["RESPONSE"] = 3] = "RESPONSE";
|
|
10
|
-
HandlerParamType[HandlerParamType["USER_CONTEXT"] = 4] = "USER_CONTEXT";
|
|
11
10
|
})(HandlerParamType = exports.HandlerParamType || (exports.HandlerParamType = {}));
|
|
@@ -17,7 +17,6 @@ const nest_explorer_js_1 = require("../services/nest-explorer.js");
|
|
|
17
17
|
const function_utils_js_1 = require("../utils/function.utils.js");
|
|
18
18
|
const params_factory_js_1 = require("./params.factory.js");
|
|
19
19
|
const noOpFunction = () => void 0;
|
|
20
|
-
const METHOD_PATCHED = 'ServiceFactory.method-patched';
|
|
21
20
|
let OpraApiFactory = class OpraApiFactory {
|
|
22
21
|
constructor() {
|
|
23
22
|
this.paramsFactory = new params_factory_js_1.OpraParamsFactory();
|
|
@@ -28,15 +27,27 @@ let OpraApiFactory = class OpraApiFactory {
|
|
|
28
27
|
info.title = info.title || 'Untitled service';
|
|
29
28
|
info.version = info.version || '1';
|
|
30
29
|
const resources = [];
|
|
31
|
-
const
|
|
30
|
+
const apiSchema = {
|
|
31
|
+
version: common_2.OpraSchema.SpecVersion,
|
|
32
32
|
info,
|
|
33
33
|
types: [],
|
|
34
34
|
resources,
|
|
35
35
|
};
|
|
36
|
+
/*
|
|
37
|
+
* Walk through modules and add Resource instances to the api schema
|
|
38
|
+
*/
|
|
36
39
|
const wrappers = this.explorerService.exploreResourceWrappers(rootModule);
|
|
37
40
|
for (const wrapper of wrappers) {
|
|
38
41
|
const instance = wrapper.instance;
|
|
39
|
-
const
|
|
42
|
+
const ctor = instance.constructor;
|
|
43
|
+
const metadata = Reflect.getMetadata(common_2.METADATA_KEY, ctor);
|
|
44
|
+
if (common_2.OpraSchema.isResource(metadata))
|
|
45
|
+
resources.push(instance);
|
|
46
|
+
}
|
|
47
|
+
for (const wrapper of wrappers) {
|
|
48
|
+
const instance = wrapper.instance;
|
|
49
|
+
const ctor = instance.constructor;
|
|
50
|
+
const resourceDef = Reflect.getMetadata(common_2.METADATA_KEY, ctor);
|
|
40
51
|
/* istanbul ignore next */
|
|
41
52
|
if (!resourceDef)
|
|
42
53
|
continue;
|
|
@@ -44,88 +55,68 @@ let OpraApiFactory = class OpraApiFactory {
|
|
|
44
55
|
/* Wrap resolver functions */
|
|
45
56
|
const prototype = Object.getPrototypeOf(instance);
|
|
46
57
|
const isRequestScoped = !wrapper.isDependencyTreeStatic();
|
|
47
|
-
const methodsToWrap =
|
|
48
|
-
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
case 'http':
|
|
64
|
-
const http = ctx.switchToHttp();
|
|
65
|
-
const req = http.getRequest().getInstance();
|
|
66
|
-
const res = http.getResponse().getInstance();
|
|
67
|
-
return preCallback(req, res, noOpFunction, ctx);
|
|
68
|
-
default:
|
|
69
|
-
throw new Error(`"${ctx.type}" context type is not implemented yet`);
|
|
70
|
-
}
|
|
71
|
-
};
|
|
72
|
-
Object.defineProperty(newPreFn, 'name', {
|
|
73
|
-
configurable: false,
|
|
74
|
-
writable: false,
|
|
75
|
-
enumerable: true,
|
|
76
|
-
value: 'pre_' + methodName
|
|
77
|
-
});
|
|
78
|
-
if (!Reflect.hasMetadata(METHOD_PATCHED, fn)) {
|
|
79
|
-
const hasParamsArgs = Reflect.hasMetadata(constants_js_1.PARAM_ARGS_METADATA, instance.constructor, methodName);
|
|
80
|
-
const patchedFn = prototype[methodName] = function (...args) {
|
|
58
|
+
// const methodsToWrap = OpraSchema.isCollectionResource(resourceDef) ? collectionMethods : [];
|
|
59
|
+
if ((common_2.OpraSchema.isCollection(resourceDef) || common_2.OpraSchema.isSingleton(resourceDef)) && resourceDef.operations) {
|
|
60
|
+
for (const opr of Object.values(resourceDef.operations)) {
|
|
61
|
+
const { handlerName } = opr;
|
|
62
|
+
const fn = prototype[handlerName];
|
|
63
|
+
if (typeof fn !== 'function')
|
|
64
|
+
continue;
|
|
65
|
+
// NestJs requires calling handler function in different order than Opra.
|
|
66
|
+
// In NestJS, handler functions must be called with these parameters (req, res, next)
|
|
67
|
+
// In Opra, handler functions must be called with these parameters (context)
|
|
68
|
+
// To work handlers properly we create new handlers that will work as a proxy to wrap parameters
|
|
69
|
+
// Opra request (context) -> Nest (req, res, next, context: QueryRequestContext) -> Opra response (context)
|
|
70
|
+
const nestHandlerName = handlerName + '::nestjs';
|
|
71
|
+
const paramArgsMetadata = Reflect.getMetadata(constants_js_1.PARAM_ARGS_METADATA, instance.constructor, handlerName);
|
|
72
|
+
const hasParamsArgs = !!paramArgsMetadata;
|
|
73
|
+
const patchedFn = prototype[nestHandlerName] = function (...args) {
|
|
81
74
|
if (hasParamsArgs)
|
|
82
75
|
return fn.apply(this, args);
|
|
83
76
|
return fn.call(this, args[3]);
|
|
84
77
|
};
|
|
85
|
-
|
|
78
|
+
if (paramArgsMetadata)
|
|
79
|
+
Reflect.defineMetadata(constants_js_1.PARAM_ARGS_METADATA, paramArgsMetadata, instance.constructor, nestHandlerName);
|
|
80
|
+
// Copy all metadata from old Function to new one
|
|
86
81
|
Reflect.getMetadataKeys(fn).forEach(k => {
|
|
87
|
-
const
|
|
88
|
-
Reflect.defineMetadata(k,
|
|
82
|
+
const m = Reflect.getMetadata(k, fn);
|
|
83
|
+
Reflect.defineMetadata(k, m, patchedFn);
|
|
84
|
+
});
|
|
85
|
+
const callback = this._createContextCallback(instance, prototype, wrapper, rootModule, nestHandlerName, isRequestScoped, undefined, contextType);
|
|
86
|
+
opr.handler = function (ctx) {
|
|
87
|
+
switch (ctx.protocol) {
|
|
88
|
+
case 'http':
|
|
89
|
+
const httpContext = ctx.switchToHttp();
|
|
90
|
+
return callback(httpContext.request, httpContext.response, noOpFunction, ctx);
|
|
91
|
+
default:
|
|
92
|
+
throw new Error(`"${ctx.protocol}" context type is not implemented yet`);
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
Object.defineProperty(opr.handler, 'name', {
|
|
96
|
+
configurable: false,
|
|
97
|
+
writable: false,
|
|
98
|
+
enumerable: true,
|
|
99
|
+
value: handlerName
|
|
89
100
|
});
|
|
90
101
|
}
|
|
91
|
-
const callback = this._createContextCallback(instance, prototype, wrapper, rootModule, methodName, isRequestScoped, undefined, contextType, false);
|
|
92
|
-
const newFn = instance[methodName] = function (ctx) {
|
|
93
|
-
switch (ctx.type) {
|
|
94
|
-
case 'http':
|
|
95
|
-
const http = ctx.switchToHttp();
|
|
96
|
-
const req = http.getRequest().getInstance();
|
|
97
|
-
const res = http.getResponse().getInstance();
|
|
98
|
-
return callback(req, res, noOpFunction, ctx);
|
|
99
|
-
default:
|
|
100
|
-
throw new Error(`"${ctx.type}" context type is not implemented yet`);
|
|
101
|
-
}
|
|
102
|
-
};
|
|
103
|
-
Object.defineProperty(newFn, 'name', {
|
|
104
|
-
configurable: false,
|
|
105
|
-
writable: false,
|
|
106
|
-
enumerable: true,
|
|
107
|
-
value: methodName
|
|
108
|
-
});
|
|
109
102
|
}
|
|
110
103
|
}
|
|
111
|
-
|
|
104
|
+
// Create api document
|
|
105
|
+
return common_2.DocumentFactory.createDocument(apiSchema);
|
|
112
106
|
}
|
|
113
|
-
_createContextCallback(instance, prototype, wrapper, moduleRef, methodName, isRequestScoped, transform = lodash_identity_1.default, contextType,
|
|
107
|
+
_createContextCallback(instance, prototype, wrapper, moduleRef, methodName, isRequestScoped, transform = lodash_identity_1.default, contextType, options) {
|
|
114
108
|
const paramsFactory = this.paramsFactory;
|
|
115
|
-
const options = !forPre ?
|
|
116
|
-
{ guards: false, interceptors: false, filters: false } : undefined;
|
|
117
|
-
const fnName = forPre ? 'pre_' + methodName : methodName;
|
|
118
109
|
if (isRequestScoped) {
|
|
119
110
|
return async (...args) => {
|
|
120
111
|
const opraContext = paramsFactory.exchangeKeyForValue(handler_paramtype_enum_js_1.HandlerParamType.CONTEXT, undefined, args);
|
|
121
112
|
const contextId = this.getContextId(opraContext);
|
|
122
113
|
this.registerContextProvider(opraContext, contextId);
|
|
123
114
|
const contextInstance = await this.injector.loadPerContext(instance, moduleRef, moduleRef.providers, contextId);
|
|
124
|
-
const callback = this.externalContextCreator.create(contextInstance, transform(contextInstance[
|
|
115
|
+
const callback = this.externalContextCreator.create(contextInstance, transform(contextInstance[methodName]), methodName, constants_js_1.PARAM_ARGS_METADATA, paramsFactory, contextId, wrapper.id, options, opraContext.protocol);
|
|
125
116
|
return callback(...args);
|
|
126
117
|
};
|
|
127
118
|
}
|
|
128
|
-
return this.externalContextCreator.create(instance, prototype[
|
|
119
|
+
return this.externalContextCreator.create(instance, prototype[methodName], methodName, constants_js_1.PARAM_ARGS_METADATA, paramsFactory, undefined, undefined, options, contextType);
|
|
129
120
|
}
|
|
130
121
|
// noinspection JSMethodCanBeStatic
|
|
131
122
|
getContextId(gqlContext) {
|
|
@@ -11,14 +11,12 @@ class OpraParamsFactory {
|
|
|
11
11
|
switch (type) {
|
|
12
12
|
case handler_paramtype_enum_js_1.HandlerParamType.CONTEXT:
|
|
13
13
|
return args[3];
|
|
14
|
-
case handler_paramtype_enum_js_1.HandlerParamType.
|
|
15
|
-
return args[3].
|
|
16
|
-
case handler_paramtype_enum_js_1.HandlerParamType.
|
|
17
|
-
return args[3].
|
|
14
|
+
case handler_paramtype_enum_js_1.HandlerParamType.API:
|
|
15
|
+
return args[3].api;
|
|
16
|
+
case handler_paramtype_enum_js_1.HandlerParamType.REQUEST:
|
|
17
|
+
return args[3].request;
|
|
18
18
|
case handler_paramtype_enum_js_1.HandlerParamType.RESPONSE:
|
|
19
19
|
return args[3].response;
|
|
20
|
-
case handler_paramtype_enum_js_1.HandlerParamType.USER_CONTEXT:
|
|
21
|
-
return args[3].executionContext.userContext;
|
|
22
20
|
default:
|
|
23
21
|
return null;
|
|
24
22
|
}
|
|
@@ -28,7 +28,7 @@ class NestExplorer {
|
|
|
28
28
|
return !!(wrapper.instance
|
|
29
29
|
&& typeof wrapper.instance === 'object'
|
|
30
30
|
&& wrapper.instance.constructor
|
|
31
|
-
&& Reflect.
|
|
31
|
+
&& common_1.OpraSchema.isResource(Reflect.getMetadata(common_1.METADATA_KEY, wrapper.instance.constructor)));
|
|
32
32
|
});
|
|
33
33
|
}
|
|
34
34
|
}
|
|
@@ -52,8 +52,12 @@ class OpraApiLoader {
|
|
|
52
52
|
if (!httpAdapter)
|
|
53
53
|
return;
|
|
54
54
|
const app = httpAdapter.getInstance();
|
|
55
|
-
|
|
56
|
-
|
|
55
|
+
let logger = moduleOptions.logger;
|
|
56
|
+
if (!logger) {
|
|
57
|
+
logger = new common_1.Logger(service.info.title);
|
|
58
|
+
logger.fatal = logger.error.bind(logger);
|
|
59
|
+
}
|
|
60
|
+
return core_2.OpraExpressAdapter.create(app, service, {
|
|
57
61
|
logger,
|
|
58
62
|
...moduleOptions
|
|
59
63
|
});
|
package/cjs/utils/param.utils.js
CHANGED
|
@@ -15,6 +15,8 @@ function assignMetadata(args, paramType, index, data, ...pipes) {
|
|
|
15
15
|
}
|
|
16
16
|
function createOpraParamDecorator(paramType) {
|
|
17
17
|
return (target, key, index) => {
|
|
18
|
+
if (!key)
|
|
19
|
+
return;
|
|
18
20
|
const args = Reflect.getMetadata(constants_js_1.PARAM_ARGS_METADATA, target.constructor, key) || {};
|
|
19
21
|
Reflect.defineMetadata(constants_js_1.PARAM_ARGS_METADATA, assignMetadata(args, paramType, index), target.constructor, key);
|
|
20
22
|
};
|
|
@@ -3,18 +3,8 @@
|
|
|
3
3
|
* parameter with the value of `RequestContext`.
|
|
4
4
|
*/
|
|
5
5
|
export declare const Context: ParameterDecorator;
|
|
6
|
-
/**
|
|
7
|
-
* Handler method parameter decorator. Populates the decorated
|
|
8
|
-
* parameter with the value of `QueryResponse`.
|
|
9
|
-
*/
|
|
10
|
-
export declare const Response: ParameterDecorator;
|
|
11
|
-
/**
|
|
12
|
-
* Handler method parameter decorator. Populates the decorated
|
|
13
|
-
* parameter with the value of `OpraService`.
|
|
14
|
-
*/
|
|
15
|
-
export declare const Service: ParameterDecorator;
|
|
16
6
|
/**
|
|
17
7
|
* Handler method parameter decorator. Populates the decorated
|
|
18
8
|
* parameter with the value of `OpraService`.
|
|
19
9
|
*/
|
|
20
|
-
export declare const
|
|
10
|
+
export declare const ApiDoc: ParameterDecorator;
|
|
@@ -5,18 +5,8 @@ import { createOpraParamDecorator } from '../utils/param.utils.js';
|
|
|
5
5
|
* parameter with the value of `RequestContext`.
|
|
6
6
|
*/
|
|
7
7
|
export const Context = createOpraParamDecorator(HandlerParamType.CONTEXT);
|
|
8
|
-
/**
|
|
9
|
-
* Handler method parameter decorator. Populates the decorated
|
|
10
|
-
* parameter with the value of `QueryResponse`.
|
|
11
|
-
*/
|
|
12
|
-
export const Response = createOpraParamDecorator(HandlerParamType.RESPONSE);
|
|
13
|
-
/**
|
|
14
|
-
* Handler method parameter decorator. Populates the decorated
|
|
15
|
-
* parameter with the value of `OpraService`.
|
|
16
|
-
*/
|
|
17
|
-
export const Service = createOpraParamDecorator(HandlerParamType.SERVICE);
|
|
18
8
|
/**
|
|
19
9
|
* Handler method parameter decorator. Populates the decorated
|
|
20
10
|
* parameter with the value of `OpraService`.
|
|
21
11
|
*/
|
|
22
|
-
export const
|
|
12
|
+
export const ApiDoc = createOpraParamDecorator(HandlerParamType.API);
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
export var HandlerParamType;
|
|
2
2
|
(function (HandlerParamType) {
|
|
3
3
|
HandlerParamType[HandlerParamType["CONTEXT"] = 0] = "CONTEXT";
|
|
4
|
-
HandlerParamType[HandlerParamType["
|
|
5
|
-
HandlerParamType[HandlerParamType["
|
|
4
|
+
HandlerParamType[HandlerParamType["API"] = 1] = "API";
|
|
5
|
+
HandlerParamType[HandlerParamType["REQUEST"] = 2] = "REQUEST";
|
|
6
6
|
HandlerParamType[HandlerParamType["RESPONSE"] = 3] = "RESPONSE";
|
|
7
|
-
HandlerParamType[HandlerParamType["USER_CONTEXT"] = 4] = "USER_CONTEXT";
|
|
8
7
|
})(HandlerParamType || (HandlerParamType = {}));
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ContextType } from '@nestjs/common';
|
|
2
2
|
import { Module } from '@nestjs/core/injector/module.js';
|
|
3
|
-
import {
|
|
3
|
+
import { ApiDocument } from '@opra/common';
|
|
4
4
|
import { OpraModuleOptions } from '../interfaces/opra-module-options.interface.js';
|
|
5
5
|
export declare class OpraApiFactory {
|
|
6
6
|
private readonly paramsFactory;
|
|
@@ -8,7 +8,7 @@ export declare class OpraApiFactory {
|
|
|
8
8
|
private readonly modulesContainer;
|
|
9
9
|
private readonly externalContextCreator;
|
|
10
10
|
private readonly explorerService;
|
|
11
|
-
generateService(rootModule: Module, moduleOptions: OpraModuleOptions, contextType: ContextType): Promise<
|
|
11
|
+
generateService(rootModule: Module, moduleOptions: OpraModuleOptions, contextType: ContextType): Promise<ApiDocument>;
|
|
12
12
|
private _createContextCallback;
|
|
13
13
|
private getContextId;
|
|
14
14
|
private registerContextProvider;
|
|
@@ -7,14 +7,13 @@ import { ExternalContextCreator } from '@nestjs/core/helpers/external-context-cr
|
|
|
7
7
|
import { Injector } from '@nestjs/core/injector/injector';
|
|
8
8
|
import { InternalCoreModule } from '@nestjs/core/injector/internal-core-module';
|
|
9
9
|
import { REQUEST_CONTEXT_ID } from '@nestjs/core/router/request/request-constants';
|
|
10
|
-
import {
|
|
10
|
+
import { DocumentFactory, METADATA_KEY, OpraSchema } from '@opra/common';
|
|
11
11
|
import { PARAM_ARGS_METADATA } from '../constants.js';
|
|
12
12
|
import { HandlerParamType } from '../enums/handler-paramtype.enum.js';
|
|
13
13
|
import { NestExplorer } from '../services/nest-explorer.js';
|
|
14
14
|
import { getNumberOfArguments } from '../utils/function.utils.js';
|
|
15
15
|
import { OpraParamsFactory } from './params.factory.js';
|
|
16
16
|
const noOpFunction = () => void 0;
|
|
17
|
-
const METHOD_PATCHED = 'ServiceFactory.method-patched';
|
|
18
17
|
let OpraApiFactory = class OpraApiFactory {
|
|
19
18
|
constructor() {
|
|
20
19
|
this.paramsFactory = new OpraParamsFactory();
|
|
@@ -25,15 +24,27 @@ let OpraApiFactory = class OpraApiFactory {
|
|
|
25
24
|
info.title = info.title || 'Untitled service';
|
|
26
25
|
info.version = info.version || '1';
|
|
27
26
|
const resources = [];
|
|
28
|
-
const
|
|
27
|
+
const apiSchema = {
|
|
28
|
+
version: OpraSchema.SpecVersion,
|
|
29
29
|
info,
|
|
30
30
|
types: [],
|
|
31
31
|
resources,
|
|
32
32
|
};
|
|
33
|
+
/*
|
|
34
|
+
* Walk through modules and add Resource instances to the api schema
|
|
35
|
+
*/
|
|
33
36
|
const wrappers = this.explorerService.exploreResourceWrappers(rootModule);
|
|
34
37
|
for (const wrapper of wrappers) {
|
|
35
38
|
const instance = wrapper.instance;
|
|
36
|
-
const
|
|
39
|
+
const ctor = instance.constructor;
|
|
40
|
+
const metadata = Reflect.getMetadata(METADATA_KEY, ctor);
|
|
41
|
+
if (OpraSchema.isResource(metadata))
|
|
42
|
+
resources.push(instance);
|
|
43
|
+
}
|
|
44
|
+
for (const wrapper of wrappers) {
|
|
45
|
+
const instance = wrapper.instance;
|
|
46
|
+
const ctor = instance.constructor;
|
|
47
|
+
const resourceDef = Reflect.getMetadata(METADATA_KEY, ctor);
|
|
37
48
|
/* istanbul ignore next */
|
|
38
49
|
if (!resourceDef)
|
|
39
50
|
continue;
|
|
@@ -41,88 +52,68 @@ let OpraApiFactory = class OpraApiFactory {
|
|
|
41
52
|
/* Wrap resolver functions */
|
|
42
53
|
const prototype = Object.getPrototypeOf(instance);
|
|
43
54
|
const isRequestScoped = !wrapper.isDependencyTreeStatic();
|
|
44
|
-
const methodsToWrap = OpraSchema.isCollectionResource(resourceDef) ? collectionMethods : [];
|
|
45
|
-
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
case 'http':
|
|
61
|
-
const http = ctx.switchToHttp();
|
|
62
|
-
const req = http.getRequest().getInstance();
|
|
63
|
-
const res = http.getResponse().getInstance();
|
|
64
|
-
return preCallback(req, res, noOpFunction, ctx);
|
|
65
|
-
default:
|
|
66
|
-
throw new Error(`"${ctx.type}" context type is not implemented yet`);
|
|
67
|
-
}
|
|
68
|
-
};
|
|
69
|
-
Object.defineProperty(newPreFn, 'name', {
|
|
70
|
-
configurable: false,
|
|
71
|
-
writable: false,
|
|
72
|
-
enumerable: true,
|
|
73
|
-
value: 'pre_' + methodName
|
|
74
|
-
});
|
|
75
|
-
if (!Reflect.hasMetadata(METHOD_PATCHED, fn)) {
|
|
76
|
-
const hasParamsArgs = Reflect.hasMetadata(PARAM_ARGS_METADATA, instance.constructor, methodName);
|
|
77
|
-
const patchedFn = prototype[methodName] = function (...args) {
|
|
55
|
+
// const methodsToWrap = OpraSchema.isCollectionResource(resourceDef) ? collectionMethods : [];
|
|
56
|
+
if ((OpraSchema.isCollection(resourceDef) || OpraSchema.isSingleton(resourceDef)) && resourceDef.operations) {
|
|
57
|
+
for (const opr of Object.values(resourceDef.operations)) {
|
|
58
|
+
const { handlerName } = opr;
|
|
59
|
+
const fn = prototype[handlerName];
|
|
60
|
+
if (typeof fn !== 'function')
|
|
61
|
+
continue;
|
|
62
|
+
// NestJs requires calling handler function in different order than Opra.
|
|
63
|
+
// In NestJS, handler functions must be called with these parameters (req, res, next)
|
|
64
|
+
// In Opra, handler functions must be called with these parameters (context)
|
|
65
|
+
// To work handlers properly we create new handlers that will work as a proxy to wrap parameters
|
|
66
|
+
// Opra request (context) -> Nest (req, res, next, context: QueryRequestContext) -> Opra response (context)
|
|
67
|
+
const nestHandlerName = handlerName + '::nestjs';
|
|
68
|
+
const paramArgsMetadata = Reflect.getMetadata(PARAM_ARGS_METADATA, instance.constructor, handlerName);
|
|
69
|
+
const hasParamsArgs = !!paramArgsMetadata;
|
|
70
|
+
const patchedFn = prototype[nestHandlerName] = function (...args) {
|
|
78
71
|
if (hasParamsArgs)
|
|
79
72
|
return fn.apply(this, args);
|
|
80
73
|
return fn.call(this, args[3]);
|
|
81
74
|
};
|
|
82
|
-
|
|
75
|
+
if (paramArgsMetadata)
|
|
76
|
+
Reflect.defineMetadata(PARAM_ARGS_METADATA, paramArgsMetadata, instance.constructor, nestHandlerName);
|
|
77
|
+
// Copy all metadata from old Function to new one
|
|
83
78
|
Reflect.getMetadataKeys(fn).forEach(k => {
|
|
84
|
-
const
|
|
85
|
-
Reflect.defineMetadata(k,
|
|
79
|
+
const m = Reflect.getMetadata(k, fn);
|
|
80
|
+
Reflect.defineMetadata(k, m, patchedFn);
|
|
81
|
+
});
|
|
82
|
+
const callback = this._createContextCallback(instance, prototype, wrapper, rootModule, nestHandlerName, isRequestScoped, undefined, contextType);
|
|
83
|
+
opr.handler = function (ctx) {
|
|
84
|
+
switch (ctx.protocol) {
|
|
85
|
+
case 'http':
|
|
86
|
+
const httpContext = ctx.switchToHttp();
|
|
87
|
+
return callback(httpContext.request, httpContext.response, noOpFunction, ctx);
|
|
88
|
+
default:
|
|
89
|
+
throw new Error(`"${ctx.protocol}" context type is not implemented yet`);
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
Object.defineProperty(opr.handler, 'name', {
|
|
93
|
+
configurable: false,
|
|
94
|
+
writable: false,
|
|
95
|
+
enumerable: true,
|
|
96
|
+
value: handlerName
|
|
86
97
|
});
|
|
87
98
|
}
|
|
88
|
-
const callback = this._createContextCallback(instance, prototype, wrapper, rootModule, methodName, isRequestScoped, undefined, contextType, false);
|
|
89
|
-
const newFn = instance[methodName] = function (ctx) {
|
|
90
|
-
switch (ctx.type) {
|
|
91
|
-
case 'http':
|
|
92
|
-
const http = ctx.switchToHttp();
|
|
93
|
-
const req = http.getRequest().getInstance();
|
|
94
|
-
const res = http.getResponse().getInstance();
|
|
95
|
-
return callback(req, res, noOpFunction, ctx);
|
|
96
|
-
default:
|
|
97
|
-
throw new Error(`"${ctx.type}" context type is not implemented yet`);
|
|
98
|
-
}
|
|
99
|
-
};
|
|
100
|
-
Object.defineProperty(newFn, 'name', {
|
|
101
|
-
configurable: false,
|
|
102
|
-
writable: false,
|
|
103
|
-
enumerable: true,
|
|
104
|
-
value: methodName
|
|
105
|
-
});
|
|
106
99
|
}
|
|
107
100
|
}
|
|
108
|
-
|
|
101
|
+
// Create api document
|
|
102
|
+
return DocumentFactory.createDocument(apiSchema);
|
|
109
103
|
}
|
|
110
|
-
_createContextCallback(instance, prototype, wrapper, moduleRef, methodName, isRequestScoped, transform = identity, contextType,
|
|
104
|
+
_createContextCallback(instance, prototype, wrapper, moduleRef, methodName, isRequestScoped, transform = identity, contextType, options) {
|
|
111
105
|
const paramsFactory = this.paramsFactory;
|
|
112
|
-
const options = !forPre ?
|
|
113
|
-
{ guards: false, interceptors: false, filters: false } : undefined;
|
|
114
|
-
const fnName = forPre ? 'pre_' + methodName : methodName;
|
|
115
106
|
if (isRequestScoped) {
|
|
116
107
|
return async (...args) => {
|
|
117
108
|
const opraContext = paramsFactory.exchangeKeyForValue(HandlerParamType.CONTEXT, undefined, args);
|
|
118
109
|
const contextId = this.getContextId(opraContext);
|
|
119
110
|
this.registerContextProvider(opraContext, contextId);
|
|
120
111
|
const contextInstance = await this.injector.loadPerContext(instance, moduleRef, moduleRef.providers, contextId);
|
|
121
|
-
const callback = this.externalContextCreator.create(contextInstance, transform(contextInstance[
|
|
112
|
+
const callback = this.externalContextCreator.create(contextInstance, transform(contextInstance[methodName]), methodName, PARAM_ARGS_METADATA, paramsFactory, contextId, wrapper.id, options, opraContext.protocol);
|
|
122
113
|
return callback(...args);
|
|
123
114
|
};
|
|
124
115
|
}
|
|
125
|
-
return this.externalContextCreator.create(instance, prototype[
|
|
116
|
+
return this.externalContextCreator.create(instance, prototype[methodName], methodName, PARAM_ARGS_METADATA, paramsFactory, undefined, undefined, options, contextType);
|
|
126
117
|
}
|
|
127
118
|
// noinspection JSMethodCanBeStatic
|
|
128
119
|
getContextId(gqlContext) {
|
|
@@ -8,14 +8,12 @@ export class OpraParamsFactory {
|
|
|
8
8
|
switch (type) {
|
|
9
9
|
case HandlerParamType.CONTEXT:
|
|
10
10
|
return args[3];
|
|
11
|
-
case HandlerParamType.
|
|
12
|
-
return args[3].
|
|
13
|
-
case HandlerParamType.
|
|
14
|
-
return args[3].
|
|
11
|
+
case HandlerParamType.API:
|
|
12
|
+
return args[3].api;
|
|
13
|
+
case HandlerParamType.REQUEST:
|
|
14
|
+
return args[3].request;
|
|
15
15
|
case HandlerParamType.RESPONSE:
|
|
16
16
|
return args[3].response;
|
|
17
|
-
case HandlerParamType.USER_CONTEXT:
|
|
18
|
-
return args[3].executionContext.userContext;
|
|
19
17
|
default:
|
|
20
18
|
return null;
|
|
21
19
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { METADATA_KEY, OpraSchema } from '@opra/common';
|
|
2
2
|
export class NestExplorer {
|
|
3
3
|
exploreProviders(rootModule, predicate) {
|
|
4
4
|
const modules = new Set();
|
|
@@ -25,7 +25,7 @@ export class NestExplorer {
|
|
|
25
25
|
return !!(wrapper.instance
|
|
26
26
|
&& typeof wrapper.instance === 'object'
|
|
27
27
|
&& wrapper.instance.constructor
|
|
28
|
-
&& Reflect.
|
|
28
|
+
&& OpraSchema.isResource(Reflect.getMetadata(METADATA_KEY, wrapper.instance.constructor)));
|
|
29
29
|
});
|
|
30
30
|
}
|
|
31
31
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ApplicationConfig, HttpAdapterHost } from '@nestjs/core';
|
|
2
2
|
import { Module } from '@nestjs/core/injector/module.js';
|
|
3
|
-
import {
|
|
3
|
+
import { ApiDocument } from '@opra/common';
|
|
4
4
|
import { OpraExpressAdapter } from '@opra/core';
|
|
5
5
|
import { OpraApiFactory } from '../factories/opra-api.factory.js';
|
|
6
6
|
import { OpraModuleOptions } from '../interfaces/opra-module-options.interface.js';
|
|
@@ -13,5 +13,5 @@ export declare class OpraApiLoader {
|
|
|
13
13
|
protected readonly opraModuleOptions: OpraModuleOptions;
|
|
14
14
|
initialize(rootModule: Module): Promise<void>;
|
|
15
15
|
stop(): Promise<void>;
|
|
16
|
-
protected registerExpress(service:
|
|
16
|
+
protected registerExpress(service: ApiDocument, moduleOptions: OpraModuleOptions): Promise<OpraExpressAdapter | undefined>;
|
|
17
17
|
}
|
|
@@ -49,8 +49,12 @@ export class OpraApiLoader {
|
|
|
49
49
|
if (!httpAdapter)
|
|
50
50
|
return;
|
|
51
51
|
const app = httpAdapter.getInstance();
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
let logger = moduleOptions.logger;
|
|
53
|
+
if (!logger) {
|
|
54
|
+
logger = new Logger(service.info.title);
|
|
55
|
+
logger.fatal = logger.error.bind(logger);
|
|
56
|
+
}
|
|
57
|
+
return OpraExpressAdapter.create(app, service, {
|
|
54
58
|
logger,
|
|
55
59
|
...moduleOptions
|
|
56
60
|
});
|
package/esm/utils/param.utils.js
CHANGED
|
@@ -12,6 +12,8 @@ function assignMetadata(args, paramType, index, data, ...pipes) {
|
|
|
12
12
|
}
|
|
13
13
|
export function createOpraParamDecorator(paramType) {
|
|
14
14
|
return (target, key, index) => {
|
|
15
|
+
if (!key)
|
|
16
|
+
return;
|
|
15
17
|
const args = Reflect.getMetadata(PARAM_ARGS_METADATA, target.constructor, key) || {};
|
|
16
18
|
Reflect.defineMetadata(PARAM_ARGS_METADATA, assignMetadata(args, paramType, index), target.constructor, key);
|
|
17
19
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opra/nestjs",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.15.0",
|
|
4
4
|
"description": "Opra NestJS module",
|
|
5
5
|
"author": "Panates",
|
|
6
6
|
"license": "MIT",
|
|
@@ -17,28 +17,28 @@
|
|
|
17
17
|
"build:esm": "tsc -b tsconfig-build-esm.json",
|
|
18
18
|
"postbuild": "cp README.md package.json ../../LICENSE ../../build/nestjs && cp ../../package.cjs.json ../../build/nestjs/cjs/package.json",
|
|
19
19
|
"lint": "eslint .",
|
|
20
|
-
"test": "
|
|
21
|
-
"cover": "
|
|
20
|
+
"test": "jest",
|
|
21
|
+
"cover": "jest --collect-coverage",
|
|
22
22
|
"clean": "npm run clean:src && npm run clean:dist && npm run clean:cover",
|
|
23
23
|
"clean:src": "ts-cleanup -s src --all && ts-cleanup -s test --all",
|
|
24
24
|
"clean:dist": "rimraf ../../build/nestjs",
|
|
25
25
|
"clean:cover": "rimraf ../../coverage/nestjs"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@opra/common": "^0.
|
|
29
|
-
"@opra/core": "^0.
|
|
30
|
-
"fast-tokenizer": "^1.2.
|
|
28
|
+
"@opra/common": "^0.15.0",
|
|
29
|
+
"@opra/core": "^0.15.0",
|
|
30
|
+
"fast-tokenizer": "^1.2.2",
|
|
31
31
|
"lodash.head": "^4.0.1",
|
|
32
32
|
"lodash.identity": "^3.0.0",
|
|
33
33
|
"reflect-metadata": "^0.1.13"
|
|
34
34
|
},
|
|
35
35
|
"peerDependencies": {
|
|
36
|
-
"@nestjs/common": "^9.
|
|
36
|
+
"@nestjs/common": "^9.4.0"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
|
-
"@nestjs/platform-express": "^9.
|
|
40
|
-
"@nestjs/testing": "^9.
|
|
41
|
-
"@types/lodash": "^4.14.
|
|
39
|
+
"@nestjs/platform-express": "^9.4.0",
|
|
40
|
+
"@nestjs/testing": "^9.4.0",
|
|
41
|
+
"@types/lodash": "^4.14.192",
|
|
42
42
|
"filedirname": "^2.7.0"
|
|
43
43
|
},
|
|
44
44
|
"type": "module",
|