Context
In my project, I have a gateway REST application and several microservices that communicate via Azure Service Bus. I also use interceptors to handle some cross-cutting concerns in my services. However, I am facing an issue where the controllers in my microservices do not work when an interceptor is attached. When I comment out the interceptor, the controllers function as expected.
Issue
Whenever I send a message to a service from the gateway, the controller doesn't work if an interceptor is attached. When I comment out the interceptor from the service, the controller works as expected. I have already simplified the interceptor to the bare minimum and ensured that ClientProxyService is correctly injected.
Example response when interceptor disabled
RESULT{"maskedGsm":"53******45"}
Example response when interceptor disabled
RESULT{"source":{"source":{}}}
Detailed Description
Here is a brief overview of my setup:
Interceptor Code:
@Injectable()export class AnalyticInterceptor implements NestInterceptor { constructor(private readonly client: ClientProxyService) {} intercept(context: ExecutionContext, next: CallHandler): Observable<any> { const eventName = context.getHandler().name; if (eventName === 'validate') { return next.handle(); } const message = context.switchToRpc().getData(); const sicil = message.id; delete message.sicil; return next.handle().pipe( map((data) => { const payload: IncomingEventPayload = { eventName: eventName, data: data, event_date: new Date(), sicil: sicil, }; this.client.emitEvent('auth-event', payload); return data; }), catchError((err) => { const payload: IncomingEventErrorPayload = { eventName: eventName, err: err, err_date: new Date(), sicil: sicil, }; this.client.emitEvent('auth-error', payload); throw err; }) ); }}interface IncomingEventPayload { eventName: string; data: any; event_date: Date; sicil: number;}interface IncomingEventErrorPayload { eventName: string; err: RpcException; err_date: Date; sicil: number;}
Custom Transport Strategy Code
export class AzureServiceBusClient extends Server implements CustomTransportStrategy { private serviceBusClient: ServiceBusClient; private queueSender: ServiceBusSender; private queueReceiver: ServiceBusReceiver; constructor(private readonly queueName: string) { super(); this.serviceBusClient = new ServiceBusClient('Endpoint=sb://<namespace>.servicebus.windows.net/;SharedAccessKeyName=<KeyName>;SharedAccessKey=<AccessKey>'); this.queueSender = this.serviceBusClient.createSender(this.queueName); this.queueReceiver = this.serviceBusClient.createReceiver(this.queueName); } async connect() {} async close() { await this.queueReceiver.close(); await this.queueSender.close(); } async dispatchEvent(packet: ReadPacket<any>): Promise<any> { await this.queueSender.sendMessages({ body: packet.data, sessionId: packet.pattern, }); } publish(packet: ReadPacket<any>, callback: (packet: WritePacket<any>) => void): () => void { const subscription = this.queueReceiver.subscribe({ processMessage: async (message: any) => { if (message.body.pattern === packet.pattern) { callback({ response: message.body.result }); } }, processError: async (args) => { console.error('Error receiving message', args); }, }); this.dispatchEvent(packet); return () => { subscription.close(); }; } listen(callback: () => void) { this.queueReceiver.subscribe({ processMessage: async (brokeredMessage) => { const handler = this.getHandlerByPattern(brokeredMessage.sessionId); if (!handler) { console.error(`No handlers for pattern ${brokeredMessage.subject}`); return; } else { const result = await handler(this.deserialize(brokeredMessage).data); await this.queueSender.sendMessages({ body: result, sessionId: brokeredMessage.sessionId, }); } }, processError: async (err) => { console.error(err); } }); callback(); } private deserialize(message: any): any { return { pattern: message.subject, data: message.body, }; }}
Example Controller
@Controller()@UseInterceptors(AnalyticInterceptor) // this broken my transport strategy export class AuthController { constructor(private readonly authService: AuthService) {} @MessagePattern('login') async login(@Payload() loginDTO: LoginDTO): Promise<any> { return this.authService.login(loginDTO); } @MessagePattern('getOtp') async getOtp(@Payload() getOtpDto: GetOtpDTO): Promise<any> { return this.authService.getOtp(getOtpDto); } @MessagePattern('loginWithGsm') async loginWithGsm(@Payload() loginWithGsmDto: LoginViaGsmDTO): Promise<any> { return this.authService.loginWithGsm(loginWithGsmDto); } @MessagePattern('checkOtp') async checkOTP(@Payload() checkOtpDTO: CheckOtpDTO): Promise<any> { return this.authService.checkOtp(checkOtpDTO); } @MessagePattern('refresh') async refresh(@Payload() refreshDto: RefreshDTO): Promise<any> { return this.authService.refresh(refreshDto); } @MessagePattern('validate') async validate(@Payload() validateTokenDto: ValidateTokenDto): Promise<any> { return this.authService.validate(validateTokenDto); } @MessagePattern('logout') async logout(@Payload() logoutDto: LogoutDTO): Promise<any> { return this.authService.logout(logoutDto); }}