graphql 1.11.8 → 1.12.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (117) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/install_generator.rb +5 -5
  3. data/lib/generators/graphql/relay_generator.rb +63 -0
  4. data/lib/generators/graphql/templates/base_connection.erb +8 -0
  5. data/lib/generators/graphql/templates/base_edge.erb +8 -0
  6. data/lib/generators/graphql/templates/node_type.erb +9 -0
  7. data/lib/generators/graphql/templates/object.erb +1 -1
  8. data/lib/generators/graphql/templates/query_type.erb +1 -3
  9. data/lib/generators/graphql/templates/schema.erb +8 -35
  10. data/lib/graphql.rb +38 -4
  11. data/lib/graphql/analysis/analyze_query.rb +7 -0
  12. data/lib/graphql/analysis/ast.rb +11 -2
  13. data/lib/graphql/analysis/ast/visitor.rb +9 -1
  14. data/lib/graphql/backtrace.rb +28 -19
  15. data/lib/graphql/backtrace/legacy_tracer.rb +56 -0
  16. data/lib/graphql/backtrace/table.rb +22 -2
  17. data/lib/graphql/backtrace/tracer.rb +40 -9
  18. data/lib/graphql/backwards_compatibility.rb +1 -0
  19. data/lib/graphql/compatibility/execution_specification.rb +1 -0
  20. data/lib/graphql/compatibility/lazy_execution_specification.rb +2 -0
  21. data/lib/graphql/compatibility/query_parser_specification.rb +2 -0
  22. data/lib/graphql/compatibility/schema_parser_specification.rb +2 -0
  23. data/lib/graphql/dataloader.rb +197 -0
  24. data/lib/graphql/dataloader/null_dataloader.rb +21 -0
  25. data/lib/graphql/dataloader/request.rb +24 -0
  26. data/lib/graphql/dataloader/request_all.rb +22 -0
  27. data/lib/graphql/dataloader/source.rb +93 -0
  28. data/lib/graphql/define/assign_global_id_field.rb +1 -1
  29. data/lib/graphql/define/instance_definable.rb +32 -2
  30. data/lib/graphql/define/type_definer.rb +5 -5
  31. data/lib/graphql/deprecated_dsl.rb +5 -0
  32. data/lib/graphql/enum_type.rb +2 -0
  33. data/lib/graphql/execution/errors.rb +4 -0
  34. data/lib/graphql/execution/execute.rb +7 -0
  35. data/lib/graphql/execution/interpreter.rb +10 -6
  36. data/lib/graphql/execution/interpreter/arguments.rb +51 -14
  37. data/lib/graphql/execution/interpreter/handles_raw_value.rb +0 -7
  38. data/lib/graphql/execution/interpreter/runtime.rb +210 -124
  39. data/lib/graphql/execution/multiplex.rb +20 -6
  40. data/lib/graphql/function.rb +4 -0
  41. data/lib/graphql/input_object_type.rb +2 -0
  42. data/lib/graphql/interface_type.rb +3 -1
  43. data/lib/graphql/language/document_from_schema_definition.rb +50 -23
  44. data/lib/graphql/object_type.rb +2 -0
  45. data/lib/graphql/pagination/connection.rb +5 -1
  46. data/lib/graphql/pagination/connections.rb +6 -16
  47. data/lib/graphql/query.rb +2 -0
  48. data/lib/graphql/query/context.rb +4 -0
  49. data/lib/graphql/query/serial_execution.rb +1 -0
  50. data/lib/graphql/relay/base_connection.rb +7 -0
  51. data/lib/graphql/relay/connection_instrumentation.rb +4 -4
  52. data/lib/graphql/relay/connection_type.rb +1 -1
  53. data/lib/graphql/relay/mutation.rb +1 -0
  54. data/lib/graphql/relay/node.rb +3 -0
  55. data/lib/graphql/relay/type_extensions.rb +2 -0
  56. data/lib/graphql/scalar_type.rb +2 -0
  57. data/lib/graphql/schema.rb +69 -32
  58. data/lib/graphql/schema/argument.rb +25 -7
  59. data/lib/graphql/schema/build_from_definition.rb +139 -51
  60. data/lib/graphql/schema/directive.rb +76 -0
  61. data/lib/graphql/schema/directive/flagged.rb +57 -0
  62. data/lib/graphql/schema/enum.rb +3 -0
  63. data/lib/graphql/schema/enum_value.rb +12 -6
  64. data/lib/graphql/schema/field.rb +28 -9
  65. data/lib/graphql/schema/field/connection_extension.rb +3 -2
  66. data/lib/graphql/schema/input_object.rb +33 -22
  67. data/lib/graphql/schema/interface.rb +1 -0
  68. data/lib/graphql/schema/member.rb +4 -0
  69. data/lib/graphql/schema/member/base_dsl_methods.rb +1 -0
  70. data/lib/graphql/schema/member/build_type.rb +3 -3
  71. data/lib/graphql/schema/member/has_arguments.rb +24 -6
  72. data/lib/graphql/schema/member/has_deprecation_reason.rb +25 -0
  73. data/lib/graphql/schema/member/has_directives.rb +98 -0
  74. data/lib/graphql/schema/member/has_validators.rb +31 -0
  75. data/lib/graphql/schema/member/type_system_helpers.rb +1 -1
  76. data/lib/graphql/schema/object.rb +11 -0
  77. data/lib/graphql/schema/printer.rb +5 -4
  78. data/lib/graphql/schema/resolver.rb +7 -0
  79. data/lib/graphql/schema/resolver/has_payload_type.rb +2 -0
  80. data/lib/graphql/schema/subscription.rb +19 -1
  81. data/lib/graphql/schema/timeout_middleware.rb +2 -0
  82. data/lib/graphql/schema/validation.rb +2 -0
  83. data/lib/graphql/schema/validator.rb +163 -0
  84. data/lib/graphql/schema/validator/exclusion_validator.rb +31 -0
  85. data/lib/graphql/schema/validator/format_validator.rb +49 -0
  86. data/lib/graphql/schema/validator/inclusion_validator.rb +33 -0
  87. data/lib/graphql/schema/validator/length_validator.rb +57 -0
  88. data/lib/graphql/schema/validator/numericality_validator.rb +71 -0
  89. data/lib/graphql/schema/validator/required_validator.rb +68 -0
  90. data/lib/graphql/static_validation/validator.rb +2 -0
  91. data/lib/graphql/subscriptions.rb +17 -20
  92. data/lib/graphql/tracing.rb +2 -2
  93. data/lib/graphql/tracing/appoptics_tracing.rb +3 -1
  94. data/lib/graphql/tracing/platform_tracing.rb +3 -1
  95. data/lib/graphql/tracing/skylight_tracing.rb +1 -1
  96. data/lib/graphql/types/relay.rb +11 -3
  97. data/lib/graphql/types/relay/base_connection.rb +2 -92
  98. data/lib/graphql/types/relay/base_edge.rb +2 -35
  99. data/lib/graphql/types/relay/connection_behaviors.rb +123 -0
  100. data/lib/graphql/types/relay/default_relay.rb +27 -0
  101. data/lib/graphql/types/relay/edge_behaviors.rb +42 -0
  102. data/lib/graphql/types/relay/has_node_field.rb +41 -0
  103. data/lib/graphql/types/relay/has_nodes_field.rb +41 -0
  104. data/lib/graphql/types/relay/node.rb +2 -4
  105. data/lib/graphql/types/relay/node_behaviors.rb +15 -0
  106. data/lib/graphql/types/relay/node_field.rb +1 -19
  107. data/lib/graphql/types/relay/nodes_field.rb +1 -19
  108. data/lib/graphql/types/relay/page_info.rb +2 -14
  109. data/lib/graphql/types/relay/page_info_behaviors.rb +25 -0
  110. data/lib/graphql/union_type.rb +2 -0
  111. data/lib/graphql/upgrader/member.rb +1 -0
  112. data/lib/graphql/upgrader/schema.rb +1 -0
  113. data/lib/graphql/version.rb +1 -1
  114. metadata +34 -9
  115. data/lib/graphql/types/relay/base_field.rb +0 -22
  116. data/lib/graphql/types/relay/base_interface.rb +0 -29
  117. data/lib/graphql/types/relay/base_object.rb +0 -26
