graphql 2.0.30 → 2.3.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (157) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/install/mutation_root_generator.rb +2 -2
  3. data/lib/generators/graphql/install/templates/base_mutation.erb +2 -0
  4. data/lib/generators/graphql/install/templates/mutation_type.erb +2 -0
  5. data/lib/generators/graphql/install_generator.rb +3 -0
  6. data/lib/generators/graphql/templates/base_argument.erb +2 -0
  7. data/lib/generators/graphql/templates/base_connection.erb +2 -0
  8. data/lib/generators/graphql/templates/base_edge.erb +2 -0
  9. data/lib/generators/graphql/templates/base_enum.erb +2 -0
  10. data/lib/generators/graphql/templates/base_field.erb +2 -0
  11. data/lib/generators/graphql/templates/base_input_object.erb +2 -0
  12. data/lib/generators/graphql/templates/base_interface.erb +2 -0
  13. data/lib/generators/graphql/templates/base_object.erb +2 -0
  14. data/lib/generators/graphql/templates/base_resolver.erb +6 -0
  15. data/lib/generators/graphql/templates/base_scalar.erb +2 -0
  16. data/lib/generators/graphql/templates/base_union.erb +2 -0
  17. data/lib/generators/graphql/templates/graphql_controller.erb +2 -0
  18. data/lib/generators/graphql/templates/loader.erb +2 -0
  19. data/lib/generators/graphql/templates/mutation.erb +2 -0
  20. data/lib/generators/graphql/templates/node_type.erb +2 -0
  21. data/lib/generators/graphql/templates/query_type.erb +2 -0
  22. data/lib/generators/graphql/templates/schema.erb +5 -0
  23. data/lib/graphql/analysis/analyzer.rb +89 -0
  24. data/lib/graphql/analysis/field_usage.rb +82 -0
  25. data/lib/graphql/analysis/max_query_complexity.rb +20 -0
  26. data/lib/graphql/analysis/max_query_depth.rb +20 -0
  27. data/lib/graphql/analysis/query_complexity.rb +183 -0
  28. data/lib/graphql/analysis/query_depth.rb +58 -0
  29. data/lib/graphql/analysis/visitor.rb +282 -0
  30. data/lib/graphql/analysis.rb +92 -1
  31. data/lib/graphql/backtrace/inspect_result.rb +0 -12
  32. data/lib/graphql/backtrace/trace.rb +12 -15
  33. data/lib/graphql/coercion_error.rb +1 -9
  34. data/lib/graphql/dataloader/async_dataloader.rb +88 -0
  35. data/lib/graphql/dataloader/null_dataloader.rb +1 -1
  36. data/lib/graphql/dataloader/request.rb +5 -0
  37. data/lib/graphql/dataloader/source.rb +11 -3
  38. data/lib/graphql/dataloader.rb +112 -142
  39. data/lib/graphql/duration_encoding_error.rb +16 -0
  40. data/lib/graphql/execution/interpreter/argument_value.rb +5 -1
  41. data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +175 -0
  42. data/lib/graphql/execution/interpreter/runtime.rb +163 -365
  43. data/lib/graphql/execution/interpreter.rb +92 -158
  44. data/lib/graphql/execution/lookahead.rb +88 -21
  45. data/lib/graphql/introspection/dynamic_fields.rb +1 -1
  46. data/lib/graphql/introspection/entry_points.rb +11 -5
  47. data/lib/graphql/introspection/schema_type.rb +3 -1
  48. data/lib/graphql/language/block_string.rb +34 -18
  49. data/lib/graphql/language/definition_slice.rb +1 -1
  50. data/lib/graphql/language/document_from_schema_definition.rb +38 -38
  51. data/lib/graphql/language/lexer.rb +305 -193
  52. data/lib/graphql/language/nodes.rb +113 -66
  53. data/lib/graphql/language/parser.rb +787 -1986
  54. data/lib/graphql/language/printer.rb +303 -146
  55. data/lib/graphql/language/sanitized_printer.rb +20 -22
  56. data/lib/graphql/language/static_visitor.rb +167 -0
  57. data/lib/graphql/language/visitor.rb +20 -81
  58. data/lib/graphql/language.rb +61 -0
  59. data/lib/graphql/load_application_object_failed_error.rb +5 -1
  60. data/lib/graphql/pagination/array_connection.rb +6 -6
  61. data/lib/graphql/pagination/connection.rb +28 -1
  62. data/lib/graphql/pagination/mongoid_relation_connection.rb +1 -2
  63. data/lib/graphql/query/context/scoped_context.rb +101 -0
  64. data/lib/graphql/query/context.rb +66 -131
  65. data/lib/graphql/query/null_context.rb +4 -11
  66. data/lib/graphql/query/validation_pipeline.rb +4 -4
  67. data/lib/graphql/query/variables.rb +3 -3
  68. data/lib/graphql/query.rb +17 -26
  69. data/lib/graphql/railtie.rb +9 -6
  70. data/lib/graphql/rake_task.rb +3 -12
  71. data/lib/graphql/rubocop/graphql/base_cop.rb +1 -1
  72. data/lib/graphql/schema/addition.rb +21 -11
  73. data/lib/graphql/schema/argument.rb +43 -8
  74. data/lib/graphql/schema/base_64_encoder.rb +3 -5
  75. data/lib/graphql/schema/build_from_definition.rb +9 -12
  76. data/lib/graphql/schema/directive/one_of.rb +12 -0
  77. data/lib/graphql/schema/directive/specified_by.rb +14 -0
  78. data/lib/graphql/schema/directive.rb +3 -1
  79. data/lib/graphql/schema/enum.rb +3 -3
  80. data/lib/graphql/schema/field/connection_extension.rb +1 -15
  81. data/lib/graphql/schema/field/scope_extension.rb +8 -1
  82. data/lib/graphql/schema/field.rb +49 -35
  83. data/lib/graphql/schema/has_single_input_argument.rb +157 -0
  84. data/lib/graphql/schema/input_object.rb +4 -4
  85. data/lib/graphql/schema/interface.rb +10 -10
  86. data/lib/graphql/schema/introspection_system.rb +4 -2
  87. data/lib/graphql/schema/late_bound_type.rb +4 -0
  88. data/lib/graphql/schema/list.rb +2 -2
  89. data/lib/graphql/schema/loader.rb +2 -3
  90. data/lib/graphql/schema/member/base_dsl_methods.rb +2 -1
  91. data/lib/graphql/schema/member/has_arguments.rb +63 -73
  92. data/lib/graphql/schema/member/has_directives.rb +1 -1
  93. data/lib/graphql/schema/member/has_fields.rb +8 -5
  94. data/lib/graphql/schema/member/has_interfaces.rb +23 -9
  95. data/lib/graphql/schema/member/relay_shortcuts.rb +1 -1
  96. data/lib/graphql/schema/member/scoped.rb +19 -0
  97. data/lib/graphql/schema/member/type_system_helpers.rb +1 -2
  98. data/lib/graphql/schema/member/validates_input.rb +3 -3
  99. data/lib/graphql/schema/mutation.rb +7 -0
  100. data/lib/graphql/schema/object.rb +8 -0
  101. data/lib/graphql/schema/printer.rb +8 -7
  102. data/lib/graphql/schema/relay_classic_mutation.rb +6 -128
  103. data/lib/graphql/schema/resolver.rb +27 -13
  104. data/lib/graphql/schema/scalar.rb +3 -3
  105. data/lib/graphql/schema/subscription.rb +11 -4
  106. data/lib/graphql/schema/union.rb +1 -1
  107. data/lib/graphql/schema/unique_within_type.rb +1 -1
  108. data/lib/graphql/schema/warden.rb +96 -95
  109. data/lib/graphql/schema.rb +323 -102
  110. data/lib/graphql/static_validation/all_rules.rb +1 -1
  111. data/lib/graphql/static_validation/base_visitor.rb +1 -1
  112. data/lib/graphql/static_validation/literal_validator.rb +2 -3
  113. data/lib/graphql/static_validation/rules/fields_will_merge.rb +2 -2
  114. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +1 -1
  115. data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +2 -2
  116. data/lib/graphql/static_validation/validation_context.rb +5 -5
  117. data/lib/graphql/static_validation/validator.rb +3 -0
  118. data/lib/graphql/static_validation.rb +0 -1
  119. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +4 -3
  120. data/lib/graphql/subscriptions/broadcast_analyzer.rb +1 -1
  121. data/lib/graphql/subscriptions/event.rb +8 -2
  122. data/lib/graphql/subscriptions/serialize.rb +2 -0
  123. data/lib/graphql/subscriptions.rb +15 -13
  124. data/lib/graphql/testing/helpers.rb +151 -0
  125. data/lib/graphql/testing.rb +2 -0
  126. data/lib/graphql/tracing/appoptics_trace.rb +2 -2
  127. data/lib/graphql/tracing/appoptics_tracing.rb +2 -2
  128. data/lib/graphql/tracing/legacy_hooks_trace.rb +74 -0
  129. data/lib/graphql/tracing/platform_tracing.rb +3 -1
  130. data/lib/graphql/tracing/{prometheus_tracing → prometheus_trace}/graphql_collector.rb +3 -1
  131. data/lib/graphql/tracing/prometheus_trace.rb +9 -9
  132. data/lib/graphql/tracing/sentry_trace.rb +112 -0
  133. data/lib/graphql/tracing/trace.rb +1 -0
  134. data/lib/graphql/tracing.rb +3 -1
  135. data/lib/graphql/type_kinds.rb +1 -1
  136. data/lib/graphql/types/iso_8601_duration.rb +77 -0
  137. data/lib/graphql/types/relay/connection_behaviors.rb +32 -2
  138. data/lib/graphql/types/relay/edge_behaviors.rb +7 -0
  139. data/lib/graphql/types.rb +1 -0
  140. data/lib/graphql/version.rb +1 -1
  141. data/lib/graphql.rb +13 -13
  142. data/readme.md +12 -2
  143. metadata +33 -26
  144. data/lib/graphql/analysis/ast/analyzer.rb +0 -84
  145. data/lib/graphql/analysis/ast/field_usage.rb +0 -57
  146. data/lib/graphql/analysis/ast/max_query_complexity.rb +0 -22
  147. data/lib/graphql/analysis/ast/max_query_depth.rb +0 -22
  148. data/lib/graphql/analysis/ast/query_complexity.rb +0 -230
  149. data/lib/graphql/analysis/ast/query_depth.rb +0 -55
  150. data/lib/graphql/analysis/ast/visitor.rb +0 -276
  151. data/lib/graphql/analysis/ast.rb +0 -81
  152. data/lib/graphql/deprecation.rb +0 -9
  153. data/lib/graphql/filter.rb +0 -59
  154. data/lib/graphql/language/parser.y +0 -560
  155. data/lib/graphql/schema/base_64_bp.rb +0 -26
  156. data/lib/graphql/static_validation/type_stack.rb +0 -216
  157. data/lib/graphql/subscriptions/instrumentation.rb +0 -28
