graphql 2.4.3 → 2.5.3
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/analyzer.rb +2 -1
- data/lib/graphql/analysis/query_complexity.rb +87 -7
- data/lib/graphql/analysis/visitor.rb +38 -41
- data/lib/graphql/analysis.rb +15 -12
- data/lib/graphql/autoload.rb +38 -0
- data/lib/graphql/backtrace/table.rb +118 -55
- data/lib/graphql/backtrace.rb +1 -19
- data/lib/graphql/current.rb +7 -2
- 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/bootstrap-5.3.3.min.css +6 -0
- data/lib/graphql/dashboard/statics/bootstrap-5.3.3.min.js +7 -0
- data/lib/graphql/dashboard/statics/charts.min.css +1 -0
- data/lib/graphql/dashboard/statics/dashboard.css +30 -0
- data/lib/graphql/dashboard/statics/dashboard.js +143 -0
- data/lib/graphql/dashboard/statics/header-icon.png +0 -0
- data/lib/graphql/dashboard/statics/icon.png +0 -0
- 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/landings/show.html.erb +18 -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 +108 -0
- data/lib/graphql/dashboard.rb +158 -0
- data/lib/graphql/dataloader/active_record_association_source.rb +64 -0
- data/lib/graphql/dataloader/active_record_source.rb +26 -0
- data/lib/graphql/dataloader/async_dataloader.rb +21 -9
- data/lib/graphql/dataloader/null_dataloader.rb +1 -1
- data/lib/graphql/dataloader/source.rb +3 -3
- data/lib/graphql/dataloader.rb +43 -14
- 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 -4
- data/lib/graphql/execution/interpreter/runtime.rb +96 -52
- data/lib/graphql/execution/interpreter.rb +16 -7
- data/lib/graphql/execution/multiplex.rb +6 -5
- data/lib/graphql/introspection/directive_location_enum.rb +1 -1
- data/lib/graphql/invalid_name_error.rb +1 -1
- data/lib/graphql/invalid_null_error.rb +19 -16
- data/lib/graphql/language/cache.rb +13 -0
- data/lib/graphql/language/document_from_schema_definition.rb +8 -7
- data/lib/graphql/language/lexer.rb +11 -4
- data/lib/graphql/language/nodes.rb +3 -0
- data/lib/graphql/language/parser.rb +15 -8
- data/lib/graphql/language/printer.rb +8 -8
- data/lib/graphql/language/static_visitor.rb +37 -33
- data/lib/graphql/language/visitor.rb +59 -55
- data/lib/graphql/pagination/connection.rb +1 -1
- data/lib/graphql/query/context/scoped_context.rb +1 -1
- data/lib/graphql/query/context.rb +7 -5
- data/lib/graphql/query/variable_validation_error.rb +1 -1
- data/lib/graphql/query.rb +22 -32
- data/lib/graphql/railtie.rb +7 -0
- data/lib/graphql/schema/addition.rb +1 -1
- data/lib/graphql/schema/always_visible.rb +1 -0
- data/lib/graphql/schema/argument.rb +7 -8
- data/lib/graphql/schema/build_from_definition.rb +99 -53
- data/lib/graphql/schema/directive/flagged.rb +3 -1
- data/lib/graphql/schema/directive.rb +2 -2
- data/lib/graphql/schema/enum.rb +36 -1
- data/lib/graphql/schema/enum_value.rb +1 -1
- data/lib/graphql/schema/field/scope_extension.rb +1 -1
- data/lib/graphql/schema/field.rb +27 -13
- data/lib/graphql/schema/field_extension.rb +1 -1
- data/lib/graphql/schema/has_single_input_argument.rb +3 -1
- data/lib/graphql/schema/input_object.rb +77 -40
- data/lib/graphql/schema/interface.rb +3 -2
- data/lib/graphql/schema/list.rb +1 -1
- data/lib/graphql/schema/loader.rb +1 -1
- data/lib/graphql/schema/member/has_arguments.rb +25 -17
- data/lib/graphql/schema/member/has_dataloader.rb +62 -0
- data/lib/graphql/schema/member/has_deprecation_reason.rb +15 -0
- data/lib/graphql/schema/member/has_directives.rb +4 -4
- data/lib/graphql/schema/member/has_fields.rb +19 -1
- data/lib/graphql/schema/member/has_interfaces.rb +5 -5
- data/lib/graphql/schema/member/has_validators.rb +1 -1
- data/lib/graphql/schema/member/scoped.rb +1 -1
- data/lib/graphql/schema/member/type_system_helpers.rb +1 -1
- data/lib/graphql/schema/member.rb +1 -0
- data/lib/graphql/schema/object.rb +25 -8
- data/lib/graphql/schema/relay_classic_mutation.rb +0 -1
- data/lib/graphql/schema/resolver.rb +12 -10
- data/lib/graphql/schema/subscription.rb +52 -6
- data/lib/graphql/schema/union.rb +1 -1
- data/lib/graphql/schema/validator/required_validator.rb +23 -6
- data/lib/graphql/schema/validator.rb +1 -1
- data/lib/graphql/schema/visibility/migration.rb +1 -0
- data/lib/graphql/schema/visibility/profile.rb +98 -244
- data/lib/graphql/schema/visibility/visit.rb +190 -0
- data/lib/graphql/schema/visibility.rb +178 -38
- data/lib/graphql/schema/warden.rb +18 -5
- data/lib/graphql/schema.rb +266 -54
- data/lib/graphql/static_validation/all_rules.rb +1 -1
- data/lib/graphql/static_validation/rules/argument_names_are_unique.rb +1 -1
- data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +47 -13
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +79 -17
- data/lib/graphql/static_validation/rules/fields_will_merge_error.rb +10 -2
- data/lib/graphql/static_validation/rules/no_definitions_are_present.rb +1 -1
- data/lib/graphql/static_validation/rules/not_single_subscription_error.rb +25 -0
- data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +1 -1
- 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 +1 -1
- data/lib/graphql/static_validation/rules/variable_names_are_unique.rb +1 -1
- data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +1 -1
- data/lib/graphql/static_validation/validation_context.rb +1 -0
- data/lib/graphql/static_validation/validator.rb +6 -1
- data/lib/graphql/subscriptions/action_cable_subscriptions.rb +1 -1
- data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +12 -10
- data/lib/graphql/subscriptions/event.rb +12 -1
- data/lib/graphql/subscriptions/serialize.rb +1 -1
- data/lib/graphql/subscriptions.rb +1 -1
- data/lib/graphql/testing/helpers.rb +7 -4
- data/lib/graphql/tracing/active_support_notifications_trace.rb +14 -3
- data/lib/graphql/tracing/active_support_notifications_tracing.rb +1 -1
- data/lib/graphql/tracing/appoptics_trace.rb +9 -1
- data/lib/graphql/tracing/appoptics_tracing.rb +7 -0
- data/lib/graphql/tracing/appsignal_trace.rb +32 -55
- data/lib/graphql/tracing/appsignal_tracing.rb +2 -0
- data/lib/graphql/tracing/call_legacy_tracers.rb +66 -0
- data/lib/graphql/tracing/data_dog_trace.rb +46 -158
- data/lib/graphql/tracing/data_dog_tracing.rb +2 -0
- data/lib/graphql/tracing/detailed_trace/memory_backend.rb +60 -0
- data/lib/graphql/tracing/detailed_trace/redis_backend.rb +72 -0
- data/lib/graphql/tracing/detailed_trace.rb +93 -0
- data/lib/graphql/tracing/legacy_hooks_trace.rb +1 -0
- data/lib/graphql/tracing/legacy_trace.rb +4 -61
- data/lib/graphql/tracing/monitor_trace.rb +283 -0
- data/lib/graphql/tracing/new_relic_trace.rb +47 -54
- data/lib/graphql/tracing/new_relic_tracing.rb +2 -0
- data/lib/graphql/tracing/notifications_trace.rb +182 -34
- data/lib/graphql/tracing/notifications_tracing.rb +2 -0
- data/lib/graphql/tracing/null_trace.rb +9 -0
- data/lib/graphql/tracing/perfetto_trace/trace.proto +141 -0
- data/lib/graphql/tracing/perfetto_trace/trace_pb.rb +33 -0
- data/lib/graphql/tracing/perfetto_trace.rb +734 -0
- data/lib/graphql/tracing/platform_trace.rb +5 -0
- data/lib/graphql/tracing/prometheus_trace/graphql_collector.rb +2 -0
- data/lib/graphql/tracing/prometheus_trace.rb +72 -68
- data/lib/graphql/tracing/prometheus_tracing.rb +2 -0
- data/lib/graphql/tracing/scout_trace.rb +32 -55
- data/lib/graphql/tracing/scout_tracing.rb +2 -0
- data/lib/graphql/tracing/sentry_trace.rb +62 -94
- data/lib/graphql/tracing/statsd_trace.rb +33 -41
- data/lib/graphql/tracing/statsd_tracing.rb +2 -0
- data/lib/graphql/tracing/trace.rb +111 -1
- data/lib/graphql/tracing.rb +31 -30
- data/lib/graphql/types/relay/connection_behaviors.rb +3 -3
- data/lib/graphql/types/relay/edge_behaviors.rb +2 -2
- data/lib/graphql/types.rb +18 -11
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +55 -47
- metadata +146 -11
- data/lib/graphql/backtrace/inspect_result.rb +0 -38
- data/lib/graphql/backtrace/trace.rb +0 -93
- data/lib/graphql/backtrace/tracer.rb +0 -80
- data/lib/graphql/schema/null_mask.rb +0 -11
- data/lib/graphql/static_validation/rules/subscription_root_exists.rb +0 -17
@@ -33,26 +33,19 @@ module GraphQL
|
|
33
33
|
|
34
34
|
private
|
35
35
|
|
36
|
-
def
|
37
|
-
@
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
def arg_conflicts
|
43
|
-
@arg_conflicts ||= Hash.new do |errors, field|
|
44
|
-
errors[field] = GraphQL::StaticValidation::FieldsWillMergeError.new(kind: :argument, field_name: field)
|
36
|
+
def conflicts
|
37
|
+
@conflicts ||= Hash.new do |h, error_type|
|
38
|
+
h[error_type] = Hash.new do |h2, field_name|
|
39
|
+
h2[field_name] = GraphQL::StaticValidation::FieldsWillMergeError.new(kind: error_type, field_name: field_name)
|
40
|
+
end
|
45
41
|
end
|
46
42
|
end
|
47
43
|
|
48
44
|
def setting_errors
|
49
|
-
@
|
50
|
-
@arg_conflicts = nil
|
51
|
-
|
45
|
+
@conflicts = nil
|
52
46
|
yield
|
53
47
|
# don't initialize these if they weren't initialized in the block:
|
54
|
-
@
|
55
|
-
@arg_conflicts && @arg_conflicts.each_value { |error| add_error(error) }
|
48
|
+
@conflicts&.each_value { |error_type| error_type.each_value { |error| add_error(error) } }
|
56
49
|
end
|
57
50
|
|
58
51
|
def conflicts_within_selection_set(node, parent_type)
|
@@ -222,7 +215,7 @@ module GraphQL
|
|
222
215
|
|
223
216
|
if !are_mutually_exclusive
|
224
217
|
if node1.name != node2.name
|
225
|
-
conflict =
|
218
|
+
conflict = conflicts[:field][response_key]
|
226
219
|
|
227
220
|
conflict.add_conflict(node1, node1.name)
|
228
221
|
conflict.add_conflict(node2, node2.name)
|
@@ -231,7 +224,7 @@ module GraphQL
|
|
231
224
|
end
|
232
225
|
|
233
226
|
if !same_arguments?(node1, node2)
|
234
|
-
conflict =
|
227
|
+
conflict = conflicts[:argument][response_key]
|
235
228
|
|
236
229
|
conflict.add_conflict(node1, GraphQL::Language.serialize(serialize_field_args(node1)))
|
237
230
|
conflict.add_conflict(node2, GraphQL::Language.serialize(serialize_field_args(node2)))
|
@@ -240,6 +233,49 @@ module GraphQL
|
|
240
233
|
end
|
241
234
|
end
|
242
235
|
|
236
|
+
if !conflicts[:field].key?(response_key) &&
|
237
|
+
(t1 = field1.definition&.type) &&
|
238
|
+
(t2 = field2.definition&.type) &&
|
239
|
+
return_types_conflict?(t1, t2)
|
240
|
+
|
241
|
+
return_error = nil
|
242
|
+
message_override = nil
|
243
|
+
case @schema.allow_legacy_invalid_return_type_conflicts
|
244
|
+
when false
|
245
|
+
return_error = true
|
246
|
+
when true
|
247
|
+
legacy_handling = @schema.legacy_invalid_return_type_conflicts(@context.query, t1, t2, node1, node2)
|
248
|
+
case legacy_handling
|
249
|
+
when nil
|
250
|
+
return_error = false
|
251
|
+
when :return_validation_error
|
252
|
+
return_error = true
|
253
|
+
when String
|
254
|
+
return_error = true
|
255
|
+
message_override = legacy_handling
|
256
|
+
else
|
257
|
+
raise GraphQL::Error, "#{@schema}.legacy_invalid_scalar_conflicts returned unexpected value: #{legacy_handling.inspect}. Expected `nil`, String, or `:return_validation_error`."
|
258
|
+
end
|
259
|
+
else
|
260
|
+
return_error = false
|
261
|
+
@context.query.logger.warn <<~WARN
|
262
|
+
GraphQL-Ruby encountered mismatched types in this query: `#{t1.to_type_signature}` (at #{node1.line}:#{node1.col}) vs. `#{t2.to_type_signature}` (at #{node2.line}:#{node2.col}).
|
263
|
+
This will return an error in future GraphQL-Ruby versions, as per the GraphQL specification
|
264
|
+
Learn about migrating here: https://graphql-ruby.org/api-doc/#{GraphQL::VERSION}/GraphQL/Schema.html#allow_legacy_invalid_return_type_conflicts-class_method
|
265
|
+
WARN
|
266
|
+
end
|
267
|
+
|
268
|
+
if return_error
|
269
|
+
conflict = conflicts[:return_type][response_key]
|
270
|
+
if message_override
|
271
|
+
conflict.message = message_override
|
272
|
+
end
|
273
|
+
conflict.add_conflict(node1, "`#{t1.to_type_signature}`")
|
274
|
+
conflict.add_conflict(node2, "`#{t2.to_type_signature}`")
|
275
|
+
@conflict_count += 1
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
243
279
|
find_conflicts_between_sub_selection_sets(
|
244
280
|
field1,
|
245
281
|
field2,
|
@@ -247,6 +283,32 @@ module GraphQL
|
|
247
283
|
)
|
248
284
|
end
|
249
285
|
|
286
|
+
def return_types_conflict?(type1, type2)
|
287
|
+
if type1.list?
|
288
|
+
if type2.list?
|
289
|
+
return_types_conflict?(type1.of_type, type2.of_type)
|
290
|
+
else
|
291
|
+
true
|
292
|
+
end
|
293
|
+
elsif type2.list?
|
294
|
+
true
|
295
|
+
elsif type1.non_null?
|
296
|
+
if type2.non_null?
|
297
|
+
return_types_conflict?(type1.of_type, type2.of_type)
|
298
|
+
else
|
299
|
+
true
|
300
|
+
end
|
301
|
+
elsif type2.non_null?
|
302
|
+
true
|
303
|
+
elsif type1.kind.leaf? && type2.kind.leaf?
|
304
|
+
type1 != type2
|
305
|
+
else
|
306
|
+
# One or more of these are composite types,
|
307
|
+
# their selections will be validated later on.
|
308
|
+
false
|
309
|
+
end
|
310
|
+
end
|
311
|
+
|
250
312
|
def find_conflicts_between_sub_selection_sets(field1, field2, mutually_exclusive:)
|
251
313
|
return if field1.definition.nil? ||
|
252
314
|
field2.definition.nil? ||
|
@@ -345,7 +407,7 @@ module GraphQL
|
|
345
407
|
fields << Field.new(node, definition, owner_type, parents)
|
346
408
|
when GraphQL::Language::Nodes::InlineFragment
|
347
409
|
fragment_type = node.type ? @types.type(node.type.name) : owner_type
|
348
|
-
find_fields_and_fragments(node.selections, parents: [*parents, fragment_type], owner_type:
|
410
|
+
find_fields_and_fragments(node.selections, parents: [*parents, fragment_type], owner_type: fragment_type, fields: fields, fragment_spreads: fragment_spreads) if fragment_type
|
349
411
|
when GraphQL::Language::Nodes::FragmentSpread
|
350
412
|
fragment_spreads << FragmentSpread.new(node.name, parents)
|
351
413
|
end
|
@@ -14,9 +14,11 @@ module GraphQL
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def message
|
17
|
-
"Field '#{field_name}' has #{kind == :argument ? 'an' : 'a'} #{kind} conflict: #{conflicts}?"
|
17
|
+
@message || "Field '#{field_name}' has #{kind == :argument ? 'an' : 'a'} #{kind} conflict: #{conflicts}?"
|
18
18
|
end
|
19
19
|
|
20
|
+
attr_writer :message
|
21
|
+
|
20
22
|
def path
|
21
23
|
[]
|
22
24
|
end
|
@@ -26,7 +28,13 @@ module GraphQL
|
|
26
28
|
end
|
27
29
|
|
28
30
|
def add_conflict(node, conflict_str)
|
29
|
-
|
31
|
+
# Can't use `.include?` here because AST nodes implement `#==`
|
32
|
+
# based on string value, not including location. But sometimes,
|
33
|
+
# identical nodes conflict because of their differing return types.
|
34
|
+
if nodes.any? { |n| n == node && n.line == node.line && n.col == node.col }
|
35
|
+
# already have an error for this node
|
36
|
+
return
|
37
|
+
end
|
30
38
|
|
31
39
|
@nodes << node
|
32
40
|
@conflicts << conflict_str
|
@@ -32,7 +32,7 @@ module GraphQL
|
|
32
32
|
|
33
33
|
def on_document(node, parent)
|
34
34
|
super
|
35
|
-
if
|
35
|
+
if !@schema_definition_nodes.empty?
|
36
36
|
add_error(GraphQL::StaticValidation::NoDefinitionsArePresentError.new(%|Query cannot contain schema definitions|, nodes: @schema_definition_nodes))
|
37
37
|
end
|
38
38
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module GraphQL
|
3
|
+
module StaticValidation
|
4
|
+
class NotSingleSubscriptionError < StaticValidation::Error
|
5
|
+
def initialize(message, path: nil, nodes: [])
|
6
|
+
super(message, path: path, nodes: nodes)
|
7
|
+
end
|
8
|
+
|
9
|
+
# A hash representation of this Message
|
10
|
+
def to_h
|
11
|
+
extensions = {
|
12
|
+
"code" => code,
|
13
|
+
}
|
14
|
+
|
15
|
+
super.merge({
|
16
|
+
"extensions" => extensions
|
17
|
+
})
|
18
|
+
end
|
19
|
+
|
20
|
+
def code
|
21
|
+
"notSingleSubscription"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -24,7 +24,7 @@ module GraphQL
|
|
24
24
|
.map!(&:name)
|
25
25
|
|
26
26
|
missing_names = required_argument_names - present_argument_names
|
27
|
-
if missing_names.
|
27
|
+
if !missing_names.empty?
|
28
28
|
add_error(GraphQL::StaticValidation::RequiredArgumentsArePresentError.new(
|
29
29
|
"#{ast_node.class.name.split("::").last} '#{ast_node.name}' is missing required arguments: #{missing_names.join(", ")}",
|
30
30
|
nodes: ast_node,
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module GraphQL
|
3
|
+
module StaticValidation
|
4
|
+
module SubscriptionRootExistsAndSingleSubscriptionSelection
|
5
|
+
def on_operation_definition(node, parent)
|
6
|
+
if node.operation_type == "subscription"
|
7
|
+
if context.types.subscription_root.nil?
|
8
|
+
add_error(GraphQL::StaticValidation::SubscriptionRootExistsError.new(
|
9
|
+
'Schema is not configured for subscriptions',
|
10
|
+
nodes: node
|
11
|
+
))
|
12
|
+
elsif node.selections.size != 1
|
13
|
+
add_error(GraphQL::StaticValidation::NotSingleSubscriptionError.new(
|
14
|
+
'A subscription operation may only have one selection',
|
15
|
+
nodes: node,
|
16
|
+
))
|
17
|
+
else
|
18
|
+
super
|
19
|
+
end
|
20
|
+
else
|
21
|
+
super
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -4,7 +4,7 @@ module GraphQL
|
|
4
4
|
module VariableNamesAreUnique
|
5
5
|
def on_operation_definition(node, parent)
|
6
6
|
var_defns = node.variables
|
7
|
-
if var_defns.
|
7
|
+
if !var_defns.empty?
|
8
8
|
vars_by_name = Hash.new { |h, k| h[k] = [] }
|
9
9
|
var_defns.each { |v| vars_by_name[v.name] << v }
|
10
10
|
vars_by_name.each do |name, defns|
|
@@ -21,7 +21,7 @@ module GraphQL
|
|
21
21
|
end
|
22
22
|
node_values = node_values.select { |value| value.is_a? GraphQL::Language::Nodes::VariableIdentifier }
|
23
23
|
|
24
|
-
if node_values.
|
24
|
+
if !node_values.empty?
|
25
25
|
argument_owner = case parent
|
26
26
|
when GraphQL::Language::Nodes::Field
|
27
27
|
context.field_definition
|
@@ -29,6 +29,7 @@ module GraphQL
|
|
29
29
|
@visitor = visitor_class.new(document, self)
|
30
30
|
end
|
31
31
|
|
32
|
+
# TODO stop using def_delegators because of Array allocations
|
32
33
|
def_delegators :@visitor,
|
33
34
|
:path, :type_definition, :field_definition, :argument_definition,
|
34
35
|
:parent_type_definition, :directive_definition, :object_types, :dependencies
|
@@ -27,6 +27,8 @@ module GraphQL
|
|
27
27
|
# @param max_errors [Integer] Maximum number of errors before aborting validation. Any positive number will limit the number of errors. Defaults to nil for no limit.
|
28
28
|
# @return [Array<Hash>]
|
29
29
|
def validate(query, validate: true, timeout: nil, max_errors: nil)
|
30
|
+
errors = nil
|
31
|
+
query.current_trace.begin_validate(query, validate)
|
30
32
|
query.current_trace.validate(validate: validate, query: query) do
|
31
33
|
begin_t = Time.now
|
32
34
|
errors = if validate == false
|
@@ -58,10 +60,13 @@ module GraphQL
|
|
58
60
|
}
|
59
61
|
end
|
60
62
|
rescue GraphQL::ExecutionError => e
|
63
|
+
errors = [e]
|
61
64
|
{
|
62
65
|
remaining_timeout: nil,
|
63
|
-
errors:
|
66
|
+
errors: errors,
|
64
67
|
}
|
68
|
+
ensure
|
69
|
+
query.current_trace.end_validate(query, validate, errors)
|
65
70
|
end
|
66
71
|
|
67
72
|
# Invoked when static validation times out.
|
@@ -171,7 +171,7 @@ module GraphQL
|
|
171
171
|
events_by_fingerprint = @events[topic]
|
172
172
|
object = nil
|
173
173
|
events_by_fingerprint.each do |_fingerprint, events|
|
174
|
-
if events.
|
174
|
+
if !events.empty? && events.first == initial_event
|
175
175
|
# The fingerprint has told us that this response should be shared by all subscribers,
|
176
176
|
# so just run it once, then deliver the result to every subscriber
|
177
177
|
first_event = events.first
|
@@ -20,12 +20,22 @@ module GraphQL
|
|
20
20
|
def after_resolve(value:, context:, object:, arguments:, **rest)
|
21
21
|
if value.is_a?(GraphQL::ExecutionError)
|
22
22
|
value
|
23
|
+
elsif @field.resolver&.method_defined?(:subscription_written?) &&
|
24
|
+
(subscription_namespace = context.namespace(:subscriptions)) &&
|
25
|
+
(subscriptions_by_path = subscription_namespace[:subscriptions])
|
26
|
+
(subscription_instance = subscriptions_by_path[context.current_path])
|
27
|
+
# If it was already written, don't append this event to be written later
|
28
|
+
if !subscription_instance.subscription_written?
|
29
|
+
events = context.namespace(:subscriptions)[:events]
|
30
|
+
events << subscription_instance.event
|
31
|
+
end
|
32
|
+
value
|
23
33
|
elsif (events = context.namespace(:subscriptions)[:events])
|
24
34
|
# This is the first execution, so gather an Event
|
25
35
|
# for the backend to register:
|
26
36
|
event = Subscriptions::Event.new(
|
27
37
|
name: field.name,
|
28
|
-
arguments:
|
38
|
+
arguments: arguments,
|
29
39
|
context: context,
|
30
40
|
field: field,
|
31
41
|
)
|
@@ -33,7 +43,7 @@ module GraphQL
|
|
33
43
|
value
|
34
44
|
elsif context.query.subscription_topic == Subscriptions::Event.serialize(
|
35
45
|
field.name,
|
36
|
-
|
46
|
+
arguments,
|
37
47
|
field,
|
38
48
|
scope: (field.subscription_scope ? context[field.subscription_scope] : nil),
|
39
49
|
)
|
@@ -45,14 +55,6 @@ module GraphQL
|
|
45
55
|
context.skip
|
46
56
|
end
|
47
57
|
end
|
48
|
-
|
49
|
-
private
|
50
|
-
|
51
|
-
def arguments_without_field_extras(arguments:)
|
52
|
-
arguments.dup.tap do |event_args|
|
53
|
-
field.extras.each { |k| event_args.delete(k) }
|
54
|
-
end
|
55
|
-
end
|
56
58
|
end
|
57
59
|
end
|
58
60
|
end
|
@@ -20,7 +20,7 @@ module GraphQL
|
|
20
20
|
|
21
21
|
def initialize(name:, arguments:, field: nil, context: nil, scope: nil)
|
22
22
|
@name = name
|
23
|
-
@arguments = arguments
|
23
|
+
@arguments = self.class.arguments_without_field_extras(arguments: arguments, field: field)
|
24
24
|
@context = context
|
25
25
|
field ||= context.field
|
26
26
|
scope_key = field.subscription_scope
|
@@ -39,6 +39,7 @@ module GraphQL
|
|
39
39
|
# @return [String] an identifier for this unit of subscription
|
40
40
|
def self.serialize(_name, arguments, field, scope:, context: GraphQL::Query::NullContext.instance)
|
41
41
|
subscription = field.resolver || GraphQL::Schema::Subscription
|
42
|
+
arguments = arguments_without_field_extras(field: field, arguments: arguments)
|
42
43
|
normalized_args = stringify_args(field, arguments.to_h, context)
|
43
44
|
subscription.topic_for(arguments: normalized_args, field: field, scope: scope)
|
44
45
|
end
|
@@ -60,6 +61,16 @@ module GraphQL
|
|
60
61
|
end
|
61
62
|
|
62
63
|
class << self
|
64
|
+
def arguments_without_field_extras(arguments:, field:)
|
65
|
+
if !field.extras.empty?
|
66
|
+
arguments = arguments.dup
|
67
|
+
field.extras.each do |extra_key|
|
68
|
+
arguments.delete(extra_key)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
arguments
|
72
|
+
end
|
73
|
+
|
63
74
|
private
|
64
75
|
|
65
76
|
# This method does not support cyclic references in the Hash,
|
@@ -146,7 +146,7 @@ module GraphQL
|
|
146
146
|
elsif obj.is_a?(Date) || obj.is_a?(Time)
|
147
147
|
# DateTime extends Date; for TimeWithZone, call `.utc` first.
|
148
148
|
{ TIMESTAMP_KEY => [obj.class.name, obj.strftime(TIMESTAMP_FORMAT)] }
|
149
|
-
elsif obj.is_a?(OpenStruct)
|
149
|
+
elsif defined?(OpenStruct) && obj.is_a?(OpenStruct)
|
150
150
|
{ OPEN_STRUCT_KEY => dump_value(obj.to_h) }
|
151
151
|
elsif defined?(ActiveRecord::Relation) && obj.is_a?(ActiveRecord::Relation)
|
152
152
|
dump_value(obj.to_a)
|
@@ -43,22 +43,25 @@ module GraphQL
|
|
43
43
|
type_name, *field_names = field_path.split(".")
|
44
44
|
dummy_query = GraphQL::Query.new(schema, "{ __typename }", context: context)
|
45
45
|
query_context = dummy_query.context
|
46
|
+
dataloader = query_context.dataloader
|
46
47
|
object_type = dummy_query.types.type(type_name) # rubocop:disable Development/ContextIsPassedCop
|
47
48
|
if object_type
|
48
49
|
graphql_result = object
|
49
50
|
field_names.each do |field_name|
|
50
51
|
inner_object = graphql_result
|
51
|
-
|
52
|
+
dataloader.run_isolated {
|
53
|
+
graphql_result = object_type.wrap(inner_object, query_context)
|
54
|
+
}
|
52
55
|
if graphql_result.nil?
|
53
56
|
return nil
|
54
57
|
end
|
55
58
|
visible_field = dummy_query.types.field(object_type, field_name) # rubocop:disable Development/ContextIsPassedCop
|
56
59
|
if visible_field
|
57
|
-
|
60
|
+
dataloader.run_isolated {
|
58
61
|
query_context[:current_field] = visible_field
|
59
62
|
field_args = visible_field.coerce_arguments(graphql_result, arguments, query_context)
|
60
63
|
field_args = schema.sync_lazy(field_args)
|
61
|
-
if visible_field.extras.
|
64
|
+
if !visible_field.extras.empty?
|
62
65
|
extra_args = {}
|
63
66
|
visible_field.extras.each do |extra|
|
64
67
|
extra_args[extra] = case extra
|
@@ -92,7 +95,7 @@ module GraphQL
|
|
92
95
|
end
|
93
96
|
graphql_result
|
94
97
|
else
|
95
|
-
unfiltered_type =
|
98
|
+
unfiltered_type = schema.use_visibility_profile? ? schema.visibility.get_type(type_name) : schema.get_type(type_name) # rubocop:disable Development/ContextIsPassedCop
|
96
99
|
if unfiltered_type
|
97
100
|
raise TypeNotVisibleError.new(type_name: type_name)
|
98
101
|
else
|
@@ -1,11 +1,22 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
3
|
+
require "graphql/tracing/notifications_trace"
|
4
4
|
|
5
5
|
module GraphQL
|
6
6
|
module Tracing
|
7
|
-
# This implementation forwards events to ActiveSupport::Notifications
|
8
|
-
#
|
7
|
+
# This implementation forwards events to ActiveSupport::Notifications with a `graphql` suffix.
|
8
|
+
#
|
9
|
+
# @example Sending execution events to ActiveSupport::Notifications
|
10
|
+
# class MySchema < GraphQL::Schema
|
11
|
+
# trace_with(GraphQL::Tracing::ActiveSupportNotificationsTrace)
|
12
|
+
# end
|
13
|
+
#
|
14
|
+
# @example Subscribing to GraphQL events with ActiveSupport::Notifications
|
15
|
+
# ActiveSupport::Notifications.subscribe(/graphql/) do |event|
|
16
|
+
# pp event.name
|
17
|
+
# pp event.payload
|
18
|
+
# end
|
19
|
+
#
|
9
20
|
module ActiveSupportNotificationsTrace
|
10
21
|
include NotificationsTrace
|
11
22
|
def initialize(engine: ActiveSupport::Notifications, **rest)
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "graphql/tracing/platform_trace"
|
4
|
+
|
3
5
|
module GraphQL
|
4
6
|
module Tracing
|
5
7
|
|
@@ -20,14 +22,18 @@ module GraphQL
|
|
20
22
|
# These GraphQL events will show up as 'graphql.execute' spans
|
21
23
|
EXEC_KEYS = ['execute_multiplex', 'execute_query', 'execute_query_lazy'].freeze
|
22
24
|
|
25
|
+
|
23
26
|
# During auto-instrumentation this version of AppOpticsTracing is compared
|
24
27
|
# with the version provided in the appoptics_apm gem, so that the newer
|
25
28
|
# version of the class can be used
|
26
29
|
|
30
|
+
|
27
31
|
def self.version
|
28
32
|
Gem::Version.new('1.0.0')
|
29
33
|
end
|
30
34
|
|
35
|
+
# rubocop:disable Development/NoEvalCop This eval takes static inputs at load-time
|
36
|
+
|
31
37
|
[
|
32
38
|
'lex',
|
33
39
|
'parse',
|
@@ -55,6 +61,8 @@ module GraphQL
|
|
55
61
|
RUBY
|
56
62
|
end
|
57
63
|
|
64
|
+
# rubocop:enable Development/NoEvalCop
|
65
|
+
|
58
66
|
def execute_field(query:, field:, ast_node:, arguments:, object:)
|
59
67
|
return_type = field.type.unwrap
|
60
68
|
trace_field = if return_type.kind.scalar? || return_type.kind.enum?
|
@@ -81,7 +89,7 @@ module GraphQL
|
|
81
89
|
end
|
82
90
|
end
|
83
91
|
|
84
|
-
def execute_field_lazy(query:, field:, ast_node:, arguments:, object:)
|
92
|
+
def execute_field_lazy(query:, field:, ast_node:, arguments:, object:) # rubocop:disable Development/TraceCallsSuperCop
|
85
93
|
execute_field(query: query, field: field, ast_node: ast_node, arguments: arguments, object: object)
|
86
94
|
end
|
87
95
|
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "graphql/tracing/platform_tracing"
|
4
|
+
|
3
5
|
module GraphQL
|
4
6
|
module Tracing
|
5
7
|
|
@@ -20,6 +22,11 @@ module GraphQL
|
|
20
22
|
# These GraphQL events will show up as 'graphql.execute' spans
|
21
23
|
EXEC_KEYS = ['execute_multiplex', 'execute_query', 'execute_query_lazy'].freeze
|
22
24
|
|
25
|
+
def initialize(...)
|
26
|
+
warn "GraphQL::Tracing::AppOptics tracing is deprecated; update to SolarWindsAPM instead, which uses OpenTelemetry."
|
27
|
+
super
|
28
|
+
end
|
29
|
+
|
23
30
|
# During auto-instrumentation this version of AppOpticsTracing is compared
|
24
31
|
# with the version provided in the appoptics_apm gem, so that the newer
|
25
32
|
# version of the class can be used
|