< Summary

Information
Class: Elsa.Identity.Services.MemoryRoleStore
Assembly: Elsa.Identity
File(s): /home/runner/work/elsa-core/elsa-core/src/modules/Elsa.Identity/Services/MemoryRoleStore.cs
Line coverage
100%
Covered lines: 23
Uncovered lines: 0
Coverable lines: 23
Total lines: 94
Line coverage: 100%
Branch coverage
75%
Covered branches: 3
Total branches: 4
Branch coverage: 75%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
AddAsync(...)100%11100%
DeleteAsync(...)100%11100%
TryDeleteAsync(...)50%22100%
SaveAsync(...)100%11100%
FindAsync(...)100%11100%
FindManyAsync(...)100%11100%
Filter(...)100%11100%
GetStorageKey(...)100%11100%
GetStorageKey(...)100%22100%

File(s)

/home/runner/work/elsa-core/elsa-core/src/modules/Elsa.Identity/Services/MemoryRoleStore.cs

#LineLine coverage
 1using Elsa.Common.Multitenancy;
 2using Elsa.Common.Services;
 3using Elsa.Identity.Contracts;
 4using Elsa.Identity.Entities;
 5using Elsa.Identity.Models;
 6
 7namespace Elsa.Identity.Services;
 8
 9/// <summary>
 10/// Represents an in-memory role store.
 11/// </summary>
 12public class MemoryRoleStore : IRoleStore, IRoleStoreWithAtomicDelete
 13{
 14    private readonly MemoryStore<Role> _store;
 15    private readonly ITenantAccessor _tenantAccessor;
 16
 17    /// <summary>
 18    /// Initializes a new instance of the <see cref="MemoryRoleStore"/> class.
 19    /// </summary>
 3220    public MemoryRoleStore(MemoryStore<Role> store, ITenantAccessor tenantAccessor)
 21    {
 3222        _store = store;
 3223        _tenantAccessor = tenantAccessor;
 3224    }
 25
 26    /// <inheritdoc />
 27    public Task AddAsync(Role role, CancellationToken cancellationToken = default)
 28    {
 229        _store.Save(role, GetStorageKey);
 230        return Task.CompletedTask;
 31    }
 32
 33    /// <inheritdoc />
 34    public Task DeleteAsync(RoleFilter filter, CancellationToken cancellationToken = default)
 35    {
 436        var roles = _store.Query(query => Filter(query, filter)).ToList();
 237        _store.DeleteMany(roles, GetStorageKey);
 238        return Task.CompletedTask;
 39    }
 40
 41    /// <inheritdoc />
 42    /// <remarks>
 43    /// The matching role's key is removed through the underlying concurrent dictionary, whose removal is a single
 44    /// compare-and-remove step. Two callers racing on the same role ID therefore see one <see langword="true"/> and
 45    /// one <see langword="false"/>, rather than both concluding they deleted it.
 46    /// </remarks>
 47    public Task<bool> TryDeleteAsync(string roleId, CancellationToken cancellationToken = default)
 48    {
 849        var role = _store.Query(query => Filter(query, new RoleFilter { Id = roleId })).FirstOrDefault();
 450        var deleted = role is not null && _store.Delete(GetStorageKey(role));
 51
 452        return Task.FromResult(deleted);
 53    }
 54
 55    /// <inheritdoc />
 56    public Task SaveAsync(Role role, CancellationToken cancellationToken = default)
 57    {
 2858        _store.Save(role, GetStorageKey);
 2859        return Task.CompletedTask;
 60    }
 61
 62    /// <inheritdoc />
 63    public Task<Role?> FindAsync(RoleFilter filter, CancellationToken cancellationToken = default)
 64    {
 13065        var result = _store.Query(query => Filter(query, filter)).FirstOrDefault();
 6566        return Task.FromResult(result);
 67    }
 68
 69    /// <inheritdoc />
 70    public Task<IEnumerable<Role>> FindManyAsync(RoleFilter filter, CancellationToken cancellationToken = default)
 71    {
 1872        var result = _store.Query(query => Filter(query, filter)).ToList().AsEnumerable();
 973        return Task.FromResult(result);
 74    }
 75
 76    /// <remarks>
 77    /// The ambient tenant is applied here rather than left to callers. Isolation previously existed only
 78    /// on the Entity Framework path, and only when multitenancy was enabled, so a deployment running the
 79    /// default in-memory stores had none at all. Null tenant IDs are retained only for the default tenant
 80    /// for backwards compatibility with records created before tenant assignment was added.
 81    /// </remarks>
 82    private IQueryable<Role> Filter(IQueryable<Role> queryable, RoleFilter filter)
 83    {
 8084        var tenantId = _tenantAccessor.TenantId;
 8085        queryable = queryable.Where(x => x.TenantId == tenantId || x.TenantId == Tenant.AgnosticTenantId || (x.TenantId 
 86
 8087        return filter.Apply(queryable);
 88    }
 89
 3690    private static string GetStorageKey(Role role) => GetStorageKey(role.TenantId, role.Id);
 91
 92    private static string GetStorageKey(string? tenantId, string roleId) =>
 3693        $"{tenantId?.Length ?? -1}:{tenantId}{roleId.Length}:{roleId}";
 94}