graphql 2.0.32 → 2.1.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.

Potentially problematic release.


This version of graphql might be problematic. Click here for more details.

Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/lib/graphql/execution/interpreter/runtime.rb +27 -34
  3. data/lib/graphql/language/document_from_schema_definition.rb +6 -16
  4. data/lib/graphql/language/nodes.rb +1 -4
  5. data/lib/graphql/language/printer.rb +233 -145
  6. data/lib/graphql/language/sanitized_printer.rb +14 -21
  7. data/lib/graphql/language/visitor.rb +56 -122
  8. data/lib/graphql/pagination/connection.rb +23 -1
  9. data/lib/graphql/query.rb +2 -19
  10. data/lib/graphql/rake_task.rb +3 -12
  11. data/lib/graphql/schema/argument.rb +5 -3
  12. data/lib/graphql/schema/build_from_definition.rb +7 -8
  13. data/lib/graphql/schema/directive.rb +1 -1
  14. data/lib/graphql/schema/enum_value.rb +1 -1
  15. data/lib/graphql/schema/field/scope_extension.rb +7 -1
  16. data/lib/graphql/schema/field.rb +1 -1
  17. data/lib/graphql/schema/input_object.rb +6 -8
  18. data/lib/graphql/schema/interface.rb +1 -5
  19. data/lib/graphql/schema/member/has_directives.rb +1 -1
  20. data/lib/graphql/schema/member/has_fields.rb +1 -1
  21. data/lib/graphql/schema/member/has_interfaces.rb +1 -1
  22. data/lib/graphql/schema/member/scoped.rb +19 -0
  23. data/lib/graphql/schema/member/type_system_helpers.rb +1 -1
  24. data/lib/graphql/schema/object.rb +8 -0
  25. data/lib/graphql/schema/printer.rb +8 -7
  26. data/lib/graphql/schema/subscription.rb +11 -4
  27. data/lib/graphql/schema/warden.rb +3 -34
  28. data/lib/graphql/schema.rb +4 -20
  29. data/lib/graphql/static_validation/validation_context.rb +0 -3
  30. data/lib/graphql/static_validation.rb +0 -1
  31. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +2 -1
  32. data/lib/graphql/subscriptions.rb +11 -6
  33. data/lib/graphql/tracing/appoptics_trace.rb +0 -4
  34. data/lib/graphql/tracing/appsignal_trace.rb +0 -4
  35. data/lib/graphql/tracing/data_dog_trace.rb +34 -25
  36. data/lib/graphql/tracing/data_dog_tracing.rb +21 -7
  37. data/lib/graphql/tracing/notifications_trace.rb +0 -4
  38. data/lib/graphql/tracing/platform_trace.rb +0 -5
  39. data/lib/graphql/tracing/prometheus_trace.rb +0 -4
  40. data/lib/graphql/tracing/scout_trace.rb +0 -3
  41. data/lib/graphql/tracing/statsd_trace.rb +0 -4
  42. data/lib/graphql/types/relay/connection_behaviors.rb +20 -3
  43. data/lib/graphql/types/relay/edge_behaviors.rb +8 -1
  44. data/lib/graphql/version.rb +1 -1
  45. data/lib/graphql.rb +0 -1
  46. metadata +24 -37
  47. data/lib/graphql/filter.rb +0 -59
  48. data/lib/graphql/static_validation/type_stack.rb +0 -216
@@ -31,11 +31,6 @@ module GraphQL
31
31
  # visitor.count
32
32
  # # => 3
33
33
  class Visitor
34
- # If any hook returns this value, the {Visitor} stops visiting this
35
- # node right away
36
- # @deprecated Use `super` to continue the visit; or don't call it to halt.
37
- SKIP = :_skip
38
-
39
34
  class DeleteNode; end
40
35
 
41
36
  # When this is returned from a visitor method,
@@ -44,25 +39,13 @@ module GraphQL
44
39
 
45
40
  def initialize(document)
46
41
  @document = document
47
- @visitors = {}
48
42
  @result = nil
49
43
  end
50
44
 
51
45
  # @return [GraphQL::Language::Nodes::Document] The document with any modifications applied
52
46
  attr_reader :result
53
47
 
