graphql 2.4.13 → 2.5.11

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 (98) hide show
  1. checksums.yaml +4 -4
  2. data/lib/graphql/analysis/query_complexity.rb +87 -7
  3. data/lib/graphql/backtrace/table.rb +37 -14
  4. data/lib/graphql/current.rb +1 -1
  5. data/lib/graphql/dashboard/detailed_traces.rb +47 -0
  6. data/lib/graphql/dashboard/installable.rb +22 -0
  7. data/lib/graphql/dashboard/limiters.rb +93 -0
  8. data/lib/graphql/dashboard/operation_store.rb +199 -0
  9. data/lib/graphql/dashboard/statics/charts.min.css +1 -0
  10. data/lib/graphql/dashboard/statics/dashboard.css +27 -0
  11. data/lib/graphql/dashboard/statics/dashboard.js +74 -9
  12. data/lib/graphql/dashboard/subscriptions.rb +96 -0
  13. data/lib/graphql/dashboard/views/graphql/dashboard/detailed_traces/traces/index.html.erb +45 -0
  14. data/lib/graphql/dashboard/views/graphql/dashboard/limiters/limiters/show.html.erb +62 -0
  15. data/lib/graphql/dashboard/views/graphql/dashboard/not_installed.html.erb +18 -0
  16. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/_form.html.erb +23 -0
  17. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/edit.html.erb +21 -0
  18. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/index.html.erb +69 -0
  19. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/new.html.erb +7 -0
  20. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/index_entries/index.html.erb +39 -0
  21. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/index_entries/show.html.erb +32 -0
  22. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/operations/index.html.erb +81 -0
  23. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/operations/show.html.erb +71 -0
  24. data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/subscriptions/show.html.erb +41 -0
  25. data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/topics/index.html.erb +55 -0
  26. data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/topics/show.html.erb +40 -0
  27. data/lib/graphql/dashboard/views/layouts/graphql/dashboard/application.html.erb +49 -1
  28. data/lib/graphql/dashboard.rb +45 -29
  29. data/lib/graphql/dataloader/active_record_association_source.rb +28 -8
  30. data/lib/graphql/dataloader/active_record_source.rb +26 -5
  31. data/lib/graphql/dataloader/null_dataloader.rb +7 -0
  32. data/lib/graphql/dataloader/source.rb +16 -4
  33. data/lib/graphql/dig.rb +2 -1
  34. data/lib/graphql/execution/interpreter/resolve.rb +3 -3
  35. data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +34 -1
  36. data/lib/graphql/execution/interpreter/runtime.rb +163 -59
  37. data/lib/graphql/execution/interpreter.rb +5 -13
  38. data/lib/graphql/execution/multiplex.rb +6 -1
  39. data/lib/graphql/invalid_null_error.rb +15 -2
  40. data/lib/graphql/language/lexer.rb +9 -2
  41. data/lib/graphql/language/nodes.rb +5 -1
  42. data/lib/graphql/language/parser.rb +14 -6
  43. data/lib/graphql/query/context.rb +3 -8
  44. data/lib/graphql/query/partial.rb +179 -0
  45. data/lib/graphql/query.rb +59 -55
  46. data/lib/graphql/schema/addition.rb +3 -1
  47. data/lib/graphql/schema/always_visible.rb +1 -0
  48. data/lib/graphql/schema/argument.rb +9 -3
  49. data/lib/graphql/schema/build_from_definition.rb +96 -47
  50. data/lib/graphql/schema/directive/flagged.rb +2 -0
  51. data/lib/graphql/schema/directive.rb +33 -1
  52. data/lib/graphql/schema/field.rb +23 -1
  53. data/lib/graphql/schema/input_object.rb +38 -30
  54. data/lib/graphql/schema/list.rb +1 -1
  55. data/lib/graphql/schema/member/has_arguments.rb +2 -2
  56. data/lib/graphql/schema/member/has_dataloader.rb +4 -2
  57. data/lib/graphql/schema/member/has_deprecation_reason.rb +15 -0
  58. data/lib/graphql/schema/member/has_interfaces.rb +2 -2
  59. data/lib/graphql/schema/member/type_system_helpers.rb +16 -2
  60. data/lib/graphql/schema/ractor_shareable.rb +79 -0
  61. data/lib/graphql/schema/resolver.rb +1 -0
  62. data/lib/graphql/schema/scalar.rb +1 -6
  63. data/lib/graphql/schema/timeout.rb +19 -2
  64. data/lib/graphql/schema/validator/required_validator.rb +15 -6
  65. data/lib/graphql/schema/visibility/migration.rb +2 -2
  66. data/lib/graphql/schema/visibility/profile.rb +107 -21
  67. data/lib/graphql/schema/visibility.rb +41 -29
  68. data/lib/graphql/schema/warden.rb +13 -5
  69. data/lib/graphql/schema.rb +228 -32
  70. data/lib/graphql/static_validation/all_rules.rb +2 -2
  71. data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +47 -13
  72. data/lib/graphql/static_validation/rules/fields_will_merge.rb +78 -16
  73. data/lib/graphql/static_validation/rules/fields_will_merge_error.rb +10 -2
  74. data/lib/graphql/static_validation/rules/not_single_subscription_error.rb +25 -0
  75. data/lib/graphql/static_validation/rules/subscription_root_exists_and_single_subscription_selection.rb +26 -0
  76. data/lib/graphql/static_validation/rules/unique_directives_per_location.rb +6 -2
  77. data/lib/graphql/testing/helpers.rb +5 -2
  78. data/lib/graphql/tracing/active_support_notifications_trace.rb +7 -0
  79. data/lib/graphql/tracing/appoptics_tracing.rb +5 -0
  80. data/lib/graphql/tracing/appsignal_trace.rb +26 -61
  81. data/lib/graphql/tracing/data_dog_trace.rb +41 -164
  82. data/lib/graphql/tracing/monitor_trace.rb +283 -0
  83. data/lib/graphql/tracing/new_relic_trace.rb +34 -164
  84. data/lib/graphql/tracing/notifications_trace.rb +183 -37
  85. data/lib/graphql/tracing/null_trace.rb +1 -1
  86. data/lib/graphql/tracing/perfetto_trace.rb +16 -19
  87. data/lib/graphql/tracing/prometheus_trace.rb +47 -74
  88. data/lib/graphql/tracing/scout_trace.rb +25 -59
  89. data/lib/graphql/tracing/sentry_trace.rb +56 -99
  90. data/lib/graphql/tracing/statsd_trace.rb +24 -47
  91. data/lib/graphql/tracing/trace.rb +0 -17
  92. data/lib/graphql/tracing.rb +1 -0
  93. data/lib/graphql/type_kinds.rb +1 -0
  94. data/lib/graphql/version.rb +1 -1
  95. data/lib/graphql.rb +1 -1
  96. metadata +35 -26
  97. data/lib/graphql/dashboard/views/graphql/dashboard/traces/index.html.erb +0 -63
  98. data/lib/graphql/static_validation/rules/subscription_root_exists.rb +0 -17
@@ -20,8 +20,8 @@ module GraphQL
20
20
  from_document(schema_superclass, parser.parse_file(definition_path), **kwargs)
21
21
  end
22
22
 
23
- def from_document(schema_superclass, document, default_resolve:, using: {}, relay: false)
24
- Builder.build(schema_superclass, document, default_resolve: default_resolve || {}, relay: relay, using: using)
23
+ def from_document(schema_superclass, document, default_resolve:, using: {}, base_types: {}, relay: false)
24
+ Builder.build(schema_superclass, document, default_resolve: default_resolve || {}, relay: relay, using: using, base_types: base_types)
25
25
  end
26
26
  end
27
27
 
@@ -30,9 +30,18 @@ module GraphQL
30
30
  include GraphQL::EmptyObjects
31
31
  extend self
32
32
 
33
- def build(schema_superclass, document, default_resolve:, using: {}, relay:)
33
+ def build(schema_superclass, document, default_resolve:, using: {}, base_types: {}, relay:)
34
34
  raise InvalidDocumentError.new('Must provide a document ast.') if !document || !document.is_a?(GraphQL::Language::Nodes::Document)
35
35
 
36
+ base_types = {
37
+ object: GraphQL::Schema::Object,
38
+ interface: GraphQL::Schema::Interface,
39
+ union: GraphQL::Schema::Union,
40
+ scalar: GraphQL::Schema::Scalar,
41
+ enum: GraphQL::Schema::Enum,
42
+ input_object: GraphQL::Schema::InputObject,
43
+ }.merge!(base_types)
44
+
36
45
  if default_resolve.is_a?(Hash)
