standardapi 6.0.0.32 → 6.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/lib/standard_api.rb +4 -0
  3. data/lib/standard_api/access_control_list.rb +114 -0
  4. data/lib/standard_api/controller.rb +32 -11
  5. data/lib/standard_api/helpers.rb +4 -3
  6. data/lib/standard_api/includes.rb +9 -0
  7. data/lib/standard_api/middleware/query_encoding.rb +3 -3
  8. data/lib/standard_api/railtie.rb +13 -2
  9. data/lib/standard_api/route_helpers.rb +5 -5
  10. data/lib/standard_api/test_case.rb +13 -3
  11. data/lib/standard_api/test_case/calculate_tests.rb +8 -2
  12. data/lib/standard_api/test_case/create_tests.rb +7 -9
  13. data/lib/standard_api/test_case/index_tests.rb +10 -0
  14. data/lib/standard_api/test_case/schema_tests.rb +7 -1
  15. data/lib/standard_api/test_case/show_tests.rb +1 -0
  16. data/lib/standard_api/test_case/update_tests.rb +8 -9
  17. data/lib/standard_api/version.rb +1 -1
  18. data/lib/standard_api/views/application/_record.json.jbuilder +14 -14
  19. data/lib/standard_api/views/application/_record.streamer +36 -34
  20. data/lib/standard_api/views/application/_schema.json.jbuilder +1 -1
  21. data/lib/standard_api/views/application/_schema.streamer +1 -1
  22. data/lib/standard_api/views/application/new.streamer +1 -1
  23. data/test/standard_api/caching_test.rb +14 -4
  24. data/test/standard_api/helpers_test.rb +25 -9
  25. data/test/standard_api/standard_api_test.rb +122 -14
  26. data/test/standard_api/test_app/app/controllers/acl/account_acl.rb +15 -0
  27. data/test/standard_api/test_app/app/controllers/acl/property_acl.rb +27 -0
  28. data/test/standard_api/test_app/app/controllers/acl/reference_acl.rb +7 -0
  29. data/test/standard_api/test_app/controllers.rb +13 -45
  30. data/test/standard_api/test_app/models.rb +17 -5
  31. data/test/standard_api/test_app/test/factories.rb +4 -3
  32. data/test/standard_api/test_app/views/photos/_photo.json.jbuilder +1 -0
  33. data/test/standard_api/test_app/views/photos/_photo.streamer +2 -1
  34. data/test/standard_api/test_helper.rb +12 -0
  35. metadata +19 -15
@@ -6,13 +6,13 @@ class PropertiesControllerTest < ActionDispatch::IntegrationTest
6
6
 
7
7
  self.includes = [ :photos, :landlord, :english_name ]
8
8
 
9
- # def normalizers
10
- # {
11
- # Property => {
12
- # "size" => lambda { |value| value.to_i }
13
- # }
14
- # }
15
- # end
9
+ def normalizers
10
+ {
11
+ Property => {
12
+ "size" => lambda { |value| value.round(4).to_s }
13
+ }
14
+ }
15
+ end
16
16
 
17
17
  # = Routing Tests
18
18
  #
@@ -94,7 +94,8 @@ class PropertiesControllerTest < ActionDispatch::IntegrationTest
94
94
 
95
95
  test 'Controller#model_params defaults to []' do
96
96
  @controller = ReferencesController.new
97
- assert_equal @controller.send(:model_params), []
97
+ @controller.params = {}
98
+ assert_equal @controller.send(:model_params), ActionController::Parameters.new
98
99
  end
99
100
 
100
101
  test 'Controller#current_mask' do
@@ -123,8 +124,9 @@ class PropertiesControllerTest < ActionDispatch::IntegrationTest
123
124
 
124
125
  model.columns.each do |column|
125
126
  assert_equal json_column_type(column.sql_type), schema.dig('models', model.name, 'attributes', column.name, 'type')
126
- default = column.default || column.default_function
127
+ default = column.default
127
128
  if default then
129
+ default = model.connection.lookup_cast_type_from_column(column).deserialize(default)
128
130
  assert_equal default, schema.dig('models', model.name, 'attributes', column.name, 'default')
