forest_liana 9.1.10 → 9.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/services/forest_liana/ability/permission/smart_action_checker.rb +1 -1
- data/app/services/forest_liana/filters_parser.rb +1 -5
- data/app/services/forest_liana/leaderboard_stat_getter.rb +1 -1
- data/app/services/forest_liana/line_stat_getter.rb +1 -1
- data/app/services/forest_liana/pie_stat_getter.rb +1 -1
- data/app/services/forest_liana/scope_manager.rb +34 -58
- data/app/services/forest_liana/utils/context_variables.rb +41 -0
- data/app/services/forest_liana/utils/context_variables_injector.rb +53 -0
- data/app/services/forest_liana/value_stat_getter.rb +1 -1
- data/lib/forest_liana/version.rb +1 -1
- data/spec/requests/actions_controller_spec.rb +32 -4
- data/spec/requests/count_spec.rb +1 -1
- data/spec/requests/resources_spec.rb +6 -7
- data/spec/requests/stats_spec.rb +29 -28
- data/spec/services/forest_liana/filters_parser_spec.rb +35 -35
- data/spec/services/forest_liana/has_many_getter_spec.rb +13 -11
- data/spec/services/forest_liana/line_stat_getter_spec.rb +1 -1
- data/spec/services/forest_liana/pie_stat_getter_spec.rb +11 -11
- data/spec/services/forest_liana/resource_updater_spec.rb +25 -21
- data/spec/services/forest_liana/resources_getter_spec.rb +10 -10
- data/spec/services/forest_liana/scope_manager_spec.rb +243 -152
- data/spec/services/forest_liana/utils/context_variables_injector_spec.rb +107 -0
- data/spec/services/forest_liana/utils/context_variables_spec.rb +43 -0
- data/spec/services/forest_liana/value_stat_getter_spec.rb +11 -11
- metadata +12 -6
@@ -5,7 +5,7 @@ module ForestLiana
|
|
5
5
|
let(:timezone) { 'Europe/Paris' }
|
6
6
|
let(:resource) { Tree }
|
7
7
|
let(:filters) { {} }
|
8
|
-
let(:filter_parser) { described_class.new(filters
|
8
|
+
let(:filter_parser) { described_class.new(filters, resource, timezone) }
|
9
9
|
let(:simple_condition_1) { { 'field' => 'name', 'operator' => 'contains', 'value' => 'Tree' } }
|
10
10
|
let(:simple_condition_2) { { 'field' => 'name', 'operator' => 'ends_with', 'value' => '3' } }
|
11
11
|
let(:simple_condition_3) { { 'field' => 'age', 'operator' => 'greater_than', 'value' => 2 } }
|
@@ -34,91 +34,91 @@ module ForestLiana
|
|
34
34
|
let(:parsed_filters) { filter_parser.apply_filters }
|
35
35
|
|
36
36
|
context 'on valid filters' do
|
37
|
-
context 'single
|
37
|
+
context 'single conditions' do
|
38
38
|
context 'not_equal' do
|
39
|
-
let(:filters) { { field
|
39
|
+
let(:filters) { { 'field' => 'age', 'operator' => 'not_equal', 'value' => 4 } }
|
40
40
|
it { expect(parsed_filters.count).to eq 2 }
|
41
41
|
end
|
42
42
|
|
43
43
|
context 'equal' do
|
44
|
-
let(:filters) { { field
|
44
|
+
let(:filters) { { 'field' => 'age', 'operator' => 'equal', 'value' => 4 } }
|
45
45
|
it { expect(parsed_filters.count).to eq 1 }
|
46
46
|
end
|
47
47
|
|
48
48
|
context 'greater_than' do
|
49
|
-
let(:filters) { { field
|
49
|
+
let(:filters) { { 'field' => 'age', 'operator' => 'greater_than', 'value' => 2 } }
|
50
50
|
it { expect(parsed_filters.count).to eq 2 }
|
51
51
|
end
|
52
52
|
|
53
53
|
context 'less_than' do
|
54
|
-
let(:filters) { { field
|
54
|
+
let(:filters) { { 'field' => 'age', 'operator' => 'less_than', 'value' => 2 } }
|
55
55
|
it { expect(parsed_filters.count).to eq 1 }
|
56
56
|
end
|
57
57
|
|
58
58
|
context 'after' do
|
59
|
-
let(:filters) { { field
|
59
|
+
let(:filters) { { 'field' => 'created_at', 'operator' => 'after', 'value' => 1.day.ago } }
|
60
60
|
it { expect(parsed_filters.count).to eq 2 }
|
61
61
|
end
|
62
62
|
|
63
63
|
context 'before' do
|
64
|
-
let(:filters) { { field
|
64
|
+
let(:filters) { { 'field' => 'created_at', 'operator' => 'before', 'value' => 1.day.ago } }
|
65
65
|
it { expect(parsed_filters.count).to eq 1 }
|
66
66
|
end
|
67
67
|
|
68
68
|
context 'contains' do
|
69
|
-
let(:filters) { { field
|
69
|
+
let(:filters) { { 'field' => 'name', 'operator' => 'contains', 'value' => 'ree' } }
|
70
70
|
it { expect(parsed_filters.count).to eq 3 }
|
71
71
|
end
|
72
72
|
|
73
73
|
context 'not_contains' do
|
74
|
-
let(:filters) { { field
|
74
|
+
let(:filters) { { 'field' => 'name', 'operator' => 'not_contains', 'value' => ' ' } }
|
75
75
|
it { expect(parsed_filters.count).to eq 0 }
|
76
76
|
end
|
77
77
|
|
78
78
|
context 'starts_with' do
|
79
|
-
let(:filters) { { field
|
79
|
+
let(:filters) { { 'field' => 'name', 'operator' => 'starts_with', 'value' => 'o' } }
|
80
80
|
it { expect(parsed_filters.count).to eq 0 }
|
81
81
|
end
|
82
82
|
|
83
83
|
context 'ends_with' do
|
84
|
-
let(:filters) { { field
|
84
|
+
let(:filters) { { 'field' => 'name', 'operator' => 'ends_with', 'value' => '3' } }
|
85
85
|
it { expect(parsed_filters.count).to eq 1 }
|
86
86
|
end
|
87
87
|
|
88
88
|
context 'present' do
|
89
|
-
let(:filters) { { field
|
89
|
+
let(:filters) { { 'field' => 'cutter_id', 'operator' => 'present', 'value' => nil } }
|
90
90
|
it { expect(parsed_filters.count).to eq 1 }
|
91
91
|
end
|
92
92
|
|
93
93
|
context 'blank' do
|
94
|
-
let(:filters) { { field
|
94
|
+
let(:filters) { { 'field' => 'cutter_id', 'operator' => 'blank', 'value' => nil } }
|
95
95
|
it { expect(parsed_filters.count).to eq 2 }
|
96
96
|
end
|
97
97
|
end
|
98
98
|
|
99
99
|
context 'belongsTo conditions' do
|
100
100
|
context 'not_equal' do
|
101
|
-
let(:filters) { { field
|
101
|
+
let(:filters) { { 'field' => 'cutter:title', 'operator' => 'not_equal', 'value' => 'king' } }
|
102
102
|
it { expect(parsed_filters.count).to eq 1 }
|
103
103
|
end
|
104
104
|
|
105
105
|
context 'equal' do
|
106
|
-
|
106
|
+
let(:filters) { { 'field' => 'cutter:title', 'operator' => 'equal', 'value' => 'king' } }
|
107
107
|
it { expect(parsed_filters.count).to eq 0 }
|
108
108
|
end
|
109
109
|
|
110
110
|
context 'contains' do
|
111
|
-
let(:filters) { { field
|
111
|
+
let(:filters) { { 'field' => 'owner:title', 'operator' => 'contains', 'value' => 'in' } }
|
112
112
|
it { expect(parsed_filters.count).to eq 3 }
|
113
113
|
end
|
114
114
|
|
115
115
|
context 'not_contains' do
|
116
|
-
let(:filters) { { field
|
116
|
+
let(:filters) { { 'field' => 'owner:title', 'operator' => 'not_contains', 'value' => 'g' } }
|
117
117
|
it { expect(parsed_filters.count).to eq 0 }
|
118
118
|
end
|
119
119
|
|
120
120
|
context 'starts_with' do
|
121
|
-
let(:filters) { { field
|
121
|
+
let(:filters) { { 'field' => 'cutter:title', 'operator' => 'starts_with', 'value' => 'v' } }
|
122
122
|
it { expect(parsed_filters.count).to eq 1 }
|
123
123
|
end
|
124
124
|
|
@@ -126,9 +126,9 @@ module ForestLiana
|
|
126
126
|
context 'different fields' do
|
127
127
|
let(:filters) {
|
128
128
|
{
|
129
|
-
aggregator
|
130
|
-
{ field
|
131
|
-
{ field
|
129
|
+
'aggregator' => 'or', 'conditions' => [
|
130
|
+
{ 'field' => 'owner:name', 'operator' => 'contains', 'value' => 'E.' },
|
131
|
+
{ 'field' => 'cutter:title', 'operator' => 'starts_with', 'value' => 'v' }
|
132
132
|
]
|
133
133
|
}
|
134
134
|
}
|
@@ -138,9 +138,9 @@ module ForestLiana
|
|
138
138
|
context 'same fields' do
|
139
139
|
let(:filters) {
|
140
140
|
{
|
141
|
-
aggregator
|
142
|
-
{ field
|
143
|
-
{ field
|
141
|
+
'aggregator' => 'and', 'conditions' => [
|
142
|
+
{ 'field' => 'owner:name', 'operator' => 'contains', 'value' => 'E.' },
|
143
|
+
{ 'field' => 'owner:title', 'operator' => 'starts_with', 'value' => 'v' }
|
144
144
|
]
|
145
145
|
}
|
146
146
|
}
|
@@ -150,7 +150,7 @@ module ForestLiana
|
|
150
150
|
end
|
151
151
|
|
152
152
|
context 'and aggregator on simple conditions' do
|
153
|
-
let(:filters) { { aggregator
|
153
|
+
let(:filters) { { 'aggregator' => 'and', 'conditions' => [simple_condition_1, simple_condition_2] } }
|
154
154
|
it { expect(parsed_filters.count).to eq 1 }
|
155
155
|
end
|
156
156
|
|
@@ -159,15 +159,15 @@ module ForestLiana
|
|
159
159
|
context 'and aggregator on simple conditions' do
|
160
160
|
let(:filters) {
|
161
161
|
{
|
162
|
-
aggregator
|
163
|
-
conditions
|
162
|
+
'aggregator' => 'or',
|
163
|
+
'conditions' => [
|
164
164
|
{
|
165
|
-
aggregator
|
166
|
-
{ aggregator
|
165
|
+
'aggregator' => 'and', 'conditions' => [
|
166
|
+
{ 'aggregator' => 'or', 'conditions' => [date_condition_1, simple_condition_1] },
|
167
167
|
simple_condition_2
|
168
168
|
]
|
169
169
|
},
|
170
|
-
{ field
|
170
|
+
{ 'field' => 'cutter:title', 'operator' => 'starts_with', 'value' => 'v' }
|
171
171
|
]
|
172
172
|
}
|
173
173
|
}
|
@@ -199,28 +199,28 @@ module ForestLiana
|
|
199
199
|
end
|
200
200
|
|
201
201
|
context 'raw value in conditions' do
|
202
|
-
let(:filters) { { aggregator
|
202
|
+
let(:filters) { { 'aggregator' => 'and', 'conditions' => [4] } }
|
203
203
|
it {
|
204
204
|
expect { parsed_filters }.to raise_error(ForestLiana::Errors::HTTP422Error, 'Filters cannot be a raw value')
|
205
205
|
}
|
206
206
|
end
|
207
207
|
|
208
208
|
context 'bad field type' do
|
209
|
-
let(:filters) { { field
|
209
|
+
let(:filters) { { 'field' => 4, 'operator' => 'oss 117', 'value' => 'tuorp' } }
|
210
210
|
it {
|
211
211
|
expect { parsed_filters }.to raise_error(ForestLiana::Errors::HTTP422Error, 'Invalid condition format')
|
212
212
|
}
|
213
213
|
end
|
214
214
|
|
215
215
|
context 'bad operator type' do
|
216
|
-
let(:filters) { { field
|
216
|
+
let(:filters) { { 'field' => 'magnetic', 'operator' => true, 'value' => 'tuorp' } }
|
217
217
|
it {
|
218
218
|
expect { parsed_filters }.to raise_error(ForestLiana::Errors::HTTP422Error, 'Invalid condition format')
|
219
219
|
}
|
220
220
|
end
|
221
221
|
|
222
222
|
context 'unexisting field' do
|
223
|
-
let(:filters) { { field
|
223
|
+
let(:filters) { { 'field' => 'magnetic', 'operator' => 'archer', 'value' => 'tuorp' } }
|
224
224
|
it {
|
225
225
|
expect { parsed_filters }.to raise_error(ForestLiana::Errors::HTTP422Error, 'Field \'magnetic\' not found')
|
226
226
|
}
|
@@ -3,7 +3,7 @@ module ForestLiana
|
|
3
3
|
describe 'when retrieving has many relationship related records' do
|
4
4
|
let(:rendering_id) { 13 }
|
5
5
|
let(:user) { { 'id' => '1', 'rendering_id' => rendering_id } }
|
6
|
-
let(:scopes) { { } }
|
6
|
+
let(:scopes) { {'scopes' => {}, 'team' => {'id' => '1', 'name' => 'Operations'}} }
|
7
7
|
let(:association) { Island.reflect_on_association(:trees) }
|
8
8
|
let(:params) {
|
9
9
|
{
|
@@ -87,19 +87,21 @@ module ForestLiana
|
|
87
87
|
end
|
88
88
|
|
89
89
|
describe 'with scopes' do
|
90
|
-
let(:scopes) {
|
91
|
-
|
92
|
-
'
|
93
|
-
|
94
|
-
'
|
95
|
-
|
96
|
-
{
|
97
|
-
|
90
|
+
let(:scopes) {
|
91
|
+
{
|
92
|
+
'scopes' =>
|
93
|
+
{
|
94
|
+
'Tree' => {
|
95
|
+
'aggregator' => 'and',
|
96
|
+
'conditions' => [{'field' => 'name', 'operator' => 'contains', 'value' => 'a'}]
|
97
|
+
}
|
98
98
|
},
|
99
|
-
|
99
|
+
'team' => {
|
100
|
+
'id' => 43,
|
101
|
+
'name' => 'Operations'
|
100
102
|
}
|
101
103
|
}
|
102
|
-
}
|
104
|
+
}
|
103
105
|
|
104
106
|
describe 'when asking for all trees related to madagascar' do
|
105
107
|
it 'should return trees belonging to madagascar and matching the scopes' do
|
@@ -2,7 +2,7 @@ module ForestLiana
|
|
2
2
|
describe LineStatGetter do
|
3
3
|
let(:rendering_id) { 13 }
|
4
4
|
let(:user) { { 'id' => '1', 'rendering_id' => rendering_id } }
|
5
|
-
let(:scopes) { { } }
|
5
|
+
let(:scopes) { {'scopes' => {}, 'team' => {'id' => '1', 'name' => 'Operations'}} }
|
6
6
|
|
7
7
|
before(:each) do
|
8
8
|
ForestLiana::ScopeManager.invalidate_scope_cache(rendering_id)
|
@@ -24,7 +24,7 @@ module ForestLiana
|
|
24
24
|
end
|
25
25
|
|
26
26
|
describe 'with not allowed aggregator' do
|
27
|
-
let(:scopes) { { } }
|
27
|
+
let(:scopes) { {'scopes' => {}, 'team' => {'id' => '1', 'name' => 'Operations'}} }
|
28
28
|
let(:model) { Tree }
|
29
29
|
let(:collection) { 'trees' }
|
30
30
|
let(:params) {
|
@@ -60,7 +60,7 @@ module ForestLiana
|
|
60
60
|
subject { PieStatGetter.new(model, params, user) }
|
61
61
|
|
62
62
|
describe 'with empty scopes' do
|
63
|
-
let(:scopes) { { } }
|
63
|
+
let(:scopes) { {'scopes' => {}, 'team' => {'id' => '1', 'name' => 'Operations'}} }
|
64
64
|
|
65
65
|
describe 'with an aggregate on the name field' do
|
66
66
|
let(:groupByFieldName) { 'name' }
|
@@ -94,16 +94,16 @@ module ForestLiana
|
|
94
94
|
describe 'with scopes' do
|
95
95
|
let(:scopes) {
|
96
96
|
{
|
97
|
-
'
|
98
|
-
|
99
|
-
'
|
97
|
+
'scopes' =>
|
98
|
+
{
|
99
|
+
'Tree' => {
|
100
100
|
'aggregator' => 'and',
|
101
|
-
'conditions' => [
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
101
|
+
'conditions' => [{'field' => 'age', 'operator' => 'less_than', 'value' => 10}]
|
102
|
+
}
|
103
|
+
},
|
104
|
+
'team' => {
|
105
|
+
'id' => 43,
|
106
|
+
'name' => 'Operations'
|
107
107
|
}
|
108
108
|
}
|
109
109
|
}
|
@@ -13,7 +13,7 @@ module ForestLiana
|
|
13
13
|
}
|
14
14
|
let(:rendering_id) { 13 }
|
15
15
|
let(:user) { { 'id' => '1', 'rendering_id' => rendering_id } }
|
16
|
-
let(:scopes) { { } }
|
16
|
+
let(:scopes) { {'scopes' => {}, 'team' => {'id' => '1', 'name' => 'Operations'}} }
|
17
17
|
|
18
18
|
subject {
|
19
19
|
described_class.new(User, params, user)
|
@@ -66,19 +66,21 @@ module ForestLiana
|
|
66
66
|
|
67
67
|
describe 'with scope excluding target record' do
|
68
68
|
let(:attributes) { { name: 'Gandalf' } }
|
69
|
-
let(:scopes) {
|
70
|
-
|
71
|
-
'
|
72
|
-
|
73
|
-
'
|
74
|
-
|
75
|
-
{
|
76
|
-
|
69
|
+
let(:scopes) {
|
70
|
+
{
|
71
|
+
'scopes' =>
|
72
|
+
{
|
73
|
+
'User' => {
|
74
|
+
'aggregator' => 'and',
|
75
|
+
'conditions' => [{'field' => 'id', 'operator' => 'greater_than', 'value' => 2}]
|
76
|
+
}
|
77
77
|
},
|
78
|
-
|
78
|
+
'team' => {
|
79
|
+
'id' => 43,
|
80
|
+
'name' => 'Operations'
|
79
81
|
}
|
80
82
|
}
|
81
|
-
}
|
83
|
+
}
|
82
84
|
|
83
85
|
it 'should not update the record name' do
|
84
86
|
subject.perform
|
@@ -90,19 +92,21 @@ module ForestLiana
|
|
90
92
|
|
91
93
|
describe 'with scope including target record' do
|
92
94
|
let(:attributes) { { name: 'Gandalf' } }
|
93
|
-
let(:scopes) {
|
94
|
-
|
95
|
-
'
|
96
|
-
|
97
|
-
'
|
98
|
-
|
99
|
-
{
|
100
|
-
|
95
|
+
let(:scopes) {
|
96
|
+
{
|
97
|
+
'scopes' =>
|
98
|
+
{
|
99
|
+
'User' => {
|
100
|
+
'aggregator' => 'and',
|
101
|
+
'conditions' => [{'field' => 'id', 'operator' => 'less_than', 'value' => 2}]
|
102
|
+
}
|
101
103
|
},
|
102
|
-
|
104
|
+
'team' => {
|
105
|
+
'id' => 43,
|
106
|
+
'name' => 'Operations'
|
103
107
|
}
|
104
108
|
}
|
105
|
-
}
|
109
|
+
}
|
106
110
|
|
107
111
|
it 'should not update the record name' do
|
108
112
|
subject.perform
|
@@ -6,7 +6,7 @@ module ForestLiana
|
|
6
6
|
let(:sort) { 'id' }
|
7
7
|
let(:fields) {}
|
8
8
|
let(:filters) {}
|
9
|
-
let(:scopes) { {} }
|
9
|
+
let(:scopes) { {'scopes' => {}, 'team' => {'id' => '1', 'name' => 'Operations'}} }
|
10
10
|
let(:rendering_id) { 13 }
|
11
11
|
let(:user) { { 'id' => '1', 'rendering_id' => rendering_id } }
|
12
12
|
|
@@ -480,16 +480,16 @@ module ForestLiana
|
|
480
480
|
let(:filters) { }
|
481
481
|
let(:scopes) {
|
482
482
|
{
|
483
|
-
'
|
484
|
-
|
485
|
-
'
|
483
|
+
'scopes' =>
|
484
|
+
{
|
485
|
+
'Island' => {
|
486
486
|
'aggregator' => 'and',
|
487
|
-
'conditions' => [
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
487
|
+
'conditions' => [{'field' => 'name', 'operator' => 'contains', 'value' => 'u'}]
|
488
|
+
}
|
489
|
+
},
|
490
|
+
'team' => {
|
491
|
+
'id' => 43,
|
492
|
+
'name' => 'Operations'
|
493
493
|
}
|
494
494
|
}
|
495
495
|
}
|