standardapi 6.0.0.26 → 6.1.0

Sign up to get free protection for your applications and to get access to all the features.
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