1+ import { isUndefined } from '@nestjs/common/utils/shared.utils' ;
2+ import { Observable } from 'rxjs' ;
13import { Logger } from '@nestjs/common/services/logger.service' ;
24import {
35 KAFKA_DEFAULT_BROKER ,
@@ -8,18 +10,29 @@ import {
810 KafkaConfig ,
911 Kafka ,
1012 Consumer ,
13+ Producer ,
1114 EachMessagePayload ,
15+ KafkaMessage ,
16+ Message ,
1217 logLevel
1318} from '../external/kafka.interface' ;
14- import { CustomTransportStrategy , KafkaOptions } from '../interfaces' ;
19+ import { CustomTransportStrategy , KafkaOptions , ReadPacket , PacketId } from '../interfaces' ;
20+ import { KafkaHeaders } from '../enums' ;
1521import { Server } from './server' ;
22+ import { partition } from 'rxjs/operators' ;
23+
24+ interface KafkaPacket {
25+ replyTopic ?: string ;
26+ replyPartition ?: number ;
27+ }
1628
1729let kafkaPackage : any = { } ;
1830
1931export class ServerKafka extends Server implements CustomTransportStrategy {
2032 protected readonly logger = new Logger ( ServerKafka . name ) ;
2133 private client : Kafka = null ;
2234 private consumer : Consumer = null ;
35+ private producer : Producer = null ;
2336 private readonly brokers : string [ ] ;
2437 private readonly clientId : string ;
2538 private readonly groupId : string ;
@@ -40,16 +53,22 @@ export class ServerKafka extends Server implements CustomTransportStrategy {
4053
4154 public close ( ) : void {
4255 this . consumer && this . consumer . disconnect ( ) ;
56+ this . producer && this . producer . disconnect ( ) ;
4357 this . consumer = null ;
58+ this . producer = null ;
4459 this . client = null ;
4560 }
4661
4762 public async start ( callback : ( ) => void ) : Promise < void > {
63+ // create consumer and producer
4864 this . consumer = this . client . consumer ( Object . assign ( this . options . consumer || { } , {
4965 groupId : this . groupId
5066 } ) ) ;
5167
68+ this . producer = this . client . producer ( this . options . producer ) ;
69+
5270 await this . consumer . connect ( ) ;
71+ await this . producer . connect ( ) ;
5372 await this . bindEvents ( this . consumer ) ;
5473 callback ( ) ;
5574 }
@@ -109,9 +128,87 @@ export class ServerKafka extends Server implements CustomTransportStrategy {
109128 public async handleMessage (
110129 payload : EachMessagePayload
111130 ) {
112- return this . handleEvent ( payload . topic , {
131+ const packet = this . deserialize ( payload ) ;
132+ const handler = this . getHandlerByPattern ( packet . pattern ) ;
133+
134+ if ( handler . isEventHandler ) {
135+ return this . handleEvent ( packet . pattern , packet ) ;
136+ }
137+
138+ // message handlers need at least a correlation id and a reply topic
139+ if ( isUndefined ( packet . id ) || isUndefined ( packet . replyTopic ) ) {
140+ throw new Error ( 'Messaging not available' ) ;
141+ }
142+
143+ const response$ = this . transformToObservable (
144+ await handler ( packet . data ) ,
145+ ) as Observable < any > ;
146+
147+ const publish = < T > ( data : T ) =>
148+ this . sendMessage (
149+ data as T & Message ,
150+ packet . replyTopic ,
151+ packet . replyPartition ,
152+ packet . id
153+ ) ;
154+
155+ response$ && this . send ( response$ , publish ) ;
156+ }
157+
158+ private deserialize ( payload : EachMessagePayload ) : KafkaPacket & ReadPacket < Message > & PacketId {
159+ // build
160+ const packet = {
161+ id : undefined ,
162+ replyTopic : undefined ,
163+ replyPartition : undefined ,
113164 pattern : payload . topic ,
114- data : payload
165+ data : Object . assign ( payload . message , {
166+ topic : payload . topic ,
167+ partition : payload . partition
168+ } )
169+ } ;
170+
171+ // parse the correlation id
172+ if ( ! isUndefined ( packet . data . headers [ KafkaHeaders . CORRELATION_ID ] ) ) {
173+ // assign the correlation id as the packet id
174+ packet . id = packet . data . headers [ KafkaHeaders . CORRELATION_ID ] . toString ( ) ;
175+
176+ // parse the topic and partition
177+ if ( ! isUndefined ( packet . data . headers [ KafkaHeaders . REPLY_TOPIC ] ) ) {
178+ packet . replyTopic = packet . data . headers [ KafkaHeaders . REPLY_TOPIC ] . toString ( ) ;
179+ }
180+
181+ if ( ! isUndefined ( packet . data . headers [ KafkaHeaders . REPLY_PARTITION ] ) ) {
182+ packet . replyPartition = parseFloat ( packet . data . headers [ KafkaHeaders . REPLY_PARTITION ] . toString ( ) ) ;
183+ }
184+ }
185+
186+ return packet ;
187+ }
188+
189+ public sendMessage < T = any > (
190+ message : T & Message ,
191+ replyTopic : string ,
192+ replyPartition : number ,
193+ correlationId : string
194+ ) : void {
195+ // assign partition
196+ message = Object . assign ( message , {
197+ partition : replyPartition || undefined
115198 } ) ;
199+
200+ // create headers if they don't exist
201+ if ( isUndefined ( message . headers ) ) {
202+ message . headers = { } ;
203+ }
204+
205+ // assign the correlation id
206+ message . headers [ KafkaHeaders . CORRELATION_ID ] = Buffer . from ( correlationId ) ;
207+
208+ // send
209+ this . producer . send ( Object . assign ( {
210+ topic : replyTopic ,
211+ messages : [ message ]
212+ } , this . options . send || { } ) ) ;
116213 }
117214}
0 commit comments