graphql 1.1.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (79) hide show
  1. checksums.yaml +4 -4
  2. data/lib/graphql/analysis/analyze_query.rb +4 -2
  3. data/lib/graphql/analysis/field_usage.rb +4 -4
  4. data/lib/graphql/analysis/query_complexity.rb +16 -21
  5. data/lib/graphql/argument.rb +13 -6
  6. data/lib/graphql/base_type.rb +2 -1
  7. data/lib/graphql/compatibility/execution_specification.rb +76 -0
  8. data/lib/graphql/compatibility/query_parser_specification.rb +16 -2
  9. data/lib/graphql/compatibility/query_parser_specification/parse_error_specification.rb +0 -5
  10. data/lib/graphql/compatibility/schema_parser_specification.rb +6 -0
  11. data/lib/graphql/define/assign_argument.rb +8 -2
  12. data/lib/graphql/define/instance_definable.rb +12 -15
  13. data/lib/graphql/directive.rb +2 -1
  14. data/lib/graphql/enum_type.rb +5 -7
  15. data/lib/graphql/field.rb +6 -11
  16. data/lib/graphql/field/resolve.rb +1 -0
  17. data/lib/graphql/input_object_type.rb +9 -9
  18. data/lib/graphql/interface_type.rb +2 -1
  19. data/lib/graphql/internal_representation.rb +1 -0
  20. data/lib/graphql/internal_representation/node.rb +31 -9
  21. data/lib/graphql/internal_representation/rewrite.rb +26 -26
  22. data/lib/graphql/internal_representation/selections.rb +41 -0
  23. data/lib/graphql/introspection/input_value_type.rb +6 -2
  24. data/lib/graphql/language/generation.rb +2 -0
  25. data/lib/graphql/language/lexer.rl +4 -0
  26. data/lib/graphql/language/nodes.rb +3 -0
  27. data/lib/graphql/language/parser.rb +525 -509
  28. data/lib/graphql/language/parser.y +2 -0
  29. data/lib/graphql/object_type.rb +2 -2
  30. data/lib/graphql/query.rb +21 -0
  31. data/lib/graphql/query/context.rb +52 -4
  32. data/lib/graphql/query/serial_execution.rb +3 -4
  33. data/lib/graphql/query/serial_execution/field_resolution.rb +35 -36
  34. data/lib/graphql/query/serial_execution/operation_resolution.rb +9 -15
  35. data/lib/graphql/query/serial_execution/selection_resolution.rb +14 -11
  36. data/lib/graphql/query/serial_execution/value_resolution.rb +18 -17
  37. data/lib/graphql/query/variables.rb +1 -1
  38. data/lib/graphql/relay/mutation.rb +5 -8
  39. data/lib/graphql/scalar_type.rb +1 -2
  40. data/lib/graphql/schema.rb +2 -13
  41. data/lib/graphql/schema/build_from_definition.rb +28 -13
  42. data/lib/graphql/schema/loader.rb +4 -1
  43. data/lib/graphql/schema/printer.rb +10 -3
  44. data/lib/graphql/schema/timeout_middleware.rb +18 -2
  45. data/lib/graphql/schema/unique_within_type.rb +6 -3
  46. data/lib/graphql/static_validation/literal_validator.rb +3 -1
  47. data/lib/graphql/union_type.rb +1 -2
  48. data/lib/graphql/version.rb +1 -1
  49. data/readme.md +1 -0
  50. data/spec/graphql/analysis/analyze_query_spec.rb +6 -8
  51. data/spec/graphql/argument_spec.rb +18 -0
  52. data/spec/graphql/define/assign_argument_spec.rb +48 -0
  53. data/spec/graphql/define/instance_definable_spec.rb +4 -2
  54. data/spec/graphql/execution_error_spec.rb +66 -0
  55. data/spec/graphql/input_object_type_spec.rb +81 -0
  56. data/spec/graphql/internal_representation/rewrite_spec.rb +104 -21
  57. data/spec/graphql/introspection/input_value_type_spec.rb +43 -6
  58. data/spec/graphql/introspection/schema_type_spec.rb +1 -0
  59. data/spec/graphql/introspection/type_type_spec.rb +2 -0
  60. data/spec/graphql/language/generation_spec.rb +3 -2
  61. data/spec/graphql/query/arguments_spec.rb +17 -4
  62. data/spec/graphql/query/context_spec.rb +23 -0
  63. data/spec/graphql/query/variables_spec.rb +15 -1
  64. data/spec/graphql/relay/mutation_spec.rb +42 -2
  65. data/spec/graphql/schema/build_from_definition_spec.rb +4 -2
  66. data/spec/graphql/schema/loader_spec.rb +59 -1
  67. data/spec/graphql/schema/printer_spec.rb +2 -0
  68. data/spec/graphql/schema/reduce_types_spec.rb +1 -1
  69. data/spec/graphql/schema/timeout_middleware_spec.rb +2 -2
  70. data/spec/graphql/schema/unique_within_type_spec.rb +9 -0
  71. data/spec/graphql/schema/validation_spec.rb +15 -3
  72. data/spec/graphql/static_validation/rules/argument_literals_are_compatible_spec.rb +122 -0
  73. data/spec/graphql/static_validation/rules/variable_default_values_are_correctly_typed_spec.rb +78 -0
  74. data/spec/support/dairy_app.rb +9 -0
  75. data/spec/support/minimum_input_object.rb +4 -0
  76. data/spec/support/star_wars_schema.rb +1 -1
  77. metadata +5 -5
  78. data/lib/graphql/query/serial_execution/execution_context.rb +0 -37
  79. data/spec/graphql/query/serial_execution/execution_context_spec.rb +0 -54
