gqli 0.2.0 → 0.3.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.
- checksums.yaml +4 -4
- data/.rubocop_todo.yml +8 -0
- data/CHANGELOG.md +4 -0
- data/README.md +1 -2
- data/lib/gqli/base.rb +2 -4
- data/lib/gqli/client.rb +14 -2
- data/lib/gqli/introspection.rb +7 -123
- data/lib/gqli/validation.rb +170 -0
- data/lib/gqli/version.rb +1 -1
- data/spec/lib/gqli/client_spec.rb +6 -1
- data/spec/lib/gqli/introspection_spec.rb +34 -0
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a2253633cb84598f7d7150f620b9161152c0c5961c92124ea2d517e97e7452b7
|
4
|
+
data.tar.gz: 84ceda416865f6295632b9044eeeeaeba29b24822a988a45e3cf8e1e6f85bfa9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 298ecee49c25bca164942b594298fefe8ddd23d3757cac7ef3a5bd1c725aa4ee72be6ef583adf35469ff28f6a3d42bd9a7ec3ded93d76542109a35eda803ced4
|
7
|
+
data.tar.gz: bc5e10415ab97c885a816e856e5b2da73f18e3e49d6c34d81933f5bc80b6841b1be15c4596193cd270ac82bc51e2e5af4e59a99bd788ee2a692fd46f9ef5144c
|
data/.rubocop_todo.yml
CHANGED
@@ -9,6 +9,8 @@
|
|
9
9
|
# Offense count: 3
|
10
10
|
Metrics/AbcSize:
|
11
11
|
Max: 20
|
12
|
+
Exclude:
|
13
|
+
- 'lib/gqli/validation.rb'
|
12
14
|
|
13
15
|
# Blocks can be arbitrarily long due to GraphQL DSL syntax.
|
14
16
|
Metrics/BlockLength:
|
@@ -17,6 +19,8 @@ Metrics/BlockLength:
|
|
17
19
|
# Offense count: 1
|
18
20
|
Metrics/CyclomaticComplexity:
|
19
21
|
Max: 11
|
22
|
+
Exclude:
|
23
|
+
- 'lib/gqli/validation.rb'
|
20
24
|
|
21
25
|
# Offense count: 8
|
22
26
|
# Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
|
@@ -28,10 +32,14 @@ Metrics/LineLength:
|
|
28
32
|
# Configuration parameters: CountComments.
|
29
33
|
Metrics/MethodLength:
|
30
34
|
Max: 15
|
35
|
+
Exclude:
|
36
|
+
- 'lib/gqli/validation.rb'
|
31
37
|
|
32
38
|
# Offense count: 1
|
33
39
|
Metrics/PerceivedComplexity:
|
34
40
|
Max: 8
|
41
|
+
Exclude:
|
42
|
+
- 'lib/gqli/validation.rb'
|
35
43
|
|
36
44
|
# Block delimiters are `{...}` to match GraphQL syntax more closely.
|
37
45
|
Style/BlockDelimiters:
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,10 @@
|
|
2
2
|
|
3
3
|
## Unreleased
|
4
4
|
|
5
|
+
## v0.3.0
|
6
|
+
### Added
|
7
|
+
* Refactored validations to their own `Validation` class, which now provide better error messages upon validation failure.
|
8
|
+
|
5
9
|
## v0.2.0
|
6
10
|
### Added
|
7
11
|
* Added `__node` to be able to create nodes in case there's a name collision with a reserved keyword or a built-in method.
|
data/README.md
CHANGED
@@ -281,7 +281,7 @@ query = GQLi::DSL.query {
|
|
281
281
|
|
282
282
|
The helper method `__node`, can also receive arguments and have children nodes as expected from any other node declaration, for example:
|
283
283
|
|
284
|
-
```
|
284
|
+
```ruby
|
285
285
|
query = GQLi::DSL.query {
|
286
286
|
__node('catCollection', limit: 5) {
|
287
287
|
items {
|
@@ -295,7 +295,6 @@ query = GQLi::DSL.query {
|
|
295
295
|
|
296
296
|
* Mutation queries
|
297
297
|
* Subscription queries
|
298
|
-
* Detailed validation errors
|
299
298
|
|
300
299
|
## Get involved
|
301
300
|
|
data/lib/gqli/base.rb
CHANGED
@@ -19,8 +19,7 @@ module GQLi
|
|
19
19
|
|
20
20
|
# Adds type match node
|
21
21
|
def __on(type_name, &block)
|
22
|
-
|
23
|
-
@__nodes << Node.new("... on #{type_name}", {}, __depth + 1, &block)
|
22
|
+
__node("... on #{type_name}", {}, &block)
|
24
23
|
end
|
25
24
|
|
26
25
|
# Adds children node into current node
|
@@ -52,8 +51,7 @@ module GQLi
|
|
52
51
|
end
|
53
52
|
|
54
53
|
def method_missing(name, *args, &block)
|
55
|
-
|
56
|
-
@__nodes << Node.new(name.to_s, __params_from_args(args), __depth + 1, &block)
|
54
|
+
__node(name.to_s, __params_from_args(args), &block)
|
57
55
|
end
|
58
56
|
end
|
59
57
|
end
|
data/lib/gqli/client.rb
CHANGED
@@ -23,7 +23,10 @@ module GQLi
|
|
23
23
|
# Executes a query
|
24
24
|
# If validations are enabled, will perform validation check before request.
|
25
25
|
def execute(query)
|
26
|
-
|
26
|
+
if validate_query
|
27
|
+
validation = schema.validate(query)
|
28
|
+
fail validation_error_message(validation) unless validation.valid?
|
29
|
+
end
|
27
30
|
|
28
31
|
execute!(query)
|
29
32
|
end
|
@@ -44,11 +47,20 @@ module GQLi
|
|
44
47
|
def valid?(query)
|
45
48
|
return true unless validate_query
|
46
49
|
|
47
|
-
|
50
|
+
schema.valid?(query)
|
48
51
|
end
|
49
52
|
|
50
53
|
protected
|
51
54
|
|
55
|
+
def validation_error_message(validation)
|
56
|
+
<<~ERROR
|
57
|
+
Validation Error: query is invalid - HTTP Request not sent.
|
58
|
+
|
59
|
+
Errors:
|
60
|
+
- #{validation.errors.join("\n - ")}
|
61
|
+
ERROR
|
62
|
+
end
|
63
|
+
|
52
64
|
def request_headers
|
53
65
|
{
|
54
66
|
accept: 'application/json',
|
data/lib/gqli/introspection.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative './dsl'
|
4
|
+
require_relative './validation'
|
4
5
|
|
5
6
|
module GQLi
|
6
7
|
# Introspection schema and validator
|
@@ -85,131 +86,14 @@ module GQLi
|
|
85
86
|
@types = schema.types
|
86
87
|
end
|
87
88
|
|
88
|
-
# Returns
|
89
|
-
def
|
90
|
-
|
91
|
-
|
92
|
-
query_type = types.find { |t| t.name.casecmp('query').zero? }
|
93
|
-
query.__nodes.each do |node|
|
94
|
-
return false unless valid_node?(query_type, node)
|
95
|
-
end
|
96
|
-
|
97
|
-
true
|
98
|
-
end
|
99
|
-
|
100
|
-
private
|
101
|
-
|
102
|
-
def valid_node?(parent_type, node)
|
103
|
-
return true if parent_type.kind == 'SCALAR'
|
104
|
-
|
105
|
-
return valid_match_node?(parent_type, node) if node.__name.start_with?('... on')
|
106
|
-
|
107
|
-
node_type = parent_type.fetch('fields', []).find { |f| f.name == node.__name }
|
108
|
-
return false if node_type.nil?
|
109
|
-
|
110
|
-
return false unless valid_params?(node_type, node)
|
111
|
-
|
112
|
-
resolved_node_type = type_for(node_type)
|
113
|
-
return false if resolved_node_type.nil?
|
114
|
-
|
115
|
-
return false unless valid_nesting_node?(resolved_node_type, node)
|
116
|
-
|
117
|
-
node.__nodes.all? { |n| valid_node?(resolved_node_type, n) }
|
118
|
-
end
|
119
|
-
|
120
|
-
def valid_match_node?(parent_type, node)
|
121
|
-
return true if parent_type.fetch('possibleTypes', []).find { |t| t.name == node.__name.gsub('... on ', '') }
|
122
|
-
false
|
123
|
-
end
|
124
|
-
|
125
|
-
def valid_params?(node_type, node)
|
126
|
-
node.__params.each do |param, value|
|
127
|
-
arg = node_type.fetch('args', []).find { |a| a.name == param.to_s }
|
128
|
-
return false if arg.nil?
|
129
|
-
|
130
|
-
arg_type = type_for(arg)
|
131
|
-
return false if arg_type.nil?
|
132
|
-
|
133
|
-
return false unless valid_value_for_type?(arg_type, value)
|
134
|
-
end
|
135
|
-
|
136
|
-
true
|
89
|
+
# Returns the evaluated validation for a query
|
90
|
+
def validate(query)
|
91
|
+
Validation.new(self, query)
|
137
92
|
end
|
138
93
|
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
true
|
143
|
-
end
|
144
|
-
|
145
|
-
def valid_object_node?(node_type, node)
|
146
|
-
return false if %w[OBJECT INTERFACE].include?(node_type.kind) && node.__nodes.empty?
|
147
|
-
true
|
148
|
-
end
|
149
|
-
|
150
|
-
def valid_array_node?(node_type, node)
|
151
|
-
return false if %w[OBJECT INTERFACE].include?(node_type.kind) && node.__nodes.empty?
|
152
|
-
true
|
153
|
-
end
|
154
|
-
|
155
|
-
def valid_value_for_type?(arg_type, value)
|
156
|
-
case value
|
157
|
-
when ::String
|
158
|
-
return false unless arg_type.name == 'String' || arg_type.name == 'ID'
|
159
|
-
when ::Integer
|
160
|
-
return false unless arg_type.name == 'Int'
|
161
|
-
when ::Float
|
162
|
-
return false unless arg_type.name == 'Float'
|
163
|
-
when ::Hash
|
164
|
-
return valid_hash_value?(arg_type, value)
|
165
|
-
when true, false
|
166
|
-
return false unless arg_type.name == 'Boolean'
|
167
|
-
else
|
168
|
-
return false
|
169
|
-
end
|
170
|
-
|
171
|
-
true
|
172
|
-
end
|
173
|
-
|
174
|
-
def valid_hash_value?(arg_type, value)
|
175
|
-
return false unless arg_type.kind == 'INPUT_OBJECT'
|
176
|
-
|
177
|
-
type = types.find { |f| f.name == arg_type.name }
|
178
|
-
return false if type.nil?
|
179
|
-
|
180
|
-
value.each do |k, v|
|
181
|
-
input_field = type.fetch('inputFields', []).find { |f| f.name == k.to_s }
|
182
|
-
return false if input_field.nil?
|
183
|
-
|
184
|
-
input_field_type = type_for(input_field)
|
185
|
-
return false if input_field_type.nil?
|
186
|
-
|
187
|
-
return false unless valid_value_for_type?(input_field_type, v)
|
188
|
-
end
|
189
|
-
end
|
190
|
-
|
191
|
-
def type_for(field_type)
|
192
|
-
type = case field_type.type.kind
|
193
|
-
when 'NON_NULL'
|
194
|
-
non_null_type(field_type.type.ofType)
|
195
|
-
when 'LIST'
|
196
|
-
field_type.type.ofType
|
197
|
-
when 'OBJECT', 'INTERFACE', 'INPUT_OBJECT'
|
198
|
-
field_type.type
|
199
|
-
when 'SCALAR'
|
200
|
-
field_type.type
|
201
|
-
end
|
202
|
-
|
203
|
-
types.find { |t| t.name == type.name }
|
204
|
-
end
|
205
|
-
|
206
|
-
def non_null_type(non_null)
|
207
|
-
case non_null.kind
|
208
|
-
when 'LIST'
|
209
|
-
non_null.ofType
|
210
|
-
else
|
211
|
-
non_null
|
212
|
-
end
|
94
|
+
# Returns if the query is valid
|
95
|
+
def valid?(query)
|
96
|
+
validate(query).valid?
|
213
97
|
end
|
214
98
|
end
|
215
99
|
end
|
@@ -0,0 +1,170 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GQLi
|
4
|
+
# Validations
|
5
|
+
class Validation
|
6
|
+
attr_reader :schema, :query, :errors
|
7
|
+
|
8
|
+
def initialize(schema, query)
|
9
|
+
@schema = schema
|
10
|
+
@query = query
|
11
|
+
@errors = []
|
12
|
+
|
13
|
+
validate
|
14
|
+
end
|
15
|
+
|
16
|
+
# Returns wether the query is valid or not
|
17
|
+
def valid?
|
18
|
+
errors.empty?
|
19
|
+
end
|
20
|
+
|
21
|
+
protected
|
22
|
+
|
23
|
+
def validate
|
24
|
+
fail 'Not a Query object' unless query.is_a?(Query)
|
25
|
+
|
26
|
+
query_type = types.find { |t| t.name.casecmp('query').zero? }
|
27
|
+
query.__nodes.each do |node|
|
28
|
+
begin
|
29
|
+
validate_node(query_type, node)
|
30
|
+
rescue StandardError => e
|
31
|
+
errors << e
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
true
|
36
|
+
rescue StandardError => e
|
37
|
+
errors << e
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def types
|
43
|
+
schema.types
|
44
|
+
end
|
45
|
+
|
46
|
+
def validate_node(parent_type, node)
|
47
|
+
return if parent_type.kind == 'SCALAR'
|
48
|
+
|
49
|
+
return valid_match_node?(parent_type, node) if node.__name.start_with?('... on')
|
50
|
+
|
51
|
+
node_type = parent_type.fetch('fields', []).find { |f| f.name == node.__name }
|
52
|
+
fail "Node type not found for '#{node.__name}'" if node_type.nil?
|
53
|
+
|
54
|
+
validate_params(node_type, node)
|
55
|
+
|
56
|
+
resolved_node_type = type_for(node_type)
|
57
|
+
fail "Node type not found for '#{node.__name}'" if resolved_node_type.nil?
|
58
|
+
|
59
|
+
validate_nesting_node(resolved_node_type, node)
|
60
|
+
|
61
|
+
node.__nodes.each { |n| validate_node(resolved_node_type, n) }
|
62
|
+
end
|
63
|
+
|
64
|
+
def valid_match_node?(parent_type, node)
|
65
|
+
return if parent_type.fetch('possibleTypes', []).find { |t| t.name == node.__name.gsub('... on ', '') }
|
66
|
+
fail "Match type '#{node.__name.gsub('... on ', '')}' invalid"
|
67
|
+
end
|
68
|
+
|
69
|
+
def validate_params(node_type, node)
|
70
|
+
node.__params.each do |param, value|
|
71
|
+
begin
|
72
|
+
arg = node_type.fetch('args', []).find { |a| a.name == param.to_s }
|
73
|
+
fail "Invalid argument '#{param}'" if arg.nil?
|
74
|
+
|
75
|
+
arg_type = type_for(arg)
|
76
|
+
fail "Argument type not found for '#{param}'" if arg_type.nil?
|
77
|
+
|
78
|
+
validate_value_for_type(arg_type, value, param)
|
79
|
+
rescue StandardError => e
|
80
|
+
errors << e
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def validate_nesting_node(node_type, node)
|
86
|
+
fail "Invalid object for node '#{node.__name}'" unless valid_object_node?(node_type, node)
|
87
|
+
end
|
88
|
+
|
89
|
+
def valid_object_node?(node_type, node)
|
90
|
+
return false if %w[OBJECT INTERFACE].include?(node_type.kind) && node.__nodes.empty?
|
91
|
+
true
|
92
|
+
end
|
93
|
+
|
94
|
+
def valid_array_node?(node_type, node)
|
95
|
+
return false if %w[OBJECT INTERFACE].include?(node_type.kind) && node.__nodes.empty?
|
96
|
+
true
|
97
|
+
end
|
98
|
+
|
99
|
+
def value_type_error(is_type, should_be, for_arg)
|
100
|
+
fail "Value is '#{is_type}', but should be '#{should_be}' for '#{for_arg}'"
|
101
|
+
end
|
102
|
+
|
103
|
+
def validate_value_for_type(arg_type, value, for_arg)
|
104
|
+
case value
|
105
|
+
when ::String
|
106
|
+
unless arg_type.name == 'String' || arg_type.kind == 'ENUM' || arg_type.name == 'ID'
|
107
|
+
value_type_error('String, Enum or ID', arg_type.name, for_arg)
|
108
|
+
end
|
109
|
+
if arg_type.kind == 'ENUM' && !arg_type.enumValues.map(&:name).include?(value)
|
110
|
+
fail "Invalid value for Enum '#{arg_type.name}' for '#{for_arg}'"
|
111
|
+
end
|
112
|
+
when ::Integer
|
113
|
+
value_type_error('Integer', arg_type.name, for_arg) unless arg_type.name == 'Int'
|
114
|
+
when ::Float
|
115
|
+
value_type_error('Float', arg_type.name, for_arg) unless arg_type.name == 'Float'
|
116
|
+
when ::Hash
|
117
|
+
validate_hash_value(arg_type, value, for_arg)
|
118
|
+
when true, false
|
119
|
+
value_type_error('Boolean', arg_type.name, for_arg) unless arg_type.name == 'Boolean'
|
120
|
+
else
|
121
|
+
value_type_error(value.class.name, arg_type.name, for_arg)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def validate_hash_value(arg_type, value, for_arg)
|
126
|
+
value_type_error('Object', arg_type.name, for_arg) unless arg_type.kind == 'INPUT_OBJECT'
|
127
|
+
|
128
|
+
type = types.find { |f| f.name == arg_type.name }
|
129
|
+
fail "Type not found for '#{arg_type.name}'" if type.nil?
|
130
|
+
|
131
|
+
value.each do |k, v|
|
132
|
+
begin
|
133
|
+
input_field = type.fetch('inputFields', []).find { |f| f.name == k.to_s }
|
134
|
+
fail "Input field definition not found for '#{k}'" if input_field.nil?
|
135
|
+
|
136
|
+
input_field_type = type_for(input_field)
|
137
|
+
fail "Input field type not found for '#{k}'" if input_field_type.nil?
|
138
|
+
|
139
|
+
validate_value_for_type(input_field_type, v, k)
|
140
|
+
rescue StandardError => e
|
141
|
+
errors << e
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def type_for(field_type)
|
147
|
+
type = case field_type.type.kind
|
148
|
+
when 'NON_NULL'
|
149
|
+
non_null_type(field_type.type.ofType)
|
150
|
+
when 'LIST'
|
151
|
+
field_type.type.ofType
|
152
|
+
when 'OBJECT', 'INTERFACE', 'INPUT_OBJECT'
|
153
|
+
field_type.type
|
154
|
+
when 'SCALAR'
|
155
|
+
field_type.type
|
156
|
+
end
|
157
|
+
|
158
|
+
types.find { |t| t.name == type.name }
|
159
|
+
end
|
160
|
+
|
161
|
+
def non_null_type(non_null)
|
162
|
+
case non_null.kind
|
163
|
+
when 'LIST'
|
164
|
+
non_null.ofType
|
165
|
+
else
|
166
|
+
non_null
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
data/lib/gqli/version.rb
CHANGED
@@ -49,7 +49,12 @@ describe GQLi::Client do
|
|
49
49
|
dsl.query {
|
50
50
|
foobar
|
51
51
|
}
|
52
|
-
)}.to raise_exception
|
52
|
+
)}.to raise_exception <<~ERROR
|
53
|
+
Validation Error: query is invalid - HTTP Request not sent.
|
54
|
+
|
55
|
+
Errors:
|
56
|
+
- Node type not found for 'foobar'
|
57
|
+
ERROR
|
53
58
|
}
|
54
59
|
end
|
55
60
|
|
@@ -54,6 +54,10 @@ describe GQLi::Introspection do
|
|
54
54
|
}
|
55
55
|
|
56
56
|
expect(subject.valid?(query)).to be_truthy
|
57
|
+
|
58
|
+
validation = subject.validate(query)
|
59
|
+
expect(validation.valid?).to be_truthy
|
60
|
+
expect(validation.errors).to be_empty
|
57
61
|
end
|
58
62
|
|
59
63
|
it 'wrong node returns false' do
|
@@ -62,6 +66,11 @@ describe GQLi::Introspection do
|
|
62
66
|
}
|
63
67
|
|
64
68
|
expect(subject.valid?(query)).to be_falsey
|
69
|
+
|
70
|
+
validation = subject.validate(query)
|
71
|
+
expect(validation.valid?).to be_falsey
|
72
|
+
expect(validation.errors).not_to be_empty
|
73
|
+
expect(validation.errors.map(&:to_s)).to include("Node type not found for 'foo'")
|
65
74
|
end
|
66
75
|
|
67
76
|
it 'object node that doesnt have proper values returns false' do
|
@@ -70,6 +79,11 @@ describe GQLi::Introspection do
|
|
70
79
|
}
|
71
80
|
|
72
81
|
expect(subject.valid?(query)).to be_falsey
|
82
|
+
|
83
|
+
validation = subject.validate(query)
|
84
|
+
expect(validation.valid?).to be_falsey
|
85
|
+
expect(validation.errors).not_to be_empty
|
86
|
+
expect(validation.errors.map(&:to_s)).to include("Invalid object for node 'catCollection'")
|
73
87
|
end
|
74
88
|
|
75
89
|
it 'object list node that doesnt have proper values returns false' do
|
@@ -80,6 +94,11 @@ describe GQLi::Introspection do
|
|
80
94
|
}
|
81
95
|
|
82
96
|
expect(subject.valid?(query)).to be_falsey
|
97
|
+
|
98
|
+
validation = subject.validate(query)
|
99
|
+
expect(validation.valid?).to be_falsey
|
100
|
+
expect(validation.errors).not_to be_empty
|
101
|
+
expect(validation.errors.map(&:to_s)).to include("Invalid object for node 'items'")
|
83
102
|
end
|
84
103
|
|
85
104
|
it 'type matching on invalid type returns false' do
|
@@ -96,6 +115,11 @@ describe GQLi::Introspection do
|
|
96
115
|
}
|
97
116
|
|
98
117
|
expect(subject.valid?(query)).to be_falsey
|
118
|
+
|
119
|
+
validation = subject.validate(query)
|
120
|
+
expect(validation.valid?).to be_falsey
|
121
|
+
expect(validation.errors).not_to be_empty
|
122
|
+
expect(validation.errors.map(&:to_s)).to include("Match type 'InvalidType' invalid")
|
99
123
|
end
|
100
124
|
|
101
125
|
it 'invalid arguments return false' do
|
@@ -108,6 +132,11 @@ describe GQLi::Introspection do
|
|
108
132
|
}
|
109
133
|
|
110
134
|
expect(subject.valid?(query)).to be_falsey
|
135
|
+
|
136
|
+
validation = subject.validate(query)
|
137
|
+
expect(validation.valid?).to be_falsey
|
138
|
+
expect(validation.errors).not_to be_empty
|
139
|
+
expect(validation.errors.map(&:to_s)).to include("Invalid argument 'invalidParam'")
|
111
140
|
end
|
112
141
|
|
113
142
|
it 'invalid argument type returns false' do
|
@@ -120,6 +149,11 @@ describe GQLi::Introspection do
|
|
120
149
|
}
|
121
150
|
|
122
151
|
expect(subject.valid?(query)).to be_falsey
|
152
|
+
|
153
|
+
validation = subject.validate(query)
|
154
|
+
expect(validation.valid?).to be_falsey
|
155
|
+
expect(validation.errors).not_to be_empty
|
156
|
+
expect(validation.errors.map(&:to_s)).to include("Value is 'String, Enum or ID', but should be 'Int' for 'limit'")
|
123
157
|
end
|
124
158
|
end
|
125
159
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gqli
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Contentful GmbH (David Litvak Bruno)
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-10-
|
11
|
+
date: 2018-10-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: http
|
@@ -316,6 +316,7 @@ files:
|
|
316
316
|
- doc/GQLi/Node.html
|
317
317
|
- doc/GQLi/Query.html
|
318
318
|
- doc/GQLi/Response.html
|
319
|
+
- doc/GQLi/Validation.html
|
319
320
|
- doc/_index.html
|
320
321
|
- doc/class_list.html
|
321
322
|
- doc/css/common.css
|
@@ -342,6 +343,7 @@ files:
|
|
342
343
|
- lib/gqli/node.rb
|
343
344
|
- lib/gqli/query.rb
|
344
345
|
- lib/gqli/response.rb
|
346
|
+
- lib/gqli/validation.rb
|
345
347
|
- lib/gqli/version.rb
|
346
348
|
- spec/fixtures/vcr_cassettes/catCollection.yml
|
347
349
|
- spec/fixtures/vcr_cassettes/client.yml
|