-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Add JSON-path to execution errors #259
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Hmm, I should probably an add array example. It should look something like |
👍 with a test for array indices I'm searching for a centralized approach while rewriting |
@@ -35,6 +35,7 @@ def result | |||
def get_finished_value(raw_value) | |||
if raw_value.is_a?(GraphQL::ExecutionError) | |||
raw_value.ast_node = irep_node.ast_node | |||
raw_value.path = irep_node.path |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@rmosolgo I'm a little stumped on how to get the current collection index context here. Just want to double check its not available somehow that I'm just missing. Otherwise I can try to hack some extra state into the execution context.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
😭 sadly it's totally not available.
Here's the .map
where the index would come from: https://github.com/rmosolgo/graphql-ruby/blob/master/lib/graphql/query/serial_execution/value_resolution.rb#L48
This feature is a hard requirement for DeferredExecution
and you can see the implementation here:
graphql-ruby/lib/graphql/execution/deferred_execution.rb
Lines 386 to 391 in 8e7700c
inner_frame = ExecFrame.new({ | |
node: frame.node, | |
path: frame.path + [idx], | |
type: wrapped_type, | |
value: item, | |
}) |
Although ... it doesn't really apply here :S I guess we have our pick among so-so options:
- mutate the
irep_node
for each loop in themap
- copy the
irep_node
and give the copy a newpath
for each loop in the map (irep_node.dup
is supported -- I think it's used for fragment inlining) - add some kind of proxy object to wrap the irep node & send it into each loop of the map
- ????
Well, added a failing test 😁 |
@rmosolgo So I think we should actually consider reverting #198, or at least renaming that
#198 defines a path format for validation errors. However, with the semantics defined in graphql-js, this should only be used as a path into the JSON result data. Which is there none in a validation error. |
Don't love the collection index implementation, but it appears to work. Any suggestions for test cases that would probably break this? |
Ugh, are either of these covered by the spec? All I can find is:
It looks like everybody is trying to be "more helpful", but in conflicting ways. There is a way that they can be disambiguated: if Anyways, I don't know that validation-error- |
Proposed renaming in #264 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me, except maybe that dup
we can spare. Do you want to wait for #264 or can this be merged independently?
@@ -72,6 +72,15 @@ def owner | |||
end | |||
end | |||
|
|||
def path | |||
path = parent ? parent.path.dup : [] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why .dup
, doesn't the method return a new array each time?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed.
def path | ||
path = parent ? parent.path.dup : [] | ||
path << name if name | ||
path << @index if @index |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just to make sure I understand, name
and @index
aren't mutually exclusive, right? An IRep node that represents a selection on a list type will have both name
(the field name) and @index
(the current iteration number)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Correct.
In the case of the root IRep, theres no name
or @index
.
@@ -45,7 +45,8 @@ class ListResolution < BaseResolution | |||
def non_null_result | |||
wrapped_type = field_type.of_type | |||
strategy_class = get_strategy_for_kind(wrapped_type.kind) | |||
value.map do |item| | |||
value.each_with_index.map do |item, index| | |||
irep_node.index = index |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mutation is generally error-prone, but I'm open to it here, since it's a hot path and not used intensively. Maybe one good thing would be to put it back to nil
when we're done, just in case? I can't think of why it would matter though. :S
An alternative implementation would be to define a delegator, something like
inner_node = IndexedNode.new(irep_node, index)
where IndexedNode
is like
class IndexedNode
extend Forwardable
def initialize(irep_node, index)
@irep_node = irep_node
@index = index
end
def path
@irep_node.path + [@index]
end
def_delegators :@irep_node, :name, :definitions, :directives, # ?? Lots of stuff
end
I find this implementation less scary, since you never mess with the original irep_node
, but it's also less efficient. I'm happy to leave it as is and refactor to something like ☝️ that if we actually have issues.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe one good thing would be to put it back to nil when we're done, just in case?
Reseting index
back to nil
in adf0c0f
Cool. Appears to have no conflicting files either. |
Nice, thanks for making this happen! Looking forward to see what it can be used for :) |
Implements
path
on error messages as seen in graphql-js.https://github.com/graphql/graphql-js/blob/89f3f1a319bb913616f2910e8a806170155ffaf0/src/error/GraphQLError.js#L42-L48
graphql/graphql-js#396
To: @rmosolgo
CC: @kdaigle