All files / microservices/server server-mqtt.ts

95% Statements 38/40
100% Branches 8/8
88.89% Functions 16/18
97.3% Lines 36/37
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  1x                         1x   1x   1x       16x 16x 16x     16x       3x 3x       3x 3x   3x       4x 4x 4x 1x         1x               6x               2x 2x 2x 2x   2x 1x   1x 1x     1x       3x 1x             4x 4x   2x         2x       2x       3x      
import { Observable } from 'rxjs';
import {
  CONNECT_EVENT,
  ERROR_EVENT,
  MESSAGE_EVENT,
  MQTT_DEFAULT_URL,
  NO_PATTERN_MESSAGE,
} from '../constants';
import { MqttClient } from '../external/mqtt-client.interface';
import { CustomTransportStrategy, PacketId, ReadPacket } from '../interfaces';
import {
  MicroserviceOptions,
  MqttOptions,
} from '../interfaces/microservice-configuration.interface';
import { Server } from './server';
 
let mqttPackage: any = {};
 
export class ServerMqtt extends Server implements CustomTransportStrategy {
  private readonly url: string;
  private mqttClient: MqttClient;
 
  constructor(private readonly options: MicroserviceOptions['options']) {
    super();
    this.url =
      this.getOptionsProp<MqttOptions>(options, 'url') || MQTT_DEFAULT_URL;
 
    mqttPackage = this.loadPackage('mqtt', ServerMqtt.name);
  }
 
  public async listen(callback: () => void) {
    this.mqttClient = this.createMqttClient();
    this.start(callback);
  }
 
  public start(callback?: () => void) {
    this.handleError(this.mqttClient);
    this.bindEvents(this.mqttClient);
 
    this.mqttClient.on(CONNECT_EVENT, callback);
  }
 
  public bindEvents(mqttClient: MqttClient) {
    mqttClient.on(MESSAGE_EVENT, this.getMessageHandler(mqttClient).bind(this));
    const registeredPatterns = Object.keys(this.messageHandlers);
    registeredPatterns.forEach(pattern =>
      mqttClient.subscribe(this.getAckQueueName(pattern)),
    );
  }
 
  public close() {
    this.mqttClient && this.mqttClient.end();
  }
 
  public createMqttClient(): MqttClient {
    return mqttPackage.connect(this.url, this.options as MqttOptions);
  }
 
  public getMessageHandler(pub: MqttClient): any {
    return async (channel, buffer) => this.handleMessage(channel, buffer, pub);
  }
 
  public async handleMessage(
    channel: string,
    buffer: Buffer,
    pub: MqttClient,
  ): Promise<any> {
    const packet = this.deserialize(buffer.toString());
    const pattern = channel.replace(/_ack$/, '');
    const publish = this.getPublisher(pub, pattern, packet.id);
    const status = 'error';
 
    if (!this.messageHandlers[pattern]) {
      return publish({ id: packet.id, status, err: NO_PATTERN_MESSAGE });
    }
    const handler = this.messageHandlers[pattern];
    const response$ = this.transformToObservable(
      await handler(packet.data),
    ) as Observable<any>;
    response$ && this.send(response$, publish);
  }
 
  public getPublisher(client: MqttClient, pattern: any, id: string): any {
    return response =>
      client.publish(
        this.getResQueueName(pattern),
        JSON.stringify(Object.assign(response, { id })),
      );
  }
 
  public deserialize(content): ReadPacket & PacketId {
    try {
      return JSON.parse(content);
    } catch (e) {
      return content;
    }
  }
 
  public getAckQueueName(pattern: string): string {
    return `${pattern}_ack`;
  }
 
  public getResQueueName(pattern: string): string {
    return `${pattern}_res`;
  }
 
  public handleError(stream) {
    stream.on(ERROR_EVENT, err => this.logger.error(err));
  }
}