Implement cascading workflow labels deletion

This commit is contained in:
Sipke Schoorstra 2022-05-27 10:53:22 +02:00
parent 7b72d18cc6
commit cce2a8731f
13 changed files with 160 additions and 17 deletions

View file

@ -13,12 +13,6 @@ public class EFCoreWorkflowDefinitionLabelStore : IWorkflowDefinitionLabelStore
public async Task SaveManyAsync(IEnumerable<WorkflowDefinitionLabel> records, CancellationToken cancellationToken = default) => await _store.SaveManyAsync(records, cancellationToken);
public async Task<bool> DeleteAsync(string id, CancellationToken cancellationToken = default) => await _store.DeleteWhereAsync(x => x.Id == id, cancellationToken) > 0;
public async Task<int> DeleteManyAsync(IEnumerable<string> ids, CancellationToken cancellationToken = default)
{
var idList = ids.ToList();
return await _store.DeleteWhereAsync(x => idList.Contains(x.Id), cancellationToken);
}
public async Task<IEnumerable<WorkflowDefinitionLabel>> FindByWorkflowDefinitionVersionIdAsync(string workflowDefinitionVersionId, CancellationToken cancellationToken = default) =>
await _store.FindManyAsync(x => x.WorkflowDefinitionVersionId == workflowDefinitionVersionId, cancellationToken);
@ -27,4 +21,22 @@ public class EFCoreWorkflowDefinitionLabelStore : IWorkflowDefinitionLabelStore
await _store.DeleteManyAsync(removed, cancellationToken);
await _store.SaveManyAsync(added, cancellationToken);
}
public async Task<int> DeleteByWorkflowDefinitionIdAsync(string workflowDefinitionId, CancellationToken cancellationToken = default) =>
await _store.DeleteWhereAsync(x => x.WorkflowDefinitionId == workflowDefinitionId, cancellationToken);
public async Task<int> DeleteByWorkflowDefinitionVersionIdAsync(string workflowDefinitionVersionId, CancellationToken cancellationToken = default) =>
await _store.DeleteWhereAsync(x => x.WorkflowDefinitionVersionId == workflowDefinitionVersionId, cancellationToken);
public async Task<int> DeleteByWorkflowDefinitionIdsAsync(IEnumerable<string> workflowDefinitionIds, CancellationToken cancellationToken = default)
{
var ids = workflowDefinitionIds.ToList();
return await _store.DeleteWhereAsync(x => ids.Contains(x.WorkflowDefinitionId), cancellationToken);
}
public async Task<int> DeleteByWorkflowDefinitionVersionIdsAsync(IEnumerable<string> workflowDefinitionVersionIds, CancellationToken cancellationToken = default)
{
var ids = workflowDefinitionVersionIds.ToList();
return await _store.DeleteWhereAsync(x => ids.Contains(x.WorkflowDefinitionVersionId), cancellationToken);
}
}

View file

@ -1,13 +1,17 @@
using Elsa.Labels.Entities;
using Elsa.Labels.Implementations;
using Elsa.Labels.Services;
using Elsa.Mediator.Configuration;
using Elsa.Mediator.Extensions;
using Elsa.Persistence.Common.Extensions;
using Elsa.ServiceConfiguration.Abstractions;
using Elsa.ServiceConfiguration.Attributes;
using Elsa.ServiceConfiguration.Services;
using Microsoft.Extensions.DependencyInjection;
namespace Elsa.Labels.Configuration;
[Dependency(typeof(MediatorConfigurator))]
public class LabelsConfigurator : ConfiguratorBase
{
public LabelsConfigurator(IServiceConfiguration serviceConfiguration) : base(serviceConfiguration)
@ -37,5 +41,7 @@ public class LabelsConfigurator : ConfiguratorBase
.AddSingleton(LabelStore)
.AddSingleton(WorkflowDefinitionLabelStore)
;
Services.AddNotificationHandlersFrom(GetType());
}
}

View file

