diff --git a/src/bundles/Elsa.Server.Web/Endpoints/Api1/Get/Endpoint.cs b/src/bundles/Elsa.Server.Web/Endpoints/Api1/Get/Endpoint.cs deleted file mode 100644 index 5d727bb33..000000000 --- a/src/bundles/Elsa.Server.Web/Endpoints/Api1/Get/Endpoint.cs +++ /dev/null @@ -1,33 +0,0 @@ -using Elsa.Abstractions; -using Elsa.Workflows.Activities; -using Elsa.Workflows.Models; -using Elsa.Workflows.Runtime.Contracts; -using Elsa.Workflows.Runtime.Parameters; -using JetBrains.Annotations; - -namespace Elsa.Server.Web.Endpoints.Api1.Get; - -/// -/// Returns a message. -/// -[UsedImplicitly] -public class Get : ElsaEndpointWithoutRequest -{ - /// - public override void Configure() - { - Get("/api-1"); - AllowAnonymous(); - } - - /// - public override async Task HandleAsync(CancellationToken ct) - { - await Task.Delay(1000, ct); - var response = new - { - Message = "OK" - }; - await SendOkAsync(response, ct); - } -} \ No newline at end of file diff --git a/src/bundles/Elsa.Server.Web/Endpoints/DynamicWorkflows/Post/Endpoint.cs b/src/bundles/Elsa.Server.Web/Endpoints/DynamicWorkflows/Post/Endpoint.cs deleted file mode 100644 index f72fb576e..000000000 --- a/src/bundles/Elsa.Server.Web/Endpoints/DynamicWorkflows/Post/Endpoint.cs +++ /dev/null @@ -1,37 +0,0 @@ -using Elsa.Abstractions; -using Elsa.Workflows.Activities; -using Elsa.Workflows.Models; -using Elsa.Workflows.Runtime.Contracts; -using Elsa.Workflows.Runtime.Options; -using Elsa.Workflows.Runtime.Parameters; - -namespace Elsa.Server.Web.Endpoints.DynamicWorkflows.Post; - -public class Post(IWorkflowRegistry workflowRegistry, IWorkflowRuntime workflowRuntime) : ElsaEndpointWithoutRequest -{ - public override void Configure() - { - Post("/dynamic-workflows"); - AllowAnonymous(); - } - - public override async Task HandleAsync(CancellationToken ct) - { - var workflow = new Workflow - { - Identity = new WorkflowIdentity("DynamicWorkflow1", 1, "DynamicWorkflow1:v1"), - Root = new Sequence - { - Activities = - { - new WriteLine("Step 1"), - new WriteLine("Step 2"), - new WriteLine("Step 3") - } - } - }; - - await workflowRegistry.RegisterAsync(workflow, ct); - await workflowRuntime.StartWorkflowAsync("DynamicWorkflow1", new StartWorkflowRuntimeParams()); - } -} \ No newline at end of file diff --git a/src/bundles/Elsa.Server.Web/Program.cs b/src/bundles/Elsa.Server.Web/Program.cs index 9c41549da..c4f259015 100644 --- a/src/bundles/Elsa.Server.Web/Program.cs +++ b/src/bundles/Elsa.Server.Web/Program.cs @@ -48,6 +48,7 @@ const bool runEFCoreMigrations = true; const bool useMemoryStores = false; const bool useCaching = true; const bool useReadOnlyMode = false; +const bool useSignalR = true; const DistributedCachingTransport distributedCachingTransport = DistributedCachingTransport.MassTransit; const MassTransitBroker useMassTransitBroker = MassTransitBroker.Memory; @@ -250,7 +251,6 @@ services { api.AddFastEndpointsAssembly(); }) - .UseRealTimeWorkflows() .UseCSharp(options => { options.AppendScript("string Greet(string name) => $\"Hello {name}!\";"); @@ -322,6 +322,11 @@ services elsa.UseQuartz(quartz => { quartz.UseSqlite(sqliteConnectionString); }); } + if (useSignalR) + { + elsa.UseRealTimeWorkflows(); + } + if (useMassTransit) { elsa.UseMassTransit(massTransit => @@ -418,19 +423,18 @@ if (app.Environment.IsDevelopment()) } // SignalR. -app.UseWorkflowsSignalRHubs(); +if (useSignalR) +{ + app.UseWorkflowsSignalRHubs(); +} // Run. app.Run(); -/// /// The main entry point for the application made public for end to end testing. -/// [UsedImplicitly] public partial class Program { - /// /// Set by the test runner to configure the module for testing. - /// public static Action? ConfigureForTest { get; set; } -} +} \ No newline at end of file diff --git a/src/clients/Elsa.Api.Client/Resources/WorkflowInstances/Contracts/IWorkflowInstancesApi.cs b/src/clients/Elsa.Api.Client/Resources/WorkflowInstances/Contracts/IWorkflowInstancesApi.cs index 4a50a368c..a225e57ad 100644 --- a/src/clients/Elsa.Api.Client/Resources/WorkflowInstances/Contracts/IWorkflowInstancesApi.cs +++ b/src/clients/Elsa.Api.Client/Resources/WorkflowInstances/Contracts/IWorkflowInstancesApi.cs @@ -1,6 +1,7 @@ using Elsa.Api.Client.Resources.WorkflowDefinitions.Models; using Elsa.Api.Client.Resources.WorkflowInstances.Models; using Elsa.Api.Client.Resources.WorkflowInstances.Requests; +using Elsa.Api.Client.Resources.WorkflowInstances.Responses; using Elsa.Api.Client.Shared.Models; using Refit; @@ -47,14 +48,13 @@ public interface IWorkflowInstancesApi Task> GetFilteredJournalAsync(string workflowInstanceId, GetFilteredJournalRequest? filter, int? skip = default, int? take = default, CancellationToken cancellationToken = default); /// - /// Checks if there are updates in the journal for a specific workflow instance. + /// Returns the execution state of the specified workflow instance. /// - /// The ID of the workflow instance for which to check for updates. - /// The request containing the ID and time from which to check for updates. + /// The ID of the workflow instance for which to return its execution state. /// The cancellation token. - /// Returns whether updates are available for the journal. - [Get("/workflow-instances/{workflowInstanceId}/journal/has-updates")] - Task HasJournalUpdates(string workflowInstanceId, [Query]HasJournalUpdateRequest request, CancellationToken cancellationToken = default); + /// Returns a response containing the execution state. + [Get("/workflow-instances/{workflowInstanceId}/execution-state")] + Task GetExecutionStateAsync(string workflowInstanceId, CancellationToken cancellationToken = default); /// /// Deletes a workflow instance. diff --git a/src/clients/Elsa.Api.Client/Resources/WorkflowInstances/Models/HasJournalUpdateRequest.cs b/src/clients/Elsa.Api.Client/Resources/WorkflowInstances/Models/HasJournalUpdateRequest.cs deleted file mode 100644 index 6efb34477..000000000 --- a/src/clients/Elsa.Api.Client/Resources/WorkflowInstances/Models/HasJournalUpdateRequest.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Elsa.Api.Client.Resources.WorkflowInstances.Models; - -/// A request to update a journal for a workflow instance. -public class HasJournalUpdateRequest -{ - /// The unique identifier of a workflow instance. - public string WorkflowInstanceId { get; set; } = default!; - - /// The start date for checking for updates in the workflow instance journal. - public DateTimeOffset UpdatesSince { get; set; } -} \ No newline at end of file diff --git a/src/clients/Elsa.Api.Client/Resources/WorkflowInstances/Responses/WorkflowInstanceExecutionStateResponse.cs b/src/clients/Elsa.Api.Client/Resources/WorkflowInstances/Responses/WorkflowInstanceExecutionStateResponse.cs new file mode 100644 index 000000000..e3262e1f8 --- /dev/null +++ b/src/clients/Elsa.Api.Client/Resources/WorkflowInstances/Responses/WorkflowInstanceExecutionStateResponse.cs @@ -0,0 +1,6 @@ +using Elsa.Api.Client.Resources.WorkflowInstances.Enums; + +namespace Elsa.Api.Client.Resources.WorkflowInstances.Responses; + +/// Represents the response containing the last updated timestamp of a workflow instance. +public record WorkflowInstanceExecutionStateResponse(WorkflowStatus Status, WorkflowSubStatus WorkflowSubStatus, DateTimeOffset UpdatedAt); \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowInstances/ExecutionState/Endpoint.cs b/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowInstances/ExecutionState/Endpoint.cs new file mode 100644 index 000000000..96ccc853d --- /dev/null +++ b/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowInstances/ExecutionState/Endpoint.cs @@ -0,0 +1,37 @@ +using Elsa.Abstractions; +using Elsa.Extensions; +using Elsa.Workflows.Management.Contracts; +using JetBrains.Annotations; + +namespace Elsa.Workflows.Api.Endpoints.WorkflowInstances.ExecutionState; + +/// Returns the execution state of the specified workflow instance. +[PublicAPI] +internal class ExecutionState(IWorkflowInstanceStore store) : ElsaEndpoint +{ + /// + public override void Configure() + { + Get("/workflow-instances/{id}/execution-state"); + ConfigurePermissions("read:workflow-instances"); + } + + /// + public override async Task HandleAsync(Request request, CancellationToken cancellationToken) + { + var workflowInstance = await store.FindAsync(request.WorkflowInstanceId, cancellationToken); + + if (workflowInstance == null) + { + await SendNotFoundAsync(cancellationToken); + return; + } + + var response = new Response( + workflowInstance.Status, + workflowInstance.SubStatus, + workflowInstance.UpdatedAt); + + await SendOkAsync(response, cancellationToken); + } +} \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowInstances/ExecutionState/Models.cs b/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowInstances/ExecutionState/Models.cs new file mode 100644 index 000000000..4a0605239 --- /dev/null +++ b/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowInstances/ExecutionState/Models.cs @@ -0,0 +1,13 @@ +using FastEndpoints; + +namespace Elsa.Workflows.Api.Endpoints.WorkflowInstances.ExecutionState; + +/// The request to check the execution state of the workflow instance. +public class Request +{ + /// The unique identifier of a workflow instance. + [BindFrom("id")] public string WorkflowInstanceId { get; set; } = default!; +} + +/// Represents the response containing the last updated timestamp of a workflow instance. +public record Response(WorkflowStatus Status, WorkflowSubStatus SubStatus, DateTimeOffset UpdatedAt); \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowInstances/Journal/HasUpdates/Endpoint.cs b/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowInstances/Journal/HasUpdates/Endpoint.cs deleted file mode 100644 index a535e25c9..000000000 --- a/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowInstances/Journal/HasUpdates/Endpoint.cs +++ /dev/null @@ -1,32 +0,0 @@ -using Elsa.Abstractions; -using Elsa.Common.Entities; -using Elsa.Common.Models; -using Elsa.Workflows.Runtime.Contracts; -using Elsa.Workflows.Runtime.Filters; -using Elsa.Workflows.Runtime.OrderDefinitions; -using JetBrains.Annotations; - -namespace Elsa.Workflows.Api.Endpoints.WorkflowInstances.Journal.HasUpdates; - -/// Endpoint that checks if there are updates for a workflow instance. -[PublicAPI] -internal class HasUpdates(IWorkflowExecutionLogStore store) : ElsaEndpoint -{ - /// - public override void Configure() - { - Get("/workflow-instances/{id}/journal/has-updates"); - ConfigurePermissions("read:workflow-instances"); - } - - /// - public override async Task ExecuteAsync(Request request, CancellationToken cancellationToken) - { - var pageArgs = PageArgs.From(1, 1, 0, 1); - var filter = new WorkflowExecutionLogRecordFilter { WorkflowInstanceId = request.WorkflowInstanceId }; - var order = new WorkflowExecutionLogRecordOrder(x => x.Sequence, OrderDirection.Descending); - var pageOfRecords = await store.FindManyAsync(filter, pageArgs, order, cancellationToken); - - return pageOfRecords.Items.Any(item => item.Timestamp >= request.UpdatesSince); - } -} \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowInstances/Journal/HasUpdates/Models.cs b/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowInstances/Journal/HasUpdates/Models.cs deleted file mode 100644 index eefafc6d0..000000000 --- a/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowInstances/Journal/HasUpdates/Models.cs +++ /dev/null @@ -1,13 +0,0 @@ -using FastEndpoints; - -namespace Elsa.Workflows.Api.Endpoints.WorkflowInstances.Journal.HasUpdates; - -/// The request to check if there are updates for a workflow instance journal. -public class Request -{ - /// The unique identifier of a workflow instance. - [BindFrom("id")] public string WorkflowInstanceId { get; set; } = default!; - - /// The start date for checking for updates in the workflow instance journal. - public DateTimeOffset UpdatesSince { get; set; } -} \ No newline at end of file