graphql 1.11.12 → 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 (124) 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/analysis/analyze_query.rb +7 -0
  11. data/lib/graphql/analysis/ast/visitor.rb +9 -1
  12. data/lib/graphql/analysis/ast.rb +11 -2
  13. data/lib/graphql/backtrace/legacy_tracer.rb +56 -0
  14. data/lib/graphql/backtrace/table.rb +22 -2
  15. data/lib/graphql/backtrace/tracer.rb +40 -9
  16. data/lib/graphql/backtrace.rb +28 -19
  17. data/lib/graphql/backwards_compatibility.rb +1 -0
  18. data/lib/graphql/compatibility/execution_specification.rb +1 -0
  19. data/lib/graphql/compatibility/lazy_execution_specification.rb +2 -0
  20. data/lib/graphql/compatibility/query_parser_specification.rb +2 -0
  21. data/lib/graphql/compatibility/schema_parser_specification.rb +2 -0
  22. data/lib/graphql/dataloader/null_dataloader.rb +21 -0
  23. data/lib/graphql/dataloader/request.rb +24 -0
  24. data/lib/graphql/dataloader/request_all.rb +22 -0
  25. data/lib/graphql/dataloader/source.rb +93 -0
  26. data/lib/graphql/dataloader.rb +197 -0
  27. data/lib/graphql/define/assign_global_id_field.rb +1 -1
  28. data/lib/graphql/define/instance_definable.rb +32 -2
  29. data/lib/graphql/define/type_definer.rb +5 -5
  30. data/lib/graphql/deprecated_dsl.rb +5 -0
  31. data/lib/graphql/enum_type.rb +2 -0
  32. data/lib/graphql/execution/errors.rb +4 -0
  33. data/lib/graphql/execution/execute.rb +7 -0
  34. data/lib/graphql/execution/interpreter/arguments.rb +51 -14
  35. data/lib/graphql/execution/interpreter/handles_raw_value.rb +0 -7
  36. data/lib/graphql/execution/interpreter/runtime.rb +210 -124
  37. data/lib/graphql/execution/interpreter.rb +10 -6
  38. data/lib/graphql/execution/multiplex.rb +20 -6
  39. data/lib/graphql/function.rb +4 -0
  40. data/lib/graphql/input_object_type.rb +2 -0
  41. data/lib/graphql/interface_type.rb +3 -1
  42. data/lib/graphql/language/document_from_schema_definition.rb +50 -23
  43. data/lib/graphql/language/nodes.rb +0 -5
  44. data/lib/graphql/language/visitor.rb +0 -1
  45. data/lib/graphql/object_type.rb +2 -0
  46. data/lib/graphql/pagination/connection.rb +5 -1
  47. data/lib/graphql/pagination/connections.rb +6 -16
  48. data/lib/graphql/query/context.rb +4 -0
  49. data/lib/graphql/query/serial_execution.rb +1 -0
  50. data/lib/graphql/query/validation_pipeline.rb +1 -1
  51. data/lib/graphql/query.rb +2 -0
  52. data/lib/graphql/relay/base_connection.rb +7 -0
  53. data/lib/graphql/relay/connection_instrumentation.rb +4 -4
  54. data/lib/graphql/relay/connection_type.rb +1 -1
  55. data/lib/graphql/relay/mutation.rb +1 -0
  56. data/lib/graphql/relay/node.rb +3 -0
  57. data/lib/graphql/relay/type_extensions.rb +2 -0
  58. data/lib/graphql/scalar_type.rb +2 -0
  59. data/lib/graphql/schema/argument.rb +30 -10
  60. data/lib/graphql/schema/build_from_definition.rb +145 -58
  61. data/lib/graphql/schema/directive/flagged.rb +57 -0
  62. data/lib/graphql/schema/directive.rb +76 -0
  63. data/lib/graphql/schema/enum.rb +3 -0
  64. data/lib/graphql/schema/enum_value.rb +13 -7
  65. data/lib/graphql/schema/field/connection_extension.rb +3 -2
  66. data/lib/graphql/schema/field.rb +28 -10
  67. data/lib/graphql/schema/input_object.rb +36 -28
  68. data/lib/graphql/schema/interface.rb +1 -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/member.rb +4 -0
  77. data/lib/graphql/schema/object.rb +11 -0
  78. data/lib/graphql/schema/printer.rb +5 -4
  79. data/lib/graphql/schema/resolver/has_payload_type.rb +2 -0
  80. data/lib/graphql/schema/resolver.rb +7 -0
  81. data/lib/graphql/schema/subscription.rb +19 -1
  82. data/lib/graphql/schema/timeout_middleware.rb +2 -0
  83. data/lib/graphql/schema/validation.rb +2 -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/schema/validator.rb +163 -0
  91. data/lib/graphql/schema.rb +72 -49
  92. data/lib/graphql/static_validation/base_visitor.rb +0 -3
  93. data/lib/graphql/static_validation/rules/fields_will_merge.rb +4 -4
  94. data/lib/graphql/static_validation/rules/fragments_are_finite.rb +2 -2
  95. data/lib/graphql/static_validation/validation_context.rb +1 -6
  96. data/lib/graphql/static_validation/validator.rb +12 -14
  97. data/lib/graphql/subscriptions.rb +17 -20
  98. data/lib/graphql/tracing/appoptics_tracing.rb +3 -1
  99. data/lib/graphql/tracing/platform_tracing.rb +3 -1
  100. data/lib/graphql/tracing/skylight_tracing.rb +1 -1
  101. data/lib/graphql/tracing.rb +2 -2
  102. data/lib/graphql/types/relay/base_connection.rb +2 -92
  103. data/lib/graphql/types/relay/base_edge.rb +2 -35
  104. data/lib/graphql/types/relay/connection_behaviors.rb +123 -0
  105. data/lib/graphql/types/relay/default_relay.rb +27 -0
  106. data/lib/graphql/types/relay/edge_behaviors.rb +42 -0
  107. data/lib/graphql/types/relay/has_node_field.rb +41 -0
  108. data/lib/graphql/types/relay/has_nodes_field.rb +41 -0
  109. data/lib/graphql/types/relay/node.rb +2 -4
  110. data/lib/graphql/types/relay/node_behaviors.rb +15 -0
  111. data/lib/graphql/types/relay/node_field.rb +1 -19
  112. data/lib/graphql/types/relay/nodes_field.rb +1 -19
  113. data/lib/graphql/types/relay/page_info.rb +2 -14
  114. data/lib/graphql/types/relay/page_info_behaviors.rb +25 -0
  115. data/lib/graphql/types/relay.rb +11 -3
  116. data/lib/graphql/union_type.rb +2 -0
  117. data/lib/graphql/upgrader/member.rb +1 -0
  118. data/lib/graphql/upgrader/schema.rb +1 -0
  119. data/lib/graphql/version.rb +1 -1
  120. data/lib/graphql.rb +38 -4
  121. metadata +34 -9
  122. data/lib/graphql/types/relay/base_field.rb +0 -22
  123. data/lib/graphql/types/relay/base_interface.rb +0 -29
  124. data/lib/graphql/types/relay/base_object.rb +0 -26
