Initial STEP 3 study game and questions API
This commit is contained in:
@@ -0,0 +1,138 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user