Merge pull request #13453 from thalesmg/20240711-r57-mt-fixes
batch of message transformation fixes
This commit is contained in:
commit
cd8bf2725a
|
@ -62,9 +62,11 @@
|
||||||
node := _,
|
node := _,
|
||||||
payload := _,
|
payload := _,
|
||||||
peername := _,
|
peername := _,
|
||||||
|
pub_props := _,
|
||||||
publish_received_at := _,
|
publish_received_at := _,
|
||||||
qos := _,
|
qos := _,
|
||||||
retain := _,
|
retain := _,
|
||||||
|
timestamp := _,
|
||||||
topic := _,
|
topic := _,
|
||||||
user_property := _,
|
user_property := _,
|
||||||
username := _,
|
username := _,
|
||||||
|
@ -345,6 +347,7 @@ message_to_context(#message{} = Message, Payload, Transformation) ->
|
||||||
undefined
|
undefined
|
||||||
end,
|
end,
|
||||||
Username = maps:get(username, Headers, undefined),
|
Username = maps:get(username, Headers, undefined),
|
||||||
|
Timestamp = erlang:system_time(millisecond),
|
||||||
#{
|
#{
|
||||||
dirty => Dirty,
|
dirty => Dirty,
|
||||||
|
|
||||||
|
@ -355,9 +358,11 @@ message_to_context(#message{} = Message, Payload, Transformation) ->
|
||||||
node => node(),
|
node => node(),
|
||||||
payload => Payload,
|
payload => Payload,
|
||||||
peername => Peername,
|
peername => Peername,
|
||||||
|
pub_props => Props,
|
||||||
publish_received_at => Message#message.timestamp,
|
publish_received_at => Message#message.timestamp,
|
||||||
qos => Message#message.qos,
|
qos => Message#message.qos,
|
||||||
retain => emqx_message:get_flag(retain, Message, false),
|
retain => emqx_message:get_flag(retain, Message, false),
|
||||||
|
timestamp => Timestamp,
|
||||||
topic => Message#message.topic,
|
topic => Message#message.topic,
|
||||||
user_property => UserProperties,
|
user_property => UserProperties,
|
||||||
username => Username
|
username => Username
|
||||||
|
@ -462,6 +467,17 @@ decode(
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{error, TraceFailureContext};
|
{error, TraceFailureContext};
|
||||||
|
throw:{schema_decode_error, ExtraContext} ->
|
||||||
|
TraceFailureContext = #trace_failure_context{
|
||||||
|
transformation = Transformation,
|
||||||
|
tag = "payload_decode_error",
|
||||||
|
context = ExtraContext#{
|
||||||
|
decoder => protobuf,
|
||||||
|
schema_name => SerdeName,
|
||||||
|
message_type => MessageType
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{error, TraceFailureContext};
|
||||||
Class:Error:Stacktrace ->
|
Class:Error:Stacktrace ->
|
||||||
TraceFailureContext = #trace_failure_context{
|
TraceFailureContext = #trace_failure_context{
|
||||||
transformation = Transformation,
|
transformation = Transformation,
|
||||||
|
|
|
@ -108,8 +108,7 @@ fields(transformation) ->
|
||||||
hoconsc:array(ref(operation)),
|
hoconsc:array(ref(operation)),
|
||||||
#{
|
#{
|
||||||
desc => ?DESC("operation"),
|
desc => ?DESC("operation"),
|
||||||
required => true,
|
default => []
|
||||||
validator => fun validate_operations/1
|
|
||||||
}
|
}
|
||||||
)}
|
)}
|
||||||
];
|
];
|
||||||
|
@ -253,11 +252,6 @@ validate_unique_topics(Topics) ->
|
||||||
{error, Msg}
|
{error, Msg}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
validate_operations([]) ->
|
|
||||||
{error, <<"at least one operation must be defined">>};
|
|
||||||
validate_operations([_ | _]) ->
|
|
||||||
ok.
|
|
||||||
|
|
||||||
compile_variform(Expression, #{make_serializable := true}) ->
|
compile_variform(Expression, #{make_serializable := true}) ->
|
||||||
case is_binary(Expression) of
|
case is_binary(Expression) of
|
||||||
true ->
|
true ->
|
||||||
|
|
|
@ -504,7 +504,7 @@ assert_monitor_metrics() ->
|
||||||
receive
|
receive
|
||||||
PATTERN = ____Msg0 -> ____Msg0
|
PATTERN = ____Msg0 -> ____Msg0
|
||||||
after TIMEOUT ->
|
after TIMEOUT ->
|
||||||
error({message_not_received, ?LINE})
|
error({message_not_received, {line, ?LINE}})
|
||||||
end
|
end
|
||||||
end)()
|
end)()
|
||||||
).
|
).
|
||||||
|
@ -608,6 +608,8 @@ t_smoke_test(_Config) ->
|
||||||
%% * peername
|
%% * peername
|
||||||
%% * publish_received_at
|
%% * publish_received_at
|
||||||
%% * username
|
%% * username
|
||||||
|
%% * timestamp
|
||||||
|
%% * pub_props (and specific fields within containing hyphens)
|
||||||
t_smoke_test_2(_Config) ->
|
t_smoke_test_2(_Config) ->
|
||||||
Name1 = <<"foo">>,
|
Name1 = <<"foo">>,
|
||||||
Operations = [
|
Operations = [
|
||||||
|
@ -617,14 +619,22 @@ t_smoke_test_2(_Config) ->
|
||||||
operation(<<"payload.peername">>, <<"peername">>),
|
operation(<<"payload.peername">>, <<"peername">>),
|
||||||
operation(<<"payload.publish_received_at">>, <<"publish_received_at">>),
|
operation(<<"payload.publish_received_at">>, <<"publish_received_at">>),
|
||||||
operation(<<"payload.username">>, <<"username">>),
|
operation(<<"payload.username">>, <<"username">>),
|
||||||
operation(<<"payload.flags">>, <<"flags">>)
|
operation(<<"payload.flags">>, <<"flags">>),
|
||||||
|
operation(<<"payload.timestamp">>, <<"timestamp">>),
|
||||||
|
operation(<<"payload.pub_props">>, <<"pub_props">>),
|
||||||
|
operation(<<"payload.content_type">>, <<"pub_props.Content-Type">>)
|
||||||
],
|
],
|
||||||
Transformation1 = transformation(Name1, Operations),
|
Transformation1 = transformation(Name1, Operations),
|
||||||
{201, _} = insert(Transformation1),
|
{201, _} = insert(Transformation1),
|
||||||
ClientId = atom_to_binary(?FUNCTION_NAME),
|
ClientId = atom_to_binary(?FUNCTION_NAME),
|
||||||
C1 = connect(ClientId),
|
C1 = connect(ClientId),
|
||||||
{ok, _, [_]} = emqtt:subscribe(C1, <<"t/#">>, [{qos, 2}]),
|
{ok, _, [_]} = emqtt:subscribe(C1, <<"t/#">>, [{qos, 2}]),
|
||||||
ok = publish(C1, <<"t/1">>, #{}),
|
ok = publish(C1, <<"t/1">>, #{}, _QoS = 0, #{
|
||||||
|
props => #{
|
||||||
|
'Content-Type' => <<"application/json">>,
|
||||||
|
'User-Property' => [{<<"a">>, <<"b">>}]
|
||||||
|
}
|
||||||
|
}),
|
||||||
{publish, #{payload := Payload0}} = ?assertReceiveReturn({publish, _}, 1_000),
|
{publish, #{payload := Payload0}} = ?assertReceiveReturn({publish, _}, 1_000),
|
||||||
NodeBin = atom_to_binary(node()),
|
NodeBin = atom_to_binary(node()),
|
||||||
?assertMatch(
|
?assertMatch(
|
||||||
|
@ -635,7 +645,13 @@ t_smoke_test_2(_Config) ->
|
||||||
<<"peername">> := <<"127.0.0.1:", _/binary>>,
|
<<"peername">> := <<"127.0.0.1:", _/binary>>,
|
||||||
<<"publish_received_at">> := PRAt,
|
<<"publish_received_at">> := PRAt,
|
||||||
<<"username">> := <<"undefined">>,
|
<<"username">> := <<"undefined">>,
|
||||||
<<"flags">> := #{<<"dup">> := false, <<"retain">> := false}
|
<<"flags">> := #{<<"dup">> := false, <<"retain">> := false},
|
||||||
|
<<"timestamp">> := _,
|
||||||
|
<<"pub_props">> := #{
|
||||||
|
<<"Content-Type">> := <<"application/json">>,
|
||||||
|
<<"User-Property">> := #{<<"a">> := <<"b">>}
|
||||||
|
},
|
||||||
|
<<"content_type">> := <<"application/json">>
|
||||||
} when is_integer(PRAt),
|
} when is_integer(PRAt),
|
||||||
emqx_utils_json:decode(Payload0, [return_maps])
|
emqx_utils_json:decode(Payload0, [return_maps])
|
||||||
),
|
),
|
||||||
|
@ -644,7 +660,12 @@ t_smoke_test_2(_Config) ->
|
||||||
Username = <<"myusername">>,
|
Username = <<"myusername">>,
|
||||||
C2 = connect(ClientId, _IsPersistent = false, #{start_props => #{username => Username}}),
|
C2 = connect(ClientId, _IsPersistent = false, #{start_props => #{username => Username}}),
|
||||||
{ok, _, [_]} = emqtt:subscribe(C2, <<"t/#">>, [{qos, 2}]),
|
{ok, _, [_]} = emqtt:subscribe(C2, <<"t/#">>, [{qos, 2}]),
|
||||||
ok = publish(C2, <<"t/1">>, #{}),
|
ok = publish(C2, <<"t/1">>, #{}, _QoS = 0, #{
|
||||||
|
props => #{
|
||||||
|
'Content-Type' => <<"application/json">>,
|
||||||
|
'User-Property' => [{<<"a">>, <<"b">>}]
|
||||||
|
}
|
||||||
|
}),
|
||||||
{publish, #{payload := Payload1}} = ?assertReceiveReturn({publish, _}, 1_000),
|
{publish, #{payload := Payload1}} = ?assertReceiveReturn({publish, _}, 1_000),
|
||||||
?assertMatch(
|
?assertMatch(
|
||||||
#{
|
#{
|
||||||
|
@ -654,7 +675,13 @@ t_smoke_test_2(_Config) ->
|
||||||
<<"peername">> := <<"127.0.0.1:", _/binary>>,
|
<<"peername">> := <<"127.0.0.1:", _/binary>>,
|
||||||
<<"publish_received_at">> := PRAt,
|
<<"publish_received_at">> := PRAt,
|
||||||
<<"username">> := Username,
|
<<"username">> := Username,
|
||||||
<<"flags">> := #{<<"dup">> := false, <<"retain">> := false}
|
<<"flags">> := #{<<"dup">> := false, <<"retain">> := false},
|
||||||
|
<<"timestamp">> := _,
|
||||||
|
<<"pub_props">> := #{
|
||||||
|
<<"Content-Type">> := <<"application/json">>,
|
||||||
|
<<"User-Property">> := #{<<"a">> := <<"b">>}
|
||||||
|
},
|
||||||
|
<<"content_type">> := <<"application/json">>
|
||||||
} when is_integer(PRAt),
|
} when is_integer(PRAt),
|
||||||
emqx_utils_json:decode(Payload1, [return_maps])
|
emqx_utils_json:decode(Payload1, [return_maps])
|
||||||
),
|
),
|
||||||
|
@ -1367,6 +1394,94 @@ t_protobuf(_Config) ->
|
||||||
|
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
|
%% Checks what happens if a wrong transformation chain is used. In this case, the second
|
||||||
|
%% transformation attempts to protobuf-decode a message that was already decoded but not
|
||||||
|
%% re-encoded by the first transformation.
|
||||||
|
t_protobuf_bad_chain(_Config) ->
|
||||||
|
?check_trace(
|
||||||
|
begin
|
||||||
|
SerdeName = <<"myserde">>,
|
||||||
|
MessageType = <<"Person">>,
|
||||||
|
protobuf_create_serde(SerdeName),
|
||||||
|
|
||||||
|
Name1 = <<"foo">>,
|
||||||
|
PayloadSerde = #{
|
||||||
|
<<"type">> => <<"protobuf">>,
|
||||||
|
<<"schema">> => SerdeName,
|
||||||
|
<<"message_type">> => MessageType
|
||||||
|
},
|
||||||
|
NoneSerde = #{<<"type">> => <<"none">>},
|
||||||
|
JSONSerde = #{<<"type">> => <<"json">>},
|
||||||
|
|
||||||
|
Transformation1 = transformation(Name1, _Ops1 = [], #{
|
||||||
|
<<"payload_decoder">> => PayloadSerde,
|
||||||
|
<<"payload_encoder">> => NoneSerde
|
||||||
|
}),
|
||||||
|
{201, _} = insert(Transformation1),
|
||||||
|
|
||||||
|
%% WRONG: after the first transformation, payload is already decoded, so we
|
||||||
|
%% shouldn't use protobuf again.
|
||||||
|
Name2 = <<"bar">>,
|
||||||
|
Transformation2A = transformation(Name2, [], #{
|
||||||
|
<<"payload_decoder">> => PayloadSerde,
|
||||||
|
<<"payload_encoder">> => JSONSerde
|
||||||
|
}),
|
||||||
|
{201, _} = insert(Transformation2A),
|
||||||
|
|
||||||
|
C = connect(<<"c1">>),
|
||||||
|
{ok, _, [_]} = emqtt:subscribe(C, <<"t/#">>),
|
||||||
|
|
||||||
|
[Payload | _] = protobuf_valid_payloads(SerdeName, MessageType),
|
||||||
|
ok = publish(C, <<"t/1">>, {raw, Payload}),
|
||||||
|
?assertNotReceive({publish, _}),
|
||||||
|
|
||||||
|
ok
|
||||||
|
end,
|
||||||
|
fun(Trace) ->
|
||||||
|
ct:pal("trace:\n ~p", [Trace]),
|
||||||
|
?assertMatch(
|
||||||
|
[],
|
||||||
|
[
|
||||||
|
E
|
||||||
|
|| #{
|
||||||
|
?snk_kind := message_transformation_failed,
|
||||||
|
message := "payload_decode_schema_failure",
|
||||||
|
reason := function_clause
|
||||||
|
} = E <- Trace
|
||||||
|
]
|
||||||
|
),
|
||||||
|
%% No unexpected crashes
|
||||||
|
?assertMatch(
|
||||||
|
[],
|
||||||
|
[
|
||||||
|
E
|
||||||
|
|| #{
|
||||||
|
?snk_kind := message_transformation_failed,
|
||||||
|
stacktrace := _
|
||||||
|
} = E <- Trace
|
||||||
|
]
|
||||||
|
),
|
||||||
|
?assertMatch(
|
||||||
|
[
|
||||||
|
#{
|
||||||
|
explain :=
|
||||||
|
<<"Attempted to schema decode an already decoded message.", _/binary>>
|
||||||
|
}
|
||||||
|
| _
|
||||||
|
],
|
||||||
|
[
|
||||||
|
E
|
||||||
|
|| #{
|
||||||
|
?snk_kind := message_transformation_failed,
|
||||||
|
message := "payload_decode_error"
|
||||||
|
} = E <- Trace
|
||||||
|
]
|
||||||
|
),
|
||||||
|
ok
|
||||||
|
end
|
||||||
|
),
|
||||||
|
ok.
|
||||||
|
|
||||||
%% Tests that restoring a backup config works.
|
%% Tests that restoring a backup config works.
|
||||||
%% * Existing transformations (identified by `name') are left untouched.
|
%% * Existing transformations (identified by `name') are left untouched.
|
||||||
%% * No transformations are removed.
|
%% * No transformations are removed.
|
||||||
|
|
|
@ -114,14 +114,9 @@ schema_test_() ->
|
||||||
transformation(<<"foo">>, [dummy_operation()])
|
transformation(<<"foo">>, [dummy_operation()])
|
||||||
])
|
])
|
||||||
)},
|
)},
|
||||||
{"operations must be non-empty",
|
{"operations may be empty",
|
||||||
?_assertThrow(
|
?_assertMatch(
|
||||||
{_Schema, [
|
[#{<<"operations">> := []}],
|
||||||
#{
|
|
||||||
reason := <<"at least one operation must be defined">>,
|
|
||||||
kind := validation_error
|
|
||||||
}
|
|
||||||
]},
|
|
||||||
parse_and_check([
|
parse_and_check([
|
||||||
transformation(
|
transformation(
|
||||||
<<"foo">>,
|
<<"foo">>,
|
||||||
|
|
|
@ -326,6 +326,13 @@ fields("ctx_schema_validation_failed") ->
|
||||||
{"event_type", event_type_sc(Event)},
|
{"event_type", event_type_sc(Event)},
|
||||||
{"validation", sc(binary(), #{desc => ?DESC("event_validation")})}
|
{"validation", sc(binary(), #{desc => ?DESC("event_validation")})}
|
||||||
| msg_event_common_fields()
|
| msg_event_common_fields()
|
||||||
|
];
|
||||||
|
fields("ctx_message_transformation_failed") ->
|
||||||
|
Event = 'message.transformation_failed',
|
||||||
|
[
|
||||||
|
{"event_type", event_type_sc(Event)},
|
||||||
|
{"transformation", sc(binary(), #{desc => ?DESC("event_transformation")})}
|
||||||
|
| msg_event_common_fields()
|
||||||
].
|
].
|
||||||
|
|
||||||
rule_input_message_context() ->
|
rule_input_message_context() ->
|
||||||
|
@ -345,7 +352,8 @@ rule_input_message_context() ->
|
||||||
ref("ctx_check_authn_complete"),
|
ref("ctx_check_authn_complete"),
|
||||||
ref("ctx_bridge_mqtt"),
|
ref("ctx_bridge_mqtt"),
|
||||||
ref("ctx_delivery_dropped"),
|
ref("ctx_delivery_dropped"),
|
||||||
ref("ctx_schema_validation_failed")
|
ref("ctx_schema_validation_failed"),
|
||||||
|
ref("ctx_message_transformation_failed")
|
||||||
]),
|
]),
|
||||||
#{
|
#{
|
||||||
desc => ?DESC("test_context"),
|
desc => ?DESC("test_context"),
|
||||||
|
|
|
@ -206,6 +206,8 @@ is_test_runtime_env() ->
|
||||||
%% is different from `topic'.
|
%% is different from `topic'.
|
||||||
get_in_topic(#{event_type := schema_validation_failed}) ->
|
get_in_topic(#{event_type := schema_validation_failed}) ->
|
||||||
<<"$events/schema_validation_failed">>;
|
<<"$events/schema_validation_failed">>;
|
||||||
|
get_in_topic(#{event_type := message_transformation_failed}) ->
|
||||||
|
<<"$events/message_transformation_failed">>;
|
||||||
get_in_topic(Context) ->
|
get_in_topic(Context) ->
|
||||||
case maps:find(event_topic, Context) of
|
case maps:find(event_topic, Context) of
|
||||||
{ok, EventTopic} ->
|
{ok, EventTopic} ->
|
||||||
|
|
|
@ -257,6 +257,21 @@ t_ctx_schema_validation_failed(_) ->
|
||||||
Expected = check_result([validation], [], Context),
|
Expected = check_result([validation], [], Context),
|
||||||
do_test(SQL, Context, Expected).
|
do_test(SQL, Context, Expected).
|
||||||
|
|
||||||
|
t_ctx_message_transformation_failed(_) ->
|
||||||
|
SQL =
|
||||||
|
<<"SELECT transformation FROM \"$events/message_transformation_failed\"">>,
|
||||||
|
Context = #{
|
||||||
|
<<"clientid">> => <<"c_emqx">>,
|
||||||
|
<<"event_type">> => <<"message_transformation_failed">>,
|
||||||
|
<<"payload">> => <<"{\"msg\": \"hello\"}">>,
|
||||||
|
<<"qos">> => 1,
|
||||||
|
<<"topic">> => <<"t/a">>,
|
||||||
|
<<"username">> => <<"u_emqx">>,
|
||||||
|
<<"transformation">> => <<"m">>
|
||||||
|
},
|
||||||
|
Expected = check_result([transformation], [], Context),
|
||||||
|
do_test(SQL, Context, Expected).
|
||||||
|
|
||||||
t_mongo_date_function_should_return_string_in_test_env(_) ->
|
t_mongo_date_function_should_return_string_in_test_env(_) ->
|
||||||
SQL =
|
SQL =
|
||||||
<<"SELECT mongo_date() as mongo_date FROM \"$events/client_check_authz_complete\"">>,
|
<<"SELECT mongo_date() as mongo_date FROM \"$events/client_check_authz_complete\"">>,
|
||||||
|
|
|
@ -90,21 +90,7 @@ handle_rule_function(sparkplug_encode, [Term | MoreArgs]) ->
|
||||||
[?EMQX_SCHEMA_REGISTRY_SPARKPLUGB_SCHEMA_NAME, Term | MoreArgs]
|
[?EMQX_SCHEMA_REGISTRY_SPARKPLUGB_SCHEMA_NAME, Term | MoreArgs]
|
||||||
);
|
);
|
||||||
handle_rule_function(schema_decode, [SchemaId, Data | MoreArgs]) ->
|
handle_rule_function(schema_decode, [SchemaId, Data | MoreArgs]) ->
|
||||||
try
|
decode(SchemaId, Data, MoreArgs);
|
||||||
decode(SchemaId, Data, MoreArgs)
|
|
||||||
catch
|
|
||||||
error:{gpb_error, {decoding_failure, {_Data, _Schema, {error, function_clause, _Stack}}}} ->
|
|
||||||
throw(
|
|
||||||
{schema_decode_error, #{
|
|
||||||
error_type => decoding_failure,
|
|
||||||
schema_id => SchemaId,
|
|
||||||
data => Data,
|
|
||||||
more_args => MoreArgs,
|
|
||||||
explain =>
|
|
||||||
<<"The given data could not be decoded. Please check the input data and the schema.">>
|
|
||||||
}}
|
|
||||||
)
|
|
||||||
end;
|
|
||||||
handle_rule_function(schema_decode, Args) ->
|
handle_rule_function(schema_decode, Args) ->
|
||||||
error({args_count_error, {schema_decode, Args}});
|
error({args_count_error, {schema_decode, Args}});
|
||||||
handle_rule_function(schema_encode, [SchemaId, Term | MoreArgs]) ->
|
handle_rule_function(schema_encode, [SchemaId, Term | MoreArgs]) ->
|
||||||
|
@ -162,7 +148,17 @@ encode(SerdeName, Data, VarArgs) when is_list(VarArgs) ->
|
||||||
with_serde(Name, F) ->
|
with_serde(Name, F) ->
|
||||||
case emqx_schema_registry:get_serde(Name) of
|
case emqx_schema_registry:get_serde(Name) of
|
||||||
{ok, Serde} ->
|
{ok, Serde} ->
|
||||||
F(Serde);
|
Meta =
|
||||||
|
case logger:get_process_metadata() of
|
||||||
|
undefined -> #{};
|
||||||
|
Meta0 -> Meta0
|
||||||
|
end,
|
||||||
|
logger:update_process_metadata(#{schema_name => Name}),
|
||||||
|
try
|
||||||
|
F(Serde)
|
||||||
|
after
|
||||||
|
logger:set_process_metadata(Meta)
|
||||||
|
end;
|
||||||
{error, not_found} ->
|
{error, not_found} ->
|
||||||
error({serde_not_found, Name})
|
error({serde_not_found, Name})
|
||||||
end.
|
end.
|
||||||
|
@ -199,10 +195,39 @@ make_serde(json, Name, Source) ->
|
||||||
eval_decode(#serde{type = avro, name = Name, eval_context = Store}, [Data]) ->
|
eval_decode(#serde{type = avro, name = Name, eval_context = Store}, [Data]) ->
|
||||||
Opts = avro:make_decoder_options([{map_type, map}, {record_type, map}]),
|
Opts = avro:make_decoder_options([{map_type, map}, {record_type, map}]),
|
||||||
avro_binary_decoder:decode(Data, Name, Store, Opts);
|
avro_binary_decoder:decode(Data, Name, Store, Opts);
|
||||||
eval_decode(#serde{type = protobuf, eval_context = SerdeMod}, [EncodedData, MessageName0]) ->
|
eval_decode(#serde{type = protobuf}, [#{} = DecodedData, MessageType]) ->
|
||||||
MessageName = binary_to_existing_atom(MessageName0, utf8),
|
%% Already decoded, so it's an user error.
|
||||||
Decoded = apply(SerdeMod, decode_msg, [EncodedData, MessageName]),
|
throw(
|
||||||
emqx_utils_maps:binary_key_map(Decoded);
|
{schema_decode_error, #{
|
||||||
|
error_type => decoding_failure,
|
||||||
|
data => DecodedData,
|
||||||
|
message_type => MessageType,
|
||||||
|
explain =>
|
||||||
|
<<
|
||||||
|
"Attempted to schema decode an already decoded message."
|
||||||
|
" Check your rules or transformation pipeline."
|
||||||
|
>>
|
||||||
|
}}
|
||||||
|
);
|
||||||
|
eval_decode(#serde{type = protobuf, eval_context = SerdeMod}, [EncodedData, MessageType0]) ->
|
||||||
|
MessageType = binary_to_existing_atom(MessageType0, utf8),
|
||||||
|
try
|
||||||
|
Decoded = apply(SerdeMod, decode_msg, [EncodedData, MessageType]),
|
||||||
|
emqx_utils_maps:binary_key_map(Decoded)
|
||||||
|
catch
|
||||||
|
error:{gpb_error, {decoding_failure, {_Data, _Schema, {error, function_clause, _Stack}}}} ->
|
||||||
|
#{schema_name := SchemaName} = logger:get_process_metadata(),
|
||||||
|
throw(
|
||||||
|
{schema_decode_error, #{
|
||||||
|
error_type => decoding_failure,
|
||||||
|
data => EncodedData,
|
||||||
|
message_type => MessageType,
|
||||||
|
schema_name => SchemaName,
|
||||||
|
explain =>
|
||||||
|
<<"The given data could not be decoded. Please check the input data and the schema.">>
|
||||||
|
}}
|
||||||
|
)
|
||||||
|
end;
|
||||||
eval_decode(#serde{type = json, name = Name}, [Data]) ->
|
eval_decode(#serde{type = json, name = Name}, [Data]) ->
|
||||||
true = is_binary(Data),
|
true = is_binary(Data),
|
||||||
Term = json_decode(Data),
|
Term = json_decode(Data),
|
||||||
|
|
|
@ -555,8 +555,8 @@ t_decode_fail(_Config) ->
|
||||||
data := <<"ss">>,
|
data := <<"ss">>,
|
||||||
error_type := decoding_failure,
|
error_type := decoding_failure,
|
||||||
explain := _,
|
explain := _,
|
||||||
more_args := [<<"Person">>],
|
message_type := 'Person',
|
||||||
schema_id := <<"my_serde">>
|
schema_name := <<"my_serde">>
|
||||||
}},
|
}},
|
||||||
emqx_rule_funcs:schema_decode(<<"my_serde">>, Payload, <<"Person">>)
|
emqx_rule_funcs:schema_decode(<<"my_serde">>, Payload, <<"Person">>)
|
||||||
),
|
),
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
Definitions.
|
Definitions.
|
||||||
%% Define regular expressions for tokens
|
%% Define regular expressions for tokens
|
||||||
IDENTIFIER = [a-zA-Z][a-zA-Z0-9_.]*
|
IDENTIFIER = [a-zA-Z][-a-zA-Z0-9_.]*
|
||||||
SQ_STRING = \'[^\']*\'
|
SQ_STRING = \'[^\']*\'
|
||||||
DQ_STRING = \"[^\"]*\"
|
DQ_STRING = \"[^\"]*\"
|
||||||
INTEGER = [+-]?[0-9]+
|
INTEGER = [+-]?[0-9]+
|
||||||
|
|
|
@ -23,7 +23,7 @@
|
||||||
|
|
||||||
-define(SYNTAX_ERROR, {error, "syntax error before:" ++ _}).
|
-define(SYNTAX_ERROR, {error, "syntax error before:" ++ _}).
|
||||||
|
|
||||||
redner_test_() ->
|
render_test_() ->
|
||||||
[
|
[
|
||||||
{"direct var reference", fun() -> ?assertEqual({ok, <<"1">>}, render("a", #{a => 1})) end},
|
{"direct var reference", fun() -> ?assertEqual({ok, <<"1">>}, render("a", #{a => 1})) end},
|
||||||
{"concat strings", fun() ->
|
{"concat strings", fun() ->
|
||||||
|
@ -32,6 +32,15 @@ redner_test_() ->
|
||||||
{"concat empty string", fun() ->
|
{"concat empty string", fun() ->
|
||||||
?assertEqual({ok, <<"">>}, render("concat([''])", #{}))
|
?assertEqual({ok, <<"">>}, render("concat([''])", #{}))
|
||||||
end},
|
end},
|
||||||
|
{"identifier with hyphen", fun() ->
|
||||||
|
?assertEqual(
|
||||||
|
{ok, <<"10">>},
|
||||||
|
render(
|
||||||
|
"pub_props.Message-Expiry-Interval",
|
||||||
|
#{pub_props => #{'Message-Expiry-Interval' => 10}}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
end},
|
||||||
{"tokens 1st", fun() ->
|
{"tokens 1st", fun() ->
|
||||||
?assertEqual({ok, <<"a">>}, render("nth(1,tokens(var, ','))", #{var => <<"a,b">>}))
|
?assertEqual({ok, <<"a">>}, render("nth(1,tokens(var, ','))", #{var => <<"a,b">>}))
|
||||||
end},
|
end},
|
||||||
|
|
|
@ -366,6 +366,12 @@ event_validation.desc:
|
||||||
event_validation.label:
|
event_validation.label:
|
||||||
"""Validation"""
|
"""Validation"""
|
||||||
|
|
||||||
|
event_transformation.desc:
|
||||||
|
"""Transformation"""
|
||||||
|
|
||||||
|
event_transformation.label:
|
||||||
|
"""Transformation"""
|
||||||
|
|
||||||
root_rule_info.desc:
|
root_rule_info.desc:
|
||||||
"""Schema for rule info"""
|
"""Schema for rule info"""
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue