forest_admin_agent 1.0.0.pre.beta.21 → 1.0.0.pre.beta.23

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/forest_admin_agent.gemspec +3 -1
  3. data/lib/forest_admin_agent/auth/oauth2/forest_provider.rb +1 -1
  4. data/lib/forest_admin_agent/auth/oidc_client_manager.rb +3 -2
  5. data/lib/forest_admin_agent/builder/agent_factory.rb +7 -9
  6. data/lib/forest_admin_agent/http/Exceptions/authentication_open_id_client.rb +1 -1
  7. data/lib/forest_admin_agent/http/Exceptions/conflict_error.rb +14 -0
  8. data/lib/forest_admin_agent/http/Exceptions/forbidden_error.rb +14 -0
  9. data/lib/forest_admin_agent/http/Exceptions/not_found_error.rb +1 -1
  10. data/lib/forest_admin_agent/http/Exceptions/require_approval.rb +15 -0
  11. data/lib/forest_admin_agent/http/forest_admin_api_requester.rb +32 -4
  12. data/lib/forest_admin_agent/http/router.rb +1 -0
  13. data/lib/forest_admin_agent/routes/abstract_authenticated_route.rb +1 -0
  14. data/lib/forest_admin_agent/routes/charts/charts.rb +214 -0
  15. data/lib/forest_admin_agent/routes/resources/count.rb +7 -4
  16. data/lib/forest_admin_agent/routes/resources/delete.rb +5 -1
  17. data/lib/forest_admin_agent/routes/resources/list.rb +9 -3
  18. data/lib/forest_admin_agent/routes/resources/related/associate_related.rb +10 -1
  19. data/lib/forest_admin_agent/routes/resources/related/count_related.rb +2 -1
  20. data/lib/forest_admin_agent/routes/resources/related/dissociate_related.rb +2 -0
  21. data/lib/forest_admin_agent/routes/resources/related/list_related.rb +9 -1
  22. data/lib/forest_admin_agent/routes/resources/related/update_related.rb +8 -1
  23. data/lib/forest_admin_agent/routes/resources/show.rb +5 -3
  24. data/lib/forest_admin_agent/routes/resources/store.rb +11 -10
  25. data/lib/forest_admin_agent/routes/resources/update.rb +4 -3
  26. data/lib/forest_admin_agent/serializer/forest_chart_serializer.rb +19 -0
  27. data/lib/forest_admin_agent/services/permissions.rb +268 -0
  28. data/lib/forest_admin_agent/services/smart_action_checker.rb +95 -0
  29. data/lib/forest_admin_agent/services/sse_cache_invalidation.rb +45 -0
  30. data/lib/forest_admin_agent/utils/context_variables.rb +39 -0
  31. data/lib/forest_admin_agent/utils/context_variables_injector.rb +53 -0
  32. data/lib/forest_admin_agent/utils/query_string_parser.rb +1 -0
  33. data/lib/forest_admin_agent/utils/schema/schema_emitter.rb +1 -1
  34. data/lib/forest_admin_agent/version.rb +1 -1
  35. data/lib/forest_admin_agent.rb +1 -0
  36. metadata +43 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a86f151f54daa3a9a8f46251c380448eed7c0fdf90ff82818a37fa213a868557
4
- data.tar.gz: 2af48bff7f6a6691f2fe495509dba2669ec64d9197886167a1de316c24846263
3
+ metadata.gz: c342682e272a5886928f4c48de0e35dbd6bcf013c4f888aa56bac238cca695da
4
+ data.tar.gz: 300d73bdac310e91c2aa4a044d095b782bd362a0fb3c6309871fad0b1fff2930
5
5
  SHA512:
6
- metadata.gz: 006cdc9ffa0c0afcb5ae7efb9a401b5964d379834bbf416d9f5cd7400de8425c83867ae2e0a83cd5a6f7d9de4043c018c3f2a650cab00383a576baf364ebaf22
7
- data.tar.gz: 3d04663fff62e53935d54c3a9312d5c575ff5b78ce7bd5fc2be1e73021f7dbda7ac43fe5f30dc141f07f6d9fb98cb23f54c093be65b2c8cb866daabfd8f97fcf
6
+ metadata.gz: 50d05943fa75c8559e127bd181f91bb36dc64352237126381d4bd1fd769fa0e51389b74051e1f7cdc55f7c6747fbdb2c91259cf135c6e2e7e7665c5cef8cefcd
7
+ data.tar.gz: be53569ca0ad92511dc33b118693445e5a2a522faa98fd24e4d3662bf70eaa3ac7f9614cc1dccc6db444f8fe204d39db43eca1709f8efe4e528f7fef2b200ebf
@@ -34,12 +34,14 @@ admin work on any Ruby application."
34
34
  spec.require_paths = ["lib"]
35
35
 
36
36
  spec.add_dependency "activesupport", ">= 6.1"
37
+ spec.add_dependency "deepsort", "~> 0.4.5"
37
38
  spec.add_dependency "dry-container", "~> 0.11"
38
39
  spec.add_dependency "faraday", "~> 2.7"
40
+ spec.add_dependency "filecache", "~> 1.0"
39
41
  spec.add_dependency "ipaddress", "~> 0.8.3"
40
42
  spec.add_dependency "jsonapi-serializers", "~> 1.0"
41
43
  spec.add_dependency "jwt", "~> 2.7"
42
- spec.add_dependency "lightly", "~> 0.4.0"
44
+ spec.add_dependency "ld-eventsource", "~> 2.2"
43
45
  spec.add_dependency "mono_logger", "~> 1.1"
44
46
  spec.add_dependency "openid_connect", "~> 2.2"
45
47
  spec.add_dependency "rake", "~> 13.0"
@@ -8,7 +8,7 @@ module ForestAdminAgent
8
8
  attr_reader :rendering_id
9
9
 
10
10
  def initialize(rendering_id, attributes = {})
11
- super attributes
11
+ super(attributes)
12
12
  @rendering_id = rendering_id
13
13
  @authorization_endpoint = '/oidc/auth'
14
14
  @token_endpoint = '/oidc/token'
@@ -1,3 +1,4 @@
1
+ require 'filecache'
1
2
  require 'openid_connect'
2
3
  require_relative 'oauth2/oidc_config'
3
4
  require_relative 'oauth2/forest_provider'
@@ -18,8 +19,8 @@ module ForestAdminAgent
18
19
  private
19
20
 
20
21
  def setup_cache(env_secret, config_agent)
21
- lightly = Lightly.new(life: TTL, dir: "#{config_agent[:cache_dir]}/issuer")
22
- lightly.get env_secret do
22
+ cache = FileCache.new('auth_issuer', (config_agent[:cache_dir]).to_s, TTL)
23
+ cache.get_or_set env_secret do
23
24
  oidc_config = retrieve_config(config_agent[:forest_server_url])