@@ -12,7 +12,7 @@ module GraphQL
12
12
  @possible_types = {}
13
13
  @types = {}
14
14
  @union_memberships = {}
15
- @references = Hash.new { |h, k| h[k] = [] }
15
+ @references = Hash.new { |h, k| h[k] = Set.new }
16
16
  @arguments_with_default_values = []
17
17
  add_type_and_traverse(new_types)
18
18
  end
@@ -20,7 +20,7 @@ module GraphQL
20
20
  private
21
21
 
22
22
  def references_to(thing, from:)
23
- @references[thing] << from
23
+ @references[thing].add(from)
24
24
  end
25
25
 
26
26
  def get_type(name)
@@ -95,7 +95,7 @@ module GraphQL
95
95
  # It's a union with possible_types
96
96
  # Replace the item by class name
97
97
  owner.assign_type_membership_object_type(type)
98
- @possible_types[owner.graphql_name] = owner.possible_types
98
+ @possible_types[owner] = owner.possible_types
99
99
  elsif type.kind.interface? && (owner.kind.object? || owner.kind.interface?)
100
100
  new_interfaces = []
101
101
  owner.interfaces.each do |int_t|
@@ -110,7 +110,7 @@ module GraphQL
110
110
  end
111
111
  owner.implements(*new_interfaces)
