graphql 2.4.13 → 2.5.11
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/analysis/query_complexity.rb +87 -7
- data/lib/graphql/backtrace/table.rb +37 -14
- data/lib/graphql/current.rb +1 -1
- data/lib/graphql/dashboard/detailed_traces.rb +47 -0
- data/lib/graphql/dashboard/installable.rb +22 -0
- data/lib/graphql/dashboard/limiters.rb +93 -0
- data/lib/graphql/dashboard/operation_store.rb +199 -0
- data/lib/graphql/dashboard/statics/charts.min.css +1 -0
- data/lib/graphql/dashboard/statics/dashboard.css +27 -0
- data/lib/graphql/dashboard/statics/dashboard.js +74 -9
- data/lib/graphql/dashboard/subscriptions.rb +96 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/detailed_traces/traces/index.html.erb +45 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/limiters/limiters/show.html.erb +62 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/not_installed.html.erb +18 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/_form.html.erb +23 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/edit.html.erb +21 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/index.html.erb +69 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/new.html.erb +7 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/index_entries/index.html.erb +39 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/index_entries/show.html.erb +32 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/operations/index.html.erb +81 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/operations/show.html.erb +71 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/subscriptions/show.html.erb +41 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/topics/index.html.erb +55 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/topics/show.html.erb +40 -0
- data/lib/graphql/dashboard/views/layouts/graphql/dashboard/application.html.erb +49 -1
- data/lib/graphql/dashboard.rb +45 -29
- data/lib/graphql/dataloader/active_record_association_source.rb +28 -8
- data/lib/graphql/dataloader/active_record_source.rb +26 -5
- data/lib/graphql/dataloader/null_dataloader.rb +7 -0
- data/lib/graphql/dataloader/source.rb +16 -4
- data/lib/graphql/dig.rb +2 -1
- data/lib/graphql/execution/interpreter/resolve.rb +3 -3
- data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +34 -1
- data/lib/graphql/execution/interpreter/runtime.rb +163 -59
- data/lib/graphql/execution/interpreter.rb +5 -13
- data/lib/graphql/execution/multiplex.rb +6 -1
- data/lib/graphql/invalid_null_error.rb +15 -2
- data/lib/graphql/language/lexer.rb +9 -2
- data/lib/graphql/language/nodes.rb +5 -1
- data/lib/graphql/language/parser.rb +14 -6
- data/lib/graphql/query/context.rb +3 -8
- data/lib/graphql/query/partial.rb +179 -0
- data/lib/graphql/query.rb +59 -55
- data/lib/graphql/schema/addition.rb +3 -1
- data/lib/graphql/schema/always_visible.rb +1 -0
- data/lib/graphql/schema/argument.rb +9 -3
- data/lib/graphql/schema/build_from_definition.rb +96 -47
- data/lib/graphql/schema/directive/flagged.rb +2 -0
- data/lib/graphql/schema/directive.rb +33 -1
- data/lib/graphql/schema/field.rb +23 -1
- data/lib/graphql/schema/input_object.rb +38 -30
- data/lib/graphql/schema/list.rb +1 -1
- data/lib/graphql/schema/member/has_arguments.rb +2 -2
- data/lib/graphql/schema/member/has_dataloader.rb +4 -2
- data/lib/graphql/schema/member/has_deprecation_reason.rb +15 -0
- data/lib/graphql/schema/member/has_interfaces.rb +2 -2
- data/lib/graphql/schema/member/type_system_helpers.rb +16 -2
- data/lib/graphql/schema/ractor_shareable.rb +79 -0
- data/lib/graphql/schema/resolver.rb +1 -0
- data/lib/graphql/schema/scalar.rb +1 -6
- data/lib/graphql/schema/timeout.rb +19 -2
- data/lib/graphql/schema/validator/required_validator.rb +15 -6
- data/lib/graphql/schema/visibility/migration.rb +2 -2
- data/lib/graphql/schema/visibility/profile.rb +107 -21
- data/lib/graphql/schema/visibility.rb +41 -29
- data/lib/graphql/schema/warden.rb +13 -5
- data/lib/graphql/schema.rb +228 -32
- data/lib/graphql/static_validation/all_rules.rb +2 -2
- data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +47 -13
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +78 -16
- data/lib/graphql/static_validation/rules/fields_will_merge_error.rb +10 -2
- data/lib/graphql/static_validation/rules/not_single_subscription_error.rb +25 -0
- data/lib/graphql/static_validation/rules/subscription_root_exists_and_single_subscription_selection.rb +26 -0
- data/lib/graphql/static_validation/rules/unique_directives_per_location.rb +6 -2
- data/lib/graphql/testing/helpers.rb +5 -2
- data/lib/graphql/tracing/active_support_notifications_trace.rb +7 -0
- data/lib/graphql/tracing/appoptics_tracing.rb +5 -0
- data/lib/graphql/tracing/appsignal_trace.rb +26 -61
- data/lib/graphql/tracing/data_dog_trace.rb +41 -164
- data/lib/graphql/tracing/monitor_trace.rb +283 -0
- data/lib/graphql/tracing/new_relic_trace.rb +34 -164
- data/lib/graphql/tracing/notifications_trace.rb +183 -37
- data/lib/graphql/tracing/null_trace.rb +1 -1
- data/lib/graphql/tracing/perfetto_trace.rb +16 -19
- data/lib/graphql/tracing/prometheus_trace.rb +47 -74
- data/lib/graphql/tracing/scout_trace.rb +25 -59
- data/lib/graphql/tracing/sentry_trace.rb +56 -99
- data/lib/graphql/tracing/statsd_trace.rb +24 -47
- data/lib/graphql/tracing/trace.rb +0 -17
- data/lib/graphql/tracing.rb +1 -0
- data/lib/graphql/type_kinds.rb +1 -0
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +1 -1
- metadata +35 -26
- data/lib/graphql/dashboard/views/graphql/dashboard/traces/index.html.erb +0 -63
- data/lib/graphql/static_validation/rules/subscription_root_exists.rb +0 -17
@@ -12,6 +12,14 @@ module GraphQL
|
|
12
12
|
@scope = scope
|
13
13
|
end
|
14
14
|
|
15
|
+
def self.batch_key_for(association, scope = nil)
|
16
|
+
if scope
|
17
|
+
[association, scope.to_sql]
|
18
|
+
else
|
19
|
+
[association]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
15
23
|
def load(record)
|
16
24
|
if (assoc = record.association(@association)).loaded?
|
17
25
|
assoc.target
|
@@ -23,7 +31,12 @@ module GraphQL
|
|
23
31
|
def fetch(records)
|
24
32
|
record_classes = Set.new.compare_by_identity
|
25
33
|
associated_classes = Set.new.compare_by_identity
|
34
|
+
scoped_fetch = !@scope.nil?
|
26
35
|
records.each do |record|
|
36
|
+
if scoped_fetch
|
37
|
+
assoc = record.association(@association)
|
38
|
+
assoc.reset
|
39
|
+
end
|
27
40
|
if record_classes.add?(record.class)
|
28
41
|
reflection = record.class.reflect_on_association(@association)
|
29
42
|
if !reflection.polymorphic? && reflection.klass
|
@@ -40,18 +53,25 @@ module GraphQL
|
|
40
53
|
|
41
54
|
::ActiveRecord::Associations::Preloader.new(records: records, associations: @association, available_records: available_records, scope: @scope).call
|
42
55
|
|
43
|
-
loaded_associated_records = records.map { |r|
|
44
|
-
|
45
|
-
|
46
|
-
if
|
47
|
-
|
48
|
-
updates[record.id] = record
|
56
|
+
loaded_associated_records = records.map { |r|
|
57
|
+
assoc = r.association(@association)
|
58
|
+
lar = assoc.target
|
59
|
+
if scoped_fetch
|
60
|
+
assoc.reset
|
49
61
|
end
|
50
|
-
|
62
|
+
lar
|
63
|
+
}
|
51
64
|
|
52
|
-
if
|
65
|
+
if !scoped_fetch
|
53
66
|
# Don't cache records loaded via scope because they might have reduced `SELECT`s
|
54
67
|
# Could check .select_values here?
|
68
|
+
records_by_model = {}
|
69
|
+
loaded_associated_records.flatten.each do |record|
|
70
|
+
if record
|
71
|
+
updates = records_by_model[record.class] ||= {}
|
72
|
+
updates[record.id] = record
|
73
|
+
end
|
74
|
+
end
|
55
75
|
records_by_model.each do |model_class, updates|
|
56
76
|
dataloader.with(RECORD_SOURCE_CLASS, model_class).merge(updates)
|
57
77
|
end
|
@@ -7,18 +7,39 @@ module GraphQL
|
|
7
7
|
def initialize(model_class, find_by: model_class.primary_key)
|
8
8
|
@model_class = model_class
|
9
9
|
@find_by = find_by
|
10
|
-
@
|
10
|
+
@find_by_many = find_by.is_a?(Array)
|
11
|
+
if @find_by_many
|
12
|
+
@type_for_column = @find_by.map { |fb| @model_class.type_for_attribute(fb) }
|
13
|
+
else
|
14
|
+
@type_for_column = @model_class.type_for_attribute(@find_by)
|
15
|
+
end
|
11
16
|
end
|
12
17
|
|
13
|
-
def
|
14
|
-
|
15
|
-
|
18
|
+
def result_key_for(requested_key)
|
19
|
+
normalize_fetch_key(requested_key)
|
20
|
+
end
|
21
|
+
|
22
|
+
def normalize_fetch_key(requested_key)
|
23
|
+
if @find_by_many
|
24
|
+
requested_key.each_with_index.map do |k, idx|
|
25
|
+
@type_for_column[idx].cast(k)
|
26
|
+
end
|
27
|
+
else
|
28
|
+
@type_for_column.cast(requested_key)
|
29
|
+
end
|
16
30
|
end
|
17
31
|
|
18
32
|
def fetch(record_ids)
|
19
33
|
records = @model_class.where(@find_by => record_ids)
|
20
34
|
record_lookup = {}
|
21
|
-
|
35
|
+
if @find_by_many
|
36
|
+
records.each do |r|
|
37
|
+
key = @find_by.map { |fb| r.public_send(fb) }
|
38
|
+
record_lookup[key] = r
|
39
|
+
end
|
40
|
+
else
|
41
|
+
records.each { |r| record_lookup[r.public_send(@find_by)] = r }
|
42
|
+
end
|
22
43
|
record_ids.map { |id| record_lookup[id] }
|
23
44
|
end
|
24
45
|
end
|
@@ -9,8 +9,11 @@ module GraphQL
|
|
9
9
|
class NullDataloader < Dataloader
|
10
10
|
# These are all no-ops because code was
|
11
11
|
# executed synchronously.
|
12
|
+
|
13
|
+
def initialize(*); end
|
12
14
|
def run; end
|
13
15
|
def run_isolated; yield; end
|
16
|
+
def clear_cache; end
|
14
17
|
def yield(_source)
|
15
18
|
raise GraphQL::Error, "GraphQL::Dataloader is not running -- add `use GraphQL::Dataloader` to your schema to use Dataloader sources."
|
16
19
|
end
|
@@ -19,6 +22,10 @@ module GraphQL
|
|
19
22
|
yield
|
20
23
|
nil
|
21
24
|
end
|
25
|
+
|
26
|
+
def with(*)
|
27
|
+
raise GraphQL::Error, "GraphQL::Dataloader is not running -- add `use GraphQL::Dataloader` to your schema to use Dataloader sources."
|
28
|
+
end
|
22
29
|
end
|
23
30
|
end
|
24
31
|
end
|
@@ -21,7 +21,7 @@ module GraphQL
|
|
21
21
|
def request(value)
|
22
22
|
res_key = result_key_for(value)
|
23
23
|
if !@results.key?(res_key)
|
24
|
-
@pending[res_key] ||= value
|
24
|
+
@pending[res_key] ||= normalize_fetch_key(value)
|
25
25
|
end
|
26
26
|
Dataloader::Request.new(self, value)
|
27
27
|
end
|
@@ -35,12 +35,24 @@ module GraphQL
|
|
35
35
|
value
|
36
36
|
end
|
37
37
|
|
38
|
+
# Implement this method if varying values given to {load} (etc) should be consolidated
|
39
|
+
# or normalized before being handed off to your {fetch} implementation.
|
40
|
+
#
|
41
|
+
# This is different than {result_key_for} because _that_ method handles unification inside Dataloader's cache,
|
42
|
+
# but this method changes the value passed into {fetch}.
|
43
|
+
#
|
44
|
+
# @param value [Object] The value passed to {load}, {load_all}, {request}, or {request_all}
|
45
|
+
# @return [Object] The value given to {fetch}
|
46
|
+
def normalize_fetch_key(value)
|
47
|
+
value
|
48
|
+
end
|
49
|
+
|
38
50
|
# @return [Dataloader::Request] a pending request for a values from `keys`. Call `.load` on that object to wait for the results.
|
39
51
|
def request_all(values)
|
40
52
|
values.each do |v|
|
41
53
|
res_key = result_key_for(v)
|
42
54
|
if !@results.key?(res_key)
|
43
|
-
@pending[res_key] ||= v
|
55
|
+
@pending[res_key] ||= normalize_fetch_key(v)
|
44
56
|
end
|
45
57
|
end
|
46
58
|
Dataloader::RequestAll.new(self, values)
|
@@ -53,7 +65,7 @@ module GraphQL
|
|
53
65
|
if @results.key?(result_key)
|
54
66
|
result_for(result_key)
|
55
67
|
else
|
56
|
-
@pending[result_key] ||= value
|
68
|
+
@pending[result_key] ||= normalize_fetch_key(value)
|
57
69
|
sync([result_key])
|
58
70
|
result_for(result_key)
|
59
71
|
end
|
@@ -68,7 +80,7 @@ module GraphQL
|
|
68
80
|
k = result_key_for(v)
|
69
81
|
result_keys << k
|
70
82
|
if !@results.key?(k)
|
71
|
-
@pending[k] ||= v
|
83
|
+
@pending[k] ||= normalize_fetch_key(v)
|
72
84
|
pending_keys << k
|
73
85
|
end
|
74
86
|
}
|
data/lib/graphql/dig.rb
CHANGED
@@ -5,7 +5,8 @@ module GraphQL
|
|
5
5
|
# so we can use some of the magic in Schema::InputObject and Interpreter::Arguments
|
6
6
|
# to handle stringified/symbolized keys.
|
7
7
|
#
|
8
|
-
# @param
|
8
|
+
# @param own_key [String, Symbol] A key to retrieve
|
9
|
+
# @param rest_keys [Array<[String, Symbol>] Retrieves the value object corresponding to the each key objects repeatedly
|
9
10
|
# @return [Object]
|
10
11
|
def dig(own_key, *rest_keys)
|
11
12
|
val = self[own_key]
|
@@ -23,9 +23,9 @@ module GraphQL
|
|
23
23
|
if smallest_depth
|
24
24
|
lazies = lazies_at_depth.delete(smallest_depth)
|
25
25
|
if !lazies.empty?
|
26
|
-
|
27
|
-
|
28
|
-
|
26
|
+
lazies.each do |l|
|
27
|
+
dataloader.append_job { l.value }
|
28
|
+
end
|
29
29
|
# Run lazies _and_ dataloader, see if more are enqueued
|
30
30
|
dataloader.run
|
31
31
|
resolve_each_depth(lazies_at_depth, dataloader)
|
@@ -21,15 +21,25 @@ module GraphQL
|
|
21
21
|
@graphql_metadata = nil
|
22
22
|
@graphql_selections = selections
|
23
23
|
@graphql_is_eager = is_eager
|
24
|
+
@base_path = nil
|
24
25
|
end
|
25
26
|
|
27
|
+
# TODO test full path in Partial
|
28
|
+
attr_writer :base_path
|
29
|
+
|
26
30
|
def path
|
27
31
|
@path ||= build_path([])
|
28
32
|
end
|
29
33
|
|
30
34
|
def build_path(path_array)
|
31
35
|
graphql_result_name && path_array.unshift(graphql_result_name)
|
32
|
-
|
36
|
+
if @graphql_parent
|
37
|
+
@graphql_parent.build_path(path_array)
|
38
|
+
elsif @base_path
|
39
|
+
@base_path + path_array
|
40
|
+
else
|
41
|
+
path_array
|
42
|
+
end
|
33
43
|
end
|
34
44
|
|
35
45
|
attr_accessor :graphql_dead
|
@@ -44,8 +54,11 @@ module GraphQL
|
|
44
54
|
def initialize(_result_name, _result_type, _application_value, _parent_result, _is_non_null_in_parent, _selections, _is_eager, _ast_node, _graphql_arguments, graphql_field) # rubocop:disable Metrics/ParameterLists
|
45
55
|
super
|
46
56
|
@graphql_result_data = {}
|
57
|
+
@ordered_result_keys = nil
|
47
58
|
end
|
48
59
|
|
60
|
+
attr_accessor :ordered_result_keys
|
61
|
+
|
49
62
|
include GraphQLResult
|
50
63
|
|
51
64
|
attr_accessor :graphql_merged_into
|
@@ -63,7 +76,13 @@ module GraphQL
|
|
63
76
|
t.set_leaf(key, value)
|
64
77
|
end
|
65
78
|
|
79
|
+
before_size = @graphql_result_data.size
|
66
80
|
@graphql_result_data[key] = value
|
81
|
+
after_size = @graphql_result_data.size
|
82
|
+
if after_size > before_size && @ordered_result_keys[before_size] != key
|
83
|
+
fix_result_order
|
84
|
+
end
|
85
|
+
|
67
86
|
# keep this up-to-date if it's been initialized
|
68
87
|
@graphql_metadata && @graphql_metadata[key] = value
|
69
88
|
|
@@ -74,7 +93,13 @@ module GraphQL
|
|
74
93
|
if (t = @graphql_merged_into)
|
75
94
|
t.set_child_result(key, value)
|
76
95
|
end
|
96
|
+
before_size = @graphql_result_data.size
|
77
97
|
@graphql_result_data[key] = value.graphql_result_data
|
98
|
+
after_size = @graphql_result_data.size
|
99
|
+
if after_size > before_size && @ordered_result_keys[before_size] != key
|
100
|
+
fix_result_order
|
101
|
+
end
|
102
|
+
|
78
103
|
# If we encounter some part of this response that requires metadata tracking,
|
79
104
|
# then create the metadata hash if necessary. It will be kept up-to-date after this.
|
80
105
|
(@graphql_metadata ||= @graphql_result_data.dup)[key] = value
|
@@ -124,6 +149,14 @@ module GraphQL
|
|
124
149
|
end
|
125
150
|
@graphql_merged_into = into_result
|
126
151
|
end
|
152
|
+
|
153
|
+
def fix_result_order
|
154
|
+
@ordered_result_keys.each do |k|
|
155
|
+
if @graphql_result_data.key?(k)
|
156
|
+
@graphql_result_data[k] = @graphql_result_data.delete(k)
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
127
160
|
end
|
128
161
|
|
129
162
|
class GraphQLResultArray
|
@@ -57,67 +57,160 @@ module GraphQL
|
|
57
57
|
end
|
58
58
|
|
59
59
|
def final_result
|
60
|
-
@response
|
60
|
+
@response.respond_to?(:graphql_result_data) ? @response.graphql_result_data : @response
|
61
61
|
end
|
62
62
|
|
63
63
|
def inspect
|
64
64
|
"#<#{self.class.name} response=#{@response.inspect}>"
|
65
65
|
end
|
66
66
|
|
67
|
-
# This _begins_ the execution. Some deferred work
|
68
|
-
# might be stored up in lazies.
|
69
67
|
# @return [void]
|
70
68
|
def run_eager
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
69
|
+
root_type = query.root_type
|
70
|
+
case query
|
71
|
+
when GraphQL::Query
|
72
|
+
ast_node = query.selected_operation
|
73
|
+
selections = ast_node.selections
|
74
|
+
object = query.root_value
|
75
|
+
is_eager = ast_node.operation_type == "mutation"
|
76
|
+
base_path = nil
|
77
|
+
when GraphQL::Query::Partial
|
78
|
+
ast_node = query.ast_nodes.first
|
79
|
+
selections = query.ast_nodes.map(&:selections).inject(&:+)
|
80
|
+
object = query.object
|
81
|
+
is_eager = false
|
82
|
+
base_path = query.path
|
84
83
|
else
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
84
|
+
raise ArgumentError, "Unexpected Runnable, can't execute: #{query.class} (#{query.inspect})"
|
85
|
+
end
|
86
|
+
object = schema.sync_lazy(object) # TODO test query partial with lazy root object
|
87
|
+
runtime_state = get_current_runtime_state
|
88
|
+
case root_type.kind.name
|
89
|
+
when "OBJECT"
|
90
|
+
object_proxy = root_type.wrap(object, context)
|
91
|
+
object_proxy = schema.sync_lazy(object_proxy)
|
92
|
+
if object_proxy.nil?
|
93
|
+
@response = nil
|
94
|
+
else
|
95
|
+
@response = GraphQLResultHash.new(nil, root_type, object_proxy, nil, false, selections, is_eager, ast_node, nil, nil)
|
96
|
+
@response.base_path = base_path
|
97
|
+
runtime_state.current_result = @response
|
98
|
+
call_method_on_directives(:resolve, object, ast_node.directives) do
|
99
|
+
each_gathered_selections(@response) do |selections, is_selection_array, ordered_result_keys|
|
100
|
+
@response.ordered_result_keys ||= ordered_result_keys
|
101
|
+
if is_selection_array
|
102
|
+
selection_response = GraphQLResultHash.new(nil, root_type, object_proxy, nil, false, selections, is_eager, ast_node, nil, nil)
|
103
|
+
selection_response.ordered_result_keys = ordered_result_keys
|
104
|
+
final_response = @response
|
105
|
+
else
|
106
|
+
selection_response = @response
|
107
|
+
final_response = nil
|
108
|
+
end
|
94
109
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
110
|
+
@dataloader.append_job {
|
111
|
+
evaluate_selections(
|
112
|
+
selections,
|
113
|
+
selection_response,
|
114
|
+
final_response,
|
115
|
+
nil,
|
116
|
+
)
|
117
|
+
}
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
when "LIST"
|
122
|
+
inner_type = root_type.unwrap
|
123
|
+
case inner_type.kind.name
|
124
|
+
when "SCALAR", "ENUM"
|
125
|
+
result_name = ast_node.alias || ast_node.name
|
126
|
+
field_defn = query.field_definition
|
127
|
+
owner_type = field_defn.owner
|
128
|
+
selection_result = GraphQLResultHash.new(nil, owner_type, nil, nil, false, EmptyObjects::EMPTY_ARRAY, false, ast_node, nil, nil)
|
129
|
+
selection_result.base_path = base_path
|
130
|
+
selection_result.ordered_result_keys = [result_name]
|
131
|
+
runtime_state = get_current_runtime_state
|
132
|
+
runtime_state.current_result = selection_result
|
133
|
+
runtime_state.current_result_name = result_name
|
134
|
+
continue_value = continue_value(object, field_defn, false, ast_node, result_name, selection_result)
|
135
|
+
if HALT != continue_value
|
136
|
+
continue_field(continue_value, owner_type, field_defn, root_type, ast_node, nil, false, nil, nil, result_name, selection_result, false, runtime_state) # rubocop:disable Metrics/ParameterLists
|
137
|
+
end
|
138
|
+
@response = selection_result[result_name]
|
139
|
+
else
|
140
|
+
@response = GraphQLResultArray.new(nil, root_type, nil, nil, false, selections, false, ast_node, nil, nil)
|
141
|
+
@response.base_path = base_path
|
142
|
+
idx = nil
|
143
|
+
object.each do |inner_value|
|
144
|
+
idx ||= 0
|
145
|
+
this_idx = idx
|
146
|
+
idx += 1
|
147
|
+
@dataloader.append_job do
|
148
|
+
runtime_state.current_result_name = this_idx
|
149
|
+
runtime_state.current_result = @response
|
150
|
+
continue_field(
|
151
|
+
inner_value, root_type, nil, inner_type, nil, @response.graphql_selections, false, object_proxy,
|
152
|
+
nil, this_idx, @response, false, runtime_state
|
101
153
|
)
|
102
|
-
|
154
|
+
end
|
103
155
|
end
|
104
156
|
end
|
157
|
+
when "SCALAR", "ENUM"
|
158
|
+
result_name = ast_node.alias || ast_node.name
|
159
|
+
field_defn = query.field_definition
|
160
|
+
owner_type = field_defn.owner
|
161
|
+
selection_result = GraphQLResultHash.new(nil, owner_type, nil, nil, false, EmptyObjects::EMPTY_ARRAY, false, ast_node, nil, nil)
|
162
|
+
selection_result.ordered_result_keys = [result_name]
|
163
|
+
selection_result.base_path = base_path
|
164
|
+
runtime_state = get_current_runtime_state
|
165
|
+
runtime_state.current_result = selection_result
|
166
|
+
runtime_state.current_result_name = result_name
|
167
|
+
continue_value = continue_value(object, field_defn, false, ast_node, result_name, selection_result)
|
168
|
+
if HALT != continue_value
|
169
|
+
continue_field(continue_value, owner_type, field_defn, query.root_type, ast_node, nil, false, nil, nil, result_name, selection_result, false, runtime_state) # rubocop:disable Metrics/ParameterLists
|
170
|
+
end
|
171
|
+
@response = selection_result[result_name]
|
172
|
+
when "UNION", "INTERFACE"
|
173
|
+
resolved_type, _resolved_obj = resolve_type(root_type, object)
|
174
|
+
resolved_type = schema.sync_lazy(resolved_type)
|
175
|
+
object_proxy = resolved_type.wrap(object, context)
|
176
|
+
object_proxy = schema.sync_lazy(object_proxy)
|
177
|
+
@response = GraphQLResultHash.new(nil, resolved_type, object_proxy, nil, false, selections, false, query.ast_nodes.first, nil, nil)
|
178
|
+
@response.base_path = base_path
|
179
|
+
each_gathered_selections(@response) do |selections, is_selection_array, ordered_result_keys|
|
180
|
+
@response.ordered_result_keys ||= ordered_result_keys
|
181
|
+
if is_selection_array == true
|
182
|
+
raise "This isn't supported yet"
|
183
|
+
end
|
184
|
+
|
185
|
+
@dataloader.append_job {
|
186
|
+
evaluate_selections(
|
187
|
+
selections,
|
188
|
+
@response,
|
189
|
+
nil,
|
190
|
+
runtime_state,
|
191
|
+
)
|
192
|
+
}
|
193
|
+
end
|
194
|
+
else
|
195
|
+
raise "Invariant: unsupported type kind for partial execution: #{root_type.kind.inspect} (#{root_type})"
|
105
196
|
end
|
106
197
|
nil
|
107
198
|
end
|
108
199
|
|
109
200
|
def each_gathered_selections(response_hash)
|
110
|
-
|
201
|
+
ordered_result_keys = []
|
202
|
+
gathered_selections = gather_selections(response_hash.graphql_application_value, response_hash.graphql_result_type, response_hash.graphql_selections, nil, {}, ordered_result_keys)
|
203
|
+
ordered_result_keys.uniq!
|
111
204
|
if gathered_selections.is_a?(Array)
|
112
205
|
gathered_selections.each do |item|
|
113
|
-
yield(item, true)
|
206
|
+
yield(item, true, ordered_result_keys)
|
114
207
|
end
|
115
208
|
else
|
116
|
-
yield(gathered_selections, false)
|
209
|
+
yield(gathered_selections, false, ordered_result_keys)
|
117
210
|
end
|
118
211
|
end
|
119
212
|
|
120
|
-
def gather_selections(owner_object, owner_type, selections, selections_to_run
|
213
|
+
def gather_selections(owner_object, owner_type, selections, selections_to_run, selections_by_name, ordered_result_keys)
|
121
214
|
selections.each do |node|
|
122
215
|
# Skip gathering this if the directive says so
|
123
216
|
if !directives_include?(node, owner_object, owner_type)
|
@@ -126,6 +219,7 @@ module GraphQL
|
|
126
219
|
|
127
220
|
if node.is_a?(GraphQL::Language::Nodes::Field)
|
128
221
|
response_key = node.alias || node.name
|
222
|
+
ordered_result_keys << response_key
|
129
223
|
selections = selections_by_name[response_key]
|
130
224
|
# if there was already a selection of this field,
|
131
225
|
# use an array to hold all selections,
|
@@ -162,14 +256,14 @@ module GraphQL
|
|
162
256
|
type_defn = query.types.type(node.type.name)
|
163
257
|
|
164
258
|
if query.types.possible_types(type_defn).include?(owner_type)
|
165
|
-
result = gather_selections(owner_object, owner_type, node.selections, selections_to_run, next_selections)
|
259
|
+
result = gather_selections(owner_object, owner_type, node.selections, selections_to_run, next_selections, ordered_result_keys)
|
166
260
|
if !result.equal?(next_selections)
|
167
261
|
selections_to_run = result
|
168
262
|
end
|
169
263
|
end
|
170
264
|
else
|
171
265
|
# it's an untyped fragment, definitely continue
|
172
|
-
result = gather_selections(owner_object, owner_type, node.selections, selections_to_run, next_selections)
|
266
|
+
result = gather_selections(owner_object, owner_type, node.selections, selections_to_run, next_selections, ordered_result_keys)
|
173
267
|
if !result.equal?(next_selections)
|
174
268
|
selections_to_run = result
|
175
269
|
end
|
@@ -178,7 +272,7 @@ module GraphQL
|
|
178
272
|
fragment_def = query.fragments[node.name]
|
179
273
|
type_defn = query.types.type(fragment_def.type.name)
|
180
274
|
if query.types.possible_types(type_defn).include?(owner_type)
|
181
|
-
result = gather_selections(owner_object, owner_type, fragment_def.selections, selections_to_run, next_selections)
|
275
|
+
result = gather_selections(owner_object, owner_type, fragment_def.selections, selections_to_run, next_selections, ordered_result_keys)
|
182
276
|
if !result.equal?(next_selections)
|
183
277
|
selections_to_run = result
|
184
278
|
end
|
@@ -204,10 +298,7 @@ module GraphQL
|
|
204
298
|
end
|
205
299
|
|
206
300
|
call_method_on_directives(:resolve, selections_result.graphql_application_value, directives) do
|
207
|
-
finished_jobs = 0
|
208
|
-
enqueued_jobs = gathered_selections.size
|
209
301
|
gathered_selections.each do |result_name, field_ast_nodes_or_ast_node|
|
210
|
-
|
211
302
|
# Field resolution may pause the fiber,
|
212
303
|
# so it wouldn't get to the `Resolve` call that happens below.
|
213
304
|
# So instead trigger a run from this outer context.
|
@@ -217,12 +308,6 @@ module GraphQL
|
|
217
308
|
evaluate_selection(
|
218
309
|
result_name, field_ast_nodes_or_ast_node, selections_result
|
219
310
|
)
|
220
|
-
finished_jobs += 1
|
221
|
-
if finished_jobs == enqueued_jobs
|
222
|
-
if target_result
|
223
|
-
selections_result.merge_into(target_result)
|
224
|
-
end
|
225
|
-
end
|
226
311
|
@dataloader.clear_cache
|
227
312
|
}
|
228
313
|
else
|
@@ -230,15 +315,12 @@ module GraphQL
|
|
230
315
|
evaluate_selection(
|
231
316
|
result_name, field_ast_nodes_or_ast_node, selections_result
|
232
317
|
)
|
233
|
-
finished_jobs += 1
|
234
|
-
if finished_jobs == enqueued_jobs
|
235
|
-
if target_result
|
236
|
-
selections_result.merge_into(target_result)
|
237
|
-
end
|
238
|
-
end
|
239
318
|
}
|
240
319
|
end
|
241
320
|
end
|
321
|
+
if target_result
|
322
|
+
selections_result.merge_into(target_result)
|
323
|
+
end
|
242
324
|
selections_result
|
243
325
|
end
|
244
326
|
end
|
@@ -464,16 +546,17 @@ module GraphQL
|
|
464
546
|
path
|
465
547
|
end
|
466
548
|
|
467
|
-
HALT = Object.new
|
549
|
+
HALT = Object.new.freeze
|
468
550
|
def continue_value(value, field, is_non_null, ast_node, result_name, selection_result) # rubocop:disable Metrics/ParameterLists
|
469
551
|
case value
|
470
552
|
when nil
|
471
553
|
if is_non_null
|
472
554
|
set_result(selection_result, result_name, nil, false, is_non_null) do
|
473
555
|
# When this comes from a list item, use the parent object:
|
474
|
-
|
556
|
+
is_from_array = selection_result.is_a?(GraphQLResultArray)
|
557
|
+
parent_type = is_from_array ? selection_result.graphql_parent.graphql_result_type : selection_result.graphql_result_type
|
475
558
|
# This block is called if `result_name` is not dead. (Maybe a previous invalid nil caused it be marked dead.)
|
476
|
-
err = parent_type::InvalidNullError.new(parent_type, field, ast_node)
|
559
|
+
err = parent_type::InvalidNullError.new(parent_type, field, ast_node, is_from_array: is_from_array)
|
477
560
|
schema.type_error(err, context)
|
478
561
|
end
|
479
562
|
else
|
@@ -581,13 +664,25 @@ module GraphQL
|
|
581
664
|
when "SCALAR", "ENUM"
|
582
665
|
r = begin
|
583
666
|
current_type.coerce_result(value, context)
|
667
|
+
rescue GraphQL::ExecutionError => ex_err
|
668
|
+
return continue_value(ex_err, field, is_non_null, ast_node, result_name, selection_result)
|
584
669
|
rescue StandardError => err
|
585
670
|
query.handle_or_reraise(err)
|
586
671
|
end
|
587
672
|
set_result(selection_result, result_name, r, false, is_non_null)
|
588
673
|
r
|
589
674
|
when "UNION", "INTERFACE"
|
590
|
-
resolved_type_or_lazy =
|
675
|
+
resolved_type_or_lazy = begin
|
676
|
+
resolve_type(current_type, value)
|
677
|
+
rescue GraphQL::ExecutionError, GraphQL::UnauthorizedError => ex_err
|
678
|
+
return continue_value(ex_err, field, is_non_null, ast_node, result_name, selection_result)
|
679
|
+
rescue StandardError => err
|
680
|
+
begin
|
681
|
+
query.handle_or_reraise(err)
|
682
|
+
rescue GraphQL::ExecutionError => ex_err
|
683
|
+
return continue_value(ex_err, field, is_non_null, ast_node, result_name, selection_result)
|
684
|
+
end
|
685
|
+
end
|
591
686
|
after_lazy(resolved_type_or_lazy, 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 |resolved_type_result, runtime_state|
|
592
687
|
if resolved_type_result.is_a?(Array) && resolved_type_result.length == 2
|
593
688
|
resolved_type, resolved_value = resolved_type_result
|
@@ -619,9 +714,11 @@ module GraphQL
|
|
619
714
|
if HALT != continue_value
|
620
715
|
response_hash = GraphQLResultHash.new(result_name, current_type, continue_value, selection_result, is_non_null, next_selections, false, ast_node, arguments, field)
|
621
716
|
set_result(selection_result, result_name, response_hash, true, is_non_null)
|
622
|
-
each_gathered_selections(response_hash) do |selections, is_selection_array|
|
717
|
+
each_gathered_selections(response_hash) do |selections, is_selection_array, ordered_result_keys|
|
718
|
+
response_hash.ordered_result_keys ||= ordered_result_keys
|
623
719
|
if is_selection_array
|
624
720
|
this_result = GraphQLResultHash.new(result_name, current_type, continue_value, selection_result, is_non_null, selections, false, ast_node, arguments, field)
|
721
|
+
this_result.ordered_result_keys = ordered_result_keys
|
625
722
|
final_result = response_hash
|
626
723
|
else
|
627
724
|
this_result = response_hash
|
@@ -720,6 +817,13 @@ module GraphQL
|
|
720
817
|
else
|
721
818
|
dir_defn = @schema_directives.fetch(dir_node.name)
|
722
819
|
raw_dir_args = arguments(nil, dir_defn, dir_node)
|
820
|
+
if !raw_dir_args.is_a?(GraphQL::ExecutionError)
|
821
|
+
begin
|
822
|
+
dir_defn.validate!(raw_dir_args, context)
|
823
|
+
rescue GraphQL::ExecutionError => err
|
824
|
+
raw_dir_args = err
|
825
|
+
end
|
826
|
+
end
|
723
827
|
dir_args = continue_value(
|
724
828
|
raw_dir_args, # value
|
725
829
|
nil, # field
|