graphql 2.4.3 → 2.5.3

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.
Files changed (171) hide show
  1. checksums.yaml +4 -4
  2. data/lib/graphql/analysis/analyzer.rb +2 -1
  3. data/lib/graphql/analysis/query_complexity.rb +87 -7
  4. data/lib/graphql/analysis/visitor.rb +38 -41
  5. data/lib/graphql/analysis.rb +15 -12
  6. data/lib/graphql/autoload.rb +38 -0
  7. data/lib/graphql/backtrace/table.rb +118 -55
  8. data/lib/graphql/backtrace.rb +1 -19
  9. data/lib/graphql/current.rb +7 -2
  10. data/lib/graphql/dashboard/detailed_traces.rb +47 -0
  11. data/lib/graphql/dashboard/installable.rb +22 -0
  12. data/lib/graphql/dashboard/limiters.rb +93 -0
  13. data/lib/graphql/dashboard/operation_store.rb +199 -0
  14. data/lib/graphql/dashboard/statics/bootstrap-5.3.3.min.css +6 -0
  15. data/lib/graphql/dashboard/statics/bootstrap-5.3.3.min.js +7 -0
  16. data/lib/graphql/dashboard/statics/charts.min.css +1 -0
  17. data/lib/graphql/dashboard/statics/dashboard.css +30 -0
  18. data/lib/graphql/dashboard/statics/dashboard.js +143 -0
  19. data/lib/graphql/dashboard/statics/header-icon.png +0 -0
  20. data/lib/graphql/dashboard/statics/icon.png +0 -0
  21. data/lib/graphql/dashboard/subscriptions.rb +96 -0
  22. data/lib/graphql/dashboard/views/graphql/dashboard/detailed_traces/traces/index.html.erb +45 -0
  23. data/lib/graphql/dashboard/views/graphql/dashboard/landings/show.html.erb +18 -0
  24. data/lib/graphql/dashboard/views/graphql/dashboard/limiters/limiters/show.html.erb +62 -0
  25. data/lib/graphql/dashboard/views/graphql/dashboard/not_installed.html.erb +18 -0
  26. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/_form.html.erb +23 -0
  27. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/edit.html.erb +21 -0
  28. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/index.html.erb +69 -0
  29. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/new.html.erb +7 -0
  30. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/index_entries/index.html.erb +39 -0
  31. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/index_entries/show.html.erb +32 -0
  32. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/operations/index.html.erb +81 -0
  33. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/operations/show.html.erb +71 -0
  34. data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/subscriptions/show.html.erb +41 -0
  35. data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/topics/index.html.erb +55 -0
  36. data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/topics/show.html.erb +40 -0
  37. data/lib/graphql/dashboard/views/layouts/graphql/dashboard/application.html.erb +108 -0
  38. data/lib/graphql/dashboard.rb +158 -0
  39. data/lib/graphql/dataloader/active_record_association_source.rb +64 -0
  40. data/lib/graphql/dataloader/active_record_source.rb +26 -0
  41. data/lib/graphql/dataloader/async_dataloader.rb +21 -9
  42. data/lib/graphql/dataloader/null_dataloader.rb +1 -1
  43. data/lib/graphql/dataloader/source.rb +3 -3
  44. data/lib/graphql/dataloader.rb +43 -14
  45. data/lib/graphql/dig.rb +2 -1
  46. data/lib/graphql/execution/interpreter/resolve.rb +3 -3
  47. data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +34 -4
  48. data/lib/graphql/execution/interpreter/runtime.rb +96 -52
  49. data/lib/graphql/execution/interpreter.rb +16 -7
  50. data/lib/graphql/execution/multiplex.rb +6 -5
  51. data/lib/graphql/introspection/directive_location_enum.rb +1 -1
  52. data/lib/graphql/invalid_name_error.rb +1 -1
  53. data/lib/graphql/invalid_null_error.rb +19 -16
  54. data/lib/graphql/language/cache.rb +13 -0
  55. data/lib/graphql/language/document_from_schema_definition.rb +8 -7
  56. data/lib/graphql/language/lexer.rb +11 -4
  57. data/lib/graphql/language/nodes.rb +3 -0
  58. data/lib/graphql/language/parser.rb +15 -8
  59. data/lib/graphql/language/printer.rb +8 -8
  60. data/lib/graphql/language/static_visitor.rb +37 -33
  61. data/lib/graphql/language/visitor.rb +59 -55
  62. data/lib/graphql/pagination/connection.rb +1 -1
  63. data/lib/graphql/query/context/scoped_context.rb +1 -1
  64. data/lib/graphql/query/context.rb +7 -5
  65. data/lib/graphql/query/variable_validation_error.rb +1 -1
  66. data/lib/graphql/query.rb +22 -32
  67. data/lib/graphql/railtie.rb +7 -0
  68. data/lib/graphql/schema/addition.rb +1 -1
  69. data/lib/graphql/schema/always_visible.rb +1 -0
  70. data/lib/graphql/schema/argument.rb +7 -8
  71. data/lib/graphql/schema/build_from_definition.rb +99 -53
  72. data/lib/graphql/schema/directive/flagged.rb +3 -1
  73. data/lib/graphql/schema/directive.rb +2 -2
  74. data/lib/graphql/schema/enum.rb +36 -1
  75. data/lib/graphql/schema/enum_value.rb +1 -1
  76. data/lib/graphql/schema/field/scope_extension.rb +1 -1
  77. data/lib/graphql/schema/field.rb +27 -13
  78. data/lib/graphql/schema/field_extension.rb +1 -1
  79. data/lib/graphql/schema/has_single_input_argument.rb +3 -1
  80. data/lib/graphql/schema/input_object.rb +77 -40
  81. data/lib/graphql/schema/interface.rb +3 -2
  82. data/lib/graphql/schema/list.rb +1 -1
  83. data/lib/graphql/schema/loader.rb +1 -1
  84. data/lib/graphql/schema/member/has_arguments.rb +25 -17
  85. data/lib/graphql/schema/member/has_dataloader.rb +62 -0
  86. data/lib/graphql/schema/member/has_deprecation_reason.rb +15 -0
  87. data/lib/graphql/schema/member/has_directives.rb +4 -4
  88. data/lib/graphql/schema/member/has_fields.rb +19 -1
  89. data/lib/graphql/schema/member/has_interfaces.rb +5 -5
  90. data/lib/graphql/schema/member/has_validators.rb +1 -1
  91. data/lib/graphql/schema/member/scoped.rb +1 -1
  92. data/lib/graphql/schema/member/type_system_helpers.rb +1 -1
  93. data/lib/graphql/schema/member.rb +1 -0
  94. data/lib/graphql/schema/object.rb +25 -8
  95. data/lib/graphql/schema/relay_classic_mutation.rb +0 -1
  96. data/lib/graphql/schema/resolver.rb +12 -10
  97. data/lib/graphql/schema/subscription.rb +52 -6
  98. data/lib/graphql/schema/union.rb +1 -1
  99. data/lib/graphql/schema/validator/required_validator.rb +23 -6
  100. data/lib/graphql/schema/validator.rb +1 -1
  101. data/lib/graphql/schema/visibility/migration.rb +1 -0
  102. data/lib/graphql/schema/visibility/profile.rb +98 -244
  103. data/lib/graphql/schema/visibility/visit.rb +190 -0
  104. data/lib/graphql/schema/visibility.rb +178 -38
  105. data/lib/graphql/schema/warden.rb +18 -5
  106. data/lib/graphql/schema.rb +266 -54
  107. data/lib/graphql/static_validation/all_rules.rb +1 -1
  108. data/lib/graphql/static_validation/rules/argument_names_are_unique.rb +1 -1
  109. data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +47 -13
  110. data/lib/graphql/static_validation/rules/fields_will_merge.rb +79 -17
  111. data/lib/graphql/static_validation/rules/fields_will_merge_error.rb +10 -2
  112. data/lib/graphql/static_validation/rules/no_definitions_are_present.rb +1 -1
  113. data/lib/graphql/static_validation/rules/not_single_subscription_error.rb +25 -0
  114. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +1 -1
  115. data/lib/graphql/static_validation/rules/subscription_root_exists_and_single_subscription_selection.rb +26 -0
  116. data/lib/graphql/static_validation/rules/unique_directives_per_location.rb +1 -1
  117. data/lib/graphql/static_validation/rules/variable_names_are_unique.rb +1 -1
  118. data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +1 -1
  119. data/lib/graphql/static_validation/validation_context.rb +1 -0
  120. data/lib/graphql/static_validation/validator.rb +6 -1
  121. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +1 -1
  122. data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +12 -10
  123. data/lib/graphql/subscriptions/event.rb +12 -1
  124. data/lib/graphql/subscriptions/serialize.rb +1 -1
  125. data/lib/graphql/subscriptions.rb +1 -1
  126. data/lib/graphql/testing/helpers.rb +7 -4
  127. data/lib/graphql/tracing/active_support_notifications_trace.rb +14 -3
  128. data/lib/graphql/tracing/active_support_notifications_tracing.rb +1 -1
  129. data/lib/graphql/tracing/appoptics_trace.rb +9 -1
  130. data/lib/graphql/tracing/appoptics_tracing.rb +7 -0
  131. data/lib/graphql/tracing/appsignal_trace.rb +32 -55
  132. data/lib/graphql/tracing/appsignal_tracing.rb +2 -0
  133. data/lib/graphql/tracing/call_legacy_tracers.rb +66 -0
  134. data/lib/graphql/tracing/data_dog_trace.rb +46 -158
  135. data/lib/graphql/tracing/data_dog_tracing.rb +2 -0
  136. data/lib/graphql/tracing/detailed_trace/memory_backend.rb +60 -0
  137. data/lib/graphql/tracing/detailed_trace/redis_backend.rb +72 -0
  138. data/lib/graphql/tracing/detailed_trace.rb +93 -0
  139. data/lib/graphql/tracing/legacy_hooks_trace.rb +1 -0
  140. data/lib/graphql/tracing/legacy_trace.rb +4 -61
  141. data/lib/graphql/tracing/monitor_trace.rb +283 -0
  142. data/lib/graphql/tracing/new_relic_trace.rb +47 -54
  143. data/lib/graphql/tracing/new_relic_tracing.rb +2 -0
  144. data/lib/graphql/tracing/notifications_trace.rb +182 -34
  145. data/lib/graphql/tracing/notifications_tracing.rb +2 -0
  146. data/lib/graphql/tracing/null_trace.rb +9 -0
  147. data/lib/graphql/tracing/perfetto_trace/trace.proto +141 -0
  148. data/lib/graphql/tracing/perfetto_trace/trace_pb.rb +33 -0
  149. data/lib/graphql/tracing/perfetto_trace.rb +734 -0
  150. data/lib/graphql/tracing/platform_trace.rb +5 -0
  151. data/lib/graphql/tracing/prometheus_trace/graphql_collector.rb +2 -0
  152. data/lib/graphql/tracing/prometheus_trace.rb +72 -68
  153. data/lib/graphql/tracing/prometheus_tracing.rb +2 -0
  154. data/lib/graphql/tracing/scout_trace.rb +32 -55
  155. data/lib/graphql/tracing/scout_tracing.rb +2 -0
  156. data/lib/graphql/tracing/sentry_trace.rb +62 -94
  157. data/lib/graphql/tracing/statsd_trace.rb +33 -41
  158. data/lib/graphql/tracing/statsd_tracing.rb +2 -0
  159. data/lib/graphql/tracing/trace.rb +111 -1
  160. data/lib/graphql/tracing.rb +31 -30
  161. data/lib/graphql/types/relay/connection_behaviors.rb +3 -3
  162. data/lib/graphql/types/relay/edge_behaviors.rb +2 -2
  163. data/lib/graphql/types.rb +18 -11
  164. data/lib/graphql/version.rb +1 -1
  165. data/lib/graphql.rb +55 -47
  166. metadata +146 -11
  167. data/lib/graphql/backtrace/inspect_result.rb +0 -38
  168. data/lib/graphql/backtrace/trace.rb +0 -93
  169. data/lib/graphql/backtrace/tracer.rb +0 -80
  170. data/lib/graphql/schema/null_mask.rb +0 -11
  171. data/lib/graphql/static_validation/rules/subscription_root_exists.rb +0 -17
