Files
OpenNest/OpenNest.Data/DrawingSetRules.cs
T
ajandClaude Opus 5.5 be057bf337 feat(server): store versioned drawing sets
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>
2026-10-20 12:51:56 -04:00

52 lines
2.2 KiB
C#

namespace OpenNest.Data;
/// <summary>
/// Bounds shared by the drawing set client and server. The client checks them before
/// sending; the server refuses anything outside them with no state change. Whether each
/// member's drawing exists is checked by the server.
/// </summary>
public static class DrawingSetRules
{
public const int MaxMembers = 500;
public const int MaxQuantity = 100_000;
/// <summary>Largest accepted set JSON body (1 MiB): room for members echoed with their drawings.</summary>
public const int MaxBodyBytes = 1024 * 1024;
/// <summary>The first rule <paramref name="record"/> breaks, or null when it may be saved.</summary>
public static string? GetError(DrawingSetRecord? record)
{
if (record is null)
return "Missing set.";
if (string.IsNullOrWhiteSpace(record.Name))
return "name is required.";
var error = DrawingLibraryRules.TextError("name", record.Name, DrawingLibraryRules.MaxTextLength)
?? DrawingLibraryRules.TextError("customer", record.Customer, DrawingLibraryRules.MaxTextLength)
?? DrawingLibraryRules.TextError("savedBy", record.SavedBy, DrawingLibraryRules.MaxTextLength)
?? DrawingLibraryRules.TextError("notes", record.Notes, DrawingLibraryRules.MaxNotesLength);
if (error is not null)
return error;
if (record.Members is null)
return "members is required.";
if (record.Members.Count > MaxMembers)
return $"A set may have at most {MaxMembers} members.";
var seen = new HashSet<Guid>();
for (var i = 0; i < record.Members.Count; i++)
{
var member = record.Members[i];
if (member is null)
return $"members[{i}] is required.";
if (member.DrawingId == Guid.Empty)
return $"members[{i}].drawingId is required.";
if (member.Quantity < 1 || member.Quantity > MaxQuantity)
return $"members[{i}].quantity must be between 1 and {MaxQuantity}.";
if (!seen.Add(member.DrawingId))
return $"members[{i}] repeats drawing {member.DrawingId}: use one member with a larger quantity.";
}
return null;
}
}