graphql 2.0.14 → 2.0.32
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/generators/graphql/mutation_delete_generator.rb +1 -1
- data/lib/generators/graphql/mutation_update_generator.rb +1 -1
- data/lib/generators/graphql/relay.rb +18 -1
- data/lib/graphql/analysis/ast/visitor.rb +42 -35
- data/lib/graphql/analysis/ast.rb +2 -2
- data/lib/graphql/backtrace/table.rb +2 -2
- data/lib/graphql/backtrace/trace.rb +96 -0
- data/lib/graphql/backtrace/tracer.rb +1 -1
- data/lib/graphql/backtrace.rb +2 -1
- data/lib/graphql/dataloader/source.rb +69 -45
- data/lib/graphql/dataloader.rb +8 -5
- data/lib/graphql/execution/interpreter/arguments.rb +1 -1
- data/lib/graphql/execution/interpreter/arguments_cache.rb +33 -33
- data/lib/graphql/execution/interpreter/resolve.rb +19 -0
- data/lib/graphql/execution/interpreter/runtime.rb +355 -268
- data/lib/graphql/execution/interpreter.rb +19 -15
- data/lib/graphql/execution/lazy.rb +6 -12
- data/lib/graphql/execution/lookahead.rb +16 -5
- data/lib/graphql/execution/multiplex.rb +2 -1
- data/lib/graphql/filter.rb +8 -2
- data/lib/graphql/introspection/directive_type.rb +2 -2
- data/lib/graphql/introspection/entry_points.rb +1 -1
- data/lib/graphql/introspection/field_type.rb +1 -1
- data/lib/graphql/introspection/schema_type.rb +2 -2
- data/lib/graphql/introspection/type_type.rb +5 -5
- data/lib/graphql/introspection.rb +1 -1
- data/lib/graphql/language/document_from_schema_definition.rb +58 -35
- data/lib/graphql/language/lexer.rb +248 -1505
- data/lib/graphql/language/nodes.rb +69 -40
- data/lib/graphql/language/parser.rb +775 -742
- data/lib/graphql/language/parser.y +44 -38
- data/lib/graphql/language/printer.rb +48 -25
- data/lib/graphql/language/visitor.rb +192 -81
- data/lib/graphql/pagination/active_record_relation_connection.rb +0 -8
- data/lib/graphql/pagination/connection.rb +5 -5
- data/lib/graphql/query/context.rb +93 -27
- data/lib/graphql/query/null_context.rb +8 -18
- data/lib/graphql/query/validation_pipeline.rb +2 -1
- data/lib/graphql/query.rb +55 -13
- data/lib/graphql/rake_task.rb +28 -1
- data/lib/graphql/schema/addition.rb +38 -12
- data/lib/graphql/schema/always_visible.rb +10 -0
- data/lib/graphql/schema/argument.rb +15 -23
- data/lib/graphql/schema/build_from_definition.rb +54 -25
- data/lib/graphql/schema/directive/transform.rb +1 -1
- data/lib/graphql/schema/directive.rb +12 -2
- data/lib/graphql/schema/enum.rb +24 -17
- data/lib/graphql/schema/enum_value.rb +3 -4
- data/lib/graphql/schema/field/connection_extension.rb +1 -1
- data/lib/graphql/schema/field.rb +95 -73
- data/lib/graphql/schema/field_extension.rb +1 -4
- data/lib/graphql/schema/find_inherited_value.rb +2 -7
- data/lib/graphql/schema/input_object.rb +9 -7
- data/lib/graphql/schema/interface.rb +5 -11
- data/lib/graphql/schema/introspection_system.rb +1 -1
- data/lib/graphql/schema/late_bound_type.rb +2 -0
- data/lib/graphql/schema/member/base_dsl_methods.rb +17 -14
- data/lib/graphql/schema/member/build_type.rb +11 -3
- data/lib/graphql/schema/member/has_arguments.rb +114 -65
- data/lib/graphql/schema/member/has_ast_node.rb +12 -0
- data/lib/graphql/schema/member/has_deprecation_reason.rb +3 -4
- data/lib/graphql/schema/member/has_directives.rb +81 -61
- data/lib/graphql/schema/member/has_fields.rb +95 -38
- data/lib/graphql/schema/member/has_interfaces.rb +49 -8
- data/lib/graphql/schema/member/has_validators.rb +32 -6
- data/lib/graphql/schema/member/relay_shortcuts.rb +19 -0
- data/lib/graphql/schema/member/type_system_helpers.rb +17 -0
- data/lib/graphql/schema/object.rb +8 -5
- data/lib/graphql/schema/printer.rb +3 -1
- data/lib/graphql/schema/relay_classic_mutation.rb +1 -1
- data/lib/graphql/schema/resolver/has_payload_type.rb +9 -9
- data/lib/graphql/schema/resolver.rb +16 -14
- data/lib/graphql/schema/timeout.rb +25 -29
- data/lib/graphql/schema/type_membership.rb +3 -0
- data/lib/graphql/schema/union.rb +10 -1
- data/lib/graphql/schema/validator.rb +2 -2
- data/lib/graphql/schema/warden.rb +64 -7
- data/lib/graphql/schema.rb +171 -28
- data/lib/graphql/static_validation/definition_dependencies.rb +7 -1
- data/lib/graphql/static_validation/literal_validator.rb +15 -1
- data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +12 -4
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +2 -2
- data/lib/graphql/static_validation/validator.rb +1 -1
- data/lib/graphql/subscriptions/action_cable_subscriptions.rb +7 -1
- data/lib/graphql/subscriptions/event.rb +2 -7
- data/lib/graphql/subscriptions.rb +5 -0
- data/lib/graphql/tracing/active_support_notifications_trace.rb +16 -0
- data/lib/graphql/tracing/appoptics_trace.rb +255 -0
- data/lib/graphql/tracing/appsignal_trace.rb +81 -0
- data/lib/graphql/tracing/data_dog_trace.rb +187 -0
- data/lib/graphql/tracing/data_dog_tracing.rb +7 -21
- data/lib/graphql/tracing/legacy_trace.rb +69 -0
- data/lib/graphql/tracing/new_relic_trace.rb +75 -0
- data/lib/graphql/tracing/notifications_trace.rb +49 -0
- data/lib/graphql/tracing/platform_trace.rb +123 -0
- data/lib/graphql/tracing/platform_tracing.rb +15 -3
- data/lib/graphql/tracing/prometheus_trace.rb +93 -0
- data/lib/graphql/tracing/prometheus_tracing/graphql_collector.rb +1 -1
- data/lib/graphql/tracing/prometheus_tracing.rb +3 -3
- data/lib/graphql/tracing/scout_trace.rb +75 -0
- data/lib/graphql/tracing/statsd_trace.rb +60 -0
- data/lib/graphql/tracing/trace.rb +75 -0
- data/lib/graphql/tracing.rb +17 -39
- data/lib/graphql/type_kinds.rb +6 -3
- data/lib/graphql/types/relay/base_connection.rb +1 -1
- data/lib/graphql/types/relay/connection_behaviors.rb +28 -6
- data/lib/graphql/types/relay/edge_behaviors.rb +16 -5
- data/lib/graphql/types/relay/node_behaviors.rb +8 -2
- data/lib/graphql/types/relay/page_info_behaviors.rb +7 -2
- data/lib/graphql/types/relay.rb +0 -1
- data/lib/graphql/types/string.rb +1 -1
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +16 -9
- data/readme.md +1 -1
- metadata +66 -29
- data/lib/graphql/language/lexer.rl +0 -280
- data/lib/graphql/types/relay/default_relay.rb +0 -21
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c86c6f3920de2d67a9d8b23a2783ec33f15b70571236db184ca9a9e6b459b80b
|
|
4
|
+
data.tar.gz: 6f4659f46b99da5c6065ba7084b899253958bdb5627b65f469262898cb38205c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 02bf27d69ec35ecdad618ed18a7731fe7d66cd1b5105fcc7625e7b68f534b31e7e5b6f458ba1efe22b5f6e9e6d819cc696e7cdeba6e80bbfa3259691d9ede441
|
|
7
|
+
data.tar.gz: 5b91453bbc8e01a72bcfbe5cef21e7f65704ff1976d945c01291e48667a4a1f36e35f690048ba2a029d5c9f019cfa9c72fa46b2f851b422117de48a4722152ec
|
|
@@ -6,7 +6,7 @@ module Graphql
|
|
|
6
6
|
# TODO: What other options should be supported?
|
|
7
7
|
#
|
|
8
8
|
# @example Generate a `GraphQL::Schema::RelayClassicMutation` by name
|
|
9
|
-
# rails g graphql:mutation
|
|
9
|
+
# rails g graphql:mutation DeletePostMutation
|
|
10
10
|
class MutationDeleteGenerator < OrmMutationsBase
|
|
11
11
|
|
|
12
12
|
desc "Scaffold a Relay Classic ORM delete mutation for the given model class"
|
|
@@ -6,7 +6,7 @@ module Graphql
|
|
|
6
6
|
# TODO: What other options should be supported?
|
|
7
7
|
#
|
|
8
8
|
# @example Generate a `GraphQL::Schema::RelayClassicMutation` by name
|
|
9
|
-
# rails g graphql:mutation
|
|
9
|
+
# rails g graphql:mutation UpdatePostMutation
|
|
10
10
|
class MutationUpdateGenerator < OrmMutationsBase
|
|
11
11
|
|
|
12
12
|
desc "Scaffold a Relay Classic ORM update mutation for the given model class"
|
|
@@ -6,7 +6,24 @@ module Graphql
|
|
|
6
6
|
# Add Node, `node(id:)`, and `nodes(ids:)`
|
|
7
7
|
template("node_type.erb", "#{options[:directory]}/types/node_type.rb")
|
|
8
8
|
in_root do
|
|
9
|
-
fields =
|
|
9
|
+
fields = <<-RUBY
|
|
10
|
+
field :node, Types::NodeType, null: true, description: "Fetches an object given its ID." do
|
|
11
|
+
argument :id, ID, required: true, description: "ID of the object."
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def node(id:)
|
|
15
|
+
context.schema.object_from_id(id, context)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
field :nodes, [Types::NodeType, null: true], null: true, description: "Fetches a list of objects given a list of IDs." do
|
|
19
|
+
argument :ids, [ID], required: true, description: "IDs of the objects."
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def nodes(ids:)
|
|
23
|
+
ids.map { |id| context.schema.object_from_id(id, context) }
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
RUBY
|
|
10
27
|
inject_into_file "#{options[:directory]}/types/query_type.rb", fields, after: /class .*QueryType\s*<\s*[^\s]+?\n/m, force: false
|
|
11
28
|
end
|
|
12
29
|
|
|
@@ -65,14 +65,41 @@ module GraphQL
|
|
|
65
65
|
end
|
|
66
66
|
|
|
67
67
|
# Visitor Hooks
|
|
68
|
+
[
|
|
69
|
+
:operation_definition, :fragment_definition,
|
|
70
|
+
:inline_fragment, :field, :directive, :argument, :fragment_spread
|
|
71
|
+
].each do |node_type|
|
|
72
|
+
module_eval <<-RUBY, __FILE__, __LINE__
|
|
73
|
+
def call_on_enter_#{node_type}(node, parent)
|
|
74
|
+
@analyzers.each do |a|
|
|
75
|
+
begin
|
|
76
|
+
a.on_enter_#{node_type}(node, parent, self)
|
|
77
|
+
rescue AnalysisError => err
|
|
78
|
+
@rescued_errors << err
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def call_on_leave_#{node_type}(node, parent)
|
|
84
|
+
@analyzers.each do |a|
|
|
85
|
+
begin
|
|
86
|
+
a.on_leave_#{node_type}(node, parent, self)
|
|
87
|
+
rescue AnalysisError => err
|
|
88
|
+
@rescued_errors << err
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
RUBY
|
|
94
|
+
end
|
|
68
95
|
|
|
69
96
|
def on_operation_definition(node, parent)
|
|
70
97
|
object_type = @schema.root_type_for_operation(node.operation_type)
|
|
71
98
|
@object_types.push(object_type)
|
|
72
99
|
@path.push("#{node.operation_type}#{node.name ? " #{node.name}" : ""}")
|
|
73
|
-
|
|
100
|
+
call_on_enter_operation_definition(node, parent)
|
|
74
101
|
super
|
|
75
|
-
|
|
102
|
+
call_on_leave_operation_definition(node, parent)
|
|
76
103
|
@object_types.pop
|
|
77
104
|
@path.pop
|
|
78
105
|
end
|
|
@@ -81,19 +108,19 @@ module GraphQL
|
|
|
81
108
|
on_fragment_with_type(node) do
|
|
82
109
|
@path.push("fragment #{node.name}")
|
|
83
110
|
@in_fragment_def = false
|
|
84
|
-
|
|
111
|
+
call_on_enter_fragment_definition(node, parent)
|
|
85
112
|
super
|
|
86
113
|
@in_fragment_def = false
|
|
87
|
-
|
|
114
|
+
call_on_leave_fragment_definition(node, parent)
|
|
88
115
|
end
|
|
89
116
|
end
|
|
90
117
|
|
|
91
118
|
def on_inline_fragment(node, parent)
|
|
92
119
|
on_fragment_with_type(node) do
|
|
93
120
|
@path.push("...#{node.type ? " on #{node.type.name}" : ""}")
|
|
94
|
-
|
|
121
|
+
call_on_enter_inline_fragment(node, parent)
|
|
95
122
|
super
|
|
96
|
-
|
|
123
|
+
call_on_leave_inline_fragment(node, parent)
|
|
97
124
|
end
|
|
98
125
|
end
|
|
99
126
|
|
|
@@ -114,12 +141,10 @@ module GraphQL
|
|
|
114
141
|
@skipping = @skip_stack.last || skip?(node)
|
|
115
142
|
@skip_stack << @skipping
|
|
116
143
|
|
|
117
|
-
|
|
144
|
+
call_on_enter_field(node, parent)
|
|
118
145
|
super
|
|
119
|
-
|
|
120
146
|
@skipping = @skip_stack.pop
|
|
121
|
-
|
|
122
|
-
call_analyzers(:on_leave_field, node, parent)
|
|
147
|
+
call_on_leave_field(node, parent)
|
|
123
148
|
@response_path.pop
|
|
124
149
|
@field_definitions.pop
|
|
125
150
|
@object_types.pop
|
|
@@ -129,9 +154,9 @@ module GraphQL
|
|
|
129
154
|
def on_directive(node, parent)
|
|
130
155
|
directive_defn = @schema.directives[node.name]
|
|
131
156
|
@directive_definitions.push(directive_defn)
|
|
132
|
-
|
|
157
|
+
call_on_enter_directive(node, parent)
|
|
133
158
|
super
|
|
134
|
-
|
|
159
|
+
call_on_leave_directive(node, parent)
|
|
135
160
|
@directive_definitions.pop
|
|
136
161
|
end
|
|
137
162
|
|
|
@@ -153,29 +178,23 @@ module GraphQL
|
|
|
153
178
|
|
|
154
179
|
@argument_definitions.push(argument_defn)
|
|
155
180
|
@path.push(node.name)
|
|
156
|
-
|
|
181
|
+
call_on_enter_argument(node, parent)
|
|
157
182
|
super
|
|
158
|
-
|
|
183
|
+
call_on_leave_argument(node, parent)
|
|
159
184
|
@argument_definitions.pop
|
|
160
185
|
@path.pop
|
|
161
186
|
end
|
|
162
187
|
|
|
163
188
|
def on_fragment_spread(node, parent)
|
|
164
189
|
@path.push("... #{node.name}")
|
|
165
|
-
|
|
190
|
+
call_on_enter_fragment_spread(node, parent)
|
|
166
191
|
enter_fragment_spread_inline(node)
|
|
167
192
|
super
|
|
168
193
|
leave_fragment_spread_inline(node)
|
|
169
|
-
|
|
194
|
+
call_on_leave_fragment_spread(node, parent)
|
|
170
195
|
@path.pop
|
|
171
196
|
end
|
|
172
197
|
|
|
173
|
-
def on_abstract_node(node, parent)
|
|
174
|
-
call_analyzers(:on_enter_abstract_node, node, parent)
|
|
175
|
-
super
|
|
176
|
-
call_analyzers(:on_leave_abstract_node, node, parent)
|
|
177
|
-
end
|
|
178
|
-
|
|
179
198
|
# @return [GraphQL::BaseType] The current object type
|
|
180
199
|
def type_definition
|
|
181
200
|
@object_types.last
|
|
@@ -226,9 +245,7 @@ module GraphQL
|
|
|
226
245
|
|
|
227
246
|
object_types << object_type
|
|
228
247
|
|
|
229
|
-
fragment_def
|
|
230
|
-
visit_node(selection, fragment_def)
|
|
231
|
-
end
|
|
248
|
+
on_fragment_definition_children(fragment_def)
|
|
232
249
|
end
|
|
233
250
|
|
|
234
251
|
# Visit a fragment spread inline instead of visiting the definition
|
|
@@ -242,16 +259,6 @@ module GraphQL
|
|
|
242
259
|
dir.any? && !GraphQL::Execution::DirectiveChecks.include?(dir, query)
|
|
243
260
|
end
|
|
244
261
|
|
|
245
|
-
def call_analyzers(method, node, parent)
|
|
246
|
-
@analyzers.each do |analyzer|
|
|
247
|
-
begin
|
|
248
|
-
analyzer.public_send(method, node, parent, self)
|
|
249
|
-
rescue AnalysisError => err
|
|
250
|
-
@rescued_errors << err
|
|
251
|
-
end
|
|
252
|
-
end
|
|
253
|
-
end
|
|
254
|
-
|
|
255
262
|
def on_fragment_with_type(node)
|
|
256
263
|
object_type = if node.type
|
|
257
264
|
@query.warden.get_type(node.type.name)
|
data/lib/graphql/analysis/ast.rb
CHANGED
|
@@ -21,7 +21,7 @@ module GraphQL
|
|
|
21
21
|
def analyze_multiplex(multiplex, analyzers)
|
|
22
22
|
multiplex_analyzers = analyzers.map { |analyzer| analyzer.new(multiplex) }
|
|
23
23
|
|
|
24
|
-
multiplex.
|
|
24
|
+
multiplex.current_trace.analyze_multiplex(multiplex: multiplex) do
|
|
25
25
|
query_results = multiplex.queries.map do |query|
|
|
26
26
|
if query.valid?
|
|
27
27
|
analyze_query(
|
|
@@ -48,7 +48,7 @@ module GraphQL
|
|
|
48
48
|
# @param analyzers [Array<GraphQL::Analysis::AST::Analyzer>]
|
|
49
49
|
# @return [Array<Any>] Results from those analyzers
|
|
50
50
|
def analyze_query(query, analyzers, multiplex_analyzers: [])
|
|
51
|
-
query.
|
|
51
|
+
query.current_trace.analyze_query(query: query) do
|
|
52
52
|
query_analyzers = analyzers
|
|
53
53
|
.map { |analyzer| analyzer.new(query) }
|
|
54
54
|
.select { |analyzer| analyzer.analyze? }
|
|
@@ -83,7 +83,7 @@ module GraphQL
|
|
|
83
83
|
value = if top && @override_value
|
|
84
84
|
@override_value
|
|
85
85
|
else
|
|
86
|
-
value_at(@context.query.context.namespace(:
|
|
86
|
+
value_at(@context.query.context.namespace(:interpreter_runtime)[:runtime], context_entry.path)
|
|
87
87
|
end
|
|
88
88
|
rows << [
|
|
89
89
|
"#{context_entry.ast_node ? context_entry.ast_node.position.join(":") : ""}",
|
|
@@ -112,7 +112,7 @@ module GraphQL
|
|
|
112
112
|
if object.is_a?(GraphQL::Schema::Object)
|
|
113
113
|
object = object.object
|
|
114
114
|
end
|
|
115
|
-
value = value_at(context_entry.namespace(:
|
|
115
|
+
value = value_at(context_entry.namespace(:interpreter_runtime)[:runtime], [])
|
|
116
116
|
rows << [
|
|
117
117
|
"#{position}",
|
|
118
118
|
"#{op_type}#{op_name ? " #{op_name}" : ""}",
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
module GraphQL
|
|
3
|
+
class Backtrace
|
|
4
|
+
module Trace
|
|
5
|
+
def validate(query:, validate:)
|
|
6
|
+
if query.multiplex
|
|
7
|
+
push_query_backtrace_context(query)
|
|
8
|
+
end
|
|
9
|
+
super
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def analyze_query(query:)
|
|
13
|
+
if query.multiplex # missing for stand-alone static validation
|
|
14
|
+
push_query_backtrace_context(query)
|
|
15
|
+
end
|
|
16
|
+
super
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def execute_query(query:)
|
|
20
|
+
push_query_backtrace_context(query)
|
|
21
|
+
super
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def execute_query_lazy(query:, multiplex:)
|
|
25
|
+
query ||= multiplex.queries.first
|
|
26
|
+
push_query_backtrace_context(query)
|
|
27
|
+
super
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def execute_field(field:, query:, ast_node:, arguments:, object:)
|
|
31
|
+
push_field_backtrace_context(field, query, ast_node, arguments, object)
|
|
32
|
+
super
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def execute_field_lazy(field:, query:, ast_node:, arguments:, object:)
|
|
36
|
+
push_field_backtrace_context(field, query, ast_node, arguments, object)
|
|
37
|
+
super
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def execute_multiplex(multiplex:)
|
|
41
|
+
super
|
|
42
|
+
rescue StandardError => err
|
|
43
|
+
# This is an unhandled error from execution,
|
|
44
|
+
# Re-raise it with a GraphQL trace.
|
|
45
|
+
multiplex_context = multiplex.context
|
|
46
|
+
potential_context = multiplex_context[:last_graphql_backtrace_context]
|
|
47
|
+
|
|
48
|
+
if potential_context.is_a?(GraphQL::Query::Context) ||
|
|
49
|
+
potential_context.is_a?(Backtrace::Frame)
|
|
50
|
+
raise TracedError.new(err, potential_context)
|
|
51
|
+
else
|
|
52
|
+
raise
|
|
53
|
+
end
|
|
54
|
+
ensure
|
|
55
|
+
multiplex_context = multiplex.context
|
|
56
|
+
multiplex_context.delete(:graphql_backtrace_contexts)
|
|
57
|
+
multiplex_context.delete(:last_graphql_backtrace_context)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
private
|
|
61
|
+
|
|
62
|
+
def push_query_backtrace_context(query)
|
|
63
|
+
push_data = query
|
|
64
|
+
multiplex = query.multiplex
|
|
65
|
+
push_key = []
|
|
66
|
+
push_storage = multiplex.context[:graphql_backtrace_contexts] ||= {}
|
|
67
|
+
push_storage[push_key] = push_data
|
|
68
|
+
multiplex.context[:last_graphql_backtrace_context] = push_data
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def push_field_backtrace_context(field, query, ast_node, arguments, object)
|
|
72
|
+
multiplex = query.multiplex
|
|
73
|
+
push_key = query.context[:current_path]
|
|
74
|
+
push_storage = multiplex.context[:graphql_backtrace_contexts]
|
|
75
|
+
parent_frame = push_storage[push_key[0..-2]]
|
|
76
|
+
|
|
77
|
+
if parent_frame.is_a?(GraphQL::Query)
|
|
78
|
+
parent_frame = parent_frame.context
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
push_data = Frame.new(
|
|
82
|
+
query: query,
|
|
83
|
+
path: push_key,
|
|
84
|
+
ast_node: ast_node,
|
|
85
|
+
field: field,
|
|
86
|
+
object: object,
|
|
87
|
+
arguments: arguments,
|
|
88
|
+
parent_frame: parent_frame,
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
push_storage[push_key] = push_data
|
|
92
|
+
multiplex.context[:last_graphql_backtrace_context] = push_data
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
@@ -25,7 +25,7 @@ module GraphQL
|
|
|
25
25
|
when "execute_field", "execute_field_lazy"
|
|
26
26
|
query = metadata[:query]
|
|
27
27
|
multiplex = query.multiplex
|
|
28
|
-
push_key =
|
|
28
|
+
push_key = query.context[:current_path]
|
|
29
29
|
parent_frame = multiplex.context[:graphql_backtrace_contexts][push_key[0..-2]]
|
|
30
30
|
|
|
31
31
|
if parent_frame.is_a?(GraphQL::Query)
|
data/lib/graphql/backtrace.rb
CHANGED
|
@@ -3,6 +3,7 @@ require "graphql/backtrace/inspect_result"
|
|
|
3
3
|
require "graphql/backtrace/table"
|
|
4
4
|
require "graphql/backtrace/traced_error"
|
|
5
5
|
require "graphql/backtrace/tracer"
|
|
6
|
+
require "graphql/backtrace/trace"
|
|
6
7
|
module GraphQL
|
|
7
8
|
# Wrap unhandled errors with {TracedError}.
|
|
8
9
|
#
|
|
@@ -23,7 +24,7 @@ module GraphQL
|
|
|
23
24
|
def_delegators :to_a, :each, :[]
|
|
24
25
|
|
|
25
26
|
def self.use(schema_defn)
|
|
26
|
-
schema_defn.
|
|
27
|
+
schema_defn.trace_with(self::Trace)
|
|
27
28
|
end
|
|
28
29
|
|
|
29
30
|
def initialize(context, value: nil)
|
|
@@ -7,9 +7,9 @@ module GraphQL
|
|
|
7
7
|
# @api private
|
|
8
8
|
def setup(dataloader)
|
|
9
9
|
# These keys have been requested but haven't been fetched yet
|
|
10
|
-
@
|
|
10
|
+
@pending = {}
|
|
11
11
|
# These keys have been passed to `fetch` but haven't been finished yet
|
|
12
|
-
@
|
|
12
|
+
@fetching = {}
|
|
13
13
|
# { key => result }
|
|
14
14
|
@results = {}
|
|
15
15
|
@dataloader = dataloader
|
|
@@ -18,42 +18,66 @@ module GraphQL
|
|
|
18
18
|
attr_reader :dataloader
|
|
19
19
|
|
|
20
20
|
# @return [Dataloader::Request] a pending request for a value from `key`. Call `.load` on that object to wait for the result.
|
|
21
|
-
def request(
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
def request(value)
|
|
22
|
+
res_key = result_key_for(value)
|
|
23
|
+
if !@results.key?(res_key)
|
|
24
|
+
@pending[res_key] ||= value
|
|
24
25
|
end
|
|
25
|
-
Dataloader::Request.new(self,
|
|
26
|
+
Dataloader::Request.new(self, value)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Implement this method to return a stable identifier if different
|
|
30
|
+
# key objects should load the same data value.
|
|
31
|
+
#
|
|
32
|
+
# @param value [Object] A value passed to `.request` or `.load`, for which a value will be loaded
|
|
33
|
+
# @return [Object] The key for tracking this pending data
|
|
34
|
+
def result_key_for(value)
|
|
35
|
+
value
|
|
26
36
|
end
|
|
27
37
|
|
|
28
38
|
# @return [Dataloader::Request] a pending request for a values from `keys`. Call `.load` on that object to wait for the results.
|
|
29
|
-
def request_all(
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
39
|
+
def request_all(values)
|
|
40
|
+
values.each do |v|
|
|
41
|
+
res_key = result_key_for(v)
|
|
42
|
+
if !@results.key?(res_key)
|
|
43
|
+
@pending[res_key] ||= v
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
Dataloader::RequestAll.new(self, values)
|
|
33
47
|
end
|
|
34
48
|
|
|
35
|
-
# @param
|
|
49
|
+
# @param value [Object] A loading value which will be passed to {#fetch} if it isn't already in the internal cache.
|
|
36
50
|
# @return [Object] The result from {#fetch} for `key`. If `key` hasn't been loaded yet, the Fiber will yield until it's loaded.
|
|
37
|
-
def load(
|
|
38
|
-
|
|
39
|
-
|
|
51
|
+
def load(value)
|
|
52
|
+
result_key = result_key_for(value)
|
|
53
|
+
if @results.key?(result_key)
|
|
54
|
+
result_for(result_key)
|
|
40
55
|
else
|
|
41
|
-
@
|
|
42
|
-
sync
|
|
43
|
-
result_for(
|
|
56
|
+
@pending[result_key] ||= value
|
|
57
|
+
sync([result_key])
|
|
58
|
+
result_for(result_key)
|
|
44
59
|
end
|
|
45
60
|
end
|
|
46
61
|
|
|
47
|
-
# @param
|
|
62
|
+
# @param values [Array<Object>] Loading keys which will be passed to `#fetch` (or read from the internal cache).
|
|
48
63
|
# @return [Object] The result from {#fetch} for `keys`. If `keys` haven't been loaded yet, the Fiber will yield until they're loaded.
|
|
49
|
-
def load_all(
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
64
|
+
def load_all(values)
|
|
65
|
+
result_keys = []
|
|
66
|
+
pending_keys = []
|
|
67
|
+
values.each { |v|
|
|
68
|
+
k = result_key_for(v)
|
|
69
|
+
result_keys << k
|
|
70
|
+
if !@results.key?(k)
|
|
71
|
+
@pending[k] ||= v
|
|
72
|
+
pending_keys << k
|
|
73
|
+
end
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if pending_keys.any?
|
|
77
|
+
sync(pending_keys)
|
|
54
78
|
end
|
|
55
79
|
|
|
56
|
-
|
|
80
|
+
result_keys.map { |k| result_for(k) }
|
|
57
81
|
end
|
|
58
82
|
|
|
59
83
|
# Subclasses must implement this method to return a value for each of `keys`
|
|
@@ -67,14 +91,13 @@ module GraphQL
|
|
|
67
91
|
# Wait for a batch, if there's anything to batch.
|
|
68
92
|
# Then run the batch and update the cache.
|
|
69
93
|
# @return [void]
|
|
70
|
-
def sync
|
|
71
|
-
pending_keys = @pending_keys.dup
|
|
94
|
+
def sync(pending_result_keys)
|
|
72
95
|
@dataloader.yield
|
|
73
96
|
iterations = 0
|
|
74
|
-
while
|
|
97
|
+
while pending_result_keys.any? { |key| !@results.key?(key) }
|
|
75
98
|
iterations += 1
|
|
76
99
|
if iterations > 1000
|
|
77
|
-
raise "#{self.class}#sync tried 1000 times to load pending keys (#{
|
|
100
|
+
raise "#{self.class}#sync tried 1000 times to load pending keys (#{pending_result_keys}), but they still weren't loaded. There is likely a circular dependency."
|
|
78
101
|
end
|
|
79
102
|
@dataloader.yield
|
|
80
103
|
end
|
|
@@ -83,15 +106,18 @@ module GraphQL
|
|
|
83
106
|
|
|
84
107
|
# @return [Boolean] True if this source has any pending requests for data.
|
|
85
108
|
def pending?
|
|
86
|
-
!@
|
|
109
|
+
!@pending.empty?
|
|
87
110
|
end
|
|
88
111
|
|
|
89
112
|
# Add these key-value pairs to this source's cache
|
|
90
113
|
# (future loads will use these merged values).
|
|
91
|
-
# @param
|
|
114
|
+
# @param new_results [Hash<Object => Object>] key-value pairs to cache in this source
|
|
92
115
|
# @return [void]
|
|
93
|
-
def merge(
|
|
94
|
-
|
|
116
|
+
def merge(new_results)
|
|
117
|
+
new_results.each do |new_k, new_v|
|
|
118
|
+
key = result_key_for(new_k)
|
|
119
|
+
@results[key] = new_v
|
|
120
|
+
end
|
|
95
121
|
nil
|
|
96
122
|
end
|
|
97
123
|
|
|
@@ -99,24 +125,22 @@ module GraphQL
|
|
|
99
125
|
# @api private
|
|
100
126
|
# @return [void]
|
|
101
127
|
def run_pending_keys
|
|
102
|
-
if !@
|
|
103
|
-
@
|
|
128
|
+
if !@fetching.empty?
|
|
129
|
+
@fetching.each_key { |k| @pending.delete(k) }
|
|
104
130
|
end
|
|
105
|
-
return if @
|
|
106
|
-
|
|
107
|
-
@
|
|
108
|
-
@
|
|
109
|
-
results = fetch(
|
|
110
|
-
|
|
131
|
+
return if @pending.empty?
|
|
132
|
+
fetch_h = @pending
|
|
133
|
+
@pending = {}
|
|
134
|
+
@fetching.merge!(fetch_h)
|
|
135
|
+
results = fetch(fetch_h.values)
|
|
136
|
+
fetch_h.each_with_index do |(key, _value), idx|
|
|
111
137
|
@results[key] = results[idx]
|
|
112
138
|
end
|
|
113
139
|
nil
|
|
114
140
|
rescue StandardError => error
|
|
115
|
-
|
|
141
|
+
fetch_h.each_key { |key| @results[key] = error }
|
|
116
142
|
ensure
|
|
117
|
-
|
|
118
|
-
@fetching_keys -= fetch_keys
|
|
119
|
-
end
|
|
143
|
+
fetch_h && fetch_h.each_key { |k| @fetching.delete(k) }
|
|
120
144
|
end
|
|
121
145
|
|
|
122
146
|
# These arguments are given to `dataloader.with(source_class, ...)`. The object
|
|
@@ -137,7 +161,7 @@ module GraphQL
|
|
|
137
161
|
[*batch_args, **batch_kwargs]
|
|
138
162
|
end
|
|
139
163
|
|
|
140
|
-
attr_reader :
|
|
164
|
+
attr_reader :pending
|
|
141
165
|
|
|
142
166
|
private
|
|
143
167
|
|
data/lib/graphql/dataloader.rb
CHANGED
|
@@ -111,8 +111,8 @@ module GraphQL
|
|
|
111
111
|
@source_cache.each do |source_class, batched_sources|
|
|
112
112
|
batched_sources.each do |batch_args, batched_source_instance|
|
|
113
113
|
if batched_source_instance.pending?
|
|
114
|
-
prev_pending_keys[batched_source_instance] = batched_source_instance.
|
|
115
|
-
batched_source_instance.
|
|
114
|
+
prev_pending_keys[batched_source_instance] = batched_source_instance.pending.dup
|
|
115
|
+
batched_source_instance.pending.clear
|
|
116
116
|
end
|
|
117
117
|
end
|
|
118
118
|
end
|
|
@@ -127,8 +127,8 @@ module GraphQL
|
|
|
127
127
|
res
|
|
128
128
|
ensure
|
|
129
129
|
@pending_jobs = prev_queue
|
|
130
|
-
prev_pending_keys.each do |source_instance,
|
|
131
|
-
source_instance.
|
|
130
|
+
prev_pending_keys.each do |source_instance, pending|
|
|
131
|
+
source_instance.pending.merge!(pending)
|
|
132
132
|
end
|
|
133
133
|
end
|
|
134
134
|
|
|
@@ -289,7 +289,10 @@ module GraphQL
|
|
|
289
289
|
fiber_locals = {}
|
|
290
290
|
|
|
291
291
|
Thread.current.keys.each do |fiber_var_key|
|
|
292
|
-
|
|
292
|
+
# This variable should be fresh in each new fiber
|
|
293
|
+
if fiber_var_key != :__graphql_runtime_info
|
|
294
|
+
fiber_locals[fiber_var_key] = Thread.current[fiber_var_key]
|
|
295
|
+
end
|
|
293
296
|
end
|
|
294
297
|
|
|
295
298
|
if @nonblocking
|