24
25
  credentials = register(
25
26
  config_agent[:env_secret],
@@ -1,5 +1,5 @@
1
1
  require 'dry-container'
2
- require 'lightly'
2
+ require 'filecache'
3
3
 
4
4
  module ForestAdminAgent
5
5
  module Builder
@@ -21,7 +21,7 @@ module ForestAdminAgent
21
21
  end
22
22
 
23
23
  def add_datasource(datasource)
24
- datasource.collections.each { |_name, collection| @customizer.add_collection(collection) }
24
+ datasource.collections.each_value { |collection| @customizer.add_collection(collection) }
25
25
  self
26
26
  end
27
27
 
@@ -40,9 +40,9 @@ module ForestAdminAgent
40
40
  if !schema_is_know || force
41
41
  # Logger::log('Info', 'schema was updated, sending new version');
42
42
  client = ForestAdminAgent::Http::ForestAdminApiRequester.new
43
- client.post('/forest/apimaps', schema)
44
- schema_file_hash_cache = Lightly.new(life: TTL_SCHEMA, dir: @options[:cache_dir].to_s)
45
- schema_file_hash_cache.get 'value' do
43
+ client.post('/forest/apimaps', schema.to_json)
44
+ schema_file_hash_cache = FileCache.new('app', @options[:cache_dir].to_s, TTL_SCHEMA)
45
+ schema_file_hash_cache.get_or_set 'value' do
46
46
  schema[:meta][:schemaFileHash]
47
47
  end
48
48
  @container.register(:schema_file_hash, schema_file_hash_cache)
@@ -59,13 +59,11 @@ module ForestAdminAgent
59
59
  end
60
60
 
61
61
  def build_cache
62
- @container.register(:cache, Lightly.new(life: TTL_CONFIG, dir: @options[:cache_dir].to_s))
62
+ @container.register(:cache, FileCache.new('app', @options[:cache_dir].to_s, TTL_SCHEMA))
63
63
  return unless @has_env_secret
64
64
 
65
65
  cache = @container.resolve(:cache)
66
- cache.get 'config' do
67
- @options.to_h
68
- end
66
+ cache.set('config', @options.to_h)
69
67
  end
70
68
 
71
69
  def build_logger
@@ -5,7 +5,7 @@ module ForestAdminAgent
5
5
  attr_reader :error, :error_description, :state
6
6
 
7
7
  def initialize(error, error_description, state)
8
- super error, 401, error_description
8
+ super(error, 401, error_description)
9
9
  @error = error
10
10
  @error_description = error_description
11
11
  @state = state
@@ -0,0 +1,14 @@
1
+ module ForestAdminAgent
2
+ module Http
3
+ module Exceptions
4
+ class ConflictError < HttpException
5
+ attr_reader :name
6
+
7
+ def initialize(message, name = 'ConflictError')
8
+ @name = name
9
+ super(429, 'Conflict', message)
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ module ForestAdminAgent
2
+ module Http
3
+ module Exceptions
4
+ class ForbiddenError < HttpException
5
+ attr_reader :name
6
+
7
+ def initialize(message, name = 'ForbiddenError')
8
+ @name = name
9
+ super(403, 'Forbidden', message)
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -5,7 +5,7 @@ module ForestAdminAgent
5
5
  attr_reader :name, :status
6
6
 
7
7
  def initialize(msg, name = 'NotFoundError')
8
- super msg
8
+ super(msg)
9
9
  @name = name
10
10
  @status = 404
11
11
  end
@@ -0,0 +1,15 @@
1
+ module ForestAdminAgent
2
+ module Http
3
+ module Exceptions
4
+ class RequireApproval < HttpException
5
+ attr_reader :name, :data
6
+
7
+ def initialize(message, name = 'RequireApproval', data = [])
8
+ @name = name
9
+ @data = data
10
+ super(403, 'Forbidden', message)
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -3,6 +3,8 @@ require 'faraday'
3
3
  module ForestAdminAgent
4
4
  module Http
5
5
  class ForestAdminApiRequester
6
+ include ForestAdminDatasourceToolkit::Exceptions
7
+
6
8
  def initialize
7
9
  @headers = {
8
10
  'Content-Type' => 'application/json',
@@ -16,12 +18,38 @@ module ForestAdminAgent
16
18
  )
17
19
  end
18
20
 
19
- def get(url, params)
20
- @client.get(url, params.to_json)
21
+ def get(url, params = nil)
22
+ @client.get(url, params)
23
+ end
24
+
25
+ def post(url, params = nil)
26
+ @client.post(url, params)
21
27
  end
22
28
 
23
- def post(url, params)
24
- @client.post(url, params.to_json)
29
+ def handle_response_error(error)
30
+ raise error if error.is_a?(ForestException)
31
+
32
+ if error.response[:message]&.include?('certificate')
33
+ raise ForestException,
34
+ 'ForestAdmin server TLS certificate cannot be verified. Please check that your system time is set properly.'
35
+ end
36
+
37
+ if error.response[:status].zero? || error.response[:status] == 502
38
+ raise ForestException, 'Failed to reach ForestAdmin server. Are you online?'
39
+ end
40
+
41
+ if error.response[:status] == 404
42
+ raise ForestException,
43
+ 'ForestAdmin server failed to find the project related to the envSecret you configured. Can you check that you copied it properly in the Forest initialization?'
44
+ end
45
+
46
+ if error.response[:status] == 503
47
+ raise ForestException,
48
+ 'Forest is in maintenance for a few minutes. We are upgrading your experience in the forest. We just need a few more minutes to get it right.'
49
+ end
50
+
51
+ raise ForestException,
52
+ 'An unexpected error occurred while contacting the ForestAdmin server. Please contact support@forestadmin.com for further investigations.'
25
53
  end
26
54
  end
27
55
  end
@@ -9,6 +9,7 @@ module ForestAdminAgent
9
9
  # api_charts_routes,
10
10
  System::HealthCheck.new.routes,
11
11
  Security::Authentication.new.routes,
12
+ Charts::Charts.new.routes,
12
13
  Resources::Count.new.routes,
13
14
  Resources::Delete.new.routes,
14
15
  Resources::List.new.routes,
@@ -6,6 +6,7 @@ module ForestAdminAgent
6
6
  Facades::Whitelist.check_ip(args[:headers]['action_dispatch.remote_ip'].to_s)
7
7
  end
8
8
  @caller = Utils::QueryStringParser.parse_caller(args)
9
+ @permissions = ForestAdminAgent::Services::Permissions.new(@caller)
9
10
  super
10
11
  end
11
12
 
@@ -0,0 +1,214 @@
1
+ require 'jsonapi-serializers'
2
+ require 'active_support/inflector'
3
+
4
+ module ForestAdminAgent
5
+ module Routes
6
+ module Charts
7
+ class Charts < AbstractAuthenticatedRoute
8
+ include ForestAdminAgent::Builder
9
+ include ForestAdminAgent::Utils
10
+ include ForestAdminDatasourceToolkit::Components::Query
11
+ include ForestAdminDatasourceToolkit::Components::Query::ConditionTree
12
+ include ForestAdminDatasourceToolkit::Components::Charts
13
+
14
+ attr_reader :filter
15
+
16
+ FORMAT = {
17
+ Day: '%d/%m/%Y',
18
+ Week: 'W%W-%Y',
19
+ Month: '%b %y',
20
+ Year: '%Y'
21
+ }.freeze
22
+
23
+ def setup_routes
24
+ add_route('forest_chart', 'post', '/stats/:collection_name', lambda { |args|
25
+ handle_request(args)
26
+ })
27
+ self
28
+ end
29
+
30
+ def handle_request(args = {})
31
+ build(args)
32
+ @permissions.can_chart?(args[:params])
33
+ @args = args
34
+ self.type = args[:params][:type]
35
+ @filter = Filter.new(
36
+ condition_tree: ConditionTreeFactory.intersect(
37
+ [
38
+ @permissions.get_scope(@collection),
39
+ ForestAdminAgent::Utils::QueryStringParser.parse_condition_tree(
40
+ @collection, args
41
+ )
42
+ ]
43
+ )
44
+ )
45
+
46
+ inject_context_variables
47
+
48
+ { content: Serializer::ForestChartSerializer.serialize(send(:"make_#{@type}")) }
49
+ end
50
+
51
+ private
52
+
53
+ def type=(type)
54
+ chart_types = %w[Value Objective Pie Line Leaderboard]
55
+ unless chart_types.include?(type)
56
+ raise ForestAdminDatasourceToolkit::Exceptions::ForestException, "Invalid Chart type #{type}"
57
+ end
58
+
59
+ @type = type.downcase
60
+ end
61
+
62
+ def inject_context_variables
63
+ user = @permissions.get_user_data(@caller.id)
64
+ team = @permissions.get_team(@caller.rendering_id)
65
+
66
+ context_variables = ForestAdminAgent::Utils::ContextVariables.new(team, user,
67
+ @args[:params][:contextVariables])
68
+ return unless @args[:params][:filter]
69
+
70
+ @filter = @filter.override(condition_tree: ContextVariablesInjector.inject_context_in_filter(
71
+ @filter.condition_tree, context_variables
72
+ ))
73
+ end
74
+
75
+ def make_value
76
+ value = compute_value(@filter)
77
+ previous_value = nil
78
+ is_and_aggregator = @filter.condition_tree&.try(:aggregator) == 'And'
79
+ with_count_previous = @filter.condition_tree&.some_leaf(&:use_interval_operator)
80
+
81
+ if with_count_previous && !is_and_aggregator
82
+ previous_value = compute_value(FilterFactory.get_previous_period_filter(@filter, @caller.timezone))
83
+ end
84
+
85
+ ValueChart.new(value, previous_value)
86
+ end
87
+
88
+ def make_objective
89
+ ObjectiveChart.new(compute_value(@filter))
90
+ end
91
+
92
+ def make_pie
93
+ group_field = @args[:params][:groupByFieldName]
94
+ aggregation = Aggregation.new(
95
+ operation: @args[:params][:aggregator],
96
+ field: @args[:params][:aggregateFieldName],
97
+ groups: group_field ? [{ field: group_field }] : []
98
+ )
99
+
100
+ result = @collection.aggregate(@caller, @filter, aggregation)
101
+
102
+ PieChart.new(result.map { |row| { key: row[:group][group_field], value: row[:value] } })
103
+ end
104
+
105
+ def make_line
106
+ group_by_field_name = @args[:params][:groupByFieldName]
107
+ time_range = @args[:params][:timeRange]
108
+ filter_only_with_values = @filter.override(
109
+ condition_tree: ConditionTree::ConditionTreeFactory.intersect(
110
+ [
111
+ @filter.condition_tree,
112
+ ConditionTree::Nodes::ConditionTreeLeaf.new(group_by_field_name, ConditionTree::Operators::PRESENT)
113
+ ]
114
+ )
115
+ )
116
+ rows = @collection.aggregate(
117
+ @caller,
118
+ filter_only_with_values,
119
+ Aggregation.new(
120
+ operation: @args[:params][:aggregator],
121
+ field: @args[:params][:aggregateField],
122
+ groups: [{ field: group_by_field_name, operation: time_range }]
123
+ )
124
+ )
125
+
126
+ values = {}
127
+ rows.each { |row| values[row[:group][group_by_field_name]] = row[:value] }
128
+ dates = values.keys.sort
129
+ current = dates[0]
130
+ last = dates.last
131
+ result = []
132
+ while current <= last
133
+ result << {
134
+ label: current.strftime(FORMAT[time_range.to_sym]),
135
+ values: { value: values[current] || 0 }
136
+ }
137
+ current += 1.send(time_range.downcase.pluralize.to_sym)
138
+ end
139
+
140
+ LineChart.new(result)
141
+ end
142
+
143
+ def make_leaderboard
144
+ field = @collection.fields[@args[:params][:relationshipFieldName]]
145
+
146
+ if field && field.type == 'OneToMany'
147
+ inverse = ForestAdminDatasourceToolkit::Utils::Collection.get_inverse_relation(
148
+ @collection,
149
+ @args[:params][:relationshipFieldName]
150
+ )
151
+ if inverse
152
+ collection = field.foreign_collection
153
+ filter = @filter.nest(inverse)
154
+ aggregation = Aggregation.new(
155
+ operation: @args[:params][:aggregator],
156
+ field: @args[:params][:aggregateFieldName],
157
+ groups: [{ field: "#{inverse}:#{@args[:params][:labelFieldName]}" }]
158
+ )
159
+ end
160
+ end
161
+
162
+ if field && field.type == 'ManyToMany'
163
+ origin = ForestAdminDatasourceToolkit::Utils::Collection.get_through_origin(
164
+ @collection,
165
+ @args[:params][:relationshipFieldName]
166
+ )
167
+ target = ForestAdminDatasourceToolkit::Utils::Collection.get_through_target(
168
+ @collection,
169
+ @args[:params][:relationshipFieldName]
170
+ )
171
+ if origin && target
172
+ collection = field.through_collection
173
+ filter = @filter.nest(origin)
174
+ aggregation = Aggregation.new(
175
+ operation: @args[:params][:aggregator],
176
+ field: @args[:params][:aggregateFieldName] ? "#{target}:#{@args[:params][:aggregateFieldName]}" : nil,
177
+ groups: [{ field: "#{origin}:#{@args[:params][:labelFieldName]}" }]
178
+ )
179
+ end
180
+ end
181
+
182
+ if collection && filter && aggregation
183
+ rows = @datasource.collection(collection).aggregate(
184
+ @caller,
185
+ filter,
186
+ aggregation,
187
+ @args[:params][:limit]
188
+ )
189
+
190
+ result = rows.map do |row|
191
+ {
192
+ key: row[:group][aggregation.groups[0][:field]],
193
+ value: row[:value]
194
+ }
195
+ end
196
+
197
+ return LeaderboardChart.new(result)
198
+ end
199
+
200
+ raise ForestAdminDatasourceToolkit::Exceptions::ForestException,
201
+ 'Failed to generate leaderboard chart: parameters do not match pre-requisites'
202
+ end
203
+
204
+ def compute_value(filter)
205
+ aggregation = Aggregation.new(operation: @args[:params][:aggregator],
206
+ field: @args[:params][:aggregateFieldName])
207
+ result = @collection.aggregate(@caller, filter, aggregation)
208
+
209
+ result[0][:value] || 0
210
+ end
211
+ end
212
+ end
213
+ end
214
+ end
@@ -3,8 +3,9 @@ require 'jsonapi-serializers'
3
3
  module ForestAdminAgent
4
4
  module Routes
5
5
  module Resources
6
- class Count < AbstractRoute
6
+ class Count < AbstractAuthenticatedRoute
7
7
  include ForestAdminAgent::Builder
8
+ include ForestAdminDatasourceToolkit::Components::Query::ConditionTree
8
9
  def setup_routes
9
10
  add_route('forest_count', 'get', '/:collection_name/count', ->(args) { handle_request(args) })
10
11
 
@@ -13,12 +14,14 @@ module ForestAdminAgent
13
14
 
14
15
  def handle_request(args = {})
15
16
  build(args)
17
+ @permissions.can?(:browse, @collection)
16
18
 
17
19
  if @collection.is_countable?
18
- caller = ForestAdminAgent::Utils::QueryStringParser.parse_caller(args)
19
- filter = ForestAdminDatasourceToolkit::Components::Query::Filter.new
20
+ filter = ForestAdminDatasourceToolkit::Components::Query::Filter.new(
21
+ condition_tree: @permissions.get_scope(@collection)
22
+ )
20
23
  aggregation = ForestAdminDatasourceToolkit::Components::Query::Aggregation.new(operation: 'Count')
21
- result = @collection.aggregate(caller, filter, aggregation)
24
+ result = @collection.aggregate(@caller, filter, aggregation)
22
25
 
23
26
  return {
24
27
  name: args[:params]['collection_name'],
@@ -7,6 +7,7 @@ module ForestAdminAgent
7
7
  class Delete < AbstractAuthenticatedRoute
8
8
  include ForestAdminAgent::Builder
9
9
  include ForestAdminDatasourceToolkit::Components::Query
10
+ include ForestAdminDatasourceToolkit::Components::Query::ConditionTree
10
11
 
11
12
  def setup_routes
12
13
  add_route('forest_delete_bulk', 'delete', '/:collection_name', ->(args) { handle_request_bulk(args) })
@@ -17,6 +18,7 @@ module ForestAdminAgent
17
18
 
18
19
  def handle_request(args = {})
19
20
  build(args)
21
+ @permissions.can?(:delete, @collection)
20
22
  id = Utils::Id.unpack_id(@collection, args[:params]['id'], with_key: true)
21
23
  delete_records(args, { ids: [id], are_excluded: false })
22
24
 
@@ -25,6 +27,7 @@ module ForestAdminAgent
25
27
 
26
28
  def handle_request_bulk(args = {})
27
29
  build(args)
30
+ @permissions.can?(:delete, @collection)
28
31
  selection_ids = Utils::Id.parse_selection_ids(@collection, args[:params], with_key: true)
29
32
  delete_records(args, selection_ids)
30
33
 
@@ -38,7 +41,8 @@ module ForestAdminAgent
38
41
  condition_tree: ConditionTree::ConditionTreeFactory.intersect(
39
42
  [
40
43
  Utils::QueryStringParser.parse_condition_tree(@collection, args),
41
- condition_tree_ids
44
+ condition_tree_ids,
45
+ @permissions.get_scope(@collection)
42
46
  ]
43
47
  )
44
48
  )
@@ -5,6 +5,7 @@ module ForestAdminAgent
5
5
  module Resources
6
6
  class List < AbstractAuthenticatedRoute
7
7
  include ForestAdminAgent::Builder
8
+ include ForestAdminDatasourceToolkit::Components::Query::ConditionTree
8
9
  def setup_routes
9
10
  add_route('forest_list', 'get', '/:collection_name', ->(args) { handle_request(args) })
10
11
 
@@ -13,14 +14,19 @@ module ForestAdminAgent
13
14
 
14
15
  def handle_request(args = {})
15
16
  build(args)
16
- caller = ForestAdminAgent::Utils::QueryStringParser.parse_caller(args)
17
+ @permissions.can?(:browse, @collection)
17
18
 
18
19
  filter = ForestAdminDatasourceToolkit::Components::Query::Filter.new(
19
- condition_tree: ForestAdminAgent::Utils::QueryStringParser.parse_condition_tree(@collection, args),
20
+ condition_tree: ConditionTreeFactory.intersect([
21
+ @permissions.get_scope(@collection),
22
+ ForestAdminAgent::Utils::QueryStringParser.parse_condition_tree(
23
+ @collection, args
24
+ )
25
+ ]),
20
26
  page: ForestAdminAgent::Utils::QueryStringParser.parse_pagination(args)
21
27
  )
22
28
  projection = ForestAdminAgent::Utils::QueryStringParser.parse_projection_with_pks(@collection, args)
23
- records = @collection.list(caller, filter, projection)
29
+ records = @collection.list(@caller, filter, projection)
24
30
 
25
31
  {
26
32
  name: args[:params]['collection_name'],
@@ -8,6 +8,7 @@ module ForestAdminAgent
8
8
  include ForestAdminAgent::Builder
9
9
  include ForestAdminDatasourceToolkit::Utils
10
10
  include ForestAdminDatasourceToolkit::Components::Query
11
+
11
12
  def setup_routes
12
13
  add_route(
13
14
  'forest_related_associate',
@@ -21,6 +22,7 @@ module ForestAdminAgent
21
22
 
22
23
  def handle_request(args = {})
23
24
  build(args)
25
+ @permissions.can?(:edit, @collection)
24
26
 
25
27
  parent_id = Utils::Id.unpack_id(@collection, args[:params]['id'], with_key: true)
26
28
  target_relation_id = Utils::Id.unpack_id(@child_collection, args[:params]['data'][0]['id'], with_key: true)
@@ -40,7 +42,14 @@ module ForestAdminAgent
40
42
  def associate_one_to_many(relation, parent_id, target_relation_id)
41
43
  id = Schema.primary_keys(@child_collection)[0]
42
44
  value = Collection.get_value(@child_collection, @caller, target_relation_id, id)
43
- filter = Filter.new(condition_tree: ConditionTree::Nodes::ConditionTreeLeaf.new(id, 'Equal', value))
45
+ filter = Filter.new(
46
+ condition_tree: ConditionTree::ConditionTreeFactory.intersect(
47
+ [
48
+ ConditionTree::Nodes::ConditionTreeLeaf.new(id, 'Equal', value),
49
+ @permissions.get_scope(@collection)
50
+ ]
51
+ )
52
+ )
44
53
  value = Collection.get_value(@collection, @caller, parent_id, relation.origin_key_target)
45
54
 
46
55
  @child_collection.update(@caller, filter, { relation.origin_key => value })
@@ -21,9 +21,10 @@ module ForestAdminAgent
21
21
 
22
22
  def handle_request(args = {})
23
23
  build(args)
24
+ @permissions.can?(:browse, @collection)
24
25
 
25
26
  if @child_collection.is_countable?
26
- filter = Filter.new
27
+ filter = Filter.new(condition_tree: @permissions.get_scope(@collection))
27
28
  id = Utils::Id.unpack_id(@collection, args[:params]['id'], with_key: true)
28
29
  result = Collection.aggregate_relation(
29
30
  @collection,
@@ -21,6 +21,7 @@ module ForestAdminAgent
21
21
 
22
22
  def handle_request(args = {})
23
23
  build(args)
24
+ @permissions.can?(:delete, @collection)
24
25
 
25
26
  parent_id = Utils::Id.unpack_id(@collection, args[:params]['id'], with_key: true)
26
27
  is_delete_mode = !args.dig(:params, :delete).nil?
@@ -84,6 +85,7 @@ module ForestAdminAgent
84
85
  Filter.new(
85
86
  condition_tree: ConditionTree::ConditionTreeFactory.intersect(
86
87
  [
88
+ @permissions.get_scope(@child_collection),
87
89
  Utils::QueryStringParser.parse_condition_tree(@child_collection, args),
88
90
  selected_ids
89
91
  ]
@@ -7,6 +7,8 @@ module ForestAdminAgent
7
7
  class ListRelated < AbstractRelatedRoute
8
8
  include ForestAdminAgent::Builder
9
9
  include ForestAdminDatasourceToolkit::Utils
10
+ include ForestAdminDatasourceToolkit::Components::Query::ConditionTree
11
+
10
12
  def setup_routes
11
13
  add_route(
12
14
  'forest_related_list',
@@ -20,10 +22,16 @@ module ForestAdminAgent
20
22
 
21
23
  def handle_request(args = {})
22
24
  build(args)
25
+ @permissions.can?(:browse, @collection)
23
26
  # TODO: add csv behaviour
24
27
 
25
28
  filter = ForestAdminDatasourceToolkit::Components::Query::Filter.new(
26
- condition_tree: ForestAdminAgent::Utils::QueryStringParser.parse_condition_tree(@child_collection, args),
29
+ condition_tree: ConditionTreeFactory.intersect(
30
+ [
31
+ @permissions.get_scope(@collection),
32
+ ForestAdminAgent::Utils::QueryStringParser.parse_condition_tree(@child_collection, args)
33
+ ]
34
+ ),
27
35
  page: ForestAdminAgent::Utils::QueryStringParser.parse_pagination(args)
28
36
  )
29
37
  projection = ForestAdminAgent::Utils::QueryStringParser.parse_projection_with_pks(@child_collection, args)
@@ -21,6 +21,7 @@ module ForestAdminAgent
21
21
 
22
22
  def handle_request(args = {})
23
23
  build(args)
24
+ @permissions.can?(:edit, @collection)
24
25
 
25
26
  relation = @collection.fields[args[:params]['relation_name']]
26
27
  parent_id = Utils::Id.unpack_id(@collection, args[:params]['id'])
@@ -61,6 +62,7 @@ module ForestAdminAgent
61
62
  old_fk_owner_filter = Filter.new(
62
63
  condition_tree: ConditionTree::ConditionTreeFactory.intersect(
63
64
  [
65
+ @permissions.get_scope(@collection),
64
66
  ConditionTree::Nodes::ConditionTreeLeaf.new(
65
67
  relation.origin_key,
66
68
  ConditionTree::Operators::EQUAL,
@@ -91,7 +93,12 @@ module ForestAdminAgent
91
93
 
92
94
  @child_collection.update(
93
95
  @caller,
94
- Filter.new(condition_tree: new_fk_owner),
96
+ Filter.new(condition_tree: ConditionTree::ConditionTreeFactory.intersect(
97
+ [
98
+ @permissions.get_scope(@collection),
99
+ new_fk_owner
100
+ ]
101
+ )),
95
102
  { relation.origin_key => origin_value }
96
103
  )
97
104
  end