112
112
  new_interfaces.each do |int|
113
- pt = @possible_types[int.graphql_name] ||= []
113
+ pt = @possible_types[int] ||= []
114
114
  if !pt.include?(owner) && owner.is_a?(Class)
115
115
  pt << owner
116
116
  end
@@ -126,6 +126,7 @@ module GraphQL
126
126
  @types[type.graphql_name] = type
127
127
  when GraphQL::Schema::Field, GraphQL::Schema::Argument
128
128
  orig_type = owner.type
129
+ unwrapped_t = type
129
130
  # Apply list/non-null wrapper as needed
130
131
  if orig_type.respond_to?(:of_type)
131
132
  transforms = []
@@ -142,6 +143,7 @@ module GraphQL
142
143
  transforms.reverse_each { |t| type = type.public_send(t) }
143
144
  end
144
145
  owner.type = type
146
+ references_to(unwrapped_t, from: owner)
145
147
  else
146
148
  raise "Unexpected update: #{owner.inspect} #{type.inspect}"
147
149
  end
@@ -164,7 +166,9 @@ module GraphQL
164
166
  @directives << type
165
167
  type.all_argument_definitions.each do |arg|
166
168
  arg_type = arg.type.unwrap
167
- references_to(arg_type, from: arg)
169
+ if !arg_type.is_a?(GraphQL::Schema::LateBoundType)
170
+ references_to(arg_type, from: arg)
171
+ end
168
172
  path.push(arg.graphql_name)
169
173
  add_type(arg_type, owner: arg, late_types: late_types, path: path)
170
174
  path.pop
@@ -187,14 +191,18 @@ module GraphQL
187
191
  type.all_field_definitions.each do |field|
188
192
  name = field.graphql_name
189
193
  field_type = field.type.unwrap
190
- references_to(field_type, from: field)
194
+ if !field_type.is_a?(GraphQL::Schema::LateBoundType)
195
+ references_to(field_type, from: field)
196
+ end
191
197
  path.push(name)
192
198
  add_type(field_type, owner: field, late_types: late_types, path: path)
193
199
  add_directives_from(field)
194
200
  field.all_argument_definitions.each do |arg|
195
201
  add_directives_from(arg)
196
202
  arg_type = arg.type.unwrap
197
- references_to(arg_type, from: arg)
203
+ if !arg_type.is_a?(GraphQL::Schema::LateBoundType)
204
+ references_to(arg_type, from: arg)
205
+ end
198
206
  path.push(arg.graphql_name)
199
207
  add_type(arg_type, owner: arg, late_types: late_types, path: path)
200
208
  path.pop
@@ -209,7 +217,9 @@ module GraphQL
209
217
  type.all_argument_definitions.each do |arg|
210
218
  add_directives_from(arg)
211
219
  arg_type = arg.type.unwrap
212
- references_to(arg_type, from: arg)
220
+ if !arg_type.is_a?(GraphQL::Schema::LateBoundType)
221
+ references_to(arg_type, from: arg)
222
+ end
213
223
  path.push(arg.graphql_name)
214
224
  add_type(arg_type, owner: arg, late_types: late_types, path: path)
215
225
  path.pop
@@ -219,7 +229,7 @@ module GraphQL
219
229
  end
220
230
  end
221
231
  if type.kind.union?
222
- @possible_types[type.graphql_name] = type.all_possible_types
232
+ @possible_types[type] = type.all_possible_types
223
233
  path.push("possible_types")
224
234
  type.all_possible_types.each do |t|
225
235
  add_type(t, owner: type, late_types: late_types, path: path)
@@ -234,7 +244,7 @@ module GraphQL
234
244
  path.pop
235
245
  end
236
246
  if type.kind.object?
237
- possible_types_for_this_name = @possible_types[type.graphql_name] ||= []
247
+ possible_types_for_this_name = @possible_types[type] ||= []
238
248
  possible_types_for_this_name << type
239
249
  end
240
250
 
@@ -246,7 +256,7 @@ module GraphQL
246
256
  interface_type = interface_type_membership.abstract_type
247
257
  # We can get these now; we'll have to get late-bound types later
248
258
  if interface_type.is_a?(Module) && type.is_a?(Class)
249
- implementers = @possible_types[interface_type.graphql_name] ||= []
259
+ implementers = @possible_types[interface_type] ||= []
250
260
  implementers << type
251
261
  end
252
262
  when String, Schema::LateBoundType
@@ -206,8 +206,8 @@ module GraphQL
206
206
  # Used by the runtime.
207
207
  # @api private
208
208
  def prepare_value(obj, value, context: nil)
209
- if value.is_a?(GraphQL::Schema::InputObject)
210
- value = value.prepare
209
+ if type.unwrap.kind.input_object?
210
+ value = recursively_prepare_input_object(value, type)
211
211
  end
212
212
 
213
213
  Schema::Validator.validate!(validators, obj, context, value)
@@ -221,8 +221,13 @@ module GraphQL
221
221
  #
222
222
  # This will have to be called later, when the runtime object _is_ available.
223
223
  value
224
- else
224
+ elsif obj.respond_to?(@prepare)
225
225
  obj.public_send(@prepare, value)
226
+ elsif owner.respond_to?(@prepare)
227
+ owner.public_send(@prepare, value, context || obj.context)
228
+ else
229
+ raise "Invalid prepare for #{@owner.name}.name: #{@prepare.inspect}. "\
230
+ "Could not find prepare method #{@prepare} on #{obj.class} or #{owner}."
226
231
  end
227
232
  elsif @prepare.respond_to?(:call)
228
233
  @prepare.call(value, context || obj.context)
@@ -285,6 +290,7 @@ module GraphQL
285
290
  # TODO code smell to access such a deeply-nested constant in a distant module
286
291
  argument_values[arg_key] = GraphQL::Execution::Interpreter::ArgumentValue.new(
287
292
  value: resolved_loaded_value,
293
+ original_value: resolved_coerced_value,
288
294
  definition: self,
289
295
  default_used: default_used,
290
296
  )
@@ -306,10 +312,15 @@ module GraphQL
306
312
  context.query.after_lazy(custom_loaded_value) do |custom_value|
307
313
  if loads
308
314
  if type.list?
309
- loaded_values = custom_value.each_with_index.map { |custom_val, idx|
310
- id = coerced_value[idx]
311
- load_method_owner.authorize_application_object(self, id, context, custom_val)
312
- }
315
+ loaded_values = []
316
+ context.dataloader.run_isolated do
317
+ custom_value.each_with_index.map { |custom_val, idx|
318
+ id = coerced_value[idx]
319
+ context.dataloader.append_job do
320
+ loaded_values[idx] = load_method_owner.authorize_application_object(self, id, context, custom_val)
321
+ end
322
+ }
323
+ end
313
324
  context.schema.after_any_lazies(loaded_values, &:itself)