54
- # Get a {NodeVisitor} for `node_class`
55
- # @param node_class [Class] The node class that you want to listen to
56
- # @return [NodeVisitor]
57
- #
58
- # @example Run a hook whenever you enter a new Field
59
- # visitor[GraphQL::Language::Nodes::Field] << ->(node, parent) { p "Here's a field" }
60
- # @deprecated see `on_` methods, like {#on_field}
61
- def [](node_class)
62
- @visitors[node_class] ||= NodeVisitor.new
63
- end
64
-
65
- # Visit `document` and all children, applying hooks as you go
48
+ # Visit `document` and all children
66
49
  # @return [void]
67
50
  def visit
68
51
  # `@document` may be any kind of node:
@@ -76,6 +59,61 @@ module GraphQL
76
59
  end
77
60
  end
78
61
 
62
+ # We don't use `alias` here because it breaks `super`
63
+ def self.make_visit_methods(ast_node_class)
64
+ node_method = ast_node_class.visit_method
65
+ children_of_type = ast_node_class.children_of_type
66
+ child_visit_method = :"#{node_method}_children"
67
+
68
+ class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
69
+ # The default implementation for visiting an AST node.
70
+ # It doesn't _do_ anything, but it continues to visiting the node's children.
71
+ # To customize this hook, override one of its make_visit_methods (or the base method?)
72
+ # in your subclasses.
73
+ #
74
+ # @param node [GraphQL::Language::Nodes::AbstractNode] the node being visited
75
+ # @param parent [GraphQL::Language::Nodes::AbstractNode, nil] the previously-visited node, or `nil` if this is the root node.
76
+ # @return [Array, nil] If there were modifications, it returns an array of new nodes, otherwise, it returns `nil`.
77
+ def #{node_method}(node, parent)
78
+ if node.equal?(DELETE_NODE)
79
+ # This might be passed to `super(DELETE_NODE, ...)`
80
+ # by a user hook, don't want to keep visiting in that case.
81
+ [node, parent]
82
+ else
83
+ new_node = node
84
+ #{
85
+ if method_defined?(child_visit_method)
86
+ "new_node = #{child_visit_method}(new_node)"
87
+ elsif children_of_type
88
+ children_of_type.map do |child_accessor, child_class|
89
+ "node.#{child_accessor}.each do |child_node|
90
+ new_child_and_node = #{child_class.visit_method}_with_modifications(child_node, new_node)
91
+ # Reassign `node` in case the child hook makes a modification
92
+ if new_child_and_node.is_a?(Array)
93
+ new_node = new_child_and_node[1]
94
+ end
95
+ end"
96
+ end.join("\n")
97
+ else
98
+ ""
99
+ end
100
+ }
101
+
102
+ if new_node.equal?(node)
103
+ [node, parent]
104
+ else
105
+ [new_node, parent]
106
+ end
107
+ end
108
+ end
109
+
110
+ def #{node_method}_with_modifications(node, parent)
111
+ new_node_and_new_parent = #{node_method}(node, parent)
112
+ apply_modifications(node, parent, new_node_and_new_parent)
113
+ end
114
+ RUBY
115
+ end
116
+
79
117
  def on_document_children(document_node)
80
118
  new_node = document_node
81
119
  document_node.children.each do |child_node|
@@ -176,68 +214,6 @@ module GraphQL
176
214
  new_node
177
215
  end
178
216
 
