Skip to content

Commit

Permalink
Merge pull request #214 from emqx/add-tags
Browse files Browse the repository at this point in the history
feat(tags): add support for tagging schemas
  • Loading branch information
thalesmg authored Jan 10, 2023
2 parents ba3d5f6 + 33162b9 commit 10fefe7
Show file tree
Hide file tree
Showing 9 changed files with 159 additions and 11 deletions.
5 changes: 4 additions & 1 deletion sample-schemas/demo_schema3.erl
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,13 @@
-include_lib("typerefl/include/types.hrl").
-behaviour(hocon_schema).

-export([namespace/0, roots/0, fields/1]).
-export([namespace/0, roots/0, fields/1, tags/0]).

namespace() -> ?MODULE.

tags() ->
[<<"tag1">>, <<"another tag">>].

roots() ->
[ {foo, hoconsc:array(hoconsc:ref(foo))}
, {bar, hoconsc:ref(parent)}
Expand Down
36 changes: 36 additions & 0 deletions sample-schemas/demo_schema4.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
%%--------------------------------------------------------------------
%% Copyright (c) 2021-2022 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%%--------------------------------------------------------------------

-module(demo_schema4).
-include_lib("typerefl/include/types.hrl").
-behaviour(hocon_schema).

-export([namespace/0, roots/0, fields/1, tags/0]).

namespace() -> ?MODULE.

roots() ->
ReferenceRoots = [Root || {_BinName, Root} <- hocon_schema:roots(demo_schema5)],
[{myfield, hoconsc:ref(myfield)}] ++
%% reference to another module with tags
ReferenceRoots.

tags() ->
[<<"tag from demo_schema4">>].

fields(myfield) ->
[ {mysubfield, integer()}
].
34 changes: 34 additions & 0 deletions sample-schemas/demo_schema5.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
%%--------------------------------------------------------------------
%% Copyright (c) 2021-2022 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%%--------------------------------------------------------------------

-module(demo_schema5).
-include_lib("typerefl/include/types.hrl").
-behaviour(hocon_schema).

-export([namespace/0, roots/0, fields/1, tags/0]).

namespace() -> ?MODULE.

roots() ->
[ "config"
].

tags() ->
[<<"tag from demo_schema5">>].

fields("config") ->
[ {bool, boolean()}
].
5 changes: 4 additions & 1 deletion sample-schemas/emqx_schema.erl
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,16 @@
bytesize/0, percent/0, file/0,
comma_separated_list/0, bar_separated_list/0, ip_port/0]).

-export([roots/0, fields/1, translations/0, translation/1]).
-export([roots/0, fields/1, translations/0, translation/1, tags/0]).
-export([t/1, t/3, ref/1]).
-export([conf_get/2, conf_get/3, keys/2, filter/1]).
-export([ssl/2, tr_ssl/2, tr_password_hash/2]).

namespace() -> "emqx".

tags() ->
[<<"EMQX">>].

roots() -> ["cluster", "node", "rpc", "log", "lager",
"acl", "mqtt",
{"zone", hoconsc:map("name", hoconsc:ref(?MODULE, "zone"))},
Expand Down
28 changes: 21 additions & 7 deletions src/hocon_schema.erl
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
-callback translation(name()) -> [translation()].
-callback validations() -> [validation()].
-callback desc(name()) -> iodata() | undefined.
-callback tags() -> [tag()].

-optional_callbacks([
namespace/0,
Expand All @@ -74,7 +75,8 @@
translations/0,
translation/1,
validations/0,
desc/1
desc/1,
tags/0
]).

-include("hoconsc.hrl").
Expand Down Expand Up @@ -151,6 +153,7 @@
translations => #{name() => [translation()]},
%% for config integrity checks
validations => [validation()],
tags => [tag()],
namespace => atom()
}.

Expand All @@ -161,6 +164,7 @@
| fun((hocon:config(), map()) -> hocon:config()).
-type validationfun() :: fun((hocon:config()) -> ok | boolean() | {error, term()}).
-type bin_name() :: binary().
-type tag() :: binary().

