diff --git a/src/clients/Elsa.Api.Client/Resources/WorkflowDefinitions/Contracts/IWorkflowDefinitionsApi.cs b/src/clients/Elsa.Api.Client/Resources/WorkflowDefinitions/Contracts/IWorkflowDefinitionsApi.cs
index 463258a64..81749ac28 100644
--- a/src/clients/Elsa.Api.Client/Resources/WorkflowDefinitions/Contracts/IWorkflowDefinitionsApi.cs
+++ b/src/clients/Elsa.Api.Client/Resources/WorkflowDefinitions/Contracts/IWorkflowDefinitionsApi.cs
@@ -69,10 +69,18 @@ public interface IWorkflowDefinitionsApi
///
/// Deletes a workflow definition.
///
- /// The ID of the workflow definition to delete.
+ /// The definition ID of the workflow definition, and all of its versions, to delete.
/// The cancellation token.
[Delete("/workflow-definitions/{definitionId}")]
Task DeleteAsync(string definitionId, CancellationToken cancellationToken = default);
+
+ ///
+ /// Deletes a workflow definition version.
+ ///
+ /// The ID of a specific workflow definition version to delete.
+ /// The cancellation token.
+ [Delete("/workflow-definition-versions/{id}")]
+ Task DeleteVersionAsync(string id, CancellationToken cancellationToken = default);
///
/// Publishes a workflow definition.
diff --git a/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowDefinitions/DeleteVersion/Endpoint.cs b/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowDefinitions/DeleteVersion/Endpoint.cs
new file mode 100644
index 000000000..3eb832d06
--- /dev/null
+++ b/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowDefinitions/DeleteVersion/Endpoint.cs
@@ -0,0 +1,32 @@
+using Elsa.Abstractions;
+using Elsa.Workflows.Management.Contracts;
+using JetBrains.Annotations;
+
+namespace Elsa.Workflows.Api.Endpoints.WorkflowDefinitions.DeleteVersion;
+
+[PublicAPI]
+internal class DeleteVersion : ElsaEndpoint
+{
+ private readonly IWorkflowDefinitionManager _workflowDefinitionManager;
+
+ public DeleteVersion(IWorkflowDefinitionManager workflowDefinitionManager)
+ {
+ _workflowDefinitionManager = workflowDefinitionManager;
+ }
+
+ public override void Configure()
+ {
+ Delete("/workflow-definition-versions/{id}");
+ ConfigurePermissions("delete:workflow-definitions");
+ }
+
+ public override async Task HandleAsync(Request request, CancellationToken cancellationToken)
+ {
+ var deleted = await _workflowDefinitionManager.DeleteByIdAsync(request.Id, cancellationToken);
+
+ if (!deleted)
+ await SendNotFoundAsync(cancellationToken);
+ else
+ await SendNoContentAsync(cancellationToken);
+ }
+}
\ No newline at end of file
diff --git a/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowDefinitions/DeleteVersion/Models.cs b/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowDefinitions/DeleteVersion/Models.cs
new file mode 100644
index 000000000..3726b984f
--- /dev/null
+++ b/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowDefinitions/DeleteVersion/Models.cs
@@ -0,0 +1,6 @@
+namespace Elsa.Workflows.Api.Endpoints.WorkflowDefinitions.DeleteVersion;
+
+internal class Request
+{
+ public string Id { get; set; } = default!;
+}
\ No newline at end of file
diff --git a/src/modules/Elsa.Workflows.Management/Contracts/IWorkflowDefinitionManager.cs b/src/modules/Elsa.Workflows.Management/Contracts/IWorkflowDefinitionManager.cs
index c4f2faac3..3a075ef70 100644
--- a/src/modules/Elsa.Workflows.Management/Contracts/IWorkflowDefinitionManager.cs
+++ b/src/modules/Elsa.Workflows.Management/Contracts/IWorkflowDefinitionManager.cs
@@ -15,6 +15,13 @@ public interface IWorkflowDefinitionManager
/// The number of deleted workflow definitions.
Task DeleteByDefinitionIdAsync(string definitionId, CancellationToken cancellationToken = default);
+ ///
+ /// Deletes a specific version of a workflow definition.
+ ///
+ /// The ID of the workflow definition version.
+ /// The cancellation token.
+ /// True if the workflow definition version was deleted, otherwise false.
+ Task DeleteByIdAsync(string id, CancellationToken cancellationToken = default);
///
/// Deletes a specific workflow definition version.
diff --git a/src/modules/Elsa.Workflows.Management/Notifications/WorkflowDefinitionVersionDeleted.cs b/src/modules/Elsa.Workflows.Management/Notifications/WorkflowDefinitionVersionDeleted.cs
index 39b5296ee..552afcf4c 100644
--- a/src/modules/Elsa.Workflows.Management/Notifications/WorkflowDefinitionVersionDeleted.cs
+++ b/src/modules/Elsa.Workflows.Management/Notifications/WorkflowDefinitionVersionDeleted.cs
@@ -1,4 +1,5 @@
using Elsa.Mediator.Contracts;
+using Elsa.Workflows.Management.Entities;
using JetBrains.Annotations;
namespace Elsa.Workflows.Management.Notifications;
@@ -6,7 +7,5 @@ namespace Elsa.Workflows.Management.Notifications;
///
/// A notification that is sent when a workflow definition version is deleted.
///
-/// The ID of the workflow definition.
-/// The version number of the workflow definition.
[PublicAPI]
-public record WorkflowDefinitionVersionDeleted(string DefinitionId, int Version) : INotification;
\ No newline at end of file
+public record WorkflowDefinitionVersionDeleted(WorkflowDefinition WorkflowDefinition) : INotification;
\ No newline at end of file
diff --git a/src/modules/Elsa.Workflows.Management/Services/WorkflowDefinitionManager.cs b/src/modules/Elsa.Workflows.Management/Services/WorkflowDefinitionManager.cs
index 1a454f885..df0343256 100644
--- a/src/modules/Elsa.Workflows.Management/Services/WorkflowDefinitionManager.cs
+++ b/src/modules/Elsa.Workflows.Management/Services/WorkflowDefinitionManager.cs
@@ -48,6 +48,18 @@ public class WorkflowDefinitionManager : IWorkflowDefinitionManager
return count;
}
+ ///
+ public async Task DeleteByIdAsync(string id, CancellationToken cancellationToken = default)
+ {
+ var filter = new WorkflowDefinitionFilter { Id = id};
+ var definition = await _store.FindAsync(filter, cancellationToken);
+
+ if (definition == null)
+ return false;
+
+ return await DeleteVersionAsync(definition, cancellationToken);
+ }
+
///
public async Task BulkDeleteByDefinitionIdsAsync(IEnumerable definitionIds, CancellationToken cancellationToken = default)
{
@@ -61,37 +73,13 @@ public class WorkflowDefinitionManager : IWorkflowDefinitionManager
///
public async Task DeleteVersionAsync(string definitionId, int versionToDelete, CancellationToken cancellationToken = default)
{
- var filter = new WorkflowDefinitionFilter { DefinitionId = definitionId, VersionOptions = VersionOptions.LatestAndPublished };
- var workflows = (await _store.FindManyAsync(filter, cancellationToken)).ToList();
- var latestVersion = workflows.WithVersion(VersionOptions.Latest).First();
- var publishedVersion = workflows.WithVersion(VersionOptions.Published).FirstOrDefault();
-
- if (versionToDelete == publishedVersion?.Version)
- {
- throw new Exception("Published version cannot be deleted");
- }
-
- filter = new WorkflowDefinitionFilter { DefinitionId = definitionId, VersionOptions = VersionOptions.SpecificVersion(versionToDelete) };
- var isDeleted = await _store.DeleteAsync(filter, cancellationToken) > 0;
-
- if (!isDeleted)
+ var filter = new WorkflowDefinitionFilter { DefinitionId = definitionId, VersionOptions = VersionOptions.SpecificVersion(versionToDelete) };
+ var definitionToDelete = await _store.FindAsync(filter, cancellationToken);
+
+ if (definitionToDelete == null)
return false;
-
- await _notificationSender.SendAsync(new WorkflowDefinitionVersionDeleted(definitionId, versionToDelete), cancellationToken);
-
- if (latestVersion.Version != versionToDelete)
- return isDeleted;
-
- filter = new WorkflowDefinitionFilter { DefinitionId = definitionId };
- var lastVersion = await _store.FindLastVersionAsync(filter, cancellationToken);
-
- if (lastVersion is null)
- return isDeleted;
-
- lastVersion.IsLatest = true;
- await _store.SaveAsync(lastVersion, cancellationToken);
-
- return isDeleted;
+
+ return await DeleteVersionAsync(definitionToDelete, cancellationToken);
}
///
@@ -159,4 +147,37 @@ public class WorkflowDefinitionManager : IWorkflowDefinitionManager
return updatedWorkflowDefinitions;
}
+
+ private async Task DeleteVersionAsync(WorkflowDefinition definitionToDelete, CancellationToken cancellationToken)
+ {
+ if (definitionToDelete.IsPublished)
+ {
+ throw new Exception("Published version cannot be deleted before retracting it");
+ }
+
+ var filter = new WorkflowDefinitionFilter { Id = definitionToDelete.Id };
+ var isDeleted = await _store.DeleteAsync(filter, cancellationToken) > 0;
+
+ if (!isDeleted)
+ return false;
+
+ await _notificationSender.SendAsync(new WorkflowDefinitionVersionDeleted(definitionToDelete), cancellationToken);
+
+ if (!definitionToDelete.IsLatest)
+ return isDeleted;
+
+ // The deleted version was also the latest version, so we need to update the current latest version to "IsLatest = true".
+ filter = new WorkflowDefinitionFilter { DefinitionId = definitionToDelete.DefinitionId };
+ var lastVersion = await _store.FindLastVersionAsync(filter, cancellationToken);
+
+ // If there are no more versions left, then we're done.
+ if (lastVersion is null)
+ return isDeleted;
+
+ // Otherwise, we need to update the latest version.
+ lastVersion.IsLatest = true;
+ await _store.SaveAsync(lastVersion, cancellationToken);
+
+ return isDeleted;
+ }
}
\ No newline at end of file