37
46
  default_resolve = ResolveMap.new(default_resolve)
38
47
  end
@@ -53,7 +62,7 @@ module GraphQL
53
62
  types[type_name] ||= begin
54
63
  defn = document.definitions.find { |d| d.respond_to?(:name) && d.name == type_name }
55
64
  if defn
56
- build_definition_from_node(defn, directive_type_resolver, default_resolve)
65
+ build_definition_from_node(defn, directive_type_resolver, default_resolve, base_types)
57
66
  elsif (built_in_defn = GraphQL::Schema::BUILT_IN_TYPES[type_name])
58
67
  built_in_defn
59
68
  else
@@ -77,14 +86,20 @@ module GraphQL
77
86
  case definition
78
87
  when GraphQL::Language::Nodes::SchemaDefinition, GraphQL::Language::Nodes::DirectiveDefinition
79
88
  nil # already handled
80
- when GraphQL::Language::Nodes::SchemaExtension
89
+ when GraphQL::Language::Nodes::SchemaExtension,
90
+ GraphQL::Language::Nodes::ScalarTypeExtension,
91
+ GraphQL::Language::Nodes::ObjectTypeExtension,
92
+ GraphQL::Language::Nodes::InterfaceTypeExtension,
93
+ GraphQL::Language::Nodes::UnionTypeExtension,
94
+ GraphQL::Language::Nodes::EnumTypeExtension,
95
+ GraphQL::Language::Nodes::InputObjectTypeExtension
81
96
  schema_extensions ||= []
82
97
  schema_extensions << definition
83
98
  else
84
99
  # It's possible that this was already loaded by the directives
85
100
  prev_type = types[definition.name]
86
101
  if prev_type.nil? || prev_type.is_a?(Schema::LateBoundType)
87
- types[definition.name] = build_definition_from_node(definition, type_resolver, default_resolve)
102
+ types[definition.name] = build_definition_from_node(definition, type_resolver, default_resolve, base_types)
88
103
  end
89
104
  end
90
105
  end
@@ -124,6 +139,34 @@ module GraphQL
124
139
 
125
140
  raise InvalidDocumentError.new('Must provide schema definition with query type or a type named Query.') unless query_root_type
126
141
 
142
+ schema_extensions&.each do |ext|
143
+ next if ext.is_a?(GraphQL::Language::Nodes::SchemaExtension)
144
+
145
+ built_type = types[ext.name]
146
+
147
+ case ext
148
+ when GraphQL::Language::Nodes::ScalarTypeExtension
149
+ build_directives(built_type, ext, type_resolver)
150
+ when GraphQL::Language::Nodes::ObjectTypeExtension
151
+ build_directives(built_type, ext, type_resolver)
152
+ build_fields(built_type, ext.fields, type_resolver, default_resolve: true)
153
+ build_interfaces(built_type, ext.interfaces, type_resolver)
154
+ when GraphQL::Language::Nodes::InterfaceTypeExtension
155
+ build_directives(built_type, ext, type_resolver)
156
+ build_fields(built_type, ext.fields, type_resolver, default_resolve: nil)
157
+ build_interfaces(built_type, ext.interfaces, type_resolver)
158
+ when GraphQL::Language::Nodes::UnionTypeExtension
159
+ build_directives(built_type, ext, type_resolver)
160
+ built_type.possible_types(*ext.types.map { |type_name| type_resolver.call(type_name) })
161
+ when GraphQL::Language::Nodes::EnumTypeExtension
162
+ build_directives(built_type, ext, type_resolver)
163
+ build_values(built_type, ext.values, type_resolver)
164
+ when GraphQL::Language::Nodes::InputObjectTypeExtension
165
+ build_directives(built_type, ext, type_resolver)
166
+ build_arguments(built_type, ext.fields, type_resolver)
167
+ end
168
+ end
169
+
127
170
  builder = self
128
171
 
129
172
  found_types = types.values
