graphql 2.1.1 → 2.1.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.
Potentially problematic release.
This version of graphql might be problematic. Click here for more details.
- 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
|
|