mirror of
https://github.com/processone/ejabberd.git
synced 2024-11-24 16:23:40 +01:00
Merge pull request #1223 from processone/expand_api
More API fixes and improvements
This commit is contained in:
commit
2ef58a33a9
@ -51,7 +51,7 @@ mech_step(State, ClientIn) ->
|
|||||||
{ok,
|
{ok,
|
||||||
[{username, User}, {authzid, AuthzId},
|
[{username, User}, {authzid, AuthzId},
|
||||||
{auth_module, ejabberd_oauth}]};
|
{auth_module, ejabberd_oauth}]};
|
||||||
false ->
|
_ ->
|
||||||
{error, <<"not-authorized">>, User}
|
{error, <<"not-authorized">>, User}
|
||||||
end;
|
end;
|
||||||
_ -> {error, <<"bad-protocol">>}
|
_ -> {error, <<"bad-protocol">>}
|
||||||
|
@ -87,6 +87,7 @@ get_commands_spec() ->
|
|||||||
args = [], result = {res, rescode}},
|
args = [], result = {res, rescode}},
|
||||||
#ejabberd_commands{name = reopen_log, tags = [logs, server],
|
#ejabberd_commands{name = reopen_log, tags = [logs, server],
|
||||||
desc = "Reopen the log files",
|
desc = "Reopen the log files",
|
||||||
|
policy = admin,
|
||||||
module = ?MODULE, function = reopen_log,
|
module = ?MODULE, function = reopen_log,
|
||||||
args = [], result = {res, rescode}},
|
args = [], result = {res, rescode}},
|
||||||
#ejabberd_commands{name = rotate_log, tags = [logs, server],
|
#ejabberd_commands{name = rotate_log, tags = [logs, server],
|
||||||
@ -380,13 +381,12 @@ register(User, Host, Password) ->
|
|||||||
{atomic, ok} ->
|
{atomic, ok} ->
|
||||||
{ok, io_lib:format("User ~s@~s successfully registered", [User, Host])};
|
{ok, io_lib:format("User ~s@~s successfully registered", [User, Host])};
|
||||||
{atomic, exists} ->
|
{atomic, exists} ->
|
||||||
String = io_lib:format("User ~s@~s already registered at node ~p",
|
Msg = io_lib:format("User ~s@~s already registered", [User, Host]),
|
||||||
[User, Host, node()]),
|
{error, conflict, 10090, Msg};
|
||||||
{exists, String};
|
|
||||||
{error, Reason} ->
|
{error, Reason} ->
|
||||||
String = io_lib:format("Can't register user ~s@~s at node ~p: ~p",
|
String = io_lib:format("Can't register user ~s@~s at node ~p: ~p",
|
||||||
[User, Host, node(), Reason]),
|
[User, Host, node(), Reason]),
|
||||||
{cannot_register, String}
|
{error, cannot_register, 10001, String}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
unregister(User, Host) ->
|
unregister(User, Host) ->
|
||||||
|
@ -425,7 +425,7 @@ get_command_definition(Name, Version) ->
|
|||||||
{V, C}
|
{V, C}
|
||||||
end)))) of
|
end)))) of
|
||||||
[{_, Command} | _ ] -> Command;
|
[{_, Command} | _ ] -> Command;
|
||||||
_E -> throw(unknown_command)
|
_E -> throw({error, unknown_command})
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec get_commands_definition(integer()) -> [ejabberd_commands()].
|
-spec get_commands_definition(integer()) -> [ejabberd_commands()].
|
||||||
@ -682,7 +682,7 @@ check_auth(Command, {User, Server, {oauth, Token}, _}) ->
|
|||||||
case ejabberd_oauth:check_token(User, Server, ScopeList, Token) of
|
case ejabberd_oauth:check_token(User, Server, ScopeList, Token) of
|
||||||
true ->
|
true ->
|
||||||
{ok, User, Server};
|
{ok, User, Server};
|
||||||
false ->
|
_ ->
|
||||||
throw({error, invalid_account_data})
|
throw({error, invalid_account_data})
|
||||||
end;
|
end;
|
||||||
check_auth(_Command, {User, Server, Password, _}) when is_binary(Password) ->
|
check_auth(_Command, {User, Server, Password, _}) when is_binary(Password) ->
|
||||||
|
@ -374,6 +374,12 @@ format_arg2(Arg, Parse)->
|
|||||||
format_result({error, ErrorAtom}, _) ->
|
format_result({error, ErrorAtom}, _) ->
|
||||||
{io_lib:format("Error: ~p", [ErrorAtom]), make_status(error)};
|
{io_lib:format("Error: ~p", [ErrorAtom]), make_status(error)};
|
||||||
|
|
||||||
|
%% An error should always be allowed to return extended error to help with API.
|
||||||
|
%% Extended error is of the form:
|
||||||
|
%% {error, type :: atom(), code :: int(), Desc :: string()}
|
||||||
|
format_result({error, ErrorAtom, Code, _Msg}, _) ->
|
||||||
|
{io_lib:format("Error: ~p", [ErrorAtom]), make_status(Code)};
|
||||||
|
|
||||||
format_result(Atom, {_Name, atom}) ->
|
format_result(Atom, {_Name, atom}) ->
|
||||||
io_lib:format("~p", [Atom]);
|
io_lib:format("~p", [Atom]);
|
||||||
|
|
||||||
@ -433,6 +439,8 @@ format_result(404, {_Name, _}) ->
|
|||||||
|
|
||||||
make_status(ok) -> ?STATUS_SUCCESS;
|
make_status(ok) -> ?STATUS_SUCCESS;
|
||||||
make_status(true) -> ?STATUS_SUCCESS;
|
make_status(true) -> ?STATUS_SUCCESS;
|
||||||
|
make_status(Code) when is_integer(Code), Code > 255 -> ?STATUS_ERROR;
|
||||||
|
make_status(Code) when is_integer(Code), Code > 0 -> Code;
|
||||||
make_status(_Error) -> ?STATUS_ERROR.
|
make_status(_Error) -> ?STATUS_ERROR.
|
||||||
|
|
||||||
get_list_commands(Version) ->
|
get_list_commands(Version) ->
|
||||||
|
@ -302,12 +302,17 @@ check_token(User, Server, ScopeList, Token) ->
|
|||||||
expire = Expire} ->
|
expire = Expire} ->
|
||||||
{MegaSecs, Secs, _} = os:timestamp(),
|
{MegaSecs, Secs, _} = os:timestamp(),
|
||||||
TS = 1000000 * MegaSecs + Secs,
|
TS = 1000000 * MegaSecs + Secs,
|
||||||
TokenScopeSet = oauth2_priv_set:new(TokenScope),
|
if
|
||||||
lists:any(fun(Scope) ->
|
Expire > TS ->
|
||||||
oauth2_priv_set:is_member(Scope, TokenScopeSet) end,
|
TokenScopeSet = oauth2_priv_set:new(TokenScope),
|
||||||
ScopeList) andalso Expire > TS;
|
lists:any(fun(Scope) ->
|
||||||
|
oauth2_priv_set:is_member(Scope, TokenScopeSet) end,
|
||||||
|
ScopeList);
|
||||||
|
true ->
|
||||||
|
{false, expired}
|
||||||
|
end;
|
||||||
_ ->
|
_ ->
|
||||||
false
|
{false, not_found}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
check_token(ScopeList, Token) ->
|
check_token(ScopeList, Token) ->
|
||||||
@ -318,15 +323,20 @@ check_token(ScopeList, Token) ->
|
|||||||
expire = Expire} ->
|
expire = Expire} ->
|
||||||
{MegaSecs, Secs, _} = os:timestamp(),
|
{MegaSecs, Secs, _} = os:timestamp(),
|
||||||
TS = 1000000 * MegaSecs + Secs,
|
TS = 1000000 * MegaSecs + Secs,
|
||||||
TokenScopeSet = oauth2_priv_set:new(TokenScope),
|
if
|
||||||
case lists:any(fun(Scope) ->
|
Expire > TS ->
|
||||||
oauth2_priv_set:is_member(Scope, TokenScopeSet) end,
|
TokenScopeSet = oauth2_priv_set:new(TokenScope),
|
||||||
ScopeList) andalso Expire > TS of
|
case lists:any(fun(Scope) ->
|
||||||
true -> {ok, user, US};
|
oauth2_priv_set:is_member(Scope, TokenScopeSet) end,
|
||||||
false -> false
|
ScopeList) of
|
||||||
|
true -> {ok, user, US};
|
||||||
|
false -> {false, no_matching_scope}
|
||||||
|
end;
|
||||||
|
true ->
|
||||||
|
{false, expired}
|
||||||
end;
|
end;
|
||||||
_ ->
|
_ ->
|
||||||
false
|
{false, not_found}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
|
||||||
|
@ -473,28 +473,34 @@ send_element(Pid, El) ->
|
|||||||
%%% ejabberd commands
|
%%% ejabberd commands
|
||||||
|
|
||||||
get_commands_spec() ->
|
get_commands_spec() ->
|
||||||
[#ejabberd_commands{name = incoming_s2s_number,
|
[#ejabberd_commands{
|
||||||
tags = [stats, s2s],
|
name = incoming_s2s_number,
|
||||||
desc =
|
tags = [stats, s2s],
|
||||||
"Number of incoming s2s connections on "
|
desc = "Number of incoming s2s connections on the node",
|
||||||
"the node",
|
policy = admin,
|
||||||
policy = admin,
|
module = ?MODULE, function = incoming_s2s_number,
|
||||||
module = ?MODULE, function = incoming_s2s_number,
|
args = [], result = {s2s_incoming, integer}},
|
||||||
args = [], result = {s2s_incoming, integer}},
|
#ejabberd_commands{
|
||||||
#ejabberd_commands{name = outgoing_s2s_number,
|
name = outgoing_s2s_number,
|
||||||
tags = [stats, s2s],
|
tags = [stats, s2s],
|
||||||
desc =
|
desc = "Number of outgoing s2s connections on the node",
|
||||||
"Number of outgoing s2s connections on "
|
policy = admin,
|
||||||
"the node",
|
module = ?MODULE, function = outgoing_s2s_number,
|
||||||
policy = admin,
|
args = [], result = {s2s_outgoing, integer}}].
|
||||||
module = ?MODULE, function = outgoing_s2s_number,
|
|
||||||
args = [], result = {s2s_outgoing, integer}}].
|
|
||||||
|
|
||||||
|
%% TODO Move those stats commands to ejabberd stats command ?
|
||||||
incoming_s2s_number() ->
|
incoming_s2s_number() ->
|
||||||
length(supervisor:which_children(ejabberd_s2s_in_sup)).
|
supervisor_count(ejabberd_s2s_in_sup).
|
||||||
|
|
||||||
outgoing_s2s_number() ->
|
outgoing_s2s_number() ->
|
||||||
length(supervisor:which_children(ejabberd_s2s_out_sup)).
|
supervisor_count(ejabberd_s2s_out_sup).
|
||||||
|
|
||||||
|
supervisor_count(Supervisor) ->
|
||||||
|
case catch supervisor:which_children(Supervisor) of
|
||||||
|
{'EXIT', _} -> 0;
|
||||||
|
Result ->
|
||||||
|
length(Result)
|
||||||
|
end.
|
||||||
|
|
||||||
%%%----------------------------------------------------------------------
|
%%%----------------------------------------------------------------------
|
||||||
%%% Update Mnesia tables
|
%%% Update Mnesia tables
|
||||||
|
@ -535,7 +535,7 @@ get_commands_spec() ->
|
|||||||
policy = user,
|
policy = user,
|
||||||
module = mod_offline, function = count_offline_messages,
|
module = mod_offline, function = count_offline_messages,
|
||||||
args = [],
|
args = [],
|
||||||
result = {res, integer}},
|
result = {value, integer}},
|
||||||
#ejabberd_commands{name = send_message, tags = [stanza],
|
#ejabberd_commands{name = send_message, tags = [stanza],
|
||||||
desc = "Send a message to a local or remote bare of full JID",
|
desc = "Send a message to a local or remote bare of full JID",
|
||||||
module = ?MODULE, function = send_message,
|
module = ?MODULE, function = send_message,
|
||||||
|
@ -162,14 +162,15 @@ check_permissions2(#request{auth = HTTPAuth, headers = Headers}, Call, _, ScopeL
|
|||||||
case oauth_check_token(ScopeList, Token) of
|
case oauth_check_token(ScopeList, Token) of
|
||||||
{ok, user, {User, Server}} ->
|
{ok, user, {User, Server}} ->
|
||||||
{ok, {User, Server, {oauth, Token}, Admin}};
|
{ok, {User, Server, {oauth, Token}, Admin}};
|
||||||
false ->
|
{false, Reason} ->
|
||||||
false
|
{false, Reason}
|
||||||
end;
|
end;
|
||||||
_ ->
|
_ ->
|
||||||
false
|
false
|
||||||
end,
|
end,
|
||||||
case Auth of
|
case Auth of
|
||||||
{ok, A} -> {allowed, Call, A};
|
{ok, A} -> {allowed, Call, A};
|
||||||
|
{false, no_matching_scope} -> outofscope_response();
|
||||||
_ -> unauthorized_response()
|
_ -> unauthorized_response()
|
||||||
end;
|
end;
|
||||||
check_permissions2(_Request, Call, open, _Scope) ->
|
check_permissions2(_Request, Call, open, _Scope) ->
|
||||||
@ -189,7 +190,7 @@ check_permissions2(#request{ip={IP, _Port}}, Call, _Policy, _Scope) ->
|
|||||||
Commands when is_list(Commands) ->
|
Commands when is_list(Commands) ->
|
||||||
case lists:member(Call, Commands) of
|
case lists:member(Call, Commands) of
|
||||||
true -> {allowed, Call, admin};
|
true -> {allowed, Call, admin};
|
||||||
_ -> unauthorized_response()
|
_ -> outofscope_response()
|
||||||
end;
|
end;
|
||||||
_E ->
|
_E ->
|
||||||
{allowed, Call, noauth}
|
{allowed, Call, noauth}
|
||||||
@ -212,28 +213,24 @@ process(_, #request{method = 'POST', data = <<>>}) ->
|
|||||||
process([Call], #request{method = 'POST', data = Data, ip = {IP, _} = IPPort} = Req) ->
|
process([Call], #request{method = 'POST', data = Data, ip = {IP, _} = IPPort} = Req) ->
|
||||||
Version = get_api_version(Req),
|
Version = get_api_version(Req),
|
||||||
try
|
try
|
||||||
Args = case jiffy:decode(Data) of
|
Args = extract_args(Data),
|
||||||
List when is_list(List) -> List;
|
|
||||||
{List} when is_list(List) -> List;
|
|
||||||
Other -> [Other]
|
|
||||||
end,
|
|
||||||
log(Call, Args, IPPort),
|
log(Call, Args, IPPort),
|
||||||
case check_permissions(Req, Call) of
|
case check_permissions(Req, Call) of
|
||||||
{allowed, Cmd, Auth} ->
|
{allowed, Cmd, Auth} ->
|
||||||
case handle(Cmd, Auth, Args, Version, IP) of
|
Result = handle(Cmd, Auth, Args, Version, IP),
|
||||||
{Code, Result} ->
|
json_format(Result);
|
||||||
json_response(Code, jiffy:encode(Result));
|
|
||||||
{HTMLCode, JSONErrorCode, Message} ->
|
|
||||||
json_error(HTMLCode, JSONErrorCode, Message)
|
|
||||||
end;
|
|
||||||
%% Warning: check_permission direcly formats 401 reply if not authorized
|
%% Warning: check_permission direcly formats 401 reply if not authorized
|
||||||
ErrorResponse ->
|
ErrorResponse ->
|
||||||
ErrorResponse
|
ErrorResponse
|
||||||
end
|
end
|
||||||
catch _:{error,{_,invalid_json}} = _Err ->
|
catch
|
||||||
?DEBUG("Bad Request: ~p", [_Err]),
|
%% TODO We need to refactor to remove redundant error return formatting
|
||||||
badrequest_response(<<"Invalid JSON input">>);
|
throw:{error, unknown_command} ->
|
||||||
_:_Error ->
|
{404, 40, <<"Command not found.">>};
|
||||||
|
_:{error,{_,invalid_json}} = _Err ->
|
||||||
|
?DEBUG("Bad Request: ~p", [_Err]),
|
||||||
|
badrequest_response(<<"Invalid JSON input">>);
|
||||||
|
_:_Error ->
|
||||||
?DEBUG("Bad Request: ~p ~p", [_Error, erlang:get_stacktrace()]),
|
?DEBUG("Bad Request: ~p ~p", [_Error, erlang:get_stacktrace()]),
|
||||||
badrequest_response()
|
badrequest_response()
|
||||||
end;
|
end;
|
||||||
@ -247,13 +244,18 @@ process([Call], #request{method = 'GET', q = Data, ip = IP} = Req) ->
|
|||||||
log(Call, Args, IP),
|
log(Call, Args, IP),
|
||||||
case check_permissions(Req, Call) of
|
case check_permissions(Req, Call) of
|
||||||
{allowed, Cmd, Auth} ->
|
{allowed, Cmd, Auth} ->
|
||||||
{Code, Result} = handle(Cmd, Auth, Args, Version, IP),
|
Result = handle(Cmd, Auth, Args, Version, IP),
|
||||||
json_response(Code, jiffy:encode(Result));
|
json_format(Result);
|
||||||
%% Warning: check_permission direcly formats 401 reply if not authorized
|
%% Warning: check_permission direcly formats 401 reply if not authorized
|
||||||
ErrorResponse ->
|
ErrorResponse ->
|
||||||
ErrorResponse
|
ErrorResponse
|
||||||
end
|
end
|
||||||
catch _:_Error ->
|
catch
|
||||||
|
%% TODO We need to refactor to remove redundant error return formatting
|
||||||
|
throw:{error, unknown_command} ->
|
||||||
|
json_format({404, 44, <<"Command not found.">>});
|
||||||
|
_:_Error ->
|
||||||
|
|
||||||
?DEBUG("Bad Request: ~p ~p", [_Error, erlang:get_stacktrace()]),
|
?DEBUG("Bad Request: ~p ~p", [_Error, erlang:get_stacktrace()]),
|
||||||
badrequest_response()
|
badrequest_response()
|
||||||
end;
|
end;
|
||||||
@ -261,7 +263,16 @@ process([], #request{method = 'OPTIONS', data = <<>>}) ->
|
|||||||
{200, ?OPTIONS_HEADER, []};
|
{200, ?OPTIONS_HEADER, []};
|
||||||
process(_Path, Request) ->
|
process(_Path, Request) ->
|
||||||
?DEBUG("Bad Request: no handler ~p", [Request]),
|
?DEBUG("Bad Request: no handler ~p", [Request]),
|
||||||
badrequest_response().
|
json_error(400, 40, <<"Missing command name.">>).
|
||||||
|
|
||||||
|
%% Be tolerant to make API more easily usable from command-line pipe.
|
||||||
|
extract_args(<<"\n">>) -> [];
|
||||||
|
extract_args(Data) ->
|
||||||
|
case jiffy:decode(Data) of
|
||||||
|
List when is_list(List) -> List;
|
||||||
|
{List} when is_list(List) -> List;
|
||||||
|
Other -> [Other]
|
||||||
|
end.
|
||||||
|
|
||||||
% get API version N from last "vN" element in URL path
|
% get API version N from last "vN" element in URL path
|
||||||
get_api_version(#request{path = Path}) ->
|
get_api_version(#request{path = Path}) ->
|
||||||
@ -302,7 +313,7 @@ handle(Call, Auth, Args, Version, IP) when is_atom(Call), is_list(Args) ->
|
|||||||
[{Key, undefined}|Acc]
|
[{Key, undefined}|Acc]
|
||||||
end, [], ArgsSpec),
|
end, [], ArgsSpec),
|
||||||
try
|
try
|
||||||
handle2(Call, Auth, match(Args2, Spec), Version, IP)
|
handle2(Call, Auth, match(Args2, Spec), Version, IP)
|
||||||
catch throw:not_found ->
|
catch throw:not_found ->
|
||||||
{404, <<"not_found">>};
|
{404, <<"not_found">>};
|
||||||
throw:{not_found, Why} when is_atom(Why) ->
|
throw:{not_found, Why} when is_atom(Why) ->
|
||||||
@ -444,22 +455,24 @@ ejabberd_command(Auth, Cmd, Args, Version, IP) ->
|
|||||||
format_command_result(Cmd, Auth, Result, Version) ->
|
format_command_result(Cmd, Auth, Result, Version) ->
|
||||||
{_, ResultFormat} = ejabberd_commands:get_command_format(Cmd, Auth, Version),
|
{_, ResultFormat} = ejabberd_commands:get_command_format(Cmd, Auth, Version),
|
||||||
case {ResultFormat, Result} of
|
case {ResultFormat, Result} of
|
||||||
{{_, rescode}, V} when V == true; V == ok ->
|
{{_, rescode}, V} when V == true; V == ok ->
|
||||||
{200, 0};
|
{200, 0};
|
||||||
{{_, rescode}, _} ->
|
{{_, rescode}, _} ->
|
||||||
{200, 1};
|
{200, 1};
|
||||||
{{_, restuple}, {V1, Text1}} when V1 == true; V1 == ok ->
|
{_, {error, ErrorAtom, Code, Msg}} ->
|
||||||
{200, iolist_to_binary(Text1)};
|
format_error_result(ErrorAtom, Code, Msg);
|
||||||
{{_, restuple}, {_, Text2}} ->
|
{{_, restuple}, {V, Text}} when V == true; V == ok ->
|
||||||
{500, iolist_to_binary(Text2)};
|
{200, iolist_to_binary(Text)};
|
||||||
{{_, {list, _}}, _V} ->
|
{{_, restuple}, {ErrorAtom, Msg}} ->
|
||||||
{_, L} = format_result(Result, ResultFormat),
|
format_error_result(ErrorAtom, 0, Msg);
|
||||||
{200, L};
|
{{_, {list, _}}, _V} ->
|
||||||
{{_, {tuple, _}}, _V} ->
|
{_, L} = format_result(Result, ResultFormat),
|
||||||
{_, T} = format_result(Result, ResultFormat),
|
{200, L};
|
||||||
{200, T};
|
{{_, {tuple, _}}, _V} ->
|
||||||
_ ->
|
{_, T} = format_result(Result, ResultFormat),
|
||||||
{200, {[format_result(Result, ResultFormat)]}}
|
{200, T};
|
||||||
|
_ ->
|
||||||
|
{200, {[format_result(Result, ResultFormat)]}}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
format_result(Atom, {Name, atom}) ->
|
format_result(Atom, {Name, atom}) ->
|
||||||
@ -497,14 +510,28 @@ format_result(Tuple, {Name, {tuple, Def}}) ->
|
|||||||
format_result(404, {_Name, _}) ->
|
format_result(404, {_Name, _}) ->
|
||||||
"not_found".
|
"not_found".
|
||||||
|
|
||||||
|
|
||||||
|
format_error_result(conflict, Code, Msg) ->
|
||||||
|
{409, Code, iolist_to_binary(Msg)};
|
||||||
|
format_error_result(_ErrorAtom, Code, Msg) ->
|
||||||
|
{500, Code, iolist_to_binary(Msg)}.
|
||||||
|
|
||||||
unauthorized_response() ->
|
unauthorized_response() ->
|
||||||
json_error(401, 10, <<"Oauth Token is invalid or expired.">>).
|
json_error(401, 10, <<"Oauth Token is invalid or expired.">>).
|
||||||
|
|
||||||
|
outofscope_response() ->
|
||||||
|
json_error(401, 11, <<"Token does not grant usage to command required scope.">>).
|
||||||
|
|
||||||
badrequest_response() ->
|
badrequest_response() ->
|
||||||
badrequest_response(<<"400 Bad Request">>).
|
badrequest_response(<<"400 Bad Request">>).
|
||||||
badrequest_response(Body) ->
|
badrequest_response(Body) ->
|
||||||
json_response(400, jiffy:encode(Body)).
|
json_response(400, jiffy:encode(Body)).
|
||||||
|
|
||||||
|
json_format({Code, Result}) ->
|
||||||
|
json_response(Code, jiffy:encode(Result));
|
||||||
|
json_format({HTMLCode, JSONErrorCode, Message}) ->
|
||||||
|
json_error(HTMLCode, JSONErrorCode, Message).
|
||||||
|
|
||||||
json_response(Code, Body) when is_integer(Code) ->
|
json_response(Code, Body) when is_integer(Code) ->
|
||||||
{Code, ?HEADER(?CT_JSON), Body}.
|
{Code, ?HEADER(?CT_JSON), Body}.
|
||||||
|
|
||||||
|
@ -174,7 +174,7 @@ defmodule EjabberdCommandsMockTest do
|
|||||||
# default version is latest one
|
# default version is latest one
|
||||||
assert :result3 == :ejabberd_commands.execute_command(command_name, [])
|
assert :result3 == :ejabberd_commands.execute_command(command_name, [])
|
||||||
# no such command in APIv0
|
# no such command in APIv0
|
||||||
assert :unknown_command ==
|
assert {:error, :unknown_command} ==
|
||||||
catch_throw :ejabberd_commands.execute_command(command_name, [], 0)
|
catch_throw :ejabberd_commands.execute_command(command_name, [], 0)
|
||||||
assert :result1 == :ejabberd_commands.execute_command(command_name, [], 1)
|
assert :result1 == :ejabberd_commands.execute_command(command_name, [], 1)
|
||||||
assert :result1 == :ejabberd_commands.execute_command(command_name, [], 2)
|
assert :result1 == :ejabberd_commands.execute_command(command_name, [], 2)
|
||||||
|
Loading…
Reference in New Issue
Block a user