emqx/apps/emqx_rule_engine/src/emqx_rule_api_schema.erl

187 lines
8.4 KiB
Erlang

-module(emqx_rule_api_schema).
-behaviour(hocon_schema).
-include_lib("typerefl/include/types.hrl").
-include_lib("emqx/include/logger.hrl").
-export([ check_params/2
]).
-export([roots/0, fields/1]).
-type tag() :: rule_creation | rule_test.
-spec check_params(map(), tag()) -> {ok, map()} | {error, term()}.
check_params(Params, Tag) ->
BTag = atom_to_binary(Tag),
try hocon_schema:check_plain(?MODULE, #{BTag => Params},
#{atom_key => true, nullable => true}, [BTag]) of
#{Tag := Checked} -> {ok, Checked}
catch
Error:Reason:ST ->
?SLOG(error, #{msg => "check_rule_params_failed",
exception => Error,
reason => Reason,
stacktrace => ST}),
{error, {Reason, ST}}
end.
%%======================================================================================
%% Hocon Schema Definitions
roots() ->
[ {"rule_creation", sc(ref("rule_creation"), #{desc => "Schema for creating rules"})}
, {"rule_info", sc(ref("rule_info"), #{desc => "Schema for rule info"})}
, {"rule_events", sc(ref("rule_events"), #{desc => "Schema for rule events"})}
, {"rule_test", sc(ref("rule_test"), #{desc => "Schema for testing rules"})}
].
fields("rule_creation") ->
[ {"id", sc(binary(),
#{ desc => "The Id of the rule", nullable => false
, example => "my_rule_id"
})}
] ++ emqx_rule_engine_schema:fields("rules");
fields("rule_info") ->
[ {"metrics", sc(ref("metrics"), #{desc => "The metrics of the rule"})}
, {"node_metrics", sc(ref("node_metrics"), #{desc => "The metrics of the rule"})}
, {"from", sc(hoconsc:array(binary()),
#{desc => "The topics of the rule", example => "t/#"})}
, {"created_at", sc(binary(),
#{ desc => "The created time of the rule"
, example => "2021-12-01T15:00:43.153+08:00"
})}
] ++ fields("rule_creation");
%% TODO: we can delete this API if the Dashboard not denpends on it
fields("rule_events") ->
ETopics = [emqx_rule_events:event_topic(E) || E <- emqx_rule_events:event_names()],
[ {"event", sc(hoconsc:enum(ETopics), #{desc => "The event topics", nullable => false})}
, {"title", sc(binary(), #{desc => "The title", example => "some title"})}
, {"description", sc(binary(), #{desc => "The description", example => "some desc"})}
, {"columns", sc(map(), #{desc => "The columns"})}
, {"test_columns", sc(map(), #{desc => "The test columns"})}
, {"sql_example", sc(binary(), #{desc => "The sql_example"})}
];
fields("rule_test") ->
[ {"context", sc(hoconsc:union([ ref("ctx_pub")
, ref("ctx_sub")
, ref("ctx_delivered")
, ref("ctx_acked")
, ref("ctx_dropped")
, ref("ctx_connected")
, ref("ctx_disconnected")
]),
#{desc => "The context of the event for testing",
default => #{}})}
, {"sql", sc(binary(), #{desc => "The SQL of the rule for testing", nullable => false})}
];
fields("metrics") ->
[ {"matched", sc(integer(), #{desc => "How much times this rule is matched"})}
, {"rate", sc(float(), #{desc => "The rate of matched, times/second"})}
, {"rate_max", sc(float(), #{desc => "The max rate of matched, times/second"})}
, {"rate_last5m", sc(float(),
#{desc => "The average rate of matched in last 5 mins, times/second"})}
];
fields("node_metrics") ->
[ {"node", sc(binary(), #{desc => "The node name", example => "emqx@127.0.0.1"})}
] ++ fields("metrics");
fields("ctx_pub") ->
[ {"event_type", sc(message_publish, #{desc => "Event Type", nullable => false})}
, {"id", sc(binary(), #{desc => "Message ID"})}
, {"clientid", sc(binary(), #{desc => "The Client ID"})}
, {"username", sc(binary(), #{desc => "The User Name"})}
, {"payload", sc(binary(), #{desc => "The Message Payload"})}
, {"peerhost", sc(binary(), #{desc => "The IP Address of the Peer Client"})}
, {"topic", sc(binary(), #{desc => "Message Topic"})}
, {"publish_received_at", sc(integer(), #{
desc => "The Time that this Message is Received"})}
] ++ [qos()];
fields("ctx_sub") ->
[ {"event_type", sc(session_subscribed, #{desc => "Event Type", nullable => false})}
, {"clientid", sc(binary(), #{desc => "The Client ID"})}
, {"username", sc(binary(), #{desc => "The User Name"})}
, {"payload", sc(binary(), #{desc => "The Message Payload"})}
, {"peerhost", sc(binary(), #{desc => "The IP Address of the Peer Client"})}
, {"topic", sc(binary(), #{desc => "Message Topic"})}
, {"publish_received_at", sc(integer(), #{
desc => "The Time that this Message is Received"})}
] ++ [qos()];
fields("ctx_unsub") ->
[{"event_type", sc(session_unsubscribed, #{desc => "Event Type", nullable => false})}] ++
proplists:delete("event_type", fields("ctx_sub"));
fields("ctx_delivered") ->
[ {"event_type", sc(message_delivered, #{desc => "Event Type", nullable => false})}
, {"id", sc(binary(), #{desc => "Message ID"})}
, {"from_clientid", sc(binary(), #{desc => "The Client ID"})}
, {"from_username", sc(binary(), #{desc => "The User Name"})}
, {"clientid", sc(binary(), #{desc => "The Client ID"})}
, {"username", sc(binary(), #{desc => "The User Name"})}
, {"payload", sc(binary(), #{desc => "The Message Payload"})}
, {"peerhost", sc(binary(), #{desc => "The IP Address of the Peer Client"})}
, {"topic", sc(binary(), #{desc => "Message Topic"})}
, {"publish_received_at", sc(integer(), #{
desc => "The Time that this Message is Received"})}
] ++ [qos()];
fields("ctx_acked") ->
[{"event_type", sc(message_acked, #{desc => "Event Type", nullable => false})}] ++
proplists:delete("event_type", fields("ctx_delivered"));
fields("ctx_dropped") ->
[ {"event_type", sc(message_dropped, #{desc => "Event Type", nullable => false})}
, {"id", sc(binary(), #{desc => "Message ID"})}
, {"reason", sc(binary(), #{desc => "The Reason for Dropping"})}
, {"clientid", sc(binary(), #{desc => "The Client ID"})}
, {"username", sc(binary(), #{desc => "The User Name"})}
, {"payload", sc(binary(), #{desc => "The Message Payload"})}
, {"peerhost", sc(binary(), #{desc => "The IP Address of the Peer Client"})}
, {"topic", sc(binary(), #{desc => "Message Topic"})}
, {"publish_received_at", sc(integer(), #{
desc => "The Time that this Message is Received"})}
] ++ [qos()];
fields("ctx_connected") ->
[ {"event_type", sc(client_connected, #{desc => "Event Type", nullable => false})}
, {"clientid", sc(binary(), #{desc => "The Client ID"})}
, {"username", sc(binary(), #{desc => "The User Name"})}
, {"mountpoint", sc(binary(), #{desc => "The Mountpoint"})}
, {"peername", sc(binary(), #{desc => "The IP Address and Port of the Peer Client"})}
, {"sockname", sc(binary(), #{desc => "The IP Address and Port of the Local Listener"})}
, {"proto_name", sc(binary(), #{desc => "Protocol Name"})}
, {"proto_ver", sc(binary(), #{desc => "Protocol Version"})}
, {"keepalive", sc(integer(), #{desc => "KeepAlive"})}
, {"clean_start", sc(boolean(), #{desc => "Clean Start", default => true})}
, {"expiry_interval", sc(integer(), #{desc => "Expiry Interval"})}
, {"is_bridge", sc(boolean(), #{desc => "Is Bridge", default => false})}
, {"connected_at", sc(integer(), #{
desc => "The Time that this Client is Connected"})}
];
fields("ctx_disconnected") ->
[ {"event_type", sc(client_disconnected, #{desc => "Event Type", nullable => false})}
, {"clientid", sc(binary(), #{desc => "The Client ID"})}
, {"username", sc(binary(), #{desc => "The User Name"})}
, {"reason", sc(binary(), #{desc => "The Reason for Disconnect"})}
, {"peername", sc(binary(), #{desc => "The IP Address and Port of the Peer Client"})}
, {"sockname", sc(binary(), #{desc => "The IP Address and Port of the Local Listener"})}
, {"disconnected_at", sc(integer(), #{
desc => "The Time that this Client is Disconnected"})}
].
qos() ->
{"qos", sc(hoconsc:union([typerefl:integer(0), typerefl:integer(1), typerefl:integer(2)]),
#{desc => "The Message QoS"})}.
sc(Type, Meta) -> hoconsc:mk(Type, Meta).
ref(Field) -> hoconsc:ref(?MODULE, Field).