gqli 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a2253633cb84598f7d7150f620b9161152c0c5961c92124ea2d517e97e7452b7
4
- data.tar.gz: 84ceda416865f6295632b9044eeeeaeba29b24822a988a45e3cf8e1e6f85bfa9
3
+ metadata.gz: 972ad0f4a3e57ffe7e87a9492805b5d206ff0825ac335dbbf4a24b4981aed375
4
+ data.tar.gz: d60e39618526584c5291bfe1fa050e1639cc458e01d1038bb62e88693764c833
5
5
  SHA512:
6
- metadata.gz: 298ecee49c25bca164942b594298fefe8ddd23d3757cac7ef3a5bd1c725aa4ee72be6ef583adf35469ff28f6a3d42bd9a7ec3ded93d76542109a35eda803ced4
7
- data.tar.gz: bc5e10415ab97c885a816e856e5b2da73f18e3e49d6c34d81933f5bc80b6841b1be15c4596193cd270ac82bc51e2e5af4e59a99bd788ee2a692fd46f9ef5144c
6
+ metadata.gz: 40b7e0a94c6c543993131280bb00dc56e9d7600c9dc698970f66ccb3ff67613820c35199d60f74d7c2ec75417f52bd73ce528b9263193b70ed7e5a6498bc835f
7
+ data.tar.gz: 00c23e2e51019bcf85a608507062d0fb46662b008674e09c546aa6151bedbb45d3c4098fd0e7fcc247d39794d96315003544429a9dd097dda172bec6ee2f36d1
@@ -29,3 +29,6 @@ Style/MutableConstant:
29
29
 
30
30
  Style/SignalException:
31
31
  EnforcedStyle: 'semantic'
32
+
33
+ Style/DoubleNegation:
34
+ Enabled: false
@@ -2,6 +2,10 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## v0.4.0
6
+ ### Added
7
+ * Add directive support.
8
+
5
9
  ## v0.3.0
6
10
  ### Added
7
11
  * Refactored validations to their own `Validation` class, which now provide better error messages upon validation failure.
data/README.md CHANGED
@@ -291,6 +291,34 @@ query = GQLi::DSL.query {
291
291
  }