@ -16,8 +16,10 @@
<ItemGroup>
<ProjectReference Include="..\..\common\Elsa.AspNetCore\Elsa.AspNetCore.csproj" />
<ProjectReference Include="..\..\common\Elsa.Mediator\Elsa.Mediator.csproj" />
<ProjectReference Include="..\..\common\Elsa.Persistence.Abstractions\Elsa.Persistence.Abstractions.csproj" />
<ProjectReference Include="..\Elsa.Workflows.Core\Elsa.Workflows.Core.csproj" />
<ProjectReference Include="..\Elsa.Workflows.Management\Elsa.Workflows.Management.csproj" />
<ProjectReference Include="..\Elsa.Workflows.Persistence\Elsa.Workflows.Persistence.csproj" />
</ItemGroup>

View file

@ -0,0 +1,21 @@
using Elsa.Labels.Services;
using Elsa.Mediator.Services;
using Elsa.Workflows.Management.Notifications;
namespace Elsa.Labels.Handlers;
// ReSharper disable once UnusedType.Global
/// <summary>
/// A handler that removes associated workflow labels in response to workflow definitions being deleted.
/// </summary>
public class DeleteWorkflowLabels : INotificationHandler<WorkflowDefinitionDeleted>, INotificationHandler<WorkflowDefinitionsDeleted>
{
private readonly IWorkflowDefinitionLabelStore _workflowDefinitionLabelStore;
public DeleteWorkflowLabels(IWorkflowDefinitionLabelStore workflowDefinitionLabelStore) => _workflowDefinitionLabelStore = workflowDefinitionLabelStore;
public async Task HandleAsync(WorkflowDefinitionDeleted notification, CancellationToken cancellationToken) =>
await _workflowDefinitionLabelStore.DeleteByWorkflowDefinitionIdAsync(notification.DefinitionId, cancellationToken);
public async Task HandleAsync(WorkflowDefinitionsDeleted notification, CancellationToken cancellationToken) =>
await _workflowDefinitionLabelStore.DeleteByWorkflowDefinitionIdsAsync(notification.DefinitionIds, cancellationToken);
}

View file

@ -49,4 +49,30 @@ public class InMemoryWorkflowDefinitionLabelStore : IWorkflowDefinitionLabelStor
_store.SaveMany(added);
return Task.CompletedTask;
}
public Task<int> DeleteByWorkflowDefinitionIdAsync(string workflowDefinitionId, CancellationToken cancellationToken = default)
{
var result = _store.DeleteWhere(x => x.WorkflowDefinitionId == workflowDefinitionId);
return Task.FromResult(result);
}
public Task<int> DeleteByWorkflowDefinitionVersionIdAsync(string workflowDefinitionVersionId, CancellationToken cancellationToken = default)
{
var result = _store.DeleteWhere(x => x.WorkflowDefinitionVersionId == workflowDefinitionVersionId);
return Task.FromResult(result);
}
public Task<int> DeleteByWorkflowDefinitionIdsAsync(IEnumerable<string> workflowDefinitionIds, CancellationToken cancellationToken = default)
{
var ids = workflowDefinitionIds.ToList();
var result = _store.DeleteWhere(x => ids.Contains(x.WorkflowDefinitionId));
return Task.FromResult(result);
}
public Task<int> DeleteByWorkflowDefinitionVersionIdsAsync(IEnumerable<string> workflowDefinitionVersionIds, CancellationToken cancellationToken = default)
{
var ids = workflowDefinitionVersionIds.ToList();
var result = _store.DeleteWhere(x => ids.Contains(x.WorkflowDefinitionVersionId));
return Task.FromResult(result);
}
}

View file

