graphql 2.1.1 → 2.1.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/analysis/ast/query_depth.rb +7 -2
- data/lib/graphql/dataloader.rb +29 -10
- data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +1 -1
- data/lib/graphql/execution/interpreter/runtime.rb +7 -26
- data/lib/graphql/execution/lookahead.rb +87 -20
- data/lib/graphql/load_application_object_failed_error.rb +5 -1
- data/lib/graphql/pagination/connection.rb +15 -10
- data/lib/graphql/pagination/mongoid_relation_connection.rb +1 -2
- data/lib/graphql/query/context.rb +4 -0
- data/lib/graphql/query/null_context.rb +2 -10
- data/lib/graphql/query.rb +10 -0
- data/lib/graphql/schema/build_from_definition.rb +0 -11
- data/lib/graphql/schema/directive/one_of.rb +12 -0
- data/lib/graphql/schema/directive.rb +1 -1
- data/lib/graphql/schema/enum.rb +2 -2
- data/lib/graphql/schema/field/scope_extension.rb +4 -3
- data/lib/graphql/schema/field.rb +2 -2
- data/lib/graphql/schema/has_single_input_argument.rb +2 -2
- data/lib/graphql/schema/input_object.rb +1 -1
- data/lib/graphql/schema/interface.rb +10 -10
- data/lib/graphql/schema/loader.rb +0 -2
- data/lib/graphql/schema/member/has_arguments.rb +47 -36
- data/lib/graphql/schema/member/has_fields.rb +4 -4
- data/lib/graphql/schema/member/has_interfaces.rb +2 -2
- data/lib/graphql/schema/member/validates_input.rb +3 -3
- data/lib/graphql/schema/resolver.rb +3 -3
- data/lib/graphql/schema/union.rb +1 -1
- data/lib/graphql/schema/warden.rb +73 -57
- data/lib/graphql/schema.rb +154 -43
- data/lib/graphql/subscriptions/event.rb +1 -1
- data/lib/graphql/subscriptions.rb +2 -2
- data/lib/graphql/version.rb +1 -1
- metadata +17 -17
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ab900a93fdb2a8326a75035bf03e27e4874cc959c916d9ac8f85602c78f55c53
|
4
|
+
data.tar.gz: ca2c67da2b27b4a661759f5e00b49178d12ae312f77d536959e7d825ecd516af
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 47639dd694f5c027bd05a2b4fae198fad2b98154ba024785f6fcd844917be5b9bf841ed82b1753061ba9890c5881a8082a62e619090492c7247b345163f45322
|
7
|
+
data.tar.gz: df76cec39f5bba1ab58958595d508869fd70610feea72af54ff40c0cd4649b8a9bc6ad6d891907b9b69fc517602bf50057d86dab563223988fb5f4ccd2763c0b
|
@@ -28,17 +28,22 @@ module GraphQL
|
|
28
28
|
def initialize(query)
|
29
29
|
@max_depth = 0
|
30
30
|
@current_depth = 0
|
31
|
+
@count_introspection_fields = query.schema.count_introspection_fields
|
31
32
|
super
|
32
33
|
end
|
33
34
|
|
34
35
|
def on_enter_field(node, parent, visitor)
|
35
|
-
return if visitor.skipping? ||
|
36
|
+
return if visitor.skipping? ||
|
37
|
+
visitor.visiting_fragment_definition? ||
|
38
|
+
(@count_introspection_fields == false && visitor.field_definition.introspection?)
|
36
39
|
|
37
40
|
@current_depth += 1
|
38
41
|
end
|
39
42
|
|
40
43
|
def on_leave_field(node, parent, visitor)
|
41
|
-
return if visitor.skipping? ||
|
44
|
+
return if visitor.skipping? ||
|
45
|
+
visitor.visiting_fragment_definition? ||
|
46
|
+
(@count_introspection_fields == false && visitor.field_definition.introspection?)
|
42
47
|
|
43
48
|
if @max_depth < @current_depth
|
44
49
|
@max_depth = @current_depth
|
data/lib/graphql/dataloader.rb
CHANGED
@@ -61,6 +61,32 @@ module GraphQL
|
|
61
61
|
@nonblocking
|
62
62
|
end
|
63
63
|
|
64
|
+
# This is called before the fiber is spawned, from the parent context (i.e. from
|
65
|
+
# the thread or fiber that it is scheduled from).
|
66
|
+
#
|
67
|
+
# @return [Hash<Symbol, Object>] Current fiber-local variables
|
68
|
+
def get_fiber_variables
|
69
|
+
fiber_vars = {}
|
70
|
+
Thread.current.keys.each do |fiber_var_key|
|
71
|
+
# This variable should be fresh in each new fiber
|
72
|
+
if fiber_var_key != :__graphql_runtime_info
|
73
|
+
fiber_vars[fiber_var_key] = Thread.current[fiber_var_key]
|
74
|
+
end
|
75
|
+
end
|
76
|
+
fiber_vars
|
77
|
+
end
|
78
|
+
|
79
|
+
# Set up the fiber variables in a new fiber.
|
80
|
+
#
|
81
|
+
# This is called within the fiber, right after it is spawned.
|
82
|
+
#
|
83
|
+
# @param vars [Hash<Symbol, Object>] Fiber-local variables from {get_fiber_variables}
|
84
|
+
# @return [void]
|
85
|
+
def set_fiber_variables(vars)
|
86
|
+
vars.each { |k, v| Thread.current[k] = v }
|
87
|
+
nil
|
88
|
+
end
|
89
|
+
|
64
90
|
# Get a Source instance from this dataloader, for calling `.load(...)` or `.request(...)` on.
|
65
91
|
#
|
66
92
|
# @param source_class [Class<GraphQL::Dataloader::Source]
|
@@ -295,23 +321,16 @@ module GraphQL
|
|
295
321
|
#
|
296
322
|
# @see https://github.com/rmosolgo/graphql-ruby/issues/3449
|
297
323
|
def spawn_fiber
|
298
|
-
|
299
|
-
|
300
|
-
Thread.current.keys.each do |fiber_var_key|
|
301
|
-
# This variable should be fresh in each new fiber
|
302
|
-
if fiber_var_key != :__graphql_runtime_info
|
303
|
-
fiber_locals[fiber_var_key] = Thread.current[fiber_var_key]
|
304
|
-
end
|
305
|
-
end
|
324
|
+
fiber_vars = get_fiber_variables
|
306
325
|
|
307
326
|
if @nonblocking
|
308
327
|
Fiber.new(blocking: false) do
|
309
|
-
|
328
|
+
set_fiber_variables(fiber_vars)
|
310
329
|
yield
|
311
330
|
end
|
312
331
|
else
|
313
332
|
Fiber.new do
|
314
|
-
|
333
|
+
set_fiber_variables(fiber_vars)
|
315
334
|
yield
|
316
335
|
end
|
317
336
|
end
|
@@ -107,7 +107,7 @@ module GraphQL
|
|
107
107
|
when GraphQLResultArray
|
108
108
|
# There's no special handling of arrays because currently, there's no way to split the execution
|
109
109
|
# of a list over several concurrent flows.
|
110
|
-
|
110
|
+
into_result.set_child_result(key, value)
|
111
111
|
else
|
112
112
|
# We have to assume that, since this passed the `fields_will_merge` selection,
|
113
113
|
# that the old and new values are the same.
|
@@ -52,13 +52,6 @@ module GraphQL
|
|
52
52
|
# { Class => Boolean }
|
53
53
|
@lazy_cache = {}
|
54
54
|
@lazy_cache.compare_by_identity
|
55
|
-
|
56
|
-
@gathered_selections_cache = Hash.new { |h, k|
|
57
|
-
cache = {}
|
58
|
-
cache.compare_by_identity
|
59
|
-
h[k] = cache
|
60
|
-
}
|
61
|
-
@gathered_selections_cache.compare_by_identity
|
62
55
|
end
|
63
56
|
|
64
57
|
def final_result
|
@@ -98,7 +91,7 @@ module GraphQL
|
|
98
91
|
@response = nil
|
99
92
|
else
|
100
93
|
call_method_on_directives(:resolve, runtime_object, root_operation.directives) do # execute query level directives
|
101
|
-
gathered_selections = gather_selections(runtime_object, root_type,
|
94
|
+
gathered_selections = gather_selections(runtime_object, root_type, root_operation.selections)
|
102
95
|
# This is kind of a hack -- `gathered_selections` is an Array if any of the selections
|
103
96
|
# require isolation during execution (because of runtime directives). In that case,
|
104
97
|
# make a new, isolated result hash for writing the result into. (That isolated response
|
@@ -143,18 +136,11 @@ module GraphQL
|
|
143
136
|
nil
|
144
137
|
end
|
145
138
|
|
146
|
-
def gather_selections(owner_object, owner_type,
|
147
|
-
if ast_node_for_caching && (cached_selections = @gathered_selections_cache[ast_node_for_caching][owner_type])
|
148
|
-
return cached_selections
|
149
|
-
end
|
150
|
-
selections_by_name ||= {} # allocate this default here so we check the cache first
|
151
|
-
|
152
|
-
should_cache = true
|
139
|
+
def gather_selections(owner_object, owner_type, selections, selections_to_run = nil, selections_by_name = {})
|
153
140
|
|
154
141
|
selections.each do |node|
|
155
142
|
# Skip gathering this if the directive says so
|
156
143
|
if !directives_include?(node, owner_object, owner_type)
|
157
|
-
should_cache = false
|
158
144
|
next
|
159
145
|
end
|
160
146
|
|
@@ -179,7 +165,6 @@ module GraphQL
|
|
179
165
|
if @runtime_directive_names.any? && node.directives.any? { |d| @runtime_directive_names.include?(d.name) }
|
180
166
|
next_selections = {}
|
181
167
|
next_selections[:graphql_directives] = node.directives
|
182
|
-
should_cache = false
|
183
168
|
if selections_to_run
|
184
169
|
selections_to_run << next_selections
|
185
170
|
else
|
@@ -197,28 +182,24 @@ module GraphQL
|
|
197
182
|
type_defn = schema.get_type(node.type.name, context)
|
198
183
|
|
199
184
|
if query.warden.possible_types(type_defn).include?(owner_type)
|
200
|
-
gather_selections(owner_object, owner_type,
|
185
|
+
gather_selections(owner_object, owner_type, node.selections, selections_to_run, next_selections)
|
201
186
|
end
|
202
187
|
else
|
203
188
|
# it's an untyped fragment, definitely continue
|
204
|
-
gather_selections(owner_object, owner_type,
|
189
|
+
gather_selections(owner_object, owner_type, node.selections, selections_to_run, next_selections)
|
205
190
|
end
|
206
191
|
when GraphQL::Language::Nodes::FragmentSpread
|
207
192
|
fragment_def = query.fragments[node.name]
|
208
193
|
type_defn = query.get_type(fragment_def.type.name)
|
209
194
|
if query.warden.possible_types(type_defn).include?(owner_type)
|
210
|
-
gather_selections(owner_object, owner_type,
|
195
|
+
gather_selections(owner_object, owner_type, fragment_def.selections, selections_to_run, next_selections)
|
211
196
|
end
|
212
197
|
else
|
213
198
|
raise "Invariant: unexpected selection class: #{node.class}"
|
214
199
|
end
|
215
200
|
end
|
216
201
|
end
|
217
|
-
|
218
|
-
if should_cache
|
219
|
-
@gathered_selections_cache[ast_node_for_caching][owner_type] = result
|
220
|
-
end
|
221
|
-
result
|
202
|
+
selections_to_run || selections_by_name
|
222
203
|
end
|
223
204
|
|
224
205
|
NO_ARGS = GraphQL::EmptyObjects::EMPTY_HASH
|
@@ -619,7 +600,7 @@ module GraphQL
|
|
619
600
|
response_hash = GraphQLResultHash.new(result_name, selection_result, is_non_null)
|
620
601
|
set_result(selection_result, result_name, response_hash, true, is_non_null)
|
621
602
|
|
622
|
-
gathered_selections = gather_selections(continue_value, current_type,
|
603
|
+
gathered_selections = gather_selections(continue_value, current_type, next_selections)
|
623
604
|
# There are two possibilities for `gathered_selections`:
|
624
605
|
# 1. All selections of this object should be evaluated together (there are no runtime directives modifying execution).
|
625
606
|
# This case is handled below, and the result can be written right into the main `response_hash` above.
|
@@ -80,6 +80,22 @@ module GraphQL
|
|
80
80
|
selection(field_name, selected_type: selected_type, arguments: arguments).selected?
|
81
81
|
end
|
82
82
|
|
83
|
+
# True if this node has a selection with alias matching `alias_name`.
|
84
|
+
# If `alias_name` is a String, it is treated as a GraphQL-style (camelized)
|
85
|
+
# field name and used verbatim. If `alias_name` is a Symbol, it is
|
86
|
+
# treated as a Ruby-style (underscored) name and camelized before comparing.
|
87
|
+
#
|
88
|
+
# If `arguments:` is provided, each provided key/value will be matched
|
89
|
+
# against the arguments in the next selection. This method will return false
|
90
|
+
# if any of the given `arguments:` are not present and matching in the next selection.
|
91
|
+
# (But, the next selection may contain _more_ than the given arguments.)
|
92
|
+
# @param alias_name [String, Symbol]
|
93
|
+
# @param arguments [Hash] Arguments which must match in the selection
|
94
|
+
# @return [Boolean]
|
95
|
+
def selects_alias?(alias_name, arguments: nil)
|
96
|
+
alias_selection(alias_name, arguments: arguments).selected?
|
97
|
+
end
|
98
|
+
|
83
99
|
# @return [Boolean] True if this lookahead represents a field that was requested
|
84
100
|
def selected?
|
85
101
|
true
|
@@ -105,6 +121,7 @@ module GraphQL
|
|
105
121
|
.tap(&:flatten!)
|
106
122
|
end
|
107
123
|
|
124
|
+
|
108
125
|
if (match_by_orig_name = all_fields.find { |f| f.original_name == field_name })
|
109
126
|
match_by_orig_name
|
110
127
|
else
|
@@ -114,23 +131,29 @@ module GraphQL
|
|
114
131
|
@query.get_field(selected_type, guessed_name)
|
115
132
|
end
|
116
133
|
end
|
134
|
+
lookahead_for_selection(next_field_defn, selected_type, arguments)
|
135
|
+
end
|
117
136
|
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
end
|
137
|
+
# Like {#selection}, but for aliases.
|
138
|
+
# It returns a null object (check with {#selected?})
|
139
|
+
# @return [GraphQL::Execution::Lookahead]
|
140
|
+
def alias_selection(alias_name, selected_type: @selected_type, arguments: nil)
|
141
|
+
alias_cache_key = [alias_name, arguments]
|
142
|
+
return alias_selections[key] if alias_selections.key?(alias_name)
|
125
143
|
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
144
|
+
alias_node = lookup_alias_node(ast_nodes, alias_name)
|
145
|
+
return NULL_LOOKAHEAD unless alias_node
|
146
|
+
|
147
|
+
next_field_defn = @query.get_field(selected_type, alias_node.name)
|
148
|
+
|
149
|
+
alias_arguments = @query.arguments_for(alias_node, next_field_defn)
|
150
|
+
if alias_arguments.is_a?(::GraphQL::Execution::Interpreter::Arguments)
|
151
|
+
alias_arguments = alias_arguments.keyword_arguments
|
133
152
|
end
|
153
|
+
|
154
|
+
return NULL_LOOKAHEAD if arguments && arguments != alias_arguments
|
155
|
+
|
156
|
+
alias_selections[alias_cache_key] = lookahead_for_selection(next_field_defn, selected_type, alias_arguments, alias_name)
|
134
157
|
end
|
135
158
|
|
136
159
|
# Like {#selection}, but for all nodes.
|
@@ -258,7 +281,7 @@ module GraphQL
|
|
258
281
|
end
|
259
282
|
find_selections(subselections_by_type, subselections_on_type, on_type, ast_selection.selections, arguments)
|
260
283
|
when GraphQL::Language::Nodes::FragmentSpread
|
261
|
-
frag_defn =
|
284
|
+
frag_defn = lookup_fragment(ast_selection)
|
262
285
|
# Again, assuming a valid AST
|
263
286
|
on_type = @query.get_type(frag_defn.type.name)
|
264
287
|
subselections_on_type = subselections_by_type[on_type] ||= {}
|
@@ -271,11 +294,11 @@ module GraphQL
|
|
271
294
|
|
272
295
|
# If a selection on `node` matches `field_name` (which is backed by `field_defn`)
|
273
296
|
# and matches the `arguments:` constraints, then add that node to `matches`
|
274
|
-
def find_selected_nodes(node, field_defn, arguments:, matches:)
|
297
|
+
def find_selected_nodes(node, field_name, field_defn, arguments:, matches:, alias_name: NOT_CONFIGURED)
|
275
298
|
return if skipped_by_directive?(node)
|
276
299
|
case node
|
277
300
|
when GraphQL::Language::Nodes::Field
|
278
|
-
if node.name ==
|
301
|
+
if node.name == field_name && (NOT_CONFIGURED.equal?(alias_name) || node.alias == alias_name)
|
279
302
|
if arguments.nil? || arguments.empty?
|
280
303
|
# No constraint applied
|
281
304
|
matches << node
|
@@ -284,10 +307,10 @@ module GraphQL
|
|
284
307
|
end
|
285
308
|
end
|
286
309
|
when GraphQL::Language::Nodes::InlineFragment
|
287
|
-
node.selections.each { |s| find_selected_nodes(s, field_defn, arguments: arguments, matches: matches) }
|
310
|
+
node.selections.each { |s| find_selected_nodes(s, field_name, field_defn, arguments: arguments, matches: matches, alias_name: alias_name) }
|
288
311
|
when GraphQL::Language::Nodes::FragmentSpread
|
289
|
-
frag_defn =
|
290
|
-
frag_defn.selections.each { |s| find_selected_nodes(s, field_defn, arguments: arguments, matches: matches) }
|
312
|
+
frag_defn = lookup_fragment(node)
|
313
|
+
frag_defn.selections.each { |s| find_selected_nodes(s, field_name, field_defn, arguments: arguments, matches: matches, alias_name: alias_name) }
|
291
314
|
else
|
292
315
|
raise "Unexpected selection comparison on #{node.class.name} (#{node})"
|
293
316
|
end
|
@@ -306,6 +329,50 @@ module GraphQL
|
|
306
329
|
query_kwargs.key?(arg_name_sym) && query_kwargs[arg_name_sym] == arg_value
|
307
330
|
end
|
308
331
|
end
|
332
|
+
|
333
|
+
def lookahead_for_selection(field_defn, selected_type, arguments, alias_name = NOT_CONFIGURED)
|
334
|
+
return NULL_LOOKAHEAD unless field_defn
|
335
|
+
|
336
|
+
next_nodes = []
|
337
|
+
field_name = field_defn.name
|
338
|
+
@ast_nodes.each do |ast_node|
|
339
|
+
ast_node.selections.each do |selection|
|
340
|
+
find_selected_nodes(selection, field_name, field_defn, arguments: arguments, matches: next_nodes, alias_name: alias_name)
|
341
|
+
end
|
342
|
+
end
|
343
|
+
|
344
|
+
return NULL_LOOKAHEAD if next_nodes.empty?
|
345
|
+
|
346
|
+
Lookahead.new(query: @query, ast_nodes: next_nodes, field: field_defn, owner_type: selected_type)
|
347
|
+
end
|
348
|
+
|
349
|
+
def alias_selections
|
350
|
+
return @alias_selections if defined?(@alias_selections)
|
351
|
+
@alias_selections ||= {}
|
352
|
+
end
|
353
|
+
|
354
|
+
def lookup_alias_node(nodes, name)
|
355
|
+
return if nodes.empty?
|
356
|
+
|
357
|
+
nodes.flat_map(&:children)
|
358
|
+
.flat_map { |child| unwrap_fragments(child) }
|
359
|
+
.find { |child| child.is_a?(GraphQL::Language::Nodes::Field) && child.alias == name }
|
360
|
+
end
|
361
|
+
|
362
|
+
def unwrap_fragments(node)
|
363
|
+
case node
|
364
|
+
when GraphQL::Language::Nodes::InlineFragment
|
365
|
+
node.children
|
366
|
+
when GraphQL::Language::Nodes::FragmentSpread
|
367
|
+
lookup_fragment(node).children
|
368
|
+
else
|
369
|
+
[node]
|
370
|
+
end
|
371
|
+
end
|
372
|
+
|
373
|
+
def lookup_fragment(ast_selection)
|
374
|
+
@query.fragments[ast_selection.name] || raise("Invariant: Can't look ahead to nonexistent fragment #{ast_selection.name} (found: #{@query.fragments.keys})")
|
375
|
+
end
|
309
376
|
end
|
310
377
|
end
|
311
378
|
end
|
@@ -12,10 +12,14 @@ module GraphQL
|
|
12
12
|
attr_reader :id
|
13
13
|
# @return [Object] The value found with this ID
|
14
14
|
attr_reader :object
|
15
|
-
|
15
|
+
# @return [GraphQL::Query::Context]
|
16
|
+
attr_reader :context
|
17
|
+
|
18
|
+
def initialize(argument:, id:, object:, context:)
|
16
19
|
@id = id
|
17
20
|
@argument = argument
|
18
21
|
@object = object
|
22
|
+
@context = context
|
19
23
|
super("No object found for `#{argument.graphql_name}: #{id.inspect}`")
|
20
24
|
end
|
21
25
|
end
|
@@ -22,10 +22,11 @@ module GraphQL
|
|
22
22
|
attr_reader :context
|
23
23
|
|
24
24
|
def context=(new_ctx)
|
25
|
-
current_runtime_state = Thread.current[:__graphql_runtime_info]
|
26
|
-
query_runtime_state = current_runtime_state[new_ctx.query]
|
27
|
-
@was_authorized_by_scope_items = query_runtime_state.was_authorized_by_scope_items
|
28
25
|
@context = new_ctx
|
26
|
+
if @was_authorized_by_scope_items.nil?
|
27
|
+
@was_authorized_by_scope_items = detect_was_authorized_by_scope_items
|
28
|
+
end
|
29
|
+
@context
|
29
30
|
end
|
30
31
|
|
31
32
|
# @return [Object] the object this collection belongs to
|
@@ -90,13 +91,7 @@ module GraphQL
|
|
90
91
|
else
|
91
92
|
default_page_size
|
92
93
|
end
|
93
|
-
@was_authorized_by_scope_items =
|
94
|
-
current_runtime_state = Thread.current[:__graphql_runtime_info]
|
95
|
-
query_runtime_state = current_runtime_state[@context.query]
|
96
|
-
query_runtime_state.was_authorized_by_scope_items
|
97
|
-
else
|
98
|
-
nil
|
99
|
-
end
|
94
|
+
@was_authorized_by_scope_items = detect_was_authorized_by_scope_items
|
100
95
|
end
|
101
96
|
|
102
97
|
def was_authorized_by_scope_items?
|
@@ -226,6 +221,16 @@ module GraphQL
|
|
226
221
|
|
227
222
|
private
|
228
223
|
|
224
|
+
def detect_was_authorized_by_scope_items
|
225
|
+
if @context &&
|
226
|
+
(current_runtime_state = Thread.current[:__graphql_runtime_info]) &&
|
227
|
+
(query_runtime_state = current_runtime_state[@context.query])
|
228
|
+
query_runtime_state.was_authorized_by_scope_items
|
229
|
+
else
|
230
|
+
nil
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
229
234
|
# @param argument [nil, Integer] `first` or `last`, as provided by the client
|
230
235
|
# @param max_page_size [nil, Integer]
|
231
236
|
# @return [nil, Integer] `nil` if the input was `nil`, otherwise a value between `0` and `max_page_size`
|
@@ -13,8 +13,7 @@ module GraphQL
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def relation_count(relation)
|
16
|
-
|
17
|
-
relation.to_a.count
|
16
|
+
relation.all.count(relation.options.slice(:limit, :skip))
|
18
17
|
end
|
19
18
|
|
20
19
|
def null_relation(relation)
|
@@ -3,6 +3,8 @@ module GraphQL
|
|
3
3
|
class Query
|
4
4
|
# This object can be `ctx` in places where there is no query
|
5
5
|
class NullContext
|
6
|
+
include Singleton
|
7
|
+
|
6
8
|
class NullQuery
|
7
9
|
def after_lazy(value)
|
8
10
|
yield(value)
|
@@ -27,16 +29,6 @@ module GraphQL
|
|
27
29
|
def interpreter?
|
28
30
|
true
|
29
31
|
end
|
30
|
-
|
31
|
-
class << self
|
32
|
-
extend Forwardable
|
33
|
-
|
34
|
-
def instance
|
35
|
-
@instance ||= self.new
|
36
|
-
end
|
37
|
-
|
38
|
-
def_delegators :instance, :query, :warden, :schema, :interpreter?, :dataloader, :[], :fetch, :dig, :key?
|
39
|
-
end
|
40
32
|
end
|
41
33
|
end
|
42
34
|
end
|
data/lib/graphql/query.rb
CHANGED
@@ -162,6 +162,14 @@ module GraphQL
|
|
162
162
|
|
163
163
|
@result_values = nil
|
164
164
|
@executed = false
|
165
|
+
|
166
|
+
@logger = if context && context[:logger] == false
|
167
|
+
Logger.new(IO::NULL)
|
168
|
+
elsif context && (l = context[:logger])
|
169
|
+
l
|
170
|
+
else
|
171
|
+
schema.default_logger
|
172
|
+
end
|
165
173
|
end
|
166
174
|
|
167
175
|
# If a document was provided to `GraphQL::Schema#execute` instead of the raw query string, we will need to get it from the document
|
@@ -369,6 +377,8 @@ module GraphQL
|
|
369
377
|
end
|
370
378
|
end
|
371
379
|
|
380
|
+
attr_reader :logger
|
381
|
+
|
372
382
|
private
|
373
383
|
|
374
384
|
def find_operation(operations, operation_name)
|
@@ -432,14 +432,12 @@ module GraphQL
|
|
432
432
|
builder = self
|
433
433
|
|
434
434
|
field_definitions.each do |field_definition|
|
435
|
-
type_name = resolve_type_name(field_definition.type)
|
436
435
|
resolve_method_name = -"resolve_field_#{field_definition.name}"
|
437
436
|
schema_field_defn = owner.field(
|
438
437
|
field_definition.name,
|
439
438
|
description: field_definition.description,
|
440
439
|
type: type_resolver.call(field_definition.type),
|
441
440
|
null: true,
|
442
|
-
connection: type_name.end_with?("Connection"),
|
443
441
|
connection_extension: nil,
|
444
442
|
deprecation_reason: build_deprecation_reason(field_definition.directives),
|
445
443
|
ast_node: field_definition,
|
@@ -487,15 +485,6 @@ module GraphQL
|
|
487
485
|
}
|
488
486
|
resolve_type_proc
|
489
487
|
end
|
490
|
-
|
491
|
-
def resolve_type_name(type)
|
492
|
-
case type
|
493
|
-
when GraphQL::Language::Nodes::TypeName
|
494
|
-
return type.name
|
495
|
-
else
|
496
|
-
resolve_type_name(type.of_type)
|
497
|
-
end
|
498
|
-
end
|
499
488
|
end
|
500
489
|
|
501
490
|
private_constant :Builder
|
@@ -6,6 +6,18 @@ module GraphQL
|
|
6
6
|
description "Requires that exactly one field must be supplied and that field must not be `null`."
|
7
7
|
locations(GraphQL::Schema::Directive::INPUT_OBJECT)
|
8
8
|
default_directive true
|
9
|
+
|
10
|
+
def initialize(...)
|
11
|
+
super
|
12
|
+
|
13
|
+
owner.extend(IsOneOf)
|
14
|
+
end
|
15
|
+
|
16
|
+
module IsOneOf
|
17
|
+
def one_of?
|
18
|
+
true
|
19
|
+
end
|
20
|
+
end
|
9
21
|
end
|
10
22
|
end
|
11
23
|
end
|
@@ -119,7 +119,7 @@ module GraphQL
|
|
119
119
|
# - lazy resolution
|
120
120
|
# Probably, those won't be needed here, since these are configuration arguments,
|
121
121
|
# not runtime arguments.
|
122
|
-
@arguments = self.class.coerce_arguments(nil, arguments, Query::NullContext)
|
122
|
+
@arguments = self.class.coerce_arguments(nil, arguments, Query::NullContext.instance)
|
123
123
|
end
|
124
124
|
|
125
125
|
def graphql_name
|
data/lib/graphql/schema/enum.rb
CHANGED
@@ -68,7 +68,7 @@ module GraphQL
|
|
68
68
|
end
|
69
69
|
|
70
70
|
# @return [Array<GraphQL::Schema::EnumValue>] Possible values of this enum
|
71
|
-
def enum_values(context = GraphQL::Query::NullContext)
|
71
|
+
def enum_values(context = GraphQL::Query::NullContext.instance)
|
72
72
|
inherited_values = superclass.respond_to?(:enum_values) ? superclass.enum_values(context) : nil
|
73
73
|
visible_values = []
|
74
74
|
warden = Warden.from_context(context)
|
@@ -110,7 +110,7 @@ module GraphQL
|
|
110
110
|
end
|
111
111
|
|
112
112
|
# @return [Hash<String => GraphQL::Schema::EnumValue>] Possible values of this enum, keyed by name.
|
113
|
-
def values(context = GraphQL::Query::NullContext)
|
113
|
+
def values(context = GraphQL::Query::NullContext.instance)
|
114
114
|
enum_values(context).each_with_object({}) { |val, obj| obj[val.graphql_name] = val }
|
115
115
|
end
|
116
116
|
|
@@ -12,9 +12,10 @@ module GraphQL
|
|
12
12
|
if ret_type.respond_to?(:scope_items)
|
13
13
|
scoped_items = ret_type.scope_items(value, context)
|
14
14
|
if !scoped_items.equal?(value) && !ret_type.reauthorize_scoped_objects
|
15
|
-
current_runtime_state = Thread.current[:__graphql_runtime_info]
|
16
|
-
|
17
|
-
|
15
|
+
if (current_runtime_state = Thread.current[:__graphql_runtime_info]) &&
|
16
|
+
(query_runtime_state = current_runtime_state[context.query])
|
17
|
+
query_runtime_state.was_authorized_by_scope_items = true
|
18
|
+
end
|
18
19
|
end
|
19
20
|
scoped_items
|
20
21
|
else
|
data/lib/graphql/schema/field.rb
CHANGED
@@ -138,7 +138,7 @@ module GraphQL
|
|
138
138
|
# As a last ditch, try to force loading the return type:
|
139
139
|
type.unwrap.name
|
140
140
|
end
|
141
|
-
@connection = return_type_name.end_with?("Connection")
|
141
|
+
@connection = return_type_name.end_with?("Connection") && return_type_name != "Connection"
|
142
142
|
else
|
143
143
|
@connection
|
144
144
|
end
|
@@ -704,7 +704,7 @@ module GraphQL
|
|
704
704
|
inner_object.dig(*@dig_keys)
|
705
705
|
elsif inner_object.key?(@method_sym)
|
706
706
|
inner_object[@method_sym]
|
707
|
-
elsif inner_object.key?(@method_str)
|
707
|
+
elsif inner_object.key?(@method_str) || !inner_object.default_proc.nil?
|
708
708
|
inner_object[@method_str]
|
709
709
|
elsif @fallback_value != NOT_CONFIGURED
|
710
710
|
@fallback_value
|
@@ -54,11 +54,11 @@ module GraphQL
|
|
54
54
|
end
|
55
55
|
end
|
56
56
|
|
57
|
-
def field_arguments(context = GraphQL::Query::NullContext)
|
57
|
+
def field_arguments(context = GraphQL::Query::NullContext.instance)
|
58
58
|
dummy.arguments(context)
|
59
59
|
end
|
60
60
|
|
61
|
-
def get_field_argument(name, context = GraphQL::Query::NullContext)
|
61
|
+
def get_field_argument(name, context = GraphQL::Query::NullContext.instance)
|
62
62
|
dummy.get_argument(name, context)
|
63
63
|
end
|
64
64
|
|