nope/lib/cli/runNopeBackend.ts
Martin Karkowski b2d3d19ad8 fixing issue
2021-10-15 16:59:56 +02:00

315 lines
8.0 KiB
TypeScript

/**
* @author Martin Karkowski
* @email m.karkowski@zema.de
* @create date 2020-11-11 13:27:58
* @modify date 2021-10-15 08:10:03
* @desc [description]
*
*/
import { ArgumentParser } from "argparse";
import { readFile } from "fs/promises";
import "reflect-metadata";
import {
getLayer,
layerDefaultParameters,
validLayerOrMirror,
validLayers
} from "../communication/getLayer.nodejs";
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 { ValidDefaultSelectors } from "../types/nope/nopeDispatcher.interface";
import { INopePackageLoader } from "../types/nope/nopePackageLoader.interface";
import { NOPELOGO } from "./renderNope";
/**
* 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<any> {
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(layerDefaultParameters, undefined, 4),
defaultValue: "not-provided",
type: "string",
dest: "params"
});
parser.addArgument(["-s", "--skip-loading-config"], {
help: "Flag to prevent loading the elements defined in the settings.json.",
action: "append",
nargs: "?",
dest: "skipLoadingConfig"
});
parser.addArgument(["--default-selector"], {
help: "The default-selector to select the service providers. Possible Values are: " +
// Display all Options:
Object.getOwnPropertyNames(ValidDefaultSelectors)
.map((item) => "\"" + item + "\"")
.join(", "),
defaultValue: "first",
type: "string",
dest: "defaultSelector"
});
parser.addArgument(["--force-emit"], {
help: "Forces emitting the events of the system. Otherwise, only subscribed events are emitted.",
action: "append",
nargs: "?",
dest: "forceEmittingUpdates"
});
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;
forceEmittingUpdates?: boolean;
defaultSelector: ValidDefaultSelectors
} = parser.parseArgs();
if (args.params === "not-provided") {
delete args.params;
}
args.skipLoadingConfig = Array.isArray(args.skipLoadingConfig);
args.forceEmittingUpdates = Array.isArray(args.forceEmittingUpdates);
return args;
}
// Define the Main Function.
// This function is used as cli tool.
export async function runNopeBackend(
_args: {
file?: string;
channel?: validLayerOrMirror;
params?: string;
// Flag to prevent loading the configuration
skipLoadingConfig?: boolean;
// Flag to force sending updates.
forceEmittingUpdates?: boolean;
// Level of the logger.
log?: LoggerLevel;
// The Enable Singletons. Defaults to true
singleton?: boolean;
// The default-selector to select the service providers
defaultSelector?: ValidDefaultSelectors
} = {}
): Promise<INopePackageLoader> {
let opts: {
params: string | number;
};
const args = Object.assign(
{
file: "./config/settings.json",
channel: "event",
skipLoadingConfig: false,
params: "not-provided",
log: "debug",
singleton: true
},
_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 = layerDefaultParameters[args.channel];
if (args.params != "not-provided") {
try {
try {
// We try to parse the data.
opts.params = JSON.parse(args.params);
} catch (e) {
opts.params = JSON.parse("\"" + args.params + "\"");
}
} catch (e) {
logger.error(
"Unable to parse the Parameters for the channel. Please use valid JSON!"
);
logger.error(args.params[0]);
logger.error(e);
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 {
loader = getPackageLoader(
{
communicator: getLayer(args.channel, opts.params, "info"),
logger: getNopeLogger("dispatcher", "info"),
forceEmittingUpdates:
args.forceEmittingUpdates || args.channel == "event",
defaultSelector: args.defaultSelector
},
_args.singleton
);
} 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;
}
}
const _dispose = () => {
// We should close the Process:
logger.warn("received 'ctrl+c'. Shutting down the Instances");
loader.dispatcher.dispose().finally(process.exit);
};
process.on("SIGINT", _dispose);
process.on("SIGTERM", _dispose);
process.on("exit", () => {
logger.info("Completed. Goodbye");
});
return loader;
}
/**
* 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();
}