graphql 2.0.30 → 2.3.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/generators/graphql/install/mutation_root_generator.rb +2 -2
- data/lib/generators/graphql/install/templates/base_mutation.erb +2 -0
- data/lib/generators/graphql/install/templates/mutation_type.erb +2 -0
- data/lib/generators/graphql/install_generator.rb +3 -0
- data/lib/generators/graphql/templates/base_argument.erb +2 -0
- data/lib/generators/graphql/templates/base_connection.erb +2 -0
- data/lib/generators/graphql/templates/base_edge.erb +2 -0
- data/lib/generators/graphql/templates/base_enum.erb +2 -0
- data/lib/generators/graphql/templates/base_field.erb +2 -0
- data/lib/generators/graphql/templates/base_input_object.erb +2 -0
- data/lib/generators/graphql/templates/base_interface.erb +2 -0
- data/lib/generators/graphql/templates/base_object.erb +2 -0
- data/lib/generators/graphql/templates/base_resolver.erb +6 -0
- data/lib/generators/graphql/templates/base_scalar.erb +2 -0
- data/lib/generators/graphql/templates/base_union.erb +2 -0
- data/lib/generators/graphql/templates/graphql_controller.erb +2 -0
- data/lib/generators/graphql/templates/loader.erb +2 -0
- data/lib/generators/graphql/templates/mutation.erb +2 -0
- data/lib/generators/graphql/templates/node_type.erb +2 -0
- data/lib/generators/graphql/templates/query_type.erb +2 -0
- data/lib/generators/graphql/templates/schema.erb +5 -0
- data/lib/graphql/analysis/analyzer.rb +89 -0
- data/lib/graphql/analysis/field_usage.rb +82 -0
- data/lib/graphql/analysis/max_query_complexity.rb +20 -0
- data/lib/graphql/analysis/max_query_depth.rb +20 -0
- data/lib/graphql/analysis/query_complexity.rb +183 -0
- data/lib/graphql/analysis/query_depth.rb +58 -0
- data/lib/graphql/analysis/visitor.rb +282 -0
- data/lib/graphql/analysis.rb +92 -1
- data/lib/graphql/backtrace/inspect_result.rb +0 -12
- data/lib/graphql/backtrace/trace.rb +12 -15
- data/lib/graphql/coercion_error.rb +1 -9
- data/lib/graphql/dataloader/async_dataloader.rb +88 -0
- data/lib/graphql/dataloader/null_dataloader.rb +1 -1
- data/lib/graphql/dataloader/request.rb +5 -0
- data/lib/graphql/dataloader/source.rb +11 -3
- data/lib/graphql/dataloader.rb +112 -142
- data/lib/graphql/duration_encoding_error.rb +16 -0
- data/lib/graphql/execution/interpreter/argument_value.rb +5 -1
- data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +175 -0
- data/lib/graphql/execution/interpreter/runtime.rb +163 -365
- data/lib/graphql/execution/interpreter.rb +92 -158
- data/lib/graphql/execution/lookahead.rb +88 -21
- data/lib/graphql/introspection/dynamic_fields.rb +1 -1
- data/lib/graphql/introspection/entry_points.rb +11 -5
- data/lib/graphql/introspection/schema_type.rb +3 -1
- data/lib/graphql/language/block_string.rb +34 -18
- data/lib/graphql/language/definition_slice.rb +1 -1
- data/lib/graphql/language/document_from_schema_definition.rb +38 -38
- data/lib/graphql/language/lexer.rb +305 -193
- data/lib/graphql/language/nodes.rb +113 -66
- data/lib/graphql/language/parser.rb +787 -1986
- data/lib/graphql/language/printer.rb +303 -146
- data/lib/graphql/language/sanitized_printer.rb +20 -22
- data/lib/graphql/language/static_visitor.rb +167 -0
- data/lib/graphql/language/visitor.rb +20 -81
- data/lib/graphql/language.rb +61 -0
- data/lib/graphql/load_application_object_failed_error.rb +5 -1
- data/lib/graphql/pagination/array_connection.rb +6 -6
- data/lib/graphql/pagination/connection.rb +28 -1
- data/lib/graphql/pagination/mongoid_relation_connection.rb +1 -2
- data/lib/graphql/query/context/scoped_context.rb +101 -0
- data/lib/graphql/query/context.rb +66 -131
- data/lib/graphql/query/null_context.rb +4 -11
- data/lib/graphql/query/validation_pipeline.rb +4 -4
- data/lib/graphql/query/variables.rb +3 -3
- data/lib/graphql/query.rb +17 -26
- data/lib/graphql/railtie.rb +9 -6
- data/lib/graphql/rake_task.rb +3 -12
- data/lib/graphql/rubocop/graphql/base_cop.rb +1 -1
- data/lib/graphql/schema/addition.rb +21 -11
- data/lib/graphql/schema/argument.rb +43 -8
- data/lib/graphql/schema/base_64_encoder.rb +3 -5
- data/lib/graphql/schema/build_from_definition.rb +9 -12
- data/lib/graphql/schema/directive/one_of.rb +12 -0
- data/lib/graphql/schema/directive/specified_by.rb +14 -0
- data/lib/graphql/schema/directive.rb +3 -1
- data/lib/graphql/schema/enum.rb +3 -3
- data/lib/graphql/schema/field/connection_extension.rb +1 -15
- data/lib/graphql/schema/field/scope_extension.rb +8 -1
- data/lib/graphql/schema/field.rb +49 -35
- data/lib/graphql/schema/has_single_input_argument.rb +157 -0
- data/lib/graphql/schema/input_object.rb +4 -4
- data/lib/graphql/schema/interface.rb +10 -10
- data/lib/graphql/schema/introspection_system.rb +4 -2
- data/lib/graphql/schema/late_bound_type.rb +4 -0
- data/lib/graphql/schema/list.rb +2 -2
- data/lib/graphql/schema/loader.rb +2 -3
- data/lib/graphql/schema/member/base_dsl_methods.rb +2 -1
- data/lib/graphql/schema/member/has_arguments.rb +63 -73
- data/lib/graphql/schema/member/has_directives.rb +1 -1
- data/lib/graphql/schema/member/has_fields.rb +8 -5
- data/lib/graphql/schema/member/has_interfaces.rb +23 -9
- data/lib/graphql/schema/member/relay_shortcuts.rb +1 -1
- data/lib/graphql/schema/member/scoped.rb +19 -0
- data/lib/graphql/schema/member/type_system_helpers.rb +1 -2
- data/lib/graphql/schema/member/validates_input.rb +3 -3
- data/lib/graphql/schema/mutation.rb +7 -0
- data/lib/graphql/schema/object.rb +8 -0
- data/lib/graphql/schema/printer.rb +8 -7
- data/lib/graphql/schema/relay_classic_mutation.rb +6 -128
- data/lib/graphql/schema/resolver.rb +27 -13
- data/lib/graphql/schema/scalar.rb +3 -3
- data/lib/graphql/schema/subscription.rb +11 -4
- data/lib/graphql/schema/union.rb +1 -1
- data/lib/graphql/schema/unique_within_type.rb +1 -1
- data/lib/graphql/schema/warden.rb +96 -95
- data/lib/graphql/schema.rb +323 -102
- data/lib/graphql/static_validation/all_rules.rb +1 -1
- data/lib/graphql/static_validation/base_visitor.rb +1 -1
- data/lib/graphql/static_validation/literal_validator.rb +2 -3
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +2 -2
- data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +1 -1
- data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +2 -2
- data/lib/graphql/static_validation/validation_context.rb +5 -5
- data/lib/graphql/static_validation/validator.rb +3 -0
- data/lib/graphql/static_validation.rb +0 -1
- data/lib/graphql/subscriptions/action_cable_subscriptions.rb +4 -3
- data/lib/graphql/subscriptions/broadcast_analyzer.rb +1 -1
- data/lib/graphql/subscriptions/event.rb +8 -2
- data/lib/graphql/subscriptions/serialize.rb +2 -0
- data/lib/graphql/subscriptions.rb +15 -13
- data/lib/graphql/testing/helpers.rb +151 -0
- data/lib/graphql/testing.rb +2 -0
- data/lib/graphql/tracing/appoptics_trace.rb +2 -2
- data/lib/graphql/tracing/appoptics_tracing.rb +2 -2
- data/lib/graphql/tracing/legacy_hooks_trace.rb +74 -0
- data/lib/graphql/tracing/platform_tracing.rb +3 -1
- data/lib/graphql/tracing/{prometheus_tracing → prometheus_trace}/graphql_collector.rb +3 -1
- data/lib/graphql/tracing/prometheus_trace.rb +9 -9
- data/lib/graphql/tracing/sentry_trace.rb +112 -0
- data/lib/graphql/tracing/trace.rb +1 -0
- data/lib/graphql/tracing.rb +3 -1
- data/lib/graphql/type_kinds.rb +1 -1
- data/lib/graphql/types/iso_8601_duration.rb +77 -0
- data/lib/graphql/types/relay/connection_behaviors.rb +32 -2
- data/lib/graphql/types/relay/edge_behaviors.rb +7 -0
- data/lib/graphql/types.rb +1 -0
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +13 -13
- data/readme.md +12 -2
- metadata +33 -26
- data/lib/graphql/analysis/ast/analyzer.rb +0 -84
- data/lib/graphql/analysis/ast/field_usage.rb +0 -57
- data/lib/graphql/analysis/ast/max_query_complexity.rb +0 -22
- data/lib/graphql/analysis/ast/max_query_depth.rb +0 -22
- data/lib/graphql/analysis/ast/query_complexity.rb +0 -230
- data/lib/graphql/analysis/ast/query_depth.rb +0 -55
- data/lib/graphql/analysis/ast/visitor.rb +0 -276
- data/lib/graphql/analysis/ast.rb +0 -81
- data/lib/graphql/deprecation.rb +0 -9
- data/lib/graphql/filter.rb +0 -59
- data/lib/graphql/language/parser.y +0 -560
- data/lib/graphql/schema/base_64_bp.rb +0 -26
- data/lib/graphql/static_validation/type_stack.rb +0 -216
- data/lib/graphql/subscriptions/instrumentation.rb +0 -28
|
@@ -12,7 +12,7 @@ module GraphQL
|
|
|
12
12
|
@possible_types = {}
|
|
13
13
|
@types = {}
|
|
14
14
|
@union_memberships = {}
|
|
15
|
-
@references = Hash.new { |h, k| h[k] =
|
|
15
|
+
@references = Hash.new { |h, k| h[k] = Set.new }
|
|
16
16
|
@arguments_with_default_values = []
|
|
17
17
|
add_type_and_traverse(new_types)
|
|
18
18
|
end
|
|
@@ -20,7 +20,7 @@ module GraphQL
|
|
|
20
20
|
private
|
|
21
21
|
|
|
22
22
|
def references_to(thing, from:)
|
|
23
|
-
@references[thing]
|
|
23
|
+
@references[thing].add(from)
|
|
24
24
|
end
|
|
25
25
|
|
|
26
26
|
def get_type(name)
|
|
@@ -95,7 +95,7 @@ module GraphQL
|
|
|
95
95
|
# It's a union with possible_types
|
|
96
96
|
# Replace the item by class name
|
|
97
97
|
owner.assign_type_membership_object_type(type)
|
|
98
|
-
@possible_types[owner
|
|
98
|
+
@possible_types[owner] = owner.possible_types
|
|
99
99
|
elsif type.kind.interface? && (owner.kind.object? || owner.kind.interface?)
|
|
100
100
|
new_interfaces = []
|
|
101
101
|
owner.interfaces.each do |int_t|
|
|
@@ -110,7 +110,7 @@ module GraphQL
|
|
|
110
110
|
end
|
|
111
111
|
owner.implements(*new_interfaces)
|
|
112
112
|
new_interfaces.each do |int|
|
|
113
|
-
pt = @possible_types[int
|
|
113
|
+
pt = @possible_types[int] ||= []
|
|
114
114
|
if !pt.include?(owner) && owner.is_a?(Class)
|
|
115
115
|
pt << owner
|
|
116
116
|
end
|
|
@@ -126,6 +126,7 @@ module GraphQL
|
|
|
126
126
|
@types[type.graphql_name] = type
|
|
127
127
|
when GraphQL::Schema::Field, GraphQL::Schema::Argument
|
|
128
128
|
orig_type = owner.type
|
|
129
|
+
unwrapped_t = type
|
|
129
130
|
# Apply list/non-null wrapper as needed
|
|
130
131
|
if orig_type.respond_to?(:of_type)
|
|
131
132
|
transforms = []
|
|
@@ -142,6 +143,7 @@ module GraphQL
|
|
|
142
143
|
transforms.reverse_each { |t| type = type.public_send(t) }
|
|
143
144
|
end
|
|
144
145
|
owner.type = type
|
|
146
|
+
references_to(unwrapped_t, from: owner)
|
|
145
147
|
else
|
|
146
148
|
raise "Unexpected update: #{owner.inspect} #{type.inspect}"
|
|
147
149
|
end
|
|
@@ -164,7 +166,9 @@ module GraphQL
|
|
|
164
166
|
@directives << type
|
|
165
167
|
type.all_argument_definitions.each do |arg|
|
|
166
168
|
arg_type = arg.type.unwrap
|
|
167
|
-
|
|
169
|
+
if !arg_type.is_a?(GraphQL::Schema::LateBoundType)
|
|
170
|
+
references_to(arg_type, from: arg)
|
|
171
|
+
end
|
|
168
172
|
path.push(arg.graphql_name)
|
|
169
173
|
add_type(arg_type, owner: arg, late_types: late_types, path: path)
|
|
170
174
|
path.pop
|
|
@@ -187,14 +191,18 @@ module GraphQL
|
|
|
187
191
|
type.all_field_definitions.each do |field|
|
|
188
192
|
name = field.graphql_name
|
|
189
193
|
field_type = field.type.unwrap
|
|
190
|
-
|
|
194
|
+
if !field_type.is_a?(GraphQL::Schema::LateBoundType)
|
|
195
|
+
references_to(field_type, from: field)
|
|
196
|
+
end
|
|
191
197
|
path.push(name)
|
|
192
198
|
add_type(field_type, owner: field, late_types: late_types, path: path)
|
|
193
199
|
add_directives_from(field)
|
|
194
200
|
field.all_argument_definitions.each do |arg|
|
|
195
201
|
add_directives_from(arg)
|
|
196
202
|
arg_type = arg.type.unwrap
|
|
197
|
-
|
|
203
|
+
if !arg_type.is_a?(GraphQL::Schema::LateBoundType)
|
|
204
|
+
references_to(arg_type, from: arg)
|
|
205
|
+
end
|
|
198
206
|
path.push(arg.graphql_name)
|
|
199
207
|
add_type(arg_type, owner: arg, late_types: late_types, path: path)
|
|
200
208
|
path.pop
|
|
@@ -209,7 +217,9 @@ module GraphQL
|
|
|
209
217
|
type.all_argument_definitions.each do |arg|
|
|
210
218
|
add_directives_from(arg)
|
|
211
219
|
arg_type = arg.type.unwrap
|
|
212
|
-
|
|
220
|
+
if !arg_type.is_a?(GraphQL::Schema::LateBoundType)
|
|
221
|
+
references_to(arg_type, from: arg)
|
|
222
|
+
end
|
|
213
223
|
path.push(arg.graphql_name)
|
|
214
224
|
add_type(arg_type, owner: arg, late_types: late_types, path: path)
|
|
215
225
|
path.pop
|
|
@@ -219,7 +229,7 @@ module GraphQL
|
|
|
219
229
|
end
|
|
220
230
|
end
|
|
221
231
|
if type.kind.union?
|
|
222
|
-
@possible_types[type
|
|
232
|
+
@possible_types[type] = type.all_possible_types
|
|
223
233
|
path.push("possible_types")
|
|
224
234
|
type.all_possible_types.each do |t|
|
|
225
235
|
add_type(t, owner: type, late_types: late_types, path: path)
|
|
@@ -234,7 +244,7 @@ module GraphQL
|
|
|
234
244
|
path.pop
|
|
235
245
|
end
|
|
236
246
|
if type.kind.object?
|
|
237
|
-
possible_types_for_this_name = @possible_types[type
|
|
247
|
+
possible_types_for_this_name = @possible_types[type] ||= []
|
|
238
248
|
possible_types_for_this_name << type
|
|
239
249
|
end
|
|
240
250
|
|
|
@@ -246,7 +256,7 @@ module GraphQL
|
|
|
246
256
|
interface_type = interface_type_membership.abstract_type
|
|
247
257
|
# We can get these now; we'll have to get late-bound types later
|
|
248
258
|
if interface_type.is_a?(Module) && type.is_a?(Class)
|
|
249
|
-
implementers = @possible_types[interface_type
|
|
259
|
+
implementers = @possible_types[interface_type] ||= []
|
|
250
260
|
implementers << type
|
|
251
261
|
end
|
|
252
262
|
when String, Schema::LateBoundType
|
|
@@ -206,8 +206,8 @@ module GraphQL
|
|
|
206
206
|
# Used by the runtime.
|
|
207
207
|
# @api private
|
|
208
208
|
def prepare_value(obj, value, context: nil)
|
|
209
|
-
if
|
|
210
|
-
value = value
|
|
209
|
+
if type.unwrap.kind.input_object?
|
|
210
|
+
value = recursively_prepare_input_object(value, type)
|
|
211
211
|
end
|
|
212
212
|
|
|
213
213
|
Schema::Validator.validate!(validators, obj, context, value)
|
|
@@ -221,8 +221,13 @@ module GraphQL
|
|
|
221
221
|
#
|
|
222
222
|
# This will have to be called later, when the runtime object _is_ available.
|
|
223
223
|
value
|
|
224
|
-
|
|
224
|
+
elsif obj.respond_to?(@prepare)
|
|
225
225
|
obj.public_send(@prepare, value)
|
|
226
|
+
elsif owner.respond_to?(@prepare)
|
|
227
|
+
owner.public_send(@prepare, value, context || obj.context)
|
|
228
|
+
else
|
|
229
|
+
raise "Invalid prepare for #{@owner.name}.name: #{@prepare.inspect}. "\
|
|
230
|
+
"Could not find prepare method #{@prepare} on #{obj.class} or #{owner}."
|
|
226
231
|
end
|
|
227
232
|
elsif @prepare.respond_to?(:call)
|
|
228
233
|
@prepare.call(value, context || obj.context)
|
|
@@ -285,6 +290,7 @@ module GraphQL
|
|
|
285
290
|
# TODO code smell to access such a deeply-nested constant in a distant module
|
|
286
291
|
argument_values[arg_key] = GraphQL::Execution::Interpreter::ArgumentValue.new(
|
|
287
292
|
value: resolved_loaded_value,
|
|
293
|
+
original_value: resolved_coerced_value,
|
|
288
294
|
definition: self,
|
|
289
295
|
default_used: default_used,
|
|
290
296
|
)
|
|
@@ -306,10 +312,15 @@ module GraphQL
|
|
|
306
312
|
context.query.after_lazy(custom_loaded_value) do |custom_value|
|
|
307
313
|
if loads
|
|
308
314
|
if type.list?
|
|
309
|
-
loaded_values =
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
315
|
+
loaded_values = []
|
|
316
|
+
context.dataloader.run_isolated do
|
|
317
|
+
custom_value.each_with_index.map { |custom_val, idx|
|
|
318
|
+
id = coerced_value[idx]
|
|
319
|
+
context.dataloader.append_job do
|
|
320
|
+
loaded_values[idx] = load_method_owner.authorize_application_object(self, id, context, custom_val)
|
|
321
|
+
end
|
|
322
|
+
}
|
|
323
|
+
end
|
|
313
324
|
context.schema.after_any_lazies(loaded_values, &:itself)
|
|
314
325
|
else
|
|
315
326
|
load_method_owner.authorize_application_object(self, coerced_value, context, custom_loaded_value)
|
|
@@ -320,7 +331,16 @@ module GraphQL
|
|
|
320
331
|
end
|
|
321
332
|
elsif loads
|
|
322
333
|
if type.list?
|
|
323
|
-
loaded_values =
|
|
334
|
+
loaded_values = []
|
|
335
|
+
# We want to run these list items all together,
|
|
336
|
+
# but we also need to wait for the result so we can return it :S
|
|
337
|
+
context.dataloader.run_isolated do
|
|
338
|
+
coerced_value.each_with_index { |val, idx|
|
|
339
|
+
context.dataloader.append_job do
|
|
340
|
+
loaded_values[idx] = load_method_owner.load_and_authorize_application_object(self, val, context)
|
|
341
|
+
end
|
|
342
|
+
}
|
|
343
|
+
end
|
|
324
344
|
context.schema.after_any_lazies(loaded_values, &:itself)
|
|
325
345
|
else
|
|
326
346
|
load_method_owner.load_and_authorize_application_object(self, coerced_value, context)
|
|
@@ -367,6 +387,21 @@ module GraphQL
|
|
|
367
387
|
|
|
368
388
|
private
|
|
369
389
|
|
|
390
|
+
def recursively_prepare_input_object(value, type)
|
|
391
|
+
if type.non_null?
|
|
392
|
+
type = type.of_type
|
|
393
|
+
end
|
|
394
|
+
|
|
395
|
+
if type.list? && !value.nil?
|
|
396
|
+
inner_type = type.of_type
|
|
397
|
+
value.map { |v| recursively_prepare_input_object(v, inner_type) }
|
|
398
|
+
elsif value.is_a?(GraphQL::Schema::InputObject)
|
|
399
|
+
value.prepare
|
|
400
|
+
else
|
|
401
|
+
value
|
|
402
|
+
end
|
|
403
|
+
end
|
|
404
|
+
|
|
370
405
|
def validate_input_type(input_type)
|
|
371
406
|
if input_type.is_a?(String) || input_type.is_a?(GraphQL::Schema::LateBoundType)
|
|
372
407
|
# Do nothing; assume this will be validated later
|
|
@@ -1,18 +1,16 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require 'graphql/schema/base_64_bp'
|
|
4
|
-
|
|
2
|
+
require "base64"
|
|
5
3
|
module GraphQL
|
|
6
4
|
class Schema
|
|
7
5
|
# @api private
|
|
8
6
|
module Base64Encoder
|
|
9
7
|
def self.encode(unencoded_text, nonce: false)
|
|
10
|
-
|
|
8
|
+
Base64.urlsafe_encode64(unencoded_text, padding: false)
|
|
11
9
|
end
|
|
12
10
|
|
|
13
11
|
def self.decode(encoded_text, nonce: false)
|
|
14
12
|
# urlsafe_decode64 is for forward compatibility
|
|
15
|
-
|
|
13
|
+
Base64.urlsafe_decode64(encoded_text)
|
|
16
14
|
rescue ArgumentError
|
|
17
15
|
raise GraphQL::ExecutionError, "Invalid input: #{encoded_text.inspect}"
|
|
18
16
|
end
|
|
@@ -7,10 +7,16 @@ module GraphQL
|
|
|
7
7
|
class << self
|
|
8
8
|
# @see {Schema.from_definition}
|
|
9
9
|
def from_definition(schema_superclass, definition_string, parser: GraphQL.default_parser, **kwargs)
|
|
10
|
+
if defined?(parser::SchemaParser)
|
|
11
|
+
parser = parser::SchemaParser
|
|
12
|
+
end
|
|
10
13
|
from_document(schema_superclass, parser.parse(definition_string), **kwargs)
|
|
11
14
|
end
|
|
12
15
|
|
|
13
16
|
def from_definition_path(schema_superclass, definition_path, parser: GraphQL.default_parser, **kwargs)
|
|
17
|
+
if defined?(parser::SchemaParser)
|
|
18
|
+
parser = parser::SchemaParser
|
|
19
|
+
end
|
|
14
20
|
from_document(schema_superclass, parser.parse_file(definition_path), **kwargs)
|
|
15
21
|
end
|
|
16
22
|
|
|
@@ -120,10 +126,12 @@ module GraphQL
|
|
|
120
126
|
|
|
121
127
|
builder = self
|
|
122
128
|
|
|
129
|
+
found_types = types.values
|
|
123
130
|
schema_class = Class.new(schema_superclass) do
|
|
124
131
|
begin
|
|
125
132
|
# Add these first so that there's some chance of resolving late-bound types
|
|
126
|
-
|
|
133
|
+
add_type_and_traverse(found_types, root: false)
|
|
134
|
+
orphan_types(found_types.select { |t| t.respond_to?(:kind) && t.kind.object? })
|
|
127
135
|
query query_root_type
|
|
128
136
|
mutation mutation_root_type
|
|
129
137
|
subscription subscription_root_type
|
|
@@ -432,14 +440,12 @@ module GraphQL
|
|
|
432
440
|
builder = self
|
|
433
441
|
|
|
434
442
|
field_definitions.each do |field_definition|
|
|
435
|
-
type_name = resolve_type_name(field_definition.type)
|
|
436
443
|
resolve_method_name = -"resolve_field_#{field_definition.name}"
|
|
437
444
|
schema_field_defn = owner.field(
|
|
438
445
|
field_definition.name,
|
|
439
446
|
description: field_definition.description,
|
|
440
447
|
type: type_resolver.call(field_definition.type),
|
|
441
448
|
null: true,
|
|
442
|
-
connection: type_name.end_with?("Connection"),
|
|
443
449
|
connection_extension: nil,
|
|
444
450
|
deprecation_reason: build_deprecation_reason(field_definition.directives),
|
|
445
451
|
ast_node: field_definition,
|
|
@@ -487,15 +493,6 @@ module GraphQL
|
|
|
487
493
|
}
|
|
488
494
|
resolve_type_proc
|
|
489
495
|
end
|
|
490
|
-
|
|
491
|
-
def resolve_type_name(type)
|
|
492
|
-
case type
|
|
493
|
-
when GraphQL::Language::Nodes::TypeName
|
|
494
|
-
return type.name
|
|
495
|
-
else
|
|
496
|
-
resolve_type_name(type.of_type)
|
|
497
|
-
end
|
|
498
|
-
end
|
|
499
496
|
end
|
|
500
497
|
|
|
501
498
|
private_constant :Builder
|
|
@@ -6,6 +6,18 @@ module GraphQL
|
|
|
6
6
|
description "Requires that exactly one field must be supplied and that field must not be `null`."
|
|
7
7
|
locations(GraphQL::Schema::Directive::INPUT_OBJECT)
|
|
8
8
|
default_directive true
|
|
9
|
+
|
|
10
|
+
def initialize(...)
|
|
11
|
+
super
|
|
12
|
+
|
|
13
|
+
owner.extend(IsOneOf)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
module IsOneOf
|
|
17
|
+
def one_of?
|
|
18
|
+
true
|
|
19
|
+
end
|
|
20
|
+
end
|
|
9
21
|
end
|
|
10
22
|
end
|
|
11
23
|
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
module GraphQL
|
|
3
|
+
class Schema
|
|
4
|
+
class Directive < GraphQL::Schema::Member
|
|
5
|
+
class SpecifiedBy < GraphQL::Schema::Directive
|
|
6
|
+
description "Exposes a URL that specifies the behavior of this scalar."
|
|
7
|
+
locations(GraphQL::Schema::Directive::SCALAR)
|
|
8
|
+
default_directive true
|
|
9
|
+
|
|
10
|
+
argument :url, String, description: "The URL that specifies the behavior of this scalar."
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -119,7 +119,7 @@ module GraphQL
|
|
|
119
119
|
# - lazy resolution
|
|
120
120
|
# Probably, those won't be needed here, since these are configuration arguments,
|
|
121
121
|
# not runtime arguments.
|
|
122
|
-
@arguments = self.class.coerce_arguments(nil, arguments, Query::NullContext)
|
|
122
|
+
@arguments = self.class.coerce_arguments(nil, arguments, Query::NullContext.instance)
|
|
123
123
|
end
|
|
124
124
|
|
|
125
125
|
def graphql_name
|
|
@@ -188,6 +188,8 @@ module GraphQL
|
|
|
188
188
|
assert_has_location(SCALAR)
|
|
189
189
|
elsif @owner < GraphQL::Schema
|
|
190
190
|
assert_has_location(SCHEMA)
|
|
191
|
+
elsif @owner < GraphQL::Schema::Resolver
|
|
192
|
+
assert_has_location(FIELD_DEFINITION)
|
|
191
193
|
else
|
|
192
194
|
raise "Unexpected directive owner class: #{@owner}"
|
|
193
195
|
end
|
data/lib/graphql/schema/enum.rb
CHANGED
|
@@ -14,7 +14,7 @@ module GraphQL
|
|
|
14
14
|
# # ONIONS
|
|
15
15
|
# # PEPPERS
|
|
16
16
|
# # }
|
|
17
|
-
# class PizzaTopping < GraphQL::Enum
|
|
17
|
+
# class PizzaTopping < GraphQL::Schema::Enum
|
|
18
18
|
# value :MUSHROOMS
|
|
19
19
|
# value :ONIONS
|
|
20
20
|
# value :PEPPERS
|
|
@@ -68,7 +68,7 @@ module GraphQL
|
|
|
68
68
|
end
|
|
69
69
|
|
|
70
70
|
# @return [Array<GraphQL::Schema::EnumValue>] Possible values of this enum
|
|
71
|
-
def enum_values(context = GraphQL::Query::NullContext)
|
|
71
|
+
def enum_values(context = GraphQL::Query::NullContext.instance)
|
|
72
72
|
inherited_values = superclass.respond_to?(:enum_values) ? superclass.enum_values(context) : nil
|
|
73
73
|
visible_values = []
|
|
74
74
|
warden = Warden.from_context(context)
|
|
@@ -110,7 +110,7 @@ module GraphQL
|
|
|
110
110
|
end
|
|
111
111
|
|
|
112
112
|
# @return [Hash<String => GraphQL::Schema::EnumValue>] Possible values of this enum, keyed by name.
|
|
113
|
-
def values(context = GraphQL::Query::NullContext)
|
|
113
|
+
def values(context = GraphQL::Query::NullContext.instance)
|
|
114
114
|
enum_values(context).each_with_object({}) { |val, obj| obj[val.graphql_name] = val }
|
|
115
115
|
end
|
|
116
116
|
|
|
@@ -54,23 +54,9 @@ module GraphQL
|
|
|
54
54
|
value.edge_class = custom_t
|
|
55
55
|
end
|
|
56
56
|
value
|
|
57
|
-
|
|
57
|
+
else
|
|
58
58
|
context.namespace(:connections)[:all_wrappers] ||= context.schema.connections.all_wrappers
|
|
59
59
|
context.schema.connections.wrap(field, object.object, value, original_arguments, context)
|
|
60
|
-
else
|
|
61
|
-
if object.is_a?(GraphQL::Schema::Object)
|
|
62
|
-
object = object.object
|
|
63
|
-
end
|
|
64
|
-
connection_class = GraphQL::Relay::BaseConnection.connection_for_nodes(value)
|
|
65
|
-
connection_class.new(
|
|
66
|
-
value,
|
|
67
|
-
original_arguments,
|
|
68
|
-
field: field,
|
|
69
|
-
max_page_size: field.max_page_size,
|
|
70
|
-
default_page_size: field.default_page_size,
|
|
71
|
-
parent: object,
|
|
72
|
-
context: context,
|
|
73
|
-
)
|
|
74
60
|
end
|
|
75
61
|
end
|
|
76
62
|
end
|
|
@@ -10,7 +10,14 @@ 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
|
+
if (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
|
+
end
|
|
20
|
+
scoped_items
|
|
14
21
|
else
|
|
15
22
|
value
|
|
16
23
|
end
|
data/lib/graphql/schema/field.rb
CHANGED
|
@@ -41,6 +41,14 @@ module GraphQL
|
|
|
41
41
|
end
|
|
42
42
|
end
|
|
43
43
|
|
|
44
|
+
def directives
|
|
45
|
+
if @resolver_class
|
|
46
|
+
@resolver_class.directives
|
|
47
|
+
else
|
|
48
|
+
super
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
44
52
|
# @return [Class] The thing this field was defined on (type, mutation, resolver)
|
|
45
53
|
attr_accessor :owner
|
|
46
54
|
|
|
@@ -138,7 +146,7 @@ module GraphQL
|
|
|
138
146
|
# As a last ditch, try to force loading the return type:
|
|
139
147
|
type.unwrap.name
|
|
140
148
|
end
|
|
141
|
-
@connection = return_type_name.end_with?("Connection")
|
|
149
|
+
@connection = return_type_name.end_with?("Connection") && return_type_name != "Connection"
|
|
142
150
|
else
|
|
143
151
|
@connection
|
|
144
152
|
end
|
|
@@ -218,8 +226,8 @@ module GraphQL
|
|
|
218
226
|
# @param ast_node [Language::Nodes::FieldDefinition, nil] If this schema was parsed from definition, this AST node defined the field
|
|
219
227
|
# @param method_conflict_warning [Boolean] If false, skip the warning if this field's method conflicts with a built-in method
|
|
220
228
|
# @param validates [Array<Hash>] Configurations for validating this field
|
|
221
|
-
# @fallback_value [Object] A fallback value if the method is not defined
|
|
222
|
-
def initialize(type: nil, name: nil, owner: nil, null: nil, description: NOT_CONFIGURED, deprecation_reason: nil, method: nil, hash_key: nil, dig: nil, resolver_method: nil, connection: nil, max_page_size: NOT_CONFIGURED, default_page_size: NOT_CONFIGURED, scope: nil, introspection: false, camelize: true, trace: nil, complexity: nil, ast_node: nil, extras: EMPTY_ARRAY, extensions: EMPTY_ARRAY, connection_extension: self.class.connection_extension, resolver_class: nil, subscription_scope: nil, relay_node_field: false, relay_nodes_field: false, method_conflict_warning: true, broadcastable: NOT_CONFIGURED, arguments: EMPTY_HASH, directives: EMPTY_HASH, validates: EMPTY_ARRAY, fallback_value: NOT_CONFIGURED, &definition_block)
|
|
229
|
+
# @param fallback_value [Object] A fallback value if the method is not defined
|
|
230
|
+
def initialize(type: nil, name: nil, owner: nil, null: nil, description: NOT_CONFIGURED, deprecation_reason: nil, method: nil, hash_key: nil, dig: nil, resolver_method: nil, connection: nil, max_page_size: NOT_CONFIGURED, default_page_size: NOT_CONFIGURED, scope: nil, introspection: false, camelize: true, trace: nil, complexity: nil, ast_node: nil, extras: EMPTY_ARRAY, extensions: EMPTY_ARRAY, connection_extension: self.class.connection_extension, resolver_class: nil, subscription_scope: nil, relay_node_field: false, relay_nodes_field: false, method_conflict_warning: true, broadcastable: NOT_CONFIGURED, arguments: EMPTY_HASH, directives: EMPTY_HASH, validates: EMPTY_ARRAY, fallback_value: NOT_CONFIGURED, dynamic_introspection: false, &definition_block)
|
|
223
231
|
if name.nil?
|
|
224
232
|
raise ArgumentError, "missing first `name` argument or keyword `name:`"
|
|
225
233
|
end
|
|
@@ -267,6 +275,7 @@ module GraphQL
|
|
|
267
275
|
@method_sym = method_name.to_sym
|
|
268
276
|
@resolver_method = (resolver_method || name_s).to_sym
|
|
269
277
|
@complexity = complexity
|
|
278
|
+
@dynamic_introspection = dynamic_introspection
|
|
270
279
|
@return_type_expr = type
|
|
271
280
|
@return_type_null = if !null.nil?
|
|
272
281
|
null
|
|
@@ -351,6 +360,8 @@ module GraphQL
|
|
|
351
360
|
@call_after_define = true
|
|
352
361
|
end
|
|
353
362
|
|
|
363
|
+
attr_accessor :dynamic_introspection
|
|
364
|
+
|
|
354
365
|
# If true, subscription updates with this field can be shared between viewers
|
|
355
366
|
# @return [Boolean, nil]
|
|
356
367
|
# @see GraphQL::Subscriptions::BroadcastAnalyzer
|
|
@@ -468,6 +479,8 @@ module GraphQL
|
|
|
468
479
|
if arguments[:last] && (max_possible_page_size.nil? || arguments[:last] > max_possible_page_size)
|
|
469
480
|
max_possible_page_size = arguments[:last]
|
|
470
481
|
end
|
|
482
|
+
elsif arguments.is_a?(GraphQL::UnauthorizedError)
|
|
483
|
+
raise arguments
|
|
471
484
|
end
|
|
472
485
|
|
|
473
486
|
if max_possible_page_size.nil?
|
|
@@ -480,41 +493,24 @@ module GraphQL
|
|
|
480
493
|
metadata_complexity = 0
|
|
481
494
|
lookahead = GraphQL::Execution::Lookahead.new(query: query, field: self, ast_nodes: nodes, owner_type: owner)
|
|
482
495
|
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
496
|
+
lookahead.selections.each do |next_lookahead|
|
|
497
|
+
# this includes `pageInfo`, `nodes` and `edges` and any custom fields
|
|
498
|
+
# TODO this doesn't support procs yet -- unlikely to need it.
|
|
499
|
+
metadata_complexity += next_lookahead.field.complexity
|
|
500
|
+
if next_lookahead.name != :nodes && next_lookahead.name != :edges
|
|
501
|
+
# subfields, eg, for pageInfo -- assumes no subselections
|
|
502
|
+
metadata_complexity += next_lookahead.selections.size
|
|
503
|
+
end
|
|
490
504
|
end
|
|
491
505
|
|
|
492
|
-
nodes_edges_complexity = 0
|
|
493
|
-
nodes_edges_complexity += 1 if lookahead.selects?(:edges)
|
|
494
|
-
nodes_edges_complexity += 1 if lookahead.selects?(:nodes)
|
|
495
|
-
|
|
496
506
|
# Possible bug: selections on `edges` and `nodes` are _both_ multiplied here. Should they be?
|
|
497
|
-
items_complexity = child_complexity - metadata_complexity
|
|
498
|
-
|
|
499
|
-
|
|
507
|
+
items_complexity = child_complexity - metadata_complexity
|
|
508
|
+
subfields_complexity = (max_possible_page_size * items_complexity) + metadata_complexity
|
|
509
|
+
# Apply this field's own complexity
|
|
510
|
+
apply_own_complexity_to(subfields_complexity, query, nodes)
|
|
500
511
|
end
|
|
501
512
|
else
|
|
502
|
-
|
|
503
|
-
case defined_complexity
|
|
504
|
-
when Proc
|
|
505
|
-
arguments = query.arguments_for(nodes.first, self)
|
|
506
|
-
if arguments.is_a?(GraphQL::ExecutionError)
|
|
507
|
-
return child_complexity
|
|
508
|
-
elsif arguments.respond_to?(:keyword_arguments)
|
|
509
|
-
arguments = arguments.keyword_arguments
|
|
510
|
-
end
|
|
511
|
-
|
|
512
|
-
defined_complexity.call(query.context, arguments, child_complexity)
|
|
513
|
-
when Numeric
|
|
514
|
-
defined_complexity + child_complexity
|
|
515
|
-
else
|
|
516
|
-
raise("Invalid complexity: #{defined_complexity.inspect} on #{path} (#{inspect})")
|
|
517
|
-
end
|
|
513
|
+
apply_own_complexity_to(child_complexity, query, nodes)
|
|
518
514
|
end
|
|
519
515
|
end
|
|
520
516
|
|
|
@@ -659,7 +655,7 @@ module GraphQL
|
|
|
659
655
|
method_to_call = nil
|
|
660
656
|
method_args = nil
|
|
661
657
|
|
|
662
|
-
Schema::Validator.validate!(validators, application_object, query_ctx, args)
|
|
658
|
+
@own_validators && Schema::Validator.validate!(validators, application_object, query_ctx, args)
|
|
663
659
|
|
|
664
660
|
query_ctx.query.after_lazy(self.authorized?(application_object, args, query_ctx)) do |is_authorized|
|
|
665
661
|
if is_authorized
|
|
@@ -701,7 +697,7 @@ module GraphQL
|
|
|
701
697
|
inner_object.dig(*@dig_keys)
|
|
702
698
|
elsif inner_object.key?(@method_sym)
|
|
703
699
|
inner_object[@method_sym]
|
|
704
|
-
elsif inner_object.key?(@method_str)
|
|
700
|
+
elsif inner_object.key?(@method_str) || !inner_object.default_proc.nil?
|
|
705
701
|
inner_object[@method_str]
|
|
706
702
|
elsif @fallback_value != NOT_CONFIGURED
|
|
707
703
|
@fallback_value
|
|
@@ -879,6 +875,24 @@ ERR
|
|
|
879
875
|
yield(obj, args)
|
|
880
876
|
end
|
|
881
877
|
end
|
|
878
|
+
|
|
879
|
+
def apply_own_complexity_to(child_complexity, query, nodes)
|
|
880
|
+
case (own_complexity = complexity)
|
|
881
|
+
when Numeric
|
|
882
|
+
own_complexity + child_complexity
|
|
883
|
+
when Proc
|
|
884
|
+
arguments = query.arguments_for(nodes.first, self)
|
|
885
|
+
if arguments.is_a?(GraphQL::ExecutionError)
|
|
886
|
+
return child_complexity
|
|
887
|
+
elsif arguments.respond_to?(:keyword_arguments)
|
|
888
|
+
arguments = arguments.keyword_arguments
|
|
889
|
+
end
|
|
890
|
+
|
|
891
|
+
own_complexity.call(query.context, arguments, child_complexity)
|
|
892
|
+
else
|
|
893
|
+
raise ArgumentError, "Invalid complexity for #{self.path}: #{own_complexity.inspect}"
|
|
894
|
+
end
|
|
895
|
+
end
|
|
882
896
|
end
|
|
883
897
|
end
|
|
884
898
|
end
|