diff --git a/alfa-server/src/main/resources/application-local.yml b/alfa-server/src/main/resources/application-local.yml index 017e2e48e7815170047c8677d07cd6c64eadcc15..55480e664c9e5159bc787f902bd7c1d8e77eda1a 100644 --- a/alfa-server/src/main/resources/application-local.yml +++ b/alfa-server/src/main/resources/application-local.yml @@ -27,10 +27,6 @@ ozgcloud: url: http://localhost:9092 profile-template: /api/userProfiles/%s search-template: /api/userProfiles/?searchBy={searchBy} - xdomea: - behoerdenschluessel: "behoerdenschluesselWirdÜberHelmGesetzt" - behoerdenschluesselUri: "behoerdenschluesselUriWirdÜberHelmGesetzt" - behoerdenschluesselVersion: "behoerdenschluesselVersionWirdÜberHelmGesetzt" keycloak: auth-server-url: http://localhost:8088 diff --git a/alfa-server/src/main/resources/application.yml b/alfa-server/src/main/resources/application.yml index 8e17178fea49083f2e1a5035d0b27d50c6670e50..3396df96aa0b06fd2c95d525f353fd88795cdd64 100644 --- a/alfa-server/src/main/resources/application.yml +++ b/alfa-server/src/main/resources/application.yml @@ -91,4 +91,18 @@ ozgcloud: - image/jpeg user-manager: profile-template: /api/userProfiles/%s - search-template: /api/userProfiles/?searchBy={searchBy} \ No newline at end of file + search-template: /api/userProfiles/?searchBy={searchBy} + xdomea: + behoerdenschluessel: + behoerdenschluessel-uri: + behoerdenschluessel-version: + codeliste: + - uri: urn:de:bund:destatis:bevoelkerungsstatistik:schluessel:kreis + version: 2021-07-31 + pattern: \d{5} + - uri: urn:de:bund:destatis:bevoelkerungsstatistik:schluessel:ags + version: 2024-06-30 + pattern: \d{8} + - uri: urn:de:bund:destatis:bevoelkerungsstatistik:schluessel:rs + version: 2024-06-30 + pattern: \d{12} diff --git a/alfa-xdomea/src/main/java/de/ozgcloud/alfa/export/AmbiguousCodelistePatternException.java b/alfa-xdomea/src/main/java/de/ozgcloud/alfa/export/AmbiguousCodelistePatternException.java new file mode 100644 index 0000000000000000000000000000000000000000..9defafcd5659d2adc634aad9271a207fc26e7b40 --- /dev/null +++ b/alfa-xdomea/src/main/java/de/ozgcloud/alfa/export/AmbiguousCodelistePatternException.java @@ -0,0 +1,14 @@ +package de.ozgcloud.alfa.export; + +import lombok.Getter; + +@Getter +public class AmbiguousCodelistePatternException extends RuntimeException { + + private final String behoerdenschluessel; + + public AmbiguousCodelistePatternException(String behoerdenschluessel) { + super("Multiple Codelisten match Behördenschlüssel " + behoerdenschluessel); + this.behoerdenschluessel = behoerdenschluessel; + } +} diff --git a/alfa-xdomea/src/main/java/de/ozgcloud/alfa/export/ExportConfiguration.java b/alfa-xdomea/src/main/java/de/ozgcloud/alfa/export/ExportConfiguration.java index 8840c0ac344b8d8d84fde3d7d48a96ec76794267..6014ffffc20570c182a91749f9d5a6fd3f1eabfb 100644 --- a/alfa-xdomea/src/main/java/de/ozgcloud/alfa/export/ExportConfiguration.java +++ b/alfa-xdomea/src/main/java/de/ozgcloud/alfa/export/ExportConfiguration.java @@ -3,12 +3,12 @@ package de.ozgcloud.alfa.export; import java.util.HashMap; import java.util.Map; -import jakarta.xml.bind.Marshaller; - import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.oxm.jaxb.Jaxb2Marshaller; +import org.springframework.validation.Validator; +import jakarta.xml.bind.Marshaller; import lombok.RequiredArgsConstructor; @RequiredArgsConstructor @@ -21,6 +21,11 @@ class ExportConfiguration { private final XdomeaNamespacePrefixMapper prefixMapper; + @Bean + static Validator configurationPropertiesValidator() { + return new XdomeaBehoerdenschluesselPropertiesValidator(); + } + @Bean Jaxb2Marshaller marshaller() { var marshaller = new Jaxb2Marshaller(); diff --git a/alfa-xdomea/src/main/java/de/ozgcloud/alfa/export/XdomeaBehoerdenschluesselProperties.java b/alfa-xdomea/src/main/java/de/ozgcloud/alfa/export/XdomeaBehoerdenschluesselProperties.java new file mode 100644 index 0000000000000000000000000000000000000000..11a20ef0688cb98c809328eedb9ec74e759a713a --- /dev/null +++ b/alfa-xdomea/src/main/java/de/ozgcloud/alfa/export/XdomeaBehoerdenschluesselProperties.java @@ -0,0 +1,52 @@ +package de.ozgcloud.alfa.export; + +import java.util.Optional; + +import org.apache.commons.lang3.StringUtils; + +import de.ozgcloud.alfa.export.XdomeaProperties.Codeliste; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +public class XdomeaBehoerdenschluesselProperties { + + private final XdomeaProperties xdomeaProperties; + + + public Optional<String> getBehoerdenschluessel() { + return ofNullableOrBlank(xdomeaProperties.getBehoerdenschluessel()); + } + + public Optional<String> getBehoerdenschluesselUri() { + return ofNullableOrBlank(xdomeaProperties.getBehoerdenschluesselUri()).or(this::getDefaultBehoerdenschluesselUri); + } + + Optional<String> getDefaultBehoerdenschluesselUri() { + return getCodeliste().map(Codeliste::getUri); + } + + public Optional<String> getBehoerdenschluesselVersion() { + return ofNullableOrBlank(xdomeaProperties.getBehoerdenschluesselVersion()).or(this::getDefaultBehoerdenschluesselVersion); + } + + Optional<String> getDefaultBehoerdenschluesselVersion() { + return getCodeliste().map(Codeliste::getVersion); + } + + Optional<Codeliste> getCodeliste() { + return ofNullableOrBlank(xdomeaProperties.getBehoerdenschluessel()).flatMap(this::getCodeliste); + } + + private Optional<Codeliste> getCodeliste(String behoerdenschluessel) { + var matchingCodelisten = xdomeaProperties.getCodeliste().stream() + .filter(codeliste -> codeliste.getPattern().matcher(behoerdenschluessel).matches()).toList(); + if (matchingCodelisten.size() > 1) { + throw new AmbiguousCodelistePatternException(behoerdenschluessel); + } + return matchingCodelisten.size() == 1 ? Optional.of(matchingCodelisten.getFirst()) : Optional.empty(); + } + + private static Optional<String> ofNullableOrBlank(String value) { + return Optional.ofNullable(value).filter(StringUtils::isNotBlank); + } +} diff --git a/alfa-xdomea/src/main/java/de/ozgcloud/alfa/export/XdomeaBehoerdenschluesselPropertiesValidator.java b/alfa-xdomea/src/main/java/de/ozgcloud/alfa/export/XdomeaBehoerdenschluesselPropertiesValidator.java new file mode 100644 index 0000000000000000000000000000000000000000..3bd07d57ee45c5848e59b0ea3bf423f860d17f30 --- /dev/null +++ b/alfa-xdomea/src/main/java/de/ozgcloud/alfa/export/XdomeaBehoerdenschluesselPropertiesValidator.java @@ -0,0 +1,35 @@ +package de.ozgcloud.alfa.export; + +import org.springframework.validation.Errors; +import org.springframework.validation.Validator; + +public class XdomeaBehoerdenschluesselPropertiesValidator implements Validator { + + @Override + public boolean supports(Class<?> clazz) { + return XdomeaProperties.class.isAssignableFrom(clazz); + } + + @Override + public void validate(Object target, Errors errors) { + var props = getBehoerdenschluesselProperties((XdomeaProperties) target); + if (props.getBehoerdenschluessel().isEmpty()) { + return; + } + try { + if (props.getBehoerdenschluesselUri().isEmpty()) { + errors.rejectValue("behoerdenschluesselUri", "ozgcloud.xdomea.behoerdenschluessel-uri.empty", "behoerdenschluessel-uri must be set"); + } + if (props.getBehoerdenschluesselVersion().isEmpty()) { + errors.rejectValue("behoerdenschluesselVersion", "ozgcloud.xdomea.behoerdenschluessel-version.empty", + "behoerdenschluessel-version must be set"); + } + } catch (AmbiguousCodelistePatternException e) { + errors.reject("ozgcloud.xdomea.codeliste.pattern.ambiguous", e.getMessage()); + } + } + + XdomeaBehoerdenschluesselProperties getBehoerdenschluesselProperties(XdomeaProperties xdomeaProperties) { + return new XdomeaBehoerdenschluesselProperties(xdomeaProperties); + } +} diff --git a/alfa-xdomea/src/main/java/de/ozgcloud/alfa/export/XdomeaProperties.java b/alfa-xdomea/src/main/java/de/ozgcloud/alfa/export/XdomeaProperties.java index 4068485fdcb1dc18f6932917678e5d9f116c643d..6c88c117f5b3003a11949bf55b84674c4dedf873 100644 --- a/alfa-xdomea/src/main/java/de/ozgcloud/alfa/export/XdomeaProperties.java +++ b/alfa-xdomea/src/main/java/de/ozgcloud/alfa/export/XdomeaProperties.java @@ -1,22 +1,49 @@ package de.ozgcloud.alfa.export; +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Pattern; + import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Configuration; +import org.springframework.validation.annotation.Validated; +import jakarta.validation.Valid; import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Getter; +import lombok.NoArgsConstructor; import lombok.Setter; -@Getter -@Setter @Configuration @ConfigurationProperties("ozgcloud.xdomea") +@Validated +@Builder +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor public class XdomeaProperties { - @NotBlank private String behoerdenschluessel; - @NotBlank private String behoerdenschluesselUri; - @NotBlank private String behoerdenschluesselVersion; + + @Valid + private List<Codeliste> codeliste = new ArrayList<>(); + + @Builder + @Setter + @Getter + public static class Codeliste { + + @NotBlank + private String uri; + @NotBlank + private String version; + @NotNull + private Pattern pattern; + } } diff --git a/alfa-xdomea/src/main/java/de/ozgcloud/alfa/vorgang/KopfCreator.java b/alfa-xdomea/src/main/java/de/ozgcloud/alfa/vorgang/KopfCreator.java index f23b06bdff34f335838f388ca0da9e389af0eedc..12fd13e14eb56ac96e8628782acfe28e31d48a13 100644 --- a/alfa-xdomea/src/main/java/de/ozgcloud/alfa/vorgang/KopfCreator.java +++ b/alfa-xdomea/src/main/java/de/ozgcloud/alfa/vorgang/KopfCreator.java @@ -7,6 +7,7 @@ import org.springframework.stereotype.Component; import de.ozgcloud.alfa.common.DateConverter; import de.ozgcloud.alfa.common.UUIDConverter; +import de.ozgcloud.alfa.export.XdomeaBehoerdenschluesselProperties; import de.ozgcloud.alfa.export.XdomeaProperties; import de.xoev.xdomea.BehoerdenkennungType; import de.xoev.xdomea.Code; @@ -57,27 +58,34 @@ class KopfCreator { KontaktType createKontaktType() { var kontakt = new KontaktType(); - kontakt.setBehoerdenkennung(createBehoerdenkennung()); + var behoerdenschluesselProperties = getBehoerdenschluesselProperties(xDomeaProperties); + if (behoerdenschluesselProperties.getBehoerdenschluessel().isPresent()) { + kontakt.setBehoerdenkennung(createBehoerdenkennung(behoerdenschluesselProperties)); + } return kontakt; } + XdomeaBehoerdenschluesselProperties getBehoerdenschluesselProperties(XdomeaProperties xdomeaProperties) { + return new XdomeaBehoerdenschluesselProperties(xdomeaProperties); + } + OrganisationseinheitType createOrganisationseinheitType(String organisationseinheitId) { var organisationseinheit = new OrganisationseinheitType(); organisationseinheit.setName(organisationseinheitId); return organisationseinheit; } - BehoerdenkennungType createBehoerdenkennung() { + BehoerdenkennungType createBehoerdenkennung(XdomeaBehoerdenschluesselProperties behoerdenschluesselProperties) { var behoerdenkennungType = new BehoerdenkennungType(); - behoerdenkennungType.setBehoerdenschluessel(createBehoerdenschluessel()); + behoerdenkennungType.setBehoerdenschluessel(createBehoerdenschluessel(behoerdenschluesselProperties)); return behoerdenkennungType; } - Code createBehoerdenschluessel() { + Code createBehoerdenschluessel(XdomeaBehoerdenschluesselProperties behoerdenschluesselProperties) { var behoerdenschluessel = new Code(); - behoerdenschluessel.setCode(xDomeaProperties.getBehoerdenschluessel()); - behoerdenschluessel.setListURI(xDomeaProperties.getBehoerdenschluesselUri()); - behoerdenschluessel.setListVersionID(xDomeaProperties.getBehoerdenschluesselVersion()); + behoerdenschluessel.setCode(behoerdenschluesselProperties.getBehoerdenschluessel().orElseThrow()); + behoerdenschluessel.setListURI(behoerdenschluesselProperties.getBehoerdenschluesselUri().orElseThrow()); + behoerdenschluessel.setListVersionID(behoerdenschluesselProperties.getBehoerdenschluesselVersion().orElseThrow()); return behoerdenschluessel; } diff --git a/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/XdomeaBehoerdenschluesselPropertiesTest.java b/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/XdomeaBehoerdenschluesselPropertiesTest.java new file mode 100644 index 0000000000000000000000000000000000000000..ca28c2ba797c2f657336438bbc5d9d6ca571b022 --- /dev/null +++ b/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/XdomeaBehoerdenschluesselPropertiesTest.java @@ -0,0 +1,293 @@ +package de.ozgcloud.alfa.export; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.Mockito.*; + +import java.util.List; +import java.util.Optional; +import java.util.UUID; +import java.util.stream.Stream; + +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.NullAndEmptySource; +import org.mockito.Spy; + +import de.ozgcloud.alfa.export.XdomeaProperties.Codeliste; +import de.ozgcloud.alfa.export.XdomeaProperties.XdomeaPropertiesBuilder; +import lombok.RequiredArgsConstructor; + +class XdomeaBehoerdenschluesselPropertiesTest { + + @Nested + class TestGetBehoerdenschluessel { + + @Test + void shouldReturnBehoerdenschluessel() { + var xdomeaProps = XdomeaPropertiesTestFactory.create(); + var bsProps = new XdomeaBehoerdenschluesselProperties(xdomeaProps); + + assertThat(bsProps.getBehoerdenschluessel()).isPresent().get().isEqualTo(XdomeaPropertiesTestFactory.BEHOERDENSCHLUESSEL); + } + + @ParameterizedTest + @NullAndEmptySource + void shouldReturnEmptyIfBehoerdenschluesselIsNotSet(String bs) { + var xdomeaProps = XdomeaPropertiesTestFactory.createBuilder().behoerdenschluessel(bs).build(); + var bsProps = new XdomeaBehoerdenschluesselProperties(xdomeaProps); + + assertThat(bsProps.getBehoerdenschluessel()).isNotPresent(); + } + } + + @Nested + class TestGetBehoerdenschluesselUri extends TestGetPropertyValue { + + @Override + protected void verifyDefaultPropertyValueAccessorWasCalled(XdomeaBehoerdenschluesselProperties bsProps) { + verify(bsProps).getDefaultBehoerdenschluesselUri(); + } + + @Override + protected String getPropertyValue(XdomeaProperties props) { + return props.getBehoerdenschluesselUri(); + } + + @Override + protected Optional<String> getPropertyValue(XdomeaBehoerdenschluesselProperties bsProps) { + return bsProps.getBehoerdenschluesselUri(); + } + + @Override + protected XdomeaPropertiesBuilder withPropertyValue(String value, XdomeaPropertiesBuilder propertiesBuilder) { + return propertiesBuilder.behoerdenschluesselUri(value); + } + + @Override + protected void mockPropertyDefaultValue(Optional<String> defaultValue, XdomeaBehoerdenschluesselProperties bsProps) { + doReturn(defaultValue).when(bsProps).getDefaultBehoerdenschluesselUri(); + } + } + + @Nested + class TestGetBehoerdenschluesselVersion extends TestGetPropertyValue { + + @Override + protected void verifyDefaultPropertyValueAccessorWasCalled(XdomeaBehoerdenschluesselProperties bsProps) { + verify(bsProps).getDefaultBehoerdenschluesselVersion(); + } + + @Override + protected String getPropertyValue(XdomeaProperties props) { + return props.getBehoerdenschluesselVersion(); + } + + @Override + protected Optional<String> getPropertyValue(XdomeaBehoerdenschluesselProperties bsProps) { + return bsProps.getBehoerdenschluesselVersion(); + } + + @Override + protected XdomeaPropertiesBuilder withPropertyValue(String value, XdomeaPropertiesBuilder propertiesBuilder) { + return propertiesBuilder.behoerdenschluesselVersion(value); + } + + @Override + protected void mockPropertyDefaultValue(Optional<String> defaultValue, XdomeaBehoerdenschluesselProperties bsProps) { + doReturn(defaultValue).when(bsProps).getDefaultBehoerdenschluesselVersion(); + } + } + + @RequiredArgsConstructor + private abstract static class TestGetPropertyValue { + + @Test + @SuppressWarnings("unused") + void shouldReturnPropertyValue() { + var xdomeaProps = XdomeaPropertiesTestFactory.create(); + var bsProps = new XdomeaBehoerdenschluesselProperties(xdomeaProps); + + var value = getPropertyValue(bsProps); + + assertThat(value).isPresent().get().isEqualTo(getPropertyValue(xdomeaProps)); + } + + @ParameterizedTest + @NullAndEmptySource + @SuppressWarnings("unused") + void shouldGetDefaultPropertyValue(String bs) { + var xdomeaProps = withPropertyValue(null, XdomeaPropertiesTestFactory.createBuilder().behoerdenschluessel(bs)).build(); + var bsProps = spy(new XdomeaBehoerdenschluesselProperties(xdomeaProps)); + mockPropertyDefaultValue(Optional.of(UUID.randomUUID().toString()), bsProps); + + getPropertyValue(bsProps); + + verifyDefaultPropertyValueAccessorWasCalled(bsProps); + } + + @ParameterizedTest + @NullAndEmptySource + @SuppressWarnings("unused") + void shouldReturnDefaultPropertyValue(String bs) { + final String DEFAULT_VALUE = UUID.randomUUID().toString(); + var xdomeaProps = withPropertyValue(null, XdomeaPropertiesTestFactory.createBuilder().behoerdenschluessel(bs)).build(); + var bsProps = spy(new XdomeaBehoerdenschluesselProperties(xdomeaProps)); + mockPropertyDefaultValue(Optional.of(DEFAULT_VALUE), bsProps); + + var value = getPropertyValue(bsProps); + + assertThat(value).isPresent().get().isEqualTo(DEFAULT_VALUE); + } + + @ParameterizedTest + @NullAndEmptySource + @SuppressWarnings("unused") + void shouldReturnEmptyIfDefaultPropertyValueIsEmpty(String bs) { + var xdomeaProps = withPropertyValue(null, XdomeaPropertiesTestFactory.createBuilder().behoerdenschluessel(bs)).build(); + var bsProps = spy(new XdomeaBehoerdenschluesselProperties(xdomeaProps)); + mockPropertyDefaultValue(Optional.empty(), bsProps); + + var value = getPropertyValue(bsProps); + + assertThat(value).isEmpty(); + } + + protected abstract void verifyDefaultPropertyValueAccessorWasCalled(XdomeaBehoerdenschluesselProperties bsProps); + + protected abstract String getPropertyValue(XdomeaProperties props); + + protected abstract Optional<String> getPropertyValue(XdomeaBehoerdenschluesselProperties bsProps); + + protected abstract XdomeaPropertiesBuilder withPropertyValue(String value, XdomeaPropertiesBuilder propertiesBuilder); + + protected abstract void mockPropertyDefaultValue(Optional<String> defaultValue, XdomeaBehoerdenschluesselProperties bsProps); + } + + @Nested + class TestGetDefaultBehoerdenschluesselUri extends TestGetPropertyDefaultValue { + + @Override + protected Optional<String> getPropertyDefaultValue(XdomeaBehoerdenschluesselProperties bsProps) { + return bsProps.getDefaultBehoerdenschluesselUri(); + } + + @Override + protected String getPropertyDefaultValueFromCodeliste(Codeliste codeliste) { + return codeliste.getUri(); + } + } + + @Nested + class TestGetDefaultBehoerdenschluesselVersion extends TestGetPropertyDefaultValue { + + @Override + protected Optional<String> getPropertyDefaultValue(XdomeaBehoerdenschluesselProperties bsProps) { + return bsProps.getDefaultBehoerdenschluesselVersion(); + } + + @Override + protected String getPropertyDefaultValueFromCodeliste(Codeliste codeliste) { + return codeliste.getVersion(); + } + } + + @RequiredArgsConstructor + private abstract static class TestGetPropertyDefaultValue { + + @Spy + private XdomeaBehoerdenschluesselProperties bsProps = new XdomeaBehoerdenschluesselProperties(XdomeaPropertiesTestFactory.create()); + + @Test + @SuppressWarnings("unused") + void shouldGetCodeliste() { + doReturn(Optional.of(XdomeaPropertiesTestFactory.CODELISTE_1)).when(bsProps).getCodeliste(); + + getPropertyDefaultValue(); + + verify(bsProps).getCodeliste(); + } + + @Test + @SuppressWarnings("unused") + void shouldReturnEmptyIfCodelisteIsEmpty() { + doReturn(Optional.empty()).when(bsProps).getCodeliste(); + + var defaultValue = getPropertyDefaultValue(); + + assertThat(defaultValue).isEmpty(); + } + + @Test + void shouldReturnDefaultValueFromCodeliste() { + doReturn(Optional.of(XdomeaPropertiesTestFactory.CODELISTE_1)).when(bsProps).getCodeliste(); + + var defaultValue = getPropertyDefaultValue(); + + assertThat(defaultValue).isPresent().get().isEqualTo(getPropertyDefaultValueFromCodeliste(XdomeaPropertiesTestFactory.CODELISTE_1)); + } + + private Optional<String> getPropertyDefaultValue() { + return getPropertyDefaultValue(bsProps); + } + + protected abstract Optional<String> getPropertyDefaultValue(XdomeaBehoerdenschluesselProperties bsProps); + + protected abstract String getPropertyDefaultValueFromCodeliste(Codeliste codeliste); + } + + @Nested + class TestGetCodeliste { + + @ParameterizedTest + @NullAndEmptySource + void shouldReturnEmtpyIfBehoerdenschluesselIsNotSet(String bs) { + var bsProps = new XdomeaBehoerdenschluesselProperties(XdomeaPropertiesTestFactory.createBuilder().behoerdenschluessel(bs).build()); + + var codeliste = bsProps.getCodeliste(); + + assertThat(codeliste).isEmpty(); + } + + @Test + void shouldReturnEmptyIfNoCodelisteMatchesBehoerdenschluessel() { + var bsProps = new XdomeaBehoerdenschluesselProperties(XdomeaPropertiesTestFactory.create()); + + var codeliste = bsProps.getCodeliste(); + + assertThat(codeliste).isEmpty(); + } + + @ParameterizedTest + @MethodSource("provideData") + void shouldReturnCodelisteMatchingBehoerdenschluessel(String bs, Codeliste expectedCodeliste) { + var bsProps = new XdomeaBehoerdenschluesselProperties(XdomeaPropertiesTestFactory.createBuilder().behoerdenschluessel(bs).build()); + + var codeliste = bsProps.getCodeliste(); + + assertThat(codeliste).isPresent().get().usingRecursiveComparison().isEqualTo(expectedCodeliste); + } + + @Test + void shouldThrowExceptionIfMatchIsAmbiguous() { + var props = XdomeaPropertiesTestFactory.createBuilder() + .behoerdenschluessel(XdomeaPropertiesTestFactory.CODELISTE_1_VALID_BEHOERDENSCHLUESSEL) + .codeliste(List.of(XdomeaPropertiesTestFactory.CODELISTE_1, XdomeaPropertiesTestFactory.CODELISTE_1)) + .build(); + var bsProps = new XdomeaBehoerdenschluesselProperties(props); + + assertThatThrownBy(bsProps::getCodeliste).isInstanceOf(AmbiguousCodelistePatternException.class) + .hasFieldOrPropertyWithValue("behoerdenschluessel", props.getBehoerdenschluessel()) + .hasMessageContaining(props.getBehoerdenschluessel()); + } + + private static Stream<Arguments> provideData() { + return Stream.of( + Arguments.of(XdomeaPropertiesTestFactory.CODELISTE_1_VALID_BEHOERDENSCHLUESSEL, XdomeaPropertiesTestFactory.CODELISTE_1), + Arguments.of(XdomeaPropertiesTestFactory.CODELISTE_2_VALID_BEHOERDENSCHLUESSEL, XdomeaPropertiesTestFactory.CODELISTE_2) + ); + } + } +} diff --git a/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/XdomeaBehoerdenschluesselPropertiesValidatorTest.java b/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/XdomeaBehoerdenschluesselPropertiesValidatorTest.java new file mode 100644 index 0000000000000000000000000000000000000000..2d7094edd9dac5ee74bbace44492c0470a7f70c1 --- /dev/null +++ b/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/XdomeaBehoerdenschluesselPropertiesValidatorTest.java @@ -0,0 +1,218 @@ +package de.ozgcloud.alfa.export; + +import static de.ozgcloud.alfa.export.XdomeaPropertiesTestFactory.*; +import static org.assertj.core.api.Assertions.*; +import static org.mockito.Mockito.*; + +import java.util.Optional; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.mockito.Spy; +import org.springframework.validation.Errors; +import org.springframework.validation.SimpleErrors; + +class XdomeaBehoerdenschluesselPropertiesValidatorTest { + + private final XdomeaProperties properties = XdomeaPropertiesTestFactory.create(); + private final Errors errors = new SimpleErrors(properties); + @Mock + private XdomeaBehoerdenschluesselProperties behoerdenschluesselProperties; + @Spy + private XdomeaBehoerdenschluesselPropertiesValidator validator; + + @Nested + class TestSupports { + + @Test + void shouldSupportXdomeaProperties() { + var supports = validator.supports(XdomeaProperties.class); + + assertThat(supports).isTrue(); + } + } + + @Nested + class TestValidate { + + @BeforeEach + void init() { + doReturn(behoerdenschluesselProperties).when(validator).getBehoerdenschluesselProperties(properties); + } + + @Test + void shouldGetBehoerdenschluesselProperties() { + validate(); + + verify(validator).getBehoerdenschluesselProperties(properties); + } + + @Test + void shouldBeValidIfAllBehoerdenschluesselPropertiesAreSet() { + givenProperties(BEHOERDENSCHLUESSEL, BEHOERDENSCHLUESSEL_URI, BEHOERDENSCHLUESSEL_VERSION); + + validate(); + + assertThat(errors.hasErrors()).isFalse(); + } + + @Test + void shouldBeValidIfBehoerdenschluesselIsNotSet() { + when(behoerdenschluesselProperties.getBehoerdenschluessel()).thenReturn(Optional.empty()); + + validate(); + + assertThat(errors.hasErrors()).isFalse(); + } + + @Nested + class OnUriNotSet { + + private static final String FIELD = "behoerdenschluesselUri"; + public static final String PROPERTY_NAME = "behoerdenschluessel-uri"; + public static final String PROPERTY_PATH = "ozgcloud.xdomea." + PROPERTY_NAME; + public static final String ERROR_CODE = PROPERTY_PATH + ".empty"; + + @BeforeEach + void init() { + givenUriNotSet(); + } + + @Test + void shouldHaveFieldErrors() { + validate(); + + assertThat(errors.hasFieldErrors(FIELD)).isTrue(); + } + + @Test + void shouldHaveMeaningfulErrorMessage() { + validate(); + + assertThat(errors.getFieldError(FIELD).getDefaultMessage()).contains(PROPERTY_NAME); + } + + @Test + void shouldHaveErrorCode() { + validate(); + + assertThat(errors.getFieldError(FIELD).getCode()).isEqualTo(ERROR_CODE); + } + + private void givenUriNotSet() { + givenProperties(BEHOERDENSCHLUESSEL, null, BEHOERDENSCHLUESSEL_VERSION); + } + } + + @Nested + class OnVersionNotSet { + + private static final String FIELD = "behoerdenschluesselVersion"; + public static final String PROPERTY_NAME = "behoerdenschluessel-version"; + public static final String PROPERTY_PATH = "ozgcloud.xdomea." + PROPERTY_NAME; + public static final String ERROR_CODE = PROPERTY_PATH + ".empty"; + + @BeforeEach + void init() { + givenVersionNotSet(); + } + + @Test + void shouldHaveFieldErrors() { + validate(); + + assertThat(errors.hasFieldErrors(FIELD)).isTrue(); + } + + @Test + void shouldHaveMeaningfulErrorMessage() { + validate(); + + assertThat(errors.getFieldError(FIELD).getDefaultMessage()).contains(PROPERTY_NAME); + } + + @Test + void shouldHaveErrorCode() { + validate(); + + assertThat(errors.getFieldError(FIELD).getCode()).isEqualTo(ERROR_CODE); + } + + private void givenVersionNotSet() { + givenProperties(BEHOERDENSCHLUESSEL, BEHOERDENSCHLUESSEL_URI, null); + } + } + + @Nested + class OnAmbiguousCodelistePattern { + + public static final String PROPERTY_PATH = "ozgcloud.xdomea.codeliste.pattern"; + public static final String ERROR_CODE = PROPERTY_PATH + ".ambiguous"; + + private final AmbiguousCodelistePatternException exception = new AmbiguousCodelistePatternException(BEHOERDENSCHLUESSEL); + + @BeforeEach + void init() { + givenAmbiguousCodelistePattern(); + } + + @Test + void shouldHaveGlobalError() { + validate(); + + assertThat(errors.hasGlobalErrors()).isTrue(); + } + + @Test + void shouldHaveErrorMessageOfException() { + validate(); + + assertThat(errors.getGlobalError().getDefaultMessage()).isEqualTo(exception.getMessage()); + } + + @Test + void shouldHaveErrorCode() { + validate(); + + assertThat(errors.getGlobalError().getCode()).isEqualTo(ERROR_CODE); + } + + private void givenAmbiguousCodelistePattern() { + when(behoerdenschluesselProperties.getBehoerdenschluessel()).thenReturn(Optional.of(BEHOERDENSCHLUESSEL)); + when(behoerdenschluesselProperties.getBehoerdenschluesselUri()).thenThrow(exception); + } + } + + private void givenProperties(String bs, String bsUri, String bsVersion) { + when(behoerdenschluesselProperties.getBehoerdenschluessel()).thenReturn(Optional.ofNullable(bs)); + when(behoerdenschluesselProperties.getBehoerdenschluesselUri()).thenReturn(Optional.ofNullable(bsUri)); + when(behoerdenschluesselProperties.getBehoerdenschluesselVersion()).thenReturn(Optional.ofNullable(bsVersion)); + } + + private void validate() { + validator.validate(properties, errors); + } + } + + @Nested + class TestGetBehoerdenschluesselProperties { + + @Test + void shouldCreateBehoerdenschluesselProperties() { + try (var mockBehoerdenschluesselProperties = mockConstruction(XdomeaBehoerdenschluesselProperties.class)) { + validator.getBehoerdenschluesselProperties(XdomeaPropertiesTestFactory.create()); + + assertThat(mockBehoerdenschluesselProperties.constructed()).hasSize(1); + } + } + + @Test + void shouldReturnBehoerdenschluesselProperties() { + var behoerdenschluesselProperties = validator.getBehoerdenschluesselProperties(XdomeaPropertiesTestFactory.create()); + + assertThat(behoerdenschluesselProperties).isNotNull(); + } + } +} diff --git a/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/XdomeaPropertiesTestFactory.java b/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/XdomeaPropertiesTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..e40672a75490af785b3afcd03ddc1089599baead --- /dev/null +++ b/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/XdomeaPropertiesTestFactory.java @@ -0,0 +1,40 @@ +package de.ozgcloud.alfa.export; + +import java.util.List; +import java.util.regex.Pattern; + +import com.thedeanda.lorem.LoremIpsum; + +import de.ozgcloud.alfa.export.XdomeaProperties.Codeliste; +import de.ozgcloud.alfa.export.XdomeaProperties.XdomeaPropertiesBuilder; + +public class XdomeaPropertiesTestFactory { + + public static final String BEHOERDENSCHLUESSEL = LoremIpsum.getInstance().getWords(1); + public static final String BEHOERDENSCHLUESSEL_URI = LoremIpsum.getInstance().getUrl(); + public static final String BEHOERDENSCHLUESSEL_VERSION = LoremIpsum.getInstance().getWords(1); + + public static final String CODELISTE_1_URI = LoremIpsum.getInstance().getUrl(); + public static final String CODELISTE_1_VERSION = LoremIpsum.getInstance().getWords(1); + public static final Pattern CODELISTE_1_PATTERN = Pattern.compile("\\d{3}"); + public static final String CODELISTE_1_VALID_BEHOERDENSCHLUESSEL = "123"; + public static final Codeliste CODELISTE_1 = Codeliste.builder().uri(CODELISTE_1_URI).version(CODELISTE_1_VERSION).pattern(CODELISTE_1_PATTERN).build(); + + public static final String CODELISTE_2_URI = LoremIpsum.getInstance().getUrl(); + public static final String CODELISTE_2_VERSION = LoremIpsum.getInstance().getWords(1); + public static final Pattern CODELISTE_2_PATTERN = Pattern.compile("\\d{7}"); + public static final String CODELISTE_2_VALID_BEHOERDENSCHLUESSEL = "1234567"; + public static final Codeliste CODELISTE_2 = Codeliste.builder().uri(CODELISTE_2_URI).version(CODELISTE_2_VERSION).pattern(CODELISTE_2_PATTERN).build(); + + public static XdomeaProperties create() { + return createBuilder().build(); + } + + public static XdomeaPropertiesBuilder createBuilder() { + return new XdomeaPropertiesBuilder() + .behoerdenschluessel(BEHOERDENSCHLUESSEL) + .behoerdenschluesselUri(BEHOERDENSCHLUESSEL_URI) + .behoerdenschluesselVersion(BEHOERDENSCHLUESSEL_VERSION) + .codeliste(List.of(CODELISTE_1, CODELISTE_2)); + } +} diff --git a/alfa-xdomea/src/test/java/de/ozgcloud/alfa/vorgang/KopfCreatorTest.java b/alfa-xdomea/src/test/java/de/ozgcloud/alfa/vorgang/KopfCreatorTest.java index 0c72b5ad9ff5d21840af73074a8f244c3899b0ab..8b05a571c79104bd212678186561026c040e1c5c 100644 --- a/alfa-xdomea/src/test/java/de/ozgcloud/alfa/vorgang/KopfCreatorTest.java +++ b/alfa-xdomea/src/test/java/de/ozgcloud/alfa/vorgang/KopfCreatorTest.java @@ -6,6 +6,8 @@ import static org.mockito.Mockito.*; import java.time.ZoneOffset; import java.time.ZonedDateTime; +import java.util.Optional; +import java.util.UUID; import javax.xml.datatype.XMLGregorianCalendar; @@ -13,7 +15,6 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; -import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.MockedStatic; import org.mockito.Spy; @@ -22,7 +23,9 @@ import com.thedeanda.lorem.LoremIpsum; import de.ozgcloud.alfa.common.DateConverter; import de.ozgcloud.alfa.common.UUIDConverter; +import de.ozgcloud.alfa.export.XdomeaBehoerdenschluesselProperties; import de.ozgcloud.alfa.export.XdomeaProperties; +import de.ozgcloud.alfa.export.XdomeaPropertiesTestFactory; import de.xoev.xdomea.BehoerdenkennungType; import de.xoev.xdomea.Code; import de.xoev.xdomea.KontaktType; @@ -33,12 +36,9 @@ import de.xoev.xdomea.SystemType; class KopfCreatorTest { + private final XdomeaProperties xDomeaProperties = XdomeaPropertiesTestFactory.create(); @Spy - @InjectMocks - private KopfCreator creator; - - @Mock - private XdomeaProperties xDomeaProperties; + private KopfCreator creator = new KopfCreator(xDomeaProperties); @Mock private DateConverter dateConverter; @@ -194,15 +194,97 @@ class KopfCreatorTest { @Nested class TestCreateKontaktType { + @Mock + private XdomeaBehoerdenschluesselProperties behoerdenschluesselProperties; + + @BeforeEach + void init() { + doReturn(behoerdenschluesselProperties).when(creator).getBehoerdenschluesselProperties(xDomeaProperties); + } + + @Test + void shouldGetBehoerdenschluesselProperties() { + creator.createKontaktType(); + + verify(creator).getBehoerdenschluesselProperties(xDomeaProperties); + } + + @Test + void shouldGetBehoerdenschluessel() { + creator.createKontaktType(); + + verify(behoerdenschluesselProperties).getBehoerdenschluessel(); + } + + @Nested + class OnBehoerdenschluesselNotSet { + + @BeforeEach + void init() { + when(behoerdenschluesselProperties.getBehoerdenschluessel()).thenReturn(Optional.empty()); + } + + @Test + void shouldNotCreateBehoerdenkennung() { + creator.createKontaktType(); + + verify(creator, never()).createBehoerdenkennung(behoerdenschluesselProperties); + } + + @Test + void shouldNotSetBehoerdenkennung() { + var kontaktType = creator.createKontaktType(); + + assertThat(kontaktType.getBehoerdenkennung()).isNull(); + } + } + + @Nested + class OnBehoerdenschluesselSet { + + private static final String BEHOERDENSCHLUESSEL = UUID.randomUUID().toString(); + + private static final BehoerdenkennungType expectedValue = new BehoerdenkennungType(); + + @BeforeEach + void init() { + when(behoerdenschluesselProperties.getBehoerdenschluessel()).thenReturn(Optional.of(BEHOERDENSCHLUESSEL)); + doReturn(expectedValue).when(creator).createBehoerdenkennung(behoerdenschluesselProperties); + } + + @Test + void shouldCreateBehoerdenkennung() { + creator.createKontaktType(); + + verify(creator).createBehoerdenkennung(behoerdenschluesselProperties); + } + + @Test + void shouldSetBehoerdenkennung() { + var kontaktType = creator.createKontaktType(); + + assertThat(kontaktType.getBehoerdenkennung()).isEqualTo(expectedValue); + } + } + } + + @Nested + class TestGetBehoerdenschluesselProperties { + @Test - void shouldSetBehoerdenkennungType() { - var expectedValue = new BehoerdenkennungType(); - doReturn(expectedValue).when(creator).createBehoerdenkennung(); + void shouldCreateBehoerdenschluesselProperties() { + try (var mockBehoerdenschluesselProperties = mockConstruction(XdomeaBehoerdenschluesselProperties.class)) { + creator.getBehoerdenschluesselProperties(XdomeaPropertiesTestFactory.create()); - var kontaktType = creator.createAbsender(ZustaendigeStelleTestFactory.ORGANISATIONSEINHEITEN_ID); + assertThat(mockBehoerdenschluesselProperties.constructed()).hasSize(1); + } + } - assertThat(kontaktType.getBehoerdenkennung()).isEqualTo(expectedValue); + @Test + void shouldReturnBehoerdenschluesselProperties() { + var behoerdenschluesselProperties = creator.getBehoerdenschluesselProperties(XdomeaPropertiesTestFactory.create()); + assertThat(behoerdenschluesselProperties).isNotNull(); } } @@ -220,12 +302,25 @@ class KopfCreatorTest { @Nested class TestCreateBehoerdenkennung { + private final Code expectedBehoerdenschluessel = new Code(); + @Mock + private XdomeaBehoerdenschluesselProperties behoerdenschluesselProperties; + + @BeforeEach + void init() { + doReturn(expectedBehoerdenschluessel).when(creator).createBehoerdenschluessel(behoerdenschluesselProperties); + } + @Test - void shouldSetBehoerdenschluessel() { - var expectedBehoerdenschluessel = new Code(); - doReturn(expectedBehoerdenschluessel).when(creator).createBehoerdenschluessel(); + void shouldcreateBehoerdenschluessel() { + creator.createBehoerdenkennung(behoerdenschluesselProperties); + + verify(creator).createBehoerdenschluessel(behoerdenschluesselProperties); + } - var behoerdenkennungType = creator.createBehoerdenkennung(); + @Test + void shouldSetBehoerdenschluessel() { + var behoerdenkennungType = creator.createBehoerdenkennung(behoerdenschluesselProperties); assertThat(behoerdenkennungType.getBehoerdenschluessel()).isEqualTo(expectedBehoerdenschluessel); } @@ -234,34 +329,39 @@ class KopfCreatorTest { @Nested class TestCreateBehoerdenschlussel { + private static final String BEHOERDENSCHLUESSEL = LoremIpsum.getInstance().getWords(1); + private static final String BEHOERDENSCHLUESSEL_URI = LoremIpsum.getInstance().getUrl(); + private static final String BEHOERDENSCHLUESSEL_VERSION = LoremIpsum.getInstance().getWords(1); + + @Mock + private XdomeaBehoerdenschluesselProperties behoerdenschluesselProperties; + + @BeforeEach + void init() { + when(behoerdenschluesselProperties.getBehoerdenschluessel()).thenReturn(Optional.of(BEHOERDENSCHLUESSEL)); + when(behoerdenschluesselProperties.getBehoerdenschluesselUri()).thenReturn(Optional.of(BEHOERDENSCHLUESSEL_URI)); + when(behoerdenschluesselProperties.getBehoerdenschluesselVersion()).thenReturn(Optional.of(BEHOERDENSCHLUESSEL_VERSION)); + } + @Test void shouldSetCode() { - var expectedBehoerdenschluessel = LoremIpsum.getInstance().getWords(1); - when(xDomeaProperties.getBehoerdenschluessel()).thenReturn(expectedBehoerdenschluessel); + var behoerdenschlussel = creator.createBehoerdenschluessel(behoerdenschluesselProperties); - var behoerdenschlussel = creator.createBehoerdenschluessel(); - - assertThat(behoerdenschlussel.getCode()).isEqualTo(expectedBehoerdenschluessel); + assertThat(behoerdenschlussel.getCode()).isEqualTo(BEHOERDENSCHLUESSEL); } @Test void shouldSetListURI() { - var expectedBehoerdenschluesselUri = LoremIpsum.getInstance().getUrl(); - when(xDomeaProperties.getBehoerdenschluesselUri()).thenReturn(expectedBehoerdenschluesselUri); - - var behoerdenschlussel = creator.createBehoerdenschluessel(); + var behoerdenschlussel = creator.createBehoerdenschluessel(behoerdenschluesselProperties); - assertThat(behoerdenschlussel.getListURI()).isEqualTo(expectedBehoerdenschluesselUri); + assertThat(behoerdenschlussel.getListURI()).isEqualTo(BEHOERDENSCHLUESSEL_URI); } @Test void shouldSetListVersionID() { - var expectedBehoerdenschluesselVersion = LoremIpsum.getInstance().getWords(1); - when(xDomeaProperties.getBehoerdenschluesselVersion()).thenReturn(expectedBehoerdenschluesselVersion); - - var behoerdenschlussel = creator.createBehoerdenschluessel(); + var behoerdenschlussel = creator.createBehoerdenschluessel(behoerdenschluesselProperties); - assertThat(behoerdenschlussel.getListVersionID()).isEqualTo(expectedBehoerdenschluesselVersion); + assertThat(behoerdenschlussel.getListVersionID()).isEqualTo(BEHOERDENSCHLUESSEL_VERSION); } }