DeutschLernen/GermanApp/Presentation/Endpoints/LessonsEndpoints.cs
Lasse Rune Hansen d90e4792d6 Initial commit: GermanApp with Clean Architecture
- Domain layer: Lesson entity, GermanWord value object, repository interfaces
- Application layer: CQRS commands, DTOs with mapping
- Infrastructure layer: EF Core with SQLite, LessonRepository
- Presentation layer: Minimal API endpoints for lessons CRUD

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-05-31 18:14:51 +02:00

140 lines
5 KiB
C#

using GermanApp.Application.DTOs;
using GermanApp.Application.UseCases.Commands;
using GermanApp.Domain.Interfaces;
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) =>
{
var lessons = await repository.GetBeginnerLessonsAsync();
return Results.Ok(lessons.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) =>
{
var lessons = await repository.GetAdvancedLessonsAsync();
return Results.Ok(lessons.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);
})
.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.Update(dto.Title, dto.Description, dto.Level);
await repository.UpdateAsync(existingLesson);
return Results.Ok(existingLesson.ToDto());
})
.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();
})
.WithName("DeleteLesson")
.WithOpenApi(operation => new(operation)
{
Summary = "Delete a lesson",
Description = "Deletes a lesson by its identifier"
});
}
}