< Summary - Heimdall API

Information
Class: ArturRios.Heimdall.Command.Handlers.HardDeleteScopePermissionCommandHandler
Assembly: ArturRios.Heimdall.Command
File(s): /home/runner/work/heimdall-api/heimdall-api/src/Application/ArturRios.Heimdall.Command/Handlers/HardDeleteScopePermissionCommandHandler.cs
Line coverage
93%
Covered lines: 14
Uncovered lines: 1
Coverable lines: 15
Total lines: 59
Line coverage: 93.3%
Branch coverage
75%
Covered branches: 3
Total branches: 4
Branch coverage: 75%
Method coverage
100%
Covered methods: 2
Fully covered methods: 1
Total methods: 2
Method coverage: 100%
Full method coverage: 50%

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
HandleAsync()75%4491.66%

File(s)

/home/runner/work/heimdall-api/heimdall-api/src/Application/ArturRios.Heimdall.Command/Handlers/HardDeleteScopePermissionCommandHandler.cs

#LineLine coverage
 1using ArturRios.Data.Relational.Core.Interfaces;
 2using ArturRios.Heimdall.Command.Input;
 3using ArturRios.Heimdall.Command.Output;
 4using ArturRios.Heimdall.Domain.Entities;
 5using ArturRios.Heimdall.Shared.Messages;
 6using ArturRios.Mediator.Command.Interfaces;
 7using ArturRios.Output;
 8using Microsoft.EntityFrameworkCore;
 9
 10namespace ArturRios.Heimdall.Command.Handlers;
 11
 12/// <summary>
 13///     Handles <see cref="HardDeleteScopePermissionCommand" /> (UC-35, FR-SP-08): locates the
 14///     permission inside the addressed scope in any deletion state (AF-35a) and permanently removes
 15///     the record. Nothing cascades — a scope permission is a leaf in the data model, and the scope
 16///     its foreign key points at is left intact. Authorization is entirely the endpoint's: UC-35's
 17///     only actor is the System Admin, so no rule is left for the handler to apply. All failures are
 18///     returned as errors on the <see cref="DataOutput{T}" /> rather than thrown.
 19/// </summary>
 1520public class HardDeleteScopePermissionCommandHandler(
 1521    IAsyncReadOnlyRepository<ScopePermission, long> permissionReader,
 1522    IAsyncRepository<ScopePermission, long> permissionWriter)
 23    : ICommandHandlerAsync<HardDeleteScopePermissionCommand, HardDeleteScopePermissionCommandOutput>
 24{
 25    public async Task<DataOutput<HardDeleteScopePermissionCommandOutput?>> HandleAsync(
 26        HardDeleteScopePermissionCommand command, CancellationToken cancellationToken = default)
 27    {
 1628        var output = DataOutput<HardDeleteScopePermissionCommandOutput?>.New;
 29
 30        // AF-35a: the permission must exist inside the addressed scope. The lookup omits an
 31        // !IsDeleted filter — a permission logically deleted by UC-34 is exactly what a cleanup
 32        // pass starts from and must still be purgeable. The route's scopeId qualifies it: an unknown
 33        // permission, an unknown scope, and a permission living in another scope are all one 404,
 34        // because the addressed resource genuinely does not exist in any of the three cases. A
 35        // repeated call lands here too — the row is already gone, and UC-35 has no idempotent path.
 1636        var permission = await permissionReader.Query()
 1637            .FirstOrDefaultAsync(x => x.PublicId == command.Id && x.Scope.PublicId == command.ScopeId);
 38
 1639        if (permission is null)
 40        {
 741            return output.WithError(ScopePermissionMessages.ScopePermissionNotFound);
 42        }
 43
 44        // UC-35 step 2: remove the record for good. No dependent is deleted first — no entity carries
 45        // a foreign key to a scope permission, so no foreign key can be violated and there is no
 46        // total to report.
 947        var delete = await permissionWriter.DeleteAsync(permission);
 48
 949        if (!delete.Success)
 50        {
 051            return output.WithErrors(delete.Errors);
 52        }
 53
 54        // UC-35 step 3.
 955        return output
 956            .WithData(new HardDeleteScopePermissionCommandOutput { Id = permission.PublicId })
 957            .WithMessage(ScopePermissionMessages.ScopePermissionHardDeletedSuccessfully);
 1658    }
 59}