forest_liana 7.0.0.beta.3 → 7.0.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/app/controllers/forest_liana/actions_controller.rb +20 -18
- data/app/controllers/forest_liana/application_controller.rb +0 -9
- 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 +8 -4
- 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 -1
- 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 +144 -23
- data/spec/requests/resources_spec.rb +2 -0
- data/spec/services/forest_liana/filters_parser_spec.rb +1 -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
@@ -7,7 +7,6 @@ module ForestLiana
|
|
7
7
|
@@expiration_in_seconds = (ENV['FOREST_PERMISSIONS_EXPIRATION_IN_SECONDS'] || 3600).to_i
|
8
8
|
|
9
9
|
def initialize(resource, permission_name, rendering_id, user_id: nil, smart_action_request_info: nil, collection_list_parameters: Hash.new, query_request_info: nil)
|
10
|
-
|
11
10
|
@collection_name = resource.present? ? ForestLiana.name_for(resource) : nil
|
12
11
|
@permission_name = permission_name
|
13
12
|
@rendering_id = rendering_id
|
@@ -119,7 +118,7 @@ module ForestLiana
|
|
119
118
|
permissions = @@renderings_cached[@rendering_id]
|
120
119
|
permissions && permissions['stats'] && permissions['stats']['queries']
|
121
120
|
end
|
122
|
-
|
121
|
+
|
123
122
|
def get_stat_with_parameters_content(statPermissionType)
|
124
123
|
permissions = @@renderings_cached[@rendering_id]
|
125
124
|
permissions && permissions['stats'] && permissions['stats'][statPermissionType]
|
@@ -163,7 +162,7 @@ module ForestLiana
|
|
163
162
|
|
164
163
|
return false unless segments_queries_permissions
|
165
164
|
|
166
|
-
# NOTICE: @query_request_info matching an existing segment query
|
165
|
+
# NOTICE: @query_request_info matching an existing segment query
|
167
166
|
return segments_queries_permissions.include? @collection_list_parameters[:segmentQuery]
|
168
167
|
end
|
169
168
|
|
@@ -172,7 +171,7 @@ module ForestLiana
|
|
172
171
|
|
173
172
|
return false unless live_queries_permissions
|
174
173
|
|
175
|
-
# NOTICE: @query_request_info matching an existing live query
|
174
|
+
# NOTICE: @query_request_info matching an existing live query
|
176
175
|
return live_queries_permissions.include? @query_request_info
|
177
176
|
end
|
178
177
|
|
@@ -23,7 +23,7 @@ module ForestLiana
|
|
23
23
|
# },
|
24
24
|
# },
|
25
25
|
# },
|
26
|
-
#
|
26
|
+
# renderings => {
|
27
27
|
# {rendering_id} => {
|
28
28
|
# {collection_id} => {
|
29
29
|
# segments => ['query1', 'query2']
|
@@ -32,7 +32,7 @@ module ForestLiana
|
|
32
32
|
# }
|
33
33
|
# }
|
34
34
|
# With `rendering_specific_only` this returns only the permissions related data specific to the provided rendering
|
35
|
-
# For now this only includes scopes
|
35
|
+
# For now this only includes scopes (but scopes are not used anymore in permissions)
|
36
36
|
def get_permissions_for_rendering(rendering_id, rendering_specific_only: false)
|
37
37
|
begin
|
38
38
|
query_parameters = { 'renderingId' => rendering_id }
|
@@ -7,8 +7,10 @@ module ForestLiana
|
|
7
7
|
timezone_offset = @params[:timezone].to_i
|
8
8
|
resource = get_resource().eager_load(@includes)
|
9
9
|
|
10
|
-
|
11
|
-
|
10
|
+
filters = ForestLiana::ScopeManager.append_scope_for_user(@params[:filters], @user, @resource.name)
|
11
|
+
|
12
|
+
unless filters.blank?
|
13
|
+
resource = FiltersParser.new(filters, resource, @params[:timezone]).apply_filters
|
12
14
|
end
|
13
15
|
|
14
16
|
result = resource
|
@@ -53,7 +55,8 @@ module ForestLiana
|
|
53
55
|
if @params[:aggregate].downcase == 'sum'
|
54
56
|
field = @params[:aggregate_field].downcase
|
55
57
|
else
|
56
|
-
|
58
|
+
# `count_id` is required only for rails v5
|
59
|
+
field = Rails::VERSION::MAJOR == 5 || @includes.size > 0 ? 'id' : 'all'
|
57
60
|
end
|
58
61
|
"#{@params[:aggregate].downcase}_#{field} #{order}"
|
59
62
|
end
|
@@ -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'
|
@@ -57,7 +60,6 @@ ForestLiana::Engine.routes.draw do
|
|
57
60
|
delete ':collection', to: router
|
58
61
|
|
59
62
|
# Smart Actions forms value
|
60
|
-
post 'actions/:action_name/values' => 'actions#values'
|
61
63
|
post 'actions/:action_name/hooks/load' => 'actions#load'
|
62
64
|
post 'actions/:action_name/hooks/change' => 'actions#change'
|
63
65
|
end
|
data/lib/forest_liana/version.rb
CHANGED
data/spec/dummy/config/routes.rb
CHANGED
@@ -1,23 +1,41 @@
|
|
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
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
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
|
+
}
|
21
39
|
|
22
40
|
describe 'hooks' do
|
23
41
|
foo = {
|
@@ -127,28 +145,32 @@ describe 'Requesting Actions routes', :type => :request do
|
|
127
145
|
island.actions = [action, fail_action, cheat_action, enums_action, multiple_enums_action]
|
128
146
|
|
129
147
|
describe 'call /load' do
|
130
|
-
params = {
|
148
|
+
params = {
|
149
|
+
data: {
|
150
|
+
attributes: { ids: [1], collection_name: 'Island' }
|
151
|
+
}
|
152
|
+
}
|
131
153
|
|
132
154
|
it 'should respond 200' do
|
133
|
-
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
|
134
156
|
expect(response.status).to eq(200)
|
135
157
|
expect(JSON.parse(response.body)).to eq({'fields' => [foo.merge({:value => nil}).stringify_keys]})
|
136
158
|
end
|
137
159
|
|
138
160
|
it 'should respond 500 with bad params' do
|
139
|
-
post '/forest/actions/my_action/hooks/load', params: {}
|
161
|
+
post '/forest/actions/my_action/hooks/load', params: {}, headers: headers
|
140
162
|
expect(response.status).to eq(500)
|
141
163
|
expect(JSON.parse(response.body)).to eq({'error' => 'Error in smart action load hook: cannot retrieve action from collection'})
|
142
164
|
end
|
143
165
|
|
144
166
|
it 'should respond 500 with bad hook result type' do
|
145
|
-
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
|
146
168
|
expect(response.status).to eq(500)
|
147
169
|
expect(JSON.parse(response.body)).to eq({'error' => 'Error in smart action load hook: hook must return an array of fields'})
|
148
170
|
end
|
149
171
|
|
150
172
|
it 'should respond 500 with bad hook result data structure' do
|
151
|
-
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
|
152
174
|
expect(response.status).to eq(500)
|
153
175
|
expect(JSON.parse(response.body)).to eq({'error' => 'Error in smart action load hook: hook must return an array of fields'})
|
154
176
|
end
|
@@ -156,10 +178,19 @@ describe 'Requesting Actions routes', :type => :request do
|
|
156
178
|
|
157
179
|
describe 'call /change' do
|
158
180
|
updated_foo = foo.clone.merge({:previousValue => nil, :value => 'bar'})
|
159
|
-
params = {
|
181
|
+
params = {
|
182
|
+
data: {
|
183
|
+
attributes: {
|
184
|
+
ids: [1],
|
185
|
+
fields: [updated_foo],
|
186
|
+
collection_name: 'Island',
|
187
|
+
changed_field: 'foo'
|
188
|
+
}
|
189
|
+
}
|
190
|
+
}
|
160
191
|
|
161
192
|
it 'should respond 200' do
|
162
|
-
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
|
163
194
|
expect(response.status).to eq(200)
|
164
195
|
expected = updated_foo.clone.merge({:value => 'baz'})
|
165
196
|
expected[:widgetEdit] = nil
|
@@ -168,21 +199,30 @@ describe 'Requesting Actions routes', :type => :request do
|
|
168
199
|
end
|
169
200
|
|
170
201
|
it 'should respond 500 with bad params' do
|
171
|
-
post '/forest/actions/my_action/hooks/change', params: JSON.dump({
|
202
|
+
post '/forest/actions/my_action/hooks/change', params: JSON.dump({ data: { attributes: { collection_name: 'Island' }}}), headers: headers
|
172
203
|
expect(response.status).to eq(500)
|
173
204
|
expect(JSON.parse(response.body)).to eq({'error' => 'Error in smart action change hook: fields params is mandatory'})
|
174
205
|
end
|
175
206
|
|
176
207
|
it 'should respond 500 with bad hook result type' do
|
177
|
-
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
|
178
209
|
expect(response.status).to eq(500)
|
179
210
|
expect(JSON.parse(response.body)).to eq({'error' => 'Error in smart action load hook: hook must return an array of fields'})
|
180
211
|
end
|
181
212
|
|
182
213
|
it 'should reset value when enums has changed' do
|
183
214
|
updated_enum = enum.clone.merge({:previousValue => nil, :value => 'a'}) # set value to a
|
184
|
-
p = {
|
185
|
-
|
215
|
+
p = {
|
216
|
+
data: {
|
217
|
+
attributes: {
|
218
|
+
ids: [1],
|
219
|
+
fields: [updated_foo, updated_enum],
|
220
|
+
collection_name: 'Island',
|
221
|
+
changed_field: 'foo'
|
222
|
+
}
|
223
|
+
}
|
224
|
+
}
|
225
|
+
post '/forest/actions/enums_action/hooks/change', params: JSON.dump(p), headers: headers
|
186
226
|
expect(response.status).to eq(200)
|
187
227
|
|
188
228
|
expected_enum = updated_enum.clone.merge({ :enums => %w[c d e], :value => nil, :widgetEdit => nil})
|
@@ -195,8 +235,17 @@ describe 'Requesting Actions routes', :type => :request do
|
|
195
235
|
|
196
236
|
it 'should not reset value when every enum values are in the enums definition' do
|
197
237
|
updated_multiple_enum = multiple_enum.clone.merge({:previousValue => nil, :value => %w[c]})
|
198
|
-
p = {
|
199
|
-
|
238
|
+
p = {
|
239
|
+
data: {
|
240
|
+
attributes: {
|
241
|
+
ids: [1],
|
242
|
+
fields: [foo, updated_multiple_enum],
|
243
|
+
collection_name: 'Island',
|
244
|
+
changed_field: 'foo'
|
245
|
+
}
|
246
|
+
}
|
247
|
+
}
|
248
|
+
post '/forest/actions/multiple_enums_action/hooks/change', params: JSON.dump(p), headers: headers
|
200
249
|
expect(response.status).to eq(200)
|
201
250
|
|
202
251
|
expected_multiple_enum = updated_multiple_enum.clone.merge({ :enums => %w[c d z], :widgetEdit => nil, :value => %w[c]})
|
@@ -209,8 +258,18 @@ describe 'Requesting Actions routes', :type => :request do
|
|
209
258
|
|
210
259
|
it 'should reset value when one of the enum values is not in the enums definition' do
|
211
260
|
wrongly_updated_multiple_enum = multiple_enum.clone.merge({:previousValue => nil, :value => %w[a b]})
|
212
|
-
p = {
|
213
|
-
|
261
|
+
p = {
|
262
|
+
data: {
|
263
|
+
attributes: {
|
264
|
+
ids: [1],
|
265
|
+
fields: [foo, wrongly_updated_multiple_enum],
|
266
|
+
collection_name: 'Island',
|
267
|
+
changed_field: 'foo'
|
268
|
+
}
|
269
|
+
}
|
270
|
+
}
|
271
|
+
|
272
|
+
post '/forest/actions/multiple_enums_action/hooks/change', params: JSON.dump(p), headers: headers
|
214
273
|
expect(response.status).to eq(200)
|
215
274
|
|
216
275
|
expected_multiple_enum = wrongly_updated_multiple_enum.clone.merge({ :enums => %w[c d z], :widgetEdit => nil, :value => nil })
|
@@ -222,4 +281,66 @@ describe 'Requesting Actions routes', :type => :request do
|
|
222
281
|
end
|
223
282
|
end
|
224
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
|
225
346
|
end
|