forest_liana 7.0.0.beta.2 → 7.0.0.beta.6

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.
Files changed (57) 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 +16 -6
  6. data/app/controllers/forest_liana/scopes_controller.rb +20 -0
  7. data/app/controllers/forest_liana/smart_actions_controller.rb +39 -6
  8. data/app/controllers/forest_liana/stats_controller.rb +5 -5
  9. data/app/services/forest_liana/apimap_sorter.rb +1 -0
  10. data/app/services/forest_liana/filters_parser.rb +8 -4
  11. data/app/services/forest_liana/has_many_dissociator.rb +2 -2
  12. data/app/services/forest_liana/has_many_getter.rb +2 -2
  13. data/app/services/forest_liana/leaderboard_stat_getter.rb +20 -14
  14. data/app/services/forest_liana/line_stat_getter.rb +5 -3
  15. data/app/services/forest_liana/permissions_checker.rb +42 -37
  16. data/app/services/forest_liana/permissions_formatter.rb +1 -1
  17. data/app/services/forest_liana/permissions_getter.rb +3 -6
  18. data/app/services/forest_liana/pie_stat_getter.rb +6 -3
  19. data/app/services/forest_liana/resource_getter.rb +6 -3
  20. data/app/services/forest_liana/resource_updater.rb +5 -2
  21. data/app/services/forest_liana/resources_getter.rb +6 -5
  22. data/app/services/forest_liana/scope_manager.rb +102 -0
  23. data/app/services/forest_liana/search_query_builder.rb +6 -3
  24. data/app/services/forest_liana/stat_getter.rb +2 -1
  25. data/app/services/forest_liana/token.rb +1 -0
  26. data/app/services/forest_liana/utils/beta_schema_utils.rb +1 -1
  27. data/app/services/forest_liana/value_stat_getter.rb +4 -2
  28. data/config/routes.rb +3 -1
  29. data/lib/forest_liana/bootstrapper.rb +4 -2
  30. data/lib/forest_liana/version.rb +1 -1
  31. data/spec/dummy/app/controllers/forest/islands_controller.rb +5 -0
  32. data/spec/dummy/config/routes.rb +4 -0
  33. data/spec/dummy/lib/forest_liana/collections/island.rb +7 -0
  34. data/spec/lib/forest_liana/bootstrapper_spec.rb +12 -0
  35. data/spec/requests/actions_controller_spec.rb +144 -23
  36. data/spec/requests/authentications_spec.rb +2 -1
  37. data/spec/requests/resources_spec.rb +2 -0
  38. data/spec/services/forest_liana/apimap_sorter_spec.rb +6 -4
  39. data/spec/services/forest_liana/filters_parser_spec.rb +1 -1
  40. data/spec/services/forest_liana/has_many_getter_spec.rb +116 -0
  41. data/spec/services/forest_liana/line_stat_getter_spec.rb +14 -6
  42. data/spec/services/forest_liana/permissions_checker_acl_disabled_spec.rb +45 -71
  43. data/spec/services/forest_liana/permissions_checker_acl_enabled_spec.rb +39 -63
  44. data/spec/services/forest_liana/permissions_checker_live_queries_spec.rb +3 -3
  45. data/spec/services/forest_liana/permissions_formatter_spec.rb +11 -11
  46. data/spec/services/forest_liana/pie_stat_getter_spec.rb +114 -0
  47. data/spec/services/forest_liana/resource_updater_spec.rb +116 -0
  48. data/spec/services/forest_liana/resources_getter_spec.rb +68 -1
  49. data/spec/services/forest_liana/scope_manager_spec.rb +232 -0
  50. data/spec/services/forest_liana/value_stat_getter_spec.rb +96 -0
  51. metadata +125 -118
  52. data/app/services/forest_liana/scope_validator.rb +0 -98
  53. data/test/services/forest_liana/has_many_getter_test.rb +0 -75
  54. data/test/services/forest_liana/pie_stat_getter_test.rb +0 -29
  55. data/test/services/forest_liana/resource_updater_test.rb +0 -86
  56. data/test/services/forest_liana/scope_validator_test.rb +0 -185
  57. data/test/services/forest_liana/value_stat_getter_test.rb +0 -71
