tramway-api 1.0.2.2 → 1.2

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: 408807959271db1306b9048f9aaf1d210ae2f8e1b4d1c364789bb706112aa5a9
4
- data.tar.gz: 3b169249d7fd04aefbb3fb7d777f2a8f6f5a163046978e780001f009fb2df77f
3
+ metadata.gz: 4e41e964d3ade1c230c77385929ee24692558d8cf59571134ccd9da815db2295
4
+ data.tar.gz: 9dc5042350b5a9deb0e2afda435797bec9015743eaa5bb0f184aaaa1ccfcbb21
5
5
  SHA512:
6
- metadata.gz: 7e42a01f15193f7a3617f3c4af061fc7959cdc319ab4e0cc81f97ef5fd6322c13a31eb4ccc8ffc3f53936fd41cc56360ce1d67ef19ecb0b9b02b2c567e85a638
7
- data.tar.gz: 1b14778d781bb1f0343f7984c426e382066e7e9add7af4d833632259ce96c1fcade765e97a6f4cd9acf147a368a12d9c85cef6f75b7a8cd7dc9c335e4b40a1b8
6
+ metadata.gz: 7627d0d269ff690cddc29a5a78369f968701e3992cb53e0b1f7c5b61d7e9e2b99e7a5af343dd5266e762bc5aa786ecfb06573128930360279e98f1ed1f7fce7f
7
+ data.tar.gz: ddebade027114042a877ff9a19a531bedd8787317bd8b3065b630a7fff9ba062262bfc719d94dc81bd514b9f4fef6790e46a34566510aa424db94421de362712
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,9 +23,9 @@ 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
 
@@ -34,12 +34,20 @@ module Tramway
34
34
  if Tramway::Api.user_based_model.respond_to? :from_token_request
35
35
  Tramway::Api.user_based_model.active.from_token_request request
36
36
  else
37
- params[:auth] && Tramway::Api.user_based_model.active.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
@@ -22,7 +23,7 @@ module Tramway::Api::V1
22
23
  end
23
24
 
24
25
  def update
25
- record_form = form_class.new model_class.find params[:id]
26
+ record_form = form_class.new model_class.active.find params[:id]
26
27
  if record_form.submit params[:data][:attributes]
27
28
  render json: record_form.model,
28
29
  serializer: serializer_class,
@@ -32,6 +33,13 @@ module Tramway::Api::V1
32
33
  end
33
34
  end
34
35
 
36
+ def show
37
+ record = model_class.active.find params[:id]
38
+ render json: record,
39
+ serializer: serializer_class,
40
+ status: :ok
41
+ end
42
+
35
43
  private
36
44
 
37
45
  def check_available_model_class
@@ -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.2'
3
+ VERSION = '1.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.2
4
+ version: '1.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-06-02 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