forest_admin_agent 1.12.14 → 1.12.16
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.
- checksums.yaml +4 -4
- data/lib/forest_admin_agent/builder/agent_factory.rb +1 -14
- data/lib/forest_admin_agent/facades/container.rb +4 -3
- data/lib/forest_admin_agent/routes/abstract_authenticated_route.rb +7 -5
- data/lib/forest_admin_agent/routes/abstract_related_route.rb +9 -7
- data/lib/forest_admin_agent/routes/abstract_route.rb +7 -3
- data/lib/forest_admin_agent/routes/action/actions.rb +31 -25
- data/lib/forest_admin_agent/routes/capabilities/collections.rb +4 -4
- data/lib/forest_admin_agent/routes/charts/api_chart_collection.rb +10 -10
- data/lib/forest_admin_agent/routes/charts/api_chart_datasource.rb +8 -7
- data/lib/forest_admin_agent/routes/charts/charts.rb +64 -66
- data/lib/forest_admin_agent/routes/request_context.rb +17 -0
- data/lib/forest_admin_agent/routes/resources/count.rb +9 -9
- data/lib/forest_admin_agent/routes/resources/csv.rb +11 -11
- data/lib/forest_admin_agent/routes/resources/delete.rb +20 -19
- data/lib/forest_admin_agent/routes/resources/list.rb +14 -14
- data/lib/forest_admin_agent/routes/resources/native_query.rb +9 -9
- data/lib/forest_admin_agent/routes/resources/related/associate_related.rb +32 -29
- data/lib/forest_admin_agent/routes/resources/related/count_related.rb +9 -9
- data/lib/forest_admin_agent/routes/resources/related/csv_related.rb +10 -9
- data/lib/forest_admin_agent/routes/resources/related/dissociate_related.rb +63 -40
- data/lib/forest_admin_agent/routes/resources/related/list_related.rb +12 -11
- data/lib/forest_admin_agent/routes/resources/related/update_related.rb +60 -52
- data/lib/forest_admin_agent/routes/resources/show.rb +8 -8
- data/lib/forest_admin_agent/routes/resources/store.rb +18 -15
- data/lib/forest_admin_agent/routes/resources/update.rb +9 -9
- data/lib/forest_admin_agent/routes/resources/update_field.rb +17 -17
- data/lib/forest_admin_agent/utils/schema/schema_emitter.rb +1 -1
- data/lib/forest_admin_agent/version.rb +1 -1
- metadata +3 -2
|
@@ -15,24 +15,24 @@ module ForestAdminAgent
|
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
def handle_request(args = {})
|
|
18
|
-
build(args)
|
|
19
|
-
|
|
18
|
+
context = build(args)
|
|
19
|
+
context.permissions.can?(:browse, context.collection)
|
|
20
20
|
|
|
21
|
-
if
|
|
21
|
+
if context.collection.is_countable?
|
|
22
22
|
filter = ForestAdminDatasourceToolkit::Components::Query::Filter.new(
|
|
23
23
|
condition_tree: ConditionTreeFactory.intersect(
|
|
24
24
|
[
|
|
25
|
-
|
|
26
|
-
parse_query_segment(
|
|
27
|
-
ForestAdminAgent::Utils::QueryStringParser.parse_condition_tree(
|
|
25
|
+
context.permissions.get_scope(context.collection),
|
|
26
|
+
parse_query_segment(context.collection, args, context.permissions, context.caller),
|
|
27
|
+
ForestAdminAgent::Utils::QueryStringParser.parse_condition_tree(context.collection, args)
|
|
28
28
|
]
|
|
29
29
|
),
|
|
30
|
-
search: QueryStringParser.parse_search(
|
|
30
|
+
search: QueryStringParser.parse_search(context.collection, args),
|
|
31
31
|
search_extended: QueryStringParser.parse_search_extended(args),
|
|
32
|
-
segment: QueryStringParser.parse_segment(
|
|
32
|
+
segment: QueryStringParser.parse_segment(context.collection, args)
|
|
33
33
|
)
|
|
34
34
|
aggregation = ForestAdminDatasourceToolkit::Components::Query::Aggregation.new(operation: 'Count')
|
|
35
|
-
result =
|
|
35
|
+
result = context.collection.aggregate(context.caller, filter, aggregation)
|
|
36
36
|
|
|
37
37
|
return {
|
|
38
38
|
name: args[:params]['collection_name'],
|
|
@@ -19,25 +19,25 @@ module ForestAdminAgent
|
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
def handle_request(args = {})
|
|
22
|
-
build(args)
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
context = build(args)
|
|
23
|
+
context.permissions.can?(:browse, context.collection)
|
|
24
|
+
context.permissions.can?(:export, context.collection)
|
|
25
25
|
filter = ForestAdminDatasourceToolkit::Components::Query::Filter.new(
|
|
26
26
|
condition_tree: ConditionTreeFactory.intersect(
|
|
27
27
|
[
|
|
28
|
-
|
|
29
|
-
parse_query_segment(
|
|
28
|
+
context.permissions.get_scope(context.collection),
|
|
29
|
+
parse_query_segment(context.collection, args, context.permissions, context.caller),
|
|
30
30
|
QueryStringParser.parse_condition_tree(
|
|
31
|
-
|
|
31
|
+
context.collection, args
|
|
32
32
|
)
|
|
33
33
|
]
|
|
34
34
|
),
|
|
35
|
-
search: QueryStringParser.parse_search(
|
|
35
|
+
search: QueryStringParser.parse_search(context.collection, args),
|
|
36
36
|
search_extended: QueryStringParser.parse_search_extended(args),
|
|
37
|
-
sort: QueryStringParser.parse_sort(
|
|
38
|
-
segment: QueryStringParser.parse_segment(
|
|
37
|
+
sort: QueryStringParser.parse_sort(context.collection, args),
|
|
38
|
+
segment: QueryStringParser.parse_segment(context.collection, args)
|
|
39
39
|
)
|
|
40
|
-
projection = QueryStringParser.parse_projection(
|
|
40
|
+
projection = QueryStringParser.parse_projection(context.collection, args)
|
|
41
41
|
filename = args[:params][:filename] || args[:params]['collection_name']
|
|
42
42
|
filename += '.csv' unless /\.csv$/i.match?(filename)
|
|
43
43
|
header = args[:params][:header]
|
|
@@ -47,7 +47,7 @@ module ForestAdminAgent
|
|
|
47
47
|
filename_with_timestamp = filename.gsub('.csv', "_export_#{now}.csv")
|
|
48
48
|
|
|
49
49
|
# Return streaming enumerator instead of full CSV string
|
|
50
|
-
list_records = ->(batch_filter) {
|
|
50
|
+
list_records = ->(batch_filter) { context.collection.list(context.caller, batch_filter, projection) }
|
|
51
51
|
|
|
52
52
|
{
|
|
53
53
|
content: {
|
|
@@ -17,35 +17,36 @@ module ForestAdminAgent
|
|
|
17
17
|
end
|
|
18
18
|
|
|
19
19
|
def handle_request(args = {})
|
|
20
|
-
build(args)
|
|
21
|
-
|
|
22
|
-
primary_key_values = Utils::Id.unpack_id(
|
|
23
|
-
delete_records(args, { ids: [primary_key_values], are_excluded: false })
|
|
20
|
+
context = build(args)
|
|
21
|
+
context.permissions.can?(:delete, context.collection)
|
|
22
|
+
primary_key_values = Utils::Id.unpack_id(context.collection, args[:params]['id'], with_key: true)
|
|
23
|
+
delete_records(args, { ids: [primary_key_values], are_excluded: false }, context)
|
|
24
24
|
|
|
25
25
|
{ content: nil, status: 204 }
|
|
26
26
|
end
|
|
27
27
|
|
|
28
28
|
def handle_request_bulk(args = {})
|
|
29
|
-
build(args)
|
|
30
|
-
|
|
31
|
-
selection_ids = Utils::Id.parse_selection_ids(
|
|
32
|
-
delete_records(args, selection_ids)
|
|
29
|
+
context = build(args)
|
|
30
|
+
context.permissions.can?(:delete, context.collection)
|
|
31
|
+
selection_ids = Utils::Id.parse_selection_ids(context.collection, args[:params], with_key: true)
|
|
32
|
+
delete_records(args, selection_ids, context)
|
|
33
33
|
|
|
34
34
|
{ content: nil, status: 204 }
|
|
35
35
|
end
|
|
36
36
|
|
|
37
|
-
def delete_records(args, selection_ids)
|
|
38
|
-
condition_tree_ids = ConditionTree::ConditionTreeFactory.match_records(
|
|
37
|
+
def delete_records(args, selection_ids, context)
|
|
38
|
+
condition_tree_ids = ConditionTree::ConditionTreeFactory.match_records(context.collection,
|
|
39
|
+
selection_ids[:ids])
|
|
39
40
|
condition_tree_ids = condition_tree_ids.inverse if selection_ids[:are_excluded]
|
|
40
41
|
|
|
41
|
-
|
|
42
|
+
context.collection.schema[:fields].each_value do |field_schema|
|
|
42
43
|
next unless ['PolymorphicOneToOne', 'PolymorphicOneToMany'].include?(field_schema.type)
|
|
43
44
|
|
|
44
45
|
origin_values = selection_ids[:ids].map do |pk_hash|
|
|
45
46
|
if pk_hash.is_a?(Hash)
|
|
46
47
|
pk_hash[field_schema.origin_key_target]
|
|
47
48
|
else
|
|
48
|
-
pk_names = ForestAdminDatasourceToolkit::Utils::Schema.primary_keys(
|
|
49
|
+
pk_names = ForestAdminDatasourceToolkit::Utils::Schema.primary_keys(context.collection)
|
|
49
50
|
index = pk_names.index(field_schema.origin_key_target)
|
|
50
51
|
pk_hash[index] if index
|
|
51
52
|
end
|
|
@@ -56,26 +57,26 @@ module ForestAdminAgent
|
|
|
56
57
|
[
|
|
57
58
|
Nodes::ConditionTreeLeaf.new(field_schema.origin_key, Operators::IN, origin_values),
|
|
58
59
|
Nodes::ConditionTreeLeaf.new(field_schema.origin_type_field, Operators::EQUAL,
|
|
59
|
-
|
|
60
|
+
context.collection.name.gsub('__', '::'))
|
|
60
61
|
]
|
|
61
62
|
)
|
|
62
63
|
filter = Filter.new(condition_tree: condition_tree)
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
64
|
+
context.datasource.get_collection(field_schema.foreign_collection)
|
|
65
|
+
.update(context.caller, filter, { field_schema.origin_key => nil,
|
|
66
|
+
field_schema.origin_type_field => nil })
|
|
66
67
|
end
|
|
67
68
|
|
|
68
69
|
filter = ForestAdminDatasourceToolkit::Components::Query::Filter.new(
|
|
69
70
|
condition_tree: ConditionTree::ConditionTreeFactory.intersect(
|
|
70
71
|
[
|
|
71
|
-
Utils::QueryStringParser.parse_condition_tree(
|
|
72
|
+
Utils::QueryStringParser.parse_condition_tree(context.collection, args),
|
|
72
73
|
condition_tree_ids,
|
|
73
|
-
|
|
74
|
+
context.permissions.get_scope(context.collection)
|
|
74
75
|
]
|
|
75
76
|
)
|
|
76
77
|
)
|
|
77
78
|
|
|
78
|
-
|
|
79
|
+
context.collection.delete(context.caller, filter)
|
|
79
80
|
end
|
|
80
81
|
end
|
|
81
82
|
end
|
|
@@ -15,45 +15,45 @@ module ForestAdminAgent
|
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
def handle_request(args = {})
|
|
18
|
-
build(args)
|
|
19
|
-
|
|
18
|
+
context = build(args)
|
|
19
|
+
context.permissions.can?(:browse, context.collection)
|
|
20
20
|
|
|
21
21
|
filter = ForestAdminDatasourceToolkit::Components::Query::Filter.new(
|
|
22
22
|
condition_tree: ConditionTreeFactory.intersect(
|
|
23
23
|
[
|
|
24
|
-
|
|
25
|
-
QueryStringParser.parse_condition_tree(
|
|
26
|
-
parse_query_segment(
|
|
24
|
+
context.permissions.get_scope(context.collection),
|
|
25
|
+
QueryStringParser.parse_condition_tree(context.collection, args),
|
|
26
|
+
parse_query_segment(context.collection, args, context.permissions, context.caller)
|
|
27
27
|
]
|
|
28
28
|
),
|
|
29
29
|
page: QueryStringParser.parse_pagination(args),
|
|
30
|
-
search: QueryStringParser.parse_search(
|
|
30
|
+
search: QueryStringParser.parse_search(context.collection, args),
|
|
31
31
|
search_extended: QueryStringParser.parse_search_extended(args),
|
|
32
|
-
sort: QueryStringParser.parse_sort(
|
|
33
|
-
segment: QueryStringParser.parse_segment(
|
|
32
|
+
sort: QueryStringParser.parse_sort(context.collection, args),
|
|
33
|
+
segment: QueryStringParser.parse_segment(context.collection, args)
|
|
34
34
|
)
|
|
35
35
|
|
|
36
|
-
projection = QueryStringParser.parse_projection_with_pks(
|
|
37
|
-
records =
|
|
36
|
+
projection = QueryStringParser.parse_projection_with_pks(context.collection, args)
|
|
37
|
+
records = context.collection.list(context.caller, filter, projection)
|
|
38
38
|
|
|
39
39
|
{
|
|
40
40
|
name: args[:params]['collection_name'],
|
|
41
41
|
content: JSONAPI::Serializer.serialize(
|
|
42
42
|
records,
|
|
43
|
-
class_name:
|
|
43
|
+
class_name: context.collection.name,
|
|
44
44
|
is_collection: true,
|
|
45
45
|
serializer: Serializer::ForestSerializer,
|
|
46
46
|
include: projection.relations(only_keys: true),
|
|
47
|
-
meta: handle_search_decorator(args[:params]['search'], records)
|
|
47
|
+
meta: handle_search_decorator(args[:params]['search'], records, context.collection)
|
|
48
48
|
)
|
|
49
49
|
}
|
|
50
50
|
end
|
|
51
51
|
|
|
52
|
-
def handle_search_decorator(search_value, records)
|
|
52
|
+
def handle_search_decorator(search_value, records, collection)
|
|
53
53
|
decorator = { decorators: [] }
|
|
54
54
|
unless search_value.nil?
|
|
55
55
|
records.each_with_index do |entry, index|
|
|
56
|
-
decorator[:decorators][index] = { id: Utils::Id.pack_id(
|
|
56
|
+
decorator[:decorators][index] = { id: Utils::Id.pack_id(collection, entry), search: [] }
|
|
57
57
|
# attributes method is defined on ActiveRecord::Base model
|
|
58
58
|
attributes = entry.respond_to?(:attributes) ? entry.attributes : entry
|
|
59
59
|
|
|
@@ -26,7 +26,7 @@ module ForestAdminAgent
|
|
|
26
26
|
end
|
|
27
27
|
|
|
28
28
|
def handle_request(args = {})
|
|
29
|
-
build(args)
|
|
29
|
+
context = build(args)
|
|
30
30
|
query = args[:params][:query].strip
|
|
31
31
|
|
|
32
32
|
QueryValidator.valid?(query)
|
|
@@ -34,31 +34,31 @@ module ForestAdminAgent
|
|
|
34
34
|
raise ForestAdminAgent::Http::Exceptions::UnprocessableError, 'Missing native query connection attribute'
|
|
35
35
|
end
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
context.permissions.can_chart?(args[:params])
|
|
38
38
|
|
|
39
39
|
query.gsub!('?', args[:params][:record_id].to_s) if args[:params][:record_id]
|
|
40
|
-
|
|
40
|
+
type = validate_and_get_type(args[:params][:type])
|
|
41
41
|
result = execute_query(
|
|
42
|
-
|
|
42
|
+
context.datasource,
|
|
43
43
|
query,
|
|
44
44
|
args[:params][:connectionName],
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
context.permissions,
|
|
46
|
+
context.caller,
|
|
47
47
|
args[:params][:contextVariables]
|
|
48
48
|
)
|
|
49
49
|
|
|
50
|
-
{ content: Serializer::ForestChartSerializer.serialize(send(:"make_#{
|
|
50
|
+
{ content: Serializer::ForestChartSerializer.serialize(send(:"make_#{type}", result)) }
|
|
51
51
|
end
|
|
52
52
|
|
|
53
53
|
private
|
|
54
54
|
|
|
55
|
-
def
|
|
55
|
+
def validate_and_get_type(type)
|
|
56
56
|
chart_types = %w[Value Objective Pie Line Leaderboard]
|
|
57
57
|
unless chart_types.include?(type)
|
|
58
58
|
raise ForestAdminDatasourceToolkit::Exceptions::ForestException, "Invalid Chart type #{type}"
|
|
59
59
|
end
|
|
60
60
|
|
|
61
|
-
|
|
61
|
+
type.downcase
|
|
62
62
|
end
|
|
63
63
|
|
|
64
64
|
def raise_error(result, key_names)
|
|
@@ -21,21 +21,21 @@ module ForestAdminAgent
|
|
|
21
21
|
end
|
|
22
22
|
|
|
23
23
|
def handle_request(args = {})
|
|
24
|
-
build(args)
|
|
25
|
-
|
|
24
|
+
context = build(args)
|
|
25
|
+
context.permissions.can?(:edit, context.collection)
|
|
26
26
|
|
|
27
|
-
parent_primary_key_values = Utils::Id.unpack_id(
|
|
28
|
-
target_primary_key_values = Utils::Id.unpack_id(
|
|
27
|
+
parent_primary_key_values = Utils::Id.unpack_id(context.collection, args[:params]['id'], with_key: true)
|
|
28
|
+
target_primary_key_values = Utils::Id.unpack_id(context.child_collection, args[:params]['data'][0]['id'],
|
|
29
29
|
with_key: true)
|
|
30
|
-
relation = Schema.get_to_many_relation(
|
|
30
|
+
relation = Schema.get_to_many_relation(context.collection, args[:params]['relation_name'])
|
|
31
31
|
|
|
32
32
|
case relation.type
|
|
33
33
|
when 'OneToMany'
|
|
34
|
-
associate_one_to_many(relation, parent_primary_key_values, target_primary_key_values)
|
|
34
|
+
associate_one_to_many(relation, parent_primary_key_values, target_primary_key_values, context)
|
|
35
35
|
when 'ManyToMany'
|
|
36
|
-
associate_many_to_many(relation, parent_primary_key_values, target_primary_key_values)
|
|
36
|
+
associate_many_to_many(relation, parent_primary_key_values, target_primary_key_values, context)
|
|
37
37
|
when 'PolymorphicOneToMany'
|
|
38
|
-
associate_polymorphic_one_to_many(relation, parent_primary_key_values, target_primary_key_values)
|
|
38
|
+
associate_polymorphic_one_to_many(relation, parent_primary_key_values, target_primary_key_values, context)
|
|
39
39
|
end
|
|
40
40
|
|
|
41
41
|
{ content: nil, status: 204 }
|
|
@@ -43,52 +43,55 @@ module ForestAdminAgent
|
|
|
43
43
|
|
|
44
44
|
private
|
|
45
45
|
|
|
46
|
-
def associate_one_to_many(relation, parent_primary_key_values, target_primary_key_values)
|
|
47
|
-
id = Schema.primary_keys(
|
|
48
|
-
value = Collection.get_value(
|
|
46
|
+
def associate_one_to_many(relation, parent_primary_key_values, target_primary_key_values, context)
|
|
47
|
+
id = Schema.primary_keys(context.child_collection)[0]
|
|
48
|
+
value = Collection.get_value(context.child_collection, context.caller, target_primary_key_values, id)
|
|
49
49
|
filter = Filter.new(
|
|
50
50
|
condition_tree: ConditionTree::ConditionTreeFactory.intersect(
|
|
51
51
|
[
|
|
52
52
|
ConditionTree::Nodes::ConditionTreeLeaf.new(id, 'Equal', value),
|
|
53
|
-
|
|
53
|
+
context.permissions.get_scope(context.collection)
|
|
54
54
|
]
|
|
55
55
|
)
|
|
56
56
|
)
|
|
57
|
-
value = Collection.get_value(
|
|
57
|
+
value = Collection.get_value(context.collection, context.caller, parent_primary_key_values,
|
|
58
|
+
relation.origin_key_target)
|
|
58
59
|
|
|
59
|
-
|
|
60
|
+
context.child_collection.update(context.caller, filter, { relation.origin_key => value })
|
|
60
61
|
end
|
|
61
62
|
|
|
62
|
-
def associate_polymorphic_one_to_many(relation, parent_primary_key_values, target_primary_key_values)
|
|
63
|
-
id = Schema.primary_keys(
|
|
64
|
-
value = Collection.get_value(
|
|
63
|
+
def associate_polymorphic_one_to_many(relation, parent_primary_key_values, target_primary_key_values, context)
|
|
64
|
+
id = Schema.primary_keys(context.child_collection)[0]
|
|
65
|
+
value = Collection.get_value(context.child_collection, context.caller, target_primary_key_values, id)
|
|
65
66
|
filter = Filter.new(
|
|
66
67
|
condition_tree: ConditionTree::ConditionTreeFactory.intersect(
|
|
67
68
|
[
|
|
68
69
|
ConditionTree::Nodes::ConditionTreeLeaf.new(id, 'Equal', value),
|
|
69
|
-
|
|
70
|
+
context.permissions.get_scope(context.collection)
|
|
70
71
|
]
|
|
71
72
|
)
|
|
72
73
|
)
|
|
73
74
|
|
|
74
|
-
value = Collection.get_value(
|
|
75
|
+
value = Collection.get_value(context.collection, context.caller, parent_primary_key_values,
|
|
76
|
+
relation.origin_key_target)
|
|
75
77
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
+
context.child_collection.update(
|
|
79
|
+
context.caller,
|
|
78
80
|
filter,
|
|
79
|
-
{ relation.origin_key => value, relation.origin_type_field =>
|
|
81
|
+
{ relation.origin_key => value, relation.origin_type_field => context.collection.name.gsub('__', '::') }
|
|
80
82
|
)
|
|
81
83
|
end
|
|
82
84
|
|
|
83
|
-
def associate_many_to_many(relation, parent_primary_key_values, target_primary_key_values)
|
|
84
|
-
id = Schema.primary_keys(
|
|
85
|
-
foreign_value = Collection.get_value(
|
|
86
|
-
|
|
87
|
-
|
|
85
|
+
def associate_many_to_many(relation, parent_primary_key_values, target_primary_key_values, context)
|
|
86
|
+
id = Schema.primary_keys(context.child_collection)[0]
|
|
87
|
+
foreign_value = Collection.get_value(context.child_collection, context.caller, target_primary_key_values,
|
|
88
|
+
id)
|
|
89
|
+
id = Schema.primary_keys(context.collection)[0]
|
|
90
|
+
origin_value = Collection.get_value(context.collection, context.caller, parent_primary_key_values, id)
|
|
88
91
|
record = { relation.origin_key => origin_value, relation.foreign_key => foreign_value }
|
|
89
92
|
|
|
90
|
-
through_collection =
|
|
91
|
-
through_collection.create(
|
|
93
|
+
through_collection = context.datasource.get_collection(relation.through_collection)
|
|
94
|
+
through_collection.create(context.caller, record)
|
|
92
95
|
end
|
|
93
96
|
end
|
|
94
97
|
end
|
|
@@ -20,23 +20,23 @@ module ForestAdminAgent
|
|
|
20
20
|
end
|
|
21
21
|
|
|
22
22
|
def handle_request(args = {})
|
|
23
|
-
build(args)
|
|
24
|
-
|
|
23
|
+
context = build(args)
|
|
24
|
+
context.permissions.can?(:browse, context.collection)
|
|
25
25
|
|
|
26
|
-
if
|
|
27
|
-
filter = Filter.new(condition_tree:
|
|
28
|
-
primary_key_values = Utils::Id.unpack_id(
|
|
26
|
+
if context.child_collection.is_countable?
|
|
27
|
+
filter = Filter.new(condition_tree: context.permissions.get_scope(context.collection))
|
|
28
|
+
primary_key_values = Utils::Id.unpack_id(context.collection, args[:params]['id'], with_key: true)
|
|
29
29
|
result = Collection.aggregate_relation(
|
|
30
|
-
|
|
30
|
+
context.collection,
|
|
31
31
|
primary_key_values,
|
|
32
32
|
args[:params]['relation_name'],
|
|
33
|
-
|
|
33
|
+
context.caller,
|
|
34
34
|
filter,
|
|
35
35
|
Aggregation.new(operation: 'Count')
|
|
36
36
|
)
|
|
37
37
|
|
|
38
38
|
return {
|
|
39
|
-
name:
|
|
39
|
+
name: context.child_collection.name,
|
|
40
40
|
content: {
|
|
41
41
|
count: result.empty? ? 0 : result[0]['value']
|
|
42
42
|
}
|
|
@@ -44,7 +44,7 @@ module ForestAdminAgent
|
|
|
44
44
|
end
|
|
45
45
|
|
|
46
46
|
{
|
|
47
|
-
name:
|
|
47
|
+
name: context.child_collection.name,
|
|
48
48
|
content: {
|
|
49
49
|
count: 'deactivated'
|
|
50
50
|
}
|
|
@@ -20,22 +20,23 @@ module ForestAdminAgent
|
|
|
20
20
|
end
|
|
21
21
|
|
|
22
22
|
def handle_request(args = {})
|
|
23
|
-
build(args)
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
context = build(args)
|
|
24
|
+
context.permissions.can?(:browse, context.collection)
|
|
25
|
+
context.permissions.can?(:export, context.collection)
|
|
26
26
|
|
|
27
27
|
filter = ForestAdminDatasourceToolkit::Components::Query::Filter.new(
|
|
28
28
|
condition_tree: ConditionTreeFactory.intersect(
|
|
29
29
|
[
|
|
30
|
-
|
|
31
|
-
ForestAdminAgent::Utils::QueryStringParser.parse_condition_tree(
|
|
30
|
+
context.permissions.get_scope(context.collection),
|
|
31
|
+
ForestAdminAgent::Utils::QueryStringParser.parse_condition_tree(context.child_collection, args)
|
|
32
32
|
]
|
|
33
33
|
)
|
|
34
34
|
)
|
|
35
|
-
projection = ForestAdminAgent::Utils::QueryStringParser.parse_projection_with_pks(
|
|
35
|
+
projection = ForestAdminAgent::Utils::QueryStringParser.parse_projection_with_pks(context.child_collection,
|
|
36
|
+
args)
|
|
36
37
|
|
|
37
38
|
# Get the parent record primary keys
|
|
38
|
-
primary_key_values = Utils::Id.unpack_id(
|
|
39
|
+
primary_key_values = Utils::Id.unpack_id(context.collection, args[:params]['id'], with_key: true)
|
|
39
40
|
relation_name = args[:params]['relation_name']
|
|
40
41
|
|
|
41
42
|
# Generate timestamp for filename
|
|
@@ -47,10 +48,10 @@ module ForestAdminAgent
|
|
|
47
48
|
# Create a callable to fetch related records
|
|
48
49
|
list_records = lambda do |batch_filter|
|
|
49
50
|
ForestAdminDatasourceToolkit::Utils::Collection.list_relation(
|
|
50
|
-
|
|
51
|
+
context.collection,
|
|
51
52
|
primary_key_values,
|
|
52
53
|
relation_name,
|
|
53
|
-
|
|
54
|
+
context.caller,
|
|
54
55
|
batch_filter,
|
|
55
56
|
projection
|
|
56
57
|
)
|
|
@@ -20,28 +20,33 @@ module ForestAdminAgent
|
|
|
20
20
|
end
|
|
21
21
|
|
|
22
22
|
def handle_request(args = {})
|
|
23
|
-
build(args)
|
|
23
|
+
context = build(args)
|
|
24
24
|
|
|
25
|
-
parent_primary_key_values = Utils::Id.unpack_id(
|
|
25
|
+
parent_primary_key_values = Utils::Id.unpack_id(context.collection, args[:params]['id'], with_key: true)
|
|
26
26
|
is_delete_mode = !args.dig(:params, :delete).nil?
|
|
27
27
|
|
|
28
28
|
if is_delete_mode
|
|
29
|
-
|
|
29
|
+
context.permissions.can?(:delete, context.child_collection)
|
|
30
30
|
else
|
|
31
|
-
|
|
31
|
+
context.permissions.can?(:edit, context.collection)
|
|
32
32
|
end
|
|
33
33
|
|
|
34
|
-
filter = get_base_foreign_filter(args)
|
|
35
|
-
relation = Schema.get_to_many_relation(
|
|
34
|
+
filter = get_base_foreign_filter(args, context)
|
|
35
|
+
relation = Schema.get_to_many_relation(context.collection, args[:params]['relation_name'])
|
|
36
|
+
|
|
37
|
+
relation_name = args[:params]['relation_name']
|
|
38
|
+
options = {
|
|
39
|
+
relation: relation,
|
|
40
|
+
relation_name: relation_name,
|
|
41
|
+
parent_pk_values: parent_primary_key_values,
|
|
42
|
+
is_delete_mode: is_delete_mode,
|
|
43
|
+
filter: filter
|
|
44
|
+
}
|
|
36
45
|
|
|
37
46
|
if ['OneToMany', 'PolymorphicOneToMany'].include?(relation.type)
|
|
38
|
-
dissociate_or_delete_one_to_many(
|
|
39
|
-
relation, args[:params]['relation_name'], parent_primary_key_values, is_delete_mode, filter
|
|
40
|
-
)
|
|
47
|
+
dissociate_or_delete_one_to_many(options, context)
|
|
41
48
|
else
|
|
42
|
-
dissociate_or_delete_many_to_many(
|
|
43
|
-
relation, args[:params]['relation_name'], parent_primary_key_values, is_delete_mode, filter
|
|
44
|
-
)
|
|
49
|
+
dissociate_or_delete_many_to_many(options, context)
|
|
45
50
|
end
|
|
46
51
|
|
|
47
52
|
{ content: nil, status: 204 }
|
|
@@ -49,51 +54,69 @@ module ForestAdminAgent
|
|
|
49
54
|
|
|
50
55
|
private
|
|
51
56
|
|
|
52
|
-
def dissociate_or_delete_one_to_many(
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
57
|
+
def dissociate_or_delete_one_to_many(options, context)
|
|
58
|
+
foreign_filter = FilterFactory.make_foreign_filter(
|
|
59
|
+
context.collection,
|
|
60
|
+
options[:parent_pk_values],
|
|
61
|
+
options[:relation_name],
|
|
62
|
+
context.caller,
|
|
63
|
+
options[:filter]
|
|
64
|
+
)
|
|
56
65
|
|
|
57
|
-
if is_delete_mode
|
|
58
|
-
|
|
66
|
+
if options[:is_delete_mode]
|
|
67
|
+
context.child_collection.delete(context.caller, foreign_filter)
|
|
59
68
|
else
|
|
60
|
-
patch = if relation.type == 'PolymorphicOneToMany'
|
|
61
|
-
{ relation.origin_key => nil, relation.origin_type_field => nil }
|
|
69
|
+
patch = if options[:relation].type == 'PolymorphicOneToMany'
|
|
70
|
+
{ options[:relation].origin_key => nil, options[:relation].origin_type_field => nil }
|
|
62
71
|
else
|
|
63
|
-
{ relation.origin_key => nil }
|
|
72
|
+
{ options[:relation].origin_key => nil }
|
|
64
73
|
end
|
|
65
|
-
|
|
74
|
+
context.child_collection.update(context.caller, foreign_filter, patch)
|
|
66
75
|
end
|
|
67
76
|
end
|
|
68
77
|
|
|
69
|
-
def dissociate_or_delete_many_to_many(
|
|
70
|
-
|
|
71
|
-
through_collection = @datasource.get_collection(relation.through_collection)
|
|
78
|
+
def dissociate_or_delete_many_to_many(options, context)
|
|
79
|
+
through_collection = context.datasource.get_collection(options[:relation].through_collection)
|
|
72
80
|
|
|
73
|
-
if is_delete_mode
|
|
81
|
+
if options[:is_delete_mode]
|
|
74
82
|
# Generate filters _BEFORE_ deleting stuff, otherwise things break.
|
|
75
|
-
foreign_filter = FilterFactory.make_foreign_filter(
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
83
|
+
foreign_filter = FilterFactory.make_foreign_filter(
|
|
84
|
+
context.collection,
|
|
85
|
+
options[:parent_pk_values],
|
|
86
|
+
options[:relation_name],
|
|
87
|
+
context.caller,
|
|
88
|
+
options[:filter]
|
|
89
|
+
)
|
|
90
|
+
through_filter = FilterFactory.make_through_filter(
|
|
91
|
+
context.collection,
|
|
92
|
+
options[:parent_pk_values],
|
|
93
|
+
options[:relation_name],
|
|
94
|
+
context.caller,
|
|
95
|
+
options[:filter]
|
|
96
|
+
)
|
|
79
97
|
|
|
80
98
|
# Delete records from through collection
|
|
81
|
-
through_collection.delete(
|
|
99
|
+
through_collection.delete(context.caller, through_filter)
|
|
82
100
|
|
|
83
101
|
# Let the datasource crash when:
|
|
84
102
|
# - the records in the foreignCollection are linked to other records in the origin collection
|
|
85
103
|
# - the underlying database/api is not cascading deletes
|
|
86
|
-
|
|
104
|
+
context.child_collection.delete(context.caller, foreign_filter)
|
|
87
105
|
else
|
|
88
|
-
through_filter = FilterFactory.make_through_filter(
|
|
89
|
-
|
|
90
|
-
|
|
106
|
+
through_filter = FilterFactory.make_through_filter(
|
|
107
|
+
context.collection,
|
|
108
|
+
options[:parent_pk_values],
|
|
109
|
+
options[:relation_name],
|
|
110
|
+
context.caller,
|
|
111
|
+
options[:filter]
|
|
112
|
+
)
|
|
113
|
+
through_collection.delete(context.caller, through_filter)
|
|
91
114
|
end
|
|
92
115
|
end
|
|
93
116
|
|
|
94
|
-
def get_base_foreign_filter(args)
|
|
95
|
-
selection_ids = Utils::Id.parse_selection_ids(
|
|
96
|
-
selected_ids = ConditionTree::ConditionTreeFactory.match_ids(
|
|
117
|
+
def get_base_foreign_filter(args, context)
|
|
118
|
+
selection_ids = Utils::Id.parse_selection_ids(context.child_collection, args[:params])
|
|
119
|
+
selected_ids = ConditionTree::ConditionTreeFactory.match_ids(context.child_collection, selection_ids[:ids])
|
|
97
120
|
|
|
98
121
|
selected_ids = selected_ids.inverse if selection_ids[:are_excluded]
|
|
99
122
|
|
|
@@ -104,8 +127,8 @@ module ForestAdminAgent
|
|
|
104
127
|
Filter.new(
|
|
105
128
|
condition_tree: ConditionTree::ConditionTreeFactory.intersect(
|
|
106
129
|
[
|
|
107
|
-
|
|
108
|
-
Utils::QueryStringParser.parse_condition_tree(
|
|
130
|
+
context.permissions.get_scope(context.child_collection),
|
|
131
|
+
Utils::QueryStringParser.parse_condition_tree(context.child_collection, args),
|
|
109
132
|
selected_ids
|
|
110
133
|
]
|
|
111
134
|
)
|