< Summary - Heimdall API

Information
Class: ArturRios.Heimdall.Command.Handlers.DeclineScopeOwnerInvitationCommandHandler
Assembly: ArturRios.Heimdall.Command
File(s): /home/ci/agent/workspace/heimdall-api-docs_main/app/src/Application/ArturRios.Heimdall.Command/Handlers/DeclineScopeOwnerInvitationCommandHandler.cs
Line coverage
100%
Covered lines: 24
Uncovered lines: 0
Coverable lines: 24
Total lines: 64
Line coverage: 100%
Branch coverage
100%
Covered branches: 8
Total branches: 8
Branch coverage: 100%
Method coverage
100%
Covered methods: 2
Fully covered methods: 2
Total methods: 2
Method coverage: 100%
Full method coverage: 100%

Metrics

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

File(s)

/home/ci/agent/workspace/heimdall-api-docs_main/app/src/Application/ArturRios.Heimdall.Command/Handlers/DeclineScopeOwnerInvitationCommandHandler.cs

#LineLine coverage
 1using ArturRios.Data.Relational.Core.Interfaces;
 2using ArturRios.Heimdall.Command.Input;
 3using ArturRios.Heimdall.Command.Output;
 4using ArturRios.Heimdall.Command.Services;
 5using ArturRios.Heimdall.Domain.Entities;
 6using ArturRios.Heimdall.Domain.Enums;
 7using ArturRios.Heimdall.Domain.Persistence;
 8using ArturRios.Heimdall.Shared.Messages;
 9using ArturRios.Mediator.Command.Interfaces;
 10using ArturRios.Output;
 11using Microsoft.EntityFrameworkCore;
 12
 13namespace ArturRios.Heimdall.Command.Handlers;
 14
 15/// <summary>
 16///     Handles <see cref="DeclineScopeOwnerInvitationCommand" /> (UC-61, FR-SC-17): the invitee ends an open
 17///     invitation as declined, and the inviter is told. Any other caller, and an ended or expired invitation,
 18///     get AF-61a's single answer, as for UC-60. All failures are returned as errors on the
 19///     <see cref="DataOutput{T}" /> rather than thrown.
 20/// </summary>
 821public class DeclineScopeOwnerInvitationCommandHandler(
 822    IAsyncReadOnlyRepository<Person, long> personReader,
 823    IAsyncReadOnlyRepository<Scope, long> scopeReader,
 824    IAsyncReadOnlyRepository<ScopeOwnerInvitation, long> invitationReader,
 825    IScopeOwnerInvitations invitations,
 826    IScopeInvitationNoticeSender notices)
 27    : ICommandHandlerAsync<DeclineScopeOwnerInvitationCommand, DeclineScopeOwnerInvitationCommandOutput>
 28{
 29    public async Task<DataOutput<DeclineScopeOwnerInvitationCommandOutput?>> HandleAsync(
 30        DeclineScopeOwnerInvitationCommand command, CancellationToken cancellationToken = default)
 31    {
 832        var output = DataOutput<DeclineScopeOwnerInvitationCommandOutput?>.New;
 833        var now = DateTime.UtcNow;
 34
 35        // AF-61a.
 836        var found = await InviteeInvitationLookup.FindAsync(
 837            personReader, invitationReader, command.ActingPersonId, command.InvitationId, now, cancellationToken);
 38
 839        if (found is null)
 40        {
 241            return output.WithError(ScopeInvitationMessages.InvitationNotOpen);
 42        }
 43
 644        var (invitee, invitation) = found.Value;
 45
 646        if (!await invitations.TryEndAsync(invitation, ScopeOwnerInvitationOutcomes.Declined, now))
 47        {
 148            return output.WithError(ScopeInvitationMessages.InvitationNotOpen);
 49        }
 50
 551        var scope = await scopeReader.Query()
 552            .FirstOrDefaultAsync(x => x.Id == invitation.ScopeId, cancellationToken);
 53
 554        if (scope is not null &&
 555            await InviteeInvitationLookup.InviterToTellAsync(personReader, invitation, cancellationToken) is { } inviter
 56        {
 257            await notices.SendAnswerAsync(inviter.Email, invitee.Name, scope.Name, accepted: false);
 58        }
 59
 560        return output
 561            .WithData(new DeclineScopeOwnerInvitationCommandOutput { Id = invitation.PublicId })
 562            .WithMessage(ScopeInvitationMessages.InvitationDeclined);
 863    }
 64}