graphql 1.12.16 → 1.13.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (151) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/core.rb +3 -1
  3. data/lib/generators/graphql/install_generator.rb +9 -2
  4. data/lib/generators/graphql/mutation_generator.rb +1 -1
  5. data/lib/generators/graphql/object_generator.rb +2 -1
  6. data/lib/generators/graphql/relay.rb +19 -11
  7. data/lib/generators/graphql/templates/schema.erb +14 -2
  8. data/lib/generators/graphql/type_generator.rb +0 -1
  9. data/lib/graphql/analysis/ast/field_usage.rb +3 -3
  10. data/lib/graphql/analysis/ast/query_complexity.rb +10 -14
  11. data/lib/graphql/analysis/ast/visitor.rb +4 -4
  12. data/lib/graphql/backtrace/table.rb +1 -1
  13. data/lib/graphql/base_type.rb +4 -2
  14. data/lib/graphql/boolean_type.rb +1 -1
  15. data/lib/graphql/dataloader/source.rb +50 -2
  16. data/lib/graphql/dataloader.rb +93 -37
  17. data/lib/graphql/define/instance_definable.rb +1 -1
  18. data/lib/graphql/deprecated_dsl.rb +11 -3
  19. data/lib/graphql/deprecation.rb +1 -5
  20. data/lib/graphql/directive/deprecated_directive.rb +1 -1
  21. data/lib/graphql/directive/include_directive.rb +1 -1
  22. data/lib/graphql/directive/skip_directive.rb +1 -1
  23. data/lib/graphql/directive.rb +0 -4
  24. data/lib/graphql/enum_type.rb +5 -1
  25. data/lib/graphql/execution/errors.rb +1 -0
  26. data/lib/graphql/execution/interpreter/arguments.rb +1 -1
  27. data/lib/graphql/execution/interpreter/arguments_cache.rb +2 -2
  28. data/lib/graphql/execution/interpreter/runtime.rb +39 -23
  29. data/lib/graphql/execution/lookahead.rb +2 -2
  30. data/lib/graphql/execution/multiplex.rb +4 -1
  31. data/lib/graphql/float_type.rb +1 -1
  32. data/lib/graphql/id_type.rb +1 -1
  33. data/lib/graphql/int_type.rb +1 -1
  34. data/lib/graphql/integer_encoding_error.rb +18 -2
  35. data/lib/graphql/introspection/directive_type.rb +1 -1
  36. data/lib/graphql/introspection/entry_points.rb +2 -2
  37. data/lib/graphql/introspection/enum_value_type.rb +2 -2
  38. data/lib/graphql/introspection/field_type.rb +2 -2
  39. data/lib/graphql/introspection/input_value_type.rb +10 -4
  40. data/lib/graphql/introspection/schema_type.rb +2 -2
  41. data/lib/graphql/introspection/type_type.rb +10 -10
  42. data/lib/graphql/language/block_string.rb +2 -6
  43. data/lib/graphql/language/document_from_schema_definition.rb +4 -2
  44. data/lib/graphql/language/lexer.rb +0 -3
  45. data/lib/graphql/language/lexer.rl +0 -4
  46. data/lib/graphql/language/nodes.rb +12 -2
  47. data/lib/graphql/language/parser.rb +442 -434
  48. data/lib/graphql/language/parser.y +5 -4
  49. data/lib/graphql/language/printer.rb +6 -1
  50. data/lib/graphql/language/sanitized_printer.rb +5 -5
  51. data/lib/graphql/language/token.rb +0 -4
  52. data/lib/graphql/name_validator.rb +0 -4
  53. data/lib/graphql/pagination/connections.rb +35 -16
  54. data/lib/graphql/query/arguments.rb +1 -1
  55. data/lib/graphql/query/arguments_cache.rb +1 -1
  56. data/lib/graphql/query/context.rb +15 -2
  57. data/lib/graphql/query/literal_input.rb +1 -1
  58. data/lib/graphql/query/null_context.rb +12 -7
  59. data/lib/graphql/query/serial_execution/field_resolution.rb +1 -1
  60. data/lib/graphql/query/validation_pipeline.rb +1 -1
  61. data/lib/graphql/query/variables.rb +5 -1
  62. data/lib/graphql/query.rb +4 -0
  63. data/lib/graphql/relay/edges_instrumentation.rb +0 -1
  64. data/lib/graphql/relay/global_id_resolve.rb +1 -1
  65. data/lib/graphql/relay/page_info.rb +1 -1
  66. data/lib/graphql/rubocop/graphql/base_cop.rb +36 -0
  67. data/lib/graphql/rubocop/graphql/default_null_true.rb +43 -0
  68. data/lib/graphql/rubocop/graphql/default_required_true.rb +43 -0
  69. data/lib/graphql/rubocop.rb +4 -0
  70. data/lib/graphql/schema/addition.rb +37 -28
  71. data/lib/graphql/schema/argument.rb +79 -34
  72. data/lib/graphql/schema/build_from_definition.rb +5 -5
  73. data/lib/graphql/schema/directive/feature.rb +1 -1
  74. data/lib/graphql/schema/directive/flagged.rb +2 -2
  75. data/lib/graphql/schema/directive/include.rb +1 -1
  76. data/lib/graphql/schema/directive/skip.rb +1 -1
  77. data/lib/graphql/schema/directive/transform.rb +1 -1
  78. data/lib/graphql/schema/directive.rb +7 -3
  79. data/lib/graphql/schema/enum.rb +60 -10
  80. data/lib/graphql/schema/enum_value.rb +6 -0
  81. data/lib/graphql/schema/field/connection_extension.rb +1 -1
  82. data/lib/graphql/schema/field.rb +140 -42
  83. data/lib/graphql/schema/field_extension.rb +52 -2
  84. data/lib/graphql/schema/find_inherited_value.rb +1 -0
  85. data/lib/graphql/schema/finder.rb +5 -5
  86. data/lib/graphql/schema/input_object.rb +13 -14
  87. data/lib/graphql/schema/interface.rb +11 -20
  88. data/lib/graphql/schema/introspection_system.rb +1 -1
  89. data/lib/graphql/schema/list.rb +3 -1
  90. data/lib/graphql/schema/member/accepts_definition.rb +15 -3
  91. data/lib/graphql/schema/member/build_type.rb +0 -4
  92. data/lib/graphql/schema/member/cached_graphql_definition.rb +29 -2
  93. data/lib/graphql/schema/member/has_arguments.rb +145 -57
  94. data/lib/graphql/schema/member/has_deprecation_reason.rb +1 -1
  95. data/lib/graphql/schema/member/has_fields.rb +76 -18
  96. data/lib/graphql/schema/member/has_interfaces.rb +90 -0
  97. data/lib/graphql/schema/member.rb +1 -0
  98. data/lib/graphql/schema/non_null.rb +3 -1
  99. data/lib/graphql/schema/object.rb +10 -75
  100. data/lib/graphql/schema/printer.rb +1 -1
  101. data/lib/graphql/schema/relay_classic_mutation.rb +37 -3
  102. data/lib/graphql/schema/resolver/has_payload_type.rb +27 -2
  103. data/lib/graphql/schema/resolver.rb +49 -64
  104. data/lib/graphql/schema/scalar.rb +2 -0
  105. data/lib/graphql/schema/subscription.rb +17 -9
  106. data/lib/graphql/schema/traversal.rb +1 -1
  107. data/lib/graphql/schema/type_expression.rb +1 -1
  108. data/lib/graphql/schema/type_membership.rb +18 -4
  109. data/lib/graphql/schema/union.rb +8 -1
  110. data/lib/graphql/schema/validator/allow_blank_validator.rb +29 -0
  111. data/lib/graphql/schema/validator/allow_null_validator.rb +26 -0
  112. data/lib/graphql/schema/validator/exclusion_validator.rb +3 -1
  113. data/lib/graphql/schema/validator/format_validator.rb +4 -5
  114. data/lib/graphql/schema/validator/inclusion_validator.rb +3 -1
  115. data/lib/graphql/schema/validator/length_validator.rb +5 -3
  116. data/lib/graphql/schema/validator/numericality_validator.rb +13 -2
  117. data/lib/graphql/schema/validator.rb +33 -25
  118. data/lib/graphql/schema/warden.rb +116 -52
  119. data/lib/graphql/schema.rb +124 -27
  120. data/lib/graphql/static_validation/base_visitor.rb +8 -5
  121. data/lib/graphql/static_validation/definition_dependencies.rb +0 -1
  122. data/lib/graphql/static_validation/error.rb +3 -1
  123. data/lib/graphql/static_validation/literal_validator.rb +1 -1
  124. data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +1 -1
  125. data/lib/graphql/static_validation/rules/fields_will_merge.rb +52 -26
  126. data/lib/graphql/static_validation/rules/fields_will_merge_error.rb +25 -4
  127. data/lib/graphql/static_validation/rules/fragments_are_finite.rb +2 -2
  128. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +3 -1
  129. data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +4 -4
  130. data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +7 -7
  131. data/lib/graphql/static_validation/validation_context.rb +8 -2
  132. data/lib/graphql/static_validation/validator.rb +15 -12
  133. data/lib/graphql/string_encoding_error.rb +13 -3
  134. data/lib/graphql/string_type.rb +1 -1
  135. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +15 -5
  136. data/lib/graphql/subscriptions/event.rb +66 -13
  137. data/lib/graphql/subscriptions/serialize.rb +1 -1
  138. data/lib/graphql/subscriptions.rb +17 -19
  139. data/lib/graphql/tracing/appsignal_tracing.rb +15 -0
  140. data/lib/graphql/types/int.rb +1 -1
  141. data/lib/graphql/types/relay/connection_behaviors.rb +26 -9
  142. data/lib/graphql/types/relay/default_relay.rb +5 -1
  143. data/lib/graphql/types/relay/edge_behaviors.rb +13 -2
  144. data/lib/graphql/types/relay/has_node_field.rb +1 -1
  145. data/lib/graphql/types/relay/has_nodes_field.rb +1 -1
  146. data/lib/graphql/types/string.rb +1 -1
  147. data/lib/graphql/unauthorized_error.rb +1 -1
  148. data/lib/graphql/version.rb +1 -1
  149. data/lib/graphql.rb +10 -32
  150. data/readme.md +1 -1
  151. metadata +13 -6