@@ -144,6 +187,10 @@ module GraphQL
144
187
 
145
188
  object_types.each do |t|
146
189
  t.interfaces.each do |int_t|
190
+ if int_t.is_a?(LateBoundType)
191
+ int_t = types[int_t.graphql_name]
192
+ t.implements(int_t)
193
+ end
147
194
  int_t.orphan_types(t)
148
195
  end
149
196
  end
@@ -192,8 +239,8 @@ module GraphQL
192
239
  end
193
240
  end
194
241
 
195
- if schema_extensions
196
- schema_extensions.each do |ext|
242
+ schema_extensions&.each do |ext|
243
+ if ext.is_a?(GraphQL::Language::Nodes::SchemaExtension)
197
244
  build_directives(schema_class, ext, type_resolver)
198
245
  end
199
246
  end
@@ -205,20 +252,20 @@ module GraphQL
205
252
  raise(GraphQL::RequiredImplementationMissingError, "Generated Schema cannot use Interface or Union types for execution. Implement resolve_type on your resolver.")
206
253
  }
207
254
 
208
- def build_definition_from_node(definition, type_resolver, default_resolve)
255
+ def build_definition_from_node(definition, type_resolver, default_resolve, base_types)
209
256
  case definition
210
257
  when GraphQL::Language::Nodes::EnumTypeDefinition
211
- build_enum_type(definition, type_resolver)
258
+ build_enum_type(definition, type_resolver, base_types[:enum])
212
259
  when GraphQL::Language::Nodes::ObjectTypeDefinition
213
- build_object_type(definition, type_resolver)
260
+ build_object_type(definition, type_resolver, base_types[:object])
214
261
  when GraphQL::Language::Nodes::InterfaceTypeDefinition
215
- build_interface_type(definition, type_resolver)
262
+ build_interface_type(definition, type_resolver, base_types[:interface])
216
263
  when GraphQL::Language::Nodes::UnionTypeDefinition
217
- build_union_type(definition, type_resolver)
264
+ build_union_type(definition, type_resolver, base_types[:union])
218
265
  when GraphQL::Language::Nodes::ScalarTypeDefinition
219
- build_scalar_type(definition, type_resolver, default_resolve: default_resolve)
266
+ build_scalar_type(definition, type_resolver, base_types[:scalar], default_resolve: default_resolve)
220
267
  when GraphQL::Language::Nodes::InputObjectTypeDefinition
221
- build_input_object_type(definition, type_resolver)
268
+ build_input_object_type(definition, type_resolver, base_types[:input_object])
222
269
  end
223
270
  end
224
271
 
@@ -284,22 +331,26 @@ module GraphQL
284
331
  end
285
332
  end
286
333
 
287
- def build_enum_type(enum_type_definition, type_resolver)
334
+ def build_enum_type(enum_type_definition, type_resolver, base_type)
288
335
  builder = self
289
- Class.new(GraphQL::Schema::Enum) do
336
+ Class.new(base_type) do
290
337
  graphql_name(enum_type_definition.name)
291
338
  builder.build_directives(self, enum_type_definition, type_resolver)
292
339
  description(enum_type_definition.description)
293
340
  ast_node(enum_type_definition)
294
- enum_type_definition.values.each do |enum_value_definition|
295
- value(enum_value_definition.name,
296
- value: enum_value_definition.name,
297
- deprecation_reason: builder.build_deprecation_reason(enum_value_definition.directives),
298
- description: enum_value_definition.description,
299
- directives: builder.prepare_directives(enum_value_definition, type_resolver),
300
- ast_node: enum_value_definition,
301
- )
302
- end
341
+ builder.build_values(self, enum_type_definition.values, type_resolver)
342
+ end
343
+ end
344
+
345
+ def build_values(type_class, enum_value_definitions, type_resolver)
346
+ enum_value_definitions.each do |enum_value_definition|
347
+ type_class.value(enum_value_definition.name,
348
+ value: enum_value_definition.name,
349
+ deprecation_reason: build_deprecation_reason(enum_value_definition.directives),
350
+ description: enum_value_definition.description,
351
+ directives: prepare_directives(enum_value_definition, type_resolver),
352
+ ast_node: enum_value_definition,
353
+ )
303
354
  end
