@opra/nestjs 1.4.1 → 1.4.3

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.
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BaseOpraNestFactory = void 0;
4
+ const constants_1 = require("@nestjs/common/constants");
5
+ class BaseOpraNestFactory {
6
+ static copyDecoratorMetadata(target, ...source) {
7
+ for (const parent of source) {
8
+ const metadataKeys = Reflect.getOwnMetadataKeys(parent);
9
+ for (const key of metadataKeys) {
10
+ if (typeof key === 'string' && key.startsWith('opra.') && !Reflect.hasOwnMetadata(key, target)) {
11
+ const metadata = Reflect.getMetadata(key, parent);
12
+ Reflect.defineMetadata(key, metadata, target);
13
+ continue;
14
+ }
15
+ if (key === constants_1.GUARDS_METADATA || key === constants_1.INTERCEPTORS_METADATA || key === constants_1.EXCEPTION_FILTERS_METADATA) {
16
+ const m1 = Reflect.getMetadata(key, target) || [];
17
+ const metadata = [...m1];
18
+ const m2 = Reflect.getOwnMetadata(key, parent) || [];
19
+ m2.forEach((t) => {
20
+ if (!metadata.includes(t)) {
21
+ metadata.push(t);
22
+ }
23
+ });
24
+ Reflect.defineMetadata(key, metadata, target);
25
+ }
26
+ }
27
+ }
28
+ }
29
+ }
30
+ exports.BaseOpraNestFactory = BaseOpraNestFactory;
@@ -0,0 +1,123 @@
1
+ "use strict";
2
+ var RpcControllerFactory_1;
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.RpcControllerFactory = void 0;
5
+ const tslib_1 = require("tslib");
6
+ const common_1 = require("@nestjs/common");
7
+ const core_1 = require("@nestjs/core");
8
+ const external_context_creator_1 = require("@nestjs/core/helpers/external-context-creator");
9
+ const injector_1 = require("@nestjs/core/injector/injector");
10
+ const internal_core_module_1 = require("@nestjs/core/injector/internal-core-module");
11
+ const request_constants_1 = require("@nestjs/core/router/request/request-constants");
12
+ const constants_1 = require("@nestjs/microservices/constants");
13
+ const common_2 = require("@opra/common");
14
+ const base_opra_nest_factory_js_1 = require("./base-opra-nest-factory.js");
15
+ const rpc_params_factory_js_1 = require("./rpc-params.factory.js");
16
+ let RpcControllerFactory = RpcControllerFactory_1 = class RpcControllerFactory extends base_opra_nest_factory_js_1.BaseOpraNestFactory {
17
+ constructor(modulesContainer, externalContextCreator) {
18
+ super();
19
+ this.modulesContainer = modulesContainer;
20
+ this.externalContextCreator = externalContextCreator;
21
+ this.paramsFactory = new rpc_params_factory_js_1.RpcParamsFactory();
22
+ this.injector = new injector_1.Injector();
23
+ }
24
+ wrapControllers() {
25
+ const out = [];
26
+ for (const { module, wrapper } of this.exploreControllers()) {
27
+ const instance = wrapper.instance;
28
+ const sourceClass = instance.constructor;
29
+ const metadata = Reflect.getMetadata(common_2.RPC_CONTROLLER_METADATA, sourceClass);
30
+ const isRequestScoped = !wrapper.isDependencyTreeStatic();
31
+ /** Create a new controller class */
32
+ const newClass = {
33
+ [sourceClass.name]: class extends sourceClass {
34
+ },
35
+ }[sourceClass.name];
36
+ /** Copy metadata keys from source class to new one */
37
+ RpcControllerFactory_1.copyDecoratorMetadata(newClass, sourceClass);
38
+ (0, common_1.Controller)()(newClass);
39
+ out.push(newClass);
40
+ if (metadata.operations) {
41
+ for (const operationName of Object.keys(metadata.operations)) {
42
+ // const orgFn: Function = sourceClass.prototype[operationName];
43
+ newClass.prototype[operationName] = this._createContextCallback(instance, wrapper, module, operationName, isRequestScoped, 'rpc');
44
+ Reflect.defineMetadata(constants_1.PARAM_ARGS_METADATA, [core_1.REQUEST], instance.constructor, operationName);
45
+ }
46
+ }
47
+ }
48
+ return out;
49
+ }
50
+ _createContextCallback(instance, wrapper, moduleRef, methodName, isRequestScoped, contextType, options) {
51
+ const paramsFactory = this.paramsFactory;
52
+ if (isRequestScoped) {
53
+ return async (opraContext) => {
54
+ const contextId = (0, core_1.createContextId)();
55
+ Object.defineProperty(opraContext, request_constants_1.REQUEST_CONTEXT_ID, {
56
+ value: contextId,
57
+ enumerable: false,
58
+ configurable: false,
59
+ writable: false,
60
+ });
61
+ this.registerContextProvider(opraContext, contextId);
62
+ const contextInstance = await this.injector.loadPerContext(instance, moduleRef, moduleRef.providers, contextId);
63
+ const contextCallback = this.externalContextCreator.create(contextInstance, contextInstance[methodName], methodName, constants_1.PARAM_ARGS_METADATA, paramsFactory, contextId, wrapper.id, options, opraContext.protocol);
64
+ return contextCallback(opraContext);
65
+ };
66
+ }
67
+ return this.externalContextCreator.create(instance, instance[methodName], methodName, constants_1.PARAM_ARGS_METADATA, paramsFactory, undefined, undefined, options, contextType);
68
+ }
69
+ registerContextProvider(request, contextId) {
70
+ if (!this._coreModuleRef) {
71
+ const coreModuleArray = [...this.modulesContainer.entries()]
72
+ .filter(
73
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
74
+ ([_, { metatype }]) => metatype && metatype.name === internal_core_module_1.InternalCoreModule.name)
75
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
76
+ .map(([_, value]) => value);
77
+ this._coreModuleRef = coreModuleArray[0];
78
+ }
79
+ if (!this._coreModuleRef) {
80
+ return;
81
+ }
82
+ const wrapper = this._coreModuleRef.getProviderByKey(core_1.REQUEST);
83
+ wrapper.setInstanceByContextId(contextId, {
84
+ instance: request,
85
+ isResolved: true,
86
+ });
87
+ }
88
+ exploreControllers() {
89
+ const scannedModules = new Set();
90
+ const controllers = new Set();
91
+ const scanModule = (module) => {
92
+ if (scannedModules.has(module))
93
+ return;
94
+ scannedModules.add(module);
95
+ for (const mm of module.imports.values()) {
96
+ scanModule(mm);
97
+ }
98
+ for (const wrapper of module.controllers.values()) {
99
+ if (wrapper.instance &&
100
+ typeof wrapper.instance === 'object' &&
101
+ wrapper.instance.constructor &&
102
+ Reflect.getMetadata(common_2.RPC_CONTROLLER_METADATA, wrapper.instance.constructor) &&
103
+ !controllers.has(wrapper)) {
104
+ controllers.add({ module, wrapper });
105
+ }
106
+ if (wrapper.host)
107
+ scanModule(wrapper.host);
108
+ }
109
+ };
110
+ for (const module of this.modulesContainer.values()) {
111
+ scanModule(module);
112
+ }
113
+ return Array.from(controllers);
114
+ }
115
+ };
116
+ exports.RpcControllerFactory = RpcControllerFactory;
117
+ exports.RpcControllerFactory = RpcControllerFactory = RpcControllerFactory_1 = tslib_1.__decorate([
118
+ (0, common_1.Injectable)(),
119
+ tslib_1.__param(0, (0, common_1.Inject)()),
120
+ tslib_1.__param(1, (0, common_1.Inject)()),
121
+ tslib_1.__metadata("design:paramtypes", [core_1.ModulesContainer,
122
+ external_context_creator_1.ExternalContextCreator])
123
+ ], RpcControllerFactory);
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RpcParamsFactory = void 0;
4
+ class RpcParamsFactory {
5
+ exchangeKeyForValue(type, data, args) {
6
+ if (!args) {
7
+ return null;
8
+ }
9
+ args = Array.isArray(args) ? args : [];
10
+ return args[0];
11
+ }
12
+ }
13
+ exports.RpcParamsFactory = RpcParamsFactory;
@@ -5,11 +5,11 @@ const tslib_1 = require("tslib");
5
5
  const node_path_1 = tslib_1.__importDefault(require("node:path"));
