All files / microservices/server server-rmq.ts

93.18% Statements 41/44
100% Branches 18/18
76.92% Functions 10/13
95.35% Lines 41/43
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 1191x   1x                         1x   1x   1x 12x 12x             12x 12x 12x     12x     12x     12x     12x       12x 12x       3x       2x 2x       3x 3x 3x   3x     3x                   4x 4x 4x     4x       2x 2x 2x 2x   2x 1x 1x           1x       1x 2x   1x               1x 1x      
import { loadPackage } from '@nestjs/common/utils/load-package.util';
import { Observable } from 'rxjs';
import {
  CONNECT_EVENT,
  DISCONNECTED_RMQ_MESSAGE,
  DISCONNECT_EVENT,
  NO_PATTERN_MESSAGE,
  RQM_DEFAULT_IS_GLOBAL_PREFETCH_COUNT,
  RQM_DEFAULT_PREFETCH_COUNT,
  RQM_DEFAULT_QUEUE,
  RQM_DEFAULT_QUEUE_OPTIONS,
  RQM_DEFAULT_URL,
} from '../constants';
import { CustomTransportStrategy, RmqOptions } from '../interfaces';
import { MicroserviceOptions } from '../interfaces/microservice-configuration.interface';
import { Server } from './server';
 
let rqmPackage: any = {};
 
export class ServerRMQ extends Server implements CustomTransportStrategy {
  private server: any = null;
  private channel: any = null;
  private readonly urls: string[];
  private readonly queue: string;
  private readonly prefetchCount: number;
  private readonly queueOptions: any;
  private readonly isGlobalPrefetchCount: boolean;
 
  constructor(private readonly options: MicroserviceOptions) {
    super();
    this.urls = this.getOptionsProp<RmqOptions>(this.options, 'urls') || [
      RQM_DEFAULT_URL,
    ];
    this.queue =
      this.getOptionsProp<RmqOptions>(this.options, 'queue') ||
      RQM_DEFAULT_QUEUE;
    this.prefetchCount =
      this.getOptionsProp<RmqOptions>(this.options, 'prefetchCount') ||
      RQM_DEFAULT_PREFETCH_COUNT;
    this.isGlobalPrefetchCount =
      this.getOptionsProp<RmqOptions>(this.options, 'isGlobalPrefetchCount') ||
      RQM_DEFAULT_IS_GLOBAL_PREFETCH_COUNT;
    this.queueOptions =
      this.getOptionsProp<RmqOptions>(this.options, 'queueOptions') ||
      RQM_DEFAULT_QUEUE_OPTIONS;
 
    loadPackage('amqplib', ServerRMQ.name);
    rqmPackage = loadPackage('amqp-connection-manager', ServerRMQ.name);
  }
 
  public async listen(callback: () => void): Promise<void> {
    await this.start(callback);
  }
 
  public close(): void {
    this.channel && this.channel.close();
    this.server && this.server.close();
  }
 
  public async start(callback?: () => void) {
    this.server = this.createClient();
    this.server.on(CONNECT_EVENT, _ => {
      this.channel = this.server.createChannel({
        json: false,
        setup: channel => this.setupChannel(channel, callback),
      });
    });
    this.server.on(DISCONNECT_EVENT, err => {
      this.logger.error(DISCONNECTED_RMQ_MESSAGE);
    });
  }
 
  public createClient<T = any>(): T {
    return rqmPackage.connect(this.urls);
  }
 
  public async setupChannel(channel: any, callback: Function) {
    await channel.assertQueue(this.queue, this.queueOptions);
    await channel.prefetch(this.prefetchCount, this.isGlobalPrefetchCount);
    channel.consume(this.queue, msg => this.handleMessage(msg), {
      noAck: true,
    });
    callback();
  }
 
  public async handleMessage(message: any): Promise<void> {
    const { content, properties } = message;
    const packet = JSON.parse(content.toString());
    const pattern = JSON.stringify(packet.pattern);
    const handler = this.getHandlerByPattern(pattern);
 
    if (!handler) {
      const status = 'error';
      return this.sendMessage(
        { status, err: NO_PATTERN_MESSAGE },
        properties.replyTo,
        properties.correlationId,
      );
    }
    const response$ = this.transformToObservable(
      await handler(packet.data),
    ) as Observable<any>;
 
    const publish = data =>
      this.sendMessage(data, properties.replyTo, properties.correlationId);
 
    response$ && this.send(response$, publish);
  }
 
  public sendMessage<T = any>(
    message: T,
    replyTo: any,
    correlationId: string,
  ): void {
    const buffer = Buffer.from(JSON.stringify(message));
    this.channel.sendToQueue(replyTo, buffer, { correlationId });
  }
}