314
325
  else
315
326
  load_method_owner.authorize_application_object(self, coerced_value, context, custom_loaded_value)
@@ -320,7 +331,16 @@ module GraphQL
320
331
  end
321
332
  elsif loads
322
333
  if type.list?
323
- loaded_values = coerced_value.map { |val| load_method_owner.load_and_authorize_application_object(self, val, context) }
334
+ loaded_values = []
335
+ # We want to run these list items all together,
336
+ # but we also need to wait for the result so we can return it :S
337
+ context.dataloader.run_isolated do
338
+ coerced_value.each_with_index { |val, idx|
339
+ context.dataloader.append_job do
340
+ loaded_values[idx] = load_method_owner.load_and_authorize_application_object(self, val, context)
341
+ end
342
+ }
343
+ end
324
344
  context.schema.after_any_lazies(loaded_values, &:itself)
325
345
  else
326
346
  load_method_owner.load_and_authorize_application_object(self, coerced_value, context)
@@ -367,6 +387,21 @@ module GraphQL
367
387
 
368
388
  private
369
389
 
390
+ def recursively_prepare_input_object(value, type)
391
+ if type.non_null?
392
+ type = type.of_type
393
+ end
394
+
395
+ if type.list? && !value.nil?
396
+ inner_type = type.of_type
397
+ value.map { |v| recursively_prepare_input_object(v, inner_type) }
398
+ elsif value.is_a?(GraphQL::Schema::InputObject)
399
+ value.prepare
400
+ else
401
+ value
402
+ end
403
+ end
404
+
370
405
  def validate_input_type(input_type)
371
406
  if input_type.is_a?(String) || input_type.is_a?(GraphQL::Schema::LateBoundType)
372
407
  # Do nothing; assume this will be validated later
@@ -1,18 +1,16 @@
1
1
  # frozen_string_literal: true
2
-
3
- require 'graphql/schema/base_64_bp'
4
-
2
+ require "base64"
5
3
  module GraphQL
6
4
  class Schema
7
5
  # @api private
8
6
  module Base64Encoder
9
7
  def self.encode(unencoded_text, nonce: false)
10
- Base64Bp.urlsafe_encode64(unencoded_text, padding: false)
8
+ Base64.urlsafe_encode64(unencoded_text, padding: false)
11
9
  end
12
10
 
13
11
  def self.decode(encoded_text, nonce: false)
14
12
  # urlsafe_decode64 is for forward compatibility
15
- Base64Bp.urlsafe_decode64(encoded_text)
13
+ Base64.urlsafe_decode64(encoded_text)
16
14
  rescue ArgumentError
17
15
  raise GraphQL::ExecutionError, "Invalid input: #{encoded_text.inspect}"
18
16
  end
@@ -7,10 +7,16 @@ module GraphQL
7
7
  class << self
8
8
  # @see {Schema.from_definition}
9
9
  def from_definition(schema_superclass, definition_string, parser: GraphQL.default_parser, **kwargs)
10
+ if defined?(parser::SchemaParser)
11
+ parser = parser::SchemaParser
12
+ end
10
13
  from_document(schema_superclass, parser.parse(definition_string), **kwargs)
11
14
  end
12
15
 
13
16
  def from_definition_path(schema_superclass, definition_path, parser: GraphQL.default_parser, **kwargs)
17
+ if defined?(parser::SchemaParser)
18
+ parser = parser::SchemaParser
19
+ end
14
20
  from_document(schema_superclass, parser.parse_file(definition_path), **kwargs)
15
21
  end
16
22
 
@@ -120,10 +126,12 @@ module GraphQL
120
126
 
121
127
  builder = self
122
128
 
129
+ found_types = types.values
123
130
  schema_class = Class.new(schema_superclass) do
124
131
  begin
125
132
  # Add these first so that there's some chance of resolving late-bound types
126
- orphan_types types.values
133
+ add_type_and_traverse(found_types, root: false)
134
+ orphan_types(found_types.select { |t| t.respond_to?(:kind) && t.kind.object? })
127
135
  query query_root_type
128
136
  mutation mutation_root_type
129
137
  subscription subscription_root_type
@@ -432,14 +440,12 @@ module GraphQL
432
440
  builder = self
433
441
 
434
442
  field_definitions.each do |field_definition|
435
- type_name = resolve_type_name(field_definition.type)
436
443
  resolve_method_name = -"resolve_field_#{field_definition.name}"
437
444
  schema_field_defn = owner.field(
438
445
  field_definition.name,
439
446
  description: field_definition.description,
440
447
  type: type_resolver.call(field_definition.type),
441
448
  null: true,
442
- connection: type_name.end_with?("Connection"),
443
449
  connection_extension: nil,
444
450
  deprecation_reason: build_deprecation_reason(field_definition.directives),
445
451
  ast_node: field_definition,
@@ -487,15 +493,6 @@ module GraphQL
487
493
  }
