graphql 2.3.4 → 2.3.6
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/generators/graphql/install/mutation_root_generator.rb +2 -2
- data/lib/graphql/analysis/analyzer.rb +89 -0
- data/lib/graphql/analysis/field_usage.rb +82 -0
- data/lib/graphql/analysis/max_query_complexity.rb +20 -0
- data/lib/graphql/analysis/max_query_depth.rb +20 -0
- data/lib/graphql/analysis/query_complexity.rb +183 -0
- data/lib/graphql/analysis/{ast/query_depth.rb → query_depth.rb} +23 -25
- data/lib/graphql/analysis/visitor.rb +282 -0
- data/lib/graphql/analysis.rb +92 -1
- data/lib/graphql/dataloader/async_dataloader.rb +2 -0
- data/lib/graphql/dataloader/null_dataloader.rb +1 -1
- data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +7 -4
- data/lib/graphql/execution/interpreter/runtime.rb +40 -59
- data/lib/graphql/execution/interpreter.rb +2 -2
- data/lib/graphql/language/nodes.rb +17 -22
- data/lib/graphql/language/parser.rb +54 -13
- data/lib/graphql/query/validation_pipeline.rb +2 -2
- data/lib/graphql/query.rb +1 -1
- data/lib/graphql/rubocop/graphql/base_cop.rb +1 -1
- data/lib/graphql/schema/addition.rb +21 -11
- data/lib/graphql/schema/argument.rb +19 -5
- data/lib/graphql/schema/directive.rb +2 -0
- data/lib/graphql/schema/field.rb +8 -0
- data/lib/graphql/schema/has_single_input_argument.rb +1 -0
- data/lib/graphql/schema/input_object.rb +1 -0
- data/lib/graphql/schema/introspection_system.rb +2 -2
- data/lib/graphql/schema/late_bound_type.rb +4 -0
- data/lib/graphql/schema/list.rb +2 -2
- data/lib/graphql/schema/member/has_arguments.rb +2 -35
- data/lib/graphql/schema/member/has_directives.rb +1 -1
- data/lib/graphql/schema/member/relay_shortcuts.rb +1 -1
- data/lib/graphql/schema/member/type_system_helpers.rb +1 -2
- data/lib/graphql/schema/resolver.rb +1 -0
- data/lib/graphql/schema/warden.rb +2 -3
- data/lib/graphql/schema.rb +20 -20
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +1 -1
- data/lib/graphql/subscriptions/action_cable_subscriptions.rb +1 -1
- data/lib/graphql/subscriptions/broadcast_analyzer.rb +1 -1
- data/lib/graphql/subscriptions.rb +1 -1
- data/lib/graphql/type_kinds.rb +1 -1
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +0 -8
- metadata +10 -11
- data/lib/graphql/analysis/ast/analyzer.rb +0 -91
- data/lib/graphql/analysis/ast/field_usage.rb +0 -84
- data/lib/graphql/analysis/ast/max_query_complexity.rb +0 -22
- data/lib/graphql/analysis/ast/max_query_depth.rb +0 -22
- data/lib/graphql/analysis/ast/query_complexity.rb +0 -185
- data/lib/graphql/analysis/ast/visitor.rb +0 -284
- data/lib/graphql/analysis/ast.rb +0 -94
@@ -65,16 +65,6 @@ module GraphQL
|
|
65
65
|
"#<#{self.class.name} response=#{@response.inspect}>"
|
66
66
|
end
|
67
67
|
|
68
|
-
def tap_or_each(obj_or_array)
|
69
|
-
if obj_or_array.is_a?(Array)
|
70
|
-
obj_or_array.each do |item|
|
71
|
-
yield(item, true)
|
72
|
-
end
|
73
|
-
else
|
74
|
-
yield(obj_or_array, false)
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
68
|
# This _begins_ the execution. Some deferred work
|
79
69
|
# might be stored up in lazies.
|
80
70
|
# @return [void]
|
@@ -84,7 +74,8 @@ module GraphQL
|
|
84
74
|
root_type = schema.root_type_for_operation(root_op_type)
|
85
75
|
runtime_object = root_type.wrap(query.root_value, context)
|
86
76
|
runtime_object = schema.sync_lazy(runtime_object)
|
87
|
-
|
77
|
+
is_eager = root_op_type == "mutation"
|
78
|
+
@response = GraphQLResultHash.new(nil, root_type, runtime_object, nil, false, root_operation.selections, is_eager)
|
88
79
|
st = get_current_runtime_state
|
89
80
|
st.current_result = @response
|
90
81
|
|
@@ -93,17 +84,9 @@ module GraphQL
|
|
93
84
|
@response = nil
|
94
85
|
else
|
95
86
|
call_method_on_directives(:resolve, runtime_object, root_operation.directives) do # execute query level directives
|
96
|
-
|
97
|
-
# This is kind of a hack -- `gathered_selections` is an Array if any of the selections
|
98
|
-
# require isolation during execution (because of runtime directives). In that case,
|
99
|
-
# make a new, isolated result hash for writing the result into. (That isolated response
|
100
|
-
# is eventually merged back into the main response)
|
101
|
-
#
|
102
|
-
# Otherwise, `gathered_selections` is a hash of selections which can be
|
103
|
-
# directly evaluated and the results can be written right into the main response hash.
|
104
|
-
tap_or_each(gathered_selections) do |selections, is_selection_array|
|
87
|
+
each_gathered_selections(@response) do |selections, is_selection_array|
|
105
88
|
if is_selection_array
|
106
|
-
selection_response = GraphQLResultHash.new(nil, root_type, runtime_object, nil, false)
|
89
|
+
selection_response = GraphQLResultHash.new(nil, root_type, runtime_object, nil, false, selections, is_eager)
|
107
90
|
final_response = @response
|
108
91
|
else
|
109
92
|
selection_response = @response
|
@@ -112,12 +95,10 @@ module GraphQL
|
|
112
95
|
|
113
96
|
@dataloader.append_job {
|
114
97
|
evaluate_selections(
|
115
|
-
root_op_type == "mutation",
|
116
98
|
selections,
|
117
99
|
selection_response,
|
118
100
|
final_response,
|
119
101
|
nil,
|
120
|
-
nil,
|
121
102
|
)
|
122
103
|
}
|
123
104
|
end
|
@@ -126,6 +107,17 @@ module GraphQL
|
|
126
107
|
nil
|
127
108
|
end
|
128
109
|
|
110
|
+
def each_gathered_selections(response_hash)
|
111
|
+
gathered_selections = gather_selections(response_hash.graphql_application_value, response_hash.graphql_result_type, response_hash.graphql_selections)
|
112
|
+
if gathered_selections.is_a?(Array)
|
113
|
+
gathered_selections.each do |item|
|
114
|
+
yield(item, true)
|
115
|
+
end
|
116
|
+
else
|
117
|
+
yield(gathered_selections, false)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
129
121
|
def gather_selections(owner_object, owner_type, selections, selections_to_run = nil, selections_by_name = {})
|
130
122
|
selections.each do |node|
|
131
123
|
# Skip gathering this if the directive says so
|
@@ -138,7 +130,7 @@ module GraphQL
|
|
138
130
|
selections = selections_by_name[response_key]
|
139
131
|
# if there was already a selection of this field,
|
140
132
|
# use an array to hold all selections,
|
141
|
-
#
|
133
|
+
# otherwise, use the single node to represent the selection
|
142
134
|
if selections
|
143
135
|
# This field was already selected at least once,
|
144
136
|
# add this node to the list of selections
|
@@ -203,7 +195,7 @@ module GraphQL
|
|
203
195
|
NO_ARGS = GraphQL::EmptyObjects::EMPTY_HASH
|
204
196
|
|
205
197
|
# @return [void]
|
206
|
-
def evaluate_selections(
|
198
|
+
def evaluate_selections(gathered_selections, selections_result, target_result, runtime_state) # rubocop:disable Metrics/ParameterLists
|
207
199
|
runtime_state ||= get_current_runtime_state
|
208
200
|
runtime_state.current_result_name = nil
|
209
201
|
runtime_state.current_result = selections_result
|
@@ -218,7 +210,7 @@ module GraphQL
|
|
218
210
|
gathered_selections.each do |result_name, field_ast_nodes_or_ast_node|
|
219
211
|
@dataloader.append_job {
|
220
212
|
evaluate_selection(
|
221
|
-
result_name, field_ast_nodes_or_ast_node,
|
213
|
+
result_name, field_ast_nodes_or_ast_node, selections_result
|
222
214
|
)
|
223
215
|
finished_jobs += 1
|
224
216
|
if target_result && finished_jobs == enqueued_jobs
|
@@ -228,7 +220,7 @@ module GraphQL
|
|
228
220
|
# Field resolution may pause the fiber,
|
229
221
|
# so it wouldn't get to the `Resolve` call that happens below.
|
230
222
|
# So instead trigger a run from this outer context.
|
231
|
-
if
|
223
|
+
if selections_result.graphql_is_eager
|
232
224
|
@dataloader.clear_cache
|
233
225
|
@dataloader.run
|
234
226
|
@dataloader.clear_cache
|
@@ -239,7 +231,7 @@ module GraphQL
|
|
239
231
|
end
|
240
232
|
|
241
233
|
# @return [void]
|
242
|
-
def evaluate_selection(result_name, field_ast_nodes_or_ast_node,
|
234
|
+
def evaluate_selection(result_name, field_ast_nodes_or_ast_node, selections_result) # rubocop:disable Metrics/ParameterLists
|
243
235
|
return if selections_result.graphql_dead
|
244
236
|
# As a performance optimization, the hash key will be a `Node` if
|
245
237
|
# there's only one selection of the field. But if there are multiple
|
@@ -266,28 +258,27 @@ module GraphQL
|
|
266
258
|
owner_object = field_defn.owner.wrap(owner_object, context)
|
267
259
|
end
|
268
260
|
|
269
|
-
return_type = field_defn.type
|
270
261
|
if !field_defn.any_arguments?
|
271
262
|
resolved_arguments = GraphQL::Execution::Interpreter::Arguments::EMPTY
|
272
263
|
if field_defn.extras.size == 0
|
273
264
|
evaluate_selection_with_resolved_keyword_args(
|
274
|
-
NO_ARGS, resolved_arguments, field_defn, ast_node, field_ast_nodes, owner_object,
|
265
|
+
NO_ARGS, resolved_arguments, field_defn, ast_node, field_ast_nodes, owner_object, result_name, selections_result, runtime_state
|
275
266
|
)
|
276
267
|
else
|
277
|
-
evaluate_selection_with_args(resolved_arguments, field_defn, ast_node, field_ast_nodes, owner_object,
|
268
|
+
evaluate_selection_with_args(resolved_arguments, field_defn, ast_node, field_ast_nodes, owner_object, result_name, selections_result, runtime_state)
|
278
269
|
end
|
279
270
|
else
|
280
271
|
@query.arguments_cache.dataload_for(ast_node, field_defn, owner_object) do |resolved_arguments|
|
281
272
|
runtime_state = get_current_runtime_state # This might be in a different fiber
|
282
|
-
evaluate_selection_with_args(resolved_arguments, field_defn, ast_node, field_ast_nodes, owner_object,
|
273
|
+
evaluate_selection_with_args(resolved_arguments, field_defn, ast_node, field_ast_nodes, owner_object, result_name, selections_result, runtime_state)
|
283
274
|
end
|
284
275
|
end
|
285
276
|
end
|
286
277
|
|
287
|
-
def evaluate_selection_with_args(arguments, field_defn, ast_node, field_ast_nodes, object,
|
278
|
+
def evaluate_selection_with_args(arguments, field_defn, ast_node, field_ast_nodes, object, result_name, selection_result, runtime_state) # rubocop:disable Metrics/ParameterLists
|
288
279
|
after_lazy(arguments, field: field_defn, ast_node: ast_node, owner_object: object, arguments: arguments, result_name: result_name, result: selection_result, runtime_state: runtime_state) do |resolved_arguments, runtime_state|
|
289
|
-
return_type_non_null = return_type.non_null?
|
290
280
|
if resolved_arguments.is_a?(GraphQL::ExecutionError) || resolved_arguments.is_a?(GraphQL::UnauthorizedError)
|
281
|
+
return_type_non_null = field_defn.type.non_null?
|
291
282
|
continue_value(resolved_arguments, field_defn, return_type_non_null, ast_node, result_name, selection_result)
|
292
283
|
next
|
293
284
|
end
|
@@ -326,7 +317,8 @@ module GraphQL
|
|
326
317
|
# to the keyword args hash _before_ freezing everything.
|
327
318
|
extra_args[:argument_details] = :__arguments_add_self
|
328
319
|
when :parent
|
329
|
-
|
320
|
+
parent_result = selection_result.graphql_parent
|
321
|
+
extra_args[:parent] = parent_result&.graphql_application_value&.object
|
330
322
|
else
|
331
323
|
extra_args[extra] = field_defn.fetch_extra(extra, context)
|
332
324
|
end
|
@@ -337,11 +329,11 @@ module GraphQL
|
|
337
329
|
resolved_arguments.keyword_arguments
|
338
330
|
end
|
339
331
|
|
340
|
-
evaluate_selection_with_resolved_keyword_args(kwarg_arguments, resolved_arguments, field_defn, ast_node, field_ast_nodes, object,
|
332
|
+
evaluate_selection_with_resolved_keyword_args(kwarg_arguments, resolved_arguments, field_defn, ast_node, field_ast_nodes, object, result_name, selection_result, runtime_state)
|
341
333
|
end
|
342
334
|
end
|
343
335
|
|
344
|
-
def evaluate_selection_with_resolved_keyword_args(kwarg_arguments, resolved_arguments, field_defn, ast_node, field_ast_nodes, object,
|
336
|
+
def evaluate_selection_with_resolved_keyword_args(kwarg_arguments, resolved_arguments, field_defn, ast_node, field_ast_nodes, object, result_name, selection_result, runtime_state) # rubocop:disable Metrics/ParameterLists
|
345
337
|
runtime_state.current_field = field_defn
|
346
338
|
runtime_state.current_arguments = resolved_arguments
|
347
339
|
runtime_state.current_result_name = result_name
|
@@ -384,7 +376,8 @@ module GraphQL
|
|
384
376
|
end
|
385
377
|
after_lazy(app_result, field: field_defn, ast_node: ast_node, owner_object: object, arguments: resolved_arguments, result_name: result_name, result: selection_result, runtime_state: runtime_state) do |inner_result, runtime_state|
|
386
378
|
owner_type = selection_result.graphql_result_type
|
387
|
-
|
379
|
+
return_type = field_defn.type
|
380
|
+
continue_value = continue_value(inner_result, field_defn, return_type.non_null?, ast_node, result_name, selection_result)
|
388
381
|
if HALT != continue_value
|
389
382
|
was_scoped = runtime_state.was_authorized_by_scope_items
|
390
383
|
runtime_state.was_authorized_by_scope_items = nil
|
@@ -395,7 +388,7 @@ module GraphQL
|
|
395
388
|
# If this field is a root mutation field, immediately resolve
|
396
389
|
# all of its child fields before moving on to the next root mutation field.
|
397
390
|
# (Subselections of this mutation will still be resolved level-by-level.)
|
398
|
-
if
|
391
|
+
if selection_result.graphql_is_eager
|
399
392
|
Interpreter::Resolve.resolve_all([field_result], @dataloader)
|
400
393
|
end
|
401
394
|
end
|
@@ -607,21 +600,11 @@ module GraphQL
|
|
607
600
|
after_lazy(object_proxy, ast_node: ast_node, field: field, owner_object: owner_object, arguments: arguments, trace: false, result_name: result_name, result: selection_result, runtime_state: runtime_state) do |inner_object, runtime_state|
|
608
601
|
continue_value = continue_value(inner_object, field, is_non_null, ast_node, result_name, selection_result)
|
609
602
|
if HALT != continue_value
|
610
|
-
response_hash = GraphQLResultHash.new(result_name, current_type, continue_value, selection_result, is_non_null)
|
603
|
+
response_hash = GraphQLResultHash.new(result_name, current_type, continue_value, selection_result, is_non_null, next_selections, false)
|
611
604
|
set_result(selection_result, result_name, response_hash, true, is_non_null)
|
612
|
-
|
613
|
-
gathered_selections = gather_selections(continue_value, current_type, next_selections)
|
614
|
-
# There are two possibilities for `gathered_selections`:
|
615
|
-
# 1. All selections of this object should be evaluated together (there are no runtime directives modifying execution).
|
616
|
-
# This case is handled below, and the result can be written right into the main `response_hash` above.
|
617
|
-
# In this case, `gathered_selections` is a hash of selections.
|
618
|
-
# 2. Some selections of this object have runtime directives that may or may not modify execution.
|
619
|
-
# That part of the selection is evaluated in an isolated way, writing into a sub-response object which is
|
620
|
-
# eventually merged into the final response. In this case, `gathered_selections` is an array of things to run in isolation.
|
621
|
-
# (Technically, it's possible that one of those entries _doesn't_ require isolation.)
|
622
|
-
tap_or_each(gathered_selections) do |selections, is_selection_array|
|
605
|
+
each_gathered_selections(response_hash) do |selections, is_selection_array|
|
623
606
|
if is_selection_array
|
624
|
-
this_result = GraphQLResultHash.new(result_name, current_type, continue_value, selection_result, is_non_null)
|
607
|
+
this_result = GraphQLResultHash.new(result_name, current_type, continue_value, selection_result, is_non_null, selections, false)
|
625
608
|
final_result = response_hash
|
626
609
|
else
|
627
610
|
this_result = response_hash
|
@@ -629,11 +612,9 @@ module GraphQL
|
|
629
612
|
end
|
630
613
|
|
631
614
|
evaluate_selections(
|
632
|
-
false,
|
633
615
|
selections,
|
634
616
|
this_result,
|
635
617
|
final_result,
|
636
|
-
owner_object.object,
|
637
618
|
runtime_state,
|
638
619
|
)
|
639
620
|
end
|
@@ -644,7 +625,7 @@ module GraphQL
|
|
644
625
|
# This is true for objects, unions, and interfaces
|
645
626
|
use_dataloader_job = !inner_type.unwrap.kind.input?
|
646
627
|
inner_type_non_null = inner_type.non_null?
|
647
|
-
response_list = GraphQLResultArray.new(result_name, current_type,
|
628
|
+
response_list = GraphQLResultArray.new(result_name, current_type, owner_object, selection_result, is_non_null, next_selections, false)
|
648
629
|
set_result(selection_result, result_name, response_list, true, is_non_null)
|
649
630
|
idx = nil
|
650
631
|
list_value = begin
|
@@ -654,10 +635,10 @@ module GraphQL
|
|
654
635
|
idx += 1
|
655
636
|
if use_dataloader_job
|
656
637
|
@dataloader.append_job do
|
657
|
-
resolve_list_item(inner_value, inner_type, inner_type_non_null, ast_node, field, owner_object, arguments, this_idx, response_list,
|
638
|
+
resolve_list_item(inner_value, inner_type, inner_type_non_null, ast_node, field, owner_object, arguments, this_idx, response_list, owner_type, was_scoped, runtime_state)
|
658
639
|
end
|
659
640
|
else
|
660
|
-
resolve_list_item(inner_value, inner_type, inner_type_non_null, ast_node, field, owner_object, arguments, this_idx, response_list,
|
641
|
+
resolve_list_item(inner_value, inner_type, inner_type_non_null, ast_node, field, owner_object, arguments, this_idx, response_list, owner_type, was_scoped, runtime_state)
|
661
642
|
end
|
662
643
|
end
|
663
644
|
|
@@ -688,7 +669,7 @@ module GraphQL
|
|
688
669
|
end
|
689
670
|
end
|
690
671
|
|
691
|
-
def resolve_list_item(inner_value, inner_type, inner_type_non_null, ast_node, field, owner_object, arguments, this_idx, response_list,
|
672
|
+
def resolve_list_item(inner_value, inner_type, inner_type_non_null, ast_node, field, owner_object, arguments, this_idx, response_list, owner_type, was_scoped, runtime_state) # rubocop:disable Metrics/ParameterLists
|
692
673
|
runtime_state.current_result_name = this_idx
|
693
674
|
runtime_state.current_result = response_list
|
694
675
|
call_method_on_directives(:resolve_each, owner_object, ast_node.directives) do
|
@@ -696,7 +677,7 @@ module GraphQL
|
|
696
677
|
after_lazy(inner_value, ast_node: ast_node, field: field, owner_object: owner_object, arguments: arguments, result_name: this_idx, result: response_list, runtime_state: runtime_state) do |inner_inner_value, runtime_state|
|
697
678
|
continue_value = continue_value(inner_inner_value, field, inner_type_non_null, ast_node, this_idx, response_list)
|
698
679
|
if HALT != continue_value
|
699
|
-
continue_field(continue_value, owner_type, field, inner_type, ast_node,
|
680
|
+
continue_field(continue_value, owner_type, field, inner_type, ast_node, response_list.graphql_selections, false, owner_object, arguments, this_idx, response_list, was_scoped, runtime_state)
|
700
681
|
end
|
701
682
|
end
|
702
683
|
end
|
@@ -20,7 +20,7 @@ module GraphQL
|
|
20
20
|
# @param queries [Array<GraphQL::Query, Hash>]
|
21
21
|
# @param context [Hash]
|
22
22
|
# @param max_complexity [Integer, nil]
|
23
|
-
# @return [Array<
|
23
|
+
# @return [Array<GraphQL::Query::Result>] One result per query
|
24
24
|
def run_all(schema, query_options, context: {}, max_complexity: schema.max_complexity)
|
25
25
|
queries = query_options.map do |opts|
|
26
26
|
case opts
|
@@ -40,7 +40,7 @@ module GraphQL
|
|
40
40
|
lazies_at_depth = Hash.new { |h, k| h[k] = [] }
|
41
41
|
multiplex_analyzers = schema.multiplex_analyzers
|
42
42
|
if multiplex.max_complexity
|
43
|
-
multiplex_analyzers += [GraphQL::Analysis::
|
43
|
+
multiplex_analyzers += [GraphQL::Analysis::MaxQueryComplexity]
|
44
44
|
end
|
45
45
|
|
46
46
|
schema.analysis_engine.analyze_multiplex(multiplex, multiplex_analyzers)
|
@@ -274,6 +274,8 @@ module GraphQL
|
|
274
274
|
]
|
275
275
|
|
276
276
|
def generate_initialize
|
277
|
+
return if method_defined?(:marshal_load, false) # checking for `:initialize` doesn't work right
|
278
|
+
|
277
279
|
scalar_method_names = @scalar_methods
|
278
280
|
# TODO: These probably should be scalar methods, but `types` returns an array
|
279
281
|
[:types, :description].each do |extra_method|
|
@@ -392,16 +394,6 @@ module GraphQL
|
|
392
394
|
|
393
395
|
# A single selection in a GraphQL query.
|
394
396
|
class Field < AbstractNode
|
395
|
-
scalar_methods :name, :alias
|
396
|
-
children_methods({
|
397
|
-
arguments: GraphQL::Language::Nodes::Argument,
|
398
|
-
selections: GraphQL::Language::Nodes::Field,
|
399
|
-
directives: GraphQL::Language::Nodes::Directive,
|
400
|
-
})
|
401
|
-
|
402
|
-
# @!attribute selections
|
403
|
-
# @return [Array<Nodes::Field>] Selections on this object (or empty array if this is a scalar field)
|
404
|
-
|
405
397
|
def initialize(name: nil, arguments: NONE, directives: NONE, selections: NONE, field_alias: nil, line: nil, col: nil, pos: nil, filename: nil, source: nil)
|
406
398
|
@name = name
|
407
399
|
@arguments = arguments || NONE
|
@@ -428,24 +420,19 @@ module GraphQL
|
|
428
420
|
@line, @col, @filename, @name, @arguments, @directives, @selections, @alias = values
|
429
421
|
end
|
430
422
|
|
431
|
-
|
432
|
-
self.children_method_name = :selections
|
433
|
-
end
|
434
|
-
|
435
|
-
# A reusable fragment, defined at document-level.
|
436
|
-
class FragmentDefinition < AbstractNode
|
437
|
-
scalar_methods :name, :type
|
423
|
+
scalar_methods :name, :alias
|
438
424
|
children_methods({
|
425
|
+
arguments: GraphQL::Language::Nodes::Argument,
|
439
426
|
selections: GraphQL::Language::Nodes::Field,
|
440
427
|
directives: GraphQL::Language::Nodes::Directive,
|
441
428
|
})
|
442
429
|
|
443
|
-
|
444
|
-
|
445
|
-
|
430
|
+
# Override this because default is `:fields`
|
431
|
+
self.children_method_name = :selections
|
432
|
+
end
|
446
433
|
|
447
|
-
|
448
|
-
|
434
|
+
# A reusable fragment, defined at document-level.
|
435
|
+
class FragmentDefinition < AbstractNode
|
449
436
|
def initialize(name: nil, type: nil, directives: NONE, selections: NONE, filename: nil, pos: nil, source: nil, line: nil, col: nil)
|
450
437
|
@name = name
|
451
438
|
@type = type
|
@@ -469,6 +456,14 @@ module GraphQL
|
|
469
456
|
def marshal_load(values)
|
470
457
|
@line, @col, @filename, @name, @type, @directives, @selections = values
|
471
458
|
end
|
459
|
+
|
460
|
+
scalar_methods :name, :type
|
461
|
+
children_methods({
|
462
|
+
selections: GraphQL::Language::Nodes::Field,
|
463
|
+
directives: GraphQL::Language::Nodes::Directive,
|
464
|
+
})
|
465
|
+
|
466
|
+
self.children_method_name = :definitions
|
472
467
|
end
|
473
468
|
|
474
469
|
# Application of a named fragment in a selection
|
@@ -379,7 +379,12 @@ module GraphQL
|
|
379
379
|
v_loc = pos
|
380
380
|
description = if at?(:STRING); string_value; end
|
381
381
|
defn_loc = pos
|
382
|
-
|
382
|
+
# Any identifier, but not true, false, or null
|
383
|
+
enum_value = if at?(:TRUE) || at?(:FALSE) || at?(:NULL)
|
384
|
+
expect_token(:IDENTIFIER)
|
385
|
+
else
|
386
|
+
parse_name
|
387
|
+
end
|
383
388
|
v_directives = parse_directives
|
384
389
|
list << EnumValueDefinition.new(pos: v_loc, definition_pos: defn_loc, description: description, name: enum_value, directives: v_directives, filename: @filename, source: self)
|
385
390
|
end
|
@@ -615,9 +620,6 @@ module GraphQL
|
|
615
620
|
when :ON
|
616
621
|
advance_token
|
617
622
|
"on"
|
618
|
-
when :DIRECTIVE
|
619
|
-
advance_token
|
620
|
-
"directive"
|
621
623
|
when :EXTEND
|
622
624
|
advance_token
|
623
625
|
"extend"
|
@@ -634,15 +636,6 @@ module GraphQL
|
|
634
636
|
end
|
635
637
|
end
|
636
638
|
|
637
|
-
# Any identifier, but not true, false, or null
|
638
|
-
def parse_enum_name
|
639
|
-
if at?(:TRUE) || at?(:FALSE) || at?(:NULL)
|
640
|
-
expect_token(:IDENTIFIER)
|
641
|
-
else
|
642
|
-
parse_name
|
643
|
-
end
|
644
|
-
end
|
645
|
-
|
646
639
|
def parse_type_name
|
647
640
|
TypeName.new(pos: pos, name: parse_name, filename: @filename, source: self)
|
648
641
|
end
|
@@ -733,6 +726,54 @@ module GraphQL
|
|
733
726
|
loc = pos
|
734
727
|
advance_token
|
735
728
|
VariableIdentifier.new(pos: loc, name: parse_name, filename: @filename, source: self)
|
729
|
+
when :SCHEMA
|
730
|
+
advance_token
|
731
|
+
Nodes::Enum.new(pos: pos, name: "schema", filename: @filename, source: self)
|
732
|
+
when :SCALAR
|
733
|
+
advance_token
|
734
|
+
Nodes::Enum.new(pos: pos, name: "scalar", filename: @filename, source: self)
|
735
|
+
when :IMPLEMENTS
|
736
|
+
advance_token
|
737
|
+
Nodes::Enum.new(pos: pos, name: "implements", filename: @filename, source: self)
|
738
|
+
when :INTERFACE
|
739
|
+
advance_token
|
740
|
+
Nodes::Enum.new(pos: pos, name: "interface", filename: @filename, source: self)
|
741
|
+
when :UNION
|
742
|
+
advance_token
|
743
|
+
Nodes::Enum.new(pos: pos, name: "union", filename: @filename, source: self)
|
744
|
+
when :ENUM
|
745
|
+
advance_token
|
746
|
+
Nodes::Enum.new(pos: pos, name: "enum", filename: @filename, source: self)
|
747
|
+
when :INPUT
|
748
|
+
advance_token
|
749
|
+
Nodes::Enum.new(pos: pos, name: "input", filename: @filename, source: self)
|
750
|
+
when :DIRECTIVE
|
751
|
+
advance_token
|
752
|
+
Nodes::Enum.new(pos: pos, name: "directive", filename: @filename, source: self)
|
753
|
+
when :TYPE
|
754
|
+
advance_token
|
755
|
+
Nodes::Enum.new(pos: pos, name: "type", filename: @filename, source: self)
|
756
|
+
when :QUERY
|
757
|
+
advance_token
|
758
|
+
Nodes::Enum.new(pos: pos, name: "query", filename: @filename, source: self)
|
759
|
+
when :MUTATION
|
760
|
+
advance_token
|
761
|
+
Nodes::Enum.new(pos: pos, name: "mutation", filename: @filename, source: self)
|
762
|
+
when :SUBSCRIPTION
|
763
|
+
advance_token
|
764
|
+
Nodes::Enum.new(pos: pos, name: "subscription", filename: @filename, source: self)
|
765
|
+
when :FRAGMENT
|
766
|
+
advance_token
|
767
|
+
Nodes::Enum.new(pos: pos, name: "fragment", filename: @filename, source: self)
|
768
|
+
when :REPEATABLE
|
769
|
+
advance_token
|
770
|
+
Nodes::Enum.new(pos: pos, name: "repeatable", filename: @filename, source: self)
|
771
|
+
when :ON
|
772
|
+
advance_token
|
773
|
+
Nodes::Enum.new(pos: pos, name: "on", filename: @filename, source: self)
|
774
|
+
when :EXTEND
|
775
|
+
advance_token
|
776
|
+
Nodes::Enum.new(pos: pos, name: "extend", filename: @filename, source: self)
|
736
777
|
else
|
737
778
|
expect_token(:VALUE)
|
738
779
|
end
|
@@ -100,10 +100,10 @@ module GraphQL
|
|
100
100
|
# Depending on the analysis engine, we must use different analyzers
|
101
101
|
# remove this once everything has switched over to AST analyzers
|
102
102
|
if max_depth
|
103
|
-
qa << GraphQL::Analysis::
|
103
|
+
qa << GraphQL::Analysis::MaxQueryDepth
|
104
104
|
end
|
105
105
|
if max_complexity
|
106
|
-
qa << GraphQL::Analysis::
|
106
|
+
qa << GraphQL::Analysis::MaxQueryComplexity
|
107
107
|
end
|
108
108
|
qa
|
109
109
|
else
|
data/lib/graphql/query.rb
CHANGED
@@ -222,7 +222,7 @@ module GraphQL
|
|
222
222
|
end
|
223
223
|
|
224
224
|
# Get the result for this query, executing it once
|
225
|
-
# @return [
|
225
|
+
# @return [GraphQL::Query::Result] A Hash-like GraphQL response, with `"data"` and/or `"errors"` keys
|
226
226
|
def result
|
227
227
|
if !@executed
|
228
228
|
Execution::Interpreter.run_all(@schema, [self], context: @context)
|
@@ -9,7 +9,7 @@ module GraphQL
|
|
9
9
|
|
10
10
|
# Return the source of `send_node`, but without the keyword argument represented by `pair_node`
|
11
11
|
def source_without_keyword_argument(send_node, pair_node)
|
12
|
-
# work back to the
|
12
|
+
# work back to the preceding comma
|
13
13
|
first_pos = pair_node.location.expression.begin_pos
|
14
14
|
end_pos = pair_node.location.expression.end_pos
|
15
15
|
node_source = send_node.source_range.source
|
@@ -12,7 +12,7 @@ module GraphQL
|
|
12
12
|
@possible_types = {}
|
13
13
|
@types = {}
|
14
14
|
@union_memberships = {}
|
15
|
-
@references = Hash.new { |h, k| h[k] =
|
15
|
+
@references = Hash.new { |h, k| h[k] = Set.new }
|
16
16
|
@arguments_with_default_values = []
|
17
17
|
add_type_and_traverse(new_types)
|
18
18
|
end
|
@@ -20,7 +20,7 @@ module GraphQL
|
|
20
20
|
private
|
21
21
|
|
22
22
|
def references_to(thing, from:)
|
23
|
-
@references[thing]
|
23
|
+
@references[thing].add(from)
|
24
24
|
end
|
25
25
|
|
26
26
|
def get_type(name)
|
@@ -95,7 +95,7 @@ module GraphQL
|
|
95
95
|
# It's a union with possible_types
|
96
96
|
# Replace the item by class name
|
97
97
|
owner.assign_type_membership_object_type(type)
|
98
|
-
@possible_types[owner
|
98
|
+
@possible_types[owner] = owner.possible_types
|
99
99
|
elsif type.kind.interface? && (owner.kind.object? || owner.kind.interface?)
|
100
100
|
new_interfaces = []
|
101
101
|
owner.interfaces.each do |int_t|
|
@@ -110,7 +110,7 @@ module GraphQL
|
|
110
110
|
end
|
111
111
|
owner.implements(*new_interfaces)
|
112
112
|
new_interfaces.each do |int|
|
113
|
-
pt = @possible_types[int
|
113
|
+
pt = @possible_types[int] ||= []
|
114
114
|
if !pt.include?(owner) && owner.is_a?(Class)
|
115
115
|
pt << owner
|
116
116
|
end
|
@@ -126,6 +126,7 @@ module GraphQL
|
|
126
126
|
@types[type.graphql_name] = type
|
127
127
|
when GraphQL::Schema::Field, GraphQL::Schema::Argument
|
128
128
|
orig_type = owner.type
|
129
|
+
unwrapped_t = type
|
129
130
|
# Apply list/non-null wrapper as needed
|
130
131
|
if orig_type.respond_to?(:of_type)
|
131
132
|
transforms = []
|
@@ -142,6 +143,7 @@ module GraphQL
|
|
142
143
|
transforms.reverse_each { |t| type = type.public_send(t) }
|
143
144
|
end
|
144
145
|
owner.type = type
|
146
|
+
references_to(unwrapped_t, from: owner)
|
145
147
|
else
|
146
148
|
raise "Unexpected update: #{owner.inspect} #{type.inspect}"
|
147
149
|
end
|
@@ -164,7 +166,9 @@ module GraphQL
|
|
164
166
|
@directives << type
|
165
167
|
type.all_argument_definitions.each do |arg|
|
166
168
|
arg_type = arg.type.unwrap
|
167
|
-
|
169
|
+
if !arg_type.is_a?(GraphQL::Schema::LateBoundType)
|
170
|
+
references_to(arg_type, from: arg)
|
171
|
+
end
|
168
172
|
path.push(arg.graphql_name)
|
169
173
|
add_type(arg_type, owner: arg, late_types: late_types, path: path)
|
170
174
|
path.pop
|
@@ -187,14 +191,18 @@ module GraphQL
|
|
187
191
|
type.all_field_definitions.each do |field|
|
188
192
|
name = field.graphql_name
|
189
193
|
field_type = field.type.unwrap
|
190
|
-
|
194
|
+
if !field_type.is_a?(GraphQL::Schema::LateBoundType)
|
195
|
+
references_to(field_type, from: field)
|
196
|
+
end
|
191
197
|
path.push(name)
|
192
198
|
add_type(field_type, owner: field, late_types: late_types, path: path)
|
193
199
|
add_directives_from(field)
|
194
200
|
field.all_argument_definitions.each do |arg|
|
195
201
|
add_directives_from(arg)
|
196
202
|
arg_type = arg.type.unwrap
|
197
|
-
|
203
|
+
if !arg_type.is_a?(GraphQL::Schema::LateBoundType)
|
204
|
+
references_to(arg_type, from: arg)
|
205
|
+
end
|
198
206
|
path.push(arg.graphql_name)
|
199
207
|
add_type(arg_type, owner: arg, late_types: late_types, path: path)
|
200
208
|
path.pop
|
@@ -209,7 +217,9 @@ module GraphQL
|
|
209
217
|
type.all_argument_definitions.each do |arg|
|
210
218
|
add_directives_from(arg)
|
211
219
|
arg_type = arg.type.unwrap
|
212
|
-
|
220
|
+
if !arg_type.is_a?(GraphQL::Schema::LateBoundType)
|
221
|
+
references_to(arg_type, from: arg)
|
222
|
+
end
|
213
223
|
path.push(arg.graphql_name)
|
214
224
|
add_type(arg_type, owner: arg, late_types: late_types, path: path)
|
215
225
|
path.pop
|
@@ -219,7 +229,7 @@ module GraphQL
|
|
219
229
|
end
|
220
230
|
end
|
221
231
|
if type.kind.union?
|
222
|
-
@possible_types[type
|
232
|
+
@possible_types[type] = type.all_possible_types
|
223
233
|
path.push("possible_types")
|
224
234
|
type.all_possible_types.each do |t|
|
225
235
|
add_type(t, owner: type, late_types: late_types, path: path)
|
@@ -234,7 +244,7 @@ module GraphQL
|
|
234
244
|
path.pop
|
235
245
|
end
|
236
246
|
if type.kind.object?
|
237
|
-
possible_types_for_this_name = @possible_types[type
|
247
|
+
possible_types_for_this_name = @possible_types[type] ||= []
|
238
248
|
possible_types_for_this_name << type
|
239
249
|
end
|
240
250
|
|
@@ -246,7 +256,7 @@ module GraphQL
|
|
246
256
|
interface_type = interface_type_membership.abstract_type
|
247
257
|
# We can get these now; we'll have to get late-bound types later
|
248
258
|
if interface_type.is_a?(Module) && type.is_a?(Class)
|
249
|
-
implementers = @possible_types[interface_type
|
259
|
+
implementers = @possible_types[interface_type] ||= []
|
250
260
|
implementers << type
|
251
261
|
end
|
252
262
|
when String, Schema::LateBoundType
|
@@ -312,10 +312,15 @@ module GraphQL
|
|
312
312
|
context.query.after_lazy(custom_loaded_value) do |custom_value|
|
313
313
|
if loads
|
314
314
|
if type.list?
|
315
|
-
loaded_values =
|
316
|
-
|
317
|
-
|
318
|
-
|
315
|
+
loaded_values = []
|
316
|
+
context.dataloader.run_isolated do
|
317
|
+
custom_value.each_with_index.map { |custom_val, idx|
|
318
|
+
id = coerced_value[idx]
|
319
|
+
context.dataloader.append_job do
|
320
|
+
loaded_values[idx] = load_method_owner.authorize_application_object(self, id, context, custom_val)
|
321
|
+
end
|
322
|
+
}
|
323
|
+
end
|
319
324
|
context.schema.after_any_lazies(loaded_values, &:itself)
|
320
325
|
else
|
321
326
|
load_method_owner.authorize_application_object(self, coerced_value, context, custom_loaded_value)
|
@@ -326,7 +331,16 @@ module GraphQL
|
|
326
331
|
end
|
327
332
|
elsif loads
|
328
333
|
if type.list?
|
329
|
-
loaded_values =
|
334
|
+
loaded_values = []
|
335
|
+
# We want to run these list items all together,
|
336
|
+
# but we also need to wait for the result so we can return it :S
|
337
|
+
context.dataloader.run_isolated do
|
338
|
+
coerced_value.each_with_index { |val, idx|
|
339
|
+
context.dataloader.append_job do
|
340
|
+
loaded_values[idx] = load_method_owner.load_and_authorize_application_object(self, val, context)
|
341
|
+
end
|
342
|
+
}
|
343
|
+
end
|
330
344
|
context.schema.after_any_lazies(loaded_values, &:itself)
|
331
345
|
else
|
332
346
|
load_method_owner.load_and_authorize_application_object(self, coerced_value, context)
|
@@ -188,6 +188,8 @@ module GraphQL
|
|
188
188
|
assert_has_location(SCALAR)
|
189
189
|
elsif @owner < GraphQL::Schema
|
190
190
|
assert_has_location(SCHEMA)
|
191
|
+
elsif @owner < GraphQL::Schema::Resolver
|
192
|
+
assert_has_location(FIELD_DEFINITION)
|
191
193
|
else
|
192
194
|
raise "Unexpected directive owner class: #{@owner}"
|
193
195
|
end
|