apicasso 0.4.6 → 0.4.7

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: 285f7a239620529fadc3773625ad7c03228443339ef2c730dccab9bc5974ae8b
4
- data.tar.gz: 90e3ed224db4b180f3498dd9c3465d7c472f6f8c97ed3aeaf808e265a1d99600
3
+ metadata.gz: cf4487870952607136a6a321524093ee1f0e0cd02935710110d4f5e94977b3be
4
+ data.tar.gz: 17633533e5faad631e27b8d0e3d6c169a86163ff5328da5d0d6cab61aabb88f7
5
5
  SHA512:
6
- metadata.gz: 11af9ea91bffca509df5074001e47a6f0f0ae731efeac2f55100bc16126676edfe890bc73c424b2e81d0e925178a37653e0bdaf3a2748a3ef32100883f4eb83f
7
- data.tar.gz: 307de7af6520e44516545fdbe757f0cfa3490053f8e386c257311dd5f267a89b98b45580981140e56bcf11000508c3d44f6720e864589fae8918e16eda4cdddf
6
+ metadata.gz: c036c58d2a4d9f1102cf47ca3d1125bbb5accbb6c69fcb583c82b7891c1801a6d68c55bfad3749e59f870c5608b48a00b1ead41b0c06c8dda60ba101785ea5d1
7
+ data.tar.gz: 22b512d38ff47b026bc0ea18c3173ce96c6d228e7623e5c2d61116d1450885a93bc19e348f97b0b3cc7f40af749d21c4487735d7996fd0d4bd6be2d991eb0f90
data/README.md CHANGED
@@ -1,9 +1,22 @@
1
1
  <img src="https://raw.githubusercontent.com/ErvalhouS/APIcasso/master/APIcasso.png" width="300" /> [![Gem Version](https://badge.fury.io/rb/apicasso.svg)](https://badge.fury.io/rb/apicasso) [![Docs Coverage](https://inch-ci.org/github/autoforce/APIcasso.svg?branch=master)](https://inch-ci.org/github/autoforce/APIcasso.svg?branch=master) [![Maintainability](https://api.codeclimate.com/v1/badges/b58bbd6b9a0376f7cfc8/maintainability)](https://codeclimate.com/github/autoforce/APIcasso/maintainability) [![codecov](https://codecov.io/gh/autoforce/APIcasso/branch/master/graph/badge.svg)](https://codecov.io/gh/autoforce/APIcasso) [![Build Status](https://travis-ci.org/autoforce/APIcasso.svg?branch=master)](https://travis-ci.org/autoforce/APIcasso)
2
2
 
3
- JSON API development can get boring and time consuming. If you think it through, every time you make one you use almost the same route structure, pointing to the same controller actions, with the same ordering, filtering and pagination features.
3
+ Create APIs in a fast and dynamic way, without the need to develop everything from scratch. You just need to create your models and let **APIcasso** do the rest for you. It is the perfect candidate to make your project development go faster or for legacy Rails projects that do not have an API.
4
+
5
+ If you think it through, JSON API development can get boring and time consuming. Every time you use almost the same route structure, pointing to the same controller actions, with the same ordering, filtering and pagination features.
6
+
7
+ **APIcasso** is intended to be used to speed-up development, acting as a full-fledged CRUD JSON API into all your models. It is a route-based abstraction that lets you create, read, list, update or delete any `ActiveRecord` object in your application. This makes it possible to make CRUD-only applications just by creating functional Rails' models. Access to your application's resources is managed by a `.scope` JSON object per API key. It uses that permission scope to restrict and extend access.
8
+
9
+ You can make your own API with only 4 steps:
10
+
11
+ ### Step 1
12
+ Create your models
13
+ ### Step 2
14
+ Insert **APIcasso** engine into your routes
15
+ ### Step 3
16
+ [Create an Apicasso::Key](https://github.com/autoforce/APIcasso#authorization)
17
+ ### Step 4
18
+ Profit! :crown: Consume your REST API
4
19
 
5
- **APIcasso** is intended to be used as a full-fledged CRUD JSON API or as a base controller to speed-up development.
6
- It is a route-based resource abstraction using API key scoping. This makes it possible to make CRUD-only applications just by creating functional Rails' models. It is a perfect candidate for legacy Rails projects that do not have an API. Access to your application's resources is managed by a `.scope` JSON object per API key. It uses that permission scope to restrict and extend access.
7
20
 
8
21
  # Installation
9
22
 
@@ -81,7 +94,7 @@ This way you enjoy all our object finder, authorization and authentication featu
81
94
 
82
95
  > But exposing my models to the internet is permissive as hell! Haven't you thought about security?
83
96
 
84
- _Sure!_ The **APIcasso** suite is exposing your application using authentication through `Authorization: Token` [HTTP header authentication](http://tools.ietf.org/html/draft-hammer-http-token-auth-01). The API key objects are manageable through the `Apicasso::Key` model, which gets setup at install. When a new key is created a `.token` is generated using an [Universally Unique Identifier(RFC 4122)](https://tools.ietf.org/html/rfc4122). A authenticated request looks like this:
97
+ _Sure!_ The **APIcasso** suite is exposing your application using authentication through `Authorization: Token` [HTTP header authentication](http://tools.ietf.org/html/draft-hammer-http-token-auth-01). The API key objects are manageable through the `Apicasso::Key` model, which gets setup at install. When a new key is created a `.token` is generated using an [Universally Unique Identifier(RFC 4122)](https://tools.ietf.org/html/rfc4122). An authenticated request looks like this:
85
98
 
86
99
  ```
87
100
  curl -X GET \
@@ -154,13 +154,18 @@ module Apicasso
154
154
  # @TODO
155
155
  # Remove this in favor of a more controllable aproach of CORS
156
156
  def set_access_control_headers
157
- response.headers['Access-Control-Allow-Origin'] = request.headers["Origin"]
157
+ response.headers['Access-Control-Allow-Origin'] = allow_origin
158
158
  response.headers['Access-Control-Allow-Methods'] = 'POST, GET, PUT, PATCH, DELETE, OPTIONS'
159
159
  response.headers['Access-Control-Allow-Credentials'] = 'true'
160
160
  response.headers['Access-Control-Allow-Headers'] = 'Origin, Content-Type, Accept, Authorization, Token, Auth-Token, Email, X-User-Token, X-User-Email'
161
161
  response.headers['Access-Control-Max-Age'] = '1728000'
162
162
  end
163
163
 
164
+ # A method to allow origin customizing through method overriding
165
+ def allow_origin
166
+ request.headers['Referer'] || request.headers['Origin'] || '*'
167
+ end
168
+
164
169
  # Checks if current request is a CORS preflight check
165
170
  def preflight?
166
171
  request.request_method == 'OPTIONS' &&
@@ -1,3 +1,3 @@
1
1
  module Apicasso
2
- VERSION = '0.4.6'.freeze
2
+ VERSION = '0.4.7'.freeze
3
3
  end
@@ -3,7 +3,7 @@ class UsedModel < ApplicationRecord
3
3
 
4
4
  validates :account_id, presence: true
5
5
  validates :unit_id, presence: true
6
- validates :slug, presence: true, uniqueness: true
6
+ validates :slug, presence: true
7
7
 
8
8
  has_one_attached :file_highlighted
9
9
  has_many_attached :files
@@ -1,6 +1,6 @@
1
1
  require 'faker'
2
2
 
3
- 9.times { UsedModel.create(
3
+ 30.times { UsedModel.create(
4
4
  active: Faker::Boolean.boolean,
5
5
  account_id: Faker::Number.number(1),
6
6
  unit_id: Faker::Number.number(1),
@@ -25,32 +25,4 @@ require 'faker'
25
25
  fuel: Faker::Number.number(1),
26
26
  fuel_text: Faker::Vehicle.fuel_type,
27
27
  shielded: Faker::Boolean.boolean
28
- )}
29
-
30
- 1.times { UsedModel.create(
31
- active: Faker::Boolean.boolean,
32
- account_id: Faker::Number.number(1),
33
- unit_id: Faker::Number.number(1),
34
- slug: 'cr-v',
35
- brand: Faker::Vehicle.make,
36
- name: Faker::Vehicle.model,
37
- model: Faker::Vehicle.model,
38
- version: Faker::Number.decimal(1, 1),
39
- model_year: Faker::Vehicle.year,
40
- production_year: Faker::Vehicle.year,
41
- kind: 'car',
42
- new_vehicle: Faker::Boolean.boolean,
43
- old_price: Faker::Number.decimal(4, 2).to_s,
44
- price_value: Faker::Number.decimal(4, 2),
45
- price: Faker::Number.decimal(4, 2).to_s,
46
- category: Faker::Vehicle.car_type,
47
- transmission: Faker::Vehicle.transmission,
48
- km_value: Faker::Number.number(8),
49
- km: Faker::Number.number(8).to_s,
50
- plate: Faker::Number.number(4),
51
- color: Faker::Vehicle.color,
52
- doors: Faker::Number.number(1),
53
- fuel: Faker::Number.number(1),
54
- fuel_text: Faker::Vehicle.fuel_type,
55
- shielded: Faker::Boolean.boolean
56
28
  )}
@@ -7,15 +7,6 @@ RSpec.describe UsedModel, type: :model do
7
7
  it 'is valid with valid attributes' do
8
8
  expect(build :used_model).to be_valid
9
9
  end
10
- it 'is invalid with slug duplicate' do
11
- used_model = build(:used_model)
12
- used_model.slug = 'duplicate'
13
- used_model.save
14
-
15
- used_model_dup = build(:used_model)
16
- used_model_dup.slug = 'duplicate'
17
- expect { used_model_dup.save! }.to raise_exception ActiveRecord::RecordInvalid
18
- end
19
10
  it 'is invalid without valid slug attribute' do
20
11
  used_model = build(:used_model)
21
12
  used_model.slug = ''
@@ -5,6 +5,7 @@ require 'rails_helper'
5
5
  RSpec.describe 'Used Model requests', type: :request do
6
6
  token = Apicasso::Key.create(scope: { manage: { used_model: true } }).token
7
7
  access_token = { 'AUTHORIZATION' => "Token token=#{token}" }
8
+
8
9
  describe 'GET /api/v1/used_model' do
9
10
  context 'with default pagination' do
10
11
  before(:all) do
@@ -14,8 +15,12 @@ RSpec.describe 'Used Model requests', type: :request do
14
15
  it 'returns status ok' do
15
16
  expect(response).to have_http_status(:ok)
16
17
  end
17
- it 'returns all UsedModel' do
18
- expect(JSON.parse(response.body)['entries'].size).to eq(UsedModel.all.size)
18
+ it 'returns UsedModel records equal to WillPaginate.per_page or all UsedModels' do
19
+ if JSON.parse(response.body)['last_page'] == false
20
+ expect(JSON.parse(response.body)['entries'].size).to eq(WillPaginate.per_page)
21
+ else
22
+ expect(JSON.parse(response.body)['entries'].size).to eq(UsedModel.count)
23
+ end
19
24
  end
20
25
  end
21
26
 
@@ -33,22 +38,26 @@ RSpec.describe 'Used Model requests', type: :request do
33
38
  end
34
39
 
35
40
  context 'with pagination' do
41
+ per_page = (1..UsedModel.count+1).to_a.sample
42
+ page = (1..5).to_a.sample
43
+
36
44
  before(:all) do
37
- get '/api/v1/used_model', params: { per_page: 5, page: 1 }, headers: access_token
45
+ get '/api/v1/used_model', params: { per_page: per_page, page: page }, headers: access_token
38
46
  end
39
47
 
40
48
  it 'returns status ok' do
41
49
  expect(response).to have_http_status(:ok)
42
50
  end
43
51
 
44
- it 'returns five records from UsedModel if not last page' do
45
- expect(JSON.parse(response.body)['entries'].size).to be(5) if JSON.parse(response.body)['last_page'] == false
52
+ it 'returns size of records from UsedModel if not last page' do
53
+ expect(JSON.parse(response.body)['entries'].size).to be(per_page) if JSON.parse(response.body)['last_page'] == false
46
54
  end
47
55
  end
48
56
 
49
57
  context 'by searching' do
58
+ brand_to_search = UsedModel.all.sample.brand
50
59
  before(:all) do
51
- get '/api/v1/used_model', params: { 'q[brand_matches]': 'Audi' }, headers: access_token
60
+ get '/api/v1/used_model', params: { 'q[brand_matches]': brand_to_search }, headers: access_token
52
61
  end
53
62
 
54
63
  it 'returns status ok' do
@@ -57,7 +66,7 @@ RSpec.describe 'Used Model requests', type: :request do
57
66
 
58
67
  it 'returns all records with brand queried' do
59
68
  JSON.parse(response.body)['entries'].each do |record|
60
- expect(record['brand']).to eq('Audi')
69
+ expect(record['brand']).to eq(brand_to_search)
61
70
  end
62
71
  end
63
72
  end
@@ -93,8 +102,9 @@ RSpec.describe 'Used Model requests', type: :request do
93
102
  end
94
103
 
95
104
  context 'with field selecting' do
105
+ field_select = UsedModel.column_names.sample
96
106
  before(:all) do
97
- get '/api/v1/used_model', params: { 'select': 'brand' }, headers: access_token
107
+ get '/api/v1/used_model', params: { 'select': field_select }, headers: access_token
98
108
  end
99
109
 
100
110
  it 'returns status ok' do
@@ -103,7 +113,7 @@ RSpec.describe 'Used Model requests', type: :request do
103
113
 
104
114
  it 'returns all records that have field queried' do
105
115
  JSON.parse(response.body)['entries'].each do |record|
106
- expect(record.keys).to include('brand')
116
+ expect(record.keys).to include(field_select)
107
117
  end
108
118
  end
109
119
  end
@@ -142,8 +152,9 @@ RSpec.describe 'Used Model requests', type: :request do
142
152
  end
143
153
 
144
154
  describe 'GET /api/v1/used_model/:id' do
155
+ id_to_get_id = UsedModel.all.sample.id.to_s
145
156
  before(:all) do
146
- get '/api/v1/used_model/1', headers: access_token
157
+ get '/api/v1/used_model/' + id_to_get_id, headers: access_token
147
158
  end
148
159
 
149
160
  it 'returns status ok' do
@@ -153,11 +164,16 @@ RSpec.describe 'Used Model requests', type: :request do
153
164
  it 'returns a record with attributes' do
154
165
  expect(JSON.parse(response.body).keys).to include('id', 'active', 'account_id', 'unit_id', 'brand', 'name', 'slug', 'model', 'version', 'model_year', 'production_year', 'kind', 'new_vehicle', 'old_price', 'price_value', 'price', 'category', 'transmission', 'km_value', 'km', 'plate', 'color', 'doors', 'fuel', 'fuel_text', 'note', 'chassis', 'shielded', 'featured', 'integrator', 'ordination', 'visits', 'bait_id', 'fipe_id', 'identifier', 'synced_at', 'deleted_at', 'created_at', 'updated_at')
155
166
  end
167
+
168
+ it 'return matches with object searched' do
169
+ expect(UsedModel.find(id_to_get_id.to_i).attributes.to_json).to eq(response.body)
170
+ end
156
171
  end
157
172
 
158
173
  describe 'GET /api/v1/used_model/:slug' do
174
+ id_to_get_slug = UsedModel.all.sample.slug.to_s
159
175
  before(:all) do
160
- get '/api/v1/used_model/cr-v', headers: access_token
176
+ get '/api/v1/used_model/' + id_to_get_slug, headers: access_token
161
177
  end
162
178
 
163
179
  it 'returns status ok' do
@@ -167,17 +183,32 @@ RSpec.describe 'Used Model requests', type: :request do
167
183
  it 'returns a record with attributes' do
168
184
  expect(JSON.parse(response.body).keys).to include('id', 'active', 'account_id', 'unit_id', 'brand', 'name', 'slug', 'model', 'version', 'model_year', 'production_year', 'kind', 'new_vehicle', 'old_price', 'price_value', 'price', 'category', 'transmission', 'km_value', 'km', 'plate', 'color', 'doors', 'fuel', 'fuel_text', 'note', 'chassis', 'shielded', 'featured', 'integrator', 'ordination', 'visits', 'bait_id', 'fipe_id', 'identifier', 'synced_at', 'deleted_at', 'created_at', 'updated_at')
169
185
  end
186
+
187
+ it 'return matches with object searched' do
188
+ expect(UsedModel.friendly.find(id_to_get_slug).attributes.to_json).to eq(response.body)
189
+ end
170
190
  end
171
191
 
172
192
  describe 'POST /api/v1/used_model/' do
193
+ slug_to_post = Faker::Lorem.word
194
+
173
195
  context 'with valid params' do
174
196
  before(:all) do
175
- post '/api/v1/used_model/', params: { 'used_model': { 'name': 'test', 'account_id': 1, 'unit_id': 1, 'slug': 'tests' }}, headers: access_token
197
+ @quantity = UsedModel.all.size
198
+ post '/api/v1/used_model/', params: { 'used_model': { 'name': 'test', 'account_id': 1, 'unit_id': 1, 'slug': slug_to_post, 'brand': 'BMW' }}, headers: access_token
176
199
  end
177
200
 
178
201
  it 'returns status created' do
179
202
  expect(response).to have_http_status(:created)
180
203
  end
204
+
205
+ it 'check records size into db' do
206
+ expect(UsedModel.all.size).to eq(@quantity + 1)
207
+ end
208
+
209
+ it 'find record into db' do
210
+ expect(UsedModel.find_by(slug: slug_to_post)).not_to eq nil
211
+ end
181
212
  end
182
213
 
183
214
  context 'with invalid params' do
@@ -189,9 +220,13 @@ RSpec.describe 'Used Model requests', type: :request do
189
220
  end
190
221
 
191
222
  describe 'PUT /api/v1/used_model/:id' do
223
+ id_to_put = UsedModel.all.sample.id.to_s
224
+ name_to_put = Faker::Lorem.word
225
+ slug_to_put = UsedModel.all.sample.slug
226
+
192
227
  context 'with valid params' do
193
228
  before(:all) do
194
- patch '/api/v1/used_model/' + UsedModel.last.id.to_s, params: { 'used_model': { 'name': 'updated' }}, headers: access_token
229
+ patch '/api/v1/used_model/' + id_to_put, params: { 'used_model': { 'name': name_to_put }}, headers: access_token
195
230
  end
196
231
 
197
232
  it 'returns status ok' do
@@ -199,29 +234,38 @@ RSpec.describe 'Used Model requests', type: :request do
199
234
  end
200
235
 
201
236
  it 'updates requested record' do
202
- expect(UsedModel.last.name).to eq('updated')
237
+ expect(UsedModel.find(id_to_put).name).to eq(name_to_put)
203
238
  end
204
239
  end
205
240
 
206
241
  context 'with invalid params' do
207
242
  it 'return a error' do
208
- patch '/api/v1/used_model/' + UsedModel.last.id.to_s, params: { 'used_model': { 'slug': 'cr-v' }}, headers: access_token
243
+ patch '/api/v1/used_model/' + id_to_put, params: { 'used_model': { 'unit_id': nil }}, headers: access_token
209
244
  expect(response).to have_http_status(:unprocessable_entity)
210
245
  end
211
246
  end
212
247
  end
213
248
 
214
249
  describe 'DELETE /api/v1/used_model/:id' do
215
- before(:all) do
216
- delete '/api/v1/used_model/' + UsedModel.last.id.to_s, headers: access_token
217
- end
250
+ id_to_del = UsedModel.all.sample.id.to_s
218
251
 
219
- it 'returns status no content' do
220
- expect(response).to have_http_status(:no_content)
221
- end
252
+ context 'with valid params' do
253
+ before(:all) do
254
+ @quantity = UsedModel.all.size
255
+ delete '/api/v1/used_model/' + id_to_del, headers: access_token
256
+ end
257
+
258
+ it 'returns status no content' do
259
+ expect(response).to have_http_status(:no_content)
260
+ end
222
261
 
223
- it 'detete a UsedModel record' do
224
- expect(UsedModel.all.size).to eq(10)
262
+ it 'detete a UsedModel record' do
263
+ expect(UsedModel.all.size).to eq(@quantity - 1)
264
+ end
265
+
266
+ it 'check if record was deleted' do
267
+ expect{ UsedModel.find(id_to_del.to_i) }.to raise_exception(ActiveRecord::RecordNotFound)
268
+ end
225
269
  end
226
270
  end
227
271
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: apicasso
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.6
4
+ version: 0.4.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Fernando Bellincanta
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-11-08 00:00:00.000000000 Z
11
+ date: 2018-11-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -307,7 +307,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
307
307
  version: '0'
308
308
  requirements: []
309
309
  rubyforge_project:
310
- rubygems_version: 2.7.6
310
+ rubygems_version: 2.7.5
311
311
  signing_key:
312
312
  specification_version: 4
313
313
  summary: An abstract API design as a mountable engine