graphql 0.18.15 → 0.19.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (89) hide show
  1. checksums.yaml +4 -4
  2. data/lib/graphql/define/assign_global_id_field.rb +1 -2
  3. data/lib/graphql/directive.rb +41 -7
  4. data/lib/graphql/directive/deprecated_directive.rb +11 -0
  5. data/lib/graphql/directive/include_directive.rb +2 -2
  6. data/lib/graphql/directive/skip_directive.rb +2 -2
  7. data/lib/graphql/input_object_type.rb +14 -0
  8. data/lib/graphql/internal_representation/node.rb +8 -4
  9. data/lib/graphql/introspection/arguments_field.rb +0 -1
  10. data/lib/graphql/introspection/directive_location_enum.rb +3 -2
  11. data/lib/graphql/introspection/directive_type.rb +12 -7
  12. data/lib/graphql/introspection/enum_value_type.rb +4 -2
  13. data/lib/graphql/introspection/enum_values_field.rb +0 -1
  14. data/lib/graphql/introspection/field_type.rb +8 -7
  15. data/lib/graphql/introspection/fields_field.rb +0 -1
  16. data/lib/graphql/introspection/input_fields_field.rb +0 -1
  17. data/lib/graphql/introspection/input_value_type.rb +8 -10
  18. data/lib/graphql/introspection/interfaces_field.rb +0 -1
  19. data/lib/graphql/introspection/of_type_field.rb +0 -1
  20. data/lib/graphql/introspection/possible_types_field.rb +0 -1
  21. data/lib/graphql/introspection/schema_type.rb +11 -9
  22. data/lib/graphql/introspection/type_kind_enum.rb +3 -3
  23. data/lib/graphql/introspection/type_type.rb +9 -6
  24. data/lib/graphql/language/generation.rb +4 -1
  25. data/lib/graphql/language/lexer.rb +353 -316
  26. data/lib/graphql/language/lexer.rl +8 -6
  27. data/lib/graphql/language/nodes.rb +12 -0
  28. data/lib/graphql/language/parser.rb +553 -501
  29. data/lib/graphql/language/parser.y +26 -16
  30. data/lib/graphql/language/parser_tests.rb +20 -1
  31. data/lib/graphql/list_type.rb +5 -1
  32. data/lib/graphql/non_null_type.rb +4 -0
  33. data/lib/graphql/object_type.rb +1 -1
  34. data/lib/graphql/query/literal_input.rb +1 -1
  35. data/lib/graphql/relay.rb +1 -1
  36. data/lib/graphql/relay/global_id_resolve.rb +3 -5
  37. data/lib/graphql/relay/node.rb +34 -0
  38. data/lib/graphql/scalar_type.rb +1 -1
  39. data/lib/graphql/schema.rb +43 -15
  40. data/lib/graphql/schema/loader.rb +2 -2
  41. data/lib/graphql/schema/printer.rb +50 -8
  42. data/lib/graphql/schema/unique_within_type.rb +28 -0
  43. data/lib/graphql/schema/validation.rb +10 -3
  44. data/lib/graphql/static_validation/rules/fields_will_merge.rb +9 -1
  45. data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +8 -2
  46. data/lib/graphql/type_kinds.rb +12 -12
  47. data/lib/graphql/version.rb +1 -1
  48. data/readme.md +6 -5
  49. data/spec/graphql/argument_spec.rb +1 -1
  50. data/spec/graphql/execution_error_spec.rb +53 -0
  51. data/spec/graphql/introspection/directive_type_spec.rb +10 -0
  52. data/spec/graphql/introspection/input_value_type_spec.rb +23 -0
  53. data/spec/graphql/language/generation_spec.rb +4 -0
  54. data/spec/graphql/query/executor_spec.rb +2 -2
  55. data/spec/graphql/relay/mutation_spec.rb +1 -1
  56. data/spec/graphql/relay/node_spec.rb +87 -0
  57. data/spec/graphql/schema/catchall_middleware_spec.rb +1 -1
  58. data/spec/graphql/schema/loader_spec.rb +37 -4
  59. data/spec/graphql/schema/printer_spec.rb +30 -7
  60. data/spec/graphql/schema/unique_within_type_spec.rb +27 -0
  61. data/spec/graphql/schema/validation_spec.rb +7 -11
  62. data/spec/graphql/schema_spec.rb +32 -2
  63. data/spec/graphql/static_validation/rules/argument_literals_are_compatible_spec.rb +14 -15
  64. data/spec/graphql/static_validation/rules/arguments_are_defined_spec.rb +10 -10
  65. data/spec/graphql/static_validation/rules/directives_are_defined_spec.rb +1 -5
  66. data/spec/graphql/static_validation/rules/directives_are_in_valid_locations_spec.rb +3 -5
  67. data/spec/graphql/static_validation/rules/fields_are_defined_on_type_spec.rb +7 -11
  68. data/spec/graphql/static_validation/rules/fields_have_appropriate_selections_spec.rb +1 -4
  69. data/spec/graphql/static_validation/rules/fields_will_merge_spec.rb +10 -13
  70. data/spec/graphql/static_validation/rules/fragment_spreads_are_possible_spec.rb +3 -5
  71. data/spec/graphql/static_validation/rules/fragment_types_exist_spec.rb +2 -4
  72. data/spec/graphql/static_validation/rules/fragments_are_finite_spec.rb +4 -6
  73. data/spec/graphql/static_validation/rules/fragments_are_named_spec.rb +2 -4
  74. data/spec/graphql/static_validation/rules/fragments_are_on_composite_types_spec.rb +7 -7
  75. data/spec/graphql/static_validation/rules/fragments_are_used_spec.rb +1 -4
  76. data/spec/graphql/static_validation/rules/mutation_root_exists_spec.rb +2 -4
  77. data/spec/graphql/static_validation/rules/required_arguments_are_present_spec.rb +3 -6
  78. data/spec/graphql/static_validation/rules/subscription_root_exists_spec.rb +2 -4
  79. data/spec/graphql/static_validation/rules/variable_default_values_are_correctly_typed_spec.rb +12 -14
  80. data/spec/graphql/static_validation/rules/variable_usages_are_allowed_spec.rb +7 -9
  81. data/spec/graphql/static_validation/rules/variables_are_input_types_spec.rb +20 -22
  82. data/spec/graphql/static_validation/rules/variables_are_used_and_defined_spec.rb +27 -23
  83. data/spec/spec_helper.rb +1 -0
  84. data/spec/support/dairy_app.rb +2 -2
  85. data/spec/support/star_wars_schema.rb +15 -18
  86. data/spec/support/static_validation_helpers.rb +27 -0
  87. metadata +25 -5
  88. data/lib/graphql/relay/global_node_identification.rb +0 -138
  89. data/spec/graphql/relay/global_node_identification_spec.rb +0 -153
