graphql_rails 0.5.2 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (88) hide show
  1. checksums.yaml +4 -4
  2. data/.hound.yml +1 -0
  3. data/.rubocop.yml +3 -3
  4. data/.ruby-version +1 -1
  5. data/.travis.yml +2 -2
  6. data/CHANGELOG.md +35 -0
  7. data/Gemfile +3 -2
  8. data/Gemfile.lock +179 -71
  9. data/docs/README.md +48 -9
  10. data/docs/_sidebar.md +5 -0
  11. data/docs/components/controller.md +294 -8
  12. data/docs/components/decorator.md +69 -0
  13. data/docs/components/model.md +349 -11
  14. data/docs/components/routes.md +43 -1
  15. data/docs/getting_started/quick_start.md +9 -2
  16. data/docs/index.html +1 -1
  17. data/docs/logging_and_monitoring/logging_and_monitoring.md +35 -0
  18. data/docs/other_tools/query_runner.md +49 -0
  19. data/docs/other_tools/schema_dump.md +29 -0
  20. data/docs/testing/testing.md +3 -1
  21. data/graphql_rails.gemspec +5 -4
  22. data/lib/generators/graphql_rails/install_generator.rb +50 -0
  23. data/lib/generators/graphql_rails/templates/example_users_controller.erb +19 -0
  24. data/lib/generators/graphql_rails/templates/graphql_application_controller.erb +8 -0
  25. data/lib/generators/graphql_rails/templates/graphql_controller.erb +20 -0
  26. data/lib/generators/graphql_rails/templates/graphql_router.erb +19 -0
  27. data/lib/generators/graphql_rails/templates/graphql_router_spec.erb +21 -0
  28. data/lib/graphql_rails.rb +7 -1
  29. data/lib/graphql_rails/attributes.rb +13 -0
  30. data/lib/graphql_rails/{attribute → attributes}/attributable.rb +25 -20
  31. data/lib/graphql_rails/attributes/attribute.rb +94 -0
  32. data/lib/graphql_rails/{attribute → attributes}/attribute_name_parser.rb +2 -2
  33. data/lib/graphql_rails/attributes/input_attribute.rb +65 -0
  34. data/lib/graphql_rails/attributes/input_type_parser.rb +62 -0
  35. data/lib/graphql_rails/attributes/type_name_info.rb +38 -0
  36. data/lib/graphql_rails/attributes/type_parseable.rb +128 -0
  37. data/lib/graphql_rails/attributes/type_parser.rb +121 -0
  38. data/lib/graphql_rails/concerns/service.rb +19 -0
  39. data/lib/graphql_rails/controller.rb +44 -22
  40. data/lib/graphql_rails/controller/action.rb +12 -67
  41. data/lib/graphql_rails/controller/action_configuration.rb +71 -31
  42. data/lib/graphql_rails/controller/build_controller_action_resolver.rb +52 -0
  43. data/lib/graphql_rails/controller/build_controller_action_resolver/controller_action_resolver.rb +28 -0
  44. data/lib/graphql_rails/controller/configuration.rb +58 -4
  45. data/lib/graphql_rails/controller/log_controller_action.rb +66 -0
  46. data/lib/graphql_rails/controller/request.rb +29 -8
  47. data/lib/graphql_rails/controller/request/format_errors.rb +58 -0
  48. data/lib/graphql_rails/decorator.rb +41 -0
  49. data/lib/graphql_rails/decorator/relation_decorator.rb +79 -0
  50. data/lib/graphql_rails/errors/custom_execution_error.rb +22 -0
  51. data/lib/graphql_rails/errors/execution_error.rb +8 -7
  52. data/lib/graphql_rails/errors/system_error.rb +14 -0
  53. data/lib/graphql_rails/errors/validation_error.rb +1 -5
  54. data/lib/graphql_rails/input_configurable.rb +47 -0
  55. data/lib/graphql_rails/integrations.rb +19 -0
  56. data/lib/graphql_rails/integrations/lograge.rb +39 -0
  57. data/lib/graphql_rails/integrations/sentry.rb +34 -0
  58. data/lib/graphql_rails/model.rb +26 -4
  59. data/lib/graphql_rails/model/add_fields_to_graphql_type.rb +45 -0
  60. data/lib/graphql_rails/model/build_connection_type.rb +48 -0
  61. data/lib/graphql_rails/model/{configuration → build_connection_type}/count_items.rb +4 -4
  62. data/lib/graphql_rails/model/build_enum_type.rb +68 -0
  63. data/lib/graphql_rails/model/{graphql_input_type_builder.rb → build_graphql_input_type.rb} +10 -2
  64. data/lib/graphql_rails/model/call_graphql_model_method.rb +72 -0
  65. data/lib/graphql_rails/model/configurable.rb +6 -2
  66. data/lib/graphql_rails/model/configuration.rb +34 -19
  67. data/lib/graphql_rails/model/find_or_build_graphql_type.rb +64 -0
  68. data/lib/graphql_rails/model/find_or_build_graphql_type_class.rb +46 -0
  69. data/lib/graphql_rails/model/input.rb +25 -11
  70. data/lib/graphql_rails/query_runner.rb +68 -0
  71. data/lib/graphql_rails/railtie.rb +10 -0
  72. data/lib/graphql_rails/router.rb +40 -13
  73. data/lib/graphql_rails/router/plain_cursor_encoder.rb +16 -0
  74. data/lib/graphql_rails/router/resource_routes_builder.rb +19 -11
  75. data/lib/graphql_rails/router/route.rb +21 -6
  76. data/lib/graphql_rails/router/schema_builder.rb +29 -11
  77. data/lib/graphql_rails/rspec_controller_helpers.rb +25 -12
  78. data/lib/graphql_rails/tasks/dump_graphql_schema.rb +57 -0
  79. data/lib/graphql_rails/tasks/schema.rake +14 -0
  80. data/lib/graphql_rails/version.rb +1 -1
  81. metadata +75 -22
  82. data/README.md +0 -194
  83. data/lib/graphql_rails/attribute.rb +0 -28
  84. data/lib/graphql_rails/attribute/type_parser.rb +0 -115
  85. data/lib/graphql_rails/controller/controller_function.rb +0 -50
  86. data/lib/graphql_rails/controller/format_results.rb +0 -36
  87. data/lib/graphql_rails/model/graphql_type_builder.rb +0 -33
  88. data/lib/graphql_rails/model/input_attribute.rb +0 -47
@@ -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
- ### *can_return_nil*
69
+ ### *permit_input*
70
70
 
71
- By default it is expected that each controller action returns model or array of models. `nil` is not allowed. You can change that by adding `can_return_nil` like this:
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(:show).permit(:email).can_return_nil
101
+ action(:create).permit_input(:some_field!)
102
+ end
103
+ ```
76
104
 
77
- def show
78
- user = User.find_by(email: params[:email])
79
- return nil if user.blank?
80
- user
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
- By default return type is determined by controller name. When you want to return some custom object, you can specify that with `returns` method:
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
+ ```
@@ -15,15 +15,15 @@ class User # works with any class including ActiveRecord
15
15
  end
16
16
  ```
17
17
 
18
- ## _graphql_
18
+ ## graphql
19
19
 
20
- This method must be called inside your model body. `grapqhl` is used for making your model convertible to graphql type.
20
+ This method must be called inside your model body. `graphql` is used for making your model convertible to graphql type.
21
21
 
22
- ### _attribute_
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
- #### _type_
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
- #### _property_
57
+ #### attribute.type: using graphql-ruby objects
58
58
 
59
- 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 `propery` option:
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
- #### _description_
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
- ### _name_
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
- ### _description_
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
- ### *graphql_type*
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
+ ```