graphql 1.10.8 → 1.11.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/object_generator.rb +50 -8
  3. data/lib/graphql.rb +4 -4
  4. data/lib/graphql/execution/instrumentation.rb +1 -1
  5. data/lib/graphql/execution/interpreter.rb +1 -1
  6. data/lib/graphql/execution/interpreter/arguments.rb +2 -5
  7. data/lib/graphql/execution/interpreter/runtime.rb +18 -23
  8. data/lib/graphql/execution/multiplex.rb +1 -2
  9. data/lib/graphql/introspection/schema_type.rb +3 -3
  10. data/lib/graphql/object_type.rb +1 -1
  11. data/lib/graphql/pagination/connection.rb +13 -6
  12. data/lib/graphql/pagination/connections.rb +5 -4
  13. data/lib/graphql/query.rb +1 -2
  14. data/lib/graphql/schema.rb +26 -3
  15. data/lib/graphql/schema/argument.rb +6 -0
  16. data/lib/graphql/schema/build_from_definition.rb +7 -12
  17. data/lib/graphql/schema/enum.rb +9 -1
  18. data/lib/graphql/schema/field.rb +60 -79
  19. data/lib/graphql/schema/field/connection_extension.rb +2 -1
  20. data/lib/graphql/schema/input_object.rb +1 -3
  21. data/lib/graphql/schema/interface.rb +5 -0
  22. data/lib/graphql/schema/list.rb +2 -1
  23. data/lib/graphql/schema/loader.rb +3 -0
  24. data/lib/graphql/schema/member.rb +1 -0
  25. data/lib/graphql/schema/member/has_arguments.rb +6 -0
  26. data/lib/graphql/schema/member/has_fields.rb +1 -1
  27. data/lib/graphql/schema/member/has_unresolved_type_error.rb +15 -0
  28. data/lib/graphql/schema/object.rb +7 -0
  29. data/lib/graphql/schema/resolver.rb +14 -0
  30. data/lib/graphql/schema/subscription.rb +1 -1
  31. data/lib/graphql/schema/union.rb +6 -0
  32. data/lib/graphql/schema/warden.rb +7 -2
  33. data/lib/graphql/subscriptions.rb +41 -8
  34. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +49 -4
  35. data/lib/graphql/subscriptions/broadcast_analyzer.rb +84 -0
  36. data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +21 -0
  37. data/lib/graphql/subscriptions/event.rb +16 -1
  38. data/lib/graphql/subscriptions/subscription_root.rb +13 -3
  39. data/lib/graphql/tracing.rb +0 -27
  40. data/lib/graphql/tracing/new_relic_tracing.rb +1 -12
  41. data/lib/graphql/tracing/platform_tracing.rb +14 -0
  42. data/lib/graphql/tracing/scout_tracing.rb +11 -0
  43. data/lib/graphql/types/iso_8601_date.rb +2 -2
  44. data/lib/graphql/types/iso_8601_date_time.rb +19 -15
  45. data/lib/graphql/version.rb +1 -1
  46. metadata +5 -2
@@ -43,9 +43,7 @@ module GraphQL
43
43
  when GraphQL::Language::Nodes::EnumTypeDefinition
44
44
  types[definition.name] = build_enum_type(definition, type_resolver)
45
45
  when GraphQL::Language::Nodes::ObjectTypeDefinition
46
- is_subscription_root = (definition.name == "Subscription" && (schema_definition.nil? || schema_definition.subscription.nil?)) || (schema_definition && (definition.name == schema_definition.subscription))
47
- should_extend_subscription_root = is_subscription_root && interpreter
48
- types[definition.name] = build_object_type(definition, type_resolver, default_resolve: default_resolve, extend_subscription_root: should_extend_subscription_root)
46
+ types[definition.name] = build_object_type(definition, type_resolver, default_resolve: default_resolve)
49
47
  when GraphQL::Language::Nodes::InterfaceTypeDefinition
50
48
  types[definition.name] = build_interface_type(definition, type_resolver)
51
49
  when GraphQL::Language::Nodes::UnionTypeDefinition
@@ -204,7 +202,7 @@ module GraphQL
204
202
  end
205
203
  end
206
204
 
207
- def build_object_type(object_type_definition, type_resolver, default_resolve:, extend_subscription_root:)
205
+ def build_object_type(object_type_definition, type_resolver, default_resolve:)
208
206
  builder = self
