graphql 0.18.15 → 0.19.0

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.
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