graphql 1.12.16 → 1.13.2
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/generators/graphql/core.rb +3 -1
- data/lib/generators/graphql/install_generator.rb +9 -2
- data/lib/generators/graphql/mutation_generator.rb +1 -1
- data/lib/generators/graphql/object_generator.rb +2 -1
- data/lib/generators/graphql/relay.rb +19 -11
- data/lib/generators/graphql/templates/schema.erb +14 -2
- data/lib/generators/graphql/type_generator.rb +0 -1
- data/lib/graphql/analysis/ast/field_usage.rb +3 -3
- data/lib/graphql/analysis/ast/query_complexity.rb +10 -14
- data/lib/graphql/analysis/ast/visitor.rb +4 -4
- data/lib/graphql/backtrace/table.rb +1 -1
- data/lib/graphql/base_type.rb +4 -2
- data/lib/graphql/boolean_type.rb +1 -1
- data/lib/graphql/dataloader/source.rb +50 -2
- data/lib/graphql/dataloader.rb +93 -37
- data/lib/graphql/define/instance_definable.rb +1 -1
- data/lib/graphql/deprecated_dsl.rb +11 -3
- data/lib/graphql/deprecation.rb +1 -5
- data/lib/graphql/directive/deprecated_directive.rb +1 -1
- data/lib/graphql/directive/include_directive.rb +1 -1
- data/lib/graphql/directive/skip_directive.rb +1 -1
- data/lib/graphql/directive.rb +0 -4
- data/lib/graphql/enum_type.rb +5 -1
- data/lib/graphql/execution/errors.rb +1 -0
- data/lib/graphql/execution/interpreter/arguments.rb +1 -1
- data/lib/graphql/execution/interpreter/arguments_cache.rb +2 -2
- data/lib/graphql/execution/interpreter/runtime.rb +39 -23
- data/lib/graphql/execution/lookahead.rb +2 -2
- data/lib/graphql/execution/multiplex.rb +4 -1
- data/lib/graphql/float_type.rb +1 -1
- data/lib/graphql/id_type.rb +1 -1
- data/lib/graphql/int_type.rb +1 -1
- data/lib/graphql/integer_encoding_error.rb +18 -2
- data/lib/graphql/introspection/directive_type.rb +1 -1
- data/lib/graphql/introspection/entry_points.rb +2 -2
- data/lib/graphql/introspection/enum_value_type.rb +2 -2
- data/lib/graphql/introspection/field_type.rb +2 -2
- data/lib/graphql/introspection/input_value_type.rb +10 -4
- data/lib/graphql/introspection/schema_type.rb +2 -2
- data/lib/graphql/introspection/type_type.rb +10 -10
- data/lib/graphql/language/block_string.rb +2 -6
- data/lib/graphql/language/document_from_schema_definition.rb +4 -2
- data/lib/graphql/language/lexer.rb +0 -3
- data/lib/graphql/language/lexer.rl +0 -4
- data/lib/graphql/language/nodes.rb +12 -2
- data/lib/graphql/language/parser.rb +442 -434
- data/lib/graphql/language/parser.y +5 -4
- data/lib/graphql/language/printer.rb +6 -1
- data/lib/graphql/language/sanitized_printer.rb +5 -5
- data/lib/graphql/language/token.rb +0 -4
- data/lib/graphql/name_validator.rb +0 -4
- data/lib/graphql/pagination/connections.rb +35 -16
- data/lib/graphql/query/arguments.rb +1 -1
- data/lib/graphql/query/arguments_cache.rb +1 -1
- data/lib/graphql/query/context.rb +15 -2
- data/lib/graphql/query/literal_input.rb +1 -1
- data/lib/graphql/query/null_context.rb +12 -7
- data/lib/graphql/query/serial_execution/field_resolution.rb +1 -1
- data/lib/graphql/query/validation_pipeline.rb +1 -1
- data/lib/graphql/query/variables.rb +5 -1
- data/lib/graphql/query.rb +4 -0
- data/lib/graphql/relay/edges_instrumentation.rb +0 -1
- data/lib/graphql/relay/global_id_resolve.rb +1 -1
- data/lib/graphql/relay/page_info.rb +1 -1
- data/lib/graphql/rubocop/graphql/base_cop.rb +36 -0
- data/lib/graphql/rubocop/graphql/default_null_true.rb +43 -0
- data/lib/graphql/rubocop/graphql/default_required_true.rb +43 -0
- data/lib/graphql/rubocop.rb +4 -0
- data/lib/graphql/schema/addition.rb +37 -28
- data/lib/graphql/schema/argument.rb +79 -34
- data/lib/graphql/schema/build_from_definition.rb +5 -5
- data/lib/graphql/schema/directive/feature.rb +1 -1
- data/lib/graphql/schema/directive/flagged.rb +2 -2
- data/lib/graphql/schema/directive/include.rb +1 -1
- data/lib/graphql/schema/directive/skip.rb +1 -1
- data/lib/graphql/schema/directive/transform.rb +1 -1
- data/lib/graphql/schema/directive.rb +7 -3
- data/lib/graphql/schema/enum.rb +60 -10
- data/lib/graphql/schema/enum_value.rb +6 -0
- data/lib/graphql/schema/field/connection_extension.rb +1 -1
- data/lib/graphql/schema/field.rb +140 -42
- data/lib/graphql/schema/field_extension.rb +52 -2
- data/lib/graphql/schema/find_inherited_value.rb +1 -0
- data/lib/graphql/schema/finder.rb +5 -5
- data/lib/graphql/schema/input_object.rb +13 -14
- data/lib/graphql/schema/interface.rb +11 -20
- data/lib/graphql/schema/introspection_system.rb +1 -1
- data/lib/graphql/schema/list.rb +3 -1
- data/lib/graphql/schema/member/accepts_definition.rb +15 -3
- data/lib/graphql/schema/member/build_type.rb +0 -4
- data/lib/graphql/schema/member/cached_graphql_definition.rb +29 -2
- data/lib/graphql/schema/member/has_arguments.rb +145 -57
- data/lib/graphql/schema/member/has_deprecation_reason.rb +1 -1
- data/lib/graphql/schema/member/has_fields.rb +76 -18
- data/lib/graphql/schema/member/has_interfaces.rb +90 -0
- data/lib/graphql/schema/member.rb +1 -0
- data/lib/graphql/schema/non_null.rb +3 -1
- data/lib/graphql/schema/object.rb +10 -75
- data/lib/graphql/schema/printer.rb +1 -1
- data/lib/graphql/schema/relay_classic_mutation.rb +37 -3
- data/lib/graphql/schema/resolver/has_payload_type.rb +27 -2
- data/lib/graphql/schema/resolver.rb +49 -64
- data/lib/graphql/schema/scalar.rb +2 -0
- data/lib/graphql/schema/subscription.rb +17 -9
- data/lib/graphql/schema/traversal.rb +1 -1
- data/lib/graphql/schema/type_expression.rb +1 -1
- data/lib/graphql/schema/type_membership.rb +18 -4
- data/lib/graphql/schema/union.rb +8 -1
- data/lib/graphql/schema/validator/allow_blank_validator.rb +29 -0
- data/lib/graphql/schema/validator/allow_null_validator.rb +26 -0
- data/lib/graphql/schema/validator/exclusion_validator.rb +3 -1
- data/lib/graphql/schema/validator/format_validator.rb +4 -5
- data/lib/graphql/schema/validator/inclusion_validator.rb +3 -1
- data/lib/graphql/schema/validator/length_validator.rb +5 -3
- data/lib/graphql/schema/validator/numericality_validator.rb +13 -2
- data/lib/graphql/schema/validator.rb +33 -25
- data/lib/graphql/schema/warden.rb +116 -52
- data/lib/graphql/schema.rb +124 -27
- data/lib/graphql/static_validation/base_visitor.rb +8 -5
- data/lib/graphql/static_validation/definition_dependencies.rb +0 -1
- data/lib/graphql/static_validation/error.rb +3 -1
- data/lib/graphql/static_validation/literal_validator.rb +1 -1
- data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +1 -1
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +52 -26
- data/lib/graphql/static_validation/rules/fields_will_merge_error.rb +25 -4
- data/lib/graphql/static_validation/rules/fragments_are_finite.rb +2 -2
- data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +3 -1
- data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +4 -4
- data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +7 -7
- data/lib/graphql/static_validation/validation_context.rb +8 -2
- data/lib/graphql/static_validation/validator.rb +15 -12
- data/lib/graphql/string_encoding_error.rb +13 -3
- data/lib/graphql/string_type.rb +1 -1
- data/lib/graphql/subscriptions/action_cable_subscriptions.rb +15 -5
- data/lib/graphql/subscriptions/event.rb +66 -13
- data/lib/graphql/subscriptions/serialize.rb +1 -1
- data/lib/graphql/subscriptions.rb +17 -19
- data/lib/graphql/tracing/appsignal_tracing.rb +15 -0
- data/lib/graphql/types/int.rb +1 -1
- data/lib/graphql/types/relay/connection_behaviors.rb +26 -9
- data/lib/graphql/types/relay/default_relay.rb +5 -1
- data/lib/graphql/types/relay/edge_behaviors.rb +13 -2
- data/lib/graphql/types/relay/has_node_field.rb +1 -1
- data/lib/graphql/types/relay/has_nodes_field.rb +1 -1
- data/lib/graphql/types/string.rb +1 -1
- data/lib/graphql/unauthorized_error.rb +1 -1
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +10 -32
- data/readme.md +1 -1
- metadata +13 -6
data/lib/graphql/schema/field.rb
CHANGED
@@ -5,10 +5,6 @@ require "graphql/schema/field/scope_extension"
|
|
5
5
|
module GraphQL
|
6
6
|
class Schema
|
7
7
|
class Field
|
8
|
-
if !String.method_defined?(:-@)
|
9
|
-
using GraphQL::StringDedupBackport
|
10
|
-
end
|
11
|
-
|
12
8
|
include GraphQL::Schema::Member::CachedGraphQLDefinition
|
13
9
|
include GraphQL::Schema::Member::AcceptsDefinition
|
14
10
|
include GraphQL::Schema::Member::HasArguments
|
@@ -61,7 +57,7 @@ module GraphQL
|
|
61
57
|
end
|
62
58
|
|
63
59
|
def inspect
|
64
|
-
"#<#{self.class} #{path}#{
|
60
|
+
"#<#{self.class} #{path}#{all_argument_definitions.any? ? "(...)" : ""}: #{type.to_type_signature}>"
|
65
61
|
end
|
66
62
|
|
67
63
|
alias :mutation :resolver
|
@@ -122,6 +118,9 @@ module GraphQL
|
|
122
118
|
else
|
123
119
|
kwargs[:type] = type
|
124
120
|
end
|
121
|
+
if type.is_a?(Class) && type < GraphQL::Schema::Mutation
|
122
|
+
raise ArgumentError, "Use `field #{name.inspect}, mutation: Mutation, ...` to provide a mutation to this field instead"
|
123
|
+
end
|
125
124
|
end
|
126
125
|
new(**kwargs, &block)
|
127
126
|
end
|
@@ -209,7 +208,7 @@ module GraphQL
|
|
209
208
|
# @param method_conflict_warning [Boolean] If false, skip the warning if this field's method conflicts with a built-in method
|
210
209
|
# @param validates [Array<Hash>] Configurations for validating this field
|
211
210
|
# @param legacy_edge_class [Class, nil] (DEPRECATED) If present, pass this along to the legacy field definition
|
212
|
-
def initialize(type: nil, name: nil, owner: nil, null:
|
211
|
+
def initialize(type: nil, name: nil, owner: nil, null: true, field: nil, function: nil, description: nil, deprecation_reason: nil, method: nil, hash_key: nil, resolver_method: nil, resolve: nil, connection: nil, max_page_size: :not_given, scope: nil, introspection: false, camelize: true, trace: nil, complexity: 1, 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: nil, arguments: EMPTY_HASH, directives: EMPTY_HASH, validates: EMPTY_ARRAY, legacy_edge_class: nil, &definition_block)
|
213
212
|
if name.nil?
|
214
213
|
raise ArgumentError, "missing first `name` argument or keyword `name:`"
|
215
214
|
end
|
@@ -217,9 +216,6 @@ module GraphQL
|
|
217
216
|
if type.nil?
|
218
217
|
raise ArgumentError, "missing second `type` argument or keyword `type:`"
|
219
218
|
end
|
220
|
-
if null.nil?
|
221
|
-
raise ArgumentError, "missing keyword argument null:"
|
222
|
-
end
|
223
219
|
end
|
224
220
|
if (field || function || resolve) && extras.any?
|
225
221
|
raise ArgumentError, "keyword `extras:` may only be used with method-based resolve and class-based field such as mutation class, please remove `field:`, `function:` or `resolve:`"
|
@@ -278,10 +274,15 @@ module GraphQL
|
|
278
274
|
@legacy_edge_class = legacy_edge_class
|
279
275
|
|
280
276
|
arguments.each do |name, arg|
|
281
|
-
|
277
|
+
case arg
|
278
|
+
when Hash
|
282
279
|
argument(name: name, **arg)
|
283
|
-
|
280
|
+
when GraphQL::Schema::Argument
|
284
281
|
add_argument(arg)
|
282
|
+
when Array
|
283
|
+
arg.each { |a| add_argument(a) }
|
284
|
+
else
|
285
|
+
raise ArgumentError, "Unexpected argument config (#{arg.class}): #{arg.inspect}"
|
285
286
|
end
|
286
287
|
end
|
287
288
|
|
@@ -289,6 +290,7 @@ module GraphQL
|
|
289
290
|
@subscription_scope = subscription_scope
|
290
291
|
|
291
292
|
@extensions = EMPTY_ARRAY
|
293
|
+
@call_after_define = false
|
292
294
|
# This should run before connection extension,
|
293
295
|
# but should it run after the definition block?
|
294
296
|
if scoped?
|
@@ -321,6 +323,9 @@ module GraphQL
|
|
321
323
|
instance_eval(&definition_block)
|
322
324
|
end
|
323
325
|
end
|
326
|
+
|
327
|
+
self.extensions.each(&:after_define_apply)
|
328
|
+
@call_after_define = true
|
324
329
|
end
|
325
330
|
|
326
331
|
# If true, subscription updates with this field can be shared between viewers
|
@@ -353,27 +358,20 @@ module GraphQL
|
|
353
358
|
# @example adding an extension with options
|
354
359
|
# extensions([MyExtensionClass, { AnotherExtensionClass => { filter: true } }])
|
355
360
|
#
|
356
|
-
# @param extensions [Array<Class, Hash<Class =>
|
361
|
+
# @param extensions [Array<Class, Hash<Class => Hash>>] Add extensions to this field. For hash elements, only the first key/value is used.
|
357
362
|
# @return [Array<GraphQL::Schema::FieldExtension>] extensions to apply to this field
|
358
363
|
def extensions(new_extensions = nil)
|
359
|
-
if new_extensions
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
@extensions = @extensions.dup
|
365
|
-
end
|
366
|
-
new_extensions.each do |extension|
|
367
|
-
if extension.is_a?(Hash)
|
368
|
-
extension = extension.to_a[0]
|
369
|
-
extension_class, options = *extension
|
370
|
-
@extensions << extension_class.new(field: self, options: options)
|
364
|
+
if new_extensions
|
365
|
+
new_extensions.each do |extension_config|
|
366
|
+
if extension_config.is_a?(Hash)
|
367
|
+
extension_class, options = *extension_config.to_a[0]
|
368
|
+
self.extension(extension_class, options)
|
371
369
|
else
|
372
|
-
|
373
|
-
@extensions << extension_class.new(field: self, options: nil)
|
370
|
+
self.extension(extension_config)
|
374
371
|
end
|
375
372
|
end
|
376
373
|
end
|
374
|
+
@extensions
|
377
375
|
end
|
378
376
|
|
379
377
|
# Add `extension` to this field, initialized with `options` if provided.
|
@@ -384,10 +382,19 @@ module GraphQL
|
|
384
382
|
# @example adding an extension with options
|
385
383
|
# extension(MyExtensionClass, filter: true)
|
386
384
|
#
|
387
|
-
# @param
|
388
|
-
# @param options [
|
389
|
-
|
390
|
-
|
385
|
+
# @param extension_class [Class] subclass of {Schema::FieldExtension}
|
386
|
+
# @param options [Hash] if provided, given as `options:` when initializing `extension`.
|
387
|
+
# @return [void]
|
388
|
+
def extension(extension_class, options = nil)
|
389
|
+
extension_inst = extension_class.new(field: self, options: options)
|
390
|
+
if @extensions.frozen?
|
391
|
+
@extensions = @extensions.dup
|
392
|
+
end
|
393
|
+
if @call_after_define
|
394
|
+
extension_inst.after_define_apply
|
395
|
+
end
|
396
|
+
@extensions << extension_inst
|
397
|
+
nil
|
391
398
|
end
|
392
399
|
|
393
400
|
# Read extras (as symbols) from this field,
|
@@ -408,6 +415,62 @@ module GraphQL
|
|
408
415
|
end
|
409
416
|
end
|
410
417
|
|
418
|
+
def calculate_complexity(query:, nodes:, child_complexity:)
|
419
|
+
if respond_to?(:complexity_for)
|
420
|
+
lookahead = GraphQL::Execution::Lookahead.new(query: query, field: self, ast_nodes: nodes, owner_type: owner)
|
421
|
+
complexity_for(child_complexity: child_complexity, query: query, lookahead: lookahead)
|
422
|
+
elsif connection?
|
423
|
+
arguments = query.arguments_for(nodes.first, self)
|
424
|
+
max_possible_page_size = nil
|
425
|
+
if arguments[:first]
|
426
|
+
max_possible_page_size = arguments[:first]
|
427
|
+
end
|
428
|
+
if arguments[:last] && (max_possible_page_size.nil? || arguments[:last] > max_possible_page_size)
|
429
|
+
max_possible_page_size = arguments[:last]
|
430
|
+
end
|
431
|
+
|
432
|
+
if max_possible_page_size.nil?
|
433
|
+
max_possible_page_size = max_page_size || query.schema.default_max_page_size
|
434
|
+
end
|
435
|
+
|
436
|
+
if max_possible_page_size.nil?
|
437
|
+
raise GraphQL::Error, "Can't calculate complexity for #{path}, no `first:`, `last:`, `max_page_size` or `default_max_page_size`"
|
438
|
+
else
|
439
|
+
metadata_complexity = 0
|
440
|
+
lookahead = GraphQL::Execution::Lookahead.new(query: query, field: self, ast_nodes: nodes, owner_type: owner)
|
441
|
+
|
442
|
+
if (page_info_lookahead = lookahead.selection(:page_info)).selected?
|
443
|
+
metadata_complexity += 1 # pageInfo
|
444
|
+
metadata_complexity += page_info_lookahead.selections.size # subfields
|
445
|
+
end
|
446
|
+
|
447
|
+
if lookahead.selects?(:total) || lookahead.selects?(:total_count) || lookahead.selects?(:count)
|
448
|
+
metadata_complexity += 1
|
449
|
+
end
|
450
|
+
|
451
|
+
nodes_edges_complexity = 0
|
452
|
+
nodes_edges_complexity += 1 if lookahead.selects?(:edges)
|
453
|
+
nodes_edges_complexity += 1 if lookahead.selects?(:nodes)
|
454
|
+
|
455
|
+
# Possible bug: selections on `edges` and `nodes` are _both_ multiplied here. Should they be?
|
456
|
+
items_complexity = child_complexity - metadata_complexity - nodes_edges_complexity
|
457
|
+
# Add 1 for _this_ field
|
458
|
+
1 + (max_possible_page_size * items_complexity) + metadata_complexity + nodes_edges_complexity
|
459
|
+
end
|
460
|
+
else
|
461
|
+
defined_complexity = complexity
|
462
|
+
case defined_complexity
|
463
|
+
when Proc
|
464
|
+
arguments = query.arguments_for(nodes.first, self)
|
465
|
+
defined_complexity.call(query.context, arguments.keyword_arguments, child_complexity)
|
466
|
+
when Numeric
|
467
|
+
defined_complexity + child_complexity
|
468
|
+
else
|
469
|
+
raise("Invalid complexity: #{defined_complexity.inspect} on #{path} (#{inspect})")
|
470
|
+
end
|
471
|
+
end
|
472
|
+
end
|
473
|
+
|
411
474
|
def complexity(new_complexity = nil)
|
412
475
|
case new_complexity
|
413
476
|
when Proc
|
@@ -436,6 +499,8 @@ module GraphQL
|
|
436
499
|
# @return [Integer, nil] Applied to connections if {#has_max_page_size?}
|
437
500
|
attr_reader :max_page_size
|
438
501
|
|
502
|
+
prepend Schema::Member::CachedGraphQLDefinition::DeprecatedToGraphQL
|
503
|
+
|
439
504
|
# @return [GraphQL::Field]
|
440
505
|
def to_graphql
|
441
506
|
field_defn = if @field
|
@@ -490,9 +555,9 @@ module GraphQL
|
|
490
555
|
field_defn.subscription_scope = @subscription_scope
|
491
556
|
field_defn.ast_node = ast_node
|
492
557
|
|
493
|
-
|
494
|
-
arg_graphql = defn.
|
495
|
-
field_defn.arguments[arg_graphql.name] = arg_graphql
|
558
|
+
all_argument_definitions.each do |defn|
|
559
|
+
arg_graphql = defn.deprecated_to_graphql
|
560
|
+
field_defn.arguments[arg_graphql.name] = arg_graphql # rubocop:disable Development/ContextIsPassedCop -- legacy-related
|
496
561
|
end
|
497
562
|
|
498
563
|
# Support a passed-in proc, one way or another
|
@@ -510,6 +575,7 @@ module GraphQL
|
|
510
575
|
field_defn
|
511
576
|
end
|
512
577
|
|
578
|
+
class MissingReturnTypeError < GraphQL::Error; end
|
513
579
|
attr_writer :type
|
514
580
|
|
515
581
|
def type
|
@@ -517,14 +583,21 @@ module GraphQL
|
|
517
583
|
Member::BuildType.parse_type(@function.type, null: false)
|
518
584
|
elsif @field
|
519
585
|
Member::BuildType.parse_type(@field.type, null: false)
|
586
|
+
elsif @return_type_expr.nil?
|
587
|
+
# Not enough info to determine type
|
588
|
+
message = "Can't determine the return type for #{self.path}"
|
589
|
+
if @resolver_class
|
590
|
+
message += " (it has `resolver: #{@resolver_class}`, consider configuration a `type ...` for that class)"
|
591
|
+
end
|
592
|
+
raise MissingReturnTypeError, message
|
520
593
|
else
|
521
594
|
Member::BuildType.parse_type(@return_type_expr, null: @return_type_null)
|
522
595
|
end
|
523
|
-
rescue GraphQL::Schema::InvalidDocumentError => err
|
596
|
+
rescue GraphQL::Schema::InvalidDocumentError, MissingReturnTypeError => err
|
524
597
|
# Let this propagate up
|
525
598
|
raise err
|
526
599
|
rescue StandardError => err
|
527
|
-
raise
|
600
|
+
raise MissingReturnTypeError, "Failed to build return type for #{@owner.graphql_name}.#{name} from #{@return_type_expr.inspect}: (#{err.class}) #{err.message}", err.backtrace
|
528
601
|
end
|
529
602
|
|
530
603
|
def visible?(context)
|
@@ -545,13 +618,39 @@ module GraphQL
|
|
545
618
|
|
546
619
|
def authorized?(object, args, context)
|
547
620
|
if @resolver_class
|
548
|
-
# The resolver will check itself during `resolve()`
|
621
|
+
# The resolver _instance_ will check itself during `resolve()`
|
549
622
|
@resolver_class.authorized?(object, context)
|
550
623
|
else
|
624
|
+
if (arg_values = context[:current_arguments])
|
625
|
+
# ^^ that's provided by the interpreter at runtime, and includes info about whether the default value was used or not.
|
626
|
+
using_arg_values = true
|
627
|
+
arg_values = arg_values.argument_values
|
628
|
+
else
|
629
|
+
arg_values = args
|
630
|
+
using_arg_values = false
|
631
|
+
end
|
551
632
|
# Faster than `.any?`
|
552
|
-
arguments.each_value do |arg|
|
553
|
-
|
554
|
-
|
633
|
+
arguments(context).each_value do |arg|
|
634
|
+
arg_key = arg.keyword
|
635
|
+
if arg_values.key?(arg_key)
|
636
|
+
arg_value = arg_values[arg_key]
|
637
|
+
if using_arg_values
|
638
|
+
if arg_value.default_used?
|
639
|
+
# pass -- no auth required for default used
|
640
|
+
next
|
641
|
+
else
|
642
|
+
application_arg_value = arg_value.value
|
643
|
+
if application_arg_value.is_a?(GraphQL::Execution::Interpreter::Arguments)
|
644
|
+
application_arg_value.keyword_arguments
|
645
|
+
end
|
646
|
+
end
|
647
|
+
else
|
648
|
+
application_arg_value = arg_value
|
649
|
+
end
|
650
|
+
|
651
|
+
if !arg.authorized?(object, application_arg_value, context)
|
652
|
+
return false
|
653
|
+
end
|
555
654
|
end
|
556
655
|
end
|
557
656
|
true
|
@@ -608,8 +707,7 @@ module GraphQL
|
|
608
707
|
if is_authorized
|
609
708
|
public_send_field(object, args, ctx)
|
610
709
|
else
|
611
|
-
|
612
|
-
ctx.schema.unauthorized_field(err)
|
710
|
+
raise GraphQL::UnauthorizedFieldError.new(object: application_object, type: object.class, context: ctx, field: self)
|
613
711
|
end
|
614
712
|
end
|
615
713
|
rescue GraphQL::UnauthorizedFieldError => err
|
@@ -649,7 +747,7 @@ module GraphQL
|
|
649
747
|
ruby_kwargs = graphql_args.to_kwargs
|
650
748
|
maybe_lazies = []
|
651
749
|
# Apply any `prepare` methods. Not great code organization, can this go somewhere better?
|
652
|
-
arguments.each do |name, arg_defn|
|
750
|
+
arguments(field_ctx).each do |name, arg_defn|
|
653
751
|
ruby_kwargs_key = arg_defn.keyword
|
654
752
|
|
655
753
|
if ruby_kwargs.key?(ruby_kwargs_key)
|
@@ -15,15 +15,40 @@ module GraphQL
|
|
15
15
|
# @return [Object]
|
16
16
|
attr_reader :options
|
17
17
|
|
18
|
+
# @return [Array<Symbol>, nil] `default_argument`s added, if any were added (otherwise, `nil`)
|
19
|
+
attr_reader :added_default_arguments
|
20
|
+
|
18
21
|
# Called when the extension is mounted with `extension(name, options)`.
|
19
|
-
# The instance
|
22
|
+
# The instance will be frozen to avoid improper use of state during execution.
|
20
23
|
# @param field [GraphQL::Schema::Field] The field where this extension was mounted
|
21
24
|
# @param options [Object] The second argument to `extension`, or `{}` if nothing was passed.
|
22
25
|
def initialize(field:, options:)
|
23
26
|
@field = field
|
24
27
|
@options = options || {}
|
28
|
+
@added_default_arguments = nil
|
25
29
|
apply
|
26
|
-
|
30
|
+
end
|
31
|
+
|
32
|
+
class << self
|
33
|
+
# @return [Array(Array, Hash), nil] A list of default argument configs, or `nil` if there aren't any
|
34
|
+
def default_argument_configurations
|
35
|
+
args = superclass.respond_to?(:default_argument_configurations) ? superclass.default_argument_configurations : nil
|
36
|
+
if @own_default_argument_configurations
|
37
|
+
if args
|
38
|
+
args.concat(@own_default_argument_configurations)
|
39
|
+
else
|
40
|
+
args = @own_default_argument_configurations.dup
|
41
|
+
end
|
42
|
+
end
|
43
|
+
args
|
44
|
+
end
|
45
|
+
|
46
|
+
# @see Argument#initialize
|
47
|
+
# @see HasArguments#argument
|
48
|
+
def default_argument(*argument_args, **argument_kwargs)
|
49
|
+
configs = @own_default_argument_configurations ||= []
|
50
|
+
configs << [argument_args, argument_kwargs]
|
51
|
+
end
|
27
52
|
end
|
28
53
|
|
29
54
|
# Called when this extension is attached to a field.
|
@@ -32,6 +57,31 @@ module GraphQL
|
|
32
57
|
def apply
|
33
58
|
end
|
34
59
|
|
60
|
+
# Called after the field's definition block has been executed.
|
61
|
+
# (Any arguments from the block are present on `field`)
|
62
|
+
# @return [void]
|
63
|
+
def after_define
|
64
|
+
end
|
65
|
+
|
66
|
+
# @api private
|
67
|
+
def after_define_apply
|
68
|
+
after_define
|
69
|
+
if (configs = self.class.default_argument_configurations)
|
70
|
+
existing_keywords = field.all_argument_definitions.map(&:keyword)
|
71
|
+
existing_keywords.uniq!
|
72
|
+
@added_default_arguments = []
|
73
|
+
configs.each do |config|
|
74
|
+
argument_args, argument_kwargs = config
|
75
|
+
arg_name = argument_args[0]
|
76
|
+
if !existing_keywords.include?(arg_name)
|
77
|
+
@added_default_arguments << arg_name
|
78
|
+
field.argument(*argument_args, **argument_kwargs)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
freeze
|
83
|
+
end
|
84
|
+
|
35
85
|
# Called before resolving {#field}. It should either:
|
36
86
|
#
|
37
87
|
# - `yield` values to continue execution; OR
|
@@ -38,7 +38,7 @@ module GraphQL
|
|
38
38
|
|
39
39
|
find_in_directive(directive, path: path)
|
40
40
|
else
|
41
|
-
type = schema.get_type(type_or_directive)
|
41
|
+
type = schema.get_type(type_or_directive) # rubocop:disable Development/ContextIsPassedCop -- build-time
|
42
42
|
|
43
43
|
if type.nil?
|
44
44
|
raise MemberNotFoundError, "Could not find type `#{type_or_directive}` in schema."
|
@@ -56,7 +56,7 @@ module GraphQL
|
|
56
56
|
|
57
57
|
def find_in_directive(directive, path:)
|
58
58
|
argument_name = path.shift
|
59
|
-
argument = directive.
|
59
|
+
argument = directive.get_argument(argument_name) # rubocop:disable Development/ContextIsPassedCop -- build-time
|
60
60
|
|
61
61
|
if argument.nil?
|
62
62
|
raise MemberNotFoundError, "Could not find argument `#{argument_name}` on directive #{directive}."
|
@@ -102,7 +102,7 @@ module GraphQL
|
|
102
102
|
|
103
103
|
def find_in_field(field, path:)
|
104
104
|
argument_name = path.shift
|
105
|
-
argument = field.
|
105
|
+
argument = field.get_argument(argument_name) # rubocop:disable Development/ContextIsPassedCop -- build-time
|
106
106
|
|
107
107
|
if argument.nil?
|
108
108
|
raise MemberNotFoundError, "Could not find argument `#{argument_name}` on field `#{field.name}`."
|
@@ -119,7 +119,7 @@ module GraphQL
|
|
119
119
|
|
120
120
|
def find_in_input_object(input_object, path:)
|
121
121
|
field_name = path.shift
|
122
|
-
input_field = input_object.
|
122
|
+
input_field = input_object.get_argument(field_name) # rubocop:disable Development/ContextIsPassedCop -- build-time
|
123
123
|
|
124
124
|
if input_field.nil?
|
125
125
|
raise MemberNotFoundError, "Could not find input field `#{field_name}` on input object type `#{input_object.graphql_name}`."
|
@@ -136,7 +136,7 @@ module GraphQL
|
|
136
136
|
|
137
137
|
def find_in_enum_type(enum_type, path:)
|
138
138
|
value_name = path.shift
|
139
|
-
enum_value = enum_type.
|
139
|
+
enum_value = enum_type.enum_values.find { |v| v.graphql_name == value_name } # rubocop:disable Development/ContextIsPassedCop -- build-time, not runtime
|
140
140
|
|
141
141
|
if enum_value.nil?
|
142
142
|
raise MemberNotFoundError, "Could not find enum value `#{value_name}` on enum type `#{enum_type.graphql_name}`."
|
@@ -31,7 +31,7 @@ module GraphQL
|
|
31
31
|
end
|
32
32
|
# Apply prepares, not great to have it duplicated here.
|
33
33
|
maybe_lazies = []
|
34
|
-
self.class.arguments.each_value do |arg_defn|
|
34
|
+
self.class.arguments(context).each_value do |arg_defn|
|
35
35
|
ruby_kwargs_key = arg_defn.keyword
|
36
36
|
|
37
37
|
if @ruby_style_hash.key?(ruby_kwargs_key)
|
@@ -40,11 +40,7 @@ module GraphQL
|
|
40
40
|
# With the interpreter, it's done during `coerce_arguments`
|
41
41
|
if loads && !arg_defn.from_resolver? && !context.interpreter?
|
42
42
|
value = @ruby_style_hash[ruby_kwargs_key]
|
43
|
-
loaded_value =
|
44
|
-
value.map { |val| load_application_object(arg_defn, loads, val, context) }
|
45
|
-
else
|
46
|
-
load_application_object(arg_defn, loads, value, context)
|
47
|
-
end
|
43
|
+
loaded_value = arg_defn.load_and_authorize_value(self, value, context)
|
48
44
|
maybe_lazies << context.schema.after_lazy(loaded_value) do |loaded_value|
|
49
45
|
overwrite_argument(ruby_kwargs_key, loaded_value)
|
50
46
|
end
|
@@ -71,11 +67,11 @@ module GraphQL
|
|
71
67
|
end
|
72
68
|
|
73
69
|
def prepare
|
74
|
-
if context
|
75
|
-
context.schema.after_any_lazies(@maybe_lazies) do
|
76
|
-
object = context[:current_object]
|
70
|
+
if @context
|
71
|
+
@context.schema.after_any_lazies(@maybe_lazies) do
|
72
|
+
object = @context[:current_object]
|
77
73
|
# Pass this object's class with `as` so that messages are rendered correctly from inherited validators
|
78
|
-
Schema::Validator.validate!(self.class.validators, object, context, @ruby_style_hash, as: self.class)
|
74
|
+
Schema::Validator.validate!(self.class.validators, object, @context, @ruby_style_hash, as: self.class)
|
79
75
|
self
|
80
76
|
end
|
81
77
|
else
|
@@ -133,8 +129,11 @@ module GraphQL
|
|
133
129
|
self[#{method_name.inspect}]
|
134
130
|
end
|
135
131
|
RUBY
|
132
|
+
argument_defn
|
136
133
|
end
|
137
134
|
|
135
|
+
prepend Schema::Member::CachedGraphQLDefinition::DeprecatedToGraphQL
|
136
|
+
|
138
137
|
def to_graphql
|
139
138
|
type_defn = GraphQL::InputObjectType.new
|
140
139
|
type_defn.name = graphql_name
|
@@ -142,8 +141,8 @@ module GraphQL
|
|
142
141
|
type_defn.metadata[:type_class] = self
|
143
142
|
type_defn.mutation = mutation
|
144
143
|
type_defn.ast_node = ast_node
|
145
|
-
|
146
|
-
type_defn.arguments[arg.graphql_definition.name] = arg.graphql_definition
|
144
|
+
all_argument_definitions.each do |arg|
|
145
|
+
type_defn.arguments[arg.graphql_definition(silence_deprecation_warning: true).name] = arg.graphql_definition(silence_deprecation_warning: true) # rubocop:disable Development/ContextIsPassedCop -- legacy-related
|
147
146
|
end
|
148
147
|
# Make a reference to a classic-style Arguments class
|
149
148
|
self.arguments_class = GraphQL::Query::Arguments.construct_arguments_class(type_defn)
|
@@ -176,7 +175,7 @@ module GraphQL
|
|
176
175
|
end
|
177
176
|
|
178
177
|
# Inject missing required arguments
|
179
|
-
missing_required_inputs = self.arguments.reduce({}) do |m, (argument_name, argument)|
|
178
|
+
missing_required_inputs = self.arguments(ctx).reduce({}) do |m, (argument_name, argument)|
|
180
179
|
if !input.key?(argument_name) && argument.type.non_null? && warden.get_argument(self, argument_name)
|
181
180
|
m[argument_name] = nil
|
182
181
|
end
|
@@ -227,7 +226,7 @@ module GraphQL
|
|
227
226
|
|
228
227
|
result = {}
|
229
228
|
|
230
|
-
arguments.each do |input_key, input_field_defn|
|
229
|
+
arguments(ctx).each do |input_key, input_field_defn|
|
231
230
|
input_value = value[input_key]
|
232
231
|
if value.key?(input_key)
|
233
232
|
result[input_key] = if input_value.nil?
|
@@ -16,6 +16,7 @@ module GraphQL
|
|
16
16
|
include GraphQL::Schema::Member::HasAstNode
|
17
17
|
include GraphQL::Schema::Member::HasUnresolvedTypeError
|
18
18
|
include GraphQL::Schema::Member::HasDirectives
|
19
|
+
include GraphQL::Schema::Member::HasInterfaces
|
19
20
|
|
20
21
|
# Methods defined in this block will be:
|
21
22
|
# - Added as class methods to this interface
|
@@ -57,9 +58,10 @@ module GraphQL
|
|
57
58
|
child_class.extend(Schema::Interface::DefinitionMethods)
|
58
59
|
|
59
60
|
child_class.type_membership_class(self.type_membership_class)
|
60
|
-
child_class.
|
61
|
-
|
62
|
-
|
61
|
+
child_class.ancestors.reverse_each do |ancestor|
|
62
|
+
if ancestor.const_defined?(:DefinitionMethods)
|
63
|
+
child_class.extend(ancestor::DefinitionMethods)
|
64
|
+
end
|
63
65
|
end
|
64
66
|
|
65
67
|
# Use an instance variable to tell whether it's been included previously or not;
|
@@ -73,16 +75,13 @@ module GraphQL
|
|
73
75
|
end
|
74
76
|
child_class.introspection(introspection)
|
75
77
|
child_class.description(description)
|
76
|
-
if overridden_graphql_name
|
77
|
-
child_class.graphql_name(overridden_graphql_name)
|
78
|
-
end
|
79
78
|
# If interfaces are mixed into each other, only define this class once
|
80
79
|
if !child_class.const_defined?(:UnresolvedTypeError, false)
|
81
80
|
add_unresolved_type_error(child_class)
|
82
81
|
end
|
83
82
|
elsif child_class < GraphQL::Schema::Object
|
84
83
|
# This is being included into an object type, make sure it's using `implements(...)`
|
85
|
-
backtrace_line = caller(0, 10).find { |line| line.include?("schema/
|
84
|
+
backtrace_line = caller(0, 10).find { |line| line.include?("schema/member/has_interfaces.rb") && line.include?("in `implements'")}
|
86
85
|
if !backtrace_line
|
87
86
|
raise "Attach interfaces using `implements(#{self})`, not `include(#{self})`"
|
88
87
|
end
|
@@ -101,6 +100,8 @@ module GraphQL
|
|
101
100
|
end
|
102
101
|
end
|
103
102
|
|
103
|
+
prepend Schema::Member::CachedGraphQLDefinition::DeprecatedToGraphQL
|
104
|
+
|
104
105
|
def to_graphql
|
105
106
|
type_defn = GraphQL::InterfaceType.new
|
106
107
|
type_defn.name = graphql_name
|
@@ -108,9 +109,9 @@ module GraphQL
|
|
108
109
|
type_defn.orphan_types = orphan_types
|
109
110
|
type_defn.type_membership_class = self.type_membership_class
|
110
111
|
type_defn.ast_node = ast_node
|
111
|
-
fields.each do |field_name, field_inst|
|
112
|
-
field_defn = field_inst.graphql_definition
|
113
|
-
type_defn.fields[field_defn.name] = field_defn
|
112
|
+
fields.each do |field_name, field_inst| # rubocop:disable Development/ContextIsPassedCop -- legacy-related
|
113
|
+
field_defn = field_inst.graphql_definition(silence_deprecation_warning: true)
|
114
|
+
type_defn.fields[field_defn.name] = field_defn # rubocop:disable Development/ContextIsPassedCop -- legacy-related
|
114
115
|
end
|
115
116
|
type_defn.metadata[:type_class] = self
|
116
117
|
if respond_to?(:resolve_type)
|
@@ -122,16 +123,6 @@ module GraphQL
|
|
122
123
|
def kind
|
123
124
|
GraphQL::TypeKinds::INTERFACE
|
124
125
|
end
|
125
|
-
|
126
|
-
protected
|
127
|
-
|
128
|
-
def own_interfaces
|
129
|
-
@own_interfaces ||= []
|
130
|
-
end
|
131
|
-
|
132
|
-
def interfaces
|
133
|
-
own_interfaces + (own_interfaces.map { |i| i.own_interfaces }).flatten
|
134
|
-
end
|
135
126
|
end
|
136
127
|
|
137
128
|
# Extend this _after_ `DefinitionMethods` is defined, so it will be used
|
@@ -107,7 +107,7 @@ module GraphQL
|
|
107
107
|
dup_type_class(const)
|
108
108
|
else
|
109
109
|
# Use `.to_graphql` to get a freshly-made version, not shared between schemas
|
110
|
-
const.
|
110
|
+
const.deprecated_to_graphql
|
111
111
|
end
|
112
112
|
rescue NameError
|
113
113
|
# Dup the built-in so that the cached fields aren't shared
|
data/lib/graphql/schema/list.rb
CHANGED
@@ -8,8 +8,10 @@ module GraphQL
|
|
8
8
|
class List < GraphQL::Schema::Wrapper
|
9
9
|
include Schema::Member::ValidatesInput
|
10
10
|
|
11
|
+
prepend Schema::Member::CachedGraphQLDefinition::DeprecatedToGraphQL
|
12
|
+
|
11
13
|
def to_graphql
|
12
|
-
@of_type.graphql_definition.to_list_type
|
14
|
+
@of_type.graphql_definition(silence_deprecation_warning: true).to_list_type
|
13
15
|
end
|
14
16
|
|
15
17
|
# @return [GraphQL::TypeKinds::LIST]
|
@@ -85,8 +85,15 @@ module GraphQL
|
|
85
85
|
define_method(name) do |*args|
|
86
86
|
if args.any?
|
87
87
|
instance_variable_set(ivar_name, args)
|
88
|
+
else
|
89
|
+
if (v = instance_variable_get(ivar_name))
|
90
|
+
v
|
91
|
+
elsif (ancestor = ancestors.find { |i| i.respond_to?(name) && i != self })
|
92
|
+
ancestor.public_send(name)
|
93
|
+
else
|
94
|
+
nil
|
95
|
+
end
|
88
96
|
end
|
89
|
-
instance_variable_get(ivar_name) || ((int = interfaces.first { |i| i.respond_to?()}) && int.public_send(name))
|
90
97
|
end
|
91
98
|
end
|
92
99
|
end
|
@@ -116,8 +123,13 @@ module GraphQL
|
|
116
123
|
end
|
117
124
|
|
118
125
|
module ToGraphQLExtension
|
119
|
-
def to_graphql
|
120
|
-
|
126
|
+
def to_graphql(*args, **kwargs)
|
127
|
+
|
128
|
+
defn = if args.empty? && kwargs.empty?
|
129
|
+
super()
|
130
|
+
else
|
131
|
+
super
|
132
|
+
end
|
121
133
|
accepts_definition_methods.each do |method_name|
|
122
134
|
value = public_send(method_name)
|
123
135
|
if !value.nil?
|
@@ -4,10 +4,6 @@ module GraphQL
|
|
4
4
|
class Member
|
5
5
|
# @api private
|
6
6
|
module BuildType
|
7
|
-
if !String.method_defined?(:match?)
|
8
|
-
using GraphQL::StringMatchBackport
|
9
|
-
end
|
10
|
-
|
11
7
|
LIST_TYPE_ERROR = "Use an array of [T] or [T, null: true] for list types; other arrays are not supported"
|
12
8
|
|
13
9
|
module_function
|