@@ -92,6 +92,8 @@ module GraphQL
92
92
  end
93
93
  end
94
94
 
95
+ class DuplicateNamesError < GraphQL::Error; end
96
+
95
97
  class UnresolvedLateBoundTypeError < GraphQL::Error
96
98
  attr_reader :type
97
99
  def initialize(type:)
@@ -161,7 +163,7 @@ module GraphQL
161
163
 
162
164
  accepts_definitions \
163
165
  :query_execution_strategy, :mutation_execution_strategy, :subscription_execution_strategy,
164
- :validate_timeout, :max_depth, :max_complexity, :default_max_page_size,
166
+ :validate_timeout, :validate_max_errors, :max_depth, :max_complexity, :default_max_page_size,
165
167
  :orphan_types, :resolve_type, :type_error, :parse_error,
166
168
  :error_bubbling,
167
169
  :raise_definition_error,
@@ -200,7 +202,7 @@ module GraphQL
200
202
  attr_accessor \
201
203
  :query, :mutation, :subscription,
202
204
  :query_execution_strategy, :mutation_execution_strategy, :subscription_execution_strategy,
203
- :validate_timeout, :max_depth, :max_complexity, :default_max_page_size,
205
+ :validate_timeout, :validate_max_errors, :max_depth, :max_complexity, :default_max_page_size,
204
206
  :orphan_types, :directives,
