graphql 1.11.7 → 1.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of graphql might be problematic. Click here for more details.

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 +61 -23
  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 +30 -5
  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
@@ -1157,6 +1156,14 @@ module GraphQL
1157
1156
  end
1158
1157
  end
1159
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
+
1160
1167
  def references_to(to_type = nil, from: nil)
1161
1168
  @own_references_to ||= Hash.new { |h, k| h[k] = [] }
1162
1169
  if to_type
@@ -1300,7 +1307,7 @@ module GraphQL
1300
1307
  attr_writer :analysis_engine
1301
1308
 
1302
1309
  def analysis_engine
1303
- @analysis_engine || find_inherited_value(:analysis_engine, GraphQL::Analysis)
1310
+ @analysis_engine || find_inherited_value(:analysis_engine, self.default_analysis_engine)
1304
1311
  end
1305
1312
 
1306
1313
  def using_ast_analysis?
@@ -1308,11 +1315,9 @@ module GraphQL
1308
1315
  end
1309
1316
 
1310
1317
  def interpreter?
1311
- if defined?(@interpreter)
1312
- @interpreter
1313
- else
1314
- find_inherited_value(:interpreter?, false)
1315
- end
1318
+ query_execution_strategy == GraphQL::Execution::Interpreter &&
1319
+ mutation_execution_strategy == GraphQL::Execution::Interpreter &&
1320
+ subscription_execution_strategy == GraphQL::Execution::Interpreter
1316
1321
  end
1317
1322
 
1318
1323
  attr_writer :interpreter
@@ -1396,7 +1401,15 @@ module GraphQL
1396
1401
  if superclass <= GraphQL::Schema
1397
1402
  superclass.default_execution_strategy
1398
1403
  else
1399
- @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
1400
1413
  end
1401
1414
  end
1402
1415
 
@@ -1550,6 +1563,10 @@ module GraphQL
1550
1563
  end
1551
1564
 
1552
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
+
1553
1570
  step = if instrument_step == :field && options[:after_built_ins]
1554
1571
  :field_after_built_ins
1555
1572
  else
@@ -1571,9 +1588,12 @@ module GraphQL
1571
1588
 
1572
1589
  # Attach a single directive to this schema
1573
1590
  # @param new_directive [Class]
1591
+ # @return void
1574
1592
  def directive(new_directive)
1575
- add_type_and_traverse(new_directive, root: false)
1576
- 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
1577
1597
  end
1578
1598
 
1579
1599
  def default_directives
@@ -1605,6 +1625,7 @@ module GraphQL
1605
1625
 
1606
1626
  def middleware(new_middleware = nil)
1607
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"
1608
1629
  own_middleware << new_middleware
1609
1630
  else
1610
1631
  # TODO make sure this is cached when running a query
@@ -1704,6 +1725,7 @@ module GraphQL
1704
1725
  else
1705
1726
  @lazy_methods = GraphQL::Execution::Lazy::LazyMethodMap.new
1706
1727
  @lazy_methods.set(GraphQL::Execution::Lazy, :value)
1728
+ @lazy_methods.set(GraphQL::Dataloader::Request, :load)
1707
1729
  end
1708
1730
  end
1709
1731
  @lazy_methods
@@ -1900,13 +1922,16 @@ module GraphQL
1900
1922
  end
1901
1923
  else
1902
1924
  own_types[type.graphql_name] = type
1925
+ add_directives_from(type)
1903
1926
  if type.kind.fields?
1904
1927
  type.fields.each do |name, field|
1905
1928
  field_type = field.type.unwrap
1906
1929
  references_to(field_type, from: field)
1907
1930
  field_path = path + [name]
1908
1931
  add_type(field_type, owner: field, late_types: late_types, path: field_path)
1932
+ add_directives_from(field)
1909
1933
  field.arguments.each do |arg_name, arg|
1934
+ add_directives_from(arg)
1910
1935
  arg_type = arg.type.unwrap
1911
1936
  references_to(arg_type, from: arg)
1912
1937
  add_type(arg_type, owner: arg, late_types: late_types, path: field_path + [arg_name])
@@ -1915,6 +1940,7 @@ module GraphQL
1915
1940
  end
1916
1941
  if type.kind.input_object?
1917
1942
  type.arguments.each do |arg_name, arg|
1943
+ add_directives_from(arg)
1918
1944
  arg_type = arg.type.unwrap
1919
1945
  references_to(arg_type, from: arg)
1920
1946
  add_type(arg_type, owner: arg, late_types: late_types, path: path + [arg_name])
@@ -1952,8 +1978,20 @@ module GraphQL
1952
1978
  end
1953
1979
  end
1954
1980
  end
1981
+
1982
+ def add_directives_from(owner)
1983
+ owner.directives.each { |dir| directive(dir.class) }
1984
+ end
1955
1985
  end
1956
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
+
1957
1995
  protected
1958
1996
 
1959
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)