graphql 1.9.0.pre1 → 1.9.0.pre2
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.
- checksums.yaml +4 -4
- data/lib/graphql.rb +5 -1
- data/lib/graphql/analysis/ast/analyzer.rb +1 -1
- data/lib/graphql/analysis/ast/visitor.rb +6 -1
- data/lib/graphql/backwards_compatibility.rb +1 -1
- data/lib/graphql/dig.rb +19 -0
- data/lib/graphql/directive.rb +13 -1
- data/lib/graphql/directive/include_directive.rb +1 -7
- data/lib/graphql/directive/skip_directive.rb +1 -8
- data/lib/graphql/execution/interpreter.rb +23 -13
- data/lib/graphql/execution/interpreter/resolve.rb +56 -0
- data/lib/graphql/execution/interpreter/runtime.rb +174 -74
- data/lib/graphql/execution/lazy.rb +7 -1
- data/lib/graphql/execution/lookahead.rb +71 -6
- data/lib/graphql/execution_error.rb +1 -1
- data/lib/graphql/introspection/entry_points.rb +5 -1
- data/lib/graphql/introspection/type_type.rb +4 -4
- data/lib/graphql/language.rb +0 -1
- data/lib/graphql/language/block_string.rb +37 -0
- data/lib/graphql/language/document_from_schema_definition.rb +1 -1
- data/lib/graphql/language/lexer.rb +55 -36
- data/lib/graphql/language/lexer.rl +8 -3
- data/lib/graphql/language/nodes.rb +27 -4
- data/lib/graphql/language/parser.rb +55 -55
- data/lib/graphql/language/parser.y +11 -11
- data/lib/graphql/language/printer.rb +1 -1
- data/lib/graphql/language/visitor.rb +22 -13
- data/lib/graphql/literal_validation_error.rb +6 -0
- data/lib/graphql/query.rb +13 -0
- data/lib/graphql/query/arguments.rb +2 -1
- data/lib/graphql/query/context.rb +3 -10
- data/lib/graphql/query/executor.rb +1 -1
- data/lib/graphql/query/validation_pipeline.rb +1 -1
- data/lib/graphql/relay/connection_resolve.rb +1 -1
- data/lib/graphql/relay/relation_connection.rb +1 -1
- data/lib/graphql/schema.rb +81 -11
- data/lib/graphql/schema/argument.rb +1 -1
- data/lib/graphql/schema/build_from_definition.rb +2 -4
- data/lib/graphql/schema/directive.rb +103 -0
- data/lib/graphql/schema/directive/feature.rb +66 -0
- data/lib/graphql/schema/directive/include.rb +25 -0
- data/lib/graphql/schema/directive/skip.rb +25 -0
- data/lib/graphql/schema/directive/transform.rb +48 -0
- data/lib/graphql/schema/enum_value.rb +2 -2
- data/lib/graphql/schema/field.rb +63 -17
- data/lib/graphql/schema/input_object.rb +1 -0
- data/lib/graphql/schema/member/base_dsl_methods.rb +4 -2
- data/lib/graphql/schema/member/build_type.rb +33 -1
- data/lib/graphql/schema/member/has_fields.rb +8 -73
- data/lib/graphql/schema/relay_classic_mutation.rb +6 -1
- data/lib/graphql/schema/resolver.rb +1 -1
- data/lib/graphql/static_validation.rb +2 -1
- data/lib/graphql/static_validation/all_rules.rb +1 -0
- data/lib/graphql/static_validation/base_visitor.rb +25 -10
- data/lib/graphql/static_validation/definition_dependencies.rb +3 -3
- data/lib/graphql/static_validation/{message.rb → error.rb} +11 -11
- data/lib/graphql/static_validation/interpreter_visitor.rb +14 -0
- data/lib/graphql/static_validation/literal_validator.rb +54 -11
- data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +34 -5
- data/lib/graphql/static_validation/rules/argument_literals_are_compatible_error.rb +31 -0
- data/lib/graphql/static_validation/rules/argument_names_are_unique.rb +2 -2
- data/lib/graphql/static_validation/rules/argument_names_are_unique_error.rb +30 -0
- data/lib/graphql/static_validation/rules/arguments_are_defined.rb +7 -1
- data/lib/graphql/static_validation/rules/arguments_are_defined_error.rb +35 -0
- data/lib/graphql/static_validation/rules/directives_are_defined.rb +5 -1
- data/lib/graphql/static_validation/rules/directives_are_defined_error.rb +29 -0
- data/lib/graphql/static_validation/rules/directives_are_in_valid_locations.rb +11 -2
- data/lib/graphql/static_validation/rules/directives_are_in_valid_locations_error.rb +31 -0
- data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +11 -2
- data/lib/graphql/static_validation/rules/fields_are_defined_on_type_error.rb +32 -0
- data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +14 -2
- data/lib/graphql/static_validation/rules/fields_have_appropriate_selections_error.rb +31 -0
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +24 -6
- data/lib/graphql/static_validation/rules/fields_will_merge_error.rb +32 -0
- data/lib/graphql/static_validation/rules/fragment_names_are_unique.rb +5 -1
- data/lib/graphql/static_validation/rules/fragment_names_are_unique_error.rb +29 -0
- data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +8 -1
- data/lib/graphql/static_validation/rules/fragment_spreads_are_possible_error.rb +35 -0
- data/lib/graphql/static_validation/rules/fragment_types_exist.rb +5 -1
- data/lib/graphql/static_validation/rules/fragment_types_exist_error.rb +29 -0
- data/lib/graphql/static_validation/rules/fragments_are_finite.rb +6 -1
- data/lib/graphql/static_validation/rules/fragments_are_finite_error.rb +29 -0
- data/lib/graphql/static_validation/rules/fragments_are_named.rb +4 -1
- data/lib/graphql/static_validation/rules/fragments_are_named_error.rb +26 -0
- data/lib/graphql/static_validation/rules/fragments_are_on_composite_types.rb +5 -1
- data/lib/graphql/static_validation/rules/fragments_are_on_composite_types_error.rb +30 -0
- data/lib/graphql/static_validation/rules/fragments_are_used.rb +13 -3
- data/lib/graphql/static_validation/rules/fragments_are_used_error.rb +29 -0
- data/lib/graphql/static_validation/rules/mutation_root_exists.rb +4 -1
- data/lib/graphql/static_validation/rules/mutation_root_exists_error.rb +26 -0
- data/lib/graphql/static_validation/rules/no_definitions_are_present.rb +2 -2
- data/lib/graphql/static_validation/rules/no_definitions_are_present_error.rb +25 -0
- data/lib/graphql/static_validation/rules/operation_names_are_valid.rb +9 -2
- data/lib/graphql/static_validation/rules/operation_names_are_valid_error.rb +28 -0
- data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +7 -1
- data/lib/graphql/static_validation/rules/required_arguments_are_present_error.rb +35 -0
- data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +47 -0
- data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present_error.rb +35 -0
- data/lib/graphql/static_validation/rules/subscription_root_exists.rb +4 -1
- data/lib/graphql/static_validation/rules/subscription_root_exists_error.rb +26 -0
- data/lib/graphql/static_validation/rules/unique_directives_per_location.rb +4 -3
- data/lib/graphql/static_validation/rules/unique_directives_per_location_error.rb +29 -0
- data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +20 -6
- data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed_error.rb +39 -0
- data/lib/graphql/static_validation/rules/variable_names_are_unique.rb +5 -1
- data/lib/graphql/static_validation/rules/variable_names_are_unique_error.rb +29 -0
- data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +8 -1
- data/lib/graphql/static_validation/rules/variable_usages_are_allowed_error.rb +38 -0
- data/lib/graphql/static_validation/rules/variables_are_input_types.rb +12 -2
- data/lib/graphql/static_validation/rules/variables_are_input_types_error.rb +32 -0
- data/lib/graphql/static_validation/rules/variables_are_used_and_defined.rb +18 -2
- data/lib/graphql/static_validation/rules/variables_are_used_and_defined_error.rb +37 -0
- data/lib/graphql/static_validation/validator.rb +24 -14
- data/lib/graphql/tracing/new_relic_tracing.rb +2 -2
- data/lib/graphql/tracing/skylight_tracing.rb +2 -2
- data/lib/graphql/unauthorized_field_error.rb +23 -0
- data/lib/graphql/version.rb +1 -1
- data/spec/graphql/analysis/ast_spec.rb +40 -0
- data/spec/graphql/authorization_spec.rb +93 -20
- data/spec/graphql/base_type_spec.rb +3 -1
- data/spec/graphql/execution/interpreter_spec.rb +127 -4
- data/spec/graphql/execution/lazy_spec.rb +49 -0
- data/spec/graphql/execution/lookahead_spec.rb +113 -21
- data/spec/graphql/execution/multiplex_spec.rb +2 -1
- data/spec/graphql/introspection/type_type_spec.rb +1 -1
- data/spec/graphql/language/lexer_spec.rb +72 -3
- data/spec/graphql/language/printer_spec.rb +18 -6
- data/spec/graphql/query/arguments_spec.rb +21 -0
- data/spec/graphql/query/context_spec.rb +10 -0
- data/spec/graphql/schema/build_from_definition_spec.rb +144 -29
- data/spec/graphql/schema/directive/feature_spec.rb +81 -0
- data/spec/graphql/schema/directive/transform_spec.rb +39 -0
- data/spec/graphql/schema/enum_spec.rb +5 -3
- data/spec/graphql/schema/field_extension_spec.rb +3 -3
- data/spec/graphql/schema/field_spec.rb +19 -0
- data/spec/graphql/schema/input_object_spec.rb +81 -0
- data/spec/graphql/schema/member/build_type_spec.rb +46 -0
- data/spec/graphql/schema/member/scoped_spec.rb +3 -3
- data/spec/graphql/schema/printer_spec.rb +244 -96
- data/spec/graphql/schema/relay_classic_mutation_spec.rb +26 -0
- data/spec/graphql/schema/resolver_spec.rb +1 -1
- data/spec/graphql/schema/warden_spec.rb +35 -11
- data/spec/graphql/static_validation/rules/argument_literals_are_compatible_spec.rb +212 -72
- data/spec/graphql/static_validation/rules/argument_names_are_unique_spec.rb +2 -2
- data/spec/graphql/static_validation/rules/arguments_are_defined_spec.rb +72 -29
- data/spec/graphql/static_validation/rules/directives_are_defined_spec.rb +4 -2
- data/spec/graphql/static_validation/rules/directives_are_in_valid_locations_spec.rb +4 -2
- data/spec/graphql/static_validation/rules/fields_are_defined_on_type_spec.rb +10 -5
- data/spec/graphql/static_validation/rules/fields_have_appropriate_selections_spec.rb +10 -5
- data/spec/graphql/static_validation/rules/fields_will_merge_spec.rb +2 -1
- data/spec/graphql/static_validation/rules/fragment_names_are_unique_spec.rb +2 -1
- data/spec/graphql/static_validation/rules/fragment_spreads_are_possible_spec.rb +6 -3
- data/spec/graphql/static_validation/rules/fragment_types_exist_spec.rb +4 -2
- data/spec/graphql/static_validation/rules/fragments_are_finite_spec.rb +4 -2
- data/spec/graphql/static_validation/rules/fragments_are_named_spec.rb +2 -1
- data/spec/graphql/static_validation/rules/fragments_are_on_composite_types_spec.rb +6 -3
- data/spec/graphql/static_validation/rules/fragments_are_used_spec.rb +22 -2
- data/spec/graphql/static_validation/rules/mutation_root_exists_spec.rb +2 -1
- data/spec/graphql/static_validation/rules/operation_names_are_valid_spec.rb +6 -3
- data/spec/graphql/static_validation/rules/required_arguments_are_present_spec.rb +13 -4
- data/spec/graphql/static_validation/rules/required_input_object_attributes_are_present_spec.rb +58 -0
- data/spec/graphql/static_validation/rules/subscription_root_exists_spec.rb +2 -1
- data/spec/graphql/static_validation/rules/unique_directives_per_location_spec.rb +14 -7
- data/spec/graphql/static_validation/rules/variable_default_values_are_correctly_typed_spec.rb +14 -7
- data/spec/graphql/static_validation/rules/variable_usages_are_allowed_spec.rb +8 -4
- data/spec/graphql/static_validation/rules/variables_are_input_types_spec.rb +8 -4
- data/spec/graphql/static_validation/rules/variables_are_used_and_defined_spec.rb +6 -3
- data/spec/graphql/static_validation/validator_spec.rb +6 -4
- data/spec/graphql/tracing/new_relic_tracing_spec.rb +10 -0
- data/spec/graphql/tracing/skylight_tracing_spec.rb +10 -0
- data/spec/graphql/types/iso_8601_date_time_spec.rb +1 -2
- data/spec/integration/mongoid/star_trek/schema.rb +5 -5
- data/spec/integration/rails/graphql/relay/relation_connection_spec.rb +37 -8
- data/spec/integration/rails/graphql/schema_spec.rb +2 -2
- data/spec/integration/rails/spec_helper.rb +10 -0
- data/spec/integration/tmp/app/graphql/types/bird_type.rb +7 -0
- data/spec/integration/tmp/dummy/Gemfile +45 -0
- data/spec/integration/tmp/dummy/README.rdoc +28 -0
- data/spec/integration/tmp/dummy/Rakefile +6 -0
- data/spec/integration/tmp/dummy/app/assets/javascripts/application.js +16 -0
- data/spec/integration/tmp/dummy/app/assets/stylesheets/application.css +15 -0
- data/spec/integration/tmp/dummy/app/controllers/application_controller.rb +5 -0
- data/spec/integration/tmp/dummy/app/controllers/graphql_controller.rb +43 -0
- data/spec/integration/tmp/dummy/app/graphql/dummy_schema.rb +34 -0
- data/spec/integration/tmp/dummy/app/graphql/types/base_enum.rb +4 -0
- data/spec/integration/tmp/dummy/app/graphql/types/base_input_object.rb +4 -0
- data/spec/integration/tmp/dummy/app/graphql/types/base_interface.rb +5 -0
- data/spec/integration/tmp/dummy/app/graphql/types/base_object.rb +4 -0
- data/spec/integration/tmp/dummy/app/graphql/types/base_scalar.rb +4 -0
- data/spec/integration/tmp/dummy/app/graphql/types/base_union.rb +4 -0
- data/spec/integration/tmp/dummy/app/graphql/types/mutation_type.rb +10 -0
- data/spec/integration/tmp/dummy/app/graphql/types/query_type.rb +15 -0
- data/spec/integration/tmp/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/integration/tmp/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/integration/tmp/dummy/bin/bundle +3 -0
- data/spec/integration/tmp/dummy/bin/rails +4 -0
- data/spec/integration/tmp/dummy/bin/rake +4 -0
- data/spec/integration/tmp/dummy/bin/setup +29 -0
- data/spec/integration/tmp/dummy/config.ru +4 -0
- data/spec/integration/tmp/dummy/config/application.rb +32 -0
- data/spec/integration/tmp/dummy/config/boot.rb +3 -0
- data/spec/integration/tmp/dummy/config/environment.rb +5 -0
- data/spec/integration/tmp/dummy/config/environments/development.rb +38 -0
- data/spec/integration/tmp/dummy/config/environments/production.rb +76 -0
- data/spec/integration/tmp/dummy/config/environments/test.rb +42 -0
- data/spec/integration/tmp/dummy/config/initializers/assets.rb +11 -0
- data/spec/integration/tmp/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/integration/tmp/dummy/config/initializers/cookies_serializer.rb +3 -0
- data/spec/integration/tmp/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/spec/integration/tmp/dummy/config/initializers/inflections.rb +16 -0
- data/spec/integration/tmp/dummy/config/initializers/mime_types.rb +4 -0
- data/spec/integration/tmp/dummy/config/initializers/session_store.rb +3 -0
- data/spec/integration/tmp/dummy/config/initializers/to_time_preserves_timezone.rb +10 -0
- data/spec/integration/tmp/dummy/config/initializers/wrap_parameters.rb +9 -0
- data/spec/integration/tmp/dummy/config/locales/en.yml +23 -0
- data/spec/integration/tmp/dummy/config/routes.rb +61 -0
- data/spec/integration/tmp/dummy/config/secrets.yml +22 -0
- data/spec/integration/tmp/dummy/db/seeds.rb +7 -0
- data/spec/integration/tmp/dummy/public/404.html +67 -0
- data/spec/integration/tmp/dummy/public/422.html +67 -0
- data/spec/integration/tmp/dummy/public/500.html +66 -0
- data/spec/integration/tmp/dummy/public/favicon.ico +0 -0
- data/spec/integration/tmp/dummy/public/robots.txt +5 -0
- data/spec/support/dummy/schema.rb +2 -2
- data/spec/support/error_bubbling_helpers.rb +23 -0
- data/spec/support/jazz.rb +53 -6
- data/spec/support/lazy_helpers.rb +26 -8
- data/spec/support/new_relic.rb +3 -0
- data/spec/support/skylight.rb +3 -0
- data/spec/support/star_wars/schema.rb +13 -9
- data/spec/support/static_validation_helpers.rb +3 -1
- metadata +145 -22
- data/lib/graphql/language/comments.rb +0 -45
- data/spec/graphql/schema/member/has_fields_spec.rb +0 -132
- data/spec/integration/tmp/app/graphql/types/family_type.rb +0 -9
@@ -32,7 +32,12 @@ module GraphQL
|
|
32
32
|
# Without the interpreter, the inputs are unwrapped by an instrumenter.
|
33
33
|
# But when using the interpreter, no instrumenters are applied.
|
34
34
|
if context.interpreter?
|
35
|
-
input = inputs[:input]
|
35
|
+
input = inputs[:input].to_h
|
36
|
+
# Transfer these from the top-level hash to the
|
37
|
+
# shortcutted `input:` object
|
38
|
+
self.class.extras.each do |ext|
|
39
|
+
input[ext] = inputs[ext]
|
40
|
+
end
|
36
41
|
else
|
37
42
|
input = inputs
|
38
43
|
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
require "graphql/static_validation/
|
2
|
+
require "graphql/static_validation/error"
|
3
3
|
require "graphql/static_validation/definition_dependencies"
|
4
4
|
require "graphql/static_validation/type_stack"
|
5
5
|
require "graphql/static_validation/validator"
|
@@ -15,3 +15,4 @@ end
|
|
15
15
|
|
16
16
|
require "graphql/static_validation/all_rules"
|
17
17
|
require "graphql/static_validation/default_visitor"
|
18
|
+
require "graphql/static_validation/interpreter_visitor"
|
@@ -25,6 +25,7 @@ module GraphQL
|
|
25
25
|
GraphQL::StaticValidation::ArgumentsAreDefined,
|
26
26
|
GraphQL::StaticValidation::ArgumentLiteralsAreCompatible,
|
27
27
|
GraphQL::StaticValidation::RequiredArgumentsArePresent,
|
28
|
+
GraphQL::StaticValidation::RequiredInputObjectAttributesArePresent,
|
28
29
|
GraphQL::StaticValidation::ArgumentNamesAreUnique,
|
29
30
|
GraphQL::StaticValidation::VariableNamesAreUnique,
|
30
31
|
GraphQL::StaticValidation::VariablesAreInputTypes,
|
@@ -14,6 +14,11 @@ module GraphQL
|
|
14
14
|
super(document)
|
15
15
|
end
|
16
16
|
|
17
|
+
# This will be overwritten by {InternalRepresentation::Rewrite} if it's included
|
18
|
+
def rewrite_document
|
19
|
+
nil
|
20
|
+
end
|
21
|
+
|
17
22
|
attr_reader :context
|
18
23
|
|
19
24
|
# @return [Array<GraphQL::ObjectType>] Types whose scope we've entered
|
@@ -27,12 +32,22 @@ module GraphQL
|
|
27
32
|
# Build a class to visit the AST and perform validation,
|
28
33
|
# or use a pre-built class if rules is `ALL_RULES` or empty.
|
29
34
|
# @param rules [Array<Module, Class>]
|
35
|
+
# @param rewrite [Boolean] if `false`, don't include rewrite
|
30
36
|
# @return [Class] A class for validating `rules` during visitation
|
31
|
-
def self.including_rules(rules)
|
37
|
+
def self.including_rules(rules, rewrite: true)
|
32
38
|
if rules.none?
|
33
|
-
|
39
|
+
if rewrite
|
40
|
+
NoValidateVisitor
|
41
|
+
else
|
42
|
+
# It's not doing _anything?!?_
|
43
|
+
BaseVisitor
|
44
|
+
end
|
34
45
|
elsif rules == ALL_RULES
|
35
|
-
|
46
|
+
if rewrite
|
47
|
+
DefaultVisitor
|
48
|
+
else
|
49
|
+
InterpreterVisitor
|
50
|
+
end
|
36
51
|
else
|
37
52
|
visitor_class = Class.new(self) do
|
38
53
|
include(GraphQL::StaticValidation::DefinitionDependencies)
|
@@ -45,7 +60,9 @@ module GraphQL
|
|
45
60
|
end
|
46
61
|
end
|
47
62
|
|
48
|
-
|
63
|
+
if rewrite
|
64
|
+
visitor_class.include(GraphQL::InternalRepresentation::Rewrite)
|
65
|
+
end
|
49
66
|
visitor_class.include(ContextMethods)
|
50
67
|
visitor_class
|
51
68
|
end
|
@@ -172,13 +189,11 @@ module GraphQL
|
|
172
189
|
|
173
190
|
private
|
174
191
|
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
nodes = Array(nodes)
|
179
|
-
m = GraphQL::StaticValidation::Message.new(message, nodes: nodes, path: path)
|
180
|
-
context.errors << m
|
192
|
+
def add_error(error, path: nil)
|
193
|
+
error.path ||= (path || @path.dup)
|
194
|
+
context.errors << error
|
181
195
|
end
|
196
|
+
|
182
197
|
end
|
183
198
|
end
|
184
199
|
end
|
@@ -20,7 +20,7 @@ module GraphQL
|
|
20
20
|
|
21
21
|
# First-level usages of spreads within definitions
|
22
22
|
# (When a key has an empty list as its value,
|
23
|
-
# we can resolve that key's
|
23
|
+
# we can resolve that key's dependents)
|
24
24
|
# { definition_node => [node, node ...] }
|
25
25
|
@defdep_immediate_dependencies = Hash.new { |h, k| h[k] = Set.new }
|
26
26
|
|
@@ -72,7 +72,7 @@ module GraphQL
|
|
72
72
|
|
73
73
|
|
74
74
|
# Map definition AST nodes to the definition AST nodes they depend on.
|
75
|
-
# Expose circular
|
75
|
+
# Expose circular dependencies.
|
76
76
|
class DependencyMap
|
77
77
|
# @return [Array<GraphQL::Language::Nodes::FragmentDefinition>]
|
78
78
|
attr_reader :cyclical_definitions
|
@@ -143,7 +143,7 @@ module GraphQL
|
|
143
143
|
# Register the dependency AND second-order dependencies
|
144
144
|
dependency_map[definition_node] << fragment_node
|
145
145
|
dependency_map[definition_node].concat(dependency_map[fragment_node])
|
146
|
-
# Since we've
|
146
|
+
# Since we've registered it, remove it from our to-do list
|
147
147
|
deps = @defdep_immediate_dependencies[definition_node]
|
148
148
|
# Can't find a way to _just_ delete from `deps` and return the deleted entries
|
149
149
|
removed, remaining = deps.partition { |spread| spread.name == fragment_node.name }
|
@@ -2,22 +2,23 @@
|
|
2
2
|
module GraphQL
|
3
3
|
module StaticValidation
|
4
4
|
# Generates GraphQL-compliant validation message.
|
5
|
-
class
|
5
|
+
class Error
|
6
6
|
# Convenience for validators
|
7
|
-
module
|
8
|
-
# Error `
|
9
|
-
def
|
7
|
+
module ErrorHelper
|
8
|
+
# Error `error_message` is located at `node`
|
9
|
+
def error(error_message, nodes, context: nil, path: nil, extensions: {})
|
10
10
|
path ||= context.path
|
11
11
|
nodes = Array(nodes)
|
12
|
-
GraphQL::StaticValidation::
|
12
|
+
GraphQL::StaticValidation::Error.new(error_message, nodes: nodes, path: path)
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
16
|
-
attr_reader
|
16
|
+
attr_reader :message
|
17
|
+
attr_accessor :path
|
17
18
|
|
18
|
-
def initialize(message, path:
|
19
|
+
def initialize(message, path: nil, nodes: [])
|
19
20
|
@message = message
|
20
|
-
@nodes = nodes
|
21
|
+
@nodes = Array(nodes)
|
21
22
|
@path = path
|
22
23
|
end
|
23
24
|
|
@@ -25,9 +26,8 @@ module GraphQL
|
|
25
26
|
def to_h
|
26
27
|
{
|
27
28
|
"message" => message,
|
28
|
-
"locations" => locations
|
29
|
-
|
30
|
-
}
|
29
|
+
"locations" => locations
|
30
|
+
}.tap { |h| h["path"] = path unless path.nil? }
|
31
31
|
end
|
32
32
|
|
33
33
|
private
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module GraphQL
|
3
|
+
module StaticValidation
|
4
|
+
class InterpreterVisitor < BaseVisitor
|
5
|
+
include(GraphQL::StaticValidation::DefinitionDependencies)
|
6
|
+
|
7
|
+
StaticValidation::ALL_RULES.reverse_each do |r|
|
8
|
+
include(r)
|
9
|
+
end
|
10
|
+
|
11
|
+
include(ContextMethods)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -9,29 +9,61 @@ module GraphQL
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def validate(ast_value, type)
|
12
|
-
if
|
13
|
-
|
12
|
+
if type.nil?
|
13
|
+
# this means we're an undefined argument, see #present_input_field_values_are_valid
|
14
|
+
return maybe_raise_if_invalid(ast_value) do
|
15
|
+
false
|
16
|
+
end
|
17
|
+
elsif ast_value.is_a?(GraphQL::Language::Nodes::NullValue)
|
18
|
+
maybe_raise_if_invalid(ast_value) do
|
19
|
+
!type.kind.non_null?
|
20
|
+
end
|
14
21
|
elsif type.kind.non_null?
|
15
|
-
(
|
22
|
+
maybe_raise_if_invalid(ast_value) do
|
23
|
+
(!ast_value.nil?)
|
24
|
+
end && validate(ast_value, type.of_type)
|
16
25
|
elsif type.kind.list?
|
17
26
|
item_type = type.of_type
|
18
27
|
ensure_array(ast_value).all? { |val| validate(val, item_type) }
|
19
28
|
elsif ast_value.is_a?(GraphQL::Language::Nodes::VariableIdentifier)
|
20
29
|
true
|
21
30
|
elsif type.kind.scalar? && constant_scalar?(ast_value)
|
22
|
-
|
23
|
-
|
24
|
-
|
31
|
+
maybe_raise_if_invalid(ast_value) do
|
32
|
+
type.valid_input?(ast_value, @context)
|
33
|
+
end
|
34
|
+
elsif type.kind.enum?
|
35
|
+
maybe_raise_if_invalid(ast_value) do
|
36
|
+
if ast_value.is_a?(GraphQL::Language::Nodes::Enum)
|
37
|
+
type.valid_input?(ast_value.name, @context)
|
38
|
+
else
|
39
|
+
# if our ast_value isn't an Enum it's going to be invalid so return false
|
40
|
+
false
|
41
|
+
end
|
42
|
+
end
|
25
43
|
elsif type.kind.input_object? && ast_value.is_a?(GraphQL::Language::Nodes::InputObject)
|
26
|
-
|
27
|
-
present_input_field_values_are_valid(type, ast_value)
|
44
|
+
maybe_raise_if_invalid(ast_value) do
|
45
|
+
required_input_fields_are_present(type, ast_value) && present_input_field_values_are_valid(type, ast_value)
|
46
|
+
end
|
28
47
|
else
|
29
|
-
|
48
|
+
maybe_raise_if_invalid(ast_value) do
|
49
|
+
false
|
50
|
+
end
|
30
51
|
end
|
31
52
|
end
|
32
53
|
|
33
54
|
private
|
34
55
|
|
56
|
+
def maybe_raise_if_invalid(ast_value)
|
57
|
+
ret = yield
|
58
|
+
if !@context.schema.error_bubbling && !ret
|
59
|
+
e = LiteralValidationError.new
|
60
|
+
e.ast_value = ast_value
|
61
|
+
raise e
|
62
|
+
else
|
63
|
+
ret
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
35
67
|
# The GraphQL grammar supports variables embedded within scalars but graphql.js
|
36
68
|
# doesn't support it so we won't either for simplicity
|
37
69
|
def constant_scalar?(ast_value)
|
@@ -47,19 +79,30 @@ module GraphQL
|
|
47
79
|
end
|
48
80
|
|
49
81
|
def required_input_fields_are_present(type, ast_node)
|
82
|
+
# TODO - would be nice to use these to create an error message so the caller knows
|
83
|
+
# that required fields are missing
|
50
84
|
required_field_names = @warden.arguments(type)
|
51
85
|
.select { |f| f.type.kind.non_null? }
|
52
86
|
.map(&:name)
|
53
87
|
present_field_names = ast_node.arguments.map(&:name)
|
54
88
|
missing_required_field_names = required_field_names - present_field_names
|
55
|
-
|
89
|
+
if @context.schema.error_bubbling
|
90
|
+
missing_required_field_names.none?
|
91
|
+
else
|
92
|
+
missing_required_field_names.all? do |name|
|
93
|
+
validate(GraphQL::Language::Nodes::NullValue.new(name: name), @warden.arguments(type).find { |f| f.name == name }.type )
|
94
|
+
end
|
95
|
+
end
|
56
96
|
end
|
57
97
|
|
58
98
|
def present_input_field_values_are_valid(type, ast_node)
|
59
99
|
field_map = @warden.arguments(type).reduce({}) { |m, f| m[f.name] = f; m}
|
60
100
|
ast_node.arguments.all? do |value|
|
61
101
|
field = field_map[value.name]
|
62
|
-
|
102
|
+
# we want to call validate on an argument even if it's an invalid one
|
103
|
+
# so that our raise exception is on it instead of the entire InputObject
|
104
|
+
type = field && field.type
|
105
|
+
validate(value.value, type)
|
63
106
|
end
|
64
107
|
end
|
65
108
|
|
@@ -31,17 +31,46 @@ module GraphQL
|
|
31
31
|
begin
|
32
32
|
valid = context.valid_literal?(node.value, arg_defn.type)
|
33
33
|
rescue GraphQL::CoercionError => err
|
34
|
-
|
34
|
+
context.schema.error_bubbling
|
35
|
+
if !context.schema.error_bubbling && !arg_defn.type.unwrap.kind.scalar?
|
36
|
+
# if error bubbling is disabled and the arg that caused this error isn't a scalar then
|
37
|
+
# short-circuit here so we avoid bubbling this up to whatever input_object / array contains us
|
38
|
+
return super
|
39
|
+
end
|
40
|
+
error = GraphQL::StaticValidation::ArgumentLiteralsAreCompatibleError.new(err.message, nodes: parent, type: "CoercionError")
|
41
|
+
rescue GraphQL::LiteralValidationError => err
|
42
|
+
# check to see if the ast node that caused the error to be raised is
|
43
|
+
# the same as the node we were checking here.
|
44
|
+
matched = if arg_defn.type.kind.list?
|
45
|
+
# for a list we claim an error if the node is contained in our list
|
46
|
+
node.value.include?(err.ast_value)
|
47
|
+
elsif arg_defn.type.kind.input_object? && node.value.is_a?(GraphQL::Language::Nodes::InputObject)
|
48
|
+
# for an input object we check the arguments
|
49
|
+
node.value.arguments.include?(err.ast_value)
|
50
|
+
else
|
51
|
+
# otherwise we just check equality
|
52
|
+
node.value == (err.ast_value)
|
53
|
+
end
|
54
|
+
if !matched
|
55
|
+
# This node isn't the node that caused the error,
|
56
|
+
# So halt this visit but continue visiting the rest of the tree
|
57
|
+
return super
|
58
|
+
end
|
35
59
|
end
|
36
60
|
|
37
61
|
if !valid
|
38
|
-
|
62
|
+
error ||= begin
|
39
63
|
kind_of_node = node_type(parent)
|
40
64
|
error_arg_name = parent_name(parent, parent_defn)
|
41
|
-
"Argument '#{node.name}' on #{kind_of_node} '#{error_arg_name}' has an invalid value. Expected type '#{arg_defn.type}'."
|
42
|
-
end
|
43
65
|
|
44
|
-
|
66
|
+
GraphQL::StaticValidation::ArgumentLiteralsAreCompatibleError.new(
|
67
|
+
"Argument '#{node.name}' on #{kind_of_node} '#{error_arg_name}' has an invalid value. Expected type '#{arg_defn.type}'.",
|
68
|
+
nodes: parent,
|
69
|
+
type: kind_of_node,
|
70
|
+
argument: node.name
|
71
|
+
)
|
72
|
+
end
|
73
|
+
add_error(error)
|
45
74
|
end
|
46
75
|
end
|
47
76
|
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module GraphQL
|
3
|
+
module StaticValidation
|
4
|
+
class ArgumentLiteralsAreCompatibleError < StaticValidation::Error
|
5
|
+
attr_reader :type_name
|
6
|
+
attr_reader :argument_name
|
7
|
+
|
8
|
+
def initialize(message, path: nil, nodes: [], type:, argument: nil)
|
9
|
+
super(message, path: path, nodes: nodes)
|
10
|
+
@type_name = type
|
11
|
+
@argument_name = argument
|
12
|
+
end
|
13
|
+
|
14
|
+
# A hash representation of this Message
|
15
|
+
def to_h
|
16
|
+
extensions = {
|
17
|
+
"code" => code,
|
18
|
+
"typeName" => type_name
|
19
|
+
}.tap { |h| h["argumentName"] = argument_name unless argument_name.nil? }
|
20
|
+
|
21
|
+
super.merge({
|
22
|
+
"extensions" => extensions
|
23
|
+
})
|
24
|
+
end
|
25
|
+
|
26
|
+
def code
|
27
|
+
"argumentLiteralsIncompatible"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -2,7 +2,7 @@
|
|
2
2
|
module GraphQL
|
3
3
|
module StaticValidation
|
4
4
|
module ArgumentNamesAreUnique
|
5
|
-
include GraphQL::StaticValidation::
|
5
|
+
include GraphQL::StaticValidation::Error::ErrorHelper
|
6
6
|
|
7
7
|
def on_field(node, parent)
|
8
8
|
validate_arguments(node)
|
@@ -21,7 +21,7 @@ module GraphQL
|
|
21
21
|
argument_defns.each { |a| args_by_name[a.name] << a }
|
22
22
|
args_by_name.each do |name, defns|
|
23
23
|
if defns.size > 1
|
24
|
-
add_error("There can be only one argument named \"#{name}\"", defns)
|
24
|
+
add_error(GraphQL::StaticValidation::ArgumentNamesAreUniqueError.new("There can be only one argument named \"#{name}\"", nodes: defns, name: name))
|
25
25
|
end
|
26
26
|
end
|
27
27
|
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module GraphQL
|
3
|
+
module StaticValidation
|
4
|
+
class ArgumentNamesAreUniqueError < StaticValidation::Error
|
5
|
+
attr_reader :name
|
6
|
+
|
7
|
+
def initialize(message, path: nil, nodes: [], name:)
|
8
|
+
super(message, path: path, nodes: nodes)
|
9
|
+
@name = name
|
10
|
+
end
|
11
|
+
|
12
|
+
# A hash representation of this Message
|
13
|
+
def to_h
|
14
|
+
extensions = {
|
15
|
+
"code" => code,
|
16
|
+
"name" => name
|
17
|
+
}
|
18
|
+
|
19
|
+
super.merge({
|
20
|
+
"extensions" => extensions
|
21
|
+
})
|
22
|
+
end
|
23
|
+
|
24
|
+
def code
|
25
|
+
"argumentNotUnique"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
@@ -29,7 +29,13 @@ module GraphQL
|
|
29
29
|
elsif parent_defn
|
30
30
|
kind_of_node = node_type(parent)
|
31
31
|
error_arg_name = parent_name(parent, parent_defn)
|
32
|
-
add_error(
|
32
|
+
add_error(GraphQL::StaticValidation::ArgumentsAreDefinedError.new(
|
33
|
+
"#{kind_of_node} '#{error_arg_name}' doesn't accept argument '#{node.name}'",
|
34
|
+
nodes: node,
|
35
|
+
name: error_arg_name,
|
36
|
+
type: kind_of_node,
|
37
|
+
argument: node.name
|
38
|
+
))
|
33
39
|
else
|
34
40
|
# Some other weird error
|
35
41
|
super
|