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

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 (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