@sap/cli-core
Version:
Command-Line Interface (CLI) Core Module
167 lines (166 loc) • 6.42 kB
JavaScript
import path from "path";
import { get } from "../logger/index.js";
import { CLI_DESCRIPTION, CLI_NAME, CLI_SAP_HELP, OPTION_FILE_PATH, OPTION_FORCE, OPTION_HELP, OPTION_HOST, OPTION_INPUT, OPTION_OPTIONS_FILE, OPTION_OUTPUT, OPTION_VERBOSE, OPTION_VERSION, ROOT_COMMAND, } from "../constants.js";
import { init as initCache } from "../cache/index.js";
import { get as getConfig, set as setConfig, setCustomConfig, initialize as initializeConfig, } from "../config/index.js";
import { buildOption, createCommand, registerLongName, } from "../utils/commands.js";
import { clear as clearDiscovery } from "../discovery/index.js";
import { get as getSettings } from "../settings/index.js";
import { addCommandsFromFolder, getOptionValueFromArgv } from "./utils.js";
import { getValueFromOptionsFile } from "../commands/handler/options/utils.js";
import { getGenericOptionsHelp, getVersion } from "../config/core.js";
import { ResultHandlerImpl } from "../result/ResultHandlerImpl.js";
import { ResultHandlerFactory } from "../result/ResultHandlerFactory.js";
import { SecretsStorageImpl } from "../cache/secrets/SecretsStorageImpl.js";
import { SecretsStorageSingleton } from "../cache/secrets/SecretsStorageSingleton.js";
import { replaceLeadingTrailingSingleQuotes } from "../commands/handler/utils.js";
let program;
let initialized = false;
const getLogger = () => get("dwc.dwc");
const MANDATORY_OPTIONS = [
OPTION_VERSION,
OPTION_HELP,
OPTION_HOST,
OPTION_FILE_PATH,
OPTION_FORCE,
OPTION_OUTPUT,
OPTION_VERBOSE,
OPTION_INPUT,
OPTION_OPTIONS_FILE,
];
const setTenant = async () => {
const config = getConfig();
if (config.host) {
return;
}
const readHostFromArgv = async () => getOptionValueFromArgv(ROOT_COMMAND, OPTION_HOST);
const readHostFromOptionsFile = async () => {
const filePath = getOptionValueFromArgv(ROOT_COMMAND, OPTION_OPTIONS_FILE);
return getValueFromOptionsFile(filePath, OPTION_HOST);
};
const readHostFromOptions = async () => {
if (!config.options?.[OPTION_HOST.longName]) {
throw new Error("host not found in options");
}
return config.options[OPTION_HOST.longName];
};
const strategies = [
{
func: readHostFromArgv,
name: "read host from argv",
},
{
func: readHostFromOptionsFile,
name: "read host from options-file",
},
{
func: readHostFromOptions,
name: "read host from options",
},
];
const { trace } = getLogger();
for (const { name, func } of strategies) {
trace(`trying to ${name}`);
try {
// eslint-disable-next-line no-await-in-loop
const host = replaceLeadingTrailingSingleQuotes(await func());
setConfig({
tenant: host,
options: { ...config.options, [OPTION_HOST.longName]: host },
});
break;
}
catch (err) {
trace(`failed to ${name}`, err);
}
}
};
const setHost = async () => {
const settings = await getSettings();
if (settings.host) {
setConfig({ options: { [OPTION_HOST.longName]: settings.host } });
}
};
const initCacheTolerant = async () => {
try {
await initCache();
}
catch {
// ignore
}
};
const registerMandatoryOptions = () => {
MANDATORY_OPTIONS.forEach((option) => registerLongName(ROOT_COMMAND, option));
};
async function setupSecretsStorage() {
const instance = new SecretsStorageImpl();
await instance.initializeStorage();
SecretsStorageSingleton.SINGLETON = instance;
}
export const init = async () => {
const { debug } = getLogger();
if (initialized) {
return;
}
initialized = true;
const resultHandler = new ResultHandlerImpl();
ResultHandlerFactory.set(resultHandler);
registerMandatoryOptions();
await setHost();
await setTenant();
await initCacheTolerant();
await setupSecretsStorage();
const cliName = getConfig()[CLI_NAME];
program = createCommand();
program.name(cliName);
program.version(getVersion(), "-v, --version", "output the current version");
program.description(getConfig()[CLI_DESCRIPTION]);
program.showHelpAfterError(`Make sure to always define the host using the --host option or by running ${cliName}` +
` config host set "<Server_URL>". Did you initialize the CLI by running ${cliName} config` +
` cache init --host "<Server_URL>"?` +
` Add option --help, -h or go to ${getConfig()[CLI_SAP_HELP]} for additional information.`);
program.addOption(await buildOption(ROOT_COMMAND, OPTION_HOST));
program.addOption(await buildOption(ROOT_COMMAND, OPTION_OPTIONS_FILE));
program.configureHelp({ sortSubcommands: true });
program.addHelpText("afterAll", `Only command-specific options are listed here. To learn more about available generic options,` +
` visit ${getGenericOptionsHelp()}`);
const commandsPath = path.join(import.meta.dirname, "..", "commands");
await addCommandsFromFolder(commandsPath, program);
debug("cli initialized");
};
export const executeCommand = async (command) => {
await program.parseAsync(command?.split(" ") ?? undefined);
};
const buildCommandsObject = (name, command, object = {}) => {
const commandName = name || "dwc";
// eslint-disable-next-line no-param-reassign
object[commandName] = async (options) => {
await executeCommand(new Array(2)
.fill("dummy")
.concat(name.split(" ").concat(options
? Object.entries(options).reduce((prev, curr) => {
const [key, value] = curr;
return prev.concat([key, value]);
}, [])
: []))
.join(" "));
return ResultHandlerFactory.get().getResult();
};
/* istanbul ignore next */
command.commands.forEach((c) => {
buildCommandsObject(name ? `${name} ${c.name()}` : c.name(), c, object);
});
return object;
};
const reinitialize = (host) => {
initialized = false;
initializeConfig();
setConfig({ tenant: host });
setCustomConfig();
clearDiscovery();
};
export const getCommands = async (host) => {
reinitialize(host);
await init();
return buildCommandsObject("", program);
};