Skip to content

Commit f45d77f

Browse files
authored
[+] Resources getter - Add a get_ids_from_request method to get all models IDs given a query or an ID list (#345) (#349)
1 parent e6813e3 commit f45d77f

File tree

9 files changed

+94
-21
lines changed

9 files changed

+94
-21
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
# Change Log
22

33
## [Unreleased]
4+
5+
## RELEASE 5.0.0-beta.0 - 2020-03-03
6+
### Added
7+
- Resources Getter - Add a get_ids_from_request method to get all models IDs given a query or an ID list.
8+
- Resource Deletion - Users can now bulk delete records.
9+
410
### Fixed
511
- Has Many Relationships - Fix records count, i.e consider filters when counting.
612

Gemfile.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
PATH
22
remote: .
33
specs:
4-
forest_liana (4.2.0)
4+
forest_liana (5.0.0.pre.beta.0)
55
arel-helpers
66
base32
77
bcrypt

app/controllers/forest_liana/resources_controller.rb

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -137,16 +137,28 @@ def update
137137
end
138138

139139
def destroy
140-
begin
141-
checker = ForestLiana::PermissionsChecker.new(@resource, 'delete', @rendering_id)
142-
return head :forbidden unless checker.is_authorized?
140+
checker = ForestLiana::PermissionsChecker.new(@resource, 'delete', @rendering_id)
141+
return head :forbidden unless checker.is_authorized?
143142

144-
@resource.destroy(params[:id]) if @resource.exists?(params[:id])
145-
head :no_content
146-
rescue => error
147-
FOREST_LOGGER.error "Record Destroy error: #{error}\n#{format_stacktrace(error)}"
148-
internal_server_error
149-
end
143+
@resource.destroy(params[:id]) if @resource.exists?(params[:id])
144+
145+
head :no_content
146+
rescue => error
147+
FOREST_LOGGER.error "Record Destroy error: #{error}\n#{format_stacktrace(error)}"
148+
internal_server_error
149+
end
150+
151+
def destroy_bulk
152+
checker = ForestLiana::PermissionsChecker.new(@resource, 'delete', @rendering_id)
153+
return head :forbidden unless checker.is_authorized?
154+
155+
ids = ForestLiana::ResourcesGetter.get_ids_from_request(params)
156+
@resource.destroy(ids) if ids&.any?
157+
158+
head :no_content
159+
rescue => error
160+
FOREST_LOGGER.error "Records Destroy error: #{error}\n#{format_stacktrace(error)}"
161+
internal_server_error
150162
end
151163

152164
private

app/controllers/forest_liana/router.rb

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,11 @@ def call(env)
3030
when 'POST'
3131
action = 'create'
3232
when 'DELETE'
33-
action = 'destroy'
33+
if params[:id]
34+
action = 'destroy'
35+
else
36+
action = 'destroy_bulk'
37+
end
3438
end
3539

3640
controller.action(action.to_sym).call(env)

app/services/forest_liana/has_many_dissociator.rb

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,22 @@ def perform
1414

1515
remove_association = !@with_deletion || @association.macro == :has_and_belongs_to_many
1616

17-
if remove_association
18-
if @data.is_a?(Array)
19-
@data.each do |record_deleted|
20-
associated_records.delete(@association.klass.find(record_deleted[:id]))
17+
if @data.is_a?(Array)
18+
record_ids = @data.map { |record| record[:id] }
19+
elsif @data.dig('attributes').present?
20+
record_ids = ForestLiana::ResourcesGetter.get_ids_from_request(@params)
21+
else
22+
record_ids = Array.new
23+
end
24+
25+
if !record_ids.nil? && record_ids.any?
26+
if remove_association
27+
record_ids.each do |id|
28+
associated_records.delete(@association.klass.find(id))
2129
end
2230
end
23-
end
2431

25-
if @with_deletion
26-
if @data.is_a?(Array)
27-
record_ids = @data.map { |record| record[:id] }
32+
if @with_deletion
2833
record_ids = record_ids.select { |record_id| @association.klass.exists?(record_id) }
2934
@association.klass.destroy(record_ids)
3035
end

app/services/forest_liana/resources_getter.rb

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,51 @@ def initialize(resource, params)
1919
prepare_query()
2020
end
2121

22+
def self.get_ids_from_request(params)
23+
attributes = params.dig('data', 'attributes')
24+
has_body_attributes = attributes != nil
25+
is_select_all_records_query = has_body_attributes && attributes[:all_records] == true
26+
27+
# NOTICE: If it is not a "select all records" query and it receives a list of ID, return list of ID.
28+
return attributes[:ids] if (!is_select_all_records_query && attributes[:ids])
29+
30+
# NOTICE: If it is a "select all records" we have to perform query to build ID list.
31+
ids = Array.new
32+
33+
# NOTICE: Merging all_records_subset_query into attributes preserves filters in HasManyGetter and ResourcesGetter.
34+
attributes = attributes.merge(attributes[:all_records_subset_query].dup.to_unsafe_h)
35+
36+
# NOTICE: Initialize actual resources getter (could either a HasManyGetter or a ResourcesGetter).
37+
is_related_data = attributes[:parent_collection_id] &&
38+
attributes[:parent_collection_name] &&
39+
attributes[:parent_association_name]
40+
if is_related_data
41+
parent_collection_name = attributes[:parent_collection_name]
42+
parent_model = ForestLiana::SchemaUtils.find_model_from_collection_name(parent_collection_name)
43+
model = parent_model.reflect_on_association(attributes[:parent_association_name].try(:to_sym))
44+
resources_getter = ForestLiana::HasManyGetter.new(parent_model, model, attributes.merge({
45+
collection: parent_collection_name,
46+
id: attributes[:parent_collection_id],
47+
association_name: attributes[:parent_association_name],
48+
}))
49+
else
50+
collection_name = attributes[:collection_name]
51+
model = ForestLiana::SchemaUtils.find_model_from_collection_name(collection_name)
52+
resources_getter = ForestLiana::ResourcesGetter.new(model, attributes)
53+
end
54+
55+
# NOTICE: build IDs list.
56+
resources_getter.query_for_batch.find_in_batches() do |records|
57+
ids += records.map { |record| record.id }
58+
end
59+
60+
# NOTICE: remove excluded IDs.
61+
ids_excluded = (attributes[:all_records_ids_excluded]).map { |id_excluded| id_excluded.to_s }
62+
return ids.select { |id| !ids_excluded.include? id.to_s } if (ids_excluded && ids_excluded.any?)
63+
64+
return ids
65+
end
66+
2267
def perform
2368
@records = @records.eager_load(@includes)
2469
end

config/routes.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
post ':collection', to: router
5454
put ':collection/:id', to: router
5555
delete ':collection/:id', to: router
56+
delete ':collection', to: router
5657

5758
# Smart Actions forms value
5859
post 'actions/:action_name/values' => 'actions#values'

lib/forest_liana/version.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
module ForestLiana
2-
VERSION = "4.2.0"
2+
VERSION = "5.0.0-beta.0"
33
end

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "forest-rails",
3-
"version": "4.2.0",
3+
"version": "5.0.0-beta.0",
44
"description": "The official Rails liana for Forest.",
55
"directories": {
66
"test": "test"

0 commit comments

Comments
 (0)