209
207
  type_def = nil
210
208
  typed_resolve_fn = ->(field, obj, args, ctx) { default_resolve.call(type_def, field, obj, args, ctx) }
@@ -214,10 +212,6 @@ module GraphQL
214
212
  graphql_name(object_type_definition.name)
215
213
  description(object_type_definition.description)
216
214
  ast_node(object_type_definition)
217
- if extend_subscription_root
218
- # This has to come before `field ...` configurations since it modifies them
219
- extend Subscriptions::SubscriptionRoot
220
- end
221
215
 
222
216
  object_type_definition.interfaces.each do |interface_name|
223
217
  interface_defn = type_resolver.call(interface_name)
@@ -303,7 +297,7 @@ module GraphQL
303
297
 
304
298
  field_definitions.map do |field_definition|
305
299
  type_name = resolve_type_name(field_definition.type)
306
-
300
+ resolve_method_name = "resolve_field_#{field_definition.name}"
307
301
  owner.field(
308
302
  field_definition.name,
309
303
  description: field_definition.description,
@@ -315,14 +309,15 @@ module GraphQL
315
309
  ast_node: field_definition,
316
310
  method_conflict_warning: false,
317
311
  camelize: false,
312
+ resolver_method: resolve_method_name,
318
313
  ) do
319
314
  builder.build_arguments(self, field_definition.arguments, type_resolver)
320
315
 
321
316
  # Don't do this for interfaces
322
317
  if default_resolve
323
- # TODO fragile hack. formalize this API?
324
- define_singleton_method :resolve_field_method do |obj, args, ctx|
325
- default_resolve.call(self, obj.object, args, ctx)
318
+ owner.send(:define_method, resolve_method_name) do |**args|
319
+ field_instance = self.class.get_field(field_definition.name)
320
+ default_resolve.call(field_instance, object, args, context)
326
321
  end
327
322
  end
328
323
  end
@@ -23,6 +23,9 @@ module GraphQL
23
23
  extend GraphQL::Schema::Member::AcceptsDefinition
24
24
  extend GraphQL::Schema::Member::ValidatesInput
25
25
 
26
+ class UnresolvedValueError < GraphQL::EnumType::UnresolvedValueError
27
+ end
28
+
26
29
  class << self
27
30
  # Define a value for this enum
28
31
  # @param graphql_name [String, Symbol] the GraphQL value for this, usually `SCREAMING_CASE`
@@ -94,7 +97,7 @@ module GraphQL
94
97
  if enum_value
95
98
  enum_value.graphql_name
96
99
  else
97
- raise(GraphQL::EnumType::UnresolvedValueError, "Can't resolve enum #{graphql_name} for #{value.inspect}")
100
+ raise(self::UnresolvedValueError, "Can't resolve enum #{graphql_name} for #{value.inspect}")
98
101
  end
99
102
  end
100
103
 
@@ -112,6 +115,11 @@ module GraphQL
112
115
  end
113
116
  end
114
117
 
118
+ def inherited(child_class)
119
+ child_class.const_set(:UnresolvedValueError, Class.new(Schema::Enum::UnresolvedValueError))
120
+ super
121
+ end
122
+
115
123
  private
116
124
 
117
125
  def own_values
@@ -191,9 +191,10 @@ module GraphQL
191
191
  # @param subscription_scope [Symbol, String] A key in `context` which will be used to scope subscription payloads
192
192
  # @param extensions [Array<Class, Hash<Class => Object>>] Named extensions to apply to this field (see also {#extension})
193
193
  # @param trace [Boolean] If true, a {GraphQL::Tracing} tracer will measure this scalar field
194
+ # @param broadcastable [Boolean] Whether or not this field can be distributed in subscription broadcasts
194
195
  # @param ast_node [Language::Nodes::FieldDefinition, nil] If this schema was parsed from definition, this AST node defined the field
195
196
  # @param method_conflict_warning [Boolean] If false, skip the warning if this field's method conflicts with a built-in method