@@ -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
@@ -157,7 +160,7 @@ module GraphQL
157
160
 
158
161
  accepts_definitions \
159
162
  :query_execution_strategy, :mutation_execution_strategy, :subscription_execution_strategy,
160
- :validate_timeout, :validate_max_errors, :max_depth, :max_complexity, :default_max_page_size,
163
+ :validate_timeout, :max_depth, :max_complexity, :default_max_page_size,
161
164
  :orphan_types, :resolve_type, :type_error, :parse_error,
162
165
  :error_bubbling,
163
166
  :raise_definition_error,
@@ -196,7 +199,7 @@ module GraphQL
196
199
  attr_accessor \
197
200
  :query, :mutation, :subscription,
198
201
  :query_execution_strategy, :mutation_execution_strategy, :subscription_execution_strategy,
199
- :validate_timeout, :validate_max_errors, :max_depth, :max_complexity, :default_max_page_size,
202
+ :validate_timeout, :max_depth, :max_complexity, :default_max_page_size,
200
203
  :orphan_types, :directives,
201
204
  :query_analyzers, :multiplex_analyzers, :instrumenters, :lazy_methods,
202
205
  :cursor_encoder,
@@ -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
@@ -366,11 +368,11 @@ module GraphQL
366
368
  validator_opts = { schema: self }
