Skip to content

Commit

Permalink
Improve CLI
Browse files Browse the repository at this point in the history
  • Loading branch information
jacekwegr committed Nov 23, 2023
1 parent d249add commit 877040e
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 77 deletions.
6 changes: 4 additions & 2 deletions big_tests/tests/cluster_commands_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -190,9 +190,11 @@ one_to_one_message(ConfigIn) ->

commands_without_args(Config) ->
{Res1, 1} = mongooseimctl_interactive("join_cluster", [], "yes\n", Config),
?assertMatch({match, _}, re:run(Res1, "You have to provide other node's name")),
?assertMatch({match, _}, re:run(Res1,
"This command requires one argument: other node's name\n")),
{Res2, 1} = mongooseimctl_interactive("remove_from_cluster", [], "yes\n",Config),
?assertMatch({match, _}, re:run(Res2, "You have to provide other node's name")).
?assertMatch({match, _}, re:run(Res2,
"This command requires one argument: other node's name\n")).

join_successful_prompt(Config) ->
%% given
Expand Down
18 changes: 1 addition & 17 deletions big_tests/tests/mongooseimctl_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,7 @@ graphql() ->
graphql_command].

help() ->
[default_help,
help_with_dual_mode,
help_with_long_mode].
[default_help].

server() ->
[server_status,
Expand Down Expand Up @@ -193,20 +191,6 @@ default_help(Config) ->
{Res, 2} = mongooseimctl_helper:run(CtlCmd, []),
expect_category_list(Res).

help_with_dual_mode(Config) ->
{Res1, 2} = mongooseimctl("help", ["--dual"], Config),
expect_category_list(Res1),
{Res2, 2} = mongooseimctl("help", ["--nonexistent"], Config),
expect_category_list(Res2),
{Res3, 2} = mongooseimctl("help", [], Config),
expect_category_list(Res3).

help_with_long_mode(Config) ->
{Res, 2} = mongooseimctl("help", ["--long"], Config),
io:format("Res: ~p", [Res]),
?assertMatch({match, _}, re:run(Res, "Usage")),
?assertMatch({match, _}, re:run(Res, "account \n\s+Account management")).

%%-----------------------------------------------------------------
%% Server management tests
%%-----------------------------------------------------------------
Expand Down
97 changes: 39 additions & 58 deletions src/ejabberd_ctl.erl
Original file line number Diff line number Diff line change
Expand Up @@ -123,13 +123,13 @@ process(["restart"]) ->
process(["join_cluster", Arg]) when is_list(Arg) ->
join_cluster(Arg);
process(["join_cluster" | _]) ->
cluster_command_without_arg();
cluster_command_usage();
process(["leave_cluster"]) ->
leave_cluster();
process(["remove_from_cluster", Arg]) when is_list(Arg) ->
remove_from_cluster(Arg);
process(["remove_from_cluster" | _]) ->
cluster_command_without_arg();
cluster_command_usage();
process(["graphql", Arg]) when is_list(Arg) ->
Doc = list_to_binary(Arg),
Ep = mongoose_graphql:get_endpoint(admin),
Expand All @@ -139,24 +139,6 @@ process(["graphql" | _]) ->
?PRINT("This command requires one string type argument!\n", []),
?STATUS_ERROR;

%% @doc The arguments --long and --dual are not documented because they are
%% automatically selected depending in the number of columns of the shell
process(["help" | Mode]) ->
{MaxC, ShCode} = get_shell_info(),
case Mode of
[] ->
print_usage(dual, MaxC, ShCode),
?STATUS_USAGE;
["--dual"] ->
print_usage(dual, MaxC, ShCode),
?STATUS_USAGE;
["--long"] ->
print_usage(long, MaxC, ShCode),
?STATUS_USAGE;
[_] ->
print_usage(dual, MaxC, ShCode),
?STATUS_USAGE
end;
process(Args) ->
case mongoose_graphql_commands:process(Args) of
#{status := executed, result := Result} ->
Expand Down Expand Up @@ -196,7 +178,7 @@ print_usage(#{category := Category, commands := Commands}) ->
print_usage_category(Category, Commands);
print_usage(_) ->
{MaxC, ShCode} = get_shell_info(),
print_usage(dual, MaxC, ShCode).
print_usage(MaxC, ShCode).

handle_graphql_result({ok, Result}) ->
JSONResult = mongoose_graphql_response:term_to_pretty_json(Result),
Expand Down Expand Up @@ -247,34 +229,33 @@ format_status([{node, Node}, {internal_status, IS}, {provided_status, PS},

print_usage() ->
{MaxC, ShCode} = get_shell_info(),
print_usage(dual, MaxC, ShCode).
print_usage(MaxC, ShCode).

Check warning on line 232 in src/ejabberd_ctl.erl

View check run for this annotation

Codecov / codecov/patch

src/ejabberd_ctl.erl#L232

Added line #L232 was not covered by tests


-spec print_usage(dual | long, MaxC :: integer(), ShCode :: boolean()) -> ok.
print_usage(HelpMode, MaxC, ShCode) ->
-spec print_usage(MaxC :: integer(), ShCode :: boolean()) -> ok.
print_usage(MaxC, ShCode) ->
?PRINT(["Usage: ", ?B("mongooseimctl"), " [", ?U("category"), "] ", ?U("command"),
" [", ?U("arguments"), "]\n\n"
"Most MongooseIM commands are grouped into the following categories:\n"], []),
print_categories(HelpMode, MaxC, ShCode),
print_categories(MaxC, ShCode),
?PRINT(["\nTo list the commands in a particular category:\n mongooseimctl ", ?U("category"),
"\n"], []),
?PRINT(["\nThe following basic system management commands do not have a category:\n"], []),
print_usage_commands(HelpMode, MaxC, ShCode, basic_commands()).
print_usage_commands(MaxC, ShCode, basic_commands()).

-spec basic_commands() -> [cmd()].
basic_commands() ->
[{"status", [], "Get MongooseIM status"},
{"stop", [], "Stop MongooseIM"},
{"restart", [], "Restart MongooseIM"},
{"help", ["[--tags [tag] | com?*]"], "Show help for the deprecated commands"},
{"graphql", ["query"], "Execute GraphQL query or mutation"}].

-spec print_categories(dual | long, MaxC :: integer(), ShCode :: boolean()) -> ok.
print_categories(HelpMode, MaxC, ShCode) ->
-spec print_categories(MaxC :: integer(), ShCode :: boolean()) -> ok.
print_categories(MaxC, ShCode) ->
SortedSpecs = lists:sort(maps:to_list(mongoose_graphql_commands:get_specs())),
Categories = [{binary_to_list(Category), [], binary_to_list(Desc)}
|| {Category, #{desc := Desc}} <- SortedSpecs],
print_usage_commands(HelpMode, MaxC, ShCode, Categories).
print_usage_commands(MaxC, ShCode, Categories).

-spec print_usage_category(mongoose_graphql_commands:category(),
mongoose_graphql_commands:command_map()) -> ok.
Expand All @@ -285,7 +266,7 @@ print_usage_category(Category, Commands) ->
"The following commands are available in the category '", Category, "':\n"], []),
CmdSpec = [{binary_to_list(Command), [], binary_to_list(Desc)}
|| {Command, #{desc := Desc}} <- maps:to_list(Commands)],
print_usage_commands(dual, MaxC, ShCode, CmdSpec),
print_usage_commands(MaxC, ShCode, CmdSpec),
?PRINT(["\nTo list the arguments for a particular command:\n"
" mongooseimctl ", Category, " ", ?U("command"), " --help", "\n"], []).

Expand All @@ -302,16 +283,15 @@ print_usage_command(Category, Command, ArgsSpec) ->
%% This will be replaced with new logic when old commands are dropped
Args = [{binary_to_list(Name), [], mongoose_graphql_commands:wrap_type(Wrap, Type)}
|| #{name := Name, type := Type, wrap := Wrap} <- ArgsSpec],
print_usage_commands(dual, MaxC, ShCode, Args),
print_usage_commands(MaxC, ShCode, Args),
?PRINT(["\nScalar values do not need quoting unless they contain special characters or spaces.\n"
"Complex input types are passed as JSON maps or lists, depending on the type.\n"
"When a type is followed by '!', the corresponding argument is required.\n"], []).

-spec print_usage_commands(HelpMode :: 'dual' | 'long',
MaxC :: integer(),
-spec print_usage_commands(MaxC :: integer(),
ShCode :: boolean(),
Commands :: [cmd(), ...]) -> 'ok'.
print_usage_commands(HelpMode, MaxC, ShCode, Commands) ->
print_usage_commands(MaxC, ShCode, Commands) ->
CmdDescsSorted = lists:keysort(1, Commands),

%% What is the length of the largest command?
Expand All @@ -336,7 +316,7 @@ print_usage_commands(HelpMode, MaxC, ShCode, Commands) ->

%% For each command in the list of commands
%% Convert its definition to a line
FmtCmdDescs = format_command_lines(CmdArgsLenDescsSorted, MaxCmdLen, MaxC, ShCode, HelpMode),
FmtCmdDescs = format_command_lines(CmdArgsLenDescsSorted, MaxCmdLen, MaxC, ShCode),
?PRINT([FmtCmdDescs], []).

%% @doc Get some info about the shell: how many columns of width and guess if
Expand Down Expand Up @@ -409,26 +389,27 @@ join(L, [Word | Words], LenLastSeg, LastSeg, ResSeg) ->
-spec format_command_lines(CALD :: [{[any()], [any()], number(), _}, ...],
MaxCmdLen :: integer(),
MaxC :: integer(),
ShCode :: boolean(),
'dual' | 'long') -> [[any(), ...], ...].
format_command_lines(CALD, MaxCmdLen, MaxC, ShCode, dual)
when MaxC - MaxCmdLen < 40 ->
%% If the space available for descriptions is too narrow, enforce long help mode
format_command_lines(CALD, MaxCmdLen, MaxC, ShCode, long);
format_command_lines(CALD, MaxCmdLen, MaxC, ShCode, dual) ->
lists:map(
fun({Cmd, Args, CmdArgsL, Desc}) ->
DescFmt = prepare_description(MaxCmdLen+4, MaxC, Desc),
[" ", ?B(Cmd), " ", [[?U(Arg), " "] || Arg <- Args], string:chars(?ASCII_SPACE_CHARACTER, MaxCmdLen - CmdArgsL + 1),
DescFmt, "\n"]
end, CALD);
format_command_lines(CALD, _MaxCmdLen, MaxC, ShCode, long) ->
lists:map(
fun({Cmd, Args, _CmdArgsL, Desc}) ->
DescFmt = prepare_description(8, MaxC, Desc),
["\n ", ?B(Cmd), " ", [[?U(Arg), " "] || Arg <- Args], "\n", " ",
DescFmt, "\n"]
end, CALD).
ShCode :: boolean()) -> [[any(), ...], ...].
format_command_lines(CALD, MaxCmdLen, MaxC, ShCode) ->
case MaxC - MaxCmdLen < 40 of
true ->
% Long mode
lists:map(

Check warning on line 397 in src/ejabberd_ctl.erl

View check run for this annotation

Codecov / codecov/patch

src/ejabberd_ctl.erl#L397

Added line #L397 was not covered by tests
fun({Cmd, Args, _CmdArgsL, Desc}) ->
DescFmt = prepare_description(8, MaxC, Desc),
["\n ", ?B(Cmd), " ", [[?U(Arg), " "] || Arg <- Args], "\n", " ",

Check warning on line 400 in src/ejabberd_ctl.erl

View check run for this annotation

Codecov / codecov/patch

src/ejabberd_ctl.erl#L399-L400

Added lines #L399 - L400 were not covered by tests
DescFmt, "\n"]
end, CALD);
false ->
% Dual mode
lists:map(
fun({Cmd, Args, CmdArgsL, Desc}) ->
DescFmt = prepare_description(MaxCmdLen+4, MaxC, Desc),
[" ", ?B(Cmd), " ", [[?U(Arg), " "] || Arg <- Args], string:chars(?ASCII_SPACE_CHARACTER, MaxCmdLen - CmdArgsL + 1),
DescFmt, "\n"]
end, CALD)
end.


%%-----------------------------
%% Print usage command
Expand Down Expand Up @@ -477,6 +458,6 @@ handle_cluster_operation(Operation, Args) ->
?STATUS_ERROR
end.

cluster_command_without_arg() ->
?PRINT("You have to provide other node's name\n", []),
cluster_command_usage() ->
?PRINT("This command requires one argument: other node's name\n", []),
?STATUS_ERROR.

0 comments on commit 877040e

Please sign in to comment.