diff --git a/big_tests/tests/graphql_cets_SUITE.erl b/big_tests/tests/graphql_cets_SUITE.erl index 54a30bba692..6d1475df00c 100644 --- a/big_tests/tests/graphql_cets_SUITE.erl +++ b/big_tests/tests/graphql_cets_SUITE.erl @@ -6,17 +6,19 @@ -import(distributed_helper, [mim/0, mim2/0, rpc/4]). -import(domain_helper, [host_type/1]). -import(mongooseimctl_helper, [rpc_call/3]). --import(graphql_helper, [execute_command/4, get_unauthorized/1, get_ok_value/2]). +-import(graphql_helper, [execute_command/4, get_unauthorized/1, get_ok_value/2, get_not_loaded/1]). all() -> [{group, admin_cets_cli}, {group, admin_cets_http}, - {group, domain_admin_cets}]. + {group, domain_admin_cets}, + {group, cets_not_configured}]. groups() -> [{admin_cets_http, [parallel], admin_cets_tests()}, {admin_cets_cli, [parallel], admin_cets_tests()}, - {domain_admin_cets, [], domain_admin_tests()}]. + {domain_admin_cets, [], domain_admin_tests()}, + {cets_not_configured, [parallel], cets_not_configured_test()}]. admin_cets_tests() -> [has_sm_table_in_info, @@ -37,6 +39,10 @@ domain_admin_tests() -> [domain_admin_get_table_info_test, domain_admin_get_system_info_test]. +cets_not_configured_test() -> + [get_table_info_not_configured_test, + get_system_info_not_configured_test]. + init_per_suite(Config) -> case rpc_call(mongoose_config, get_opt, [[internal_databases, cets, backend], undefined]) of rdbms -> @@ -46,24 +52,49 @@ init_per_suite(Config) -> ok = rpc_call(cets_discovery, wait_for_ready, [mongoose_cets_discovery, 5000]), Config2 ++ distributed_helper:require_rpc_nodes([mim, mim2]); _ -> - {skip, "CETS is not configured with RDBMS"} + Config end. end_per_suite(Config) -> - ensure_bad_node_unregistered(), - escalus:end_per_suite(Config). + case rpc_call(mongoose_config, lookup_opt, [[internal_databases, cets, backend]]) of + {ok, rdbms} -> + ensure_bad_node_unregistered(), + escalus:end_per_suite(Config); + _ -> + ok + end. init_per_group(admin_cets_http, Config) -> - graphql_helper:init_admin_handler(Config); + Config1 = graphql_helper:init_admin_handler(Config), + skip_if_cets_not_configured(Config1); init_per_group(admin_cets_cli, Config) -> - graphql_helper:init_admin_cli(Config); + Config1 = graphql_helper:init_admin_cli(Config), + skip_if_cets_not_configured(Config1); init_per_group(domain_admin_cets, Config) -> - graphql_helper:init_domain_admin_handler(Config). + Config1 = graphql_helper:init_domain_admin_handler(Config), + skip_if_cets_not_configured(Config1); +init_per_group(cets_not_configured, Config) -> + case rpc_call(mongoose_config, lookup_opt, [[internal_databases, cets]]) of + {error, not_found} -> + graphql_helper:init_admin_handler(Config); + {ok, _} -> + {skip, "CETS is configured"} + end. +end_per_group(cets_not_configured, _Config) -> + graphql_helper:clean(); end_per_group(_, _Config) -> graphql_helper:clean(), escalus_fresh:clean(). +skip_if_cets_not_configured(Config) -> + case rpc_call(mongoose_config, lookup_opt, [[internal_databases, cets, backend]]) of + {ok, rdbms} -> + Config; + _ -> + {skip, "CETS is not configured with RDBMS"} + end. + init_per_testcase(has_sm_table_in_info, Config) -> case rpc_call(ejabberd_sm, sm_backend, []) of ejabberd_sm_cets -> @@ -186,6 +217,14 @@ domain_admin_get_table_info_test(Config) -> domain_admin_get_system_info_test(Config) -> get_unauthorized(get_system_info(Config)). +% CETS not configured tests + +get_table_info_not_configured_test(Config) -> + get_not_loaded(get_table_info(Config)). + +get_system_info_not_configured_test(Config) -> + get_not_loaded(get_system_info(Config)). + %-------------------------------------------------------------------------------------------------- % Helpers %-------------------------------------------------------------------------------------------------- diff --git a/big_tests/tests/graphql_helper.erl b/big_tests/tests/graphql_helper.erl index 59443be8dba..4eb23c54dd9 100644 --- a/big_tests/tests/graphql_helper.erl +++ b/big_tests/tests/graphql_helper.erl @@ -200,7 +200,7 @@ get_listener_opts(EpName) -> get_not_loaded(Resp) -> ?assertEqual(<<"deps_not_loaded">>, get_err_code(Resp)), - ?assertEqual(<<"Some of required modules or services are not loaded">>, get_err_msg(Resp)). + ?assertNotEqual(nomatch, binary:match(get_err_msg(Resp), <<"Some of the required">>)). get_err_code(Resp) -> get_value([extensions, code], get_error(1, Resp)). diff --git a/big_tests/tests/graphql_http_upload_SUITE.erl b/big_tests/tests/graphql_http_upload_SUITE.erl index 2310e53a553..5bb9f4b653c 100644 --- a/big_tests/tests/graphql_http_upload_SUITE.erl +++ b/big_tests/tests/graphql_http_upload_SUITE.erl @@ -213,7 +213,7 @@ user_http_upload_not_configured(Config) -> user_http_upload_not_configured(Config, Alice) -> Result = user_get_url(<<"test">>, 123, <<"Test">>, 123, Alice, Config), ?assertEqual(<<"deps_not_loaded">>, get_err_code(Result)), - ?assertEqual(<<"Some of required modules or services are not loaded">>, get_err_msg(Result)). + ?assertEqual(<<"Some of the required modules are not loaded">>, get_err_msg(Result)). % Admin test cases @@ -268,7 +268,7 @@ admin_http_upload_not_configured(Config) -> admin_http_upload_not_configured(Config, Domain) -> Result = admin_get_url(Domain, <<"test">>, 123, <<"Test">>, 123, Config), ?assertEqual(<<"deps_not_loaded">>, get_err_code(Result)), - ?assertEqual(<<"Some of required modules or services are not loaded">>, get_err_msg(Result)). + ?assertEqual(<<"Some of the required modules are not loaded">>, get_err_msg(Result)). domain_admin_get_url_no_permission(Config) -> Result1 = admin_get_url(<<"AAAAA">>, <<"test">>, 123, <<"Test">>, 123, Config), diff --git a/big_tests/tests/graphql_mnesia_SUITE.erl b/big_tests/tests/graphql_mnesia_SUITE.erl index 7c93551f8b9..ef3d34060aa 100644 --- a/big_tests/tests/graphql_mnesia_SUITE.erl +++ b/big_tests/tests/graphql_mnesia_SUITE.erl @@ -8,7 +8,7 @@ -import(mongooseimctl_helper, [rpc_call/3]). -import(graphql_helper, [execute_command/4, execute_user_command/5, user_to_bin/1, get_ok_value/2, get_err_code/1, get_err_value/2, get_unauthorized/1, - get_coercion_err_msg/1]). + get_coercion_err_msg/1, get_not_loaded/1]). -record(mnesia_table_test, {key :: integer(), name :: binary()}). -record(vcard, {us, vcard}). @@ -16,12 +16,14 @@ all() -> [{group, admin_mnesia_cli}, {group, admin_mnesia_http}, - {group, domain_admin_mnesia}]. + {group, domain_admin_mnesia}, + {group, mnesia_not_configured}]. groups() -> [{admin_mnesia_http, [sequence], admin_mnesia_tests()}, {admin_mnesia_cli, [sequence], admin_mnesia_tests()}, - {domain_admin_mnesia, [], domain_admin_tests()}]. + {domain_admin_mnesia, [], domain_admin_tests()}, + {mnesia_not_configured, [parallel], mnesia_not_configured_tests()}]. admin_mnesia_tests() -> [dump_mnesia_table_test, @@ -64,6 +66,18 @@ domain_admin_tests() -> domain_admin_set_master_test, domain_admin_get_info_test]. +mnesia_not_configured_tests() -> + [backup_not_configured_test, + change_nodename_not_configured_test, + dump_not_configured_test, + dump_table_not_configured_test, + install_fallback_not_configured_test, + load_not_configured_test, + restore_not_configured_test, + set_master_not_configured_test, + system_info_not_configured_test + ]. + init_per_suite(Config) -> application:ensure_all_started(jid), ok = mnesia:create_schema([node()]), @@ -76,16 +90,33 @@ end_per_suite(_C) -> mnesia:delete_schema([node()]). init_per_group(admin_mnesia_http, Config) -> - graphql_helper:init_admin_handler(Config); + Config1 = graphql_helper:init_admin_handler(Config), + skip_if_mnesia_not_configured(Config1); init_per_group(admin_mnesia_cli, Config) -> - graphql_helper:init_admin_cli(Config); + Config1 = graphql_helper:init_admin_cli(Config), + skip_if_mnesia_not_configured(Config1); init_per_group(domain_admin_mnesia, Config) -> - graphql_helper:init_domain_admin_handler(Config). - + Config1 = graphql_helper:init_domain_admin_handler(Config), + skip_if_mnesia_not_configured(Config1); +init_per_group(mnesia_not_configured, Config) -> + case rpc_call(mongoose_config, lookup_opt, [[internal_databases, mnesia]]) of + {error, not_found} -> + graphql_helper:init_admin_handler(Config); + {ok, _} -> + {skip, "Mnesia is configured"} + end. end_per_group(_, _Config) -> graphql_helper:clean(), escalus_fresh:clean(). +skip_if_mnesia_not_configured(Config) -> + case rpc_call(mongoose_config, lookup_opt, [[internal_databases, mnesia]]) of + {error, not_found} -> + {skip, "Mnesia is not configured"}; + {ok, _} -> + Config + end. + % Admin tests dump_mnesia_table_test(Config) -> @@ -313,6 +344,38 @@ domain_admin_set_master_test(Config) -> domain_admin_get_info_test(Config) -> get_unauthorized(get_info([<<"running_db_nodes">>], Config)). +backup_not_configured_test(Config) -> + Filename = <<"backup_not_configured_test">>, + get_not_loaded(backup_mnesia(Filename, Config)). + +change_nodename_not_configured_test(Config) -> + Filename1 = <<"change_nodename_not_configured_test">>, + Filename2 = <<"change_nodename2_not_configured_test">>, + ChangeFrom = <<"mongooseim@localhost">>, + ChangeTo = <<"change_nodename_not_configured_test@localhost">>, + get_not_loaded(change_nodename(ChangeFrom, ChangeTo, Filename1, Filename2, Config)). + +dump_not_configured_test(Config) -> + get_not_loaded(dump_mnesia(<<"File">>, Config)). + +dump_table_not_configured_test(Config) -> + get_not_loaded(dump_mnesia_table(<<"File">>, <<"mnesia_table_test">>, Config)). + +install_fallback_not_configured_test(Config) -> + get_not_loaded(install_fallback(<<"Path">>, Config)). + +load_not_configured_test(Config) -> + get_not_loaded(load_mnesia(<<"Path">>, Config)). + +restore_not_configured_test(Config) -> + get_not_loaded(restore_mnesia(<<"Path">>, Config)). + +set_master_not_configured_test(Config) -> + get_not_loaded(set_master(mim(), Config)). + +system_info_not_configured_test(Config) -> + get_not_loaded(get_info([<<"running_db_nodes">>], Config)). + %-------------------------------------------------------------------------------------------------- % Helpers %-------------------------------------------------------------------------------------------------- diff --git a/priv/graphql/schemas/admin/cets.gql b/priv/graphql/schemas/admin/cets.gql index 8f4f3b86acd..5ab9b998706 100644 --- a/priv/graphql/schemas/admin/cets.gql +++ b/priv/graphql/schemas/admin/cets.gql @@ -1,11 +1,11 @@ "Allow admin to get information about CETS status" -type CETSAdminQuery @protected{ +type CETSAdminQuery @use(internal_databases: ["cets"]) @protected{ "Get a list of tables from the local node" tableInfo: [CETSTableInfo] - @protected(type: GLOBAL) + @protected(type: GLOBAL) @use "Get status of CETS" systemInfo: CETSSystemInfo - @protected(type: GLOBAL) + @protected(type: GLOBAL) @use } type CETSTableInfo { diff --git a/priv/graphql/schemas/admin/mnesia.gql b/priv/graphql/schemas/admin/mnesia.gql index 00ce17adc63..dd621d12709 100644 --- a/priv/graphql/schemas/admin/mnesia.gql +++ b/priv/graphql/schemas/admin/mnesia.gql @@ -1,44 +1,44 @@ """ Allow admin to acquire information about mnesia database """ -type MnesiaAdminQuery @protected{ +type MnesiaAdminQuery @use(internal_databases: ["mnesia"]) @protected{ """ Get the information about appropriate mnesia property for a specified key, if no keys are provided all the available properties will be returned """ systemInfo(keys: [String!]): [MnesiaInfo] - @protected(type: GLOBAL) + @protected(type: GLOBAL) @use } """ Allow admin to backup, dump, load, restore and modify mnesia database """ -type MnesiaAdminMutation @protected{ +type MnesiaAdminMutation @use(internal_databases: ["mnesia"]) @protected{ "Set mnesia's master node" setMaster(node: NodeName!): String - @protected(type: GLOBAL) + @protected(type: GLOBAL) @use "Change nodename from 'fromString' to 'toString' in 'source' backup file and create new 'target' backup file" changeNodename(fromString: NodeName!, toString: NodeName!, source: String!, target: String!): String - @protected(type: GLOBAL) + @protected(type: GLOBAL) @use "Save mnesia backup to file 'path'" backup(path: String!): String - @protected(type: GLOBAL) + @protected(type: GLOBAL) @use "Restore mnesia backup from file 'path'" restore(path: String!): String - @protected(type: GLOBAL) + @protected(type: GLOBAL) @use "Dump mnesia to file 'path'" dump(path: String!): String - @protected(type: GLOBAL) + @protected(type: GLOBAL) @use "Dump mnesia table 'table' to file 'path'" dumpTable(path: String!, table: String!): String - @protected(type: GLOBAL) + @protected(type: GLOBAL) @use "Load mnesia from file 'path' that was previously dumped" load(path: String!): String - @protected(type: GLOBAL) + @protected(type: GLOBAL) @use "Install mnesia fallback" installFallback(path: String!): String - @protected(type: GLOBAL) + @protected(type: GLOBAL) @use } union MnesiaInfo = MnesiaStringResponse | MnesiaListResponse | MnesiaIntResponse diff --git a/priv/graphql/schemas/global/use_dir.gql b/priv/graphql/schemas/global/use_dir.gql index ece15d131cd..325be79a5c4 100644 --- a/priv/graphql/schemas/global/use_dir.gql +++ b/priv/graphql/schemas/global/use_dir.gql @@ -6,5 +6,7 @@ directive @use( modules: [String!] = [], "The list of services used by the category or command" services: [String!] = [], + "The list of internal databases used by the category or command" + internal_databases: [String!] = [], "The name of argument that contains domain or host type" arg: String) on FIELD_DEFINITION | OBJECT diff --git a/src/graphql/directive/mongoose_graphql_directive_use.erl b/src/graphql/directive/mongoose_graphql_directive_use.erl index a32cfb21889..7bbe1f972c0 100644 --- a/src/graphql/directive/mongoose_graphql_directive_use.erl +++ b/src/graphql/directive/mongoose_graphql_directive_use.erl @@ -42,21 +42,26 @@ -type use_ctx() :: #{modules := [binary()], services := [binary()], + internal_databases := [binary()], arg => binary(), atom => term()}. +-type dependency_type() :: internal_databases | modules | services. +-type dependency_name() :: binary(). %% @doc Check the collected modules and services and swap the field resolver if any of them %% is not loaded. The new field resolver returns the error that some modules or services %% are not loaded. handle_directive(#directive{id = <<"use">>, args = Args}, #schema_field{} = Field, Ctx) -> - #{modules := Modules, services := Services} = UseCtx = aggregate_use_ctx(Args, Ctx), - UnloadedServices = filter_unloaded_services(Services), - UnloadedModules = filter_unloaded_modules(UseCtx, Ctx, Modules), - case {UnloadedModules, UnloadedServices} of - {[], []} -> + #{modules := Modules, services := Services, internal_databases := DB} = + UseCtx = aggregate_use_ctx(Args, Ctx), + Items = [{modules, filter_unloaded_modules(UseCtx, Ctx, Modules)}, + {services, filter_unloaded_services(Services)}, + {internal_databases, filter_unloaded_db(DB)}], + case lists:filter(fun({_, Names}) -> Names =/= [] end, Items) of + [] -> Field; - {_, _} -> - Fun = resolve_not_loaded_fun(UnloadedModules, UnloadedServices), + NotLoaded -> + Fun = resolve_not_loaded_fun(NotLoaded), Field#schema_field{resolve = Fun} end. @@ -76,15 +81,21 @@ get_arg_value(_UseCtx, #{admin := #jid{lserver = Domain}}) -> Domain. -spec aggregate_use_ctx(list(), ctx()) -> use_ctx(). -aggregate_use_ctx(Args, #{use_dir := #{modules := Modules0, services := Services0}}) -> - #{modules := Modules, services := Services} = UseCtx = prepare_use_dir_args(Args), - UseCtx#{modules => Modules0 ++ Modules, services => Services0 ++ Services}; +aggregate_use_ctx(Args, #{use_dir := #{modules := Modules0, services := Services0, + internal_databases := Databases0}}) -> + #{modules := Modules, services := Services, internal_databases := Databases} = + UseCtx = prepare_use_dir_args(Args), + UpdatedModules = Modules0 ++ Modules, + UpdatedServices = Services0 ++ Services, + UpdatedDatabases = Databases0 ++ Databases, + UseCtx#{modules => UpdatedModules, services => UpdatedServices, + internal_databases => UpdatedDatabases}; aggregate_use_ctx(Args, _Ctx) -> prepare_use_dir_args(Args). -spec prepare_use_dir_args([{graphql:name(), term()}]) -> use_ctx(). prepare_use_dir_args(Args) -> - Default = #{modules => [], services => []}, + Default = #{modules => [], services => [], internal_databases => []}, RdyArgs = maps:from_list([{binary_to_existing_atom(name(N)), V} || {N, V} <- Args]), maps:merge(Default, RdyArgs). @@ -128,8 +139,32 @@ filter_unloaded_services(Services) -> lists:filter(fun(S) -> not mongoose_service:is_loaded(binary_to_existing_atom(S)) end, Services). --spec resolve_not_loaded_fun([binary()], [binary()]) -> resolver(). -resolve_not_loaded_fun(Modules, Services) -> - Msg = <<"Some of required modules or services are not loaded">>, - Extra = #{not_loaded_modules => Modules, not_loaded_services => Services}, +-spec filter_unloaded_db([binary()]) -> [binary()]. +filter_unloaded_db(DBs) -> + lists:filter(fun(DB) -> is_database_unloaded(DB) end, DBs). + +-spec is_database_unloaded(binary()) -> boolean(). +is_database_unloaded(DB) -> + mongoose_config:lookup_opt([internal_databases, + binary_to_existing_atom(DB)]) == {error, not_found}. + +-spec resolve_not_loaded_fun([{dependency_type(), [dependency_name()]}]) -> resolver(). +resolve_not_loaded_fun(NotLoaded) -> + Msg = not_loaded_message(NotLoaded), + Extra = maps:from_list([{error_key(Type), Names} || {Type, Names} <- NotLoaded]), fun(_, _, _, _) -> mongoose_graphql_helper:make_error(deps_not_loaded, Msg, Extra) end. + +-spec not_loaded_message([{dependency_type(), [dependency_name()]}]) -> binary(). +not_loaded_message(NotLoaded) -> + MsgPrefix = <<"Some of the required ">>, + MsgList = string:join([dependency_type_to_string(Item) || {Item, _} <- NotLoaded], " and "), + MsgBinaryList = list_to_binary(MsgList), + <>. + +-spec dependency_type_to_string(dependency_type()) -> [string()]. +dependency_type_to_string(Type) -> + string:replace(atom_to_list(Type), "_", " "). + +-spec error_key(dependency_type()) -> atom(). +error_key(Type) -> + list_to_atom("not_loaded_" ++ atom_to_list(Type)). diff --git a/test/mongoose_graphql_SUITE.erl b/test/mongoose_graphql_SUITE.erl index 8e8ce1a2969..5a79781c3fb 100644 --- a/test/mongoose_graphql_SUITE.erl +++ b/test/mongoose_graphql_SUITE.erl @@ -113,12 +113,19 @@ use_directive() -> use_dir_all_modules_loaded, use_dir_all_modules_and_services_loaded, use_dir_module_and_service_not_loaded, - use_dir_object_module_and_service_not_loaded, - use_dir_object_all_modules_and_services_loaded, - use_dir_auth_admin_all_modules_and_services_loaded, - use_dir_auth_user_all_modules_and_services_loaded, - use_dir_auth_admin_module_and_service_not_loaded, - use_dir_auth_user_module_and_service_not_loaded + use_dir_module_service_and_db_loaded, + use_dir_db_not_loaded, + use_dir_module_service_and_db_not_loaded, + use_dir_object_module_service_and_db_loaded, + use_dir_object_all_modules_services_and_dbs_loaded, + use_dir_object_module_and_db_not_loaded, + use_dir_object_service_and_db_not_loaded, + use_dir_auth_admin_all_modules_services_and_dbs_loaded, + use_dir_auth_user_all_modules_services_and_dbs_loaded, + use_dir_auth_admin_module_service_and_db_not_loaded, + use_dir_auth_user_module_service_and_db_not_loaded, + use_dir_auth_admin_db_not_loaded, + use_dir_auth_user_db_not_loaded ]. user_listener() -> @@ -154,7 +161,7 @@ common_tests() -> init_per_suite(Config) -> %% Register atoms for `binary_to_existing_atom` - [mod_x, mod_z, service_x, service_d], + [mod_x, mod_z, service_x, service_d, db_x], application:ensure_all_started(cowboy), application:ensure_all_started(jid), Config. @@ -205,6 +212,7 @@ init_per_group(domain_permissions, Config) -> [{domains, Domains} | Config]; init_per_group(use_directive, Config) -> Config1 = meck_domain_api(Config), + mongoose_config:set_opts(#{internal_databases => #{db_a => #{}}}), meck_module_and_service_checking(Config1); init_per_group(_G, Config) -> Config. @@ -226,7 +234,8 @@ end_per_group(domain_permissions, _Config) -> meck:unload(mongoose_domain_api); end_per_group(use_directive, Config) -> unmeck_domain_api(Config), - unmeck_module_and_service_checking(Config); + unmeck_module_and_service_checking(Config), + mongoose_config:erase_opts(); end_per_group(_, Config) -> Config. @@ -276,13 +285,20 @@ init_per_testcase(C, Config) when C =:= check_object_permissions; init_per_testcase(C, Config) when C =:= use_dir_module_not_loaded; C =:= use_dir_all_modules_loaded; C =:= use_dir_module_and_service_not_loaded; + C =:= use_dir_module_service_and_db_loaded; + C =:= use_dir_db_not_loaded; + C =:= use_dir_module_service_and_db_not_loaded; C =:= use_dir_all_modules_and_services_loaded; - C =:= use_dir_object_module_and_service_not_loaded; - C =:= use_dir_object_all_modules_and_services_loaded; - C =:= use_dir_auth_user_all_modules_and_services_loaded; - C =:= use_dir_auth_admin_all_modules_and_services_loaded; - C =:= use_dir_auth_user_module_and_service_not_loaded; - C =:= use_dir_auth_admin_module_and_service_not_loaded -> + C =:= use_dir_object_module_service_and_db_loaded; + C =:= use_dir_object_all_modules_services_and_dbs_loaded; + C =:= use_dir_object_module_and_db_not_loaded; + C =:= use_dir_object_service_and_db_not_loaded; + C =:= use_dir_auth_user_all_modules_services_and_dbs_loaded; + C =:= use_dir_auth_admin_all_modules_services_and_dbs_loaded; + C =:= use_dir_auth_user_module_service_and_db_not_loaded; + C =:= use_dir_auth_admin_module_service_and_db_not_loaded; + C =:= use_dir_auth_admin_db_not_loaded; + C =:= use_dir_auth_user_db_not_loaded -> {Mapping, Pattern} = example_directives_schema_data(Config), {ok, _} = mongoose_graphql:create_endpoint(C, Mapping, [Pattern]), Ep = mongoose_graphql:get_endpoint(C), @@ -681,8 +697,7 @@ use_directive_can_use_auth_user_domain(Config) -> execute(Ep, Body, {<<"alice@localhost">>, <<"makota">>}), #{<<"extensions">> := #{<<"code">> := <<"deps_not_loaded">>, - <<"not_loaded_modules">> := [<<"mod_x">>], - <<"not_loaded_services">> := []} + <<"not_loaded_modules">> := [<<"mod_x">>]} } = Error. no_creds_defined_admin_can_access_protected(_Config) -> @@ -712,8 +727,7 @@ use_directive_can_use_auth_domain_admin_domain(Config) -> execute(Ep, Body, {<<"admin@localhost">>, <<"makota">>}), #{<<"extensions">> := #{<<"code">> := <<"deps_not_loaded">>, - <<"not_loaded_modules">> := [<<"mod_x">>], - <<"not_loaded_services">> := []} + <<"not_loaded_modules">> := [<<"mod_x">>]} } = Error. auth_domain_admin_wrong_password_error(Config) -> @@ -846,10 +860,9 @@ use_dir_module_not_loaded(Config) -> #{errors := [Error]} = execute_ast(Config, Ctx2, Ast), #{extensions := #{code := deps_not_loaded, - not_loaded_modules := [<<"mod_b">>], - not_loaded_services := [] + not_loaded_modules := [<<"mod_b">>] }, - message := <<"Some of required modules or services are not loaded">>, + message := <<"Some of the required modules are not loaded">>, path := [<<"catA">>, <<"command">>] } = Error. @@ -870,18 +883,80 @@ use_dir_module_and_service_not_loaded(Config) -> not_loaded_modules := [<<"mod_z">>], not_loaded_services := [<<"service_d">>] }, - message := <<"Some of required modules or services are not loaded">>, + message := <<"Some of the required modules and services are not loaded">>, path := [<<"catA">>, <<"command3">>] } = Error. -use_dir_object_all_modules_and_services_loaded(Config) -> +use_dir_module_service_and_db_loaded(Config) -> + Doc = <<"{catA { command4(domain: \"localhost\")} }">>, + Ctx = #{}, + {Ast, Ctx2} = check_directives(Config, Ctx, Doc), + Res = execute_ast(Config, Ctx2, Ast), + ?assertEqual(#{data => #{<<"catA">> => #{<<"command4">> => <<"command4">>}}}, Res). + +use_dir_db_not_loaded(Config) -> + Doc = <<"{catA { command5(domain: \"localhost\")} }">>, + Ctx = #{}, + {Ast, Ctx2} = check_directives(Config, Ctx, Doc), + #{errors := [Error]} = execute_ast(Config, Ctx2, Ast), + #{extensions := + #{code := deps_not_loaded, + not_loaded_internal_databases := [<<"db_x">>]}, + message := + <<"Some of the required internal databases are not loaded">>, + path := [<<"catA">>, <<"command5">>] + } = Error. + +use_dir_module_service_and_db_not_loaded(Config) -> + Doc = <<"{catA { command6(domain: \"localhost\")} }">>, + Ctx = #{}, + {Ast, Ctx2} = check_directives(Config, Ctx, Doc), + #{errors := [Error]} = execute_ast(Config, Ctx2, Ast), + #{extensions := + #{code := deps_not_loaded, + not_loaded_modules := [<<"mod_x">>], + not_loaded_services := [<<"service_x">>], + not_loaded_internal_databases := [<<"db_x">>]}, + message := + <<"Some of the required modules and services and internal databases are not loaded">>, + path := [<<"catA">>, <<"command6">>] + } = Error. + +use_dir_object_all_modules_services_and_dbs_loaded(Config) -> Doc = <<"{ catC { command(domain: \"localhost\") } }">>, Ctx = #{}, {Ast, Ctx2} = check_directives(Config, Ctx, Doc), Res = execute_ast(Config, Ctx2, Ast), ?assertEqual(#{data => #{<<"catC">> => #{<<"command">> => <<"command">>}}}, Res). -use_dir_object_module_and_service_not_loaded(Config) -> +use_dir_object_module_and_db_not_loaded(Config) -> + Doc = <<"{ catD { command(domain: \"localhost\") } }">>, + Ctx = #{}, + {Ast, Ctx2} = check_directives(Config, Ctx, Doc), + #{errors := [Error]} = execute_ast(Config, Ctx2, Ast), + #{extensions := + #{code := deps_not_loaded, + not_loaded_modules := [<<"mod_x">>], + not_loaded_internal_databases := [<<"db_x">>]}, + message := + <<"Some of the required modules and internal databases are not loaded">>, + path := [<<"catD">>, <<"command">>] + } = Error. + +use_dir_object_service_and_db_not_loaded(Config) -> + Doc = <<"{ catD { command3 } }">>, + Ctx = #{user => jid:make_bare(<<"user">>, <<"localhost">>)}, + {Ast, Ctx2} = check_directives(Config, Ctx, Doc), + #{errors := [Error]} = execute_ast(Config, Ctx2, Ast), + #{extensions := + #{code := deps_not_loaded, + not_loaded_services := [<<"service_x">>], + not_loaded_internal_databases := [<<"db_x">>]}, + message := <<"Some of the required services and internal databases are not loaded">>, + path := [<<"catD">>, <<"command3">>] + } = Error. + +use_dir_object_module_service_and_db_loaded(Config) -> Doc = <<"{ catB { command(domain: \"localhost\") } }">>, Ctx = #{}, {Ast, Ctx2} = check_directives(Config, Ctx, Doc), @@ -889,54 +964,67 @@ use_dir_object_module_and_service_not_loaded(Config) -> #{extensions := #{code := deps_not_loaded, not_loaded_modules := [<<"mod_x">>], - not_loaded_services := [<<"service_x">>] + not_loaded_services := [<<"service_x">>], + not_loaded_internal_databases := [<<"db_x">>] }, - message := <<"Some of required modules or services are not loaded">>, + message := + <<"Some of the required modules and services and internal databases are not loaded">>, path := [<<"catB">>, <<"command">>] } = Error. -use_dir_auth_user_all_modules_and_services_loaded(Config) -> +use_dir_auth_all_modules_services_and_dbs_loaded(UserRole, Config) -> Doc = <<"{ catC { command2 } }">>, - Ctx = #{user => jid:make_bare(<<"user">>, <<"localhost">>)}, + Ctx = #{user => jid:make_bare(UserRole, <<"localhost">>)}, {Ast, Ctx2} = check_directives(Config, Ctx, Doc), Res = execute_ast(Config, Ctx2, Ast), ?assertEqual(#{data => #{<<"catC">> => #{<<"command2">> => <<"command2">>}}}, Res). -use_dir_auth_admin_all_modules_and_services_loaded(Config) -> - Doc = <<"{ catC { command2 } }">>, - Ctx = #{user => jid:make_bare(<<"admin">>, <<"localhost">>)}, - {Ast, Ctx2} = check_directives(Config, Ctx, Doc), - Res = execute_ast(Config, Ctx2, Ast), - ?assertEqual(#{data => #{<<"catC">> => #{<<"command2">> => <<"command2">>}}}, Res). +use_dir_auth_user_all_modules_services_and_dbs_loaded(Config) -> + use_dir_auth_all_modules_services_and_dbs_loaded(<<"user">>, Config). + +use_dir_auth_admin_all_modules_services_and_dbs_loaded(Config) -> + use_dir_auth_all_modules_services_and_dbs_loaded(<<"admin">>, Config). -use_dir_auth_user_module_and_service_not_loaded(Config) -> +use_dir_auth_module_service_and_db_not_loaded(UserRole, Config) -> Doc = <<"{ catB { command2 } }">>, - Ctx = #{user => jid:make_bare(<<"user">>, <<"localhost">>)}, + Ctx = #{user => jid:make_bare(UserRole, <<"localhost">>)}, {Ast, Ctx2} = check_directives(Config, Ctx, Doc), #{errors := [Error]} = execute_ast(Config, Ctx2, Ast), #{extensions := #{code := deps_not_loaded, not_loaded_modules := [<<"mod_x">>], - not_loaded_services := [<<"service_x">>] + not_loaded_services := [<<"service_x">>], + not_loaded_internal_databases := [<<"db_x">>] }, - message := <<"Some of required modules or services are not loaded">>, + message := + <<"Some of the required modules and services and internal databases are not loaded">>, path := [<<"catB">>, <<"command2">>] } = Error. -use_dir_auth_admin_module_and_service_not_loaded(Config) -> - Doc = <<"{ catB { command2 } }">>, - Ctx = #{user => jid:make_bare(<<"admin">>, <<"localhost">>)}, +use_dir_auth_user_module_service_and_db_not_loaded(Config) -> + use_dir_auth_module_service_and_db_not_loaded(<<"user">>, Config). + +use_dir_auth_admin_module_service_and_db_not_loaded(Config) -> + use_dir_auth_module_service_and_db_not_loaded(<<"admin">>, Config). + +use_dir_auth_db_not_loaded(UserRole, Config) -> + Doc = <<"{ catD { command2 } }">>, + Ctx = #{user => jid:make_bare(UserRole, <<"localhost">>)}, {Ast, Ctx2} = check_directives(Config, Ctx, Doc), #{errors := [Error]} = execute_ast(Config, Ctx2, Ast), #{extensions := #{code := deps_not_loaded, - not_loaded_modules := [<<"mod_x">>], - not_loaded_services := [<<"service_x">>] - }, - message := <<"Some of required modules or services are not loaded">>, - path := [<<"catB">>, <<"command2">>] + not_loaded_internal_databases := [<<"db_x">>]}, + message := <<"Some of the required internal databases are not loaded">>, + path := [<<"catD">>, <<"command2">>] } = Error. +use_dir_auth_user_db_not_loaded(Config) -> + use_dir_auth_db_not_loaded(<<"user">>, Config). + +use_dir_auth_admin_db_not_loaded(Config) -> + use_dir_auth_db_not_loaded(<<"admin">>, Config). + %% Helpers assert_code(Code, Data) -> @@ -1153,4 +1241,5 @@ meck_module_and_service_checking(Config) -> unmeck_module_and_service_checking(_Config) -> meck:unload(gen_mod), - meck:unload(mongoose_service). + meck:unload(mongoose_service), + mongoose_config:erase_opts(). diff --git a/test/mongoose_graphql_SUITE_data/directives.gql b/test/mongoose_graphql_SUITE_data/directives.gql index c969d3d011d..1dc2bbd72a7 100644 --- a/test/mongoose_graphql_SUITE_data/directives.gql +++ b/test/mongoose_graphql_SUITE_data/directives.gql @@ -12,7 +12,8 @@ enum ProtectionType{ DEFAULT } -directive @use (modules: [String!] = [], services: [String!] = []) on FIELD_DEFINITION | OBJECT +directive @use (modules: [String!] = [], services: [String!] = [], + internal_databases: [String!] = []) on FIELD_DEFINITION | OBJECT type UserQuery @use{ field: String diff --git a/test/mongoose_graphql_SUITE_data/directives_schema.gql b/test/mongoose_graphql_SUITE_data/directives_schema.gql index f0b2ed1d4d5..a5372683510 100644 --- a/test/mongoose_graphql_SUITE_data/directives_schema.gql +++ b/test/mongoose_graphql_SUITE_data/directives_schema.gql @@ -12,12 +12,14 @@ enum ProtectionType{ DEFAULT } -directive @use(modules: [String!] = [], services: [String!] = [], arg: String) on FIELD_DEFINITION | OBJECT +directive @use(modules: [String!] = [], services: [String!] = [], + internal_databases: [String!] = [], arg: String) on FIELD_DEFINITION | OBJECT type UserQuery { catA: Category catB: CategoryB catC: CategoryC + catD: CategoryD } type Category @protected { @@ -27,10 +29,16 @@ type Category @protected { @use(modules: ["mod_b"], services: ["service_a", "service_b"], arg: "domain") command3(domain: String): String @use(modules: ["mod_a", "mod_z"], services: ["service_a", "service_d"], arg: "domain") + command4(domain: String): String + @use(modules: ["mod_a"], services: ["service_a"], internal_databases: ["db_a"], arg: "domain") + command5(domain: String): String + @use(modules: ["mod_a"], services: ["service_a"], internal_databases: ["db_x"], arg: "domain") + command6(domain: String): String + @use(modules: ["mod_x"], services: ["service_x"], internal_databases: ["db_x"], arg: "domain") } type CategoryB - @use(modules: ["mod_x"], services: ["service_x"]) + @use(modules: ["mod_x"], services: ["service_x"], internal_databases: ["db_x"]) @protected { command(domain: String): String @use(modules: ["mod_a"], arg: "domain") @@ -39,7 +47,7 @@ type CategoryB } type CategoryC - @use(modules: ["mod_a"], services: ["service_a"]) + @use(modules: ["mod_a"], services: ["service_a"], internal_databases: ["db_a"]) @protected { command(domain: String): String @use(modules: ["mod_b"], arg: "domain") @@ -47,7 +55,17 @@ type CategoryC @use(modules: ["mod_b"]) } +type CategoryD + @use(modules: ["mod_a"], services: ["service_a"], internal_databases: ["db_x"]) + @protected { + command(domain: String): String + @use(modules: ["mod_x"], arg: "domain") + command2: String + @use + command3: String + @use(services: ["service_x"]) +} + type UserMutation @protected{ field: String } - diff --git a/test/mongoose_graphql_default_resolver.erl b/test/mongoose_graphql_default_resolver.erl index 8c587534914..bce0e7b6cc3 100644 --- a/test/mongoose_graphql_default_resolver.erl +++ b/test/mongoose_graphql_default_resolver.erl @@ -13,9 +13,13 @@ execute(_Ctx, _Obj, <<"id">>, #{<<"value">> := Value}) -> execute(_Ctx, _Obj, Cmd, _Attrs) when Cmd =:= <<"catA">>; Cmd =:= <<"catB">>; Cmd =:= <<"catC">>; + Cmd =:= <<"catD">>; Cmd =:= <<"command">>; Cmd =:= <<"command2">>; - Cmd =:= <<"command3">> -> + Cmd =:= <<"command3">>; + Cmd =:= <<"command4">>; + Cmd =:= <<"command5">>; + Cmd =:= <<"command6">> -> {ok, Cmd}; execute(_Ctx, _Obj, Field, _Attrs) -> {error, {not_implemented, Field}}.