forest_liana 6.6.0 → 7.0.0.beta.3
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 +44 -29
- data/app/controllers/forest_liana/application_controller.rb +2 -2
- data/app/controllers/forest_liana/associations_controller.rb +1 -1
- data/app/controllers/forest_liana/base_controller.rb +1 -1
- data/app/controllers/forest_liana/resources_controller.rb +7 -6
- data/app/serializers/forest_liana/intercom_attribute_serializer.rb +1 -1
- data/app/serializers/forest_liana/intercom_conversation_serializer.rb +1 -1
- data/app/serializers/forest_liana/mixpanel_event_serializer.rb +1 -1
- data/app/serializers/forest_liana/serializer_factory.rb +1 -1
- data/app/serializers/forest_liana/stat_serializer.rb +1 -1
- data/app/serializers/forest_liana/stripe_bank_account_serializer.rb +1 -1
- data/app/serializers/forest_liana/stripe_card_serializer.rb +1 -1
- data/app/serializers/forest_liana/stripe_invoice_serializer.rb +1 -1
- data/app/serializers/forest_liana/stripe_payment_serializer.rb +1 -1
- data/app/serializers/forest_liana/stripe_subscription_serializer.rb +1 -1
- data/app/services/forest_liana/apimap_sorter.rb +1 -0
- data/app/services/forest_liana/permissions_checker.rb +40 -34
- data/app/services/forest_liana/permissions_formatter.rb +1 -1
- data/app/services/forest_liana/permissions_getter.rb +1 -4
- data/app/services/forest_liana/smart_action_field_validator.rb +49 -0
- data/config/initializers/errors.rb +17 -0
- data/lib/forest_liana/bootstrapper.rb +2 -2
- data/lib/forest_liana/schema_file_updater.rb +8 -0
- data/lib/forest_liana/version.rb +1 -1
- data/spec/lib/forest_liana/schema_file_updater_spec.rb +94 -0
- data/spec/requests/actions_controller_spec.rb +22 -20
- data/spec/services/forest_liana/permissions_checker_acl_disabled_spec.rb +45 -69
- data/spec/services/forest_liana/permissions_checker_acl_enabled_spec.rb +39 -63
- data/spec/services/forest_liana/permissions_checker_live_queries_spec.rb +3 -3
- data/spec/services/forest_liana/permissions_formatter_spec.rb +11 -11
- data/spec/services/forest_liana/smart_action_field_validator_spec.rb +70 -0
- metadata +119 -117
- data/app/helpers/forest_liana/is_same_data_structure_helper.rb +0 -44
- data/spec/helpers/forest_liana/is_same_data_structure_helper_spec.rb +0 -87
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3115af32c59b5bf0e32218f3d40cb661809d58316714654520bde1bfee235098
|
4
|
+
data.tar.gz: 850a6dbc96d8ff1e74ffe7970dc98ff2bd721d6b834087353052208722b55810
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ea018906c77a41db7a7dc1022cd6485e5f9a293763c1822a64706092f8be74d1046230198af6d0041277cb8f2b852115633b96f622f69a192dc99f911cd31178
|
7
|
+
data.tar.gz: cf12c7f0821b3e9232fe884e3d5c04470f6467d4a571ffc88ce2f6e36c9edac6704dcdd9a0c431dd222435bac1c31b512a5585bc9fb99f141e309e41f9ff8696
|
@@ -27,34 +27,45 @@ module ForestLiana
|
|
27
27
|
end
|
28
28
|
|
29
29
|
def get_smart_action_load_ctx(fields)
|
30
|
-
fields = fields.
|
31
|
-
ForestLiana::WidgetsHelper.set_field_widget(
|
32
|
-
|
30
|
+
fields = fields.map do |field|
|
31
|
+
ForestLiana::WidgetsHelper.set_field_widget(field)
|
32
|
+
field[:value] = nil unless field[:value]
|
33
|
+
field
|
33
34
|
end
|
34
35
|
{:record => get_record, :fields => fields}
|
35
36
|
end
|
36
37
|
|
37
|
-
def get_smart_action_change_ctx(fields)
|
38
|
-
|
39
|
-
|
38
|
+
def get_smart_action_change_ctx(fields, field_changed)
|
39
|
+
found_field_changed = fields.find{|field| field[:field] == field_changed}
|
40
|
+
fields = fields.map do |field|
|
41
|
+
field = field.permit!.to_h.symbolize_keys
|
40
42
|
ForestLiana::WidgetsHelper.set_field_widget(field)
|
41
|
-
|
43
|
+
field
|
42
44
|
end
|
43
|
-
{:record => get_record, :fields => fields}
|
45
|
+
{:record => get_record, :field_changed => found_field_changed, :fields => fields}
|
44
46
|
end
|
45
47
|
|
46
|
-
def handle_result(result,
|
47
|
-
if result.nil? || !result.is_a?(
|
48
|
-
return render status: 500, json: { error: 'Error in smart action load hook: hook must return an
|
48
|
+
def handle_result(result, action)
|
49
|
+
if result.nil? || !result.is_a?(Array)
|
50
|
+
return render status: 500, json: { error: 'Error in smart action load hook: hook must return an array of fields' }
|
49
51
|
end
|
50
|
-
|
51
|
-
|
52
|
-
|
52
|
+
|
53
|
+
# Validate that the fields are well formed.
|
54
|
+
begin
|
55
|
+
# action.hooks[:change] is a hashmap here
|
56
|
+
# to do the validation, only the hook names are require
|
57
|
+
change_hooks_name = action.hooks[:change].nil? ? nil : action.hooks[:change].keys
|
58
|
+
ForestLiana::SmartActionFieldValidator.validate_smart_action_fields(result, action.name, change_hooks_name)
|
59
|
+
rescue ForestLiana::Errors::SmartActionInvalidFieldError => invalid_field_error
|
60
|
+
FOREST_LOGGER.warn invalid_field_error.message
|
61
|
+
rescue ForestLiana::Errors::SmartActionInvalidFieldHookError => invalid_hook_error
|
62
|
+
FOREST_LOGGER.error invalid_hook_error.message
|
63
|
+
return render status: 500, json: { error: invalid_hook_error.message }
|
53
64
|
end
|
54
65
|
|
55
66
|
# Apply result on fields (transform the object back to an array), preserve order.
|
56
|
-
fields =
|
57
|
-
updated_field = result[field[:field]
|
67
|
+
fields = result.map do |field|
|
68
|
+
updated_field = result.find{|f| f[:field] == field[:field]}
|
58
69
|
|
59
70
|
# Reset `value` when not present in `enums` (which means `enums` has changed).
|
60
71
|
if updated_field[:enums].is_a?(Array)
|
@@ -72,7 +83,7 @@ module ForestLiana
|
|
72
83
|
updated_field
|
73
84
|
end
|
74
85
|
|
75
|
-
render serializer: nil, json: { fields: fields}, status: :ok
|
86
|
+
render serializer: nil, json: { fields: fields }, status: :ok
|
76
87
|
end
|
77
88
|
|
78
89
|
def load
|
@@ -81,14 +92,13 @@ module ForestLiana
|
|
81
92
|
if !action
|
82
93
|
render status: 500, json: {error: 'Error in smart action load hook: cannot retrieve action from collection'}
|
83
94
|
else
|
84
|
-
#
|
95
|
+
# Get the smart action hook load context
|
85
96
|
context = get_smart_action_load_ctx(action.fields)
|
86
|
-
formatted_fields = context[:fields].clone # clone for following test on is_same_data_structure
|
87
97
|
|
88
98
|
# Call the user-defined load hook.
|
89
99
|
result = action.hooks[:load].(context)
|
90
100
|
|
91
|
-
handle_result(result,
|
101
|
+
handle_result(result, action)
|
92
102
|
end
|
93
103
|
end
|
94
104
|
|
@@ -96,17 +106,22 @@ module ForestLiana
|
|
96
106
|
action = get_action(params[:collectionName])
|
97
107
|
|
98
108
|
if !action
|
99
|
-
render status: 500, json: {error: 'Error in smart action change hook: cannot retrieve action from collection'}
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
109
|
+
return render status: 500, json: {error: 'Error in smart action change hook: cannot retrieve action from collection'}
|
110
|
+
elsif params[:fields].nil?
|
111
|
+
return render status: 500, json: {error: 'Error in smart action change hook: fields params is mandatory'}
|
112
|
+
elsif !params[:fields].is_a?(Array)
|
113
|
+
return render status: 500, json: {error: 'Error in smart action change hook: fields params must be an array'}
|
114
|
+
end
|
104
115
|
|
105
|
-
|
106
|
-
|
116
|
+
# Get the smart action hook change context
|
117
|
+
context = get_smart_action_change_ctx(params[:fields], params[:changedField])
|
107
118
|
|
108
|
-
|
109
|
-
|
119
|
+
field_changed_hook = context[:field_changed][:hook]
|
120
|
+
|
121
|
+
# Call the user-defined change hook.
|
122
|
+
result = action.hooks[:change][field_changed_hook].(context)
|
123
|
+
|
124
|
+
handle_result(result, action)
|
110
125
|
end
|
111
126
|
end
|
112
127
|
end
|
@@ -34,14 +34,14 @@ module ForestLiana
|
|
34
34
|
|
35
35
|
def serialize_model(record, options = {})
|
36
36
|
options[:is_collection] = false
|
37
|
-
json = JSONAPI::Serializer.serialize(record, options)
|
37
|
+
json = ForestAdmin::JSONAPI::Serializer.serialize(record, options)
|
38
38
|
|
39
39
|
force_utf8_encoding(json)
|
40
40
|
end
|
41
41
|
|
42
42
|
def serialize_models(records, options = {}, fields_searched = [])
|
43
43
|
options[:is_collection] = true
|
44
|
-
json = JSONAPI::Serializer.serialize(records, options)
|
44
|
+
json = ForestAdmin::JSONAPI::Serializer.serialize(records, options)
|
45
45
|
|
46
46
|
if options[:params] && options[:params][:search]
|
47
47
|
# NOTICE: Add the Smart Fields with a 'String' type.
|
@@ -41,7 +41,7 @@ module ForestLiana
|
|
41
41
|
updater.perform
|
42
42
|
|
43
43
|
if updater.errors
|
44
|
-
render serializer: nil, json: JSONAPI::Serializer.serialize_errors(
|
44
|
+
render serializer: nil, json: ForestAdmin::JSONAPI::Serializer.serialize_errors(
|
45
45
|
updater.errors), status: 422
|
46
46
|
else
|
47
47
|
head :no_content
|
@@ -24,7 +24,7 @@ module ForestLiana
|
|
24
24
|
end
|
25
25
|
end
|
26
26
|
rescue ForestLiana::Errors::ExpectedError => exception
|
27
|
-
error_data = JSONAPI::Serializer.serialize_errors([{
|
27
|
+
error_data = ForestAdmin::JSONAPI::Serializer.serialize_errors([{
|
28
28
|
status: exception.error_code,
|
29
29
|
detail: exception.message
|
30
30
|
}])
|
@@ -41,7 +41,7 @@ module ForestLiana
|
|
41
41
|
status: :unprocessable_entity, serializer: nil
|
42
42
|
rescue ForestLiana::Errors::ExpectedError => error
|
43
43
|
error.display_error
|
44
|
-
error_data = JSONAPI::Serializer.serialize_errors([{
|
44
|
+
error_data = ForestAdmin::JSONAPI::Serializer.serialize_errors([{
|
45
45
|
status: error.error_code,
|
46
46
|
detail: error.message
|
47
47
|
}])
|
@@ -73,7 +73,7 @@ module ForestLiana
|
|
73
73
|
status: :unprocessable_entity, serializer: nil
|
74
74
|
rescue ForestLiana::Errors::ExpectedError => error
|
75
75
|
error.display_error
|
76
|
-
error_data = JSONAPI::Serializer.serialize_errors([{
|
76
|
+
error_data = ForestAdmin::JSONAPI::Serializer.serialize_errors([{
|
77
77
|
status: error.error_code,
|
78
78
|
detail: error.message
|
79
79
|
}])
|
@@ -108,12 +108,12 @@ module ForestLiana
|
|
108
108
|
creator.perform
|
109
109
|
|
110
110
|
if creator.errors
|
111
|
-
render serializer: nil, json: JSONAPI::Serializer.serialize_errors(
|
111
|
+
render serializer: nil, json: ForestAdmin::JSONAPI::Serializer.serialize_errors(
|
112
112
|
creator.errors), status: 400
|
113
113
|
elsif creator.record.valid?
|
114
114
|
render serializer: nil, json: render_record_jsonapi(creator.record)
|
115
115
|
else
|
116
|
-
render serializer: nil, json: JSONAPI::Serializer.serialize_errors(
|
116
|
+
render serializer: nil, json: ForestAdmin::JSONAPI::Serializer.serialize_errors(
|
117
117
|
creator.record.errors), status: 400
|
118
118
|
end
|
119
119
|
rescue => error
|
@@ -131,12 +131,12 @@ module ForestLiana
|
|
131
131
|
updater.perform
|
132
132
|
|
133
133
|
if updater.errors
|
134
|
-
render serializer: nil, json: JSONAPI::Serializer.serialize_errors(
|
134
|
+
render serializer: nil, json: ForestAdmin::JSONAPI::Serializer.serialize_errors(
|
135
135
|
updater.errors), status: 400
|
136
136
|
elsif updater.record.valid?
|
137
137
|
render serializer: nil, json: render_record_jsonapi(updater.record)
|
138
138
|
else
|
139
|
-
render serializer: nil, json: JSONAPI::Serializer.serialize_errors(
|
139
|
+
render serializer: nil, json: ForestAdmin::JSONAPI::Serializer.serialize_errors(
|
140
140
|
updater.record.errors), status: 400
|
141
141
|
end
|
142
142
|
rescue => error
|
@@ -249,6 +249,7 @@ module ForestLiana
|
|
249
249
|
{
|
250
250
|
user_id: user['id'],
|
251
251
|
filters: collection_list_request[:filters],
|
252
|
+
segmentQuery: collection_list_request[:segmentQuery],
|
252
253
|
}
|
253
254
|
end
|
254
255
|
end
|
@@ -1,12 +1,12 @@
|
|
1
1
|
module ForestLiana
|
2
2
|
class PermissionsChecker
|
3
3
|
@@permissions_cached = Hash.new
|
4
|
-
@@
|
4
|
+
@@renderings_cached = Hash.new
|
5
5
|
@@roles_acl_activated = false
|
6
|
-
|
6
|
+
|
7
7
|
@@expiration_in_seconds = (ENV['FOREST_PERMISSIONS_EXPIRATION_IN_SECONDS'] || 3600).to_i
|
8
8
|
|
9
|
-
def initialize(resource, permission_name, rendering_id, user_id: nil, smart_action_request_info: nil, collection_list_parameters:
|
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
10
|
|
11
11
|
@collection_name = resource.present? ? ForestLiana.name_for(resource) : nil
|
12
12
|
@permission_name = permission_name
|
@@ -39,13 +39,16 @@ module ForestLiana
|
|
39
39
|
permissions['data'] = ForestLiana::PermissionsFormatter.convert_to_new_format(permissions['data'], @rendering_id)
|
40
40
|
@@permissions_cached[@rendering_id] = permissions
|
41
41
|
end
|
42
|
-
|
42
|
+
|
43
|
+
# NOTICE: Add stats permissions to the RenderingPermissions
|
44
|
+
permissions['data']['renderings'][@rendering_id]['stats'] = permissions['stats']
|
45
|
+
add_rendering_permissions_to_cache(permissions)
|
43
46
|
end
|
44
47
|
|
45
|
-
def
|
48
|
+
def add_rendering_permissions_to_cache(permissions)
|
46
49
|
permissions['data']['renderings'].keys.each { |rendering_id|
|
47
|
-
@@
|
48
|
-
@@
|
50
|
+
@@renderings_cached[rendering_id] = permissions['data']['renderings'][rendering_id]
|
51
|
+
@@renderings_cached[rendering_id]['last_fetch'] = Time.now
|
49
52
|
} if permissions['data']['renderings']
|
50
53
|
end
|
51
54
|
|
@@ -59,20 +62,17 @@ module ForestLiana
|
|
59
62
|
return stat_with_parameters_allowed?
|
60
63
|
end
|
61
64
|
|
62
|
-
|
63
|
-
|
64
65
|
if permissions && permissions[@collection_name] &&
|
65
66
|
permissions[@collection_name]['collection']
|
66
67
|
if @permission_name === 'actions'
|
67
68
|
return smart_action_allowed?(permissions[@collection_name]['actions'])
|
68
69
|
else
|
69
70
|
if @permission_name === 'browseEnabled'
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
return false unless are_scopes_valid?(scope_permissions)
|
71
|
+
refresh_rendering_cache if rendering_cache_expired?
|
72
|
+
|
73
|
+
# NOTICE: In this case we need to check that that query is allowed
|
74
|
+
if @collection_list_parameters[:segmentQuery].present?
|
75
|
+
return false unless segment_query_allowed?
|
76
76
|
end
|
77
77
|
end
|
78
78
|
return is_user_allowed(permissions[@collection_name]['collection'][@permission_name])
|
@@ -82,23 +82,27 @@ module ForestLiana
|
|
82
82
|
end
|
83
83
|
end
|
84
84
|
|
85
|
-
def
|
86
|
-
@@
|
87
|
-
@@
|
88
|
-
@@
|
85
|
+
def get_segments_in_permissions
|
86
|
+
@@renderings_cached[@rendering_id] &&
|
87
|
+
@@renderings_cached[@rendering_id][@collection_name] &&
|
88
|
+
@@renderings_cached[@rendering_id][@collection_name]['segments']
|
89
89
|
end
|
90
90
|
|
91
|
-
def
|
92
|
-
return true unless @@
|
91
|
+
def rendering_cache_expired?
|
92
|
+
return true unless @@renderings_cached[@rendering_id] && @@renderings_cached[@rendering_id]['last_fetch']
|
93
93
|
|
94
|
-
elapsed_seconds = date_difference_in_seconds(Time.now, @@
|
94
|
+
elapsed_seconds = date_difference_in_seconds(Time.now, @@renderings_cached[@rendering_id]['last_fetch'])
|
95
95
|
elapsed_seconds >= @@expiration_in_seconds
|
96
96
|
end
|
97
97
|
|
98
|
-
# This will happen only on rolesACLActivated (as
|
99
|
-
def
|
98
|
+
# This will happen only on rolesACLActivated (as segments cache will always be up to date on disabled)
|
99
|
+
def refresh_rendering_cache
|
100
100
|
permissions = ForestLiana::PermissionsGetter::get_permissions_for_rendering(@rendering_id, rendering_specific_only: true)
|
101
|
-
|
101
|
+
|
102
|
+
# NOTICE: Add stats permissions to the RenderingPermissions
|
103
|
+
permissions['data']['renderings'][@rendering_id]['stats'] = permissions['stats']
|
104
|
+
|
105
|
+
add_rendering_permissions_to_cache(permissions)
|
102
106
|
end
|
103
107
|
|
104
108
|
# When acl disabled permissions are stored and retrieved by rendering
|
@@ -112,12 +116,12 @@ module ForestLiana
|
|
112
116
|
end
|
113
117
|
|
114
118
|
def get_live_query_permissions_content
|
115
|
-
permissions =
|
119
|
+
permissions = @@renderings_cached[@rendering_id]
|
116
120
|
permissions && permissions['stats'] && permissions['stats']['queries']
|
117
121
|
end
|
118
122
|
|
119
123
|
def get_stat_with_parameters_content(statPermissionType)
|
120
|
-
permissions =
|
124
|
+
permissions = @@renderings_cached[@rendering_id]
|
121
125
|
permissions && permissions['stats'] && permissions['stats'][statPermissionType]
|
122
126
|
end
|
123
127
|
|
@@ -154,11 +158,13 @@ module ForestLiana
|
|
154
158
|
is_user_allowed(smart_action_permissions['triggerEnabled'])
|
155
159
|
end
|
156
160
|
|
157
|
-
def
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
161
|
+
def segment_query_allowed?
|
162
|
+
segments_queries_permissions = get_segments_in_permissions
|
163
|
+
|
164
|
+
return false unless segments_queries_permissions
|
165
|
+
|
166
|
+
# NOTICE: @query_request_info matching an existing segment query
|
167
|
+
return segments_queries_permissions.include? @collection_list_parameters[:segmentQuery]
|
162
168
|
end
|
163
169
|
|
164
170
|
def live_query_allowed?
|
@@ -177,7 +183,7 @@ module ForestLiana
|
|
177
183
|
return false unless pool_permissions
|
178
184
|
|
179
185
|
# NOTICE: equivalent to Object.values in js & removes nil values
|
180
|
-
array_permission_infos = @query_request_info.values.
|
186
|
+
array_permission_infos = @query_request_info.values.select{ |x| !x.nil? }
|
181
187
|
|
182
188
|
# NOTICE: Is there any pool_permissions containing the array_permission_infos
|
183
189
|
return pool_permissions.any? {
|
@@ -201,7 +207,7 @@ module ForestLiana
|
|
201
207
|
# Used only for testing purpose
|
202
208
|
def self.empty_cache
|
203
209
|
@@permissions_cached = Hash.new
|
204
|
-
@@
|
210
|
+
@@renderings_cached = Hash.new
|
205
211
|
@@roles_acl_activated = false
|
206
212
|
@@expiration_in_seconds = (ENV['FOREST_PERMISSIONS_EXPIRATION_IN_SECONDS'] || 3600).to_i
|
207
213
|
end
|