graphql 1.9.17 → 1.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (142) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/templates/schema.erb +7 -0
  3. data/lib/graphql/analysis/ast/field_usage.rb +1 -1
  4. data/lib/graphql/analysis/ast/visitor.rb +3 -3
  5. data/lib/graphql/analysis/ast.rb +12 -11
  6. data/lib/graphql/argument.rb +7 -35
  7. data/lib/graphql/backtrace/table.rb +10 -2
  8. data/lib/graphql/base_type.rb +4 -0
  9. data/lib/graphql/compatibility/query_parser_specification/parse_error_specification.rb +5 -9
  10. data/lib/graphql/define/assign_enum_value.rb +1 -1
  11. data/lib/graphql/define/assign_object_field.rb +3 -3
  12. data/lib/graphql/define/defined_object_proxy.rb +8 -2
  13. data/lib/graphql/define/instance_definable.rb +10 -106
  14. data/lib/graphql/directive/deprecated_directive.rb +1 -12
  15. data/lib/graphql/directive.rb +4 -1
  16. data/lib/graphql/enum_type.rb +5 -71
  17. data/lib/graphql/execution/directive_checks.rb +2 -2
  18. data/lib/graphql/execution/errors.rb +2 -3
  19. data/lib/graphql/execution/execute.rb +1 -1
  20. data/lib/graphql/execution/interpreter/runtime.rb +106 -55
  21. data/lib/graphql/execution/interpreter.rb +5 -11
  22. data/lib/graphql/execution/lazy/lazy_method_map.rb +4 -0
  23. data/lib/graphql/execution/lookahead.rb +5 -5
  24. data/lib/graphql/execution/multiplex.rb +13 -3
  25. data/lib/graphql/field.rb +9 -117
  26. data/lib/graphql/filter.rb +1 -1
  27. data/lib/graphql/function.rb +1 -30
  28. data/lib/graphql/input_object_type.rb +2 -24
  29. data/lib/graphql/interface_type.rb +2 -23
  30. data/lib/graphql/introspection/base_object.rb +2 -5
  31. data/lib/graphql/introspection/directive_type.rb +1 -1
  32. data/lib/graphql/introspection/entry_points.rb +7 -7
  33. data/lib/graphql/introspection/input_value_type.rb +27 -9
  34. data/lib/graphql/introspection/schema_type.rb +1 -6
  35. data/lib/graphql/introspection/type_type.rb +5 -5
  36. data/lib/graphql/language/definition_slice.rb +21 -10
  37. data/lib/graphql/language/document_from_schema_definition.rb +50 -44
  38. data/lib/graphql/language/nodes.rb +3 -3
  39. data/lib/graphql/language/parser.rb +644 -646
  40. data/lib/graphql/language/parser.y +6 -4
  41. data/lib/graphql/language.rb +1 -1
  42. data/lib/graphql/non_null_type.rb +0 -10
  43. data/lib/graphql/object_type.rb +1 -21
  44. data/lib/graphql/pagination/active_record_relation_connection.rb +35 -0
  45. data/lib/graphql/pagination/array_connection.rb +77 -0
  46. data/lib/graphql/pagination/connection.rb +171 -0
  47. data/lib/graphql/pagination/connections.rb +108 -0
  48. data/lib/graphql/pagination/mongoid_relation_connection.rb +25 -0
  49. data/lib/graphql/pagination/relation_connection.rb +151 -0
  50. data/lib/graphql/pagination/sequel_dataset_connection.rb +28 -0
  51. data/lib/graphql/pagination.rb +6 -0
  52. data/lib/graphql/query/arguments.rb +2 -1
  53. data/lib/graphql/query/context.rb +2 -5
  54. data/lib/graphql/query/literal_input.rb +30 -10
  55. data/lib/graphql/query/variable_validation_error.rb +1 -1
  56. data/lib/graphql/query/variables.rb +7 -3
  57. data/lib/graphql/query.rb +9 -5
  58. data/lib/graphql/relay/base_connection.rb +4 -0
  59. data/lib/graphql/relay/connection_type.rb +2 -1
  60. data/lib/graphql/relay/edge_type.rb +1 -0
  61. data/lib/graphql/relay/edges_instrumentation.rb +1 -1
  62. data/lib/graphql/relay/mutation.rb +1 -86
  63. data/lib/graphql/relay/node.rb +2 -2
  64. data/lib/graphql/scalar_type.rb +1 -58
  65. data/lib/graphql/schema/argument.rb +51 -6
  66. data/lib/graphql/schema/build_from_definition/resolve_map/default_resolve.rb +1 -1
  67. data/lib/graphql/schema/build_from_definition/resolve_map.rb +10 -4
  68. data/lib/graphql/schema/build_from_definition.rb +167 -178
  69. data/lib/graphql/schema/built_in_types.rb +5 -5
  70. data/lib/graphql/schema/directive/deprecated.rb +18 -0
  71. data/lib/graphql/schema/directive.rb +28 -2
  72. data/lib/graphql/schema/enum.rb +40 -3
  73. data/lib/graphql/schema/enum_value.rb +5 -1
  74. data/lib/graphql/schema/field/connection_extension.rb +11 -1
  75. data/lib/graphql/schema/field.rb +59 -31
  76. data/lib/graphql/schema/find_inherited_value.rb +13 -0
  77. data/lib/graphql/schema/finder.rb +13 -11
  78. data/lib/graphql/schema/input_object.rb +107 -2
  79. data/lib/graphql/schema/interface.rb +10 -7
  80. data/lib/graphql/schema/introspection_system.rb +108 -37
  81. data/lib/graphql/schema/late_bound_type.rb +1 -0
  82. data/lib/graphql/schema/list.rb +41 -0
  83. data/lib/graphql/schema/loader.rb +16 -4
  84. data/lib/graphql/schema/member/base_dsl_methods.rb +21 -11
  85. data/lib/graphql/schema/member/build_type.rb +5 -1
  86. data/lib/graphql/schema/member/cached_graphql_definition.rb +5 -0
  87. data/lib/graphql/schema/member/has_arguments.rb +2 -2
  88. data/lib/graphql/schema/member/has_ast_node.rb +17 -0
  89. data/lib/graphql/schema/member/has_fields.rb +4 -4
  90. data/lib/graphql/schema/member/validates_input.rb +33 -0
  91. data/lib/graphql/schema/member.rb +5 -0
  92. data/lib/graphql/schema/mutation.rb +1 -1
  93. data/lib/graphql/schema/non_null.rb +25 -0
  94. data/lib/graphql/schema/object.rb +15 -5
  95. data/lib/graphql/schema/printer.rb +1 -2
  96. data/lib/graphql/schema/relay_classic_mutation.rb +1 -1
  97. data/lib/graphql/schema/resolver.rb +3 -15
  98. data/lib/graphql/schema/scalar.rb +19 -3
  99. data/lib/graphql/schema/subscription.rb +5 -5
  100. data/lib/graphql/schema/traversal.rb +1 -1
  101. data/lib/graphql/schema/type_expression.rb +21 -13
  102. data/lib/graphql/schema/type_membership.rb +2 -2
  103. data/lib/graphql/schema/union.rb +2 -3
  104. data/lib/graphql/schema/validation.rb +2 -2
  105. data/lib/graphql/schema/warden.rb +45 -20
  106. data/lib/graphql/schema.rb +764 -151
  107. data/lib/graphql/static_validation/base_visitor.rb +10 -6
  108. data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +9 -4
  109. data/lib/graphql/static_validation/rules/arguments_are_defined.rb +10 -7
  110. data/lib/graphql/static_validation/rules/directives_are_in_valid_locations.rb +1 -1
  111. data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +4 -4
  112. data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +5 -5
  113. data/lib/graphql/static_validation/rules/fields_will_merge.rb +4 -4
  114. data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +3 -3
  115. data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +3 -3
  116. data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +3 -3
  117. data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +5 -6
  118. data/lib/graphql/static_validation/rules/variables_are_input_types.rb +1 -1
  119. data/lib/graphql/static_validation/rules/variables_are_used_and_defined.rb +1 -1
  120. data/lib/graphql/static_validation/type_stack.rb +2 -2
  121. data/lib/graphql/static_validation/validator.rb +1 -1
  122. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +3 -3
  123. data/lib/graphql/subscriptions/event.rb +7 -4
  124. data/lib/graphql/subscriptions/instrumentation.rb +10 -5
  125. data/lib/graphql/subscriptions/subscription_root.rb +0 -1
  126. data/lib/graphql/subscriptions.rb +34 -9
  127. data/lib/graphql/tracing/active_support_notifications_tracing.rb +14 -10
  128. data/lib/graphql/tracing/appsignal_tracing.rb +8 -0
  129. data/lib/graphql/tracing/data_dog_tracing.rb +8 -0
  130. data/lib/graphql/tracing/new_relic_tracing.rb +8 -0
  131. data/lib/graphql/tracing/platform_tracing.rb +26 -6
  132. data/lib/graphql/tracing/prometheus_tracing.rb +8 -0
  133. data/lib/graphql/tracing/scout_tracing.rb +8 -0
  134. data/lib/graphql/tracing/skylight_tracing.rb +8 -0
  135. data/lib/graphql/tracing.rb +7 -3
  136. data/lib/graphql/types/int.rb +1 -1
  137. data/lib/graphql/types/relay/base_connection.rb +3 -1
  138. data/lib/graphql/union_type.rb +13 -28
  139. data/lib/graphql/unresolved_type_error.rb +2 -2
  140. data/lib/graphql/version.rb +1 -1
  141. data/lib/graphql.rb +2 -1
  142. metadata +15 -4
