Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Commands API version 1 #4118

Merged
merged 18 commits into from
Jan 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
1326a77
ejabberd_commands: Update -type and remove obsolete @type
badlop Nov 28, 2023
98d7519
ejabberd_commands: Add the command version as a tag "vX"
badlop Nov 30, 2023
f18b8d4
Commands: Add a new muc_sub tag to all the relevant commands
badlop Nov 23, 2023
0961fa1
Commands: When result is rescode, result_desc is automatically added
badlop Nov 24, 2023
c5a5dd8
Commands: Improve syntax of many commands documentation
badlop Nov 24, 2023
d4113d9
Commands: set_presence: switch priority argument from string to integer
badlop Nov 24, 2023
e26729b
Commands: Use list arguments in many commands that used separators
badlop Nov 29, 2023
8671bf7
mod_http_api: When no specific API version is requested, use the latest
badlop Nov 29, 2023
d570870
mod_http_api: When using API version>0, avoid result names for intege…
badlop Nov 28, 2023
9f42f17
mod_http_api: Fix to allow the client override the API version
badlop Nov 30, 2023
c4c0cd1
ejabberd_ctl: Add support for list and tuple arguments
badlop Nov 23, 2023
b34572e
ejabberd_ctl: Show proper command help when version is explicitly set
badlop Nov 30, 2023
d65638e
ejabberd_ctl: Pass API version to format_result
badlop Nov 28, 2023
9076668
ejabberd_ctl: When API version>0, update syntax of list results
badlop Nov 30, 2023
d140f99
ejabberd_xmlrpc: Fix support for restuple error response
badlop Nov 28, 2023
57bd0ef
Docs: Optional support to get commands from runtime instead of BEAM f…
badlop Nov 30, 2023
d585b1f
Docs: When definer is unknown, don't show Module section
badlop Nov 30, 2023
fc13fdc
Docs: Separate tags with commas in markdown docs
badlop Nov 30, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 20 additions & 38 deletions include/ejabberd_commands.hrl
Original file line number Diff line number Diff line change
Expand Up @@ -67,42 +67,24 @@
args_example = none :: none | [any()] | '_',
result_example = none :: any()}).

%% TODO Fix me: Type is not up to date
-type ejabberd_commands() :: #ejabberd_commands{name :: atom(),
tags :: [atom()],
desc :: string(),
longdesc :: string(),
version :: integer(),
module :: atom(),
function :: atom(),
args :: [aterm()],
policy :: open | restricted | admin | user,
access :: [{atom(),atom(),atom()}|atom()],
result :: rterm()}.
-type ejabberd_commands() :: #ejabberd_commands{name :: atom(),
tags :: [atom()],
desc :: string(),
longdesc :: string(),
version :: integer(),
note :: string(),
weight :: integer(),
module :: atom(),
function :: atom(),
args :: [aterm()],
policy :: open | restricted | admin | user,
access :: [{atom(),atom(),atom()}|atom()],
definer :: atom(),
result :: rterm(),
args_rename :: [{atom(),atom()}],
args_desc :: none | [string()] | '_',
result_desc :: none | string() | '_',
args_example :: none | [any()] | '_',
result_example :: any()
}.

%% @type ejabberd_commands() = #ejabberd_commands{
%% name = atom(),
%% tags = [atom()],
%% desc = string(),
%% longdesc = string(),
%% module = atom(),
%% function = atom(),
%% args = [aterm()],
%% result = rterm()
%% }.
%% desc: Description of the command
%% args: Describe the accepted arguments.
%% This way the function that calls the command can format the
%% arguments before calling.

%% @type atype() = integer | string | {tuple, [aterm()]} | {list, aterm()}.
%% Allowed types for arguments are integer, string, tuple and list.

%% @type rtype() = integer | string | atom | {tuple, [rterm()]} | {list, rterm()} | rescode | restuple.
%% A rtype is either an atom or a tuple with two elements.

%% @type aterm() = {Name::atom(), Type::atype()}.
%% An argument term is a tuple with the term name and the term type.

