فهرست منبع

feat: upgrade hocon to 0.25.0 to replace nullable with required.

Zhongwen Deng 4 سال پیش
والد
کامیت
db584f79d6
56فایلهای تغییر یافته به همراه5627 افزوده شده و 247 حذف شده
  1. 1 1
      apps/emqx/rebar.config
  2. 3 3
      apps/emqx/src/emqx_config.erl
  3. 3 3
      apps/emqx/src/emqx_hocon.erl
  4. 12 12
      apps/emqx/src/emqx_schema.erl
  5. 6 6
      apps/emqx_authn/src/emqx_authn_api.erl
  6. 1 2
      apps/emqx_authn/src/emqx_authn_password_hashing.erl
  7. 2 2
      apps/emqx_authn/src/emqx_authn_schema.erl
  8. 1 1
      apps/emqx_authn/src/simple_authn/emqx_authn_http.erl
  9. 2 2
      apps/emqx_authn/src/simple_authn/emqx_authn_mongodb.erl
  10. 1 1
      apps/emqx_authz/src/emqx_authz_api_schema.erl
  11. 2 2
      apps/emqx_authz/src/emqx_authz_schema.erl
  12. 1 1
      apps/emqx_bridge/src/emqx_bridge_http_schema.erl
  13. 2 2
      apps/emqx_bridge/src/emqx_bridge_schema.erl
  14. 5 7
      apps/emqx_conf/src/emqx_conf.erl
  15. 1 1
      apps/emqx_conf/src/emqx_conf_schema.erl
  16. 7 7
      apps/emqx_connector/src/emqx_connector_http.erl
  17. 6 8
      apps/emqx_connector/src/emqx_connector_mongo.erl
  18. 1 1
      apps/emqx_connector/src/emqx_connector_mysql.erl
  19. 2 2
      apps/emqx_connector/src/emqx_connector_pgsql.erl
  20. 2 2
      apps/emqx_connector/src/emqx_connector_redis.erl
  21. 3 3
      apps/emqx_connector/src/emqx_connector_schema_lib.erl
  22. 2 2
      apps/emqx_connector/src/mqtt/emqx_connector_mqtt_schema.erl
  23. 2 2
      apps/emqx_connector/test/emqx_connector_test_helpers.erl
  24. 5 5
      apps/emqx_dashboard/src/emqx_dashboard_schema.erl
  25. 16 22
      apps/emqx_dashboard/src/emqx_dashboard_swagger.erl
  26. 3 4
      apps/emqx_dashboard/test/emqx_swagger_parameter_SUITE.erl
  27. 2 2
      apps/emqx_dashboard/test/emqx_swagger_remote_schema.erl
  28. 4 4
      apps/emqx_dashboard/test/emqx_swagger_requestBody_SUITE.erl
  29. 4 4
      apps/emqx_dashboard/test/emqx_swagger_response_SUITE.erl
  30. 7 7
      apps/emqx_gateway/src/emqx_gateway_api.erl
  31. 2 2
      apps/emqx_gateway/src/emqx_gateway_api_authn.erl
  32. 3 3
      apps/emqx_gateway/src/emqx_gateway_api_clients.erl
  33. 18 18
      apps/emqx_gateway/src/emqx_gateway_api_listeners.erl
  34. 22 22
      apps/emqx_gateway/src/emqx_gateway_schema.erl
  35. 1 1
      apps/emqx_management/src/emqx_mgmt_api_alarms.erl
  36. 3 3
      apps/emqx_management/src/emqx_mgmt_api_app.erl
  37. 6 6
      apps/emqx_management/src/emqx_mgmt_api_banned.erl
  38. 14 14
      apps/emqx_management/src/emqx_mgmt_api_clients.erl
  39. 1 1
      apps/emqx_management/src/emqx_mgmt_api_configs.erl
  40. 1 1
      apps/emqx_management/src/emqx_mgmt_api_metrics.erl
  41. 2 2
      apps/emqx_management/src/emqx_mgmt_api_nodes.erl
  42. 1 1
      apps/emqx_management/src/emqx_mgmt_api_stats.erl
  43. 6 6
      apps/emqx_management/src/emqx_mgmt_api_subscriptions.erl
  44. 12 12
      apps/emqx_management/src/emqx_mgmt_api_trace.erl
  45. 1 1
      apps/emqx_management/test/emqx_mgmt_api_trace_SUITE.erl
  46. 6 6
      apps/emqx_modules/src/emqx_topic_metrics_api.erl
  47. 4 4
      apps/emqx_plugins/src/emqx_plugins_schema.erl
  48. 1 1
      apps/emqx_prometheus/rebar.config
  49. 1 1
      apps/emqx_psk/src/emqx_psk_schema.erl
  50. 2 2
      apps/emqx_resource/test/emqx_test_resource.erl
  51. 11 11
      apps/emqx_rule_engine/src/emqx_rule_api_schema.erl
  52. 3 3
      apps/emqx_rule_engine/src/emqx_rule_engine_schema.erl
  53. 3 3
      apps/emqx_statsd/src/emqx_statsd_schema.erl
  54. 1 1
      mix.exs
  55. 1 1
      rebar.config
  56. 5392 0
      schema_v1.json

+ 1 - 1
apps/emqx/rebar.config

@@ -19,7 +19,7 @@
     , {esockd, {git, "https://github.com/emqx/esockd", {tag, "5.9.0"}}}
     , {ekka, {git, "https://github.com/emqx/ekka", {tag, "0.12.1"}}}
     , {gen_rpc, {git, "https://github.com/emqx/gen_rpc", {tag, "2.8.0"}}}
-    , {hocon, {git, "https://github.com/emqx/hocon.git", {tag, "0.24.0"}}}
+    , {hocon, {git, "https://github.com/emqx/hocon.git", {tag, "0.25.0"}}}
     , {pbkdf2, {git, "https://github.com/emqx/erlang-pbkdf2.git", {tag, "2.0.4"}}}
     , {recon, {git, "https://github.com/ferd/recon", {tag, "2.5.1"}}}
     , {snabbkaffe, {git, "https://github.com/kafka4beam/snabbkaffe.git", {tag, "0.18.0"}}}

+ 3 - 3
apps/emqx/src/emqx_config.erl

@@ -295,7 +295,7 @@ include_dirs() ->
     [filename:join(emqx:data_dir(), "configs")].
 
 merge_envs(SchemaMod, RawConf) ->
-    Opts = #{nullable => true, %% TODO: evil, remove, nullable should be declared in schema
+    Opts = #{required => false, %% TODO: evil, remove, required should be declared in schema
              format => map,
              apply_override_envs => true
             },
@@ -308,7 +308,7 @@ check_config(SchemaMod, RawConf) ->
 
 check_config(SchemaMod, RawConf, Opts0) ->
     Opts1 = #{return_plain => true,
-              nullable => true, %% TODO: evil, remove, nullable should be declared in schema
+              required => false, %% TODO: evil, remove, required should be declared in schema
               format => map
              },
     Opts = maps:merge(Opts0, Opts1),
@@ -331,7 +331,7 @@ fill_defaults(RawConf) ->
 -spec fill_defaults(module(), raw_config()) -> map().
 fill_defaults(SchemaMod, RawConf) ->
     hocon_tconf:check_plain(SchemaMod, RawConf,
-        #{nullable => true, only_fill_defaults => true},
+        #{required => false, only_fill_defaults => true},
         root_names_from_conf(RawConf)).
 
 

+ 3 - 3
apps/emqx/src/emqx_hocon.erl

@@ -34,9 +34,9 @@ format_path([Name | Rest]) ->
 -spec check(module(), hocon:config() | iodata()) ->
         {ok, hocon:config()} | {error, any()}.
 check(SchemaModule, Conf) when is_map(Conf) ->
-    %% TODO: remove nullable
-    %% fields should state nullable or not in their schema
-    Opts = #{atom_key => true, nullable => true},
+    %% TODO: remove required
+    %% fields should state required or not in their schema
+    Opts = #{atom_key => true, required => false},
     try
         {ok, hocon_tconf:check_plain(SchemaModule, Conf, Opts)}
     catch

+ 12 - 12
apps/emqx/src/emqx_schema.erl

@@ -602,31 +602,31 @@ fields("listeners") ->
     [ {"tcp",
        sc(map(name, ref("mqtt_tcp_listener")),
           #{ desc => "TCP listeners"
-           , nullable => {true, recursively}
+           , required => {false, recursively}
            })
       }
     , {"ssl",
        sc(map(name, ref("mqtt_ssl_listener")),
           #{ desc => "SSL listeners"
-           , nullable => {true, recursively}
+           , required => {false, recursively}
            })
       }
     , {"ws",
        sc(map(name, ref("mqtt_ws_listener")),
           #{ desc => "HTTP websocket listeners"
-           , nullable => {true, recursively}
+           , required => {false, recursively}
            })
       }
     , {"wss",
        sc(map(name, ref("mqtt_wss_listener")),
           #{ desc => "HTTPS websocket listeners"
-           , nullable => {true, recursively}
+           , required => {false, recursively}
            })
       }
     , {"quic",
        sc(map(name, ref("mqtt_quic_listener")),
           #{ desc => "QUIC listeners"
-           , nullable => {true, recursively}
+           , required => {false, recursively}
            })
       }
     ];
