using MaBibli.Api.Services.Catalogue; using MaBibli.Api.Services.Identite; using MaBibli.Shared.Catalogue; using MaBibli.Shared.Dtos; using MaBibli.Shared.Entites; namespace MaBibli.Api.Endpoints; public static class LivresEndpoints { /// /// CRUD du catalogue. /// /// /// Aucune lecture ne filtre sur AjoutePar : la bibliothèque est commune. /// Ce champ n'est même pas acceptable en paramètre de requête, précisément pour qu'on ne /// puisse pas s'en servir comme d'un cloisonnement par inadvertance. /// public static IEndpointRouteBuilder MapLivresEndpoints(this IEndpointRouteBuilder routes) { var groupe = routes.MapGroup("/api/livres").WithTags("Livres"); groupe.MapGet("/", async ( string? recherche, Format? format, Statut? statut, IServiceCatalogue service, CancellationToken ct) => { var criteres = new CritereLivres { Recherche = recherche, Format = format, Statut = statut, }; return Results.Ok(await service.ListerAsync(criteres, ct)); }) .WithName("ListerLivres") .WithSummary("Liste le catalogue, filtrable par format et statut, cherchable sur titre et auteur.") .WithDescription( "Catalogue unique : physiques et numériques cohabitent, distingués par leur format. " + "La bibliothèque est commune — la liste n'est jamais restreinte à l'utilisateur courant.") .Produces>(); groupe.MapGet("/{id:int}", async (int id, IServiceCatalogue service, CancellationToken ct) => { var livre = await service.ObtenirAsync(id, ct); return livre is null ? Results.NotFound() : Results.Ok(livre); }) .WithName("ObtenirLivre") .Produces() .Produces(StatusCodes.Status404NotFound); groupe.MapPost("/", async ( EnregistrementLivre saisie, IServiceCatalogue service, IFournisseurUtilisateur utilisateurs, CancellationToken ct) => { // AjoutePar vient du serveur, pas de la charge utile. var utilisateur = utilisateurs.Obtenir(); var resultat = await service.CreerAsync(saisie, utilisateur.Identifiant, ct); return resultat.Erreur is not null ? Results.BadRequest(new { erreur = resultat.Erreur }) : Results.Created($"/api/livres/{resultat.Livre!.Id}", resultat.Livre); }) .WithName("CreerLivre") .WithSummary("Ajoute un livre. « AjoutePar » est déterminé par le serveur.") .Produces(StatusCodes.Status201Created) .Produces(StatusCodes.Status400BadRequest); groupe.MapPut("/{id:int}", async ( int id, EnregistrementLivre saisie, IServiceCatalogue service, CancellationToken ct) => { var resultat = await service.ModifierAsync(id, saisie, ct); if (resultat.EstIntrouvable) { return Results.NotFound(); } return resultat.Erreur is not null ? Results.BadRequest(new { erreur = resultat.Erreur }) : Results.Ok(resultat.Livre); }) .WithName("ModifierLivre") .WithSummary("Modifie un livre. « AjoutePar » et « DateAjout » restent inchangés.") .Produces() .Produces(StatusCodes.Status400BadRequest) .Produces(StatusCodes.Status404NotFound); groupe.MapPatch("/{id:int}/statut", async ( int id, ChangementStatut changement, IServiceCatalogue service, CancellationToken ct) => { var livre = await service.ChangerStatutAsync(id, changement.Statut, ct); return livre is null ? Results.NotFound() : Results.Ok(livre); }) .WithName("ChangerStatutLivre") .WithSummary("Change le seul statut de lecture — l'action la plus fréquente.") .Produces() .Produces(StatusCodes.Status404NotFound); groupe.MapDelete("/{id:int}", async (int id, IServiceCatalogue service, CancellationToken ct) => { var supprime = await service.SupprimerAsync(id, ct); return supprime ? Results.NoContent() : Results.NotFound(); }) .WithName("SupprimerLivre") .Produces(StatusCodes.Status204NoContent) .Produces(StatusCodes.Status404NotFound); return routes; } /// Expose l'identité de la personne connectée, pour affichage côté client. public static IEndpointRouteBuilder MapIdentiteEndpoints(this IEndpointRouteBuilder routes) { routes.MapGet("/api/moi", (IFournisseurUtilisateur utilisateurs) => Results.Ok(utilisateurs.Obtenir())) .WithName("UtilisateurCourant") .WithTags("Identité") .WithSummary("Identité lue dans les en-têtes SSOwat, ou utilisateur simulé en développement.") .Produces(); return routes; } }