@ -7,8 +7,7 @@ public interface IWorkflowDefinitionLabelStore
Task SaveAsync(WorkflowDefinitionLabel record, CancellationToken cancellationToken = default);
Task SaveManyAsync(IEnumerable<WorkflowDefinitionLabel> records, CancellationToken cancellationToken = default);
Task<bool> DeleteAsync(string id, CancellationToken cancellationToken = default);
Task<int> DeleteManyAsync(IEnumerable<string> ids, CancellationToken cancellationToken = default);
/// <summary>
/// Returns a list of label IDs associated with the specified workflow definition version ID.
/// </summary>
@ -18,4 +17,24 @@ public interface IWorkflowDefinitionLabelStore
/// Removes and adds the specified sets of label IDs for the specified workflow definition version ID.
/// </summary>
Task ReplaceAsync(IEnumerable<WorkflowDefinitionLabel> removed, IEnumerable<WorkflowDefinitionLabel> added, CancellationToken cancellationToken = default);
/// <summary>
/// Deletes all associated labels for the specified workflow definition ID.
/// </summary>
Task<int> DeleteByWorkflowDefinitionIdAsync(string workflowDefinitionId, CancellationToken cancellationToken = default);
/// <summary>
/// Deletes all associated labels for the specified workflow definition version ID.
/// </summary>
Task<int> DeleteByWorkflowDefinitionVersionIdAsync(string workflowDefinitionVersionId, CancellationToken cancellationToken = default);
/// <summary>
/// Deletes all associated labels for the specified workflow definition IDs.
/// </summary>
Task<int> DeleteByWorkflowDefinitionIdsAsync(IEnumerable<string> workflowDefinitionIds, CancellationToken cancellationToken = default);
/// <summary>
/// Deletes all associated labels for the specified workflow definition version IDs.
/// </summary>
Task<int> DeleteByWorkflowDefinitionVersionIdsAsync(IEnumerable<string> workflowDefinitionVersionIds, CancellationToken cancellationToken = default);
}

View file

