graphql 1.10.8 → 1.11.0

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.
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