129
131
  else
130
132
  assert_nil schema.dig('models', model.name, 'attributes', column.name, 'default')
@@ -190,7 +192,7 @@ class PropertiesControllerTest < ActionDispatch::IntegrationTest
190
192
  property = create(:property, photos: [])
191
193
  photo = create(:photo)
192
194
 
193
- post "/properties/#{property.id}/photos/#{photo.id}.json"
195
+ post "/properties/#{property.id}/photos/#{photo.id}"
194
196
  assert_equal property.photos.reload.map(&:id), [photo.id]
195
197
  assert_response :created
196
198
 
@@ -198,9 +200,18 @@ class PropertiesControllerTest < ActionDispatch::IntegrationTest
198
200
  assert_response :not_found
199
201
  end
200
202
 
203
+ test 'Controller#add_resource with has_one' do
204
+ photo = create(:document)
205
+ property = create(:property)
206
+ post "/properties/#{property.id}/document/#{photo.id}"
207
+ assert_equal property.reload.document, photo
208
+ assert_response :created
209
+ end
210
+
201
211
  test 'Controller#remove_resource' do
202
212
  photo = create(:photo)
203
213
  property = create(:property, photos: [photo])
214
+ assert_equal property.photos.reload, [photo]
204
215
  delete "/properties/#{property.id}/photos/#{photo.id}"
205
216
  assert_equal property.photos.reload, []
206
217
  assert_response :no_content
@@ -209,6 +220,15 @@ class PropertiesControllerTest < ActionDispatch::IntegrationTest
209
220
  assert_response :not_found
210
221
  end
211
222
 
223
+ test 'Controller#remove_resource with has_one' do
224
+ photo = create(:document)
225
+ property = create(:property, document: photo)
226
+ assert_equal property.document, photo
227
+ delete "/properties/#{property.id}/document/#{photo.id}"
228
+ assert_nil property.reload.document
229
+ assert_response :no_content
230
+ end
231
+
212
232
  # = View Tests
213
233
 
214
234
  test 'rendering tables' do
@@ -222,7 +242,7 @@ class PropertiesControllerTest < ActionDispatch::IntegrationTest
222
242
  test 'rendering null attribute' do
223
243
  property = create(:property)
224
244
  get property_path(property, format: 'json'), params: { id: property.id, include: [:landlord] }
225
- assert_equal true, JSON(response.body).has_key?('landlord')
245
+ assert JSON(response.body).has_key?('landlord')
226
246
  assert_nil JSON(response.body)['landlord']
227
247
  end
228
248
 
@@ -320,14 +340,22 @@ class PropertiesControllerTest < ActionDispatch::IntegrationTest
320
340
  test 'include with where key' do
321
341
  photo_a = create(:photo)
322
342
  photo_b = create(:photo)
343
+ photo_c = create(:photo)
323
344
 
324
- property = create(:property, photos: [photo_b])
345
+ property = create(:property, photos: [photo_b, photo_c])
325
346
  get property_path(property, include: { photos: { where: { id: photo_a.id } } }, format: :json)
326
347
  assert_equal [], JSON(response.body)['photos']
327
348
 
328
349
  property.photos << photo_a
329
350
  get property_path(property, include: { photos: { where: { id: photo_a.id } } }, format: :json)
330
351
  assert_equal [photo_a.id], JSON(response.body)['photos'].map { |x| x['id'] }
352
+ get property_path(property, include: { photos: { where: [
353
+ { id: photo_a.id },
354
+ 'OR',
355
+ { id: photo_c.id}
356
+ ] } }, format: :json)
357
+ assert_equal [photo_a.id, photo_c.id].sort,
358
+ JSON(response.body)['photos'].map { |x| x['id'] }.sort
331
359
  end
332
360
 
333
361
  test 'include with order key' do
@@ -338,6 +366,14 @@ class PropertiesControllerTest < ActionDispatch::IntegrationTest
338
366
  assert_equal photos.map(&:id).sort, JSON(response.body)['photos'].map { |x| x['id'] }
339
367
  end
340
368
 
