graphql 1.10.4 → 1.10.5
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/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
|