Skip to content

Commit 0e3b54a

Browse files
fix(filters): filtering on date only fields now format value properly (#727)
1 parent 1ca652b commit 0e3b54a

File tree

4 files changed

+84
-31
lines changed

4 files changed

+84
-31
lines changed

app/services/forest_liana/filters_parser.rb

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,8 @@ def parse_condition_without_smart_field(condition)
8484
field_name = condition['field']
8585

8686
if @operator_date_parser.is_date_operator?(operator)
87-
condition = @operator_date_parser.get_date_filter(operator, value)
87+
field_schema = SchemaUtils.find_column_schema_by_name(@resource.name, field_name)
88+
condition = @operator_date_parser.get_date_filter(operator, value, field_schema)
8889
return "#{parse_field_name(field_name)} #{condition}"
8990
end
9091

@@ -259,10 +260,12 @@ def parse_aggregation_on_previous_interval(node, previous_condition)
259260

260261
def parse_previous_interval_condition(condition)
261262
raise_empty_condition_in_filter_error unless condition
263+
field_schema = SchemaUtils.find_column_schema_by_name(@resource.name, condition['field'])
262264

263265
parsed_condition = @operator_date_parser.get_date_filter_for_previous_interval(
264266
condition['operator'],
265-
condition['value']
267+
condition['value'],
268+
field_schema
266269
)
267270

268271
"#{parse_field_name(condition['field'])} #{parsed_condition}"

app/services/forest_liana/operator_date_interval_parser.rb

Lines changed: 36 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,16 @@ def to_client_timezone(date)
7070
date - @timezone_offset.hours
7171
end
7272

73-
def get_date_filter(operator, value)
73+
74+
def format_date(field_type, value)
75+
if field_type == 'Dateonly'
76+
return value.strftime('%Y-%m-%d')
77+
end
78+
79+
value
80+
end
81+
82+
def get_date_filter(operator, value, field_schema)
7483
return nil unless is_date_operator? operator
7584

7685
filter = case operator
@@ -79,18 +88,18 @@ def get_date_filter(operator, value)
7988
when OPERATOR_PAST
8089
"<= '#{Time.now}'"
8190
when OPERATOR_TODAY
82-
"BETWEEN '#{to_client_timezone(Time.now.beginning_of_day)}' " +
83-
"AND '#{to_client_timezone(Time.now.end_of_day)}'"
91+
"BETWEEN '#{format_date(field_schema[:type], to_client_timezone(Time.now.beginning_of_day))}' " +
92+
"AND '#{format_date(field_schema[:type], to_client_timezone(Time.now.end_of_day))}'"
8493
when OPERATOR_PREVIOUS_X_DAYS
8594
ensure_integer_value(value)
8695
"BETWEEN '" +
87-
"#{to_client_timezone(Integer(value).day.ago.beginning_of_day)}'" +
88-
" AND '#{to_client_timezone(1.day.ago.end_of_day)}'"
96+
"#{format_date(field_schema[:type], to_client_timezone(Integer(value).day.ago.beginning_of_day))}'" +
97+
" AND '#{format_date(field_schema[:type], to_client_timezone(1.day.ago.end_of_day))}'"
8998
when OPERATOR_PREVIOUS_X_DAYS_TO_DATE
9099
ensure_integer_value(value)
91100
"BETWEEN '" +
92-
"#{to_client_timezone((Integer(value) - 1).day.ago.beginning_of_day)}'" +
93-
" AND '#{Time.now}'"
101+
"#{format_date(field_schema[:type], to_client_timezone((Integer(value) - 1).day.ago.beginning_of_day))}'" +
102+
" AND '#{format_date(field_schema[:type], Time.now)}'"
94103
when OPERATOR_BEFORE_X_HOURS_AGO
95104
ensure_integer_value(value)
96105
"< '#{(Integer(value)).hour.ago}'"
@@ -109,35 +118,35 @@ def get_date_filter(operator, value)
109118
to_date = PERIODS[operator][:to_date]
110119

111120
if to_date
112-
from = to_client_timezone(Time.now.send("beginning_of_#{period_of_time}"))
113-
to = Time.now
121+
from = format_date(field_schema[:type], to_client_timezone(Time.now.send("beginning_of_#{period_of_time}")))
122+
to = format_date(field_schema[:type], Time.now)
114123
else
115-
from = to_client_timezone(duration.send(period).ago
116-
.send("beginning_of_#{period_of_time}"))
117-
to = to_client_timezone(duration.send(period).ago
118-
.send("end_of_#{period_of_time}"))
124+
from = format_date(field_schema[:type], to_client_timezone(duration.send(period).ago
125+
.send("beginning_of_#{period_of_time}")))
126+
to = format_date(field_schema[:type], to_client_timezone(duration.send(period).ago
127+
.send("end_of_#{period_of_time}")))
119128
end
120129

121130
"BETWEEN '#{from}' AND '#{to}'"
122131
end
123132

124-
def get_date_filter_for_previous_interval(operator, value)
133+
def get_date_filter_for_previous_interval(operator, value, field_schema)
125134
return nil unless has_previous_interval? operator
126135

127136
case operator
128137
when OPERATOR_TODAY
129-
return "BETWEEN '#{to_client_timezone(1.day.ago.beginning_of_day)}' AND " +
130-
"'#{to_client_timezone(1.day.ago.end_of_day)}'"
138+
return "BETWEEN '#{format_date(field_schema[:type], to_client_timezone(1.day.ago.beginning_of_day))}' AND " +
139+
"'#{format_date(field_schema[:type], to_client_timezone(1.day.ago.end_of_day))}'"
131140
when OPERATOR_PREVIOUS_X_DAYS
132141
ensure_integer_value(value)
133142
return "BETWEEN '" +
134-
"#{to_client_timezone((Integer(value) * 2).day.ago.beginning_of_day)}'" +
135-
" AND '#{to_client_timezone((Integer(value) + 1).day.ago.end_of_day)}'"
143+
"#{format_date(field_schema[:type], to_client_timezone((Integer(value) * 2).day.ago.beginning_of_day))}'" +
144+
" AND '#{format_date(field_schema[:type], to_client_timezone((Integer(value) + 1).day.ago.end_of_day))}'"
136145
when OPERATOR_PREVIOUS_X_DAYS_TO_DATE
137146
ensure_integer_value(value)
138147
return "BETWEEN '" +
139-
"#{to_client_timezone(((Integer(value) * 2) - 1).day.ago.beginning_of_day)}'" +
140-
" AND '#{to_client_timezone(Integer(value).day.ago)}'"
148+
"#{format_date(field_schema[:type], to_client_timezone(((Integer(value) * 2) - 1).day.ago.beginning_of_day))}'" +
149+
" AND '#{format_date(field_schema[:type], to_client_timezone(Integer(value).day.ago))}'"
141150
end
142151

143152
duration = PERIODS[operator][:duration]
@@ -146,14 +155,14 @@ def get_date_filter_for_previous_interval(operator, value)
146155
to_date = PERIODS[operator][:to_date]
147156

148157
if to_date
149-
from = to_client_timezone((duration)
150-
.send(period).ago.send("beginning_of_#{period_of_time}"))
151-
to = to_client_timezone((duration).send(period).ago)
158+
from = format_date(field_schema[:type], to_client_timezone((duration)
159+
.send(period).ago.send("beginning_of_#{period_of_time}")))
160+
to = format_date(field_schema[:type], to_client_timezone((duration).send(period).ago))
152161
else
153-
from = to_client_timezone((duration * 2).send(period).ago
154-
.send("beginning_of_#{period_of_time}"))
155-
to = to_client_timezone((duration * 2).send(period).ago
156-
.send("end_of_#{period_of_time}"))
162+
from = format_date(field_schema[:type], to_client_timezone((duration * 2).send(period).ago
163+
.send("beginning_of_#{period_of_time}")))
164+
to = format_date(field_schema[:type], to_client_timezone((duration * 2).send(period).ago
165+
.send("end_of_#{period_of_time}")))
157166
end
158167

159168
"BETWEEN '#{from}' AND '#{to}'"

app/services/forest_liana/schema_utils.rb

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,23 @@ def self.find_model_from_collection_name(collection_name, logs = false)
4949
model_found
5050
end
5151

52+
def self.find_column_schema_by_name(collection_name, field_name)
53+
schema = ForestLiana.apimap.find { |collection| collection.name == collection_name }
54+
if field_name.include?(':')
55+
relation, field_name = field_name.split(':')
56+
relation_schema = schema.fields.find do |field|
57+
field[:field].to_s == relation
58+
end
59+
foreign_collection_name, = relation_schema[:reference].split('.')
60+
61+
return find_column_schema_by_name(foreign_collection_name, field_name)
62+
else
63+
return schema.fields.find do |field|
64+
field[:field].to_s == field_name
65+
end
66+
end
67+
end
68+
5269
def self.tables_names
5370
ActiveRecord::Base.connection.tables
5471
end

test/services/forest_liana/operator_date_interval_parser_test.rb

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,36 @@ class OperatorDateIntervalParserTest < ActiveSupport::TestCase
33
test 'OPERATOR_AFTER_X_HOURS_AGO and OPERATOR_BEFORE_X_HOURS_AGO should not take timezone into account' do
44
# Setting a big timezone (GMT+10) on purpose, the timezone should not be applied on the result date
55
operatorDateIntervalParser = OperatorDateIntervalParser.new('Australia/Sydney')
6+
fake_field_schema = {
7+
field: "foo",
8+
type: "Dateonly",
9+
is_filterable: true,
10+
is_sortable: true,
11+
is_read_only: false,
12+
is_required: false,
13+
is_virtual: false,
14+
default_value: nil,
15+
integration: nil,
16+
reference: nil,
17+
inverse_of: nil,
18+
relationships: nil,
19+
widget: nil,
20+
validations: []
21+
}
622

7-
result = operatorDateIntervalParser.get_date_filter(OperatorDateIntervalParser::OPERATOR_AFTER_X_HOURS_AGO, 2)
23+
result = operatorDateIntervalParser.get_date_filter(
24+
OperatorDateIntervalParser::OPERATOR_AFTER_X_HOURS_AGO,
25+
2,
26+
fake_field_schema
27+
)
828
hourComputed = result.split('> ')[1].tr('\'', '').to_datetime.hour
929
assert hourComputed == Time.now.utc.hour - 2
1030

11-
result = operatorDateIntervalParser.get_date_filter(OperatorDateIntervalParser::OPERATOR_BEFORE_X_HOURS_AGO, 2)
31+
result = operatorDateIntervalParser.get_date_filter(
32+
OperatorDateIntervalParser::OPERATOR_BEFORE_X_HOURS_AGO,
33+
2,
34+
fake_field_schema
35+
)
1236
hourComputed = result.split('< ')[1].tr('\'', '').to_datetime.hour
1337
assert hourComputed == Time.now.utc.hour - 2
1438
end

0 commit comments

Comments
 (0)