graphql 1.11.4 → 1.12.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (160) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/install_generator.rb +5 -5
  3. data/lib/generators/graphql/object_generator.rb +2 -0
  4. data/lib/generators/graphql/relay_generator.rb +63 -0
  5. data/lib/generators/graphql/templates/base_connection.erb +8 -0
  6. data/lib/generators/graphql/templates/base_edge.erb +8 -0
  7. data/lib/generators/graphql/templates/node_type.erb +9 -0
  8. data/lib/generators/graphql/templates/object.erb +1 -1
  9. data/lib/generators/graphql/templates/query_type.erb +1 -3
  10. data/lib/generators/graphql/templates/schema.erb +8 -35
  11. data/lib/generators/graphql/templates/union.erb +1 -1
  12. data/lib/graphql.rb +55 -4
  13. data/lib/graphql/analysis/analyze_query.rb +7 -0
  14. data/lib/graphql/analysis/ast.rb +11 -2
  15. data/lib/graphql/analysis/ast/visitor.rb +9 -1
  16. data/lib/graphql/argument.rb +3 -3
  17. data/lib/graphql/backtrace.rb +28 -19
  18. data/lib/graphql/backtrace/legacy_tracer.rb +56 -0
  19. data/lib/graphql/backtrace/table.rb +22 -2
  20. data/lib/graphql/backtrace/tracer.rb +40 -8
  21. data/lib/graphql/backwards_compatibility.rb +1 -0
  22. data/lib/graphql/compatibility/execution_specification.rb +1 -0
  23. data/lib/graphql/compatibility/lazy_execution_specification.rb +2 -0
  24. data/lib/graphql/compatibility/query_parser_specification.rb +2 -0
  25. data/lib/graphql/compatibility/schema_parser_specification.rb +2 -0
  26. data/lib/graphql/dataloader.rb +198 -0
  27. data/lib/graphql/dataloader/null_dataloader.rb +21 -0
  28. data/lib/graphql/dataloader/request.rb +24 -0
  29. data/lib/graphql/dataloader/request_all.rb +22 -0
  30. data/lib/graphql/dataloader/source.rb +93 -0
  31. data/lib/graphql/define/assign_global_id_field.rb +2 -2
  32. data/lib/graphql/define/instance_definable.rb +32 -2
  33. data/lib/graphql/define/type_definer.rb +5 -5
  34. data/lib/graphql/deprecated_dsl.rb +5 -0
  35. data/lib/graphql/enum_type.rb +2 -0
  36. data/lib/graphql/execution/errors.rb +4 -0
  37. data/lib/graphql/execution/execute.rb +7 -0
  38. data/lib/graphql/execution/interpreter.rb +20 -6
  39. data/lib/graphql/execution/interpreter/arguments.rb +57 -5
  40. data/lib/graphql/execution/interpreter/arguments_cache.rb +8 -0
  41. data/lib/graphql/execution/interpreter/handles_raw_value.rb +0 -7
  42. data/lib/graphql/execution/interpreter/runtime.rb +236 -120
  43. data/lib/graphql/execution/multiplex.rb +20 -6
  44. data/lib/graphql/function.rb +4 -0
  45. data/lib/graphql/input_object_type.rb +2 -0
  46. data/lib/graphql/integer_decoding_error.rb +17 -0
  47. data/lib/graphql/interface_type.rb +3 -1
  48. data/lib/graphql/introspection.rb +96 -0
  49. data/lib/graphql/introspection/field_type.rb +7 -3
  50. data/lib/graphql/introspection/input_value_type.rb +6 -0
  51. data/lib/graphql/introspection/introspection_query.rb +6 -92
  52. data/lib/graphql/introspection/type_type.rb +7 -3
  53. data/lib/graphql/invalid_null_error.rb +1 -1
  54. data/lib/graphql/language/block_string.rb +24 -5
  55. data/lib/graphql/language/document_from_schema_definition.rb +50 -23
  56. data/lib/graphql/language/lexer.rb +7 -3
  57. data/lib/graphql/language/lexer.rl +7 -3
  58. data/lib/graphql/language/nodes.rb +1 -1
  59. data/lib/graphql/language/parser.rb +107 -103
  60. data/lib/graphql/language/parser.y +4 -0
  61. data/lib/graphql/language/sanitized_printer.rb +59 -26
  62. data/lib/graphql/name_validator.rb +6 -7
  63. data/lib/graphql/object_type.rb +2 -0
  64. data/lib/graphql/pagination/connection.rb +5 -1
  65. data/lib/graphql/pagination/connections.rb +15 -17
  66. data/lib/graphql/query.rb +8 -3
  67. data/lib/graphql/query/context.rb +18 -3
  68. data/lib/graphql/query/serial_execution.rb +1 -0
  69. data/lib/graphql/query/validation_pipeline.rb +1 -1
  70. data/lib/graphql/relay/array_connection.rb +2 -2
  71. data/lib/graphql/relay/base_connection.rb +7 -0
  72. data/lib/graphql/relay/connection_instrumentation.rb +4 -4
  73. data/lib/graphql/relay/connection_type.rb +1 -1
  74. data/lib/graphql/relay/mutation.rb +1 -0
  75. data/lib/graphql/relay/node.rb +3 -0
  76. data/lib/graphql/relay/range_add.rb +14 -5
  77. data/lib/graphql/relay/type_extensions.rb +2 -0
  78. data/lib/graphql/scalar_type.rb +2 -0
  79. data/lib/graphql/schema.rb +104 -39
  80. data/lib/graphql/schema/argument.rb +74 -5
  81. data/lib/graphql/schema/build_from_definition.rb +203 -86
  82. data/lib/graphql/schema/default_type_error.rb +2 -0
  83. data/lib/graphql/schema/directive.rb +76 -0
  84. data/lib/graphql/schema/directive/deprecated.rb +1 -1
  85. data/lib/graphql/schema/directive/flagged.rb +57 -0
  86. data/lib/graphql/schema/enum.rb +3 -0
  87. data/lib/graphql/schema/enum_value.rb +12 -6
  88. data/lib/graphql/schema/field.rb +59 -24
  89. data/lib/graphql/schema/field/connection_extension.rb +10 -8
  90. data/lib/graphql/schema/field/scope_extension.rb +1 -1
  91. data/lib/graphql/schema/input_object.rb +38 -25
  92. data/lib/graphql/schema/interface.rb +2 -1
  93. data/lib/graphql/schema/late_bound_type.rb +2 -2
  94. data/lib/graphql/schema/loader.rb +1 -0
  95. data/lib/graphql/schema/member.rb +4 -0
  96. data/lib/graphql/schema/member/base_dsl_methods.rb +1 -0
  97. data/lib/graphql/schema/member/build_type.rb +17 -7
  98. data/lib/graphql/schema/member/has_arguments.rb +70 -51
  99. data/lib/graphql/schema/member/has_deprecation_reason.rb +25 -0
  100. data/lib/graphql/schema/member/has_directives.rb +98 -0
  101. data/lib/graphql/schema/member/has_fields.rb +2 -2
  102. data/lib/graphql/schema/member/has_validators.rb +31 -0
  103. data/lib/graphql/schema/member/type_system_helpers.rb +3 -3
  104. data/lib/graphql/schema/object.rb +11 -0
  105. data/lib/graphql/schema/printer.rb +5 -4
  106. data/lib/graphql/schema/relay_classic_mutation.rb +4 -2
  107. data/lib/graphql/schema/resolver.rb +7 -0
  108. data/lib/graphql/schema/resolver/has_payload_type.rb +2 -0
  109. data/lib/graphql/schema/subscription.rb +19 -1
  110. data/lib/graphql/schema/timeout.rb +29 -15
  111. data/lib/graphql/schema/timeout_middleware.rb +2 -0
  112. data/lib/graphql/schema/unique_within_type.rb +1 -2
  113. data/lib/graphql/schema/validation.rb +10 -0
  114. data/lib/graphql/schema/validator.rb +163 -0
  115. data/lib/graphql/schema/validator/exclusion_validator.rb +31 -0
  116. data/lib/graphql/schema/validator/format_validator.rb +49 -0
  117. data/lib/graphql/schema/validator/inclusion_validator.rb +33 -0
  118. data/lib/graphql/schema/validator/length_validator.rb +57 -0
  119. data/lib/graphql/schema/validator/numericality_validator.rb +71 -0
  120. data/lib/graphql/schema/validator/required_validator.rb +68 -0
  121. data/lib/graphql/static_validation.rb +1 -0
  122. data/lib/graphql/static_validation/all_rules.rb +1 -0
  123. data/lib/graphql/static_validation/rules/fields_will_merge.rb +25 -17
  124. data/lib/graphql/static_validation/rules/input_object_names_are_unique.rb +30 -0
  125. data/lib/graphql/static_validation/rules/input_object_names_are_unique_error.rb +30 -0
  126. data/lib/graphql/static_validation/validation_timeout_error.rb +25 -0
  127. data/lib/graphql/static_validation/validator.rb +33 -7
  128. data/lib/graphql/subscriptions.rb +18 -23
  129. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +21 -7
  130. data/lib/graphql/tracing.rb +2 -2
  131. data/lib/graphql/tracing/appoptics_tracing.rb +3 -1
  132. data/lib/graphql/tracing/platform_tracing.rb +4 -2
  133. data/lib/graphql/tracing/prometheus_tracing/graphql_collector.rb +4 -1
  134. data/lib/graphql/tracing/skylight_tracing.rb +1 -1
  135. data/lib/graphql/types/int.rb +9 -2
  136. data/lib/graphql/types/relay.rb +11 -3
  137. data/lib/graphql/types/relay/base_connection.rb +2 -91
  138. data/lib/graphql/types/relay/base_edge.rb +2 -34
  139. data/lib/graphql/types/relay/connection_behaviors.rb +123 -0
  140. data/lib/graphql/types/relay/default_relay.rb +27 -0
  141. data/lib/graphql/types/relay/edge_behaviors.rb +42 -0
  142. data/lib/graphql/types/relay/has_node_field.rb +41 -0
  143. data/lib/graphql/types/relay/has_nodes_field.rb +41 -0
  144. data/lib/graphql/types/relay/node.rb +2 -4
  145. data/lib/graphql/types/relay/node_behaviors.rb +15 -0
  146. data/lib/graphql/types/relay/node_field.rb +1 -19
  147. data/lib/graphql/types/relay/nodes_field.rb +1 -19
  148. data/lib/graphql/types/relay/page_info.rb +2 -14
  149. data/lib/graphql/types/relay/page_info_behaviors.rb +25 -0
  150. data/lib/graphql/types/string.rb +7 -1
  151. data/lib/graphql/unauthorized_error.rb +1 -1
  152. data/lib/graphql/union_type.rb +2 -0
  153. data/lib/graphql/upgrader/member.rb +1 -0
  154. data/lib/graphql/upgrader/schema.rb +1 -0
  155. data/lib/graphql/version.rb +1 -1
  156. data/readme.md +1 -1
  157. metadata +49 -6
  158. data/lib/graphql/types/relay/base_field.rb +0 -22
  159. data/lib/graphql/types/relay/base_interface.rb +0 -29
  160. data/lib/graphql/types/relay/base_object.rb +0 -26
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ class Schema
5
+ class Validator
6
+ # Use this to specifically reject values from an argument.
7
+ #
8
+ # @example disallow certain values
9
+ #
10
+ # argument :favorite_non_prime, Integer, required: true,
11
+ # validates: { exclusion: { in: [2, 3, 5, 7, ... ]} }
12
+ #
13
+ class ExclusionValidator < Validator
14
+ # @param message [String]
15
+ # @param in [Array] The values to reject
16
+ def initialize(message: "%{validated} is reserved", in:, **default_options)
17
+ # `in` is a reserved word, so work around that
18
+ @in_list = binding.local_variable_get(:in)
19
+ @message = message
20
+ super(**default_options)
21
+ end
22
+
23
+ def validate(_object, _context, value)
24
+ if @in_list.include?(value)
25
+ @message
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ class Schema
5
+ class Validator
6
+ # Use this to assert that string values match (or don't match) the given RegExp.
7
+ #
8
+ # @example requiring input to match a pattern
9
+ #
10
+ # argument :handle, String, required: true,
11
+ # validates: { format: { with: /\A[a-z0-9_]+\Z/ } }
12
+ #
13
+ # @example reject inputs that match a pattern
14
+ #
15
+ # argument :word_that_doesnt_begin_with_a_vowel, String, required: true,
16
+ # validates: { format: { without: /\A[aeiou]/ } }
17
+ #
18
+ # # It's pretty hard to come up with a legitimate use case for `without:`
19
+ #
20
+ class FormatValidator < Validator
21
+ if !String.method_defined?(:match?)
22
+ using GraphQL::StringMatchBackport
23
+ end
24
+
25
+ # @param with [RegExp, nil]
26
+ # @param without [Regexp, nil]
27
+ # @param message [String]
28
+ def initialize(
29
+ with: nil,
30
+ without: nil,
31
+ message: "%{validated} is invalid",
32
+ **default_options
33
+ )
34
+ @with_pattern = with
35
+ @without_pattern = without
36
+ @message = message
37
+ super(**default_options)
38
+ end
39
+
40
+ def validate(_object, _context, value)
41
+ if (@with_pattern && !value.match?(@with_pattern)) ||
42
+ (@without_pattern && value.match?(@without_pattern))
43
+ @message
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ class Schema
5
+ class Validator
6
+ # You can use this to allow certain values for an argument.
7
+ #
8
+ # Usually, a {GraphQL::Schema::Enum} is better for this, because it's self-documenting.
9
+ #
10
+ # @example only allow certain values for an argument
11
+ #
12
+ # argument :favorite_prime, Integer, required: true,
13
+ # validates: { inclusion: { in: [2, 3, 5, 7, 11, ... ] } }
14
+ #
15
+ class InclusionValidator < Validator
16
+ # @param message [String]
17
+ # @param in [Array] The values to allow
18
+ def initialize(in:, message: "%{validated} is not included in the list", **default_options)
19
+ # `in` is a reserved word, so work around that
20
+ @in_list = binding.local_variable_get(:in)
21
+ @message = message
22
+ super(**default_options)
23
+ end
24
+
25
+ def validate(_object, _context, value)
26
+ if !@in_list.include?(value)
27
+ @message
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ class Schema
5
+ class Validator
6
+ # Use this to enforce a `.length` restriction on incoming values. It works for both Strings and Lists.
7
+ #
8
+ # @example Allow no more than 10 IDs
9
+ #
10
+ # argument :ids, [ID], required: true, validates: { length: { maximum: 10 } }
11
+ #
12
+ # @example Require three selections
13
+ #
14
+ # argument :ice_cream_preferences, [ICE_CREAM_FLAVOR], required: true, validates: { length: { is: 3 } }
15
+ #
16
+ class LengthValidator < Validator
17
+ # @param maximum [Integer]
18
+ # @param too_long [String] Used when `maximum` is exceeded or value is greater than `within`
19
+ # @param minimum [Integer]
20
+ # @param too_short [String] Used with value is less than `minimum` or less than `within`
21
+ # @param is [Integer] Exact length requirement
22
+ # @param wrong_length [String] Used when value doesn't match `is`
23
+ # @param within [Range] An allowed range (becomes `minimum:` and `maximum:` under the hood)
24
+ # @param message [String]
25
+ def initialize(
26
+ maximum: nil, too_long: "%{validated} is too long (maximum is %{count})",
27
+ minimum: nil, too_short: "%{validated} is too short (minimum is %{count})",
28
+ is: nil, within: nil, wrong_length: "%{validated} is the wrong length (should be %{count})",
29
+ message: nil,
30
+ **default_options
31
+ )
32
+ if within && (minimum || maximum)
33
+ raise ArgumentError, "`length: { ... }` may include `within:` _or_ `minimum:`/`maximum:`, but not both"
34
+ end
35
+ # Under the hood, `within` is decomposed into `minimum` and `maximum`
36
+ @maximum = maximum || (within && within.max)
37
+ @too_long = message || too_long
38
+ @minimum = minimum || (within && within.min)
39
+ @too_short = message || too_short
40
+ @is = is
41
+ @wrong_length = message || wrong_length
42
+ super(**default_options)
43
+ end
44
+
45
+ def validate(_object, _context, value)
46
+ if @maximum && value.length > @maximum
47
+ partial_format(@too_long, { count: @maximum })
48
+ elsif @minimum && value.length < @minimum
49
+ partial_format(@too_short, { count: @minimum })
50
+ elsif @is && value.length != @is
51
+ partial_format(@wrong_length, { count: @is })
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,71 @@
1
+ module GraphQL
2
+ class Schema
3
+ class Validator
4
+ # Use this to assert numerical comparisons hold true for inputs.
5
+ #
6
+ # @example Require a number between 0 and 1
7
+ #
8
+ # argument :batting_average, Float, required: true, validates: { numericality: { within: 0..1 } }
9
+ #
10
+ # @example Require the number 42
11
+ #
12
+ # argument :the_answer, Integer, required: true, validates: { numericality: { equal_to: 42 } }
13
+ #
14
+ # @example Require a real number
15
+ #
16
+ # argument :items_count, Integer, required: true, validates: { numericality: { greater_than_or_equal_to: 0 } }
17
+ #
18
+ class NumericalityValidator < Validator
19
+ # @param greater_than [Integer]
20
+ # @param greater_than_or_equal_to [Integer]
21
+ # @param less_than [Integer]
22
+ # @param less_than_or_equal_to [Integer]
23
+ # @param equal_to [Integer]
24
+ # @param other_than [Integer]
25
+ # @param odd [Boolean]
26
+ # @param even [Boolean]
27
+ # @param message [String] used for all validation failures
28
+ def initialize(
29
+ greater_than: nil, greater_than_or_equal_to: nil,
30
+ less_than: nil, less_than_or_equal_to: nil,
31
+ equal_to: nil, other_than: nil,
32
+ odd: nil, even: nil,
33
+ message: "%{validated} must be %{comparison} %{target}",
34
+ **default_options
35
+ )
36
+
37
+ @greater_than = greater_than
38
+ @greater_than_or_equal_to = greater_than_or_equal_to
39
+ @less_than = less_than
40
+ @less_than_or_equal_to = less_than_or_equal_to
41
+ @equal_to = equal_to
42
+ @other_than = other_than
43
+ @odd = odd
44
+ @even = even
45
+ @message = message
46
+ super(**default_options)
47
+ end
48
+
49
+ def validate(object, context, value)
50
+ if @greater_than && value <= @greater_than
51
+ partial_format(@message, { comparison: "greater than", target: @greater_than })
52
+ elsif @greater_than_or_equal_to && value < @greater_than_or_equal_to
53
+ partial_format(@message, { comparison: "greater than or equal to", target: @greater_than_or_equal_to })
54
+ elsif @less_than && value >= @less_than
55
+ partial_format(@message, { comparison: "less than", target: @less_than })
56
+ elsif @less_than_or_equal_to && value > @less_than_or_equal_to
57
+ partial_format(@message, { comparison: "less than or equal to", target: @less_than_or_equal_to })
58
+ elsif @equal_to && value != @equal_to
59
+ partial_format(@message, { comparison: "equal to", target: @equal_to })
60
+ elsif @other_than && value == @other_than
61
+ partial_format(@message, { comparison: "something other than", target: @other_than })
62
+ elsif @even && !value.even?
63
+ (partial_format(@message, { comparison: "even", target: "" })).strip
64
+ elsif @odd && !value.odd?
65
+ (partial_format(@message, { comparison: "odd", target: "" })).strip
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ class Schema
5
+ class Validator
6
+ # Use this validator to require _one_ of the named arguments to be present.
7
+ # Or, use Arrays of symbols to name a valid _set_ of arguments.
8
+ #
9
+ # (This is for specifying mutually exclusive sets of arguments.)
10
+ #
11
+ # @example Require exactly one of these arguments
12
+ #
13
+ # field :update_amount, IngredientAmount, null: false do
14
+ # argument :ingredient_id, ID, required: true
15
+ # argument :cups, Integer, required: false
16
+ # argument :tablespoons, Integer, required: false
17
+ # argument :teaspoons, Integer, required: true
18
+ # validates required: { one_of: [:cups, :tablespoons, :teaspoons] }
19
+ # end
20
+ #
21
+ # @example Require one of these _sets_ of arguments
22
+ #
23
+ # field :find_object, Node, null: true do
24
+ # argument :node_id, ID, required: false
25
+ # argument :object_type, String, required: false
26
+ # argument :object_id, Integer, required: false
27
+ # # either a global `node_id` or an `object_type`/`object_id` pair is required:
28
+ # validates required: { one_of: [:node_id, [:object_type, :object_id]] }
29
+ # end
30
+ #
31
+ class RequiredValidator < Validator
32
+ # @param one_of [Symbol, Array<Symbol>] An argument, or a list of arguments, that represents a valid set of inputs for this field
33
+ # @param message [String]
34
+ def initialize(one_of:, message: "%{validated} has the wrong arguments", **default_options)
35
+ @one_of = one_of
36
+ @message = message
37
+ super(**default_options)
38
+ end
39
+
40
+ def validate(_object, _context, value)
41
+ matched_conditions = 0
42
+
43
+ @one_of.each do |one_of_condition|
44
+ case one_of_condition
45
+ when Symbol
46
+ if value.key?(one_of_condition)
47
+ matched_conditions += 1
48
+ end
49
+ when Array
50
+ if one_of_condition.all? { |k| value.key?(k) }
51
+ matched_conditions += 1
52
+ break
53
+ end
54
+ else
55
+ raise ArgumentError, "Unknown one_of condition: #{one_of_condition.inspect}"
56
+ end
57
+ end
58
+
59
+ if matched_conditions == 1
60
+ nil # OK
61
+ else
62
+ @message
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -4,6 +4,7 @@ require "graphql/static_validation/definition_dependencies"
4
4
  require "graphql/static_validation/type_stack"
