< Summary

Information
Class: Elsa.Persistence.EFCore.Modules.Runtime.EFBookmarkQueueDeadLetterStore
Assembly: Elsa.Persistence.EFCore
File(s): /home/runner/work/elsa-core/elsa-core/src/modules/Elsa.Persistence.EFCore/Modules/Runtime/BookmarkQueueDeadLetterStore.cs
Line coverage
1%
Covered lines: 1
Uncovered lines: 67
Coverable lines: 68
Total lines: 163
Line coverage: 1.4%
Branch coverage
0%
Covered branches: 0
Total branches: 22
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
SaveAsync(...)100%210%
AddAsync(...)100%210%
AddOrGetExistingAsync()0%620%
AddOrGetExistingManyAsync()0%2040%
TryMarkReplayedAsync()0%4260%
FindAsync(...)100%210%
FindManyAsync()100%210%
PageAsync()0%620%
PageAsync()100%210%
DeleteAsync()100%210%
AddOrGetExistingIndividuallyAsync()0%620%
OnSaveAsync(...)0%620%
OnLoadAsync(...)0%2040%

File(s)

/home/runner/work/elsa-core/elsa-core/src/modules/Elsa.Persistence.EFCore/Modules/Runtime/BookmarkQueueDeadLetterStore.cs

