graphql-ruby
graphql-ruby copied to clipboard
@export directive
I was reading through the documentation and I found a few mentions of the @export directive.
I can see how you can easily create a custom directive. My plan of action would be to add the variable when the directive is resolved, but I don't know where to hook it up so you don't hit the Variable $xxx is used by anonymous query but not declared error. (I also don't know if the query is done top to bottom, so if I would add the @export directive on top and then use it later, if that would work.
Having very little experience with the source code of this gem, I was wondering if you deem it feasible to implement an @export directive with the current hooks. Any pointers in a specific direction would also be super helpful 😄.
(running the resolve and changing the variable is also not very clean at the moment in my proof of concept) I'm doing
context.query.variables.instance_eval("@storage")[arguments[:as]]
Which obviously goes deep in the internals of the implementation. But I don't know if there's a cleaner way for this?
After a bit of experimentation, this seems to do the basic version of what I want, but it's very dirty. So wondering if there are better hooks/ways to do this:
module Directives
class Export < GraphQL::Schema::Directive
description "Exports the variable from one query to another."
locations(
GraphQL::Schema::Directive::FIELD,
)
argument :as, String, description: "The name of the variable to export"
# Implement the Directive API
def self.resolve(object, arguments, context)
return_value = yield
context.query.variables.instance_eval("@storage")[arguments[:as]] = return_value
return_value
end
end
end
module GraphQL::StaticValidation::VariablesAreUsedAndDefined
def on_directive(node, parent)
# copy from on_operation_definition
if node.name == "export"
# initialize the hash of vars for this context:
# mark variables as defined:
usage_context = @variable_context_stack.last
var_hash = @variable_usages_for_context[usage_context]
var_name = node.arguments.find { |arg| arg.name == "as" }.value
var_usage = var_hash[var_name]
var_usage.declared_by = node
var_usage.path = context.path
super
end
end
end
Hey! In general, your implementation above seems good to me. It looks like Query::Variables could be improved to have a first-class API for setting new values. As a slight improvement, you could use query.variables.instance_variable_get(:@storate) instead of instance_eval. (It does the same thing, but it avoids instance_eval which gives me the willies.)
Thanks! It didn't feel super clean, but if you say this is the way, then I'm taking your word for it 😊.
Would there be a way to hook into the StaticValidation in a way that is actually 'supported'? For example a hook of some sorts on the directive?
Both Query and Schema support setting a static validator into which you can customize the rules. So, just write your own rule and add it to the set along with all the defaults. See https://github.com/rmosolgo/graphql-ruby/pull/4529/files