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