forest_admin_datasource_toolkit 1.0.0.pre.beta.23 → 1.0.0.pre.beta.25

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 786b58c3bc21c7187d7a95025aae6229de6c04bac42549cede55f060be797d86
4
- data.tar.gz: 477ffdc3ed1eba5e5240e641741f9f7cbd311373e35594438ccc452584a75830
3
+ metadata.gz: 8139afcd60607522083451780f9a7b7d98c9bdac9630a5befd8c9a795b5d1faf
4
+ data.tar.gz: ae361fde5da4f144c85ade92cd843c8f035c9b063f2f9c25330eba9fd55e8928
5
5
  SHA512:
6
- metadata.gz: 46282da2e6d7221c853d234d382a3712a4fcb0e0340c06f93ba97f2a9ca26967a0adc0e153e6b904dfe39c4aaadcb31ffe0a3731ae6a097e3520120dbdaa0e16
7
- data.tar.gz: 52c33562da9679ea958fec285158dc6c12f5fb7ff9a3076f2724951bae500e99debb10925a562add7daeab4d749102494b46c6a33eaaaee22e54c5fb4c861984
6
+ metadata.gz: 485716a1fdd2f1bc67a8d6043503be51479b240d27158877a54edb10f06de3b1ea22a4e64688b046e648e7b9a0decd79703ec08f96f5150f659f1a5bec6f0a12
7
+ data.tar.gz: 3c0d856e370425024bc2b5854a1ce904a66bccf0e15426d6ede04fb1ef8a1c3c5889b9cddd3eb6b8fe2e2ef92c2f68cb8006f9ba7c3180008a7e98f7d4e483b6
@@ -1,6 +1,6 @@
1
1
  module ForestAdminDatasourceToolkit
2
2
  class Collection < Components::Contracts::CollectionContract
3
- attr_accessor :fields, :segments
3
+ attr_accessor :segments
4
4
 
5
5
  attr_reader :actions,
6
6
  :charts,
@@ -17,28 +17,37 @@ module ForestAdminDatasourceToolkit
17
17
  @datasource = datasource
18
18
  @name = name
19
19
  @native_driver = native_driver
20
- @fields = {}
21
- @schema = {}
22
- @fields = {}
20
+ @schema = {
21
+ fields: {},
22
+ countable: false
23
+ }
23
24
  @actions = {}
24
25
  @segments = {}
25
26
  @charts = {}
26
27
  @searchable = false
27
- @countable = true
28
+ @countable = false
29
+ end
30
+
31
+ def enable_count
32
+ @schema[:countable] = true
28
33
  end
29
34
 
30
35
  def is_countable?
31
- @countable
36
+ @schema[:countable]
32
37
  end
33
38
 
34
39
  def is_searchable?
35
40
  @searchable
36
41
  end
37
42
 
43
+ def fields
44
+ @schema[:fields]
45
+ end
46
+
38
47
  def add_field(name, field)
39
- raise Exceptions::ForestException, "Field #{name} already defined in collection" if @fields.key?(name)
48
+ raise Exceptions::ForestException, "Field #{name} already defined in collection" if @schema[:fields].key?(name)
40
49
 
41
- @fields[name] = field
50
+ @schema[:fields][name] = field
42
51
  end
43
52
 
44
53
  def add_fields(fields)
@@ -10,7 +10,7 @@ module ForestAdminDatasourceToolkit
10
10
  raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
11
11
  end
12
12
 
13
- def collection(name)
13
+ def get_collection(name)
14
14
  raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
15
15
  end
16
16
 
@@ -22,7 +22,7 @@ module ForestAdminDatasourceToolkit
22
22
  raise ForestException, 'Collection must have at least one primary key' if primary_key_names.empty?
23
23
 
24
24
  primary_key_names.each do |name|