196
- def initialize(type: nil, name: nil, owner: nil, null: nil, field: nil, function: nil, description: nil, deprecation_reason: nil, method: nil, hash_key: nil, resolver_method: nil, resolve: nil, connection: nil, max_page_size: :not_given, scope: nil, introspection: false, camelize: true, trace: nil, complexity: 1, ast_node: nil, extras: [], extensions: EMPTY_ARRAY, connection_extension: self.class.connection_extension, resolver_class: nil, subscription_scope: nil, relay_node_field: false, relay_nodes_field: false, method_conflict_warning: true, arguments: EMPTY_HASH, &definition_block)
197
+ def initialize(type: nil, name: nil, owner: nil, null: nil, field: nil, function: nil, description: nil, deprecation_reason: nil, method: nil, hash_key: nil, resolver_method: nil, resolve: nil, connection: nil, max_page_size: :not_given, scope: nil, introspection: false, camelize: true, trace: nil, complexity: 1, ast_node: nil, extras: [], extensions: EMPTY_ARRAY, connection_extension: self.class.connection_extension, resolver_class: nil, subscription_scope: nil, relay_node_field: false, relay_nodes_field: false, method_conflict_warning: true, broadcastable: nil, arguments: EMPTY_HASH, &definition_block)
197
198
  if name.nil?
198
199
  raise ArgumentError, "missing first `name` argument or keyword `name:`"
199
200
  end
@@ -237,8 +238,8 @@ module GraphQL
237
238
  end
238
239
 
239
240
  # TODO: I think non-string/symbol hash keys are wrongly normalized (eg `1` will not work)
240
- method_name = method || hash_key || @underscored_name
241
- resolver_method ||= @underscored_name.to_sym
241
+ method_name = method || hash_key || name_s
242
+ resolver_method ||= name_s.to_sym
242
243
 
243
244
  @method_str = method_name.to_s
244
245
  @method_sym = method_name.to_sym
@@ -251,6 +252,7 @@ module GraphQL
251
252
  @max_page_size = max_page_size == :not_given ? nil : max_page_size
252
253
  @introspection = introspection
253
254
  @extras = extras
255
+ @broadcastable = broadcastable
254
256
  @resolver_class = resolver_class
255
257
  @scope = scope
256
258
  @trace = trace
@@ -295,6 +297,13 @@ module GraphQL
295
297
  end
296
298
  end
297
299
 
300
+ # If true, subscription updates with this field can be shared between viewers
301
+ # @return [Boolean, nil]
302
+ # @see GraphQL::Subscriptions::BroadcastAnalyzer
303
+ def broadcastable?
304
+ @broadcastable
305
+ end
306
+
298
307
  # @param text [String]
299
308
  # @return [String]
300
309
  def description(text = nil)
@@ -534,7 +543,7 @@ module GraphQL
534
543
  @resolve_proc.call(extended_obj, args, ctx)
535
544
  end
536
545
  else
537
- public_send_field(after_obj, ruby_args, ctx)
546
+ public_send_field(after_obj, ruby_args, query_ctx)
538
547
  end
539
548
  else
540
549
  err = GraphQL::UnauthorizedFieldError.new(object: inner_obj, type: obj.class, context: ctx, field: self)
@@ -556,34 +565,13 @@ module GraphQL
556
565
  begin
557
566
  # Unwrap the GraphQL object to get the application object.
558
567
  application_object = object.object
559
- if self.authorized?(application_object, args, ctx)
560
- # Apply field extensions
561
- with_extensions(object, args, ctx) do |extended_obj, extended_args|
562
- field_receiver = if @resolver_class
563
- resolver_obj = if extended_obj.is_a?(GraphQL::Schema::Object)
564
- extended_obj.object
565
- else
566
- extended_obj
567
- end
568
- @resolver_class.new(object: resolver_obj, context: ctx, field: self)
569
- else
570
- extended_obj
571
- end
572
-
573
- if field_receiver.respond_to?(@resolver_method)
574
- # Call the method with kwargs, if there are any
575
- if extended_args.any?
576
- field_receiver.public_send(@resolver_method, **extended_args)
577
- else
578
- field_receiver.public_send(@resolver_method)
579
- end
580
- else
581
- resolve_field_method(field_receiver, extended_args, ctx)
582
- end
568
+ ctx.schema.after_lazy(self.authorized?(application_object, args, ctx)) do |is_authorized|
569
+ if is_authorized
570
+ public_send_field(object, args, ctx)
571
+ else
572
+ err = GraphQL::UnauthorizedFieldError.new(object: application_object, type: object.class, context: ctx, field: self)
573
+ ctx.schema.unauthorized_field(err)
583
574
  end
