forest_liana 7.0.0.beta.3 → 7.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/forest_liana/actions_controller.rb +20 -18
  3. data/app/controllers/forest_liana/application_controller.rb +0 -9
  4. data/app/controllers/forest_liana/associations_controller.rb +2 -2
  5. data/app/controllers/forest_liana/resources_controller.rb +15 -6
  6. data/app/controllers/forest_liana/scopes_controller.rb +20 -0
  7. data/app/controllers/forest_liana/smart_actions_controller.rb +39 -3
  8. data/app/controllers/forest_liana/stats_controller.rb +5 -5
  9. data/app/services/forest_liana/filters_parser.rb +8 -4
  10. data/app/services/forest_liana/has_many_dissociator.rb +2 -2
  11. data/app/services/forest_liana/has_many_getter.rb +2 -2
  12. data/app/services/forest_liana/leaderboard_stat_getter.rb +20 -14
  13. data/app/services/forest_liana/line_stat_getter.rb +4 -2
  14. data/app/services/forest_liana/permissions_checker.rb +3 -4
  15. data/app/services/forest_liana/permissions_getter.rb +2 -2
  16. data/app/services/forest_liana/pie_stat_getter.rb +6 -3
  17. data/app/services/forest_liana/resource_getter.rb +6 -3
  18. data/app/services/forest_liana/resource_updater.rb +5 -2
  19. data/app/services/forest_liana/resources_getter.rb +6 -5
  20. data/app/services/forest_liana/scope_manager.rb +102 -0
  21. data/app/services/forest_liana/search_query_builder.rb +6 -3
  22. data/app/services/forest_liana/stat_getter.rb +2 -1
  23. data/app/services/forest_liana/value_stat_getter.rb +4 -2
  24. data/config/routes.rb +3 -1
  25. data/lib/forest_liana/version.rb +1 -1
  26. data/spec/dummy/app/controllers/forest/islands_controller.rb +5 -0
  27. data/spec/dummy/config/routes.rb +4 -0
  28. data/spec/dummy/lib/forest_liana/collections/island.rb +7 -0
  29. data/spec/requests/actions_controller_spec.rb +144 -23
  30. data/spec/requests/resources_spec.rb +2 -0
  31. data/spec/services/forest_liana/filters_parser_spec.rb +1 -1
  32. data/spec/services/forest_liana/has_many_getter_spec.rb +116 -0
  33. data/spec/services/forest_liana/line_stat_getter_spec.rb +14 -6
  34. data/spec/services/forest_liana/permissions_checker_acl_disabled_spec.rb +1 -3
  35. data/spec/services/forest_liana/pie_stat_getter_spec.rb +114 -0
  36. data/spec/services/forest_liana/resource_updater_spec.rb +116 -0
  37. data/spec/services/forest_liana/resources_getter_spec.rb +68 -1
  38. data/spec/services/forest_liana/scope_manager_spec.rb +232 -0
  39. data/spec/services/forest_liana/value_stat_getter_spec.rb +96 -0
  40. metadata +20 -15
  41. data/app/services/forest_liana/scope_validator.rb +0 -98
  42. data/test/services/forest_liana/has_many_getter_test.rb +0 -75
  43. data/test/services/forest_liana/pie_stat_getter_test.rb +0 -29
  44. data/test/services/forest_liana/resource_updater_test.rb +0 -86
  45. data/test/services/forest_liana/scope_validator_test.rb +0 -185
  46. data/test/services/forest_liana/value_stat_getter_test.rb +0 -71