@@ -12,6 +12,7 @@ module GraphQL
12
12
  # Define a custom connection type for this object type
13
13
  # @return [GraphQL::ObjectType]
14
14
  def define_connection(**kwargs, &block)
15
+ warn ".connection_type and .define_connection will be removed from GraphQL-Ruby 2.0, use class-based type definitions instead: https://graphql-ruby.org/schema/class_based_api.html"
15
16
  GraphQL::Relay::ConnectionType.create_type(self, **kwargs, &block)
16
17
  end
17
18
 
@@ -23,6 +24,7 @@ module GraphQL
23
24
  # Define a custom edge type for this object type
24
25
  # @return [GraphQL::ObjectType]
25
26
  def define_edge(**kwargs, &block)
27
+ warn ".edge_type and .define_edge will be removed from GraphQL-Ruby 2.0, use class-based type definitions instead: https://graphql-ruby.org/schema/class_based_api.html"
26
28
  GraphQL::Relay::EdgeType.create_type(self, **kwargs, &block)
27
29
  end
28
30
  end
@@ -2,6 +2,8 @@
2
2
  module GraphQL
3
3
  # @api deprecated
4
4
  class ScalarType < GraphQL::BaseType
5
+ extend Define::InstanceDefinable::DeprecatedDefine
6
+
5
7
  accepts_definitions :coerce, :coerce_input, :coerce_result
6
8
  ensure_defined :coerce_non_null_input, :coerce_result
7
9
 
@@ -21,6 +21,7 @@ require "graphql/schema/validation"
21
21
  require "graphql/schema/warden"
22
22
  require "graphql/schema/build_from_definition"
23
23
 
24
+ require "graphql/schema/validator"
24
25
  require "graphql/schema/member"
25
26
  require "graphql/schema/wrapper"
26
27
  require "graphql/schema/list"
@@ -40,6 +41,7 @@ require "graphql/schema/directive/deprecated"
40
41
  require "graphql/schema/directive/include"
41
42
  require "graphql/schema/directive/skip"
42
43
  require "graphql/schema/directive/feature"
44
+ require "graphql/schema/directive/flagged"
43
45
  require "graphql/schema/directive/transform"
