Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
af52052
Bump org.springframework.boot from 3.5.8 to 4.0.0 in /backend
dependabot[bot] Dec 12, 2025
5ae2f79
Bump org.springframework.cloud:spring-cloud-starter-kubernetes-client…
dependabot[bot] Dec 11, 2025
f152bbe
Bump org.hibernate:hibernate-jpamodelgen in /backend
dependabot[bot] Dec 5, 2025
59d18bd
Fix flyway
malte-laukoetter Dec 12, 2025
476b4c6
Replace PostgreSQLEnumJdbcType with NAMED_ENUM
malte-laukoetter Dec 12, 2025
9230099
Rename HttpHeaders#containsKey to containsHeader
malte-laukoetter Nov 21, 2025
b30392c
Replace hibernate NullPrecedence with jpa Nulls
malte-laukoetter Nov 21, 2025
0bdcdc5
Adjust spring module names
malte-laukoetter Nov 21, 2025
a13c043
Fix test dependencies
malte-laukoetter Nov 21, 2025
3c67ff3
Add missing MockitoExtension
malte-laukoetter Nov 21, 2025
de1a881
Update to jackson 3
malte-laukoetter Nov 21, 2025
529ee53
Parse JsonPatch using jackson 2
malte-laukoetter Dec 15, 2025
ab881e0
Fix deserialization of null values for booleans
malte-laukoetter Dec 16, 2025
f5a40a6
Fix criteria for creatingDocumentationOffice
malte-laukoetter Dec 15, 2025
5086ceb
Fix use of redis in health check
malte-laukoetter Dec 15, 2025
bf02447
Fix searching linkable documentation units
malte-laukoetter Dec 18, 2025
ef8a830
Use JPA Metamodel API instead of strings
malte-laukoetter Dec 18, 2025
f480613
Update neuris-juris-xml-export
malte-laukoetter Dec 18, 2025
63ec2c4
Add deprecation Javadoc comments
malte-laukoetter Dec 18, 2025
5f8a55c
Remove unneeded throws statements
malte-laukoetter Dec 18, 2025
157bc2b
Replace deprecated JacksonXmlRootElement
malte-laukoetter Dec 18, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 21 additions & 10 deletions backend/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import java.io.Serializable
plugins {
java
jacoco
id("org.springframework.boot") version "3.5.8"
id("org.springframework.boot") version "4.0.0"
id("io.spring.dependency-management") version "1.1.7"
id("com.diffplug.spotless") version "8.1.0"
id("org.sonarqube") version "7.2.1.6560"
Expand Down Expand Up @@ -153,20 +153,21 @@ sonar {
dependencies {
val testContainersVersion = "2.0.2"

implementation("org.springframework.boot:spring-boot-starter-web")
implementation("org.springframework.boot:spring-boot-starter-webmvc")
implementation("org.springframework.boot:spring-boot-starter-actuator")
implementation("org.springframework.boot:spring-boot-starter-data-redis")
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
implementation("org.springframework.boot:spring-boot-starter-validation")
implementation("org.springframework.boot:spring-boot-autoconfigure")
implementation("org.springframework.session:spring-session-data-redis")
implementation("org.springframework.boot:spring-boot-starter-security")
implementation("org.springframework.boot:spring-boot-starter-oauth2-client") {
implementation("org.springframework.boot:spring-boot-starter-security-oauth2-client") {
exclude(group = "net.minidev", module = "json-smart")
}
implementation("org.springframework.security:spring-security-oauth2-resource-server:7.0.0")
implementation("org.springframework.boot:spring-boot-starter-flyway")

implementation("org.springframework.cloud:spring-cloud-starter-kubernetes-client-config:3.3.0")
implementation("org.springframework.cloud:spring-cloud-starter-kubernetes-client-config:5.0.0")

// CVE-2022-3171
implementation("com.google.protobuf:protobuf-java:4.33.2")
Expand Down Expand Up @@ -197,22 +198,23 @@ dependencies {
implementation("com.icegreen:greenmail:2.1.7")

// package served by private repo, requires authentication:
implementation("de.bund.digitalservice:neuris-juris-xml-export:0.10.45") {
implementation("de.bund.digitalservice:neuris-juris-xml-export:0.10.46") {
exclude(group = "org.slf4j", module = "slf4j-simple")
}
// for local development:
// implementation(files("../../neuris-juris-xml-export/build/libs/neuris-juris-xml-export-0.10.45.jar"))
// implementation(files("../../neuris-juris-xml-export/build/libs/neuris-juris-xml-export-0.10.46.jar"))
// or with local gradle project (look also into settings.gradle.kts)
// implementation(project(":exporter"))

implementation("de.bund.digitalservice:neuris-caselaw-migration-schema:0.0.118")
// for local development:
// implementation(files("../../ris-data-migration/schema/build/libs/schema-0.0.118.jar"))

implementation("com.fasterxml.jackson.core:jackson-core:2.20.1")
implementation("com.fasterxml.jackson.core:jackson-databind:2.20.1")
implementation("tools.jackson.core:jackson-core:3.0.2")
implementation("tools.jackson.core:jackson-databind:3.0.2")
implementation("com.fasterxml.jackson.core:jackson-annotations:2.20")
implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.20.1")
implementation("tools.jackson.dataformat:jackson-dataformat-xml:3.0.2")
implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.20.1")

implementation("com.gravity9:json-patch-path:2.0.2")

Expand All @@ -239,6 +241,15 @@ dependencies {
testImplementation("org.springframework.boot:spring-boot-starter-test") {
exclude(group = "org.mockito", module = "mockito-core")
}

testImplementation("org.springframework.boot:spring-boot-starter-webmvc-test")
testImplementation("org.springframework.boot:spring-boot-starter-actuator-test")
testImplementation("org.springframework.boot:spring-boot-starter-data-redis-test")
testImplementation("org.springframework.boot:spring-boot-starter-data-jpa-test")
testImplementation("org.springframework.boot:spring-boot-starter-validation-test")
testImplementation("org.springframework.boot:spring-boot-starter-security-test")
testImplementation("org.springframework.boot:spring-boot-starter-security-oauth2-client-test")
testImplementation("org.springframework.boot:spring-boot-starter-flyway-test")
testImplementation("org.mockito:mockito-junit-jupiter:5.21.0")
testImplementation("org.mockito:mockito-inline:5.2.0")

Expand All @@ -251,7 +262,7 @@ dependencies {

compileOnly("org.projectlombok:lombok")
annotationProcessor("org.projectlombok:lombok")
annotationProcessor("org.hibernate:hibernate-jpamodelgen:6.6.36.Final")
annotationProcessor("org.hibernate:hibernate-jpamodelgen:7.1.11.Final")

developmentOnly("org.springframework.boot:spring-boot-devtools")
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package de.bund.digitalservice.ris.caselaw.adapter;

import com.fasterxml.jackson.databind.ObjectMapper;
import de.bund.digitalservice.ris.caselaw.domain.Decision;
import de.bund.digitalservice.ris.caselaw.domain.LegalPeriodicalEdition;
import de.bund.digitalservice.ris.caselaw.domain.Reference;
Expand All @@ -14,6 +13,7 @@
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import tools.jackson.databind.ObjectMapper;

/** Wraps the Juris XML exporter to provide a common interface for XML export. */
public class JurisXmlExporterWrapper implements XmlExporter {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package de.bund.digitalservice.ris.caselaw.adapter;

import com.fasterxml.jackson.databind.ObjectMapper;
import de.bund.digitalservice.ris.caselaw.adapter.caselawldml.CaseLawLdml;
import de.bund.digitalservice.ris.caselaw.adapter.database.jpa.AttachmentDTO;
import de.bund.digitalservice.ris.caselaw.adapter.database.jpa.AttachmentRepository;
Expand Down Expand Up @@ -30,6 +29,7 @@
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.mapping.MappingException;
import org.springframework.stereotype.Service;
import tools.jackson.databind.ObjectMapper;

@Service
@Slf4j
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ public S3AttachmentService(
public Attachment attachFileToDocumentationUnit(
UUID documentationUnitId, ByteBuffer byteBuffer, HttpHeaders httpHeaders, User user) {
String fileName =
httpHeaders.containsKey("X-Filename")
httpHeaders.containsHeader("X-Filename")
? httpHeaders.getFirst("X-Filename")
: "Kein Dateiname gefunden";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,9 @@
import lombok.ToString;
import lombok.experimental.Accessors;
import lombok.experimental.SuperBuilder;
import org.hibernate.annotations.JdbcType;
import org.hibernate.dialect.PostgreSQLEnumJdbcType;
import org.hibernate.annotations.JdbcTypeCode;
import org.hibernate.proxy.HibernateProxy;
import org.hibernate.type.SqlTypes;

@Getter
@Setter
Expand Down Expand Up @@ -270,7 +270,7 @@ public class DecisionDTO extends DocumentationUnitDTO {
/** Rechtsmittel zugelassen durch */
@Column(name = "appeal_admitted_by")
@Enumerated(EnumType.STRING)
@JdbcType(PostgreSQLEnumJdbcType.class)
@JdbcTypeCode(SqlTypes.NAMED_ENUM)
private AppealAdmitter appealAdmittedBy;

/** Rechtsmittel */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.hibernate.annotations.JdbcType;
import org.hibernate.dialect.PostgreSQLEnumJdbcType;
import org.hibernate.annotations.JdbcTypeCode;
import org.hibernate.type.SqlTypes;

@Getter
@Setter
Expand All @@ -37,7 +37,7 @@ public class DuplicateRelationDTO {

@Column(name = "status")
@Enumerated(EnumType.STRING)
@JdbcType(PostgreSQLEnumJdbcType.class)
@JdbcTypeCode(SqlTypes.NAMED_ENUM)
private DuplicateRelationStatus relationStatus;

@ManyToOne(fetch = FetchType.LAZY)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import org.hibernate.annotations.JdbcType;
import org.hibernate.dialect.PostgreSQLEnumJdbcType;
import org.hibernate.annotations.JdbcTypeCode;
import org.hibernate.type.SqlTypes;

@Getter
@Setter
Expand All @@ -38,7 +38,7 @@ public class IncomeTypeDTO {

@Column(name = "type_of_income")
@Enumerated(EnumType.STRING)
@JdbcType(PostgreSQLEnumJdbcType.class)
@JdbcTypeCode(SqlTypes.NAMED_ENUM)
private TypeOfIncome typeOfIncome;

@Column @NotNull private Long rank;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.hibernate.annotations.JdbcType;
import org.hibernate.dialect.PostgreSQLEnumJdbcType;
import org.hibernate.annotations.JdbcTypeCode;
import org.hibernate.type.SqlTypes;

@Getter
@Setter
Expand All @@ -43,7 +43,7 @@ public class OriginOfTranslationDTO {

@Column(name = "translation_type")
@Enumerated(EnumType.STRING)
@JdbcType(PostgreSQLEnumJdbcType.class)
@JdbcTypeCode(SqlTypes.NAMED_ENUM)
private TranslationType translationType;

@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -955,10 +955,7 @@ public Slice<RelatedDocumentationUnit> searchLinkableDocumentationUnits(
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<DocumentationUnitDTO> criteriaQuery =
criteriaBuilder.createQuery(DocumentationUnitDTO.class);
Root<? extends DocumentationUnitDTO> root =
onlyPendingProceedings
? criteriaQuery.from(PendingProceedingDTO.class)
: criteriaQuery.from(DocumentationUnitDTO.class);
Root<DocumentationUnitDTO> root = criteriaQuery.from(DocumentationUnitDTO.class);

// Conditions setup
Predicate conditions = criteriaBuilder.conjunction(); // Start with an empty conjunction (AND)
Expand All @@ -976,15 +973,16 @@ public Slice<RelatedDocumentationUnit> searchLinkableDocumentationUnits(
// 1. Filter by document number
if (documentNumberToExclude != null) {
Predicate documentNumberPredicate =
criteriaBuilder.notEqual(root.get("documentNumber"), documentNumberToExclude);
criteriaBuilder.notEqual(
root.get(DocumentationUnitDTO_.documentNumber), documentNumberToExclude);
conditions = criteriaBuilder.and(conditions, documentNumberPredicate);
}

// 2. Filter by court type
if (courtType != null) {
Predicate courtTypePredicate =
criteriaBuilder.like(
criteriaBuilder.upper(root.get("court").get("type")),
criteriaBuilder.upper(root.get(DocumentationUnitDTO_.court).get(CourtDTO_.type)),
"%" + courtType.toUpperCase() + "%");
conditions = criteriaBuilder.and(conditions, courtTypePredicate);
}
Expand All @@ -993,71 +991,81 @@ public Slice<RelatedDocumentationUnit> searchLinkableDocumentationUnits(
if (courtLocation != null) {
Predicate courtLocationPredicate =
criteriaBuilder.like(
criteriaBuilder.upper(root.get("court").get("location")),
criteriaBuilder.upper(root.get(DocumentationUnitDTO_.court).get(CourtDTO_.location)),
"%" + courtLocation.toUpperCase() + "%");
conditions = criteriaBuilder.and(conditions, courtLocationPredicate);
}

// 4. Filter by decision date
if (decisionDate != null) {
Predicate decisionDatePredicate = criteriaBuilder.equal(root.get("date"), decisionDate);
Predicate decisionDatePredicate =
criteriaBuilder.equal(root.get(DocumentationUnitDTO_.date), decisionDate);
conditions = criteriaBuilder.and(conditions, decisionDatePredicate);
}

// 5. Filter by file number
if (fileNumber != null) {
Join<DocumentationUnitDTO, String> fileNumberJoin = root.join("fileNumbers", JoinType.LEFT);
Join<DocumentationUnitDTO, FileNumberDTO> fileNumberJoin =
root.join(DocumentationUnitDTO_.fileNumbers, JoinType.LEFT);
Predicate fileNumberPredicate =
criteriaBuilder.like(
criteriaBuilder.upper(fileNumberJoin.get("value")), fileNumber.toUpperCase() + "%");
criteriaBuilder.upper(fileNumberJoin.get(FileNumberDTO_.value)),
fileNumber.toUpperCase() + "%");
conditions = criteriaBuilder.and(conditions, fileNumberPredicate);
}

// 6. Filter by document type
if (documentType != null) {
Predicate documentTypePredicate =
criteriaBuilder.equal(
root.get("documentType"), DocumentTypeTransformer.transformToDTO(documentType));
root.get(DocumentationUnitDTO_.documentType),
DocumentTypeTransformer.transformToDTO(documentType));
conditions = criteriaBuilder.and(conditions, documentTypePredicate);
}

// 7. Filter by publication status
final String PUBLICATION_STATUS = "publicationStatus";
final String STATUS = "status";
Predicate documentationOfficeIdPredicate =
criteriaBuilder.equal(
root.get("documentationOffice").get("id"), documentationOfficeDTO.getId());

// 8. Filter by document number
// 7. Filter by document number
if (documentNumber != null) {
conditions =
criteriaBuilder.and(
conditions,
criteriaBuilder.like(
criteriaBuilder.upper(root.get("documentNumber")),
criteriaBuilder.upper(root.get(DocumentationUnitDTO_.documentNumber)),
"%" + documentNumber.trim().toUpperCase() + "%"));
}

// 8. Filter by onlyPendingProceedings
if (onlyPendingProceedings) {
conditions =
criteriaBuilder.and(
conditions, criteriaBuilder.equal(root.type(), PendingProceedingDTO.class));
}

// 9. Filter by publication status
Predicate documentationOfficeIdPredicate =
criteriaBuilder.equal(
root.get(DocumentationUnitDTO_.documentationOffice).get(DocumentationOfficeDTO_.id),
documentationOfficeDTO.getId());

Predicate publicationStatusPredicate =
criteriaBuilder.or(
criteriaBuilder.equal(
root.get(STATUS).get(PUBLICATION_STATUS), PublicationStatus.PUBLISHED),
root.get(DocumentationUnitDTO_.status).get(StatusDTO_.publicationStatus),
PublicationStatus.PUBLISHED),
criteriaBuilder.equal(
root.get(STATUS).get(PUBLICATION_STATUS), PublicationStatus.PUBLISHING));
root.get(DocumentationUnitDTO_.status).get(StatusDTO_.publicationStatus),
PublicationStatus.PUBLISHING));

Predicate externalHandoverPendingPredicate =
// PendingProceedings don't have a `creatingDocumentationOffice` so this predicate does not
// work when filtering
// for PendingProceedings.
onlyPendingProceedings
? criteriaBuilder.disjunction()
: criteriaBuilder.and(
criteriaBuilder.equal(
root.get(STATUS).get(PUBLICATION_STATUS),
PublicationStatus.EXTERNAL_HANDOVER_PENDING),
criteriaBuilder.equal(
root.get("creatingDocumentationOffice").get("id"),
documentationOfficeDTO.getId()));
criteriaBuilder.and(
criteriaBuilder.equal(
root.get(DocumentationUnitDTO_.status).get(StatusDTO_.publicationStatus),
PublicationStatus.EXTERNAL_HANDOVER_PENDING),
criteriaBuilder.equal(
criteriaBuilder
.treat(root, DecisionDTO.class)
.get(DecisionDTO_.creatingDocumentationOffice)
.get(DocumentationOfficeDTO_.id),
documentationOfficeDTO.getId()));

Predicate finalPredicate =
criteriaBuilder.or(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.Expression;
import jakarta.persistence.criteria.Join;
import jakarta.persistence.criteria.Nulls;
import jakarta.persistence.criteria.Order;
import jakarta.persistence.criteria.Path;
import jakarta.persistence.criteria.Predicate;
Expand All @@ -35,7 +36,6 @@
import java.util.UUID;
import lombok.Builder;
import lombok.extern.slf4j.Slf4j;
import org.hibernate.query.NullPrecedence;
import org.hibernate.query.criteria.HibernateCriteriaBuilder;
import org.hibernate.query.criteria.JpaPredicate;
import org.jetbrains.annotations.NotNull;
Expand Down Expand Up @@ -564,14 +564,12 @@ private List<Order> getOrderCriteria(
if (parameters.scheduledOnly || parameters.handoverDate.isPresent()) {
orderCriteria.add(
cb.desc(root.get(DocumentationUnitDTO_.scheduledPublicationDateTime))
.nullPrecedence(NullPrecedence.LAST));
.nullPrecedence(Nulls.LAST));
orderCriteria.add(
cb.desc(root.get(DocumentationUnitDTO_.lastHandoverDateTime))
.nullPrecedence(NullPrecedence.LAST));
cb.desc(root.get(DocumentationUnitDTO_.lastHandoverDateTime)).nullPrecedence(Nulls.LAST));
}

orderCriteria.add(
cb.desc(root.get(DocumentationUnitDTO_.date)).nullPrecedence(NullPrecedence.LAST));
orderCriteria.add(cb.desc(root.get(DocumentationUnitDTO_.date)).nullPrecedence(Nulls.LAST));
orderCriteria.add(cb.desc(root.get(DocumentationUnitDTO_.documentNumber)));
return orderCriteria;
}
Expand Down
Loading
Loading