25
- operators = collection.fields[name].filter_operators
25
+ operators = collection.schema[:fields][name].filter_operators
26
26
  unless operators.include?(Operators::EQUAL) || operators.include?(Operators::IN)
27
27
  raise ForestException, "Field '#{name}' must support operators: ['Equal', 'In']"
28
28
  end
@@ -54,15 +54,15 @@ module ForestAdminDatasourceToolkit
54
54
  def self.previous_interval(duration)
55
55
  interval(
56
56
  lambda { |now|
57
- duration == 'quarter' ? now.prev_quarter : (now - 1.send(duration)).send("beginning_of_#{duration}")
57
+ duration == 'quarter' ? now.prev_quarter : (now - 1.send(duration)).send(:"beginning_of_#{duration}")
58
58
  },
59
- ->(now) { now.send("beginning_of_#{duration}") }
59
+ ->(now) { now.send(:"beginning_of_#{duration}") }
60
60
  )
61
61
  end
62
62
 
63
63
  def self.previous_interval_to_date(duration)
64
64
  interval(
65
- ->(now) { now.send("beginning_of_#{duration}") },
65
+ ->(now) { now.send(:"beginning_of_#{duration}") },
66
66
  ->(now) { now }
67
67
  )
68
68
  end
@@ -61,7 +61,7 @@ module ForestAdminDatasourceToolkit
61
61
  if relation.is_a?(OneToManySchema)
62
62
  origin_tree = Nodes::ConditionTreeLeaf.new(relation.origin_key, Operators::EQUAL, origin_value)
63
63
  else