44
46
  require "graphql/schema/type_membership"
45
47
 
@@ -80,6 +82,7 @@ module GraphQL
80
82
  extend GraphQL::Schema::Member::AcceptsDefinition
81
83
  extend GraphQL::Schema::Member::HasAstNode
82
84
  include GraphQL::Define::InstanceDefinable
85
+ extend GraphQL::Define::InstanceDefinable::DeprecatedDefine
83
86
  extend GraphQL::Schema::FindInheritedValue
84
87
 
85
88
  class DuplicateTypeNamesError < GraphQL::Error
@@ -281,11 +284,11 @@ module GraphQL
281
284
  @lazy_methods = GraphQL::Execution::Lazy::LazyMethodMap.new
282
285
  @lazy_methods.set(GraphQL::Execution::Lazy, :value)
283
286
  @cursor_encoder = Base64Encoder
284
- # Default to the built-in execution strategy:
287
+ # For schema instances, default to legacy runtime modules
285
288
  @analysis_engine = GraphQL::Analysis
286
- @query_execution_strategy = self.class.default_execution_strategy
287
- @mutation_execution_strategy = self.class.default_execution_strategy
288
- @subscription_execution_strategy = self.class.default_execution_strategy
289
+ @query_execution_strategy = GraphQL::Execution::Execute
290
+ @mutation_execution_strategy = GraphQL::Execution::Execute
291
+ @subscription_execution_strategy = GraphQL::Execution::Execute
289
292
  @default_mask = GraphQL::Schema::NullMask
290
293
  @rebuilding_artifacts = false
291
294
  @context_class = GraphQL::Query::Context
@@ -300,12 +303,11 @@ module GraphQL
300
303
 
301
304
  # @return [Boolean] True if using the new {GraphQL::Execution::Interpreter}
302
305
  def interpreter?
303
- @interpreter
306
+ query_execution_strategy == GraphQL::Execution::Interpreter &&
307
+ mutation_execution_strategy == GraphQL::Execution::Interpreter &&
308
+ subscription_execution_strategy == GraphQL::Execution::Interpreter
304
309
  end
305
310
 
306
- # @api private
307
- attr_writer :interpreter
308
-
309
311
  def inspect
310
312
  "#<#{self.class.name} ...>"
311
313
  end
@@ -370,7 +372,7 @@ module GraphQL
370
372
  res[:errors]
371
373
  end
372
374
 
373
- def define(**kwargs, &block)
375
+ def deprecated_define(**kwargs, &block)
374
376
  super
375
377
  ensure_defined
376
378
  # Assert that all necessary configs are present:
@@ -709,7 +711,7 @@ module GraphQL
709
711
  alias :_schema_class :class
710
712
  def_delegators :_schema_class, :unauthorized_object, :unauthorized_field, :inaccessible_fields
711
713
  def_delegators :_schema_class, :directive
712
- def_delegators :_schema_class, :error_handler
714
+ def_delegators :_schema_class, :error_handler, :rescues
713
715
 
714
716
 
715
717
  # Given this schema member, find the class-based definition object
@@ -787,9 +789,8 @@ module GraphQL
787
789
  # @param default_resolve [<#call(type, field, obj, args, ctx)>] A callable for handling field resolution
788
790
  # @param parser [Object] An object for handling definition string parsing (must respond to `parse`)
789
791
  # @param using [Hash] Plugins to attach to the created schema with `use(key, value)`
790
- # @param interpreter [Boolean] If false, the legacy {Execution::Execute} runtime will be used
791
792
  # @return [Class] the schema described by `document`
792
- def self.from_definition(definition_or_path, default_resolve: nil, interpreter: true, parser: GraphQL.default_parser, using: {})
793
+ def self.from_definition(definition_or_path, default_resolve: nil, parser: GraphQL.default_parser, using: {})
793
794
  # If the file ends in `.graphql`, treat it like a filepath
794
795
  if definition_or_path.end_with?(".graphql")
795
796
  GraphQL::Schema::BuildFromDefinition.from_definition_path(
@@ -797,7 +798,6 @@ module GraphQL
797
798
  default_resolve: default_resolve,
798
799
  parser: parser,
799
800
  using: using,
800
- interpreter: interpreter,
801
801
  )
802
802
  else
803
803
  GraphQL::Schema::BuildFromDefinition.from_definition(
@@ -805,7 +805,6 @@ module GraphQL
805
805
  default_resolve: default_resolve,
806
806
  parser: parser,
807
807
  using: using,
808
- interpreter: interpreter,
809
808
  )
810
809
  end
811
810
  end
@@ -1119,15 +1118,14 @@ module GraphQL
1119
1118
  type.possible_types(context: context)
1120
1119
  else
1121
1120
  stored_possible_types = own_possible_types[type.graphql_name]