@@ -1145,7 +1145,7 @@ mqtt_listener() ->
 base_listener() ->
     [ {"bind",
        sc(hoconsc:union([ip_port(), integer()]),
-          #{ nullable => false
+          #{ required => true
            })}
     , {"acceptors",
        sc(integer(),
@@ -1204,7 +1204,7 @@ common_ssl_opts_schema(Defaults) ->
     , {"cacertfile",
        sc(string(),
           #{ default => D("cacertfile")
-           , nullable => true
+           , required => false
            , desc =>
 """Trusted PEM format CA certificates bundle file.<br>
 The certificates in this file are used to verify the TLS peer's certificates.
@@ -1219,7 +1219,7 @@ already established connections.
     , {"certfile",
        sc(string(),
           #{ default => D("certfile")
-           , nullable => true
+           , required => false
            , desc =>
 """PEM format certificates chain file.<br>
 The certificates in this file should be in reversed order of the certificate
@@ -1233,7 +1233,7 @@ the file if it is to be added.
     , {"keyfile",
        sc(string(),
           #{ default => D("keyfile")
-           , nullable => true
+           , required => false
            , desc =>
 """PEM format private key file.<br>
 """
@@ -1257,7 +1257,7 @@ the file if it is to be added.
     , {"password",
        sc(string(),
           #{ sensitive => true
-           , nullable => true
+           , required => false
            , desc =>
 """String containing the user's password. Only used if the private
 key file is password-protected."""
@@ -1305,7 +1305,7 @@ server_ssl_opts_schema(Defaults, IsRanchListener) ->
     [ {"dhfile",
        sc(string(),
           #{ default => D("dhfile")
-           , nullable => true
+           , required => false
            , desc =>
 """Path to a file containing PEM-encoded Diffie Hellman parameters
 to be used by the server if a cipher suite using Diffie Hellman
@@ -1361,7 +1361,7 @@ client_ssl_opts_schema(Defaults) ->
     common_ssl_opts_schema(Defaults) ++
     [ { "server_name_indication",
         sc(hoconsc:union([disable, string()]),
-           #{ nullable => true
+           #{ required => false
             , desc =>
 """Specify the host name to be used in TLS Server Name Indication extension.<br>
 For instance, when connecting to \"server.example.net\", the genuine server

+ 6 - 6
apps/emqx_authn/src/emqx_authn_api.erl

@@ -116,7 +116,7 @@ fields(request_user_create) ->
 fields(request_user_update) ->
     [
         {password, binary()},
-        {is_superuser, mk(boolean(), #{default => false, nullable => true})}
+        {is_superuser, mk(boolean(), #{default => false, required => false})}
     ];
 
 fields(request_move) ->
@@ -128,7 +128,7 @@ fields(request_import_users) ->
 fields(response_user) ->
     [
         {user_id, binary()},
-        {is_superuser, mk(boolean(), #{default => false, nullable => true})}
+        {is_superuser, mk(boolean(), #{default => false, required => false})}
     ];
 
 fields(response_users) ->
@@ -379,8 +379,8 @@ schema("/authentication/:id/users") ->
             description => <<"List users in authenticator in global authentication chain">>,
             parameters => [
                 param_auth_id(),
-                {page, mk(integer(), #{in => query, desc => <<"Page Index">>, nullable => true})},
-                {limit, mk(integer(), #{in => query, desc => <<"Page Limit">>, nullable => true})}
+                {page, mk(integer(), #{in => query, desc => <<"Page Index">>, required => false})},
+                {limit, mk(integer(), #{in => query, desc => <<"Page Limit">>, required => false})}
             ],
             responses => #{
                 200 => emqx_dashboard_swagger:schema_with_example(
@@ -415,8 +415,8 @@ schema("/listeners/:listener_id/authentication/:id/users") ->
             description => <<"List users in authenticator in listener authentication chain">>,
             parameters => [
                 param_listener_id(), param_auth_id(),
-                {page, mk(integer(), #{in => query, desc => <<"Page Index">>, nullable => true})},
-                {limit, mk(integer(), #{in => query, desc => <<"Page Limit">>, nullable => true})}
+                {page, mk(integer(), #{in => query, desc => <<"Page Index">>, required => false})},
+                {limit, mk(integer(), #{in => query, desc => <<"Page Limit">>, required => false})}
             ],
             responses => #{
                 200 => emqx_dashboard_swagger:schema_with_example(

+ 1 - 2
apps/emqx_authn/src/emqx_authn_password_hashing.erl

@@ -81,8 +81,7 @@ salt_rounds(default) -> 10;
 salt_rounds(_) -> undefined.
 
 dk_length(type) -> integer();
-dk_length(nullable) -> true;
-dk_length(default) -> undefined;
+dk_length(required) -> false;
 dk_length(_) -> undefined.
 
 type_rw(type) ->

+ 2 - 2
apps/emqx_authn/src/emqx_authn_schema.erl

@@ -54,8 +54,8 @@ root_type() ->
 
 mechanism(Name) ->
     hoconsc:mk(hoconsc:enum([Name]),
-               #{nullable => false}).
+               #{required => true}).
 
 backend(Name) ->
     hoconsc:mk(hoconsc:enum([Name]),
-               #{nullable => false}).
+               #{required => true}).

+ 1 - 1
apps/emqx_authn/src/simple_authn/emqx_authn_http.erl

@@ -77,7 +77,7 @@ validations() ->
 
 url(type) -> binary();
 url(validator) -> [?NOT_EMPTY("the value of the field 'url' cannot be empty")];
-url(nullable) -> false;
+url(required) -> true;
 url(_) -> undefined.
 
 headers(type) -> map();

+ 2 - 2
apps/emqx_authn/src/simple_authn/emqx_authn_mongodb.erl

@@ -76,11 +76,11 @@ password_hash_field(type) -> binary();
 password_hash_field(_) -> undefined.
 
 salt_field(type) -> binary();
-salt_field(nullable) -> true;
+salt_field(required) -> false;
 salt_field(_) -> undefined.
 
 is_superuser_field(type) -> binary();
-is_superuser_field(nullable) -> true;
+is_superuser_field(required) -> false;
 is_superuser_field(_) -> undefined.
 
 %%------------------------------------------------------------------------------

+ 1 - 1
apps/emqx_authz/src/emqx_authz_api_schema.erl

@@ -85,7 +85,7 @@ fields(position) ->
 
 url(type) -> binary();
 url(validator) -> [?NOT_EMPTY("the value of the field 'url' cannot be empty")];
-url(nullable) -> false;
+url(required) -> true;
 url(_) -> undefined.
 
 headers(type) -> map();

+ 2 - 2
apps/emqx_authz/src/emqx_authz_schema.erl

@@ -145,7 +145,7 @@ fields(redis_cluster) ->
 http_common_fields() ->
     [ {url, fun url/1}
     , {request_timeout, mk_duration("Request timeout", #{default => "30s"})}
-    , {body, #{type => map(), nullable => true}}
+    , {body, #{type => map(), required => false}}
     ] ++ maps:to_list(maps:without([ base_url
                                    , pool_type],
                                    maps:from_list(connector_fields(http)))).
@@ -181,7 +181,7 @@ headers_no_content_type(_) -> undefined.
 
 url(type) -> binary();
 url(validator) -> [?NOT_EMPTY("the value of the field 'url' cannot be empty")];
-url(nullable) -> false;
+url(required) -> true;
 url(_) -> undefined.
 
 %%--------------------------------------------------------------------

+ 1 - 1
apps/emqx_bridge/src/emqx_bridge_http_schema.erl

@@ -13,7 +13,7 @@ roots() -> [].
 fields("bridge") ->
     basic_config() ++
     [ {url, mk(binary(),
-          #{ nullable => false
+          #{ required => true
            , desc =>"""
 The URL of the HTTP Bridge.<br>
 Template with variables is allowed in the path, but variables cannot be used in the scheme, host,

+ 2 - 2
apps/emqx_bridge/src/emqx_bridge_schema.erl

@@ -52,7 +52,7 @@ common_bridge_fields() ->
            })}
     , {connector,
         mk(binary(),
-           #{ nullable => false
+           #{ required => true
             , example => <<"mqtt:my_mqtt_connector">>
             , desc =>"""
 The connector ID to be used for this bridge. Connector IDs must be of format:
@@ -75,7 +75,7 @@ metrics_status_fields() ->
 
 direction_field(Dir, Desc) ->
     {direction, mk(Dir,
-        #{ nullable => false
+        #{ required => true
          , default => egress
          , desc => "The direction of the bridge. Can be one of 'ingress' or 'egress'.<br>"
             ++ Desc

+ 5 - 7
apps/emqx_conf/src/emqx_conf.erl

@@ -162,7 +162,7 @@ check_cluster_rpc_result(Result) ->
 %% Only gen hot_conf schema, not all configuration fields.
 gen_hot_conf_schema(File) ->
     {ApiSpec0, Components0} = emqx_dashboard_swagger:spec(emqx_mgmt_api_configs,
-        #{schema_to_spec_func => fun hocon_schema_to_spec/2}),
+        #{schema_converter => fun hocon_schema_to_spec/2}),
     ApiSpec = lists:foldl(fun({Path, Spec, _, _}, Acc) ->
         NewSpec = maps:fold(fun(Method, #{responses := Responses}, SubAcc) ->
             case Responses of
@@ -187,8 +187,6 @@ gen_hot_conf_schema(File) ->
 -define(TO_REF(_N_, _F_), iolist_to_binary([to_bin(_N_), ".", to_bin(_F_)])).
 -define(TO_COMPONENTS_SCHEMA(_M_, _F_), iolist_to_binary([<<"#/components/schemas/">>,
     ?TO_REF(emqx_dashboard_swagger:namespace(_M_), _F_)])).
--define(TO_COMPONENTS_PARAM(_M_, _F_), iolist_to_binary([<<"#/components/parameters/">>,
-    ?TO_REF(emqx_dashboard_swagger:namespace(_M_), _F_)])).
 
 hocon_schema_to_spec(?R_REF(Module, StructName), _LocalModule) ->
     {#{<<"$ref">> => ?TO_COMPONENTS_SCHEMA(Module, StructName)},
@@ -224,8 +222,8 @@ typename_to_spec("term()", _Mod) -> #{type => string};
 typename_to_spec("boolean()", _Mod) -> #{type => boolean};
 typename_to_spec("binary()", _Mod) -> #{type => string};
 typename_to_spec("float()", _Mod) -> #{type => number};
-typename_to_spec("integer()", _Mod) -> #{type => integer};
-typename_to_spec("non_neg_integer()", _Mod) -> #{type => integer, minimum => 1};
+typename_to_spec("integer()", _Mod) -> #{type => number};
+typename_to_spec("non_neg_integer()", _Mod) -> #{type => number, minimum => 1};
 typename_to_spec("number()", _Mod) -> #{type => number};
 typename_to_spec("string()", _Mod) -> #{type => string};
 typename_to_spec("atom()", _Mod) -> #{type => string};
@@ -234,7 +232,7 @@ typename_to_spec("duration()", _Mod) -> #{type => duration};
 typename_to_spec("duration_s()", _Mod) -> #{type => duration};
 typename_to_spec("duration_ms()", _Mod) -> #{type => duration};
 typename_to_spec("percent()", _Mod) -> #{type => percent};
-typename_to_spec("file()", _Mod) -> #{type => file};
+typename_to_spec("file()", _Mod) -> #{type => string};
 typename_to_spec("ip_port()", _Mod) -> #{type => ip_port};
 typename_to_spec("url()", _Mod) -> #{type => url};
 typename_to_spec("bytesize()", _Mod) -> #{type => byteSize};
@@ -263,7 +261,7 @@ default_type(Type) -> Type.
 range(Name) ->
     case string:split(Name, "..") of
         [MinStr, MaxStr] -> %% 1..10 1..inf -inf..10
-            Schema = #{type => integer},
+            Schema = #{type => number},
             Schema1 = add_integer_prop(Schema, minimum, MinStr),
             add_integer_prop(Schema1, maximum, MaxStr);
         _ -> nomatch

+ 1 - 1
apps/emqx_conf/src/emqx_conf_schema.erl

@@ -262,7 +262,7 @@ fields("node") ->
            })}
     , {"data_dir",
        sc(string(),
-          #{ nullable => false,
+          #{ required => true,
              mapping => "emqx.data_dir",
              desc => "Path to the persistent data directory. It must be unique per broker instance."
            })}

+ 7 - 7
apps/emqx_connector/src/emqx_connector_http.erl

@@ -61,7 +61,7 @@ roots() ->
 fields(config) ->
     [ {base_url,
        sc(url(),
-          #{ nullable => false
+          #{ required => true
            , validator => fun(#{query := _Query}) ->
                             {error, "There must be no query in the base_url"};
                             (_) -> ok
@@ -106,7 +106,7 @@ For example: http://localhost:9901/
     , {request, hoconsc:mk(
         ref("request"),
         #{ default => undefined
-         , nullable => true
+         , required => false
          , desc => """
 If the request is provided, the caller can send HTTP requests via
 <code>emqx_resource:query(ResourceId, {send_message, BridgeId, Message})</code>
@@ -115,13 +115,13 @@ If the request is provided, the caller can send HTTP requests via
     ] ++ emqx_connector_schema_lib:ssl_fields();
 
 fields("request") ->
-    [ {method, hoconsc:mk(hoconsc:enum([post, put, get, delete]), #{nullable => true})}
-    , {path, hoconsc:mk(binary(), #{nullable => true})}
-    , {body, hoconsc:mk(binary(), #{nullable => true})}
-    , {headers, hoconsc:mk(map(), #{nullable => true})}
+    [ {method, hoconsc:mk(hoconsc:enum([post, put, get, delete]), #{required => false})}
+    , {path, hoconsc:mk(binary(), #{required => false})}
+    , {body, hoconsc:mk(binary(), #{required => false})}
+    , {headers, hoconsc:mk(map(), #{required => false})}
     , {request_timeout,
         sc(emqx_schema:duration_ms(),
-           #{ nullable => true
+           #{ required => false
             , desc => "The timeout when sending request to the HTTP server"
             })}
     ].

+ 6 - 8
apps/emqx_connector/src/emqx_connector_mongo.erl

@@ -91,11 +91,9 @@ mongo_fields() ->
     , {pool_size, fun emqx_connector_schema_lib:pool_size/1}
     , {username, fun emqx_connector_schema_lib:username/1}
     , {password, fun emqx_connector_schema_lib:password/1}
-    , {auth_source, #{type => binary(),
-                      nullable => true}}
+    , {auth_source, #{type => binary(), required => false}}
     , {database, fun emqx_connector_schema_lib:database/1}
-    , {topology, #{type => hoconsc:ref(?MODULE, topology),
-                   nullable => true}}
+    , {topology, #{type => hoconsc:ref(?MODULE, topology), required => false}}
     ] ++
     emqx_connector_schema_lib:ssl_fields().
 
@@ -289,14 +287,14 @@ init_worker_options([], Acc) -> Acc.
 %% Schema funcs
 
 server(type) -> emqx_schema:ip_port();
-server(nullable) -> false;
+server(required) -> true;
 server(validator) -> [?NOT_EMPTY("the value of the field 'server' cannot be empty")];
 server(converter) -> fun to_server_raw/1;
 server(desc) -> ?SERVER_DESC("MongoDB", integer_to_list(?MONGO_DEFAULT_PORT));
 server(_) -> undefined.
 
 servers(type) -> binary();
-servers(nullable) -> false;
+servers(required) -> true;
 servers(validator) -> [?NOT_EMPTY("the value of the field 'servers' cannot be empty")];
 servers(converter) -> fun to_servers_raw/1;
 servers(desc) -> ?SERVERS_DESC ++ server(desc);
@@ -311,11 +309,11 @@ r_mode(default) -> master;
 r_mode(_) -> undefined.
 
 duration(type) -> emqx_schema:duration_ms();
-duration(nullable) -> true;
+duration(required) -> false;
 duration(_) -> undefined.
 
 replica_set_name(type) -> binary();
-replica_set_name(nullable) -> true;
+replica_set_name(required) -> false;
 replica_set_name(_) -> undefined.
 
 srv_record(type) -> boolean();

+ 1 - 1
apps/emqx_connector/src/emqx_connector_mysql.erl

@@ -50,7 +50,7 @@ fields(config) ->
     emqx_connector_schema_lib:ssl_fields().
 
 server(type) -> emqx_schema:ip_port();
-server(nullable) -> false;
+server(required) -> true;
 server(validator) -> [?NOT_EMPTY("the value of the field 'server' cannot be empty")];
 server(converter) -> fun to_server/1;
 server(desc) -> ?SERVER_DESC("MySQL", integer_to_list(?MYSQL_DEFAULT_PORT));

+ 2 - 2
apps/emqx_connector/src/emqx_connector_pgsql.erl

@@ -56,11 +56,11 @@ fields(config) ->
     emqx_connector_schema_lib:ssl_fields().
 
 named_queries(type) -> map();
-named_queries(nullable) -> true;
+named_queries(required) -> false;
 named_queries(_) -> undefined.
 
 server(type) -> emqx_schema:ip_port();
-server(nullable) -> false;
+server(required) -> true;
 server(validator) -> [?NOT_EMPTY("the value of the field 'server' cannot be empty")];
 server(converter) -> fun to_server/1;
 server(desc) -> ?SERVER_DESC("PostgreSQL", integer_to_list(?PGSQL_DEFAULT_PORT));

+ 2 - 2
apps/emqx_connector/src/emqx_connector_redis.erl

@@ -76,14 +76,14 @@ fields(sentinel) ->
     emqx_connector_schema_lib:ssl_fields().
 
 server(type) -> emqx_schema:ip_port();
-server(nullable) -> false;
+server(required) -> true;
 server(validator) -> [?NOT_EMPTY("the value of the field 'server' cannot be empty")];
 server(converter) -> fun to_server_raw/1;
 server(desc) -> ?SERVER_DESC("Redis", integer_to_list(?REDIS_DEFAULT_PORT));
 server(_) -> undefined.
 
 servers(type) -> list();
-servers(nullable) -> false;
+servers(required) -> true;
 servers(validator) -> [?NOT_EMPTY("the value of the field 'servers' cannot be empty")];
 servers(converter) -> fun to_servers_raw/1;
 servers(desc) -> ?SERVERS_DESC ++ server(desc);

+ 3 - 3
apps/emqx_connector/src/emqx_connector_schema_lib.erl

@@ -66,7 +66,7 @@ relational_db_fields() ->
     ].
 
 database(type) -> binary();
-database(nullable) -> false;
+database(required) -> true;
 database(validator) -> [?NOT_EMPTY("the value of the field 'database' cannot be empty")];
 database(_) -> undefined.
 
@@ -76,11 +76,11 @@ pool_size(validator) -> [?MIN(1)];
 pool_size(_) -> undefined.
 
 username(type) -> binary();
-username(nullable) -> true;
+username(required) -> false;
 username(_) -> undefined.
 
 password(type) -> binary();
-password(nullable) -> true;
+password(required) -> false;
 password(_) -> undefined.
 
 auto_reconnect(type) -> boolean();

+ 2 - 2
apps/emqx_connector/src/mqtt/emqx_connector_mqtt_schema.erl

@@ -57,7 +57,7 @@ topic filters for 'remote_topic' of ingress connections.
             })}
     , {name,
        sc(binary(),
-          #{ nullable => true
+          #{ required => false
            , desc => "Connector name, used as a human-readable description of the connector."
            })}
     , {server,
@@ -105,7 +105,7 @@ fields("ingress") ->
     %% the message maybe subscribed by rules, in this case 'local_topic' is not necessary
     [ {remote_topic,
         sc(binary(),
-           #{ nullable => false
+           #{ required => true
             , desc => "Receive messages from which topic of the remote broker"
             })}
     , {remote_qos,

+ 2 - 2
apps/emqx_connector/test/emqx_connector_test_helpers.erl

@@ -29,8 +29,8 @@ check_fields({FieldName, FieldValue}) ->
     if
         is_map(FieldValue) ->
             ?assert(
-                maps:is_key(type, FieldValue) and maps:is_key(default, FieldValue) or
-                    (maps:is_key(nullable, FieldValue) and maps:get(nullable, FieldValue, false) =:= true)
+                (maps:is_key(type, FieldValue) andalso maps:is_key(default, FieldValue)) orelse
+                    ((maps:is_key(required, FieldValue) andalso maps:get(required, FieldValue)))
             );
         true ->
             ?assert(is_function(FieldValue))

+ 5 - 5
apps/emqx_dashboard/src/emqx_dashboard_schema.erl

@@ -48,7 +48,7 @@ fields("http") ->
     [ {"protocol", sc(
         hoconsc:enum([http, https]),
         #{ desc => "HTTP/HTTPS protocol."
-         , nullable => false
+         , required => true
          , default => http})}
     , {"bind", fun bind/1}
     , {"num_acceptors", sc(
@@ -74,18 +74,18 @@ fields("https") ->
 
 bind(type) -> hoconsc:union([non_neg_integer(), emqx_schema:ip_port()]);
 bind(default) -> 18083;
-bind(nullable) -> false;
+bind(required) -> true;
 bind(desc) -> "Port without IP(18083) or port with specified IP(127.0.0.1:18083).";
 bind(_) -> undefined.
 
 default_username(type) -> string();
 default_username(default) -> "admin";
-default_username(nullable) -> false;
+default_username(required) -> true;
 default_username(_) -> undefined.
 
 default_password(type) -> string();
 default_password(default) -> "public";
-default_password(nullable) -> false;
+default_password(required) -> true;
 default_password(sensitive) -> true;
 default_password(desc) -> """
 The initial default password for dashboard 'admin' user.
@@ -94,7 +94,7 @@ default_password(_) -> undefined.
 
 cors(type) -> boolean();
 cors(default) -> false;
-cors(nullable) -> true;
+cors(required) -> false;
 cors(desc) ->
 """Support Cross-Origin Resource Sharing (CORS).
 Allows a server to indicate any origins (domain, scheme, or port) other than

+ 16 - 22
apps/emqx_dashboard/src/emqx_dashboard_swagger.erl

@@ -56,7 +56,10 @@
 -type(filter_result() :: {ok, request()} | {400, 'BAD_REQUEST', binary()}).
 -type(filter() :: fun((request(), request_meta()) -> filter_result())).
 
--type(spec_opts() :: #{check_schema => boolean() | filter(), translate_body => boolean()}).
+-type(spec_opts() :: #{check_schema => boolean() | filter(),
+                       translate_body => boolean(),
+                       schema_converter => fun((hocon_schema:schema(), Module::atom()) -> map())
+                       }).
 
 -type(route_path() :: string() | binary()).
 -type(route_methods() :: map()).
@@ -217,7 +220,7 @@ check_request_body(#{body := Body}, Schema, Module, CheckFun, true) ->
             _ -> Type0
         end,
     NewSchema = ?INIT_SCHEMA#{roots => [{root, Type}]},
-    Option = #{nullable => true},
+    Option = #{required => false},
     #{<<"root">> := NewBody} = CheckFun(NewSchema, #{<<"root">> => Body}, Option),
     NewBody;
 %% TODO not support nest object check yet, please use ref!
@@ -265,30 +268,22 @@ parameters(Params, Module) ->
                     In = hocon_schema:field_schema(Type, in),
                     In =:= undefined andalso
                         throw({error, <<"missing in:path/query field in parameters">>}),
-                    Nullable = hocon_schema:field_schema(Type, nullable),
+                    Required = hocon_schema:field_schema(Type, required),
                     Default = hocon_schema:field_schema(Type, default),
                     HoconType = hocon_schema:field_schema(Type, type),
-                    Meta = init_meta(Nullable, Default),
+                    Meta = init_meta(Default),
                     {ParamType, Refs} = hocon_schema_to_spec(HoconType, Module),
                     Spec0 = init_prop([required | ?DEFAULT_FIELDS],
                         #{schema => maps:merge(ParamType, Meta), name => Name, in => In}, Type),
-                    Spec1 = trans_required(Spec0, Nullable, In),
+                    Spec1 = trans_required(Spec0, Required, In),
                     Spec2 = trans_desc(Spec1, Type),
                     {[Spec2 | Acc], Refs ++ RefsAcc}
             end
                     end, {[], []}, Params),
     {lists:reverse(SpecList), AllRefs}.
 
-init_meta(Nullable, Default) ->
-    Init =
-        case Nullable of
-            true -> #{nullable => true};
-            _ -> #{}
-        end,
-    case Default =:= undefined of
-        true -> Init;
-        false -> Init#{default => Default}
-    end.
+init_meta(undefined) -> #{};
+init_meta(Default) -> #{default => Default}.
 
 init_prop(Keys, Init, Type) ->
     lists:foldl(fun(Key, Acc) ->
@@ -298,7 +293,7 @@ init_prop(Keys, Init, Type) ->
         end
                 end, Init, Keys).
 
-trans_required(Spec, false, _) -> Spec#{required => true};
+trans_required(Spec, true, _) -> Spec#{required => true};
 trans_required(Spec, _, path) -> Spec#{required => true};
 trans_required(Spec, _, _) -> Spec.
 
@@ -335,7 +330,7 @@ response(Status, #{content := _} = Content, {Acc, RefsAcc, Module, Options}) ->
 response(Status, ?REF(StructName), {Acc, RefsAcc, Module, Options}) ->
     response(Status, ?R_REF(Module, StructName), {Acc, RefsAcc, Module, Options});
 response(Status, ?R_REF(_Mod, _Name) = RRef, {Acc, RefsAcc, Module, Options}) ->
-    SchemaToSpec = schema_to_spec_func(Options),
+    SchemaToSpec = schema_converter(Options),
     {Spec, Refs} = SchemaToSpec(RRef, Module),
     Content = content(Spec),
     {Acc#{integer_to_binary(Status) => #{<<"content">> => Content}}, Refs ++ RefsAcc, Module, Options};
@@ -557,7 +552,7 @@ parse_object(PropList = [_ | _], Module, Options) when is_list(PropList) ->
                     HoconType = hocon_schema:field_schema(Hocon, type),
                     Init0 = init_prop([default | ?DEFAULT_FIELDS], #{}, Hocon),
                     Init = trans_desc(Init0, Hocon),
-                    SchemaToSpec = schema_to_spec_func(Options),
+                    SchemaToSpec = schema_converter(Options),
                     {Prop, Refs1} = SchemaToSpec(HoconType, Module),
                     NewRequiredAcc =
                         case is_required(Hocon) of
@@ -581,8 +576,7 @@ parse_object(Other, Module, Options) ->
             args => Other, module => Module, options => Options}}).
 
 is_required(Hocon) ->
-    hocon_schema:field_schema(Hocon, required) =:= true orelse
-        hocon_schema:field_schema(Hocon, nullable) =:= false.
+    hocon_schema:field_schema(Hocon, required) =:= true.
 
 content(ApiSpec) ->
     content(ApiSpec, undefined).
@@ -596,5 +590,5 @@ to_ref(Mod, StructName, Acc, RefsAcc) ->
     Ref = #{<<"$ref">> => ?TO_COMPONENTS_PARAM(Mod, StructName)},
     {[Ref | Acc], [{Mod, StructName, parameter} | RefsAcc]}.
 
-schema_to_spec_func(Options) ->
-    maps:get(schema_to_spec_func, Options, fun hocon_schema_to_spec/2).
+schema_converter(Options) ->
+    maps:get(schema_converter, Options, fun hocon_schema_to_spec/2).

+ 3 - 4
apps/emqx_dashboard/test/emqx_swagger_parameter_SUITE.erl

@@ -124,8 +124,7 @@ t_nullable(_Config) ->
     NullableFalse = [#{in => query,name => userid, required => true,
         schema => #{example => <<"binary-example">>, type => string}}],
     NullableTrue = [#{in => query,name => userid,
-        schema => #{example => <<"binary-example">>, type => string,
-            nullable => true}}],
+        schema => #{example => <<"binary-example">>, type => string}, required => false}],
     validate("/nullable/false", NullableFalse),
     validate("/nullable/true", NullableTrue),
     ok.
@@ -358,9 +357,9 @@ schema("/test/without/in") ->
 schema("/required/false") ->
     to_schema([{'userid', mk(binary(), #{in => query, required => false})}]);
 schema("/nullable/false") ->
-    to_schema([{'userid', mk(binary(), #{in => query, nullable => false})}]);
+    to_schema([{'userid', mk(binary(), #{in => query, required => true})}]);
 schema("/nullable/true") ->
-    to_schema([{'userid', mk(binary(), #{in => query, nullable => true})}]);
+    to_schema([{'userid', mk(binary(), #{in => query, required => false})}]);
 schema("/method/ok") ->
     Response = #{responses => #{200 => <<"ok">>}},
     lists:foldl(fun(Method, Acc) ->  Acc#{Method => Response} end,

+ 2 - 2
apps/emqx_dashboard/test/emqx_swagger_remote_schema.erl

@@ -50,10 +50,10 @@ fields("ref3") ->
 
 default_username(type) -> string();
 default_username(default) -> "admin";
-default_username(nullable) -> false;
+default_username(required) -> true;
 default_username(_) -> undefined.
 
 default_password(type) -> string();
 default_password(default) -> "public";
-default_password(nullable) -> false;
+default_password(required) -> true;
 default_password(_) -> undefined.

+ 4 - 4
apps/emqx_dashboard/test/emqx_swagger_requestBody_SUITE.erl

@@ -499,16 +499,16 @@ paths() ->
 
 schema("/object") ->
     to_schema([
-        {per_page, mk(range(1, 100), #{nullable => false, desc => <<"good per page desc">>})},
+        {per_page, mk(range(1, 100), #{required => true, desc => <<"good per page desc">>})},
         {timeout, mk(hoconsc:union([infinity, emqx_schema:duration_s()]),
-            #{default => 5, nullable => false})},
+            #{default => 5, required => true})},
         {inner_ref, mk(hoconsc:ref(?MODULE, good_ref), #{})}
     ]);
 schema("/nest/object") ->
     to_schema([
         {per_page, mk(range(1, 100), #{desc => <<"good per page desc">>})},
         {timeout, mk(hoconsc:union([infinity, emqx_schema:duration_s()]),
-            #{default => 5, nullable => false})},
+            #{default => 5, required => true})},
         {nest_object, [
             {good_nest_1, mk(integer(), #{})},
             {good_nest_2, mk(hoconsc:ref(?MODULE, good_ref), #{})}
@@ -572,5 +572,5 @@ enable(_) -> undefined.
 
 init_file(type) -> binary();
 init_file(desc) -> <<"test test desc">>;
-init_file(nullable) -> true;
+init_file(required) -> false;
 init_file(_) -> undefined.

+ 4 - 4
apps/emqx_dashboard/test/emqx_swagger_response_SUITE.erl

@@ -299,9 +299,9 @@ schema("/simple/bin") ->
     to_schema(<<"binary ok">>);
 schema("/object") ->
     Object = [
-        {per_page, mk(range(1, 100), #{nullable => false, desc => <<"good per page desc">>})},
+        {per_page, mk(range(1, 100), #{required => true, desc => <<"good per page desc">>})},
         {timeout, mk(hoconsc:union([infinity, emqx_schema:duration_s()]),
-            #{default => 5, nullable => false})},
+            #{default => 5, required => true})},
         {inner_ref, mk(hoconsc:ref(?MODULE, good_ref), #{})}
     ],
     to_schema(Object);
@@ -309,7 +309,7 @@ schema("/nest/object") ->
     Response = [
         {per_page, mk(range(1, 100), #{desc => <<"good per page desc">>})},
         {timeout, mk(hoconsc:union([infinity, emqx_schema:duration_s()]),
-            #{default => 5, nullable => false})},
+            #{default => 5, required => true})},
         {nest_object, [
             {good_nest_1, mk(integer(), #{})},
             {good_nest_2, mk(hoconsc:ref(?MODULE, good_ref), #{})}
@@ -425,5 +425,5 @@ enable(_) -> undefined.
 
 init_file(type) -> binary();
 init_file(desc) -> <<"test test desc">>;
-init_file(nullable) -> true;
+init_file(required) -> false;
 init_file(_) -> undefined.

+ 7 - 7
apps/emqx_gateway/src/emqx_gateway_api.erl

@@ -219,7 +219,7 @@ params_gateway_status_in_qs() ->
     [{status,
       mk(binary(),
          #{ in => query
-          , nullable => true
+          , required => false
           , desc => <<"Gateway Status">>
           , example => <<"">>
           })}
@@ -245,11 +245,11 @@ fields(gateway_overview) ->
           #{desc => <<"The Gateway created datetime">>})}
     , {started_at,
        mk(binary(),
-          #{ nullable => true
+          #{ required => false
            , desc => <<"The Gateway started datetime">>})}
     , {stopped_at,
        mk(binary(),
-          #{ nullable => true
+          #{ required => false
            , desc => <<"The Gateway stopped datetime">>})}
     , {max_connections,
        mk(integer(),
@@ -260,7 +260,7 @@ fields(gateway_overview) ->
            })}
     , {listeners,
        mk(hoconsc:array(ref(gateway_listener_overview)),
-         #{ nullable => {true, recursively}
+         #{ required => {false, recursively}
           , desc => <<"The Gateway listeners overview">>})}
     ];
 fields(gateway_listener_overview) ->
@@ -295,7 +295,7 @@ fields(Listener) when Listener == tcp_listener;
                       Listener == dtls_listener ->
     [ {id,
        mk(binary(),
-          #{ nullable => true
+          #{ required => false
            , desc => <<"Listener ID">>})}
     , {type,
        mk(hoconsc:union([tcp, ssl, udp, dtls]),
@@ -305,7 +305,7 @@ fields(Listener) when Listener == tcp_listener;
           #{ desc => <<"Listener Name">>})}
     , {running,
        mk(boolean(),
-          #{ nullable => true
+          #{ required => false
            , desc => <<"Listener running status">>})}
     ] ++ emqx_gateway_schema:fields(Listener);
 
@@ -334,7 +334,7 @@ convert_listener_struct(Schema) ->
     {value, {listeners,
              #{type := Type}}, Schema1} = lists:keytake(listeners, 1, Schema),
     ListenerSchema = hoconsc:mk(listeners_schema(Type),
-                                #{ nullable => {true, recursively}
+                                #{ required => {false, recursively}
                                  , desc => <<"The gateway listeners">>
                                  }),
     lists:keystore(listeners, 1, Schema1, {listeners, ListenerSchema}).

+ 2 - 2
apps/emqx_gateway/src/emqx_gateway_api_authn.erl

@@ -287,13 +287,13 @@ params_userid_in_path() ->
 params_paging_in_qs() ->
     [{page, mk(integer(),
                #{ in => query
-                , nullable => true
+                , required => false
                 , desc => <<"Page Index">>
                 , example => 1
                 })},
      {limit, mk(integer(),
                 #{ in => query
-                 , nullable => true
+                 , required => false
                  , desc => <<"Page Limit">>
                  , example => 100
                  })}

+ 3 - 3
apps/emqx_gateway/src/emqx_gateway_api_clients.erl

@@ -468,7 +468,7 @@ params_client_insta() ->
     ++ params_gateway_name_in_path().
 
 params_client_searching_in_qs() ->
-    M = #{in => query, nullable => true, example => <<"">>},
+    M = #{in => query, required => false, example => <<"">>},
     [ {node,
        mk(binary(),
           M#{desc => <<"Match the client's node name">>})}
@@ -532,7 +532,7 @@ params_paging() ->
     [ {page,
        mk(integer(),
           #{ in => query
-           , nullable => true
+           , required => false
            , desc => <<"Page Index">>
            , example => 1
            })}
@@ -540,7 +540,7 @@ params_paging() ->
         mk(integer(),
            #{ in => query
             , desc => <<"Page Limit">>
-            , nullable => true
+            , required => false
             , example => 100
             })}
     ].

+ 18 - 18
apps/emqx_gateway/src/emqx_gateway_api_listeners.erl

@@ -434,13 +434,13 @@ params_userid_in_path() ->
 params_paging_in_qs() ->
     [{page, mk(integer(),
                #{ in => query
-                , nullable => true
+                , required => false
                 , desc => <<"Page Index">>
                 , example => 1
                 })},
      {limit, mk(integer(),
                 #{ in => query
-                 , nullable => true
+                 , required => false
                  , desc => <<"Page Limit">>
                  , example => 100
                  })}
@@ -457,22 +457,22 @@ fields(listener) ->
     common_listener_opts() ++
     [ {tcp,
        mk(ref(tcp_listener_opts),
-          #{ nullable => {true, recursively}
+          #{ required => {false, recursively}
            , desc => <<"The tcp socket options for tcp or ssl listener">>
            })}
     , {ssl,
        mk(ref(ssl_listener_opts),
-          #{ nullable => {true, recursively}
+          #{ required => {false, recursively}
            , desc => <<"The ssl socket options for ssl listener">>
            })}
     , {udp,
        mk(ref(udp_listener_opts),
-          #{ nullable => {true, recursively}
+          #{ required => {false, recursively}
            , desc => <<"The udp socket options for udp or dtls listener">>
            })}
     , {dtls,
        mk(ref(dtls_listener_opts),
-          #{ nullable => {true, recursively}
+          #{ required => {false, recursively}
            , desc => <<"The dtls socket options for dtls listener">>
            })}
     ];
@@ -529,47 +529,47 @@ lists_key_without([K | Ks], N, L) ->
 common_listener_opts() ->
     [ {enable,
        mk(boolean(),
-          #{ nullable => true
+          #{ required => false
            , desc => <<"Whether to enable this listener">>})}
     , {id,
        mk(binary(),
-          #{ nullable => true
+          #{ required => false
            , desc => <<"Listener Id">>})}
     , {name,
        mk(binary(),
-          #{ nullable => true
+          #{ required => false
            , desc => <<"Listener name">>})}
     , {type,
        mk(hoconsc:enum([tcp, ssl, udp, dtls]),
-          #{ nullable => true
+          #{ required => false
            , desc => <<"Listener type. Enum: tcp, udp, ssl, dtls">>})}
     , {running,
        mk(boolean(),
-          #{ nullable => true
+          #{ required => false
            , desc => <<"Listener running status">>})}
     , {bind,
        mk(binary(),
-          #{ nullable => true
+          #{ required => false
            , desc => <<"Listener bind address or port">>})}
     , {acceptors,
        mk(integer(),
-          #{ nullable => true
+          #{ required => false
            , desc => <<"Listener acceptors number">>})}
     , {access_rules,
        mk(hoconsc:array(binary()),
-          #{ nullable => true
+          #{ required => false
            , desc => <<"Listener Access rules for client">>})}
     , {max_conn_rate,
        mk(integer(),
-          #{ nullable => true
+          #{ required => false
            , desc => <<"Max connection rate for the listener">>})}
     , {max_connections,
        mk(integer(),
-          #{ nullable => true
+          #{ required => false
            , desc => <<"Max connections for the listener">>})}
     , {mountpoint,
        mk(binary(),
-          #{ nullable => true
+          #{ required => false
            , desc =>
 <<"The Mounpoint for clients of the listener. "
   "The gateway-level mountpoint configuration can be overloaded "
@@ -577,7 +577,7 @@ common_listener_opts() ->
     %% FIXME:
     , {authentication,
        mk(emqx_authn_schema:authenticator_type(),
-          #{ nullable => {true, recursively}
+          #{ required => {false, recursively}
            , desc => <<"The authenticatior for this listener">>
            })}
     ] ++ emqx_gateway_schema:proxy_protocol_opts().

+ 22 - 22
apps/emqx_gateway/src/emqx_gateway_schema.erl

@@ -59,21 +59,21 @@ roots() -> [gateway].
 fields(gateway) ->
     [{stomp,
       sc(ref(stomp),
-         #{ nullable => {true, recursively}
+         #{ required => {false, recursively}
           , desc =>
 "The Stomp Gateway configuration.<br>
 This gateway supports v1.2/1.1/1.0"
           })},
      {mqttsn,
       sc(ref(mqttsn),
-         #{ nullable => {true, recursively}
+         #{ required => {false, recursively}
           , desc =>
 "The MQTT-SN Gateway configuration.<br>
 This gateway only supports the v1.2 protocol"
           })},
      {coap,
       sc(ref(coap),
-         #{ nullable => {true, recursively}
+         #{ required => {false, recursively}
           , desc =>
 "The CoAP Gateway configuration.<br>
 This gateway is implemented based on RFC-7252 and
@@ -81,14 +81,14 @@ https://core-wg.github.io/coap-pubsub/draft-ietf-core-pubsub.html"
           })},
      {lwm2m,
       sc(ref(lwm2m),
-         #{ nullable => {true, recursively}
+         #{ required => {false, recursively}
           , desc =>
 "The LwM2M Gateway configuration.<br>
 This gateway only supports the v1.0.1 protocol"
           })},
      {exproto,
       sc(ref(exproto),
-         #{ nullable => {true, recursively}
+         #{ required => {false, recursively}
           , desc => "The Extension Protocol configuration"
           })}
     ];
@@ -120,7 +120,7 @@ fields(mqttsn) ->
     [ {gateway_id,
        sc(integer(),
           #{ default => 1
-           , nullable => false
+           , required => true
            , desc =>
 "MQTT-SN Gateway ID.<br>
 When the <code>broadcast</code> option is enabled,
@@ -145,7 +145,7 @@ The client just sends its 'PUBLISH' messages to a GW"
     , {predefined,
        sc(hoconsc:array(ref(mqttsn_predefined)),
           #{ default => []
-           , nullable => {true, recursively}
+           , required => {false, recursively}
            , desc =>
 <<"The pre-defined topic IDs and topic names.<br>
 A 'pre-defined' topic ID is a topic ID whose mapping to a topic name
@@ -221,7 +221,7 @@ fields(lwm2m) ->
     [ {xml_dir,
        sc(binary(),
           #{ default =>"etc/lwm2m_xml"
-           , nullable => false
+           , required => true
            , desc => "The Directory for LwM2M Resource definition"
            })}
     , {lifetime_min,
@@ -261,7 +261,7 @@ beyond this time window are temporarily stored in memory."
            })}
     , {translators,
        sc(ref(lwm2m_translators),
-          #{ nullable => false
+          #{ required => true
            , desc => "Topic configuration for LwM2M's gateway publishing and subscription"
            })}
     , {listeners, sc(ref(udp_listeners))}
@@ -270,12 +270,12 @@ beyond this time window are temporarily stored in memory."
 fields(exproto) ->
     [ {server,
        sc(ref(exproto_grpc_server),
-          #{ nullable => false
+          #{ required => true
            , desc => "Configurations for starting the <code>ConnectionAdapter</code> service"
            })}
     , {handler,
        sc(ref(exproto_grpc_handler),
-          #{ nullable => false
+          #{ required => true
            , desc => "Configurations for request to <code>ConnectionHandler</code> service"
            })}
     , {listeners, sc(ref(udp_tcp_listeners))}
@@ -284,18 +284,18 @@ fields(exproto) ->
 fields(exproto_grpc_server) ->
     [ {bind,
        sc(hoconsc:union([ip_port(), integer()]),
-          #{nullable => false})}
+          #{required => true})}
     , {ssl,
        sc(ref(ssl_server_opts),
-          #{ nullable => {true, recursively}
+          #{ required => {false, recursively}
            })}
     ];
 
 fields(exproto_grpc_handler) ->
-    [ {address, sc(binary(), #{nullable => false})}
+    [ {address, sc(binary(), #{required => true})}
     , {ssl,
        sc(ref(ssl_client_opts),
-          #{ nullable => {true, recursively}
+          #{ required => {false, recursively}
            })}
     ];
 
@@ -324,13 +324,13 @@ fields(lwm2m_translators) ->
 For each new LwM2M client that succeeds in going online, the gateway creates
 a subscription relationship to receive downstream commands and send it to
 the LwM2M client"
-           , nullable => false
+           , required => true
            })}
     , {response,
        sc(ref(translator),
           #{ desc =>
 "The topic for gateway to publish the acknowledge events from LwM2M client"
-           , nullable => false
+           , required => true
            })}
     , {notify,
        sc(ref(translator),
@@ -338,24 +338,24 @@ the LwM2M client"
 "The topic for gateway to publish the notify events from LwM2M client.<br>
  After succeed observe a resource of LwM2M client, Gateway will send the
  notify events via this topic, if the client reports any resource changes"
-           , nullable => false
+           , required => true
            })}
     , {register,
        sc(ref(translator),
           #{ desc =>
 "The topic for gateway to publish the register events from LwM2M client.<br>"
-           , nullable => false
+           , required => true
            })}
     , {update,
        sc(ref(translator),
           #{ desc =>
 "The topic for gateway to publish the update events from LwM2M client.<br>"
-           , nullable => false
+           , required => true
            })}
     ];
 
 fields(translator) ->
-    [ {topic, sc(binary(), #{nullable => false})}
+    [ {topic, sc(binary(), #{required => true})}
     , {qos, sc(emqx_schema:qos(), #{default => 0})}
     ];
 
@@ -423,7 +423,7 @@ fields(dtls_opts) ->
 
 authentication_schema() ->
     sc(emqx_authn_schema:authenticator_type(),
-       #{ nullable => {true, recursively}
+       #{ required => {false, recursively}
         , desc =>
 """Default authentication configs for all the gateway listeners.<br>
 For per-listener overrides see <code>authentication</code>

+ 1 - 1
apps/emqx_management/src/emqx_mgmt_api_alarms.erl

@@ -44,7 +44,7 @@ schema("/alarms") ->
                 hoconsc:ref(emqx_dashboard_swagger, limit),
                 {activated, hoconsc:mk(boolean(), #{in => query,
                     desc => <<"All alarms, if not specified">>,
-                    nullable => true})}
+                    required => false})}
             ],
             responses => #{
                 200 => [

+ 3 - 3
apps/emqx_management/src/emqx_mgmt_api_app.erl

@@ -94,7 +94,7 @@ fields(app) ->
         {expired_at, hoconsc:mk(hoconsc:union([undefined, emqx_datetime:epoch_second()]),
             #{desc => "No longer valid datetime",
                 example => <<"2021-12-05T02:01:34.186Z">>,
-                nullable => true,
+                required => false,
                 default => undefined
             })},
         {created_at, hoconsc:mk(emqx_datetime:epoch_second(),
@@ -102,8 +102,8 @@ fields(app) ->
                 example => <<"2021-12-01T00:00:00.000Z">>
             })},
         {desc, hoconsc:mk(binary(),
-            #{example => <<"Note">>, nullable => true})},
-        {enable, hoconsc:mk(boolean(), #{desc => "Enable/Disable", nullable => true})}
+            #{example => <<"Note">>, required => false})},
+        {enable, hoconsc:mk(boolean(), #{desc => "Enable/Disable", required => false})}
     ];
 fields(name) ->
     [{name, hoconsc:mk(binary(),

+ 6 - 6
apps/emqx_management/src/emqx_mgmt_api_banned.erl

@@ -101,27 +101,27 @@ fields(ban) ->
     [
         {as, hoconsc:mk(hoconsc:enum(?BANNED_TYPES), #{
             desc => <<"Banned type clientid, username, peerhost">>,
-            nullable => false,
+            required => true,
             example => username})},
         {who, hoconsc:mk(binary(), #{
             desc => <<"Client info as banned type">>,
-            nullable => false,
+            required => true,
             example => <<"Banned name"/utf8>>})},
         {by, hoconsc:mk(binary(), #{
             desc => <<"Commander">>,
-            nullable => true,
+            required => false,
             example => <<"mgmt_api">>})},
         {reason, hoconsc:mk(binary(), #{
             desc => <<"Banned reason">>,
-            nullable => true,
+            required => false,
             example => <<"Too many requests">>})},
         {at, hoconsc:mk(emqx_datetime:epoch_second(), #{
             desc => <<"Create banned time, rfc3339, now if not specified">>,
-            nullable => true,
+            required => false,
             example => <<"2021-10-25T21:48:47+08:00">>})},
         {until, hoconsc:mk(emqx_datetime:epoch_second(), #{
             desc => <<"Cancel banned time, rfc3339, now + 5 minute if not specified">>,
-            nullable => true,
+            required => false,
             example => <<"2021-10-25T21:53:47+08:00">>})
         }
     ];

+ 14 - 14
apps/emqx_management/src/emqx_mgmt_api_clients.erl

@@ -93,65 +93,65 @@ schema("/clients") ->
                 hoconsc:ref(emqx_dashboard_swagger, limit),
                 {node, hoconsc:mk(binary(), #{
                     in => query,
-                    nullable => true,
+                    required => false,
                     desc => <<"Node name">>,
                     example => atom_to_list(node())})},
                 {username, hoconsc:mk(binary(), #{
                     in => query,
-                    nullable => true,
+                    required => false,
                     desc => <<"User name">>})},
                 {zone, hoconsc:mk(binary(), #{
                     in => query,
-                    nullable => true})},
+                    required => false})},
                 {ip_address, hoconsc:mk(binary(), #{
                     in => query,
-                    nullable => true,
+                    required => false,
                     desc => <<"Client's IP address">>,
                     example => <<"127.0.0.1">>})},
                 {conn_state, hoconsc:mk(hoconsc:enum([connected, idle, disconnected]), #{
                     in => query,
-                    nullable => true,
+                    required => false,
                     desc => <<"The current connection status of the client, ",
                         "the possible values are connected,idle,disconnected">>})},
                 {clean_start, hoconsc:mk(boolean(), #{
                     in => query,
-                    nullable => true,
+                    required => false,
                     description => <<"Whether the client uses a new session">>})},
                 {proto_name, hoconsc:mk(hoconsc:enum(['MQTT', 'CoAP', 'LwM2M', 'MQTT-SN']), #{
                     in => query,
-                    nullable => true,
+                    required => false,
                     description => <<"Client protocol name, ",
                         "the possible values are MQTT,CoAP,LwM2M,MQTT-SN">>})},
                 {proto_ver, hoconsc:mk(binary(), #{
                     in => query,
-                    nullable => true,
+                    required => false,
                     desc => <<"Client protocol version">>})},
                 {like_clientid, hoconsc:mk(binary(), #{
                     in => query,
-                    nullable => true,
+                    required => false,
                     desc => <<"Fuzzy search of client identifier by substring method">>})},
                 {like_username, hoconsc:mk(binary(), #{
                     in => query,
-                    nullable => true,
+                    required => false,
                     desc => <<"Client user name, fuzzy search by substring">>})},
                 {gte_created_at, hoconsc:mk(emqx_datetime:epoch_millisecond(), #{
                     in => query,
-                    nullable => true,
+                    required => false,
                     desc => <<"Search client session creation time by greater",
                         " than or equal method, rfc3339 or timestamp(millisecond)">>})},
                 {lte_created_at, hoconsc:mk(emqx_datetime:epoch_millisecond(), #{
                     in => query,
-                    nullable => true,
+                    required => false,
                     desc => <<"Search client session creation time by less",
                         " than or equal method, rfc3339 or timestamp(millisecond)">>})},
                 {gte_connected_at, hoconsc:mk(emqx_datetime:epoch_millisecond(), #{
                     in => query,
-                    nullable => true,
+                    required => false,
                     desc => <<"Search client connection creation time by greater"
                         " than or equal method, rfc3339 or timestamp(epoch millisecond)">>})},
                 {lte_connected_at, hoconsc:mk(emqx_datetime:epoch_millisecond(), #{
                     in => query,
-                    nullable => true,
+                    required => false,
                     desc => <<"Search client connection creation time by less"
                         " than or equal method, rfc3339 or timestamp(millisecond)">>})}
             ],

+ 1 - 1
apps/emqx_management/src/emqx_mgmt_api_configs.erl

@@ -67,7 +67,7 @@ schema("/configs") ->
                         desc =>
  <<"Node's name: If you do not fill in the fields, this node will be used by default.">>})}],
             responses => #{
-                200 => config_list([])
+                200 => config_list(?EXCLUDES)
             }
         }
     };

+ 1 - 1
apps/emqx_management/src/emqx_mgmt_api_metrics.erl

@@ -71,7 +71,7 @@ schema("/metrics") ->
                   [{ aggregate
                    , mk( boolean()
                        , #{ in => query
-                          , nullable => true
+                          , required => false
                           , desc => <<"Whether to aggregate all nodes Metrics">>})
                    }]
             , responses =>

+ 2 - 2
apps/emqx_management/src/emqx_mgmt_api_nodes.erl

@@ -140,7 +140,7 @@ fields(node_info) ->
       , mk( emqx_schema:bytesize()
           , #{desc => <<"Used memory">>, example => "256.00M"})}
     , { node_status
-      , mk( enum(["Running", "Stopped"])
+      , mk( enum(['Running', 'Stopped'])
           , #{desc => <<"Node status">>, example => "Running"})}
     , { otp_release
       , mk( string()
@@ -164,7 +164,7 @@ fields(node_info) ->
       , mk( string()
           , #{desc => <<"Path to log files">>, example => "path/to/log | not found"})}
     , { role
-      , mk( enum(["core", "replicant"])
+      , mk( enum([core, replicant])
           , #{desc => <<"Node role">>, example => "core"})}
     ].
 

+ 1 - 1
apps/emqx_management/src/emqx_mgmt_api_stats.erl

@@ -59,7 +59,7 @@ fields(aggregate) ->
       , mk( boolean()
           , #{ desc => <<"Calculation aggregate for all nodes">>
              , in => query
-             , nullable => true
+             , required => false
              , example => false})}
     ];
 fields(node_stats_data) ->

+ 6 - 6
apps/emqx_management/src/emqx_mgmt_api_subscriptions.erl

@@ -75,38 +75,38 @@ parameters() ->
         {
             node, hoconsc:mk(binary(), #{
             in => query,
-            nullable => true,
+            required => false,
             desc => <<"Node name">>,
             example => atom_to_list(node())})
         },
         {
             clientid, hoconsc:mk(binary(), #{
             in => query,
-            nullable => true,
+            required => false,
             desc => <<"Client ID">>})
         },
         {
             qos, hoconsc:mk(emqx_schema:qos(), #{
             in => query,
-            nullable => true,
+            required => false,
             desc => <<"QoS">>})
         },
         {
             topic, hoconsc:mk(binary(), #{
             in => query,
-            nullable => true,
+            required => false,
             desc => <<"Topic, url encoding">>})
         },
         {
             match_topic, hoconsc:mk(binary(), #{
             in => query,
-            nullable => true,
+            required => false,
             desc => <<"Match topic string, url encoding">>})
         },
         {
             share_group, hoconsc:mk(binary(), #{
             in => query,
-            nullable => true,
+            required => false,
             desc => <<"Shared subscription group name">>})
         }
     ].

+ 12 - 12
apps/emqx_management/src/emqx_mgmt_api_trace.erl

@@ -144,45 +144,45 @@ fields(trace) ->
         {name, hoconsc:mk(binary(),
             #{desc => "Unique and format by [a-zA-Z0-9-_]",
                 validator => fun ?MODULE:validate_name/1,
-                nullable => false,
+                required => true,
                 example => <<"EMQX-TRACE-1">>})},
         {type, hoconsc:mk(hoconsc:enum([clientid, topic, ip_address]),
             #{desc => """Filter type""",
-                nullable => false,
+                required => true,
                 example => <<"clientid">>})},
         {topic, hoconsc:mk(binary(),
             #{desc => """support mqtt wildcard topic.""",
-                nullable => true,
+                required => false,
                 example => <<"/dev/#">>})},
         {clientid, hoconsc:mk(binary(),
             #{desc => """mqtt clientid.""",
-                nullable => true,
+                required => false,
                 example => <<"dev-001">>})},
         %% TODO add ip_address type in emqx_schema.erl
         {ip_address, hoconsc:mk(binary(),
             #{desc => "client ip address",
-                nullable => true,
+                required => false,
                 example => <<"127.0.0.1">>
             })},
         {status, hoconsc:mk(hoconsc:enum([running, stopped, waiting]),
             #{desc => "trace status",
-                nullable => true,
+                required => false,
                 example => running
             })},
         {start_at, hoconsc:mk(emqx_datetime:epoch_second(),
             #{desc => "rfc3339 timestamp or epoch second",
-                nullable => true,
+                required => false,
                 example => <<"2021-11-04T18:17:38+08:00">>
             })},
         {end_at, hoconsc:mk(emqx_datetime:epoch_second(),
             #{desc => "rfc3339 timestamp or epoch second",
-                nullable => true,
+                required => false,
                 example => <<"2021-11-05T18:17:38+08:00">>
             })},
         {log_size, hoconsc:mk(hoconsc:array(map()),
             #{desc => "trace log size",
                 example => [#{<<"node">> => <<"emqx@127.0.0.1">>, <<"size">> => 1024}],
-                nullable => true})}
+                required => false})}
     ];
 fields(name) ->
     [{name, hoconsc:mk(binary(),
@@ -198,14 +198,14 @@ fields(node) ->
         #{
             desc => "Node name",
             in => query,
-            nullable => true
+            required => false
         })}];
 fields(bytes) ->
     [{bytes, hoconsc:mk(integer(),
         #{
             desc => "Maximum number of bytes to store in request",
             in => query,
-            nullable => true,
+            required => false,
             default => 1000
         })}];
 fields(position) ->
@@ -213,7 +213,7 @@ fields(position) ->
         #{
             desc => "Offset from the current trace position.",
             in => query,
-            nullable => true,
+            required => false,
             default => 0
         })}].
 

+ 1 - 1
apps/emqx_management/test/emqx_mgmt_api_trace_SUITE.erl

@@ -58,7 +58,7 @@ t_http_test(_Config) ->
     ?assertEqual(
         #{
             <<"code">> => <<"BAD_REQUEST">>,
-            <<"message">> => <<"name : not_nullable">>
+            <<"message">> => <<"name : mandatory_required_field">>
         }, json(Body)),
 
     Name = <<"test-name">>,

+ 6 - 6
apps/emqx_modules/src/emqx_topic_metrics_api.erl

@@ -121,12 +121,12 @@ fields(reset) ->
                 <<"Topic Name. If this parameter is not present,"
                   " all created topic metrics will be reset">>
              , example => <<"testtopic/1">>
-             , nullable => true})}
+             , required => false})}
     , {action
       , mk( string()
           , #{ desc => <<"Action Name. Only as a \"reset\"">>
              , enum => [reset]
-             , nullable => false
+             , required => true
              , example => <<"reset">>})}
     ];
 
