Skip to content

Commit

Permalink
Merge pull request #118 from jxnkwlp/feature/cleanup
Browse files Browse the repository at this point in the history
🚀 Support automatic cleanup of workflow instance data #117
  • Loading branch information
jxnkwlp authored Sep 1, 2024
2 parents ced60f4 + f72e763 commit ea6dabc
Show file tree
Hide file tree
Showing 46 changed files with 1,581 additions and 102 deletions.
6 changes: 3 additions & 3 deletions common.props
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,15 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Roslynator.Analyzers" Version="4.10.0">
<PackageReference Include="Roslynator.Analyzers" Version="4.12.4">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
<PackageReference Include="Roslynator.Formatting.Analyzers" Version="4.10.0">
<PackageReference Include="Roslynator.Formatting.Analyzers" Version="4.12.4">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
<PackageReference Include="Roslynator.CodeAnalysis.Analyzers" Version="4.10.0">
<PackageReference Include="Roslynator.CodeAnalysis.Analyzers" Version="4.12.4">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ public override void Define(IPermissionDefinitionContext context)
instancePermission.AddChild(ElsaModulePermissions.Instances.Statistic, L("Permission:Instance.Statistic"));
instancePermission.AddChild(ElsaModulePermissions.Instances.Data, L("Permission:Instance.Data"));
instancePermission.AddChild(ElsaModulePermissions.Instances.Delete, L("Permission:Delete"));
instancePermission.AddChild(ElsaModulePermissions.Instances.Cleanup, L("Permission:Instance.Cleanup"));

var definitionPermission = myGroup.AddPermission(ElsaModulePermissions.Definitions.Default, L("Permission:Definition"));
definitionPermission.AddChild(ElsaModulePermissions.Definitions.CreateOrUpdateOrPublish, L("Permission:Definition.Publish"));
Expand Down Expand Up @@ -45,6 +46,9 @@ public override void Define(IPermissionDefinitionContext context)
globalCodes.AddChild(ElsaModulePermissions.GlobalCodes.Create, L("Permission:Create"));
globalCodes.AddChild(ElsaModulePermissions.GlobalCodes.Update, L("Permission:Update"));
globalCodes.AddChild(ElsaModulePermissions.GlobalCodes.Delete, L("Permission:Delete"));

var settings = myGroup.AddPermission(ElsaModulePermissions.Settings.Default, L("Permission:Settings"));
settings.AddChild(ElsaModulePermissions.Settings.InstanceCleanup, L("Permission:Settings.InstanceCleanup"));
}

private static LocalizableString L(string name)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace Passingwind.Abp.ElsaModule.Settings;

