graphql 0.9.5 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/lib/graphql/base_type.rb +24 -0
  3. data/lib/graphql/definition_helpers/defined_by_config.rb +5 -4
  4. data/lib/graphql/definition_helpers/non_null_with_bang.rb +1 -1
  5. data/lib/graphql/definition_helpers/type_definer.rb +1 -1
  6. data/lib/graphql/enum_type.rb +16 -3
  7. data/lib/graphql/input_object_type.rb +10 -0
  8. data/lib/graphql/language.rb +0 -5
  9. data/lib/graphql/language/nodes.rb +79 -75
  10. data/lib/graphql/language/parser.rb +109 -106
  11. data/lib/graphql/language/transform.rb +100 -91
  12. data/lib/graphql/language/visitor.rb +78 -74
  13. data/lib/graphql/list_type.rb +5 -0
  14. data/lib/graphql/non_null_type.rb +6 -2
  15. data/lib/graphql/query.rb +58 -9
  16. data/lib/graphql/query/arguments.rb +29 -26
  17. data/lib/graphql/query/base_execution/value_resolution.rb +3 -3
  18. data/lib/graphql/query/directive_chain.rb +1 -1
  19. data/lib/graphql/query/executor.rb +6 -27
  20. data/lib/graphql/query/literal_input.rb +89 -0
  21. data/lib/graphql/query/ruby_input.rb +20 -0
  22. data/lib/graphql/query/serial_execution/field_resolution.rb +1 -1
  23. data/lib/graphql/query/variables.rb +39 -0
  24. data/lib/graphql/scalar_type.rb +27 -5
  25. data/lib/graphql/schema.rb +5 -0
  26. data/lib/graphql/schema/type_expression.rb +28 -0
  27. data/lib/graphql/static_validation/literal_validator.rb +5 -2
  28. data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +0 -2
  29. data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +1 -1
  30. data/lib/graphql/version.rb +1 -1
  31. data/readme.md +3 -2
  32. data/spec/graphql/enum_type_spec.rb +7 -2
  33. data/spec/graphql/input_object_type_spec.rb +45 -0
  34. data/spec/graphql/language/parser_spec.rb +2 -1
  35. data/spec/graphql/language/transform_spec.rb +5 -0
  36. data/spec/graphql/query/base_execution/value_resolution_spec.rb +46 -0
  37. data/spec/graphql/query/context_spec.rb +37 -0
  38. data/spec/graphql/query/executor_spec.rb +33 -0
  39. data/spec/graphql/query_spec.rb +110 -26
  40. data/spec/graphql/schema/type_expression_spec.rb +38 -0
  41. data/spec/graphql/static_validation/rules/argument_literals_are_compatible_spec.rb +2 -2
  42. data/spec/support/dairy_app.rb +17 -17
  43. data/spec/support/dairy_data.rb +2 -2
  44. metadata +12 -2
@@ -1,108 +1,117 @@
1
- class Parslet::Context
2
- def create_node(name, attributes)
3
- node_class = GraphQL::Language::Nodes.const_get(name)
4
- node_class.new(attributes)
5
- end
6
- end
7
-
8
- module GraphQL::Language
9
- # {Transform} is a [parslet](http://kschiess.github.io/parslet/) transform for for turning the AST into objects in {GraphQL::Language::Nodes} objects.
10
- class Transform < Parslet::Transform
11
- def self.optional_sequence(name)
12
- rule(name => simple(:val)) { [] }
13
- rule(name => sequence(:val)) { val }
1
+ module GraphQL
2
+ module Language
3
+ # Extend `Parslet::Context` with a convenience method
4
+ # for building AST nodes
5
+ module ParsletContextCreateNode
6
+ refine Parslet::Context do
7
+ # @param [Symbol] name of a node constant
8
+ # @param [Hash] attributes to initialize the {Node} with
9
+ # @return [GraphQL::Language::Node] a node of type `name` with attributes `attributes`
10
+ def create_node(name, attributes)
11
+ node_class = GraphQL::Language::Nodes.const_get(name)
12
+ node_class.new(attributes)
13
+ end
14
+ end
14
15
  end
