graphql 1.12.24 → 1.13.19
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/core.rb +3 -8
- data/lib/generators/graphql/enum_generator.rb +4 -10
- data/lib/generators/graphql/field_extractor.rb +31 -0
- data/lib/generators/graphql/input_generator.rb +50 -0
- data/lib/generators/graphql/install/mutation_root_generator.rb +34 -0
- data/lib/generators/graphql/install_generator.rb +10 -3
- data/lib/generators/graphql/interface_generator.rb +7 -7
- data/lib/generators/graphql/mutation_create_generator.rb +22 -0
- data/lib/generators/graphql/mutation_delete_generator.rb +22 -0
- data/lib/generators/graphql/mutation_generator.rb +5 -30
- data/lib/generators/graphql/mutation_update_generator.rb +22 -0
- data/lib/generators/graphql/object_generator.rb +8 -37
- data/lib/generators/graphql/orm_mutations_base.rb +40 -0
- data/lib/generators/graphql/scalar_generator.rb +4 -2
- data/lib/generators/graphql/templates/enum.erb +5 -1
- data/lib/generators/graphql/templates/input.erb +9 -0
- data/lib/generators/graphql/templates/interface.erb +4 -2
- data/lib/generators/graphql/templates/mutation.erb +1 -1
- data/lib/generators/graphql/templates/mutation_create.erb +20 -0
- data/lib/generators/graphql/templates/mutation_delete.erb +20 -0
- data/lib/generators/graphql/templates/mutation_update.erb +21 -0
- data/lib/generators/graphql/templates/object.erb +4 -2
- data/lib/generators/graphql/templates/scalar.erb +3 -1
- data/lib/generators/graphql/templates/union.erb +4 -2
- data/lib/generators/graphql/type_generator.rb +46 -10
- data/lib/generators/graphql/union_generator.rb +5 -5
- data/lib/graphql/analysis/ast/field_usage.rb +2 -2
- data/lib/graphql/analysis/ast/query_complexity.rb +10 -14
- data/lib/graphql/analysis/ast/visitor.rb +5 -4
- data/lib/graphql/argument.rb +1 -1
- data/lib/graphql/backtrace/table.rb +1 -1
- data/lib/graphql/base_type.rb +5 -3
- data/lib/graphql/boolean_type.rb +1 -1
- data/lib/graphql/dataloader/source.rb +2 -2
- data/lib/graphql/dataloader.rb +55 -22
- data/lib/graphql/date_encoding_error.rb +16 -0
- data/lib/graphql/define/instance_definable.rb +15 -0
- data/lib/graphql/directive/deprecated_directive.rb +1 -1
- data/lib/graphql/directive/include_directive.rb +1 -1
- data/lib/graphql/directive/skip_directive.rb +1 -1
- data/lib/graphql/directive.rb +1 -5
- data/lib/graphql/enum_type.rb +7 -3
- data/lib/graphql/execution/errors.rb +1 -0
- data/lib/graphql/execution/interpreter/arguments.rb +1 -1
- data/lib/graphql/execution/interpreter/arguments_cache.rb +6 -4
- data/lib/graphql/execution/interpreter/runtime.rb +66 -38
- data/lib/graphql/execution/lookahead.rb +2 -2
- data/lib/graphql/execution/multiplex.rb +4 -1
- data/lib/graphql/field.rb +1 -1
- data/lib/graphql/float_type.rb +1 -1
- data/lib/graphql/id_type.rb +1 -1
- data/lib/graphql/input_object_type.rb +1 -1
- data/lib/graphql/int_type.rb +1 -1
- data/lib/graphql/interface_type.rb +1 -1
- data/lib/graphql/introspection/directive_location_enum.rb +2 -2
- data/lib/graphql/introspection/directive_type.rb +5 -3
- data/lib/graphql/introspection/entry_points.rb +2 -2
- data/lib/graphql/introspection/enum_value_type.rb +2 -2
- data/lib/graphql/introspection/field_type.rb +3 -3
- data/lib/graphql/introspection/input_value_type.rb +4 -4
- data/lib/graphql/introspection/schema_type.rb +9 -4
- data/lib/graphql/introspection/type_type.rb +18 -12
- data/lib/graphql/introspection.rb +4 -1
- data/lib/graphql/language/block_string.rb +2 -6
- data/lib/graphql/language/document_from_schema_definition.rb +11 -4
- data/lib/graphql/language/lexer.rb +50 -28
- data/lib/graphql/language/lexer.rl +2 -4
- data/lib/graphql/language/nodes.rb +4 -3
- data/lib/graphql/language/parser.rb +841 -820
- data/lib/graphql/language/parser.y +13 -6
- data/lib/graphql/language/printer.rb +10 -1
- data/lib/graphql/language/sanitized_printer.rb +5 -5
- data/lib/graphql/language/token.rb +0 -4
- data/lib/graphql/name_validator.rb +0 -4
- data/lib/graphql/object_type.rb +2 -2
- data/lib/graphql/pagination/active_record_relation_connection.rb +43 -6
- data/lib/graphql/pagination/relation_connection.rb +59 -29
- data/lib/graphql/query/arguments.rb +1 -1
- data/lib/graphql/query/arguments_cache.rb +1 -1
- data/lib/graphql/query/context.rb +15 -2
- data/lib/graphql/query/input_validation_result.rb +9 -0
- data/lib/graphql/query/literal_input.rb +1 -1
- data/lib/graphql/query/null_context.rb +12 -7
- data/lib/graphql/query/serial_execution/field_resolution.rb +1 -1
- data/lib/graphql/query/validation_pipeline.rb +2 -3
- data/lib/graphql/query/variable_validation_error.rb +2 -2
- data/lib/graphql/query/variables.rb +35 -4
- data/lib/graphql/query.rb +0 -1
- data/lib/graphql/relay/connection_type.rb +15 -2
- data/lib/graphql/relay/edges_instrumentation.rb +0 -1
- data/lib/graphql/relay/global_id_resolve.rb +1 -2
- data/lib/graphql/relay/mutation.rb +1 -1
- data/lib/graphql/relay/page_info.rb +1 -1
- data/lib/graphql/relay/range_add.rb +4 -0
- data/lib/graphql/rubocop/graphql/base_cop.rb +36 -0
- data/lib/graphql/rubocop/graphql/default_null_true.rb +43 -0
- data/lib/graphql/rubocop/graphql/default_required_true.rb +43 -0
- data/lib/graphql/rubocop.rb +4 -0
- data/lib/graphql/scalar_type.rb +1 -1
- data/lib/graphql/schema/addition.rb +37 -28
- data/lib/graphql/schema/argument.rb +30 -15
- data/lib/graphql/schema/build_from_definition.rb +6 -5
- data/lib/graphql/schema/directive/feature.rb +1 -1
- data/lib/graphql/schema/directive/flagged.rb +2 -2
- data/lib/graphql/schema/directive/include.rb +1 -1
- data/lib/graphql/schema/directive/skip.rb +1 -1
- data/lib/graphql/schema/directive/transform.rb +1 -1
- data/lib/graphql/schema/directive.rb +23 -4
- data/lib/graphql/schema/enum.rb +61 -12
- data/lib/graphql/schema/enum_value.rb +6 -0
- data/lib/graphql/schema/field/connection_extension.rb +1 -1
- data/lib/graphql/schema/field.rb +261 -83
- data/lib/graphql/schema/field_extension.rb +89 -2
- data/lib/graphql/schema/find_inherited_value.rb +1 -0
- data/lib/graphql/schema/finder.rb +5 -5
- data/lib/graphql/schema/input_object.rb +24 -7
- data/lib/graphql/schema/interface.rb +11 -20
- data/lib/graphql/schema/introspection_system.rb +1 -1
- data/lib/graphql/schema/list.rb +21 -4
- data/lib/graphql/schema/loader.rb +3 -0
- data/lib/graphql/schema/member/accepts_definition.rb +15 -3
- data/lib/graphql/schema/member/base_dsl_methods.rb +1 -1
- data/lib/graphql/schema/member/build_type.rb +0 -4
- data/lib/graphql/schema/member/cached_graphql_definition.rb +29 -2
- data/lib/graphql/schema/member/has_arguments.rb +56 -14
- data/lib/graphql/schema/member/has_deprecation_reason.rb +1 -1
- data/lib/graphql/schema/member/has_fields.rb +76 -18
- data/lib/graphql/schema/member/has_interfaces.rb +100 -0
- data/lib/graphql/schema/member/validates_input.rb +2 -2
- data/lib/graphql/schema/member.rb +1 -0
- data/lib/graphql/schema/non_null.rb +9 -3
- data/lib/graphql/schema/object.rb +10 -75
- data/lib/graphql/schema/printer.rb +1 -1
- data/lib/graphql/schema/relay_classic_mutation.rb +37 -3
- data/lib/graphql/schema/resolver/has_payload_type.rb +27 -2
- data/lib/graphql/schema/resolver.rb +37 -17
- data/lib/graphql/schema/scalar.rb +15 -1
- data/lib/graphql/schema/subscription.rb +11 -1
- data/lib/graphql/schema/traversal.rb +1 -1
- data/lib/graphql/schema/type_expression.rb +1 -1
- data/lib/graphql/schema/type_membership.rb +18 -4
- data/lib/graphql/schema/union.rb +8 -1
- data/lib/graphql/schema/validator/format_validator.rb +0 -4
- data/lib/graphql/schema/validator/numericality_validator.rb +1 -0
- data/lib/graphql/schema/validator/required_validator.rb +29 -15
- data/lib/graphql/schema/validator.rb +4 -7
- data/lib/graphql/schema/warden.rb +126 -53
- data/lib/graphql/schema.rb +120 -24
- data/lib/graphql/static_validation/all_rules.rb +1 -0
- data/lib/graphql/static_validation/base_visitor.rb +6 -6
- data/lib/graphql/static_validation/definition_dependencies.rb +0 -1
- data/lib/graphql/static_validation/literal_validator.rb +1 -1
- data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +1 -1
- data/lib/graphql/static_validation/rules/arguments_are_defined.rb +1 -1
- data/lib/graphql/static_validation/rules/directives_are_in_valid_locations.rb +1 -1
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +1 -1
- data/lib/graphql/static_validation/rules/query_root_exists.rb +17 -0
- data/lib/graphql/static_validation/rules/query_root_exists_error.rb +26 -0
- data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +3 -3
- data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +4 -4
- data/lib/graphql/static_validation/rules/unique_directives_per_location.rb +1 -1
- data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +13 -7
- data/lib/graphql/static_validation/validation_context.rb +4 -0
- data/lib/graphql/string_type.rb +1 -1
- data/lib/graphql/subscriptions/action_cable_subscriptions.rb +8 -4
- data/lib/graphql/subscriptions/event.rb +20 -12
- data/lib/graphql/subscriptions/serialize.rb +22 -2
- data/lib/graphql/subscriptions.rb +17 -19
- data/lib/graphql/tracing/active_support_notifications_tracing.rb +6 -20
- data/lib/graphql/tracing/data_dog_tracing.rb +24 -2
- data/lib/graphql/tracing/notifications_tracing.rb +59 -0
- data/lib/graphql/tracing/platform_tracing.rb +20 -10
- data/lib/graphql/types/iso_8601_date.rb +13 -5
- data/lib/graphql/types/iso_8601_date_time.rb +8 -1
- data/lib/graphql/types/relay/connection_behaviors.rb +28 -10
- data/lib/graphql/types/relay/default_relay.rb +5 -1
- data/lib/graphql/types/relay/edge_behaviors.rb +13 -2
- data/lib/graphql/types/relay/has_node_field.rb +1 -1
- data/lib/graphql/types/relay/has_nodes_field.rb +1 -1
- data/lib/graphql/types/relay/node_field.rb +2 -3
- data/lib/graphql/types/relay/nodes_field.rb +19 -3
- data/lib/graphql/types/string.rb +1 -1
- data/lib/graphql/union_type.rb +1 -1
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +22 -32
- metadata +31 -11
- /data/lib/generators/graphql/{templates → install/templates}/base_mutation.erb +0 -0
- /data/lib/generators/graphql/{templates → install/templates}/mutation_type.erb +0 -0
@@ -71,9 +71,17 @@ module GraphQL
|
|
71
71
|
when SYMBOL_KEY
|
72
72
|
value[SYMBOL_KEY].to_sym
|
73
73
|
when TIMESTAMP_KEY
|
74
|
-
timestamp_class_name,
|
74
|
+
timestamp_class_name, *timestamp_args = value[TIMESTAMP_KEY]
|
75
75
|
timestamp_class = Object.const_get(timestamp_class_name)
|
76
|
-
|
76
|
+
if defined?(ActiveSupport::TimeWithZone) && timestamp_class <= ActiveSupport::TimeWithZone
|
77
|
+
zone_name, timestamp_s = timestamp_args
|
78
|
+
zone = ActiveSupport::TimeZone[zone_name]
|
79
|
+
raise "Zone #{zone_name} not found, unable to deserialize" unless zone
|
80
|
+
zone.strptime(timestamp_s, TIMESTAMP_FORMAT)
|
81
|
+
else
|
82
|
+
timestamp_s = timestamp_args.first
|
83
|
+
timestamp_class.strptime(timestamp_s, TIMESTAMP_FORMAT)
|
84
|
+
end
|
77
85
|
when OPEN_STRUCT_KEY
|
78
86
|
ostruct_values = load_value(value[OPEN_STRUCT_KEY])
|
79
87
|
OpenStruct.new(ostruct_values)
|
@@ -123,6 +131,18 @@ module GraphQL
|
|
123
131
|
{ SYMBOL_KEY => obj.to_s }
|
124
132
|
elsif obj.respond_to?(:to_gid_param)
|
125
133
|
{GLOBALID_KEY => obj.to_gid_param}
|
134
|
+
elsif defined?(ActiveSupport::TimeWithZone) && obj.is_a?(ActiveSupport::TimeWithZone) && obj.class.name != Time.name
|
135
|
+
# This handles a case where Rails prior to 7 would
|
136
|
+
# make the class ActiveSupport::TimeWithZone return "Time" for
|
137
|
+
# its name. In Rails 7, it will now return "ActiveSupport::TimeWithZone",
|
138
|
+
# which happens to be incompatible with expectations we have
|
139
|
+
# with what a Time class supports ( notably, strptime in `load_value` ).
|
140
|
+
#
|
141
|
+
# This now passes along the name of the zone, such that a future deserialization
|
142
|
+
# of this string will use the correct time zone from the ActiveSupport TimeZone
|
143
|
+
# list to produce the time.
|
144
|
+
#
|
145
|
+
{ TIMESTAMP_KEY => [obj.class.name, obj.time_zone.name, obj.strftime(TIMESTAMP_FORMAT)] }
|
126
146
|
elsif obj.is_a?(Date) || obj.is_a?(Time)
|
127
147
|
# DateTime extends Date; for TimeWithZone, call `.utc` first.
|
128
148
|
{ TIMESTAMP_KEY => [obj.class.name, obj.strftime(TIMESTAMP_FORMAT)] }
|
@@ -16,6 +16,13 @@ module GraphQL
|
|
16
16
|
class InvalidTriggerError < GraphQL::Error
|
17
17
|
end
|
18
18
|
|
19
|
+
# Raised when either:
|
20
|
+
# - An initial subscription didn't have a value for `context[subscription_scope]`
|
21
|
+
# - Or, an update didn't pass `.trigger(..., scope:)`
|
22
|
+
# When raised, the initial subscription or update fails completely.
|
23
|
+
class SubscriptionScopeMissingError < GraphQL::Error
|
24
|
+
end
|
25
|
+
|
19
26
|
# @see {Subscriptions#initialize} for options, concrete implementations may add options.
|
20
27
|
def self.use(defn, options = {})
|
21
28
|
schema = defn.is_a?(Class) ? defn : defn.target
|
@@ -76,7 +83,8 @@ module GraphQL
|
|
76
83
|
end
|
77
84
|
|
78
85
|
# Normalize symbol-keyed args to strings, try camelizing them
|
79
|
-
|
86
|
+
# Should this accept a real context somehow?
|
87
|
+
normalized_args = normalize_arguments(normalized_event_name, field, args, GraphQL::Query::NullContext)
|
80
88
|
|
81
89
|
event = Subscriptions::Event.new(
|
82
90
|
name: normalized_event_name,
|
@@ -151,16 +159,6 @@ module GraphQL
|
|
151
159
|
# @param object [Object]
|
152
160
|
# @return [void]
|
153
161
|
def execute_all(event, object)
|
154
|
-
each_subscription_id(event) do |subscription_id|
|
155
|
-
execute(subscription_id, event, object)
|
156
|
-
end
|
157
|
-
end
|
158
|
-
|
159
|
-
# Get each `subscription_id` subscribed to `event.topic` and yield them
|
160
|
-
# @param event [GraphQL::Subscriptions::Event]
|
161
|
-
# @yieldparam subscription_id [String]
|
162
|
-
# @return [void]
|
163
|
-
def each_subscription_id(event)
|
164
162
|
raise GraphQL::RequiredImplementationMissingError
|
165
163
|
end
|
166
164
|
|
@@ -233,7 +231,7 @@ module GraphQL
|
|
233
231
|
# @param arg_owner [GraphQL::Field, GraphQL::BaseType]
|
234
232
|
# @param args [Hash, Array, Any] some GraphQL input value to coerce as `arg_owner`
|
235
233
|
# @return [Any] normalized arguments value
|
236
|
-
def normalize_arguments(event_name, arg_owner, args)
|
234
|
+
def normalize_arguments(event_name, arg_owner, args, context)
|
237
235
|
case arg_owner
|
238
236
|
when GraphQL::Field, GraphQL::InputObjectType, GraphQL::Schema::Field, Class
|
239
237
|
if arg_owner.is_a?(Class) && !arg_owner.kind.input_object?
|
@@ -244,19 +242,19 @@ module GraphQL
|
|
244
242
|
missing_arg_names = []
|
245
243
|
args.each do |k, v|
|
246
244
|
arg_name = k.to_s
|
247
|
-
arg_defn = arg_owner.
|
245
|
+
arg_defn = arg_owner.get_argument(arg_name, context)
|
248
246
|
if arg_defn
|
249
247
|
normalized_arg_name = arg_name
|
250
248
|
else
|
251
249
|
normalized_arg_name = normalize_name(arg_name)
|
252
|
-
arg_defn = arg_owner.
|
250
|
+
arg_defn = arg_owner.get_argument(normalized_arg_name, context)
|
253
251
|
end
|
254
252
|
|
255
253
|
if arg_defn
|
256
254
|
if arg_defn.loads
|
257
255
|
normalized_arg_name = arg_defn.keyword.to_s
|
258
256
|
end
|
259
|
-
normalized = normalize_arguments(event_name, arg_defn.type, v)
|
257
|
+
normalized = normalize_arguments(event_name, arg_defn.type, v, context)
|
260
258
|
normalized_args[normalized_arg_name] = normalized
|
261
259
|
else
|
262
260
|
# Couldn't find a matching argument definition
|
@@ -266,12 +264,12 @@ module GraphQL
|
|
266
264
|
|
267
265
|
# Backfill default values so that trigger arguments
|
268
266
|
# match query arguments.
|
269
|
-
arg_owner.arguments.each do |
|
267
|
+
arg_owner.arguments(context).each do |_name, arg_defn|
|
270
268
|
if arg_defn.default_value? && !normalized_args.key?(arg_defn.name)
|
271
269
|
default_value = arg_defn.default_value
|
272
270
|
# We don't have an underlying "object" here, so it can't call methods.
|
273
271
|
# This is broken.
|
274
|
-
normalized_args[arg_defn.name] = arg_defn.prepare_value(nil, default_value, context:
|
272
|
+
normalized_args[arg_defn.name] = arg_defn.prepare_value(nil, default_value, context: context)
|
275
273
|
end
|
276
274
|
end
|
277
275
|
|
@@ -290,9 +288,9 @@ module GraphQL
|
|
290
288
|
|
291
289
|
normalized_args
|
292
290
|
when GraphQL::ListType, GraphQL::Schema::List
|
293
|
-
args.map { |a| normalize_arguments(event_name, arg_owner.of_type, a) }
|
291
|
+
args.map { |a| normalize_arguments(event_name, arg_owner.of_type, a, context) }
|
294
292
|
when GraphQL::NonNullType, GraphQL::Schema::NonNull
|
295
|
-
normalize_arguments(event_name, arg_owner.of_type, args)
|
293
|
+
normalize_arguments(event_name, arg_owner.of_type, args, context)
|
296
294
|
else
|
297
295
|
args
|
298
296
|
end
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'graphql/tracing/notifications_tracing'
|
4
|
+
|
3
5
|
module GraphQL
|
4
6
|
module Tracing
|
5
7
|
# This implementation forwards events to ActiveSupport::Notifications
|
@@ -8,27 +10,11 @@ module GraphQL
|
|
8
10
|
# @see KEYS for event names
|
9
11
|
module ActiveSupportNotificationsTracing
|
10
12
|
# A cache of frequently-used keys to avoid needless string allocations
|
11
|
-
KEYS =
|
12
|
-
|
13
|
-
"parse" => "parse.graphql",
|
14
|
-
"validate" => "validate.graphql",
|
15
|
-
"analyze_multiplex" => "analyze_multiplex.graphql",
|
16
|
-
"analyze_query" => "analyze_query.graphql",
|
17
|
-
"execute_query" => "execute_query.graphql",
|
18
|
-
"execute_query_lazy" => "execute_query_lazy.graphql",
|
19
|
-
"execute_field" => "execute_field.graphql",
|
20
|
-
"execute_field_lazy" => "execute_field_lazy.graphql",
|
21
|
-
"authorized" => "authorized.graphql",
|
22
|
-
"authorized_lazy" => "authorized_lazy.graphql",
|
23
|
-
"resolve_type" => "resolve_type.graphql",
|
24
|
-
"resolve_type_lazy" => "resolve_type.graphql",
|
25
|
-
}
|
13
|
+
KEYS = NotificationsTracing::KEYS
|
14
|
+
NOTIFICATIONS_ENGINE = NotificationsTracing.new(ActiveSupport::Notifications) if defined?(ActiveSupport::Notifications)
|
26
15
|
|
27
|
-
def self.trace(key, metadata)
|
28
|
-
|
29
|
-
ActiveSupport::Notifications.instrument(prefixed_key, metadata) do
|
30
|
-
yield
|
31
|
-
end
|
16
|
+
def self.trace(key, metadata, &blk)
|
17
|
+
NOTIFICATIONS_ENGINE.trace(key, metadata, &blk)
|
32
18
|
end
|
33
19
|
end
|
34
20
|
end
|
@@ -17,10 +17,21 @@ module GraphQL
|
|
17
17
|
def platform_trace(platform_key, key, data)
|
18
18
|
tracer.trace(platform_key, service: service_name) do |span|
|
19
19
|
span.span_type = 'custom'
|
20
|
+
if defined?(Datadog::Tracing::Metadata::Ext) # Introduced in ddtrace 1.0
|
21
|
+
span.set_tag(Datadog::Tracing::Metadata::Ext::TAG_COMPONENT, 'graphql')
|
22
|
+
span.set_tag(Datadog::Tracing::Metadata::Ext::TAG_OPERATION, key)
|
23
|
+
end
|
20
24
|
|
21
25
|
if key == 'execute_multiplex'
|
22
26
|
operations = data[:multiplex].queries.map(&:selected_operation_name).join(', ')
|
23
|
-
|
27
|
+
|
28
|
+
resource = if operations.empty?
|
29
|
+
first_query = data[:multiplex].queries.first
|
30
|
+
fallback_transaction_name(first_query && first_query.context)
|
31
|
+
else
|
32
|
+
operations
|
33
|
+
end
|
34
|
+
span.resource = resource if resource
|
24
35
|
|
25
36
|
# For top span of query, set the analytics sample rate tag, if available.
|
26
37
|
if analytics_enabled?
|
@@ -34,6 +45,8 @@ module GraphQL
|
|
34
45
|
span.set_tag(:query_string, data[:query].query_string)
|
35
46
|
end
|
36
47
|
|
48
|
+
prepare_span(key, data, span)
|
49
|
+
|
37
50
|
yield
|
38
51
|
end
|
39
52
|
end
|
@@ -42,8 +55,17 @@ module GraphQL
|
|
42
55
|
options.fetch(:service, 'ruby-graphql')
|
43
56
|
end
|
44
57
|
|
58
|
+
# Implement this method in a subclass to apply custom tags to datadog spans
|
59
|
+
# @param key [String] The event being traced
|
60
|
+
# @param data [Hash] The runtime data for this event (@see GraphQL::Tracing for keys for each event)
|
61
|
+
# @param span [Datadog::Tracing::SpanOperation] The datadog span for this event
|
62
|
+
def prepare_span(key, data, span)
|
63
|
+
end
|
64
|
+
|
45
65
|
def tracer
|
46
|
-
|
66
|
+
default_tracer = defined?(Datadog::Tracing) ? Datadog::Tracing : Datadog.tracer
|
67
|
+
|
68
|
+
options.fetch(:tracer, default_tracer)
|
47
69
|
end
|
48
70
|
|
49
71
|
def analytics_available?
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GraphQL
|
4
|
+
module Tracing
|
5
|
+
# This implementation forwards events to a notification handler (i.e.
|
6
|
+
# ActiveSupport::Notifications or Dry::Monitor::Notifications)
|
7
|
+
# with a `graphql` suffix.
|
8
|
+
#
|
9
|
+
# @see KEYS for event names
|
10
|
+
class NotificationsTracing
|
11
|
+
# A cache of frequently-used keys to avoid needless string allocations
|
12
|
+
KEYS = {
|
13
|
+
"lex" => "lex.graphql",
|
14
|
+
"parse" => "parse.graphql",
|
15
|
+
"validate" => "validate.graphql",
|
16
|
+
"analyze_multiplex" => "analyze_multiplex.graphql",
|
17
|
+
"analyze_query" => "analyze_query.graphql",
|
18
|
+
"execute_query" => "execute_query.graphql",
|
19
|
+
"execute_query_lazy" => "execute_query_lazy.graphql",
|
20
|
+
"execute_field" => "execute_field.graphql",
|
21
|
+
"execute_field_lazy" => "execute_field_lazy.graphql",
|
22
|
+
"authorized" => "authorized.graphql",
|
23
|
+
"authorized_lazy" => "authorized_lazy.graphql",
|
24
|
+
"resolve_type" => "resolve_type.graphql",
|
25
|
+
"resolve_type_lazy" => "resolve_type.graphql",
|
26
|
+
}
|
27
|
+
|
28
|
+
MAX_KEYS_SIZE = 100
|
29
|
+
|
30
|
+
# Initialize a new NotificationsTracing instance
|
31
|
+
#
|
32
|
+
# @param [Object] notifications_engine The notifications engine to use
|
33
|
+
def initialize(notifications_engine)
|
34
|
+
@notifications_engine = notifications_engine
|
35
|
+
end
|
36
|
+
|
37
|
+
# Sends a GraphQL tracing event to the notification handler
|
38
|
+
#
|
39
|
+
# @example
|
40
|
+
# . notifications_engine = Dry::Monitor::Notifications.new(:graphql)
|
41
|
+
# . tracer = GraphQL::Tracing::NotificationsTracing.new(notifications_engine)
|
42
|
+
# . tracer.trace("lex") { ... }
|
43
|
+
#
|
44
|
+
# @param [string] key The key for the event
|
45
|
+
# @param [Hash] metadata The metadata for the event
|
46
|
+
# @yield The block to execute for the event
|
47
|
+
def trace(key, metadata, &blk)
|
48
|
+
prefixed_key = KEYS[key] || "#{key}.graphql"
|
49
|
+
|
50
|
+
# Cache the new keys while making sure not to induce a memory leak
|
51
|
+
if KEYS.size < MAX_KEYS_SIZE
|
52
|
+
KEYS[key] ||= prefixed_key
|
53
|
+
end
|
54
|
+
|
55
|
+
@notifications_engine.instrument(prefixed_key, metadata, &blk)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -10,6 +10,10 @@ module GraphQL
|
|
10
10
|
class PlatformTracing
|
11
11
|
class << self
|
12
12
|
attr_accessor :platform_keys
|
13
|
+
|
14
|
+
def inherited(child_class)
|
15
|
+
child_class.platform_keys = self.platform_keys
|
16
|
+
end
|
13
17
|
end
|
14
18
|
|
15
19
|
def initialize(options = {})
|
@@ -32,6 +36,7 @@ module GraphQL
|
|
32
36
|
trace_field = true # implemented with instrumenter
|
33
37
|
else
|
34
38
|
field = data[:field]
|
39
|
+
# HERE
|
35
40
|
return_type = field.type.unwrap
|
36
41
|
trace_field = if return_type.kind.scalar? || return_type.kind.enum?
|
37
42
|
(field.trace.nil? && @trace_scalars) || field.trace
|
@@ -41,7 +46,7 @@ module GraphQL
|
|
41
46
|
|
42
47
|
platform_key = if trace_field
|
43
48
|
context = data.fetch(:query).context
|
44
|
-
cached_platform_key(context, field) { platform_field_key(data[:owner], field) }
|
49
|
+
cached_platform_key(context, field, :field) { platform_field_key(data[:owner], field) }
|
45
50
|
else
|
46
51
|
nil
|
47
52
|
end
|
@@ -57,14 +62,14 @@ module GraphQL
|
|
57
62
|
when "authorized", "authorized_lazy"
|
58
63
|
type = data.fetch(:type)
|
59
64
|
context = data.fetch(:context)
|
60
|
-
platform_key = cached_platform_key(context, type) { platform_authorized_key(type) }
|
65
|
+
platform_key = cached_platform_key(context, type, :authorized) { platform_authorized_key(type) }
|
61
66
|
platform_trace(platform_key, key, data) do
|
62
67
|
yield
|
63
68
|
end
|
64
69
|
when "resolve_type", "resolve_type_lazy"
|
65
70
|
type = data.fetch(:type)
|
66
71
|
context = data.fetch(:context)
|
67
|
-
platform_key = cached_platform_key(context, type) { platform_resolve_type_key(type) }
|
72
|
+
platform_key = cached_platform_key(context, type, :resolve_type) { platform_resolve_type_key(type) }
|
68
73
|
platform_trace(platform_key, key, data) do
|
69
74
|
yield
|
70
75
|
end
|
@@ -104,17 +109,22 @@ module GraphQL
|
|
104
109
|
|
105
110
|
private
|
106
111
|
|
107
|
-
# Get the transaction name based on the operation type and name
|
112
|
+
# Get the transaction name based on the operation type and name if possible, or fall back to a user provided
|
113
|
+
# one. Useful for anonymous queries.
|
108
114
|
def transaction_name(query)
|
109
115
|
selected_op = query.selected_operation
|
110
|
-
if selected_op
|
116
|
+
txn_name = if selected_op
|
111
117
|
op_type = selected_op.operation_type
|
112
|
-
op_name = selected_op.name || "anonymous"
|
118
|
+
op_name = selected_op.name || fallback_transaction_name(query.context) || "anonymous"
|
119
|
+
"#{op_type}.#{op_name}"
|
113
120
|
else
|
114
|
-
|
115
|
-
op_name = "anonymous"
|
121
|
+
"query.anonymous"
|
116
122
|
end
|
117
|
-
"GraphQL/#{
|
123
|
+
"GraphQL/#{txn_name}"
|
124
|
+
end
|
125
|
+
|
126
|
+
def fallback_transaction_name(context)
|
127
|
+
context[:tracing_fallback_transaction_name]
|
118
128
|
end
|
119
129
|
|
120
130
|
attr_reader :options
|
@@ -130,7 +140,7 @@ module GraphQL
|
|
130
140
|
# If the key isn't present, the given block is called and the result is cached for `key`.
|
131
141
|
#
|
132
142
|
# @return [String]
|
133
|
-
def cached_platform_key(ctx, key)
|
143
|
+
def cached_platform_key(ctx, key, trace_phase)
|
134
144
|
cache = ctx.namespace(self.class)[:platform_key_cache] ||= {}
|
135
145
|
cache.fetch(key) { cache[key] = yield }
|
136
146
|
end
|
@@ -21,13 +21,21 @@ module GraphQL
|
|
21
21
|
Date.parse(value.to_s).iso8601
|
22
22
|
end
|
23
23
|
|
24
|
-
# @param str_value [String]
|
24
|
+
# @param str_value [String, Date, DateTime, Time]
|
25
25
|
# @return [Date]
|
26
|
-
def self.coerce_input(
|
27
|
-
|
26
|
+
def self.coerce_input(value, ctx)
|
27
|
+
if value.is_a?(::Date)
|
28
|
+
value
|
29
|
+
elsif value.is_a?(::DateTime)
|
30
|
+
value.to_date
|
31
|
+
elsif value.is_a?(::Time)
|
32
|
+
value.to_date
|
33
|
+
else
|
34
|
+
Date.iso8601(value)
|
35
|
+
end
|
28
36
|
rescue ArgumentError, TypeError
|
29
|
-
|
30
|
-
|
37
|
+
err = GraphQL::DateEncodingError.new(value)
|
38
|
+
ctx.schema.type_error(err, ctx)
|
31
39
|
end
|
32
40
|
end
|
33
41
|
end
|
@@ -54,7 +54,14 @@ module GraphQL
|
|
54
54
|
Time.iso8601(str_value)
|
55
55
|
rescue ArgumentError, TypeError
|
56
56
|
begin
|
57
|
-
Date.iso8601(str_value).to_time
|
57
|
+
dt = Date.iso8601(str_value).to_time
|
58
|
+
# For compatibility, continue accepting dates given without times
|
59
|
+
# But without this, it would zero out given any time part of `str_value` (hours and/or minutes)
|
60
|
+
if dt.iso8601.start_with?(str_value)
|
61
|
+
dt
|
62
|
+
else
|
63
|
+
nil
|
64
|
+
end
|
58
65
|
rescue ArgumentError, TypeError
|
59
66
|
# Invalid input
|
60
67
|
nil
|
@@ -35,7 +35,8 @@ module GraphQL
|
|
35
35
|
# It's called when you subclass this base connection, trying to use the
|
36
36
|
# class name to set defaults. You can call it again in the class definition
|
37
37
|
# to override the default (or provide a value, if the default lookup failed).
|
38
|
-
|
38
|
+
# @param field_options [Hash] Any extra keyword arguments to pass to the `field :edges, ...` and `field :nodes, ...` configurations
|
39
|
+
def edge_type(edge_type_class, edge_class: GraphQL::Relay::Edge, node_type: edge_type_class.node_type, nodes_field: self.has_nodes_field, node_nullable: self.node_nullable, edges_nullable: self.edges_nullable, edge_nullable: self.edge_nullable, field_options: nil)
|
39
40
|
# Set this connection's graphql name
|
40
41
|
node_type_name = node_type.graphql_name
|
41
42
|
|
@@ -43,13 +44,22 @@ module GraphQL
|
|
43
44
|
@edge_type = edge_type_class
|
44
45
|
@edge_class = edge_class
|
45
46
|
|
46
|
-
|
47
|
+
base_field_options = {
|
48
|
+
name: :edges,
|
49
|
+
type: [edge_type_class, null: edge_nullable],
|
47
50
|
null: edges_nullable,
|
48
51
|
description: "A list of edges.",
|
49
52
|
legacy_edge_class: edge_class, # This is used by the old runtime only, for EdgesInstrumentation
|
50
|
-
connection: false
|
53
|
+
connection: false,
|
54
|
+
}
|
51
55
|
|
52
|
-
|
56
|
+
if field_options
|
57
|
+
base_field_options.merge!(field_options)
|
58
|
+
end
|
59
|
+
|
60
|
+
field(**base_field_options)
|
61
|
+
|
62
|
+
define_nodes_field(node_nullable, field_options: field_options) if nodes_field
|
53
63
|
|
54
64
|
description("The connection type for #{node_type_name}.")
|
55
65
|
end
|
@@ -60,8 +70,8 @@ module GraphQL
|
|
60
70
|
end
|
61
71
|
|
62
72
|
# Add the shortcut `nodes` field to this connection and its subclasses
|
63
|
-
def nodes_field(node_nullable: self.node_nullable)
|
64
|
-
define_nodes_field(node_nullable)
|
73
|
+
def nodes_field(node_nullable: self.node_nullable, field_options: nil)
|
74
|
+
define_nodes_field(node_nullable, field_options: field_options)
|
65
75
|
end
|
66
76
|
|
67
77
|
def authorized?(obj, ctx)
|
@@ -73,7 +83,8 @@ module GraphQL
|
|
73
83
|
end
|
74
84
|
|
75
85
|
def visible?(ctx)
|
76
|
-
node_type
|
86
|
+
# if this is an abstract base class, there may be no `node_type`
|
87
|
+
node_type ? node_type.visible?(ctx) : super
|
77
88
|
end
|
78
89
|
|
79
90
|
# Set the default `node_nullable` for this class and its child classes. (Defaults to `true`.)
|
@@ -118,11 +129,18 @@ module GraphQL
|
|
118
129
|
|
119
130
|
private
|
120
131
|
|
121
|
-
def define_nodes_field(nullable)
|
122
|
-
|
132
|
+
def define_nodes_field(nullable, field_options: nil)
|
133
|
+
base_field_options = {
|
134
|
+
name: :nodes,
|
135
|
+
type: [@node_type, null: nullable],
|
123
136
|
null: nullable,
|
124
137
|
description: "A list of nodes.",
|
125
|
-
connection: false
|
138
|
+
connection: false,
|
139
|
+
}
|
140
|
+
if field_options
|
141
|
+
base_field_options.merge!(field_options)
|
142
|
+
end
|
143
|
+
field(**base_field_options)
|
126
144
|
end
|
127
145
|
end
|
128
146
|
|
@@ -17,7 +17,11 @@ module GraphQL
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def to_graphql
|
20
|
-
type_defn =
|
20
|
+
type_defn = if method(:to_graphql).super_method.arity
|
21
|
+
super(silence_deprecation_warning: true)
|
22
|
+
else
|
23
|
+
super
|
24
|
+
end
|
21
25
|
type_defn.default_relay = default_relay?
|
22
26
|
type_defn
|
23
27
|
end
|
@@ -16,11 +16,22 @@ module GraphQL
|
|
16
16
|
#
|
17
17
|
# @param node_type [Class] A `Schema::Object` subclass
|
18
18
|
# @param null [Boolean]
|
19
|
-
|
19
|
+
# @param field_options [Hash] Any extra arguments to pass to the `field :node` configuration
|
20
|
+
def node_type(node_type = nil, null: self.node_nullable, field_options: nil)
|
20
21
|
if node_type
|
21
22
|
@node_type = node_type
|
22
23
|
# Add a default `node` field
|
23
|
-
|
24
|
+
base_field_options = {
|
25
|
+
name: :node,
|
26
|
+
type: node_type,
|
27
|
+
null: null,
|
28
|
+
description: "The item at the end of the edge.",
|
29
|
+
connection: false,
|
30
|
+
}
|
31
|
+
if field_options
|
32
|
+
base_field_options.merge!(field_options)
|
33
|
+
end
|
34
|
+
field(**base_field_options)
|
24
35
|
end
|
25
36
|
@node_type
|
26
37
|
end
|
@@ -2,8 +2,7 @@
|
|
2
2
|
module GraphQL
|
3
3
|
module Types
|
4
4
|
module Relay
|
5
|
-
#
|
6
|
-
# or use it for inspiration for your own field definition.
|
5
|
+
# Don't use this field directly, instead, use one of these approaches:
|
7
6
|
#
|
8
7
|
# @example Adding this field directly
|
9
8
|
# include GraphQL::Types::Relay::HasNodeField
|
@@ -19,7 +18,7 @@ module GraphQL
|
|
19
18
|
# context.schema.object_from_id(id, context)
|
20
19
|
# end
|
21
20
|
#
|
22
|
-
|
21
|
+
DeprecatedNodeField = GraphQL::Schema::Field.new(owner: nil, **HasNodeField.field_options, &HasNodeField.field_block)
|
23
22
|
end
|
24
23
|
end
|
25
24
|
end
|
@@ -2,8 +2,7 @@
|
|
2
2
|
module GraphQL
|
3
3
|
module Types
|
4
4
|
module Relay
|
5
|
-
#
|
6
|
-
# or use it for inspiration for your own field definition.
|
5
|
+
# Don't use this directly, instead, use one of these:
|
7
6
|
#
|
8
7
|
# @example Adding this field directly
|
9
8
|
# include GraphQL::Types::Relay::HasNodesField
|
@@ -21,7 +20,24 @@ module GraphQL
|
|
21
20
|
# end
|
22
21
|
# end
|
23
22
|
#
|
24
|
-
|
23
|
+
def self.const_missing(const_name)
|
24
|
+
if const_name == :NodesField
|
25
|
+
message = "NodesField is deprecated, use `include GraphQL::Types::Relay::HasNodesField` instead."
|
26
|
+
message += "\n(referenced from #{caller(1, 1).first})"
|
27
|
+
GraphQL::Deprecation.warn(message)
|
28
|
+
|
29
|
+
DeprecatedNodesField
|
30
|
+
elsif const_name == :NodeField
|
31
|
+
message = "NodeField is deprecated, use `include GraphQL::Types::Relay::HasNodeField` instead."
|
32
|
+
message += "\n(referenced from #{caller(1, 1).first})"
|
33
|
+
GraphQL::Deprecation.warn(message)
|
34
|
+
|
35
|
+
DeprecatedNodeField
|
36
|
+
else
|
37
|
+
super
|
38
|
+
end
|
39
|
+
end
|
40
|
+
DeprecatedNodesField = GraphQL::Schema::Field.new(owner: nil, **HasNodesField.field_options, &HasNodesField.field_block)
|
25
41
|
end
|
26
42
|
end
|
27
43
|
end
|
data/lib/graphql/types/string.rb
CHANGED
data/lib/graphql/union_type.rb
CHANGED
@@ -11,7 +11,7 @@ module GraphQL
|
|
11
11
|
end
|
12
12
|
end
|
13
13
|
|
14
|
-
|
14
|
+
deprecated_accepts_definitions :resolve_type, :type_membership_class,
|
15
15
|
possible_types: AcceptPossibleTypesDefinition
|
16
16
|
ensure_defined :possible_types, :resolve_type, :resolve_type_proc, :type_membership_class
|
17
17
|
|
data/lib/graphql/version.rb
CHANGED