# 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));
      }
    }