forest_liana 7.0.0.beta.4 → 7.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/controllers/forest_liana/actions_controller.rb +2 -2
- data/app/controllers/forest_liana/associations_controller.rb +2 -2
- data/app/controllers/forest_liana/resources_controller.rb +15 -6
- data/app/controllers/forest_liana/scopes_controller.rb +20 -0
- data/app/controllers/forest_liana/smart_actions_controller.rb +39 -3
- data/app/controllers/forest_liana/stats_controller.rb +5 -5
- data/app/services/forest_liana/filters_parser.rb +25 -9
- data/app/services/forest_liana/has_many_dissociator.rb +2 -2
- data/app/services/forest_liana/has_many_getter.rb +2 -2
- data/app/services/forest_liana/leaderboard_stat_getter.rb +20 -14
- data/app/services/forest_liana/line_stat_getter.rb +4 -2
- data/app/services/forest_liana/permissions_checker.rb +3 -4
- data/app/services/forest_liana/permissions_getter.rb +2 -2
- data/app/services/forest_liana/pie_stat_getter.rb +6 -3
- data/app/services/forest_liana/resource_getter.rb +6 -3
- data/app/services/forest_liana/resource_updater.rb +5 -2
- data/app/services/forest_liana/resources_getter.rb +6 -5
- data/app/services/forest_liana/scope_manager.rb +102 -0
- data/app/services/forest_liana/search_query_builder.rb +6 -3
- data/app/services/forest_liana/stat_getter.rb +2 -1
- data/app/services/forest_liana/value_stat_getter.rb +4 -2
- data/config/routes.rb +3 -0
- data/lib/forest_liana/version.rb +1 -1
- data/spec/dummy/app/controllers/forest/islands_controller.rb +5 -0
- data/spec/dummy/config/routes.rb +4 -0
- data/spec/dummy/lib/forest_liana/collections/island.rb +7 -0
- data/spec/requests/actions_controller_spec.rb +100 -12
- data/spec/requests/resources_spec.rb +2 -0
- data/spec/services/forest_liana/filters_parser_spec.rb +27 -1
- data/spec/services/forest_liana/has_many_getter_spec.rb +116 -0
- data/spec/services/forest_liana/line_stat_getter_spec.rb +14 -6
- data/spec/services/forest_liana/permissions_checker_acl_disabled_spec.rb +1 -3
- data/spec/services/forest_liana/pie_stat_getter_spec.rb +114 -0
- data/spec/services/forest_liana/resource_updater_spec.rb +116 -0
- data/spec/services/forest_liana/resources_getter_spec.rb +68 -1
- data/spec/services/forest_liana/scope_manager_spec.rb +232 -0
- data/spec/services/forest_liana/value_stat_getter_spec.rb +96 -0
- metadata +20 -15
- data/app/services/forest_liana/scope_validator.rb +0 -98
- data/test/services/forest_liana/has_many_getter_test.rb +0 -75
- data/test/services/forest_liana/pie_stat_getter_test.rb +0 -29
- data/test/services/forest_liana/resource_updater_test.rb +0 -86
- data/test/services/forest_liana/scope_validator_test.rb +0 -185
- data/test/services/forest_liana/value_stat_getter_test.rb +0 -71
@@ -2,16 +2,19 @@ module ForestLiana
|
|
2
2
|
class ResourceGetter < BaseGetter
|
3
3
|
attr_accessor :record
|
4
4
|
|
5
|
-
def initialize(resource, params)
|
5
|
+
def initialize(resource, params, forest_user)
|
6
6
|
@resource = resource
|
7
7
|
@params = params
|
8
|
-
@collection_name = ForestLiana.name_for(
|
8
|
+
@collection_name = ForestLiana.name_for(resource)
|
9
|
+
@user = forest_user
|
9
10
|
@collection = get_collection(@collection_name)
|
10
11
|
compute_includes()
|
11
12
|
end
|
12
13
|
|
13
14
|
def perform
|
14
|
-
|
15
|
+
records = get_resource().eager_load(@includes)
|
16
|
+
scoped_records = ForestLiana::ScopeManager.apply_scopes_on_records(records, @user, @collection_name, @params[:timezone])
|
17
|
+
@record = scoped_records.find(@params[:id])
|
15
18
|
end
|
16
19
|
end
|
17
20
|
end
|
@@ -3,15 +3,18 @@ module ForestLiana
|
|
3
3
|
attr_accessor :record
|
4
4
|
attr_accessor :errors
|
5
5
|
|
6
|
-
def initialize(resource, params)
|
6
|
+
def initialize(resource, params, forest_user)
|
7
7
|
@resource = resource
|
8
8
|
@params = params
|
9
9
|
@errors = nil
|
10
|
+
@user = forest_user
|
10
11
|
end
|
11
12
|
|
12
13
|
def perform
|
13
14
|
begin
|
14
|
-
|
15
|
+
collection_name = ForestLiana.name_for(@resource)
|
16
|
+
scoped_records = ForestLiana::ScopeManager.apply_scopes_on_records(@resource, @user, collection_name, @params[:timezone])
|
17
|
+
@record = scoped_records.find(@params[:id])
|
15
18
|
|
16
19
|
if has_strong_parameter
|
17
20
|
@record.update(resource_params)
|
@@ -4,7 +4,7 @@ module ForestLiana
|
|
4
4
|
attr_reader :includes
|
5
5
|
attr_reader :records_count
|
6
6
|
|
7
|
-
def initialize(resource, params)
|
7
|
+
def initialize(resource, params, forest_user)
|
8
8
|
@resource = resource
|
9
9
|
@params = params
|
10
10
|
@count_needs_includes = false
|
@@ -14,12 +14,13 @@ module ForestLiana
|
|
14
14
|
@field_names_requested = field_names_requested
|
15
15
|
get_segment
|
16
16
|
compute_includes
|
17
|
-
@
|
17
|
+
@user = forest_user
|
18
|
+
@search_query_builder = SearchQueryBuilder.new(@params, @includes, @collection, forest_user)
|
18
19
|
|
19
20
|
prepare_query
|
20
21
|
end
|
21
22
|
|
22
|
-
def self.get_ids_from_request(params)
|
23
|
+
def self.get_ids_from_request(params, user)
|
23
24
|
attributes = params.dig('data', 'attributes')
|
24
25
|
has_body_attributes = attributes != nil
|
25
26
|
is_select_all_records_query = has_body_attributes && attributes[:all_records] == true
|
@@ -45,11 +46,11 @@ module ForestLiana
|
|
45
46
|
collection: parent_collection_name,
|
46
47
|
id: attributes[:parent_collection_id],
|
47
48
|
association_name: attributes[:parent_association_name],
|
48
|
-
}))
|
49
|
+
}), user)
|
49
50
|
else
|
50
51
|
collection_name = attributes[:collection_name]
|
51
52
|
model = ForestLiana::SchemaUtils.find_model_from_collection_name(collection_name)
|
52
|
-
resources_getter = ForestLiana::ResourcesGetter.new(model, attributes)
|
53
|
+
resources_getter = ForestLiana::ResourcesGetter.new(model, attributes, user)
|
53
54
|
end
|
54
55
|
|
55
56
|
# NOTICE: build IDs list.
|
@@ -0,0 +1,102 @@
|
|
1
|
+
module ForestLiana
|
2
|
+
class ScopeManager
|
3
|
+
@@scopes_cache = Hash.new
|
4
|
+
# 5 minutes exipration cache
|
5
|
+
@@scope_cache_expiration_delta = 300
|
6
|
+
|
7
|
+
def self.apply_scopes_on_records(records, forest_user, collection_name, timezone)
|
8
|
+
scope_filters = get_scope_for_user(forest_user, collection_name, as_string: true)
|
9
|
+
|
10
|
+
return records if scope_filters.blank?
|
11
|
+
|
12
|
+
FiltersParser.new(scope_filters, records, timezone).apply_filters
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.append_scope_for_user(existing_filter, user, collection_name)
|
16
|
+
scope_filter = get_scope_for_user(user, collection_name, as_string: true)
|
17
|
+
filters = [existing_filter, scope_filter].compact
|
18
|
+
|
19
|
+
case filters.length
|
20
|
+
when 0
|
21
|
+
nil
|
22
|
+
when 1
|
23
|
+
filters[0]
|
24
|
+
else
|
25
|
+
"{\"aggregator\":\"and\",\"conditions\":[#{existing_filter},#{scope_filter}]}"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.get_scope_for_user(user, collection_name, as_string: false)
|
30
|
+
raise 'Missing required rendering_id' unless user['rendering_id']
|
31
|
+
raise 'Missing required collection_name' unless collection_name
|
32
|
+
|
33
|
+
collection_scope = get_collection_scope(user['rendering_id'], collection_name)
|
34
|
+
|
35
|
+
return nil unless collection_scope
|
36
|
+
|
37
|
+
filters = format_dynamic_values(user['id'], collection_scope)
|
38
|
+
|
39
|
+
as_string && filters ? JSON.generate(filters) : filters
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.get_collection_scope(rendering_id, collection_name)
|
43
|
+
if !@@scopes_cache[rendering_id]
|
44
|
+
# when scope cache is unset wait for the refresh
|
45
|
+
refresh_scopes_cache(rendering_id)
|
46
|
+
elsif has_cache_expired?(rendering_id)
|
47
|
+
# when cache expired refresh the scopes without waiting for it
|
48
|
+
Thread.new { refresh_scopes_cache(rendering_id) }
|
49
|
+
end
|
50
|
+
|
51
|
+
@@scopes_cache[rendering_id][:scopes][collection_name]
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.has_cache_expired?(rendering_id)
|
55
|
+
rendering_scopes = @@scopes_cache[rendering_id]
|
56
|
+
return true unless rendering_scopes
|
57
|
+
|
58
|
+
second_since_last_fetch = Time.now - rendering_scopes[:fetched_at]
|
59
|
+
second_since_last_fetch >= @@scope_cache_expiration_delta
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.refresh_scopes_cache(rendering_id)
|
63
|
+
scopes = fetch_scopes(rendering_id)
|
64
|
+
@@scopes_cache[rendering_id] = {
|
65
|
+
:fetched_at => Time.now,
|
66
|
+
:scopes => scopes
|
67
|
+
}
|
68
|
+
end
|
69
|
+
|
70
|
+
def self.fetch_scopes(rendering_id)
|
71
|
+
query_parameters = { 'renderingId' => rendering_id }
|
72
|
+
response = ForestLiana::ForestApiRequester.get('/liana/scopes', query: query_parameters)
|
73
|
+
|
74
|
+
if response.is_a?(Net::HTTPOK)
|
75
|
+
JSON.parse(response.body)
|
76
|
+
else
|
77
|
+
raise 'Unable to fetch scopes'
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def self.format_dynamic_values(user_id, collection_scope)
|
82
|
+
filter = collection_scope.dig('scope', 'filter')
|
83
|
+
return nil unless filter
|
84
|
+
|
85
|
+
dynamic_scopes_values = collection_scope.dig('scope', 'dynamicScopesValues')
|
86
|
+
|
87
|
+
# Only goes one level deep as required for now
|
88
|
+
filter['conditions'].map do |condition|
|
89
|
+
value = condition['value']
|
90
|
+
if value.is_a?(String) && value.start_with?('$currentUser')
|
91
|
+
condition['value'] = dynamic_scopes_values.dig('users', user_id, value)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
filter
|
96
|
+
end
|
97
|
+
|
98
|
+
def self.invalidate_scope_cache(rendering_id)
|
99
|
+
@@scopes_cache.delete(rendering_id)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -4,12 +4,13 @@ module ForestLiana
|
|
4
4
|
|
5
5
|
attr_reader :fields_searched
|
6
6
|
|
7
|
-
def initialize(params, includes, collection)
|
7
|
+
def initialize(params, includes, collection, user)
|
8
8
|
@params = params
|
9
9
|
@includes = includes
|
10
10
|
@collection = collection
|
11
11
|
@fields_searched = []
|
12
12
|
@search = @params[:search]
|
13
|
+
@user = user
|
13
14
|
end
|
14
15
|
|
15
16
|
def perform(resource)
|
@@ -18,8 +19,10 @@ module ForestLiana
|
|
18
19
|
ForestLiana::QueryHelper.get_tables_associated_to_relations_name(@resource)
|
19
20
|
@records = search_param
|
20
21
|
|
21
|
-
|
22
|
-
|
22
|
+
filters = ForestLiana::ScopeManager.append_scope_for_user(@params[:filters], @user, @collection.name)
|
23
|
+
|
24
|
+
unless filters.blank?
|
25
|
+
@records = FiltersParser.new(filters, @records, @params[:timezone]).apply_filters
|
23
26
|
end
|
24
27
|
|
25
28
|
if @search
|
@@ -6,8 +6,10 @@ module ForestLiana
|
|
6
6
|
return if @params[:aggregate].blank?
|
7
7
|
resource = get_resource().eager_load(@includes)
|
8
8
|
|
9
|
-
|
10
|
-
|
9
|
+
filters = ForestLiana::ScopeManager.append_scope_for_user(@params[:filters], @user, @resource.name)
|
10
|
+
|
11
|
+
unless filters.blank?
|
12
|
+
filter_parser = FiltersParser.new(filters, resource, @params[:timezone])
|
11
13
|
resource = filter_parser.apply_filters
|
12
14
|
raw_previous_interval = filter_parser.get_previous_interval_condition
|
13
15
|
|
data/config/routes.rb
CHANGED
@@ -20,6 +20,9 @@ ForestLiana::Engine.routes.draw do
|
|
20
20
|
post '/stats/:collection' => 'stats#get'
|
21
21
|
post '/stats' => 'stats#get_with_live_query'
|
22
22
|
|
23
|
+
# Scopes
|
24
|
+
post '/scope-cache-invalidation' => 'scopes#invalidate_scope_cache'
|
25
|
+
|
23
26
|
# Stripe Integration
|
24
27
|
get '(*collection)_stripe_payments' => 'stripe#payments'
|
25
28
|
get ':collection/:id/stripe_payments' => 'stripe#payments'
|
data/lib/forest_liana/version.rb
CHANGED
data/spec/dummy/config/routes.rb
CHANGED
@@ -1,16 +1,42 @@
|
|
1
1
|
require 'rails_helper'
|
2
2
|
|
3
3
|
describe 'Requesting Actions routes', :type => :request do
|
4
|
+
let(:rendering_id) { 13 }
|
5
|
+
let(:scope_filters) { nil }
|
6
|
+
|
4
7
|
before(:each) do
|
5
8
|
allow(ForestLiana::IpWhitelist).to receive(:is_ip_whitelist_retrieved) { true }
|
6
9
|
allow(ForestLiana::IpWhitelist).to receive(:is_ip_valid) { true }
|
7
|
-
Island.create(name: 'Corsica')
|
10
|
+
Island.create(id: 1, name: 'Corsica')
|
11
|
+
|
12
|
+
ForestLiana::ScopeManager.invalidate_scope_cache(rendering_id)
|
13
|
+
allow(ForestLiana::ScopeManager).to receive(:get_scope_for_user).and_return(scope_filters)
|
8
14
|
end
|
9
15
|
|
10
16
|
after(:each) do
|
11
17
|
Island.destroy_all
|
12
18
|
end
|
13
|
-
|
19
|
+
|
20
|
+
let(:token) {
|
21
|
+
JWT.encode({
|
22
|
+
id: 38,
|
23
|
+
email: 'michael.kelso@that70.show',
|
24
|
+
first_name: 'Michael',
|
25
|
+
last_name: 'Kelso',
|
26
|
+
team: 'Operations',
|
27
|
+
rendering_id: rendering_id,
|
28
|
+
exp: Time.now.to_i + 2.weeks.to_i
|
29
|
+
}, ForestLiana.auth_secret, 'HS256')
|
30
|
+
}
|
31
|
+
|
32
|
+
let(:headers) {
|
33
|
+
{
|
34
|
+
'Accept' => 'application/json',
|
35
|
+
'Content-Type' => 'application/json',
|
36
|
+
'Authorization' => "Bearer #{token}"
|
37
|
+
}
|
38
|
+
}
|
39
|
+
|
14
40
|
describe 'hooks' do
|
15
41
|
foo = {
|
16
42
|
field: 'foo',
|
@@ -126,25 +152,25 @@ describe 'Requesting Actions routes', :type => :request do
|
|
126
152
|
}
|
127
153
|
|
128
154
|
it 'should respond 200' do
|
129
|
-
post '/forest/actions/my_action/hooks/load', params: JSON.dump(params), headers:
|
155
|
+
post '/forest/actions/my_action/hooks/load', params: JSON.dump(params), headers: headers
|
130
156
|
expect(response.status).to eq(200)
|
131
157
|
expect(JSON.parse(response.body)).to eq({'fields' => [foo.merge({:value => nil}).stringify_keys]})
|
132
158
|
end
|
133
159
|
|
134
160
|
it 'should respond 500 with bad params' do
|
135
|
-
post '/forest/actions/my_action/hooks/load', params: {}
|
161
|
+
post '/forest/actions/my_action/hooks/load', params: {}, headers: headers
|
136
162
|
expect(response.status).to eq(500)
|
137
163
|
expect(JSON.parse(response.body)).to eq({'error' => 'Error in smart action load hook: cannot retrieve action from collection'})
|
138
164
|
end
|
139
165
|
|
140
166
|
it 'should respond 500 with bad hook result type' do
|
141
|
-
post '/forest/actions/fail_action/hooks/load', params: JSON.dump(params), headers:
|
167
|
+
post '/forest/actions/fail_action/hooks/load', params: JSON.dump(params), headers: headers
|
142
168
|
expect(response.status).to eq(500)
|
143
169
|
expect(JSON.parse(response.body)).to eq({'error' => 'Error in smart action load hook: hook must return an array of fields'})
|
144
170
|
end
|
145
171
|
|
146
172
|
it 'should respond 500 with bad hook result data structure' do
|
147
|
-
post '/forest/actions/cheat_action/hooks/load', params: JSON.dump(params), headers:
|
173
|
+
post '/forest/actions/cheat_action/hooks/load', params: JSON.dump(params), headers: headers
|
148
174
|
expect(response.status).to eq(500)
|
149
175
|
expect(JSON.parse(response.body)).to eq({'error' => 'Error in smart action load hook: hook must return an array of fields'})
|
150
176
|
end
|
@@ -164,7 +190,7 @@ describe 'Requesting Actions routes', :type => :request do
|
|
164
190
|
}
|
165
191
|
|
166
192
|
it 'should respond 200' do
|
167
|
-
post '/forest/actions/my_action/hooks/change', params: JSON.dump(params), headers:
|
193
|
+
post '/forest/actions/my_action/hooks/change', params: JSON.dump(params), headers: headers
|
168
194
|
expect(response.status).to eq(200)
|
169
195
|
expected = updated_foo.clone.merge({:value => 'baz'})
|
170
196
|
expected[:widgetEdit] = nil
|
@@ -173,13 +199,13 @@ describe 'Requesting Actions routes', :type => :request do
|
|
173
199
|
end
|
174
200
|
|
175
201
|
it 'should respond 500 with bad params' do
|
176
|
-
post '/forest/actions/my_action/hooks/change', params: JSON.dump({ data: { attributes: { collection_name: 'Island' }}}), headers:
|
202
|
+
post '/forest/actions/my_action/hooks/change', params: JSON.dump({ data: { attributes: { collection_name: 'Island' }}}), headers: headers
|
177
203
|
expect(response.status).to eq(500)
|
178
204
|
expect(JSON.parse(response.body)).to eq({'error' => 'Error in smart action change hook: fields params is mandatory'})
|
179
205
|
end
|
180
206
|
|
181
207
|
it 'should respond 500 with bad hook result type' do
|
182
|
-
post '/forest/actions/fail_action/hooks/change', params: JSON.dump(params), headers:
|
208
|
+
post '/forest/actions/fail_action/hooks/change', params: JSON.dump(params), headers: headers
|
183
209
|
expect(response.status).to eq(500)
|
184
210
|
expect(JSON.parse(response.body)).to eq({'error' => 'Error in smart action load hook: hook must return an array of fields'})
|
185
211
|
end
|
@@ -196,7 +222,7 @@ describe 'Requesting Actions routes', :type => :request do
|
|
196
222
|
}
|
197
223
|
}
|
198
224
|
}
|
199
|
-
post '/forest/actions/enums_action/hooks/change', params: JSON.dump(p), headers:
|
225
|
+
post '/forest/actions/enums_action/hooks/change', params: JSON.dump(p), headers: headers
|
200
226
|
expect(response.status).to eq(200)
|
201
227
|
|
202
228
|
expected_enum = updated_enum.clone.merge({ :enums => %w[c d e], :value => nil, :widgetEdit => nil})
|
@@ -219,7 +245,7 @@ describe 'Requesting Actions routes', :type => :request do
|
|
219
245
|
}
|
220
246
|
}
|
221
247
|
}
|
222
|
-
post '/forest/actions/multiple_enums_action/hooks/change', params: JSON.dump(p), headers:
|
248
|
+
post '/forest/actions/multiple_enums_action/hooks/change', params: JSON.dump(p), headers: headers
|
223
249
|
expect(response.status).to eq(200)
|
224
250
|
|
225
251
|
expected_multiple_enum = updated_multiple_enum.clone.merge({ :enums => %w[c d z], :widgetEdit => nil, :value => %w[c]})
|
@@ -243,7 +269,7 @@ describe 'Requesting Actions routes', :type => :request do
|
|
243
269
|
}
|
244
270
|
}
|
245
271
|
|
246
|
-
post '/forest/actions/multiple_enums_action/hooks/change', params: JSON.dump(p), headers:
|
272
|
+
post '/forest/actions/multiple_enums_action/hooks/change', params: JSON.dump(p), headers: headers
|
247
273
|
expect(response.status).to eq(200)
|
248
274
|
|
249
275
|
expected_multiple_enum = wrongly_updated_multiple_enum.clone.merge({ :enums => %w[c d z], :widgetEdit => nil, :value => nil })
|
@@ -255,4 +281,66 @@ describe 'Requesting Actions routes', :type => :request do
|
|
255
281
|
end
|
256
282
|
end
|
257
283
|
end
|
284
|
+
|
285
|
+
describe 'calling the action' do
|
286
|
+
before(:each) do
|
287
|
+
allow_any_instance_of(ForestLiana::PermissionsChecker).to receive(:is_authorized?) { true }
|
288
|
+
end
|
289
|
+
|
290
|
+
let(:all_records) { false }
|
291
|
+
let(:params) {
|
292
|
+
{
|
293
|
+
data: {
|
294
|
+
attributes: {
|
295
|
+
collection_name: 'Island',
|
296
|
+
ids: ['1'],
|
297
|
+
all_records: all_records,
|
298
|
+
smart_action_id: 'Island-Test'
|
299
|
+
},
|
300
|
+
type: 'custom-action-requests'
|
301
|
+
},
|
302
|
+
timezone: 'Europe/Paris'
|
303
|
+
}
|
304
|
+
}
|
305
|
+
|
306
|
+
describe 'without scopes' do
|
307
|
+
it 'should respond 200 and perform the action' do
|
308
|
+
post '/forest/actions/test', params: JSON.dump(params), headers: headers
|
309
|
+
expect(response.status).to eq(200)
|
310
|
+
expect(JSON.parse(response.body)).to eq({'success' => 'You are OK.'})
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
314
|
+
describe 'with scopes' do
|
315
|
+
describe 'when record is in scope' do
|
316
|
+
let(:scope_filters) { JSON.generate({ field: 'name', operator: 'equal', value: 'Corsica' }) }
|
317
|
+
|
318
|
+
it 'should respond 200 and perform the action' do
|
319
|
+
post '/forest/actions/test', params: JSON.dump(params), headers: headers
|
320
|
+
expect(response.status).to eq(200)
|
321
|
+
expect(JSON.parse(response.body)).to eq({'success' => 'You are OK.'})
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
325
|
+
describe 'when record is out of scope' do
|
326
|
+
let(:scope_filters) { JSON.generate({ field: 'name', operator: 'equal', value: 'Ré' }) }
|
327
|
+
|
328
|
+
it 'should respond 400 and NOT perform the action' do
|
329
|
+
post '/forest/actions/test', params: JSON.dump(params), headers: headers
|
330
|
+
expect(response.status).to eq(400)
|
331
|
+
expect(JSON.parse(response.body)).to eq({ 'error' => 'Smart Action: target record not found' })
|
332
|
+
end
|
333
|
+
|
334
|
+
describe 'and all_records are targeted' do
|
335
|
+
let(:all_records) { true }
|
336
|
+
|
337
|
+
it 'should respond 200 and perform the action' do
|
338
|
+
post '/forest/actions/test', params: JSON.dump(params), headers: headers
|
339
|
+
expect(response.status).to eq(200)
|
340
|
+
expect(JSON.parse(response.body)).to eq({'success' => 'You are OK.'})
|
341
|
+
end
|
342
|
+
end
|
343
|
+
end
|
344
|
+
end
|
345
|
+
end
|
258
346
|
end
|