graphql 2.4.3 → 2.5.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.
- checksums.yaml +4 -4
- data/lib/graphql/analysis/analyzer.rb +2 -1
- data/lib/graphql/analysis/visitor.rb +38 -41
- data/lib/graphql/analysis.rb +15 -12
- data/lib/graphql/autoload.rb +38 -0
- data/lib/graphql/backtrace/table.rb +118 -55
- data/lib/graphql/backtrace.rb +1 -19
- data/lib/graphql/current.rb +6 -1
- data/lib/graphql/dashboard/detailed_traces.rb +47 -0
- data/lib/graphql/dashboard/installable.rb +22 -0
- data/lib/graphql/dashboard/limiters.rb +93 -0
- data/lib/graphql/dashboard/operation_store.rb +199 -0
- data/lib/graphql/dashboard/statics/bootstrap-5.3.3.min.css +6 -0
- data/lib/graphql/dashboard/statics/bootstrap-5.3.3.min.js +7 -0
- data/lib/graphql/dashboard/statics/charts.min.css +1 -0
- data/lib/graphql/dashboard/statics/dashboard.css +30 -0
- data/lib/graphql/dashboard/statics/dashboard.js +143 -0
- data/lib/graphql/dashboard/statics/header-icon.png +0 -0
- data/lib/graphql/dashboard/statics/icon.png +0 -0
- data/lib/graphql/dashboard/subscriptions.rb +96 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/detailed_traces/traces/index.html.erb +45 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/landings/show.html.erb +18 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/limiters/limiters/show.html.erb +62 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/not_installed.html.erb +18 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/_form.html.erb +23 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/edit.html.erb +21 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/index.html.erb +69 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/new.html.erb +7 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/index_entries/index.html.erb +39 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/index_entries/show.html.erb +32 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/operations/index.html.erb +81 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/operations/show.html.erb +71 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/subscriptions/show.html.erb +41 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/topics/index.html.erb +55 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/topics/show.html.erb +40 -0
- data/lib/graphql/dashboard/views/layouts/graphql/dashboard/application.html.erb +108 -0
- data/lib/graphql/dashboard.rb +158 -0
- data/lib/graphql/dataloader/active_record_association_source.rb +64 -0
- data/lib/graphql/dataloader/active_record_source.rb +26 -0
- data/lib/graphql/dataloader/async_dataloader.rb +21 -9
- data/lib/graphql/dataloader/null_dataloader.rb +1 -1
- data/lib/graphql/dataloader/source.rb +3 -3
- data/lib/graphql/dataloader.rb +43 -14
- data/lib/graphql/execution/interpreter/resolve.rb +3 -3
- data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +34 -4
- data/lib/graphql/execution/interpreter/runtime.rb +94 -51
- data/lib/graphql/execution/interpreter.rb +16 -7
- data/lib/graphql/execution/multiplex.rb +1 -5
- data/lib/graphql/introspection/directive_location_enum.rb +1 -1
- data/lib/graphql/invalid_name_error.rb +1 -1
- data/lib/graphql/invalid_null_error.rb +5 -15
- data/lib/graphql/language/cache.rb +13 -0
- data/lib/graphql/language/document_from_schema_definition.rb +8 -7
- data/lib/graphql/language/lexer.rb +11 -4
- data/lib/graphql/language/nodes.rb +3 -0
- data/lib/graphql/language/parser.rb +15 -8
- data/lib/graphql/language/printer.rb +8 -8
- data/lib/graphql/language/static_visitor.rb +37 -33
- data/lib/graphql/language/visitor.rb +59 -55
- data/lib/graphql/pagination/connection.rb +1 -1
- data/lib/graphql/query/context/scoped_context.rb +1 -1
- data/lib/graphql/query/context.rb +6 -5
- data/lib/graphql/query/variable_validation_error.rb +1 -1
- data/lib/graphql/query.rb +19 -23
- data/lib/graphql/railtie.rb +7 -0
- data/lib/graphql/schema/addition.rb +1 -1
- data/lib/graphql/schema/argument.rb +7 -8
- data/lib/graphql/schema/build_from_definition.rb +99 -53
- data/lib/graphql/schema/directive/flagged.rb +3 -1
- data/lib/graphql/schema/directive.rb +2 -2
- data/lib/graphql/schema/enum.rb +36 -1
- data/lib/graphql/schema/enum_value.rb +1 -1
- data/lib/graphql/schema/field/scope_extension.rb +1 -1
- data/lib/graphql/schema/field.rb +27 -13
- data/lib/graphql/schema/field_extension.rb +1 -1
- data/lib/graphql/schema/has_single_input_argument.rb +3 -1
- data/lib/graphql/schema/input_object.rb +77 -40
- data/lib/graphql/schema/interface.rb +3 -2
- data/lib/graphql/schema/loader.rb +1 -1
- data/lib/graphql/schema/member/has_arguments.rb +25 -17
- data/lib/graphql/schema/member/has_dataloader.rb +60 -0
- data/lib/graphql/schema/member/has_deprecation_reason.rb +15 -0
- data/lib/graphql/schema/member/has_directives.rb +4 -4
- data/lib/graphql/schema/member/has_fields.rb +19 -1
- data/lib/graphql/schema/member/has_interfaces.rb +5 -5
- data/lib/graphql/schema/member/has_validators.rb +1 -1
- data/lib/graphql/schema/member/scoped.rb +1 -1
- data/lib/graphql/schema/member/type_system_helpers.rb +1 -1
- data/lib/graphql/schema/member.rb +1 -0
- data/lib/graphql/schema/object.rb +25 -8
- data/lib/graphql/schema/relay_classic_mutation.rb +0 -1
- data/lib/graphql/schema/resolver.rb +12 -10
- data/lib/graphql/schema/subscription.rb +52 -6
- data/lib/graphql/schema/union.rb +1 -1
- data/lib/graphql/schema/validator/required_validator.rb +23 -6
- data/lib/graphql/schema/validator.rb +1 -1
- data/lib/graphql/schema/visibility/migration.rb +1 -0
- data/lib/graphql/schema/visibility/profile.rb +95 -243
- data/lib/graphql/schema/visibility/visit.rb +190 -0
- data/lib/graphql/schema/visibility.rb +169 -28
- data/lib/graphql/schema/warden.rb +18 -5
- data/lib/graphql/schema.rb +93 -44
- data/lib/graphql/static_validation/all_rules.rb +1 -1
- data/lib/graphql/static_validation/rules/argument_names_are_unique.rb +1 -1
- data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +1 -1
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +1 -1
- data/lib/graphql/static_validation/rules/no_definitions_are_present.rb +1 -1
- data/lib/graphql/static_validation/rules/not_single_subscription_error.rb +25 -0
- data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +1 -1
- data/lib/graphql/static_validation/rules/subscription_root_exists_and_single_subscription_selection.rb +26 -0
- data/lib/graphql/static_validation/rules/unique_directives_per_location.rb +1 -1
- data/lib/graphql/static_validation/rules/variable_names_are_unique.rb +1 -1
- data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +1 -1
- data/lib/graphql/static_validation/validation_context.rb +1 -0
- data/lib/graphql/static_validation/validator.rb +6 -1
- data/lib/graphql/subscriptions/action_cable_subscriptions.rb +1 -1
- data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +12 -10
- data/lib/graphql/subscriptions/event.rb +12 -1
- data/lib/graphql/subscriptions/serialize.rb +1 -1
- data/lib/graphql/subscriptions.rb +1 -1
- data/lib/graphql/testing/helpers.rb +7 -4
- data/lib/graphql/tracing/active_support_notifications_trace.rb +14 -3
- data/lib/graphql/tracing/active_support_notifications_tracing.rb +1 -1
- data/lib/graphql/tracing/appoptics_trace.rb +9 -1
- data/lib/graphql/tracing/appoptics_tracing.rb +7 -0
- data/lib/graphql/tracing/appsignal_trace.rb +32 -55
- data/lib/graphql/tracing/appsignal_tracing.rb +2 -0
- data/lib/graphql/tracing/call_legacy_tracers.rb +66 -0
- data/lib/graphql/tracing/data_dog_trace.rb +46 -158
- data/lib/graphql/tracing/data_dog_tracing.rb +2 -0
- data/lib/graphql/tracing/detailed_trace/memory_backend.rb +60 -0
- data/lib/graphql/tracing/detailed_trace/redis_backend.rb +72 -0
- data/lib/graphql/tracing/detailed_trace.rb +93 -0
- data/lib/graphql/tracing/legacy_hooks_trace.rb +1 -0
- data/lib/graphql/tracing/legacy_trace.rb +4 -61
- data/lib/graphql/tracing/monitor_trace.rb +283 -0
- data/lib/graphql/tracing/new_relic_trace.rb +47 -54
- data/lib/graphql/tracing/new_relic_tracing.rb +2 -0
- data/lib/graphql/tracing/notifications_trace.rb +182 -34
- data/lib/graphql/tracing/notifications_tracing.rb +2 -0
- data/lib/graphql/tracing/null_trace.rb +9 -0
- data/lib/graphql/tracing/perfetto_trace/trace.proto +141 -0
- data/lib/graphql/tracing/perfetto_trace/trace_pb.rb +33 -0
- data/lib/graphql/tracing/perfetto_trace.rb +734 -0
- data/lib/graphql/tracing/platform_trace.rb +5 -0
- data/lib/graphql/tracing/prometheus_trace/graphql_collector.rb +2 -0
- data/lib/graphql/tracing/prometheus_trace.rb +72 -68
- data/lib/graphql/tracing/prometheus_tracing.rb +2 -0
- data/lib/graphql/tracing/scout_trace.rb +32 -55
- data/lib/graphql/tracing/scout_tracing.rb +2 -0
- data/lib/graphql/tracing/sentry_trace.rb +62 -94
- data/lib/graphql/tracing/statsd_trace.rb +33 -41
- data/lib/graphql/tracing/statsd_tracing.rb +2 -0
- data/lib/graphql/tracing/trace.rb +111 -1
- data/lib/graphql/tracing.rb +31 -30
- data/lib/graphql/types/relay/connection_behaviors.rb +3 -3
- data/lib/graphql/types/relay/edge_behaviors.rb +2 -2
- data/lib/graphql/types.rb +18 -11
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +55 -47
- metadata +146 -11
- data/lib/graphql/backtrace/inspect_result.rb +0 -38
- data/lib/graphql/backtrace/trace.rb +0 -93
- data/lib/graphql/backtrace/tracer.rb +0 -80
- data/lib/graphql/schema/null_mask.rb +0 -11
- data/lib/graphql/static_validation/rules/subscription_root_exists.rb +0 -17
data/lib/graphql/schema/field.rb
CHANGED
@@ -41,10 +41,24 @@ module GraphQL
|
|
41
41
|
end
|
42
42
|
end
|
43
43
|
|
44
|
+
# @return [String, nil]
|
45
|
+
def deprecation_reason
|
46
|
+
super || @resolver_class&.deprecation_reason
|
47
|
+
end
|
48
|
+
|
44
49
|
def directives
|
45
|
-
if @resolver_class && (r_dirs = @resolver_class.directives).
|
46
|
-
if (own_dirs = super).
|
47
|
-
|
50
|
+
if @resolver_class && !(r_dirs = @resolver_class.directives).empty?
|
51
|
+
if !(own_dirs = super).empty?
|
52
|
+
new_dirs = own_dirs.dup
|
53
|
+
r_dirs.each do |r_dir|
|
54
|
+
if r_dir.class.repeatable? ||
|
55
|
+
( (r_dir_name = r_dir.graphql_name) &&
|
56
|
+
(!new_dirs.any? { |d| d.graphql_name == r_dir_name })
|
57
|
+
)
|
58
|
+
new_dirs << r_dir
|
59
|
+
end
|
60
|
+
end
|
61
|
+
new_dirs
|
48
62
|
else
|
49
63
|
r_dirs
|
50
64
|
end
|
@@ -81,7 +95,7 @@ module GraphQL
|
|
81
95
|
end
|
82
96
|
|
83
97
|
def inspect
|
84
|
-
"#<#{self.class} #{path}#{all_argument_definitions.
|
98
|
+
"#<#{self.class} #{path}#{!all_argument_definitions.empty? ? "(...)" : ""}: #{type.to_type_signature}>"
|
85
99
|
end
|
86
100
|
|
87
101
|
alias :mutation :resolver
|
@@ -255,7 +269,7 @@ module GraphQL
|
|
255
269
|
|
256
270
|
@underscored_name = -Member::BuildType.underscore(name_s)
|
257
271
|
@name = -(camelize ? Member::BuildType.camelize(name_s) : name_s)
|
258
|
-
|
272
|
+
NameValidator.validate!(@name)
|
259
273
|
@description = description
|
260
274
|
@comment = comment
|
261
275
|
@type = @owner_type = @own_validators = @own_directives = @own_arguments = @arguments_statically_coercible = nil # these will be prepared later if necessary
|
@@ -335,15 +349,15 @@ module GraphQL
|
|
335
349
|
@call_after_define = false
|
336
350
|
set_pagination_extensions(connection_extension: connection_extension)
|
337
351
|
# Do this last so we have as much context as possible when initializing them:
|
338
|
-
if extensions.
|
352
|
+
if !extensions.empty?
|
339
353
|
self.extensions(extensions)
|
340
354
|
end
|
341
355
|
|
342
|
-
if resolver_class && resolver_class.extensions.
|
356
|
+
if resolver_class && !resolver_class.extensions.empty?
|
343
357
|
self.extensions(resolver_class.extensions)
|
344
358
|
end
|
345
359
|
|
346
|
-
if directives.
|
360
|
+
if !directives.empty?
|
347
361
|
directives.each do |(dir_class, options)|
|
348
362
|
self.directive(dir_class, **options)
|
349
363
|
end
|
@@ -369,7 +383,7 @@ module GraphQL
|
|
369
383
|
if @definition_block.arity == 1
|
370
384
|
@definition_block.call(self)
|
371
385
|
else
|
372
|
-
|
386
|
+
instance_exec(self, &@definition_block)
|
373
387
|
end
|
374
388
|
self.extensions.each(&:after_define_apply)
|
375
389
|
@call_after_define = true
|
@@ -482,7 +496,7 @@ module GraphQL
|
|
482
496
|
if new_extras.nil?
|
483
497
|
# Read the value
|
484
498
|
field_extras = @extras
|
485
|
-
if @resolver_class &&
|
499
|
+
if @resolver_class && !@resolver_class.extras.empty?
|
486
500
|
field_extras + @resolver_class.extras
|
487
501
|
else
|
488
502
|
field_extras
|
@@ -732,7 +746,7 @@ module GraphQL
|
|
732
746
|
method_to_call = resolver_method
|
733
747
|
method_receiver = obj
|
734
748
|
# Call the method with kwargs, if there are any
|
735
|
-
if ruby_kwargs.
|
749
|
+
if !ruby_kwargs.empty?
|
736
750
|
obj.public_send(resolver_method, **ruby_kwargs)
|
737
751
|
else
|
738
752
|
obj.public_send(resolver_method)
|
@@ -752,7 +766,7 @@ module GraphQL
|
|
752
766
|
elsif inner_object.respond_to?(@method_sym)
|
753
767
|
method_to_call = @method_sym
|
754
768
|
method_receiver = obj.object
|
755
|
-
if ruby_kwargs.
|
769
|
+
if !ruby_kwargs.empty?
|
756
770
|
inner_object.public_send(@method_sym, **ruby_kwargs)
|
757
771
|
else
|
758
772
|
inner_object.public_send(@method_sym)
|
@@ -839,7 +853,7 @@ module GraphQL
|
|
839
853
|
unsatisfied_ruby_kwargs.clear
|
840
854
|
end
|
841
855
|
|
842
|
-
if unsatisfied_ruby_kwargs.
|
856
|
+
if !unsatisfied_ruby_kwargs.empty? || !unsatisfied_method_params.empty?
|
843
857
|
raise FieldImplementationFailed.new, <<-ERR
|
844
858
|
Failed to call `#{method_name.inspect}` on #{receiver.inspect} because the Ruby method params were incompatible with the GraphQL arguments:
|
845
859
|
|
@@ -32,7 +32,7 @@ module GraphQL
|
|
32
32
|
input_kwargs = {}
|
33
33
|
end
|
34
34
|
|
35
|
-
if input_kwargs.
|
35
|
+
if !input_kwargs.empty?
|
36
36
|
super(**input_kwargs)
|
37
37
|
else
|
38
38
|
super()
|
@@ -136,6 +136,8 @@ module GraphQL
|
|
136
136
|
super || "Autogenerated input type of #{self.mutation.graphql_name}"
|
137
137
|
end
|
138
138
|
end
|
139
|
+
# For compatibility, in case no arguments are defined:
|
140
|
+
has_no_arguments(true)
|
139
141
|
mutation(mutation_class)
|
140
142
|
# these might be inherited:
|
141
143
|
mutation_args.each do |arg|
|
@@ -10,6 +10,14 @@ module GraphQL
|
|
10
10
|
|
11
11
|
include GraphQL::Dig
|
12
12
|
|
13
|
+
# Raised when an InputObject doesn't have any arguments defined and hasn't explicitly opted out of this requirement
|
14
|
+
class ArgumentsAreRequiredError < GraphQL::Error
|
15
|
+
def initialize(input_object_type)
|
16
|
+
message = "Input Object types must have arguments, but #{input_object_type.graphql_name} doesn't have any. Define an argument for this type, remove it from your schema, or add `has_no_arguments(true)` to its definition."
|
17
|
+
super(message)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
13
21
|
# @return [GraphQL::Query::Context] The context for this query
|
14
22
|
attr_reader :context
|
15
23
|
# @return [GraphQL::Execution::Interpereter::Arguments] The underlying arguments instance
|
@@ -30,7 +38,7 @@ module GraphQL
|
|
30
38
|
# Weirdly, procs are applied during coercion, but not methods.
|
31
39
|
# Probably because these methods require a `self`.
|
32
40
|
if arg_defn.prepare.is_a?(Symbol) || context.nil?
|
33
|
-
prepared_value = arg_defn.prepare_value(self, @ruby_style_hash[ruby_kwargs_key])
|
41
|
+
prepared_value = arg_defn.prepare_value(self, @ruby_style_hash[ruby_kwargs_key], context: context)
|
34
42
|
overwrite_argument(ruby_kwargs_key, prepared_value)
|
35
43
|
end
|
36
44
|
end
|
@@ -45,42 +53,18 @@ module GraphQL
|
|
45
53
|
to_h
|
46
54
|
end
|
47
55
|
|
48
|
-
def
|
49
|
-
if
|
50
|
-
|
51
|
-
# Pass this object's class with `as` so that messages are rendered correctly from inherited validators
|
52
|
-
Schema::Validator.validate!(self.class.validators, object, @context, @ruby_style_hash, as: self.class)
|
53
|
-
self
|
56
|
+
def deconstruct_keys(keys = nil)
|
57
|
+
if keys.nil?
|
58
|
+
@ruby_style_hash
|
54
59
|
else
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
def self.authorized?(obj, value, ctx)
|
60
|
-
# Authorize each argument (but this doesn't apply if `prepare` is implemented):
|
61
|
-
if value.respond_to?(:key?)
|
62
|
-
ctx.types.arguments(self).each do |input_obj_arg|
|
63
|
-
if value.key?(input_obj_arg.keyword) &&
|
64
|
-
!input_obj_arg.authorized?(obj, value[input_obj_arg.keyword], ctx)
|
65
|
-
return false
|
66
|
-
end
|
67
|
-
end
|
60
|
+
new_h = {}
|
61
|
+
keys.each { |k| @ruby_style_hash.key?(k) && new_h[k] = @ruby_style_hash[k] }
|
62
|
+
new_h
|
68
63
|
end
|
69
|
-
# It didn't early-return false:
|
70
|
-
true
|
71
64
|
end
|
72
65
|
|
73
|
-
def
|
74
|
-
|
75
|
-
if all_argument_definitions.any? { |arg| arg.type.non_null? }
|
76
|
-
raise ArgumentError, "`one_of` may not be used with required arguments -- add `required: false` to argument definitions to use `one_of`"
|
77
|
-
end
|
78
|
-
directive(GraphQL::Schema::Directive::OneOf)
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
|
-
def self.one_of?
|
83
|
-
false # Re-defined when `OneOf` is added
|
66
|
+
def prepare
|
67
|
+
self
|
84
68
|
end
|
85
69
|
|
86
70
|
def unwrap_value(value)
|
@@ -120,7 +104,42 @@ module GraphQL
|
|
120
104
|
@ruby_style_hash.dup
|
121
105
|
end
|
122
106
|
|
107
|
+
# @api private
|
108
|
+
def validate_for(context)
|
109
|
+
object = context[:current_object]
|
110
|
+
# Pass this object's class with `as` so that messages are rendered correctly from inherited validators
|
111
|
+
Schema::Validator.validate!(self.class.validators, object, context, @ruby_style_hash, as: self.class)
|
112
|
+
nil
|
113
|
+
end
|
114
|
+
|
123
115
|
class << self
|
116
|
+
def authorized?(obj, value, ctx)
|
117
|
+
# Authorize each argument (but this doesn't apply if `prepare` is implemented):
|
118
|
+
if value.respond_to?(:key?)
|
119
|
+
ctx.types.arguments(self).each do |input_obj_arg|
|
120
|
+
if value.key?(input_obj_arg.keyword) &&
|
121
|
+
!input_obj_arg.authorized?(obj, value[input_obj_arg.keyword], ctx)
|
122
|
+
return false
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
# It didn't early-return false:
|
127
|
+
true
|
128
|
+
end
|
129
|
+
|
130
|
+
def one_of
|
131
|
+
if !one_of?
|
132
|
+
if all_argument_definitions.any? { |arg| arg.type.non_null? }
|
133
|
+
raise ArgumentError, "`one_of` may not be used with required arguments -- add `required: false` to argument definitions to use `one_of`"
|
134
|
+
end
|
135
|
+
directive(GraphQL::Schema::Directive::OneOf)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def one_of?
|
140
|
+
false # Re-defined when `OneOf` is added
|
141
|
+
end
|
142
|
+
|
124
143
|
def argument(*args, **kwargs, &block)
|
125
144
|
argument_defn = super(*args, **kwargs, &block)
|
126
145
|
if one_of?
|
@@ -132,14 +151,8 @@ module GraphQL
|
|
132
151
|
end
|
133
152
|
end
|
134
153
|
# Add a method access
|
135
|
-
method_name = argument_defn.keyword
|
136
154
|
suppress_redefinition_warning do
|
137
|
-
|
138
|
-
def #{method_name}
|
139
|
-
self[#{method_name.inspect}]
|
140
|
-
end
|
141
|
-
alias_method :#{method_name}, :#{method_name}
|
142
|
-
RUBY
|
155
|
+
define_accessor_method(argument_defn.keyword)
|
143
156
|
end
|
144
157
|
argument_defn
|
145
158
|
end
|
@@ -246,6 +259,25 @@ module GraphQL
|
|
246
259
|
result
|
247
260
|
end
|
248
261
|
|
262
|
+
# @param new_has_no_arguments [Boolean] Call with `true` to make this InputObject type ignore the requirement to have any defined arguments.
|
263
|
+
# @return [void]
|
264
|
+
def has_no_arguments(new_has_no_arguments)
|
265
|
+
@has_no_arguments = new_has_no_arguments
|
266
|
+
nil
|
267
|
+
end
|
268
|
+
|
269
|
+
# @return [Boolean] `true` if `has_no_arguments(true)` was configued
|
270
|
+
def has_no_arguments?
|
271
|
+
@has_no_arguments
|
272
|
+
end
|
273
|
+
|
274
|
+
def arguments(context = GraphQL::Query::NullContext.instance, require_defined_arguments = true)
|
275
|
+
if require_defined_arguments && !has_no_arguments? && !any_arguments?
|
276
|
+
warn(GraphQL::Schema::InputObject::ArgumentsAreRequiredError.new(self).message + "\n\nThis will raise an error in a future GraphQL-Ruby version.")
|
277
|
+
end
|
278
|
+
super(context, false)
|
279
|
+
end
|
280
|
+
|
249
281
|
private
|
250
282
|
|
251
283
|
# Suppress redefinition warning for objectId arguments
|
@@ -256,6 +288,11 @@ module GraphQL
|
|
256
288
|
ensure
|
257
289
|
$VERBOSE = verbose
|
258
290
|
end
|
291
|
+
|
292
|
+
def define_accessor_method(method_name)
|
293
|
+
define_method(method_name) { self[method_name] }
|
294
|
+
alias_method(method_name, method_name)
|
295
|
+
end
|
259
296
|
end
|
260
297
|
|
261
298
|
private
|
@@ -13,6 +13,7 @@ module GraphQL
|
|
13
13
|
include GraphQL::Schema::Member::Scoped
|
14
14
|
include GraphQL::Schema::Member::HasAstNode
|
15
15
|
include GraphQL::Schema::Member::HasUnresolvedTypeError
|
16
|
+
include GraphQL::Schema::Member::HasDataloader
|
16
17
|
include GraphQL::Schema::Member::HasDirectives
|
17
18
|
include GraphQL::Schema::Member::HasInterfaces
|
18
19
|
|
@@ -29,7 +30,7 @@ module GraphQL
|
|
29
30
|
const_set(:DefinitionMethods, defn_methods_module)
|
30
31
|
extend(self::DefinitionMethods)
|
31
32
|
end
|
32
|
-
self::DefinitionMethods.
|
33
|
+
self::DefinitionMethods.module_exec(&block)
|
33
34
|
end
|
34
35
|
|
35
36
|
# @see {Schema::Warden} hides interfaces without visible implementations
|
@@ -90,7 +91,7 @@ module GraphQL
|
|
90
91
|
# @param types [Class, Module]
|
91
92
|
# @return [Array<Module, Class>] Implementers of this interface, if they're registered
|
92
93
|
def orphan_types(*types)
|
93
|
-
if types.
|
94
|
+
if !types.empty?
|
94
95
|
@orphan_types ||= []
|
95
96
|
@orphan_types.concat(types)
|
96
97
|
else
|
@@ -76,8 +76,8 @@ module GraphQL
|
|
76
76
|
end
|
77
77
|
|
78
78
|
# @return [Hash<String => GraphQL::Schema::Argument] Arguments defined on this thing, keyed by name. Includes inherited definitions
|
79
|
-
def arguments(context = GraphQL::Query::NullContext.instance)
|
80
|
-
if own_arguments.
|
79
|
+
def arguments(context = GraphQL::Query::NullContext.instance, _require_defined_arguments = nil)
|
80
|
+
if !own_arguments.empty?
|
81
81
|
own_arguments_that_apply = {}
|
82
82
|
own_arguments.each do |name, args_entry|
|
83
83
|
if (visible_defn = Warden.visible_entry?(:visible_argument?, args_entry, context))
|
@@ -90,7 +90,7 @@ module GraphQL
|
|
90
90
|
end
|
91
91
|
|
92
92
|
def any_arguments?
|
93
|
-
own_arguments.
|
93
|
+
!own_arguments.empty?
|
94
94
|
end
|
95
95
|
|
96
96
|
module ClassConfigured
|
@@ -100,12 +100,12 @@ module GraphQL
|
|
100
100
|
end
|
101
101
|
|
102
102
|
module InheritedArguments
|
103
|
-
def arguments(context = GraphQL::Query::NullContext.instance)
|
104
|
-
own_arguments = super
|
105
|
-
inherited_arguments = superclass.arguments(context)
|
103
|
+
def arguments(context = GraphQL::Query::NullContext.instance, require_defined_arguments = true)
|
104
|
+
own_arguments = super(context, require_defined_arguments)
|
105
|
+
inherited_arguments = superclass.arguments(context, false)
|
106
106
|
|
107
|
-
if own_arguments.
|
108
|
-
if inherited_arguments.
|
107
|
+
if !own_arguments.empty?
|
108
|
+
if !inherited_arguments.empty?
|
109
109
|
# Local definitions override inherited ones
|
110
110
|
inherited_arguments.merge(own_arguments)
|
111
111
|
else
|
@@ -149,12 +149,12 @@ module GraphQL
|
|
149
149
|
end
|
150
150
|
|
151
151
|
module FieldConfigured
|
152
|
-
def arguments(context = GraphQL::Query::NullContext.instance)
|
152
|
+
def arguments(context = GraphQL::Query::NullContext.instance, _require_defined_arguments = nil)
|
153
153
|
own_arguments = super
|
154
154
|
if @resolver_class
|
155
155
|
inherited_arguments = @resolver_class.field_arguments(context)
|
156
|
-
if own_arguments.
|
157
|
-
if inherited_arguments.
|
156
|
+
if !own_arguments.empty?
|
157
|
+
if !inherited_arguments.empty?
|
158
158
|
inherited_arguments.merge(own_arguments)
|
159
159
|
else
|
160
160
|
own_arguments
|
@@ -198,7 +198,7 @@ module GraphQL
|
|
198
198
|
end
|
199
199
|
|
200
200
|
def all_argument_definitions
|
201
|
-
if own_arguments.
|
201
|
+
if !own_arguments.empty?
|
202
202
|
all_defns = own_arguments.values
|
203
203
|
all_defns.flatten!
|
204
204
|
all_defns
|
@@ -359,7 +359,8 @@ module GraphQL
|
|
359
359
|
if application_object.nil?
|
360
360
|
nil
|
361
361
|
else
|
362
|
-
|
362
|
+
arg_loads_type = argument.loads
|
363
|
+
maybe_lazy_resolve_type = context.schema.resolve_type(arg_loads_type, application_object, context)
|
363
364
|
context.query.after_lazy(maybe_lazy_resolve_type) do |resolve_type_result|
|
364
365
|
if resolve_type_result.is_a?(Array) && resolve_type_result.size == 2
|
365
366
|
application_object_type, application_object = resolve_type_result
|
@@ -368,10 +369,17 @@ module GraphQL
|
|
368
369
|
# application_object is already assigned
|
369
370
|
end
|
370
371
|
|
371
|
-
if
|
372
|
-
|
373
|
-
|
374
|
-
|
372
|
+
passes_possible_types_check = if context.types.loadable?(arg_loads_type, context)
|
373
|
+
if arg_loads_type.kind.union?
|
374
|
+
# This union is used in `loads:` but not otherwise visible to this query
|
375
|
+
context.types.loadable_possible_types(arg_loads_type, context).include?(application_object_type)
|
376
|
+
else
|
377
|
+
true
|
378
|
+
end
|
379
|
+
else
|
380
|
+
context.types.possible_types(arg_loads_type).include?(application_object_type)
|
381
|
+
end
|
382
|
+
if !passes_possible_types_check
|
375
383
|
err = GraphQL::LoadApplicationObjectFailedError.new(context: context, argument: argument, id: id, object: application_object)
|
376
384
|
application_object = load_application_object_failed(err)
|
377
385
|
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GraphQL
|
4
|
+
class Schema
|
5
|
+
class Member
|
6
|
+
module HasDataloader
|
7
|
+
# @return [GraphQL::Dataloader] The dataloader for the currently-running query
|
8
|
+
def dataloader
|
9
|
+
context.dataloader
|
10
|
+
end
|
11
|
+
|
12
|
+
# A shortcut method for loading a key from a source.
|
13
|
+
# Identical to `dataloader.with(source_class, *source_args).load(load_key)`
|
14
|
+
# @param source_class [Class<GraphQL::Dataloader::Source>]
|
15
|
+
# @param source_args [Array<Object>] Any extra parameters defined in `source_class`'s `initialize` method
|
16
|
+
# @param load_key [Object] The key to look up using `def fetch`
|
17
|
+
def dataload(source_class, *source_args, load_key)
|
18
|
+
dataloader.with(source_class, *source_args).load(load_key)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Find an object with ActiveRecord via {Dataloader::ActiveRecordSource}.
|
22
|
+
# @param model [Class<ActiveRecord::Base>]
|
23
|
+
# @param find_by_value [Object] Usually an `id`, might be another value if `find_by:` is also provided
|
24
|
+
# @param find_by [Symbol, String] A column name to look the record up by. (Defaults to the model's primary key.)
|
25
|
+
# @return [ActiveRecord::Base, nil]
|
26
|
+
# @example Finding a record by ID
|
27
|
+
# dataload_record(Post, 5) # Like `Post.find(5)`, but dataloaded
|
28
|
+
# @example Finding a record by another attribute
|
29
|
+
# dataload_record(User, "matz", find_by: :handle) # Like `User.find_by(handle: "matz")`, but dataloaded
|
30
|
+
def dataload_record(model, find_by_value, find_by: nil)
|
31
|
+
source = if find_by
|
32
|
+
dataloader.with(Dataloader::ActiveRecordSource, model, find_by: find_by)
|
33
|
+
else
|
34
|
+
dataloader.with(Dataloader::ActiveRecordSource, model)
|
35
|
+
end
|
36
|
+
|
37
|
+
source.load(find_by_value)
|
38
|
+
end
|
39
|
+
|
40
|
+
# Look up an associated record using a Rails association.
|
41
|
+
# @param association_name [Symbol] A `belongs_to` or `has_one` association. (If a `has_many` association is named here, it will be selected without pagination.)
|
42
|
+
# @param record [ActiveRecord::Base] The object that the association belongs to.
|
43
|
+
# @param scope [ActiveRecord::Relation] A scope to look up the associated record in
|
44
|
+
# @return [ActiveRecord::Base, nil] The associated record, if there is one
|
45
|
+
# @example Looking up a belongs_to on the current object
|
46
|
+
# dataload_association(:parent) # Equivalent to `object.parent`, but dataloaded
|
47
|
+
# @example Looking up an associated record on some other object
|
48
|
+
# dataload_association(:post, comment) # Equivalent to `comment.post`, but dataloaded
|
49
|
+
def dataload_association(record = object, association_name, scope: nil)
|
50
|
+
source = if scope
|
51
|
+
dataloader.with(Dataloader::ActiveRecordAssociationSource, association_name, scope)
|
52
|
+
else
|
53
|
+
dataloader.with(Dataloader::ActiveRecordAssociationSource, association_name)
|
54
|
+
end
|
55
|
+
source.load(record)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -18,6 +18,21 @@ module GraphQL
|
|
18
18
|
directive(GraphQL::Schema::Directive::Deprecated, reason: text)
|
19
19
|
end
|
20
20
|
end
|
21
|
+
|
22
|
+
def self.extended(child_class)
|
23
|
+
super
|
24
|
+
child_class.extend(ClassMethods)
|
25
|
+
end
|
26
|
+
|
27
|
+
module ClassMethods
|
28
|
+
def deprecation_reason(new_reason = NOT_CONFIGURED)
|
29
|
+
if NOT_CONFIGURED.equal?(new_reason)
|
30
|
+
super()
|
31
|
+
else
|
32
|
+
self.deprecation_reason = new_reason
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
21
36
|
end
|
22
37
|
end
|
23
38
|
end
|
@@ -6,7 +6,7 @@ module GraphQL
|
|
6
6
|
module HasDirectives
|
7
7
|
def self.extended(child_cls)
|
8
8
|
super
|
9
|
-
child_cls.
|
9
|
+
child_cls.module_exec { self.own_directives = nil }
|
10
10
|
end
|
11
11
|
|
12
12
|
def inherited(child_cls)
|
@@ -55,14 +55,14 @@ module GraphQL
|
|
55
55
|
else
|
56
56
|
GraphQL::EmptyObjects::EMPTY_ARRAY
|
57
57
|
end
|
58
|
-
if inherited_directives.
|
58
|
+
if !inherited_directives.empty? && directives
|
59
59
|
dirs = []
|
60
60
|
merge_directives(dirs, inherited_directives)
|
61
61
|
merge_directives(dirs, directives)
|
62
62
|
dirs
|
63
63
|
elsif directives
|
64
64
|
directives
|
65
|
-
elsif inherited_directives.
|
65
|
+
elsif !inherited_directives.empty?
|
66
66
|
inherited_directives
|
67
67
|
else
|
68
68
|
GraphQL::EmptyObjects::EMPTY_ARRAY
|
@@ -71,7 +71,7 @@ module GraphQL
|
|
71
71
|
dirs = nil
|
72
72
|
schema_member.ancestors.reverse_each do |ancestor|
|
73
73
|
if ancestor.respond_to?(:own_directives) &&
|
74
|
-
(anc_dirs = ancestor.own_directives).
|
74
|
+
!(anc_dirs = ancestor.own_directives).empty?
|
75
75
|
dirs ||= []
|
76
76
|
merge_directives(dirs, anc_dirs)
|
77
77
|
end
|
@@ -79,6 +79,18 @@ module GraphQL
|
|
79
79
|
end
|
80
80
|
end
|
81
81
|
|
82
|
+
# @param new_has_no_fields [Boolean] Call with `true` to make this Object type ignore the requirement to have any defined fields.
|
83
|
+
# @return [void]
|
84
|
+
def has_no_fields(new_has_no_fields)
|
85
|
+
@has_no_fields = new_has_no_fields
|
86
|
+
nil
|
87
|
+
end
|
88
|
+
|
89
|
+
# @return [Boolean] `true` if `has_no_fields(true)` was configued
|
90
|
+
def has_no_fields?
|
91
|
+
@has_no_fields
|
92
|
+
end
|
93
|
+
|
82
94
|
# @return [Hash<String => GraphQL::Schema::Field, Array<GraphQL::Schema::Field>>] Fields defined on this class _specifically_, not parent classes
|
83
95
|
def own_fields
|
84
96
|
@own_fields ||= {}
|
@@ -155,9 +167,11 @@ module GraphQL
|
|
155
167
|
warden = Warden.from_context(context)
|
156
168
|
# Local overrides take precedence over inherited fields
|
157
169
|
visible_fields = {}
|
170
|
+
had_any_fields_at_all = false
|
158
171
|
for ancestor in ancestors
|
159
172
|
if ancestor.respond_to?(:own_fields) && visible_interface_implementation?(ancestor, context, warden)
|
160
173
|
ancestor.own_fields.each do |field_name, fields_entry|
|
174
|
+
had_any_fields_at_all = true
|
161
175
|
# Choose the most local definition that passes `.visible?` --
|
162
176
|
# stop checking for fields by name once one has been found.
|
163
177
|
if !visible_fields.key?(field_name) && (f = Warden.visible_entry?(:visible_field?, fields_entry, context, warden))
|
@@ -166,6 +180,9 @@ module GraphQL
|
|
166
180
|
end
|
167
181
|
end
|
168
182
|
end
|
183
|
+
if !had_any_fields_at_all && !has_no_fields?
|
184
|
+
warn(GraphQL::Schema::Object::FieldsAreRequiredError.new(self).message + "\n\nThis will raise an error in a future GraphQL-Ruby version.")
|
185
|
+
end
|
169
186
|
visible_fields
|
170
187
|
end
|
171
188
|
end
|
@@ -185,9 +202,10 @@ module GraphQL
|
|
185
202
|
|
186
203
|
def inherited(subclass)
|
187
204
|
super
|
188
|
-
subclass.
|
205
|
+
subclass.class_exec do
|
189
206
|
@own_fields ||= nil
|
190
207
|
@field_class ||= nil
|
208
|
+
@has_no_fields ||= false
|
191
209
|
end
|
192
210
|
end
|
193
211
|
|
@@ -24,7 +24,7 @@ module GraphQL
|
|
24
24
|
implements(next_interface)
|
25
25
|
end
|
26
26
|
elsif int.is_a?(String) || int.is_a?(GraphQL::Schema::LateBoundType)
|
27
|
-
if options.
|
27
|
+
if !options.empty?
|
28
28
|
raise ArgumentError, "`implements(...)` doesn't support options with late-loaded types yet. Remove #{options} and open an issue to request this feature."
|
29
29
|
end
|
30
30
|
new_memberships << int
|
@@ -73,13 +73,13 @@ module GraphQL
|
|
73
73
|
def interfaces(context = GraphQL::Query::NullContext.instance)
|
74
74
|
visible_interfaces = super
|
75
75
|
inherited_interfaces = superclass.interfaces(context)
|
76
|
-
if visible_interfaces.
|
77
|
-
if inherited_interfaces.
|
76
|
+
if !visible_interfaces.empty?
|
77
|
+
if !inherited_interfaces.empty?
|
78
78
|
visible_interfaces.concat(inherited_interfaces)
|
79
79
|
visible_interfaces.uniq!
|
80
80
|
end
|
81
81
|
visible_interfaces
|
82
|
-
elsif inherited_interfaces.
|
82
|
+
elsif !inherited_interfaces.empty?
|
83
83
|
inherited_interfaces
|
84
84
|
else
|
85
85
|
EmptyObjects::EMPTY_ARRAY
|
@@ -133,7 +133,7 @@ module GraphQL
|
|
133
133
|
|
134
134
|
def inherited(subclass)
|
135
135
|
super
|
136
|
-
subclass.
|
136
|
+
subclass.class_exec do
|
137
137
|
@own_interface_type_memberships ||= nil
|
138
138
|
end
|
139
139
|
end
|