@@ -46,12 +46,11 @@ module GraphQL
46
46
 
47
47
  root_operation = query.selected_operation
48
48
  root_op_type = root_operation.operation_type || "query"
49
- legacy_root_type = schema.root_type_for_operation(root_op_type)
50
- root_type = legacy_root_type.metadata[:type_class] || raise("Invariant: type must be class-based: #{legacy_root_type}")
49
+ root_type = schema.root_type_for_operation(root_op_type)
51
50
  path = []
52
51
  @interpreter_context[:current_object] = query.root_value
53
52
  @interpreter_context[:current_path] = path
54
- object_proxy = root_type.authorized_new(query.root_value, context)
53
+ object_proxy = authorized_new(root_type, query.root_value, context, path)
55
54
  object_proxy = schema.sync_lazy(object_proxy)
56
55
  if object_proxy.nil?
57
56
  # Root .authorized? returned false.
@@ -89,11 +88,10 @@ module GraphQL
89
88
  end
90
89
  when GraphQL::Language::Nodes::InlineFragment
91
90
  if node.type
92
- type_defn = schema.types[node.type.name]
93
- type_defn = type_defn.metadata[:type_class]
91
+ type_defn = schema.get_type(node.type.name)
94
92
  # Faster than .map{}.include?()
95
93
  query.warden.possible_types(type_defn).each do |t|