@@ -0,0 +1,28 @@
1
+ module GraphQL
2
+ class Schema
3
+ module UniqueWithinType
4
+ DEFAULT_SEPARATOR = "-"
5
+
6
+ module_function
7
+
8
+ # @param type_name [String]
9
+ # @param object_value [Any]
10
+ # @return [String] a unique, opaque ID generated as a function of the two inputs
11
+ def encode(type_name, object_value, separator: DEFAULT_SEPARATOR)
12
+ object_value_str = object_value.to_s
13
+
14
+ if type_name.include?(separator) || object_value_str.include?(separator)
15
+ raise "encode(#{type_name}, #{object_value_str}) contains reserved characters `#{separator}`"
16
+ end
17
+
18
+ Base64.strict_encode64([type_name, object_value_str].join(separator))
19
+ end
20
+
21
+ # @param node_id [String] A unique ID generated by {.encode}
22
+ # @return [Array<(String, String)>] The type name & value passed to {.encode}
23
+ def decode(node_id, separator: DEFAULT_SEPARATOR)
24
+ Base64.decode64(node_id).split(separator)
25
+ end
26
+ end
27
+ end
28
+ end
@@ -100,9 +100,16 @@ module GraphQL
100
100
 
101
101
  DEFAULT_VALUE_IS_VALID_FOR_TYPE = -> (type) {
102
102
  if !type.default_value.nil?
103
- coerced_value = type.type.coerce_input(type.default_value)
104
- if coerced_value.nil?
105
- "default value #{type.default_value.inspect} is not valid for type #{type.type}"
103
+ coerced_value = begin
104
+ type.type.coerce_result(type.default_value)
105
+ rescue => ex
106
+ ex
107
+ end
108
+
109
+ if coerced_value.nil? || coerced_value.is_a?(StandardError)
110
+ msg = "default value #{type.default_value.inspect} is not valid for type #{type.type}"
111
+ msg += " (#{coerced_value})" if coerced_value.is_a?(StandardError)
112
+ msg
106
113
  end
107
114
  end
108
115
  }
