graphql 1.5.10 → 1.5.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|