@ -2,10 +2,9 @@ using System.Collections.Generic;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
using Elsa.AspNetCore;
using Elsa.AspNetCore.Attributes;
using Elsa.Workflows.Core.Serialization;
using Elsa.Workflows.Persistence.Services;
using Elsa.Workflows.Management.Services;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
@ -15,12 +14,12 @@ namespace Elsa.Workflows.Api.Endpoints.WorkflowDefinitions;
[ApiEndpoint(ControllerNames.WorkflowDefinitions, "BulkDelete")]
public class BulkDelete : Controller
{
private readonly IWorkflowDefinitionStore _store;
private readonly IWorkflowDefinitionManager _workflowDefinitionManager;
private readonly WorkflowSerializerOptionsProvider _serializerOptionsProvider;
public BulkDelete(IWorkflowDefinitionStore store, WorkflowSerializerOptionsProvider serializerOptionsProvider)
public BulkDelete(IWorkflowDefinitionManager workflowDefinitionManager, WorkflowSerializerOptionsProvider serializerOptionsProvider)
{
_store = store;
_workflowDefinitionManager = workflowDefinitionManager;
_serializerOptionsProvider = serializerOptionsProvider;
}
@ -29,7 +28,7 @@ public class BulkDelete : Controller
{
var serializerOptions = _serializerOptionsProvider.CreateApiOptions();
var model = await Request.ReadFromJsonAsync<BulkDeleteWorkflowDefinitionsRequest>(serializerOptions, cancellationToken);
var count = await _store.DeleteManyByDefinitionIdsAsync(model!.DefinitionIds, cancellationToken);
var count = await _workflowDefinitionManager.BulkDeleteByDefinitionIdsAsync(model!.DefinitionIds, cancellationToken);
return Json(new BulkDeleteWorkflowDefinitionsResponse(count), serializerOptions);
}

View file

@ -2,6 +2,7 @@ using System.Threading;
using System.Threading.Tasks;
using Elsa.AspNetCore;
using Elsa.AspNetCore.Attributes;
using Elsa.Workflows.Management.Services;
using Elsa.Workflows.Persistence.Services;
using Microsoft.AspNetCore.Mvc;
@ -11,13 +12,16 @@ namespace Elsa.Workflows.Api.Endpoints.WorkflowDefinitions;
[ApiEndpoint(ControllerNames.WorkflowDefinitions, "Delete")]
public class Delete : Controller
{
private readonly IWorkflowDefinitionStore _store;
public Delete(IWorkflowDefinitionStore store) => _store = store;
private readonly IWorkflowDefinitionManager _workflowDefinitionManager;
public Delete(IWorkflowDefinitionManager workflowDefinitionManager)
{
_workflowDefinitionManager = workflowDefinitionManager;
}
[HttpDelete]
public async Task<IActionResult> HandleAsync(string definitionId, CancellationToken cancellationToken)
{
var result = await _store.DeleteByDefinitionIdAsync(definitionId, cancellationToken);
var result = await _workflowDefinitionManager.DeleteByDefinitionIdAsync(definitionId, cancellationToken);
return result == 0 ? NotFound() : NoContent();
}
}

View file

@ -1,4 +1,6 @@
using Elsa.Expressions.Services;
using Elsa.Mediator.Configuration;
using Elsa.Mediator.Extensions;
using Elsa.ServiceConfiguration.Abstractions;
using Elsa.ServiceConfiguration.Attributes;
using Elsa.ServiceConfiguration.Services;
@ -16,6 +18,7 @@ using Microsoft.Extensions.DependencyInjection;
namespace Elsa.Workflows.Management.Configuration;
[Dependency(typeof(MediatorConfigurator))]
[Dependency(typeof(WorkflowsConfigurator))]
[Dependency(typeof(WorkflowPersistenceConfigurator))]
public class WorkflowManagementConfigurator : ConfiguratorBase
@ -36,6 +39,7 @@ public class WorkflowManagementConfigurator : ConfiguratorBase
{
Services
.AddSingleton<IWorkflowPublisher, WorkflowPublisher>()
.AddSingleton<IWorkflowDefinitionManager, WorkflowDefinitionManager>()
.AddSingleton<IActivityDescriber, ActivityDescriber>()
.AddSingleton<IActivityRegistry, ActivityRegistry>()
.AddSingleton<IActivityRegistryPopulator, ActivityRegistryPopulator>()

View file

@ -0,0 +1,33 @@
using Elsa.Mediator.Services;
using Elsa.Workflows.Management.Notifications;
using Elsa.Workflows.Management.Services;
using Elsa.Workflows.Persistence.Services;
namespace Elsa.Workflows.Management.Implementations;
public class WorkflowDefinitionManager : IWorkflowDefinitionManager
{
private readonly IWorkflowDefinitionStore _store;
private readonly IEventPublisher _eventPublisher;
public WorkflowDefinitionManager(IWorkflowDefinitionStore store, IEventPublisher eventPublisher)
{
_store = store;
_eventPublisher = eventPublisher;
}
public async Task<int> DeleteByDefinitionIdAsync(string definitionId, CancellationToken cancellationToken = default)
{
var count = await _store.DeleteByDefinitionIdAsync(definitionId, cancellationToken);
await _eventPublisher.PublishAsync(new WorkflowDefinitionDeleted(definitionId), cancellationToken);
return count;
}
public async Task<int> BulkDeleteByDefinitionIdsAsync(IEnumerable<string> definitionIds, CancellationToken cancellationToken = default)
{
var ids = definitionIds.ToList();
var count = await _store.DeleteManyByDefinitionIdsAsync(ids, cancellationToken);
await _eventPublisher.PublishAsync(new WorkflowDefinitionsDeleted(ids), cancellationToken);
return count;
}
}

View file

@ -0,0 +1,5 @@
using Elsa.Mediator.Services;
namespace Elsa.Workflows.Management.Notifications;
public record WorkflowDefinitionDeleted(string DefinitionId) : INotification;

View file

@ -0,0 +1,5 @@
using Elsa.Mediator.Services;
namespace Elsa.Workflows.Management.Notifications;
public record WorkflowDefinitionsDeleted(ICollection<string> DefinitionIds) : INotification;

View file

@ -0,0 +1,7 @@
namespace Elsa.Workflows.Management.Services;
public interface IWorkflowDefinitionManager
{
Task<int> DeleteByDefinitionIdAsync(string definitionId, CancellationToken cancellationToken = default);
Task<int> BulkDeleteByDefinitionIdsAsync(IEnumerable<string> definitionIds, CancellationToken cancellationToken = default);
}