forest_liana 3.3.0 → 4.0.0.pre.beta.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d23c0e51c9b7da5f7e2b09400b9b2e76c5152cd6
4
- data.tar.gz: e0e7bee4717ba84f70c6c2603a4250b6b3671a2c
3
+ metadata.gz: dafadb3ed7cf1c34d0010729e459c13ba38e22de
4
+ data.tar.gz: 6dff3ee72a4aa231b68508e3d87b0931fb16d718
5
5
  SHA512:
6
- metadata.gz: 0f65c9b9cc4ee79d2df3bcb89cf549d1985d626a008ba7504459018414e30339b63f9769fbd828d8cf7e03f32bd1d92cec5ee4373ce46ba9a588e7319528cccf
7
- data.tar.gz: 4b91d89dcc180d49f38b73604e0c69fe052fc0ac10fc14cbda60ed6c8eb33c84e9736c917317d6f315649934cc3a0aa9d2177642d79a949e31d479809f25d71a
6
+ metadata.gz: 48338ecfc6319ca9ab824fa47771167e9203e3bbe567b3d38ff907b0ae067dc01f20e5830ed7079bdeb67115d54ede8570ce53125a738a016874b90bbc76979f
7
+ data.tar.gz: 052c0e82c436681276c2982480002adcc107b7e0b0018f1bd7056af1e6f6d5f3e281e9f9022d6b432b798224d08595bf9da81da04ab5c1fb52a87c2071c56d08
@@ -25,10 +25,11 @@ module ForestLiana
25
25
  # NOTICE: The Forest user email is returned to track changes made using
26
26
  # Forest with Papertrail.
27
27
  define_method :user_for_paper_trail do
28
- forest_user['data']['data']['email']
28
+ @jwt_decoded_token['email']
29
29
  end
30
30
  end
31
31
 
32
+ # NOTICE: Helper method for Smart Routes logic based on current user info.
32
33
  def forest_user
33
34
  @jwt_decoded_token
34
35
  end
@@ -69,7 +70,13 @@ module ForestLiana
69
70
 
70
71
  @jwt_decoded_token = JWT.decode(token, ForestLiana.auth_secret, true,
71
72
  { algorithm: 'HS256' }).try(:first)
72
- @rendering_id = @jwt_decoded_token['data']['relationships']['renderings']['data'][0]['id']
73
+
74
+ # NOTICE: Automatically logs out the users that use tokens having an old data format.
75
+ if @jwt_decoded_token['data']
76
+ raise ForestLiana::Errors::HTTP401Error.new("Your token format is invalid, please login again.")
77
+ end
78
+
79
+ @rendering_id = @jwt_decoded_token['rendering_id']
73
80
  else
74
81
  head :unauthorized
75
82
  end
