diff --git a/apps/emqx_rule_engine/i18n/emqx_rule_api_schema.conf b/apps/emqx_rule_engine/i18n/emqx_rule_api_schema.conf new file mode 100644 index 000000000..2c17d929c --- /dev/null +++ b/apps/emqx_rule_engine/i18n/emqx_rule_api_schema.conf @@ -0,0 +1,685 @@ +emqx_rule_api_schema { + + event_event_type { + desc { + en: "Event Type" + zh: "事件类型" + } + label: { + en: "Event Type" + zh: "事件类型" + } + } + + event_id { + desc { + en: "Message ID" + zh: "消息 ID" + } + label: { + en: "Message ID" + zh: "消息 ID" + } + } + + event_clientid { + desc { + en: "The Client ID" + zh: "客户端 ID" + } + label: { + en: "Client ID" + zh: "客户端 ID" + } + } + + event_username { + desc { + en: "The User Name" + zh: "" + } + label: { + en: "Username" + zh: "用户名" + } + } + + event_payload { + desc { + en: "The Message Payload" + zh: "消息负载" + } + label: { + en: "Message Payload" + zh: "消息负载" + } + } + + event_peerhost { + desc { + en: "The IP Address of the Peer Client" + zh: "对等客户端的 IP 地址" + } + label: { + en: "Peer IP Address" + zh: "对等客户端的 IP" + } + } + + event_topic { + desc { + en: "Message Topic" + zh: "消息主题" + } + label: { + en: "Message Topic" + zh: "消息主题" + } + } + + event_publish_received_at { + desc { + en: "The Time that this Message is Received" + zh: "消息被接受的时间" + } + label: { + en: "Message Received Time" + zh: "消息被接受的时间" + } + } + + event_qos { + desc { + en: "The Message QoS" + zh: "消息的 QoS" + } + label: { + en: "Message QoS" + zh: "消息 QoS" + } + } + + event_from_clientid { + desc { + en: "The Client ID" + zh: "事件来源客户端的 ID" + } + label: { + en: "Client ID" + zh: "客户端 ID" + } + } + + event_from_username { + desc { + en: "The User Name" + zh: "事件来源客户端的用户名" + } + label: { + en: "Username" + zh: "用户名" + } + } + + event_mountpoint { + desc { + en: "The Mountpoint" + zh: "挂载点" + } + label: { + en: "Mountpoint" + zh: "挂载点" + } + } + + event_peername { + desc { + en: "The IP Address and Port of the Peer Client" + zh: "对等客户端的 IP 地址和端口" + } + label: { + en: "IP Address And Port" + zh: "IP 地址和端口" + } + } + + event_sockname { + desc { + en: "The IP Address and Port of the Local Listener" + zh: "本地监听的 IP 地址和端口" + } + label: { + en: "IP Address And Port" + zh: "IP 地址和端口" + } + } + + event_proto_name { + desc { + en: "Protocol Name" + zh: "协议名称" + } + label: { + en: "Protocol Name" + zh: "协议名称" + } + } + + event_proto_ver { + desc { + en: "Protocol Version" + zh: "协议版本" + } + label: { + en: "Protocol Version" + zh: "协议版本" + } + } + + event_keepalive { + desc { + en: "KeepAlive" + zh: "保持连接" + } + label: { + en: "KeepAlive" + zh: "保持连接" + } + } + + event_clean_start { + desc { + en: "Clean Start" + zh: "清除会话" + } + label: { + en: "Clean Start" + zh: "清除会话" + } + } + + event_expiry_interval { + desc { + en: "Expiry Interval" + zh: "到期间隔" + } + label: { + en: "Expiry Interval" + zh: "到期间隔" + } + } + + event_is_bridge { + desc { + en: "Is Bridge" + zh: "是否桥接" + } + label: { + en: "Is Bridge" + zh: "是否桥接" + } + } + + event_connected_at { + desc { + en: "The Time that this Client is Connected" + zh: "客户端连接完成时的时刻" + } + label: { + en: "Connected Time" + zh: "连接完成时的时刻" + } + } + + event_action { + desc { + en: "Publish or Subscribe" + zh: "订阅或发布" + } + label: { + en: "Publish or Subscribe" + zh: "订阅或发布" + } + } + + event_authz_source { + desc { + en: "Cache, Plugs or Default" + zh: "缓存,插件或者默认值" + } + label: { + en: "Auth Source" + zh: "认证源" + } + } + + event_result { + desc { + en: "Allow or Deny" + zh: "允许或禁止" + } + label: { + en: "Auth Result" + zh: "认证结果" + } + } + + event_server { + desc { + en: "The IP address (or hostname) and port of the MQTT broker, in IP:Port format" + zh: "MQTT broker的 IP 地址(或主机名)和端口,采用 IP:Port 格式" + } + label: { + en: "Server IP And Port" + zh: "服务器 IP 地址和端口" + } + } + + event_dup { + desc { + en: "The DUP flag of the MQTT message" + zh: "MQTT 消息的 DUP 标志" + } + label: { + en: "DUP Flag" + zh: "DUP 标志" + } + } + + event_retain { + desc { + en: "If is a retain message" + zh: "是否是保留消息" + } + label: { + en: "Retain Message" + zh: "保留消息" + } + } + + event_ctx_dropped { + desc { + en: "The Reason for Dropping" + zh: "消息被丢弃的原因" + } + label: { + en: "Dropped Reason" + zh: "丢弃原因" + } + } + + event_ctx_disconnected_reason { + desc { + en: "The Reason for Disconnect" + zh: "断开连接的原因" + } + label: { + en: "Disconnect Reason" + zh: "断开连接原因" + } + } + + event_ctx_disconnected_da { + desc { + en: "The Time that this Client is Disconnected" + zh: "客户端断开连接的时刻" + } + label: { + en: "Disconnected Time" + zh: "客户端断开连接时刻" + } + } + + event_ctx_connack_reason_code { + desc { + en: "The reason code" + zh: "错误码" + } + label: { + en: "Reason Code" + zh: "错误码" + } + } + + rule_id { + desc { + en: "The ID of the rule" + zh: "规则的 ID " + } + label: { + en: "Rule ID" + zh: "规则 ID " + } + } + + node_node { + desc { + en: "The node name" + zh: "节点名字" + } + label: { + en: "Node Name" + zh: "节点名字" + } + } + + metrics_sql_matched { + desc { + en: "How much times the FROM clause of the SQL is matched." + zh: "SQL 的 FROM 子句匹配的次数。" + } + label: { + en: "Matched" + zh: "命中数" + } + } + + metrics_sql_matched_rate { + desc { + en: "The rate of matched, times/second" + zh: "命中速率,次/秒" + } + label: { + en: "命中速率" + zh: "Matched Rate" + } + } + + metrics_sql_matched_rate_max { + desc { + en: "The max rate of matched, times/second" + zh: "最大命中速率,次/秒" + } + label: { + en: "Max Matched Rate" + zh: "最大命中速率" + } + } + + metrics_sql_matched_rate_last5m { + desc { + en: "The average rate of matched in last 5 minutes, times/second" + zh: "5分钟平均命中速率,次/秒" + } + label: { + en: "Average Matched Rate" + zh: "平均命中速率" + } + } + + metrics_sql_passed { + desc { + en: "How much times the SQL is passed" + zh: "SQL 通过的次数" + } + label: { + en: "SQL Passed" + zh: "SQL 通过" + } + } + + metrics_sql_failed { + desc { + en: "How much times the SQL is failed" + zh: "SQL 失败的次数" + } + label: { + en: "SQL Failed" + zh: "SQL 失败" + } + } + + metrics_sql_failed_exception { + desc { + en: "How much times the SQL is failed due to exceptions. This may because of a crash when calling a SQL function, or trying to do arithmetic operation on undefined variables" + zh: "SQL 由于执行异常而失败的次数。 这可能是因为调用 SQL 函数时崩溃,或者试图对未定义的变量进行算术运算" + } + label: { + en: "SQL Exception" + zh: "SQL 执行异常" + } + } + + metrics_sql_failed_unknown { + desc { + en: "How much times the SQL is failed due to an unknown error." + zh: "由于未知错误导致 SQL 失败的次数。" + } + label: { + en: "SQL Unknown Error" + zh: "SQL 未知错误" + } + } + + metrics_outputs_total { + desc { + en: "How much times the outputs are called by the rule. This value may several times of 'sql.matched', depending on the number of the outputs of the rule." + zh: "规则调用输出的次数。 该值可能是“sql.matched”的几倍,具体取决于规则输出的数量。" + } + label: { + en: "Output Total" + zh: "调用输出次数" + } + } + + metrics_outputs_success { + desc { + en: "How much times the rule success to call the outputs." + zh: "规则成功调用输出的次数。" + } + label: { + en: "Success Output" + zh: "成功调用输出次数" + } + } + + metrics_outputs_failed { + desc { + en: "How much times the rule failed to call the outputs." + zh: "规则调用输出失败的次数。" + } + label: { + en: "Failed Output" + zh: "调用输出失败次数" + } + } + + metrics_outputs_failed_out_of_service { + desc { + en: "How much times the rule failed to call outputs due to the output is out of service. For example, a bridge is disabled or stopped." + zh: "由于输出停止服务而导致规则调用输出失败的次数。 例如,桥接被禁用或停止。" + } + label: { + en: "Fail Output" + zh: "调用输出失败次数" + } + } + + metrics_outputs_failed_unknown { + desc { + en: "How much times the rule failed to call outputs due to to an unknown error." + zh: "由于未知错误,规则调用输出失败的次数。" + } + label: { + en: "Fail Output" + zh: "调用输出失败次数" + } + } + + test_context { + desc { + en: "The context of the event for testing" + zh: "测试事件的上下文" + } + label: { + en: "Event Conetxt" + zh: "事件上下文" + } + } + + test_sql { + desc { + en: "The SQL of the rule for testing" + zh: "测试的 SQL" + } + label: { + en: "Test SQL" + zh: "测试 SQL" + } + } + + rs_event { + desc { + en: "The event topics" + zh: "事件主题" + } + label: { + en: "Event Topics" + zh: "事件主题" + } + } + + rs_title { + desc { + en: "The title" + zh: "标题" + } + label: { + en: "Title" + zh: "标题" + } + } + + rs_description { + desc { + en: "The description" + zh: "描述" + } + label: { + en: "Description" + zh: "描述" + } + } + + rs_columns { + desc { + en: "The columns" + zh: "列" + } + label: { + en: "Column" + zh: "列" + } + } + + rs_test_columns { + desc { + en: "The test columns" + zh: "测试列" + } + label: { + en: "Test Columns" + zh: "测试列" + } + } + + rs_sql_example { + desc { + en: "The sql_example" + zh: "SQL 例子" + } + label: { + en: "SQL Example" + zh: "SQL 例子" + } + } + + ri_metrics { + desc { + en: "The metrics of the rule" + zh: "规则的计数器" + } + label: { + en: "Rule Metrics" + zh: "规则计数器" + } + } + + ri_node_metrics { + desc { + en: "The metrics of the rule for each node" + zh: "每个节点的规则计数器" + } + label: { + en: "Each Node Rule Metrics" + zh: "每个节点规则计数器" + } + } + + ri_from { + desc { + en: "The topics of the rule" + zh: "规则指定的主题" + } + label: { + en: "Topics of Rule" + zh: "规则指定的主题" + } + } + + ri_created_at { + desc { + en: "The created time of the rule" + zh: "规则创建时间" + } + label: { + en: "Rule Create Time" + zh: "规则创建时间" + } + } + + root_rule_creation { + desc { + en: "Schema for creating rules" + zh: "用于创建规则的 Schema" + } + label: { + en: "Create Schema" + zh: "用于创建规则的 Schema" + } + } + + root_rule_info { + desc { + en: "Schema for rule info" + zh: "用于规则信息的 Schema" + } + label: { + en: "Info Schema" + zh: "用于规则信息的 Schema" + } + } + + root_rule_events { + desc { + en: "Schema for rule events" + zh: "用于事件的 Schema" + } + label: { + en: "Rule Events Schema" + zh: "用于规则事件的 Schema" + } + } + + root_rule_test { + desc { + en: "Schema for testing rules" + zh: "用于规则测试的 Schema" + } + label: { + en: "Rule Test Schema" + zh: "用于规则测试的 Schema" + } + } + +} diff --git a/apps/emqx_rule_engine/i18n/emqx_rule_engine_api.conf b/apps/emqx_rule_engine/i18n/emqx_rule_engine_api.conf new file mode 100644 index 000000000..c77c66d7c --- /dev/null +++ b/apps/emqx_rule_engine/i18n/emqx_rule_engine_api.conf @@ -0,0 +1,100 @@ +emqx_rule_engine_api { + + api1 { + desc { + en: "List all rules" + zh: "列出所有规则" + } + label: { + en: "List All Rules" + zh: "列出所有规则" + } + } + + api2 { + desc { + en: "Create a new rule using given Id" + zh: "通过指定 ID 创建规则" + } + label: { + en: "Create Rule By ID" + zh: "通过指定 ID 创建规则" + } + } + + api3 { + desc { + en: "List all events can be used in rules" + zh: "列出所有能被规则使用的事件" + } + label: { + en: "List All Events Can Be Used In Rule" + zh: "列出所有能被规则使用的事件" + } + } + + api4 { + desc { + en: "Get a rule by given Id" + zh: "通过 ID 查询规则" + } + label: { + en: "Get Rule" + zh: "查询规则" + } + } + + api5 { + desc { + en: "Update a rule by given Id to all nodes in the cluster" + zh: "通过 ID 更新集群里所有节点上的规则" + } + label: { + en: "Update Cluster Rule" + zh: "更新集群规则" + } + } + + api6 { + desc { + en: "Delete a rule by given Id from all nodes in the cluster" + zh: "通过 ID 删除集群里所有节点上的规则" + } + label: { + en: "Delete Cluster Rule" + zh: "删除集群规则" + } + } + + api7 { + desc { + en: "Reset a rule metrics" + zh: "重置规则计数" + } + label: { + en: "Reset Rule Metrics" + zh: "重置规则计数" + } + } + + api8 { + desc { + en: "Test a rule" + zh: "测试一个规则" + } + label: { + en: "Test Rule" + zh: "测试规则" + } + } + desc9 { + desc { + en: "List of rules" + zh: "列出所有规则" + } + label: { + en: "List Rules" + zh: "列出所有规则" + } + } +} diff --git a/apps/emqx_rule_engine/i18n/emqx_rule_engine_schema.conf b/apps/emqx_rule_engine/i18n/emqx_rule_engine_schema.conf new file mode 100644 index 000000000..a09912c71 --- /dev/null +++ b/apps/emqx_rule_engine/i18n/emqx_rule_engine_schema.conf @@ -0,0 +1,242 @@ +emqx_rule_engine_schema { + + rules_name { + desc { + en: "The name of the rule" + zh: "规则名字" + } + label: { + en: "Rule Name" + zh: "规则名字" + } + } + + rules_sql { + desc { + en: """ +SQL query to transform the messages.
+Example: SELECT * FROM "test/topic" WHERE payload.x = 1
+""" + zh: """ +用于处理消息的 SQL 。
+示例:SELECT * FROM "test/topic" WHERE payload.x = 1
+""" + } + label: { + en: "Rule SQL" + zh: "规则 SQL" + } + } + + rules_outputs { + desc { + en: """ +A list of outputs of the rule.
+An output can be a string that refers to the channel ID of an EMQX bridge, or an object +that refers to a function.
+There a some built-in functions like "republish" and "console", and we also support user +provided functions in the format: "{module}:{function}".
+The outputs in the list are executed sequentially. +This means that if one of the output is executing slowly, all the following outputs will not +be executed until it returns.
+If one of the output crashed, all other outputs come after it will still be executed, in the +original order.
+If there's any error when running an output, there will be an error message, and the 'failure' +counter of the function output or the bridge channel will increase. +""" + zh: """ +规则的动作列表。
+动作可以是指向 EMQX bridge 的引用,也可以是一个指向函数的对象。
+我们支持一些内置函数,如“republish”和“console”,我们还支持用户提供的函数,它的格式为:“{module}:{function}”。
+列表中的动作按顺序执行。这意味着如果其中一个动作执行缓慢,则以下所有动作都不会被执行直到它返回。
+如果其中一个动作崩溃,在它之后的所有动作仍然会被按照原始顺序执行。
+如果运行动作时出现任何错误,则会出现错误消息,并且相应的计数器会增加。 +""" + } + label: { + en: "Rule Action List" + zh: "动作列表" + } + } + + rules_enable { + desc { + en: "Enable or disable the rule" + zh: "启用或禁用规则引擎" + } + label: { + en: "Enable Or Disable Rule" + zh: "启用或禁用规则引擎" + } + } + + rules_description { + desc { + en: "The description of the rule" + zh: "规则的描述" + } + label: { + en: "Rule Description" + zh: "规则描述" + } + } + + republish_function { + desc { + en: """Republish the message as a new MQTT message""" + zh: """将消息重新发布为新的 MQTT 消息""" + } + label: { + en: "Republish Function" + zh: "重新发布函数" + } + } + + console_function { + desc { + en: """Print the outputs to the console""" + zh: "将输出打印到控制台" + } + label: { + en: "Console Function" + zh: "控制台函数" + } + } + + user_provided_function_function { + desc { + en: """ +The user provided function. Should be in the format: '{module}:{function}'.
+Where {module} is the Erlang callback module and {function} is the Erlang function. +
+To write your own function, checkout the function console and +republish in the source file: +apps/emqx_rule_engine/src/emqx_rule_outputs.erl as an example. +""" + zh: """ +用户提供的函数。 格式应为:'{module}:{function}'。
+其中 {module} 是 Erlang 回调模块, {function} 是 Erlang 函数。
+要编写自己的函数,请检查源文件:apps/emqx_rule_engine/src/emqx_rule_outputs.erl 中的示例函数 consolerepublish 。 +""" + } + label: { + en: "User Provided Function" + zh: "用户提供的函数" + } + } + + user_provided_function_args { + desc { + en: """ +The args will be passed as the 3rd argument to module:function/3, +checkout the function console and republish in the source file: +apps/emqx_rule_engine/src/emqx_rule_outputs.erl as an example. +""" + zh: """ +用户提供的参数将作为函数 module:function/3 的第三个参数, +请检查源文件:apps/emqx_rule_engine/src/emqx_rule_outputs.erl 中的示例函数 consolerepublish 。 +""" + } + label: { + en: "User Provided Function Args" + zh: "用户提供函数的参数" + } + } + + republish_args_topic { + desc { + en: """ +The target topic of message to be re-published.
+Template with variables is allowed, see description of the 'republish_args'. +""" + zh: """ +重新发布消息的目标主题。
+允许使用带有变量的模板,请参阅“republish_args”的描述。 +""" + } + label: { + en: "Target Topic" + zh: "目标主题" + } + } + + republish_args_qos { + desc { + en: """ +The qos of the message to be re-published. +Template with variables is allowed, see description of the 'republish_args'.
+Defaults to ${qos}. If variable ${qos} is not found from the selected result of the rule, +0 is used. +""" + zh: """ +要重新发布的消息的 qos。允许使用带有变量的模板,请参阅“republish_args”的描述。
+默认为 ${qos}。 如果从规则的选择结果中没有找到变量 ${qos},则使用 0。 +""" + } + label: { + en: "Message QoS" + zh: "消息 QoS 等级" + } + } + + republish_args_retain { + desc { + en: """ +The 'retain' flag of the message to be re-published. +Template with variables is allowed, see description of the 'republish_args'.
+Defaults to ${retain}. If variable ${retain} is not found from the selected result +of the rule, false is used. +""" + zh: """ +要重新发布的消息的“保留”标志。允许使用带有变量的模板,请参阅“republish_args”的描述。
+默认为 ${retain}。 如果从所选结果中未找到变量 ${retain},则使用 false。 +""" + } + label: { + en: "Retain Flag" + zh: "保留消息标志" + } + } + + republish_args_payload { + desc { + en: """ +The payload of the message to be re-published. +Template with variables is allowed, see description of the 'republish_args'.
. +Defaults to ${payload}. If variable ${payload} is not found from the selected result +of the rule, then the string "undefined" is used. +""" + zh: """ +要重新发布的消息的有效负载。允许使用带有变量的模板,请参阅“republish_args”的描述。
。 +默认为 ${payload}。 如果从所选结果中未找到变量 ${payload},则使用字符串 "undefined"。 +""" + } + label: { + en: "Message Payload" + zh: "消息负载" + } + } + + rule_engine_ignore_sys_message { + desc { + en: "When set to 'true' (default), rule-engine will ignore messages published to $SYS topics." + zh: "当设置为“true”(默认)时,规则引擎将忽略发布到 $SYS 主题的消息。" + } + label: { + en: "Ignore Sys Message" + zh: "忽略系统消息" + } + } + + rule_engine_rules { + desc { + en: """The rules""" + zh: "规则" + } + label: { + en: "Rules" + zh: "规则" + } + } + +} diff --git a/apps/emqx_rule_engine/src/emqx_rule_api_schema.erl b/apps/emqx_rule_engine/src/emqx_rule_api_schema.erl index 02a1610fb..9997043ff 100644 --- a/apps/emqx_rule_engine/src/emqx_rule_api_schema.erl +++ b/apps/emqx_rule_engine/src/emqx_rule_api_schema.erl @@ -3,6 +3,7 @@ -behaviour(hocon_schema). -include_lib("typerefl/include/types.hrl"). +-include_lib("hocon/include/hoconsc.hrl"). -include_lib("emqx/include/logger.hrl"). -export([ check_params/2 @@ -30,10 +31,10 @@ check_params(Params, Tag) -> %% 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"})} + [ {"rule_creation", sc(ref("rule_creation"), #{desc => ?DESC("root_rule_creation")})} + , {"rule_info", sc(ref("rule_info"), #{desc => ?DESC("root_rule_info")})} + , {"rule_events", sc(ref("rule_events"), #{desc => ?DESC("root_rule_events")})} + , {"rule_test", sc(ref("rule_test"), #{desc => ?DESC("root_rule_test")})} ]. fields("rule_creation") -> @@ -41,14 +42,14 @@ fields("rule_creation") -> fields("rule_info") -> [ rule_id() - , {"metrics", sc(ref("metrics"), #{desc => "The metrics of the rule"})} + , {"metrics", sc(ref("metrics"), #{desc => ?DESC("ri_metrics")})} , {"node_metrics", sc(hoconsc:array(ref("node_metrics")), - #{ desc => "The metrics of the rule for each node" + #{ desc => ?DESC("ri_node_metrics") })} , {"from", sc(hoconsc:array(binary()), - #{desc => "The topics of the rule", example => "t/#"})} + #{desc => ?DESC("ri_from"), example => "t/#"})} , {"created_at", sc(binary(), - #{ desc => "The created time of the rule" + #{ desc => ?DESC("ri_created_at") , example => "2021-12-01T15:00:43.153+08:00" })} ] ++ fields("rule_creation"); @@ -56,12 +57,12 @@ fields("rule_info") -> %% TODO: we can delete this API if the Dashboard not depends on it fields("rule_events") -> ETopics = [binary_to_atom(emqx_rule_events:event_topic(E)) || E <- emqx_rule_events:event_names()], - [ {"event", sc(hoconsc:enum(ETopics), #{desc => "The event topics", required => true})} - , {"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"})} + [ {"event", sc(hoconsc:enum(ETopics), #{desc => ?DESC("rs_event"), required => true})} + , {"title", sc(binary(), #{desc => ?DESC("rs_title"), example => "some title"})} + , {"description", sc(binary(), #{desc => ?DESC("rs_description"), example => "some desc"})} + , {"columns", sc(map(), #{desc => ?DESC("rs_columns")})} + , {"test_columns", sc(map(), #{desc => ?DESC("rs_test_columns")})} + , {"sql_example", sc(binary(), #{desc => ?DESC("rs_sql_example")})} ]; fields("rule_test") -> @@ -77,183 +78,177 @@ fields("rule_test") -> , ref("ctx_check_authz_complete") , ref("ctx_bridge_mqtt") ]), - #{desc => "The context of the event for testing", + #{desc => ?DESC("test_context"), default => #{}})} - , {"sql", sc(binary(), #{desc => "The SQL of the rule for testing", required => true})} + , {"sql", sc(binary(), #{desc => ?DESC("test_sql"), required => true})} ]; fields("metrics") -> [ {"sql.matched", sc(non_neg_integer(), #{ - desc => "How much times the FROM clause of the SQL is matched." + desc => ?DESC("metrics_sql_matched") })} - , {"sql.matched.rate", sc(float(), #{desc => "The rate of matched, times/second"})} - , {"sql.matched.rate.max", sc(float(), #{desc => "The max rate of matched, times/second"})} + , {"sql.matched.rate", sc(float(), #{desc => ?DESC("metrics_sql_matched_rate") })} + , {"sql.matched.rate.max", sc(float(), #{desc => ?DESC("metrics_sql_matched_rate_max") })} , {"sql.matched.rate.last5m", sc(float(), - #{desc => "The average rate of matched in last 5 minutes, times/second"})} - , {"sql.passed", sc(non_neg_integer(), #{desc => "How much times the SQL is passed"})} - , {"sql.failed", sc(non_neg_integer(), #{desc => "How much times the SQL is failed"})} + #{desc => ?DESC("metrics_sql_matched_rate_last5m") })} + , {"sql.passed", sc(non_neg_integer(), #{desc => ?DESC("metrics_sql_passed") })} + , {"sql.failed", sc(non_neg_integer(), #{desc => ?DESC("metrics_sql_failed") })} , {"sql.failed.exception", sc(non_neg_integer(), #{ - desc => "How much times the SQL is failed due to exceptions. " - "This may because of a crash when calling a SQL function, or " - "trying to do arithmetic operation on undefined variables" + desc => ?DESC("metrics_sql_failed_exception") })} , {"sql.failed.unknown", sc(non_neg_integer(), #{ - desc => "How much times the SQL is failed due to an unknown error." + desc => ?DESC("metrics_sql_failed_unknown") })} , {"outputs.total", sc(non_neg_integer(), #{ - desc => "How much times the outputs are called by the rule. " - "This value may several times of 'sql.matched', depending on the " - "number of the outputs of the rule." + desc => ?DESC("metrics_outputs_total") })} , {"outputs.success", sc(non_neg_integer(), #{ - desc => "How much times the rule success to call the outputs." + desc => ?DESC("metrics_outputs_success") })} , {"outputs.failed", sc(non_neg_integer(), #{ - desc => "How much times the rule failed to call the outputs." + desc => ?DESC("metrics_outputs_failed") })} , {"outputs.failed.out_of_service", sc(non_neg_integer(), #{ - desc => "How much times the rule failed to call outputs due to the output is " - "out of service. For example, a bridge is disabled or stopped." + desc => ?DESC("metrics_outputs_failed_out_of_service") })} , {"outputs.failed.unknown", sc(non_neg_integer(), #{ - desc => "How much times the rule failed to call outputs due to to an unknown error." + desc => ?DESC("metrics_outputs_failed_unknown") })} ]; fields("node_metrics") -> - [ {"node", sc(binary(), #{desc => "The node name", example => "emqx@127.0.0.1"})} + [ {"node", sc(binary(), #{desc => ?DESC("node_node"), example => "emqx@127.0.0.1"})} ] ++ fields("metrics"); fields("ctx_pub") -> - [ {"event_type", sc(message_publish, #{desc => "Event Type", required => true})} - , {"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"})} + [ {"event_type", sc(message_publish, #{desc => ?DESC("event_event_type"), required => true})} + , {"id", sc(binary(), #{desc => ?DESC("event_id")})} + , {"clientid", sc(binary(), #{desc => ?DESC("event_clientid")})} + , {"username", sc(binary(), #{desc => ?DESC("event_username")})} + , {"payload", sc(binary(), #{desc => ?DESC("event_payload")})} + , {"peerhost", sc(binary(), #{desc => ?DESC("event_peerhost")})} + , {"topic", sc(binary(), #{desc => ?DESC("event_topic")})} , {"publish_received_at", sc(integer(), #{ - desc => "The Time that this Message is Received"})} + desc => ?DESC("event_publish_received_at")})} ] ++ [qos()]; fields("ctx_sub") -> - [ {"event_type", sc(session_subscribed, #{desc => "Event Type", required => true})} - , {"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"})} + [ {"event_type", sc(session_subscribed, #{desc => ?DESC("event_event_type"), required => true})} + , {"clientid", sc(binary(), #{desc => ?DESC("event_clientid")})} + , {"username", sc(binary(), #{desc => ?DESC("event_username")})} + , {"payload", sc(binary(), #{desc => ?DESC("event_payload")})} + , {"peerhost", sc(binary(), #{desc => ?DESC("event_peerhost")})} + , {"topic", sc(binary(), #{desc => ?DESC("event_topic")})} , {"publish_received_at", sc(integer(), #{ - desc => "The Time that this Message is Received"})} + desc => ?DESC("event_publish_received_at")})} ] ++ [qos()]; fields("ctx_unsub") -> - [{"event_type", sc(session_unsubscribed, #{desc => "Event Type", required => true})}] ++ + [{"event_type", sc(session_unsubscribed, #{desc => ?DESC("event_event_type"), required => true})}] ++ proplists:delete("event_type", fields("ctx_sub")); fields("ctx_delivered") -> - [ {"event_type", sc(message_delivered, #{desc => "Event Type", required => true})} - , {"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"})} + [ {"event_type", sc(message_delivered, #{desc => ?DESC("event_event_type"), required => true})} + , {"id", sc(binary(), #{desc => ?DESC("event_id")})} + , {"from_clientid", sc(binary(), #{desc => ?DESC("event_from_clientid")})} + , {"from_username", sc(binary(), #{desc => ?DESC("event_from_username")})} + , {"clientid", sc(binary(), #{desc => ?DESC("event_clientid")})} + , {"username", sc(binary(), #{desc => ?DESC("event_username")})} + , {"payload", sc(binary(), #{desc => ?DESC("event_payload")})} + , {"peerhost", sc(binary(), #{desc => ?DESC("event_peerhost")})} + , {"topic", sc(binary(), #{desc => ?DESC("event_topic")})} , {"publish_received_at", sc(integer(), #{ - desc => "The Time that this Message is Received"})} + desc => ?DESC("event_publish_received_at")})} ] ++ [qos()]; fields("ctx_acked") -> - [{"event_type", sc(message_acked, #{desc => "Event Type", required => true})}] ++ + [{"event_type", sc(message_acked, #{desc => ?DESC("event_event_type"), required => true})}] ++ proplists:delete("event_type", fields("ctx_delivered")); fields("ctx_dropped") -> - [ {"event_type", sc(message_dropped, #{desc => "Event Type", required => true})} - , {"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"})} + [ {"event_type", sc(message_dropped, #{desc => ?DESC("event_event_type"), required => true})} + , {"id", sc(binary(), #{desc => ?DESC("event_id")})} + , {"reason", sc(binary(), #{desc => ?DESC("event_ctx_dropped")})} + , {"clientid", sc(binary(), #{desc => ?DESC("event_clientid")})} + , {"username", sc(binary(), #{desc => ?DESC("event_username")})} + , {"payload", sc(binary(), #{desc => ?DESC("event_payload")})} + , {"peerhost", sc(binary(), #{desc => ?DESC("event_peerhost")})} + , {"topic", sc(binary(), #{desc => ?DESC("event_topic")})} , {"publish_received_at", sc(integer(), #{ - desc => "The Time that this Message is Received"})} + desc => ?DESC("event_publish_received_at")})} ] ++ [qos()]; fields("ctx_connected") -> - [ {"event_type", sc(client_connected, #{desc => "Event Type", required => true})} - , {"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})} + [ {"event_type", sc(client_connected, #{desc => ?DESC("event_event_type"), required => true})} + , {"clientid", sc(binary(), #{desc => ?DESC("event_clientid")})} + , {"username", sc(binary(), #{desc => ?DESC("event_username")})} + , {"mountpoint", sc(binary(), #{desc => ?DESC("event_mountpoint")})} + , {"peername", sc(binary(), #{desc => ?DESC("event_peername")})} + , {"sockname", sc(binary(), #{desc => ?DESC("event_sockname")})} + , {"proto_name", sc(binary(), #{desc => ?DESC("event_proto_name")})} + , {"proto_ver", sc(binary(), #{desc => ?DESC("event_proto_ver")})} + , {"keepalive", sc(integer(), #{desc => ?DESC("event_keepalive")})} + , {"clean_start", sc(boolean(), #{desc => ?DESC("event_clean_start"), default => true})} + , {"expiry_interval", sc(integer(), #{desc => ?DESC("event_expiry_interval")})} + , {"is_bridge", sc(boolean(), #{desc => ?DESC("event_is_bridge"), default => false})} , {"connected_at", sc(integer(), #{ - desc => "The Time that this Client is Connected"})} + desc => ?DESC("event_connected_at")})} ]; fields("ctx_disconnected") -> - [ {"event_type", sc(client_disconnected, #{desc => "Event Type", required => true})} - , {"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"})} + [ {"event_type", sc(client_disconnected, #{desc => ?DESC("event_event_type"), required => true})} + , {"clientid", sc(binary(), #{desc => ?DESC("event_clientid")})} + , {"username", sc(binary(), #{desc => ?DESC("event_username")})} + , {"reason", sc(binary(), #{desc => ?DESC("event_ctx_disconnected_reason")})} + , {"peername", sc(binary(), #{desc => ?DESC("event_peername")})} + , {"sockname", sc(binary(), #{desc => ?DESC("event_sockname")})} , {"disconnected_at", sc(integer(), #{ - desc => "The Time that this Client is Disconnected"})} + desc => ?DESC("event_ctx_disconnected_da")})} ]; fields("ctx_connack") -> - [ {"event_type", sc(client_connack, #{desc => "Event Type", required => true})} - , {"reason_code", sc(binary(), #{desc => "The reason code"})} - , {"clientid", sc(binary(), #{desc => "The Client ID"})} - , {"clean_start", sc(boolean(), #{desc => "Clean Start", default => true})} - , {"username", sc(binary(), #{desc => "The User Name"})} - , {"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"})} - , {"expiry_interval", sc(integer(), #{desc => "Expiry Interval"})} + [ {"event_type", sc(client_connack, #{desc => ?DESC("event_event_type"), required => true})} + , {"reason_code", sc(binary(), #{desc => ?DESC("event_ctx_connack_reason_code")})} + , {"clientid", sc(binary(), #{desc => ?DESC("event_clientid")})} + , {"clean_start", sc(boolean(), #{desc => ?DESC("event_clean_start"), default => true})} + , {"username", sc(binary(), #{desc => ?DESC("event_username")})} + , {"peername", sc(binary(), #{desc => ?DESC("event_peername")})} + , {"sockname", sc(binary(), #{desc => ?DESC("event_sockname")})} + , {"proto_name", sc(binary(), #{desc => ?DESC("event_proto_name")})} + , {"proto_ver", sc(binary(), #{desc => ?DESC("event_proto_ver")})} + , {"keepalive", sc(integer(), #{desc => ?DESC("event_keepalive")})} + , {"expiry_interval", sc(integer(), #{desc => ?DESC("event_expiry_interval")})} , {"connected_at", sc(integer(), #{ - desc => "The Time that this Client is Connected"})} + desc => ?DESC("event_connected_at")})} ]; fields("ctx_check_authz_complete") -> - [ {"event_type", sc(client_check_authz_complete, #{desc => "Event Type", required => true})} - , {"clientid", sc(binary(), #{desc => "The Client ID"})} - , {"username", sc(binary(), #{desc => "The User Name"})} - , {"peerhost", sc(binary(), #{desc => "The IP Address of the Peer Client"})} - , {"topic", sc(binary(), #{desc => "Message Topic"})} - , {"action", sc(binary(), #{desc => "Publish or Subscribe"})} - , {"authz_source", sc(binary(), #{desc => "Cache, Plugs or Default"})} - , {"result", sc(binary(), #{desc => "Allow or Deny"})} + [ {"event_type", sc(client_check_authz_complete, #{desc => ?DESC("event_event_type"), required => true})} + , {"clientid", sc(binary(), #{desc => ?DESC("event_clientid")})} + , {"username", sc(binary(), #{desc => ?DESC("event_username")})} + , {"peerhost", sc(binary(), #{desc => ?DESC("event_peerhost")})} + , {"topic", sc(binary(), #{desc => ?DESC("event_topic")})} + , {"action", sc(binary(), #{desc => ?DESC("event_action")})} + , {"authz_source", sc(binary(), #{desc => ?DESC("event_authz_source")})} + , {"result", sc(binary(), #{desc => ?DESC("event_result")})} ]; fields("ctx_bridge_mqtt") -> - [ {"event_type", sc('$bridges/mqtt:*', #{desc => "Event Type", required => true})} - , {"id", sc(binary(), #{desc => "Message ID"})} - , {"payload", sc(binary(), #{desc => "The Message Payload"})} - , {"topic", sc(binary(), #{desc => "Message Topic"})} - , {"server", sc(binary(), #{desc => "The IP address (or hostname) and port of the MQTT broker," - " in IP:Port format"})} - , {"dup", sc(binary(), #{desc => "The DUP flag of the MQTT message"})} - , {"retain", sc(binary(), #{desc => "If is a retain message"})} + [ {"event_type", sc('$bridges/mqtt:*', #{desc => ?DESC("event_event_type"), required => true})} + , {"id", sc(binary(), #{desc => ?DESC("event_id")})} + , {"payload", sc(binary(), #{desc => ?DESC("event_payload")})} + , {"topic", sc(binary(), #{desc => ?DESC("event_topic")})} + , {"server", sc(binary(), #{desc => ?DESC("event_server")})} + , {"dup", sc(binary(), #{desc => ?DESC("event_dup")})} + , {"retain", sc(binary(), #{desc => ?DESC("event_retain")})} , {"message_received_at", sc(integer(), #{ - desc => "The Time that this Message is Received"})} + desc => ?DESC("event_publish_received_at")})} ] ++ [qos()]. qos() -> - {"qos", sc(emqx_schema:qos(), #{desc => "The Message QoS"})}. + {"qos", sc(emqx_schema:qos(), #{desc => ?DESC("event_qos")})}. rule_id() -> {"id", sc(binary(), - #{ desc => "The ID of the rule", required => true + #{ desc => ?DESC("rule_id"), required => true , example => "293fb66f" })}. diff --git a/apps/emqx_rule_engine/src/emqx_rule_engine_api.erl b/apps/emqx_rule_engine/src/emqx_rule_engine_api.erl index ff2415d5c..7d2e34f5b 100644 --- a/apps/emqx_rule_engine/src/emqx_rule_engine_api.erl +++ b/apps/emqx_rule_engine/src/emqx_rule_engine_api.erl @@ -18,6 +18,7 @@ -include("rule_engine.hrl"). -include_lib("emqx/include/logger.hrl"). +-include_lib("hocon/include/hoconsc.hrl"). -include_lib("typerefl/include/types.hrl"). -behaviour(minirest_api). @@ -102,14 +103,14 @@ schema("/rules") -> 'operationId' => '/rules', get => #{ tags => [<<"rules">>], - description => <<"List all rules">>, + description => ?DESC("api1"), summary => <<"List Rules">>, responses => #{ - 200 => mk(array(rule_info_schema()), #{desc => "List of rules"}) + 200 => mk(array(rule_info_schema()), #{desc => ?DESC("desc9")}) }}, post => #{ tags => [<<"rules">>], - description => <<"Create a new rule using given Id">>, + description => ?DESC("api2"), summary => <<"Create a Rule">>, 'requestBody' => rule_creation_schema(), responses => #{ @@ -123,7 +124,7 @@ schema("/rule_events") -> 'operationId' => '/rule_events', get => #{ tags => [<<"rules">>], - description => <<"List all events can be used in rules">>, + description => ?DESC("api3"), summary => <<"List Events">>, responses => #{ 200 => mk(ref(emqx_rule_api_schema, "rule_events"), #{}) @@ -136,7 +137,7 @@ schema("/rules/:id") -> 'operationId' => '/rules/:id', get => #{ tags => [<<"rules">>], - description => <<"Get a rule by given Id">>, + description => ?DESC("api4"), summary => <<"Get a Rule">>, parameters => param_path_id(), responses => #{ @@ -146,7 +147,7 @@ schema("/rules/:id") -> }, put => #{ tags => [<<"rules">>], - description => <<"Update a rule by given Id to all nodes in the cluster">>, + description => ?DESC("api5"), summary => <<"Update a Rule">>, parameters => param_path_id(), 'requestBody' => rule_creation_schema(), @@ -157,7 +158,7 @@ schema("/rules/:id") -> }, delete => #{ tags => [<<"rules">>], - description => <<"Delete a rule by given Id from all nodes in the cluster">>, + description => ?DESC("api6"), summary => <<"Delete a Rule">>, parameters => param_path_id(), responses => #{ @@ -171,7 +172,7 @@ schema("/rules/:id/reset_metrics") -> 'operationId' => '/rules/:id/reset_metrics', put => #{ tags => [<<"rules">>], - description => <<"Reset a rule metrics">>, + description => ?DESC("api7"), summary => <<"Reset a Rule Metrics">>, parameters => param_path_id(), responses => #{ @@ -186,7 +187,7 @@ schema("/rule_test") -> 'operationId' => '/rule_test', post => #{ tags => [<<"rules">>], - description => <<"Test a rule">>, + description => ?DESC("api8"), summary => <<"Test a Rule">>, 'requestBody' => rule_test_schema(), responses => #{ diff --git a/apps/emqx_rule_engine/src/emqx_rule_engine_schema.erl b/apps/emqx_rule_engine/src/emqx_rule_engine_schema.erl index c39269ff3..d0f557d50 100644 --- a/apps/emqx_rule_engine/src/emqx_rule_engine_schema.erl +++ b/apps/emqx_rule_engine/src/emqx_rule_engine_schema.erl @@ -17,6 +17,7 @@ -module(emqx_rule_engine_schema). -include_lib("typerefl/include/types.hrl"). +-include_lib("hocon/include/hoconsc.hrl"). -behaviour(hocon_schema). @@ -34,38 +35,21 @@ namespace() -> rule_engine. roots() -> ["rule_engine"]. fields("rule_engine") -> - [ {ignore_sys_message, sc(boolean(), #{default => true, desc => -"When set to 'true' (default), rule-engine will ignore messages published to $SYS topics." + [ {ignore_sys_message, sc(boolean(), #{default => true, desc => ?DESC("rule_engine_ignore_sys_message") })} - , {rules, sc(hoconsc:map("id", ref("rules")), #{desc => "The rules", default => #{}})} + , {rules, sc(hoconsc:map("id", ref("rules")), #{desc => ?DESC("rule_engine_rules"), default => #{}})} ]; fields("rules") -> [ rule_name() , {"sql", sc(binary(), - #{ desc => " -SQL query to transform the messages.
-Example: SELECT * FROM \"test/topic\" WHERE payload.x = 1
-" + #{ desc => ?DESC("rules_sql") , example => "SELECT * FROM \"test/topic\" WHERE payload.x = 1" , required => true , validator => fun ?MODULE:validate_sql/1 })} , {"outputs", sc(hoconsc:array(hoconsc:union(outputs())), - #{ desc => " -A list of outputs of the rule.
-An output can be a string that refers to the channel ID of an EMQX bridge, or an object -that refers to a function.
-There a some built-in functions like \"republish\" and \"console\", and we also support user -provided functions in the format: \"{module}:{function}\".
-The outputs in the list are executed sequentially. -This means that if one of the output is executing slowly, all the following outputs will not -be executed until it returns.
-If one of the output crashed, all other outputs come after it will still be executed, in the -original order.
-If there's any error when running an output, there will be an error message, and the 'failure' -counter of the function output or the bridge channel will increase. -" + #{ desc => ?DESC("rules_outputs") , default => [] , example => [ <<"http:my_http_bridge">>, @@ -74,21 +58,21 @@ counter of the function output or the bridge channel will increase. #{function => console} ] })} - , {"enable", sc(boolean(), #{desc => "Enable or disable the rule", default => true})} + , {"enable", sc(boolean(), #{desc => ?DESC("rules_enable"), default => true})} , {"description", sc(binary(), - #{ desc => "The description of the rule" + #{ desc => ?DESC("rules_description") , example => "Some description" , default => <<>> })} ]; fields("builtin_output_republish") -> - [ {function, sc(republish, #{desc => "Republish the message as a new MQTT message"})} + [ {function, sc(republish, #{desc => ?DESC("republish_function")})} , {args, sc(ref("republish_args"), #{default => #{}})} ]; fields("builtin_output_console") -> - [ {function, sc(console, #{desc => "Print the outputs to the console"})} + [ {function, sc(console, #{desc => ?DESC("console_function")})} %% we may support some args for the console output in the future %, {args, sc(map(), #{desc => "The arguments of the built-in 'console' output", % default => #{}})} @@ -96,62 +80,33 @@ fields("builtin_output_console") -> fields("user_provided_function") -> [ {function, sc(binary(), - #{ desc => " -The user provided function. Should be in the format: '{module}:{function}'.
-Where {module} is the Erlang callback module and {function} is the Erlang function. -
-To write your own function, checkout the function console and -republish in the source file: -apps/emqx_rule_engine/src/emqx_rule_outputs.erl as an example. -" + #{ desc => ?DESC("user_provided_function_function") , example => "module:function" })} , {args, sc(map(), - #{ desc => " -The args will be passed as the 3rd argument to module:function/3, -checkout the function console and republish in the source file: -apps/emqx_rule_engine/src/emqx_rule_outputs.erl as an example. -" + #{ desc => ?DESC("user_provided_function_args") , default => #{} })} ]; fields("republish_args") -> [ {topic, sc(binary(), - #{ desc =>" -The target topic of message to be re-published.
-Template with variables is allowed, see description of the 'republish_args'. -" + #{ desc => ?DESC("republish_args_topic") , required => true , example => <<"a/1">> })} , {qos, sc(qos(), - #{ desc => " -The qos of the message to be re-published. -Template with variables is allowed, see description of the 'republish_args'.
-Defaults to ${qos}. If variable ${qos} is not found from the selected result of the rule, -0 is used. -" + #{ desc => ?DESC("republish_args_qos") , default => <<"${qos}">> , example => <<"${qos}">> })} , {retain, sc(hoconsc:union([binary(), boolean()]), - #{ desc => " -The 'retain' flag of the message to be re-published. -Template with variables is allowed, see description of the 'republish_args'.
-Defaults to ${retain}. If variable ${retain} is not found from the selected result -of the rule, false is used. -" + #{ desc => ?DESC("republish_args_retain") , default => <<"${retain}">> , example => <<"${retain}">> })} , {payload, sc(binary(), - #{ desc => " -The payload of the message to be re-published. -Template with variables is allowed, see description of the 'republish_args'.
. -Defaults to ${payload}. If variable ${payload} is not found from the selected result -of the rule, then the string \"undefined\" is used. -" + #{ desc => ?DESC("republish_args_payload") , default => <<"${payload}">> , example => <<"${payload}">> })} @@ -191,7 +146,7 @@ desc(_) -> rule_name() -> {"name", sc(binary(), - #{ desc => "The name of the rule" + #{ desc => ?DESC("rules_name") , default => "" , required => true , example => "foo"