graphql 1.6.8 → 1.7.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.
- checksums.yaml +4 -4
- data/lib/graphql.rb +5 -0
- data/lib/graphql/analysis/analyze_query.rb +21 -17
- data/lib/graphql/argument.rb +6 -2
- data/lib/graphql/backtrace.rb +50 -0
- data/lib/graphql/backtrace/inspect_result.rb +51 -0
- data/lib/graphql/backtrace/table.rb +120 -0
- data/lib/graphql/backtrace/traced_error.rb +55 -0
- data/lib/graphql/backtrace/tracer.rb +50 -0
- data/lib/graphql/enum_type.rb +1 -10
- data/lib/graphql/execution.rb +1 -2
- data/lib/graphql/execution/execute.rb +98 -89
- data/lib/graphql/execution/flatten.rb +40 -0
- data/lib/graphql/execution/lazy/resolve.rb +7 -7
- data/lib/graphql/execution/multiplex.rb +29 -29
- data/lib/graphql/field.rb +5 -1
- data/lib/graphql/internal_representation/node.rb +16 -0
- data/lib/graphql/invalid_name_error.rb +11 -0
- data/lib/graphql/language/parser.rb +11 -5
- data/lib/graphql/language/parser.y +11 -5
- data/lib/graphql/name_validator.rb +16 -0
- data/lib/graphql/object_type.rb +5 -0
- data/lib/graphql/query.rb +28 -7
- data/lib/graphql/query/context.rb +155 -52
- data/lib/graphql/query/literal_input.rb +36 -9
- data/lib/graphql/query/null_context.rb +7 -1
- data/lib/graphql/query/result.rb +63 -0
- data/lib/graphql/query/serial_execution/field_resolution.rb +3 -4
- data/lib/graphql/query/serial_execution/value_resolution.rb +3 -4
- data/lib/graphql/query/variables.rb +1 -1
- data/lib/graphql/schema.rb +31 -0
- data/lib/graphql/schema/traversal.rb +16 -1
- data/lib/graphql/schema/warden.rb +40 -4
- data/lib/graphql/static_validation/validator.rb +20 -18
- data/lib/graphql/subscriptions.rb +129 -0
- data/lib/graphql/subscriptions/action_cable_subscriptions.rb +122 -0
- data/lib/graphql/subscriptions/event.rb +52 -0
- data/lib/graphql/subscriptions/instrumentation.rb +68 -0
- data/lib/graphql/tracing.rb +80 -0
- data/lib/graphql/tracing/active_support_notifications_tracing.rb +31 -0
- data/lib/graphql/version.rb +1 -1
- data/readme.md +1 -1
- data/spec/graphql/analysis/analyze_query_spec.rb +19 -0
- data/spec/graphql/argument_spec.rb +28 -0
- data/spec/graphql/backtrace_spec.rb +144 -0
- data/spec/graphql/define/assign_argument_spec.rb +12 -0
- data/spec/graphql/enum_type_spec.rb +1 -1
- data/spec/graphql/execution/execute_spec.rb +66 -0
- data/spec/graphql/execution/lazy_spec.rb +4 -3
- data/spec/graphql/language/parser_spec.rb +16 -0
- data/spec/graphql/object_type_spec.rb +14 -0
- data/spec/graphql/query/context_spec.rb +134 -27
- data/spec/graphql/query/result_spec.rb +29 -0
- data/spec/graphql/query/variables_spec.rb +13 -0
- data/spec/graphql/query_spec.rb +22 -0
- data/spec/graphql/schema/build_from_definition_spec.rb +2 -0
- data/spec/graphql/schema/traversal_spec.rb +70 -12
- data/spec/graphql/schema/warden_spec.rb +67 -1
- data/spec/graphql/schema_spec.rb +29 -0
- data/spec/graphql/static_validation/validator_spec.rb +16 -0
- data/spec/graphql/subscriptions_spec.rb +331 -0
- data/spec/graphql/tracing/active_support_notifications_tracing_spec.rb +57 -0
- data/spec/graphql/tracing_spec.rb +47 -0
- data/spec/spec_helper.rb +32 -0
- data/spec/support/star_wars/schema.rb +39 -0
- metadata +27 -4
- data/lib/graphql/execution/field_result.rb +0 -54
- data/lib/graphql/execution/selection_result.rb +0 -90
data/lib/graphql/field.rb
CHANGED
@@ -129,6 +129,7 @@ module GraphQL
|
|
129
129
|
:edge_class,
|
130
130
|
:relay_node_field,
|
131
131
|
:relay_nodes_field,
|
132
|
+
:subscription_scope,
|
132
133
|
argument: GraphQL::Define::AssignArgument
|
133
134
|
|
134
135
|
ensure_defined(
|
@@ -136,7 +137,7 @@ module GraphQL
|
|
136
137
|
:mutation, :arguments, :complexity, :function,
|
137
138
|
:resolve, :resolve=, :lazy_resolve, :lazy_resolve=, :lazy_resolve_proc, :resolve_proc,
|
138
139
|
:type, :type=, :name=, :property=, :hash_key=,
|
139
|
-
:relay_node_field, :relay_nodes_field, :edges?, :edge_class
|
140
|
+
:relay_node_field, :relay_nodes_field, :edges?, :edge_class, :subscription_scope
|
140
141
|
)
|
141
142
|
|
142
143
|
# @return [Boolean] True if this is the Relay find-by-id field
|
@@ -180,6 +181,9 @@ module GraphQL
|
|
180
181
|
|
181
182
|
attr_writer :connection
|
182
183
|
|
184
|
+
# @return [nil, String] Prefix for subscription names from this field
|
185
|
+
attr_accessor :subscription_scope
|
186
|
+
|
183
187
|
# @return [Boolean]
|
184
188
|
def connection?
|
185
189
|
@connection
|
@@ -145,6 +145,22 @@ module GraphQL
|
|
145
145
|
# @return [GraphQL::Query]
|
146
146
|
attr_reader :query
|
147
147
|
|
148
|
+
def subscription_topic
|
149
|
+
@subscription_topic ||= begin
|
150
|
+
scope = if definition.subscription_scope
|
151
|
+
@query.context[definition.subscription_scope]
|
152
|
+
else
|
153
|
+
nil
|
154
|
+
end
|
155
|
+
Subscriptions::Event.serialize(
|
156
|
+
definition_name,
|
157
|
+
@query.arguments_for(self, definition),
|
158
|
+
definition,
|
159
|
+
scope: scope
|
160
|
+
)
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
148
164
|
protected
|
149
165
|
|
150
166
|
attr_writer :owner_type, :parent
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module GraphQL
|
3
|
+
class InvalidNameError < GraphQL::ExecutionError
|
4
|
+
attr_reader :name, :valid_regex
|
5
|
+
def initialize(name, valid_regex)
|
6
|
+
@name = name
|
7
|
+
@valid_regex = valid_regex
|
8
|
+
super("Names must match #{@valid_regex.inspect} but '#{@name}' does not")
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -21,11 +21,17 @@ end
|
|
21
21
|
|
22
22
|
def parse_document
|
23
23
|
@document ||= begin
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
24
|
+
# Break the string into tokens
|
25
|
+
GraphQL::Tracing.trace("lex", {query_string: @query_string}) do
|
26
|
+
@tokens ||= GraphQL.scan(@query_string)
|
27
|
+
end
|
28
|
+
# From the tokens, build an AST
|
29
|
+
GraphQL::Tracing.trace("parse", {query_string: @query_string}) do
|
30
|
+
if @tokens.none?
|
31
|
+
make_node(:Document, definitions: [], filename: @filename)
|
32
|
+
else
|
33
|
+
do_parse
|
34
|
+
end
|
29
35
|
end
|
30
36
|
end
|
31
37
|
end
|
@@ -361,11 +361,17 @@ end
|
|
361
361
|
|
362
362
|
def parse_document
|
363
363
|
@document ||= begin
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
364
|
+
# Break the string into tokens
|
365
|
+
GraphQL::Tracing.trace("lex", {query_string: @query_string}) do
|
366
|
+
@tokens ||= GraphQL.scan(@query_string)
|
367
|
+
end
|
368
|
+
# From the tokens, build an AST
|
369
|
+
GraphQL::Tracing.trace("parse", {query_string: @query_string}) do
|
370
|
+
if @tokens.none?
|
371
|
+
make_node(:Document, definitions: [], filename: @filename)
|
372
|
+
else
|
373
|
+
do_parse
|
374
|
+
end
|
369
375
|
end
|
370
376
|
end
|
371
377
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module GraphQL
|
3
|
+
class NameValidator
|
4
|
+
VALID_NAME_REGEX = /^[_a-zA-Z][_a-zA-Z0-9]*$/
|
5
|
+
|
6
|
+
def self.validate!(name)
|
7
|
+
raise GraphQL::InvalidNameError.new(name, VALID_NAME_REGEX) unless valid?(name)
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def self.valid?(name)
|
13
|
+
name =~ VALID_NAME_REGEX
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
data/lib/graphql/object_type.rb
CHANGED
data/lib/graphql/query.rb
CHANGED
@@ -5,6 +5,7 @@ require "graphql/query/context"
|
|
5
5
|
require "graphql/query/executor"
|
6
6
|
require "graphql/query/literal_input"
|
7
7
|
require "graphql/query/null_context"
|
8
|
+
require "graphql/query/result"
|
8
9
|
require "graphql/query/serial_execution"
|
9
10
|
require "graphql/query/variables"
|
10
11
|
require "graphql/query/input_validation_result"
|
@@ -48,6 +49,12 @@ module GraphQL
|
|
48
49
|
selected_operation.name
|
49
50
|
end
|
50
51
|
|
52
|
+
# @return [String, nil] the triggered event, if this query is a subscription update
|
53
|
+
attr_reader :subscription_topic
|
54
|
+
|
55
|
+
# @return [String, nil]
|
56
|
+
attr_reader :operation_name
|
57
|
+
|
51
58
|
# Prepare query `query_string` on `schema`
|
52
59
|
# @param schema [GraphQL::Schema]
|
53
60
|
# @param query_string [String]
|
@@ -59,10 +66,11 @@ module GraphQL
|
|
59
66
|
# @param max_complexity [Numeric] the maximum field complexity for this query (falls back to schema-level value)
|
60
67
|
# @param except [<#call(schema_member, context)>] If provided, objects will be hidden from the schema when `.call(schema_member, context)` returns truthy
|
61
68
|
# @param only [<#call(schema_member, context)>] If provided, objects will be hidden from the schema when `.call(schema_member, context)` returns false
|
62
|
-
def initialize(schema, query_string = nil, query: nil, document: nil, context: nil, variables: {}, validate: true, operation_name: nil, root_value: nil, max_depth: nil, max_complexity: nil, except: nil, only: nil)
|
69
|
+
def initialize(schema, query_string = nil, query: nil, document: nil, context: nil, variables: {}, validate: true, subscription_topic: nil, operation_name: nil, root_value: nil, max_depth: nil, max_complexity: nil, except: nil, only: nil)
|
63
70
|
@schema = schema
|
64
71
|
@filter = schema.default_filter.merge(except: except, only: only)
|
65
|
-
@context = Context.new(query: self, values: context)
|
72
|
+
@context = Context.new(query: self, object: root_value, values: context)
|
73
|
+
@subscription_topic = subscription_topic
|
66
74
|
@root_value = root_value
|
67
75
|
@fragments = nil
|
68
76
|
@operations = nil
|
@@ -78,6 +86,10 @@ module GraphQL
|
|
78
86
|
@query_string = query_string || query
|
79
87
|
@document = document
|
80
88
|
|
89
|
+
if @query_string && @document
|
90
|
+
raise ArgumentError, "Query should only be provided a query string or a document, not both."
|
91
|
+
end
|
92
|
+
|
81
93
|
# A two-layer cache of type resolution:
|
82
94
|
# { abstract_type => { value => resolved_type } }
|
83
95
|
@resolved_types_cache = Hash.new do |h1, k1|
|
@@ -94,22 +106,25 @@ module GraphQL
|
|
94
106
|
@mutation = false
|
95
107
|
@operation_name = operation_name
|
96
108
|
@prepared_ast = false
|
97
|
-
|
98
109
|
@validation_pipeline = nil
|
99
110
|
@max_depth = max_depth || schema.max_depth
|
100
111
|
@max_complexity = max_complexity || schema.max_complexity
|
101
112
|
|
102
|
-
@
|
113
|
+
@result_values = nil
|
103
114
|
@executed = false
|
104
115
|
end
|
105
116
|
|
117
|
+
def subscription_update?
|
118
|
+
@subscription_topic && subscription?
|
119
|
+
end
|
120
|
+
|
106
121
|
# @api private
|
107
|
-
def
|
122
|
+
def result_values=(result_hash)
|
108
123
|
if @executed
|
109
124
|
raise "Invariant: Can't reassign result"
|
110
125
|
else
|
111
126
|
@executed = true
|
112
|
-
@
|
127
|
+
@result_values = result_hash
|
113
128
|
end
|
114
129
|
end
|
115
130
|
|
@@ -129,7 +144,7 @@ module GraphQL
|
|
129
144
|
Execution::Multiplex.run_queries(@schema, [self])
|
130
145
|
}
|
131
146
|
end
|
132
|
-
@result
|
147
|
+
@result ||= Query::Result.new(query: self, values: @result_values)
|
133
148
|
end
|
134
149
|
|
135
150
|
def static_errors
|
@@ -228,6 +243,10 @@ module GraphQL
|
|
228
243
|
nil
|
229
244
|
end
|
230
245
|
|
246
|
+
def subscription?
|
247
|
+
with_prepared_ast { @subscription }
|
248
|
+
end
|
249
|
+
|
231
250
|
private
|
232
251
|
|
233
252
|
def find_operation(operations, operation_name)
|
@@ -277,6 +296,7 @@ module GraphQL
|
|
277
296
|
# with no operations returns an empty hash
|
278
297
|
@ast_variables = []
|
279
298
|
@mutation = false
|
299
|
+
@subscription = false
|
280
300
|
operation_name_error = nil
|
281
301
|
if @operations.any?
|
282
302
|
@selected_operation = find_operation(@operations, @operation_name)
|
@@ -289,6 +309,7 @@ module GraphQL
|
|
289
309
|
@ast_variables = @selected_operation.variables
|
290
310
|
@mutation = @selected_operation.operation_type == "mutation"
|
291
311
|
@query = @selected_operation.operation_type == "query"
|
312
|
+
@subscription = @selected_operation.operation_type == "subscription"
|
292
313
|
end
|
293
314
|
end
|
294
315
|
|
@@ -1,10 +1,80 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
# test_via: ../execution/execute.rb
|
3
|
+
# test_via: ../execution/lazy.rb
|
2
4
|
module GraphQL
|
3
5
|
class Query
|
4
6
|
# Expose some query-specific info to field resolve functions.
|
5
7
|
# It delegates `[]` to the hash that's passed to `GraphQL::Query#initialize`.
|
6
8
|
class Context
|
9
|
+
module SharedMethods
|
10
|
+
# @return [Object] The target for field resultion
|
11
|
+
attr_accessor :object
|
12
|
+
|
13
|
+
# @return [Hash, Array, String, Integer, Float, Boolean, nil] The resolved value for this field
|
14
|
+
attr_reader :value
|
15
|
+
|
16
|
+
# @return [Boolean] were any fields of this selection skipped?
|
17
|
+
attr_reader :skipped
|
18
|
+
alias :skipped? :skipped
|
19
|
+
|
20
|
+
# @api private
|
21
|
+
attr_writer :skipped
|
22
|
+
|
23
|
+
# Return this value to tell the runtime
|
24
|
+
# to exclude this field from the response altogether
|
25
|
+
def skip
|
26
|
+
GraphQL::Execution::Execute::SKIP
|
27
|
+
end
|
28
|
+
|
29
|
+
# @return [Boolean] True if this selection has been nullified by a null child
|
30
|
+
def invalid_null?
|
31
|
+
@invalid_null
|
32
|
+
end
|
33
|
+
|
34
|
+
# Remove this child from the result value
|
35
|
+
# (used for null propagation and skip)
|
36
|
+
# @api private
|
37
|
+
def delete(child_ctx)
|
38
|
+
@value.delete(child_ctx.key)
|
39
|
+
end
|
40
|
+
|
41
|
+
# Create a child context to use for `key`
|
42
|
+
# @param key [String, Integer] The key in the response (name or index)
|
43
|
+
# @param irep_node [InternalRepresentation::Node] The node being evaluated
|
44
|
+
# @api private
|
45
|
+
def spawn_child(key:, irep_node:, object:)
|
46
|
+
FieldResolutionContext.new(
|
47
|
+
context: @context,
|
48
|
+
parent: self,
|
49
|
+
object: object,
|
50
|
+
key: key,
|
51
|
+
irep_node: irep_node,
|
52
|
+
)
|
53
|
+
end
|
54
|
+
|
55
|
+
# Add error at query-level.
|
56
|
+
# @param error [GraphQL::ExecutionError] an execution error
|
57
|
+
# @return [void]
|
58
|
+
def add_error(error)
|
59
|
+
if !error.is_a?(ExecutionError)
|
60
|
+
raise TypeError, "expected error to be a ExecutionError, but was #{error.class}"
|
61
|
+
end
|
62
|
+
errors << error
|
63
|
+
nil
|
64
|
+
end
|
65
|
+
|
66
|
+
# @example Print the GraphQL backtrace during field resolution
|
67
|
+
# puts ctx.backtrace
|
68
|
+
#
|
69
|
+
# @return [GraphQL::Backtrace] The backtrace for this point in query execution
|
70
|
+
def backtrace
|
71
|
+
GraphQL::Backtrace.new(self)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
include SharedMethods
|
7
76
|
extend GraphQL::Delegate
|
77
|
+
|
8
78
|
attr_reader :execution_strategy
|
9
79
|
# `strategy` is required by GraphQL::Batch
|
10
80
|
alias_method :strategy, :execution_strategy
|
@@ -17,7 +87,9 @@ module GraphQL
|
|
17
87
|
end
|
18
88
|
|
19
89
|
# @return [GraphQL::InternalRepresentation::Node] The internal representation for this query node
|
20
|
-
|
90
|
+
def irep_node
|
91
|
+
@irep_node ||= query.irep_selection
|
92
|
+
end
|
21
93
|
|
22
94
|
# @return [GraphQL::Language::Nodes::Field] The AST node for the currently-executing field
|
23
95
|
def ast_node
|
@@ -39,17 +111,23 @@ module GraphQL
|
|
39
111
|
# Make a new context which delegates key lookup to `values`
|
40
112
|
# @param query [GraphQL::Query] the query who owns this context
|
41
113
|
# @param values [Hash] A hash of arbitrary values which will be accessible at query-time
|
42
|
-
def initialize(query:, values:)
|
114
|
+
def initialize(query:, values: , object:)
|
43
115
|
@query = query
|
44
116
|
@schema = query.schema
|
45
117
|
@provided_values = values || {}
|
118
|
+
@object = object
|
46
119
|
# Namespaced storage, where user-provided values are in `nil` namespace:
|
47
120
|
@storage = Hash.new { |h, k| h[k] = {} }
|
48
121
|
@storage[nil] = @provided_values
|
49
122
|
@errors = []
|
50
123
|
@path = []
|
124
|
+
@value = nil
|
125
|
+
@context = self # for SharedMethods
|
51
126
|
end
|
52
127
|
|
128
|
+
# @api private
|
129
|
+
attr_writer :value
|
130
|
+
|
53
131
|
def_delegators :@provided_values, :[], :[]=, :to_h, :key?, :fetch
|
54
132
|
|
55
133
|
# @!method [](key)
|
@@ -58,6 +136,7 @@ module GraphQL
|
|
58
136
|
# @!method []=(key, value)
|
59
137
|
# Reassign `key` to the hash passed to {Schema#execute} as `context:`
|
60
138
|
|
139
|
+
|
61
140
|
# @return [GraphQL::Schema::Warden]
|
62
141
|
def warden
|
63
142
|
@warden ||= @query.warden
|
@@ -70,46 +149,32 @@ module GraphQL
|
|
70
149
|
@storage[ns]
|
71
150
|
end
|
72
151
|
|
73
|
-
def
|
74
|
-
|
75
|
-
context: self,
|
76
|
-
parent: self,
|
77
|
-
key: key,
|
78
|
-
selection: selection,
|
79
|
-
parent_type: parent_type,
|
80
|
-
field: field,
|
81
|
-
)
|
152
|
+
def inspect
|
153
|
+
"#<Query::Context ...>"
|
82
154
|
end
|
83
155
|
|
84
|
-
#
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
end
|
89
|
-
|
90
|
-
# Add error at query-level.
|
91
|
-
# @param error [GraphQL::ExecutionError] an execution error
|
92
|
-
# @return [void]
|
93
|
-
def add_error(error)
|
94
|
-
if !error.is_a?(ExecutionError)
|
95
|
-
raise TypeError, "expected error to be a ExecutionError, but was #{error.class}"
|
96
|
-
end
|
97
|
-
errors << error
|
98
|
-
nil
|
156
|
+
# @api private
|
157
|
+
def received_null_child
|
158
|
+
@invalid_null = true
|
159
|
+
@value = nil
|
99
160
|
end
|
100
161
|
|
101
162
|
class FieldResolutionContext
|
163
|
+
include SharedMethods
|
102
164
|
extend GraphQL::Delegate
|
103
165
|
|
104
|
-
attr_reader :
|
166
|
+
attr_reader :irep_node, :field, :parent_type, :query, :schema, :parent, :key, :type
|
167
|
+
alias :selection :irep_node
|
105
168
|
|
106
|
-
def initialize(context:, key:,
|
169
|
+
def initialize(context:, key:, irep_node:, parent:, object:)
|
107
170
|
@context = context
|
108
171
|
@key = key
|
109
172
|
@parent = parent
|
110
|
-
@
|
111
|
-
@
|
112
|
-
@
|
173
|
+
@object = object
|
174
|
+
@irep_node = irep_node
|
175
|
+
@field = irep_node.definition
|
176
|
+
@parent_type = irep_node.owner_type
|
177
|
+
@type = field.type
|
113
178
|
# This is needed constantly, so set it ahead of time:
|
114
179
|
@query = context.query
|
115
180
|
@schema = context.schema
|
@@ -122,41 +187,79 @@ module GraphQL
|
|
122
187
|
def_delegators :@context,
|
123
188
|
:[], :[]=, :key?, :fetch, :to_h, :namespace,
|
124
189
|
:spawn, :schema, :warden, :errors,
|
125
|
-
:execution_strategy, :strategy
|
190
|
+
:execution_strategy, :strategy
|
126
191
|
|
127
192
|
# @return [GraphQL::Language::Nodes::Field] The AST node for the currently-executing field
|
128
193
|
def ast_node
|
129
|
-
@
|
130
|
-
end
|
131
|
-
|
132
|
-
# @return [GraphQL::InternalRepresentation::Node]
|
133
|
-
def irep_node
|
134
|
-
@selection
|
194
|
+
@irep_node.ast_node
|
135
195
|
end
|
136
196
|
|
137
197
|
# Add error to current field resolution.
|
138
198
|
# @param error [GraphQL::ExecutionError] an execution error
|
139
199
|
# @return [void]
|
140
200
|
def add_error(error)
|
141
|
-
|
142
|
-
raise TypeError, "expected error to be a ExecutionError, but was #{error.class}"
|
143
|
-
end
|
144
|
-
|
201
|
+
super
|
145
202
|
error.ast_node ||= irep_node.ast_node
|
146
203
|
error.path ||= path
|
147
|
-
errors << error
|
148
204
|
nil
|
149
205
|
end
|
150
206
|
|
151
|
-
def
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
207
|
+
def inspect
|
208
|
+
"#<GraphQL Context @ #{irep_node.owner_type.name}.#{field.name}>"
|
209
|
+
end
|
210
|
+
|
211
|
+
# Set a new value for this field in the response.
|
212
|
+
# It may be updated after resolving a {Lazy}.
|
213
|
+
# If it is {Execute::PROPAGATE_NULL}, tell the owner to propagate null.
|
214
|
+
# If it's {Execute::Execution::SKIP}, remove this field result from its parent
|
215
|
+
# @param new_value [Any] The GraphQL-ready value
|
216
|
+
# @api private
|
217
|
+
def value=(new_value)
|
218
|
+
case new_value
|
219
|
+
when GraphQL::Execution::Execute::PROPAGATE_NULL, nil
|
220
|
+
@invalid_null = true
|
221
|
+
@value = nil
|
222
|
+
if @type.kind.non_null?
|
223
|
+
@parent.received_null_child
|
224
|
+
end
|
225
|
+
when GraphQL::Execution::Execute::SKIP
|
226
|
+
@parent.skipped = true
|
227
|
+
@parent.delete(self)
|
228
|
+
else
|
229
|
+
@value = new_value
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
protected
|
234
|
+
|
235
|
+
def received_null_child
|
236
|
+
case @value
|
237
|
+
when Hash
|
238
|
+
self.value = GraphQL::Execution::Execute::PROPAGATE_NULL
|
239
|
+
when Array
|
240
|
+
if list_of_non_null_items?(@type)
|
241
|
+
self.value = GraphQL::Execution::Execute::PROPAGATE_NULL
|
242
|
+
end
|
243
|
+
when nil
|
244
|
+
# TODO This is a hack
|
245
|
+
# It was already nulled out but it's getting reassigned
|
246
|
+
else
|
247
|
+
raise "Unexpected value for received_null_child (#{self.value.class}): #{value}"
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
private
|
252
|
+
|
253
|
+
def list_of_non_null_items?(type)
|
254
|
+
case type
|
255
|
+
when GraphQL::NonNullType
|
256
|
+
# Unwrap [T]!
|
257
|
+
list_of_non_null_items?(type.of_type)
|
258
|
+
when GraphQL::ListType
|
259
|
+
type.of_type.is_a?(GraphQL::NonNullType)
|
260
|
+
else
|
261
|
+
raise "Unexpected list_of_non_null_items check: #{type}"
|
262
|
+
end
|
160
263
|
end
|
161
264
|
end
|
162
265
|
end
|