292
292
  ```
293
293
 
294
+ ### Directives
295
+
296
+ In GraphQL, nodes can be selectively included or removed by the usage of directives, there are 2 directives available for the querying specification to do this: `@include` and `@skip`.
297
+
298
+ ```ruby
299
+ query = GQLi::DSL.query {
300
+ someNode(:@include => {if: object.includes_some_node?})
301
+ }
302
+ ```
303
+
304
+ This will get transformed to:
305
+
306
+ ```graphql
307
+ query {
308
+ someNode @include(if: true) # or false
309
+ }
310
+ ```
311
+
312
+ This behaviour is equivalent to using native Ruby to include/exclude a field from the query:
313
+
314
+ ```ruby
315
+ query = GQLi::DSL.query {
316
+ someNode if object.includes_some_node?
317
+ }
318
+ ```
319
+
320
+ The difference is that by using the native implementation, in case of the condition not being met, the node will be completely not included in the outgoing query.
321
+
294
322
  ## Yet to be implemented
295
323
 
296
324
  * Mutation queries
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GQLi
4
+ # Wrapper for Enum values
5
+ class EnumValue
6
+ attr_reader :value
7
+
8
+ def initialize(value)
9
+ @value = value
10
+ end
11
+
12
+ # Serializes the enum value to string
13
+ def to_s
14
+ value
15
+ end
16
+ end
17
+ end
@@ -15,7 +15,7 @@ module GQLi
15
15
  # Serializes to a GraphQL string
16
16
  def to_gql
17
17
  result = ' ' * __depth + __name
18
- result += '(' + __params_to_s(__params, true) + ')' unless __params.empty?
18
+ result += __params_to_s(__params, true) unless __params.empty?
19
19
  unless __nodes.empty?
20
20
  result += " {\n"
21
21
  result += __nodes.map(&:to_gql).join("\n")
@@ -27,14 +27,27 @@ module GQLi
27
27
 
28
28
  private
29
29
 
30
+ def __directive?(params)
31
+ params.size == 1 && params.keys.first.to_s.start_with?('@')
32
+ end
33
+
34
+ def __directive(params)
35
+ params.first.tap do |directive, directive_params|
36
+ return " #{directive}#{__params_to_s(directive_params, true)}"
37
+ end
38
+ end
39
+
30
40
  def __params_to_s(params, initial = false)
31
41
  case params
32
42
  when ::Hash
43
+ return __directive(params) if __directive?(params)
44
+
33
45
  result = params.map do |k, v|
34
46
  "#{k}: #{__params_to_s(v)}"
35
47
  end.join(', ')
36
48
 
37
- return result if initial
49
+ return "(#{result})" if initial
50
+
38
51
  "{#{result}}"
39
52
  when ::Array
40
53
  "[#{params.map { |p| __params_to_s(p) }.join(', ')}]"
@@ -44,7 +44,7 @@ module GQLi
44
44
  end
45
45
 
46
46
  def validate_node(parent_type, node)
47
- return if parent_type.kind == 'SCALAR'
47
+ validate_directives(node)
48
48
 
49
49
  return valid_match_node?(parent_type, node) if node.__name.start_with?('... on')
50
50
 
@@ -66,8 +66,26 @@ module GQLi
66
66
  fail "Match type '#{node.__name.gsub('... on ', '')}' invalid"
67
67
  end
68
68
 
69
+ def validate_directives(node)
70
+ return unless node.__params.size == 1
71
+ node.__params.first.tap do |k, v|
72
+ break unless k.to_s.start_with?('@')
73
+
74
+ fail "Directive unknown '#{k}'" unless %i[@include @skip].include?(k)
75
+ fail "Missing arguments for directive '#{k}'" if v.nil? || !v.is_a?(::Hash) || v.empty?
76
+ v.each do |arg, value|
77
+ begin
78
+ fail "Invalid argument '#{arg}' for directive '#{k}'" if arg.to_s != 'if'
79
+ fail "Invalid value for 'if`, must be a boolean" if value != !!value
80
+ rescue StandardError => e
81
+ errors << e
82
+ end
83
+ end
84
+ end
85
+ end
86
+
69
87
  def validate_params(node_type, node)
70
- node.__params.each do |param, value|
88
+ node.__params.reject { |p, _| p.to_s.start_with?('@') }.each do |param, value|
71
89
  begin
72
90
  arg = node_type.fetch('args', []).find { |a| a.name == param.to_s }
73
91
  fail "Invalid argument '#{param}'" if arg.nil?
@@ -3,5 +3,5 @@
3
3
  # GraphQL Client and DSL library
4
4
  module GQLi
5
5
  # Gem version
6
- VERSION = '0.3.0'
6
+ VERSION = '0.4.0'
7
7
  end
@@ -116,6 +116,20 @@ describe GQLi::DSL do
116
116
  GRAPHQL
117
117
  end
118
118
 
119
+ it 'nodes can have directives' do
120
+ query = subject.query {
121
+ someNode(:@include => {if: true})
122
+ otherNode(:@skip => {if: false})
123
+ }
124
+
125
+ expect(query.to_gql).to eq <<~GRAPHQL
126
+ query {
127
+ someNode @include(if: true)
128
+ otherNode @skip(if: false)
129
+ }
130
+ GRAPHQL
131
+ end
132
+
119
133
  it 'nodes can have arbitrarily nested nodes' do
