refactor: move metrics out of /rules(/:id) to /rules/:id/metrics

This commit is contained in:
Stefan Strigler 2022-12-01 15:30:23 +01:00
parent 380b75d4a9
commit 0b324da7cb
7 changed files with 113 additions and 44 deletions

View File

@ -641,7 +641,7 @@ t_ingress_mqtt_bridge_with_rules(_) ->
end end
), ),
%% and also the rule should be matched, with matched + 1: %% and also the rule should be matched, with matched + 1:
{ok, 200, Rule1} = request(get, uri(["rules", RuleId]), []), {ok, 200, Rule1} = request(get, uri(["rules", RuleId, "metrics"]), []),
#{ #{
<<"id">> := RuleId, <<"id">> := RuleId,
<<"metrics">> := #{ <<"metrics">> := #{
@ -748,7 +748,7 @@ t_egress_mqtt_bridge_with_rules(_) ->
timer:sleep(100), timer:sleep(100),
wait_for_resource_ready(BridgeIDEgress, 5), wait_for_resource_ready(BridgeIDEgress, 5),
emqx:publish(emqx_message:make(RuleTopic, Payload2)), emqx:publish(emqx_message:make(RuleTopic, Payload2)),
{ok, 200, Rule1} = request(get, uri(["rules", RuleId]), []), {ok, 200, Rule1} = request(get, uri(["rules", RuleId, "metrics"]), []),
#{ #{
<<"id">> := RuleId, <<"id">> := RuleId,
<<"metrics">> := #{ <<"metrics">> := #{

View File

