graphql 2.0.27 → 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.
- checksums.yaml +4 -4
- data/lib/graphql/execution/interpreter/runtime.rb +23 -21
- data/lib/graphql/language/document_from_schema_definition.rb +6 -16
- data/lib/graphql/language/nodes.rb +1 -1
- data/lib/graphql/language/printer.rb +233 -145
- data/lib/graphql/language/sanitized_printer.rb +14 -21
- data/lib/graphql/language/visitor.rb +18 -81
- data/lib/graphql/pagination/connection.rb +23 -1
- data/lib/graphql/query.rb +2 -19
- data/lib/graphql/rake_task.rb +3 -12
- data/lib/graphql/schema/field/scope_extension.rb +7 -1
- data/lib/graphql/schema/member/scoped.rb +19 -0
- data/lib/graphql/schema/object.rb +8 -0
- data/lib/graphql/schema/printer.rb +8 -7
- data/lib/graphql/schema/subscription.rb +11 -4
- data/lib/graphql/schema/warden.rb +3 -34
- data/lib/graphql/schema.rb +4 -20
- data/lib/graphql/static_validation/validation_context.rb +0 -3
- data/lib/graphql/static_validation.rb +0 -1
- data/lib/graphql/subscriptions/action_cable_subscriptions.rb +2 -1
- data/lib/graphql/subscriptions.rb +11 -6
- data/lib/graphql/types/relay/connection_behaviors.rb +19 -2
- data/lib/graphql/types/relay/edge_behaviors.rb +7 -0
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +0 -1
- metadata +1 -3
- data/lib/graphql/filter.rb +0 -59
- 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
|
-
#
|
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:
|
@@ -88,7 +71,6 @@ module GraphQL
|
|
88
71
|
# To customize this hook, override one of its make_visit_methods (or the base method?)
|
89
72
|
# in your subclasses.
|
90
73
|
#
|
91
|
-
# For compatibility, it calls hook procs, too.
|
92
74
|
# @param node [GraphQL::Language::Nodes::AbstractNode] the node being visited
|
93
75
|
# @param parent [GraphQL::Language::Nodes::AbstractNode, nil] the previously-visited node, or `nil` if this is the root node.
|
94
76
|
# @return [Array, nil] If there were modifications, it returns an array of new nodes, otherwise, it returns `nil`.
|
@@ -98,29 +80,24 @@ module GraphQL
|
|
98
80
|
# by a user hook, don't want to keep visiting in that case.
|
99
81
|
[node, parent]
|
100
82
|
else
|
101
|
-
# Run hooks if there are any
|
102
83
|
new_node = node
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
end
|
121
|
-
}
|
122
|
-
end
|
123
|
-
end_visit(new_node, parent) unless no_hooks
|
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
|
+
}
|
124
101
|
|
125
102
|
if new_node.equal?(node)
|
126
103
|
[node, parent]
|
@@ -305,46 +282,6 @@ module GraphQL
|
|
305
282
|
new_node_and_new_parent
|
306
283
|
end
|
307
284
|
end
|
308
|
-
|
309
|
-
def begin_visit(node, parent)
|
310
|
-
node_visitor = self[node.class]
|
311
|
-
self.class.apply_hooks(node_visitor.enter, node, parent)
|
312
|
-
end
|
313
|
-
|
314
|
-
# Should global `leave` visitors come first or last?
|
315
|
-
def end_visit(node, parent)
|
316
|
-
node_visitor = self[node.class]
|
317
|
-
self.class.apply_hooks(node_visitor.leave, node, parent)
|
318
|
-
end
|
319
|
-
|
320
|
-
# If one of the visitors returns SKIP, stop visiting this node
|
321
|
-
def self.apply_hooks(hooks, node, parent)
|
322
|
-
hooks.each do |proc|
|
323
|
-
return false if proc.call(node, parent) == SKIP
|
324
|
-
end
|
325
|
-
true
|
326
|
-
end
|
327
|
-
|
328
|
-
# Collect `enter` and `leave` hooks for classes in {GraphQL::Language::Nodes}
|
329
|
-
#
|
330
|
-
# Access {NodeVisitor}s via {GraphQL::Language::Visitor#[]}
|
331
|
-
class NodeVisitor
|
332
|
-
# @return [Array<Proc>] Hooks to call when entering a node of this type
|
333
|
-
attr_reader :enter
|
334
|
-
# @return [Array<Proc>] Hooks to call when leaving a node of this type
|
335
|
-
attr_reader :leave
|
336
|
-
|
337
|
-
def initialize
|
338
|
-
@enter = []
|
339
|
-
@leave = []
|
340
|
-
end
|
341
|
-
|
342
|
-
# Shorthand to add a hook to the {#enter} array
|
343
|
-
# @param hook [Proc] A hook to add
|
344
|
-
def <<(hook)
|
345
|
-
enter << hook
|
346
|
-
end
|
347
|
-
end
|
348
285
|
end
|
349
286
|
end
|
350
287
|
end
|
@@ -19,7 +19,14 @@ module GraphQL
|
|
19
19
|
attr_reader :items
|
20
20
|
|
21
21
|
# @return [GraphQL::Query::Context]
|
22
|
-
|
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
|
-
|
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(
|
386
|
+
@warden ||= @schema.warden_class.new(schema: @schema, context: @context)
|
404
387
|
parse_error = nil
|
405
388
|
@document ||= begin
|
406
389
|
if query_string
|
data/lib/graphql/rake_task.rb
CHANGED
@@ -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
|
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
|
-
|
111
|
+
context: context
|
121
112
|
)
|
122
113
|
when :to_definition
|
123
|
-
schema.to_definition(
|
114
|
+
schema.to_definition(context: context)
|
124
115
|
else
|
125
116
|
raise ArgumentError, "Unexpected schema dump method: #{method_name.inspect}"
|
126
117
|
end
|
@@ -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
|
@@ -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
|
@@ -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,
|
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) {
|
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
|
-
|
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
|
-
|
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
|
@@ -4,37 +4,12 @@ require 'set'
|
|
4
4
|
|
5
5
|
module GraphQL
|
6
6
|
class Schema
|
7
|
-
# Restrict access to a {GraphQL::Schema} with a user-defined
|
7
|
+
# Restrict access to a {GraphQL::Schema} with a user-defined `visible?` implementations.
|
8
8
|
#
|
9
9
|
# When validating and executing a query, all access to schema members
|
10
10
|
# should go through a warden. If you access the schema directly,
|
11
11
|
# you may show a client something that it shouldn't be allowed to see.
|
12
12
|
#
|
13
|
-
# @example Hiding private fields
|
14
|
-
# private_members = -> (member, ctx) { member.metadata[:private] }
|
15
|
-
# result = Schema.execute(query_string, except: private_members)
|
16
|
-
#
|
17
|
-
# @example Custom filter implementation
|
18
|
-
# # It must respond to `#call(member)`.
|
19
|
-
# class MissingRequiredFlags
|
20
|
-
# def initialize(user)
|
21
|
-
# @user = user
|
22
|
-
# end
|
23
|
-
#
|
24
|
-
# # Return `false` if any required flags are missing
|
25
|
-
# def call(member, ctx)
|
26
|
-
# member.metadata[:required_flags].any? do |flag|
|
27
|
-
# !@user.has_flag?(flag)
|
28
|
-
# end
|
29
|
-
# end
|
30
|
-
# end
|
31
|
-
#
|
32
|
-
# # Then, use the custom filter in query:
|
33
|
-
# missing_required_flags = MissingRequiredFlags.new(current_user)
|
34
|
-
#
|
35
|
-
# # This query can only access members which match the user's flags
|
36
|
-
# result = Schema.execute(query_string, except: missing_required_flags)
|
37
|
-
#
|
38
13
|
# @api private
|
39
14
|
class Warden
|
40
15
|
def self.from_context(context)
|
@@ -114,22 +89,16 @@ module GraphQL
|
|
114
89
|
def interfaces(obj_type); obj_type.interfaces; end
|
115
90
|
end
|
116
91
|
|
117
|
-
# @param filter [<#call(member)>] Objects are hidden when `.call(member, ctx)` returns true
|
118
92
|
# @param context [GraphQL::Query::Context]
|
119
93
|
# @param schema [GraphQL::Schema]
|
120
|
-
def initialize(
|
94
|
+
def initialize(context:, schema:)
|
121
95
|
@schema = schema
|
122
96
|
# Cache these to avoid repeated hits to the inheritance chain when one isn't present
|
123
97
|
@query = @schema.query
|
124
98
|
@mutation = @schema.mutation
|
125
99
|
@subscription = @schema.subscription
|
126
100
|
@context = context
|
127
|
-
@visibility_cache =
|
128
|
-
read_through { |m| filter.call(m, context) }
|
129
|
-
else
|
130
|
-
read_through { |m| schema.visible?(m, context) }
|
131
|
-
end
|
132
|
-
|
101
|
+
@visibility_cache = read_through { |m| schema.visible?(m, context) }
|
133
102
|
@visibility_cache.compare_by_identity
|
134
103
|
# Initialize all ivars to improve object shape consistency:
|
135
104
|
@types = @visible_types = @reachable_types = @visible_parent_fields =
|
data/lib/graphql/schema.rb
CHANGED
@@ -222,7 +222,7 @@ module GraphQL
|
|
222
222
|
# @param include_specified_by_url [Boolean] If true, scalar types' `specifiedByUrl:` will be included in the response
|
223
223
|
# @param include_is_one_of [Boolean] If true, `isOneOf: true|false` will be included with input objects
|
224
224
|
# @return [Hash] GraphQL result
|
225
|
-
def as_json(
|
225
|
+
def as_json(context: {}, include_deprecated_args: true, include_schema_description: false, include_is_repeatable: false, include_specified_by_url: false, include_is_one_of: false)
|
226
226
|
introspection_query = Introspection.query(
|
227
227
|
include_deprecated_args: include_deprecated_args,
|
228
228
|
include_schema_description: include_schema_description,
|
@@ -231,16 +231,14 @@ module GraphQL
|
|
231
231
|
include_specified_by_url: include_specified_by_url,
|
232
232
|
)
|
233
233
|
|
234
|
-
execute(introspection_query,
|
234
|
+
execute(introspection_query, context: context).to_h
|
235
235
|
end
|
236
236
|
|
237
237
|
# Return the GraphQL IDL for the schema
|
238
238
|
# @param context [Hash]
|
239
|
-
# @param only [<#call(member, ctx)>]
|
240
|
-
# @param except [<#call(member, ctx)>]
|
241
239
|
# @return [String]
|
242
|
-
def to_definition(
|
243
|
-
GraphQL::Schema::Printer.print_schema(self,
|
240
|
+
def to_definition(context: {})
|
241
|
+
GraphQL::Schema::Printer.print_schema(self, context: context)
|
244
242
|
end
|
245
243
|
|
246
244
|
# Return the GraphQL::Language::Document IDL AST for the schema
|
@@ -268,20 +266,6 @@ module GraphQL
|
|
268
266
|
@find_cache[path] ||= @finder.find(path)
|
269
267
|
end
|
270
268
|
|
271
|
-
def default_filter
|
272
|
-
GraphQL::Filter.new(except: default_mask)
|
273
|
-
end
|
274
|
-
|
275
|
-
def default_mask(new_mask = nil)
|
276
|
-
if new_mask
|
277
|
-
line = caller(2, 10).find { |l| !l.include?("lib/graphql") }
|
278
|
-
GraphQL::Deprecation.warn("GraphQL::Filter and Schema.mask are deprecated and will be removed in v2.1.0. Implement `visible?` on your schema members instead (https://graphql-ruby.org/authorization/visibility.html).\n #{line}")
|
279
|
-
@own_default_mask = new_mask
|
280
|
-
else
|
281
|
-
@own_default_mask || find_inherited_value(:default_mask, Schema::NullMask)
|
282
|
-
end
|
283
|
-
end
|
284
|
-
|
285
269
|
def static_validator
|
286
270
|
GraphQL::StaticValidation::Validator.new(schema: self)
|
287
271
|
end
|
@@ -8,9 +8,6 @@ module GraphQL
|
|
8
8
|
# It provides access to the schema & fragments which validators may read from.
|
9
9
|
#
|
10
10
|
# It holds a list of errors which each validator may add to.
|
11
|
-
#
|
12
|
-
# It also provides limited access to the {TypeStack} instance,
|
13
|
-
# which tracks state as you climb in and out of different fields.
|
14
11
|
class ValidationContext
|
15
12
|
extend Forwardable
|
16
13
|
|
@@ -1,7 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
require "graphql/static_validation/error"
|
3
3
|
require "graphql/static_validation/definition_dependencies"
|
4
|
-
require "graphql/static_validation/type_stack"
|
5
4
|
require "graphql/static_validation/validator"
|
6
5
|
require "graphql/static_validation/validation_context"
|
7
6
|
require "graphql/static_validation/validation_timeout_error"
|
@@ -124,7 +124,8 @@ module GraphQL
|
|
124
124
|
# This subscription was re-evaluated.
|
125
125
|
# Send it to the specific stream where this client was waiting.
|
126
126
|
def deliver(subscription_id, result)
|
127
|
-
|
127
|
+
has_more = !result.context.namespace(:subscriptions)[:final_update]
|
128
|
+
payload = { result: result.to_h, more: has_more }
|
128
129
|
@action_cable.server.broadcast(stream_subscription_name(subscription_id), payload)
|
129
130
|
end
|
130
131
|
|
@@ -125,10 +125,10 @@ module GraphQL
|
|
125
125
|
variables: variables,
|
126
126
|
root_value: object,
|
127
127
|
}
|
128
|
-
|
128
|
+
|
129
129
|
# merge event's and query's context together
|
130
130
|
context.merge!(event.context) unless event.context.nil? || context.nil?
|
131
|
-
|
131
|
+
|
132
132
|
execute_options[:validate] = validate_update?(**execute_options)
|
133
133
|
result = @schema.execute(**execute_options)
|
134
134
|
subscriptions_context = result.context.namespace(:subscriptions)
|
@@ -136,11 +136,9 @@ module GraphQL
|
|
136
136
|
result = nil
|
137
137
|
end
|
138
138
|
|
139
|
-
unsubscribed
|
140
|
-
|
141
|
-
if unsubscribed
|
139
|
+
if subscriptions_context[:unsubscribed] && !subscriptions_context[:final_update]
|
142
140
|
# `unsubscribe` was called, clean up on our side
|
143
|
-
#
|
141
|
+
# The transport should also send `{more: false}` to client
|
144
142
|
delete_subscription(subscription_id)
|
145
143
|
result = nil
|
146
144
|
end
|
@@ -164,7 +162,14 @@ module GraphQL
|
|
164
162
|
res = execute_update(subscription_id, event, object)
|
165
163
|
if !res.nil?
|
166
164
|
deliver(subscription_id, res)
|
165
|
+
|
166
|
+
if res.context.namespace(:subscriptions)[:unsubscribed]
|
167
|
+
# `unsubscribe` was called, clean up on our side
|
168
|
+
# The transport should also send `{more: false}` to client
|
169
|
+
delete_subscription(subscription_id)
|
170
|
+
end
|
167
171
|
end
|
172
|
+
|
168
173
|
end
|
169
174
|
|
170
175
|
# Event `event` occurred on `object`,
|
@@ -67,9 +67,8 @@ module GraphQL
|
|
67
67
|
type: [edge_type_class, null: edge_nullable],
|
68
68
|
null: edges_nullable,
|
69
69
|
description: "A list of edges.",
|
70
|
+
scope: false, # Assume that the connection was already scoped.
|
70
71
|
connection: false,
|
71
|
-
# Assume that the connection was scoped before this step:
|
72
|
-
scope: false,
|
73
72
|
}
|
74
73
|
|
75
74
|
if field_options
|
@@ -170,6 +169,24 @@ module GraphQL
|
|
170
169
|
obj_type.field :page_info, GraphQL::Types::Relay::PageInfo, null: false, description: "Information to aid in pagination."
|
171
170
|
end
|
172
171
|
end
|
172
|
+
|
173
|
+
def edges
|
174
|
+
# Assume that whatever authorization needed to happen
|
175
|
+
# already happened at the connection level.
|
176
|
+
current_runtime_state = Thread.current[:__graphql_runtime_info]
|
177
|
+
query_runtime_state = current_runtime_state[context.query]
|
178
|
+
query_runtime_state.was_authorized_by_scope_items = @object.was_authorized_by_scope_items?
|
179
|
+
@object.edges
|
180
|
+
end
|
181
|
+
|
182
|
+
def nodes
|
183
|
+
# Assume that whatever authorization needed to happen
|
184
|
+
# already happened at the connection level.
|
185
|
+
current_runtime_state = Thread.current[:__graphql_runtime_info]
|
186
|
+
query_runtime_state = current_runtime_state[context.query]
|
187
|
+
query_runtime_state.was_authorized_by_scope_items = @object.was_authorized_by_scope_items?
|
188
|
+
@object.nodes
|
189
|
+
end
|
173
190
|
end
|
174
191
|
end
|
175
192
|
end
|
@@ -12,6 +12,13 @@ module GraphQL
|
|
12
12
|
child_class.node_nullable(true)
|
13
13
|
end
|
14
14
|
|
15
|
+
def node
|
16
|
+
current_runtime_state = Thread.current[:__graphql_runtime_info]
|
17
|
+
query_runtime_state = current_runtime_state[context.query]
|
18
|
+
query_runtime_state.was_authorized_by_scope_items = @object.was_authorized_by_scope_items?
|
19
|
+
@object.node
|
20
|
+
end
|
21
|
+
|
15
22
|
module ClassMethods
|
16
23
|
def inherited(child_class)
|
17
24
|
super
|
data/lib/graphql/version.rb
CHANGED