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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8471d0089a6858a8fc54493fcd2e1d10ec3cf82d021ed347645dc999c1e0ec5e
4
- data.tar.gz: cfac408c1ffdff059610eaa98aa40e093705c74f74953f886abb792f622c2ac4
3
+ metadata.gz: f096bb076a59df02a1c9a51b348a4265b4c4bcc085ee17297873b31cafdfe70a
4
+ data.tar.gz: 27a50ee08425905661fd685c4c5fed9619f2e86ca78aa510a1d18b749694452c
5
5
  SHA512:
6
- metadata.gz: 1695890938108ed0b87b9334c945bd6277a9784934ef6c9bab9a5def8d82aac2a49fb1f2cd649f67aeec2425b226a24767413418999ab745a3d1e822e8222646
7
- data.tar.gz: ad5835d2ff8a1fcd14cb4b4e49400401cab586283d874c6fe83c54c1c0a40f68574aa089bef8116041a3287d1f65d9da873a08e641f4fd414bf1df66f3404065
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/) I will publish in the repository the export for the chained requests I'm using.
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
- # Nullifying strong params for API
16
- def params
17
- request.parameters
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
- raise NoMethodError unless @model.respond_to?(params[:do])
113
- return true, MultiJson.dump(params[:id].blank? ? @model.send(params[:do], params) : @model.send(params[:do], params[:id].to_i, params))
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
- params[:current_user] = current_user = @current_user
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
@@ -9,5 +9,4 @@ class Api::V2::AuthenticationController < ActionController::API
9
9
  head :ok
10
10
  end
11
11
  end
12
-
13
12
  end
@@ -1,7 +1,9 @@
1
1
  require 'concerns/schema_based_api_user'
2
+ require 'concerns/schema_based_api_role'
2
3
 
3
4
  Rails.application.configure do
4
5
  config.after_initialize do
5
6
  User.send(:include, SchemaBasedApiUser)
7
+ Role.send(:include, SchemaBasedApiRole)
6
8
  end
7
9
  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&parameter=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&parameter=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&parameter=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&parameter=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
@@ -1,3 +1,3 @@
1
1
  module SchemaBasedApi
2
- VERSION = '2.1.14'
2
+ VERSION = '2.1.15'
3
3
  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.14
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-18 00:00:00.000000000 Z
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