@nestjs/microservices
Version:
Nest - modern, fast, powerful node.js web framework (@microservices)
172 lines (171 loc) • 8.54 kB
JavaScript
import { forkJoin, from as fromPromise, isObservable, mergeMap, of, } from 'rxjs';
import { RequestContextHost } from './context/request-context-host.js';
import { DEFAULT_CALLBACK_METADATA, DEFAULT_GRPC_CALLBACK_METADATA, } from './context/rpc-metadata-constants.js';
import { Transport } from './enums/index.js';
import { ListenerMetadataExplorer, } from './listener-metadata-explorer.js';
import { ServerGrpc } from './server/index.js';
import { isUndefined } from '@nestjs/common/internal';
import { ContextIdFactory, MetadataScanner, } from '@nestjs/core';
import { ExecutionContextHost, STATIC_CONTEXT, REQUEST_CONTEXT_ID, } from '@nestjs/core/internal';
export class ListenersController {
clientsContainer;
contextCreator;
container;
injector;
clientFactory;
exceptionFiltersContext;
graphInspector;
metadataExplorer = new ListenerMetadataExplorer(new MetadataScanner());
exceptionFiltersCache = new WeakMap();
constructor(clientsContainer, contextCreator, container, injector, clientFactory, exceptionFiltersContext, graphInspector) {
this.clientsContainer = clientsContainer;
this.contextCreator = contextCreator;
this.container = container;
this.injector = injector;
this.clientFactory = clientFactory;
this.exceptionFiltersContext = exceptionFiltersContext;
this.graphInspector = graphInspector;
}
registerPatternHandlers(instanceWrapper, serverInstance, moduleKey) {
const { instance } = instanceWrapper;
const isStatic = instanceWrapper.isDependencyTreeStatic();
const patternHandlers = this.metadataExplorer.explore(instance);
const moduleRef = this.container.getModuleByKey(moduleKey);
const defaultCallMetadata = serverInstance instanceof ServerGrpc
? DEFAULT_GRPC_CALLBACK_METADATA
: DEFAULT_CALLBACK_METADATA;
patternHandlers
.filter(({ transport }) => isUndefined(transport) ||
isUndefined(serverInstance.transportId) ||
transport === serverInstance.transportId)
.flatMap(handler => handler.patterns.map(pattern => ({
...handler,
patterns: [pattern],
})))
.forEach((definition) => {
const { patterns: [pattern], targetCallback, methodKey, extras, isEventHandler, } = definition;
this.insertEntrypointDefinition(instanceWrapper, definition, serverInstance.transportId);
if (isStatic) {
const proxy = this.contextCreator.create(instance, targetCallback, moduleKey, methodKey, STATIC_CONTEXT, undefined, defaultCallMetadata);
if (isEventHandler) {
const eventHandler = async (...args) => {
const originalArgs = args;
const [dataOrContextHost] = originalArgs;
if (dataOrContextHost instanceof RequestContextHost) {
args = args.slice(1, args.length);
}
const returnValue = proxy(...args);
return this.forkJoinHandlersIfAttached(returnValue, originalArgs, eventHandler);
};
return serverInstance.addHandler(pattern, eventHandler, isEventHandler, extras);
}
else {
return serverInstance.addHandler(pattern, proxy, isEventHandler, extras);
}
}
const asyncHandler = this.createRequestScopedHandler(instanceWrapper, pattern, moduleRef, moduleKey, methodKey, defaultCallMetadata, isEventHandler);
serverInstance.addHandler(pattern, asyncHandler, isEventHandler, extras);
});
}
insertEntrypointDefinition(instanceWrapper, definition, transportId) {
this.graphInspector.insertEntrypointDefinition({
type: 'microservice',
methodName: definition.methodKey,
className: instanceWrapper.metatype?.name,
classNodeId: instanceWrapper.id,
metadata: {
key: definition.patterns.toString(),
transportId: typeof transportId === 'number'
? Transport[transportId]
: transportId,
patterns: definition.patterns,
isEventHandler: definition.isEventHandler,
extras: definition.extras,
},
}, instanceWrapper.id);
}
forkJoinHandlersIfAttached(currentReturnValue, originalArgs, handlerRef) {
if (handlerRef.next) {
const returnedValueWrapper = handlerRef.next(...originalArgs);
return forkJoin({
current: this.transformToObservable(currentReturnValue),
next: this.transformToObservable(returnedValueWrapper),
});
}
return currentReturnValue;
}
assignClientsToProperties(instance) {
for (const { property, metadata, } of this.metadataExplorer.scanForClientHooks(instance)) {
const client = this.clientFactory.create(metadata);
this.clientsContainer.addClient(client);
this.assignClientToInstance(instance, property, client);
}
}
assignClientToInstance(instance, property, client) {
Reflect.set(instance, property, client);
}
createRequestScopedHandler(wrapper, pattern, moduleRef, moduleKey, methodKey, defaultCallMetadata = DEFAULT_CALLBACK_METADATA, isEventHandler = false) {
const collection = moduleRef.controllers;
const { instance } = wrapper;
const isTreeDurable = wrapper.isDependencyTreeDurable();
const requestScopedHandler = async (...args) => {
try {
let contextId;
let [dataOrContextHost] = args;
if (dataOrContextHost instanceof RequestContextHost) {
contextId = this.getContextId(dataOrContextHost, isTreeDurable);
args.shift();
}
else {
const [data, reqCtx] = args;
const request = RequestContextHost.create(pattern, data, reqCtx);
contextId = this.getContextId(request, isTreeDurable);
dataOrContextHost = request;
}
const contextInstance = await this.injector.loadPerContext(instance, moduleRef, collection, contextId);
const proxy = this.contextCreator.create(contextInstance, contextInstance[methodKey], moduleKey, methodKey, contextId, wrapper.id, defaultCallMetadata);
const returnValue = proxy(...args);
if (isEventHandler) {
return this.forkJoinHandlersIfAttached(returnValue, [dataOrContextHost, ...args], requestScopedHandler);
}
return returnValue;
}
catch (err) {
let exceptionFilter = this.exceptionFiltersCache.get(instance[methodKey]);
if (!exceptionFilter) {
exceptionFilter = this.exceptionFiltersContext.create(instance, instance[methodKey], moduleKey);
this.exceptionFiltersCache.set(instance[methodKey], exceptionFilter);
}
const host = new ExecutionContextHost(args);
host.setType('rpc');
return exceptionFilter.handle(err, host);
}
};
return requestScopedHandler;
}
getContextId(request, isTreeDurable) {
const contextId = ContextIdFactory.getByRequest(request);
if (!request[REQUEST_CONTEXT_ID]) {
Object.defineProperty(request, REQUEST_CONTEXT_ID, {
value: contextId,
enumerable: false,
writable: false,
configurable: false,
});
const requestProviderValue = isTreeDurable
? contextId.payload
: Object.assign(request, contextId.payload);
this.container.registerRequestProvider(requestProviderValue, contextId);
}
return contextId;
}
transformToObservable(resultOrDeferred) {
if (resultOrDeferred instanceof Promise) {
return fromPromise(resultOrDeferred).pipe(mergeMap(val => (isObservable(val) ? val : of(val))));
}
if (isObservable(resultOrDeferred)) {
return resultOrDeferred;
}
return of(resultOrDeferred);
}
}