304
355
  end
305
356
 
@@ -313,9 +364,9 @@ module GraphQL
313
364
  reason.value
314
365
  end
315
366
 
316
- def build_scalar_type(scalar_type_definition, type_resolver, default_resolve:)
367
+ def build_scalar_type(scalar_type_definition, type_resolver, base_type, default_resolve:)
317
368
  builder = self
318
- Class.new(GraphQL::Schema::Scalar) do
369
+ Class.new(base_type) do
319
370
  graphql_name(scalar_type_definition.name)
320
371
  description(scalar_type_definition.description)
321
372
  ast_node(scalar_type_definition)
@@ -336,9 +387,9 @@ module GraphQL
336
387
  end
337
388
  end
338
389
 
339
- def build_union_type(union_type_definition, type_resolver)
390
+ def build_union_type(union_type_definition, type_resolver, base_type)
340
391
  builder = self
341
- Class.new(GraphQL::Schema::Union) do
392
+ Class.new(base_type) do
342
393
  graphql_name(union_type_definition.name)
343
394
  description(union_type_definition.description)
344
395
  possible_types(*union_type_definition.types.map { |type_name| type_resolver.call(type_name) })
@@ -347,27 +398,28 @@ module GraphQL
347
398
  end
348
399
  end
349
400
 
350
- def build_object_type(object_type_definition, type_resolver)
401
+ def build_object_type(object_type_definition, type_resolver, base_type)
351
402
  builder = self
352
403
 
353
- Class.new(GraphQL::Schema::Object) do
404
+ Class.new(base_type) do
354
405
  graphql_name(object_type_definition.name)
355
406
  description(object_type_definition.description)
356
407
  ast_node(object_type_definition)
357
408
  builder.build_directives(self, object_type_definition, type_resolver)
358
-
359
- object_type_definition.interfaces.each do |interface_name|
360
- interface_defn = type_resolver.call(interface_name)
361
- implements(interface_defn)
362
- end
363
-
409
+ builder.build_interfaces(self, object_type_definition.interfaces, type_resolver)
364
410
  builder.build_fields(self, object_type_definition.fields, type_resolver, default_resolve: true)
365
411
  end
366
412
  end
367
413
 
368
- def build_input_object_type(input_object_type_definition, type_resolver)
414
+ def build_interfaces(type_class, interface_names, type_resolver)
415
+ interface_names.each do |interface_name|
416
+ type_class.implements(type_resolver.call(interface_name))
417
+ end
418
+ end
419
+
420
+ def build_input_object_type(input_object_type_definition, type_resolver, base_type)
369
421
  builder = self
370
- Class.new(GraphQL::Schema::InputObject) do
422
+ Class.new(base_type) do
371
423
  graphql_name(input_object_type_definition.name)
372
424
  description(input_object_type_definition.description)
373
425
  ast_node(input_object_type_definition)
@@ -427,16 +479,13 @@ module GraphQL
427
479
  end
428
480
  end
429
481
 
430
- def build_interface_type(interface_type_definition, type_resolver)
482
+ def build_interface_type(interface_type_definition, type_resolver, base_type)
431
483
  builder = self
432
484
  Module.new do
433
- include GraphQL::Schema::Interface
485
+ include base_type
434
486
  graphql_name(interface_type_definition.name)
435
487
  description(interface_type_definition.description)
436
- interface_type_definition.interfaces.each do |interface_name|
437
- interface_defn = type_resolver.call(interface_name)
438
- implements(interface_defn)
439
- end
488
+ builder.build_interfaces(self, interface_type_definition.interfaces, type_resolver)
440
489
  ast_node(interface_type_definition)
441
490
  builder.build_directives(self, interface_type_definition, type_resolver)
442
491
 
@@ -474,7 +523,7 @@ module GraphQL
474
523
 
475
524
  def define_field_resolve_method(owner, method_name, field_name)
476
525
  owner.define_method(method_name) { |**args|
477
- field_instance = self.class.get_field(field_name)
526
+ field_instance = context.types.field(owner, field_name)
478
527
  context.schema.definition_default_resolve.call(self.class, field_instance, object, args, context)
479
528
  }
480
529
  end
@@ -37,6 +37,8 @@ module GraphQL
37
37
 
