Commit Graph

417 Commits

Author SHA1 Message Date
Marcel
e5d953dee8 test(config): rewrite ForwardHeadersConfigurationTest as context-less binder test
Drops @SpringBootTest + PostgresContainerConfig + @MockitoBean S3Client in
favour of Spring's Binder API against application.yaml. The new test binds
the property into the typed ServerProperties.ForwardHeadersStrategy enum,
so typos (`nativ`, `Native`, `framework `) and future enum renames fail
the build with BindException — addresses the silent-coercion concern that
the YAML-string assertion missed.

Verified the test goes red on a typo (BindException: Failed to convert
"nativ" → ForwardHeadersStrategy) and green on `native`.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-11 13:01:06 +02:00
Marcel
83f022ff4b feat(security): trust X-Forwarded-Proto behind reverse proxy
Adds server.forward-headers-strategy: native so that Jetty honours
X-Forwarded-{Proto,For,Host} from Caddy. Without this, getScheme(),
redirect URLs, and Spring Session "Secure" cookies reflect the
internal http hop instead of the original https client request.

Refs #497.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 21:33:39 +02:00
Marcel
915ad9f5c6 test(fts): add overflow guard and UUID-as-String regression tests
Some checks failed
CI / Unit & Component Tests (push) Failing after 4m35s
CI / OCR Service Tests (push) Successful in 39s
CI / Backend Unit Tests (push) Failing after 3m18s
- searchDocuments_relevance_returns_empty_when_offset_exceeds_maxInt:
  proves the long→int guard fires and findFtsPageRaw is never called
- searchDocuments_relevance_handles_string_uuid_from_jdbc_driver:
  exercises the toFtsPage String fallback branch for JDBC drivers that
  return UUID columns as String instead of java.util.UUID

Addresses Sara's review concerns on PR #488.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 16:35:01 +02:00
Marcel
143622bf27 refactor(fts): address PR #488 review concerns
- Extract isPureTextRelevance() private static method to replace the
  7-clause inline boolean in searchDocuments
- Guard long→int cast in relevanceSortedPageFromSql to prevent silent
  overflow at page ≥43M (CWE-190)
- resolvePersonName now uses the typed API client (createApiClient)
  instead of raw fetch, aligning with project conventions
- Update DocumentServiceTest stubs to match new FTS path (findFtsPageRaw
  + findAllById instead of findAllMatchingIdsByFts)
- Rewrite page.server.spec.ts person-name tests to mock via path-based
  API dispatch, matching the new api.GET call site

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 16:35:01 +02:00
Marcel
a3906976e8 test(fts): add integration tests and update unit tests for SQL-paginated relevance
- DocumentFtsPagedIntegrationTest: Testcontainers repo-level tests for
  findFtsPageRaw (page size, window total, last page, no matches, stopword)
- DocumentServiceSortTest: rewritten to stub findFtsPageRaw + findAllById
  for the pure-text RELEVANCE path; verifies filter-active path stays in-memory
- DocumentServiceTest: update two enrichment tests to use new SQL-path stubs

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 16:35:01 +02:00
Marcel
fea837b345 refactor(fts): add FtsHit/FtsPage records; rename findRankedIdsByFts -> findAllMatchingIdsByFts
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 16:35:01 +02:00
Marcel
7ca44d7df1 fix(db): add indexes on documents.sender_id and document_comments.author_id
Some checks failed
CI / Unit & Component Tests (push) Failing after 4m26s
CI / OCR Service Tests (push) Successful in 32s
CI / Backend Unit Tests (push) Failing after 3m16s
CI / Unit & Component Tests (pull_request) Failing after 4m33s
CI / OCR Service Tests (pull_request) Successful in 39s
CI / Backend Unit Tests (pull_request) Failing after 3m16s
Flyway V62 adds idx_documents_sender_id and idx_comments_author_id to speed up
FK-driven queries on the persons page and briefwechsel view. Closes #470.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 16:31:30 +02:00
Marcel
8b25a5b940 fix(user): replace Math.abs(hashCode()) with Math.floorMod in computeColor
Some checks failed
CI / Unit & Component Tests (push) Has been cancelled
CI / OCR Service Tests (push) Has been cancelled
CI / Backend Unit Tests (push) Has been cancelled
Math.abs(Integer.MIN_VALUE) overflows back to Integer.MIN_VALUE (negative),
making the old pattern unsafe for any palette size that doesn't evenly divide
MIN_VALUE. Math.floorMod always returns a non-negative residue in [0, n-1],
eliminating the overflow edge case entirely.

