graphql 1.5.10 → 1.5.11
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/generators/graphql/templates/function.erb +1 -1
- data/lib/generators/graphql/templates/schema.erb +1 -2
- data/lib/generators/graphql/type_generator.rb +4 -2
- data/lib/graphql/analysis/analyze_query.rb +1 -1
- data/lib/graphql/function.rb +1 -1
- data/lib/graphql/internal_representation.rb +1 -0
- data/lib/graphql/internal_representation/document.rb +27 -0
- data/lib/graphql/internal_representation/rewrite.rb +12 -7
- data/lib/graphql/list_type.rb +8 -3
- data/lib/graphql/non_null_type.rb +1 -0
- data/lib/graphql/query.rb +10 -6
- data/lib/graphql/schema.rb +3 -2
- data/lib/graphql/schema/build_from_definition.rb +5 -2
- data/lib/graphql/static_validation/definition_dependencies.rb +36 -22
- data/lib/graphql/static_validation/validator.rb +2 -2
- data/lib/graphql/version.rb +1 -1
- data/spec/generators/graphql/function_generator_spec.rb +1 -1
- data/spec/generators/graphql/install_generator_spec.rb +1 -2
- data/spec/generators/graphql/object_generator_spec.rb +9 -0
- data/spec/graphql/internal_representation/rewrite_spec.rb +42 -4
- data/spec/graphql/list_type_spec.rb +20 -0
- data/spec/graphql/query/executor_spec.rb +21 -0
- data/spec/graphql/query_spec.rb +36 -0
- data/spec/graphql/schema/build_from_definition_spec.rb +16 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bb2841b1c8677bc9188757eca08b2f365f0e0af8
|
4
|
+
data.tar.gz: 12bc61f9684e3bae2a4d06c22f9378064fcf191d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7859ae0afed299edb29dedc59d014f057b85dde4e3547a300d312c0ba137f23cc23d6c70c664a3afd6985c3b393483aa012c8e7ba6e70e0713ad203163dbc089
|
7
|
+
data.tar.gz: 61ab9c38a66e013b09bec6f5865aae178110ba99b230b2a94b325ea1afef7b1bb9beb98744c52cee0d8b2c2813b377a39dbef13c920bf319cddfeef1ba2f3e07
|
@@ -1,6 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
require 'rails/generators/base'
|
3
3
|
require 'graphql'
|
4
|
+
require 'active_support'
|
5
|
+
require 'active_support/core_ext/string/inflections'
|
4
6
|
|
5
7
|
module Graphql
|
6
8
|
module Generators
|
@@ -39,10 +41,10 @@ module Graphql
|
|
39
41
|
when "Int", "Float", "Boolean", "String", "ID"
|
40
42
|
"types.#{type_expression}"
|
41
43
|
else
|
42
|
-
"Types::#{type_expression}Type"
|
44
|
+
"Types::#{type_expression.camelize}Type"
|
43
45
|
end
|
44
46
|
when :graphql
|
45
|
-
type_expression
|
47
|
+
type_expression.camelize
|
46
48
|
else
|
47
49
|
raise "Unexpected normalize mode: #{mode}"
|
48
50
|
end
|
data/lib/graphql/function.rb
CHANGED
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module GraphQL
|
3
|
+
module InternalRepresentation
|
4
|
+
class Document
|
5
|
+
# @return [Hash<String, Node>] Operation Nodes of this query
|
6
|
+
attr_reader :operation_definitions
|
7
|
+
|
8
|
+
# @return [Hash<String, Node>] Fragment definition Nodes of this query
|
9
|
+
attr_reader :fragment_definitions
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@operation_definitions = {}
|
13
|
+
@fragment_definitions = {}
|
14
|
+
end
|
15
|
+
|
16
|
+
def [](key)
|
17
|
+
warn "#{self.class}#[] is deprecated; use `operation_definitions[]` instead"
|
18
|
+
operation_definitions[key]
|
19
|
+
end
|
20
|
+
|
21
|
+
def each(&block)
|
22
|
+
warn "#{self.class}#each is deprecated; use `operation_definitions.each` instead"
|
23
|
+
operation_definitions.each(&block)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -18,11 +18,17 @@ module GraphQL
|
|
18
18
|
|
19
19
|
NO_DIRECTIVES = [].freeze
|
20
20
|
|
21
|
-
# @return
|
22
|
-
attr_reader :
|
21
|
+
# @return InternalRepresentation::Document
|
22
|
+
attr_reader :document
|
23
23
|
|
24
24
|
def initialize
|
25
|
-
@
|
25
|
+
@document = InternalRepresentation::Document.new
|
26
|
+
end
|
27
|
+
|
28
|
+
# @return [Hash<String, Node>] Roots of this query
|
29
|
+
def operations
|
30
|
+
warn "#{self.class}#operations is deprecated; use `document.operation_definitions` instead"
|
31
|
+
document.operation_definitions
|
26
32
|
end
|
27
33
|
|
28
34
|
def validate(context)
|
@@ -39,14 +45,13 @@ module GraphQL
|
|
39
45
|
# Array<Scope>
|
40
46
|
scopes_stack = []
|
41
47
|
|
42
|
-
fragment_definitions = {}
|
43
48
|
skip_nodes = Set.new
|
44
49
|
|
45
|
-
visit_op = VisitDefinition.new(context, @
|
50
|
+
visit_op = VisitDefinition.new(context, @document.operation_definitions, nodes_stack, scopes_stack)
|
46
51
|
visitor[Nodes::OperationDefinition].enter << visit_op.method(:enter)
|
47
52
|
visitor[Nodes::OperationDefinition].leave << visit_op.method(:leave)
|
48
53
|
|
49
|
-
visit_frag = VisitDefinition.new(context, fragment_definitions, nodes_stack, scopes_stack)
|
54
|
+
visit_frag = VisitDefinition.new(context, @document.fragment_definitions, nodes_stack, scopes_stack)
|
50
55
|
visitor[Nodes::FragmentDefinition].enter << visit_frag.method(:enter)
|
51
56
|
visitor[Nodes::FragmentDefinition].leave << visit_frag.method(:leave)
|
52
57
|
|
@@ -137,7 +142,7 @@ module GraphQL
|
|
137
142
|
# can be shared between its usages.
|
138
143
|
context.on_dependency_resolve do |defn_ast_node, spread_ast_nodes, frag_ast_node|
|
139
144
|
frag_name = frag_ast_node.name
|
140
|
-
fragment_node = fragment_definitions[frag_name]
|
145
|
+
fragment_node = @document.fragment_definitions[frag_name]
|
141
146
|
|
142
147
|
if fragment_node
|
143
148
|
spread_ast_nodes.each do |spread_ast_node|
|
data/lib/graphql/list_type.rb
CHANGED
@@ -39,25 +39,26 @@ module GraphQL
|
|
39
39
|
def to_s
|
40
40
|
"[#{of_type.to_s}]"
|
41
41
|
end
|
42
|
+
alias_method :inspect, :to_s
|
42
43
|
|
43
44
|
def coerce_result(value, ctx = nil)
|
44
45
|
if ctx.nil?
|
45
46
|
warn_deprecated_coerce("coerce_isolated_result")
|
46
47
|
ctx = GraphQL::Query::NullContext
|
47
48
|
end
|
48
|
-
|
49
|
+
ensure_array(value).map { |item| item.nil? ? nil : of_type.coerce_result(item, ctx) }
|
49
50
|
end
|
50
51
|
|
51
52
|
private
|
52
53
|
|
53
54
|
def coerce_non_null_input(value, ctx)
|
54
|
-
|
55
|
+
ensure_array(value).map { |item| of_type.coerce_input(item, ctx) }
|
55
56
|
end
|
56
57
|
|
57
58
|
def validate_non_null_input(value, ctx)
|
58
59
|
result = GraphQL::Query::InputValidationResult.new
|
59
60
|
|
60
|
-
|
61
|
+
ensure_array(value).each_with_index do |item, index|
|
61
62
|
item_result = of_type.validate_input(item, ctx)
|
62
63
|
if !item_result.valid?
|
63
64
|
result.merge_result!(index, item_result)
|
@@ -66,5 +67,9 @@ module GraphQL
|
|
66
67
|
|
67
68
|
result
|
68
69
|
end
|
70
|
+
|
71
|
+
def ensure_array(value)
|
72
|
+
value.is_a?(Array) ? value : [value]
|
73
|
+
end
|
69
74
|
end
|
70
75
|
end
|
data/lib/graphql/query.rb
CHANGED
@@ -27,7 +27,7 @@ module GraphQL
|
|
27
27
|
end
|
28
28
|
end
|
29
29
|
|
30
|
-
attr_reader :schema, :document, :context, :fragments, :operations, :root_value, :query_string, :warden, :provided_variables
|
30
|
+
attr_reader :schema, :document, :context, :fragments, :operations, :root_value, :query_string, :warden, :provided_variables, :operation_name
|
31
31
|
|
32
32
|
# Prepare query `query_string` on `schema`
|
33
33
|
# @param schema [GraphQL::Schema]
|
@@ -83,13 +83,17 @@ module GraphQL
|
|
83
83
|
@ast_variables = []
|
84
84
|
@mutation = false
|
85
85
|
operation_name_error = nil
|
86
|
+
@operation_name = nil
|
87
|
+
|
86
88
|
if @operations.any?
|
87
|
-
|
88
|
-
if
|
89
|
+
selected_operation = find_operation(@operations, operation_name)
|
90
|
+
if selected_operation.nil?
|
89
91
|
operation_name_error = GraphQL::Query::OperationNameMissingError.new(operation_name)
|
90
92
|
else
|
91
|
-
@
|
92
|
-
@
|
93
|
+
@operation_name = selected_operation.name
|
94
|
+
@ast_variables = selected_operation.variables
|
95
|
+
@mutation = selected_operation.operation_type == "mutation"
|
96
|
+
@selected_operation = selected_operation
|
93
97
|
end
|
94
98
|
end
|
95
99
|
|
@@ -155,7 +159,7 @@ module GraphQL
|
|
155
159
|
end
|
156
160
|
|
157
161
|
def irep_selection
|
158
|
-
@selection ||= internal_representation[selected_operation.name]
|
162
|
+
@selection ||= internal_representation.operation_definitions[selected_operation.name]
|
159
163
|
end
|
160
164
|
|
161
165
|
# Node-level cache for calculating arguments. Used during execution and query analysis.
|
data/lib/graphql/schema.rb
CHANGED
@@ -399,9 +399,10 @@ module GraphQL
|
|
399
399
|
# Create schema from an IDL schema.
|
400
400
|
# @param definition_string [String] A schema definition string
|
401
401
|
# @param default_resolve [<#call(type, field, obj, args, ctx)>] A callable for handling field resolution
|
402
|
+
# @param parser [Object] An object for handling definition string parsing (must respond to `parse`)
|
402
403
|
# @return [GraphQL::Schema] the schema described by `document`
|
403
|
-
def self.from_definition(string, default_resolve: BuildFromDefinition::DefaultResolve)
|
404
|
-
GraphQL::Schema::BuildFromDefinition.from_definition(string, default_resolve: default_resolve)
|
404
|
+
def self.from_definition(string, default_resolve: BuildFromDefinition::DefaultResolve, parser: BuildFromDefinition::DefaultParser)
|
405
|
+
GraphQL::Schema::BuildFromDefinition.from_definition(string, default_resolve: default_resolve, parser: parser)
|
405
406
|
end
|
406
407
|
|
407
408
|
# Error that is raised when [#Schema#from_definition] is passed an invalid schema definition string.
|
@@ -3,12 +3,15 @@ module GraphQL
|
|
3
3
|
class Schema
|
4
4
|
module BuildFromDefinition
|
5
5
|
class << self
|
6
|
-
def from_definition(definition_string, default_resolve:)
|
7
|
-
document =
|
6
|
+
def from_definition(definition_string, default_resolve:, parser: DefaultParser)
|
7
|
+
document = parser.parse(definition_string)
|
8
8
|
Builder.build(document, default_resolve: default_resolve)
|
9
9
|
end
|
10
10
|
end
|
11
11
|
|
12
|
+
# @api private
|
13
|
+
DefaultParser = GraphQL::Language::Parser
|
14
|
+
|
12
15
|
# @api private
|
13
16
|
module DefaultResolve
|
14
17
|
def self.call(type, field, obj, args, ctx)
|
@@ -12,17 +12,19 @@ module GraphQL
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def initialize
|
15
|
+
@node_paths = {}
|
16
|
+
|
15
17
|
# { name => node } pairs for fragments
|
16
18
|
@fragment_definitions = {}
|
17
19
|
|
18
20
|
# This tracks dependencies from fragment to Node where it was used
|
19
|
-
# {
|
21
|
+
# { fragment_definition_node => [dependent_node, dependent_node]}
|
20
22
|
@dependent_definitions = Hash.new { |h, k| h[k] = Set.new }
|
21
23
|
|
22
24
|
# First-level usages of spreads within definitions
|
23
25
|
# (When a key has an empty list as its value,
|
24
26
|
# we can resolve that key's depenedents)
|
25
|
-
# {
|
27
|
+
# { definition_node => [node, node ...] }
|
26
28
|
@immediate_dependencies = Hash.new { |h, k| h[k] = Set.new }
|
27
29
|
end
|
28
30
|
|
@@ -38,8 +40,19 @@ module GraphQL
|
|
38
40
|
# this node is the one who depends on it
|
39
41
|
current_parent = nil
|
40
42
|
|
43
|
+
visitor[GraphQL::Language::Nodes::Document] << ->(node, prev_node) {
|
44
|
+
node.definitions.each do |definition|
|
45
|
+
case definition
|
46
|
+
when GraphQL::Language::Nodes::OperationDefinition
|
47
|
+
when GraphQL::Language::Nodes::FragmentDefinition
|
48
|
+
@fragment_definitions[definition.name] = definition
|
49
|
+
end
|
50
|
+
end
|
51
|
+
}
|
52
|
+
|
41
53
|
visitor[GraphQL::Language::Nodes::OperationDefinition] << ->(node, prev_node) {
|
42
|
-
|
54
|
+
@node_paths[node] = NodeWithPath.new(node, context.path)
|
55
|
+
current_parent = node
|
43
56
|
}
|
44
57
|
|
45
58
|
visitor[GraphQL::Language::Nodes::OperationDefinition].leave << ->(node, prev_node) {
|
@@ -47,7 +60,8 @@ module GraphQL
|
|
47
60
|
}
|
48
61
|
|
49
62
|
visitor[GraphQL::Language::Nodes::FragmentDefinition] << ->(node, prev_node) {
|
50
|
-
|
63
|
+
@node_paths[node] = NodeWithPath.new(node, context.path)
|
64
|
+
current_parent = node
|
51
65
|
}
|
52
66
|
|
53
67
|
visitor[GraphQL::Language::Nodes::FragmentDefinition].leave << ->(node, prev_node) {
|
@@ -55,9 +69,11 @@ module GraphQL
|
|
55
69
|
}
|
56
70
|
|
57
71
|
visitor[GraphQL::Language::Nodes::FragmentSpread] << ->(node, prev_node) {
|
72
|
+
@node_paths[node] = NodeWithPath.new(node, context.path)
|
73
|
+
|
58
74
|
# Track both sides of the dependency
|
59
|
-
@dependent_definitions[node.name] << current_parent
|
60
|
-
@immediate_dependencies[current_parent
|
75
|
+
@dependent_definitions[@fragment_definitions[node.name]] << current_parent
|
76
|
+
@immediate_dependencies[current_parent] << node
|
61
77
|
}
|
62
78
|
end
|
63
79
|
|
@@ -113,40 +129,39 @@ module GraphQL
|
|
113
129
|
# determine them at the end. This way, we can treat fragments with the
|
114
130
|
# same name as if they were the same name. If _any_ of the fragments
|
115
131
|
# with that name has a dependency, we record it.
|
116
|
-
|
132
|
+
independent_fragment_nodes = @fragment_definitions.values - @immediate_dependencies.keys
|
117
133
|
|
118
|
-
while
|
134
|
+
while fragment_node = independent_fragment_nodes.pop
|
119
135
|
loops += 1
|
120
136
|
if loops > max_loops
|
121
137
|
raise("Resolution loops exceeded the number of definitions; infinite loop detected.")
|
122
138
|
end
|
123
|
-
fragment_node = @fragment_definitions[fragment_name]
|
124
139
|
# Since it's independent, let's remove it from here.
|
125
140
|
# That way, we can use the remainder to identify cycles
|
126
|
-
@immediate_dependencies.delete(
|
127
|
-
fragment_usages = @dependent_definitions[
|
141
|
+
@immediate_dependencies.delete(fragment_node)
|
142
|
+
fragment_usages = @dependent_definitions[fragment_node]
|
128
143
|
if fragment_usages.none?
|
129
144
|
# If we didn't record any usages during the visit,
|
130
145
|
# then this fragment is unused.
|
131
|
-
dependency_map.unused_dependencies << fragment_node
|
146
|
+
dependency_map.unused_dependencies << @node_paths[fragment_node]
|
132
147
|
else
|
133
148
|
fragment_usages.each do |definition_node|
|
134
149
|
# Register the dependency AND second-order dependencies
|
135
150
|
dependency_map[definition_node] << fragment_node
|
136
151
|
dependency_map[definition_node].concat(dependency_map[fragment_node])
|
137
152
|
# Since we've regestered it, remove it from our to-do list
|
138
|
-
deps = @immediate_dependencies[definition_node
|
153
|
+
deps = @immediate_dependencies[definition_node]
|
139
154
|
# Can't find a way to _just_ delete from `deps` and return the deleted entries
|
140
|
-
removed, remaining = deps.partition { |spread| spread.name ==
|
141
|
-
@immediate_dependencies[definition_node
|
155
|
+
removed, remaining = deps.partition { |spread| spread.name == fragment_node.name }
|
156
|
+
@immediate_dependencies[definition_node] = remaining
|
142
157
|
if block_given?
|
143
158
|
yield(definition_node, removed, fragment_node)
|
144
159
|
end
|
145
|
-
if remaining.none? && definition_node.
|
160
|
+
if remaining.none? && definition_node.is_a?(GraphQL::Language::Nodes::FragmentDefinition)
|
146
161
|
# If all of this definition's dependencies have
|
147
162
|
# been resolved, we can now resolve its
|
148
163
|
# own dependents.
|
149
|
-
|
164
|
+
independent_fragment_nodes << definition_node
|
150
165
|
end
|
151
166
|
end
|
152
167
|
end
|
@@ -155,21 +170,20 @@ module GraphQL
|
|
155
170
|
# If any dependencies were _unmet_
|
156
171
|
# (eg, spreads with no corresponding definition)
|
157
172
|
# then they're still in there
|
158
|
-
@immediate_dependencies.each do |
|
173
|
+
@immediate_dependencies.each do |defn_node, deps|
|
159
174
|
deps.each do |spread|
|
160
175
|
if @fragment_definitions[spread.name].nil?
|
161
|
-
defn_node
|
162
|
-
dependency_map.unmet_dependencies[defn_node] << spread
|
176
|
+
dependency_map.unmet_dependencies[@node_paths[defn_node]] << @node_paths[spread]
|
163
177
|
deps.delete(spread)
|
164
178
|
end
|
165
179
|
end
|
166
180
|
if deps.none?
|
167
|
-
@immediate_dependencies.delete(
|
181
|
+
@immediate_dependencies.delete(defn_node)
|
168
182
|
end
|
169
183
|
end
|
170
184
|
|
171
185
|
# Anything left in @immediate_dependencies is cyclical
|
172
|
-
cyclical_nodes = @immediate_dependencies.keys.map { |n| @
|
186
|
+
cyclical_nodes = @immediate_dependencies.keys.map { |n| @node_paths[n] }
|
173
187
|
# @immediate_dependencies also includes operation names, but we don't care about
|
174
188
|
# those. They became nil when we looked them up on `@fragment_definitions`, so remove them.
|
175
189
|
cyclical_nodes.compact!
|
@@ -33,8 +33,8 @@ module GraphQL
|
|
33
33
|
|
34
34
|
context.visitor.visit
|
35
35
|
# Post-validation: allow validators to register handlers on rewritten query nodes
|
36
|
-
rewrite_result = rewrite.
|
37
|
-
GraphQL::InternalRepresentation::Visit.visit_each_node(rewrite_result, context.each_irep_node_handlers)
|
36
|
+
rewrite_result = rewrite.document
|
37
|
+
GraphQL::InternalRepresentation::Visit.visit_each_node(rewrite_result.operation_definitions, context.each_irep_node_handlers)
|
38
38
|
|
39
39
|
{
|
40
40
|
errors: context.errors,
|
data/lib/graphql/version.rb
CHANGED
@@ -30,6 +30,15 @@ RUBY
|
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
33
|
+
test "it generates classifed file" do
|
34
|
+
run_generator(["page"])
|
35
|
+
assert_file "app/graphql/types/page_type.rb", <<-RUBY
|
36
|
+
Types::PageType = GraphQL::ObjectType.define do
|
37
|
+
name "Page"
|
38
|
+
end
|
39
|
+
RUBY
|
40
|
+
end
|
41
|
+
|
33
42
|
test "it makes Relay nodes" do
|
34
43
|
run_generator(["Page", "--node"])
|
35
44
|
assert_file "app/graphql/types/page_type.rb", <<-RUBY
|
@@ -104,7 +104,7 @@ describe GraphQL::InternalRepresentation::Rewrite do
|
|
104
104
|
}
|
105
105
|
|
106
106
|
it "groups selections by object types which they apply to" do
|
107
|
-
doc = rewrite_result["getPlant"]
|
107
|
+
doc = rewrite_result.operation_definitions["getPlant"]
|
108
108
|
assert_equal nil, doc.definition
|
109
109
|
|
110
110
|
plant_scoped_selection = doc.scoped_children[schema.types["Query"]]["plant"]
|
@@ -126,7 +126,7 @@ describe GraphQL::InternalRepresentation::Rewrite do
|
|
126
126
|
end
|
127
127
|
|
128
128
|
it "tracks parent nodes" do
|
129
|
-
doc = rewrite_result["getPlant"]
|
129
|
+
doc = rewrite_result.operation_definitions["getPlant"]
|
130
130
|
assert_equal nil, doc.parent
|
131
131
|
|
132
132
|
plant_selection = doc.typed_children[schema.types["Query"]]["plant"]
|
@@ -171,7 +171,7 @@ describe GraphQL::InternalRepresentation::Rewrite do
|
|
171
171
|
}
|
172
172
|
|
173
173
|
it "applies directives from all contexts" do
|
174
|
-
doc = rewrite_result["getPlant"]
|
174
|
+
doc = rewrite_result.operation_definitions["getPlant"]
|
175
175
|
plant_selection = doc.typed_children[schema.types["Query"]]["plant"]
|
176
176
|
leaf_type_selection = plant_selection.typed_children[schema.types["Nut"]]["leafType"]
|
177
177
|
# Only unskipped occurrences in the AST
|
@@ -208,7 +208,7 @@ describe GraphQL::InternalRepresentation::Rewrite do
|
|
208
208
|
}
|
209
209
|
|
210
210
|
it "applies spreads to their parents only" do
|
211
|
-
doc = rewrite_result[nil]
|
211
|
+
doc = rewrite_result.operation_definitions[nil]
|
212
212
|
plant_selection = doc.typed_children[schema.types["Query"]]["plant"]
|
213
213
|
nut_habitat_selections = plant_selection.typed_children[schema.types["Nut"]]["habitats"].typed_children[schema.types["Habitat"]]
|
214
214
|
assert_equal ["averageWeight", "residentName", "seasons"], nut_habitat_selections.keys.sort
|
@@ -286,4 +286,42 @@ describe GraphQL::InternalRepresentation::Rewrite do
|
|
286
286
|
end
|
287
287
|
end
|
288
288
|
end
|
289
|
+
|
290
|
+
describe "fragment definition without query" do
|
291
|
+
let(:query_string) {
|
292
|
+
<<-GRAPHQL
|
293
|
+
fragment NutFields on Nut {
|
294
|
+
leafType
|
295
|
+
... TreeFields
|
296
|
+
}
|
297
|
+
|
298
|
+
fragment TreeFields on Tree {
|
299
|
+
habitats {
|
300
|
+
... HabitatFields
|
301
|
+
}
|
302
|
+
}
|
303
|
+
|
304
|
+
fragment HabitatFields on Habitat {
|
305
|
+
seasons
|
306
|
+
}
|
307
|
+
GRAPHQL
|
308
|
+
}
|
309
|
+
|
310
|
+
let(:validator) {
|
311
|
+
rules = GraphQL::StaticValidation::ALL_RULES - [
|
312
|
+
GraphQL::StaticValidation::FragmentsAreUsed
|
313
|
+
]
|
314
|
+
GraphQL::StaticValidation::Validator.new(schema: schema, rules: rules)
|
315
|
+
}
|
316
|
+
|
317
|
+
it "tracks fragment definitions without operation" do
|
318
|
+
doc = rewrite_result.fragment_definitions["NutFields"]
|
319
|
+
|
320
|
+
assert doc.typed_children[schema.types["Nut"]]["leafType"]
|
321
|
+
|
322
|
+
assert nut_selections = doc.typed_children[schema.types["Nut"]]
|
323
|
+
habitats_selections = nut_selections["habitats"].typed_children[schema.types["Habitat"]]
|
324
|
+
assert_equal ["seasons"], habitats_selections.keys
|
325
|
+
end
|
326
|
+
end
|
289
327
|
end
|
@@ -8,6 +8,10 @@ describe GraphQL::ListType do
|
|
8
8
|
assert_equal([1.0, 2.0, 3.0].inspect, float_list.coerce_isolated_input([1, 2, 3]).inspect)
|
9
9
|
end
|
10
10
|
|
11
|
+
it "converts items that are not lists into lists" do
|
12
|
+
assert_equal([1.0].inspect, float_list.coerce_isolated_input(1.0).inspect)
|
13
|
+
end
|
14
|
+
|
11
15
|
describe "validate_input with bad input" do
|
12
16
|
let(:bad_num) { "bad_num" }
|
13
17
|
let(:result) { float_list.validate_isolated_input([bad_num, 2.0, 3.0]) }
|
@@ -30,4 +34,20 @@ describe GraphQL::ListType do
|
|
30
34
|
assert_equal(actual, expected)
|
31
35
|
end
|
32
36
|
end
|
37
|
+
|
38
|
+
describe "list of input objects" do
|
39
|
+
let(:input_object) do
|
40
|
+
GraphQL::InputObjectType.define do
|
41
|
+
name "SomeInputObjectType"
|
42
|
+
argument :float, !types.Float
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
let(:input_object_list) { input_object.to_list_type }
|
47
|
+
|
48
|
+
it "converts hashes into lists of hashes" do
|
49
|
+
hash = { 'float' => 1.0 }
|
50
|
+
assert_equal([hash].inspect, input_object_list.coerce_isolated_input(hash).map(&:to_h).inspect)
|
51
|
+
end
|
52
|
+
end
|
33
53
|
end
|
@@ -58,6 +58,27 @@ describe GraphQL::Query::Executor do
|
|
58
58
|
end
|
59
59
|
end
|
60
60
|
|
61
|
+
describe "operation and fragment defintions of the same name" do
|
62
|
+
let(:query_string) { %|
|
63
|
+
query Cheese { cheese(id: 1) { ...Cheese } }
|
64
|
+
query MoreCheese { cheese(id: 2) { ...Cheese } }
|
65
|
+
fragment Cheese on Cheese { flavor }
|
66
|
+
|}
|
67
|
+
|
68
|
+
let(:operation_name) { "Cheese" }
|
69
|
+
|
70
|
+
it "runs the named operation" do
|
71
|
+
expected = {
|
72
|
+
"data" => {
|
73
|
+
"cheese" => {
|
74
|
+
"flavor" => "Brie"
|
75
|
+
}
|
76
|
+
}
|
77
|
+
}
|
78
|
+
assert_equal(expected, result)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
61
82
|
|
62
83
|
describe "execution order" do
|
63
84
|
let(:query_string) {%|
|
data/spec/graphql/query_spec.rb
CHANGED
@@ -57,6 +57,32 @@ describe GraphQL::Query do
|
|
57
57
|
end
|
58
58
|
end
|
59
59
|
|
60
|
+
describe "operation_name" do
|
61
|
+
describe "when provided" do
|
62
|
+
let(:query_string) { <<-GRAPHQL
|
63
|
+
query q1 { cheese(id: 1) { flavor } }
|
64
|
+
query q2 { cheese(id: 2) { flavor } }
|
65
|
+
GRAPHQL
|
66
|
+
}
|
67
|
+
let(:operation_name) { "q2" }
|
68
|
+
|
69
|
+
it "returns the provided name" do
|
70
|
+
assert_equal "q2", query.operation_name
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
describe "when inferred" do
|
75
|
+
let(:query_string) { <<-GRAPHQL
|
76
|
+
query q3 { cheese(id: 3) { flavor } }
|
77
|
+
GRAPHQL
|
78
|
+
}
|
79
|
+
|
80
|
+
it "returns the inferred name" do
|
81
|
+
assert_equal "q3", query.operation_name
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
60
86
|
describe "when passed a document instance" do
|
61
87
|
let(:query) { GraphQL::Query.new(
|
62
88
|
schema,
|
@@ -529,4 +555,14 @@ describe GraphQL::Query do
|
|
529
555
|
assert_equal([nil], expected_args.first['id'])
|
530
556
|
end
|
531
557
|
end
|
558
|
+
|
559
|
+
describe '#internal_representation' do
|
560
|
+
it "includes all definition roots" do
|
561
|
+
assert_kind_of GraphQL::InternalRepresentation::Node, query.internal_representation.operation_definitions["getFlavor"]
|
562
|
+
assert_kind_of GraphQL::InternalRepresentation::Node, query.internal_representation.fragment_definitions["cheeseFields"]
|
563
|
+
assert_kind_of GraphQL::InternalRepresentation::Node, query.internal_representation.fragment_definitions["edibleFields"]
|
564
|
+
assert_kind_of GraphQL::InternalRepresentation::Node, query.internal_representation.fragment_definitions["milkFields"]
|
565
|
+
assert_kind_of GraphQL::InternalRepresentation::Node, query.internal_representation.fragment_definitions["dairyFields"]
|
566
|
+
end
|
567
|
+
end
|
532
568
|
end
|
@@ -813,5 +813,21 @@ SCHEMA
|
|
813
813
|
assert_equal(result.to_json, '{"data":{"allTodos":[{"text":"Pay the bills.","from_context":null},{"text":"Buy Milk","from_context":"bar"}]}}')
|
814
814
|
end
|
815
815
|
end
|
816
|
+
|
817
|
+
describe "custom parser behavior" do
|
818
|
+
module BadParser
|
819
|
+
ParseError = Class.new(StandardError)
|
820
|
+
|
821
|
+
def self.parse(string)
|
822
|
+
raise ParseError
|
823
|
+
end
|
824
|
+
end
|
825
|
+
|
826
|
+
it 'accepts a parser callable' do
|
827
|
+
assert_raises(BadParser::ParseError) do
|
828
|
+
GraphQL::Schema.from_definition(schema_defn, parser: BadParser)
|
829
|
+
end
|
830
|
+
end
|
831
|
+
end
|
816
832
|
end
|
817
833
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: graphql
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.5.
|
4
|
+
version: 1.5.11
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Robert Mosolgo
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-05-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: benchmark-ips
|
@@ -362,6 +362,7 @@ files:
|
|
362
362
|
- lib/graphql/int_type.rb
|
363
363
|
- lib/graphql/interface_type.rb
|
364
364
|
- lib/graphql/internal_representation.rb
|
365
|
+
- lib/graphql/internal_representation/document.rb
|
365
366
|
- lib/graphql/internal_representation/node.rb
|
366
367
|
- lib/graphql/internal_representation/print.rb
|
367
368
|
- lib/graphql/internal_representation/rewrite.rb
|