205
207
  :query_analyzers, :multiplex_analyzers, :instrumenters, :lazy_methods,
206
208
  :cursor_encoder,
@@ -552,7 +554,7 @@ module GraphQL
552
554
  end
553
555
  end
554
556
 
555
- # @see [GraphQL::Schema::Warden] Resticted access to root types
557
+ # @see [GraphQL::Schema::Warden] Restricted access to root types
556
558
  # @return [GraphQL::ObjectType, nil]
557
559
  def root_type_for_operation(operation)
558
560
  case operation
@@ -843,7 +845,7 @@ module GraphQL
843
845
  # - Cause the Schema instance to be created, if it hasn't been created yet
844
846
  # - Delegate to that instance
845
847
  # Eventually, the methods will be moved into this class, removing the need for the singleton.
846
- def_delegators :graphql_definition,
848
+ def_delegators :deprecated_graphql_definition,
847
849
  # Execution
848
850
  :execution_strategy_for_operation,
849
851
  # Configuration
@@ -852,6 +854,10 @@ module GraphQL
852
854
  :id_from_object=, :object_from_id=,
853
855
  :remove_handler
854
856
 
857
+ def deprecated_graphql_definition
858
+ graphql_definition(silence_deprecation_warning: true)
859
+ end
860
+
855
861
  # @return [GraphQL::Subscriptions]
856
862
  attr_accessor :subscriptions
857
863
 
@@ -894,8 +900,15 @@ module GraphQL
894
900
  @find_cache[path] ||= @finder.find(path)
895
901
  end
896
902
 
897
- def graphql_definition
898
- @graphql_definition ||= to_graphql
903
+ def graphql_definition(silence_deprecation_warning: false)
904
+ @graphql_definition ||= begin
905
+ unless silence_deprecation_warning
906
+ message = "Legacy `.graphql_definition` objects are deprecated and will be removed in GraphQL-Ruby 2.0. Use a class-based definition instead."
907
+ caller_message = "\n\nCalled on #{self.inspect} from:\n #{caller(1, 25).map { |l| " #{l}" }.join("\n")}"
908
+ GraphQL::Deprecation.warn(message + caller_message)
909
+ end
910
+ to_graphql(silence_deprecation_warning: silence_deprecation_warning)
911
+ end
899
912
  end
900
913
 
901
914
  def default_filter
@@ -927,18 +940,20 @@ module GraphQL
927
940
  find_inherited_value(:plugins, EMPTY_ARRAY) + own_plugins
928
941
  end
929
942
 
943
+ prepend Schema::Member::CachedGraphQLDefinition::DeprecatedToGraphQL
930
944
  def to_graphql
931
945
  schema_defn = self.new
932
946
  schema_defn.raise_definition_error = true
933
- schema_defn.query = query && query.graphql_definition
934
- schema_defn.mutation = mutation && mutation.graphql_definition
935
- schema_defn.subscription = subscription && subscription.graphql_definition
947
+ schema_defn.query = query && query.graphql_definition(silence_deprecation_warning: true)
948
+ schema_defn.mutation = mutation && mutation.graphql_definition(silence_deprecation_warning: true)
949
+ schema_defn.subscription = subscription && subscription.graphql_definition(silence_deprecation_warning: true)
936
950
  schema_defn.validate_timeout = validate_timeout