179
- # rubocop:disable Development/NoEvalCop This eval takes static inputs at load-time
180
-
181
- def self.make_visit_methods(ast_node_class)
182
- node_method = ast_node_class.visit_method
183
- children_of_type = ast_node_class.children_of_type
184
- child_visit_method = :"#{node_method}_children"
185
-
186
- class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
187
- # The default implementation for visiting an AST node.
188
- # It doesn't _do_ anything, but it continues to visiting the node's children.
189
- # To customize this hook, override one of its make_visit_methods (or the base method?)
190
- # in your subclasses.
191
- #
192
- # For compatibility, it calls hook procs, too.
193
- # @param node [GraphQL::Language::Nodes::AbstractNode] the node being visited
194
- # @param parent [GraphQL::Language::Nodes::AbstractNode, nil] the previously-visited node, or `nil` if this is the root node.
195
- # @return [Array, nil] If there were modifications, it returns an array of new nodes, otherwise, it returns `nil`.
196
- def #{node_method}(node, parent)
197
- if node.equal?(DELETE_NODE)
198
- # This might be passed to `super(DELETE_NODE, ...)`
199
- # by a user hook, don't want to keep visiting in that case.
200
- [node, parent]
201
- else
202
- # Run hooks if there are any
203
- new_node = node
204
- no_hooks = !@visitors.key?(node.class)
205
- if no_hooks || begin_visit(new_node, parent)
206
- #{
207
- if method_defined?(child_visit_method)
208
- "new_node = #{child_visit_method}(new_node)"
209
- elsif children_of_type
210
- children_of_type.map do |child_accessor, child_class|
211
- "node.#{child_accessor}.each do |child_node|
212
- new_child_and_node = #{child_class.visit_method}_with_modifications(child_node, new_node)
213
- # Reassign `node` in case the child hook makes a modification
214
- if new_child_and_node.is_a?(Array)
215
- new_node = new_child_and_node[1]
216
- end
217
- end"
218
- end.join("\n")
219
- else
220
- ""
221
- end
222
- }
223
- end
224
- end_visit(new_node, parent) unless no_hooks
225
- if new_node.equal?(node)
226
- [node, parent]
227
- else
228
- [new_node, parent]
229
- end
230
- end
231
- end
232
- def #{node_method}_with_modifications(node, parent)
233
- new_node_and_new_parent = #{node_method}(node, parent)
234
- apply_modifications(node, parent, new_node_and_new_parent)
235
- end
236
- RUBY
237
- end
238
-
239
-
240
-
241
217
  [
242
218
  Language::Nodes::Argument,
243
219
  Language::Nodes::Directive,
@@ -278,8 +254,6 @@ module GraphQL
278
254
  make_visit_methods(ast_node_class)
279
255
  end
280
256
 
281
- # rubocop:enable Development/NoEvalCop
282
-
283
257
  private
284
258
 
285
259
  def apply_modifications(node, parent, new_node_and_new_parent)
@@ -308,46 +282,6 @@ module GraphQL
308
282
  new_node_and_new_parent
309
283
  end
310
284
  end
311
-
312
- def begin_visit(node, parent)
313
- node_visitor = self[node.class]
314
- self.class.apply_hooks(node_visitor.enter, node, parent)
315
- end
316
-
317
- # Should global `leave` visitors come first or last?
318
- def end_visit(node, parent)
319
- node_visitor = self[node.class]
320
- self.class.apply_hooks(node_visitor.leave, node, parent)
321
- end
322
-
323
- # If one of the visitors returns SKIP, stop visiting this node
324
- def self.apply_hooks(hooks, node, parent)
325
- hooks.each do |proc|
326
- return false if proc.call(node, parent) == SKIP
327
- end
328
- true
329
- end
330
-
331
- # Collect `enter` and `leave` hooks for classes in {GraphQL::Language::Nodes}
332
- #
333
- # Access {NodeVisitor}s via {GraphQL::Language::Visitor#[]}
334
- class NodeVisitor
335
- # @return [Array<Proc>] Hooks to call when entering a node of this type
336
- attr_reader :enter
337
- # @return [Array<Proc>] Hooks to call when leaving a node of this type
338
- attr_reader :leave
339
-
340
- def initialize
341
- @enter = []
342
- @leave = []
343
- end
344
-
345
- # Shorthand to add a hook to the {#enter} array
346
- # @param hook [Proc] A hook to add
347
- def <<(hook)
348
- enter << hook
349
- end
350
- end
351
285
  end
352
286
  end
353
287
  end
@@ -19,7 +19,14 @@ module GraphQL
19
19
  attr_reader :items
20
20
 
21
21
  # @return [GraphQL::Query::Context]
22
- attr_accessor :context
22
+ attr_reader :context
23
+
24
+ def context=(new_ctx)
25
+ current_runtime_state = Thread.current[:__graphql_runtime_info]
26
+ query_runtime_state = current_runtime_state[new_ctx.query]
27
+ @was_authorized_by_scope_items = query_runtime_state.was_authorized_by_scope_items
28
+ @context = new_ctx
29
+ end
23
30
 
24
31
  # @return [Object] the object this collection belongs to
25
32
  attr_accessor :parent
@@ -83,6 +90,17 @@ module GraphQL
83
90
  else
84
91
  default_page_size
85
92
  end
93
+ @was_authorized_by_scope_items = if @context
94
+ current_runtime_state = Thread.current[:__graphql_runtime_info]
95
+ query_runtime_state = current_runtime_state[@context.query]
96
+ query_runtime_state.was_authorized_by_scope_items
97
+ else
98
+ nil
99
+ end
100
+ end
101
+
102
+ def was_authorized_by_scope_items?
103
+ @was_authorized_by_scope_items
86
104
  end
87
105
 
88
106
  def max_page_size=(new_value)
@@ -247,6 +265,10 @@ module GraphQL
247
265
  def cursor
248
266
  @cursor ||= @connection.cursor_for(@node)
249
267
  end
268
+
269
+ def was_authorized_by_scope_items?
270
+ @connection.was_authorized_by_scope_items?
271
+ end
250
272
  end
251
273
  end
252
274
  end
data/lib/graphql/query.rb CHANGED
@@ -95,15 +95,10 @@ module GraphQL
95
95
  # @param root_value [Object] the object used to resolve fields on the root type
96
96
  # @param max_depth [Numeric] the maximum number of nested selections allowed for this query (falls back to schema-level value)
97
97
  # @param max_complexity [Numeric] the maximum field complexity for this query (falls back to schema-level value)
98
- # @param except [<#call(schema_member, context)>] If provided, objects will be hidden from the schema when `.call(schema_member, context)` returns truthy
99
- # @param only [<#call(schema_member, context)>] If provided, objects will be hidden from the schema when `.call(schema_member, context)` returns false
100
- def initialize(schema, query_string = nil, query: nil, document: nil, context: nil, variables: nil, validate: true, static_validator: nil, subscription_topic: nil, operation_name: nil, root_value: nil, max_depth: schema.max_depth, max_complexity: schema.max_complexity, except: nil, only: nil, warden: nil)
98
+ def initialize(schema, query_string = nil, query: nil, document: nil, context: nil, variables: nil, validate: true, static_validator: nil, subscription_topic: nil, operation_name: nil, root_value: nil, max_depth: schema.max_depth, max_complexity: schema.max_complexity, warden: nil)
101
99
  # Even if `variables: nil` is passed, use an empty hash for simpler logic
102
100
  variables ||= {}
103
101
  @schema = schema
104
- if only || except
105
- merge_filters(except: except, only: only)
106
- end
107
102
  @context = schema.context_class.new(query: self, object: root_value, values: context)
108
103
  @warden = warden
109
104
  @subscription_topic = subscription_topic
@@ -129,7 +124,6 @@ module GraphQL
129
124
  raise ArgumentError, "context[:tracers] are not supported without `trace_with(GraphQL::Tracing::CallLegacyTracers)` in the schema configuration, please add it."
130
125
  end
131
126
 
132
-
133
127
  @analysis_errors = []
134
128
  if variables.is_a?(String)
135
129
  raise ArgumentError, "Query variables should be a Hash, not a String. Try JSON.parse to prepare variables."
@@ -354,17 +348,6 @@ module GraphQL
354
348
  with_prepared_ast { @query }
355
349
  end
356
350
 
357
- # @return [void]
358
- def merge_filters(only: nil, except: nil)
359
- if @prepared_ast
360
- raise "Can't add filters after preparing the query"
361
- else
362
- @filter ||= @schema.default_filter
363
- @filter = @filter.merge(only: only, except: except)
364
- end
365
- nil
366
- end
367
-
368
351
  def subscription?
369
352
  with_prepared_ast { @subscription }
370
353
  end
@@ -400,7 +383,7 @@ module GraphQL
400
383
 
401
384
  def prepare_ast
402
385
  @prepared_ast = true
403
- @warden ||= @schema.warden_class.new(@filter, schema: @schema, context: @context)
386
+ @warden ||= @schema.warden_class.new(schema: @schema, context: @context)
404
387
  parse_error = nil
405
388
  @document ||= begin
406
389
  if query_string
@@ -9,8 +9,7 @@ module GraphQL
9
9
  # By default, schemas are looked up by name as constants using `schema_name:`.
10
10
  # You can provide a `load_schema` function to return your schema another way.
11
11
  #
12
- # `load_context:`, `only:` and `except:` are supported so that
13
- # you can keep an eye on how filters affect your schema.
12
+ # Use `load_context:` and `visible?` to dump schemas under certain visibility constraints.
14
13
  #
15
14
  # @example Dump a Schema to .graphql + .json files
16
15
  # require "graphql/rake_task"
@@ -36,8 +35,6 @@ module GraphQL
36
35
  schema_name: nil,
37
36
  load_schema: ->(task) { Object.const_get(task.schema_name) },
38
37
  load_context: ->(task) { {} },
39
- only: nil,
40
- except: nil,
41
38
  directory: ".",
42
39
  idl_outfile: "schema.graphql",
43
40
  json_outfile: "schema.json",
@@ -68,12 +65,6 @@ module GraphQL
68
65
  # @return [<#call(task)>] A callable for loading the query context
69
66
  attr_accessor :load_context
70
67
 
71
- # @return [<#call(member, ctx)>, nil] A filter for this task
72
- attr_accessor :only
73
-
74
- # @return [<#call(member, ctx)>, nil] A filter for this task
75
- attr_accessor :except
76
-
77
68
  # @return [String] target for IDL task
78
69
  attr_accessor :idl_outfile
79
70
 
@@ -117,10 +108,10 @@ module GraphQL
117
108
  include_is_repeatable: include_is_repeatable,
118
109
  include_specified_by_url: include_specified_by_url,
119
110
  include_schema_description: include_schema_description,
120
- only: @only, except: @except, context: context
111
+ context: context
121
112
  )