@@ -30,7 +30,7 @@ module GraphQL
30
30
 
31
31
  def inherited(subclass)
32
32
  super
33
- subclass.class_eval do
33
+ subclass.class_exec do
34
34
  @reauthorize_scoped_objects = nil
35
35
  end
36
36
  end
@@ -42,7 +42,7 @@ module GraphQL
42
42
  private
43
43
 
44
44
  def inherited(subclass)
45
- subclass.class_eval do
45
+ subclass.class_exec do
46
46
  @to_non_null_type ||= nil
47
47
  @to_list_type ||= nil
48
48
  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
- maybe_lazy_auth_val = context.query.current_trace.authorized(query: context.query, type: self, object: object) do
61
- begin
62
- authorized?(object, context)
63
- rescue GraphQL::UnauthorizedError => err
64
- context.schema.unauthorized_object(err)
65
- rescue StandardError => err
66
- context.query.handle_or_reraise(err)
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
@@ -1,5 +1,4 @@
1
1
  # frozen_string_literal: true
2
- require "graphql/types/string"
3
2
 
4
3
  module GraphQL
5
4
  class Schema
@@ -27,6 +27,8 @@ 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
31
+ extend Schema::Member::HasDeprecationReason
30
32
 
31
33
  # @param object [Object] The application object that this field is being resolved on