369
+ test 'include relation with default order using an order key' do
370
+ p1 = create(:photo)
371
+ p2 = create(:photo)
372
+ account = create(:account, photos: [ p1, p2 ])
373
+ get account_path(account, include: { photos: { order: { created_at: :desc } } }, format: 'json')
374
+ assert_equal [ p2.id, p1.id ], JSON(response.body)['photos'].map { |x| x["id"] }
375
+ end
376
+
341
377
  test 'include with limit key' do
342
378
  5.times { create(:property, photos: Array.new(5) { create(:photo) }) }
343
379
  get properties_path(include: { photos: { limit: 1 } }, limit: 5, format: 'json')
@@ -380,8 +416,8 @@ class PropertiesControllerTest < ActionDispatch::IntegrationTest
380
416
 
381
417
  json = JSON(response.body)
382
418
 
383
- assert_equal json.find { |x| x['id'] == account_reference.id }.dig('subject', 'photos', 0, 'id'), photo.id
384
- assert_equal json.find { |x| x['id'] == property_reference.id }.dig('subject', 'landlord', 'id'), account.id
419
+ assert_equal photo.id, json.find { |x| x['id'] == account_reference.id }.dig('subject', 'photos', 0, 'id')
420
+ assert_equal account.id, json.find { |x| x['id'] == property_reference.id }.dig('subject', 'landlord', 'id')
385
421
  end
386
422
 
387
423
  test 'include with distinct key' do
@@ -549,6 +585,19 @@ class PropertiesControllerTest < ActionDispatch::IntegrationTest
549
585
  assert_equal properties.map(&:id).sort.reverse, JSON(response.body).map { |x| x['id'] }
550
586
  end
551
587
 
588
+ test 'ordering via nulls_first/last' do
589
+ p1 = create(:property, description: 'test')
590
+ p2 = create(:property, description: nil)
591
+
592
+ get properties_path(format: 'json'), params: { limit: 100, order: { description: { desc: 'nulls_last' } } }
593
+ properties = JSON(response.body)
594
+ assert_equal p1.id, properties.first['id']
595
+
596
+ get properties_path(format: 'json'), params: { limit: 100, order: { description: { asc: 'nulls_last' } } }
597
+ properties = JSON(response.body)
598
+ assert_equal p1.id, properties.first['id']
599
+ end
600
+
552
601
  # Calculate Test
553
602
  test 'calculate' do
554
603
  create(:photo)
@@ -556,6 +605,65 @@ class PropertiesControllerTest < ActionDispatch::IntegrationTest
556
605
  assert_equal [1], JSON(response.body)
557
606
  end
558
607
 
608
+ test 'calculate distinct aggregation' do
609
+ assert_sql(<<-SQL) do
610
+ SELECT DISTINCT COUNT("properties"."id") FROM "properties"
611
+ SQL
612
+ create(:property)
613
+ create(:property)
614
+ get '/properties/calculate', params: {
615
+ select: { count: "id" },
616
+ distinct: true
617
+ }
618
+ assert_equal [2], JSON(response.body)
619
+ end
620
+ end
621
+
622
+ test 'calculate aggregation distinctly' do
623
+ assert_sql(<<-SQL) do
624
+ SELECT COUNT(DISTINCT "properties"."id") FROM "properties"
625
+ SQL
626
+ create(:property)
627
+ create(:property)
628
+ get '/properties/calculate', params: {
629
+ select: { count: { distinct: "id" } }
630
+ }
631
+ assert_equal [2], JSON(response.body)
632
+ end
633
+ end
634
+
635
+ test 'calculate distinct aggregation distinctly' do
636
+ assert_sql(<<-SQL) do
637
+ SELECT DISTINCT COUNT(DISTINCT "properties"."id") FROM "properties"
638
+ SQL
639
+ create(:property)
640
+ create(:property)
641
+ get '/properties/calculate', params: {
642
+ select: { count: { distinct: "id" } },
643
+ distinct: true
644
+ }
645
+ assert_equal [2], JSON(response.body)
646
+ end
647
+ end
648
+
649
+ test 'calculate distinct count' do
650
+ p1 = create(:property)
651
+ p2 = create(:property)
652
+ a1 = create(:account, property: p1)
653
+ a2 = create(:account, property: p2)
654
+ get '/properties/calculate', params: {
655
+ select: { count: 'id' },
656
+ where: {
657
+ accounts: {
658
+ id: [a1.id, a2.id]
659
+ }
660
+ },
661
+ group_by: 'id',
662
+ distinct: true
663
+ }
664
+ assert_equal Hash[p1.id.to_s, 1, p2.id.to_s, 1], JSON(response.body)
665
+ end
666
+
559
667
  test 'calculate group_by' do
