Files
step3/api/QuestionsApi/Controllers/ExamSimulationsController.cs
T
2026-07-14 21:22:09 -05:00

137 lines
5.1 KiB
C#

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using QuestionsApi.Models;
using QuestionsApi.Services;
using System.Security.Claims;
namespace QuestionsApi.Controllers;
[ApiController]
[Route("api/exam-simulations")]
[Authorize(Roles = "USER,SUPER")]
public class ExamSimulationsController(ExamSimulationStore simulations) : ControllerBase
{
[HttpGet("readiness")]
public async Task<IActionResult> Readiness() => Ok(await simulations.GetReadinessAsync(UserId()));
[HttpPost]
public async Task<IActionResult> Create()
{
try
{
var simulation = await simulations.CreateAsync(UserId());
return Ok(await simulations.ToResponseAsync(simulation));
}
catch (InvalidOperationException exception) { return Conflict(exception.Message); }
}
[HttpGet("preview-results")]
[Authorize(Roles = "SUPER")]
public async Task<IActionResult> PreviewResults()
{
try { return Ok(await simulations.BuildResultsPreviewAsync(UserId())); }
catch (InvalidOperationException exception) { return Conflict(exception.Message); }
}
[HttpGet("active")]
public async Task<IActionResult> Active()
{
var simulation = await simulations.FindActiveAsync(UserId());
return simulation is null ? NoContent() : Ok(await simulations.ToResponseAsync(simulation));
}
[HttpGet("completed")]
public async Task<IActionResult> Completed([FromQuery] int limit = 20, [FromQuery] string? before = null)
{
DateTime? cursor = null;
if (!string.IsNullOrWhiteSpace(before))
{
if (!DateTime.TryParse(before, null, System.Globalization.DateTimeStyles.RoundtripKind, out var parsed))
return BadRequest("The completed-simulation cursor is invalid.");
cursor = parsed;
}
return Ok(await simulations.GetCompletedAsync(UserId(), limit, cursor));
}
[HttpGet("{id}")]
public async Task<IActionResult> Get(string id)
{
var simulation = await simulations.GetOwnedAsync(id, UserId());
return simulation is null ? NotFound() : Ok(await simulations.ToResponseAsync(simulation));
}
[HttpPost("{id}/blocks/start")]
public async Task<IActionResult> StartBlock(string id)
{
var simulation = await simulations.GetOwnedAsync(id, UserId());
if (simulation is null) return NotFound();
try { return Ok(await simulations.ToResponseAsync(await simulations.StartNextBlockAsync(simulation))); }
catch (InvalidOperationException exception) { return Conflict(exception.Message); }
}
[HttpPut("{id}/responses")]
public async Task<IActionResult> SaveResponse(string id, SaveSimulationResponseRequest request)
{
var simulation = await simulations.GetOwnedAsync(id, UserId());
if (simulation is null) return NotFound();
try { return Ok(await simulations.ToResponseAsync(await simulations.SaveResponseAsync(simulation, request))); }
catch (ArgumentException exception) { return BadRequest(exception.Message); }
catch (InvalidOperationException exception) { return Conflict(exception.Message); }
}
[HttpPost("{id}/blocks/submit")]
public async Task<IActionResult> SubmitBlock(string id)
{
var simulation = await simulations.GetOwnedAsync(id, UserId());
if (simulation is null) return NotFound();
try { return Ok(await simulations.ToResponseAsync(await simulations.SubmitBlockAsync(simulation))); }
catch (InvalidOperationException exception) { return Conflict(exception.Message); }
}
[HttpPost("{id}/abandon")]
public async Task<IActionResult> Abandon(string id)
{
var simulation = await simulations.GetOwnedAsync(id, UserId());
if (simulation is null) return NotFound();
await simulations.AbandonAsync(simulation);
return NoContent();
}
private string UserId() => User.FindFirstValue(ClaimTypes.NameIdentifier)
?? User.FindFirstValue(ClaimTypes.Name)
?? User.Identity?.Name
?? throw new UnauthorizedAccessException();
}
[ApiController]
[Route("api/step3-outcomes")]
[Authorize(Roles = "USER,SUPER")]
public class Step3OutcomesController(ExamSimulationStore simulations) : ControllerBase
{
[HttpGet("me")]
public async Task<IActionResult> Get()
{
var outcome = await simulations.GetOutcomeAsync(UserId());
return outcome is null ? NoContent() : Ok(outcome);
}
[HttpPut("me")]
public async Task<IActionResult> Put(UpsertStep3OutcomeRequest request)
{
try { return Ok(await simulations.UpsertOutcomeAsync(UserId(), request)); }
catch (ArgumentException exception) { return BadRequest(exception.Message); }
}
[HttpDelete("me")]
public async Task<IActionResult> Delete()
{
await simulations.DeleteOutcomeAsync(UserId());
return NoContent();
}
private string UserId() => User.FindFirstValue(ClaimTypes.NameIdentifier)
?? User.FindFirstValue(ClaimTypes.Name)
?? User.Identity?.Name
?? throw new UnauthorizedAccessException();
}