@@ -2,9 +2,10 @@ module ForestLiana
2
2
  class StatGetter < 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
+ @user = forest_user
8
9
  compute_includes()
9
10
  end
10
11
  end
@@ -19,6 +19,7 @@ module ForestLiana
19
19
  first_name: user['first_name'],
20
20
  last_name: user['last_name'],
21
21
  team: user['teams'][0],
22
+ role: user['role'],
22
23
  rendering_id: rendering_id,
23
24
  exp: expiration_in_seconds()
24
25
  }, ForestLiana.auth_secret, 'HS256')
@@ -6,7 +6,7 @@ module ForestLiana
6
6
 
7
7
  return nil unless collection
8
8
 
9
- collection.actions.find { |action| action.endpoint == endpoint && action.http_method == http_method }
9
+ collection.actions.find { |action| (action.endpoint == endpoint || "/#{action.endpoint}" == endpoint) && action.http_method == http_method }
10
10
  end
11
11
  end
12
12
  end
@@ -6,8 +6,10 @@ module ForestLiana
6
6
  return if @params[:aggregate].blank?
7
7
  resource = get_resource().eager_load(@includes)
8
8
 
9
- unless @params[:filters].blank?
10
- filter_parser = FiltersParser.new(@params[:filters], resource, @params[:timezone])
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
@@ -203,10 +203,12 @@ module ForestLiana
203
203
 
204
204
  def setup_forest_liana_meta
205
205
  ForestLiana.meta = {
206
- database_type: database_type,
207
206
  liana: 'forest-rails',
208
207
  liana_version: ForestLiana::VERSION,
209
- orm_version: Gem.loaded_specs["activerecord"].version.version
208
+ stack: {
209
+ database_type: database_type,
210
+ orm_version: Gem.loaded_specs["activerecord"].version.version,
211
+ }
210
212
  }
211
213
  end
212
214
 
@@ -1,3 +1,3 @@
1
1
  module ForestLiana
2
- VERSION = "7.0.0.beta.2"
2
+ VERSION = "7.0.0.beta.6"
3
3
  end
@@ -0,0 +1,5 @@
1
+ class Forest::IslandsController < ForestLiana::SmartActionsController
2
+ def test
3
+ render json: { success: 'You are OK.' }
4
+ end
5
+ end
@@ -1,3 +1,7 @@
1
1
  Rails.application.routes.draw do
2
+ namespace :forest do
3
+ post '/actions/test' => 'islands#test'
4
+ end
5
+
2
6
  mount ForestLiana::Engine => "/forest"
3
7
  end
@@ -0,0 +1,7 @@
1
+ class Forest::Island
2
+ include ForestLiana::Collection
3
+
4
+ collection :Island
5
+
6
+ action 'Test'
7
+ end
@@ -0,0 +1,12 @@
1
+ module ForestLiana
2
+ describe Bootstrapper do
3
+ describe 'setup_forest_liana_meta' do
4
+ it "should put statistic data related to user stack on a dedicated object" do
5
+ expect(ForestLiana.meta[:stack])
6
+ .to include(:orm_version)
7
+ expect(ForestLiana.meta[:stack])
8
+ .to include(:database_type)
9
+ end
10
+ end
11
+ end
12
+ end
@@ -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
- describe 'call /values' do
15
- it 'should respond 200' do
16
- post '/forest/actions/foo/values', {}
17
- expect(response.status).to eq(200)
18
- expect(response.body).to be {}
19
- end
20
- end
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 = {recordIds: [1], collectionName: 'Island'}
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: { 'CONTENT_TYPE' => 'application/json' }
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: { 'CONTENT_TYPE' => 'application/json' }
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: { 'CONTENT_TYPE' => 'application/json' }
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 = {recordIds: [1], fields: [updated_foo], collectionName: 'Island', changedField: 'foo'}
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: { 'CONTENT_TYPE' => 'application/json' }
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({collectionName: 'Island'}), headers: { 'CONTENT_TYPE' => 'application/json' }
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: { 'CONTENT_TYPE' => 'application/json' }
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 = {recordIds: [1], fields: [updated_foo, updated_enum], collectionName: 'Island', changedField: 'foo'}
185
- post '/forest/actions/enums_action/hooks/change', params: JSON.dump(p), headers: { 'CONTENT_TYPE' => 'application/json' }
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 = {recordIds: [1], fields: [foo, updated_multiple_enum], collectionName: 'Island', changedField: 'foo'}
199
- post '/forest/actions/multiple_enums_action/hooks/change', params: JSON.dump(p), headers: { 'CONTENT_TYPE' => 'application/json' }
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 = {recordIds: [1], fields: [foo, wrongly_updated_multiple_enum], collectionName: 'Island', changedField: 'foo'}
213
- post '/forest/actions/multiple_enums_action/hooks/change', params: JSON.dump(p), headers: { 'CONTENT_TYPE' => 'application/json' }
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
@@ -45,7 +45,7 @@ describe "Authentications", type: :request do
45
45
 