15
16
 
17
+ using ParsletContextCreateNode
16
18
 
19
+ # {Transform} is a [parslet](http://kschiess.github.io/parslet/) transform for for turning the AST into objects in {GraphQL::Language::Nodes} objects.
20
+ class Transform < Parslet::Transform
21
+ def self.optional_sequence(name)
22
+ rule(name => simple(:val)) { [] }
23
+ rule(name => sequence(:val)) { val }
24
+ end
17
25
 
18
- # Document
19
- rule(document_parts: sequence(:p)) { create_node(:Document, parts: p, line: (p.first ? p.first.line : 1), col: (p.first ? p.first.col : 1))}
20
- rule(document_parts: simple(:p)) { create_node(:Document, parts: [], line: 1, col: 1)}
26
+ # Document
27
+ rule(document_parts: sequence(:p)) { create_node(:Document, parts: p, line: (p.first ? p.first.line : 1), col: (p.first ? p.first.col : 1))}
28
+ rule(document_parts: simple(:p)) { create_node(:Document, parts: [], line: 1, col: 1)}
21
29
 
22
- # Fragment Definition
23
- rule(
24
- fragment_keyword: simple(:kw),
25
- fragment_name: simple(:name),
26
- type_condition: simple(:type),
27
- directives: sequence(:directives),
28
- selections: sequence(:selections)
29
- ) { create_node(:FragmentDefinition, name: name.to_s, type: type.to_s, directives: directives, selections: selections, position_source: kw)}
30
+ # Fragment Definition
31
+ rule(
32
+ fragment_keyword: simple(:kw),
33
+ fragment_name: simple(:name),
34
+ type_condition: simple(:type),
35
+ directives: sequence(:directives),
36
+ selections: sequence(:selections)
37
+ ) { create_node(:FragmentDefinition, name: name.to_s, type: type.to_s, directives: directives, selections: selections, position_source: kw)}
30
38
 
31
- rule(
32
- fragment_spread_keyword: simple(:kw),
33
- fragment_spread_name: simple(:n),
34
- directives: sequence(:d)
35
- ) { create_node(:FragmentSpread, name: n.to_s, directives: d, position_source: kw)}
39
+ rule(
40
+ fragment_spread_keyword: simple(:kw),
41
+ fragment_spread_name: simple(:n),
42
+ directives: sequence(:d)
43
+ ) { create_node(:FragmentSpread, name: n.to_s, directives: d, position_source: kw)}
36
44
 
37
- rule(
38
- fragment_spread_keyword: simple(:kw),
39
- inline_fragment_type: simple(:n),
40
- directives: sequence(:d),
41
- selections: sequence(:s),
42
- ) { create_node(:InlineFragment, type: n.to_s, directives: d, selections: s, position_source: kw)}
45
+ rule(
46
+ fragment_spread_keyword: simple(:kw),
47
+ inline_fragment_type: simple(:n),
48
+ directives: sequence(:d),
49
+ selections: sequence(:s),
50
+ ) { create_node(:InlineFragment, type: n.to_s, directives: d, selections: s, position_source: kw)}
43
51
 
