diff --git a/apps/emqx_connector/i18n/emqx_connector_mongo.conf b/apps/emqx_connector/i18n/emqx_connector_mongo.conf
index 88fb67ded..71e970574 100644
--- a/apps/emqx_connector/i18n/emqx_connector_mongo.conf
+++ b/apps/emqx_connector/i18n/emqx_connector_mongo.conf
@@ -72,9 +72,9 @@ A host entry has the following form: `Host[:Port]`.
The MongoDB default port 27017 is used if `[:Port]` is not specified.
"""
zh: """
-集群要连接的节点列表。 节点之间用逗号分隔,如:`Node[,Node].
`
-对于每个节点,应为:将要连接的 IPv4 或 IPv6 地址或主机名。
-主机条目具有以下形式:`Host[:Port]`。
+集群将要连接的节点列表。 节点之间用逗号分隔,如:`Node[,Node].
`
+每个节点的配置为:将要连接的 IPv4 或 IPv6 地址或主机名。
+主机名具有以下形式:`Host[:Port]`。
如果未指定 `[:Port]`,则使用 MongoDB 默认端口 27017。
"""
}
diff --git a/apps/emqx_connector/i18n/emqx_connector_redis.conf b/apps/emqx_connector/i18n/emqx_connector_redis.conf
index 59c1df54f..baba431e2 100644
--- a/apps/emqx_connector/i18n/emqx_connector_redis.conf
+++ b/apps/emqx_connector/i18n/emqx_connector_redis.conf
@@ -72,9 +72,10 @@ A host entry has the following form: `Host[:Port]`.
The MongoDB default port 27017 is used if `[:Port]` is not specified.
"""
zh: """
-集群要连接的节点列表。 节点之间用逗号分隔,如:`Node[,Node].
`
-对于每个节点,应为:将要连接的 IPv4 或 IPv6 地址或主机名。
-主机条目具有以下形式:`Host[:Port]`。
+
+集群将要连接的节点列表。 节点之间用逗号分隔,如:`Node[,Node].
`
+每个节点的配置为:将要连接的 IPv4 或 IPv6 地址或主机名。
+主机名具有以下形式:`Host[:Port]`。
如果未指定 `[:Port]`,则使用 Redis 默认端口 6379。
"""
}
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
中的示例函数 console
和republish
。
+"""
+ }
+ 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
中的示例函数 console
和republish
。
+"""
+ }
+ 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"