graphql 2.4.5 → 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 +37 -40
- data/lib/graphql/analysis.rb +12 -9
- data/lib/graphql/autoload.rb +1 -0
- data/lib/graphql/backtrace/table.rb +118 -55
- data/lib/graphql/backtrace.rb +1 -19
- data/lib/graphql/current.rb +5 -0
- 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 +17 -5
- data/lib/graphql/dataloader/null_dataloader.rb +1 -1
- data/lib/graphql/dataloader/source.rb +2 -2
- data/lib/graphql/dataloader.rb +37 -5
- data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +11 -4
- data/lib/graphql/execution/interpreter/runtime.rb +71 -34
- data/lib/graphql/execution/interpreter.rb +12 -3
- 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/lexer.rb +7 -3
- data/lib/graphql/language/nodes.rb +3 -0
- data/lib/graphql/language/parser.rb +15 -8
- data/lib/graphql/language/static_visitor.rb +37 -33
- data/lib/graphql/language/visitor.rb +59 -55
- data/lib/graphql/query.rb +7 -13
- data/lib/graphql/railtie.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.rb +1 -1
- data/lib/graphql/schema/enum.rb +36 -1
- data/lib/graphql/schema/enum_value.rb +1 -1
- data/lib/graphql/schema/field.rb +2 -2
- data/lib/graphql/schema/input_object.rb +16 -16
- data/lib/graphql/schema/interface.rb +2 -1
- data/lib/graphql/schema/member/has_arguments.rb +13 -5
- data/lib/graphql/schema/member/has_dataloader.rb +60 -0
- data/lib/graphql/schema/member/has_directives.rb +1 -1
- data/lib/graphql/schema/member/has_fields.rb +1 -1
- data/lib/graphql/schema/member/has_interfaces.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 +17 -8
- data/lib/graphql/schema/resolver.rb +6 -5
- data/lib/graphql/schema/subscription.rb +50 -4
- data/lib/graphql/schema/validator/required_validator.rb +23 -6
- data/lib/graphql/schema/visibility/migration.rb +1 -0
- data/lib/graphql/schema/visibility/profile.rb +11 -5
- data/lib/graphql/schema/visibility.rb +14 -9
- data/lib/graphql/schema/warden.rb +14 -1
- data/lib/graphql/schema.rb +63 -35
- data/lib/graphql/static_validation/all_rules.rb +1 -1
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +1 -1
- data/lib/graphql/static_validation/rules/not_single_subscription_error.rb +25 -0
- data/lib/graphql/static_validation/rules/subscription_root_exists_and_single_subscription_selection.rb +26 -0
- data/lib/graphql/static_validation/validator.rb +6 -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/testing/helpers.rb +5 -2
- 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 +1 -1
- data/lib/graphql/types/relay/edge_behaviors.rb +1 -1
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +4 -29
- metadata +144 -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/enum.rb
CHANGED
|
@@ -61,12 +61,19 @@ module GraphQL
|
|
|
61
61
|
# @option kwargs [String] :description, the GraphQL description for this value, present in documentation
|
|
62
62
|
# @option kwargs [String] :comment, the GraphQL comment for this value, present in documentation
|
|
63
63
|
# @option kwargs [::Object] :value the translated Ruby value for this object (defaults to `graphql_name`)
|
|
64
|
+
# @option kwargs [::Object] :value_method, the method name to fetch `graphql_name` (defaults to `graphql_name.downcase`)
|
|
64
65
|
# @option kwargs [String] :deprecation_reason if this object is deprecated, include a message here
|
|
66
|
+
# @param value_method [Symbol, false] A method to generate for this value, or `false` to skip generation
|
|
65
67
|
# @return [void]
|
|
66
68
|
# @see {Schema::EnumValue} which handles these inputs by default
|
|
67
|
-
def value(*args, **kwargs, &block)
|
|
69
|
+
def value(*args, value_method: nil, **kwargs, &block)
|
|
68
70
|
kwargs[:owner] = self
|
|
69
71
|
value = enum_value_class.new(*args, **kwargs, &block)
|
|
72
|
+
|
|
73
|
+
if value_method || (value_methods && value_method != false)
|
|
74
|
+
generate_value_method(value, value_method)
|
|
75
|
+
end
|
|
76
|
+
|
|
70
77
|
key = value.graphql_name
|
|
71
78
|
prev_value = own_values[key]
|
|
72
79
|
case prev_value
|
|
@@ -154,6 +161,18 @@ module GraphQL
|
|
|
154
161
|
end
|
|
155
162
|
end
|
|
156
163
|
|
|
164
|
+
def value_methods(new_value = NOT_CONFIGURED)
|
|
165
|
+
if NOT_CONFIGURED.equal?(new_value)
|
|
166
|
+
if @value_methods != nil
|
|
167
|
+
@value_methods
|
|
168
|
+
else
|
|
169
|
+
find_inherited_value(:value_methods, false)
|
|
170
|
+
end
|
|
171
|
+
else
|
|
172
|
+
@value_methods = new_value
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
|
|
157
176
|
def kind
|
|
158
177
|
GraphQL::TypeKinds::ENUM
|
|
159
178
|
end
|
|
@@ -215,6 +234,7 @@ module GraphQL
|
|
|
215
234
|
# because they would end up with names like `#<Class0x1234>::UnresolvedValueError` which messes up bug trackers
|
|
216
235
|
child_class.const_set(:UnresolvedValueError, Class.new(Schema::Enum::UnresolvedValueError))
|
|
217
236
|
end
|
|
237
|
+
child_class.class_exec { @value_methods = nil }
|
|
218
238
|
super
|
|
219
239
|
end
|
|
220
240
|
|
|
@@ -223,6 +243,21 @@ module GraphQL
|
|
|
223
243
|
def own_values
|
|
224
244
|
@own_values ||= {}
|
|
225
245
|
end
|
|
246
|
+
|
|
247
|
+
def generate_value_method(value, configured_value_method)
|
|
248
|
+
return if configured_value_method == false
|
|
249
|
+
|
|
250
|
+
value_method_name = configured_value_method || value.graphql_name.downcase
|
|
251
|
+
|
|
252
|
+
if respond_to?(value_method_name.to_sym)
|
|
253
|
+
warn "Failed to define value method for :#{value_method_name}, because " \
|
|
254
|
+
"#{value.owner.name || value.owner.graphql_name} already responds to that method. Use `value_method:` to override the method name " \
|
|
255
|
+
"or `value_method: false` to disable Enum value method generation."
|
|
256
|
+
return
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
define_singleton_method(value_method_name) { value.graphql_name }
|
|
260
|
+
end
|
|
226
261
|
end
|
|
227
262
|
|
|
228
263
|
enum_value_class(GraphQL::Schema::EnumValue)
|
data/lib/graphql/schema/field.rb
CHANGED
|
@@ -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
|
|
@@ -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
|
|
@@ -59,19 +59,12 @@ module GraphQL
|
|
|
59
59
|
else
|
|
60
60
|
new_h = {}
|
|
61
61
|
keys.each { |k| @ruby_style_hash.key?(k) && new_h[k] = @ruby_style_hash[k] }
|
|
62
|
-
new_h
|
|
62
|
+
new_h
|
|
63
63
|
end
|
|
64
64
|
end
|
|
65
65
|
|
|
66
66
|
def prepare
|
|
67
|
-
|
|
68
|
-
object = @context[:current_object]
|
|
69
|
-
# Pass this object's class with `as` so that messages are rendered correctly from inherited validators
|
|
70
|
-
Schema::Validator.validate!(self.class.validators, object, @context, @ruby_style_hash, as: self.class)
|
|
71
|
-
self
|
|
72
|
-
else
|
|
73
|
-
self
|
|
74
|
-
end
|
|
67
|
+
self
|
|
75
68
|
end
|
|
76
69
|
|
|
77
70
|
def unwrap_value(value)
|
|
@@ -111,6 +104,14 @@ module GraphQL
|
|
|
111
104
|
@ruby_style_hash.dup
|
|
112
105
|
end
|
|
113
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
|
+
|
|
114
115
|
class << self
|
|
115
116
|
def authorized?(obj, value, ctx)
|
|
116
117
|
# Authorize each argument (but this doesn't apply if `prepare` is implemented):
|
|
@@ -150,14 +151,8 @@ module GraphQL
|
|
|
150
151
|
end
|
|
151
152
|
end
|
|
152
153
|
# Add a method access
|
|
153
|
-
method_name = argument_defn.keyword
|
|
154
154
|
suppress_redefinition_warning do
|
|
155
|
-
|
|
156
|
-
def #{method_name}
|
|
157
|
-
self[#{method_name.inspect}]
|
|
158
|
-
end
|
|
159
|
-
alias_method :#{method_name}, :#{method_name}
|
|
160
|
-
RUBY
|
|
155
|
+
define_accessor_method(argument_defn.keyword)
|
|
161
156
|
end
|
|
162
157
|
argument_defn
|
|
163
158
|
end
|
|
@@ -293,6 +288,11 @@ module GraphQL
|
|
|
293
288
|
ensure
|
|
294
289
|
$VERBOSE = verbose
|
|
295
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
|
|
296
296
|
end
|
|
297
297
|
|
|
298
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
|
|
@@ -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
|
|
@@ -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,7 @@ 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
|
|
10
11
|
|
|
11
12
|
# Raised when an Object doesn't have any field defined and hasn't explicitly opted out of this requirement
|
|
12
13
|
class FieldsAreRequiredError < GraphQL::Error
|
|
@@ -65,20 +66,28 @@ module GraphQL
|
|
|
65
66
|
# @return [GraphQL::Schema::Object, GraphQL::Execution::Lazy]
|
|
66
67
|
# @raise [GraphQL::UnauthorizedError] if the user-provided hook returns `false`
|
|
67
68
|
def authorized_new(object, context)
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
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
|
|
75
79
|
end
|
|
80
|
+
ensure
|
|
81
|
+
context.query.current_trace.end_authorized(self, object, context, maybe_lazy_auth_val)
|
|
76
82
|
end
|
|
77
83
|
|
|
78
84
|
auth_val = if context.schema.lazy?(maybe_lazy_auth_val)
|
|
79
85
|
GraphQL::Execution::Lazy.new do
|
|
86
|
+
context.query.current_trace.begin_authorized(self, object, context)
|
|
80
87
|
context.query.current_trace.authorized_lazy(query: context.query, type: self, object: object) do
|
|
81
|
-
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
|
|
82
91
|
end
|
|
83
92
|
end
|
|
84
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
|
|
|
@@ -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
|
|
@@ -19,13 +19,22 @@ module GraphQL
|
|
|
19
19
|
# propagate null.
|
|
20
20
|
null false
|
|
21
21
|
|
|
22
|
+
# @api private
|
|
22
23
|
def initialize(object:, context:, field:)
|
|
23
24
|
super
|
|
24
25
|
# Figure out whether this is an update or an initial subscription
|
|
25
26
|
@mode = context.query.subscription_update? ? :update : :subscribe
|
|
27
|
+
@subscription_written = false
|
|
28
|
+
@original_arguments = nil
|
|
29
|
+
if (subs_ns = context.namespace(:subscriptions)) &&
|
|
30
|
+
(sub_insts = subs_ns[:subscriptions])
|
|
31
|
+
sub_insts[context.current_path] = self
|
|
32
|
+
end
|
|
26
33
|
end
|
|
27
34
|
|
|
35
|
+
# @api private
|
|
28
36
|
def resolve_with_support(**args)
|
|
37
|
+
@original_arguments = args # before `loads:` have been run
|
|
29
38
|
result = nil
|
|
30
39
|
unsubscribed = true
|
|
31
40
|
unsubscribed_result = catch :graphql_subscription_unsubscribed do
|
|
@@ -46,7 +55,9 @@ module GraphQL
|
|
|
46
55
|
end
|
|
47
56
|
end
|
|
48
57
|
|
|
49
|
-
# Implement the {Resolve} API
|
|
58
|
+
# Implement the {Resolve} API.
|
|
59
|
+
# You can implement this if you want code to run for _both_ the initial subscription
|
|
60
|
+
# and for later updates. Or, implement {#subscribe} and {#update}
|
|
50
61
|
def resolve(**args)
|
|
51
62
|
# Dispatch based on `@mode`, which will raise a `NoMethodError` if we ever
|
|
52
63
|
# have an unexpected `@mode`
|
|
@@ -54,6 +65,7 @@ module GraphQL
|
|
|
54
65
|
end
|
|
55
66
|
|
|
56
67
|
# Wrap the user-defined `#subscribe` hook
|
|
68
|
+
# @api private
|
|
57
69
|
def resolve_subscribe(**args)
|
|
58
70
|
ret_val = !args.empty? ? subscribe(**args) : subscribe
|
|
59
71
|
if ret_val == :no_response
|
|
@@ -71,6 +83,7 @@ module GraphQL
|
|
|
71
83
|
end
|
|
72
84
|
|
|
73
85
|
# Wrap the user-provided `#update` hook
|
|
86
|
+
# @api private
|
|
74
87
|
def resolve_update(**args)
|
|
75
88
|
ret_val = !args.empty? ? update(**args) : update
|
|
76
89
|
if ret_val == NO_UPDATE
|
|
@@ -106,14 +119,13 @@ module GraphQL
|
|
|
106
119
|
throw :graphql_subscription_unsubscribed, update_value
|
|
107
120
|
end
|
|
108
121
|
|
|
109
|
-
READING_SCOPE = ::Object.new
|
|
110
122
|
# Call this method to provide a new subscription_scope; OR
|
|
111
123
|
# call it without an argument to get the subscription_scope
|
|
112
124
|
# @param new_scope [Symbol]
|
|
113
125
|
# @param optional [Boolean] If true, then don't require `scope:` to be provided to updates to this subscription.
|
|
114
126
|
# @return [Symbol]
|
|
115
|
-
def self.subscription_scope(new_scope =
|
|
116
|
-
if new_scope !=
|
|
127
|
+
def self.subscription_scope(new_scope = NOT_CONFIGURED, optional: false)
|
|
128
|
+
if new_scope != NOT_CONFIGURED
|
|
117
129
|
@subscription_scope = new_scope
|
|
118
130
|
@subscription_scope_optional = optional
|
|
119
131
|
elsif defined?(@subscription_scope)
|
|
@@ -150,6 +162,40 @@ module GraphQL
|
|
|
150
162
|
def self.topic_for(arguments:, field:, scope:)
|
|
151
163
|
Subscriptions::Serialize.dump_recursive([scope, field.graphql_name, arguments])
|
|
152
164
|
end
|
|
165
|
+
|
|
166
|
+
# Calls through to `schema.subscriptions` to register this subscription with the backend.
|
|
167
|
+
# This is automatically called by GraphQL-Ruby after a query finishes successfully,
|
|
168
|
+
# but if you need to commit the subscription during `#subscribe`, you can call it there.
|
|
169
|
+
# (This method also sets a flag showing that this subscription was already written.)
|
|
170
|
+
#
|
|
171
|
+
# If you call this method yourself, you may also need to {#unsubscribe}
|
|
172
|
+
# or call `subscriptions.delete_subscription` to clean up the database if the query crashes with an error
|
|
173
|
+
# later in execution.
|
|
174
|
+
# @return [void]
|
|
175
|
+
def write_subscription
|
|
176
|
+
if subscription_written?
|
|
177
|
+
raise GraphQL::Error, "`write_subscription` was called but `#{self.class}#subscription_written?` is already true. Remove a call to `write subscription`."
|
|
178
|
+
else
|
|
179
|
+
@subscription_written = true
|
|
180
|
+
context.schema.subscriptions.write_subscription(context.query, [event])
|
|
181
|
+
end
|
|
182
|
+
nil
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
# @return [Boolean] `true` if {#write_subscription} was called already
|
|
186
|
+
def subscription_written?
|
|
187
|
+
@subscription_written
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
# @return [Subscriptions::Event] This object is used as a representation of this subscription for the backend
|
|
191
|
+
def event
|
|
192
|
+
@event ||= Subscriptions::Event.new(
|
|
193
|
+
name: field.name,
|
|
194
|
+
arguments: @original_arguments,
|
|
195
|
+
context: context,
|
|
196
|
+
field: field,
|
|
197
|
+
)
|
|
198
|
+
end
|
|
153
199
|
end
|
|
154
200
|
end
|
|
155
201
|
end
|
|
@@ -51,19 +51,36 @@ module GraphQL
|
|
|
51
51
|
end
|
|
52
52
|
|
|
53
53
|
def validate(_object, context, value)
|
|
54
|
-
|
|
54
|
+
fully_matched_conditions = 0
|
|
55
|
+
partially_matched_conditions = 0
|
|
55
56
|
|
|
56
57
|
if !value.nil?
|
|
57
58
|
@one_of.each do |one_of_condition|
|
|
58
59
|
case one_of_condition
|
|
59
60
|
when Symbol
|
|
60
61
|
if value.key?(one_of_condition)
|
|
61
|
-
|
|
62
|
+
fully_matched_conditions += 1
|
|
62
63
|
end
|
|
63
64
|
when Array
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
65
|
+
any_match = false
|
|
66
|
+
full_match = true
|
|
67
|
+
|
|
68
|
+
one_of_condition.each do |k|
|
|
69
|
+
if value.key?(k)
|
|
70
|
+
any_match = true
|
|
71
|
+
else
|
|
72
|
+
full_match = false
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
partial_match = !full_match && any_match
|
|
77
|
+
|
|
78
|
+
if full_match
|
|
79
|
+
fully_matched_conditions += 1
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
if partial_match
|
|
83
|
+
partially_matched_conditions += 1
|
|
67
84
|
end
|
|
68
85
|
else
|
|
69
86
|
raise ArgumentError, "Unknown one_of condition: #{one_of_condition.inspect}"
|
|
@@ -71,7 +88,7 @@ module GraphQL
|
|
|
71
88
|
end
|
|
72
89
|
end
|
|
73
90
|
|
|
74
|
-
if
|
|
91
|
+
if fully_matched_conditions == 1 && partially_matched_conditions == 0
|
|
75
92
|
nil # OK
|
|
76
93
|
else
|
|
77
94
|
@message || build_message(context)
|
|
@@ -18,7 +18,7 @@ module GraphQL
|
|
|
18
18
|
if ctx.respond_to?(:types) && (types = ctx.types).is_a?(self)
|
|
19
19
|
types
|
|
20
20
|
else
|
|
21
|
-
schema.visibility.profile_for(ctx
|
|
21
|
+
schema.visibility.profile_for(ctx)
|
|
22
22
|
end
|
|
23
23
|
end
|
|
24
24
|
|
|
@@ -84,6 +84,8 @@ module GraphQL
|
|
|
84
84
|
@cached_arguments = Hash.new do |h, owner|
|
|
85
85
|
h[owner] = non_duplicate_items(owner.all_argument_definitions, @cached_visible_arguments)
|
|
86
86
|
end.compare_by_identity
|
|
87
|
+
|
|
88
|
+
@loadable_possible_types = Hash.new { |h, union_type| h[union_type] = union_type.possible_types }.compare_by_identity
|
|
87
89
|
end
|
|
88
90
|
|
|
89
91
|
def field_on_visible_interface?(field, owner)
|
|
@@ -157,7 +159,7 @@ module GraphQL
|
|
|
157
159
|
end
|
|
158
160
|
end
|
|
159
161
|
end
|
|
160
|
-
visible_f
|
|
162
|
+
visible_f&.ensure_loaded
|
|
161
163
|
elsif f && @cached_visible_fields[owner][f.ensure_loaded]
|
|
162
164
|
f
|
|
163
165
|
else
|
|
@@ -249,6 +251,10 @@ module GraphQL
|
|
|
249
251
|
!@all_types[t.graphql_name] && @cached_visible[t]
|
|
250
252
|
end
|
|
251
253
|
|
|
254
|
+
def loadable_possible_types(t, _ctx)
|
|
255
|
+
@loadable_possible_types[t]
|
|
256
|
+
end
|
|
257
|
+
|
|
252
258
|
def loaded_types
|
|
253
259
|
@all_types.values
|
|
254
260
|
end
|
|
@@ -313,9 +319,9 @@ module GraphQL
|
|
|
313
319
|
case type.kind.name
|
|
314
320
|
when "INTERFACE"
|
|
315
321
|
pts = []
|
|
316
|
-
@schema.visibility.all_interface_type_memberships[type].each do |itm|
|
|
317
|
-
if @cached_visible[itm] &&
|
|
318
|
-
pts <<
|
|
322
|
+
@schema.visibility.all_interface_type_memberships[type].each do |(itm, impl_type)|
|
|
323
|
+
if @cached_visible[itm] && @cached_visible[impl_type] && referenced?(impl_type)
|
|
324
|
+
pts << impl_type
|
|
319
325
|
end
|
|
320
326
|
end
|
|
321
327
|
pts
|