feat(timeline): add TimelineEventRequest input DTO
Flat input DTO with Bean Validation (@NotBlank/@NotNull/@Size). createdBy/ updatedBy deliberately absent (server-populated; CWE-639). version is an optional concurrency token, exempt from the server-only audit rule. Per #775. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -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}.
|
||||||
|
*
|
||||||
|
* <p><strong>{@code createdBy}/{@code updatedBy} are intentionally absent.</strong> 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 <em>update</em> only. This is a concurrency token, <strong>not</strong>
|
||||||
|
* 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<UUID> personIds,
|
||||||
|
@Size(max = 50) List<UUID> documentIds
|
||||||
|
) {}
|
||||||
Reference in New Issue
Block a user