./ct_report/coverage/mongoose_graphql.COVER.html

1 %% @doc This module provides main interface to graphql. It initializes schemas
2 %% and allows executing queries with permissions checks.
3 %% @end
4 -module(mongoose_graphql).
5
6 -include_lib("kernel/include/logger.hrl").
7
8 %API
9 -export([init/0,
10 get_endpoint/1,
11 create_endpoint/3,
12 execute/2,
13 execute/3]).
14
15 -ignore_xref([create_endpoint/3]).
16
17 -type request() :: #{document := binary(),
18 operation_name := binary() | undefined,
19 vars := map(),
20 authorized := boolean(),
21 ctx := map()}.
22
23 -export_type([request/0]).
24
25 -define(USER_EP_NAME, user_schema_ep).
26 -define(ADMIN_EP_NAME, admin_schema_ep).
27
28 %% @doc Create and initialize endpoints for user and admin.
29 -spec init() -> ok.
30 init() ->
31 80 create_endpoint(?USER_EP_NAME, user_mapping_rules(), schema_global_patterns("user")),
32 80 create_endpoint(?ADMIN_EP_NAME, admin_mapping_rules(), schema_global_patterns("admin")),
33 80 ok.
34
35 %% @doc Get endpoint_context for passed endpoint name.
36 -spec get_endpoint(atom()) -> graphql:endpoint_context().
37 get_endpoint(admin) ->
38 88 graphql_schema:get_endpoint_ctx(?ADMIN_EP_NAME);
39 get_endpoint(user) ->
40 50 graphql_schema:get_endpoint_ctx(?USER_EP_NAME);
41 get_endpoint(Name) ->
42
:-(
graphql_schema:get_endpoint_ctx(Name).
43
44 %% @doc Create a new endpoint and load schema.
45 -spec create_endpoint(atom(), map(), [file:filename_all()]) -> gen:start_ret().
46 create_endpoint(Name, Mapping, Patterns) ->
47 160 Res = graphql_schema:start_link(Name),
48 160 Ep = graphql_schema:get_endpoint_ctx(Name),
49 160 {ok, SchemaData} = load_multiple_file_schema(Patterns),
50 160 ok = graphql:load_schema(Ep, Mapping, SchemaData),
51 160 ok = graphql:validate_schema(Ep),
52 160 Res.
53
54 %% @doc Execute request on a given endpoint.
55 -spec execute(graphql:endpoint_context(), request()) ->
56 {ok, map()} | {error, term()}.
57 execute(Ep, #{document := Doc,
58 operation_name := OpName,
59 authorized := AuthStatus,
60 vars := Vars,
61 ctx := Ctx}) ->
62 138 try
63 138 {ok, Ast} = graphql_parse(Doc),
64 137 {ok, #{ast := Ast2,
65 fun_env := FunEnv}} = graphql:type_check(Ep, Ast),
66 137 ok = graphql:validate(Ast2),
67 137 ok = mongoose_graphql_permissions:check_permissions(OpName, AuthStatus, Ast2),
68 137 Coerced = graphql:type_check_params(Ep, FunEnv, OpName, Vars),
69 135 Ctx2 = Ctx#{params => Coerced,
70 operation_name => OpName,
71 authorized => AuthStatus,
72 error_module => mongoose_graphql_errors},
73 135 {ok, graphql:execute(Ep, Ctx2, Ast2)}
74 catch
75 throw:{error, Err} ->
76 3 {error, Err};
77 Class:Reason:Stacktrace ->
78
:-(
Err = #{what => graphql_internal_crash,
79 class => Class, reason => Reason,
80 stacktrace => Stacktrace},
81
:-(
?LOG_ERROR(Err),
82
:-(
{error, internal_crash}
83 end.
84
85 %% @doc Execute selected operation on a given endpoint with authorization.
86 -spec execute(graphql:endpoint_context(), undefined | binary(), binary()) ->
87 {ok, map()} | {error, term()}.
88 execute(Ep, OpName, Doc) ->
89 2 Req = #{document => Doc,
90 operation_name => OpName,
91 vars => #{},
92 authorized => true,
93 ctx => #{}},
94 2 execute(Ep, Req).
95
96 % Internal
97
98 -spec schema_global_patterns(file:name_all()) -> [file:filename_all()].
99 schema_global_patterns(SchemaDir) ->
100 160 [schema_pattern(SchemaDir), schema_pattern("global")].
101
102 -spec schema_pattern(file:name_all()) -> file:filename_all().
103 schema_pattern(DirName) ->
104 320 schema_pattern(DirName, "*.gql").
105
106 -spec schema_pattern(file:name_all(), file:name_all()) -> file:filename_all().
107 schema_pattern(DirName, Pattern) ->
108 320 filename:join([code:priv_dir(mongooseim), "graphql/schemas", DirName, Pattern]).
109
110 graphql_parse(Doc) ->
111 138 case graphql:parse(Doc) of
112 {ok, _} = Ok ->
113 137 Ok;
114 {error, Err} ->
115 1 graphql_err:abort([], parse, Err)
116 end.
117
118 admin_mapping_rules() ->
119 80 #{objects => #{
120 'AdminQuery' => mongoose_graphql_admin_query,
121 'DomainAdminQuery' => mongoose_graphql_domain_admin_query,
122 'AdminMutation' => mongoose_graphql_admin_mutation,
123 'DomainAdminMutation' => mongoose_graphql_domain_admin_mutation,
124 'SessionAdminMutation' => mongoose_graphql_session_admin_mutation,
125 'SessionAdminQuery' => mongoose_graphql_session_admin_query,
126 'StanzaAdminMutation' => mongoose_graphql_stanza_admin_mutation,
127 'StanzaAdminQuery' => mongoose_graphql_stanza_admin_query,
128 'AccountAdminQuery' => mongoose_graphql_account_admin_query,
129 'AccountAdminMutation' => mongoose_graphql_account_admin_mutation,
130 'MUCLightAdminMutation' => mongoose_graphql_muc_light_admin_mutation,
131 'MUCLightAdminQuery' => mongoose_graphql_muc_light_admin_query,
132 'Domain' => mongoose_graphql_domain,
133 default => mongoose_graphql_default},
134 interfaces => #{default => mongoose_graphql_default},
135 scalars => #{default => mongoose_graphql_scalar},
136 enums => #{default => mongoose_graphql_enum}}.
137
138 user_mapping_rules() ->
139 80 #{objects => #{
140 'UserQuery' => mongoose_graphql_user_query,
141 'UserMutation' => mongoose_graphql_user_mutation,
142 'AccountUserQuery' => mongoose_graphql_account_user_query,
143 'AccountUserMutation' => mongoose_graphql_account_user_mutation,
144 'MUCLightUserMutation' => mongoose_graphql_muc_light_user_mutation,
145 'MUCLightUserQuery' => mongoose_graphql_muc_light_user_query,
146 'SessionUserQuery' => mongoose_graphql_session_user_query,
147 'UserAuthInfo' => mongoose_graphql_user_auth_info,
148 default => mongoose_graphql_default},
149 interfaces => #{default => mongoose_graphql_default},
150 scalars => #{default => mongoose_graphql_scalar},
151 enums => #{default => mongoose_graphql_enum}}.
152
153 load_multiple_file_schema(Patterns) ->
154 160 Paths = lists:flatmap(fun(P) -> filelib:wildcard(P) end, Patterns),
155 160 try
156 160 SchemaData = [read_schema_file(P) || P <- Paths],
157 160 {ok, lists:flatten(SchemaData)}
158 catch
159 throw:{error, Reason, Path} ->
160
:-(
?LOG_ERROR(#{what => graphql_cannot_load_schema,
161
:-(
reason => Reason, path => Path}),
162
:-(
{error, cannot_load}
163 end.
164
165 read_schema_file(Path) ->
166 2000 case file:read_file(Path) of
167 {ok, Data} ->
168 2000 binary_to_list(Data);
169 {error, Reason} ->
170
:-(
throw({error, Reason, Path})
171 end.
Line Hits Source