< Summary - Heimdall API

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

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
HelloWorld()100%11100%
Ready()50%44100%
Detailed()50%44100%

File(s)

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

#LineLine coverage
 1using ArturRios.Heimdall.Domain.Enums;
 2using ArturRios.Heimdall.Query.HealthChecks;
 3using ArturRios.Heimdall.Query.Input;
 4using ArturRios.Heimdall.Query.Output;
 5using ArturRios.Heimdall.WebApi.Monitoring;
 6using ArturRios.Heimdall.WebApi.Security;
 7using ArturRios.Mediator.Query;
 8using ArturRios.Output;
 9using ArturRios.Util.Http;
 10using ArturRios.Util.WebApi.AspNetCore;
 11using ArturRios.Util.WebApi.Security.Attributes;
 12using Microsoft.AspNetCore.Mvc;
 13
 14namespace ArturRios.Heimdall.WebApi.Controllers;
 15
 16/// <remarks>
 17///     Under <c>api/</c> like every other route, because that prefix is all that carries a request on
 18///     the web UI's host through to the API — see <c>OpenApiContractTests</c>. The container probe and
 19///     yggdrasil's status catalog call the root <c>/healthcheck</c>, which the edge pipeline in
 20///     <c>EdgePipeline</c> rewrites to this address.
 21/// </remarks>
 22[Route("api/healthcheck")]
 2623public class HealthCheckController(QueryMediator queryMediator, DatabaseHealthCheck databaseHealthCheck,
 2624    ReadinessCache readinessCache) : Controller
 25{
 26    /// <summary>
 27    ///     Basic liveness check (UC-30, FR-HC-01): confirms the API process is up and responding.
 28    ///     Public — no authentication required.
 29    /// </summary>
 30    [HttpGet]
 31    [Route("")]
 32    [AllowAnonymous]
 33    public ActionResult<DataOutput<string?>> HelloWorld()
 34    {
 2135        var result = DataOutput<string?>.New
 2136            .WithData("Hello world!")
 2137            .WithMessage("Heimdall API is working.");
 38
 2139        return result.ToActionResult(HttpStatusCodes.Ok);
 40    }
 41
 42    /// <summary>
 43    ///     Readiness check (#215): <c>200 OK</c> when the database answers, <c>503 Service Unavailable</c>
 44    ///     when it does not. Public — no authentication required.
 45    /// </summary>
 46    /// <remarks>
 47    ///     Liveness above reads no database, so an instance that has lost it stays "healthy" and keeps
 48    ///     receiving traffic it can only fail. This is the probe for that: an orchestrator or load
 49    ///     balancer stops routing to the instance without restarting it. It asks
 50    ///     <c>DatabaseHealthCheck</c> alone and answers with the bare status, never a service
 51    ///     breakdown or a cause, which is why it can be anonymous where <c>detailed</c> cannot. The
 52    ///     cause is in the log, where <c>DatabaseHealthCheck</c> writes it.
 53    ///     The answer is held for five seconds (<c>ReadinessCache</c>), so an anonymous caller
 54    ///     cannot make the probe a database read per request.
 55    /// </remarks>
 56    [HttpGet]
 57    [Route("ready")]
 58    [AllowAnonymous]
 59    [SuppressServerErrorLog]
 60    public async Task<ActionResult<DataOutput<string?>>> Ready()
 61    {
 362        var healthy = await readinessCache.IsReadyAsync(databaseHealthCheck.IsHealthyAsync);
 63
 364        var result = DataOutput<string?>.New
 365            .WithData(healthy ? HealthStatuses.Healthy : HealthStatuses.Unhealthy);
 66
 367        return result.ToActionResult(healthy ? HttpStatusCodes.Ok : HttpStatusCodes.ServiceUnavailable);
 368    }
 69
 70    /// <summary>
 71    ///     Detailed health check (UC-30, FR-HC-02…07): reports the status of each verified service
 72    ///     plus an aggregate general status. Restricted to System Admins (AF-30a → 403, AF-30b → 401).
 73    ///     Returns <c>200 OK</c> when healthy and <c>503 Service Unavailable</c> when any verified
 74    ///     service is down (FR-HC-07, AF-30c).
 75    /// </summary>
 76    [HttpGet]
 77    [Route("detailed")]
 78    [RoleRequirement((int)Roles.SystemAdmin)]
 79    public async Task<ActionResult<DataOutput<HealthCheckOutput?>>> Detailed()
 80    {
 281        var result = await queryMediator
 282            .ExecuteQueryAsync<DetailedHealthQuery, HealthCheckOutput>(new DetailedHealthQuery(), HttpContext.RequestAbo
 83
 284        var statusCode = result.Data?.Status == HealthStatuses.Healthy
 285            ? HttpStatusCodes.Ok
 286            : HttpStatusCodes.ServiceUnavailable;
 87
 288        return result.ToActionResult(statusCode);
 289    }
 90}