%% @type rterm() = {Name::atom(), Type::rtype()}.
%% A result term is a tuple with the term name and the term type.
6 changes: 3 additions & 3 deletions src/ejabberd_acme.erl
Original file line number Diff line number Diff line change
Expand Up @@ -450,11 +450,11 @@ delete_obsolete_data() ->
%%%===================================================================
get_commands_spec() ->
[#ejabberd_commands{name = request_certificate, tags = [acme],
desc = "Requests certificates for all or the specified "
"domains: all | domain1,domain2,...",
desc = "Requests certificates for all or some domains",
longdesc = "Domains can be `all`, or a list of domains separared with comma characters",
module = ?MODULE, function = request_certificate,
args_desc = ["Domains for which to acquire a certificate"],
args_example = ["all | domain.tld,conference.domain.tld,..."],
args_example = ["example.com,domain.tld,conference.domain.tld"],
args = [{domains, string}],
result = {res, restuple}},
#ejabberd_commands{name = list_certificates, tags = [acme],
Expand Down
12 changes: 7 additions & 5 deletions src/ejabberd_admin.erl
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ get_commands_spec() ->
desc = "Reopen the log files after being renamed",
longdesc = "This can be useful when an external tool is "
"used for log rotation. See "
"https://docs.ejabberd.im/admin/guide/troubleshooting/#log-files",
"[Log Files](https://docs.ejabberd.im/admin/guide/troubleshooting/#log-files).",
policy = admin,
module = ?MODULE, function = reopen_log,
args = [], result = {res, rescode}},
Expand Down Expand Up @@ -157,9 +157,10 @@ get_commands_spec() ->
result = {levelatom, atom}},
#ejabberd_commands{name = set_loglevel, tags = [logs],
desc = "Set the loglevel",
longdesc = "Possible loglevels: `none`, `emergency`, `alert`, `critical`,
`error`, `warning`, `notice`, `info`, `debug`.",
module = ?MODULE, function = set_loglevel,
args_desc = ["Desired logging level: none | emergency | alert | critical "
"| error | warning | notice | info | debug"],
args_desc = ["Desired logging level"],
args_example = ["debug"],
args = [{loglevel, string}],
result = {res, rescode}},
Expand All @@ -171,7 +172,8 @@ get_commands_spec() ->
result_example = ["mod_configure", "mod_vcard"],
result = {modules, {list, {module, string}}}},
#ejabberd_commands{name = update, tags = [server],
desc = "Update the given module, or use the keyword: all",
desc = "Update the given module",
longdesc = "To update all the possible modules, use `all`.",
module = ?MODULE, function = update,
args_example = ["mod_vcard"],
args = [{module, string}],
Expand Down Expand Up @@ -373,7 +375,7 @@ get_commands_spec() ->
result = {res, rescode}},
#ejabberd_commands{name = set_master, tags = [cluster],
desc = "Set master node of the clustered Mnesia tables",
longdesc = "If you provide as nodename `self`, this "
longdesc = "If `nodename` is set to `self`, then this "
"node will be set as its own master.",
module = ?MODULE, function = set_master,
args_desc = ["Name of the erlang node that will be considered master of this node"],
Expand Down
17 changes: 15 additions & 2 deletions src/ejabberd_commands.erl
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,8 @@ get_commands_spec() ->
args_desc = ["Path to file where generated "
"documentation should be stored",
"Regexp matching names of commands or modules "
"that will be included inside generated document",
"that will be included inside generated document, "
"or `runtime` to get commands registered at runtime",
"Comma separated list of languages (chosen from `java`, `perl`, `xmlrpc`, `json`) "
"that will have example invocation include in markdown document"],
result_desc = "0 if command failed, 1 when succeeded",
Expand Down Expand Up @@ -147,13 +148,25 @@ register_commands(Definer, Commands) ->
lists:foreach(
fun(Command) ->
%% XXX check if command exists
mnesia:dirty_write(Command#ejabberd_commands{definer = Definer})
mnesia:dirty_write(register_command_prepare(Command, Definer))
%% ?DEBUG("This command is already defined:~n~p", [Command])
end,
Commands),
ejabberd_access_permissions:invalidate(),
ok.




register_command_prepare(Command, Definer) ->
Tags1 = Command#ejabberd_commands.tags,
Tags2 = case Command#ejabberd_commands.version of
0 -> Tags1;
Version -> Tags1 ++ [list_to_atom("v"++integer_to_list(Version))]
end,
Command#ejabberd_commands{definer = Definer, tags = Tags2}.


-spec unregister_commands([ejabberd_commands()]) -> ok.

unregister_commands(Commands) ->
Expand Down
14 changes: 11 additions & 3 deletions src/ejabberd_commands_doc.erl
Original file line number Diff line number Diff line change
Expand Up @@ -386,7 +386,7 @@ gen_doc(#ejabberd_commands{name=Name, tags=Tags, desc=Desc, longdesc=LongDesc,
ResultText = case Result of
{res,rescode} ->
[?TAG(dl, [gen_param(res, integer,
"Status code (0 on success, 1 otherwise)",
"Status code (`0` on success, `1` otherwise)",
HTMLOutput)])];
{res,restuple} ->
[?TAG(dl, [gen_param(res, string,
Expand All @@ -400,9 +400,9 @@ gen_doc(#ejabberd_commands{name=Name, tags=Tags, desc=Desc, longdesc=LongDesc,
[?TAG(dl, [gen_param(RName, Type, ResultDesc, HTMLOutput)])]
end
end,
TagsText = [?RAW("*`"++atom_to_list(Tag)++"`* ") || Tag <- Tags],
TagsText = ?RAW(string:join(["*`"++atom_to_list(Tag)++"`*" || Tag <- Tags], ", ")),
IsDefinerMod = case Definer of
unknown -> true;
unknown -> false;
_ -> lists:member(gen_mod, proplists:get_value(behaviour, Definer:module_info(attributes)))
end,
ModuleText = case IsDefinerMod of
Expand Down Expand Up @@ -477,8 +477,16 @@ maybe_add_policy_arguments(#ejabberd_commands{args=Args1, policy=user}=Cmd) ->
maybe_add_policy_arguments(Cmd) ->
Cmd.

generate_md_output(File, <<"runtime">>, Languages) ->
Cmds = lists:map(fun({N, _, _}) ->
ejabberd_commands:get_command_definition(N)
end, ejabberd_commands:list_commands()),
generate_md_output(File, <<".">>, Languages, Cmds);
generate_md_output(File, RegExp, Languages) ->
Cmds = find_commands_definitions(),
generate_md_output(File, RegExp, Languages, Cmds).

generate_md_output(File, RegExp, Languages, Cmds) ->
{ok, RE} = re:compile(RegExp),
Cmds2 = lists:filter(fun(#ejabberd_commands{name=Name, module=Module}) ->
re:run(atom_to_list(Name), RE, [{capture, none}]) == match orelse
Expand Down
Loading