standardapi 6.0.0.26 → 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 (48) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +15 -6
  3. data/lib/standard_api.rb +5 -0
  4. data/lib/standard_api/access_control_list.rb +114 -0
  5. data/lib/standard_api/active_record/connection_adapters/postgresql/schema_statements.rb +21 -0
  6. data/lib/standard_api/controller.rb +75 -78
  7. data/lib/standard_api/errors.rb +9 -0
  8. data/lib/standard_api/helpers.rb +66 -13
  9. data/lib/standard_api/includes.rb +9 -0
  10. data/lib/standard_api/middleware/query_encoding.rb +3 -3
  11. data/lib/standard_api/railtie.rb +13 -2
  12. data/lib/standard_api/route_helpers.rb +5 -5
  13. data/lib/standard_api/test_case.rb +24 -14
  14. data/lib/standard_api/test_case/calculate_tests.rb +10 -4
  15. data/lib/standard_api/test_case/create_tests.rb +13 -15
  16. data/lib/standard_api/test_case/index_tests.rb +14 -4
  17. data/lib/standard_api/test_case/schema_tests.rb +25 -3
  18. data/lib/standard_api/test_case/show_tests.rb +1 -0
  19. data/lib/standard_api/test_case/update_tests.rb +8 -9
  20. data/lib/standard_api/version.rb +1 -1
  21. data/lib/standard_api/views/application/_record.json.jbuilder +33 -30
  22. data/lib/standard_api/views/application/_record.streamer +36 -34
  23. data/lib/standard_api/views/application/_schema.json.jbuilder +68 -0
  24. data/lib/standard_api/views/application/_schema.streamer +78 -0
  25. data/lib/standard_api/views/application/new.streamer +1 -1
  26. data/lib/standard_api/views/application/schema.json.jbuilder +1 -12
  27. data/lib/standard_api/views/application/schema.streamer +1 -16
  28. data/test/standard_api/caching_test.rb +43 -0
  29. data/test/standard_api/helpers_test.rb +172 -0
  30. data/test/standard_api/performance.rb +39 -0
  31. data/test/standard_api/route_helpers_test.rb +33 -0
  32. data/test/standard_api/standard_api_test.rb +699 -0
  33. data/test/standard_api/test_app.rb +1 -0
  34. data/test/standard_api/test_app/app/controllers/acl/account_acl.rb +15 -0
  35. data/test/standard_api/test_app/app/controllers/acl/property_acl.rb +27 -0
  36. data/test/standard_api/test_app/app/controllers/acl/reference_acl.rb +7 -0
  37. data/test/standard_api/test_app/controllers.rb +13 -45
  38. data/test/standard_api/test_app/models.rb +38 -4
  39. data/test/standard_api/test_app/test/factories.rb +4 -3
  40. data/test/standard_api/test_app/views/photos/_photo.json.jbuilder +1 -0
  41. data/test/standard_api/test_app/views/photos/_photo.streamer +18 -0
  42. data/test/standard_api/test_app/views/photos/_schema.json.jbuilder +1 -0
  43. data/test/standard_api/test_app/views/photos/_schema.streamer +3 -0
  44. data/test/standard_api/test_app/views/photos/schema.json.jbuilder +1 -1
  45. data/test/standard_api/test_app/views/photos/schema.streamer +1 -0
  46. data/test/standard_api/test_helper.rb +238 -0
  47. metadata +33 -17
  48. data/test/standard_api/test_app/log/test.log +0 -129516
@@ -37,6 +37,7 @@ require 'standard_api/test_app/controllers'
37
37
  # Test Application Routes
38
38
  Rails.application.routes.draw do
39
39
  get :tables, to: 'application#tables', as: :tables
40
+ get :schema, to: 'application#schema', as: :schema
40
41
 
41
42
  [:properties, :photos, :documents, :references, :sessions, :unlimited, :default_limit].each do |r|
42
43
  standard_resources r
@@ -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]
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
@@ -21,6 +22,9 @@ class Property < ActiveRecord::Base
21
22
  has_and_belongs_to_many :photos
22
23
  has_many :accounts
23
24
  has_one :landlord, class_name: 'Account'
25
+ has_one :document_attachments, class_name: "Attachment", as: :record, inverse_of: :record
26
+ has_one :document, through: "document_attachments"
27
+
24
28
 
25
29
  validates :name, presence: true