1122
- visible_possible_types = if stored_possible_types && type.kind.interface?
1123
- stored_possible_types.select do |possible_type|
1124
- # Use `.graphql_name` comparison to match legacy vs class-based types.
1125
- # When we don't need to support legacy `.define` types, use `.include?(type)` instead.
1126
- possible_type.interfaces(context).any? { |interface| interface.graphql_name == type.graphql_name }
1127
- end
1128
- else
1129
- stored_possible_types
1130
- end
1121
+ visible_possible_types = stored_possible_types.select do |possible_type|
1122
+ next true unless type.kind.interface?
1123
+ next true unless possible_type.kind.object?
1124
+
1125
+ # Use `.graphql_name` comparison to match legacy vs class-based types.
1126
+ # When we don't need to support legacy `.define` types, use `.include?(type)` instead.
1127
+ possible_type.interfaces(context).any? { |interface| interface.graphql_name == type.graphql_name }
1128
+ end if stored_possible_types
1131
1129
  visible_possible_types ||
1132
1130
  introspection_system.possible_types[type.graphql_name] ||
1133
1131
  (
@@ -1158,6 +1156,14 @@ module GraphQL
1158
1156
  end
1159
1157
  end
1160
1158
 
1159
+ # @api private
1160
+ # @see GraphQL::Dataloader
1161
+ def dataloader_class
1162
+ @dataloader_class || GraphQL::Dataloader::NullDataloader
1163
+ end
1164
+
1165
+ attr_writer :dataloader_class
1166
+
1161
1167
  def references_to(to_type = nil, from: nil)
1162
1168
  @own_references_to ||= Hash.new { |h, k| h[k] = [] }
1163
1169
  if to_type
@@ -1301,7 +1307,7 @@ module GraphQL
1301
1307
  attr_writer :analysis_engine
1302
1308
 
1303
1309
  def analysis_engine
1304
- @analysis_engine || find_inherited_value(:analysis_engine, GraphQL::Analysis)
1310
+ @analysis_engine || find_inherited_value(:analysis_engine, self.default_analysis_engine)
1305
1311
  end
1306
1312
 
1307
1313
  def using_ast_analysis?
@@ -1309,11 +1315,9 @@ module GraphQL
1309
1315
  end
1310
1316
 
1311
1317
  def interpreter?
1312
- if defined?(@interpreter)
1313
- @interpreter
1314
- else
1315
- find_inherited_value(:interpreter?, false)
1316
- end
1318
+ query_execution_strategy == GraphQL::Execution::Interpreter &&
1319
+ mutation_execution_strategy == GraphQL::Execution::Interpreter &&
1320
+ subscription_execution_strategy == GraphQL::Execution::Interpreter
1317
1321
  end
1318
1322
 
1319
1323
  attr_writer :interpreter
@@ -1397,7 +1401,15 @@ module GraphQL
1397
1401
  if superclass <= GraphQL::Schema
1398
1402
  superclass.default_execution_strategy
1399
1403
  else
1400
- @default_execution_strategy ||= GraphQL::Execution::Execute
1404
+ @default_execution_strategy ||= GraphQL::Execution::Interpreter
1405
+ end
1406
+ end
1407
+
1408
+ def default_analysis_engine
1409
+ if superclass <= GraphQL::Schema
1410
+ superclass.default_analysis_engine
1411
+ else
1412
+ @default_analysis_engine ||= GraphQL::Analysis::AST
1401
1413
  end
1402
1414
  end
1403
1415
 
@@ -1551,6 +1563,10 @@ module GraphQL
1551
1563
  end
1552
1564
 
1553
1565
  def instrument(instrument_step, instrumenter, options = {})
1566
+ if instrument_step == :field
1567
+ warn "Field instrumentation (#{instrumenter.inspect}) will be removed in GraphQL-Ruby 2.0, please upgrade to field extensions: https://graphql-ruby.org/type_definitions/field_extensions.html"
1568
+ end
1569
+
1554
1570
  step = if instrument_step == :field && options[:after_built_ins]
1555
1571
  :field_after_built_ins
1556
1572
  else
@@ -1572,9 +1588,12 @@ module GraphQL
1572
1588
 
1573
1589
  # Attach a single directive to this schema
1574
1590
  # @param new_directive [Class]
1591
+ # @return void
1575
1592
  def directive(new_directive)
1576
- add_type_and_traverse(new_directive, root: false)
1577
- own_directives[new_directive.graphql_name] = new_directive
1593
+ own_directives[new_directive.graphql_name] ||= begin
1594
+ add_type_and_traverse(new_directive, root: false)
1595
+ new_directive
1596
+ end
1578
1597
  end
1579
1598
 
1580
1599
  def default_directives
@@ -1606,6 +1625,7 @@ module GraphQL
1606
1625
 
1607
1626
  def middleware(new_middleware = nil)
1608
1627
  if new_middleware
1628
+ warn "Middleware will be removed in GraphQL-Ruby 2.0, please upgrade to Field Extensions: https://graphql-ruby.org/type_definitions/field_extensions.html"
1609
1629
  own_middleware << new_middleware
1610
1630
  else
1611
1631
  # TODO make sure this is cached when running a query
@@ -1705,6 +1725,7 @@ module GraphQL
1705
1725
  else
1706
1726
  @lazy_methods = GraphQL::Execution::Lazy::LazyMethodMap.new
1707
1727
  @lazy_methods.set(GraphQL::Execution::Lazy, :value)
1728
+ @lazy_methods.set(GraphQL::Dataloader::Request, :load)
1708
1729
  end
1709
1730
  end
1710
1731
  @lazy_methods
@@ -1901,13 +1922,16 @@ module GraphQL
1901
1922
  end
1902
1923
  else
1903
1924
  own_types[type.graphql_name] = type
1925
+ add_directives_from(type)
1904
1926
  if type.kind.fields?
1905
1927
  type.fields.each do |name, field|
1906
1928
  field_type = field.type.unwrap
1907
1929
  references_to(field_type, from: field)
1908
1930
  field_path = path + [name]
1909
1931
  add_type(field_type, owner: field, late_types: late_types, path: field_path)
1932
+ add_directives_from(field)
1910
1933
  field.arguments.each do |arg_name, arg|
1934
+ add_directives_from(arg)
1911
1935
  arg_type = arg.type.unwrap
1912
1936
  references_to(arg_type, from: arg)
1913
1937
  add_type(arg_type, owner: arg, late_types: late_types, path: field_path + [arg_name])
@@ -1916,6 +1940,7 @@ module GraphQL
1916
1940
  end
1917
1941
  if type.kind.input_object?
1918
1942
  type.arguments.each do |arg_name, arg|
1943
+ add_directives_from(arg)
1919
1944
  arg_type = arg.type.unwrap
1920
1945
  references_to(arg_type, from: arg)
1921
1946
  add_type(arg_type, owner: arg, late_types: late_types, path: path + [arg_name])
@@ -1953,8 +1978,20 @@ module GraphQL
1953
1978
  end
1954
1979
  end
1955
1980
  end
1981
+
1982
+ def add_directives_from(owner)
1983
+ owner.directives.each { |dir| directive(dir.class) }
1984
+ end
1956
1985
  end
1957
1986
 
1987
+ def dataloader_class
1988
+ self.class.dataloader_class
1989
+ end
1990
+
1991
+ # Install these here so that subclasses will also install it.
1992
+ use(GraphQL::Execution::Errors)
1993
+ use(GraphQL::Pagination::Connections)
1994
+
1958
1995
  protected
1959
1996
 
1960
1997
  def rescues?
@@ -10,6 +10,10 @@ module GraphQL
10
10
  include GraphQL::Schema::Member::AcceptsDefinition
11
11
  include GraphQL::Schema::Member::HasPath
12
12
  include GraphQL::Schema::Member::HasAstNode
13
+ include GraphQL::Schema::Member::HasDirectives
14
+ include GraphQL::Schema::Member::HasDeprecationReason
15
+ include GraphQL::Schema::Member::HasValidators
16
+ include GraphQL::Schema::FindInheritedValue::EmptyObjects
13
17
 
14
18
  NO_DEFAULT = :__no_default__
15
19
 
@@ -45,8 +49,10 @@ module GraphQL
45
49
  # @param camelize [Boolean] if true, the name will be camelized when building the schema
46
50
  # @param from_resolver [Boolean] if true, a Resolver class defined this argument
47
51
  # @param method_access [Boolean] If false, don't build method access on legacy {Query::Arguments} instances.
52
+ # @param directives [Hash{Class => Hash}]
48
53
  # @param deprecation_reason [String]
49
- def initialize(arg_name = nil, type_expr = nil, desc = nil, required:, type: nil, name: nil, loads: nil, description: nil, ast_node: nil, default_value: NO_DEFAULT, as: nil, from_resolver: false, camelize: true, prepare: nil, method_access: true, owner:, deprecation_reason: nil, &definition_block)
54
+ # @param validates [Hash, nil] Options for building validators, if any should be applied
55
+ def initialize(arg_name = nil, type_expr = nil, desc = nil, required:, type: nil, name: nil, loads: nil, description: nil, ast_node: nil, default_value: NO_DEFAULT, as: nil, from_resolver: false, camelize: true, prepare: nil, method_access: true, owner:, validates: nil, directives: nil, deprecation_reason: nil, &definition_block)
50
56
  arg_name ||= name
51
57
  @name = -(camelize ? Member::BuildType.camelize(arg_name.to_s) : arg_name.to_s)
52
58
  @type_expr = type_expr || type
@@ -63,6 +69,14 @@ module GraphQL
63
69
  @method_access = method_access
64
70
  self.deprecation_reason = deprecation_reason
65
71
 
72
+ if directives
73
+ directives.each do |dir_class, dir_options|
74
+ directive(dir_class, **dir_options)
75
+ end
76
+ end
77
+
78
+ self.validates(validates)
79
+
66
80
  if definition_block
67
81
  if definition_block.arity == 1
68
82
  instance_exec(self, &definition_block)
@@ -94,14 +108,16 @@ module GraphQL
94
108
  # @return [String] Deprecation reason for this argument
95
109
  def deprecation_reason(text = nil)
96
110
  if text
97
- validate_deprecated_or_optional(null: @null, deprecation_reason: text)
98
- @deprecation_reason = text
111
+ self.deprecation_reason = text
99
112
  else
100
- @deprecation_reason
113
+ super()
101
114
  end
102
115
  end
103
116
 
104
- alias_method :deprecation_reason=, :deprecation_reason
117
+ def deprecation_reason=(new_reason)
118
+ validate_deprecated_or_optional(null: @null, deprecation_reason: new_reason)
119
+ super
120
+ end
105
121
 
106
122
  def visible?(context)
107
123
  true
@@ -157,8 +173,8 @@ module GraphQL
157
173
  if NO_DEFAULT != @default_value
158
174
  argument.default_value = @default_value
159
175
  end
160
- if @deprecation_reason
161
- argument.deprecation_reason = @deprecation_reason
176
+ if self.deprecation_reason
177
+ argument.deprecation_reason = self.deprecation_reason
162
178
  end
163
179
  argument
164
180
  end
@@ -199,6 +215,8 @@ module GraphQL
199
215
  value = value.prepare
200
216
  end
201
217
 
218
+ Schema::Validator.validate!(validators, obj, context, value)
219
+
202
220
  if @prepare.nil?
203
221
  value
204
222
  elsif @prepare.is_a?(String) || @prepare.is_a?(Symbol)
@@ -3,6 +3,7 @@ require "graphql/schema/build_from_definition/resolve_map"
3
3
 
4
4
  module GraphQL
5
5
  class Schema
6
+ # TODO Populate `.directive(...)` from here
6
7
  module BuildFromDefinition
7
8
  if !String.method_defined?(:-@)
8
9
  using GraphQL::StringDedupBackport
@@ -18,8 +19,8 @@ module GraphQL
18
19
  from_document(parser.parse_file(definition_path), **kwargs)
19
20
  end
20
21
 
21
- def from_document(document, default_resolve:, using: {}, relay: false, interpreter: true)
22
- Builder.build(document, default_resolve: default_resolve || {}, relay: relay, using: using, interpreter: interpreter)
22
+ def from_document(document, default_resolve:, using: {}, relay: false)
23
+ Builder.build(document, default_resolve: default_resolve || {}, relay: relay, using: using)
23
24
  end
24
25
  end
25
26
 
@@ -27,7 +28,7 @@ module GraphQL
27
28
  module Builder
28
29
  extend self
29
30
 
30
- def build(document, default_resolve:, using: {}, interpreter: true, relay:)
31
+ def build(document, default_resolve:, using: {}, relay:)
31
32
  raise InvalidDocumentError.new('Must provide a document ast.') if !document || !document.is_a?(GraphQL::Language::Nodes::Document)
32
33
 
33
34
  if default_resolve.is_a?(Hash)
@@ -41,45 +42,43 @@ module GraphQL
41
42
  schema_definition = schema_defns.first
42
43
  types = {}
43
44
  directives = {}
44
- type_resolver = ->(type) { resolve_type(types, type) }
45
+ type_resolver = build_resolve_type(types, directives, ->(type_name) { types[type_name] ||= Schema::LateBoundType.new(type_name)})
46
+ # Make a different type resolver because we need to coerce directive arguments
47
+ # _while_ building the schema.
48
+ # It will dig for a type if it encounters a custom type. This could be a problem if there are cycles.
49
+ directive_type_resolver = nil
50
+ directive_type_resolver = build_resolve_type(GraphQL::Schema::BUILT_IN_TYPES, directives, ->(type_name) {
51
+ types[type_name] ||= begin
52
+ defn = document.definitions.find { |d| d.respond_to?(:name) && d.name == type_name }
53
+ build_definition_from_node(defn, directive_type_resolver, default_resolve)
54
+ end
55
+ })
45
56
 
46
57
  document.definitions.each do |definition|
47
- case definition
48
- when GraphQL::Language::Nodes::SchemaDefinition
49
- nil # already handled
50
- when GraphQL::Language::Nodes::EnumTypeDefinition
51
- types[definition.name] = build_enum_type(definition, type_resolver)
52
- when GraphQL::Language::Nodes::ObjectTypeDefinition
53
- types[definition.name] = build_object_type(definition, type_resolver)
54
- when GraphQL::Language::Nodes::InterfaceTypeDefinition
55
- types[definition.name] = build_interface_type(definition, type_resolver)
56
- when GraphQL::Language::Nodes::UnionTypeDefinition
57
- types[definition.name] = build_union_type(definition, type_resolver)
58
- when GraphQL::Language::Nodes::ScalarTypeDefinition
59
- types[definition.name] = build_scalar_type(definition, type_resolver, default_resolve: default_resolve)
60
- when GraphQL::Language::Nodes::InputObjectTypeDefinition
61
- types[definition.name] = build_input_object_type(definition, type_resolver)
62
- when GraphQL::Language::Nodes::DirectiveDefinition
63
- directives[definition.name] = build_directive(definition, type_resolver)
58
+ if definition.is_a?(GraphQL::Language::Nodes::DirectiveDefinition)
59
+ directives[definition.name] = build_directive(definition, directive_type_resolver)
64
60
  end
65
61
  end
66
62
 
67
- # At this point, if types named by the built in types are _late-bound_ types,
68
- # that means they were referenced in the schema but not defined in the schema.
69
- # That's supported for built-in types. (Eg, you can use `String` without defining it.)
70
- # In that case, insert the concrete type definition now.
71
- #
72
- # However, if the type in `types` is a _concrete_ type definition, that means that
73
- # the document contained an explicit definition of the scalar type.
74
- # Don't override it in this case.
75
- GraphQL::Schema::BUILT_IN_TYPES.each do |scalar_name, built_in_scalar|
76
- existing_type = types[scalar_name]
77
- if existing_type.is_a?(GraphQL::Schema::LateBoundType)
78
- types[scalar_name] = built_in_scalar
63
+ directives = GraphQL::Schema.default_directives.merge(directives)
64
+
65
+ # In case any directives referenced built-in types for their arguments:
66
+ replace_late_bound_types_with_built_in(types)
67
+
68
+ document.definitions.each do |definition|
69
+ case definition
70
+ when GraphQL::Language::Nodes::SchemaDefinition, GraphQL::Language::Nodes::DirectiveDefinition
71
+ nil # already handled
72
+ else
73
+ # It's possible that this was already loaded by the directives
74
+ prev_type = types[definition.name]
75
+ if prev_type.nil? || prev_type.is_a?(Schema::LateBoundType)
76
+ types[definition.name] = build_definition_from_node(definition, type_resolver, default_resolve)
77
+ end
79
78
  end
80
79
  end
81
80
 
82
- directives = GraphQL::Schema.default_directives.merge(directives)
81
+ replace_late_bound_types_with_built_in(types)
83
82
 
84
83
  if schema_definition
85
84
  if schema_definition.query
@@ -133,11 +132,6 @@ module GraphQL
133
132
  ast_node(schema_definition)
134
133
  end
135
134
 
136
- if interpreter
137
- use GraphQL::Execution::Interpreter
138
- use GraphQL::Analysis::AST
139
- end
140
-
141
135
  using.each do |plugin, options|
142
136
  if options
143
137
  use(plugin, **options)
@@ -169,10 +163,85 @@ module GraphQL
169
163
  raise(GraphQL::RequiredImplementationMissingError, "Generated Schema cannot use Interface or Union types for execution. Implement resolve_type on your resolver.")
170
164
  }