46
46
  describe "GET /authentication/callback" do
47
47
  before() do
48
- response = '{"data":{"id":666,"attributes":{"first_name":"Alice","last_name":"Doe","email":"alice@forestadmin.com","teams":[1,2,3]}}}'
48
+ response = '{"data":{"id":666,"attributes":{"first_name":"Alice","last_name":"Doe","email":"alice@forestadmin.com","teams":[1,2,3],"role":"Test"}}}'
49
49
  allow(ForestLiana::ForestApiRequester).to receive(:get).with(
50
50
  "/liana/v2/renderings/42/authorization", { :headers => { "forest-token" => "THE-ACCESS-TOKEN" }, :query=> {} }
51
51
  ).and_return(
@@ -72,6 +72,7 @@ describe "Authentications", type: :request do
72
72
  "first_name" => 'Alice',
73
73
  "last_name" => 'Doe',
74
74
  "team" => 1,
75
+ "role" => "Test",
75
76
  }
76
77
 
77
78
  expect(decoded).to include(expected_token_data)
@@ -17,6 +17,8 @@ describe 'Requesting Tree resources', :type => :request do
17
17
  allow(ForestLiana::IpWhitelist).to receive(:is_ip_valid) { true }
18
18
 
19
19
  allow_any_instance_of(ForestLiana::PermissionsChecker).to receive(:is_authorized?) { true }
20
+
21
+ allow(ForestLiana::ScopeManager).to receive(:fetch_scopes).and_return({})
20
22
  end
21
23
 
