< Summary

Information
Class: Elsa.Persistence.EFCore.Modules.Management.EFCoreWorkflowDefinitionStore
Assembly: Elsa.Persistence.EFCore
File(s): /home/runner/work/elsa-core/elsa-core/src/modules/Elsa.Persistence.EFCore/Modules/Management/WorkflowDefinitionStore.cs
Line coverage
72%
Covered lines: 94
Uncovered lines: 36
Coverable lines: 130
Total lines: 270
Line coverage: 72.3%
Branch coverage
62%
Covered branches: 39
Total branches: 62
Branch coverage: 62.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%
FindAsync()100%11100%
FindAsync()100%11100%
FindManyAsync()100%210%
FindManyAsync()100%11100%
FindManyAsync()100%11100%
FindManyAsync()100%11100%
FindSummariesAsync()100%210%
FindSummariesAsync()0%2040%
FindSummariesAsync()100%11100%
FindSummariesAsync()75%4485.71%
FindLastVersionAsync()100%11100%
SaveAsync()100%11100%
SaveManyAsync()100%11100%
DeleteAsync()75%4487.5%
AnyAsync()100%210%
CountDistinctAsync()100%210%
GetIsNameUnique()100%210%
OnSaveAsync(...)100%11100%
OnLoadAsync(...)75%4476.47%
Filter(...)78.94%503880%
Paginate(...)0%7280%
.ctor()100%11100%
.ctor(...)100%11100%
get_Options()100%11100%
get_Variables()100%11100%
get_Inputs()100%11100%
get_Outputs()100%11100%
get_Outcomes()100%11100%
get_CustomProperties()100%11100%

File(s)

/home/runner/work/elsa-core/elsa-core/src/modules/Elsa.Persistence.EFCore/Modules/Management/WorkflowDefinitionStore.cs