171
165
 
166
+ def build_definition_from_node(definition, type_resolver, default_resolve)
167
+ case definition
168
+ when GraphQL::Language::Nodes::EnumTypeDefinition
169
+ build_enum_type(definition, type_resolver)
170
+ when GraphQL::Language::Nodes::ObjectTypeDefinition
171
+ build_object_type(definition, type_resolver)
172
+ when GraphQL::Language::Nodes::InterfaceTypeDefinition
173
+ build_interface_type(definition, type_resolver)
174
+ when GraphQL::Language::Nodes::UnionTypeDefinition
175
+ build_union_type(definition, type_resolver)
176
+ when GraphQL::Language::Nodes::ScalarTypeDefinition
177
+ build_scalar_type(definition, type_resolver, default_resolve: default_resolve)
178
+ when GraphQL::Language::Nodes::InputObjectTypeDefinition
179
+ build_input_object_type(definition, type_resolver)
180
+ end
181
+ end
182
+
183
+ # Modify `types`, replacing any late-bound references to built-in types
184
+ # with their actual definitions.
185
+ #
186
+ # (Schema definitions are allowed to reference those built-ins without redefining them.)
187
+ # @return void
188
+ def replace_late_bound_types_with_built_in(types)
189
+ GraphQL::Schema::BUILT_IN_TYPES.each do |scalar_name, built_in_scalar|
190
+ existing_type = types[scalar_name]
191
+ if existing_type.is_a?(GraphQL::Schema::LateBoundType)
192
+ types[scalar_name] = built_in_scalar
193
+ end
194
+ end
195
+ end
196
+
197
+ def build_directives(definition, ast_node, type_resolver)
198
+ dirs = prepare_directives(ast_node, type_resolver)
199
+ dirs.each do |dir_class, options|
200
+ definition.directive(dir_class, **options)
201
+ end
202
+ end
203
+
204
+ def prepare_directives(ast_node, type_resolver)
205
+ dirs = {}
206
+ ast_node.directives.each do |dir_node|
207
+ if dir_node.name == "deprecated"
208
+ # This is handled using `deprecation_reason`
209
+ next
210
+ else
211
+ dir_class = type_resolver.call(dir_node.name)
212
+ if dir_class.nil?
213
+ raise ArgumentError, "No definition for @#{dir_node.name} on #{ast_node.name} at #{ast_node.line}:#{ast_node.col}"
214
+ end
215
+ options = args_to_kwargs(dir_class, dir_node)
216
+ dirs[dir_class] = options
217
+ end
218
+ end
219
+ dirs
220
+ end
221
+
222
+ def args_to_kwargs(arg_owner, node)
223
+ if node.respond_to?(:arguments)
224
+ kwargs = {}
225
+ node.arguments.each do |arg_node|
226
+ arg_defn = arg_owner.get_argument(arg_node.name)
227
+ kwargs[arg_defn.keyword] = args_to_kwargs(arg_defn.type.unwrap, arg_node.value)
228
+ end
229
+ kwargs
230
+ elsif node.is_a?(Array)
231
+ node.map { |n| args_to_kwargs(arg_owner, n) }
232
+ elsif node.is_a?(Language::Nodes::Enum)
233
+ node.name
234
+ else
235
+ # scalar
236
+ node
237
+ end
238
+ end
239
+
172
240
  def build_enum_type(enum_type_definition, type_resolver)
