@opra/nestjs
Version:
Opra NestJS module
117 lines (116 loc) • 5.68 kB
JavaScript
import { __decorate, __metadata } from "tslib";
import { Controller, Injectable } from '@nestjs/common';
import { createContextId, ModulesContainer, REQUEST } from '@nestjs/core';
import { ExternalContextCreator, } from '@nestjs/core/helpers/external-context-creator.js';
import { Injector } from '@nestjs/core/injector/injector.js';
import { InternalCoreModule } from '@nestjs/core/injector/internal-core-module/index.js';
import { REQUEST_CONTEXT_ID } from '@nestjs/core/router/request/request-constants.js';
import { PARAM_ARGS_METADATA } from '@nestjs/microservices/constants.js';
import { RPC_CONTROLLER_METADATA } from '@opra/common';
import { OpraNestUtils } from './opra-nest-utils.js';
import { RpcParamsFactory } from './rpc-params.factory.js';
let RpcControllerFactory = class RpcControllerFactory {
constructor(modulesContainer, externalContextCreator) {
this.modulesContainer = modulesContainer;
this.externalContextCreator = externalContextCreator;
this.paramsFactory = new RpcParamsFactory();
this.injector = new Injector();
}
wrapControllers() {
const out = [];
for (const { module, wrapper } of this.exploreControllers()) {
const instance = wrapper.instance;
const sourceClass = instance.constructor;
const metadata = Reflect.getMetadata(RPC_CONTROLLER_METADATA, sourceClass);
const isRequestScoped = !wrapper.isDependencyTreeStatic();
/** Create a new controller class */
const newClass = {
[sourceClass.name]: class extends sourceClass {
},
}[sourceClass.name];
/** Copy metadata keys from source class to new one */
OpraNestUtils.copyDecoratorMetadata(newClass, sourceClass);
Controller()(newClass);
out.push(newClass);
if (metadata.operations) {
for (const operationName of Object.keys(metadata.operations)) {
// const orgFn: Function = sourceClass.prototype[operationName];
newClass.prototype[operationName] = this._createContextCallback(instance, wrapper, module, operationName, isRequestScoped, 'rpc');
Reflect.defineMetadata(PARAM_ARGS_METADATA, [REQUEST], instance.constructor, operationName);
}
}
}
return out;
}
_createContextCallback(instance, wrapper, moduleRef, methodName, isRequestScoped, contextType, options) {
const paramsFactory = this.paramsFactory;
if (isRequestScoped) {
return async (opraContext) => {
const contextId = createContextId();
Object.defineProperty(opraContext, REQUEST_CONTEXT_ID, {
value: contextId,
enumerable: false,
configurable: false,
writable: false,
});
this.registerContextProvider(opraContext, contextId);
const contextInstance = await this.injector.loadPerContext(instance, moduleRef, moduleRef.providers, contextId);
const contextCallback = this.externalContextCreator.create(contextInstance, contextInstance[methodName], methodName, PARAM_ARGS_METADATA, paramsFactory, contextId, wrapper.id, options, opraContext.protocol);
return contextCallback(opraContext);
};
}
return this.externalContextCreator.create(instance, instance[methodName], methodName, PARAM_ARGS_METADATA, paramsFactory, undefined, undefined, options, contextType);
}
registerContextProvider(request, contextId) {
if (!this._coreModuleRef) {
const coreModuleArray = [...this.modulesContainer.entries()]
.filter(
// eslint-disable-next-line @typescript-eslint/no-unused-vars
([_, { metatype }]) => metatype && metatype.name === InternalCoreModule.name)
// eslint-disable-next-line @typescript-eslint/no-unused-vars
.map(([_, value]) => value);
this._coreModuleRef = coreModuleArray[0];
}
if (!this._coreModuleRef) {
return;
}
const wrapper = this._coreModuleRef.getProviderByKey(REQUEST);
wrapper.setInstanceByContextId(contextId, {
instance: request,
isResolved: true,
});
}
exploreControllers() {
const scannedModules = new Set();
const controllers = new Set();
const scanModule = (module) => {
if (scannedModules.has(module))
return;
scannedModules.add(module);
for (const mm of module.imports.values()) {
scanModule(mm);
}
for (const wrapper of module.controllers.values()) {
if (wrapper.instance &&
typeof wrapper.instance === 'object' &&
wrapper.instance.constructor &&
Reflect.getMetadata(RPC_CONTROLLER_METADATA, wrapper.instance.constructor) &&
!controllers.has(wrapper)) {
controllers.add({ module, wrapper });
}
if (wrapper.host)
scanModule(wrapper.host);
}
};
for (const module of this.modulesContainer.values()) {
scanModule(module);
}
return Array.from(controllers);
}
};
RpcControllerFactory = __decorate([
Injectable(),
__metadata("design:paramtypes", [ModulesContainer,
ExternalContextCreator])
], RpcControllerFactory);
export { RpcControllerFactory };