@@ -0,0 +1,266 @@
1
+ module ForestLiana
2
+ class FiltersParser
3
+ AGGREGATOR_OPERATOR = %w(and or)
4
+
5
+ def initialize(filters, resource, timezone)
6
+ begin
7
+ @filters = JSON.parse(filters)
8
+ rescue JSON::ParserError
9
+ raise ForestLiana::Errors::HTTP422Error.new('Invalid filters JSON format')
10
+ end
11
+
12
+ @resource = resource
13
+ @operator_date_parser = OperatorDateIntervalParser.new(timezone)
14
+ @joins = []
15
+ end
16
+
17
+ def apply_filters
18
+ return @resource unless @filters
19
+
20
+ where = parse_aggregation(@filters)
21
+ return @resource unless where
22
+
23
+ @joins.each do |join|
24
+ current_resource = @resource.reflect_on_association(join.name).klass
25
+ current_resource.include(ArelHelpers::Aliases)
26
+ current_resource.aliased_as(join.name) do |aliased_resource|
27
+ @resource = @resource.joins(ArelHelpers.join_association(@resource, join.name, Arel::Nodes::OuterJoin, aliases: [aliased_resource]))
28
+ end
29
+ end
30
+
31
+ @resource.where(where)
32
+ end
33
+
34
+ def parse_aggregation(node)
35
+ ensure_valid_aggregation(node)
36
+
37
+ return parse_condition(node) unless node['aggregator']
38
+
39
+ conditions = []
40
+ node['conditions'].each do |condition|
41
+ conditions.push(parse_aggregation(condition))
42
+ end
43
+
44
+ operator = parse_aggregation_operator(node['aggregator'])
45
+
46
+ conditions.empty? ? nil : "(#{conditions.join(" #{operator} ")})"
47
+ end
48
+
49
+ def parse_condition(condition)
50
+ ensure_valid_condition(condition)
51
+
52
+ operator = condition['operator']
53
+ value = condition['value']
54
+ field = condition['field']
55
+
56
+ if @operator_date_parser.is_date_operator?(operator)
57
+ condition = @operator_date_parser.get_date_filter(operator, value)
58
+ return "#{parse_field_name(field)} #{condition}"
59
+ end
60
+
61
+ if is_belongs_to(field)
62
+ association = field.partition(':').first.to_sym
63
+ association_field = field.partition(':').last
64
+
65
+ unless @resource.reflect_on_association(association)
66
+ raise ForestLiana::Errors::HTTP422Error.new("Association '#{association}' not found")
67
+ end
68
+
69
+ current_resource = @resource.reflect_on_association(association).klass
70
+ else
71
+ association_field = field
72
+ current_resource = @resource
73
+ end
74
+
75
+ # NOTICE: Set the integer value instead of a string if "enum" type
76
+ # NOTICE: Rails 3 do not have a defined_enums method
77
+ if current_resource.respond_to?(:defined_enums) && current_resource.defined_enums.has_key?(association_field)
78
+ value = current_resource.defined_enums[association_field][value]
79
+ end
80
+
81
+ parsed_field = parse_field_name(field)
82
+ parsed_operator = parse_operator(operator)
83
+ parsed_value = parse_value(operator, value)
84
+
85
+ if Rails::VERSION::MAJOR >= 5
86
+ ActiveRecord::Base.sanitize_sql(["#{parsed_field} #{parsed_operator} ?", parsed_value])
87
+ else
88
+ "#{parsed_field} #{parsed_operator} #{ActiveRecord::Base.sanitize(parsed_value)}"
89
+ end
90
+ end
91
+
92
+ def parse_aggregation_operator(aggregator_operator)
93
+ unless AGGREGATOR_OPERATOR.include?(aggregator_operator)
94
+ raise_unknown_operator_error(aggregator_operator)
95
+ end
96
+
97
+ aggregator_operator.upcase
98
+ end
99
+
100
+ def parse_operator(operator)
101
+ case operator
102
+ when 'not'
103
+ 'NOT'
104
+ when 'greater_than', 'after'
105
+ '>'
106
+ when 'less_than', 'before'
107
+ '<'
108
+ when 'contains', 'starts_with', 'ends_with'
109
+ 'LIKE'
110
+ when 'not_contains'
111
+ 'NOT LIKE'
112
+ when 'not_equal'
113
+ '!='
114
+ when 'equal'
115
+ '='
116
+ when 'blank'
117
+ 'IS'
118
+ when 'present'
119
+ 'IS NOT'
120
+ else
121
+ raise_unknown_operator_error(operator)
122
+ end
123
+ end
124
+
125
+ def parse_value(operator, value)
126
+ case operator
127
+ when 'not', 'greater_than', 'less_than', 'not_equal', 'equal', 'before', 'after'
128
+ value
129
+ when 'contains', 'not_contains'
130
+ "%#{value}%"
131
+ when 'starts_with'
132
+ "#{value}%"
133
+ when 'ends_with'
134
+ "%#{value}"
135
+ when 'present', 'blank'
136
+ else
137
+ raise_unknown_operator_error(operator)
138
+ end
139
+ end
140
+
141
+ def parse_field_name(field)
142
+ if is_belongs_to(field)
143
+ current_resource = @resource.reflect_on_association(field.split(':').first.to_sym)&.klass
144
+ raise ForestLiana::Errors::HTTP422Error.new("Field '#{field}' not found") unless current_resource
145
+
146
+ association = get_association_name_for_condition(field)
147
+ quoted_table_name = ActiveRecord::Base.connection.quote_column_name(association)
148
+ quoted_field_name = ActiveRecord::Base.connection.quote_column_name(field.split(':')[1])
149
+ else
150
+ quoted_table_name = @resource.quoted_table_name
151
+ quoted_field_name = ActiveRecord::Base.connection.quote_column_name(field)
152
+ current_resource = @resource
153
+ end
154
+
155
+ column_found = current_resource.columns.find { |column| column.name == field.split(':').last }
156
+
157
+ if column_found.nil?
158
+ raise ForestLiana::Errors::HTTP422Error.new("Field '#{field}' not found")
159
+ end
160
+
161
+ "#{quoted_table_name}.#{quoted_field_name}"
162
+ end
163
+
164
+ def is_belongs_to(field)
165
+ field.include?(':')
166
+ end
167
+
168
+ def get_association_name_for_condition(field)
169
+ field, subfield = field.split(':')
170
+
171
+ association = @resource.reflect_on_association(field.to_sym)
172
+ return nil if association.blank?
173
+
174
+ @joins << association unless @joins.include? association
175
+
176
+ association.name
177
+ end
178
+
179
+ # NOTICE: Look for a previous interval condition matching the following:
180
+ # - If the filter is a simple condition at the root the check is done right away.
181
+ # - There can't be a previous interval condition if the aggregator is 'or' (no meaning).
182
+ # - The condition's operator has to be elligible for a previous interval.
183
+ # - There can't be two previous interval condition.
184
+ def get_previous_interval_condition
185
+ current_previous_interval = nil
186
+ # NOTICE: Leaf condition at root
187
+ unless @filters['aggregator']
188
+ return @filters if @operator_date_parser.has_previous_interval?(@filters['operator'])
189
+ end
190
+
191
+ if @filters['aggregator'] === 'and'
192
+ @filters['conditions'].each do |condition|
193
+ # NOTICE: Nested conditions
194
+ return nil if condition['aggregator']
195
+
196
+ if @operator_date_parser.has_previous_interval?(condition['operator'])
197
+ # NOTICE: There can't be two previous_interval.
198
+ return nil if current_previous_interval
199
+
200
+ current_previous_interval = condition
201
+ end
202
+ end
203
+ end
204
+
205
+ current_previous_interval
206
+ end
207
+
208
+ def apply_filters_on_previous_interval(previous_condition)
209
+ # Ressource should have already been joined
210
+ where = parse_aggregation_on_previous_interval(@filters, previous_condition)
211
+
212
+ @resource.where(where)
213
+ end
214
+
215
+ def parse_aggregation_on_previous_interval(node, previous_condition)
216
+ raise_empty_condition_in_filter_error unless node
217
+
218
+ return parse_previous_interval_condition(node) unless node['aggregator']
219
+
220
+ conditions = []
221
+ node['conditions'].each do |condition|
222
+ if condition == previous_condition
223
+ conditions.push(parse_previous_interval_condition(condition))
224
+ else
225
+ conditions.push(parse_aggregation(condition))
226
+ end
227
+ end
228
+
229
+ operator = parse_aggregation_operator(node['aggregator'])
230
+
231
+ conditions.empty? ? nil : "(#{conditions.join(" #{operator} ")})"
232
+ end
233
+
234
+ def parse_previous_interval_condition(condition)
235
+ raise_empty_condition_in_filter_error unless condition
236
+
237
+ parsed_condition = @operator_date_parser.get_date_filter_for_previous_interval(
238
+ condition['operator'],
239
+ condition['value']
240
+ )
241
+
242
+ "#{parse_field_name(condition['field'])} #{parsed_condition}"
243
+ end
244
+
245
+ def raise_unknown_operator_error(operator)
246
+ raise ForestLiana::Errors::HTTP422Error.new("Unknown provided operator '#{operator}'")
247
+ end
248
+
249
+ def raise_empty_condition_in_filter_error
250
+ raise ForestLiana::Errors::HTTP422Error.new('Empty condition in filter')
251
+ end
252
+
253
+ def ensure_valid_aggregation(node)
254
+ raise ForestLiana::Errors::HTTP422Error.new('Filters cannot be a raw value') unless node.is_a?(Hash)
255
+ raise_empty_condition_in_filter_error if node.empty?
256
+ end
257
+
258
+ def ensure_valid_condition(condition)
259
+ raise_empty_condition_in_filter_error if condition.empty?
260
+ raise ForestLiana::Errors::HTTP422Error.new('Condition cannot be a raw value') unless condition.is_a?(Hash)
261
+ unless condition['field'].is_a?(String) and condition['operator'].is_a?(String)
262
+ raise ForestLiana::Errors::HTTP422Error.new('Invalid condition format')
263
+ end
264
+ end
265
+ end
266
+ end
@@ -22,17 +22,8 @@ module ForestLiana
22
22
  def perform