26
30
  accepts_nested_attributes_for :photos
@@ -34,22 +38,46 @@ class Reference < ActiveRecord::Base
34
38
  belongs_to :subject, polymorphic: true
35
39
  end
36
40
 
41
+ class Document < ActiveRecord::Base
42
+ attr_accessor :file
43
+ end
44
+
45
+ class Attachment < ActiveRecord::Base
46
+ belongs_to :record, polymorphic: true
47
+ belongs_to :document
48
+ end
49
+
37
50
  # = Migration
38
51
 
39
- class CreateModelTables < ActiveRecord::Migration[5.2]
52
+ class CreateModelTables < ActiveRecord::Migration[6.0]
40
53
 
41
54
  def self.up
42
55
 
56
+ comment = "test comment"
57
+ exec_query(<<-SQL, "SQL")
58
+ COMMENT ON DATABASE #{quote_column_name(current_database)} IS #{quote(comment)};
59
+ SQL
60
+
43
61
  create_table "accounts", force: :cascade do |t|
44
62
  t.string 'name', limit: 255
45
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"
46
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"
47
74
  end
48
75
 
49
76
  create_table "photos", force: :cascade do |t|
50
77
  t.integer "account_id"
51
78
  t.integer "property_id"
52
79
  t.string "format", limit: 255
80
+ t.datetime "created_at", null: false
53
81
  end
54
82
 
55
83
  create_table "properties", force: :cascade do |t|
@@ -68,12 +96,12 @@ class CreateModelTables < ActiveRecord::Migration[5.2]
68
96
  t.string "key"
69
97
  t.string "value"
70
98
  end
71
-
99
+
72
100
  create_table "photos_properties", force: :cascade do |t|
73
101
  t.integer "photo_id"
74
102
  t.integer "property_id"
75
103
  end
76
-
104
+
77
105
  create_table "landlords_properties", force: :cascade do |t|
78
106
  t.integer "landlord_id"
79
107
  t.integer "property_id"
@@ -82,6 +110,12 @@ class CreateModelTables < ActiveRecord::Migration[5.2]
82
110
  create_table "documents", force: :cascade do |t|
83
111
  t.string 'type'
84
112
  end
113
+
114
+ create_table "attachments", force: :cascade do |t|
115
+ t.string 'record_type'
116
+ t.integer 'record_id'
117
+ t.integer 'document_id'
118
+ end
85
119
  end
86
120
 
87
121
  end
@@ -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
@@ -2,6 +2,7 @@ json.set! :id, photo.id
2
2
  json.set! :account_id, photo.account_id
3
3
  json.set! :property_id, photo.property_id
4
4
  json.set! :format, photo.format
5
+ json.set! :created_at, photo.created_at
5
6
  json.set! :template, 'photos/_photo'
6
7
 
7
8
  if includes[:account]
