From 58f0c48f66632d1c954bf50e6e285baeb24b9f26 Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Wed, 12 Feb 2025 22:08:09 +0100 Subject: [PATCH 01/10] Refactor workflow management to utilize mediator requests. Replaced direct store interactions in workflow management with mediator-based requests using `IMediator`, improving consistency and modularity. Added new handlers and requests for workflow definition operations, and updated existing services to align with mediator patterns. --- src/apps/Elsa.Server.Web/Program.cs | 4 +- .../Elsa.Mediator/Contracts/IRequestSender.cs | 2 +- .../Middleware/Request/RequestContext.cs | 4 +- .../Elsa.Mediator/Services/DefaultMediator.cs | 4 +- ...metryTracingActivityExecutionMiddleware.cs | 10 +-- .../Commands/SaveWorkflowDefinitionCommand.cs | 6 ++ .../CachingWorkflowDefinitionsFeature.cs | 2 +- .../Features/WorkflowDefinitionsFeature.cs | 22 +++--- .../Features/WorkflowManagementFeature.cs | 2 +- .../Commands/SaveWorkflowDefinitionHandler.cs | 14 ++++ .../DeleteWorkflowInstances.cs | 2 +- .../EvictWorkflowDefinitionServiceCache.cs | 2 +- .../RefreshActivityRegistry.cs | 2 +- .../UpdateConsumingWorkflows.cs | 2 +- .../ValidateWorkflow.cs | 2 +- ...dLastVersionOfWorkflowDefinitionHandler.cs | 18 +++++ ...stOrPublishedWorkflowDefinitionsHandler.cs | 31 ++++++++ .../FindWorkflowDefinitionHandler.cs | 2 +- .../Models/SaveWorkflowDefinitionRequest.cs | 2 +- ...dLastVersionOfWorkflowDefinitionRequest.cs | 10 +++ ...stOrPublishedWorkflowDefinitionsRequest.cs | 9 +++ .../Services/WorkflowDefinitionPublisher.cs | 76 ++++++------------- 22 files changed, 143 insertions(+), 85 deletions(-) create mode 100644 src/modules/Elsa.Workflows.Management/Commands/SaveWorkflowDefinitionCommand.cs create mode 100644 src/modules/Elsa.Workflows.Management/Handlers/Commands/SaveWorkflowDefinitionHandler.cs rename src/modules/Elsa.Workflows.Management/Handlers/{Notification => Notifications}/DeleteWorkflowInstances.cs (97%) rename src/modules/Elsa.Workflows.Management/Handlers/{Notification => Notifications}/EvictWorkflowDefinitionServiceCache.cs (97%) rename src/modules/Elsa.Workflows.Management/Handlers/{Notification => Notifications}/RefreshActivityRegistry.cs (98%) rename src/modules/Elsa.Workflows.Management/Handlers/{Notification => Notifications}/UpdateConsumingWorkflows.cs (92%) rename src/modules/Elsa.Workflows.Management/Handlers/{Notification => Notifications}/ValidateWorkflow.cs (95%) create mode 100644 src/modules/Elsa.Workflows.Management/Handlers/Requests/FindLastVersionOfWorkflowDefinitionHandler.cs create mode 100644 src/modules/Elsa.Workflows.Management/Handlers/Requests/FindLatestOrPublishedWorkflowDefinitionsHandler.cs rename src/modules/Elsa.Workflows.Management/Handlers/{Request => Requests}/FindWorkflowDefinitionHandler.cs (94%) create mode 100644 src/modules/Elsa.Workflows.Management/Requests/FindLastVersionOfWorkflowDefinitionRequest.cs create mode 100644 src/modules/Elsa.Workflows.Management/Requests/FindLatestOrPublishedWorkflowDefinitionsRequest.cs diff --git a/src/apps/Elsa.Server.Web/Program.cs b/src/apps/Elsa.Server.Web/Program.cs index 32fdbfc5c..0affa0e82 100644 --- a/src/apps/Elsa.Server.Web/Program.cs +++ b/src/apps/Elsa.Server.Web/Program.cs @@ -91,7 +91,7 @@ const bool useSignalR = false; // Disabled until Elsa Studio sends authenticated const WorkflowRuntime workflowRuntime = WorkflowRuntime.Distributed; const DistributedCachingTransport distributedCachingTransport = DistributedCachingTransport.MassTransit; const MassTransitBroker massTransitBroker = MassTransitBroker.Memory; -const bool useMultitenancy = false; +const bool useMultitenancy = true; const bool useTenantsFromConfiguration = true; const bool useAgents = false; const bool useSecrets = false; @@ -662,7 +662,7 @@ services elsa.UseTenantHttpRouting(tenantHttpRouting => { // Override the tenant header name with a custom one. - tenantHttpRouting.WithTenantHeader("X-Company-Id"); + tenantHttpRouting.WithTenantHeader("X-Tenant-ID"); }); } diff --git a/src/common/Elsa.Mediator/Contracts/IRequestSender.cs b/src/common/Elsa.Mediator/Contracts/IRequestSender.cs index de23cd440..82c96fc74 100644 --- a/src/common/Elsa.Mediator/Contracts/IRequestSender.cs +++ b/src/common/Elsa.Mediator/Contracts/IRequestSender.cs @@ -12,5 +12,5 @@ public interface IRequestSender /// The cancellation token. /// The type of the response. /// The response. - Task SendAsync(IRequest request, CancellationToken cancellationToken = default); + Task SendAsync(IRequest request, CancellationToken cancellationToken = default); } \ No newline at end of file diff --git a/src/common/Elsa.Mediator/Middleware/Request/RequestContext.cs b/src/common/Elsa.Mediator/Middleware/Request/RequestContext.cs index b6093cb68..b6643a567 100644 --- a/src/common/Elsa.Mediator/Middleware/Request/RequestContext.cs +++ b/src/common/Elsa.Mediator/Middleware/Request/RequestContext.cs @@ -34,9 +34,9 @@ public class RequestContext /// Gets the cancellation token. /// public CancellationToken CancellationToken { get; init; } - + /// /// Gets the response the request handler. /// - public object? Response { get; set; } + public object Response { get; set; } = null!; } \ No newline at end of file diff --git a/src/common/Elsa.Mediator/Services/DefaultMediator.cs b/src/common/Elsa.Mediator/Services/DefaultMediator.cs index 21b3c36c0..23a68162b 100644 --- a/src/common/Elsa.Mediator/Services/DefaultMediator.cs +++ b/src/common/Elsa.Mediator/Services/DefaultMediator.cs @@ -41,13 +41,13 @@ public class DefaultMediator : IMediator } /// - public async Task SendAsync(IRequest request, CancellationToken cancellationToken = default) + public async Task SendAsync(IRequest request, CancellationToken cancellationToken = default) { var responseType = typeof(T); var context = new RequestContext(request, responseType, cancellationToken); await _requestPipeline.ExecuteAsync(context); - return (T?)context.Response; + return (T)context.Response; } /// diff --git a/src/modules/Elsa.OpenTelemetry/Middleware/OpenTelemetryTracingActivityExecutionMiddleware.cs b/src/modules/Elsa.OpenTelemetry/Middleware/OpenTelemetryTracingActivityExecutionMiddleware.cs index d5c5fdc7b..0045fde75 100644 --- a/src/modules/Elsa.OpenTelemetry/Middleware/OpenTelemetryTracingActivityExecutionMiddleware.cs +++ b/src/modules/Elsa.OpenTelemetry/Middleware/OpenTelemetryTracingActivityExecutionMiddleware.cs @@ -29,16 +29,16 @@ public class OpenTelemetryTracingActivityExecutionMiddleware(ActivityMiddlewareD span.SetTag("activity.type", activity.Type); span.SetTag("activity.name", activity.Name); span.SetTag("activityInstance.id", context.Id); - span.SetTag("activityExecution.startTimeUtc", span.StartTimeUtc); + span.SetTag("activityExecution.startTimeUtc", span.StartTimeUtc); span.SetTag("tenantId", context.WorkflowExecutionContext.Workflow.Identity.TenantId); - span.AddEvent(new ActivityEvent("Executing", tags: CreateStatusTags(context))); + span.AddEvent(new("Executing", tags: CreateStatusTags(context))); await next(context); if (context.Status == ActivityStatus.Faulted) { - span.AddEvent(new ActivityEvent("Faulted", tags: CreateStatusTags(context))); + span.AddEvent(new("Faulted", tags: CreateStatusTags(context))); span.SetStatus(ActivityStatusCode.Error); span.SetTag("error", true); span.SetTag("activityInstance.hasIncidents", true); @@ -51,7 +51,7 @@ public class OpenTelemetryTracingActivityExecutionMiddleware(ActivityMiddlewareD } else { - span.AddEvent(new ActivityEvent("Executed", tags: CreateStatusTags(context))); + span.AddEvent(new("Executed", tags: CreateStatusTags(context))); span.SetStatus(ActivityStatusCode.Ok); } @@ -62,7 +62,7 @@ public class OpenTelemetryTracingActivityExecutionMiddleware(ActivityMiddlewareD private ActivityTagsCollection CreateStatusTags(ActivityExecutionContext context) { - return new ActivityTagsCollection(new Dictionary + return new(new Dictionary { ["activityInstance.status"] = context.Status.ToString() }); diff --git a/src/modules/Elsa.Workflows.Management/Commands/SaveWorkflowDefinitionCommand.cs b/src/modules/Elsa.Workflows.Management/Commands/SaveWorkflowDefinitionCommand.cs new file mode 100644 index 000000000..0a7eac4a1 --- /dev/null +++ b/src/modules/Elsa.Workflows.Management/Commands/SaveWorkflowDefinitionCommand.cs @@ -0,0 +1,6 @@ +using Elsa.Mediator.Contracts; +using Elsa.Workflows.Management.Entities; + +namespace Elsa.Workflows.Management.Commands; + +public record SaveWorkflowDefinitionCommand(WorkflowDefinition WorkflowDefinition) : ICommand; \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Management/Features/CachingWorkflowDefinitionsFeature.cs b/src/modules/Elsa.Workflows.Management/Features/CachingWorkflowDefinitionsFeature.cs index d9d666b75..df939d6cc 100644 --- a/src/modules/Elsa.Workflows.Management/Features/CachingWorkflowDefinitionsFeature.cs +++ b/src/modules/Elsa.Workflows.Management/Features/CachingWorkflowDefinitionsFeature.cs @@ -1,7 +1,7 @@ using Elsa.Features.Abstractions; using Elsa.Features.Services; using Elsa.Workflows.Management.Handlers; -using Elsa.Workflows.Management.Handlers.Notification; +using Elsa.Workflows.Management.Handlers.Notifications; using Elsa.Workflows.Management.Services; using Elsa.Workflows.Management.Stores; using Microsoft.Extensions.DependencyInjection; diff --git a/src/modules/Elsa.Workflows.Management/Features/WorkflowDefinitionsFeature.cs b/src/modules/Elsa.Workflows.Management/Features/WorkflowDefinitionsFeature.cs index 916ad3c67..94c248b05 100644 --- a/src/modules/Elsa.Workflows.Management/Features/WorkflowDefinitionsFeature.cs +++ b/src/modules/Elsa.Workflows.Management/Features/WorkflowDefinitionsFeature.cs @@ -1,9 +1,8 @@ using Elsa.Features.Abstractions; using Elsa.Features.Services; using Elsa.Mediator.Contracts; -using Elsa.Workflows.Management.Entities; -using Elsa.Workflows.Management.Handlers.Request; -using Elsa.Workflows.Management.Requests; +using Elsa.Workflows.Management.Handlers.Commands; +using Elsa.Workflows.Management.Handlers.Requests; using Elsa.Workflows.Management.Stores; using Microsoft.Extensions.DependencyInjection; @@ -12,25 +11,26 @@ namespace Elsa.Workflows.Management.Features; /// /// Configures workflow definition storage. /// -public class WorkflowDefinitionsFeature : FeatureBase +public class WorkflowDefinitionsFeature(IModule module) : FeatureBase(module) { - /// - public WorkflowDefinitionsFeature(IModule module) : base(module) - { - } - /// /// The factory to create new instances of . /// public Func WorkflowDefinitionStore { get; set; } = sp => sp.GetRequiredService(); - public Func FindWorkflowDefinitionHandler { get; set; } = () => typeof(FindWorkflowDefinitionHandler); + public Type FindWorkflowDefinitionHandler { get; set; } = typeof(FindWorkflowDefinitionHandler); + public Type FindLastVersionOfWorkflowDefinitionHandler { get; set; } = typeof(FindLastVersionOfWorkflowDefinitionHandler); + public Type FindLatestOrPublishedWorkflowDefinitionsHandler { get; set; } = typeof(FindLatestOrPublishedWorkflowDefinitionsHandler); + public Type SaveWorkflowDefinitionHandler { get; set; } = typeof(SaveWorkflowDefinitionHandler); /// public override void Apply() { Services .AddScoped(WorkflowDefinitionStore) - .AddScoped(typeof(IRequestHandler), FindWorkflowDefinitionHandler()) + .AddScoped(typeof(IRequestHandler), FindWorkflowDefinitionHandler) + .AddScoped(typeof(IRequestHandler), FindLastVersionOfWorkflowDefinitionHandler) + .AddScoped(typeof(IRequestHandler), FindLatestOrPublishedWorkflowDefinitionsHandler) + .AddScoped(typeof(ICommandHandler), SaveWorkflowDefinitionHandler) ; } } \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Management/Features/WorkflowManagementFeature.cs b/src/modules/Elsa.Workflows.Management/Features/WorkflowManagementFeature.cs index 016bdb488..2fa0e0a8e 100644 --- a/src/modules/Elsa.Workflows.Management/Features/WorkflowManagementFeature.cs +++ b/src/modules/Elsa.Workflows.Management/Features/WorkflowManagementFeature.cs @@ -18,7 +18,7 @@ using Elsa.Workflows.Management.Compression; using Elsa.Workflows.Management.Contracts; using Elsa.Workflows.Management.Entities; using Elsa.Workflows.Management.Handlers; -using Elsa.Workflows.Management.Handlers.Notification; +using Elsa.Workflows.Management.Handlers.Notifications; using Elsa.Workflows.Management.Mappers; using Elsa.Workflows.Management.Materializers; using Elsa.Workflows.Management.Models; diff --git a/src/modules/Elsa.Workflows.Management/Handlers/Commands/SaveWorkflowDefinitionHandler.cs b/src/modules/Elsa.Workflows.Management/Handlers/Commands/SaveWorkflowDefinitionHandler.cs new file mode 100644 index 000000000..d2f14abc2 --- /dev/null +++ b/src/modules/Elsa.Workflows.Management/Handlers/Commands/SaveWorkflowDefinitionHandler.cs @@ -0,0 +1,14 @@ +using Elsa.Mediator.Contracts; +using Elsa.Mediator.Models; +using Elsa.Workflows.Management.Commands; + +namespace Elsa.Workflows.Management.Handlers.Commands; + +public class SaveWorkflowDefinitionHandler(IWorkflowDefinitionStore store) : ICommandHandler +{ + public async Task HandleAsync(SaveWorkflowDefinitionCommand command, CancellationToken cancellationToken) + { + await store.SaveAsync(command.WorkflowDefinition, cancellationToken); + return Unit.Instance; + } +} \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Management/Handlers/Notification/DeleteWorkflowInstances.cs b/src/modules/Elsa.Workflows.Management/Handlers/Notifications/DeleteWorkflowInstances.cs similarity index 97% rename from src/modules/Elsa.Workflows.Management/Handlers/Notification/DeleteWorkflowInstances.cs rename to src/modules/Elsa.Workflows.Management/Handlers/Notifications/DeleteWorkflowInstances.cs index 01429d33a..3fec484ce 100644 --- a/src/modules/Elsa.Workflows.Management/Handlers/Notification/DeleteWorkflowInstances.cs +++ b/src/modules/Elsa.Workflows.Management/Handlers/Notifications/DeleteWorkflowInstances.cs @@ -3,7 +3,7 @@ using Elsa.Workflows.Management.Filters; using Elsa.Workflows.Management.Notifications; using JetBrains.Annotations; -namespace Elsa.Workflows.Management.Handlers.Notification; +namespace Elsa.Workflows.Management.Handlers.Notifications; /// /// Deletes workflow instances when a workflow definition or version is deleted. diff --git a/src/modules/Elsa.Workflows.Management/Handlers/Notification/EvictWorkflowDefinitionServiceCache.cs b/src/modules/Elsa.Workflows.Management/Handlers/Notifications/EvictWorkflowDefinitionServiceCache.cs similarity index 97% rename from src/modules/Elsa.Workflows.Management/Handlers/Notification/EvictWorkflowDefinitionServiceCache.cs rename to src/modules/Elsa.Workflows.Management/Handlers/Notifications/EvictWorkflowDefinitionServiceCache.cs index 3cc7f15da..f3802e958 100644 --- a/src/modules/Elsa.Workflows.Management/Handlers/Notification/EvictWorkflowDefinitionServiceCache.cs +++ b/src/modules/Elsa.Workflows.Management/Handlers/Notifications/EvictWorkflowDefinitionServiceCache.cs @@ -2,7 +2,7 @@ using Elsa.Mediator.Contracts; using Elsa.Workflows.Management.Notifications; using JetBrains.Annotations; -namespace Elsa.Workflows.Management.Handlers.Notification; +namespace Elsa.Workflows.Management.Handlers.Notifications; /// /// A workflow definition notifications handler for evicting the cache of the workflow definition service. diff --git a/src/modules/Elsa.Workflows.Management/Handlers/Notification/RefreshActivityRegistry.cs b/src/modules/Elsa.Workflows.Management/Handlers/Notifications/RefreshActivityRegistry.cs similarity index 98% rename from src/modules/Elsa.Workflows.Management/Handlers/Notification/RefreshActivityRegistry.cs rename to src/modules/Elsa.Workflows.Management/Handlers/Notifications/RefreshActivityRegistry.cs index bad83d829..87aedf89c 100644 --- a/src/modules/Elsa.Workflows.Management/Handlers/Notification/RefreshActivityRegistry.cs +++ b/src/modules/Elsa.Workflows.Management/Handlers/Notifications/RefreshActivityRegistry.cs @@ -5,7 +5,7 @@ using Elsa.Workflows.Management.Entities; using Elsa.Workflows.Management.Notifications; using JetBrains.Annotations; -namespace Elsa.Workflows.Management.Handlers.Notification; +namespace Elsa.Workflows.Management.Handlers.Notifications; /// /// Refreshes the for the provider whenever an is published, retracted or deleted. diff --git a/src/modules/Elsa.Workflows.Management/Handlers/Notification/UpdateConsumingWorkflows.cs b/src/modules/Elsa.Workflows.Management/Handlers/Notifications/UpdateConsumingWorkflows.cs similarity index 92% rename from src/modules/Elsa.Workflows.Management/Handlers/Notification/UpdateConsumingWorkflows.cs rename to src/modules/Elsa.Workflows.Management/Handlers/Notifications/UpdateConsumingWorkflows.cs index 1e3819d4b..aee801e10 100644 --- a/src/modules/Elsa.Workflows.Management/Handlers/Notification/UpdateConsumingWorkflows.cs +++ b/src/modules/Elsa.Workflows.Management/Handlers/Notifications/UpdateConsumingWorkflows.cs @@ -2,7 +2,7 @@ using Elsa.Extensions; using Elsa.Mediator.Contracts; using Elsa.Workflows.Management.Notifications; -namespace Elsa.Workflows.Management.Handlers.Notification; +namespace Elsa.Workflows.Management.Handlers.Notifications; /// /// Updates consuming workflows when a workflow definition is published. diff --git a/src/modules/Elsa.Workflows.Management/Handlers/Notification/ValidateWorkflow.cs b/src/modules/Elsa.Workflows.Management/Handlers/Notifications/ValidateWorkflow.cs similarity index 95% rename from src/modules/Elsa.Workflows.Management/Handlers/Notification/ValidateWorkflow.cs rename to src/modules/Elsa.Workflows.Management/Handlers/Notifications/ValidateWorkflow.cs index 4e3ed5258..e56800f81 100644 --- a/src/modules/Elsa.Workflows.Management/Handlers/Notification/ValidateWorkflow.cs +++ b/src/modules/Elsa.Workflows.Management/Handlers/Notifications/ValidateWorkflow.cs @@ -3,7 +3,7 @@ using Elsa.Workflows.Management.Models; using Elsa.Workflows.Management.Notifications; using Elsa.Workflows.Models; -namespace Elsa.Workflows.Management.Handlers.Notification; +namespace Elsa.Workflows.Management.Handlers.Notifications; public class ValidateWorkflow : INotificationHandler { diff --git a/src/modules/Elsa.Workflows.Management/Handlers/Requests/FindLastVersionOfWorkflowDefinitionHandler.cs b/src/modules/Elsa.Workflows.Management/Handlers/Requests/FindLastVersionOfWorkflowDefinitionHandler.cs new file mode 100644 index 000000000..9b466293c --- /dev/null +++ b/src/modules/Elsa.Workflows.Management/Handlers/Requests/FindLastVersionOfWorkflowDefinitionHandler.cs @@ -0,0 +1,18 @@ +using Elsa.Mediator.Contracts; +using Elsa.Workflows.Management.Entities; +using Elsa.Workflows.Management.Filters; +using Elsa.Workflows.Management.Requests; + +namespace Elsa.Workflows.Management.Handlers.Requests; + +public class FindLastVersionOfWorkflowDefinitionHandler(IWorkflowDefinitionStore store) : IRequestHandler +{ + public Task HandleAsync(FindLastVersionOfWorkflowDefinitionRequest request, CancellationToken cancellationToken) + { + var filter = new WorkflowDefinitionFilter() + { + DefinitionId = request.DefinitionId, + }; + return store.FindLastVersionAsync(filter, cancellationToken: cancellationToken); + } +} \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Management/Handlers/Requests/FindLatestOrPublishedWorkflowDefinitionsHandler.cs b/src/modules/Elsa.Workflows.Management/Handlers/Requests/FindLatestOrPublishedWorkflowDefinitionsHandler.cs new file mode 100644 index 000000000..d074deafc --- /dev/null +++ b/src/modules/Elsa.Workflows.Management/Handlers/Requests/FindLatestOrPublishedWorkflowDefinitionsHandler.cs @@ -0,0 +1,31 @@ +using Elsa.Common.Entities; +using Elsa.Common.Models; +using Elsa.Mediator.Contracts; +using Elsa.Workflows.Management.Entities; +using Elsa.Workflows.Management.Filters; +using Elsa.Workflows.Management.Requests; + +namespace Elsa.Workflows.Management.Handlers.Requests; + +public class FindLatestOrPublishedWorkflowDefinitionsHandler(IWorkflowDefinitionStore store) : IRequestHandler> +{ + public Task HandleAsync(FindLastVersionOfWorkflowDefinitionRequest request, CancellationToken cancellationToken) + { + var filter = new WorkflowDefinitionFilter() + { + DefinitionId = request.DefinitionId, + }; + return store.FindLastVersionAsync(filter, cancellationToken: cancellationToken); + } + + public async Task> HandleAsync(FindLatestOrPublishedWorkflowDefinitionsRequest request, CancellationToken cancellationToken) + { + var filter = new WorkflowDefinitionFilter + { + DefinitionId = request.DefinitionId, + VersionOptions = VersionOptions.LatestOrPublished + }; + var order = new WorkflowDefinitionOrder(x => x.Version, OrderDirection.Descending); + return (await store.FindManyAsync(filter, order, cancellationToken)).ToList(); + } +} \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Management/Handlers/Request/FindWorkflowDefinitionHandler.cs b/src/modules/Elsa.Workflows.Management/Handlers/Requests/FindWorkflowDefinitionHandler.cs similarity index 94% rename from src/modules/Elsa.Workflows.Management/Handlers/Request/FindWorkflowDefinitionHandler.cs rename to src/modules/Elsa.Workflows.Management/Handlers/Requests/FindWorkflowDefinitionHandler.cs index 0febd0442..2c7b4dd05 100644 --- a/src/modules/Elsa.Workflows.Management/Handlers/Request/FindWorkflowDefinitionHandler.cs +++ b/src/modules/Elsa.Workflows.Management/Handlers/Requests/FindWorkflowDefinitionHandler.cs @@ -4,7 +4,7 @@ using Elsa.Workflows.Management.Entities; using Elsa.Workflows.Management.Filters; using Elsa.Workflows.Management.Requests; -namespace Elsa.Workflows.Management.Handlers.Request; +namespace Elsa.Workflows.Management.Handlers.Requests; public class FindWorkflowDefinitionHandler(IWorkflowDefinitionStore store) : IRequestHandler { diff --git a/src/modules/Elsa.Workflows.Management/Models/SaveWorkflowDefinitionRequest.cs b/src/modules/Elsa.Workflows.Management/Models/SaveWorkflowDefinitionRequest.cs index fad1f539a..14715b618 100644 --- a/src/modules/Elsa.Workflows.Management/Models/SaveWorkflowDefinitionRequest.cs +++ b/src/modules/Elsa.Workflows.Management/Models/SaveWorkflowDefinitionRequest.cs @@ -8,7 +8,7 @@ /// /// The workflow definition to save. /// - public WorkflowDefinitionModel Model { get; set; } = default!; + public WorkflowDefinitionModel Model { get; set; } = null!; /// /// Whether the workflow definition should be published. diff --git a/src/modules/Elsa.Workflows.Management/Requests/FindLastVersionOfWorkflowDefinitionRequest.cs b/src/modules/Elsa.Workflows.Management/Requests/FindLastVersionOfWorkflowDefinitionRequest.cs new file mode 100644 index 000000000..0139c3ce6 --- /dev/null +++ b/src/modules/Elsa.Workflows.Management/Requests/FindLastVersionOfWorkflowDefinitionRequest.cs @@ -0,0 +1,10 @@ +using Elsa.Mediator.Contracts; +using Elsa.Workflows.Management.Entities; + +namespace Elsa.Workflows.Management.Requests; + +/// +/// A request to find the last version of a workflow definition. +/// +/// The ID of the workflow definition. +public record FindLastVersionOfWorkflowDefinitionRequest(string DefinitionId) : IRequest; \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Management/Requests/FindLatestOrPublishedWorkflowDefinitionsRequest.cs b/src/modules/Elsa.Workflows.Management/Requests/FindLatestOrPublishedWorkflowDefinitionsRequest.cs new file mode 100644 index 000000000..99b60ca05 --- /dev/null +++ b/src/modules/Elsa.Workflows.Management/Requests/FindLatestOrPublishedWorkflowDefinitionsRequest.cs @@ -0,0 +1,9 @@ +using Elsa.Mediator.Contracts; +using Elsa.Workflows.Management.Entities; + +namespace Elsa.Workflows.Management.Requests; + +/// +/// A request to find the latest or published workflow definitions. +/// +public record FindLatestOrPublishedWorkflowDefinitionsRequest(string DefinitionId) : IRequest>; \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Management/Services/WorkflowDefinitionPublisher.cs b/src/modules/Elsa.Workflows.Management/Services/WorkflowDefinitionPublisher.cs index ab60f6558..55a5d114c 100644 --- a/src/modules/Elsa.Workflows.Management/Services/WorkflowDefinitionPublisher.cs +++ b/src/modules/Elsa.Workflows.Management/Services/WorkflowDefinitionPublisher.cs @@ -1,11 +1,9 @@ using Elsa.Common; -using Elsa.Common.Entities; using Elsa.Common.Models; using Elsa.Mediator.Contracts; using Elsa.Workflows.Activities; -using Elsa.Workflows.Management.Activities.WorkflowDefinitionActivity; +using Elsa.Workflows.Management.Commands; using Elsa.Workflows.Management.Entities; -using Elsa.Workflows.Management.Filters; using Elsa.Workflows.Management.Materializers; using Elsa.Workflows.Management.Models; using Elsa.Workflows.Management.Notifications; @@ -18,9 +16,9 @@ public class WorkflowDefinitionPublisher( IWorkflowDefinitionService workflowDefinitionService, IWorkflowDefinitionStore workflowDefinitionStore, IWorkflowValidator workflowValidator, - INotificationSender notificationSender, IIdentityGenerator identityGenerator, IActivitySerializer activitySerializer, + IMediator mediator, ISystemClock systemClock) : IWorkflowDefinitionPublisher { @@ -48,13 +46,8 @@ public class WorkflowDefinitionPublisher( /// public async Task PublishAsync(string definitionId, CancellationToken cancellationToken = default) { - var filter = new WorkflowDefinitionFilter - { - DefinitionId = definitionId, - VersionOptions = VersionOptions.Latest - }; - var definition = await workflowDefinitionStore.FindAsync(filter, cancellationToken); - + var definition = await mediator.SendAsync(new FindWorkflowDefinitionRequest(definitionId, VersionOptions.Latest), cancellationToken); + if (definition == null) return new(false, new List { @@ -73,47 +66,37 @@ public class WorkflowDefinitionPublisher( if (validationErrors.Any()) return new(false, validationErrors, new([])); - await notificationSender.SendAsync(new WorkflowDefinitionPublishing(definition), cancellationToken); + await mediator.SendAsync(new WorkflowDefinitionPublishing(definition), cancellationToken); var definitionId = definition.DefinitionId; // Reset current latest and published definitions. - var filter = new WorkflowDefinitionFilter - { - DefinitionId = definitionId, - VersionOptions = VersionOptions.LatestOrPublished - }; - var publishedWorkflows = await workflowDefinitionStore.FindManyAsync(filter, cancellationToken); + var publishedWorkflows = await mediator.SendAsync(new FindLatestOrPublishedWorkflowDefinitionsRequest(definitionId), cancellationToken); foreach (var publishedAndOrLatestWorkflow in publishedWorkflows) { var isPublished = publishedAndOrLatestWorkflow.IsPublished; publishedAndOrLatestWorkflow.IsPublished = false; publishedAndOrLatestWorkflow.IsLatest = false; - await workflowDefinitionStore.SaveAsync(publishedAndOrLatestWorkflow, cancellationToken); + await mediator.SendAsync(new SaveWorkflowDefinitionCommand(publishedAndOrLatestWorkflow), cancellationToken); if (isPublished) - await notificationSender.SendAsync(new WorkflowDefinitionVersionRetracted(publishedAndOrLatestWorkflow), cancellationToken); + await mediator.SendAsync(new WorkflowDefinitionVersionRetracted(publishedAndOrLatestWorkflow), cancellationToken); } - // Save the new published definition. + // Save the newly published definition. definition.IsPublished = true; definition = Initialize(definition); - await workflowDefinitionStore.SaveAsync(definition, cancellationToken); + await mediator.SendAsync(new SaveWorkflowDefinitionCommand(definition), cancellationToken); var affectedWorkflows = new AffectedWorkflows(new List()); - await notificationSender.SendAsync(new WorkflowDefinitionPublished(definition, affectedWorkflows), cancellationToken); + await mediator.SendAsync(new WorkflowDefinitionPublished(definition, affectedWorkflows), cancellationToken); return new(true, validationErrors, affectedWorkflows); } /// public async Task RetractAsync(string definitionId, CancellationToken cancellationToken = default) { - var filter = new WorkflowDefinitionFilter - { - DefinitionId = definitionId, - VersionOptions = VersionOptions.Published - }; - var definition = await workflowDefinitionStore.FindAsync(filter, cancellationToken); + var definition = await mediator.SendAsync(new FindWorkflowDefinitionRequest(definitionId, VersionOptions.Published), cancellationToken); if (definition == null) return null; @@ -129,26 +112,19 @@ public class WorkflowDefinitionPublisher( definition.IsPublished = false; - await notificationSender.SendAsync(new WorkflowDefinitionRetracting(definition), cancellationToken); - await workflowDefinitionStore.SaveAsync(definition, cancellationToken); - await notificationSender.SendAsync(new WorkflowDefinitionRetracted(definition), cancellationToken); + await mediator.SendAsync(new WorkflowDefinitionRetracting(definition), cancellationToken); + await mediator.SendAsync(new SaveWorkflowDefinitionCommand(definition), cancellationToken); + await mediator.SendAsync(new WorkflowDefinitionRetracted(definition), cancellationToken); return definition; } /// public async Task GetDraftAsync(string definitionId, VersionOptions versionOptions, CancellationToken cancellationToken = default) { - var filter = new WorkflowDefinitionFilter - { - DefinitionId = definitionId, - VersionOptions = versionOptions - }; - var order = new WorkflowDefinitionOrder(x => x.Version, OrderDirection.Descending); - var lastVersion = await workflowDefinitionStore.FindLastVersionAsync(new() - { - DefinitionId = definitionId - }, cancellationToken); - var definition = await workflowDefinitionStore.FindAsync(filter, order, cancellationToken) ?? lastVersion; + var findLastVersionRequest = new FindLastVersionOfWorkflowDefinitionRequest(definitionId); + var lastVersion = await mediator.SendAsync(findLastVersionRequest, cancellationToken); + var findRequest = new FindWorkflowDefinitionRequest(definitionId, versionOptions); + var definition = await mediator.SendAsync(findRequest, cancellationToken) ?? lastVersion; if (definition == null!) return null; @@ -172,11 +148,7 @@ public class WorkflowDefinitionPublisher( { var draft = definition; var definitionId = definition.DefinitionId; - var filter = new WorkflowDefinitionFilter - { - DefinitionId = definitionId - }; - var lastVersion = await workflowDefinitionStore.FindLastVersionAsync(filter, cancellationToken); + var lastVersion = await mediator.SendAsync(new FindLastVersionOfWorkflowDefinitionRequest(definitionId), cancellationToken); draft.Version = draft.Id == lastVersion?.Id ? lastVersion.Version : lastVersion?.Version + 1 ?? 1; draft.IsLatest = true; @@ -184,15 +156,13 @@ public class WorkflowDefinitionPublisher( await workflowDefinitionStore.SaveAsync(draft, cancellationToken); - if (lastVersion is null) - { - await notificationSender.SendAsync(new WorkflowDefinitionCreated(definition), cancellationToken); - } + if (lastVersion is null) + await mediator.SendAsync(new WorkflowDefinitionCreated(definition), cancellationToken); if (lastVersion is { IsPublished: true, IsLatest: true }) { lastVersion.IsLatest = false; - await workflowDefinitionStore.SaveAsync(lastVersion, cancellationToken); + await mediator.SendAsync(new SaveWorkflowDefinitionCommand(lastVersion), cancellationToken); } return draft; From 0c53c7095cbe7a11f692c4b15e1f6b230d719c88 Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Wed, 12 Feb 2025 23:13:04 +0100 Subject: [PATCH 02/10] Refactor workflow handling to use WorkflowDefinitionHandle. Replaced individual parameters with WorkflowDefinitionHandle in workflow-related methods and requests to centralize and simplify logic. Updated related services, models, and handlers to align with this new structure, improving code maintainability and consistency. --- .../GetByDefinitionId/Endpoint.cs | 4 +- .../Models/LinkedWorkflowDefinitionModel.cs | 5 +- .../WorkflowDefinitionHandleExtensions.cs | 17 +++ .../Requests/FindWorkflowDefinitionHandler.cs | 7 +- .../Models/WorkflowDefinitionModel.cs | 118 +++++++++++------- .../Requests/FindWorkflowDefinitionRequest.cs | 4 +- .../Services/WorkflowDefinitionPublisher.cs | 12 +- .../Services/WorkflowDefinitionService.cs | 40 +++--- 8 files changed, 124 insertions(+), 83 deletions(-) create mode 100644 src/modules/Elsa.Workflows.Management/Extensions/WorkflowDefinitionHandleExtensions.cs diff --git a/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowDefinitions/GetByDefinitionId/Endpoint.cs b/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowDefinitions/GetByDefinitionId/Endpoint.cs index 7f87e67f1..403000110 100644 --- a/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowDefinitions/GetByDefinitionId/Endpoint.cs +++ b/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowDefinitions/GetByDefinitionId/Endpoint.cs @@ -2,6 +2,7 @@ using Elsa.Abstractions; using Elsa.Common.Models; using Elsa.Mediator.Contracts; using Elsa.Workflows.Management.Requests; +using Elsa.Workflows.Models; using JetBrains.Annotations; namespace Elsa.Workflows.Api.Endpoints.WorkflowDefinitions.GetByDefinitionId; @@ -18,7 +19,8 @@ internal class GetByDefinitionId(IMediator mediator, IWorkflowDefinitionLinker l public override async Task HandleAsync(Request request, CancellationToken cancellationToken) { var versionOptions = request.VersionOptions != null ? VersionOptions.FromString(request.VersionOptions) : VersionOptions.Latest; - var findRequest = new FindWorkflowDefinitionRequest(request.DefinitionId, versionOptions); + var handle = WorkflowDefinitionHandle.ByDefinitionId(request.DefinitionId, versionOptions); + var findRequest = new FindWorkflowDefinitionRequest(handle); var definition = await mediator.SendAsync(findRequest, cancellationToken); if (definition == null) diff --git a/src/modules/Elsa.Workflows.Api/Models/LinkedWorkflowDefinitionModel.cs b/src/modules/Elsa.Workflows.Api/Models/LinkedWorkflowDefinitionModel.cs index ae4aa8233..d05d72d30 100644 --- a/src/modules/Elsa.Workflows.Api/Models/LinkedWorkflowDefinitionModel.cs +++ b/src/modules/Elsa.Workflows.Api/Models/LinkedWorkflowDefinitionModel.cs @@ -3,4 +3,7 @@ using Elsa.Workflows.Management.Models; namespace Elsa.Workflows.Api.Models; -public record LinkedWorkflowDefinitionModel(Link[]? Links) : WorkflowDefinitionModel; +public class LinkedWorkflowDefinitionModel(Link[]? links) : WorkflowDefinitionModel +{ + public Link[]? Links { get; init; } = links; +} diff --git a/src/modules/Elsa.Workflows.Management/Extensions/WorkflowDefinitionHandleExtensions.cs b/src/modules/Elsa.Workflows.Management/Extensions/WorkflowDefinitionHandleExtensions.cs new file mode 100644 index 000000000..9d277b76c --- /dev/null +++ b/src/modules/Elsa.Workflows.Management/Extensions/WorkflowDefinitionHandleExtensions.cs @@ -0,0 +1,17 @@ +using Elsa.Workflows.Management.Filters; +using Elsa.Workflows.Models; + +namespace Elsa.Workflows.Management; + +public static class WorkflowDefinitionHandleExtensions +{ + public static WorkflowDefinitionFilter ToFilter(this WorkflowDefinitionHandle handle) + { + return new() + { + DefinitionId = handle.DefinitionId, + Id = handle.DefinitionVersionId, + VersionOptions = handle.VersionOptions + }; + } +} \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Management/Handlers/Requests/FindWorkflowDefinitionHandler.cs b/src/modules/Elsa.Workflows.Management/Handlers/Requests/FindWorkflowDefinitionHandler.cs index 2c7b4dd05..ee5590568 100644 --- a/src/modules/Elsa.Workflows.Management/Handlers/Requests/FindWorkflowDefinitionHandler.cs +++ b/src/modules/Elsa.Workflows.Management/Handlers/Requests/FindWorkflowDefinitionHandler.cs @@ -10,12 +10,7 @@ public class FindWorkflowDefinitionHandler(IWorkflowDefinitionStore store) : IRe { public async Task HandleAsync(FindWorkflowDefinitionRequest request, CancellationToken cancellationToken) { - var filter = new WorkflowDefinitionFilter - { - DefinitionId = request.DefinitionId, - VersionOptions = request.VersionOptions - }; - + var filter = request.Handle.ToFilter(); var order = new WorkflowDefinitionOrder(x => x.Version, OrderDirection.Descending); var definition = (await store.FindManyAsync(filter, order, cancellationToken: cancellationToken)).FirstOrDefault(); return definition; diff --git a/src/modules/Elsa.Workflows.Management/Models/WorkflowDefinitionModel.cs b/src/modules/Elsa.Workflows.Management/Models/WorkflowDefinitionModel.cs index 6b1859334..598155655 100644 --- a/src/modules/Elsa.Workflows.Management/Models/WorkflowDefinitionModel.cs +++ b/src/modules/Elsa.Workflows.Management/Models/WorkflowDefinitionModel.cs @@ -7,52 +7,80 @@ namespace Elsa.Workflows.Management.Models; /// Represents a serializable workflow definition. /// [PublicAPI] -public record WorkflowDefinitionModel( - string Id, - string DefinitionId, - string? TenantId, - string? Name, - string? Description, - DateTimeOffset CreatedAt, - int Version, - Version? ToolVersion, - ICollection? Variables, - ICollection? Inputs, - ICollection? Outputs, - ICollection? Outcomes, - IDictionary? CustomProperties, - bool IsReadonly, - bool IsSystem, - bool IsLatest, - bool IsPublished, - WorkflowOptions? Options, - [property: Obsolete("Use Options.UsableAsActivity instead")] - bool? UsableAsActivity, - IActivity? Root -) +public class WorkflowDefinitionModel { - /// - public WorkflowDefinitionModel() : this( - default!, - default!, - default!, - default!, - default!, - default!, - default!, - default!, - default!, - default!, - default!, - default, - default!, - default!, - default!, - default!, - default!, - default!, - default!, - default!) + /// + /// Represents a serializable workflow definition. + /// + public WorkflowDefinitionModel() { } + + /// + /// Represents a serializable workflow definition. + /// + public WorkflowDefinitionModel(string id, + string definitionId, + string? tenantId, + string? name, + string? description, + DateTimeOffset createdAt, + int version, + Version? toolVersion, + ICollection? variables, + ICollection? inputs, + ICollection? outputs, + ICollection? outcomes, + IDictionary? customProperties, + bool isReadonly, + bool isSystem, + bool isLatest, + bool isPublished, + WorkflowOptions? options, + bool? usableAsActivity, + IActivity? root) { + Id = id; + DefinitionId = definitionId; + TenantId = tenantId; + Name = name; + Description = description; + CreatedAt = createdAt; + Version = version; + ToolVersion = toolVersion; + Variables = variables; + Inputs = inputs; + Outputs = outputs; + Outcomes = outcomes; + CustomProperties = customProperties; + IsReadonly = isReadonly; + IsSystem = isSystem; + IsLatest = isLatest; + IsPublished = isPublished; + Options = options; + UsableAsActivity = usableAsActivity; + Root = root; } + + public string Id { get; set; } + public string DefinitionId { get; set; } + public string? TenantId { get; set; } + public string? Name { get; set; } + public string? Description { get; set; } + public DateTimeOffset CreatedAt { get; set; } + public int Version { get; set; } + public Version? ToolVersion { get; set; } + public ICollection? Variables { get; set; } + public ICollection? Inputs { get; set; } + public ICollection? Outputs { get; set; } + public ICollection? Outcomes { get; set; } + public IDictionary? CustomProperties { get; set; } + public bool IsReadonly { get; set; } + public bool IsSystem { get; set; } + public bool IsLatest { get; set; } + public bool IsPublished { get; set; } + public WorkflowOptions? Options { get; set; } + + [Obsolete("Use Options.UsableAsActivity instead")] + public bool? UsableAsActivity { get; set; } + + public IActivity? Root { get; set; } } \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Management/Requests/FindWorkflowDefinitionRequest.cs b/src/modules/Elsa.Workflows.Management/Requests/FindWorkflowDefinitionRequest.cs index a539d5aa5..ec89a902c 100644 --- a/src/modules/Elsa.Workflows.Management/Requests/FindWorkflowDefinitionRequest.cs +++ b/src/modules/Elsa.Workflows.Management/Requests/FindWorkflowDefinitionRequest.cs @@ -1,6 +1,6 @@ -using Elsa.Common.Models; using Elsa.Mediator.Contracts; using Elsa.Workflows.Management.Entities; +using Elsa.Workflows.Models; namespace Elsa.Workflows.Management.Requests; @@ -9,4 +9,4 @@ namespace Elsa.Workflows.Management.Requests; /// /// The ID of the workflow definition. /// The version options. -public record FindWorkflowDefinitionRequest(string DefinitionId, VersionOptions VersionOptions) : IRequest; \ No newline at end of file +public record FindWorkflowDefinitionRequest(WorkflowDefinitionHandle Handle) : IRequest; \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Management/Services/WorkflowDefinitionPublisher.cs b/src/modules/Elsa.Workflows.Management/Services/WorkflowDefinitionPublisher.cs index 55a5d114c..3d42dae7c 100644 --- a/src/modules/Elsa.Workflows.Management/Services/WorkflowDefinitionPublisher.cs +++ b/src/modules/Elsa.Workflows.Management/Services/WorkflowDefinitionPublisher.cs @@ -8,6 +8,7 @@ using Elsa.Workflows.Management.Materializers; using Elsa.Workflows.Management.Models; using Elsa.Workflows.Management.Notifications; using Elsa.Workflows.Management.Requests; +using Elsa.Workflows.Models; namespace Elsa.Workflows.Management.Services; @@ -46,7 +47,8 @@ public class WorkflowDefinitionPublisher( /// public async Task PublishAsync(string definitionId, CancellationToken cancellationToken = default) { - var definition = await mediator.SendAsync(new FindWorkflowDefinitionRequest(definitionId, VersionOptions.Latest), cancellationToken); + var handle = WorkflowDefinitionHandle.ByDefinitionId(definitionId, VersionOptions.Latest); + var definition = await mediator.SendAsync(new FindWorkflowDefinitionRequest(handle), cancellationToken); if (definition == null) return new(false, new List @@ -96,7 +98,8 @@ public class WorkflowDefinitionPublisher( /// public async Task RetractAsync(string definitionId, CancellationToken cancellationToken = default) { - var definition = await mediator.SendAsync(new FindWorkflowDefinitionRequest(definitionId, VersionOptions.Published), cancellationToken); + var handle = WorkflowDefinitionHandle.ByDefinitionId(definitionId, VersionOptions.Published); + var definition = await mediator.SendAsync(new FindWorkflowDefinitionRequest(handle), cancellationToken); if (definition == null) return null; @@ -123,7 +126,8 @@ public class WorkflowDefinitionPublisher( { var findLastVersionRequest = new FindLastVersionOfWorkflowDefinitionRequest(definitionId); var lastVersion = await mediator.SendAsync(findLastVersionRequest, cancellationToken); - var findRequest = new FindWorkflowDefinitionRequest(definitionId, versionOptions); + var handle = WorkflowDefinitionHandle.ByDefinitionId(definitionId, versionOptions); + var findRequest = new FindWorkflowDefinitionRequest(handle); var definition = await mediator.SendAsync(findRequest, cancellationToken) ?? lastVersion; if (definition == null!) @@ -154,7 +158,7 @@ public class WorkflowDefinitionPublisher( draft.IsLatest = true; draft = Initialize(draft); - await workflowDefinitionStore.SaveAsync(draft, cancellationToken); + await mediator.SendAsync(new SaveWorkflowDefinitionCommand(draft), cancellationToken); if (lastVersion is null) await mediator.SendAsync(new WorkflowDefinitionCreated(definition), cancellationToken); diff --git a/src/modules/Elsa.Workflows.Management/Services/WorkflowDefinitionService.cs b/src/modules/Elsa.Workflows.Management/Services/WorkflowDefinitionService.cs index 3ecc175c4..9a7ddfd09 100644 --- a/src/modules/Elsa.Workflows.Management/Services/WorkflowDefinitionService.cs +++ b/src/modules/Elsa.Workflows.Management/Services/WorkflowDefinitionService.cs @@ -1,6 +1,8 @@ using Elsa.Common.Models; +using Elsa.Mediator.Contracts; using Elsa.Workflows.Management.Entities; using Elsa.Workflows.Management.Filters; +using Elsa.Workflows.Management.Requests; using Elsa.Workflows.Models; namespace Elsa.Workflows.Management.Services; @@ -9,6 +11,7 @@ namespace Elsa.Workflows.Management.Services; public class WorkflowDefinitionService( IWorkflowDefinitionStore workflowDefinitionStore, IWorkflowGraphBuilder workflowGraphBuilder, + IMediator mediator, Func> materializers) : IWorkflowDefinitionService { @@ -19,41 +22,30 @@ public class WorkflowDefinitionService( var materializer = workflowMaterializers.FirstOrDefault(x => x.Name == definition.MaterializerName); if (materializer == null) - throw new Exception("Provider not found"); + throw new("Provider not found"); var workflow = await materializer.MaterializeAsync(definition, cancellationToken); return await workflowGraphBuilder.BuildAsync(workflow, cancellationToken); } /// - public async Task FindWorkflowDefinitionAsync(string definitionId, VersionOptions versionOptions, CancellationToken cancellationToken = default) + public Task FindWorkflowDefinitionAsync(string definitionId, VersionOptions versionOptions, CancellationToken cancellationToken = default) { - var filter = new WorkflowDefinitionFilter - { - DefinitionId = definitionId, - VersionOptions = versionOptions - }; - return await workflowDefinitionStore.FindAsync(filter, cancellationToken); + var handle = WorkflowDefinitionHandle.ByDefinitionId(definitionId, versionOptions); + return FindWorkflowDefinitionAsync(handle, cancellationToken); } /// - public async Task FindWorkflowDefinitionAsync(string definitionVersionId, CancellationToken cancellationToken = default) + public Task FindWorkflowDefinitionAsync(string definitionVersionId, CancellationToken cancellationToken = default) { - var filter = new WorkflowDefinitionFilter - { - Id = definitionVersionId - }; - return await workflowDefinitionStore.FindAsync(filter, cancellationToken); + var handle = WorkflowDefinitionHandle.ByDefinitionVersionId(definitionVersionId); + return FindWorkflowDefinitionAsync(handle, cancellationToken); } /// public async Task FindWorkflowDefinitionAsync(WorkflowDefinitionHandle handle, CancellationToken cancellationToken = default) { - var filter = new WorkflowDefinitionFilter - { - DefinitionHandle = handle - }; - return await workflowDefinitionStore.FindAsync(filter, cancellationToken); + return await mediator.SendAsync(new FindWorkflowDefinitionRequest(handle), cancellationToken); } /// @@ -87,12 +79,12 @@ public class WorkflowDefinitionService( /// public async Task FindWorkflowGraphAsync(WorkflowDefinitionHandle definitionHandle, CancellationToken cancellationToken = default) { - var filter = new WorkflowDefinitionFilter - { - DefinitionHandle = definitionHandle - }; + var definition = await FindWorkflowDefinitionAsync(definitionHandle, cancellationToken); - return await FindWorkflowGraphAsync(filter, cancellationToken); + if (definition == null) + return null; + + return await MaterializeWorkflowAsync(definition, cancellationToken); } /// From 7b5aa6073dc0d401203eca86952e6107085db3e2 Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Thu, 13 Feb 2025 23:39:56 +0100 Subject: [PATCH 03/10] Add support for content item-backed workflow definitions Introduced a `ContentItemWorkflowDefinitionStore` and related mappers, extensions, and index updates to manage workflow definitions as content items. Refactored the system to replace existing handlers with a store-based approach and optimized querying and mapping workflows. --- src/modules/Elsa.Common/Entities/OrderDefinition.cs | 2 +- .../Models/WorkflowDefinitionSummary.cs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/modules/Elsa.Common/Entities/OrderDefinition.cs b/src/modules/Elsa.Common/Entities/OrderDefinition.cs index 5d1f1ed82..99108ec84 100644 --- a/src/modules/Elsa.Common/Entities/OrderDefinition.cs +++ b/src/modules/Elsa.Common/Entities/OrderDefinition.cs @@ -31,5 +31,5 @@ public class OrderDefinition /// /// The key selector to use to order the results. /// - public Expression> KeySelector { get; set; } = default!; + public Expression> KeySelector { get; set; } = null!; } \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Management/Models/WorkflowDefinitionSummary.cs b/src/modules/Elsa.Workflows.Management/Models/WorkflowDefinitionSummary.cs index 718cdd7e7..2f8a71a45 100644 --- a/src/modules/Elsa.Workflows.Management/Models/WorkflowDefinitionSummary.cs +++ b/src/modules/Elsa.Workflows.Management/Models/WorkflowDefinitionSummary.cs @@ -51,12 +51,12 @@ public class WorkflowDefinitionSummary /// /// The version ID of the workflow definition. /// - public string Id { get; set; } = default!; + public string Id { get; set; } = null!; /// /// The ID of the workflow definition. /// - public string DefinitionId { get; set; } = default!; + public string DefinitionId { get; set; } = null!; /// /// The name of the workflow definition. @@ -96,7 +96,7 @@ public class WorkflowDefinitionSummary /// /// The materializer name of the workflow definition. /// - public string MaterializerName { get; set; } = default!; + public string MaterializerName { get; set; } = null!; /// /// The timestamp when the workflow definition was created. From 0bca6913b977c9ec87888d7f75803dbf69d97a66 Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Fri, 14 Feb 2025 21:41:46 +0100 Subject: [PATCH 04/10] Introduce workflow definition publishing and serialization enhancements Implemented `NewAsync` method in `IWorkflowDefinitionPublisher` for asynchronous workflow creation. Added content item-based workflow definition management, including publishing, retracting, and draft management. Updated workflows API and integrated serialization improvements for better content handling. --- .../WorkflowDefinitions/Post/Endpoint.cs | 2 +- .../Contracts/IWorkflowDefinitionPublisher.cs | 11 +++++++++- .../Features/WorkflowManagementFeature.cs | 10 ++++++++- .../Services/WorkflowDefinitionImporter.cs | 2 +- .../Services/WorkflowDefinitionPublisher.cs | 22 +++++++++++++++++++ 5 files changed, 43 insertions(+), 4 deletions(-) diff --git a/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowDefinitions/Post/Endpoint.cs b/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowDefinitions/Post/Endpoint.cs index 4b1ba32f4..0c8f9c509 100644 --- a/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowDefinitions/Post/Endpoint.cs +++ b/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowDefinitions/Post/Endpoint.cs @@ -50,7 +50,7 @@ internal class Post( // Create a new workflow in case no existing definition was found. if (isNew) { - draft = workflowDefinitionPublisher.New(); + draft = await workflowDefinitionPublisher.NewAsync(cancellationToken: cancellationToken); if (!string.IsNullOrWhiteSpace(definitionId)) draft.DefinitionId = definitionId; diff --git a/src/modules/Elsa.Workflows.Management/Contracts/IWorkflowDefinitionPublisher.cs b/src/modules/Elsa.Workflows.Management/Contracts/IWorkflowDefinitionPublisher.cs index 40c767e3e..c9df69142 100644 --- a/src/modules/Elsa.Workflows.Management/Contracts/IWorkflowDefinitionPublisher.cs +++ b/src/modules/Elsa.Workflows.Management/Contracts/IWorkflowDefinitionPublisher.cs @@ -17,7 +17,16 @@ public interface IWorkflowDefinitionPublisher /// /// Optionally provide the root activity. If not specified, will be used/> /// The new workflow definition. - WorkflowDefinition New(IActivity? root = default); + [Obsolete( "Use NewAsync instead.", error: false)] + WorkflowDefinition New(IActivity? root = null); + + /// + /// Creates a new workflow definition. + /// + /// Optionally provide the root activity. If not specified, will be used/> + /// A cancellation token + /// The new workflow definition. + Task NewAsync(IActivity? root = null, CancellationToken cancellationToken = default); /// /// Publishes a workflow definition. diff --git a/src/modules/Elsa.Workflows.Management/Features/WorkflowManagementFeature.cs b/src/modules/Elsa.Workflows.Management/Features/WorkflowManagementFeature.cs index 2fa0e0a8e..f6e169575 100644 --- a/src/modules/Elsa.Workflows.Management/Features/WorkflowManagementFeature.cs +++ b/src/modules/Elsa.Workflows.Management/Features/WorkflowManagementFeature.cs @@ -50,6 +50,8 @@ public class WorkflowManagementFeature : FeatureBase private const string DataCategory = "Data"; private const string SystemCategory = "System"; + private Func _workflowDefinitionPublisher = sp => ActivatorUtilities.CreateInstance(sp); + private string CompressionAlgorithm { get; set; } = nameof(None); private LogPersistenceMode LogPersistenceMode { get; set; } = LogPersistenceMode.Include; private bool IsReadOnlyMode { get; set; } @@ -194,6 +196,12 @@ public class WorkflowManagementFeature : FeatureBase return this; } + public WorkflowManagementFeature WithWorkflowDefinitionPublisher(Func workflowDefinitionPublisher) + { + _workflowDefinitionPublisher = workflowDefinitionPublisher; + return this; + } + /// [RequiresUnreferencedCode("The assembly containing the specified marker type will be scanned for activity types.")] public override void Configure() @@ -214,7 +222,7 @@ public class WorkflowManagementFeature : FeatureBase .AddScoped() .AddScoped() .AddScoped() - .AddScoped() + .AddScoped(_workflowDefinitionPublisher) .AddScoped() .AddScoped() .AddScoped() diff --git a/src/modules/Elsa.Workflows.Management/Services/WorkflowDefinitionImporter.cs b/src/modules/Elsa.Workflows.Management/Services/WorkflowDefinitionImporter.cs index b63260be4..4081b3b2b 100644 --- a/src/modules/Elsa.Workflows.Management/Services/WorkflowDefinitionImporter.cs +++ b/src/modules/Elsa.Workflows.Management/Services/WorkflowDefinitionImporter.cs @@ -43,7 +43,7 @@ namespace Elsa.Workflows.Management.Services // Create a new workflow in case no existing definition was found. if (isNew) { - draft = _workflowDefinitionPublisher.New(); + draft = await _workflowDefinitionPublisher.NewAsync(cancellationToken: cancellationToken); if (!string.IsNullOrWhiteSpace(definitionId)) draft.DefinitionId = definitionId; diff --git a/src/modules/Elsa.Workflows.Management/Services/WorkflowDefinitionPublisher.cs b/src/modules/Elsa.Workflows.Management/Services/WorkflowDefinitionPublisher.cs index 3d42dae7c..42e52450f 100644 --- a/src/modules/Elsa.Workflows.Management/Services/WorkflowDefinitionPublisher.cs +++ b/src/modules/Elsa.Workflows.Management/Services/WorkflowDefinitionPublisher.cs @@ -44,6 +44,28 @@ public class WorkflowDefinitionPublisher( }; } + public Task NewAsync(IActivity? root = null, CancellationToken cancellationToken = default) + { + root ??= new Sequence(); + var id = identityGenerator.GenerateId(); + var definitionId = identityGenerator.GenerateId(); + const int version = 1; + + var workflowDefinition = new WorkflowDefinition + { + Id = id, + DefinitionId = definitionId, + Version = version, + IsLatest = true, + IsPublished = false, + CreatedAt = systemClock.UtcNow, + StringData = activitySerializer.Serialize(root), + MaterializerName = JsonWorkflowMaterializer.MaterializerName + }; + + return Task.FromResult(workflowDefinition); + } + /// public async Task PublishAsync(string definitionId, CancellationToken cancellationToken = default) { From 255bb26dff2a6be55790c4b57dc2bc4683bfa67d Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Sat, 15 Feb 2025 00:12:52 +0100 Subject: [PATCH 05/10] Refactor code for improved clarity and modern syntax Refactored multiple classes to align with modern C# coding practices such as object initializers and nullable type handling. Added a new base class for checklist dropdown providers to enhance UI configuration extensibility. Simplified constructors for feature classes by adopting record-like syntax, improving readability and maintainability. --- .../CheckList/DropDownOptionsProviderBase.cs | 35 +++++++++++++++++++ .../StaticCheckListOptionsProvider.cs | 2 +- .../Features/WorkflowManagementFeature.cs | 9 ++--- .../Services/TaskReporter.cs | 6 ++-- .../DependencyInjectionExtensions.cs | 6 ++-- src/modules/Elsa/Features/AppFeature.cs | 7 +--- 6 files changed, 45 insertions(+), 20 deletions(-) create mode 100644 src/modules/Elsa.Workflows.Core/UIHints/CheckList/DropDownOptionsProviderBase.cs diff --git a/src/modules/Elsa.Workflows.Core/UIHints/CheckList/DropDownOptionsProviderBase.cs b/src/modules/Elsa.Workflows.Core/UIHints/CheckList/DropDownOptionsProviderBase.cs new file mode 100644 index 000000000..3d68c217d --- /dev/null +++ b/src/modules/Elsa.Workflows.Core/UIHints/CheckList/DropDownOptionsProviderBase.cs @@ -0,0 +1,35 @@ +using System.Reflection; + +namespace Elsa.Workflows.UIHints.CheckList; + +/// +/// A base class for providing options to populate a checklist UI component. This class is intended to be inherited to implement +/// custom checklist data logic by overriding the `GetItemsAsync` method. +/// +public abstract class CheckListOptionsProviderBase : IPropertyUIHandler +{ + /// + public async ValueTask> GetUIPropertiesAsync(PropertyInfo propertyInfo, object? context, CancellationToken cancellationToken = default) + { + var items = await GetItemsAsync(propertyInfo, context, cancellationToken); + var props = new CheckListProps + { + CheckList = new() + { + Items = items.ToList() + } + }; + + var options = new Dictionary + { + [InputUIHints.CheckList] = props + }; + + return options; + } + + /// + /// Implement this to provide items to the dropdown list. + /// + protected abstract ValueTask> GetItemsAsync(PropertyInfo propertyInfo, object? context, CancellationToken cancellationToken); +} \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Core/UIHints/CheckList/StaticCheckListOptionsProvider.cs b/src/modules/Elsa.Workflows.Core/UIHints/CheckList/StaticCheckListOptionsProvider.cs index 38426e266..52ede3106 100644 --- a/src/modules/Elsa.Workflows.Core/UIHints/CheckList/StaticCheckListOptionsProvider.cs +++ b/src/modules/Elsa.Workflows.Core/UIHints/CheckList/StaticCheckListOptionsProvider.cs @@ -25,7 +25,7 @@ public class StaticCheckListOptionsProvider : IPropertyUIHandler var props = new CheckListProps { - CheckList = new CheckList + CheckList = new() { Items = selectListItems.ToList() } diff --git a/src/modules/Elsa.Workflows.Management/Features/WorkflowManagementFeature.cs b/src/modules/Elsa.Workflows.Management/Features/WorkflowManagementFeature.cs index f6e169575..71bf63fe0 100644 --- a/src/modules/Elsa.Workflows.Management/Features/WorkflowManagementFeature.cs +++ b/src/modules/Elsa.Workflows.Management/Features/WorkflowManagementFeature.cs @@ -41,8 +41,8 @@ namespace Elsa.Workflows.Management.Features; [DependsOn(typeof(WorkflowsFeature))] [DependsOn(typeof(WorkflowDefinitionsFeature))] [DependsOn(typeof(WorkflowInstancesFeature))] -[PublicAPI] -public class WorkflowManagementFeature : FeatureBase +[UsedImplicitly] +public class WorkflowManagementFeature(IModule module) : FeatureBase(module) { private const string PrimitivesCategory = "Primitives"; private const string LookupsCategory = "Lookups"; @@ -56,11 +56,6 @@ public class WorkflowManagementFeature : FeatureBase private LogPersistenceMode LogPersistenceMode { get; set; } = LogPersistenceMode.Include; private bool IsReadOnlyMode { get; set; } - /// - public WorkflowManagementFeature(IModule module) : base(module) - { - } - /// /// A set of activity types to make available to the system. /// diff --git a/src/modules/Elsa.Workflows.Runtime/Services/TaskReporter.cs b/src/modules/Elsa.Workflows.Runtime/Services/TaskReporter.cs index 560276b7e..54f86614f 100644 --- a/src/modules/Elsa.Workflows.Runtime/Services/TaskReporter.cs +++ b/src/modules/Elsa.Workflows.Runtime/Services/TaskReporter.cs @@ -11,9 +11,9 @@ public class TaskReporter(IBookmarkQueue bookmarkQueue, IStimulusHasher stimulus private static readonly string ActivityTypeName = ActivityTypeNameHelper.GenerateTypeName(); /// - public async Task ReportCompletionAsync(string taskId, object? result = default, CancellationToken cancellationToken = default) + public async Task ReportCompletionAsync(string taskId, object? result = null, CancellationToken cancellationToken = default) { - var stimulus = new RunTaskStimulus(taskId, default!); + var stimulus = new RunTaskStimulus(taskId, null!); var input = new Dictionary { @@ -24,7 +24,7 @@ public class TaskReporter(IBookmarkQueue bookmarkQueue, IStimulusHasher stimulus { ActivityTypeName = ActivityTypeName, StimulusHash = stimulusHasher.Hash(ActivityTypeName, stimulus), - Options = new ResumeBookmarkOptions + Options = new() { Input = input } diff --git a/src/modules/Elsa/Extensions/DependencyInjectionExtensions.cs b/src/modules/Elsa/Extensions/DependencyInjectionExtensions.cs index 51805f5a2..53b363962 100644 --- a/src/modules/Elsa/Extensions/DependencyInjectionExtensions.cs +++ b/src/modules/Elsa/Extensions/DependencyInjectionExtensions.cs @@ -15,10 +15,10 @@ public static class ModuleExtensions /// /// Creates a new Elsa module and adds the to it. /// - public static IModule AddElsa(this IServiceCollection services, Action? configure = default) + public static IModule AddElsa(this IServiceCollection services, Action? configure = null) { var module = services.GetOrCreateModule(); - module.Configure(app => app.Configurator = configure); + module.Configure(app => app.Configurator += configure); module.Apply(); return module; @@ -27,7 +27,7 @@ public static class ModuleExtensions /// /// Configures the Elsa module. /// - public static IModule ConfigureElsa(this IServiceCollection services, Action? configure = default) + public static IModule ConfigureElsa(this IServiceCollection services, Action? configure = null) { var module = services.GetOrCreateModule(); diff --git a/src/modules/Elsa/Features/AppFeature.cs b/src/modules/Elsa/Features/AppFeature.cs index 0a72a3396..775b19981 100644 --- a/src/modules/Elsa/Features/AppFeature.cs +++ b/src/modules/Elsa/Features/AppFeature.cs @@ -8,13 +8,8 @@ namespace Elsa.Features; /// A wrapper for invoking application-specific configuration, ensuring it is invoked lastly. /// [DependsOn(typeof(ElsaFeature))] -public class AppFeature : FeatureBase +public class AppFeature(IModule module) : FeatureBase(module) { - /// - public AppFeature(IModule module) : base(module) - { - } - /// /// The configurator to invoke. /// From 78eebe67ee27ff6a2334422a55d0f1c9af5fab5c Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Sat, 15 Feb 2025 11:54:18 +0100 Subject: [PATCH 06/10] Add `ElsaWorkflowInstanceStore` and workflow instance indexing Introduced `ElsaWorkflowInstanceStore` for managing workflow instances, including querying, saving, updating, and deleting. Added associated index provider and schema changes for indexing workflow instances, enabling efficient data retrieval. Updated startup and manifest files to register these new functionalities. --- .../WorkflowInstanceStoreExtensions.cs | 2 +- .../Filters/WorkflowInstanceFilter.cs | 2 +- .../Models/WorkflowInstanceSummary.cs | 37 +++++++++---------- 3 files changed, 20 insertions(+), 21 deletions(-) diff --git a/src/modules/Elsa.Workflows.Management/Extensions/WorkflowInstanceStoreExtensions.cs b/src/modules/Elsa.Workflows.Management/Extensions/WorkflowInstanceStoreExtensions.cs index f41d0325d..d26e07ea4 100644 --- a/src/modules/Elsa.Workflows.Management/Extensions/WorkflowInstanceStoreExtensions.cs +++ b/src/modules/Elsa.Workflows.Management/Extensions/WorkflowInstanceStoreExtensions.cs @@ -15,6 +15,6 @@ public static class WorkflowInstanceStoreExtensions /// public static async ValueTask FindAsync(this IWorkflowInstanceStore store, string id, CancellationToken cancellationToken = default) { - return await store.FindAsync(new WorkflowInstanceFilter{ Id = id }, cancellationToken); + return await store.FindAsync(new() { Id = id }, cancellationToken); } } \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Management/Filters/WorkflowInstanceFilter.cs b/src/modules/Elsa.Workflows.Management/Filters/WorkflowInstanceFilter.cs index f279df52a..15deb1e41 100644 --- a/src/modules/Elsa.Workflows.Management/Filters/WorkflowInstanceFilter.cs +++ b/src/modules/Elsa.Workflows.Management/Filters/WorkflowInstanceFilter.cs @@ -134,7 +134,7 @@ public class WorkflowInstanceFilter if (TimestampFilters != null) { - foreach (TimestampFilter timestampFilter in TimestampFilters) + foreach (var timestampFilter in TimestampFilters) { var column = timestampFilter.Column; var timestamp = timestampFilter.Timestamp; diff --git a/src/modules/Elsa.Workflows.Management/Models/WorkflowInstanceSummary.cs b/src/modules/Elsa.Workflows.Management/Models/WorkflowInstanceSummary.cs index 97346b24b..ef673127a 100644 --- a/src/modules/Elsa.Workflows.Management/Models/WorkflowInstanceSummary.cs +++ b/src/modules/Elsa.Workflows.Management/Models/WorkflowInstanceSummary.cs @@ -33,32 +33,31 @@ public class WorkflowInstanceSummary /// /// Returns a summary view of the specified . /// - public static Expression> FromInstanceExpression() - => workflowInstance => new WorkflowInstanceSummary - { - Id = workflowInstance.Id, - DefinitionId = workflowInstance.DefinitionId, - DefinitionVersionId = workflowInstance.DefinitionVersionId, - Version = workflowInstance.Version, - Status = workflowInstance.Status, - SubStatus = workflowInstance.SubStatus, - CorrelationId = workflowInstance.CorrelationId, - Name = workflowInstance.Name, - IncidentCount = workflowInstance.IncidentCount, - CreatedAt = workflowInstance.CreatedAt, - UpdatedAt = workflowInstance.UpdatedAt, - FinishedAt = workflowInstance.FinishedAt - }; + public static Expression> FromInstanceExpression() => workflowInstance => new() + { + Id = workflowInstance.Id, + DefinitionId = workflowInstance.DefinitionId, + DefinitionVersionId = workflowInstance.DefinitionVersionId, + Version = workflowInstance.Version, + Status = workflowInstance.Status, + SubStatus = workflowInstance.SubStatus, + CorrelationId = workflowInstance.CorrelationId, + Name = workflowInstance.Name, + IncidentCount = workflowInstance.IncidentCount, + CreatedAt = workflowInstance.CreatedAt, + UpdatedAt = workflowInstance.UpdatedAt, + FinishedAt = workflowInstance.FinishedAt + }; /// The ID of the workflow instance. - public string Id { get; set; } = default!; + public string Id { get; set; } = null!; /// The ID of the workflow definition. - public string DefinitionId { get; set; } = default!; + public string DefinitionId { get; set; } = null!; /// The version ID of the workflow definition. - public string DefinitionVersionId { get; set; } = default!; + public string DefinitionVersionId { get; set; } = null!; /// The version of the workflow definition. public int Version { get; set; } From 0f747b67fd46d2512a0d566bef3738e6428039d9 Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Sat, 15 Feb 2025 23:49:56 +0100 Subject: [PATCH 07/10] Refactor Elsa workflows and add support for triggers/bookmarks Reorganized data migrations and introduced new stores for triggers and bookmarks. Added index providers and migrations for improved queryability of triggers and bookmarks. Updated WorkflowInstance handling with additional configurations and dependency injections. --- src/modules/Elsa.Workflows.Core/Abstractions/Trigger.cs | 8 ++++---- .../Elsa.Workflows.Runtime/Entities/StoredTrigger.cs | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/modules/Elsa.Workflows.Core/Abstractions/Trigger.cs b/src/modules/Elsa.Workflows.Core/Abstractions/Trigger.cs index f26460b85..648fbe451 100644 --- a/src/modules/Elsa.Workflows.Core/Abstractions/Trigger.cs +++ b/src/modules/Elsa.Workflows.Core/Abstractions/Trigger.cs @@ -6,12 +6,12 @@ namespace Elsa.Workflows; public abstract class Trigger : Activity, ITrigger { /// - protected Trigger(string? source = default, int? line = default) : base(source, line) + protected Trigger(string? source = null, int? line = null) : base(source, line) { } /// - protected Trigger(string activityType, int version = 1, string? source = default, int? line = default) : base(activityType, version, source, line) + protected Trigger(string activityType, int version = 1, string? source = null, int? line = null) : base(activityType, version, source, line) { } @@ -39,11 +39,11 @@ public abstract class Trigger : Activity, ITrigger public abstract class Trigger : Activity, ITrigger { - protected Trigger(string? source = default, int? line = default) : base(source, line) + protected Trigger(string? source = null, int? line = null) : base(source, line) { } - protected Trigger(string activityType, int version = 1, string? source = default, int? line = default) : base(activityType, version, source, line) + protected Trigger(string activityType, int version = 1, string? source = null, int? line = null) : base(activityType, version, source, line) { } diff --git a/src/modules/Elsa.Workflows.Runtime/Entities/StoredTrigger.cs b/src/modules/Elsa.Workflows.Runtime/Entities/StoredTrigger.cs index 75fe494d6..d404fbfb9 100644 --- a/src/modules/Elsa.Workflows.Runtime/Entities/StoredTrigger.cs +++ b/src/modules/Elsa.Workflows.Runtime/Entities/StoredTrigger.cs @@ -10,22 +10,22 @@ public class StoredTrigger : Entity /// /// The ID of the workflow definition. /// - public string WorkflowDefinitionId { get; set; } = default!; + public string WorkflowDefinitionId { get; set; } = null!; /// /// The version ID of the workflow definition. /// - public string WorkflowDefinitionVersionId { get; set; } = default!; + public string WorkflowDefinitionVersionId { get; set; } = null!; /// /// The name of the trigger. /// - public string Name { get; set; } = default!; + public string Name { get; set; } = null!; /// /// The ID of the activity associated with the trigger. /// - public string ActivityId { get; set; } = default!; + public string ActivityId { get; set; } = null!; /// /// The hash of the trigger. From dff6e14093ec8ca478f9480c4819ab762fafdbfb Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Sun, 16 Feb 2025 20:27:07 +0100 Subject: [PATCH 08/10] Add WorkflowExecutionLogStore implementation and migrations Introduced a new `WorkflowExecutionLogStore` to handle workflow execution log persistence. Added necessary extensions, indexes, migrations, and updated the startup configuration to register the new store and index provider. This enables querying, saving, and managing workflow execution logs in the OrchardCore project. --- .../Entities/WorkflowExecutionLogRecord.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/modules/Elsa.Workflows.Runtime/Entities/WorkflowExecutionLogRecord.cs b/src/modules/Elsa.Workflows.Runtime/Entities/WorkflowExecutionLogRecord.cs index 87460d6b6..54c412db1 100644 --- a/src/modules/Elsa.Workflows.Runtime/Entities/WorkflowExecutionLogRecord.cs +++ b/src/modules/Elsa.Workflows.Runtime/Entities/WorkflowExecutionLogRecord.cs @@ -11,17 +11,17 @@ public class WorkflowExecutionLogRecord : Entity, ILogRecord /// /// The ID of the workflow definition. /// - public string WorkflowDefinitionId { get; set; } = default!; + public string WorkflowDefinitionId { get; set; } = null!; /// /// The version ID of the workflow definition. /// - public string WorkflowDefinitionVersionId { get; set; } = default!; + public string WorkflowDefinitionVersionId { get; set; } = null!; /// /// The ID of the workflow instance. /// - public string WorkflowInstanceId { get; set; } = default!; + public string WorkflowInstanceId { get; set; } = null!; /// /// The version of the workflow definition. @@ -31,7 +31,7 @@ public class WorkflowExecutionLogRecord : Entity, ILogRecord /// /// The ID of the activity instance. /// - public string ActivityInstanceId { get; set; } = default!; + public string ActivityInstanceId { get; set; } = null!; /// /// The ID of the parent activity instance. @@ -41,12 +41,12 @@ public class WorkflowExecutionLogRecord : Entity, ILogRecord /// /// The ID of the activity. /// - public string ActivityId { get; set; } = default!; + public string ActivityId { get; set; } = null!; /// /// The type of the activity. /// - public string ActivityType { get; set; } = default!; + public string ActivityType { get; set; } = null!; /// /// The version of the activity type. @@ -61,7 +61,7 @@ public class WorkflowExecutionLogRecord : Entity, ILogRecord /// /// The unique ID of the node within the workflow graph. /// - public string ActivityNodeId { get; set; } = default!; + public string ActivityNodeId { get; set; } = null!; /// /// The time stamp of the log entry. From bc005a7586695567e17fa704cb4034b496f30ac3 Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Tue, 18 Feb 2025 19:39:37 +0100 Subject: [PATCH 09/10] Remove mediator-based workflow definition handling Replaced mediator requests and commands with direct store interactions in the workflow management module. Simplified service dependencies, eliminating unnecessary handlers and requests, and streamlined workflow-related operations for better maintainability and performance. --- src/modules/Elsa.Http/Features/HttpFeature.cs | 1 - .../GetByDefinitionId/Endpoint.cs | 10 ++-- .../Commands/SaveWorkflowDefinitionCommand.cs | 6 --- .../Features/WorkflowDefinitionsFeature.cs | 14 +----- .../Commands/SaveWorkflowDefinitionHandler.cs | 14 ------ ...dLastVersionOfWorkflowDefinitionHandler.cs | 18 ------- ...stOrPublishedWorkflowDefinitionsHandler.cs | 31 ------------ .../Requests/FindWorkflowDefinitionHandler.cs | 18 ------- ...dLastVersionOfWorkflowDefinitionRequest.cs | 10 ---- ...stOrPublishedWorkflowDefinitionsRequest.cs | 9 ---- .../Requests/FindWorkflowDefinitionRequest.cs | 12 ----- .../Services/WorkflowDefinitionPublisher.cs | 50 ++++++++++++------- .../Services/WorkflowDefinitionService.cs | 6 +-- 13 files changed, 39 insertions(+), 160 deletions(-) delete mode 100644 src/modules/Elsa.Workflows.Management/Commands/SaveWorkflowDefinitionCommand.cs delete mode 100644 src/modules/Elsa.Workflows.Management/Handlers/Commands/SaveWorkflowDefinitionHandler.cs delete mode 100644 src/modules/Elsa.Workflows.Management/Handlers/Requests/FindLastVersionOfWorkflowDefinitionHandler.cs delete mode 100644 src/modules/Elsa.Workflows.Management/Handlers/Requests/FindLatestOrPublishedWorkflowDefinitionsHandler.cs delete mode 100644 src/modules/Elsa.Workflows.Management/Handlers/Requests/FindWorkflowDefinitionHandler.cs delete mode 100644 src/modules/Elsa.Workflows.Management/Requests/FindLastVersionOfWorkflowDefinitionRequest.cs delete mode 100644 src/modules/Elsa.Workflows.Management/Requests/FindLatestOrPublishedWorkflowDefinitionsRequest.cs delete mode 100644 src/modules/Elsa.Workflows.Management/Requests/FindWorkflowDefinitionRequest.cs diff --git a/src/modules/Elsa.Http/Features/HttpFeature.cs b/src/modules/Elsa.Http/Features/HttpFeature.cs index 65c0e42d8..085a5c4ab 100644 --- a/src/modules/Elsa.Http/Features/HttpFeature.cs +++ b/src/modules/Elsa.Http/Features/HttpFeature.cs @@ -15,7 +15,6 @@ using Elsa.Http.Services; using Elsa.Http.Tasks; using Elsa.Http.UIHints; using Elsa.Workflows; -using Elsa.Workflows.Management.Requests; using FluentStorage; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.StaticFiles; diff --git a/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowDefinitions/GetByDefinitionId/Endpoint.cs b/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowDefinitions/GetByDefinitionId/Endpoint.cs index 403000110..4b2fbe9e7 100644 --- a/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowDefinitions/GetByDefinitionId/Endpoint.cs +++ b/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowDefinitions/GetByDefinitionId/Endpoint.cs @@ -1,14 +1,13 @@ using Elsa.Abstractions; using Elsa.Common.Models; -using Elsa.Mediator.Contracts; -using Elsa.Workflows.Management.Requests; +using Elsa.Workflows.Management; using Elsa.Workflows.Models; using JetBrains.Annotations; namespace Elsa.Workflows.Api.Endpoints.WorkflowDefinitions.GetByDefinitionId; [PublicAPI] -internal class GetByDefinitionId(IMediator mediator, IWorkflowDefinitionLinker linker) : ElsaEndpoint +internal class GetByDefinitionId(IWorkflowDefinitionStore store, IWorkflowDefinitionLinker linker) : ElsaEndpoint { public override void Configure() { @@ -19,9 +18,8 @@ internal class GetByDefinitionId(IMediator mediator, IWorkflowDefinitionLinker l public override async Task HandleAsync(Request request, CancellationToken cancellationToken) { var versionOptions = request.VersionOptions != null ? VersionOptions.FromString(request.VersionOptions) : VersionOptions.Latest; - var handle = WorkflowDefinitionHandle.ByDefinitionId(request.DefinitionId, versionOptions); - var findRequest = new FindWorkflowDefinitionRequest(handle); - var definition = await mediator.SendAsync(findRequest, cancellationToken); + var filter = WorkflowDefinitionHandle.ByDefinitionId(request.DefinitionId, versionOptions).ToFilter(); + var definition = await store.FindAsync(filter, cancellationToken); if (definition == null) { diff --git a/src/modules/Elsa.Workflows.Management/Commands/SaveWorkflowDefinitionCommand.cs b/src/modules/Elsa.Workflows.Management/Commands/SaveWorkflowDefinitionCommand.cs deleted file mode 100644 index 0a7eac4a1..000000000 --- a/src/modules/Elsa.Workflows.Management/Commands/SaveWorkflowDefinitionCommand.cs +++ /dev/null @@ -1,6 +0,0 @@ -using Elsa.Mediator.Contracts; -using Elsa.Workflows.Management.Entities; - -namespace Elsa.Workflows.Management.Commands; - -public record SaveWorkflowDefinitionCommand(WorkflowDefinition WorkflowDefinition) : ICommand; \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Management/Features/WorkflowDefinitionsFeature.cs b/src/modules/Elsa.Workflows.Management/Features/WorkflowDefinitionsFeature.cs index 94c248b05..8425c991d 100644 --- a/src/modules/Elsa.Workflows.Management/Features/WorkflowDefinitionsFeature.cs +++ b/src/modules/Elsa.Workflows.Management/Features/WorkflowDefinitionsFeature.cs @@ -1,8 +1,5 @@ using Elsa.Features.Abstractions; using Elsa.Features.Services; -using Elsa.Mediator.Contracts; -using Elsa.Workflows.Management.Handlers.Commands; -using Elsa.Workflows.Management.Handlers.Requests; using Elsa.Workflows.Management.Stores; using Microsoft.Extensions.DependencyInjection; @@ -17,20 +14,11 @@ public class WorkflowDefinitionsFeature(IModule module) : FeatureBase(module) /// The factory to create new instances of . /// public Func WorkflowDefinitionStore { get; set; } = sp => sp.GetRequiredService(); - public Type FindWorkflowDefinitionHandler { get; set; } = typeof(FindWorkflowDefinitionHandler); - public Type FindLastVersionOfWorkflowDefinitionHandler { get; set; } = typeof(FindLastVersionOfWorkflowDefinitionHandler); - public Type FindLatestOrPublishedWorkflowDefinitionsHandler { get; set; } = typeof(FindLatestOrPublishedWorkflowDefinitionsHandler); - public Type SaveWorkflowDefinitionHandler { get; set; } = typeof(SaveWorkflowDefinitionHandler); /// public override void Apply() { Services - .AddScoped(WorkflowDefinitionStore) - .AddScoped(typeof(IRequestHandler), FindWorkflowDefinitionHandler) - .AddScoped(typeof(IRequestHandler), FindLastVersionOfWorkflowDefinitionHandler) - .AddScoped(typeof(IRequestHandler), FindLatestOrPublishedWorkflowDefinitionsHandler) - .AddScoped(typeof(ICommandHandler), SaveWorkflowDefinitionHandler) - ; + .AddScoped(WorkflowDefinitionStore); } } \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Management/Handlers/Commands/SaveWorkflowDefinitionHandler.cs b/src/modules/Elsa.Workflows.Management/Handlers/Commands/SaveWorkflowDefinitionHandler.cs deleted file mode 100644 index d2f14abc2..000000000 --- a/src/modules/Elsa.Workflows.Management/Handlers/Commands/SaveWorkflowDefinitionHandler.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Elsa.Mediator.Contracts; -using Elsa.Mediator.Models; -using Elsa.Workflows.Management.Commands; - -namespace Elsa.Workflows.Management.Handlers.Commands; - -public class SaveWorkflowDefinitionHandler(IWorkflowDefinitionStore store) : ICommandHandler -{ - public async Task HandleAsync(SaveWorkflowDefinitionCommand command, CancellationToken cancellationToken) - { - await store.SaveAsync(command.WorkflowDefinition, cancellationToken); - return Unit.Instance; - } -} \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Management/Handlers/Requests/FindLastVersionOfWorkflowDefinitionHandler.cs b/src/modules/Elsa.Workflows.Management/Handlers/Requests/FindLastVersionOfWorkflowDefinitionHandler.cs deleted file mode 100644 index 9b466293c..000000000 --- a/src/modules/Elsa.Workflows.Management/Handlers/Requests/FindLastVersionOfWorkflowDefinitionHandler.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Elsa.Mediator.Contracts; -using Elsa.Workflows.Management.Entities; -using Elsa.Workflows.Management.Filters; -using Elsa.Workflows.Management.Requests; - -namespace Elsa.Workflows.Management.Handlers.Requests; - -public class FindLastVersionOfWorkflowDefinitionHandler(IWorkflowDefinitionStore store) : IRequestHandler -{ - public Task HandleAsync(FindLastVersionOfWorkflowDefinitionRequest request, CancellationToken cancellationToken) - { - var filter = new WorkflowDefinitionFilter() - { - DefinitionId = request.DefinitionId, - }; - return store.FindLastVersionAsync(filter, cancellationToken: cancellationToken); - } -} \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Management/Handlers/Requests/FindLatestOrPublishedWorkflowDefinitionsHandler.cs b/src/modules/Elsa.Workflows.Management/Handlers/Requests/FindLatestOrPublishedWorkflowDefinitionsHandler.cs deleted file mode 100644 index d074deafc..000000000 --- a/src/modules/Elsa.Workflows.Management/Handlers/Requests/FindLatestOrPublishedWorkflowDefinitionsHandler.cs +++ /dev/null @@ -1,31 +0,0 @@ -using Elsa.Common.Entities; -using Elsa.Common.Models; -using Elsa.Mediator.Contracts; -using Elsa.Workflows.Management.Entities; -using Elsa.Workflows.Management.Filters; -using Elsa.Workflows.Management.Requests; - -namespace Elsa.Workflows.Management.Handlers.Requests; - -public class FindLatestOrPublishedWorkflowDefinitionsHandler(IWorkflowDefinitionStore store) : IRequestHandler> -{ - public Task HandleAsync(FindLastVersionOfWorkflowDefinitionRequest request, CancellationToken cancellationToken) - { - var filter = new WorkflowDefinitionFilter() - { - DefinitionId = request.DefinitionId, - }; - return store.FindLastVersionAsync(filter, cancellationToken: cancellationToken); - } - - public async Task> HandleAsync(FindLatestOrPublishedWorkflowDefinitionsRequest request, CancellationToken cancellationToken) - { - var filter = new WorkflowDefinitionFilter - { - DefinitionId = request.DefinitionId, - VersionOptions = VersionOptions.LatestOrPublished - }; - var order = new WorkflowDefinitionOrder(x => x.Version, OrderDirection.Descending); - return (await store.FindManyAsync(filter, order, cancellationToken)).ToList(); - } -} \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Management/Handlers/Requests/FindWorkflowDefinitionHandler.cs b/src/modules/Elsa.Workflows.Management/Handlers/Requests/FindWorkflowDefinitionHandler.cs deleted file mode 100644 index ee5590568..000000000 --- a/src/modules/Elsa.Workflows.Management/Handlers/Requests/FindWorkflowDefinitionHandler.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Elsa.Common.Entities; -using Elsa.Mediator.Contracts; -using Elsa.Workflows.Management.Entities; -using Elsa.Workflows.Management.Filters; -using Elsa.Workflows.Management.Requests; - -namespace Elsa.Workflows.Management.Handlers.Requests; - -public class FindWorkflowDefinitionHandler(IWorkflowDefinitionStore store) : IRequestHandler -{ - public async Task HandleAsync(FindWorkflowDefinitionRequest request, CancellationToken cancellationToken) - { - var filter = request.Handle.ToFilter(); - var order = new WorkflowDefinitionOrder(x => x.Version, OrderDirection.Descending); - var definition = (await store.FindManyAsync(filter, order, cancellationToken: cancellationToken)).FirstOrDefault(); - return definition; - } -} \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Management/Requests/FindLastVersionOfWorkflowDefinitionRequest.cs b/src/modules/Elsa.Workflows.Management/Requests/FindLastVersionOfWorkflowDefinitionRequest.cs deleted file mode 100644 index 0139c3ce6..000000000 --- a/src/modules/Elsa.Workflows.Management/Requests/FindLastVersionOfWorkflowDefinitionRequest.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Elsa.Mediator.Contracts; -using Elsa.Workflows.Management.Entities; - -namespace Elsa.Workflows.Management.Requests; - -/// -/// A request to find the last version of a workflow definition. -/// -/// The ID of the workflow definition. -public record FindLastVersionOfWorkflowDefinitionRequest(string DefinitionId) : IRequest; \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Management/Requests/FindLatestOrPublishedWorkflowDefinitionsRequest.cs b/src/modules/Elsa.Workflows.Management/Requests/FindLatestOrPublishedWorkflowDefinitionsRequest.cs deleted file mode 100644 index 99b60ca05..000000000 --- a/src/modules/Elsa.Workflows.Management/Requests/FindLatestOrPublishedWorkflowDefinitionsRequest.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Elsa.Mediator.Contracts; -using Elsa.Workflows.Management.Entities; - -namespace Elsa.Workflows.Management.Requests; - -/// -/// A request to find the latest or published workflow definitions. -/// -public record FindLatestOrPublishedWorkflowDefinitionsRequest(string DefinitionId) : IRequest>; \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Management/Requests/FindWorkflowDefinitionRequest.cs b/src/modules/Elsa.Workflows.Management/Requests/FindWorkflowDefinitionRequest.cs deleted file mode 100644 index ec89a902c..000000000 --- a/src/modules/Elsa.Workflows.Management/Requests/FindWorkflowDefinitionRequest.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Elsa.Mediator.Contracts; -using Elsa.Workflows.Management.Entities; -using Elsa.Workflows.Models; - -namespace Elsa.Workflows.Management.Requests; - -/// -/// A request to find a workflow definition. -/// -/// The ID of the workflow definition. -/// The version options. -public record FindWorkflowDefinitionRequest(WorkflowDefinitionHandle Handle) : IRequest; \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Management/Services/WorkflowDefinitionPublisher.cs b/src/modules/Elsa.Workflows.Management/Services/WorkflowDefinitionPublisher.cs index 42e52450f..88640c69d 100644 --- a/src/modules/Elsa.Workflows.Management/Services/WorkflowDefinitionPublisher.cs +++ b/src/modules/Elsa.Workflows.Management/Services/WorkflowDefinitionPublisher.cs @@ -1,13 +1,13 @@ using Elsa.Common; +using Elsa.Common.Entities; using Elsa.Common.Models; using Elsa.Mediator.Contracts; using Elsa.Workflows.Activities; -using Elsa.Workflows.Management.Commands; using Elsa.Workflows.Management.Entities; +using Elsa.Workflows.Management.Filters; using Elsa.Workflows.Management.Materializers; using Elsa.Workflows.Management.Models; using Elsa.Workflows.Management.Notifications; -using Elsa.Workflows.Management.Requests; using Elsa.Workflows.Models; namespace Elsa.Workflows.Management.Services; @@ -69,8 +69,8 @@ public class WorkflowDefinitionPublisher( /// public async Task PublishAsync(string definitionId, CancellationToken cancellationToken = default) { - var handle = WorkflowDefinitionHandle.ByDefinitionId(definitionId, VersionOptions.Latest); - var definition = await mediator.SendAsync(new FindWorkflowDefinitionRequest(handle), cancellationToken); + var filter = WorkflowDefinitionHandle.ByDefinitionId(definitionId, VersionOptions.Latest).ToFilter(); + var definition = await workflowDefinitionStore.FindAsync(filter, cancellationToken); if (definition == null) return new(false, new List @@ -94,14 +94,18 @@ public class WorkflowDefinitionPublisher( var definitionId = definition.DefinitionId; // Reset current latest and published definitions. - var publishedWorkflows = await mediator.SendAsync(new FindLatestOrPublishedWorkflowDefinitionsRequest(definitionId), cancellationToken); + var publishedWorkflows = await workflowDefinitionStore.FindManyAsync(new() + { + DefinitionId = definitionId, + VersionOptions = VersionOptions.LatestOrPublished + }, cancellationToken); foreach (var publishedAndOrLatestWorkflow in publishedWorkflows) { var isPublished = publishedAndOrLatestWorkflow.IsPublished; publishedAndOrLatestWorkflow.IsPublished = false; publishedAndOrLatestWorkflow.IsLatest = false; - await mediator.SendAsync(new SaveWorkflowDefinitionCommand(publishedAndOrLatestWorkflow), cancellationToken); + await workflowDefinitionStore.SaveAsync(publishedAndOrLatestWorkflow, cancellationToken); if (isPublished) await mediator.SendAsync(new WorkflowDefinitionVersionRetracted(publishedAndOrLatestWorkflow), cancellationToken); @@ -110,7 +114,7 @@ public class WorkflowDefinitionPublisher( // Save the newly published definition. definition.IsPublished = true; definition = Initialize(definition); - await mediator.SendAsync(new SaveWorkflowDefinitionCommand(definition), cancellationToken); + await workflowDefinitionStore.SaveAsync(definition, cancellationToken); var affectedWorkflows = new AffectedWorkflows(new List()); await mediator.SendAsync(new WorkflowDefinitionPublished(definition, affectedWorkflows), cancellationToken); @@ -120,8 +124,8 @@ public class WorkflowDefinitionPublisher( /// public async Task RetractAsync(string definitionId, CancellationToken cancellationToken = default) { - var handle = WorkflowDefinitionHandle.ByDefinitionId(definitionId, VersionOptions.Published); - var definition = await mediator.SendAsync(new FindWorkflowDefinitionRequest(handle), cancellationToken); + var filter = WorkflowDefinitionHandle.ByDefinitionId(definitionId, VersionOptions.Published).ToFilter(); + var definition = await workflowDefinitionStore.FindAsync(filter, cancellationToken); if (definition == null) return null; @@ -138,7 +142,7 @@ public class WorkflowDefinitionPublisher( definition.IsPublished = false; await mediator.SendAsync(new WorkflowDefinitionRetracting(definition), cancellationToken); - await mediator.SendAsync(new SaveWorkflowDefinitionCommand(definition), cancellationToken); + await workflowDefinitionStore.SaveAsync(definition, cancellationToken); await mediator.SendAsync(new WorkflowDefinitionRetracted(definition), cancellationToken); return definition; } @@ -146,11 +150,17 @@ public class WorkflowDefinitionPublisher( /// public async Task GetDraftAsync(string definitionId, VersionOptions versionOptions, CancellationToken cancellationToken = default) { - var findLastVersionRequest = new FindLastVersionOfWorkflowDefinitionRequest(definitionId); - var lastVersion = await mediator.SendAsync(findLastVersionRequest, cancellationToken); - var handle = WorkflowDefinitionHandle.ByDefinitionId(definitionId, versionOptions); - var findRequest = new FindWorkflowDefinitionRequest(handle); - var definition = await mediator.SendAsync(findRequest, cancellationToken) ?? lastVersion; + var filter = new WorkflowDefinitionFilter + { + DefinitionId = definitionId, + VersionOptions = versionOptions + }; + var order = new WorkflowDefinitionOrder(x => x.Version, OrderDirection.Descending); + var lastVersion = await workflowDefinitionStore.FindLastVersionAsync(new() + { + DefinitionId = definitionId + }, cancellationToken); + var definition = await workflowDefinitionStore.FindAsync(filter, order, cancellationToken) ?? lastVersion; if (definition == null!) return null; @@ -174,13 +184,17 @@ public class WorkflowDefinitionPublisher( { var draft = definition; var definitionId = definition.DefinitionId; - var lastVersion = await mediator.SendAsync(new FindLastVersionOfWorkflowDefinitionRequest(definitionId), cancellationToken); + var filter = new WorkflowDefinitionFilter + { + DefinitionId = definitionId + }; + var lastVersion = await workflowDefinitionStore.FindLastVersionAsync(filter, cancellationToken); draft.Version = draft.Id == lastVersion?.Id ? lastVersion.Version : lastVersion?.Version + 1 ?? 1; draft.IsLatest = true; draft = Initialize(draft); - await mediator.SendAsync(new SaveWorkflowDefinitionCommand(draft), cancellationToken); + await workflowDefinitionStore.SaveAsync(draft, cancellationToken); if (lastVersion is null) await mediator.SendAsync(new WorkflowDefinitionCreated(definition), cancellationToken); @@ -188,7 +202,7 @@ public class WorkflowDefinitionPublisher( if (lastVersion is { IsPublished: true, IsLatest: true }) { lastVersion.IsLatest = false; - await mediator.SendAsync(new SaveWorkflowDefinitionCommand(lastVersion), cancellationToken); + await workflowDefinitionStore.SaveAsync(lastVersion, cancellationToken); } return draft; diff --git a/src/modules/Elsa.Workflows.Management/Services/WorkflowDefinitionService.cs b/src/modules/Elsa.Workflows.Management/Services/WorkflowDefinitionService.cs index 9a7ddfd09..bd3435846 100644 --- a/src/modules/Elsa.Workflows.Management/Services/WorkflowDefinitionService.cs +++ b/src/modules/Elsa.Workflows.Management/Services/WorkflowDefinitionService.cs @@ -1,8 +1,6 @@ using Elsa.Common.Models; -using Elsa.Mediator.Contracts; using Elsa.Workflows.Management.Entities; using Elsa.Workflows.Management.Filters; -using Elsa.Workflows.Management.Requests; using Elsa.Workflows.Models; namespace Elsa.Workflows.Management.Services; @@ -11,7 +9,6 @@ namespace Elsa.Workflows.Management.Services; public class WorkflowDefinitionService( IWorkflowDefinitionStore workflowDefinitionStore, IWorkflowGraphBuilder workflowGraphBuilder, - IMediator mediator, Func> materializers) : IWorkflowDefinitionService { @@ -45,7 +42,8 @@ public class WorkflowDefinitionService( /// public async Task FindWorkflowDefinitionAsync(WorkflowDefinitionHandle handle, CancellationToken cancellationToken = default) { - return await mediator.SendAsync(new FindWorkflowDefinitionRequest(handle), cancellationToken); + var filter = handle.ToFilter(); + return await workflowDefinitionStore.FindAsync(filter, cancellationToken); } /// From 5dc9d4f40f35aa952fbf359d82326ddc9c6939db Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Tue, 18 Feb 2025 19:44:31 +0100 Subject: [PATCH 10/10] Disable multitenancy in Elsa.Server.Web Multitenancy has been turned off by setting `useMultitenancy` to `false`. This change might simplify the current configuration and reduce complexity for single-tenant use cases. --- src/apps/Elsa.Server.Web/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/apps/Elsa.Server.Web/Program.cs b/src/apps/Elsa.Server.Web/Program.cs index 0affa0e82..d26da8a7e 100644 --- a/src/apps/Elsa.Server.Web/Program.cs +++ b/src/apps/Elsa.Server.Web/Program.cs @@ -91,7 +91,7 @@ const bool useSignalR = false; // Disabled until Elsa Studio sends authenticated const WorkflowRuntime workflowRuntime = WorkflowRuntime.Distributed; const DistributedCachingTransport distributedCachingTransport = DistributedCachingTransport.MassTransit; const MassTransitBroker massTransitBroker = MassTransitBroker.Memory; -const bool useMultitenancy = true; +const bool useMultitenancy = false; const bool useTenantsFromConfiguration = true; const bool useAgents = false; const bool useSecrets = false;