diff --git a/src/clients/Elsa.Api.Client/Resources/WorkflowDefinitions/Contracts/IWorkflowDefinitionsApi.cs b/src/clients/Elsa.Api.Client/Resources/WorkflowDefinitions/Contracts/IWorkflowDefinitionsApi.cs index 15d255e73..23fed1f70 100644 --- a/src/clients/Elsa.Api.Client/Resources/WorkflowDefinitions/Contracts/IWorkflowDefinitionsApi.cs +++ b/src/clients/Elsa.Api.Client/Resources/WorkflowDefinitions/Contracts/IWorkflowDefinitionsApi.cs @@ -1,5 +1,6 @@ using Elsa.Api.Client.Models; using Elsa.Api.Client.Resources.WorkflowDefinitions.Models; +using JetBrains.Annotations; using Refit; namespace Elsa.Api.Client.Resources.WorkflowDefinitions.Contracts; @@ -7,6 +8,7 @@ namespace Elsa.Api.Client.Resources.WorkflowDefinitions.Contracts; /// /// Represents a client for the workflow definitions API. /// +[PublicAPI] public interface IWorkflowDefinitionsApi { /// @@ -27,8 +29,24 @@ public interface IWorkflowDefinitionsApi /// Whether to include the root activity of composite activities. /// The cancellation token. /// The workflow definition. - [Get("/workflow-definitions/{definitionId}?versionOptions={VersionOptions}&includeCompositeRoot={includeCompositeRoot}")] + [Get("/workflow-definitions/{definitionId}?versionOptions={versionOptions}&includeCompositeRoot={includeCompositeRoot}")] Task GetAsync(string definitionId, VersionOptions? versionOptions = default, bool includeCompositeRoot = false, CancellationToken cancellationToken = default); + + /// + /// Gets the number of workflow definitions. + /// + /// The cancellation token. + [Get("/workflow-definitions/query/count")] + Task CountAsync(CancellationToken cancellationToken = default); + + /// + /// Gets a value indicating whether a workflow definition name is unique. + /// + /// The name to check. + /// The cancellation token. + /// A response containing a value indicating whether the name is unique. + [Get("/workflow-definitions/validation/is-name-unique?name={name}")] + Task GetIsNameUniqueAsync(string name, CancellationToken cancellationToken = default); /// /// Saves a workflow definition. diff --git a/src/clients/Elsa.Api.Client/Resources/WorkflowDefinitions/Models/CountWorkflowDefinitionsResponse.cs b/src/clients/Elsa.Api.Client/Resources/WorkflowDefinitions/Models/CountWorkflowDefinitionsResponse.cs new file mode 100644 index 000000000..c19a98856 --- /dev/null +++ b/src/clients/Elsa.Api.Client/Resources/WorkflowDefinitions/Models/CountWorkflowDefinitionsResponse.cs @@ -0,0 +1,6 @@ +namespace Elsa.Api.Client.Resources.WorkflowDefinitions.Models; + +/// +/// A response containing the number of workflow definitions. +/// +public record CountWorkflowDefinitionsResponse(long Count); \ No newline at end of file diff --git a/src/clients/Elsa.Api.Client/Resources/WorkflowDefinitions/Models/GetIsNameUniqueResponse.cs b/src/clients/Elsa.Api.Client/Resources/WorkflowDefinitions/Models/GetIsNameUniqueResponse.cs new file mode 100644 index 000000000..c7f4ff548 --- /dev/null +++ b/src/clients/Elsa.Api.Client/Resources/WorkflowDefinitions/Models/GetIsNameUniqueResponse.cs @@ -0,0 +1,7 @@ +namespace Elsa.Api.Client.Resources.WorkflowDefinitions.Models; + +/// +/// A response containing a value indicating whether a workflow definition name is unique. +/// +/// true if the name is unique, otherwise false. +public record GetIsNameUniqueResponse(bool IsUnique); \ No newline at end of file diff --git a/src/modules/Elsa.Common/Services/MemoryStore.cs b/src/modules/Elsa.Common/Services/MemoryStore.cs index e261352fe..b1ebac9e5 100644 --- a/src/modules/Elsa.Common/Services/MemoryStore.cs +++ b/src/modules/Elsa.Common/Services/MemoryStore.cs @@ -152,4 +152,17 @@ public class MemoryStore /// The predicate to match. /// True if any entity matches the specified predicate, otherwise false. public bool AnyAsync(Func predicate) => Entities.Values.Any(predicate); + + /// + /// Returns the number of entities matching the specified predicate. + /// + /// The predicate to match. + /// The property to distinct by. + /// True if any entity matches the specified predicate, otherwise false. + public long CountAsync(Func predicate, Func propertySelector) + { + return Entities.Values + .DistinctBy(propertySelector) + .Count(predicate); + } } \ No newline at end of file diff --git a/src/modules/Elsa.Dapper/Abstractions/SqlDialectBase.cs b/src/modules/Elsa.Dapper/Abstractions/SqlDialectBase.cs index 39c3c5462..64f96b17b 100644 --- a/src/modules/Elsa.Dapper/Abstractions/SqlDialectBase.cs +++ b/src/modules/Elsa.Dapper/Abstractions/SqlDialectBase.cs @@ -22,7 +22,10 @@ public abstract class SqlDialectBase : ISqlDialect public string Delete(string table) => $"delete from {table} where 1=1"; /// - public string Count(string table) => $"select COUNT(*) from {table} where 1=1"; + public string Count(string table) => Count("*", table); + + /// + public string Count(string fieldExpression, string table) => $"select COUNT({fieldExpression}) from {table} where 1=1"; /// public virtual string And(string field) => $"and {field} = @{field}"; diff --git a/src/modules/Elsa.Dapper/Contracts/ISqlDialect.cs b/src/modules/Elsa.Dapper/Contracts/ISqlDialect.cs index 94b5877e5..b7ecc5314 100644 --- a/src/modules/Elsa.Dapper/Contracts/ISqlDialect.cs +++ b/src/modules/Elsa.Dapper/Contracts/ISqlDialect.cs @@ -31,6 +31,13 @@ public interface ISqlDialect /// /// The table to query. string Count(string table); + + /// + /// Returns a SELECT count(fieldExpression) FROM query. + /// + /// The field expression to query. Examples: "*", "distinct FieldName" + /// The table to query. + string Count(string fieldExpression, string table); /// /// Returns an AND clause. diff --git a/src/modules/Elsa.Dapper/Extensions/ParameterizedQueryBuilderExtensions.cs b/src/modules/Elsa.Dapper/Extensions/ParameterizedQueryBuilderExtensions.cs index bac9afa63..bd805fb00 100644 --- a/src/modules/Elsa.Dapper/Extensions/ParameterizedQueryBuilderExtensions.cs +++ b/src/modules/Elsa.Dapper/Extensions/ParameterizedQueryBuilderExtensions.cs @@ -81,6 +81,18 @@ public static class ParameterizedQueryBuilderExtensions return query; } + /// + /// Begins a SELECT COUNT(expression) FROM query. + /// + /// The query. + /// The expression. Examples: "*" or "DISTINCT FieldName" + /// The table. + public static ParameterizedQuery Count(this ParameterizedQuery query, string expression, string table) + { + query.Sql.AppendLine(query.Dialect.Count(expression, table)); + return query; + } + /// /// Appends an AND clause to the query if the value is not null. /// diff --git a/src/modules/Elsa.Dapper/Modules/Management/Services/DapperWorkflowDefinitionStore.cs b/src/modules/Elsa.Dapper/Modules/Management/Services/DapperWorkflowDefinitionStore.cs index 90802f584..81a1448c9 100644 --- a/src/modules/Elsa.Dapper/Modules/Management/Services/DapperWorkflowDefinitionStore.cs +++ b/src/modules/Elsa.Dapper/Modules/Management/Services/DapperWorkflowDefinitionStore.cs @@ -142,6 +142,14 @@ public class DapperWorkflowDefinitionStore : IWorkflowDefinitionStore return await _store.AnyAsync(q => ApplyFilter(q, filter), cancellationToken); } + /// + public async Task CountDistinctAsync(CancellationToken cancellationToken = default) + { + return await _store.CountAsync( + filter => filter.Count($"distinct {nameof(WorkflowDefinition.DefinitionId)}", TableName), + cancellationToken); + } + private void ApplyFilter(ParameterizedQuery query, WorkflowDefinitionFilter filter) { ParameterizedQueryBuilderExtensions.Equals(query diff --git a/src/modules/Elsa.EntityFrameworkCore/Common/Store.cs b/src/modules/Elsa.EntityFrameworkCore/Common/Store.cs index 220ce135a..bf13688f2 100644 --- a/src/modules/Elsa.EntityFrameworkCore/Common/Store.cs +++ b/src/modules/Elsa.EntityFrameworkCore/Common/Store.cs @@ -305,4 +305,33 @@ public class Store where TDbContext : DbContext where TEnti var set = dbContext.Set(); return await set.AnyAsync(predicate, cancellationToken); } + + /// + /// Counts the number of entities matching a predicate. + /// + /// The predicate. + /// The cancellation token. + public async Task CountAsync(Expression> predicate, CancellationToken cancellationToken = default) + { + await using var dbContext = await CreateDbContextAsync(cancellationToken); + var set = dbContext.Set(); + return await set.CountAsync(predicate, cancellationToken); + } + + /// + /// Counts the distinct number of entities matching a predicate. + /// + /// The predicate. + /// The property selector to distinct by. + /// The cancellation token. + public async Task CountAsync(Expression> predicate, Expression> propertySelector, CancellationToken cancellationToken = default) + { + await using var dbContext = await CreateDbContextAsync(cancellationToken); + var set = dbContext.Set(); + return await set + .Where(predicate) + .Select(propertySelector) + .Distinct() + .CountAsync(cancellationToken); + } } \ No newline at end of file diff --git a/src/modules/Elsa.EntityFrameworkCore/Modules/Management/WorkflowDefinitionStore.cs b/src/modules/Elsa.EntityFrameworkCore/Modules/Management/WorkflowDefinitionStore.cs index 1afd5a797..0a37bdd4d 100644 --- a/src/modules/Elsa.EntityFrameworkCore/Modules/Management/WorkflowDefinitionStore.cs +++ b/src/modules/Elsa.EntityFrameworkCore/Modules/Management/WorkflowDefinitionStore.cs @@ -141,6 +141,12 @@ public class EFCoreWorkflowDefinitionStore : IWorkflowDefinitionStore /// public async Task AnyAsync(WorkflowDefinitionFilter filter, CancellationToken cancellationToken = default) => await _store.QueryAsync(queryable => Filter(queryable, filter), cancellationToken).Any(); + /// + public async Task CountDistinctAsync(CancellationToken cancellationToken = default) + { + return await _store.CountAsync(x => true, x => x.DefinitionId, cancellationToken); + } + private ValueTask SaveAsync(ManagementElsaDbContext managementElsaDbContext, WorkflowDefinition entity, CancellationToken cancellationToken) { var data = new WorkflowDefinitionState(entity.Options, entity.Variables, entity.Inputs, entity.Outputs, entity.Outcomes, entity.CustomProperties); diff --git a/src/modules/Elsa.MongoDb/Common/MongoDbStore.cs b/src/modules/Elsa.MongoDb/Common/MongoDbStore.cs index 6d88dbd74..0f882ac13 100644 --- a/src/modules/Elsa.MongoDb/Common/MongoDbStore.cs +++ b/src/modules/Elsa.MongoDb/Common/MongoDbStore.cs @@ -133,6 +133,12 @@ public class MongoDbStore where TDocument : class public async Task CountAsync(Func, IMongoQueryable> query, CancellationToken cancellationToken = default) => await query(_collection.AsQueryable()).LongCountAsync(cancellationToken); + /// + /// Counts documents in the collection using a filter and distinct by a key selector. + /// + public async Task CountAsync(Func, IMongoQueryable> query, Expression> propertySelector, CancellationToken cancellationToken = default) => + await query((IMongoQueryable)_collection.AsQueryable().DistinctBy(propertySelector)).LongCountAsync(cancellationToken); + /// /// Checks if any documents exist. /// diff --git a/src/modules/Elsa.MongoDb/Modules/Management/WorkflowDefinitionStore.cs b/src/modules/Elsa.MongoDb/Modules/Management/WorkflowDefinitionStore.cs index 7ab0c0d5f..c7196f514 100644 --- a/src/modules/Elsa.MongoDb/Modules/Management/WorkflowDefinitionStore.cs +++ b/src/modules/Elsa.MongoDb/Modules/Management/WorkflowDefinitionStore.cs @@ -59,11 +59,11 @@ public class MongoWorkflowDefinitionStore : IWorkflowDefinitionStore } /// - public async Task> FindManyAsync(WorkflowDefinitionFilter filter, CancellationToken cancellationToken = default) => + public async Task> FindManyAsync(WorkflowDefinitionFilter filter, CancellationToken cancellationToken = default) => await _mongoDbStore.FindManyAsync(queryable => Filter(queryable, filter), cancellationToken); /// - public async Task> FindManyAsync(WorkflowDefinitionFilter filter, WorkflowDefinitionOrder order, CancellationToken cancellationToken = default) => + public async Task> FindManyAsync(WorkflowDefinitionFilter filter, WorkflowDefinitionOrder order, CancellationToken cancellationToken = default) => await _mongoDbStore.FindManyAsync(queryable => Order(Filter(queryable, filter), order), cancellationToken); /// @@ -106,11 +106,11 @@ public class MongoWorkflowDefinitionStore : IWorkflowDefinitionStore } /// - public async Task SaveAsync(WorkflowDefinition definition, CancellationToken cancellationToken = default) => + public async Task SaveAsync(WorkflowDefinition definition, CancellationToken cancellationToken = default) => await _mongoDbStore.SaveAsync(definition, cancellationToken); /// - public async Task SaveManyAsync(IEnumerable definitions, CancellationToken cancellationToken = default) => + public async Task SaveManyAsync(IEnumerable definitions, CancellationToken cancellationToken = default) => await _mongoDbStore.SaveManyAsync(definitions.Select(i => i), cancellationToken); /// @@ -123,18 +123,26 @@ public class MongoWorkflowDefinitionStore : IWorkflowDefinitionStore } /// - public async Task AnyAsync(WorkflowDefinitionFilter filter, CancellationToken cancellationToken = default) => - await _mongoDbStore.FindManyAsync(queryable => Filter(queryable, filter), cancellationToken).Any(); + public async Task AnyAsync(WorkflowDefinitionFilter filter, CancellationToken cancellationToken = default) + { + return await _mongoDbStore.FindManyAsync(queryable => Filter(queryable, filter), cancellationToken).Any(); + } - private IMongoQueryable Filter(IMongoQueryable queryable, WorkflowDefinitionFilter filter) => + /// + public async Task CountDistinctAsync(CancellationToken cancellationToken = default) + { + return await _mongoDbStore.CountAsync(queryable => queryable, x => x.DefinitionId, cancellationToken); + } + + private IMongoQueryable Filter(IMongoQueryable queryable, WorkflowDefinitionFilter filter) => (filter.Apply(queryable) as IMongoQueryable)!; - private IMongoQueryable Order(IMongoQueryable queryable, WorkflowDefinitionOrder order) => + private IMongoQueryable Order(IMongoQueryable queryable, WorkflowDefinitionOrder order) => (queryable.OrderBy(order) as IMongoQueryable)!; - - private IMongoQueryable Paginate(IMongoQueryable queryable, PageArgs pageArgs) => + + private IMongoQueryable Paginate(IMongoQueryable queryable, PageArgs pageArgs) => (queryable.Paginate(pageArgs) as IMongoQueryable)!; - - private IMongoQueryable OrderAndPaginate(IMongoQueryable queryable, WorkflowDefinitionOrder order, PageArgs pageArgs) => + + private IMongoQueryable OrderAndPaginate(IMongoQueryable queryable, WorkflowDefinitionOrder order, PageArgs pageArgs) => (queryable.OrderBy(order).Paginate(pageArgs) as IMongoQueryable)!; -} +} \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowDefinitions/Count/Endpoint.cs b/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowDefinitions/Count/Endpoint.cs new file mode 100644 index 000000000..238cf9c2e --- /dev/null +++ b/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowDefinitions/Count/Endpoint.cs @@ -0,0 +1,39 @@ +using Elsa.Abstractions; +using Elsa.Common.Entities; +using Elsa.Common.Models; +using Elsa.Workflows.Core.Contracts; +using Elsa.Workflows.Core.Serialization.Converters; +using Elsa.Workflows.Management.Contracts; +using Elsa.Workflows.Management.Filters; +using Elsa.Workflows.Management.Mappers; +using JetBrains.Annotations; +using Microsoft.AspNetCore.Http; + +namespace Elsa.Workflows.Api.Endpoints.WorkflowDefinitions.Count; + +/// +/// An endpoint for counting workflow definitions. +/// +[PublicAPI] +internal class Count : ElsaEndpointWithoutRequest +{ + private readonly IWorkflowDefinitionStore _store; + + public Count(IWorkflowDefinitionStore store) + { + _store = store; + } + + public override void Configure() + { + Get("/workflow-definitions/query/count"); + ConfigurePermissions("read:workflow-definitions"); + } + + public override async Task HandleAsync(CancellationToken cancellationToken) + { + var count = await _store.CountDistinctAsync(cancellationToken); + var response = new Response(count); + await SendOkAsync(response, cancellationToken); + } +} \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowDefinitions/Count/Models.cs b/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowDefinitions/Count/Models.cs new file mode 100644 index 000000000..17477507f --- /dev/null +++ b/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowDefinitions/Count/Models.cs @@ -0,0 +1,3 @@ +namespace Elsa.Workflows.Api.Endpoints.WorkflowDefinitions.Count; + +internal record Response(long Count); \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowDefinitions/IsNameUnique/Endpoint.cs b/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowDefinitions/IsNameUnique/Endpoint.cs new file mode 100644 index 000000000..39e02f5a6 --- /dev/null +++ b/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowDefinitions/IsNameUnique/Endpoint.cs @@ -0,0 +1,45 @@ +using Elsa.Abstractions; +using Elsa.Common.Entities; +using Elsa.Common.Models; +using Elsa.Workflows.Core.Contracts; +using Elsa.Workflows.Core.Serialization.Converters; +using Elsa.Workflows.Management.Contracts; +using Elsa.Workflows.Management.Filters; +using Elsa.Workflows.Management.Mappers; +using JetBrains.Annotations; +using Microsoft.AspNetCore.Http; + +namespace Elsa.Workflows.Api.Endpoints.WorkflowDefinitions.IsNameUnique; + +/// +/// Checks if a workflow definition name is unique. +/// +[PublicAPI] +internal class IsNameUnique : ElsaEndpoint +{ + private readonly IWorkflowDefinitionStore _store; + + public IsNameUnique(IWorkflowDefinitionStore store) + { + _store = store; + } + + public override void Configure() + { + Get("/workflow-definitions/validation/is-name-unique"); + ConfigurePermissions("read:workflow-definitions"); + } + + public override async Task HandleAsync(Request request, CancellationToken cancellationToken) + { + var filter = new WorkflowDefinitionFilter + { + Name = request.Name.Trim() + }; + + var exists = await _store.AnyAsync(filter, cancellationToken); + var response = new Response(!exists); + + await SendOkAsync(response, cancellationToken); + } +} \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowDefinitions/IsNameUnique/Models.cs b/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowDefinitions/IsNameUnique/Models.cs new file mode 100644 index 000000000..349c9110f --- /dev/null +++ b/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowDefinitions/IsNameUnique/Models.cs @@ -0,0 +1,8 @@ +namespace Elsa.Workflows.Api.Endpoints.WorkflowDefinitions.IsNameUnique; + +internal class Request +{ + public string Name { get; set; } = default!; +} + +internal record Response(bool IsUnique); diff --git a/src/modules/Elsa.Workflows.Api/Extensions/ApplicationBuilderExtensions.cs b/src/modules/Elsa.Workflows.Api/Extensions/ApplicationBuilderExtensions.cs index a164a9a45..019cfafca 100644 --- a/src/modules/Elsa.Workflows.Api/Extensions/ApplicationBuilderExtensions.cs +++ b/src/modules/Elsa.Workflows.Api/Extensions/ApplicationBuilderExtensions.cs @@ -4,7 +4,13 @@ using Microsoft.AspNetCore.Builder; // ReSharper disable once CheckNamespace namespace Elsa.Extensions; +/// +/// Provides extensions to . +/// public static class ApplicationBuilderExtensions { + /// + /// Adds middleware that handles JSON serialization errors. + /// public static IApplicationBuilder UseJsonSerializationErrorHandler(this IApplicationBuilder app) => app.UseMiddleware(); } \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Api/Extensions/ModuleExtensions.cs b/src/modules/Elsa.Workflows.Api/Extensions/ModuleExtensions.cs index 13171af63..b302aeb9e 100644 --- a/src/modules/Elsa.Workflows.Api/Extensions/ModuleExtensions.cs +++ b/src/modules/Elsa.Workflows.Api/Extensions/ModuleExtensions.cs @@ -4,8 +4,14 @@ using Elsa.Workflows.Api.Features; // ReSharper disable once CheckNamespace namespace Elsa.Extensions; +/// +/// Provides extensions to . +/// public static class ModuleExtensions { + /// + /// Configures the workflows API feature. + /// public static IModule UseWorkflowsApi(this IModule module, Action? configure = default) { module.Configure(configure); diff --git a/src/modules/Elsa.Workflows.Api/Extensions/WorkflowsApiFeatureExtensions.cs b/src/modules/Elsa.Workflows.Api/Extensions/WorkflowsApiFeatureExtensions.cs index 514f9679f..418e1f482 100644 --- a/src/modules/Elsa.Workflows.Api/Extensions/WorkflowsApiFeatureExtensions.cs +++ b/src/modules/Elsa.Workflows.Api/Extensions/WorkflowsApiFeatureExtensions.cs @@ -3,8 +3,14 @@ using Elsa.Workflows.Api.Features; // ReSharper disable once CheckNamespace namespace Elsa.Extensions; +/// +/// Provides extensions to . +/// public static class WorkflowsApiFeatureExtensions { + /// + /// Adds FastEndpoint endpoints from the assembly containing . + /// public static WorkflowsApiFeature AddFastEndpointsAssembly(this WorkflowsApiFeature feature) { feature.Module.AddFastEndpointsAssembly(); diff --git a/src/modules/Elsa.Workflows.Management/Contracts/IWorkflowDefinitionStore.cs b/src/modules/Elsa.Workflows.Management/Contracts/IWorkflowDefinitionStore.cs index 7432c6db2..e323d4af3 100644 --- a/src/modules/Elsa.Workflows.Management/Contracts/IWorkflowDefinitionStore.cs +++ b/src/modules/Elsa.Workflows.Management/Contracts/IWorkflowDefinitionStore.cs @@ -141,4 +141,9 @@ public interface IWorkflowDefinitionStore /// The cancellation token. /// True if any workflow definition matches the specified filter. Task AnyAsync(WorkflowDefinitionFilter filter, CancellationToken cancellationToken = default); + + /// + /// Returns the number of logical workflow definitions. + /// + Task CountDistinctAsync(CancellationToken cancellationToken = default); } \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Management/Services/MemoryWorkflowDefinitionStore.cs b/src/modules/Elsa.Workflows.Management/Services/MemoryWorkflowDefinitionStore.cs index 166ab10e5..2c4d1ca84 100644 --- a/src/modules/Elsa.Workflows.Management/Services/MemoryWorkflowDefinitionStore.cs +++ b/src/modules/Elsa.Workflows.Management/Services/MemoryWorkflowDefinitionStore.cs @@ -137,7 +137,12 @@ public class MemoryWorkflowDefinitionStore : IWorkflowDefinitionStore var exists = _store.Query(query => Filter(query, filter)).Any(); return Task.FromResult(exists); } - + + public Task CountDistinctAsync(CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + private IQueryable Filter(IQueryable queryable, WorkflowDefinitionFilter filter) => filter.Apply(queryable); private string GetId(WorkflowDefinition workflowDefinition) => workflowDefinition.Id;