@@ -1,98 +0,0 @@
1
- module ForestLiana
2
- class ScopeValidator
3
- def initialize(scope_permissions, users_variable_values)
4
- @scope_filters = scope_permissions
5
- @users_variable_values = users_variable_values
6
- end
7
-
8
- def is_scope_in_request?(scope_request)
9
- begin
10
- filters = JSON.parse(scope_request[:filters])
11
- rescue JSON::ParserError
12
- raise ForestLiana::Errors::HTTP422Error.new('Invalid filters JSON format')
13
- end
14
- @computed_scope = compute_condition_filters_from_scope(scope_request[:user_id])
15
-
16
- # NOTICE: Perfom a travel in the request condition filters tree to find the scope
17
- tagged_scope_filters = get_scope_found_in_request(filters)
18
-
19
- # NOTICE: Permission system always send an aggregator even if there is only one condition
20
- # In that case, if the condition is valid, then request was not edited
21
- return !tagged_scope_filters.nil? if @scope_filters['conditions'].length == 1
22
-
23
- # NOTICE: If there is more than one condition, do a final validation on the condition filters
24
- return tagged_scope_filters != nil &&
25
- tagged_scope_filters[:aggregator] == @scope_filters['aggregator'] &&
26
- tagged_scope_filters[:conditions] &&
27
- tagged_scope_filters[:conditions].length == @scope_filters['conditions'].length
28
- end
29
-
30
- private
31
-
32
- def compute_condition_filters_from_scope(user_id)
33
- computed_condition_filters = @scope_filters.clone
34
- computed_condition_filters['conditions'].each do |condition|
35
- if condition.include?('value') &&
36
- !condition['value'].nil? &&
37
- condition['value'].instance_of?(String) &&
38
- condition['value'].start_with?('$') &&
39
- @users_variable_values.include?(user_id)
40
- condition['value'] = @users_variable_values[user_id][condition['value']]
41
- end
42
- end
43
- return computed_condition_filters
44
- end
45
-
46
- def get_scope_found_in_request(filters)
47
- return nil unless filters
48
- return search_scope_aggregation(filters)
49
- end
50
-
51
- def search_scope_aggregation(node)
52
- ensure_valid_aggregation(node)
53
-
54
- return is_scope_condition?(node) unless node['aggregator']
55
-
56
- # NOTICE: Remove conditions that are not from the scope
57
- filtered_conditions = node['conditions'].map { |condition|
58
- search_scope_aggregation(condition)
59
- }.select { |condition|
60
- condition
61
- }
62
-
63
- # NOTICE: If there is only one condition filter left and its current aggregator is
64
- # an "and", this condition filter is the searched scope
65
- if (filtered_conditions.length == 1 &&
66
- filtered_conditions.first.is_a?(Hash) &&
67
- filtered_conditions.first.include?(:aggregator) &&
68
- node['aggregator'] == 'and')
69
- return filtered_conditions.first
70
- end
71
-
72
- # NOTICE: Otherwise, validate if the current node is the scope and return nil
73
- # if it's not
74
- return (filtered_conditions.length == @scope_filters['conditions'].length &&
75
- node['aggregator'] == @scope_filters['aggregator']) ?
76
- { aggregator: node['aggregator'], conditions: filtered_conditions } :
77
- nil
78
- end
79
-
80
- def is_scope_condition?(condition)
81
- ensure_valid_condition(condition)
82
- return @computed_scope['conditions'].include?(condition)
83
- end
84
-
85
- def ensure_valid_aggregation(node)
86
- raise ForestLiana::Errors::HTTP422Error.new('Filters cannot be a raw value') unless node.is_a?(Hash)
87
- raise_empty_condition_in_filter_error if node.empty?
88
- end
89
-
90
- def ensure_valid_condition(condition)
91
- raise_empty_condition_in_filter_error if condition.empty?
92
- raise ForestLiana::Errors::HTTP422Error.new('Condition cannot be a raw value') unless condition.is_a?(Hash)
93
- unless condition['field'].is_a?(String) and condition['operator'].is_a?(String)
94
- raise ForestLiana::Errors::HTTP422Error.new('Invalid condition format')
95
- end
96
- end
97
- end
98
- end
@@ -1,75 +0,0 @@
1
- module ForestLiana
2
- class HasManyGetterTest < ActiveSupport::TestCase
3
-
4
- collectionOwner = ForestLiana::Model::Collection.new({
5
- name: 'Owner',
6
- fields: []
7
- })
8
-
9
- collectionTree = ForestLiana::Model::Collection.new({
10
- name: 'Tree',
11
- fields: []
12
- })
13
-
14
- ForestLiana.apimap << collectionOwner
15
- ForestLiana.apimap << collectionTree
16
- ForestLiana.models << Owner
17
- ForestLiana.models << Tree
18
-
19
- test 'HasMany Getter page 1 size 15' do
20
- association = Owner.reflect_on_association(:trees)
21
-
22
- getter = HasManyGetter.new(Owner, association, {
23
- id: 1,
24
- association_name: 'trees',
25
- page: { size: 15, number: 1 },
26
- timezone: 'America/Nome'
27
- })
28
- getter.perform
29
- records = getter.records
30
- count = getter.count
31
-
32
- assert records.count == 3
33
- assert count = 3
34
- assert records.first.id == 7
35
- end
36
-
37
- test 'HasMany Getter with sort parameter' do
38
- association = Owner.reflect_on_association(:trees)
39
-
40
- getter = HasManyGetter.new(Owner, association, {
41
- id: 1,
42
- association_name: 'trees',
43
- sort: '-id',
44
- page: { size: 15, number: 1 },
45
- timezone: 'America/Nome'
46
- })
47
- getter.perform
48
- records = getter.records
49
- count = getter.count
50
-
51
- assert records.count == 3
52
- assert count = 3
53
- assert records.first.id == 8
54
- end
55
-
56
- test 'HasMany Getter with search parameter' do
57
- association = Owner.reflect_on_association(:trees)
58
-
59
- getter = HasManyGetter.new(Owner, association, {
60
- id: 1,
61
- association_name: 'trees',
62
- search: 'Fir',
63
- page: { size: 15, number: 1 },
64
- timezone: 'America/Nome'
65
- })
66
- getter.perform
67
- records = getter.records
68
- count = getter.count
69
-
70
- assert records.count == 1
71
- assert count = 1
72
- assert records.first.id == 8
73
- end
74
- end
75
- end
@@ -1,29 +0,0 @@
1
- module ForestLiana
2
- class PieStatGetterTest < ActiveSupport::TestCase
3
- test 'Pie stat getter with an aggregate on a boolean field' do
4
- stat = PieStatGetter.new(BooleanField, {
5
- type: "Pie",
6
- collection: "boolean_field",
7
- timezone: "Europe/Paris",
8
- aggregate: "Count",
9
- group_by_field: "field"
10
- })
11
-
12
- stat.perform
13
- assert stat.record.value.count == 0
14
- end
15
-
16
- test 'Pie stat getter with an aggregate on a foreign key' do
17
- stat = PieStatGetter.new(BelongsToField, {
18
- type: "Pie",
19
- collection: "belongs_to_field",
20
- timezone: "Europe/Paris",
21
- aggregate: "Count",
22
- group_by_field: "has_one_field_id"
23
- })
24
-
25
- stat.perform
26
- assert stat.record.value.count == 30
27
- end
28
- end
29
- end
@@ -1,86 +0,0 @@
1
- module ForestLiana
2
- class ResourceUpdaterTest < ActiveSupport::TestCase
3
-
4
- collection = ForestLiana::Model::Collection.new({
5
- name: 'SerializeField',
6
- fields: [{
7
- type: 'String',
8
- field: 'field'
9
- }]
10
- })
11
-
12
- ForestLiana.apimap << collection
13
- ForestLiana.models << SerializeField
14
-
15
- test 'Update a record on a "serialize" attribute with a missing value' do
16
- params = ActionController::Parameters.new(
17
- id: 1,
18
- data: {
19
- id: 1,
20
- type: "SerializeField",
21
- attributes: {}
22
- }
23
- )
24
- updater = ResourceUpdater.new(SerializeField, params)
25
- updater.perform
26
-
27
- assert updater.record.valid?
28
- assert updater.record.field == "value 1"
29
- end
30
-
31
- test 'Update a record on a "serialize" attribute with a null value' do
32
- params = ActionController::Parameters.new(
33
- id: 1,
34
- data: {
35
- id: 1,
36
- type: "SerializeField",
37
- attributes: {
38
- field: nil
39
- }
40
- }
41
- )
42
- updater = ResourceUpdater.new(SerializeField, params)
43
- updater.perform
44
-
45
- assert updater.record.valid?
46
- assert updater.record.field == []
47
- end
48
-
49
- test 'Update a record on a "serialize" attribute with a bad format value' do
50
- params = ActionController::Parameters.new(
51
- id: 1,
52
- data: {
53
- id: 1,
54
- type: "SerializeField",
55
- attributes: {
56
- field: "Lucas"
57
- }
58
- }
59
- )
60
- updater = ResourceUpdater.new(SerializeField, params)
61
- updater.perform
62
-
63
- assert updater.record.valid?
64
- assert updater.record.field == "value 1"
65
- assert updater.errors[0][:detail] == "Bad format for 'field' attribute value."
66
- end
67
-
68
- test 'Update a record on a "serialize" attribute with a well formated value' do
69
- params = ActionController::Parameters.new(
70
- id: 1,
71
- data: {
72
- id: 1,
73
- type: "SerializeField",
74
- attributes: {
75
- field: "[\"test\", \"test\"]"
76
- }
77
- }
78
- )
79
- updater = ResourceUpdater.new(SerializeField, params)
80
- updater.perform
81
-
82
- assert updater.record.valid?
83
- assert updater.record.field == ["test", "test"]
84
- end
85
- end
86
- end
@@ -1,185 +0,0 @@
1
- module ForestLiana
2
- class ScopeValidatorTest < ActiveSupport::TestCase
3
- test 'Request with aggregated condition filters should be allowed if it matches the scope exactly' do
4
- scope_validator = ForestLiana::ScopeValidator.new({
5
- 'aggregator' => 'and',
6
- 'conditions' => [
7
- { 'field' => 'name', 'value' => 'john', 'operator' => 'equal' },
8
- { 'field' => 'price', 'value' => '2500', 'operator' => 'equal' }
9
- ]
10
- }, [])
11
-
12
- allowed = scope_validator.is_scope_in_request?({
13
- user_id: '1',
14
- filters: JSON.generate({
15
- aggregator: 'and',
16
- conditions: [
17
- { field: 'name', value: 'john', operator: 'equal' },
18
- { field: 'price', value: '2500', operator: 'equal' }
19
- ]
20
- })
21
- })
22
- assert allowed == true
23
- end
24
-
25
- test 'Request with simple condition filter should be allowed if it matches the scope exactly' do
26
- scope_validator = ForestLiana::ScopeValidator.new({
27
- 'aggregator' => 'and',
28
- 'conditions' => [
29
- { 'field' => 'field', 'value' => 'value', 'operator' => 'equal' }
30
- ]
31
- }, [])
32
- allowed = scope_validator.is_scope_in_request?({
33
- user_id: '1',
34
- filters: JSON.generate({
35
- field: 'field', value: 'value', operator: 'equal'
36
- })
37
- })
38
- assert allowed == true
39
- end
40
-
41
- test 'Request with multiples condition filters should be allowed if it contains the scope ' do
42
- scope_validator = ForestLiana::ScopeValidator.new({
43
- 'aggregator' => 'and',
44
- 'conditions' => [
45
- { 'field' => 'name', 'value' => 'doe', 'operator' => 'equal' }
46
- ]
47
- }, []
48
- )
49
-
50
- allowed = scope_validator.is_scope_in_request?({
51
- user_id: '1',
52
- filters: JSON.generate({
53
- aggregator: 'and',
54
- conditions: [
55
- { field: 'name', value: 'doe', operator: 'equal' },
56
- { field: 'field2', value: 'value2', operator: 'equal' }
57
- ]
58
- })
59
- })
60
- assert allowed == true
61
- end
62
-
63
- test 'Request with dynamic user values should be allowed if it matches the scope exactly' do
64
- scope_validator = ForestLiana::ScopeValidator.new({
65
- 'aggregator' => 'and',
66
- 'conditions' => [
67
- { 'field' => 'name', 'value' => '$currentUser.lastname', 'operator' => 'equal' }
68
- ],
69
- }, {
70
- '1' => { '$currentUser.lastname' => 'john' }
71
- })
72
-
73
- allowed = scope_validator.is_scope_in_request?({
74
- user_id: '1',
75
- filters: JSON.generate({
76
- 'field' => 'name', 'value' => 'john', 'operator' => 'equal'
77
- })
78
- })
79
- assert allowed == true
80
- end
81
-
82
- test 'Request with multiples aggregation and dynamic values should be allowed if it contains the scope' do
83
- scope_validator = ForestLiana::ScopeValidator.new({
84
- 'aggregator' => 'or',
85
- 'conditions' => [
86
- { 'field' => 'price', 'value' => '2500', 'operator' => 'equal' },
87
- { 'field' => 'name', 'value' => '$currentUser.lastname', 'operator' => 'equal' }
88
- ]
89
- }, {
90
- '1' => { '$currentUser.lastname' => 'john' }
91
- })
92
-
93
- allowed = scope_validator.is_scope_in_request?({
94
- user_id: '1',
95
- filters: JSON.generate({
96
- aggregator: 'and',
97
- conditions: [
98
- { field: 'field', value: 'value', operator: 'equal' },
99
- {
100
- aggregator: 'or',
101
- conditions: [
102
- { field: 'price', value: '2500', operator: 'equal' },
103
- { field: 'name', value: 'john', operator: 'equal' }
104
- ]
105
- }
106
- ]
107
- })
108
- })
109
- assert allowed == true
110
- end
111
-
112
- test 'Request that does not match the expect scope should not be allowed' do
113
- scope_validator = ForestLiana::ScopeValidator.new({
114
- 'aggregator' => 'and',
115
- 'conditions' => [
116
- { 'field' => 'name', 'value' => 'john', 'operator' => 'equal' },
117
- { 'field' => 'price', 'value' => '2500', 'operator' => 'equal' }
118
- ]
119
- }, [])
120
-
121
- allowed = scope_validator.is_scope_in_request?({
122
- user_id: '1',
123
- filters: JSON.generate({
124
- aggregator: 'and',
125
- conditions: [
126
- { field: 'name', value: 'definitely_not_john', operator: 'equal' },
127
- { field: 'price', value: '0', operator: 'equal' }
128
- ]
129
- })
130
- })
131
- assert allowed == false
132
- end
133
-
134
- test 'Request that are missing part of the scope should not be allowed' do
135
- scope_validator = ForestLiana::ScopeValidator.new({
136
- 'aggregator' => 'and',
137
- 'conditions' => [
138
- { 'field' => 'name', 'value' => 'john', 'operator' => 'equal' },
139
- { 'field' => 'price', 'value' => '2500', 'operator' => 'equal' }
140
- ]
141
- }, [])
142
-
143
- allowed = scope_validator.is_scope_in_request?({
144
- user_id: '1',
145
- filters: JSON.generate({
146
- aggregator: 'and',
147
- conditions: [
148
- { field: 'name', value: 'john', operator: 'equal' },
149
- ]
150
- })
151
- })
152
- assert allowed == false
153
- end
154
-
155
- test 'Request that does not have a top aggregator being "and" should not be allowed' do
156
- scope_validator = ForestLiana::ScopeValidator.new({
157
- 'aggregator' => 'and',
158
- 'conditions' => [
159
- { 'field' => 'price', 'value' => '2500', 'operator' => 'equal' },
160
- { 'field' => 'name', 'value' => '$currentUser.lastname', 'operator' => 'equal' }
161
- ]
162
- }, {
163
- '1' => { '$currentUser.lastname' => 'john' }
164
- })
165
-
166
- allowed = scope_validator.is_scope_in_request?({
167
- user_id: '1',
168
- filters: JSON.generate({
169
- aggregator: 'or',
170
- conditions: [
171
- { field: 'field', value: 'value', operator: 'equal' },
172
- {
173
- aggregator: 'and',
174
- conditions: [
175
- { field: 'price', value: '2500', operator: 'equal' },
176
- { field: 'name', value: 'john', operator: 'equal' }
177
- ]
178
- }
179
- ]
180
- })
181
- })
182
- assert allowed == false
183
- end
184
- end
185
- end