951
+ schema_defn.validate_max_errors = validate_max_errors
937
952
  schema_defn.max_complexity = max_complexity
938
953
  schema_defn.error_bubbling = error_bubbling
939
954
  schema_defn.max_depth = max_depth
940
955
  schema_defn.default_max_page_size = default_max_page_size
941
- schema_defn.orphan_types = orphan_types.map(&:graphql_definition)
956
+ schema_defn.orphan_types = orphan_types.map { |t| t.graphql_definition(silence_deprecation_warning: true) }
942
957
  schema_defn.disable_introspection_entry_points = disable_introspection_entry_points?
943
958
  schema_defn.disable_schema_introspection_entry_point = disable_schema_introspection_entry_point?
944
959
  schema_defn.disable_type_introspection_entry_point = disable_type_introspection_entry_point?
@@ -995,16 +1010,58 @@ module GraphQL
995
1010
  # Build a map of `{ name => type }` and return it
996
1011
  # @return [Hash<String => Class>] A dictionary of type classes by their GraphQL name
997
1012
  # @see get_type Which is more efficient for finding _one type_ by name, because it doesn't merge hashes.
998
- def types
999
- non_introspection_types.merge(introspection_system.types)
1013
+ def types(context = GraphQL::Query::NullContext)
1014
+ all_types = non_introspection_types.merge(introspection_system.types)
1015
+ visible_types = {}
1016
+ all_types.each do |k, v|
1017
+ visible_types[k] =if v.is_a?(Array)
1018
+ visible_t = nil
1019
+ v.each do |t|
1020
+ if t.visible?(context)
1021
+ if visible_t.nil?
1022
+ visible_t = t
1023
+ else
1024
+ raise DuplicateNamesError, "Found two visible type definitions for `#{k}`: #{visible_t.inspect}, #{t.inspect}"
1025
+ end
1026
+ end
1027
+ end
1028
+ visible_t
1029
+ else
1030
+ v
1031
+ end
1032
+ end
1033
+ visible_types
1000
1034
  end
1001
1035
 
1002
1036
  # @param type_name [String]
1003
1037
  # @return [Module, nil] A type, or nil if there's no type called `type_name`
1004
- def get_type(type_name)
1005
- own_types[type_name] ||
1006
- introspection_system.types[type_name] ||
1007
- find_inherited_value(:types, EMPTY_HASH)[type_name]
1038
+ def get_type(type_name, context = GraphQL::Query::NullContext)
1039
+ local_entry = own_types[type_name]
1040
+ type_defn = case local_entry
1041
+ when nil
1042
+ nil
1043
+ when Array
1044
+ visible_t = nil
1045
+ warden = Warden.from_context(context)
1046
+ local_entry.each do |t|
1047
+ if warden.visible_type?(t, context)
1048
+ if visible_t.nil?
1049
+ visible_t = t
1050
+ else
1051
+ raise DuplicateNamesError, "Found two visible type definitions for `#{type_name}`: #{visible_t.inspect}, #{t.inspect}"
1052
+ end
1053
+ end
1054
+ end
1055
+ visible_t
1056
+ when Module
1057
+ local_entry
1058
+ else
1059
+ raise "Invariant: unexpected own_types[#{type_name.inspect}]: #{local_entry.inspect}"
1060
+ end
1061
+
1062
+ type_defn ||
1063
+ introspection_system.types[type_name] || # todo context-specific introspection?
1064
+ (superclass.respond_to?(:get_type) ? superclass.get_type(type_name, context) : nil)
1008
1065
  end
1009
1066
 
1010
1067
  # @api private
@@ -1073,7 +1130,7 @@ module GraphQL
1073
1130
  end
1074
1131
  end
1075
1132
 
1076
- # @see [GraphQL::Schema::Warden] Resticted access to root types
1133
+ # @see [GraphQL::Schema::Warden] Restricted access to root types
1077
1134
  # @return [GraphQL::ObjectType, nil]
1078
1135
  def root_type_for_operation(operation)
1079
1136
  case operation
@@ -1181,19 +1238,19 @@ module GraphQL
1181
1238
  GraphQL::Schema::TypeExpression.build_type(type_owner, ast_node)
1182
1239
  end
1183
1240
 
1184
- def get_field(type_or_name, field_name)
1241
+ def get_field(type_or_name, field_name, context = GraphQL::Query::NullContext)
1185
1242
  parent_type = case type_or_name
1186
1243
  when LateBoundType
1187
- get_type(type_or_name.name)
1244
+ get_type(type_or_name.name, context)
1188
1245
  when String
1189
- get_type(type_or_name)
1246
+ get_type(type_or_name, context)
1190
1247
  when Module
1191
1248
  type_or_name
1192
1249
  else
1193
1250
  raise ArgumentError, "unexpected field owner for #{field_name.inspect}: #{type_or_name.inspect} (#{type_or_name.class})"