560
668
  create(:photo, format: 'jpg')
561
669
  create(:photo, format: 'jpg')
@@ -0,0 +1,15 @@
1
+ module AccountACL
2
+
3
+ def attributes
4
+ [ "property_id", "name" ]
5
+ end
6
+
7
+ def orders
8
+ [ "id" ]
9
+ end
10
+
11
+ def includes
12
+ [ "photos", "subject", "property" ]
13
+ end
14
+
15
+ end
@@ -0,0 +1,27 @@
1
+ module PropertyACL
2
+
3
+ def attributes
4
+ [ :name,
5
+ :aliases,
6
+ :description,
7
+ :constructed,
8
+ :size,
9
+ :active
10
+ # :photos_attributes,
11
+ # { photos_attributes: [ :id, :account_id, :property_id, :format] }
12
+ ]
13
+ end
14
+
15
+ def orders
16
+ ["id", "name", "aliases", "description", "constructed", "size", "created_at", "active"]
17
+ end
18
+
19
+ def includes
20
+ [ :photos, :landlord, :english_name, :document ]
21
+ end
22
+
23
+ def nested
24
+ [ :photos ]
25
+ end
26
+
27
+ end
@@ -0,0 +1,7 @@
1
+ module ReferenceACL
2
+
3
+ def includes
4
+ { subject: [ :landlord, :photos ] }
5
+ end
6
+
7
+ end
@@ -1,77 +1,45 @@
1
1
  class ApplicationController < ActionController::Base
2
2
  include StandardAPI::Controller
3
+ include StandardAPI::AccessControlList
3
4
  prepend_view_path File.join(File.dirname(__FILE__), 'views')
4
5
 
5
- private
6
-
7
- def account_params
8
- [ "property_id", "name" ]
9
- end
10
-
11
- def account_orders
12
- [ "id" ]
13
- end
14
-
15
- def account_includes
16
- [ "photos" ]
17
- end
18
-
19
- def property_params
20
- [ :name,
21
- :aliases,
22
- :description,
23
- :constructed,
24
- :size,
25
- :active,
26
- :photos_attributes,
27
- { photos_attributes: [ :id, :account_id, :property_id, :format] }
28
- ]
29
- end
30
-
31
- def property_orders
32
- ["id", "name", "aliases", "description", "constructed", "size", "created_at", "active"]
33
- end
34
-
35
- def property_includes
36
- [:photos, :landlord, :english_name, :document]
37
- end
38
-
39
- def reference_includes
40
- { subject: [ :landlord, :photos ] }
41
- end
42
-
43
6
  end
44
7
 
45
8
  class PropertiesController < ApplicationController
46
9
  end
47
10
 
48
11
  class AccountsController < ApplicationController
12
+
13
+ def show
14
+ @account = Account.last
15
+ end
16
+
49
17
  end
50
18
 
51
19
  class DocumentsController < ApplicationController
52
20
 
53
- def document_params
21
+ def document_attributes
54
22
  [ :file, :type ]
55
23
  end
56
-
24
+
57
25
  def document_orders
58
- [:id]
26
+ [ :id ]
59
27
  end
60
28
 
61
29
  end
62
30
 
63
31
  class PhotosController < ApplicationController
64
32
 
65
- def photo_params
33
+ def photo_attributes
66
34
  [ :id, :account_id, :property_id, :format ]
67
35
  end
68
36
 
69
37
  def photo_orders
70
- [:id]
38
+ [ :id ]
71
39
  end
72
40
 
73
41
  def photo_includes
74
- [:account]
42
+ [ :account ]
75
43
  end