44
- # Operation Definition
45
- rule(
46
- operation_type: simple(:ot),
47
- name: simple(:n),
48
- variables: sequence(:v),
49
- directives: sequence(:d),
50
- selections: sequence(:s),
51
- ) { create_node(:OperationDefinition, operation_type: ot.to_s, name: n.to_s, variables: v, directives: d, selections: s, position_source: ot) }
52
- optional_sequence(:optional_variables)
53
- rule(variable_name: simple(:n), variable_type: simple(:t), variable_optional_default_value: simple(:v)) { create_node(:Variable, name: n.name, type: t, default_value: v, line: n.line, col: n.col)}
54
- rule(variable_name: simple(:n), variable_type: simple(:t), variable_optional_default_value: sequence(:v)) { create_node(:Variable, name: n.name, type: t, default_value: v, line: n.line, col: n.col)}
55
- rule(variable_default_value: simple(:v) ) { v }
56
- rule(variable_default_value: sequence(:v) ) { v }
57
- # Query short-hand
58
- rule(unnamed_selections: sequence(:s)) { create_node(:OperationDefinition, selections: s, operation_type: "query", name: nil, variables: [], directives: [], line: s.first.line, col: s.first.col)}
52
+ # Operation Definition
53
+ rule(
54
+ operation_type: simple(:ot),
55
+ name: simple(:n),
56
+ variables: sequence(:v),
57
+ directives: sequence(:d),
58
+ selections: sequence(:s),
59
+ ) { create_node(:OperationDefinition, operation_type: ot.to_s, name: n.to_s, variables: v, directives: d, selections: s, position_source: ot) }
60
+ optional_sequence(:optional_variables)
61
+ rule(variable_name: simple(:n), variable_type: simple(:t), variable_optional_default_value: simple(:v)) { create_node(:Variable, name: n.name, type: t, default_value: v, line: n.line, col: n.col)}
62
+ rule(variable_name: simple(:n), variable_type: simple(:t), variable_optional_default_value: sequence(:v)) { create_node(:Variable, name: n.name, type: t, default_value: v, line: n.line, col: n.col)}
63
+ rule(variable_default_value: simple(:v) ) { v }
64
+ rule(variable_default_value: sequence(:v) ) { v }
65
+ # Query short-hand
66
+ rule(unnamed_selections: sequence(:s)) { create_node(:OperationDefinition, selections: s, operation_type: "query", name: nil, variables: [], directives: [], line: s.first.line, col: s.first.col)}
59
67
 
60
- # Field
61
- rule(
62
- alias: simple(:a),
63
- field_name: simple(:name),
64
- field_arguments: sequence(:args),
65
- directives: sequence(:dir),
66
- selections: sequence(:sel)
67
- ) { create_node(:Field, alias: a && a.to_s, name: name.to_s, arguments: args, directives: dir, selections: sel, position_source: [a, name].find { |part| !part.nil? }) }
68
+ # Field
69
+ rule(
70
+ alias: simple(:a),
71
+ field_name: simple(:name),
72
+ field_arguments: sequence(:args),
73
+ directives: sequence(:dir),
74
+ selections: sequence(:sel)
75
+ ) { create_node(:Field, alias: a && a.to_s, name: name.to_s, arguments: args, directives: dir, selections: sel, position_source: [a, name].find { |part| !part.nil? }) }
68
76
 
69
- rule(alias_name: simple(:a)) { a }
70
- optional_sequence(:optional_field_arguments)
71
- rule(field_argument_name: simple(:n), field_argument_value: simple(:v)) { create_node(:Argument, name: n.to_s, value: v, position_source: n)}
72
- rule(field_argument_name: simple(:n), field_argument_value: sequence(:v)) { create_node(:Argument, name: n.to_s, value: v, position_source: n)}
73
- optional_sequence(:optional_selections)
74
- optional_sequence(:optional_directives)
77
+ rule(alias_name: simple(:a)) { a }
78
+ optional_sequence(:optional_field_arguments)
79
+ rule(field_argument_name: simple(:n), field_argument_value: simple(:v)) { create_node(:Argument, name: n.to_s, value: v, position_source: n)}
80
+ rule(field_argument_name: simple(:n), field_argument_value: sequence(:v)) { create_node(:Argument, name: n.to_s, value: v, position_source: n)}
81
+ optional_sequence(:optional_selections)
82
+ optional_sequence(:optional_directives)
75
83
 
76
- # Directive
77
- rule(directive_name: simple(:name), directive_arguments: sequence(:args)) { create_node(:Directive, name: name.to_s, arguments: args, position_source: name ) }
78
- rule(directive_argument_name: simple(:n), directive_argument_value: simple(:v)) { create_node(:Argument, name: n.to_s, value: v, position_source: n)}
79
- optional_sequence(:optional_directive_arguments)
84
+ # Directive
85
+ rule(directive_name: simple(:name), directive_arguments: sequence(:args)) { create_node(:Directive, name: name.to_s, arguments: args, position_source: name ) }
86
+ rule(directive_argument_name: simple(:n), directive_argument_value: simple(:v)) { create_node(:Argument, name: n.to_s, value: v, position_source: n)}
87
+ optional_sequence(:optional_directive_arguments)
80
88
 