38
38
  argument :by, [String], "Flags to check for this schema member"
39
39
 
40
+ repeatable(true)
41
+
40
42
  module VisibleByFlag
41
43
  def self.included(schema_class)
42
44
  schema_class.extend(self)
@@ -9,6 +9,7 @@ module GraphQL
9
9
  class Directive < GraphQL::Schema::Member
10
10
  extend GraphQL::Schema::Member::HasArguments
11
11
  extend GraphQL::Schema::Member::HasArguments::HasDirectiveArguments
12
+ extend GraphQL::Schema::Member::HasValidators
12
13
 
13
14
  class << self
14
15
  # Directives aren't types, they don't have kinds.
@@ -75,6 +76,10 @@ module GraphQL
75
76
  yield
76
77
  end
77
78
 
79
+ def validate!(arguments, context)
80
+ Schema::Validator.validate!(validators, self, context, arguments)
81
+ end
82
+
78
83
  def on_field?
79
84
  locations.include?(FIELD)
80
85
  end
@@ -111,6 +116,9 @@ module GraphQL
111
116
  # @return [GraphQL::Interpreter::Arguments]
112
117
  attr_reader :arguments
113
118
 
119
+ class InvalidArgumentError < GraphQL::Error
120
+ end
121
+
114
122
  def initialize(owner, **arguments)
115
123
  @owner = owner
116
124
  assert_valid_owner
@@ -119,7 +127,31 @@ module GraphQL
119
127
  # - lazy resolution
120
128
  # Probably, those won't be needed here, since these are configuration arguments,
121
129
  # not runtime arguments.
122
- @arguments = self.class.coerce_arguments(nil, arguments, Query::NullContext.instance)
130
+ context = Query::NullContext.instance
131
+ self.class.all_argument_definitions.each do |arg_defn|
132
+ if arguments.key?(arg_defn.keyword)
133
+ value = arguments[arg_defn.keyword]
134
+ # This is a Ruby-land value; convert it to graphql for validation
135
+ graphql_value = begin
136
+ arg_defn.type.unwrap.coerce_isolated_result(value)
137
+ rescue GraphQL::Schema::Enum::UnresolvedValueError
138
+ # Let validation handle this
139
+ value
140
+ end
141
+ else
142
+ value = graphql_value = nil
143
+ end
144
+
145
+ result = arg_defn.type.validate_input(graphql_value, context)
146
+ if !result.valid?
147
+ raise InvalidArgumentError, "@#{graphql_name}.#{arg_defn.graphql_name} on #{owner.path} is invalid (#{value.inspect}): #{result.problems.first["explanation"]}"
148
+ end
149
+ end
150
+ self.class.validate!(arguments, context)
151
+ @arguments = self.class.coerce_arguments(nil, arguments, context)
152
+ if @arguments.is_a?(GraphQL::ExecutionError)
153
+ raise @arguments
154
+ end
123
155
  end
124
156
 
125
157
  def graphql_name
@@ -41,10 +41,24 @@ module GraphQL
41
41
  end
42
42
  end
43
43
 
44
+ # @return [String, nil]
45
+ def deprecation_reason
46
+ super || @resolver_class&.deprecation_reason
47
+ end
48
+
44
49
  def directives
45
50
  if @resolver_class && !(r_dirs = @resolver_class.directives).empty?
46
51
  if !(own_dirs = super).empty?
47
- own_dirs + r_dirs
52
+ new_dirs = own_dirs.dup
53
+ r_dirs.each do |r_dir|
54
+ if r_dir.class.repeatable? ||
55
+ ( (r_dir_name = r_dir.graphql_name) &&
56
+ (!new_dirs.any? { |d| d.graphql_name == r_dir_name })
57
+ )
58
+ new_dirs << r_dir
59
+ end
60
+ end
61
+ new_dirs
48
62
  else
49
63
  r_dirs
50
64
  end
@@ -602,6 +616,14 @@ module GraphQL
602
616
  end
603
617
  end
604
618
 
619
+ def freeze
620
+ type
621
+ owner_type
622
+ arguments_statically_coercible?
623
+ connection?
624
+ super
625
+ end
626
+
605
627
  class MissingReturnTypeError < GraphQL::Error; end