64
- through_collection = collection.datasource.collection(relation.through_collection)
64
+ through_collection = collection.datasource.get_collection(relation.through_collection)
65
65
  through_tree = ConditionTreeFactory.intersect([
66
66
  Nodes::ConditionTreeLeaf.new(relation.origin_key, Operators::EQUAL, origin_value),
67
67
  Nodes::ConditionTreeLeaf.new(relation.foreign_key, Operators::PRESENT)
@@ -86,8 +86,8 @@ module ForestAdminDatasourceToolkit
86
86
  unit = unit.downcase
87
87
  start = "beginning_of_#{unit}"
88
88
  end_ = "end_of_#{unit}"
89
- start_period = Time.now.in_time_zone(timezone).send("prev_#{unit}").send(start)
90
- end_period = Time.now.in_time_zone(timezone).send("prev_#{unit}").send(end_)
89
+ start_period = Time.now.in_time_zone(timezone).send(:"prev_#{unit}").send(start)
90
+ end_period = Time.now.in_time_zone(timezone).send(:"prev_#{unit}").send(end_)
91
91
 
92
92
  get_previous_condition_tree(field, start_period.to_datetime, end_period.to_datetime)
93
93
  end
@@ -104,13 +104,13 @@ module ForestAdminDatasourceToolkit
104
104
  end
105
105
 
106
106
  def self.make_through_filter(collection, id, relation_name, caller, base_foreign_filter)
107
- relation = collection.fields[relation_name]
107
+ relation = collection.schema[:fields][relation_name]
108
108
  origin_value = Utils::Collection.get_value(collection, caller, id, relation.origin_key_target)
109
109
  foreign_relation = Utils::Collection.get_through_target(collection, relation_name)
110
110
 
111
111
  # Optimization for many to many when there is not search/segment (saves one query)
112
112
  if foreign_relation && base_foreign_filter.nestable?
113
- foreign_key = collection.datasource.collection(relation.through_collection).fields[relation.foreign_key]
113
+ foreign_key = collection.datasource.get_collection(relation.through_collection).schema[:fields][relation.foreign_key]
114
114
  base_through_filter = base_foreign_filter.nest(foreign_relation)
115
115
  condition_tree = ConditionTreeFactory.intersect(
116
116
  [
@@ -129,7 +129,7 @@ module ForestAdminDatasourceToolkit
129
129
 
130
130
  # Otherwise we have no choice but to call the target collection so that search and segment
131
131
  # are correctly apply, and then match ids in the though collection.
132
- target = collection.datasource.collection(relation.foreign_collection)
132
+ target = collection.datasource.get_collection(relation.foreign_collection)
133
133
  records = target.list(
134
134
  caller,
135
135
  make_foreign_filter(collection, id, relation_name, caller, base_foreign_filter),
@@ -9,8 +9,8 @@ module ForestAdminDatasourceToolkit
9
9
  end
10
10
 
11
11
  relations.each do |relation, projection|
12
- schema = collection.fields[relation]
13
- association = collection.datasource.collection(schema.foreign_collection)
12
+ schema = collection.schema[:fields][relation]
13
+ association = collection.datasource.get_collection(schema.foreign_collection)
14
14
  projection_with_pks = projection.with_pks(association).nest(prefix: relation)
15
15
 
16
16
  projection_with_pks.each { |field| push(field) unless include?(field) }
@@ -4,14 +4,14 @@ module ForestAdminDatasourceToolkit
4
4
  class ProjectionFactory
5
5
  include ForestAdminDatasourceToolkit::Utils
6
6
  def self.all(collection)
7
- projection_fields = collection.fields.reduce([]) do |memo, path|
7
+ projection_fields = collection.schema[:fields].reduce([]) do |memo, path|
8
8
  column_name = path[0]
9
9
  schema = path[1]
10
10
  memo += [column_name] if schema.type == 'Column'
11
11
 
12
12
  if schema.type == 'OneToOne' || schema.type == 'ManyToOne'
13
- relation = collection.datasource.collection(schema.foreign_collection)
14
- relation_columns = relation.fields
13
+ relation = collection.datasource.get_collection(schema.foreign_collection)
14
+ relation_columns = relation.schema[:fields]
15
15
  .select { |_column_name, relation_column| relation_column.type == 'Column' }
16
16
  .keys
17
17
  .map { |relation_column_name| "#{column_name}:#{relation_column_name}" }
@@ -8,7 +8,7 @@ module ForestAdminDatasourceToolkit
8
8
  @collections = {}
9
9
  end
10
10
 
11
- def collection(name)
11
+ def get_collection(name)
12
12
  raise Exceptions::ForestException, "Collection #{name} not found." unless @collections.key? name
13
13
 
14
14
  @collections[name]
@@ -0,0 +1,109 @@
1
+ module ForestAdminDatasourceToolkit
2
+ module Decorators
3
+ class CollectionDecorator < Collection
4
+ attr_reader :datasource, :child_collection, :last_schema
5
+
6
+ def initialize(child_collection, datasource)
7
+ super
8
+ @child_collection = child_collection
9
+ @datasource = datasource
10
+
11
+ # When the child collection invalidates its schema, we also invalidate ours.
12
+ # This is done like this, and not in the markSchemaAsDirty method, because we don't have
13
+ # a reference to parent collections from children.
14
+ return unless child_collection.is_a?(CollectionDecorator)
15
+
16
+ child_collection.define_singleton_method(:mark_schema_as_dirty) do
17
+ # Call the original method (the child)
18
+ original_child_mark_schema_as_dirty.call(child_collection)
19
+
20
+ # Invalidate our schema (the parent)
21
+ mark_schema_as_dirty
22
+ end
23
+ end
24
+
25
+ def native_driver
26
+ # TODO
27
+ end
28
+
29
+ def schema
30
+ unless @last_schema
31
+ sub_schema = @child_collection.schema
32
+ @last_schema = refine_schema(sub_schema)
33
+ end
34
+
35
+ @last_schema
36
+ end
37
+
38
+ def name
39
+ @child_collection.name
40
+ end
41
+
42
+ def execute(caller, name, data, filter = nil)
43
+ refined_filter = refine_filter(caller, filter)
44
+
45
+ @child_collection.execute(caller, name, data, refined_filter)
46
+ end
47
+
48
+ def get_form(caller, name, data = nil, filter = nil, metas = nil)
49
+ refined_filter = refine_filter(caller, filter)
50
+
51
+ @child_collection.get_form(caller, name, data, refined_filter, metas)
52
+ end
53
+
54
+ def create(caller, data)
55
+ @child_collection.create(caller, data)
56
+ end
57
+
58
+ def list(caller, filter = nil, projection = nil)
59
+ refined_filter = refine_filter(caller, filter)
60
+
61
+ @child_collection.list(caller, refined_filter, projection)
62
+ end
63
+
64
+ def update(caller, filter, patch)
65
+ refined_filter = refine_filter(caller, filter)
66
+
67
+ @child_collection.update(caller, refined_filter, patch)
68
+ end
69
+
70
+ def delete(caller, filter)
71
+ refined_filter = refine_filter(caller, filter)
72
+
73
+ @child_collection.delete(caller, refined_filter)
74
+ end
75
+
76
+ def aggregate(caller, filter, aggregation, limit = nil)
77
+ refined_filter = refine_filter(caller, filter)
78
+
79
+ @child_collection.aggregate(caller, refined_filter, aggregation, limit)
80
+ end
81
+
82
+ def render_chart(caller, name, record_id)
83
+ @child_collection.render_chart(caller, name, record_id)
84
+ end
85
+
86
+ protected
87
+
88
+ def mark_schema_as_dirty
89
+ @last_schema = nil
90
+ end
91
+
92
+ def refine_filter(_caller, filter = nil)
93
+ filter
94
+ end
95
+
96
+ def refine_schema(sub_schema)
97
+ sub_schema
98
+ end
99
+
100
+ private
101
+
102
+ def push_customization(customization)
103
+ @stack.queue_customization(customization)
104
+
105
+ self
106
+ end
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,28 @@
1
+ module ForestAdminDatasourceToolkit
2
+ module Decorators
3
+ class DatasourceDecorator
4
+ def initialize(child_datasource, collection_decorator_class)
5
+ @child_datasource = child_datasource
6
+ @collection_decorator_class = collection_decorator_class
7
+ @decorators = {}
8
+ end
9
+
10
+ def collections
11
+ @child_datasource.collections.transform_values { |c| get_collection(c.name) }
12
+ end
13
+
14
+ def get_collection(name)
15
+ collection = @child_datasource.get_collection(name)
16
+ unless @decorators.key?(collection.name)
17
+ @decorators[collection.name] = @collection_decorator_class.new(collection, self)
18
+ end
19
+
20
+ @decorators[collection.name]
21
+ end
22
+
23
+ def render_chart(caller, name)
24
+ @child_datasource.render_chart(caller, name)
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,21 @@
1
+ module ForestAdminDatasourceToolkit
2
+ module Decorators
3
+ module Schema
4
+ class SchemaCollectionDecorator < CollectionDecorator
5
+ def initialize(child_collection, datasource)
6
+ super
7
+ @schema_override = {}
8
+ end
9
+
10
+ def override_schema(value)
11
+ @schema_override.merge!(value)
12
+ mark_schema_as_dirty
13
+ end
14
+
15
+ def refine_schema(sub_schema)
16
+ sub_schema.merge(@schema_override)
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -7,10 +7,10 @@ module ForestAdminDatasourceToolkit
7
7
  include ForestAdminDatasourceToolkit::Exceptions
8
8
 
9
9
  def self.get_inverse_relation(collection, relation_name)
10
- relation_field = collection.fields[relation_name]
11
- foreign_collection = collection.datasource.collection(relation_field.foreign_collection)
10
+ relation_field = collection.schema[:fields][relation_name]
11
+ foreign_collection = collection.datasource.get_collection(relation_field.foreign_collection)
12
12
 
13
- inverse = foreign_collection.fields.select do |_name, field|
13
+ inverse = foreign_collection.schema[:fields].select do |_name, field|
14
14
  field.is_a?(RelationSchema) &&
15
15
  field.foreign_collection == collection.name &&
16
16
  (
@@ -50,7 +50,7 @@ module ForestAdminDatasourceToolkit
50
50
  end
51
51
 
52
52
  def self.get_field_schema(collection, field_name)
53
- fields = collection.fields
53
+ fields = collection.schema[:fields]
54
54
  unless field_name.include?(':')
55
55
  raise ForestException, "Column not found #{collection.name}.#{field_name}" unless fields.key?(field_name)
56
56
 
@@ -67,7 +67,7 @@ module ForestAdminDatasourceToolkit
67
67
  end
68
68
 
69
69
  get_field_schema(
70
- collection.datasource.collection(relation_schema.foreign_collection), field_name.split(':')[1..].join(':')
70
+ collection.datasource.get_collection(relation_schema.foreign_collection), field_name.split(':')[1..].join(':')
71
71
  )
72
72
  end
73
73
 
@@ -90,11 +90,11 @@ module ForestAdminDatasourceToolkit
90
90
  end
91
91
 
92
92
  def self.get_through_target(collection, relation_name)
93
- relation = collection.fields[relation_name]
93
+ relation = collection.schema[:fields][relation_name]
94
94
  raise ForestException, 'Relation must be many to many' unless relation.is_a?(ManyToManySchema)
95
95
 
96
- through_collection = collection.datasource.collection(relation.through_collection)
97
- through_collection.fields.select do |field_name, field|
96
+ through_collection = collection.datasource.get_collection(relation.through_collection)
97
+ through_collection.schema[:fields].select do |field_name, field|
98
98
  if field.is_a?(ManyToOneSchema) &&
99
99
  field.foreign_collection == relation.foreign_collection &&
100
100
  field.foreign_key == relation.foreign_key &&
@@ -107,11 +107,11 @@ module ForestAdminDatasourceToolkit
107
107
  end
108
108
 
109
109
  def self.get_through_origin(collection, relation_name)
110
- relation = collection.fields[relation_name]
110
+ relation = collection.schema[:fields][relation_name]
111
111
  raise ForestException, 'Relation must be many to many' unless relation.is_a?(ManyToManySchema)
112
112
 
113
- through_collection = collection.datasource.collection(relation.through_collection)
114
- through_collection.fields.select do |field_name, field|
113
+ through_collection = collection.datasource.get_collection(relation.through_collection)
114
+ through_collection.schema[:fields].select do |field_name, field|
115
115
  if field.is_a?(ManyToOneSchema) &&
116
116
  field.foreign_collection == collection.name &&
117
117
  field.foreign_key == relation.origin_key &&
@@ -124,14 +124,14 @@ module ForestAdminDatasourceToolkit
124
124
  end
125
125
 
126
126
  def self.list_relation(collection, id, relation_name, caller, foreign_filter, projection)
127
- relation = collection.fields[relation_name]
128
- foreign_collection = collection.datasource.collection(relation.foreign_collection)
127
+ relation = collection.schema[:fields][relation_name]
128
+ foreign_collection = collection.datasource.get_collection(relation.foreign_collection)
129
129
 
130
130
  if relation.is_a?(ManyToManySchema) && foreign_filter.nestable?
131
131
  foreign_relation = get_through_target(collection, relation_name)
132
132
 
133
133
  if foreign_relation
134
- through_collection = collection.datasource.collection(relation.through_collection)
134
+ through_collection = collection.datasource.get_collection(relation.through_collection)
135
135
  records = through_collection.list(
136
136
  caller,
137
137
  FilterFactory.make_through_filter(collection, id, relation_name, caller, foreign_filter),
@@ -150,13 +150,13 @@ module ForestAdminDatasourceToolkit
150
150
  end
151
151
 
152
152
  def self.aggregate_relation(collection, id, relation_name, caller, foreign_filter, aggregation, limit = nil)
153
- relation = collection.fields[relation_name]
154
- foreign_collection = collection.datasource.collection(relation.foreign_collection)
153
+ relation = collection.schema[:fields][relation_name]
154
+ foreign_collection = collection.datasource.get_collection(relation.foreign_collection)
155
155
 
156
156
  if relation.is_a?(ManyToManySchema) && foreign_filter.nestable?
157
157
  foreign_relation = get_through_target(collection, relation_name)
158
158
  if foreign_relation
159
- through_collection = collection.datasource.collection(relation.through_collection)
159
+ through_collection = collection.datasource.get_collection(relation.through_collection)
160
160
 
161
161
  return through_collection.aggregate(
162
162
  caller,
@@ -2,33 +2,33 @@ module ForestAdminDatasourceToolkit
2
2
  module Utils
3
3
  class Schema
4
4
  def self.foreign_key?(collection, name)
5
- field = collection.fields[name]
5
+ field = collection.schema[:fields][name]
6
6
 
7
7
  field.type == 'Column' &&
8
- collection.fields.any? do |_key, relation|
8
+ collection.schema[:fields].any? do |_key, relation|
9
9
  relation.type == 'ManyToOne' && relation.foreign_key == name
10
10
  end
11
11
  end
12
12
 
13
13
  def self.primary_key?(collection, name)
14
- field = collection.fields[name]
14
+ field = collection.schema[:fields][name]
15
15
 
16
16
  field.type == 'Column' && field.is_primary_key
17
17
  end
18
18
 
19
19
  def self.primary_keys(collection)
20
- collection.fields.keys.select do |field_name|
21
- field = collection.fields[field_name]
20
+ collection.schema[:fields].keys.select do |field_name|
21
+ field = collection.schema[:fields][field_name]
22
22
  field.type == 'Column' && field.is_primary_key
23
23
  end
24
24
  end
25
25
 
26
26
  def self.get_to_many_relation(collection, relation_name)
27
- unless collection.fields.key?(relation_name)
27
+ unless collection.schema[:fields].key?(relation_name)
28
28
  raise Exceptions::ForestException, "Relation #{relation_name} not found"
29
29
  end
30
30
 
31
- relation = collection.fields[relation_name]
31
+ relation = collection.schema[:fields][relation_name]
32
32
 
33
33
  if relation.type != 'OneToMany' && relation.type != 'ManyToMany'
34
34
  raise Exceptions::ForestException,
@@ -1,3 +1,3 @@
1
1
  module ForestAdminDatasourceToolkit
2
- VERSION = "1.0.0-beta.23"
2
+ VERSION = "1.0.0-beta.25"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: forest_admin_datasource_toolkit
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0.pre.beta.23
4
+ version: 1.0.0.pre.beta.25
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: 2023-12-13 00:00:00.000000000 Z
12
+ date: 2023-12-20 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activesupport
@@ -82,6 +82,9 @@ files:
82
82
  - lib/forest_admin_datasource_toolkit/components/query/projection.rb
83
83
  - lib/forest_admin_datasource_toolkit/components/query/projection_factory.rb
84
84
  - lib/forest_admin_datasource_toolkit/datasource.rb
85
+ - lib/forest_admin_datasource_toolkit/decorators/collection_decorator.rb
86
+ - lib/forest_admin_datasource_toolkit/decorators/datasource_decorator.rb
87
+ - lib/forest_admin_datasource_toolkit/decorators/schema/schema_collection_decorator.rb
85
88
  - lib/forest_admin_datasource_toolkit/exceptions/forest_exception.rb
86
89
  - lib/forest_admin_datasource_toolkit/schema/column_schema.rb
87
90
  - lib/forest_admin_datasource_toolkit/schema/primitive_type.rb