96
- if t.metadata[:type_class] == owner_type
94
+ if t == owner_type
97
95
  gather_selections(owner_object, owner_type, node.selections, selections_by_name)
98
96
  break
99
97
  end
@@ -104,10 +102,10 @@ module GraphQL
104
102
  end
105
103
  when GraphQL::Language::Nodes::FragmentSpread
106
104
  fragment_def = query.fragments[node.name]
107
- type_defn = schema.types[fragment_def.type.name]
108
- type_defn = type_defn.metadata[:type_class]
109
- schema.possible_types(type_defn).each do |t|
110
- if t.metadata[:type_class] == owner_type
105
+ type_defn = schema.get_type(fragment_def.type.name)
106
+ possible_types = query.warden.possible_types(type_defn)
107
+ possible_types.each do |t|
108
+ if t == owner_type
111
109
  gather_selections(owner_object, owner_type, fragment_def.selections, selections_by_name)
112
110
  break
113
111
  end
@@ -138,18 +136,18 @@ module GraphQL
138
136
  field_defn = @fields_cache[owner_type][field_name] ||= owner_type.get_field(field_name)
139
137
  is_introspection = false
140
138
  if field_defn.nil?
141
- field_defn = if owner_type == schema.query.metadata[:type_class] && (entry_point_field = schema.introspection_system.entry_point(name: field_name))
139
+ field_defn = if owner_type == schema.query && (entry_point_field = schema.introspection_system.entry_point(name: field_name))
142
140
  is_introspection = true
143
- entry_point_field.metadata[:type_class]
141
+ entry_point_field
144
142
  elsif (dynamic_field = schema.introspection_system.dynamic_field(name: field_name))
145
143
  is_introspection = true
146
- dynamic_field.metadata[:type_class]
144
+ dynamic_field
147
145
  else
148
146
  raise "Invariant: no field for #{owner_type}.#{field_name}"
149
147
  end
150
148
  end
151
149
 
152
- return_type = resolve_if_late_bound_type(field_defn.type)
150
+ return_type = field_defn.type
153
151
 
154
152
  next_path = path.dup
155
153
  next_path << result_name
@@ -167,7 +165,7 @@ module GraphQL
167
165
  object = owner_object
168
166
 
169
167
  if is_introspection
170
- object = field_defn.owner.authorized_new(object, context)
168
+ object = authorized_new(field_defn.owner, object, context, next_path)
171
169
  end
172
170
 
173
171
  begin
@@ -295,8 +293,8 @@ module GraphQL
295
293
  write_in_response(path, r)
296
294
  r
297
295
  when "UNION", "INTERFACE"
298
- resolved_type_or_lazy = query.resolve_type(type, value)
299
- after_lazy(resolved_type_or_lazy, owner: type, path: path, scoped_context: context.scoped_context, field: field, owner_object: owner_object, arguments: arguments) do |resolved_type|
296
+ resolved_type_or_lazy = resolve_type(type, value, path)
297
+ after_lazy(resolved_type_or_lazy, owner: type, path: path, scoped_context: context.scoped_context, field: field, owner_object: owner_object, arguments: arguments, trace: false) do |resolved_type|
300
298
  possible_types = query.possible_types(type)
301
299
 
302
300
  if !possible_types.include?(resolved_type)
@@ -306,17 +304,16 @@ module GraphQL
306
304
  write_in_response(path, nil)
307
305
  nil
308
306
  else
309
- resolved_type = resolved_type.metadata[:type_class]
310
307
  continue_field(path, value, field, resolved_type, ast_node, next_selections, is_non_null, owner_object, arguments)
311
308
  end
312
309
  end
313
310
  when "OBJECT"
314
311
  object_proxy = begin
315
- type.authorized_new(value, context)
312
+ authorized_new(type, value, context, path)
316
313
  rescue GraphQL::ExecutionError => err
317
314
  err
318
315
  end