584
- else
585
- err = GraphQL::UnauthorizedFieldError.new(object: application_object, type: object.class, context: ctx, field: self)
586
- ctx.schema.unauthorized_field(err)
587
575
  end
588
576
  rescue GraphQL::UnauthorizedFieldError => err
589
577
  err.field ||= self
@@ -595,43 +583,6 @@ module GraphQL
595
583
  err
596
584
  end
597
585
 
598
- # Find a way to resolve this field, checking:
599
- #
600
- # - Hash keys, if the wrapped object is a hash;
601
- # - A method on the wrapped object;
602
- # - Or, raise not implemented.
603
- #
604
- # This can be overridden by defining a method on the object type.
605
- # @param obj [GraphQL::Schema::Object]
606
- # @param ruby_kwargs [Hash<Symbol => Object>]
607
- # @param ctx [GraphQL::Query::Context]
608
- def resolve_field_method(obj, ruby_kwargs, ctx)
609
- if obj.object.is_a?(Hash)
610
- inner_object = obj.object
611
- if inner_object.key?(@method_sym)
612
- inner_object[@method_sym]
613
- else
614
- inner_object[@method_str]
615
- end
616
- elsif obj.object.respond_to?(@method_sym)
617
- if ruby_kwargs.any?
618
- obj.object.public_send(@method_sym, **ruby_kwargs)
619
- else
620
- obj.object.public_send(@method_sym)
621
- end
622
- else
623
- raise <<-ERR
624
- Failed to implement #{@owner.graphql_name}.#{@name}, tried:
625
-
626
- - `#{obj.class}##{@resolver_method}`, which did not exist
627
- - `#{obj.object.class}##{@method_sym}`, which did not exist
628
- - Looking up hash key `#{@method_sym.inspect}` or `#{@method_str.inspect}` on `#{obj.object}`, but it wasn't a Hash
629
-
630
- To implement this field, define one of the methods above (and check for typos)
631
- ERR
632
- end
633
- end
634
-
635
586
  # @param ctx [GraphQL::Query::Context::FieldResolutionContext]
636
587
  def fetch_extra(extra_name, ctx)
637
588
  if extra_name != :path && extra_name != :ast_node && respond_to?(extra_name)
@@ -672,6 +623,8 @@ module GraphQL
672
623
  else
673
624
  load_application_object(arg_defn, loads, value, field_ctx.query.context)
674
625
  end
626
+ elsif arg_defn.type.list? && value.is_a?(Array)
627
+ field_ctx.schema.after_any_lazies(value, &:itself)
675
628
  else
676
629
  value
677
630
  end
@@ -700,24 +653,52 @@ module GraphQL
700
653
  end
701
654
  end
702
655
 
703
- def public_send_field(obj, ruby_kwargs, field_ctx)
704
- query_ctx = field_ctx.query.context
705
- with_extensions(obj, ruby_kwargs, query_ctx) do |extended_obj, extended_args|
656
+ def public_send_field(unextended_obj, unextended_ruby_kwargs, query_ctx)
657
+ with_extensions(unextended_obj, unextended_ruby_kwargs, query_ctx) do |obj, ruby_kwargs|
706
658
  if @resolver_class
707
- if extended_obj.is_a?(GraphQL::Schema::Object)
708
- extended_obj = extended_obj.object
659
+ if obj.is_a?(GraphQL::Schema::Object)
660
+ obj = obj.object
709
661
  end
710
- extended_obj = @resolver_class.new(object: extended_obj, context: query_ctx, field: self)
662
+ obj = @resolver_class.new(object: obj, context: query_ctx, field: self)
711
663
  end
712
664
 
713
- if extended_obj.respond_to?(@resolver_method)
714
- if extended_args.any?
715
- extended_obj.public_send(@resolver_method, **extended_args)
665
+ # Find a way to resolve this field, checking:
666
+ #
667
+ # - A method on the type instance;
668
+ # - Hash keys, if the wrapped object is a hash;
669
+ # - A method on the wrapped object;
670
+ # - Or, raise not implemented.
671
+ #
672
+ if obj.respond_to?(@resolver_method)
673
+ # Call the method with kwargs, if there are any
674
+ if ruby_kwargs.any?
675
+ obj.public_send(@resolver_method, **ruby_kwargs)
676
+ else
677
+ obj.public_send(@resolver_method)
678
+ end
679
+ elsif obj.object.is_a?(Hash)
680
+ inner_object = obj.object
681
+ if inner_object.key?(@method_sym)
682
+ inner_object[@method_sym]
716
683
  else
