Skip to content

Commit

Permalink
Adding graphql resolvers for stats
Browse files Browse the repository at this point in the history
  • Loading branch information
Janusz Jakubiec authored and Janusz Jakubiec committed Jul 6, 2022
1 parent bbdc781 commit ed6afad
Show file tree
Hide file tree
Showing 11 changed files with 211 additions and 53 deletions.
1 change: 1 addition & 0 deletions big_tests/default.spec
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
{suites, "tests", graphql_roster_SUITE}.
{suites, "tests", graphql_session_SUITE}.
{suites, "tests", graphql_stanza_SUITE}.
{suites, "tests", graphql_stats_SUITE}.
{suites, "tests", graphql_vcard_SUITE}.
{suites, "tests", graphql_http_upload_SUITE}.
{suites, "tests", graphql_metric_SUITE}.
Expand Down
1 change: 1 addition & 0 deletions big_tests/dynamic_domains.spec
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
{suites, "tests", graphql_stanza_SUITE}.
{suites, "tests", graphql_vcard_SUITE}.
{suites, "tests", graphql_offline_SUITE}.
{suites, "tests", graphql_stats_SUITE}.
{suites, "tests", graphql_http_upload_SUITE}.
{suites, "tests", graphql_metric_SUITE}.

Expand Down
116 changes: 116 additions & 0 deletions big_tests/tests/graphql_stats_SUITE.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
-module(graphql_stats_SUITE).

-compile([export_all, nowarn_export_all]).

-import(distributed_helper, [require_rpc_nodes/1]).
-import(domain_helper, [host_type/0, domain/0]).
-import(graphql_helper, [execute_user/3, execute_auth/2, user_to_bin/1]).
-import(config_parser_helper, [mod_config/2]).
-import(mongooseimctl_helper, [mongooseimctl/3, rpc_call/3]).

-include_lib("common_test/include/ct.hrl").
-include_lib("eunit/include/eunit.hrl").
-include_lib("exml/include/exml.hrl").
-include_lib("escalus/include/escalus.hrl").
-include("../../include/mod_roster.hrl").

suite() ->
require_rpc_nodes([mim]) ++ escalus:suite().

all() ->
[{group, admin_stats}].

groups() ->
[{admin_stats, [], admin_stats_handler()}].

admin_stats_handler() ->
[admin_get_incoming_s2s_number_test,
admin_get_outgoing_s2s_number_test,
admin_stats_no_stat_name_test,
admin_stats_domain_no_stat_name_test,
admin_stats_test,
admin_stats_domain_test].

init_per_suite(Config) ->
escalus:init_per_suite(Config).

end_per_suite(Config) ->
escalus:end_per_suite(Config).

init_per_group(_, Config) ->
graphql_helper:init_admin_handler(Config).

end_per_group(_, _Config) ->
escalus_fresh:clean().

init_per_testcase(CaseName, Config) ->
escalus:init_per_testcase(CaseName, Config).

end_per_testcase(CaseName, Config) ->
escalus:end_per_testcase(CaseName, Config).

% Admin test cases