81
- # Type Defs
82
- rule(type_name: simple(:n)) { create_node(:TypeName, name: n.to_s, position_source: n) }
83
- rule(list_type: simple(:t)) { create_node(:ListType, of_type: t, line: t.line, col: t.col)}
84
- rule(non_null_type: simple(:t)) { create_node(:NonNullType, of_type: t, line: t.line, col: t.col)}
89
+ # Type Defs
90
+ rule(type_name: simple(:n)) { create_node(:TypeName, name: n.to_s, position_source: n) }
91
+ rule(list_type: simple(:t)) { create_node(:ListType, of_type: t, line: t.line, col: t.col)}
92
+ rule(non_null_type: simple(:t)) { create_node(:NonNullType, of_type: t, line: t.line, col: t.col)}
85
93
 
86
- # Values
87
- rule(array: sequence(:v)) { v }
88
- rule(boolean: simple(:v)) { v == "true" ? true : false }
89
- rule(input_object: sequence(:v)) { create_node(:InputObject, pairs: v, line: v.first.line, col: v.first.col) }
90
- rule(input_object_name: simple(:n), input_object_value: simple(:v)) { create_node(:Argument, name: n.to_s, value: v, position_source: n)}
91
- rule(input_object_name: simple(:n), input_object_value: sequence(:v)) { create_node(:Argument, name: n.to_s, value: v, position_source: n)}
92
- rule(int: simple(:v)) { v.to_i }
93
- rule(float: simple(:v)) { v.to_f }
94
+ # Values
95
+ rule(array: sequence(:v)) { v }
96
+ rule(boolean: simple(:v)) { v == "true" ? true : false }
97
+ rule(input_object: sequence(:v)) { create_node(:InputObject, pairs: v, line: v.first.line, col: v.first.col) }
98
+ rule(input_object_name: simple(:n), input_object_value: simple(:v)) { create_node(:Argument, name: n.to_s, value: v, position_source: n)}
99
+ rule(input_object_name: simple(:n), input_object_value: sequence(:v)) { create_node(:Argument, name: n.to_s, value: v, position_source: n)}
100
+ rule(int: simple(:v)) { v.to_i }
101
+ rule(float: simple(:v)) { v.to_f }
94
102
 
