Allow filtering with timestamp (#8842).
git-svn-id: http://svn.redmine.org/redmine/trunk@12477 e93f8b46-1217-0410-a6f0-8f06a7374b81
This commit is contained in:
parent
58a63c49c4
commit
a4d3da988a
|
@ -242,7 +242,9 @@ class Query < ActiveRecord::Base
|
||||||
when :date, :date_past
|
when :date, :date_past
|
||||||
case operator_for(field)
|
case operator_for(field)
|
||||||
when "=", ">=", "<=", "><"
|
when "=", ">=", "<=", "><"
|
||||||
add_filter_error(field, :invalid) if values_for(field).detect {|v| v.present? && (!v.match(/^\d{4}-\d{2}-\d{2}$/) || (Date.parse(v) rescue nil).nil?) }
|
add_filter_error(field, :invalid) if values_for(field).detect {|v|
|
||||||
|
v.present? && (!v.match(/\A\d{4}-\d{2}-\d{2}(T\d{2}((:)?\d{2}){,2}(Z|\d{2}:?\d{2})?)?\z/) || parse_date(v).nil?)
|
||||||
|
}
|
||||||
when ">t-", "<t-", "t-", ">t+", "<t+", "t+", "><t+", "><t-"
|
when ">t-", "<t-", "t-", ">t+", "<t+", "t+", "><t+", "><t-"
|
||||||
add_filter_error(field, :invalid) if values_for(field).detect {|v| v.present? && !v.match(/^\d+$/) }
|
add_filter_error(field, :invalid) if values_for(field).detect {|v| v.present? && !v.match(/^\d+$/) }
|
||||||
end
|
end
|
||||||
|
@ -624,7 +626,7 @@ class Query < ActiveRecord::Base
|
||||||
if value.any?
|
if value.any?
|
||||||
case type_for(field)
|
case type_for(field)
|
||||||
when :date, :date_past
|
when :date, :date_past
|
||||||
sql = date_clause(db_table, db_field, (Date.parse(value.first) rescue nil), (Date.parse(value.first) rescue nil))
|
sql = date_clause(db_table, db_field, parse_date(value.first), parse_date(value.first))
|
||||||
when :integer
|
when :integer
|
||||||
if is_custom_filter
|
if is_custom_filter
|
||||||
sql = "(#{db_table}.#{db_field} <> '' AND CAST(CASE #{db_table}.#{db_field} WHEN '' THEN '0' ELSE #{db_table}.#{db_field} END AS decimal(30,3)) = #{value.first.to_i})"
|
sql = "(#{db_table}.#{db_field} <> '' AND CAST(CASE #{db_table}.#{db_field} WHEN '' THEN '0' ELSE #{db_table}.#{db_field} END AS decimal(30,3)) = #{value.first.to_i})"
|
||||||
|
@ -659,7 +661,7 @@ class Query < ActiveRecord::Base
|
||||||
sql << " AND #{db_table}.#{db_field} <> ''" if is_custom_filter
|
sql << " AND #{db_table}.#{db_field} <> ''" if is_custom_filter
|
||||||
when ">="
|
when ">="
|
||||||
if [:date, :date_past].include?(type_for(field))
|
if [:date, :date_past].include?(type_for(field))
|
||||||
sql = date_clause(db_table, db_field, (Date.parse(value.first) rescue nil), nil)
|
sql = date_clause(db_table, db_field, parse_date(value.first), nil)
|
||||||
else
|
else
|
||||||
if is_custom_filter
|
if is_custom_filter
|
||||||
sql = "(#{db_table}.#{db_field} <> '' AND CAST(CASE #{db_table}.#{db_field} WHEN '' THEN '0' ELSE #{db_table}.#{db_field} END AS decimal(30,3)) >= #{value.first.to_f})"
|
sql = "(#{db_table}.#{db_field} <> '' AND CAST(CASE #{db_table}.#{db_field} WHEN '' THEN '0' ELSE #{db_table}.#{db_field} END AS decimal(30,3)) >= #{value.first.to_f})"
|
||||||
|
@ -669,7 +671,7 @@ class Query < ActiveRecord::Base
|
||||||
end
|
end
|
||||||
when "<="
|
when "<="
|
||||||
if [:date, :date_past].include?(type_for(field))
|
if [:date, :date_past].include?(type_for(field))
|
||||||
sql = date_clause(db_table, db_field, nil, (Date.parse(value.first) rescue nil))
|
sql = date_clause(db_table, db_field, nil, parse_date(value.first))
|
||||||
else
|
else
|
||||||
if is_custom_filter
|
if is_custom_filter
|
||||||
sql = "(#{db_table}.#{db_field} <> '' AND CAST(CASE #{db_table}.#{db_field} WHEN '' THEN '0' ELSE #{db_table}.#{db_field} END AS decimal(30,3)) <= #{value.first.to_f})"
|
sql = "(#{db_table}.#{db_field} <> '' AND CAST(CASE #{db_table}.#{db_field} WHEN '' THEN '0' ELSE #{db_table}.#{db_field} END AS decimal(30,3)) <= #{value.first.to_f})"
|
||||||
|
@ -679,7 +681,7 @@ class Query < ActiveRecord::Base
|
||||||
end
|
end
|
||||||
when "><"
|
when "><"
|
||||||
if [:date, :date_past].include?(type_for(field))
|
if [:date, :date_past].include?(type_for(field))
|
||||||
sql = date_clause(db_table, db_field, (Date.parse(value[0]) rescue nil), (Date.parse(value[1]) rescue nil))
|
sql = date_clause(db_table, db_field, parse_date(value[0]), parse_date(value[1]))
|
||||||
else
|
else
|
||||||
if is_custom_filter
|
if is_custom_filter
|
||||||
sql = "(#{db_table}.#{db_field} <> '' AND CAST(CASE #{db_table}.#{db_field} WHEN '' THEN '0' ELSE #{db_table}.#{db_field} END AS decimal(30,3)) BETWEEN #{value[0].to_f} AND #{value[1].to_f})"
|
sql = "(#{db_table}.#{db_field} <> '' AND CAST(CASE #{db_table}.#{db_field} WHEN '' THEN '0' ELSE #{db_table}.#{db_field} END AS decimal(30,3)) BETWEEN #{value[0].to_f} AND #{value[1].to_f})"
|
||||||
|
@ -809,19 +811,23 @@ class Query < ActiveRecord::Base
|
||||||
def date_clause(table, field, from, to)
|
def date_clause(table, field, from, to)
|
||||||
s = []
|
s = []
|
||||||
if from
|
if from
|
||||||
from_yesterday = from - 1
|
if from.is_a?(Date)
|
||||||
from_yesterday_time = Time.local(from_yesterday.year, from_yesterday.month, from_yesterday.day)
|
from = Time.local(from.year, from.month, from.day).beginning_of_day
|
||||||
if self.class.default_timezone == :utc
|
|
||||||
from_yesterday_time = from_yesterday_time.utc
|
|
||||||
end
|
end
|
||||||
s << ("#{table}.#{field} > '%s'" % [connection.quoted_date(from_yesterday_time.end_of_day)])
|
if self.class.default_timezone == :utc
|
||||||
|
from = from.utc
|
||||||
|
end
|
||||||
|
from = from - 1
|
||||||
|
s << ("#{table}.#{field} > '%s'" % [connection.quoted_date(from)])
|
||||||
end
|
end
|
||||||
if to
|
if to
|
||||||
to_time = Time.local(to.year, to.month, to.day)
|
if to.is_a?(Date)
|
||||||
if self.class.default_timezone == :utc
|
to = Time.local(to.year, to.month, to.day).end_of_day
|
||||||
to_time = to_time.utc
|
|
||||||
end
|
end
|
||||||
s << ("#{table}.#{field} <= '%s'" % [connection.quoted_date(to_time.end_of_day)])
|
if self.class.default_timezone == :utc
|
||||||
|
to = to.utc
|
||||||
|
end
|
||||||
|
s << ("#{table}.#{field} <= '%s'" % [connection.quoted_date(to)])
|
||||||
end
|
end
|
||||||
s.join(' AND ')
|
s.join(' AND ')
|
||||||
end
|
end
|
||||||
|
@ -831,6 +837,15 @@ class Query < ActiveRecord::Base
|
||||||
date_clause(table, field, (days_from ? Date.today + days_from : nil), (days_to ? Date.today + days_to : nil))
|
date_clause(table, field, (days_from ? Date.today + days_from : nil), (days_to ? Date.today + days_to : nil))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Returns a Date or Time from the given filter value
|
||||||
|
def parse_date(arg)
|
||||||
|
if arg.to_s =~ /\A\d{4}-\d{2}-\d{2}T/
|
||||||
|
Time.parse(arg) rescue nil
|
||||||
|
else
|
||||||
|
Date.parse(arg) rescue nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# Additional joins required for the given sort options
|
# Additional joins required for the given sort options
|
||||||
def joins_for_order_statement(order_options)
|
def joins_for_order_statement(order_options)
|
||||||
joins = []
|
joins = []
|
||||||
|
|
|
@ -162,6 +162,29 @@ class Redmine::ApiTest::IssuesTest < Redmine::ApiTest::Base
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_index_should_allow_timestamp_filtering
|
||||||
|
Issue.delete_all
|
||||||
|
Issue.generate!(:subject => '1').update_column(:updated_on, Time.parse("2014-01-02T10:25:00Z"))
|
||||||
|
Issue.generate!(:subject => '2').update_column(:updated_on, Time.parse("2014-01-02T12:13:00Z"))
|
||||||
|
|
||||||
|
get '/issues.xml',
|
||||||
|
{:set_filter => 1, :f => ['updated_on'], :op => {:updated_on => '<='},
|
||||||
|
:v => {:updated_on => ['2014-01-02T12:00:00Z']}}
|
||||||
|
assert_select 'issues>issue', :count => 1
|
||||||
|
assert_select 'issues>issue>subject', :text => '1'
|
||||||
|
|
||||||
|
get '/issues.xml',
|
||||||
|
{:set_filter => 1, :f => ['updated_on'], :op => {:updated_on => '>='},
|
||||||
|
:v => {:updated_on => ['2014-01-02T12:00:00Z']}}
|
||||||
|
assert_select 'issues>issue', :count => 1
|
||||||
|
assert_select 'issues>issue>subject', :text => '2'
|
||||||
|
|
||||||
|
get '/issues.xml',
|
||||||
|
{:set_filter => 1, :f => ['updated_on'], :op => {:updated_on => '>='},
|
||||||
|
:v => {:updated_on => ['2014-01-02T08:00:00Z']}}
|
||||||
|
assert_select 'issues>issue', :count => 2
|
||||||
|
end
|
||||||
|
|
||||||
context "/index.json" do
|
context "/index.json" do
|
||||||
should_allow_api_authentication(:get, "/projects/private-child/issues.json")
|
should_allow_api_authentication(:get, "/projects/private-child/issues.json")
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue