./ct_report/coverage/mongoose_graphql_errors.COVER.html

1 %% @doc Implements callbacks that format custom errors returned from resolvers or crashes.
2 %% In addition, it can format each type of error that occurred in any graphql
3 %% or mongoose_graphql phase.
4 %% @end
5 -module(mongoose_graphql_errors).
6
7 -export([format_error/1, err/2, crash/2]).
8
9 -ignore_xref([format_error/1, err/2, crash/2]).
10
11 -include("mongoose_graphql_types.hrl").
12
13 -type err_msg() :: #{message := binary(), extensions => map(), path => list()}.
14
15 %% callback invoked when resolver returns error tuple
16 -spec err(map(), term()) -> err_msg().
17 err(_Ctx, #{jid := Jid, what := unknown_user}) when is_binary(Jid) ->
18
:-(
#{message => <<"Given user does not exist">>, extensions => #{code => unknown_user, jid => Jid}};
19 err(_Ctx, #{domain := Domain, what := unknown_domain}) when is_binary(Domain) ->
20
:-(
#{message => <<"Given domain does not exist">>, extensions => #{code => unknown_domain, domain => Domain}};
21 err(_Ctx, #{domain := Domain, what := domain_not_found}) ->
22
:-(
#{message => <<"Given domain does not exist">>, extensions => #{code => domain_not_found, domain => Domain}};
23 err(_Ctx, #{domain := Domain, what := domain_duplicate}) when is_binary(Domain) ->
24
:-(
#{message => <<"Domain already exists">>, extensions => #{code => domain_duplicate, domain => Domain}};
25 err(_Ctx, #{domain := Domain, what := domain_static}) when is_binary(Domain) ->
26
:-(
#{message => <<"Domain static">>, extensions => #{code => domain_static, domain => Domain}};
27 err(_Ctx, #{host_type := HostType, what := unknown_host_type}) when is_binary(HostType) ->
28
:-(
#{message => <<"Unknown host type">>, extensions => #{code => unknown_host_type, hostType => HostType}};
29 err(_Ctx, #{host_type := HostType, what := wrong_host_type}) when is_binary(HostType) ->
30
:-(
#{message => <<"Wrong host type">>, extensions => #{code => wrong_host_type, hostType => HostType}};
31 err(_Ctx, #{term := Term, what := db_error}) ->
32
:-(
#{message => <<"Database error">>, extensions => #{code => db_error, term => Term}};
33 err(_Ctx, #{host_type := HostType, what := service_disabled}) when is_binary(HostType) ->
34
:-(
#{message => <<"Service disabled">>, extensions => #{code => service_disabled, hostType => HostType}};
35 err(_Ctx, #resolver_error{reason = Code, msg = Msg, context = Ext}) ->
36 58 #{message => iolist_to_binary(Msg), extensions => Ext#{code => Code}};
37 err(_Ctx, ErrorTerm) ->
38
:-(
#{message => iolist_to_binary(io_lib:format("~p", [ErrorTerm])),
39 extensions => #{code => resolver_error}}.
40
41 %% callback invoked when resolver crashes
42 -spec crash(map(), term()) -> err_msg().
43 crash(_Ctx, #{type := Type}) ->
44
:-(
#{message => <<"Unexpected ", Type/binary, " resolver crash">>,
45 extensions => #{code => resolver_crash}}.
46
47 %% @doc Format error that occurred in any phase including HTTP request decoding.
48 -spec format_error(term())-> {integer(), err_msg()}.
49 format_error(#{phase := Phase, error_term := Term} = Err) when Phase =:= authorize;
50 Phase =:= decode;
51 Phase =:= parse ->
52 2 Msg = #{extensions => #{code => err_code(Phase, Term)},
53 message => iolist_to_binary(err_msg(Phase, Term))},
54 2 {err_http_code(Phase), add_path(Err, Msg)};
55 format_error(#{error_term := _, phase := Phase} = Err) when Phase =:= execute;
56 Phase =:= type_check;
57 Phase =:= validate;
58 Phase =:= uncategorized ->
59 2 Err2 = maps:merge(#{path => []}, Err),
60 2 [ErrMsg] = graphql:format_errors(#{}, [Err2]),
61 2 {400, ErrMsg};
62 format_error(internal_crash) ->
63
:-(
Msg = #{message => <<"GraphQL Internal Server Error">>,
64 extensions => #{code => internal_server_error}},
65
:-(
{500, Msg};
66 format_error(Err) ->
67
:-(
Msg = #{extensions => #{code => uncategorized},
68 message => iolist_to_binary(io_lib:format("~p", [Err]))},
69
:-(
{400, Msg}.
70
71 %% Internal
72
73 err_http_code(authorize) ->
74
:-(
401;
75 err_http_code(_) ->
76 2 400.
77
78 err_code(_, Term) ->
79 2 simplify(Term).
80
81 2 simplify(A) when is_atom(A) -> A;
82
:-(
simplify(B) when is_binary(B) -> B;
83
:-(
simplify(T) when is_tuple(T) -> element(1, T).
84
85 err_msg(parse, Result) ->
86
:-(
parse_err_msg(Result);
87 err_msg(decode, Result) ->
88 2 decode_err_msg(Result);
89 err_msg(authorize, Result) ->
90
:-(
authorize_err_msg(Result).
91
92 authorize_err_msg({request_error, {header, <<"authorization">>}, _}) ->
93
:-(
"Malformed authorization header. Please consult the relevant specification";
94 authorize_err_msg(wrong_credentials) ->
95
:-(
"The provided credentials are wrong";
96 authorize_err_msg({no_permissions, Op}) ->
97
:-(
io_lib:format("Cannot execute query ~s without permissions", [Op]).
98
99 parse_err_msg({parser_error, {Line, graphql_parser, Msg}}) ->
100
:-(
io_lib:format("Cannot parse line ~B because of ~s", [Line, Msg]);
101 parse_err_msg({scanner_error, {Line, graphql_scanner, Msg}}) ->
102
:-(
Formatted = lists:flatten(graphql_scanner:format_error(Msg)),
103
:-(
io_lib:format("Cannot scan line ~B because of ~s", [Line, Formatted]).
104
105 decode_err_msg(no_query_supplied) ->
106 2 "The query was not supplied in the request body";
107 decode_err_msg(invalid_json_body) ->
108
:-(
"The request JSON body is invalid";
109 decode_err_msg(variables_invalid_json) ->
110
:-(
"The variables' JSON is invalid".
111
112 add_path(#{path := Path}, ErrMsg) ->
113
:-(
ErrMsg#{path => Path};
114 add_path(_, ErrMsg) ->
115 2 ErrMsg.
Line Hits Source