173
241
  builder = self
174
242
  Class.new(GraphQL::Schema::Enum) do
175
243
  graphql_name(enum_type_definition.name)
244
+ builder.build_directives(self, enum_type_definition, type_resolver)
176
245
  description(enum_type_definition.description)
177
246
  ast_node(enum_type_definition)
178
247
  enum_type_definition.values.each do |enum_value_definition|
@@ -180,6 +249,7 @@ module GraphQL
180
249
  value: enum_value_definition.name,
181
250
  deprecation_reason: builder.build_deprecation_reason(enum_value_definition.directives),
182
251
  description: enum_value_definition.description,
252
+ directives: builder.prepare_directives(enum_value_definition, type_resolver),
183
253
  ast_node: enum_value_definition,
184
254
  )
185
255
  end
@@ -202,6 +272,7 @@ module GraphQL
202
272
  graphql_name(scalar_type_definition.name)
203
273
  description(scalar_type_definition.description)
204
274
  ast_node(scalar_type_definition)
275
+ builder.build_directives(self, scalar_type_definition, type_resolver)
205
276
 
206
277
  if default_resolve.respond_to?(:coerce_input)
207
278
  # Put these method definitions in another method to avoid retaining `type_resolve`
@@ -219,11 +290,13 @@ module GraphQL
219
290
  end