717
- extended_obj.public_send(@resolver_method)
684
+ inner_object[@method_str]
685
+ end
686
+ elsif obj.object.respond_to?(@method_sym)
687
+ if ruby_kwargs.any?
688
+ obj.object.public_send(@method_sym, **ruby_kwargs)
689
+ else
690
+ obj.object.public_send(@method_sym)
718
691
  end
719
692
  else
720
- resolve_field_method(extended_obj, extended_args, query_ctx)
693
+ raise <<-ERR
694
+ Failed to implement #{@owner.graphql_name}.#{@name}, tried:
695
+
696
+ - `#{obj.class}##{@resolver_method}`, which did not exist
697
+ - `#{obj.object.class}##{@method_sym}`, which did not exist
698
+ - Looking up hash key `#{@method_sym.inspect}` or `#{@method_str.inspect}` on `#{obj.object}`, but it wasn't a Hash
699
+
700
+ To implement this field, define one of the methods above (and check for typos)
701
+ ERR
721
702
  end
722
703
  end
723
704
  end
@@ -31,6 +31,7 @@ module GraphQL
31
31
  elsif value.is_a?(GraphQL::Pagination::Connection)
32
32
  # update the connection with some things that may not have been provided
33
33
  value.context ||= context
34
+ value.parent ||= object.object
34
35
  value.first_value ||= arguments[:first]
35
36
  value.after_value ||= arguments[:after]
36
37
  value.last_value ||= arguments[:last]
@@ -41,7 +42,7 @@ module GraphQL
41
42
  value
42
43
  elsif context.schema.new_connections?
43
44
  wrappers = context.namespace(:connections)[:all_wrappers] ||= context.schema.connections.all_wrappers
44
- context.schema.connections.wrap(field, value, arguments, context, wrappers: wrappers)
45
+ context.schema.connections.wrap(field, object.object, value, arguments, context, wrappers: wrappers)
45
46
  else
46
47
  if object.is_a?(GraphQL::Schema::Object)
47
48
  object = object.object
@@ -21,10 +21,8 @@ module GraphQL
21
21
  @ruby_style_hash = @arguments.to_kwargs
22
22
  end
23
23
  # Apply prepares, not great to have it duplicated here.
24
- @arguments_by_keyword = {}
25
24
  maybe_lazies = []
26
- self.class.arguments.each do |name, arg_defn|
27
- @arguments_by_keyword[arg_defn.keyword] = arg_defn
25
+ self.class.arguments.each_value do |arg_defn|
28
26
  ruby_kwargs_key = arg_defn.keyword
29
27
 
30
28
  if @ruby_style_hash.key?(ruby_kwargs_key)
@@ -14,6 +14,7 @@ module GraphQL
14
14
  include GraphQL::Schema::Member::RelayShortcuts
15
15
  include GraphQL::Schema::Member::Scoped
16
16
  include GraphQL::Schema::Member::HasAstNode
17
+ include GraphQL::Schema::Member::HasUnresolvedTypeError
17
18
 
18
19
  # Methods defined in this block will be:
19
20
  # - Added as class methods to this interface
@@ -74,6 +75,10 @@ module GraphQL
74
75
  if overridden_graphql_name
75
76
  child_class.graphql_name(overridden_graphql_name)
76
77
  end
78
+ # If interfaces are mixed into each other, only define this class once
79
+ if !child_class.const_defined?(:UnresolvedTypeError, false)
80
+ add_unresolved_type_error(child_class)
81
+ end
77
82
  elsif child_class < GraphQL::Schema::Object
78
83
  # This is being included into an object type, make sure it's using `implements(...)`
79
84
  backtrace_line = caller(0, 10).find { |line| line.include?("schema/object.rb") && line.include?("in `implements'")}
@@ -44,7 +44,8 @@ module GraphQL
44
44
  if value.nil?
45
45
  nil
46
46
  else
