Просмотр исходного кода

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.
Thales Macedo Garitezi 2 лет назад
Родитель
Сommit
cc8631223e

+ 10 - 1
apps/emqx/src/emqx_hocon.erl

@@ -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 =

+ 5 - 1
apps/emqx/src/emqx_schema.erl

@@ -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

+ 15 - 3
apps/emqx/test/emqx_schema_tests.erl

@@ -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">>)
         )
     ].

+ 1 - 1
apps/emqx_resource/test/emqx_resource_schema_tests.erl

@@ -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">>)
             )