DeutschLernen/GermanApp/Presentation/Endpoints/LessonsEndpoints.cs
Lasse Rune Hansen ba81359afa fix(backend): update Lesson entity and related code to use LevelId foreign key
- Updated Lesson entity to use LevelId (int) instead of Level (int)
- Added navigation property Level (Level entity)
- Added Order, Topic, and IsActive properties to Lesson
- Updated Lesson.Create() factory method signature to accept levelId, title, order, topic, description
- Split Update method into individual property update methods
- Updated LessonDto to use LevelId, LevelName, LevelCode instead of Level int
- Added CreateLessonDto and UpdateLessonDto with all required fields
- Added UpdateFromDto extension method for Lesson
- Updated CreateLessonCommand to validate LevelId instead of Level
- Updated SeedDataExtension to seed Level entities first, then use new Lesson.Create() signature
- Made SeedDatabaseAsync async and updated Program.cs to await it
- Updated LessonsEndpoints to use GetByLevelAsync for beginner/advanced queries
- Fixed missing using directive for Domain.Entities

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-06-07 21:25:46 +02:00

155 lines
5.8 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()));
})
.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());
})
.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()));
})
.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()));
})
.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()));
})
.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"
});
}
}