diff --git a/backend/src/main/java/org/raddatz/familienarchiv/timeline/TimelineEventRequest.java b/backend/src/main/java/org/raddatz/familienarchiv/timeline/TimelineEventRequest.java new file mode 100644 index 00000000..dd25b3e9 --- /dev/null +++ b/backend/src/main/java/org/raddatz/familienarchiv/timeline/TimelineEventRequest.java @@ -0,0 +1,40 @@ +package org.raddatz.familienarchiv.timeline; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; + +import org.raddatz.familienarchiv.document.DatePrecision; + +import java.time.LocalDate; +import java.util.List; +import java.util.UUID; + +/** + * Flat input DTO for creating/updating a {@link TimelineEvent}. Bean Validation fires at the + * controller boundary (via {@code @Valid}) and produces a 400 {@code VALIDATION_ERROR} for the + * presence/size constraints below; cross-field rules (the RANGE invariant), date normalization, + * id dedupe, and the title-length structured-error guard live in {@code TimelineEventService}. + * + *

{@code createdBy}/{@code updatedBy} are intentionally absent. Authorship is + * server-populated from the session principal only — accepting it from the body would be an + * authorship-forgery / mass-assignment vector (CWE-639; see ADR-040 §7). + * + * @param version optional optimistic-lock concurrency token (the {@code @Version} the client last + * saw), applied on update only. This is a concurrency token, not + * an authorship field, so it is deliberately exempt from the §7 server-only audit rule. + * Null on update means "no concurrency check" (last-write-wins). No range validation — + * a stale/negative value is simply a mismatch the lock rejects at flush; the lock, not + * a validator, is the control. + */ +public record TimelineEventRequest( + @NotBlank @Size(max = 255) String title, + @NotNull EventType type, + @NotNull LocalDate eventDate, + DatePrecision precision, + LocalDate eventDateEnd, + @Size(max = 5000) String description, + Long version, + @Size(max = 50) List personIds, + @Size(max = 50) List documentIds +) {}