@@ -0,0 +1,18 @@
1
+ json.object! do
2
+ json.set! :id, photo.id
3
+ json.set! :account_id, photo.account_id
4
+ json.set! :property_id, photo.property_id
5
+ json.set! :format, photo.format
6
+ json.set! :created_at, photo.created_at
7
+ json.set! :template, 'photos/_photo'
8
+
9
+ if includes[:account]
10
+ json.set! :account do
11
+ if photo.account
12
+ json.partial! 'application/record', record: photo.account, includes: includes[:account]
13
+ else
14
+ json.value! nil
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1 @@
1
+ json.set! 'template', 'photos/schema'
@@ -0,0 +1,3 @@
1
+ json.object! do
2
+ json.set! 'template', 'photos/schema'
3
+ end
@@ -1 +1 @@
1
- json.set! 'template', 'photos/schema'
1
+ json.partial! 'schema'
@@ -0,0 +1 @@
1
+ json.partial! 'schema'
@@ -0,0 +1,238 @@
1
+ require File.expand_path('../test_app', __FILE__)
2
+
3
+ require "minitest/autorun"
4
+ require 'minitest/unit'
5
+ require 'factory_bot'
6
+ require 'faker'
7
+ require 'standard_api/test_case'
8
+ require 'byebug'
9
+ require 'mocha/minitest'
10
+
11
+ # Setup the test db
12
+ ActiveSupport.test_order = :random
13
+
14
+ include ActionDispatch::TestProcess
15
+
16
+ class ActiveSupport::TestCase
17
+ include ActiveRecord::TestFixtures
18
+ include FactoryBot::Syntax::Methods
19
+
20
+ def setup
21
+ @routes ||= TestApplication.routes
22
+ @subscribers, @layouts, @partials = [], {}, {}
23
+
24
+ Rails.cache.clear
25
+
26
+ @subscribers << ActiveSupport::Notifications.subscribe("!render_template.action_view") do |_name, _start, _finish, _id, payload|
27
+ path = payload[:identifier]
28
+ virtual_path = payload[:virtual_path]
29
+ format, handler = *path.split("/").last.split('.').last(2)
30
+
31
+ partial = virtual_path =~ /^.*\/_[^\/]*$/
32
+
33
+ if partial
34
+ if @partials[virtual_path]
35
+ @partials[virtual_path][:count] += 1
36
+ else
37
+ @partials[virtual_path] = {
38
+ count: 1,
39
+ path: virtual_path,
40
+ format: format,
41
+ handler: handler
42
+ }
43
+ end
44
+ else
45
+ if @layouts[virtual_path]
46
+ @layouts[virtual_path][:count] += 1
47
+ else
48
+ @layouts[virtual_path] = {
49
+ count: 1,
50
+ path: virtual_path,
51
+ format: format,
52
+ handler: handler
53
+ }
54
+ end
55
+ end
56
+ end
57
+ end
58
+
59
+ def teardown
60
+ @subscribers.each do |subscriber|
61
+ ActiveSupport::Notifications.unsubscribe(subscriber)
62
+ end
63
+ end
64
+
65
+ # = Helper Methods
66
+
67
+ def controller_path
68
+ if defined?(@controller)
69
+ @controller.controller_path
70
+ else
71
+ controller_class.new.controller_path
72
+ end
73
+ end
74
+
75
+ def path_with_action(action, options={})
76
+ { :controller => controller_path, :action => action }.merge(options)
77
+ end
78
+
79
+ def assert_sql(sql, &block)
80
+ queries = []
81
+ callback = -> (*, payload) do
82
+ queries << payload[:sql]
83
+ end
84
+
85
+ ActiveSupport::Notifications.subscribed(callback, "sql.active_record", &block)
86
+
87
+ assert_not_nil queries.map { |x| x.strip.gsub(/\s+/, ' ') }.
88
+ find { |x| x == sql.strip.gsub(/\s+/, ' ') }
89
+ end
90
+
91
+ def assert_rendered(options = {}, message = nil)
92
+ options = case options
93
+ when NilClass, Regexp, String, Symbol
94
+ { layout: options }
95
+ when Hash
96
+ options
97
+ else
98
+ raise ArgumentError, "assert_template only accepts a String, Symbol, Hash, Regexp, or nil"
99
+ end
100
+
101
+ options.assert_valid_keys(:layout, :partial, :count, :format, :handler)
102
+
103
+ if expected_layout = options[:layout]
104
+ case expected_layout
105
+ when String, Symbol
106
+ msg = message || sprintf("expecting layout <%s> but action rendered <%s>",
107
+ expected_layout, @layouts.keys)
108
+ assert_includes @layouts.keys, expected_layout.to_s, msg
109
+
110
+ key = expected_layout.to_s
111
+ value = @layouts[key]
112
+
113
+ if expected_count = options[:count]
114
+ actual_count = value[:count]
115
+ msg = message || sprintf("expecting %s to be rendered %s time(s) but rendered %s time(s)",
116
+ expected_partial, expected_count, actual_count)
117
+ assert_equal expected_count, actual_count, msg
118
+ end
119
+
120
+ if expected_format = options[:format]
121
+ actual_format = value[:format]
122
+ msg = message || sprintf("expecting %s to be rendered as %s but rendered as %s",
123
+ expected_partial, expected_format, actual_format)
124
+ assert_equal expected_format, actual_format, msg
125
+ end
126
+
127
+ if expected_handler = options[:handler]
128
+ actual_handler = value[:handler]
129
+ msg = message || sprintf("expecting %s to be rendered as %s but rendered as %s",
130
+ expected_partial, expected_handler, actual_handler)
131
+ assert_equal expected_handler, actual_handler, msg
132
+ end
133
+ when Regexp
134
+ msg = message || sprintf("expecting layout <%s> but action rendered <%s>",
135
+ expected_layout, @layouts.keys)
136
+ assert(@layouts.keys.any? {|l| l =~ expected_layout }, msg)
137
+
138
+ key = @layouts.keys.find {|l| l =~ expected_layout }
139
+ value = @layouts[key]
140
+
141
+ if expected_count = options[:count]
142
+ actual_count = value[:count]
143
+ msg = message || sprintf("expecting %s to be rendered %s time(s) but rendered %s time(s)",
144
+ expected_partial, expected_count, actual_count)
145
+ assert_equal expected_count, actual_count, msg
146
+ end
147
+
148
+ if expected_format = options[:format]
149
+ actual_format = value[:format]
150
+ msg = message || sprintf("expecting %s to be rendered as %s but rendered as %s",
151
+ expected_partial, expected_format, actual_format)
152
+ assert_equal expected_format, actual_format, msg
153
+ end
154
+
155
+ if expected_handler = options[:handler]
156
+ actual_handler = value[:handler]
157
+ msg = message || sprintf("expecting %s to be rendered as %s but rendered as %s",
158
+ expected_partial, expected_handler, actual_handler)
159
+ assert_equal expected_handler, actual_handler, msg
160
+ end
161
+ when nil, false
162
+ assert(@layouts.empty?, msg)
163
+ end
164
+ elsif expected_partial = options[:partial]
165
+ case expected_partial
166
+ when String, Symbol
167
+ msg = message || sprintf("expecting partial <%s> but action rendered <%s>",
168
+ expected_partial, @partials.keys)
169
+ assert_includes @partials.keys, expected_partial.to_s, msg
170
+
171
+ key = expected_partial.to_s
172
+ value = @partials[key]
173
+
174
+ if expected_count = options[:count]
175
+ actual_count = value[:count]
176
+ msg = message || sprintf("expecting %s to be rendered %s time(s) but rendered %s time(s)",
177
+ expected_partial, expected_count, actual_count)
178
+ assert_equal expected_count, actual_count, msg
179
+ end
180
+
181
+ if expected_format = options[:format]
182
+ actual_format = value[:format]
183
+ msg = message || sprintf("expecting %s to be rendered as %s but rendered as %s",
184
+ expected_partial, expected_format, actual_format)
185
+ assert_equal expected_format, actual_format, msg
186
+ end
187
+
188
+ if expected_handler = options[:handler]
189
+ actual_handler = value[:handler]
190
+ msg = message || sprintf("expecting %s to be rendered as %s but rendered as %s",
191
+ expected_partial, expected_handler, actual_handler)
192
+ assert_equal expected_handler, actual_handler, msg
193
+ end
194
+ when Regexp
195
+ msg = message || sprintf("expecting partial <%s> but action rendered <%s>",
196
+ expected_partial, @partials.keys)
197
+ assert(@partials.keys.any? {|l| l =~ expected_partial }, msg)
198
+
199
+ key = @partials.keys.find {|l| l =~ expected_partial }
200
+ value = @partials[key]
201
+
202
+ if expected_count = options[:count]
203
+ actual_count = value[:count]
204
+ msg = message || sprintf("expecting %s to be rendered %s time(s) but rendered %s time(s)",
205
+ expected_partial, expected_count, actual_count)
206
+ assert_equal expected_count, actual_count, msg
207
+ end
208
+
209
+ if expected_format = options[:format]
210
+ actual_format = value[:format]
211
+ msg = message || sprintf("expecting %s to be rendered as %s but rendered as %s",
212
+ expected_partial, expected_format, actual_format)
213
+ assert_equal expected_format, actual_format, msg
214
+ end
215
+
216
+ if expected_handler = options[:handler]
217
+ actual_handler = value[:handler]
218
+ msg = message || sprintf("expecting %s to be rendered as %s but rendered as %s",
219
+ expected_partial, expected_handler, actual_handler)
220
+ assert_equal expected_handler, actual_handler, msg
221
+ end
222
+ when nil, false
223
+ assert(@partials.empty?, msg)
224
+ end
225
+ end
226
+ end
227
+
228
+ end
229
+
230
+ class ActionController::TestCase
231
+
232
+ def assigns(key = nil)
233
+ assigns = {}.with_indifferent_access
234
+ @controller.view_assigns.each { |k, v| assigns.regular_writer(k, v) }
235
+ key.nil? ? assigns : assigns[key]
236
+ end
237
+
238
+ end