- Gerbil.CharacterTraits (List<string>, JSON text column, opaque labels) + CharacterNote (string?). On GerbilDto + Input; round-trips on GET/POST/PUT. Additive migration. - SaleAdAnimal gains Traits (German labels) + CharacterNote; prompt builder emits "Charakter: …" + "Charakter-Notiz: …" lines for the AI Verkaufstext. - Tests: traits/note round-trip (POST->GET via SQLite host), empty-default, prompt inclusion.
109 lines
4.9 KiB
C#
109 lines
4.9 KiB
C#
using GerbilManagerWebAPI.Common;
|
|
using GerbilManagerWebAPI.Dtos;
|
|
using GerbilManagerWebAPI.Models;
|
|
using Gridify;
|
|
using Microsoft.AspNetCore.Http.HttpResults;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
using Microsoft.EntityFrameworkCore;
|
|
|
|
namespace GerbilManagerWebAPI.Endpoints
|
|
{
|
|
public static class GerbilEndpoints
|
|
{
|
|
public static IEndpointRouteBuilder MapGerbilEndpoints(this IEndpointRouteBuilder app)
|
|
{
|
|
var group = app.MapGroup("/gerbils").WithTags("Gerbils");
|
|
|
|
// GET /gerbils (Gridify: filter/order/page; e.g. status==Active, litterId==…, orderBy=name)
|
|
group.MapGet("/", async ([AsParameters] GridifyParams query, ApplicationContext db) =>
|
|
TypedResults.Ok(await db.Gerbils.AsNoTracking()
|
|
.ToPagedResultAsync(query, ToDto)));
|
|
|
|
// GET /gerbils/breeders — distinct non-empty Herkunft values for the Tiere filter dropdown
|
|
group.MapGet("/breeders", async (ApplicationContext db) =>
|
|
TypedResults.Ok(await db.Gerbils.AsNoTracking()
|
|
.Where(g => g.OriginBreeder != null && g.OriginBreeder != "")
|
|
.Select(g => g.OriginBreeder!)
|
|
.Distinct().OrderBy(b => b).ToListAsync()));
|
|
|
|
// GET /gerbils/{id}
|
|
group.MapGet("/{id:guid}", async Task<Results<Ok<GerbilDto>, NotFound>> (Guid id, ApplicationContext db) =>
|
|
{
|
|
var g = await db.Gerbils.AsNoTracking().FirstOrDefaultAsync(x => x.Id == id);
|
|
return g is null ? TypedResults.NotFound() : TypedResults.Ok(ToDto(g));
|
|
});
|
|
|
|
// POST /gerbils
|
|
group.MapPost("/", async Task<Results<Created<GerbilDto>, ValidationProblem>> (GerbilInput input, ApplicationContext db) =>
|
|
{
|
|
if (string.IsNullOrWhiteSpace(input.Name))
|
|
return TypedResults.ValidationProblem(new Dictionary<string, string[]> { ["name"] = ["Name is required."] });
|
|
|
|
var g = new Gerbil { Id = Guid.NewGuid(), Name = input.Name };
|
|
Apply(g, input, isCreate: true);
|
|
db.Gerbils.Add(g);
|
|
await db.SaveChangesAsync();
|
|
return TypedResults.Created($"/gerbils/{g.Id}", ToDto(g));
|
|
});
|
|
|
|
// PUT /gerbils/{id}
|
|
group.MapPut("/{id:guid}", async Task<Results<NoContent, NotFound>> (Guid id, GerbilInput input, ApplicationContext db) =>
|
|
{
|
|
var g = await db.Gerbils.FirstOrDefaultAsync(x => x.Id == id);
|
|
if (g is null) return TypedResults.NotFound();
|
|
g.Name = input.Name;
|
|
Apply(g, input, isCreate: false);
|
|
await db.SaveChangesAsync();
|
|
return TypedResults.NoContent();
|
|
});
|
|
|
|
// DELETE /gerbils/{id} (409 if referenced as a litter parent)
|
|
group.MapDelete("/{id:guid}", async Task<Results<NoContent, NotFound, Conflict<string>>> (Guid id, ApplicationContext db) =>
|
|
{
|
|
var g = await db.Gerbils.FirstOrDefaultAsync(x => x.Id == id);
|
|
if (g is null) return TypedResults.NotFound();
|
|
db.Gerbils.Remove(g);
|
|
try
|
|
{
|
|
await db.SaveChangesAsync();
|
|
return TypedResults.NoContent();
|
|
}
|
|
catch (DbUpdateException)
|
|
{
|
|
return TypedResults.Conflict("Gerbil is referenced as a litter parent and cannot be deleted.");
|
|
}
|
|
});
|
|
|
|
return app;
|
|
}
|
|
|
|
private static void Apply(Gerbil g, GerbilInput i, bool isCreate)
|
|
{
|
|
g.Gender = i.Gender;
|
|
g.Status = i.Status ?? (isCreate ? GerbilStatus.Active : g.Status);
|
|
g.LitterId = i.LitterId;
|
|
g.OriginContactId = i.OriginContactId;
|
|
g.ReceiverContactId = i.ReceiverContactId;
|
|
g.EnclosureId = i.EnclosureId;
|
|
g.ColorVarietyId = i.ColorVarietyId;
|
|
g.DateOfBirth = i.DateOfBirth;
|
|
g.DateOfDeath = i.DateOfDeath;
|
|
g.CauseOfDeath = i.CauseOfDeath;
|
|
g.GoHomeDate = i.GoHomeDate;
|
|
g.Genotype = i.Genotype;
|
|
g.Notes = i.Notes;
|
|
g.ImportSource = i.ImportSource;
|
|
g.ExternalRef = i.ExternalRef;
|
|
g.OriginBreeder = i.OriginBreeder;
|
|
g.CharacterTraits = i.CharacterTraits ?? new List<string>();
|
|
g.CharacterNote = i.CharacterNote;
|
|
}
|
|
|
|
internal static GerbilDto ToDto(Gerbil g) => new(
|
|
g.Id, g.Name, g.Gender, g.Status, g.LitterId, g.OriginContactId, g.ReceiverContactId,
|
|
g.EnclosureId, g.ColorVarietyId, g.DateOfBirth, g.DateOfDeath, g.CauseOfDeath,
|
|
g.GoHomeDate, g.Genotype, g.Notes, g.ImportSource, g.ExternalRef, g.OriginBreeder,
|
|
g.CharacterTraits, g.CharacterNote);
|
|
}
|
|
}
|