diff --git a/rebar.config b/rebar.config index fc14962..59e145e 100644 --- a/rebar.config +++ b/rebar.config @@ -106,4 +106,8 @@ {base_plt_prefix, "sumo_db"} ]}. -{plugins, [rebar3_hex]}. \ No newline at end of file +{plugins, [rebar3_hex]}. + +%% == Shell == + +{shell, [{apps, [sumo_db]}]}. \ No newline at end of file diff --git a/src/adapter_test_helpers/sumo_basic_test_helper.erl b/src/adapter_test_helpers/sumo_basic_test_helper.erl index 50141ae..0f96c2e 100644 --- a/src/adapter_test_helpers/sumo_basic_test_helper.erl +++ b/src/adapter_test_helpers/sumo_basic_test_helper.erl @@ -229,9 +229,8 @@ init_store(Name) -> %%% Internal functions %%%============================================================================= -pick_up_event({Name, _, _} = Event) -> - EventManager = sumo_config:get_event_manager(Name), - EventManager:pick_up_event(Event). +pick_up_event(Event) -> + sumo_test_people_events_manager:pick_up_event(Event). create(Name, Args) -> Res = sumo:persist(Name, Args), diff --git a/src/adapter_test_helpers/sumo_events_test_helper.erl b/src/adapter_test_helpers/sumo_events_test_helper.erl index cae39c4..953b777 100644 --- a/src/adapter_test_helpers/sumo_events_test_helper.erl +++ b/src/adapter_test_helpers/sumo_events_test_helper.erl @@ -12,12 +12,13 @@ -spec events_manager_supervisor_running(Config :: config()) -> ok. events_manager_supervisor_running(Config) -> {_, DocName} = lists:keyfind(name, 1, Config), - - case sumo_config:get_event_manager(DocName) of - undefined -> + case sumo_config:get_event_managers(DocName) of + [] -> ok; - EventManager -> - ct:comment("~p should be running", [EventManager]), - [EventManager] = gen_event:which_handlers(EventManager), + EventManagers -> + ct:comment("~p should be running", [EventManagers]), + EventManagers = [begin + [E] = gen_event:which_handlers(EM), E + end || EM <- EventManagers], ok end. diff --git a/src/adapter_test_helpers/sumo_test_people_events_manager.erl b/src/adapter_test_helpers/sumo_test_people_events_manager.erl index 0a25cea..6e41f7e 100644 --- a/src/adapter_test_helpers/sumo_test_people_events_manager.erl +++ b/src/adapter_test_helpers/sumo_test_people_events_manager.erl @@ -18,9 +18,7 @@ -spec pick_up_event(tuple()) -> ok | no_event. pick_up_event(Event) -> - EventMgr = proplists:get_value( - people, application:get_env(sumo_db, events, [])), - gen_event:call(EventMgr, ?MODULE, {pick_up_event, Event}). + gen_event:call(?MODULE, ?MODULE, {pick_up_event, Event}). -spec init([]) -> {ok, state()}. init([]) -> diff --git a/src/sumo_config.erl b/src/sumo_config.erl index 8393a48..845941c 100644 --- a/src/sumo_config.erl +++ b/src/sumo_config.erl @@ -28,10 +28,15 @@ get_store/1, get_props/1, get_prop_value/2, + get_prop_value/3, get_events/0, - get_event_manager/1 + get_event_managers/0, + get_event_managers/1, + add_event_managers/2 ]). +-include_lib("stdlib/include/ms_transform.hrl"). + %%%=================================================================== %%% Types %%%=================================================================== @@ -51,28 +56,18 @@ -spec init() -> ok. init() -> - sumo_config = ets:new(sumo_config, [ - protected, - named_table, - {read_concurrency, true} - ]), - Docs = application:get_env(sumo_db, docs, []), - Events = application:get_env(sumo_db, events, []), - NewDocs = lists:foldl(fun({DocName, EventManager}, Acc) -> - case lists:keyfind(DocName, 1, Acc) of - {DocName, Store, Props} -> - NewDoc = {DocName, Store, Props#{event_manager => EventManager}}, - lists:keyreplace(DocName, 1, Acc, NewDoc); - _ -> - Acc - end - end, Docs, Events), - true = ets:insert(sumo_config, NewDocs), - ok. + case ets:info(?MODULE) of + undefined -> + do_init(); + _ -> + true = ets:delete(?MODULE), + do_init() + end. -spec get_docs() -> [doc_config()]. get_docs() -> - application:get_env(sumo_db, docs, []). + MS = ets:fun2ms(fun({X, Y, Z}) -> {X, Y, Z} end), + ets:select(?MODULE, MS). -spec get_doc(atom()) -> doc_config() | undefined. get_doc(DocName) -> @@ -91,23 +86,83 @@ get_props(DocName) -> -spec get_prop_value(atom(), atom()) -> term(). get_prop_value(DocName, Prop) -> - maps:get(Prop, get_props(DocName), undefined). + get_prop_value(DocName, Prop, undefined). + +-spec get_prop_value(atom(), atom(), term()) -> term(). +get_prop_value(DocName, Prop, Default) -> + maps:get(Prop, get_props(DocName), Default). -spec get_events() -> [event_config()]. get_events() -> application:get_env(sumo_db, events, []). --spec get_event_manager(atom()) -> module(). -get_event_manager(DocName) -> - get_prop_value(DocName, event_manager). +-spec get_event_managers() -> [module()]. +get_event_managers() -> + lookup_element('$event_managers', 2, []). + +-spec get_event_managers(atom()) -> [module()]. +get_event_managers(DocName) -> + get_prop_value(DocName, event_managers, []). + +-spec add_event_managers(atom(), [module()] | module()) -> [doc_config()]. +add_event_managers(DocName, EventManagers) -> + update_doc_entry(DocName, EventManagers, get_docs()). %%%=================================================================== %%% Internal functions %%%=================================================================== +%% @private +do_init() -> + ?MODULE = ets:new(?MODULE, [named_table, public, {read_concurrency, true}]), + Docs = application:get_env(sumo_db, docs, []), + Events = application:get_env(sumo_db, events, []), + UpdatedDocs = load_entry(Docs, Events), + EvManagers = load_event_managers(UpdatedDocs), + Entries = [{'$event_managers', EvManagers} | UpdatedDocs], + true = ets:insert(?MODULE, Entries), + ok. + +%% @private +load_entry(Docs, Events) -> + lists:foldl(fun({DocName, EventManagers}, DocAcc) -> + update_doc_entry(DocName, EventManagers, DocAcc) + end, Docs, Events). + +%% @private +load_event_managers(Docs) -> + lists:usort(lists:foldl(fun({_, _, Props}, Acc) -> + maps:get(event_managers, Props, []) ++ Acc + end, [], Docs)). + +%% @private +update_doc_entry(_DocName, [], Docs) -> + Docs; +update_doc_entry('_', EventManagers, Docs) when is_list(EventManagers) -> + DocNames = [DocName || {DocName, _, _} <- Docs], + lists:foldl(fun(DocName, DocAcc) -> + update_doc_entry(DocName, EventManagers, DocAcc) + end, Docs, DocNames); +update_doc_entry(DocName, EventManagers, Docs) when is_list(EventManagers) -> + case lists:keyfind(DocName, 1, Docs) of + {DocName, Store, Props} -> + CurrentEventManagers = maps:get(event_managers, Props, []), + NewEventManagers = lists:usort(EventManagers ++ CurrentEventManagers), + NewDoc = {DocName, Store, Props#{event_managers => NewEventManagers}}, + lists:keyreplace(DocName, 1, Docs, NewDoc); + _ -> + Docs + end; +update_doc_entry(DocName, EventManager, Docs) when is_atom(EventManager) -> + update_doc_entry(DocName, [EventManager], Docs). + %% @private lookup_element(Key, Pos) -> + lookup_element(Key, Pos, undefined). + +%% @private +lookup_element(Key, Pos, Default) -> try ets:lookup_element(?MODULE, Key, Pos) catch - _:_ -> undefined + _:_ -> Default end. diff --git a/src/sumo_event.erl b/src/sumo_event.erl index 0562f52..f390e86 100644 --- a/src/sumo_event.erl +++ b/src/sumo_event.erl @@ -36,7 +36,11 @@ dispatch(DocName, Event) -> %% @doc Dispatch an event through gen_event:notify/2. -spec dispatch(sumo:schema_name(), term(), term()) -> ok. dispatch(DocName, Event, Args) -> - case sumo_config:get_event_manager(DocName) of - undefined -> ok; - EventManager -> gen_event:notify(EventManager, {DocName, Event, Args}) + case sumo_config:get_event_managers(DocName) of + [] -> + ok; + EventManagers -> + lists:foreach(fun(EventManager) -> + gen_event:notify(EventManager, {DocName, Event, Args}) + end, EventManagers) end. diff --git a/src/sumo_event_manager_sup.erl b/src/sumo_event_manager_sup.erl index de1f7bb..762aab4 100644 --- a/src/sumo_event_manager_sup.erl +++ b/src/sumo_event_manager_sup.erl @@ -43,9 +43,8 @@ start_link() -> supervisor:start_link({local, ?MODULE}, ?MODULE, []). -spec init(any()) -> {ok, {supervisor:sup_flags(), [supervisor:child_spec()]}}. init([]) -> - Events = sumo_config:get_events(), - EventsManagers = lists:usort([Manager || {_, Manager} <- Events]), - ManagersList = [manager(EventManager) || EventManager <- EventsManagers], + EventManagers = sumo_config:get_event_managers(), + ManagersList = [manager(EventManager) || EventManager <- EventManagers], {ok, {{one_for_one, 5, 10}, ManagersList}}. %%%============================================================================= diff --git a/test/conditional_logic_SUITE.erl b/test/conditional_logic_SUITE.erl index 8aaac73..081640a 100644 --- a/test/conditional_logic_SUITE.erl +++ b/test/conditional_logic_SUITE.erl @@ -48,4 +48,5 @@ init_per_suite(Config) -> -spec end_per_suite(config()) -> config(). end_per_suite(Config) -> + _ = application:stop(sumo_db), Config. diff --git a/test/sumo_basic_SUITE.erl b/test/sumo_basic_SUITE.erl index 2cd07ca..7729649 100644 --- a/test/sumo_basic_SUITE.erl +++ b/test/sumo_basic_SUITE.erl @@ -44,9 +44,6 @@ all() -> -spec init_per_suite(config()) -> config(). init_per_suite(Config) -> ok = sumo_test_utils:start_apps(), - EventMgr = proplists:get_value( - people, application:get_env(sumo_db, events, [])), - gen_event:add_handler(EventMgr, sumo_test_people_events_manager, []), [{name, people} | Config]. -spec init_per_testcase(atom(), config()) -> config(). @@ -57,4 +54,5 @@ init_per_testcase(_, Config) -> -spec end_per_suite(config()) -> config(). end_per_suite(Config) -> + _ = application:stop(sumo_db), Config. diff --git a/test/sumo_config_SUITE.erl b/test/sumo_config_SUITE.erl new file mode 100644 index 0000000..ebeb1f1 --- /dev/null +++ b/test/sumo_config_SUITE.erl @@ -0,0 +1,149 @@ +-module(sumo_config_SUITE). + +%% CT +-export([ + all/0, + init_per_suite/1, + end_per_suite/1, + init_per_testcase/2 +]). + +%% Test Cases +-export([ + t_sumo_config/1, + t_reload_sumo_config/1 +]). + +-define(EXCLUDED_FUNS, [ + module_info, + all, + init_per_suite, + init_per_testcase +]). + +-type config() :: [{atom(), term()}]. + +%%%============================================================================= +%%% Common Test +%%%============================================================================= + +-spec all() -> [atom()]. +all() -> + Exports = ?MODULE:module_info(exports), + [F || {F, _} <- Exports, not lists:member(F, ?EXCLUDED_FUNS)]. + +-spec init_per_suite(config()) -> config(). +init_per_suite(Config) -> + ok = sumo_test_utils:start_apps(), + Docs = application:get_env(sumo_db, docs, []), + Events = application:get_env(sumo_db, events, []), + [{docs, Docs}, {events, Events} | Config]. + +-spec end_per_suite(config()) -> config(). +end_per_suite(Config) -> + cleanup_config(Config), + _ = application:stop(sumo_db), + Config. + +-spec init_per_testcase(atom(), config()) -> config(). +init_per_testcase(_, Config) -> + setup_config(), + Config. + +%%%============================================================================= +%%% Test Cases +%%%============================================================================= + +-spec t_sumo_config(config()) -> ok. +t_sumo_config(_Config) -> + validate_docs(), + + % expected doc managers + ExpectedManagersDoc1 = [ + ev_manager1, + super_ev_manager + ], + ExpectedManagersDoc2 = [ + ev_manager1, + ev_manager2, + super_ev_manager + ], + ExpectedManagersDoc3 = [ + super_ev_manager + ], + + % check event managers + ExpectedManagersDoc1 = sumo_config:get_event_managers(doc1), + ExpectedManagersDoc2 = sumo_config:get_event_managers(doc2), + ExpectedManagersDoc3 = sumo_config:get_event_managers(doc3), + ExpectedManagersDoc3 = sumo_config:get_event_managers(doc4), + + ok. + +-spec t_reload_sumo_config(config()) -> ok. +t_reload_sumo_config(_Config) -> + validate_docs(), + + % set other manager and reload it + NewDocs = [{docX, storeX, #{module => mod1}} | docs()], + application:set_env(sumo_db, docs, NewDocs), + application:set_env(sumo_db, events, [ + {docX, ev_managerX}, + {'_', super_ev_manager} + ]), + sumo_config:init(), + + % expected doc managers + ExpectedManagersDoc1 = [ + ev_managerX, + super_ev_manager + ], + ExpectedManagersDoc2 = [ + super_ev_manager + ], + + % check event managers + ExpectedManagersDoc2 = sumo_config:get_event_managers(doc1), + ExpectedManagersDoc2 = sumo_config:get_event_managers(doc2), + ExpectedManagersDoc2 = sumo_config:get_event_managers(doc3), + ExpectedManagersDoc2 = sumo_config:get_event_managers(doc4), + ExpectedManagersDoc1 = sumo_config:get_event_managers(docX), + + ok. + +%%%============================================================================= +%%% Internal functions +%%%============================================================================= + +%% @private +setup_config() -> + ok = application:set_env(sumo_db, docs, docs()), + ok = application:set_env(sumo_db, events, events()), + sumo_config:init(). + +%% @private +cleanup_config(Config) -> + Docs = sumo_utils:keyfind(docs, Config), + Events = sumo_utils:keyfind(events, Config), + ok = application:set_env(sumo_db, docs, Docs), + ok = application:set_env(sumo_db, events, Events). + +%% @private +docs() -> + [{doc1, store1, #{module => mod1}}, + {doc2, store2, #{module => mod2}}, + {doc3, store3, #{module => mod3}}, + {doc4, store4, #{module => mod4}}]. + +%% @private +events() -> + [{doc1, ev_manager1}, + {doc2, [ev_manager1, ev_manager2]}, + {doc4, []}, + {'_', super_ev_manager}]. + +%% @private +validate_docs() -> + Docs = lists:usort([DocName || {DocName, _, _} <- docs()]), + Docs = lists:usort([DocName || {DocName, _, _} <- sumo_config:get_docs()]), + ok. diff --git a/test/sumo_events_SUITE.erl b/test/sumo_events_SUITE.erl index 52714bf..3cf81fb 100644 --- a/test/sumo_events_SUITE.erl +++ b/test/sumo_events_SUITE.erl @@ -27,4 +27,5 @@ init_per_suite(Config) -> -spec end_per_suite(Config::config()) -> config(). end_per_suite(Config) -> + _ = application:stop(sumo_db), Config. diff --git a/test/sumo_test_utils.erl b/test/sumo_test_utils.erl index cd50444..fdcf961 100644 --- a/test/sumo_test_utils.erl +++ b/test/sumo_test_utils.erl @@ -11,4 +11,11 @@ start_apps() -> end, {ok, _} = application:ensure_all_started(mnesia), {ok, _} = application:ensure_all_started(sumo_db), + init_events(), ok. + +%% @private +init_events() -> + lists:foreach(fun(EventManager) -> + gen_event:add_handler(EventManager, EventManager, []) + end, sumo_config:get_event_managers()). diff --git a/test/test.config b/test/test.config index fe582de..3597acf 100644 --- a/test/test.config +++ b/test/test.config @@ -17,7 +17,7 @@ {people, sumo_test_mnesia, #{module => sumo_test_people_mnesia}} ]}, {events, [ - {people, sumo_test_people_events_manager} + {'_', sumo_test_people_events_manager} ]} ]},