@cap-js-community/mtx-tool
Version:
Multitenancy and Extensibility Tool is a cli to reduce operational overhead for multitenant Cloud Foundry applications
127 lines (114 loc) • 4.04 kB
JavaScript
;
// https://apidocs.cloudfoundry.org/12.36.0/service_bindings/list_all_service_bindings.html
const { sleep, partition, question, isObject } = require("./shared/static");
const { assert, fail, ApplicationError } = require("./shared/error");
const { newContext } = require("./context");
const { PASS_ARG_META, FLAG_ARG, USAGE, GENERIC_COMMAND_INFOS, APP_COMMAND_INFOS } = require("./commands");
const { LEVEL, Logger } = require("./shared/logger");
const logger = Logger.getInstance();
const _dangerGuard = async () => {
logger.info('this is a dangerous operation, wait for 15sec and then enter "yes" if you are sure.');
await sleep(15000);
const answer = await question("do you want to proceed?");
assert(answer === "yes", "failed danger guard check");
};
const checkOption = async (cliOption, args) => {
const [firstArg] = args;
const [passArgs, flagArgs] = partition(args.slice(1), (arg) => !arg.startsWith("--"));
const {
callback,
commandVariants = [],
requiredPassArgs = [],
optionalPassArgs = [],
optionalFlagArgs = [],
silent = false,
passContext = true,
danger = false,
useCache = true,
readonly = false,
} = cliOption;
if (!firstArg || !commandVariants.includes(firstArg)) {
return false;
}
const allPassArgs = [].concat(requiredPassArgs, optionalPassArgs);
const command = commandVariants[commandVariants.length - 1];
let flagValues = null;
// NOTE: this mixes in (required and optional) positional arguments that can
// be replaced by env variables.
for (const [index, passArg] of allPassArgs.entries()) {
const envVariable = PASS_ARG_META[passArg]?.envVariable;
const value = envVariable && process.env[envVariable];
if (envVariable && value) {
passArgs.splice(index, 0, value);
}
}
assert(
passArgs.length >= requiredPassArgs.length,
'command "%s" requires %s %s',
command,
requiredPassArgs.length === 1 ? "argument" : "arguments",
requiredPassArgs.join(", ")
);
assert(
passArgs.length <= allPassArgs.length,
'command "%s" takes %s %s',
command,
allPassArgs.length === 1 ? "argument" : "arguments",
allPassArgs.join(", ")
);
if (optionalFlagArgs) {
flagArgs.forEach((flagArg) => {
assert(
flagArg === FLAG_ARG.FORCE || optionalFlagArgs.includes(flagArg),
'flag argument "%s" not valid for command "%s"',
flagArg,
command
);
});
flagValues = optionalFlagArgs.map((flag) => flagArgs.includes(flag));
}
const doForce = flagArgs.includes(FLAG_ARG.FORCE);
const doJsonOutput = flagArgs.includes(FLAG_ARG.JSON_OUTPUT);
const maskedPassArgs = passArgs.map((arg, index) => (PASS_ARG_META[allPassArgs[index]]?.sensitive ? "*****" : arg));
doJsonOutput && logger.setMaxLevel(LEVEL.ERROR);
!silent && logger.info("running", command, ...maskedPassArgs, ...flagArgs);
const context = passContext ? await newContext({ usePersistedCache: useCache, isReadonlyCommand: readonly }) : null;
danger && !doForce && (await _dangerGuard());
const result = context ? await callback(context, passArgs, flagValues) : await callback(passArgs, flagValues);
doJsonOutput && logger.setMaxLevel(LEVEL.INFO);
if (typeof result === "string") {
logger.info(result);
} else if (isObject(result)) {
logger.info(JSON.stringify(result, null, 2));
}
return true;
};
const cli = async (args) => {
try {
const [firstArg] = args;
if (!firstArg) {
logger.info(USAGE);
return;
}
for (const appCliOption of [].concat(
[GENERIC_COMMAND_INFOS.HELP, GENERIC_COMMAND_INFOS.VERSION],
Object.values(APP_COMMAND_INFOS)
)) {
if (await checkOption(appCliOption, args)) {
return;
}
}
fail('unknown command "%s"', firstArg);
} catch (err) {
if (!(err instanceof ApplicationError)) {
throw err;
}
if (err.message) {
logger.error("error: " + err.message);
}
process.exit(-1);
}
};
module.exports = {
cli,
};