graphql 1.13.0 → 1.13.4
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/graphql/analysis/ast/field_usage.rb +6 -2
- data/lib/graphql/base_type.rb +4 -2
- data/lib/graphql/boolean_type.rb +1 -1
- 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/execution/interpreter/runtime.rb +11 -7
- data/lib/graphql/execution/multiplex.rb +3 -0
- 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/language/block_string.rb +2 -2
- data/lib/graphql/language/document_from_schema_definition.rb +7 -3
- data/lib/graphql/language/nodes.rb +11 -2
- data/lib/graphql/pagination/active_record_relation_connection.rb +43 -6
- data/lib/graphql/pagination/relation_connection.rb +57 -27
- data/lib/graphql/query/context.rb +10 -0
- data/lib/graphql/relay/global_id_resolve.rb +1 -1
- data/lib/graphql/relay/page_info.rb +1 -1
- data/lib/graphql/schema/argument.rb +8 -10
- data/lib/graphql/schema/directive.rb +5 -1
- data/lib/graphql/schema/enum.rb +3 -1
- data/lib/graphql/schema/enum_value.rb +2 -0
- data/lib/graphql/schema/field.rb +139 -62
- data/lib/graphql/schema/field_extension.rb +89 -2
- data/lib/graphql/schema/input_object.rb +18 -1
- data/lib/graphql/schema/interface.rb +3 -1
- 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 +7 -2
- data/lib/graphql/schema/member/cached_graphql_definition.rb +29 -2
- data/lib/graphql/schema/non_null.rb +7 -1
- data/lib/graphql/schema/object.rb +3 -1
- data/lib/graphql/schema/relay_classic_mutation.rb +8 -0
- data/lib/graphql/schema/resolver.rb +19 -13
- data/lib/graphql/schema/scalar.rb +2 -0
- data/lib/graphql/schema/traversal.rb +1 -1
- data/lib/graphql/schema/union.rb +2 -0
- data/lib/graphql/schema/validator/required_validator.rb +29 -15
- data/lib/graphql/schema/validator.rb +4 -7
- data/lib/graphql/schema.rb +24 -8
- data/lib/graphql/static_validation/all_rules.rb +1 -0
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +14 -7
- data/lib/graphql/static_validation/rules/query_root_exists.rb +17 -0
- data/lib/graphql/static_validation/rules/query_root_exists_error.rb +26 -0
- data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +2 -0
- data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +6 -0
- data/lib/graphql/string_type.rb +1 -1
- data/lib/graphql/subscriptions/action_cable_subscriptions.rb +2 -0
- data/lib/graphql/subscriptions/serialize.rb +22 -2
- data/lib/graphql/tracing/active_support_notifications_tracing.rb +6 -20
- data/lib/graphql/tracing/notifications_tracing.rb +59 -0
- 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/node_field.rb +14 -3
- data/lib/graphql/types/relay/nodes_field.rb +13 -3
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +1 -1
- metadata +5 -2
data/lib/graphql/schema/field.rb
CHANGED
@@ -16,6 +16,8 @@ module GraphQL
|
|
16
16
|
include GraphQL::Schema::Member::HasDirectives
|
17
17
|
include GraphQL::Schema::Member::HasDeprecationReason
|
18
18
|
|
19
|
+
class FieldImplementationFailed < GraphQL::Error; end
|
20
|
+
|
19
21
|
# @return [String] the GraphQL name for this field, camelized unless `camelize: false` is provided
|
20
22
|
attr_reader :name
|
21
23
|
alias :graphql_name :name
|
@@ -290,6 +292,7 @@ module GraphQL
|
|
290
292
|
@subscription_scope = subscription_scope
|
291
293
|
|
292
294
|
@extensions = EMPTY_ARRAY
|
295
|
+
@call_after_define = false
|
293
296
|
# This should run before connection extension,
|
294
297
|
# but should it run after the definition block?
|
295
298
|
if scoped?
|
@@ -322,6 +325,9 @@ module GraphQL
|
|
322
325
|
instance_eval(&definition_block)
|
323
326
|
end
|
324
327
|
end
|
328
|
+
|
329
|
+
self.extensions.each(&:after_define_apply)
|
330
|
+
@call_after_define = true
|
325
331
|
end
|
326
332
|
|
327
333
|
# If true, subscription updates with this field can be shared between viewers
|
@@ -354,27 +360,20 @@ module GraphQL
|
|
354
360
|
# @example adding an extension with options
|
355
361
|
# extensions([MyExtensionClass, { AnotherExtensionClass => { filter: true } }])
|
356
362
|
#
|
357
|
-
# @param extensions [Array<Class, Hash<Class =>
|
363
|
+
# @param extensions [Array<Class, Hash<Class => Hash>>] Add extensions to this field. For hash elements, only the first key/value is used.
|
358
364
|
# @return [Array<GraphQL::Schema::FieldExtension>] extensions to apply to this field
|
359
365
|
def extensions(new_extensions = nil)
|
360
|
-
if new_extensions
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
@extensions = @extensions.dup
|
366
|
-
end
|
367
|
-
new_extensions.each do |extension|
|
368
|
-
if extension.is_a?(Hash)
|
369
|
-
extension = extension.to_a[0]
|
370
|
-
extension_class, options = *extension
|
371
|
-
@extensions << extension_class.new(field: self, options: options)
|
366
|
+
if new_extensions
|
367
|
+
new_extensions.each do |extension_config|
|
368
|
+
if extension_config.is_a?(Hash)
|
369
|
+
extension_class, options = *extension_config.to_a[0]
|
370
|
+
self.extension(extension_class, options)
|
372
371
|
else
|
373
|
-
|
374
|
-
@extensions << extension_class.new(field: self, options: nil)
|
372
|
+
self.extension(extension_config)
|
375
373
|
end
|
376
374
|
end
|
377
375
|
end
|
376
|
+
@extensions
|
378
377
|
end
|
379
378
|
|
380
379
|
# Add `extension` to this field, initialized with `options` if provided.
|
@@ -385,10 +384,19 @@ module GraphQL
|
|
385
384
|
# @example adding an extension with options
|
386
385
|
# extension(MyExtensionClass, filter: true)
|
387
386
|
#
|
388
|
-
# @param
|
389
|
-
# @param options [
|
390
|
-
|
391
|
-
|
387
|
+
# @param extension_class [Class] subclass of {Schema::FieldExtension}
|
388
|
+
# @param options [Hash] if provided, given as `options:` when initializing `extension`.
|
389
|
+
# @return [void]
|
390
|
+
def extension(extension_class, options = nil)
|
391
|
+
extension_inst = extension_class.new(field: self, options: options)
|
392
|
+
if @extensions.frozen?
|
393
|
+
@extensions = @extensions.dup
|
394
|
+
end
|
395
|
+
if @call_after_define
|
396
|
+
extension_inst.after_define_apply
|
397
|
+
end
|
398
|
+
@extensions << extension_inst
|
399
|
+
nil
|
392
400
|
end
|
393
401
|
|
394
402
|
# Read extras (as symbols) from this field,
|
@@ -441,10 +449,15 @@ module GraphQL
|
|
441
449
|
if lookahead.selects?(:total) || lookahead.selects?(:total_count) || lookahead.selects?(:count)
|
442
450
|
metadata_complexity += 1
|
443
451
|
end
|
452
|
+
|
453
|
+
nodes_edges_complexity = 0
|
454
|
+
nodes_edges_complexity += 1 if lookahead.selects?(:edges)
|
455
|
+
nodes_edges_complexity += 1 if lookahead.selects?(:nodes)
|
456
|
+
|
444
457
|
# Possible bug: selections on `edges` and `nodes` are _both_ multiplied here. Should they be?
|
445
|
-
items_complexity = child_complexity - metadata_complexity
|
458
|
+
items_complexity = child_complexity - metadata_complexity - nodes_edges_complexity
|
446
459
|
# Add 1 for _this_ field
|
447
|
-
1 + (max_possible_page_size * items_complexity) + metadata_complexity
|
460
|
+
1 + (max_possible_page_size * items_complexity) + metadata_complexity + nodes_edges_complexity
|
448
461
|
end
|
449
462
|
else
|
450
463
|
defined_complexity = complexity
|
@@ -488,6 +501,8 @@ module GraphQL
|
|
488
501
|
# @return [Integer, nil] Applied to connections if {#has_max_page_size?}
|
489
502
|
attr_reader :max_page_size
|
490
503
|
|
504
|
+
prepend Schema::Member::CachedGraphQLDefinition::DeprecatedToGraphQL
|
505
|
+
|
491
506
|
# @return [GraphQL::Field]
|
492
507
|
def to_graphql
|
493
508
|
field_defn = if @field
|
@@ -543,7 +558,7 @@ module GraphQL
|
|
543
558
|
field_defn.ast_node = ast_node
|
544
559
|
|
545
560
|
all_argument_definitions.each do |defn|
|
546
|
-
arg_graphql = defn.
|
561
|
+
arg_graphql = defn.deprecated_to_graphql
|
547
562
|
field_defn.arguments[arg_graphql.name] = arg_graphql # rubocop:disable Development/ContextIsPassedCop -- legacy-related
|
548
563
|
end
|
549
564
|
|
@@ -605,7 +620,7 @@ module GraphQL
|
|
605
620
|
|
606
621
|
def authorized?(object, args, context)
|
607
622
|
if @resolver_class
|
608
|
-
# The resolver will check itself during `resolve()`
|
623
|
+
# The resolver _instance_ will check itself during `resolve()`
|
609
624
|
@resolver_class.authorized?(object, context)
|
610
625
|
else
|
611
626
|
if (arg_values = context[:current_arguments])
|
@@ -779,51 +794,103 @@ module GraphQL
|
|
779
794
|
|
780
795
|
def public_send_field(unextended_obj, unextended_ruby_kwargs, query_ctx)
|
781
796
|
with_extensions(unextended_obj, unextended_ruby_kwargs, query_ctx) do |obj, ruby_kwargs|
|
782
|
-
|
783
|
-
|
784
|
-
|
785
|
-
|
786
|
-
|
787
|
-
|
788
|
-
|
789
|
-
|
790
|
-
#
|
791
|
-
# - A method on the type instance;
|
792
|
-
# - Hash keys, if the wrapped object is a hash;
|
793
|
-
# - A method on the wrapped object;
|
794
|
-
# - Or, raise not implemented.
|
795
|
-
#
|
796
|
-
if obj.respond_to?(@resolver_method)
|
797
|
-
# Call the method with kwargs, if there are any
|
798
|
-
if ruby_kwargs.any?
|
799
|
-
obj.public_send(@resolver_method, **ruby_kwargs)
|
800
|
-
else
|
801
|
-
obj.public_send(@resolver_method)
|
797
|
+
begin
|
798
|
+
method_receiver = nil
|
799
|
+
method_to_call = nil
|
800
|
+
if @resolver_class
|
801
|
+
if obj.is_a?(GraphQL::Schema::Object)
|
802
|
+
obj = obj.object
|
803
|
+
end
|
804
|
+
obj = @resolver_class.new(object: obj, context: query_ctx, field: self)
|
802
805
|
end
|
803
|
-
|
804
|
-
|
805
|
-
|
806
|
-
|
806
|
+
|
807
|
+
# Find a way to resolve this field, checking:
|
808
|
+
#
|
809
|
+
# - A method on the type instance;
|
810
|
+
# - Hash keys, if the wrapped object is a hash;
|
811
|
+
# - A method on the wrapped object;
|
812
|
+
# - Or, raise not implemented.
|
813
|
+
#
|
814
|
+
if obj.respond_to?(@resolver_method)
|
815
|
+
method_to_call = @resolver_method
|
816
|
+
method_receiver = obj
|
817
|
+
# Call the method with kwargs, if there are any
|
818
|
+
if ruby_kwargs.any?
|
819
|
+
obj.public_send(@resolver_method, **ruby_kwargs)
|
820
|
+
else
|
821
|
+
obj.public_send(@resolver_method)
|
822
|
+
end
|
823
|
+
elsif obj.object.is_a?(Hash)
|
824
|
+
inner_object = obj.object
|
825
|
+
if inner_object.key?(@method_sym)
|
826
|
+
inner_object[@method_sym]
|
827
|
+
else
|
828
|
+
inner_object[@method_str]
|
829
|
+
end
|
830
|
+
elsif obj.object.respond_to?(@method_sym)
|
831
|
+
method_to_call = @method_sym
|
832
|
+
method_receiver = obj.object
|
833
|
+
if ruby_kwargs.any?
|
834
|
+
obj.object.public_send(@method_sym, **ruby_kwargs)
|
835
|
+
else
|
836
|
+
obj.object.public_send(@method_sym)
|
837
|
+
end
|
807
838
|
else
|
808
|
-
|
839
|
+
raise <<-ERR
|
840
|
+
Failed to implement #{@owner.graphql_name}.#{@name}, tried:
|
841
|
+
|
842
|
+
- `#{obj.class}##{@resolver_method}`, which did not exist
|
843
|
+
- `#{obj.object.class}##{@method_sym}`, which did not exist
|
844
|
+
- Looking up hash key `#{@method_sym.inspect}` or `#{@method_str.inspect}` on `#{obj.object}`, but it wasn't a Hash
|
845
|
+
|
846
|
+
To implement this field, define one of the methods above (and check for typos)
|
847
|
+
ERR
|
809
848
|
end
|
810
|
-
|
811
|
-
|
812
|
-
|
849
|
+
rescue ArgumentError
|
850
|
+
assert_satisfactory_implementation(method_receiver, method_to_call, ruby_kwargs)
|
851
|
+
# if the line above doesn't raise, re-raise
|
852
|
+
raise
|
853
|
+
end
|
854
|
+
end
|
855
|
+
end
|
856
|
+
|
857
|
+
def assert_satisfactory_implementation(receiver, method_name, ruby_kwargs)
|
858
|
+
method_defn = receiver.method(method_name)
|
859
|
+
unsatisfied_ruby_kwargs = ruby_kwargs.dup
|
860
|
+
unsatisfied_method_params = []
|
861
|
+
encountered_keyrest = false
|
862
|
+
method_defn.parameters.each do |(param_type, param_name)|
|
863
|
+
case param_type
|
864
|
+
when :key
|
865
|
+
unsatisfied_ruby_kwargs.delete(param_name)
|
866
|
+
when :keyreq
|
867
|
+
if unsatisfied_ruby_kwargs.key?(param_name)
|
868
|
+
unsatisfied_ruby_kwargs.delete(param_name)
|
813
869
|
else
|
814
|
-
|
870
|
+
unsatisfied_method_params << "- `#{param_name}:` is required by Ruby, but not by GraphQL. Consider `#{param_name}: nil` instead, or making this argument required in GraphQL."
|
815
871
|
end
|
816
|
-
|
817
|
-
|
818
|
-
|
872
|
+
when :keyrest
|
873
|
+
encountered_keyrest = true
|
874
|
+
when :req
|
875
|
+
unsatisfied_method_params << "- `#{param_name}` is required by Ruby, but GraphQL doesn't pass positional arguments. If it's meant to be a GraphQL argument, use `#{param_name}:` instead. Otherwise, remove it."
|
876
|
+
when :opt, :rest
|
877
|
+
# This is fine, although it will never be present
|
878
|
+
end
|
879
|
+
end
|
880
|
+
|
881
|
+
if encountered_keyrest
|
882
|
+
unsatisfied_ruby_kwargs.clear
|
883
|
+
end
|
819
884
|
|
820
|
-
|
821
|
-
|
822
|
-
|
885
|
+
if unsatisfied_ruby_kwargs.any? || unsatisfied_method_params.any?
|
886
|
+
raise FieldImplementationFailed.new, <<-ERR
|
887
|
+
Failed to call #{method_name} on #{receiver.inspect} because the Ruby method params were incompatible with the GraphQL arguments:
|
823
888
|
|
824
|
-
|
825
|
-
|
826
|
-
|
889
|
+
#{ unsatisfied_ruby_kwargs
|
890
|
+
.map { |key, value| "- `#{key}: #{value}` was given by GraphQL but not defined in the Ruby method. Add `#{key}:` to the method parameters." }
|
891
|
+
.concat(unsatisfied_method_params)
|
892
|
+
.join("\n") }
|
893
|
+
ERR
|
827
894
|
end
|
828
895
|
end
|
829
896
|
|
@@ -837,8 +904,12 @@ module GraphQL
|
|
837
904
|
# This is a hack to get the _last_ value for extended obj and args,
|
838
905
|
# in case one of the extensions doesn't `yield`.
|
839
906
|
# (There's another implementation that uses multiple-return, but I'm wary of the perf cost of the extra arrays)
|
840
|
-
extended = { args: args, obj: obj, memos: nil }
|
907
|
+
extended = { args: args, obj: obj, memos: nil, added_extras: nil }
|
841
908
|
value = run_extensions_before_resolve(obj, args, ctx, extended) do |obj, args|
|
909
|
+
if (added_extras = extended[:added_extras])
|
910
|
+
args = args.dup
|
911
|
+
added_extras.each { |e| args.delete(e) }
|
912
|
+
end
|
842
913
|
yield(obj, args)
|
843
914
|
end
|
844
915
|
|
@@ -867,6 +938,12 @@ module GraphQL
|
|
867
938
|
memos = extended[:memos] ||= {}
|
868
939
|
memos[idx] = memo
|
869
940
|
end
|
941
|
+
|
942
|
+
if (extras = extension.added_extras)
|
943
|
+
ae = extended[:added_extras] ||= []
|
944
|
+
ae.concat(extras)
|
945
|
+
end
|
946
|
+
|
870
947
|
extended[:obj] = extended_obj
|
871
948
|
extended[:args] = extended_args
|
872
949
|
run_extensions_before_resolve(extended_obj, extended_args, ctx, extended, idx: idx + 1) { |o, a| yield(o, a) }
|
@@ -15,23 +15,110 @@ 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
|
-
freeze
|
27
30
|
end
|
28
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
|
52
|
+
|
53
|
+
# If configured, these `extras` will be added to the field if they aren't already present,
|
54
|
+
# but removed by from `arguments` before the field's `resolve` is called.
|
55
|
+
# (The extras _will_ be present for other extensions, though.)
|
56
|
+
#
|
57
|
+
# @param new_extras [Array<Symbol>] If provided, assign extras used by this extension
|
58
|
+
# @return [Array<Symbol>] any extras assigned to this extension
|
59
|
+
def extras(new_extras = nil)
|
60
|
+
if new_extras
|
61
|
+
@own_extras = new_extras
|
62
|
+
end
|
63
|
+
|
64
|
+
inherited_extras = self.superclass.respond_to?(:extras) ? superclass.extras : nil
|
65
|
+
if @own_extras
|
66
|
+
if inherited_extras
|
67
|
+
inherited_extras + @own_extras
|
68
|
+
else
|
69
|
+
@own_extras
|
70
|
+
end
|
71
|
+
elsif inherited_extras
|
72
|
+
inherited_extras
|
73
|
+
else
|
74
|
+
NO_EXTRAS
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
NO_EXTRAS = [].freeze
|
80
|
+
private_constant :NO_EXTRAS
|
81
|
+
|
29
82
|
# Called when this extension is attached to a field.
|
30
83
|
# The field definition may be extended during this method.
|
31
84
|
# @return [void]
|
32
85
|
def apply
|
33
86
|
end
|
34
87
|
|
88
|
+
# Called after the field's definition block has been executed.
|
89
|
+
# (Any arguments from the block are present on `field`)
|
90
|
+
# @return [void]
|
91
|
+
def after_define
|
92
|
+
end
|
93
|
+
|
94
|
+
# @api private
|
95
|
+
def after_define_apply
|
96
|
+
after_define
|
97
|
+
if (configs = self.class.default_argument_configurations)
|
98
|
+
existing_keywords = field.all_argument_definitions.map(&:keyword)
|
99
|
+
existing_keywords.uniq!
|
100
|
+
@added_default_arguments = []
|
101
|
+
configs.each do |config|
|
102
|
+
argument_args, argument_kwargs = config
|
103
|
+
arg_name = argument_args[0]
|
104
|
+
if !existing_keywords.include?(arg_name)
|
105
|
+
@added_default_arguments << arg_name
|
106
|
+
field.argument(*argument_args, **argument_kwargs)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
if (extras = self.class.extras).any?
|
111
|
+
@added_extras = extras - field.extras
|
112
|
+
field.extras(@added_extras)
|
113
|
+
else
|
114
|
+
@added_extras = nil
|
115
|
+
end
|
116
|
+
freeze
|
117
|
+
end
|
118
|
+
|
119
|
+
# @api private
|
120
|
+
attr_reader :added_extras
|
121
|
+
|
35
122
|
# Called before resolving {#field}. It should either:
|
36
123
|
#
|
37
124
|
# - `yield` values to continue execution; OR
|
@@ -79,6 +79,21 @@ module GraphQL
|
|
79
79
|
end
|
80
80
|
end
|
81
81
|
|
82
|
+
def self.authorized?(obj, value, ctx)
|
83
|
+
# Authorize each argument (but this doesn't apply if `prepare` is implemented):
|
84
|
+
if value.is_a?(InputObject)
|
85
|
+
arguments(ctx).each do |_name, input_obj_arg|
|
86
|
+
input_obj_arg = input_obj_arg.type_class
|
87
|
+
if value.key?(input_obj_arg.keyword) &&
|
88
|
+
!input_obj_arg.authorized?(obj, value[input_obj_arg.keyword], ctx)
|
89
|
+
return false
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
# It didn't early-return false:
|
94
|
+
true
|
95
|
+
end
|
96
|
+
|
82
97
|
def unwrap_value(value)
|
83
98
|
case value
|
84
99
|
when Array
|
@@ -132,6 +147,8 @@ module GraphQL
|
|
132
147
|
argument_defn
|
133
148
|
end
|
134
149
|
|
150
|
+
prepend Schema::Member::CachedGraphQLDefinition::DeprecatedToGraphQL
|
151
|
+
|
135
152
|
def to_graphql
|
136
153
|
type_defn = GraphQL::InputObjectType.new
|
137
154
|
type_defn.name = graphql_name
|
@@ -140,7 +157,7 @@ module GraphQL
|
|
140
157
|
type_defn.mutation = mutation
|
141
158
|
type_defn.ast_node = ast_node
|
142
159
|
all_argument_definitions.each do |arg|
|
143
|
-
type_defn.arguments[arg.graphql_definition.name] = arg.graphql_definition # rubocop:disable Development/ContextIsPassedCop -- legacy-related
|
160
|
+
type_defn.arguments[arg.graphql_definition(silence_deprecation_warning: true).name] = arg.graphql_definition(silence_deprecation_warning: true) # rubocop:disable Development/ContextIsPassedCop -- legacy-related
|
144
161
|
end
|
145
162
|
# Make a reference to a classic-style Arguments class
|
146
163
|
self.arguments_class = GraphQL::Query::Arguments.construct_arguments_class(type_defn)
|
@@ -100,6 +100,8 @@ module GraphQL
|
|
100
100
|
end
|
101
101
|
end
|
102
102
|
|
103
|
+
prepend Schema::Member::CachedGraphQLDefinition::DeprecatedToGraphQL
|
104
|
+
|
103
105
|
def to_graphql
|
104
106
|
type_defn = GraphQL::InterfaceType.new
|
105
107
|
type_defn.name = graphql_name
|
@@ -108,7 +110,7 @@ module GraphQL
|
|
108
110
|
type_defn.type_membership_class = self.type_membership_class
|
109
111
|
type_defn.ast_node = ast_node
|
110
112
|
fields.each do |field_name, field_inst| # rubocop:disable Development/ContextIsPassedCop -- legacy-related
|
111
|
-
field_defn = field_inst.graphql_definition
|
113
|
+
field_defn = field_inst.graphql_definition(silence_deprecation_warning: true)
|
112
114
|
type_defn.fields[field_defn.name] = field_defn # rubocop:disable Development/ContextIsPassedCop -- legacy-related
|
113
115
|
end
|
114
116
|
type_defn.metadata[:type_class] = self
|
@@ -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]
|
@@ -123,8 +123,13 @@ module GraphQL
|
|
123
123
|
end
|
124
124
|
|
125
125
|
module ToGraphQLExtension
|
126
|
-
def to_graphql
|
127
|
-
|
126
|
+
def to_graphql(*args, **kwargs)
|
127
|
+
|
128
|
+
defn = if args.empty? && kwargs.empty?
|
129
|
+
super()
|
130
|
+
else
|
131
|
+
super
|
132
|
+
end
|
128
133
|
accepts_definition_methods.each do |method_name|
|
129
134
|
value = public_send(method_name)
|
130
135
|
if !value.nil?
|
@@ -11,8 +11,24 @@ module GraphQL
|
|
11
11
|
# A cached result of {.to_graphql}.
|
12
12
|
# It's cached here so that user-overridden {.to_graphql} implementations
|
13
13
|
# are also cached
|
14
|
-
def graphql_definition
|
15
|
-
@graphql_definition ||=
|
14
|
+
def graphql_definition(silence_deprecation_warning: false)
|
15
|
+
@graphql_definition ||= begin
|
16
|
+
unless silence_deprecation_warning
|
17
|
+
message = "Legacy `.graphql_definition` objects are deprecated and will be removed in GraphQL-Ruby 2.0. Remove `.graphql_definition` to use a class-based definition instead."
|
18
|
+
caller_message = "\n\nCalled on #{self.inspect} from:\n #{caller(1, 25).map { |l| " #{l}" }.join("\n")}"
|
19
|
+
GraphQL::Deprecation.warn(message + caller_message)
|
20
|
+
end
|
21
|
+
deprecated_to_graphql
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def deprecated_to_graphql
|
26
|
+
case method(:to_graphql).arity
|
27
|
+
when 0
|
28
|
+
to_graphql
|
29
|
+
else
|
30
|
+
to_graphql(silence_deprecation_warning: true)
|
31
|
+
end
|
16
32
|
end
|
17
33
|
|
18
34
|
# This is for a common interface with .define-based types
|
@@ -25,6 +41,17 @@ module GraphQL
|
|
25
41
|
super
|
26
42
|
@graphql_definition = nil
|
27
43
|
end
|
44
|
+
|
45
|
+
module DeprecatedToGraphQL
|
46
|
+
def to_graphql(silence_deprecation_warning: false)
|
47
|
+
unless silence_deprecation_warning
|
48
|
+
message = "Legacy `.to_graphql` objects are deprecated and will be removed in GraphQL-Ruby 2.0. Remove `.to_graphql` to use a class-based definition instead."
|
49
|
+
caller_message = "\n\nCalled on #{self.inspect} from:\n #{caller(1, 25).map { |l| " #{l}" }.join("\n")}"
|
50
|
+
GraphQL::Deprecation.warn(message + caller_message)
|
51
|
+
end
|
52
|
+
super()
|
53
|
+
end
|
54
|
+
end
|
28
55
|
end
|
29
56
|
end
|
30
57
|
end
|
@@ -8,8 +8,10 @@ module GraphQL
|
|
8
8
|
class NonNull < 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_non_null_type
|
14
|
+
@of_type.graphql_definition(silence_deprecation_warning: true).to_non_null_type
|
13
15
|
end
|
14
16
|
|
15
17
|
# @return [GraphQL::TypeKinds::NON_NULL]
|
@@ -51,6 +53,10 @@ module GraphQL
|
|
51
53
|
end
|
52
54
|
|
53
55
|
def coerce_input(value, ctx)
|
56
|
+
# `.validate_input` above is used for variables, but this method is used for arguments
|
57
|
+
if value.nil?
|
58
|
+
raise GraphQL::ExecutionError, "`null` is not a valid input for `#{to_type_signature}`, please provide a value for this argument."
|
59
|
+
end
|
54
60
|
of_type.coerce_input(value, ctx)
|
55
61
|
end
|
56
62
|
|
@@ -122,6 +122,8 @@ module GraphQL
|
|
122
122
|
all_fields
|
123
123
|
end
|
124
124
|
|
125
|
+
prepend Schema::Member::CachedGraphQLDefinition::DeprecatedToGraphQL
|
126
|
+
|
125
127
|
# @return [GraphQL::ObjectType]
|
126
128
|
def to_graphql
|
127
129
|
obj_type = GraphQL::ObjectType.new
|
@@ -132,7 +134,7 @@ module GraphQL
|
|
132
134
|
obj_type.mutation = mutation
|
133
135
|
obj_type.ast_node = ast_node
|
134
136
|
fields.each do |field_name, field_inst| # rubocop:disable Development/ContextIsPassedCop -- legacy-related
|
135
|
-
field_defn = field_inst.to_graphql
|
137
|
+
field_defn = field_inst.to_graphql(silence_deprecation_warning: true)
|
136
138
|
obj_type.fields[field_defn.name] = field_defn # rubocop:disable Development/ContextIsPassedCop -- legacy-related
|
137
139
|
end
|
138
140
|
|
@@ -155,6 +155,14 @@ module GraphQL
|
|
155
155
|
end
|
156
156
|
end
|
157
157
|
end
|
158
|
+
|
159
|
+
private
|
160
|
+
|
161
|
+
def authorize_arguments(args, values)
|
162
|
+
# remove the `input` wrapper to match values
|
163
|
+
input_args = args["input"].type.unwrap.arguments(context)
|
164
|
+
super(input_args, values)
|
165
|
+
end
|
158
166
|
end
|
159
167
|
end
|
160
168
|
end
|
@@ -145,19 +145,9 @@ module GraphQL
|
|
145
145
|
# @raise [GraphQL::UnauthorizedError] To signal an authorization failure
|
146
146
|
# @return [Boolean, early_return_data] If `false`, execution will stop (and `early_return_data` will be returned instead, if present.)
|
147
147
|
def authorized?(**inputs)
|
148
|
-
self.class
|
149
|
-
|
150
|
-
|
151
|
-
arg_auth, err = argument.authorized?(self, arg_value, context)
|
152
|
-
if !arg_auth
|
153
|
-
return arg_auth, err
|
154
|
-
else
|
155
|
-
true
|
156
|
-
end
|
157
|
-
else
|
158
|
-
true
|
159
|
-
end
|
160
|
-
end
|
148
|
+
arg_owner = @field # || self.class
|
149
|
+
args = arg_owner.arguments(context)
|
150
|
+
authorize_arguments(args, inputs)
|
161
151
|
end
|
162
152
|
|
163
153
|
# Called when an object loaded by `loads:` fails the `.authorized?` check for its resolved GraphQL object type.
|
@@ -172,6 +162,22 @@ module GraphQL
|
|
172
162
|
|
173
163
|
private
|
174
164
|
|
165
|
+
def authorize_arguments(args, inputs)
|
166
|
+
args.each_value do |argument|
|
167
|
+
arg_keyword = argument.keyword
|
168
|
+
if inputs.key?(arg_keyword) && !(arg_value = inputs[arg_keyword]).nil? && (arg_value != argument.default_value)
|
169
|
+
arg_auth, err = argument.authorized?(self, arg_value, context)
|
170
|
+
if !arg_auth
|
171
|
+
return arg_auth, err
|
172
|
+
else
|
173
|
+
true
|
174
|
+
end
|
175
|
+
else
|
176
|
+
true
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
175
181
|
def load_arguments(args)
|
176
182
|
prepared_args = {}
|
177
183
|
prepare_lazies = []
|