graphql 1.12.16 → 1.13.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/generators/graphql/core.rb +3 -1
- data/lib/generators/graphql/install_generator.rb +9 -2
- data/lib/generators/graphql/mutation_generator.rb +1 -1
- data/lib/generators/graphql/object_generator.rb +2 -1
- data/lib/generators/graphql/relay.rb +19 -11
- data/lib/generators/graphql/templates/schema.erb +14 -2
- data/lib/generators/graphql/type_generator.rb +0 -1
- data/lib/graphql/analysis/ast/field_usage.rb +3 -3
- data/lib/graphql/analysis/ast/query_complexity.rb +10 -14
- data/lib/graphql/analysis/ast/visitor.rb +4 -4
- data/lib/graphql/backtrace/table.rb +1 -1
- data/lib/graphql/base_type.rb +4 -2
- data/lib/graphql/boolean_type.rb +1 -1
- data/lib/graphql/dataloader/source.rb +50 -2
- data/lib/graphql/dataloader.rb +93 -37
- data/lib/graphql/define/instance_definable.rb +1 -1
- data/lib/graphql/deprecated_dsl.rb +11 -3
- data/lib/graphql/deprecation.rb +1 -5
- data/lib/graphql/directive/deprecated_directive.rb +1 -1
- data/lib/graphql/directive/include_directive.rb +1 -1
- data/lib/graphql/directive/skip_directive.rb +1 -1
- data/lib/graphql/directive.rb +0 -4
- data/lib/graphql/enum_type.rb +5 -1
- data/lib/graphql/execution/errors.rb +1 -0
- data/lib/graphql/execution/interpreter/arguments.rb +1 -1
- data/lib/graphql/execution/interpreter/arguments_cache.rb +2 -2
- data/lib/graphql/execution/interpreter/runtime.rb +39 -23
- data/lib/graphql/execution/lookahead.rb +2 -2
- data/lib/graphql/execution/multiplex.rb +4 -1
- data/lib/graphql/float_type.rb +1 -1
- data/lib/graphql/id_type.rb +1 -1
- data/lib/graphql/int_type.rb +1 -1
- data/lib/graphql/integer_encoding_error.rb +18 -2
- data/lib/graphql/introspection/directive_type.rb +1 -1
- data/lib/graphql/introspection/entry_points.rb +2 -2
- data/lib/graphql/introspection/enum_value_type.rb +2 -2
- data/lib/graphql/introspection/field_type.rb +2 -2
- data/lib/graphql/introspection/input_value_type.rb +10 -4
- data/lib/graphql/introspection/schema_type.rb +2 -2
- data/lib/graphql/introspection/type_type.rb +10 -10
- data/lib/graphql/language/block_string.rb +2 -6
- data/lib/graphql/language/document_from_schema_definition.rb +4 -2
- data/lib/graphql/language/lexer.rb +0 -3
- data/lib/graphql/language/lexer.rl +0 -4
- data/lib/graphql/language/nodes.rb +12 -2
- data/lib/graphql/language/parser.rb +442 -434
- data/lib/graphql/language/parser.y +5 -4
- data/lib/graphql/language/printer.rb +6 -1
- data/lib/graphql/language/sanitized_printer.rb +5 -5
- data/lib/graphql/language/token.rb +0 -4
- data/lib/graphql/name_validator.rb +0 -4
- data/lib/graphql/pagination/connections.rb +35 -16
- data/lib/graphql/query/arguments.rb +1 -1
- data/lib/graphql/query/arguments_cache.rb +1 -1
- data/lib/graphql/query/context.rb +15 -2
- data/lib/graphql/query/literal_input.rb +1 -1
- data/lib/graphql/query/null_context.rb +12 -7
- data/lib/graphql/query/serial_execution/field_resolution.rb +1 -1
- data/lib/graphql/query/validation_pipeline.rb +1 -1
- data/lib/graphql/query/variables.rb +5 -1
- data/lib/graphql/query.rb +4 -0
- data/lib/graphql/relay/edges_instrumentation.rb +0 -1
- data/lib/graphql/relay/global_id_resolve.rb +1 -1
- data/lib/graphql/relay/page_info.rb +1 -1
- data/lib/graphql/rubocop/graphql/base_cop.rb +36 -0
- data/lib/graphql/rubocop/graphql/default_null_true.rb +43 -0
- data/lib/graphql/rubocop/graphql/default_required_true.rb +43 -0
- data/lib/graphql/rubocop.rb +4 -0
- data/lib/graphql/schema/addition.rb +37 -28
- data/lib/graphql/schema/argument.rb +79 -34
- data/lib/graphql/schema/build_from_definition.rb +5 -5
- data/lib/graphql/schema/directive/feature.rb +1 -1
- data/lib/graphql/schema/directive/flagged.rb +2 -2
- data/lib/graphql/schema/directive/include.rb +1 -1
- data/lib/graphql/schema/directive/skip.rb +1 -1
- data/lib/graphql/schema/directive/transform.rb +1 -1
- data/lib/graphql/schema/directive.rb +7 -3
- data/lib/graphql/schema/enum.rb +60 -10
- data/lib/graphql/schema/enum_value.rb +6 -0
- data/lib/graphql/schema/field/connection_extension.rb +1 -1
- data/lib/graphql/schema/field.rb +140 -42
- data/lib/graphql/schema/field_extension.rb +52 -2
- data/lib/graphql/schema/find_inherited_value.rb +1 -0
- data/lib/graphql/schema/finder.rb +5 -5
- data/lib/graphql/schema/input_object.rb +13 -14
- data/lib/graphql/schema/interface.rb +11 -20
- data/lib/graphql/schema/introspection_system.rb +1 -1
- data/lib/graphql/schema/list.rb +3 -1
- data/lib/graphql/schema/member/accepts_definition.rb +15 -3
- data/lib/graphql/schema/member/build_type.rb +0 -4
- data/lib/graphql/schema/member/cached_graphql_definition.rb +29 -2
- data/lib/graphql/schema/member/has_arguments.rb +145 -57
- data/lib/graphql/schema/member/has_deprecation_reason.rb +1 -1
- data/lib/graphql/schema/member/has_fields.rb +76 -18
- data/lib/graphql/schema/member/has_interfaces.rb +90 -0
- data/lib/graphql/schema/member.rb +1 -0
- data/lib/graphql/schema/non_null.rb +3 -1
- data/lib/graphql/schema/object.rb +10 -75
- data/lib/graphql/schema/printer.rb +1 -1
- data/lib/graphql/schema/relay_classic_mutation.rb +37 -3
- data/lib/graphql/schema/resolver/has_payload_type.rb +27 -2
- data/lib/graphql/schema/resolver.rb +49 -64
- data/lib/graphql/schema/scalar.rb +2 -0
- data/lib/graphql/schema/subscription.rb +17 -9
- data/lib/graphql/schema/traversal.rb +1 -1
- data/lib/graphql/schema/type_expression.rb +1 -1
- data/lib/graphql/schema/type_membership.rb +18 -4
- data/lib/graphql/schema/union.rb +8 -1
- data/lib/graphql/schema/validator/allow_blank_validator.rb +29 -0
- data/lib/graphql/schema/validator/allow_null_validator.rb +26 -0
- data/lib/graphql/schema/validator/exclusion_validator.rb +3 -1
- data/lib/graphql/schema/validator/format_validator.rb +4 -5
- data/lib/graphql/schema/validator/inclusion_validator.rb +3 -1
- data/lib/graphql/schema/validator/length_validator.rb +5 -3
- data/lib/graphql/schema/validator/numericality_validator.rb +13 -2
- data/lib/graphql/schema/validator.rb +33 -25
- data/lib/graphql/schema/warden.rb +116 -52
- data/lib/graphql/schema.rb +124 -27
- data/lib/graphql/static_validation/base_visitor.rb +8 -5
- data/lib/graphql/static_validation/definition_dependencies.rb +0 -1
- data/lib/graphql/static_validation/error.rb +3 -1
- data/lib/graphql/static_validation/literal_validator.rb +1 -1
- data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +1 -1
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +52 -26
- data/lib/graphql/static_validation/rules/fields_will_merge_error.rb +25 -4
- data/lib/graphql/static_validation/rules/fragments_are_finite.rb +2 -2
- data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +3 -1
- data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +4 -4
- data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +7 -7
- data/lib/graphql/static_validation/validation_context.rb +8 -2
- data/lib/graphql/static_validation/validator.rb +15 -12
- data/lib/graphql/string_encoding_error.rb +13 -3
- data/lib/graphql/string_type.rb +1 -1
- data/lib/graphql/subscriptions/action_cable_subscriptions.rb +15 -5
- data/lib/graphql/subscriptions/event.rb +66 -13
- data/lib/graphql/subscriptions/serialize.rb +1 -1
- data/lib/graphql/subscriptions.rb +17 -19
- data/lib/graphql/tracing/appsignal_tracing.rb +15 -0
- data/lib/graphql/types/int.rb +1 -1
- data/lib/graphql/types/relay/connection_behaviors.rb +26 -9
- data/lib/graphql/types/relay/default_relay.rb +5 -1
- data/lib/graphql/types/relay/edge_behaviors.rb +13 -2
- data/lib/graphql/types/relay/has_node_field.rb +1 -1
- data/lib/graphql/types/relay/has_nodes_field.rb +1 -1
- data/lib/graphql/types/string.rb +1 -1
- data/lib/graphql/unauthorized_error.rb +1 -1
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +10 -32
- data/readme.md +1 -1
- metadata +13 -6
data/lib/graphql/schema.rb
CHANGED
@@ -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]
|
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 :
|
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 ||=
|
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(
|
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
|
-
|
1007
|
-
|
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]
|
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.
|
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
|
-
|
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
|
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
|
128
|
+
@context.warden.get_argument(directive_defn, node.name)
|
129
129
|
elsif (field_defn = @field_definitions.last)
|
130
|
-
field_defn
|
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
|
-
@
|
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
|
@@ -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 =
|
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
|
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
|
-
|
178
|
-
|
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
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
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
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
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? ||
|
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.
|
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 :
|
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
|
-
@
|
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
|
-
|
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 =
|
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 =
|
38
|
-
.select{|
|
39
|
-
.
|
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
|
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
|
-
|
25
|
+
argument_owner = case parent
|
26
26
|
when GraphQL::Language::Nodes::Field
|
27
|
-
context.field_definition
|
27
|
+
context.field_definition
|
28
28
|
when GraphQL::Language::Nodes::Directive
|
29
|
-
context.directive_definition
|
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
|
-
|
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 &&
|
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(
|
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 =
|
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
|