47
- ensure_array(value).map { |item| item.nil? ? item : of_type.coerce_input(item, ctx) }
47
+ coerced = ensure_array(value).map { |item| item.nil? ? item : of_type.coerce_input(item, ctx) }
48
+ ctx.schema.after_any_lazies(coerced, &:itself)
48
49
  end
49
50
  end
50
51
 
@@ -157,6 +157,7 @@ module GraphQL
157
157
  type: type_resolver.call(field_hash["type"]),
158
158
  description: field_hash["description"],
159
159
  null: true,
160
+ camelize: false,
160
161
  ) do
161
162
  if field_hash["args"].any?
162
163
  loader.build_arguments(self, field_hash["args"], type_resolver)
@@ -171,6 +172,8 @@ module GraphQL
171
172
  type: type_resolver.call(arg["type"]),
172
173
  description: arg["description"],
173
174
  required: false,
175
+ method_access: false,
176
+ camelize: false,
174
177
  }
175
178
 
176
179
  if arg["defaultValue"]
@@ -5,6 +5,7 @@ require 'graphql/schema/member/cached_graphql_definition'
5
5
  require 'graphql/schema/member/graphql_type_names'
6
6
  require 'graphql/schema/member/has_ast_node'
7
7
  require 'graphql/schema/member/has_path'
8
+ require 'graphql/schema/member/has_unresolved_type_error'
8
9
  require 'graphql/schema/member/relay_shortcuts'
9
10
  require 'graphql/schema/member/scoped'
10
11
  require 'graphql/schema/member/type_system_helpers'
@@ -135,6 +135,12 @@ module GraphQL
135
135
  end
136
136
  end
137
137
 
138
+ def arguments_statically_coercible?
139
+ return @arguments_statically_coercible if defined?(@arguments_statically_coercible)
140
+
141
+ @arguments_statically_coercible = arguments.each_value.all?(&:statically_coercible?)
142
+ end
143
+
138
144
  module ArgumentClassAccessor
139
145
  def argument_class(new_arg_class = nil)
140
146
  if new_arg_class
@@ -47,7 +47,7 @@ module GraphQL
47
47
  # A list of GraphQL-Ruby keywords.
48
48
  #
49
49
  # @api private
50
- GRAPHQL_RUBY_KEYWORDS = [:context, :object, :method]
50
+ GRAPHQL_RUBY_KEYWORDS = [:context, :object, :method, :raw_value]
51
51
 
52
52
  # A list of field names that we should advise users to pick a different
53
53
  # resolve method name.
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ class Schema
5
+ class Member
6
+ # Set up a type-specific error to make debugging & bug tracker integration better
7
+ module HasUnresolvedTypeError
8
+ private
9
+ def add_unresolved_type_error(child_class)
10
+ child_class.const_set(:UnresolvedTypeError, Class.new(GraphQL::UnresolvedTypeError))
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -71,6 +71,13 @@ module GraphQL
71
71
  end
72
72
 
73
73
  class << self
74
+ # Set up a type-specific invalid null error to use when this object's non-null fields wrongly return `nil`.
75
+ # It should help with debugging and bug tracker integrations.
76
+ def inherited(child_class)
77
+ child_class.const_set(:InvalidNullError, Class.new(GraphQL::InvalidNullError))
78
+ super
79
+ end
80
+
74
81
  def implements(*new_interfaces, **options)
75
82
  new_memberships = []
76
83
  new_interfaces.each do |int|
@@ -250,6 +250,19 @@ module GraphQL
250
250
  @complexity || (superclass.respond_to?(:complexity) ? superclass.complexity : 1)
251
251
  end
252
252
 
253
+ def broadcastable(new_broadcastable)
254
+ @broadcastable = new_broadcastable
255
+ end
256
+
257
+ # @return [Boolean, nil]
258
+ def broadcastable?
259
+ if defined?(@broadcastable)
260
+ @broadcastable
261
+ else
262
+ (superclass.respond_to?(:broadcastable?) ? superclass.broadcastable? : nil)
263
+ end
264
+ end
265
+
253
266
  def field_options
254
267
  {
255
268
  type: type_expr,
@@ -261,6 +274,7 @@ module GraphQL
261
274
  null: null,
262
275
  complexity: complexity,
263
276
  extensions: extensions,
277
+ broadcastable: broadcastable?,
264
278
  }
265
279
  end
266
280