schema_based_api 2.1.14 → 2.1.15
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +142 -1
- data/app/controllers/api/v2/application_controller.rb +18 -15
- data/app/controllers/api/v2/authentication_controller.rb +0 -1
- data/config/initializers/after_initialize_for_schema_based_api.rb +2 -0
- data/lib/concerns/schema_based_api_role.rb +41 -0
- data/lib/concerns/schema_based_api_user.rb +36 -0
- data/lib/schema_based_api/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f096bb076a59df02a1c9a51b348a4265b4c4bcc085ee17297873b31cafdfe70a
|
4
|
+
data.tar.gz: 27a50ee08425905661fd685c4c5fed9619f2e86ca78aa510a1d18b749694452c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9bfceb05a06565d3896d4cf3cdfdffe2b99c99e034f4879cf8a83d75c15f283211ee59e7e956ad60080f52aa51a0e45f5a35cd8ca021ff9ccc829dbf5b1a9df1
|
7
|
+
data.tar.gz: 1ddf3166f10c7e884081ed873f926ad25ca60e5382690a5a391b13777223616189184c8bb97b11047e7e1e1ea01839e27860632dd0a0d51dceeed39f763ffb5f
|
data/README.md
CHANGED
@@ -50,11 +50,152 @@ This will setup a User model, Role model and the HABTM table between the two.
|
|
50
50
|
Then, if you fire up your ```rails server``` you can already get a jwt and perform different operations.
|
51
51
|
The default admin user created during the migration step has a randomly generated password you can find in a .passwords file in the root of your project, that's the initial password, in production you can replace that one, but for testing it proved handy to have it promptly available.
|
52
52
|
|
53
|
+
## Consuming the API
|
54
|
+
|
55
|
+
### Getting the Token
|
56
|
+
|
57
|
+
The first thing that must be done by the client is to get a Token using the credentials:
|
58
|
+
|
59
|
+
```bash
|
60
|
+
POST http://localhost:3000/api/v2/authenticate
|
61
|
+
```
|
62
|
+
|
63
|
+
with a POST body like the one below:
|
64
|
+
|
65
|
+
```json
|
66
|
+
{
|
67
|
+
"auth": {
|
68
|
+
"email": "<REPLACE>",
|
69
|
+
"password": "<REPLACE>"
|
70
|
+
}
|
71
|
+
}
|
72
|
+
```
|
73
|
+
|
74
|
+
This action will return in the header a *Token* you can use for the following requests.
|
75
|
+
Bear in mind that the *Token* will expire within 15 minutes and that at each succesful request a new token is returned using the same *Token* header, so, at each interaction between client server, just making an authenticated and succesful request, will give you back a way of continuing to make authenticated requests without the extra overhead of an authentication for each one and without having to keep long expiry times for the *Token*.
|
76
|
+
|
77
|
+
### Info API
|
78
|
+
|
79
|
+
The info API **api/v2/info/** can be used to retrieve general information about the REST API:
|
80
|
+
|
81
|
+
#### Version
|
82
|
+
|
83
|
+
By issuing a GET on this api, you will get a response containing the version of the schema_based_api.
|
84
|
+
This is a request which doesn't require authentication, it could be used as a checkpoint for consuming the resources exposed by this engine.
|
85
|
+
|
86
|
+
```bash
|
87
|
+
GET http://localhost:3000/api/v2/info/version
|
88
|
+
```
|
89
|
+
|
90
|
+
Would produce a response body like this one:
|
91
|
+
|
92
|
+
```json
|
93
|
+
{
|
94
|
+
"version": "2.1.14"
|
95
|
+
}
|
96
|
+
```
|
97
|
+
|
98
|
+
#### Roles
|
99
|
+
|
100
|
+
**Authenticated Request** by issuing a GET request to */api/v2/info/roles*:
|
101
|
+
|
102
|
+
```bash
|
103
|
+
GET http://localhost:3000/api/v2/info/roles
|
104
|
+
```
|
105
|
+
|
106
|
+
Something like this can be retrieved:
|
107
|
+
|
108
|
+
```json
|
109
|
+
[
|
110
|
+
{
|
111
|
+
"id": 1,
|
112
|
+
"name": "role-1586521657646",
|
113
|
+
"created_at": "2020-04-10T12:27:38.061Z",
|
114
|
+
"updated_at": "2020-04-10T12:27:38.061Z",
|
115
|
+
"lock_version": 0
|
116
|
+
},
|
117
|
+
{
|
118
|
+
"id": 2,
|
119
|
+
"name": "role-1586522353509",
|
120
|
+
"created_at": "2020-04-10T12:39:14.276Z",
|
121
|
+
"updated_at": "2020-04-10T12:39:14.276Z",
|
122
|
+
"lock_version": 0
|
123
|
+
}
|
124
|
+
]
|
125
|
+
```
|
126
|
+
|
127
|
+
#### Schema
|
128
|
+
|
129
|
+
**Authenticated Request** This action will send back the *authorized* models accessible by the current user at least for the [:read ability](https://github.com/ryanb/cancan/wiki/checking-abilities). The list will also show the field types of the model and the associations.
|
130
|
+
|
131
|
+
By issuing this GET request:
|
132
|
+
|
133
|
+
```bash
|
134
|
+
GET http://localhost:3000/api/v2/info/roles
|
135
|
+
```
|
136
|
+
|
137
|
+
You will get something like:
|
138
|
+
|
139
|
+
```json
|
140
|
+
{
|
141
|
+
"users": {
|
142
|
+
"id": "integer",
|
143
|
+
"email": "string",
|
144
|
+
"encrypted_password": "string",
|
145
|
+
"admin": "boolean",
|
146
|
+
"lock_version": "integer",
|
147
|
+
"associations": {
|
148
|
+
"has_many": [
|
149
|
+
"role_users",
|
150
|
+
"roles"
|
151
|
+
],
|
152
|
+
"belongs_to": []
|
153
|
+
},
|
154
|
+
"methods": null
|
155
|
+
},
|
156
|
+
"role_users": {
|
157
|
+
"id": "integer",
|
158
|
+
"created_at": "datetime",
|
159
|
+
"updated_at": "datetime",
|
160
|
+
"associations": {
|
161
|
+
"has_many": [],
|
162
|
+
"belongs_to": [
|
163
|
+
"user",
|
164
|
+
"role"
|
165
|
+
]
|
166
|
+
},
|
167
|
+
"methods": null
|
168
|
+
},
|
169
|
+
"roles": {
|
170
|
+
"id": "integer",
|
171
|
+
"name": "string",
|
172
|
+
"created_at": "datetime",
|
173
|
+
"updated_at": "datetime",
|
174
|
+
"lock_version": "integer",
|
175
|
+
"associations": {
|
176
|
+
"has_many": [
|
177
|
+
"role_users",
|
178
|
+
"users"
|
179
|
+
],
|
180
|
+
"belongs_to": []
|
181
|
+
},
|
182
|
+
"methods": null
|
183
|
+
}
|
184
|
+
}
|
185
|
+
```
|
186
|
+
|
187
|
+
The *methods* key will list the **custom actions** that can be used in addition to normal CRUD operations, these can be bulk actions and anything that can serve a purpose, usually to simplify the interaction between client and server (i.e. getting in one request the result of a complex computations which usually would be sorted out using more requests). Later on this topic.
|
188
|
+
|
53
189
|
## Testing
|
54
190
|
|
55
|
-
If you want to manually test the API using [Insomnia](https://insomnia.rest/)
|
191
|
+
If you want to manually test the API using [Insomnia](https://insomnia.rest/) you can find the chained request in Insomnia v4 json format inside the **test/insomnia** folder.
|
56
192
|
In the next few days, I'll publish also the rspec tests.
|
57
193
|
|
194
|
+
## TODO
|
195
|
+
|
196
|
+
* Integrate a settings gem
|
197
|
+
* Add DSL for users and roles
|
198
|
+
|
58
199
|
## References
|
59
200
|
THanks to all these people for ideas:
|
60
201
|
|
@@ -1,21 +1,15 @@
|
|
1
1
|
class Api::V2::ApplicationController < ActionController::API
|
2
|
+
# For the DSL part
|
2
3
|
include ActiveHashRelation
|
3
|
-
|
4
|
-
before_action :authenticate_request
|
5
|
-
before_action :extract_model
|
6
|
-
before_action :find_record, only: [ :show, :destroy, :update ]
|
7
|
-
|
8
|
-
attr_accessor :current_user
|
9
|
-
|
10
4
|
# Actions will be authorized directly in the action
|
11
5
|
include CanCan::ControllerAdditions
|
12
|
-
|
13
6
|
include ::ApiExceptionManagement
|
7
|
+
|
8
|
+
attr_accessor :current_user
|
14
9
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
end
|
10
|
+
before_action :authenticate_request
|
11
|
+
before_action :extract_model
|
12
|
+
before_action :find_record, only: [ :show, :destroy, :update ]
|
19
13
|
|
20
14
|
# GET :controller/
|
21
15
|
def index
|
@@ -109,8 +103,11 @@ class Api::V2::ApplicationController < ActionController::API
|
|
109
103
|
# or
|
110
104
|
# [GET|PUT|POST|DELETE] :controller/:id?do=:custom_action
|
111
105
|
unless params[:do].blank?
|
112
|
-
|
113
|
-
|
106
|
+
# Poor man's solution to avoid the possibility to
|
107
|
+
# call an unwanted method in the AR Model.
|
108
|
+
resource = "custom_action_#{params[:do]}"
|
109
|
+
raise NoMethodError unless @model.respond_to?(resource)
|
110
|
+
return true, MultiJson.dump(params[:id].blank? ? @model.send(resource, params) : @model.send(resource, params[:id].to_i, params))
|
114
111
|
end
|
115
112
|
# if it's here there is no custom action in the request querystring
|
116
113
|
return false
|
@@ -119,7 +116,8 @@ class Api::V2::ApplicationController < ActionController::API
|
|
119
116
|
def authenticate_request
|
120
117
|
@current_user = AuthorizeApiRequest.call(request.headers).result
|
121
118
|
return unauthenticated! unless @current_user
|
122
|
-
|
119
|
+
current_user = @current_user
|
120
|
+
params[:current_user_id] = @current_user.id
|
123
121
|
# Now every time the user fires off a successful GET request,
|
124
122
|
# a new token is generated and passed to them, and the clock resets.
|
125
123
|
response.headers['Token'] = JsonWebToken.encode(user_id: current_user.id)
|
@@ -148,4 +146,9 @@ class Api::V2::ApplicationController < ActionController::API
|
|
148
146
|
# Only ActiveRecords can have this model caputed
|
149
147
|
return not_found! if (!@model.new.is_a? ActiveRecord::Base rescue false)
|
150
148
|
end
|
149
|
+
|
150
|
+
# Nullifying strong params for API
|
151
|
+
def params
|
152
|
+
request.parameters
|
153
|
+
end
|
151
154
|
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module SchemaBasedApiRole
|
2
|
+
extend ActiveSupport::Concern
|
3
|
+
|
4
|
+
included do
|
5
|
+
## DSL (AKA what to show in the returned JSON)
|
6
|
+
# Use @@json_attrs to drive json rendering for
|
7
|
+
# API model responses (index, show and update ones).
|
8
|
+
# For reference:
|
9
|
+
# https://api.rubyonrails.org/classes/ActiveModel/Serializers/JSON.html
|
10
|
+
# The object passed accepts only these keys:
|
11
|
+
# - only: list [] of model field names in symbol notation to be shown in JSON
|
12
|
+
# serialization.
|
13
|
+
# - except: exclude these fields from the JSON serialization, is a list []
|
14
|
+
# of model field names in symbol notation.
|
15
|
+
# - methods: include the result of some methods defined in the model (virtual
|
16
|
+
# fields).
|
17
|
+
# - include: include associated models, it's a list [] of hashes {} which also
|
18
|
+
# accepts the [:only, :except, :methods, :include] keys.
|
19
|
+
cattr_accessor :json_attrs
|
20
|
+
@@json_attrs = {
|
21
|
+
except: [:lock_version],
|
22
|
+
include: [:users]
|
23
|
+
}
|
24
|
+
|
25
|
+
## CUSTOM ACTIONS
|
26
|
+
# Here you can add custom actions to be called from the API
|
27
|
+
# The action must return an serializable (JSON) object.
|
28
|
+
|
29
|
+
# Here you can find an example *without* ID, in the API it could be called like this:
|
30
|
+
# GET /api/v2/:controller?do=test¶meter=sample
|
31
|
+
# def self.custom_action_test params=nil
|
32
|
+
# { test: [ :first, :second, :third ], params: params}
|
33
|
+
# end
|
34
|
+
|
35
|
+
# Here you can find an example *with* ID, in the API it could be called like this:
|
36
|
+
# GET /api/v2/:controller/:id?do=test_with_id¶meter=sample
|
37
|
+
# def self.custom_action_test_with_id id=nil, params=nil
|
38
|
+
# { test: [ :first, :second, :third ], id: id, params: params}
|
39
|
+
# end
|
40
|
+
end
|
41
|
+
end
|
@@ -5,5 +5,41 @@ module SchemaBasedApiUser
|
|
5
5
|
def authenticate password
|
6
6
|
self&.valid_password?(password) ? self : nil
|
7
7
|
end
|
8
|
+
|
9
|
+
## DSL (AKA what to show in the returned JSON)
|
10
|
+
# Use @@json_attrs to drive json rendering for
|
11
|
+
# API model responses (index, show and update ones).
|
12
|
+
# For reference:
|
13
|
+
# https://api.rubyonrails.org/classes/ActiveModel/Serializers/JSON.html
|
14
|
+
# The object passed accepts only these keys:
|
15
|
+
# - only: list [] of model field names in symbol notation to be shown in JSON
|
16
|
+
# serialization.
|
17
|
+
# - except: exclude these fields from the JSON serialization, is a list []
|
18
|
+
# of model field names in symbol notation.
|
19
|
+
# - methods: include the result of some methods defined in the model (virtual
|
20
|
+
# fields).
|
21
|
+
# - include: include associated models, it's a list [] of hashes {} which also
|
22
|
+
# accepts the [:only, :except, :methods, :include] keys.
|
23
|
+
cattr_accessor :json_attrs
|
24
|
+
@@json_attrs = {
|
25
|
+
except: [:lock_version],
|
26
|
+
include: [:roles]
|
27
|
+
}
|
28
|
+
|
29
|
+
## CUSTOM ACTIONS
|
30
|
+
# Here you can add custom actions to be called from the API
|
31
|
+
# The action must return an serializable (JSON) object.
|
32
|
+
|
33
|
+
# Here you can find an example *without* ID, in the API it could be called like this:
|
34
|
+
# GET /api/v2/:controller?do=test¶meter=sample
|
35
|
+
# def self.custom_action_test params=nil
|
36
|
+
# { test: [ :first, :second, :third ], params: params}
|
37
|
+
# end
|
38
|
+
|
39
|
+
# Here you can find an example *with* ID, in the API it could be called like this:
|
40
|
+
# GET /api/v2/:controller/:id?do=test_with_id¶meter=sample
|
41
|
+
# def self.custom_action_test_with_id id=nil, params=nil
|
42
|
+
# { test: [ :first, :second, :third ], id: id, params: params}
|
43
|
+
# end
|
8
44
|
end
|
9
45
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: schema_based_api
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.1.
|
4
|
+
version: 2.1.15
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Gabriele Tassoni
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-04-
|
11
|
+
date: 2020-04-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -179,6 +179,7 @@ files:
|
|
179
179
|
- config/initializers/wrap_parameters.rb
|
180
180
|
- config/routes.rb
|
181
181
|
- lib/concerns/api_exception_management.rb
|
182
|
+
- lib/concerns/schema_based_api_role.rb
|
182
183
|
- lib/concerns/schema_based_api_user.rb
|
183
184
|
- lib/json_web_token.rb
|
184
185
|
- lib/schema_based_api.rb
|