feat(ai): queue strategy snapshots

This commit is contained in:
cesnimda
2026-08-09 12:51:46 +02:00
parent 5eb9b3cb96
commit a62122640c
12 changed files with 790 additions and 158 deletions
@@ -14,14 +14,14 @@ public sealed class OperationsController(UserOperationStore operations) : Contro
public async Task<ActionResult<IReadOnlyList<OperationDto>>> List([FromQuery] int limit = 25, CancellationToken cancellationToken = default)
{
if (limit is < 1 or > 100) return BadRequest(new { code = "invalid_limit", message = "Limit must be between 1 and 100." });
return Ok((await operations.ListAsync(limit, cancellationToken)).Select(ToDto).ToList());
return Ok((await operations.ListAsync(limit, cancellationToken)).Select(OperationDto.From).ToList());
}
[HttpGet("{id:guid}")]
public async Task<ActionResult<OperationDto>> Get(Guid id, CancellationToken cancellationToken)
{
var operation = await operations.GetAsync(id, cancellationToken);
return operation is null ? NotFound() : Ok(ToDto(operation));
return operation is null ? NotFound() : Ok(OperationDto.From(operation));
}
[HttpPost("{id:guid}/cancel")]
@@ -30,7 +30,7 @@ public sealed class OperationsController(UserOperationStore operations) : Contro
if (await operations.GetAsync(id, cancellationToken) is null) return NotFound();
if (!await operations.RequestCancellationAsync(id, cancellationToken))
return Conflict(new { code = "operation_not_cancellable", message = "This operation can no longer be cancelled." });
return Ok(ToDto((await operations.GetAsync(id, cancellationToken))!));
return Ok(OperationDto.From((await operations.GetAsync(id, cancellationToken))!));
}
[HttpPost("{id:guid}/retry")]
@@ -39,24 +39,9 @@ public sealed class OperationsController(UserOperationStore operations) : Contro
if (await operations.GetAsync(id, cancellationToken) is null) return NotFound();
if (!await operations.RetryAsync(id, cancellationToken))
return Conflict(new { code = "operation_not_retryable", message = "Only failed or cancelled operations can be retried." });
return Ok(ToDto((await operations.GetAsync(id, cancellationToken))!));
return Ok(OperationDto.From((await operations.GetAsync(id, cancellationToken))!));
}
private static OperationDto ToDto(UserOperation operation) => new(
operation.Id,
operation.TaskType,
operation.Status,
operation.SubjectType,
operation.CreatedAtUtc,
operation.StartedAtUtc,
operation.CompletedAtUtc,
operation.DeadlineAtUtc,
operation.CancellationRequestedAtUtc,
operation.ProgressStage,
operation.ProgressPercent,
operation.FailureCategory,
!OperationStatuses.IsTerminal(operation.Status) && operation.CancellationRequestedAtUtc is null,
operation.Status is OperationStatuses.Failed or OperationStatuses.Cancelled);
}
public sealed record OperationDto(
@@ -73,7 +58,24 @@ public sealed record OperationDto(
int? ProgressPercent,
string? FailureCategory,
bool CanCancel,
bool CanRetry);
bool CanRetry)
{
public static OperationDto From(UserOperation operation) => new(
operation.Id,
operation.TaskType,
operation.Status,
operation.SubjectType,
operation.CreatedAtUtc,
operation.StartedAtUtc,
operation.CompletedAtUtc,
operation.DeadlineAtUtc,
operation.CancellationRequestedAtUtc,
operation.ProgressStage,
operation.ProgressPercent,
operation.FailureCategory,
!OperationStatuses.IsTerminal(operation.Status) && operation.CancellationRequestedAtUtc is null,
operation.Status is OperationStatuses.Failed or OperationStatuses.Cancelled);
}
[ApiController]
[Route("api/notifications")]