model_driven_api 3.0.9 → 3.0.11
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +1 -490
- data/app/commands/authorize_api_request.rb +2 -2
- data/app/controllers/api/v2/application_controller.rb +12 -3
- data/lib/model_driven_api/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 77497a359b4d336baf533725376f1122b376d2a97b05bcf3e8f7e203d4963780
|
4
|
+
data.tar.gz: 8a799a1009588c764b5dbec7ea1b1cfd4c7f8c1d4ac4554bccb22b35877531a1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: abd1210273d04c393f6a6b46e593f52da8dba14bce52dd3351b3cc3c5575c9c931826538035566a186c9d4af0596cea2f54aa1ce6d685dc7136feb933f82ce1a
|
7
|
+
data.tar.gz: 6b822a4b6da0f2718a7837124e654fc6c5ba7223798171621d669de2fd54aa4e9a933a99d5c5cf7c5b0b5641fc2eab6f5b729bef7d4d7ae8a4531fda20e3aa87
|
data/README.md
CHANGED
@@ -1,490 +1 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
## Goals
|
4
|
-
|
5
|
-
* To have a comprehensive and meaningful Model driven API right out of the box by just creating migrations in your rails app or engine. With all the CRUD operations in place out of the box and easily expandable with custom actions if needed.
|
6
|
-
* To have a plain REST implementation which adapts the returned JSON to the specific needs of the client, **without the need to change backend code**, this may overcome the biggest disadvantage of REST vs GraphQL = client driver presentation.
|
7
|
-
|
8
|
-
## TL;DR 5-10 minutes adoption
|
9
|
-
|
10
|
-
1. Add this line to your application's Gemfile or as a dependency for your engine gem: ```gem 'model_driven_api'```
|
11
|
-
2. Run from the shell: ```bundle```
|
12
|
-
3. Add needed models, like:
|
13
|
-
```bash
|
14
|
-
rails g migration AddLocation name:string:index description:text:index
|
15
|
-
rails g migration AddProduct name:string:index code:string:uniq location:references
|
16
|
-
# Any other migration(s) you need...
|
17
|
-
```
|
18
|
-
4. Run the migrations: ```rails db:migrate```
|
19
|
-
5. Bring up your dev server: ```rails s```
|
20
|
-
6. Use **[Insomnia](https://github.com/Kong/insomnia)** rest client to try the endpoints by importing [the API v2 tests](test/insomnia/ApiV2Tests.json) and editing the environment variables as needed.
|
21
|
-
|
22
|
-
This will setup a *User* model, *Role* model, *Permissions* model and the HABTM table between these + any added model you created at the step 3.
|
23
|
-
|
24
|
-
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.
|
25
|
-
|
26
|
-
|
27
|
-
I've always been interested in effortless, no-fuss, conventions' based development, DRYness, and pragmatic programming, I've always thought that at this point of the technology evolution, we need not to configure too much to have our software run, having the software adapt to data layers and from there building up APIs, visualizations, etc. in an automatic way. This is a first step to have a schema driven API or better model drive, based on the underlining database, the data it has to serve and some sane dafults, or conventions. This effort also gives, thanks to meta programming, an insight on the actual schema, via the info API, the translations available and the DSL which can change the way the data is presented, leading to a strong base for automatica built of UIs consuming the API (react, vue, angular based PWAs, maybe! ;-) ).
|
28
|
-
|
29
|
-
Doing this means also narrowing a bit the scope of the tools, taking decisions, at least for the first implementations and versions of this engine, so, this works well if the data is relational, this is a prerequisite (postgres, mysql, mssql, etc.).
|
30
|
-
|
31
|
-
## REST Enhanced
|
32
|
-
|
33
|
-
Thanks to the inclusion of [Ransack](https://github.com/activerecord-hackery/ransack/wiki) and [ActiveModel::Serializer](https://api.rubyonrails.org/classes/ActiveModel/Serializers/JSON.html), by just adding the querystring keys **q** and **a**, you can create complex queries (q) to obtain just the records you need, which present in the returnd JSON just the attributes (a) needed.
|
34
|
-
By combining the two keys, you can obtain just the data you want.
|
35
|
-
|
36
|
-
The *json_attrs* or *a* query string passed accepts these keys (Please see [ActiveModel::Serializer](https://api.rubyonrails.org/classes/ActiveModel/Serializers/JSON.html) for reference):
|
37
|
-
- only: list [] of model fields to be shown in JSON serialization
|
38
|
-
- except: exclude these fields from the JSON serialization, this is a list []
|
39
|
-
- methods: include the result of some methods defined in the model, this is a list []
|
40
|
-
- include: include associated models, it's an object {} which also accepts the keys described in this document (only, except, methods, include)
|
41
|
-
|
42
|
-
### Example
|
43
|
-
|
44
|
-
```
|
45
|
-
{{ base_url }}/{{ controller }}?a[only][]=locked&a[only][]=username&a[methods][]=jwe_data
|
46
|
-
```
|
47
|
-
|
48
|
-
Is translated to:
|
49
|
-
|
50
|
-
```
|
51
|
-
{a: {only: ["locked", "username"], methods: ["jwe_data"]}}
|
52
|
-
```
|
53
|
-
|
54
|
-
Which tells the API controller just to return this optimized serialization:
|
55
|
-
|
56
|
-
```
|
57
|
-
[
|
58
|
-
{
|
59
|
-
"username": "Administrator",
|
60
|
-
"locked": false,
|
61
|
-
"jwe_data": "eyJhbGciOiJkaXIiLCJlbmMiOiJSMTI4R0NNIn0..yz0tnC6y3BzgoOsO.BjHb9CRIb0vrv7nnEx54Ac8-cATPJ9sTlQSSxRbTmtcPHc5KhvtyE_hyBRnIcK92bzUBRwdy6ASB2XJVy1VfWxAmO8E.4tOzJlfuXi-shaRhDSkOyg"
|
62
|
-
}
|
63
|
-
]
|
64
|
-
```
|
65
|
-
|
66
|
-
By combinig with Ransack's **q** query string key (please read [Ransack](https://github.com/activerecord-hackery/ransack/wiki) documentation to discover all the possible and complex searches you can make), you can obtain right what you want:
|
67
|
-
|
68
|
-
```
|
69
|
-
{{ base_url }}/{{ controller }}?a[only][]=locked&a[only][]=username&a[methods][]=jwe_data&q[email_cont][]=adm
|
70
|
-
```
|
71
|
-
|
72
|
-
Which translates to:
|
73
|
-
|
74
|
-
```
|
75
|
-
{a: {only: ["locked", "username"], methods: ["jwe_data"]}, q: { email_cont: ["adm"]}}
|
76
|
-
```
|
77
|
-
|
78
|
-
For bigger searches, which may over crowd the querystring length, you can always use the default [Search](#Search) POST endpoint.
|
79
|
-
|
80
|
-
## v2?
|
81
|
-
|
82
|
-
Yes, this is the second version of such an effort and you can note it from the api calls, which are all under the ```/api/v2``` namespace the [/api/v1](https://github.com/gabrieletassoni/thecore_api) one, was were it all started, many ideas are ported from there, such as the generation of the automatic model based crud actions, as well as custom actions definitions and all the things that make also this gem useful for my daily job were already in place, but it was too coupled with [thecore](https://github.com/gabrieletassoni/thecore)'s [rails_admin](https://github.com/sferik/rails_admin) UI, making it impossible to create a complete UI-less, API only application, out of the box and directly based of the DB schema, with all the bells and whistles I needed (mainly self adapting, data and schema driven API functionalities).
|
83
|
-
So it all began again, making a better thecore_api gem into this model_driven_api gem, more polished, more functional and self contained.
|
84
|
-
|
85
|
-
## What has changed
|
86
|
-
|
87
|
-
* Replace v1 with **v2** in the url
|
88
|
-
* **Custom actions** defined in model's concerns now are triggered by a do querystring, for example: ```/api/v2/:model?do=custom_action``` or ```/api/v2/:model/:id?do=custom_action```
|
89
|
-
* Searches using Ransack can be done either by GET or POST, but POST is preferred.
|
90
|
-
|
91
|
-
## Standards Used
|
92
|
-
|
93
|
-
* [JWT](https://github.com/jwt/ruby-jwt) for authentication.
|
94
|
-
* [CanCanCan](https://github.com/CanCanCommunity/cancancan) for authorization.
|
95
|
-
* [Ransack](https://github.com/activerecord-hackery/ransack) query engine for complex searches going beyond CRUD's listing scope.
|
96
|
-
* Catch all routing rule to automatically add basic crud operations to any AR model in the app.
|
97
|
-
|
98
|
-
## API
|
99
|
-
|
100
|
-
All the **Models** of *WebApp* follow the same endpoint specification as a standard. The only deviations to the normally expected **CRUD** endpoints are for the **authenticate** controller and the **info** controller described below. This is due to their specific nature, which is not the common **CRUD** interaction, but to provide specifically a way to retrieve the **[JWT](https://jwt.io/)** or retrieve low level information on the **structure** of the API.
|
101
|
-
|
102
|
-
### Return Values
|
103
|
-
|
104
|
-
The expected return values are:
|
105
|
-
|
106
|
-
- **200** *success*: the request performed the expected action.
|
107
|
-
- **401** *unauthenticated*: username, password or token are invalid.
|
108
|
-
- **403** *unauthorized*: the user performing the action is authenticated, but doesn't have the permission to perform it.
|
109
|
-
- **404** *not found*: the record or custom action is not present in the
|
110
|
-
- **422** *invalid*: the specified body of the request has not passed the validations for the model.
|
111
|
-
- **500** *errors on the platform*: usually this means you found a bug in the code.
|
112
|
-
|
113
|
-
### Getting the Token
|
114
|
-
|
115
|
-
The first thing that must be done by the client is to get a Token using the credentials:
|
116
|
-
|
117
|
-
```bash
|
118
|
-
POST http://localhost:3000/api/v2/authenticate
|
119
|
-
```
|
120
|
-
|
121
|
-
with a POST body like the one below:
|
122
|
-
|
123
|
-
```json
|
124
|
-
{
|
125
|
-
"auth": {
|
126
|
-
"email": "<REPLACE>",
|
127
|
-
"password": "<REPLACE>"
|
128
|
-
}
|
129
|
-
}
|
130
|
-
```
|
131
|
-
|
132
|
-
This action will return in the **header** a *Token* you can use for the following requests.
|
133
|
-
Bear in mind that the *Token* will expire within 15 minutes and that at **each successful** request a ***new*** token is returned using the same **header**, so, at each interaction between client server, just making an authenticated and successful 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*.
|
134
|
-
|
135
|
-
Keep in mind that the Token, if decoded, bears the information about the expiration time as part of the payload.
|
136
|
-
|
137
|
-
#### Authenticated Requests
|
138
|
-
|
139
|
-
Once the JWT has been retrieved, the **Authenticated Request**s must use it in a header of Bearer Type like this one:
|
140
|
-
|
141
|
-
```
|
142
|
-
Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJleHAiOjE1OTA3NzQyMzR9.Z-1yECp55VD560UcB7gIhgVWJNjn8HUerG5s4TVSRko
|
143
|
-
```
|
144
|
-
|
145
|
-
#### Token Refresh
|
146
|
-
|
147
|
-
If issued during the token validity period, this will just return a new JWT to be used during following API request.
|
148
|
-
|
149
|
-
```bash
|
150
|
-
:GET http://localhost:3000/api/v2/info/heartbeat
|
151
|
-
```
|
152
|
-
|
153
|
-
### CRUD Actions
|
154
|
-
|
155
|
-
All the interactions with the **Models** are **Authenticated Request** (see below for reference on **Getting the Token**), all the models have the following six **CRUD** actions and the **custom** actions defined for them in addition (see below for reference to custom actions in the **Schema** section of the **Info** controller):
|
156
|
-
|
157
|
-
#### List
|
158
|
-
|
159
|
-
Returns a list of all the records for the models specified by the expected fields as per **DSL** section below.
|
160
|
-
Example request for **users** model:
|
161
|
-
|
162
|
-
```bash
|
163
|
-
GET http://localhost:3000/api/v2/users
|
164
|
-
```
|
165
|
-
|
166
|
-
#### Search
|
167
|
-
|
168
|
-
Returns a list of all the records for the models specified by the expected fields as per **DSL** section below, the returned records are **filtered** by the **search predicates** you can find in the body of the request:
|
169
|
-
Example request for **users** model:
|
170
|
-
|
171
|
-
```bash
|
172
|
-
POST http://localhost:3000/api/v2/users/search
|
173
|
-
```
|
174
|
-
|
175
|
-
Example of the body of the request (*q* means **question**, the *_eq* particle means **equals**, so in this simple example I'm looking for records which have the *id* attribute set to 1, mimicking the **Show** action below):
|
176
|
-
|
177
|
-
```json
|
178
|
-
{
|
179
|
-
"q":{
|
180
|
-
"id_eq": 1
|
181
|
-
}
|
182
|
-
}
|
183
|
-
```
|
184
|
-
|
185
|
-
The complete documentation of the predicates that can be used is provided by the **[Ransack](https://github.com/activerecord-hackery/ransack)** library, the filtering, ordering, grouping options are really infinite.
|
186
|
-
|
187
|
-
#### Create
|
188
|
-
|
189
|
-
Creates a record for the specified **Model**.
|
190
|
-
Validations on the data sent are triggered.
|
191
|
-
Example request for **users** model:
|
192
|
-
|
193
|
-
```bash
|
194
|
-
POST http://localhost:3000/api/v2/users
|
195
|
-
```
|
196
|
-
|
197
|
-
Example of the body of the request:
|
198
|
-
|
199
|
-
```json
|
200
|
-
{
|
201
|
-
"user": {
|
202
|
-
"email": "prova@example.com",
|
203
|
-
"admin": false,
|
204
|
-
"password": "prova63562",
|
205
|
-
"password_confirmation": "prova63562"
|
206
|
-
}
|
207
|
-
}
|
208
|
-
```
|
209
|
-
|
210
|
-
#### Show
|
211
|
-
|
212
|
-
Retrieves a single record as specified by the expected fields as per **DSL** section below.
|
213
|
-
Example request for **users** model (retrieves the record with ID = 1):
|
214
|
-
|
215
|
-
```bash
|
216
|
-
GET http://localhost:3000/api/v2/users/1
|
217
|
-
```
|
218
|
-
|
219
|
-
#### Edit
|
220
|
-
|
221
|
-
Changes the value of one or more attributes for the specified model. In the body of the PUT request you can just use the attributes you want to change, it's **not** necessary to use all the attributes of the record.
|
222
|
-
Example request for **users** model with ID = 1:
|
223
|
-
|
224
|
-
```bash
|
225
|
-
PUT http://localhost:3000/api/v2/users/1
|
226
|
-
```
|
227
|
-
|
228
|
-
Example of the body of the request:
|
229
|
-
|
230
|
-
```json
|
231
|
-
{
|
232
|
-
"user": {
|
233
|
-
"email": "ciao@example.com"
|
234
|
-
}
|
235
|
-
}
|
236
|
-
```
|
237
|
-
|
238
|
-
#### Delete
|
239
|
-
|
240
|
-
Deletes the specified record.
|
241
|
-
Example request for **users** model with ID = 1:
|
242
|
-
|
243
|
-
```bash
|
244
|
-
DELETE http://localhost:3000/api/v2/users/1
|
245
|
-
```
|
246
|
-
|
247
|
-
#### Custom Actions
|
248
|
-
|
249
|
-
Are triggered by a **do** querystring, for example: `GET /api/v2/:model?do=custom_action` or `GET /api/v2/:model/:id?do=custom_action` respectively for a custom action which works on the entire records collection or a custom action which works on the specific record identified by :id.
|
250
|
-
|
251
|
-
### Info API
|
252
|
-
|
253
|
-
The info API **/api/v2/info/** can be used to retrieve general information about the REST API.
|
254
|
-
|
255
|
-
#### Version
|
256
|
-
|
257
|
-
By issuing a GET on this api, you will get a response containing the version of *WebApp*.
|
258
|
-
This is a request which **doesn't require authentication**, it could be used as a checkpoint for consuming the resources exposed by this engine.
|
259
|
-
|
260
|
-
Example:
|
261
|
-
|
262
|
-
```bash
|
263
|
-
GET http://localhost:3000/api/v2/info/version
|
264
|
-
```
|
265
|
-
|
266
|
-
Would produce a response body like this one:
|
267
|
-
|
268
|
-
```json
|
269
|
-
{
|
270
|
-
"version": "2.1.14"
|
271
|
-
}
|
272
|
-
```
|
273
|
-
|
274
|
-
#### Roles
|
275
|
-
|
276
|
-
**Authenticated Request** by issuing a GET request to */api/v2/info/roles*:
|
277
|
-
|
278
|
-
```bash
|
279
|
-
GET http://localhost:3000/api/v2/info/roles
|
280
|
-
```
|
281
|
-
|
282
|
-
Something like this can be retrieved:
|
283
|
-
|
284
|
-
```json
|
285
|
-
[
|
286
|
-
{
|
287
|
-
"id": 1,
|
288
|
-
"name": "role-1586521657646",
|
289
|
-
"created_at": "2020-04-10T12:27:38.061Z",
|
290
|
-
"updated_at": "2020-04-10T12:27:38.061Z",
|
291
|
-
"lock_version": 0
|
292
|
-
},
|
293
|
-
{
|
294
|
-
"id": 2,
|
295
|
-
"name": "role-1586522353509",
|
296
|
-
"created_at": "2020-04-10T12:39:14.276Z",
|
297
|
-
"updated_at": "2020-04-10T12:39:14.276Z",
|
298
|
-
"lock_version": 0
|
299
|
-
}
|
300
|
-
]
|
301
|
-
```
|
302
|
-
|
303
|
-
#### Schema
|
304
|
-
|
305
|
-
**Authenticated Request** This action will send back the *authorized* models accessible by the **User** owner of the *Token* 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.
|
306
|
-
|
307
|
-
By issuing this GET request:
|
308
|
-
|
309
|
-
```bash
|
310
|
-
GET http://localhost:3000/api/v2/info/schema
|
311
|
-
```
|
312
|
-
|
313
|
-
You will get something like:
|
314
|
-
|
315
|
-
```json
|
316
|
-
{
|
317
|
-
"users": {
|
318
|
-
"id": "integer",
|
319
|
-
"email": "string",
|
320
|
-
"encrypted_password": "string",
|
321
|
-
"admin": "boolean",
|
322
|
-
"lock_version": "integer",
|
323
|
-
"associations": {
|
324
|
-
"has_many": [
|
325
|
-
"role_users",
|
326
|
-
"roles"
|
327
|
-
],
|
328
|
-
"belongs_to": []
|
329
|
-
},
|
330
|
-
"methods": null
|
331
|
-
},
|
332
|
-
"role_users": {
|
333
|
-
"id": "integer",
|
334
|
-
"created_at": "datetime",
|
335
|
-
"updated_at": "datetime",
|
336
|
-
"associations": {
|
337
|
-
"has_many": [],
|
338
|
-
"belongs_to": [
|
339
|
-
"user",
|
340
|
-
"role"
|
341
|
-
]
|
342
|
-
},
|
343
|
-
"methods": null
|
344
|
-
},
|
345
|
-
"roles": {
|
346
|
-
"id": "integer",
|
347
|
-
"name": "string",
|
348
|
-
"created_at": "datetime",
|
349
|
-
"updated_at": "datetime",
|
350
|
-
"lock_version": "integer",
|
351
|
-
"associations": {
|
352
|
-
"has_many": [
|
353
|
-
"role_users",
|
354
|
-
"users"
|
355
|
-
],
|
356
|
-
"belongs_to": []
|
357
|
-
},
|
358
|
-
"methods": null
|
359
|
-
}
|
360
|
-
}
|
361
|
-
```
|
362
|
-
|
363
|
-
The ***associations***: key lists the relations between each model and the associated models, be them a n:1 (belongs_to) or a n:m (has_many) one.
|
364
|
-
The ***methods*** key will list the **custom actions** that can be used in addition to normal CRUD operations, these are usually **bulk actions** or any computation that can serve a specific purpose outside the basic CRUD scope used 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).
|
365
|
-
|
366
|
-
#### DSL
|
367
|
-
|
368
|
-
**Authenticated Request** This action will send back, for each model, which are the fields to be expected in the returning JSON of each request which has a returning value.
|
369
|
-
This information can complement the **Schema** action above output by giving information on what to expect as returned fields, associations and aggregates (methods) from each **READ** action of the **CRUD**. It can be used both to *validate* the returned values of a **LIST** or a **SHOW** or to *drive* UI generation of the clients.
|
370
|
-
|
371
|
-
By issuing this GET request:
|
372
|
-
|
373
|
-
```bash
|
374
|
-
GET http://localhost:3000/api/v2/info/dsl
|
375
|
-
```
|
376
|
-
|
377
|
-
You will get something like:
|
378
|
-
|
379
|
-
```json
|
380
|
-
{
|
381
|
-
"users": {
|
382
|
-
"except": [
|
383
|
-
"lock_version",
|
384
|
-
"created_at",
|
385
|
-
"updated_at"
|
386
|
-
],
|
387
|
-
"include": [
|
388
|
-
"roles"
|
389
|
-
]
|
390
|
-
},
|
391
|
-
"roles": {
|
392
|
-
"except": [
|
393
|
-
"lock_version",
|
394
|
-
"created_at",
|
395
|
-
"updated_at"
|
396
|
-
],
|
397
|
-
"include": [
|
398
|
-
{
|
399
|
-
"users": {
|
400
|
-
"only": [
|
401
|
-
"id"
|
402
|
-
]
|
403
|
-
}
|
404
|
-
}
|
405
|
-
]
|
406
|
-
},
|
407
|
-
"role_users": null
|
408
|
-
}
|
409
|
-
```
|
410
|
-
|
411
|
-
#### Translations
|
412
|
-
|
413
|
-
**Authenticated Request** This action will send back, all the **translations** of *model* names, *attribute* names and any other translation defined in the backend.
|
414
|
-
This can be used by the UI clients to be aligned with the translations found in the Backend.
|
415
|
-
The locale for which the translation is requested can be specified by the querystring *locale*, it defaults to **it**.
|
416
|
-
For **Model** translations, the ones used more, the key to look for is ***activerecord*** and subkeys **models** and **attributes**.
|
417
|
-
|
418
|
-
By issuing this GET request:
|
419
|
-
|
420
|
-
```bash
|
421
|
-
GET http://localhost:3000/api/v2/info/translations?locale=it
|
422
|
-
```
|
423
|
-
|
424
|
-
You will get smething like (incomplete for briefness):
|
425
|
-
|
426
|
-
```json
|
427
|
-
{
|
428
|
-
"activerecord": {
|
429
|
-
"attributes": {
|
430
|
-
"user": {
|
431
|
-
"confirmation_sent_at": "Conferma inviata a",
|
432
|
-
"confirmation_token": "Token di conferma",
|
433
|
-
"confirmed_at": "Confermato il",
|
434
|
-
"created_at": "Data di Creazione",
|
435
|
-
"current_password": "Password corrente",
|
436
|
-
"current_sign_in_at": "Accesso corrente il",
|
437
|
-
"current_sign_in_ip": "IP accesso corrente",
|
438
|
-
"email": "E-Mail",
|
439
|
-
"encrypted_password": "Password criptata",
|
440
|
-
"failed_attempts": "Tentativi falliti",
|
441
|
-
"last_sign_in_at": "Ultimo accesso il",
|
442
|
-
"last_sign_in_ip": "Ultimo IP di accesso",
|
443
|
-
"locked_at": "Bloccato il",
|
444
|
-
"password": "Password",
|
445
|
-
"password_confirmation": "Conferma Password",
|
446
|
-
"remember_created_at": "Ricordami creato il",
|
447
|
-
"remember_me": "Ricordami",
|
448
|
-
"reset_password_sent_at": "Reset password inviata a",
|
449
|
-
"reset_password_token": "Token di reset password",
|
450
|
-
"sign_in_count": "Numero di accessi",
|
451
|
-
"unconfirmed_email": "Email non confermata",
|
452
|
-
"unlock_token": "Token di sblocco",
|
453
|
-
"updated_at": "Aggiornato il",
|
454
|
-
"username": "Nome Utente",
|
455
|
-
"code": "Codice",
|
456
|
-
"roles": "Ruoli",
|
457
|
-
"admin": "Amministratore?",
|
458
|
-
"locked": "Bloccato?",
|
459
|
-
"third_party": "Ente Terzo?"
|
460
|
-
},
|
461
|
-
"role": {
|
462
|
-
"users": "Utenti",
|
463
|
-
"name": "Nome",
|
464
|
-
"permissions": "Permessi"
|
465
|
-
},
|
466
|
-
"permission": {
|
467
|
-
"predicate": "Predicato",
|
468
|
-
"action": "Azione",
|
469
|
-
"model": "Modello"
|
470
|
-
},
|
471
|
-
|
472
|
-
[ ... ]
|
473
|
-
}
|
474
|
-
```
|
475
|
-
|
476
|
-
## Testing
|
477
|
-
|
478
|
-
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.
|
479
|
-
Once loaded the tests inside the insomnia application, please right click on the folder, then in the menu select *</> Environment* and change ```base_url```, ```email``` and ```password``` as needed to set them for all the subsequent actions.
|
480
|
-
|
481
|
-
## TODO
|
482
|
-
|
483
|
-
## References
|
484
|
-
Thanks to all these people for ideas:
|
485
|
-
|
486
|
-
* [Billy Cheng](https://medium.com/@billy.sf.cheng/a-rails-6-application-part-1-api-1ee5ccf7ed01) For a way to have a nice and clean implementation of the JWT on top of Devise.
|
487
|
-
* [Daniel](https://medium.com/@tdaniel/passing-refreshed-jwts-from-rails-api-using-headers-859f1cfe88e9) For a smart way to manage token expiration.
|
488
|
-
|
489
|
-
## License
|
490
|
-
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
1
|
+
This is part of Thecore framework: https://github.com/gabrieletassoni/thecore/tree/release/3
|
@@ -129,9 +129,12 @@ class Api::V2::ApplicationController < ActionController::API
|
|
129
129
|
unless params[:do].blank?
|
130
130
|
# Poor man's solution to avoid the possibility to
|
131
131
|
# call an unwanted method in the AR Model.
|
132
|
-
|
132
|
+
custom_action, token = params[:do].split("-")
|
133
|
+
resource = "custom_action_#{custom_action}"
|
133
134
|
raise NoMethodError unless @model.respond_to?(resource)
|
134
135
|
# puts json_attrs
|
136
|
+
params[:request_url] = request.url
|
137
|
+
params[:token] = token.presence || bearer_token
|
135
138
|
body, status = @model.send(resource, params)
|
136
139
|
return true, body.to_json(json_attrs), status
|
137
140
|
end
|
@@ -139,6 +142,12 @@ class Api::V2::ApplicationController < ActionController::API
|
|
139
142
|
return false
|
140
143
|
end
|
141
144
|
|
145
|
+
def bearer_token
|
146
|
+
pattern = /^Bearer /
|
147
|
+
header = request.headers['Authorization']
|
148
|
+
header.gsub(pattern, '') if header && header.match(pattern)
|
149
|
+
end
|
150
|
+
|
142
151
|
def class_exists?(class_name)
|
143
152
|
klass = Module.const_get(class_name)
|
144
153
|
return klass.is_a?(Class)
|
@@ -150,10 +159,10 @@ class Api::V2::ApplicationController < ActionController::API
|
|
150
159
|
@current_user = nil
|
151
160
|
Settings.ns(:security).allowed_authorization_headers.split(",").each do |header|
|
152
161
|
# puts "Found header #{header}: #{request.headers[header]}"
|
153
|
-
check_authorization("Authorize#{header}".constantize.call(request
|
162
|
+
check_authorization("Authorize#{header}".constantize.call(request))
|
154
163
|
end
|
155
164
|
|
156
|
-
check_authorization AuthorizeApiRequest.call(request
|
165
|
+
check_authorization AuthorizeApiRequest.call(request) unless @current_user
|
157
166
|
return unauthenticated!(OpenStruct.new({message: @auth_errors})) unless @current_user
|
158
167
|
|
159
168
|
current_user = @current_user
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: model_driven_api
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.0.
|
4
|
+
version: 3.0.11
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Gabriele Tassoni
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-09-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: thecore_backend_commons
|