/// <inheritdoc/> public virtual async Task CorrelateAsync(V1CorrelationContext context, CancellationToken cancellationToken = default) { if (context == null) { throw new ArgumentNullException(nameof(context)); } await this.SignalStream.WriteAsync(new(V1RuntimeSignalType.Correlate, Dynamic.FromObject(context)), cancellationToken); }
/// <summary> /// Performs a correlation on a <see cref="V1Event"/>, in a given <see cref="V1CorrelationContext"/> and using the specified <see cref="V1EventFilter"/> /// </summary> /// <param name="correlation">The <see cref="V1Correlation"/> to perform</param> /// <param name="correlationContext">The <see cref="V1CorrelationContext"/> in which to perform the <see cref="V1Correlation"/></param> /// <param name="e">The <see cref="V1Event"/> to correlate</param> /// <param name="filter">The <see cref="V1EventFilter"/> used to correlate the <see cref="V1Event"/></param> /// <param name="cancellationToken">A <see cref="CancellationToken"/></param> /// <returns>A new awaitable <see cref="Task"/></returns> protected virtual async Task CorrelateAsync(V1Correlation correlation, V1CorrelationContext correlationContext, V1Event e, V1EventFilter filter, CancellationToken cancellationToken = default) { this.Logger.LogInformation("Correlating event to context with id '{contextId}'...", correlationContext.Id); correlationContext.Correlate(e, filter.CorrelationMappings.Keys, true); correlation = await this.Correlations.UpdateAsync(correlation, cancellationToken); await this.Correlations.SaveChangesAsync(cancellationToken); this.Logger.LogInformation("Event successfully correlated to context with id '{contextId}'", correlationContext.Id); this.Logger.LogInformation("Attempting to complete the correlation with id '{correlationId}' in context with id '{contextId}'...", correlation.Id, correlationContext.Id); if (!correlation.TryComplete(correlationContext)) { this.Logger.LogInformation("Correlations conditions are not met in the specified correlation context"); return; } this.Logger.LogInformation("Correlation with id '{correlationId}' has been completed in context with id '{contextId}. Computing outcome...", correlation.Id, correlationContext.Id); switch (correlation.Outcome.Type) { case V1CorrelationOutcomeType.Start: await this.Mediator.ExecuteAndUnwrapAsync(new V1CreateWorkflowInstanceCommand(correlation.Outcome.Target, V1WorkflowInstanceActivationType.Trigger, new(), correlationContext, true, null), cancellationToken); break; case V1CorrelationOutcomeType.Correlate: await this.Mediator.ExecuteAndUnwrapAsync(new V1CorrelateWorkflowInstanceCommand(correlation.Outcome.Target, correlationContext), cancellationToken); break; default: throw new NotSupportedException($"The specified {nameof(V1CorrelationOutcomeType)} '{correlation.Outcome.Type}' is not supported"); } correlation.ReleaseContext(correlationContext); correlation = await this.Correlations.UpdateAsync(correlation, cancellationToken); await this.Correlations.SaveChangesAsync(cancellationToken); if (correlation.Lifetime == V1CorrelationLifetime.Singleton) { this.Logger.LogInformation("The correlation with id '{correlationId}' is a singleton and its context has been released. Disposing of it...", correlation.Id); await this.Mediator.ExecuteAndUnwrapAsync(new V1DeleteCorrelationCommand(correlation.Id), cancellationToken); this.Logger.LogInformation("The correlation with id '{correlationId}' has been successfully disposed of", correlation.Id); } this.Logger.LogInformation("Correlation outcome successfully computed"); }
/// <inheritdoc/> public virtual async Task <IOperationResult> HandleAsync(V1CorrelateEventCommand command, CancellationToken cancellationToken = default) { this.Logger.LogInformation("Processing event with id '{eventId}', type '{eventType}' and source '{eventSource}'...", command.Event.Id, command.Event.Type, command.Event.Source); var correlations = await this.Mediator.ExecuteAndUnwrapAsync(new V1GetEventCorrelationsQuery(command.Event), cancellationToken); await foreach (var correlation in correlations) { this.Logger.LogInformation("Processing correlation with id '{correlationId}'...", correlation.Id); var matchingCondition = correlation.GetMatchingConditionFor(command.Event) !; var matchingFilter = matchingCondition.GetMatchingFilterFor(command.Event) !; var matchingContexts = correlation.Contexts .Where(c => c.CorrelatesTo(command.Event)) .ToList(); switch (correlation.Lifetime) { case V1CorrelationLifetime.Singleton: var matchingContext = matchingContexts.FirstOrDefault(); if (matchingContext == null) { this.Logger.LogInformation("Failed to find a matching correlation context"); if (correlation.Contexts.Any()) { throw new Exception("Failed to correlate event"); //should not happen } this.Logger.LogInformation("Creating a new correlation context..."); matchingContext = V1CorrelationContext.CreateFor(command.Event, matchingFilter.CorrelationMappings.Keys); correlation.AddContext(matchingContext); await this.Correlations.UpdateAsync(correlation, cancellationToken); await this.Correlations.SaveChangesAsync(cancellationToken); this.Logger.LogInformation("Correlation context with id '{contextId}' successfully created", matchingContext.Id); this.Logger.LogInformation("Event successfully correlated to context with id '{contextId}'", matchingContext.Id); } else { await this.CorrelateAsync(correlation, matchingContext, command.Event, matchingFilter, cancellationToken); } break; case V1CorrelationLifetime.Transient: matchingContext = matchingContexts.FirstOrDefault(); if (matchingContext == null) { this.Logger.LogInformation("Failed to find a matching correlation context"); this.Logger.LogInformation("Creating a new correlation context..."); matchingContext = V1CorrelationContext.CreateFor(command.Event, matchingFilter.CorrelationMappings.Keys); correlation.AddContext(matchingContext); await this.Correlations.UpdateAsync(correlation, cancellationToken); await this.Correlations.SaveChangesAsync(cancellationToken); this.Logger.LogInformation("Correlation context with id '{contextId}' successfully created", matchingContext.Id); this.Logger.LogInformation("Event successfully correlated to context with id '{contextId}'", matchingContext.Id); matchingContexts.Add(matchingContext); } this.Logger.LogInformation("Found {matchingContextCount} matching correlation contexts", matchingContexts.Count()); foreach (var context in matchingContexts) { await this.CorrelateAsync(correlation, context, command.Event, matchingFilter, cancellationToken); } break; default: throw new NotSupportedException($"The specified {nameof(V1CorrelationLifetime)} '{correlation.Lifetime}' is not supported"); } } return(this.Ok()); }
/// <summary> /// Initializes a new <see cref="V1CorrelateWorkflowInstanceCommand"/> /// </summary> /// <param name="id">The id of the <see cref="V1WorkflowInstance"/> to correlate</param> /// <param name="correlationContext">The <see cref="V1CorrelationContext"/> to correlate the <see cref="V1WorkflowInstance"/> with</param> public V1CorrelateWorkflowInstanceCommand(string id, V1CorrelationContext correlationContext) { this.Id = id; this.CorrelationContext = correlationContext; }