Files
mabibli/MaBibli.Api/Endpoints/PretsEndpoints.cs
T
Mathieu LimonierandClaude Opus 5 6a6d745af4 MaBibli 1.0.0
Gestion de bibliothèque personnelle auto-hébergée : catalogue, prêts,
scan de code-barres, consultation hors-ligne.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-22 22:36:16 +02:00

100 lines
4.4 KiB
C#

using MaBibli.Api.Services.Prets;
using MaBibli.Shared.Dtos;
namespace MaBibli.Api.Endpoints;
public static class PretsEndpoints
{
/// <summary>
/// Prêts : qui a quoi, depuis quand, et l'historique de ce qui est revenu.
/// </summary>
/// <remarks>
/// <b>Aucun point d'entrée ne filtre sur l'utilisateur courant</b>, et aucun n'enregistre qui
/// a saisi le prêt. La bibliothèque est commune : tout le foyer voit les mêmes prêts, et
/// n'importe qui peut noter le retour d'un livre qu'il a récupéré.
/// <para>
/// L'emprunteur est du <b>texte libre</b> : ce sont des personnes extérieures au foyer, sans
/// compte YunoHost. Ne jamais le rapprocher d'un identifiant d'utilisateur.
/// </para>
/// </remarks>
public static IEndpointRouteBuilder MapPretsEndpoints(this IEndpointRouteBuilder routes)
{
var prets = routes.MapGroup("/api/prets").WithTags("Prêts");
prets.MapGet("/en-cours", async (IServicePrets service, CancellationToken ct) =>
Results.Ok(await service.EnCoursAsync(ct)))
.WithName("ListerPretsEnCours")
.WithSummary("Les livres qui ne sont pas à la maison, du prêt le plus ancien au plus récent.")
.WithDescription(
"L'ordre est volontairement croissant : ce qu'on cherche ici, c'est le livre "
+ "sorti depuis des mois qu'on avait oublié, pas celui prêté hier.")
.Produces<IReadOnlyList<PretDto>>();
prets.MapPost("/{id:int}/retour", async (
int id,
RetourPret retour,
IServicePrets service,
CancellationToken ct) =>
{
var resultat = await service.RendreAsync(id, retour, ct);
if (resultat.EstIntrouvable)
{
return Results.NotFound();
}
return resultat.Erreur is not null
? Results.BadRequest(new { erreur = resultat.Erreur })
: Results.Ok(resultat.Pret);
})
.WithName("MarquerPretRendu")
.WithSummary("Clôt un prêt en posant sa date de retour. Le prêt n'est pas supprimé : il devient l'historique.")
.Produces<PretDto>()
.Produces(StatusCodes.Status400BadRequest)
.Produces(StatusCodes.Status404NotFound);
var parLivre = routes.MapGroup("/api/livres/{livreId:int}/prets").WithTags("Prêts");
parLivre.MapGet("/", async (int livreId, IServicePrets service, CancellationToken ct) =>
{
var historique = await service.HistoriqueAsync(livreId, ct);
return historique is null ? Results.NotFound() : Results.Ok(historique);
})
.WithName("HistoriquePretsLivre")
.WithSummary("Tous les prêts de ce livre, clos comme en cours, du plus récent au plus ancien.")
.WithDescription(
"Une liste vide signifie « jamais prêté » ; un livre inconnu répond 404. "
+ "Les prêts clos ne disparaissent jamais : c'est la raison d'être de la table.")
.Produces<IReadOnlyList<PretDto>>()
.Produces(StatusCodes.Status404NotFound);
parLivre.MapPost("/", async (
int livreId,
EnregistrementPret saisie,
IServicePrets service,
CancellationToken ct) =>
{
var resultat = await service.PreterAsync(livreId, saisie, ct);
if (resultat.EstIntrouvable)
{
return Results.NotFound();
}
return resultat.Erreur is not null
? Results.BadRequest(new { erreur = resultat.Erreur })
: Results.Created($"/api/prets/{resultat.Pret!.Id}", resultat.Pret);
})
.WithName("PreterLivre")
.WithSummary("Prête ce livre à quelqu'un.")
.WithDescription(
"Refusé si le livre est déjà sorti (un exemplaire ne se prête pas deux fois) ou "
+ "s'il est au format numérique (une fiche d'ebook n'a pas d'exemplaire à confier).")
.Produces<PretDto>(StatusCodes.Status201Created)
.Produces(StatusCodes.Status400BadRequest)
.Produces(StatusCodes.Status404NotFound);
return routes;
}
}