< Summary - Heimdall API

Information
Class: ArturRios.Heimdall.Command.Handlers.HardDeleteApplicationCommandHandler
Assembly: ArturRios.Heimdall.Command
File(s): /home/runner/work/heimdall-api/heimdall-api/src/Application/ArturRios.Heimdall.Command/Handlers/HardDeleteApplicationCommandHandler.cs
Line coverage
93%
Covered lines: 14
Uncovered lines: 1
Coverable lines: 15
Total lines: 60
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/HardDeleteApplicationCommandHandler.cs

#LineLine coverage
 1using ArturRios.Data.Relational.Core.Interfaces;
 2using ArturRios.Heimdall.Command.Input;
 3using ArturRios.Heimdall.Command.Output;
 4using ArturRios.Heimdall.Shared.Messages;
 5using ArturRios.Mediator.Command.Interfaces;
 6using ArturRios.Output;
 7using Microsoft.EntityFrameworkCore;
 8using Application = ArturRios.Heimdall.Domain.Entities.Application;
 9
 10namespace ArturRios.Heimdall.Command.Handlers;
 11
 12/// <summary>
 13///     Handles <see cref="HardDeleteApplicationCommand" /> (UC-20, FR-AP-08): locates the application
 14///     inside the addressed scope in any deletion state (AF-20a) and permanently removes the record.
 15///     The database cascades the removal to the application's credentials (UC-53), its only
 16///     dependent rows; the scope and owner its foreign keys point at are left intact. Authorization is entirely the end
 17///     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 HardDeleteApplicationCommandHandler(
 1521    IAsyncReadOnlyRepository<Application, long> applicationReader,
 1522    IAsyncRepository<Application, long> applicationWriter)
 23    : ICommandHandlerAsync<HardDeleteApplicationCommand, HardDeleteApplicationCommandOutput>
 24{
 25    public async Task<DataOutput<HardDeleteApplicationCommandOutput?>> HandleAsync(
 26        HardDeleteApplicationCommand command, CancellationToken cancellationToken = default)
 27    {
 1628        var output = DataOutput<HardDeleteApplicationCommandOutput?>.New;
 29
 30        // AF-20a: the application must exist inside the addressed scope. The lookup omits an
 31        // !IsDeleted filter — a logically deleted application, whether by UC-19 or by UC-04's cascade
 32        // from its scope, is exactly what a cleanup pass starts from and must still be purgeable, the
 33        // same call UC-05 and UC-10 make. The route's scopeId qualifies it: an unknown application,
 34        // an unknown scope, and an application living in another scope are all one 404, because the
 35        // addressed resource genuinely does not exist in any of the three cases. A repeated call
 36        // lands here too — the row is already gone, and UC-20 has no idempotent path.
 1637        var application = await applicationReader.Query()
 1638            .FirstOrDefaultAsync(x => x.PublicId == command.Id && x.Scope.PublicId == command.ScopeId);
 39
 1640        if (application is null)
 41        {
 742            return output.WithError(ApplicationMessages.ApplicationNotFound);
 43        }
 44
 45        // UC-20 step 2: remove the record for good. No dependent is deleted first — no entity carries
 46        // a foreign key to an application, so no foreign key can be violated and there is no total to
 47        // report, unlike the scope and person hard deletes.
 948        var delete = await applicationWriter.DeleteAsync(application);
 49
 950        if (!delete.Success)
 51        {
 052            return output.WithErrors(delete.Errors);
 53        }
 54
 55        // UC-20 step 3.
 956        return output
 957            .WithData(new HardDeleteApplicationCommandOutput { Id = application.PublicId })
 958            .WithMessage(ApplicationMessages.ApplicationHardDeletedSuccessfully);
 1659    }
 60}