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.

Files changed (189) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/core.rb +3 -8
  3. data/lib/generators/graphql/enum_generator.rb +4 -10
  4. data/lib/generators/graphql/field_extractor.rb +31 -0
  5. data/lib/generators/graphql/input_generator.rb +50 -0
  6. data/lib/generators/graphql/install/mutation_root_generator.rb +34 -0
  7. data/lib/generators/graphql/install_generator.rb +10 -3
  8. data/lib/generators/graphql/interface_generator.rb +7 -7
  9. data/lib/generators/graphql/mutation_create_generator.rb +22 -0
  10. data/lib/generators/graphql/mutation_delete_generator.rb +22 -0
  11. data/lib/generators/graphql/mutation_generator.rb +5 -30
  12. data/lib/generators/graphql/mutation_update_generator.rb +22 -0
  13. data/lib/generators/graphql/object_generator.rb +8 -37
  14. data/lib/generators/graphql/orm_mutations_base.rb +40 -0
  15. data/lib/generators/graphql/scalar_generator.rb +4 -2
  16. data/lib/generators/graphql/templates/enum.erb +5 -1
  17. data/lib/generators/graphql/templates/input.erb +9 -0
  18. data/lib/generators/graphql/templates/interface.erb +4 -2
  19. data/lib/generators/graphql/templates/mutation.erb +1 -1
  20. data/lib/generators/graphql/templates/mutation_create.erb +20 -0
  21. data/lib/generators/graphql/templates/mutation_delete.erb +20 -0
  22. data/lib/generators/graphql/templates/mutation_update.erb +21 -0
  23. data/lib/generators/graphql/templates/object.erb +4 -2
  24. data/lib/generators/graphql/templates/scalar.erb +3 -1
  25. data/lib/generators/graphql/templates/union.erb +4 -2
  26. data/lib/generators/graphql/type_generator.rb +46 -10
  27. data/lib/generators/graphql/union_generator.rb +5 -5
  28. data/lib/graphql/analysis/ast/field_usage.rb +2 -2
  29. data/lib/graphql/analysis/ast/query_complexity.rb +10 -14
  30. data/lib/graphql/analysis/ast/visitor.rb +5 -4
  31. data/lib/graphql/argument.rb +1 -1
  32. data/lib/graphql/backtrace/table.rb +1 -1
  33. data/lib/graphql/base_type.rb +5 -3
  34. data/lib/graphql/boolean_type.rb +1 -1
  35. data/lib/graphql/dataloader/source.rb +2 -2
  36. data/lib/graphql/dataloader.rb +55 -22
  37. data/lib/graphql/date_encoding_error.rb +16 -0
  38. data/lib/graphql/define/instance_definable.rb +15 -0
  39. data/lib/graphql/directive/deprecated_directive.rb +1 -1
  40. data/lib/graphql/directive/include_directive.rb +1 -1
  41. data/lib/graphql/directive/skip_directive.rb +1 -1
  42. data/lib/graphql/directive.rb +1 -5
  43. data/lib/graphql/enum_type.rb +7 -3
  44. data/lib/graphql/execution/errors.rb +1 -0
  45. data/lib/graphql/execution/interpreter/arguments.rb +1 -1
  46. data/lib/graphql/execution/interpreter/arguments_cache.rb +6 -4
  47. data/lib/graphql/execution/interpreter/runtime.rb +66 -38
  48. data/lib/graphql/execution/lookahead.rb +2 -2
  49. data/lib/graphql/execution/multiplex.rb +4 -1
  50. data/lib/graphql/field.rb +1 -1
  51. data/lib/graphql/float_type.rb +1 -1
  52. data/lib/graphql/id_type.rb +1 -1
  53. data/lib/graphql/input_object_type.rb +1 -1
  54. data/lib/graphql/int_type.rb +1 -1
  55. data/lib/graphql/interface_type.rb +1 -1
  56. data/lib/graphql/introspection/directive_location_enum.rb +2 -2
  57. data/lib/graphql/introspection/directive_type.rb +5 -3
  58. data/lib/graphql/introspection/entry_points.rb +2 -2
  59. data/lib/graphql/introspection/enum_value_type.rb +2 -2
  60. data/lib/graphql/introspection/field_type.rb +3 -3
  61. data/lib/graphql/introspection/input_value_type.rb +4 -4
  62. data/lib/graphql/introspection/schema_type.rb +9 -4
  63. data/lib/graphql/introspection/type_type.rb +18 -12
  64. data/lib/graphql/introspection.rb +4 -1
  65. data/lib/graphql/language/block_string.rb +2 -6
  66. data/lib/graphql/language/document_from_schema_definition.rb +11 -4
  67. data/lib/graphql/language/lexer.rb +50 -28
  68. data/lib/graphql/language/lexer.rl +2 -4
  69. data/lib/graphql/language/nodes.rb +4 -3
  70. data/lib/graphql/language/parser.rb +841 -820
  71. data/lib/graphql/language/parser.y +13 -6
  72. data/lib/graphql/language/printer.rb +10 -1
  73. data/lib/graphql/language/sanitized_printer.rb +5 -5
  74. data/lib/graphql/language/token.rb +0 -4
  75. data/lib/graphql/name_validator.rb +0 -4
  76. data/lib/graphql/object_type.rb +2 -2
  77. data/lib/graphql/pagination/active_record_relation_connection.rb +43 -6
  78. data/lib/graphql/pagination/relation_connection.rb +59 -29
  79. data/lib/graphql/query/arguments.rb +1 -1
  80. data/lib/graphql/query/arguments_cache.rb +1 -1
  81. data/lib/graphql/query/context.rb +15 -2
  82. data/lib/graphql/query/input_validation_result.rb +9 -0
  83. data/lib/graphql/query/literal_input.rb +1 -1
  84. data/lib/graphql/query/null_context.rb +12 -7
  85. data/lib/graphql/query/serial_execution/field_resolution.rb +1 -1
  86. data/lib/graphql/query/validation_pipeline.rb +2 -3
  87. data/lib/graphql/query/variable_validation_error.rb +2 -2
  88. data/lib/graphql/query/variables.rb +35 -4
  89. data/lib/graphql/query.rb +0 -1
  90. data/lib/graphql/relay/connection_type.rb +15 -2
  91. data/lib/graphql/relay/edges_instrumentation.rb +0 -1
  92. data/lib/graphql/relay/global_id_resolve.rb +1 -2
  93. data/lib/graphql/relay/mutation.rb +1 -1
  94. data/lib/graphql/relay/page_info.rb +1 -1
  95. data/lib/graphql/relay/range_add.rb +4 -0
  96. data/lib/graphql/rubocop/graphql/base_cop.rb +36 -0
  97. data/lib/graphql/rubocop/graphql/default_null_true.rb +43 -0
  98. data/lib/graphql/rubocop/graphql/default_required_true.rb +43 -0
  99. data/lib/graphql/rubocop.rb +4 -0
  100. data/lib/graphql/scalar_type.rb +1 -1
  101. data/lib/graphql/schema/addition.rb +37 -28
  102. data/lib/graphql/schema/argument.rb +30 -15
  103. data/lib/graphql/schema/build_from_definition.rb +6 -5
  104. data/lib/graphql/schema/directive/feature.rb +1 -1
  105. data/lib/graphql/schema/directive/flagged.rb +2 -2
  106. data/lib/graphql/schema/directive/include.rb +1 -1
  107. data/lib/graphql/schema/directive/skip.rb +1 -1
  108. data/lib/graphql/schema/directive/transform.rb +1 -1
  109. data/lib/graphql/schema/directive.rb +23 -4
  110. data/lib/graphql/schema/enum.rb +61 -12
  111. data/lib/graphql/schema/enum_value.rb +6 -0
  112. data/lib/graphql/schema/field/connection_extension.rb +1 -1
  113. data/lib/graphql/schema/field.rb +261 -83
  114. data/lib/graphql/schema/field_extension.rb +89 -2
  115. data/lib/graphql/schema/find_inherited_value.rb +1 -0
  116. data/lib/graphql/schema/finder.rb +5 -5
  117. data/lib/graphql/schema/input_object.rb +24 -7
  118. data/lib/graphql/schema/interface.rb +11 -20
  119. data/lib/graphql/schema/introspection_system.rb +1 -1
  120. data/lib/graphql/schema/list.rb +21 -4
  121. data/lib/graphql/schema/loader.rb +3 -0
  122. data/lib/graphql/schema/member/accepts_definition.rb +15 -3
  123. data/lib/graphql/schema/member/base_dsl_methods.rb +1 -1
  124. data/lib/graphql/schema/member/build_type.rb +0 -4
  125. data/lib/graphql/schema/member/cached_graphql_definition.rb +29 -2
  126. data/lib/graphql/schema/member/has_arguments.rb +56 -14
  127. data/lib/graphql/schema/member/has_deprecation_reason.rb +1 -1
  128. data/lib/graphql/schema/member/has_fields.rb +76 -18
  129. data/lib/graphql/schema/member/has_interfaces.rb +100 -0
  130. data/lib/graphql/schema/member/validates_input.rb +2 -2
  131. data/lib/graphql/schema/member.rb +1 -0
  132. data/lib/graphql/schema/non_null.rb +9 -3
  133. data/lib/graphql/schema/object.rb +10 -75
  134. data/lib/graphql/schema/printer.rb +1 -1
  135. data/lib/graphql/schema/relay_classic_mutation.rb +37 -3
  136. data/lib/graphql/schema/resolver/has_payload_type.rb +27 -2
  137. data/lib/graphql/schema/resolver.rb +37 -17
  138. data/lib/graphql/schema/scalar.rb +15 -1
  139. data/lib/graphql/schema/subscription.rb +11 -1
  140. data/lib/graphql/schema/traversal.rb +1 -1
  141. data/lib/graphql/schema/type_expression.rb +1 -1
  142. data/lib/graphql/schema/type_membership.rb +18 -4
  143. data/lib/graphql/schema/union.rb +8 -1
  144. data/lib/graphql/schema/validator/format_validator.rb +0 -4
  145. data/lib/graphql/schema/validator/numericality_validator.rb +1 -0
  146. data/lib/graphql/schema/validator/required_validator.rb +29 -15
  147. data/lib/graphql/schema/validator.rb +4 -7
  148. data/lib/graphql/schema/warden.rb +126 -53
  149. data/lib/graphql/schema.rb +120 -24
  150. data/lib/graphql/static_validation/all_rules.rb +1 -0
  151. data/lib/graphql/static_validation/base_visitor.rb +6 -6
  152. data/lib/graphql/static_validation/definition_dependencies.rb +0 -1
  153. data/lib/graphql/static_validation/literal_validator.rb +1 -1
  154. data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +1 -1
  155. data/lib/graphql/static_validation/rules/arguments_are_defined.rb +1 -1
  156. data/lib/graphql/static_validation/rules/directives_are_in_valid_locations.rb +1 -1
  157. data/lib/graphql/static_validation/rules/fields_will_merge.rb +1 -1
  158. data/lib/graphql/static_validation/rules/query_root_exists.rb +17 -0
  159. data/lib/graphql/static_validation/rules/query_root_exists_error.rb +26 -0
  160. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +3 -3
  161. data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +4 -4
  162. data/lib/graphql/static_validation/rules/unique_directives_per_location.rb +1 -1
  163. data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +13 -7
  164. data/lib/graphql/static_validation/validation_context.rb +4 -0
  165. data/lib/graphql/string_type.rb +1 -1
  166. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +8 -4
  167. data/lib/graphql/subscriptions/event.rb +20 -12
  168. data/lib/graphql/subscriptions/serialize.rb +22 -2
  169. data/lib/graphql/subscriptions.rb +17 -19
  170. data/lib/graphql/tracing/active_support_notifications_tracing.rb +6 -20
  171. data/lib/graphql/tracing/data_dog_tracing.rb +24 -2
  172. data/lib/graphql/tracing/notifications_tracing.rb +59 -0
  173. data/lib/graphql/tracing/platform_tracing.rb +20 -10
  174. data/lib/graphql/types/iso_8601_date.rb +13 -5
  175. data/lib/graphql/types/iso_8601_date_time.rb +8 -1
  176. data/lib/graphql/types/relay/connection_behaviors.rb +28 -10
  177. data/lib/graphql/types/relay/default_relay.rb +5 -1
  178. data/lib/graphql/types/relay/edge_behaviors.rb +13 -2
  179. data/lib/graphql/types/relay/has_node_field.rb +1 -1
  180. data/lib/graphql/types/relay/has_nodes_field.rb +1 -1
  181. data/lib/graphql/types/relay/node_field.rb +2 -3
  182. data/lib/graphql/types/relay/nodes_field.rb +19 -3
  183. data/lib/graphql/types/string.rb +1 -1
  184. data/lib/graphql/union_type.rb +1 -1
  185. data/lib/graphql/version.rb +1 -1
  186. data/lib/graphql.rb +22 -32
  187. metadata +31 -11
  188. /data/lib/generators/graphql/{templates → install/templates}/base_mutation.erb +0 -0
  189. /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, timestamp_s = value[TIMESTAMP_KEY]
