diff --git a/stack/keycloak/migration/add-authorities-monitoring-waehleranzahl.yml b/stack/keycloak/migration/add-authorities-monitoring-waehleranzahl.yml new file mode 100644 index 000000000..92a9454a7 --- /dev/null +++ b/stack/keycloak/migration/add-authorities-monitoring-waehleranzahl.yml @@ -0,0 +1,46 @@ +id: add authorities monitoring waehleranzahl +author: dragonfly28 +realm: ${SSO_REALM} +changes: + - addRole: + name: Monitoring_BUSINESSACTION_GetWahlbeteiligung + clientRole: true + clientId: ${SSO_CLIENT_ID} + - assignRoleToGroup: + group: allMonitoringAuthorities + role: Monitoring_BUSINESSACTION_GetWahlbeteiligung + clientId: ${SSO_CLIENT_ID} + + - addRole: + name: Monitoring_BUSINESSACTION_PostWahlbeteiligung + clientRole: true + clientId: ${SSO_CLIENT_ID} + - assignRoleToGroup: + group: allMonitoringAuthorities + role: Monitoring_BUSINESSACTION_PostWahlbeteiligung + clientId: ${SSO_CLIENT_ID} + + - addRole: + name: Monitoring_READ_Waehleranzahl + clientRole: true + clientId: ${SSO_CLIENT_ID} + - assignRoleToGroup: + group: allMonitoringAuthorities + role: Monitoring_READ_Waehleranzahl + clientId: ${SSO_CLIENT_ID} + - addRole: + name: Monitoring_WRITE_Waehleranzahl + clientRole: true + clientId: ${SSO_CLIENT_ID} + - assignRoleToGroup: + group: allMonitoringAuthorities + role: Monitoring_WRITE_Waehleranzahl + clientId: ${SSO_CLIENT_ID} + - addRole: + name: Monitoring_DELETE_Waehleranzahl + clientRole: true + clientId: ${SSO_CLIENT_ID} + - assignRoleToGroup: + group: allMonitoringAuthorities + role: Monitoring_DELETE_Waehleranzahl + clientId: ${SSO_CLIENT_ID} \ No newline at end of file diff --git a/stack/keycloak/migration/create-group-all-monitoring-authorities.yml b/stack/keycloak/migration/create-group-all-monitoring-authorities.yml new file mode 100644 index 000000000..5b06ee5dd --- /dev/null +++ b/stack/keycloak/migration/create-group-all-monitoring-authorities.yml @@ -0,0 +1,15 @@ +id: create group allMonitoringAuthorities and link wls_all* +author: dragonfly28 +realm: ${SSO_REALM} +changes: + - addGroup: + name: allMonitoringAuthorities + - assignGroup: + user: wls_all + group: allMonitoringAuthorities + - assignGroup: + user: wls_all_uwb + group: allMonitoringAuthorities + - assignGroup: + user: wls_all_bwb + group: allMonitoringAuthorities \ No newline at end of file diff --git a/stack/keycloak/migration/keycloak-changelog.yml b/stack/keycloak/migration/keycloak-changelog.yml index 27ee19839..2da4cc61f 100644 --- a/stack/keycloak/migration/keycloak-changelog.yml +++ b/stack/keycloak/migration/keycloak-changelog.yml @@ -44,3 +44,5 @@ includes: - path: add-authorities-vorfaelleundvorkommnisse.yml - path: add-authorities-eai-ergebnismeldung.yml - path: add-authorities-basisdaten-wahltermindaten.yml + - path: create-group-all-monitoring-authorities.yml + - path: add-authorities-monitoring-waehleranzahl.yml \ No newline at end of file diff --git a/wls-monitoring-service/pom.xml b/wls-monitoring-service/pom.xml index f3a04c4ee..99381920b 100644 --- a/wls-monitoring-service/pom.xml +++ b/wls-monitoring-service/pom.xml @@ -36,7 +36,9 @@ 1.18.30 0.2.0 + 1.6.2 2.6.0 + 1.1.0 @@ -77,6 +79,13 @@ spring-boot-starter-actuator + + + de.muenchen.oss.wahllokalsystem.wls-common + exception + 1.1.0 + + org.springframework.boot @@ -165,6 +174,18 @@ springdoc-openapi-starter-webmvc-ui ${org.springdoc.version} + + org.openapitools + jackson-databind-nullable + 0.2.6 + + + + + de.muenchen.oss.wahllokalsystem.wls-common + security + ${wls.common.version} + @@ -187,6 +208,11 @@ spring-security-test test + + org.springframework.cloud + spring-cloud-starter-contract-stub-runner + test + org.junit.jupiter junit-jupiter-api @@ -197,6 +223,12 @@ junit-jupiter-engine test + + de.muenchen.oss.wahllokalsystem.wls-common + testing + ${wls.common.version} + test + @@ -328,14 +360,67 @@ lombok ${org.projectlombok.lombok.version} + + org.mapstruct + mapstruct-processor + ${org.mapstruct.version} + org.projectlombok lombok-mapstruct-binding ${org.projectlombok.mapstructbinding.version} + + + -Amapstruct.defaultComponentModel=spring + + + -Amapstruct.unmappedTargetPolicy=ERROR + + + + org.openapitools + openapi-generator-maven-plugin + 7.7.0 + + + generateEAI + + generate + + + ${project.basedir}/src/main/resources/openapis/openapi.eai.0.1.0.json + + java + resttemplate + + ${project.groupId}.monitoringservice.eai.aou.client + ${project.groupId}.monitoringservice.eai.aou.model + + false + false + false + false + + + false + false + true + true + + + + + BaseApi.java,ApiClient.java,JavaTimeFormatter.java,Authentication.java,OAuth.java,ApiKeyAuth.java,HttpBasicAuth.java,HttpBearerAuth.java,RFC3339DateFormat.java + + + + + + maven-scm-plugin ${maven-scm-plugin.version} @@ -348,7 +433,7 @@ maven-surefire-plugin ${maven-surefire-plugin.version} - ${surefireArgLine} -Dfile.encoding=${project.build.sourceEncoding} + ${surefireArgLine} -Dfile.encoding=${project.build.sourceEncoding} @@ -368,10 +453,10 @@ prepare-agent - true - ${sonar.jacoco.reportPath} - - surefireArgLine + true + ${sonar.jacoco.reportPath} + + surefireArgLine diff --git a/wls-monitoring-service/src/main/java/de/muenchen/oss/wahllokalsystem/monitoringservice/MicroServiceApplication.java b/wls-monitoring-service/src/main/java/de/muenchen/oss/wahllokalsystem/monitoringservice/MicroServiceApplication.java index fc0ca1aa2..4f197abc7 100644 --- a/wls-monitoring-service/src/main/java/de/muenchen/oss/wahllokalsystem/monitoringservice/MicroServiceApplication.java +++ b/wls-monitoring-service/src/main/java/de/muenchen/oss/wahllokalsystem/monitoringservice/MicroServiceApplication.java @@ -18,7 +18,8 @@ @ComponentScan( basePackages = { "org.springframework.data.jpa.convert.threeten", - "de.muenchen.oss.wahllokalsystem.monitoringservice" + "de.muenchen.oss.wahllokalsystem.monitoringservice", + "de.muenchen.oss.wahllokalsystem.wls.common.exception", } ) @EntityScan( diff --git a/wls-monitoring-service/src/main/java/de/muenchen/oss/wahllokalsystem/monitoringservice/client/waehleranzahl/DummyClientImpl.java b/wls-monitoring-service/src/main/java/de/muenchen/oss/wahllokalsystem/monitoringservice/client/waehleranzahl/DummyClientImpl.java new file mode 100644 index 000000000..4f7e6212b --- /dev/null +++ b/wls-monitoring-service/src/main/java/de/muenchen/oss/wahllokalsystem/monitoringservice/client/waehleranzahl/DummyClientImpl.java @@ -0,0 +1,22 @@ +package de.muenchen.oss.wahllokalsystem.monitoringservice.client.waehleranzahl; + +import de.muenchen.oss.wahllokalsystem.monitoringservice.configuration.Profiles; +import de.muenchen.oss.wahllokalsystem.monitoringservice.service.waehleranzahl.WaehleranzahlClient; +import de.muenchen.oss.wahllokalsystem.monitoringservice.service.waehleranzahl.WaehleranzahlModel; +import de.muenchen.oss.wahllokalsystem.wls.common.exception.WlsException; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.annotation.Profile; +import org.springframework.stereotype.Component; + +@Component +@Profile(Profiles.DUMMY_CLIENTS) +@RequiredArgsConstructor +@Slf4j +public class DummyClientImpl implements WaehleranzahlClient { + + @Override + public void postWahlbeteiligung(WaehleranzahlModel waehleranzahlModel) throws WlsException { + log.info("Dummy client postWahlbeteiligung() called instead of EAI with: {}", waehleranzahlModel); + } +} diff --git a/wls-monitoring-service/src/main/java/de/muenchen/oss/wahllokalsystem/monitoringservice/client/waehleranzahl/WaehleranzahlClientImpl.java b/wls-monitoring-service/src/main/java/de/muenchen/oss/wahllokalsystem/monitoringservice/client/waehleranzahl/WaehleranzahlClientImpl.java new file mode 100644 index 000000000..fa079f830 --- /dev/null +++ b/wls-monitoring-service/src/main/java/de/muenchen/oss/wahllokalsystem/monitoringservice/client/waehleranzahl/WaehleranzahlClientImpl.java @@ -0,0 +1,32 @@ +package de.muenchen.oss.wahllokalsystem.monitoringservice.client.waehleranzahl; + +import de.muenchen.oss.wahllokalsystem.monitoringservice.configuration.Profiles; +import de.muenchen.oss.wahllokalsystem.monitoringservice.eai.aou.client.WahlbeteiligungControllerApi; +import de.muenchen.oss.wahllokalsystem.monitoringservice.service.waehleranzahl.WaehleranzahlClient; +import de.muenchen.oss.wahllokalsystem.monitoringservice.service.waehleranzahl.WaehleranzahlModel; +import de.muenchen.oss.wahllokalsystem.wls.common.exception.WlsException; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import lombok.val; +import org.springframework.context.annotation.Profile; +import org.springframework.stereotype.Component; + +@Component +@Profile(Profiles.NOT + Profiles.DUMMY_CLIENTS) +@RequiredArgsConstructor +@Slf4j +public class WaehleranzahlClientImpl implements WaehleranzahlClient { + + private final WahlbeteiligungControllerApi wahlbeteiligungControllerApi; + private final WaehleranzahlClientMapper waehleranzahlClientMapper; + + @Override + public void postWahlbeteiligung(final WaehleranzahlModel waehleranzahlModel) throws WlsException { + val wahlbeteiligungsMeldungDTO = waehleranzahlClientMapper.fromModelToRemoteClientDTO(waehleranzahlModel); + try { + wahlbeteiligungControllerApi.saveWahlbeteiligung(wahlbeteiligungsMeldungDTO); + } catch (final Exception exception) { + log.error("Wahllokalzustand nicht gesendet. Exception: ", exception); + } + } +} diff --git a/wls-monitoring-service/src/main/java/de/muenchen/oss/wahllokalsystem/monitoringservice/client/waehleranzahl/WaehleranzahlClientMapper.java b/wls-monitoring-service/src/main/java/de/muenchen/oss/wahllokalsystem/monitoringservice/client/waehleranzahl/WaehleranzahlClientMapper.java new file mode 100644 index 000000000..3c798e2a9 --- /dev/null +++ b/wls-monitoring-service/src/main/java/de/muenchen/oss/wahllokalsystem/monitoringservice/client/waehleranzahl/WaehleranzahlClientMapper.java @@ -0,0 +1,30 @@ +package de.muenchen.oss.wahllokalsystem.monitoringservice.client.waehleranzahl; + +import de.muenchen.oss.wahllokalsystem.monitoringservice.eai.aou.model.WahlbeteiligungsMeldungDTO; +import de.muenchen.oss.wahllokalsystem.monitoringservice.service.waehleranzahl.WaehleranzahlModel; +import java.time.LocalDateTime; +import java.time.OffsetDateTime; +import java.time.ZoneId; +import lombok.val; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; + +@Mapper +public interface WaehleranzahlClientMapper { + + ZoneId DEFAULT_ZONE_ID = ZoneId.systemDefault(); + + @Mapping(target = "wahlID", ignore = true) + @Mapping(target = "wahlbezirkID", source = "bezirkUndWahlID.wahlbezirkID") + @Mapping(target = "meldeZeitpunkt", source = "uhrzeit") + WahlbeteiligungsMeldungDTO fromModelToRemoteClientDTO(WaehleranzahlModel waehleranzahlModel); + + default OffsetDateTime localDateTimeToOffsetDateTime(LocalDateTime localDateTime) { + return localDateTimeToOffsetDateTime(localDateTime, DEFAULT_ZONE_ID); + } + + default OffsetDateTime localDateTimeToOffsetDateTime(LocalDateTime localDateTime, ZoneId zoneId) { + val zoneOffset = zoneId.getRules().getOffset(localDateTime); + return localDateTime.atOffset(zoneOffset); + } +} diff --git a/wls-monitoring-service/src/main/java/de/muenchen/oss/wahllokalsystem/monitoringservice/configuration/BasePathConfiguration.java b/wls-monitoring-service/src/main/java/de/muenchen/oss/wahllokalsystem/monitoringservice/configuration/BasePathConfiguration.java new file mode 100644 index 000000000..6af3d48cf --- /dev/null +++ b/wls-monitoring-service/src/main/java/de/muenchen/oss/wahllokalsystem/monitoringservice/configuration/BasePathConfiguration.java @@ -0,0 +1,22 @@ +package de.muenchen.oss.wahllokalsystem.monitoringservice.configuration; + +import de.muenchen.oss.wahllokalsystem.monitoringservice.eai.aou.ApiClient; +import jakarta.annotation.PostConstruct; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; + +@Configuration +@RequiredArgsConstructor +public class BasePathConfiguration { + + @Value("${app.clients.eai.basePath}") + String eaiBasePath; + + private final ApiClient eaiApiClient; + + @PostConstruct + public void updateBasePaths() { + eaiApiClient.setBasePath(eaiBasePath); + } +} diff --git a/wls-monitoring-service/src/main/java/de/muenchen/oss/wahllokalsystem/monitoringservice/configuration/ClientConfiguration.java b/wls-monitoring-service/src/main/java/de/muenchen/oss/wahllokalsystem/monitoringservice/configuration/ClientConfiguration.java new file mode 100644 index 000000000..36762b320 --- /dev/null +++ b/wls-monitoring-service/src/main/java/de/muenchen/oss/wahllokalsystem/monitoringservice/configuration/ClientConfiguration.java @@ -0,0 +1,42 @@ +package de.muenchen.oss.wahllokalsystem.monitoringservice.configuration; + +import com.fasterxml.jackson.databind.ObjectMapper; +import de.muenchen.oss.wahllokalsystem.wls.common.exception.errorhandler.WlsResponseErrorHandler; +import lombok.val; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.oauth2.core.AbstractOAuth2Token; +import org.springframework.web.client.RestTemplate; + +@Configuration +public class ClientConfiguration { + + @Bean + public WlsResponseErrorHandler wlsResponseErrorHandler(final ObjectMapper objectMapper) { + return new WlsResponseErrorHandler(objectMapper); + } + + @Bean + public RestTemplate restTemplate(final WlsResponseErrorHandler wlsResponseErrorHandler) { + val restTemplate = new RestTemplate(); + + restTemplate.setErrorHandler(wlsResponseErrorHandler); + restTemplate.getInterceptors().add((request, body, execution) -> { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + if (authentication == null) { + return execution.execute(request, body); + } + + if (!(authentication.getCredentials() instanceof AbstractOAuth2Token token)) { + return execution.execute(request, body); + } + + request.getHeaders().setBearerAuth(token.getTokenValue()); + return execution.execute(request, body); + }); + + return restTemplate; + } +} diff --git a/wls-monitoring-service/src/main/java/de/muenchen/oss/wahllokalsystem/monitoringservice/configuration/Profiles.java b/wls-monitoring-service/src/main/java/de/muenchen/oss/wahllokalsystem/monitoringservice/configuration/Profiles.java new file mode 100644 index 000000000..88d27a22e --- /dev/null +++ b/wls-monitoring-service/src/main/java/de/muenchen/oss/wahllokalsystem/monitoringservice/configuration/Profiles.java @@ -0,0 +1,12 @@ +package de.muenchen.oss.wahllokalsystem.monitoringservice.configuration; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class Profiles { + + public static final String NOT = "!"; + + public static final String DUMMY_CLIENTS = "dummy.clients"; +} diff --git a/wls-monitoring-service/src/main/java/de/muenchen/oss/wahllokalsystem/monitoringservice/domain/BaseEntity.java b/wls-monitoring-service/src/main/java/de/muenchen/oss/wahllokalsystem/monitoringservice/domain/BaseEntity.java deleted file mode 100644 index 96233c6aa..000000000 --- a/wls-monitoring-service/src/main/java/de/muenchen/oss/wahllokalsystem/monitoringservice/domain/BaseEntity.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c): it@M - Dienstleister für Informations- und Telekommunikationstechnik - * der Landeshauptstadt München, 2024 - */ -package de.muenchen.oss.wahllokalsystem.monitoringservice.domain; - -import static java.sql.Types.VARCHAR; - -import jakarta.persistence.Column; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.Id; -import jakarta.persistence.MappedSuperclass; -import java.io.Serializable; -import java.util.UUID; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; -import lombok.ToString; -import org.hibernate.annotations.GenericGenerator; -import org.hibernate.annotations.JdbcTypeCode; - -@MappedSuperclass -@NoArgsConstructor -@Getter -@Setter -@ToString -@EqualsAndHashCode -public abstract class BaseEntity implements Cloneable, Serializable { - - private static final long serialVersionUID = 1L; - - @Column(name = "id", length = 36) - @Id - @GeneratedValue(generator = "uuid") - @GenericGenerator(name = "uuid", strategy = "uuid2") - @JdbcTypeCode(VARCHAR) - private UUID id; - -} diff --git a/wls-monitoring-service/src/main/java/de/muenchen/oss/wahllokalsystem/monitoringservice/domain/TheEntity.java b/wls-monitoring-service/src/main/java/de/muenchen/oss/wahllokalsystem/monitoringservice/domain/TheEntity.java deleted file mode 100644 index 5027146b5..000000000 --- a/wls-monitoring-service/src/main/java/de/muenchen/oss/wahllokalsystem/monitoringservice/domain/TheEntity.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c): it@M - Dienstleister für Informations- und Telekommunikationstechnik - * der Landeshauptstadt München, 2024 - */ -package de.muenchen.oss.wahllokalsystem.monitoringservice.domain; - -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.validation.constraints.NotNull; -import jakarta.validation.constraints.Size; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; -import lombok.ToString; - -/** - * This class represents a TheEntity. - *

- * The entity's content will be loaded according to the reference variable. - *

- */ -@Entity -// Definition of getter, setter, ... -@Getter -@Setter -@ToString(callSuper = true) -@EqualsAndHashCode(callSuper = true) -@NoArgsConstructor -public class TheEntity extends BaseEntity { - - private static final long serialVersionUID = 1L; - - // ========= // - // Variables // - // ========= // - - @Column(name = "textattribute", nullable = false, length = 8) - @NotNull - @Size(min = 2, max = 8) - private String textAttribute; - -} diff --git a/wls-monitoring-service/src/main/java/de/muenchen/oss/wahllokalsystem/monitoringservice/domain/waehleranzahl/Waehleranzahl.java b/wls-monitoring-service/src/main/java/de/muenchen/oss/wahllokalsystem/monitoringservice/domain/waehleranzahl/Waehleranzahl.java new file mode 100644 index 000000000..f7c3b8cf6 --- /dev/null +++ b/wls-monitoring-service/src/main/java/de/muenchen/oss/wahllokalsystem/monitoringservice/domain/waehleranzahl/Waehleranzahl.java @@ -0,0 +1,34 @@ +package de.muenchen.oss.wahllokalsystem.monitoringservice.domain.waehleranzahl; + +import de.muenchen.oss.wahllokalsystem.wls.common.security.domain.BezirkUndWahlID; +import jakarta.persistence.EmbeddedId; +import jakarta.persistence.Entity; +import jakarta.validation.constraints.NotNull; +import java.time.LocalDateTime; +import lombok.AllArgsConstructor; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; + +@Entity +@Getter +@Setter +@ToString +@EqualsAndHashCode +@NoArgsConstructor +@AllArgsConstructor +public class Waehleranzahl { + + @EmbeddedId + @NotNull + private BezirkUndWahlID bezirkUndWahlID; + + @NotNull + private long anzahlWaehler; + + @NotNull + private LocalDateTime uhrzeit; + +} diff --git a/wls-monitoring-service/src/main/java/de/muenchen/oss/wahllokalsystem/monitoringservice/domain/waehleranzahl/WaehleranzahlRepository.java b/wls-monitoring-service/src/main/java/de/muenchen/oss/wahllokalsystem/monitoringservice/domain/waehleranzahl/WaehleranzahlRepository.java new file mode 100644 index 000000000..c6da41725 --- /dev/null +++ b/wls-monitoring-service/src/main/java/de/muenchen/oss/wahllokalsystem/monitoringservice/domain/waehleranzahl/WaehleranzahlRepository.java @@ -0,0 +1,47 @@ +package de.muenchen.oss.wahllokalsystem.monitoringservice.domain.waehleranzahl; + +import de.muenchen.oss.wahllokalsystem.wls.common.security.domain.BezirkUndWahlID; +import java.util.Optional; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.CachePut; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.data.repository.CrudRepository; +import org.springframework.security.access.prepost.PreAuthorize; + +@PreAuthorize("hasAuthority('Monitoring_READ_Waehleranzahl')") +public interface WaehleranzahlRepository extends CrudRepository { + + String CACHE = "WAEHLERANZAHL_CACHE"; + + @Override + Iterable findAll(); + + @Override + @Cacheable(value = CACHE, key = "#p0") + Optional findById(BezirkUndWahlID bezirkUndWahlID); + + @Override + @CachePut(value = CACHE, key = "#p0.bezirkUndWahlID") + @PreAuthorize("hasAuthority('Monitoring_WRITE_Waehleranzahl')") + S save(S waehleranzahl); + + @Override + @CacheEvict(value = CACHE, key = "#p0") + @PreAuthorize("hasAuthority('Monitoring_DELETE_Waehleranzahl')") + void deleteById(BezirkUndWahlID bezirkUndWahlID); + + @Override + @CacheEvict(value = CACHE, key = "#p0.bezirkUndWahlID") + @PreAuthorize("hasAuthority('Monitoring_DELETE_Waehleranzahl')") + void delete(Waehleranzahl entity); + + @Override + @CacheEvict(value = CACHE, allEntries = true) + @PreAuthorize("hasAuthority('Monitoring_DELETE_Waehleranzahl')") + void deleteAll(Iterable entities); + + @Override + @CacheEvict(value = CACHE, allEntries = true) + @PreAuthorize("hasAuthority('Monitoring_DELETE_Waehleranzahl')") + void deleteAll(); +} diff --git a/wls-monitoring-service/src/main/java/de/muenchen/oss/wahllokalsystem/monitoringservice/exception/ExceptionConstants.java b/wls-monitoring-service/src/main/java/de/muenchen/oss/wahllokalsystem/monitoringservice/exception/ExceptionConstants.java new file mode 100644 index 000000000..be20ff26d --- /dev/null +++ b/wls-monitoring-service/src/main/java/de/muenchen/oss/wahllokalsystem/monitoringservice/exception/ExceptionConstants.java @@ -0,0 +1,19 @@ +package de.muenchen.oss.wahllokalsystem.monitoringservice.exception; + +import de.muenchen.oss.wahllokalsystem.wls.common.exception.util.ExceptionDataWrapper; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class ExceptionConstants { + + //waehleranzahl + public static final ExceptionDataWrapper GETWAHLBETEILIGUNG_SUCHKRITERIEN_UNVOLLSTAENDIG = new ExceptionDataWrapper("102", + "Fehler in getWahlbeteiligung(): Parameter unvollstaendig."); + public static final ExceptionDataWrapper POSTWAHLBETEILIGUNG_UNSAVEABLE = new ExceptionDataWrapper("101", + "Fehler in postWahlbeteiligung(): Waehleranzahl konnte nicht gespeichert werden."); + + public static ExceptionDataWrapper FAILED_COMMUNICATION_WITH_EAI = new ExceptionDataWrapper("100", + "Bei der Kommunikation mit dem Aoueai-Service ist ein Fehler aufgetreten. Es konnten daher keine Daten geladen werden."); + +} diff --git a/wls-monitoring-service/src/main/java/de/muenchen/oss/wahllokalsystem/monitoringservice/exception/GlobalExceptionHandler.java b/wls-monitoring-service/src/main/java/de/muenchen/oss/wahllokalsystem/monitoringservice/exception/GlobalExceptionHandler.java new file mode 100644 index 000000000..24a71555f --- /dev/null +++ b/wls-monitoring-service/src/main/java/de/muenchen/oss/wahllokalsystem/monitoringservice/exception/GlobalExceptionHandler.java @@ -0,0 +1,33 @@ +package de.muenchen.oss.wahllokalsystem.monitoringservice.exception; + +import de.muenchen.oss.wahllokalsystem.wls.common.exception.errorhandler.AbstractExceptionHandler; +import de.muenchen.oss.wahllokalsystem.wls.common.exception.rest.model.DTOMapper; +import de.muenchen.oss.wahllokalsystem.wls.common.exception.rest.model.WlsExceptionDTO; +import de.muenchen.oss.wahllokalsystem.wls.common.exception.util.ServiceIDFormatter; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; + +@ControllerAdvice +@Slf4j +public class GlobalExceptionHandler extends AbstractExceptionHandler { + + private final ServiceIDFormatter serviceIDFormatter; + + public GlobalExceptionHandler(final ServiceIDFormatter serviceIDFormatter, final DTOMapper dtoMapper) { + super(dtoMapper); + this.serviceIDFormatter = serviceIDFormatter; + } + + @ExceptionHandler + public ResponseEntity handleThrowables(final Throwable throwable) { + log.info("handling throwable", throwable); + return createResponse(getWahlExceptionDTO(throwable)); + } + + @Override + protected String getService() { + return serviceIDFormatter.getId(); + } +} diff --git a/wls-monitoring-service/src/main/java/de/muenchen/oss/wahllokalsystem/monitoringservice/rest/AbstractController.java b/wls-monitoring-service/src/main/java/de/muenchen/oss/wahllokalsystem/monitoringservice/rest/AbstractController.java new file mode 100644 index 000000000..0377cbb3d --- /dev/null +++ b/wls-monitoring-service/src/main/java/de/muenchen/oss/wahllokalsystem/monitoringservice/rest/AbstractController.java @@ -0,0 +1,11 @@ +package de.muenchen.oss.wahllokalsystem.monitoringservice.rest; + +import java.util.Optional; +import org.springframework.http.ResponseEntity; + +public abstract class AbstractController { + + protected ResponseEntity okWithBodyOrNoContent(final Optional body) { + return body.map(ResponseEntity::ok).orElseGet(() -> ResponseEntity.noContent().build()); + } +} diff --git a/wls-monitoring-service/src/main/java/de/muenchen/oss/wahllokalsystem/monitoringservice/rest/TheEntityRepository.java b/wls-monitoring-service/src/main/java/de/muenchen/oss/wahllokalsystem/monitoringservice/rest/TheEntityRepository.java deleted file mode 100644 index 3c6211b44..000000000 --- a/wls-monitoring-service/src/main/java/de/muenchen/oss/wahllokalsystem/monitoringservice/rest/TheEntityRepository.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright (c): it@M - Dienstleister für Informations- und Telekommunikationstechnik - * der Landeshauptstadt München, 2024 - */ -package de.muenchen.oss.wahllokalsystem.monitoringservice.rest; - -import de.muenchen.oss.wahllokalsystem.monitoringservice.domain.TheEntity; -import java.util.Optional; -import java.util.UUID; -import org.springframework.cache.annotation.CacheEvict; -import org.springframework.cache.annotation.CachePut; -import org.springframework.cache.annotation.Cacheable; -import org.springframework.data.repository.CrudRepository; -import org.springframework.security.access.prepost.PreAuthorize; - -/** - * Provides a Repository for {@link TheEntity}. This Repository is exported as a REST resource. - *