76
44
 
77
45
  end
@@ -104,4 +72,4 @@ class DefaultLimitController < ApplicationController
104
72
  100
105
73
  end
106
74
 
107
- end
75
+ end
@@ -1,8 +1,9 @@
1
1
  # = Models
2
2
 
3
3
  class Account < ActiveRecord::Base
4
- has_many :photos
4
+ has_many :photos, -> { order(:created_at) }
5
5
  belongs_to :property
6
+ belongs_to :subject, polymorphic: true
6
7
  end
7
8
 
8
9
  class Photo < ActiveRecord::Base
@@ -24,6 +25,7 @@ class Property < ActiveRecord::Base
24
25
  has_one :document_attachments, class_name: "Attachment", as: :record, inverse_of: :record
25
26
  has_one :document, through: "document_attachments"
26
27
 
28
+
27
29
  validates :name, presence: true
28
30
  accepts_nested_attributes_for :photos
29
31
 
@@ -59,13 +61,23 @@ class CreateModelTables < ActiveRecord::Migration[6.0]
59
61
  create_table "accounts", force: :cascade do |t|
60
62
  t.string 'name', limit: 255
61
63
  t.integer 'property_id'
64
+ t.integer "subject_id"
65
+ t.string "subject_type"
66
+ t.datetime "property_cached_at"
67
+ t.datetime "subject_cached_at"
62
68
  t.integer 'photos_count', null: false, default: 0
69
+ t.datetime "created_at", null: false
70
+ end
71
+
72
+ create_table "landlords", force: :cascade do |t|
73
+ t.string "name"
63
74
  end
64
75
 
65
76
  create_table "photos", force: :cascade do |t|
66
77
  t.integer "account_id"
67
78
  t.integer "property_id"
68
79
  t.string "format", limit: 255
80
+ t.datetime "created_at", null: false
69
81
  end
70
82
 
71
83
  create_table "properties", force: :cascade do |t|
@@ -84,12 +96,12 @@ class CreateModelTables < ActiveRecord::Migration[6.0]
84
96
  t.string "key"
85
97
  t.string "value"
86
98
  end
87
-
99
+
88
100
  create_table "photos_properties", force: :cascade do |t|
89
101
  t.integer "photo_id"
90
102
  t.integer "property_id"
91
103
  end
92
-
104
+
93
105
  create_table "landlords_properties", force: :cascade do |t|
94
106
  t.integer "landlord_id"
95
107
  t.integer "property_id"
@@ -98,8 +110,8 @@ class CreateModelTables < ActiveRecord::Migration[6.0]
98
110
  create_table "documents", force: :cascade do |t|
99
111
  t.string 'type'
100
112
  end
101
-
102
- create_table "attachments", force: :cascade do |t|
113
+
114
+ create_table "attachments", force: :cascade do |t|
103
115
  t.string 'record_type'
104
116
  t.integer 'record_id'
105
117
  t.integer 'document_id'
@@ -1,4 +1,5 @@
1
1
  FactoryBot.define do
2
+
2
3
  factory :account do
3
4
  name { Faker::Name.name }
4
5
 
@@ -7,7 +8,7 @@ FactoryBot.define do
7
8
  name { nil }
8
9
  end
9
10
  end
10
-
11
+
11
12
  factory :landlord do
12
13
  name { Faker::Name.name }
13
14
  end
@@ -17,12 +18,12 @@ FactoryBot.define do
17
18
  end
18
19
 
19
20
  factory :document do
20
- file { fixture_file_upload(Rails.root + 'test/fixtures/photo.png', 'image/png') }
21
+ file { Rack::Test::UploadedFile.new(File.join(Rails.root, 'test/fixtures/photo.png'), 'image/png') }
21
22
  end
22
23
 
23
24
  factory :pdf do
24
25
  type { 'Pdf' }
25
- file { fixture_file_upload(Rails.root + 'test/fixtures/photo.png', 'image/png') }
26
+ file { Rack::Test::UploadedFile.new(File.join(Rails.root, 'test/fixtures/photo.png'), 'image/png') }
26
27
  end
27
28
 
28
29
  factory :reference do