Skip to content

Commit

Permalink
feat(schema_hooks): introduce new API for union option injections
Browse files Browse the repository at this point in the history
  • Loading branch information
thalesmg committed Mar 15, 2024
1 parent 00fe5de commit 3cefa09
Show file tree
Hide file tree
Showing 2 changed files with 108 additions and 9 deletions.
8 changes: 4 additions & 4 deletions apps/emqx/src/emqx_ds_schema.erl
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ namespace() ->
schema() ->
[
{messages,
ds_schema(ref(builtin), #{
ds_schema(ref(builtin), 'durable_storage.messages', #{
default =>
#{
<<"backend">> => builtin
Expand All @@ -86,7 +86,7 @@ schema() ->
desc => ?DESC(messages)
})},
{sessions,
ds_schema(ref(builtin_session), #{
ds_schema(ref(builtin_session), 'durable_storage.sessions', #{
default =>
#{
<<"backend">> => builtin
Expand Down Expand Up @@ -288,11 +288,11 @@ desc(_) ->
%% Internal functions
%%================================================================================

ds_schema(MainRef, Options) ->
ds_schema(MainRef, InjectionPoint, Options) ->
sc(
hoconsc:union([
MainRef
| emqx_schema_hooks:injection_point('durable_storage.backends', [])
| emqx_schema_hooks:union_option_injections(InjectionPoint, [])
]),
Options
).
Expand Down
109 changes: 104 additions & 5 deletions apps/emqx/src/emqx_schema_hooks.erl
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,26 @@
#{
hookpoint() => [hocon_schema:field()]
}.
-optional_callbacks([injected_fields/0, injected_fields/1]).
-callback union_option_injections() ->
#{
hookpoint() => [hocon_schema:type()]
}.
-optional_callbacks([
injected_fields/0,
injected_fields/1,
union_option_injections/0
]).

-export_type([hookpoint/0]).

-define(HOOKPOINT_PT_KEY(POINT_NAME), {?MODULE, fields, POINT_NAME}).
-define(UNION_OPTION_PT_KEY(POINT_NAME), {?MODULE, union_options, POINT_NAME}).

-export([
injection_point/1,
injection_point/2,
union_option_injections/1,
union_option_injections/2,
inject_from_modules/1
]).

Expand All @@ -56,12 +67,22 @@ injection_point(PointName) ->
injection_point(PointName, Default) ->
persistent_term:get(?HOOKPOINT_PT_KEY(PointName), Default).

-spec union_option_injections(hookpoint()) -> [hocon_schema:field()].
union_option_injections(PointName) ->
union_option_injections(PointName, []).

-spec union_option_injections(hookpoint(), [hocon_schema:type()]) -> [hocon_schema:type()].
union_option_injections(PointName, Default) ->
persistent_term:get(?UNION_OPTION_PT_KEY(PointName), Default).

-spec erase_injections() -> ok.
erase_injections() ->
lists:foreach(
fun
({?HOOKPOINT_PT_KEY(_) = Key, _}) ->
persistent_term:erase(Key);
({?UNION_OPTION_PT_KEY(_) = Key, _}) ->
persistent_term:erase(Key);
(_) ->
ok
end,
Expand All @@ -74,6 +95,8 @@ any_injections() ->
fun
({?HOOKPOINT_PT_KEY(_), _}) ->
true;
({?UNION_OPTION_PT_KEY(_), _}) ->
true;
(_) ->
false
end,
Expand All @@ -82,6 +105,7 @@ any_injections() ->

-spec inject_from_modules([module() | {module(), term()}]) -> ok.
inject_from_modules(Modules) ->
ok = register_union_options(Modules),
Injections =
lists:foldl(
fun append_module_injections/2,
Expand All @@ -95,9 +119,21 @@ inject_from_modules(Modules) ->
%%--------------------------------------------------------------------

append_module_injections(Module, AllInjections) when is_atom(Module) ->
append_module_injections(Module:injected_fields(), AllInjections);
ModuleInjections = call_if_implemented(
Module,
injected_fields,
[],
#{}
),
append_module_injections(ModuleInjections, AllInjections);
append_module_injections({Module, Options}, AllInjections) when is_atom(Module) ->
append_module_injections(Module:injected_fields(Options), AllInjections);
ModuleInjections = call_if_implemented(
Module,
injected_fields,
[Options],
#{}
),
append_module_injections(ModuleInjections, AllInjections);
append_module_injections(ModuleInjections, AllInjections) when is_map(ModuleInjections) ->
maps:fold(
fun(PointName, Fields, Acc) ->
Expand All @@ -117,7 +153,7 @@ append_module_injections(ModuleInjections, AllInjections) when is_map(ModuleInje
inject_fields([]) ->
ok;
inject_fields([{PointName, Fields} | Rest]) ->
case any_injections(PointName) of
case any_field_injections(PointName) of
true ->
inject_fields(Rest);
false ->
Expand All @@ -129,5 +165,68 @@ inject_fields(PointName, Fields) ->
Key = ?HOOKPOINT_PT_KEY(PointName),
persistent_term:put(Key, Fields).

any_injections(PointName) ->
any_field_injections(PointName) ->
persistent_term:get(?HOOKPOINT_PT_KEY(PointName), undefined) =/= undefined.

register_union_options(Modules) ->
AllInjections =
lists:foldl(
fun append_union_option_injections/2,
#{},
Modules
),
ok = inject_union_options(maps:to_list(AllInjections)).

inject_union_options([]) ->
ok;
inject_union_options([{PointName, Types} | Rest]) ->
case any_union_option_injections(PointName) of
true ->
inject_union_options(Rest);
false ->
ok = inject_union_options(PointName, Types),
inject_union_options(Rest)
end.

inject_union_options(PointName, Types) ->
Key = ?UNION_OPTION_PT_KEY(PointName),
persistent_term:put(Key, Types).

append_union_option_injections(Module, AllInjections) when is_atom(Module) ->
ModuleInjections = call_if_implemented(
Module,
union_option_injections,
[],
#{}
),
append_union_option_injections(ModuleInjections, AllInjections);
append_union_option_injections({Module, _Args}, AllInjections) when is_atom(Module) ->
AllInjections;
append_union_option_injections(ModuleInjections, AllInjections) when is_map(ModuleInjections) ->
maps:fold(
fun(PointName, Types, Acc) ->
maps:update_with(
PointName,
fun(Types0) ->
Types0 ++ Types
end,
Types,
Acc
)
end,
AllInjections,
ModuleInjections
).

any_union_option_injections(PointName) ->
persistent_term:get(?UNION_OPTION_PT_KEY(PointName), undefined) =/= undefined.

call_if_implemented(Mod, Fun, Args, Default) ->
%% ensure modeule is loaded
_ = Mod:module_info(),
case erlang:function_exported(Mod, Fun, length(Args)) of
true ->
apply(Mod, Fun, Args);
false ->
Default
end.

0 comments on commit 3cefa09

Please sign in to comment.