All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
- Add FluentValidation.AspNetCore package - Create LevelValidators (CreateLevelValidator, UpdateLevelValidator) - Create LessonValidators (CreateLessonValidator, UpdateLessonValidator) - Register FluentValidation in Program.cs with auto-validation - Update LevelsController and LessonsController to use IActionResult - Make service methods virtual for Moq testing compatibility - Add 26 integration tests for LevelsController (13 tests) - Add 34 integration tests for LessonsController (17 tests) Generated by Mistral Vibe. Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
138 lines
4.6 KiB
C#
138 lines
4.6 KiB
C#
using GermanApp.Application.DTOs;
|
|
using GermanApp.Application.Services;
|
|
using Microsoft.AspNetCore.Authorization;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
|
|
namespace GermanApp.Presentation.Controllers;
|
|
|
|
/// <summary>
|
|
/// API controller for managing CEFR levels.
|
|
/// This is part of the Presentation layer.
|
|
/// </summary>
|
|
[ApiController]
|
|
[Route("api/[controller]")]
|
|
[Authorize]
|
|
public class LevelsController : ControllerBase
|
|
{
|
|
private readonly LevelService _levelService;
|
|
|
|
public LevelsController(LevelService levelService)
|
|
{
|
|
_levelService = levelService;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets all CEFR levels ordered by their sort order.
|
|
/// </summary>
|
|
/// <returns>List of all levels</returns>
|
|
[HttpGet]
|
|
[AllowAnonymous]
|
|
public async Task<IActionResult> GetAllLevelsAsync(CancellationToken cancellationToken = default)
|
|
{
|
|
var levels = await _levelService.GetAllLevelsAsync(cancellationToken);
|
|
return Ok(levels);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a specific level by its ID.
|
|
/// </summary>
|
|
/// <param name="id">The level ID</param>
|
|
/// <returns>The level with the specified ID</returns>
|
|
[HttpGet("{id}")]
|
|
[AllowAnonymous]
|
|
public async Task<IActionResult> GetLevelByIdAsync(int id, CancellationToken cancellationToken = default)
|
|
{
|
|
var level = await _levelService.GetLevelByIdAsync(id, cancellationToken);
|
|
if (level == null)
|
|
return NotFound();
|
|
return Ok(level);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a level by its code (e.g., "A1", "B2").
|
|
/// </summary>
|
|
/// <param name="code">The level code</param>
|
|
/// <returns>The level with the specified code</returns>
|
|
[HttpGet("by-code/{code}")]
|
|
[AllowAnonymous]
|
|
public async Task<IActionResult> GetLevelByCodeAsync(string code, CancellationToken cancellationToken = default)
|
|
{
|
|
var level = await _levelService.GetLevelByCodeAsync(code, cancellationToken);
|
|
if (level == null)
|
|
return NotFound();
|
|
return Ok(level);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates a new CEFR level.
|
|
/// </summary>
|
|
/// <param name="dto">The level data</param>
|
|
/// <returns>The created level</returns>
|
|
[HttpPost]
|
|
[Authorize(Roles = "Admin")]
|
|
public async Task<IActionResult> CreateLevelAsync(CreateLevelDto dto, CancellationToken cancellationToken = default)
|
|
{
|
|
var level = await _levelService.CreateLevelAsync(dto, cancellationToken);
|
|
return CreatedAtAction(nameof(GetLevelByIdAsync), new { id = level.Id }, level);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Updates an existing CEFR level.
|
|
/// </summary>
|
|
/// <param name="id">The level ID</param>
|
|
/// <param name="dto">The updated level data</param>
|
|
/// <returns>The updated level</returns>
|
|
[HttpPut("{id}")]
|
|
[Authorize(Roles = "Admin")]
|
|
public async Task<IActionResult> UpdateLevelAsync(int id, UpdateLevelDto dto, CancellationToken cancellationToken = default)
|
|
{
|
|
var level = await _levelService.UpdateLevelAsync(id, dto, cancellationToken);
|
|
if (level == null)
|
|
return NotFound();
|
|
return Ok(level);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Deletes a CEFR level by its ID.
|
|
/// </summary>
|
|
/// <param name="id">The level ID</param>
|
|
/// <returns>No content on success</returns>
|
|
[HttpDelete("{id}")]
|
|
[Authorize(Roles = "Admin")]
|
|
public async Task<IActionResult> DeleteLevelAsync(int id, CancellationToken cancellationToken = default)
|
|
{
|
|
var result = await _levelService.DeleteLevelAsync(id, cancellationToken);
|
|
if (!result)
|
|
return NotFound();
|
|
return NoContent();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the first level (lowest order number) - typically A1.
|
|
/// </summary>
|
|
/// <returns>The first level</returns>
|
|
[HttpGet("first")]
|
|
[AllowAnonymous]
|
|
public async Task<IActionResult> GetFirstLevelAsync(CancellationToken cancellationToken = default)
|
|
{
|
|
var level = await _levelService.GetFirstLevelAsync(cancellationToken);
|
|
if (level == null)
|
|
return NotFound();
|
|
return Ok(level);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the next level after the specified one.
|
|
/// </summary>
|
|
/// <param name="currentLevelId">The current level ID</param>
|
|
/// <returns>The next level</returns>
|
|
[HttpGet("next/{currentLevelId}")]
|
|
[AllowAnonymous]
|
|
public async Task<IActionResult> GetNextLevelAsync(int currentLevelId, CancellationToken cancellationToken = default)
|
|
{
|
|
var level = await _levelService.GetNextLevelAsync(currentLevelId, cancellationToken);
|
|
if (level == null)
|
|
return NotFound();
|
|
return Ok(level);
|
|
}
|
|
}
|