1194
1251
  end
1195
1252
 
1196
- if parent_type.kind.fields? && (field = parent_type.get_field(field_name))
1253
+ if parent_type.kind.fields? && (field = parent_type.get_field(field_name, context))
1197
1254
  field
1198
1255
  elsif parent_type == query && (entry_point_field = introspection_system.entry_point(name: field_name))
1199
1256
  entry_point_field
@@ -1204,8 +1261,8 @@ module GraphQL
1204
1261
  end
1205
1262
  end
1206
1263
 
1207
- def get_fields(type)
1208
- type.fields
1264
+ def get_fields(type, context = GraphQL::Query::NullContext)
1265
+ type.fields(context)
1209
1266
  end
1210
1267
 
1211
1268
  def introspection(new_introspection_namespace = nil)
@@ -1290,10 +1347,22 @@ module GraphQL
1290
1347
  validator_opts = { schema: self }
1291
1348
  rules && (validator_opts[:rules] = rules)
1292
1349
  validator = GraphQL::StaticValidation::Validator.new(**validator_opts)
1293
- res = validator.validate(query, timeout: validate_timeout)
1350
+ res = validator.validate(query, timeout: validate_timeout, max_errors: validate_max_errors)
1294
1351
  res[:errors]
1295
1352
  end
1296
1353
 
1354
+ attr_writer :validate_max_errors
1355
+
1356
+ def validate_max_errors(new_validate_max_errors = nil)
1357
+ if new_validate_max_errors
1358
+ @validate_max_errors = new_validate_max_errors
1359
+ elsif defined?(@validate_max_errors)
1360
+ @validate_max_errors
1361
+ else
1362
+ find_inherited_value(:validate_max_errors)
1363
+ end
1364
+ end
1365
+
1297
1366
  attr_writer :max_complexity
1298
1367
 
1299
1368
  def max_complexity(max_complexity = nil)
@@ -1392,7 +1461,6 @@ module GraphQL
1392
1461
  if new_orphan_types.any?
1393
1462
  new_orphan_types = new_orphan_types.flatten
1394
1463
  add_type_and_traverse(new_orphan_types, root: false)
1395
- @orphan_types = new_orphan_types
1396
1464
  own_orphan_types.concat(new_orphan_types.flatten)
1397
1465
  end
1398
1466
 
@@ -1702,7 +1770,7 @@ module GraphQL
1702
1770
  if subscription.singleton_class.ancestors.include?(Subscriptions::SubscriptionRoot)
1703
1771
  GraphQL::Deprecation.warn("`extend Subscriptions::SubscriptionRoot` is no longer required; you may remove it from #{self}'s `subscription` root type (#{subscription}).")
1704
1772
  else
1705
- subscription.fields.each do |name, field|
1773
+ subscription.all_field_definitions.each do |field|
1706
1774
  field.extension(Subscriptions::DefaultSubscriptionResolveExtension)
1707
1775
  end
1708
1776
  end
@@ -1724,7 +1792,36 @@ module GraphQL
1724
1792
  end
1725
1793
  new_types = Array(t)
1726
1794
  addition = Schema::Addition.new(schema: self, own_types: own_types, new_types: new_types)
1727
- own_types.merge!(addition.types)
1795
+ addition.types.each do |name, types_entry| # rubocop:disable Development/ContextIsPassedCop -- build-time, not query-time
1796
+ if (prev_entry = own_types[name])
1797
+ prev_entries = case prev_entry
1798
+ when Array
1799
+ prev_entry
1800
+ when Module
1801
+ own_types[name] = [prev_entry]
1802
+ else
1803
+ raise "Invariant: unexpected prev_entry at #{name.inspect} when adding #{t.inspect}"
1804
+ end
1805
+
1806
+ case types_entry
1807
+ when Array
1808
+ prev_entries.concat(types_entry)
1809
+ prev_entries.uniq! # in case any are being re-visited
1810
+ when Module
1811
+ if !prev_entries.include?(types_entry)
1812
+ prev_entries << types_entry
1813
+ end
1814
+ else
1815
+ raise "Invariant: unexpected types_entry at #{name} when adding #{t.inspect}"
1816
+ end
1817
+ else
1818
+ if types_entry.is_a?(Array)
1819
+ types_entry.uniq!
1820
+ end
1821
+ own_types[name] = types_entry
1822
+ end
1823
+ end
1824
+
1728
1825
  own_possible_types.merge!(addition.possible_types) { |key, old_val, new_val| old_val + new_val }
1729
1826
  own_union_memberships.merge!(addition.union_memberships)
1730
1827
 
@@ -94,7 +94,7 @@ module GraphQL
94
94
 
95
95
  def on_field(node, parent)
96
96
  parent_type = @object_types.last
97
- field_definition = @schema.get_field(parent_type, node.name)
97
+ field_definition = @schema.get_field(parent_type, node.name, @context.query.context)
98
98
  @field_definitions.push(field_definition)
99
99
  if !field_definition.nil?
