@@ -14,6 +14,8 @@ const EVENT_QUEUE_ACTIONS = {
1414 EXCEEDED : "eventQueueRetriesExceeded" ,
1515 CLUSTER : "eventQueueCluster" ,
1616 CHECK_AND_ADJUST : "eventQueueCheckAndAdjustPayload" ,
17+ SAGA_SUCCESS : "#succeeded" ,
18+ SAGA_FAILED : "#failed" ,
1719} ;
1820
1921class EventQueueGenericOutboxHandler extends EventQueueBaseClass {
@@ -260,7 +262,7 @@ class EventQueueGenericOutboxHandler extends EventQueueBaseClass {
260262 async checkEventAndGeneratePayload ( queueEntry ) {
261263 const payload = await super . checkEventAndGeneratePayload ( queueEntry ) ;
262264 const { event } = payload ;
263- const handlerName = this . #checkHandlerExists( EVENT_QUEUE_ACTIONS . CHECK_AND_ADJUST , event ) ;
265+ const handlerName = this . #checkHandlerExists( { eventQueueFn : EVENT_QUEUE_ACTIONS . CHECK_AND_ADJUST , event } ) ;
264266 if ( ! handlerName ) {
265267 return payload ;
266268 }
@@ -281,7 +283,7 @@ class EventQueueGenericOutboxHandler extends EventQueueBaseClass {
281283
282284 async hookForExceededEvents ( exceededEvent ) {
283285 const { event } = exceededEvent . payload ;
284- const handlerName = this . #checkHandlerExists( EVENT_QUEUE_ACTIONS . EXCEEDED , event ) ;
286+ const handlerName = this . #checkHandlerExists( { eventQueueFn : EVENT_QUEUE_ACTIONS . EXCEEDED , event } ) ;
285287 if ( ! handlerName ) {
286288 return await super . hookForExceededEvents ( exceededEvent ) ;
287289 }
@@ -299,14 +301,23 @@ class EventQueueGenericOutboxHandler extends EventQueueBaseClass {
299301 return await super . beforeProcessingEvents ( ) ;
300302 }
301303
302- #checkHandlerExists( eventQueueFn , event ) {
303- const specificHandler = this . __onHandlers [ [ eventQueueFn , event ] . join ( "." ) ] ;
304+ #checkHandlerExists( { eventQueueFn, event, saga } = { } ) {
305+ if ( eventQueueFn ) {
306+ const specificHandler = this . __onHandlers [ [ eventQueueFn , event ] . join ( "." ) ] ;
307+ if ( specificHandler ) {
308+ return specificHandler ;
309+ }
310+
311+ const genericHandler = this . __onHandlers [ eventQueueFn ] ;
312+ return genericHandler ?? null ;
313+ }
314+
315+ const specificHandler = this . __onHandlers [ [ event , saga ] . join ( "/" ) ] ;
304316 if ( specificHandler ) {
305317 return specificHandler ;
306318 }
307319
308- const genericHandler = this . __onHandlers [ eventQueueFn ] ;
309- return genericHandler ?? null ;
320+ return this . __onHandlers [ saga ] ;
310321 }
311322
312323 async processPeriodicEvent ( processContext , key , queueEntry ) {
@@ -319,7 +330,7 @@ class EventQueueGenericOutboxHandler extends EventQueueBaseClass {
319330 #buildDispatchData( context , payload , { key, queueEntries } = { } ) {
320331 const { useEventQueueUser } = this . eventConfig ;
321332 const userId = useEventQueueUser ? config . userId : payload . contextUser ;
322- const reg = payload . _fromSend ? new cds . Request ( payload ) : new cds . Event ( payload ) ;
333+ const reg = payload . das_fromSend ? new cds . Request ( payload ) : new cds . Event ( payload ) ;
323334 const invocationFn = payload . _fromSend ? "send" : "emit" ;
324335 delete reg . _fromSend ;
325336 delete reg . contextUser ;
@@ -344,7 +355,9 @@ class EventQueueGenericOutboxHandler extends EventQueueBaseClass {
344355 const { userId, invocationFn, reg } = this . #buildDispatchData( processContext , payload , { key, queueEntries } ) ;
345356 await this . #setContextUser( processContext , userId , reg ) ;
346357 const result = await this . __srvUnboxed . tx ( processContext ) [ invocationFn ] ( reg ) ;
347- return this . #determineResultStatus( result , queueEntries ) ;
358+ const statusTuple = this . #determineResultStatus( result , queueEntries ) ;
359+ await this . #publishFollowupEvents( processContext , reg , statusTuple ) ;
360+ return statusTuple ;
348361 } catch ( err ) {
349362 this . logger . error ( "error processing outboxed service call" , err , {
350363 serviceName : this . eventSubType ,
@@ -359,11 +372,41 @@ class EventQueueGenericOutboxHandler extends EventQueueBaseClass {
359372 }
360373 }
361374
375+ async #publishFollowupEvents( processContext , req , statusTuple ) {
376+ const succeeded = this . #checkHandlerExists( { event : req . event , saga : EVENT_QUEUE_ACTIONS . SAGA_SUCCESS } ) ;
377+ const failed = this . #checkHandlerExists( { event : req . event , saga : EVENT_QUEUE_ACTIONS . SAGA_FAILED } ) ;
378+
379+ if ( ! succeeded && ! failed ) {
380+ return ;
381+ }
382+
383+ // NOTE: required for #failed because tx is rolledback and new events would not be commmited!
384+ const tx = cds . tx ( processContext ) ;
385+ const nextEvents = tx . _eventQueue ?. events ;
386+
387+ if ( nextEvents ?. length ) {
388+ tx . _eventQueue . events = [ ] ;
389+ }
390+
391+ for ( const [ id , result ] of statusTuple ) {
392+ if ( succeeded && result . status === EventProcessingStatus . Done ) {
393+ ( await this . __srv . tx ( processContext ) ) . send ( succeeded , result . nextData ?? req . data ) ;
394+ }
395+
396+ if ( failed && result . status === EventProcessingStatus . Error ) {
397+ ( await this . __srv . tx ( processContext ) ) . send ( failed , result . nextData ?? req . data ) ;
398+ }
399+ }
400+
401+ this . nextSagaEvents = tx . _eventQueue . events ;
402+ tx . _eventQueue . events = nextEvents ?? [ ] ;
403+ }
404+
362405 #determineResultStatus( result , queueEntries ) {
363406 const validStatusValues = Object . values ( EventProcessingStatus ) ;
364407 const validStatus = validStatusValues . includes ( result ) ;
365408 if ( validStatus ) {
366- return queueEntries . map ( ( queueEntry ) => [ queueEntry . ID , result ] ) ;
409+ return queueEntries . map ( ( queueEntry ) => [ queueEntry . ID , { status : result } ] ) ;
367410 }
368411
369412 if ( result instanceof Object && ! Array . isArray ( result ) ) {
0 commit comments