@@ -135,21 +135,21 @@ fields(topic_metrics) ->
       , mk( binary()
           , #{ desc => <<"Topic Name">>
              , example => <<"testtopic/1">>
-             , nullable => false})},
+             , required => true})},
       { create_time
       , mk( emqx_datetime:epoch_second()
           , #{ desc => <<"Topic Metrics created date time, in rfc3339">>
-             , nullable => false
+             , required => true
              , example => <<"2022-01-14T21:48:47+08:00">>})},
       { reset_time
       , mk( emqx_datetime:epoch_second()
           , #{ desc => <<"Topic Metrics reset date time, in rfc3339. Nullable if never reset">>
-             , nullable => true
+             , required => false
              , example => <<"2022-01-14T21:48:47+08:00">>})},
       { metrics
       , mk( ref(metrics)
           , #{ desc => <<"Topic Metrics fields">>
-             , nullable => false})
+             , required => true})
       }
     ];
 

+ 4 - 4
apps/emqx_plugins/src/emqx_plugins_schema.erl

@@ -54,12 +54,12 @@ state_fields() ->
                              "It should match the plugin application name-version as the "
                              "for the plugin release package name<br>"
                              "For example: my_plugin-0.1.0."
-                   , nullable => false
+                   , required => true
                    })}
     , {enable,
        hoconsc:mk(boolean(),
                   #{ desc => "Set to 'true' to enable this plugin"
-                   , nullable => false
+                   , required => true
                    })}
     ].
 