95
- ESCAPES = /\\(["\\\/bfnrt])/
96
- UTF_8 = /\\u[\da-f]{4}/i
97
- UTF_8_REPLACE = -> (m) { [m[-4..-1].to_i(16)].pack('U') }
103
+ ESCAPES = /\\(["\\\/bfnrt])/
104
+ UTF_8 = /\\u[\da-f]{4}/i
105
+ UTF_8_REPLACE = -> (m) { [m[-4..-1].to_i(16)].pack('U') }
98
106
 
99
- rule(string: simple(:v)) {
100
- string = v.to_s
101
- string.gsub!(ESCAPES, '\1')
102
- string.gsub!(UTF_8, &UTF_8_REPLACE)
103
- string
104
- }
105
- rule(variable: simple(:v)) { create_node(:VariableIdentifier, name: v.to_s, position_source: v) }
106
- rule(enum: simple(:v)) { create_node(:Enum, name: v.to_s, position_source: v)}
107
+ rule(string: simple(:v)) {
108
+ string = v.to_s
109
+ string.gsub!(ESCAPES, '\1')
110
+ string.gsub!(UTF_8, &UTF_8_REPLACE)
111
+ string
112
+ }
113
+ rule(variable: simple(:v)) { create_node(:VariableIdentifier, name: v.to_s, position_source: v) }
114
+ rule(enum: simple(:v)) { create_node(:Enum, name: v.to_s, position_source: v)}
115
+ end
107
116
  end
108
117
  end
@@ -1,88 +1,92 @@
1
- # Depth-first traversal through the tree, calling hooks at each stop.
2
- #
3
- # @example Create a visitor, add hooks, then search a document
4
- # total_field_count = 0
5
- # visitor = GraphQL::Language::Visitor.new
6
- # # Whenever you find a field, increment the field count:
7
- # visitor[GraphQL::Language::Nodes::Field] << -> (node) { total_field_count += 1 }
8
- # # When we finish, print the field count:
9
- # visitor[GraphQL::Language::Nodes::Document].leave << -> (node) { p total_field_count }
10
- # visitor.visit(document)
11
- # # => 6
12
- #
13
- class GraphQL::Language::Visitor
14
- # If any hook returns this value, the {Visitor} stops visiting this
15
- # node right away
16
- SKIP = :_skip
1
+ module GraphQL
2
+ module Language
3
+ # Depth-first traversal through the tree, calling hooks at each stop.
4
+ #
5
+ # @example Create a visitor, add hooks, then search a document
6
+ # total_field_count = 0
7
+ # visitor = GraphQL::Language::Visitor.new
8
+ # # Whenever you find a field, increment the field count:
9
+ # visitor[GraphQL::Language::Nodes::Field] << -> (node) { total_field_count += 1 }
10
+ # # When we finish, print the field count:
11
+ # visitor[GraphQL::Language::Nodes::Document].leave << -> (node) { p total_field_count }
12
+ # visitor.visit(document)
13
+ # # => 6
14
+ #
15
+ class Visitor
16
+ # If any hook returns this value, the {Visitor} stops visiting this
17
+ # node right away
18
+ SKIP = :_skip
17
19
 
18
- # @return [Array<Proc>] Hooks to call when entering _any_ node
19
- attr_reader :enter
20
- # @return [Array<Proc>] Hooks to call when leaving _any_ node
21
- attr_reader :leave
20
+ # @return [Array<Proc>] Hooks to call when entering _any_ node
21
+ attr_reader :enter
22
+ # @return [Array<Proc>] Hooks to call when leaving _any_ node
23
+ attr_reader :leave
22
24
 
23
- def initialize
24
- @visitors = {}
25
- @enter = []
26
- @leave = []
27
- end
25
+ def initialize
26
+ @visitors = {}
27
+ @enter = []
28
+ @leave = []
29
+ end
28
30
 
29
- # Get a {NodeVisitor} for `node_class`
30
- # @param node_class [Class] The node class that you want to listen to
31
- # @return [NodeVisitor]
32
- #
33
- # @example Run a hook whenever you enter a new Field
34
- # visitor[GraphQL::Language::Nodes::Field] << -> (node, parent) { p "Here's a field" }
35
- def [](node_class)
36
- @visitors[node_class] ||= NodeVisitor.new
37
- end
31
+ # Get a {NodeVisitor} for `node_class`
32
+ # @param node_class [Class] The node class that you want to listen to
33
+ # @return [NodeVisitor]
34
+ #
35
+ # @example Run a hook whenever you enter a new Field
36
+ # visitor[GraphQL::Language::Nodes::Field] << -> (node, parent) { p "Here's a field" }
37
+ def [](node_class)
38
+ @visitors[node_class] ||= NodeVisitor.new
39
+ end
38
40
 
39
- # Visit `root` and all children, applying hooks as you go
40
- # @param root [GraphQL::Language::Nodes::AbstractNode] some node to start parsing on
41
- # @return [void]
42
- def visit(root, parent=nil)
43
- begin_visit(root, parent) &&
44
- root.children.reduce(true) { |memo, child| memo && visit(child, root) }
45
- end_visit(root, parent)
46
- end
41
+ # Visit `root` and all children, applying hooks as you go
42
+ # @param root [GraphQL::Language::Nodes::AbstractNode] some node to start parsing on
43
+ # @return [void]
44
+ def visit(root, parent=nil)
45
+ begin_visit(root, parent) &&
46
+ root.children.reduce(true) { |memo, child| memo && visit(child, root) }
47
+ end_visit(root, parent)
48
+ end
47
49
 
48
- private
50
+ private
49
51
 
50
- def begin_visit(node, parent)
51
- self.class.apply_hooks(enter, node, parent)
52
- node_visitor = self[node.class]
53
- self.class.apply_hooks(node_visitor.enter, node, parent)
54
- end
52
+ def begin_visit(node, parent)
53
+ self.class.apply_hooks(enter, node, parent)
54
+ node_visitor = self[node.class]
55
+ self.class.apply_hooks(node_visitor.enter, node, parent)
56
+ end
55
57
 
56
- # Should global `leave` visitors come first or last?
57
- def end_visit(node, parent)
58
- self.class.apply_hooks(leave, node, parent)
59
- node_visitor = self[node.class]
60
- self.class.apply_hooks(node_visitor.leave, node, parent)
61
- end
58
+ # Should global `leave` visitors come first or last?
59
+ def end_visit(node, parent)
60
+ self.class.apply_hooks(leave, node, parent)
61
+ node_visitor = self[node.class]
62
+ self.class.apply_hooks(node_visitor.leave, node, parent)
63
+ end
62
64
 
63
- # If one of the visitors returns SKIP, stop visiting this node
64
- def self.apply_hooks(hooks, node, parent)
65
- hooks.reduce(true) { |memo, proc| memo && (proc.call(node, parent) != SKIP) }
66
- end
65
+ # If one of the visitors returns SKIP, stop visiting this node
66
+ def self.apply_hooks(hooks, node, parent)
67
+ hooks.reduce(true) { |memo, proc| memo && (proc.call(node, parent) != SKIP) }
68
+ end
67
69
 
68
- # Collect `enter` and `leave` hooks for classes in {GraphQL::Language::Nodes}
69
- #
70
- # Access {NodeVisitor}s via {GraphQL::Language::Visitor#[]}
71
- class NodeVisitor
72
- # @return [Array<Proc>] Hooks to call when entering a node of this type
73
- attr_reader :enter
74
- # @return [Array<Proc>] Hooks to call when leaving a node of this type
75
- attr_reader :leave
70
+ # Collect `enter` and `leave` hooks for classes in {GraphQL::Language::Nodes}
71
+ #
72
+ # Access {NodeVisitor}s via {GraphQL::Language::Visitor#[]}
73
+ class NodeVisitor
74
+ # @return [Array<Proc>] Hooks to call when entering a node of this type
75
+ attr_reader :enter
76
+ # @return [Array<Proc>] Hooks to call when leaving a node of this type
77
+ attr_reader :leave
76
78
 
77
- def initialize
78
- @enter = []
79
- @leave = []
80
- end
79
+ def initialize
80
+ @enter = []
81
+ @leave = []
82
+ end
81
83
 
82
- # Shorthand to add a hook to the {#enter} array
83
- # @param hook [Proc] A hook to add
84
- def <<(hook)
85
- enter << hook
84
+ # Shorthand to add a hook to the {#enter} array
85
+ # @param hook [Proc] A hook to add
86
+ def <<(hook)
87
+ enter << hook
88
+ end
89
+ end
86
90
  end
87
91
  end
88
92
  end
@@ -16,4 +16,9 @@ class GraphQL::ListType < GraphQL::BaseType
16
16
  def to_s
17
17
  "[#{of_type.to_s}]"
18
18
  end
19
+
20
+ def coerce_input(value)
21
+ inner_type = of_type
22
+ value.map { |item| inner_type.coerce_input!(item) }
23
+ end
19
24
  end
@@ -13,8 +13,12 @@ class GraphQL::NonNullType < GraphQL::BaseType
13
13
  "Non-Null"
14
14
  end
15
15
 
16
- def coerce(value)
17
- of_type.coerce(value)
16
+ def coerce_input(value)
17
+ of_type.coerce_input(value)
18
+ end
19
+
20
+ def coerce_result(value)
21
+ of_type.coerce_result(value)
18
22
  end
19
23
 
20
24
  def kind
@@ -1,9 +1,23 @@
1
1
  class GraphQL::Query
2
+ class OperationNameMissingError < StandardError
3
+ def initialize(names)
4
+ msg = "You must provide an operation name from: #{names.join(", ")}"
5
+ super(msg)
6
+ end
7
+ end
8
+
9
+ class VariableMissingError < StandardError
10
+ def initialize(name, type)
11
+ msg = "Variable #{name} of type #{type} can't be null"
12
+ super(msg)
13
+ end
14
+ end
15
+
2
16
  # If a resolve function returns `GraphQL::Query::DEFAULT_RESOLVE`,
3
17
  # The executor will send the field's name to the target object
4
18
  # and use the result.
5
19
  DEFAULT_RESOLVE = :__default_resolve
6
- attr_reader :schema, :document, :context, :fragments, :variables, :operations, :debug
20
+ attr_reader :schema, :document, :context, :fragments, :operations, :debug
7
21
 
8
22
  # Prepare query `query_string` on `schema`
9
23
  # @param schema [GraphQL::Schema]
@@ -17,13 +31,11 @@ class GraphQL::Query
17
31
  @schema = schema
18
32
  @debug = debug
19
33
  @context = Context.new(values: context)
20
-
21
- @variables = variables
22
34
  @validate = validate
23
35
  @operation_name = operation_name
24
36
  @fragments = {}
25
37
  @operations = {}
26
-
38
+ @provided_variables = variables
27
39
  @document = GraphQL.parse(query_string)
28
40
  @document.parts.each do |part|
29
41
  if part.is_a?(GraphQL::Language::Nodes::FragmentDefinition)
@@ -40,20 +52,57 @@ class GraphQL::Query
40
52
  return { "errors" => validation_errors }
41
53
  end
42
54
 
43
- @result ||= Executor.new(self, @operation_name).result
55
+ @result ||= Executor.new(self).result
56
+ end
57
+
58
+
59
+ # This is the operation to run for this query.
60
+ # If more than one operation is present, it must be named at runtime.
61
+ # @return [GraphQL::Language::Nodes::OperationDefinition, nil]
62
+ def selected_operation
63
+ @selected_operation ||= find_operation(@operations, @operation_name)
64
+ end
65
+
66
+ # Determine the values for variables of this query, using default values
67
+ # if a value isn't provided at runtime.
68
+ #
69
+ # Raises if a non-null variable isn't provided at runtime.
70
+ # @return [GraphQL::Query::Variables] Variables to apply to this query
71
+ def variables
72
+ @variables ||= GraphQL::Query::Variables.new(
73
+ schema,
74
+ selected_operation.variables,
75
+ @provided_variables
76
+ )
44
77
  end
45
78
 
46
79
  private
47
80
 
48
81
  def validation_errors
49
- @validation_errors ||= @schema.static_validator.validate(@document)
82
+ @validation_errors ||= schema.static_validator.validate(document)
83
+ end
84
+
85
+
86
+ def find_operation(operations, operation_name)
87
+ if operations.length == 1
88
+ operations.values.first
89
+ elsif operations.length == 0
90
+ nil
91
+ elsif !operations.key?(operation_name)
92
+ raise OperationNameMissingError, operations.keys
93
+ else
94
+ operations[operation_name]
95
+ end
50
96
  end
51
97
  end
52
98
 
53
99
  require 'graphql/query/arguments'
54
100
  require 'graphql/query/base_execution'
55
- require 'graphql/query/serial_execution'
56
- require 'graphql/query/type_resolver'
101
+ require 'graphql/query/context'
57
102
  require 'graphql/query/directive_chain'
58
103
  require 'graphql/query/executor'
59
- require 'graphql/query/context'
104
+ require 'graphql/query/literal_input'
105
+ require 'graphql/query/ruby_input'
106
+ require 'graphql/query/serial_execution'
107
+ require 'graphql/query/type_resolver'
108
+ require 'graphql/query/variables'