5
5
  require "graphql/static_validation/validator"
6
6
  require "graphql/static_validation/validation_context"
7
+ require "graphql/static_validation/validation_timeout_error"
7
8
  require "graphql/static_validation/literal_validator"
8
9
  require "graphql/static_validation/base_visitor"
9
10
  require "graphql/static_validation/no_validate_visitor"
@@ -34,6 +34,7 @@ module GraphQL
34
34
  GraphQL::StaticValidation::VariableUsagesAreAllowed,
35
35
  GraphQL::StaticValidation::MutationRootExists,
36
36
  GraphQL::StaticValidation::SubscriptionRootExists,
37
+ GraphQL::StaticValidation::InputObjectNamesAreUnique,
37
38
  ]
38
39
  end
39
40
  end
@@ -202,15 +202,16 @@ module GraphQL
202
202
  )
203
203
  end
204
204
 
205
- args = possible_arguments(node1, node2)
206
- if args.size > 1
207
- msg = "Field '#{response_key}' has an argument conflict: #{args.map { |arg| GraphQL::Language.serialize(arg) }.join(" or ")}?"
205
+ 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}?"
208
209
  context.errors << GraphQL::StaticValidation::FieldsWillMergeError.new(
209
210
  msg,
210
211
  nodes: [node1, node2],
211
212
  path: [],
212
213
  field_name: response_key,
213
- conflicts: args.map { |arg| GraphQL::Language.serialize(arg) }.join(" or ")
214
+ conflicts: conflicts
214
215
  )