32
34
  # @param context [GraphQL::Query::Context]
@@ -49,11 +51,6 @@ module GraphQL
49
51
  # @return [GraphQL::Query::Context]
50
52
  attr_reader :context
51
53
 
52
- # @return [GraphQL::Dataloader]
53
- def dataloader
54
- context.dataloader
55
- end
56
-
57
54
  # @return [GraphQL::Schema::Field]
58
55
  attr_reader :field
59
56
 
@@ -67,7 +64,7 @@ module GraphQL
67
64
  # @api private
68
65
  def resolve_with_support(**args)
69
66
  # First call the ready? hook which may raise
70
- raw_ready_val = if args.any?
67
+ raw_ready_val = if !args.empty?
71
68
  ready?(**args)
72
69
  else
73
70
  ready?
@@ -88,7 +85,7 @@ module GraphQL
88
85
  @prepared_arguments = loaded_args
89
86
  Schema::Validator.validate!(self.class.validators, object, context, loaded_args, as: @field)
90
87
  # Then call `authorized?`, which may raise or may return a lazy object
91
- raw_authorized_val = if loaded_args.any?
88
+ raw_authorized_val = if !loaded_args.empty?
92
89
  authorized?(**loaded_args)
93
90
  else
94
91
  authorized?
@@ -117,7 +114,7 @@ module GraphQL
117
114
 
