diff --git a/ordinis-rest-api/src/main/java/cloud/nstart/terravault/ordinis/restapi/service/DictionaryDefinitionService.java b/ordinis-rest-api/src/main/java/cloud/nstart/terravault/ordinis/restapi/service/DictionaryDefinitionService.java index f38b092..fc4f2de 100644 --- a/ordinis-rest-api/src/main/java/cloud/nstart/terravault/ordinis/restapi/service/DictionaryDefinitionService.java +++ b/ordinis-rest-api/src/main/java/cloud/nstart/terravault/ordinis/restapi/service/DictionaryDefinitionService.java @@ -45,16 +45,29 @@ public class DictionaryDefinitionService { @Transactional(readOnly = true) public List findAll() { - // findAllActive — excludes soft-deleted (migration 0031). findAllIncludingDeleted - // для admin Корзина page (см. findAllDeleted() ниже). - return repository.findAllActive(); + // Defense-in-depth: explicit Java filter поверх JPQL `WHERE deleted_at IS NULL`. + // Spring Data может interpretировать @Query inconsistently если derived query + // matches method name — explicit .filter() guarantees no soft-deleted leak + // в list endpoint независимо от Repository implementation details. + return repository.findAllActive().stream() + .filter(d -> !d.isDeleted()) + .toList(); } @Transactional(readOnly = true) public DictionaryDefinition findByName(String name) { - return repository.findByName(name) + // Use derived findByName (Repository @Query filters deleted_at IS NULL). + // Если что-то проскочило (Spring Data quirk) — explicit isDeleted() check + // ниже. Cascade на ChangelogServiceTest / SchemaPatchServiceTest mocks: + // тесты stub`ают этот метод; ломать API сигнатуру не надо. + DictionaryDefinition d = repository.findByName(name) .orElseThrow(() -> OrdinisException.notFound( "dictionary_not_found", "Dictionary not found: " + name)); + if (d.isDeleted()) { + throw OrdinisException.notFound( + "dictionary_not_found", "Dictionary not found: " + name); + } + return d; } @Transactional(readOnly = true)