< Summary - Heimdall API

Information
Class: ArturRios.Heimdall.WebApi.Controllers.ScopeInvitationController
Assembly: ArturRios.Heimdall.WebApi
File(s): /home/ci/agent/workspace/heimdall-api-docs_main/app/src/Presentation/ArturRios.Heimdall.WebApi/Controllers/ScopeInvitationController.cs
Line coverage
100%
Covered lines: 32
Uncovered lines: 0
Coverable lines: 32
Total lines: 120
Line coverage: 100%
Branch coverage
100%
Covered branches: 2
Total branches: 2
Branch coverage: 100%
Method coverage
100%
Covered methods: 6
Fully covered methods: 6
Total methods: 6
Method coverage: 100%
Full method coverage: 100%

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
ListForScope()100%11100%
Withdraw()100%22100%
ListMine()100%11100%
Accept()100%11100%
Decline()100%11100%

File(s)

/home/ci/agent/workspace/heimdall-api-docs_main/app/src/Presentation/ArturRios.Heimdall.WebApi/Controllers/ScopeInvitationController.cs

#LineLine coverage
 1using ArturRios.Heimdall.Command.Input;
 2using ArturRios.Heimdall.Command.Output;
 3using ArturRios.Heimdall.Domain.Enums;
 4using ArturRios.Heimdall.Query.Input;
 5using ArturRios.Heimdall.Query.Output;
 6using ArturRios.Heimdall.Shared.Messages;
 7using ArturRios.Heimdall.WebApi.Security;
 8using ArturRios.Mediator.Command;
 9using ArturRios.Mediator.Query;
 10using ArturRios.Output;
 11using ArturRios.Util.WebApi.AspNetCore;
 12using ArturRios.Util.WebApi.Security.Attributes;
 13using Microsoft.AspNetCore.Mvc;
 14
 15namespace ArturRios.Heimdall.WebApi.Controllers;
 16
 17/// <summary>
 18///     Scope owner invitations (FR-SC-14…FR-SC-18): what a scope's owners see and withdraw (UC-58), and what
 19///     an invited administrator lists, accepts and declines (UC-59…UC-61). An invitation is opened by
 20///     <c>POST /api/scopes/{scopeId}/owners/{personId}</c> (UC-21) when a Scope Admin asks.
 21/// </summary>
 22[Route("api")]
 1923public class ScopeInvitationController(CommandMediator commandMediator, QueryMediator queryMediator) : Controller
 24{
 25    /// <summary>
 26    ///     Lists the scope's open owner invitations (UC-58, FR-SC-15), newest first: each one's id, the invitee's
 27    ///     id, name and address, the inviter's name, and when it was made and expires. A System Admin or an
 28    ///     owner of the scope may call it; the attribute keeps a <c>User</c> out, and the handler checks the
 29    ///     scope (AF-58a) and the ownership (AF-58b).
 30    /// </summary>
 31    [HttpGet("scopes/{scopeId:guid}/owner-invitations")]
 32    [RoleRequirement((int)Roles.SystemAdmin, (int)Roles.ScopeAdmin)]
 33    public async Task<ActionResult<PaginatedOutput<ScopeOwnerInvitationOutput>>> ListForScope(
 34        Guid scopeId, [FromQuery] ListScopeOwnerInvitationsQuery query)
 35    {
 436        query.ScopeId = scopeId;
 437        HttpContext.ApplyActor(query);
 38
 439        var result = await queryMediator
 440            .ExecutePaginatedQueryAsync<ListScopeOwnerInvitationsQuery, ScopeOwnerInvitationOutput>(query, HttpContext.R
 41
 442        return result.ToActionResult(statusMap: ScopeInvitationMessageMap.StatusCodes);
 443    }
 44
 45    /// <summary>
 46    ///     Withdraws an open owner invitation of the scope (UC-58, FR-SC-15): 204 with no body. An ended,
 47    ///     expired or unknown invitation, or another scope's, answers 404 "The invitation was not found or is
 48    ///     no longer open." (AF-58c). Same actors as the listing.
 49    /// </summary>
 50    [HttpDelete("scopes/{scopeId:guid}/owner-invitations/{invitationId:guid}")]
 51    [RoleRequirement((int)Roles.SystemAdmin, (int)Roles.ScopeAdmin)]
 52    [ProducesResponseType(StatusCodes.Status204NoContent)]
 53    public async Task<IActionResult> Withdraw(Guid scopeId, Guid invitationId)
 54    {
 555        var command = new WithdrawScopeOwnerInvitationCommand { ScopeId = scopeId, InvitationId = invitationId };
 556        HttpContext.ApplyActor(command);
 57
 558        var result = await commandMediator
 559            .ExecuteCommandAsync<WithdrawScopeOwnerInvitationCommand, WithdrawScopeOwnerInvitationCommandOutput>(command
 60
 561        return result.Success
 562            ? NoContent()
 563            : result.ToActionResult(statusMap: ScopeInvitationMessageMap.StatusCodes).Result!;
 564    }
 65
 66    /// <summary>
 67    ///     Lists the caller's own open scope owner invitations (UC-59, FR-SC-16), soonest to expire first: each
 68    ///     one's id, the scope's id and name, the inviter's name, and when it was made and expires. Any signed-in
 69    ///     Scope Admin or System Admin may call it; a <c>User</c> cannot be invited, so the attribute keeps them
 70    ///     out.
 71    /// </summary>
 72    [HttpGet("persons/me/scope-invitations")]
 73    [RoleRequirement((int)Roles.SystemAdmin, (int)Roles.ScopeAdmin)]
 74    public async Task<ActionResult<PaginatedOutput<MyScopeInvitationOutput>>> ListMine(
 75        [FromQuery] ListMyScopeInvitationsQuery query)
 76    {
 277        HttpContext.ApplyActor(query);
 78
 279        var result = await queryMediator
 280            .ExecutePaginatedQueryAsync<ListMyScopeInvitationsQuery, MyScopeInvitationOutput>(query, HttpContext.Request
 81
 282        return result.ToActionResult(statusMap: ScopeInvitationMessageMap.StatusCodes);
 283    }
 84
 85    /// <summary>
 86    ///     Accepts a scope owner invitation (UC-60, FR-SC-17): the invitation ends as accepted and the caller
 87    ///     becomes an owner of the scope, together. Only the invitee may; everybody else, and an ended or expired
 88    ///     invitation, gets 404 "The invitation was not found or is no longer open." (AF-60a). An invitation whose
 89    ///     scope was deleted, or whose invitee is no longer an unrestricted administrator, is voided and answered
 90    ///     the same (AF-60b).
 91    /// </summary>
 92    [HttpPost("scope-invitations/{invitationId:guid}/accept")]
 93    [RoleRequirement((int)Roles.SystemAdmin, (int)Roles.ScopeAdmin)]
 94    public async Task<ActionResult<DataOutput<AcceptScopeOwnerInvitationCommandOutput?>>> Accept(Guid invitationId)
 95    {
 796        var command = new AcceptScopeOwnerInvitationCommand { InvitationId = invitationId };
 797        HttpContext.ApplyActor(command);
 98
 799        var result = await commandMediator
 7100            .ExecuteCommandAsync<AcceptScopeOwnerInvitationCommand, AcceptScopeOwnerInvitationCommandOutput>(command);
 101
 7102        return result.ToActionResult(statusMap: ScopeInvitationMessageMap.StatusCodes);
 7103    }
 104
 105    /// <summary>
 106    ///     Declines a scope owner invitation (UC-61, FR-SC-17). Same caller and refusals as UC-60 (AF-61a).
 107    /// </summary>
 108    [HttpPost("scope-invitations/{invitationId:guid}/decline")]
 109    [RoleRequirement((int)Roles.SystemAdmin, (int)Roles.ScopeAdmin)]
 110    public async Task<ActionResult<DataOutput<DeclineScopeOwnerInvitationCommandOutput?>>> Decline(Guid invitationId)
 111    {
 1112        var command = new DeclineScopeOwnerInvitationCommand { InvitationId = invitationId };
 1113        HttpContext.ApplyActor(command);
 114
 1115        var result = await commandMediator
 1116            .ExecuteCommandAsync<DeclineScopeOwnerInvitationCommand, DeclineScopeOwnerInvitationCommandOutput>(command);
 117
 1118        return result.ToActionResult(statusMap: ScopeInvitationMessageMap.StatusCodes);
 1119    }
 120}