nope/lib/cli/createService.ts

509 lines
13 KiB
TypeScript
Raw Normal View History

2021-01-08 15:58:55 +00:00
/**
* @author Martin Karkowski
* @email m.karkowski@zema.de
* @create date 2021-01-08 15:16:51
* @modify date 2021-02-09 10:18:08
2021-01-08 15:58:55 +00:00
* @desc [description]
*/
import { ArgumentParser } from "argparse";
import { readFileSync } from "fs";
import * as handlebars from "handlebars";
import * as inquirer from "inquirer";
import { camelCase } from "lodash";
import { type } from "os";
import { join } from "path";
2021-12-04 07:25:26 +00:00
import {
layerDefaultParameters,
validLayers,
} from "../communication/getLayer.nodejs";
2021-01-08 15:58:55 +00:00
import { createFile } from "../helpers/fileMethods";
import { IConfigFile } from "../loader/loadPackages";
import { getNopeLogger } from "../logger/getLogger";
import { LoggerLevels } from "../logger/nopeLogger";
2021-01-08 15:58:55 +00:00
// Define the Main Function.
// This function is used as cli tool.
export async function createService(
additionalArguments: {
help: string;
type: "str" | "number";
name: string | string;
defaultValue?: any;
}[] = []
) {
2021-01-08 15:58:55 +00:00
// Flag, to determine the OS
const runningInLinux = type() === "Linux";
const parser = new ArgumentParser({
// version: "1.0.0",
add_help: true,
2021-12-04 07:25:26 +00:00
description: "Command Line interface, which enables creating services.",
2021-01-08 15:58:55 +00:00
});
for (const arg of additionalArguments) {
parser.add_argument(arg.name, {
help: arg.help,
default: arg.defaultValue,
2021-12-04 07:25:26 +00:00
type: arg.type,
});
}
parser.add_argument("-f", "--file", {
2021-01-08 15:58:55 +00:00
help: "File containing containing the package definitions.",
default: "./config/settings.json",
type: "str",
2021-12-04 07:25:26 +00:00
dest: "file",
2021-01-08 15:58:55 +00:00
});
parser.add_argument("-o", "--outputDir", {
2021-01-08 15:58:55 +00:00
help: "File containing containing the package definitions.",
default: "./services/",
type: "str",
2021-12-04 07:25:26 +00:00
dest: "outputDir",
2021-01-08 15:58:55 +00:00
});
parser.add_argument("--noFuncs", {
2021-05-21 07:33:49 +00:00
help: "Prevents using Functions",
2022-01-17 17:06:10 +00:00
action: "store_true",
2021-12-04 07:25:26 +00:00
dest: "noFuncs",
2021-05-21 07:33:49 +00:00
});
parser.add_argument("--runInstances", {
2021-05-21 07:33:49 +00:00
help: "Run instances provided in a packages.",
2022-01-17 17:06:10 +00:00
action: "store_true",
2021-12-04 07:25:26 +00:00
dest: "runInstances",
2021-05-21 07:33:49 +00:00
});
parser.add_argument("--runEveryService", {
2021-05-21 07:33:49 +00:00
help: "Run instances provided in a packages.",
2022-01-17 17:06:10 +00:00
action: "store_true",
2021-12-04 07:25:26 +00:00
dest: "runEveryService",
2021-05-21 07:33:49 +00:00
});
parser.add_argument("--runExtra", {
2021-05-21 07:33:49 +00:00
help: "Runs every instance in an extra Service",
2022-01-17 17:06:10 +00:00
action: "store_true",
2021-12-04 07:25:26 +00:00
dest: "runExtra",
2021-05-21 07:33:49 +00:00
});
parser.add_argument("--layer", {
2021-05-21 07:33:49 +00:00
help: "Defaultly selects layer to use.",
default: "not-provided",
type: "str",
2021-12-04 07:25:26 +00:00
dest: "layer",
2021-05-21 07:33:49 +00:00
});
parser.add_argument("--params", {
2021-10-20 18:40:44 +00:00
help:
"Paramas for the Channel, to connect to. The Following Defaults are used: \n" +
JSON.stringify(layerDefaultParameters, undefined, 4),
default: "not-provided",
type: "str",
2021-12-04 07:25:26 +00:00
dest: "params",
2021-10-20 18:40:44 +00:00
});
parser.add_argument("--log", {
2021-05-21 07:33:49 +00:00
help:
2021-12-04 07:25:26 +00:00
'Specify the Logger Level of the Services. Defaults to "info". Valid values are: ' +
2021-05-21 07:33:49 +00:00
LoggerLevels.join(", "),
default: "not-provided",
type: "str",
2021-12-04 07:25:26 +00:00
dest: "logLevel",
2021-05-21 07:33:49 +00:00
});
const args = parser.parse_args();
2021-01-08 15:58:55 +00:00
// Define a Logger
const logger = getNopeLogger("Setup-CLI");
try {
logger.info("Reading config File");
const data: IConfigFile = JSON.parse(
readFileSync(args.file).toString("utf-8")
);
const config: IConfigFile = {
functions: [],
2021-12-04 07:25:26 +00:00
packages: [],
2021-01-08 15:58:55 +00:00
};
2021-05-21 07:33:49 +00:00
const modeSelection = [];
const argsToUse: any = {};
if (!args.noFuncs) {
modeSelection.push({
2021-01-08 15:58:55 +00:00
type: "confirm",
name: "useFunction",
message: "Do you want to share Functions",
2021-12-04 07:25:26 +00:00
default: false,
2021-05-21 07:33:49 +00:00
});
} else {
argsToUse.useFunction = false;
}
if (!args.runInstances) {
modeSelection.push({
2021-01-08 15:58:55 +00:00
type: "confirm",
2021-05-21 07:33:49 +00:00
name: "detailPackages",
2021-01-08 15:58:55 +00:00
message: "Do you want to run instances provided in a package?",
2021-12-04 07:25:26 +00:00
default: true,
2021-05-21 07:33:49 +00:00
});
} else {
argsToUse.detailPackages = false;
}
if (!args.runEveryService) {
modeSelection.push({
2021-01-08 15:58:55 +00:00
type: "confirm",
name: "detailInstances",
message:
"Do you want to detail, which instance should be hosted as Service?",
2021-12-04 07:25:26 +00:00
default: true,
2021-05-21 07:33:49 +00:00
});
} else {
argsToUse.detailInstances = false;
}
if (!args.runExtra) {
modeSelection.push({
2021-01-08 15:58:55 +00:00
type: "confirm",
name: "runExtra",
message: "Do you want to run every selected option as single Service?",
2021-12-04 07:25:26 +00:00
default: true,
2021-05-21 07:33:49 +00:00
});
} else {
argsToUse.runExtra = true;
}
2021-10-20 18:40:44 +00:00
if (args.logLevel === "not-provided") {
2021-05-21 07:33:49 +00:00
modeSelection.push({
2021-01-08 15:58:55 +00:00
type: "list",
2021-10-20 18:40:44 +00:00
name: "logLevel",
message: "Select the Level of the Logger, to use.",
2021-12-04 07:25:26 +00:00
choices: LoggerLevels,
2021-05-21 07:33:49 +00:00
});
} else {
2021-10-20 18:40:44 +00:00
argsToUse.logLevel = args.logLevel;
2021-05-21 07:33:49 +00:00
}
2021-10-20 18:40:44 +00:00
if (args.layer === "not-provided") {
2021-05-21 07:33:49 +00:00
modeSelection.push({
type: "list",
2021-10-20 18:40:44 +00:00
name: "layer",
message: "Select the Communication Layer to use",
2021-12-04 07:25:26 +00:00
choices: Object.getOwnPropertyNames(validLayers),
2021-05-21 07:33:49 +00:00
});
} else {
2021-10-20 18:40:44 +00:00
argsToUse.layer = args.layer;
2021-05-21 07:33:49 +00:00
}
2021-01-08 15:58:55 +00:00
2021-05-21 07:33:49 +00:00
let result: any = {};
if (modeSelection.length > 0) {
result = await inquirer.prompt(modeSelection);
}
result = Object.assign({}, result, argsToUse);
2021-01-08 15:58:55 +00:00
2021-10-20 18:40:44 +00:00
let params: string = null;
if (result.layer !== "event" && args.params === "not-provided") {
2021-12-04 07:25:26 +00:00
const resultOfQuestion = (
await inquirer.prompt({
type: "confirm",
name: "result",
message: "Do you want to add custom Layer Parameters",
default: true,
})
).result;
2021-10-20 18:40:44 +00:00
if (resultOfQuestion) {
while (params == null) {
2021-12-04 07:25:26 +00:00
params = (
await inquirer.prompt({
type: "input",
name: "result",
message: "Please enter the parameters.",
default: true,
})
).result;
2021-10-20 18:40:44 +00:00
}
} else {
params = layerDefaultParameters[args.channel];
}
} else {
try {
try {
// We try to parse the data.
params = JSON.parse(args.params);
} catch (e) {
2021-12-04 07:25:26 +00:00
params = JSON.parse('"' + args.params + '"');
2021-10-20 18:40:44 +00:00
}
} 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;
}
}
2021-01-08 15:58:55 +00:00
if (result.useFunction) {
config.functions = (
await inquirer.prompt([
{
type: "checkbox",
name: "functions",
message: "Select/Deselect Functions, that should be hosted",
choices: data.functions.map((func) => {
return {
name: func.path,
value: func,
2021-12-04 07:25:26 +00:00
checked: false,
2021-01-08 15:58:55 +00:00
};
2021-12-04 07:25:26 +00:00
}),
},
2021-01-08 15:58:55 +00:00
])
).functions;
}
2021-05-21 07:33:49 +00:00
// Defaultly assign the default-Packages.
config.packages = data.packages;
if (result.detailPackages) {
2021-01-08 15:58:55 +00:00
// Determine the Packages which should be hosted.
config.packages = (
await inquirer.prompt([
{
type: "checkbox",
name: "packages",
message: "Select/Deselect Packages, that should be hosted:",
choices: data.packages.map((p) => {
return {
name: p.nameOfPackage,
value: p,
2021-12-04 07:25:26 +00:00
checked: true,
2021-01-08 15:58:55 +00:00
};
2021-12-04 07:25:26 +00:00
}),
},
2021-01-08 15:58:55 +00:00
])
).packages;
2021-05-21 07:33:49 +00:00
}
2021-01-08 15:58:55 +00:00
2021-05-21 07:33:49 +00:00
// Now, that we know, which package we should use, let us "detail" the instances we should use.
if (result.detailInstances) {
// Iterate over the Pacakges
for (const p of config.packages) {
// Determine the Instances to keep:
2021-10-20 18:40:44 +00:00
if (p.defaultInstances.length > 0) {
p.defaultInstances = (
await inquirer.prompt([
{
type: "checkbox",
name: "instances",
message: "Select/Deselect Instances, that should be hosted:",
choices: p.defaultInstances.map((instance) => {
return {
name: instance.options.identifier,
value: instance,
2021-12-04 07:25:26 +00:00
checked: true,
2021-10-20 18:40:44 +00:00
};
2021-12-04 07:25:26 +00:00
}),
},
2021-10-20 18:40:44 +00:00
])
).instances;
}
2021-01-08 15:58:55 +00:00
}
}
const serviceTemplates = runningInLinux
? [
2021-12-04 07:25:26 +00:00
{
name: "nopeService.service",
render: handlebars.compile(
readFileSync(
join(
__dirname,
"..",
"..",
"lib",
"templates",
"linux.service.handlebars"
)
).toString("utf-8")
),
},
]
2021-01-08 15:58:55 +00:00
: [
2021-12-04 07:25:26 +00:00
{
name: "index.js",
render: handlebars.compile(
readFileSync(
join(
__dirname,
"..",
"..",
"lib",
"templates",
"index.js.handlebars"
)
).toString("utf-8")
),
},
{
name: "service.js",
render: handlebars.compile(
readFileSync(
join(
__dirname,
"..",
"..",
"lib",
"templates",
"service.js.handlebars"
)
).toString("utf-8")
),
},
{
name: "00-install.bat",
render: handlebars.compile(`set DIR=%~dp0
2021-01-08 15:58:55 +00:00
cd "%DIR%"
cd ..
cd ..
cd ..
node {{{pathToFolder}}}\\service.js -m install
2021-12-04 07:25:26 +00:00
`),
},
{
name: "01-start.bat",
render: handlebars.compile(`set DIR=%~dp0
2021-07-29 13:27:35 +00:00
cd "%DIR%"
cd ..
cd ..
cd ..
node {{{pathToFolder}}}\\service.js -m start
2021-12-04 07:25:26 +00:00
`),
},
{
name: "02-restart.bat",
render: handlebars.compile(`set DIR=%~dp0
2021-01-08 15:58:55 +00:00
cd "%DIR%"
cd ..
cd ..
cd ..
node {{{pathToFolder}}}\\service.js -m restart
2021-12-04 07:25:26 +00:00
`),
},
{
name: "03-uninstall.bat",
render: handlebars.compile(`set DIR=%~dp0
2021-01-08 15:58:55 +00:00
cd "%DIR%"
cd ..
cd ..
cd ..
2021-12-04 07:25:26 +00:00
node {{{pathToFolder}}}\\service.js -m uninstall`),
},
{
name: "00-manual_starter.bat",
render: handlebars.compile(`set DIR=%~dp0
2021-01-08 15:58:55 +00:00
cd "%DIR%"
cd ..
cd ..
cd ..
2021-12-04 07:25:26 +00:00
node {{{pathToFolder}}}\\index.js`),
},
];
2021-01-08 15:58:55 +00:00
const storeConfig = async (
config: IConfigFile,
name: string,
path: string
) => {
// Create the Settings File:
await createFile(
join(path, "settings.json"),
JSON.stringify(config, undefined, 4)
);
// Write the Templates
for (const template of serviceTemplates) {
await createFile(
join(path, template.name),
template.render({
name,
path,
2021-10-20 19:06:46 +00:00
params,
2021-01-08 15:58:55 +00:00
layer: result.layer,
pathToFile: join(path, template.name),
pathToFolder: join(path),
2021-12-04 07:25:26 +00:00
logLevel: result.logLevel,
2021-01-08 15:58:55 +00:00
})
);
}
2021-05-21 07:36:44 +00:00
2021-05-21 07:43:07 +00:00
logger.info("Create Service at", path);
2021-01-08 15:58:55 +00:00
};
if (result.runExtra) {
const promises: Promise<any>[] = [];
for (const _package of config.packages) {
for (const _instance of _package.defaultInstances) {
const autostart = {};
if (_package.autostart[_instance.options.identifier]) {
autostart[_instance.options.identifier] =
_package.autostart[_instance.options.identifier];
}
const _config: IConfigFile = {
functions: [],
packages: [
{
autostart,
defaultInstances: [_instance],
nameOfPackage: _package.nameOfPackage,
2021-12-04 07:25:26 +00:00
path: _package.path,
},
],
2021-01-08 15:58:55 +00:00
};
promises.push(
storeConfig(
_config,
camelCase(_package.nameOfPackage),
join(
args.outputDir,
camelCase(_package.nameOfPackage),
camelCase(_instance.options.identifier)
)
)
);
}
}
await Promise.all(promises);
} else {
// Create a Syntetic Package, which will be created.
await storeConfig(
config,
"NopeService",
join(args.outputDir, "combinedService")
);
}
} catch (error) {
logger.error("Failed generating the Config");
logger.error(error);
}
}
// If requested As Main => Perform the Operation.
if (require.main === module) {
2022-07-23 05:34:38 +00:00
createService().catch((e) => {
console.error(e);
});
}