public class CleanupSettingsDto
{
public bool Enabled { get; set; }
public int KeepDays { get; set; }
public bool ScopeAll { get; set; }
public int[] Scopes { get; set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using System.Threading.Tasks;
using Volo.Abp.Application.Services;

namespace Passingwind.Abp.ElsaModule.Settings;

public interface IWorkflowSettingsAppService : IApplicationService
{
Task<CleanupSettingsDto> GetCleanupAsync();
Task UpdateCleanupAsync(CleanupSettingsDto input);
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,6 @@ public interface IWorkflowInstanceAppService : IApplicationService
Task<PagedResultDto<WorkflowDefinitionBasicDto>> GetAssignableDefinitionAsync(WorkflowDefinitionListRequestDto input);

Task<WorkflowDefinitionVersionDto> GetDefinitionAsync(Guid id);

Task CleanupAsync();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Passingwind.Abp.ElsaModule.Cleanup;
using Passingwind.Abp.ElsaModule.Common;
using Passingwind.Abp.ElsaModule.Permissions;

namespace Passingwind.Abp.ElsaModule.Settings;

[Authorize(ElsaModulePermissions.Settings.Default)]
public class WorkflowSettingsAppService : ElsaModuleAppService, IWorkflowSettingsAppService
{
protected ICleanupSettingsManager CleanupSettingsManager { get; }

public WorkflowSettingsAppService(ICleanupSettingsManager cleanupSettingsManager)
{
CleanupSettingsManager = cleanupSettingsManager;
}

[Authorize(ElsaModulePermissions.Settings.InstanceCleanup)]
public virtual async Task<CleanupSettingsDto> GetCleanupAsync()
{
var settings = await CleanupSettingsManager.GetAsync();

var scopes = settings.Scopes;

return new CleanupSettingsDto()
{
Enabled = settings.Enabled,
KeepDays = settings.KeepDays,
ScopeAll = scopes.HasFlag(WorkflowInstanceCleanupScope.All),
Scopes = Enum.GetValues<WorkflowInstanceCleanupScope>().Cast<WorkflowInstanceCleanupScope>().Where(x => scopes.HasFlag(x)).Cast<int>().ToArray(),
};
}

[Authorize(ElsaModulePermissions.Settings.InstanceCleanup)]
public virtual async Task UpdateCleanupAsync(CleanupSettingsDto input)
{
await CleanupSettingsManager.UpdateAsync(new CleanupSettings
{
Enabled = input.Enabled,
KeepDays = input.KeepDays,
Scopes = input.ScopeAll ? WorkflowInstanceCleanupScope.All : (WorkflowInstanceCleanupScope)input.Scopes.Sum(),
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using Elsa.Services;
using Elsa.Services.WorkflowStorage;
using Microsoft.AspNetCore.Authorization;
using Passingwind.Abp.ElsaModule.Cleanup;
using Passingwind.Abp.ElsaModule.Common;
using Passingwind.Abp.ElsaModule.Permissions;
using Passingwind.Abp.ElsaModule.Stores;
Expand Down Expand Up @@ -34,6 +35,7 @@ public class WorkflowInstanceAppService : ElsaModuleAppService, IWorkflowInstanc
private readonly IDistributedCache<WorkflowInstanceStatusCountStatisticsResultDto> _workflowInstanceStatusCountStatisticsDistributedCache;
private readonly IWorkflowPermissionProvider _workflowPermissionProvider;
private readonly IWorkflowDefinitionVersionRepository _workflowDefinitionVersionRepository;
private readonly ICleanupRunner _cleanupRunner;

public WorkflowInstanceAppService(
IJsonSerializer jsonSerializer,
Expand All @@ -48,7 +50,8 @@ public WorkflowInstanceAppService(
IDistributedCache<WorkflowInstanceDateCountStatisticsResultDto> workflowInstanceDateCountStatisticsDistributedCache,
IDistributedCache<WorkflowInstanceStatusCountStatisticsResultDto> workflowInstanceStatusCountStatisticsDistributedCache,
IWorkflowPermissionProvider workflowPermissionProvider,
IWorkflowDefinitionVersionRepository workflowDefinitionVersionRepository)
IWorkflowDefinitionVersionRepository workflowDefinitionVersionRepository,
ICleanupRunner cleanupRunner)
{
_jsonSerializer = jsonSerializer;
_workflowInstanceRepository = workflowInstanceRepository;
Expand All @@ -63,6 +66,7 @@ public WorkflowInstanceAppService(
_workflowInstanceStatusCountStatisticsDistributedCache = workflowInstanceStatusCountStatisticsDistributedCache;
_workflowPermissionProvider = workflowPermissionProvider;
_workflowDefinitionVersionRepository = workflowDefinitionVersionRepository;
_cleanupRunner = cleanupRunner;
}

[Authorize(Policy = ElsaModulePermissions.Instances.Action)]
Expand Down Expand Up @@ -189,10 +193,14 @@ public async Task<PagedResultDto<WorkflowInstanceBasicDto>> GetListAsync(Workflo
version: input.Version,
status: input.WorkflowStatus,
correlationId: input.CorrelationId,
creationTimes: input.CreationTimes,
lastExecutedTimes: input.LastExecutedTimes,
finishedTimes: input.FinishedTimes,
faultedTimes: input.FaultedTimes);
minCreationTime: input.CreationTimes?.Length >= 1 ? input.CreationTimes[0] : null,
maxCreationTime: input.CreationTimes?.Length == 2 ? input.CreationTimes[1] : null,
minLastExecutedTime: input.LastExecutedTimes?.Length >= 1 ? input.LastExecutedTimes[0] : null,
maxLastExecutedTime: input.LastExecutedTimes?.Length == 2 ? input.LastExecutedTimes[1] : null,
minFinishedTime: input.FinishedTimes?.Length >= 1 ? input.FinishedTimes[0] : null,
maxFinishedTime: input.FinishedTimes?.Length == 2 ? input.FinishedTimes[1] : null,
minFaultedTime: input.FaultedTimes?.Length >= 1 ? input.FaultedTimes[0] : null,
maxFaultedTime: input.FaultedTimes?.Length == 2 ? input.FaultedTimes[1] : null);

var list = await _workflowInstanceRepository.GetPagedListAsync(
input.SkipCount,
Expand All @@ -203,10 +211,14 @@ public async Task<PagedResultDto<WorkflowInstanceBasicDto>> GetListAsync(Workflo
version: input.Version,
status: input.WorkflowStatus,
correlationId: input.CorrelationId,
creationTimes: input.CreationTimes,
lastExecutedTimes: input.LastExecutedTimes,
finishedTimes: input.FinishedTimes,
faultedTimes: input.FaultedTimes);
minCreationTime: input.CreationTimes?.Length >= 1 ? input.CreationTimes[0] : null,
maxCreationTime: input.CreationTimes?.Length == 2 ? input.CreationTimes[1] : null,
minLastExecutedTime: input.LastExecutedTimes?.Length >= 1 ? input.LastExecutedTimes[0] : null,
maxLastExecutedTime: input.LastExecutedTimes?.Length == 2 ? input.LastExecutedTimes[1] : null,
minFinishedTime: input.FinishedTimes?.Length >= 1 ? input.FinishedTimes[0] : null,
maxFinishedTime: input.FinishedTimes?.Length == 2 ? input.FinishedTimes[1] : null,
minFaultedTime: input.FaultedTimes?.Length >= 1 ? input.FaultedTimes[0] : null,
maxFaultedTime: input.FaultedTimes?.Length == 2 ? input.FaultedTimes[1] : null);

return new PagedResultDto<WorkflowInstanceBasicDto>(count, ObjectMapper.Map<List<WorkflowInstance>, List<WorkflowInstanceBasicDto>>(list));
}
Expand All @@ -223,7 +235,7 @@ public async Task DeleteAsync(Guid id)
[Authorize(Policy = ElsaModulePermissions.Instances.Delete)]
public async Task BatchDeleteAsync(WorkflowInstanceBatchActionRequestDto input)
{
if (input?.Ids?.Any() == true)
if (input?.Ids?.Length > 0)
{
foreach (var id in input.Ids)
{
Expand Down Expand Up @@ -294,7 +306,9 @@ public async Task<WorkflowInstanceDateCountStatisticsResultDto> GetStatusDateCou
{
var datePeriod = input.DatePeriod ?? 30;
if (datePeriod <= 0)
{
throw new ArgumentOutOfRangeException("datePeriod must > 0");
}

double tz = input.Tz;

Expand Down Expand Up @@ -382,7 +396,7 @@ public async Task BatchCancelAsync(WorkflowInstanceBatchActionRequestDto input)
{
// TODO check permissions

if (input?.Ids?.Any() == true)
if (input?.Ids?.Length > 0)
{
foreach (var id in input.Ids)
{
Expand All @@ -396,7 +410,7 @@ public async Task BatchRetryAsync(WorkflowInstanceBatchActionRequestDto input)
{
// TODO check permissions

if (input?.Ids?.Any() == true)
if (input?.Ids?.Length > 0)
{
foreach (var id in input.Ids)
{
Expand Down Expand Up @@ -459,4 +473,10 @@ public async Task<WorkflowDefinitionVersionDto> GetDefinitionAsync(Guid id)
dto.Definition = ObjectMapper.Map<WorkflowDefinition, WorkflowDefinitionDto>(definition);
return dto;
}

[Authorize(Policy = ElsaModulePermissions.Instances.Cleanup)]
public async Task CleanupAsync()
{
await _cleanupRunner.RunAsync(true);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using System;

namespace Passingwind.Abp.ElsaModule.Common;

[Flags]
public enum WorkflowInstanceCleanupScope
{
None = 0,
Input = 1,
Output = 1 << 1,
Faults = 1 << 2,
Variables = 1 << 3,
Metadata = 1 << 4,
ActivityScopes = 1 << 5,
ActivityData = 1 << 6,
ExecutionLogs = 1 << 7,
All = 1 << 16,
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"Permission:Instance.Acton": "Acton",
"Permission:Instance.Statistic": "Statistic",
"Permission:Instance.Data": "View data",
"Permission:Instance.Cleanup": "Cleanup",

"Permission:Definition": "Definition management",
"Permission:Definition.Publish": "Create or publish",
Expand All @@ -24,7 +25,12 @@

"Permission:GlobalVariables": "Global variables management",

"Permission:WorkflowTeams": "Workflow Teams Management",
"Permission:WorkflowTeams.ManagePermissions": "Manage permissions"
"Permission:WorkflowTeams": "Workflow teams management",
"Permission:WorkflowTeams.ManagePermissions": "Manage permissions",

"Permission:WorkflowGroups": "Workflow groups management",
"Permission:GlobalCodes": "Global codes management",
"Permission:Settings": "Settings management",
"Permission:Settings.InstanceCleanup": "Instance cleanup"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"Permission:Instance.Acton": "操作",
"Permission:Instance.Statistic": "统计",
"Permission:Instance.Data": "查看详细数据",
"Permission:Instance.Cleanup": "清理",

"Permission:Definition": "定义管理",
"Permission:Definition.Publish": "保存或发布",
Expand All @@ -25,6 +26,11 @@
"Permission:GlobalVariables": "全局变量管理",

"Permission:WorkflowTeams": "团队管理",
"Permission:WorkflowTeams.ManagePermissions": "权限管理"
"Permission:WorkflowTeams.ManagePermissions": "权限管理",

"Permission:WorkflowGroups": "工作流分组管理",
"Permission:GlobalCodes": "全局代码管理",
"Permission:Settings": "设置管理",
"Permission:Settings.InstanceCleanup": "实例清理"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ public static class Instances
public const string Delete = Default + ".Delete";
public const string Data = Default + ".Data";
public const string Statistic = Default + ".Statistics";
public const string Cleanup = Default + ".Cleanup";
}

public static class Definitions
Expand All @@ -31,6 +32,12 @@ public static class Definitions
public const string ManagePermissions = Default + ".ManagePermissions";
}

public static class Settings
{
public const string Default = GroupName + ".Settings";
public const string InstanceCleanup = Default + ".InstanceCleanup";
}

public static class GlobalVariables
{
public const string Default = GroupName + ".GlobalVariables";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using System;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Volo.Abp.BackgroundWorkers;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Threading;

namespace Passingwind.Abp.ElsaModule.Cleanup;

public class CleanupBackgroundWorker : AsyncPeriodicBackgroundWorkerBase, ITransientDependency
{
public CleanupBackgroundWorker(AbpAsyncTimer timer, IServiceScopeFactory serviceScopeFactory) : base(timer, serviceScopeFactory)
{
timer.Period = (int)TimeSpan.FromHours(6).TotalMilliseconds;
}

protected override async Task DoWorkAsync(PeriodicBackgroundWorkerContext workerContext)
{
var service = workerContext.ServiceProvider.GetRequiredService<ICleanupRunner>();

try
{
await service.RunAsync();
}
catch (Exception ex)
{
Logger.LogError(ex, "Executing workflow instance cleanup job failed.");
}
}
}
38 changes: 38 additions & 0 deletions src/Passingwind.Abp.ElsaModule.Domain/Cleanup/CleanupRunner.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Timing;

namespace Passingwind.Abp.ElsaModule.Cleanup;

public class CleanupRunner : ICleanupRunner, IScopedDependency
{
protected ILogger<CleanupRunner> Logger { get; }
protected IClock Clock { get; }
protected ICleanupSettingsManager CleanupSettingsManager { get; }
protected IWorkflowInstanceCleanupProvider InstanceCleanupProvider { get; }

public CleanupRunner(ILogger<CleanupRunner> logger, IClock clock, ICleanupSettingsManager cleanupSettingsManager, IWorkflowInstanceCleanupProvider instanceCleanupProvider)
{
Logger = logger;
Clock = clock;
CleanupSettingsManager = cleanupSettingsManager;
InstanceCleanupProvider = instanceCleanupProvider;
}

public virtual async Task RunAsync(bool fource = false, CancellationToken cancellationToken = default)
{
var settings = await CleanupSettingsManager.GetAsync(cancellationToken);

if (!settings.Enabled && !fource)
{
Logger.LogDebug("Cleanup settings are not enabled.");
return;
}

var endDate = Clock.Now.Date.AddDays(-settings.KeepDays);

await InstanceCleanupProvider.ExecutingAsync(endDate, settings.Scopes, cancellationToken);
}
}
Loading

0 comments on commit ea6dabc

Please sign in to comment.