< Summary

Information
Class: Elsa.Workflows.Management.Services.WorkflowDefinitionPublisher
Assembly: Elsa.Workflows.Management
File(s): /home/runner/work/elsa-core/elsa-core/src/modules/Elsa.Workflows.Management/Services/WorkflowDefinitionPublisher.cs
Line coverage
64%
Covered lines: 108
Uncovered lines: 60
Coverable lines: 168
Total lines: 271
Line coverage: 64.2%
Branch coverage
51%
Covered branches: 27
Total branches: 52
Branch coverage: 51.9%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
New(...)0%620%
NewAsync(...)100%22100%
PublishAsync()50%2255.55%
PublishAsync()75%8896.29%
RetractAsync()0%620%
RetractAsync()0%620%
RevertVersionAsync()0%2040%
GetDraftAsync()75%8895.65%
SaveDraftAsync()56.25%161693.1%
Initialize(...)50%9657.14%

File(s)

/home/runner/work/elsa-core/elsa-core/src/modules/Elsa.Workflows.Management/Services/WorkflowDefinitionPublisher.cs

#LineLine coverage
 1using Elsa.Common;
 2using Elsa.Common.Entities;
 3using Elsa.Common.Models;
 4using Elsa.Mediator.Contracts;
 5using Elsa.Workflows.Activities;
 6using Elsa.Workflows.Management.Entities;
 7using Elsa.Workflows.Management.Filters;
 8using Elsa.Workflows.Management.Materializers;
 9using Elsa.Workflows.Management.Models;
 10using Elsa.Workflows.Management.Notifications;
 11using Elsa.Workflows.Management.Options;
 12using Elsa.Workflows.Models;
 13using Microsoft.Extensions.Options;
 14
 15namespace Elsa.Workflows.Management.Services;
 16
 17/// <inheritdoc />
 40418public class WorkflowDefinitionPublisher(
 40419    IWorkflowDefinitionService workflowDefinitionService,
 40420    IWorkflowDefinitionStore workflowDefinitionStore,
 40421    IWorkflowValidator workflowValidator,
 40422    IIdentityGenerator identityGenerator,
 40423    IActivitySerializer activitySerializer,
 40424    IMediator mediator,
 40425    ISystemClock systemClock,
 40426    IOptions<ManagementOptions> options)
 27    : IWorkflowDefinitionPublisher
 28{
 29    /// <inheritdoc />
 30    public WorkflowDefinition New(IActivity? root = null)
 31    {
 032        root ??= new Sequence();
 033        var id = identityGenerator.GenerateId();
 034        var definitionId = identityGenerator.GenerateId();
 35        const int version = 1;
 36
 037        return new()
 038        {
 039            Id = id,
 040            DefinitionId = definitionId,
 041            Version = version,
 042            IsLatest = true,
 043            IsPublished = false,
 044            CreatedAt = systemClock.UtcNow,
 045            StringData = activitySerializer.Serialize(root),
 046            MaterializerName = JsonWorkflowMaterializer.MaterializerName
 047        };
 48    }
 49
 50    public Task<WorkflowDefinition> NewAsync(IActivity? root = null, CancellationToken cancellationToken = default)
 51    {
 652        root ??= new Sequence();
 653        var id = identityGenerator.GenerateId();
 654        var definitionId = identityGenerator.GenerateId();
 55        const int version = 1;
 56
 657        var workflowDefinition = new WorkflowDefinition
 658        {
 659            Id = id,
 660            DefinitionId = definitionId,
 661            Version = version,
 662            IsLatest = true,
 663            IsPublished = false,
 664            CreatedAt = systemClock.UtcNow,
 665            StringData = activitySerializer.Serialize(root),
 666            MaterializerName = JsonWorkflowMaterializer.MaterializerName
 667        };
 68
 669        return Task.FromResult(workflowDefinition);
 70    }
 71
 72    /// <inheritdoc />
 73    public async Task<PublishWorkflowDefinitionResult> PublishAsync(string definitionId, CancellationToken cancellationT
 74    {
 175        var filter = WorkflowDefinitionHandle.ByDefinitionId(definitionId, VersionOptions.Latest).ToFilter();
 176        var definition = await workflowDefinitionStore.FindAsync(filter, cancellationToken);
 77
 178        if (definition == null)
 079            return new(false, new List<WorkflowValidationError>
 080            {
 081                new("Workflow definition not found.")
 082            }, new([]));
 83
 184        return await PublishAsync(definition, cancellationToken);
 185    }
 86
 87    /// <inheritdoc />
 88    public async Task<PublishWorkflowDefinitionResult> PublishAsync(WorkflowDefinition definition, CancellationToken can
 89    {
 590        var workflowGraph = await workflowDefinitionService.MaterializeWorkflowAsync(definition, cancellationToken);
 591        var validationErrors = (await workflowValidator.ValidateAsync(workflowGraph.Workflow, cancellationToken)).ToList
 92
 593        if (validationErrors.Any() && options.Value.FailOnValidationErrors)
 094            return new(false, validationErrors, new([]));
 95
 596        await mediator.SendAsync(new WorkflowDefinitionPublishing(definition), cancellationToken);
 597        var definitionId = definition.DefinitionId;
 98
 99        // Reset current latest and published definitions.
 5100        var publishedWorkflows = await workflowDefinitionStore.FindManyAsync(new()
 5101        {
 5102            DefinitionId = definitionId,
 5103            VersionOptions = VersionOptions.LatestOrPublished
 5104        }, cancellationToken);
 105
 18106        foreach (var publishedAndOrLatestWorkflow in publishedWorkflows)
 107        {
 4108            var isPublished = publishedAndOrLatestWorkflow.IsPublished;
 4109            publishedAndOrLatestWorkflow.IsPublished = false;
 4110            publishedAndOrLatestWorkflow.IsLatest = false;
 4111            await workflowDefinitionStore.SaveAsync(publishedAndOrLatestWorkflow, cancellationToken);
 112
 4113            if (isPublished)
 3114                await mediator.SendAsync(new WorkflowDefinitionVersionRetracted(publishedAndOrLatestWorkflow), cancellat
 4115        }
 116
 117        // Save the newly published definition.
 5118        definition.IsPublished = true;
 5119        definition.IsLatest = true;
 5120        definition = Initialize(definition);
 5121        await workflowDefinitionStore.SaveAsync(definition, cancellationToken);
 122
 5123        var affectedWorkflows = new AffectedWorkflows(new List<WorkflowDefinition>());
 5124        await mediator.SendAsync(new WorkflowDefinitionPublished(definition, affectedWorkflows), cancellationToken);
 5125        return new(true, validationErrors, affectedWorkflows);
 5126    }
 127
 128    /// <inheritdoc />
 129    public async Task<WorkflowDefinition?> RetractAsync(string definitionId, CancellationToken cancellationToken = defau
 130    {
 0131        var filter = WorkflowDefinitionHandle.ByDefinitionId(definitionId, VersionOptions.Published).ToFilter();
 0132        var definition = await workflowDefinitionStore.FindAsync(filter, cancellationToken);
 133
 0134        if (definition == null)
 0135            return null;
 136
 0137        return await RetractAsync(definition, cancellationToken);
 0138    }
 139
 140    /// <inheritdoc />
 141    public async Task<WorkflowDefinition> RetractAsync(WorkflowDefinition definition, CancellationToken cancellationToke
 142    {
 0143        if (!definition.IsPublished)
 0144            throw new InvalidOperationException("Cannot retract an unpublished workflow definition.");
 145
 0146        definition.IsPublished = false;
 147
 0148        await mediator.SendAsync(new WorkflowDefinitionRetracting(definition), cancellationToken);
 0149        await workflowDefinitionStore.SaveAsync(definition, cancellationToken);
 0150        await mediator.SendAsync(new WorkflowDefinitionRetracted(definition), cancellationToken);
 0151        return definition;
 0152    }
 153
 154    public async Task<WorkflowDefinition> RevertVersionAsync(string definitionId, int version, CancellationToken cancell
 155    {
 0156        var latestVersionFilter = new WorkflowDefinitionFilter
 0157        {
 0158            DefinitionId = definitionId,
 0159            VersionOptions = VersionOptions.Latest
 0160        };
 0161        var latestVersion = await workflowDefinitionStore.FindAsync(latestVersionFilter, cancellationToken);
 0162        var lastVersionFilter = new WorkflowDefinitionFilter
 0163        {
 0164            DefinitionId = definitionId
 0165        };
 0166        var lastVersion = await workflowDefinitionStore.FindLastVersionAsync(lastVersionFilter, cancellationToken);
 167
 0168        if (latestVersion != null)
 169        {
 0170            latestVersion.IsLatest = false;
 0171            await workflowDefinitionStore.SaveAsync(latestVersion, cancellationToken);
 172        }
 173
 0174        var draft = await GetDraftAsync(definitionId, VersionOptions.SpecificVersion(version), cancellationToken);
 0175        draft!.Id = identityGenerator.GenerateId();
 0176        draft.Version = (lastVersion?.Version ?? 0) + 1;
 0177        draft.IsLatest = true;
 178
 0179        await workflowDefinitionStore.SaveAsync(draft, cancellationToken);
 0180        return draft;
 0181    }
 182
 183    /// <inheritdoc />
 184    public async Task<WorkflowDefinition?> GetDraftAsync(string definitionId, VersionOptions versionOptions, Cancellatio
 185    {
 8186        var filter = new WorkflowDefinitionFilter
 8187        {
 8188            DefinitionId = definitionId,
 8189            VersionOptions = versionOptions
 8190        };
 8191        var order = new WorkflowDefinitionOrder<int>(x => x.Version, OrderDirection.Descending);
 8192        var lastVersion = await workflowDefinitionStore.FindLastVersionAsync(new()
 8193        {
 8194            DefinitionId = definitionId
 8195        }, cancellationToken);
 8196        var definition = await workflowDefinitionStore.FindAsync(filter, order, cancellationToken) ?? lastVersion;
 197
 8198        if (definition == null!)
 6199            return null;
 200
 2201        if (!definition.IsPublished)
 0202            return definition;
 203
 2204        var draft = definition.ShallowClone();
 205
 2206        draft.Version = lastVersion?.Version + 1 ?? 1;
 2207        draft.CreatedAt = systemClock.UtcNow;
 2208        draft.Id = identityGenerator.GenerateId();
 2209        draft.IsLatest = true;
 2210        draft.IsPublished = false;
 211
 2212        return draft;
 8213    }
 214
 215    /// <inheritdoc />
 216    public async Task<WorkflowDefinition> SaveDraftAsync(WorkflowDefinition definition, CancellationToken cancellationTo
 217    {
 4218        var draft = definition;
 4219        var definitionId = definition.DefinitionId;
 4220        var filter = new WorkflowDefinitionFilter
 4221        {
 4222            DefinitionId = definitionId
 4223        };
 4224        var highestVersion = await workflowDefinitionStore.FindLastVersionAsync(filter, cancellationToken);
 4225        var latestVersion = await workflowDefinitionStore.FindAsync(new()
 4226        {
 4227            DefinitionId = definitionId,
 4228            VersionOptions = VersionOptions.Latest
 4229        }, cancellationToken);
 230
 4231        draft.Version = draft.Id == latestVersion?.Id
 4232            ? latestVersion.Version
 4233            : draft.Id == highestVersion?.Id
 4234                ? highestVersion.Version
 4235                : highestVersion?.Version + 1 ?? 1;
 4236        draft.IsLatest = true;
 4237        draft = Initialize(draft);
 238
 4239        await mediator.SendAsync(new WorkflowDefinitionDraftSaving(draft), cancellationToken);
 240
 4241        if (latestVersion != null && latestVersion.Id != draft.Id)
 242        {
 0243            latestVersion.IsLatest = false;
 0244            await workflowDefinitionStore.SaveManyAsync([latestVersion, draft], cancellationToken);
 245        }
 246        else
 247        {
 4248            await workflowDefinitionStore.SaveAsync(draft, cancellationToken);
 249        }
 4250        await mediator.SendAsync(new WorkflowDefinitionDraftSaved(draft), cancellationToken);
 251
 4252        if (highestVersion is null)
 4253            await mediator.SendAsync(new WorkflowDefinitionCreated(definition), cancellationToken);
 254
 4255        return draft;
 4256    }
 257
 258    private WorkflowDefinition Initialize(WorkflowDefinition definition)
 259    {
 9260        if (definition.Id == null!)
 0261            definition.Id = identityGenerator.GenerateId();
 262
 9263        if (definition.DefinitionId == null!)
 0264            definition.DefinitionId = identityGenerator.GenerateId();
 265
 9266        if (definition.Version == 0)
 0267            definition.Version = 1;
 268
 9269        return definition;
 270    }
 271}