mirror of
https://github.com/ajisaacs/OpenNest.git
synced 2026-10-27 15:01:07 -04:00
A drawing set is a named list of library drawings with quantities (a kit,
an assembly, a recurring order) that will later be released into one nest
per material. This is the server half: sets get the same revision,
If-Match and archive contract as /api/drawings so the client and tests
follow a known shape.
Schema v7 adds drawing_sets, drawing_set_revisions and
drawing_set_members. Members reference drawings by id and mean each
drawing's current revision, so reads join the current drawing record:
the client can group members by material without one request per drawing,
and the joined record is ignored on save so a read record can be sent
back unchanged. Every change keeps its own member list, and
/api/drawings/{id}/sets answers "used in" from current revisions only.
The drawing query's enum, integer and history-paging parsing is now shared
so both APIs refuse bad input identically.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
49 lines
1.9 KiB
C#
49 lines
1.9 KiB
C#
namespace OpenNest.Data;
|
|
|
|
/// <summary>
|
|
/// One bounded page request for browsing drawing sets (<c>GET /api/sets/query</c>).
|
|
/// The server filters and pages in SQL; the client and server share these bounds.
|
|
/// </summary>
|
|
public sealed class DrawingSetQuery
|
|
{
|
|
public const int DefaultLimit = 100;
|
|
public const int MaxLimit = 500;
|
|
public const int MaxSearchLength = 200;
|
|
|
|
/// <summary>
|
|
/// Case-insensitive (ASCII) substring of name, customer or notes. Blank means no filter.
|
|
/// </summary>
|
|
public string Search { get; init; } = "";
|
|
|
|
/// <summary>Exact customer, compared by <see cref="SharedListNames.Key"/>. Blank means any.</summary>
|
|
public string Customer { get; init; } = "";
|
|
|
|
public bool IncludeArchived { get; init; }
|
|
|
|
/// <summary>Column to order by; ties are broken by id in the same direction.</summary>
|
|
public DrawingSetSortField Sort { get; init; } = DrawingSetSortField.SavedAt;
|
|
|
|
/// <summary>Largest/newest/Z first when true (the default, newest saved first).</summary>
|
|
public bool Descending { get; init; } = true;
|
|
|
|
public int Offset { get; init; }
|
|
|
|
public int Limit { get; init; } = DefaultLimit;
|
|
|
|
/// <summary>The search text as matched: trimmed, never null.</summary>
|
|
public string NormalizedSearch => (Search ?? "").Trim();
|
|
|
|
/// <summary>The first violated bound, or null when the query may be sent.</summary>
|
|
public string? GetValidationError()
|
|
{
|
|
if (!Enum.IsDefined(Sort))
|
|
return "sort must be a supported column.";
|
|
if (Offset < 0)
|
|
return "offset must be zero or greater.";
|
|
if (Limit < 1 || Limit > MaxLimit)
|
|
return $"limit must be between 1 and {MaxLimit}.";
|
|
return DrawingLibraryRules.TextError("search", NormalizedSearch, MaxSearchLength)
|
|
?? DrawingLibraryRules.TextError("customer", Customer ?? "", DrawingLibraryRules.MaxTextLength);
|
|
}
|
|
}
|