215
216
  end
216
217
  end
@@ -326,20 +327,19 @@ module GraphQL
326
327
  [fields, fragment_spreads]
327
328
  end
328
329
 
329
- def possible_arguments(field1, field2)
330
+ def same_arguments?(field1, field2)
330
331
  # Check for incompatible / non-identical arguments on this node:
331
- [field1, field2].map do |n|
332
- if n.arguments.any?
333
- serialized_args = {}
334
- n.arguments.each do |a|
335
- arg_value = a.value
336
- serialized_args[a.name] = serialize_arg(arg_value)
337
- end
338
- serialized_args
339
- else
340
- NO_ARGS
341
- end
342
- end.uniq
332
+ arguments1 = field1.arguments
333
+ arguments2 = field2.arguments
334
+
335
+ return false if arguments1.length != arguments2.length
336
+
337
+ arguments1.all? do |argument1|
338
+ argument2 = arguments2.find { |argument| argument.name == argument1.name }
339
+ return false if argument2.nil?
340
+
341
+ serialize_arg(argument1.value) == serialize_arg(argument2.value)
342
+ end
343
343
  end
344
344
 
345
345
  def serialize_arg(arg_value)
@@ -353,6 +353,14 @@ module GraphQL
353
353
  end
354
354
  end
355
355
 
