graphql-api 0.1.5 → 0.1.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +46 -25
- data/lib/graphql/api/policy.rb +6 -2
- data/lib/graphql/api/resolvers/field.rb +5 -6
- data/lib/graphql/api/resolvers/model_create_mutation.rb +2 -4
- data/lib/graphql/api/resolvers/model_delete_mutation.rb +2 -4
- data/lib/graphql/api/resolvers/model_find_query.rb +2 -4
- data/lib/graphql/api/resolvers/model_list_query.rb +11 -1
- data/lib/graphql/api/resolvers/model_update_mutation.rb +2 -4
- data/lib/graphql/api/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d066b645b42c6471423c84254b296abe806d8bda
|
4
|
+
data.tar.gz: dcbc217929b63f258c12c63f392b373acbe6d9dd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e3b258f21b2d9d8d5de142659f3334c22d37ec2cf260c448dc6cb166b2461d22bb46ba033a65b2738e5258ee99b4f5e1a0fa4bd82fba6dc9a42d727e1c8718aa
|
7
|
+
data.tar.gz: eb187d750c57364262bccc79ac107fd331de3c34e51bdc625f1d5003b0a0491863376d57f50051d416c3a3d8ced201ed210f8f348497a87fcbc4507ad2d49908
|
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# GraphQL-Api
|
2
|
-
GraphQL-Api is an opinionated Graphql framework for Rails that supports
|
3
|
-
auto generating queries based on Active Record models and plain Ruby
|
2
|
+
GraphQL-Api is an opinionated Graphql framework for Rails that supports
|
3
|
+
auto generating queries based on Active Record models and plain Ruby
|
4
4
|
objects.
|
5
5
|
|
6
6
|
## Example
|
@@ -93,46 +93,43 @@ class GraphqlController < ApplicationController
|
|
93
93
|
# will respond to graphql requests and pass through the current user
|
94
94
|
def create
|
95
95
|
render json: GraphSchema.execute(
|
96
|
-
params[:query],
|
97
|
-
variables: params[:variables] || {},
|
96
|
+
params[:query],
|
97
|
+
variables: params[:variables] || {},
|
98
98
|
context: {current_user: current_user}
|
99
99
|
)
|
100
100
|
end
|
101
|
-
|
101
|
+
|
102
102
|
end
|
103
103
|
```
|
104
104
|
|
105
105
|
### Authorization
|
106
106
|
|
107
|
-
GraphQL-Api will check for an `access_<field>?(ctx)` method on all model
|
108
|
-
objects before returning the value. If this method returns false, the
|
107
|
+
GraphQL-Api will check for an `access_<field>?(ctx)` method on all model
|
108
|
+
objects before returning the value. If this method returns false, the
|
109
109
|
value will be `nil`.
|
110
110
|
|
111
|
-
To scope queries for the model, define the `graph_find(args, ctx)` and
|
111
|
+
To scope queries for the model, define the `graph_find(args, ctx)` and
|
112
112
|
`graph_where(args, ctx)` methods using the `ctx` parameter to get the
|
113
113
|
current user and apply a scoped query. For example:
|
114
114
|
|
115
115
|
```ruby
|
116
116
|
class Blog < ActiveRecord::Base
|
117
117
|
belongs_to :author
|
118
|
-
|
118
|
+
|
119
119
|
def self.graph_find(args, ctx)
|
120
120
|
ctx[:current_user].blogs.find(args[:id])
|
121
121
|
end
|
122
|
-
|
122
|
+
|
123
123
|
def access_content?(ctx)
|
124
124
|
ctx[:current_user].is_admin?
|
125
125
|
end
|
126
|
-
|
126
|
+
|
127
127
|
end
|
128
128
|
```
|
129
129
|
|
130
130
|
For more complicated access management, define query objects and Poro's
|
131
131
|
with only a subset of fields that can be accessed.
|
132
132
|
|
133
|
-
Future work is this area is ongoing. For example, a CanCan integration
|
134
|
-
could be a much simpler way of separating out this logic.
|
135
|
-
|
136
133
|
## Documentation
|
137
134
|
|
138
135
|
### Querying
|
@@ -151,14 +148,39 @@ you can pass them in directly to the new command.
|
|
151
148
|
|
152
149
|
### Policy Objects
|
153
150
|
|
154
|
-
Policy objects can be
|
155
|
-
|
151
|
+
Policy objects can be created for controlling access to fields of a model.
|
152
|
+
This enables you to provide a DRY solution for controlling access to granular
|
153
|
+
attributes of models and PORO's.
|
156
154
|
|
157
|
-
|
155
|
+
The policy object is defined as follows, with the naming convention following
|
156
|
+
the pattern `{Model}Policy`.
|
157
|
+
```ruby
|
158
|
+
class UserPolicy < GraphQL::Api::Policy
|
158
159
|
|
159
|
-
|
160
|
-
|
160
|
+
def read?
|
161
|
+
# model is the instance, user is the current_user
|
162
|
+
user.role == 'admin' || user.id == model.id
|
163
|
+
end
|
161
164
|
|
165
|
+
end
|
166
|
+
```
|
167
|
+
There are four key methods that represent the CRUD operations:
|
168
|
+
- `read?`
|
169
|
+
- `create?`
|
170
|
+
- `update?`
|
171
|
+
- `destroy?`
|
172
|
+
|
173
|
+
These are checked by the default resolvers created by GraphQL-Api
|
174
|
+
before the mutations or queries take place. Additionally, for control
|
175
|
+
over access to fields, you may define a method on the policy with the
|
176
|
+
following form:
|
177
|
+
|
178
|
+
access_{field}?
|
179
|
+
|
180
|
+
If this method exists, the policy will be consulted before reading the
|
181
|
+
field. You can control the response behaviour by overriding the
|
182
|
+
`unauthorized_field_access(field_name)` method on the policy object.
|
183
|
+
By default it will return nil.
|
162
184
|
|
163
185
|
### Model Objects
|
164
186
|
|
@@ -175,7 +197,7 @@ relationship is set up. Column types are also inferred.
|
|
175
197
|
|
176
198
|
GraphQL-Api will set up two queries on the main Graphql query object. One for
|
177
199
|
a single record and another for a collection. You can override these queries
|
178
|
-
by setting a `graph_find(args, ctx)` and `graph_where(args, ctx)` class
|
200
|
+
by setting a `graph_find(args, ctx)` and `graph_where(args, ctx)` class
|
179
201
|
methods on your model. The `ctx` parameter will contain the context passed
|
180
202
|
in from the controller while the `args` parameter will contain the arguments
|
181
203
|
passed into the graphql query.
|
@@ -183,7 +205,7 @@ passed into the graphql query.
|
|
183
205
|
#### Poro
|
184
206
|
|
185
207
|
Plain old ruby objects are supported by implementing a class method called
|
186
|
-
`fields` on the object that returns the expected [types](#types) hash.
|
208
|
+
`fields` on the object that returns the expected [types](#types) hash.
|
187
209
|
Methods on the Poro should be defined with the same name as the provided
|
188
210
|
fields.
|
189
211
|
|
@@ -206,7 +228,7 @@ below will create a `updateBlogCommand` mutation as well as a `deleteBlogCommand
|
|
206
228
|
class BlogCommand < GraphQL::Api::CommandType
|
207
229
|
inputs name: :string, tags: [:string], id: :integer
|
208
230
|
returns blog: Blog
|
209
|
-
|
231
|
+
|
210
232
|
# this tells GraphQL-Api to make two mutations that call the below methods.
|
211
233
|
actions :update, :delete
|
212
234
|
|
@@ -262,8 +284,8 @@ docs for how to do this.
|
|
262
284
|
|
263
285
|
### Types
|
264
286
|
|
265
|
-
Field types and argument types are all supplied as a hash of key value
|
266
|
-
pairs. An exclamation mark at the end of the type marks it as required,
|
287
|
+
Field types and argument types are all supplied as a hash of key value
|
288
|
+
pairs. An exclamation mark at the end of the type marks it as required,
|
267
289
|
and wrapping the type in an array marks it as a list of that type.
|
268
290
|
|
269
291
|
```ruby
|
@@ -289,7 +311,6 @@ Note, these are the same as active record's column types for consistency.
|
|
289
311
|
## Roadmap
|
290
312
|
|
291
313
|
- [ ] Customizing resolvers
|
292
|
-
- [ ] CanCan support
|
293
314
|
- [ ] Relay support
|
294
315
|
- [ ] Additional object support (enums, interfaces ...)
|
295
316
|
- [ ] Support non rails frameworks
|
data/lib/graphql/api/policy.rb
CHANGED
@@ -10,15 +10,14 @@ module GraphQL::Api
|
|
10
10
|
|
11
11
|
def call(obj, args, ctx)
|
12
12
|
if @policy_class
|
13
|
-
policy = @policy_class.new(ctx
|
14
|
-
return
|
13
|
+
policy = @policy_class.new(ctx, obj)
|
14
|
+
return policy.unauthorized! unless policy.read?
|
15
15
|
|
16
16
|
if policy.respond_to?("access_#{@name}?")
|
17
|
-
|
18
|
-
else
|
19
|
-
obj.send(@name)
|
17
|
+
return policy.unauthorized_field_access(@name) unless policy.send("access_#{@name}?")
|
20
18
|
end
|
21
|
-
|
19
|
+
|
20
|
+
obj.send(@name)
|
22
21
|
elsif obj.respond_to?("access_#{@name}?")
|
23
22
|
obj.send(@name) if obj.send("access_#{@name}?", ctx)
|
24
23
|
else
|
@@ -9,10 +9,8 @@ module GraphQL::Api
|
|
9
9
|
|
10
10
|
def call(inputs, ctx)
|
11
11
|
if @policy_class
|
12
|
-
policy = @policy_class.new(ctx
|
13
|
-
unless policy.create?
|
14
|
-
return {key => nil}
|
15
|
-
end
|
12
|
+
policy = @policy_class.new(ctx, nil)
|
13
|
+
return policy.unauthorized! unless policy.create?
|
16
14
|
end
|
17
15
|
|
18
16
|
item = @model.create!(inputs.to_h)
|
@@ -11,10 +11,8 @@ module GraphQL::Api
|
|
11
11
|
item = @model.find(inputs[:id])
|
12
12
|
|
13
13
|
if @policy_class
|
14
|
-
policy = @policy_class.new(ctx
|
15
|
-
unless policy.destroy?
|
16
|
-
return {key => nil}
|
17
|
-
end
|
14
|
+
policy = @policy_class.new(ctx, item)
|
15
|
+
return policy.unauthorized! unless policy.destroy?
|
18
16
|
end
|
19
17
|
|
20
18
|
item.destroy!
|
@@ -15,10 +15,8 @@ module GraphQL::Api
|
|
15
15
|
end
|
16
16
|
|
17
17
|
if @policy_class
|
18
|
-
policy = @policy_class.new(ctx
|
19
|
-
unless policy.read?
|
20
|
-
return nil
|
21
|
-
end
|
18
|
+
policy = @policy_class.new(ctx, item)
|
19
|
+
return policy.unauthorized! unless policy.read?
|
22
20
|
end
|
23
21
|
|
24
22
|
item
|
@@ -4,6 +4,7 @@ module GraphQL::Api
|
|
4
4
|
|
5
5
|
def initialize(model)
|
6
6
|
@model = model
|
7
|
+
@policy_class = "#{model.name}Policy".safe_constantize
|
7
8
|
end
|
8
9
|
|
9
10
|
def call(obj, args, ctx)
|
@@ -20,7 +21,16 @@ module GraphQL::Api
|
|
20
21
|
|
21
22
|
q = @model.where(query_args)
|
22
23
|
q.eager_load(*eager_load) if eager_load.any?
|
23
|
-
q.limit(args[:limit] || 30)
|
24
|
+
results = q.limit(args[:limit] || 30)
|
25
|
+
|
26
|
+
if @policy_class
|
27
|
+
results.each do |res|
|
28
|
+
policy = @policy_class.new(ctx, res)
|
29
|
+
return policy.unauthorized! unless policy.read?
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
results
|
24
34
|
end
|
25
35
|
end
|
26
36
|
|
@@ -11,10 +11,8 @@ module GraphQL::Api
|
|
11
11
|
item = @model.find(inputs[:id])
|
12
12
|
|
13
13
|
if @policy_class
|
14
|
-
policy = @policy_class.new(ctx
|
15
|
-
unless policy.destroy?
|
16
|
-
return {key => nil}
|
17
|
-
end
|
14
|
+
policy = @policy_class.new(ctx, item)
|
15
|
+
return policy.unauthorized! unless policy.destroy?
|
18
16
|
end
|
19
17
|
|
20
18
|
item.update!(inputs.to_h)
|
data/lib/graphql/api/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: graphql-api
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Colin Walker
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2017-01-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|