src/dependency/dependency.manager.ts
import { FastifyInstance } from "fastify";
import { AuthLoader } from "../auth/auth.loader";
import { RepositoryLoader } from "../database/repository.loader";
import { AUTH, CONTROLLER, MAIN_COMPONENTS, REPOSITORY, SERVICE, SOCKET, WORKER } from "../definitions";
import { ControllersLoader } from "../routing/controllers/controller.loader";
import { ServicesLoader } from "../services/services.loader";
import SocketLoader from "../sockets/socket.loader";
import { Constructor, Instance } from "../types";
import { ILoader, inject } from "../utils/directory.loader";
import { CustomLoader } from "../utils/loaders/custom.loader";
import { WorkerLoader } from "../worker/worker.loader";
import DependencyComposer from "./dependency.composer";
export enum DepType {
Controller,
Service,
Repository,
Socket,
Auth,
Worker,
Custom,
}
export const DepMap = {
[CONTROLLER]: DepType.Controller,
[REPOSITORY]: DepType.Repository,
[SERVICE]: DepType.Service,
[WORKER]: DepType.Worker,
[SOCKET]: DepType.Socket,
[AUTH]: DepType.Auth,
};
export interface Options {
sources: string | string[];
}
interface RootDeps {
app: FastifyInstance;
socketio?: SocketIO.Server;
}
interface Loaders {
[index: string]: ILoader;
}
interface Queues {
[index: string]: Array<[Constructor, string]>;
}
export class DependencyManager {
public static getManager() {
if (!this.manager) {
this.manager = new DependencyManager();
}
return this.manager;
}
private static manager: DependencyManager;
private loaders: Loaders;
private queues: Queues;
private roots: RootDeps;
private constructor() {
this.queues = this.instansiateQueues();
}
public classify(sources: string | string[]): void {
inject(sources, (target, path) => this.queues[this.getType(target)].push([ target, path ]));
}
public getDeps(type: DepType) {
return this.queues[type];
}
public applyRoots(rootDeps: RootDeps) {
this.roots = rootDeps;
}
public async compose(): Promise<void> {
this.loaders = this.instansiateLoaders();
await this.processPart(DepType.Repository);
// Log.completion(`${this.queues[DepType.Auth].length} Repositories was successfully loaded`);
await this.processPart(DepType.Service);
// Log.completion(`${this.queues[DepType.Service].length} Services were successfully loaded`);
await this.processPart(DepType.Worker);
// Log.completion(`${this.queues[DepType.Worker].length} Workers were successfully loaded`);
await this.processPart(DepType.Auth);
// Log.completion(`${this.queues[DepType.Auth].length} Auth was successfully loaded`);
await this.processPart(DepType.Socket);
// Log.completion(`${this.queues[DepType.Socket].length} Sockets was successfully loaded`);
await this.processPart(DepType.Controller);
// Log.completion(`${this.queues[DepType.Controller].length} Controllers were successfully loaded`);
}
public async processDep<T>(dep: Constructor<T>): Promise<Instance<T>> {
if (!this.loaders) {
this.loaders = this.instansiateLoaders();
}
const type = this.getType(dep);
const processor = await this.getLoader(type, true).processBase();
return processor(dep);
}
private async processPart(key: DepType): Promise<void> {
const loader = this.getLoader(key);
if (!loader) {
return;
}
const processor = await loader.processBase();
for (const [ classType, filePath ] of this.queues[key]) {
await processor(classType, filePath);
}
}
private instansiateLoaders(): Loaders {
const dependencyComposer = DependencyComposer.getComposer();
const { app, socketio } = this.roots;
const loaders: Loaders = ({
[DepType.Auth]: new AuthLoader({ dependencyComposer }),
[DepType.Service]: new ServicesLoader({ dependencyComposer }),
[DepType.Controller]: new ControllersLoader({ dependencyComposer, app }),
[DepType.Worker]: new WorkerLoader({ dependencyComposer}),
[DepType.Repository]: new RepositoryLoader({ dependencyComposer }),
[DepType.Custom]: new CustomLoader(),
});
if (socketio) {
loaders[DepType.Socket] = new SocketLoader({ dependencyComposer, socketio });
}
return loaders;
}
private instansiateQueues(): Queues {
return {
[DepType.Auth]: [],
[DepType.Service]: [],
[DepType.Controller]: [],
[DepType.Repository]: [],
[DepType.Worker]: [],
[DepType.Socket]: [],
};
}
private getType(target: Instance | Function): DepType {
const belongsTo = this.getRefer(target);
for (const type of MAIN_COMPONENTS) {
if (belongsTo(type)) {
return DepMap[type];
}
}
return DepType.Custom;
}
private getRefer(target: Instance | Function): (key: string) => boolean {
return (identifier: string) => Reflect.hasMetadata(identifier, target);
}
private getLoader(key: DepType, isSingle: boolean = false) {
if ((isSingle && !this.loaders[key]) || (!isSingle && !this.loaders[key] && this.queues[key].length > 0)) {
throw Error(`${DepType[key]} processor doesn't exist. Install all required dependencies and fill configuration`);
}
return this.loaders[key];
}
}