220
291
 
221
292
  def build_union_type(union_type_definition, type_resolver)
293
+ builder = self
222
294
  Class.new(GraphQL::Schema::Union) do
223
295
  graphql_name(union_type_definition.name)
224
296
  description(union_type_definition.description)
225
297
  possible_types(*union_type_definition.types.map { |type_name| type_resolver.call(type_name) })
226
298
  ast_node(union_type_definition)
299
+ builder.build_directives(self, union_type_definition, type_resolver)
227
300
  end
228
301
  end
229
302
 
@@ -234,6 +307,7 @@ module GraphQL
234
307
  graphql_name(object_type_definition.name)
235
308
  description(object_type_definition.description)
236
309
  ast_node(object_type_definition)
310
+ builder.build_directives(self, object_type_definition, type_resolver)
237
311
 
238
312
  object_type_definition.interfaces.each do |interface_name|
239
313
  interface_defn = type_resolver.call(interface_name)
@@ -250,6 +324,7 @@ module GraphQL
250
324
  graphql_name(input_object_type_definition.name)
251
325
  description(input_object_type_definition.description)
252
326
  ast_node(input_object_type_definition)
327
+ builder.build_directives(self, input_object_type_definition, type_resolver)
253
328
  builder.build_arguments(self, input_object_type_definition.fields, type_resolver)
