graphql_rails 0.6.0 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.hound.yml +1 -0
- data/.rubocop.yml +3 -3
- data/.ruby-version +1 -1
- data/.travis.yml +2 -2
- data/CHANGELOG.md +38 -0
- data/Gemfile +3 -2
- data/Gemfile.lock +181 -71
- data/docs/README.md +48 -9
- data/docs/_sidebar.md +5 -0
- data/docs/components/controller.md +294 -8
- data/docs/components/decorator.md +69 -0
- data/docs/components/model.md +349 -11
- data/docs/components/routes.md +43 -1
- data/docs/getting_started/quick_start.md +9 -2
- data/docs/index.html +1 -1
- data/docs/logging_and_monitoring/logging_and_monitoring.md +35 -0
- data/docs/other_tools/query_runner.md +49 -0
- data/docs/other_tools/schema_dump.md +29 -0
- data/docs/testing/testing.md +3 -1
- data/graphql_rails.gemspec +5 -4
- data/lib/generators/graphql_rails/install_generator.rb +50 -0
- data/lib/generators/graphql_rails/templates/example_users_controller.erb +19 -0
- data/lib/generators/graphql_rails/templates/graphql_application_controller.erb +8 -0
- data/lib/generators/graphql_rails/templates/graphql_controller.erb +20 -0
- data/lib/generators/graphql_rails/templates/graphql_router.erb +19 -0
- data/lib/generators/graphql_rails/templates/graphql_router_spec.erb +21 -0
- data/lib/graphql_rails.rb +7 -1
- data/lib/graphql_rails/attributes.rb +13 -0
- data/lib/graphql_rails/{attribute → attributes}/attributable.rb +25 -20
- data/lib/graphql_rails/attributes/attribute.rb +94 -0
- data/lib/graphql_rails/{attribute → attributes}/attribute_name_parser.rb +2 -2
- data/lib/graphql_rails/attributes/input_attribute.rb +65 -0
- data/lib/graphql_rails/attributes/input_type_parser.rb +62 -0
- data/lib/graphql_rails/attributes/type_name_info.rb +38 -0
- data/lib/graphql_rails/attributes/type_parseable.rb +128 -0
- data/lib/graphql_rails/attributes/type_parser.rb +121 -0
- data/lib/graphql_rails/concerns/service.rb +19 -0
- data/lib/graphql_rails/controller.rb +42 -21
- data/lib/graphql_rails/controller/action.rb +12 -67
- data/lib/graphql_rails/controller/action_configuration.rb +71 -31
- data/lib/graphql_rails/controller/build_controller_action_resolver.rb +52 -0
- data/lib/graphql_rails/controller/build_controller_action_resolver/controller_action_resolver.rb +28 -0
- data/lib/graphql_rails/controller/configuration.rb +56 -4
- data/lib/graphql_rails/controller/log_controller_action.rb +71 -0
- data/lib/graphql_rails/controller/request.rb +29 -8
- data/lib/graphql_rails/controller/request/format_errors.rb +58 -0
- data/lib/graphql_rails/decorator.rb +41 -0
- data/lib/graphql_rails/decorator/relation_decorator.rb +75 -0
- data/lib/graphql_rails/errors/custom_execution_error.rb +22 -0
- data/lib/graphql_rails/errors/execution_error.rb +8 -7
- data/lib/graphql_rails/errors/system_error.rb +14 -0
- data/lib/graphql_rails/errors/validation_error.rb +1 -5
- data/lib/graphql_rails/input_configurable.rb +47 -0
- data/lib/graphql_rails/integrations.rb +19 -0
- data/lib/graphql_rails/integrations/lograge.rb +39 -0
- data/lib/graphql_rails/integrations/sentry.rb +34 -0
- data/lib/graphql_rails/model.rb +26 -4
- data/lib/graphql_rails/model/add_fields_to_graphql_type.rb +45 -0
- data/lib/graphql_rails/model/build_connection_type.rb +52 -0
- data/lib/graphql_rails/model/{configuration → build_connection_type}/count_items.rb +5 -5
- data/lib/graphql_rails/model/build_enum_type.rb +68 -0
- data/lib/graphql_rails/model/{graphql_input_type_builder.rb → build_graphql_input_type.rb} +10 -2
- data/lib/graphql_rails/model/call_graphql_model_method.rb +72 -0
- data/lib/graphql_rails/model/configurable.rb +6 -2
- data/lib/graphql_rails/model/configuration.rb +34 -19
- data/lib/graphql_rails/model/find_or_build_graphql_type.rb +64 -0
- data/lib/graphql_rails/model/find_or_build_graphql_type_class.rb +46 -0
- data/lib/graphql_rails/model/input.rb +25 -11
- data/lib/graphql_rails/query_runner.rb +68 -0
- data/lib/graphql_rails/railtie.rb +10 -0
- data/lib/graphql_rails/router.rb +40 -13
- data/lib/graphql_rails/router/resource_routes_builder.rb +19 -11
- data/lib/graphql_rails/router/route.rb +21 -6
- data/lib/graphql_rails/router/schema_builder.rb +36 -11
- data/lib/graphql_rails/rspec_controller_helpers.rb +6 -4
- data/lib/graphql_rails/tasks/dump_graphql_schema.rb +57 -0
- data/lib/graphql_rails/tasks/schema.rake +14 -0
- data/lib/graphql_rails/version.rb +1 -1
- metadata +78 -26
- data/README.md +0 -194
- data/lib/graphql_rails/attribute.rb +0 -28
- data/lib/graphql_rails/attribute/type_parser.rb +0 -115
- data/lib/graphql_rails/controller/controller_function.rb +0 -50
- data/lib/graphql_rails/controller/format_results.rb +0 -36
- data/lib/graphql_rails/model/graphql_type_builder.rb +0 -33
- data/lib/graphql_rails/model/input_attribute.rb +0 -47
data/docs/_sidebar.md
CHANGED
@@ -6,4 +6,9 @@
|
|
6
6
|
* [Routes](components/routes)
|
7
7
|
* [Model](components/model)
|
8
8
|
* [Controller](components/controller)
|
9
|
+
* [Decorator](components/decorator)
|
10
|
+
* [Logging and monitoring](logging_and_monitoring/logging_and_monitoring)
|
9
11
|
* [Testing](testing/testing)
|
12
|
+
* Other tools
|
13
|
+
* [Schema Snapshot](other_tools/schema_dump)
|
14
|
+
* [Query Runner](other_tools/query_runner)
|
@@ -66,22 +66,95 @@ class UsersController < GraphqlRails::Controller
|
|
66
66
|
end
|
67
67
|
```
|
68
68
|
|
69
|
-
### *
|
69
|
+
### *permit_input*
|
70
70
|
|
71
|
-
|
71
|
+
Allows to permit single input field. It allows to set additional options for each field.
|
72
|
+
|
73
|
+
#### *type*
|
74
|
+
|
75
|
+
Specifies input type:
|
76
|
+
|
77
|
+
```ruby
|
78
|
+
class OrderController < GraphqlRails::Controller
|
79
|
+
action(:create)
|
80
|
+
.permit_input(:price, type: :integer!)
|
81
|
+
# Same as `.permit(amount: :integer!)`
|
82
|
+
end
|
83
|
+
```
|
84
|
+
|
85
|
+
#### required type
|
86
|
+
|
87
|
+
There are few ways how to mark field as required.
|
88
|
+
|
89
|
+
1. Adding exclamation mark at the end of type name:
|
90
|
+
|
91
|
+
```ruby
|
92
|
+
class UsersController < GraphqlRails::Controller
|
93
|
+
action(:create).permit_input(:some_field, type: :int!)
|
94
|
+
end
|
95
|
+
```
|
96
|
+
|
97
|
+
2. Adding exclamation mark at the end of name
|
72
98
|
|
73
99
|
```ruby
|
74
100
|
class UsersController < GraphqlRails::Controller
|
75
|
-
action(:
|
101
|
+
action(:create).permit_input(:some_field!)
|
102
|
+
end
|
103
|
+
```
|
76
104
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
105
|
+
3. Adding `required: true` options
|
106
|
+
|
107
|
+
```ruby
|
108
|
+
class UsersController < GraphqlRails::Controller
|
109
|
+
action(:create).permit_input(:some_field, type: :bool, required: true)
|
110
|
+
end
|
111
|
+
```
|
112
|
+
|
113
|
+
#### *description*
|
114
|
+
|
115
|
+
You can describe each input by adding `description` keyword argument:
|
116
|
+
|
117
|
+
```ruby
|
118
|
+
class OrderController < GraphqlRails::Controller
|
119
|
+
action(:create)
|
120
|
+
.permit_input(:price, type: :integer!, description: 'Price in Euro cents')
|
121
|
+
end
|
122
|
+
```
|
123
|
+
|
124
|
+
#### *subtype*
|
125
|
+
|
126
|
+
`subtype` allows to specify which named input should be used. Here is an example:
|
127
|
+
|
128
|
+
Let's say you have user with two input types
|
129
|
+
|
130
|
+
```ruby
|
131
|
+
class User
|
132
|
+
graphql.input do |c|
|
133
|
+
c.attribute :full_name
|
134
|
+
c.attribute :email
|
135
|
+
end
|
136
|
+
|
137
|
+
graphql.input(:change_password) do |c|
|
138
|
+
c.attribute :password
|
139
|
+
c.attribute :password_confirmation
|
81
140
|
end
|
82
141
|
end
|
83
142
|
```
|
84
143
|
|
144
|
+
If you do not specify `subtype` then default (without name) input will be used. You need to specify subtype if you want to use non-default input:
|
145
|
+
|
146
|
+
```ruby
|
147
|
+
class OrderController < GraphqlRails::Controller
|
148
|
+
# this is the input with email and full_name:
|
149
|
+
action(:create)
|
150
|
+
.permit_input(:input, type: 'User!')
|
151
|
+
|
152
|
+
# this is the input with password and password_confirmation:
|
153
|
+
action(:update_password)
|
154
|
+
.permit_input(:input, type: 'User!', subtype: :change_password)
|
155
|
+
end
|
156
|
+
```
|
157
|
+
|
85
158
|
### *paginated*
|
86
159
|
|
87
160
|
You can mark collection action as `paginated`. In this case controller will return relay connection type and it will be possible to return only partial results. No need to do anything on controller side (you should always return full list of items)
|
@@ -96,6 +169,8 @@ class UsersController < GraphqlRails::Controller
|
|
96
169
|
end
|
97
170
|
```
|
98
171
|
|
172
|
+
Also check ['decorating controller responses'](components/decorator) for more details about working with active record and decorators.
|
173
|
+
|
99
174
|
#### *max_page_size*
|
100
175
|
|
101
176
|
Allows to specify max items count per request
|
@@ -110,9 +185,29 @@ class UsersController < GraphqlRails::Controller
|
|
110
185
|
end
|
111
186
|
```
|
112
187
|
|
188
|
+
### *model*
|
189
|
+
|
190
|
+
If you want to define model dynamically, you can use combination of `model` and `returns_list` or `returns_single`. This is especially handy when model is used with `action_default`:
|
191
|
+
|
192
|
+
```ruby
|
193
|
+
class OrdersController < GraphqlRails::Controller
|
194
|
+
model('Order')
|
195
|
+
action(:show).returns_single # returns `Order!`
|
196
|
+
action(:index).returns_list # returns `[Order!]!`
|
197
|
+
|
198
|
+
def show
|
199
|
+
Order.first
|
200
|
+
end
|
201
|
+
|
202
|
+
def index
|
203
|
+
Order.all
|
204
|
+
end
|
205
|
+
end
|
206
|
+
```
|
207
|
+
|
113
208
|
### *returns*
|
114
209
|
|
115
|
-
|
210
|
+
You must specify what each action will return. This is done with `returns` method:
|
116
211
|
|
117
212
|
```ruby
|
118
213
|
class UsersController < GraphqlRails::Controller
|
@@ -124,6 +219,48 @@ class UsersController < GraphqlRails::Controller
|
|
124
219
|
end
|
125
220
|
```
|
126
221
|
|
222
|
+
You can also return raw graphql-ruby types:
|
223
|
+
|
224
|
+
```ruby
|
225
|
+
# raw graphql-ruby type:
|
226
|
+
class OrderType < GraphQL::Schema::Object
|
227
|
+
graphql_name 'Order'
|
228
|
+
field :id, ID
|
229
|
+
end
|
230
|
+
|
231
|
+
class UsersController < GraphqlRails::Controller
|
232
|
+
action(:last_order).permit(:id).returns(OrderType)
|
233
|
+
end
|
234
|
+
```
|
235
|
+
|
236
|
+
Check [graphql-ruby documentation](https://graphql-ruby.org) for more details about graphql-ruby types.
|
237
|
+
|
238
|
+
### *returns_list*
|
239
|
+
|
240
|
+
When you have defined `model` dynamically, you can use `returns_list` to indicate that action must return list without specifying model type for each action. By default list and inner types are required but you can change that with `required_list: false` and `required_inner: false`
|
241
|
+
|
242
|
+
```ruby
|
243
|
+
class OrdersController < GraphqlRails::Controller
|
244
|
+
model('Order')
|
245
|
+
|
246
|
+
action(:index).returns_list(required_list: false, required_inner: false) # returns `[Order]`
|
247
|
+
action(:search).permit(:filter).returns_list # returns `[Order!]!`
|
248
|
+
end
|
249
|
+
```
|
250
|
+
|
251
|
+
### *returns_single*
|
252
|
+
|
253
|
+
When you have defined `model` dynamically, you can use `returns_single` to indicate that action must return single item without specifying model type for each action. By default return type is required, but you can change that by providing `required: false` flag:
|
254
|
+
|
255
|
+
```ruby
|
256
|
+
class OrdersController < GraphqlRails::Controller
|
257
|
+
model('Order')
|
258
|
+
|
259
|
+
action(:show).returns_single(required: false) # returns `Order`
|
260
|
+
action(:update).permit(title: :string!).returns_single # returns `Order!`
|
261
|
+
end
|
262
|
+
```
|
263
|
+
|
127
264
|
### *describe*
|
128
265
|
|
129
266
|
If you want to improve graphql documentation, you can add description for each action. To do so, use `describe` method:
|
@@ -153,6 +290,57 @@ class UsersController < GraphqlRails::Controller
|
|
153
290
|
end
|
154
291
|
```
|
155
292
|
|
293
|
+
### configuring action with a block
|
294
|
+
|
295
|
+
If you do not like chainable methods, you can use "block" style action configuration:
|
296
|
+
|
297
|
+
```ruby
|
298
|
+
class UsersController < GraphqlRails::Controller
|
299
|
+
action(:index) do |action|
|
300
|
+
action.paginated
|
301
|
+
action.permit(limit: :int!)
|
302
|
+
action.returns '[User!]!'
|
303
|
+
end
|
304
|
+
|
305
|
+
def create
|
306
|
+
User.create(params)
|
307
|
+
end
|
308
|
+
end
|
309
|
+
```
|
310
|
+
|
311
|
+
## *model*
|
312
|
+
|
313
|
+
`model` is just a shorter version of `action_default.model`. See `action.model` and `action_default` for more information:
|
314
|
+
|
315
|
+
```ruby
|
316
|
+
class OrdersController < GraphqlRails::Controller
|
317
|
+
model('Order')
|
318
|
+
action(:show).returns_single # returns `Order!`
|
319
|
+
action(:index).returns_list # returns `[Order!]!`
|
320
|
+
|
321
|
+
def show
|
322
|
+
Order.first
|
323
|
+
end
|
324
|
+
|
325
|
+
def index
|
326
|
+
Order.all
|
327
|
+
end
|
328
|
+
end
|
329
|
+
```
|
330
|
+
|
331
|
+
## *action_default*
|
332
|
+
|
333
|
+
Sometimes you want to have some shared attributes for all your actions. In order to make this possible you need to use `action_default`. It acts identical to `action` and is "inherited" by all actions defined after `action_default` part:
|
334
|
+
|
335
|
+
```ruby
|
336
|
+
class UsersController < GraphqlRails::Controller
|
337
|
+
action_default.permit(token: :string!)
|
338
|
+
|
339
|
+
action(:update).returns('User!') # action(:update) has `permit(token: :string!)
|
340
|
+
action(:create).returns('User') # action(:create) has `permit(token: :string!)
|
341
|
+
end
|
342
|
+
```
|
343
|
+
|
156
344
|
## *before_action*
|
157
345
|
|
158
346
|
You can add `before_action` to run some filters before calling your controller action. Here is an example:
|
@@ -263,3 +451,101 @@ class UsersController < GraphqlRails::Controller
|
|
263
451
|
end
|
264
452
|
end
|
265
453
|
```
|
454
|
+
|
455
|
+
## decorating objects
|
456
|
+
|
457
|
+
See ['Decorating controller responses'](components/decorator) for various options how you can decorate paginated responses
|
458
|
+
|
459
|
+
## Rendering errors
|
460
|
+
|
461
|
+
### Rendering strings as errors
|
462
|
+
|
463
|
+
The simplest way to render an error is to provide a list of error messages, like this:
|
464
|
+
|
465
|
+
```ruby
|
466
|
+
class UsersController < GraphqlRails::Controller
|
467
|
+
action(:update).permit(:id, input: 'UserInput!').returns('User!')
|
468
|
+
|
469
|
+
def update
|
470
|
+
user = User.find(params[:id])
|
471
|
+
|
472
|
+
if user.update(params[:input])
|
473
|
+
user
|
474
|
+
else
|
475
|
+
render(errors: ['Something went wrong'])
|
476
|
+
end
|
477
|
+
end
|
478
|
+
end
|
479
|
+
```
|
480
|
+
|
481
|
+
### Rendering validation errors
|
482
|
+
|
483
|
+
GraphqlRails controller has `#render` method which you can use to render errors:
|
484
|
+
|
485
|
+
```ruby
|
486
|
+
class UsersController < GraphqlRails::Controller
|
487
|
+
action(:update).permit(:id, input: 'UserInput!').returns('User!')
|
488
|
+
|
489
|
+
def update
|
490
|
+
user = User.find(params[:id])
|
491
|
+
|
492
|
+
if user.update(params[:input])
|
493
|
+
user
|
494
|
+
else
|
495
|
+
render(errors: user.errors)
|
496
|
+
end
|
497
|
+
end
|
498
|
+
end
|
499
|
+
```
|
500
|
+
|
501
|
+
### Rendering errors with custom data
|
502
|
+
|
503
|
+
When you want to return errors with custom data, you can provide hash like this:
|
504
|
+
|
505
|
+
```ruby
|
506
|
+
class UsersController < GraphqlRails::Controller
|
507
|
+
action(:update).permit(:id, input: 'UserInput!').returns('User!')
|
508
|
+
|
509
|
+
def update
|
510
|
+
user = User.find(params[:id])
|
511
|
+
|
512
|
+
if user.update(params[:input])
|
513
|
+
user
|
514
|
+
else
|
515
|
+
render(
|
516
|
+
errors: [
|
517
|
+
{ message: 'Something went wrong', code: 500, type: 'fatal' },
|
518
|
+
{ message: 'Something went wrong', custom_param: true, ... },
|
519
|
+
]
|
520
|
+
)
|
521
|
+
end
|
522
|
+
end
|
523
|
+
end
|
524
|
+
```
|
525
|
+
|
526
|
+
### Raising custom error classes
|
527
|
+
|
528
|
+
If you want to have customized error classes you need to create errors which inherit from `GraphqlRails::ExecutionError`
|
529
|
+
|
530
|
+
```ruby
|
531
|
+
class MyCustomError < GraphqlRails::ExecutionError
|
532
|
+
def to_h
|
533
|
+
# this part will be rendered in graphql
|
534
|
+
{ something_custom: 'yes' }
|
535
|
+
end
|
536
|
+
end
|
537
|
+
|
538
|
+
class UsersController < GraphqlRails::Controller
|
539
|
+
action(:update).permit(:id, input: 'UserInput!').returns('User!')
|
540
|
+
|
541
|
+
def update
|
542
|
+
user = User.find(params[:id])
|
543
|
+
|
544
|
+
if user.update(params[:input])
|
545
|
+
user
|
546
|
+
else
|
547
|
+
raise MyCustomError, 'ups!'
|
548
|
+
end
|
549
|
+
end
|
550
|
+
end
|
551
|
+
```
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# Decorator
|
2
|
+
|
3
|
+
Decorator is mostly used whit paginated results, because it can wrap ActiveRecord relations in a "pagination-friendly" way
|
4
|
+
|
5
|
+
## Passing extra options to decorator
|
6
|
+
|
7
|
+
Let's say you want to decorate `comment`, but you also need `user` in order to print some details. Here is decorator for such comment:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
class CommentDecorator < SimpleDelegator
|
11
|
+
include GraphqlRails::Decorator
|
12
|
+
|
13
|
+
def initialize(comment, current_user)
|
14
|
+
@comment = comment
|
15
|
+
@current_user = user
|
16
|
+
end
|
17
|
+
|
18
|
+
def author_name
|
19
|
+
if @current_user.can_see_author_name?(@comment)
|
20
|
+
@comment.author_name
|
21
|
+
else
|
22
|
+
'secret author'
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
```
|
27
|
+
|
28
|
+
In order to decorate object with exra arguments, simply pass them to `.decorate` method. Like this:
|
29
|
+
|
30
|
+
```ruby
|
31
|
+
CommentDecorator.decorate(comment, current_user)
|
32
|
+
```
|
33
|
+
|
34
|
+
The only requirement is that first object should be the object which you are decorating. Other arguments are treated as extra data and they are not modified
|
35
|
+
|
36
|
+
## Decorating controller responses
|
37
|
+
|
38
|
+
If you want to decorate your controller response you can use `GraphqlRails::Decorator` module. It can decorate simple objects and ActiveRecord::Relation objects. This is very handy when you need to decorated paginated actions:
|
39
|
+
|
40
|
+
```ruby
|
41
|
+
class User < ActiveRecord::Base
|
42
|
+
# it's not GraphqlRails::Model !
|
43
|
+
end
|
44
|
+
|
45
|
+
class UserDecorator < SimpleDelegator
|
46
|
+
include GraphqlRails::Model
|
47
|
+
include GraphqlRails::Decorator
|
48
|
+
|
49
|
+
graphql_rails do
|
50
|
+
# some setup, attributes, etc...
|
51
|
+
end
|
52
|
+
|
53
|
+
def initialize(user); end
|
54
|
+
end
|
55
|
+
|
56
|
+
class UsersController < GraphqlRails::Controller
|
57
|
+
action(:index).paginated.returns('[UserDecorator!]!')
|
58
|
+
|
59
|
+
def index
|
60
|
+
users = User.where(active: true)
|
61
|
+
UserDecorator.decorate(users)
|
62
|
+
end
|
63
|
+
|
64
|
+
def create
|
65
|
+
user = User.create(params)
|
66
|
+
UserDecorator.decorate(user)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
```
|
data/docs/components/model.md
CHANGED
@@ -15,15 +15,15 @@ class User # works with any class including ActiveRecord
|
|
15
15
|
end
|
16
16
|
```
|
17
17
|
|
18
|
-
##
|
18
|
+
## graphql
|
19
19
|
|
20
|
-
This method must be called inside your model body. `
|
20
|
+
This method must be called inside your model body. `graphql` is used for making your model convertible to graphql type.
|
21
21
|
|
22
|
-
|
22
|
+
## attribute
|
23
23
|
|
24
24
|
Most commonly you will use `attribute` to make your model methods and attributes visible via graphql endpoint.
|
25
25
|
|
26
|
-
|
26
|
+
### attribute.type
|
27
27
|
|
28
28
|
Some types can be determined by attribute name, so you can skip this attribute:
|
29
29
|
|
@@ -54,11 +54,35 @@ class User
|
|
54
54
|
end
|
55
55
|
```
|
56
56
|
|
57
|
-
####
|
57
|
+
#### attribute.type: using graphql-ruby objects
|
58
58
|
|
59
|
-
|
59
|
+
You can also use raw graphql-ruby objects as attribute types. Here is an example:
|
60
60
|
|
61
|
+
```ruby
|
62
|
+
# raw graphql-ruby type:
|
63
|
+
class AddressType < GraphQL::Schema::Object
|
64
|
+
graphql_name 'Address'
|
65
|
+
|
66
|
+
field :city, String, null: false
|
67
|
+
field :street_name, String, null: false
|
68
|
+
field :street_number, Integer
|
69
|
+
end
|
70
|
+
|
71
|
+
# GraphqlRails model:
|
72
|
+
class User
|
73
|
+
include GraphqlRails::Model
|
74
|
+
|
75
|
+
graphql.attribute :address, type: AddressType, required: true
|
76
|
+
end
|
61
77
|
```
|
78
|
+
|
79
|
+
Check [graphql-ruby documentation](https://graphql-ruby.org) for more details about graphql-ruby types.
|
80
|
+
|
81
|
+
### attribute.property
|
82
|
+
|
83
|
+
By default graphql attribute names are expected to be same as model methods/attributes, but if you want to use different name on grapqhl side, you can use `property` option:
|
84
|
+
|
85
|
+
```ruby
|
62
86
|
class User
|
63
87
|
include GraphqlRails::Model
|
64
88
|
|
@@ -72,11 +96,11 @@ class User
|
|
72
96
|
end
|
73
97
|
```
|
74
98
|
|
75
|
-
|
99
|
+
### attribute.description
|
76
100
|
|
77
101
|
You can also describe each attribute and make graphql documentation even more readable. To do so, add `description` option:
|
78
102
|
|
79
|
-
```
|
103
|
+
```ruby
|
80
104
|
class User
|
81
105
|
include GraphqlRails::Model
|
82
106
|
|
@@ -86,7 +110,173 @@ class User
|
|
86
110
|
end
|
87
111
|
```
|
88
112
|
|
89
|
-
###
|
113
|
+
### attribute.options
|
114
|
+
|
115
|
+
Allows passing options to attribute definition. Available options:
|
116
|
+
|
117
|
+
* `attribute_name_format` - if `:original` value is passed, it will not camelCase attribute name.
|
118
|
+
|
119
|
+
```ruby
|
120
|
+
class User
|
121
|
+
include GraphqlRails::Model
|
122
|
+
|
123
|
+
graphql do |c|
|
124
|
+
c.attribute :first_name # will be accessible as firstName from client side
|
125
|
+
c.attribute :first_name, options: { attribute_name_format: :original } # will be accessible as first_name from client side
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
### attribute.permit
|
130
|
+
|
131
|
+
To define attributes which are accepted by each model method, you need to call `permit` method, like this:
|
132
|
+
|
133
|
+
```ruby
|
134
|
+
class User
|
135
|
+
include GraphqlRails::Model
|
136
|
+
|
137
|
+
graphql do |c|
|
138
|
+
c.attribute(:avatar_url).permit(size: :int!)
|
139
|
+
end
|
140
|
+
|
141
|
+
def avatar_url(size:)
|
142
|
+
# some code here
|
143
|
+
end
|
144
|
+
end
|
145
|
+
```
|
146
|
+
|
147
|
+
### attribute.permit_input
|
148
|
+
|
149
|
+
Allows to permit single input field. It allows to set additional options for each field.
|
150
|
+
|
151
|
+
#### attribute.permit_input.type
|
152
|
+
|
153
|
+
#### attribute.permit_input: required type
|
154
|
+
|
155
|
+
There are few ways how to mark field as required.
|
156
|
+
|
157
|
+
1. Adding exclamation mark at the end of type name:
|
158
|
+
|
159
|
+
```ruby
|
160
|
+
class User
|
161
|
+
include GraphqlRails::Model
|
162
|
+
|
163
|
+
graphql.attribute(:avatar_url).permit_input(:size, type: :int!)
|
164
|
+
end
|
165
|
+
```
|
166
|
+
|
167
|
+
2. Adding `required: true` options
|
168
|
+
|
169
|
+
```ruby
|
170
|
+
class User
|
171
|
+
include GraphqlRails::Model
|
172
|
+
|
173
|
+
graphql.attribute(:avatar_url).permit_input(:size, type: :int, required: true)
|
174
|
+
end
|
175
|
+
```
|
176
|
+
|
177
|
+
#### attribute.permit_input.description
|
178
|
+
|
179
|
+
You can describe each input by adding `description` keyword argument:
|
180
|
+
|
181
|
+
```ruby
|
182
|
+
class User
|
183
|
+
include GraphqlRails::Model
|
184
|
+
|
185
|
+
graphql.attribute(:avatar_url).permit_input(:size, description: 'max size of avatar')
|
186
|
+
end
|
187
|
+
```
|
188
|
+
|
189
|
+
#### *subtype*
|
190
|
+
|
191
|
+
`subtype` allows to specify which named input should be used. Here is an example:
|
192
|
+
|
193
|
+
```ruby
|
194
|
+
class Image
|
195
|
+
graphql.input(:size_options) do |c|
|
196
|
+
c.attribute :width
|
197
|
+
c.attribute :height
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
class User
|
202
|
+
graphql.attribute(:avatar_url).permit_input(:size, type: Image, subtype: :size_options)
|
203
|
+
end
|
204
|
+
```
|
205
|
+
|
206
|
+
### attribute.paginated
|
207
|
+
|
208
|
+
You can mark collection method as `paginated`. In this case method will return relay connection type and it will be possible to return only partial results. No need to do anything on method side (you should always return full list of items)
|
209
|
+
|
210
|
+
```ruby
|
211
|
+
class User
|
212
|
+
include GraphqlRails::Model
|
213
|
+
|
214
|
+
graphql.attribute :items, type: '[Item]', paginatted: true
|
215
|
+
|
216
|
+
def items
|
217
|
+
Item.all
|
218
|
+
end
|
219
|
+
end
|
220
|
+
```
|
221
|
+
|
222
|
+
### attribute.required
|
223
|
+
|
224
|
+
You can mark attribute as required using `required` method:
|
225
|
+
|
226
|
+
```ruby
|
227
|
+
class User
|
228
|
+
include GraphqlRails::Model
|
229
|
+
|
230
|
+
graphql.attribute(:item).type('Item').required
|
231
|
+
end
|
232
|
+
```
|
233
|
+
|
234
|
+
### attribute.optional
|
235
|
+
|
236
|
+
You can mark attribute as optional using `optional` method:
|
237
|
+
|
238
|
+
```ruby
|
239
|
+
class User
|
240
|
+
include GraphqlRails::Model
|
241
|
+
|
242
|
+
graphql.attribute(:item).type('Item').optional
|
243
|
+
end
|
244
|
+
```
|
245
|
+
|
246
|
+
### "attribute" configuration with chainable methods
|
247
|
+
|
248
|
+
If your attribute definition is complex, you can define attribute in more eye-friendly chainable way with:
|
249
|
+
|
250
|
+
```ruby
|
251
|
+
class User
|
252
|
+
include GraphqlRails::Model
|
253
|
+
|
254
|
+
graphql do |c|
|
255
|
+
c.attribute(:shop_id)
|
256
|
+
.type('ID!')
|
257
|
+
.description('references to shop')
|
258
|
+
end
|
259
|
+
end
|
260
|
+
```
|
261
|
+
|
262
|
+
### "attribute" configuration with a block
|
263
|
+
|
264
|
+
You can also use block in order to specify attribute configuration:
|
265
|
+
|
266
|
+
```ruby
|
267
|
+
class User
|
268
|
+
include GraphqlRails::Model
|
269
|
+
|
270
|
+
graphql do |c|
|
271
|
+
c.attribute(:shop_id) do |attr|
|
272
|
+
attr.type 'ID!'
|
273
|
+
attr.description 'references to shop'
|
274
|
+
end
|
275
|
+
end
|
276
|
+
end
|
277
|
+
```
|
278
|
+
|
279
|
+
## name
|
90
280
|
|
91
281
|
By default grapqhl type name will be same as model name, but you can change it via `name` method
|
92
282
|
|
@@ -100,7 +290,7 @@ class User
|
|
100
290
|
end
|
101
291
|
```
|
102
292
|
|
103
|
-
|
293
|
+
## description
|
104
294
|
|
105
295
|
To improve grapqhl documentation, you can description for your graphql type:
|
106
296
|
|
@@ -114,10 +304,158 @@ class User
|
|
114
304
|
end
|
115
305
|
```
|
116
306
|
|
117
|
-
|
307
|
+
## graphql_type
|
118
308
|
|
119
309
|
Sometimes it's handy to get raw graphql type. To do so you can call:
|
120
310
|
|
121
311
|
```ruby
|
122
312
|
YourModel.graphql.grapqhl_type
|
123
313
|
```
|
314
|
+
|
315
|
+
## input
|
316
|
+
|
317
|
+
You can define input types:
|
318
|
+
|
319
|
+
```ruby
|
320
|
+
class User
|
321
|
+
include GraphqlRails::Model
|
322
|
+
|
323
|
+
graphql.input do |c|
|
324
|
+
c.attribute :name
|
325
|
+
end
|
326
|
+
end
|
327
|
+
```
|
328
|
+
|
329
|
+
Also you can have multiple input types:
|
330
|
+
|
331
|
+
```ruby
|
332
|
+
class User
|
333
|
+
include GraphqlRails::Model
|
334
|
+
|
335
|
+
graphql.input(:create) do |c|
|
336
|
+
c.attribute :name
|
337
|
+
end
|
338
|
+
|
339
|
+
graphql.input(:update) do |c|
|
340
|
+
c.attribute :id
|
341
|
+
c.attribute :name
|
342
|
+
end
|
343
|
+
end
|
344
|
+
```
|
345
|
+
|
346
|
+
### input attribute
|
347
|
+
|
348
|
+
Most commonly you will use `attribute` to define what kind of values your endpoint accepts
|
349
|
+
|
350
|
+
#### input type
|
351
|
+
|
352
|
+
You can specify your input attribute type. If type is not provided then type is set to `:string`.
|
353
|
+
|
354
|
+
```ruby
|
355
|
+
class User
|
356
|
+
include GraphqlRails::Model
|
357
|
+
|
358
|
+
graphql.input do |c|
|
359
|
+
c.attribute :friends_count, type: :integer!
|
360
|
+
end
|
361
|
+
end
|
362
|
+
```
|
363
|
+
|
364
|
+
#### required type
|
365
|
+
|
366
|
+
There are few ways how to mark field as required.
|
367
|
+
|
368
|
+
1. Adding exclamation mark at the end of type name:
|
369
|
+
|
370
|
+
```ruby
|
371
|
+
class User
|
372
|
+
include GraphqlRails::Model
|
373
|
+
|
374
|
+
graphql.input do |c|
|
375
|
+
c.attribute :friends_count, type: :integer!
|
376
|
+
end
|
377
|
+
end
|
378
|
+
```
|
379
|
+
|
380
|
+
2. Adding `required: true` value
|
381
|
+
|
382
|
+
```ruby
|
383
|
+
class User
|
384
|
+
include GraphqlRails::Model
|
385
|
+
|
386
|
+
graphql.input do |c|
|
387
|
+
c.attribute :friends_count, type: :integer, required: true
|
388
|
+
end
|
389
|
+
end
|
390
|
+
```
|
391
|
+
|
392
|
+
#### input enum type
|
393
|
+
|
394
|
+
You can specify your input attribute as enum:
|
395
|
+
|
396
|
+
```ruby
|
397
|
+
class User
|
398
|
+
include GraphqlRails::Model
|
399
|
+
|
400
|
+
graphql.input do |c|
|
401
|
+
c.attribute :favorite_fruit, enum: %i[apple orange]
|
402
|
+
end
|
403
|
+
end
|
404
|
+
```
|
405
|
+
|
406
|
+
By default enum type is not required. To make it required add `required: true`:
|
407
|
+
|
408
|
+
```ruby
|
409
|
+
class User
|
410
|
+
include GraphqlRails::Model
|
411
|
+
|
412
|
+
graphql.input do |c|
|
413
|
+
c.attribute :favorite_fruit, required: true, enum: %i[apple orange]
|
414
|
+
end
|
415
|
+
end
|
416
|
+
```
|
417
|
+
|
418
|
+
#### input attribute description
|
419
|
+
|
420
|
+
To improve graphql endpoint documentation, you can add description for each input attribute:
|
421
|
+
|
422
|
+
```ruby
|
423
|
+
class User
|
424
|
+
include GraphqlRails::Model
|
425
|
+
|
426
|
+
graphql.input do |c|
|
427
|
+
c.attribute :name, description: "User's first name"
|
428
|
+
end
|
429
|
+
end
|
430
|
+
```
|
431
|
+
|
432
|
+
## graphql_context
|
433
|
+
|
434
|
+
It's possible to access graphql_context in your model using method `graphql_context`:
|
435
|
+
|
436
|
+
```ruby
|
437
|
+
class User
|
438
|
+
include GraphqlRails::Model
|
439
|
+
|
440
|
+
def method_with_context
|
441
|
+
graphql_context[:some_data]
|
442
|
+
end
|
443
|
+
end
|
444
|
+
```
|
445
|
+
|
446
|
+
Keep in mind that this value will be set only during graphql execution, but in tests this value will be `nil`. To avoid this, you need to set `show_graphql_context` manually like this:
|
447
|
+
|
448
|
+
```ruby
|
449
|
+
class User
|
450
|
+
include GraphqlRails::Model
|
451
|
+
|
452
|
+
def show_graphql_context
|
453
|
+
graphql_context[:data]
|
454
|
+
end
|
455
|
+
end
|
456
|
+
|
457
|
+
user = User.new(...)
|
458
|
+
user.show_graphql_context #=> NoMethodError: undefined method `[]' for nil:NilClass
|
459
|
+
user.graphql_context = { data: 'goes to context' }
|
460
|
+
user.show_graphql_context #=> { data: 'goes to context' }
|
461
|
+
```
|