@@ -32,6 +32,7 @@ describe GraphQL::Introspection::SchemaType do
32
32
  {"name"=>"milk"},
33
33
  {"name"=>"root"},
34
34
  {"name"=>"searchDairy"},
35
+ {"name"=>"valueWithExecutionError"},
35
36
  ]
36
37
  },
37
38
  "mutationType"=> {
@@ -18,6 +18,7 @@ describe GraphQL::Introspection::TypeType do
18
18
  {"name"=>"id", "isDeprecated" => false, "type" => { "kind" => "NON_NULL", "name" => nil, "ofType" => { "name" => "Int"}}},
19
19
  {"name"=>"nullableCheese", "isDeprecated"=>false, "type"=>{ "kind" => "OBJECT", "name" => "Cheese", "ofType"=>nil}},
20
20
  {"name"=>"origin", "isDeprecated" => false, "type" => { "kind" => "NON_NULL", "name" => nil, "ofType" => { "name" => "String"}}},
21
+ {"name"=>"selfAsEdible", "isDeprecated"=>false, "type"=>{"kind"=>"INTERFACE", "name"=>"Edible", "ofType"=>nil}},
21
22
  {"name"=>"similarCheese", "isDeprecated"=>false, "type"=>{ "kind" => "OBJECT", "name"=>"Cheese", "ofType"=>nil}},
22
23
  {"name"=>"source", "isDeprecated" => false, "type" => { "kind" => "NON_NULL", "name" => nil, "ofType" => { "name" => "DairyAnimal"}}},
23
24
  ]}
@@ -49,6 +50,7 @@ describe GraphQL::Introspection::TypeType do
49
50
  {"type"=>{"kind"=>"LIST","name"=>nil, "ofType"=>{"name"=>"String"}}},
50
51
  {"type"=>{"kind"=>"NON_NULL","name"=>nil, "ofType"=>{"name"=>"ID"}}},
51
52
  {"type"=>{"kind"=>"NON_NULL","name"=>nil, "ofType"=>{"name"=>"String"}}},
53
+ {"type"=>{"kind"=>"INTERFACE", "name"=>"Edible", "ofType"=>nil}},
52
54
  {"type"=>{"kind"=>"ENUM","name"=>"DairyAnimal", "ofType"=>nil}},
53
55
  ]
54
56
  },
@@ -31,7 +31,7 @@ describe GraphQL::Language::Generation do
31
31
  describe "inputs" do