Fixes SpotBugs RV_ABSOLUTE_VALUE_OF_HASHCODE (priority 1, CORRECTNESS).
Closes #471

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 15:48:59 +02:00
Marcel
265b4f1484 fix(comment): declare missing @PathVariable params on block comment endpoints
Some checks failed
CI / Unit & Component Tests (push) Has been cancelled
CI / OCR Service Tests (push) Has been cancelled
CI / Backend Unit Tests (push) Has been cancelled
getBlockComments was missing documentId; replyToBlockComment was missing
blockId. Spring silently ignored undeclared path variables — the segments
were parsed but never bound. Now both parameters are explicitly declared so
Spring rejects non-UUID values with 400.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 15:45:48 +02:00
Marcel
bfc3a17676 test(migration): guard cleanup in try-finally to ensure isolation
Some checks failed
CI / Unit & Component Tests (pull_request) Failing after 3m57s
CI / OCR Service Tests (pull_request) Successful in 40s
CI / Backend Unit Tests (pull_request) Failing after 3m21s
CI / Unit & Component Tests (push) Failing after 4m1s
CI / OCR Service Tests (push) Successful in 38s
CI / Backend Unit Tests (push) Failing after 3m24s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 15:25:26 +02:00
Marcel
eb54a98ea2 fix(user): use builder in createGroup and guard against null permissions
Some checks failed
CI / Unit & Component Tests (push) Has been cancelled
CI / OCR Service Tests (push) Has been cancelled
CI / Backend Unit Tests (push) Has been cancelled
CI / Unit & Component Tests (pull_request) Failing after 4m2s
CI / OCR Service Tests (pull_request) Successful in 37s
CI / Backend Unit Tests (pull_request) Failing after 3m18s
Null dto.permissions now produces an empty HashSet instead of propagating null
into the @ElementCollection — prevents a silent NPE after V64 adds NOT NULL.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 15:19:20 +02:00
Marcel
3fcdfa85f1 fix(db): add PRIMARY KEY to group_permissions; promote tbmp UNIQUE to PK
V63 deduplicates any phantom (group_id, permission) rows accumulated since
the initial schema. V64 sets NOT NULL on permission and adds pk_group_permissions.
V65 renames uq_tbmp_block_person to pk_tbmp for naming-convention consistency.
Integration tests confirm each constraint via pg_catalog.pg_constraint. Closes #469 (partial).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 15:18:46 +02:00
Marcel
9b5547757a fix(security): cap PersonController size param at 50 to prevent resource exhaustion
Addresses @Nora review: ?sort=documentCount&size=999999 could trigger a
full-table query and large serialization. Cap enforced at controller boundary.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-08 15:56:47 +02:00
Marcel
92587b050e feat(stats): add totalStories to StatsDTO via GeschichteService.countPublished()
Addresses @Elicit review concern: stories stat tile was permanently showing
"—" because StatsDTO had no published-story count. Now wired end-to-end.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-08 15:56:47 +02:00
Marcel
5b645f6374 feat(person): add findTopByDocumentCount endpoint for reader dashboard
PersonController GET /api/persons?sort=documentCount&size=N returns the top N
persons by combined sender+receiver document count for the reader dashboard.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-08 15:56:47 +02:00
Marcel
d76ee5fa31 fix(security): restrict DRAFT list to author — prevent cross-user draft leak
GeschichteService.list() now applies hasAuthor(currentUser()) whenever
status == DRAFT, so BLOG_WRITE users cannot read other users' unpublished stories.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-08 15:56:47 +02:00
Marcel
5146aeb568 feat(document): add DocumentSort.UPDATED_AT for reader dashboard feed
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-08 15:56:47 +02:00
Marcel
86de118d63 refactor(documents): bundle density filters into a record (#385)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-08 11:42:38 +02:00
Marcel
c0a1f04df5 chore(documents): density endpoint produces=application/json (#385)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-08 11:38:29 +02:00
Marcel
c9be6cc165 test(documents): @Transactional rollback in DocumentDensityIntegrationTest (#385)
Replaces @DirtiesContext(AFTER_EACH_TEST_METHOD), which restarted
the full Spring context per test (≈10–15s × 7), with @Transactional
rollback. Each test still sees a clean slate via the spring-test
default rollback, but the context is shared across the class.

Wall time for this class dropped from 35s to 17.87s in local runs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-08 10:53:48 +02:00
Marcel
e92e9e452e feat(documents): make density endpoint filter-reactive (#385)
Density bars now recompute when other filters change so the chart always
matches the list it sits above. Selectable filters: q, senderId, receiverId,
tag (multi), tagQ, status, tagOp. Date bounds (from/to) are deliberately
omitted — the chart is the surface for picking those, so it must always
span the broader space the user is selecting within.

Architectural shift: drop the native SQL GROUP BY in favour of in-memory
grouping over the existing Specification-driven findAll. This composes for
free with all the search predicates (FTS-rank-then-filter, sender/receiver,
tag-with-descendants, tagQ partial match, status, tagOp) and keeps the
density implementation a thin layer on top of searchDocuments. At the
current archive size (~5k docs) this stays well under the p95 200ms target;
Cache-Control: max-age=300 absorbs repeated browse loads.

- Removes findDensityByMonth, findMinMaxDocumentDate, DocumentDateRangeProjection.
- Replaces DocumentService.getDensity(LocalDate, LocalDate) with the
  filter-aware overload.
- Endpoint accepts the same query params as /api/documents/search minus
  paging+sort+from+to.
- DocumentDensityIntegrationTest rewritten as @SpringBootTest covering
  no-filter / sender / tag / status / sender+tag combos via real PostgreSQL.
- DocumentServiceTest unit tests updated to the new signature.
- DocumentControllerTest tests forwarding of senderId+tag+tagOp and q+status.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-07 23:06:47 +02:00
Marcel
1060be7def feat(documents): add GET /api/documents/density endpoint (#385)
Authenticated read endpoint backing the timeline density widget. Optional
from/to LocalDate query params narrow the aggregation. Response carries
Cache-Control: private, max-age=300 so repeated browse sessions skip the
aggregation query (per Tobias' devops review). No @RequirePermission needed —
inherits the global anyRequest().authenticated() rule.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-07 21:52:36 +02:00
Marcel
fbf4725e97 feat(documents): add DocumentService.getDensity (#385)
Maps the repository's Object[] rows into a DocumentDensityResult and pairs
them with the archive-wide min/max meta_date range. Read-only, no
@Transactional needed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-07 21:50:05 +02:00
Marcel
c90b42d045 feat(documents): add density and date-range repository queries (#385)
Native SQL aggregations backing GET /api/documents/density:
- findDensityByMonth groups documents by truncated meta_date with optional
  from/to bounds (frontend fills zero-count gaps).
- findMinMaxDocumentDate returns the earliest/latest meta_date via projection,
  null on empty archive.

Covered by DocumentDensityIntegrationTest (Testcontainers PostgreSQL): empty
archive, single+multi-month grouping, from/to bounds, null meta_date exclusion,
min/max edge cases.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-07 21:47:59 +02:00
Marcel
708fd9d63e refactor(comment): promote CommentData to top-level record in comment package
Some checks failed
CI / Unit & Component Tests (push) Failing after 3m32s
CI / OCR Service Tests (push) Successful in 47s
CI / Backend Unit Tests (push) Has been cancelled
CI / Unit & Component Tests (pull_request) Failing after 3m32s
CI / OCR Service Tests (pull_request) Successful in 38s
CI / Backend Unit Tests (pull_request) Failing after 3m25s
Moves the nested `CommentData` record out of `CommentService` into its own
`document/comment/CommentData.java` file, removing the cross-domain coupling
where `DashboardService` depended on an inner type of `CommentService`.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-07 19:47:27 +02:00
Marcel
abe8ab8668 refactor(comment): remove dead findAnnotationIdsByIds; fix aria-label i18n; rename misleading test
Some checks failed
CI / Unit & Component Tests (pull_request) Failing after 3m36s
CI / OCR Service Tests (pull_request) Successful in 35s
CI / Backend Unit Tests (pull_request) Failing after 3m24s
CI / Unit & Component Tests (push) Failing after 3m30s
CI / OCR Service Tests (push) Successful in 38s
CI / Backend Unit Tests (push) Failing after 3m22s
- Remove `findAnnotationIdsByIds` from CommentService — no production caller exists now
  that DashboardService uses `findDataByIds` directly; along with its test coverage
- Fix aria-label construction in ChronikRow: pass actorName to i18n message function
  instead of manually prepending the actor, so all locales render correctly
- Rename `findDataByIds_does_not_truncate_at_exactly_120_chars` →
  `findDataByIds_preserves_content_at_exactly_120_chars` for accurate description

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-07 19:05:46 +02:00
Marcel
e877847b7e feat(dashboard): add commentPreview to ActivityFeedItemDTO; wire via findDataByIds()
ActivityFeedItemDTO gains a nullable commentPreview field (plain-text, 120 chars max).
DashboardService.getActivity() now calls findDataByIds() once instead of
findAnnotationIdsByIds(), halving DB round-trips for the Chronik page load.
Empty-string previews are normalised to null so the frontend can use ?? cleanly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-07 17:55:02 +02:00
Marcel
7c25d08506 feat(comment): add findDataByIds() — batch-fetch annotationId + plain-text preview in one query
Replaces the single-purpose findAnnotationIdsByIds() (kept as delegation shim).
Introduces CommentData record (annotationId + preview) and stripAndTruncate()
using Jsoup.parse().text() for DOM-safe HTML stripping. Truncates to 120 chars.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-07 17:49:40 +02:00
Marcel
0981355247 test(archunit): add Rule 2 coverage for importing and audit domains
Some checks failed
CI / Unit & Component Tests (push) Has been cancelled
CI / Backend Unit Tests (push) Has been cancelled
CI / OCR Service Tests (push) Successful in 36s
CI / Unit & Component Tests (pull_request) Failing after 3m33s
CI / OCR Service Tests (pull_request) Successful in 36s
CI / Backend Unit Tests (pull_request) Failing after 3m24s
MassImportService delegates to other domain services (no direct repo
access), and AuditService only touches its own AuditLogRepository —
both pass the boundary rule cleanly. Closes the known hole flagged
by Sara and Markus in PR #428.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-05 17:59:08 +02:00
Marcel
0dd58556a7 test(archunit): fix foreignJpaRepositoryFor exact-segment matching
Replace substring contains() with a regex exact-segment match so a
domain whose name is a substring of another (e.g. "tag" in "tagging")
cannot silently escape the predicate and produce a false negative.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-05 17:57:47 +02:00
Marcel
22ec808b2d test(backend): add ArchUnit domain boundary enforcement (Rules 1–4)
Some checks failed
CI / Unit & Component Tests (push) Failing after 3m28s
CI / OCR Service Tests (push) Successful in 35s
CI / Backend Unit Tests (push) Failing after 3m17s
CI / Unit & Component Tests (pull_request) Failing after 3m25s
CI / OCR Service Tests (pull_request) Successful in 30s
CI / Backend Unit Tests (pull_request) Failing after 3m19s
Rules enforced:
- Rule 1: no @RestController may inject a JpaRepository directly (preserves @RequirePermission AOP enforcement)
- Rule 2: @Service classes access only their own domain's repositories, never a foreign domain's
- Rule 3: no @Configuration class (except @SpringBootApplication) in domain packages
- Rule 4: all @Entity classes reside in a domain package

Rule 5 (URL prefix per controller domain) deferred — tracked in #427.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-05 17:13:41 +02:00
Marcel
548df84219 test(annotation): wire TranscriptionBlockRepository mock and add cascade test
Some checks failed
CI / Unit & Component Tests (pull_request) Failing after 3m21s
CI / OCR Service Tests (pull_request) Successful in 32s
CI / Backend Unit Tests (pull_request) Failing after 3m20s
CI / Unit & Component Tests (push) Failing after 3m6s
CI / OCR Service Tests (push) Successful in 33s
CI / Backend Unit Tests (push) Failing after 3m9s
AnnotationService was changed to call transcriptionBlockRepository
directly, but the test still mocked TranscriptionService — causing a
NPE and leaving the cascade path uncovered.

Replace the @Mock TranscriptionService with @Mock
TranscriptionBlockRepository, update the two existing delete-test
verifications, and add a dedicated
deleteAnnotation_cascadesToTranscriptionBlocks test.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-05 16:25:41 +02:00
Marcel
3db5b48cda test(document): remove dead updateThumbnailMetadata test
ThumbnailService now calls documentRepository.save() directly.
DocumentService.updateThumbnailMetadata() has no production callers,
so its test describes behaviour that no longer exists in the
production path.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-05 16:22:58 +02:00
Marcel
16dacd8f4c fix(test): update ThumbnailAsyncRunnerTest to use DocumentRepository
ThumbnailAsyncRunner was changed to inject DocumentRepository directly
(breaking the DocumentService cycle), but the test still passed
DocumentService to the constructor — a type mismatch that prevented
the test suite from compiling.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-05 16:22:23 +02:00
Marcel
fbbe0789d0 fix(document): break DocumentService ↔ ThumbnailAsyncRunner ↔ ThumbnailService cycle
Some checks failed
CI / Unit & Component Tests (push) Failing after 3m29s
CI / OCR Service Tests (push) Successful in 38s
CI / Backend Unit Tests (push) Failing after 1m54s
CI / Unit & Component Tests (pull_request) Failing after 28s
CI / OCR Service Tests (pull_request) Successful in 36s
CI / Backend Unit Tests (pull_request) Failing after 1m49s
Spring Framework 7 prohibits constructor injection cycles even with @Lazy.
Replace DocumentService dependencies in ThumbnailAsyncRunner and ThumbnailService
with direct DocumentRepository calls — both are intra-domain reads/saves.
Update ThumbnailServiceTest to mock DocumentRepository accordingly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-05 15:56:05 +02:00
Marcel
27e7fa9170 refactor(cleanup): delete empty legacy packages, move remaining test files to domain packages
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-05 12:59:02 +02:00
Marcel
5e53a261fc refactor(shared): move remaining services to domain packages (stats→dashboard, filestorage, importing, notification, exception)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-05 12:55:51 +02:00
Marcel
930b1d23ce refactor(security): move SecurityConfig to security/ package
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-05 12:48:29 +02:00
Marcel
af2c983fe2 refactor(user): move user domain to user/ package, rename DataInitializer to UserDataInitializer
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-05 12:45:30 +02:00
Marcel
e85057bed2 refactor(document): move document domain core to document/ package
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-05 12:39:20 +02:00
Marcel
bb7d872a61 refactor(document): move document sub-packages transcription/annotation/comment
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-05 12:23:28 +02:00
Marcel
c0a1c9ff5f refactor(ocr): move ocr domain to package org.raddatz.familienarchiv.ocr
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-05 11:41:19 +02:00
Marcel
b41e1335d2 refactor(person/relationship): move relationship sub-package under person domain
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-05 11:35:09 +02:00
Marcel
b466dfcec6 refactor(person): move person domain to package org.raddatz.familienarchiv.person
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-05 11:32:06 +02:00
Marcel
a39fd9928c refactor(notification): move notification domain to package org.raddatz.familienarchiv.notification
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-05 11:24:20 +02:00
Marcel
0ad3f3e58d refactor(geschichte): move geschichte domain to package org.raddatz.familienarchiv.geschichte
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-05 11:19:56 +02:00
Marcel
3643fa357c refactor(tag): move tag domain to package org.raddatz.familienarchiv.tag
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-05 11:13:55 +02:00
Marcel
89e9a2452e refactor(test): remove issue reference from makeService javadoc
Some checks failed
CI / Unit & Component Tests (pull_request) Failing after 3m44s
CI / OCR Service Tests (pull_request) Successful in 41s
CI / Backend Unit Tests (pull_request) Failing after 3m16s
CI / Unit & Component Tests (push) Failing after 4m5s
CI / OCR Service Tests (push) Successful in 57s
CI / Backend Unit Tests (push) Failing after 3m12s
Issue numbers in code comments rot as the codebase evolves. The why
(keeping real-database fidelity without pulling full service trees in)
is what matters, not the fix number.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-05 10:37:06 +02:00
Marcel
2506523f3b refactor(transcription/annotation): break mutual repo dependency
Some checks failed
CI / Unit & Component Tests (push) Failing after 4m2s
CI / OCR Service Tests (push) Successful in 42s
CI / Backend Unit Tests (push) Failing after 3m17s
CI / Unit & Component Tests (pull_request) Failing after 3m49s
CI / OCR Service Tests (pull_request) Successful in 39s
CI / Backend Unit Tests (pull_request) Failing after 3m17s
TranscriptionService injected AnnotationRepository; AnnotationService injected
TranscriptionBlockRepository. Each side now talks through the other domain's
service:

- TranscriptionService.deleteByAnnotationId — new write delegation; called
  from AnnotationService.deleteAnnotation in place of the foreign repo.
- AnnotationService.deleteById / deleteAllById — new write delegations; called
  from TranscriptionService for cascading annotation cleanup.
- AnnotationService.findById (added in #417 commit 6) replaces the read.
- @Lazy on AnnotationService's TranscriptionService field breaks the
  resulting two-bean cycle at construction time, mirroring the existing
  @Lazy self-reference pattern in SenderModelService.

Refs #417 (C6.2 violations #10 and #11).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-05 07:48:26 +02:00
Marcel
f5151f3949 refactor(ocr-training): route SenderModelService and OcrTrainingService through TranscriptionBlockQueryService
Both services injected TranscriptionBlockRepository directly to read block
counts. They now go through TranscriptionBlockQueryService (count() and
countManualKurrentBlocksByPerson() added as 1-line delegations) — chosen over
TranscriptionService to avoid the existing
SenderModelService → TrainingDataExportService → TranscriptionBlockQueryService
chain reaching back into TranscriptionService and creating a cycle.

Refs #417 (C6.2 violations #8 and #9).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-05 07:40:34 +02:00