Skip to content

Commit

Permalink
Merge pull request #4118 from badlop/api-version-1
Browse files Browse the repository at this point in the history
Commands API version 1
  • Loading branch information
badlop committed Jan 5, 2024
2 parents 9756819 + fc13fdc commit e26c547
Show file tree
Hide file tree
Showing 12 changed files with 410 additions and 244 deletions.
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

0 comments on commit e26c547

Please sign in to comment.