- Added .RequireAuthorization() to all GET endpoints in LessonsEndpoints.cs
- /api/lessons
- /api/lessons/{id}
- /api/lessons/beginner
- /api/lessons/advanced
- /api/lessons/level/{level}
- Updated admin-module.md feature documentation
- Marked all acceptance criteria as complete
- Updated functional requirements status to Complete
- Updated Definition of Done criteria
- Added note about Phase 4 completion
This ensures all learning content endpoints now require JWT authentication,
completing the Phase 4 Admin Module authentication enforcement requirement.
Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
160 lines
6 KiB
C#
160 lines
6 KiB
C#
using GermanApp.Application.DTOs;
|
|
using GermanApp.Application.UseCases.Commands;
|
|
using GermanApp.Domain.Entities;
|
|
using GermanApp.Domain.Interfaces;
|
|
using Microsoft.AspNetCore.Authorization;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
|
|
namespace GermanApp.Presentation.Endpoints;
|
|
|
|
/// <summary>
|
|
/// Minimal API endpoints for Lesson resources.
|
|
/// This is part of the Presentation layer.
|
|
/// </summary>
|
|
public static class LessonsEndpoints
|
|
{
|
|
public static void MapLessonsEndpoints(this WebApplication app)
|
|
{
|
|
var group = app.MapGroup("/api/lessons");
|
|
|
|
// GET /api/lessons
|
|
group.MapGet("/", async ([FromServices] ILessonRepository repository) =>
|
|
{
|
|
var lessons = await repository.GetAllAsync();
|
|
return Results.Ok(lessons.Select(l => l.ToDto()));
|
|
})
|
|
.RequireAuthorization()
|
|
.WithName("GetAllLessons")
|
|
.WithOpenApi(operation => new(operation)
|
|
{
|
|
Summary = "Get all lessons",
|
|
Description = "Retrieves all available German lessons"
|
|
});
|
|
|
|
// GET /api/lessons/{id}
|
|
group.MapGet("/{id}", async ([FromRoute] int id, [FromServices] ILessonRepository repository) =>
|
|
{
|
|
var lesson = await repository.GetByIdAsync(id);
|
|
return lesson is null ? Results.NotFound() : Results.Ok(lesson.ToDto());
|
|
})
|
|
.RequireAuthorization()
|
|
.WithName("GetLessonById")
|
|
.WithOpenApi(operation => new(operation)
|
|
{
|
|
Summary = "Get lesson by ID",
|
|
Description = "Retrieves a specific lesson by its identifier"
|
|
});
|
|
|
|
// GET /api/lessons/beginner
|
|
group.MapGet("/beginner", async ([FromServices] ILessonRepository repository) =>
|
|
{
|
|
// Beginner lessons are A1 (level 1) and A2 (level 2)
|
|
var beginnerLessons = new List<Lesson>();
|
|
var level1Lessons = await repository.GetByLevelAsync(1);
|
|
var level2Lessons = await repository.GetByLevelAsync(2);
|
|
beginnerLessons.AddRange(level1Lessons);
|
|
beginnerLessons.AddRange(level2Lessons);
|
|
return Results.Ok(beginnerLessons.OrderBy(l => l.LevelId).ThenBy(l => l.Order).Select(l => l.ToDto()));
|
|
})
|
|
.RequireAuthorization()
|
|
.WithName("GetBeginnerLessons")
|
|
.WithOpenApi(operation => new(operation)
|
|
{
|
|
Summary = "Get beginner lessons",
|
|
Description = "Retrieves all lessons at beginner level (A1-A2)"
|
|
});
|
|
|
|
// GET /api/lessons/advanced
|
|
group.MapGet("/advanced", async ([FromServices] ILessonRepository repository) =>
|
|
{
|
|
// Advanced lessons are B2 (level 4) and C1 (level 5)
|
|
var advancedLessons = new List<Lesson>();
|
|
var level4Lessons = await repository.GetByLevelAsync(4);
|
|
var level5Lessons = await repository.GetByLevelAsync(5);
|
|
advancedLessons.AddRange(level4Lessons);
|
|
advancedLessons.AddRange(level5Lessons);
|
|
return Results.Ok(advancedLessons.OrderBy(l => l.LevelId).ThenBy(l => l.Order).Select(l => l.ToDto()));
|
|
})
|
|
.RequireAuthorization()
|
|
.WithName("GetAdvancedLessons")
|
|
.WithOpenApi(operation => new(operation)
|
|
{
|
|
Summary = "Get advanced lessons",
|
|
Description = "Retrieves all lessons at advanced level (B2-C1)"
|
|
});
|
|
|
|
// GET /api/lessons/level/{level}
|
|
group.MapGet("/level/{level}", async ([FromRoute] int level, [FromServices] ILessonRepository repository) =>
|
|
{
|
|
var lessons = await repository.GetByLevelAsync(level);
|
|
return Results.Ok(lessons.Select(l => l.ToDto()));
|
|
})
|
|
.RequireAuthorization()
|
|
.WithName("GetLessonsByLevel")
|
|
.WithOpenApi(operation => new(operation)
|
|
{
|
|
Summary = "Get lessons by level",
|
|
Description = "Retrieves all lessons at a specific proficiency level"
|
|
});
|
|
|
|
// POST /api/lessons
|
|
group.MapPost("/", async (
|
|
[FromBody] CreateLessonDto dto,
|
|
[FromServices] ICommandHandler<CreateLessonCommand, LessonDto> handler,
|
|
CancellationToken cancellationToken) =>
|
|
{
|
|
var command = new CreateLessonCommand(dto);
|
|
var result = await handler.Handle(command, cancellationToken);
|
|
return Results.Created($"/api/lessons/{result.Id}", result);
|
|
})
|
|
.RequireAuthorization()
|
|
.WithName("CreateLesson")
|
|
.WithOpenApi(operation => new(operation)
|
|
{
|
|
Summary = "Create a new lesson",
|
|
Description = "Creates a new German lesson"
|
|
});
|
|
|
|
// PUT /api/lessons/{id}
|
|
group.MapPut("/{id}", async (
|
|
[FromRoute] int id,
|
|
[FromBody] UpdateLessonDto dto,
|
|
[FromServices] ILessonRepository repository) =>
|
|
{
|
|
var existingLesson = await repository.GetByIdAsync(id);
|
|
if (existingLesson is null)
|
|
return Results.NotFound();
|
|
|
|
// Map DTO to entity
|
|
existingLesson.UpdateFromDto(dto);
|
|
await repository.UpdateAsync(existingLesson);
|
|
|
|
return Results.Ok(existingLesson.ToDto());
|
|
})
|
|
.RequireAuthorization()
|
|
.WithName("UpdateLesson")
|
|
.WithOpenApi(operation => new(operation)
|
|
{
|
|
Summary = "Update a lesson",
|
|
Description = "Updates an existing lesson"
|
|
});
|
|
|
|
// DELETE /api/lessons/{id}
|
|
group.MapDelete("/{id}", async ([FromRoute] int id, [FromServices] ILessonRepository repository) =>
|
|
{
|
|
var lesson = await repository.GetByIdAsync(id);
|
|
if (lesson is null)
|
|
return Results.NotFound();
|
|
|
|
await repository.DeleteAsync(lesson);
|
|
return Results.NoContent();
|
|
})
|
|
.RequireAuthorization()
|
|
.WithName("DeleteLesson")
|
|
.WithOpenApi(operation => new(operation)
|
|
{
|
|
Summary = "Delete a lesson",
|
|
Description = "Deletes a lesson by its identifier"
|
|
});
|
|
}
|
|
}
|