Add CCS study tools and case reporting
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using QuestionsApi.Models;
|
||||
using QuestionsApi.Services;
|
||||
using System.Security.Claims;
|
||||
|
||||
namespace QuestionsApi.Controllers;
|
||||
|
||||
[ApiController, Route("api/ccs/reports")]
|
||||
public sealed class CcsCaseReportsController(CcsStore cases, CcsCaseReportStore reports) : ControllerBase
|
||||
{
|
||||
private const int MinDetails = 10;
|
||||
private const int MaxDetails = 2000;
|
||||
|
||||
[HttpPost, Authorize(Roles = "USER,SUPER")]
|
||||
public async Task<IActionResult> Create(CreateCcsCaseReportRequest request)
|
||||
{
|
||||
var category = request.Category.Trim().ToLowerInvariant();
|
||||
var details = request.Details.Trim();
|
||||
if (!CcsCaseReportCategory.All.Contains(category)) return BadRequest("category is invalid");
|
||||
if (details.Length is < MinDetails or > MaxDetails) return BadRequest($"details must be between {MinDetails} and {MaxDetails} characters");
|
||||
var attempt = await cases.GetOwnedAsync(request.AttemptId.Trim(), UserId());
|
||||
if (attempt is null) return NotFound();
|
||||
var report = await reports.CreateAsync(attempt, await cases.DefinitionAsync(attempt), category, details);
|
||||
return StatusCode(StatusCodes.Status201Created, new { id = report.Id, report.AttemptId, report.Category, report.Status, report.CreatedUtc });
|
||||
}
|
||||
|
||||
[HttpGet, Authorize(Roles = "SUPER")]
|
||||
public async Task<IActionResult> List([FromQuery] string status = "open", [FromQuery] string category = "", [FromQuery] string caseExternalId = "", [FromQuery] string userId = "", [FromQuery] int skip = 0, [FromQuery] int limit = 50)
|
||||
{
|
||||
status = status.Trim().ToLowerInvariant(); category = category.Trim().ToLowerInvariant();
|
||||
if (status != "all" && status != QuestionReportStatus.Open && !QuestionReportStatus.Terminal.Contains(status)) return BadRequest("status is invalid");
|
||||
if (category.Length > 0 && !CcsCaseReportCategory.All.Contains(category)) return BadRequest("category is invalid");
|
||||
if (skip < 0 || limit is < 1 or > 100) return BadRequest("pagination is invalid");
|
||||
return Ok(await reports.SearchAsync(status, category, caseExternalId.Trim(), userId.Trim(), skip, limit));
|
||||
}
|
||||
|
||||
[HttpGet("{id}"), Authorize(Roles = "SUPER")]
|
||||
public async Task<IActionResult> Get(string id) => await reports.GetAsync(id.Trim()) is { } report ? Ok(report) : NotFound();
|
||||
|
||||
[HttpPatch("{id}/status"), Authorize(Roles = "SUPER")]
|
||||
public async Task<IActionResult> Close(string id, UpdateQuestionReportStatusRequest request)
|
||||
{
|
||||
var status = request.Status.Trim().ToLowerInvariant();
|
||||
if (!QuestionReportStatus.Terminal.Contains(status)) return BadRequest("status must be resolved or dismissed");
|
||||
if (request.ReviewerNote.Length > MaxDetails) return BadRequest("reviewerNote must be 2000 characters or fewer");
|
||||
var updated = await reports.CloseAsync(id.Trim(), status, UserId(), request.ReviewerNote.Trim());
|
||||
return updated is null ? Conflict("report is missing or already closed") : Ok(updated);
|
||||
}
|
||||
|
||||
private string UserId() => User.FindFirstValue(ClaimTypes.NameIdentifier) ?? User.FindFirstValue(ClaimTypes.Name) ?? User.Identity?.Name ?? "";
|
||||
}
|
||||
Reference in New Issue
Block a user