schema_based_api 2.1.14 → 2.1.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|