@@ -69,14 +69,14 @@ root_fields() ->
     ].
 
 states(type) -> hoconsc:array(hoconsc:ref(state));
-states(nullable) -> true;
+states(required) -> false;
 states(default) -> [];
 states(desc) -> "An array of plugins in the desired states.<br>"
                 "The plugins are started in the defined order";
 states(_) -> undefined.
 
 install_dir(type) -> string();
-install_dir(nullable) -> true;
+install_dir(required) -> false;
 install_dir(default) -> "plugins"; %% runner's root dir
 install_dir(T) when T =/= desc -> undefined;
 install_dir(desc) -> """

+ 1 - 1
apps/emqx_prometheus/rebar.config

@@ -4,7 +4,7 @@
  [ {emqx, {path, "../emqx"}},
    %% FIXME: tag this as v3.1.3
    {prometheus, {git, "https://github.com/deadtrickster/prometheus.erl", {tag, "v4.8.1"}}},
-   {hocon, {git, "https://github.com/emqx/hocon.git", {tag, "0.24.0"}}}
+   {hocon, {git, "https://github.com/emqx/hocon.git", {tag, "0.25.0"}}}
  ]}.
 
 {edoc_opts, [{preprocess, true}]}.

+ 1 - 1
apps/emqx_psk/src/emqx_psk_schema.erl

@@ -60,7 +60,7 @@ init_file(desc) ->
       "The file has to be structured line-by-line, each line must be in ",
       "the format of <code>PSKIdentity:SharedSecret</code>. For example: ",
       "<code>mydevice1:c2VjcmV0</code>">>;
-init_file(nullable) -> true;
+init_file(required) -> false;
 init_file(_) -> undefined.
 
 separator(type) -> binary();

+ 2 - 2
apps/emqx_resource/test/emqx_test_resource.erl

@@ -35,11 +35,11 @@ roots() -> [{name, fun name/1},
             {register, fun register/1}].
 
 name(type) -> atom();
-name(nullable) -> false;
+name(required) -> true;
 name(_) -> undefined.
 
 register(type) -> boolean();
-register(nullable) -> false;
+register(required) -> true;
 register(default) -> false;
 register(_) -> undefined.
 

+ 11 - 11
apps/emqx_rule_engine/src/emqx_rule_api_schema.erl

@@ -55,7 +55,7 @@ fields("rule_info") ->
 %% TODO: we can delete this API if the Dashboard not denpends on it
 fields("rule_events") ->
     ETopics = [binary_to_atom(emqx_rule_events:event_topic(E)) || E <- emqx_rule_events:event_names()],
-    [ {"event", sc(hoconsc:enum(ETopics), #{desc => "The event topics", nullable => false})}
+    [ {"event", sc(hoconsc:enum(ETopics), #{desc => "The event topics", required => true})}
     , {"title", sc(binary(), #{desc => "The title", example => "some title"})}
     , {"description", sc(binary(), #{desc => "The description", example => "some desc"})}
     , {"columns", sc(map(), #{desc => "The columns"})}
@@ -75,7 +75,7 @@ fields("rule_test") ->
                                    ]),
         #{desc => "The context of the event for testing",
           default => #{}})}
-    , {"sql", sc(binary(), #{desc => "The SQL of the rule for testing", nullable => false})}
+    , {"sql", sc(binary(), #{desc => "The SQL of the rule for testing", required => true})}
     ];
 
 fields("metrics") ->
@@ -121,7 +121,7 @@ fields("node_metrics") ->
     ] ++ fields("metrics");
 
 fields("ctx_pub") ->
-    [ {"event_type", sc(message_publish, #{desc => "Event Type", nullable => false})}
+    [ {"event_type", sc(message_publish, #{desc => "Event Type", required => true})}
     , {"id", sc(binary(), #{desc => "Message ID"})}
     , {"clientid", sc(binary(), #{desc => "The Client ID"})}
     , {"username", sc(binary(), #{desc => "The User Name"})}
@@ -133,7 +133,7 @@ fields("ctx_pub") ->
     ] ++ [qos()];
 
 fields("ctx_sub") ->
-    [ {"event_type", sc(session_subscribed, #{desc => "Event Type", nullable => false})}
+    [ {"event_type", sc(session_subscribed, #{desc => "Event Type", required => true})}
     , {"clientid", sc(binary(), #{desc => "The Client ID"})}
     , {"username", sc(binary(), #{desc => "The User Name"})}
     , {"payload", sc(binary(), #{desc => "The Message Payload"})}
@@ -144,11 +144,11 @@ fields("ctx_sub") ->
     ] ++ [qos()];
 
 fields("ctx_unsub") ->
-    [{"event_type", sc(session_unsubscribed, #{desc => "Event Type", nullable => false})}] ++
+    [{"event_type", sc(session_unsubscribed, #{desc => "Event Type", required => true})}] ++
     proplists:delete("event_type", fields("ctx_sub"));
 
 fields("ctx_delivered") ->
-    [ {"event_type", sc(message_delivered, #{desc => "Event Type", nullable => false})}
+    [ {"event_type", sc(message_delivered, #{desc => "Event Type", required => true})}
     , {"id", sc(binary(), #{desc => "Message ID"})}
     , {"from_clientid", sc(binary(), #{desc => "The Client ID"})}
     , {"from_username", sc(binary(), #{desc => "The User Name"})}
@@ -162,11 +162,11 @@ fields("ctx_delivered") ->
     ] ++ [qos()];
 
 fields("ctx_acked") ->
-    [{"event_type", sc(message_acked, #{desc => "Event Type", nullable => false})}] ++
+    [{"event_type", sc(message_acked, #{desc => "Event Type", required => true})}] ++
     proplists:delete("event_type", fields("ctx_delivered"));
 
 fields("ctx_dropped") ->
-    [ {"event_type", sc(message_dropped, #{desc => "Event Type", nullable => false})}
+    [ {"event_type", sc(message_dropped, #{desc => "Event Type", required => true})}
     , {"id", sc(binary(), #{desc => "Message ID"})}
     , {"reason", sc(binary(), #{desc => "The Reason for Dropping"})}
     , {"clientid", sc(binary(), #{desc => "The Client ID"})}
@@ -179,7 +179,7 @@ fields("ctx_dropped") ->
     ] ++ [qos()];
 
 fields("ctx_connected") ->
-    [ {"event_type", sc(client_connected, #{desc => "Event Type", nullable => false})}
+    [ {"event_type", sc(client_connected, #{desc => "Event Type", required => true})}
     , {"clientid", sc(binary(), #{desc => "The Client ID"})}
     , {"username", sc(binary(), #{desc => "The User Name"})}
     , {"mountpoint", sc(binary(), #{desc => "The Mountpoint"})}
@@ -196,7 +196,7 @@ fields("ctx_connected") ->
     ];
 
 fields("ctx_disconnected") ->
-    [ {"event_type", sc(client_disconnected, #{desc => "Event Type", nullable => false})}
+    [ {"event_type", sc(client_disconnected, #{desc => "Event Type", required => true})}
     , {"clientid", sc(binary(), #{desc => "The Client ID"})}
     , {"username", sc(binary(), #{desc => "The User Name"})}
     , {"reason", sc(binary(), #{desc => "The Reason for Disconnect"})}
@@ -211,7 +211,7 @@ qos() ->
 
 rule_id() ->
     {"id", sc(binary(),
-        #{ desc => "The ID of the rule", nullable => false
+        #{ desc => "The ID of the rule", required => true
          , example => "293fb66f"
          })}.
 

+ 3 - 3
apps/emqx_rule_engine/src/emqx_rule_engine_schema.erl

@@ -46,7 +46,7 @@ SQL query to transform the messages.<br>
 Example: <code>SELECT * FROM \"test/topic\" WHERE payload.x = 1</code><br>
 """
          , example => "SELECT * FROM \"test/topic\" WHERE payload.x = 1"
