- 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>
140 lines
5 KiB
C#
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"
|
|
});
|
|
}
|
|
}
|