Browse Source

feat: Add module dependency logic

Owen Diffey 1 year ago
parent
commit
0a58a5f84d

+ 11 - 0
backend/src/BaseModule.ts

@@ -1,5 +1,6 @@
 import LogBook, { Log } from "./LogBook";
 import ModuleManager from "./ModuleManager";
+import { Modules } from "./types/Modules";
 
 export enum ModuleStatus {
 	LOADED = "LOADED",
@@ -20,6 +21,8 @@ export default abstract class BaseModule {
 
 	protected status: ModuleStatus;
 
+	protected dependentModules: (keyof Modules)[];
+
 	/**
 	 * Base Module
 	 *
@@ -30,6 +33,7 @@ export default abstract class BaseModule {
 		this.logBook = LogBook.getPrimaryInstance();
 		this.name = name;
 		this.status = ModuleStatus.LOADED;
+		this.dependentModules = [];
 		this.log(`Module (${this.name}) loaded`);
 	}
 
@@ -60,6 +64,13 @@ export default abstract class BaseModule {
 		this.status = status;
 	}
 
+	/**
+	 * getDependentModules - Get module dependencies
+	 */
+	public getDependentModules() {
+		return this.dependentModules;
+	}
+
 	/**
 	 * startup - Startup module
 	 */

+ 46 - 15
backend/src/ModuleManager.ts

@@ -61,26 +61,57 @@ export default class ModuleManager {
 	}
 
 	/**
-	 * startup - Handle startup
+	 * startModule - Start module
 	 */
-	public async startup() {
-		await this.loadModules().catch(async err => {
-			await this.shutdown();
+	private async startModule(module: Modules[keyof Modules]) {
+		switch (module.getStatus()) {
+			case ModuleStatus.STARTING:
+			case ModuleStatus.STARTED:
+				return;
+			case ModuleStatus.ERROR:
+				throw new Error("Dependent module failed to start");
+			case ModuleStatus.STOPPING:
+			case ModuleStatus.STOPPED:
+			case ModuleStatus.DISABLED:
+				throw new Error("Dependent module is unavailable");
+			default:
+				break;
+		}
+
+		for (const name of module.getDependentModules()) {
+			const dependency = this.getModule(name);
+
+			if (!dependency) throw new Error("Dependent module not found");
+
+			// eslint-disable-next-line no-await-in-loop
+			await this.startModule(dependency);
+		}
+
+		await module.startup().catch(async err => {
+			module.setStatus(ModuleStatus.ERROR);
 			throw err;
 		});
-		if (!this.modules) throw new Error("No modules were loaded");
-		await Promise.all(
-			Object.values(this.modules).map(async module => {
-				await module.startup().catch(async err => {
-					module.setStatus(ModuleStatus.ERROR);
-					throw err;
-				});
-			})
-		).catch(async err => {
+	}
+
+	/**
+	 * startup - Handle startup
+	 */
+	public async startup() {
+		try {
+			await this.loadModules();
+
+			if (!this.modules) throw new Error("No modules were loaded");
+
+			for (const module of Object.values(this.modules)) {
+				// eslint-disable-next-line no-await-in-loop
+				await this.startModule(module);
+			}
+
+			JobQueue.getPrimaryInstance().resume();
+		} catch (err) {
 			await this.shutdown();
 			throw err;
-		});
-		JobQueue.getPrimaryInstance().resume();
+		}
 	}
 
 	/**

+ 1 - 0
backend/src/modules/DataModule.ts

@@ -34,6 +34,7 @@ export default class DataModule extends BaseModule {
 	public constructor() {
 		super("data");
 
+		this.dependentModules = ["events"];
 		this.jobQueue = JobQueue.getPrimaryInstance();
 	}
 

+ 2 - 0
backend/src/modules/StationModule.ts

@@ -8,6 +8,8 @@ export default class StationModule extends BaseModule {
 	 */
 	public constructor() {
 		super("stations");
+
+		this.dependentModules = ["data"];
 	}
 
 	/**