-         , nullable => false
+         , required => true
          , validator => fun ?MODULE:validate_sql/1
          })}
     , {"outputs", sc(hoconsc:array(hoconsc:union(outputs())),
@@ -143,7 +143,7 @@ fields("republish_args") ->
 The target topic of message to be re-published.<br>
 Template with variables is allowed, see description of the 'republish_args'.
 """
-          , nullable => false
+          , required => true
           , example => <<"a/1">>
           })}
     , {qos, sc(qos(),
@@ -182,7 +182,7 @@ rule_name() ->
     {"name", sc(binary(),
         #{ desc => "The name of the rule"
          , default => ""
-         , nullable => false
+         , required => true
          , example => "foo"
          })}.
 

+ 3 - 3
apps/emqx_statsd/src/emqx_statsd_schema.erl

@@ -33,19 +33,19 @@ namespace() -> "statsd".
 roots() -> ["statsd"].
 
 fields("statsd") ->
-    [ {enable, hoconsc:mk(boolean(), #{default => false, nullable => false})}
+    [ {enable, hoconsc:mk(boolean(), #{default => false, required => true})}
     , {server, fun server/1}
     , {sample_time_interval, fun duration_ms/1}
     , {flush_time_interval, fun duration_ms/1}
     ].
 
 server(type) -> emqx_schema:ip_port();
-server(nullable) -> false;
+server(required) -> true;
 server(default) -> "127.0.0.1:8125";
 server(_) -> undefined.
 
 duration_ms(type) -> emqx_schema:duration_ms();
-duration_ms(nullable) -> false;
+duration_ms(required) -> true;
 duration_ms(default) -> "10s";
 duration_ms(_) -> undefined.
 

+ 1 - 1
mix.exs

@@ -68,7 +68,7 @@ defmodule EMQXUmbrella.MixProject do
       # in conflict by emqtt and hocon
       {:getopt, "1.0.2", override: true},
       {:snabbkaffe, github: "kafka4beam/snabbkaffe", tag: "0.18.0", override: true},
-      {:hocon, github: "emqx/hocon", tag: "0.24.0", override: true},
+      {:hocon, github: "emqx/hocon", tag: "0.25.0", override: true},
       {:emqx_http_lib, github: "emqx/emqx_http_lib", tag: "0.4.1", override: true},
       {:esasl, github: "emqx/esasl", tag: "0.2.0"},
       {:jose, github: "potatosalad/erlang-jose", tag: "1.11.2"},

+ 1 - 1
rebar.config

@@ -66,7 +66,7 @@
     , {system_monitor, {git, "https://github.com/ieQu1/system_monitor", {tag, "3.0.2"}}}
     , {getopt, "1.0.2"}
     , {snabbkaffe, {git, "https://github.com/kafka4beam/snabbkaffe.git", {tag, "0.18.0"}}}
-    , {hocon, {git, "https://github.com/emqx/hocon.git", {tag, "0.24.0"}}}
+    , {hocon, {git, "https://github.com/emqx/hocon.git", {tag, "0.25.0"}}}
     , {emqx_http_lib, {git, "https://github.com/emqx/emqx_http_lib.git", {tag, "0.4.1"}}}
     , {esasl, {git, "https://github.com/emqx/esasl", {tag, "0.2.0"}}}
     , {jose, {git, "https://github.com/potatosalad/erlang-jose", {tag, "1.11.2"}}}

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 5392 - 0
schema_v1.json