254
329
  end
255
330
  end
@@ -290,6 +365,7 @@ module GraphQL
290
365
  ast_node: argument_defn,
291
366
  camelize: false,
292
367
  method_access: false,
368
+ directives: prepare_directives(argument_defn, type_resolver),
293
369
  **default_value_kwargs
294
370
  )
295
371
  end
@@ -313,6 +389,7 @@ module GraphQL
313
389
  graphql_name(interface_type_definition.name)
314
390
  description(interface_type_definition.description)
315
391
  ast_node(interface_type_definition)
392
+ builder.build_directives(self, interface_type_definition, type_resolver)
316
393
 
317
394
  builder.build_fields(self, interface_type_definition.fields, type_resolver, default_resolve: nil)
318
395
  end
@@ -335,6 +412,7 @@ module GraphQL
335
412
  ast_node: field_definition,
336
413
  method_conflict_warning: false,
337
414
  camelize: false,
415
+ directives: prepare_directives(field_definition, type_resolver),
338
416
  resolver_method: resolve_method_name,
339
417
  )
340
418
 
@@ -353,18 +431,28 @@ module GraphQL
353
431
  end
354
432
  end
355
433
 
356
- def resolve_type(types, ast_node)
357
- case ast_node
358
- when GraphQL::Language::Nodes::TypeName
359
- type_name = ast_node.name
360
- types[type_name] ||= GraphQL::Schema::LateBoundType.new(type_name)
361
- when GraphQL::Language::Nodes::NonNullType
362
- resolve_type(types, ast_node.of_type).to_non_null_type
363
- when GraphQL::Language::Nodes::ListType
364
- resolve_type(types, ast_node.of_type).to_list_type
365
- else
366
- raise "Unexpected ast_node: #{ast_node.inspect}"
367
- end
434
+ def build_resolve_type(lookup_hash, directives, missing_type_handler)
435
+ resolve_type_proc = nil
436
+ resolve_type_proc = ->(ast_node) {
437
+ case ast_node
438
+ when GraphQL::Language::Nodes::TypeName
439
+ type_name = ast_node.name
440
+ if lookup_hash.key?(type_name)
441
+ lookup_hash[type_name]
442
+ else
443
+ missing_type_handler.call(type_name)
444
+ end
445
+ when GraphQL::Language::Nodes::NonNullType
446
+ resolve_type_proc.call(ast_node.of_type).to_non_null_type
447
+ when GraphQL::Language::Nodes::ListType
448
+ resolve_type_proc.call(ast_node.of_type).to_list_type
449
+ when String
450
+ directives[ast_node]
451
+ else
452
+ raise "Unexpected ast_node: #{ast_node.inspect}"
453
+ end
454
+ }
455
+ resolve_type_proc
368
456
  end
369
457
 
370
458
  def resolve_type_name(type)