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