feat(timeline): add TimelineEventView response views

TimelineEventView + nested PersonView + timeline-local DocumentRef. Explicit
field allow-list, never the raw entity (lazy-collection 500 + curator-field
leak). DocumentRef stays timeline-local by design (#775 R7). Per ADR-040 §2.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Marcel
2026-06-13 10:46:06 +02:00
committed by marcel
parent 0eea19c0d4
commit b7a5cd7b53

View File

@@ -0,0 +1,59 @@
package org.raddatz.familienarchiv.timeline;
import io.swagger.v3.oas.annotations.media.Schema;
import org.raddatz.familienarchiv.document.DatePrecision;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
import java.util.UUID;
/**
* Response view for the timeline event endpoints — returned by GET, POST, and PUT alike.
* Assembled inside the service transaction (after {@code saveAndFlush} on the write paths, so
* {@code version} is non-null) from the managed entity's already-loaded collections. The raw
* {@link TimelineEvent} is never serialized: its LAZY {@code persons}/{@code documents}
* collections under {@code open-in-view: false} would otherwise 500 (ADR-036/ADR-040 §2), and
* splatting the entities would leak curator-internal fields ({@code Person.notes},
* {@code provisional}, transcription data) to every READ_ALL reader. The explicit field
* allow-list below is that guarantee.
*/
public record TimelineEventView(
@Schema(requiredMode = Schema.RequiredMode.REQUIRED) UUID id,
@Schema(requiredMode = Schema.RequiredMode.REQUIRED) String title,
@Schema(requiredMode = Schema.RequiredMode.REQUIRED) EventType type,
@Schema(requiredMode = Schema.RequiredMode.REQUIRED) LocalDate eventDate,
@Schema(requiredMode = Schema.RequiredMode.REQUIRED) DatePrecision precision,
LocalDate eventDateEnd,
String description,
@Schema(requiredMode = Schema.RequiredMode.REQUIRED) Long version,
@Schema(requiredMode = Schema.RequiredMode.REQUIRED) UUID createdBy,
@Schema(requiredMode = Schema.RequiredMode.REQUIRED) LocalDateTime createdAt,
@Schema(requiredMode = Schema.RequiredMode.REQUIRED) UUID updatedBy,
@Schema(requiredMode = Schema.RequiredMode.REQUIRED) LocalDateTime updatedAt,
@Schema(requiredMode = Schema.RequiredMode.REQUIRED) List<PersonView> persons,
@Schema(requiredMode = Schema.RequiredMode.REQUIRED) List<DocumentRef> documents
) {
/** Summarised person — exposes only id, firstName, and lastName. Mirrors GeschichteView.PersonView. */
public record PersonView(
@Schema(requiredMode = Schema.RequiredMode.REQUIRED) UUID id,
String firstName,
String lastName
) {}
/**
* Summarised linked document — id, title, and the eager {@code documentDate} only (no lazy
* sender/receiver hop, no person-name leak through the document side).
*
* <p>timeline-local by design; do not promote to {@code document/} — see #775 R7. Reusing
* {@code geschichte.journeyitem.DocumentSummary} would force a cross-domain import of a
* package-private mapper plus duplicated name-assembly logic; a 3-field local record is the
* lower-coupling choice.
*/
public record DocumentRef(
@Schema(requiredMode = Schema.RequiredMode.REQUIRED) UUID id,
@Schema(requiredMode = Schema.RequiredMode.REQUIRED) String title,
LocalDate documentDate
) {}
}