319
- after_lazy(object_proxy, owner: type, path: path, scoped_context: context.scoped_context, field: field, owner_object: owner_object, arguments: arguments) do |inner_object|
316
+ after_lazy(object_proxy, owner: type, path: path, scoped_context: context.scoped_context, field: field, owner_object: owner_object, arguments: arguments, trace: false) do |inner_object|
320
317
  continue_value = continue_value(path, inner_object, field, is_non_null, ast_node)
321
318
  if HALT != continue_value
322
319
  response_hash = {}
@@ -349,8 +346,6 @@ module GraphQL
349
346
  response_list
350
347
  when "NON_NULL"
351
348
  inner_type = type.of_type
352
- # For fields like `__schema: __Schema!`
353
- inner_type = resolve_if_late_bound_type(inner_type)
354
349
  # Don't `set_type_at_path` because we want the static type,
355
350
  # we're going to use that to determine whether a `nil` should be propagated or not.
356
351
  continue_field(path, value, field, inner_type, ast_node, next_selections, true, owner_object, arguments)
@@ -370,7 +365,7 @@ module GraphQL
370
365
  else
371
366
  dir_defn = schema.directives.fetch(dir_node.name)
372
367
  if !dir_defn.is_a?(Class)
373
- dir_defn = dir_defn.metadata[:type_class] || raise("Only class-based directives are supported (not `@#{dir_node.name}`)")
368
+ dir_defn = dir_defn.type_class || raise("Only class-based directives are supported (not `@#{dir_node.name}`)")
374
369
  end
375
370
  dir_args = arguments(nil, dir_defn, dir_node)
376
371
  dir_defn.resolve(object, dir_args, context) do
@@ -382,7 +377,7 @@ module GraphQL
382
377
  # Check {Schema::Directive.include?} for each directive that's present
383
378
  def directives_include?(node, graphql_object, parent_type)
384
379
  node.directives.each do |dir_node|
385
- dir_defn = schema.directives.fetch(dir_node.name).metadata[:type_class] || raise("Only class-based directives are supported (not #{dir_node.name.inspect})")
380
+ dir_defn = schema.directives.fetch(dir_node.name).type_class || raise("Only class-based directives are supported (not #{dir_node.name.inspect})")
386
381
  args = arguments(graphql_object, dir_defn, dir_node)
387
382
  if !dir_defn.include?(graphql_object, args, context)
388
383
  return false
@@ -391,20 +386,13 @@ module GraphQL
391
386
  true
392
387
  end
393
388
 
394
- def resolve_if_late_bound_type(type)
395
- if type.is_a?(GraphQL::Schema::LateBoundType)
396
- query.warden.get_type(type.name).metadata[:type_class]
397
- else
398
- type
399
- end
400
- end
401
-
402
389
  # @param obj [Object] Some user-returned value that may want to be batched
403
390
  # @param path [Array<String>]
404
391
  # @param field [GraphQL::Schema::Field]
405
392
  # @param eager [Boolean] Set to `true` for mutation root fields only
393
+ # @param trace [Boolean] If `false`, don't wrap this with field tracing
406
394
  # @return [GraphQL::Execution::Lazy, Object] If loading `object` will be deferred, it's a wrapper over it.
407
- def after_lazy(lazy_obj, owner:, field:, path:, scoped_context:, owner_object:, arguments:, eager: false)
395
+ def after_lazy(lazy_obj, owner:, field:, path:, scoped_context:, owner_object:, arguments:, eager: false, trace: true)
408
396
  @interpreter_context[:current_object] = owner_object
409
397
  @interpreter_context[:current_arguments] = arguments
410
398
  @interpreter_context[:current_path] = path
@@ -420,7 +408,11 @@ module GraphQL
420
408
  # but don't wrap the continuation below
421
409
  inner_obj = begin
422
410
  query.with_error_handling do
423
- query.trace("execute_field_lazy", {owner: owner, field: field, path: path, query: query, object: owner_object, arguments: arguments}) do
411
+ if trace
412
+ query.trace("execute_field_lazy", {owner: owner, field: field, path: path, query: query, object: owner_object, arguments: arguments}) do
413
+ schema.sync_lazy(lazy_obj)
414
+ end
415
+ else
424
416
  schema.sync_lazy(lazy_obj)
425
417
  end
426
418
  end
@@ -466,7 +458,7 @@ module GraphQL
466
458
  arg_defn = arg_defns[arg_name]
467
459
  # Need to distinguish between client-provided `nil`
468
460
  # and nothing-at-all
469
- is_present, value = arg_to_value(graphql_object, arg_defn.type, arg_value)
461
+ is_present, value = arg_to_value(graphql_object, arg_defn.type, arg_value, already_arguments: false)
470
462
  if is_present
471
463
  # This doesn't apply to directives, which are legacy
472
464
  # Can remove this when Skip and Include use classes or something.
@@ -478,54 +470,78 @@ module GraphQL
478
470
  end
479
471
  arg_defns.each do |name, arg_defn|
480
472
  if arg_defn.default_value? && !kwarg_arguments.key?(arg_defn.keyword)