32
32
  let(:query_string) {%|
33
33
  query {
34
- field(int: 3, float: 4.7e-24, bool: false, string: "☀︎🏆\\n escaped \\" unicode ¶ /", enum: ENUM_NAME, array: [7, 8, 9], object: {a: [1, 2, 3], b: {c: "4"}}, unicode_bom: "\xef\xbb\xbfquery")
34
+ field(null_value: null, null_in_array: [1, null, 3], int: 3, float: 4.7e-24, bool: false, string: "☀︎🏆\\n escaped \\" unicode ¶ /", enum: ENUM_NAME, array: [7, 8, 9], object: {a: [1, 2, 3], b: {c: "4"}}, unicode_bom: "\xef\xbb\xbfquery")
35
35
  }
36
36
  |}
37
37
 
@@ -102,7 +102,7 @@ describe GraphQL::Language::Generation do
102
102
  end
103
103
 
104
104
  describe "full featured schema" do
105
- # From: https://github.com/graphql/graphql-js/blob/a725499b155285c2e33647a93393c82689b20b0f/src/language/__tests__/schema-kitchen-sink.graphql
105
+ # From: https://github.com/graphql/graphql-js/blob/bc96406ab44453a120da25a0bd6e2b0237119ddf/src/language/__tests__/schema-kitchen-sink.graphql
106
106
  let(:query_string) {<<-schema
107
107
  schema {
108
108
  query: QueryType
@@ -119,6 +119,7 @@ describe GraphQL::Language::Generation do
119
119
  four(argument: String = "string"): String
120
120
  five(argument: [String] = ["string", "string"]): String
121
121
  six(argument: InputType = {key: "value"}): Type
122
+ seven(argument: String = null): Type
122
123
  }
123
124
 
124
125
  # Scalar description
@@ -168,11 +168,9 @@ describe GraphQL::Query::Arguments do
168
168
  assert_equal true, test_inputs.key?(:b)
169
169
 
170
170
  assert_equal false, test_inputs.key?(:c)
171
- # This _was_ present in the variables,
172
- # but it was nil, which is not allowed in GraphQL
173
- assert_equal false, test_inputs.key?(:d)
171
+ assert_equal true, test_inputs.key?(:d)
174
172
 
175
- assert_equal({"a" => 1, "b" => 2}, test_inputs.to_h)
173
+ assert_equal({"a" => 1, "b" => 2, "d" => nil}, test_inputs.to_h)
176
174
  end
177
175
 
178
176
  it "works with variable default values" do
@@ -188,5 +186,20 @@ describe GraphQL::Query::Arguments do
188
186
  assert_equal false, test_defaults.key?(:d)
189
187
  assert_equal({"a" => 1, "b" => 2}, test_defaults.to_h)
190
188
  end
189
+
190
+ it "works with variable default values with null" do
191
+ schema.execute("query ArgTest($arg: TestInput = {d: null}){ argTest(d: $arg) }")
192
+
193
+ test_defaults = arg_values.last["d"]
194
+
195
+ assert_equal false, test_defaults.key?(:a)
196
+ # This is present from default val
197
+ assert_equal true, test_defaults.key?(:b)
198
+
199
+ assert_equal false, test_defaults.key?(:c)
200
+ assert_equal true, test_defaults.key?(:d)
201
+
202
+ assert_equal({"d" => nil, "b" => 2}, test_defaults.to_h)
203
+ end
191
204
  end
192
205
  end
@@ -1,6 +1,9 @@
1
1
  require "spec_helper"
2
2
 
3
3
  describe GraphQL::Query::Context do
4
+ CTX = []
5
+ before { CTX.clear }
6
+
4
7
  let(:query_type) { GraphQL::ObjectType.define {
5
8
  name "Query"
6
9
  field :context, types.String do
@@ -16,6 +19,10 @@ describe GraphQL::Query::Context do
16
19
  field :queryName, types.String do
17
20
  resolve ->(target, args, ctx) { ctx.query.class.name }
18
21
  end
22
+
23
+ field :pushContext, types.Int do
24
+ resolve ->(t,a,c) { CTX << c; 1 }
25
+ end
19
26
  }}
20
27
  let(:schema) { GraphQL::Schema.define(query: query_type, mutation: nil)}
21
28
  let(:result) { schema.execute(query_string, context: {"some_key" => "some value"})}
@@ -81,4 +88,20 @@ describe GraphQL::Query::Context do
81
88
  assert_equal("wow!", context[:some_key])
82
89
  end
83
90
  end
91
+
92
+ describe "accessing context after the fact" do
93
+ let(:query_string) { %|
94
+ { pushContext }
95
+ |}
96
+
97
+ it "preserves path information" do
98
+ assert_equal 1, result["data"]["pushContext"]
99
+ last_ctx = CTX.pop
100
+ assert_equal ["pushContext"], last_ctx.path
101
+ err = GraphQL::ExecutionError.new("Test position info")
102
+ last_ctx.add_error(err)
103
+ assert_equal ["pushContext"], err.path
104
+ assert_equal [2, 9], [err.ast_node.line, err.ast_node.col]
105
+ end
106
+ end
84
107
  end
@@ -2,7 +2,7 @@ require "spec_helper"
2
2
 
3
3
  describe GraphQL::Query::Variables do
4
4
  let(:query_string) {%|
5
- query getCheese($animals: [DairyAnimal]) {
5
+ query getCheese($animals: [DairyAnimal], $int: Int, $intWithDefault: Int = 10) {
6
6
  cheese(id: 1) {
7
7
  similarCheese(source: $animals)
8
8
  }
@@ -25,5 +25,19 @@ describe GraphQL::Query::Variables do
25
25
  assert_equal ["YAK"], variables["animals"]
26
26
  end
27
27
  end
28
+
29
+ describe "coercing null" do
30
+ let(:provided_variables) {
31
+ {"int" => nil, "intWithDefault" => nil}
32
+ }
33
+
34
+ it "null variable" do
35
+ assert_equal nil, variables["int"]
36
+ end
37
+
38
+ it "preserves explicit null when variable has a default value" do
39
+ assert_equal nil, variables["intWithDefault"]
40
+ end
41
+ end
28
42
  end
29
43
  end
@@ -2,8 +2,8 @@ require 'spec_helper'
2
2
 
3
3
  describe GraphQL::Relay::Mutation do
4
4
  let(:query_string) {%|
5
- mutation addBagel($clientMutationId: String) {
6
- introduceShip(input: {shipName: "Bagel", factionId: "1", clientMutationId: $clientMutationId}) {
5
+ mutation addBagel($clientMutationId: String, $shipName: String = "Bagel") {
6
+ introduceShip(input: {shipName: $shipName, factionId: "1", clientMutationId: $clientMutationId}) {
7
7
  clientMutationId
8
8
  shipEdge {
9
9
  node { name, id }
@@ -25,6 +25,24 @@ describe GraphQL::Relay::Mutation do
25
25
  STAR_WARS_DATA["Faction"]["1"].ships.delete("9")
26
26
  end
27
27
 
28
+ it "supports null values" do
29
+ result = star_wars_query(query_string, "clientMutationId" => "1234", "shipName" => nil)
30
+
31
+ expected = {"data" => {
32
+ "introduceShip" => {
33
+ "clientMutationId" => "1234",
34
+ "shipEdge" => {
35
+ "node" => {
36
+ "name" => nil,
37
+ "id" => GraphQL::Schema::UniqueWithinType.encode("Ship", "9"),
38
+ },
39
+ },
40
+ "faction" => {"name" => STAR_WARS_DATA["Faction"]["1"].name }
41
+ }
42
+ }}
43
+ assert_equal(expected, result)
44
+ end
45
+
28
46
  it "returns the result & clientMutationId" do
29
47
  result = star_wars_query(query_string, "clientMutationId" => "1234")
30
48
  expected = {"data" => {
@@ -71,6 +89,11 @@ describe GraphQL::Relay::Mutation do
71
89
  custom_type = custom_return_type
72
90
  GraphQL::Relay::Mutation.define do
73
91
  name "CustomReturnTypeTest"
92
+
93
+ input_field :nullDefault, types.String, default_value: nil
94
+ input_field :noDefault, types.String
95
+ input_field :stringDefault, types.String, default_value: 'String'
96
+
74
97
  return_type custom_type
75
98
  resolve ->(obj, input, ctx) {
76
99
  OpenStruct.new(name: "Custom Return Type Test")
@@ -78,6 +101,8 @@ describe GraphQL::Relay::Mutation do
78
101
  end
79
102
  }
80
103
 
104
+ let(:input) { mutation.field.arguments['input'].type.unwrap }
105
+
81
106
  let(:schema) {
82
107
  mutation_field = mutation.field
83
108
 
@@ -102,5 +127,20 @@ describe GraphQL::Relay::Mutation do
102
127
  it "doesn't get a mutation in the metadata" do
103
128
  assert_equal nil, custom_return_type.mutation
104
129
  end
130
+
131
+ it "supports input fields with nil default value" do
132
+ assert input.arguments['nullDefault'].default_value?
133
+ assert_equal nil, input.arguments['nullDefault'].default_value
134
+ end
135
+
136
+ it "supports input fields with no default value" do
137
+ assert !input.arguments['noDefault'].default_value?
138
+ assert_equal nil, input.arguments['noDefault'].default_value
139
+ end
140
+
141
+ it "supports input fields with non-nil default value" do
142
+ assert input.arguments['stringDefault'].default_value?
143
+ assert_equal "String", input.arguments['stringDefault'].default_value
144
+ end
105
145
  end
106
146
  end
@@ -31,7 +31,7 @@ schema {
31
31
  query: Hello
32
32
  }
33
33
 
34
- directive @foo(arg: Int) on FIELD
34
+ directive @foo(arg: Int, nullDefault: Int = null) on FIELD
35
35
 
36
36
  type Hello {
37
37
  str: String
@@ -356,6 +356,7 @@ schema {
356
356
 
357
357
  input Input {
358
358
  int: Int
359
+ nullDefault: Int = null
359
360
  }
360
361
 
361
362
  type Root {
@@ -380,6 +381,7 @@ enum Color {
380
381
  type Hello {
381
382
  str(int: Int = 2): String
382
383
  hello(color: Color = RED): String
384
+ nullable(color: Color = null): String
383
385
  }
384
386
  SCHEMA
385
387
 
@@ -415,7 +417,7 @@ enum Color {
415
417
  }
416
418
 
417
419
  type Mutation {
418
- hello(str: String, int: Int, color: Color = RED): String
420
+ hello(str: String, int: Int, color: Color = RED, nullDefault: Int = null): String
419
421
  }
420
422
 
421
423
  type Query {
@@ -37,6 +37,17 @@ describe GraphQL::Schema::Loader do
37
37
  input_field :sub, types[sub_input_type]
38
38
  end
39
39
 
40
+ variant_input_type_with_nulls = GraphQL::InputObjectType.define do
41
+ name "VariedWithNulls"
42
+ input_field :id, types.ID, default_value: nil
43
+ input_field :int, types.Int, default_value: nil
44
+ input_field :bigint, big_int_type, default_value: nil
45
+ input_field :float, types.Float, default_value: nil
46
+ input_field :bool, types.Boolean, default_value: nil
47
+ input_field :enum, choice_type, default_value: nil
48
+ input_field :sub, types[sub_input_type], default_value: nil
49
+ end
50
+
40
51
  comment_type = GraphQL::ObjectType.define do
41
52
  name "Comment"
42
53
  description "A blog comment"
@@ -90,6 +101,7 @@ describe GraphQL::Schema::Loader do
90
101
  type post_type
91
102
  argument :id, !types.ID
92
103
  argument :varied, variant_input_type, default_value: { id: "123", int: 234, float: 2.3, enum: :foo, sub: [{ string: "str" }] }
104
+ argument :variedWithNull, variant_input_type_with_nulls, default_value: { id: nil, int: nil, float: nil, enum: nil, sub: nil, bigint: nil, bool: nil }
93
105
  end
94
106
 
95
107
  field :content do
@@ -205,7 +217,53 @@ describe GraphQL::Schema::Loader do
205
217
  field = type.fields['post']
206
218
  arg = field.arguments['varied']
207
219
 
208
- assert_equal arg.default_value, { 'id' => "123", 'bigint' => nil, 'bool' => nil, 'int' => 234, 'float' => 2.3, 'enum' => "FOO", 'sub' => [{ 'string' => "str" }] }
220
+ assert_equal arg.default_value, { 'id' => "123", 'int' => 234, 'float' => 2.3, 'enum' => "FOO", 'sub' => [{ 'string' => "str" }] }
221
+ assert !arg.default_value.key?('bool'), 'Omits default value for unspecified arguments'
222
+ end
223
+
224
+ it "does not set default value when there are none on input fields" do
225
+ type = loaded_schema.types['Varied']
226
+
227
+ assert !type.arguments['id'].default_value?
228
+ assert !type.arguments['int'].default_value?
229
+ assert type.arguments['bigint'].default_value?
230
+ assert !type.arguments['float'].default_value?
231
+ assert !type.arguments['bool'].default_value?
232
+ assert !type.arguments['enum'].default_value?
233
+ assert !type.arguments['sub'].default_value?
234
+ end
235
+
236
+ it "sets correct default values `null` on input fields" do
237
+ type = loaded_schema.types['VariedWithNulls']
238
+
239
+ assert type.arguments['id'].default_value?
240
+ assert type.arguments['id'].default_value.nil?
241
+
242
+ assert type.arguments['int'].default_value?
243
+ assert type.arguments['int'].default_value.nil?
244
+
245
+ assert type.arguments['bigint'].default_value?
246
+ assert type.arguments['bigint'].default_value.nil?
247
+
248
+ assert type.arguments['float'].default_value?
249
+ assert type.arguments['float'].default_value.nil?
250
+
251
+ assert type.arguments['bool'].default_value?
252
+ assert type.arguments['bool'].default_value.nil?
253
+
254
+ assert type.arguments['enum'].default_value?
255
+ assert type.arguments['enum'].default_value.nil?
256
+
257
+ assert type.arguments['sub'].default_value?
258
+ assert type.arguments['sub'].default_value.nil?
259
+ end
260
+
261
+ it "sets correct default values `null` on complex field arguments" do
262
+ type = loaded_schema.types['Query']
263
+ field = type.fields['post']
264
+ arg = field.arguments['variedWithNull']
265
+
266
+ assert_equal arg.default_value, { 'id' => nil, 'int' => nil, 'float' => nil, 'enum' => nil, 'sub' => nil, 'bool' => nil, 'bigint' => nil }
209
267
  end
210
268
  end
211
269
  end
@@ -85,6 +85,7 @@ describe GraphQL::Schema::Printer do
85
85
  type post_type
86
86
  argument :id, !types.ID, 'Post ID'
87
87
  argument :varied, variant_input_type, default_value: { id: "123", int: 234, float: 2.3, enum: :foo, sub: [{ string: "str" }] }
88
+ argument :variedWithNulls, variant_input_type, default_value: { id: nil, int: nil, float: nil, enum: nil, sub: nil }
88
89
  resolve ->(obj, args, ctx) { Post.find(args["id"]) }
89
90
  end
90
91
  end
@@ -444,6 +445,7 @@ type Query {
444
445
  # Post ID
445
446
  id: ID!
446
447
  varied: Varied = {id: \"123\", int: 234, float: 2.3, enum: FOO, sub: [{string: \"str\"}]}
448
+ variedWithNulls: Varied = {id: null, int: null, float: null, enum: null, sub: null}
447
449
  ): Post
448
450
  }
449
451
 
@@ -10,9 +10,9 @@ describe GraphQL::Schema::ReduceTypes do
10
10
  "Cheese" => CheeseType,
11
11
  "Float" => GraphQL::FLOAT_TYPE,
12
12
  "String" => GraphQL::STRING_TYPE,
13
+ "Edible" => EdibleInterface,
13
14
  "DairyAnimal" => DairyAnimalEnum,
14
15
  "Int" => GraphQL::INT_TYPE,
15
- "Edible" => EdibleInterface,
16
16
  "AnimalProduct" => AnimalProductInterface,
17
17
  "LocalProduct" => LocalProductInterface,
18
18
  }
@@ -166,7 +166,7 @@ describe GraphQL::Schema::TimeoutMiddleware do
166
166
  describe "with a custom block" do
167
167
  let(:timeout_middleware) {
168
168
  GraphQL::Schema::TimeoutMiddleware.new(max_seconds: max_seconds) do |err, query|
169
- raise("Query timed out after 2s: #{query.class.name} on #{query.context.ast_node.alias}")
169
+ raise("Query timed out after 2s: #{query.operations.count} on #{query.context.ast_node.alias}")
170
170
  end
171
171
  }
172
172
  let(:query_string) {%|
@@ -181,7 +181,7 @@ describe GraphQL::Schema::TimeoutMiddleware do
181
181
 
182
182
  it "calls the block" do
183
183
  err = assert_raises(RuntimeError) { result }
184
- assert_equal "Query timed out after 2s: GraphQL::Query on d", err.message
184
+ assert_equal "Query timed out after 2s: 1 on d", err.message
185
185
  end
186
186
  end
187
187
  end
@@ -9,6 +9,15 @@ describe GraphQL::Schema::UniqueWithinType do
9
9
  assert_equal("123", id)
10
10
  end
11
11
 
12
+ it "allows you specify default separator" do
13
+ GraphQL::Schema::UniqueWithinType.default_id_separator = '|'
14
+ global_id = GraphQL::Schema::UniqueWithinType.encode("Type-With-UUID", "250cda0e-a89d-41cf-99e1-2872d89f1100")
15
+ type_name, id = GraphQL::Schema::UniqueWithinType.decode(global_id)
16
+ assert_equal("Type-With-UUID", type_name)
17
+ assert_equal("250cda0e-a89d-41cf-99e1-2872d89f1100", id)
18
+ GraphQL::Schema::UniqueWithinType.default_id_separator = '-'
19
+ end
20
+
12
21
  it "allows you to specify the separator" do
13
22
  custom_separator = "---"
14
23
  global_id = GraphQL::Schema::UniqueWithinType.encode("Type-With-UUID", "250cda0e-a89d-41cf-99e1-2872d89f1100", separator: custom_separator)
@@ -200,7 +200,7 @@ describe GraphQL::Schema::Validation do
200
200
  end
201
201
  }
202
202
 
203
- let(:invalid_default_argument_for_non_null_field) {
203
+ let(:invalid_default_argument_for_non_null_argument) {
204
204
  GraphQL::Argument.define do
205
205
  name "InvalidDefault"
206
206
  type !GraphQL::INT_TYPE
@@ -208,12 +208,24 @@ describe GraphQL::Schema::Validation do
208
208
  end
209
209
  }
210
210
 
211
+ let(:null_default_value) {
212
+ GraphQL::Argument.define do
213
+ name "NullDefault"
214
+ type DairyAnimalEnum
215
+ default_value nil
216
+ end
217
+ }
218
+
211
219
  it "requires the type is a Base type" do
212
220
  assert_error_includes untyped_argument, "must be a valid input type (Scalar or InputObject), not Symbol"
213
221
  end
214
222
 
215
- it "does not allow default values for non-null fields" do
216
- assert_error_includes invalid_default_argument_for_non_null_field, 'Variable InvalidDefault of type "Int!" is required and will not use the default value. Perhaps you meant to use type "Int".'
223
+ it "does not allow default values for non-null argument" do
224
+ assert_error_includes invalid_default_argument_for_non_null_argument, 'Variable InvalidDefault of type "Int!" is required and will not use the default value. Perhaps you meant to use type "Int".'
225
+ end
226
+
227
+ it "allows null default value for nullable argument" do
228
+ assert_equal nil, GraphQL::Schema::Validation.validate(null_default_value)
217
229
  end
218
230
  end
219
231
  end
@@ -65,4 +65,126 @@ describe GraphQL::StaticValidation::ArgumentLiteralsAreCompatible do
65
65
  }
66
66
  assert_includes(errors, fragment_error)
67
67
  end
68
+
69
+ describe "null value" do
70
+ describe "nullable arg" do
71
+ let(:schema) {
72
+ GraphQL::Schema.from_definition(%|
73
+ type Query {
74
+ field(arg: Int): Int
75
+ }
76
+ |)
77
+ }
78
+ let(:query_string) {%|
79
+ query {
80
+ field(arg: null)
81
+ }
82
+ |}
83
+
84
+ it "finds no errors" do
85
+ assert_equal [], errors
86
+ end
87
+ end
88
+
89
+ describe "non-nullable arg" do
90
+ let(:schema) {
91
+ GraphQL::Schema.from_definition(%|
92
+ type Query {
93
+ field(arg: Int!): Int
94
+ }
95
+ |)
96
+ }
97
+ let(:query_string) {%|
98
+ query {
99
+ field(arg: null)
100
+ }
101
+ |}
102
+
103
+ it "finds error" do
104
+ assert_equal [{
105
+ "message"=>"Argument 'arg' on Field 'field' has an invalid value. Expected type 'Int!'.",
106
+ "locations"=>[{"line"=>3, "column"=>11}],
107
+ "fields"=>["query", "field", "arg"],
108
+ }], errors
109
+ end
110
+ end
111
+
112
+ describe "non-nullable array" do
113
+ let(:schema) {
114
+ GraphQL::Schema.from_definition(%|
115
+ type Query {
116
+ field(arg: [Int!]): Int
117
+ }
118
+ |)
119
+ }
120
+ let(:query_string) {%|
121
+ query {
122
+ field(arg: [null])
123
+ }
124
+ |}
125
+
126
+ it "finds error" do
127
+ assert_equal [{
128
+ "message"=>"Argument 'arg' on Field 'field' has an invalid value. Expected type '[Int!]'.",
129
+ "locations"=>[{"line"=>3, "column"=>11}],
130
+ "fields"=>["query", "field", "arg"],
131
+ }], errors
132
+ end
133
+ end
134
+
135
+ describe "array with nullable values" do
136
+ let(:schema) {
137
+ GraphQL::Schema.from_definition(%|
138
+ type Query {
139
+ field(arg: [Int]): Int
140
+ }
141
+ |)
142
+ }
143
+ let(:query_string) {%|
144
+ query {
145
+ field(arg: [null])
146
+ }
147
+ |}
148
+
149
+ it "finds no errors" do
150
+ assert_equal [], errors
151
+ end
152
+ end
153
+
154
+ describe "input object" do
155
+ let(:schema) {
156
+ GraphQL::Schema.from_definition(%|
157
+ type Query {
158
+ field(arg: Input): Int
159
+ }
160
+
161
+ input Input {
162
+ a: Int
163
+ b: Int!
164
+ }
165
+ |)
166
+ }
167
+ let(:query_string) {%|
168
+ query {
169
+ field(arg: {a: null, b: null})
170
+ }
171
+ |}
172
+
173
+ it "finds errors" do
174
+ assert_equal 2, errors.length
175
+
176
+ assert_includes errors, {
177
+ "message"=> "Argument 'arg' on Field 'field' has an invalid value. Expected type 'Input'.",
178
+ "locations"=>[{"line"=>3, "column"=>11}],
179
+ "fields"=>["query", "field", "arg"]
180
+ }
181
+
182
+ assert_includes errors, {
183
+ "message"=>"Argument 'b' on InputObject 'Input' has an invalid value. Expected type 'Int!'.",
184
+ "locations"=>[{"line"=>3, "column"=>22}],
185
+ "fields"=>["query", "field", "arg", "b"]
186
+ }
187
+ end
188
+ end
189
+ end
68
190
  end