graphql_rails 0.3.2 → 0.3.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +3 -0
- data/Gemfile.lock +1 -1
- data/README.md +2 -2
- data/docs/.nojekyll +0 -0
- data/docs/README.md +188 -0
- data/docs/_sidebar.md +9 -0
- data/docs/components/controller.md +152 -0
- data/docs/components/model.md +123 -0
- data/docs/components/routes.md +87 -0
- data/docs/getting_started/quick_start.md +55 -0
- data/docs/getting_started/setup.md +16 -0
- data/docs/index.html +70 -0
- data/docs/testing/testing.md +68 -0
- data/lib/graphql_rails/controller.rb +9 -7
- data/lib/graphql_rails/controller/before_action_filter.rb +30 -0
- data/lib/graphql_rails/controller/configuration.rb +10 -6
- data/lib/graphql_rails/model/configuration.rb +7 -1
- data/lib/graphql_rails/version.rb +1 -1
- metadata +13 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e2bd0f0c9f6aaa1808567914c7a1440302a8b7dc828e97937a264dc33cbdd0d5
|
4
|
+
data.tar.gz: eac8327fdf61cbd8bedb9301f261448a48ebeaf0d14ce78a6fd986d5aa752eb9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e9df5b2875fc19b6a61259938ea99397edb51282c964f7bd511b1709c6625352e7c3d22428eac5ade6617b4a54244fb4b4ffa1574951b8368079fd7428cc5d68
|
7
|
+
data.tar.gz: bb8c01ac692e2137f9f0d3ab39a1b7a562ba337145ea0f90a0dd3325a5a67caf523d9f97c0536008adce4815ad15adda1ae036d8ded6f970c16bd761bc7c9a19
|
data/.rubocop.yml
CHANGED
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# GraphqlRails
|
2
2
|
|
3
|
-
[![Build Status](https://travis-ci.org/
|
4
|
-
[![codecov](https://codecov.io/gh/
|
3
|
+
[![Build Status](https://travis-ci.org/samesystem/graphql_rails.svg?branch=master)](https://travis-ci.org/samesystem/graphql_rails)
|
4
|
+
[![codecov](https://codecov.io/gh/samesystem/graphql_rails/branch/master/graph/badge.svg)](https://codecov.io/gh/samesystem/graphql_rails)
|
5
5
|
|
6
6
|
Rails style structure for GrapQL API.
|
7
7
|
|
data/docs/.nojekyll
ADDED
File without changes
|
data/docs/README.md
ADDED
@@ -0,0 +1,188 @@
|
|
1
|
+
# GraphqlRails
|
2
|
+
|
3
|
+
|
4
|
+
Rails style structure for GrapQL API.
|
5
|
+
|
6
|
+
## Installation
|
7
|
+
|
8
|
+
Add this line to your application's Gemfile:
|
9
|
+
|
10
|
+
```ruby
|
11
|
+
gem 'graphql_rails'
|
12
|
+
```
|
13
|
+
|
14
|
+
And then execute:
|
15
|
+
|
16
|
+
$ bundle
|
17
|
+
|
18
|
+
Or install it yourself as:
|
19
|
+
|
20
|
+
$ gem install graphql_rails
|
21
|
+
|
22
|
+
## Usage
|
23
|
+
|
24
|
+
### Define GraphQL schema as RoR routes
|
25
|
+
|
26
|
+
```ruby
|
27
|
+
MyGraphqlSchema = GraphqlRails::Router.draw do
|
28
|
+
# will create createUser, updateUser, deleteUser mutations and user, users queries.
|
29
|
+
# expects that UsersController class exist
|
30
|
+
resources :users
|
31
|
+
|
32
|
+
# if you want custom queries or mutation
|
33
|
+
query 'searchLogs', to: 'logs#search' # redirects request to LogsController
|
34
|
+
mutation 'changeUserPassword', to: 'users#change_password'
|
35
|
+
end
|
36
|
+
```
|
37
|
+
|
38
|
+
### Define your Graphql model
|
39
|
+
|
40
|
+
```ruby
|
41
|
+
class User # works with any class including ActiveRecord
|
42
|
+
include GraphqlRails::Model
|
43
|
+
|
44
|
+
graphql do |c|
|
45
|
+
# most common attributes, like :id, :name, :title has default type, so you don't have to specify it (but you can!)
|
46
|
+
c.attribute :id
|
47
|
+
|
48
|
+
c.attribute :email, type: :string
|
49
|
+
c.attribute :surname, type: :string
|
50
|
+
end
|
51
|
+
end
|
52
|
+
```
|
53
|
+
|
54
|
+
### Define controller
|
55
|
+
|
56
|
+
```ruby
|
57
|
+
class UsersController < GraphqlRails::Controller
|
58
|
+
# graphql requires to describe which attributes controller action accepts and which returns
|
59
|
+
action(:change_user_password)
|
60
|
+
.permit(:password!, :id!) # Bang (!) indicates that attribute is required
|
61
|
+
|
62
|
+
def change_user_password
|
63
|
+
user = User.find(params[:id])
|
64
|
+
user.update!(password: params[:password])
|
65
|
+
|
66
|
+
# returned value needs to have all methods defined in model `graphql do` part
|
67
|
+
user # or SomeDecorator.new(user)
|
68
|
+
end
|
69
|
+
|
70
|
+
action(:search).permit(search_fields!: SearchFieldsInput) # you can specify your own input fields
|
71
|
+
def search
|
72
|
+
end
|
73
|
+
end
|
74
|
+
```
|
75
|
+
|
76
|
+
## Routes
|
77
|
+
|
78
|
+
```ruby
|
79
|
+
MyGraphqlSchema = GraphqlRails::Router.draw do
|
80
|
+
# generates `friend`, `createFriend`, `updateFriend`, `destroyFriend`, `friends` routes
|
81
|
+
resources :friends
|
82
|
+
resources :shops, only: [:show, :index] # generates `shop` and `shops` routes only
|
83
|
+
resources :orders, except: :update # generates all routes except `updateOrder`
|
84
|
+
|
85
|
+
resources :users do
|
86
|
+
# generates `findUser` query
|
87
|
+
query :find, on: :member
|
88
|
+
|
89
|
+
# generates `searchUsers` query
|
90
|
+
query :search, on: :collection
|
91
|
+
end
|
92
|
+
|
93
|
+
# you can use namespaced controllers too:
|
94
|
+
scope module: 'admin' do
|
95
|
+
# `updateTranslations` route will be handeled by `Admin::TranslationsController`
|
96
|
+
mutation :updateTranslations, to: 'translations#update'
|
97
|
+
|
98
|
+
# all :groups routes will be handeled by `Admin::GroupsController`
|
99
|
+
resources :groups
|
100
|
+
end
|
101
|
+
end
|
102
|
+
```
|
103
|
+
|
104
|
+
## Testing your GrapqhlRails::Controller in RSpec
|
105
|
+
|
106
|
+
### Setup
|
107
|
+
|
108
|
+
Add those lines in your `spec/spec_helper.rb` file
|
109
|
+
|
110
|
+
```ruby
|
111
|
+
# spec/spec_helper.rb
|
112
|
+
require 'graphql_rails/rspec_controller_helpers'
|
113
|
+
|
114
|
+
RSpec.configure do |config|
|
115
|
+
config.include(GraphqlRails::RSpecControllerHelpers, type: :graphql_controller)
|
116
|
+
# ... your other configuration ...
|
117
|
+
end
|
118
|
+
```
|
119
|
+
|
120
|
+
### Helper methods
|
121
|
+
|
122
|
+
There are 3 helper methods:
|
123
|
+
|
124
|
+
* `mutation(:your_controller_action_name, params: {}, context: {})`. `params` and `context` are optional
|
125
|
+
* `query(:your_controller_action_name, params: {}, context: {})`. `params` and `context` are optional
|
126
|
+
* `response`. Response is set only after you call `mutation` or `query`
|
127
|
+
|
128
|
+
### Test examples
|
129
|
+
|
130
|
+
```ruby
|
131
|
+
class MyGraphqlController
|
132
|
+
def index
|
133
|
+
"Called from index: #{params[:message]}"
|
134
|
+
end
|
135
|
+
|
136
|
+
action(:create_user).permit(:full_name, :email)
|
137
|
+
def create_user
|
138
|
+
User.create!(params)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
RSpec.describe MyGraphqlController, type: :graphql_controller do
|
143
|
+
describe '#index' do
|
144
|
+
it 'is successful' do
|
145
|
+
query(:index)
|
146
|
+
expect(response).to be_successful
|
147
|
+
end
|
148
|
+
|
149
|
+
it 'returns correct message' do
|
150
|
+
query(:index, params: { message: 'Hello world!' })
|
151
|
+
expect(response.result).to eq "Called from index: Hello world!"
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
describe '#create_user' do
|
156
|
+
context 'when bad email is given' do
|
157
|
+
it 'fails' do
|
158
|
+
mutation(:create_user, params { email: 'bad' })
|
159
|
+
expect(response).to be_failure
|
160
|
+
end
|
161
|
+
|
162
|
+
it 'contains errors' do
|
163
|
+
mutation(:create_user, params { email: 'bad' })
|
164
|
+
expect(response.errors).not_to be_empty
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
```
|
170
|
+
|
171
|
+
|
172
|
+
## Development
|
173
|
+
|
174
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
175
|
+
|
176
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
177
|
+
|
178
|
+
## Contributing
|
179
|
+
|
180
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/graphql_rails. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
181
|
+
|
182
|
+
## License
|
183
|
+
|
184
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
185
|
+
|
186
|
+
## Code of Conduct
|
187
|
+
|
188
|
+
Everyone interacting in the GraphqlRails project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/graphql_rails/blob/master/CODE_OF_CONDUCT.md).
|
data/docs/_sidebar.md
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
* [Home](README)
|
2
|
+
* Getting started
|
3
|
+
* [Setup](getting_started/setup)
|
4
|
+
* [Quick start](getting_started/quick_start)
|
5
|
+
* Components
|
6
|
+
* [Routes](components/routes)
|
7
|
+
* [Model](components/model)
|
8
|
+
* [Controller](components/controller)
|
9
|
+
* [Testing](testing/testing)
|
@@ -0,0 +1,152 @@
|
|
1
|
+
# Controller
|
2
|
+
|
3
|
+
Each controller should inherit from `GraphqlRails::Controller`. It is handy to have `ApplicationGraphqlController`:
|
4
|
+
|
5
|
+
```ruby
|
6
|
+
class ApplicationGraphqlController < GraphqlRails::Controller
|
7
|
+
# write your shared code here
|
8
|
+
end
|
9
|
+
```
|
10
|
+
|
11
|
+
## *action*
|
12
|
+
|
13
|
+
to specify details about each controller action, you need to call method `action` inside controller. For example:
|
14
|
+
|
15
|
+
```ruby
|
16
|
+
class ApplicationGraphqlController < GraphqlRails::Controller
|
17
|
+
# write your shared code here
|
18
|
+
end
|
19
|
+
```
|
20
|
+
|
21
|
+
### *permit*
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
class UsersController < GraphqlRails::Controller
|
25
|
+
action(:create).describe('Creates user')
|
26
|
+
|
27
|
+
def create
|
28
|
+
User.create(params)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
```
|
32
|
+
|
33
|
+
### *can_return_nil*
|
34
|
+
|
35
|
+
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:
|
36
|
+
|
37
|
+
```ruby
|
38
|
+
class UsersController < GraphqlRails::Controller
|
39
|
+
action(:show).permit(:email).can_return_nil
|
40
|
+
|
41
|
+
def show
|
42
|
+
user = User.find_by(email: params[:email])
|
43
|
+
return nil if user.blank?
|
44
|
+
user
|
45
|
+
end
|
46
|
+
end
|
47
|
+
```
|
48
|
+
|
49
|
+
### *paginated*
|
50
|
+
|
51
|
+
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)
|
52
|
+
|
53
|
+
```ruby
|
54
|
+
class UsersController < GraphqlRails::Controller
|
55
|
+
action(:index).paginated
|
56
|
+
|
57
|
+
def index
|
58
|
+
User.all
|
59
|
+
end
|
60
|
+
end
|
61
|
+
```
|
62
|
+
|
63
|
+
#### *max_page_size*
|
64
|
+
|
65
|
+
Allows to specify max items count per request
|
66
|
+
|
67
|
+
```ruby
|
68
|
+
class UsersController < GraphqlRails::Controller
|
69
|
+
action(:index).paginated(max_page_size: 10) # add max items limit
|
70
|
+
|
71
|
+
def index
|
72
|
+
User.all # it will render 10 users even you have more
|
73
|
+
end
|
74
|
+
end
|
75
|
+
```
|
76
|
+
|
77
|
+
### *returns*
|
78
|
+
|
79
|
+
By default return type is determined by controller name. When you want to return some custom object, you can specify that with `returns` method:
|
80
|
+
|
81
|
+
```ruby
|
82
|
+
class UsersController < GraphqlRails::Controller
|
83
|
+
action(:last_order).permit(:id).returns(Order)
|
84
|
+
|
85
|
+
def last_order
|
86
|
+
user = User.find(params[:id]).orders.last
|
87
|
+
end
|
88
|
+
end
|
89
|
+
```
|
90
|
+
|
91
|
+
### *describe*
|
92
|
+
|
93
|
+
If you want to improve graphql documentation, you can add description for each action. To do so, use `describe` method:
|
94
|
+
|
95
|
+
```ruby
|
96
|
+
class UsersController < GraphqlRails::Controller
|
97
|
+
action(:create).describe('Creates user')
|
98
|
+
|
99
|
+
def create
|
100
|
+
User.create(params)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
```
|
104
|
+
|
105
|
+
## *before_action*
|
106
|
+
|
107
|
+
You can add `before_action` to run some filters before calling your controller action. Here is an example:
|
108
|
+
|
109
|
+
```ruby
|
110
|
+
class UsersController < GraphqlRails::Controller
|
111
|
+
before_action :require_auth_token
|
112
|
+
|
113
|
+
def create
|
114
|
+
User.create(params)
|
115
|
+
end
|
116
|
+
|
117
|
+
private
|
118
|
+
|
119
|
+
def require_auth_token # will run before `UsersController#create` action
|
120
|
+
raise 'Not authenticated' unless User.where(token: params[:token]).exist?
|
121
|
+
end
|
122
|
+
end
|
123
|
+
```
|
124
|
+
|
125
|
+
### *only* and *except* option
|
126
|
+
|
127
|
+
`UsersController.before_action` accepts `only` or `except` options which allows to skip filters for some actions.
|
128
|
+
|
129
|
+
```ruby
|
130
|
+
class UsersController < GraphqlRails::Controller
|
131
|
+
before_action :require_auth_token, except: :show
|
132
|
+
before_action :require_admin_token, only: %i[update destroy]
|
133
|
+
|
134
|
+
def create
|
135
|
+
User.create(params)
|
136
|
+
end
|
137
|
+
|
138
|
+
def destroy
|
139
|
+
User.create(params)
|
140
|
+
end
|
141
|
+
|
142
|
+
private
|
143
|
+
|
144
|
+
def require_auth_token
|
145
|
+
raise 'Not authenticated' unless User.where(token: params[:token]).exist?
|
146
|
+
end
|
147
|
+
|
148
|
+
def require_auth_token
|
149
|
+
raise 'Admin not authenticated' unless Admin.where(token: params[:admin_token]).exist?
|
150
|
+
end
|
151
|
+
end
|
152
|
+
```
|
@@ -0,0 +1,123 @@
|
|
1
|
+
# Model
|
2
|
+
|
3
|
+
To make your model graphql-firendly, you need to inlcude `GraphqlRails::Model`. Your model can be any ruby class (PORO, ActiveRecord::Base or anything else)
|
4
|
+
|
5
|
+
Also you need to define which attributes can be exposed via graphql. To do so, use `graphql` method inside your model body. Example:
|
6
|
+
|
7
|
+
```ruby
|
8
|
+
class User # works with any class including ActiveRecord
|
9
|
+
include GraphqlRails::Model
|
10
|
+
|
11
|
+
graphql do |c|
|
12
|
+
c.attribute :id
|
13
|
+
c.full_name, type: :string
|
14
|
+
end
|
15
|
+
end
|
16
|
+
```
|
17
|
+
|
18
|
+
## _graphql_
|
19
|
+
|
20
|
+
This method must be called inside your model body. `grapqhl` is used for making your model convertible to graphql type.
|
21
|
+
|
22
|
+
### _attribute_
|
23
|
+
|
24
|
+
Most commonly you will use `attribute` to make your model methods and attributes visible via graphql endpoint.
|
25
|
+
|
26
|
+
#### _type_
|
27
|
+
|
28
|
+
Some types can be determined by attribute name, so you can skip this attribute:
|
29
|
+
|
30
|
+
* attributes which ends with name `*_id` has `ID` type
|
31
|
+
* attributes which ends with `?` has `Boolean` type
|
32
|
+
* all other attributes without type are considered to be `String`
|
33
|
+
|
34
|
+
available types are:
|
35
|
+
|
36
|
+
* ID: `'id'`
|
37
|
+
* String: `'string'`, `'str'`, `'text'`
|
38
|
+
* Boolean: `'bool'`, `'boolean'`
|
39
|
+
* Float: `'float'`, `'double'`, `'decimal'`
|
40
|
+
|
41
|
+
usage example:
|
42
|
+
|
43
|
+
```ruby
|
44
|
+
class User
|
45
|
+
include GraphqlRails::Model
|
46
|
+
|
47
|
+
graphql do |c|
|
48
|
+
c.attribute :shop_id # ID type
|
49
|
+
c.attribute :full_name # String type
|
50
|
+
c.attribute :admin? # Boolean type
|
51
|
+
c.attribute :level, type: 'integer'
|
52
|
+
c.attribute :money, type: 'float'
|
53
|
+
end
|
54
|
+
end
|
55
|
+
```
|
56
|
+
|
57
|
+
#### _property_
|
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:
|
60
|
+
|
61
|
+
```
|
62
|
+
class User
|
63
|
+
include GraphqlRails::Model
|
64
|
+
|
65
|
+
graphql do |c|
|
66
|
+
c.attribute :shop_id, property: :department_id
|
67
|
+
end
|
68
|
+
|
69
|
+
def department_id
|
70
|
+
456
|
71
|
+
end
|
72
|
+
end
|
73
|
+
```
|
74
|
+
|
75
|
+
#### _description_
|
76
|
+
|
77
|
+
You can also describe each attribute and make graphql documentation even more readable. To do so, add `description` option:
|
78
|
+
|
79
|
+
```
|
80
|
+
class User
|
81
|
+
include GraphqlRails::Model
|
82
|
+
|
83
|
+
graphql do |c|
|
84
|
+
c.attribute :shop_id, description: 'references to shop'
|
85
|
+
end
|
86
|
+
end
|
87
|
+
```
|
88
|
+
|
89
|
+
### _name_
|
90
|
+
|
91
|
+
By default grapqhl type name will be same as model name, but you can change it via `name` method
|
92
|
+
|
93
|
+
```ruby
|
94
|
+
class User
|
95
|
+
include GraphqlRails::Model
|
96
|
+
|
97
|
+
graphql do |c|
|
98
|
+
c.name 'Employee'
|
99
|
+
end
|
100
|
+
end
|
101
|
+
```
|
102
|
+
|
103
|
+
### _description_
|
104
|
+
|
105
|
+
To improve grapqhl documentation, you can description for your graphql type:
|
106
|
+
|
107
|
+
```ruby
|
108
|
+
class User
|
109
|
+
include GraphqlRails::Model
|
110
|
+
|
111
|
+
graphql do |c|
|
112
|
+
c.description 'Users are awesome!'
|
113
|
+
end
|
114
|
+
end
|
115
|
+
```
|
116
|
+
|
117
|
+
### *graphql_type*
|
118
|
+
|
119
|
+
Sometimes it's handy to get raw graphql type. To do so you can call:
|
120
|
+
|
121
|
+
```ruby
|
122
|
+
YourModel.graphql.grapqhl_type
|
123
|
+
```
|
@@ -0,0 +1,87 @@
|
|
1
|
+
# Routes
|
2
|
+
|
3
|
+
Routes are generated via `GraphqlRails::Router.draw` method. It is very similar to rails router, except that instead of `match`, `get`, `post` actions you have `query` and `mutation` actions. In most cases you will use `resources` action. It works same as in rails router.
|
4
|
+
|
5
|
+
Here is simple router example:
|
6
|
+
|
7
|
+
```ruby
|
8
|
+
MyGraphqlSchema = GraphqlRails::Router.draw do
|
9
|
+
# generates `friend`, `createFriend`, `updateFriend`, `destroyFriend`, `friends` routes
|
10
|
+
resources :friends
|
11
|
+
resources :shops, only: [:show, :index] # generates `shop` and `shops` routes only
|
12
|
+
resources :orders, except: :update # generates all routes except `updateOrder`
|
13
|
+
|
14
|
+
resources :users do
|
15
|
+
# generates `findUser` query
|
16
|
+
query :find, on: :member
|
17
|
+
|
18
|
+
# generates `searchUsers` query
|
19
|
+
query :search, on: :collection
|
20
|
+
end
|
21
|
+
|
22
|
+
# you can use namespaced controllers too:
|
23
|
+
scope module: 'admin' do
|
24
|
+
# `updateTranslations` route will be handeled by `Admin::TranslationsController`
|
25
|
+
mutation :updateTranslations, to: 'translations#update'
|
26
|
+
|
27
|
+
# all :groups routes will be handeled by `Admin::GroupsController`
|
28
|
+
resources :groups
|
29
|
+
end
|
30
|
+
end
|
31
|
+
```
|
32
|
+
|
33
|
+
## _resources_
|
34
|
+
|
35
|
+
`resources` method generates routes to `index`, `show`, `create`, `update` and `destroy` controller actions.
|
36
|
+
|
37
|
+
### _only_ and _except_ option
|
38
|
+
|
39
|
+
If you want to exclude some actions you can use `only` or `except` options.
|
40
|
+
|
41
|
+
```ruby
|
42
|
+
MyGraphqlSchema = GraphqlRails::Router.draw do
|
43
|
+
resouces :users
|
44
|
+
resouces :friends, only: :index
|
45
|
+
resouces :posts, except: [:destroy, :index]
|
46
|
+
end
|
47
|
+
```
|
48
|
+
|
49
|
+
### custom actions
|
50
|
+
|
51
|
+
Sometimes it's handy so have non-CRUD actions in your controller. To define such route you can call `resources` with a block:
|
52
|
+
|
53
|
+
```ruby
|
54
|
+
MyGraphqlSchema = GraphqlRails::Router.draw do
|
55
|
+
resouces :users do
|
56
|
+
mutation :changePassword, on: :member
|
57
|
+
query :active, on: :collection
|
58
|
+
end
|
59
|
+
end
|
60
|
+
```
|
61
|
+
|
62
|
+
## _query_ and _mutation_
|
63
|
+
|
64
|
+
in case you want to have non-CRUD controller with custom actions you can define your own `query`/`mutation` actions like this:
|
65
|
+
|
66
|
+
```ruby
|
67
|
+
MyGraphqlSchema = GraphqlRails::Router.draw do
|
68
|
+
mutation :logIn, to: 'sessions#login'
|
69
|
+
query :me, to 'users#current_user'
|
70
|
+
end
|
71
|
+
```
|
72
|
+
|
73
|
+
## _scope_
|
74
|
+
|
75
|
+
### _module_ options
|
76
|
+
|
77
|
+
currently `scope` method accepts single option: `module`. `module` allows to specify controller namespace. So you can use scoped controllers, like so:
|
78
|
+
|
79
|
+
```ruby
|
80
|
+
MyGraphqlSchema = GraphqlRails::Router.draw do
|
81
|
+
scope module: 'admin/top_secret' do
|
82
|
+
mutation :logIn, to: 'sessions#login' # this will trigger Admin::TopSecret::SessionsController
|
83
|
+
end
|
84
|
+
|
85
|
+
mutation :logIn, to: 'sessions#login' # this will trigger ::SessionsController
|
86
|
+
end
|
87
|
+
```
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# Quick Start
|
2
|
+
|
3
|
+
## Define GraphQL schema as RoR routes
|
4
|
+
|
5
|
+
```ruby
|
6
|
+
MyGraphqlSchema = GraphqlRails::Router.draw do
|
7
|
+
# will create createUser, updateUser, deleteUser mutations and user, users queries.
|
8
|
+
# expects that UsersController class exist
|
9
|
+
resources :users
|
10
|
+
|
11
|
+
# if you want custom queries or mutation
|
12
|
+
query 'searchLogs', to: 'logs#search' # redirects request to LogsController
|
13
|
+
mutation 'changeUserPassword', to: 'users#change_password'
|
14
|
+
end
|
15
|
+
```
|
16
|
+
|
17
|
+
## Define your Graphql model
|
18
|
+
|
19
|
+
```ruby
|
20
|
+
class User # works with any class including ActiveRecord
|
21
|
+
include GraphqlRails::Model
|
22
|
+
|
23
|
+
graphql do |c|
|
24
|
+
# most common attributes, like :id, :name, :title has default type, so you don't have to specify it (but you can!)
|
25
|
+
c.attribute :id
|
26
|
+
|
27
|
+
c.attribute :email, :string
|
28
|
+
c.attribute :surname, :string
|
29
|
+
end
|
30
|
+
end
|
31
|
+
```
|
32
|
+
|
33
|
+
## Define controller
|
34
|
+
|
35
|
+
```ruby
|
36
|
+
class UsersController < GraphqlRails::Controller
|
37
|
+
# graphql requires to describe which attributes controller action accepts and which returns
|
38
|
+
action(:change_user_password)
|
39
|
+
.permit(:password!, :id!) # Bang (!) indicates that attribute is required
|
40
|
+
|
41
|
+
def change_user_password
|
42
|
+
user = User.find(params[:id])
|
43
|
+
user.update!(password: params[:password])
|
44
|
+
|
45
|
+
# returned value needs to have all methods defined in model `graphql do` part
|
46
|
+
user # or SomeDecorator.new(user)
|
47
|
+
end
|
48
|
+
|
49
|
+
action(:search).permit(search_fields!: SearchFieldsInput) # you can specify your own input fields
|
50
|
+
def search
|
51
|
+
end
|
52
|
+
end
|
53
|
+
```
|
54
|
+
|
55
|
+
Congrats, you are done!
|
data/docs/index.html
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html lang="en">
|
3
|
+
<head>
|
4
|
+
<meta charset="UTF-8">
|
5
|
+
<title>Document</title>
|
6
|
+
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
|
7
|
+
<meta name="description" content="Description">
|
8
|
+
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
|
9
|
+
<link rel="stylesheet" href="https://unpkg.com/docsify/lib/themes/vue.css">
|
10
|
+
</head>
|
11
|
+
<body>
|
12
|
+
<div id="app"></div>
|
13
|
+
<script>
|
14
|
+
function parseQueryString (queryString) {
|
15
|
+
var params = {};
|
16
|
+
var temp;
|
17
|
+
// Split into key/value pairs
|
18
|
+
queries = queryString.split("&");
|
19
|
+
// Convert the array of strings into an object
|
20
|
+
for (var i = 0, l = queries.length; i < l; i++ ) {
|
21
|
+
temp = queries[i].split('=');
|
22
|
+
params[temp[0]] = temp[1];
|
23
|
+
}
|
24
|
+
return params;
|
25
|
+
};
|
26
|
+
|
27
|
+
function getJsonFromUrl() {
|
28
|
+
return parseQueryString(location.search.substr(1));
|
29
|
+
}
|
30
|
+
|
31
|
+
window.$docsify = {
|
32
|
+
auto2top: true,
|
33
|
+
name: 'GraphqlRails',
|
34
|
+
repo: 'https://github.com/samesystem/graphql_rails',
|
35
|
+
subMaxLevel: 3,
|
36
|
+
loadSidebar: true,
|
37
|
+
formatUpdated: '{MM}/{DD} {HH}:{mm}',
|
38
|
+
branchBasePath: 'https://raw.githubusercontent.com/samesystem/graphql_rails/',
|
39
|
+
plugins: [
|
40
|
+
function (hook, vm) { // reasign any config value by param attribute
|
41
|
+
Object.assign(window.$docsify, getJsonFromUrl());
|
42
|
+
},
|
43
|
+
|
44
|
+
function (hook, vm) { // allow to change branch
|
45
|
+
if (!window.$docsify.branchBasePath || !window.$docsify.branch) {
|
46
|
+
return;
|
47
|
+
}
|
48
|
+
|
49
|
+
var branch = window.$docsify.branch;
|
50
|
+
var basePath = window.$docsify.branchBasePath + branch;
|
51
|
+
window.$docsify.basePath = basePath;
|
52
|
+
},
|
53
|
+
|
54
|
+
function (hook, vm) { // add edit page link
|
55
|
+
hook.beforeEach(function (html) {
|
56
|
+
var branch = window.$docsify.branch || 'master'
|
57
|
+
var url = 'https://github.com/samesystem/graphql_rails/edit/' + branch + '/docs/' + vm.route.file
|
58
|
+
var editHtml = '[:memo: Edit Document](' + url + ')\n'
|
59
|
+
return html
|
60
|
+
+ '\n\n----\n\n'
|
61
|
+
+ editHtml
|
62
|
+
})
|
63
|
+
}
|
64
|
+
]
|
65
|
+
}
|
66
|
+
</script>
|
67
|
+
<script src="https://unpkg.com/docsify/lib/docsify.js"></script>
|
68
|
+
<script src="https://unpkg.com/docsify/lib/plugins/search.min.js"></script>
|
69
|
+
</body>
|
70
|
+
</html>
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# Testing
|
2
|
+
|
3
|
+
## Testing graphql controllers in RSpec
|
4
|
+
|
5
|
+
### Setup
|
6
|
+
|
7
|
+
Add those lines in your `spec/spec_helper.rb` file
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
# spec/spec_helper.rb
|
11
|
+
require 'graphql_rails/rspec_controller_helpers'
|
12
|
+
|
13
|
+
RSpec.configure do |config|
|
14
|
+
config.include(GraphqlRails::RSpecControllerHelpers, type: :graphql_controller)
|
15
|
+
# ... your other configuration ...
|
16
|
+
end
|
17
|
+
```
|
18
|
+
|
19
|
+
### Helper methods
|
20
|
+
|
21
|
+
There are 3 helper methods:
|
22
|
+
|
23
|
+
* `mutation(:your_controller_action_name, params: {}, context: {})`. `params` and `context` are optional
|
24
|
+
* `query(:your_controller_action_name, params: {}, context: {})`. `params` and `context` are optional
|
25
|
+
* `response`. Response is set only after you call `mutation` or `query`
|
26
|
+
|
27
|
+
### Test examples
|
28
|
+
|
29
|
+
```ruby
|
30
|
+
class MyGraphqlController
|
31
|
+
def index
|
32
|
+
"Called from index: #{params[:message]}"
|
33
|
+
end
|
34
|
+
|
35
|
+
action(:create_user).permit(:full_name, :email)
|
36
|
+
def create_user
|
37
|
+
User.create!(params)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
RSpec.describe MyGraphqlController, type: :graphql_controller do
|
42
|
+
describe '#index' do
|
43
|
+
it 'is successful' do
|
44
|
+
query(:index)
|
45
|
+
expect(response).to be_successful
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'returns correct message' do
|
49
|
+
query(:index, params: { message: 'Hello world!' })
|
50
|
+
expect(response.result).to eq "Called from index: Hello world!"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe '#create_user' do
|
55
|
+
context 'when bad email is given' do
|
56
|
+
it 'fails' do
|
57
|
+
mutation(:create_user, params { email: 'bad' })
|
58
|
+
expect(response).to be_failure
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'contains errors' do
|
62
|
+
mutation(:create_user, params { email: 'bad' })
|
63
|
+
expect(response.errors).not_to be_empty
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
```
|
@@ -9,12 +9,12 @@ module GraphqlRails
|
|
9
9
|
# base class for all graphql_rails controllers
|
10
10
|
class Controller
|
11
11
|
class << self
|
12
|
-
def before_action(
|
13
|
-
controller_configuration.add_before_action(
|
12
|
+
def before_action(*args)
|
13
|
+
controller_configuration.add_before_action(*args)
|
14
14
|
end
|
15
15
|
|
16
|
-
def action(
|
17
|
-
controller_configuration.action(
|
16
|
+
def action(action_name)
|
17
|
+
controller_configuration.action(action_name)
|
18
18
|
end
|
19
19
|
|
20
20
|
def controller_configuration
|
@@ -55,9 +55,11 @@ module GraphqlRails
|
|
55
55
|
|
56
56
|
private
|
57
57
|
|
58
|
-
def call_with_rendering(
|
59
|
-
self.class.controller_configuration.
|
60
|
-
|
58
|
+
def call_with_rendering(action_name)
|
59
|
+
before_actions = self.class.controller_configuration.before_actions_for(action_name)
|
60
|
+
before_actions.each { |before_action| send(before_action.name) }
|
61
|
+
response = public_send(action_name)
|
62
|
+
|
61
63
|
render response if graphql_request.no_object_to_return?
|
62
64
|
rescue StandardError => error
|
63
65
|
render error: error
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GraphqlRails
|
4
|
+
class Controller
|
5
|
+
# stores information about controller filter
|
6
|
+
class BeforeActionFilter
|
7
|
+
attr_reader :name
|
8
|
+
|
9
|
+
def initialize(name, only: [], except: [])
|
10
|
+
@name = name
|
11
|
+
@only_actions = Array(only).map(&:to_sym)
|
12
|
+
@except_actions = Array(except).map(&:to_sym)
|
13
|
+
end
|
14
|
+
|
15
|
+
def applicable_for?(action_name)
|
16
|
+
if only_actions.any?
|
17
|
+
only_actions.include?(action_name.to_sym)
|
18
|
+
elsif except_actions.any?
|
19
|
+
!except_actions.include?(action_name.to_sym)
|
20
|
+
else
|
21
|
+
true
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
attr_reader :only_actions, :except_actions
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -3,21 +3,25 @@
|
|
3
3
|
require 'active_support/core_ext/string/inflections'
|
4
4
|
require 'graphql_rails/attribute'
|
5
5
|
require 'graphql_rails/controller/action_configuration'
|
6
|
+
require 'graphql_rails/controller/before_action_filter'
|
6
7
|
|
7
8
|
module GraphqlRails
|
8
9
|
class Controller
|
9
10
|
# stores all graphql_rails contoller specific config
|
10
11
|
class Configuration
|
11
|
-
attr_reader :before_actions
|
12
|
-
|
13
12
|
def initialize(controller)
|
14
13
|
@controller = controller
|
15
|
-
@before_actions =
|
14
|
+
@before_actions = {}
|
16
15
|
@action_by_name = {}
|
17
16
|
end
|
18
17
|
|
19
|
-
def
|
20
|
-
before_actions
|
18
|
+
def before_actions_for(action_name)
|
19
|
+
before_actions.values.select { |action| action.applicable_for?(action_name) }
|
20
|
+
end
|
21
|
+
|
22
|
+
def add_before_action(name, **options)
|
23
|
+
symbolized_name = name.to_sym
|
24
|
+
before_actions[symbolized_name] = BeforeActionFilter.new(symbolized_name, **options)
|
21
25
|
end
|
22
26
|
|
23
27
|
def action(method_name)
|
@@ -26,7 +30,7 @@ module GraphqlRails
|
|
26
30
|
|
27
31
|
private
|
28
32
|
|
29
|
-
attr_reader :controller
|
33
|
+
attr_reader :controller, :before_actions
|
30
34
|
end
|
31
35
|
end
|
32
36
|
end
|
@@ -9,6 +9,12 @@ module GraphqlRails
|
|
9
9
|
class Configuration
|
10
10
|
attr_reader :attributes
|
11
11
|
|
12
|
+
COUNT_TOTAL_ITEMS = lambda do |obj, _args, _ctx|
|
13
|
+
obj_nodes = obj.nodes
|
14
|
+
obj_nodes = obj_nodes.except(:offset) if obj_nodes.is_a?(ActiveRecord::Relation)
|
15
|
+
obj_nodes.size
|
16
|
+
end
|
17
|
+
|
12
18
|
def initialize(model_class)
|
13
19
|
@model_class = model_class
|
14
20
|
@attributes = {}
|
@@ -42,7 +48,7 @@ module GraphqlRails
|
|
42
48
|
def connection_type
|
43
49
|
@connection_type ||= begin
|
44
50
|
graphql_type.define_connection do
|
45
|
-
field :total, types.Int, resolve:
|
51
|
+
field :total, types.Int, resolve: COUNT_TOTAL_ITEMS
|
46
52
|
end
|
47
53
|
end
|
48
54
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: graphql_rails
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Povilas Jurčys
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-
|
11
|
+
date: 2018-09-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: graphql
|
@@ -101,6 +101,16 @@ files:
|
|
101
101
|
- Rakefile
|
102
102
|
- bin/console
|
103
103
|
- bin/setup
|
104
|
+
- docs/.nojekyll
|
105
|
+
- docs/README.md
|
106
|
+
- docs/_sidebar.md
|
107
|
+
- docs/components/controller.md
|
108
|
+
- docs/components/model.md
|
109
|
+
- docs/components/routes.md
|
110
|
+
- docs/getting_started/quick_start.md
|
111
|
+
- docs/getting_started/setup.md
|
112
|
+
- docs/index.html
|
113
|
+
- docs/testing/testing.md
|
104
114
|
- graphql_rails.gemspec
|
105
115
|
- lib/graphql_rails.rb
|
106
116
|
- lib/graphql_rails/attribute.rb
|
@@ -108,6 +118,7 @@ files:
|
|
108
118
|
- lib/graphql_rails/controller.rb
|
109
119
|
- lib/graphql_rails/controller/action.rb
|
110
120
|
- lib/graphql_rails/controller/action_configuration.rb
|
121
|
+
- lib/graphql_rails/controller/before_action_filter.rb
|
111
122
|
- lib/graphql_rails/controller/configuration.rb
|
112
123
|
- lib/graphql_rails/controller/controller_function.rb
|
113
124
|
- lib/graphql_rails/controller/format_results.rb
|