488
494
  resolve_type_proc
489
495
  end
490
-
491
- def resolve_type_name(type)
492
- case type
493
- when GraphQL::Language::Nodes::TypeName
494
- return type.name
495
- else
496
- resolve_type_name(type.of_type)
497
- end
498
- end
499
496
  end
500
497
 
501
498
  private_constant :Builder
@@ -6,6 +6,18 @@ module GraphQL
6
6
  description "Requires that exactly one field must be supplied and that field must not be `null`."
7
7
  locations(GraphQL::Schema::Directive::INPUT_OBJECT)
8
8
  default_directive true
9
+
10
+ def initialize(...)
11
+ super
12
+
13
+ owner.extend(IsOneOf)
14
+ end
15
+
16
+ module IsOneOf
17
+ def one_of?
18
+ true
19
+ end
20
+ end
9
21
  end
10
22
  end
11
23
  end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+ module GraphQL
3
+ class Schema
4
+ class Directive < GraphQL::Schema::Member
5
+ class SpecifiedBy < GraphQL::Schema::Directive
6
+ description "Exposes a URL that specifies the behavior of this scalar."
7
+ locations(GraphQL::Schema::Directive::SCALAR)
8
+ default_directive true
9
+
10
+ argument :url, String, description: "The URL that specifies the behavior of this scalar."
11
+ end
12
+ end
13
+ end
14
+ end
@@ -119,7 +119,7 @@ module GraphQL
119
119
  # - lazy resolution
120
120
  # Probably, those won't be needed here, since these are configuration arguments,
121
121
  # not runtime arguments.
122
- @arguments = self.class.coerce_arguments(nil, arguments, Query::NullContext)
122
+ @arguments = self.class.coerce_arguments(nil, arguments, Query::NullContext.instance)
123
123
  end
124
124
 
125
125
  def graphql_name
@@ -188,6 +188,8 @@ module GraphQL
188
188
  assert_has_location(SCALAR)
189
189
  elsif @owner < GraphQL::Schema
190
190
  assert_has_location(SCHEMA)
191
+ elsif @owner < GraphQL::Schema::Resolver
192
+ assert_has_location(FIELD_DEFINITION)
191
193
  else
192
194
  raise "Unexpected directive owner class: #{@owner}"
193
195
  end
@@ -14,7 +14,7 @@ module GraphQL
14
14
  # # ONIONS
15
15
  # # PEPPERS
16
16
  # # }
17
- # class PizzaTopping < GraphQL::Enum
17
+ # class PizzaTopping < GraphQL::Schema::Enum
18
18
  # value :MUSHROOMS
19
19
  # value :ONIONS
20
20
  # value :PEPPERS
@@ -68,7 +68,7 @@ module GraphQL
68
68
  end
69
69
 
70
70
  # @return [Array<GraphQL::Schema::EnumValue>] Possible values of this enum
71
- def enum_values(context = GraphQL::Query::NullContext)
71
+ def enum_values(context = GraphQL::Query::NullContext.instance)
72
72
  inherited_values = superclass.respond_to?(:enum_values) ? superclass.enum_values(context) : nil
73
73
  visible_values = []
74
74
  warden = Warden.from_context(context)
@@ -110,7 +110,7 @@ module GraphQL
110
110
  end
111
111
 
112
112
  # @return [Hash<String => GraphQL::Schema::EnumValue>] Possible values of this enum, keyed by name.
113
- def values(context = GraphQL::Query::NullContext)
113
+ def values(context = GraphQL::Query::NullContext.instance)
114
114
  enum_values(context).each_with_object({}) { |val, obj| obj[val.graphql_name] = val }
115
115
  end
116
116
 
@@ -54,23 +54,9 @@ module GraphQL
54
54
  value.edge_class = custom_t
55
55
  end
56
56
  value
57
- elsif context.schema.new_connections?
57
+ else
58
58
  context.namespace(:connections)[:all_wrappers] ||= context.schema.connections.all_wrappers
59
59
  context.schema.connections.wrap(field, object.object, value, original_arguments, context)
60
- else
61
- if object.is_a?(GraphQL::Schema::Object)
62
- object = object.object
63
- end
64
- connection_class = GraphQL::Relay::BaseConnection.connection_for_nodes(value)
65
- connection_class.new(
66
- value,
67
- original_arguments,
68
- field: field,
69
- max_page_size: field.max_page_size,
70
- default_page_size: field.default_page_size,
71
- parent: object,
72
- context: context,
73
- )
74
60
  end
75
61
  end
76
62
  end
@@ -10,7 +10,14 @@ module GraphQL
10
10
  else
11
11
  ret_type = @field.type.unwrap
