graphql 0.6.0 → 0.6.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c92fe281f2beeab6fd1e4c6165f07c5156ccb09a
4
- data.tar.gz: 309513164d1b60381f7283592fb3f83f4105fb9b
3
+ metadata.gz: 16c0fb0de67cb50d74c16528abcccb26ad91b01f
4
+ data.tar.gz: 008d40ba5dca1c8fb2c8800f987f02981c64d6f9
5
5
  SHA512:
6
- metadata.gz: 0a7345b8975ac69de12cae806c9b0f72bb9ada248a6fedf0f948113573676c4c938b090ff37b8f7b4cc5a3bcc661aadb5239700a832ab5ad9746d911f69cf198
7
- data.tar.gz: dc45fdc6b9670f353fa37be7fe1a019d39874f78f89e33ec971dc61119e40e88a4837092bb1ecaa6861623c53c18e750a39d19006a8c5bc15edf1c2f5d18e47f
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
- # TODO: support unicode, escaped chars (match the spec)
99
- rule(:value_string) { str('"') >> match('[^\"]').repeat(1).as(:string) >> str('"')}
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
- rule(string: simple(:v)) { v.to_s }
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
@@ -13,7 +13,7 @@ class GraphQL::Query::Arguments
13
13
  end
14
14
  end
15
15
 
16
- def_delegators :@hash, :keys, :values
16
+ def_delegators :@hash, :keys, :values, :inspect, :to_h
17
17
 
18
18
  def [](key)
19
19
  @hash[key.to_s]
@@ -4,9 +4,14 @@ class GraphQL::Schema::EachItemValidator
4
4
  end
5
5
 
6
6
  def validate(items, as:, must_be:)
7
- invalid_items = items.select {|k| !yield(k) }
8
- if invalid_items.any?
9
- @errors << "#{as} must be #{must_be}, but some aren't: #{invalid_items.map(&:to_s).join(", ")}"
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
- local_as = as || implementation_as
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
- yield(object.public_send(method_name))
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
- if var_type != arg_defn.type
34
- context.errors << message("Type mismatch on variable $#{ast_var.name} and argument #{arg_node.name} (#{var_type.to_s} / #{arg_defn.type.to_s})", arg_node)
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
@@ -1,3 +1,3 @@
1
1
  module GraphQL
2
- VERSION = "0.6.0"
2
+ VERSION = "0.6.1"
3
3
  end
@@ -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(%{myAlias: myField(stringKey: "my_string", boolKey: false, objKey: {key : true})}), 'gets alias and arguments')
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(%|me @flag, @include(if: "something") {name, id}|, parse: :field)
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("@doSomething(vigorously: true)", parse: :directive)
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(3, errors.length)
34
+ assert_equal(4, errors.length)
29
35
  expected = [
30
36
  {
31
- "message"=>"Type mismatch on variable $badInt and argument id (Int / Int!)",
32
- "locations"=>[{"line"=>12, "column"=>28}]
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"=>13, "column"=>28}]
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"=>"Type mismatch on variable $badAnimals and argument source ([DairyAnimal]! / [DairyAnimal!]!)",
40
- "locations"=>[{"line"=>16, "column"=>31}]
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)
@@ -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.0
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-14 00:00:00.000000000 Z
11
+ date: 2015-08-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: parslet