74
+ timestamp_class_name, *timestamp_args = value[TIMESTAMP_KEY]
75
75
  timestamp_class = Object.const_get(timestamp_class_name)
76
- timestamp_class.strptime(timestamp_s, TIMESTAMP_FORMAT)
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
- normalized_args = normalize_arguments(normalized_event_name, field, args)
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.arguments[arg_name]
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.arguments[normalized_arg_name]
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 |name, arg_defn|
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: GraphQL::Query::NullContext)
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
- "lex" => "lex.graphql",
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
- prefixed_key = KEYS[key] || "#{key}.graphql"
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
- span.resource = operations unless operations.empty?
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
- options.fetch(:tracer, Datadog.tracer)
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
- op_type = "query"
115
- op_name = "anonymous"
121
+ "query.anonymous"
116
122
  end
117
- "GraphQL/#{op_type}.#{op_name}"
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(str_value, _ctx)
27
- Date.iso8601(str_value)
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
- # Invalid input
30
- nil
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
- 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)
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
- field :edges, [edge_type_class, null: edge_nullable],
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
- define_nodes_field(node_nullable) if nodes_field
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.visible?(ctx)
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
- field :nodes, [@node_type, null: nullable],
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 = super
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
- def node_type(node_type = nil, null: self.node_nullable)
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
- field :node, node_type, null: null, description: "The item at the end of the edge.", connection: false
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
@@ -22,7 +22,7 @@ module GraphQL
22
22
 