606
628
  attr_writer :type
607
629
 
@@ -38,7 +38,7 @@ module GraphQL
38
38
  # Weirdly, procs are applied during coercion, but not methods.
39
39
  # Probably because these methods require a `self`.
40
40
  if arg_defn.prepare.is_a?(Symbol) || context.nil?
41
- prepared_value = arg_defn.prepare_value(self, @ruby_style_hash[ruby_kwargs_key])
41
+ prepared_value = arg_defn.prepare_value(self, @ruby_style_hash[ruby_kwargs_key], context: context)
42
42
  overwrite_argument(ruby_kwargs_key, prepared_value)
43
43
  end
44
44
  end
@@ -64,14 +64,7 @@ module GraphQL
64
64
  end
65
65
 
66
66
  def prepare
67
- if @context
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):
@@ -175,30 +176,37 @@ module GraphQL
175
176
  return GraphQL::Query::InputValidationResult.from_problem(INVALID_OBJECT_MESSAGE % { object: JSON.generate(input, quirks_mode: true) })
176
177
  end
177
178
 
178
- # Inject missing required arguments
179
- missing_required_inputs = ctx.types.arguments(self).reduce({}) do |m, (argument)|
180
- if !input.key?(argument.graphql_name) && argument.type.non_null? && !argument.default_value? && types.argument(self, argument.graphql_name)
181
- m[argument.graphql_name] = nil
182
- end
183
-
184
- m
185
- end
186
179
 
187
180
  result = nil
188
- [input, missing_required_inputs].each do |args_to_validate|
189
- args_to_validate.each do |argument_name, value|
190
- argument = types.argument(self, argument_name)
191
- # Items in the input that are unexpected
192
- if argument.nil?
193
- result ||= Query::InputValidationResult.new
194
- result.add_problem("Field is not defined on #{self.graphql_name}", [argument_name])
195
- else
196
- # Items in the input that are expected, but have invalid values
197
- argument_result = argument.type.validate_input(value, ctx)
181
+
182
+
183
+ input.each do |argument_name, value|
184
+ argument = types.argument(self, argument_name)
185
+ if argument.nil? && ctx.is_a?(Query::NullContext) && argument_name.is_a?(Symbol)
186
+ # Validating definition directive arguments which come in as Symbols
187
+ argument = types.arguments(self).find { |arg| arg.keyword == argument_name }
188
+ end
189
+ # Items in the input that are unexpected
190
+ if argument.nil?
191
+ result ||= Query::InputValidationResult.new
192
+ result.add_problem("Field is not defined on #{self.graphql_name}", [argument_name])
193
+ else
194
+ # Items in the input that are expected, but have invalid values
195
+ argument_result = argument.type.validate_input(value, ctx)
196
+ if !argument_result.valid?
198
197
  result ||= Query::InputValidationResult.new
199
- if !argument_result.valid?
200
- result.merge_result!(argument_name, argument_result)
201
- end
198
+ result.merge_result!(argument_name, argument_result)
199
+ end
200
+ end
201
+ end
202
+
203
+ # Check for missing non-null arguments
204
+ ctx.types.arguments(self).each do |argument|
205
+ if !input.key?(argument.graphql_name) && argument.type.non_null? && !argument.default_value?
206
+ result ||= Query::InputValidationResult.new
207
+ argument_result = argument.type.validate_input(nil, ctx)
208
+ if !argument_result.valid?
209
+ result.merge_result!(argument.graphql_name, argument_result)
202
210
  end
203
211
  end
204
212
  end
@@ -4,7 +4,7 @@ module GraphQL
4
4
  class Schema
5
5
  # Represents a list type in the schema.
6
6
  # Wraps a {Schema::Member} as a list type.
7
- # @see {Schema::Member::TypeSystemHelpers#to_list_type}
7
+ # @see Schema::Member::TypeSystemHelpers#to_list_type Create a list type from another GraphQL type
8
8
  class List < GraphQL::Schema::Wrapper
9
9
  include Schema::Member::ValidatesInput
10
10
 
@@ -370,8 +370,8 @@ module GraphQL
370
370
  end
371
371
 
