@@ -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 ) {
@@ -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,51 @@ 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+ if ( req . event . endsWith ( EVENT_QUEUE_ACTIONS . SAGA_SUCCESS ) || req . event . endsWith ( EVENT_QUEUE_ACTIONS . SAGA_FAILED ) ) {
384+ return ;
385+ }
386+
387+ // NOTE: required for #failed because tx is rolledback and new events would not be commmited!
388+ const tx = cds . tx ( processContext ) ;
389+ const nextEvents = tx . _eventQueue ?. events ;
390+
391+ if ( nextEvents ?. length ) {
392+ tx . _eventQueue . events = [ ] ;
393+ }
394+
395+ for ( const [ , result ] of statusTuple ) {
396+ if ( succeeded && result . status === EventProcessingStatus . Done ) {
397+ await this . __srv . tx ( processContext ) . send ( succeeded , result . nextData ?? req . data ) ;
398+ }
399+
400+ if ( failed && result . status === EventProcessingStatus . Error ) {
401+ await this . __srv . tx ( processContext ) . send ( failed , result . nextData ?? req . data ) ;
402+ }
403+
404+ delete result . nextData ;
405+ }
406+
407+ if ( config . insertEventsBeforeCommit ) {
408+ this . nextSagaEvents = tx . _eventQueue . events ;
409+ } else {
410+ this . nextSagaEvents = tx . _eventQueue . events . filter ( ( event ) => JSON . parse ( event . payload ) . event === failed ) ;
411+ }
412+ tx . _eventQueue . events = nextEvents ?? [ ] ;
413+ }
414+
362415 #determineResultStatus( result , queueEntries ) {
363416 const validStatusValues = Object . values ( EventProcessingStatus ) ;
364417 const validStatus = validStatusValues . includes ( result ) ;
365418 if ( validStatus ) {
366- return queueEntries . map ( ( queueEntry ) => [ queueEntry . ID , result ] ) ;
419+ return queueEntries . map ( ( queueEntry ) => [ queueEntry . ID , { status : result } ] ) ;
367420 }
368421
369422 if ( result instanceof Object && ! Array . isArray ( result ) ) {
@@ -375,7 +428,7 @@ class EventQueueGenericOutboxHandler extends EventQueueBaseClass {
375428 }
376429
377430 if ( ! Array . isArray ( result ) ) {
378- return queueEntries . map ( ( queueEntry ) => [ queueEntry . ID , EventProcessingStatus . Done ] ) ;
431+ return queueEntries . map ( ( queueEntry ) => [ queueEntry . ID , { status : EventProcessingStatus . Done } ] ) ;
379432 }
380433
381434 const [ firstEntry ] = result ;
@@ -430,7 +483,7 @@ class EventQueueGenericOutboxHandler extends EventQueueBaseClass {
430483 if ( valid ) {
431484 return result ;
432485 } else {
433- return queueEntries . map ( ( queueEntry ) => [ queueEntry . ID , EventProcessingStatus . Done ] ) ;
486+ return queueEntries . map ( ( queueEntry ) => [ queueEntry . ID , { status : EventProcessingStatus . Done } ] ) ;
434487 }
435488 }
436489}
0 commit comments