< Summary

Information
Class: Elsa.Workflows.Runtime.BookmarkQueueDeadLetterManager
Assembly: Elsa.Workflows.Runtime
File(s): /home/runner/work/elsa-core/elsa-core/src/modules/Elsa.Workflows.Runtime/Services/BookmarkQueueDeadLetterManager.cs
Line coverage
98%
Covered lines: 96
Uncovered lines: 1
Coverable lines: 97
Total lines: 141
Line coverage: 98.9%
Branch coverage
88%
Covered branches: 16
Total branches: 18
Branch coverage: 88.8%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
DeadLetterAsync()100%11100%
DeadLetterManyAsync()87.5%8893.33%
CreateDeadLetterItem(...)100%88100%
ReplayAsync()75%44100%

File(s)

/home/runner/work/elsa-core/elsa-core/src/modules/Elsa.Workflows.Runtime/Services/BookmarkQueueDeadLetterManager.cs

#LineLine coverage
 1using Elsa.Common;
 2using Elsa.Workflows.Runtime.Entities;
 3using Elsa.Workflows.Runtime.Filters;
 4using Microsoft.Extensions.Logging;
 5
 6namespace Elsa.Workflows.Runtime;
 7
 4508public class BookmarkQueueDeadLetterManager(
 4509    IBookmarkQueueDeadLetterStore deadLetterStore,
 45010    IBookmarkQueueStore bookmarkQueueStore,
 45011    IBookmarkQueueSignaler bookmarkQueueSignaler,
 45012    ISystemClock systemClock,
 45013    IIdentityGenerator identityGenerator,
 45014    ILogger<BookmarkQueueDeadLetterManager> logger) : IBookmarkQueueDeadLetterManager
 15{
 16    public async Task<BookmarkQueueDeadLetterItem> DeadLetterAsync(BookmarkQueueItem item, string reason, Exception? exc
 17    {
 618        var results = await DeadLetterManyAsync([item], reason, exception, cancellationToken);
 619        return results.Single();
 620    }
 21
 22    public async Task<IReadOnlyCollection<BookmarkQueueDeadLetterItem>> DeadLetterManyAsync(IEnumerable<BookmarkQueueIte
 23    {
 1224        var itemList = items.ToList();
 25
 1226        if (itemList.Count == 0)
 027            return Array.Empty<BookmarkQueueDeadLetterItem>();
 28
 2629        var originalQueueItemIds = itemList.Select(x => x.Id).ToList();
 1230        var existingDeadLetters = (await deadLetterStore.FindManyAsync(new BookmarkQueueDeadLetterFilter { OriginalQueue
 1531        var existingByOriginalQueueItemId = existingDeadLetters.ToDictionary(x => x.OriginalQueueItemId);
 1232        var now = systemClock.UtcNow;
 1233        var deadLetterItems = itemList
 1434            .Where(item => !existingByOriginalQueueItemId.ContainsKey(item.Id))
 1135            .Select(item => CreateDeadLetterItem(item, reason, exception, now))
 1236            .ToList();
 37
 1238        if (deadLetterItems.Count == 0)
 639            return itemList.Select(x => existingByOriginalQueueItemId[x.Id]).ToList();
 40
 941        var savedDeadLetterItems = await deadLetterStore.AddOrGetExistingManyAsync(deadLetterItems, cancellationToken);
 1842        var savedByOriginalQueueItemId = savedDeadLetterItems.ToDictionary(x => x.OriginalQueueItemId);
 1843        var newDeadLetterIds = deadLetterItems.Select(x => x.Id).ToHashSet();
 44
 4645        foreach (var savedDeadLetterItem in savedDeadLetterItems.Where(x => newDeadLetterIds.Contains(x.Id)))
 46        {
 1047            logger.LogInformation(
 1048                "Moved bookmark queue item {BookmarkQueueItemId} to dead letter {BookmarkQueueDeadLetterItemId} because 
 1049                savedDeadLetterItem.OriginalQueueItemId,
 1050                savedDeadLetterItem.Id,
 1051                reason);
 52        }
 53
 1854        return itemList.Select(x => existingByOriginalQueueItemId.GetValueOrDefault(x.Id) ?? savedByOriginalQueueItemId[
 1155    }
 56
 57    private BookmarkQueueDeadLetterItem CreateDeadLetterItem(BookmarkQueueItem item, string reason, Exception? exception
 58    {
 1159        var deadLetterItem = new BookmarkQueueDeadLetterItem
 1160        {
 1161            Id = identityGenerator.GenerateId(),
 1162            TenantId = item.TenantId,
 1163            OriginalQueueItemId = item.Id,
 1164            WorkflowInstanceId = item.WorkflowInstanceId,
 1165            CorrelationId = item.CorrelationId,
 1166            BookmarkId = item.BookmarkId,
 1167            StimulusHash = item.StimulusHash,
 1168            ActivityInstanceId = item.ActivityInstanceId,
 1169            ActivityTypeName = item.ActivityTypeName,
 1170            Options = item.Options,
 1171            OriginalCreatedAt = item.CreatedAt,
 1172            DeadLetteredAt = now,
 1173            Reason = reason,
 1174            DeliveryAttempts = item.DeliveryAttempts,
 1175            LastAttemptedAt = item.LastAttemptedAt,
 1176            LastErrorType = exception?.GetType().FullName ?? item.LastErrorType,
 1177            LastErrorMessage = exception?.Message ?? item.LastErrorMessage,
 1178            CanReplay = true
 1179        };
 80
 1181        return deadLetterItem;
 82    }
 83
 84    public async Task<ReplayBookmarkQueueDeadLetterResult> ReplayAsync(string id, CancellationToken cancellationToken = 
 85    {
 886        var queueItemId = identityGenerator.GenerateId();
 887        var replayedAt = systemClock.UtcNow;
 888        var item = await deadLetterStore.TryMarkReplayedAsync(id, queueItemId, replayedAt, cancellationToken);
 89
 890        if (item == null)
 91        {
 392            var existing = await deadLetterStore.FindAsync(new BookmarkQueueDeadLetterFilter { Id = id }, cancellationTo
 393            return existing == null
 394                ? new(false, null, ReplayBookmarkQueueDeadLetterResult.ReasonNotFound)
 395                : new(false, existing.ReplayedQueueItemId, ReplayBookmarkQueueDeadLetterResult.ReasonNotReplayable);
 96        }
 97
 598        var queueItem = new BookmarkQueueItem
 599        {
 5100            Id = queueItemId,
 5101            TenantId = item.TenantId,
 5102            WorkflowInstanceId = item.WorkflowInstanceId,
 5103            CorrelationId = item.CorrelationId,
 5104            BookmarkId = item.BookmarkId,
 5105            StimulusHash = item.StimulusHash,
 5106            ActivityInstanceId = item.ActivityInstanceId,
 5107            ActivityTypeName = item.ActivityTypeName,
 5108            Options = item.Options,
 5109            CreatedAt = systemClock.UtcNow
 5110        };
 111
 112        try
 113        {
 5114            await bookmarkQueueStore.AddAsync(queueItem, cancellationToken);
 4115        }
 1116        catch (Exception ex) when (ex is not OutOfMemoryException and not StackOverflowException)
 117        {
 1118            item.CanReplay = true;
 1119            item.ReplayedAt = null;
 1120            item.ReplayedQueueItemId = null;
 1121            await deadLetterStore.SaveAsync(item, CancellationToken.None);
 122
 1123            logger.LogWarning(
 1124                ex,
 1125                "Failed to enqueue replayed bookmark queue item {BookmarkQueueItemId}; restored dead-letter item {Bookma
 1126                queueItem.Id,
 1127                item.Id);
 128
 1129            throw;
 130        }
 131
 4132        await bookmarkQueueSignaler.TriggerAsync(cancellationToken);
 133
 4134        logger.LogInformation(
 4135            "Replayed bookmark queue dead-letter item {BookmarkQueueDeadLetterItemId} as queue item {BookmarkQueueItemId
 4136            item.Id,
 4137            queueItem.Id);
 138
 4139        return new(true, queueItem.Id, null);
 7140    }
 141}