100
100
  next_object_type = field_definition.type.unwrap
@@ -120,14 +120,14 @@ module GraphQL
120
120
  argument_defn = if (arg = @argument_definitions.last)
121
121
  arg_type = arg.type.unwrap
122
122
  if arg_type.kind.input_object?
123
- arg_type.arguments[node.name]
123
+ @context.warden.get_argument(arg_type, node.name)
124
124
  else
125
125
  nil
126
126
  end
127
127
  elsif (directive_defn = @directive_definitions.last)
128
- directive_defn.arguments[node.name]
128
+ @context.warden.get_argument(directive_defn, node.name)
129
129
  elsif (field_defn = @field_definitions.last)
130
- field_defn.arguments[node.name]
130
+ @context.warden.get_argument(field_defn, node.name)
131
131
  else
132
132
  nil
133
133
  end
@@ -187,7 +187,7 @@ module GraphQL
187
187
 
188
188
  def on_fragment_with_type(node)
189
189
  object_type = if node.type
190
- @schema.get_type(node.type.name)
190
+ @context.warden.get_type(node.type.name)
191
191
  else
192
192
  @object_types.last
193
193
  end
@@ -205,6 +205,9 @@ 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
208
211
  error.path ||= (path || @path.dup)
209
212
  context.errors << error
210
213
  end
@@ -70,7 +70,6 @@ module GraphQL
70
70
  @dependency_map ||= resolve_dependencies(&block)
71
71
  end
72
72
 
73
-
74
73
  # Map definition AST nodes to the definition AST nodes they depend on.
75
74
  # Expose circular dependencies.
76
75
  class DependencyMap
@@ -32,8 +32,10 @@ module GraphQL
32
32
 
33
33
  private
34
34
 
35
+ attr_reader :nodes
36
+
35
37
  def locations
36
- @nodes.map do |node|
38
+ nodes.map do |node|
37
39
  h = {"line" => node.line, "column" => node.col}
38
40
  h["filename"] = node.filename if node.filename
39
41
  h
@@ -95,7 +95,7 @@ module GraphQL
95
95
  def required_input_fields_are_present(type, ast_node)
96
96
  # TODO - would be nice to use these to create an error message so the caller knows
97
97
  # that required fields are missing
98
- required_field_names = type.arguments.each_value
98
+ required_field_names = @warden.arguments(type)
99
99
  .select { |argument| argument.type.kind.non_null? && @warden.get_argument(type, argument.name) }
100
100
  .map(&:name)
101
101
 
@@ -15,7 +15,7 @@ module GraphQL
15
15
  if @context.schema.error_bubbling || context.errors.none? { |err| err.path.take(@path.size) == @path }
16
16
  parent_defn = parent_definition(parent)
17
17
 
18
- if parent_defn && (arg_defn = parent_defn.arguments[node.name])
18
+ if parent_defn && (arg_defn = context.warden.get_argument(parent_defn, node.name))
19
19
  validation_result = context.validate_literal(node.value, arg_defn.type)
20
20
  if !validation_result.valid?
21
21
  kind_of_node = node_type(parent)
@@ -10,6 +10,7 @@ module GraphQL
10
10
  #
11
11
  # Original Algorithm: https://github.com/graphql/graphql-js/blob/master/src/validation/rules/OverlappingFieldsCanBeMerged.js
12
12
  NO_ARGS = {}.freeze
13
+
13
14
  Field = Struct.new(:node, :definition, :owner_type, :parents)
14
15
  FragmentSpread = Struct.new(:name, :parents)
15
16
 
@@ -17,24 +18,47 @@ module GraphQL
17
18
  super
18
19
  @visited_fragments = {}
19
20
  @compared_fragments = {}
21
+ @conflict_count = 0
20
22
  end
21
23
 
22
24
  def on_operation_definition(node, _parent)
23
- conflicts_within_selection_set(node, type_definition)
25
+ setting_errors { conflicts_within_selection_set(node, type_definition) }
24
26
  super
25
27
  end
26
28
 
27
29
  def on_field(node, _parent)
28
- conflicts_within_selection_set(node, type_definition)
30
+ setting_errors { conflicts_within_selection_set(node, type_definition) }
29
31
  super
30
32
  end
31
33
 
32
34
  private
33
35
 
36
+ def field_conflicts
37
+ @field_conflicts ||= Hash.new do |errors, field|
38
+ errors[field] = GraphQL::StaticValidation::FieldsWillMergeError.new(kind: :field, field_name: field)
39
+ end
40
+ end
41
+
42
+ def arg_conflicts
43
+ @arg_conflicts ||= Hash.new do |errors, field|
44
+ errors[field] = GraphQL::StaticValidation::FieldsWillMergeError.new(kind: :argument, field_name: field)
45
+ end
46
+ end
47
+
48
+ def setting_errors
49
+ @field_conflicts = nil
50
+ @arg_conflicts = nil
51
+
52
+ yield
53
+ # don't initialize these if they weren't initialized in the block:
54
+ @field_conflicts && @field_conflicts.each_value { |error| add_error(error) }
55
+ @arg_conflicts && @arg_conflicts.each_value { |error| add_error(error) }
56
+ end
57
+
34
58
  def conflicts_within_selection_set(node, parent_type)
