fix(schema): avoid `function_clause` error when compacting errors

Fixes https://emqx.atlassian.net/browse/EMQX-10168

The bridge probe API displayed the typecheck errors for the new
timeout duration types correctly, but when an user tried to create the
bridge anyway a `function_clause` error was raised when trying to
compact hocon errors:

```
09:47:19.045 [warning] [exception: :error, path: '/bridges', reason: {:case_clause, {:error, {:config_update_crashed, :function_clause}}}, stacktrace: [{:emqx_bridge_api, :create_or_update_bridge, 4, [file: '/home/thales/dev/emqx/emqx/apps/emqx_bridge/src/emqx_bridge_api.erl', line: 602]}, {:minirest_handler, :apply_callback, 3, [file: '/home/thales/dev/emqx/emqx/deps/minirest/src/minirest_handler.erl', line: 111]}, {:minirest_handler, :handle, 2, [file: '/home/thales/dev/emqx/emqx/deps/minirest/src/minirest_handler.erl', line: 44]}, {:minirest_handler, :init, 2, [file: '/home/thales/dev/emqx/emqx/deps/minirest/src/minirest_handler.erl', line: 27]}, {:cowboy_handler, :execute, 2, [file: '/home/thales/dev/emqx/emqx/deps/cowboy/src/cowboy_handler.erl', line: 41]}, {:cowboy_stream_h, :execute, 3, [file: '/home/thales/dev/emqx/emqx/deps/cowboy/src/cowboy_stream_h.erl', line: 318]}, {:cowboy_stream_h, :request_process, 3, [file: '/home/thales/dev/emqx/emqx/deps/cowboy/src/cowboy_stream_h.erl', line: 302]}, {:proc_lib, :init_p_do_apply, 3, [file: 'proc_lib.erl', line: 240]}]]
```

This fixes the issue so that both APIs return more friendly error messages.
This commit is contained in:
Thales Macedo Garitezi 2023-06-07 10:26:54 -03:00
parent f9ff1007a0
commit cc8631223e
4 changed files with 31 additions and 6 deletions

View File

@ -100,7 +100,16 @@ no_stacktrace(Map) ->
%% it's maybe too much when reporting to the user
-spec compact_errors(any(), Stacktrace :: list()) -> {error, any()}.
compact_errors({SchemaModule, Errors}, Stacktrace) ->
compact_errors(SchemaModule, Errors, Stacktrace).
compact_errors(SchemaModule, Errors, Stacktrace);
compact_errors(ErrorContext0, _Stacktrace) when is_map(ErrorContext0) ->
case ErrorContext0 of
#{exception := #{schema_module := _Mod, message := _Msg} = Detail} ->
Error0 = maps:remove(exception, ErrorContext0),
Error = maps:merge(Error0, Detail),
{error, Error};
_ ->
{error, ErrorContext0}
end.
compact_errors(SchemaModule, [Error0 | More], _Stacktrace) when is_map(Error0) ->
Error1 =

View File

@ -2695,7 +2695,11 @@ do_to_timeout_duration(Str, Fn, Max, Unit) ->
Msg = lists:flatten(
io_lib:format("timeout value too large (max: ~b ~s)", [Max, Unit])
),
throw(Msg)
throw(#{
schema_module => ?MODULE,
message => Msg,
kind => validation_error
})
end;
Err ->
Err

View File

@ -886,15 +886,27 @@ timeout_types_test_() ->
typerefl:from_string(emqx_schema:timeout_duration_s(), <<"4294967000ms">>)
),
?_assertThrow(
"timeout value too large (max: 4294967295 ms)",
#{
kind := validation_error,
message := "timeout value too large (max: 4294967295 ms)",
schema_module := emqx_schema
},
typerefl:from_string(emqx_schema:timeout_duration(), <<"4294967296ms">>)
),
?_assertThrow(
"timeout value too large (max: 4294967295 ms)",
#{
kind := validation_error,
message := "timeout value too large (max: 4294967295 ms)",
schema_module := emqx_schema
},
typerefl:from_string(emqx_schema:timeout_duration_ms(), <<"4294967296ms">>)
),
?_assertThrow(
"timeout value too large (max: 4294967 s)",
#{
kind := validation_error,
message := "timeout value too large (max: 4294967 s)",
schema_module := emqx_schema
},
typerefl:from_string(emqx_schema:timeout_duration_s(), <<"4294967001ms">>)
)
].

View File

@ -67,7 +67,7 @@ health_check_interval_validator_test_() ->
parse_and_check_webhook_bridge(webhook_bridge_health_check_hocon(<<"3_600_000ms">>))
)},
?_assertThrow(
#{exception := "timeout value too large" ++ _},
#{exception := #{message := "timeout value too large" ++ _}},
parse_and_check_webhook_bridge(
webhook_bridge_health_check_hocon(<<"150000000000000s">>)
)