367
369
  rules && (validator_opts[:rules] = rules)
368
370
  validator = GraphQL::StaticValidation::Validator.new(**validator_opts)
369
- res = validator.validate(query, timeout: validate_timeout, max_errors: validate_max_errors)
371
+ res = validator.validate(query, timeout: validate_timeout)
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
@@ -951,7 +950,6 @@ module GraphQL
951
950
  schema_defn.mutation = mutation && mutation.graphql_definition
952
951
  schema_defn.subscription = subscription && subscription.graphql_definition
953
952
  schema_defn.validate_timeout = validate_timeout
954
- schema_defn.validate_max_errors = validate_max_errors
955
953
  schema_defn.max_complexity = max_complexity
956
954
  schema_defn.error_bubbling = error_bubbling
957
955
  schema_defn.max_depth = max_depth
@@ -1120,15 +1118,14 @@ module GraphQL
1120
1118
  type.possible_types(context: context)
1121
1119
  else
1122
1120
  stored_possible_types = own_possible_types[type.graphql_name]
1123
- visible_possible_types = if stored_possible_types && type.kind.interface?
1124
- stored_possible_types.select do |possible_type|
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
1129
- else
1130
- stored_possible_types
1131
- 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
1132
1129
  visible_possible_types ||
1133
1130
  introspection_system.possible_types[type.graphql_name] ||
