tramway-api 1.0.2.1 → 1.1.0.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9346538207af9ec0049d742fb1eac1454d4df33e07aa3a35a841b143ff2e11df
4
- data.tar.gz: ee30211331f44d21efff5f98a2146c72a1002e8953829c108fe92c86875aec2c
3
+ metadata.gz: 9e7b86a5c04f4c4eb43219e86a6317ca0eac3b8b47088dc25b9e0d4ea9de0703
4
+ data.tar.gz: 745e03b27aa436a26467efa95f2e2f2afa6db7e8483ed7e824078462f1b29a77
5
5
  SHA512:
6
- metadata.gz: 21f1a84fb90e7c2fa039da20bdd48b0d5fa0d1f72a1ce44a8489cbe8fa38662cb2c0ba1f370aa1f85f134b0524ed6faa9236eb90e79ff4258d6944704ca80787
7
- data.tar.gz: 5b0413e29392e52bcb7fc96861c50a9cf170664925e87824960d867f754985fbf03140a0580a0d8806370e1a6c0dfff3c50c772b240f657074f27417d40d5e57
6
+ metadata.gz: 9868cc378cdea23161d5cc078e8542e6a9bf98e4cc7a6b3d8395242cdeb350746a3ec6c56cb1794e50f6ca219e77a2c0d891d0f69c8922bd329c11d673b0c47d
7
+ data.tar.gz: d05ae85c794ee056694d0fd810a41f5f9c41672e433645b3bac485f222d12bd2585ecbd3d3729be028a4b755b06115e9ac438f8031c564040f36ac6c7ce23905
data/README.md CHANGED
@@ -1,26 +1,287 @@
1
1
  # Tramway::Api
2
- Short description and motivation.
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
- ## Installation
8
- Add this line to your application's Gemfile:
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
- gem 'tramway-api'
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
- And then execute:
15
- ```bash
16
- $ bundle
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
- Or install it yourself as:
20
- ```bash
21
- $ gem install tramway-api
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] && Tramway::Api.user_based_model.find_by(email: auth_params[:email])
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 :email, :password
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 = form_class_name(Tramway::Api.user_based_model).new Tramway::Api.user_based_model.new
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
@@ -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
- @@user_based_model ||= ::Tramway::User::User
15
+ @@auth_config[:user_model]
8
16
  end
9
17
 
10
- def user_based_model=(model_class)
11
- @@user_based_model = model_class
18
+ def auth_attributes
19
+ @@auth_config[:auth_attributes]
12
20
  end
13
21
 
14
22
  def set_available_models(**models)
@@ -1,5 +1,5 @@
1
1
  module Tramway
2
2
  module Api
3
- VERSION = '1.0.2.1'
3
+ VERSION = '1.1.0.2'
4
4
  end
5
5
  end
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.1
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-05-29 00:00:00.000000000 Z
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
- rubyforge_project:
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