From acf352c732ac4afe5857072250e3b89ebc31520b Mon Sep 17 00:00:00 2001 From: "alexander.kerscher" Date: Thu, 4 Dec 2025 15:02:43 +0100 Subject: [PATCH 1/4] Validator als annotation eingebaut --- .../dave/controller/ZaehlungController.java | 3 +- .../dtos/bearbeiten/BearbeiteZaehlungDTO.java | 2 + .../validation/BearbeiteZaehlungValid.java | 21 +++++ .../BearbeiteZaehlungValidator.java | 85 +++++++++++++++++++ 4 files changed, 110 insertions(+), 1 deletion(-) create mode 100644 src/main/java/de/muenchen/dave/validation/BearbeiteZaehlungValid.java create mode 100644 src/main/java/de/muenchen/dave/validation/BearbeiteZaehlungValidator.java diff --git a/src/main/java/de/muenchen/dave/controller/ZaehlungController.java b/src/main/java/de/muenchen/dave/controller/ZaehlungController.java index 6ea076b84..214e1cfcf 100644 --- a/src/main/java/de/muenchen/dave/controller/ZaehlungController.java +++ b/src/main/java/de/muenchen/dave/controller/ZaehlungController.java @@ -14,6 +14,7 @@ import de.muenchen.dave.services.ChatMessageService; import de.muenchen.dave.services.persist.ExternalZaehlungPersistierungsService; import de.muenchen.dave.services.persist.InternalZaehlungPersistierungsService; +import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; import java.util.List; import lombok.extern.slf4j.Slf4j; @@ -81,7 +82,7 @@ public ResponseEntity saveWithZeitintervall(@RequestBody @NotNull @PostMapping(value = "/save", produces = MediaType.APPLICATION_JSON_VALUE) @PreAuthorize("hasRole(T(de.muenchen.dave.security.AuthoritiesEnum).FACHADMIN.name())") - public ResponseEntity saveZaehlung(@RequestBody @NotNull final BearbeiteZaehlungDTO zaehlung, + public ResponseEntity saveZaehlung(@RequestBody @NotNull @Valid final BearbeiteZaehlungDTO zaehlung, @RequestParam(value = REQUEST_PARAMETER_ZAEHLSTELLE_ID) @NotNull final String zaehlstelleId) { log.debug("Zaehlung speichern: {}", zaehlung); try { diff --git a/src/main/java/de/muenchen/dave/domain/dtos/bearbeiten/BearbeiteZaehlungDTO.java b/src/main/java/de/muenchen/dave/domain/dtos/bearbeiten/BearbeiteZaehlungDTO.java index eac8628c5..a3754a836 100644 --- a/src/main/java/de/muenchen/dave/domain/dtos/bearbeiten/BearbeiteZaehlungDTO.java +++ b/src/main/java/de/muenchen/dave/domain/dtos/bearbeiten/BearbeiteZaehlungDTO.java @@ -2,12 +2,14 @@ import de.muenchen.dave.domain.dtos.PkwEinheitDTO; import de.muenchen.dave.domain.enums.Fahrzeug; +import de.muenchen.dave.validation.BearbeiteZaehlungValid; import java.time.LocalDate; import java.util.List; import lombok.Data; import org.springframework.data.elasticsearch.core.geo.GeoPoint; @Data +@BearbeiteZaehlungValid public class BearbeiteZaehlungDTO { String id; diff --git a/src/main/java/de/muenchen/dave/validation/BearbeiteZaehlungValid.java b/src/main/java/de/muenchen/dave/validation/BearbeiteZaehlungValid.java new file mode 100644 index 000000000..0348047f4 --- /dev/null +++ b/src/main/java/de/muenchen/dave/validation/BearbeiteZaehlungValid.java @@ -0,0 +1,21 @@ +package de.muenchen.dave.validation; + +import jakarta.validation.Constraint; +import jakarta.validation.Payload; +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target({ ElementType.TYPE }) +@Retention(RetentionPolicy.RUNTIME) +@Constraint(validatedBy = BearbeiteZaehlungValidator.class) +@Documented +public @interface BearbeiteZaehlungValid { + String message() default "Die zu speichernde Zählung ist nicht valide."; + + Class[] groups() default {}; + + Class[] payload() default {}; +} diff --git a/src/main/java/de/muenchen/dave/validation/BearbeiteZaehlungValidator.java b/src/main/java/de/muenchen/dave/validation/BearbeiteZaehlungValidator.java new file mode 100644 index 000000000..8a93d3ca3 --- /dev/null +++ b/src/main/java/de/muenchen/dave/validation/BearbeiteZaehlungValidator.java @@ -0,0 +1,85 @@ +package de.muenchen.dave.validation; + +import de.muenchen.dave.domain.dtos.bearbeiten.BearbeiteKnotenarmDTO; +import de.muenchen.dave.domain.dtos.bearbeiten.BearbeiteZaehlungDTO; +import de.muenchen.dave.domain.enums.Fahrzeug; +import de.muenchen.dave.domain.enums.Zaehlart; +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; +import java.util.List; +import java.util.Optional; +import lombok.NoArgsConstructor; +import org.apache.commons.collections4.CollectionUtils; +import org.springframework.stereotype.Component; + +@Component +@NoArgsConstructor +public class BearbeiteZaehlungValidator implements ConstraintValidator { + + /** + * Prüft, ob die zu speichernde Zählung valide ist. + * + * @param toValidate {@link BearbeiteZaehlungDTO} zum Validieren. + * @param constraintValidatorContext in welchem die Validierung stattfindet. + * @return true, wenn die Zählung valide ist, sonst false. + */ + @Override + public boolean isValid(final BearbeiteZaehlungDTO toValidate, final ConstraintValidatorContext constraintValidatorContext) { + return toValidate != null && validateZaehlung(toValidate); + } + + /** + * Ruft die einzelnen Validierungsmethoden auf und sammelt die Ergebnisse. + * + * @param toValidate {@link BearbeiteZaehlungDTO} zum Validieren. + * @return true, wenn alle Validierungen erfolgreich waren, sonst false. + */ + private boolean validateZaehlung(final BearbeiteZaehlungDTO toValidate) { + return areZaehlartAndSelectedCategoriesValid(toValidate) && areZaehlartAndSelctedKnotenarmeValid(toValidate); + } + + /** + * Validiert anhand der ausgewaehlten Zaehlart, ob exakt 2 sich gegenueberliegende Knotenarme + * ausgewaehlt wurden. + * Erlaubte Knotenarme bei der {@link Zaehlart}.QJS : 1 & 3 || 2 & 4 || 5 & 7 || 6 & 8 + * + * @param toValidate {@link BearbeiteZaehlungDTO} zum Validieren. + * @return true, wenn die Validierung erfolgreich waren, sonst false. + */ + protected boolean areZaehlartAndSelctedKnotenarmeValid(final BearbeiteZaehlungDTO toValidate) { + boolean isValid = true; + if (Zaehlart.QJS.name().equals(toValidate.getZaehlart())) { + isValid = toValidate.getKnotenarme().size() == 2; + final Optional reduce = toValidate.getKnotenarme() + .stream() + .map(BearbeiteKnotenarmDTO::getNummer) + .reduce((integer, integer2) -> Math.abs(integer - integer2)); + if (reduce.isPresent()) { + isValid = isValid && reduce.get() == 2; + } + } + return isValid; + } + + /** + * Validiert anhand der ausgewaehlten Zaehlart, ob die richtigen Fahrzeuge ausgewaehlt wurden. + * Bei den {@link Zaehlart}.QJS FJS QU darf nur {@link Fahrzeug}.RAD und/oder .FUSS ausgewaehlt + * sein. + * + * @param toValidate {@link BearbeiteZaehlungDTO} zum Validieren. + * @return true, wenn die Validierung erfolgreich waren, sonst false. + */ + protected boolean areZaehlartAndSelectedCategoriesValid(final BearbeiteZaehlungDTO toValidate) { + // Wenn die Zählart QJS, FJS oder QU ist, dann darf nur RAD oder FUSS ausgewählt sein + boolean isValid = true; + final List zaehlarten = List.of(Zaehlart.QJS.name(), Zaehlart.FJS.name(), Zaehlart.QU.name()); + if (toValidate.getZaehlart() != null && zaehlarten.contains(toValidate.getZaehlart())) { + final List selectedCategoriesWithoutRadAndFuss = toValidate.getKategorien() + .stream() + .filter(fahrzeug -> !(fahrzeug.equals(Fahrzeug.RAD) || fahrzeug.equals(Fahrzeug.FUSS))) + .toList(); + isValid = CollectionUtils.isEmpty(selectedCategoriesWithoutRadAndFuss); + } + return isValid; + } +} From 79b47eacd3767d25f1b97ebf29f3bc84a4287d92 Mon Sep 17 00:00:00 2001 From: "alexander.kerscher" Date: Wed, 10 Dec 2025 14:10:24 +0100 Subject: [PATCH 2/4] update Validator --- .../dave/validation/BearbeiteZaehlungValidator.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/de/muenchen/dave/validation/BearbeiteZaehlungValidator.java b/src/main/java/de/muenchen/dave/validation/BearbeiteZaehlungValidator.java index 8a93d3ca3..9053ccb4d 100644 --- a/src/main/java/de/muenchen/dave/validation/BearbeiteZaehlungValidator.java +++ b/src/main/java/de/muenchen/dave/validation/BearbeiteZaehlungValidator.java @@ -50,12 +50,12 @@ protected boolean areZaehlartAndSelctedKnotenarmeValid(final BearbeiteZaehlungDT boolean isValid = true; if (Zaehlart.QJS.name().equals(toValidate.getZaehlart())) { isValid = toValidate.getKnotenarme().size() == 2; - final Optional reduce = toValidate.getKnotenarme() - .stream() - .map(BearbeiteKnotenarmDTO::getNummer) - .reduce((integer, integer2) -> Math.abs(integer - integer2)); - if (reduce.isPresent()) { - isValid = isValid && reduce.get() == 2; + if (isValid) { + final Optional reduce = toValidate.getKnotenarme() + .stream() + .map(BearbeiteKnotenarmDTO::getNummer) + .reduce((integer, integer2) -> Math.abs(integer - integer2)); + isValid = reduce.get() == 2; } } return isValid; From 39055d6b1f63b50aeee78e5755387d71e77b9549 Mon Sep 17 00:00:00 2001 From: "alexander.kerscher" Date: Wed, 10 Dec 2025 14:10:35 +0100 Subject: [PATCH 3/4] add Tests for Validator --- .../BearbeiteZaehlungValidatorTest.java | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 src/test/java/de/muenchen/dave/validation/BearbeiteZaehlungValidatorTest.java diff --git a/src/test/java/de/muenchen/dave/validation/BearbeiteZaehlungValidatorTest.java b/src/test/java/de/muenchen/dave/validation/BearbeiteZaehlungValidatorTest.java new file mode 100644 index 000000000..60a522683 --- /dev/null +++ b/src/test/java/de/muenchen/dave/validation/BearbeiteZaehlungValidatorTest.java @@ -0,0 +1,67 @@ +package de.muenchen.dave.validation; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +import de.muenchen.dave.domain.dtos.bearbeiten.BearbeiteKnotenarmDTO; +import de.muenchen.dave.domain.dtos.bearbeiten.BearbeiteZaehlungDTO; +import de.muenchen.dave.domain.enums.Fahrzeug; +import de.muenchen.dave.domain.enums.Zaehlart; +import java.util.ArrayList; +import org.junit.jupiter.api.Test; + +public class BearbeiteZaehlungValidatorTest { + + private final BearbeiteZaehlungValidator validator = new BearbeiteZaehlungValidator(); + + @Test + void isValidTest() { + assertThat(this.validator.isValid(null, null), is(false)); + assertThat(this.validator.isValid(new BearbeiteZaehlungDTO(), null), is(true)); + } + + @Test + void areZaehlartAndSelctedKnotenarmeValidTest() { + final BearbeiteZaehlungDTO toValidate = new BearbeiteZaehlungDTO(); + toValidate.setZaehlart(Zaehlart.FJS.name()); + assertThat(this.validator.areZaehlartAndSelctedKnotenarmeValid(toValidate), is(true)); + toValidate.setZaehlart(Zaehlart.QJS.name()); + toValidate.setKnotenarme(new ArrayList<>()); + assertThat(this.validator.areZaehlartAndSelctedKnotenarmeValid(toValidate), is(false)); + final BearbeiteKnotenarmDTO node1 = new BearbeiteKnotenarmDTO(); + node1.setNummer(1); + toValidate.getKnotenarme().add(node1); + final BearbeiteKnotenarmDTO node2 = new BearbeiteKnotenarmDTO(); + toValidate.getKnotenarme().add(node2); + assertThat(this.validator.areZaehlartAndSelctedKnotenarmeValid(toValidate), is(false)); + node2.setNummer(2); + assertThat(this.validator.areZaehlartAndSelctedKnotenarmeValid(toValidate), is(false)); + node2.setNummer(3); + assertThat(this.validator.areZaehlartAndSelctedKnotenarmeValid(toValidate), is(true)); + toValidate.getKnotenarme().add(new BearbeiteKnotenarmDTO()); + assertThat(this.validator.areZaehlartAndSelctedKnotenarmeValid(toValidate), is(false)); + } + + @Test + void areZaehlartAndSelectedCategoriesValidTest() { + final BearbeiteZaehlungDTO toValidate = new BearbeiteZaehlungDTO(); + assertThat(this.validator.areZaehlartAndSelectedCategoriesValid(toValidate), is(true)); + toValidate.setZaehlart(null); + assertThat(this.validator.areZaehlartAndSelectedCategoriesValid(toValidate), is(true)); + toValidate.setZaehlart(Zaehlart.N.name()); + assertThat(this.validator.areZaehlartAndSelectedCategoriesValid(toValidate), is(true)); + toValidate.setZaehlart(Zaehlart.QJS.name()); + toValidate.setKategorien(new ArrayList<>()); + assertThat(this.validator.areZaehlartAndSelectedCategoriesValid(toValidate), is(true)); + toValidate.getKategorien().add(Fahrzeug.FUSS); + assertThat(this.validator.areZaehlartAndSelectedCategoriesValid(toValidate), is(true)); + toValidate.setZaehlart(Zaehlart.QU.name()); + toValidate.getKategorien().add(Fahrzeug.RAD); + assertThat(this.validator.areZaehlartAndSelectedCategoriesValid(toValidate), is(true)); + toValidate.setZaehlart(Zaehlart.FJS.name()); + assertThat(this.validator.areZaehlartAndSelectedCategoriesValid(toValidate), is(true)); + toValidate.getKategorien().add(Fahrzeug.KFZ); + assertThat(this.validator.areZaehlartAndSelectedCategoriesValid(toValidate), is(false)); + } + +} From b0de8f7b85ae703212894a27df73b437cf98a0d3 Mon Sep 17 00:00:00 2001 From: "alexander.kerscher" Date: Fri, 12 Dec 2025 13:38:36 +0100 Subject: [PATCH 4/4] anmerkungen umgesetzt --- .../validation/BearbeiteZaehlungValidator.java | 9 +++++---- .../validation/BearbeiteZaehlungValidatorTest.java | 14 +++++++------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/main/java/de/muenchen/dave/validation/BearbeiteZaehlungValidator.java b/src/main/java/de/muenchen/dave/validation/BearbeiteZaehlungValidator.java index 9053ccb4d..4a001eb0e 100644 --- a/src/main/java/de/muenchen/dave/validation/BearbeiteZaehlungValidator.java +++ b/src/main/java/de/muenchen/dave/validation/BearbeiteZaehlungValidator.java @@ -35,18 +35,18 @@ public boolean isValid(final BearbeiteZaehlungDTO toValidate, final ConstraintVa * @return true, wenn alle Validierungen erfolgreich waren, sonst false. */ private boolean validateZaehlung(final BearbeiteZaehlungDTO toValidate) { - return areZaehlartAndSelectedCategoriesValid(toValidate) && areZaehlartAndSelctedKnotenarmeValid(toValidate); + return areZaehlartAndSelectedCategoriesValid(toValidate) && areZaehlartAndSelectedKnotenarmeValid(toValidate); } /** * Validiert anhand der ausgewaehlten Zaehlart, ob exakt 2 sich gegenueberliegende Knotenarme * ausgewaehlt wurden. - * Erlaubte Knotenarme bei der {@link Zaehlart}.QJS : 1 & 3 || 2 & 4 || 5 & 7 || 6 & 8 + * Erlaubte Knotenarme bei der {@link Zaehlart#QJS} : 1 & 3 || 2 & 4 || 5 & 7 || 6 & 8 * * @param toValidate {@link BearbeiteZaehlungDTO} zum Validieren. * @return true, wenn die Validierung erfolgreich waren, sonst false. */ - protected boolean areZaehlartAndSelctedKnotenarmeValid(final BearbeiteZaehlungDTO toValidate) { + protected boolean areZaehlartAndSelectedKnotenarmeValid(final BearbeiteZaehlungDTO toValidate) { boolean isValid = true; if (Zaehlart.QJS.name().equals(toValidate.getZaehlart())) { isValid = toValidate.getKnotenarme().size() == 2; @@ -63,7 +63,8 @@ protected boolean areZaehlartAndSelctedKnotenarmeValid(final BearbeiteZaehlungDT /** * Validiert anhand der ausgewaehlten Zaehlart, ob die richtigen Fahrzeuge ausgewaehlt wurden. - * Bei den {@link Zaehlart}.QJS FJS QU darf nur {@link Fahrzeug}.RAD und/oder .FUSS ausgewaehlt + * Bei den {@link Zaehlart#QJS}, {@link Zaehlart#FJS} und {@link Zaehlart#QU} darf nur + * {@link Fahrzeug#RAD}.RAD und/oder {@link Fahrzeug#FUSS} ausgewaehlt sein * sein. * * @param toValidate {@link BearbeiteZaehlungDTO} zum Validieren. diff --git a/src/test/java/de/muenchen/dave/validation/BearbeiteZaehlungValidatorTest.java b/src/test/java/de/muenchen/dave/validation/BearbeiteZaehlungValidatorTest.java index 60a522683..fa34ecdb1 100644 --- a/src/test/java/de/muenchen/dave/validation/BearbeiteZaehlungValidatorTest.java +++ b/src/test/java/de/muenchen/dave/validation/BearbeiteZaehlungValidatorTest.java @@ -21,25 +21,25 @@ void isValidTest() { } @Test - void areZaehlartAndSelctedKnotenarmeValidTest() { + void areZaehlartAndSelectedKnotenarmeValidTest() { final BearbeiteZaehlungDTO toValidate = new BearbeiteZaehlungDTO(); toValidate.setZaehlart(Zaehlart.FJS.name()); - assertThat(this.validator.areZaehlartAndSelctedKnotenarmeValid(toValidate), is(true)); + assertThat(this.validator.areZaehlartAndSelectedKnotenarmeValid(toValidate), is(true)); toValidate.setZaehlart(Zaehlart.QJS.name()); toValidate.setKnotenarme(new ArrayList<>()); - assertThat(this.validator.areZaehlartAndSelctedKnotenarmeValid(toValidate), is(false)); + assertThat(this.validator.areZaehlartAndSelectedKnotenarmeValid(toValidate), is(false)); final BearbeiteKnotenarmDTO node1 = new BearbeiteKnotenarmDTO(); node1.setNummer(1); toValidate.getKnotenarme().add(node1); final BearbeiteKnotenarmDTO node2 = new BearbeiteKnotenarmDTO(); toValidate.getKnotenarme().add(node2); - assertThat(this.validator.areZaehlartAndSelctedKnotenarmeValid(toValidate), is(false)); + assertThat(this.validator.areZaehlartAndSelectedKnotenarmeValid(toValidate), is(false)); node2.setNummer(2); - assertThat(this.validator.areZaehlartAndSelctedKnotenarmeValid(toValidate), is(false)); + assertThat(this.validator.areZaehlartAndSelectedKnotenarmeValid(toValidate), is(false)); node2.setNummer(3); - assertThat(this.validator.areZaehlartAndSelctedKnotenarmeValid(toValidate), is(true)); + assertThat(this.validator.areZaehlartAndSelectedKnotenarmeValid(toValidate), is(true)); toValidate.getKnotenarme().add(new BearbeiteKnotenarmDTO()); - assertThat(this.validator.areZaehlartAndSelctedKnotenarmeValid(toValidate), is(false)); + assertThat(this.validator.areZaehlartAndSelectedKnotenarmeValid(toValidate), is(false)); } @Test