@@ -76,7 +76,7 @@ module GraphQL
76
76
 
77
77
  args = defs.map { |defn| reduce_list(defn.arguments)}.uniq
78
78
  if args.length != 1
79
- errors << message("Field '#{name}' has an argument conflict: #{args.map {|a| JSON.dump(a) }.join(" or ")}?", defs.first, context: context)
79
+ errors << message("Field '#{name}' has an argument conflict: #{args.map {|a| print_arg(a) }.join(" or ")}?", defs.first, context: context)
80
80
  end
81
81
 
82
82
  @errors = errors
@@ -84,6 +84,14 @@ module GraphQL
84
84
 
85
85
  private
86
86
 
87
+ def print_arg(arg)
88
+ case arg
89
+ when GraphQL::Language::Nodes::VariableIdentifier
90
+ "$#{arg.name}"
91
+ else
92
+ JSON.dump(arg)
93
+ end
94
+ end
87
95
  # Turn AST tree into a hash
88
96
  # can't look up args, the names just have to match
89
97
  def reduce_list(args)
@@ -23,8 +23,10 @@ module GraphQL
23
23
  context.visitor[GraphQL::Language::Nodes::Document].leave << -> (doc_node, parent) {
24
24
  spreads_to_validate.each do |frag_spread|
25
25
  fragment_child_name = context.fragments[frag_spread.node.name].type
26
- fragment_child = context.schema.types[fragment_child_name]
27
- validate_fragment_in_scope(frag_spread.parent_type, fragment_child, frag_spread.node, context, frag_spread.path)
26
+ fragment_child = context.schema.types.fetch(fragment_child_name, nil) # Might be non-existent type name
27
+ if fragment_child
28
+ validate_fragment_in_scope(frag_spread.parent_type, fragment_child, frag_spread.node, context, frag_spread.path)
29
+ end
28
30
  end
29
31
  }
30
32
  end
@@ -32,6 +34,10 @@ module GraphQL
32
34
  private
33
35
 
34
36
  def validate_fragment_in_scope(parent_type, child_type, node, context, path)
37
+ if !child_type.kind.fields?
38
+ # It's not a valid fragment type, this error was handled someplace else
39
+ return
40
+ end
35
41
  intersecting_types = get_possible_types(parent_type, context.schema) & get_possible_types(child_type, context.schema)
36
42
  if intersecting_types.none?
37
43
  name = node.respond_to?(:name) ? " #{node.name}" : ""
@@ -3,14 +3,15 @@ module GraphQL
3
3
  module TypeKinds
4
4
  # These objects are singletons, eg `GraphQL::TypeKinds::UNION`, `GraphQL::TypeKinds::SCALAR`.
5
5
  class TypeKind
6
- attr_reader :name
7
- def initialize(name, resolves: false, fields: false, wraps: false, input: false)
6
+ attr_reader :name, :description
7
+ def initialize(name, resolves: false, fields: false, wraps: false, input: false, description: nil)
8
8
  @name = name
9
9
  @resolves = resolves
10
10
  @fields = fields
11
11
  @wraps = wraps
12
12
  @input = input
13
13
  @composite = fields? || resolves?
14
+ @description = description
14
15
  end
15
16
 
16
17
  # Does this TypeKind have multiple possible implementors?
@@ -27,19 +28,18 @@ module GraphQL
27
28
  end
28
29
 
