graphql-dsl 1.0.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 +7 -0
- data/.github/workflows/main.yml +23 -0
- data/.gitignore +12 -0
- data/.rspec +2 -0
- data/.rubocop.yml +44 -0
- data/.ruby-version +1 -0
- data/.yardopts +1 -0
- data/CODE_OF_CONDUCT.md +84 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +64 -0
- data/LICENSE.txt +21 -0
- data/README.md +899 -0
- data/Rakefile +6 -0
- data/bin/console +15 -0
- data/bin/setup +7 -0
- data/graphql-dsl.gemspec +38 -0
- data/lib/graphql/dsl/constants.rb +9 -0
- data/lib/graphql/dsl/error.rb +32 -0
- data/lib/graphql/dsl/formatter/arguments.rb +26 -0
- data/lib/graphql/dsl/formatter/directives.rb +52 -0
- data/lib/graphql/dsl/formatter/executable_document.rb +22 -0
- data/lib/graphql/dsl/formatter/field.rb +49 -0
- data/lib/graphql/dsl/formatter/formatter.rb +41 -0
- data/lib/graphql/dsl/formatter/fragment_operation.rb +41 -0
- data/lib/graphql/dsl/formatter/fragment_spread.rb +25 -0
- data/lib/graphql/dsl/formatter/inline_fragment.rb +43 -0
- data/lib/graphql/dsl/formatter/operation.rb +60 -0
- data/lib/graphql/dsl/formatter/values.rb +146 -0
- data/lib/graphql/dsl/formatter/variable_definitions.rb +43 -0
- data/lib/graphql/dsl/nodes/containers/directive.rb +46 -0
- data/lib/graphql/dsl/nodes/containers/variable_definition.rb +52 -0
- data/lib/graphql/dsl/nodes/executable_document.rb +69 -0
- data/lib/graphql/dsl/nodes/field.rb +39 -0
- data/lib/graphql/dsl/nodes/fragment_operation.rb +36 -0
- data/lib/graphql/dsl/nodes/fragment_spread.rb +24 -0
- data/lib/graphql/dsl/nodes/inline_fragment.rb +34 -0
- data/lib/graphql/dsl/nodes/mixins/selection_set.rb +106 -0
- data/lib/graphql/dsl/nodes/node.rb +39 -0
- data/lib/graphql/dsl/nodes/operation.rb +61 -0
- data/lib/graphql/dsl/version.rb +9 -0
- data/lib/graphql/dsl.rb +230 -0
- data/lib/graphql-dsl.rb +3 -0
- data/lib/graphql_dsl.rb +36 -0
- data/tasks/readme/update.rake +143 -0
- metadata +173 -0
data/README.md
ADDED
@@ -0,0 +1,899 @@
|
|
1
|
+
<h1 align="center">✨ GraphQL DSL ✨</h1>
|
2
|
+
|
3
|
+
<p align="center">
|
4
|
+
<a href="https://github.com/maxd/graphql-dsl/actions/workflows/main.yml"><img src="https://github.com/maxd/graphql-dsl/actions/workflows/main.yml/badge.svg" alt="main" /></a>
|
5
|
+
<a href="https://www.ruby-lang.org/en/"><img src="https://img.shields.io/static/v1?label=language&message=Ruby&color=CC342D&style=flat&logo=ruby&logoColor=CC342D" alt="ruby" /></a>
|
6
|
+
<a href="https://graphql.org/"><img src="https://img.shields.io/static/v1?label=language&message=GraphQL&color=E10098&style=flat&logo=graphql&logoColor=E10098" alt="GraphQL" /></a>
|
7
|
+
</p>
|
8
|
+
|
9
|
+
`graphql-dsl` lets you easy create GraphQL queries by code:
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
extend GraphQL::DSL # Include DSL methods like `query`, `mutation`, etc.
|
13
|
+
using GraphQL::DSL # Include refine methods like `variable` and `directive` if required.
|
14
|
+
|
15
|
+
# Query alive characters from Rick and Morty unofficial GraphQL API:
|
16
|
+
# https://rickandmortyapi.com/graphql
|
17
|
+
puts query(:aliveCharacters, species: variable(:String!, 'Human')) {
|
18
|
+
characters(filter: { status: 'Alive', species: :$species }) {
|
19
|
+
results {
|
20
|
+
name
|
21
|
+
image
|
22
|
+
}
|
23
|
+
}
|
24
|
+
}.to_gql
|
25
|
+
```
|
26
|
+
|
27
|
+
```graphql
|
28
|
+
query aliveCharacters($species: String! = "Human")
|
29
|
+
{
|
30
|
+
characters(filter: {status: "Alive", species: $species})
|
31
|
+
{
|
32
|
+
results
|
33
|
+
{
|
34
|
+
name
|
35
|
+
image
|
36
|
+
}
|
37
|
+
}
|
38
|
+
}
|
39
|
+
```
|
40
|
+
|
41
|
+
## 🧩 Supported GraphQL features
|
42
|
+
|
43
|
+
The GraphQL DSL base on [draft](https://spec.graphql.org/draft/) version of GraphQL specification (updated at Wed, Sep 15, 2021)
|
44
|
+
and support these features:
|
45
|
+
|
46
|
+
- [x] Executable Documents (exclude type system definition documents)
|
47
|
+
- [x] All operations (`query`, `mutation`, `subscription`) and their features (variable, directives, selection sets, etc.)
|
48
|
+
- [x] All field features (aliases, arguments, directives, etc.)
|
49
|
+
- [x] Fragments (include inline fragments)
|
50
|
+
|
51
|
+
## ⚙️ Installation
|
52
|
+
|
53
|
+
Add this line to your application's Gemfile:
|
54
|
+
|
55
|
+
```ruby
|
56
|
+
gem 'graphql-dsl', '~> 1.0.0'
|
57
|
+
```
|
58
|
+
|
59
|
+
And then execute `bundle install`.
|
60
|
+
|
61
|
+
## ⚡️ Getting Started
|
62
|
+
|
63
|
+
Choose an appropriate way to use GraphQL DSL:
|
64
|
+
|
65
|
+
1. Call methods of `GraphQL::DSL` module directly
|
66
|
+
|
67
|
+
```ruby
|
68
|
+
rockets_query = GraphQL::DSL.query {
|
69
|
+
rockets {
|
70
|
+
name
|
71
|
+
}
|
72
|
+
}.to_gql
|
73
|
+
|
74
|
+
puts rockets_query
|
75
|
+
```
|
76
|
+
|
77
|
+
<details>
|
78
|
+
<summary>STDOUT</summary>
|
79
|
+
|
80
|
+
```graphql
|
81
|
+
{
|
82
|
+
rockets
|
83
|
+
{
|
84
|
+
name
|
85
|
+
}
|
86
|
+
}
|
87
|
+
```
|
88
|
+
</details>
|
89
|
+
|
90
|
+
1. Extend class or module use `GraphQL::DSL` module
|
91
|
+
|
92
|
+
```ruby
|
93
|
+
module SpaceXQueries
|
94
|
+
extend GraphQL::DSL
|
95
|
+
|
96
|
+
# Create constant with GraphQL query
|
97
|
+
ROCKETS = query {
|
98
|
+
rockets {
|
99
|
+
name
|
100
|
+
}
|
101
|
+
}.to_gql
|
102
|
+
end
|
103
|
+
|
104
|
+
puts SpaceXQueries::ROCKETS
|
105
|
+
```
|
106
|
+
|
107
|
+
<details>
|
108
|
+
<summary>STDOUT</summary>
|
109
|
+
|
110
|
+
```graphql
|
111
|
+
{
|
112
|
+
rockets
|
113
|
+
{
|
114
|
+
name
|
115
|
+
}
|
116
|
+
}
|
117
|
+
```
|
118
|
+
</details>
|
119
|
+
|
120
|
+
```ruby
|
121
|
+
module SpaceXQueries
|
122
|
+
extend GraphQL::DSL
|
123
|
+
|
124
|
+
# `extend self` or `module_function` required to
|
125
|
+
# call of `SpaceXQueries.rockets`
|
126
|
+
extend self
|
127
|
+
|
128
|
+
# use memorization or lazy initialization
|
129
|
+
# to avoid generation of query on each method call
|
130
|
+
def rockets
|
131
|
+
query {
|
132
|
+
rockets {
|
133
|
+
name
|
134
|
+
}
|
135
|
+
}.to_gql
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
puts SpaceXQueries.rockets
|
140
|
+
```
|
141
|
+
|
142
|
+
<details>
|
143
|
+
<summary>STDOUT</summary>
|
144
|
+
|
145
|
+
```graphql
|
146
|
+
{
|
147
|
+
rockets
|
148
|
+
{
|
149
|
+
name
|
150
|
+
}
|
151
|
+
}
|
152
|
+
```
|
153
|
+
</details>
|
154
|
+
|
155
|
+
1. Include `GraphQL::DSL` module to class
|
156
|
+
|
157
|
+
```ruby
|
158
|
+
class SpaceXQueries
|
159
|
+
include GraphQL::DSL
|
160
|
+
|
161
|
+
# use memorization or lazy initialization
|
162
|
+
# to avoid generation of query on each method call
|
163
|
+
def rockets
|
164
|
+
query {
|
165
|
+
rockets {
|
166
|
+
name
|
167
|
+
}
|
168
|
+
}.to_gql
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
queries = SpaceXQueries.new
|
173
|
+
puts queries.rockets
|
174
|
+
```
|
175
|
+
|
176
|
+
<details>
|
177
|
+
<summary>STDOUT</summary>
|
178
|
+
|
179
|
+
```graphql
|
180
|
+
{
|
181
|
+
rockets
|
182
|
+
{
|
183
|
+
name
|
184
|
+
}
|
185
|
+
}
|
186
|
+
```
|
187
|
+
</details>
|
188
|
+
|
189
|
+
## 👀 Examples
|
190
|
+
|
191
|
+
:bulb: _Non-official SpaceX GraphQL and Rick and Morty APIs are using for most of examples.
|
192
|
+
So, you can test generated GraphQL queries [here](https://api.spacex.land/graphql/) and [here](https://rickandmortyapi.com/graphql)._
|
193
|
+
|
194
|
+
### Operations
|
195
|
+
|
196
|
+
The GraphQL support three types of operations:
|
197
|
+
|
198
|
+
* `query` - for fetch data.
|
199
|
+
* `mutation` - for update data.
|
200
|
+
* `subscription` - for fetch stream of data during a log time.
|
201
|
+
|
202
|
+
To create these operations use correspond GraphQL DSL methods:
|
203
|
+
|
204
|
+
* `GraphQL::DSL#query`
|
205
|
+
* `GraphQL::DSL#mutation`
|
206
|
+
* `GraphQL::DSL#subscription`
|
207
|
+
|
208
|
+
:bulb: _All of them have the same signatures therefore all examples below will use `query` operation._
|
209
|
+
|
210
|
+
#### Anonymous operations
|
211
|
+
|
212
|
+
Call correspond `GraphQL::DSL` method without any arguments to create anonymous operation:
|
213
|
+
|
214
|
+
```ruby
|
215
|
+
puts GraphQL::DSL.query {
|
216
|
+
rockets {
|
217
|
+
name
|
218
|
+
}
|
219
|
+
}.to_gql
|
220
|
+
```
|
221
|
+
|
222
|
+
<details>
|
223
|
+
<summary>STDOUT</summary>
|
224
|
+
|
225
|
+
```graphql
|
226
|
+
{
|
227
|
+
rockets
|
228
|
+
{
|
229
|
+
name
|
230
|
+
}
|
231
|
+
}
|
232
|
+
```
|
233
|
+
</details>
|
234
|
+
|
235
|
+
#### Named operations
|
236
|
+
|
237
|
+
Use string or symbol to specify operation name:
|
238
|
+
|
239
|
+
```ruby
|
240
|
+
puts GraphQL::DSL.query(:rockets) {
|
241
|
+
rockets {
|
242
|
+
name
|
243
|
+
}
|
244
|
+
}.to_gql
|
245
|
+
```
|
246
|
+
|
247
|
+
<details>
|
248
|
+
<summary>STDOUT</summary>
|
249
|
+
|
250
|
+
```graphql
|
251
|
+
query rockets
|
252
|
+
{
|
253
|
+
rockets
|
254
|
+
{
|
255
|
+
name
|
256
|
+
}
|
257
|
+
}
|
258
|
+
```
|
259
|
+
</details>
|
260
|
+
|
261
|
+
#### Parameterized operations
|
262
|
+
|
263
|
+
Pass variable definitions to second argument of correspond `GraphQL::DSL` method:
|
264
|
+
|
265
|
+
```ruby
|
266
|
+
using GraphQL::DSL # Include refined `variable` method
|
267
|
+
|
268
|
+
puts GraphQL::DSL.query(:capsules, type: :String, status: variable(:String!, 'active')) {
|
269
|
+
capsules(find: { type: :$type, status: :$status }) {
|
270
|
+
type
|
271
|
+
status
|
272
|
+
landings
|
273
|
+
}
|
274
|
+
}.to_gql
|
275
|
+
```
|
276
|
+
|
277
|
+
<details>
|
278
|
+
<summary>STDOUT</summary>
|
279
|
+
|
280
|
+
```graphql
|
281
|
+
query capsules($type: String, $status: String! = "active")
|
282
|
+
{
|
283
|
+
capsules(find: {type: $type, status: $status})
|
284
|
+
{
|
285
|
+
type
|
286
|
+
status
|
287
|
+
landings
|
288
|
+
}
|
289
|
+
}
|
290
|
+
```
|
291
|
+
</details>
|
292
|
+
|
293
|
+
Choose appropriate notation to define variable type, default value and directives:
|
294
|
+
|
295
|
+
:bulb: _See more about types definition [here](#types)._
|
296
|
+
|
297
|
+
<details>
|
298
|
+
<summary>Use <code>Symbol</code> or <code>String</code></summary>
|
299
|
+
|
300
|
+
```ruby
|
301
|
+
# <variable name>: <type>, ...
|
302
|
+
|
303
|
+
puts GraphQL::DSL.query(:capsules, status: :String!) {
|
304
|
+
capsules(find: { status: :$status }) {
|
305
|
+
type
|
306
|
+
status
|
307
|
+
landings
|
308
|
+
}
|
309
|
+
}.to_gql
|
310
|
+
```
|
311
|
+
|
312
|
+
```graphql
|
313
|
+
query capsules($status: String!)
|
314
|
+
{
|
315
|
+
capsules(find: {status: $status})
|
316
|
+
{
|
317
|
+
type
|
318
|
+
status
|
319
|
+
landings
|
320
|
+
}
|
321
|
+
}
|
322
|
+
```
|
323
|
+
</details>
|
324
|
+
|
325
|
+
<details>
|
326
|
+
<summary>Use <code>variable</code> refined method</summary>
|
327
|
+
|
328
|
+
```ruby
|
329
|
+
# <variable name>: variable(<type>, [<default value>], [<directives>]), ...
|
330
|
+
|
331
|
+
using GraphQL::DSL # Required to refine `variable` method
|
332
|
+
|
333
|
+
puts GraphQL::DSL.query(:capsules, status: variable(:String!, 'active')) {
|
334
|
+
capsules(find: { status: :$status }) {
|
335
|
+
type
|
336
|
+
status
|
337
|
+
landings
|
338
|
+
}
|
339
|
+
}.to_gql
|
340
|
+
```
|
341
|
+
|
342
|
+
```graphql
|
343
|
+
query capsules($status: String! = "active")
|
344
|
+
{
|
345
|
+
capsules(find: {status: $status})
|
346
|
+
{
|
347
|
+
type
|
348
|
+
status
|
349
|
+
landings
|
350
|
+
}
|
351
|
+
}
|
352
|
+
```
|
353
|
+
</details>
|
354
|
+
|
355
|
+
<details>
|
356
|
+
<summary>Use <code>__var</code> method</summary>
|
357
|
+
|
358
|
+
```ruby
|
359
|
+
# __var <variable name>, <type>, [default: <default value>], [directives: <directives>]
|
360
|
+
|
361
|
+
puts GraphQL::DSL.query(:capsules) {
|
362
|
+
__var :status, :String!, default: "active"
|
363
|
+
|
364
|
+
capsules(find: { status: :$status }) {
|
365
|
+
type
|
366
|
+
status
|
367
|
+
landings
|
368
|
+
}
|
369
|
+
}.to_gql
|
370
|
+
```
|
371
|
+
|
372
|
+
```graphql
|
373
|
+
query capsules($status: String! = "active")
|
374
|
+
{
|
375
|
+
capsules(find: {status: $status})
|
376
|
+
{
|
377
|
+
type
|
378
|
+
status
|
379
|
+
landings
|
380
|
+
}
|
381
|
+
}
|
382
|
+
```
|
383
|
+
</details>
|
384
|
+
|
385
|
+
:bulb: _More information about directives you can find [here](#directives)._
|
386
|
+
|
387
|
+
#### Operation's directives
|
388
|
+
|
389
|
+
Pass operation's directives to third argument of correspond `GraphQL::DSL` method:
|
390
|
+
|
391
|
+
```ruby
|
392
|
+
using GraphQL::DSL # Include refined `variable` and `directive` methods
|
393
|
+
|
394
|
+
puts GraphQL::DSL.query(:capsules, { status: variable(:String!, 'active') }, [ directive(:priority, level: :LOW) ]) {
|
395
|
+
capsules(find: { status: :$status }) {
|
396
|
+
type
|
397
|
+
status
|
398
|
+
landings
|
399
|
+
}
|
400
|
+
}.to_gql
|
401
|
+
```
|
402
|
+
|
403
|
+
<details>
|
404
|
+
<summary>STDOUT</summary>
|
405
|
+
|
406
|
+
```graphql
|
407
|
+
query capsules($status: String! = "active") @priority(level: LOW)
|
408
|
+
{
|
409
|
+
capsules(find: {status: $status})
|
410
|
+
{
|
411
|
+
type
|
412
|
+
status
|
413
|
+
landings
|
414
|
+
}
|
415
|
+
}
|
416
|
+
```
|
417
|
+
</details>
|
418
|
+
|
419
|
+
:bulb: _More information about directives you can find [here](#directives)._
|
420
|
+
|
421
|
+
### Selection Sets
|
422
|
+
|
423
|
+
[Selection Set](https://spec.graphql.org/draft/#sec-Selection-Sets) is a block that contains fields, spread or
|
424
|
+
internal fragments. Operations (`query`, `mutation`, `subscription`), fragment operations, spread and internal fragments
|
425
|
+
must have `Selection Set` for select or update (in case of mutation) data. Even a field can contains `Selection Set`.
|
426
|
+
|
427
|
+
```ruby
|
428
|
+
puts GraphQL::DSL.query { # this is `Selection Set` of query
|
429
|
+
company { # this is `Selection Set` of `company` field
|
430
|
+
name
|
431
|
+
ceo
|
432
|
+
cto
|
433
|
+
}
|
434
|
+
}.to_gql
|
435
|
+
```
|
436
|
+
|
437
|
+
<details>
|
438
|
+
<summary>STDOUT</summary>
|
439
|
+
|
440
|
+
```graphql
|
441
|
+
{
|
442
|
+
company
|
443
|
+
{
|
444
|
+
name
|
445
|
+
ceo
|
446
|
+
cto
|
447
|
+
}
|
448
|
+
}
|
449
|
+
```
|
450
|
+
</details>
|
451
|
+
|
452
|
+
### Fields
|
453
|
+
|
454
|
+
[Selection Set](#selection-set) should contains one or more fields to select or update (in case of mutation) data.
|
455
|
+
|
456
|
+
To create field just declare it name inside of `Selection Set` block:
|
457
|
+
|
458
|
+
```ruby
|
459
|
+
puts GraphQL::DSL.query {
|
460
|
+
company { # this is `company` field
|
461
|
+
name # this is `name` fields declared in `Selection Set` of `company` field
|
462
|
+
}
|
463
|
+
}.to_gql
|
464
|
+
```
|
465
|
+
|
466
|
+
<details>
|
467
|
+
<summary>STDOUT</summary>
|
468
|
+
|
469
|
+
```graphql
|
470
|
+
{
|
471
|
+
company
|
472
|
+
{
|
473
|
+
name
|
474
|
+
}
|
475
|
+
}
|
476
|
+
```
|
477
|
+
</details>
|
478
|
+
|
479
|
+
As you can see above some fields can have `Selection Set` and allow to declare sub-fields.
|
480
|
+
|
481
|
+
In rare cases will be impossible to declare field in such way because its name can conflict with Ruby's keywords and
|
482
|
+
methods. In this case you can declare field use `__field` method:
|
483
|
+
|
484
|
+
```ruby
|
485
|
+
# __field <name>, [__alias: <alias name>], [__directives: <directives>], [<arguments>]
|
486
|
+
|
487
|
+
puts GraphQL::DSL.query {
|
488
|
+
__field(:class) { # `class` is Ruby's keyword
|
489
|
+
__field(:object_id) # `object_id` is `Object` method
|
490
|
+
}
|
491
|
+
}.to_gql
|
492
|
+
```
|
493
|
+
|
494
|
+
<details>
|
495
|
+
<summary>STDOUT</summary>
|
496
|
+
|
497
|
+
```graphql
|
498
|
+
{
|
499
|
+
class
|
500
|
+
{
|
501
|
+
object_id
|
502
|
+
}
|
503
|
+
}
|
504
|
+
```
|
505
|
+
</details>
|
506
|
+
|
507
|
+
To rename field in GraphQL response specify alias in `__alias` argument:
|
508
|
+
|
509
|
+
```ruby
|
510
|
+
puts GraphQL::DSL.query {
|
511
|
+
company {
|
512
|
+
name __alias: :businessName
|
513
|
+
}
|
514
|
+
}.to_gql
|
515
|
+
```
|
516
|
+
|
517
|
+
<details>
|
518
|
+
<summary>STDOUT</summary>
|
519
|
+
|
520
|
+
```graphql
|
521
|
+
{
|
522
|
+
company
|
523
|
+
{
|
524
|
+
businessName: name
|
525
|
+
}
|
526
|
+
}
|
527
|
+
```
|
528
|
+
</details>
|
529
|
+
|
530
|
+
Some field can accept arguments and change their data base on them:
|
531
|
+
|
532
|
+
```ruby
|
533
|
+
puts GraphQL::DSL.query {
|
534
|
+
company {
|
535
|
+
revenue currency: :RUB # convert revenue value to Russian Rubles
|
536
|
+
}
|
537
|
+
}.to_gql
|
538
|
+
```
|
539
|
+
|
540
|
+
<details>
|
541
|
+
<summary>STDOUT</summary>
|
542
|
+
|
543
|
+
```graphql
|
544
|
+
{
|
545
|
+
company
|
546
|
+
{
|
547
|
+
revenue(currency: RUB)
|
548
|
+
}
|
549
|
+
}
|
550
|
+
```
|
551
|
+
</details>
|
552
|
+
|
553
|
+
Any field can have directives. Pass them though `__directives` argument:
|
554
|
+
|
555
|
+
```ruby
|
556
|
+
using GraphQL::DSL # Required to refine `directive` method
|
557
|
+
|
558
|
+
puts GraphQL::DSL.query(:company, additionalInfo: :Boolean) {
|
559
|
+
company {
|
560
|
+
name
|
561
|
+
revenue __directives: [ directive(:include, if: :$additionalInfo) ]
|
562
|
+
}
|
563
|
+
}.to_gql
|
564
|
+
```
|
565
|
+
|
566
|
+
<details>
|
567
|
+
<summary>STDOUT</summary>
|
568
|
+
|
569
|
+
```graphql
|
570
|
+
query company($additionalInfo: Boolean)
|
571
|
+
{
|
572
|
+
company
|
573
|
+
{
|
574
|
+
name
|
575
|
+
revenue @include(if: $additionalInfo)
|
576
|
+
}
|
577
|
+
}
|
578
|
+
```
|
579
|
+
</details>
|
580
|
+
|
581
|
+
### Executable Documents
|
582
|
+
|
583
|
+
Executable Document helps to union several operations or fragments to one request:
|
584
|
+
|
585
|
+
```ruby
|
586
|
+
puts GraphQL::DSL.executable_document {
|
587
|
+
query(:companies) {
|
588
|
+
company {
|
589
|
+
name
|
590
|
+
}
|
591
|
+
}
|
592
|
+
|
593
|
+
query(:rockets) {
|
594
|
+
rockets {
|
595
|
+
name
|
596
|
+
}
|
597
|
+
}
|
598
|
+
}.to_gql
|
599
|
+
```
|
600
|
+
|
601
|
+
<details>
|
602
|
+
<summary>STDOUT</summary>
|
603
|
+
|
604
|
+
```graphql
|
605
|
+
query companies
|
606
|
+
{
|
607
|
+
company
|
608
|
+
{
|
609
|
+
name
|
610
|
+
}
|
611
|
+
}
|
612
|
+
|
613
|
+
query rockets
|
614
|
+
{
|
615
|
+
rockets
|
616
|
+
{
|
617
|
+
name
|
618
|
+
}
|
619
|
+
}
|
620
|
+
```
|
621
|
+
</details>
|
622
|
+
|
623
|
+
### Fragments
|
624
|
+
|
625
|
+
Fragments may contains common repeated selections of fields and can be reused in different operations.
|
626
|
+
Each fragment must have a name, type and optional directives.
|
627
|
+
|
628
|
+
:bulb: _See more about type definitions [here](#types)._
|
629
|
+
|
630
|
+
```ruby
|
631
|
+
# fragment(<fragment name>, <type>, [<directives>])
|
632
|
+
|
633
|
+
fragment(:ship, :Ship) {
|
634
|
+
id
|
635
|
+
name
|
636
|
+
}
|
637
|
+
```
|
638
|
+
|
639
|
+
Fragment spread is using to insert fragment to other operations or fragments. Use `__frgment` command to create fragment
|
640
|
+
spread and insert fragment by its name.
|
641
|
+
|
642
|
+
```ruby
|
643
|
+
# __fragment(<fragment name>, [__directives: <directives>])
|
644
|
+
|
645
|
+
puts GraphQL::DSL.executable_document {
|
646
|
+
query(:cargo_ships) {
|
647
|
+
ships(find: { type: "Cargo" }) {
|
648
|
+
__fragment :ship
|
649
|
+
}
|
650
|
+
}
|
651
|
+
query(:barges) {
|
652
|
+
ships(find: { type: "Barge" }) {
|
653
|
+
__fragment :ship
|
654
|
+
}
|
655
|
+
}
|
656
|
+
|
657
|
+
fragment(:ship, :Ship) {
|
658
|
+
id
|
659
|
+
name
|
660
|
+
}
|
661
|
+
}.to_gql
|
662
|
+
```
|
663
|
+
|
664
|
+
<details>
|
665
|
+
<summary>STDOUT</summary>
|
666
|
+
|
667
|
+
```graphql
|
668
|
+
query cargo_ships
|
669
|
+
{
|
670
|
+
ships(find: {type: "Cargo"})
|
671
|
+
{
|
672
|
+
...ship
|
673
|
+
}
|
674
|
+
}
|
675
|
+
|
676
|
+
query barges
|
677
|
+
{
|
678
|
+
ships(find: {type: "Barge"})
|
679
|
+
{
|
680
|
+
...ship
|
681
|
+
}
|
682
|
+
}
|
683
|
+
|
684
|
+
fragment ship on Ship
|
685
|
+
{
|
686
|
+
id
|
687
|
+
name
|
688
|
+
}
|
689
|
+
```
|
690
|
+
</details>
|
691
|
+
|
692
|
+
### Inline fragments
|
693
|
+
|
694
|
+
Inline fragments helps to define fields from heterogeneous collections
|
695
|
+
(collections which can contains different types of objects). Use `__inline_fragment` to insert inline fragment
|
696
|
+
to operation or fragment.
|
697
|
+
|
698
|
+
:bulb: _See more about type definitions [here](#types)._
|
699
|
+
|
700
|
+
```ruby
|
701
|
+
# __inline_fragment([<type>]) { Selections Set }
|
702
|
+
|
703
|
+
puts GraphQL::DSL.query {
|
704
|
+
messages {
|
705
|
+
__inline_fragment(:AdSection) {
|
706
|
+
title
|
707
|
+
image
|
708
|
+
}
|
709
|
+
|
710
|
+
__inline_fragment(:MessageSection) {
|
711
|
+
title
|
712
|
+
message
|
713
|
+
author
|
714
|
+
}
|
715
|
+
}
|
716
|
+
}.to_gql
|
717
|
+
```
|
718
|
+
|
719
|
+
<details>
|
720
|
+
<summary>STDOUT</summary>
|
721
|
+
|
722
|
+
```graphql
|
723
|
+
{
|
724
|
+
messages
|
725
|
+
{
|
726
|
+
... on AdSection
|
727
|
+
{
|
728
|
+
title
|
729
|
+
image
|
730
|
+
}
|
731
|
+
... on MessageSection
|
732
|
+
{
|
733
|
+
title
|
734
|
+
message
|
735
|
+
author
|
736
|
+
}
|
737
|
+
}
|
738
|
+
}
|
739
|
+
```
|
740
|
+
</details>
|
741
|
+
|
742
|
+
Inline fragments may also be used to apply a directive to a group of fields:
|
743
|
+
|
744
|
+
```ruby
|
745
|
+
using GraphQL::DSL # Required to refine `directive` method
|
746
|
+
|
747
|
+
# __inline_fragment([<type>]) { Selections Set }
|
748
|
+
|
749
|
+
puts GraphQL::DSL.query(:company, additionalInfo: :Boolean) {
|
750
|
+
company {
|
751
|
+
name
|
752
|
+
|
753
|
+
__inline_fragment(nil, __directives: [ directive(:include, if: :$additionalInfo) ]) {
|
754
|
+
revenue
|
755
|
+
valuation
|
756
|
+
}
|
757
|
+
}
|
758
|
+
}.to_gql
|
759
|
+
```
|
760
|
+
|
761
|
+
<details>
|
762
|
+
<summary>STDOUT</summary>
|
763
|
+
|
764
|
+
```graphql
|
765
|
+
query company($additionalInfo: Boolean)
|
766
|
+
{
|
767
|
+
company
|
768
|
+
{
|
769
|
+
name
|
770
|
+
... @include(if: $additionalInfo)
|
771
|
+
{
|
772
|
+
revenue
|
773
|
+
valuation
|
774
|
+
}
|
775
|
+
}
|
776
|
+
}
|
777
|
+
```
|
778
|
+
</details>
|
779
|
+
|
780
|
+
### Directives
|
781
|
+
|
782
|
+
:warning: Non-official SpaceX GraphQL API doesn't support any directives therefore examples below will be fail with error.
|
783
|
+
|
784
|
+
Choose appropriate notation to define directive:
|
785
|
+
|
786
|
+
<details>
|
787
|
+
<summary>Use <code>Symbol</code> or <code>String</code></summary>
|
788
|
+
|
789
|
+
```ruby
|
790
|
+
# (:<name> | "name"), ...
|
791
|
+
|
792
|
+
puts GraphQL::DSL.query(:rockets, {}, [ :lowPriority ]) {
|
793
|
+
rockets {
|
794
|
+
name
|
795
|
+
}
|
796
|
+
}.to_gql
|
797
|
+
```
|
798
|
+
|
799
|
+
```graphql
|
800
|
+
query rockets @lowPriority
|
801
|
+
{
|
802
|
+
rockets
|
803
|
+
{
|
804
|
+
name
|
805
|
+
}
|
806
|
+
}
|
807
|
+
```
|
808
|
+
</details>
|
809
|
+
|
810
|
+
<details>
|
811
|
+
<summary>Use refined <code>directive</code> method</summary>
|
812
|
+
|
813
|
+
```ruby
|
814
|
+
# directive(<directive name>, [<arguments>]), ...
|
815
|
+
|
816
|
+
using GraphQL::DSL # Include refined `directive` method
|
817
|
+
|
818
|
+
puts GraphQL::DSL.query(:rockets, {}, [ directive(:lowPriority) ]) {
|
819
|
+
rockets {
|
820
|
+
name
|
821
|
+
}
|
822
|
+
}.to_gql
|
823
|
+
```
|
824
|
+
|
825
|
+
```graphql
|
826
|
+
query rockets @lowPriority
|
827
|
+
{
|
828
|
+
rockets
|
829
|
+
{
|
830
|
+
name
|
831
|
+
}
|
832
|
+
}
|
833
|
+
```
|
834
|
+
</details>
|
835
|
+
|
836
|
+
### Types
|
837
|
+
|
838
|
+
Types for operation variables and fragments may be declared in several ways in GraphQL DSL.
|
839
|
+
|
840
|
+
#### Named Types
|
841
|
+
|
842
|
+
Named Type can be declared like a symbol or string, for instance: `:Int`, `'Int'`
|
843
|
+
|
844
|
+
#### List Types
|
845
|
+
|
846
|
+
List Type can be declared like a string only, for instance: `'[Int]'`
|
847
|
+
|
848
|
+
#### Not Null Types
|
849
|
+
|
850
|
+
Not Null Type can be declared like a string or symbol, for instance: `:Int!`, `'Int!'`, `'[Int!]!'`
|
851
|
+
|
852
|
+
## 🚲 Example
|
853
|
+
|
854
|
+
[graphql-dsl-example](https://github.com/maxd/graphql-dsl-example) shows how to use GraphQL DSL in Ruby applications.
|
855
|
+
|
856
|
+
## Roadmap
|
857
|
+
|
858
|
+
* [ ] `[Fearure]` Implement `ExecutableDocument#include` to include external operations
|
859
|
+
* [ ] `[Fearure]` Strict validation of any argument
|
860
|
+
* [ ] `[Fearure]` Compact format of GraphQL queries
|
861
|
+
* [ ] `[Improvement]` Overload `__inline_fragment` for signature without type
|
862
|
+
|
863
|
+
## 💻 Development
|
864
|
+
|
865
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `bundle exec rspec` to run the tests.
|
866
|
+
You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
867
|
+
|
868
|
+
To release a new version:
|
869
|
+
|
870
|
+
* update the version number in `lib/graphql/dsl/version.rb` file
|
871
|
+
* run `bundle exec rake readme:update` to update `README.md` file
|
872
|
+
* run `bundle exec rake release` to create a git tag for the version, push git commits and the created tag,
|
873
|
+
and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
874
|
+
|
875
|
+
## 👍 Contributing
|
876
|
+
|
877
|
+
Contributions are what make the open source community such an amazing place to learn, inspire, and create.
|
878
|
+
Any contributions you make are **greatly appreciated**.
|
879
|
+
|
880
|
+
1. Fork the Project
|
881
|
+
2. Create your Feature Branch (`git checkout -b feature/NewFeature`)
|
882
|
+
3. Commit your Changes (`git commit -m 'Add some NewFeature'`)
|
883
|
+
4. Push to the Branch (`git push origin feature/NewFeature`)
|
884
|
+
5. Open a Pull Request
|
885
|
+
|
886
|
+
## 📜 License
|
887
|
+
|
888
|
+
Distributed under the MIT License. See `LICENSE` for more information.
|
889
|
+
|
890
|
+
## 🥰 Code of Conduct
|
891
|
+
|
892
|
+
Everyone interacting in the GraphQL DSL project's codebases and issue trackers is expected to
|
893
|
+
follow the [code of conduct](https://github.com/maxd/graphql-dsl/blob/master/CODE_OF_CONDUCT.md).
|
894
|
+
|
895
|
+
## 📚 Resources
|
896
|
+
|
897
|
+
* [Introduction to GraphQL](https://graphql.org/learn/)
|
898
|
+
* [GraphQL Specification](https://spec.graphql.org/)
|
899
|
+
* Inspired by [gqli.rb](https://github.com/contentful-labs/gqli.rb) gem
|