graphql 0.6.0 → 0.6.1
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/language/parser.rb +11 -2
- data/lib/graphql/language/transform.rb +11 -1
- data/lib/graphql/query/arguments.rb +1 -1
- data/lib/graphql/schema/each_item_validator.rb +8 -3
- data/lib/graphql/schema/implementation_validator.rb +9 -3
- data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +35 -2
- data/lib/graphql/version.rb +1 -1
- data/spec/graphql/language/parser_spec.rb +6 -3
- data/spec/graphql/language/transform_spec.rb +4 -4
- data/spec/graphql/schema/type_validator_spec.rb +10 -0
- data/spec/graphql/static_validation/rules/variable_usages_are_allowed_spec.rb +16 -6
- data/spec/support/dairy_app.rb +0 -2
- 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: 16c0fb0de67cb50d74c16528abcccb26ad91b01f
|
4
|
+
data.tar.gz: 008d40ba5dca1c8fb2c8800f987f02981c64d6f9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2573ce8d132b479472db3a680861289e996ad213c6173ad1fadac0ed3377219a7cc41cf7089149b60e81b2dc75fe5c50a38fc6af51e201744b129fc2bf934019
|
7
|
+
data.tar.gz: 79877bafa23685bedf218e78f9572a1795a30383433943586842ba7605696a71eb391fcf429cc66f6992a9ff29121fff6086d9b428d925541a0f655ef20c90f5
|
@@ -95,8 +95,17 @@ module GraphQL::Language
|
|
95
95
|
rule(:value_input_object) { str("{") >> value_input_object_pair.repeat(1).as(:input_object) >> str("}") }
|
96
96
|
rule(:value_input_object_pair) { space? >> name.as(:input_object_name) >> space? >> str(":") >> space? >> value.as(:input_object_value) >> separator? }
|
97
97
|
rule(:value_int) { (value_sign? >> match('\d').repeat(1)).as(:int) }
|
98
|
-
|
99
|
-
rule(:
|
98
|
+
rule(:value_string) { str('"') >> value_string_char.repeat.as(:string) >> str('"')}
|
99
|
+
rule(:value_string_char) { value_string_escaped_char | value_string_escaped_unicode | value_string_source_char}
|
100
|
+
rule(:value_string_escaped_char) { str("\\") >> match('["\/bfnrt]') }
|
101
|
+
rule(:value_string_escaped_unicode) { str("\\") >> match('u[\dA-Fa-f]{4}')}
|
102
|
+
rule(:value_string_source_char) { (str('"') | str("\\") | value_string_line_terminator).absent? >> any }
|
103
|
+
rule(:value_string_line_terminator) {
|
104
|
+
str('\u000A') | # new line
|
105
|
+
str('\u000D') | # carriage return
|
106
|
+
str('\u2028') | # line separator
|
107
|
+
str('\u2029') # paragraph separator
|
108
|
+
}
|
100
109
|
rule(:value_enum) { name.as(:enum) }
|
101
110
|
rule(:value_variable) { str("$") >> name.as(:variable) }
|
102
111
|
|
@@ -91,7 +91,17 @@ module GraphQL::Language
|
|
91
91
|
rule(input_object_name: simple(:n), input_object_value: sequence(:v)) { create_node(:Argument, name: n.to_s, value: v, position_source: n)}
|
92
92
|
rule(int: simple(:v)) { v.to_i }
|
93
93
|
rule(float: simple(:v)) { v.to_f }
|
94
|
-
|
94
|
+
|
95
|
+
ESCAPES = /\\(["\\\/bfnrt])/
|
96
|
+
UTF_8 = /\\u[\da-f]{4}/i
|
97
|
+
UTF_8_REPLACE = -> (m) { [m[-4..-1].to_i(16)].pack('U') }
|
98
|
+
|
99
|
+
rule(string: simple(:v)) {
|
100
|
+
string = v.to_s
|
101
|
+
string.gsub!(ESCAPES, '\1')
|
102
|
+
string.gsub!(UTF_8, &UTF_8_REPLACE)
|
103
|
+
string
|
104
|
+
}
|
95
105
|
rule(variable: simple(:v)) { create_node(:VariableIdentifier, name: v.to_s, position_source: v) }
|
96
106
|
rule(enum: simple(:v)) { create_node(:Enum, name: v.to_s, position_source: v)}
|
97
107
|
end
|
@@ -4,9 +4,14 @@ class GraphQL::Schema::EachItemValidator
|
|
4
4
|
end
|
5
5
|
|
6
6
|
def validate(items, as:, must_be:)
|
7
|
-
|
8
|
-
|
9
|
-
|
7
|
+
if !items.is_a?(Array)
|
8
|
+
@errors << "#{as} must be an Array, not #{items.inspect}"
|
9
|
+
return
|
10
|
+
else
|
11
|
+
invalid_items = items.select {|k| !yield(k) }
|
12
|
+
if invalid_items.any?
|
13
|
+
@errors << "#{as} must be #{must_be}, but some aren't: #{invalid_items.map(&:to_s).join(", ")}"
|
14
|
+
end
|
10
15
|
end
|
11
16
|
end
|
12
17
|
end
|
@@ -11,11 +11,17 @@ class GraphQL::Schema::ImplementationValidator
|
|
11
11
|
# If `block_given?`, yield the return value of that method
|
12
12
|
# If provided, use `as` in the error message, overriding class-level `as`.
|
13
13
|
def must_respond_to(method_name, args: [], as: nil)
|
14
|
+
local_as = as || implementation_as
|
15
|
+
method_signature = "##{method_name}(#{args.join(", ")})"
|
14
16
|
if !object.respond_to?(method_name)
|
15
|
-
|
16
|
-
errors << "#{object.to_s} must respond to ##{method_name}(#{args.join(", ")}) to be a #{local_as}"
|
17
|
+
errors << "#{object.to_s} must respond to #{method_signature} to be a #{local_as}"
|
17
18
|
elsif block_given?
|
18
|
-
|
19
|
+
return_value = object.public_send(method_name)
|
20
|
+
if return_value.nil?
|
21
|
+
errors << "#{object.to_s} must return a value for #{method_signature} to be a #{local_as}"
|
22
|
+
else
|
23
|
+
yield(return_value)
|
24
|
+
end
|
19
25
|
end
|
20
26
|
end
|
21
27
|
end
|
@@ -30,8 +30,17 @@ class GraphQL::StaticValidation::VariableUsagesAreAllowed
|
|
30
30
|
end
|
31
31
|
|
32
32
|
arg_defn = arguments[arg_node.name]
|
33
|
-
|
34
|
-
|
33
|
+
arg_defn_type = arg_defn.type
|
34
|
+
|
35
|
+
var_inner_type = var_type.kind.unwrap(var_type)
|
36
|
+
arg_inner_type = arg_defn_type.kind.unwrap(arg_defn_type)
|
37
|
+
|
38
|
+
if var_inner_type != arg_inner_type
|
39
|
+
context.errors << create_error("Type mismatch", var_type, ast_var, arg_defn, arg_node)
|
40
|
+
elsif list_dimension(var_type) != list_dimension(arg_defn_type)
|
41
|
+
context.errors << create_error("List dimension mismatch", var_type, ast_var, arg_defn, arg_node)
|
42
|
+
elsif !non_null_levels_match(arg_defn_type, var_type)
|
43
|
+
context.errors << create_error("Nullability mismatch", var_type, ast_var, arg_defn, arg_node)
|
35
44
|
end
|
36
45
|
end
|
37
46
|
|
@@ -44,4 +53,28 @@ class GraphQL::StaticValidation::VariableUsagesAreAllowed
|
|
44
53
|
types[ast_type.name]
|
45
54
|
end
|
46
55
|
end
|
56
|
+
|
57
|
+
def create_error(error_message, var_type, ast_var, arg_defn, arg_node)
|
58
|
+
message("#{error_message} on variable $#{ast_var.name} and argument #{arg_node.name} (#{var_type.to_s} / #{arg_defn.type.to_s})", arg_node)
|
59
|
+
end
|
60
|
+
|
61
|
+
def list_dimension(type)
|
62
|
+
if type.kind.list?
|
63
|
+
1 + list_dimension(type.of_type)
|
64
|
+
elsif type.kind.non_null?
|
65
|
+
list_dimension(type.of_type)
|
66
|
+
else
|
67
|
+
0
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def non_null_levels_match(arg_type, var_type)
|
72
|
+
if arg_type.kind.non_null? && !var_type.kind.non_null?
|
73
|
+
false
|
74
|
+
elsif arg_type.kind.wraps? && var_type.kind.wraps?
|
75
|
+
non_null_levels_match(arg_type.of_type, var_type.of_type)
|
76
|
+
else
|
77
|
+
true
|
78
|
+
end
|
79
|
+
end
|
47
80
|
end
|
data/lib/graphql/version.rb
CHANGED
@@ -59,7 +59,7 @@ describe GraphQL::Language::Parser do
|
|
59
59
|
|
60
60
|
it 'parses directives' do
|
61
61
|
assert(parser.directives.parse_with_debug("@doSomething"), 'gets without argument')
|
62
|
-
assert(parser.directives.parse_with_debug('@doSomething(why: "forSomeReason")'), 'gets with argument')
|
62
|
+
assert(parser.directives.parse_with_debug('@doSomething(why: "\"forSomeReason\"")'), 'gets with argument')
|
63
63
|
assert(parser.directives.parse_with_debug('@myFlag, @doSomething(why: "forSomeReason")'), 'gets multiple')
|
64
64
|
end
|
65
65
|
|
@@ -67,7 +67,7 @@ describe GraphQL::Language::Parser do
|
|
67
67
|
assert(parser.field.parse_with_debug(%|myField { name, id }|), 'gets subselections')
|
68
68
|
assert(parser.field.parse_with_debug(%{myAlias: myField}), 'gets an alias')
|
69
69
|
assert(parser.field.parse_with_debug(%{myField(intKey: 1, floatKey: 1.1e5)}), 'gets arguments')
|
70
|
-
assert(parser.field.parse_with_debug(
|
70
|
+
assert(parser.field.parse_with_debug('myAlias: myField(stringKey: "\"my_string\"", boolKey: false, objKey: {key : true})'), 'gets alias and arguments')
|
71
71
|
assert(parser.field.parse_with_debug(%|myField @withFlag, @skip(if: true) { name, id }|), 'gets with directive')
|
72
72
|
end
|
73
73
|
|
@@ -100,7 +100,10 @@ describe GraphQL::Language::Parser do
|
|
100
100
|
end
|
101
101
|
|
102
102
|
it 'gets strings' do
|
103
|
-
assert(parser.value.parse_with_debug('"my string"'))
|
103
|
+
assert(parser.value.parse_with_debug('"my string"'), "plain strings")
|
104
|
+
assert(parser.value.parse_with_debug('""'), "empty strings")
|
105
|
+
assert(parser.value.parse_with_debug('"\"Hi!\"\n"'), "escaped strings")
|
106
|
+
assert(parser.value.parse_with_debug('"\u0025\u0026"'), "escaped unicode")
|
104
107
|
end
|
105
108
|
|
106
109
|
it 'gets arrays' do
|
@@ -99,20 +99,20 @@ describe GraphQL::Language::Transform do
|
|
99
99
|
assert_equal("SO_COOL", res.arguments[1].value.name)
|
100
100
|
assert_equal({"nice" => {"very" => true}}, res.arguments[2].value.to_h)
|
101
101
|
|
102
|
-
res = get_result(
|
102
|
+
res = get_result('me @flag, @include(if: "\"something\"") {name, id}', parse: :field)
|
103
103
|
assert_equal("me", res.name)
|
104
104
|
assert_equal(nil, res.alias)
|
105
105
|
assert_equal(2, res.directives.length)
|
106
106
|
assert_equal("flag", res.directives.first.name)
|
107
|
-
assert_equal("something", res.directives.last.arguments.first.value)
|
107
|
+
assert_equal('"something"', res.directives.last.arguments.first.value)
|
108
108
|
assert_equal(2, res.selections.length)
|
109
109
|
end
|
110
110
|
|
111
111
|
it 'transforms directives' do
|
112
|
-
res = get_result(
|
112
|
+
res = get_result('@doSomething(vigorously: "\"true\u0025\"")', parse: :directive)
|
113
113
|
assert_equal("doSomething", res.name, 'gets the name without @')
|
114
114
|
assert_equal("vigorously", res.arguments.first.name)
|
115
|
-
assert_equal(true, res.arguments.first.value)
|
115
|
+
assert_equal('"true%"', res.arguments.first.value)
|
116
116
|
|
117
117
|
res = get_result("@someFlag", parse: :directive)
|
118
118
|
assert_equal("someFlag", res.name)
|
@@ -28,6 +28,16 @@ describe GraphQL::Schema::TypeValidator do
|
|
28
28
|
end
|
29
29
|
end
|
30
30
|
|
31
|
+
describe 'when a method returns nil' do
|
32
|
+
let(:type_defn) { base_type_defn.merge(interfaces: nil)}
|
33
|
+
it 'requires name' do
|
34
|
+
assert_equal(
|
35
|
+
["InvalidType must return a value for #interfaces() to be a OBJECT"],
|
36
|
+
errors
|
37
|
+
)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
31
41
|
describe "when a field name isnt a string" do
|
32
42
|
let(:type_defn) { base_type_defn.merge(fields: {symbol_field: (GraphQL::Field.new {|f|}) }) }
|
33
43
|
it "requires string names" do
|
@@ -9,6 +9,7 @@ describe GraphQL::StaticValidation::VariableUsagesAreAllowed do
|
|
9
9
|
$badStr: String!,
|
10
10
|
$goodAnimals: [DairyAnimal!]!,
|
11
11
|
$badAnimals: [DairyAnimal]!,
|
12
|
+
$deepAnimals: [[DairyAnimal!]!]!,
|
12
13
|
) {
|
13
14
|
goodCheese: cheese(id: $goodInt) { source }
|
14
15
|
okCheese: cheese(id: $okInt) { source }
|
@@ -17,6 +18,11 @@ describe GraphQL::StaticValidation::VariableUsagesAreAllowed do
|
|
17
18
|
cheese(id: 1) {
|
18
19
|
similarCheeses(source: $goodAnimals)
|
19
20
|
other: similarCheeses(source: $badAnimals)
|
21
|
+
tooDeep: similarCheeses(source: $deepAnimals)
|
22
|
+
}
|
23
|
+
|
24
|
+
milk(id: 1) {
|
25
|
+
flavors(limit: $okInt)
|
20
26
|
}
|
21
27
|
}
|
22
28
|
')}
|
@@ -25,19 +31,23 @@ describe GraphQL::StaticValidation::VariableUsagesAreAllowed do
|
|
25
31
|
let(:errors) { validator.validate(document) }
|
26
32
|
|
27
33
|
it "finds variables used as arguments but don't match the argument's type" do
|
28
|
-
assert_equal(
|
34
|
+
assert_equal(4, errors.length)
|
29
35
|
expected = [
|
30
36
|
{
|
31
|
-
"message"=>"
|
32
|
-
"locations"=>[{"line"=>
|
37
|
+
"message"=>"Nullability mismatch on variable $badInt and argument id (Int / Int!)",
|
38
|
+
"locations"=>[{"line"=>13, "column"=>28}]
|
33
39
|
},
|
34
40
|
{
|
35
41
|
"message"=>"Type mismatch on variable $badStr and argument id (String! / Int!)",
|
36
|
-
"locations"=>[{"line"=>
|
42
|
+
"locations"=>[{"line"=>14, "column"=>28}]
|
43
|
+
},
|
44
|
+
{
|
45
|
+
"message"=>"Nullability mismatch on variable $badAnimals and argument source ([DairyAnimal]! / [DairyAnimal!]!)",
|
46
|
+
"locations"=>[{"line"=>17, "column"=>31}]
|
37
47
|
},
|
38
48
|
{
|
39
|
-
"message"=>"
|
40
|
-
"locations"=>[{"line"=>
|
49
|
+
"message"=>"List dimension mismatch on variable $deepAnimals and argument source ([[DairyAnimal!]!]! / [DairyAnimal!]!)",
|
50
|
+
"locations"=>[{"line"=>18, "column"=>33}]
|
41
51
|
}
|
42
52
|
]
|
43
53
|
assert_equal(expected, errors)
|
data/spec/support/dairy_app.rb
CHANGED
@@ -140,8 +140,6 @@ ReplaceValuesInputType = GraphQL::InputObjectType.define do
|
|
140
140
|
input_field :values, !types[!types.Int]
|
141
141
|
end
|
142
142
|
|
143
|
-
p ReplaceValuesInputType.input_fields.inspect
|
144
|
-
|
145
143
|
MutationType = GraphQL::ObjectType.define do
|
146
144
|
name "Mutation"
|
147
145
|
description "The root for mutations in this schema"
|
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.6.
|
4
|
+
version: 0.6.1
|
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-08-
|
11
|
+
date: 2015-08-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: parslet
|