Skip to content

Commit 926e362

Browse files
authored
v1.2: Build errors from Blueprint tree (#153)
* Towards resolution building Phase.Error structs * 484 tests, 22 failures & field suggestions * Validation: VariablesAreInputTypes, tests * GREEN * ScalarLeafs validation * Basic pipeline tests, hook in ScalarLeafs
1 parent c66c187 commit 926e362

47 files changed

Lines changed: 1473 additions & 537 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
defmodule Absinthe.Blueprint.Document.Result do
2+
3+
alias __MODULE__
4+
5+
@type t ::
6+
Result.Object
7+
| Result.List
8+
| Result.Leaf
9+
10+
end
Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,20 @@
11
defmodule Absinthe.Blueprint.Document.Result.Leaf do
22

3+
alias Absinthe.{Blueprint, Phase}
4+
35
@enforce_keys [:emitter, :value]
46
defstruct [
57
:emitter,
6-
:value
8+
:value,
9+
errors: [],
10+
flags: %{}
711
]
812

13+
@type t :: %__MODULE__{
14+
emitter: Blueprint.Document.Field.t,
15+
value: Blueprint.Document.Result.t,
16+
errors: [Phase.Error.t],
17+
flags: [Blueprint.flag_t],
18+
}
19+
920
end
Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,21 @@
11
defmodule Absinthe.Blueprint.Document.Result.List do
22

3+
alias Absinthe.{Blueprint, Phase}
4+
35
@enforce_keys [:emitter, :values]
46
defstruct [
57
:emitter,
6-
:values
8+
:values,
9+
# Added by phases
10+
errors: [],
11+
flags: %{}
712
]
813

14+
@type t :: %__MODULE__{
15+
emitter: Blueprint.Document.Field.t,
16+
values: [Blueprint.Document.Result.t],
17+
errors: [Phase.Error.t],
18+
flags: [Blueprint.flag_t],
19+
}
20+
921
end
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,21 @@
11
defmodule Absinthe.Blueprint.Document.Result.Object do
22

3+
alias Absinthe.{Blueprint, Phase}
4+
35
@enforce_keys [:emitter, :fields]
46
defstruct [
57
:emitter,
68
:fields,
9+
# Added by phases
10+
errors: [],
11+
flags: %{}
712
]
813

14+
@type t :: %__MODULE__{
15+
emitter: Blueprint.Document.Field.t,
16+
fields: [Blueprint.Document.Result.t],
17+
errors: [Phase.Error.t],
18+
flags: [Blueprint.flag_t]
19+
}
20+
921
end

lib/absinthe/blueprint/document/variable_definition.ex

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ defmodule Absinthe.Blueprint.Document.VariableDefinition do
1212
flags: %{},
1313
provided_value: nil,
1414
errors: [],
15-
schema_type: nil,
15+
schema_node: nil,
1616
]
1717

1818
@type t :: %__MODULE__{
@@ -23,7 +23,7 @@ defmodule Absinthe.Blueprint.Document.VariableDefinition do
2323
provided_value: nil | Blueprint.Input.t,
2424
errors: [Absinthe.Phase.Error.t],
2525
flags: Blueprint.flags_t,
26-
schema_type: Type.t,
26+
schema_node: Type.t,
2727
}
2828

2929
end

lib/absinthe/blueprint/input.ex

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,5 +102,8 @@ defmodule Absinthe.Blueprint.Input do
102102
def inspect(nil) do
103103
"null"
104104
end
105+
def inspect(other) do
106+
Kernel.inspect(other)
107+
end
105108

106109
end

lib/absinthe/phase/debug.ex

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ defmodule Absinthe.Phase.Debug do
55

66
@spec run(any) :: {:ok, Blueprint.t}
77
def run(input) do
8-
IO.inspect(input)
8+
if System.get_env("DEBUG") do
9+
IO.inspect(input)
10+
end
911
{:ok, input}
1012
end
1113

