Timeline: TimelineEvent CRUD API (#775) #822
Reference in New Issue
Block a user
Delete Branch "feat/issue-775-timeline-crud-api"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes #775.
Curator CRUD for
TimelineEvent, layeredController → Service → Repositoryon the entity merged by #774. Backend-only except the regenerated API types + i18n. TDD throughout; 62 targeted tests green.Endpoints
POST /api/timeline/events→ 201 +TimelineEventView·@RequirePermission(WRITE_ALL)PUT /api/timeline/events/{id}→ 200 + updated view ·@RequirePermission(WRITE_ALL)DELETE /api/timeline/events/{id}→ 204 ·@RequirePermission(WRITE_ALL)GET /api/timeline/events/{id}→ 200 + view · global-auth READ baseline (no annotation, documented)Highlights
TimelineEventView+PersonView+ timeline-localDocumentRef(R7), assembled in-tx after flush; explicit allow-list (nonotes/provisionalleak).createdBy+updatedByon create, onlyupdatedByon update (preserves creator);createdBy/updatedByabsent from the DTO (CWE-639).PersonService.getAllById+ distinct-size check, documents via per-idgetDocumentByIdloop.TIMELINE_EVENT_CONFLICT; centralizedGlobalExceptionHandlerbackstop → generic 409CONFLICT, no Sentry, class-name-only logging (CWE-209).⚠️ Deviation from the issue body (intentional, maintainer-approved)
The integration test #775 mandated to "prove the lock engages end-to-end" caught that the spec's prescribed mechanism —
event.setVersion(clientVersion)thensaveAndFlushon a managed entity — does not engage Hibernate's optimistic lock (Hibernate ignores a manually-set@Versionon a managed entity and uses its own loaded-version snapshot for theWHERE version=?clause), so a stale write silently succeeds.Replaced with
requireVersionMatch: an explicit compare of the client's last-seen token against the freshly-loaded version (the true semantics of the Q1 client-supplied-token decision). The native@Versionincrement still fires on every save, andsaveAndFlush+catch remains the backstop for genuinely concurrent flushes. Null token ⇒ last-write-wins. Conflict codes unchanged. The issue body still describes the originalsetVersionwording per maintainer direction — the code is the corrected source of truth here.Tests (62 green)
TimelineEventServiceTest(23) ·TimelineEventControllerTest(19) ·TimelineEventServiceIntegrationTest(2, Testcontainers — view-assembly LazyInit guard + no-leak JSON + real 409) ·GlobalExceptionHandlerTest(5) ·ArchitectureTest(13)../mvnw clean package -DskipTests✅ · frontend lint ✅ · regeneratedfrontend/src/lib/generated/api.tscommitted (no CI drift guard).Docs
R9 ErrorCode catalog sync in
CLAUDE.md+docs/ARCHITECTURE.md; frontenderrors.ts+ de/en/es i18n. Diagrams/GLOSSARY/ADR-040 were done by #774.🤖 Generated with Claude Code
errors.ts ErrorCode union + getErrorMessage() cases for the four new codes, with de/en/es i18n keys. Conflict messages are calm/recoverable ('...wurde zwischenzeitlich geändert. Bitte neu laden.'). Per #775. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>