356
+ def serialize_field_args(field)
357
+ serialized_args = {}
358
+ field.arguments.each do |argument|
359
+ serialized_args[argument.name] = serialize_arg(argument.value)
360
+ end
361
+ serialized_args
362
+ end
363
+
356
364
  def compared_fragments_key(frag1, frag2, exclusive)
357
365
  # Cache key to not compare two fragments more than once.
358
366
  # The key includes both fragment names sorted (this way we
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+ module GraphQL
3
+ module StaticValidation
4
+ module InputObjectNamesAreUnique
5
+ def on_input_object(node, parent)
6
+ validate_input_fields(node)
7
+ super
8
+ end
9
+
10
+ private
11
+
12
+ def validate_input_fields(node)
13
+ input_field_defns = node.arguments
14
+ input_fields_by_name = Hash.new { |h, k| h[k] = [] }
15
+ input_field_defns.each { |a| input_fields_by_name[a.name] << a }
16
+
17
+ input_fields_by_name.each do |name, defns|
18
+ if defns.size > 1
19
+ error = GraphQL::StaticValidation::InputObjectNamesAreUniqueError.new(
20
+ "There can be only one input field named \"#{name}\"",
21
+ nodes: defns,
22
+ name: name
23
+ )
24
+ add_error(error)
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+ module GraphQL
3
+ module StaticValidation
4
+ class InputObjectNamesAreUniqueError < StaticValidation::Error
5
+ attr_reader :name
6
+
7
+ def initialize(message, path: nil, nodes: [], name:)
8
+ super(message, path: path, nodes: nodes)
9
+ @name = name
10
+ end
11
+
12
+ # A hash representation of this Message
13
+ def to_h
14
+ extensions = {
15
+ "code" => code,
16
+ "name" => name
17
+ }
18
+
19
+ super.merge({
20
+ "extensions" => extensions
21
+ })
22
+ end
23
+
24
+ def code
25
+ "inputFieldNotUnique"
26
+ end
27
+ end
28
+ end
29
+ end
30
+