122
113
  when :to_definition
123
- schema.to_definition(only: @only, except: @except, context: context)
114
+ schema.to_definition(context: context)
124
115
  else
125
116
  raise ArgumentError, "Unexpected schema dump method: #{method_name.inspect}"
126
117
  end
@@ -53,7 +53,6 @@ module GraphQL
53
53
  def initialize(arg_name = nil, type_expr = nil, desc = nil, required: true, type: nil, name: nil, loads: nil, description: nil, ast_node: nil, default_value: NOT_CONFIGURED, as: nil, from_resolver: false, camelize: true, prepare: nil, owner:, validates: nil, directives: nil, deprecation_reason: nil, replace_null_with_default: false, &definition_block)
54
54
  arg_name ||= name
55
55
  @name = -(camelize ? Member::BuildType.camelize(arg_name.to_s) : arg_name.to_s)
56
- NameValidator.validate!(@name)
57
56
  @type_expr = type_expr || type
58
57
  @description = desc || description
59
58
  @null = required != true
@@ -89,8 +88,11 @@ module GraphQL
89
88
  end
90
89
 
91
90
  if definition_block
92
- # `self` will still be self, it will also be the first argument to the block:
93
- instance_exec(self, &definition_block)
91
+ if definition_block.arity == 1
92
+ instance_exec(self, &definition_block)
93
+ else
94
+ instance_eval(&definition_block)
95
+ end
94
96
  end
