139 lines
4.4 KiB
C#
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;
|
|
}
|
|
}
|