forest_admin_datasource_customizer 1.0.0.pre.beta.86 → 1.0.0.pre.beta.88
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_datasource_customizer/collection_customizer.rb +7 -0
- data/lib/forest_admin_datasource_customizer/decorators/decorators_stack.rb +3 -1
- data/lib/forest_admin_datasource_customizer/decorators/lazy_join/lazy_join_collection_decorator.rb +129 -0
- data/lib/forest_admin_datasource_customizer/decorators/search/search_collection_decorator.rb +29 -10
- data/lib/forest_admin_datasource_customizer/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8c7120cf1ef2b8e044dfe6e93646c75be81f8b8e9fd28b4e1c157f03db1d705a
|
4
|
+
data.tar.gz: 99c70deb71c742d033bae93fcebcd028dfbece9d4eae804e51d62b40da16ecbd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3cdc27aba86bf2925c5db8cb0be2b105702352fbbb9a7a1b1fd91193189d250f819a097397eae438ec92d6dceba388ab622d441f84a353c99d90acdb88a6dfd3
|
7
|
+
data.tar.gz: 4e747e188ed52a3403bbea8a74fb01e086e32ac283436178535bce196c52e6e682a2fad589dc167f2d54f2d80b7d3d324281547a91afbac2f907432ae74f1d4f
|
@@ -33,6 +33,13 @@ module ForestAdminDatasourceCustomizer
|
|
33
33
|
push_customization { @stack.search.get_collection(@name).replace_search(definition) }
|
34
34
|
end
|
35
35
|
|
36
|
+
# Disable the search bar
|
37
|
+
# Example:
|
38
|
+
# collection.disable_search
|
39
|
+
def disable_search
|
40
|
+
push_customization { @stack.search.get_collection(@name).disable_search }
|
41
|
+
end
|
42
|
+
|
36
43
|
def add_field(name, definition)
|
37
44
|
push_customization do
|
38
45
|
collection_before_relations = @stack.early_computed.get_collection(@name)
|
@@ -5,7 +5,7 @@ module ForestAdminDatasourceCustomizer
|
|
5
5
|
|
6
6
|
attr_reader :datasource, :schema, :search, :early_computed, :late_computed, :action, :relation, :late_op_emulate,
|
7
7
|
:early_op_emulate, :validation, :sort, :rename_field, :publication, :write, :chart, :hook, :segment,
|
8
|
-
:binary, :override
|
8
|
+
:binary, :override, :lazy_join
|
9
9
|
|
10
10
|
def initialize(datasource)
|
11
11
|
@customizations = []
|
@@ -19,6 +19,8 @@ module ForestAdminDatasourceCustomizer
|
|
19
19
|
last = @early_op_emulate = DatasourceDecorator.new(last, OperatorsEmulate::OperatorsEmulateCollectionDecorator)
|
20
20
|
last = DatasourceDecorator.new(last, OperatorsEquivalence::OperatorsEquivalenceCollectionDecorator)
|
21
21
|
last = @relation = DatasourceDecorator.new(last, Relation::RelationCollectionDecorator)
|
22
|
+
# lazy join is just before relation, to avoid relations to do useless stuff
|
23
|
+
last = @lazy_join = DatasourceDecorator.new(last, LazyJoin::LazyJoinCollectionDecorator)
|
22
24
|
last = @late_computed = DatasourceDecorator.new(last, Computed::ComputeCollectionDecorator)
|
23
25
|
last = @late_op_emulate = DatasourceDecorator.new(last, OperatorsEmulate::OperatorsEmulateCollectionDecorator)
|
24
26
|
last = DatasourceDecorator.new(last, OperatorsEquivalence::OperatorsEquivalenceCollectionDecorator)
|
data/lib/forest_admin_datasource_customizer/decorators/lazy_join/lazy_join_collection_decorator.rb
ADDED
@@ -0,0 +1,129 @@
|
|
1
|
+
module ForestAdminDatasourceCustomizer
|
2
|
+
module Decorators
|
3
|
+
module LazyJoin
|
4
|
+
class LazyJoinCollectionDecorator < ForestAdminDatasourceToolkit::Decorators::CollectionDecorator
|
5
|
+
include ForestAdminDatasourceToolkit::Decorators
|
6
|
+
include ForestAdminDatasourceToolkit::Components::Query
|
7
|
+
include ForestAdminDatasourceToolkit::Components::Query::ConditionTree
|
8
|
+
|
9
|
+
def list(caller, filter, projection)
|
10
|
+
simplified_projection = get_projection_without_useless_joins(projection)
|
11
|
+
refined_filter = refine_filter(caller, filter)
|
12
|
+
records = child_collection.list(caller, refined_filter, simplified_projection)
|
13
|
+
|
14
|
+
apply_joins_on_records(projection, simplified_projection, records)
|
15
|
+
end
|
16
|
+
|
17
|
+
def aggregate(caller, filter, aggregation, limit = nil)
|
18
|
+
refined_filter = refine_filter(caller, filter)
|
19
|
+
replaced = {}
|
20
|
+
refined_aggregation = aggregation.replace_fields do |field_name|
|
21
|
+
if useless_join?(field_name.split(':')[0], aggregation.projection)
|
22
|
+
new_field_name = get_foreign_key_for_projection(field_name)
|
23
|
+
replaced[new_field_name] = field_name
|
24
|
+
|
25
|
+
new_field_name
|
26
|
+
else
|
27
|
+
field_name
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
results = child_collection.aggregate(caller, refined_filter, refined_aggregation, limit)
|
32
|
+
|
33
|
+
apply_joins_on_aggregate_result(aggregation, refined_aggregation, results, replaced)
|
34
|
+
end
|
35
|
+
|
36
|
+
def refine_filter(_caller, filter = nil)
|
37
|
+
filter&.override(
|
38
|
+
condition_tree: filter.condition_tree&.replace_leafs do |leaf|
|
39
|
+
if useless_join?(leaf.field.split(':')[0], filter.condition_tree.projection)
|
40
|
+
leaf.override(field: get_foreign_key_for_projection(leaf.field))
|
41
|
+
else
|
42
|
+
leaf
|
43
|
+
end
|
44
|
+
end
|
45
|
+
)
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def get_foreign_key_for_projection(field_name)
|
51
|
+
relation_name = field_name.split(':')[0]
|
52
|
+
relation_schema = schema[:fields][relation_name]
|
53
|
+
|
54
|
+
relation_schema.foreign_key
|
55
|
+
end
|
56
|
+
|
57
|
+
def useless_join?(relation_name, projection)
|
58
|
+
relation_schema = schema[:fields][relation_name]
|
59
|
+
sub_projection = projection.relations[relation_name]
|
60
|
+
|
61
|
+
relation_schema.type == 'ManyToOne' &&
|
62
|
+
sub_projection.size == 1 &&
|
63
|
+
sub_projection[0] == relation_schema.foreign_key_target
|
64
|
+
end
|
65
|
+
|
66
|
+
def get_projection_without_useless_joins(projection)
|
67
|
+
new_projection = Projection.new(projection)
|
68
|
+
|
69
|
+
projection.relations.each do |relation_name, relation_projection|
|
70
|
+
next unless useless_join?(relation_name, projection)
|
71
|
+
|
72
|
+
# remove foreign key target from projection
|
73
|
+
new_projection.delete("#{relation_name}:#{relation_projection[0]}")
|
74
|
+
|
75
|
+
# add foreign keys to projection
|
76
|
+
fk_field = get_foreign_key_for_projection("#{relation_name}:#{relation_projection[0]}")
|
77
|
+
new_projection << fk_field
|
78
|
+
end
|
79
|
+
|
80
|
+
new_projection
|
81
|
+
end
|
82
|
+
|
83
|
+
def apply_joins_on_records(initial_projection, requested_projection, records)
|
84
|
+
return records if initial_projection == requested_projection
|
85
|
+
|
86
|
+
projections_to_add = Projection.new(initial_projection.reject do |field|
|
87
|
+
requested_projection.include?(field)
|
88
|
+
end)
|
89
|
+
projections_to_rm = Projection.new(requested_projection.reject { |field| initial_projection.include?(field) })
|
90
|
+
|
91
|
+
records.each do |record|
|
92
|
+
# add to record relation:id
|
93
|
+
projections_to_add.relations.each do |relation_name, relation_projection|
|
94
|
+
relation_schema = schema[:fields][relation_name]
|
95
|
+
|
96
|
+
if relation_schema && relation_schema.type == 'ManyToOne'
|
97
|
+
fk_value = record[get_foreign_key_for_projection("#{relation_name}:#{relation_projection[0]}")]
|
98
|
+
record[relation_name] = fk_value.nil? ? nil : { relation_projection[0] => fk_value }
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# remove foreign keys
|
103
|
+
projections_to_rm.each { |field| record.delete(field) }
|
104
|
+
end
|
105
|
+
|
106
|
+
records
|
107
|
+
end
|
108
|
+
|
109
|
+
def apply_joins_on_aggregate_result(initial_aggregation, requested_aggregation, results, fields_to_replace)
|
110
|
+
return result if initial_aggregation == requested_aggregation
|
111
|
+
|
112
|
+
results.each do |result|
|
113
|
+
group = {}
|
114
|
+
result['group'].each do |field, value|
|
115
|
+
if fields_to_replace.include?(field)
|
116
|
+
group[fields_to_replace[field]] = value
|
117
|
+
else
|
118
|
+
group[field] = value
|
119
|
+
end
|
120
|
+
end
|
121
|
+
result['group'] = group
|
122
|
+
end
|
123
|
+
|
124
|
+
results
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
data/lib/forest_admin_datasource_customizer/decorators/search/search_collection_decorator.rb
CHANGED
@@ -8,6 +8,11 @@ module ForestAdminDatasourceCustomizer
|
|
8
8
|
def initialize(child_collection, datasource)
|
9
9
|
super
|
10
10
|
@replacer = nil
|
11
|
+
@disabled_search = get_fields(false).empty?
|
12
|
+
end
|
13
|
+
|
14
|
+
def disable_search
|
15
|
+
@disabled_search = true
|
11
16
|
end
|
12
17
|
|
13
18
|
def replace_search(replacer)
|
@@ -15,7 +20,7 @@ module ForestAdminDatasourceCustomizer
|
|
15
20
|
end
|
16
21
|
|
17
22
|
def refine_schema(sub_schema)
|
18
|
-
sub_schema.merge({ searchable:
|
23
|
+
sub_schema.merge({ searchable: !@disabled_search })
|
19
24
|
end
|
20
25
|
|
21
26
|
def refine_filter(caller, filter)
|
@@ -48,7 +53,7 @@ module ForestAdminDatasourceCustomizer
|
|
48
53
|
private
|
49
54
|
|
50
55
|
def default_replacer(search, extended)
|
51
|
-
searchable_fields = get_fields(
|
56
|
+
searchable_fields = get_fields(extended)
|
52
57
|
|
53
58
|
conditions = searchable_fields.map do |field, schema|
|
54
59
|
build_condition(field, schema, search)
|
@@ -64,11 +69,11 @@ module ForestAdminDatasourceCustomizer
|
|
64
69
|
is_number = number?(search_string)
|
65
70
|
is_uuid = uuid?(search_string)
|
66
71
|
|
67
|
-
if column_type == PrimitiveType::NUMBER && is_number
|
72
|
+
if column_type == PrimitiveType::NUMBER && is_number
|
68
73
|
return Nodes::ConditionTreeLeaf.new(field, Operators::EQUAL, search_string.to_f)
|
69
74
|
end
|
70
75
|
|
71
|
-
if column_type == PrimitiveType::ENUM
|
76
|
+
if column_type == PrimitiveType::ENUM
|
72
77
|
search_value = lenient_find(enum_values, search_string)
|
73
78
|
|
74
79
|
return Nodes::ConditionTreeLeaf.new(field, Operators::EQUAL, search_value) if search_value
|
@@ -92,17 +97,17 @@ module ForestAdminDatasourceCustomizer
|
|
92
97
|
return Nodes::ConditionTreeLeaf.new(field, operator, search_string) if operator
|
93
98
|
end
|
94
99
|
|
95
|
-
if column_type == PrimitiveType::UUID && is_uuid
|
100
|
+
if column_type == PrimitiveType::UUID && is_uuid
|
96
101
|
return Nodes::ConditionTreeLeaf.new(field, Operators::EQUAL, search_string)
|
97
102
|
end
|
98
103
|
|
99
104
|
nil
|
100
105
|
end
|
101
106
|
|
102
|
-
def get_fields(
|
107
|
+
def get_fields(extended)
|
103
108
|
fields = []
|
104
|
-
|
105
|
-
fields.push([name, field]) if field.type == 'Column'
|
109
|
+
@child_collection.schema[:fields].each do |name, field|
|
110
|
+
fields.push([name, field]) if field.type == 'Column' && searchable_field?(field)
|
106
111
|
|
107
112
|
if field.type == 'PolymorphicManyToOne' && extended
|
108
113
|
ForestAdminAgent::Facades::Container.logger.log(
|
@@ -116,16 +121,30 @@ module ForestAdminDatasourceCustomizer
|
|
116
121
|
next unless extended &&
|
117
122
|
(field.type == 'ManyToOne' || field.type == 'OneToOne' || field.type == 'PolymorphicOneToOne')
|
118
123
|
|
119
|
-
related =
|
124
|
+
related = @child_collection.datasource.get_collection(field.foreign_collection)
|
120
125
|
|
121
126
|
related.schema[:fields].each do |sub_name, sub_field|
|
122
|
-
fields.push(["#{name}:#{sub_name}", sub_field]) if sub_field.type == 'Column'
|
127
|
+
fields.push(["#{name}:#{sub_name}", sub_field]) if sub_field.type == 'Column' &&
|
128
|
+
searchable_field?(sub_field)
|
123
129
|
end
|
124
130
|
end
|
125
131
|
|
126
132
|
fields
|
127
133
|
end
|
128
134
|
|
135
|
+
def searchable_field?(field)
|
136
|
+
operators = field.filter_operators
|
137
|
+
|
138
|
+
if field.column_type == PrimitiveType::STRING
|
139
|
+
return operators&.include?(Operators::EQUAL) ||
|
140
|
+
operators&.include?(Operators::CONTAINS) ||
|
141
|
+
operators&.include?(Operators::I_CONTAINS)
|
142
|
+
end
|
143
|
+
|
144
|
+
[PrimitiveType::UUID, PrimitiveType::ENUM, PrimitiveType::NUMBER].include?(field.column_type) &&
|
145
|
+
operators&.include?(Operators::EQUAL)
|
146
|
+
end
|
147
|
+
|
129
148
|
def lenient_find(haystack, needle)
|
130
149
|
haystack&.find { |v| v == needle.strip } || haystack&.find { |v| v.downcase == needle.downcase.strip }
|
131
150
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: forest_admin_datasource_customizer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.0.pre.beta.
|
4
|
+
version: 1.0.0.pre.beta.88
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Matthieu
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: exe
|
11
11
|
cert_chain: []
|
12
|
-
date: 2025-01-
|
12
|
+
date: 2025-01-08 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activesupport
|
@@ -116,6 +116,7 @@ files:
|
|
116
116
|
- lib/forest_admin_datasource_customizer/decorators/hook/context/hook_context.rb
|
117
117
|
- lib/forest_admin_datasource_customizer/decorators/hook/hook_collection_decorator.rb
|
118
118
|
- lib/forest_admin_datasource_customizer/decorators/hook/hooks.rb
|
119
|
+
- lib/forest_admin_datasource_customizer/decorators/lazy_join/lazy_join_collection_decorator.rb
|
119
120
|
- lib/forest_admin_datasource_customizer/decorators/operators_emulate/operators_emulate_collection_decorator.rb
|
120
121
|
- lib/forest_admin_datasource_customizer/decorators/operators_equivalence/operators_equivalence_collection_decorator.rb
|
121
122
|
- lib/forest_admin_datasource_customizer/decorators/override/context/create_override_customization_context.rb
|