graphql 0.10.4 → 0.10.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/graphql/definition_helpers/defined_by_config.rb +1 -1
- data/lib/graphql/input_object_type.rb +1 -0
- data/lib/graphql/language/parser.rb +2 -2
- data/lib/graphql/language/transform.rb +1 -1
- data/lib/graphql/static_validation/arguments_validator.rb +31 -2
- data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +3 -6
- data/lib/graphql/static_validation/rules/arguments_are_defined.rb +3 -1
- data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +4 -0
- data/lib/graphql/static_validation/type_stack.rb +32 -1
- data/lib/graphql/static_validation/validator.rb +9 -0
- data/lib/graphql/version.rb +1 -1
- data/readme.md +8 -7
- data/spec/graphql/enum_type_spec.rb +5 -1
- data/spec/graphql/language/parser_spec.rb +7 -3
- data/spec/graphql/language/transform_spec.rb +19 -0
- data/spec/graphql/static_validation/rules/argument_literals_are_compatible_spec.rb +13 -7
- data/spec/graphql/static_validation/rules/arguments_are_defined_spec.rb +13 -6
- data/spec/graphql/static_validation/rules/variable_usages_are_allowed_spec.rb +9 -4
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6cd1237e5dde15c5cfa1dbd20213f6c2dcadd533
|
4
|
+
data.tar.gz: e45738645ee04b67b211f66b3f7f56b84bd195da
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bc1b9ee393c15f7e9424a144ed6dbf6b3f724e6041236f995668a18cfb7d328d380624441921a16cd50d22cb6a87d8423a75772a9d28d06e0e4610a303c8b4df
|
7
|
+
data.tar.gz: 32205354a511517f5fac3d83b431d633035edc5799b73dc5fbb660f091dbe1de06e582779245a00d8bf11cb8e38164af252205852c990c111f289b20ba8aca9c
|
@@ -66,7 +66,7 @@ module GraphQL::DefinitionHelpers::DefinedByConfig
|
|
66
66
|
|
67
67
|
# For EnumType
|
68
68
|
def value(name, desc = nil, deprecation_reason: nil, value: name)
|
69
|
-
values << GraphQL::EnumType::EnumValue.new(name: name, description:
|
69
|
+
values << GraphQL::EnumType::EnumValue.new(name: name, description: desc, deprecation_reason: deprecation_reason, value: value)
|
70
70
|
end
|
71
71
|
|
72
72
|
# For InputObjectType
|
@@ -10,6 +10,7 @@
|
|
10
10
|
class GraphQL::InputObjectType < GraphQL::BaseType
|
11
11
|
attr_accessor :name, :description, :input_fields
|
12
12
|
defined_by_config :name, :description, :input_fields
|
13
|
+
alias :arguments :input_fields
|
13
14
|
|
14
15
|
def input_fields=(new_fields)
|
15
16
|
@input_fields = GraphQL::DefinitionHelpers::StringNamedHash.new(new_fields).to_h
|
@@ -64,7 +64,7 @@ module GraphQL
|
|
64
64
|
}
|
65
65
|
|
66
66
|
rule(:field_alias) { name.as(:alias_name) >> space? >> str(":") >> space? }
|
67
|
-
rule(:field_arguments) { str("(") >> field_argument.repeat(1) >> str(")") }
|
67
|
+
rule(:field_arguments) { str("(") >> space? >> field_argument.repeat(1) >> space? >> str(")") }
|
68
68
|
rule(:field_argument) { name.as(:field_argument_name) >> str(":") >> space? >> value.as(:field_argument_value) >> separator? }
|
69
69
|
|
70
70
|
rule(:directives) { (directive >> separator?).repeat(1) }
|
@@ -94,7 +94,7 @@ module GraphQL
|
|
94
94
|
rule(:value_array) { (str("[") >> space? >> (value >> separator?).repeat(0) >> str("]")).as(:array) }
|
95
95
|
rule(:value_boolean) { (str("true") | str("false")).as(:boolean) }
|
96
96
|
rule(:value_float) { (value_sign? >> match('\d').repeat(1) >> str(".") >> match('\d').repeat(1) >> (match("[eE]") >> value_sign? >> match('\d').repeat(1)).maybe).as(:float) }
|
97
|
-
rule(:value_input_object) { str("{") >> space? >> value_input_object_pair.repeat(
|
97
|
+
rule(:value_input_object) { str("{") >> space? >> value_input_object_pair.repeat(0).as(:input_object) >> space? >> str("}") }
|
98
98
|
rule(:value_input_object_pair) { space? >> name.as(:input_object_name) >> space? >> str(":") >> space? >> value.as(:input_object_value) >> separator? }
|
99
99
|
rule(:value_int) { (value_sign? >> match('\d').repeat(1)).as(:int) }
|
100
100
|
rule(:value_string) { str('"') >> value_string_char.repeat.maybe.as(:optional_string_content).as(:string) >> str('"')}
|
@@ -95,7 +95,7 @@ module GraphQL
|
|
95
95
|
rule(array: sequence(:v)) { v }
|
96
96
|
rule(array: simple(:v)) { [] } # just `nil`
|
97
97
|
rule(boolean: simple(:v)) { v == "true" ? true : false }
|
98
|
-
rule(input_object: sequence(:v)) { create_node(:InputObject, pairs: v, line: v.first.line, col: v.first.col) }
|
98
|
+
rule(input_object: sequence(:v)) { create_node(:InputObject, pairs: v, line: (v.first ? v.first.line : 1), col: (v.first ? v.first.col : 1)) }
|
99
99
|
rule(input_object_name: simple(:n), input_object_value: simple(:v)) { create_node(:Argument, name: n.to_s, value: v, position_source: n)}
|
100
100
|
rule(input_object_name: simple(:n), input_object_value: sequence(:v)) { create_node(:Argument, name: n.to_s, value: v, position_source: n)}
|
101
101
|
rule(int: simple(:v)) { v.to_i }
|
@@ -5,8 +5,21 @@ class GraphQL::StaticValidation::ArgumentsValidator
|
|
5
5
|
def validate(context)
|
6
6
|
visitor = context.visitor
|
7
7
|
visitor[GraphQL::Language::Nodes::Argument] << -> (node, parent) {
|
8
|
-
|
9
|
-
|
8
|
+
if parent.is_a?(GraphQL::Language::Nodes::InputObject)
|
9
|
+
arg_defn = context.argument_definition
|
10
|
+
if arg_defn.nil?
|
11
|
+
return
|
12
|
+
else
|
13
|
+
parent_defn = arg_defn.type.unwrap
|
14
|
+
puts "#{node}"
|
15
|
+
puts "#{parent_defn}"
|
16
|
+
if parent_defn.is_a?(GraphQL::ScalarType)
|
17
|
+
return
|
18
|
+
end
|
19
|
+
end
|
20
|
+
elsif context.skip_field?(parent.name)
|
21
|
+
return
|
22
|
+
elsif parent.is_a?(GraphQL::Language::Nodes::Directive)
|
10
23
|
parent_defn = context.schema.directives[parent.name]
|
11
24
|
else
|
12
25
|
parent_defn = context.field_definition
|
@@ -14,4 +27,20 @@ class GraphQL::StaticValidation::ArgumentsValidator
|
|
14
27
|
validate_node(parent, node, parent_defn, context)
|
15
28
|
}
|
16
29
|
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def parent_name(parent, type_defn)
|
34
|
+
field_name = if parent.is_a?(GraphQL::Language::Nodes::Field)
|
35
|
+
parent.alias || parent.name
|
36
|
+
elsif parent.is_a?(GraphQL::Language::Nodes::InputObject)
|
37
|
+
type_defn.name
|
38
|
+
else
|
39
|
+
parent.name
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def node_type(parent)
|
44
|
+
parent.class.name.split("::").last
|
45
|
+
end
|
17
46
|
end
|
@@ -5,12 +5,9 @@ class GraphQL::StaticValidation::ArgumentLiteralsAreCompatible < GraphQL::Static
|
|
5
5
|
arg_defn = defn.arguments[node.name]
|
6
6
|
valid = validator.validate(node.value, arg_defn.type)
|
7
7
|
if !valid
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
parent.name
|
12
|
-
end
|
13
|
-
context.errors << message("Argument #{node.name} on #{parent.class.name.split("::").last} '#{field_name}' has an invalid value", parent)
|
8
|
+
kind_of_node = node_type(parent)
|
9
|
+
error_arg_name = parent_name(parent, defn)
|
10
|
+
context.errors << message("Argument '#{node.name}' on #{kind_of_node} '#{error_arg_name}' has an invalid value", parent)
|
14
11
|
end
|
15
12
|
end
|
16
13
|
end
|
@@ -2,7 +2,9 @@ class GraphQL::StaticValidation::ArgumentsAreDefined < GraphQL::StaticValidation
|
|
2
2
|
def validate_node(parent, node, defn, context)
|
3
3
|
argument_defn = defn.arguments[node.name]
|
4
4
|
if argument_defn.nil?
|
5
|
-
|
5
|
+
kind_of_node = node_type(parent)
|
6
|
+
error_arg_name = parent_name(parent, defn)
|
7
|
+
context.errors << message("#{kind_of_node} '#{error_arg_name}' doesn't accept argument '#{node.name}'", parent)
|
6
8
|
GraphQL::Language::Visitor::SKIP
|
7
9
|
else
|
8
10
|
nil
|
@@ -15,6 +15,10 @@ class GraphQL::StaticValidation::VariableUsagesAreAllowed
|
|
15
15
|
arguments = context.field_definition.arguments
|
16
16
|
elsif parent.is_a?(GraphQL::Language::Nodes::Directive)
|
17
17
|
arguments = context.directive_definition.arguments
|
18
|
+
elsif parent.is_a?(GraphQL::Language::Nodes::InputObject)
|
19
|
+
arguments = context.argument_definition.type.unwrap.input_fields
|
20
|
+
else
|
21
|
+
raise("Unexpected argument parent: #{parent}")
|
18
22
|
end
|
19
23
|
var_defn_ast = declared_variables[node.value.name]
|
20
24
|
# Might be undefined :(
|
@@ -24,6 +24,9 @@ class GraphQL::StaticValidation::TypeStack
|
|
24
24
|
# @return [Array<GraphQL::Node::Directive>] directives which have been entered
|
25
25
|
attr_reader :directive_definitions
|
26
26
|
|
27
|
+
# @return [Array<GraphQL::Node::Argument>] arguments which have been entered
|
28
|
+
attr_reader :argument_definitions
|
29
|
+
|
27
30
|
# @param schema [GraphQL::Schema] the schema whose types to use when climbing this document
|
28
31
|
# @param visitor [GraphQL::Language::Visitor] a visitor to follow & watch the types
|
29
32
|
def initialize(schema, visitor)
|
@@ -31,6 +34,7 @@ class GraphQL::StaticValidation::TypeStack
|
|
31
34
|
@object_types = []
|
32
35
|
@field_definitions = []
|
33
36
|
@directive_definitions = []
|
37
|
+
@argument_definitions = []
|
34
38
|
visitor.enter << -> (node, parent) { PUSH_STRATEGIES[node.class].push(self, node) }
|
35
39
|
visitor.leave << -> (node, parent) { PUSH_STRATEGIES[node.class].pop(self, node) }
|
36
40
|
end
|
@@ -65,7 +69,7 @@ class GraphQL::StaticValidation::TypeStack
|
|
65
69
|
|
66
70
|
class OperationDefinitionStrategy
|
67
71
|
def push(stack, node)
|
68
|
-
#
|
72
|
+
# eg, QueryType, MutationType
|
69
73
|
object_type = stack.schema.public_send(node.operation_type)
|
70
74
|
stack.object_types.push(object_type)
|
71
75
|
end
|
@@ -110,6 +114,33 @@ class GraphQL::StaticValidation::TypeStack
|
|
110
114
|
end
|
111
115
|
end
|
112
116
|
|
117
|
+
class ArgumentStrategy
|
118
|
+
# Push `argument_defn` onto the stack.
|
119
|
+
# It's possible that `argument_defn` will be nil.
|
120
|
+
# Push it anyways so `pop` has something to pop.
|
121
|
+
def push(stack, node)
|
122
|
+
if stack.argument_definitions.last
|
123
|
+
arg_type = stack.argument_definitions.last.type.unwrap
|
124
|
+
if arg_type.kind.input_object?
|
125
|
+
argument_defn = arg_type.input_fields[node.name]
|
126
|
+
else
|
127
|
+
argument_defn = nil
|
128
|
+
end
|
129
|
+
elsif stack.directive_definitions.last
|
130
|
+
argument_defn = stack.directive_definitions.last.arguments[node.name]
|
131
|
+
elsif stack.field_definitions.last
|
132
|
+
argument_defn = stack.field_definitions.last.arguments[node.name]
|
133
|
+
else
|
134
|
+
argument_defn = nil
|
135
|
+
end
|
136
|
+
stack.argument_definitions.push(argument_defn)
|
137
|
+
end
|
138
|
+
|
139
|
+
def pop(stack, node)
|
140
|
+
stack.argument_definitions.pop
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
113
144
|
# A no-op strategy (don't handle this node)
|
114
145
|
class NullStrategy
|
115
146
|
def self.new; self; end
|
@@ -54,14 +54,23 @@ class GraphQL::StaticValidation::Validator
|
|
54
54
|
@type_stack.object_types
|
55
55
|
end
|
56
56
|
|
57
|
+
# @return [GraphQL::Field, nil] The most-recently-entered GraphQL::Field, if currently inside one
|
57
58
|
def field_definition
|
58
59
|
@type_stack.field_definitions.last
|
59
60
|
end
|
60
61
|
|
62
|
+
# @return [GraphQL::Directive, nil] The most-recently-entered GraphQL::Directive, if currently inside one
|
61
63
|
def directive_definition
|
62
64
|
@type_stack.directive_definitions.last
|
63
65
|
end
|
64
66
|
|
67
|
+
# @return [GraphQL::Argument, nil] The most-recently-entered GraphQL::Argument, if currently inside one
|
68
|
+
def argument_definition
|
69
|
+
# Don't get the _last_ one because that's the current one.
|
70
|
+
# Get the second-to-last one, which is the parent of the current one.
|
71
|
+
@type_stack.argument_definitions[-2]
|
72
|
+
end
|
73
|
+
|
65
74
|
# Don't try to validate dynamic fields
|
66
75
|
# since they aren't defined by the type system
|
67
76
|
def skip_field?(field_name)
|
data/lib/graphql/version.rb
CHANGED
data/readme.md
CHANGED
@@ -96,8 +96,7 @@ If you're building a backend for [Relay](http://facebook.github.io/relay/), you'
|
|
96
96
|
## To Do
|
97
97
|
|
98
98
|
- Code clean-up
|
99
|
-
- Raise if you try to configure an attribute which doesn't suit the type
|
100
|
-
- ie, if you try to define `resolve` on an ObjectType, it should somehow raise
|
99
|
+
- Raise if you try to configure an attribute which doesn't suit the type (ie, if you try to define `resolve` on an ObjectType, it should somehow raise)
|
101
100
|
- Clean up file structure in `lib/query` (don't need serial_execution namespace anymore)
|
102
101
|
- Overriding `!` on types breaks ActiveSupport `.blank?`
|
103
102
|
|
@@ -109,17 +108,19 @@ If you're building a backend for [Relay](http://facebook.github.io/relay/), you'
|
|
109
108
|
my_type.blank?
|
110
109
|
# => MyType!
|
111
110
|
```
|
111
|
+
|
112
|
+
- Accept strings for circular type references
|
113
|
+
- Interface's possible types should be a property of the schema, not the interface
|
112
114
|
- Statically validate type of variables (see early return in LiteralValidator)
|
113
115
|
- Big ideas:
|
114
|
-
- Use [graphql-parser](https://github.com/shopify/graphql-parser) (Ruby bindings for [libgraphqlparser](https://github.com/graphql/libgraphqlparser)) instead of Parslet
|
116
|
+
- Use [graphql-parser](https://github.com/shopify/graphql-parser) (Ruby bindings for [libgraphqlparser](https://github.com/graphql/libgraphqlparser)) instead of Parslet ([underway-ish](https://github.com/rmosolgo/graphql-libgraphqlparser-ruby))
|
115
117
|
- Revamp the fixture Schema to be more useful (better names, more extensible)
|
116
118
|
- __Subscriptions__
|
117
119
|
- This is a good chance to make an `Operation` abstraction of which `query`, `mutation` and `subscription` are members
|
118
120
|
- For a subscription, `graphql` would send an outbound message to the system (allow the host application to manage its own subscriptions via Pusher, ActionCable, whatever)
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
- Inline variables?
|
121
|
+
- Documentation
|
122
|
+
- Write a "Getting started with Rails"-type blog post
|
123
|
+
- Compile existing articles & slide decks and link to them from a guide
|
123
124
|
|
124
125
|
## Goals
|
125
126
|
|
@@ -8,8 +8,12 @@ describe GraphQL::EnumType do
|
|
8
8
|
assert_equal(1, enum.coerce_input("COW"))
|
9
9
|
end
|
10
10
|
|
11
|
-
it
|
11
|
+
it "coerces result values to value's value" do
|
12
12
|
assert_equal("YAK", enum.coerce_result("YAK"))
|
13
13
|
assert_equal("COW", enum.coerce_result(1))
|
14
14
|
end
|
15
|
+
|
16
|
+
it 'has value description' do
|
17
|
+
assert_equal("Animal with horns", enum.values['GOAT'].description)
|
18
|
+
end
|
15
19
|
end
|
@@ -15,7 +15,9 @@ describe GraphQL::Language::Parser do
|
|
15
15
|
$input: SomeInputType = {key: "value"},
|
16
16
|
) @veggie, @healthy(vitamins: true) {
|
17
17
|
# change the cucumber
|
18
|
-
changeStuff(
|
18
|
+
changeStuff(
|
19
|
+
thing: $cucumbers
|
20
|
+
) {
|
19
21
|
id,
|
20
22
|
name,
|
21
23
|
... on Species { color },
|
@@ -23,13 +25,13 @@ describe GraphQL::Language::Parser do
|
|
23
25
|
}
|
24
26
|
}
|
25
27
|
subscription watchStuff {
|
26
|
-
field, otherField
|
28
|
+
field(emptyObj: {}), otherField
|
27
29
|
}
|
28
30
|
|
29
31
|
# a fragment:
|
30
32
|
fragment family on Species {
|
31
33
|
family {
|
32
|
-
name,
|
34
|
+
name, # name of the family
|
33
35
|
members(first: 3, query: {isPlant: true}) # some of the other examples
|
34
36
|
}
|
35
37
|
}
|
@@ -125,6 +127,8 @@ describe GraphQL::Language::Parser do
|
|
125
127
|
assert(parser.value.parse_with_debug('{name: "tomato", calories: 50}'), 'gets scalar values')
|
126
128
|
assert(parser.value.parse_with_debug('{listOfValues: [1, 2, [3]], nestedObject: {nestedKey: "nested{Value}"}}'), 'gets complex values')
|
127
129
|
assert(parser.value.parse_with_debug('{variableKey: $variableValue}'), 'gets variables')
|
130
|
+
assert(parser.value.parse_with_debug('{}'), 'gets empty')
|
131
|
+
assert(parser.value.parse_with_debug('{ }'), 'gets empty')
|
128
132
|
end
|
129
133
|
|
130
134
|
it 'gets enums' do
|
@@ -23,6 +23,7 @@ describe GraphQL::Language::Transform do
|
|
23
23
|
...personInfo
|
24
24
|
someStuff(vars: [1,2,3])
|
25
25
|
someOtherStuff(input: {ints: [1,2,3]})
|
26
|
+
someEmptyStuff(emptyObj: {}, emptySpaceObj: { })
|
26
27
|
}
|
27
28
|
}
|
28
29
|
|
@@ -109,6 +110,24 @@ describe GraphQL::Language::Transform do
|
|
109
110
|
assert_equal(2, res.selections.length)
|
110
111
|
end
|
111
112
|
|
113
|
+
it 'transforms input objects' do
|
114
|
+
res_one_pair = get_result(%q|{one: 1}|, parse: :value_input_object)
|
115
|
+
res_two_pair = get_result(%q|{first: "Apple", second: "Banana"}|, parse: :value_input_object)
|
116
|
+
res_empty = get_result(%q|{}|, parse: :value_input_object)
|
117
|
+
res_empty_space = get_result(%q|{ }|, parse: :value_input_object)
|
118
|
+
|
119
|
+
assert_equal('one', res_one_pair.pairs[0].name)
|
120
|
+
assert_equal(1 , res_one_pair.pairs[0].value)
|
121
|
+
|
122
|
+
assert_equal('first' , res_two_pair.pairs[0].name)
|
123
|
+
assert_equal('Apple' , res_two_pair.pairs[0].value)
|
124
|
+
assert_equal('second', res_two_pair.pairs[1].name)
|
125
|
+
assert_equal('Banana', res_two_pair.pairs[1].value)
|
126
|
+
|
127
|
+
assert_equal([], res_empty.pairs)
|
128
|
+
assert_equal([], res_empty_space.pairs)
|
129
|
+
end
|
130
|
+
|
112
131
|
it 'transforms directives' do
|
113
132
|
res = get_result('@doSomething(vigorously: "\"true\u0025\"")', parse: :directive)
|
114
133
|
assert_equal("doSomething", res.name, 'gets the name without @')
|
@@ -5,7 +5,7 @@ describe GraphQL::StaticValidation::ArgumentLiteralsAreCompatible do
|
|
5
5
|
query getCheese {
|
6
6
|
cheese(id: "aasdlkfj") { source }
|
7
7
|
cheese(id: 1) { source @skip(if: {id: 1})}
|
8
|
-
yakSource: searchDairy(product: [{source:
|
8
|
+
yakSource: searchDairy(product: [{source: COW, fatContent: 1.1}]) { source }
|
9
9
|
badSource: searchDairy(product: [{source: 1.1}]) { source }
|
10
10
|
missingSource: searchDairy(product: [{fatContent: 1.1}]) { source }
|
11
11
|
}
|
@@ -19,34 +19,40 @@ describe GraphQL::StaticValidation::ArgumentLiteralsAreCompatible do
|
|
19
19
|
let(:errors) { validator.validate(document) }
|
20
20
|
|
21
21
|
it 'finds undefined or missing-required arguments to fields and directives' do
|
22
|
-
assert_equal(
|
22
|
+
assert_equal(6, errors.length)
|
23
23
|
|
24
24
|
query_root_error = {
|
25
|
-
"message"=>"Argument id on Field 'cheese' has an invalid value",
|
25
|
+
"message"=>"Argument 'id' on Field 'cheese' has an invalid value",
|
26
26
|
"locations"=>[{"line"=>3, "column"=>7}]
|
27
27
|
}
|
28
28
|
assert_includes(errors, query_root_error)
|
29
29
|
|
30
30
|
directive_error = {
|
31
|
-
"message"=>"Argument if on Directive 'skip' has an invalid value",
|
31
|
+
"message"=>"Argument 'if' on Directive 'skip' has an invalid value",
|
32
32
|
"locations"=>[{"line"=>4, "column"=>31}]
|
33
33
|
}
|
34
34
|
assert_includes(errors, directive_error)
|
35
35
|
|
36
36
|
input_object_error = {
|
37
|
-
"message"=>"Argument product on Field 'badSource' has an invalid value",
|
37
|
+
"message"=>"Argument 'product' on Field 'badSource' has an invalid value",
|
38
38
|
"locations"=>[{"line"=>6, "column"=>7}]
|
39
39
|
}
|
40
40
|
assert_includes(errors, input_object_error)
|
41
41
|
|
42
|
+
input_object_field_error = {
|
43
|
+
"message"=>"Argument 'source' on InputObject 'DairyProductInput' has an invalid value",
|
44
|
+
"locations"=>[{"line"=>6, "column"=>41}]
|
45
|
+
}
|
46
|
+
assert_includes(errors, input_object_field_error)
|
47
|
+
|
42
48
|
missing_required_field_error = {
|
43
|
-
"message"=>"Argument product on Field 'missingSource' has an invalid value",
|
49
|
+
"message"=>"Argument 'product' on Field 'missingSource' has an invalid value",
|
44
50
|
"locations"=>[{"line"=>7, "column"=>7}]
|
45
51
|
}
|
46
52
|
assert_includes(errors, missing_required_field_error)
|
47
53
|
|
48
54
|
fragment_error = {
|
49
|
-
"message"=>"Argument source on Field 'similarCheese' has an invalid value",
|
55
|
+
"message"=>"Argument 'source' on Field 'similarCheese' has an invalid value",
|
50
56
|
"locations"=>[{"line"=>11, "column"=>7}]
|
51
57
|
}
|
52
58
|
assert_includes(errors, fragment_error)
|
@@ -5,6 +5,7 @@ describe GraphQL::StaticValidation::ArgumentsAreDefined do
|
|
5
5
|
query getCheese {
|
6
6
|
cheese(id: 1) { source }
|
7
7
|
cheese(silly: false) { source }
|
8
|
+
searchDairy(product: [{wacky: 1}])
|
8
9
|
}
|
9
10
|
|
10
11
|
fragment cheeseFields on Cheese {
|
@@ -17,23 +18,29 @@ describe GraphQL::StaticValidation::ArgumentsAreDefined do
|
|
17
18
|
let(:errors) { validator.validate(document) }
|
18
19
|
|
19
20
|
it 'finds undefined arguments to fields and directives' do
|
20
|
-
assert_equal(
|
21
|
+
assert_equal(4, errors.length)
|
21
22
|
|
22
23
|
query_root_error = {
|
23
|
-
"message"=>"Field 'cheese' doesn't accept argument silly",
|
24
|
+
"message"=>"Field 'cheese' doesn't accept argument 'silly'",
|
24
25
|
"locations"=>[{"line"=>4, "column"=>7}]
|
25
26
|
}
|
26
27
|
assert_includes(errors, query_root_error)
|
27
28
|
|
29
|
+
input_obj_record = {
|
30
|
+
"message"=>"InputObject 'DairyProductInput' doesn't accept argument 'wacky'",
|
31
|
+
"locations"=>[{"line"=>5, "column"=>30}]
|
32
|
+
}
|
33
|
+
assert_includes(errors, input_obj_record)
|
34
|
+
|
28
35
|
fragment_error = {
|
29
|
-
"message"=>"Field 'similarCheese' doesn't accept argument nonsense",
|
30
|
-
"locations"=>[{"line"=>
|
36
|
+
"message"=>"Field 'similarCheese' doesn't accept argument 'nonsense'",
|
37
|
+
"locations"=>[{"line"=>9, "column"=>7}]
|
31
38
|
}
|
32
39
|
assert_includes(errors, fragment_error)
|
33
40
|
|
34
41
|
directive_error = {
|
35
|
-
"message"=>"Directive 'skip' doesn't accept argument something",
|
36
|
-
"locations"=>[{"line"=>
|
42
|
+
"message"=>"Directive 'skip' doesn't accept argument 'something'",
|
43
|
+
"locations"=>[{"line"=>10, "column"=>11}]
|
37
44
|
}
|
38
45
|
assert_includes(errors, directive_error)
|
39
46
|
end
|
@@ -10,6 +10,7 @@ describe GraphQL::StaticValidation::VariableUsagesAreAllowed do
|
|
10
10
|
$goodAnimals: [DairyAnimal!]!,
|
11
11
|
$badAnimals: [DairyAnimal]!,
|
12
12
|
$deepAnimals: [[DairyAnimal!]!]!,
|
13
|
+
$goodSource: DairyAnimal!,
|
13
14
|
) {
|
14
15
|
goodCheese: cheese(id: $goodInt) { source }
|
15
16
|
okCheese: cheese(id: $okInt) { source }
|
@@ -24,6 +25,10 @@ describe GraphQL::StaticValidation::VariableUsagesAreAllowed do
|
|
24
25
|
milk(id: 1) {
|
25
26
|
flavors(limit: $okInt)
|
26
27
|
}
|
28
|
+
|
29
|
+
searchDairy(product: [{source: $goodSource}]) {
|
30
|
+
... on Cheese { id }
|
31
|
+
}
|
27
32
|
}
|
28
33
|
')}
|
29
34
|
|
@@ -35,19 +40,19 @@ describe GraphQL::StaticValidation::VariableUsagesAreAllowed do
|
|
35
40
|
expected = [
|
36
41
|
{
|
37
42
|
"message"=>"Nullability mismatch on variable $badInt and argument id (Int / Int!)",
|
38
|
-
"locations"=>[{"line"=>
|
43
|
+
"locations"=>[{"line"=>14, "column"=>28}]
|
39
44
|
},
|
40
45
|
{
|
41
46
|
"message"=>"Type mismatch on variable $badStr and argument id (String! / Int!)",
|
42
|
-
"locations"=>[{"line"=>
|
47
|
+
"locations"=>[{"line"=>15, "column"=>28}]
|
43
48
|
},
|
44
49
|
{
|
45
50
|
"message"=>"Nullability mismatch on variable $badAnimals and argument source ([DairyAnimal]! / [DairyAnimal!]!)",
|
46
|
-
"locations"=>[{"line"=>
|
51
|
+
"locations"=>[{"line"=>18, "column"=>30}]
|
47
52
|
},
|
48
53
|
{
|
49
54
|
"message"=>"List dimension mismatch on variable $deepAnimals and argument source ([[DairyAnimal!]!]! / [DairyAnimal!]!)",
|
50
|
-
"locations"=>[{"line"=>
|
55
|
+
"locations"=>[{"line"=>19, "column"=>32}]
|
51
56
|
}
|
52
57
|
]
|
53
58
|
assert_equal(expected, errors)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: graphql
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.10.
|
4
|
+
version: 0.10.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Robert Mosolgo
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-12-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: parslet
|