tramway-api 1.0.2.1 → 1.1.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +272 -11
- data/app/controllers/tramway/api/application_controller.rb +14 -6
- data/app/controllers/tramway/api/v1/records_controller.rb +1 -0
- data/app/controllers/tramway/api/v1/users_controller.rb +8 -1
- data/lib/tramway/api.rb +11 -3
- data/lib/tramway/api/version.rb +1 -1
- metadata +3 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9e7b86a5c04f4c4eb43219e86a6317ca0eac3b8b47088dc25b9e0d4ea9de0703
|
4
|
+
data.tar.gz: 745e03b27aa436a26467efa95f2e2f2afa6db7e8483ed7e824078462f1b29a77
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9868cc378cdea23161d5cc078e8542e6a9bf98e4cc7a6b3d8395242cdeb350746a3ec6c56cb1794e50f6ca219e77a2c0d891d0f69c8922bd329c11d673b0c47d
|
7
|
+
data.tar.gz: d05ae85c794ee056694d0fd810a41f5f9c41672e433645b3bac485f222d12bd2585ecbd3d3729be028a4b755b06115e9ac438f8031c564040f36ac6c7ce23905
|
data/README.md
CHANGED
@@ -1,26 +1,287 @@
|
|
1
1
|
# Tramway::Api
|
2
|
-
|
2
|
+
|
3
|
+
## English Readme
|
4
|
+
|
5
|
+
coming soon...
|
6
|
+
|
7
|
+
## Russian Readme
|
8
|
+
|
9
|
+
Простой в использовании, легко устанавливаемый и плохо кастомизируемый Rails-engine с готовым CRUD через API.
|
10
|
+
|
11
|
+
Принцип работы. В приложении заранее указывается для каких моделей создаётся API CRUD. Идея проекта - возможность быстрой выкатки API, с возможностью в последствии избавиться от Tramway API, когда ваш проект становится сложнее.
|
12
|
+
|
13
|
+
Гем НЕ манкипатчит стандартные классы и поведение Rails! Именно по этой причине было решено реализовать как Rails-engine, который в последствии можно просто и легко удалить из проекта.
|
14
|
+
|
15
|
+
Фичи:
|
16
|
+
|
17
|
+
* готовый CRUD для определённых разработчиком моделей
|
18
|
+
* сохранение истории изменений записей (используется гем `audited`)
|
19
|
+
* поддержка загрузки файлов (используется `carrierwave`)
|
20
|
+
* аутентификация пользователя через JWT (используется `knock`)
|
21
|
+
* поддержка по умолчанию JSON API спецификации (через гем `active_model_serializers`)
|
22
|
+
* мягкое удаление записей по умолчанию
|
23
|
+
* поддержка коммуникации по уникальному uid объектов, чтобы не публиковать ID в базе
|
24
|
+
|
25
|
+
Ограничения:
|
26
|
+
|
27
|
+
* только с ActiveRecord
|
28
|
+
* только с версией Rails 5.1.* (поддержка 5.2 вскоре будет реализована автором гема, поддержка автором Rails 6 начнётся с версии 6.1. По религиозным автор не использует Rails версий *.0.*
|
29
|
+
* Ruby >= 2.3
|
30
|
+
* все модели, которые будут использованы гемом должны наследоваться от `Tramway::Core::ApplicationRecord`
|
31
|
+
* все модели, которые будут использованы гемом должны иметь атрибут `state`, типа `string` или `text`. Этот атрибут нужен для реализации мягкого удаления. Полное удаление записей из базы не поддерживается
|
32
|
+
* все модели, которые будут использованы гемом должны иметь атрибут
|
33
|
+
|
34
|
+
Недостатки, которые будут вскоре ликвидированы:
|
35
|
+
|
36
|
+
* ядро `tramway-core` подгружает в себя ненужных для API гемов (недостаток не имеет смысла в случае использования вместе с этим решением гема `tramway-admin`):
|
37
|
+
* bootstrap
|
38
|
+
* font_awesome5_rails
|
39
|
+
* haml
|
40
|
+
* требуется ручное добавление требуемых для работы гемов
|
41
|
+
```ruby
|
42
|
+
gem 'active_model_serializers', '0.10.5'
|
43
|
+
gem 'tramway-core'
|
44
|
+
gem 'state_machine', github: 'seuros/state_machine'
|
45
|
+
gem 'knock'
|
46
|
+
```
|
3
47
|
|
4
48
|
## Usage
|
5
|
-
How to use my plugin.
|
6
49
|
|
7
|
-
|
8
|
-
|
50
|
+
```
|
51
|
+
rails new tramway_api_sample
|
52
|
+
```
|
53
|
+
|
54
|
+
*Gemfile*
|
55
|
+
|
56
|
+
```ruby
|
57
|
+
gem 'tramway-api', '>= 1.1.0.1'
|
58
|
+
gem 'active_model_serializers', '0.10.5'
|
59
|
+
gem 'tramway-core'
|
60
|
+
gem 'state_machine', github: 'seuros/state_machine'
|
61
|
+
gem 'knock'
|
62
|
+
```
|
63
|
+
|
64
|
+
Run `bundle install`
|
65
|
+
|
66
|
+
Then generate User (you use another name, it's just an example) model
|
67
|
+
|
68
|
+
```
|
69
|
+
rails g model user email:text password_digest:text username:text state:text uid:text
|
70
|
+
```
|
71
|
+
|
72
|
+
Add generating uid by default
|
73
|
+
|
74
|
+
*db/migrate/create_users_*.rb
|
75
|
+
|
76
|
+
```ruby
|
77
|
+
t.uuid :uid, default: 'uuid_generate_v4()'
|
78
|
+
```
|
79
|
+
|
80
|
+
*app/models/user.rb*
|
81
|
+
|
82
|
+
```ruby
|
83
|
+
class User < Tramway::Core::ApplicationRecord
|
84
|
+
has_secure_password
|
85
|
+
end
|
86
|
+
```
|
87
|
+
|
88
|
+
Create file `config/initializers/tramway.rb`
|
89
|
+
|
90
|
+
```ruby
|
91
|
+
::Tramway::Api.auth_config = { user_model: User, auth_attributes: %i[email username] }
|
92
|
+
::Tramway::Api.set_available_models user: %i[create update]
|
93
|
+
```
|
94
|
+
|
95
|
+
Run `rails g tramway:core:install`
|
96
|
+
|
97
|
+
Run `rails db:create db:migrate`
|
98
|
+
|
99
|
+
*config/routes.rb*
|
100
|
+
|
101
|
+
```ruby
|
102
|
+
Rails.application.routes.draw do
|
103
|
+
mount Tramway::Api::Engine, at: '/api'
|
104
|
+
end
|
105
|
+
```
|
106
|
+
|
107
|
+
Create file *app/forms/user_sign_up_form.rb*
|
108
|
+
|
109
|
+
```ruby
|
110
|
+
class UserSignUpForm < Tramway::Core::ApplicationForm
|
111
|
+
properties :username, :email, :password
|
112
|
+
end
|
113
|
+
```
|
114
|
+
|
115
|
+
**DONE!**
|
116
|
+
|
117
|
+
## Testing
|
118
|
+
|
119
|
+
# Preparation (optional)
|
120
|
+
|
121
|
+
Let's write RSpec test to check what we have:
|
122
|
+
|
123
|
+
*Gemfile*
|
9
124
|
|
10
125
|
```ruby
|
11
|
-
|
126
|
+
group :test do
|
127
|
+
gem 'rspec-rails', '~> 3.5'
|
128
|
+
gem 'rspec-json_expectations', '2.2.0'
|
129
|
+
gem 'factory_bot_rails', '~> 4.0'
|
130
|
+
gem 'json_matchers'
|
131
|
+
gem 'json_api_test_helpers', '1.1.1'
|
132
|
+
end
|
12
133
|
```
|
13
134
|
|
14
|
-
|
15
|
-
|
16
|
-
|
135
|
+
Run `bundle install`
|
136
|
+
|
137
|
+
Run `RAILS_ENV=test rails db:create db:migrate`
|
138
|
+
|
139
|
+
Run `mkdir spec`
|
140
|
+
|
141
|
+
Create file `spec/spec_helper.rb` with:
|
142
|
+
|
143
|
+
```ruby
|
144
|
+
ENV['RAILS_ENV'] ||= 'test'
|
145
|
+
require File.expand_path('../config/environment', __dir__)
|
146
|
+
require 'rspec/rails'
|
147
|
+
require 'rspec/autorun'
|
148
|
+
require 'rspec/expectations'
|
149
|
+
require 'rspec/json_expectations'
|
150
|
+
|
151
|
+
RSpec.configure do |config|
|
152
|
+
config.expect_with :rspec do |expectations|
|
153
|
+
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
154
|
+
end
|
155
|
+
config.mock_with :rspec do |mocks|
|
156
|
+
mocks.verify_partial_doubles = true
|
157
|
+
end
|
158
|
+
end
|
17
159
|
```
|
18
160
|
|
19
|
-
|
20
|
-
|
21
|
-
|
161
|
+
Create file `spec/rails_helper.rb` with:
|
162
|
+
|
163
|
+
```ruby
|
164
|
+
require 'spec_helper'
|
165
|
+
require 'factory_bot'
|
166
|
+
require 'rspec/rails'
|
167
|
+
require 'rspec/json_expectations'
|
168
|
+
require 'json_matchers/rspec'
|
169
|
+
require 'json_api_test_helpers'
|
170
|
+
require 'rake'
|
171
|
+
|
172
|
+
RSpec.configure do |config|
|
173
|
+
config.include FactoryBot::Syntax::Methods
|
174
|
+
config.include RSpec::Rails::RequestExampleGroup, type: :feature
|
175
|
+
config.include JsonApiTestHelpers
|
176
|
+
end
|
22
177
|
```
|
23
178
|
|
179
|
+
# SignUp user
|
180
|
+
|
181
|
+
Create file `spec/tramway_api_spec.rb` with:
|
182
|
+
|
183
|
+
```ruby
|
184
|
+
require 'rails_helper'
|
185
|
+
|
186
|
+
RSpec.describe 'Post creating user', type: :feature do
|
187
|
+
describe 'POST /api/v1/user with model User' do
|
188
|
+
let(:attributes) { attributes_for :user }
|
189
|
+
|
190
|
+
it 'returns created status' do
|
191
|
+
post '/api/v1/user', params: { user: attributes }
|
192
|
+
expect(response.status).to eq 201
|
193
|
+
end
|
194
|
+
|
195
|
+
it 'returns no errors' do
|
196
|
+
post '/api/v1/user', params: { user: attributes }
|
197
|
+
|
198
|
+
expect(json_response[:response]). to be_nil
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
```
|
203
|
+
|
204
|
+
# SignIn User
|
205
|
+
|
206
|
+
```ruby
|
207
|
+
require 'rails_helper'
|
208
|
+
|
209
|
+
RSpec.describe 'Post generate token', type: :feature do
|
210
|
+
describe 'POST /api/v1/user_token' do
|
211
|
+
let(:user) { create :user, password: '123456789' }
|
212
|
+
|
213
|
+
it 'returns created status' do
|
214
|
+
post '/api/v1/user_token', params: { auth: { login: user.email, password: '123456789' } }
|
215
|
+
|
216
|
+
expect(response.status).to eq 201
|
217
|
+
end
|
218
|
+
|
219
|
+
it 'returns token' do
|
220
|
+
post '/api/v1/user_token', params: { auth: { login: user.email, password: '123456789' } }
|
221
|
+
|
222
|
+
expect(json_response[:auth_token].present?).to be_truthy
|
223
|
+
expect(json_response[:user]).to include_json({ email: user.email, uid: user.uid })
|
224
|
+
end
|
225
|
+
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
```
|
230
|
+
|
231
|
+
Run `rspec` to test
|
232
|
+
|
233
|
+
We have route `user`, which create new authenticable user.
|
234
|
+
|
235
|
+
For other models we have route `records`.
|
236
|
+
|
237
|
+
```
|
238
|
+
~: rails routes
|
239
|
+
Prefix Verb URI Pattern Controller#Action
|
240
|
+
tramway_api /api Tramway::Api::Engine
|
241
|
+
|
242
|
+
Routes for Tramway::Api::Engine:
|
243
|
+
v1_user_token POST /v1/user_token(.:format) tramway/api/v1/user_tokens#create
|
244
|
+
v1_user GET /v1/user(.:format) tramway/api/v1/users#show
|
245
|
+
POST /v1/user(.:format) tramway/api/v1/users#create
|
246
|
+
v1_records GET /v1/records(.:format) tramway/api/v1/records#index
|
247
|
+
POST /v1/records(.:format) tramway/api/v1/records#create
|
248
|
+
new_v1_record GET /v1/records/new(.:format) tramway/api/v1/records#new
|
249
|
+
edit_v1_record GET /v1/records/:id/edit(.:format) tramway/api/v1/records#edit
|
250
|
+
v1_record GET /v1/records/:id(.:format) tramway/api/v1/records#show
|
251
|
+
PATCH /v1/records/:id(.:format) tramway/api/v1/records#update
|
252
|
+
PUT /v1/records/:id(.:format) tramway/api/v1/records#update
|
253
|
+
DELETE /v1/records/:id(.:format) tramway/api/v1/records#destroy
|
254
|
+
|
255
|
+
```
|
256
|
+
|
257
|
+
## Methods
|
258
|
+
|
259
|
+
### Initializer methods
|
260
|
+
|
261
|
+
#### auth_config
|
262
|
+
|
263
|
+
Sets default ActiveRecord model, which used as main user to be authenticated with JWT.
|
264
|
+
|
265
|
+
`user_model` - model name
|
266
|
+
`auth_attributes` - array of available attributes used as login.
|
267
|
+
|
268
|
+
this model must have field `password_digest`, because we use `bcrypt` gem for authentication (providing other name of password attribute instead of `password` is coming soon)
|
269
|
+
|
270
|
+
### set_available_models
|
271
|
+
|
272
|
+
Sets ActiveRecord models which will be used in API
|
273
|
+
|
274
|
+
Argument is a hash. Keys are underscored models names, values are arrays of available methods for every model.
|
275
|
+
|
276
|
+
Enabled methods:
|
277
|
+
|
278
|
+
* create
|
279
|
+
* update
|
280
|
+
* show
|
281
|
+
* index
|
282
|
+
* destroy
|
283
|
+
|
284
|
+
|
24
285
|
## Contributing
|
25
286
|
Contribution directions go here.
|
26
287
|
|
@@ -1,7 +1,7 @@
|
|
1
1
|
module Tramway
|
2
2
|
module Api
|
3
3
|
class ApplicationController < ::Tramway::Core::ApplicationController
|
4
|
-
include Knock::Authenticable
|
4
|
+
include ::Knock::Authenticable
|
5
5
|
protect_from_forgery with: :null_session, if: proc { |c| c.request.format == 'application/json' }
|
6
6
|
rescue_from ActiveRecord::RecordNotFound, with: :not_found
|
7
7
|
|
@@ -23,23 +23,31 @@ module Tramway
|
|
23
23
|
|
24
24
|
def auth_token
|
25
25
|
if entity.respond_to? :to_token_payload
|
26
|
-
Knock::AuthToken.new payload: entity.to_token_payload
|
26
|
+
::Knock::AuthToken.new payload: entity.to_token_payload
|
27
27
|
else
|
28
|
-
Knock::AuthToken.new payload: { sub: entity.id }
|
28
|
+
::Knock::AuthToken.new payload: { sub: entity.id }
|
29
29
|
end
|
30
30
|
end
|
31
31
|
|
32
32
|
def entity
|
33
33
|
@entity ||=
|
34
34
|
if Tramway::Api.user_based_model.respond_to? :from_token_request
|
35
|
-
Tramway::Api.user_based_model.from_token_request request
|
35
|
+
Tramway::Api.user_based_model.active.from_token_request request
|
36
36
|
else
|
37
|
-
params[:auth] &&
|
37
|
+
params[:auth] && find_user_by_auth_attributes
|
38
38
|
end
|
39
39
|
end
|
40
40
|
|
41
|
+
def find_user_by_auth_attributes
|
42
|
+
Tramway::Api.auth_attributes.each do |attribute|
|
43
|
+
object = Tramway::Api.user_based_model.active.where.not(attribute => nil).find_by(attribute => auth_params[:login])
|
44
|
+
return object if object
|
45
|
+
end
|
46
|
+
nil
|
47
|
+
end
|
48
|
+
|
41
49
|
def auth_params
|
42
|
-
params[:auth]&.permit
|
50
|
+
params[:auth]&.permit(:login, :password)
|
43
51
|
end
|
44
52
|
end
|
45
53
|
end
|
@@ -2,6 +2,7 @@ module Tramway::Api::V1
|
|
2
2
|
class RecordsController < ::Tramway::Api::V1::ApplicationController
|
3
3
|
before_action :check_available_model_class
|
4
4
|
before_action :check_available_model_action
|
5
|
+
before_action :authenticate_user
|
5
6
|
|
6
7
|
def index
|
7
8
|
records = model_class.active.order(id: :desc).send params[:scope] || :all
|
@@ -5,7 +5,8 @@ class Tramway::Api::V1::UsersController < ::Tramway::Api::V1::ApplicationControl
|
|
5
5
|
include Tramway::ClassNameHelpers
|
6
6
|
|
7
7
|
def create
|
8
|
-
user_form =
|
8
|
+
user_form = sign_up_form_class_name(Tramway::Api.user_based_model).new Tramway::Api.user_based_model.new
|
9
|
+
# Implement JSON API spec here
|
9
10
|
if user_form.submit params[Tramway::Api.user_based_model.name.underscore]
|
10
11
|
token = ::Knock::AuthToken.new(payload: { sub: user_form.model.id }).token
|
11
12
|
# FIXME refactor this bullshit
|
@@ -24,4 +25,10 @@ class Tramway::Api::V1::UsersController < ::Tramway::Api::V1::ApplicationControl
|
|
24
25
|
def show
|
25
26
|
render json: current_user, status: :ok
|
26
27
|
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def sign_up_form_class_name(model_class)
|
32
|
+
form_class_name "#{model_class}SignUp"
|
33
|
+
end
|
27
34
|
end
|
data/lib/tramway/api.rb
CHANGED
@@ -3,12 +3,20 @@ require 'tramway/api/engine'
|
|
3
3
|
module Tramway
|
4
4
|
module Api
|
5
5
|
class << self
|
6
|
+
def auth_config
|
7
|
+
@@auth_config ||= { user_model: ::Tramway::User::User, auth_attributes: :email }
|
8
|
+
end
|
9
|
+
|
10
|
+
def auth_config=(**params)
|
11
|
+
@@auth_config = params
|
12
|
+
end
|
13
|
+
|
6
14
|
def user_based_model
|
7
|
-
@@
|
15
|
+
@@auth_config[:user_model]
|
8
16
|
end
|
9
17
|
|
10
|
-
def
|
11
|
-
@@
|
18
|
+
def auth_attributes
|
19
|
+
@@auth_config[:auth_attributes]
|
12
20
|
end
|
13
21
|
|
14
22
|
def set_available_models(**models)
|
data/lib/tramway/api/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tramway-api
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.2
|
4
|
+
version: 1.1.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Pavel Kalashnikov
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-09-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: knock
|
@@ -99,8 +99,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
99
99
|
- !ruby/object:Gem::Version
|
100
100
|
version: '0'
|
101
101
|
requirements: []
|
102
|
-
|
103
|
-
rubygems_version: 2.7.7
|
102
|
+
rubygems_version: 3.0.3
|
104
103
|
signing_key:
|
105
104
|
specification_version: 4
|
106
105
|
summary: Engine for api
|