Merge pull request #7461 from ieQu1/doc-schema-6
docs(schema): Document fields and records
This commit is contained in:
commit
13e4feef13
|
@ -894,7 +894,7 @@ conn_congestion {
|
||||||
## Whether to alarm the congested connections.
|
## Whether to alarm the congested connections.
|
||||||
##
|
##
|
||||||
## Sometimes the mqtt connection (usually an MQTT subscriber) may
|
## Sometimes the mqtt connection (usually an MQTT subscriber) may
|
||||||
## get "congested", because there're too many packets to be sent.
|
## get "congested", because there's too many packets to sent.
|
||||||
## The socket tries to buffer the packets until the buffer is
|
## The socket tries to buffer the packets until the buffer is
|
||||||
## full. If more packets come after that, the packets will be
|
## full. If more packets come after that, the packets will be
|
||||||
## "pending" in a queue and we consider the connection is
|
## "pending" in a queue and we consider the connection is
|
||||||
|
@ -914,7 +914,7 @@ conn_congestion {
|
||||||
enable_alarm = true
|
enable_alarm = true
|
||||||
|
|
||||||
## Won't clear the congested alarm in how long time.
|
## Won't clear the congested alarm in how long time.
|
||||||
## The alarm is cleared only when there're no pending bytes in
|
## The alarm is cleared only when there's no pending bytes in
|
||||||
## the queue, and also it has been `min_alarm_sustain_duration`
|
## the queue, and also it has been `min_alarm_sustain_duration`
|
||||||
## time since the last time we considered the connection is "congested".
|
## time since the last time we considered the connection is "congested".
|
||||||
##
|
##
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
|
|
||||||
-export([ roots/0, fields/1, to_rate/1, to_capacity/1
|
-export([ roots/0, fields/1, to_rate/1, to_capacity/1
|
||||||
, minimum_period/0, to_burst_rate/1, to_initial/1
|
, minimum_period/0, to_burst_rate/1, to_initial/1
|
||||||
, namespace/0, get_bucket_cfg_path/2
|
, namespace/0, get_bucket_cfg_path/2, desc/1
|
||||||
]).
|
]).
|
||||||
|
|
||||||
-define(KILOBYTE, 1024).
|
-define(KILOBYTE, 1024).
|
||||||
|
@ -149,6 +149,17 @@ the check/consume will succeed, but it will be forced to wait for a short period
|
||||||
, default => force})}
|
, default => force})}
|
||||||
].
|
].
|
||||||
|
|
||||||
|
desc(limiter) ->
|
||||||
|
"Settings for the rate limiter.";
|
||||||
|
desc(limiter_opts) ->
|
||||||
|
"Settings for the limiter.";
|
||||||
|
desc(bucket_opts) ->
|
||||||
|
"Settings for the bucket.";
|
||||||
|
desc(client_bucket) ->
|
||||||
|
"Settings for the client bucket.";
|
||||||
|
desc(_) ->
|
||||||
|
undefined.
|
||||||
|
|
||||||
%% minimum period is 100ms
|
%% minimum period is 100ms
|
||||||
minimum_period() ->
|
minimum_period() ->
|
||||||
100.
|
100.
|
||||||
|
|
|
@ -304,7 +304,7 @@ fields("stats") ->
|
||||||
boolean(),
|
boolean(),
|
||||||
#{
|
#{
|
||||||
default => true,
|
default => true,
|
||||||
desc => "Enable/disable statistic data collection"
|
desc => "Enable/disable statistic data collection."
|
||||||
}
|
}
|
||||||
)}
|
)}
|
||||||
];
|
];
|
||||||
|
@ -339,17 +339,26 @@ fields("cache") ->
|
||||||
{"enable",
|
{"enable",
|
||||||
sc(
|
sc(
|
||||||
boolean(),
|
boolean(),
|
||||||
#{default => true}
|
#{
|
||||||
|
default => true,
|
||||||
|
desc => "Enable or disable the authorization cache."
|
||||||
|
}
|
||||||
)},
|
)},
|
||||||
{"max_size",
|
{"max_size",
|
||||||
sc(
|
sc(
|
||||||
range(1, 1048576),
|
range(1, 1048576),
|
||||||
#{default => 32}
|
#{
|
||||||
|
default => 32,
|
||||||
|
desc => "Maximum number of cached items."
|
||||||
|
}
|
||||||
)},
|
)},
|
||||||
{"ttl",
|
{"ttl",
|
||||||
sc(
|
sc(
|
||||||
duration(),
|
duration(),
|
||||||
#{default => "1m"}
|
#{
|
||||||
|
default => "1m",
|
||||||
|
desc => "Time to live for the cached data."
|
||||||
|
}
|
||||||
)}
|
)}
|
||||||
];
|
];
|
||||||
fields("mqtt") ->
|
fields("mqtt") ->
|
||||||
|
@ -762,12 +771,23 @@ fields("conn_congestion") ->
|
||||||
{"enable_alarm",
|
{"enable_alarm",
|
||||||
sc(
|
sc(
|
||||||
boolean(),
|
boolean(),
|
||||||
#{default => false}
|
#{
|
||||||
|
default => false,
|
||||||
|
desc => "Enable or disable connection congestion alarm."
|
||||||
|
}
|
||||||
)},
|
)},
|
||||||
{"min_alarm_sustain_duration",
|
{"min_alarm_sustain_duration",
|
||||||
sc(
|
sc(
|
||||||
duration(),
|
duration(),
|
||||||
#{default => "1m"}
|
#{
|
||||||
|
default => "1m",
|
||||||
|
desc =>
|
||||||
|
"Minimal time before clearing the alarm.\n\n"
|
||||||
|
"The alarm is cleared only when there's no pending data in\n"
|
||||||
|
"the queue, and at least `min_alarm_sustain_duration`\n"
|
||||||
|
"milliseconds passed since the last time we considered the connection \"congested\".\n\n"
|
||||||
|
"This is to avoid clearing and raising the alarm again too often."
|
||||||
|
}
|
||||||
)}
|
)}
|
||||||
];
|
];
|
||||||
fields("force_gc") ->
|
fields("force_gc") ->
|
||||||
|
@ -1357,7 +1377,7 @@ fields("sysmon_vm") ->
|
||||||
desc =>
|
desc =>
|
||||||
"The threshold, as percentage of processes, for how many\n"
|
"The threshold, as percentage of processes, for how many\n"
|
||||||
" processes can simultaneously exist at the local node before the corresponding\n"
|
" processes can simultaneously exist at the local node before the corresponding\n"
|
||||||
" alarm is set."
|
" alarm is raised."
|
||||||
}
|
}
|
||||||
)},
|
)},
|
||||||
{"process_low_watermark",
|
{"process_low_watermark",
|
||||||
|
@ -1431,7 +1451,7 @@ fields("sysmon_os") ->
|
||||||
default => "80%",
|
default => "80%",
|
||||||
desc =>
|
desc =>
|
||||||
"The threshold, as percentage of system CPU load,\n"
|
"The threshold, as percentage of system CPU load,\n"
|
||||||
" for how much system cpu can be used before the corresponding alarm is set."
|
" for how much system cpu can be used before the corresponding alarm is raised."
|
||||||
}
|
}
|
||||||
)},
|
)},
|
||||||
{"cpu_low_watermark",
|
{"cpu_low_watermark",
|
||||||
|
@ -1459,7 +1479,7 @@ fields("sysmon_os") ->
|
||||||
default => "70%",
|
default => "70%",
|
||||||
desc =>
|
desc =>
|
||||||
"The threshold, as percentage of system memory,\n"
|
"The threshold, as percentage of system memory,\n"
|
||||||
" for how much system memory can be allocated before the corresponding alarm is set."
|
" for how much system memory can be allocated before the corresponding alarm is raised."
|
||||||
}
|
}
|
||||||
)},
|
)},
|
||||||
{"procmem_high_watermark",
|
{"procmem_high_watermark",
|
||||||
|
@ -1470,7 +1490,7 @@ fields("sysmon_os") ->
|
||||||
desc =>
|
desc =>
|
||||||
"The threshold, as percentage of system memory,\n"
|
"The threshold, as percentage of system memory,\n"
|
||||||
" for how much system memory can be allocated by one Erlang process before\n"
|
" for how much system memory can be allocated by one Erlang process before\n"
|
||||||
" the corresponding alarm is set."
|
" the corresponding alarm is raised."
|
||||||
}
|
}
|
||||||
)}
|
)}
|
||||||
];
|
];
|
||||||
|
@ -1704,7 +1724,13 @@ base_listener() ->
|
||||||
}
|
}
|
||||||
)},
|
)},
|
||||||
{"limiter",
|
{"limiter",
|
||||||
sc(map("ratelimit's type", emqx_limiter_schema:bucket_name()), #{default => #{}})}
|
sc(
|
||||||
|
map("ratelimit's type", emqx_limiter_schema:bucket_name()),
|
||||||
|
#{
|
||||||
|
default => #{},
|
||||||
|
desc => "Type of the rate limit."
|
||||||
|
}
|
||||||
|
)}
|
||||||
].
|
].
|
||||||
|
|
||||||
desc("persistent_session_store") ->
|
desc("persistent_session_store") ->
|
||||||
|
|
|
@ -73,14 +73,17 @@ fields(other_algorithms) ->
|
||||||
{salt_position, fun salt_position/1}].
|
{salt_position, fun salt_position/1}].
|
||||||
|
|
||||||
salt_position(type) -> {enum, [prefix, suffix]};
|
salt_position(type) -> {enum, [prefix, suffix]};
|
||||||
|
salt_position(desc) -> "Specifies whether the password salt is stored as a prefix or the suffix.";
|
||||||
salt_position(default) -> prefix;
|
salt_position(default) -> prefix;
|
||||||
salt_position(_) -> undefined.
|
salt_position(_) -> undefined.
|
||||||
|
|
||||||
salt_rounds(type) -> integer();
|
salt_rounds(type) -> integer();
|
||||||
|
salt_rounds(desc) -> "Cost factor for the bcrypt hash.";
|
||||||
salt_rounds(default) -> 10;
|
salt_rounds(default) -> 10;
|
||||||
salt_rounds(_) -> undefined.
|
salt_rounds(_) -> undefined.
|
||||||
|
|
||||||
dk_length(type) -> integer();
|
dk_length(type) -> integer();
|
||||||
|
dk_length(desc) -> "Length of the derived key.";
|
||||||
dk_length(required) -> false;
|
dk_length(required) -> false;
|
||||||
dk_length(_) -> undefined.
|
dk_length(_) -> undefined.
|
||||||
|
|
||||||
|
|
|
@ -55,11 +55,15 @@ root_type() ->
|
||||||
|
|
||||||
mechanism(Name) ->
|
mechanism(Name) ->
|
||||||
hoconsc:mk(hoconsc:enum([Name]),
|
hoconsc:mk(hoconsc:enum([Name]),
|
||||||
#{required => true}).
|
#{ required => true
|
||||||
|
, desc => "Authentication mechanism."
|
||||||
|
}).
|
||||||
|
|
||||||
backend(Name) ->
|
backend(Name) ->
|
||||||
hoconsc:mk(hoconsc:enum([Name]),
|
hoconsc:mk(hoconsc:enum([Name]),
|
||||||
#{required => true}).
|
#{ required => true
|
||||||
|
, desc => "Backend type."
|
||||||
|
}).
|
||||||
|
|
||||||
fields("metrics_status_fields") ->
|
fields("metrics_status_fields") ->
|
||||||
[ {"metrics", mk(ref(?MODULE, "metrics"), #{desc => "The metrics of the resource"})}
|
[ {"metrics", mk(ref(?MODULE, "metrics"), #{desc => "The metrics of the resource"})}
|
||||||
|
@ -89,7 +93,7 @@ fields("node_metrics") ->
|
||||||
|
|
||||||
fields("node_status") ->
|
fields("node_status") ->
|
||||||
[ node_name()
|
[ node_name()
|
||||||
, {"status", mk(status(), #{})}
|
, {"status", mk(status(), #{desc => "Status of the node."})}
|
||||||
].
|
].
|
||||||
|
|
||||||
status() ->
|
status() ->
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
-export([ namespace/0
|
-export([ namespace/0
|
||||||
, roots/0
|
, roots/0
|
||||||
, fields/1
|
, fields/1
|
||||||
|
, desc/1
|
||||||
]).
|
]).
|
||||||
|
|
||||||
-export([ refs/0
|
-export([ refs/0
|
||||||
|
@ -55,6 +56,15 @@ fields('replica-set') ->
|
||||||
fields('sharded-cluster') ->
|
fields('sharded-cluster') ->
|
||||||
common_fields() ++ emqx_connector_mongo:fields(sharded).
|
common_fields() ++ emqx_connector_mongo:fields(sharded).
|
||||||
|
|
||||||
|
desc(standalone) ->
|
||||||
|
"Configuration for a standalone MongoDB instance.";
|
||||||
|
desc('replica-set') ->
|
||||||
|
"Configuration for a replica set.";
|
||||||
|
desc('sharded-cluster') ->
|
||||||
|
"Configuration for a sharded cluster.";
|
||||||
|
desc(_) ->
|
||||||
|
undefined.
|
||||||
|
|
||||||
common_fields() ->
|
common_fields() ->
|
||||||
[ {mechanism, emqx_authn_schema:mechanism('password_based')}
|
[ {mechanism, emqx_authn_schema:mechanism('password_based')}
|
||||||
, {backend, emqx_authn_schema:backend(mongodb)}
|
, {backend, emqx_authn_schema:backend(mongodb)}
|
||||||
|
@ -67,19 +77,27 @@ common_fields() ->
|
||||||
] ++ emqx_authn_schema:common_fields().
|
] ++ emqx_authn_schema:common_fields().
|
||||||
|
|
||||||
collection(type) -> binary();
|
collection(type) -> binary();
|
||||||
|
collection(desc) -> "Collection used to store authentication data.";
|
||||||
collection(_) -> undefined.
|
collection(_) -> undefined.
|
||||||
|
|
||||||
selector(type) -> map();
|
selector(type) -> map();
|
||||||
|
selector(desc) -> "Statement that is executed during the authentication process. "
|
||||||
|
"Commands can support following wildcards:\n"
|
||||||
|
" - `${username}`: substituted with client's username\n"
|
||||||
|
" - `${clientid}`: substituted with the clientid";
|
||||||
selector(_) -> undefined.
|
selector(_) -> undefined.
|
||||||
|
|
||||||
password_hash_field(type) -> binary();
|
password_hash_field(type) -> binary();
|
||||||
|
password_hash_field(desc) -> "Document field that contains password hash.";
|
||||||
password_hash_field(_) -> undefined.
|
password_hash_field(_) -> undefined.
|
||||||
|
|
||||||
salt_field(type) -> binary();
|
salt_field(type) -> binary();
|
||||||
|
salt_field(desc) -> "Document field that contains the password salt.";
|
||||||
salt_field(required) -> false;
|
salt_field(required) -> false;
|
||||||
salt_field(_) -> undefined.
|
salt_field(_) -> undefined.
|
||||||
|
|
||||||
is_superuser_field(type) -> binary();
|
is_superuser_field(type) -> binary();
|
||||||
|
is_superuser_field(desc) -> "Document field that defines if the user has superuser privileges.";
|
||||||
is_superuser_field(required) -> false;
|
is_superuser_field(required) -> false;
|
||||||
is_superuser_field(_) -> undefined.
|
is_superuser_field(_) -> undefined.
|
||||||
|
|
||||||
|
|
|
@ -66,7 +66,7 @@ fields("authorization") ->
|
||||||
]),
|
]),
|
||||||
default => [],
|
default => [],
|
||||||
desc =>
|
desc =>
|
||||||
"""
|
"
|
||||||
Authorization data sources.<br>
|
Authorization data sources.<br>
|
||||||
An array of authorization (ACL) data providers.
|
An array of authorization (ACL) data providers.
|
||||||
It is designed as an array, not a hash-map, so the sources can be
|
It is designed as an array, not a hash-map, so the sources can be
|
||||||
|
@ -84,7 +84,7 @@ the default action configured in 'authorization.no_match' is applied.<br>
|
||||||
NOTE:
|
NOTE:
|
||||||
The source elements are identified by their 'type'.
|
The source elements are identified by their 'type'.
|
||||||
It is NOT allowed to configure two or more sources of the same type.
|
It is NOT allowed to configure two or more sources of the same type.
|
||||||
"""
|
"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
@ -94,7 +94,7 @@ fields(file) ->
|
||||||
default => true}}
|
default => true}}
|
||||||
, {path, #{type => string(),
|
, {path, #{type => string(),
|
||||||
required => true,
|
required => true,
|
||||||
desc => """
|
desc => "
|
||||||
Path to the file which contains the ACL rules.<br>
|
Path to the file which contains the ACL rules.<br>
|
||||||
If the file provisioned before starting EMQX node,
|
If the file provisioned before starting EMQX node,
|
||||||
it can be placed anywhere as long as EMQX has read access to it.
|
it can be placed anywhere as long as EMQX has read access to it.
|
||||||
|
@ -102,7 +102,7 @@ it can be placed anywhere as long as EMQX has read access to it.
|
||||||
In case the rule-set is created from EMQX dashboard or management API,
|
In case the rule-set is created from EMQX dashboard or management API,
|
||||||
the file will be placed in `authz` subdirectory inside EMQX's `data_dir`,
|
the file will be placed in `authz` subdirectory inside EMQX's `data_dir`,
|
||||||
and the new rules will override all rules from the old config file.
|
and the new rules will override all rules from the old config file.
|
||||||
"""
|
"
|
||||||
}}
|
}}
|
||||||
];
|
];
|
||||||
fields(http_get) ->
|
fields(http_get) ->
|
||||||
|
@ -145,18 +145,19 @@ fields(redis_cluster) ->
|
||||||
|
|
||||||
http_common_fields() ->
|
http_common_fields() ->
|
||||||
[ {url, fun url/1}
|
[ {url, fun url/1}
|
||||||
, {request_timeout, mk_duration("Request timeout", #{default => "30s"})}
|
, {request_timeout, mk_duration("Request timeout", #{default => "30s", desc => "Request timeout."})}
|
||||||
, {body, #{type => map(), required => false}}
|
, {body, #{type => map(), required => false, desc => "HTTP request body."}}
|
||||||
] ++ maps:to_list(maps:without([ base_url
|
] ++ maps:to_list(maps:without([ base_url
|
||||||
, pool_type],
|
, pool_type],
|
||||||
maps:from_list(connector_fields(http)))).
|
maps:from_list(connector_fields(http)))).
|
||||||
|
|
||||||
mongo_common_fields() ->
|
mongo_common_fields() ->
|
||||||
[ {collection, #{type => atom()}}
|
[ {collection, #{type => atom(), desc => "`MongoDB` collection containing the authorization data."}}
|
||||||
, {selector, #{type => map()}}
|
, {selector, #{type => map(), desc => "MQL query used to select the authorization record."}}
|
||||||
, {type, #{type => mongodb}}
|
, {type, #{type => mongodb, desc => "Database backend."}}
|
||||||
, {enable, #{type => boolean(),
|
, {enable, #{type => boolean(),
|
||||||
default => true}}
|
default => true,
|
||||||
|
desc => "Enable or disable the backend."}}
|
||||||
].
|
].
|
||||||
|
|
||||||
validations() ->
|
validations() ->
|
||||||
|
@ -165,6 +166,7 @@ validations() ->
|
||||||
].
|
].
|
||||||
|
|
||||||
headers(type) -> list({binary(), binary()});
|
headers(type) -> list({binary(), binary()});
|
||||||
|
headers(desc) -> "List of HTTP headers.";
|
||||||
headers(converter) ->
|
headers(converter) ->
|
||||||
fun(Headers) ->
|
fun(Headers) ->
|
||||||
maps:to_list(maps:merge(default_headers(), transform_header_name(Headers)))
|
maps:to_list(maps:merge(default_headers(), transform_header_name(Headers)))
|
||||||
|
@ -173,6 +175,7 @@ headers(default) -> default_headers();
|
||||||
headers(_) -> undefined.
|
headers(_) -> undefined.
|
||||||
|
|
||||||
headers_no_content_type(type) -> list({binary(), binary()});
|
headers_no_content_type(type) -> list({binary(), binary()});
|
||||||
|
headers_no_content_type(desc) -> "List of HTTP headers.";
|
||||||
headers_no_content_type(converter) ->
|
headers_no_content_type(converter) ->
|
||||||
fun(Headers) ->
|
fun(Headers) ->
|
||||||
maps:to_list(maps:merge(default_headers_no_content_type(), transform_header_name(Headers)))
|
maps:to_list(maps:merge(default_headers_no_content_type(), transform_header_name(Headers)))
|
||||||
|
@ -181,6 +184,7 @@ headers_no_content_type(default) -> default_headers_no_content_type();
|
||||||
headers_no_content_type(_) -> undefined.
|
headers_no_content_type(_) -> undefined.
|
||||||
|
|
||||||
url(type) -> binary();
|
url(type) -> binary();
|
||||||
|
url(desc) -> "URL of the auth server.";
|
||||||
url(validator) -> [?NOT_EMPTY("the value of the field 'url' cannot be empty")];
|
url(validator) -> [?NOT_EMPTY("the value of the field 'url' cannot be empty")];
|
||||||
url(required) -> true;
|
url(required) -> true;
|
||||||
url(_) -> undefined.
|
url(_) -> undefined.
|
||||||
|
@ -244,6 +248,7 @@ union_array(Item) when is_list(Item) ->
|
||||||
|
|
||||||
query() ->
|
query() ->
|
||||||
#{type => binary(),
|
#{type => binary(),
|
||||||
|
desc => "",
|
||||||
validator => fun(S) ->
|
validator => fun(S) ->
|
||||||
case size(S) > 0 of
|
case size(S) > 0 of
|
||||||
true -> ok;
|
true -> ok;
|
||||||
|
@ -264,9 +269,10 @@ connector_fields(DB, Fields) ->
|
||||||
error:Reason ->
|
error:Reason ->
|
||||||
erlang:error(Reason)
|
erlang:error(Reason)
|
||||||
end,
|
end,
|
||||||
[ {type, #{type => DB}}
|
[ {type, #{type => DB, desc => "Database backend."}}
|
||||||
, {enable, #{type => boolean(),
|
, {enable, #{type => boolean(),
|
||||||
default => true}}
|
default => true,
|
||||||
|
desc => "Enable or disable the backend."}}
|
||||||
] ++ erlang:apply(Mod, fields, [Fields]).
|
] ++ erlang:apply(Mod, fields, [Fields]).
|
||||||
|
|
||||||
to_list(A) when is_atom(A) ->
|
to_list(A) when is_atom(A) ->
|
||||||
|
|
|
@ -421,7 +421,7 @@ fields("db") ->
|
||||||
, 'readOnly' => true
|
, 'readOnly' => true
|
||||||
, desc => """
|
, desc => """
|
||||||
Select the backend for the embedded database.<br/>
|
Select the backend for the embedded database.<br/>
|
||||||
<code>rlog</code> is the default backend, a new experimental backend
|
<code>rlog</code> is the default backend,
|
||||||
that is suitable for very large clusters.<br/>
|
that is suitable for very large clusters.<br/>
|
||||||
<code>mnesia</code> is a backend that offers decent performance in small clusters.
|
<code>mnesia</code> is a backend that offers decent performance in small clusters.
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -24,12 +24,13 @@
|
||||||
-define(REDIS_DEFAULT_PORT, 6379).
|
-define(REDIS_DEFAULT_PORT, 6379).
|
||||||
-define(PGSQL_DEFAULT_PORT, 5432).
|
-define(PGSQL_DEFAULT_PORT, 5432).
|
||||||
|
|
||||||
-define(SERVERS_DESC, "A Node list for Cluster to connect to. The nodes should be split with ',', such as: 'Node[,Node]'<br>\nFor each Node should be:<br>").
|
-define(SERVERS_DESC, "A Node list for Cluster to connect to. The nodes should be separated with commas, such as: `Node[,Node].<br/>`
|
||||||
|
For each Node should be: ").
|
||||||
|
|
||||||
-define(SERVER_DESC(TYPE, DEFAULT_PORT), """
|
-define(SERVER_DESC(TYPE, DEFAULT_PORT), "
|
||||||
The IPv4 or IPv6 address or host name to connect to.<br>
|
The IPv4 or IPv6 address or the hostname to connect to.<br/>
|
||||||
A host entry has the following form: 'Host[:Port]'<br>
|
A host entry has the following form: `Host[:Port]`.<br/>
|
||||||
The """ ++ TYPE ++ " default port " ++ DEFAULT_PORT ++ " is used if '[:Port]' isn't present"
|
The " ++ TYPE ++ " default port " ++ DEFAULT_PORT ++ " is used if `[:Port]` is not specified."
|
||||||
).
|
).
|
||||||
|
|
||||||
-define(THROW_ERROR(Str), erlang:throw({error, Str})).
|
-define(THROW_ERROR(Str), erlang:throw({error, Str})).
|
||||||
|
|
|
@ -54,13 +54,15 @@ roots() ->
|
||||||
|
|
||||||
fields(single) ->
|
fields(single) ->
|
||||||
[ {mongo_type, #{type => single,
|
[ {mongo_type, #{type => single,
|
||||||
default => single}}
|
default => single,
|
||||||
|
desc => "Standalone instance."}}
|
||||||
, {server, fun server/1}
|
, {server, fun server/1}
|
||||||
, {w_mode, fun w_mode/1}
|
, {w_mode, fun w_mode/1}
|
||||||
] ++ mongo_fields();
|
] ++ mongo_fields();
|
||||||
fields(rs) ->
|
fields(rs) ->
|
||||||
[ {mongo_type, #{type => rs,
|
[ {mongo_type, #{type => rs,
|
||||||
default => rs}}
|
default => rs,
|
||||||
|
desc => "Replica set."}}
|
||||||
, {servers, fun servers/1}
|
, {servers, fun servers/1}
|
||||||
, {w_mode, fun w_mode/1}
|
, {w_mode, fun w_mode/1}
|
||||||
, {r_mode, fun r_mode/1}
|
, {r_mode, fun r_mode/1}
|
||||||
|
@ -68,7 +70,8 @@ fields(rs) ->
|
||||||
] ++ mongo_fields();
|
] ++ mongo_fields();
|
||||||
fields(sharded) ->
|
fields(sharded) ->
|
||||||
[ {mongo_type, #{type => sharded,
|
[ {mongo_type, #{type => sharded,
|
||||||
default => sharded}}
|
default => sharded,
|
||||||
|
desc => "Sharded cluster."}}
|
||||||
, {servers, fun servers/1}
|
, {servers, fun servers/1}
|
||||||
, {w_mode, fun w_mode/1}
|
, {w_mode, fun w_mode/1}
|
||||||
] ++ mongo_fields();
|
] ++ mongo_fields();
|
||||||
|
@ -306,22 +309,27 @@ servers(desc) -> ?SERVERS_DESC ++ server(desc);
|
||||||
servers(_) -> undefined.
|
servers(_) -> undefined.
|
||||||
|
|
||||||
w_mode(type) -> hoconsc:enum([unsafe, safe]);
|
w_mode(type) -> hoconsc:enum([unsafe, safe]);
|
||||||
|
w_mode(desc) -> "Write mode.";
|
||||||
w_mode(default) -> unsafe;
|
w_mode(default) -> unsafe;
|
||||||
w_mode(_) -> undefined.
|
w_mode(_) -> undefined.
|
||||||
|
|
||||||
r_mode(type) -> hoconsc:enum([master, slave_ok]);
|
r_mode(type) -> hoconsc:enum([master, slave_ok]);
|
||||||
|
r_mode(desc) -> "Read mode.";
|
||||||
r_mode(default) -> master;
|
r_mode(default) -> master;
|
||||||
r_mode(_) -> undefined.
|
r_mode(_) -> undefined.
|
||||||
|
|
||||||
duration(type) -> emqx_schema:duration_ms();
|
duration(type) -> emqx_schema:duration_ms();
|
||||||
|
duration(desc) -> "Time interval, such as timeout or TTL.";
|
||||||
duration(required) -> false;
|
duration(required) -> false;
|
||||||
duration(_) -> undefined.
|
duration(_) -> undefined.
|
||||||
|
|
||||||
replica_set_name(type) -> binary();
|
replica_set_name(type) -> binary();
|
||||||
|
replica_set_name(desc) -> "Name of the replica set.";
|
||||||
replica_set_name(required) -> false;
|
replica_set_name(required) -> false;
|
||||||
replica_set_name(_) -> undefined.
|
replica_set_name(_) -> undefined.
|
||||||
|
|
||||||
srv_record(type) -> boolean();
|
srv_record(type) -> boolean();
|
||||||
|
srv_record(desc) -> "Use DNS SRV record.";
|
||||||
srv_record(default) -> false;
|
srv_record(default) -> false;
|
||||||
srv_record(_) -> undefined.
|
srv_record(_) -> undefined.
|
||||||
|
|
||||||
|
|
|
@ -52,7 +52,8 @@ fields(_) -> [].
|
||||||
|
|
||||||
ssl_fields() ->
|
ssl_fields() ->
|
||||||
[ {ssl, #{type => hoconsc:ref(emqx_schema, ssl_client_opts),
|
[ {ssl, #{type => hoconsc:ref(emqx_schema, ssl_client_opts),
|
||||||
default => #{<<"enable">> => false}
|
default => #{<<"enable">> => false},
|
||||||
|
desc => "SSL connection settings."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
].
|
].
|
||||||
|
@ -66,24 +67,29 @@ relational_db_fields() ->
|
||||||
].
|
].
|
||||||
|
|
||||||
database(type) -> binary();
|
database(type) -> binary();
|
||||||
|
database(desc) -> "Database name.";
|
||||||
database(required) -> true;
|
database(required) -> true;
|
||||||
database(validator) -> [?NOT_EMPTY("the value of the field 'database' cannot be empty")];
|
database(validator) -> [?NOT_EMPTY("the value of the field 'database' cannot be empty")];
|
||||||
database(_) -> undefined.
|
database(_) -> undefined.
|
||||||
|
|
||||||
pool_size(type) -> integer();
|
pool_size(type) -> integer();
|
||||||
|
pool_size(desc) -> "Size of the connection pool.";
|
||||||
pool_size(default) -> 8;
|
pool_size(default) -> 8;
|
||||||
pool_size(validator) -> [?MIN(1)];
|
pool_size(validator) -> [?MIN(1)];
|
||||||
pool_size(_) -> undefined.
|
pool_size(_) -> undefined.
|
||||||
|
|
||||||
username(type) -> binary();
|
username(type) -> binary();
|
||||||
|
username(desc) -> "EMQX's username in the external database.";
|
||||||
username(required) -> false;
|
username(required) -> false;
|
||||||
username(_) -> undefined.
|
username(_) -> undefined.
|
||||||
|
|
||||||
password(type) -> binary();
|
password(type) -> binary();
|
||||||
|
password(desc) -> "EMQX's password in the external database.";
|
||||||
password(required) -> false;
|
password(required) -> false;
|
||||||
password(_) -> undefined.
|
password(_) -> undefined.
|
||||||
|
|
||||||
auto_reconnect(type) -> boolean();
|
auto_reconnect(type) -> boolean();
|
||||||
|
auto_reconnect(desc) -> "Enable automatic reconnect to the database.";
|
||||||
auto_reconnect(default) -> true;
|
auto_reconnect(default) -> true;
|
||||||
auto_reconnect(_) -> undefined.
|
auto_reconnect(_) -> undefined.
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
|
|
||||||
-export([ roots/0
|
-export([ roots/0
|
||||||
, fields/1
|
, fields/1
|
||||||
|
, desc/1
|
||||||
]).
|
]).
|
||||||
|
|
||||||
-export([ ingress_desc/0
|
-export([ ingress_desc/0
|
||||||
|
@ -43,19 +44,19 @@ fields("connector") ->
|
||||||
[ {mode,
|
[ {mode,
|
||||||
sc(hoconsc:enum([cluster_shareload]),
|
sc(hoconsc:enum([cluster_shareload]),
|
||||||
#{ default => cluster_shareload
|
#{ default => cluster_shareload
|
||||||
, desc => """
|
, desc => "
|
||||||
The mode of the MQTT Bridge. Can be one of 'cluster_singleton' or 'cluster_shareload'<br>
|
The mode of the MQTT Bridge. Can be one of 'cluster_singleton' or 'cluster_shareload'<br/>
|
||||||
|
|
||||||
- cluster_singleton: create a unique MQTT connection within the emqx cluster.<br>
|
- cluster_singleton: create a unique MQTT connection within the emqx cluster.<br/>
|
||||||
In 'cluster_singleton' node, all messages toward the remote broker go through the same
|
In 'cluster_singleton' node, all messages toward the remote broker go through the same
|
||||||
MQTT connection.<br>
|
MQTT connection.<br/>
|
||||||
- cluster_shareload: create an MQTT connection on each node in the emqx cluster.<br>
|
- cluster_shareload: create an MQTT connection on each node in the emqx cluster.<br/>
|
||||||
In 'cluster_shareload' mode, the incoming load from the remote broker is shared by
|
In 'cluster_shareload' mode, the incoming load from the remote broker is shared by
|
||||||
using shared subscription.<br>
|
using shared subscription.<br/>
|
||||||
Note that the 'clientid' is suffixed by the node name, this is to avoid
|
Note that the 'clientid' is suffixed by the node name, this is to avoid
|
||||||
clientid conflicts between different nodes. And we can only use shared subscription
|
clientid conflicts between different nodes. And we can only use shared subscription
|
||||||
topic filters for 'remote_topic' of ingress connections.
|
topic filters for 'remote_topic' of ingress connections.
|
||||||
"""
|
"
|
||||||
})}
|
})}
|
||||||
, {server,
|
, {server,
|
||||||
sc(emqx_schema:ip_port(),
|
sc(emqx_schema:ip_port(),
|
||||||
|
@ -97,11 +98,7 @@ topic filters for 'remote_topic' of ingress connections.
|
||||||
, desc => "Max inflight (sent, but un-acked) messages of the MQTT protocol"
|
, desc => "Max inflight (sent, but un-acked) messages of the MQTT protocol"
|
||||||
})}
|
})}
|
||||||
, {replayq,
|
, {replayq,
|
||||||
sc(ref("replayq"),
|
sc(ref("replayq"), #{})}
|
||||||
#{ desc => """
|
|
||||||
Queue messages in disk files.
|
|
||||||
"""
|
|
||||||
})}
|
|
||||||
] ++ emqx_connector_schema_lib:ssl_fields();
|
] ++ emqx_connector_schema_lib:ssl_fields();
|
||||||
|
|
||||||
fields("ingress") ->
|
fields("ingress") ->
|
||||||
|
@ -120,23 +117,23 @@ fields("ingress") ->
|
||||||
, {local_topic,
|
, {local_topic,
|
||||||
sc(binary(),
|
sc(binary(),
|
||||||
#{ validator => fun ?MODULE:non_empty_string/1
|
#{ validator => fun ?MODULE:non_empty_string/1
|
||||||
, desc => """
|
, desc => "
|
||||||
Send messages to which topic of the local broker.<br>
|
Send messages to which topic of the local broker.<br/>
|
||||||
Template with variables is allowed.
|
Template with variables is allowed.
|
||||||
"""
|
"
|
||||||
})}
|
})}
|
||||||
, {local_qos,
|
, {local_qos,
|
||||||
sc(qos(),
|
sc(qos(),
|
||||||
#{ default => <<"${qos}">>
|
#{ default => <<"${qos}">>
|
||||||
, desc => """
|
, desc => "
|
||||||
The QoS of the MQTT message to be sent.<br>
|
The QoS of the MQTT message to be sent.<br/>
|
||||||
Template with variables is allowed."""
|
Template with variables is allowed."
|
||||||
})}
|
})}
|
||||||
, {hookpoint,
|
, {hookpoint,
|
||||||
sc(binary(),
|
sc(binary(),
|
||||||
#{ desc => """
|
#{ desc => "
|
||||||
The hook point will be triggered when there's any message received from the remote broker.
|
The hook point will be triggered when there's any message received from the remote broker.
|
||||||
"""
|
"
|
||||||
})}
|
})}
|
||||||
] ++ common_inout_confs();
|
] ++ common_inout_confs();
|
||||||
|
|
||||||
|
@ -151,94 +148,101 @@ fields("egress") ->
|
||||||
sc(binary(),
|
sc(binary(),
|
||||||
#{ default => <<"${topic}">>
|
#{ default => <<"${topic}">>
|
||||||
, validator => fun ?MODULE:non_empty_string/1
|
, validator => fun ?MODULE:non_empty_string/1
|
||||||
, desc => """
|
, desc => "
|
||||||
Forward to which topic of the remote broker.<br>
|
Forward to which topic of the remote broker.<br/>
|
||||||
Template with variables is allowed.
|
Template with variables is allowed.
|
||||||
"""
|
"
|
||||||
})}
|
})}
|
||||||
, {remote_qos,
|
, {remote_qos,
|
||||||
sc(qos(),
|
sc(qos(),
|
||||||
#{ default => <<"${qos}">>
|
#{ default => <<"${qos}">>
|
||||||
, desc => """
|
, desc => "
|
||||||
The QoS of the MQTT message to be sent.<br>
|
The QoS of the MQTT message to be sent.<br/>
|
||||||
Template with variables is allowed."""
|
Template with variables is allowed."
|
||||||
})}
|
})}
|
||||||
] ++ common_inout_confs();
|
] ++ common_inout_confs();
|
||||||
|
|
||||||
fields("replayq") ->
|
fields("replayq") ->
|
||||||
[ {dir,
|
[ {dir,
|
||||||
sc(hoconsc:union([boolean(), string()]),
|
sc(hoconsc:union([boolean(), string()]),
|
||||||
#{ desc => """
|
#{ desc => "
|
||||||
The dir where the replayq file saved.<br>
|
The dir where the replayq file saved.<br/>
|
||||||
Set to 'false' disables the replayq feature.
|
Set to 'false' disables the replayq feature.
|
||||||
"""
|
"
|
||||||
})}
|
})}
|
||||||
, {seg_bytes,
|
, {seg_bytes,
|
||||||
sc(emqx_schema:bytesize(),
|
sc(emqx_schema:bytesize(),
|
||||||
#{ default => "100MB"
|
#{ default => "100MB"
|
||||||
, desc => """
|
, desc => "
|
||||||
The size in bytes of a single segment.<br>
|
The size in bytes of a single segment.<br/>
|
||||||
A segment is mapping to a file in the replayq dir. If the current segment is full, a new segment
|
A segment is mapping to a file in the replayq dir. If the current segment is full, a new segment
|
||||||
(file) will be opened to write.
|
(file) will be opened to write.
|
||||||
"""
|
"
|
||||||
})}
|
})}
|
||||||
, {offload,
|
, {offload,
|
||||||
sc(boolean(),
|
sc(boolean(),
|
||||||
#{ default => false
|
#{ default => false
|
||||||
, desc => """
|
, desc => "
|
||||||
In offload mode, the disk queue is only used to offload queue tail segments.<br>
|
In offload mode, the disk queue is only used to offload queue tail segments.<br/>
|
||||||
The messages are cached in the memory first, then it writes to the replayq files after the size of
|
The messages are cached in the memory first, then it writes to the replayq files after the size of
|
||||||
the memory cache reaches 'seg_bytes'.
|
the memory cache reaches 'seg_bytes'.
|
||||||
"""
|
"
|
||||||
})}
|
})}
|
||||||
].
|
].
|
||||||
|
|
||||||
|
desc("ingress") ->
|
||||||
|
ingress_desc();
|
||||||
|
desc("egress") ->
|
||||||
|
egress_desc();
|
||||||
|
desc("replayq") ->
|
||||||
|
"Queue messages in disk files.";
|
||||||
|
desc(_) ->
|
||||||
|
undefined.
|
||||||
|
|
||||||
topic_mappings() ->
|
topic_mappings() ->
|
||||||
[ {ingress,
|
[ {ingress,
|
||||||
sc(ref("ingress"),
|
sc(ref("ingress"),
|
||||||
#{ default => #{}
|
#{ default => #{}
|
||||||
, desc => ingress_desc()
|
|
||||||
})}
|
})}
|
||||||
, {egress,
|
, {egress,
|
||||||
sc(ref("egress"),
|
sc(ref("egress"),
|
||||||
#{ default => #{}
|
#{ default => #{}
|
||||||
, desc => egress_desc()
|
|
||||||
})}
|
})}
|
||||||
].
|
].
|
||||||
|
|
||||||
ingress_desc() -> """
|
ingress_desc() -> "
|
||||||
The ingress config defines how this bridge receive messages from the remote MQTT broker, and then
|
The ingress config defines how this bridge receive messages from the remote MQTT broker, and then
|
||||||
send them to the local broker.<br>
|
send them to the local broker.<br/>
|
||||||
Template with variables is allowed in 'local_topic', 'remote_qos', 'qos', 'retain',
|
Template with variables is allowed in 'local_topic', 'remote_qos', 'qos', 'retain',
|
||||||
'payload'.<br>
|
'payload'.<br/>
|
||||||
NOTE: if this bridge is used as the input of a rule (emqx rule engine), and also local_topic is
|
NOTE: if this bridge is used as the input of a rule (emqx rule engine), and also local_topic is
|
||||||
configured, then messages got from the remote broker will be sent to both the 'local_topic' and
|
configured, then messages got from the remote broker will be sent to both the 'local_topic' and
|
||||||
the rule.
|
the rule.
|
||||||
""".
|
".
|
||||||
|
|
||||||
egress_desc() -> """
|
egress_desc() -> "
|
||||||
The egress config defines how this bridge forwards messages from the local broker to the remote
|
The egress config defines how this bridge forwards messages from the local broker to the remote
|
||||||
broker.<br>
|
broker.<br/>
|
||||||
Template with variables is allowed in 'remote_topic', 'qos', 'retain', 'payload'.<br>
|
Template with variables is allowed in 'remote_topic', 'qos', 'retain', 'payload'.<br/>
|
||||||
NOTE: if this bridge is used as the output of a rule (emqx rule engine), and also local_topic
|
NOTE: if this bridge is used as the output of a rule (emqx rule engine), and also local_topic
|
||||||
is configured, then both the data got from the rule and the MQTT messages that matches
|
is configured, then both the data got from the rule and the MQTT messages that matches
|
||||||
local_topic will be forwarded.
|
local_topic will be forwarded.
|
||||||
""".
|
".
|
||||||
|
|
||||||
common_inout_confs() ->
|
common_inout_confs() ->
|
||||||
[ {retain,
|
[ {retain,
|
||||||
sc(hoconsc:union([boolean(), binary()]),
|
sc(hoconsc:union([boolean(), binary()]),
|
||||||
#{ default => <<"${retain}">>
|
#{ default => <<"${retain}">>
|
||||||
, desc => """
|
, desc => "
|
||||||
The 'retain' flag of the MQTT message to be sent.<br>
|
The 'retain' flag of the MQTT message to be sent.<br/>
|
||||||
Template with variables is allowed."""
|
Template with variables is allowed."
|
||||||
})}
|
})}
|
||||||
, {payload,
|
, {payload,
|
||||||
sc(binary(),
|
sc(binary(),
|
||||||
#{ default => <<"${payload}">>
|
#{ default => <<"${payload}">>
|
||||||
, desc => """
|
, desc => "
|
||||||
The payload of the MQTT message to be sent.<br>
|
The payload of the MQTT message to be sent.<br/>
|
||||||
Template with variables is allowed."""
|
Template with variables is allowed."
|
||||||
})}
|
})}
|
||||||
].
|
].
|
||||||
|
|
||||||
|
|
|
@ -32,7 +32,7 @@
|
||||||
|
|
||||||
-reflect_type([duration/0]).
|
-reflect_type([duration/0]).
|
||||||
|
|
||||||
-export([namespace/0, roots/0, fields/1, server_config/0]).
|
-export([namespace/0, roots/0, fields/1, desc/1, server_config/0]).
|
||||||
|
|
||||||
namespace() -> exhook.
|
namespace() -> exhook.
|
||||||
|
|
||||||
|
@ -41,7 +41,9 @@ roots() -> [exhook].
|
||||||
fields(exhook) ->
|
fields(exhook) ->
|
||||||
[{servers,
|
[{servers,
|
||||||
sc(hoconsc:array(ref(server)),
|
sc(hoconsc:array(ref(server)),
|
||||||
#{default => []})}
|
#{ default => []
|
||||||
|
, desc => "List of exhook servers."
|
||||||
|
})}
|
||||||
];
|
];
|
||||||
|
|
||||||
fields(server) ->
|
fields(server) ->
|
||||||
|
@ -61,7 +63,7 @@ fields(server) ->
|
||||||
})}
|
})}
|
||||||
, {failed_action, failed_action()}
|
, {failed_action, failed_action()}
|
||||||
, {ssl,
|
, {ssl,
|
||||||
sc(ref(ssl_conf), #{desc => "SSL client config"})}
|
sc(ref(ssl_conf), #{})}
|
||||||
, {auto_reconnect,
|
, {auto_reconnect,
|
||||||
sc(hoconsc:union([false, duration()]),
|
sc(hoconsc:union([false, duration()]),
|
||||||
#{ default => "60s"
|
#{ default => "60s"
|
||||||
|
@ -81,6 +83,15 @@ fields(ssl_conf) ->
|
||||||
Schema = emqx_schema:client_ssl_opts_schema(#{}),
|
Schema = emqx_schema:client_ssl_opts_schema(#{}),
|
||||||
lists:keydelete(user_lookup_fun, 1, Schema).
|
lists:keydelete(user_lookup_fun, 1, Schema).
|
||||||
|
|
||||||
|
desc(exhook) ->
|
||||||
|
"External hook (exhook) configuration.";
|
||||||
|
desc(server) ->
|
||||||
|
"gRPC server configuration.";
|
||||||
|
desc(ssl_conf) ->
|
||||||
|
"SSL client configuration.";
|
||||||
|
desc(_) ->
|
||||||
|
undefined.
|
||||||
|
|
||||||
%% types
|
%% types
|
||||||
sc(Type, Meta) -> Meta#{type => Type}.
|
sc(Type, Meta) -> Meta#{type => Type}.
|
||||||
|
|
||||||
|
|
|
@ -294,18 +294,22 @@ fields(exproto) ->
|
||||||
fields(exproto_grpc_server) ->
|
fields(exproto_grpc_server) ->
|
||||||
[ {bind,
|
[ {bind,
|
||||||
sc(hoconsc:union([ip_port(), integer()]),
|
sc(hoconsc:union([ip_port(), integer()]),
|
||||||
#{required => true})}
|
#{ required => true
|
||||||
|
, desc => "Listening address and port for the gRPC server."
|
||||||
|
})}
|
||||||
, {ssl,
|
, {ssl,
|
||||||
sc(ref(ssl_server_opts),
|
sc(ref(ssl_server_opts),
|
||||||
#{ required => {false, recursively}
|
#{ required => {false, recursively}
|
||||||
|
, desc => "SSL configuration for the gRPC server."
|
||||||
})}
|
})}
|
||||||
];
|
];
|
||||||
|
|
||||||
fields(exproto_grpc_handler) ->
|
fields(exproto_grpc_handler) ->
|
||||||
[ {address, sc(binary(), #{required => true})}
|
[ {address, sc(binary(), #{required => true, desc => "gRPC server address."})}
|
||||||
, {ssl,
|
, {ssl,
|
||||||
sc(ref(emqx_schema, ssl_client_opts),
|
sc(ref(emqx_schema, ssl_client_opts),
|
||||||
#{ required => {false, recursively}
|
#{ required => {false, recursively}
|
||||||
|
, desc => "SSL configuration for the gRPC client."
|
||||||
})}
|
})}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -318,9 +322,9 @@ fields(ssl_server_opts) ->
|
||||||
}, true);
|
}, true);
|
||||||
|
|
||||||
fields(clientinfo_override) ->
|
fields(clientinfo_override) ->
|
||||||
[ {username, sc(binary())}
|
[ {username, sc(binary(), #{desc => "Template for overriding username."})}
|
||||||
, {password, sc(binary())}
|
, {password, sc(binary(), #{desc => "Template for overriding password."})}
|
||||||
, {clientid, sc(binary())}
|
, {clientid, sc(binary(), #{desc => "Template for overriding clientid."})}
|
||||||
];
|
];
|
||||||
|
|
||||||
fields(lwm2m_translators) ->
|
fields(lwm2m_translators) ->
|
||||||
|
@ -362,25 +366,31 @@ the LwM2M client"
|
||||||
];
|
];
|
||||||
|
|
||||||
fields(translator) ->
|
fields(translator) ->
|
||||||
[ {topic, sc(binary(), #{required => true})}
|
[ {topic, sc(binary(),
|
||||||
, {qos, sc(emqx_schema:qos(), #{default => 0})}
|
#{ required => true
|
||||||
|
, desc => "Which topic the device's upstream message is published to."
|
||||||
|
})}
|
||||||
|
, {qos, sc(emqx_schema:qos(),
|
||||||
|
#{ default => 0
|
||||||
|
, desc => "QoS of the published messages."
|
||||||
|
})}
|
||||||
];
|
];
|
||||||
|
|
||||||
fields(udp_listeners) ->
|
fields(udp_listeners) ->
|
||||||
[ {udp, sc(map(name, ref(udp_listener)))}
|
[ {udp, sc(map(name, ref(udp_listener)), #{desc => "UDP configuration."})}
|
||||||
, {dtls, sc(map(name, ref(dtls_listener)))}
|
, {dtls, sc(map(name, ref(dtls_listener)), #{desc => "DTLS configuration."})}
|
||||||
];
|
];
|
||||||
|
|
||||||
fields(tcp_listeners) ->
|
fields(tcp_listeners) ->
|
||||||
[ {tcp, sc(map(name, ref(tcp_listener)))}
|
[ {tcp, sc(map(name, ref(tcp_listener)), #{desc => "TCP configuration."})}
|
||||||
, {ssl, sc(map(name, ref(ssl_listener)))}
|
, {ssl, sc(map(name, ref(ssl_listener)), #{desc => "SSL configuration."})}
|
||||||
];
|
];
|
||||||
|
|
||||||
fields(udp_tcp_listeners) ->
|
fields(udp_tcp_listeners) ->
|
||||||
[ {udp, sc(map(name, ref(udp_listener)))}
|
[ {udp, sc(map(name, ref(udp_listener)), #{desc => "UDP configuration."})}
|
||||||
, {dtls, sc(map(name, ref(dtls_listener)))}
|
, {dtls, sc(map(name, ref(dtls_listener)), #{desc => "DTLS configuration."})}
|
||||||
, {tcp, sc(map(name, ref(tcp_listener)))}
|
, {tcp, sc(map(name, ref(tcp_listener)), #{desc => "TCP configuration."})}
|
||||||
, {ssl, sc(map(name, ref(ssl_listener)))}
|
, {ssl, sc(map(name, ref(ssl_listener)), #{desc => "SSL configuration."})}
|
||||||
];
|
];
|
||||||
|
|
||||||
fields(tcp_listener) ->
|
fields(tcp_listener) ->
|
||||||
|
@ -524,9 +534,7 @@ It has two purposes:
|
||||||
, desc => ""
|
, desc => ""
|
||||||
})}
|
})}
|
||||||
, {clientinfo_override,
|
, {clientinfo_override,
|
||||||
sc(ref(clientinfo_override),
|
sc(ref(clientinfo_override), #{})}
|
||||||
#{ desc => "ClientInfo override"
|
|
||||||
})}
|
|
||||||
, {?EMQX_AUTHENTICATION_CONFIG_ROOT_NAME_ATOM, authentication_schema()}
|
, {?EMQX_AUTHENTICATION_CONFIG_ROOT_NAME_ATOM, authentication_schema()}
|
||||||
].
|
].
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,8 @@
|
||||||
-export([
|
-export([
|
||||||
namespace/0,
|
namespace/0,
|
||||||
roots/0,
|
roots/0,
|
||||||
fields/1
|
fields/1,
|
||||||
|
desc/1
|
||||||
]).
|
]).
|
||||||
|
|
||||||
namespace() -> modules.
|
namespace() -> modules.
|
||||||
|
@ -65,6 +66,17 @@ fields("rewrite") ->
|
||||||
fields("topic_metrics") ->
|
fields("topic_metrics") ->
|
||||||
[{topic, sc(binary(), #{})}].
|
[{topic, sc(binary(), #{})}].
|
||||||
|
|
||||||
|
desc("telemetry") ->
|
||||||
|
"Settings for the telemetry module.";
|
||||||
|
desc("delayed") ->
|
||||||
|
"Settings for the delayed module.";
|
||||||
|
desc("rewrite") ->
|
||||||
|
"Settings for the rewrite module.";
|
||||||
|
desc("topic_metrics") ->
|
||||||
|
"Settings for the topic metrics module.";
|
||||||
|
desc(_) ->
|
||||||
|
undefined.
|
||||||
|
|
||||||
regular_expression(type) -> binary();
|
regular_expression(type) -> binary();
|
||||||
regular_expression(desc) -> "Regular expressions";
|
regular_expression(desc) -> "Regular expressions";
|
||||||
regular_expression(example) -> "^x/y/(.+)$";
|
regular_expression(example) -> "^x/y/(.+)$";
|
||||||
|
|
|
@ -22,7 +22,9 @@
|
||||||
|
|
||||||
-export([ namespace/0
|
-export([ namespace/0
|
||||||
, roots/0
|
, roots/0
|
||||||
, fields/1]).
|
, fields/1
|
||||||
|
, desc/1
|
||||||
|
]).
|
||||||
|
|
||||||
-export([ validate_sql/1
|
-export([ validate_sql/1
|
||||||
]).
|
]).
|
||||||
|
@ -82,30 +84,7 @@ counter of the function output or the bridge channel will increase.
|
||||||
|
|
||||||
fields("builtin_output_republish") ->
|
fields("builtin_output_republish") ->
|
||||||
[ {function, sc(republish, #{desc => "Republish the message as a new MQTT message"})}
|
[ {function, sc(republish, #{desc => "Republish the message as a new MQTT message"})}
|
||||||
, {args, sc(ref("republish_args"),
|
, {args, sc(ref("republish_args"), #{default => #{}})}
|
||||||
#{ desc => """
|
|
||||||
The arguments of the built-in 'republish' output.<br>
|
|
||||||
We can use variables in the args.<br>
|
|
||||||
|
|
||||||
The variables are selected by the rule. For example, if the rule SQL is defined as following:
|
|
||||||
<code>
|
|
||||||
SELECT clientid, qos, payload FROM \"t/1\"
|
|
||||||
</code>
|
|
||||||
Then there are 3 variables available: <code>clientid</code>, <code>qos</code> and
|
|
||||||
<code>payload</code>. And if we've set the args to:
|
|
||||||
<code>
|
|
||||||
{
|
|
||||||
topic = \"t/${clientid}\"
|
|
||||||
qos = \"${qos}\"
|
|
||||||
payload = \"msg: ${payload}\"
|
|
||||||
}
|
|
||||||
</code>
|
|
||||||
When the rule is triggered by an MQTT message with payload = \"hello\", qos = 1,
|
|
||||||
clientid = \"Steve\", the rule will republish a new MQTT message to topic \"t/Steve\",
|
|
||||||
payload = \"msg: hello\", and qos = 1.
|
|
||||||
"""
|
|
||||||
, default => #{}
|
|
||||||
})}
|
|
||||||
];
|
];
|
||||||
|
|
||||||
fields("builtin_output_console") ->
|
fields("builtin_output_console") ->
|
||||||
|
@ -178,6 +157,38 @@ of the rule, then the string \"undefined\" is used.
|
||||||
})}
|
})}
|
||||||
].
|
].
|
||||||
|
|
||||||
|
desc("rule_engine") ->
|
||||||
|
"Configuration for the EMQX Rule Engine.";
|
||||||
|
desc("rules") ->
|
||||||
|
"Configuration for a rule.";
|
||||||
|
desc("builtin_output_republish") ->
|
||||||
|
"Configuration for a built-in output.";
|
||||||
|
desc("builtin_output_console") ->
|
||||||
|
"Configuration for a built-in output.";
|
||||||
|
desc("user_provided_function") ->
|
||||||
|
"Configuration for a built-in output.";
|
||||||
|
desc("republish_args") ->
|
||||||
|
"The arguments of the built-in 'republish' output.<br>"
|
||||||
|
"One can use variables in the args.<br>\n"
|
||||||
|
"The variables are selected by the rule. For example, if the rule SQL is defined as following:\n"
|
||||||
|
"<code>\n"
|
||||||
|
" SELECT clientid, qos, payload FROM \"t/1\"\n"
|
||||||
|
"</code>\n"
|
||||||
|
"Then there are 3 variables available: <code>clientid</code>, <code>qos</code> and\n"
|
||||||
|
"<code>payload</code>. And if we've set the args to:\n"
|
||||||
|
"<code>\n"
|
||||||
|
" {\n"
|
||||||
|
" topic = \"t/${clientid}\"\n"
|
||||||
|
" qos = \"${qos}\"\n"
|
||||||
|
" payload = \"msg: ${payload}\"\n"
|
||||||
|
" }\n"
|
||||||
|
"</code>\n"
|
||||||
|
"When the rule is triggered by an MQTT message with payload = `hello`, qos = 1,\n"
|
||||||
|
"clientid = `Steve`, the rule will republish a new MQTT message to topic `t/Steve`,\n"
|
||||||
|
"payload = `msg: hello`, and `qos = 1`.";
|
||||||
|
desc(_) ->
|
||||||
|
undefined.
|
||||||
|
|
||||||
rule_name() ->
|
rule_name() ->
|
||||||
{"name", sc(binary(),
|
{"name", sc(binary(),
|
||||||
#{ desc => "The name of the rule"
|
#{ desc => "The name of the rule"
|
||||||
|
|
Loading…
Reference in New Issue