graphql 1.10.0.pre1 → 1.10.0.pre2
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/core.rb +1 -0
- data/lib/generators/graphql/install_generator.rb +1 -0
- data/lib/generators/graphql/mutation_generator.rb +1 -1
- data/lib/generators/graphql/templates/base_field.erb +0 -4
- data/lib/generators/graphql/templates/base_mutation.erb +8 -0
- data/lib/generators/graphql/templates/graphql_controller.erb +5 -0
- data/lib/generators/graphql/templates/mutation.erb +1 -1
- data/lib/generators/graphql/templates/schema.erb +1 -1
- data/lib/graphql.rb +4 -1
- data/lib/graphql/analysis/ast.rb +14 -13
- data/lib/graphql/analysis/ast/analyzer.rb +23 -4
- data/lib/graphql/analysis/ast/field_usage.rb +1 -1
- data/lib/graphql/analysis/ast/max_query_complexity.rb +3 -3
- data/lib/graphql/analysis/ast/max_query_depth.rb +7 -3
- data/lib/graphql/analysis/ast/query_complexity.rb +2 -2
- data/lib/graphql/analysis/ast/visitor.rb +3 -3
- data/lib/graphql/base_type.rb +1 -1
- data/lib/graphql/directive.rb +0 -1
- data/lib/graphql/directive/deprecated_directive.rb +1 -12
- data/lib/graphql/execution/errors.rb +4 -8
- data/lib/graphql/execution/interpreter.rb +5 -11
- data/lib/graphql/execution/interpreter/runtime.rb +56 -48
- data/lib/graphql/execution/lazy/lazy_method_map.rb +4 -0
- data/lib/graphql/execution/lookahead.rb +5 -5
- data/lib/graphql/execution/multiplex.rb +10 -0
- data/lib/graphql/function.rb +1 -1
- data/lib/graphql/input_object_type.rb +3 -2
- data/lib/graphql/interface_type.rb +1 -1
- data/lib/graphql/introspection/base_object.rb +2 -5
- data/lib/graphql/introspection/directive_type.rb +1 -1
- data/lib/graphql/introspection/entry_points.rb +6 -6
- data/lib/graphql/introspection/schema_type.rb +1 -6
- data/lib/graphql/introspection/type_type.rb +5 -5
- data/lib/graphql/language.rb +1 -1
- data/lib/graphql/language/block_string.rb +2 -2
- data/lib/graphql/language/definition_slice.rb +21 -10
- data/lib/graphql/language/document_from_schema_definition.rb +42 -42
- data/lib/graphql/language/lexer.rb +49 -48
- data/lib/graphql/language/lexer.rl +49 -48
- data/lib/graphql/language/nodes.rb +11 -8
- data/lib/graphql/language/parser.rb +4 -1
- data/lib/graphql/language/parser.y +4 -1
- data/lib/graphql/language/token.rb +1 -1
- data/lib/graphql/pagination/array_connection.rb +0 -1
- data/lib/graphql/pagination/connection.rb +31 -10
- data/lib/graphql/pagination/connections.rb +7 -2
- data/lib/graphql/pagination/relation_connection.rb +1 -7
- data/lib/graphql/query.rb +9 -4
- data/lib/graphql/query/arguments.rb +8 -1
- data/lib/graphql/query/literal_input.rb +2 -1
- data/lib/graphql/query/variables.rb +5 -1
- data/lib/graphql/relay/base_connection.rb +3 -3
- data/lib/graphql/relay/relation_connection.rb +9 -5
- data/lib/graphql/schema.rb +699 -153
- data/lib/graphql/schema/argument.rb +20 -4
- data/lib/graphql/schema/build_from_definition.rb +64 -31
- data/lib/graphql/schema/built_in_types.rb +5 -5
- data/lib/graphql/schema/directive.rb +16 -1
- data/lib/graphql/schema/directive/deprecated.rb +18 -0
- data/lib/graphql/schema/directive/feature.rb +1 -1
- data/lib/graphql/schema/enum.rb +39 -3
- data/lib/graphql/schema/field.rb +39 -9
- data/lib/graphql/schema/field/connection_extension.rb +4 -4
- data/lib/graphql/schema/find_inherited_value.rb +13 -0
- data/lib/graphql/schema/finder.rb +13 -11
- data/lib/graphql/schema/input_object.rb +109 -1
- data/lib/graphql/schema/interface.rb +8 -7
- data/lib/graphql/schema/introspection_system.rb +104 -36
- data/lib/graphql/schema/late_bound_type.rb +1 -0
- data/lib/graphql/schema/list.rb +26 -0
- data/lib/graphql/schema/loader.rb +10 -4
- data/lib/graphql/schema/member.rb +3 -0
- data/lib/graphql/schema/member/base_dsl_methods.rb +23 -13
- data/lib/graphql/schema/member/build_type.rb +1 -1
- data/lib/graphql/schema/member/has_arguments.rb +2 -2
- data/lib/graphql/schema/member/has_fields.rb +15 -6
- data/lib/graphql/schema/member/instrumentation.rb +6 -1
- data/lib/graphql/schema/member/type_system_helpers.rb +1 -1
- data/lib/graphql/schema/member/validates_input.rb +33 -0
- data/lib/graphql/schema/non_null.rb +25 -0
- data/lib/graphql/schema/object.rb +14 -1
- data/lib/graphql/schema/printer.rb +4 -3
- data/lib/graphql/schema/relay_classic_mutation.rb +5 -1
- data/lib/graphql/schema/resolver.rb +20 -2
- data/lib/graphql/schema/scalar.rb +18 -3
- data/lib/graphql/schema/subscription.rb +1 -1
- data/lib/graphql/schema/timeout_middleware.rb +3 -2
- data/lib/graphql/schema/traversal.rb +1 -1
- data/lib/graphql/schema/type_expression.rb +22 -24
- data/lib/graphql/schema/validation.rb +17 -1
- data/lib/graphql/schema/warden.rb +46 -17
- data/lib/graphql/schema/wrapper.rb +1 -1
- data/lib/graphql/static_validation/base_visitor.rb +10 -6
- data/lib/graphql/static_validation/definition_dependencies.rb +21 -12
- data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +4 -4
- data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +3 -3
- data/lib/graphql/static_validation/type_stack.rb +2 -2
- data/lib/graphql/static_validation/validator.rb +1 -1
- data/lib/graphql/subscriptions.rb +38 -13
- data/lib/graphql/subscriptions/event.rb +24 -7
- data/lib/graphql/subscriptions/instrumentation.rb +1 -1
- data/lib/graphql/subscriptions/subscription_root.rb +0 -1
- data/lib/graphql/tracing/active_support_notifications_tracing.rb +10 -10
- data/lib/graphql/tracing/platform_tracing.rb +1 -2
- data/lib/graphql/tracing/skylight_tracing.rb +1 -0
- data/lib/graphql/unresolved_type_error.rb +2 -2
- data/lib/graphql/upgrader/member.rb +1 -1
- data/lib/graphql/version.rb +1 -1
- metadata +5 -2
@@ -3,11 +3,9 @@ module GraphQL
|
|
3
3
|
class Schema
|
4
4
|
class Scalar < GraphQL::Schema::Member
|
5
5
|
extend GraphQL::Schema::Member::AcceptsDefinition
|
6
|
+
extend GraphQL::Schema::Member::ValidatesInput
|
6
7
|
|
7
8
|
class << self
|
8
|
-
extend Forwardable
|
9
|
-
def_delegators :graphql_definition, :coerce_isolated_input, :coerce_isolated_result
|
10
|
-
|
11
9
|
def coerce_input(val, ctx)
|
12
10
|
val
|
13
11
|
end
|
@@ -38,6 +36,23 @@ module GraphQL
|
|
38
36
|
end
|
39
37
|
@default_scalar
|
40
38
|
end
|
39
|
+
|
40
|
+
def default_scalar?
|
41
|
+
@default_scalar ||= false
|
42
|
+
end
|
43
|
+
|
44
|
+
def validate_non_null_input(value, ctx)
|
45
|
+
result = Query::InputValidationResult.new
|
46
|
+
if coerce_input(value, ctx).nil?
|
47
|
+
str_value = if value == Float::INFINITY
|
48
|
+
""
|
49
|
+
else
|
50
|
+
" #{GraphQL::Language.serialize(value)}"
|
51
|
+
end
|
52
|
+
result.add_problem("Could not coerce value#{str_value} to #{graphql_name}")
|
53
|
+
end
|
54
|
+
result
|
55
|
+
end
|
41
56
|
end
|
42
57
|
end
|
43
58
|
end
|
@@ -29,7 +29,7 @@ module GraphQL
|
|
29
29
|
# propagate null.
|
30
30
|
null false
|
31
31
|
|
32
|
-
def initialize(object:, context:)
|
32
|
+
def initialize(object:, context:, field:)
|
33
33
|
super
|
34
34
|
# Figure out whether this is an update or an initial subscription
|
35
35
|
@mode = context.query.subscription_update? ? :update : :subscribe
|
@@ -35,9 +35,10 @@ module GraphQL
|
|
35
35
|
|
36
36
|
def call(parent_type, parent_object, field_definition, field_args, query_context)
|
37
37
|
ns = query_context.namespace(self.class)
|
38
|
-
|
38
|
+
now = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
39
|
+
timeout_at = ns[:timeout_at] ||= now + @max_seconds
|
39
40
|
|
40
|
-
if timeout_at <
|
41
|
+
if timeout_at < now
|
41
42
|
on_timeout(parent_type, parent_object, field_definition, field_args, query_context)
|
42
43
|
else
|
43
44
|
yield
|
@@ -113,7 +113,7 @@ Some late-bound types couldn't be resolved:
|
|
113
113
|
# Find the starting points, then visit them
|
114
114
|
visit_roots = [member.query, member.mutation, member.subscription]
|
115
115
|
if @introspection
|
116
|
-
introspection_types = schema.introspection_system.
|
116
|
+
introspection_types = schema.introspection_system.types.values
|
117
117
|
visit_roots.concat(introspection_types)
|
118
118
|
if member.query
|
119
119
|
member.introspection_system.entry_points.each do |introspection_field|
|
@@ -5,38 +5,36 @@ module GraphQL
|
|
5
5
|
module TypeExpression
|
6
6
|
# Fetch a type from a type map by its AST specification.
|
7
7
|
# Return `nil` if not found.
|
8
|
-
# @param
|
8
|
+
# @param type_owner [#get_type] A thing for looking up types by name
|
9
9
|
# @param ast_node [GraphQL::Language::Nodes::AbstractNode]
|
10
|
-
# @return [GraphQL::
|
11
|
-
def self.build_type(
|
12
|
-
|
13
|
-
|
14
|
-
|
10
|
+
# @return [Class, GraphQL::Schema::NonNull, GraphQL::Schema:List]
|
11
|
+
def self.build_type(type_owner, ast_node)
|
12
|
+
case ast_node
|
13
|
+
when GraphQL::Language::Nodes::TypeName
|
14
|
+
type_owner.get_type(ast_node.name)
|
15
|
+
when GraphQL::Language::Nodes::NonNullType
|
16
|
+
ast_inner_type = ast_node.of_type
|
17
|
+
inner_type = build_type(type_owner, ast_inner_type)
|
18
|
+
wrap_type(inner_type, :to_non_null_type)
|
19
|
+
when GraphQL::Language::Nodes::ListType
|
20
|
+
ast_inner_type = ast_node.of_type
|
21
|
+
inner_type = build_type(type_owner, ast_inner_type)
|
22
|
+
wrap_type(inner_type, :to_list_type)
|
23
|
+
else
|
24
|
+
raise "Invariant: unexpected type from ast: #{ast_node.inspect}"
|
25
|
+
end
|
15
26
|
end
|
16
27
|
|
17
28
|
class << self
|
18
29
|
private
|
19
30
|
|
20
|
-
def
|
21
|
-
|
22
|
-
when GraphQL::Language::Nodes::TypeName
|
23
|
-
types.fetch(ast_node.name, nil)
|
24
|
-
when GraphQL::Language::Nodes::NonNullType
|
25
|
-
ast_inner_type = ast_node.of_type
|
26
|
-
inner_type = build_type(types, ast_inner_type)
|
27
|
-
wrap_type(inner_type, GraphQL::Schema::NonNull)
|
28
|
-
when GraphQL::Language::Nodes::ListType
|
29
|
-
ast_inner_type = ast_node.of_type
|
30
|
-
inner_type = build_type(types, ast_inner_type)
|
31
|
-
wrap_type(inner_type, GraphQL::Schema::List)
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
def wrap_type(of_type, wrapper)
|
36
|
-
if of_type.nil?
|
31
|
+
def wrap_type(type, wrapper_method)
|
32
|
+
if type.nil?
|
37
33
|
nil
|
34
|
+
elsif wrapper_method == :to_list_type || wrapper_method == :to_non_null_type
|
35
|
+
type.public_send(wrapper_method)
|
38
36
|
else
|
39
|
-
wrapper.
|
37
|
+
raise ArgumentError, "Unexpected wrapper method: #{wrapper_method.inspect}"
|
40
38
|
end
|
41
39
|
end
|
42
40
|
end
|
@@ -77,6 +77,18 @@ module GraphQL
|
|
77
77
|
}
|
78
78
|
end
|
79
79
|
|
80
|
+
def self.count_at_least(item_name, minimum_count, get_items_proc)
|
81
|
+
->(type) {
|
82
|
+
items = get_items_proc.call(type)
|
83
|
+
|
84
|
+
if items.size < minimum_count
|
85
|
+
"#{type.name} must define at least #{minimum_count} #{item_name}. #{items.size} defined."
|
86
|
+
else
|
87
|
+
nil
|
88
|
+
end
|
89
|
+
}
|
90
|
+
end
|
91
|
+
|
80
92
|
def self.assert_named_items_are_valid(item_name, get_items_proc)
|
81
93
|
->(type) {
|
82
94
|
items = get_items_proc.call(type)
|
@@ -92,7 +104,9 @@ module GraphQL
|
|
92
104
|
}
|
93
105
|
end
|
94
106
|
|
107
|
+
HAS_AT_LEAST_ONE_FIELD = Rules.count_at_least("field", 1, ->(type) { type.all_fields })
|
95
108
|
FIELDS_ARE_VALID = Rules.assert_named_items_are_valid("field", ->(type) { type.all_fields })
|
109
|
+
HAS_AT_LEAST_ONE_ARGUMENT = Rules.count_at_least("argument", 1, ->(type) { type.arguments })
|
96
110
|
|
97
111
|
HAS_ONE_OR_MORE_POSSIBLE_TYPES = ->(type) {
|
98
112
|
type.possible_types.length >= 1 ? nil : "must have at least one possible type"
|
@@ -140,7 +154,7 @@ module GraphQL
|
|
140
154
|
}
|
141
155
|
|
142
156
|
SCHEMA_CAN_FETCH_IDS = ->(schema) {
|
143
|
-
has_node_field = schema.query && schema.query.
|
157
|
+
has_node_field = schema.query && schema.query.fields.each_value.any?(&:relay_node_field)
|
144
158
|
if has_node_field && schema.object_from_id_proc.nil?
|
145
159
|
"schema contains `node(id:...)` field, so you must define a `object_from_id -> (id, ctx) { ... }` function"
|
146
160
|
else
|
@@ -260,11 +274,13 @@ module GraphQL
|
|
260
274
|
Rules::DESCRIPTION_IS_STRING_OR_NIL,
|
261
275
|
],
|
262
276
|
GraphQL::ObjectType => [
|
277
|
+
Rules::HAS_AT_LEAST_ONE_FIELD,
|
263
278
|
Rules.assert_property_list_of(:interfaces, GraphQL::InterfaceType),
|
264
279
|
Rules::FIELDS_ARE_VALID,
|
265
280
|
Rules::INTERFACES_ARE_IMPLEMENTED,
|
266
281
|
],
|
267
282
|
GraphQL::InputObjectType => [
|
283
|
+
Rules::HAS_AT_LEAST_ONE_ARGUMENT,
|
268
284
|
Rules::ARGUMENTS_ARE_STRING_TO_ARGUMENT,
|
269
285
|
Rules::ARGUMENTS_ARE_VALID,
|
270
286
|
],
|
@@ -39,19 +39,31 @@ module GraphQL
|
|
39
39
|
# @param schema [GraphQL::Schema]
|
40
40
|
# @param deep_check [Boolean]
|
41
41
|
def initialize(filter, context:, schema:)
|
42
|
-
@schema = schema
|
42
|
+
@schema = schema.interpreter? ? schema : schema.graphql_definition
|
43
|
+
# Cache these to avoid repeated hits to the inheritance chain when one isn't present
|
44
|
+
@query = @schema.query
|
45
|
+
@mutation = @schema.mutation
|
46
|
+
@subscription = @schema.subscription
|
43
47
|
@visibility_cache = read_through { |m| filter.call(m, context) }
|
44
48
|
end
|
45
49
|
|
46
50
|
# @return [Array<GraphQL::BaseType>] Visible types in the schema
|
47
51
|
def types
|
48
|
-
@types ||=
|
52
|
+
@types ||= begin
|
53
|
+
vis_types = {}
|
54
|
+
@schema.types.each do |n, t|
|
55
|
+
if visible_type?(t)
|
56
|
+
vis_types[n] = t
|
57
|
+
end
|
58
|
+
end
|
59
|
+
vis_types
|
60
|
+
end
|
49
61
|
end
|
50
62
|
|
51
63
|
# @return [GraphQL::BaseType, nil] The type named `type_name`, if it exists (else `nil`)
|
52
64
|
def get_type(type_name)
|
53
65
|
@visible_types ||= read_through do |name|
|
54
|
-
type_defn = @schema.
|
66
|
+
type_defn = @schema.get_type(name)
|
55
67
|
if type_defn && visible_type?(type_defn)
|
56
68
|
type_defn
|
57
69
|
else
|
@@ -81,7 +93,15 @@ module GraphQL
|
|
81
93
|
|
82
94
|
# @return [Array<GraphQL::BaseType>] The types which may be member of `type_defn`
|
83
95
|
def possible_types(type_defn)
|
84
|
-
@visible_possible_types ||=
|
96
|
+
@visible_possible_types ||= if @schema.is_a?(Class)
|
97
|
+
all_possible_types = @schema.possible_types
|
98
|
+
read_through { |type_defn|
|
99
|
+
pt = all_possible_types[type_defn.graphql_name] || []
|
100
|
+
pt.select { |t| visible_type?(t) }
|
101
|
+
}
|
102
|
+
else
|
103
|
+
read_through { |type_defn| @schema.possible_types(type_defn).select { |t| visible_type?(t) } }
|
104
|
+
end
|
85
105
|
@visible_possible_types[type_defn]
|
86
106
|
end
|
87
107
|
|
@@ -136,28 +156,37 @@ module GraphQL
|
|
136
156
|
end
|
137
157
|
|
138
158
|
def visible_type?(type_defn)
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
159
|
+
@type_visibility ||= read_through do |type_defn|
|
160
|
+
next false unless visible?(type_defn)
|
161
|
+
next true if root_type?(type_defn) || type_defn.introspection?
|
162
|
+
|
163
|
+
if type_defn.kind.union?
|
164
|
+
visible_possible_types?(type_defn) && (referenced?(type_defn) || orphan_type?(type_defn))
|
165
|
+
elsif type_defn.kind.interface?
|
166
|
+
visible_possible_types?(type_defn)
|
167
|
+
else
|
168
|
+
referenced?(type_defn) || visible_abstract_type?(type_defn)
|
169
|
+
end
|
149
170
|
end
|
171
|
+
|
172
|
+
@type_visibility[type_defn]
|
150
173
|
end
|
151
174
|
|
152
175
|
def root_type?(type_defn)
|
153
|
-
@
|
176
|
+
@query == type_defn ||
|
177
|
+
@mutation == type_defn ||
|
178
|
+
@subscription == type_defn
|
154
179
|
end
|
155
180
|
|
156
181
|
def referenced?(type_defn)
|
157
|
-
|
182
|
+
@references_to ||= @schema.references_to
|
183
|
+
graphql_name = type_defn.unwrap.graphql_name
|
184
|
+
members = @references_to[graphql_name] || NO_REFERENCES
|
158
185
|
members.any? { |m| visible?(m) }
|
159
186
|
end
|
160
187
|
|
188
|
+
NO_REFERENCES = [].freeze
|
189
|
+
|
161
190
|
def orphan_type?(type_defn)
|
162
191
|
@schema.orphan_types.include?(type_defn)
|
163
192
|
end
|
@@ -170,7 +199,7 @@ module GraphQL
|
|
170
199
|
end
|
171
200
|
|
172
201
|
def visible_possible_types?(type_defn)
|
173
|
-
|
202
|
+
possible_types(type_defn).any? { |t| visible_type?(t) }
|
174
203
|
end
|
175
204
|
|
176
205
|
def visible?(member)
|
@@ -71,7 +71,7 @@ module GraphQL
|
|
71
71
|
module ContextMethods
|
72
72
|
def on_operation_definition(node, parent)
|
73
73
|
object_type = @schema.root_type_for_operation(node.operation_type)
|
74
|
-
|
74
|
+
push_type(object_type)
|
75
75
|
@path.push("#{node.operation_type}#{node.name ? " #{node.name}" : ""}")
|
76
76
|
super
|
77
77
|
@object_types.pop
|
@@ -98,9 +98,9 @@ module GraphQL
|
|
98
98
|
@field_definitions.push(field_definition)
|
99
99
|
if !field_definition.nil?
|
100
100
|
next_object_type = field_definition.type.unwrap
|
101
|
-
|
101
|
+
push_type(next_object_type)
|
102
102
|
else
|
103
|
-
|
103
|
+
push_type(nil)
|
104
104
|
end
|
105
105
|
@path.push(node.alias || node.name)
|
106
106
|
super
|
@@ -120,7 +120,7 @@ module GraphQL
|
|
120
120
|
argument_defn = if (arg = @argument_definitions.last)
|
121
121
|
arg_type = arg.type.unwrap
|
122
122
|
if arg_type.kind.input_object?
|
123
|
-
arg_type.
|
123
|
+
arg_type.arguments[node.name]
|
124
124
|
else
|
125
125
|
nil
|
126
126
|
end
|
@@ -187,15 +187,19 @@ module GraphQL
|
|
187
187
|
|
188
188
|
def on_fragment_with_type(node)
|
189
189
|
object_type = if node.type
|
190
|
-
@schema.
|
190
|
+
@schema.get_type(node.type.name)
|
191
191
|
else
|
192
192
|
@object_types.last
|
193
193
|
end
|
194
|
-
|
194
|
+
push_type(object_type)
|
195
195
|
yield(node)
|
196
196
|
@object_types.pop
|
197
197
|
@path.pop
|
198
198
|
end
|
199
|
+
|
200
|
+
def push_type(t)
|
201
|
+
@object_types.push(t)
|
202
|
+
end
|
199
203
|
end
|
200
204
|
|
201
205
|
private
|
@@ -11,11 +11,11 @@ module GraphQL
|
|
11
11
|
super
|
12
12
|
@defdep_node_paths = {}
|
13
13
|
|
14
|
-
# { name => node } pairs for fragments
|
15
|
-
@defdep_fragment_definitions = {}
|
14
|
+
# { name => [node, ...] } pairs for fragments (although duplicate-named fragments are _invalid_, they are _possible_)
|
15
|
+
@defdep_fragment_definitions = Hash.new{ |h, k| h[k] = [] }
|
16
16
|
|
17
17
|
# This tracks dependencies from fragment to Node where it was used
|
18
|
-
# {
|
18
|
+
# { fragment_definition_name => [dependent_node, dependent_node]}
|
19
19
|
@defdep_dependent_definitions = Hash.new { |h, k| h[k] = Set.new }
|
20
20
|
|
21
21
|
# First-level usages of spreads within definitions
|
@@ -32,7 +32,7 @@ module GraphQL
|
|
32
32
|
def on_document(node, parent)
|
33
33
|
node.definitions.each do |definition|
|
34
34
|
if definition.is_a? GraphQL::Language::Nodes::FragmentDefinition
|
35
|
-
@defdep_fragment_definitions[definition.name]
|
35
|
+
@defdep_fragment_definitions[definition.name] << definition
|
36
36
|
end
|
37
37
|
end
|
38
38
|
super
|
@@ -42,7 +42,7 @@ module GraphQL
|
|
42
42
|
end
|
43
43
|
|
44
44
|
def on_operation_definition(node, prev_node)
|
45
|
-
@defdep_node_paths[node] = NodeWithPath.new(node, context.path)
|
45
|
+
@defdep_node_paths[node.name] = NodeWithPath.new(node, context.path)
|
46
46
|
@defdep_current_parent = node
|
47
47
|
super
|
48
48
|
@defdep_current_parent = nil
|
@@ -59,7 +59,7 @@ module GraphQL
|
|
59
59
|
@defdep_node_paths[node] = NodeWithPath.new(node, context.path)
|
60
60
|
|
61
61
|
# Track both sides of the dependency
|
62
|
-
@defdep_dependent_definitions[
|
62
|
+
@defdep_dependent_definitions[node.name] << @defdep_current_parent
|
63
63
|
@defdep_immediate_dependencies[@defdep_current_parent] << node
|
64
64
|
super
|
65
65
|
end
|
@@ -116,24 +116,28 @@ module GraphQL
|
|
116
116
|
dependency_map = DependencyMap.new
|
117
117
|
# Don't allow the loop to run more times
|
118
118
|
# than the number of fragments in the document
|
119
|
-
max_loops =
|
119
|
+
max_loops = 0
|
120
|
+
@defdep_fragment_definitions.each_value do |v|
|
121
|
+
max_loops += v.size
|
122
|
+
end
|
123
|
+
|
120
124
|
loops = 0
|
121
125
|
|
122
126
|
# Instead of tracking independent fragments _as you visit_,
|
123
127
|
# determine them at the end. This way, we can treat fragments with the
|
124
128
|
# same name as if they were the same name. If _any_ of the fragments
|
125
129
|
# with that name has a dependency, we record it.
|
126
|
-
independent_fragment_nodes = @defdep_fragment_definitions.values - @defdep_immediate_dependencies.keys
|
130
|
+
independent_fragment_nodes = @defdep_fragment_definitions.values.flatten - @defdep_immediate_dependencies.keys
|
127
131
|
|
128
132
|
while fragment_node = independent_fragment_nodes.pop
|
129
133
|
loops += 1
|
130
134
|
if loops > max_loops
|
131
|
-
raise("Resolution loops exceeded the number of definitions; infinite loop detected.")
|
135
|
+
raise("Resolution loops exceeded the number of definitions; infinite loop detected. (Max: #{max_loops}, Current: #{loops})")
|
132
136
|
end
|
133
137
|
# Since it's independent, let's remove it from here.
|
134
138
|
# That way, we can use the remainder to identify cycles
|
135
139
|
@defdep_immediate_dependencies.delete(fragment_node)
|
136
|
-
fragment_usages = @defdep_dependent_definitions[fragment_node]
|
140
|
+
fragment_usages = @defdep_dependent_definitions[fragment_node.name]
|
137
141
|
if fragment_usages.empty?
|
138
142
|
# If we didn't record any usages during the visit,
|
139
143
|
# then this fragment is unused.
|
@@ -151,10 +155,15 @@ module GraphQL
|
|
151
155
|
if block_given?
|
152
156
|
yield(definition_node, removed, fragment_node)
|
153
157
|
end
|
154
|
-
if remaining.empty? &&
|
158
|
+
if remaining.empty? &&
|
159
|
+
definition_node.is_a?(GraphQL::Language::Nodes::FragmentDefinition) &&
|
160
|
+
definition_node.name != fragment_node.name
|
155
161
|
# If all of this definition's dependencies have
|
156
162
|
# been resolved, we can now resolve its
|
157
163
|
# own dependents.
|
164
|
+
#
|
165
|
+
# But, it's possible to have a duplicate-named fragment here.
|
166
|
+
# Skip it in that case
|
158
167
|
independent_fragment_nodes << definition_node
|
159
168
|
end
|
160
169
|
end
|
@@ -166,7 +175,7 @@ module GraphQL
|
|
166
175
|
# then they're still in there
|
167
176
|
@defdep_immediate_dependencies.each do |defn_node, deps|
|
168
177
|
deps.each do |spread|
|
169
|
-
if
|
178
|
+
if !@defdep_fragment_definitions.key?(spread.name)
|
170
179
|
dependency_map.unmet_dependencies[@defdep_node_paths[defn_node]] << @defdep_node_paths[spread]
|
171
180
|
deps.delete(spread)
|
172
181
|
end
|