admin_get_incoming_s2s_number_test(Config) ->
GraphQlRequest = admin_get_incoming_s2s(Config, #{}),
Number = ok_result(<<"stats">>, <<"getIncomingS2SNumber">>, GraphQlRequest),
?assertEqual(0, Number).

admin_get_outgoing_s2s_number_test(Config) ->
GraphQlRequest = admin_get_outgoing_s2s(Config, #{}),
Number = ok_result(<<"stats">>, <<"getOutgoingS2SNumber">>, GraphQlRequest),
?assertEqual(0, Number).

admin_stats_no_stat_name_test(Config) ->
GraphQlRequest = admin_get_stats(Config, #{<<"statName">> => <<"AAA">>}),
ParsedResult = error_result(<<"extensions">>, <<"code">>, GraphQlRequest),
?assertEqual(<<"no_command_error">>, ParsedResult).

admin_stats_domain_no_stat_name_test(Config) ->
Vars = #{<<"statName">> => <<"AAA">>, <<"domain">> => domain()},
GraphQlRequest = admin_get_stats_domain(Config, Vars),
ParsedResult = error_result(<<"extensions">>, <<"code">>, GraphQlRequest),
?assertEqual(<<"no_command_error">>, ParsedResult).

admin_stats_test(Config) ->
GraphQlRequest = admin_get_stats(Config, #{<<"statName">> => <<"onlineusers">>}),
Number = ok_result(<<"stats">>, <<"stats">>, GraphQlRequest),
?assertEqual(0, Number).

admin_stats_domain_test(Config) ->
Vars = #{<<"statName">> => <<"onlineusers">>, <<"domain">> => domain()},
GraphQlRequest = admin_get_stats(Config, Vars),
Number = ok_result(<<"stats">>, <<"stats">>, GraphQlRequest),
?assertEqual(0, Number).

% Helpers

admin_get_incoming_s2s(Config, Vars) ->
Query = <<"query Q1
{stats{getIncomingS2SNumber}}">>,
admin_send_mutation(Config, Vars, Query).

admin_get_outgoing_s2s(Config, Vars) ->
Query = <<"query Q1
{stats{getOutgoingS2SNumber}}">>,
admin_send_mutation(Config, Vars, Query).

admin_get_stats(Config, Vars) ->
Query = <<"query Q1($statName: String!)
{stats{stats(statName: $statName)}}">>,
admin_send_mutation(Config, Vars, Query).

admin_get_stats_domain(Config, Vars) ->
Query = <<"query Q1($statName: String!, $domain: String)
{stats{stats(statName: $statName, domain: $domain)}}">>,
admin_send_mutation(Config, Vars, Query).

admin_send_mutation(Config, Vars, Query) ->
Body = #{query => Query, operationName => <<"Q1">>, variables => Vars},
execute_auth(Body, Config).

error_result(What1, What2, {{<<"200">>, <<"OK">>}, #{<<"errors">> := [Data]}}) ->
maps:get(What2, maps:get(What1, Data)).

ok_result(What1, What2, {{<<"200">>, <<"OK">>}, #{<<"data">> := Data}}) ->
maps:get(What2, maps:get(What1, Data)).
2 changes: 2 additions & 0 deletions priv/graphql/schemas/admin/admin_schema.gql
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ type AdminQuery{
private: PrivateAdminQuery
"Metrics management"
metric: MetricAdminQuery
"Statistics"
stats: StatsAdminQuery
}

"""
Expand Down
10 changes: 10 additions & 0 deletions priv/graphql/schemas/admin/stats.gql
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
"Allow admin to get statistics"
type StatsAdminQuery @protected{
"Get number of incoming s2s connections"
getIncomingS2SNumber: Int
"Get number of outgoing s2s connections"
getOutgoingS2SNumber: Int
"Get specific stat"
stats(statName: String!, domain: String): Int
@protected(type: DOMAIN, args: ["domain"])
}
43 changes: 3 additions & 40 deletions src/admin_extra/service_admin_extra_stats.erl
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,7 @@
-author('[email protected]').


-export([
commands/0,

stats/1, stats/2
]).
-export([commands/0]).

-ignore_xref([commands/0, stats/1, stats/2]).

Expand All @@ -48,46 +44,13 @@ commands() ->
#ejabberd_commands{name = stats, tags = [stats],
desc = "Get statistical value:"
" registeredusers onlineusers onlineusersnode uptimeseconds",
module = ?MODULE, function = stats,
module = stats_api, function = stats,
args = [{name, binary}],
result = {stat, integer}},
#ejabberd_commands{name = stats_host, tags = [stats],
desc = "Get statistical value for this host:"
" registeredusers onlineusers",
module = ?MODULE, function = stats,
module = stats_api, function = stats,
args = [{name, binary}, {host, binary}],
result = {stat, integer}}
].

%%%
%%% Stats
%%%

-spec stats(binary()) -> integer() | {error, string()}.
stats(Name) ->
case Name of
<<"uptimeseconds">> ->
trunc(element(1, erlang:statistics(wall_clock))/1000);
<<"registeredusers">> ->
Domains = lists:flatmap(fun mongoose_domain_api:get_domains_by_host_type/1,
?ALL_HOST_TYPES),
lists:sum([ejabberd_auth:get_vh_registered_users_number(Domain) || Domain <- Domains]);
<<"onlineusersnode">> ->
ejabberd_sm:get_node_sessions_number();
<<"onlineusers">> ->
ejabberd_sm:get_total_sessions_number();
_ ->
{error, "Wrong command name."}
end.


-spec stats(binary(), jid:server()) -> integer() | {error, string()}.
stats(Name, Host) ->
case Name of
<<"registeredusers">> ->
ejabberd_auth:get_vh_registered_users_number(Host);
<<"onlineusers">> ->
ejabberd_sm:get_vh_session_number(Host);
_ ->
{error, "Wrong command name."}
end.
15 changes: 2 additions & 13 deletions src/ejabberd_s2s.erl
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,6 @@
find_connection/2,
dirty_get_connections/0,
allow_host/2,
incoming_s2s_number/0,
outgoing_s2s_number/0,
domain_utf8_to_ascii/1,
timeout/0,
lookup_certfile/1
Expand Down Expand Up @@ -516,26 +514,17 @@ commands() ->
#ejabberd_commands{name = incoming_s2s_number,
tags = [stats, s2s],
desc = "Number of incoming s2s connections on the node",
module = ?MODULE, function = incoming_s2s_number,
module = stats_api, function = incoming_s2s_number,
args = [],
result = {s2s_incoming, integer}},
#ejabberd_commands{name = outgoing_s2s_number,
tags = [stats, s2s],
desc = "Number of outgoing s2s connections on the node",
module = ?MODULE, function = outgoing_s2s_number,
module = stats_api, function = outgoing_s2s_number,
args = [],
result = {s2s_outgoing, integer}}
].

-spec incoming_s2s_number() -> non_neg_integer().
incoming_s2s_number() ->
length(supervisor:which_children(ejabberd_s2s_in_sup)).

-spec outgoing_s2s_number() -> non_neg_integer().
outgoing_s2s_number() ->
length(supervisor:which_children(ejabberd_s2s_out_sup)).


%% Check if host is in blacklist or white list
allow_host(MyServer, S2SHost) ->
case mongoose_domain_api:get_host_type(MyServer) of
Expand Down
2 changes: 2 additions & 0 deletions src/graphql/admin/mongoose_graphql_admin_query.erl
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ execute(_Ctx, _Obj, <<"roster">>, _Args) ->
{ok, roster};
execute(_Ctx, _Obj, <<"session">>, _Args) ->
{ok, session};
execute(_Ctx, _Obj, <<"stats">>, _Args) ->
{ok, stats};
execute(_Ctx, _Obj, <<"stanza">>, _Args) ->
{ok, #{}};
execute(_Ctx, _Obj, <<"vcard">>, _Args) ->
Expand Down
31 changes: 31 additions & 0 deletions src/graphql/admin/mongoose_graphql_stats_admin_query.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
-module(mongoose_graphql_stats_admin_query).
-behaviour(mongoose_graphql).

-export([execute/4]).

-import(mongoose_graphql_helper, [make_error/2]).

-ignore_xref([execute/4]).

-include("../mongoose_graphql_types.hrl").
-include("mongoose.hrl").
-include("jlib.hrl").

execute(_Ctx, stats, <<"getIncomingS2SNumber">>, #{}) ->
{ok, stats_api:incoming_s2s_number()};
execute(_Ctx, stats, <<"getOutgoingS2SNumber">>, #{}) ->
{ok, stats_api:outgoing_s2s_number()};
execute(_Ctx, stats, <<"stats">>, #{<<"domain">> := null, <<"statName">> := StatName}) ->
case stats_api:stats(StatName) of
{error, String} ->
make_error({no_command_error, String}, #{statName => StatName});
Result ->
{ok, Result}
end;
execute(_Ctx, stats, <<"stats">>, #{<<"domain">> := Domain, <<"statName">> := StatName}) ->
case stats_api:stats(StatName, Domain) of
{error, String} ->
make_error({no_command_error, String}, #{statName => StatName});
Result ->
{ok, Result}
end.
1 change: 1 addition & 0 deletions src/graphql/mongoose_graphql.erl
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ admin_mapping_rules() ->
'SessionAdminMutation' => mongoose_graphql_session_admin_mutation,
'SessionAdminQuery' => mongoose_graphql_session_admin_query,
'StanzaAdminMutation' => mongoose_graphql_stanza_admin_mutation,
'StatsAdminQuery' => mongoose_graphql_stats_admin_query,
'StanzaAdminQuery' => mongoose_graphql_stanza_admin_query,
'LastAdminMutation' => mongoose_graphql_last_admin_mutation,
'LastAdminQuery' => mongoose_graphql_last_admin_query,
Expand Down
42 changes: 42 additions & 0 deletions src/stats_api.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
-module(stats_api).

-export([incoming_s2s_number/0, outgoing_s2s_number/0, stats/1, stats/2]).

-include("mongoose.hrl").
-include("ejabberd_commands.hrl").

-spec incoming_s2s_number() -> non_neg_integer().
incoming_s2s_number() ->
length(supervisor:which_children(ejabberd_s2s_in_sup)).

-spec outgoing_s2s_number() -> non_neg_integer().
outgoing_s2s_number() ->
length(supervisor:which_children(ejabberd_s2s_out_sup)).

-spec stats(binary()) -> integer() | {error, string()}.
stats(Name) ->
case Name of
<<"uptimeseconds">> ->
trunc(element(1, erlang:statistics(wall_clock))/1000);
<<"registeredusers">> ->
Domains = lists:flatmap(fun mongoose_domain_api:get_domains_by_host_type/1,
?ALL_HOST_TYPES),
lists:sum([ejabberd_auth:get_vh_registered_users_number(Domain) || Domain <- Domains]);
<<"onlineusersnode">> ->
ejabberd_sm:get_node_sessions_number();
<<"onlineusers">> ->
ejabberd_sm:get_total_sessions_number();
_ ->
{error, "Wrong command name."}
end.

-spec stats(binary(), jid:server()) -> integer() | {error, string()}.
stats(Name, Host) ->
case Name of
<<"registeredusers">> ->
ejabberd_auth:get_vh_registered_users_number(Host);
<<"onlineusers">> ->
ejabberd_sm:get_vh_session_number(Host);
_ ->
{error, "Wrong command name."}
end.

0 comments on commit ed6afad

Please sign in to comment.