481
- _is_present, value = arg_to_value(graphql_object, arg_defn.type, arg_defn.default_value)
473
+ _is_present, value = arg_to_value(graphql_object, arg_defn.type, arg_defn.default_value, already_arguments: false)
482
474
  kwarg_arguments[arg_defn.keyword] = value
483
475
  end
484
476
  end
485
477
  kwarg_arguments
486
478
  end
487
479
 
480
+ # TODO CAN THIS USE `.coerce_input` ???
481
+
488
482
  # Get a Ruby-ready value from a client query.
489
483
  # @param graphql_object [Object] The owner of the field whose argument this is
490
484
  # @param arg_type [Class, GraphQL::Schema::NonNull, GraphQL::Schema::List]
491
485
  # @param ast_value [GraphQL::Language::Nodes::VariableIdentifier, String, Integer, Float, Boolean]
486
+ # @param already_arguments [Boolean] if true, don't re-coerce these with `arguments(...)`
492
487
  # @return [Array(is_present, value)]
493
- def arg_to_value(graphql_object, arg_type, ast_value)
488
+ def arg_to_value(graphql_object, arg_type, ast_value, already_arguments:)
494
489
  if ast_value.is_a?(GraphQL::Language::Nodes::VariableIdentifier)
495
490
  # If it's not here, it will get added later
496
491
  if query.variables.key?(ast_value.name)
497
- return true, query.variables[ast_value.name]
492
+ variable_value = query.variables[ast_value.name]
493
+ arg_to_value(graphql_object, arg_type, variable_value, already_arguments: true)
498
494
  else
499
495
  return false, nil
500
496
  end
501
497
  elsif ast_value.is_a?(GraphQL::Language::Nodes::NullValue)
502
498
  return true, nil
503
499
  elsif arg_type.is_a?(GraphQL::Schema::NonNull)
504
- arg_to_value(graphql_object, arg_type.of_type, ast_value)
500
+ arg_to_value(graphql_object, arg_type.of_type, ast_value, already_arguments: already_arguments)
505
501
  elsif arg_type.is_a?(GraphQL::Schema::List)
506
- # Treat a single value like a list
507
- arg_value = Array(ast_value)
508
- list = []
509
- arg_value.map do |inner_v|
510
- _present, value = arg_to_value(graphql_object, arg_type.of_type, inner_v)
511
- list << value
512
- end
513
- return true, list
502
+ if ast_value.nil?
503
+ return true, nil
504
+ else
505
+ # Treat a single value like a list
506
+ arg_value = Array(ast_value)
507
+ list = []
508
+ arg_value.map do |inner_v|
509
+ _present, value = arg_to_value(graphql_object, arg_type.of_type, inner_v, already_arguments: already_arguments)
510
+ list << value
511
+ end
512
+ return true, list
513
+ end
514
514
  elsif arg_type.is_a?(Class) && arg_type < GraphQL::Schema::InputObject
515
- # For these, `prepare` is applied during `#initialize`.
516
- # Pass `nil` so it will be skipped in `#arguments`.
517
- # What a mess.
518
- args = arguments(nil, arg_type, ast_value)
519
- # We're not tracking defaults_used, but for our purposes
520
- # we compare the value to the default value.
515
+ if already_arguments
516
+ # This came from a variable, already prepared.
517
+ # But replace `nil` with `{}` like we would for `arg_type`
518
+ if ast_value.nil?
519
+ return false, nil
520
+ else
521
+ args = ast_value
522
+ end
523
+ else
524
+ # For these, `prepare` is applied during `#initialize`.
525
+ # Pass `nil` so it will be skipped in `#arguments`.
526
+ # What a mess.
527
+ args = arguments(nil, arg_type, ast_value)
528
+ end
521
529
 
522
530
  input_obj = query.with_error_handling do
531
+ # We're not tracking defaults_used, but for our purposes
532
+ # we compare the value to the default value.
523
533
  arg_type.new(ruby_kwargs: args, context: context, defaults_used: nil)
524
534
  end
525
535
  return true, input_obj
526
536
  else
527
- flat_value = flatten_ast_value(ast_value)
528
- return true, arg_type.coerce_input(flat_value, context)
537
+ flat_value = if already_arguments
538
+ # It was coerced by variable handling
539
+ ast_value
540
+ else
541
+ v = flatten_ast_value(ast_value)
542
+ arg_type.coerce_input(v, context)
543
+ end
544
+ return true, flat_value
529
545
  end
530
546
  end
531
547
 
@@ -628,6 +644,41 @@ module GraphQL
628
644
  end
629
645
  res && res[:__dead]
630
646
  end