35
59
  return if parent_type.nil?
36
60
 
37
- fields, fragment_spreads = fields_and_fragments_from_selection(node, owner_type: parent_type, parents: [])
61
+ fields, fragment_spreads = fields_and_fragments_from_selection(node, owner_type: parent_type, parents: nil)
38
62
 
39
63
  # (A) Find find all conflicts "within" the fields of this selection set.
40
64
  find_conflicts_within(fields)
@@ -174,15 +198,21 @@ module GraphQL
174
198
  response_keys.each do |key, fields|
175
199
  next if fields.size < 2
176
200
  # find conflicts within nodes
177
- for i in 0..fields.size - 1
178
- for j in i + 1..fields.size - 1
201
+ i = 0
202
+ while i < fields.size
203
+ j = i + 1
204
+ while j < fields.size
179
205
  find_conflict(key, fields[i], fields[j])
206
+ j += 1
180
207
  end
208
+ i += 1
181
209
  end
182
210
  end
183
211
  end
184
212
 
185
213
  def find_conflict(response_key, field1, field2, mutually_exclusive: false)
214
+ return if @conflict_count >= context.max_errors
215
+
186
216
  node1 = field1.node
187
217
  node2 = field2.node
188
218
 
@@ -191,28 +221,21 @@ module GraphQL
191
221
 
192
222
  if !are_mutually_exclusive
193
223
  if node1.name != node2.name
194
- errored_nodes = [node1.name, node2.name].sort.join(" or ")
195
- msg = "Field '#{response_key}' has a field conflict: #{errored_nodes}?"
196
- context.errors << GraphQL::StaticValidation::FieldsWillMergeError.new(
197
- msg,
198
- nodes: [node1, node2],
199
- path: [],
200
- field_name: response_key,
201
- conflicts: errored_nodes
202
- )
224
+ conflict = field_conflicts[response_key]
225
+
226
+ conflict.add_conflict(node1, node1.name)
227
+ conflict.add_conflict(node2, node2.name)
228
+
229
+ @conflict_count += 1
203
230
  end
204
231
 
205
232
  if !same_arguments?(node1, node2)
206
- args = [serialize_field_args(node1), serialize_field_args(node2)]
207
- conflicts = args.map { |arg| GraphQL::Language.serialize(arg) }.join(" or ")
208
- msg = "Field '#{response_key}' has an argument conflict: #{conflicts}?"
209
- context.errors << GraphQL::StaticValidation::FieldsWillMergeError.new(
210
- msg,
211
- nodes: [node1, node2],
212
- path: [],
213
- field_name: response_key,
214
- conflicts: conflicts
215
- )
233
+ conflict = arg_conflicts[response_key]
234
+
235
+ conflict.add_conflict(node1, GraphQL::Language.serialize(serialize_field_args(node1)))
236
+ conflict.add_conflict(node2, GraphQL::Language.serialize(serialize_field_args(node2)))
237
+
238
+ @conflict_count += 1
216
239
  end
217
240
  end
218
241
 
@@ -224,7 +247,9 @@ module GraphQL
224
247
  end
225
248
 
226
249
  def find_conflicts_between_sub_selection_sets(field1, field2, mutually_exclusive:)
227
- return if field1.definition.nil? || field2.definition.nil?
250
+ return if field1.definition.nil? ||
251
+ field2.definition.nil? ||
252
+ (field1.node.selections.empty? && field2.node.selections.empty?)
228
253
 
229
254
  return_type1 = field1.definition.type.unwrap
230
255
  return_type2 = field2.definition.type.unwrap
@@ -304,6 +329,7 @@ module GraphQL
304
329
  if node.selections.empty?
305
330
  NO_SELECTIONS
306
331
  else
332
+ parents ||= []
307
333
  fields, fragment_spreads = find_fields_and_fragments(node.selections, owner_type: owner_type, parents: parents, fields: [], fragment_spreads: [])
308
334
  response_keys = fields.group_by { |f| f.node.alias || f.node.name }
309
335
  [response_keys, fragment_spreads]
@@ -314,7 +340,7 @@ module GraphQL
314
340
  selections.each do |node|
315
341
  case node
316
342
  when GraphQL::Language::Nodes::Field
317
- definition = context.schema.get_field(owner_type, node.name)
343
+ definition = context.query.get_field(owner_type, node.name)
318
344
  fields << Field.new(node, definition, owner_type, parents)
319
345
  when GraphQL::Language::Nodes::InlineFragment
320
346
  fragment_type = node.type ? context.warden.get_type(node.type.name) : owner_type
@@ -3,12 +3,33 @@ module GraphQL
3
3
  module StaticValidation
4
4
  class FieldsWillMergeError < StaticValidation::Error