29
30
  TYPE_KINDS = [
30
- SCALAR = TypeKind.new("SCALAR", input: true),
31
- OBJECT = TypeKind.new("OBJECT", fields: true),
32
- INTERFACE = TypeKind.new("INTERFACE", resolves: true, fields: true),
33
- UNION = TypeKind.new("UNION", resolves: true),
34
- ENUM = TypeKind.new("ENUM", input: true),
35
- INPUT_OBJECT = TypeKind.new("INPUT_OBJECT", input: true),
36
- LIST = TypeKind.new("LIST", wraps: true),
37
- NON_NULL = TypeKind.new("NON_NULL", wraps: true),
31
+ SCALAR = TypeKind.new("SCALAR", input: true, description: 'Indicates this type is a scalar.'),
32
+ OBJECT = TypeKind.new("OBJECT", fields: true, description: 'Indicates this type is an object. `fields` and `interfaces` are valid fields.'),
33
+ INTERFACE = TypeKind.new("INTERFACE", resolves: true, fields: true, description: 'Indicates this type is an interface. `fields` and `possibleTypes` are valid fields.'),
34
+ UNION = TypeKind.new("UNION", resolves: true, description: 'Indicates this type is a union. `possibleTypes` is a valid field.'),
35
+ ENUM = TypeKind.new("ENUM", input: true, description: 'Indicates this type is an enum. `enumValues` is a valid field.'),
36
+ INPUT_OBJECT = TypeKind.new("INPUT_OBJECT", input: true, description: 'Indicates this type is an input object. `inputFields` is a valid field.'),
37
+ LIST = TypeKind.new("LIST", wraps: true, description: 'Indicates this type is a list. `ofType` is a valid field.'),
38
+ NON_NULL = TypeKind.new("NON_NULL", wraps: true, description: 'Indicates this type is a non-null. `ofType` is a valid field.'),
38
39
  ]
39
40
 
40
- KIND_NAMES = TYPE_KINDS.map(&:name)
41
41
  class TypeKind
42
- KIND_NAMES.each do |kind_name|
42
+ TYPE_KINDS.map(&:name).each do |kind_name|
43
43
  define_method("#{kind_name.downcase}?") do
44
44
  self.name == kind_name
45
45
  end
@@ -1,3 +1,3 @@
1
1
  module GraphQL
2
- VERSION = "0.18.15"
2
+ VERSION = "0.19.0"
3
3
  end
data/readme.md CHANGED
@@ -114,7 +114,7 @@ If you're building a backend for [Relay](http://facebook.github.io/relay/), you'
114
114
 
115
115
  ## To Do
116
116
 
