standardapi 6.0.0.32 → 6.1.0

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.
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