diff --git a/apps/emqx_auth/src/emqx_authn/emqx_authn_utils.erl b/apps/emqx_auth/src/emqx_authn/emqx_authn_utils.erl index 0a938eafb..f782e0e6c 100644 --- a/apps/emqx_auth/src/emqx_authn/emqx_authn_utils.erl +++ b/apps/emqx_auth/src/emqx_authn/emqx_authn_utils.erl @@ -108,22 +108,22 @@ check_password_from_selected_map(Algorithm, Selected, Password) -> end. parse_deep(Template) -> - Result = emqx_connector_template:parse_deep(Template), + Result = emqx_template:parse_deep(Template), handle_disallowed_placeholders(Result, {deep, Template}). parse_str(Template) -> - Result = emqx_connector_template:parse(Template), + Result = emqx_template:parse(Template), handle_disallowed_placeholders(Result, {string, Template}). parse_sql(Template, ReplaceWith) -> - {Statement, Result} = emqx_connector_template_sql:parse_prepstmt( + {Statement, Result} = emqx_template_sql:parse_prepstmt( Template, #{parameters => ReplaceWith, strip_double_quote => true} ), {Statement, handle_disallowed_placeholders(Result, {string, Template})}. handle_disallowed_placeholders(Template, Source) -> - case emqx_connector_template:validate(?ALLOWED_VARS, Template) of + case emqx_template:validate(?ALLOWED_VARS, Template) of ok -> Template; {error, Disallowed} -> @@ -139,14 +139,14 @@ handle_disallowed_placeholders(Template, Source) -> Result = prerender_disallowed_placeholders(Template), case Source of {string, _} -> - emqx_connector_template:parse(Result); + emqx_template:parse(Result); {deep, _} -> - emqx_connector_template:parse_deep(Result) + emqx_template:parse_deep(Result) end end. prerender_disallowed_placeholders(Template) -> - {Result, _} = emqx_connector_template:render(Template, #{}, #{ + {Result, _} = emqx_template:render(Template, #{}, #{ var_trans => fun(Name, _) -> % NOTE % Rendering disallowed placeholders in escaped form, which will then @@ -162,7 +162,7 @@ prerender_disallowed_placeholders(Template) -> render_deep(Template, Credential) -> % NOTE % Ignoring errors here, undefined bindings will be replaced with empty string. - {Term, _Errors} = emqx_connector_template:render( + {Term, _Errors} = emqx_template:render( Template, mapping_credential(Credential), #{var_trans => fun to_string/2} @@ -172,7 +172,7 @@ render_deep(Template, Credential) -> render_str(Template, Credential) -> % NOTE % Ignoring errors here, undefined bindings will be replaced with empty string. - {String, _Errors} = emqx_connector_template:render( + {String, _Errors} = emqx_template:render( Template, mapping_credential(Credential), #{var_trans => fun to_string/2} @@ -182,7 +182,7 @@ render_str(Template, Credential) -> render_urlencoded_str(Template, Credential) -> % NOTE % Ignoring errors here, undefined bindings will be replaced with empty string. - {String, _Errors} = emqx_connector_template:render( + {String, _Errors} = emqx_template:render( Template, mapping_credential(Credential), #{var_trans => fun to_urlencoded_string/2} @@ -192,7 +192,7 @@ render_urlencoded_str(Template, Credential) -> render_sql_params(ParamList, Credential) -> % NOTE % Ignoring errors here, undefined bindings will be replaced with empty string. - {Row, _Errors} = emqx_connector_template:render( + {Row, _Errors} = emqx_template:render( ParamList, mapping_credential(Credential), #{var_trans => fun to_sql_valaue/2} @@ -322,10 +322,10 @@ to_urlencoded_string(Name, Value) -> emqx_http_lib:uri_encode(to_string(Name, Value)). to_string(Name, Value) -> - emqx_connector_template:to_string(render_var(Name, Value)). + emqx_template:to_string(render_var(Name, Value)). to_sql_valaue(Name, Value) -> - emqx_connector_sql:to_sql_value(render_var(Name, Value)). + emqx_utils_sql:to_sql_value(render_var(Name, Value)). render_var(_, undefined) -> % NOTE diff --git a/apps/emqx_auth/src/emqx_authz/emqx_authz_rule.erl b/apps/emqx_auth/src/emqx_authz/emqx_authz_rule.erl index 160f9cac4..ad6dec56b 100644 --- a/apps/emqx_auth/src/emqx_authz/emqx_authz_rule.erl +++ b/apps/emqx_auth/src/emqx_authz/emqx_authz_rule.erl @@ -184,7 +184,7 @@ compile_topic({eq, Topic}) -> {eq, emqx_topic:words(bin(Topic))}; compile_topic(Topic) -> Template = emqx_authz_utils:parse_str(Topic, [?VAR_USERNAME, ?VAR_CLIENTID]), - case emqx_connector_template:is_const(Template) of + case emqx_template:is_const(Template) of true -> emqx_topic:words(bin(Topic)); false -> {pattern, Template} end. @@ -302,7 +302,7 @@ match_who(_, _) -> match_topics(_ClientInfo, _Topic, []) -> false; match_topics(ClientInfo, Topic, [{pattern, PatternFilter} | Filters]) -> - TopicFilter = bin(emqx_connector_template:render_strict(PatternFilter, ClientInfo)), + TopicFilter = bin(emqx_template:render_strict(PatternFilter, ClientInfo)), match_topic(emqx_topic:words(Topic), emqx_topic:words(TopicFilter)) orelse match_topics(ClientInfo, Topic, Filters); match_topics(ClientInfo, Topic, [TopicFilter | Filters]) -> diff --git a/apps/emqx_auth/src/emqx_authz/emqx_authz_utils.erl b/apps/emqx_auth/src/emqx_authz/emqx_authz_utils.erl index 444955504..a17a563ae 100644 --- a/apps/emqx_auth/src/emqx_authz/emqx_authz_utils.erl +++ b/apps/emqx_auth/src/emqx_authz/emqx_authz_utils.erl @@ -110,15 +110,15 @@ update_config(Path, ConfigRequest) -> }). parse_deep(Template, PlaceHolders) -> - Result = emqx_connector_template:parse_deep(Template), + Result = emqx_template:parse_deep(Template), handle_disallowed_placeholders(Result, {deep, Template}, PlaceHolders). parse_str(Template, PlaceHolders) -> - Result = emqx_connector_template:parse(Template), + Result = emqx_template:parse(Template), handle_disallowed_placeholders(Result, {string, Template}, PlaceHolders). parse_sql(Template, ReplaceWith, PlaceHolders) -> - {Statement, Result} = emqx_connector_template_sql:parse_prepstmt( + {Statement, Result} = emqx_template_sql:parse_prepstmt( Template, #{parameters => ReplaceWith, strip_double_quote => true} ), @@ -126,7 +126,7 @@ parse_sql(Template, ReplaceWith, PlaceHolders) -> {Statement, FResult}. handle_disallowed_placeholders(Template, Source, Allowed) -> - case emqx_connector_template:validate(Allowed, Template) of + case emqx_template:validate(Allowed, Template) of ok -> Template; {error, Disallowed} -> @@ -142,14 +142,14 @@ handle_disallowed_placeholders(Template, Source, Allowed) -> Result = prerender_disallowed_placeholders(Template, Allowed), case Source of {string, _} -> - emqx_connector_template:parse(Result); + emqx_template:parse(Result); {deep, _} -> - emqx_connector_template:parse_deep(Result) + emqx_template:parse_deep(Result) end end. prerender_disallowed_placeholders(Template, Allowed) -> - {Result, _} = emqx_connector_template:render(Template, #{}, #{ + {Result, _} = emqx_template:render(Template, #{}, #{ var_trans => fun(Name, _) -> % NOTE % Rendering disallowed placeholders in escaped form, which will then @@ -165,7 +165,7 @@ prerender_disallowed_placeholders(Template, Allowed) -> render_deep(Template, Values) -> % NOTE % Ignoring errors here, undefined bindings will be replaced with empty string. - {Term, _Errors} = emqx_connector_template:render( + {Term, _Errors} = emqx_template:render( Template, client_vars(Values), #{var_trans => fun to_string/2} @@ -175,7 +175,7 @@ render_deep(Template, Values) -> render_str(Template, Values) -> % NOTE % Ignoring errors here, undefined bindings will be replaced with empty string. - {String, _Errors} = emqx_connector_template:render( + {String, _Errors} = emqx_template:render( Template, client_vars(Values), #{var_trans => fun to_string/2} @@ -185,7 +185,7 @@ render_str(Template, Values) -> render_urlencoded_str(Template, Values) -> % NOTE % Ignoring errors here, undefined bindings will be replaced with empty string. - {String, _Errors} = emqx_connector_template:render( + {String, _Errors} = emqx_template:render( Template, client_vars(Values), #{var_trans => fun to_urlencoded_string/2} @@ -195,7 +195,7 @@ render_urlencoded_str(Template, Values) -> render_sql_params(ParamList, Values) -> % NOTE % Ignoring errors here, undefined bindings will be replaced with empty string. - {Row, _Errors} = emqx_connector_template:render( + {Row, _Errors} = emqx_template:render( ParamList, client_vars(Values), #{var_trans => fun to_sql_value/2} @@ -270,10 +270,10 @@ to_urlencoded_string(Name, Value) -> emqx_http_lib:uri_encode(to_string(Name, Value)). to_string(Name, Value) -> - emqx_connector_template:to_string(render_var(Name, Value)). + emqx_template:to_string(render_var(Name, Value)). to_sql_value(Name, Value) -> - emqx_connector_sql:to_sql_value(render_var(Name, Value)). + emqx_utils_sql:to_sql_value(render_var(Name, Value)). render_var(_, undefined) -> % NOTE diff --git a/apps/emqx_auth/test/emqx_authz/emqx_authz_rule_SUITE.erl b/apps/emqx_auth/test/emqx_authz/emqx_authz_rule_SUITE.erl index 5031daff6..d81a93038 100644 --- a/apps/emqx_auth/test/emqx_authz/emqx_authz_rule_SUITE.erl +++ b/apps/emqx_auth/test/emqx_authz/emqx_authz_rule_SUITE.erl @@ -69,8 +69,8 @@ set_special_configs(_App) -> t_compile(_) -> % NOTE % Some of the following testcase are relying on the internal representation of - % `emqx_connector_template:t()`. If the internal representation is changed, these - % testcases may fail. + % `emqx_template:t()`. If the internal representation is changed, these testcases + % may fail. ?assertEqual({deny, all, all, [['#']]}, emqx_authz_rule:compile({deny, all})), ?assertEqual( diff --git a/apps/emqx_bridge_http/src/emqx_bridge_http_connector.erl b/apps/emqx_bridge_http/src/emqx_bridge_http_connector.erl index 869f081fb..88f55af52 100644 --- a/apps/emqx_bridge_http/src/emqx_bridge_http_connector.erl +++ b/apps/emqx_bridge_http/src/emqx_bridge_http_connector.erl @@ -535,7 +535,7 @@ maybe_parse_template(Key, Conf) -> end. parse_template(String) -> - emqx_connector_template:parse(String). + emqx_template:parse(String). process_request( #{ @@ -573,7 +573,7 @@ render_headers(HeaderTks, Msg) -> render_template(Template, Msg) -> % NOTE: ignoring errors here, missing variables will be rendered as `"undefined"`. - {String, _Errors} = emqx_connector_template:render(Template, Msg), + {String, _Errors} = emqx_template:render(Template, Msg), String. render_template_string(Template, Msg) -> diff --git a/apps/emqx_bridge_http/test/emqx_bridge_http_connector_tests.erl b/apps/emqx_bridge_http/test/emqx_bridge_http_connector_tests.erl index 1de210260..4f5e2929c 100644 --- a/apps/emqx_bridge_http/test/emqx_bridge_http_connector_tests.erl +++ b/apps/emqx_bridge_http/test/emqx_bridge_http_connector_tests.erl @@ -84,7 +84,7 @@ is_wrapped(_Other) -> false. untmpl(Tpl) -> - iolist_to_binary(emqx_connector_template:render_strict(Tpl, #{})). + iolist_to_binary(emqx_template:render_strict(Tpl, #{})). is_unwrapped_headers(Headers) -> lists:all(fun is_unwrapped_header/1, Headers). diff --git a/apps/emqx_connector/src/emqx_connector_sql.erl b/apps/emqx_connector/src/emqx_connector_sql.erl deleted file mode 100644 index be0b220e6..000000000 --- a/apps/emqx_connector/src/emqx_connector_sql.erl +++ /dev/null @@ -1,159 +0,0 @@ -%%-------------------------------------------------------------------- -%% Copyright (c) 2022-2023 EMQ Technologies Co., Ltd. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%%-------------------------------------------------------------------- - --module(emqx_connector_sql). - --export([get_statement_type/1]). --export([parse_insert/1]). - --export([to_sql_value/1]). --export([to_sql_string/2]). - --export([escape_sql/1]). --export([escape_cql/1]). --export([escape_mysql/1]). - --export_type([value/0]). - --type statement_type() :: select | insert | delete. --type value() :: null | binary() | number() | boolean() | [value()]. - --dialyzer({no_improper_lists, [escape_mysql/4, escape_prepend/4]}). - --spec get_statement_type(iodata()) -> statement_type() | {error, unknown}. -get_statement_type(Query) -> - KnownTypes = #{ - <<"select">> => select, - <<"insert">> => insert, - <<"delete">> => delete - }, - case re:run(Query, <<"^\\s*([a-zA-Z]+)">>, [{capture, all_but_first, binary}]) of - {match, [Token]} -> - maps:get(string:lowercase(Token), KnownTypes, {error, unknown}); - _ -> - {error, unknown} - end. - -%% @doc Parse an INSERT SQL statement into its INSERT part and the VALUES part. -%% SQL = <<"INSERT INTO \"abc\" (c1, c2, c3) VALUES (${a}, ${b}, ${c.prop})">> -%% {ok, {<<"INSERT INTO \"abc\" (c1, c2, c3)">>, <<"(${a}, ${b}, ${c.prop})">>}} --spec parse_insert(iodata()) -> - {ok, {_Statement :: binary(), _Rows :: binary()}} | {error, not_insert_sql}. -parse_insert(SQL) -> - case re:split(SQL, "((?i)values)", [{return, binary}]) of - [Part1, _, Part3] -> - case string:trim(Part1, leading) of - <<"insert", _/binary>> = InsertSQL -> - {ok, {InsertSQL, Part3}}; - <<"INSERT", _/binary>> = InsertSQL -> - {ok, {InsertSQL, Part3}}; - _ -> - {error, not_insert_sql} - end; - _ -> - {error, not_insert_sql} - end. - -%% @doc Convert an Erlang term to a value that can be used primarily in -%% prepared SQL statements. --spec to_sql_value(term()) -> value(). -to_sql_value(undefined) -> null; -to_sql_value(List) when is_list(List) -> List; -to_sql_value(Bin) when is_binary(Bin) -> Bin; -to_sql_value(Num) when is_number(Num) -> Num; -to_sql_value(Bool) when is_boolean(Bool) -> Bool; -to_sql_value(Atom) when is_atom(Atom) -> atom_to_binary(Atom, utf8); -to_sql_value(Map) when is_map(Map) -> emqx_utils_json:encode(Map). - -%% @doc Convert an Erlang term to a string that can be interpolated in literal -%% SQL statements. The value is escaped if necessary. --spec to_sql_string(term(), Options) -> iodata() when - Options :: #{ - escaping => cql | mysql | sql - }. -to_sql_string(String, #{escaping := mysql}) when is_binary(String) -> - try - escape_mysql(String) - catch - throw:invalid_utf8 -> - [<<"0x">>, binary:encode_hex(String)] - end; -to_sql_string(Term, #{escaping := mysql}) -> - maybe_escape(Term, fun escape_mysql/1); -to_sql_string(Term, #{escaping := cql}) -> - maybe_escape(Term, fun escape_cql/1); -to_sql_string(Term, #{}) -> - maybe_escape(Term, fun escape_sql/1). - --spec maybe_escape(_Value, fun((binary()) -> iodata())) -> iodata(). -maybe_escape(undefined, _EscapeFun) -> - <<"NULL">>; -maybe_escape(Str, EscapeFun) when is_binary(Str) -> - EscapeFun(Str); -maybe_escape(Str, EscapeFun) when is_list(Str) -> - case unicode:characters_to_binary(Str) of - Bin when is_binary(Bin) -> - EscapeFun(Bin); - Otherwise -> - error(Otherwise) - end; -maybe_escape(Val, EscapeFun) when is_atom(Val) orelse is_map(Val) -> - EscapeFun(emqx_connector_template:to_string(Val)); -maybe_escape(Val, _EscapeFun) -> - emqx_connector_template:to_string(Val). - --spec escape_sql(binary()) -> iodata(). -escape_sql(S) -> - % NOTE - % This is a bit misleading: currently, escaping logic in `escape_sql/1` likely - % won't work with pgsql since it does not support C-style escapes by default. - % https://www.postgresql.org/docs/14/sql-syntax-lexical.html#SQL-SYNTAX-CONSTANTS - ES = binary:replace(S, [<<"\\">>, <<"'">>], <<"\\">>, [global, {insert_replaced, 1}]), - [$', ES, $']. - --spec escape_cql(binary()) -> iodata(). -escape_cql(S) -> - ES = binary:replace(S, <<"'">>, <<"'">>, [global, {insert_replaced, 1}]), - [$', ES, $']. - --spec escape_mysql(binary()) -> iodata(). -escape_mysql(S0) -> - % https://dev.mysql.com/doc/refman/8.0/en/string-literals.html - [$', escape_mysql(S0, 0, 0, S0), $']. - -%% NOTE -%% This thing looks more complicated than needed because it's optimized for as few -%% intermediate memory (re)allocations as possible. -escape_mysql(<<$', Rest/binary>>, I, Run, Src) -> - escape_prepend(I, Run, Src, [<<"\\'">> | escape_mysql(Rest, I + Run + 1, 0, Src)]); -escape_mysql(<<$\\, Rest/binary>>, I, Run, Src) -> - escape_prepend(I, Run, Src, [<<"\\\\">> | escape_mysql(Rest, I + Run + 1, 0, Src)]); -escape_mysql(<<0, Rest/binary>>, I, Run, Src) -> - escape_prepend(I, Run, Src, [<<"\\0">> | escape_mysql(Rest, I + Run + 1, 0, Src)]); -escape_mysql(<<_/utf8, Rest/binary>> = S, I, Run, Src) -> - CWidth = byte_size(S) - byte_size(Rest), - escape_mysql(Rest, I, Run + CWidth, Src); -escape_mysql(<<>>, 0, _, Src) -> - Src; -escape_mysql(<<>>, I, Run, Src) -> - binary:part(Src, I, Run); -escape_mysql(_, _I, _Run, _Src) -> - throw(invalid_utf8). - -escape_prepend(_RunI, 0, _Src, Tail) -> - Tail; -escape_prepend(I, Run, Src, Tail) -> - [binary:part(Src, I, Run) | Tail]. diff --git a/apps/emqx_mysql/src/emqx_mysql.erl b/apps/emqx_mysql/src/emqx_mysql.erl index 927c9d067..e052b9b89 100644 --- a/apps/emqx_mysql/src/emqx_mysql.erl +++ b/apps/emqx_mysql/src/emqx_mysql.erl @@ -46,7 +46,7 @@ default_port => ?MYSQL_DEFAULT_PORT }). --type template() :: {unicode:chardata(), emqx_connector_template:str()}. +-type template() :: {unicode:chardata(), emqx_template:str()}. -type state() :: #{ pool_name := binary(), @@ -387,16 +387,16 @@ parse_prepare_sql(Config) -> #{query_templates => Templates}. parse_prepare_sql(Key, Query, Acc) -> - Template = emqx_connector_template_sql:parse_prepstmt(Query, #{parameters => '?'}), + Template = emqx_template_sql:parse_prepstmt(Query, #{parameters => '?'}), AccNext = Acc#{{Key, prepstmt} => Template}, parse_batch_sql(Key, Query, AccNext). parse_batch_sql(Key, Query, Acc) -> - case emqx_connector_sql:get_statement_type(Query) of + case emqx_utils_sql:get_statement_type(Query) of insert -> - case emqx_connector_sql:parse_insert(Query) of + case emqx_utils_sql:parse_insert(Query) of {ok, {Insert, Params}} -> - RowTemplate = emqx_connector_template_sql:parse(Params), + RowTemplate = emqx_template_sql:parse(Params), Acc#{{Key, batch} => {Insert, RowTemplate}}; {error, Reason} -> ?SLOG(error, #{ @@ -427,7 +427,7 @@ proc_sql_params(TypeOrKey, SQLOrData, Params, #{query_templates := Templates}) - {SQLOrData, Params}; {_InsertPart, RowTemplate} -> % NOTE: ignoring errors here, missing variables are set to `null`. - {Row, _Errors} = emqx_connector_template_sql:render_prepstmt(RowTemplate, SQLOrData), + {Row, _Errors} = emqx_template_sql:render_prepstmt(RowTemplate, SQLOrData), {TypeOrKey, Row} end. @@ -438,7 +438,7 @@ on_batch_insert(InstId, BatchReqs, {InsertPart, RowTemplate}, State) -> render_row(RowTemplate, Data) -> % NOTE: ignoring errors here, missing variables are set to "NULL". - {Row, _Errors} = emqx_connector_template_sql:render(RowTemplate, Data, #{escaping => mysql}), + {Row, _Errors} = emqx_template_sql:render(RowTemplate, Data, #{escaping => mysql}), Row. on_sql_query( diff --git a/apps/emqx_postgresql/src/emqx_postgresql.erl b/apps/emqx_postgresql/src/emqx_postgresql.erl index 71ba93b9b..3f7b43c79 100644 --- a/apps/emqx_postgresql/src/emqx_postgresql.erl +++ b/apps/emqx_postgresql/src/emqx_postgresql.erl @@ -52,7 +52,7 @@ default_port => ?PGSQL_DEFAULT_PORT }). --type template() :: {unicode:chardata(), emqx_connector_template_sql:row_template()}. +-type template() :: {unicode:chardata(), emqx_template_sql:row_template()}. -type state() :: #{ pool_name := binary(), @@ -428,12 +428,12 @@ parse_prepare_sql(Config) -> #{query_templates => Templates}. parse_prepare_sql(Key, Query, Acc) -> - Template = emqx_connector_template_sql:parse_prepstmt(Query, #{parameters => '$n'}), + Template = emqx_template_sql:parse_prepstmt(Query, #{parameters => '$n'}), Acc#{Key => Template}. render_prepare_sql_row(RowTemplate, Data) -> % NOTE: ignoring errors here, missing variables will be replaced with `null`. - {Row, _Errors} = emqx_connector_template_sql:render_prepstmt(RowTemplate, Data), + {Row, _Errors} = emqx_template_sql:render_prepstmt(RowTemplate, Data), Row. init_prepare(State = #{query_templates := Templates}) when map_size(Templates) == 0 -> diff --git a/apps/emqx_prometheus/src/emqx_prometheus.erl b/apps/emqx_prometheus/src/emqx_prometheus.erl index 41dec9ee9..a242931c4 100644 --- a/apps/emqx_prometheus/src/emqx_prometheus.erl +++ b/apps/emqx_prometheus/src/emqx_prometheus.erl @@ -114,8 +114,8 @@ handle_info(_Msg, State) -> push_to_push_gateway(Uri, Headers, JobName) when is_list(Headers) -> [Name, Ip] = string:tokens(atom_to_list(node()), "@"), % NOTE: allowing errors here to keep rough backward compatibility - {JobName1, Errors} = emqx_connector_template:render( - emqx_connector_template:parse(JobName), + {JobName1, Errors} = emqx_template:render( + emqx_template:parse(JobName), #{<<"name">> => Name, <<"host">> => Ip} ), _ = diff --git a/apps/emqx_rule_engine/src/emqx_rule_actions.erl b/apps/emqx_rule_engine/src/emqx_rule_actions.erl index 7473572c8..7a8b2520c 100644 --- a/apps/emqx_rule_engine/src/emqx_rule_actions.erl +++ b/apps/emqx_rule_engine/src/emqx_rule_actions.erl @@ -71,7 +71,7 @@ pre_process_action_args( ) -> Args#{ preprocessed_tmpl => #{ - topic => emqx_connector_template:parse(Topic), + topic => emqx_template:parse(Topic), qos => parse_vars(QoS), retain => parse_vars(Retain), payload => parse_payload(Payload), @@ -119,8 +119,8 @@ republish( } ) -> % NOTE: rendering missing bindings as string "undefined" - {TopicString, _Errors1} = emqx_connector_template:render(TopicTemplate, Selected), - {PayloadString, _Errors2} = emqx_connector_template:render(PayloadTemplate, Selected), + {TopicString, _Errors1} = emqx_template:render(TopicTemplate, Selected), + {PayloadString, _Errors2} = emqx_template:render(PayloadTemplate, Selected), Topic = iolist_to_binary(TopicString), Payload = iolist_to_binary(PayloadString), QoS = render_simple_var(QoSTemplate, Selected, 0), @@ -202,13 +202,13 @@ safe_publish(RuleId, Topic, QoS, Flags, Payload, PubProps) -> emqx_metrics:inc_msg(Msg). parse_vars(Data) when is_binary(Data) -> - emqx_connector_template:parse(Data); + emqx_template:parse(Data); parse_vars(Data) -> {const, Data}. parse_mqtt_properties(MQTTPropertiesTemplate) -> maps:map( - fun(_Key, V) -> emqx_connector_template:parse(V) end, + fun(_Key, V) -> emqx_template:parse(V) end, MQTTPropertiesTemplate ). @@ -220,13 +220,13 @@ parse_user_properties(<<"${pub_props.'User-Property'}">>) -> ?ORIGINAL_USER_PROPERTIES; parse_user_properties(<<"${", _/binary>> = V) -> %% use a variable - emqx_connector_template:parse(V); + emqx_template:parse(V); parse_user_properties(_) -> %% invalid, discard undefined. render_simple_var([{var, _Name, Accessor}], Data, Default) -> - case emqx_connector_template:lookup_var(Accessor, Data) of + case emqx_template:lookup_var(Accessor, Data) of {ok, Var} -> Var; %% cannot find the variable from Data {error, _} -> Default @@ -236,8 +236,8 @@ render_simple_var({const, Val}, _Data, _Default) -> parse_payload(Payload) -> case string:is_empty(Payload) of - false -> emqx_connector_template:parse(Payload); - true -> emqx_connector_template:parse("${.}") + false -> emqx_template:parse(Payload); + true -> emqx_template:parse("${.}") end. render_pub_props(UserPropertiesTemplate, Selected, Env) -> @@ -259,7 +259,7 @@ render_mqtt_properties(MQTTPropertiesTemplate, Selected, Env) -> fun(K, Template, Acc) -> try V = unicode:characters_to_binary( - emqx_connector_template:render_strict(Template, Selected) + emqx_template:render_strict(Template, Selected) ), Acc#{K => V} catch diff --git a/apps/emqx_connector/src/emqx_connector_template.erl b/apps/emqx_utils/src/emqx_template.erl similarity index 99% rename from apps/emqx_connector/src/emqx_connector_template.erl rename to apps/emqx_utils/src/emqx_template.erl index 619dbd6ec..deb25d807 100644 --- a/apps/emqx_connector/src/emqx_connector_template.erl +++ b/apps/emqx_utils/src/emqx_template.erl @@ -14,9 +14,7 @@ %% limitations under the License. %%-------------------------------------------------------------------- --module(emqx_connector_template). - --include_lib("emqx/include/emqx_placeholder.hrl"). +-module(emqx_template). -export([parse/1]). -export([parse/2]). @@ -76,6 +74,8 @@ var_trans => var_trans() }. +-define(PH_VAR_THIS, '$this'). + -define(RE_PLACEHOLDER, "\\$\\{[.]?([a-zA-Z0-9._]*)\\}"). -define(RE_ESCAPE, "\\$\\{(\\$)\\}"). diff --git a/apps/emqx_connector/src/emqx_connector_template_sql.erl b/apps/emqx_utils/src/emqx_template_sql.erl similarity index 77% rename from apps/emqx_connector/src/emqx_connector_template_sql.erl rename to apps/emqx_utils/src/emqx_template_sql.erl index 90d79415e..f215cd868 100644 --- a/apps/emqx_connector/src/emqx_connector_template_sql.erl +++ b/apps/emqx_utils/src/emqx_template_sql.erl @@ -14,7 +14,7 @@ %% limitations under the License. %%-------------------------------------------------------------------- --module(emqx_connector_template_sql). +-module(emqx_template_sql). -export([parse/1]). -export([parse/2]). @@ -27,15 +27,15 @@ -export_type([row_template/0]). --type template() :: emqx_connector_template:t(). --type row_template() :: [emqx_connector_template:placeholder()]. --type bindings() :: emqx_connector_template:bindings(). +-type template() :: emqx_template:t(). +-type row_template() :: [emqx_template:placeholder()]. +-type bindings() :: emqx_template:bindings(). --type values() :: [emqx_connector_sql:value()]. +-type values() :: [emqx_utils_sql:value()]. -type parse_opts() :: #{ parameters => '$n' | ':n' | '?', - % Inherited from `emqx_connector_template:parse_opts()` + % Inherited from `emqx_template:parse_opts()` strip_double_quote => boolean() }. @@ -57,7 +57,7 @@ parse(String) -> -spec parse(unicode:chardata(), parse_opts()) -> template(). parse(String, Opts) -> - emqx_connector_template:parse(String, Opts). + emqx_template:parse(String, Opts). %% @doc Render an SQL statement template given a set of bindings. %% Interpolation generally follows the SQL syntax, strings are escaped according to the @@ -65,8 +65,8 @@ parse(String, Opts) -> -spec render(template(), bindings(), render_opts()) -> {unicode:chardata(), [_Error]}. render(Template, Bindings, Opts) -> - emqx_connector_template:render(Template, Bindings, #{ - var_trans => fun(Value) -> emqx_connector_sql:to_sql_string(Value, Opts) end + emqx_template:render(Template, Bindings, #{ + var_trans => fun(Value) -> emqx_utils_sql:to_sql_string(Value, Opts) end }). %% @doc Render an SQL statement template given a set of bindings. @@ -74,8 +74,8 @@ render(Template, Bindings, Opts) -> -spec render_strict(template(), bindings(), render_opts()) -> unicode:chardata(). render_strict(Template, Bindings, Opts) -> - emqx_connector_template:render_strict(Template, Bindings, #{ - var_trans => fun(Value) -> emqx_connector_sql:to_sql_string(Value, Opts) end + emqx_template:render_strict(Template, Bindings, #{ + var_trans => fun(Value) -> emqx_utils_sql:to_sql_string(Value, Opts) end }). %% @doc Parse an SQL statement string into a prepared statement and a row template. @@ -83,7 +83,7 @@ render_strict(Template, Bindings, Opts) -> %% during the execution of the prepared statement. %% Example: %% ``` -%% {Statement, RowTemplate} = emqx_connector_template_sql:parse_prepstmt( +%% {Statement, RowTemplate} = emqx_template_sql:parse_prepstmt( %% "INSERT INTO table (id, name, age) VALUES (${id}, ${name}, 42)", %% #{parameters => '$n'} %% ), @@ -93,7 +93,7 @@ render_strict(Template, Bindings, Opts) -> -spec parse_prepstmt(unicode:chardata(), parse_opts()) -> {unicode:chardata(), row_template()}. parse_prepstmt(String, Opts) -> - Template = emqx_connector_template:parse(String, maps:with(?TEMPLATE_PARSE_OPTS, Opts)), + Template = emqx_template:parse(String, maps:with(?TEMPLATE_PARSE_OPTS, Opts)), Statement = mk_prepared_statement(Template, Opts), Placeholders = [Placeholder || Placeholder <- Template, element(1, Placeholder) == var], {Statement, Placeholders}. @@ -123,15 +123,15 @@ mk_replace(':n', N) -> %% @doc Render a row template into a list of SQL values. %% An _SQL value_ is a vaguely defined concept here, it is something that's considered %% compatible with the protocol of the database being used. See the definition of -%% `emqx_connector_sql:value()` for more details. +%% `emqx_utils_sql:value()` for more details. -spec render_prepstmt(template(), bindings()) -> {values(), [_Error]}. render_prepstmt(Template, Bindings) -> - Opts = #{var_trans => fun emqx_connector_sql:to_sql_value/1}, - emqx_connector_template:render(Template, Bindings, Opts). + Opts = #{var_trans => fun emqx_utils_sql:to_sql_value/1}, + emqx_template:render(Template, Bindings, Opts). -spec render_prepstmt_strict(template(), bindings()) -> values(). render_prepstmt_strict(Template, Bindings) -> - Opts = #{var_trans => fun emqx_connector_sql:to_sql_value/1}, - emqx_connector_template:render_strict(Template, Bindings, Opts). + Opts = #{var_trans => fun emqx_utils_sql:to_sql_value/1}, + emqx_template:render_strict(Template, Bindings, Opts). diff --git a/apps/emqx_utils/src/emqx_utils_sql.erl b/apps/emqx_utils/src/emqx_utils_sql.erl index 3caed6b62..12aac6464 100644 --- a/apps/emqx_utils/src/emqx_utils_sql.erl +++ b/apps/emqx_utils/src/emqx_utils_sql.erl @@ -80,7 +80,7 @@ to_sql_value(Map) when is_map(Map) -> emqx_utils_json:encode(Map). %% @doc Convert an Erlang term to a string that can be interpolated in literal %% SQL statements. The value is escaped if necessary. --spec to_sql_string(term(), Options) -> iodata() when +-spec to_sql_string(term(), Options) -> unicode:chardata() when Options :: #{ escaping => cql | mysql | sql }. @@ -98,7 +98,9 @@ to_sql_string(Term, #{escaping := cql}) -> to_sql_string(Term, #{}) -> maybe_escape(Term, fun escape_sql/1). --spec maybe_escape(_Value, fun((binary()) -> iodata())) -> iodata(). +-spec maybe_escape(_Value, fun((binary()) -> iodata())) -> unicode:chardata(). +maybe_escape(undefined, _EscapeFun) -> + <<"NULL">>; maybe_escape(Str, EscapeFun) when is_binary(Str) -> EscapeFun(Str); maybe_escape(Str, EscapeFun) when is_list(Str) -> @@ -109,9 +111,9 @@ maybe_escape(Str, EscapeFun) when is_list(Str) -> error(Otherwise) end; maybe_escape(Val, EscapeFun) when is_atom(Val) orelse is_map(Val) -> - EscapeFun(emqx_utils_conv:bin(Val)); + EscapeFun(emqx_template:to_string(Val)); maybe_escape(Val, _EscapeFun) -> - emqx_utils_conv:bin(Val). + emqx_template:to_string(Val). -spec escape_sql(binary()) -> iodata(). escape_sql(S) -> diff --git a/apps/emqx_connector/test/emqx_connector_template_SUITE.erl b/apps/emqx_utils/test/emqx_template_SUITE.erl similarity index 69% rename from apps/emqx_connector/test/emqx_connector_template_SUITE.erl rename to apps/emqx_utils/test/emqx_template_SUITE.erl index 3700caa96..657c3c94f 100644 --- a/apps/emqx_connector/test/emqx_connector_template_SUITE.erl +++ b/apps/emqx_utils/test/emqx_template_SUITE.erl @@ -14,7 +14,7 @@ %% limitations under the License. %%-------------------------------------------------------------------- --module(emqx_connector_template_SUITE). +-module(emqx_template_SUITE). -compile(export_all). -compile(nowarn_export_all). @@ -33,7 +33,7 @@ t_render(_) -> l => [0, 1, 1000], u => "utf-8 is วษนวษฅ" }, - Template = emqx_connector_template:parse( + Template = emqx_template:parse( <<"a:${a},b:${b},c:${c},d:${d},d1:${d.d1},l:${l},u:${u}">> ), ?assertEqual( @@ -43,8 +43,8 @@ t_render(_) -> t_render_var_trans(_) -> Bindings = #{a => <<"1">>, b => 1, c => #{prop => 1.0}}, - Template = emqx_connector_template:parse(<<"a:${a},b:${b},c:${c.prop}">>), - {String, Errors} = emqx_connector_template:render( + Template = emqx_template:parse(<<"a:${a},b:${b},c:${c.prop}">>), + {String, Errors} = emqx_template:render( Template, Bindings, #{var_trans => fun(Name, _) -> "<" ++ Name ++ ">" end} @@ -56,10 +56,10 @@ t_render_var_trans(_) -> t_render_path(_) -> Bindings = #{d => #{d1 => <<"hi">>}}, - Template = emqx_connector_template:parse(<<"d.d1:${d.d1}">>), + Template = emqx_template:parse(<<"d.d1:${d.d1}">>), ?assertEqual( ok, - emqx_connector_template:validate(["d.d1"], Template) + emqx_template:validate(["d.d1"], Template) ), ?assertEqual( {<<"d.d1:hi">>, []}, @@ -68,10 +68,10 @@ t_render_path(_) -> t_render_custom_ph(_) -> Bindings = #{a => <<"a">>, b => <<"b">>}, - Template = emqx_connector_template:parse(<<"a:${a},b:${b}">>), + Template = emqx_template:parse(<<"a:${a},b:${b}">>), ?assertEqual( {error, [{"b", disallowed}]}, - emqx_connector_template:validate(["a"], Template) + emqx_template:validate(["a"], Template) ), ?assertEqual( <<"a:a,b:b">>, @@ -80,8 +80,8 @@ t_render_custom_ph(_) -> t_render_this(_) -> Bindings = #{a => <<"a">>, b => [1, 2, 3]}, - Template = emqx_connector_template:parse(<<"this:${} / also:${.}">>), - ?assertEqual(ok, emqx_connector_template:validate(["."], Template)), + Template = emqx_template:parse(<<"this:${} / also:${.}">>), + ?assertEqual(ok, emqx_template:validate(["."], Template)), ?assertEqual( % NOTE: order of the keys in the JSON object depends on the JSON encoder <<"this:{\"b\":[1,2,3],\"a\":\"a\"} / also:{\"b\":[1,2,3],\"a\":\"a\"}">>, @@ -90,7 +90,7 @@ t_render_this(_) -> t_render_missing_bindings(_) -> Bindings = #{no => #{}}, - Template = emqx_connector_template:parse( + Template = emqx_template:parse( <<"a:${a},b:${b},c:${c},d:${d.d1},e:${no.such_atom_i_swear}">> ), ?assertEqual( @@ -116,33 +116,33 @@ t_render_missing_bindings(_) -> t_unparse(_) -> TString = <<"a:${a},b:${b},c:$${c},d:{${d.d1}},e:${$}{e},lit:${$}{$}">>, - Template = emqx_connector_template:parse(TString), + Template = emqx_template:parse(TString), ?assertEqual( TString, - unicode:characters_to_binary(emqx_connector_template:unparse(Template)) + unicode:characters_to_binary(emqx_template:unparse(Template)) ). t_const(_) -> ?assertEqual( true, - emqx_connector_template:is_const(emqx_connector_template:parse(<<"">>)) + emqx_template:is_const(emqx_template:parse(<<"">>)) ), ?assertEqual( false, - emqx_connector_template:is_const( - emqx_connector_template:parse(<<"a:${a},b:${b},c:${$}{c}">>) + emqx_template:is_const( + emqx_template:parse(<<"a:${a},b:${b},c:${$}{c}">>) ) ), ?assertEqual( true, - emqx_connector_template:is_const( - emqx_connector_template:parse(<<"a:${$}{a},b:${$}{b}">>) + emqx_template:is_const( + emqx_template:parse(<<"a:${$}{a},b:${$}{b}">>) ) ). t_render_partial_ph(_) -> Bindings = #{a => <<"1">>, b => 1, c => 1.0, d => #{d1 => <<"hi">>}}, - Template = emqx_connector_template:parse(<<"a:$a,b:b},c:{c},d:${d">>), + Template = emqx_template:parse(<<"a:$a,b:b},c:{c},d:${d">>), ?assertEqual( <<"a:$a,b:b},c:{c},d:${d">>, render_strict_string(Template, Bindings) @@ -150,7 +150,7 @@ t_render_partial_ph(_) -> t_parse_escaped(_) -> Bindings = #{a => <<"1">>, b => 1, c => "VAR"}, - Template = emqx_connector_template:parse(<<"a:${a},b:${$}{b},c:${$}{${c}},lit:${$}{$}">>), + Template = emqx_template:parse(<<"a:${a},b:${$}{b},c:${$}{${c}},lit:${$}{$}">>), ?assertEqual( <<"a:1,b:${b},c:${VAR},lit:${$}">>, render_strict_string(Template, Bindings) @@ -158,7 +158,7 @@ t_parse_escaped(_) -> t_parse_escaped_dquote(_) -> Bindings = #{a => <<"1">>, b => 1}, - Template = emqx_connector_template:parse(<<"a:\"${a}\",b:\"${$}{b}\"">>, #{ + Template = emqx_template:parse(<<"a:\"${a}\",b:\"${$}{b}\"">>, #{ strip_double_quote => true }), ?assertEqual( @@ -169,30 +169,30 @@ t_parse_escaped_dquote(_) -> t_parse_sql_prepstmt(_) -> Bindings = #{a => <<"1">>, b => 1, c => 1.0, d => #{d1 => <<"hi">>}}, {PrepareStatement, RowTemplate} = - emqx_connector_template_sql:parse_prepstmt(<<"a:${a},b:${b},c:${c},d:${d}">>, #{ + emqx_template_sql:parse_prepstmt(<<"a:${a},b:${b},c:${c},d:${d}">>, #{ parameters => '?' }), ?assertEqual(<<"a:?,b:?,c:?,d:?">>, bin(PrepareStatement)), ?assertEqual( {[<<"1">>, 1, 1.0, <<"{\"d1\":\"hi\"}">>], _Errors = []}, - emqx_connector_template_sql:render_prepstmt(RowTemplate, Bindings) + emqx_template_sql:render_prepstmt(RowTemplate, Bindings) ). t_parse_sql_prepstmt_n(_) -> Bindings = #{a => undefined, b => true, c => atom, d => #{d1 => 42.1337}}, {PrepareStatement, RowTemplate} = - emqx_connector_template_sql:parse_prepstmt(<<"a:${a},b:${b},c:${c},d:${d}">>, #{ + emqx_template_sql:parse_prepstmt(<<"a:${a},b:${b},c:${c},d:${d}">>, #{ parameters => '$n' }), ?assertEqual(<<"a:$1,b:$2,c:$3,d:$4">>, bin(PrepareStatement)), ?assertEqual( [null, true, <<"atom">>, <<"{\"d1\":42.1337}">>], - emqx_connector_template_sql:render_prepstmt_strict(RowTemplate, Bindings) + emqx_template_sql:render_prepstmt_strict(RowTemplate, Bindings) ). t_parse_sql_prepstmt_colon(_) -> {PrepareStatement, _RowTemplate} = - emqx_connector_template_sql:parse_prepstmt(<<"a=${a},b=${b},c=${c},d=${d}">>, #{ + emqx_template_sql:parse_prepstmt(<<"a=${a},b=${b},c=${c},d=${d}">>, #{ parameters => ':n' }), ?assertEqual(<<"a=:1,b=:2,c=:3,d=:4">>, bin(PrepareStatement)). @@ -200,9 +200,9 @@ t_parse_sql_prepstmt_colon(_) -> t_parse_sql_prepstmt_partial_ph(_) -> Bindings = #{a => <<"1">>, b => 1, c => 1.0, d => #{d1 => <<"hi">>}}, {PrepareStatement, RowTemplate} = - emqx_connector_template_sql:parse_prepstmt(<<"a:$a,b:b},c:{c},d:${d">>, #{parameters => '?'}), + emqx_template_sql:parse_prepstmt(<<"a:$a,b:b},c:{c},d:${d">>, #{parameters => '?'}), ?assertEqual(<<"a:$a,b:b},c:{c},d:${d">>, bin(PrepareStatement)), - ?assertEqual([], emqx_connector_template_sql:render_prepstmt_strict(RowTemplate, Bindings)). + ?assertEqual([], emqx_template_sql:render_prepstmt_strict(RowTemplate, Bindings)). t_render_sql(_) -> Bindings = #{ @@ -213,14 +213,14 @@ t_render_sql(_) -> n => undefined, u => "utf8's cool ๐Ÿธ" }, - Template = emqx_connector_template:parse(<<"a:${a},b:${b},c:${c},d:${d},n:${n},u:${u}">>), + Template = emqx_template:parse(<<"a:${a},b:${b},c:${c},d:${d},n:${n},u:${u}">>), ?assertMatch( {_String, _Errors = []}, - emqx_connector_template_sql:render(Template, Bindings, #{}) + emqx_template_sql:render(Template, Bindings, #{}) ), ?assertEqual( <<"a:'1',b:1,c:1.0,d:'{\"d1\":\"hi\"}',n:NULL,u:'utf8\\'s cool ๐Ÿธ'"/utf8>>, - bin(emqx_connector_template_sql:render_strict(Template, Bindings, #{})) + bin(emqx_template_sql:render_strict(Template, Bindings, #{})) ). t_render_mysql(_) -> @@ -236,7 +236,7 @@ t_render_mysql(_) -> g => "utf8's cool ๐Ÿธ", h => imgood }, - Template = emqx_connector_template_sql:parse( + Template = emqx_template_sql:parse( <<"a:${a},b:${b},c:${c},d:${d},e:${e},f:${f},g:${g},h:${h}">> ), ?assertEqual( @@ -245,7 +245,7 @@ t_render_mysql(_) -> "e:'\\\\\\0๐Ÿ’ฉ',f:0x6E6F6E2D75746638DCC900,g:'utf8\\'s cool ๐Ÿธ',"/utf8, "h:'imgood'" >>, - bin(emqx_connector_template_sql:render_strict(Template, Bindings, #{escaping => mysql})) + bin(emqx_template_sql:render_strict(Template, Bindings, #{escaping => mysql})) ). t_render_cql(_) -> @@ -257,18 +257,18 @@ t_render_cql(_) -> c => 1.0, d => #{d1 => <<"someone's phone">>} }, - Template = emqx_connector_template:parse(<<"a:${a},b:${b},c:${c},d:${d}">>), + Template = emqx_template:parse(<<"a:${a},b:${b},c:${c},d:${d}">>), ?assertEqual( <<"a:'1''''2',b:1,c:1.0,d:'{\"d1\":\"someone''s phone\"}'">>, - bin(emqx_connector_template_sql:render_strict(Template, Bindings, #{escaping => cql})) + bin(emqx_template_sql:render_strict(Template, Bindings, #{escaping => cql})) ). t_render_sql_custom_ph(_) -> {PrepareStatement, RowTemplate} = - emqx_connector_template_sql:parse_prepstmt(<<"a:${a},b:${b.c}">>, #{parameters => '$n'}), + emqx_template_sql:parse_prepstmt(<<"a:${a},b:${b.c}">>, #{parameters => '$n'}), ?assertEqual( {error, [{"b.c", disallowed}]}, - emqx_connector_template:validate(["a"], RowTemplate) + emqx_template:validate(["a"], RowTemplate) ), ?assertEqual(<<"a:$1,b:$2">>, bin(PrepareStatement)). @@ -276,57 +276,57 @@ t_render_sql_strip_double_quote(_) -> Bindings = #{a => <<"a">>, b => <<"b">>}, %% no strip_double_quote option: "${key}" -> "value" - {PrepareStatement1, RowTemplate1} = emqx_connector_template_sql:parse_prepstmt( + {PrepareStatement1, RowTemplate1} = emqx_template_sql:parse_prepstmt( <<"a:\"${a}\",b:\"${b}\"">>, #{parameters => '$n'} ), ?assertEqual(<<"a:\"$1\",b:\"$2\"">>, bin(PrepareStatement1)), ?assertEqual( [<<"a">>, <<"b">>], - emqx_connector_template_sql:render_prepstmt_strict(RowTemplate1, Bindings) + emqx_template_sql:render_prepstmt_strict(RowTemplate1, Bindings) ), %% strip_double_quote = true: "${key}" -> value - {PrepareStatement2, RowTemplate2} = emqx_connector_template_sql:parse_prepstmt( + {PrepareStatement2, RowTemplate2} = emqx_template_sql:parse_prepstmt( <<"a:\"${a}\",b:\"${b}\"">>, #{parameters => '$n', strip_double_quote => true} ), ?assertEqual(<<"a:$1,b:$2">>, bin(PrepareStatement2)), ?assertEqual( [<<"a">>, <<"b">>], - emqx_connector_template_sql:render_prepstmt_strict(RowTemplate2, Bindings) + emqx_template_sql:render_prepstmt_strict(RowTemplate2, Bindings) ). t_render_tmpl_deep(_) -> Bindings = #{a => <<"1">>, b => 1, c => 1.0, d => #{d1 => <<"hi">>}}, - Template = emqx_connector_template:parse_deep( + Template = emqx_template:parse_deep( #{<<"${a}">> => [<<"$${b}">>, "c", 2, 3.0, '${d}', {[<<"${c}">>, <<"${$}{d}">>], 0}]} ), ?assertEqual( {error, [{V, disallowed} || V <- ["b", "c"]]}, - emqx_connector_template:validate(["a"], Template) + emqx_template:validate(["a"], Template) ), ?assertEqual( #{<<"1">> => [<<"$1">>, "c", 2, 3.0, '${d}', {[<<"1.0">>, <<"${d}">>], 0}]}, - emqx_connector_template:render_strict(Template, Bindings) + emqx_template:render_strict(Template, Bindings) ). t_unparse_tmpl_deep(_) -> Term = #{<<"${a}">> => [<<"$${b}">>, "c", 2, 3.0, '${d}', {[<<"${c}">>], <<"${$}{d}">>, 0}]}, - Template = emqx_connector_template:parse_deep(Term), - ?assertEqual(Term, emqx_connector_template:unparse(Template)). + Template = emqx_template:parse_deep(Term), + ?assertEqual(Term, emqx_template:unparse(Template)). %% render_string(Template, Bindings) -> - {String, Errors} = emqx_connector_template:render(Template, Bindings), + {String, Errors} = emqx_template:render(Template, Bindings), {bin(String), Errors}. render_strict_string(Template, Bindings) -> - bin(emqx_connector_template:render_strict(Template, Bindings)). + bin(emqx_template:render_strict(Template, Bindings)). bin(String) -> unicode:characters_to_binary(String).