@hashgraph/solo
Version:
An opinionated CLI tool to deploy and manage private Hedera Networks.
152 lines (129 loc) • 5.43 kB
text/typescript
// SPDX-License-Identifier: Apache-2.0
import {SoloError} from '../errors/solo-error.js';
import {type AnyYargs, type ArgvStruct} from '../../types/aliases.js';
import {type SoloLogger} from '../logging/solo-logger.js';
import {type CommandDefinition} from '../../types/index.js';
import {type CommandFlags} from '../../types/flag-types.js';
import {Flags as flags} from '../../commands/flags.js';
import {inject, injectable} from 'tsyringe-neo';
import {InjectTokens} from '../dependency-injection/inject-tokens.js';
import {InitCommand} from '../../commands/init/init.js';
import {patchInject} from '../dependency-injection/container-helper.js';
import {type TaskList} from '../task-list/task-list.js';
import {ListrContext, ListrRendererValue} from 'listr2';
import * as constants from '../constants.js';
()
export class Subcommand {
// TODO: Subcommand should have its own class file
public constructor(
public readonly name: string,
public readonly description: string,
public readonly commandHandlerClass: any,
public readonly commandHandler: (argv: ArgvStruct) => Promise<boolean>,
public readonly flags: CommandFlags,
public readonly dependencies: string[] = [],
public readonly createCluster: boolean = false,
(InjectTokens.InitCommand) private readonly initCommand?: InitCommand,
(InjectTokens.TaskList)
private readonly taskList?: TaskList<ListrContext, ListrRendererValue, ListrRendererValue>,
) {
this.initCommand = patchInject(initCommand, InjectTokens.InitCommand, this.constructor.name);
this.taskList = patchInject(taskList, InjectTokens.TaskList, this.constructor.name);
}
public async installDependencies(): Promise<void> {
const tasks: any = this.taskList.newTaskList(
[
...this.initCommand.installDependenciesTasks({
deps: this.dependencies,
createCluster: this.createCluster,
}),
],
constants.LISTR_DEFAULT_OPTIONS.DEFAULT,
undefined,
this.name,
);
if (this.taskList.parentTaskListMap.size === 0) {
try {
await tasks.run();
} catch (error: Error | any) {
throw new SoloError(`Could not install dependencies: ${error.message}`, error);
}
}
}
}
// TODO: CommandGroup should have its own class file
export class CommandGroup {
public readonly subcommands: Subcommand[] = [];
public constructor(
public readonly name: string,
public readonly description: string,
) {}
public addSubcommand(subcommand: Subcommand): CommandGroup {
this.subcommands.push(subcommand);
return this;
}
}
// TODO: CommandBuilder should have its own class file
export class CommandBuilder {
private readonly commandGroups: CommandGroup[] = [];
public constructor(
private readonly name: string,
private readonly description: string,
private readonly logger: SoloLogger,
) {}
public addCommandGroup(commandGroup: CommandGroup): CommandBuilder {
this.commandGroups.push(commandGroup);
return this;
}
public build(): CommandDefinition {
const commandGroups: CommandGroup[] = this.commandGroups;
const logger: SoloLogger = this.logger;
const commandName: string = this.name;
const commandDescription: string = this.description;
const demandCommand: string = `select a ${commandName} command`;
return {
command: commandName,
desc: commandDescription,
builder: (yargs: AnyYargs): AnyYargs => {
for (const commandGroup of commandGroups) {
yargs.command({
command: commandGroup.name,
desc: commandGroup.description,
builder: (yargs: AnyYargs): AnyYargs => {
for (const subcommand of commandGroup.subcommands) {
const handlerDefinition: CommandDefinition = {
command: subcommand.name,
desc: subcommand.description,
handler: async (argv): Promise<void> => {
const commandPath: string = `${commandName} ${commandGroup.name} ${subcommand.name}`;
logger.info(`==== Running '${commandPath}' ===`);
const handlerCallback: (argv: ArgvStruct) => Promise<boolean> = subcommand.commandHandler.bind(
subcommand.commandHandlerClass,
);
await subcommand.installDependencies();
const response: boolean = await handlerCallback(argv);
logger.info(`==== Finished running '${commandPath}'====`);
if (!response) {
throw new SoloError(`Error running ${commandPath}, expected return value to be true`);
}
},
};
if (subcommand.flags) {
handlerDefinition.builder = (y: AnyYargs): void => {
flags.setRequiredCommandFlags(y, ...subcommand.flags.required);
flags.setOptionalCommandFlags(y, ...subcommand.flags.optional);
};
}
yargs.command(handlerDefinition);
}
yargs.demandCommand(1, `Select a ${commandName} ${commandGroup.name} command`);
return yargs;
},
});
}
yargs.demandCommand(1, demandCommand);
return yargs;
},
};
}
}