1134
1131
  (
@@ -1159,6 +1156,14 @@ module GraphQL
1159
1156
  end
1160
1157
  end
1161
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
+
1162
1167
  def references_to(to_type = nil, from: nil)
1163
1168
  @own_references_to ||= Hash.new { |h, k| h[k] = [] }
1164
1169
  if to_type
@@ -1287,19 +1292,6 @@ module GraphQL
1287
1292
  end
1288
1293
  end
1289
1294
 
1290
- attr_writer :validate_max_errors
1291
-
1292
- def validate_max_errors(new_validate_max_errors = nil)
1293
- if new_validate_max_errors
1294
- @validate_max_errors = new_validate_max_errors
1295
- elsif defined?(@validate_max_errors)
1296
- @validate_max_errors
1297
- else
1298
- find_inherited_value(:validate_max_errors)
1299
- end
1300
- end
1301
-
1302
-
1303
1295
  attr_writer :max_complexity
1304
1296
 
1305
1297
  def max_complexity(max_complexity = nil)
@@ -1315,7 +1307,7 @@ module GraphQL
1315
1307
  attr_writer :analysis_engine
1316
1308
 
1317
1309
  def analysis_engine
1318
- @analysis_engine || find_inherited_value(:analysis_engine, GraphQL::Analysis)
1310
+ @analysis_engine || find_inherited_value(:analysis_engine, self.default_analysis_engine)
1319
1311
  end
1320
1312
 
1321
1313
  def using_ast_analysis?
@@ -1323,11 +1315,9 @@ module GraphQL
1323
1315
  end
1324
1316
 
1325
1317
  def interpreter?
1326
- if defined?(@interpreter)
1327
- @interpreter
1328
- else
1329
- find_inherited_value(:interpreter?, false)
1330
- end
1318
+ query_execution_strategy == GraphQL::Execution::Interpreter &&
1319
+ mutation_execution_strategy == GraphQL::Execution::Interpreter &&
1320
+ subscription_execution_strategy == GraphQL::Execution::Interpreter
1331
1321
  end
1332
1322
 
1333
1323
  attr_writer :interpreter
@@ -1411,7 +1401,15 @@ module GraphQL
1411
1401
  if superclass <= GraphQL::Schema
1412
1402
  superclass.default_execution_strategy
1413
1403
  else
1414
- @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
1415
1413
  end
1416
1414
  end
1417
1415
 
@@ -1565,6 +1563,10 @@ module GraphQL
1565
1563
  end
1566
1564
 
1567
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
+
1568
1570
  step = if instrument_step == :field && options[:after_built_ins]
1569
1571
  :field_after_built_ins
1570
1572
  else
@@ -1586,9 +1588,12 @@ module GraphQL
1586
1588
 
1587
1589
  # Attach a single directive to this schema
1588
1590
  # @param new_directive [Class]
1591
+ # @return void
1589
1592
  def directive(new_directive)
1590
- add_type_and_traverse(new_directive, root: false)
1591
- 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
1592
1597
  end
1593
1598
 
1594
1599
  def default_directives
@@ -1620,6 +1625,7 @@ module GraphQL
1620
1625
 
1621
1626
  def middleware(new_middleware = nil)
1622
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"
1623
1629
  own_middleware << new_middleware
1624
1630
  else
1625
1631
  # TODO make sure this is cached when running a query
@@ -1719,6 +1725,7 @@ module GraphQL
1719
1725
  else
1720
1726
  @lazy_methods = GraphQL::Execution::Lazy::LazyMethodMap.new
1721
1727
  @lazy_methods.set(GraphQL::Execution::Lazy, :value)
1728
+ @lazy_methods.set(GraphQL::Dataloader::Request, :load)
1722
1729
  end
1723
1730
  end
1724
1731
  @lazy_methods
@@ -1915,13 +1922,16 @@ module GraphQL
1915
1922
  end
1916
1923
  else
1917
1924
  own_types[type.graphql_name] = type
1925
+ add_directives_from(type)
1918
1926
  if type.kind.fields?
1919
1927
  type.fields.each do |name, field|
1920
1928
  field_type = field.type.unwrap
1921
1929
  references_to(field_type, from: field)
1922
1930
  field_path = path + [name]
1923
1931
  add_type(field_type, owner: field, late_types: late_types, path: field_path)
1932
+ add_directives_from(field)
1924
1933
  field.arguments.each do |arg_name, arg|
1934
+ add_directives_from(arg)
1925
1935
  arg_type = arg.type.unwrap
1926
1936
  references_to(arg_type, from: arg)
1927
1937
  add_type(arg_type, owner: arg, late_types: late_types, path: field_path + [arg_name])
@@ -1930,6 +1940,7 @@ module GraphQL
1930
1940
  end
1931
1941
  if type.kind.input_object?
1932
1942
  type.arguments.each do |arg_name, arg|
1943
+ add_directives_from(arg)
1933
1944
  arg_type = arg.type.unwrap
1934
1945
  references_to(arg_type, from: arg)
1935
1946
  add_type(arg_type, owner: arg, late_types: late_types, path: path + [arg_name])
@@ -1967,8 +1978,20 @@ module GraphQL
1967
1978
  end
1968
1979
  end
1969
1980
  end
1981
+
1982
+ def add_directives_from(owner)
1983
+ owner.directives.each { |dir| directive(dir.class) }
1984
+ end
1970
1985
  end
1971
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
+
1972
1995
  protected
1973
1996
 
1974
1997
  def rescues?
@@ -205,9 +205,6 @@ module GraphQL
205
205
  private
206
206
 
207
207
  def add_error(error, path: nil)
208
- if @context.too_many_errors?
209
- throw :too_many_validation_errors
210
- end
211
208
  error.path ||= (path || @path.dup)
212
209
  context.errors << error
213
210
  end
@@ -193,26 +193,26 @@ module GraphQL
193
193
  if node1.name != node2.name
194
194
  errored_nodes = [node1.name, node2.name].sort.join(" or ")
195
195
  msg = "Field '#{response_key}' has a field conflict: #{errored_nodes}?"
196
- add_error(GraphQL::StaticValidation::FieldsWillMergeError.new(
196
+ context.errors << GraphQL::StaticValidation::FieldsWillMergeError.new(
197
197
  msg,
198
198
  nodes: [node1, node2],
199
199
  path: [],
200
200
  field_name: response_key,
201
201
  conflicts: errored_nodes
202
- ))
202
+ )
203
203
  end
204
204
 
205
205
  if !same_arguments?(node1, node2)
206
206
  args = [serialize_field_args(node1), serialize_field_args(node2)]
207
207
  conflicts = args.map { |arg| GraphQL::Language.serialize(arg) }.join(" or ")
208
208
  msg = "Field '#{response_key}' has an argument conflict: #{conflicts}?"
209
- add_error(GraphQL::StaticValidation::FieldsWillMergeError.new(
209
+ context.errors << GraphQL::StaticValidation::FieldsWillMergeError.new(
210
210
  msg,
211
211
  nodes: [node1, node2],
212
212
  path: [],
213
213
  field_name: response_key,
214
214
  conflicts: conflicts
215
- ))
215
+ )
216
216
  end
217
217
  end
218
218
 
@@ -7,12 +7,12 @@ module GraphQL
7
7
  dependency_map = context.dependencies
8
8
  dependency_map.cyclical_definitions.each do |defn|
9
9
  if defn.node.is_a?(GraphQL::Language::Nodes::FragmentDefinition)
10
- add_error(GraphQL::StaticValidation::FragmentsAreFiniteError.new(
10
+ context.errors << GraphQL::StaticValidation::FragmentsAreFiniteError.new(
11
11
  "Fragment #{defn.name} contains an infinite loop",
12
12
  nodes: defn.node,
13
13
  path: defn.path,
14
14
  name: defn.name
15
- ))
15
+ )
16
16
  end
17
17
  end
18
18
  end
@@ -19,11 +19,10 @@ module GraphQL
19
19
 
20
20
  def_delegators :@query, :schema, :document, :fragments, :operations, :warden
21
21
 
22
- def initialize(query, visitor_class, max_errors)
22
+ def initialize(query, visitor_class)
23
23
  @query = query
24
24
  @literal_validator = LiteralValidator.new(context: query.context)
25
25
  @errors = []
26
- @max_errors = max_errors || Float::INFINITY
27
26
  @on_dependency_resolve_handlers = []
28
27
  @visitor = visitor_class.new(document, self)
29
28
  end
@@ -39,10 +38,6 @@ module GraphQL
39
38
  def validate_literal(ast_value, type)
40
39
  @literal_validator.validate(ast_value, type)
41
40
  end
42
-
43
- def too_many_errors?
44
- @errors.length >= @max_errors
45
- end
46
41
  end
47
42
  end
48
43
  end
@@ -22,9 +22,8 @@ module GraphQL
22
22
  # @param query [GraphQL::Query]
23
23
  # @param validate [Boolean]
24
24
  # @param timeout [Float] Number of seconds to wait before aborting validation. Any positive number may be used, including Floats to specify fractional seconds.
25
- # @param max_errors [Integer] Maximum number of errors before aborting validation. Any positive number will limit the number of errors. Defaults to nil for no limit.
26
25
  # @return [Array<Hash>]
27
- def validate(query, validate: true, timeout: nil, max_errors: nil)
26
+ def validate(query, validate: true, timeout: nil)
28
27
  query.trace("validate", { validate: validate, query: query }) do
29
28
  can_skip_rewrite = query.context.interpreter? && query.schema.using_ast_analysis? && query.schema.is_a?(Class)
30
29
  errors = if validate == false && can_skip_rewrite
@@ -33,26 +32,25 @@ module GraphQL
33
32
  rules_to_use = validate ? @rules : []
34
33
  visitor_class = BaseVisitor.including_rules(rules_to_use, rewrite: !can_skip_rewrite)
35
34
 
36
- context = GraphQL::StaticValidation::ValidationContext.new(query, visitor_class, max_errors)
35
+ context = GraphQL::StaticValidation::ValidationContext.new(query, visitor_class)
37
36
 
38
37
  begin
39
38
  # CAUTION: Usage of the timeout module makes the assumption that validation rules are stateless Ruby code that requires no cleanup if process was interrupted. This means no blocking IO calls, native gems, locks, or `rescue` clauses that must be reached.
40
39
  # A timeout value of 0 or nil will execute the block without any timeout.
41
40
  Timeout::timeout(timeout) do
42
-
43
- catch(:too_many_validation_errors) do
44
- # Attach legacy-style rules.
45
- # Only loop through rules if it has legacy-style rules
46
- unless (legacy_rules = rules_to_use - GraphQL::StaticValidation::ALL_RULES).empty?
47
- legacy_rules.each do |rule_class_or_module|
48
- if rule_class_or_module.method_defined?(:validate)
49
- rule_class_or_module.new.validate(context)
50
- end
41
+ # Attach legacy-style rules.
42
+ # Only loop through rules if it has legacy-style rules
43
+ unless (legacy_rules = rules_to_use - GraphQL::StaticValidation::ALL_RULES).empty?
44
+ legacy_rules.each do |rule_class_or_module|
45
+ if rule_class_or_module.method_defined?(:validate)
46
+ warn "Legacy validator rules will be removed from GraphQL-Ruby 2.0, use a module instead (see the built-in rules: https://github.com/rmosolgo/graphql-ruby/tree/master/lib/graphql/static_validation/rules)"
47
+ warn " -> Legacy validator: #{rule_class_or_module}"
48
+ rule_class_or_module.new.validate(context)
51
49
  end
52
50
  end
53
-
54
- context.visitor.visit
55
51
  end
52
+
53
+ context.visitor.visit
56
54
  end
57
55
  rescue Timeout::Error
58
56
  handle_timeout(query, context)
@@ -26,7 +26,9 @@ module GraphQL
26
26
 
27
27
  instrumentation = Subscriptions::Instrumentation.new(schema: schema)
28
28
  defn.instrument(:query, instrumentation)
29
- defn.instrument(:field, instrumentation)
29
+ if !schema.is_a?(Class)
30
+ defn.instrument(:field, instrumentation)
31
+ end
30
32
  options[:schema] = schema
31
33
  schema.subscriptions = self.new(**options)
32
34
  schema.add_subscription_extension_if_necessary
@@ -107,31 +109,26 @@ module GraphQL
107
109
  variables = query_data.fetch(:variables)
108
110
  context = query_data.fetch(:context)
109
111
  operation_name = query_data.fetch(:operation_name)
110
- result = nil
111
- # this will be set to `false` unless `.execute` is terminated
112
- # with a `throw :graphql_subscription_unsubscribed`
113
- unsubscribed = true
114
- catch(:graphql_subscription_unsubscribed) do
115
- catch(:graphql_no_subscription_update) do
116
- # Re-evaluate the saved query,
117
- # but if it terminates early with a `throw`,
118
- # it will stay `nil`
119
- result = @schema.execute(
120
- query: query_string,
121
- context: context,
122
- subscription_topic: event.topic,
123
- operation_name: operation_name,
124
- variables: variables,
125
- root_value: object,
126
- )
127
- end
128
- unsubscribed = false
112
+ result = @schema.execute(
113
+ query: query_string,
114
+ context: context,
115
+ subscription_topic: event.topic,
116
+ operation_name: operation_name,
117
+ variables: variables,
118
+ root_value: object,
119
+ )
120
+ subscriptions_context = result.context.namespace(:subscriptions)
121
+ if subscriptions_context[:no_update]
122
+ result = nil
129
123
  end
130
124
 
125
+ unsubscribed = subscriptions_context[:unsubscribed]
126
+
131
127
  if unsubscribed
132
128
  # `unsubscribe` was called, clean up on our side
133
129
  # TODO also send `{more: false}` to client?
134
130
  delete_subscription(subscription_id)
131
+ result = nil
135
132
  end
136
133
 
137
134
  result
@@ -57,7 +57,7 @@ module GraphQL
57
57
  def platform_field_key(type, field)
58
58
  "graphql.#{type.graphql_name}.#{field.graphql_name}"
59
59
  end
60
-
60
+
61
61
  def platform_authorized_key(type)
62
62
  "graphql.authorized.#{type.graphql_name}"
63
63
  end
@@ -112,6 +112,8 @@ module GraphQL
112
112
  graphql_query_string(data[key])
113
113
  when :multiplex
114
114
  graphql_multiplex(data[key])
115
+ when :path
116
+ [key, data[key].join(".")]
115
117
  else
116
118
  [key, data[key]]
117
119
  end
@@ -96,7 +96,9 @@ module GraphQL
96
96
 
97
97
  def self.use(schema_defn, options = {})
98
98
  tracer = self.new(**options)
99
- schema_defn.instrument(:field, tracer)
99
+ if !schema_defn.is_a?(Class)
100
+ schema_defn.instrument(:field, tracer)
101
+ end
100
102
  schema_defn.tracer(tracer)
101
103
  end
102
104
 
@@ -18,7 +18,7 @@ module GraphQL
18
18
  # This is not advised if you run more than one query per HTTP request, for example, with `graphql-client` or multiplexing.
19
19
  # It can also be specified per-query with `context[:set_skylight_endpoint_name]`.
20
20
  def initialize(options = {})
21
- warn("GraphQL::Tracing::SkylightTracing is deprecated, please enable Skylight's GraphQL probe instead: https://www.skylight.io/support/getting-more-from-skylight#graphql.")
21
+ warn("GraphQL::Tracing::SkylightTracing is deprecated and will be removed in GraphQL-Ruby 2.0, please enable Skylight's GraphQL probe instead: https://www.skylight.io/support/getting-more-from-skylight#graphql.")
22
22
  @set_endpoint_name = options.fetch(:set_endpoint_name, false)
23
23
  super
24
24
  end
@@ -42,8 +42,8 @@ module GraphQL
42
42
  # execute_multiplex | `{ multiplex: GraphQL::Execution::Multiplex }`
43
43
  # execute_query | `{ query: GraphQL::Query }`
44
44
  # execute_query_lazy | `{ query: GraphQL::Query?, multiplex: GraphQL::Execution::Multiplex? }`
45
- # execute_field | `{ context: GraphQL::Query::Context::FieldResolutionContext?, owner: Class?, field: GraphQL::Schema::Field?, query: GraphQL::Query?, path: Array<String, Integer>?}`
46
- # execute_field_lazy | `{ context: GraphQL::Query::Context::FieldResolutionContext?, owner: Class?, field: GraphQL::Schema::Field?, query: GraphQL::Query?, path: Array<String, Integer>?}`
45
+ # execute_field | `{ owner: Class, field: GraphQL::Schema::Field, query: GraphQL::Query, path: Array<String, Integer>, ast_node: GraphQL::Language::Nodes::Field}`
46
+ # execute_field_lazy | `{ owner: Class, field: GraphQL::Schema::Field, query: GraphQL::Query, path: Array<String, Integer>, ast_node: GraphQL::Language::Nodes::Field}`
47
47
  # authorized | `{ context: GraphQL::Query::Context, type: Class, object: Object, path: Array<String, Integer> }`
48
48
  # authorized_lazy | `{ context: GraphQL::Query::Context, type: Class, object: Object, path: Array<String, Integer> }`
49
49
  # resolve_type | `{ context: GraphQL::Query::Context, type: Class, object: Object, path: Array<String, Integer> }`
@@ -27,98 +27,8 @@ module GraphQL
27
27
  # end
28
28
  #
29
29
  # @see Relay::BaseEdge for edge types
30
- class BaseConnection < Types::Relay::BaseObject
31
- extend Forwardable
32
- def_delegators :@object, :cursor_from_node, :parent
33
-
34
- class << self
35
- # @return [Class]
36
- attr_reader :node_type
37
-
38
- # @return [Class]
39
- attr_reader :edge_class
40
-
41
- # Configure this connection to return `edges` and `nodes` based on `edge_type_class`.
42
- #
43
- # This method will use the inputs to create:
44
- # - `edges` field
45
- # - `nodes` field
46
- # - description
47
- #
48
- # It's called when you subclass this base connection, trying to use the
49
- # class name to set defaults. You can call it again in the class definition
50
- # to override the default (or provide a value, if the default lookup failed).
51
- def edge_type(edge_type_class, edge_class: GraphQL::Relay::Edge, node_type: edge_type_class.node_type, nodes_field: true, node_nullable: true)
52
- # Set this connection's graphql name
53
- node_type_name = node_type.graphql_name
54
-
55
- @node_type = node_type
56
- @edge_type = edge_type_class
57
- @edge_class = edge_class
58
-
59
- field :edges, [edge_type_class, null: true],
60
- null: true,
61
- description: "A list of edges.",
62
- edge_class: edge_class
63
-
64
- define_nodes_field(node_nullable) if nodes_field
65
-
66
- description("The connection type for #{node_type_name}.")
67
- end
68
-
69
- # Filter this list according to the way its node type would scope them
70
- def scope_items(items, context)
71
- node_type.scope_items(items, context)
72
- end
73
-
74
- # Add the shortcut `nodes` field to this connection and its subclasses
75
- def nodes_field
76
- define_nodes_field
77
- end
78
-
79
- def authorized?(obj, ctx)
80
- true # Let nodes be filtered out
81
- end
82
-
83
- def accessible?(ctx)
84
- node_type.accessible?(ctx)
85
- end
86
-
87
- def visible?(ctx)
88
- node_type.visible?(ctx)
89
- end
90
-
91
- private
92
-
93
- def define_nodes_field(nullable = true)
94
- type = nullable ? [@node_type, null: true] : [@node_type]
95
- field :nodes, type,
96
- null: nullable,
97
- description: "A list of nodes.",
98
- connection: false
99
- end
100
- end
101
-
102
- field :page_info, GraphQL::Types::Relay::PageInfo, null: false, description: "Information to aid in pagination."
103
-
104
- # By default this calls through to the ConnectionWrapper's edge nodes method,
105
- # but sometimes you need to override it to support the `nodes` field
106
- def nodes
107
- @object.edge_nodes
108
- end
109
-
110
- def edges
111
- if @object.is_a?(GraphQL::Pagination::Connection)
112
- @object.edges
113
- elsif context.interpreter?
114
- context.schema.after_lazy(object.edge_nodes) do |nodes|
115
- nodes.map { |n| self.class.edge_class.new(n, object) }
116
- end
117
- else
118
- # This is done by edges_instrumentation
119
- @object.edge_nodes
120
- end
121
- end
30
+ class BaseConnection < Schema::Object
31
+ include ConnectionBehaviors
122
32
  end
123
33
  end
124
34
  end