1818using Neuroglia . AsyncApi . IO ;
1919using Neuroglia . AsyncApi . v3 ;
2020using Neuroglia . Data . Expressions ;
21+ using System . Threading ;
2122
2223namespace Synapse . Runner . Services . Executors ;
2324
@@ -94,6 +95,11 @@ public class AsyncApiCallExecutor(IServiceProvider serviceProvider, ILogger<Asyn
9495 /// </summary>
9596 protected uint ? Offset { get ; set ; }
9697
98+ /// <summary>
99+ /// Gets/sets a boolean indicating whether or not to keep consuming incoming messages
100+ /// </summary>
101+ protected bool KeepConsume { get ; set ; } = true ;
102+
97103 /// <summary>
98104 /// Gets the path for the specified message
99105 /// </summary>
@@ -234,7 +240,7 @@ protected virtual async Task DoExecuteSubscribeOperationAsync(CancellationToken
234240 if ( this . AsyncApi . Subscription == null ) throw new NullReferenceException ( "The 'subscription' must be set when performing an AsyncAPI v3 subscribe operation" ) ;
235241 await using var asyncApiClient = this . AsyncApiClientFactory . CreateFor ( this . Document ) ;
236242 var parameters = new AsyncApiSubscribeOperationParameters ( this . Operation . Key , this . AsyncApi . Server , this . AsyncApi . Protocol ) ;
237- await using var result = await asyncApiClient . SubscribeAsync ( parameters , cancellationToken ) . ConfigureAwait ( false ) ;
243+ var result = await asyncApiClient . SubscribeAsync ( parameters , cancellationToken ) . ConfigureAwait ( false ) ;
238244 if ( ! result . IsSuccessful ) throw new Exception ( "Failed to execute the AsyncAPI subscribe operation" ) ;
239245 if ( result . Messages == null )
240246 {
@@ -244,24 +250,24 @@ protected virtual async Task DoExecuteSubscribeOperationAsync(CancellationToken
244250 var observable = result . Messages ;
245251 if ( this . AsyncApi . Subscription . Consume . For != null ) observable = observable . TakeUntil ( Observable . Timer ( this . AsyncApi . Subscription . Consume . For . ToTimeSpan ( ) ) ) ;
246252 if ( this . AsyncApi . Subscription . Consume . Amount . HasValue ) observable = observable . Take ( this . AsyncApi . Subscription . Consume . Amount . Value ) ;
247- else if ( ! string . IsNullOrWhiteSpace ( this . AsyncApi . Subscription . Consume . While ) ) observable = observable . Select ( message => Observable . FromAsync ( async ( ) =>
248- {
249- var keepGoing = await this . Task . Workflow . Expressions . EvaluateConditionAsync ( this . AsyncApi . Subscription . Consume . While , this . Task . Input ! , this . GetExpressionEvaluationArguments ( ) , cancellationToken ) . ConfigureAwait ( false ) ;
250- return ( message , keepGoing ) ;
251- } ) ) . Concat ( ) . TakeWhile ( i => i . keepGoing ) . Select ( i => i . message ) ;
252- else if ( ! string . IsNullOrWhiteSpace ( this . AsyncApi . Subscription . Consume . Until ) ) observable = observable . Select ( message => Observable . FromAsync ( async ( ) =>
253- {
254- var keepGoing = ! ( await this . Task . Workflow . Expressions . EvaluateConditionAsync ( this . AsyncApi . Subscription . Consume . Until , this . Task . Input ! , this . GetExpressionEvaluationArguments ( ) , cancellationToken ) . ConfigureAwait ( false ) ) ;
255- return ( message , keepGoing ) ;
256- } ) ) . Concat ( ) . TakeWhile ( i => i . keepGoing ) . Select ( i => i . message ) ;
257253 if ( this . AsyncApi . Subscription . Foreach == null )
258254 {
259- var messages = await observable . ToAsyncEnumerable ( ) . ToListAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
255+ var messages = await observable . ToAsyncEnumerable ( ) . TakeWhileAwait ( async m =>
256+ {
257+ if ( ! string . IsNullOrWhiteSpace ( this . AsyncApi . Subscription . Consume . While ) ) return await this . Task . Workflow . Expressions . EvaluateConditionAsync ( this . AsyncApi . Subscription . Consume . While , this . Task . Input ! , this . GetExpressionEvaluationArguments ( ) , cancellationToken ) . ConfigureAwait ( false ) ;
258+ if ( ! string . IsNullOrWhiteSpace ( this . AsyncApi . Subscription . Consume . Until ) ) return ! await this . Task . Workflow . Expressions . EvaluateConditionAsync ( this . AsyncApi . Subscription . Consume . Until , this . Task . Input ! , this . GetExpressionEvaluationArguments ( ) , cancellationToken ) . ConfigureAwait ( false ) ;
259+ return true ;
260+ } ) . ToListAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
260261 await this . SetResultAsync ( messages , this . Task . Definition . Then , cancellationToken ) . ConfigureAwait ( false ) ;
261262 }
262263 else
263264 {
264- this . Subscription = observable . SubscribeAsync ( OnStreamingMessageAsync , OnStreamingErrorAsync , OnStreamingCompletedAsync ) ;
265+ //todo: fix
266+ this . Subscription = observable . TakeWhile ( _ => this . KeepConsume ) . SelectMany ( m =>
267+ {
268+ OnStreamingMessageAsync ( m ) . GetAwaiter ( ) . GetResult ( ) ;
269+ return Observable . Return ( m ) ;
270+ } ) . SubscribeAsync ( _ => System . Threading . Tasks . Task . CompletedTask , OnStreamingErrorAsync , OnStreamingCompletedAsync ) ;
265271 }
266272 }
267273
@@ -274,6 +280,11 @@ protected virtual async Task OnStreamingMessageAsync(IAsyncApiMessage message)
274280 {
275281 if ( this . AsyncApi == null || this . Document == null || this . Operation . Value == null ) throw new InvalidOperationException ( "The executor must be initialized before execution" ) ;
276282 if ( this . AsyncApi . Subscription == null ) throw new NullReferenceException ( "The 'subscription' must be set when performing an AsyncAPI v3 subscribe operation" ) ;
283+ if ( ! string . IsNullOrWhiteSpace ( this . AsyncApi . Subscription . Consume . While ) && ! await this . Task . Workflow . Expressions . EvaluateConditionAsync ( this . AsyncApi . Subscription . Consume . While , this . Task . Input ! , this . GetExpressionEvaluationArguments ( ) , this . CancellationTokenSource ! . Token ) . ConfigureAwait ( false ) )
284+ {
285+ this . KeepConsume = false ;
286+ return ;
287+ }
277288 if ( this . AsyncApi . Subscription . Foreach ? . Do != null )
278289 {
279290 var taskDefinition = new DoTaskDefinition ( )
@@ -284,30 +295,36 @@ protected virtual async Task OnStreamingMessageAsync(IAsyncApiMessage message)
284295 new ( SynapseDefaults . Tasks . Metadata . PathPrefix . Name , false )
285296 ]
286297 } ;
287- var arguments = this . GetExpressionEvaluationArguments ( ) ;
288298 var messageData = message as object ;
299+ var offset = this . Offset ?? 0 ;
300+ if ( ! this . Offset . HasValue ) this . Offset = 0 ;
301+ var arguments = this . GetExpressionEvaluationArguments ( ) ;
302+ arguments ??= new Dictionary < string , object > ( ) ;
303+ arguments [ this . AsyncApi . Subscription . Foreach . Item ?? RuntimeExpressions . Arguments . Each ] = messageData ! ;
304+ arguments [ this . AsyncApi . Subscription . Foreach . At ?? RuntimeExpressions . Arguments . Index ] = offset ;
289305 if ( this . AsyncApi . Subscription . Foreach . Output ? . As is string fromExpression ) messageData = await this . Task . Workflow . Expressions . EvaluateAsync < object > ( fromExpression , messageData ?? new ( ) , arguments , this . CancellationTokenSource ! . Token ) . ConfigureAwait ( false ) ;
290- else if ( this . AsyncApi . Subscription . Foreach . Output ? . As != null ) messageData = await this . Task . Workflow . Expressions . EvaluateAsync < object > ( this . AsyncApi . Subscription . Foreach . Output . As , messageData ?? new ( ) , arguments , this . CancellationTokenSource ! . Token ) . ConfigureAwait ( false ) ;
306+ else if ( this . AsyncApi . Subscription . Foreach . Output ? . As != null ) messageData = await this . Task . Workflow . Expressions . EvaluateAsync < object > ( this . AsyncApi . Subscription . Foreach . Output . As ! , messageData ?? new ( ) , arguments , this . CancellationTokenSource ! . Token ) . ConfigureAwait ( false ) ;
291307 if ( this . AsyncApi . Subscription . Foreach . Export ? . As is string toExpression )
292308 {
293309 var context = ( await this . Task . Workflow . Expressions . EvaluateAsync < IDictionary < string , object > > ( toExpression , messageData ?? new ( ) , arguments , this . CancellationTokenSource ! . Token ) . ConfigureAwait ( false ) ) ! ;
294310 await this . Task . SetContextDataAsync ( context , this . CancellationTokenSource ! . Token ) . ConfigureAwait ( false ) ;
295311 }
296312 else if ( this . AsyncApi . Subscription . Foreach . Export ? . As != null )
297313 {
298- var context = ( await this . Task . Workflow . Expressions . EvaluateAsync < IDictionary < string , object > > ( this . AsyncApi . Subscription . Foreach . Export . As , messageData ?? new ( ) , arguments , this . CancellationTokenSource ! . Token ) . ConfigureAwait ( false ) ) ! ;
314+ var context = ( await this . Task . Workflow . Expressions . EvaluateAsync < IDictionary < string , object > > ( this . AsyncApi . Subscription . Foreach . Export . As ! , messageData ?? new ( ) , arguments , this . CancellationTokenSource ! . Token ) . ConfigureAwait ( false ) ) ! ;
299315 await this . Task . SetContextDataAsync ( context , this . CancellationTokenSource ! . Token ) . ConfigureAwait ( false ) ;
300316 }
301- var offset = this . Offset ?? 0 ;
302- if ( ! this . Offset . HasValue ) this . Offset = 0 ;
303- arguments ??= new Dictionary < string , object > ( ) ;
304- arguments [ this . AsyncApi . Subscription . Foreach . Item ?? RuntimeExpressions . Arguments . Each ] = messageData ! ;
305- arguments [ this . AsyncApi . Subscription . Foreach . At ?? RuntimeExpressions . Arguments . Index ] = offset ;
306317 var task = await this . Task . Workflow . CreateTaskAsync ( taskDefinition , this . GetPathFor ( offset ) , this . Task . Input , null , this . Task , false , this . CancellationTokenSource ! . Token ) . ConfigureAwait ( false ) ;
307318 var taskExecutor = await this . CreateTaskExecutorAsync ( task , taskDefinition , this . Task . ContextData , arguments , this . CancellationTokenSource ! . Token ) . ConfigureAwait ( false ) ;
308319 await taskExecutor . ExecuteAsync ( this . CancellationTokenSource ! . Token ) . ConfigureAwait ( false ) ;
320+ if ( this . Task . ContextData != taskExecutor . Task . ContextData ) await this . Task . SetContextDataAsync ( taskExecutor . Task . ContextData , this . CancellationTokenSource ! . Token ) . ConfigureAwait ( false ) ;
309321 this . Offset ++ ;
310322 }
323+ if ( ! string . IsNullOrWhiteSpace ( this . AsyncApi . Subscription . Consume . Until ) && await this . Task . Workflow . Expressions . EvaluateConditionAsync ( this . AsyncApi . Subscription . Consume . Until , this . Task . Input ! , this . GetExpressionEvaluationArguments ( ) , this . CancellationTokenSource ! . Token ) . ConfigureAwait ( false ) )
324+ {
325+ this . KeepConsume = false ;
326+ return ;
327+ }
311328 }
312329
313330 /// <summary>
0 commit comments