23
23
  def field_block
24
24
  Proc.new {
25
- argument :id, "ID!", required: true,
25
+ argument :id, "ID!",
26
26
  description: "ID of the object."
27
27
 
28
28
  def resolve(obj, args, ctx)
@@ -22,7 +22,7 @@ module GraphQL
22
22
 
23
23
  def field_block
24
24
  Proc.new {
25
- argument :ids, "[ID!]!", required: true,
25
+ argument :ids, "[ID!]!",
26
26
  description: "IDs of the objects."
27
27
 
28
28
  def resolve(obj, args, ctx)
@@ -2,8 +2,7 @@
2
2
  module GraphQL
3
3
  module Types
4
4
  module Relay
5
- # This can be used for implementing `Query.node(id: ...)`,
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
- NodeField = GraphQL::Schema::Field.new(owner: nil, **HasNodeField.field_options, &HasNodeField.field_block)
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
- # This can be used for implementing `Query.nodes(ids: ...)`,
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
- NodesField = GraphQL::Schema::Field.new(owner: nil, **HasNodesField.field_options, &HasNodesField.field_block)
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
@@ -7,7 +7,7 @@ module GraphQL
7
7
 
8
8
  def self.coerce_result(value, ctx)
9
9
  str = value.to_s
10
- if str.encoding == Encoding::UTF_8
10
+ if str.ascii_only? || str.encoding == Encoding::UTF_8
11
11
  str
12
12
  elsif str.frozen?
13
13
  str.encode(Encoding::UTF_8)
@@ -11,7 +11,7 @@ module GraphQL
11
11
  end
12
12
  end
13
13
 
14
- accepts_definitions :resolve_type, :type_membership_class,
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
 
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module GraphQL
3
- VERSION = "1.12.24"
3
+ VERSION = "1.13.19"
4
4
  end