graphql 2.4.3 → 2.5.0
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 +11 -4
- data/lib/graphql/execution/interpreter/runtime.rb +78 -41
- 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 +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 +76 -39
- 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 +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 +285 -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 +63 -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
|
@@ -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,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
|
|
@@ -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'
|
|
@@ -7,6 +7,15 @@ module GraphQL
|
|
|
7
7
|
class Object < GraphQL::Schema::Member
|
|
8
8
|
extend GraphQL::Schema::Member::HasFields
|
|
9
9
|
extend GraphQL::Schema::Member::HasInterfaces
|
|
10
|
+
include Member::HasDataloader
|
|
11
|
+
|
|
12
|
+
# Raised when an Object doesn't have any field defined and hasn't explicitly opted out of this requirement
|
|
13
|
+
class FieldsAreRequiredError < GraphQL::Error
|
|
14
|
+
def initialize(object_type)
|
|
15
|
+
message = "Object types must have fields, but #{object_type.graphql_name} doesn't have any. Define a field for this type, remove it from your schema, or add `has_no_fields(true)` to its definition."
|
|
16
|
+
super(message)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
10
19
|
|
|
11
20
|
# @return [Object] the application object this type is wrapping
|
|
12
21
|
attr_reader :object
|
|
@@ -57,20 +66,28 @@ module GraphQL
|
|
|
57
66
|
# @return [GraphQL::Schema::Object, GraphQL::Execution::Lazy]
|
|
58
67
|
# @raise [GraphQL::UnauthorizedError] if the user-provided hook returns `false`
|
|
59
68
|
def authorized_new(object, context)
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
69
|
+
context.query.current_trace.begin_authorized(self, object, context)
|
|
70
|
+
begin
|
|
71
|
+
maybe_lazy_auth_val = context.query.current_trace.authorized(query: context.query, type: self, object: object) do
|
|
72
|
+
begin
|
|
73
|
+
authorized?(object, context)
|
|
74
|
+
rescue GraphQL::UnauthorizedError => err
|
|
75
|
+
context.schema.unauthorized_object(err)
|
|
76
|
+
rescue StandardError => err
|
|
77
|
+
context.query.handle_or_reraise(err)
|
|
78
|
+
end
|
|
67
79
|
end
|
|
80
|
+
ensure
|
|
81
|
+
context.query.current_trace.end_authorized(self, object, context, maybe_lazy_auth_val)
|
|
68
82
|
end
|
|
69
83
|
|
|
70
84
|
auth_val = if context.schema.lazy?(maybe_lazy_auth_val)
|
|
71
85
|
GraphQL::Execution::Lazy.new do
|
|
86
|
+
context.query.current_trace.begin_authorized(self, object, context)
|
|
72
87
|
context.query.current_trace.authorized_lazy(query: context.query, type: self, object: object) do
|
|
73
|
-
context.schema.sync_lazy(maybe_lazy_auth_val)
|
|
88
|
+
res = context.schema.sync_lazy(maybe_lazy_auth_val)
|
|
89
|
+
context.query.current_trace.end_authorized(self, object, context, res)
|
|
90
|
+
res
|
|
74
91
|
end
|
|
75
92
|
end
|
|
76
93
|
else
|
|
@@ -27,6 +27,7 @@ module GraphQL
|
|
|
27
27
|
include Schema::Member::HasPath
|
|
28
28
|
extend Schema::Member::HasPath
|
|
29
29
|
extend Schema::Member::HasDirectives
|
|
30
|
+
include Schema::Member::HasDataloader
|
|
30
31
|
|
|
31
32
|
# @param object [Object] The application object that this field is being resolved on
|
|
32
33
|
# @param context [GraphQL::Query::Context]
|
|
@@ -49,11 +50,6 @@ module GraphQL
|
|
|
49
50
|
# @return [GraphQL::Query::Context]
|
|
50
51
|
attr_reader :context
|
|
51
52
|
|
|
52
|
-
# @return [GraphQL::Dataloader]
|
|
53
|
-
def dataloader
|
|
54
|
-
context.dataloader
|
|
55
|
-
end
|
|
56
|
-
|
|
57
53
|
# @return [GraphQL::Schema::Field]
|
|
58
54
|
attr_reader :field
|
|
59
55
|
|
|
@@ -67,7 +63,7 @@ module GraphQL
|
|
|
67
63
|
# @api private
|
|
68
64
|
def resolve_with_support(**args)
|
|
69
65
|
# First call the ready? hook which may raise
|
|
70
|
-
raw_ready_val = if args.
|
|
66
|
+
raw_ready_val = if !args.empty?
|
|
71
67
|
ready?(**args)
|
|
72
68
|
else
|
|
73
69
|
ready?
|
|
@@ -88,7 +84,7 @@ module GraphQL
|
|
|
88
84
|
@prepared_arguments = loaded_args
|
|
89
85
|
Schema::Validator.validate!(self.class.validators, object, context, loaded_args, as: @field)
|
|
90
86
|
# Then call `authorized?`, which may raise or may return a lazy object
|
|
91
|
-
raw_authorized_val = if loaded_args.
|
|
87
|
+
raw_authorized_val = if !loaded_args.empty?
|
|
92
88
|
authorized?(**loaded_args)
|
|
93
89
|
else
|
|
94
90
|
authorized?
|
|
@@ -117,7 +113,7 @@ module GraphQL
|
|
|
117
113
|
|
|
118
114
|
# @api private {GraphQL::Schema::Mutation} uses this to clear the dataloader cache
|
|
119
115
|
def call_resolve(args_hash)
|
|
120
|
-
if args_hash.
|
|
116
|
+
if !args_hash.empty?
|
|
121
117
|
public_send(self.class.resolve_method, **args_hash)
|
|
122
118
|
else
|
|
123
119
|
public_send(self.class.resolve_method)
|
|
@@ -208,7 +204,7 @@ module GraphQL
|
|
|
208
204
|
end
|
|
209
205
|
|
|
210
206
|
# Avoid returning a lazy if none are needed
|
|
211
|
-
if prepare_lazies.
|
|
207
|
+
if !prepare_lazies.empty?
|
|
212
208
|
GraphQL::Execution::Lazy.all(prepare_lazies).then { prepared_args }
|
|
213
209
|
else
|
|
214
210
|
prepared_args
|
|
@@ -394,7 +390,7 @@ module GraphQL
|
|
|
394
390
|
if superclass.respond_to?(:extensions)
|
|
395
391
|
s_exts = superclass.extensions
|
|
396
392
|
if own_exts
|
|
397
|
-
if s_exts.
|
|
393
|
+
if !s_exts.empty?
|
|
398
394
|
own_exts + s_exts
|
|
399
395
|
else
|
|
400
396
|
own_exts
|
|
@@ -407,6 +403,11 @@ module GraphQL
|
|
|
407
403
|
end
|
|
408
404
|
end
|
|
409
405
|
|
|
406
|
+
def inherited(child_class)
|
|
407
|
+
child_class.description(description)
|
|
408
|
+
super
|
|
409
|
+
end
|
|
410
|
+
|
|
410
411
|
private
|
|
411
412
|
|
|
412
413
|
attr_reader :own_extensions
|