graphql 2.4.3 → 2.4.13
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 +95 -55
- data/lib/graphql/backtrace.rb +1 -19
- data/lib/graphql/current.rb +6 -1
- 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/dashboard.css +3 -0
- data/lib/graphql/dashboard/statics/dashboard.js +78 -0
- data/lib/graphql/dashboard/statics/header-icon.png +0 -0
- data/lib/graphql/dashboard/statics/icon.png +0 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/landings/show.html.erb +18 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/traces/index.html.erb +63 -0
- data/lib/graphql/dashboard/views/layouts/graphql/dashboard/application.html.erb +60 -0
- data/lib/graphql/dashboard.rb +142 -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 +11 -4
- data/lib/graphql/execution/interpreter/runtime.rb +67 -40
- data/lib/graphql/execution/interpreter.rb +16 -6
- data/lib/graphql/execution/multiplex.rb +0 -4
- 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 +2 -2
- 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 +20 -22
- data/lib/graphql/railtie.rb +7 -0
- data/lib/graphql/schema/addition.rb +1 -1
- data/lib/graphql/schema/argument.rb +3 -5
- data/lib/graphql/schema/build_from_definition.rb +8 -7
- data/lib/graphql/schema/directive/flagged.rb +1 -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 +12 -12
- 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 +70 -34
- 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_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 +11 -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 +69 -237
- 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 +90 -43
- 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/required_arguments_are_present.rb +1 -1
- 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 +2 -2
- data/lib/graphql/tracing/active_support_notifications_trace.rb +7 -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 +2 -0
- data/lib/graphql/tracing/appsignal_trace.rb +12 -0
- 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 +11 -0
- 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/new_relic_trace.rb +164 -41
- data/lib/graphql/tracing/new_relic_tracing.rb +2 -0
- data/lib/graphql/tracing/notifications_trace.rb +4 -0
- 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 +737 -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 +31 -0
- data/lib/graphql/tracing/prometheus_tracing.rb +2 -0
- data/lib/graphql/tracing/scout_trace.rb +11 -0
- data/lib/graphql/tracing/scout_tracing.rb +2 -0
- data/lib/graphql/tracing/sentry_trace.rb +11 -0
- data/lib/graphql/tracing/statsd_trace.rb +15 -0
- data/lib/graphql/tracing/statsd_tracing.rb +2 -0
- data/lib/graphql/tracing/trace.rb +128 -1
- data/lib/graphql/tracing.rb +30 -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 +152 -10
- 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/schema/field.rb
CHANGED
@@ -42,8 +42,8 @@ module GraphQL
|
|
42
42
|
end
|
43
43
|
|
44
44
|
def directives
|
45
|
-
if @resolver_class && (r_dirs = @resolver_class.directives).
|
46
|
-
if (own_dirs = super).
|
45
|
+
if @resolver_class && !(r_dirs = @resolver_class.directives).empty?
|
46
|
+
if !(own_dirs = super).empty?
|
47
47
|
own_dirs + r_dirs
|
48
48
|
else
|
49
49
|
r_dirs
|
@@ -81,7 +81,7 @@ module GraphQL
|
|
81
81
|
end
|
82
82
|
|
83
83
|
def inspect
|
84
|
-
"#<#{self.class} #{path}#{all_argument_definitions.
|
84
|
+
"#<#{self.class} #{path}#{!all_argument_definitions.empty? ? "(...)" : ""}: #{type.to_type_signature}>"
|
85
85
|
end
|
86
86
|
|
87
87
|
alias :mutation :resolver
|
@@ -255,7 +255,7 @@ module GraphQL
|
|
255
255
|
|
256
256
|
@underscored_name = -Member::BuildType.underscore(name_s)
|
257
257
|
@name = -(camelize ? Member::BuildType.camelize(name_s) : name_s)
|
258
|
-
|
258
|
+
NameValidator.validate!(@name)
|
259
259
|
@description = description
|
260
260
|
@comment = comment
|
261
261
|
@type = @owner_type = @own_validators = @own_directives = @own_arguments = @arguments_statically_coercible = nil # these will be prepared later if necessary
|
@@ -335,15 +335,15 @@ module GraphQL
|
|
335
335
|
@call_after_define = false
|
336
336
|
set_pagination_extensions(connection_extension: connection_extension)
|
337
337
|
# Do this last so we have as much context as possible when initializing them:
|
338
|
-
if extensions.
|
338
|
+
if !extensions.empty?
|
339
339
|
self.extensions(extensions)
|
340
340
|
end
|
341
341
|
|
342
|
-
if resolver_class && resolver_class.extensions.
|
342
|
+
if resolver_class && !resolver_class.extensions.empty?
|
343
343
|
self.extensions(resolver_class.extensions)
|
344
344
|
end
|
345
345
|
|
346
|
-
if directives.
|
346
|
+
if !directives.empty?
|
347
347
|
directives.each do |(dir_class, options)|
|
348
348
|
self.directive(dir_class, **options)
|
349
349
|
end
|
@@ -369,7 +369,7 @@ module GraphQL
|
|
369
369
|
if @definition_block.arity == 1
|
370
370
|
@definition_block.call(self)
|
371
371
|
else
|
372
|
-
|
372
|
+
instance_exec(self, &@definition_block)
|
373
373
|
end
|
374
374
|
self.extensions.each(&:after_define_apply)
|
375
375
|
@call_after_define = true
|
@@ -482,7 +482,7 @@ module GraphQL
|
|
482
482
|
if new_extras.nil?
|
483
483
|
# Read the value
|
484
484
|
field_extras = @extras
|
485
|
-
if @resolver_class &&
|
485
|
+
if @resolver_class && !@resolver_class.extras.empty?
|
486
486
|
field_extras + @resolver_class.extras
|
487
487
|
else
|
488
488
|
field_extras
|
@@ -732,7 +732,7 @@ module GraphQL
|
|
732
732
|
method_to_call = resolver_method
|
733
733
|
method_receiver = obj
|
734
734
|
# Call the method with kwargs, if there are any
|
735
|
-
if ruby_kwargs.
|
735
|
+
if !ruby_kwargs.empty?
|
736
736
|
obj.public_send(resolver_method, **ruby_kwargs)
|
737
737
|
else
|
738
738
|
obj.public_send(resolver_method)
|
@@ -752,7 +752,7 @@ module GraphQL
|
|
752
752
|
elsif inner_object.respond_to?(@method_sym)
|
753
753
|
method_to_call = @method_sym
|
754
754
|
method_receiver = obj.object
|
755
|
-
if ruby_kwargs.
|
755
|
+
if !ruby_kwargs.empty?
|
756
756
|
inner_object.public_send(@method_sym, **ruby_kwargs)
|
757
757
|
else
|
758
758
|
inner_object.public_send(@method_sym)
|
@@ -839,7 +839,7 @@ module GraphQL
|
|
839
839
|
unsatisfied_ruby_kwargs.clear
|
840
840
|
end
|
841
841
|
|
842
|
-
if unsatisfied_ruby_kwargs.
|
842
|
+
if !unsatisfied_ruby_kwargs.empty? || !unsatisfied_method_params.empty?
|
843
843
|
raise FieldImplementationFailed.new, <<-ERR
|
844
844
|
Failed to call `#{method_name.inspect}` on #{receiver.inspect} because the Ruby method params were incompatible with the GraphQL arguments:
|
845
845
|
|
@@ -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
|
@@ -45,6 +53,16 @@ module GraphQL
|
|
45
53
|
to_h
|
46
54
|
end
|
47
55
|
|
56
|
+
def deconstruct_keys(keys = nil)
|
57
|
+
if keys.nil?
|
58
|
+
@ruby_style_hash
|
59
|
+
else
|
60
|
+
new_h = {}
|
61
|
+
keys.each { |k| @ruby_style_hash.key?(k) && new_h[k] = @ruby_style_hash[k] }
|
62
|
+
new_h
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
48
66
|
def prepare
|
49
67
|
if @context
|
50
68
|
object = @context[:current_object]
|
@@ -56,33 +74,6 @@ module GraphQL
|
|
56
74
|
end
|
57
75
|
end
|
58
76
|
|
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
|
68
|
-
end
|
69
|
-
# It didn't early-return false:
|
70
|
-
true
|
71
|
-
end
|
72
|
-
|
73
|
-
def self.one_of
|
74
|
-
if !one_of?
|
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
|
84
|
-
end
|
85
|
-
|
86
77
|
def unwrap_value(value)
|
87
78
|
case value
|
88
79
|
when Array
|
@@ -121,6 +112,33 @@ module GraphQL
|
|
121
112
|
end
|
122
113
|
|
123
114
|
class << self
|
115
|
+
def authorized?(obj, value, ctx)
|
116
|
+
# Authorize each argument (but this doesn't apply if `prepare` is implemented):
|
117
|
+
if value.respond_to?(:key?)
|
118
|
+
ctx.types.arguments(self).each do |input_obj_arg|
|
119
|
+
if value.key?(input_obj_arg.keyword) &&
|
120
|
+
!input_obj_arg.authorized?(obj, value[input_obj_arg.keyword], ctx)
|
121
|
+
return false
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
# It didn't early-return false:
|
126
|
+
true
|
127
|
+
end
|
128
|
+
|
129
|
+
def one_of
|
130
|
+
if !one_of?
|
131
|
+
if all_argument_definitions.any? { |arg| arg.type.non_null? }
|
132
|
+
raise ArgumentError, "`one_of` may not be used with required arguments -- add `required: false` to argument definitions to use `one_of`"
|
133
|
+
end
|
134
|
+
directive(GraphQL::Schema::Directive::OneOf)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def one_of?
|
139
|
+
false # Re-defined when `OneOf` is added
|
140
|
+
end
|
141
|
+
|
124
142
|
def argument(*args, **kwargs, &block)
|
125
143
|
argument_defn = super(*args, **kwargs, &block)
|
126
144
|
if one_of?
|
@@ -132,14 +150,8 @@ module GraphQL
|
|
132
150
|
end
|
133
151
|
end
|
134
152
|
# Add a method access
|
135
|
-
method_name = argument_defn.keyword
|
136
153
|
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
|
154
|
+
define_accessor_method(argument_defn.keyword)
|
143
155
|
end
|
144
156
|
argument_defn
|
145
157
|
end
|
@@ -246,6 +258,25 @@ module GraphQL
|
|
246
258
|
result
|
247
259
|
end
|
248
260
|
|
261
|
+
# @param new_has_no_arguments [Boolean] Call with `true` to make this InputObject type ignore the requirement to have any defined arguments.
|
262
|
+
# @return [void]
|
263
|
+
def has_no_arguments(new_has_no_arguments)
|
264
|
+
@has_no_arguments = new_has_no_arguments
|
265
|
+
nil
|
266
|
+
end
|
267
|
+
|
268
|
+
# @return [Boolean] `true` if `has_no_arguments(true)` was configued
|
269
|
+
def has_no_arguments?
|
270
|
+
@has_no_arguments
|
271
|
+
end
|
272
|
+
|
273
|
+
def arguments(context = GraphQL::Query::NullContext.instance, require_defined_arguments = true)
|
274
|
+
if require_defined_arguments && !has_no_arguments? && !any_arguments?
|
275
|
+
warn(GraphQL::Schema::InputObject::ArgumentsAreRequiredError.new(self).message + "\n\nThis will raise an error in a future GraphQL-Ruby version.")
|
276
|
+
end
|
277
|
+
super(context, false)
|
278
|
+
end
|
279
|
+
|
249
280
|
private
|
250
281
|
|
251
282
|
# Suppress redefinition warning for objectId arguments
|
@@ -256,6 +287,11 @@ module GraphQL
|
|
256
287
|
ensure
|
257
288
|
$VERBOSE = verbose
|
258
289
|
end
|
290
|
+
|
291
|
+
def define_accessor_method(method_name)
|
292
|
+
define_method(method_name) { self[method_name] }
|
293
|
+
alias_method(method_name, method_name)
|
294
|
+
end
|
259
295
|
end
|
260
296
|
|
261
297
|
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
|
@@ -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
|
@@ -2,6 +2,7 @@
|
|
2
2
|
require 'graphql/schema/member/base_dsl_methods'
|
3
3
|
require 'graphql/schema/member/graphql_type_names'
|
4
4
|
require 'graphql/schema/member/has_ast_node'
|
5
|
+
require 'graphql/schema/member/has_dataloader'
|
5
6
|
require 'graphql/schema/member/has_directives'
|
6
7
|
require 'graphql/schema/member/has_deprecation_reason'
|
7
8
|
require 'graphql/schema/member/has_interfaces'
|