12
12
  if ret_type.respond_to?(:scope_items)
13
- ret_type.scope_items(value, context)
13
+ scoped_items = ret_type.scope_items(value, context)
14
+ if !scoped_items.equal?(value) && !ret_type.reauthorize_scoped_objects
15
+ if (current_runtime_state = Thread.current[:__graphql_runtime_info]) &&
16
+ (query_runtime_state = current_runtime_state[context.query])
17
+ query_runtime_state.was_authorized_by_scope_items = true
18
+ end
19
+ end
20
+ scoped_items
14
21
  else
15
22
  value
16
23
  end
@@ -41,6 +41,14 @@ module GraphQL
41
41
  end
42
42
  end
43
43
 
44
+ def directives
45
+ if @resolver_class
46
+ @resolver_class.directives
47
+ else
48
+ super
49
+ end
50
+ end
51
+
44
52
  # @return [Class] The thing this field was defined on (type, mutation, resolver)
45
53
  attr_accessor :owner
46
54
 
@@ -138,7 +146,7 @@ module GraphQL
138
146
  # As a last ditch, try to force loading the return type:
139
147
  type.unwrap.name
140
148
  end
141
- @connection = return_type_name.end_with?("Connection")
149
+ @connection = return_type_name.end_with?("Connection") && return_type_name != "Connection"
142
150
  else
143
151
  @connection
144
152
  end
@@ -218,8 +226,8 @@ module GraphQL
218
226
  # @param ast_node [Language::Nodes::FieldDefinition, nil] If this schema was parsed from definition, this AST node defined the field
219
227
  # @param method_conflict_warning [Boolean] If false, skip the warning if this field's method conflicts with a built-in method
220
228
  # @param validates [Array<Hash>] Configurations for validating this field
221
- # @fallback_value [Object] A fallback value if the method is not defined
222
- def initialize(type: nil, name: nil, owner: nil, null: nil, description: NOT_CONFIGURED, deprecation_reason: nil, method: nil, hash_key: nil, dig: nil, resolver_method: nil, connection: nil, max_page_size: NOT_CONFIGURED, default_page_size: NOT_CONFIGURED, scope: nil, introspection: false, camelize: true, trace: nil, complexity: nil, ast_node: nil, extras: EMPTY_ARRAY, extensions: EMPTY_ARRAY, connection_extension: self.class.connection_extension, resolver_class: nil, subscription_scope: nil, relay_node_field: false, relay_nodes_field: false, method_conflict_warning: true, broadcastable: NOT_CONFIGURED, arguments: EMPTY_HASH, directives: EMPTY_HASH, validates: EMPTY_ARRAY, fallback_value: NOT_CONFIGURED, &definition_block)
229
+ # @param fallback_value [Object] A fallback value if the method is not defined
230
+ def initialize(type: nil, name: nil, owner: nil, null: nil, description: NOT_CONFIGURED, deprecation_reason: nil, method: nil, hash_key: nil, dig: nil, resolver_method: nil, connection: nil, max_page_size: NOT_CONFIGURED, default_page_size: NOT_CONFIGURED, scope: nil, introspection: false, camelize: true, trace: nil, complexity: nil, ast_node: nil, extras: EMPTY_ARRAY, extensions: EMPTY_ARRAY, connection_extension: self.class.connection_extension, resolver_class: nil, subscription_scope: nil, relay_node_field: false, relay_nodes_field: false, method_conflict_warning: true, broadcastable: NOT_CONFIGURED, arguments: EMPTY_HASH, directives: EMPTY_HASH, validates: EMPTY_ARRAY, fallback_value: NOT_CONFIGURED, dynamic_introspection: false, &definition_block)
223
231
  if name.nil?
224
232
  raise ArgumentError, "missing first `name` argument or keyword `name:`"
225
233
  end
@@ -267,6 +275,7 @@ module GraphQL
267
275
  @method_sym = method_name.to_sym
268
276
  @resolver_method = (resolver_method || name_s).to_sym
269
277
  @complexity = complexity
278
+ @dynamic_introspection = dynamic_introspection
270
279
  @return_type_expr = type
271
280
  @return_type_null = if !null.nil?
272
281
  null
@@ -351,6 +360,8 @@ module GraphQL
351
360
  @call_after_define = true
352
361
  end
353
362
 
363
+ attr_accessor :dynamic_introspection
364
+
354
365
  # If true, subscription updates with this field can be shared between viewers
355
366
  # @return [Boolean, nil]
356
367
  # @see GraphQL::Subscriptions::BroadcastAnalyzer
@@ -468,6 +479,8 @@ module GraphQL
468
479
  if arguments[:last] && (max_possible_page_size.nil? || arguments[:last] > max_possible_page_size)
469
480
  max_possible_page_size = arguments[:last]
470
481
  end