95
97
  end
96
98
 
@@ -453,18 +453,17 @@ module GraphQL
453
453
 
454
454
  # Don't do this for interfaces
455
455
  if default_resolve
456
- define_field_resolve_method(owner, resolve_method_name, field_definition.name)
456
+ owner.class_eval <<-RUBY, __FILE__, __LINE__
457
+ # frozen_string_literal: true
458
+ def #{resolve_method_name}(**args)
459
+ field_instance = self.class.get_field("#{field_definition.name}")
460
+ context.schema.definition_default_resolve.call(self.class, field_instance, object, args, context)
461
+ end
462
+ RUBY
457
463
  end
458
464
  end
459
465
  end
460
466
 
461
- def define_field_resolve_method(owner, method_name, field_name)
462
- owner.define_method(method_name) { |**args|
463
- field_instance = self.class.get_field(field_name)
464
- context.schema.definition_default_resolve.call(self.class, field_instance, object, args, context)
465
- }
466
- end
467
-
468
467
  def build_resolve_type(lookup_hash, directives, missing_type_handler)
469
468
  resolve_type_proc = nil
470
469
  resolve_type_proc = ->(ast_node) {
@@ -99,7 +99,7 @@ module GraphQL
99
99
 
100
100
  def inherited(subclass)
101
101
  super
102
- subclass.class_exec do
102
+ subclass.class_eval do
103
103
  @default_graphql_name ||= nil
104
104
  end
105
105
  end
@@ -47,7 +47,7 @@ module GraphQL
47
47
  end
48
48
 
49
49
  if block_given?
50
- instance_exec(self, &block)
50
+ instance_eval(&block)
51
51
  end
52
52
  end
53
53
 
@@ -10,7 +10,13 @@ module GraphQL
10
10
  else
11
11
  ret_type = @field.type.unwrap
12
12
  if ret_type.respond_to?(:scope_items)
13
- ret_type.scope_items(value, context)
13
+ scoped_items = ret_type.scope_items(value, context)
14
+ if !scoped_items.equal?(value) && !ret_type.reauthorize_scoped_objects
15
+ current_runtime_state = Thread.current[:__graphql_runtime_info]
16
+ query_runtime_state = current_runtime_state[context.query]
17
+ query_runtime_state.was_authorized_by_scope_items = true
18
+ end
19
+ scoped_items
14
20
  else
15
21
  value
16
22
  end
@@ -233,7 +233,7 @@ module GraphQL
233
233
 
234
234
  @underscored_name = -Member::BuildType.underscore(name_s)
235
235
  @name = -(camelize ? Member::BuildType.camelize(name_s) : name_s)
236
- NameValidator.validate!(@name)
236
+
237
237
  @description = description
238
238
  @type = @owner_type = @own_validators = @own_directives = @own_arguments = @arguments_statically_coercible = nil # these will be prepared later if necessary
239
239
 
@@ -131,7 +131,12 @@ module GraphQL
131
131
  end
132
132
  end
133
133
  # Add a method access
134
- define_accessor_method(argument_defn.keyword)
134
+ method_name = argument_defn.keyword
135
+ class_eval <<-RUBY, __FILE__, __LINE__
136
+ def #{method_name}
137
+ self[#{method_name.inspect}]
138
+ end
139
+ RUBY
135
140
  argument_defn
136
141
  end
137
142
 
@@ -237,13 +242,6 @@ module GraphQL
237
242
 
238
243
  result
239
244
  end
240
-
241
- private
242
-
243
- def define_accessor_method(method_name)
244
- define_method(method_name) { self[method_name] }
245
- alias_method(method_name, method_name)
246
- end
247
245
  end
248
246
 
249
247
  private
@@ -69,11 +69,7 @@ module GraphQL
69
69
  end
70
70
  elsif child_class < GraphQL::Schema::Object
71
71
  # This is being included into an object type, make sure it's using `implements(...)`
72
- backtrace_line = caller_locations(0, 10).find do |location|
73
- location.base_label == "implements" &&
74
- location.path.end_with?("schema/member/has_interfaces.rb")
75
- end
76
-
72
+ backtrace_line = caller(0, 10).find { |line| line.include?("schema/member/has_interfaces.rb") && line.include?("in `implements'")}
77
73
  if !backtrace_line
78
74
  raise "Attach interfaces using `implements(#{self})`, not `include(#{self})`"
79
75
  end
@@ -6,7 +6,7 @@ module GraphQL
6
6
  module HasDirectives
7
7
  def self.extended(child_cls)
8
8
  super
9
- child_cls.module_exec { self.own_directives = nil }
9
+ child_cls.module_eval { self.own_directives = nil }
10
10
  end
11
11
 
12
12
  def inherited(child_cls)
@@ -180,7 +180,7 @@ module GraphQL
180
180
 
181
181
  def inherited(subclass)
182
182
  super
183
- subclass.class_exec do
183
+ subclass.class_eval do
184
184
  @own_fields ||= nil
185
185
  @field_class ||= nil
186
186
  end
@@ -119,7 +119,7 @@ module GraphQL
119
119
 
120
120
  def inherited(subclass)
121
121
  super
122
- subclass.class_exec do
122
+ subclass.class_eval do
123
123
  @own_interface_type_memberships ||= nil
124
124
  end
125
125
  end
@@ -15,6 +15,25 @@ module GraphQL
15
15
  def scope_items(items, context)
16
16
  items
17
17
  end
18
+
19
+ def reauthorize_scoped_objects(new_value = nil)
20
+ if new_value.nil?
21
+ if @reauthorize_scoped_objects != nil
22
+ @reauthorize_scoped_objects
23
+ else
24
+ find_inherited_value(:reauthorize_scoped_objects, nil)
25
+ end
26
+ else
27
+ @reauthorize_scoped_objects = new_value
28
+ end
29
+ end
30
+
31
+ def inherited(subclass)
32
+ super
33
+ subclass.class_eval do
34
+ @reauthorize_scoped_objects = nil
35
+ end
36
+ end
18
37
  end
19
38
  end
20
39
  end
@@ -43,7 +43,7 @@ module GraphQL
43
43
  private
44
44
 
45
45
  def inherited(subclass)
46
- subclass.class_exec do
46
+ subclass.class_eval do
47
47
  @to_non_null_type ||= nil
48
48
  @to_list_type ||= nil
49
49
  end
@@ -30,6 +30,10 @@ module GraphQL
30
30
  # @see authorized_new to make instances
31
31
  protected :new
32
32
 
33
+ def wrap_scoped(object, context)
34
+ scoped_new(object, context)
35
+ end
36
+
33
37
  # This is called by the runtime to return an object to call methods on.
34
38
  def wrap(object, context)
35
39
  authorized_new(object, context)
@@ -91,6 +95,10 @@ module GraphQL
91
95
  end
92
96
  end
93
97
  end
98
+
99
+ def scoped_new(object, context)
100
+ self.new(object, context)
101
+ end
94
102
  end
95
103
 
96
104
  def initialize(object, context)
@@ -36,15 +36,11 @@ module GraphQL
36
36
 
37
37
  # @param schema [GraphQL::Schema]
38
38
  # @param context [Hash]
39
- # @param only [<#call(member, ctx)>]
40
- # @param except [<#call(member, ctx)>]
41
39
  # @param introspection [Boolean] Should include the introspection types in the string?
42
- def initialize(schema, context: nil, only: nil, except: nil, introspection: false)
40
+ def initialize(schema, context: nil, introspection: false)
43
41
  @document_from_schema = GraphQL::Language::DocumentFromSchemaDefinition.new(
44
42
  schema,
45
43
  context: context,
46
- only: only,
47
- except: except,
48
44
  include_introspection_types: introspection,
49
45
  )
50
46
 
@@ -61,7 +57,12 @@ module GraphQL
61
57
  false
62
58
  end
63
59
  end
64
- schema = Class.new(GraphQL::Schema) { query(query_root) }
60
+ schema = Class.new(GraphQL::Schema) {
61
+ query(query_root)
62
+ def self.visible?(member, _ctx)
63
+ member.graphql_name != "Root"
64
+ end
65
+ }
65
66
 
66
67
  introspection_schema_ast = GraphQL::Language::DocumentFromSchemaDefinition.new(
67
68
  schema,
@@ -94,7 +95,7 @@ module GraphQL
94
95
 
95
96
  class IntrospectionPrinter < GraphQL::Language::Printer
96
97
  def print_schema_definition(schema)
97
- "schema {\n query: Root\n}"
98
+ print_string("schema {\n query: Root\n}")
98
99
  end
99
100
  end
100
101
  end
@@ -28,14 +28,19 @@ module GraphQL
28
28
  def resolve_with_support(**args)
29
29
  result = nil
30
30
  unsubscribed = true
31
- catch :graphql_subscription_unsubscribed do
31
+ unsubscribed_result = catch :graphql_subscription_unsubscribed do
32
32
  result = super
33
33
  unsubscribed = false
34
34
  end
35
35
 
36
36
 
37
37
  if unsubscribed
38
- context.skip
38
+ if unsubscribed_result
39
+ context.namespace(:subscriptions)[:final_update] = true
40
+ unsubscribed_result
41
+ else
42
+ context.skip
43
+ end
39
44
  else
40
45
  result
41
46
  end
@@ -94,9 +99,11 @@ module GraphQL
94
99
  end
95
100
 
96
101
  # Call this to halt execution and remove this subscription from the system
97
- def unsubscribe
102
+ # @param update_value [Object] if present, deliver this update before unsubscribing
103
+ # @return [void]
104
+ def unsubscribe(update_value = nil)
98
105
  context.namespace(:subscriptions)[:unsubscribed] = true
99
- throw :graphql_subscription_unsubscribed
106
+ throw :graphql_subscription_unsubscribed, update_value
100
107
  end
101
108
 
102
109
  READING_SCOPE = ::Object.new