117
- - StaticValidation improvements
117
+ - StaticValidation improvements ([in progress](https://github.com/rmosolgo/graphql-ruby/pull/268))
118
118
  - Use catch-all type/field/argument definitions instead of terminating traversal
119
119
  - Reduce ad-hoc traversals?
120
120
  - Validators are order-dependent, is this a smell?
@@ -123,17 +123,16 @@ If you're building a backend for [Relay](http://facebook.github.io/relay/), you'
123
123
  - Add Rails-y argument validations, eg `less_than: 100`, `max_length: 255`, `one_of: [...]`
124
124
  - Must be customizable
125
125
  - Relay:
126
- - `GlobalNodeIdentification.to_global_id` should receive the type name and _object_, not `id`. (Or, maintain the "`type_name, id` in, `type_name, id` out" pattern?)
127
126
  - Reduce duplication in ArrayConnection / RelationConnection
128
127
  - Improve API for creating edges (better RANGE_ADD support)
129
128
  - If the new edge isn't a member of the connection's objects, raise a nice error
130
129
  - Missing Enum value should raise a descriptive error, not "key not found"
131
130
  - `args` should whitelist keys -- if you request a key that isn't defined for the field, it should 💥
132
- - Fix middleware
131
+ - Fix middleware ([discussion](https://github.com/rmosolgo/graphql-ruby/issues/186))
133
132
  - Handle out-of-bounds lookup, eg `graphql-batch`
134
133
  - Handle non-serial execution, eg `@defer`
135
134
  - Support non-instance-eval `.define`, eg `.define { |defn| ... }`
136
- - First-class promise support
135
+ - First-class promise support ([discussion](https://github.com/rmosolgo/graphql-ruby/issues/274))
137
136
  - like `graphql-batch` but more local
138
137
  - support promises in connection resolves
139
138
  - Add immutable transformation API to AST
@@ -141,10 +140,12 @@ If you're building a backend for [Relay](http://facebook.github.io/relay/), you'
141
140
  - Adding fields to selections (`__typename` can go anywhere, others are type-specific)
142
141
  - Renaming fragments from local names to unique names
143
142
  - Support AST subclasses? This would be hard, I think classes are used as hash keys in many places.
144
- - Support object deep-copy (schema, type, field, argument)? To support multiple schemas based on the same types.
143
+ - Support object deep-copy (schema, type, field, argument)? To support multiple schemas based on the same types. ([discussion](https://github.com/rmosolgo/graphql-ruby/issues/269))
145
144
  - Improve the website
146
145
  - Feature the logo in the header
147
146
  - Split `readme.md` into `index.md` (a homepage with code samples) and a technical readme (how to install, link to homepage)
148
147
  - Move "Related projects" to a guide
149
148
  - Revisit guides, maybe split them into smaller, more specific pages
150
149
  - Put guide titles into the `<title />`
150
+ - Document encrypted & versioned cursors
151
+ - Eager load `Schema#types` after `.define { ... }`
@@ -15,7 +15,7 @@ describe GraphQL::Argument do
15
15
  }
16
16
 
17
17
  expected_error = %|Query is invalid: field "invalid" argument "invalid" default value ["123"] is not valid for type Float|
18
- assert_equal expected_error, err.message
18
+ assert_includes err.message, expected_error
19
19
  end
20
20
 
21
21
  it "accepts proc type" do
@@ -118,4 +118,57 @@ describe GraphQL::ExecutionError do
118
118
  assert_equal(expected_result, result)
119
119
  end
120
120
  end
121
+
122
+ describe "named query when returned from a field" do
123
+ let(:query_string) {%|
124
+ query MilkQuery {
125
+ dairy {
126
+ milks {
127
+ source
128
+ executionError
129
+ allDairy {
130
+ __typename
131
+ ... on Milk {
132
+ origin
133
+ executionError
134
+ }
135
+ }
136
+ }
137
+ }
138
+ }
139
+ |}
140
+ it "the error is inserted into the errors key and the rest of the query is fulfilled" do
141
+ expected_result = {
142
+ "data"=>{
143
+ "dairy" => {
144
+ "milks" => [
145
+ {
146
+ "source" => "COW",
147
+ "executionError" => nil,
148
+ "allDairy" => [
149
+ { "__typename" => "Cheese" },
150
+ { "__typename" => "Cheese" },
151
+ { "__typename" => "Cheese" },
152
+ { "__typename" => "Milk", "origin" => "Antiquity", "executionError" => nil }
153
+ ]
154
+ }
155
+ ]
156
+ }
157
+ },
158
+ "errors"=>[
159
+ {
160
+ "message"=>"There was an execution error",
161
+ "locations"=>[{"line"=>6, "column"=>11}],
162
+ "path"=>["dairy", "milks", 0, "executionError"]
163
+ },
164
+ {
165
+ "message"=>"There was an execution error",
166
+ "locations"=>[{"line"=>11, "column"=>15}],
167
+ "path"=>["dairy", "milks", 0, "allDairy", 3, "executionError"]
168
+ }
169
+ ]
170
+ }
171
+ assert_equal(expected_result, result)
172
+ end
173
+ end
121
174
  end
@@ -42,6 +42,16 @@ describe GraphQL::Introspection::DirectiveType do
42
42
  "onFragment" => true,
43
43
  "onOperation" => false,
44
44
  },
45
+ {
46
+ "name" => "deprecated",
47
+ "args" => [
48
+ {"name"=>"reason", "type"=>{"name"=>"String", "ofType"=>nil}}
49
+ ],
50
+ "locations"=>["FIELD_DEFINITION", "ENUM_VALUE"],
51
+ "onField" => false,
52
+ "onFragment" => false,
53
+ "onOperation" => false,
54
+ },
45
55
  ]
46
56
  }
47
57
  }}
@@ -39,4 +39,27 @@ describe GraphQL::Introspection::InputValueType do
39
39
  }}
40
40
  assert_equal(expected, result)
41
41
  end
42
+
43
+ let(:cheese_type) {
44
+ DummySchema.execute(%|
45
+ {
46
+ __type(name: "Cheese") {
47
+ fields {
48
+ name
49
+ args {
50
+ name
51
+ defaultValue
52
+ }
53
+ }
54
+ }
55
+ }
56
+ |)
57
+ }
58
+
59
+ it "converts default values to GraphQL values" do
60
+ field = cheese_type['data']['__type']['fields'].detect { |f| f['name'] == 'similarCheese' }
61
+ arg = field['args'].detect { |a| a['name'] == 'source' }
62
+
63
+ assert_equal('["COW"]', arg['defaultValue'])
64
+ end
42
65
  end