23
23
  value = get_resource().eager_load(@includes)
24
24
 
25
- if @params[:filterType] && @params[:filters]
26
- conditions = []
27
- filter_operator = " #{@params[:filterType]} ".upcase
28
-
29
- @params[:filters].try(:each) do |filter|
30
- operator, filter_value = OperatorValueParser.parse(filter[:value])
31
- conditions << OperatorValueParser.get_condition(filter[:field],
32
- operator, filter_value, @resource, @params[:timezone])
33
- end
34
-
35
- value = value.where(conditions.join(filter_operator))
25
+ if @params[:filters]
26
+ value = FiltersParser.new(@params[:filters], value, @params[:timezone]).apply_filters
36
27
  end
37
28
 
38
29
  value = value.send(time_range, group_by_date_field, {
@@ -94,25 +94,13 @@ module ForestLiana
94
94
 
95
95
  def create_token(user, rendering_id)
96
96
  JWT.encode({
97
- exp: Time.now.to_i + 2.weeks.to_i,
98
- data: {
99
- id: user['id'],
100
- type: 'users',
101
- data: {
102
- email: user['email'],
103
- first_name: user['first_name'],
104
- last_name: user['last_name'],
105
- teams: user['teams']
106
- },
107
- relationships: {
108
- renderings: {
109
- data: [{
110
- type: 'renderings',
111
- id: rendering_id
112
- }]
113
- }
114
- }
115
- }
97
+ id: user['id'],
98
+ email: user['email'],
99
+ first_name: user['first_name'],
100
+ last_name: user['last_name'],
101
+ team: user['teams'][0],
102
+ rendering_id: rendering_id,
103
+ exp: Time.now.to_i + 2.weeks.to_i
116
104
  }, ForestLiana.auth_secret, 'HS256')
117
105
  end
118
106
  end
@@ -1,67 +1,68 @@
1
1
  module ForestLiana
2
2
  class OperatorDateIntervalParser
3
+ OPERATOR_PAST = 'past'
4
+ OPERATOR_FUTURE = 'future'
5
+ OPERATOR_TODAY = 'today'
6
+
7
+ OPERATOR_YESTERDAY = 'yesterday'
8
+ OPERATOR_PREVIOUS_WEEK = 'previous_week'
9
+ OPERATOR_PREVIOUS_MONTH = 'previous_month'
10
+ OPERATOR_PREVIOUS_QUARTER = 'previous_quarter'
11
+ OPERATOR_PREVIOUS_YEAR = 'previous_year'
12
+ OPERATOR_PREVIOUS_WEEK_TO_DATE = 'previous_week_to_date'
13
+ OPERATOR_PREVIOUS_MONTH_TO_DATE = 'previous_month_to_date'
14
+ OPERATOR_PREVIOUS_QUARTER_TO_DATE = 'previous_quarter_to_date'
15
+ OPERATOR_PREVIOUS_YEAR_TO_DATE = 'previous_year_to_date'
16
+
17
+ OPERATOR_PREVIOUS_X_DAYS = 'previous_x_days'
18
+ OPERATOR_PREVIOUS_X_DAYS_TO_DATE = 'previous_x_days_to_date'
19
+ OPERATOR_BEFORE_X_HOURS_AGO = 'before_x_hours_ago'
20
+ OPERATOR_AFTER_X_HOURS_AGO = 'after_x_hours_ago'
21
+
3
22
  PERIODS = {
4
- :$yesterday => { duration: 1, period: 'day' },
5
- :$previousWeek => { duration: 1, period: 'week' },
6
- :$previousMonth => { duration: 1, period: 'month' },
7
- :$previousQuarter => { duration: 3, period: 'month',
8
- period_of_time: 'quarter' },
9
- :$previousYear => { duration: 1, period: 'year' },
10
- :$weekToDate => { duration: 1, period: 'week', to_date: true },
11
- :$monthToDate => { duration: 1, period: 'month', to_date: true },
12
- :$quarterToDate => { duration: 3, period: 'month',
13
- period_of_time: 'quarter', to_date: true },
14
- :$yearToDate => { duration: 1, period: 'year', to_date: true }
23
+ OPERATOR_YESTERDAY => { duration: 1, period: 'day' },
24
+ OPERATOR_PREVIOUS_WEEK => { duration: 1, period: 'week' },
25
+ OPERATOR_PREVIOUS_WEEK_TO_DATE => { duration: 1, period: 'week', to_date: true },
26
+ OPERATOR_PREVIOUS_MONTH => { duration: 1, period: 'month' },
27
+ OPERATOR_PREVIOUS_MONTH_TO_DATE => { duration: 1, period: 'month', to_date: true },
28
+ OPERATOR_PREVIOUS_QUARTER => { duration: 3, period: 'month', period_of_time: 'quarter' },
29
+ OPERATOR_PREVIOUS_QUARTER_TO_DATE => { duration: 3, period: 'month', period_of_time: 'quarter', to_date: true },
30
+ OPERATOR_PREVIOUS_YEAR => { duration: 1, period: 'year' },
31
+ OPERATOR_PREVIOUS_YEAR_TO_DATE => { duration: 1, period: 'year', to_date: true }
15
32
  }
16
33
 
17
- PERIODS_PAST = '$past';
18
- PERIODS_FUTURE = '$future';
19
- PERIODS_TODAY = '$today';
20
-
21
- PERIODS_PREVIOUS_X_DAYS = /^\$previous(\d+)Days$/;
22
- PERIODS_X_DAYS_TO_DATE = /^\$(\d+)DaysToDate$/;
23
- PERIODS_X_HOURS_BEFORE = /^\$(\d+)HoursBefore$/;
24
- PERIODS_X_HOURS_AFTER = /^\$(\d+)HoursAfter$/;
25
-
26
- def initialize(value, timezone)
27
- @value = value
34
+ DATE_OPERATORS_HAVING_PREVIOUS_INTERVAL = [
35
+ OPERATOR_TODAY,
36
+ OPERATOR_YESTERDAY,
37
+ OPERATOR_PREVIOUS_WEEK,
38
+ OPERATOR_PREVIOUS_MONTH,
39
+ OPERATOR_PREVIOUS_QUARTER,
40
+ OPERATOR_PREVIOUS_YEAR,
41
+ OPERATOR_PREVIOUS_WEEK_TO_DATE,
42
+ OPERATOR_PREVIOUS_MONTH_TO_DATE,
43
+ OPERATOR_PREVIOUS_QUARTER_TO_DATE,
44
+ OPERATOR_PREVIOUS_YEAR_TO_DATE,
45
+ OPERATOR_PREVIOUS_X_DAYS,
46
+ OPERATOR_PREVIOUS_X_DAYS_TO_DATE
47
+ ]
48
+
49
+ DATE_OPERATORS = DATE_OPERATORS_HAVING_PREVIOUS_INTERVAL.concat [
50
+ OPERATOR_FUTURE,
51
+ OPERATOR_PAST,
52
+ OPERATOR_BEFORE_X_HOURS_AGO,
53
+ OPERATOR_AFTER_X_HOURS_AGO
54
+ ]
55
+
56
+ def initialize(timezone)
28
57
  @timezone_offset = Time.now.in_time_zone(timezone).utc_offset / 3600
29
58
  end
30
59
 
31
- def is_interval_date_value
32
- return false if @value.nil?
33
- return true if PERIODS[@value.to_sym]
34
-
35
- return true if [PERIODS_PAST, PERIODS_FUTURE, PERIODS_TODAY].include? @value
36
-
37
- match = PERIODS_PREVIOUS_X_DAYS.match(@value)
38
- return true if match && match[1]
39
-
40
- match = PERIODS_X_DAYS_TO_DATE.match(@value)
41
- return true if match && match[1]
42
-
43
- match = PERIODS_X_HOURS_BEFORE.match(@value)
44
- return true if match && match[1]
45
-
46
- match = PERIODS_X_HOURS_AFTER.match(@value)
47
- return true if match && match[1]
48
-
49
- false
60
+ def is_date_operator?(operator)
61
+ DATE_OPERATORS.include? operator
50
62
  end
51
63
 
52
- def has_previous_interval
53
- return false if @value.nil?
54
- return true if PERIODS[@value.to_sym]
55
-
56
- return true if PERIODS_TODAY == @value
57
-
58
- match = PERIODS_PREVIOUS_X_DAYS.match(@value)
59
- return true if match && match[1]
60
-
61
- match = PERIODS_X_DAYS_TO_DATE.match(@value)
62
- return true if match && match[1]
63
-
64
- false
64
+ def has_previous_interval?(operator)
65
+ DATE_OPERATORS_HAVING_PREVIOUS_INTERVAL.include? operator
65
66
  end
66
67
 
67
68
  def to_client_timezone(date)
@@ -69,46 +70,39 @@ module ForestLiana
69
70
  date - @timezone_offset.hours
70
71
  end
71
72
 
72
- def get_interval_date_filter
73
- return nil unless is_interval_date_value()
74
-
75
- return ">= '#{Time.now}'" if @value == PERIODS_FUTURE
76
- return "<= '#{Time.now}'" if @value == PERIODS_PAST
73
+ def get_date_filter(operator, value)
74
+ return nil unless is_date_operator? operator
77
75
 
78
- if @value == PERIODS_TODAY
76
+ case operator
77
+ when OPERATOR_FUTURE
78
+ return ">= '#{Time.now}'"
79
+ when OPERATOR_PAST
80
+ return "<= '#{Time.now}'"
81
+ when OPERATOR_TODAY
79
82
  return "BETWEEN '#{to_client_timezone(Time.now.beginning_of_day)}' " +
80
83
  "AND '#{to_client_timezone(Time.now.end_of_day)}'"
81
- end
82
-
83
- match = PERIODS_PREVIOUS_X_DAYS.match(@value)
84
- if match && match[1]
84
+ when OPERATOR_PREVIOUS_X_DAYS
85
+ ensure_integer_value(value)
85
86
  return "BETWEEN '" +
86
- "#{to_client_timezone(Integer(match[1]).day.ago.beginning_of_day)}'" +
87
+ "#{to_client_timezone(Integer(value).day.ago.beginning_of_day)}'" +
87
88
  " AND '#{to_client_timezone(1.day.ago.end_of_day)}'"
88
- end
89
-
90
- match = PERIODS_X_DAYS_TO_DATE.match(@value)
91
- if match && match[1]
89
+ when OPERATOR_PREVIOUS_X_DAYS_TO_DATE
90
+ ensure_integer_value(value)
92
91
  return "BETWEEN '" +
93
- "#{to_client_timezone((Integer(match[1]) - 1).day.ago.beginning_of_day)}'" +
92
+ "#{to_client_timezone((Integer(value) - 1).day.ago.beginning_of_day)}'" +
94
93
  " AND '#{Time.now}'"
94
+ when OPERATOR_BEFORE_X_HOURS_AGO
95
+ ensure_integer_value(value)
96
+ return "< '#{to_client_timezone((Integer(value)).hour.ago)}'"
97
+ when OPERATOR_AFTER_X_HOURS_AGO
98
+ ensure_integer_value(value)
99
+ return "> '#{to_client_timezone((Integer(value)).hour.ago)}'"
95
100
  end
96
101
 
97
- match = PERIODS_X_HOURS_BEFORE.match(@value)
98
- if match && match[1]
99
- return "< '#{to_client_timezone((Integer(match[1])).hour.ago)}'"
100
- end
101
-
102
- match = PERIODS_X_HOURS_AFTER.match(@value)
103
- if match && match[1]
104
- return "> '#{to_client_timezone((Integer(match[1])).hour.ago)}'"
105
- end
106
-
107
- duration = PERIODS[@value.to_sym][:duration]
108
- period = PERIODS[@value.to_sym][:period]
109
- period_of_time = PERIODS[@value.to_sym][:period_of_time] ||
110
- PERIODS[@value.to_sym][:period]
111
- to_date = PERIODS[@value.to_sym][:to_date]
102
+ duration = PERIODS[operator][:duration]
103
+ period = PERIODS[operator][:period]
104
+ period_of_time = PERIODS[operator][:period_of_time] || period
105
+ to_date = PERIODS[operator][:to_date]
112
106
 
113
107
  if to_date
114
108
  from = to_client_timezone(Time.now.send("beginning_of_#{period_of_time}"))
@@ -122,45 +116,47 @@ module ForestLiana
122
116
  "BETWEEN '#{from}' AND '#{to}'"
123
117
  end
124
118
 
125
- def get_interval_date_filter_for_previous_interval
126
- return nil unless has_previous_interval()
119
+ def get_date_filter_for_previous_interval(operator, value)
120
+ return nil unless has_previous_interval? operator
127
121
 
128
- if @value == PERIODS_TODAY
122
+ case operator
123
+ when OPERATOR_TODAY
129
124
  return "BETWEEN '#{to_client_timezone(1.day.ago.beginning_of_day)}' AND " +
130
125
  "'#{to_client_timezone(1.day.ago.end_of_day)}'"
131
- end
132
-
133
- match = PERIODS_PREVIOUS_X_DAYS.match(@value)
134
- if match && match[1]
126
+ when OPERATOR_PREVIOUS_X_DAYS
127
+ ensure_integer_value(value)
135
128
  return "BETWEEN '" +
136
- "#{to_client_timezone((Integer(match[1]) * 2).day.ago.beginning_of_day)}'" +
137
- " AND '#{to_client_timezone((Integer(match[1]) + 1).day.ago.end_of_day)}'"
138
- end
139
-
140
- match = PERIODS_X_DAYS_TO_DATE.match(@value)
141
- if match && match[1]
129
+ "#{to_client_timezone((Integer(value) * 2).day.ago.beginning_of_day)}'" +
130
+ " AND '#{to_client_timezone((Integer(value) + 1).day.ago.end_of_day)}'"
131
+ when OPERATOR_PREVIOUS_X_DAYS_TO_DATE
132
+ ensure_integer_value(value)
142
133
  return "BETWEEN '" +
143
- "#{to_client_timezone(((Integer(match[1]) * 2) - 1).day.ago.beginning_of_day)}'" +
144
- " AND '#{to_client_timezone(Integer(match[1]).day.ago)}'"
134
+ "#{to_client_timezone(((Integer(value) * 2) - 1).day.ago.beginning_of_day)}'" +
135
+ " AND '#{to_client_timezone(Integer(value).day.ago)}'"
145
136
  end
146
137
 
147
- duration = PERIODS[@value.to_sym][:duration]
148
- period = PERIODS[@value.to_sym][:period]
149
- period_of_time = PERIODS[@value.to_sym][:period_of_time] ||
150
- PERIODS[@value.to_sym][:period]
151
- to_date = PERIODS[@value.to_sym][:to_date]
138
+ duration = PERIODS[operator][:duration]
139
+ period = PERIODS[operator][:period]
140
+ period_of_time = PERIODS[operator][:period_of_time] || period
141
+ to_date = PERIODS[operator][:to_date]
152
142
 
153
143
  if to_date
154
- from = to_client_timezone((duration).send(period).ago
155
- .send("beginning_of_#{period_of_time}"))
144
+ from = to_client_timezone((duration)
145
+ .send(period).ago.send("beginning_of_#{period_of_time}"))
156
146
  to = to_client_timezone((duration).send(period).ago)
157
147
  else
158
148
  from = to_client_timezone((duration * 2).send(period).ago
159
- .send("beginning_of_#{period_of_time}"))
149
+ .send("beginning_of_#{period_of_time}"))
160
150
  to = to_client_timezone((1 + duration).send(period).ago
161
- .send("end_of_#{period_of_time}"))
151
+ .send("end_of_#{period_of_time}"))
162
152
  end
163
153
  "BETWEEN '#{from}' AND '#{to}'"
164
154
  end
155
+
156
+ def ensure_integer_value(value)
157
+ unless value.is_a?(Integer) || /\A[-+]?\d+\z/.match(value)
158
+ raise ForestLiana::Errors::HTTP422Error.new('\'value\' should be an Integer')
159
+ end
160
+ end
165
161
  end
166
162
  end