Skip to content

Commit a36fead

Browse files
author
Robert Mosolgo
authored
Merge pull request #259 from josh/execution-error-path
Add JSON-path to execution errors
2 parents 0d1e787 + b10f40d commit a36fead

File tree

10 files changed

+109
-11
lines changed

10 files changed

+109
-11
lines changed

lib/graphql/execution_error.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ class ExecutionError < GraphQL::Error
66
# @return [GraphQL::Language::Nodes::Field] the field where the error occured
77
attr_accessor :ast_node
88

9+
# @return [String] an array describing the JSON-path into the execution
10+
# response which corresponds to this error.
11+
attr_accessor :path
12+
913
def initialize(message, ast_node: nil)
1014
@ast_node = ast_node
1115
super(message)
@@ -24,6 +28,9 @@ def to_h
2428
}
2529
]
2630
end
31+
if path
32+
hash["path"] = path
33+
end
2734
hash
2835
end
2936
end

lib/graphql/internal_representation/node.rb

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,15 @@ def owner
7272
end
7373
end
7474

75+
def path
76+
path = parent ? parent.path : []
77+
path << name if name
78+
path << @index if @index
79+
path
80+
end
81+
82+
attr_writer :index
83+
7584
def inspect(indent = 0)
7685
own_indent = " " * indent
7786
self_inspect = "#{own_indent}<Node #{name} (#{definition_name}: {#{definitions.keys.join("|")}} -> #{return_type})>"

lib/graphql/query/serial_execution/field_resolution.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ def result
3535
def get_finished_value(raw_value)
3636
if raw_value.is_a?(GraphQL::ExecutionError)
3737
raw_value.ast_node = irep_node.ast_node
38+
raw_value.path = irep_node.path
3839
execution_context.add_error(raw_value)
3940
end
4041

lib/graphql/query/serial_execution/value_resolution.rb

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,13 @@ class ListResolution < BaseResolution
4545
def non_null_result
4646
wrapped_type = field_type.of_type
4747
strategy_class = get_strategy_for_kind(wrapped_type.kind)
48-
value.map do |item|
48+
result = value.each_with_index.map do |item, index|
49+
irep_node.index = index
4950
inner_strategy = strategy_class.new(item, wrapped_type, target, parent_type, irep_node, execution_context)
5051
inner_strategy.result
5152
end
53+
irep_node.index = nil
54+
result
5255
end
5356
end
5457

spec/graphql/execution_error_spec.rb

Lines changed: 63 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,28 @@
1818
}
1919
flavor
2020
}
21+
allDairy {
22+
... on Cheese {
23+
flavor
24+
}
25+
... on Milk {
26+
source
27+
executionError
28+
}
29+
}
30+
dairy {
31+
milks {
32+
source
33+
executionError
34+
allDairy {
35+
__typename
36+
... on Milk {
37+
origin
38+
executionError
39+
}
40+
}
41+
}
42+
}
2143
executionError
2244
}
2345
@@ -38,20 +60,58 @@
3860
},
3961
"flavor" => "Brie",
4062
},
63+
"allDairy" => [
64+
{ "flavor" => "Brie" },
65+
{ "flavor" => "Gouda" },
66+
{ "flavor" => "Manchego" },
67+
{ "source" => "COW", "executionError" => nil }
68+
],
69+
"dairy" => {
70+
"milks" => [
71+
{
72+
"source" => "COW",
73+
"executionError" => nil,
74+
"allDairy" => [
75+
{ "__typename" => "Cheese" },
76+
{ "__typename" => "Cheese" },
77+
{ "__typename" => "Cheese" },
78+
{ "__typename" => "Milk", "origin" => "Antiquity", "executionError" => nil }
79+
]
80+
}
81+
]
82+
},
4183
"executionError" => nil,
4284
},
4385
"errors"=>[
4486
{
4587
"message"=>"No cheeses are made from Yak milk!",
46-
"locations"=>[{"line"=>5, "column"=>9}]
88+
"locations"=>[{"line"=>5, "column"=>9}],
89+
"path"=>["cheese", "error1"]
4790
},
4891
{
4992
"message"=>"No cheeses are made from Yak milk!",
50-
"locations"=>[{"line"=>8, "column"=>9}]
93+
"locations"=>[{"line"=>8, "column"=>9}],
94+
"path"=>["cheese", "error2"]
95+
},
96+
{
97+
"message"=>"There was an execution error",
98+
"locations"=>[{"line"=>22, "column"=>11}],
99+
"path"=>["allDairy", 3, "executionError"]
100+
},
101+
{
102+
"message"=>"There was an execution error",
103+
"locations"=>[{"line"=>28, "column"=>11}],
104+
"path"=>["dairy", "milks", 0, "executionError"]
105+
},
106+
{
107+
"message"=>"There was an execution error",
108+
"locations"=>[{"line"=>33, "column"=>15}],
109+
"path"=>["dairy", "milks", 0, "allDairy", 3, "executionError"]
51110
},
52111
{
53112
"message"=>"There was an execution error",
54-
"locations"=>[{"line"=>16, "column"=>7}]
113+
"locations"=>[{"line"=>38, "column"=>7}],
114+
"path"=>["executionError"]
55115
},
56116
]
57117
}

