graphql 0.11.0 → 0.11.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/graphql/base_type.rb +6 -2
- data/lib/graphql/definition_helpers.rb +0 -5
- data/lib/graphql/definition_helpers/defined_by_config.rb +106 -102
- data/lib/graphql/definition_helpers/non_null_with_bang.rb +13 -9
- data/lib/graphql/definition_helpers/string_named_hash.rb +19 -15
- data/lib/graphql/definition_helpers/type_definer.rb +25 -21
- data/lib/graphql/enum_type.rb +8 -2
- data/lib/graphql/float_type.rb +1 -1
- data/lib/graphql/input_object_type.rb +27 -6
- data/lib/graphql/interface_type.rb +10 -0
- data/lib/graphql/introspection/fields_field.rb +2 -2
- data/lib/graphql/list_type.rb +12 -2
- data/lib/graphql/non_null_type.rb +11 -1
- data/lib/graphql/object_type.rb +19 -0
- data/lib/graphql/query.rb +2 -14
- data/lib/graphql/query/input_validation_result.rb +23 -0
- data/lib/graphql/query/literal_input.rb +3 -1
- data/lib/graphql/query/serial_execution/field_resolution.rb +3 -1
- data/lib/graphql/query/serial_execution/selection_resolution.rb +21 -15
- data/lib/graphql/query/variable_validation_error.rb +18 -0
- data/lib/graphql/query/variables.rb +4 -8
- data/lib/graphql/scalar_type.rb +10 -4
- data/lib/graphql/schema.rb +4 -2
- data/lib/graphql/schema/printer.rb +12 -3
- data/lib/graphql/schema/type_reducer.rb +4 -3
- data/lib/graphql/static_validation.rb +1 -0
- data/lib/graphql/static_validation/all_rules.rb +1 -0
- data/lib/graphql/static_validation/rules/document_does_not_exceed_max_depth.rb +79 -0
- data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +1 -1
- data/lib/graphql/static_validation/validation_context.rb +63 -0
- data/lib/graphql/static_validation/validator.rb +1 -52
- data/lib/graphql/version.rb +1 -1
- data/readme.md +21 -22
- data/spec/graphql/enum_type_spec.rb +8 -0
- data/spec/graphql/input_object_type_spec.rb +101 -3
- data/spec/graphql/introspection/schema_type_spec.rb +6 -6
- data/spec/graphql/introspection/type_type_spec.rb +6 -6
- data/spec/graphql/language/transform_spec.rb +9 -5
- data/spec/graphql/list_type_spec.rb +23 -0
- data/spec/graphql/object_type_spec.rb +11 -4
- data/spec/graphql/query/executor_spec.rb +34 -5
- data/spec/graphql/query_spec.rb +22 -3
- data/spec/graphql/scalar_type_spec.rb +28 -0
- data/spec/graphql/schema/type_reducer_spec.rb +2 -2
- data/spec/graphql/static_validation/complexity_validator_spec.rb +15 -0
- data/spec/graphql/static_validation/rules/document_does_not_exceed_max_depth_spec.rb +93 -0
- data/spec/support/dairy_app.rb +2 -2
- data/spec/support/minimum_input_object.rb +8 -5
- metadata +10 -4
- data/spec/graphql/static_validation/complexity_validator.rb +0 -15
@@ -23,4 +23,14 @@ class GraphQL::InterfaceType < GraphQL::BaseType
|
|
23
23
|
def possible_types
|
24
24
|
@possible_types ||= []
|
25
25
|
end
|
26
|
+
|
27
|
+
# @return [GraphQL::Field] The defined field for `field_name`
|
28
|
+
def get_field(field_name)
|
29
|
+
fields[field_name]
|
30
|
+
end
|
31
|
+
|
32
|
+
# @return [Array<GraphQL::Field>] All fields on this type
|
33
|
+
def all_fields
|
34
|
+
fields.values
|
35
|
+
end
|
26
36
|
end
|
@@ -4,10 +4,10 @@ GraphQL::Introspection::FieldsField = GraphQL::Field.define do
|
|
4
4
|
argument :includeDeprecated, GraphQL::BOOLEAN_TYPE, default_value: false
|
5
5
|
resolve -> (object, arguments, context) {
|
6
6
|
return nil if !object.kind.fields?
|
7
|
-
fields = object.
|
7
|
+
fields = object.all_fields
|
8
8
|
if !arguments["includeDeprecated"]
|
9
9
|
fields = fields.select {|f| !f.deprecation_reason }
|
10
10
|
end
|
11
|
-
fields
|
11
|
+
fields.sort_by { |f| f.name }
|
12
12
|
}
|
13
13
|
end
|
data/lib/graphql/list_type.rb
CHANGED
@@ -17,10 +17,20 @@ class GraphQL::ListType < GraphQL::BaseType
|
|
17
17
|
"[#{of_type.to_s}]"
|
18
18
|
end
|
19
19
|
|
20
|
-
def
|
21
|
-
|
20
|
+
def validate_non_null_input(value)
|
21
|
+
result = GraphQL::Query::InputValidationResult.new
|
22
|
+
|
23
|
+
ensure_array(value).each_with_index do |item, index|
|
24
|
+
item_result = of_type.validate_input(item)
|
25
|
+
if !item_result.valid?
|
26
|
+
result.merge_result!(index, item_result)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
result
|
22
31
|
end
|
23
32
|
|
33
|
+
|
24
34
|
def coerce_non_null_input(value)
|
25
35
|
ensure_array(value).map{ |item| of_type.coerce_input(item) }
|
26
36
|
end
|
@@ -14,7 +14,17 @@ class GraphQL::NonNullType < GraphQL::BaseType
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def valid_input?(value)
|
17
|
-
|
17
|
+
validate_input(value).valid?
|
18
|
+
end
|
19
|
+
|
20
|
+
def validate_input(value)
|
21
|
+
if value.nil?
|
22
|
+
result = GraphQL::Query::InputValidationResult.new
|
23
|
+
result.add_problem("Expected value to not be null")
|
24
|
+
result
|
25
|
+
else
|
26
|
+
of_type.validate_input(value)
|
27
|
+
end
|
18
28
|
end
|
19
29
|
|
20
30
|
def coerce_input(value)
|
data/lib/graphql/object_type.rb
CHANGED
@@ -42,4 +42,23 @@ class GraphQL::ObjectType < GraphQL::BaseType
|
|
42
42
|
def kind
|
43
43
|
GraphQL::TypeKinds::OBJECT
|
44
44
|
end
|
45
|
+
|
46
|
+
# @return [GraphQL::Field] The field definition for `field_name` (may be inherited from interfaces)
|
47
|
+
def get_field(field_name)
|
48
|
+
fields[field_name] || interface_fields[field_name]
|
49
|
+
end
|
50
|
+
|
51
|
+
# @return [Array<GraphQL::Field>] All fields, including ones inherited from interfaces
|
52
|
+
def all_fields
|
53
|
+
interface_fields.merge(self.fields).values
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
# Create a {name => defn} hash for fields inherited from interfaces
|
59
|
+
def interface_fields
|
60
|
+
interfaces.reduce({}) do |memo, iface|
|
61
|
+
memo.merge!(iface.fields)
|
62
|
+
end
|
63
|
+
end
|
45
64
|
end
|
data/lib/graphql/query.rb
CHANGED
@@ -6,20 +6,6 @@ class GraphQL::Query
|
|
6
6
|
end
|
7
7
|
end
|
8
8
|
|
9
|
-
class VariableValidationError < GraphQL::ExecutionError
|
10
|
-
def initialize(variable_ast, type, reason)
|
11
|
-
msg = "Variable #{variable_ast.name} of type #{type} #{reason}"
|
12
|
-
super(msg)
|
13
|
-
self.ast_node = variable_ast
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
class VariableMissingError < VariableValidationError
|
18
|
-
def initialize(variable_ast, type)
|
19
|
-
super(variable_ast, type, "can't be null")
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
9
|
# If a resolve function returns `GraphQL::Query::DEFAULT_RESOLVE`,
|
24
10
|
# The executor will send the field's name to the target object
|
25
11
|
# and use the result.
|
@@ -111,3 +97,5 @@ require 'graphql/query/literal_input'
|
|
111
97
|
require 'graphql/query/serial_execution'
|
112
98
|
require 'graphql/query/type_resolver'
|
113
99
|
require 'graphql/query/variables'
|
100
|
+
require 'graphql/query/input_validation_result'
|
101
|
+
require 'graphql/query/variable_validation_error'
|
@@ -0,0 +1,23 @@
|
|
1
|
+
class GraphQL::Query
|
2
|
+
class InputValidationResult
|
3
|
+
attr_accessor :problems
|
4
|
+
|
5
|
+
def valid?
|
6
|
+
@problems.nil?
|
7
|
+
end
|
8
|
+
|
9
|
+
def add_problem(explanation, path = nil)
|
10
|
+
@problems ||= []
|
11
|
+
@problems.push({ 'path' => path || [], 'explanation' => explanation })
|
12
|
+
end
|
13
|
+
|
14
|
+
def merge_result!(path, inner_result)
|
15
|
+
return if inner_result.valid?
|
16
|
+
|
17
|
+
inner_result.problems.each do |p|
|
18
|
+
item_path = [path, *p['path']]
|
19
|
+
add_problem(p['explanation'], item_path)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -55,7 +55,9 @@ module GraphQL
|
|
55
55
|
type.input_fields.each do |arg_name, arg_defn|
|
56
56
|
if hash[arg_name].nil?
|
57
57
|
value = LiteralInput.coerce(arg_defn.type, arg_defn.default_value, variables)
|
58
|
-
|
58
|
+
if !value.nil?
|
59
|
+
hash[arg_name] = value
|
60
|
+
end
|
59
61
|
end
|
60
62
|
end
|
61
63
|
Arguments.new(hash)
|
@@ -10,7 +10,9 @@ module GraphQL
|
|
10
10
|
@target = target
|
11
11
|
@execution_context = execution_context
|
12
12
|
@field = execution_context.get_field(parent_type, ast_node.name)
|
13
|
-
|
13
|
+
if @field.nil?
|
14
|
+
raise("No field found on #{parent_type.name} '#{parent_type}' for '#{ast_node.name}'")
|
15
|
+
end
|
14
16
|
@arguments = GraphQL::Query::LiteralInput.from_arguments(
|
15
17
|
ast_node.arguments,
|
16
18
|
field.arguments,
|
@@ -18,7 +18,7 @@ module GraphQL
|
|
18
18
|
result.merge(resolve_field(ast_node))
|
19
19
|
}
|
20
20
|
rescue GraphQL::InvalidNullError => err
|
21
|
-
execution_context.add_error(err)
|
21
|
+
err.parent_error? || execution_context.add_error(err)
|
22
22
|
nil
|
23
23
|
end
|
24
24
|
|
@@ -56,8 +56,11 @@ module GraphQL
|
|
56
56
|
end
|
57
57
|
|
58
58
|
def flatten_fragment(ast_fragment)
|
59
|
-
|
60
|
-
|
59
|
+
if fragment_type_can_apply?(ast_fragment)
|
60
|
+
flatten_and_merge_selections(ast_fragment.selections)
|
61
|
+
else
|
62
|
+
{}
|
63
|
+
end
|
61
64
|
end
|
62
65
|
|
63
66
|
def fragment_type_can_apply?(ast_fragment)
|
@@ -68,18 +71,21 @@ module GraphQL
|
|
68
71
|
|
69
72
|
def merge_fields(field1, field2)
|
70
73
|
field_type = execution_context.get_field(type, field2.name).type.unwrap
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
74
|
+
|
75
|
+
if field_type.kind.fields?
|
76
|
+
# create a new ast field node merging selections from each field.
|
77
|
+
# Because of static validation, we can assume that name, alias,
|
78
|
+
# arguments, and directives are exactly the same for fields 1 and 2.
|
79
|
+
GraphQL::Language::Nodes::Field.new(
|
80
|
+
name: field2.name,
|
81
|
+
alias: field2.alias,
|
82
|
+
arguments: field2.arguments,
|
83
|
+
directives: field2.directives,
|
84
|
+
selections: field1.selections + field2.selections
|
85
|
+
)
|
86
|
+
else
|
87
|
+
field2
|
88
|
+
end
|
83
89
|
end
|
84
90
|
|
85
91
|
def resolve_field(ast_node)
|
@@ -0,0 +1,18 @@
|
|
1
|
+
class GraphQL::Query
|
2
|
+
class VariableValidationError < GraphQL::ExecutionError
|
3
|
+
attr_accessor :value, :validation_result
|
4
|
+
|
5
|
+
def initialize(variable_ast, type, value, validation_result)
|
6
|
+
@value = value
|
7
|
+
@validation_result = validation_result
|
8
|
+
|
9
|
+
msg = "Variable #{variable_ast.name} of type #{type} was provided invalid value"
|
10
|
+
super(msg)
|
11
|
+
self.ast_node = variable_ast
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_h
|
15
|
+
super.merge({ 'value' => value, 'problems' => validation_result.problems })
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -27,14 +27,10 @@ module GraphQL
|
|
27
27
|
default_value = ast_variable.default_value
|
28
28
|
provided_value = @provided_variables[variable_name]
|
29
29
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
raise GraphQL::Query::VariableValidationError.new(ast_variable, variable_type, "was provided invalid value #{JSON.dump(provided_value)}")
|
35
|
-
end
|
36
|
-
end
|
37
|
-
if provided_value.nil?
|
30
|
+
validation_result = variable_type.validate_input(provided_value)
|
31
|
+
if !validation_result.valid?
|
32
|
+
raise GraphQL::Query::VariableValidationError.new(ast_variable, variable_type, provided_value, validation_result)
|
33
|
+
elsif provided_value.nil?
|
38
34
|
GraphQL::Query::LiteralInput.coerce(variable_type, default_value, {})
|
39
35
|
else
|
40
36
|
variable_type.coerce_input(provided_value)
|
data/lib/graphql/scalar_type.rb
CHANGED
@@ -19,8 +19,10 @@ module GraphQL
|
|
19
19
|
self.coerce_result = proc
|
20
20
|
end
|
21
21
|
|
22
|
-
def
|
23
|
-
|
22
|
+
def validate_non_null_input(value)
|
23
|
+
result = Query::InputValidationResult.new
|
24
|
+
result.add_problem("Could not coerce value #{JSON.dump(value)} to #{name}") if coerce_non_null_input(value).nil?
|
25
|
+
result
|
24
26
|
end
|
25
27
|
|
26
28
|
def coerce_non_null_input(value)
|
@@ -28,7 +30,9 @@ module GraphQL
|
|
28
30
|
end
|
29
31
|
|
30
32
|
def coerce_input=(proc)
|
31
|
-
|
33
|
+
if !proc.nil?
|
34
|
+
@coerce_input_proc = proc
|
35
|
+
end
|
32
36
|
end
|
33
37
|
|
34
38
|
def coerce_result(value)
|
@@ -36,7 +40,9 @@ module GraphQL
|
|
36
40
|
end
|
37
41
|
|
38
42
|
def coerce_result=(proc)
|
39
|
-
|
43
|
+
if !proc.nil?
|
44
|
+
@coerce_result_proc = proc
|
45
|
+
end
|
40
46
|
end
|
41
47
|
|
42
48
|
def kind
|
data/lib/graphql/schema.rb
CHANGED
@@ -6,6 +6,7 @@ class GraphQL::Schema
|
|
6
6
|
DYNAMIC_FIELDS = ["__type", "__typename", "__schema"]
|
7
7
|
|
8
8
|
attr_reader :query, :mutation, :subscription, :directives, :static_validator
|
9
|
+
attr_accessor :max_depth
|
9
10
|
# Override these if you don't want the default executor:
|
10
11
|
attr_accessor :query_execution_strategy,
|
11
12
|
:mutation_execution_strategy,
|
@@ -17,10 +18,11 @@ class GraphQL::Schema
|
|
17
18
|
# @param query [GraphQL::ObjectType] the query root for the schema
|
18
19
|
# @param mutation [GraphQL::ObjectType] the mutation root for the schema
|
19
20
|
# @param subscription [GraphQL::ObjectType] the subscription root for the schema
|
20
|
-
def initialize(query:, mutation: nil, subscription: nil)
|
21
|
+
def initialize(query:, mutation: nil, subscription: nil, max_depth: nil)
|
21
22
|
@query = query
|
22
23
|
@mutation = mutation
|
23
24
|
@subscription = subscription
|
25
|
+
@max_depth = max_depth
|
24
26
|
@directives = DIRECTIVES.reduce({}) { |m, d| m[d.name] = d; m }
|
25
27
|
@static_validator = GraphQL::StaticValidation::Validator.new(schema: self)
|
26
28
|
@rescue_middleware = GraphQL::Schema::RescueMiddleware.new
|
@@ -49,7 +51,7 @@ class GraphQL::Schema
|
|
49
51
|
# Resolve field named `field_name` for type `parent_type`.
|
50
52
|
# Handles dynamic fields `__typename`, `__type` and `__schema`, too
|
51
53
|
def get_field(parent_type, field_name)
|
52
|
-
defined_field = parent_type.
|
54
|
+
defined_field = parent_type.get_field(field_name)
|
53
55
|
if defined_field
|
54
56
|
defined_field
|
55
57
|
elsif field_name == "__typename"
|
@@ -48,7 +48,7 @@ module GraphQL
|
|
48
48
|
module TypeKindPrinters
|
49
49
|
module FieldPrinter
|
50
50
|
def print_fields(type)
|
51
|
-
type.
|
51
|
+
type.all_fields.map{ |field| " #{field.name}#{print_args(field)}: #{field.type}" }.join("\n")
|
52
52
|
end
|
53
53
|
|
54
54
|
def print_args(field)
|
@@ -57,7 +57,12 @@ module GraphQL
|
|
57
57
|
end
|
58
58
|
|
59
59
|
def print_input_value(arg)
|
60
|
-
|
60
|
+
if arg.default_value.nil?
|
61
|
+
default_string = nil
|
62
|
+
else
|
63
|
+
default_string = " = #{print_value(arg.default_value, arg.type)}"
|
64
|
+
end
|
65
|
+
|
61
66
|
"#{arg.name}: #{arg.type.to_s}#{default_string}"
|
62
67
|
end
|
63
68
|
|
@@ -98,7 +103,11 @@ module GraphQL
|
|
98
103
|
class ObjectPrinter
|
99
104
|
extend FieldPrinter
|
100
105
|
def self.print(type)
|
101
|
-
|
106
|
+
if type.interfaces.any?
|
107
|
+
implementations = " implements #{type.interfaces.map(&:to_s).join(", ")}"
|
108
|
+
else
|
109
|
+
implementations = nil
|
110
|
+
end
|
102
111
|
"type #{type.name}#{implementations} {\n#{print_fields(type)}\n}"
|
103
112
|
end
|
104
113
|
end
|
@@ -30,7 +30,7 @@ class GraphQL::Schema::TypeReducer
|
|
30
30
|
def find_types(type, type_hash)
|
31
31
|
type_hash[type.name] = type
|
32
32
|
if type.kind.fields?
|
33
|
-
type.
|
33
|
+
type.all_fields.each do |field|
|
34
34
|
reduce_type(field.type, type_hash)
|
35
35
|
field.arguments.each do |name, argument|
|
36
36
|
reduce_type(argument.type, type_hash)
|
@@ -57,10 +57,11 @@ class GraphQL::Schema::TypeReducer
|
|
57
57
|
end
|
58
58
|
|
59
59
|
def reduce_type(type, type_hash)
|
60
|
-
|
60
|
+
if type.is_a?(GraphQL::BaseType)
|
61
|
+
self.class.new(type.unwrap, type_hash).result
|
62
|
+
else
|
61
63
|
raise GraphQL::Schema::InvalidTypeError.new(type, ["Must be a GraphQL::BaseType"])
|
62
64
|
end
|
63
|
-
self.class.new(type.unwrap, type_hash).result
|
64
65
|
end
|
65
66
|
|
66
67
|
def validate_type(type)
|
@@ -5,6 +5,7 @@ require 'graphql/static_validation/message'
|
|
5
5
|
require 'graphql/static_validation/arguments_validator'
|
6
6
|
require 'graphql/static_validation/type_stack'
|
7
7
|
require 'graphql/static_validation/validator'
|
8
|
+
require 'graphql/static_validation/validation_context'
|
8
9
|
require 'graphql/static_validation/literal_validator'
|
9
10
|
|
10
11
|
rules_glob = File.expand_path("../static_validation/rules/*.rb", __FILE__)
|
@@ -20,4 +20,5 @@ GraphQL::StaticValidation::ALL_RULES = [
|
|
20
20
|
GraphQL::StaticValidation::VariableDefaultValuesAreCorrectlyTyped,
|
21
21
|
GraphQL::StaticValidation::VariablesAreUsedAndDefined,
|
22
22
|
GraphQL::StaticValidation::VariableUsagesAreAllowed,
|
23
|
+
GraphQL::StaticValidation::DocumentDoesNotExceedMaxDepth,
|
23
24
|
]
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module GraphQL
|
2
|
+
module StaticValidation
|
3
|
+
class DocumentDoesNotExceedMaxDepth
|
4
|
+
include GraphQL::StaticValidation::Message::MessageHelper
|
5
|
+
|
6
|
+
def validate(context)
|
7
|
+
max_allowed_depth = context.schema.max_depth
|
8
|
+
return if max_allowed_depth.nil?
|
9
|
+
|
10
|
+
visitor = context.visitor
|
11
|
+
|
12
|
+
# operation or fragment name
|
13
|
+
current_field_scope = nil
|
14
|
+
current_depth = 0
|
15
|
+
skip_current_scope = false
|
16
|
+
|
17
|
+
# {name => depth} pairs for operations and fragments
|
18
|
+
depths = Hash.new { |h, k| h[k] = 0 }
|
19
|
+
|
20
|
+
# {name => [fragmentName...]} pairs
|
21
|
+
fragments = Hash.new { |h, k| h[k] = []}
|
22
|
+
|
23
|
+
visitor[GraphQL::Language::Nodes::Document].leave << -> (node, parent) {
|
24
|
+
context.errors.none? && assert_under_max_depth(context, max_allowed_depth, depths, fragments)
|
25
|
+
}
|
26
|
+
|
27
|
+
visitor[GraphQL::Language::Nodes::OperationDefinition] << -> (node, parent) {
|
28
|
+
current_field_scope = node.name
|
29
|
+
}
|
30
|
+
|
31
|
+
visitor[GraphQL::Language::Nodes::FragmentDefinition] << -> (node, parent) {
|
32
|
+
current_field_scope = node.name
|
33
|
+
}
|
34
|
+
|
35
|
+
visitor[GraphQL::Language::Nodes::Field] << -> (node, parent) {
|
36
|
+
# Don't validate queries on __schema, __type
|
37
|
+
skip_current_scope ||= context.skip_field?(node.name)
|
38
|
+
|
39
|
+
if node.selections.any? && !skip_current_scope
|
40
|
+
current_depth += 1
|
41
|
+
if current_depth > depths[current_field_scope]
|
42
|
+
depths[current_field_scope] = current_depth
|
43
|
+
end
|
44
|
+
end
|
45
|
+
}
|
46
|
+
|
47
|
+
visitor[GraphQL::Language::Nodes::Field].leave << -> (node, parent) {
|
48
|
+
if skip_current_scope && context.skip_field?(node.name)
|
49
|
+
skip_current_scope = false
|
50
|
+
elsif node.selections.any?
|
51
|
+
current_depth -= 1
|
52
|
+
end
|
53
|
+
}
|
54
|
+
|
55
|
+
visitor [GraphQL::Language::Nodes::FragmentSpread] << -> (node, parent) {
|
56
|
+
fragments[current_field_scope] << node.name
|
57
|
+
}
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def assert_under_max_depth(context, max_allowed_depth, depths, fragments)
|
63
|
+
context.operations.each do |op_name, operation|
|
64
|
+
op_depth = get_total_depth(op_name, depths, fragments)
|
65
|
+
if op_depth > max_allowed_depth
|
66
|
+
op_name ||= "operation"
|
67
|
+
context.errors << message("#{op_name} has depth of #{op_depth}, which exceeds max depth of #{max_allowed_depth}", operation)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# Get the total depth of a given fragment or operation
|
73
|
+
def get_total_depth(scope_name, depths, fragments)
|
74
|
+
own_fragments = fragments[scope_name]
|
75
|
+
depths[scope_name] + own_fragments.reduce(0) { |memo, frag_name| memo + get_total_depth(frag_name, depths, fragments) }
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|