graphql 1.11.12 → 1.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of graphql might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/lib/generators/graphql/install_generator.rb +5 -5
- data/lib/generators/graphql/relay_generator.rb +63 -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/node_type.erb +9 -0
- data/lib/generators/graphql/templates/object.erb +1 -1
- data/lib/generators/graphql/templates/query_type.erb +1 -3
- data/lib/generators/graphql/templates/schema.erb +8 -35
- data/lib/graphql/analysis/analyze_query.rb +7 -0
- data/lib/graphql/analysis/ast/visitor.rb +9 -1
- data/lib/graphql/analysis/ast.rb +11 -2
- data/lib/graphql/backtrace/legacy_tracer.rb +56 -0
- data/lib/graphql/backtrace/table.rb +22 -2
- data/lib/graphql/backtrace/tracer.rb +40 -9
- data/lib/graphql/backtrace.rb +28 -19
- 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/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/dataloader.rb +197 -0
- data/lib/graphql/define/assign_global_id_field.rb +1 -1
- 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/arguments.rb +51 -14
- data/lib/graphql/execution/interpreter/handles_raw_value.rb +0 -7
- data/lib/graphql/execution/interpreter/runtime.rb +210 -124
- data/lib/graphql/execution/interpreter.rb +10 -6
- 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/interface_type.rb +3 -1
- data/lib/graphql/language/document_from_schema_definition.rb +50 -23
- data/lib/graphql/language/nodes.rb +0 -5
- data/lib/graphql/language/visitor.rb +0 -1
- data/lib/graphql/object_type.rb +2 -0
- data/lib/graphql/pagination/connection.rb +5 -1
- data/lib/graphql/pagination/connections.rb +6 -16
- data/lib/graphql/query/context.rb +4 -0
- data/lib/graphql/query/serial_execution.rb +1 -0
- data/lib/graphql/query/validation_pipeline.rb +1 -1
- data/lib/graphql/query.rb +2 -0
- 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/type_extensions.rb +2 -0
- data/lib/graphql/scalar_type.rb +2 -0
- data/lib/graphql/schema/argument.rb +30 -10
- data/lib/graphql/schema/build_from_definition.rb +145 -58
- data/lib/graphql/schema/directive/flagged.rb +57 -0
- data/lib/graphql/schema/directive.rb +76 -0
- data/lib/graphql/schema/enum.rb +3 -0
- data/lib/graphql/schema/enum_value.rb +13 -7
- data/lib/graphql/schema/field/connection_extension.rb +3 -2
- data/lib/graphql/schema/field.rb +28 -10
- data/lib/graphql/schema/input_object.rb +36 -28
- data/lib/graphql/schema/interface.rb +1 -0
- data/lib/graphql/schema/member/base_dsl_methods.rb +1 -0
- data/lib/graphql/schema/member/build_type.rb +3 -3
- data/lib/graphql/schema/member/has_arguments.rb +24 -6
- 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_validators.rb +31 -0
- data/lib/graphql/schema/member/type_system_helpers.rb +1 -1
- data/lib/graphql/schema/member.rb +4 -0
- data/lib/graphql/schema/object.rb +11 -0
- data/lib/graphql/schema/printer.rb +5 -4
- data/lib/graphql/schema/resolver/has_payload_type.rb +2 -0
- data/lib/graphql/schema/resolver.rb +7 -0
- data/lib/graphql/schema/subscription.rb +19 -1
- data/lib/graphql/schema/timeout_middleware.rb +2 -0
- data/lib/graphql/schema/validation.rb +2 -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/validator.rb +163 -0
- data/lib/graphql/schema.rb +72 -49
- data/lib/graphql/static_validation/base_visitor.rb +0 -3
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +4 -4
- data/lib/graphql/static_validation/rules/fragments_are_finite.rb +2 -2
- data/lib/graphql/static_validation/validation_context.rb +1 -6
- data/lib/graphql/static_validation/validator.rb +12 -14
- data/lib/graphql/subscriptions.rb +17 -20
- data/lib/graphql/tracing/appoptics_tracing.rb +3 -1
- data/lib/graphql/tracing/platform_tracing.rb +3 -1
- data/lib/graphql/tracing/skylight_tracing.rb +1 -1
- data/lib/graphql/tracing.rb +2 -2
- data/lib/graphql/types/relay/base_connection.rb +2 -92
- data/lib/graphql/types/relay/base_edge.rb +2 -35
- 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/relay.rb +11 -3
- 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/lib/graphql.rb +38 -4
- metadata +34 -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
@@ -6,6 +6,8 @@ module GraphQL
|
|
6
6
|
# Its {RULES} contain objects that respond to `#call(type)`. Rules are
|
7
7
|
# looked up for given types (by class ancestry), then applied to
|
8
8
|
# the object until an error is returned.
|
9
|
+
#
|
10
|
+
# Remove this in GraphQL-Ruby 2.0 when schema instances are removed.
|
9
11
|
class Validation
|
10
12
|
# Lookup the rules for `object` based on its class,
|
11
13
|
# Then returns an error message or `nil`
|
@@ -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
|
@@ -0,0 +1,163 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GraphQL
|
4
|
+
class Schema
|
5
|
+
class Validator
|
6
|
+
# The thing being validated
|
7
|
+
# @return [GraphQL::Schema::Argument, GraphQL::Schema::Field, GraphQL::Schema::Resolver, Class<GraphQL::Schema::InputObject>]
|
8
|
+
attr_reader :validated
|
9
|
+
|
10
|
+
# TODO should this implement `if:` and `unless:` ?
|
11
|
+
# @param validated [GraphQL::Schema::Argument, GraphQL::Schema::Field, GraphQL::Schema::Resolver, Class<GraphQL::Schema::InputObject>] The argument or argument owner this validator is attached to
|
12
|
+
# @param allow_blank [Boolean] if `true`, then objects that respond to `.blank?` and return true for `.blank?` will skip this validation
|
13
|
+
# @param allow_null [Boolean] if `true`, then incoming `null`s will skip this validation
|
14
|
+
def initialize(validated:, allow_blank: false, allow_null: false)
|
15
|
+
@validated = validated
|
16
|
+
@allow_blank = allow_blank
|
17
|
+
@allow_null = allow_null
|
18
|
+
end
|
19
|
+
|
20
|
+
# @param object [Object] The application object that this argument's field is being resolved for
|
21
|
+
# @param context [GraphQL::Query::Context]
|
22
|
+
# @param value [Object] The client-provided value for this argument (after parsing and coercing by the input type)
|
23
|
+
# @return [nil, Array<String>, String] Error message or messages to add
|
24
|
+
def validate(object, context, value)
|
25
|
+
raise GraphQL::RequiredImplementationMissingError, "Validator classes should implement #validate"
|
26
|
+
end
|
27
|
+
|
28
|
+
# This is called by the validation system and eventually calls {#validate}.
|
29
|
+
# @api private
|
30
|
+
def apply(object, context, value)
|
31
|
+
if value.nil?
|
32
|
+
if @allow_null
|
33
|
+
nil # skip this
|
34
|
+
else
|
35
|
+
"%{validated} can't be null"
|
36
|
+
end
|
37
|
+
elsif value.respond_to?(:blank?) && value.blank?
|
38
|
+
if @allow_blank
|
39
|
+
nil # skip this
|
40
|
+
else
|
41
|
+
"%{validated} can't be blank"
|
42
|
+
end
|
43
|
+
else
|
44
|
+
validate(object, context, value)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# This is like `String#%`, but it supports the case that only some of `string`'s
|
49
|
+
# values are present in `substitutions`
|
50
|
+
def partial_format(string, substitutions)
|
51
|
+
substitutions.each do |key, value|
|
52
|
+
sub_v = value.is_a?(String) ? value : value.to_s
|
53
|
+
string = string.gsub("%{#{key}}", sub_v)
|
54
|
+
end
|
55
|
+
string
|
56
|
+
end
|
57
|
+
|
58
|
+
# @param schema_member [GraphQL::Schema::Field, GraphQL::Schema::Argument, Class<GraphQL::Schema::InputObject>]
|
59
|
+
# @param validates_hash [Hash{Symbol => Hash}, Hash{Class => Hash} nil] A configuration passed as `validates:`
|
60
|
+
# @return [Array<Validator>]
|
61
|
+
def self.from_config(schema_member, validates_hash)
|
62
|
+
if validates_hash.nil? || validates_hash.empty?
|
63
|
+
EMPTY_ARRAY
|
64
|
+
else
|
65
|
+
validates_hash.map do |validator_name, options|
|
66
|
+
validator_class = case validator_name
|
67
|
+
when Class
|
68
|
+
validator_name
|
69
|
+
else
|
70
|
+
all_validators[validator_name] || raise(ArgumentError, "unknown validation: #{validator_name.inspect}")
|
71
|
+
end
|
72
|
+
validator_class.new(validated: schema_member, **options)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# Add `validator_class` to be initialized when `validates:` is given `name`.
|
78
|
+
# (It's initialized with whatever options are given by the key `name`).
|
79
|
+
# @param name [Symbol]
|
80
|
+
# @param validator_class [Class]
|
81
|
+
# @return [void]
|
82
|
+
def self.install(name, validator_class)
|
83
|
+
all_validators[name] = validator_class
|
84
|
+
nil
|
85
|
+
end
|
86
|
+
|
87
|
+
# Remove whatever validator class is {.install}ed at `name`, if there is one
|
88
|
+
# @param name [Symbol]
|
89
|
+
# @return [void]
|
90
|
+
def self.uninstall(name)
|
91
|
+
all_validators.delete(name)
|
92
|
+
nil
|
93
|
+
end
|
94
|
+
|
95
|
+
class << self
|
96
|
+
attr_accessor :all_validators
|
97
|
+
end
|
98
|
+
|
99
|
+
self.all_validators = {}
|
100
|
+
|
101
|
+
include Schema::FindInheritedValue::EmptyObjects
|
102
|
+
|
103
|
+
class ValidationFailedError < GraphQL::ExecutionError
|
104
|
+
attr_reader :errors
|
105
|
+
|
106
|
+
def initialize(errors:)
|
107
|
+
@errors = errors
|
108
|
+
super(errors.join(", "))
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
# @param validators [Array<Validator>]
|
113
|
+
# @param object [Object]
|
114
|
+
# @param context [Query::Context]
|
115
|
+
# @param value [Object]
|
116
|
+
# @return [void]
|
117
|
+
# @raises [ValidationFailedError]
|
118
|
+
def self.validate!(validators, object, context, value, as: nil)
|
119
|
+
# Assuming the default case is no errors, reduce allocations in that case.
|
120
|
+
# This will be replaced with a mutable array if we actually get any errors.
|
121
|
+
all_errors = EMPTY_ARRAY
|
122
|
+
|
123
|
+
validators.each do |validator|
|
124
|
+
validated = as || validator.validated
|
125
|
+
errors = validator.apply(object, context, value)
|
126
|
+
if errors &&
|
127
|
+
(errors.is_a?(Array) && errors != EMPTY_ARRAY) ||
|
128
|
+
(errors.is_a?(String))
|
129
|
+
if all_errors.frozen? # It's empty
|
130
|
+
all_errors = []
|
131
|
+
end
|
132
|
+
interpolation_vars = { validated: validated.graphql_name }
|
133
|
+
if errors.is_a?(String)
|
134
|
+
all_errors << (errors % interpolation_vars)
|
135
|
+
else
|
136
|
+
errors = errors.map { |e| e % interpolation_vars }
|
137
|
+
all_errors.concat(errors)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
if all_errors.any?
|
143
|
+
raise ValidationFailedError.new(errors: all_errors)
|
144
|
+
end
|
145
|
+
nil
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
|
152
|
+
require "graphql/schema/validator/length_validator"
|
153
|
+
GraphQL::Schema::Validator.install(:length, GraphQL::Schema::Validator::LengthValidator)
|
154
|
+
require "graphql/schema/validator/numericality_validator"
|
155
|
+
GraphQL::Schema::Validator.install(:numericality, GraphQL::Schema::Validator::NumericalityValidator)
|
156
|
+
require "graphql/schema/validator/format_validator"
|
157
|
+
GraphQL::Schema::Validator.install(:format, GraphQL::Schema::Validator::FormatValidator)
|
158
|
+
require "graphql/schema/validator/inclusion_validator"
|
159
|
+
GraphQL::Schema::Validator.install(:inclusion, GraphQL::Schema::Validator::InclusionValidator)
|
160
|
+
require "graphql/schema/validator/exclusion_validator"
|
161
|
+
GraphQL::Schema::Validator.install(:exclusion, GraphQL::Schema::Validator::ExclusionValidator)
|
162
|
+
require "graphql/schema/validator/required_validator"
|
163
|
+
GraphQL::Schema::Validator.install(:required, GraphQL::Schema::Validator::RequiredValidator)
|