graphql 0.11.0 → 0.11.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/graphql/base_type.rb +6 -2
- data/lib/graphql/definition_helpers.rb +0 -5
- data/lib/graphql/definition_helpers/defined_by_config.rb +106 -102
- data/lib/graphql/definition_helpers/non_null_with_bang.rb +13 -9
- data/lib/graphql/definition_helpers/string_named_hash.rb +19 -15
- data/lib/graphql/definition_helpers/type_definer.rb +25 -21
- data/lib/graphql/enum_type.rb +8 -2
- data/lib/graphql/float_type.rb +1 -1
- data/lib/graphql/input_object_type.rb +27 -6
- data/lib/graphql/interface_type.rb +10 -0
- data/lib/graphql/introspection/fields_field.rb +2 -2
- data/lib/graphql/list_type.rb +12 -2
- data/lib/graphql/non_null_type.rb +11 -1
- data/lib/graphql/object_type.rb +19 -0
- data/lib/graphql/query.rb +2 -14
- data/lib/graphql/query/input_validation_result.rb +23 -0
- data/lib/graphql/query/literal_input.rb +3 -1
- data/lib/graphql/query/serial_execution/field_resolution.rb +3 -1
- data/lib/graphql/query/serial_execution/selection_resolution.rb +21 -15
- data/lib/graphql/query/variable_validation_error.rb +18 -0
- data/lib/graphql/query/variables.rb +4 -8
- data/lib/graphql/scalar_type.rb +10 -4
- data/lib/graphql/schema.rb +4 -2
- data/lib/graphql/schema/printer.rb +12 -3
- data/lib/graphql/schema/type_reducer.rb +4 -3
- data/lib/graphql/static_validation.rb +1 -0
- data/lib/graphql/static_validation/all_rules.rb +1 -0
- data/lib/graphql/static_validation/rules/document_does_not_exceed_max_depth.rb +79 -0
- data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +1 -1
- data/lib/graphql/static_validation/validation_context.rb +63 -0
- data/lib/graphql/static_validation/validator.rb +1 -52
- data/lib/graphql/version.rb +1 -1
- data/readme.md +21 -22
- data/spec/graphql/enum_type_spec.rb +8 -0
- data/spec/graphql/input_object_type_spec.rb +101 -3
- data/spec/graphql/introspection/schema_type_spec.rb +6 -6
- data/spec/graphql/introspection/type_type_spec.rb +6 -6
- data/spec/graphql/language/transform_spec.rb +9 -5
- data/spec/graphql/list_type_spec.rb +23 -0
- data/spec/graphql/object_type_spec.rb +11 -4
- data/spec/graphql/query/executor_spec.rb +34 -5
- data/spec/graphql/query_spec.rb +22 -3
- data/spec/graphql/scalar_type_spec.rb +28 -0
- data/spec/graphql/schema/type_reducer_spec.rb +2 -2
- data/spec/graphql/static_validation/complexity_validator_spec.rb +15 -0
- data/spec/graphql/static_validation/rules/document_does_not_exceed_max_depth_spec.rb +93 -0
- data/spec/support/dairy_app.rb +2 -2
- data/spec/support/minimum_input_object.rb +8 -5
- metadata +10 -4
- data/spec/graphql/static_validation/complexity_validator.rb +0 -15
@@ -19,7 +19,7 @@ class GraphQL::StaticValidation::FieldsAreDefinedOnType
|
|
19
19
|
return GraphQL::Language::Visitor::SKIP
|
20
20
|
end
|
21
21
|
|
22
|
-
field = parent_type.
|
22
|
+
field = parent_type.get_field(ast_field.name)
|
23
23
|
if field.nil?
|
24
24
|
errors << message("Field '#{ast_field.name}' doesn't exist on type '#{parent_type.name}'", parent)
|
25
25
|
return GraphQL::Language::Visitor::SKIP
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module GraphQL
|
2
|
+
module StaticValidation
|
3
|
+
# The validation context gets passed to each validator.
|
4
|
+
#
|
5
|
+
# It exposes a {GraphQL::Language::Visitor} where validators may add hooks. ({Visitor#visit} is called in {Validator#validate})
|
6
|
+
#
|
7
|
+
# It provides access to the schema & fragments which validators may read from.
|
8
|
+
#
|
9
|
+
# It holds a list of errors which each validator may add to.
|
10
|
+
#
|
11
|
+
# It also provides limited access to the {TypeStack} instance,
|
12
|
+
# which tracks state as you climb in and out of different fields.
|
13
|
+
class ValidationContext
|
14
|
+
attr_reader :schema, :document, :errors, :visitor, :fragments, :operations
|
15
|
+
def initialize(schema, document)
|
16
|
+
@schema = schema
|
17
|
+
@document = document
|
18
|
+
@fragments = {}
|
19
|
+
@operations = {}
|
20
|
+
|
21
|
+
document.definitions.each do |definition|
|
22
|
+
case definition
|
23
|
+
when GraphQL::Language::Nodes::FragmentDefinition
|
24
|
+
@fragments[definition.name] = definition
|
25
|
+
when GraphQL::Language::Nodes::OperationDefinition
|
26
|
+
@operations[definition.name] = definition
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
@errors = []
|
31
|
+
@visitor = GraphQL::Language::Visitor.new
|
32
|
+
@type_stack = GraphQL::StaticValidation::TypeStack.new(schema, visitor)
|
33
|
+
end
|
34
|
+
|
35
|
+
def object_types
|
36
|
+
@type_stack.object_types
|
37
|
+
end
|
38
|
+
|
39
|
+
# @return [GraphQL::Field, nil] The most-recently-entered GraphQL::Field, if currently inside one
|
40
|
+
def field_definition
|
41
|
+
@type_stack.field_definitions.last
|
42
|
+
end
|
43
|
+
|
44
|
+
# @return [GraphQL::Directive, nil] The most-recently-entered GraphQL::Directive, if currently inside one
|
45
|
+
def directive_definition
|
46
|
+
@type_stack.directive_definitions.last
|
47
|
+
end
|
48
|
+
|
49
|
+
# @return [GraphQL::Argument, nil] The most-recently-entered GraphQL::Argument, if currently inside one
|
50
|
+
def argument_definition
|
51
|
+
# Don't get the _last_ one because that's the current one.
|
52
|
+
# Get the second-to-last one, which is the parent of the current one.
|
53
|
+
@type_stack.argument_definitions[-2]
|
54
|
+
end
|
55
|
+
|
56
|
+
# Don't try to validate dynamic fields
|
57
|
+
# since they aren't defined by the type system
|
58
|
+
def skip_field?(field_name)
|
59
|
+
GraphQL::Schema::DYNAMIC_FIELDS.include?(field_name)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -19,62 +19,11 @@ class GraphQL::StaticValidation::Validator
|
|
19
19
|
# @param document [GraphQL::Language::Nodes::Document]
|
20
20
|
# @return [Array<Hash>]
|
21
21
|
def validate(document)
|
22
|
-
context =
|
22
|
+
context = GraphQL::StaticValidation::ValidationContext.new(@schema, document)
|
23
23
|
@rules.each do |rules|
|
24
24
|
rules.new.validate(context)
|
25
25
|
end
|
26
26
|
context.visitor.visit(document)
|
27
27
|
context.errors.map(&:to_h)
|
28
28
|
end
|
29
|
-
|
30
|
-
# The validation context gets passed to each validator.
|
31
|
-
#
|
32
|
-
# It exposes a {GraphQL::Language::Visitor} where validators may add hooks. ({Visitor#visit} is called in {Validator#validate})
|
33
|
-
#
|
34
|
-
# It provides access to the schema & fragments which validators may read from.
|
35
|
-
#
|
36
|
-
# It holds a list of errors which each validator may add to.
|
37
|
-
#
|
38
|
-
# It also provides limited access to the {TypeStack} instance,
|
39
|
-
# which tracks state as you climb in and out of different fields.
|
40
|
-
class Context
|
41
|
-
attr_reader :schema, :document, :errors, :visitor, :fragments
|
42
|
-
def initialize(schema, document)
|
43
|
-
@schema = schema
|
44
|
-
@document = document
|
45
|
-
@fragments = document.definitions.each_with_object({}) do |part, memo|
|
46
|
-
part.is_a?(GraphQL::Language::Nodes::FragmentDefinition) && memo[part.name] = part
|
47
|
-
end
|
48
|
-
@errors = []
|
49
|
-
@visitor = GraphQL::Language::Visitor.new
|
50
|
-
@type_stack = GraphQL::StaticValidation::TypeStack.new(schema, visitor)
|
51
|
-
end
|
52
|
-
|
53
|
-
def object_types
|
54
|
-
@type_stack.object_types
|
55
|
-
end
|
56
|
-
|
57
|
-
# @return [GraphQL::Field, nil] The most-recently-entered GraphQL::Field, if currently inside one
|
58
|
-
def field_definition
|
59
|
-
@type_stack.field_definitions.last
|
60
|
-
end
|
61
|
-
|
62
|
-
# @return [GraphQL::Directive, nil] The most-recently-entered GraphQL::Directive, if currently inside one
|
63
|
-
def directive_definition
|
64
|
-
@type_stack.directive_definitions.last
|
65
|
-
end
|
66
|
-
|
67
|
-
# @return [GraphQL::Argument, nil] The most-recently-entered GraphQL::Argument, if currently inside one
|
68
|
-
def argument_definition
|
69
|
-
# Don't get the _last_ one because that's the current one.
|
70
|
-
# Get the second-to-last one, which is the parent of the current one.
|
71
|
-
@type_stack.argument_definitions[-2]
|
72
|
-
end
|
73
|
-
|
74
|
-
# Don't try to validate dynamic fields
|
75
|
-
# since they aren't defined by the type system
|
76
|
-
def skip_field?(field_name)
|
77
|
-
GraphQL::Schema::DYNAMIC_FIELDS.include?(field_name)
|
78
|
-
end
|
79
|
-
end
|
80
29
|
end
|
data/lib/graphql/version.rb
CHANGED
data/readme.md
CHANGED
@@ -58,7 +58,10 @@ QueryType = GraphQL::ObjectType.define do
|
|
58
58
|
end
|
59
59
|
|
60
60
|
# Then create your schema
|
61
|
-
Schema = GraphQL::Schema.new(
|
61
|
+
Schema = GraphQL::Schema.new(
|
62
|
+
query: QueryType,
|
63
|
+
max_depth: 8,
|
64
|
+
)
|
62
65
|
```
|
63
66
|
|
64
67
|
See also:
|
@@ -108,24 +111,6 @@ https://medium.com/@gauravtiwari/graphql-and-relay-on-rails-first-relay-powered-
|
|
108
111
|
3. http://mgiroux.me/2015/getting-started-with-rails-graphql-relay/
|
109
112
|
4. http://mgiroux.me/2015/uploading-files-using-relay-with-rails/
|
110
113
|
|
111
|
-
## To Do
|
112
|
-
|
113
|
-
- Code clean-up
|
114
|
-
- Raise if you try to configure an attribute which doesn't suit the type (ie, if you try to define `resolve` on an ObjectType, it should somehow raise)
|
115
|
-
- make `DefinitionHelpers` more friendly for extension
|
116
|
-
- Interface's possible types should be a property of the schema, not the interface
|
117
|
-
- Type lookup should be by type name (to support reloaded constants in Rails code)
|
118
|
-
- Add a complexity validator (reject queries if they're too big)
|
119
|
-
- Add a custom dump for Relay (it expects default value strings to be double-quoted)
|
120
|
-
- Make variable validation provide a specific, useful message
|
121
|
-
- Add docs for shared behaviors & DRY code
|
122
|
-
- After releasing the next version, use it for [graphql-libgraphqlparser](https://github.com/rmosolgo/graphql-libgraphqlparser-ruby)
|
123
|
-
- Big ideas:
|
124
|
-
- Revamp the fixture Schema to be more useful (better names, more extensible)
|
125
|
-
- __Subscriptions__
|
126
|
-
- This is a good chance to make an `Operation` abstraction of which `query`, `mutation` and `subscription` are members
|
127
|
-
- For a subscription, `graphql` would send an outbound message to the system (allow the host application to manage its own subscriptions via Pusher, ActionCable, whatever)
|
128
|
-
|
129
114
|
## Goals
|
130
115
|
|
131
116
|
- Implement the GraphQL spec & support a Relay front end
|
@@ -145,8 +130,22 @@ https://medium.com/@gauravtiwari/graphql-and-relay-on-rails-first-relay-powered-
|
|
145
130
|
- [`graphql-batch`](https://github.com/shopify/graphql-batch), a batched query execution strategy
|
146
131
|
- [`graphql-parallel`](https://github.com/rmosolgo/graphql-parallel), an asynchronous query execution strategy
|
147
132
|
- [Example Relay support](https://github.com/rmosolgo/graphql-relay-ruby) in Ruby
|
133
|
+
- [`graphql-libgraphqlparser`](https://github.com/rmosolgo/graphql-libgraphqlparser), bindings to [libgraphqlparser](https://github.com/graphql/libgraphqlparser), a C-level parser.
|
148
134
|
|
149
|
-
##
|
135
|
+
## To Do
|
150
136
|
|
151
|
-
-
|
152
|
-
-
|
137
|
+
- Code clean-up
|
138
|
+
- Raise if you try to configure an attribute which doesn't suit the type (ie, if you try to define `resolve` on an ObjectType, it should somehow raise)
|
139
|
+
- make `DefinitionHelpers` more friendly for extension
|
140
|
+
- Interface's possible types should be a property of the schema, not the interface
|
141
|
+
- Type lookup should be by type name (to support reloaded constants in Rails code)
|
142
|
+
- Depth validator should be aware of fragments
|
143
|
+
- Add a complexity validator (reject queries if they're too big)
|
144
|
+
- Add a custom dump for Relay (it expects default value strings to be double-quoted)
|
145
|
+
- Add docs for shared behaviors & DRY code
|
146
|
+
- Optimize the pure-Ruby parser (hand-write, RACC?!)
|
147
|
+
- Big ideas:
|
148
|
+
- Revamp the fixture Schema to be more useful (better names, more extensible)
|
149
|
+
- __Subscriptions__
|
150
|
+
- This is a good chance to make an `Operation` abstraction of which `query`, `mutation` and `subscription` are members
|
151
|
+
- For a subscription, `graphql` would send an outbound message to the system (allow the host application to manage its own subscriptions via Pusher, ActionCable, whatever)
|
@@ -16,4 +16,12 @@ describe GraphQL::EnumType do
|
|
16
16
|
it 'has value description' do
|
17
17
|
assert_equal("Animal with horns", enum.values['GOAT'].description)
|
18
18
|
end
|
19
|
+
|
20
|
+
describe 'validate_input with bad input' do
|
21
|
+
let(:result) { DairyAnimalEnum.validate_input('bad enum') }
|
22
|
+
|
23
|
+
it 'returns an invalid result' do
|
24
|
+
assert(!result.valid?)
|
25
|
+
end
|
26
|
+
end
|
19
27
|
end
|
@@ -12,8 +12,106 @@ describe GraphQL::InputObjectType do
|
|
12
12
|
|
13
13
|
describe "input validation" do
|
14
14
|
it "Accepts anything that yields key-value pairs to #all?" do
|
15
|
-
values_obj = MinimumInputObject.new
|
16
|
-
assert DairyProductInputType.
|
15
|
+
values_obj = MinimumInputObject.new({"source" => "COW", "fatContent" => 0.4})
|
16
|
+
assert DairyProductInputType.valid_input?(values_obj)
|
17
|
+
end
|
18
|
+
|
19
|
+
describe 'validate_input with non-enumerable input' do
|
20
|
+
it "returns a valid result for MinimumInputObject" do
|
21
|
+
result = DairyProductInputType.validate_input(MinimumInputObject.new({"source" => "COW", "fatContent" => 0.4}))
|
22
|
+
assert(result.valid?)
|
23
|
+
end
|
24
|
+
|
25
|
+
it "returns an invalid result for MinimumInvalidInputObject" do
|
26
|
+
invalid_input = MinimumInputObject.new({"source" => "KOALA", "fatContent" => 0.4})
|
27
|
+
result = DairyProductInputType.validate_input(invalid_input)
|
28
|
+
assert(!result.valid?)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe 'validate_input with enumerable input' do
|
33
|
+
describe 'with good input' do
|
34
|
+
let(:input) do
|
35
|
+
{
|
36
|
+
'source' => 'COW',
|
37
|
+
'fatContent' => 0.4
|
38
|
+
}
|
39
|
+
end
|
40
|
+
let(:result) { DairyProductInputType.validate_input(input) }
|
41
|
+
|
42
|
+
it 'returns a valid result' do
|
43
|
+
assert(result.valid?)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe 'with bad enum and float' do
|
48
|
+
let(:result) { DairyProductInputType.validate_input('source' => 'KOALA', 'fatContent' => 'bad_num') }
|
49
|
+
|
50
|
+
it 'returns an invalid result' do
|
51
|
+
assert(!result.valid?)
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'has problems with correct paths' do
|
55
|
+
paths = result.problems.map { |p| p['path'] }
|
56
|
+
assert(paths.include?(['source']))
|
57
|
+
assert(paths.include?(['fatContent']))
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'has correct problem explanation' do
|
61
|
+
expected = DairyAnimalEnum.validate_input('KOALA').problems[0]['explanation']
|
62
|
+
|
63
|
+
source_problem = result.problems.detect { |p| p['path'] == ['source'] }
|
64
|
+
actual = source_problem['explanation']
|
65
|
+
|
66
|
+
assert_equal(expected, actual)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
describe 'with extra argument' do
|
71
|
+
let(:result) { DairyProductInputType.validate_input('source' => 'COW', 'fatContent' => 0.4, 'isDelicious' => false) }
|
72
|
+
|
73
|
+
it 'returns an invalid result' do
|
74
|
+
assert(!result.valid?)
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'has problem with correct path' do
|
78
|
+
paths = result.problems.map { |p| p['path'] }
|
79
|
+
assert_equal(paths, [['isDelicious']])
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'has correct problem explanation' do
|
83
|
+
assert(result.problems[0]['explanation'].include?('Field is not defined'))
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
describe 'list with one invalid element' do
|
88
|
+
let(:list_type) { GraphQL::ListType.new(of_type: DairyProductInputType) }
|
89
|
+
let(:result) do
|
90
|
+
list_type.validate_input([
|
91
|
+
{ 'source' => 'COW', 'fatContent' => 0.4 },
|
92
|
+
{ 'source' => 'KOALA', 'fatContent' => 0.4 }
|
93
|
+
])
|
94
|
+
end
|
95
|
+
|
96
|
+
it 'returns an invalid result' do
|
97
|
+
assert(!result.valid?)
|
98
|
+
end
|
99
|
+
|
100
|
+
it 'has one problem' do
|
101
|
+
assert_equal(result.problems.length, 1)
|
102
|
+
end
|
103
|
+
|
104
|
+
it 'has problem with correct path' do
|
105
|
+
path = result.problems[0]['path']
|
106
|
+
assert_equal(path, [1, 'source'])
|
107
|
+
end
|
108
|
+
|
109
|
+
it 'has problem with correct explanation' do
|
110
|
+
expected = DairyAnimalEnum.validate_input('KOALA').problems[0]['explanation']
|
111
|
+
actual = result.problems[0]['explanation']
|
112
|
+
assert_equal(expected, actual)
|
113
|
+
end
|
114
|
+
end
|
17
115
|
end
|
18
116
|
end
|
19
117
|
|
@@ -22,7 +120,7 @@ describe GraphQL::InputObjectType do
|
|
22
120
|
let(:result) { DummySchema.execute(query_string, variables: variables) }
|
23
121
|
|
24
122
|
describe "list inputs" do
|
25
|
-
let(:variables) { {"search" => [MinimumInputObject.new]} }
|
123
|
+
let(:variables) { {"search" => [MinimumInputObject.new({"source" => "COW", "fatContent" => 0.4})]} }
|
26
124
|
let(:query_string) {%|
|
27
125
|
query getCheeses($search: [DairyProductInput]!){
|
28
126
|
sheep: searchDairy(product: [{source: SHEEP, fatContent: 0.1}]) {
|
@@ -19,15 +19,15 @@ describe GraphQL::Introspection::SchemaType do
|
|
19
19
|
"queryType"=>{
|
20
20
|
"fields"=>[
|
21
21
|
{"name"=>"cheese"},
|
22
|
-
{"name"=>"milk"},
|
23
|
-
{"name"=>"dairy"},
|
24
|
-
{"name"=>"fromSource"},
|
25
|
-
{"name"=>"favoriteEdible"},
|
26
22
|
{"name"=>"cow"},
|
27
|
-
{"name"=>"
|
23
|
+
{"name"=>"dairy"},
|
28
24
|
{"name"=>"error"},
|
29
25
|
{"name"=>"executionError"},
|
30
|
-
{"name"=>"
|
26
|
+
{"name"=>"favoriteEdible"},
|
27
|
+
{"name"=>"fromSource"},
|
28
|
+
{"name"=>"maybeNull"},
|
29
|
+
{"name"=>"milk"},
|
30
|
+
{"name"=>"searchDairy"},
|
31
31
|
]
|
32
32
|
},
|
33
33
|
"mutationType"=> {
|
@@ -12,11 +12,11 @@ describe GraphQL::Introspection::TypeType do
|
|
12
12
|
|}
|
13
13
|
let(:result) { DummySchema.execute(query_string, context: {}, variables: {"cheeseId" => 2}) }
|
14
14
|
let(:cheese_fields) {[
|
15
|
-
{"name"=>"id", "isDeprecated" => false, "type" => { "name" => "Non-Null", "ofType" => { "name" => "Int"}}},
|
16
15
|
{"name"=>"flavor", "isDeprecated" => false, "type" => { "name" => "Non-Null", "ofType" => { "name" => "String"}}},
|
16
|
+
{"name"=>"id", "isDeprecated" => false, "type" => { "name" => "Non-Null", "ofType" => { "name" => "Int"}}},
|
17
17
|
{"name"=>"origin", "isDeprecated" => false, "type" => { "name" => "Non-Null", "ofType" => { "name" => "String"}}},
|
18
|
-
{"name"=>"source", "isDeprecated" => false, "type" => { "name" => "Non-Null", "ofType" => { "name" => "DairyAnimal"}}},
|
19
18
|
{"name"=>"similarCheese", "isDeprecated"=>false, "type"=>{"name"=>"Cheese", "ofType"=>nil}},
|
19
|
+
{"name"=>"source", "isDeprecated" => false, "type" => { "name" => "Non-Null", "ofType" => { "name" => "DairyAnimal"}}},
|
20
20
|
]}
|
21
21
|
|
22
22
|
let(:dairy_animals) {[
|
@@ -37,11 +37,11 @@ describe GraphQL::Introspection::TypeType do
|
|
37
37
|
{"name"=>"AnimalProduct"}
|
38
38
|
],
|
39
39
|
"fields"=>[
|
40
|
-
{"type"=>{"name"=>"Non-Null", "ofType"=>{"name"=>"ID"}}},
|
41
|
-
{"type"=>{"name"=>"DairyAnimal", "ofType"=>nil}},
|
42
|
-
{"type"=>{"name"=>"Non-Null", "ofType"=>{"name"=>"String"}}},
|
43
40
|
{"type"=>{"name"=>"Non-Null", "ofType"=>{"name"=>"Float"}}},
|
44
41
|
{"type"=>{"name"=>"List", "ofType"=>{"name"=>"String"}}},
|
42
|
+
{"type"=>{"name"=>"Non-Null", "ofType"=>{"name"=>"ID"}}},
|
43
|
+
{"type"=>{"name"=>"Non-Null", "ofType"=>{"name"=>"String"}}},
|
44
|
+
{"type"=>{"name"=>"DairyAnimal", "ofType"=>nil}},
|
45
45
|
]
|
46
46
|
},
|
47
47
|
"dairyAnimal"=>{
|
@@ -75,7 +75,7 @@ describe GraphQL::Introspection::TypeType do
|
|
75
75
|
|}
|
76
76
|
let(:deprecated_fields) { {"name"=>"fatContent", "isDeprecated"=>true, "type"=>{"name"=>"Non-Null", "ofType"=>{"name"=>"Float"}}} }
|
77
77
|
it 'can expose deprecated fields' do
|
78
|
-
new_cheese_fields =
|
78
|
+
new_cheese_fields = [deprecated_fields] + cheese_fields
|
79
79
|
expected = { "data" => {
|
80
80
|
"cheeseType" => {
|
81
81
|
"name"=> "Cheese",
|
@@ -113,17 +113,21 @@ describe GraphQL::Language::Transform do
|
|
113
113
|
|
114
114
|
it 'transforms input objects' do
|
115
115
|
res_one_pair = get_result(%q|{one: 1}|, parse: :value_input_object)
|
116
|
-
|
116
|
+
res_many_pair = get_result(%q|{first: "Apple", second: "Banana", third: ORANGE}|, parse: :value_input_object)
|
117
117
|
res_empty = get_result(%q|{}|, parse: :value_input_object)
|
118
118
|
res_empty_space = get_result(%q|{ }|, parse: :value_input_object)
|
119
119
|
|
120
120
|
assert_equal('one', res_one_pair.arguments[0].name)
|
121
121
|
assert_equal(1 , res_one_pair.arguments[0].value)
|
122
122
|
|
123
|
-
assert_equal(
|
124
|
-
assert_equal(
|
125
|
-
assert_equal(
|
126
|
-
assert_equal(
|
123
|
+
assert_equal("first" , res_many_pair.arguments[0].name)
|
124
|
+
assert_equal("Apple" , res_many_pair.arguments[0].value)
|
125
|
+
assert_equal("second", res_many_pair.arguments[1].name)
|
126
|
+
assert_equal("Banana", res_many_pair.arguments[1].value)
|
127
|
+
|
128
|
+
assert(res_many_pair.arguments[2].value.is_a?(GraphQL::Language::Nodes::Enum))
|
129
|
+
assert_equal("third", res_many_pair.arguments[2].name)
|
130
|
+
assert_equal("ORANGE", res_many_pair.arguments[2].value.name)
|
127
131
|
|
128
132
|
assert_equal([], res_empty.arguments)
|
129
133
|
assert_equal([], res_empty_space.arguments)
|
@@ -6,4 +6,27 @@ describe GraphQL::ListType do
|
|
6
6
|
it 'coerces elements in the list' do
|
7
7
|
assert_equal([1.0, 2.0, 3.0].inspect, float_list.coerce_input([1, 2, 3]).inspect)
|
8
8
|
end
|
9
|
+
|
10
|
+
describe 'validate_input with bad input' do
|
11
|
+
let(:bad_num) { 'bad_num' }
|
12
|
+
let(:result) { float_list.validate_input([bad_num, 2.0, 3.0]) }
|
13
|
+
|
14
|
+
it 'returns an invalid result' do
|
15
|
+
assert(!result.valid?)
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'has one problem' do
|
19
|
+
assert_equal(result.problems.length, 1)
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'has path [0]' do
|
23
|
+
assert_equal(result.problems[0]['path'], [0])
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'has the correct explanation' do
|
27
|
+
expected = GraphQL::FLOAT_TYPE.validate_input(bad_num).problems[0]['explanation']
|
28
|
+
actual = result.problems[0]['explanation']
|
29
|
+
assert_equal(actual, expected)
|
30
|
+
end
|
31
|
+
end
|
9
32
|
end
|