@@ -96,6 +96,10 @@ describe GraphQL::Language::Generation do
96
96
  input AnnotatedInput @onInputObjectType {
97
97
  annotatedField: Type @onField
98
98
  }
99
+
100
+ directive @skip(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT
101
+
102
+ directive @include(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT
99
103
  schema
100
104
  }
101
105
 
@@ -144,7 +144,7 @@ describe GraphQL::Query::Executor do
144
144
  {
145
145
  "message" => "BOOM",
146
146
  "locations" => [ { "line" => 1, "column" => 28 } ],
147
- "path" => ["noMilk", "cow", "cantBeNullButRaisesExecutionError"]
147
+ "path" => ["cow", "cantBeNullButRaisesExecutionError"]
148
148
  }
149
149
  ]
150
150
  }
@@ -169,7 +169,7 @@ describe GraphQL::Query::Executor do
169
169
  {
170
170
  "message"=>"Error was handled!",
171
171
  "locations" => [{"line"=>1, "column"=>17}],
172
- "path"=>["noMilk", "error"]
172
+ "path"=>["error"]
173
173
  }
174
174
  ]
175
175
  }
@@ -33,7 +33,7 @@ describe GraphQL::Relay::Mutation do
33
33
  "shipEdge" => {
34
34
  "node" => {
35
35
  "name" => "Bagel",
36
- "id" => NodeIdentification.to_global_id("Ship", "9"),
36
+ "id" => GraphQL::Schema::UniqueWithinType.encode("Ship", "9"),
37
37
  },
38
38
  },
39
39
  "faction" => {"name" => STAR_WARS_DATA["Faction"]["1"].name }
@@ -0,0 +1,87 @@
1
+ require "spec_helper"
2
+
3
+ describe GraphQL::Relay::Node do
4
+ describe ".field" do
5
+ describe "Custom global IDs" do
6
+ before do
7
+ # TODO: make the schema eager-load so we can remove this
8
+ # Ensure the schema is defined:
9
+ StarWarsSchema.types
10
+
11
+ @previous_id_from_object_proc = StarWarsSchema.id_from_object_proc
12
+ @previous_object_from_id_proc = StarWarsSchema.object_from_id_proc
13
+
14
+ StarWarsSchema.id_from_object = -> (obj, type_name, ctx) {
15
+ "#{type_name}/#{obj.id}"
16
+ }
17
+
18
+ StarWarsSchema.object_from_id = -> (global_id, ctx) {
19
+ type_name, id = global_id.split("/")
20
+ STAR_WARS_DATA[type_name][id]
21
+ }
22
+ end
23
+
24
+ after do
25
+ StarWarsSchema.id_from_object = @previous_id_from_object_proc
26
+ StarWarsSchema.object_from_id = @previous_object_from_id_proc
27
+ end
28
+
29
+ it "Deconstructs the ID by the custom proc" do
30
+ result = star_wars_query(%| { node(id: "Base/1") { ... on Base { name } } }|)
31
+ base_name = result["data"]["node"]["name"]
32
+ assert_equal "Yavin", base_name
33
+ end
34
+
35
+ describe "generating IDs" do
36
+ it "Applies custom-defined ID generation" do
37
+ result = star_wars_query(%| { largestBase { id } }|)
38
+ generated_id = result["data"]["largestBase"]["id"]
39
+ assert_equal "Base/3", generated_id
40
+ end
41
+ end
42
+ end
43
+
44
+ describe "setting a description" do
45
+ it "allows you to set a description" do
46
+ node_field = GraphQL::Relay::Node.field
47
+ node_field.description = "Hello, World!"
48
+ assert_equal "Hello, World!", node_field.description
49
+ end
50
+ end
51
+
52
+
53
+ it 'finds objects by id' do
54
+ id = GraphQL::Schema::UniqueWithinType.encode("Faction", "1")
55
+ result = star_wars_query(%|{
56
+ node(id: "#{id}") {
57
+ id,
58
+ ... on Faction {
59
+ name
60
+ ships(first: 1) {
61
+ edges {
62
+ node {
63
+ name
64
+ }
65
+ }
66
+ }
67
+ }
68
+ }
69
+ }|)
70
+ expected = {"data" => {
71
+ "node"=>{
72
+ "id"=>"RmFjdGlvbi0x",
73
+ "name"=>"Alliance to Restore the Republic",
74
+ "ships"=>{
75
+ "edges"=>[
76
+ {"node"=>{
77
+ "name" => "X-Wing"
78
+ }
79
+ }
80
+ ]
81
+ }
82
+ }
83
+ }}
84
+ assert_equal(expected, result)
85
+ end
86
+ end
87
+ end
@@ -22,7 +22,7 @@ describe GraphQL::Schema::CatchallMiddleware do
22
22
  {
23
23
  "message"=>"Internal error",
24
24
  "locations"=>[{"line"=>1, "column"=>17}],
25
- "path"=>["noMilk", "error"]
25
+ "path"=>["error"]
26
26
  },