120
134
  query = subject.query {
121
135
  aNode {
@@ -155,5 +155,98 @@ describe GQLi::Introspection do
155
155
  expect(validation.errors).not_to be_empty
156
156
  expect(validation.errors.map(&:to_s)).to include("Value is 'String, Enum or ID', but should be 'Int' for 'limit'")
157
157
  end
158
+
159
+ describe 'directives' do
160
+ it 'can create a query with a directive and validations should not fail' do
161
+ query = dsl.query {
162
+ catCollection {
163
+ items {
164
+ name(:@include => { if: true })
165
+ }
166
+ }
167
+ }
168
+
169
+ validation = subject.validate(query)
170
+ expect(validation.valid?).to be_truthy
171
+ end
172
+
173
+ it 'unknown directives will fail' do
174
+ query = dsl.query {
175
+ catCollection(:@unknownDirective => nil)
176
+ }
177
+
178
+ validation = subject.validate(query)
179
+ expect(validation.valid?).to be_falsey
180
+ expect(validation.errors).not_to be_empty
181
+ expect(validation.errors.map(&:to_s)).to include("Directive unknown '@unknownDirective'")
182
+ end
183
+
184
+ it 'known directive will fail if no arguments are passed' do
185
+ query = dsl.query {
186
+ catCollection(:@include => nil)
187
+ }
188
+
189
+ validation = subject.validate(query)
190
+ expect(validation.valid?).to be_falsey
191
+ expect(validation.errors).not_to be_empty
192
+ expect(validation.errors.map(&:to_s)).to include("Missing arguments for directive '@include'")
193
+ end
194
+
195
+ it 'known directive will fail if arguments are empty' do
196
+ query = dsl.query {
197
+ catCollection(:@include => {})
198
+ }
199
+
200
+ validation = subject.validate(query)
201
+ expect(validation.valid?).to be_falsey
202
+ expect(validation.errors).not_to be_empty
203
+ expect(validation.errors.map(&:to_s)).to include("Missing arguments for directive '@include'")
204
+ end
205
+
206
+ it 'known directive will fail if arguments is not if' do
207
+ query = dsl.query {
208
+ catCollection {
209
+ items {
210
+ name(:@include => { else: true })
211
+ }
212
+ }
213
+ }
214
+
215
+ validation = subject.validate(query)
216
+ expect(validation.valid?).to be_falsey
217
+ expect(validation.errors).not_to be_empty
218
+ expect(validation.errors.map(&:to_s)).to include("Invalid argument 'else' for directive '@include'")
219
+ end
220
+
221
+ it 'known directive will fail when if value is not boolean' do
222
+ query = dsl.query {
223
+ catCollection {
224
+ items {
225
+ name(:@include => { if: 123 })
226
+ }
227
+ }
228
+ }
229
+
230
+ validation = subject.validate(query)
231
+ expect(validation.valid?).to be_falsey
232
+ expect(validation.errors).not_to be_empty
233
+ expect(validation.errors.map(&:to_s)).to include("Invalid value for 'if`, must be a boolean")
234
+ end
235
+
236
+ it 'known directive will fail when multiple arguments are passed' do
237
+ query = dsl.query {
238
+ catCollection {
239
+ items {
240
+ name(:@include => { if: true, else: false })
241
+ }
242
+ }
243
+ }
244
+
245
+ validation = subject.validate(query)
246
+ expect(validation.valid?).to be_falsey
247
+ expect(validation.errors).not_to be_empty
248
+ expect(validation.errors.map(&:to_s)).to include("Invalid argument 'else' for directive '@include'")
249
+ end
250
+ end
158
251
  end
159
252
  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.3.0
4
+ version: 0.4.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-19 00:00:00.000000000 Z
11
+ date: 2018-11-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: http
@@ -311,6 +311,7 @@ files:
311
311
  - doc/GQLi/Base.html
312
312
  - doc/GQLi/Client.html
313
313
  - doc/GQLi/DSL.html
314
+ - doc/GQLi/EnumValue.html
314
315
  - doc/GQLi/Fragment.html
315
316
  - doc/GQLi/Introspection.html
316
317
  - doc/GQLi/Node.html
@@ -338,6 +339,7 @@ files:
338
339
  - lib/gqli/base.rb
339
340
  - lib/gqli/client.rb
340
341
  - lib/gqli/dsl.rb
342
+ - lib/gqli/enum_value.rb
341
343
  - lib/gqli/fragment.rb
342
344
  - lib/gqli/introspection.rb
343
345
  - lib/gqli/node.rb