@ -84,6 +84,17 @@ emqx_rule_engine_api {
} }
} }
api4_1 {
desc {
en: "Get a rule's metrics by given Id"
zh: "通过给定的 Id 获得规则的指标数据"
}
label: {
en: "Get Metric"
zh: "获得指标数据"
}
}
api5 { api5 {
desc { desc {
en: "Update a rule by given Id to all nodes in the cluster" en: "Update a rule by given Id to all nodes in the cluster"

View File

@ -43,12 +43,6 @@ fields("rule_creation") ->
fields("rule_info") -> fields("rule_info") ->
[ [
rule_id(), rule_id(),
{"metrics", sc(ref("metrics"), #{desc => ?DESC("ri_metrics")})},
{"node_metrics",
sc(
hoconsc:array(ref("node_metrics")),
#{desc => ?DESC("ri_node_metrics")}
)},
{"from", {"from",
sc( sc(
hoconsc:array(binary()), hoconsc:array(binary()),
@ -63,6 +57,16 @@ fields("rule_info") ->
} }
)} )}
] ++ fields("rule_creation"); ] ++ fields("rule_creation");
fields("rule_metrics") ->
[
rule_id(),
{"metrics", sc(ref("metrics"), #{desc => ?DESC("ri_metrics")})},
{"node_metrics",
sc(
hoconsc:array(ref("node_metrics")),
#{desc => ?DESC("ri_node_metrics")}
)}
];
%% TODO: we can delete this API if the Dashboard not depends on it %% TODO: we can delete this API if the Dashboard not depends on it
fields("rule_events") -> fields("rule_events") ->
ETopics = emqx_rule_events:event_topics_enum(), ETopics = emqx_rule_events:event_topics_enum(),

View File

@ -31,12 +31,18 @@
-export([api_spec/0, paths/0, schema/1, namespace/0]). -export([api_spec/0, paths/0, schema/1, namespace/0]).
%% API callbacks %% API callbacks
-export(['/rule_events'/2, '/rule_test'/2, '/rules'/2, '/rules/:id'/2, '/rules/:id/reset_metrics'/2]). -export([
'/rule_events'/2,
'/rule_test'/2,
'/rules'/2,
'/rules/:id'/2,
'/rules/:id/metrics'/2,
'/rules/:id/metrics/reset'/2
]).
%% query callback %% query callback
-export([qs2ms/2, run_fuzzy_match/2, format_rule_resp/1]). -export([qs2ms/2, run_fuzzy_match/2, format_rule_resp/1]).
-define(ERR_NO_RULE(ID), list_to_binary(io_lib:format("Rule ~ts Not Found", [(ID)]))).
-define(ERR_BADARGS(REASON), begin -define(ERR_BADARGS(REASON), begin
R0 = err_msg(REASON), R0 = err_msg(REASON),
<<"Bad Arguments: ", R0/binary>> <<"Bad Arguments: ", R0/binary>>
@ -126,7 +132,15 @@ namespace() -> "rule".
api_spec() -> api_spec() ->
emqx_dashboard_swagger:spec(?MODULE, #{check_schema => false}). emqx_dashboard_swagger:spec(?MODULE, #{check_schema => false}).
paths() -> ["/rule_events", "/rule_test", "/rules", "/rules/:id", "/rules/:id/reset_metrics"]. paths() ->
[
"/rule_events",
"/rule_test",
"/rules",
"/rules/:id",
"/rules/:id/metrics",
"/rules/:id/metrics/reset"
].
error_schema(Code, Message) when is_atom(Code) -> error_schema(Code, Message) when is_atom(Code) ->
emqx_dashboard_swagger:error_codes([Code], list_to_binary(Message)). emqx_dashboard_swagger:error_codes([Code], list_to_binary(Message)).
@ -140,6 +154,9 @@ rule_test_schema() ->
rule_info_schema() -> rule_info_schema() ->
ref(emqx_rule_api_schema, "rule_info"). ref(emqx_rule_api_schema, "rule_info").
rule_metrics_schema() ->
ref(emqx_rule_api_schema, "rule_metrics").
schema("/rules") -> schema("/rules") ->
#{ #{
'operationId' => '/rules', 'operationId' => '/rules',
@ -230,17 +247,31 @@ schema("/rules/:id") ->
} }
} }
}; };
schema("/rules/:id/reset_metrics") -> schema("/rules/:id/metrics") ->
#{ #{
'operationId' => '/rules/:id/reset_metrics', 'operationId' => '/rules/:id/metrics',
get => #{
tags => [<<"rules">>],
description => ?DESC("api4_1"),
summary => <<"Get a Rule's Metrics">>,
parameters => param_path_id(),
responses => #{
404 => error_schema('NOT_FOUND', "Rule not found"),
200 => rule_metrics_schema()
}
}
};
schema("/rules/:id/metrics/reset") ->
#{
'operationId' => '/rules/:id/metrics/reset',
put => #{ put => #{
tags => [<<"rules">>], tags => [<<"rules">>],
description => ?DESC("api7"), description => ?DESC("api7"),
summary => <<"Reset a Rule Metrics">>, summary => <<"Reset a Rule Metrics">>,
parameters => param_path_id(), parameters => param_path_id(),
responses => #{ responses => #{
400 => error_schema('BAD_REQUEST', "RPC Call Failed"), 404 => error_schema('NOT_FOUND', "Rule not found"),
200 => <<"Reset Success">> 204 => <<"Reset Success">>
} }
} }
}; };
@ -363,15 +394,29 @@ param_path_id() ->
}), }),
{500, #{code => 'INTERNAL_ERROR', message => ?ERR_BADARGS(Reason)}} {500, #{code => 'INTERNAL_ERROR', message => ?ERR_BADARGS(Reason)}}
end. end.
'/rules/:id/reset_metrics'(put, #{bindings := #{id := RuleId}}) ->
case emqx_rule_engine_proto_v1:reset_metrics(RuleId) of '/rules/:id/metrics'(get, #{bindings := #{id := Id}}) ->
ok -> case emqx_rule_engine:get_rule(Id) of
{200, <<"Reset Success">>}; {ok, _Rule} ->
Failed -> NodeMetrics = get_rule_metrics(Id),
{400, #{ MetricsResp =
code => 'BAD_REQUEST', #{
message => err_msg(Failed) id => Id,
}} metrics => aggregate_metrics(NodeMetrics),
node_metrics => NodeMetrics
},
{200, MetricsResp};
not_found ->
{404, #{code => 'NOT_FOUND', message => <<"Rule Id Not Found">>}}
end.
'/rules/:id/metrics/reset'(put, #{bindings := #{id := Id}}) ->
case emqx_rule_engine:get_rule(Id) of
{ok, _Rule} ->
ok = emqx_rule_engine_proto_v1:reset_metrics(Id),
{204};
not_found ->
{404, #{code => 'NOT_FOUND', message => <<"Rule Id Not Found">>}}
end. end.
%%------------------------------------------------------------------------------ %%------------------------------------------------------------------------------
@ -394,15 +439,12 @@ format_rule_resp(#{
enable := Enable, enable := Enable,
description := Descr description := Descr
}) -> }) ->
NodeMetrics = get_rule_metrics(Id),
#{ #{
id => Id, id => Id,
name => Name, name => Name,
from => Topics, from => Topics,
actions => format_action(Action), actions => format_action(Action),
sql => SQL, sql => SQL,
metrics => aggregate_metrics(NodeMetrics),
node_metrics => NodeMetrics,
enable => Enable, enable => Enable,
created_at => format_datetime(CreatedAt, millisecond), created_at => format_datetime(CreatedAt, millisecond),
description => Descr description => Descr

View File

@ -25,7 +25,17 @@ init_per_testcase(_, Config) ->
Config. Config.
end_per_testcase(_, _Config) -> end_per_testcase(_, _Config) ->
ok. {200, #{data := Rules}} =
emqx_rule_engine_api:'/rules'(get, #{query_string => #{}}),
lists:foreach(
fun(#{id := Id}) ->
emqx_rule_engine_api:'/rules/:id'(
delete,
#{bindings => #{id => Id}}
)
end,
Rules
).
t_crud_rule_api(_Config) -> t_crud_rule_api(_Config) ->
RuleID = <<"my_rule">>, RuleID = <<"my_rule">>,
@ -49,15 +59,18 @@ t_crud_rule_api(_Config) ->
ct:pal("RList : ~p", [Rules]), ct:pal("RList : ~p", [Rules]),
?assert(length(Rules) > 0), ?assert(length(Rules) > 0),
{200, Rule0} = emqx_rule_engine_api:'/rules/:id/reset_metrics'(put, #{ {204} = emqx_rule_engine_api:'/rules/:id/metrics/reset'(put, #{
bindings => #{id => RuleID} bindings => #{id => RuleID}
}), }),
?assertEqual(<<"Reset Success">>, Rule0),
{200, Rule1} = emqx_rule_engine_api:'/rules/:id'(get, #{bindings => #{id => RuleID}}), {200, Rule1} = emqx_rule_engine_api:'/rules/:id'(get, #{bindings => #{id => RuleID}}),
ct:pal("RShow : ~p", [Rule1]), ct:pal("RShow : ~p", [Rule1]),
?assertEqual(Rule, Rule1), ?assertEqual(Rule, Rule1),
{200, Metrics} = emqx_rule_engine_api:'/rules/:id/metrics'(get, #{bindings => #{id => RuleID}}),
ct:pal("RMetrics : ~p", [Metrics]),
?assertMatch(#{id := RuleID, metrics := _, node_metrics := _}, Metrics),
{200, Rule2} = emqx_rule_engine_api:'/rules/:id'(put, #{ {200, Rule2} = emqx_rule_engine_api:'/rules/:id'(put, #{
bindings => #{id => RuleID}, bindings => #{id => RuleID},
body => Params0#{<<"sql">> => <<"select * from \"t/b\"">>} body => Params0#{<<"sql">> => <<"select * from \"t/b\"">>}
@ -68,6 +81,14 @@ t_crud_rule_api(_Config) ->
?assertEqual(Rule3, Rule2), ?assertEqual(Rule3, Rule2),
?assertEqual(<<"select * from \"t/b\"">>, maps:get(sql, Rule3)), ?assertEqual(<<"select * from \"t/b\"">>, maps:get(sql, Rule3)),
{404, _} = emqx_rule_engine_api:'/rules/:id'(get, #{bindings => #{id => <<"unknown_rule">>}}),
{404, _} = emqx_rule_engine_api:'/rules/:id/metrics'(get, #{
bindings => #{id => <<"unknown_rule">>}
}),
{404, _} = emqx_rule_engine_api:'/rules/:id/metrics/reset'(put, #{
bindings => #{id => <<"unknown_rule">>}
}),
?assertMatch( ?assertMatch(
{204}, {204},
emqx_rule_engine_api:'/rules/:id'( emqx_rule_engine_api:'/rules/:id'(
@ -150,20 +171,6 @@ t_list_rule_api(_Config) ->
QueryStr6 = #{query_string => #{<<"like_id">> => RuleID}}, QueryStr6 = #{query_string => #{<<"like_id">> => RuleID}},
{200, Result6} = emqx_rule_engine_api:'/rules'(get, QueryStr6), {200, Result6} = emqx_rule_engine_api:'/rules'(get, QueryStr6),
?assertEqual(maps:get(data, Result1), maps:get(data, Result6)), ?assertEqual(maps:get(data, Result1), maps:get(data, Result6)),
%% clean up
lists:foreach(
fun(Id) ->
?assertMatch(
{204},
emqx_rule_engine_api:'/rules/:id'(
delete,
#{bindings => #{id => Id}}
)
)
end,
AddIds
),
ok. ok.
test_rule_params() -> test_rule_params() ->

View File

@ -14,6 +14,8 @@
- Refactor authn API by replacing `POST /authentication/{id}/move` with `PUT /authentication/{id}/position/{position}`. [#9419](https://github.com/emqx/emqx/pull/9419). - Refactor authn API by replacing `POST /authentication/{id}/move` with `PUT /authentication/{id}/position/{position}`. [#9419](https://github.com/emqx/emqx/pull/9419).
Same is done for `/listeners/{listener_id}/authentication/id/...`. Same is done for `/listeners/{listener_id}/authentication/id/...`.
- Redesign `/rules` API to make `metrics` a dedicated resources rather than being included with every response [#9461](https://github.com/emqx/emqx/pull/9461).
## Bug fixes ## Bug fixes
- Fix that the obsolete SSL files aren't deleted after the ExHook config update [#9432](https://github.com/emqx/emqx/pull/9432). - Fix that the obsolete SSL files aren't deleted after the ExHook config update [#9432](https://github.com/emqx/emqx/pull/9432).

View File

@ -8,11 +8,14 @@
- 支持在 Apple Silicon 架构下编译苹果系统的发行版本 [#9423](https://github.com/emqx/emqx/pull/9423)。 - 支持在 Apple Silicon 架构下编译苹果系统的发行版本 [#9423](https://github.com/emqx/emqx/pull/9423)。
- 删除了老的共享订阅支持方式, 不再使用 `$queue` 前缀 [#9412](https://github.com/emqx/emqx/pull/9412)。 - 删除了老的共享订阅支持方式, 不再使用 `$queue` 前缀 [#9412](https://github.com/emqx/emqx/pull/9412)。
共享订阅自 MQTT v5.0 开始已成为协议标准,可以使用 `$share` 前缀代替 `$queue` 共享订阅自 MQTT v5.0 开始已成为协议标准,可以使用 `$share` 前缀代替 `$queue`
- 重构认证 API使用 `PUT /authentication/{id}/position/{position}` 代替了 `POST /authentication/{id}/move` [#9419](https://github.com/emqx/emqx/pull/9419)。 - 重构认证 API使用 `PUT /authentication/{id}/position/{position}` 代替了 `POST /authentication/{id}/move` [#9419](https://github.com/emqx/emqx/pull/9419)。
- 重新设计了 `/rules` API`metrics` 改为专用资源,而不再是包含在每个响应中 [#9461](https://github.com/emqx/emqx/pull/9461)。
## 修复 ## 修复
- 修复 ExHook 更新 SSL 相关配置后,过时的 SSL 文件没有被删除的问题 [#9432](https://github.com/emqx/emqx/pull/9432)。 - 修复 ExHook 更新 SSL 相关配置后,过时的 SSL 文件没有被删除的问题 [#9432](https://github.com/emqx/emqx/pull/9432)。