forest_admin_datasource_customizer 1.0.0.pre.beta.21 → 1.0.0.pre.beta.58
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/forest_admin_datasource_customizer.gemspec +3 -2
- data/lib/forest_admin_datasource_customizer/collection_customizer.rb +292 -5
- data/lib/forest_admin_datasource_customizer/context/agent_customization_context.rb +18 -0
- data/lib/forest_admin_datasource_customizer/context/collection_customization_context.rb +15 -0
- data/lib/forest_admin_datasource_customizer/context/relaxed_wrappers/relaxed_collection.rb +50 -0
- data/lib/forest_admin_datasource_customizer/context/relaxed_wrappers/relaxed_data_source.rb +18 -0
- data/lib/forest_admin_datasource_customizer/datasource_customizer.rb +43 -13
- data/lib/forest_admin_datasource_customizer/decorators/action/action_collection_decorator.rb +137 -0
- data/lib/forest_admin_datasource_customizer/decorators/action/base_action.rb +67 -0
- data/lib/forest_admin_datasource_customizer/decorators/action/context/action_context.rb +56 -0
- data/lib/forest_admin_datasource_customizer/decorators/action/context/action_context_single.rb +26 -0
- data/lib/forest_admin_datasource_customizer/decorators/action/dynamic_field.rb +50 -0
- data/lib/forest_admin_datasource_customizer/decorators/action/result_builder.rb +68 -0
- data/lib/forest_admin_datasource_customizer/decorators/action/types/action_scope.rb +15 -0
- data/lib/forest_admin_datasource_customizer/decorators/action/types/field_type.rb +35 -0
- data/lib/forest_admin_datasource_customizer/decorators/action/widget_field.rb +357 -0
- data/lib/forest_admin_datasource_customizer/decorators/binary/binary_collection_decorator.rb +215 -0
- data/lib/forest_admin_datasource_customizer/decorators/binary/binary_helper.rb +17 -0
- data/lib/forest_admin_datasource_customizer/decorators/chart/chart_collection_decorator.rb +41 -0
- data/lib/forest_admin_datasource_customizer/decorators/chart/chart_context.rb +33 -0
- data/lib/forest_admin_datasource_customizer/decorators/chart/chart_datasource_decorator.rb +46 -0
- data/lib/forest_admin_datasource_customizer/decorators/chart/result_builder.rb +148 -0
- data/lib/forest_admin_datasource_customizer/decorators/computed/compute_collection_decorator.rb +115 -0
- data/lib/forest_admin_datasource_customizer/decorators/computed/computed_definition.rb +21 -0
- data/lib/forest_admin_datasource_customizer/decorators/computed/utils/computed_field.rb +74 -0
- data/lib/forest_admin_datasource_customizer/decorators/computed/utils/flattener.rb +49 -0
- data/lib/forest_admin_datasource_customizer/decorators/decorators_stack.rb +33 -4
- data/lib/forest_admin_datasource_customizer/decorators/hook/context/after/hook_after_aggregate_context.rb +18 -0
- data/lib/forest_admin_datasource_customizer/decorators/hook/context/after/hook_after_create_context.rb +18 -0
- data/lib/forest_admin_datasource_customizer/decorators/hook/context/after/hook_after_delete_context.rb +12 -0
- data/lib/forest_admin_datasource_customizer/decorators/hook/context/after/hook_after_list_context.rb +18 -0
- data/lib/forest_admin_datasource_customizer/decorators/hook/context/after/hook_after_update_context.rb +12 -0
- data/lib/forest_admin_datasource_customizer/decorators/hook/context/before/hook_before_aggregate_context.rb +20 -0
- data/lib/forest_admin_datasource_customizer/decorators/hook/context/before/hook_before_create_context.rb +18 -0
- data/lib/forest_admin_datasource_customizer/decorators/hook/context/before/hook_before_delete_context.rb +18 -0
- data/lib/forest_admin_datasource_customizer/decorators/hook/context/before/hook_before_list_context.rb +19 -0
- data/lib/forest_admin_datasource_customizer/decorators/hook/context/before/hook_before_update_context.rb +19 -0
- data/lib/forest_admin_datasource_customizer/decorators/hook/context/hook_context.rb +22 -0
- data/lib/forest_admin_datasource_customizer/decorators/hook/hook_collection_decorator.rb +95 -0
- data/lib/forest_admin_datasource_customizer/decorators/hook/hooks.rb +26 -0
- data/lib/forest_admin_datasource_customizer/decorators/operators_emulate/operators_emulate_collection_decorator.rb +118 -0
- data/lib/forest_admin_datasource_customizer/decorators/operators_equivalence/operators_equivalence_collection_decorator.rb +50 -0
- data/lib/forest_admin_datasource_customizer/decorators/override/context/create_override_customization_context.rb +16 -0
- data/lib/forest_admin_datasource_customizer/decorators/override/context/delete_override_customization_context.rb +16 -0
- data/lib/forest_admin_datasource_customizer/decorators/override/context/update_override_customization_context.rb +17 -0
- data/lib/forest_admin_datasource_customizer/decorators/override/override_collection_decorator.rb +49 -0
- data/lib/forest_admin_datasource_customizer/decorators/publication/publication_collection_decorator.rb +95 -0
- data/lib/forest_admin_datasource_customizer/decorators/publication/publication_datasource_decorator.rb +57 -0
- data/lib/forest_admin_datasource_customizer/decorators/relation/relation_collection_decorator.rb +268 -0
- data/lib/forest_admin_datasource_customizer/decorators/rename_collection/rename_collection_datasource_decorator.rb +70 -0
- data/lib/forest_admin_datasource_customizer/decorators/rename_collection/rename_collection_decorator.rb +37 -0
- data/lib/forest_admin_datasource_customizer/decorators/rename_field/rename_field_collection_decorator.rb +190 -0
- data/lib/forest_admin_datasource_customizer/decorators/schema/schema_collection_decorator.rb +21 -0
- data/lib/forest_admin_datasource_customizer/decorators/search/search_collection_decorator.rb +135 -0
- data/lib/forest_admin_datasource_customizer/decorators/segment/segment_collection_decorator.rb +60 -0
- data/lib/forest_admin_datasource_customizer/decorators/sort/sort_collection_decorator.rb +127 -0
- data/lib/forest_admin_datasource_customizer/decorators/validation/validation_collection_decorator.rb +82 -0
- data/lib/forest_admin_datasource_customizer/decorators/write/create_relations/create_relations_collection_decorator.rb +75 -0
- data/lib/forest_admin_datasource_customizer/decorators/write/update_relations/update_relations_collection_decorator.rb +96 -0
- data/lib/forest_admin_datasource_customizer/decorators/write/write_datasource_decorator.rb +14 -0
- data/lib/forest_admin_datasource_customizer/decorators/write/write_replace/write_customization_context.rb +18 -0
- data/lib/forest_admin_datasource_customizer/decorators/write/write_replace/write_replace_collection_decorator.rb +125 -0
- data/lib/forest_admin_datasource_customizer/plugins/add_external_relation.rb +27 -0
- data/lib/forest_admin_datasource_customizer/plugins/import_field.rb +74 -0
- data/lib/forest_admin_datasource_customizer/version.rb +1 -1
- metadata +84 -5
- data/README.md +0 -31
@@ -0,0 +1,16 @@
|
|
1
|
+
module ForestAdminDatasourceCustomizer
|
2
|
+
module Decorators
|
3
|
+
module Override
|
4
|
+
module Context
|
5
|
+
class CreateOverrideCustomizationContext < ForestAdminDatasourceCustomizer::Context::CollectionCustomizationContext
|
6
|
+
attr_reader :data
|
7
|
+
|
8
|
+
def initialize(collection, caller, data)
|
9
|
+
super(collection, caller)
|
10
|
+
@data = data
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module ForestAdminDatasourceCustomizer
|
2
|
+
module Decorators
|
3
|
+
module Override
|
4
|
+
module Context
|
5
|
+
class DeleteOverrideCustomizationContext < ForestAdminDatasourceCustomizer::Context::CollectionCustomizationContext
|
6
|
+
attr_reader :filter
|
7
|
+
|
8
|
+
def initialize(collection, caller, filter)
|
9
|
+
super(collection, caller)
|
10
|
+
@filter = filter
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module ForestAdminDatasourceCustomizer
|
2
|
+
module Decorators
|
3
|
+
module Override
|
4
|
+
module Context
|
5
|
+
class UpdateOverrideCustomizationContext < ForestAdminDatasourceCustomizer::Context::CollectionCustomizationContext
|
6
|
+
attr_reader :filter, :patch
|
7
|
+
|
8
|
+
def initialize(collection, caller, filter, patch)
|
9
|
+
super(collection, caller)
|
10
|
+
@filter = filter
|
11
|
+
@patch = patch
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/lib/forest_admin_datasource_customizer/decorators/override/override_collection_decorator.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
module ForestAdminDatasourceCustomizer
|
2
|
+
module Decorators
|
3
|
+
module Override
|
4
|
+
class OverrideCollectionDecorator < ForestAdminDatasourceToolkit::Decorators::CollectionDecorator
|
5
|
+
include Context
|
6
|
+
attr_reader :create_handler, :update_handler, :delete_handler
|
7
|
+
|
8
|
+
def create(caller, data)
|
9
|
+
if @create_handler
|
10
|
+
context = CreateOverrideCustomizationContext.new(@child_collection, caller, data)
|
11
|
+
return @create_handler.call(context)
|
12
|
+
end
|
13
|
+
|
14
|
+
super
|
15
|
+
end
|
16
|
+
|
17
|
+
def add_create_handler(handler)
|
18
|
+
@create_handler = handler
|
19
|
+
end
|
20
|
+
|
21
|
+
def update(caller, filter, patch)
|
22
|
+
if @update_handler
|
23
|
+
context = UpdateOverrideCustomizationContext.new(@child_collection, caller, filter, patch)
|
24
|
+
return @update_handler.call(context)
|
25
|
+
end
|
26
|
+
|
27
|
+
super
|
28
|
+
end
|
29
|
+
|
30
|
+
def add_update_handler(handler)
|
31
|
+
@update_handler = handler
|
32
|
+
end
|
33
|
+
|
34
|
+
def delete(caller, filter)
|
35
|
+
if @delete_handler
|
36
|
+
context = DeleteOverrideCustomizationContext.new(@child_collection, caller, filter)
|
37
|
+
return @delete_handler.call(context)
|
38
|
+
end
|
39
|
+
|
40
|
+
super
|
41
|
+
end
|
42
|
+
|
43
|
+
def add_delete_handler(handler)
|
44
|
+
@delete_handler = handler
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
module ForestAdminDatasourceCustomizer
|
2
|
+
module Decorators
|
3
|
+
module Publication
|
4
|
+
class PublicationCollectionDecorator < ForestAdminDatasourceToolkit::Decorators::CollectionDecorator
|
5
|
+
include ForestAdminDatasourceToolkit::Exceptions
|
6
|
+
attr_reader :blacklist
|
7
|
+
|
8
|
+
def initialize(child_collection, datasource)
|
9
|
+
super
|
10
|
+
@blacklist = []
|
11
|
+
end
|
12
|
+
|
13
|
+
def change_field_visibility(name, visible)
|
14
|
+
field = child_collection.schema[:fields][name]
|
15
|
+
raise ForestException, "No such field '#{name}'" unless field
|
16
|
+
raise ForestException, 'Cannot hide primary key' if ForestAdminDatasourceToolkit::Utils::Schema.primary_key?(
|
17
|
+
child_collection, name
|
18
|
+
)
|
19
|
+
|
20
|
+
if visible
|
21
|
+
@blacklist.delete(name)
|
22
|
+
else
|
23
|
+
@blacklist << name
|
24
|
+
end
|
25
|
+
|
26
|
+
mark_schema_as_dirty
|
27
|
+
end
|
28
|
+
|
29
|
+
def create(caller, data)
|
30
|
+
record = {}
|
31
|
+
child_collection.create(caller, data).each do |key, value|
|
32
|
+
record[key] = value unless @blacklist.include?(key)
|
33
|
+
end
|
34
|
+
|
35
|
+
record
|
36
|
+
end
|
37
|
+
|
38
|
+
def refine_schema(child_schema)
|
39
|
+
fields = {}
|
40
|
+
|
41
|
+
child_schema[:fields].each do |name, field|
|
42
|
+
fields[name] = field if published?(name)
|
43
|
+
end
|
44
|
+
|
45
|
+
child_schema[:fields] = fields
|
46
|
+
|
47
|
+
child_schema
|
48
|
+
end
|
49
|
+
|
50
|
+
def published?(name)
|
51
|
+
# Explicitly hidden
|
52
|
+
return false if @blacklist.include?(name)
|
53
|
+
|
54
|
+
# Implicitly hidden
|
55
|
+
field = child_collection.schema[:fields][name]
|
56
|
+
|
57
|
+
if field.type == 'ManyToOne'
|
58
|
+
return (
|
59
|
+
datasource.published?(field.foreign_collection) &&
|
60
|
+
published?(field.foreign_key) &&
|
61
|
+
datasource.get_collection(field.foreign_collection).published?(field.foreign_key_target)
|
62
|
+
)
|
63
|
+
end
|
64
|
+
|
65
|
+
if field.type == 'OneToOne' || field.type == 'OneToMany'
|
66
|
+
return (
|
67
|
+
datasource.published?(field.foreign_collection) &&
|
68
|
+
datasource.get_collection(field.foreign_collection).published?(field.origin_key) &&
|
69
|
+
published?(field.origin_key_target)
|
70
|
+
)
|
71
|
+
end
|
72
|
+
|
73
|
+
if field.type == 'ManyToMany'
|
74
|
+
return (
|
75
|
+
datasource.published?(field.through_collection) &&
|
76
|
+
datasource.published?(field.foreign_collection) &&
|
77
|
+
datasource.get_collection(field.through_collection).published?(field.foreign_key) &&
|
78
|
+
datasource.get_collection(field.through_collection).published?(field.origin_key) &&
|
79
|
+
published?(field.origin_key_target) &&
|
80
|
+
datasource.get_collection(field.foreign_collection).published?(field.foreign_key_target)
|
81
|
+
)
|
82
|
+
end
|
83
|
+
|
84
|
+
true
|
85
|
+
end
|
86
|
+
|
87
|
+
# rubocop:disable Lint/UselessMethodDefinition
|
88
|
+
def mark_schema_as_dirty
|
89
|
+
super
|
90
|
+
end
|
91
|
+
# rubocop:enable Lint/UselessMethodDefinition
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module ForestAdminDatasourceCustomizer
|
2
|
+
module Decorators
|
3
|
+
module Publication
|
4
|
+
class PublicationDatasourceDecorator < ForestAdminDatasourceToolkit::Decorators::DatasourceDecorator
|
5
|
+
include ForestAdminDatasourceToolkit::Exceptions
|
6
|
+
attr_reader :blacklist
|
7
|
+
|
8
|
+
def initialize(child_datasource)
|
9
|
+
super(child_datasource, PublicationCollectionDecorator)
|
10
|
+
@blacklist = []
|
11
|
+
end
|
12
|
+
|
13
|
+
def collections
|
14
|
+
@child_datasource.collections.except(*@blacklist).to_h do |name, _collection|
|
15
|
+
[name, get_collection(name)]
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def get_collection(name)
|
20
|
+
raise ForestException, "Collection '#{name}' was removed." if @blacklist.include?(name)
|
21
|
+
|
22
|
+
super
|
23
|
+
end
|
24
|
+
|
25
|
+
def keep_collections_matching(include = [], exclude = [])
|
26
|
+
validate_collection_names(include.to_a + exclude.to_a)
|
27
|
+
|
28
|
+
# List collection we're keeping from the white/black list.
|
29
|
+
@child_datasource.collections.each_key do |collection|
|
30
|
+
remove_collection(collection) if (include && !include.include?(collection)) || exclude&.include?(collection)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def remove_collection(collection_name)
|
35
|
+
validate_collection_names([collection_name])
|
36
|
+
|
37
|
+
# Delete the collection
|
38
|
+
@blacklist << collection_name
|
39
|
+
|
40
|
+
# Tell all collections that their schema is dirty: if we removed a collection, all
|
41
|
+
# relations to this collection are now invalid and should be unpublished.
|
42
|
+
collections.each_value(&:mark_schema_as_dirty)
|
43
|
+
end
|
44
|
+
|
45
|
+
def published?(collection_name)
|
46
|
+
!@blacklist.include?(collection_name)
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def validate_collection_names(names)
|
52
|
+
names.each { |name| get_collection(name) }
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
data/lib/forest_admin_datasource_customizer/decorators/relation/relation_collection_decorator.rb
ADDED
@@ -0,0 +1,268 @@
|
|
1
|
+
module ForestAdminDatasourceCustomizer
|
2
|
+
module Decorators
|
3
|
+
module Relation
|
4
|
+
class RelationCollectionDecorator < ForestAdminDatasourceToolkit::Decorators::CollectionDecorator
|
5
|
+
include ForestAdminDatasourceToolkit::Exceptions
|
6
|
+
include ForestAdminDatasourceToolkit::Utils
|
7
|
+
include ForestAdminDatasourceToolkit::Components::Query
|
8
|
+
include ForestAdminDatasourceToolkit::Components::Query::ConditionTree
|
9
|
+
include ForestAdminDatasourceToolkit::Components::Query::ConditionTree::Nodes
|
10
|
+
include ForestAdminDatasourceToolkit::Schema
|
11
|
+
|
12
|
+
attr_reader :relations
|
13
|
+
|
14
|
+
def initialize(child_collection, datasource)
|
15
|
+
super
|
16
|
+
@relations = {}
|
17
|
+
end
|
18
|
+
|
19
|
+
def add_relation(name, partial_joint)
|
20
|
+
relation = relation_with_optional_fields(partial_joint)
|
21
|
+
check_foreign_keys(relation)
|
22
|
+
check_origin_keys(relation)
|
23
|
+
|
24
|
+
@relations[name] = relation
|
25
|
+
mark_schema_as_dirty
|
26
|
+
end
|
27
|
+
|
28
|
+
def list(caller, filter, projection)
|
29
|
+
new_filter = refine_filter(caller, filter)
|
30
|
+
new_projection = projection.replace { |field| rewrite_field(field) }
|
31
|
+
records = child_collection.list(caller, new_filter, new_projection)
|
32
|
+
return records if new_projection.equals(projection)
|
33
|
+
|
34
|
+
records = re_project_in_place(caller, records, projection)
|
35
|
+
|
36
|
+
projection.apply(records)
|
37
|
+
end
|
38
|
+
|
39
|
+
def aggregate(caller, filter, aggregation, limit = nil)
|
40
|
+
new_filter = refine_filter(caller, filter)
|
41
|
+
|
42
|
+
# No emulated relations are used in the aggregation
|
43
|
+
if aggregation.projection.relations.keys.all? { |prefix| !@relations.key?(prefix) }
|
44
|
+
return child_collection.aggregate(caller, new_filter, aggregation, limit)
|
45
|
+
end
|
46
|
+
|
47
|
+
# Fallback to full emulation.
|
48
|
+
aggregation.apply(list(caller, filter, aggregation.projection), caller.timezone, limit)
|
49
|
+
end
|
50
|
+
|
51
|
+
protected
|
52
|
+
|
53
|
+
def refine_schema(child_schema)
|
54
|
+
@relations.each do |name, relation|
|
55
|
+
child_schema[:fields][name] = relation
|
56
|
+
end
|
57
|
+
|
58
|
+
child_schema
|
59
|
+
end
|
60
|
+
|
61
|
+
def refine_filter(caller, filter)
|
62
|
+
filter.override({
|
63
|
+
condition_tree: filter.condition_tree&.replace_leafs do |leaf|
|
64
|
+
rewrite_leaf(caller, leaf)
|
65
|
+
end,
|
66
|
+
sort: filter.sort&.replace_clauses do |clause|
|
67
|
+
rewrite_field(clause[:field]).map do |field|
|
68
|
+
clause.merge(field: field)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
})
|
72
|
+
end
|
73
|
+
|
74
|
+
def relation_with_optional_fields(partial_joint)
|
75
|
+
relation = partial_joint.dup
|
76
|
+
target = datasource.get_collection(partial_joint[:foreign_collection])
|
77
|
+
|
78
|
+
case relation[:type]
|
79
|
+
when 'ManyToOne'
|
80
|
+
relation = Relations::ManyToOneSchema.new(
|
81
|
+
foreign_key: relation[:foreign_key],
|
82
|
+
foreign_key_target: if relation[:foreign_key_target].nil?
|
83
|
+
ForestAdminDatasourceToolkit::Utils::Schema.primary_keys(target).first
|
84
|
+
else
|
85
|
+
relation[:foreign_key_target]
|
86
|
+
end,
|
87
|
+
foreign_collection: relation[:foreign_collection]
|
88
|
+
)
|
89
|
+
when 'OneToOne'
|
90
|
+
relation = Relations::OneToOneSchema.new(
|
91
|
+
origin_key: relation[:origin_key],
|
92
|
+
origin_key_target: if relation[:origin_key_target].nil?
|
93
|
+
ForestAdminDatasourceToolkit::Utils::Schema.primary_keys(self).first
|
94
|
+
else
|
95
|
+
relation[:origin_key_target]
|
96
|
+
end,
|
97
|
+
foreign_collection: relation[:foreign_collection]
|
98
|
+
)
|
99
|
+
when 'OneToMany'
|
100
|
+
relation = Relations::OneToManySchema.new(
|
101
|
+
origin_key: relation[:origin_key],
|
102
|
+
origin_key_target: if relation[:origin_key_target].nil?
|
103
|
+
ForestAdminDatasourceToolkit::Utils::Schema.primary_keys(self).first
|
104
|
+
else
|
105
|
+
relation[:origin_key_target]
|
106
|
+
end,
|
107
|
+
foreign_collection: relation[:foreign_collection]
|
108
|
+
)
|
109
|
+
when 'ManyToMany'
|
110
|
+
relation = Relations::ManyToManySchema.new(
|
111
|
+
origin_key: relation[:origin_key],
|
112
|
+
origin_key_target: if relation[:origin_key_target].nil?
|
113
|
+
ForestAdminDatasourceToolkit::Utils::Schema.primary_keys(self).first
|
114
|
+
else
|
115
|
+
relation[:origin_key_target]
|
116
|
+
end,
|
117
|
+
foreign_key: relation[:foreign_key],
|
118
|
+
foreign_key_target: if relation[:foreign_key_target].nil?
|
119
|
+
ForestAdminDatasourceToolkit::Utils::Schema.primary_keys(target).first
|
120
|
+
else
|
121
|
+
relation[:foreign_key_target]
|
122
|
+
end,
|
123
|
+
foreign_collection: relation[:foreign_collection],
|
124
|
+
through_collection: relation[:through_collection]
|
125
|
+
)
|
126
|
+
end
|
127
|
+
|
128
|
+
relation
|
129
|
+
end
|
130
|
+
|
131
|
+
def check_foreign_keys(relation)
|
132
|
+
return unless relation.type == 'ManyToOne' || relation.type == 'ManyToMany'
|
133
|
+
|
134
|
+
check_keys(
|
135
|
+
relation.type == 'ManyToMany' ? datasource.get_collection(relation.through_collection) : self,
|
136
|
+
datasource.get_collection(relation.foreign_collection),
|
137
|
+
relation.foreign_key,
|
138
|
+
relation.foreign_key_target
|
139
|
+
)
|
140
|
+
end
|
141
|
+
|
142
|
+
def check_origin_keys(relation)
|
143
|
+
return unless relation.type == 'OneToMany' || relation.type == 'OneToOne' || relation.type == 'ManyToMany'
|
144
|
+
|
145
|
+
check_keys(
|
146
|
+
relation.type == 'ManyToMany' ? datasource.get_collection(relation.through_collection) : datasource.get_collection(relation.foreign_collection),
|
147
|
+
self,
|
148
|
+
relation.origin_key,
|
149
|
+
relation.origin_key_target
|
150
|
+
)
|
151
|
+
end
|
152
|
+
|
153
|
+
def check_keys(owner, target_owner, key_name, target_name)
|
154
|
+
check_column(owner, key_name)
|
155
|
+
check_column(target_owner, target_name)
|
156
|
+
|
157
|
+
key = owner.schema[:fields][key_name]
|
158
|
+
target = target_owner.schema[:fields][target_name]
|
159
|
+
|
160
|
+
return unless key.column_type != target.column_type
|
161
|
+
|
162
|
+
raise ForestException,
|
163
|
+
"Types from '#{owner.name}.#{key_name}' and '#{target_owner.name}.#{target_name}' do not match."
|
164
|
+
end
|
165
|
+
|
166
|
+
def check_column(owner, name)
|
167
|
+
column = owner.schema[:fields][name]
|
168
|
+
|
169
|
+
raise ForestException, "Column not found: '#{owner.name}.#{name}'" if !column || column.type != 'Column'
|
170
|
+
|
171
|
+
return if column.filter_operators.include?(Operators::IN)
|
172
|
+
|
173
|
+
raise ForestException, "Column does not support the In operator: '#{owner.name}.#{name}'"
|
174
|
+
end
|
175
|
+
|
176
|
+
def rewrite_field(field)
|
177
|
+
prefix = field.split(':').first
|
178
|
+
field_schema = schema[:fields][prefix]
|
179
|
+
|
180
|
+
return [field] if field_schema.type == 'Column'
|
181
|
+
|
182
|
+
relation = datasource.get_collection(field_schema.foreign_collection)
|
183
|
+
result = []
|
184
|
+
|
185
|
+
if !@relations.key?(prefix)
|
186
|
+
result = relation.rewrite_field(field[prefix.length + 1..]).map { |sub_field| "#{prefix}:#{sub_field}" }
|
187
|
+
elsif field_schema.is_a? Relations::ManyToOneSchema
|
188
|
+
result = [field_schema.foreign_key]
|
189
|
+
elsif field_schema.is_a?(Relations::OneToOneSchema) ||
|
190
|
+
field_schema.is_a?(Relations::OneToManySchema) ||
|
191
|
+
field_schema.is_a?(Relations::ManyToManySchema)
|
192
|
+
result = [field_schema.origin_key_target]
|
193
|
+
end
|
194
|
+
|
195
|
+
result
|
196
|
+
end
|
197
|
+
|
198
|
+
def rewrite_leaf(caller, leaf)
|
199
|
+
prefix = leaf.field.split(':').first
|
200
|
+
field_schema = schema[:fields][prefix]
|
201
|
+
return leaf if field_schema.type == 'Column'
|
202
|
+
|
203
|
+
relation = datasource.get_collection(field_schema.foreign_collection)
|
204
|
+
result = leaf
|
205
|
+
|
206
|
+
if !@relations.key?(prefix)
|
207
|
+
result = relation.rewrite_leaf(caller, leaf.unnest).nest(prefix)
|
208
|
+
elsif field_schema.type == 'ManyToOne'
|
209
|
+
records = relation.list(
|
210
|
+
caller,
|
211
|
+
Filter.new(condition_tree: leaf.unnest),
|
212
|
+
Projection.new([field_schema.foreign_key_target])
|
213
|
+
)
|
214
|
+
|
215
|
+
result = ConditionTreeLeaf.new(field_schema.foreign_key, 'In', records.map do |record|
|
216
|
+
record[field_schema.foreign_key_target]
|
217
|
+
end.uniq)
|
218
|
+
elsif field_schema.type == 'OneToOne'
|
219
|
+
records = relation.list(
|
220
|
+
caller,
|
221
|
+
Filter.new(condition_tree: leaf.unnest),
|
222
|
+
Projection.new([field_schema.origin_key])
|
223
|
+
)
|
224
|
+
|
225
|
+
result = ConditionTreeLeaf.new(field_schema.origin_key_target, 'In', records.map do |record|
|
226
|
+
record[field_schema.origin_key]
|
227
|
+
end.uniq)
|
228
|
+
end
|
229
|
+
|
230
|
+
result
|
231
|
+
end
|
232
|
+
|
233
|
+
def re_project_in_place(caller, records, projection)
|
234
|
+
projection.relations.each do |prefix, sub_projection|
|
235
|
+
re_project_relation_in_place(caller, records, prefix, sub_projection)
|
236
|
+
end
|
237
|
+
|
238
|
+
records
|
239
|
+
end
|
240
|
+
|
241
|
+
def re_project_relation_in_place(caller, records, name, projection)
|
242
|
+
field_schema = schema[:fields][name]
|
243
|
+
association = datasource.get_collection(field_schema.foreign_collection)
|
244
|
+
|
245
|
+
if !@relations[name]
|
246
|
+
association.re_project_in_place(caller, records.map { |r| r[name] }.filter { |fk| !fk.nil? }, projection)
|
247
|
+
elsif field_schema.type == 'ManyToOne'
|
248
|
+
ids = records.map { |record| record[field_schema.foreign_key] }.filter { |fk| !fk.nil? }.uniq
|
249
|
+
sub_filter = Filter.new(condition_tree: ConditionTreeLeaf.new(field_schema.foreign_key_target, 'In', ids))
|
250
|
+
sub_records = association.list(caller, sub_filter, projection.union([field_schema.foreign_key_target]))
|
251
|
+
|
252
|
+
records.each do |record|
|
253
|
+
record[name] = sub_records.find { |sr| sr[field_schema.foreign_key_target] == record[field_schema.foreign_key] }
|
254
|
+
end
|
255
|
+
elsif field_schema.type == 'OneToOne' || field_schema.type == 'OneToMany'
|
256
|
+
ids = records.map { |record| record[field_schema.origin_key_target] }.filter { |okt| !okt.nil? }.uniq
|
257
|
+
sub_filter = Filter.new(condition_tree: ConditionTreeLeaf.new(field_schema.origin_key, 'In', ids))
|
258
|
+
sub_records = association.list(caller, sub_filter, projection.union([field_schema.origin_key]))
|
259
|
+
|
260
|
+
records.each do |record|
|
261
|
+
record[name] = sub_records.find { |sr| sr[field_schema.origin_key] == record[field_schema.origin_key_target] }
|
262
|
+
end
|
263
|
+
end
|
264
|
+
end
|
265
|
+
end
|
266
|
+
end
|
267
|
+
end
|
268
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module ForestAdminDatasourceCustomizer
|
2
|
+
module Decorators
|
3
|
+
module RenameCollection
|
4
|
+
class RenameCollectionDatasourceDecorator < ForestAdminDatasourceToolkit::Decorators::DatasourceDecorator
|
5
|
+
include ForestAdminDatasourceToolkit
|
6
|
+
include ForestAdminDatasourceToolkit::Decorators
|
7
|
+
include ForestAdminDatasourceToolkit::Components::Query::ConditionTree
|
8
|
+
|
9
|
+
def initialize(child_datasource)
|
10
|
+
@from_child_name = {}
|
11
|
+
@to_child_name = {}
|
12
|
+
super(child_datasource, RenameCollectionDecorator)
|
13
|
+
end
|
14
|
+
|
15
|
+
def collections
|
16
|
+
@child_datasource.collections.to_h do |name, _collection|
|
17
|
+
[name, method(:get_collection).super_method.call(name)]
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def get_collection(name)
|
22
|
+
# Collection has been renamed, user is using the new name
|
23
|
+
return super(@to_child_name[name]) if @to_child_name.key?(name)
|
24
|
+
|
25
|
+
# Collection has been renamed, user is using the old name
|
26
|
+
if @from_child_name.key?(name)
|
27
|
+
raise Exceptions::ForestException, "Collection '#{name}' has been renamed to '#{@from_child_name[name]}'"
|
28
|
+
end
|
29
|
+
|
30
|
+
# Collection has not been renamed
|
31
|
+
super
|
32
|
+
end
|
33
|
+
|
34
|
+
def rename_collections(renames = [])
|
35
|
+
renames.each do |current_name, new_name|
|
36
|
+
rename_collection(current_name, new_name)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def rename_collection(current_name, new_name)
|
41
|
+
# Check collection exists
|
42
|
+
get_collection(current_name)
|
43
|
+
|
44
|
+
return unless current_name != new_name
|
45
|
+
|
46
|
+
# Check new name is not already used
|
47
|
+
if collections.any? { |name, _collection| name == new_name }
|
48
|
+
raise Exceptions::ForestException,
|
49
|
+
"The given new collection name '#{new_name}' is already defined"
|
50
|
+
end
|
51
|
+
|
52
|
+
# Check we don't rename a collection twice
|
53
|
+
if @to_child_name[current_name]
|
54
|
+
raise Exceptions::ForestException,
|
55
|
+
"Cannot rename a collection twice: #{@to_child_name[current_name]}->#{current_name}->#{new_name}"
|
56
|
+
end
|
57
|
+
|
58
|
+
@from_child_name[current_name] = new_name
|
59
|
+
@to_child_name[new_name] = current_name
|
60
|
+
|
61
|
+
collections.each_value(&:mark_schema_as_dirty)
|
62
|
+
end
|
63
|
+
|
64
|
+
def get_collection_name(child_name)
|
65
|
+
@from_child_name[child_name] || child_name
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module ForestAdminDatasourceCustomizer
|
2
|
+
module Decorators
|
3
|
+
module RenameCollection
|
4
|
+
class RenameCollectionDecorator < ForestAdminDatasourceToolkit::Decorators::CollectionDecorator
|
5
|
+
include ForestAdminDatasourceToolkit::Decorators
|
6
|
+
include ForestAdminDatasourceToolkit::Components::Query::ConditionTree
|
7
|
+
|
8
|
+
def name
|
9
|
+
datasource.get_collection_name(super)
|
10
|
+
end
|
11
|
+
|
12
|
+
def refine_schema(sub_schema)
|
13
|
+
fields = {}
|
14
|
+
|
15
|
+
sub_schema[:fields].each do |name, old_schema|
|
16
|
+
if old_schema.type != 'Column'
|
17
|
+
old_schema.foreign_collection = datasource.get_collection_name(old_schema.foreign_collection)
|
18
|
+
if old_schema.type == 'ManyToMany'
|
19
|
+
old_schema.through_collection = datasource.get_collection_name(old_schema.through_collection)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
fields[name] = old_schema
|
24
|
+
end
|
25
|
+
|
26
|
+
sub_schema
|
27
|
+
end
|
28
|
+
|
29
|
+
# rubocop:disable Lint/UselessMethodDefinition
|
30
|
+
def mark_schema_as_dirty
|
31
|
+
super
|
32
|
+
end
|
33
|
+
# rubocop:enable Lint/UselessMethodDefinition
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|