482
+ elsif arguments.is_a?(GraphQL::UnauthorizedError)
483
+ raise arguments
471
484
  end
472
485
 
473
486
  if max_possible_page_size.nil?
@@ -480,41 +493,24 @@ module GraphQL
480
493
  metadata_complexity = 0
481
494
  lookahead = GraphQL::Execution::Lookahead.new(query: query, field: self, ast_nodes: nodes, owner_type: owner)
482
495
 
483
- if (page_info_lookahead = lookahead.selection(:page_info)).selected?
484
- metadata_complexity += 1 # pageInfo
485
- metadata_complexity += page_info_lookahead.selections.size # subfields
486
- end
487
-
488
- if lookahead.selects?(:total) || lookahead.selects?(:total_count) || lookahead.selects?(:count)
489
- metadata_complexity += 1
496
+ lookahead.selections.each do |next_lookahead|
497
+ # this includes `pageInfo`, `nodes` and `edges` and any custom fields
498
+ # TODO this doesn't support procs yet -- unlikely to need it.
499
+ metadata_complexity += next_lookahead.field.complexity
500
+ if next_lookahead.name != :nodes && next_lookahead.name != :edges
501
+ # subfields, eg, for pageInfo -- assumes no subselections
502
+ metadata_complexity += next_lookahead.selections.size
503
+ end
490
504
  end
491
505
 
492
- nodes_edges_complexity = 0
493
- nodes_edges_complexity += 1 if lookahead.selects?(:edges)
494
- nodes_edges_complexity += 1 if lookahead.selects?(:nodes)
495
-
496
506
  # Possible bug: selections on `edges` and `nodes` are _both_ multiplied here. Should they be?
497
- items_complexity = child_complexity - metadata_complexity - nodes_edges_complexity
498
- # Add 1 for _this_ field
499
- 1 + (max_possible_page_size * items_complexity) + metadata_complexity + nodes_edges_complexity
507
+ items_complexity = child_complexity - metadata_complexity
508
+ subfields_complexity = (max_possible_page_size * items_complexity) + metadata_complexity
509
+ # Apply this field's own complexity
510
+ apply_own_complexity_to(subfields_complexity, query, nodes)
500
511
  end
501
512
  else
502
- defined_complexity = complexity
503
- case defined_complexity
504
- when Proc
505
- arguments = query.arguments_for(nodes.first, self)
506
- if arguments.is_a?(GraphQL::ExecutionError)
507
- return child_complexity
508
- elsif arguments.respond_to?(:keyword_arguments)
509
- arguments = arguments.keyword_arguments
510
- end
511
-
512
- defined_complexity.call(query.context, arguments, child_complexity)
513
- when Numeric
514
- defined_complexity + child_complexity
515
- else
516
- raise("Invalid complexity: #{defined_complexity.inspect} on #{path} (#{inspect})")
517
- end
513
+ apply_own_complexity_to(child_complexity, query, nodes)
518
514
  end
519
515
  end
520
516
 
@@ -659,7 +655,7 @@ module GraphQL
659
655
  method_to_call = nil
660
656
  method_args = nil
661
657
 
662
- Schema::Validator.validate!(validators, application_object, query_ctx, args)
658
+ @own_validators && Schema::Validator.validate!(validators, application_object, query_ctx, args)
663
659
 
664
660
  query_ctx.query.after_lazy(self.authorized?(application_object, args, query_ctx)) do |is_authorized|
665
661
  if is_authorized
@@ -701,7 +697,7 @@ module GraphQL
701
697
  inner_object.dig(*@dig_keys)
702
698
  elsif inner_object.key?(@method_sym)
703
699
  inner_object[@method_sym]
704
- elsif inner_object.key?(@method_str)
700
+ elsif inner_object.key?(@method_str) || !inner_object.default_proc.nil?
705
701
  inner_object[@method_str]
706
702
  elsif @fallback_value != NOT_CONFIGURED
707
703
  @fallback_value
@@ -879,6 +875,24 @@ ERR
879
875
  yield(obj, args)
880
876
  end
881
877
  end
878
+
879
+ def apply_own_complexity_to(child_complexity, query, nodes)
880
+ case (own_complexity = complexity)
881
+ when Numeric
882
+ own_complexity + child_complexity
883
+ when Proc
884
+ arguments = query.arguments_for(nodes.first, self)
885
+ if arguments.is_a?(GraphQL::ExecutionError)
886
+ return child_complexity
887
+ elsif arguments.respond_to?(:keyword_arguments)
888
+ arguments = arguments.keyword_arguments
889
+ end
890
+
891
+ own_complexity.call(query.context, arguments, child_complexity)
892
+ else
893
+ raise ArgumentError, "Invalid complexity for #{self.path}: #{own_complexity.inspect}"
894
+ end
895
+ end
882
896
  end
883
897
  end
884
898
  end