| | | 1 | | using Elsa.UserTasks.Contracts; |
| | | 2 | | using Elsa.UserTasks.Models; |
| | | 3 | | using Elsa.Workflows; |
| | | 4 | | using Elsa.Common; |
| | | 5 | | |
| | | 6 | | namespace Elsa.UserTasks.Services; |
| | | 7 | | |
| | 71 | 8 | | public sealed class DefaultUserTaskProjectionService( |
| | 71 | 9 | | IUserTaskManager manager, |
| | 71 | 10 | | IUserTaskRepository repository, |
| | 71 | 11 | | IUserTaskNotificationSink notifications, |
| | 71 | 12 | | IUserTaskGuestSessionIssuer guestSessions, |
| | 71 | 13 | | IIdentityGenerator identityGenerator, |
| | 71 | 14 | | ISystemClock clock) : IUserTaskProjectionService |
| | | 15 | | { |
| | | 16 | | public async Task ProjectCommittedBookmarksAsync(IReadOnlyCollection<UserTaskMaterialization> materializations, Canc |
| | | 17 | | { |
| | 0 | 18 | | foreach (var materialization in materializations) |
| | 0 | 19 | | await manager.ProjectAsync(materialization, cancellationToken); |
| | 0 | 20 | | } |
| | | 21 | | |
| | | 22 | | public async Task FinalizeBookmarkRemovalAsync(UserTaskBookmarkRemoval removal, CancellationToken cancellationToken |
| | | 23 | | { |
| | 3 | 24 | | var task = await repository.GetAsync(removal.TenantId, removal.TaskId, cancellationToken); |
| | 3 | 25 | | if (task == null || task.BookmarkId != removal.BookmarkId) |
| | 0 | 26 | | return; |
| | 3 | 27 | | var expectedRevision = task.Revision; |
| | 3 | 28 | | var terminalStatus = task.Status switch |
| | 3 | 29 | | { |
| | 1 | 30 | | UserTaskStatus.Completing => UserTaskStatus.Completed, |
| | 1 | 31 | | UserTaskStatus.TimingOut => UserTaskStatus.TimedOut, |
| | 0 | 32 | | UserTaskStatus.Cancelling => UserTaskStatus.Cancelled, |
| | 2 | 33 | | _ when !task.IsTerminal => UserTaskStatus.Cancelled, |
| | 0 | 34 | | _ => (UserTaskStatus?)null |
| | 3 | 35 | | }; |
| | 3 | 36 | | if (terminalStatus == null) |
| | 0 | 37 | | return; |
| | 3 | 38 | | var expectedOperationKind = task.Status switch |
| | 3 | 39 | | { |
| | 1 | 40 | | UserTaskStatus.Completing => UserTaskOperationKind.Complete, |
| | 1 | 41 | | UserTaskStatus.TimingOut => UserTaskOperationKind.Timeout, |
| | 0 | 42 | | UserTaskStatus.Cancelling => UserTaskOperationKind.Cancel, |
| | 1 | 43 | | _ => (UserTaskOperationKind?)null |
| | 3 | 44 | | }; |
| | 3 | 45 | | var operation = expectedOperationKind is { } operationKind |
| | 2 | 46 | | ? task.Operations.LastOrDefault(x => x.Status == UserTaskOperationStatus.Accepted && x.Kind == operationKind |
| | 3 | 47 | | : null; |
| | 3 | 48 | | if (task.Status is (UserTaskStatus.Completing or UserTaskStatus.TimingOut or UserTaskStatus.Cancelling) && opera |
| | 0 | 49 | | return; |
| | 3 | 50 | | if (operation != null) |
| | | 51 | | { |
| | 2 | 52 | | var index = task.Operations.IndexOf(operation); |
| | 2 | 53 | | task.Operations[index] = operation with { Status = UserTaskOperationStatus.Completed, UpdatedAt = clock.UtcN |
| | | 54 | | } |
| | 3 | 55 | | task.Status = terminalStatus.Value; |
| | 3 | 56 | | task.CompletedAt ??= clock.UtcNow; |
| | 3 | 57 | | task.Events.Add(new UserTaskEvent(identityGenerator.GenerateId(), task.TenantId, task.Id, expectedRevision + 1, |
| | 3 | 58 | | terminalStatus.Value.ToString(), clock.UtcNow, operation == null ? null : task.CompletedBy, operation?.Opera |
| | 3 | 59 | | Metadata: operation?.ActionKey == null ? null : new Dictionary<string, object?> { ["actionKey"] = operation. |
| | | 60 | | try |
| | | 61 | | { |
| | 3 | 62 | | await repository.SaveAsync(task, expectedRevision, cancellationToken); |
| | 3 | 63 | | } |
| | 0 | 64 | | catch (UserTaskRevisionConflictException) |
| | | 65 | | { |
| | 0 | 66 | | return; |
| | | 67 | | } |
| | | 68 | | // The task is closed, so any outstanding guest session for it must stop working immediately rather |
| | | 69 | | // than lingering until its own TTL elapses. |
| | 3 | 70 | | await guestSessions.RevokeForTaskAsync(task.TenantId, task.Id, cancellationToken); |
| | 3 | 71 | | var committed = await repository.GetAsync(task.TenantId, task.Id, cancellationToken) ?? task; |
| | 3 | 72 | | await notifications.PublishAsync(terminalStatus.Value switch |
| | 3 | 73 | | { |
| | 1 | 74 | | UserTaskStatus.Completed => new UserTaskCompleted(committed.TenantId, committed.Id, committed.Status, commit |
| | 1 | 75 | | UserTaskStatus.TimedOut => new UserTaskTimedOut(committed.TenantId, committed.Id, committed.Status, committe |
| | 1 | 76 | | _ => new UserTaskCancelled(committed.TenantId, committed.Id, committed.Status, committed.Revision) |
| | 3 | 77 | | }, cancellationToken); |
| | 3 | 78 | | } |
| | | 79 | | } |