/// <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 <Integration.Models.V1Workflow> > HandleAsync(V1CreateWorkflowCommand command, CancellationToken cancellationToken = default) { var validationResult = await this.WorkflowValidator.ValidateAsync(command.Definition, true, true, cancellationToken); if (!validationResult.IsValid) { return(this.Invalid(validationResult.AsErrors().ToArray())); } foreach (var subflowRef in command.Definition.GetSubflowReferences()) { var reference = subflowRef.WorkflowId; if (!string.IsNullOrWhiteSpace(subflowRef.Version)) { reference += $":{subflowRef.Version}"; } var subflow = await this.Mediator.ExecuteAndUnwrapAsync(new V1GetWorkflowByIdQuery(subflowRef.WorkflowId, subflowRef.Version), cancellationToken); if (subflow == null) { throw DomainException.NullReference(typeof(V1Workflow), $"Failed to find the referenced workflow '{reference}'"); } } if (command.IfNotExists && await this.Workflows.ContainsAsync(command.Definition.GetUniqueIdentifier(), cancellationToken)) { return(this.NotModified()); } else { while (await this.Workflows.ContainsAsync(command.Definition.GetUniqueIdentifier(), cancellationToken)) { var version = Version.Parse(command.Definition.Version); version = new Version(version.Major, version.Minor, version.Build == -1 ? 1 : version.Build + 1); command.Definition.Version = version.ToString(3); } } var workflow = await this.Workflows.AddAsync(new(command.Definition), cancellationToken); await this.Workflows.SaveChangesAsync(cancellationToken); var startState = workflow.Definition.GetStartState(); if (startState is EventStateDefinition eventState) { var lifetime = V1CorrelationLifetime.Transient; var conditionType = eventState.Exclusive ? V1CorrelationConditionType.AnyOf : V1CorrelationConditionType.AllOf; var conditions = new List <V1CorrelationCondition>(); foreach (var trigger in eventState.Triggers) { var filters = new List <V1EventFilter>(trigger.Events.Count); foreach (var eventRef in trigger.Events) { if (!workflow.Definition.TryGetEvent(eventRef, out var e)) { throw DomainException.NullReference(typeof(EventDefinition), eventRef, nameof(EventDefinition.Name)); } filters.Add(V1EventFilter.Match(e)); } conditions.Add(new(filters.ToArray())); } var outcome = new V1CorrelationOutcome(V1CorrelationOutcomeType.Start, workflow.Id); await this.Mediator.ExecuteAndUnwrapAsync(new V1CreateCorrelationCommand(lifetime, conditionType, conditions, outcome, null)); } else if (!string.IsNullOrWhiteSpace(workflow.Definition.Start?.Schedule?.Cron?.Expression)) { await this.Mediator.ExecuteAndUnwrapAsync(new V1ScheduleWorkflowCommand(workflow.Id, false), cancellationToken); } return(this.Ok(this.Mapper.Map <Integration.Models.V1Workflow>(workflow))); }