5
5
  attr_reader :field_name
6
- attr_reader :conflicts
6
+ attr_reader :kind
7
+
8
+ def initialize(kind:, field_name:)
9
+ super(nil)
7
10
 
8
- def initialize(message, path: nil, nodes: [], field_name:, conflicts:)
9
- super(message, path: path, nodes: nodes)
10
11
  @field_name = field_name
11
- @conflicts = conflicts
12
+ @kind = kind
13
+ @conflicts = []
14
+ end
15
+
16
+ def message
17
+ "Field '#{field_name}' has #{kind == :argument ? 'an' : 'a'} #{kind} conflict: #{conflicts}?"
18
+ end
19
+
20
+ def path
21
+ []
22
+ end
23
+
24
+ def conflicts
25
+ @conflicts.join(' or ')
26
+ end
27
+
28
+ def add_conflict(node, conflict_str)
29
+ return if nodes.include?(node)
30
+
31
+ @nodes << node
32
+ @conflicts << conflict_str
12
33
  end
13
34
 
14
35
  # A hash representation of this Message
@@ -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
- context.errors << GraphQL::StaticValidation::FragmentsAreFiniteError.new(
10
+ add_error(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
@@ -16,8 +16,10 @@ module GraphQL
16
16
  private
17
17
 
18
18
  def assert_required_args(ast_node, defn)
19
+ args = defn.arguments(context.query.context)
20
+ return if args.empty?
19
21
  present_argument_names = ast_node.arguments.map(&:name)
20
- required_argument_names = defn.arguments.each_value
22
+ required_argument_names = context.warden.arguments(defn)
21
23
  .select { |a| a.type.kind.non_null? && !a.default_value? && context.warden.get_argument(defn, a.name) }
22
24
  .map(&:name)
23
25
 
@@ -34,16 +34,16 @@ module GraphQL
34
34
  parent_type = get_parent_type(context, parent)
35
35
  return unless parent_type && parent_type.kind.input_object?
36
36
 
37
- required_fields = parent_type.arguments
38
- .select{|k,v| v.type.kind.non_null?}
39
- .keys
37
+ required_fields = context.warden.arguments(parent_type)
38
+ .select{|arg| arg.type.kind.non_null?}
39
+ .map(&:graphql_name)
40
40
 
41
41
  present_fields = ast_node.arguments.map(&:name)
42
42
  missing_fields = required_fields - present_fields
43
43
 
44
44
  missing_fields.each do |missing_field|
45
45
  path = [*context.path, missing_field]
46
- missing_field_type = parent_type.arguments[missing_field].type
46
+ missing_field_type = context.warden.get_argument(parent_type, missing_field).type
47
47
  add_error(RequiredInputObjectAttributesArePresentError.new(
48
48
  "Argument '#{missing_field}' on InputObject '#{parent_type.to_type_signature}' is required. Expected type #{missing_field_type.to_type_signature}",
49
49
  argument_name: missing_field,
@@ -22,15 +22,15 @@ module GraphQL
22
22
  node_values = node_values.select { |value| value.is_a? GraphQL::Language::Nodes::VariableIdentifier }
23
23
 
24
24
  if node_values.any?
25
- arguments = case parent
25
+ argument_owner = case parent
26
26
  when GraphQL::Language::Nodes::Field
27
- context.field_definition.arguments
27
+ context.field_definition
28
28
  when GraphQL::Language::Nodes::Directive
29
- context.directive_definition.arguments
29
+ context.directive_definition
30
30
  when GraphQL::Language::Nodes::InputObject
31
31
  arg_type = context.argument_definition.type.unwrap
32
32
  if arg_type.kind.input_object?
33
- arguments = arg_type.arguments
33
+ arg_type
34
34
  else
35
35
  # This is some kind of error
36
36
  nil
@@ -43,7 +43,7 @@ module GraphQL
43
43
  var_defn_ast = @declared_variables[node_value.name]
44
44
  # Might be undefined :(
45
45
  # VariablesAreUsedAndDefined can't finalize its search until the end of the document.
46
- var_defn_ast && arguments && validate_usage(arguments, node, var_defn_ast)
46
+ var_defn_ast && argument_owner && validate_usage(argument_owner, node, var_defn_ast)
47
47
  end
48
48
  end
49
49
  super
@@ -51,7 +51,7 @@ module GraphQL
51
51
 
52
52
  private
53
53
 
54
- def validate_usage(arguments, arg_node, ast_var)
54
+ def validate_usage(argument_owner, arg_node, ast_var)
55
55
  var_type = context.schema.type_from_ast(ast_var.type, context: context)
56
56
  if var_type.nil?
57
57
  return
@@ -65,7 +65,7 @@ module GraphQL
65
65
  end
66
66
  end
67
67
 
68
- arg_defn = arguments[arg_node.name]
68
+ arg_defn = context.warden.get_argument(argument_owner, arg_node.name)
69
69
  arg_defn_type = arg_defn.type
70
70
 
71
71
  var_inner_type = var_type.unwrap