graphql 1.10.4 → 1.10.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/graphql.rb +0 -1
- data/lib/graphql/analysis/ast/query_complexity.rb +117 -105
- data/lib/graphql/execution/lookahead.rb +21 -108
- data/lib/graphql/pagination/active_record_relation_connection.rb +7 -1
- data/lib/graphql/pagination/array_connection.rb +2 -2
- data/lib/graphql/pagination/connection.rb +17 -5
- data/lib/graphql/pagination/relation_connection.rb +3 -2
- data/lib/graphql/query/input_validation_result.rb +23 -6
- data/lib/graphql/query/variables.rb +1 -1
- data/lib/graphql/relay/array_connection.rb +8 -10
- data/lib/graphql/relay/base_connection.rb +19 -11
- data/lib/graphql/relay/relation_connection.rb +8 -10
- data/lib/graphql/scalar_type.rb +14 -1
- data/lib/graphql/schema.rb +6 -0
- data/lib/graphql/schema/build_from_definition.rb +3 -0
- data/lib/graphql/schema/scalar.rb +9 -1
- data/lib/graphql/static_validation/literal_validator.rb +47 -22
- data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +42 -87
- data/lib/graphql/static_validation/rules/argument_literals_are_compatible_error.rb +17 -5
- data/lib/graphql/static_validation/rules/arguments_are_defined.rb +25 -20
- data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +9 -9
- data/lib/graphql/static_validation/validation_context.rb +1 -1
- data/lib/graphql/version.rb +1 -1
- metadata +2 -3
- data/lib/graphql/literal_validation_error.rb +0 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ace6be63c92b2a2139381664968aef4eeed01a5b1e3936af31ef3b0d7735b944
|
4
|
+
data.tar.gz: 6d572562697f474dc82f81e3c28e9d234bf9348ef2446db41a10fa4b6d52cec4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4da367c261c3fd79192aea827be4b0cebc864820d4dba6ed74a3c57d9c7772511bf6b96bdd4316e9186bd751954a964b09bc1e455fed4f687edb958d869f25e5
|
7
|
+
data.tar.gz: a5d4c7c349cebf6ce1e064763c47a61e6018a29d4b563ce833390ed5d378f5dd977b884f2a1809a7935e43902a9e33e23682662192cc3ff5ab8079c75c1b5fb6
|
data/lib/graphql.rb
CHANGED
@@ -109,7 +109,6 @@ require "graphql/introspection"
|
|
109
109
|
|
110
110
|
require "graphql/analysis_error"
|
111
111
|
require "graphql/coercion_error"
|
112
|
-
require "graphql/literal_validation_error"
|
113
112
|
require "graphql/runtime_type_error"
|
114
113
|
require "graphql/invalid_null_error"
|
115
114
|
require "graphql/invalid_name_error"
|
@@ -21,13 +21,17 @@ module GraphQL
|
|
21
21
|
# since the lexical binding isn't important.
|
22
22
|
HASH_CHILDREN = ->(h, k) { h[k] = {} }
|
23
23
|
|
24
|
+
attr_reader :field_definition, :response_path, :query
|
25
|
+
|
24
26
|
# @param node [Language::Nodes::Field] The AST node; used for providing argument values when necessary
|
25
27
|
# @param field_definition [GraphQL::Field, GraphQL::Schema::Field] Used for getting the `.complexity` configuration
|
26
|
-
# @param query [
|
27
|
-
|
28
|
+
# @param query [GraphQL::Query] Used for `query.possible_types`
|
29
|
+
# @param response_path [Array<String>] The path to the response key for the field
|
30
|
+
def initialize(node, field_definition, query, response_path)
|
31
|
+
@node = node
|
28
32
|
@field_definition = field_definition
|
29
33
|
@query = query
|
30
|
-
@
|
34
|
+
@response_path = response_path
|
31
35
|
@scoped_children = nil
|
32
36
|
end
|
33
37
|
|
@@ -73,9 +77,9 @@ module GraphQL
|
|
73
77
|
# `node` and `visitor.field_definition` may appear from a cache,
|
74
78
|
# but I think that's ok. If the arguments _didn't_ match,
|
75
79
|
# then the query would have been rejected as invalid.
|
76
|
-
complexities_on_type = @complexities_on_type_by_query[visitor.query] ||= [ScopedTypeComplexity.new(nil, nil, query)]
|
80
|
+
complexities_on_type = @complexities_on_type_by_query[visitor.query] ||= [ScopedTypeComplexity.new(nil, nil, query, visitor.response_path)]
|
77
81
|
|
78
|
-
complexity = complexities_on_type.last.scoped_children[parent_type][field_key] ||= ScopedTypeComplexity.new(node, visitor.field_definition, visitor.query)
|
82
|
+
complexity = complexities_on_type.last.scoped_children[parent_type][field_key] ||= ScopedTypeComplexity.new(node, visitor.field_definition, visitor.query, visitor.response_path)
|
79
83
|
# Push it on the stack.
|
80
84
|
complexities_on_type.push(complexity)
|
81
85
|
end
|
@@ -89,132 +93,140 @@ module GraphQL
|
|
89
93
|
complexities_on_type.pop
|
90
94
|
end
|
91
95
|
|
96
|
+
private
|
97
|
+
|
92
98
|
# @return [Integer]
|
93
99
|
def max_possible_complexity
|
94
100
|
@complexities_on_type_by_query.reduce(0) do |total, (query, complexities_on_type)|
|
95
101
|
root_complexity = complexities_on_type.last
|
96
102
|
# Use this entry point to calculate the total complexity
|
97
|
-
total_complexity_for_query =
|
103
|
+
total_complexity_for_query = merged_max_complexity_for_scopes(query, [root_complexity.scoped_children])
|
98
104
|
total + total_complexity_for_query
|
99
105
|
end
|
100
106
|
end
|
101
107
|
|
102
|
-
#
|
103
|
-
#
|
104
|
-
#
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
def applies_to?(query, left_scope, right_scope)
|
112
|
-
if left_scope == right_scope
|
113
|
-
# This can happen when several branches are being analyzed together
|
114
|
-
true
|
115
|
-
else
|
116
|
-
# Check if these two scopes have _any_ types in common.
|
117
|
-
possible_right_types = query.possible_types(right_scope)
|
118
|
-
possible_left_types = query.possible_types(left_scope)
|
119
|
-
!(possible_right_types & possible_left_types).empty?
|
120
|
-
end
|
108
|
+
# @param query [GraphQL::Query] Used for `query.possible_types`
|
109
|
+
# @param scoped_children_hashes [Array<Hash>] Array of scoped children hashes
|
110
|
+
# @return [Integer]
|
111
|
+
def merged_max_complexity_for_scopes(query, scoped_children_hashes)
|
112
|
+
# Figure out what scopes are possible here.
|
113
|
+
# Use a hash, but ignore the values; it's just a fast way to work with the keys.
|
114
|
+
all_scopes = {}
|
115
|
+
scoped_children_hashes.each do |h|
|
116
|
+
all_scopes.merge!(h)
|
121
117
|
end
|
122
118
|
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
119
|
+
# If an abstract scope is present, but _all_ of its concrete types
|
120
|
+
# are also in the list, remove it from the list of scopes to check,
|
121
|
+
# because every possible type is covered by a concrete type.
|
122
|
+
# (That is, there are no remainder types to check.)
|
123
|
+
prev_keys = all_scopes.keys
|
124
|
+
prev_keys.each do |scope|
|
125
|
+
next unless scope.kind.abstract?
|
126
|
+
|
127
|
+
missing_concrete_types = query.possible_types(scope).select { |t| !all_scopes.key?(t) }
|
128
|
+
# This concrete type is possible _only_ as a member of the abstract type.
|
129
|
+
# So, attribute to it the complexity which belongs to the abstract type.
|
130
|
+
missing_concrete_types.each do |concrete_scope|
|
131
|
+
all_scopes[concrete_scope] = all_scopes[scope]
|
129
132
|
end
|
133
|
+
all_scopes.delete(scope)
|
134
|
+
end
|
130
135
|
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
136
|
+
# This will hold `{ type => int }` pairs, one for each possible branch
|
137
|
+
complexity_by_scope = {}
|
138
|
+
|
139
|
+
# For each scope,
|
140
|
+
# find the lexical selections that might apply to it,
|
141
|
+
# and gather them together into an array.
|
142
|
+
# Then, treat the set of selection hashes
|
143
|
+
# as a set and calculate the complexity for them as a unit
|
144
|
+
all_scopes.each do |scope, _|
|
145
|
+
# These will be the selections on `scope`
|
146
|
+
children_for_scope = []
|
147
|
+
scoped_children_hashes.each do |sc_h|
|
148
|
+
sc_h.each do |inner_scope, children_hash|
|
149
|
+
if applies_to?(query, scope, inner_scope)
|
150
|
+
children_for_scope << children_hash
|
145
151
|
end
|
146
|
-
all_scopes.delete(scope)
|
147
152
|
end
|
148
153
|
end
|
149
154
|
|
150
|
-
#
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
# and gather them together into an array.
|
156
|
-
# Then, treat the set of selection hashes
|
157
|
-
# as a set and calculate the complexity for them as a unit
|
158
|
-
all_scopes.each do |scope, _|
|
159
|
-
# These will be the selections on `scope`
|
160
|
-
children_for_scope = []
|
161
|
-
scoped_children_hashes.each do |sc_h|
|
162
|
-
sc_h.each do |inner_scope, children_hash|
|
163
|
-
if applies_to?(query, scope, inner_scope)
|
164
|
-
children_for_scope << children_hash
|
165
|
-
end
|
166
|
-
end
|
167
|
-
end
|
155
|
+
# Calculate the complexity for `scope`, merging all
|
156
|
+
# possible lexical branches.
|
157
|
+
complexity_value = merged_max_complexity(query, children_for_scope)
|
158
|
+
complexity_by_scope[scope] = complexity_value
|
159
|
+
end
|
168
160
|
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
complexity_by_scope[scope] = complexity_value
|
173
|
-
end
|
161
|
+
# Return the max complexity among all scopes
|
162
|
+
complexity_by_scope.each_value.max
|
163
|
+
end
|
174
164
|
|
175
|
-
|
176
|
-
|
165
|
+
def applies_to?(query, left_scope, right_scope)
|
166
|
+
if left_scope == right_scope
|
167
|
+
# This can happen when several branches are being analyzed together
|
168
|
+
true
|
169
|
+
else
|
170
|
+
# Check if these two scopes have _any_ types in common.
|
171
|
+
possible_right_types = query.possible_types(right_scope)
|
172
|
+
possible_left_types = query.possible_types(left_scope)
|
173
|
+
!(possible_right_types & possible_left_types).empty?
|
177
174
|
end
|
175
|
+
end
|
178
176
|
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
complexity_for_keys = {}
|
188
|
-
all_keys.each do |child_key|
|
189
|
-
|
190
|
-
scoped_children_for_key = nil
|
191
|
-
complexity_for_key = nil
|
192
|
-
children_for_scope.each do |children_hash|
|
193
|
-
if children_hash.key?(child_key)
|
194
|
-
complexity_for_key = children_hash[child_key]
|
195
|
-
if complexity_for_key.terminal?
|
196
|
-
# Assume that all terminals would return the same complexity
|
197
|
-
# Since it's a terminal, its child complexity is zero.
|
198
|
-
complexity_for_key = complexity_for_key.own_complexity(0)
|
199
|
-
complexity_for_keys[child_key] = complexity_for_key
|
200
|
-
else
|
201
|
-
scoped_children_for_key ||= []
|
202
|
-
scoped_children_for_key << complexity_for_key.scoped_children
|
203
|
-
end
|
204
|
-
end
|
205
|
-
end
|
177
|
+
# A hook which is called whenever a field's max complexity is calculated.
|
178
|
+
# Override this method to capture individual field complexity details.
|
179
|
+
#
|
180
|
+
# @param scoped_type_complexity [ScopedTypeComplexity]
|
181
|
+
# @param max_complexity [Numeric] Field's maximum complexity including child complexity
|
182
|
+
# @param child_complexity [Numeric, nil] Field's child complexity
|
183
|
+
def field_complexity(scoped_type_complexity, max_complexity:, child_complexity: nil)
|
184
|
+
end
|
206
185
|
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
186
|
+
# @param children_for_scope [Array<Hash>] An array of `scoped_children[scope]` hashes
|
187
|
+
# (`{field_key => complexity}`)
|
188
|
+
# @return [Integer] Complexity value for all these selections in the current scope
|
189
|
+
def merged_max_complexity(query, children_for_scope)
|
190
|
+
all_keys = []
|
191
|
+
children_for_scope.each do |c|
|
192
|
+
all_keys.concat(c.keys)
|
193
|
+
end
|
194
|
+
all_keys.uniq!
|
195
|
+
complexity_for_keys = {}
|
196
|
+
|
197
|
+
all_keys.each do |child_key|
|
198
|
+
scoped_children_for_key = nil
|
199
|
+
complexity_for_key = nil
|
200
|
+
children_for_scope.each do |children_hash|
|
201
|
+
next unless children_hash.key?(child_key)
|
202
|
+
|
203
|
+
complexity_for_key = children_hash[child_key]
|
204
|
+
if complexity_for_key.terminal?
|
205
|
+
# Assume that all terminals would return the same complexity
|
206
|
+
# Since it's a terminal, its child complexity is zero.
|
207
|
+
complexity = complexity_for_key.own_complexity(0)
|
208
|
+
complexity_for_keys[child_key] = complexity
|
209
|
+
|
210
|
+
field_complexity(complexity_for_key, max_complexity: complexity, child_complexity: nil)
|
211
|
+
else
|
212
|
+
scoped_children_for_key ||= []
|
213
|
+
scoped_children_for_key << complexity_for_key.scoped_children
|
212
214
|
end
|
213
215
|
end
|
214
216
|
|
215
|
-
|
216
|
-
|
217
|
+
next unless scoped_children_for_key
|
218
|
+
|
219
|
+
child_complexity = merged_max_complexity_for_scopes(query, scoped_children_for_key)
|
220
|
+
# This is the _last_ one we visited; assume it's representative.
|
221
|
+
max_complexity = complexity_for_key.own_complexity(child_complexity)
|
222
|
+
|
223
|
+
field_complexity(complexity_for_key, max_complexity: max_complexity, child_complexity: child_complexity)
|
224
|
+
|
225
|
+
complexity_for_keys[child_key] = max_complexity
|
217
226
|
end
|
227
|
+
|
228
|
+
# Calculate the child complexity by summing the complexity of all selections
|
229
|
+
complexity_for_keys.each_value.inject(0, &:+)
|
218
230
|
end
|
219
231
|
end
|
220
232
|
end
|
@@ -51,7 +51,15 @@ module GraphQL
|
|
51
51
|
|
52
52
|
# @return [Hash<Symbol, Object>]
|
53
53
|
def arguments
|
54
|
-
|
54
|
+
if defined?(@arguments)
|
55
|
+
@arguments
|
56
|
+
else
|
57
|
+
@arguments = if @field
|
58
|
+
@query.arguments_for(@ast_nodes.first, @field)
|
59
|
+
else
|
60
|
+
nil
|
61
|
+
end
|
62
|
+
end
|
55
63
|
end
|
56
64
|
|
57
65
|
# True if this node has a selection on `field_name`.
|
@@ -81,7 +89,7 @@ module GraphQL
|
|
81
89
|
def selection(field_name, selected_type: @selected_type, arguments: nil)
|
82
90
|
next_field_name = normalize_name(field_name)
|
83
91
|
|
84
|
-
next_field_defn =
|
92
|
+
next_field_defn = get_class_based_field(selected_type, next_field_name)
|
85
93
|
if next_field_defn
|
86
94
|
next_nodes = []
|
87
95
|
@ast_nodes.each do |ast_node|
|
@@ -127,7 +135,7 @@ module GraphQL
|
|
127
135
|
|
128
136
|
subselections_by_type.each do |type, ast_nodes_by_response_key|
|
129
137
|
ast_nodes_by_response_key.each do |response_key, ast_nodes|
|
130
|
-
field_defn =
|
138
|
+
field_defn = get_class_based_field(type, ast_nodes.first.name)
|
131
139
|
lookahead = Lookahead.new(query: @query, ast_nodes: ast_nodes, field: field_defn, owner_type: type)
|
132
140
|
subselections.push(lookahead)
|
133
141
|
end
|
@@ -203,12 +211,19 @@ module GraphQL
|
|
203
211
|
end
|
204
212
|
end
|
205
213
|
|
214
|
+
# Wrap get_field and ensure that it returns a GraphQL::Schema::Field.
|
215
|
+
# Remove this when legacy execution is removed.
|
216
|
+
def get_class_based_field(type, name)
|
217
|
+
f = @query.get_field(type, name)
|
218
|
+
f && f.type_class
|
219
|
+
end
|
220
|
+
|
206
221
|
def skipped_by_directive?(ast_selection)
|
207
222
|
ast_selection.directives.each do |directive|
|
208
223
|
dir_defn = @query.schema.directives.fetch(directive.name)
|
209
224
|
directive_class = dir_defn.type_class
|
210
225
|
if directive_class
|
211
|
-
dir_args =
|
226
|
+
dir_args = @query.arguments_for(directive, dir_defn)
|
212
227
|
return true unless directive_class.static_include?(dir_args, @query.context)
|
213
228
|
end
|
214
229
|
end
|
@@ -227,7 +242,7 @@ module GraphQL
|
|
227
242
|
elsif arguments.nil? || arguments.empty?
|
228
243
|
selections_on_type[response_key] = [ast_selection]
|
229
244
|
else
|
230
|
-
field_defn =
|
245
|
+
field_defn = get_class_based_field(selected_type, ast_selection.name)
|
231
246
|
if arguments_match?(arguments, field_defn, ast_selection)
|
232
247
|
selections_on_type[response_key] = [ast_selection]
|
233
248
|
end
|
@@ -278,115 +293,13 @@ module GraphQL
|
|
278
293
|
end
|
279
294
|
|
280
295
|
def arguments_match?(arguments, field_defn, field_node)
|
281
|
-
query_kwargs =
|
296
|
+
query_kwargs = @query.arguments_for(field_node, field_defn)
|
282
297
|
arguments.all? do |arg_name, arg_value|
|
283
298
|
arg_name = normalize_keyword(arg_name)
|
284
299
|
# Make sure the constraint is present with a matching value
|
285
300
|
query_kwargs.key?(arg_name) && query_kwargs[arg_name] == arg_value
|
286
301
|
end
|
287
302
|
end
|
288
|
-
|
289
|
-
# TODO Dedup with interpreter
|
290
|
-
module ArgumentHelpers
|
291
|
-
module_function
|
292
|
-
|
293
|
-
def arguments(query, arg_owner, ast_node)
|
294
|
-
kwarg_arguments = {}
|
295
|
-
arg_defns = arg_owner.arguments
|
296
|
-
ast_node.arguments.each do |arg|
|
297
|
-
arg_defn = arg_defns[arg.name] || raise("Invariant: missing argument definition for #{arg.name.inspect} in #{arg_defns.keys} from #{arg_owner}")
|
298
|
-
# Need to distinguish between client-provided `nil`
|
299
|
-
# and nothing-at-all
|
300
|
-
is_present, value = arg_to_value(query, arg_defn.type, arg.value)
|
301
|
-
if is_present
|
302
|
-
kwarg_arguments[arg_defn.keyword] = value
|
303
|
-
end
|
304
|
-
end
|
305
|
-
arg_defns.each do |name, arg_defn|
|
306
|
-
if arg_defn.default_value? && !kwarg_arguments.key?(arg_defn.keyword)
|
307
|
-
kwarg_arguments[arg_defn.keyword] = arg_defn.default_value
|
308
|
-
end
|
309
|
-
end
|
310
|
-
kwarg_arguments
|
311
|
-
end
|
312
|
-
|
313
|
-
# Get a Ruby-ready value from a client query.
|
314
|
-
# @param graphql_object [Object] The owner of the field whose argument this is
|
315
|
-
# @param arg_type [Class, GraphQL::Schema::NonNull, GraphQL::Schema::List]
|
316
|
-
# @param ast_value [GraphQL::Language::Nodes::VariableIdentifier, String, Integer, Float, Boolean]
|
317
|
-
# @return [Array(is_present, value)]
|
318
|
-
def arg_to_value(query, arg_type, ast_value)
|
319
|
-
if ast_value.is_a?(GraphQL::Language::Nodes::VariableIdentifier)
|
320
|
-
# If it's not here, it will get added later
|
321
|
-
if query.variables.key?(ast_value.name)
|
322
|
-
return true, query.variables[ast_value.name]
|
323
|
-
else
|
324
|
-
return false, nil
|
325
|
-
end
|
326
|
-
elsif ast_value.is_a?(GraphQL::Language::Nodes::NullValue)
|
327
|
-
return true, nil
|
328
|
-
elsif arg_type.is_a?(GraphQL::Schema::NonNull)
|
329
|
-
arg_to_value(query, arg_type.of_type, ast_value)
|
330
|
-
elsif arg_type.is_a?(GraphQL::Schema::List)
|
331
|
-
# Treat a single value like a list
|
332
|
-
arg_value = Array(ast_value)
|
333
|
-
list = []
|
334
|
-
arg_value.map do |inner_v|
|
335
|
-
_present, value = arg_to_value(query, arg_type.of_type, inner_v)
|
336
|
-
list << value
|
337
|
-
end
|
338
|
-
return true, list
|
339
|
-
elsif arg_type.is_a?(Class) && arg_type < GraphQL::Schema::InputObject
|
340
|
-
# For these, `prepare` is applied during `#initialize`.
|
341
|
-
# Pass `nil` so it will be skipped in `#arguments`.
|
342
|
-
# What a mess.
|
343
|
-
args = arguments(query, nil, arg_type, ast_value)
|
344
|
-
# We're not tracking defaults_used, but for our purposes
|
345
|
-
# we compare the value to the default value.
|
346
|
-
return true, arg_type.new(ruby_kwargs: args, context: query.context, defaults_used: nil)
|
347
|
-
else
|
348
|
-
flat_value = flatten_ast_value(query, ast_value)
|
349
|
-
return true, arg_type.coerce_input(flat_value, query.context)
|
350
|
-
end
|
351
|
-
end
|
352
|
-
|
353
|
-
def flatten_ast_value(query, v)
|
354
|
-
case v
|
355
|
-
when GraphQL::Language::Nodes::Enum
|
356
|
-
v.name
|
357
|
-
when GraphQL::Language::Nodes::InputObject
|
358
|
-
h = {}
|
359
|
-
v.arguments.each do |arg|
|
360
|
-
h[arg.name] = flatten_ast_value(query, arg.value)
|
361
|
-
end
|
362
|
-
h
|
363
|
-
when Array
|
364
|
-
v.map { |v2| flatten_ast_value(query, v2) }
|
365
|
-
when GraphQL::Language::Nodes::VariableIdentifier
|
366
|
-
flatten_ast_value(query.variables[v.name])
|
367
|
-
else
|
368
|
-
v
|
369
|
-
end
|
370
|
-
end
|
371
|
-
end
|
372
|
-
|
373
|
-
# TODO dedup with interpreter
|
374
|
-
module FieldHelpers
|
375
|
-
module_function
|
376
|
-
|
377
|
-
def get_field(schema, owner_type, field_name)
|
378
|
-
field_defn = owner_type.get_field(field_name)
|
379
|
-
field_defn ||= if owner_type == schema.query.type_class && (entry_point_field = schema.introspection_system.entry_point(name: field_name))
|
380
|
-
entry_point_field.type_class
|
381
|
-
elsif (dynamic_field = schema.introspection_system.dynamic_field(name: field_name))
|
382
|
-
dynamic_field.type_class
|
383
|
-
else
|
384
|
-
nil
|
385
|
-
end
|
386
|
-
|
387
|
-
field_defn
|
388
|
-
end
|
389
|
-
end
|
390
303
|
end
|
391
304
|
end
|
392
305
|
end
|