graphql 1.11.6 → 1.12.3
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/object_generator.rb +2 -0
- 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.rb +39 -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/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 -9
- data/lib/graphql/backwards_compatibility.rb +2 -1
- data/lib/graphql/base_type.rb +1 -1
- 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 +198 -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 +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 +7 -2
- data/lib/graphql/deprecation.rb +13 -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 +10 -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 +219 -117
- 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/internal_representation/document.rb +2 -2
- data/lib/graphql/internal_representation/rewrite.rb +1 -1
- data/lib/graphql/invalid_null_error.rb +1 -1
- data/lib/graphql/language/document_from_schema_definition.rb +50 -23
- 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.rb +6 -1
- data/lib/graphql/query/arguments.rb +1 -1
- data/lib/graphql/query/context.rb +8 -1
- data/lib/graphql/query/serial_execution.rb +1 -0
- data/lib/graphql/query/validation_pipeline.rb +1 -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/type_extensions.rb +2 -0
- data/lib/graphql/scalar_type.rb +2 -0
- data/lib/graphql/schema.rb +80 -29
- data/lib/graphql/schema/argument.rb +25 -7
- data/lib/graphql/schema/build_from_definition.rb +139 -51
- data/lib/graphql/schema/default_type_error.rb +2 -0
- data/lib/graphql/schema/directive.rb +76 -0
- 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 +50 -22
- data/lib/graphql/schema/field/connection_extension.rb +3 -2
- data/lib/graphql/schema/field/scope_extension.rb +1 -1
- data/lib/graphql/schema/input_object.rb +33 -22
- data/lib/graphql/schema/interface.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 +3 -3
- data/lib/graphql/schema/member/has_arguments.rb +67 -50
- 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/middleware_chain.rb +1 -1
- data/lib/graphql/schema/object.rb +11 -0
- data/lib/graphql/schema/printer.rb +5 -4
- data/lib/graphql/schema/relay_classic_mutation.rb +1 -1
- 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 +19 -1
- data/lib/graphql/schema/timeout_middleware.rb +3 -1
- data/lib/graphql/schema/validation.rb +4 -2
- 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/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 +32 -9
- data/lib/graphql/subscriptions.rb +17 -20
- data/lib/graphql/subscriptions/subscription_root.rb +1 -1
- data/lib/graphql/tracing.rb +2 -2
- data/lib/graphql/tracing/appoptics_tracing.rb +3 -1
- 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/relay.rb +11 -3
- data/lib/graphql/types/relay/base_connection.rb +2 -91
- 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/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 +53 -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,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GraphQL
|
4
|
+
class Schema
|
5
|
+
class Member
|
6
|
+
module HasDeprecationReason
|
7
|
+
# @return [String, nil] Explains why this member was deprecated (if present, this will be marked deprecated in introspection)
|
8
|
+
def deprecation_reason
|
9
|
+
dir = self.directives.find { |d| d.is_a?(GraphQL::Schema::Directive::Deprecated) }
|
10
|
+
dir && dir.arguments[:reason]
|
11
|
+
end
|
12
|
+
|
13
|
+
# Set the deprecation reason for this member, or remove it by assigning `nil`
|
14
|
+
# @param text [String, nil]
|
15
|
+
def deprecation_reason=(text)
|
16
|
+
if text.nil?
|
17
|
+
remove_directive(GraphQL::Schema::Directive::Deprecated)
|
18
|
+
else
|
19
|
+
directive(GraphQL::Schema::Directive::Deprecated, reason: text)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GraphQL
|
4
|
+
class Schema
|
5
|
+
class Member
|
6
|
+
module HasDirectives
|
7
|
+
# Create an instance of `dir_class` for `self`, using `options`.
|
8
|
+
#
|
9
|
+
# It removes a previously-attached instance of `dir_class`, if there is one.
|
10
|
+
#
|
11
|
+
# @return [void]
|
12
|
+
def directive(dir_class, **options)
|
13
|
+
@own_directives ||= []
|
14
|
+
remove_directive(dir_class)
|
15
|
+
@own_directives << dir_class.new(self, **options)
|
16
|
+
nil
|
17
|
+
end
|
18
|
+
|
19
|
+
# Remove an attached instance of `dir_class`, if there is one
|
20
|
+
# @param dir_class [Class<GraphQL::Schema::Directive>]
|
21
|
+
# @return [viod]
|
22
|
+
def remove_directive(dir_class)
|
23
|
+
@own_directives && @own_directives.reject! { |d| d.is_a?(dir_class) }
|
24
|
+
nil
|
25
|
+
end
|
26
|
+
|
27
|
+
NO_DIRECTIVES = [].freeze
|
28
|
+
|
29
|
+
def directives
|
30
|
+
case self
|
31
|
+
when Class
|
32
|
+
inherited_directives = if superclass.respond_to?(:directives)
|
33
|
+
superclass.directives
|
34
|
+
else
|
35
|
+
NO_DIRECTIVES
|
36
|
+
end
|
37
|
+
if inherited_directives.any? && @own_directives
|
38
|
+
dirs = []
|
39
|
+
merge_directives(dirs, inherited_directives)
|
40
|
+
merge_directives(dirs, @own_directives)
|
41
|
+
dirs
|
42
|
+
elsif @own_directives
|
43
|
+
@own_directives
|
44
|
+
elsif inherited_directives.any?
|
45
|
+
inherited_directives
|
46
|
+
else
|
47
|
+
NO_DIRECTIVES
|
48
|
+
end
|
49
|
+
when Module
|
50
|
+
dirs = nil
|
51
|
+
self.ancestors.reverse_each do |ancestor|
|
52
|
+
if ancestor.respond_to?(:own_directives) &&
|
53
|
+
(anc_dirs = ancestor.own_directives).any?
|
54
|
+
dirs ||= []
|
55
|
+
merge_directives(dirs, anc_dirs)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
if own_directives
|
59
|
+
dirs ||= []
|
60
|
+
merge_directives(dirs, own_directives)
|
61
|
+
end
|
62
|
+
dirs || NO_DIRECTIVES
|
63
|
+
when HasDirectives
|
64
|
+
@own_directives || NO_DIRECTIVES
|
65
|
+
else
|
66
|
+
raise "Invariant: how could #{self} not be a Class, Module, or instance of HasDirectives?"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
protected
|
71
|
+
|
72
|
+
def own_directives
|
73
|
+
@own_directives
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
# Modify `target` by adding items from `dirs` such that:
|
79
|
+
# - Any name conflict is overriden by the incoming member of `dirs`
|
80
|
+
# - Any other member of `dirs` is appended
|
81
|
+
# @param target [Array<GraphQL::Schema::Directive>]
|
82
|
+
# @param dirs [Array<GraphQL::Schema::Directive>]
|
83
|
+
# @return [void]
|
84
|
+
def merge_directives(target, dirs)
|
85
|
+
dirs.each do |dir|
|
86
|
+
if (idx = target.find_index { |d| d.graphql_name == dir.graphql_name })
|
87
|
+
target.slice!(idx)
|
88
|
+
target.insert(idx, dir)
|
89
|
+
else
|
90
|
+
target << dir
|
91
|
+
end
|
92
|
+
end
|
93
|
+
nil
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module GraphQL
|
3
|
+
class Schema
|
4
|
+
class Member
|
5
|
+
module HasValidators
|
6
|
+
include Schema::FindInheritedValue::EmptyObjects
|
7
|
+
|
8
|
+
# Build {GraphQL::Schema::Validator}s based on the given configuration
|
9
|
+
# and use them for this schema member
|
10
|
+
# @param validation_config [Hash{Symbol => Hash}]
|
11
|
+
# @return [void]
|
12
|
+
def validates(validation_config)
|
13
|
+
new_validators = GraphQL::Schema::Validator.from_config(self, validation_config)
|
14
|
+
@own_validators ||= []
|
15
|
+
@own_validators.concat(new_validators)
|
16
|
+
nil
|
17
|
+
end
|
18
|
+
|
19
|
+
# @return [Array<GraphQL::Schema::Validator>]
|
20
|
+
def validators
|
21
|
+
own_validators = @own_validators || EMPTY_ARRAY
|
22
|
+
if self.is_a?(Class) && superclass.respond_to?(:validators) && (inherited_validators = superclass.validators).any?
|
23
|
+
inherited_validators + own_validators
|
24
|
+
else
|
25
|
+
own_validators
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -71,7 +71,7 @@ module GraphQL
|
|
71
71
|
|
72
72
|
def wrap(callable)
|
73
73
|
if BackwardsCompatibility.get_arity(callable) == 6
|
74
|
-
warn("Middleware that takes a next_middleware parameter is deprecated (#{callable.inspect}); instead, accept a block and use yield.")
|
74
|
+
GraphQL::Deprecation.warn("Middleware that takes a next_middleware parameter is deprecated (#{callable.inspect}); instead, accept a block and use yield.")
|
75
75
|
MiddlewareWrapper.new(callable)
|
76
76
|
else
|
77
77
|
callable
|
@@ -14,6 +14,17 @@ module GraphQL
|
|
14
14
|
# @return [GraphQL::Query::Context] the context instance for this query
|
15
15
|
attr_reader :context
|
16
16
|
|
17
|
+
# @return [GraphQL::Dataloader]
|
18
|
+
def dataloader
|
19
|
+
context.dataloader
|
20
|
+
end
|
21
|
+
|
22
|
+
# Call this in a field method to return a value that should be returned to the client
|
23
|
+
# without any further handling by GraphQL.
|
24
|
+
def raw_value(obj)
|
25
|
+
GraphQL::Execution::Interpreter::RawValue.new(obj)
|
26
|
+
end
|
27
|
+
|
17
28
|
class << self
|
18
29
|
# This is protected so that we can be sure callers use the public method, {.authorized_new}
|
19
30
|
# @see authorized_new to make instances
|
@@ -59,14 +59,15 @@ module GraphQL
|
|
59
59
|
|
60
60
|
# Return the GraphQL schema string for the introspection type system
|
61
61
|
def self.print_introspection_schema
|
62
|
-
query_root =
|
63
|
-
|
62
|
+
query_root = Class.new(GraphQL::Schema::Object) do
|
63
|
+
graphql_name "Root"
|
64
|
+
field :throwaway_field, String, null: true
|
64
65
|
end
|
65
|
-
schema = GraphQL::Schema
|
66
|
+
schema = Class.new(GraphQL::Schema) { query(query_root) }
|
66
67
|
|
67
68
|
introspection_schema_ast = GraphQL::Language::DocumentFromSchemaDefinition.new(
|
68
69
|
schema,
|
69
|
-
except: ->(member, _) { member.
|
70
|
+
except: ->(member, _) { member.graphql_name == "Root" },
|
70
71
|
include_introspection_types: true,
|
71
72
|
include_built_in_directives: true,
|
72
73
|
).document
|
@@ -105,7 +105,7 @@ module GraphQL
|
|
105
105
|
sig = super
|
106
106
|
# Arguments were added at the root, but they should be nested
|
107
107
|
sig[:arguments].clear
|
108
|
-
sig[:arguments][:input] = { type: input_type, required: true }
|
108
|
+
sig[:arguments][:input] = { type: input_type, required: true, description: "Parameters for #{graphql_name}" }
|
109
109
|
sig
|
110
110
|
end
|
111
111
|
|
@@ -24,6 +24,7 @@ module GraphQL
|
|
24
24
|
# Really we only need description from here, but:
|
25
25
|
extend Schema::Member::BaseDSLMethods
|
26
26
|
extend GraphQL::Schema::Member::HasArguments
|
27
|
+
extend GraphQL::Schema::Member::HasValidators
|
27
28
|
include Schema::Member::HasPath
|
28
29
|
extend Schema::Member::HasPath
|
29
30
|
|
@@ -49,6 +50,11 @@ module GraphQL
|
|
49
50
|
# @return [GraphQL::Query::Context]
|
50
51
|
attr_reader :context
|
51
52
|
|
53
|
+
# @return [GraphQL::Dataloader]
|
54
|
+
def dataloader
|
55
|
+
context.dataloader
|
56
|
+
end
|
57
|
+
|
52
58
|
# @return [GraphQL::Schema::Field]
|
53
59
|
attr_reader :field
|
54
60
|
|
@@ -80,6 +86,7 @@ module GraphQL
|
|
80
86
|
load_arguments_val = load_arguments(args)
|
81
87
|
context.schema.after_lazy(load_arguments_val) do |loaded_args|
|
82
88
|
@prepared_arguments = loaded_args
|
89
|
+
Schema::Validator.validate!(self.class.validators, object, context, loaded_args, as: @field)
|
83
90
|
# Then call `authorized?`, which may raise or may return a lazy object
|
84
91
|
authorized_val = if loaded_args.any?
|
85
92
|
authorized?(**loaded_args)
|
@@ -25,6 +25,22 @@ module GraphQL
|
|
25
25
|
@mode = context.query.subscription_update? ? :update : :subscribe
|
26
26
|
end
|
27
27
|
|
28
|
+
def resolve_with_support(**args)
|
29
|
+
result = nil
|
30
|
+
unsubscribed = true
|
31
|
+
catch :graphql_subscription_unsubscribed do
|
32
|
+
result = super
|
33
|
+
unsubscribed = false
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
if unsubscribed
|
38
|
+
context.skip
|
39
|
+
else
|
40
|
+
result
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
28
44
|
# Implement the {Resolve} API
|
29
45
|
def resolve(**args)
|
30
46
|
# Dispatch based on `@mode`, which will raise a `NoMethodError` if we ever
|
@@ -55,7 +71,8 @@ module GraphQL
|
|
55
71
|
def resolve_update(**args)
|
56
72
|
ret_val = args.any? ? update(**args) : update
|
57
73
|
if ret_val == :no_update
|
58
|
-
|
74
|
+
context.namespace(:subscriptions)[:no_update] = true
|
75
|
+
context.skip
|
59
76
|
else
|
60
77
|
ret_val
|
61
78
|
end
|
@@ -80,6 +97,7 @@ module GraphQL
|
|
80
97
|
|
81
98
|
# Call this to halt execution and remove this subscription from the system
|
82
99
|
def unsubscribe
|
100
|
+
context.namespace(:subscriptions)[:unsubscribed] = true
|
83
101
|
throw :graphql_subscription_unsubscribed
|
84
102
|
end
|
85
103
|
|
@@ -23,12 +23,14 @@ module GraphQL
|
|
23
23
|
# Bugsnag.notify(timeout_error, {query_string: query_ctx.query.query_string})
|
24
24
|
# end
|
25
25
|
#
|
26
|
+
# @api deprecated
|
27
|
+
# @see Schema::Timeout
|
26
28
|
class TimeoutMiddleware
|
27
29
|
# @param max_seconds [Numeric] how many seconds the query should be allowed to resolve new fields
|
28
30
|
def initialize(max_seconds:, context_key: nil, &block)
|
29
31
|
@max_seconds = max_seconds
|
30
32
|
if context_key
|
31
|
-
warn("TimeoutMiddleware's `context_key` is ignored, timeout data is now stored in isolated storage")
|
33
|
+
GraphQL::Deprecation.warn("TimeoutMiddleware's `context_key` is ignored, timeout data is now stored in isolated storage")
|
32
34
|
end
|
33
35
|
@error_handler = block
|
34
36
|
end
|
@@ -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`
|
@@ -201,7 +203,7 @@ module GraphQL
|
|
201
203
|
RESERVED_TYPE_NAME = ->(type) {
|
202
204
|
if type.name.start_with?('__') && !type.introspection?
|
203
205
|
# TODO: make this a hard failure in a later version
|
204
|
-
warn("Name #{type.name.inspect} must not begin with \"__\", which is reserved by GraphQL introspection.")
|
206
|
+
GraphQL::Deprecation.warn("Name #{type.name.inspect} must not begin with \"__\", which is reserved by GraphQL introspection.")
|
205
207
|
nil
|
206
208
|
else
|
207
209
|
# ok name
|
@@ -211,7 +213,7 @@ module GraphQL
|
|
211
213
|
RESERVED_NAME = ->(named_thing) {
|
212
214
|
if named_thing.name.start_with?('__')
|
213
215
|
# TODO: make this a hard failure in a later version
|
214
|
-
warn("Name #{named_thing.name.inspect} must not begin with \"__\", which is reserved by GraphQL introspection.")
|
216
|
+
GraphQL::Deprecation.warn("Name #{named_thing.name.inspect} must not begin with \"__\", which is reserved by GraphQL introspection.")
|
215
217
|
nil
|
216
218
|
else
|
217
219
|
# no worries
|
@@ -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)
|