372
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
373
+ if arg_loads_type.kind.abstract?
374
+ # This union/interface is used in `loads:` but not otherwise visible to this query
375
375
  context.types.loadable_possible_types(arg_loads_type, context).include?(application_object_type)
376
376
  else
377
377
  true
@@ -3,6 +3,8 @@
3
3
  module GraphQL
4
4
  class Schema
5
5
  class Member
6
+ # @api public
7
+ # Shared methods for working with {Dataloader} inside GraphQL runtime objects.
6
8
  module HasDataloader
7
9
  # @return [GraphQL::Dataloader] The dataloader for the currently-running query
8
10
  def dataloader
@@ -37,7 +39,7 @@ module GraphQL
37
39
  source.load(find_by_value)
38
40
  end
39
41
 
40
- # Look up an associated record using a Rails association.
42
+ # Look up an associated record using a Rails association (via {Dataloader::ActiveRecordAssociationSource})
41
43
  # @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
44
  # @param record [ActiveRecord::Base] The object that the association belongs to.
43
45
  # @param scope [ActiveRecord::Relation] A scope to look up the associated record in
@@ -45,7 +47,7 @@ module GraphQL
45
47
  # @example Looking up a belongs_to on the current object
46
48
  # dataload_association(:parent) # Equivalent to `object.parent`, but dataloaded
47
49
  # @example Looking up an associated record on some other object
48
- # dataload_association(:post, comment) # Equivalent to `comment.post`, but dataloaded
50
+ # dataload_association(comment, :post) # Equivalent to `comment.post`, but dataloaded
49
51
  def dataload_association(record = object, association_name, scope: nil)
50
52
  source = if scope
51
53
  dataloader.with(Dataloader::ActiveRecordAssociationSource, association_name, scope)
@@ -18,6 +18,21 @@ module GraphQL
18
18
  directive(GraphQL::Schema::Directive::Deprecated, reason: text)
19
19
  end
20
20
  end
21
+
22
+ def self.extended(child_class)
23
+ super
24
+ child_class.extend(ClassMethods)
25
+ end
26
+
27
+ module ClassMethods
28
+ def deprecation_reason(new_reason = NOT_CONFIGURED)
29
+ if NOT_CONFIGURED.equal?(new_reason)
30
+ super()
31
+ else
32
+ self.deprecation_reason = new_reason
33
+ end
34
+ end
35
+ end
21
36
  end
22
37
  end
23
38
  end
@@ -8,8 +8,8 @@ module GraphQL
8
8
  new_memberships = []
9
9
  new_interfaces.each do |int|
10
10
  if int.is_a?(Module)
11
- unless int.include?(GraphQL::Schema::Interface)
12
- raise "#{int} cannot be implemented since it's not a GraphQL Interface. Use `include` for plain Ruby modules."
11
+ unless int.include?(GraphQL::Schema::Interface) && !int.is_a?(Class)
12
+ raise "#{int.respond_to?(:graphql_name) ? "#{int.graphql_name} (#{int})" : int.inspect} cannot be implemented since it's not a GraphQL Interface. Use `include` for plain Ruby modules."
13
13
  end
14
14
 
15
15
  new_memberships << int.type_membership_class.new(int, self, **options)
@@ -12,12 +12,26 @@ module GraphQL
12
12
 
13
13
  # @return [Schema::NonNull] Make a non-null-type representation of this type
14
14
  def to_non_null_type
15
- @to_non_null_type ||= GraphQL::Schema::NonNull.new(self)
15
+ @to_non_null_type || begin
16
+ t = GraphQL::Schema::NonNull.new(self)
17
+ if frozen?
18
+ t
19
+ else
20
+ @to_non_null_type = t
21
+ end
22
+ end
16
23
  end
17
24
 
18
25
  # @return [Schema::List] Make a list-type representation of this type
19
26
  def to_list_type
20
- @to_list_type ||= GraphQL::Schema::List.new(self)
27
+ @to_list_type || begin
28
+ t = GraphQL::Schema::List.new(self)
29
+ if frozen?
30
+ t
31
+ else
32
+ @to_list_type = t
33
+ end
34
+ end
21
35
  end
22
36
 
23
37
  # @return [Boolean] true if this is a non-nullable type. A nullable list of non-nullables is considered nullable.