Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
module Gateway
class Graphql
OP_FIELD = GraphQL::Language::Nodes::OperationDefinition
def initialize(proxy, error_presenter)
@error_presenter = error_presenter
@proxy = proxy
@output = {"data" => {}}
end
# Given a graphQL document, execute those OperationDefinitions which map to a RPC
# The document will be modified to not include those fields / selections which were executed.
def execute(document, variables = {}, metadata = {})
@variables = variables || {}
@metadata = metadata || {}
document.definitions.reject! do |d|
# Filter out fields handled by GRPC.
d.selections.reject! { |s| graphql(s) } if d.is_a?(OP_FIELD)
d.selections.empty? # Filter the empty operations.
end
@output
end
private
# Execute a GraphQL field as an RPC on the proxy.
# @param field [GraphQL::Language::Nodes::GraphqlSelection] the RPC field.
# @param block [Proc] the presenter for the error handler.
def graphql(field)
return false unless @proxy.respond_to?(field.name)
key = (field.alias || field.name).to_s
resp = @proxy.rpc(field.name, vars_from(field), @metadata)
@output["data"][key] = present(field, resp)
return true
rescue => e
@output["errors"] ||= []
@output["errors"] << @error_presenter.call(e)
return true
end
# Filter the response down to the selected fields.
# n.b., the GRPC server should not include unselected fields, but those fields will appear
# as blank/empty in the response unless we actually filter the keys.
def present(field, resp)
return resp unless field.selections && resp.is_a?(Hash)
if resp.keys == [:seconds, :nanos] # FUGLY way to detect timestamps...
return Time.at(resp[:seconds]).to_datetime
end
field.selections.each_with_object({}) do |s, out|
out[(s.alias || s.name).to_s] = present(s, resp[s.name.to_sym])
end
end
# Extract the variables from a field.
def vars_from(field)
vars = {}
vars[:selections] = graphql_selections_array(field)
field.arguments.each do |arg|
val = if arg.value.is_a?(GraphQL::Language::Nodes::VariableIdentifier)
@variables[arg.value.name.to_s]
elsif arg.value.is_a?(GraphQL::Language::Nodes::Enum)
arg.value.name
else
arg.value
end
vars[arg.name] = val
end
vars
end
# Turn a field into a selections object for the RPC.
def graphql_selections_array(field)
return nil unless field && field.selections.any?
field.selections.map do |sel|
{
:name => sel.name,
:selections => graphql_selections_array(sel)
}
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment