/** * @author Martin Karkowski * @email m.karkowski@zema.de * @create date 2020-11-11 13:27:58 * @modify date 2021-02-05 10:29:03 * @desc [description] */ import { ArgumentParser } from "argparse"; import { readFile } from "fs/promises"; import "reflect-metadata"; import { AmqpLayer } from "../communication/amqpLayer"; import { Bridge } from "../communication/bridge"; import { EventLayer } from "../communication/eventLayer"; import { IoSocketClient } from "../communication/IoSocketClient"; import { IoSocketServer } from "../communication/IoSocketServer"; import { getPackageLoader } from "../loader/getPackageLoader"; import { loadFunctions, loadPackages } from "../loader/loadPackages"; import { getNopeLogger } from "../logger/getLogger"; import { LoggerLevel, LoggerLevels } from "../logger/nopeLogger"; import { setGlobalLoggerLevel } from "../logger/setGlobalLoggerLevel"; import { ICommunicationBridge } from "../types/nope/nopeCommunication.interface"; import { INopeDispatcher } from "../types/nope/nopeDispatcher.interface"; import { INopePackageLoader } from "../types/nope/nopePackageLoader.interface"; import { NOPELOGO } from "./renderNope"; export const validLayers = { "event": EventLayer, "amqp": AmqpLayer, "io-server": IoSocketServer, "io-client": IoSocketClient }; const layerDefaults = { event: [], amqp: ["localhost"], "io-server": [7000], "io-client": ["http://localhost:7000"] }; /** * Helper Function to Read-In the Arguments used by the * cli-tool * * @return {*} */ export async function readInArgs( additionalArguments: { help: string; type: "string" | "number"; name: string | string; defaultValue?: any; }[] = [] ): Promise { const parser = new ArgumentParser({ version: "1.0.0", addHelp: true, description: "Command Line interface, determines the available Packages." }); for (const arg of additionalArguments) { parser.addArgument(arg.name, { help: arg.help, defaultValue: arg.defaultValue, type: arg.type }); } parser.addArgument(["-f", "--file"], { help: "File containing containing the package definitions.", defaultValue: "./config/settings.json", type: "string", dest: "file" }); parser.addArgument(["-c", "--channel"], { help: "The Communication Channel, which should be used. Possible Values are: " + // Display all Options: Object.getOwnPropertyNames(validLayers) .map((item) => "\"" + item + "\"") .join(", "), defaultValue: "event", type: "string", dest: "channel" }); parser.addArgument(["-p", "--params"], { help: "Paramas for the Channel, to connect to. The Following Defaults are used: \n" + JSON.stringify(layerDefaults, undefined, 4), defaultValue: "not-provided", type: "string", dest: "params" }); parser.addArgument(["-s", "--skipLoadingConfig"], { help: "Flag to prevent loading the elements defined in the settings.json.", action: "append", nargs: "?", dest: "skipLoadingConfig" }); parser.addArgument(["-l", "--log"], { help: "Specify the Logger Level. Defaults to \"info\". Valid values are: " + LoggerLevels.join(", "), defaultValue: "debug", type: "string", dest: "log" }); const args: { file: string; channel: keyof typeof validLayers; params?: string; skipLoadingConfig?: boolean; log: LoggerLevel; } = parser.parseArgs(); if (args.params === "not-provided") { delete args.params; } args.skipLoadingConfig = Array.isArray(args.skipLoadingConfig); return args; } // Define the Main Function. // This function is used as cli tool. export async function runNopeBackend( _args: { file?: string; channel?: keyof typeof validLayers; params?: string; skipLoadingConfig?: boolean; log?: LoggerLevel; } = {} ): Promise { let opts: { params: any[]; }; const args = Object.assign( { file: "./config/settings.json", channel: "event", skipLoadingConfig: false, params: "not-provided", log: "debug" }, _args ); try { // Try to read in the default config file. opts = JSON.parse( await readFile("./nopeconfig.json", { encoding: "utf-8" }) ); } catch (error) { opts = {} as any; } if (LoggerLevels.includes(args.log)) { setGlobalLoggerLevel(args.log); } // Define a Logger const logger = getNopeLogger("starter"); if (!Object.getOwnPropertyNames(validLayers).includes(args.channel)) { logger.error( "Invalid Channel. Please use the following values. " + Object.getOwnPropertyNames(validLayers) .map((item) => "\"" + item + "\"") .join(", ") ); return; } // Assign the Default Setting for the Channel. opts.params = layerDefaults[args.channel]; if (args.params != "not-provided") { try { opts.params = JSON.parse(args.params); } catch (e) { logger.error( "Unable to parse the Parameters for the channel. Please use valid JSON!" ); return; } } // If required load all Packages. if (!args.skipLoadingConfig) { // Try to load the Modules. try { logger.info("loading Functions"); await loadFunctions(args.file); } catch (e) { logger.error( "Unable to load the Packages defined in " + args.file + " See Output for detailled information", e ); return; } } let loader: INopePackageLoader; try { let communicator: ICommunicationBridge = null; const bridgedLayers: Array = [ "io-server", "io-client" ]; if (bridgedLayers.includes(args.channel)){ communicator = new validLayers[args.channel](...opts.params) as any as ICommunicationBridge; } else { communicator = new Bridge() as any as ICommunicationBridge; communicator.addLayer(new validLayers[args.channel](...opts.params), true); } loader = getPackageLoader({ communicator, logger: getNopeLogger("dispatcher", "debug") }); } catch (e) { getNopeLogger("cli", "info").error("failed to load the Packages", e); } // If required load all Packages. if (!args.skipLoadingConfig) { // Try to load the Modules. try { logger.info("loading Packages"); await loadPackages(loader, args.file); } catch (e) { logger.error( "Unable to load the Packages defined in " + args.file + " See Output for detailled information", e ); return; } } return loader.dispatcher; } /** * Main Function. * * @export */ export function main() { // Subscribe to unhandled Reactions. process.on("unhandledRejection", (reason, p) => { console.log("Unhandled Rejection at: Promise", p, "reason:", reason); console.error(reason); // application specific logging, throwing an error, or other logic here // Forward the error throw reason; }); console.log(NOPELOGO); console.log("\n\n"); readInArgs() .then((args) => runNopeBackend(args).catch(console.error)) .catch(console.error); } export default main; // If requested As Main => Perform the Operation. if (require.main === module) { main(); }