11import contextlib
22import functools
3+ import itertools
34import logging
45import sys
56import warnings
2829from corva .models .merge .raw import RawPartialRerunMergeEvent
2930from corva .models .scheduled .raw import RawScheduledEvent
3031from corva .models .scheduled .scheduled import ScheduledEvent , ScheduledNaturalTimeEvent
32+ from corva .models .scheduled .scheduler_type import SchedulerType
3133from corva .models .stream .raw import RawStreamEvent
3234from corva .models .stream .stream import StreamEvent
3335from corva .models .task import RawTaskEvent , TaskEvent , TaskStatus
@@ -58,6 +60,7 @@ def base_handler(
5860 func : Callable ,
5961 raw_event_type : Type [RawBaseEvent ],
6062 handler : Optional [logging .Handler ],
63+ merge_events : bool = False ,
6164) -> Callable [[Any , Any ], List [Any ]]:
6265 @functools .wraps (func )
6366 def wrapper (aws_event : Any , aws_context : Any ) -> List [Any ]:
@@ -84,6 +87,8 @@ def wrapper(aws_event: Any, aws_context: Any) -> List[Any]:
8487 url = SETTINGS .CACHE_URL , decode_responses = True , max_connections = 1
8588 )
8689 data_transformation_type = raw_custom_event_type or raw_event_type
90+ if merge_events :
91+ aws_event = _merge_events (aws_event , data_transformation_type )
8792 raw_events = data_transformation_type .from_raw_event (event = aws_event )
8893
8994 if (
@@ -120,18 +125,26 @@ def stream(
120125 func : Optional [Callable [[StreamEventT , Api , UserRedisSdk ], Any ]] = None ,
121126 * ,
122127 handler : Optional [logging .Handler ] = None ,
128+ merge_events : bool = False ,
123129) -> Callable :
124130 """Runs stream app.
125131
126132 Arguments:
127133 handler: logging handler to include in Corva logger.
134+ merge_events: if True - merge all incoming events into one before
135+ passing them to func
128136 """
129137
130138 if func is None :
131- return functools .partial (stream , handler = handler )
139+ return functools .partial (stream , handler = handler , merge_events = merge_events )
132140
133141 @functools .wraps (func )
134- @functools .partial (base_handler , raw_event_type = RawStreamEvent , handler = handler )
142+ @functools .partial (
143+ base_handler ,
144+ raw_event_type = RawStreamEvent ,
145+ handler = handler ,
146+ merge_events = merge_events ,
147+ )
135148 def wrapper (
136149 event : RawStreamEvent ,
137150 api_key : str ,
@@ -221,18 +234,26 @@ def scheduled(
221234 func : Optional [Callable [[ScheduledEventT , Api , UserRedisSdk ], Any ]] = None ,
222235 * ,
223236 handler : Optional [logging .Handler ] = None ,
237+ merge_events : bool = False ,
224238) -> Callable :
225239 """Runs scheduled app.
226240
227241 Arguments:
228242 handler: logging handler to include in Corva logger.
243+ merge_events: if True - merge all incoming events into one before
244+ passing them to func
229245 """
230246
231247 if func is None :
232- return functools .partial (scheduled , handler = handler )
248+ return functools .partial (scheduled , handler = handler , merge_events = merge_events )
233249
234250 @functools .wraps (func )
235- @functools .partial (base_handler , raw_event_type = RawScheduledEvent , handler = handler )
251+ @functools .partial (
252+ base_handler ,
253+ raw_event_type = RawScheduledEvent ,
254+ handler = handler ,
255+ merge_events = merge_events ,
256+ )
236257 def wrapper (
237258 event : RawScheduledEvent ,
238259 api_key : str ,
@@ -441,7 +462,6 @@ def wrapper(
441462 logging_ctx : LoggingContext ,
442463 redis_client : redis .Redis ,
443464 ) -> Any :
444-
445465 logging_ctx .asset_id = event .data .asset_id
446466 logging_ctx .app_connection_id = event .data .app_connection_id
447467
@@ -544,3 +564,51 @@ def _get_custom_event_type_by_raw_aws_event(
544564 if events :
545565 return event_type , handler
546566 return None , None
567+
568+
569+ def _merge_events (
570+ aws_event : Any ,
571+ data_transformation_type : Type [RawBaseEvent ],
572+ ) -> Any :
573+ """
574+ Merges incoming aws_events into one.
575+ Merge happens differently, depending on app type.
576+ Only "scheduled" and "stream" type of apps can be processed here.
577+ If somehow any other type is passed - raise an exception
578+ """
579+ if data_transformation_type is RawScheduledEvent :
580+ # scheduled event
581+ if not isinstance (aws_event [0 ], dict ):
582+ aws_event = list (itertools .chain (* aws_event ))
583+ scheduler_type = aws_event [0 ]["scheduler_type" ]
584+ if isinstance (scheduler_type , SchedulerType ):
585+ scheduler_type = scheduler_type .value
586+ is_depth = scheduler_type == SchedulerType .data_depth_milestone .value
587+ event_start , event_end = (
588+ ("top_depth" , "bottom_depth" )
589+ if is_depth
590+ else ("schedule_start" , "schedule_end" )
591+ )
592+ min_event_start = min (e [event_start ] for e in aws_event )
593+ max_event_end = max (
594+ (e [event_end ] for e in aws_event if e .get (event_end ) is not None ),
595+ default = None ,
596+ )
597+ aws_event [0 ][event_start ] = min_event_start
598+ if max_event_end :
599+ aws_event [0 ][event_end ] = max_event_end
600+ aws_event = aws_event [0 ]
601+
602+ elif data_transformation_type is RawStreamEvent :
603+ # stream event
604+ for event in aws_event [1 :]:
605+ aws_event [0 ]["records" ].extend (event ["records" ])
606+ aws_event = [aws_event [0 ]]
607+
608+ else :
609+ CORVA_LOGGER .warning (
610+ f"{ data_transformation_type .__name__ } does not support `merge event` "
611+ "parameter."
612+ )
613+
614+ return aws_event
0 commit comments