rxstack/rxstack

View on GitHub
packages/core/src/kernel/kernel.ts

Summary

Maintainability
A
0 mins
Test Coverage
A
100%
import {HttpDefinition, WebSocketDefinition} from './interfaces';
import {Injectable, Injector} from 'injection-js';
import {Request, Response} from './models';
import {AsyncEventDispatcher} from '@rxstack/async-event-dispatcher';
import {Exception, transformToException} from '@rxstack/exceptions';
import {KernelEvents} from './kernel-events';
import {RequestEvent, ResponseEvent, ExceptionEvent} from './events';
import {InjectorAwareInterface} from '../application';
import {HttpMetadata, httpMetadataStorage, WebSocketMetadata, webSocketMetadataStorage} from './metadata';

@Injectable()
export class Kernel implements InjectorAwareInterface {

  httpDefinitions: HttpDefinition[] = [];

  webSocketDefinitions: WebSocketDefinition[] = [];

  private injector: Injector;

  setInjector(injector: Injector): void {
    this.injector = injector;
  }

  initialize(): void {
    httpMetadataStorage.all().forEach((metadata: HttpMetadata) => this.registerDefinition(metadata));
    webSocketMetadataStorage.all().forEach((metadata: WebSocketMetadata) => this.registerDefinition(metadata));
  }

  reset(): void {
    this.httpDefinitions = [];
    this.webSocketDefinitions = [];
  }

  private registerDefinition(metadata: HttpMetadata|WebSocketMetadata): void {
    // controller instance
    const controller: Record<string, any> = this.injector.get(metadata.target, false);
    if (!controller) {
      return;
    }
    if (metadata.transport === 'HTTP') {
      this.pushHttpDefinition(<HttpMetadata>metadata, controller);
    } else {
      this.pushWebSocketDefinition(<WebSocketMetadata>metadata, controller);
    }
  }

  private pushHttpDefinition(metadata: HttpMetadata, controller: Record<string, any>): void {
    const path = `${metadata.path}`.replace(new RegExp('/*$'), '');
    this.httpDefinitions.push({
      path: path,
      name: metadata.name,
      method: metadata.httpMethod,
      handler: async (request: Request): Promise<Response> => {
        request.method = metadata.httpMethod;
        request.path = path;
        request.routeName = metadata.name;
        request.controller = controller;
        return this.process(request, controller, metadata.propertyKey);
      }
    });
  }

  private pushWebSocketDefinition(metadata: WebSocketMetadata, controller: Record<string, any>): void {
    this.webSocketDefinitions.push({
      name: metadata.name,
      handler: async (request: Request): Promise<Response> => {
        request.routeName = metadata.name;
        request.controller = controller;
        return this.process(request, controller, metadata.propertyKey);
      }
    });
  }

  private async process(request: Request, controller: Record<string, any>, propertyKey: string): Promise<Response> {
    let response: Response;
    try {
      const requestEvent = new RequestEvent(request);
      await this.injector.get(AsyncEventDispatcher).dispatch(KernelEvents.KERNEL_REQUEST, requestEvent);

      if (requestEvent.hasResponse()) {
        return await this.handleResponse(requestEvent.getResponse(), request);
      }
      // call controller
      response = await controller[propertyKey].call(controller, request);
      return await this.handleResponse(response, request);
    } catch (e) {
      return await this.handleException(transformToException(e), request);
    }
  }

  private async handleResponse(response: Response, request: Request): Promise<Response> {
    try {
      const responseEvent = new ResponseEvent(request, response);
      await this.injector.get(AsyncEventDispatcher).dispatch(KernelEvents.KERNEL_RESPONSE, responseEvent);
      return responseEvent.getResponse();
    } catch (e) {
      throw transformToException(e);
    }
  }

  private async handleException(exception: Exception, request: Request): Promise<Response> {
    try {
      const exceptionEvent = new ExceptionEvent(exception, request);
      await this.injector.get(AsyncEventDispatcher).dispatch(KernelEvents.KERNEL_EXCEPTION, exceptionEvent);
      if (exceptionEvent.hasResponse()) {
        return await this.handleResponse(exceptionEvent.getResponse(), request);
      }
      throw exceptionEvent.getException();
    } catch (e) {
      throw transformToException(e);
    }
  }
}