647
+
648
+ def resolve_type(type, value, path)
649
+ trace_payload = { context: context, type: type, object: value, path: path }
650
+ resolved_type = query.trace("resolve_type", trace_payload) do
651
+ query.resolve_type(type, value)
652
+ end
653
+
654
+ if schema.lazy?(resolved_type)
655
+ GraphQL::Execution::Lazy.new do
656
+ query.trace("resolve_type_lazy", trace_payload) do
657
+ schema.sync_lazy(resolved_type)
658
+ end
659
+ end
660
+ else
661
+ resolved_type
662
+ end
663
+ end
664
+
665
+ def authorized_new(type, value, context, path)
666
+ trace_payload = { context: context, type: type, object: value, path: path }
667
+
668
+ auth_val = context.query.trace("authorized", trace_payload) do
669
+ type.authorized_new(value, context)
670
+ end
671
+
672
+ if context.schema.lazy?(auth_val)
673
+ GraphQL::Execution::Lazy.new do
674
+ context.query.trace("authorized_lazy", trace_payload) do
675
+ context.schema.sync_lazy(auth_val)
676
+ end
677
+ end
678
+ else
679
+ auth_val
680
+ end
681
+ end
631
682
  end
632
683
  end
633
684
  end
@@ -17,17 +17,11 @@ module GraphQL
17
17
  runtime.final_value
18
18
  end
19
19
 
20
- def self.use(schema_defn)
21
- schema_defn.target.interpreter = true
22
- # Reach through the legacy objects for the actual class defn
23
- schema_class = schema_defn.target.class
24
- # This is not good, since both of these are holding state now,
25
- # we have to update both :(
26
- [schema_class, schema_defn].each do |schema_config|
27
- schema_config.query_execution_strategy(GraphQL::Execution::Interpreter)
28
- schema_config.mutation_execution_strategy(GraphQL::Execution::Interpreter)
29
- schema_config.subscription_execution_strategy(GraphQL::Execution::Interpreter)
30
- end
20
+ def self.use(schema_class)
21
+ schema_class.interpreter = true
22
+ schema_class.query_execution_strategy(GraphQL::Execution::Interpreter)
23
+ schema_class.mutation_execution_strategy(GraphQL::Execution::Interpreter)
24
+ schema_class.subscription_execution_strategy(GraphQL::Execution::Interpreter)
31
25
  end
32
26
 
33
27
  def self.begin_multiplex(multiplex)
@@ -36,6 +36,10 @@ module GraphQL
36
36
  @storage.compute_if_absent(value.class) { find_superclass_method(value.class) }
37
37
  end
38
38
 
39
+ def each
40
+ @storage.each_pair { |k, v| yield(k, v) }
41
+ end
42
+
39
43
  protected
40
44
 
41
45
  attr_reader :storage
@@ -223,14 +223,14 @@ module GraphQL
223
223
  subselections_on_type = selections_on_type
224
224
  if (t = ast_selection.type)
225
225
  # Assuming this is valid, that `t` will be found.
226
- on_type = @query.schema.types[t.name].metadata[:type_class]
226
+ on_type = @query.schema.get_type(t.name).type_class
227
227
  subselections_on_type = subselections_by_type[on_type] ||= {}
228
228
  end
229
229
  find_selections(subselections_by_type, subselections_on_type, on_type, ast_selection.selections, arguments)
230
230
  when GraphQL::Language::Nodes::FragmentSpread
231
231
  frag_defn = @query.fragments[ast_selection.name] || raise("Invariant: Can't look ahead to nonexistent fragment #{ast_selection.name} (found: #{@query.fragments.keys})")
232
232
  # Again, assuming a valid AST
233
- on_type = @query.schema.types[frag_defn.type.name].metadata[:type_class]
233
+ on_type = @query.schema.get_type(frag_defn.type.name).type_class
234
234
  subselections_on_type = subselections_by_type[on_type] ||= {}
235
235
  find_selections(subselections_by_type, subselections_on_type, on_type, frag_defn.selections, arguments)
236
236
  else
@@ -366,10 +366,10 @@ module GraphQL
366
366
 
367
367
  def get_field(schema, owner_type, field_name)
368
368
  field_defn = owner_type.get_field(field_name)
369
- field_defn ||= if owner_type == schema.query.metadata[:type_class] && (entry_point_field = schema.introspection_system.entry_point(name: field_name))
370
- entry_point_field.metadata[:type_class]
369
+ field_defn ||= if owner_type == schema.query.type_class && (entry_point_field = schema.introspection_system.entry_point(name: field_name))
370
+ entry_point_field.type_class
371
371
  elsif (dynamic_field = schema.introspection_system.dynamic_field(name: field_name))
372
- dynamic_field.metadata[:type_class]
372
+ dynamic_field.type_class
373
373
  else
374
374
  nil
375
375
  end
@@ -44,9 +44,9 @@ module GraphQL
44
44
  end
45
45
 
46
46
  class << self
47
- def run_all(schema, query_options, *args)
48
- queries = query_options.map { |opts| GraphQL::Query.new(schema, nil, opts) }
49
- run_queries(schema, queries, *args)
47
+ def run_all(schema, query_options, **kwargs)
48
+ queries = query_options.map { |opts| GraphQL::Query.new(schema, nil, **opts) }
49
+ run_queries(schema, queries, **kwargs)
50
50
  end
