Files
AutopilotMonitor/src/Backend/AutopilotMonitor.Functions/Functions/Backup/GetBackupJobStatusFunction.cs
T
Oliver Kieselbach 9fd343d328 fix(backend): typed bodies and error envelope for backup, session-restore and delegation wrappers
Six functions wrote their responses through a private wrapper taking the body as
object, which no typed-response guard could see. Their error bodies carried the code
in the error slot, no correlation id, and RestoreRow returned the raw exception text
on a 500.

- Backup job status, row restore and backup trigger answer through ResponseHelper:
  errors are the envelope, the 500 is sanitized, the 202 body is BackupTriggerResponse.
- Same for the session-deletion maintenance trigger, RestoreSession's three error
  sites and the delegation acknowledgements (MessageResponse).
- Backup codes are constants (Constants.BackupErrorCodes) and part of apiErrorCodes;
  RunAlreadyActive and EnqueueFailed join ApiErrorCodes.
- TypedResponseGuardTests gains guard F: no HttpResponseData-returning method with an
  object parameter. BackupWireParityTests pins that the move from the backup
  serializer options to the worker pipeline leaves the wire unchanged.
- Web reads BackupTriggerResponse from the generated wire types.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-21 22:19:38 +02:00

45 lines
1.8 KiB
C#

using System.Threading.Tasks;
using AutopilotMonitor.Functions.DataAccess.TableStorage;
using AutopilotMonitor.Functions.Helpers;
using AutopilotMonitor.Shared;
using AutopilotMonitor.Shared.Models.Backup;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Http;
using Microsoft.Extensions.Logging;
namespace AutopilotMonitor.Functions.Functions.Backup
{
/// <summary>
/// <c>GET /api/global/backups/jobs/{jobId}</c> — polling endpoint for the
/// 202-Accepted async backup workflow. GA-only via <c>EndpointAccessPolicyCatalog</c>.
/// Returns the live <see cref="BackupJobStatus"/> DTO including
/// <c>backupOutcome</c> (Success / Partial) and <c>lastHeartbeatUtc</c> so the
/// UI can render Completed / Partial / Failed banners and the operator can spot
/// stalled jobs at a glance.
/// </summary>
public class GetBackupJobStatusFunction
{
private readonly BackupJobsRepository _jobs;
private readonly ILogger<GetBackupJobStatusFunction> _logger;
public GetBackupJobStatusFunction(BackupJobsRepository jobs, ILogger<GetBackupJobStatusFunction> logger)
{
_jobs = jobs;
_logger = logger;
}
[Function("GetBackupJobStatus")]
public async Task<HttpResponseData> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "global/backups/jobs/{jobId}")] HttpRequestData req,
string jobId)
{
var (job, _) = await _jobs.GetWithETagAsync(jobId, req.FunctionContext.CancellationToken);
if (job is null)
{
return await req.NotFoundAsync($"Backup job {jobId} not found.", Constants.BackupErrorCodes.JobNotFound);
}
return await req.OkAsync(job);
}
}
}