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:
@@ -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
|
||||||
|
) {}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user