6
6
  const objects_1 = require("@jsopen/objects");
7
7
  const common_1 = require("@nestjs/common");
8
- const constants_js_1 = require("@nestjs/common/constants.js");
9
8
  const common_2 = require("@opra/common");
10
9
  const http_1 = require("@opra/http");
11
10
  const ts_gems_1 = require("ts-gems");
12
11
  const public_decorator_js_1 = require("../decorators/public.decorator.js");
12
+ const base_opra_nest_factory_js_1 = require("../helpers/base-opra-nest-factory.js");
13
13
  class OpraHttpNestjsAdapter extends http_1.HttpAdapter {
14
14
  constructor(options) {
15
15
  super(options);
@@ -53,13 +53,14 @@ class OpraHttpNestjsAdapter extends http_1.HttpAdapter {
53
53
  const metadata = Reflect.getMetadata(common_2.HTTP_CONTROLLER_METADATA, sourceClass);
54
54
  if (!metadata)
55
55
  return;
56
+ /** Create a new controller class */
56
57
  const newClass = {
57
58
  [sourceClass.name]: class extends sourceClass {
58
59
  },
59
60
  }[sourceClass.name];
60
61
  /** Copy metadata keys from source class to new one */
61
- let metadataKeys;
62
- OpraHttpNestjsAdapter.copyDecoratorMetadataToChild(newClass, parentTree);
62
+ base_opra_nest_factory_js_1.BaseOpraNestFactory.copyDecoratorMetadata(newClass, ...parentTree);
63
+ (0, common_1.Controller)()(newClass);
63
64
  const newPath = metadata.path ? node_path_1.default.join(currentPath, metadata.path) : currentPath;
64
65
  const adapter = this;
65
66
  // adapter.logger =
@@ -67,8 +68,8 @@ class OpraHttpNestjsAdapter extends http_1.HttpAdapter {
67
68
  adapter.handler.onError = (context, error) => {
68
69
  throw error;
69
70
  };
70
- (0, common_1.Controller)()(newClass);
71
71
  this.nestControllers.push(newClass);
72
+ let metadataKeys;
72
73
  if (metadata.operations) {
73
74
  for (const [k, v] of Object.entries(metadata.operations)) {
74
75
  const operationHandler = sourceClass.prototype[k];
@@ -156,28 +157,5 @@ class OpraHttpNestjsAdapter extends http_1.HttpAdapter {
156
157
  }
157
158
  }
158
159
  }
159
- static copyDecoratorMetadataToChild(target, parentTree) {
160
- for (const parent of parentTree) {
161
- const metadataKeys = Reflect.getOwnMetadataKeys(parent);
162
- for (const key of metadataKeys) {
163
- if (typeof key === 'string' && key.startsWith('opra:') && !Reflect.hasOwnMetadata(key, target)) {
164
- const metadata = Reflect.getMetadata(key, parent);
165
- Reflect.defineMetadata(key, metadata, target);
166
- continue;
167
- }
168
- if (key === constants_js_1.GUARDS_METADATA || key === constants_js_1.INTERCEPTORS_METADATA || key === constants_js_1.EXCEPTION_FILTERS_METADATA) {
169
- const m1 = Reflect.getMetadata(key, target) || [];
170
- const metadata = [...m1];
171
- const m2 = Reflect.getOwnMetadata(key, parent) || [];
172
- m2.forEach((t) => {
173
- if (!metadata.includes(t)) {
174
- metadata.push(t);
175
- }
176
- });
177
- Reflect.defineMetadata(key, metadata, target);
178
- }
179
- }
180
- }
181
- }
182
160
  }
183
161
  exports.OpraHttpNestjsAdapter = OpraHttpNestjsAdapter;
@@ -6,13 +6,14 @@ const tslib_1 = require("tslib");
6
6
  const objects_1 = require("@jsopen/objects");
7
7
  const common_1 = require("@nestjs/common");
8
8
  const core_1 = require("@nestjs/core");
9
+ const common_2 = require("@opra/common");
9
10
  const kafka_1 = require("@opra/kafka");
10
11
  const constants_js_1 = require("../constants.js");
11
- const initialize_adapter_js_1 = require("./helpers/initialize-adapter.js");
12
+ const rpc_controller_factory_service_js_1 = require("../helpers/rpc-controller-factory.service.js");
12
13
  const opraKafkaNestjsAdapterToken = Symbol('OpraKafkaNestjsAdapter');
13
14
  let OpraKafkaCoreModule = OpraKafkaCoreModule_1 = class OpraKafkaCoreModule {
14
- constructor(moduleRef, adapter, config) {
15
- this.moduleRef = moduleRef;
15
+ constructor(controllerFactory, adapter, config) {
16
+ this.controllerFactory = controllerFactory;
16
17
  this.adapter = adapter;
17
18
  this.config = config;
18
19
  }
@@ -54,8 +55,21 @@ let OpraKafkaCoreModule = OpraKafkaCoreModule_1 = class OpraKafkaCoreModule {
54
55
  const token = moduleOptions.id || kafka_1.KafkaAdapter;
55
56
  const adapterProvider = {
56
57
  provide: token,
57
- inject: [core_1.ModuleRef, constants_js_1.OPRA_KAFKA_MODULE_CONFIG],
58
- useFactory: async (moduleRef, config) => {
58
+ inject: [rpc_controller_factory_service_js_1.RpcControllerFactory, core_1.ModuleRef, constants_js_1.OPRA_KAFKA_MODULE_CONFIG],
59
+ useFactory: async (controllerFactory, moduleRef, config) => {
60
+ const controllers = controllerFactory.exploreControllers().map(x => x.wrapper.instance.constructor);
61
+ const document = await common_2.ApiDocumentFactory.createDocument({
62
+ info: config.info,
63
+ types: config.types,
64
+ references: config.references,
65
+ api: {
66
+ name: config.name,
67
+ description: config.description,
68
+ transport: 'rpc',
69
+ platform: 'kafka',
70
+ controllers,
71
+ },
72
+ });
59
73
  const interceptors = moduleOptions.interceptors
60
74
  ? moduleOptions.interceptors.map(x => {
61
75
  if ((0, objects_1.isConstructor)(x)) {
@@ -68,7 +82,7 @@ let OpraKafkaCoreModule = OpraKafkaCoreModule_1 = class OpraKafkaCoreModule {
68
82
  return x;
69
83
  })
70
84
  : undefined;
71
- return new kafka_1.KafkaAdapter({ ...config, interceptors });
85
+ return new kafka_1.KafkaAdapter(document, { ...config, interceptors });
72
86
  },
73
87
  };
74
88
  return {
@@ -77,6 +91,7 @@ let OpraKafkaCoreModule = OpraKafkaCoreModule_1 = class OpraKafkaCoreModule {
77
91
  controllers: moduleOptions.controllers,
78
92
  providers: [
79
93
  ...(moduleOptions?.providers || []),
94
+ rpc_controller_factory_service_js_1.RpcControllerFactory,
80
95
  adapterProvider,
81
96
  {
82
97
  provide: opraKafkaNestjsAdapterToken,
@@ -87,15 +102,21 @@ let OpraKafkaCoreModule = OpraKafkaCoreModule_1 = class OpraKafkaCoreModule {
87
102
  exports: [...(moduleOptions?.exports || []), adapterProvider],
88
103
  };
89
104
  }
90
- async onModuleInit() {
91
- /** Check if not initialized before */
92
- if (!this.adapter.document) {
93
- await (0, initialize_adapter_js_1.initializeAdapter)(this.moduleRef, this.adapter, this.config);
105
+ onModuleInit() {
106
+ /** NestJS initialize controller instances on init stage.
107
+ * So we should update instance properties */
108
+ const rpcApi = this.adapter.document.rpcApi;
109
+ const controllers = Array.from(rpcApi.controllers.values());
110
+ for (const { wrapper } of this.controllerFactory.exploreControllers().values()) {
111
+ const ctor = wrapper.instance.constructor;
112
+ const controller = controllers.find(x => x.ctor === ctor);
113
+ if (controller) {
114
+ controller.instance = wrapper.instance;
115
+ }
94
116
  }
95
117
  }
96
118
  async onApplicationBootstrap() {
97
- if (this.adapter.document)
98
- await this.adapter.start();
119
+ await this.adapter.start();
99
120
  }
100
121
  async onApplicationShutdown() {
101
122
  await this.adapter.close();
@@ -107,6 +128,6 @@ exports.OpraKafkaCoreModule = OpraKafkaCoreModule = OpraKafkaCoreModule_1 = tsli
107
128
  (0, common_1.Global)(),
108
129
  tslib_1.__param(1, (0, common_1.Inject)(opraKafkaNestjsAdapterToken)),
109
130
  tslib_1.__param(2, (0, common_1.Inject)(constants_js_1.OPRA_KAFKA_MODULE_CONFIG)),
110
- tslib_1.__metadata("design:paramtypes", [core_1.ModuleRef,
131
+ tslib_1.__metadata("design:paramtypes", [rpc_controller_factory_service_js_1.RpcControllerFactory,
111
132
  kafka_1.KafkaAdapter, Object])
112
133
  ], OpraKafkaCoreModule);
@@ -0,0 +1,26 @@
1
+ import { EXCEPTION_FILTERS_METADATA, GUARDS_METADATA, INTERCEPTORS_METADATA } from '@nestjs/common/constants';
2
+ export class BaseOpraNestFactory {
3
+ static copyDecoratorMetadata(target, ...source) {
4
+ for (const parent of source) {
5
+ const metadataKeys = Reflect.getOwnMetadataKeys(parent);
6
+ for (const key of metadataKeys) {
7
+ if (typeof key === 'string' && key.startsWith('opra.') && !Reflect.hasOwnMetadata(key, target)) {
8
+ const metadata = Reflect.getMetadata(key, parent);
9
+ Reflect.defineMetadata(key, metadata, target);
10
+ continue;
11
+ }
12
+ if (key === GUARDS_METADATA || key === INTERCEPTORS_METADATA || key === EXCEPTION_FILTERS_METADATA) {
13
+ const m1 = Reflect.getMetadata(key, target) || [];
14
+ const metadata = [...m1];
15
+ const m2 = Reflect.getOwnMetadata(key, parent) || [];
16
+ m2.forEach((t) => {
17
+ if (!metadata.includes(t)) {
18
+ metadata.push(t);
19
+ }
20
+ });
21
+ Reflect.defineMetadata(key, metadata, target);
22
+ }
23
+ }
24
+ }
25
+ }
26
+ }
@@ -0,0 +1,120 @@
1
+ var RpcControllerFactory_1;
2
+ import { __decorate, __metadata, __param } from "tslib";
3
+ import { Controller, Inject, Injectable } from '@nestjs/common';
4
+ import { createContextId, ModulesContainer, REQUEST } from '@nestjs/core';
5
+ import { ExternalContextCreator } from '@nestjs/core/helpers/external-context-creator';
6
+ import { Injector } from '@nestjs/core/injector/injector';
7
+ import { InternalCoreModule } from '@nestjs/core/injector/internal-core-module';
8
+ import { REQUEST_CONTEXT_ID } from '@nestjs/core/router/request/request-constants';
9
+ import { PARAM_ARGS_METADATA } from '@nestjs/microservices/constants';
10
+ import { RPC_CONTROLLER_METADATA } from '@opra/common';
11
+ import { BaseOpraNestFactory } from './base-opra-nest-factory.js';
12
+ import { RpcParamsFactory } from './rpc-params.factory.js';
13
+ let RpcControllerFactory = RpcControllerFactory_1 = class RpcControllerFactory extends BaseOpraNestFactory {
14
+ constructor(modulesContainer, externalContextCreator) {
15
+ super();
16
+ this.modulesContainer = modulesContainer;
17
+ this.externalContextCreator = externalContextCreator;
18
+ this.paramsFactory = new RpcParamsFactory();
19
+ this.injector = new Injector();
20
+ }
21
+ wrapControllers() {
22
+ const out = [];
23
+ for (const { module, wrapper } of this.exploreControllers()) {
24
+ const instance = wrapper.instance;
25
+ const sourceClass = instance.constructor;
26
+ const metadata = Reflect.getMetadata(RPC_CONTROLLER_METADATA, sourceClass);
27
+ const isRequestScoped = !wrapper.isDependencyTreeStatic();
28
+ /** Create a new controller class */
29
+ const newClass = {
30
+ [sourceClass.name]: class extends sourceClass {
31
+ },
32
+ }[sourceClass.name];
33
+ /** Copy metadata keys from source class to new one */
34
+ RpcControllerFactory_1.copyDecoratorMetadata(newClass, sourceClass);
35
+ Controller()(newClass);
36
+ out.push(newClass);
37
+ if (metadata.operations) {
38
+ for (const operationName of Object.keys(metadata.operations)) {
39
+ // const orgFn: Function = sourceClass.prototype[operationName];
40
+ newClass.prototype[operationName] = this._createContextCallback(instance, wrapper, module, operationName, isRequestScoped, 'rpc');
41
+ Reflect.defineMetadata(PARAM_ARGS_METADATA, [REQUEST], instance.constructor, operationName);
42
+ }
43
+ }
44
+ }
45
+ return out;
46
+ }
47
+ _createContextCallback(instance, wrapper, moduleRef, methodName, isRequestScoped, contextType, options) {
48
+ const paramsFactory = this.paramsFactory;
49
+ if (isRequestScoped) {
50
+ return async (opraContext) => {
51
+ const contextId = createContextId();
52
+ Object.defineProperty(opraContext, REQUEST_CONTEXT_ID, {
53
+ value: contextId,
54
+ enumerable: false,
55
+ configurable: false,
56
+ writable: false,
57
+ });
58
+ this.registerContextProvider(opraContext, contextId);
59
+ const contextInstance = await this.injector.loadPerContext(instance, moduleRef, moduleRef.providers, contextId);
60
+ const contextCallback = this.externalContextCreator.create(contextInstance, contextInstance[methodName], methodName, PARAM_ARGS_METADATA, paramsFactory, contextId, wrapper.id, options, opraContext.protocol);
61
+ return contextCallback(opraContext);
62
+ };
63
+ }
64
+ return this.externalContextCreator.create(instance, instance[methodName], methodName, PARAM_ARGS_METADATA, paramsFactory, undefined, undefined, options, contextType);
65
+ }
66
+ registerContextProvider(request, contextId) {
67
+ if (!this._coreModuleRef) {
68
+ const coreModuleArray = [...this.modulesContainer.entries()]
69
+ .filter(
70
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
71
+ ([_, { metatype }]) => metatype && metatype.name === InternalCoreModule.name)
72
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
73
+ .map(([_, value]) => value);
74
+ this._coreModuleRef = coreModuleArray[0];
75
+ }
76
+ if (!this._coreModuleRef) {
77
+ return;
78
+ }
79
+ const wrapper = this._coreModuleRef.getProviderByKey(REQUEST);
80
+ wrapper.setInstanceByContextId(contextId, {
81
+ instance: request,
82
+ isResolved: true,
83
+ });
84
+ }
85
+ exploreControllers() {
86
+ const scannedModules = new Set();
87
+ const controllers = new Set();
88
+ const scanModule = (module) => {
89
+ if (scannedModules.has(module))
90
+ return;
91
+ scannedModules.add(module);
92
+ for (const mm of module.imports.values()) {
93
+ scanModule(mm);
94
+ }
95
+ for (const wrapper of module.controllers.values()) {
96
+ if (wrapper.instance &&
97
+ typeof wrapper.instance === 'object' &&
98
+ wrapper.instance.constructor &&
99
+ Reflect.getMetadata(RPC_CONTROLLER_METADATA, wrapper.instance.constructor) &&
100
+ !controllers.has(wrapper)) {
101
+ controllers.add({ module, wrapper });
102
+ }
103
+ if (wrapper.host)
104
+ scanModule(wrapper.host);
105
+ }
106
+ };
107
+ for (const module of this.modulesContainer.values()) {
108
+ scanModule(module);
109
+ }
110
+ return Array.from(controllers);
111
+ }
112
+ };
113
+ RpcControllerFactory = RpcControllerFactory_1 = __decorate([
114
+ Injectable(),
115
+ __param(0, Inject()),
116
+ __param(1, Inject()),
117
+ __metadata("design:paramtypes", [ModulesContainer,
118
+ ExternalContextCreator])
119
+ ], RpcControllerFactory);
120
+ export { RpcControllerFactory };
@@ -0,0 +1,9 @@
1
+ export class RpcParamsFactory {
2
+ exchangeKeyForValue(type, data, args) {
3
+ if (!args) {
4
+ return null;
5
+ }
6
+ args = Array.isArray(args) ? args : [];
7
+ return args[0];
8
+ }
9
+ }
@@ -2,11 +2,11 @@ import { __decorate, __metadata, __param } from "tslib";
2
2
  import nodePath from 'node:path';
3
3
  import { isConstructor } from '@jsopen/objects';
4
4
  import { Controller, Delete, Get, Head, Next, Options, Patch, Post, Put, Req, Res, Search, } from '@nestjs/common';
5
- import { EXCEPTION_FILTERS_METADATA, GUARDS_METADATA, INTERCEPTORS_METADATA } from '@nestjs/common/constants.js';
6
5
  import { HTTP_CONTROLLER_METADATA, NotFoundError } from '@opra/common';
7
6
  import { HttpAdapter } from '@opra/http';
8
7
  import { asMutable } from 'ts-gems';
9
8
  import { Public } from '../decorators/public.decorator.js';
9
+ import { BaseOpraNestFactory } from '../helpers/base-opra-nest-factory.js';
10
10
  export class OpraHttpNestjsAdapter extends HttpAdapter {
11
11
  constructor(options) {
12
12
  super(options);
@@ -50,13 +50,14 @@ export class OpraHttpNestjsAdapter extends HttpAdapter {
50
50
  const metadata = Reflect.getMetadata(HTTP_CONTROLLER_METADATA, sourceClass);
51
51
  if (!metadata)
52
52
  return;
53
+ /** Create a new controller class */
53
54
  const newClass = {
54
55
  [sourceClass.name]: class extends sourceClass {
55
56
  },
56
57
  }[sourceClass.name];
57
58
  /** Copy metadata keys from source class to new one */
58
- let metadataKeys;
59
- OpraHttpNestjsAdapter.copyDecoratorMetadataToChild(newClass, parentTree);
59
+ BaseOpraNestFactory.copyDecoratorMetadata(newClass, ...parentTree);
60
+ Controller()(newClass);
60
61
  const newPath = metadata.path ? nodePath.join(currentPath, metadata.path) : currentPath;
61
62
  const adapter = this;
62
63
  // adapter.logger =
@@ -64,8 +65,8 @@ export class OpraHttpNestjsAdapter extends HttpAdapter {
64
65
  adapter.handler.onError = (context, error) => {
65
66
  throw error;
66
67
  };
67
- Controller()(newClass);
68
68
  this.nestControllers.push(newClass);
69
+ let metadataKeys;
69
70
  if (metadata.operations) {
70
71
  for (const [k, v] of Object.entries(metadata.operations)) {
71
72
  const operationHandler = sourceClass.prototype[k];
@@ -153,27 +154,4 @@ export class OpraHttpNestjsAdapter extends HttpAdapter {
153
154
  }
154
155
  }
155
156
  }
156
- static copyDecoratorMetadataToChild(target, parentTree) {
157
- for (const parent of parentTree) {
158
- const metadataKeys = Reflect.getOwnMetadataKeys(parent);
159
- for (const key of metadataKeys) {
160
- if (typeof key === 'string' && key.startsWith('opra:') && !Reflect.hasOwnMetadata(key, target)) {
161
- const metadata = Reflect.getMetadata(key, parent);
162
- Reflect.defineMetadata(key, metadata, target);
163
- continue;
164
- }
165
- if (key === GUARDS_METADATA || key === INTERCEPTORS_METADATA || key === EXCEPTION_FILTERS_METADATA) {
166
- const m1 = Reflect.getMetadata(key, target) || [];
167
- const metadata = [...m1];
168
- const m2 = Reflect.getOwnMetadata(key, parent) || [];
169
- m2.forEach((t) => {
170
- if (!metadata.includes(t)) {
171
- metadata.push(t);
172
- }
173
- });
174
- Reflect.defineMetadata(key, metadata, target);
175
- }
176
- }
177
- }
178
- }
179
157
  }
@@ -3,13 +3,14 @@ import { __decorate, __metadata, __param } from "tslib";
3
3
  import { isConstructor } from '@jsopen/objects';
4
4
  import { Global, Inject, Logger, Module, } from '@nestjs/common';
5
5
  import { ModuleRef } from '@nestjs/core';
6
+ import { ApiDocumentFactory } from '@opra/common';
6
7
  import { KafkaAdapter } from '@opra/kafka';
7
8
  import { OPRA_KAFKA_MODULE_CONFIG } from '../constants.js';
8
- import { initializeAdapter } from './helpers/initialize-adapter.js';
9
+ import { RpcControllerFactory } from '../helpers/rpc-controller-factory.service.js';
9
10
  const opraKafkaNestjsAdapterToken = Symbol('OpraKafkaNestjsAdapter');
10
11
  let OpraKafkaCoreModule = OpraKafkaCoreModule_1 = class OpraKafkaCoreModule {
11
- constructor(moduleRef, adapter, config) {
12
- this.moduleRef = moduleRef;
12
+ constructor(controllerFactory, adapter, config) {
13
+ this.controllerFactory = controllerFactory;
13
14
  this.adapter = adapter;
14
15
  this.config = config;
15
16
  }
@@ -51,8 +52,21 @@ let OpraKafkaCoreModule = OpraKafkaCoreModule_1 = class OpraKafkaCoreModule {
51
52
  const token = moduleOptions.id || KafkaAdapter;
52
53
  const adapterProvider = {
53
54
  provide: token,
54
- inject: [ModuleRef, OPRA_KAFKA_MODULE_CONFIG],
55
- useFactory: async (moduleRef, config) => {
55
+ inject: [RpcControllerFactory, ModuleRef, OPRA_KAFKA_MODULE_CONFIG],
56
+ useFactory: async (controllerFactory, moduleRef, config) => {
57
+ const controllers = controllerFactory.exploreControllers().map(x => x.wrapper.instance.constructor);
58
+ const document = await ApiDocumentFactory.createDocument({
59
+ info: config.info,
60
+ types: config.types,
61
+ references: config.references,
62
+ api: {
63
+ name: config.name,
64
+ description: config.description,
65
+ transport: 'rpc',
66
+ platform: 'kafka',
67
+ controllers,
68
+ },
69
+ });
56
70
  const interceptors = moduleOptions.interceptors
57
71
  ? moduleOptions.interceptors.map(x => {
58
72
  if (isConstructor(x)) {
@@ -65,7 +79,7 @@ let OpraKafkaCoreModule = OpraKafkaCoreModule_1 = class OpraKafkaCoreModule {
65
79
  return x;
66
80
  })
67
81
  : undefined;
68
- return new KafkaAdapter({ ...config, interceptors });
82
+ return new KafkaAdapter(document, { ...config, interceptors });
69
83
  },
70
84
  };
71
85
  return {
@@ -74,6 +88,7 @@ let OpraKafkaCoreModule = OpraKafkaCoreModule_1 = class OpraKafkaCoreModule {
74
88
  controllers: moduleOptions.controllers,
75
89
  providers: [
76
90
  ...(moduleOptions?.providers || []),
91
+ RpcControllerFactory,
77
92
  adapterProvider,
78
93
  {
79
94
  provide: opraKafkaNestjsAdapterToken,
@@ -84,15 +99,21 @@ let OpraKafkaCoreModule = OpraKafkaCoreModule_1 = class OpraKafkaCoreModule {
84
99
  exports: [...(moduleOptions?.exports || []), adapterProvider],
85
100
  };
86
101
  }
87
- async onModuleInit() {
88
- /** Check if not initialized before */
89
- if (!this.adapter.document) {
90
- await initializeAdapter(this.moduleRef, this.adapter, this.config);
102
+ onModuleInit() {
103
+ /** NestJS initialize controller instances on init stage.
104
+ * So we should update instance properties */
105
+ const rpcApi = this.adapter.document.rpcApi;
106
+ const controllers = Array.from(rpcApi.controllers.values());
107
+ for (const { wrapper } of this.controllerFactory.exploreControllers().values()) {
108
+ const ctor = wrapper.instance.constructor;
109
+ const controller = controllers.find(x => x.ctor === ctor);
110
+ if (controller) {
111
+ controller.instance = wrapper.instance;
112
+ }
91
113
  }
92
114
  }
93
115
  async onApplicationBootstrap() {
94
- if (this.adapter.document)
95
- await this.adapter.start();
116
+ await this.adapter.start();
96
117
  }
97
118
  async onApplicationShutdown() {
98
119
  await this.adapter.close();
@@ -103,7 +124,7 @@ OpraKafkaCoreModule = OpraKafkaCoreModule_1 = __decorate([
103
124
  Global(),
104
125
  __param(1, Inject(opraKafkaNestjsAdapterToken)),
105
126
  __param(2, Inject(OPRA_KAFKA_MODULE_CONFIG)),
106
- __metadata("design:paramtypes", [ModuleRef,
127
+ __metadata("design:paramtypes", [RpcControllerFactory,
107
128
  KafkaAdapter, Object])
108
129
  ], OpraKafkaCoreModule);
109
130
  export { OpraKafkaCoreModule };
package/package.json CHANGED
@@ -1,27 +1,26 @@
1
1
  {
2
2
  "name": "@opra/nestjs",
3
- "version": "1.4.1",
3
+ "version": "1.4.3",
4
4
  "description": "Opra NestJS module",
5
5
  "author": "Panates",
6
6
  "license": "MIT",
7
7
  "dependencies": {
8
8
  "@jsopen/objects": "^1.5.0",
9
- "@opra/common": "^1.4.1",
10
- "@opra/core": "^1.4.1",
9
+ "@opra/common": "^1.4.3",
10
+ "@opra/core": "^1.4.3",
11
11
  "fast-tokenizer": "^1.7.0",
12
- "lodash.head": "^4.0.1",
13
12
  "putil-promisify": "^1.10.1",
14
13
  "reflect-metadata": "^0.2.2",
15
14
  "tslib": "^2.8.1"
16
15
  },
17
16
  "peerDependencies": {
18
- "@nestjs/common": "^10.4.12",
19
- "@nestjs/core": "^10.4.12"
17
+ "@nestjs/common": "^10.4.13",
18
+ "@nestjs/core": "^10.4.13"
20
19
  },
21
20
  "optionalDependencies": {
22
- "@nestjs/microservices": "^10.4.12",
23
- "@opra/http": "^1.4.1",
24
- "@opra/kafka": "^1.4.1"
21
+ "@nestjs/microservices": "^10.4.13",
22
+ "@opra/http": "^1.4.3",
23
+ "@opra/kafka": "^1.4.3"
25
24
  },
26
25
  "type": "module",
27
26
  "exports": {
@@ -0,0 +1,4 @@
1
+ import type { Type } from '@nestjs/common';
2
+ export declare class BaseOpraNestFactory {
3
+ static copyDecoratorMetadata(target: Type, ...source: Type[]): void;
4
+ }
@@ -0,0 +1,21 @@
1
+ import { type Type } from '@nestjs/common';
2
+ import { ModulesContainer } from '@nestjs/core';
3
+ import { ExternalContextCreator } from '@nestjs/core/helpers/external-context-creator';
4
+ import type { InstanceWrapper } from '@nestjs/core/injector/instance-wrapper';
5
+ import type { Module } from '@nestjs/core/injector/module.js';
6
+ import { BaseOpraNestFactory } from './base-opra-nest-factory.js';
7
+ export declare class RpcControllerFactory extends BaseOpraNestFactory {
8
+ private readonly modulesContainer;
9
+ private readonly externalContextCreator;
10
+ private _coreModuleRef?;
11
+ private readonly paramsFactory;
12
+ private readonly injector;
13
+ constructor(modulesContainer: ModulesContainer, externalContextCreator: ExternalContextCreator);
14
+ wrapControllers(): Type[];
15
+ private _createContextCallback;
16
+ private registerContextProvider;
17
+ exploreControllers(): {
18
+ module: Module;
19
+ wrapper: InstanceWrapper;
20
+ }[];
21
+ }
@@ -0,0 +1,4 @@
1
+ import type { ParamsFactory } from '@nestjs/core/helpers/external-context-creator.js';
2
+ export declare class RpcParamsFactory implements ParamsFactory {
3
+ exchangeKeyForValue(type: number, data: any, args: any): any;
4
+ }
@@ -9,5 +9,4 @@ export declare class OpraHttpNestjsAdapter extends HttpAdapter {
9
9
  close(): Promise<void>;
10
10
  protected _addRootController(isPublic?: boolean): void;
11
11
  protected _addToNestControllers(sourceClass: Type, currentPath: string, parentTree: Type[]): void;
12
- static copyDecoratorMetadataToChild(target: Type, parentTree: Type[]): void;
13
12
  }
@@ -1,16 +1,16 @@
1
1
  import { type DynamicModule, OnApplicationBootstrap, OnApplicationShutdown, OnModuleInit } from '@nestjs/common';
2
- import { ModuleRef } from '@nestjs/core';
3
2
  import { KafkaAdapter } from '@opra/kafka';
3
+ import { RpcControllerFactory } from '../helpers/rpc-controller-factory.service.js';
4
4
  import type { OpraKafkaModule } from './opra-kafka.module.js';
5
5
  export declare class OpraKafkaCoreModule implements OnModuleInit, OnApplicationBootstrap, OnApplicationShutdown {
6
- protected moduleRef: ModuleRef;
6
+ private controllerFactory;
7
7
  protected adapter: KafkaAdapter;
8
8
  protected config: OpraKafkaModule.ApiConfig;
9
- constructor(moduleRef: ModuleRef, adapter: KafkaAdapter, config: OpraKafkaModule.ApiConfig);
9
+ constructor(controllerFactory: RpcControllerFactory, adapter: KafkaAdapter, config: OpraKafkaModule.ApiConfig);
10
10
  static forRoot(moduleOptions: OpraKafkaModule.ModuleOptions): DynamicModule;
11
11
  static forRootAsync(moduleOptions: OpraKafkaModule.AsyncModuleOptions): DynamicModule;
12
12
  protected static _getDynamicModule(moduleOptions: OpraKafkaModule.ModuleOptions | OpraKafkaModule.AsyncModuleOptions): DynamicModule;
13
- onModuleInit(): Promise<void>;
13
+ onModuleInit(): any;
14
14
  onApplicationBootstrap(): Promise<void>;
15
15
  onApplicationShutdown(): Promise<void>;
16
16
  }
@@ -1,44 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.initializeAdapter = initializeAdapter;
4
- const common_1 = require("@opra/common");
5
- async function initializeAdapter(moduleRef, adapter, config) {
6
- const controllers = scanControllers(moduleRef);
7
- const document = await common_1.ApiDocumentFactory.createDocument({
8
- info: config.info,
9
- types: config.types,
10
- references: config.references,
11
- api: {
12
- name: config.name,
13
- description: config.description,
14
- transport: 'rpc',
15
- platform: 'kafka',
16
- controllers,
17
- },
18
- });
19
- await adapter.initialize(document);
20
- }
21
- function scanControllers(moduleRef) {
22
- const container = moduleRef.container;
23
- const modules = container.getModules();
24
- const out = [];
25
- modules.forEach(({ controllers }) => {
26
- controllers.forEach(wrapper => {
27
- const ctor = Object.getPrototypeOf(wrapper.instance).constructor;
28
- const metadata = Reflect.getMetadata(common_1.RPC_CONTROLLER_METADATA, ctor);
29
- if (!metadata)
30
- return;
31
- const instance = {};
32
- Object.setPrototypeOf(instance, wrapper.instance);
33
- out.push(wrapper.instance);
34
- // if (metadata.operations) {
35
- // for (const [k, _] of Object.keys(metadata.operations)) {
36
- // const isRequestScoped = !wrapper.isDependencyTreeStatic();
37
- // // const fn = instance[k];
38
- // // instance[k] = fn;
39
- // }
40
- // }
41
- });
42
- });
43
- return out;
44
- }
@@ -1,41 +0,0 @@
1
- import { ApiDocumentFactory, RPC_CONTROLLER_METADATA } from '@opra/common';
2
- export async function initializeAdapter(moduleRef, adapter, config) {
3
- const controllers = scanControllers(moduleRef);
4
- const document = await ApiDocumentFactory.createDocument({
5
- info: config.info,
6
- types: config.types,
7
- references: config.references,
8
- api: {
9
- name: config.name,
10
- description: config.description,
11
- transport: 'rpc',
12
- platform: 'kafka',
13
- controllers,
14
- },
15
- });
16
- await adapter.initialize(document);
17
- }
18
- function scanControllers(moduleRef) {
19
- const container = moduleRef.container;
20
- const modules = container.getModules();
21
- const out = [];
22
- modules.forEach(({ controllers }) => {
23
- controllers.forEach(wrapper => {
24
- const ctor = Object.getPrototypeOf(wrapper.instance).constructor;
25
- const metadata = Reflect.getMetadata(RPC_CONTROLLER_METADATA, ctor);
26
- if (!metadata)
27
- return;
28
- const instance = {};
29
- Object.setPrototypeOf(instance, wrapper.instance);
30
- out.push(wrapper.instance);
31
- // if (metadata.operations) {
32
- // for (const [k, _] of Object.keys(metadata.operations)) {
33
- // const isRequestScoped = !wrapper.isDependencyTreeStatic();
34
- // // const fn = instance[k];
35
- // // instance[k] = fn;
36
- // }
37
- // }
38
- });
39
- });
40
- return out;
41
- }
@@ -1,4 +0,0 @@
1
- import { ModuleRef } from '@nestjs/core';
2
- import { KafkaAdapter } from '@opra/kafka';
3
- import type { OpraKafkaModule } from '../opra-kafka.module.js';
4
- export declare function initializeAdapter(moduleRef: ModuleRef, adapter: KafkaAdapter, config: OpraKafkaModule.ApiConfig): Promise<void>;