Esempio n. 1
0
        /// <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)));
        }