%% @doc Get translation identified by `Name' from the given schema.
-spec translation(schema(), name()) -> [translation()].
Expand Down Expand Up @@ -255,12 +259,14 @@ fields(Sc, Name) ->
%% @doc Get fields and meta data of the struct for the given struct name.
-spec fields_and_meta(schema(), name()) -> fields().
fields_and_meta(Mod, Name) when is_atom(Mod) ->
case Mod:fields(Name) of
Fields when is_list(Fields) ->
maybe_add_desc(Mod, Name, #{fields => Fields});
Fields ->
ensure_struct_meta(Fields)
end;
Meta =
case Mod:fields(Name) of
Fields when is_list(Fields) ->
maybe_add_desc(Mod, Name, #{fields => Fields});
Fields ->
ensure_struct_meta(Fields)
end,
Meta#{tags => tags(Mod)};
fields_and_meta(#{fields := Fields}, Name) when is_function(Fields) ->
ensure_struct_meta(Fields(Name));
fields_and_meta(#{fields := Fields}, Name) when is_map(Fields) ->
Expand All @@ -279,6 +285,14 @@ maybe_add_desc(Mod, Name, Meta) ->
Meta
end.

tags(Mod) when is_atom(Mod) ->
case erlang:function_exported(Mod, tags, 0) of
true ->
Mod:tags();
false ->
[]
end.

ensure_struct_meta(Fields) when is_list(Fields) -> #{fields => Fields};
ensure_struct_meta(#{fields := _} = Fields) -> Fields.

Expand Down
1 change: 1 addition & 0 deletions src/hocon_schema_json.erl
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ gen_struct(_RootNs, Ns, Name, #{fields := Fields} = Meta, Opts) ->
S0 = #{
full_name => bin(hocon_schema:fmt_ref(Ns, Name)),
paths => [bin(P) || P <- Paths],
tags => maps:get(tags, Meta, []),
fields => fmt_fields(Ns, Fields, Opts)
},
case Meta of
Expand Down
9 changes: 7 additions & 2 deletions src/hocon_schema_md.erl
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

-module(hocon_schema_md).

-export([gen/2]).
-export([gen/2, gen_from_structs/2]).

-include("hoconsc.hrl").
-include("hocon_private.hrl").
Expand All @@ -25,11 +25,14 @@ gen(Schema, undefined) ->
gen(Schema, "# HOCON Document");
gen(Schema, Title) when is_list(Title) orelse is_binary(Title) ->
gen(Schema, #{title => Title, body => <<>>});
gen(Schema, #{title := Title, body := Body} = Opts0) ->
gen(Schema, Opts0) ->
Opts = ensure_env_prefix_opt(Opts0),
File = maps:get(desc_file, Opts, undefined),
Lang = maps:get(lang, Opts, "en"),
Structs = hocon_schema_json:gen(Schema, #{desc_file => File, lang => Lang}),
gen_from_structs(Structs, Opts).

gen_from_structs(Structs, #{title := Title, body := Body} = Opts) ->
[
Title,
"\n",
Expand Down Expand Up @@ -129,6 +132,8 @@ fmt_mapping(Str) -> ["`", Str, "`"].

fmt_type(T) -> hocon_md:code(do_type(T)).

do_type(#{kind := <<Kind/binary>>} = Type) ->
do_type(Type#{kind := binary_to_atom(Kind)});
do_type(#{kind := primitive, name := Name}) ->
Name;
do_type(#{kind := singleton, name := Name}) ->
Expand Down
2 changes: 2 additions & 0 deletions test/hocon_schema_example_tests.erl
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,13 @@
-export([
roots/0,
fields/1,
tags/0,
namespace/0
]).

namespace() -> example.
roots() -> ["example"].
tags() -> [<<"tag1">>, <<"another tag">>].

fields("example") ->
Element = #{
Expand Down
50 changes: 50 additions & 0 deletions test/hocon_schema_json_tests.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
%%--------------------------------------------------------------------
%% Copyright (c) 2022 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%%--------------------------------------------------------------------

-module(hocon_schema_json_tests).

-include_lib("eunit/include/eunit.hrl").
-include_lib("typerefl/include/types.hrl").

tags_test_() ->
[
{"no tags function exported", ?_assertMatch([#{tags := []} | _], gen(demo_schema2))},
{"with tags function exported", fun() ->
Json = gen(demo_schema3),
?assertMatch([#{tags := []} | _], Json),
[_Root | Rest] = Json,
lists:foreach(
fun(Struct) ->
?assertMatch(
#{tags := [<<"tag1">>, <<"another tag">>]},
Struct
)
end,
Rest
)
end},
{"with references to schemas with different tags", fun() ->
Json = gen(demo_schema4),
?assertMatch([#{tags := []}, _, _], Json),
[_Root, Schema4Struct, Schema5Struct] = Json,
?assertMatch(#{tags := [<<"tag from demo_schema4">>]}, Schema4Struct),
?assertMatch(#{tags := [<<"tag from demo_schema5">>]}, Schema5Struct),
ok
end}
].

gen(Schema) ->
hocon_schema_json:gen(Schema).

0 comments on commit 10fefe7

Please sign in to comment.