Files
step3/api/QuestionsApi/Controllers/QuestionsController.cs
T

139 lines
4.4 KiB
C#

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using MongoDB.Driver;
using QuestionsApi.Models;
using QuestionsApi.Services;
namespace QuestionsApi.Controllers;
[ApiController]
[Route("api/[controller]")]
public class QuestionsController : ControllerBase
{
private readonly QuestionStore _questions;
public QuestionsController(QuestionStore questions)
{
_questions = questions;
}
[HttpGet("systems")]
[Authorize(Roles = "USER,SUPER")]
public async Task<IActionResult> Systems()
{
var systems = await _questions.GetSystemsAsync(User.IsInRole("SUPER"));
return Ok(systems);
}
[HttpPost("search")]
[Authorize(Roles = "USER,SUPER")]
public async Task<IActionResult> Search([FromBody] QuestionSearchRequest req)
{
var questions = await _questions.SearchAsync(req, User.IsInRole("SUPER"));
return Ok(questions.Select(QuestionResponse.FromModel).ToList());
}
[HttpGet("{id}")]
[Authorize(Roles = "USER,SUPER")]
public async Task<IActionResult> Get(string id)
{
var question = await _questions.GetByIdAsync(id);
if (question is null || (!question.IsPublished && !User.IsInRole("SUPER")))
return NotFound();
return Ok(QuestionResponse.FromModel(question));
}
[HttpPost]
[Authorize(Roles = "SUPER")]
public async Task<IActionResult> Create([FromBody] UpsertQuestionRequest req)
{
var validation = ValidateQuestion(req);
if (validation is not null)
return BadRequest(validation);
var created = await _questions.CreateAsync(req);
if (created is null)
return Conflict("Question externalId already exists");
return Ok(QuestionResponse.FromModel(created));
}
[HttpPost("bulk")]
[Authorize(Roles = "SUPER")]
public async Task<IActionResult> BulkUpsert([FromBody] BulkUpsertQuestionsRequest req)
{
if (req.Questions.Count == 0)
return BadRequest("questions required");
foreach (var question in req.Questions)
{
var validation = ValidateQuestion(question);
if (validation is not null)
return BadRequest($"{question.ExternalId}: {validation}");
}
var upserted = await _questions.UpsertManyByExternalIdAsync(req.Questions);
return Ok(new { upserted });
}
[HttpPut("{id}")]
[Authorize(Roles = "SUPER")]
public async Task<IActionResult> Update(string id, [FromBody] UpsertQuestionRequest req)
{
var validation = ValidateQuestion(req);
if (validation is not null)
return BadRequest(validation);
try
{
var updated = await _questions.UpdateAsync(id, req);
if (updated is null)
return NotFound();
return Ok(QuestionResponse.FromModel(updated));
}
catch (MongoWriteException ex) when (ex.WriteError.Category == ServerErrorCategory.DuplicateKey)
{
return Conflict("Question externalId already exists");
}
}
[HttpDelete("{id}")]
[Authorize(Roles = "SUPER")]
public async Task<IActionResult> Delete(string id)
{
var deleted = await _questions.DeleteAsync(id);
if (!deleted)
return NotFound();
return Ok("Deleted");
}
private static string? ValidateQuestion(UpsertQuestionRequest req)
{
if (string.IsNullOrWhiteSpace(req.ExternalId))
return "externalId required";
if (string.IsNullOrWhiteSpace(req.Stem))
return "stem required";
if (string.IsNullOrWhiteSpace(req.LeadIn))
return "leadIn required";
if (req.Choices.Count < 2)
return "at least two choices required";
if (string.IsNullOrWhiteSpace(req.CorrectChoiceId))
return "correctChoiceId required";
if (!req.Choices.Any(c => c.Id == req.CorrectChoiceId))
return "correctChoiceId must match a choice id";
if (string.IsNullOrWhiteSpace(req.Explanation))
return "explanation required";
if (string.IsNullOrWhiteSpace(req.System))
return "system required";
if (string.IsNullOrWhiteSpace(req.Discipline))
return "discipline required";
if (req.Difficulty is < 1 or > 5)
return "difficulty must be between 1 and 5";
return null;
}
}