118
115
  # @api private {GraphQL::Schema::Mutation} uses this to clear the dataloader cache
119
116
  def call_resolve(args_hash)
120
- if args_hash.any?
117
+ if !args_hash.empty?
121
118
  public_send(self.class.resolve_method, **args_hash)
122
119
  else
123
120
  public_send(self.class.resolve_method)
@@ -208,7 +205,7 @@ module GraphQL
208
205
  end
209
206
 
210
207
  # Avoid returning a lazy if none are needed
211
- if prepare_lazies.any?
208
+ if !prepare_lazies.empty?
212
209
  GraphQL::Execution::Lazy.all(prepare_lazies).then { prepared_args }
213
210
  else
214
211
  prepared_args
@@ -394,7 +391,7 @@ module GraphQL
394
391
  if superclass.respond_to?(:extensions)
395
392
  s_exts = superclass.extensions
396
393
  if own_exts
397
- if s_exts.any?
394
+ if !s_exts.empty?
398
395
  own_exts + s_exts
399
396
  else
400
397
  own_exts
@@ -407,6 +404,11 @@ module GraphQL
407
404
  end
408
405
  end
409
406
 
407
+ def inherited(child_class)
408
+ child_class.description(description)
409
+ super
410
+ end
411
+
410
412
  private
411
413
 
412
414
  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,8 +65,9 @@ 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
- ret_val = args.any? ? subscribe(**args) : subscribe
70
+ ret_val = !args.empty? ? subscribe(**args) : subscribe
59
71
  if ret_val == :no_response
60
72
  context.skip
61
73
  else
@@ -71,8 +83,9 @@ 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
- ret_val = args.any? ? update(**args) : update
88
+ ret_val = !args.empty? ? update(**args) : update
76
89
  if ret_val == NO_UPDATE
77
90
  context.namespace(:subscriptions)[:no_update] = true
78
91
  context.skip
@@ -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 = READING_SCOPE, optional: false)
116
- if new_scope != READING_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
@@ -11,7 +11,7 @@ module GraphQL
11
11
  end
12
12
 
13
13
  def possible_types(*types, context: GraphQL::Query::NullContext.instance, **options)
14
- if types.any?
14
+ if !types.empty?
15
15
  types.each do |t|
16
16
  assert_valid_union_member(t)
17
17
  type_memberships << type_membership_class.new(self, t, **options)
@@ -51,19 +51,36 @@ module GraphQL
51
51
  end
52
52
 
53
53
  def validate(_object, context, value)
54
- matched_conditions = 0
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
- matched_conditions += 1
62
+ fully_matched_conditions += 1
62
63
  end
63
64
  when Array
64
- if one_of_condition.all? { |k| value.key?(k) }
65
- matched_conditions += 1
66
- break
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 matched_conditions == 1
91
+ if fully_matched_conditions == 1 && partially_matched_conditions == 0
75
92
  nil # OK
76
93
  else
77
94
  @message || build_message(context)
@@ -143,7 +143,7 @@ module GraphQL
143
143
  end
144
144
  end
145
145
 
146
- if all_errors.any?
146
+ if !all_errors.empty?
147
147
  raise ValidationFailedError.new(errors: all_errors)
148
148
  end
149
149
  nil
@@ -112,6 +112,7 @@ module GraphQL
112
112
  :all_types_h,
113
113
  :fields,
114
114
  :loadable?,
115
+ :loadable_possible_types,
115
116
  :type,
116
117
  :arguments,
117
118
  :argument,