forest_admin_datasource_toolkit 1.0.0.pre.beta.21
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.rspec +3 -0
- data/README.md +31 -0
- data/Rakefile +12 -0
- data/forest_admin_datasource_toolkit.gemspec +37 -0
- data/lib/forest_admin_datasource_toolkit/collection.rb +50 -0
- data/lib/forest_admin_datasource_toolkit/components/caller.rb +31 -0
- data/lib/forest_admin_datasource_toolkit/components/contracts/collection_contract.rb +51 -0
- data/lib/forest_admin_datasource_toolkit/components/contracts/datasource_contract.rb +27 -0
- data/lib/forest_admin_datasource_toolkit/components/query/aggregation.rb +23 -0
- data/lib/forest_admin_datasource_toolkit/components/query/condition_tree/condition_tree_equivalent.rb +66 -0
- data/lib/forest_admin_datasource_toolkit/components/query/condition_tree/condition_tree_factory.rb +123 -0
- data/lib/forest_admin_datasource_toolkit/components/query/condition_tree/nodes/condition_tree.rb +70 -0
- data/lib/forest_admin_datasource_toolkit/components/query/condition_tree/nodes/condition_tree_branch.rb +68 -0
- data/lib/forest_admin_datasource_toolkit/components/query/condition_tree/nodes/condition_tree_leaf.rb +144 -0
- data/lib/forest_admin_datasource_toolkit/components/query/condition_tree/operators.rb +100 -0
- data/lib/forest_admin_datasource_toolkit/components/query/condition_tree/transforms/comparisons.rb +123 -0
- data/lib/forest_admin_datasource_toolkit/components/query/condition_tree/transforms/pattern.rb +48 -0
- data/lib/forest_admin_datasource_toolkit/components/query/condition_tree/transforms/times.rb +112 -0
- data/lib/forest_admin_datasource_toolkit/components/query/filter.rb +45 -0
- data/lib/forest_admin_datasource_toolkit/components/query/filter_factory.rb +158 -0
- data/lib/forest_admin_datasource_toolkit/components/query/page.rb +14 -0
- data/lib/forest_admin_datasource_toolkit/components/query/projection.rb +42 -0
- data/lib/forest_admin_datasource_toolkit/components/query/projection_factory.rb +30 -0
- data/lib/forest_admin_datasource_toolkit/datasource.rb +29 -0
- data/lib/forest_admin_datasource_toolkit/exceptions/forest_exception.rb +10 -0
- data/lib/forest_admin_datasource_toolkit/schema/column_schema.rb +34 -0
- data/lib/forest_admin_datasource_toolkit/schema/primitive_type.rb +31 -0
- data/lib/forest_admin_datasource_toolkit/schema/relation_schema.rb +13 -0
- data/lib/forest_admin_datasource_toolkit/schema/relations/many_to_many_schema.rb +26 -0
- data/lib/forest_admin_datasource_toolkit/schema/relations/many_to_one_schema.rb +16 -0
- data/lib/forest_admin_datasource_toolkit/schema/relations/one_to_many_schema.rb +16 -0
- data/lib/forest_admin_datasource_toolkit/schema/relations/one_to_one_schema.rb +16 -0
- data/lib/forest_admin_datasource_toolkit/utils/collection.rb +162 -0
- data/lib/forest_admin_datasource_toolkit/utils/record.rb +20 -0
- data/lib/forest_admin_datasource_toolkit/utils/schema.rb +42 -0
- data/lib/forest_admin_datasource_toolkit/version.rb +3 -0
- data/lib/forest_admin_datasource_toolkit.rb +10 -0
- data/sig/forest_admin_datasource_toolkit/collection.rbs +26 -0
- data/sig/forest_admin_datasource_toolkit/components/contracts/collection_contract.rbs +29 -0
- data/sig/forest_admin_datasource_toolkit/components/contracts/datasource_contract.rbs +17 -0
- data/sig/forest_admin_datasource_toolkit/datasource.rbs +12 -0
- data/sig/forest_admin_datasource_toolkit/schema/column_schema.rbs +15 -0
- data/sig/forest_admin_datasource_toolkit/schema/relation_schema.rbs +8 -0
- data/sig/forest_admin_datasource_toolkit/schema/relations/many_relation_schema.rbs +10 -0
- data/sig/forest_admin_datasource_toolkit/schema/relations/many_to_many_schema.rbs +11 -0
- data/sig/forest_admin_datasource_toolkit/schema/relations/single_relation_schema.rbs +10 -0
- data/sig/forest_admin_datasource_toolkit.rbs +4 -0
- metadata +126 -0
@@ -0,0 +1,144 @@
|
|
1
|
+
module ForestAdminDatasourceToolkit
|
2
|
+
module Components
|
3
|
+
module Query
|
4
|
+
module ConditionTree
|
5
|
+
module Nodes
|
6
|
+
class ConditionTreeLeaf < ConditionTree
|
7
|
+
include ForestAdminDatasourceToolkit::Utils
|
8
|
+
include ForestAdminDatasourceToolkit::Exceptions
|
9
|
+
|
10
|
+
attr_reader :field, :operator, :value
|
11
|
+
|
12
|
+
def initialize(field, operator, value = nil)
|
13
|
+
@field = field
|
14
|
+
@operator = operator
|
15
|
+
@value = value
|
16
|
+
valid_operator(@operator) if @operator
|
17
|
+
super()
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_h
|
21
|
+
{
|
22
|
+
field: @field,
|
23
|
+
operator: @operator,
|
24
|
+
value: @value
|
25
|
+
}
|
26
|
+
end
|
27
|
+
|
28
|
+
def valid_operator(value)
|
29
|
+
return if Operators.exist?(value)
|
30
|
+
|
31
|
+
raise ForestException, "Invalid operators, the #{value} operator does not exist."
|
32
|
+
end
|
33
|
+
|
34
|
+
def inverse
|
35
|
+
return override(operator: "Not_#{@operator}") if Operators.exist?("Not_#{@operator}")
|
36
|
+
return override(operator: @operator[4..]) if @operator.start_with?('Not')
|
37
|
+
|
38
|
+
case @operator
|
39
|
+
when 'Blank'
|
40
|
+
override(operator: 'Present')
|
41
|
+
when 'Present'
|
42
|
+
override(operator: 'Blank')
|
43
|
+
else
|
44
|
+
raise ForestException, "Operator: #{@operator} cannot be inverted."
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def replace_leafs
|
49
|
+
result = yield(self)
|
50
|
+
if result.nil?
|
51
|
+
nil
|
52
|
+
elsif result.is_a?(ConditionTree)
|
53
|
+
result
|
54
|
+
else
|
55
|
+
ConditionTreeFactory.from_array(result)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def match(record, collection, timezone)
|
60
|
+
field_value = Record.field_value(record, @field)
|
61
|
+
column_type = Utils::Collection.get_field_schema(collection, @field).column_type
|
62
|
+
|
63
|
+
supported = [
|
64
|
+
Operators::IN,
|
65
|
+
Operators::EQUAL,
|
66
|
+
Operators::LESS_THAN,
|
67
|
+
Operators::GREATER_THAN,
|
68
|
+
Operators::MATCH,
|
69
|
+
Operators::STARTS_WITH,
|
70
|
+
Operators::ENDS_WITH,
|
71
|
+
Operators::LONGER_THAN,
|
72
|
+
Operators::SHORTER_THAN,
|
73
|
+
Operators::INCLUDES_ALL,
|
74
|
+
Operators::NOT_IN,
|
75
|
+
Operators::NOT_EQUAL,
|
76
|
+
Operators::NOT_CONTAINS
|
77
|
+
]
|
78
|
+
|
79
|
+
case @operator
|
80
|
+
when Operators::IN
|
81
|
+
Array(@value).include?(field_value)
|
82
|
+
when Operators::EQUAL
|
83
|
+
field_value == @value
|
84
|
+
when Operators::LESS_THAN
|
85
|
+
field_value < @value
|
86
|
+
when Operators::GREATER_THAN
|
87
|
+
field_value > @value
|
88
|
+
when Operators::MATCH
|
89
|
+
field_value.is_a?(String) && @value.match(field_value)
|
90
|
+
when Operators::STARTS_WITH
|
91
|
+
field_value.is_a?(String) && field_value.start_with?(@value)
|
92
|
+
when Operators::ENDS_WITH
|
93
|
+
field_value.is_a?(String) && field_value.end_with?(@value)
|
94
|
+
when Operators::LONGER_THAN
|
95
|
+
field_value.is_a?(String) && field_value.length > @value
|
96
|
+
when Operators::SHORTER_THAN
|
97
|
+
field_value.is_a?(String) && field_value.length < @value
|
98
|
+
when Operators::INCLUDES_ALL
|
99
|
+
Array(@value).all? { |v| field_value.include?(v) }
|
100
|
+
when Operators::NOT_IN, Operators::NOT_EQUAL, Operators::NOT_CONTAINS
|
101
|
+
!inverse.match(record, collection, timezone)
|
102
|
+
else
|
103
|
+
ConditionTreeEquivalent.get_equivalent_tree(
|
104
|
+
self,
|
105
|
+
supported,
|
106
|
+
column_type,
|
107
|
+
timezone
|
108
|
+
)&.match(record, collection, timezone)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def for_each_leaf
|
113
|
+
yield(self)
|
114
|
+
end
|
115
|
+
|
116
|
+
def every_leaf
|
117
|
+
yield(self)
|
118
|
+
end
|
119
|
+
|
120
|
+
def some_leaf
|
121
|
+
yield(self)
|
122
|
+
end
|
123
|
+
|
124
|
+
def projection
|
125
|
+
Projection.new([@field])
|
126
|
+
end
|
127
|
+
|
128
|
+
def override(args)
|
129
|
+
ConditionTreeLeaf.new(
|
130
|
+
args[:field] || @field,
|
131
|
+
args[:operator] || @operator,
|
132
|
+
args[:value] || @value
|
133
|
+
)
|
134
|
+
end
|
135
|
+
|
136
|
+
def use_interval_operator
|
137
|
+
Operators.interval_operators.include?(@operator)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
module ForestAdminDatasourceToolkit
|
2
|
+
module Components
|
3
|
+
module Query
|
4
|
+
module ConditionTree
|
5
|
+
class Operators
|
6
|
+
EQUAL = 'Equal'.freeze
|
7
|
+
NOT_EQUAL = 'Not_Equal'.freeze
|
8
|
+
LESS_THAN = 'Less_Than'.freeze
|
9
|
+
GREATER_THAN = 'Greater_Than'.freeze
|
10
|
+
MATCH = 'Match'.freeze
|
11
|
+
LIKE = 'Like'.freeze
|
12
|
+
I_LIKE = 'ILike'.freeze
|
13
|
+
NOT_CONTAINS = 'Not_Contains'.freeze
|
14
|
+
CONTAINS = 'Contains'.freeze
|
15
|
+
I_CONTAINS = 'IContains'.freeze
|
16
|
+
LONGER_THAN = 'Longer_Than'.freeze
|
17
|
+
SHORTER_THAN = 'Shorter_Than'.freeze
|
18
|
+
INCLUDES_ALL = 'Includes_All'.freeze
|
19
|
+
PRESENT = 'Present'.freeze
|
20
|
+
BLANK = 'Blank'.freeze
|
21
|
+
IN = 'In'.freeze
|
22
|
+
NOT_IN = 'Not_In'.freeze
|
23
|
+
STARTS_WITH = 'Starts_With'.freeze
|
24
|
+
I_STARTS_WITH = 'IStarts_With'.freeze
|
25
|
+
ENDS_WITH = 'Ends_With'.freeze
|
26
|
+
I_ENDS_WITH = 'IEnds_With'.freeze
|
27
|
+
MISSING = 'Missing'.freeze
|
28
|
+
BEFORE = 'Before'.freeze
|
29
|
+
AFTER = 'After'.freeze
|
30
|
+
AFTER_X_HOURS_AGO = 'After_X_Hours_Ago'.freeze
|
31
|
+
BEFORE_X_HOURS_AGO = 'Before_X_Hours_Ago'.freeze
|
32
|
+
FUTURE = 'Future'.freeze
|
33
|
+
PAST = 'Past'.freeze
|
34
|
+
TODAY = 'Today'.freeze
|
35
|
+
YESTERDAY = 'Yesterday'.freeze
|
36
|
+
PREVIOUS_WEEK = 'Previous_Week'.freeze
|
37
|
+
PREVIOUS_MONTH = 'Previous_Month'.freeze
|
38
|
+
PREVIOUS_QUARTER = 'Previous_Quarter'.freeze
|
39
|
+
PREVIOUS_YEAR = 'Previous_Year'.freeze
|
40
|
+
PREVIOUS_WEEK_TO_DATE = 'Previous_Week_To_Date'.freeze
|
41
|
+
PREVIOUS_MONTH_TO_DATE = 'Previous_Month_To_Date'.freeze
|
42
|
+
PREVIOUS_QUARTER_TO_DATE = 'Previous_Quarter_To_Date'.freeze
|
43
|
+
PREVIOUS_YEAR_TO_DATE = 'Previous_Year_To_Date'.freeze
|
44
|
+
PREVIOUS_X_DAYS = 'Previous_X_Days'.freeze
|
45
|
+
PREVIOUS_X_DAYS_TO_DATE = 'Previous_X_Days_To_Date'.freeze
|
46
|
+
|
47
|
+
def self.all
|
48
|
+
constants
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.exist?(operator_value)
|
52
|
+
case operator_value
|
53
|
+
when 'ILike'
|
54
|
+
operator_value = 'I_LIKE'
|
55
|
+
when 'IContains'
|
56
|
+
operator_value = 'I_CONTAINS'
|
57
|
+
when 'IStarts_With'
|
58
|
+
operator_value = 'I_STARTS_WITH'
|
59
|
+
when 'IEnds_With'
|
60
|
+
operator_value = 'I_ENDS_WITH'
|
61
|
+
end
|
62
|
+
|
63
|
+
all.include?(operator_value.upcase.to_sym)
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.interval_operators
|
67
|
+
[
|
68
|
+
self::TODAY,
|
69
|
+
self::YESTERDAY,
|
70
|
+
self::PREVIOUS_MONTH,
|
71
|
+
self::PREVIOUS_QUARTER,
|
72
|
+
self::PREVIOUS_WEEK,
|
73
|
+
self::PREVIOUS_YEAR,
|
74
|
+
self::PREVIOUS_MONTH_TO_DATE,
|
75
|
+
self::PREVIOUS_QUARTER_TO_DATE,
|
76
|
+
self::PREVIOUS_WEEK_TO_DATE,
|
77
|
+
self::PREVIOUS_X_DAYS_TO_DATE,
|
78
|
+
self::PREVIOUS_X_DAYS,
|
79
|
+
self::PREVIOUS_YEAR_TO_DATE
|
80
|
+
]
|
81
|
+
end
|
82
|
+
|
83
|
+
def self.unique_operators
|
84
|
+
[
|
85
|
+
self::EQUAL,
|
86
|
+
self::NOT_EQUAL,
|
87
|
+
self::LESS_THAN,
|
88
|
+
self::GREATER_THAN,
|
89
|
+
self::MATCH,
|
90
|
+
self::NOT_CONTAINS,
|
91
|
+
self::LONGER_THAN,
|
92
|
+
self::SHORTER_THAN,
|
93
|
+
self::INCLUDES_ALL
|
94
|
+
]
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
data/lib/forest_admin_datasource_toolkit/components/query/condition_tree/transforms/comparisons.rb
ADDED
@@ -0,0 +1,123 @@
|
|
1
|
+
module ForestAdminDatasourceToolkit
|
2
|
+
module Components
|
3
|
+
module Query
|
4
|
+
module ConditionTree
|
5
|
+
module Transforms
|
6
|
+
class Comparisons
|
7
|
+
include ForestAdminDatasourceToolkit::Components::Query::ConditionTree::Nodes
|
8
|
+
|
9
|
+
def self.transforms
|
10
|
+
{
|
11
|
+
Operators::BLANK => [
|
12
|
+
{
|
13
|
+
depends_on: [Operators::IN],
|
14
|
+
for_types: ['String'],
|
15
|
+
replacer: ->(leaf) { leaf.override({ operator: Operators::IN, value: [nil, ''] }) }
|
16
|
+
},
|
17
|
+
{
|
18
|
+
depends_on: [Operators::MISSING],
|
19
|
+
replacer: ->(leaf) { leaf.override({ operator: Operators::MISSING }) }
|
20
|
+
}
|
21
|
+
],
|
22
|
+
Operators::MISSING => [
|
23
|
+
{
|
24
|
+
depends_on: [Operators::EQUAL],
|
25
|
+
replacer: ->(leaf) { leaf.override({ operator: Operators::EQUAL, value: nil }) }
|
26
|
+
}
|
27
|
+
],
|
28
|
+
Operators::PRESENT => [
|
29
|
+
{
|
30
|
+
depends_on: [Operators::NOT_IN],
|
31
|
+
for_types: ['String'],
|
32
|
+
replacer: ->(leaf) { leaf.override({ operator: Operators::NOT_IN, value: [nil, ''] }) }
|
33
|
+
},
|
34
|
+
{
|
35
|
+
depends_on: [Operators::NOT_EQUAL],
|
36
|
+
replacer: ->(leaf) { leaf.override({ operator: Operators::NOT_EQUAL, value: nil }) }
|
37
|
+
}
|
38
|
+
],
|
39
|
+
Operators::EQUAL => [
|
40
|
+
{
|
41
|
+
depends_on: [Operators::IN],
|
42
|
+
replacer: ->(leaf) { leaf.override({ operator: Operators::IN, value: [leaf.value] }) }
|
43
|
+
}
|
44
|
+
],
|
45
|
+
Operators::IN => [
|
46
|
+
{
|
47
|
+
depends_on: [Operators::EQUAL, Operators::MATCH],
|
48
|
+
for_types: ['String'],
|
49
|
+
replacer: lambda { |leaf|
|
50
|
+
values = leaf.value
|
51
|
+
conditions = []
|
52
|
+
|
53
|
+
[nil, ''].each do |value|
|
54
|
+
if values.include?(value)
|
55
|
+
conditions.push(ConditionTreeLeaf.new(leaf.field, Operators::EQUAL, value))
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
if values.any? { |value| !value.nil? && value != '' }
|
60
|
+
escaped = values.filter { |value| !value.nil? && value != '' }
|
61
|
+
|
62
|
+
conditions.push(ConditionTreeLeaf.new(leaf.field, Operators::MATCH,
|
63
|
+
"/(#{escaped.join("|")})/g"))
|
64
|
+
end
|
65
|
+
|
66
|
+
ConditionTreeFactory.union(conditions)
|
67
|
+
}
|
68
|
+
},
|
69
|
+
{
|
70
|
+
depends_on: [Operators::EQUAL],
|
71
|
+
replacer: lambda { |leaf|
|
72
|
+
ConditionTreeFactory.union(
|
73
|
+
leaf.value.map { |item| leaf.override({ operator: Operators::EQUAL, value: item }) }
|
74
|
+
)
|
75
|
+
}
|
76
|
+
}
|
77
|
+
],
|
78
|
+
Operators::NOT_EQUAL => [
|
79
|
+
{
|
80
|
+
depends_on: [Operators::NOT_IN],
|
81
|
+
replacer: ->(leaf) { leaf.override({ operator: Operators::NOT_IN, value: [leaf.value] }) }
|
82
|
+
}
|
83
|
+
],
|
84
|
+
Operators::NOT_IN => [
|
85
|
+
{
|
86
|
+
depends_on: [Operators::NOT_EQUAL, Operators::MATCH],
|
87
|
+
for_types: ['String'],
|
88
|
+
replacer: lambda { |leaf|
|
89
|
+
values = leaf.value
|
90
|
+
conditions = []
|
91
|
+
|
92
|
+
[nil, ''].each do |value|
|
93
|
+
if values.include?(value)
|
94
|
+
conditions.push(ConditionTreeLeaf.new(leaf.field, Operators::NOT_EQUAL, value))
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
if values.any? { |value| !value.nil? && value != '' }
|
99
|
+
escaped = values.filter { |value| !value.nil? && value != '' }
|
100
|
+
conditions.push(ConditionTreeLeaf.new(leaf.field, Operators::MATCH,
|
101
|
+
"/(?!(#{escaped.join("|")}))/g"))
|
102
|
+
end
|
103
|
+
|
104
|
+
ConditionTreeFactory.intersect(conditions)
|
105
|
+
}
|
106
|
+
},
|
107
|
+
{
|
108
|
+
depends_on: [Operators::NOT_EQUAL],
|
109
|
+
replacer: lambda { |leaf|
|
110
|
+
ConditionTreeFactory.intersect(
|
111
|
+
leaf.value.map { |item| leaf.override({ operator: Operators::NOT_EQUAL, value: item }) }
|
112
|
+
)
|
113
|
+
}
|
114
|
+
}
|
115
|
+
]
|
116
|
+
}
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
data/lib/forest_admin_datasource_toolkit/components/query/condition_tree/transforms/pattern.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
module ForestAdminDatasourceToolkit
|
2
|
+
module Components
|
3
|
+
module Query
|
4
|
+
module ConditionTree
|
5
|
+
module Transforms
|
6
|
+
class Pattern
|
7
|
+
def self.likes(get_pattern, case_sensitive)
|
8
|
+
operator = case_sensitive ? Operators::LIKE : Operators::I_LIKE
|
9
|
+
|
10
|
+
{
|
11
|
+
dependsOn: [operator],
|
12
|
+
forTypes: ['String'],
|
13
|
+
replacer: ->(leaf) { leaf.override(operator: operator, value: get_pattern.call(leaf.value)) }
|
14
|
+
}
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.match(case_sensitive)
|
18
|
+
{
|
19
|
+
dependsOn: [Operators::MATCH],
|
20
|
+
forTypes: ['String'],
|
21
|
+
replacer: lambda { |leaf|
|
22
|
+
regex = leaf.value.gsub(/([\.\\\+\*\?\[\^\]\$\(\)\{\}\=\!\<\>\|\:\-])/, '\\\\\1')
|
23
|
+
regex.gsub!('%', '.*')
|
24
|
+
regex.tr!('_', '.')
|
25
|
+
|
26
|
+
leaf.override(operator: Operators::MATCH, value: "/^#{regex}$/#{case_sensitive ? "" : "i"}")
|
27
|
+
}
|
28
|
+
}
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.transforms
|
32
|
+
{
|
33
|
+
Operators::CONTAINS => [likes(->(value) { "%#{value}%" }, true)],
|
34
|
+
Operators::STARTS_WITH => [likes(->(value) { "#{value}%" }, true)],
|
35
|
+
Operators::ENDS_WITH => [likes(->(value) { "%#{value}" }, true)],
|
36
|
+
Operators::I_CONTAINS => [likes(->(value) { "%#{value}%" }, false)],
|
37
|
+
Operators::I_STARTS_WITH => [likes(->(value) { "#{value}%" }, false)],
|
38
|
+
Operators::I_ENDS_WITH => [likes(->(value) { "%#{value}" }, false)],
|
39
|
+
Operators::I_LIKE => [match(false)],
|
40
|
+
Operators::LIKE => [match(true)]
|
41
|
+
}
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
require 'active_support/all'
|
2
|
+
require 'active_support/core_ext/numeric/time'
|
3
|
+
|
4
|
+
module ForestAdminDatasourceToolkit
|
5
|
+
module Components
|
6
|
+
module Query
|
7
|
+
module ConditionTree
|
8
|
+
module Transforms
|
9
|
+
class Times
|
10
|
+
def self.format(value)
|
11
|
+
value.in_time_zone('UTC').iso8601
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.compare(operator)
|
15
|
+
{
|
16
|
+
dependsOn: [operator],
|
17
|
+
forTypes: ['Date', 'Dateonly'],
|
18
|
+
replacer: lambda { |leaf, tz|
|
19
|
+
leaf.override(operator: operator, value: format(yield(Time.now.in_time_zone(tz), leaf.value)))
|
20
|
+
}
|
21
|
+
}
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.interval(start_fn, end_fn)
|
25
|
+
{
|
26
|
+
dependsOn: [Operators::LESS_THAN, Operators::GREATER_THAN],
|
27
|
+
forTypes: ['Date', 'Dateonly'],
|
28
|
+
replacer: lambda do |leaf, tz|
|
29
|
+
value_greater_than = if leaf.value.nil?
|
30
|
+
format(start_fn.call(Time.now.in_time_zone(tz)))
|
31
|
+
else
|
32
|
+
format(start_fn.call(
|
33
|
+
Time.now.in_time_zone(tz), leaf.value
|
34
|
+
))
|
35
|
+
end
|
36
|
+
value_less_than = if leaf.value.nil?
|
37
|
+
format(end_fn.call(Time.now.in_time_zone(tz)))
|
38
|
+
else
|
39
|
+
format(end_fn.call(
|
40
|
+
Time.now.in_time_zone(tz), leaf.value
|
41
|
+
))
|
42
|
+
end
|
43
|
+
|
44
|
+
ConditionTreeFactory.intersect(
|
45
|
+
[
|
46
|
+
leaf.override(operator: Operators::GREATER_THAN, value: value_greater_than),
|
47
|
+
leaf.override(operator: Operators::LESS_THAN, value: value_less_than)
|
48
|
+
]
|
49
|
+
)
|
50
|
+
end
|
51
|
+
}
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.previous_interval(duration)
|
55
|
+
interval(
|
56
|
+
lambda { |now|
|
57
|
+
duration == 'quarter' ? now.prev_quarter : (now - 1.send(duration)).send("beginning_of_#{duration}")
|
58
|
+
},
|
59
|
+
->(now) { now.send("beginning_of_#{duration}") }
|
60
|
+
)
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.previous_interval_to_date(duration)
|
64
|
+
interval(
|
65
|
+
->(now) { now.send("beginning_of_#{duration}") },
|
66
|
+
->(now) { now }
|
67
|
+
)
|
68
|
+
end
|
69
|
+
|
70
|
+
def self.transforms
|
71
|
+
{
|
72
|
+
Operators::BEFORE => [compare(Operators::LESS_THAN) { |_now, value| Time.parse(value.to_s) }],
|
73
|
+
Operators::AFTER => [compare(Operators::GREATER_THAN) { |_now, value| Time.parse(value.to_s) }],
|
74
|
+
Operators::PAST => [compare(Operators::LESS_THAN) { |now| now }],
|
75
|
+
Operators::FUTURE => [compare(Operators::GREATER_THAN) { |now| now }],
|
76
|
+
Operators::BEFORE_X_HOURS_AGO => [compare(Operators::LESS_THAN) { |now, value| now - value.hours }],
|
77
|
+
Operators::AFTER_X_HOURS_AGO => [compare(Operators::GREATER_THAN) { |now, value| now - value.hours }],
|
78
|
+
Operators::PREVIOUS_WEEK_TO_DATE => [previous_interval_to_date('week')],
|
79
|
+
Operators::PREVIOUS_MONTH_TO_DATE => [previous_interval_to_date('month')],
|
80
|
+
Operators::PREVIOUS_QUARTER_TO_DATE => [previous_interval_to_date('quarter')],
|
81
|
+
Operators::PREVIOUS_YEAR_TO_DATE => [previous_interval_to_date('year')],
|
82
|
+
Operators::YESTERDAY => [previous_interval('day')],
|
83
|
+
Operators::PREVIOUS_WEEK => [previous_interval('week')],
|
84
|
+
Operators::PREVIOUS_MONTH => [previous_interval('month')],
|
85
|
+
Operators::PREVIOUS_QUARTER => [previous_interval('quarter')],
|
86
|
+
Operators::PREVIOUS_YEAR => [previous_interval('year')],
|
87
|
+
Operators::PREVIOUS_X_DAYS_TO_DATE => [
|
88
|
+
interval(
|
89
|
+
->(now, value) { (now - value.days).beginning_of_day },
|
90
|
+
->(now, _value) { now }
|
91
|
+
)
|
92
|
+
],
|
93
|
+
Operators::PREVIOUS_X_DAYS => [
|
94
|
+
interval(
|
95
|
+
->(now, value) { (now - value.days).beginning_of_day },
|
96
|
+
->(now, _value) { now.beginning_of_day }
|
97
|
+
)
|
98
|
+
],
|
99
|
+
Operators::TODAY => [
|
100
|
+
interval(
|
101
|
+
->(now) { now.beginning_of_day },
|
102
|
+
->(now) { (now + 1.day).beginning_of_day }
|
103
|
+
)
|
104
|
+
]
|
105
|
+
}
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module ForestAdminDatasourceToolkit
|
2
|
+
module Components
|
3
|
+
module Query
|
4
|
+
class Filter
|
5
|
+
attr_reader :condition_tree, :segment, :sort, :search, :search_extended, :page
|
6
|
+
|
7
|
+
def initialize(condition_tree: nil, search: nil, search_extended: nil, segment: nil, sort: nil, page: nil)
|
8
|
+
@condition_tree = condition_tree
|
9
|
+
@search = search
|
10
|
+
@search_extended = search_extended
|
11
|
+
@segment = segment
|
12
|
+
@sort = sort
|
13
|
+
@page = page
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_h
|
17
|
+
{
|
18
|
+
condition_tree: @condition_tree,
|
19
|
+
search: @search,
|
20
|
+
search_extended: @search_extended,
|
21
|
+
segment: @segment,
|
22
|
+
sort: @sort,
|
23
|
+
page: @page
|
24
|
+
}
|
25
|
+
end
|
26
|
+
|
27
|
+
def nestable?
|
28
|
+
!@search && !@segment
|
29
|
+
end
|
30
|
+
|
31
|
+
def override(args)
|
32
|
+
args = to_h.merge(args)
|
33
|
+
|
34
|
+
Filter.new(**args)
|
35
|
+
end
|
36
|
+
|
37
|
+
def nest(prefix)
|
38
|
+
raise ForestException, "Filter can't be nested" unless nestable?
|
39
|
+
|
40
|
+
override(condition_tree: @condition_tree&.nest(prefix))
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|