graphql 1.11.3 → 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.
- checksums.yaml +4 -4
- data/lib/generators/graphql/core.rb +8 -0
- data/lib/generators/graphql/install_generator.rb +5 -5
- data/lib/generators/graphql/object_generator.rb +2 -0
- data/lib/generators/graphql/relay_generator.rb +63 -0
- data/lib/generators/graphql/templates/base_argument.erb +2 -0
- data/lib/generators/graphql/templates/base_connection.erb +8 -0
- data/lib/generators/graphql/templates/base_edge.erb +8 -0
- data/lib/generators/graphql/templates/base_enum.erb +2 -0
- data/lib/generators/graphql/templates/base_field.erb +2 -0
- data/lib/generators/graphql/templates/base_input_object.erb +2 -0
- data/lib/generators/graphql/templates/base_interface.erb +2 -0
- data/lib/generators/graphql/templates/base_mutation.erb +2 -0
- data/lib/generators/graphql/templates/base_object.erb +2 -0
- data/lib/generators/graphql/templates/base_scalar.erb +2 -0
- data/lib/generators/graphql/templates/base_union.erb +2 -0
- data/lib/generators/graphql/templates/enum.erb +2 -0
- data/lib/generators/graphql/templates/graphql_controller.erb +2 -0
- data/lib/generators/graphql/templates/interface.erb +2 -0
- data/lib/generators/graphql/templates/loader.erb +2 -0
- data/lib/generators/graphql/templates/mutation.erb +2 -0
- data/lib/generators/graphql/templates/mutation_type.erb +2 -0
- data/lib/generators/graphql/templates/node_type.erb +9 -0
- data/lib/generators/graphql/templates/object.erb +3 -1
- data/lib/generators/graphql/templates/query_type.erb +3 -3
- data/lib/generators/graphql/templates/scalar.erb +2 -0
- data/lib/generators/graphql/templates/schema.erb +10 -35
- data/lib/generators/graphql/templates/union.erb +3 -1
- data/lib/graphql.rb +55 -4
- data/lib/graphql/analysis/analyze_query.rb +7 -0
- data/lib/graphql/analysis/ast.rb +11 -2
- data/lib/graphql/analysis/ast/visitor.rb +9 -1
- data/lib/graphql/argument.rb +3 -3
- data/lib/graphql/backtrace.rb +28 -19
- data/lib/graphql/backtrace/legacy_tracer.rb +56 -0
- data/lib/graphql/backtrace/table.rb +22 -2
- data/lib/graphql/backtrace/tracer.rb +40 -8
- data/lib/graphql/backwards_compatibility.rb +1 -0
- data/lib/graphql/compatibility/execution_specification.rb +1 -0
- data/lib/graphql/compatibility/lazy_execution_specification.rb +2 -0
- data/lib/graphql/compatibility/query_parser_specification.rb +2 -0
- data/lib/graphql/compatibility/schema_parser_specification.rb +2 -0
- data/lib/graphql/dataloader.rb +197 -0
- data/lib/graphql/dataloader/null_dataloader.rb +21 -0
- data/lib/graphql/dataloader/request.rb +24 -0
- data/lib/graphql/dataloader/request_all.rb +22 -0
- data/lib/graphql/dataloader/source.rb +93 -0
- data/lib/graphql/define/assign_global_id_field.rb +2 -2
- data/lib/graphql/define/instance_definable.rb +32 -2
- data/lib/graphql/define/type_definer.rb +5 -5
- data/lib/graphql/deprecated_dsl.rb +5 -0
- data/lib/graphql/enum_type.rb +2 -0
- data/lib/graphql/execution/errors.rb +4 -0
- data/lib/graphql/execution/execute.rb +7 -0
- data/lib/graphql/execution/interpreter.rb +20 -6
- data/lib/graphql/execution/interpreter/arguments.rb +57 -5
- data/lib/graphql/execution/interpreter/arguments_cache.rb +8 -0
- data/lib/graphql/execution/interpreter/handles_raw_value.rb +0 -7
- data/lib/graphql/execution/interpreter/runtime.rb +251 -138
- data/lib/graphql/execution/multiplex.rb +20 -6
- data/lib/graphql/function.rb +4 -0
- data/lib/graphql/input_object_type.rb +2 -0
- data/lib/graphql/integer_decoding_error.rb +17 -0
- data/lib/graphql/interface_type.rb +3 -1
- data/lib/graphql/introspection.rb +96 -0
- data/lib/graphql/introspection/field_type.rb +7 -3
- data/lib/graphql/introspection/input_value_type.rb +6 -0
- data/lib/graphql/introspection/introspection_query.rb +6 -92
- data/lib/graphql/introspection/type_type.rb +7 -3
- data/lib/graphql/invalid_null_error.rb +1 -1
- data/lib/graphql/language/block_string.rb +24 -5
- data/lib/graphql/language/document_from_schema_definition.rb +50 -23
- data/lib/graphql/language/lexer.rb +7 -3
- data/lib/graphql/language/lexer.rl +7 -3
- data/lib/graphql/language/nodes.rb +1 -1
- data/lib/graphql/language/parser.rb +107 -103
- data/lib/graphql/language/parser.y +4 -0
- data/lib/graphql/language/sanitized_printer.rb +59 -26
- data/lib/graphql/name_validator.rb +6 -7
- data/lib/graphql/object_type.rb +2 -0
- data/lib/graphql/pagination/connection.rb +5 -1
- data/lib/graphql/pagination/connections.rb +15 -17
- data/lib/graphql/query.rb +8 -3
- data/lib/graphql/query/context.rb +38 -4
- data/lib/graphql/query/fingerprint.rb +2 -0
- data/lib/graphql/query/serial_execution.rb +1 -0
- data/lib/graphql/query/validation_pipeline.rb +4 -1
- data/lib/graphql/relay/array_connection.rb +2 -2
- data/lib/graphql/relay/base_connection.rb +7 -0
- data/lib/graphql/relay/connection_instrumentation.rb +4 -4
- data/lib/graphql/relay/connection_type.rb +1 -1
- data/lib/graphql/relay/mutation.rb +1 -0
- data/lib/graphql/relay/node.rb +3 -0
- data/lib/graphql/relay/range_add.rb +14 -5
- data/lib/graphql/relay/type_extensions.rb +2 -0
- data/lib/graphql/scalar_type.rb +2 -0
- data/lib/graphql/schema.rb +107 -38
- data/lib/graphql/schema/argument.rb +74 -5
- data/lib/graphql/schema/build_from_definition.rb +203 -86
- data/lib/graphql/schema/default_type_error.rb +2 -0
- data/lib/graphql/schema/directive.rb +76 -0
- data/lib/graphql/schema/directive/deprecated.rb +1 -1
- data/lib/graphql/schema/directive/flagged.rb +57 -0
- data/lib/graphql/schema/enum.rb +3 -0
- data/lib/graphql/schema/enum_value.rb +12 -6
- data/lib/graphql/schema/field.rb +59 -24
- data/lib/graphql/schema/field/connection_extension.rb +11 -9
- data/lib/graphql/schema/field/scope_extension.rb +1 -1
- data/lib/graphql/schema/input_object.rb +38 -25
- data/lib/graphql/schema/interface.rb +2 -1
- data/lib/graphql/schema/late_bound_type.rb +2 -2
- data/lib/graphql/schema/loader.rb +1 -0
- data/lib/graphql/schema/member.rb +4 -0
- data/lib/graphql/schema/member/base_dsl_methods.rb +1 -0
- data/lib/graphql/schema/member/build_type.rb +17 -7
- data/lib/graphql/schema/member/has_arguments.rb +70 -51
- data/lib/graphql/schema/member/has_deprecation_reason.rb +25 -0
- data/lib/graphql/schema/member/has_directives.rb +98 -0
- data/lib/graphql/schema/member/has_fields.rb +2 -2
- data/lib/graphql/schema/member/has_validators.rb +31 -0
- data/lib/graphql/schema/member/type_system_helpers.rb +3 -3
- data/lib/graphql/schema/object.rb +11 -0
- data/lib/graphql/schema/printer.rb +5 -4
- data/lib/graphql/schema/relay_classic_mutation.rb +4 -2
- data/lib/graphql/schema/resolver.rb +7 -0
- data/lib/graphql/schema/resolver/has_payload_type.rb +2 -0
- data/lib/graphql/schema/subscription.rb +20 -12
- data/lib/graphql/schema/timeout.rb +29 -15
- data/lib/graphql/schema/timeout_middleware.rb +2 -0
- data/lib/graphql/schema/unique_within_type.rb +1 -2
- data/lib/graphql/schema/validation.rb +10 -0
- data/lib/graphql/schema/validator.rb +163 -0
- data/lib/graphql/schema/validator/exclusion_validator.rb +31 -0
- data/lib/graphql/schema/validator/format_validator.rb +49 -0
- data/lib/graphql/schema/validator/inclusion_validator.rb +33 -0
- data/lib/graphql/schema/validator/length_validator.rb +57 -0
- data/lib/graphql/schema/validator/numericality_validator.rb +71 -0
- data/lib/graphql/schema/validator/required_validator.rb +68 -0
- data/lib/graphql/schema/warden.rb +2 -3
- data/lib/graphql/static_validation.rb +1 -0
- data/lib/graphql/static_validation/all_rules.rb +1 -0
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +25 -17
- data/lib/graphql/static_validation/rules/input_object_names_are_unique.rb +30 -0
- data/lib/graphql/static_validation/rules/input_object_names_are_unique_error.rb +30 -0
- data/lib/graphql/static_validation/validation_timeout_error.rb +25 -0
- data/lib/graphql/static_validation/validator.rb +31 -7
- data/lib/graphql/subscriptions.rb +23 -16
- data/lib/graphql/subscriptions/action_cable_subscriptions.rb +21 -7
- data/lib/graphql/tracing.rb +2 -2
- data/lib/graphql/tracing/appoptics_tracing.rb +12 -2
- data/lib/graphql/tracing/platform_tracing.rb +4 -2
- data/lib/graphql/tracing/prometheus_tracing/graphql_collector.rb +4 -1
- data/lib/graphql/tracing/skylight_tracing.rb +1 -1
- data/lib/graphql/types/int.rb +9 -2
- data/lib/graphql/types/iso_8601_date_time.rb +2 -1
- data/lib/graphql/types/relay.rb +11 -3
- data/lib/graphql/types/relay/base_connection.rb +2 -90
- data/lib/graphql/types/relay/base_edge.rb +2 -34
- data/lib/graphql/types/relay/connection_behaviors.rb +123 -0
- data/lib/graphql/types/relay/default_relay.rb +27 -0
- data/lib/graphql/types/relay/edge_behaviors.rb +42 -0
- data/lib/graphql/types/relay/has_node_field.rb +41 -0
- data/lib/graphql/types/relay/has_nodes_field.rb +41 -0
- data/lib/graphql/types/relay/node.rb +2 -4
- data/lib/graphql/types/relay/node_behaviors.rb +15 -0
- data/lib/graphql/types/relay/node_field.rb +1 -19
- data/lib/graphql/types/relay/nodes_field.rb +1 -19
- data/lib/graphql/types/relay/page_info.rb +2 -14
- data/lib/graphql/types/relay/page_info_behaviors.rb +25 -0
- data/lib/graphql/types/string.rb +7 -1
- data/lib/graphql/unauthorized_error.rb +1 -1
- data/lib/graphql/union_type.rb +2 -0
- data/lib/graphql/upgrader/member.rb +1 -0
- data/lib/graphql/upgrader/schema.rb +1 -0
- data/lib/graphql/version.rb +1 -1
- data/readme.md +1 -1
- metadata +38 -9
- data/lib/graphql/types/relay/base_field.rb +0 -22
- data/lib/graphql/types/relay/base_interface.rb +0 -29
- 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
|
@@ -40,7 +40,6 @@ module GraphQL
|
|
40
40
|
# @param filter [<#call(member)>] Objects are hidden when `.call(member, ctx)` returns true
|
41
41
|
# @param context [GraphQL::Query::Context]
|
42
42
|
# @param schema [GraphQL::Schema]
|
43
|
-
# @param deep_check [Boolean]
|
44
43
|
def initialize(filter, context:, schema:)
|
45
44
|
@schema = schema.interpreter? ? schema : schema.graphql_definition
|
46
45
|
# Cache these to avoid repeated hits to the inheritance chain when one isn't present
|
@@ -51,7 +50,7 @@ module GraphQL
|
|
51
50
|
@visibility_cache = read_through { |m| filter.call(m, context) }
|
52
51
|
end
|
53
52
|
|
54
|
-
# @return [
|
53
|
+
# @return [Hash<String, GraphQL::BaseType>] Visible types in the schema
|
55
54
|
def types
|
56
55
|
@types ||= begin
|
57
56
|
vis_types = {}
|
@@ -199,7 +198,7 @@ module GraphQL
|
|
199
198
|
if (iface_field_defn = interface_type.get_field(field_defn.graphql_name))
|
200
199
|
any_interface_has_field = true
|
201
200
|
|
202
|
-
if
|
201
|
+
if interfaces(type_defn).include?(interface_type) && visible_field?(interface_type, iface_field_defn)
|
203
202
|
any_interface_has_visible_field = true
|
204
203
|
end
|
205
204
|
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"
|
@@ -202,15 +202,16 @@ module GraphQL
|
|
202
202
|
)
|
203
203
|
end
|
204
204
|
|
205
|
-
|
206
|
-
|
207
|
-
|
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:
|
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
|
330
|
+
def same_arguments?(field1, field2)
|
330
331
|
# Check for incompatible / non-identical arguments on this node:
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
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
|