51
51
 
52
52
  # @param schema [GraphQL::Schema]
@@ -95,6 +95,7 @@ module GraphQL
95
95
  query.result
96
96
  end
97
97
  rescue Exception
98
+ # TODO rescue at a higher level so it will catch errors in analysis, too
98
99
  # Assign values here so that the query's `@executed` becomes true
99
100
  queries.map { |q| q.result_values ||= {} }
100
101
  raise
@@ -173,6 +174,15 @@ module GraphQL
173
174
  def instrument_and_analyze(multiplex)
174
175
  GraphQL::Execution::Instrumentation.apply_instrumenters(multiplex) do
175
176
  schema = multiplex.schema
177
+ if schema.interpreter? && schema.analysis_engine != GraphQL::Analysis::AST
178
+ raise <<-ERR
179
+ Can't use `GraphQL::Execution::Interpreter` without `GraphQL::Analysis::AST`, please add this plugin to your schema:
180
+
181
+ use GraphQL::Analysis::AST
182
+
183
+ For information about the new analysis engine: https://graphql-ruby.org/queries/ast_analysis.html
184
+ ERR
185
+ end
176
186
  multiplex_analyzers = schema.multiplex_analyzers
177
187
  if multiplex.max_complexity
178
188
  multiplex_analyzers += if schema.using_ast_analysis?
data/lib/graphql/field.rb CHANGED
@@ -2,123 +2,7 @@
2
2
  require "graphql/field/resolve"
3
3
 
4
4
  module GraphQL
5
- # {Field}s belong to {ObjectType}s and {InterfaceType}s.
6
- #
7
- # They're usually created with the `field` helper. If you create it by hand, make sure {#name} is a String.
8
- #
9
- # A field must have a return type, but if you want to defer the return type calculation until later,
10
- # you can pass a proc for the return type. That proc will be called when the schema is defined.
11
- #
12
- # @example Lazy type resolution
13
- # # If the field's type isn't defined yet, you can pass a proc
14
- # field :city, -> { TypeForModelName.find("City") }
15
- #
16
- # For complex field definition, you can pass a block to the `field` helper, eg `field :name do ... end`.
17
- # This block is equivalent to calling `GraphQL::Field.define { ... }`.
18
- #
19
- # @example Defining a field with a block
20
- # field :city, CityType do
21
- # # field definition continues inside the block
22
- # end
23
- #
24
- # ## Resolve
25
- #
26
- # Fields have `resolve` functions to determine their values at query-time.
27
- # The default implementation is to call a method on the object based on the field name.
28
- #
29
- # @example Create a field which calls a method with the same name.
30
- # GraphQL::ObjectType.define do
31
- # field :name, types.String, "The name of this thing "
32
- # end
33
- #
34
- # You can specify a custom proc with the `resolve` helper.
35
- #
36
- # There are some shortcuts for common `resolve` implementations:
37
- # - Provide `property:` to call a method with a different name than the field name
38
- # - Provide `hash_key:` to resolve the field by doing a key lookup, eg `obj[:my_hash_key]`
39
- #
40
- # @example Create a field that calls a different method on the object
41
- # GraphQL::ObjectType.define do
42
- # # use the `property` keyword:
43
- # field :firstName, types.String, property: :first_name
44
- # end
45
- #
46
- # @example Create a field looks up with `[hash_key]`
47
- # GraphQL::ObjectType.define do
48
- # # use the `hash_key` keyword:
49
- # field :firstName, types.String, hash_key: :first_name
50
- # end
51
- #
52
- # ## Arguments
53
- #
54
- # Fields can take inputs; they're called arguments. You can define them with the `argument` helper.
55
- #
56
- # @example Create a field with an argument
57
- # field :students, types[StudentType] do
58
- # argument :grade, types.Int
59
- # resolve ->(obj, args, ctx) {
60
- # Student.where(grade: args[:grade])
61
- # }
62
- # end
63
- #
64
- # They can have default values which will be provided to `resolve` if the query doesn't include a value.
65
- #
66
- # @example Argument with a default value
67
- # field :events, types[EventType] do
68
- # # by default, don't include past events
69
- # argument :includePast, types.Boolean, default_value: false
70
- # resolve ->(obj, args, ctx) {
71
- # args[:includePast] # => false if no value was provided in the query
72
- # # ...
73
- # }
74
- # end
75
- #
76
- # Only certain types maybe used for inputs:
77
- #
78
- # - Scalars
79
- # - Enums
80
- # - Input Objects
81
- # - Lists of those types
82
- #
83
- # Input types may also be non-null -- in that case, the query will fail
84
- # if the input is not present.
85
- #
86
- # ## Complexity
87
- #
88
- # Fields can have _complexity_ values which describe the computation cost of resolving the field.
89
- # You can provide the complexity as a constant with `complexity:` or as a proc, with the `complexity` helper.
90
- #
91
- # @example Custom complexity values
92
- # # Complexity can be a number or a proc.
93
- #
94
- # # Complexity can be defined with a keyword:
95
- # field :expensive_calculation, !types.Int, complexity: 10
96
- #
97
- # # Or inside the block:
98
- # field :expensive_calculation_2, !types.Int do
99
- # complexity ->(ctx, args, child_complexity) { ctx[:current_user].staff? ? 0 : 10 }
100
- # end
101
- #
102
- # @example Calculating the complexity of a list field
103
- # field :items, types[ItemType] do
104
- # argument :limit, !types.Int
105
- # # Multiply the child complexity by the possible items on the list
106
- # complexity ->(ctx, args, child_complexity) { child_complexity * args[:limit] }
107
- # end
108
- #
109
- # @example Creating a field, then assigning it to a type
110
- # name_field = GraphQL::Field.define do
111
- # name("Name")
112
- # type(!types.String)
113
- # description("The name of this thing")
114
- # resolve ->(object, arguments, context) { object.name }
115
- # end
116
- #
117
- # NamedType = GraphQL::ObjectType.define do
118
- # # The second argument may be a GraphQL::Field
119
- # field :name, name_field
120
- # end
121
- #
5
+ # @api deprecated
122
6
  class Field