27
27
  ]
28
28
  }
@@ -11,7 +11,7 @@ describe GraphQL::Schema::Loader do
11
11
  choice_type = GraphQL::EnumType.define do
12
12
  name "Choice"
13
13
 
14
- value "FOO"
14
+ value "FOO", value: :foo
15
15
  value "BAR"
16
16
  end
17
17
 
@@ -30,7 +30,7 @@ describe GraphQL::Schema::Loader do
30
30
  name "Varied"
31
31
  input_field :id, types.ID
32
32
  input_field :int, types.Int
33
- input_field :bigint, big_int_type
33
+ input_field :bigint, big_int_type, default_value: 2**54
34
34
  input_field :float, types.Float
35
35
  input_field :bool, types.Boolean
36
36
  input_field :enum, choice_type
@@ -43,6 +43,10 @@ describe GraphQL::Schema::Loader do
43
43
  interfaces [node_type]
44
44
 
45
45
  field :body, !types.String
46
+
47
+ field :fieldWithArg, types.Int do
48
+ argument :bigint, big_int_type, default_value: 2**54
49
+ end
46
50
  end
47
51
 
48
52
  media_type = GraphQL::InterfaceType.define do
@@ -85,7 +89,7 @@ describe GraphQL::Schema::Loader do
85
89
  field :post do
86
90
  type post_type
87
91
  argument :id, !types.ID
88
- argument :varied, variant_input_type, default_value: { id: "123", int: 234, float: 2.3, enum: "FOO", sub: [{ string: "str" }] }
92
+ argument :varied, variant_input_type, default_value: { id: "123", int: 234, float: 2.3, enum: :foo, sub: [{ string: "str" }] }
89
93
  end
90
94
 
91
95
  field :content do
@@ -166,8 +170,37 @@ describe GraphQL::Schema::Loader do
166
170
  end
167
171
  end
168
172
 
173
+ let(:loaded_schema) { GraphQL::Schema::Loader.load(schema_json) }
174
+
169
175
  it "returns the schema" do
170
- assert_deep_equal(schema, GraphQL::Schema::Loader.load(schema_json))
176
+ assert_deep_equal(schema, loaded_schema)
177
+ end
178
+
179
+ it "can export the loaded schema" do
180
+ assert loaded_schema.execute(GraphQL::Introspection::INTROSPECTION_QUERY)
181
+ end
182
+
183
+ it "sets correct default values on custom scalar arguments" do
184
+ type = loaded_schema.types["Comment"]
185
+ field = type.fields['fieldWithArg']
186
+ arg = field.arguments['bigint']
187
+
188
+ assert_equal((2**54).to_s, arg.default_value)
189
+ end
190
+
191
+ it "sets correct default values on custom scalar input fields" do
192
+ type = loaded_schema.types["Varied"]
193
+ field = type.input_fields['bigint']
194
+
195
+ assert_equal((2**54).to_s, field.default_value)
196
+ end
197
+
198
+ it "sets correct default values for complex field arguments" do
199
+ type = loaded_schema.types['Query']
200
+ field = type.fields['post']
201
+ arg = field.arguments['varied']
202
+
203
+ assert_equal arg.default_value, { 'id' => "123", 'bigint' => nil, 'bool' => nil, 'int' => 234, 'float' => 2.3, 'enum' => "FOO", 'sub' => [{ 'string' => "str" }] }
171
204
  end
172
205
  end
173
206
  end