All files / microservices/client client-proxy.ts

96.67% Statements 29/30
93.33% Branches 14/15
92.31% Functions 12/13
96.43% Lines 27/28
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 931x 1x 1x               1x 1x 1x               1x     51x           5x 1x   4x     2x 2x 2x                           4x 3x 1x 2x 1x   1x         5x 5x               4x         4x 4x               124x       11x      
import { randomStringGenerator } from '@nestjs/common/utils/random-string-generator.util';
import { isNil, isString } from '@nestjs/common/utils/shared.utils';
import {
  defer,
  fromEvent,
  merge,
  Observable,
  Observer,
  throwError as _throw,
} from 'rxjs';
import { map, mergeMap, take } from 'rxjs/operators';
import { CONNECT_EVENT, ERROR_EVENT } from '../constants';
import { InvalidMessageException } from '../exceptions/errors/invalid-message.exception';
import {
  ClientOptions,
  PacketId,
  ReadPacket,
  WritePacket,
} from '../interfaces';
 
export abstract class ClientProxy {
  public abstract connect(): Promise<any>;
  public abstract close(): any;
  protected routingMap = new Map<string, Function>();
 
  public send<TResult = any, TInput = any>(
    pattern: any,
    data: TInput,
  ): Observable<TResult> {
    if (isNil(pattern) || isNil(data)) {
      return _throw(new InvalidMessageException());
    }
    return defer(async () => this.connect()).pipe(
      mergeMap(
        () =>
          new Observable((observer: Observer<TResult>) => {
            const callback = this.createObserver(observer);
            return this.publish({ pattern, data }, callback);
          }),
      ),
    );
  }
 
  protected abstract publish(
    packet: ReadPacket,
    callback: (packet: WritePacket) => void,
  ): Function | void;
 
  protected createObserver<T>(
    observer: Observer<T>,
  ): (packet: WritePacket) => void {
    return ({ err, response, isDisposed }: WritePacket) => {
      if (err) {
        return observer.error(err);
      } else if (isDisposed) {
        return observer.complete();
      }
      observer.next(response);
    };
  }
 
  protected assignPacketId(packet: ReadPacket): ReadPacket & PacketId {
    const id = randomStringGenerator();
    return Object.assign(packet, { id });
  }
 
  protected connect$(
    instance: any,
    errorEvent = ERROR_EVENT,
    connectEvent = CONNECT_EVENT,
  ): Observable<any> {
    const error$ = fromEvent(instance, errorEvent).pipe(
      map(err => {
        throw err;
      }),
    );
    const connect$ = fromEvent(instance, connectEvent);
    return merge(error$, connect$).pipe(take(1));
  }
 
  protected getOptionsProp<T extends { options? }>(
    obj: ClientOptions['options'],
    prop: keyof T['options'],
    defaultValue = undefined,
  ) {
    return obj ? obj[prop as string] : defaultValue;
  }
 
  protected normalizePattern<T = any>(pattern: T): string {
    return isString(pattern) ? pattern : JSON.stringify(pattern);
  }
}