graphql 2.0.13 → 2.3.10
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/mutation_root_generator.rb +2 -2
- data/lib/generators/graphql/install/templates/base_mutation.erb +2 -0
- data/lib/generators/graphql/install/templates/mutation_type.erb +2 -0
- data/lib/generators/graphql/install_generator.rb +3 -0
- data/lib/generators/graphql/mutation_delete_generator.rb +1 -1
- data/lib/generators/graphql/mutation_update_generator.rb +1 -1
- data/lib/generators/graphql/relay.rb +18 -1
- data/lib/generators/graphql/templates/base_argument.erb +2 -0
- data/lib/generators/graphql/templates/base_connection.erb +2 -0
- data/lib/generators/graphql/templates/base_edge.erb +2 -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_object.erb +2 -0
- data/lib/generators/graphql/templates/base_resolver.erb +6 -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/graphql_controller.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/node_type.erb +2 -0
- data/lib/generators/graphql/templates/query_type.erb +2 -0
- data/lib/generators/graphql/templates/schema.erb +8 -0
- data/lib/graphql/analysis/analyzer.rb +89 -0
- data/lib/graphql/analysis/field_usage.rb +82 -0
- data/lib/graphql/analysis/max_query_complexity.rb +20 -0
- data/lib/graphql/analysis/max_query_depth.rb +20 -0
- data/lib/graphql/analysis/query_complexity.rb +183 -0
- data/lib/graphql/analysis/query_depth.rb +58 -0
- data/lib/graphql/analysis/visitor.rb +283 -0
- data/lib/graphql/analysis.rb +92 -1
- data/lib/graphql/backtrace/inspect_result.rb +0 -12
- data/lib/graphql/backtrace/table.rb +2 -2
- data/lib/graphql/backtrace/trace.rb +93 -0
- data/lib/graphql/backtrace/tracer.rb +1 -1
- data/lib/graphql/backtrace.rb +2 -1
- data/lib/graphql/coercion_error.rb +1 -9
- data/lib/graphql/dataloader/async_dataloader.rb +88 -0
- data/lib/graphql/dataloader/null_dataloader.rb +1 -1
- data/lib/graphql/dataloader/request.rb +5 -0
- data/lib/graphql/dataloader/source.rb +89 -45
- data/lib/graphql/dataloader.rb +115 -142
- data/lib/graphql/duration_encoding_error.rb +16 -0
- data/lib/graphql/execution/interpreter/argument_value.rb +5 -1
- data/lib/graphql/execution/interpreter/arguments.rb +1 -1
- data/lib/graphql/execution/interpreter/arguments_cache.rb +33 -33
- data/lib/graphql/execution/interpreter/resolve.rb +19 -0
- data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +175 -0
- data/lib/graphql/execution/interpreter/runtime.rb +331 -455
- data/lib/graphql/execution/interpreter.rb +125 -61
- data/lib/graphql/execution/lazy.rb +6 -12
- data/lib/graphql/execution/lookahead.rb +124 -46
- data/lib/graphql/execution/multiplex.rb +3 -117
- data/lib/graphql/execution.rb +0 -1
- data/lib/graphql/introspection/directive_type.rb +3 -3
- data/lib/graphql/introspection/dynamic_fields.rb +1 -1
- data/lib/graphql/introspection/entry_points.rb +11 -5
- data/lib/graphql/introspection/field_type.rb +2 -2
- data/lib/graphql/introspection/schema_type.rb +10 -13
- data/lib/graphql/introspection/type_type.rb +17 -10
- data/lib/graphql/introspection.rb +3 -2
- data/lib/graphql/language/block_string.rb +34 -18
- data/lib/graphql/language/definition_slice.rb +1 -1
- data/lib/graphql/language/document_from_schema_definition.rb +75 -59
- data/lib/graphql/language/lexer.rb +358 -1506
- data/lib/graphql/language/nodes.rb +166 -93
- data/lib/graphql/language/parser.rb +795 -1953
- data/lib/graphql/language/printer.rb +340 -160
- data/lib/graphql/language/sanitized_printer.rb +21 -23
- data/lib/graphql/language/static_visitor.rb +167 -0
- data/lib/graphql/language/visitor.rb +188 -141
- data/lib/graphql/language.rb +61 -1
- data/lib/graphql/load_application_object_failed_error.rb +5 -1
- data/lib/graphql/pagination/active_record_relation_connection.rb +0 -8
- data/lib/graphql/pagination/array_connection.rb +6 -6
- data/lib/graphql/pagination/connection.rb +33 -6
- data/lib/graphql/pagination/mongoid_relation_connection.rb +1 -2
- data/lib/graphql/query/context/scoped_context.rb +101 -0
- data/lib/graphql/query/context.rb +117 -112
- data/lib/graphql/query/null_context.rb +12 -25
- data/lib/graphql/query/validation_pipeline.rb +6 -5
- data/lib/graphql/query/variables.rb +3 -3
- data/lib/graphql/query.rb +86 -30
- data/lib/graphql/railtie.rb +9 -6
- data/lib/graphql/rake_task.rb +29 -11
- data/lib/graphql/rubocop/graphql/base_cop.rb +1 -1
- data/lib/graphql/schema/addition.rb +59 -23
- data/lib/graphql/schema/always_visible.rb +11 -0
- data/lib/graphql/schema/argument.rb +55 -26
- data/lib/graphql/schema/base_64_encoder.rb +3 -5
- data/lib/graphql/schema/build_from_definition.rb +56 -32
- data/lib/graphql/schema/directive/one_of.rb +24 -0
- data/lib/graphql/schema/directive/specified_by.rb +14 -0
- data/lib/graphql/schema/directive/transform.rb +1 -1
- data/lib/graphql/schema/directive.rb +15 -3
- data/lib/graphql/schema/enum.rb +35 -24
- data/lib/graphql/schema/enum_value.rb +2 -3
- data/lib/graphql/schema/field/connection_extension.rb +2 -16
- data/lib/graphql/schema/field/scope_extension.rb +8 -1
- data/lib/graphql/schema/field.rb +147 -107
- data/lib/graphql/schema/field_extension.rb +1 -4
- data/lib/graphql/schema/find_inherited_value.rb +2 -7
- data/lib/graphql/schema/has_single_input_argument.rb +158 -0
- data/lib/graphql/schema/input_object.rb +47 -11
- data/lib/graphql/schema/interface.rb +15 -21
- data/lib/graphql/schema/introspection_system.rb +7 -17
- data/lib/graphql/schema/late_bound_type.rb +10 -0
- data/lib/graphql/schema/list.rb +2 -2
- data/lib/graphql/schema/loader.rb +2 -3
- data/lib/graphql/schema/member/base_dsl_methods.rb +18 -14
- data/lib/graphql/schema/member/build_type.rb +11 -3
- data/lib/graphql/schema/member/has_arguments.rb +170 -130
- data/lib/graphql/schema/member/has_ast_node.rb +12 -0
- data/lib/graphql/schema/member/has_deprecation_reason.rb +3 -4
- data/lib/graphql/schema/member/has_directives.rb +81 -61
- data/lib/graphql/schema/member/has_fields.rb +100 -38
- data/lib/graphql/schema/member/has_interfaces.rb +65 -10
- data/lib/graphql/schema/member/has_unresolved_type_error.rb +5 -1
- data/lib/graphql/schema/member/has_validators.rb +32 -6
- data/lib/graphql/schema/member/relay_shortcuts.rb +19 -0
- data/lib/graphql/schema/member/scoped.rb +19 -0
- data/lib/graphql/schema/member/type_system_helpers.rb +16 -0
- data/lib/graphql/schema/member/validates_input.rb +3 -3
- data/lib/graphql/schema/mutation.rb +7 -0
- data/lib/graphql/schema/object.rb +16 -5
- data/lib/graphql/schema/printer.rb +11 -8
- data/lib/graphql/schema/relay_classic_mutation.rb +7 -129
- data/lib/graphql/schema/resolver/has_payload_type.rb +9 -9
- data/lib/graphql/schema/resolver.rb +47 -32
- data/lib/graphql/schema/scalar.rb +3 -3
- data/lib/graphql/schema/subscription.rb +11 -4
- data/lib/graphql/schema/subset.rb +397 -0
- data/lib/graphql/schema/timeout.rb +25 -29
- data/lib/graphql/schema/type_expression.rb +2 -2
- data/lib/graphql/schema/type_membership.rb +3 -0
- data/lib/graphql/schema/union.rb +11 -2
- data/lib/graphql/schema/unique_within_type.rb +1 -1
- data/lib/graphql/schema/validator/all_validator.rb +60 -0
- data/lib/graphql/schema/validator.rb +4 -2
- data/lib/graphql/schema/warden.rb +238 -93
- data/lib/graphql/schema.rb +498 -103
- data/lib/graphql/static_validation/all_rules.rb +2 -1
- data/lib/graphql/static_validation/base_visitor.rb +7 -6
- data/lib/graphql/static_validation/definition_dependencies.rb +7 -1
- data/lib/graphql/static_validation/literal_validator.rb +24 -7
- data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +1 -1
- data/lib/graphql/static_validation/rules/arguments_are_defined.rb +1 -1
- data/lib/graphql/static_validation/rules/directives_are_defined.rb +1 -2
- data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +1 -1
- data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +12 -4
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +10 -10
- data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +3 -3
- data/lib/graphql/static_validation/rules/fragment_types_exist.rb +1 -1
- data/lib/graphql/static_validation/rules/fragments_are_on_composite_types.rb +1 -1
- data/lib/graphql/static_validation/rules/mutation_root_exists.rb +1 -1
- data/lib/graphql/static_validation/rules/one_of_input_objects_are_valid.rb +66 -0
- data/lib/graphql/static_validation/rules/one_of_input_objects_are_valid_error.rb +29 -0
- data/lib/graphql/static_validation/rules/query_root_exists.rb +1 -1
- data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +4 -4
- data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +5 -5
- data/lib/graphql/static_validation/rules/subscription_root_exists.rb +1 -1
- data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +18 -27
- data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +1 -1
- data/lib/graphql/static_validation/rules/variables_are_input_types.rb +1 -1
- data/lib/graphql/static_validation/validation_context.rb +5 -5
- data/lib/graphql/static_validation/validator.rb +4 -1
- data/lib/graphql/static_validation.rb +0 -1
- data/lib/graphql/subscriptions/action_cable_subscriptions.rb +11 -4
- data/lib/graphql/subscriptions/broadcast_analyzer.rb +11 -5
- data/lib/graphql/subscriptions/event.rb +11 -10
- data/lib/graphql/subscriptions/serialize.rb +2 -0
- data/lib/graphql/subscriptions.rb +20 -13
- data/lib/graphql/testing/helpers.rb +151 -0
- data/lib/graphql/testing.rb +2 -0
- data/lib/graphql/tracing/active_support_notifications_trace.rb +16 -0
- data/lib/graphql/tracing/appoptics_trace.rb +251 -0
- data/lib/graphql/tracing/appoptics_tracing.rb +2 -2
- data/lib/graphql/tracing/appsignal_trace.rb +77 -0
- data/lib/graphql/tracing/data_dog_trace.rb +183 -0
- data/lib/graphql/tracing/data_dog_tracing.rb +9 -21
- data/lib/graphql/{execution/instrumentation.rb → tracing/legacy_hooks_trace.rb} +10 -28
- data/lib/graphql/tracing/legacy_trace.rb +69 -0
- data/lib/graphql/tracing/new_relic_trace.rb +75 -0
- data/lib/graphql/tracing/notifications_trace.rb +45 -0
- data/lib/graphql/tracing/platform_trace.rb +118 -0
- data/lib/graphql/tracing/platform_tracing.rb +17 -3
- data/lib/graphql/tracing/{prometheus_tracing → prometheus_trace}/graphql_collector.rb +4 -2
- data/lib/graphql/tracing/prometheus_trace.rb +89 -0
- data/lib/graphql/tracing/prometheus_tracing.rb +3 -3
- data/lib/graphql/tracing/scout_trace.rb +72 -0
- data/lib/graphql/tracing/sentry_trace.rb +112 -0
- data/lib/graphql/tracing/statsd_trace.rb +56 -0
- data/lib/graphql/tracing/trace.rb +76 -0
- data/lib/graphql/tracing.rb +20 -40
- data/lib/graphql/type_kinds.rb +7 -4
- data/lib/graphql/types/iso_8601_duration.rb +77 -0
- data/lib/graphql/types/relay/base_connection.rb +1 -1
- data/lib/graphql/types/relay/connection_behaviors.rb +68 -6
- data/lib/graphql/types/relay/edge_behaviors.rb +33 -5
- data/lib/graphql/types/relay/node_behaviors.rb +8 -2
- data/lib/graphql/types/relay/page_info_behaviors.rb +11 -2
- data/lib/graphql/types/relay.rb +0 -1
- data/lib/graphql/types/string.rb +1 -1
- data/lib/graphql/types.rb +1 -0
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +27 -20
- data/readme.md +13 -3
- metadata +96 -47
- data/lib/graphql/analysis/ast/analyzer.rb +0 -84
- data/lib/graphql/analysis/ast/field_usage.rb +0 -57
- data/lib/graphql/analysis/ast/max_query_complexity.rb +0 -22
- data/lib/graphql/analysis/ast/max_query_depth.rb +0 -22
- data/lib/graphql/analysis/ast/query_complexity.rb +0 -230
- data/lib/graphql/analysis/ast/query_depth.rb +0 -55
- data/lib/graphql/analysis/ast/visitor.rb +0 -269
- data/lib/graphql/analysis/ast.rb +0 -81
- data/lib/graphql/deprecation.rb +0 -9
- data/lib/graphql/filter.rb +0 -53
- data/lib/graphql/language/lexer.rl +0 -280
- data/lib/graphql/language/parser.y +0 -554
- data/lib/graphql/language/token.rb +0 -34
- data/lib/graphql/schema/base_64_bp.rb +0 -26
- data/lib/graphql/schema/invalid_type_error.rb +0 -7
- data/lib/graphql/static_validation/type_stack.rb +0 -216
- data/lib/graphql/subscriptions/instrumentation.rb +0 -28
- data/lib/graphql/types/relay/default_relay.rb +0 -21
@@ -3,7 +3,7 @@ module GraphQL
|
|
3
3
|
module StaticValidation
|
4
4
|
# Default rules for {GraphQL::StaticValidation::Validator}
|
5
5
|
#
|
6
|
-
# Order is important here. Some validators
|
6
|
+
# Order is important here. Some validators skip later hooks.
|
7
7
|
# which stops the visit on that node. That way it doesn't try to find fields on types that
|
8
8
|
# don't exist, etc.
|
9
9
|
ALL_RULES = [
|
@@ -36,6 +36,7 @@ module GraphQL
|
|
36
36
|
GraphQL::StaticValidation::QueryRootExists,
|
37
37
|
GraphQL::StaticValidation::SubscriptionRootExists,
|
38
38
|
GraphQL::StaticValidation::InputObjectNamesAreUnique,
|
39
|
+
GraphQL::StaticValidation::OneOfInputObjectsAreValid,
|
39
40
|
]
|
40
41
|
end
|
41
42
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
module GraphQL
|
3
3
|
module StaticValidation
|
4
|
-
class BaseVisitor < GraphQL::Language::
|
4
|
+
class BaseVisitor < GraphQL::Language::StaticVisitor
|
5
5
|
def initialize(document, context)
|
6
6
|
@path = []
|
7
7
|
@object_types = []
|
@@ -10,6 +10,7 @@ module GraphQL
|
|
10
10
|
@argument_definitions = []
|
11
11
|
@directive_definitions = []
|
12
12
|
@context = context
|
13
|
+
@types = context.query.types
|
13
14
|
@schema = context.schema
|
14
15
|
super(document)
|
15
16
|
end
|
@@ -77,7 +78,7 @@ module GraphQL
|
|
77
78
|
|
78
79
|
def on_field(node, parent)
|
79
80
|
parent_type = @object_types.last
|
80
|
-
field_definition = @
|
81
|
+
field_definition = @types.field(parent_type, node.name)
|
81
82
|
@field_definitions.push(field_definition)
|
82
83
|
if !field_definition.nil?
|
83
84
|
next_object_type = field_definition.type.unwrap
|
@@ -103,14 +104,14 @@ module GraphQL
|
|
103
104
|
argument_defn = if (arg = @argument_definitions.last)
|
104
105
|
arg_type = arg.type.unwrap
|
105
106
|
if arg_type.kind.input_object?
|
106
|
-
@
|
107
|
+
@types.argument(arg_type, node.name)
|
107
108
|
else
|
108
109
|
nil
|
109
110
|
end
|
110
111
|
elsif (directive_defn = @directive_definitions.last)
|
111
|
-
@
|
112
|
+
@types.argument(directive_defn, node.name)
|
112
113
|
elsif (field_defn = @field_definitions.last)
|
113
|
-
@
|
114
|
+
@types.argument(field_defn, node.name)
|
114
115
|
else
|
115
116
|
nil
|
116
117
|
end
|
@@ -170,7 +171,7 @@ module GraphQL
|
|
170
171
|
|
171
172
|
def on_fragment_with_type(node)
|
172
173
|
object_type = if node.type
|
173
|
-
@
|
174
|
+
@types.type(node.type.name)
|
174
175
|
else
|
175
176
|
@object_types.last
|
176
177
|
end
|
@@ -127,8 +127,14 @@ module GraphQL
|
|
127
127
|
# same name as if they were the same name. If _any_ of the fragments
|
128
128
|
# with that name has a dependency, we record it.
|
129
129
|
independent_fragment_nodes = @defdep_fragment_definitions.values.flatten - @defdep_immediate_dependencies.keys
|
130
|
-
|
130
|
+
visited_fragment_names = Set.new
|
131
131
|
while fragment_node = independent_fragment_nodes.pop
|
132
|
+
if visited_fragment_names.add?(fragment_node.name)
|
133
|
+
# this is a new fragment name
|
134
|
+
else
|
135
|
+
# this is a duplicate fragment name
|
136
|
+
next
|
137
|
+
end
|
132
138
|
loops += 1
|
133
139
|
if loops > max_loops
|
134
140
|
raise("Resolution loops exceeded the number of definitions; infinite loop detected. (Max: #{max_loops}, Current: #{loops})")
|
@@ -5,7 +5,7 @@ module GraphQL
|
|
5
5
|
class LiteralValidator
|
6
6
|
def initialize(context:)
|
7
7
|
@context = context
|
8
|
-
@
|
8
|
+
@types = context.types
|
9
9
|
@invalid_response = GraphQL::Query::InputValidationResult.new(valid: false, problems: [])
|
10
10
|
@valid_response = GraphQL::Query::InputValidationResult.new(valid: true, problems: [])
|
11
11
|
end
|
@@ -18,6 +18,19 @@ module GraphQL
|
|
18
18
|
|
19
19
|
private
|
20
20
|
|
21
|
+
def replace_nulls_in(ast_value)
|
22
|
+
case ast_value
|
23
|
+
when Array
|
24
|
+
ast_value.map { |v| replace_nulls_in(v) }
|
25
|
+
when GraphQL::Language::Nodes::InputObject
|
26
|
+
ast_value.to_h
|
27
|
+
when GraphQL::Language::Nodes::NullValue
|
28
|
+
nil
|
29
|
+
else
|
30
|
+
ast_value
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
21
34
|
def recursively_validate(ast_value, type)
|
22
35
|
if type.nil?
|
23
36
|
# this means we're an undefined argument, see #present_input_field_values_are_valid
|
@@ -42,7 +55,8 @@ module GraphQL
|
|
42
55
|
@valid_response
|
43
56
|
elsif type.kind.scalar? && constant_scalar?(ast_value)
|
44
57
|
maybe_raise_if_invalid(ast_value) do
|
45
|
-
|
58
|
+
ruby_value = replace_nulls_in(ast_value)
|
59
|
+
type.validate_input(ruby_value, @context)
|
46
60
|
end
|
47
61
|
elsif type.kind.enum?
|
48
62
|
maybe_raise_if_invalid(ast_value) do
|
@@ -95,9 +109,9 @@ module GraphQL
|
|
95
109
|
def required_input_fields_are_present(type, ast_node)
|
96
110
|
# TODO - would be nice to use these to create an error message so the caller knows
|
97
111
|
# that required fields are missing
|
98
|
-
required_field_names = @
|
99
|
-
.select { |argument| argument.type.kind.non_null? &&
|
100
|
-
.map(&:name)
|
112
|
+
required_field_names = @types.arguments(type)
|
113
|
+
.select { |argument| argument.type.kind.non_null? && !argument.default_value? }
|
114
|
+
.map!(&:name)
|
101
115
|
|
102
116
|
present_field_names = ast_node.arguments.map(&:name)
|
103
117
|
missing_required_field_names = required_field_names - present_field_names
|
@@ -105,16 +119,19 @@ module GraphQL
|
|
105
119
|
missing_required_field_names.empty? ? @valid_response : @invalid_response
|
106
120
|
else
|
107
121
|
results = missing_required_field_names.map do |name|
|
108
|
-
arg_type = @
|
122
|
+
arg_type = @types.argument(type, name).type
|
109
123
|
recursively_validate(GraphQL::Language::Nodes::NullValue.new(name: name), arg_type)
|
110
124
|
end
|
125
|
+
if type.one_of? && ast_node.arguments.size != 1
|
126
|
+
results << Query::InputValidationResult.from_problem("`#{type.graphql_name}` is a OneOf type, so only one argument may be given (instead of #{ast_node.arguments.size})")
|
127
|
+
end
|
111
128
|
merge_results(results)
|
112
129
|
end
|
113
130
|
end
|
114
131
|
|
115
132
|
def present_input_field_values_are_valid(type, ast_node)
|
116
133
|
results = ast_node.arguments.map do |value|
|
117
|
-
field = @
|
134
|
+
field = @types.argument(type, value.name)
|
118
135
|
# we want to call validate on an argument even if it's an invalid one
|
119
136
|
# so that our raise exception is on it instead of the entire InputObject
|
120
137
|
field_type = field && field.type
|
@@ -15,7 +15,7 @@ module GraphQL
|
|
15
15
|
if @context.schema.error_bubbling || context.errors.none? { |err| err.path.take(@path.size) == @path }
|
16
16
|
parent_defn = parent_definition(parent)
|
17
17
|
|
18
|
-
if parent_defn && (arg_defn =
|
18
|
+
if parent_defn && (arg_defn = @types.argument(parent_defn, node.name))
|
19
19
|
validation_result = context.validate_literal(node.value, arg_defn.type)
|
20
20
|
if !validation_result.valid?
|
21
21
|
kind_of_node = node_type(parent)
|
@@ -5,7 +5,7 @@ module GraphQL
|
|
5
5
|
def on_argument(node, parent)
|
6
6
|
parent_defn = parent_definition(parent)
|
7
7
|
|
8
|
-
if parent_defn &&
|
8
|
+
if parent_defn && @types.argument(parent_defn, node.name)
|
9
9
|
super
|
10
10
|
elsif parent_defn
|
11
11
|
kind_of_node = node_type(parent)
|
@@ -4,11 +4,10 @@ module GraphQL
|
|
4
4
|
module DirectivesAreDefined
|
5
5
|
def initialize(*)
|
6
6
|
super
|
7
|
-
@directive_names = context.warden.directives.map(&:graphql_name)
|
8
7
|
end
|
9
8
|
|
10
9
|
def on_directive(node, parent)
|
11
|
-
if !@
|
10
|
+
if !@types.directive_exists?(node.name)
|
12
11
|
@directives_are_defined_errors_by_name ||= {}
|
13
12
|
error = @directives_are_defined_errors_by_name[node.name] ||= begin
|
14
13
|
err = GraphQL::StaticValidation::DirectivesAreDefinedError.new(
|
@@ -4,7 +4,7 @@ module GraphQL
|
|
4
4
|
module FieldsAreDefinedOnType
|
5
5
|
def on_field(node, parent)
|
6
6
|
parent_type = @object_types[-2]
|
7
|
-
field = context.
|
7
|
+
field = context.query.types.field(parent_type, node.name)
|
8
8
|
|
9
9
|
if field.nil?
|
10
10
|
if parent_type.kind.union?
|
@@ -26,11 +26,19 @@ module GraphQL
|
|
26
26
|
msg = if resolved_type.nil?
|
27
27
|
nil
|
28
28
|
elsif resolved_type.kind.scalar? && ast_node.selections.any?
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
29
|
+
selection_strs = ast_node.selections.map do |n|
|
30
|
+
case n
|
31
|
+
when GraphQL::Language::Nodes::InlineFragment
|
32
|
+
"\"... on #{n.type.name} { ... }\""
|
33
|
+
when GraphQL::Language::Nodes::Field
|
34
|
+
"\"#{n.name}\""
|
35
|
+
when GraphQL::Language::Nodes::FragmentSpread
|
36
|
+
"\"#{n.name}\""
|
37
|
+
else
|
38
|
+
raise "Invariant: unexpected selection node: #{n}"
|
39
|
+
end
|
33
40
|
end
|
41
|
+
"Selections can't be made on scalars (%{node_name} returns #{resolved_type.graphql_name} but has selections [#{selection_strs.join(", ")}])"
|
34
42
|
elsif resolved_type.kind.fields? && ast_node.selections.empty?
|
35
43
|
"Field must have selections (%{node_name} returns #{resolved_type.graphql_name} but has no selections. Did you mean '#{ast_node.name} { ... }'?)"
|
36
44
|
else
|
@@ -9,7 +9,7 @@ module GraphQL
|
|
9
9
|
# without ambiguity.
|
10
10
|
#
|
11
11
|
# Original Algorithm: https://github.com/graphql/graphql-js/blob/master/src/validation/rules/OverlappingFieldsCanBeMerged.js
|
12
|
-
NO_ARGS =
|
12
|
+
NO_ARGS = GraphQL::EmptyObjects::EMPTY_HASH
|
13
13
|
|
14
14
|
Field = Struct.new(:node, :definition, :owner_type, :parents)
|
15
15
|
FragmentSpread = Struct.new(:name, :parents)
|
@@ -117,8 +117,8 @@ module GraphQL
|
|
117
117
|
|
118
118
|
return if fragment1.nil? || fragment2.nil?
|
119
119
|
|
120
|
-
fragment_type1 = context.
|
121
|
-
fragment_type2 = context.
|
120
|
+
fragment_type1 = context.query.types.type(fragment1.type.name)
|
121
|
+
fragment_type2 = context.query.types.type(fragment2.type.name)
|
122
122
|
|
123
123
|
return if fragment_type1.nil? || fragment_type2.nil?
|
124
124
|
|
@@ -170,7 +170,7 @@ module GraphQL
|
|
170
170
|
fragment = context.fragments[fragment_name]
|
171
171
|
return if fragment.nil?
|
172
172
|
|
173
|
-
fragment_type =
|
173
|
+
fragment_type = @types.type(fragment.type.name)
|
174
174
|
return if fragment_type.nil?
|
175
175
|
|
176
176
|
fragment_fields, fragment_spreads = fields_and_fragments_from_selection(fragment, owner_type: fragment_type, parents: [*fragment_spread.parents, fragment_type])
|
@@ -323,7 +323,7 @@ module GraphQL
|
|
323
323
|
end
|
324
324
|
end
|
325
325
|
|
326
|
-
NO_SELECTIONS = [
|
326
|
+
NO_SELECTIONS = [GraphQL::EmptyObjects::EMPTY_HASH, GraphQL::EmptyObjects::EMPTY_ARRAY].freeze
|
327
327
|
|
328
328
|
def fields_and_fragments_from_selection(node, owner_type:, parents:)
|
329
329
|
if node.selections.empty?
|
@@ -340,10 +340,10 @@ module GraphQL
|
|
340
340
|
selections.each do |node|
|
341
341
|
case node
|
342
342
|
when GraphQL::Language::Nodes::Field
|
343
|
-
definition =
|
343
|
+
definition = @types.field(owner_type, node.name)
|
344
344
|
fields << Field.new(node, definition, owner_type, parents)
|
345
345
|
when GraphQL::Language::Nodes::InlineFragment
|
346
|
-
fragment_type = node.type ?
|
346
|
+
fragment_type = node.type ? @types.type(node.type.name) : owner_type
|
347
347
|
find_fields_and_fragments(node.selections, parents: [*parents, fragment_type], owner_type: owner_type, fields: fields, fragment_spreads: fragment_spreads) if fragment_type
|
348
348
|
when GraphQL::Language::Nodes::FragmentSpread
|
349
349
|
fragment_spreads << FragmentSpread.new(node.name, parents)
|
@@ -396,7 +396,7 @@ module GraphQL
|
|
396
396
|
end
|
397
397
|
|
398
398
|
# Given two list of parents, find out if they are mutually exclusive
|
399
|
-
# In this context, `parents`
|
399
|
+
# In this context, `parents` represents the "self scope" of the field,
|
400
400
|
# what types may be found at this point in the query.
|
401
401
|
def mutually_exclusive?(parents1, parents2)
|
402
402
|
if parents1.empty? || parents2.empty?
|
@@ -411,8 +411,8 @@ module GraphQL
|
|
411
411
|
false
|
412
412
|
else
|
413
413
|
# Check if these two scopes have _any_ types in common.
|
414
|
-
possible_right_types = context.
|
415
|
-
possible_left_types = context.
|
414
|
+
possible_right_types = context.types.possible_types(type1)
|
415
|
+
possible_left_types = context.types.possible_types(type2)
|
416
416
|
(possible_right_types & possible_left_types).empty?
|
417
417
|
end
|
418
418
|
end
|
@@ -28,7 +28,7 @@ module GraphQL
|
|
28
28
|
frag_node = context.fragments[frag_spread.node.name]
|
29
29
|
if frag_node
|
30
30
|
fragment_child_name = frag_node.type.name
|
31
|
-
fragment_child =
|
31
|
+
fragment_child = @types.type(fragment_child_name)
|
32
32
|
# Might be non-existent type name
|
33
33
|
if fragment_child
|
34
34
|
validate_fragment_in_scope(frag_spread.parent_type, fragment_child, frag_spread.node, context, frag_spread.path)
|
@@ -44,8 +44,8 @@ module GraphQL
|
|
44
44
|
# It's not a valid fragment type, this error was handled someplace else
|
45
45
|
return
|
46
46
|
end
|
47
|
-
parent_types =
|
48
|
-
child_types =
|
47
|
+
parent_types = @types.possible_types(parent_type.unwrap)
|
48
|
+
child_types = @types.possible_types(child_type.unwrap)
|
49
49
|
|
50
50
|
if child_types.none? { |c| parent_types.include?(c) }
|
51
51
|
name = node.respond_to?(:name) ? " #{node.name}" : ""
|
@@ -21,7 +21,7 @@ module GraphQL
|
|
21
21
|
true
|
22
22
|
else
|
23
23
|
type_name = fragment_node.type.name
|
24
|
-
type =
|
24
|
+
type = @types.type(type_name)
|
25
25
|
if type.nil?
|
26
26
|
add_error(GraphQL::StaticValidation::FragmentTypesExistError.new(
|
27
27
|
"No such type #{type_name}, so it can't be a fragment condition",
|
@@ -19,7 +19,7 @@ module GraphQL
|
|
19
19
|
true
|
20
20
|
else
|
21
21
|
type_name = node_type.to_query_string
|
22
|
-
type_def =
|
22
|
+
type_def = @types.type(type_name)
|
23
23
|
if type_def.nil? || !type_def.kind.composite?
|
24
24
|
add_error(GraphQL::StaticValidation::FragmentsAreOnCompositeTypesError.new(
|
25
25
|
"Invalid fragment on type #{type_name} (must be Union, Interface or Object)",
|
@@ -3,7 +3,7 @@ module GraphQL
|
|
3
3
|
module StaticValidation
|
4
4
|
module MutationRootExists
|
5
5
|
def on_operation_definition(node, _parent)
|
6
|
-
if node.operation_type == 'mutation' && context.
|
6
|
+
if node.operation_type == 'mutation' && context.query.types.mutation_root.nil?
|
7
7
|
add_error(GraphQL::StaticValidation::MutationRootExistsError.new(
|
8
8
|
'Schema is not configured for mutations',
|
9
9
|
nodes: node
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module GraphQL
|
3
|
+
module StaticValidation
|
4
|
+
module OneOfInputObjectsAreValid
|
5
|
+
def on_input_object(node, parent)
|
6
|
+
return super unless parent.is_a?(GraphQL::Language::Nodes::Argument)
|
7
|
+
|
8
|
+
parent_type = get_parent_type(context, parent)
|
9
|
+
return super unless parent_type && parent_type.kind.input_object? && parent_type.one_of?
|
10
|
+
|
11
|
+
validate_one_of_input_object(node, context, parent_type)
|
12
|
+
super
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def validate_one_of_input_object(ast_node, context, parent_type)
|
18
|
+
present_fields = ast_node.arguments.map(&:name)
|
19
|
+
input_object_type = parent_type.to_type_signature
|
20
|
+
|
21
|
+
if present_fields.count != 1
|
22
|
+
add_error(
|
23
|
+
OneOfInputObjectsAreValidError.new(
|
24
|
+
"OneOf Input Object '#{input_object_type}' must specify exactly one key.",
|
25
|
+
path: context.path,
|
26
|
+
nodes: ast_node,
|
27
|
+
input_object_type: input_object_type
|
28
|
+
)
|
29
|
+
)
|
30
|
+
return
|
31
|
+
end
|
32
|
+
|
33
|
+
field = present_fields.first
|
34
|
+
value = ast_node.arguments.first.value
|
35
|
+
|
36
|
+
if value.is_a?(GraphQL::Language::Nodes::NullValue)
|
37
|
+
add_error(
|
38
|
+
OneOfInputObjectsAreValidError.new(
|
39
|
+
"Argument '#{input_object_type}.#{field}' must be non-null.",
|
40
|
+
path: [*context.path, field],
|
41
|
+
nodes: ast_node.arguments.first,
|
42
|
+
input_object_type: input_object_type
|
43
|
+
)
|
44
|
+
)
|
45
|
+
return
|
46
|
+
end
|
47
|
+
|
48
|
+
if value.is_a?(GraphQL::Language::Nodes::VariableIdentifier)
|
49
|
+
variable_name = value.name
|
50
|
+
variable_type = @declared_variables[variable_name].type
|
51
|
+
|
52
|
+
unless variable_type.is_a?(GraphQL::Language::Nodes::NonNullType)
|
53
|
+
add_error(
|
54
|
+
OneOfInputObjectsAreValidError.new(
|
55
|
+
"Variable '#{variable_name}' must be non-nullable to be used for OneOf Input Object '#{input_object_type}'.",
|
56
|
+
path: [*context.path, field],
|
57
|
+
nodes: ast_node,
|
58
|
+
input_object_type: input_object_type
|
59
|
+
)
|
60
|
+
)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module GraphQL
|
3
|
+
module StaticValidation
|
4
|
+
class OneOfInputObjectsAreValidError < StaticValidation::Error
|
5
|
+
attr_reader :input_object_type
|
6
|
+
|
7
|
+
def initialize(message, path:, nodes:, input_object_type:)
|
8
|
+
super(message, path: path, nodes: nodes)
|
9
|
+
@input_object_type = input_object_type
|
10
|
+
end
|
11
|
+
|
12
|
+
# A hash representation of this Message
|
13
|
+
def to_h
|
14
|
+
extensions = {
|
15
|
+
"code" => code,
|
16
|
+
"inputObjectType" => input_object_type
|
17
|
+
}
|
18
|
+
|
19
|
+
super.merge({
|
20
|
+
"extensions" => extensions
|
21
|
+
})
|
22
|
+
end
|
23
|
+
|
24
|
+
def code
|
25
|
+
"invalidOneOfInputObject"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -3,7 +3,7 @@ module GraphQL
|
|
3
3
|
module StaticValidation
|
4
4
|
module QueryRootExists
|
5
5
|
def on_operation_definition(node, _parent)
|
6
|
-
if (node.operation_type == 'query' || node.operation_type.nil?) && context.
|
6
|
+
if (node.operation_type == 'query' || node.operation_type.nil?) && context.query.types.query_root.nil?
|
7
7
|
add_error(GraphQL::StaticValidation::QueryRootExistsError.new(
|
8
8
|
'Schema is not configured for queries',
|
9
9
|
nodes: node
|
@@ -16,12 +16,12 @@ module GraphQL
|
|
16
16
|
private
|
17
17
|
|
18
18
|
def assert_required_args(ast_node, defn)
|
19
|
-
args =
|
19
|
+
args = @context.query.types.arguments(defn)
|
20
20
|
return if args.empty?
|
21
21
|
present_argument_names = ast_node.arguments.map(&:name)
|
22
|
-
required_argument_names = context.
|
23
|
-
.select { |a| a.type.kind.non_null? && !a.default_value? && context.
|
24
|
-
.map(&:name)
|
22
|
+
required_argument_names = context.query.types.arguments(defn)
|
23
|
+
.select { |a| a.type.kind.non_null? && !a.default_value? && context.query.types.argument(defn, a.name) }
|
24
|
+
.map!(&:name)
|
25
25
|
|
26
26
|
missing_names = required_argument_names - present_argument_names
|
27
27
|
if missing_names.any?
|
@@ -26,7 +26,7 @@ module GraphQL
|
|
26
26
|
context.directive_definition || context.field_definition
|
27
27
|
end
|
28
28
|
|
29
|
-
parent_type = context.
|
29
|
+
parent_type = context.types.argument(defn, parent_name(parent, defn))
|
30
30
|
parent_type ? parent_type.type.unwrap : nil
|
31
31
|
end
|
32
32
|
|
@@ -34,16 +34,16 @@ module GraphQL
|
|
34
34
|
parent_type = get_parent_type(context, parent)
|
35
35
|
return unless parent_type && parent_type.kind.input_object?
|
36
36
|
|
37
|
-
required_fields = context.
|
38
|
-
.select{|arg| arg.type.kind.non_null?}
|
39
|
-
.map(&:graphql_name)
|
37
|
+
required_fields = context.types.arguments(parent_type)
|
38
|
+
.select{ |arg| arg.type.kind.non_null? && !arg.default_value? }
|
39
|
+
.map!(&:graphql_name)
|
40
40
|
|
41
41
|
present_fields = ast_node.arguments.map(&:name)
|
42
42
|
missing_fields = required_fields - present_fields
|
43
43
|
|
44
44
|
missing_fields.each do |missing_field|
|
45
45
|
path = [*context.path, missing_field]
|
46
|
-
missing_field_type = context.
|
46
|
+
missing_field_type = context.types.argument(parent_type, missing_field).type
|
47
47
|
add_error(RequiredInputObjectAttributesArePresentError.new(
|
48
48
|
"Argument '#{missing_field}' on InputObject '#{parent_type.to_type_signature}' is required. Expected type #{missing_field_type.to_type_signature}",
|
49
49
|
argument_name: missing_field,
|
@@ -3,7 +3,7 @@ module GraphQL
|
|
3
3
|
module StaticValidation
|
4
4
|
module SubscriptionRootExists
|
5
5
|
def on_operation_definition(node, _parent)
|
6
|
-
if node.operation_type == "subscription" && context.
|
6
|
+
if node.operation_type == "subscription" && context.types.subscription_root.nil?
|
7
7
|
add_error(GraphQL::StaticValidation::SubscriptionRootExistsError.new(
|
8
8
|
'Schema is not configured for subscriptions',
|
9
9
|
nodes: node
|
@@ -5,36 +5,27 @@ module GraphQL
|
|
5
5
|
def on_variable_definition(node, parent)
|
6
6
|
if !node.default_value.nil?
|
7
7
|
value = node.default_value
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
nodes: node,
|
12
|
-
name: node.name,
|
13
|
-
error_type: VariableDefaultValuesAreCorrectlyTypedError::VIOLATIONS[:INVALID_ON_NON_NULL]
|
14
|
-
))
|
8
|
+
type = context.schema.type_from_ast(node.type, context: context)
|
9
|
+
if type.nil?
|
10
|
+
# This is handled by another validator
|
15
11
|
else
|
16
|
-
|
17
|
-
if type.nil?
|
18
|
-
# This is handled by another validator
|
19
|
-
else
|
20
|
-
validation_result = context.validate_literal(value, type)
|
12
|
+
validation_result = context.validate_literal(value, type)
|
21
13
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
end
|
28
|
-
|
29
|
-
error_message ||= "Default value for $#{node.name} doesn't match type #{type.to_type_signature}"
|
30
|
-
add_error(GraphQL::StaticValidation::VariableDefaultValuesAreCorrectlyTypedError.new(
|
31
|
-
error_message,
|
32
|
-
nodes: node,
|
33
|
-
name: node.name,
|
34
|
-
type: type.to_type_signature,
|
35
|
-
error_type: VariableDefaultValuesAreCorrectlyTypedError::VIOLATIONS[:INVALID_TYPE],
|
36
|
-
))
|
14
|
+
if !validation_result.valid?
|
15
|
+
problems = validation_result.problems
|
16
|
+
first_problem = problems && problems.first
|
17
|
+
if first_problem
|
18
|
+
error_message = first_problem["explanation"]
|
37
19
|
end
|
20
|
+
|
21
|
+
error_message ||= "Default value for $#{node.name} doesn't match type #{type.to_type_signature}"
|
22
|
+
add_error(GraphQL::StaticValidation::VariableDefaultValuesAreCorrectlyTypedError.new(
|
23
|
+
error_message,
|
24
|
+
nodes: node,
|
25
|
+
name: node.name,
|
26
|
+
type: type.to_type_signature,
|
27
|
+
error_type: VariableDefaultValuesAreCorrectlyTypedError::VIOLATIONS[:INVALID_TYPE],
|
28
|
+
))
|
38
29
|
end
|
39
30
|
end
|
40
31
|
end
|
@@ -65,7 +65,7 @@ module GraphQL
|
|
65
65
|
end
|
66
66
|
end
|
67
67
|
|
68
|
-
arg_defn =
|
68
|
+
arg_defn = @types.argument(argument_owner, arg_node.name)
|
69
69
|
arg_defn_type = arg_defn.type
|
70
70
|
|
71
71
|
# If the argument is non-null, but it was given a default value,
|
@@ -4,7 +4,7 @@ module GraphQL
|
|
4
4
|
module VariablesAreInputTypes
|
5
5
|
def on_variable_definition(node, parent)
|
6
6
|
type_name = get_type_name(node.type)
|
7
|
-
type = context.
|
7
|
+
type = context.query.types.type(type_name)
|
8
8
|
|
9
9
|
if type.nil?
|
10
10
|
add_error(GraphQL::StaticValidation::VariablesAreInputTypesError.new(
|
@@ -8,20 +8,20 @@ module GraphQL
|
|
8
8
|
# It provides access to the schema & fragments which validators may read from.
|
9
9
|
#
|
10
10
|
# It holds a list of errors which each validator may add to.
|
11
|
-
#
|
12
|
-
# It also provides limited access to the {TypeStack} instance,
|
13
|
-
# which tracks state as you climb in and out of different fields.
|
14
11
|
class ValidationContext
|
15
12
|
extend Forwardable
|
16
13
|
|
17
14
|
attr_reader :query, :errors, :visitor,
|
18
15
|
:on_dependency_resolve_handlers,
|
19
|
-
:max_errors
|
16
|
+
:max_errors, :types, :schema
|
17
|
+
|
20
18
|
|
21
|
-
def_delegators :@query, :
|
19
|
+
def_delegators :@query, :document, :fragments, :operations
|
22
20
|
|
23
21
|
def initialize(query, visitor_class, max_errors)
|
24
22
|
@query = query
|
23
|
+
@types = query.types # TODO update migrated callers to use this accessor
|
24
|
+
@schema = query.schema
|
25
25
|
@literal_validator = LiteralValidator.new(context: query.context)
|
26
26
|
@errors = []
|
27
27
|
@max_errors = max_errors || Float::INFINITY
|
@@ -27,7 +27,8 @@ module GraphQL
|
|
27
27
|
# @param max_errors [Integer] Maximum number of errors before aborting validation. Any positive number will limit the number of errors. Defaults to nil for no limit.
|
28
28
|
# @return [Array<Hash>]
|
29
29
|
def validate(query, validate: true, timeout: nil, max_errors: nil)
|
30
|
-
query.
|
30
|
+
query.current_trace.validate(validate: validate, query: query) do
|
31
|
+
begin_t = Time.now
|
31
32
|
errors = if validate == false
|
32
33
|
[]
|
33
34
|
else
|
@@ -52,11 +53,13 @@ module GraphQL
|
|
52
53
|
end
|
53
54
|
|
54
55
|
{
|
56
|
+
remaining_timeout: timeout ? (timeout - (Time.now - begin_t)) : nil,
|
55
57
|
errors: errors,
|
56
58
|
}
|
57
59
|
end
|
58
60
|
rescue GraphQL::ExecutionError => e
|
59
61
|
{
|
62
|
+
remaining_timeout: nil,
|
60
63
|
errors: [e],
|
61
64
|
}
|
62
65
|
end
|