spec/graphql/introspection/type_type_spec.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@
4141
{"name"=>"LocalProduct"},
4242
],
4343
"fields"=>[
44+
{"type"=>{"name"=>"List", "ofType"=>{"name"=>"DairyProduct"}}},
45+
{"type"=>{"name"=>"String", "ofType"=>nil}},
4446
{"type"=>{"name"=>"Non-Null", "ofType"=>{"name"=>"Float"}}},
4547
{"type"=>{"name"=>"List", "ofType"=>{"name"=>"String"}}},
4648
{"type"=>{"name"=>"Non-Null", "ofType"=>{"name"=>"ID"}}},

spec/graphql/query/executor_spec.rb

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,8 @@
143143
"errors" => [
144144
{
145145
"message" => "BOOM",
146-
"locations" => [ { "line" => 1, "column" => 28 } ]
146+
"locations" => [ { "line" => 1, "column" => 28 } ],
147+
"path" => ["noMilk", "cow", "cantBeNullButRaisesExecutionError"]
147148
}
148149
]
149150
}
@@ -167,7 +168,8 @@
167168
"errors"=>[
168169
{
169170
"message"=>"Error was handled!",
170-
"locations" => [{"line"=>1, "column"=>17}]
171+
"locations" => [{"line"=>1, "column"=>17}],
172+
"path"=>["noMilk", "error"]
171173
}
172174
]
173175
}

spec/graphql/schema/catchall_middleware_spec.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
{
2323
"message"=>"Internal error",
2424
"locations"=>[{"line"=>1, "column"=>17}],
25+
"path"=>["noMilk", "error"]
2526
},
2627
]
2728
}

spec/graphql/schema/timeout_middleware_spec.rb

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -64,11 +64,13 @@
6464
expected_errors = [
6565
{
6666
"message"=>"Timeout on Query.sleepFor",
67-
"locations"=>[{"line"=>6, "column"=>9}]
67+
"locations"=>[{"line"=>6, "column"=>9}],
68+
"path"=>["d"]
6869
},
6970
{
7071
"message"=>"Timeout on Query.sleepFor",
71-
"locations"=>[{"line"=>7, "column"=>9}]
72+
"locations"=>[{"line"=>7, "column"=>9}],
73+
"path"=>["e"]
7274
},
7375
]
7476
assert_equal expected_data, result["data"]
@@ -116,11 +118,13 @@
116118
expected_errors = [
117119
{
118120
"message"=>"Timeout on NestedSleep.seconds",
119-
"locations"=>[{"line"=>10, "column"=>15}]
121+
"locations"=>[{"line"=>10, "column"=>15}],
122+
"path"=>["a", "b", "c", "d", "seconds"]
120123
},
121124
{
122125
"message"=>"Timeout on NestedSleep.nestedSleep",
123-
"locations"=>[{"line"=>11, "column"=>15}]
126+
"locations"=>[{"line"=>11, "column"=>15}],
127+
"path"=>["a", "b", "c", "d", "e"]
124128
},
125129
]
126130

@@ -149,7 +153,8 @@
149153
expected_errors = [
150154
{
151155
"message"=>"Timeout on Query.sleepFor",
152-
"locations"=>[{"line"=>6, "column"=>9}]
156+
"locations"=>[{"line"=>6, "column"=>9}],
157+
"path"=>["d"]
153158
},
154159
]
155160

spec/support/dairy_app.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,14 @@ class NoSuchDairyError < StandardError; end
105105
args[:limit] ? milk.flavors.first(args[:limit]) : milk.flavors
106106
}
107107
end
108+
field :executionError do
109+
type GraphQL::STRING_TYPE
110+
resolve -> (t, a, c) { raise(GraphQL::ExecutionError, "There was an execution error") }
111+
end
112+
113+
field :allDairy, -> { types[DairyProductUnion] } do
114+
resolve -> (obj, args, ctx) { CHEESES.values + MILKS.values }
115+
end
108116
end
109117

110118
SweetenerInterface = GraphQL::InterfaceType.define do

0 commit comments

Comments
 (0)