standardapi 6.0.0.32 → 7.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.
- checksums.yaml +4 -4
- data/README.md +80 -58
- data/lib/standard_api/access_control_list.rb +148 -0
- data/lib/standard_api/controller.rb +116 -27
- data/lib/standard_api/helpers.rb +17 -10
- data/lib/standard_api/includes.rb +9 -0
- data/lib/standard_api/middleware/query_encoding.rb +3 -3
- data/lib/standard_api/middleware.rb +5 -0
- data/lib/standard_api/railtie.rb +30 -2
- data/lib/standard_api/route_helpers.rb +64 -14
- data/lib/standard_api/test_case/calculate_tests.rb +15 -8
- data/lib/standard_api/test_case/create_tests.rb +7 -9
- data/lib/standard_api/test_case/destroy_tests.rb +19 -7
- data/lib/standard_api/test_case/index_tests.rb +10 -6
- data/lib/standard_api/test_case/schema_tests.rb +7 -1
- data/lib/standard_api/test_case/show_tests.rb +8 -7
- data/lib/standard_api/test_case/update_tests.rb +15 -15
- data/lib/standard_api/test_case.rb +13 -3
- data/lib/standard_api/version.rb +1 -1
- data/lib/standard_api/views/application/_record.json.jbuilder +18 -17
- data/lib/standard_api/views/application/_record.streamer +40 -37
- data/lib/standard_api/views/application/_schema.json.jbuilder +20 -8
- data/lib/standard_api/views/application/_schema.streamer +22 -8
- data/lib/standard_api/views/application/new.streamer +1 -1
- data/lib/standard_api.rb +5 -0
- data/test/standard_api/caching_test.rb +14 -4
- data/test/standard_api/controller/include_test.rb +107 -0
- data/test/standard_api/controller/subresource_test.rb +157 -0
- data/test/standard_api/helpers_test.rb +34 -17
- data/test/standard_api/nested_attributes/belongs_to_test.rb +71 -0
- data/test/standard_api/nested_attributes/has_and_belongs_to_many_test.rb +70 -0
- data/test/standard_api/nested_attributes/has_many_test.rb +85 -0
- data/test/standard_api/nested_attributes/has_one_test.rb +71 -0
- data/test/standard_api/route_helpers_test.rb +56 -0
- data/test/standard_api/standard_api_test.rb +182 -44
- data/test/standard_api/test_app/app/controllers/acl/account_acl.rb +15 -0
- data/test/standard_api/test_app/app/controllers/acl/camera_acl.rb +7 -0
- data/test/standard_api/test_app/app/controllers/acl/photo_acl.rb +13 -0
- data/test/standard_api/test_app/app/controllers/acl/property_acl.rb +33 -0
- data/test/standard_api/test_app/app/controllers/acl/reference_acl.rb +7 -0
- data/test/standard_api/test_app/controllers.rb +28 -43
- data/test/standard_api/test_app/models.rb +76 -7
- data/test/standard_api/test_app/test/factories.rb +7 -3
- data/test/standard_api/test_app/views/photos/_photo.json.jbuilder +1 -0
- data/test/standard_api/test_app/views/photos/_photo.streamer +2 -1
- data/test/standard_api/test_app/views/sessions/create.json.jbuilder +1 -0
- data/test/standard_api/test_app/views/sessions/create.streamer +3 -0
- data/test/standard_api/test_app.rb +12 -1
- data/test/standard_api/test_helper.rb +21 -0
- metadata +59 -16
@@ -13,8 +13,8 @@ require 'msgpack'
|
|
13
13
|
module StandardAPI
|
14
14
|
module Middleware
|
15
15
|
class QueryEncoding
|
16
|
-
MSGPACK_MIME_TYPE = "application/msgpack"
|
17
|
-
HTTP_METHOD_OVERRIDE_HEADER = "HTTP_QUERY_ENCODING"
|
16
|
+
MSGPACK_MIME_TYPE = "application/msgpack"
|
17
|
+
HTTP_METHOD_OVERRIDE_HEADER = "HTTP_QUERY_ENCODING"
|
18
18
|
|
19
19
|
def initialize(app)
|
20
20
|
@app = app
|
@@ -31,4 +31,4 @@ module StandardAPI
|
|
31
31
|
|
32
32
|
end
|
33
33
|
end
|
34
|
-
end
|
34
|
+
end
|
data/lib/standard_api/railtie.rb
CHANGED
@@ -1,12 +1,40 @@
|
|
1
1
|
module StandardAPI
|
2
2
|
class Railtie < ::Rails::Railtie
|
3
3
|
|
4
|
-
initializer 'standardapi' do
|
4
|
+
initializer 'standardapi', :before => :set_autoload_paths do |app|
|
5
|
+
if app.root.join('app', 'controllers', 'acl').exist?
|
6
|
+
ActiveSupport::Inflector.inflections(:en) do |inflect|
|
7
|
+
inflect.acronym 'ACL'
|
8
|
+
end
|
9
|
+
|
10
|
+
app.config.autoload_paths << app.root.join('app', 'controllers', 'acl').to_s
|
11
|
+
end
|
12
|
+
|
13
|
+
ActiveSupport.on_load(:before_configuration) do
|
14
|
+
::ActionDispatch::Routing::Mapper.send :include, StandardAPI::RouteHelpers
|
15
|
+
end
|
16
|
+
|
5
17
|
ActiveSupport.on_load(:action_view) do
|
6
18
|
::ActionView::Base.send :include, StandardAPI::Helpers
|
7
|
-
::ActionDispatch::Routing::Mapper.send :include, StandardAPI::RouteHelpers
|
8
19
|
end
|
9
20
|
end
|
10
21
|
|
11
22
|
end
|
23
|
+
|
24
|
+
module AutosaveByDefault
|
25
|
+
def self.included base
|
26
|
+
base.class_eval do
|
27
|
+
class <<self
|
28
|
+
alias_method :standard_build, :build
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.build(model, name, scope, options, &block)
|
32
|
+
options[:autosave] = true
|
33
|
+
standard_build(model, name, scope, options, &block)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
::ActiveRecord::Associations::Builder::Association.include(AutosaveByDefault)
|
12
40
|
end
|
@@ -1,10 +1,10 @@
|
|
1
1
|
module StandardAPI
|
2
2
|
module RouteHelpers
|
3
|
-
|
3
|
+
|
4
4
|
# StandardAPI wrapper for ActionDispatch::Routing::Mapper::Resources#resources
|
5
5
|
#
|
6
6
|
# Includes the following routes
|
7
|
-
#
|
7
|
+
#
|
8
8
|
# GET /schema
|
9
9
|
# GET /calculate
|
10
10
|
#
|
@@ -20,20 +20,47 @@ module StandardAPI
|
|
20
20
|
# end
|
21
21
|
def standard_resources(*resources, &block)
|
22
22
|
options = resources.extract_options!.dup
|
23
|
-
|
23
|
+
|
24
24
|
resources(*resources, options) do
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
25
|
+
block.call if block # custom routes take precedence over standardapi routes
|
26
|
+
|
27
|
+
available_actions = if only = parent_resource.instance_variable_get(:@only)
|
28
|
+
Array(only).map(&:to_sym)
|
29
|
+
else
|
30
|
+
if parent_resource.instance_variable_get(:@api_only)
|
31
|
+
[:index, :create, :show, :update, :destroy]
|
32
|
+
else
|
33
|
+
[:index, :create, :new, :show, :update, :destroy, :edit]
|
34
|
+
end + [ :schema, :calculate, :add_resource, :remove_resource, :create_resource ]
|
35
|
+
end
|
36
|
+
|
37
|
+
actions = if except = parent_resource.instance_variable_get(:@except)
|
38
|
+
available_actions - Array(except).map(&:to_sym)
|
39
|
+
else
|
40
|
+
available_actions
|
41
|
+
end
|
42
|
+
|
43
|
+
get :schema, on: :collection if actions.include?(:schema)
|
44
|
+
get :calculate, on: :collection if actions.include?(:calculate)
|
45
|
+
|
46
|
+
if actions.include?(:add_resource)
|
47
|
+
post ':relationship/:resource_id' => :add_resource, on: :member
|
48
|
+
end
|
49
|
+
|
50
|
+
if actions.include?(:create_resource)
|
51
|
+
post ':relationship' => :create_resource, on: :member
|
52
|
+
end
|
53
|
+
|
54
|
+
if actions.include?(:remove_resource)
|
55
|
+
delete ':relationship/:resource_id' => :remove_resource, on: :member
|
56
|
+
end
|
30
57
|
end
|
31
58
|
end
|
32
59
|
|
33
60
|
# StandardAPI wrapper for ActionDispatch::Routing::Mapper::Resources#resource
|
34
61
|
#
|
35
62
|
# Includes the following routes
|
36
|
-
#
|
63
|
+
#
|
37
64
|
# GET /schema
|
38
65
|
# GET /calculate
|
39
66
|
#
|
@@ -49,12 +76,35 @@ module StandardAPI
|
|
49
76
|
# end
|
50
77
|
def standard_resource(*resource, &block)
|
51
78
|
options = resource.extract_options!.dup
|
52
|
-
|
79
|
+
|
53
80
|
resource(*resource, options) do
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
81
|
+
available_actions = if only = parent_resource.instance_variable_get(:@only)
|
82
|
+
Array(only).map(&:to_sym)
|
83
|
+
else
|
84
|
+
if parent_resource.instance_variable_get(:@api_only)
|
85
|
+
[:index, :create, :show, :update, :destroy]
|
86
|
+
else
|
87
|
+
[:index, :create, :new, :show, :update, :destroy, :edit]
|
88
|
+
end + [ :schema, :calculate, :add_resource, :remove_resource ]
|
89
|
+
end
|
90
|
+
|
91
|
+
actions = if except = parent_resource.instance_variable_get(:@except)
|
92
|
+
available_actions - Array(except).map(&:to_sym)
|
93
|
+
else
|
94
|
+
available_actions
|
95
|
+
end
|
96
|
+
|
97
|
+
get :schema, on: :collection if actions.include?(:schema)
|
98
|
+
get :calculate, on: :collection if actions.include?(:calculate)
|
99
|
+
|
100
|
+
if actions.include?(:add_resource)
|
101
|
+
post ':relationship/:resource_id' => :add_resource, on: :member
|
102
|
+
end
|
103
|
+
|
104
|
+
if actions.include?(:remove_resource)
|
105
|
+
delete ':relationship/:resource_id' => :remove_resource, on: :member
|
106
|
+
end
|
107
|
+
|
58
108
|
block.call if block
|
59
109
|
end
|
60
110
|
end
|
@@ -26,8 +26,14 @@ module StandardAPI
|
|
26
26
|
calculations = @controller.instance_variable_get('@calculations')
|
27
27
|
expectations = selects.map { |s| model.send(s.keys.first, column.name) }
|
28
28
|
expectations = [expectations] if expectations.length > 1
|
29
|
-
|
30
|
-
|
29
|
+
|
30
|
+
if math_column
|
31
|
+
assert_equal expectations.map { |a| a.map { |b| b.round(9) } },
|
32
|
+
calculations.map { |a| a.map { |b| b.round(9) } }
|
33
|
+
else
|
34
|
+
assert_equal expectations.map { |b| b.round(9) },
|
35
|
+
calculations.map { |b| b.round(9) }
|
36
|
+
end
|
31
37
|
end
|
32
38
|
|
33
39
|
test '#calculate.json params[:where]' do
|
@@ -53,22 +59,23 @@ module StandardAPI
|
|
53
59
|
# calculations
|
54
60
|
end
|
55
61
|
|
56
|
-
test '#calculate.json
|
62
|
+
test '#calculate.json mask_for' do
|
57
63
|
# This is just to instance @controller
|
58
64
|
get resource_path(:calculate)
|
59
65
|
|
60
|
-
# If #
|
61
|
-
# test other's implementation of #
|
62
|
-
return if @controller.method(:
|
66
|
+
# If #mask isn't defined by StandardAPI we don't know how to
|
67
|
+
# test other's implementation of #mask_for. Return and don't test.
|
68
|
+
return if @controller.method(:mask_for).owner != StandardAPI
|
63
69
|
|
64
70
|
m = create_model
|
65
71
|
|
66
|
-
@controller.
|
72
|
+
@controller.define_singleton_method(:mask_for) do |table_name|
|
73
|
+
{ id: m.id + 100 }
|
74
|
+
end
|
67
75
|
selects = [{ count: :id}, { maximum: :id }, { minimum: :id }, { average: :id }]
|
68
76
|
get :calculate, select: selects, format: 'json'
|
69
77
|
assert_response :ok
|
70
78
|
assert_equal [[0, nil, nil, nil]], @controller.instance_variable_get('@calculations')
|
71
|
-
@controller.current_mask.delete(plural_name)
|
72
79
|
end
|
73
80
|
|
74
81
|
end
|
@@ -22,7 +22,7 @@ module StandardAPI
|
|
22
22
|
json = JSON.parse(response.body)
|
23
23
|
assert json.is_a?(Hash)
|
24
24
|
|
25
|
-
|
25
|
+
create_attributes(m.reload).select { |x| attrs.keys.map(&:to_s).include?(x) }.each do |key, value|
|
26
26
|
message = "Model / Attribute: #{m.class.name}##{key}"
|
27
27
|
if value.is_a?(BigDecimal)
|
28
28
|
assert_equal_or_nil normalize_to_json(m, key, attrs[key.to_sym]).to_s.to_f, json[key.to_s].to_s.to_f, message
|
@@ -53,9 +53,9 @@ module StandardAPI
|
|
53
53
|
json = JSON.parse(response.body)
|
54
54
|
assert json.is_a?(Hash)
|
55
55
|
m.reload
|
56
|
-
|
56
|
+
create_attributes(m).select { |x| attrs.keys.map(&:to_s).include?(x) }.each do |key, value|
|
57
57
|
message = "Model / Attribute: #{m.class.name}##{key}"
|
58
|
-
assert_equal_or_nil normalize_attribute(m, key, attrs[key.to_sym]), value, message
|
58
|
+
assert_equal_or_nil normalize_attribute(m, key, attrs[key.to_sym]), normalize_attribute(m, key, value), message
|
59
59
|
end
|
60
60
|
end
|
61
61
|
end
|
@@ -64,8 +64,7 @@ module StandardAPI
|
|
64
64
|
trait = FactoryBot.factories[singular_name].definition.defined_traits.any? { |x| x.name.to_s == 'invalid' }
|
65
65
|
|
66
66
|
if !trait
|
67
|
-
|
68
|
-
warn("No invalid trait for #{model.name}. Skipping invalid tests")
|
67
|
+
skip("No invalid trait for #{model.name}. Skipping invalid tests")
|
69
68
|
return
|
70
69
|
end
|
71
70
|
|
@@ -106,8 +105,7 @@ module StandardAPI
|
|
106
105
|
trait = FactoryBot.factories[singular_name].definition.defined_traits.any? { |x| x.name.to_s == 'invalid' }
|
107
106
|
|
108
107
|
if !trait
|
109
|
-
|
110
|
-
warn("No invalid trait for #{model.name}. Skipping invalid tests")
|
108
|
+
skip("No invalid trait for #{model.name}. Skipping invalid tests")
|
111
109
|
return
|
112
110
|
end
|
113
111
|
|
@@ -146,7 +144,7 @@ module StandardAPI
|
|
146
144
|
next if !association
|
147
145
|
|
148
146
|
if ['belongs_to', 'has_one'].include?(association.macro.to_s)
|
149
|
-
|
147
|
+
create_attributes(m.send(included)) do |key, value|
|
150
148
|
assert_equal json[included.to_s][key.to_s], normalize_to_json(m, key, value)
|
151
149
|
end
|
152
150
|
else
|
@@ -160,7 +158,7 @@ module StandardAPI
|
|
160
158
|
nil
|
161
159
|
end
|
162
160
|
|
163
|
-
|
161
|
+
create_attributes(m2).each do |key, value|
|
164
162
|
message = "Model / Attribute: #{m2.class.name}##{key}"
|
165
163
|
if m_json[key.to_s].nil?
|
166
164
|
assert_nil normalize_to_json(m2, key, value), message
|
@@ -13,23 +13,35 @@ module StandardAPI
|
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
16
|
-
test '#destroy.json
|
16
|
+
test '#destroy.json mask_for' do
|
17
17
|
m = create_model
|
18
18
|
|
19
19
|
# This is just to instance @controller
|
20
20
|
get resource_path(:show, id: m.id, format: 'json')
|
21
21
|
|
22
|
-
# If #
|
23
|
-
# test other's implementation of #
|
24
|
-
return if @controller.method(:
|
22
|
+
# If #mask_for isn't defined by StandardAPI we don't know how to
|
23
|
+
# test other's implementation of #mask_for. Return and don't test.
|
24
|
+
return if @controller.method(:mask_for).owner != StandardAPI
|
25
25
|
|
26
|
-
@controller.
|
26
|
+
@controller.define_singleton_method(:mask_for) do |table_name|
|
27
|
+
{ id: m.id + 1 }
|
28
|
+
end
|
27
29
|
assert_raises(ActiveRecord::RecordNotFound) do
|
28
30
|
delete resource_path(:destroy, id: m.id, format: :json)
|
29
31
|
end
|
30
|
-
@controller.current_mask.delete(plural_name)
|
31
32
|
end
|
32
33
|
|
34
|
+
test '#destroy.json with comma separated ids' do
|
35
|
+
m1 = create_model
|
36
|
+
m2 = create_model
|
37
|
+
m3 = create_model
|
38
|
+
|
39
|
+
assert_difference("#{model.name}.count", -3) do
|
40
|
+
delete resource_path(:destroy, id: "#{m1.id},#{m2.id},#{m3.id}", format: :json)
|
41
|
+
assert_response :no_content
|
42
|
+
assert_equal '', response.body
|
43
|
+
end
|
44
|
+
end
|
33
45
|
end
|
34
46
|
end
|
35
|
-
end
|
47
|
+
end
|
@@ -69,9 +69,12 @@ module StandardAPI
|
|
69
69
|
end
|
70
70
|
|
71
71
|
test '#index.json params[:include]' do
|
72
|
+
next if includes.empty?
|
73
|
+
|
72
74
|
travel_to Time.now do
|
73
75
|
create_model
|
74
76
|
get resource_path(:index, format: :json), params: { limit: 100, include: includes }
|
77
|
+
assert_response :ok
|
75
78
|
|
76
79
|
json = JSON.parse(response.body)[0]
|
77
80
|
assert json.is_a?(Hash)
|
@@ -114,20 +117,21 @@ module StandardAPI
|
|
114
117
|
end
|
115
118
|
end
|
116
119
|
|
117
|
-
test '#index.json
|
120
|
+
test '#index.json mask_for' do
|
118
121
|
# This is just to instance @controller
|
119
122
|
get resource_path(:index, format: :json), params: { limit: 1 }
|
120
123
|
|
121
|
-
# If #
|
122
|
-
# test other's implementation of #
|
123
|
-
return if @controller.method(:
|
124
|
+
# If #mask_for isn't defined by StandardAPI we don't know how to
|
125
|
+
# test other's implementation of #mask_for. Return and don't test.
|
126
|
+
return if @controller.method(:mask_for).owner != StandardAPI
|
124
127
|
|
125
128
|
m = create_model
|
126
|
-
@controller.
|
129
|
+
@controller.define_singleton_method(:mask_for) do |table_name|
|
130
|
+
{ id: m.id }
|
131
|
+
end
|
127
132
|
get :index, format: :json
|
128
133
|
models = @controller.instance_variable_get("@#{plural_name}")
|
129
134
|
assert_equal model.where(id: m.id).sort(required_orders).to_sql, models.to_sql
|
130
|
-
@controller.current_mask.delete(plural_name)
|
131
135
|
end
|
132
136
|
end
|
133
137
|
end
|
@@ -18,7 +18,13 @@ module StandardAPI
|
|
18
18
|
assert_equal_or_nil column.null, actual_column['null']
|
19
19
|
assert_equal_or_nil column.array, actual_column['array']
|
20
20
|
assert_equal_or_nil column.comment, actual_column['comment']
|
21
|
-
|
21
|
+
|
22
|
+
if column.default
|
23
|
+
default = model.connection.lookup_cast_type_from_column(column).deserialize(column.default)
|
24
|
+
assert_equal default, actual_column['default']
|
25
|
+
else
|
26
|
+
assert_nil column.default
|
27
|
+
end
|
22
28
|
end
|
23
29
|
|
24
30
|
assert json['limit']
|
@@ -15,6 +15,7 @@ module StandardAPI
|
|
15
15
|
test '#show.json params[:include]' do
|
16
16
|
m = create_model
|
17
17
|
get resource_path(:show, id: m.id, include: includes, format: :json)
|
18
|
+
assert_response :ok
|
18
19
|
|
19
20
|
json = JSON.parse(response.body)
|
20
21
|
includes.each do |included|
|
@@ -51,22 +52,22 @@ module StandardAPI
|
|
51
52
|
end
|
52
53
|
end
|
53
54
|
|
54
|
-
test '#show.json
|
55
|
+
test '#show.json mask_for' do
|
55
56
|
m = create_model
|
56
57
|
|
57
58
|
# This is just to instance @controller
|
58
59
|
get resource_path(:show, id: m.id, format: :json)
|
59
60
|
|
60
|
-
# If #
|
61
|
-
# test other's implementation of #
|
62
|
-
return if @controller.method(:
|
63
|
-
|
61
|
+
# If #mask_for isn't defined by StandardAPI we don't know how to
|
62
|
+
# test other's implementation of #mask_for. Return and don't test.
|
63
|
+
return if @controller.method(:mask_for).owner != StandardAPI
|
64
64
|
|
65
|
-
@controller.
|
65
|
+
@controller.define_singleton_method(:mask_for) do |table_name|
|
66
|
+
{ id: m.id + 1 }
|
67
|
+
end
|
66
68
|
assert_raises(ActiveRecord::RecordNotFound) do
|
67
69
|
get resource_path(:show, id: m.id, format: :json)
|
68
70
|
end
|
69
|
-
@controller.current_mask.delete(plural_name)
|
70
71
|
end
|
71
72
|
|
72
73
|
end
|
@@ -16,7 +16,7 @@ module StandardAPI
|
|
16
16
|
put resource_path(:update, :id => m.id, format: format), params: { singular_name => attrs }, as: as
|
17
17
|
assert_response :ok, "Updating #{m.class.name} with #{attrs.inspect}"
|
18
18
|
|
19
|
-
|
19
|
+
update_attributes(m.reload).select { |x| attrs.keys.map(&:to_s).include?(x) }.each do |key, value|
|
20
20
|
message = "Model / Attribute: #{m.class.name}##{key}"
|
21
21
|
if value.is_a?(BigDecimal)
|
22
22
|
assert_equal_or_nil normalize_attribute(m, key, attrs[key.to_sym]).to_s.to_f, value.to_s.to_f, message
|
@@ -34,7 +34,7 @@ module StandardAPI
|
|
34
34
|
get resource_path(:show, id: m.id), as: :json
|
35
35
|
|
36
36
|
return if @controller.method(:update).owner != StandardAPI
|
37
|
-
|
37
|
+
|
38
38
|
attrs = attributes_for(singular_name).select{|k,v| !model.readonly_attributes.include?(k.to_s) }
|
39
39
|
create_webmocks(attrs)
|
40
40
|
|
@@ -57,9 +57,9 @@ module StandardAPI
|
|
57
57
|
assert_response :ok, "Updating #{m.class.name} with #{attrs.inspect}"
|
58
58
|
|
59
59
|
# (m.attribute_names & attrs.keys.map(&:to_s)).each do |test_key|
|
60
|
-
|
60
|
+
update_attributes(m.reload).select { |x| attrs.keys.map(&:to_s).include?(x) }.each do |key, value|
|
61
61
|
message = "Model / Attribute: #{m.class.name}##{key}"
|
62
|
-
assert_equal_or_nil normalize_attribute(m, key, attrs[key.to_sym]), value, message
|
62
|
+
assert_equal_or_nil normalize_attribute(m, key, attrs[key.to_sym]), normalize_attribute(m, key, value), message
|
63
63
|
end
|
64
64
|
assert JSON.parse(@response.body).is_a?(Hash)
|
65
65
|
end
|
@@ -68,8 +68,7 @@ module StandardAPI
|
|
68
68
|
trait = FactoryBot.factories[singular_name].definition.defined_traits.any? { |x| x.name.to_s == 'invalid' }
|
69
69
|
|
70
70
|
if !trait
|
71
|
-
|
72
|
-
warn("No invalid trait for #{model.name}. Skipping invalid tests")
|
71
|
+
skip("No invalid trait for #{model.name}. Skipping invalid tests")
|
73
72
|
return
|
74
73
|
end
|
75
74
|
|
@@ -94,7 +93,7 @@ module StandardAPI
|
|
94
93
|
|
95
94
|
put resource_path(:update, :id => m.id, format: format), params: { include: includes, singular_name => attrs }, as: as
|
96
95
|
assert_response :ok, "Updating #{m.class.name} with #{attrs.inspect}"
|
97
|
-
|
96
|
+
|
98
97
|
controller_model = @controller.instance_variable_get("@#{singular_name}")
|
99
98
|
json = JSON.parse(response.body)
|
100
99
|
includes.each do |included|
|
@@ -104,7 +103,7 @@ module StandardAPI
|
|
104
103
|
next if !association
|
105
104
|
|
106
105
|
if ['belongs_to', 'has_one'].include?(association.macro.to_s)
|
107
|
-
|
106
|
+
update_attributes(controller_model.send(included)) do |key, value|
|
108
107
|
message = "Model / Attribute: #{controller_model.send(included).class.name}##{key}"
|
109
108
|
assert_equal json[included.to_s][key.to_s], value, message
|
110
109
|
end
|
@@ -118,7 +117,7 @@ module StandardAPI
|
|
118
117
|
nil
|
119
118
|
end
|
120
119
|
|
121
|
-
|
120
|
+
update_attributes(m).each do |key, value|
|
122
121
|
message = "Model / Attribute: #{m.class.name}##{key}"
|
123
122
|
if m_json[key.to_s].nil?
|
124
123
|
assert_nil normalize_to_json(m, key, value), message
|
@@ -132,21 +131,22 @@ module StandardAPI
|
|
132
131
|
end
|
133
132
|
end
|
134
133
|
|
135
|
-
test '#update.json
|
134
|
+
test '#update.json mask_for' do
|
136
135
|
m = create_model
|
137
136
|
|
138
137
|
# This is just to instance @controller
|
139
138
|
get resource_path(:index, format: :json), params: { limit: 1 }
|
140
139
|
|
141
|
-
# If #
|
142
|
-
# test other's implementation of #
|
143
|
-
return if @controller.method(:
|
140
|
+
# If #mask_for isn't defined by StandardAPI we don't know how to
|
141
|
+
# test other's implementation of #mask_for. Return and don't test.
|
142
|
+
return if @controller.method(:mask_for).owner != StandardAPI
|
144
143
|
|
145
|
-
@controller.
|
144
|
+
@controller.define_singleton_method(:mask_for) do |table_name|
|
145
|
+
{ id: m.id + 1 }
|
146
|
+
end
|
146
147
|
assert_raises(ActiveRecord::RecordNotFound) do
|
147
148
|
put resource_path(:update, :id => m.id), as: :json
|
148
149
|
end
|
149
|
-
@controller.current_mask.delete(plural_name)
|
150
150
|
end
|
151
151
|
|
152
152
|
end
|
@@ -25,8 +25,7 @@ module StandardAPI::TestCase
|
|
25
25
|
end
|
26
26
|
|
27
27
|
begin
|
28
|
-
|
29
|
-
model_class = model_class_name.constantize
|
28
|
+
model_class = klass.name.gsub(/Test$/, '').constantize.model
|
30
29
|
|
31
30
|
klass.send(:filters=, model_class.attribute_names)
|
32
31
|
klass.send(:orders=, model_class.attribute_names)
|
@@ -138,8 +137,19 @@ module StandardAPI::TestCase
|
|
138
137
|
|
139
138
|
def view_attributes(record)
|
140
139
|
return [] if record.nil?
|
141
|
-
record.attributes.select
|
140
|
+
record.attributes.select do |x|
|
141
|
+
!@controller.send(:excludes_for, record.class).include?(x.to_sym)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
def update_attributes(record)
|
146
|
+
return [] if record.nil?
|
147
|
+
record.attributes.select do |x|
|
148
|
+
!record.class.readonly_attributes.include?(x.to_s) &&
|
149
|
+
!@controller.send(:excludes_for, record.class).include?(x.to_sym)
|
150
|
+
end
|
142
151
|
end
|
152
|
+
alias_method :create_attributes, :update_attributes
|
143
153
|
|
144
154
|
module ClassMethods
|
145
155
|
|
data/lib/standard_api/version.rb
CHANGED
@@ -1,26 +1,27 @@
|
|
1
|
-
record.
|
1
|
+
record.attribute_names.each do |name|
|
2
2
|
# Skip if attribute is included in excludes
|
3
|
-
next if defined?(excludes) && excludes[record.model_name.singular.to_sym].try(:find) { |x| x.to_s == name
|
4
|
-
|
3
|
+
next if defined?(excludes) && excludes[record.model_name.singular.to_sym].try(:find) { |x| x.to_s == name }
|
4
|
+
|
5
|
+
serialize_attribute(json, record, name, record.type_for_attribute(name).type)
|
5
6
|
end
|
6
7
|
|
7
8
|
includes.each do |inc, subinc|
|
8
9
|
next if ["limit", "offset", "order", "when", "where", "distinct", "distinct_on"].include?(inc)
|
9
10
|
|
10
|
-
|
11
11
|
case association = record.class.reflect_on_association(inc)
|
12
12
|
when ActiveRecord::Reflection::AbstractReflection
|
13
13
|
if association.collection?
|
14
|
-
can_cache = can_cache_relation?(record
|
15
|
-
json.
|
16
|
-
|
17
|
-
|
14
|
+
can_cache = can_cache_relation?(record, inc, subinc)
|
15
|
+
json.set! inc do
|
16
|
+
json.cache_if!(can_cache, can_cache ? association_cache_key(record, inc, subinc) : nil) do
|
17
|
+
partial = model_partial(association.klass)
|
18
|
+
|
18
19
|
# TODO limit causes preloaded assocations to reload
|
19
20
|
sub_records = record.send(inc)
|
20
21
|
|
21
22
|
sub_records = sub_records.limit(subinc['limit']) if subinc['limit']
|
22
23
|
sub_records = sub_records.offset(subinc['offset']) if subinc['offset']
|
23
|
-
sub_records = sub_records.
|
24
|
+
sub_records = sub_records.reorder(subinc['order']) if subinc['order']
|
24
25
|
sub_records = sub_records.filter(subinc['where']) if subinc['where']
|
25
26
|
sub_records = sub_records.distinct if subinc['distinct']
|
26
27
|
sub_records = sub_records.distinct_on(subinc['distinct_on']) if subinc['distinct_on']
|
@@ -29,17 +30,17 @@ includes.each do |inc, subinc|
|
|
29
30
|
end
|
30
31
|
end
|
31
32
|
else
|
32
|
-
can_cache = can_cache_relation?(record
|
33
|
+
can_cache = can_cache_relation?(record, inc, subinc)
|
33
34
|
if association.is_a?(ActiveRecord::Reflection::BelongsToReflection)
|
34
35
|
can_cache = can_cache && !record.send(association.foreign_key).nil?
|
35
36
|
end
|
36
|
-
json.
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
37
|
+
json.set! inc do
|
38
|
+
json.cache_if!(can_cache, can_cache ? association_cache_key(record, inc, subinc) : nil) do
|
39
|
+
value = record.send(inc)
|
40
|
+
if value.nil?
|
41
|
+
json.null!
|
42
|
+
else
|
43
|
+
partial = model_partial(value)
|
43
44
|
json.partial! partial, partial.split('/').last.to_sym => value, includes: subinc
|
44
45
|
end
|
45
46
|
end
|