@@ -52,6 +52,7 @@ pub fn build_graph_buckets(
5252 range : & DateRange ,
5353 interval : GraphInterval ,
5454 timezone : Option < & str > ,
55+ max_datapoints : usize ,
5556) -> Result < Vec < DateRange > > {
5657 if range. start >= range. end {
5758 return Ok ( Vec :: new ( ) ) ;
@@ -71,9 +72,31 @@ pub fn build_graph_buckets(
7172 GraphInterval :: Day => resolve_local_day_start ( timezone, range. start . with_timezone ( & timezone) . date_naive ( ) ) ?,
7273 } ;
7374
74- let mut buckets = Vec :: new ( ) ;
75+ let mut bucket_count = 0 ;
7576 let mut bucket_start = aligned_start;
7677
78+ while bucket_start < range. end {
79+ let next_bucket_start = match interval {
80+ GraphInterval :: Hour => bucket_start + Duration :: hours ( 1 ) ,
81+ GraphInterval :: Day => {
82+ let next_date = bucket_start
83+ . with_timezone ( & timezone)
84+ . date_naive ( )
85+ . checked_add_days ( Days :: new ( 1 ) )
86+ . context ( "Failed to advance bucket date" ) ?;
87+ resolve_local_day_start ( timezone, next_date) ?
88+ }
89+ } ;
90+
91+ bucket_count += 1 ;
92+ if bucket_count > max_datapoints {
93+ anyhow:: bail!( "Too many data points" ) ;
94+ }
95+ bucket_start = next_bucket_start;
96+ }
97+
98+ let mut buckets = Vec :: with_capacity ( bucket_count) ;
99+ bucket_start = aligned_start;
77100 while bucket_start < range. end {
78101 let next_bucket_start = match interval {
79102 GraphInterval :: Hour => bucket_start + Duration :: hours ( 1 ) ,
@@ -248,7 +271,7 @@ mod tests {
248271 } ;
249272
250273 let buckets =
251- build_graph_buckets ( & range, GraphInterval :: Hour , Some ( timezone) ) . expect ( "failed to build buckets" ) ;
274+ build_graph_buckets ( & range, GraphInterval :: Hour , Some ( timezone) , 2000 ) . expect ( "failed to build buckets" ) ;
252275 let local_starts = buckets
253276 . iter ( )
254277 . map ( |bucket| bucket. start . with_timezone ( & Tz :: Asia__Kolkata ) . format ( "%Y-%m-%d %H:%M" ) . to_string ( ) )
@@ -266,7 +289,8 @@ mod tests {
266289 end : local_datetime ( Tz :: America__New_York , 2024 , 1 , 3 , 12 , 0 ) ,
267290 } ;
268291
269- let buckets = build_graph_buckets ( & range, GraphInterval :: Day , Some ( timezone) ) . expect ( "failed to build buckets" ) ;
292+ let buckets =
293+ build_graph_buckets ( & range, GraphInterval :: Day , Some ( timezone) , 2000 ) . expect ( "failed to build buckets" ) ;
270294 let local_starts = buckets
271295 . iter ( )
272296 . map ( |bucket| bucket. start . with_timezone ( & Tz :: America__New_York ) . format ( "%Y-%m-%d %H:%M" ) . to_string ( ) )
@@ -299,7 +323,8 @@ mod tests {
299323 . expect ( "failed to append events" ) ;
300324
301325 let range = DateRange { start, end } ;
302- let buckets = build_graph_buckets ( & range, GraphInterval :: Day , Some ( "UTC" ) ) . expect ( "failed to build buckets" ) ;
326+ let buckets =
327+ build_graph_buckets ( & range, GraphInterval :: Day , Some ( "UTC" ) , 2000 ) . expect ( "failed to build buckets" ) ;
303328 let conn = app. events_conn ( ) . expect ( "failed to get events conn" ) ;
304329 let report =
305330 overall_report ( & conn, & [ "entity-1" . to_string ( ) ] , "pageview" , & range, & buckets, & [ ] , & Metric :: Views )
@@ -308,4 +333,13 @@ mod tests {
308333 let values = report. iter ( ) . map ( |point| point. value ) . collect :: < Vec < _ > > ( ) ;
309334 assert_eq ! ( values, vec![ 1.0 , 1.0 ] ) ;
310335 }
336+
337+ #[ test]
338+ fn build_graph_buckets_rejects_excessive_ranges ( ) {
339+ let start = local_datetime ( Tz :: UTC , 2024 , 1 , 1 , 0 , 0 ) ;
340+ let max_datapoints = 2000 ;
341+ let range = DateRange { start, end : start + Duration :: hours ( max_datapoints as i64 + 1 ) } ;
342+
343+ assert ! ( build_graph_buckets( & range, GraphInterval :: Hour , Some ( "UTC" ) , max_datapoints) . is_err( ) ) ;
344+ }
311345}
0 commit comments