#LineLine coverage
 1using System.Diagnostics.CodeAnalysis;
 2using System.Text.Json.Serialization;
 3using Elsa.Common.Entities;
 4using Elsa.Common.Models;
 5using Elsa.Extensions;
 6using Elsa.Workflows;
 7using Elsa.Workflows.Management;
 8using Elsa.Workflows.Management.Entities;
 9using Elsa.Workflows.Management.Filters;
 10using Elsa.Workflows.Management.Models;
 11using Elsa.Workflows.Memory;
 12using Elsa.Workflows.Models;
 13using JetBrains.Annotations;
 14using Microsoft.EntityFrameworkCore;
 15using Microsoft.Extensions.Logging;
 16using Open.Linq.AsyncExtensions;
 17
 18namespace Elsa.Persistence.EFCore.Modules.Management;
 19
 20/// <inheritdoc />
 21[UsedImplicitly]
 32422public class EFCoreWorkflowDefinitionStore(EntityStore<ManagementElsaDbContext, WorkflowDefinition> store, IPayloadSeria
 23    : IWorkflowDefinitionStore
 24{
 25    /// <inheritdoc />
 26    public async Task<WorkflowDefinition?> FindAsync(WorkflowDefinitionFilter filter, CancellationToken cancellationToke
 27    {
 108528        var orderBy = new WorkflowDefinitionOrder<DateTimeOffset>(x => x.CreatedAt, OrderDirection.Ascending);
 108529        return await FindAsync(filter, orderBy, cancellationToken);
 108530    }
 31
 32    /// <inheritdoc />
 33    public async Task<WorkflowDefinition?> FindAsync<TOrderBy>(WorkflowDefinitionFilter filter, WorkflowDefinitionOrder<
 34    {
 218235        return await store.QueryAsync(queryable => Filter(queryable, filter).OrderBy(order), OnLoadAsync, filter.TenantA
 109136    }
 37
 38    /// <inheritdoc />
 39    public async Task<Page<WorkflowDefinition>> FindManyAsync(WorkflowDefinitionFilter filter, PageArgs pageArgs, Cancel
 40    {
 041        var orderBy = new WorkflowDefinitionOrder<DateTimeOffset>(x => x.CreatedAt, OrderDirection.Ascending);
 042        return await FindManyAsync(filter, orderBy, pageArgs, cancellationToken);
 043    }
 44
 45    /// <inheritdoc />
 46    public async Task<Page<WorkflowDefinition>> FindManyAsync<TOrderBy>(WorkflowDefinitionFilter filter, WorkflowDefinit
 47    {
 248        var count = await store.QueryAsync(queryable => Filter(queryable, filter), cancellationToken).LongCount();
 249        var results = await store.QueryAsync(queryable => Filter(queryable, filter).OrderBy(order).Paginate(pageArgs), O
 150        return new(results, count);
 151    }
 52
 53    /// <inheritdoc />
 54    public async Task<IEnumerable<WorkflowDefinition>> FindManyAsync(WorkflowDefinitionFilter filter, CancellationToken 
 55    {
 115256        var orderBy = new WorkflowDefinitionOrder<DateTimeOffset>(x => x.CreatedAt, OrderDirection.Ascending);
 115257        return await FindManyAsync(filter, orderBy, cancellationToken);
 115258    }
 59
 60    /// <inheritdoc />
 61    public async Task<IEnumerable<WorkflowDefinition>> FindManyAsync<TOrderBy>(WorkflowDefinitionFilter filter, Workflow
 62    {
 230463        return await store.QueryAsync(queryable => Filter(queryable, filter).OrderBy(order), OnLoadAsync, filter.TenantA
 115264    }
 65
 66    /// <inheritdoc />
 67    [RequiresUnreferencedCode("The method 'FindSummariesAsync' is used for serialization and requires unreferenced code 
 68    public async Task<Page<WorkflowDefinitionSummary>> FindSummariesAsync(WorkflowDefinitionFilter filter, PageArgs page
 69    {
 070        var orderBy = new WorkflowDefinitionOrder<DateTimeOffset>(x => x.CreatedAt, OrderDirection.Ascending);
 071        return await FindSummariesAsync(filter, orderBy, pageArgs, cancellationToken);
 072    }
 73
 74    /// <inheritdoc />
 75    [RequiresUnreferencedCode("The method 'FindSummariesAsync' is used for serialization and requires unreferenced code 
 76    public async Task<Page<WorkflowDefinitionSummary>> FindSummariesAsync<TOrderBy>(WorkflowDefinitionFilter filter, Wor
 77    {
 078        await using var dbContext = await store.CreateDbContextAsync(cancellationToken);
 079        var set = dbContext.WorkflowDefinitions.AsNoTracking();
 080        var queryable = Filter(set.AsQueryable(), filter).OrderBy(order);
 81
 082        if (filter.TenantAgnostic)
 083            queryable = queryable.IgnoreQueryFilters();
 84
 085        var count = await queryable.LongCountAsync(cancellationToken);
 086        queryable = Paginate(queryable, pageArgs);
 087        var results = await queryable.Select(WorkflowDefinitionSummary.FromDefinitionExpression()).ToListAsync(cancellat
 088        return Page.Of(results, count);
 089    }
 90
 91    /// <inheritdoc />
 92    [RequiresUnreferencedCode("The method 'FindSummariesAsync' is used for serialization and requires unreferenced code 
 93    public async Task<IEnumerable<WorkflowDefinitionSummary>> FindSummariesAsync(WorkflowDefinitionFilter filter, Cancel
 94    {
 295        var orderBy = new WorkflowDefinitionOrder<DateTimeOffset>(x => x.CreatedAt, OrderDirection.Ascending);
 296        return await FindSummariesAsync(filter, orderBy, cancellationToken);
 297    }
 98
 99    /// <inheritdoc />
 100    [RequiresUnreferencedCode("The method 'FindSummariesAsync' is used for serialization and requires unreferenced code 
 101    public async Task<IEnumerable<WorkflowDefinitionSummary>> FindSummariesAsync<TOrderBy>(WorkflowDefinitionFilter filt
 102    {
 2103        await using var dbContext = await store.CreateDbContextAsync(cancellationToken);
 2104        var set = dbContext.WorkflowDefinitions.AsNoTracking();
 2105        var queryable = Filter(set.AsQueryable(), filter).OrderBy(order);
 106
 2107        if (filter.TenantAgnostic)
 0108            queryable = queryable.IgnoreQueryFilters();
 109
 2110        return await queryable.Select(WorkflowDefinitionSummary.FromDefinitionExpression()).ToListAsync(cancellationToke
 2111    }
 112
 113    /// <inheritdoc />
 114    public async Task<WorkflowDefinition?> FindLastVersionAsync(WorkflowDefinitionFilter filter, CancellationToken cance
 115    {
 14116        return await store.QueryAsync(queryable => Filter(queryable, filter).OrderByDescending(x => x.Version), OnLoadAs
 7117    }
 118
 119    /// <inheritdoc />
 120    public async Task SaveAsync(WorkflowDefinition definition, CancellationToken cancellationToken = default)
 121    {
 11122        await store.SaveAsync(definition, OnSaveAsync, cancellationToken);
 11123    }
 124
 125    /// <inheritdoc />
 126    public async Task SaveManyAsync(IEnumerable<WorkflowDefinition> definitions, CancellationToken cancellationToken = d
 127    {
 574128        await store.SaveManyAsync(definitions, OnSaveAsync, cancellationToken);
 574129    }
 130
 131    /// <inheritdoc />
 132    public async Task<long> DeleteAsync(WorkflowDefinitionFilter filter, CancellationToken cancellationToken = default)
 133    {
 4134        await using var dbContext = await store.CreateDbContextAsync(cancellationToken);
 4135        var set = dbContext.WorkflowDefinitions;
 4136        var queryable = set.AsQueryable();
 137
 4138        if (filter.TenantAgnostic)
 0139            queryable = queryable.IgnoreQueryFilters();
 140
 4141        var ids = await Filter(queryable, filter).Select(x => x.Id).Distinct().ToListAsync(cancellationToken);
 4142        return await store.DeleteWhereAsync(x => ids.Contains(x.Id), cancellationToken);
 4143    }
 144
 145    /// <inheritdoc />
 146    public async Task<bool> AnyAsync(WorkflowDefinitionFilter filter, CancellationToken cancellationToken = default)
 147    {
 0148        return await store.QueryAsync(queryable => Filter(queryable, filter), filter.TenantAgnostic, cancellationToken).
 0149    }
 150
 151    /// <inheritdoc />
 152    public async Task<long> CountDistinctAsync(CancellationToken cancellationToken = default)
 153    {
 0154        return await store.CountAsync(x => true, x => x.DefinitionId, false, cancellationToken);
 0155    }
 156
 157    /// <inheritdoc />
 158    public async Task<bool> GetIsNameUnique(string name, string? definitionId = null, CancellationToken cancellationToke
 159    {
 0160        var exists = await store.AnyAsync(x => x.Name == name && x.DefinitionId != definitionId, false, cancellationToke
 0161        return !exists;
 0162    }
 163
 164    private ValueTask OnSaveAsync(ManagementElsaDbContext managementElsaDbContext, WorkflowDefinition entity, Cancellati
 165    {
 588166        var data = new WorkflowDefinitionState(entity.Options, entity.Variables, entity.Inputs, entity.Outputs, entity.O
 588167        var json = payloadSerializer.Serialize(data);
 168
 588169        managementElsaDbContext.Entry(entity).Property("Data").CurrentValue = json;
 588170        managementElsaDbContext.Entry(entity).Property("UsableAsActivity").CurrentValue = data.Options.UsableAsActivity;
 588171        return ValueTask.CompletedTask;
 172    }
 173
 174    private ValueTask OnLoadAsync(ManagementElsaDbContext managementElsaDbContext, WorkflowDefinition? entity, Cancellat
 175    {
 2115176        if (entity == null)
 0177            return ValueTask.CompletedTask;
 178
 2115179        var data = new WorkflowDefinitionState(entity.Options, entity.Variables, entity.Inputs, entity.Outputs, entity.O
 2115180        var json = (string?)managementElsaDbContext.Entry(entity).Property("Data").CurrentValue;
 181
 182        try
 183        {
 2115184            if (!string.IsNullOrWhiteSpace(json))
 2115185                data = payloadSerializer.Deserialize<WorkflowDefinitionState>(json);
 2115186        }
 0187        catch (Exception exp)
 188        {
 0189            logger.LogError(exp, "Could not deserialize workflow definition state: {DefinitionId}. Reverting to default 
 0190        }
 191
 2115192        entity.Options = data.Options;
 2115193        entity.Variables = data.Variables;
 2115194        entity.Inputs = data.Inputs;
 2115195        entity.Outputs = data.Outputs;
 2115196        entity.Outcomes = data.Outcomes;
 2115197        entity.CustomProperties = data.CustomProperties;
 198
 2115199        return ValueTask.CompletedTask;
 200    }
 201
 202    private IQueryable<WorkflowDefinition> Filter(IQueryable<WorkflowDefinition> queryable, WorkflowDefinitionFilter fil
 203    {
 2258204        var definitionId = filter.DefinitionId ?? filter.DefinitionHandle?.DefinitionId;
 2258205        var versionOptions = filter.VersionOptions ?? filter.DefinitionHandle?.VersionOptions;
 2258206        var id = filter.Id ?? filter.DefinitionHandle?.DefinitionVersionId;
 207
 4177208        if (definitionId != null) queryable = queryable.Where(x => x.DefinitionId == definitionId);
 2264209        if (filter.DefinitionIds != null) queryable = queryable.Where(x => filter.DefinitionIds.Contains(x.DefinitionId)
 2718210        if (id != null) queryable = queryable.Where(x => x.Id == id);
 2258211        if (filter.Ids != null) queryable = queryable.Where(x => filter.Ids.Contains(x.Id));
 4042212        if (versionOptions != null) queryable = queryable.WithVersion(versionOptions.Value);
 2258213        if (filter.MaterializerName != null) queryable = queryable.Where(x => x.MaterializerName == filter.MaterializerN
 2258214        if (filter.Name != null) queryable = queryable.Where(x => x.Name == filter.Name);
 2258215        if (filter.Names != null) queryable = queryable.Where(x => filter.Names.Contains(x.Name!));
 2269216        if (filter.UsableAsActivity != null) queryable = queryable.Where(x => EF.Property<bool>(x, "UsableAsActivity") =
 2258217        if (!string.IsNullOrWhiteSpace(filter.SearchTerm)) queryable = queryable.Where(x => x.Name!.ToLower().Contains(f
 218
 219        // TEMP: IsSystem may be null when upgrading from older versions of Elsa to 3.2. See issue #5366.
 220        // In a future version, we should remove this check and simply do queryable.Where(x => x.IsSystem == filter.IsSy
 2258221        if (filter.IsSystem != null)
 0222            queryable = filter.IsSystem == true
 0223                ? queryable.Where(x => x.IsSystem == true)
 0224#pragma warning disable CS0472 // The result of the expression is always the same since a value of this type is never eq
 0225                : queryable.Where(x => x.IsSystem == false || x.IsSystem == null!);
 226#pragma warning restore CS0472 // The result of the expression is always the same since a value of this type is never eq
 227
 2264228        if (filter.IsReadonly != null) queryable = queryable.Where(x => x.IsReadonly == filter.IsReadonly);
 2258229        return queryable;
 230    }
 231
 232    private IQueryable<WorkflowDefinition> Paginate(IQueryable<WorkflowDefinition> queryable, PageArgs? pageArgs)
 233    {
 0234        if (pageArgs?.Offset != null) queryable = queryable.Skip(pageArgs.Offset.Value);
 0235        if (pageArgs?.Limit != null) queryable = queryable.Take(pageArgs.Limit.Value);
 0236        return queryable;
 237    }
 238
 239    private class WorkflowDefinitionState
 240    {
 241        [JsonConstructor]
 2115242        public WorkflowDefinitionState()
 243        {
 2115244        }
 245
 2703246        public WorkflowDefinitionState(
 2703247            WorkflowOptions options,
 2703248            ICollection<Variable> variables,
 2703249            ICollection<InputDefinition> inputs,
 2703250            ICollection<OutputDefinition> outputs,
 2703251            ICollection<string> outcomes,
 2703252            IDictionary<string, object> customProperties
 2703253        )
 254        {
 2703255            Options = options;
 2703256            Variables = variables;
 2703257            Inputs = inputs;
 2703258            Outputs = outputs;
 2703259            Outcomes = outcomes;
 2703260            CustomProperties = customProperties;
 2703261        }
 262
 12927263        public WorkflowOptions Options { get; set; } = new();
 12339264        public ICollection<Variable> Variables { get; set; } = new List<Variable>();
 12339265        public ICollection<InputDefinition> Inputs { get; set; } = new List<InputDefinition>();
 12339266        public ICollection<OutputDefinition> Outputs { get; set; } = new List<OutputDefinition>();
 12339267        public ICollection<string> Outcomes { get; set; } = new List<string>();
 12339268        public IDictionary<string, object> CustomProperties { get; set; } = new Dictionary<string, object>();
 269    }
 270}

Methods/Properties

.ctor(Elsa.Persistence.EFCore.EntityStore`2<Elsa.Persistence.EFCore.Modules.Management.ManagementElsaDbContext,Elsa.Workflows.Management.Entities.WorkflowDefinition>,Elsa.Workflows.IPayloadSerializer,Microsoft.Extensions.Logging.ILogger`1<Elsa.Persistence.EFCore.Modules.Management.EFCoreWorkflowDefinitionStore>)
FindAsync()
FindAsync()
FindManyAsync()
FindManyAsync()
FindManyAsync()
FindManyAsync()
FindSummariesAsync()
FindSummariesAsync()
FindSummariesAsync()
FindSummariesAsync()
FindLastVersionAsync()
SaveAsync()
SaveManyAsync()
DeleteAsync()
AnyAsync()
CountDistinctAsync()
GetIsNameUnique()
OnSaveAsync(Elsa.Persistence.EFCore.Modules.Management.ManagementElsaDbContext,Elsa.Workflows.Management.Entities.WorkflowDefinition,System.Threading.CancellationToken)
OnLoadAsync(Elsa.Persistence.EFCore.Modules.Management.ManagementElsaDbContext,Elsa.Workflows.Management.Entities.WorkflowDefinition,System.Threading.CancellationToken)
Filter(System.Linq.IQueryable`1<Elsa.Workflows.Management.Entities.WorkflowDefinition>,Elsa.Workflows.Management.Filters.WorkflowDefinitionFilter)
Paginate(System.Linq.IQueryable`1<Elsa.Workflows.Management.Entities.WorkflowDefinition>,Elsa.Common.Models.PageArgs)
.ctor()
.ctor(Elsa.Workflows.Models.WorkflowOptions,System.Collections.Generic.ICollection`1<Elsa.Workflows.Memory.Variable>,System.Collections.Generic.ICollection`1<Elsa.Workflows.Models.InputDefinition>,System.Collections.Generic.ICollection`1<Elsa.Workflows.Models.OutputDefinition>,System.Collections.Generic.ICollection`1<System.String>,System.Collections.Generic.IDictionary`2<System.String,System.Object>)
get_Options()
get_Variables()
get_Inputs()
get_Outputs()
get_Outcomes()
get_CustomProperties()