22
24
  token = JWT.encode({
@@ -4,9 +4,11 @@ module ForestLiana
4
4
  context 'on a disordered apimap' do
5
5
  apimap = {
6
6
  'meta': {
7
- 'orm_version': '4.34.9',
7
+ stack: {
8
+ 'orm_version': '4.34.9',
9
+ 'database_type': 'postgresql',
10
+ },
8
11
  'liana_version': '1.5.24',
9
- 'database_type': 'postgresql',
10
12
  liana: 'forest-rails',
11
13
  },
12
14
  'data': [{
@@ -165,8 +167,8 @@ module ForestLiana
165
167
  end
166
168
 
167
169
  it 'should sort the meta values' do
168
- expect(apimap_sorted['meta'].keys).to eq(
169
- ['database_type', 'liana', 'liana_version', 'orm_version'])
170
+ expect(apimap_sorted['meta'].keys).to eq(['liana', 'liana_version', 'stack'])
171
+ expect(apimap_sorted['meta']['stack'].keys).to eq(['database_type', 'orm_version'])
170
172
  end
171
173
  end
172
174
  end
@@ -281,7 +281,7 @@ module ForestLiana
281
281
  let(:result) { filter_parser.parse_condition(condition) }
282
282
 
283
283
  context 'on valid condition' do
284
- it { expect(result).to eq "\"trees\".\"name\" LIKE '%3'" }
284
+ it { expect(result).to eq "\"trees\".\"name\" LIKE ('%3')" }
285
285
  end
286
286
 
287
287
  context 'on belongs_to condition' do
@@ -0,0 +1,116 @@
1
+ module ForestLiana
2
+ describe HasManyGetter do
3
+ describe 'when retrieving has many relationship related records' do
4
+ let(:rendering_id) { 13 }
5
+ let(:user) { { 'id' => '1', 'rendering_id' => rendering_id } }
6
+ let(:scopes) { { } }
7
+ let(:association) { Island.reflect_on_association(:trees) }
8
+ let(:params) {
9
+ {
10
+ id: Island.first.id,
11
+ association_name: 'trees',
12
+ page: { size: 15, number: 1 },
13
+ timezone: 'America/Nome'
14
+ }
15
+ }
16
+
17
+ subject {
18
+ described_class.new(Island, association, params, user)
19
+ }
20
+
21
+ before(:each) do
22
+ madagascar = Island.create(name: 'madagascar')
23
+ re = Island.create(name: 'ré')
24
+ Tree.create(name: 'lemon tree', island: madagascar)
25
+ Tree.create(name: 'banana tree', island: madagascar)
26
+ Tree.create(name: 'papaya tree', island: madagascar)
27
+ Tree.create(name: 'apple tree', island: re)
28
+ Tree.create(name: 'banana tree', island: re)
29
+ ForestLiana::ScopeManager.invalidate_scope_cache(rendering_id)
30
+ allow(ForestLiana::ScopeManager).to receive(:fetch_scopes).and_return(scopes)
31
+ end
32
+
33
+ after(:each) do
34
+ Island.destroy_all
35
+ Tree.destroy_all
36
+ end
37
+
38
+ describe 'with empty scopes' do
39
+ describe 'with page 1 size 15' do
40
+ it 'should return the 3 trees matching madagascar' do
41
+ subject.perform
42
+
43
+ expect(subject.records.count).to eq 3
44
+ expect(subject.count).to eq 3
45
+ end
46
+ end
47
+
48
+ describe 'when sorting by decreasing id' do
49
+ let(:params) {
50
+ {
51
+ id: Island.first.id,
52
+ association_name: 'trees',
53
+ sort: '-id',
54
+ page: { size: 15, number: 1 },
55
+ timezone: 'America/Nome'
56
+ }
57
+ }
58
+
59
+ it 'should order records properly' do
60
+ subject.perform
61
+
62
+ expect(subject.records.count).to eq 3
63
+ expect(subject.count).to eq 3
64
+ expect(subject.records.first.id).to be > subject.records.last.id
65
+ end
66
+ end
67
+
68
+ describe 'when searching for banana tree' do
69
+ let(:params) {
70
+ {
71
+ id: Island.first.id,
72
+ association_name: 'trees',
73
+ search: 'banana',
74
+ page: { size: 15, number: 1 },
75
+ timezone: 'America/Nome'
76
+ }
77
+ }
78
+
79
+ it 'should return only the banana tree linked to madagascar' do
80
+ subject.perform
81
+
82
+ expect(subject.records.count).to eq 1
83
+ expect(subject.count).to eq 1
84
+ expect(subject.records.first.island.name).to eq 'madagascar'
85
+ end
86
+ end
87
+ end
88
+
89
+ describe 'with scopes' do
90
+ let(:scopes) { {
91
+ 'Tree' => {
92
+ 'scope'=> {
93
+ 'filter'=> {
94
+ 'aggregator' => 'and',
95
+ 'conditions' => [
96
+ { 'field' => 'name', 'operator' => 'contains', 'value' => 'a' }
97
+ ]
98
+ },
99
+ 'dynamicScopesValues' => { }
100
+ }
101
+ }
102
+ } }
103
+
104
+ describe 'when asking for all trees related to madagascar' do
105
+ it 'should return trees belonging to madagascar and matching the scopes' do
106
+ subject.perform
107
+
108
+ # Only `papaya` and `banana` contain an `a`
109
+ expect(subject.records.count).to eq 2
110
+ expect(subject.count).to eq 2
111
+ end
112
+ end
113
+ end
114
+ end
115
+ end
116
+ end