graphql 0.10.4 → 0.10.5
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/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
|