#LineLine coverage
 1using Elsa.Common.Models;
 2using Elsa.Extensions;
 3using Elsa.Workflows;
 4using Elsa.Workflows.Runtime;
 5using Elsa.Workflows.Runtime.Entities;
 6using Elsa.Workflows.Runtime.Filters;
 7using Elsa.Workflows.Runtime.Options;
 8using Elsa.Workflows.Runtime.OrderDefinitions;
 9using JetBrains.Annotations;
 10using Microsoft.EntityFrameworkCore;
 11using Open.Linq.AsyncExtensions;
 12
 13namespace Elsa.Persistence.EFCore.Modules.Runtime;
 14
 15/// <summary>
 16/// An EF Core implementation of <see cref="IBookmarkQueueDeadLetterStore"/>.
 17/// </summary>
 18[UsedImplicitly]
 26119public class EFBookmarkQueueDeadLetterStore(Store<RuntimeElsaDbContext, BookmarkQueueDeadLetterItem> store, IPayloadSeri
 20{
 21    /// <inheritdoc />
 22    public Task SaveAsync(BookmarkQueueDeadLetterItem record, CancellationToken cancellationToken = default)
 23    {
 024        return store.SaveAsync(record, s => s.Id, OnSaveAsync, cancellationToken);
 25    }
 26
 27    /// <inheritdoc />
 28    public Task AddAsync(BookmarkQueueDeadLetterItem record, CancellationToken cancellationToken = default)
 29    {
 030        return store.AddAsync(record, OnSaveAsync, cancellationToken);
 31    }
 32
 33    /// <inheritdoc />
 34    public async Task<BookmarkQueueDeadLetterItem> AddOrGetExistingAsync(BookmarkQueueDeadLetterItem record, Cancellatio
 35    {
 36        try
 37        {
 038            await AddAsync(record, cancellationToken);
 039            return record;
 40        }
 041        catch (Exception ex) when (DbExceptionClassifier.IsDuplicateKey(ex))
 42        {
 043            var existing = await FindAsync(new BookmarkQueueDeadLetterFilter { OriginalQueueItemId = record.OriginalQueu
 044            if (existing != null)
 045                return existing;
 46
 047            throw;
 48        }
 049    }
 50
 51    /// <inheritdoc />
 52    public async Task<IReadOnlyCollection<BookmarkQueueDeadLetterItem>> AddOrGetExistingManyAsync(IEnumerable<BookmarkQu
 53    {
 054        var recordList = records.ToList();
 55
 056        if (recordList.Count == 0)
 057            return Array.Empty<BookmarkQueueDeadLetterItem>();
 58
 059        var originalQueueItemIds = recordList.Select(x => x.OriginalQueueItemId).ToList();
 060        var existingItems = (await FindManyAsync(new BookmarkQueueDeadLetterFilter { OriginalQueueItemIds = originalQueu
 061        var existingByOriginalQueueItemId = existingItems.ToDictionary(x => x.OriginalQueueItemId);
 062        var recordsToAdd = recordList.Where(x => !existingByOriginalQueueItemId.ContainsKey(x.OriginalQueueItemId)).ToLi
 63
 064        if (recordsToAdd.Count == 0)
 065            return recordList.Select(x => existingByOriginalQueueItemId[x.OriginalQueueItemId]).ToList();
 66
 67        try
 68        {
 069            await store.AddManyAsync(recordsToAdd, OnSaveAsync, cancellationToken);
 070        }
 071        catch (Exception ex) when (DbExceptionClassifier.IsDuplicateKey(ex))
 72        {
 073            return await AddOrGetExistingIndividuallyAsync(recordList, cancellationToken);
 74        }
 75
 076        return recordList.Select(x => existingByOriginalQueueItemId.GetValueOrDefault(x.OriginalQueueItemId) ?? x).ToLis
 077    }
 78
 79    /// <inheritdoc />
 80    public async Task<BookmarkQueueDeadLetterItem?> TryMarkReplayedAsync(string id, string queueItemId, DateTimeOffset r
 81    {
 082        await using var dbContext = await store.CreateDbContextAsync(cancellationToken);
 083        var affectedRows = await dbContext.Set<BookmarkQueueDeadLetterItem>()
 084            .Where(x => x.Id == id && x.CanReplay && x.ReplayedAt == null)
 085            .ExecuteUpdateAsync(
 086                setters => setters
 087                    .SetProperty(x => x.CanReplay, false)
 088                    .SetProperty(x => x.ReplayedAt, replayedAt)
 089                    .SetProperty(x => x.ReplayedQueueItemId, queueItemId),
 090                cancellationToken);
 91
 092        if (affectedRows == 0)
 093            return null;
 94
 095        var entity = await dbContext.Set<BookmarkQueueDeadLetterItem>().AsNoTracking().FirstOrDefaultAsync(x => x.Id == 
 096        if (entity == null)
 097            return null;
 98
 099        await OnLoadAsync(dbContext, entity, cancellationToken);
 0100        return entity;
 0101    }
 102
 103    /// <inheritdoc />
 104    public Task<BookmarkQueueDeadLetterItem?> FindAsync(BookmarkQueueDeadLetterFilter filter, CancellationToken cancella
 105    {
 0106        return store.FindAsync(filter.Apply, OnLoadAsync, cancellationToken);
 107    }
 108
 109    /// <inheritdoc />
 110    public async Task<IEnumerable<BookmarkQueueDeadLetterItem>> FindManyAsync(BookmarkQueueDeadLetterFilter filter, Canc
 111    {
 0112        return await store.QueryAsync(filter.Apply, OnLoadAsync, filter.TenantAgnostic, cancellationToken);
 0113    }
 114
 115    /// <inheritdoc />
 116    public async Task<Page<BookmarkQueueDeadLetterItem>> PageAsync<TOrderBy>(PageArgs pageArgs, BookmarkQueueDeadLetterI
 117    {
 0118        var count = await store.QueryAsync(queryable => queryable, cancellationToken).LongCount();
 0119        var results = await store.QueryAsync(queryable => queryable.OrderBy(orderBy).Paginate(pageArgs), OnLoadAsync, ca
 0120        return new(results, count);
 0121    }
 122
 123    /// <inheritdoc />
 124    public async Task<Page<BookmarkQueueDeadLetterItem>> PageAsync<TOrderBy>(PageArgs pageArgs, BookmarkQueueDeadLetterF
 125    {
 0126        var count = await store.QueryAsync(filter.Apply, filter.TenantAgnostic, cancellationToken).LongCount();
 0127        var results = await store.QueryAsync(queryable => filter.Apply(queryable).OrderBy(orderBy).Paginate(pageArgs), O
 0128        return new(results, count);
 0129    }
 130
 131    /// <inheritdoc />
 132    public async Task<long> DeleteAsync(BookmarkQueueDeadLetterFilter filter, CancellationToken cancellationToken = defa
 133    {
 0134        return await store.DeleteWhereAsync(filter.Apply, cancellationToken);
 0135    }
 136
 137    private async Task<IReadOnlyCollection<BookmarkQueueDeadLetterItem>> AddOrGetExistingIndividuallyAsync(IEnumerable<B
 138    {
 0139        var results = new List<BookmarkQueueDeadLetterItem>();
 140
 0141        foreach (var record in records)
 0142            results.Add(await AddOrGetExistingAsync(record, cancellationToken));
 143
 0144        return results;
 0145    }
 146
 147    private ValueTask OnSaveAsync(RuntimeElsaDbContext dbContext, BookmarkQueueDeadLetterItem entity, CancellationToken 
 148    {
 0149        dbContext.Entry(entity).Property("SerializedOptions").CurrentValue = entity.Options != null ? serializer.Seriali
 0150        return default;
 151    }
 152
 153    private ValueTask OnLoadAsync(RuntimeElsaDbContext dbContext, BookmarkQueueDeadLetterItem? entity, CancellationToken
 154    {
 0155        if (entity is null)
 0156            return default;
 157
 0158        var optionsJson = dbContext.Entry(entity).Property<string>("SerializedOptions").CurrentValue;
 0159        entity.Options = !string.IsNullOrEmpty(optionsJson) ? serializer.Deserialize<ResumeBookmarkOptions>(optionsJson)
 160
 0161        return default;
 162    }
 163}