graphql 2.5.4 → 2.5.6
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/analysis/query_complexity.rb +2 -2
- data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +11 -1
- data/lib/graphql/execution/interpreter/runtime.rb +123 -34
- data/lib/graphql/execution/interpreter.rb +1 -1
- data/lib/graphql/query/partial.rb +156 -0
- data/lib/graphql/query.rb +55 -43
- data/lib/graphql/schema/input_object.rb +24 -21
- data/lib/graphql/schema/visibility.rb +1 -1
- data/lib/graphql/schema.rb +1 -1
- data/lib/graphql/tracing/notifications_trace.rb +3 -1
- data/lib/graphql/version.rb +1 -1
- metadata +18 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f669e7593a3d951ee74480991e79d6a10fcd4b069746cd6244897b32fb6373fa
|
4
|
+
data.tar.gz: decb90c238b320f288b9a672b068e543745b9f46f82249fe93771230f8096257
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f1afa8434158b861073704a39b1ce84ee76c2bd636669c734ab43af66cf15c396ddaadc2f7c1dcf1fa61c030a0bddc0fc362f6db1bc6ad144efcaa61142565fd
|
7
|
+
data.tar.gz: ef88e624f49aafc803c6acfef8e44e1336f3494ce83bdf5b074825450cc6bc10eafd5738b012e177ceb5581c0425b9e93466e84d50bcec28b00169dbbc82c9b5
|
@@ -28,14 +28,14 @@ module GraphQL
|
|
28
28
|
end
|
29
29
|
when nil
|
30
30
|
subject.logger.warn <<~GRAPHQL
|
31
|
-
GraphQL-Ruby's complexity cost system is getting some "breaking fixes" in a future version. See the migration notes at https://graphql-ruby.org/api-
|
31
|
+
GraphQL-Ruby's complexity cost system is getting some "breaking fixes" in a future version. See the migration notes at https://graphql-ruby.org/api-doc/#{GraphQL::VERSION}/GraphQL/Schema.html#complexity_cost_calculation_mode_for-class_method
|
32
32
|
|
33
33
|
To opt into the future behavior, configure your schema (#{subject.schema.name ? subject.schema.name : subject.schema.ancestors}) with:
|
34
34
|
|
35
35
|
complexity_cost_calculation_mode(:future) # or `:legacy`, `:compare`
|
36
36
|
|
37
37
|
GRAPHQL
|
38
|
-
max_possible_complexity
|
38
|
+
max_possible_complexity(mode: :legacy)
|
39
39
|
else
|
40
40
|
raise ArgumentError, "Expected `:future`, `:legacy`, `:compare`, or `nil` from `#{query.schema}.complexity_cost_calculation_mode_for` but got: #{query.schema.complexity_cost_calculation_mode.inspect}"
|
41
41
|
end
|
@@ -21,15 +21,25 @@ module GraphQL
|
|
21
21
|
@graphql_metadata = nil
|
22
22
|
@graphql_selections = selections
|
23
23
|
@graphql_is_eager = is_eager
|
24
|
+
@base_path = nil
|
24
25
|
end
|
25
26
|
|
27
|
+
# TODO test full path in Partial
|
28
|
+
attr_writer :base_path
|
29
|
+
|
26
30
|
def path
|
27
31
|
@path ||= build_path([])
|
28
32
|
end
|
29
33
|
|
30
34
|
def build_path(path_array)
|
31
35
|
graphql_result_name && path_array.unshift(graphql_result_name)
|
32
|
-
|
36
|
+
if @graphql_parent
|
37
|
+
@graphql_parent.build_path(path_array)
|
38
|
+
elsif @base_path
|
39
|
+
@base_path + path_array
|
40
|
+
else
|
41
|
+
path_array
|
42
|
+
end
|
33
43
|
end
|
34
44
|
|
35
45
|
attr_accessor :graphql_dead
|
@@ -57,53 +57,142 @@ module GraphQL
|
|
57
57
|
end
|
58
58
|
|
59
59
|
def final_result
|
60
|
-
@response
|
60
|
+
@response.respond_to?(:graphql_result_data) ? @response.graphql_result_data : @response
|
61
61
|
end
|
62
62
|
|
63
63
|
def inspect
|
64
64
|
"#<#{self.class.name} response=#{@response.inspect}>"
|
65
65
|
end
|
66
66
|
|
67
|
-
# This _begins_ the execution. Some deferred work
|
68
|
-
# might be stored up in lazies.
|
69
67
|
# @return [void]
|
70
68
|
def run_eager
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
69
|
+
root_type = query.root_type
|
70
|
+
case query
|
71
|
+
when GraphQL::Query
|
72
|
+
ast_node = query.selected_operation
|
73
|
+
selections = ast_node.selections
|
74
|
+
object = query.root_value
|
75
|
+
is_eager = ast_node.operation_type == "mutation"
|
76
|
+
base_path = nil
|
77
|
+
when GraphQL::Query::Partial
|
78
|
+
ast_node = query.ast_nodes.first
|
79
|
+
selections = query.ast_nodes.map(&:selections).inject(&:+)
|
80
|
+
object = query.object
|
81
|
+
is_eager = false
|
82
|
+
base_path = query.path
|
84
83
|
else
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
84
|
+
raise ArgumentError, "Unexpected Runnable, can't execute: #{query.class} (#{query.inspect})"
|
85
|
+
end
|
86
|
+
object = schema.sync_lazy(object) # TODO test query partial with lazy root object
|
87
|
+
runtime_state = get_current_runtime_state
|
88
|
+
case root_type.kind.name
|
89
|
+
when "OBJECT"
|
90
|
+
object_proxy = root_type.wrap(object, context)
|
91
|
+
object_proxy = schema.sync_lazy(object_proxy)
|
92
|
+
if object_proxy.nil?
|
93
|
+
@response = nil
|
94
|
+
else
|
95
|
+
@response = GraphQLResultHash.new(nil, root_type, object_proxy, nil, false, selections, is_eager, ast_node, nil, nil)
|
96
|
+
@response.base_path = base_path
|
97
|
+
runtime_state.current_result = @response
|
98
|
+
call_method_on_directives(:resolve, object, ast_node.directives) do
|
99
|
+
each_gathered_selections(@response) do |selections, is_selection_array, ordered_result_keys|
|
100
|
+
@response.ordered_result_keys ||= ordered_result_keys
|
101
|
+
if is_selection_array
|
102
|
+
selection_response = GraphQLResultHash.new(nil, root_type, object_proxy, nil, false, selections, is_eager, ast_node, nil, nil)
|
103
|
+
selection_response.ordered_result_keys = ordered_result_keys
|
104
|
+
final_response = @response
|
105
|
+
else
|
106
|
+
selection_response = @response
|
107
|
+
final_response = nil
|
108
|
+
end
|
96
109
|
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
110
|
+
@dataloader.append_job {
|
111
|
+
evaluate_selections(
|
112
|
+
selections,
|
113
|
+
selection_response,
|
114
|
+
final_response,
|
115
|
+
nil,
|
116
|
+
)
|
117
|
+
}
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
when "LIST"
|
122
|
+
inner_type = root_type.unwrap
|
123
|
+
case inner_type.kind.name
|
124
|
+
when "SCALAR", "ENUM"
|
125
|
+
result_name = ast_node.alias || ast_node.name
|
126
|
+
owner_type = query.field_definition.owner
|
127
|
+
selection_result = GraphQLResultHash.new(nil, owner_type, nil, nil, false, EmptyObjects::EMPTY_ARRAY, false, ast_node, nil, nil)
|
128
|
+
selection_result.base_path = base_path
|
129
|
+
selection_result.ordered_result_keys = [result_name]
|
130
|
+
runtime_state = get_current_runtime_state
|
131
|
+
runtime_state.current_result = selection_result
|
132
|
+
runtime_state.current_result_name = result_name
|
133
|
+
field_defn = query.field_definition
|
134
|
+
continue_value = continue_value(object, field_defn, false, ast_node, result_name, selection_result)
|
135
|
+
if HALT != continue_value
|
136
|
+
continue_field(continue_value, owner_type, field_defn, root_type, ast_node, nil, false, nil, nil, result_name, selection_result, false, runtime_state) # rubocop:disable Metrics/ParameterLists
|
137
|
+
end
|
138
|
+
@response = selection_result[result_name]
|
139
|
+
else
|
140
|
+
@response = GraphQLResultArray.new(nil, root_type, nil, nil, false, selections, false, ast_node, nil, nil)
|
141
|
+
@response.base_path = base_path
|
142
|
+
idx = nil
|
143
|
+
object.each do |inner_value|
|
144
|
+
idx ||= 0
|
145
|
+
this_idx = idx
|
146
|
+
idx += 1
|
147
|
+
@dataloader.append_job do
|
148
|
+
runtime_state.current_result_name = this_idx
|
149
|
+
runtime_state.current_result = @response
|
150
|
+
continue_field(
|
151
|
+
inner_value, root_type, nil, inner_type, nil, @response.graphql_selections, false, object_proxy,
|
152
|
+
nil, this_idx, @response, false, runtime_state
|
103
153
|
)
|
104
|
-
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
when "SCALAR", "ENUM"
|
158
|
+
result_name = ast_node.alias || ast_node.name
|
159
|
+
owner_type = query.field_definition.owner
|
160
|
+
selection_result = GraphQLResultHash.new(nil, query.parent_type, nil, nil, false, EmptyObjects::EMPTY_ARRAY, false, ast_node, nil, nil)
|
161
|
+
selection_result.ordered_result_keys = [result_name]
|
162
|
+
selection_result.base_path = base_path
|
163
|
+
runtime_state = get_current_runtime_state
|
164
|
+
runtime_state.current_result = selection_result
|
165
|
+
runtime_state.current_result_name = result_name
|
166
|
+
field_defn = query.field_definition
|
167
|
+
continue_value = continue_value(object, field_defn, false, ast_node, result_name, selection_result)
|
168
|
+
if HALT != continue_value
|
169
|
+
continue_field(continue_value, owner_type, field_defn, query.root_type, ast_node, nil, false, nil, nil, result_name, selection_result, false, runtime_state) # rubocop:disable Metrics/ParameterLists
|
170
|
+
end
|
171
|
+
@response = selection_result[result_name]
|
172
|
+
when "UNION", "INTERFACE"
|
173
|
+
resolved_type, _resolved_obj = resolve_type(root_type, object)
|
174
|
+
resolved_type = schema.sync_lazy(resolved_type)
|
175
|
+
object_proxy = resolved_type.wrap(object, context)
|
176
|
+
object_proxy = schema.sync_lazy(object_proxy)
|
177
|
+
@response = GraphQLResultHash.new(nil, resolved_type, object_proxy, nil, false, selections, false, query.ast_nodes.first, nil, nil)
|
178
|
+
@response.base_path = base_path
|
179
|
+
each_gathered_selections(@response) do |selections, is_selection_array, ordered_result_keys|
|
180
|
+
@response.ordered_result_keys ||= ordered_result_keys
|
181
|
+
if is_selection_array == true
|
182
|
+
raise "This isn't supported yet"
|
105
183
|
end
|
184
|
+
|
185
|
+
@dataloader.append_job {
|
186
|
+
evaluate_selections(
|
187
|
+
selections,
|
188
|
+
@response,
|
189
|
+
nil,
|
190
|
+
runtime_state,
|
191
|
+
)
|
192
|
+
}
|
106
193
|
end
|
194
|
+
else
|
195
|
+
raise "Invariant: unsupported type kind for partial execution: #{root_type.kind.inspect} (#{root_type})"
|
107
196
|
end
|
108
197
|
nil
|
109
198
|
end
|
@@ -26,7 +26,7 @@ module GraphQL
|
|
26
26
|
query = case opts
|
27
27
|
when Hash
|
28
28
|
schema.query_class.new(schema, nil, **opts)
|
29
|
-
when GraphQL::Query
|
29
|
+
when GraphQL::Query, GraphQL::Query::Partial
|
30
30
|
opts
|
31
31
|
else
|
32
32
|
raise "Expected Hash or GraphQL::Query, not #{opts.class} (#{opts.inspect})"
|
@@ -0,0 +1,156 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module GraphQL
|
3
|
+
class Query
|
4
|
+
# This class is _like_ a {GraphQL::Query}, except it can run on an arbitrary path within a query string.
|
5
|
+
#
|
6
|
+
# It depends on a "parent" {Query}.
|
7
|
+
#
|
8
|
+
# During execution, it calls query-related tracing hooks but passes itself as `query:`.
|
9
|
+
#
|
10
|
+
# The {Partial} will use your {Schema.resolve_type} hook to find the right GraphQL type to use for
|
11
|
+
# `object` in some cases.
|
12
|
+
#
|
13
|
+
# @see Query#run_partials Run via {Query#run_partials}
|
14
|
+
class Partial
|
15
|
+
include Query::Runnable
|
16
|
+
|
17
|
+
# @param path [Array<String, Integer>] A path in `query.query_string` to start executing from
|
18
|
+
# @param object [Object] A starting object for execution
|
19
|
+
# @param query [GraphQL::Query] A full query instance that this partial is based on. Caches are shared.
|
20
|
+
# @param context [Hash] Extra context values to merge into `query.context`, if provided
|
21
|
+
def initialize(path:, object:, query:, context: nil)
|
22
|
+
@path = path
|
23
|
+
@object = object
|
24
|
+
@query = query
|
25
|
+
@schema = query.schema
|
26
|
+
context_vals = @query.context.to_h
|
27
|
+
if context
|
28
|
+
context_vals = context_vals.merge(context)
|
29
|
+
end
|
30
|
+
@context = GraphQL::Query::Context.new(query: self, schema: @query.schema, values: context_vals)
|
31
|
+
@multiplex = nil
|
32
|
+
@result_values = nil
|
33
|
+
@result = nil
|
34
|
+
selections = [@query.selected_operation]
|
35
|
+
type = @query.root_type
|
36
|
+
parent_type = nil
|
37
|
+
field_defn = nil
|
38
|
+
@path.each do |name_in_doc|
|
39
|
+
if name_in_doc.is_a?(Integer)
|
40
|
+
if type.list?
|
41
|
+
type = type.unwrap
|
42
|
+
next
|
43
|
+
else
|
44
|
+
raise ArgumentError, "Received path with index `#{name_in_doc}`, but type wasn't a list. Type: #{type.to_type_signature}, path: #{@path}"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
next_selections = []
|
49
|
+
selections.each do |selection|
|
50
|
+
selections_to_check = []
|
51
|
+
selections_to_check.concat(selection.selections)
|
52
|
+
while (sel = selections_to_check.shift)
|
53
|
+
case sel
|
54
|
+
when GraphQL::Language::Nodes::InlineFragment
|
55
|
+
selections_to_check.concat(sel.selections)
|
56
|
+
when GraphQL::Language::Nodes::FragmentSpread
|
57
|
+
fragment = @query.fragments[sel.name]
|
58
|
+
selections_to_check.concat(fragment.selections)
|
59
|
+
when GraphQL::Language::Nodes::Field
|
60
|
+
if sel.alias == name_in_doc || sel.name == name_in_doc
|
61
|
+
next_selections << sel
|
62
|
+
end
|
63
|
+
else
|
64
|
+
raise "Unexpected selection in partial path: #{sel.class}, #{sel.inspect}"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
if next_selections.empty?
|
70
|
+
raise ArgumentError, "Path `#{@path.inspect}` is not present in this query. `#{name_in_doc.inspect}` was not found. Try a different path or rewrite the query to include it."
|
71
|
+
end
|
72
|
+
field_name = next_selections.first.name
|
73
|
+
field_defn = @schema.get_field(type, field_name, @query.context) || raise("Invariant: no field called #{field_name} on #{type.graphql_name}")
|
74
|
+
parent_type = type
|
75
|
+
type = field_defn.type
|
76
|
+
if type.non_null?
|
77
|
+
type = type.of_type
|
78
|
+
end
|
79
|
+
selections = next_selections
|
80
|
+
end
|
81
|
+
@parent_type = parent_type
|
82
|
+
@ast_nodes = selections
|
83
|
+
@root_type = type
|
84
|
+
@field_definition = field_defn
|
85
|
+
@leaf = @root_type.unwrap.kind.leaf?
|
86
|
+
end
|
87
|
+
|
88
|
+
def leaf?
|
89
|
+
@leaf
|
90
|
+
end
|
91
|
+
|
92
|
+
attr_reader :context, :query, :ast_nodes, :root_type, :object, :field_definition, :path, :parent_type, :schema
|
93
|
+
|
94
|
+
attr_accessor :multiplex, :result_values
|
95
|
+
|
96
|
+
class Result < GraphQL::Query::Result
|
97
|
+
def path
|
98
|
+
@query.path
|
99
|
+
end
|
100
|
+
|
101
|
+
# @return [GraphQL::Query::Partial]
|
102
|
+
def partial
|
103
|
+
@query
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def result
|
108
|
+
@result ||= Result.new(query: self, values: result_values)
|
109
|
+
end
|
110
|
+
|
111
|
+
def current_trace
|
112
|
+
@query.current_trace
|
113
|
+
end
|
114
|
+
|
115
|
+
def types
|
116
|
+
@query.types
|
117
|
+
end
|
118
|
+
|
119
|
+
def resolve_type(...)
|
120
|
+
@query.resolve_type(...)
|
121
|
+
end
|
122
|
+
|
123
|
+
def variables
|
124
|
+
@query.variables
|
125
|
+
end
|
126
|
+
|
127
|
+
def fragments
|
128
|
+
@query.fragments
|
129
|
+
end
|
130
|
+
|
131
|
+
def valid?
|
132
|
+
@query.valid?
|
133
|
+
end
|
134
|
+
|
135
|
+
def analyzers
|
136
|
+
EmptyObjects::EMPTY_ARRAY
|
137
|
+
end
|
138
|
+
|
139
|
+
def analysis_errors=(_ignored)
|
140
|
+
# pass
|
141
|
+
end
|
142
|
+
|
143
|
+
def subscription?
|
144
|
+
@query.subscription?
|
145
|
+
end
|
146
|
+
|
147
|
+
def selected_operation
|
148
|
+
ast_nodes.first
|
149
|
+
end
|
150
|
+
|
151
|
+
def static_errors
|
152
|
+
@query.static_errors
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
data/lib/graphql/query.rb
CHANGED
@@ -10,12 +10,47 @@ module GraphQL
|
|
10
10
|
autoload :Context, "graphql/query/context"
|
11
11
|
autoload :Fingerprint, "graphql/query/fingerprint"
|
12
12
|
autoload :NullContext, "graphql/query/null_context"
|
13
|
+
autoload :Partial, "graphql/query/partial"
|
13
14
|
autoload :Result, "graphql/query/result"
|
14
15
|
autoload :Variables, "graphql/query/variables"
|
15
16
|
autoload :InputValidationResult, "graphql/query/input_validation_result"
|
16
17
|
autoload :VariableValidationError, "graphql/query/variable_validation_error"
|
17
18
|
autoload :ValidationPipeline, "graphql/query/validation_pipeline"
|
18
19
|
|
20
|
+
# Code shared with {Partial}
|
21
|
+
module Runnable
|
22
|
+
def after_lazy(value, &block)
|
23
|
+
if !defined?(@runtime_instance)
|
24
|
+
@runtime_instance = context.namespace(:interpreter_runtime)[:runtime]
|
25
|
+
end
|
26
|
+
|
27
|
+
if @runtime_instance
|
28
|
+
@runtime_instance.minimal_after_lazy(value, &block)
|
29
|
+
else
|
30
|
+
@schema.after_lazy(value, &block)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Node-level cache for calculating arguments. Used during execution and query analysis.
|
35
|
+
# @param ast_node [GraphQL::Language::Nodes::AbstractNode]
|
36
|
+
# @param definition [GraphQL::Schema::Field]
|
37
|
+
# @param parent_object [GraphQL::Schema::Object]
|
38
|
+
# @return [Hash{Symbol => Object}]
|
39
|
+
def arguments_for(ast_node, definition, parent_object: nil)
|
40
|
+
arguments_cache.fetch(ast_node, definition, parent_object)
|
41
|
+
end
|
42
|
+
|
43
|
+
def arguments_cache
|
44
|
+
@arguments_cache ||= Execution::Interpreter::ArgumentsCache.new(self)
|
45
|
+
end
|
46
|
+
|
47
|
+
# @api private
|
48
|
+
def handle_or_reraise(err)
|
49
|
+
@schema.handle_or_reraise(context, err)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
include Runnable
|
19
54
|
class OperationNameMissingError < GraphQL::ExecutionError
|
20
55
|
def initialize(name)
|
21
56
|
msg = if name.nil?
|
@@ -198,19 +233,10 @@ module GraphQL
|
|
198
233
|
# @return [GraphQL::Execution::Lookahead]
|
199
234
|
def lookahead
|
200
235
|
@lookahead ||= begin
|
201
|
-
|
202
|
-
if ast_node.nil?
|
236
|
+
if selected_operation.nil?
|
203
237
|
GraphQL::Execution::Lookahead::NULL_LOOKAHEAD
|
204
238
|
else
|
205
|
-
root_type
|
206
|
-
when nil, "query"
|
207
|
-
types.query_root # rubocop:disable Development/ContextIsPassedCop
|
208
|
-
when "mutation"
|
209
|
-
types.mutation_root # rubocop:disable Development/ContextIsPassedCop
|
210
|
-
when "subscription"
|
211
|
-
types.subscription_root # rubocop:disable Development/ContextIsPassedCop
|
212
|
-
end
|
213
|
-
GraphQL::Execution::Lookahead.new(query: self, root_type: root_type, ast_nodes: [ast_node])
|
239
|
+
GraphQL::Execution::Lookahead.new(query: self, root_type: root_type, ast_nodes: [selected_operation])
|
214
240
|
end
|
215
241
|
end
|
216
242
|
end
|
@@ -236,6 +262,18 @@ module GraphQL
|
|
236
262
|
with_prepared_ast { @operations }
|
237
263
|
end
|
238
264
|
|
265
|
+
# Run subtree partials of this query and return their results.
|
266
|
+
# Each partial is identified with a `path:` and `object:`
|
267
|
+
# where the path references a field in the AST and the object will be treated
|
268
|
+
# as the return value from that field. Subfields of the field named by `path`
|
269
|
+
# will be executed with `object` as the starting point
|
270
|
+
# @param partials_hashes [Array<Hash{Symbol => Object}>] Hashes with `path:` and `object:` keys
|
271
|
+
# @return [Array<GraphQL::Query::Result>]
|
272
|
+
def run_partials(partials_hashes)
|
273
|
+
partials = partials_hashes.map { |partial_options| Partial.new(query: self, **partial_options) }
|
274
|
+
Execution::Interpreter.run_all(@schema, partials, context: @context)
|
275
|
+
end
|
276
|
+
|
239
277
|
# Get the result for this query, executing it once
|
240
278
|
# @return [GraphQL::Query::Result] A Hash-like GraphQL response, with `"data"` and/or `"errors"` keys
|
241
279
|
def result
|
@@ -278,19 +316,6 @@ module GraphQL
|
|
278
316
|
end
|
279
317
|
end
|
280
318
|
|
281
|
-
# Node-level cache for calculating arguments. Used during execution and query analysis.
|
282
|
-
# @param ast_node [GraphQL::Language::Nodes::AbstractNode]
|
283
|
-
# @param definition [GraphQL::Schema::Field]
|
284
|
-
# @param parent_object [GraphQL::Schema::Object]
|
285
|
-
# @return [Hash{Symbol => Object}]
|
286
|
-
def arguments_for(ast_node, definition, parent_object: nil)
|
287
|
-
arguments_cache.fetch(ast_node, definition, parent_object)
|
288
|
-
end
|
289
|
-
|
290
|
-
def arguments_cache
|
291
|
-
@arguments_cache ||= Execution::Interpreter::ArgumentsCache.new(self)
|
292
|
-
end
|
293
|
-
|
294
319
|
# A version of the given query string, with:
|
295
320
|
# - Variables inlined to the query
|
296
321
|
# - Strings replaced with `<REDACTED>`
|
@@ -357,17 +382,21 @@ module GraphQL
|
|
357
382
|
|
358
383
|
def root_type_for_operation(op_type)
|
359
384
|
case op_type
|
360
|
-
when "query"
|
385
|
+
when "query", nil
|
361
386
|
types.query_root # rubocop:disable Development/ContextIsPassedCop
|
362
387
|
when "mutation"
|
363
388
|
types.mutation_root # rubocop:disable Development/ContextIsPassedCop
|
364
389
|
when "subscription"
|
365
390
|
types.subscription_root # rubocop:disable Development/ContextIsPassedCop
|
366
391
|
else
|
367
|
-
raise ArgumentError, "unexpected root type name: #{op_type.inspect}; expected 'query', 'mutation', or 'subscription'"
|
392
|
+
raise ArgumentError, "unexpected root type name: #{op_type.inspect}; expected nil, 'query', 'mutation', or 'subscription'"
|
368
393
|
end
|
369
394
|
end
|
370
395
|
|
396
|
+
def root_type
|
397
|
+
root_type_for_operation(selected_operation.operation_type)
|
398
|
+
end
|
399
|
+
|
371
400
|
def types
|
372
401
|
@visibility_profile || warden.visibility_profile
|
373
402
|
end
|
@@ -400,23 +429,6 @@ module GraphQL
|
|
400
429
|
with_prepared_ast { @subscription }
|
401
430
|
end
|
402
431
|
|
403
|
-
# @api private
|
404
|
-
def handle_or_reraise(err)
|
405
|
-
schema.handle_or_reraise(context, err)
|
406
|
-
end
|
407
|
-
|
408
|
-
def after_lazy(value, &block)
|
409
|
-
if !defined?(@runtime_instance)
|
410
|
-
@runtime_instance = context.namespace(:interpreter_runtime)[:runtime]
|
411
|
-
end
|
412
|
-
|
413
|
-
if @runtime_instance
|
414
|
-
@runtime_instance.minimal_after_lazy(value, &block)
|
415
|
-
else
|
416
|
-
@schema.after_lazy(value, &block)
|
417
|
-
end
|
418
|
-
end
|
419
|
-
|
420
432
|
attr_reader :logger
|
421
433
|
|
422
434
|
private
|
@@ -176,30 +176,33 @@ module GraphQL
|
|
176
176
|
return GraphQL::Query::InputValidationResult.from_problem(INVALID_OBJECT_MESSAGE % { object: JSON.generate(input, quirks_mode: true) })
|
177
177
|
end
|
178
178
|
|
179
|
-
# Inject missing required arguments
|
180
|
-
missing_required_inputs = ctx.types.arguments(self).reduce({}) do |m, (argument)|
|
181
|
-
if !input.key?(argument.graphql_name) && argument.type.non_null? && !argument.default_value? && types.argument(self, argument.graphql_name)
|
182
|
-
m[argument.graphql_name] = nil
|
183
|
-
end
|
184
|
-
|
185
|
-
m
|
186
|
-
end
|
187
179
|
|
188
180
|
result = nil
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
181
|
+
|
182
|
+
|
183
|
+
input.each do |argument_name, value|
|
184
|
+
argument = types.argument(self, argument_name)
|
185
|
+
# Items in the input that are unexpected
|
186
|
+
if argument.nil?
|
187
|
+
result ||= Query::InputValidationResult.new
|
188
|
+
result.add_problem("Field is not defined on #{self.graphql_name}", [argument_name])
|
189
|
+
else
|
190
|
+
# Items in the input that are expected, but have invalid values
|
191
|
+
argument_result = argument.type.validate_input(value, ctx)
|
192
|
+
if !argument_result.valid?
|
199
193
|
result ||= Query::InputValidationResult.new
|
200
|
-
|
201
|
-
|
202
|
-
|
194
|
+
result.merge_result!(argument_name, argument_result)
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
# Check for missing non-null arguments
|
200
|
+
ctx.types.arguments(self).each do |argument|
|
201
|
+
if !input.key?(argument.graphql_name) && argument.type.non_null? && !argument.default_value?
|
202
|
+
result ||= Query::InputValidationResult.new
|
203
|
+
argument_result = argument.type.validate_input(nil, ctx)
|
204
|
+
if !argument_result.valid?
|
205
|
+
result.merge_result!(argument.graphql_name, argument_result)
|
203
206
|
end
|
204
207
|
end
|
205
208
|
end
|
@@ -12,7 +12,7 @@ module GraphQL
|
|
12
12
|
# @param profiles [Hash<Symbol => Hash>] A hash of `name => context` pairs for preloading visibility profiles
|
13
13
|
# @param preload [Boolean] if `true`, load the default schema profile and all named profiles immediately (defaults to `true` for `Rails.env.production?`)
|
14
14
|
# @param migration_errors [Boolean] if `true`, raise an error when `Visibility` and `Warden` return different results
|
15
|
-
def self.use(schema, dynamic: false, profiles: EmptyObjects::EMPTY_HASH, preload: (defined?(Rails) ? Rails.env.production? : nil), migration_errors: false)
|
15
|
+
def self.use(schema, dynamic: false, profiles: EmptyObjects::EMPTY_HASH, preload: (defined?(Rails.env) ? Rails.env.production? : nil), migration_errors: false)
|
16
16
|
profiles&.each { |name, ctx|
|
17
17
|
ctx[:visibility_profile] = name
|
18
18
|
ctx.freeze
|
data/lib/graphql/schema.rb
CHANGED
@@ -1189,7 +1189,7 @@ module GraphQL
|
|
1189
1189
|
# @param context [GraphQL::Query::Context] The query context for the currently-executing query
|
1190
1190
|
# @return [Class<GraphQL::Schema::Object] The Object type definition to use for `obj`
|
1191
1191
|
def resolve_type(abstract_type, application_object, context)
|
1192
|
-
raise GraphQL::RequiredImplementationMissingError, "#{self.name}.resolve_type(abstract_type, application_object, context) must be implemented to use Union types, Interface types, or `
|
1192
|
+
raise GraphQL::RequiredImplementationMissingError, "#{self.name}.resolve_type(abstract_type, application_object, context) must be implemented to use Union types, Interface types, `loads:`, or `run_partials` (tried to resolve: #{abstract_type.name})"
|
1193
1193
|
end
|
1194
1194
|
# rubocop:enable Lint/DuplicateMethods
|
1195
1195
|
|
@@ -137,7 +137,9 @@ module GraphQL
|
|
137
137
|
|
138
138
|
def dataloader_fiber_resume(source)
|
139
139
|
prev_ev = Fiber[PREVIOUS_EV_KEY]
|
140
|
-
|
140
|
+
if prev_ev
|
141
|
+
begin_notifications_event(prev_ev.name, prev_ev.payload)
|
142
|
+
end
|
141
143
|
super
|
142
144
|
end
|
143
145
|
|
data/lib/graphql/version.rb
CHANGED
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: graphql
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.5.
|
4
|
+
version: 2.5.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Robert Mosolgo
|
8
8
|
bindir: bin
|
9
9
|
cert_chain: []
|
10
|
-
date: 2025-
|
10
|
+
date: 2025-05-05 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: base64
|
@@ -163,6 +163,20 @@ dependencies:
|
|
163
163
|
- - ">="
|
164
164
|
- !ruby/object:Gem::Version
|
165
165
|
version: '0'
|
166
|
+
- !ruby/object:Gem::Dependency
|
167
|
+
name: ostruct
|
168
|
+
requirement: !ruby/object:Gem::Requirement
|
169
|
+
requirements:
|
170
|
+
- - ">="
|
171
|
+
- !ruby/object:Gem::Version
|
172
|
+
version: '0'
|
173
|
+
type: :development
|
174
|
+
prerelease: false
|
175
|
+
version_requirements: !ruby/object:Gem::Requirement
|
176
|
+
requirements:
|
177
|
+
- - ">="
|
178
|
+
- !ruby/object:Gem::Version
|
179
|
+
version: '0'
|
166
180
|
- !ruby/object:Gem::Dependency
|
167
181
|
name: rake
|
168
182
|
requirement: !ruby/object:Gem::Requirement
|
@@ -536,6 +550,7 @@ files:
|
|
536
550
|
- lib/graphql/query/fingerprint.rb
|
537
551
|
- lib/graphql/query/input_validation_result.rb
|
538
552
|
- lib/graphql/query/null_context.rb
|
553
|
+
- lib/graphql/query/partial.rb
|
539
554
|
- lib/graphql/query/result.rb
|
540
555
|
- lib/graphql/query/validation_pipeline.rb
|
541
556
|
- lib/graphql/query/variable_validation_error.rb
|
@@ -801,7 +816,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
801
816
|
- !ruby/object:Gem::Version
|
802
817
|
version: '0'
|
803
818
|
requirements: []
|
804
|
-
rubygems_version: 3.6.
|
819
|
+
rubygems_version: 3.6.8
|
805
820
|
specification_version: 4
|
806
821
|
summary: A GraphQL language and runtime for Ruby
|
807
822
|
test_files: []
|