using System.Text.Json;
using System.Text.Json.Serialization;
using OpenNest.Data;
namespace OpenNest.Server;
///
/// /api/settings: the settings every workstation shares (machines, post settings, clamps
/// and nest defaults), one JSON document per kind and key. Creating returns 201, or 200 when the
/// same document is already stored; changes and deletes follow the If-Match contract (428
/// missing, 400 malformed, 409 stale with the current document).
///
internal static class SettingRoutes
{
private static readonly JsonSerializerOptions WriteOptions = new(JsonSerializerDefaults.Web)
{
PropertyNameCaseInsensitive = true,
UnmappedMemberHandling = JsonUnmappedMemberHandling.Disallow,
};
public static void MapSettings(this IEndpointRouteBuilder app)
{
app.MapGet("/api/settings", (NestDatabase db) => Results.Ok(db.ListSharedSettings()));
app.MapGet("/api/settings/{kind}/{key}", (string kind, string key, NestDatabase db) =>
!SharedSettingKinds.IsKnown(kind) ? Results.BadRequest($"Unknown settings kind '{kind}'.") :
db.GetSharedSetting(kind, key) is { } record ? Results.Ok(record) : Results.NotFound());
app.MapPost("/api/settings/{kind}/{key}", async (string kind, string key, HttpRequest request, NestDatabase db) =>
{
var (write, error) = await ReadWrite(request, kind, key);
if (error is not null) return error;
try
{
var (record, created) = db.CreateSharedSetting(kind, key, write!);
return created ? Results.Created($"/api/settings/{kind}/{record.Key}", record) : Results.Ok(record);
}
catch (SharedSettingConflictException conflict) { return Conflict(conflict); }
});
app.MapPut("/api/settings/{kind}/{key}", async (string kind, string key, HttpRequest request, NestDatabase db) =>
{
var precondition = IfMatch.Read(request, out var expected);
if (precondition is not null) return precondition;
var (write, error) = await ReadWrite(request, kind, key);
if (error is not null) return error;
try
{
var updated = db.UpdateSharedSetting(kind, key, write!, expected);
return updated is null ? Results.NotFound() : Results.Ok(updated);
}
catch (SharedSettingConflictException conflict) { return Conflict(conflict); }
});
app.MapDelete("/api/settings/{kind}/{key}", (string kind, string key, string? savedBy, HttpRequest request, NestDatabase db) =>
{
var precondition = IfMatch.Read(request, out var expected);
if (precondition is not null) return precondition;
if (SharedSettingRules.NormalizeKey(kind, key) is null)
return Results.BadRequest($"Invalid {kind} settings key.");
if ((savedBy ?? "").Length > SharedSettingRules.MaxSavedByLength)
return Results.BadRequest("savedBy is too long.");
try
{
return db.DeleteSharedSetting(kind, key, savedBy ?? "", expected) ? Results.NoContent() : Results.NotFound();
}
catch (SharedSettingConflictException conflict) { return Conflict(conflict); }
});
}
private static IResult Conflict(SharedSettingConflictException conflict) =>
Results.Json(conflict.Current, statusCode: StatusCodes.Status409Conflict);
private static async Task<(SharedSettingWrite? Write, IResult? Error)> ReadWrite(HttpRequest request, string kind, string key)
{
var (json, error) = await DrawingSetRoutes.ReadJson(request, "settings document");
if (error is not null) return (null, error);
SharedSettingWrite? write;
try { write = JsonSerializer.Deserialize(json!, WriteOptions); }
catch (JsonException) { return (null, Results.BadRequest("Invalid settings JSON.")); }
return SharedSettingRules.GetError(kind, key, write) is { } invalid
? (null, Results.BadRequest(invalid))
: (write, null);
}
}