123
7
  include GraphQL::Define::InstanceDefinable
124
8
  accepts_definitions :name, :description, :deprecation_reason,
@@ -196,6 +80,10 @@ module GraphQL
196
80
 
197
81
  attr_accessor :ast_node
198
82
 
83
+ # Future-compatible alias
84
+ # @see {GraphQL::SchemaMember}
85
+ alias :graphql_definition :itself
86
+
199
87
  # @return [Boolean]
200
88
  def connection?
201
89
  @connection
@@ -315,6 +203,10 @@ module GraphQL
315
203
  }
316
204
  end
317
205
 
206
+ def type_class
207
+ metadata[:type_class]
208
+ end
209
+
318
210
  private
319
211
 
320
212
  def build_default_resolver
@@ -33,7 +33,7 @@ module GraphQL
33
33
  end
34
34
 
35
35
  def self.build(onlies)
36
- case onlies
36
+ case onlies.size
37
37
  when 0
38
38
  nil
39
39
  when 1
@@ -1,35 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
  module GraphQL
3
- # A reusable container for field logic, including arguments, resolve, return type, and documentation.
4
- #
5
- # Class-level values defined with the DSL will be inherited,
6
- # so {GraphQL::Function}s can extend one another.
7
- #
8
- # It's OK to override the instance methods here in order to customize behavior of instances.
9
- #
10
- # @example A reusable GraphQL::Function attached as a field
11
- # class FindRecord < GraphQL::Function
12
- # attr_reader :type
13
- #
14
- # def initialize(model:, type:)
15
- # @model = model
16
- # @type = type
17
- # end
18
- #
19
- # argument :id, GraphQL::ID_TYPE
20
- #
21
- # def call(obj, args, ctx)
22
- # @model.find(args.id)
23
- # end
24
- # end
25
- #
26
- # QueryType = GraphQL::ObjectType.define do
27
- # name "Query"
28
- # field :post, function: FindRecord.new(model: Post, type: PostType)
29
- # field :comment, function: FindRecord.new(model: Comment, type: CommentType)
30
- # end
31
- #
32
- # @see {GraphQL::Schema::Resolver} for a replacement for `GraphQL::Function`
3
+ # @api deprecated
33
4
  class Function
34
5
  # @return [Hash<String => GraphQL::Argument>] Arguments, keyed by name
35
6
  def arguments
@@ -1,28 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
  module GraphQL
3
- # {InputObjectType}s are key-value inputs for fields.
4
- #
5
- # Input objects have _arguments_ which are identical to {GraphQL::Field} arguments.
6
- # They map names to types and support default values.
7
- # Their input types can be any input types, including {InputObjectType}s.
8
- #
9
- # @example An input type with name and number
10
- # PlayerInput = GraphQL::InputObjectType.define do
11
- # name("Player")
12
- # argument :name, !types.String
13
- # argument :number, !types.Int
14
- # end
15
- #
16
- # In a `resolve` function, you can access the values by making nested lookups on `args`.
17
- #
18
- # @example Accessing input values in a resolve function
19
- # resolve ->(obj, args, ctx) {
20
- # args[:player][:name] # => "Tony Gwynn"
21
- # args[:player][:number] # => 19
22
- # args[:player].to_h # { "name" => "Tony Gwynn", "number" => 19 }
23
- # # ...
24
- # }
25
- #
3
+ # @api deprecated
26
4
  class InputObjectType < GraphQL::BaseType
27
5
  accepts_definitions(
28
6
  :arguments, :mutation,
@@ -136,7 +114,7 @@ module GraphQL
136
114
  # Items in the input that are unexpected
137
115
  input.each do |name, value|
138
116
  if visible_arguments_map[name].nil?
139
- result.add_problem("Field is not defined on #{self.name}", [name])
117
+ result.add_problem("Field is not defined on #{self.graphql_name}", [name])
140
118
  end
141
119
  end
142
120