- * The Repository handles CRUD Operations. Every Operation is secured and takes care of the tenancy. - * For specific Documentation on how the generated REST point - * behaves, please consider the Spring Data Rest Reference - * here. - *

- */ -@PreAuthorize("hasAuthority(T(de.muenchen.oss.wahllokalsystem.monitoringservice.security.AuthoritiesEnum).WLS_MONITORING_SERVICE_READ_THEENTITY.name())") -public interface TheEntityRepository extends CrudRepository { //NOSONAR - - /** - * Name for the specific cache. - */ - String CACHE = "THEENTITY_CACHE"; - - /** - * Get one specific {@link TheEntity} by its unique id. - * - * @param id The identifier of the {@link TheEntity}. - * @return The {@link TheEntity} with the requested id. - */ - @Override - @Cacheable(value = CACHE, key = "#p0") - Optional findById(UUID id); - - /** - * Create or update a {@link TheEntity}. - *

- * If the id already exists, the {@link TheEntity} will be overridden, hence update. If the id does - * not already exist, a new {@link TheEntity} will be - * created, hence create. - *

- * - * @param theEntity The {@link TheEntity} that will be saved. - * @return the saved {@link TheEntity}. - */ - @Override - @CachePut(value = CACHE, key = "#p0.id") - @PreAuthorize( - "hasAuthority(T(de.muenchen.oss.wahllokalsystem.monitoringservice.security.AuthoritiesEnum).WLS_MONITORING_SERVICE_WRITE_THEENTITY.name())" - ) - S save(S theEntity); - - /** - * Create or update a collection of {@link TheEntity}. - *

- * If the id already exists, the {@link TheEntity}s will be overridden, hence update. If the id does - * not already exist, the new {@link TheEntity}s will be - * created, hence create. - *

- * - * @param entities The {@link TheEntity} that will be saved. - * @return the collection saved {@link TheEntity}. - */ - @Override - @PreAuthorize( - "hasAuthority(T(de.muenchen.oss.wahllokalsystem.monitoringservice.security.AuthoritiesEnum).WLS_MONITORING_SERVICE_WRITE_THEENTITY.name())" - ) - Iterable saveAll(Iterable entities); - - /** - * Delete the {@link TheEntity} by a specified id. - * - * @param id the unique id of the {@link TheEntity} that will be deleted. - */ - @Override - @CacheEvict(value = CACHE, key = "#p0") - @PreAuthorize( - "hasAuthority(T(de.muenchen.oss.wahllokalsystem.monitoringservice.security.AuthoritiesEnum).WLS_MONITORING_SERVICE_DELETE_THEENTITY.name())" - ) - void deleteById(UUID id); - - /** - * Delete a {@link TheEntity} by entity. - * - * @param entity The {@link TheEntity} that will be deleted. - */ - @Override - @CacheEvict(value = CACHE, key = "#p0.id") - @PreAuthorize( - "hasAuthority(T(de.muenchen.oss.wahllokalsystem.monitoringservice.security.AuthoritiesEnum).WLS_MONITORING_SERVICE_DELETE_THEENTITY.name())" - ) - void delete(TheEntity entity); - - /** - * Delete multiple {@link TheEntity} entities by their id. - * - * @param entities The Iterable of {@link TheEntity} that will be deleted. - */ - @Override - @CacheEvict(value = CACHE, allEntries = true) - @PreAuthorize( - "hasAuthority(T(de.muenchen.oss.wahllokalsystem.monitoringservice.security.AuthoritiesEnum).WLS_MONITORING_SERVICE_DELETE_THEENTITY.name())" - ) - void deleteAll(Iterable entities); - - /** - * Delete all {@link TheEntity} entities. - */ - @Override - @CacheEvict(value = CACHE, allEntries = true) - @PreAuthorize( - "hasAuthority(T(de.muenchen.oss.wahllokalsystem.monitoringservice.security.AuthoritiesEnum).WLS_MONITORING_SERVICE_DELETE_THEENTITY.name())" - ) - void deleteAll(); - -} diff --git a/wls-monitoring-service/src/main/java/de/muenchen/oss/wahllokalsystem/monitoringservice/rest/waehleranzahl/WaehleranzahlController.java b/wls-monitoring-service/src/main/java/de/muenchen/oss/wahllokalsystem/monitoringservice/rest/waehleranzahl/WaehleranzahlController.java new file mode 100644 index 000000000..b28bbf0f5 --- /dev/null +++ b/wls-monitoring-service/src/main/java/de/muenchen/oss/wahllokalsystem/monitoringservice/rest/waehleranzahl/WaehleranzahlController.java @@ -0,0 +1,45 @@ +package de.muenchen.oss.wahllokalsystem.monitoringservice.rest.waehleranzahl; + +import de.muenchen.oss.wahllokalsystem.monitoringservice.rest.AbstractController; +import de.muenchen.oss.wahllokalsystem.monitoringservice.service.waehleranzahl.WaehleranzahlService; +import de.muenchen.oss.wahllokalsystem.wls.common.security.domain.BezirkUndWahlID; +import io.swagger.v3.oas.annotations.Operation; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import lombok.val; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/businessActions/wahlbeteiligung") +@RequiredArgsConstructor +@Slf4j +public class WaehleranzahlController extends AbstractController { + + private final WaehleranzahlService waehleranzahlService; + private final WaehleranzahlDTOMapper waehleranzahlDTOMapper; + + @Operation(description = "Laden der zuvor gespeicherten Wahlbeteiligung für die Wahl {wahlID} für den Wahlbezirk {wahlbezirkID}.") + @GetMapping("/{wahlID}/{wahlbezirkID}") + ResponseEntity getWahlbeteiligung(@PathVariable("wahlID") final String wahlID, @PathVariable("wahlbezirkID") final String wahlbezirkID) { + val waehleranzahlModel = waehleranzahlService.getWahlbeteiligung(new BezirkUndWahlID(wahlID, wahlbezirkID)); + + return okWithBodyOrNoContent(waehleranzahlModel.map(waehleranzahlDTOMapper::toDTO)); + } + + @Operation(description = "Speichern und Weiterleiten der Wahlbeteiligung für die Wahl {wahlID} für den Wahlbezirk {wahlbezirkID}.") + @PostMapping("/{wahlID}/{wahlbezirkID}") + public void postWahlbeteiligung(@PathVariable("wahlbezirkID") final String wahlbezirkID, @PathVariable("wahlID") final String wahlID, + @RequestBody WaehleranzahlDTO waehleranzahl) { + log.info("postWahlbeteiligung {}", wahlbezirkID); + + val waehleranzahlSetModel = waehleranzahlDTOMapper.toSetModel(new BezirkUndWahlID(wahlID, wahlbezirkID), waehleranzahl); + + waehleranzahlService.postWahlbeteiligung(waehleranzahlSetModel); + } +} diff --git a/wls-monitoring-service/src/main/java/de/muenchen/oss/wahllokalsystem/monitoringservice/rest/waehleranzahl/WaehleranzahlDTO.java b/wls-monitoring-service/src/main/java/de/muenchen/oss/wahllokalsystem/monitoringservice/rest/waehleranzahl/WaehleranzahlDTO.java new file mode 100644 index 000000000..35fb96c23 --- /dev/null +++ b/wls-monitoring-service/src/main/java/de/muenchen/oss/wahllokalsystem/monitoringservice/rest/waehleranzahl/WaehleranzahlDTO.java @@ -0,0 +1,11 @@ +package de.muenchen.oss.wahllokalsystem.monitoringservice.rest.waehleranzahl; + +import jakarta.validation.constraints.NotNull; +import java.time.LocalDateTime; +import lombok.Builder; + +@Builder +public record WaehleranzahlDTO( + @NotNull Long anzahlWaehler, + @NotNull LocalDateTime uhrzeit) { +} diff --git a/wls-monitoring-service/src/main/java/de/muenchen/oss/wahllokalsystem/monitoringservice/rest/waehleranzahl/WaehleranzahlDTOMapper.java b/wls-monitoring-service/src/main/java/de/muenchen/oss/wahllokalsystem/monitoringservice/rest/waehleranzahl/WaehleranzahlDTOMapper.java new file mode 100644 index 000000000..291968704 --- /dev/null +++ b/wls-monitoring-service/src/main/java/de/muenchen/oss/wahllokalsystem/monitoringservice/rest/waehleranzahl/WaehleranzahlDTOMapper.java @@ -0,0 +1,13 @@ +package de.muenchen.oss.wahllokalsystem.monitoringservice.rest.waehleranzahl; + +import de.muenchen.oss.wahllokalsystem.monitoringservice.service.waehleranzahl.WaehleranzahlModel; +import de.muenchen.oss.wahllokalsystem.wls.common.security.domain.BezirkUndWahlID; +import org.mapstruct.Mapper; + +@Mapper +public interface WaehleranzahlDTOMapper { + + WaehleranzahlDTO toDTO(WaehleranzahlModel waehleranzahlModel); + + WaehleranzahlModel toSetModel(BezirkUndWahlID bezirkUndWahlID, WaehleranzahlDTO waehleranzahlDTO); +} diff --git a/wls-monitoring-service/src/main/java/de/muenchen/oss/wahllokalsystem/monitoringservice/service/waehleranzahl/WaehleranzahlClient.java b/wls-monitoring-service/src/main/java/de/muenchen/oss/wahllokalsystem/monitoringservice/service/waehleranzahl/WaehleranzahlClient.java new file mode 100644 index 000000000..6aeabdd14 --- /dev/null +++ b/wls-monitoring-service/src/main/java/de/muenchen/oss/wahllokalsystem/monitoringservice/service/waehleranzahl/WaehleranzahlClient.java @@ -0,0 +1,15 @@ +package de.muenchen.oss.wahllokalsystem.monitoringservice.service.waehleranzahl; + +import de.muenchen.oss.wahllokalsystem.wls.common.exception.WlsException; + +public interface WaehleranzahlClient { + + /** + * @param waehleranzahlModel references a specific number of voters for a wahlId, wahlbezirkID and + * time + * @throws WlsException + * {@link de.muenchen.oss.wahllokalsystem.wls.common.exception.TechnischeWlsException} + * if there were trouble during communication + */ + void postWahlbeteiligung(final WaehleranzahlModel waehleranzahlModel) throws WlsException; +} diff --git a/wls-monitoring-service/src/main/java/de/muenchen/oss/wahllokalsystem/monitoringservice/service/waehleranzahl/WaehleranzahlModel.java b/wls-monitoring-service/src/main/java/de/muenchen/oss/wahllokalsystem/monitoringservice/service/waehleranzahl/WaehleranzahlModel.java new file mode 100644 index 000000000..f3f8fdfd0 --- /dev/null +++ b/wls-monitoring-service/src/main/java/de/muenchen/oss/wahllokalsystem/monitoringservice/service/waehleranzahl/WaehleranzahlModel.java @@ -0,0 +1,13 @@ +package de.muenchen.oss.wahllokalsystem.monitoringservice.service.waehleranzahl; + +import de.muenchen.oss.wahllokalsystem.wls.common.security.domain.BezirkUndWahlID; +import jakarta.validation.constraints.NotNull; +import java.time.LocalDateTime; +import lombok.Builder; + +@Builder +public record WaehleranzahlModel( + @NotNull BezirkUndWahlID bezirkUndWahlID, + @NotNull long anzahlWaehler, + @NotNull LocalDateTime uhrzeit) { +} diff --git a/wls-monitoring-service/src/main/java/de/muenchen/oss/wahllokalsystem/monitoringservice/service/waehleranzahl/WaehleranzahlModelMapper.java b/wls-monitoring-service/src/main/java/de/muenchen/oss/wahllokalsystem/monitoringservice/service/waehleranzahl/WaehleranzahlModelMapper.java new file mode 100644 index 000000000..84554e90e --- /dev/null +++ b/wls-monitoring-service/src/main/java/de/muenchen/oss/wahllokalsystem/monitoringservice/service/waehleranzahl/WaehleranzahlModelMapper.java @@ -0,0 +1,13 @@ +package de.muenchen.oss.wahllokalsystem.monitoringservice.service.waehleranzahl; + +import de.muenchen.oss.wahllokalsystem.monitoringservice.domain.waehleranzahl.Waehleranzahl; +import org.mapstruct.Mapper; + +@Mapper +public interface WaehleranzahlModelMapper { + + Waehleranzahl toEntity(WaehleranzahlModel waehleranzahlModel); + + WaehleranzahlModel toModel(Waehleranzahl entity); + +} diff --git a/wls-monitoring-service/src/main/java/de/muenchen/oss/wahllokalsystem/monitoringservice/service/waehleranzahl/WaehleranzahlService.java b/wls-monitoring-service/src/main/java/de/muenchen/oss/wahllokalsystem/monitoringservice/service/waehleranzahl/WaehleranzahlService.java new file mode 100644 index 000000000..58218c212 --- /dev/null +++ b/wls-monitoring-service/src/main/java/de/muenchen/oss/wahllokalsystem/monitoringservice/service/waehleranzahl/WaehleranzahlService.java @@ -0,0 +1,43 @@ +package de.muenchen.oss.wahllokalsystem.monitoringservice.service.waehleranzahl; + +import de.muenchen.oss.wahllokalsystem.monitoringservice.domain.waehleranzahl.WaehleranzahlRepository; +import de.muenchen.oss.wahllokalsystem.monitoringservice.exception.ExceptionConstants; +import de.muenchen.oss.wahllokalsystem.wls.common.exception.util.ExceptionFactory; +import de.muenchen.oss.wahllokalsystem.wls.common.security.domain.BezirkUndWahlID; +import java.util.Optional; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import lombok.val; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +@Slf4j +public class WaehleranzahlService { + + private final WaehleranzahlValidator waehleranzahlValidator; + private final WaehleranzahlRepository waehleranzahlRepository; + private final WaehleranzahlModelMapper waehleranzahlModelMapper; + private final ExceptionFactory exceptionFactory; + private final WaehleranzahlClient waehleranzahlClient; + + @PreAuthorize("hasAuthority('Monitoring_BUSINESSACTION_GetWahlbeteiligung')") + public Optional getWahlbeteiligung(BezirkUndWahlID bezirkUndWahlID) { + waehleranzahlValidator.validWahlIdUndWahlbezirkIDOrThrow(bezirkUndWahlID); + val waehleranzahlFromRepo = waehleranzahlRepository.findById(bezirkUndWahlID); + + return waehleranzahlFromRepo.map(waehleranzahlModelMapper::toModel); + } + + @PreAuthorize("hasAuthority('Monitoring_BUSINESSACTION_PostWahlbeteiligung')") + public void postWahlbeteiligung(WaehleranzahlModel waehleranzahl) { + try { + waehleranzahlRepository.save(waehleranzahlModelMapper.toEntity(waehleranzahl)); + } catch (Exception e) { + log.error("#postWahlbeteiligung: Die Wahlen konnten aufgrund eines Fehlers nicht gespeichert werden:", e); + throw exceptionFactory.createTechnischeWlsException(ExceptionConstants.POSTWAHLBETEILIGUNG_UNSAVEABLE); + } + waehleranzahlClient.postWahlbeteiligung(waehleranzahl); + } +} diff --git a/wls-monitoring-service/src/main/java/de/muenchen/oss/wahllokalsystem/monitoringservice/service/waehleranzahl/WaehleranzahlValidator.java b/wls-monitoring-service/src/main/java/de/muenchen/oss/wahllokalsystem/monitoringservice/service/waehleranzahl/WaehleranzahlValidator.java new file mode 100644 index 000000000..32ddb42fb --- /dev/null +++ b/wls-monitoring-service/src/main/java/de/muenchen/oss/wahllokalsystem/monitoringservice/service/waehleranzahl/WaehleranzahlValidator.java @@ -0,0 +1,22 @@ +package de.muenchen.oss.wahllokalsystem.monitoringservice.service.waehleranzahl; + +import de.muenchen.oss.wahllokalsystem.monitoringservice.exception.ExceptionConstants; +import de.muenchen.oss.wahllokalsystem.wls.common.exception.util.ExceptionFactory; +import de.muenchen.oss.wahllokalsystem.wls.common.security.domain.BezirkUndWahlID; +import lombok.RequiredArgsConstructor; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +public class WaehleranzahlValidator { + + private final ExceptionFactory exceptionFactory; + + public void validWahlIdUndWahlbezirkIDOrThrow(final BezirkUndWahlID bezirkUndWahlID) { + if (bezirkUndWahlID == null || StringUtils.isEmpty(bezirkUndWahlID.getWahlID()) || StringUtils.isEmpty(bezirkUndWahlID.getWahlbezirkID()) || + StringUtils.isBlank(bezirkUndWahlID.getWahlID()) || StringUtils.isBlank(bezirkUndWahlID.getWahlbezirkID())) { + throw exceptionFactory.createFachlicheWlsException(ExceptionConstants.GETWAHLBETEILIGUNG_SUCHKRITERIEN_UNVOLLSTAENDIG); + } + } +} diff --git a/wls-monitoring-service/src/main/resources/application.yml b/wls-monitoring-service/src/main/resources/application.yml index 075fe3973..5e80699c6 100644 --- a/wls-monitoring-service/src/main/resources/application.yml +++ b/wls-monitoring-service/src/main/resources/application.yml @@ -5,6 +5,7 @@ spring: group: local: - db-h2 + - dummy.clients flyway: locations: - classpath:db/migrations/{vendor} @@ -24,6 +25,10 @@ security: oauth2: resource.user-info-uri: http://kubernetes.docker.internal:8100/auth/realms/${realm}/protocol/openid-connect/userinfo +service: + info: + oid: WLS-MONITORING + # Define the local keycloak realm here realm: wls_realm @@ -57,3 +62,8 @@ management: enabled: true info.application.name: @project.artifactId@ info.application.version: @project.version@ + +app: + clients: + eai: + basePath: http://localhost:39149 \ No newline at end of file diff --git a/wls-monitoring-service/src/main/resources/db/migrations/h2/V0_1__createTableTheEntity.sql b/wls-monitoring-service/src/main/resources/db/migrations/h2/V0_1__createTableTheEntity.sql deleted file mode 100644 index 1f2b83f72..000000000 --- a/wls-monitoring-service/src/main/resources/db/migrations/h2/V0_1__createTableTheEntity.sql +++ /dev/null @@ -1,5 +0,0 @@ -CREATE TABLE theEntity -( - id varchar2(36) NOT NULL primary key, - textAttribute varchar2(8) NOT NULL -) \ No newline at end of file diff --git a/wls-monitoring-service/src/main/resources/db/migrations/h2/V0_1__createTableWaehleranzahl.sql b/wls-monitoring-service/src/main/resources/db/migrations/h2/V0_1__createTableWaehleranzahl.sql new file mode 100644 index 000000000..51482aa2c --- /dev/null +++ b/wls-monitoring-service/src/main/resources/db/migrations/h2/V0_1__createTableWaehleranzahl.sql @@ -0,0 +1,9 @@ +CREATE TABLE Waehleranzahl +( + wahlID VARCHAR(255) NOT NULL, + wahlbezirkID VARCHAR(255) NOT NULL, + anzahlWaehler BIGINT NOT NULL, + uhrzeit DATETIME NOT NULL, + + PRIMARY KEY (wahlID, wahlbezirkID) +); \ No newline at end of file diff --git a/wls-monitoring-service/src/main/resources/db/migrations/oracle/V0_1__createTableTheEntity.sql b/wls-monitoring-service/src/main/resources/db/migrations/oracle/V0_1__createTableTheEntity.sql deleted file mode 100644 index 1f2b83f72..000000000 --- a/wls-monitoring-service/src/main/resources/db/migrations/oracle/V0_1__createTableTheEntity.sql +++ /dev/null @@ -1,5 +0,0 @@ -CREATE TABLE theEntity -( - id varchar2(36) NOT NULL primary key, - textAttribute varchar2(8) NOT NULL -) \ No newline at end of file diff --git a/wls-monitoring-service/src/main/resources/db/migrations/oracle/V0_1__createTableWaehleranzahl.sql b/wls-monitoring-service/src/main/resources/db/migrations/oracle/V0_1__createTableWaehleranzahl.sql new file mode 100644 index 000000000..67e99c350 --- /dev/null +++ b/wls-monitoring-service/src/main/resources/db/migrations/oracle/V0_1__createTableWaehleranzahl.sql @@ -0,0 +1,9 @@ +CREATE TABLE Waehleranzahl +( + wahlID VARCHAR(255) NOT NULL, + wahlbezirkID VARCHAR(255) NOT NULL, + anzahlWaehler NUMBER NOT NULL, + uhrzeit TIMESTAMP NOT NULL, + + PRIMARY KEY (wahlID, wahlbezirkID) +); \ No newline at end of file diff --git a/wls-monitoring-service/src/main/resources/openapis/openapi.eai.0.1.0.json b/wls-monitoring-service/src/main/resources/openapis/openapi.eai.0.1.0.json new file mode 100644 index 000000000..de8c135d1 --- /dev/null +++ b/wls-monitoring-service/src/main/resources/openapis/openapi.eai.0.1.0.json @@ -0,0 +1 @@ +{"openapi":"3.0.1","info":{"title":"EAI Service","contact":{"name":"Your Name","email":"Your E-Mail-Address"},"version":"0.1.0"},"servers":[{"url":"http://localhost:8080","description":"Generated server url"}],"security":[{"bearerAuth":[]}],"paths":{"/wahlvorstaende/anwesenheit":{"put":{"tags":["wahlvorstand-controller"],"description":"Aktualisieren der Anwesenheit der Wahlvorstandsmitglieder eines bestimmten Wahlbezirkes","operationId":"saveAnwesenheit","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WahlvorstandsaktualisierungDTO"}}},"required":true},"responses":{"200":{"description":"OK"},"400":{"description":"request body validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WlsExceptionDTO"}}}},"404":{"description":"resource not found"},"500":{"description":"unhandled error during communication with other system","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WlsExceptionDTO"}}}}}}},"/wahllokalzustand":{"post":{"tags":["wahllokalzustand-controller"],"operationId":"saveWahllokalZustand","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WahllokalZustandDTO"}}},"required":true},"responses":{"200":{"description":"OK"},"400":{"description":"request body validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WlsExceptionDTO"}}}},"404":{"description":"resource not found"},"500":{"description":"unhandled error during communication with other system","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WlsExceptionDTO"}}}}}}},"/wahlergebnis":{"post":{"tags":["wahlergebnis-controller"],"operationId":"saveWahlergebnismeldung","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErgebnismeldungDTO"}}},"required":true},"responses":{"200":{"description":"OK"},"400":{"description":"request body validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WlsExceptionDTO"}}}},"404":{"description":"resource not found"},"500":{"description":"unhandled error during communication with other system","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WlsExceptionDTO"}}}}}}},"/wahlbeteiligung":{"post":{"tags":["wahlbeteiligung-controller"],"operationId":"saveWahlbeteiligung","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WahlbeteiligungsMeldungDTO"}}},"required":true},"responses":{"200":{"description":"OK"},"400":{"description":"request body validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WlsExceptionDTO"}}}},"404":{"description":"resource not found"},"500":{"description":"unhandled error during communication with other system","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WlsExceptionDTO"}}}}}}},"/wahlvorstaende":{"get":{"tags":["wahlvorstand-controller"],"description":"Abrufen des Wahlvorstandes für einen bestimmten Wahlbezirk","operationId":"loadWahlvorstand","parameters":[{"name":"wahlbezirkID","in":"query","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/WahlvorstandDTO"}}}},"400":{"description":"request body validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WlsExceptionDTO"}}}},"404":{"description":"resource not found"},"500":{"description":"unhandled error during communication with other system","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WlsExceptionDTO"}}}}}}},"/wahldaten/wahltage":{"get":{"tags":["wahldaten-controller"],"operationId":"loadWahltageSinceIncluding","parameters":[{"name":"includingSince","in":"query","required":true,"schema":{"type":"string","format":"date"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"uniqueItems":true,"type":"array","items":{"$ref":"#/components/schemas/WahltagDTO"}}}}},"400":{"description":"request body validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WlsExceptionDTO"}}}},"404":{"description":"resource not found"},"500":{"description":"unhandled error during communication with other system","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WlsExceptionDTO"}}}}}}},"/wahldaten/wahlen":{"get":{"tags":["wahldaten-controller"],"operationId":"loadWahlen","parameters":[{"name":"forDate","in":"query","required":true,"schema":{"type":"string","format":"date"}},{"name":"withNummer","in":"query","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"uniqueItems":true,"type":"array","items":{"$ref":"#/components/schemas/WahlDTO"}}}}},"400":{"description":"request body validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WlsExceptionDTO"}}}},"404":{"description":"resource not found"},"500":{"description":"unhandled error during communication with other system","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WlsExceptionDTO"}}}}}}},"/wahldaten/wahlbezirke/{wahlbezirkID}/wahlberechtigte":{"get":{"tags":["wahldaten-controller"],"operationId":"loadWahlberechtigte","parameters":[{"name":"wahlbezirkID","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/WahlberechtigteDTO"}}}}},"400":{"description":"request body validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WlsExceptionDTO"}}}},"404":{"description":"resource not found"},"500":{"description":"unhandled error during communication with other system","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WlsExceptionDTO"}}}}}}},"/wahldaten/wahlbezirk":{"get":{"tags":["wahldaten-controller"],"operationId":"loadWahlbezirke","parameters":[{"name":"forDate","in":"query","required":true,"schema":{"type":"string","format":"date"}},{"name":"withNummer","in":"query","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"uniqueItems":true,"type":"array","items":{"$ref":"#/components/schemas/WahlbezirkDTO"}}}}},"400":{"description":"request body validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WlsExceptionDTO"}}}},"404":{"description":"resource not found"},"500":{"description":"unhandled error during communication with other system","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WlsExceptionDTO"}}}}}}},"/wahldaten/basisdaten":{"get":{"tags":["wahldaten-controller"],"operationId":"loadBasisdaten","parameters":[{"name":"forDate","in":"query","required":true,"schema":{"type":"string","format":"date"}},{"name":"withNummer","in":"query","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/BasisdatenDTO"}}}},"400":{"description":"request body validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WlsExceptionDTO"}}}},"404":{"description":"resource not found"},"500":{"description":"unhandled error during communication with other system","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WlsExceptionDTO"}}}}}}},"/vorschlaege/wahl/{wahlID}/{wahlbezirkID}":{"get":{"tags":["wahlvorschlag-controller"],"operationId":"loadWahlvorschlaege","parameters":[{"name":"wahlID","in":"path","required":true,"schema":{"type":"string"}},{"name":"wahlbezirkID","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/WahlvorschlaegeDTO"}}}},"400":{"description":"request body validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WlsExceptionDTO"}}}},"404":{"description":"resource not found"},"500":{"description":"unhandled error during communication with other system","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WlsExceptionDTO"}}}}}}},"/vorschlaege/wahl/{wahlID}/liste":{"get":{"tags":["wahlvorschlag-controller"],"operationId":"loadWahlvorschlaegeListe","parameters":[{"name":"forDate","in":"query","required":true,"schema":{"type":"string","format":"date"}},{"name":"wahlID","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/WahlvorschlaegeListeDTO"}}}},"400":{"description":"request body validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WlsExceptionDTO"}}}},"404":{"description":"resource not found"},"500":{"description":"unhandled error during communication with other system","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WlsExceptionDTO"}}}}}}},"/vorschlaege/referendum/{wahlID}/{wahlbezirkID}":{"get":{"tags":["wahlvorschlag-controller"],"operationId":"loadReferendumvorlagen","parameters":[{"name":"wahlID","in":"path","required":true,"schema":{"type":"string"}},{"name":"wahlbezirkID","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ReferendumvorlagenDTO"}}}},"400":{"description":"request body validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WlsExceptionDTO"}}}},"404":{"description":"resource not found"},"500":{"description":"unhandled error during communication with other system","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WlsExceptionDTO"}}}}}}}},"components":{"schemas":{"WahlvorstandsaktualisierungDTO":{"required":["anwesenheitBeginn","mitglieder","wahlbezirkID"],"type":"object","properties":{"wahlbezirkID":{"type":"string"},"mitglieder":{"maxItems":2147483647,"minItems":1,"uniqueItems":true,"type":"array","items":{"$ref":"#/components/schemas/WahlvorstandsmitgliedAktualisierungDTO"}},"anwesenheitBeginn":{"type":"string","format":"date-time"}}},"WahlvorstandsmitgliedAktualisierungDTO":{"required":["anwesend","identifikator"],"type":"object","properties":{"identifikator":{"type":"string"},"anwesend":{"type":"boolean"}}},"DruckzustandDTO":{"required":["niederschriftDruckUhrzeit","niederschriftSendenUhrzeit","schnellmeldungDruckUhrzeit","schnellmeldungSendenUhrzeit","wahlID"],"type":"object","properties":{"wahlID":{"type":"string"},"schnellmeldungSendenUhrzeit":{"type":"string","format":"date-time"},"niederschriftSendenUhrzeit":{"type":"string","format":"date-time"},"schnellmeldungDruckUhrzeit":{"type":"string","format":"date-time"},"niederschriftDruckUhrzeit":{"type":"string","format":"date-time"}}},"WahllokalZustandDTO":{"required":["druckzustaende","letzteAbmeldung","wahlbezirkID","zuletztGesehen"],"type":"object","properties":{"wahlbezirkID":{"type":"string"},"zuletztGesehen":{"type":"string","format":"date-time"},"letzteAbmeldung":{"type":"string","format":"date-time"},"druckzustaende":{"maxItems":2147483647,"minItems":1,"uniqueItems":true,"type":"array","items":{"$ref":"#/components/schemas/DruckzustandDTO"}}}},"AWerteDTO":{"type":"object","properties":{"a1":{"type":"integer","format":"int64"},"a2":{"type":"integer","format":"int64"}}},"BWerteDTO":{"type":"object","properties":{"b":{"type":"integer","format":"int64"},"b1":{"type":"integer","format":"int64"},"b2":{"type":"integer","format":"int64"}}},"ErgebnisDTO":{"required":["ergebnis","kandidatID","stimmenart","wahlvorschlagID","wahlvorschlagsordnungszahl"],"type":"object","properties":{"stimmenart":{"type":"string"},"wahlvorschlagsordnungszahl":{"type":"integer","format":"int64"},"ergebnis":{"type":"integer","format":"int64"},"wahlvorschlagID":{"type":"string"},"kandidatID":{"type":"string"}}},"ErgebnismeldungDTO":{"required":["wahlID","wahlbezirkID"],"type":"object","properties":{"wahlbezirkID":{"type":"string"},"wahlID":{"type":"string"},"meldungsart":{"type":"string","enum":["NIEDERSCHRIFT","SCHNELLMELDUNG"]},"aWerte":{"$ref":"#/components/schemas/AWerteDTO"},"bWerte":{"$ref":"#/components/schemas/BWerteDTO"},"wahlbriefeWerte":{"$ref":"#/components/schemas/WahlbriefeWerteDTO"},"ungueltigeStimmzettels":{"type":"array","items":{"$ref":"#/components/schemas/UngueltigeStimmzettelDTO"}},"ungueltigeStimmzettelAnzahl":{"type":"integer","format":"int64"},"ergebnisse":{"type":"array","items":{"$ref":"#/components/schemas/ErgebnisDTO"}},"wahlart":{"type":"string","enum":["BAW","BEB","BTW","BZW","EUW","LTW","MBW","OBW","SRW","SVW","VE"]}}},"UngueltigeStimmzettelDTO":{"type":"object","properties":{"stimmenart":{"type":"string"},"anzahl":{"type":"integer","format":"int64"},"wahlvorschlagID":{"type":"string"}}},"WahlbriefeWerteDTO":{"type":"object","properties":{"zurueckgewiesenGesamt":{"type":"integer","format":"int64"}}},"WahlbeteiligungsMeldungDTO":{"required":["anzahlWaehler","meldeZeitpunkt","wahlID","wahlbezirkID"],"type":"object","properties":{"wahlID":{"type":"string"},"wahlbezirkID":{"type":"string"},"anzahlWaehler":{"type":"integer","format":"int64"},"meldeZeitpunkt":{"type":"string","format":"date-time"}}},"WahlvorstandDTO":{"required":["mitglieder","wahlbezirkID"],"type":"object","properties":{"wahlbezirkID":{"type":"string"},"mitglieder":{"maxItems":2147483647,"minItems":1,"uniqueItems":true,"type":"array","items":{"$ref":"#/components/schemas/WahlvorstandsmitgliedDTO"}}}},"WahlvorstandsmitgliedDTO":{"required":["anwesend","funktion","identifikator","nachname","vorname"],"type":"object","properties":{"identifikator":{"type":"string"},"vorname":{"type":"string"},"nachname":{"type":"string"},"funktion":{"type":"string","enum":["W","SB","SWB","SSB","B"]},"anwesend":{"type":"boolean"}}},"WahltagDTO":{"required":["beschreibung","identifikator","nummer","tag"],"type":"object","properties":{"identifikator":{"type":"string"},"tag":{"type":"string","format":"date"},"beschreibung":{"type":"string"},"nummer":{"type":"string"}}},"WahlDTO":{"required":["identifikator","name","nummer","wahlart","wahltag"],"type":"object","properties":{"identifikator":{"type":"string"},"name":{"type":"string"},"wahlart":{"type":"string","enum":["BAW","BEB","BTW","BZW","EUW","LTW","MBW","OBW","SRW","SVW","VE"]},"wahltag":{"type":"string","format":"date"},"nummer":{"type":"string"}}},"WahlberechtigteDTO":{"required":["a1","a2","a3","wahlID","wahlbezirkID"],"type":"object","properties":{"wahlID":{"type":"string"},"wahlbezirkID":{"type":"string"},"a1":{"type":"integer","format":"int64"},"a2":{"type":"integer","format":"int64"},"a3":{"type":"integer","format":"int64"}}},"WahlbezirkDTO":{"required":["identifikator","nummer","wahlID","wahlbezirkArt","wahlnummer","wahltag"],"type":"object","properties":{"identifikator":{"type":"string"},"wahlbezirkArt":{"type":"string","enum":["UWB","BWB"]},"nummer":{"type":"string"},"wahltag":{"type":"string","format":"date"},"wahlnummer":{"type":"string"},"wahlID":{"type":"string"}}},"BasisdatenDTO":{"required":["basisstrukturdaten","stimmzettelgebiete","wahlbezirke","wahlen"],"type":"object","properties":{"basisstrukturdaten":{"uniqueItems":true,"type":"array","items":{"$ref":"#/components/schemas/BasisstrukturdatenDTO"}},"wahlen":{"uniqueItems":true,"type":"array","items":{"$ref":"#/components/schemas/WahlDTO"}},"wahlbezirke":{"uniqueItems":true,"type":"array","items":{"$ref":"#/components/schemas/WahlbezirkDTO"}},"stimmzettelgebiete":{"uniqueItems":true,"type":"array","items":{"$ref":"#/components/schemas/StimmzettelgebietDTO"}}}},"BasisstrukturdatenDTO":{"required":["stimmzettelgebietID","wahlID","wahlbezirkID","wahltag"],"type":"object","properties":{"wahlID":{"type":"string"},"stimmzettelgebietID":{"type":"string"},"wahlbezirkID":{"type":"string"},"wahltag":{"type":"string","format":"date"}}},"StimmzettelgebietDTO":{"required":["stimmzettelgebietsart"],"type":"object","properties":{"identifikator":{"type":"string"},"nummer":{"type":"string"},"name":{"type":"string"},"wahltag":{"type":"string","format":"date"},"stimmzettelgebietsart":{"type":"string","enum":["SB","SG","SK","WK"]}}},"KandidatDTO":{"required":["direktkandidat","einzelbewerber","identifikator","listenposition","name","tabellenSpalteInNiederschrift"],"type":"object","properties":{"identifikator":{"type":"string"},"name":{"type":"string"},"listenposition":{"type":"integer","format":"int64"},"direktkandidat":{"type":"boolean"},"tabellenSpalteInNiederschrift":{"type":"integer","format":"int64"},"einzelbewerber":{"type":"boolean"}}},"WahlvorschlaegeDTO":{"required":["stimmzettelgebietID","wahlID","wahlbezirkID","wahlvorschlaege"],"type":"object","properties":{"wahlbezirkID":{"type":"string"},"wahlID":{"type":"string"},"stimmzettelgebietID":{"type":"string"},"wahlvorschlaege":{"maxItems":2147483647,"minItems":1,"uniqueItems":true,"type":"array","items":{"$ref":"#/components/schemas/WahlvorschlagDTO"}}}},"WahlvorschlagDTO":{"required":["erhaeltStimmen","identifikator","kurzname","ordnungszahl"],"type":"object","properties":{"identifikator":{"type":"string"},"ordnungszahl":{"type":"integer","format":"int64"},"kurzname":{"type":"string"},"erhaeltStimmen":{"type":"boolean"},"kandidaten":{"uniqueItems":true,"type":"array","items":{"$ref":"#/components/schemas/KandidatDTO"}}}},"WahlvorschlaegeListeDTO":{"required":["wahlID","wahlvorschlaegeliste"],"type":"object","properties":{"wahlID":{"type":"string"},"wahlvorschlaegeliste":{"maxItems":2147483647,"minItems":1,"uniqueItems":true,"type":"array","items":{"$ref":"#/components/schemas/WahlvorschlaegeDTO"}}}},"ReferendumoptionDTO":{"required":["id","name"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"position":{"type":"integer","format":"int64"}}},"ReferendumvorlageDTO":{"required":["frage","kurzname","ordnungszahl","referendumoptionen","wahlvorschlagID"],"type":"object","properties":{"wahlvorschlagID":{"type":"string"},"ordnungszahl":{"type":"integer","format":"int64"},"kurzname":{"type":"string"},"frage":{"type":"string"},"referendumoptionen":{"maxItems":2147483647,"minItems":1,"uniqueItems":true,"type":"array","items":{"$ref":"#/components/schemas/ReferendumoptionDTO"}}}},"ReferendumvorlagenDTO":{"required":["referendumvorlagen","stimmzettelgebietID"],"type":"object","properties":{"stimmzettelgebietID":{"type":"string"},"referendumvorlagen":{"maxItems":2147483647,"minItems":1,"uniqueItems":true,"type":"array","items":{"$ref":"#/components/schemas/ReferendumvorlageDTO"}}}},"WlsExceptionDTO":{"required":["category","code","message","service"],"type":"object","properties":{"category":{"type":"string","enum":["F","T","S","I"]},"code":{"type":"string"},"service":{"type":"string"},"message":{"type":"string"}}}},"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}}} \ No newline at end of file diff --git a/wls-monitoring-service/src/test/java/de/muenchen/oss/wahllokalsystem/monitoringservice/TestConstants.java b/wls-monitoring-service/src/test/java/de/muenchen/oss/wahllokalsystem/monitoringservice/TestConstants.java index 4baaa9a9e..6668e6af7 100644 --- a/wls-monitoring-service/src/test/java/de/muenchen/oss/wahllokalsystem/monitoringservice/TestConstants.java +++ b/wls-monitoring-service/src/test/java/de/muenchen/oss/wahllokalsystem/monitoringservice/TestConstants.java @@ -5,12 +5,7 @@ package de.muenchen.oss.wahllokalsystem.monitoringservice; import lombok.AccessLevel; -import lombok.EqualsAndHashCode; -import lombok.Getter; import lombok.NoArgsConstructor; -import lombok.Setter; -import lombok.ToString; -import org.springframework.hateoas.RepresentationModel; @NoArgsConstructor(access = AccessLevel.PRIVATE) public final class TestConstants { @@ -19,15 +14,4 @@ public final class TestConstants { public static final String SPRING_NO_SECURITY_PROFILE = "no-security"; - @NoArgsConstructor - @Getter - @Setter - @EqualsAndHashCode(callSuper = true) - @ToString(callSuper = true) - public static class TheEntityDto extends RepresentationModel { - - private String textAttribute; - - } - } diff --git a/wls-monitoring-service/src/test/java/de/muenchen/oss/wahllokalsystem/monitoringservice/client/waehleranzahl/WaehleranzahlClientImplTest.java b/wls-monitoring-service/src/test/java/de/muenchen/oss/wahllokalsystem/monitoringservice/client/waehleranzahl/WaehleranzahlClientImplTest.java new file mode 100644 index 000000000..f255a8201 --- /dev/null +++ b/wls-monitoring-service/src/test/java/de/muenchen/oss/wahllokalsystem/monitoringservice/client/waehleranzahl/WaehleranzahlClientImplTest.java @@ -0,0 +1,66 @@ +package de.muenchen.oss.wahllokalsystem.monitoringservice.client.waehleranzahl; + +import de.muenchen.oss.wahllokalsystem.monitoringservice.eai.aou.client.WahlbeteiligungControllerApi; +import de.muenchen.oss.wahllokalsystem.monitoringservice.eai.aou.model.WahlbeteiligungsMeldungDTO; +import de.muenchen.oss.wahllokalsystem.monitoringservice.service.waehleranzahl.WaehleranzahlModel; +import de.muenchen.oss.wahllokalsystem.wls.common.security.domain.BezirkUndWahlID; +import java.time.LocalDateTime; +import java.time.OffsetDateTime; +import lombok.val; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +class WaehleranzahlClientImplTest { + + @Mock + WahlbeteiligungControllerApi wahlbeteiligungControllerApi; + + @Mock + WaehleranzahlClientMapper waehleranzahlClientMapper; + + @InjectMocks + WaehleranzahlClientImpl unitUnderTest; + + @Nested + class PostWahlbeteiligung { + + @Test + void should_callEaiApiWithDTO_when_clientIsCalledWithModel() { + val waehleranzahlModel = new WaehleranzahlModel(new BezirkUndWahlID("wahlID01", "wahlbezirkID01"), 99L, LocalDateTime.now()); + val mockedWahlbeteiligungsMeldungDTO = createWahlbeteiligungsMeldungDTO(); + Mockito.when(waehleranzahlClientMapper.fromModelToRemoteClientDTO(waehleranzahlModel)).thenReturn(mockedWahlbeteiligungsMeldungDTO); + + unitUnderTest.postWahlbeteiligung(waehleranzahlModel); + Mockito.verify(wahlbeteiligungControllerApi).saveWahlbeteiligung(mockedWahlbeteiligungsMeldungDTO); + } + + @Test + void should_notThrowException_when_eaiApiThrowsAnyException() { + val waehleranzahlModel = new WaehleranzahlModel(new BezirkUndWahlID("wahlID01", "wahlbezirkID01"), 99L, LocalDateTime.now()); + val mockedWahlbeteiligungsMeldungDTO = createWahlbeteiligungsMeldungDTO(); + Mockito.when(waehleranzahlClientMapper.fromModelToRemoteClientDTO(waehleranzahlModel)).thenReturn(mockedWahlbeteiligungsMeldungDTO); + + val mockedApiException = new IllegalArgumentException("Nix-Connect"); + Mockito.doThrow(mockedApiException).when(wahlbeteiligungControllerApi).saveWahlbeteiligung(mockedWahlbeteiligungsMeldungDTO); + + Assertions.assertThatNoException().isThrownBy(() -> unitUnderTest.postWahlbeteiligung(waehleranzahlModel)); + } + + private WahlbeteiligungsMeldungDTO createWahlbeteiligungsMeldungDTO() { + val wahlID = "wahlID01"; + val wahlbezirkID = "wahlbezirkID01"; + val anzahlWahler = 99L; + val meldeZeitpunkt = OffsetDateTime.now(); + return new WahlbeteiligungsMeldungDTO().wahlID(wahlID).wahlbezirkID(wahlbezirkID).anzahlWaehler(anzahlWahler) + .meldeZeitpunkt(meldeZeitpunkt); + } + } + +} diff --git a/wls-monitoring-service/src/test/java/de/muenchen/oss/wahllokalsystem/monitoringservice/client/waehleranzahl/WaehleranzahlClientMapperTest.java b/wls-monitoring-service/src/test/java/de/muenchen/oss/wahllokalsystem/monitoringservice/client/waehleranzahl/WaehleranzahlClientMapperTest.java new file mode 100644 index 000000000..d10dd75af --- /dev/null +++ b/wls-monitoring-service/src/test/java/de/muenchen/oss/wahllokalsystem/monitoringservice/client/waehleranzahl/WaehleranzahlClientMapperTest.java @@ -0,0 +1,67 @@ +package de.muenchen.oss.wahllokalsystem.monitoringservice.client.waehleranzahl; + +import de.muenchen.oss.wahllokalsystem.monitoringservice.eai.aou.model.WahlbeteiligungsMeldungDTO; +import de.muenchen.oss.wahllokalsystem.monitoringservice.service.waehleranzahl.WaehleranzahlModel; +import de.muenchen.oss.wahllokalsystem.wls.common.security.domain.BezirkUndWahlID; +import java.time.LocalDateTime; +import java.time.OffsetDateTime; +import java.time.ZoneId; +import java.time.ZoneOffset; +import lombok.val; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mapstruct.factory.Mappers; + +public class WaehleranzahlClientMapperTest { + + private final WaehleranzahlClientMapper unitUnderTest = Mappers.getMapper(WaehleranzahlClientMapper.class); + + @Nested + class FromModelToRemoteClientDTO { + + @Test + void should_return_RemoteClientDTO_when_modelIsGiven() { + val wahlID = "wahlID01"; + val wahlbezirkID = "wahlbezirkID01"; + val anzahlWahler = 99L; + val meldeZeitpunkt = LocalDateTime.now(); + val bezirkUndWahlID = new BezirkUndWahlID(wahlID, wahlbezirkID); + + val waehleranzahlModel = new WaehleranzahlModel(bezirkUndWahlID, anzahlWahler, meldeZeitpunkt); + Assertions.assertThat(waehleranzahlModel).hasNoNullFieldsOrProperties(); + + val result = unitUnderTest.fromModelToRemoteClientDTO(waehleranzahlModel); + + val zoneOffset = ZoneId.systemDefault().getRules().getOffset(meldeZeitpunkt); + val expectedWahlbeteiligungsMeldungDTO = new WahlbeteiligungsMeldungDTO().wahlID(null).wahlbezirkID(wahlbezirkID).anzahlWaehler(anzahlWahler) + .meldeZeitpunkt(meldeZeitpunkt.atOffset(zoneOffset)); + Assertions.assertThat(result).isEqualTo(expectedWahlbeteiligungsMeldungDTO); + } + } + + @Nested + class LocalDateTimeToOffsetDateTime { + + @Test + void should_useSummerSeasonOffset_when_mapping() { + val localDateTime = LocalDateTime.parse("2024-06-12T12:13:14.567"); + val zoneID = ZoneId.of("Europe/Berlin"); + + val result = unitUnderTest.localDateTimeToOffsetDateTime(localDateTime, zoneID); + + val expectedResult = OffsetDateTime.of(localDateTime, ZoneOffset.ofHours(2)); + Assertions.assertThat(result).isEqualTo(expectedResult); + } + + @Test + void should_useWinterSeasonOffset_when_mapping() { + val localDateTime = LocalDateTime.parse("2024-11-12T12:13:14.567"); + val zoneID = ZoneId.of("Europe/Berlin"); + val result = unitUnderTest.localDateTimeToOffsetDateTime(localDateTime, zoneID); + + val expectedResult = OffsetDateTime.of(localDateTime, ZoneOffset.ofHours(1)); + Assertions.assertThat(result).isEqualTo(expectedResult); + } + } +} diff --git a/wls-monitoring-service/src/test/java/de/muenchen/oss/wahllokalsystem/monitoringservice/configuration/CacheControlConfigurationTest.java b/wls-monitoring-service/src/test/java/de/muenchen/oss/wahllokalsystem/monitoringservice/configuration/CacheControlConfigurationTest.java deleted file mode 100644 index 4d6ddbc57..000000000 --- a/wls-monitoring-service/src/test/java/de/muenchen/oss/wahllokalsystem/monitoringservice/configuration/CacheControlConfigurationTest.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (c): it@M - Dienstleister für Informations- und Telekommunikationstechnik - * der Landeshauptstadt München, 2024 - */ -package de.muenchen.oss.wahllokalsystem.monitoringservice.configuration; - -import static de.muenchen.oss.wahllokalsystem.monitoringservice.TestConstants.SPRING_NO_SECURITY_PROFILE; -import static de.muenchen.oss.wahllokalsystem.monitoringservice.TestConstants.SPRING_TEST_PROFILE; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import de.muenchen.oss.wahllokalsystem.monitoringservice.MicroServiceApplication; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.web.client.TestRestTemplate; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpMethod; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.test.context.ActiveProfiles; - -@SpringBootTest( - classes = { MicroServiceApplication.class }, - webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, - properties = { - "spring.datasource.url=jdbc:h2:mem:testexample;DB_CLOSE_ON_EXIT=FALSE", - "refarch.gracefulshutdown.pre-wait-seconds=0" - } -) -@ActiveProfiles(profiles = { SPRING_TEST_PROFILE, SPRING_NO_SECURITY_PROFILE }) -class CacheControlConfigurationTest { - - private static final String ENTITY_ENDPOINT_URL = "/theEntities"; - - private static final String EXPECTED_CACHE_CONTROL_HEADER_VALUES = "no-cache, no-store, must-revalidate"; - - @Autowired - private TestRestTemplate testRestTemplate; - - @Test - @Disabled - void testForCacheControlHeadersForEntityEndpoint() { - ResponseEntity response = testRestTemplate.exchange(ENTITY_ENDPOINT_URL, HttpMethod.GET, null, String.class); - assertEquals(HttpStatus.OK, response.getStatusCode()); - assertTrue(response.getHeaders().containsKey(HttpHeaders.CACHE_CONTROL)); - assertEquals(EXPECTED_CACHE_CONTROL_HEADER_VALUES, response.getHeaders().getCacheControl()); - } - -} diff --git a/wls-monitoring-service/src/test/java/de/muenchen/oss/wahllokalsystem/monitoringservice/configuration/SecurityConfigurationTest.java b/wls-monitoring-service/src/test/java/de/muenchen/oss/wahllokalsystem/monitoringservice/configuration/SecurityConfigurationTest.java index 1f6ff4d6b..10cf64d31 100644 --- a/wls-monitoring-service/src/test/java/de/muenchen/oss/wahllokalsystem/monitoringservice/configuration/SecurityConfigurationTest.java +++ b/wls-monitoring-service/src/test/java/de/muenchen/oss/wahllokalsystem/monitoringservice/configuration/SecurityConfigurationTest.java @@ -1,15 +1,26 @@ package de.muenchen.oss.wahllokalsystem.monitoringservice.configuration; import static de.muenchen.oss.wahllokalsystem.monitoringservice.TestConstants.SPRING_TEST_PROFILE; +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import com.fasterxml.jackson.databind.ObjectMapper; import de.muenchen.oss.wahllokalsystem.monitoringservice.MicroServiceApplication; +import de.muenchen.oss.wahllokalsystem.monitoringservice.rest.waehleranzahl.WaehleranzahlDTO; +import de.muenchen.oss.wahllokalsystem.monitoringservice.service.waehleranzahl.WaehleranzahlService; +import lombok.val; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.actuate.observability.AutoConfigureObservability; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.MediaType; +import org.springframework.security.test.context.support.WithAnonymousUser; +import org.springframework.security.test.context.support.WithMockUser; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.web.servlet.MockMvc; @@ -19,9 +30,15 @@ @ActiveProfiles(profiles = { SPRING_TEST_PROFILE }) class SecurityConfigurationTest { + @MockBean + WaehleranzahlService waehleranzahlService; + @Autowired MockMvc api; + @Autowired + ObjectMapper objectMapper; + @Test void accessSecuredResourceRootThenUnauthorized() throws Exception { api.perform(get("/")) @@ -64,4 +81,35 @@ void accessUnsecuredResourceSwaggerUiThenOk() throws Exception { .andExpect(status().isOk()); } + @Nested + class Waehleranzahl { + + @Test + @WithAnonymousUser + void should_return401Unauthorized_when_getWithUnauthorizedAnonymousUser() throws Exception { + api.perform(get("/businessActions/wahlbeteiligung/wahlID/wahlbezirkID")).andExpect(status().isUnauthorized()); + } + + @Test + @WithMockUser + void should_return204NoContent_when_getWithAuthorizedMockUser() throws Exception { + api.perform(get("/businessActions/wahlbeteiligung/wahlID/wahlbezirkID")).andExpect(status().isNoContent()); + } + + @Test + @WithAnonymousUser + void should_return401Unauthorized_when_postWithUnauthorizedAnonymousUser() throws Exception { + api.perform(post("/businessActions/wahlbeteiligung/wahlID/wahlbezirkID").with(csrf())).andExpect(status().isUnauthorized()); + } + + @Test + @WithMockUser + void should_return200OK_when_postWithAuthorizedMockUser() throws Exception { + val requestBody = new WaehleranzahlDTO(null, null); + val request = post("/businessActions/wahlbeteiligung/wahlID/wahlbezirkID").with(csrf()).contentType(MediaType.APPLICATION_JSON).content( + objectMapper.writeValueAsString(requestBody)); + + api.perform(request).andExpect(status().isOk()); + } + } } diff --git a/wls-monitoring-service/src/test/java/de/muenchen/oss/wahllokalsystem/monitoringservice/configuration/UnicodeConfigurationTest.java b/wls-monitoring-service/src/test/java/de/muenchen/oss/wahllokalsystem/monitoringservice/configuration/UnicodeConfigurationTest.java deleted file mode 100644 index 119a8b609..000000000 --- a/wls-monitoring-service/src/test/java/de/muenchen/oss/wahllokalsystem/monitoringservice/configuration/UnicodeConfigurationTest.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (c): it@M - Dienstleister für Informations- und Telekommunikationstechnik - * der Landeshauptstadt München, 2024 - */ -package de.muenchen.oss.wahllokalsystem.monitoringservice.configuration; - -import static de.muenchen.oss.wahllokalsystem.monitoringservice.TestConstants.SPRING_NO_SECURITY_PROFILE; -import static de.muenchen.oss.wahllokalsystem.monitoringservice.TestConstants.SPRING_TEST_PROFILE; -import static de.muenchen.oss.wahllokalsystem.monitoringservice.TestConstants.TheEntityDto; -import static org.junit.jupiter.api.Assertions.assertEquals; - -import de.muenchen.oss.wahllokalsystem.monitoringservice.MicroServiceApplication; -import de.muenchen.oss.wahllokalsystem.monitoringservice.domain.TheEntity; -import de.muenchen.oss.wahllokalsystem.monitoringservice.rest.TheEntityRepository; -import java.net.URI; -import java.util.UUID; -import org.apache.commons.lang3.StringUtils; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.web.client.TestRestTemplate; -import org.springframework.test.context.ActiveProfiles; - -@SpringBootTest( - classes = { MicroServiceApplication.class }, - webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, - properties = { - "spring.datasource.url=jdbc:h2:mem:testexample;DB_CLOSE_ON_EXIT=FALSE", - "refarch.gracefulshutdown.pre-wait-seconds=0" - } -) -@ActiveProfiles(profiles = { SPRING_TEST_PROFILE, SPRING_NO_SECURITY_PROFILE }) -class UnicodeConfigurationTest { - - private static final String ENTITY_ENDPOINT_URL = "/theEntities"; - - /** - * Decomposed string: String "Ä-é" represented with unicode letters "A◌̈-e◌́" - */ - private static final String TEXT_ATTRIBUTE_DECOMPOSED = "\u0041\u0308-\u0065\u0301"; - - /** - * Composed string: String "Ä-é" represented with unicode letters "Ä-é". - */ - private static final String TEXT_ATTRIBUTE_COMPOSED = "\u00c4-\u00e9"; - - @Autowired - private TestRestTemplate testRestTemplate; - - @Autowired - private TheEntityRepository theEntityRepository; - - @Test - @Disabled - void testForNfcNormalization() { - // Persist entity with decomposed string. - final TheEntityDto theEntityDto = new TheEntityDto(); - theEntityDto.setTextAttribute(TEXT_ATTRIBUTE_DECOMPOSED); - assertEquals(TEXT_ATTRIBUTE_DECOMPOSED.length(), theEntityDto.getTextAttribute().length()); - final TheEntityDto response = testRestTemplate.postForEntity(URI.create(ENTITY_ENDPOINT_URL), theEntityDto, TheEntityDto.class).getBody(); - - // Check whether response contains a composed string. - assertEquals(TEXT_ATTRIBUTE_COMPOSED, response.getTextAttribute()); - assertEquals(TEXT_ATTRIBUTE_COMPOSED.length(), response.getTextAttribute().length()); - - // Extract uuid from self link. - final UUID uuid = UUID.fromString(StringUtils.substringAfterLast(response.getRequiredLink("self").getHref(), "/")); - - // Check persisted entity contains a composed string via JPA repository. - final TheEntity theEntity = theEntityRepository.findById(uuid).orElse(null); - assertEquals(TEXT_ATTRIBUTE_COMPOSED, theEntity.getTextAttribute()); - assertEquals(TEXT_ATTRIBUTE_COMPOSED.length(), theEntity.getTextAttribute().length()); - } - -} diff --git a/wls-monitoring-service/src/test/java/de/muenchen/oss/wahllokalsystem/monitoringservice/rest/TheEntityRepositoryTest.java b/wls-monitoring-service/src/test/java/de/muenchen/oss/wahllokalsystem/monitoringservice/rest/TheEntityRepositoryTest.java deleted file mode 100644 index b301a4131..000000000 --- a/wls-monitoring-service/src/test/java/de/muenchen/oss/wahllokalsystem/monitoringservice/rest/TheEntityRepositoryTest.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (c): it@M - Dienstleister für Informations- und Telekommunikationstechnik - * der Landeshauptstadt München, 2024 - */ -package de.muenchen.oss.wahllokalsystem.monitoringservice.rest; - -import static de.muenchen.oss.wahllokalsystem.monitoringservice.TestConstants.SPRING_NO_SECURITY_PROFILE; -import static de.muenchen.oss.wahllokalsystem.monitoringservice.TestConstants.SPRING_TEST_PROFILE; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; - -import de.muenchen.oss.wahllokalsystem.monitoringservice.MicroServiceApplication; -import de.muenchen.oss.wahllokalsystem.monitoringservice.domain.TheEntity; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.ActiveProfiles; -import org.springframework.transaction.annotation.Propagation; -import org.springframework.transaction.annotation.Transactional; - -@SpringBootTest( - classes = { MicroServiceApplication.class }, - webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, - properties = { - "spring.datasource.url=jdbc:h2:mem:wls;DB_CLOSE_ON_EXIT=FALSE", - "refarch.gracefulshutdown.pre-wait-seconds=0" - } -) -@ActiveProfiles(profiles = { SPRING_TEST_PROFILE, SPRING_NO_SECURITY_PROFILE }) -class TheEntityRepositoryTest { - - @Autowired - private TheEntityRepository repository; - - @Test - @Transactional(propagation = Propagation.REQUIRED, noRollbackFor = Exception.class) - void testSave() { - - // Implement your logic here by replacing and/or extending the code - - // initialize - TheEntity original = new TheEntity(); - original.setTextAttribute("test"); - - // persist - original = repository.save(original); - - // check - TheEntity persisted = repository.findById(original.getId()).orElse(null); - assertNotNull(persisted); - assertEquals(original, persisted); - - } - -} diff --git a/wls-monitoring-service/src/test/java/de/muenchen/oss/wahllokalsystem/monitoringservice/rest/waehleranzahl/WaehleranzahlControllerIntegrationTest.java b/wls-monitoring-service/src/test/java/de/muenchen/oss/wahllokalsystem/monitoringservice/rest/waehleranzahl/WaehleranzahlControllerIntegrationTest.java new file mode 100644 index 000000000..e73bf7bb7 --- /dev/null +++ b/wls-monitoring-service/src/test/java/de/muenchen/oss/wahllokalsystem/monitoringservice/rest/waehleranzahl/WaehleranzahlControllerIntegrationTest.java @@ -0,0 +1,164 @@ +package de.muenchen.oss.wahllokalsystem.monitoringservice.rest.waehleranzahl; + +import static de.muenchen.oss.wahllokalsystem.monitoringservice.TestConstants.SPRING_NO_SECURITY_PROFILE; +import static de.muenchen.oss.wahllokalsystem.monitoringservice.TestConstants.SPRING_TEST_PROFILE; +import static org.mockito.ArgumentMatchers.any; +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import com.fasterxml.jackson.databind.ObjectMapper; +import de.muenchen.oss.wahllokalsystem.monitoringservice.MicroServiceApplication; +import de.muenchen.oss.wahllokalsystem.monitoringservice.domain.waehleranzahl.Waehleranzahl; +import de.muenchen.oss.wahllokalsystem.monitoringservice.domain.waehleranzahl.WaehleranzahlRepository; +import de.muenchen.oss.wahllokalsystem.monitoringservice.exception.ExceptionConstants; +import de.muenchen.oss.wahllokalsystem.monitoringservice.service.waehleranzahl.WaehleranzahlModelMapper; +import de.muenchen.oss.wahllokalsystem.wls.common.exception.rest.model.WlsExceptionCategory; +import de.muenchen.oss.wahllokalsystem.wls.common.exception.rest.model.WlsExceptionDTO; +import de.muenchen.oss.wahllokalsystem.wls.common.security.domain.BezirkUndWahlID; +import java.time.LocalDateTime; +import lombok.val; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.SpyBean; +import org.springframework.cloud.contract.wiremock.AutoConfigureWireMock; +import org.springframework.http.MediaType; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.RequestBuilder; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; + +@SpringBootTest(classes = MicroServiceApplication.class, webEnvironment = SpringBootTest.WebEnvironment.MOCK) +@AutoConfigureMockMvc +@AutoConfigureWireMock +@ActiveProfiles(profiles = { SPRING_TEST_PROFILE, SPRING_NO_SECURITY_PROFILE }) +public class WaehleranzahlControllerIntegrationTest { + + @Value("${service.info.oid}") + String serviceID; + + @Autowired + ObjectMapper objectMapper; + + @SpyBean + WaehleranzahlRepository wahleranzahlRepository; + + @Autowired + WaehleranzahlDTOMapper waehleranzahlDTOMapper; + + @Autowired + WaehleranzahlModelMapper waehleranzahlModelMapper; + + @Autowired + MockMvc api; + + @Autowired + WaehleranzahlRepository waehleranzahlRepository; + + @AfterEach + void tearDown() { + waehleranzahlRepository.deleteAll(); + } + + @Nested + class GetWahlbeteiligung { + + @Test + void should_returnEmptyResponse_when_noDataFound() throws Exception { + val wahlID = "wahlID01"; + val wahlbezirkID = "wahlbezirkID01"; + val request = MockMvcRequestBuilders.get("/businessActions/wahlbeteiligung/" + wahlID + "/" + wahlbezirkID); + + val response = api.perform(request).andExpect(status().isNoContent()).andReturn(); + + Assertions.assertThat(response.getResponse().getContentAsString()).isEmpty(); + } + + @Test + void should_returnOkAndData_when_dataFound() throws Exception { + val wahlID = "wahlID01"; + val wahlbezirkID = "wahlbezirkID01"; + val bezirkUndWahlID = new BezirkUndWahlID(wahlID, wahlbezirkID); + val anzahlWaehler = 99; + val uhrzeit = LocalDateTime.parse("2024-09-13T12:11:21.343"); + + val waehleranzahlToFind = new Waehleranzahl(bezirkUndWahlID, anzahlWaehler, uhrzeit); + waehleranzahlRepository.save(waehleranzahlToFind); + + val request = MockMvcRequestBuilders.get("/businessActions/wahlbeteiligung/" + wahlID + "/" + wahlbezirkID); + + val response = api.perform(request).andExpect(status().isOk()).andReturn(); + val responseBodyAsDTO = objectMapper.readValue(response.getResponse().getContentAsString(), WaehleranzahlDTO.class); + + val expectedResponseBody = waehleranzahlDTOMapper.toDTO(waehleranzahlModelMapper.toModel(waehleranzahlToFind)); + Assertions.assertThat(responseBodyAsDTO).isEqualTo(expectedResponseBody); + } + + @Nested + class PostWahlbeteiligung { + + @Test + void should_overwriteExistingData_when_newDataIsStoredWithSameID() throws Exception { + val wahlID = "wahlID01"; + val wahlbezirkID = "wahlbezirkID01"; + val bezirkUndWahlID = new BezirkUndWahlID(wahlID, wahlbezirkID); + val anzahlWaehler_1 = 99L; + val uhrzeit_1 = LocalDateTime.parse("2024-09-13T12:11:21.343"); + + // store data with bezirkUndWahlID + val waehleranzahlToFind = new Waehleranzahl(bezirkUndWahlID, anzahlWaehler_1, uhrzeit_1); + waehleranzahlRepository.save(waehleranzahlToFind); + + // Overwrite existing data with same bezirkUndWahlID + val anzahlWaehler_2 = 55L; + val uhrzeit_2 = LocalDateTime.parse("2024-09-13T12:11:21.666"); + val waehleranzahlDTO_2 = new WaehleranzahlDTO(anzahlWaehler_2, uhrzeit_2); + + val request_2 = buildPostRequest(wahlID, wahlbezirkID, waehleranzahlDTO_2); + api.perform(request_2).andExpect(status().isOk()).andReturn(); + + val waehleranzahlFromRepo_2 = waehleranzahlRepository.findById(bezirkUndWahlID).get(); + val expectedWaehleranzahl_2 = waehleranzahlModelMapper.toEntity(waehleranzahlDTOMapper.toSetModel(bezirkUndWahlID, waehleranzahlDTO_2)); + + Assertions.assertThat(waehleranzahlFromRepo_2).usingRecursiveComparison().isEqualTo(expectedWaehleranzahl_2); + } + } + + @Test + void should_throwTechnischeWlsException_when_saveInDBFails() throws Exception { + val wahlID = "_ "; + val wahlbezirkID = "wahlbezirkID01"; + val anzahlWaehler = 99L; + val uhrzeit = LocalDateTime.parse("2024-09-13T12:11:21.343"); + val waehleranzahlDTO = new WaehleranzahlDTO(anzahlWaehler, uhrzeit); + + val request = MockMvcRequestBuilders.post("/businessActions/wahlbeteiligung/" + wahlID + "/" + wahlbezirkID).with(csrf()) + .contentType(MediaType.APPLICATION_JSON) + .content( + objectMapper.writeValueAsString(waehleranzahlDTO)); + + Mockito.doThrow(new RuntimeException("DB-Error")).when(wahleranzahlRepository).save(any()); + + val response = api.perform(request).andExpect(status().isInternalServerError()).andReturn(); + + val responseBodyAsWlsExceptionDTO = objectMapper.readValue(response.getResponse().getContentAsByteArray(), WlsExceptionDTO.class); + + val expectedWlsExceptionDTO = new WlsExceptionDTO(WlsExceptionCategory.T, ExceptionConstants.POSTWAHLBETEILIGUNG_UNSAVEABLE.code(), + serviceID, ExceptionConstants.POSTWAHLBETEILIGUNG_UNSAVEABLE.message()); + + Assertions.assertThat(responseBodyAsWlsExceptionDTO).isEqualTo(expectedWlsExceptionDTO); + } + + private RequestBuilder buildPostRequest(final String wahlID, final String wahlbezirkID, final WaehleranzahlDTO requestBody) throws Exception { + return post("/businessActions/wahlbeteiligung/" + wahlID + "/" + wahlbezirkID).with(csrf()).contentType(MediaType.APPLICATION_JSON).content( + objectMapper.writeValueAsString(requestBody)); + } + } +} diff --git a/wls-monitoring-service/src/test/java/de/muenchen/oss/wahllokalsystem/monitoringservice/rest/waehleranzahl/WaehleranzahlControllerTest.java b/wls-monitoring-service/src/test/java/de/muenchen/oss/wahllokalsystem/monitoringservice/rest/waehleranzahl/WaehleranzahlControllerTest.java new file mode 100644 index 000000000..ee08ff25f --- /dev/null +++ b/wls-monitoring-service/src/test/java/de/muenchen/oss/wahllokalsystem/monitoringservice/rest/waehleranzahl/WaehleranzahlControllerTest.java @@ -0,0 +1,88 @@ +package de.muenchen.oss.wahllokalsystem.monitoringservice.rest.waehleranzahl; + +import de.muenchen.oss.wahllokalsystem.monitoringservice.service.waehleranzahl.WaehleranzahlModel; +import de.muenchen.oss.wahllokalsystem.monitoringservice.service.waehleranzahl.WaehleranzahlService; +import de.muenchen.oss.wahllokalsystem.wls.common.security.domain.BezirkUndWahlID; +import java.time.LocalDateTime; +import java.util.Optional; +import lombok.val; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.http.HttpStatus; + +@ExtendWith(MockitoExtension.class) +public class WaehleranzahlControllerTest { + + @Mock + WaehleranzahlService waehleranzahlService; + + @Mock + WaehleranzahlDTOMapper waehleranzahlDTOMapper; + + @InjectMocks + WaehleranzahlController unitUnderTest; + + @Nested + class GetWahlbeteiligung { + + @Test + void should_returnHttp200AndData_when_dataIsFound() { + + val wahlID = "wahlID01"; + val wahlbezirkID = "wahlbezirkID01"; + val bezirkUndWahlID = new BezirkUndWahlID(wahlID, wahlbezirkID); + val uhrzeit = LocalDateTime.now(); + + val mockedServiceModel = new WaehleranzahlModel(null, 0, null); + val mockedMappedServiceDTO = new WaehleranzahlDTO(99L, uhrzeit); + + Mockito.when(waehleranzahlService.getWahlbeteiligung(bezirkUndWahlID)).thenReturn(Optional.of(mockedServiceModel)); + Mockito.when(waehleranzahlDTOMapper.toDTO(mockedServiceModel)).thenReturn(mockedMappedServiceDTO); + + val result = unitUnderTest.getWahlbeteiligung(wahlID, wahlbezirkID); + + Assertions.assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK); + Assertions.assertThat(result.getBody()).isSameAs(mockedMappedServiceDTO); + } + + @Test + void should_returnHttp204AndBodyIsNull_when_serviceReturnsNoData() { + val wahlID = "wahlID01"; + val wahlbezirkID = "wahlbezirkID01"; + val bezirkUndWahlID = new BezirkUndWahlID(wahlID, wahlbezirkID); + + Mockito.when(waehleranzahlService.getWahlbeteiligung(bezirkUndWahlID)).thenReturn(Optional.empty()); + + val result = unitUnderTest.getWahlbeteiligung(wahlID, wahlbezirkID); + + Assertions.assertThat(result.getStatusCode()).isEqualTo(HttpStatus.NO_CONTENT); + Assertions.assertThat(result.getBody()).isNull(); + } + } + + @Nested + class PostWahlbeteiligung { + + @Test + void should_notThrowException_when_serviceIsCalled() { + val wahlID = "wahlID01"; + val wahlbezirkID = "wahlbezirkID01"; + val bezirkUndWahlID = new BezirkUndWahlID(wahlID, wahlbezirkID); + + val requestBody = Mockito.mock(WaehleranzahlDTO.class); + val mockedMappedRequest = Mockito.mock(WaehleranzahlModel.class); + + Mockito.when(waehleranzahlDTOMapper.toSetModel(bezirkUndWahlID, requestBody)).thenReturn(mockedMappedRequest); + + unitUnderTest.postWahlbeteiligung(wahlbezirkID, wahlID, requestBody); + + Mockito.verify(waehleranzahlService).postWahlbeteiligung(mockedMappedRequest); + } + } +} diff --git a/wls-monitoring-service/src/test/java/de/muenchen/oss/wahllokalsystem/monitoringservice/rest/waehleranzahl/WaehleranzahlDTOMapperTest.java b/wls-monitoring-service/src/test/java/de/muenchen/oss/wahllokalsystem/monitoringservice/rest/waehleranzahl/WaehleranzahlDTOMapperTest.java new file mode 100644 index 000000000..7605b5806 --- /dev/null +++ b/wls-monitoring-service/src/test/java/de/muenchen/oss/wahllokalsystem/monitoringservice/rest/waehleranzahl/WaehleranzahlDTOMapperTest.java @@ -0,0 +1,65 @@ +package de.muenchen.oss.wahllokalsystem.monitoringservice.rest.waehleranzahl; + +import de.muenchen.oss.wahllokalsystem.monitoringservice.service.waehleranzahl.WaehleranzahlModel; +import de.muenchen.oss.wahllokalsystem.wls.common.security.domain.BezirkUndWahlID; +import java.time.LocalDateTime; +import lombok.val; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mapstruct.factory.Mappers; + +class WaehleranzahlDTOMapperTest { + + private final WaehleranzahlDTOMapper unitUnderTest = Mappers.getMapper(WaehleranzahlDTOMapper.class); + + @Nested + class ToDTO { + + @Test + void should_returnNull_when_modelIsNull() { + Assertions.assertThat(unitUnderTest.toDTO(null)).isNull(); + } + + @Test + void should_mapModelToDto_when_ModelIsNotNull() { + String wahlID = "wahlID01"; + String wahlbezirkID = "wahlbezirkID01"; + long anzahlWaehler = 99L; + LocalDateTime uhrzeit = LocalDateTime.now(); + BezirkUndWahlID bezirkUndWahlID = new BezirkUndWahlID(wahlID, wahlbezirkID); + + val modelInput = new WaehleranzahlModel(bezirkUndWahlID, anzahlWaehler, uhrzeit); + val dtoExpected = new WaehleranzahlDTO(anzahlWaehler, uhrzeit); + + val result = unitUnderTest.toDTO(modelInput); + Assertions.assertThat(result).isEqualTo(dtoExpected); + } + + } + + @Nested + class ToSetModel { + + @Test + void should_returnNull_when_dtoIsNull() { + Assertions.assertThat(unitUnderTest.toSetModel(null, null)).isNull(); + } + + @Test + void should_mapDtoToModel_when_DtoIsNotNull() { + String wahlID = "wahlID01"; + String wahlbezirkID = "wahlbezirkID01"; + long anzahlWaehler = 99L; + LocalDateTime uhrzeit = LocalDateTime.now(); + BezirkUndWahlID bezirkUndWahlID = new BezirkUndWahlID(wahlID, wahlbezirkID); + + val dtoInput = new WaehleranzahlDTO(anzahlWaehler, uhrzeit); + val modelExpected = new WaehleranzahlModel(bezirkUndWahlID, anzahlWaehler, uhrzeit); + + val result = unitUnderTest.toSetModel(bezirkUndWahlID, dtoInput); + Assertions.assertThat(result).isEqualTo(modelExpected); + } + + } +} diff --git a/wls-monitoring-service/src/test/java/de/muenchen/oss/wahllokalsystem/monitoringservice/service/waehleranzahl/WaehleranzahlModelMapperTest.java b/wls-monitoring-service/src/test/java/de/muenchen/oss/wahllokalsystem/monitoringservice/service/waehleranzahl/WaehleranzahlModelMapperTest.java new file mode 100644 index 000000000..0e587b890 --- /dev/null +++ b/wls-monitoring-service/src/test/java/de/muenchen/oss/wahllokalsystem/monitoringservice/service/waehleranzahl/WaehleranzahlModelMapperTest.java @@ -0,0 +1,46 @@ +package de.muenchen.oss.wahllokalsystem.monitoringservice.service.waehleranzahl; + +import de.muenchen.oss.wahllokalsystem.monitoringservice.domain.waehleranzahl.Waehleranzahl; +import de.muenchen.oss.wahllokalsystem.wls.common.security.domain.BezirkUndWahlID; +import java.time.LocalDateTime; +import lombok.val; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mapstruct.factory.Mappers; + +class WaehleranzahlModelMapperTest { + + private final WaehleranzahlModelMapper unitUnderTest = Mappers.getMapper(WaehleranzahlModelMapper.class); + + @Test + void should_returnEqualWaehleranzahlModel_when_waehleranzahlIsMapped() { + val wahlID = "wahlID01"; + val wahlbezirkID = "wahlbezirkID01"; + BezirkUndWahlID bezirkUndWahlID = new BezirkUndWahlID(wahlID, wahlbezirkID); + val anzahlWaehler = 99; + LocalDateTime uhrzeit = LocalDateTime.now(); + val waehleranzahlEntity = new Waehleranzahl(bezirkUndWahlID, anzahlWaehler, uhrzeit); + + val waehleranzahlModel = unitUnderTest.toModel(waehleranzahlEntity); + + val expectedResult = new WaehleranzahlModel(bezirkUndWahlID, anzahlWaehler, uhrzeit); + + Assertions.assertThat(waehleranzahlModel).isEqualTo(expectedResult); + } + + @Test + void should_returnEqualWaehleranzahl_when_waehleranzahlModelIsMapped() { + val wahlID = "wahlID01"; + val wahlbezirkID = "wahlbezirkID01"; + BezirkUndWahlID bezirkUndWahlID = new BezirkUndWahlID(wahlID, wahlbezirkID); + val anzahlWaehler = 99; + LocalDateTime uhrzeit = LocalDateTime.now(); + val waehleranzahlModel = new WaehleranzahlModel(bezirkUndWahlID, anzahlWaehler, uhrzeit); + + val waehleranzahlEntity = unitUnderTest.toEntity(waehleranzahlModel); + + val expectedResult = new Waehleranzahl(bezirkUndWahlID, anzahlWaehler, uhrzeit); + + Assertions.assertThat(waehleranzahlEntity).isEqualTo(expectedResult); + } +} diff --git a/wls-monitoring-service/src/test/java/de/muenchen/oss/wahllokalsystem/monitoringservice/service/waehleranzahl/WaehleranzahlServiceSecurityTest.java b/wls-monitoring-service/src/test/java/de/muenchen/oss/wahllokalsystem/monitoringservice/service/waehleranzahl/WaehleranzahlServiceSecurityTest.java new file mode 100644 index 000000000..dd59ec9d2 --- /dev/null +++ b/wls-monitoring-service/src/test/java/de/muenchen/oss/wahllokalsystem/monitoringservice/service/waehleranzahl/WaehleranzahlServiceSecurityTest.java @@ -0,0 +1,149 @@ +package de.muenchen.oss.wahllokalsystem.monitoringservice.service.waehleranzahl; + +import static org.springframework.security.core.context.SecurityContextHolder.clearContext; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.tomakehurst.wiremock.client.WireMock; +import de.muenchen.oss.wahllokalsystem.monitoringservice.MicroServiceApplication; +import de.muenchen.oss.wahllokalsystem.monitoringservice.TestConstants; +import de.muenchen.oss.wahllokalsystem.monitoringservice.domain.waehleranzahl.Waehleranzahl; +import de.muenchen.oss.wahllokalsystem.monitoringservice.domain.waehleranzahl.WaehleranzahlRepository; +import de.muenchen.oss.wahllokalsystem.monitoringservice.rest.waehleranzahl.WaehleranzahlDTOMapper; +import de.muenchen.oss.wahllokalsystem.monitoringservice.utils.Authorities; +import de.muenchen.oss.wahllokalsystem.wls.common.exception.TechnischeWlsException; +import de.muenchen.oss.wahllokalsystem.wls.common.security.domain.BezirkUndWahlID; +import de.muenchen.oss.wahllokalsystem.wls.common.testing.SecurityUtils; +import java.time.LocalDateTime; +import java.util.stream.Stream; +import lombok.val; +import org.assertj.core.api.Assertions; +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.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.aggregator.ArgumentsAccessor; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.cloud.contract.wiremock.AutoConfigureWireMock; +import org.springframework.http.HttpStatus; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.test.context.ActiveProfiles; + +@SpringBootTest(classes = MicroServiceApplication.class) +@ActiveProfiles({ TestConstants.SPRING_TEST_PROFILE }) +@AutoConfigureWireMock +public class WaehleranzahlServiceSecurityTest { + + @Autowired + WaehleranzahlService waehleranzahlService; + + @Autowired + WaehleranzahlRepository waehleranzahlRepository; + + @Autowired + WaehleranzahlDTOMapper waehleranzahlDTOMapper; + + @Autowired + ObjectMapper objectMapper; + + @MockBean + WaehleranzahlValidator waehleranzahlValidator; + + @BeforeEach + void setup() { + clearContext(); + } + + @AfterEach + void tearDown() { + SecurityUtils.runWith(Authorities.REPOSITORY_DELETE_WAEHLERANZAHL); + waehleranzahlRepository.deleteAll(); + } + + @Nested + class GetWahlbeteiligung { + + @Test + void should_grantAccessAndThrowNoException_when_authoritiesAreValid() { + BezirkUndWahlID bezirkUndWahlID = new BezirkUndWahlID("wahlID01", "wahlbezirkID01"); + SecurityUtils.runWith(Authorities.REPOSITORY_WRITE_WAEHLERANZAHL); + waehleranzahlRepository.save(new Waehleranzahl(bezirkUndWahlID, 99, LocalDateTime.now())); + + SecurityUtils.runWith(Authorities.ALL_AUTHORITIES_GET_WAEHLERANZAHL); + + Assertions.assertThatNoException().isThrownBy(() -> waehleranzahlService.getWahlbeteiligung(bezirkUndWahlID)); + } + + @ParameterizedTest(name = "{index} - {1} missing") + @MethodSource("getMissingAuthoritiesVariations") + void should_failWithAccessDeniedException_when_anyAuthorityIsMissing(final ArgumentsAccessor argumentsAccessor) { + SecurityUtils.runWith(argumentsAccessor.get(0, String[].class)); + BezirkUndWahlID bezirkUndWahlID = new BezirkUndWahlID("wahlID01", "wahlbezirkID01"); + Assertions.assertThatThrownBy(() -> waehleranzahlService.getWahlbeteiligung(bezirkUndWahlID)).isInstanceOf(AccessDeniedException.class); + } + + private static Stream getMissingAuthoritiesVariations() { + return SecurityUtils.buildArgumentsForMissingAuthoritiesVariations(Authorities.ALL_AUTHORITIES_GET_WAEHLERANZAHL); + } + } + + @Nested + class PostWahlbeteiligung { + + @Test + void should_grantAccessAndThrowNoException_when_authoritiesAreValid() throws Exception { + SecurityUtils.runWith(Authorities.ALL_AUTHORITIES_SET_WAEHLERANZAHL); + String wahlID = "wahlID01"; + String wahlbezirkID = "wahlbezirkID01"; + BezirkUndWahlID bezirkUndWahlID = new BezirkUndWahlID(wahlID, wahlbezirkID); + val waehleranzahlToSave = new WaehleranzahlModel(bezirkUndWahlID, 99L, LocalDateTime.now()); + + val waehleranzahlDTO = waehleranzahlDTOMapper.toDTO(waehleranzahlToSave); + WireMock.stubFor(WireMock.post("/wahlbeteiligung") + .willReturn(WireMock.aResponse().withHeader("Content-Type", "application/json").withStatus(HttpStatus.OK.value()) + .withBody(objectMapper.writeValueAsBytes(waehleranzahlDTO)))); + + Assertions.assertThatNoException().isThrownBy(() -> waehleranzahlService.postWahlbeteiligung(waehleranzahlToSave)); + } + + @Test + void should_failWithAccessDeniedException_when_serviceAuthorityIsMissing() throws Exception { + SecurityUtils.runWith(Authorities.REPOSITORY_WRITE_WAEHLERANZAHL); + + String wahlID = "wahlID01"; + String wahlbezirkID = "wahlbezirkID01"; + BezirkUndWahlID bezirkUndWahlID = new BezirkUndWahlID(wahlID, wahlbezirkID); + val waehleranzahlToSave = new WaehleranzahlModel(bezirkUndWahlID, 99L, LocalDateTime.now()); + + val waehleranzahlDTO = waehleranzahlDTOMapper.toDTO(waehleranzahlToSave); + WireMock.stubFor(WireMock.post("/wahlbeteiligung") + .willReturn(WireMock.aResponse().withHeader("Content-Type", "application/json").withStatus(HttpStatus.OK.value()) + .withBody(objectMapper.writeValueAsBytes(waehleranzahlDTO)))); + + Assertions.assertThatThrownBy(() -> waehleranzahlService.postWahlbeteiligung(waehleranzahlToSave)).isInstanceOf(AccessDeniedException.class); + } + + @Test + void should_failWithTechnischeWlsException_when_repoAuthorityIsMissing() throws Exception { + SecurityUtils.runWith(Authorities.SERVICE_POST_WAEHLERANZAHL); + + String wahlID = "wahlID01"; + String wahlbezirkID = "wahlbezirkID01"; + BezirkUndWahlID bezirkUndWahlID = new BezirkUndWahlID(wahlID, wahlbezirkID); + val waehleranzahlToSave = new WaehleranzahlModel(bezirkUndWahlID, 99L, LocalDateTime.now()); + + val waehleranzahlDTO = waehleranzahlDTOMapper.toDTO(waehleranzahlToSave); + WireMock.stubFor(WireMock.post("/wahlbeteiligung") + .willReturn(WireMock.aResponse().withHeader("Content-Type", "application/json").withStatus(HttpStatus.OK.value()) + .withBody(objectMapper.writeValueAsBytes(waehleranzahlDTO)))); + + Assertions.assertThatThrownBy(() -> waehleranzahlService.postWahlbeteiligung(waehleranzahlToSave)).isInstanceOf(TechnischeWlsException.class); + } + + } + +} diff --git a/wls-monitoring-service/src/test/java/de/muenchen/oss/wahllokalsystem/monitoringservice/service/waehleranzahl/WaehleranzahlServiceTest.java b/wls-monitoring-service/src/test/java/de/muenchen/oss/wahllokalsystem/monitoringservice/service/waehleranzahl/WaehleranzahlServiceTest.java new file mode 100644 index 000000000..9be7aeb77 --- /dev/null +++ b/wls-monitoring-service/src/test/java/de/muenchen/oss/wahllokalsystem/monitoringservice/service/waehleranzahl/WaehleranzahlServiceTest.java @@ -0,0 +1,77 @@ +package de.muenchen.oss.wahllokalsystem.monitoringservice.service.waehleranzahl; + +import de.muenchen.oss.wahllokalsystem.monitoringservice.domain.waehleranzahl.Waehleranzahl; +import de.muenchen.oss.wahllokalsystem.monitoringservice.domain.waehleranzahl.WaehleranzahlRepository; +import de.muenchen.oss.wahllokalsystem.wls.common.security.domain.BezirkUndWahlID; +import java.time.LocalDateTime; +import java.util.Optional; +import lombok.val; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +public class WaehleranzahlServiceTest { + + @Mock + WaehleranzahlRepository waehleranzahlRepository; + @Mock + WaehleranzahlModelMapper waehleranzahlModelMapper; + @Mock + WaehleranzahlValidator waehleranzahlValidator; + @Mock + WaehleranzahlClient waehleranzahlClient; + + @InjectMocks + WaehleranzahlService unitUnderTest; + + @Nested + class GetWahlbeteiligung { + + @Test + void should_returnRepoData_when_repoDataFound() { + BezirkUndWahlID bezirkUndWahlID = new BezirkUndWahlID("wahlID01", "wahlbezirkID01"); + + val mockedRepoResponse = new Waehleranzahl(); + val mockedMappedRepoResponse = new WaehleranzahlModel(bezirkUndWahlID, 99L, LocalDateTime.now()); + Mockito.doNothing().when(waehleranzahlValidator).validWahlIdUndWahlbezirkIDOrThrow(bezirkUndWahlID); + + Mockito.when(waehleranzahlRepository.findById(bezirkUndWahlID)).thenReturn(Optional.of(mockedRepoResponse)); + Mockito.when(waehleranzahlModelMapper.toModel(mockedRepoResponse)).thenReturn(mockedMappedRepoResponse); + + Assertions.assertThat(unitUnderTest.getWahlbeteiligung(bezirkUndWahlID).get()).isSameAs(mockedMappedRepoResponse); + } + + @Test + void should_returnEmptyResult_when_repoDataNotFound() { + BezirkUndWahlID bezirkUndWahlID = new BezirkUndWahlID("wahlID01", "wahlbezirkID01"); + + Mockito.when(waehleranzahlRepository.findById(bezirkUndWahlID)).thenReturn(Optional.empty()); + + val result = unitUnderTest.getWahlbeteiligung(bezirkUndWahlID); + Assertions.assertThat(result).isEmpty(); + } + } + + @Nested + class PostWahlbeteiligung { + + @Test + void should_notThrowExceptionAndSaveDataInRepo_when_modelIsGiven() { + val waehleranzahlSetModel = WaehleranzahlModel.builder().build(); + val mockedKonfigurationEntity = new Waehleranzahl(); + + Mockito.when(waehleranzahlModelMapper.toEntity(waehleranzahlSetModel)).thenReturn(mockedKonfigurationEntity); + + Assertions.assertThatNoException().isThrownBy(() -> unitUnderTest.postWahlbeteiligung(waehleranzahlSetModel)); + + Mockito.verify(waehleranzahlClient).postWahlbeteiligung(waehleranzahlSetModel); + Mockito.verify(waehleranzahlRepository).save(mockedKonfigurationEntity); + } + } +} diff --git a/wls-monitoring-service/src/test/java/de/muenchen/oss/wahllokalsystem/monitoringservice/service/waehleranzahl/WaehleranzahlValidatorTest.java b/wls-monitoring-service/src/test/java/de/muenchen/oss/wahllokalsystem/monitoringservice/service/waehleranzahl/WaehleranzahlValidatorTest.java new file mode 100644 index 000000000..80f8bd84a --- /dev/null +++ b/wls-monitoring-service/src/test/java/de/muenchen/oss/wahllokalsystem/monitoringservice/service/waehleranzahl/WaehleranzahlValidatorTest.java @@ -0,0 +1,75 @@ +package de.muenchen.oss.wahllokalsystem.monitoringservice.service.waehleranzahl; + +import de.muenchen.oss.wahllokalsystem.monitoringservice.exception.ExceptionConstants; +import de.muenchen.oss.wahllokalsystem.wls.common.exception.FachlicheWlsException; +import de.muenchen.oss.wahllokalsystem.wls.common.exception.util.ExceptionFactory; +import de.muenchen.oss.wahllokalsystem.wls.common.security.domain.BezirkUndWahlID; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +class WaehleranzahlValidatorTest { + + @Mock + ExceptionFactory exceptionFactory; + + @InjectMocks + WaehleranzahlValidator unitUnderTest; + + @Nested + class ValidWahlIdUndWahlbezirkIDOrThrow { + + private final FachlicheWlsException mockedFachlicheWlsException = FachlicheWlsException.withCode("").buildWithMessage(""); + + @Test + void should_throwNoException_when_valid() { + Assertions.assertThatNoException().isThrownBy(() -> unitUnderTest.validWahlIdUndWahlbezirkIDOrThrow(new BezirkUndWahlID("wahlID", "wahlbezirkID"))); + } + + @Test + void should_throwException_when_parameterIsNull() { + Mockito.when(exceptionFactory.createFachlicheWlsException(ExceptionConstants.GETWAHLBETEILIGUNG_SUCHKRITERIEN_UNVOLLSTAENDIG)) + .thenReturn(mockedFachlicheWlsException); + Assertions.assertThatException().isThrownBy(() -> unitUnderTest.validWahlIdUndWahlbezirkIDOrThrow(null)).isSameAs(mockedFachlicheWlsException); + } + + @Test + void should_throwException_when_wahlIDIsNull() { + Mockito.when(exceptionFactory.createFachlicheWlsException(ExceptionConstants.GETWAHLBETEILIGUNG_SUCHKRITERIEN_UNVOLLSTAENDIG)) + .thenReturn(mockedFachlicheWlsException); + Assertions.assertThatException().isThrownBy(() -> unitUnderTest.validWahlIdUndWahlbezirkIDOrThrow(new BezirkUndWahlID(null, "wahlbezirkID"))) + .isSameAs(mockedFachlicheWlsException); + } + + @Test + void should_throwException_when_wahlIDIsEmptyString() { + Mockito.when(exceptionFactory.createFachlicheWlsException(ExceptionConstants.GETWAHLBETEILIGUNG_SUCHKRITERIEN_UNVOLLSTAENDIG)) + .thenReturn(mockedFachlicheWlsException); + Assertions.assertThatException().isThrownBy(() -> unitUnderTest.validWahlIdUndWahlbezirkIDOrThrow(new BezirkUndWahlID("", "wahlbezirkID"))) + .isSameAs(mockedFachlicheWlsException); + } + + @Test + void should_throwException_when_wahlbezirkIDIsNull() { + Mockito.when(exceptionFactory.createFachlicheWlsException(ExceptionConstants.GETWAHLBETEILIGUNG_SUCHKRITERIEN_UNVOLLSTAENDIG)) + .thenReturn(mockedFachlicheWlsException); + Assertions.assertThatException().isThrownBy(() -> unitUnderTest.validWahlIdUndWahlbezirkIDOrThrow(new BezirkUndWahlID("wahlID", null))) + .isSameAs(mockedFachlicheWlsException); + } + + @Test + void should_throwException_when_wahlbezirkIDIsEmptyString() { + Mockito.when(exceptionFactory.createFachlicheWlsException(ExceptionConstants.GETWAHLBETEILIGUNG_SUCHKRITERIEN_UNVOLLSTAENDIG)) + .thenReturn(mockedFachlicheWlsException); + Assertions.assertThatException().isThrownBy(() -> unitUnderTest.validWahlIdUndWahlbezirkIDOrThrow(new BezirkUndWahlID("wahlID", ""))) + .isSameAs(mockedFachlicheWlsException); + } + } + +} diff --git a/wls-monitoring-service/src/test/java/de/muenchen/oss/wahllokalsystem/monitoringservice/utils/Authorities.java b/wls-monitoring-service/src/test/java/de/muenchen/oss/wahllokalsystem/monitoringservice/utils/Authorities.java new file mode 100644 index 000000000..fba20d202 --- /dev/null +++ b/wls-monitoring-service/src/test/java/de/muenchen/oss/wahllokalsystem/monitoringservice/utils/Authorities.java @@ -0,0 +1,24 @@ +package de.muenchen.oss.wahllokalsystem.monitoringservice.utils; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class Authorities { + + public static final String SERVICE_GET_WAEHLERANZAHL = "Monitoring_BUSINESSACTION_GetWahlbeteiligung"; + public static final String SERVICE_POST_WAEHLERANZAHL = "Monitoring_BUSINESSACTION_PostWahlbeteiligung"; + + public static final String REPOSITORY_READ_WAEHLERANZAHL = "Monitoring_READ_Waehleranzahl"; + public static final String REPOSITORY_DELETE_WAEHLERANZAHL = "Monitoring_DELETE_Waehleranzahl"; + public static final String REPOSITORY_WRITE_WAEHLERANZAHL = "Monitoring_WRITE_Waehleranzahl"; + + public static final String[] ALL_AUTHORITIES_GET_WAEHLERANZAHL = new String[] { + SERVICE_GET_WAEHLERANZAHL, + REPOSITORY_READ_WAEHLERANZAHL + }; + public static final String[] ALL_AUTHORITIES_SET_WAEHLERANZAHL = new String[] { + SERVICE_POST_WAEHLERANZAHL, + REPOSITORY_WRITE_WAEHLERANZAHL + }; +} diff --git a/wls-monitoring-service/src/test/resources/application-test.yml b/wls-monitoring-service/src/test/resources/application-test.yml new file mode 100644 index 000000000..7b6942275 --- /dev/null +++ b/wls-monitoring-service/src/test/resources/application-test.yml @@ -0,0 +1,25 @@ +app: + clients: + eai: + basePath: http://localhost:${wiremock.server.port}/ + +spring: + # Spring JPA + h2.console.enabled: true + jpa: + database: H2 + hibernate: + # always drop and create the db should be the best + # configuration for local (development) mode. this + # is also the default, that spring offers by convention. + # but here explicite: + ddl-auto: validate + naming.physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl + # Logging for database operation + show-sql: true + properties: + hibernate: + format_sql: true +wiremock: + server: + port: 0 diff --git a/wls-monitoring-service/src/test/resources/http.request/http-client.env.json b/wls-monitoring-service/src/test/resources/http.request/http-client.env.json new file mode 100644 index 000000000..00eae94a3 --- /dev/null +++ b/wls-monitoring-service/src/test/resources/http.request/http-client.env.json @@ -0,0 +1,14 @@ +{ + "docker": { + "token_type": "", + "auth_token": "", + "WLS_MONITORING_SERVICE_URL": "http://localhost:8201", + "SSO_URL": "http://kubernetes.docker.internal:8100" + }, + "nonDocker": { + "token_type": "", + "auth_token": "", + "WLS_MONITORING_SERVICE_URL": "http://localhost:39154", + "SSO_URL": "http://kubernetes.docker.internal:8100" + } +} \ No newline at end of file diff --git a/wls-monitoring-service/src/test/resources/http.request/waehleranzahl.http b/wls-monitoring-service/src/test/resources/http.request/waehleranzahl.http new file mode 100644 index 000000000..db6c1d12a --- /dev/null +++ b/wls-monitoring-service/src/test/resources/http.request/waehleranzahl.http @@ -0,0 +1,34 @@ +### Get token wls_all +POST {{ SSO_URL }}/auth/realms/wls_realm/protocol/openid-connect/token +Content-Type: application/x-www-form-urlencoded + +password = test & +grant_type = password & +client_secret = top-secret & +client_id = wls & +username = wls_all + +> {% + client.global.set("auth_token", response.body.access_token); + client.global.set("token_type", response.body.token_type); +%} + +### get userinfo with auth_token +GET {{ SSO_URL }}/auth/realms/wls_realm/protocol/openid-connect/userinfo +Authorization: {{ token_type }} {{ auth_token }} + +### get wahleranzahl by wahlID and wahlbezirkID +GET {{ WLS_MONITORING_SERVICE_URL }}/businessActions/wahlbeteiligung/wahlID01/wahlbezirkID01 +Authorization: {{ token_type }} {{ auth_token }} + +### POST waehleranzahl +POST {{ WLS_MONITORING_SERVICE_URL }}/businessActions/wahlbeteiligung/wahlID01/wahlbezirkID01 +Authorization: {{ token_type }} {{ auth_token }} +Content-Type: application/json + +{ + "wahlID": "wahlID01", + "wahlbezirkID": "wahlbezirk01", + "anzahlWaehler": 99, + "uhrzeit": "2024-10-21T23:59:12.123" +}