lib/absinthe/phase/document/arguments/data.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ defmodule Absinthe.Phase.Document.Arguments.Data do
7171
other
7272
end
7373
end
74-
defp build_value(%Blueprint.Input.Object{} = node, adapter) do
74+
defp build_value(%Blueprint.Input.Object{schema_node: %{fields: _}} = node, adapter) do
7575
{result, fields} = node.fields
7676
|> Enum.reduce({%{}, []}, fn
7777
field, {data, fields} ->
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
defmodule Absinthe.Phase.Document.CascadeInvalid do
2+
@moduledoc """
3+
Ensure any nodes whose children are invalid that need to be made
4+
invalid is marked invalid.
5+
"""
6+
7+
use Absinthe.Phase
8+
9+
alias Absinthe.Blueprint
10+
11+
@spec run(Blueprint.t) :: {:ok, Blueprint.t}
12+
def run(input) do
13+
result = Blueprint.update_current(input, &process(&1, input.schema))
14+
{:ok, result}
15+
end
16+
17+
defp process(operation, schema) do
18+
Blueprint.prewalk(operation, &handle_node(&1, schema))
19+
end
20+
21+
defp handle_node(%Blueprint.Document.Field{} = node, schema) do
22+
node = if any_invalid?(node.arguments) do
23+
node |> flag_invalid(:bad_arguments)
24+
else
25+
node
26+
end
27+
node = if any_invalid?(node.directives) do
28+
node |> flag_invalid(:bad_directives)
29+
else
30+
node
31+
end
32+
node
33+
end
34+
defp handle_node(node, _) do
35+
node
36+
end
37+
38+
end

lib/absinthe/phase/document/execution/data.ex

Lines changed: 43 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,27 +4,40 @@ defmodule Absinthe.Phase.Document.Execution.Data do
44
Produces data fit for external encoding from annotated value tree
55
"""
66

7-
alias Absinthe.Blueprint
7+
alias Absinthe.{Blueprint, Phase}
88
use Absinthe.Phase
99

1010
def run(blueprint) do
11-
result = blueprint
12-
|> Blueprint.current_operation
13-
|> process
14-
|> clean
11+
result = blueprint |> process
1512
{:ok, result}
1613
end
1714

18-
defp process(%Blueprint.Document.Operation{resolution: tree}) do
19-
{data, errors} = field_data(tree.fields, [])
20-
%{data: data, errors: errors}
15+
defp process(%Blueprint{} = blueprint) do
16+
{_, document_errors} = Blueprint.prewalk(blueprint, [], &document_errors/2)
17+
{data, errors} = case Blueprint.current_operation(blueprint) do
18+
nil ->
19+
{nil, document_errors}
20+
op ->
21+
field_data(op.resolution.fields, document_errors)
22+
end
23+
{:ok, format_result(data, errors |> Enum.uniq)}
2124
end
2225

23-
defp clean(%{data: data} = result) when map_size(data) == 0 do
24-
Map.delete(result, :data)
26+
defp format_result(nil, errors) do
27+
%{data: %{}, errors: Enum.map(errors, &format_error/1)}
2528
end
26-
defp clean(%{errors: []} = result) do
27-
Map.delete(result, :errors)
29+
defp format_result(data, []) do
30+
%{data: data}
31+
end
32+
defp format_result(data, errors) do
33+
%{data: data, errors: Enum.map(errors, &format_error/1)}
34+
end
35+
36+
defp document_errors(%{errors: errs} = node, acc) do
37+
{node, acc ++ errs}
38+
end
39+
defp document_errors(node, acc) do
40+
{node, acc}
2841
end
2942

3043
# Leaf
@@ -38,25 +51,37 @@ defmodule Absinthe.Phase.Document.Execution.Data do
3851

3952
defp list_data(fields, errors, acc \\ [])
4053
defp list_data([], errors, acc), do: {:lists.reverse(acc), errors}
41-
defp list_data([{:ok, field} | fields], errors, acc) do
54+
defp list_data([%{errors: []} = field | fields], errors, acc) do
4255
{value, errors} = data(field, errors)
4356
list_data(fields, errors, [value | acc])
4457
end
45-
defp list_data([{:error, error} | fields], errors, acc) do
46-
list_data(fields, List.wrap(error) ++ errors, acc)
58+
defp list_data([%{errors: errs} = field | fields], errors, acc) when length(errs) > 0 do
59+
list_data(fields, errs ++ errors, acc)
4760
end
4861

4962
defp field_data(fields, errors, acc \\ [])
5063
defp field_data([], errors, acc), do: {:maps.from_list(acc), errors}
51-
defp field_data([{:ok, field} | fields], errors, acc) do
64+
defp field_data([%{errors: []} = field | fields], errors, acc) do
5265
{value, errors} = data(field, errors)
5366
field_data(fields, errors, [{field_name(field), value} | acc])
5467
end
55-
defp field_data([{:error, error} | fields], errors, acc) do
56-
field_data(fields, List.wrap(error) ++ errors, acc)
68+
defp field_data([%{errors: errs} = field | fields], errors, acc) when length(errs) > 0 do
69+
field_data(fields, errs ++ errors, acc)
5770
end
5871

5972
defp field_name(%{emitter: %{alias: nil, name: name}}), do: name
6073
defp field_name(%{emitter: %{alias: name}}), do: name
6174
defp field_name(%{emitter: %{name: name}}), do: name
75+
76+
defp format_error(%Phase.Error{locations: []} = error) do
77+
%{message: error.message}
78+
end
79+
defp format_error(%Phase.Error{} = error) do
80+
%{message: error.message, locations: Enum.map(error.locations, &format_location/1)}
81+
end
82+
83+
defp format_location(%{line: line, column: col}) do
84+
%{line: line || 0, column: col || 0}
85+
end
86+
6287
end

0 commit comments

Comments
 (0)