Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

189 lesen und schreiben des status inklusive update an monitoring service #621

Merged
Show file tree
Hide file tree
Changes from 52 commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
42d2cb5
added monitoring service api and defined client
MrSebastian Dec 3, 2024
9edfe53
add mapstruct
MrSebastian Dec 3, 2024
f867739
add dependency wls-common:security
MrSebastian Dec 3, 2024
50b9c97
use offsetdatetime as localdatetime
MrSebastian Dec 3, 2024
86e87f8
✨ impl of get and post of status - first runnable draft
MrSebastian Dec 3, 2024
2ca3ca3
add example http requests for status
MrSebastian Dec 3, 2024
8317aa9
Merge remote-tracking branch 'origin/185-erfassung-awerte-inklusive-e…
MrSebastian Dec 5, 2024
f2ff99e
move status client to new package
MrSebastian Dec 5, 2024
c996d14
redesign status client interaction checks into small sender classes
MrSebastian Dec 5, 2024
33bf974
🚧 some tests
MrSebastian Dec 5, 2024
049c1b7
created tests for implementation
MrSebastian Dec 5, 2024
e3f2e5c
remove unused class
MrSebastian Dec 6, 2024
fc834cc
bugfixing
MrSebastian Dec 6, 2024
415289d
spotless:apply
MrSebastian Dec 6, 2024
af37894
Merge remote-tracking branch 'origin/189-lesen-und-schreiben-des-stat…
MrSebastian Dec 6, 2024
2cbf88c
update ergebnismeldung service doc with implementation of status
MrSebastian Dec 6, 2024
023da77
add openapi doc annotations to status controller
MrSebastian Dec 6, 2024
299040a
improve formatting: remove empty lines and comments
MrSebastian Dec 6, 2024
8a3f5a2
remove duplicate dummy client impl classes
MrSebastian Dec 6, 2024
57ce0ad
remove unintended reformatting
MrSebastian Dec 6, 2024
22f8d12
revert unintended change
MrSebastian Dec 6, 2024
1f11543
remove abstractRestController
MrSebastian Dec 6, 2024
84eacd0
improve naming
MrSebastian Dec 6, 2024
b672777
clean up code
MrSebastian Dec 6, 2024
66d87da
add noBezirkID check to local profile
MrSebastian Dec 6, 2024
45c35a3
add authorities to keycloak migration
MrSebastian Dec 6, 2024
266551e
finalize status example requests
MrSebastian Dec 6, 2024
1d6f18a
Merge branch 'dev' into 189-lesen-und-schreiben-des-status-inklusive-…
MrSebastian Dec 6, 2024
4aa6252
spotless:apply
MrSebastian Dec 6, 2024
418ecc3
fix: remove response schema for ok result
MrSebastian Dec 6, 2024
f8c4075
add null checks
MrSebastian Dec 6, 2024
92af0fb
improved naming
MrSebastian Dec 6, 2024
7d94fcb
fix testname
MrSebastian Dec 6, 2024
4dfdba3
fix testname
MrSebastian Dec 6, 2024
ff3319d
fix naming
MrSebastian Dec 6, 2024
3b27c09
fix typo in authorities
MrSebastian Dec 6, 2024
a101fb0
fix import of wrong class
MrSebastian Dec 6, 2024
3cb7691
fix typo
MrSebastian Dec 6, 2024
db78eb3
fix: remove response schema on 204 of get
MrSebastian Dec 6, 2024
5a1cf3a
improve test data consistency
MrSebastian Dec 6, 2024
1343fd2
add testcase without niederschrift
MrSebastian Dec 6, 2024
8ca3948
improved and steamlined logmessages of dummy client
MrSebastian Dec 6, 2024
97d44f2
fixed method name
MrSebastian Dec 6, 2024
c7922c0
fix typo
MrSebastian Dec 6, 2024
b045126
fiy typo in validation methods
MrSebastian Dec 6, 2024
f982636
remove empty line
MrSebastian Dec 6, 2024
b103831
fix typo
MrSebastian Dec 6, 2024
0fdf2bb
remove double empty line
MrSebastian Dec 6, 2024
d585333
unify method across sender classes
MrSebastian Dec 6, 2024
6de5817
remove empty line
MrSebastian Dec 6, 2024
6c8750c
fix testname
MrSebastian Dec 6, 2024
86ce520
spotless:apply
MrSebastian Dec 6, 2024
e5dcb7c
combine exists checks that could return false
MrSebastian Dec 9, 2024
4d4c564
remove empty lines
MrSebastian Dec 9, 2024
adc9ae0
fix import: use commons-lang3 instead of wiremock
MrSebastian Dec 9, 2024
e1e577a
👌 - improve naming
MrSebastian Dec 9, 2024
9c4c916
remove empty line
MrSebastian Dec 9, 2024
89b1707
redesign handle of different precision after request handling
MrSebastian Dec 9, 2024
e4e4c2e
Merge branch 'dev' into 189-lesen-und-schreiben-des-status-inklusive-…
MrSebastian Dec 9, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion docs/src/services/ergebnismeldung-service/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,13 @@ Die Methode initialisiereAWerte() initialisiert die Wahlberechtigten (A-Werte) f
diese im lokalen Repository gespeichert.
- Bei Nichterfolg, wird geprüft ob im lokalen Repository wenigstens 'alte' A-Werte existieren.
- Falls weder im externen Wahlsystem noch im lokalen Repository A-Werte gefunden werden
liefert der Service einen Fehler.
liefert der Service einen Fehler.

### Update des Status der Ergebnisermittlung

Bei der Auszählung (Ergebnisermittlung) wird zuerst eine Schnellmeldung vorbereitet. Den Abschluss der Auszählung
bildet die Niederschrift. Die Schnellmeldung und die Niederschrift können gedruckt und übermittelt werden.

Welche Aktionen für die jeweiligen Dokumente bereits in einem Wahllokal vollzogen wurden, wird über den Status gepflegt.

Über Änderungen an dem Status wird auch der [Monitoring-Service](/services/monitoring-service/) informiert.
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
id: add authorities ergebnismeldung status
author: MrSebastian
realm: ${SSO_REALM}
changes:
- addRole:
name: Ergebnismeldung_BUSINESSACTION_GetStatus
clientRole: true
clientId: ${SSO_CLIENT_ID}
- assignRoleToGroup:
group: allErgebnismeldungAuthorities
role: Ergebnismeldung_BUSINESSACTION_GetStatus
clientId: ${SSO_CLIENT_ID}

- addRole:
name: Ergebnismeldung_BUSINESSACTION_PostStatus
clientRole: true
clientId: ${SSO_CLIENT_ID}
- assignRoleToGroup:
group: allErgebnismeldungAuthorities
role: Ergebnismeldung_BUSINESSACTION_PostStatus
clientId: ${SSO_CLIENT_ID}

- addRole:
name: Ergebnismeldung_READ_Status
clientRole: true
clientId: ${SSO_CLIENT_ID}
- assignRoleToGroup:
group: allErgebnismeldungAuthorities
role: Ergebnismeldung_READ_Status
clientId: ${SSO_CLIENT_ID}
- addRole:
name: Ergebnismeldung_WRITE_Status
clientRole: true
clientId: ${SSO_CLIENT_ID}
- assignRoleToGroup:
group: allErgebnismeldungAuthorities
role: Ergebnismeldung_WRITE_Status
clientId: ${SSO_CLIENT_ID}
- addRole:
name: Ergebnismeldung_DELETE_Status
clientRole: true
clientId: ${SSO_CLIENT_ID}
- assignRoleToGroup:
group: allErgebnismeldungAuthorities
role: Ergebnismeldung_DELETE_Status
clientId: ${SSO_CLIENT_ID}
1 change: 1 addition & 0 deletions stack/keycloak/migration/keycloak-changelog.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,4 @@ includes:
- path: add-authorities-wahlvorstand.yml
- path: create-group-all-ergebnismeldung-authorities.yml
- path: add-authorities-ergebnismeldung-awerte.yml
- path: add-authorities-ergebnismeldung-status.yml
68 changes: 48 additions & 20 deletions wls-ergebnismeldung-service/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,13 @@
<artifactId>spring-security-oauth2-jose</artifactId>
</dependency>

<!-- Mapping -->
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>${org.mapstruct.version}</version>
</dependency>

<!-- json etc. -->
<dependency>
<groupId>org.springframework.hateoas</groupId>
Expand Down Expand Up @@ -386,6 +393,36 @@
<groupId>org.openapitools</groupId>
<artifactId>openapi-generator-maven-plugin</artifactId>
<version>7.9.0</version>
<configuration>
<generatorName>java</generatorName>
<library>resttemplate</library>

<generateApiTests>false</generateApiTests>
<generateModelTests>false</generateModelTests>
<generateApiDocumentation>false</generateApiDocumentation>
<generateModelDocumentation>false</generateModelDocumentation>

<configOptions>
<hideGenerationTimestamp>false</hideGenerationTimestamp>
<legacyDiscriminatorBehavior>false</legacyDiscriminatorBehavior>
<generateClientAsBean>true</generateClientAsBean>
<useJakartaEe>true</useJakartaEe>
</configOptions>

<!-- #611 -->
<typeMappings>
<typeMapping>OffsetDateTime=java.time.LocalDateTime</typeMapping>
</typeMappings>
<importMappings>
<importMapping>java.time.OffsetDateTime=java.time.LocalDateTime</importMapping>
</importMappings>
MrSebastian marked this conversation as resolved.
Show resolved Hide resolved

vjohnslhm marked this conversation as resolved.
Show resolved Hide resolved
<globalProperties>
<supportingFiles>
BaseApi.java,ApiClient.java,JavaTimeFormatter.java,Authentication.java,OAuth.java,ApiKeyAuth.java,HttpBasicAuth.java,HttpBearerAuth.java,RFC3339DateFormat.java
</supportingFiles>
</globalProperties>
</configuration>
<executions>
<execution>
<id>generateEAI</id>
Expand All @@ -395,29 +432,20 @@
<configuration>
<inputSpec>${project.basedir}/src/main/resources/openapis/openapi.eai.0.3.0.json</inputSpec>

<generatorName>java</generatorName>
<library>resttemplate</library>

<apiPackage>${project.groupId}.ergebnismeldungservice.eai.aou.client</apiPackage>
<modelPackage>${project.groupId}.ergebnismeldungservice.eai.aou.model</modelPackage>
</configuration>
</execution>
<execution>
<id>generateMonitoring</id>
<goals>
<goal>generate</goal>
</goals>
<configuration>
<inputSpec>${project.basedir}/src/main/resources/openapis/openapi.monitoring.0.1.0.json</inputSpec>

<generateApiTests>false</generateApiTests>
<generateModelTests>false</generateModelTests>
<generateApiDocumentation>false</generateApiDocumentation>
<generateModelDocumentation>false</generateModelDocumentation>

<configOptions>
<hideGenerationTimestamp>false</hideGenerationTimestamp>
<legacyDiscriminatorBehavior>false</legacyDiscriminatorBehavior>
<generateClientAsBean>true</generateClientAsBean>
<useJakartaEe>true</useJakartaEe>
</configOptions>

<globalProperties>
<supportingFiles>
BaseApi.java,ApiClient.java,JavaTimeFormatter.java,Authentication.java,OAuth.java,ApiKeyAuth.java,HttpBasicAuth.java,HttpBearerAuth.java,RFC3339DateFormat.java
</supportingFiles>
</globalProperties>
vjohnslhm marked this conversation as resolved.
Show resolved Hide resolved
<apiPackage>${groupId}.ergebnismeldungservice.eai.monitoring.client</apiPackage>
<modelPackage>${groupId}.ergebnismeldungservice.eai.monitoring.model</modelPackage>
</configuration>
</execution>
</executions>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@
scanBasePackages = {
"org.springframework.data.jpa.convert.threeten",
"de.muenchen.oss.wahllokalsystem.ergebnismeldungservice",
"de.muenchen.oss.wahllokalsystem.wls.common.exception"
"de.muenchen.oss.wahllokalsystem.wls.common.exception",
"de.muenchen.oss.wahllokalsystem.wls.common.security"
}
)
public class MicroServiceApplication {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,46 @@
import de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.configuration.Profiles;
import de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.service.awerte.AWerteClient;
import de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.service.awerte.AWerteModel;
import de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.service.status.sender.StatusClient;
import de.muenchen.oss.wahllokalsystem.wls.common.exception.WlsException;
import de.muenchen.oss.wahllokalsystem.wls.common.security.domain.BezirkUndWahlID;
import java.time.LocalDateTime;
import java.util.List;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Component;

@Component
@Profile(Profiles.DUMMY_CLIENTS)
@Slf4j
public class DummyClientImpl
implements AWerteClient {
implements AWerteClient, StatusClient {

@Override
public List<AWerteModel> getAWerte(final String wahlbezirkID) throws WlsException {
return List.of(
new AWerteModel(new BezirkUndWahlID("wahlID01", wahlbezirkID), 25L, 26L));
}

@Override
public void postSchnellmeldungSendungsuhrzeit(BezirkUndWahlID bezirkUndWahlID, LocalDateTime schnellmeldungSendungsuhrzeit)
throws WlsException {
log.info("dummy client postSchnellmeldungSendungsuhrzeit of {} on {} called instead of monitoring", bezirkUndWahlID, schnellmeldungSendungsuhrzeit);
}

@Override
public void postSchnellmeldungDruckuhrzeit(BezirkUndWahlID bezirkUndWahlID, LocalDateTime schnellmeldungDruckuhrzeit) throws WlsException {
log.info("dummy client postSchnellmeldungDruckuhrzeit of {} on {} called instead of monitoring", bezirkUndWahlID, schnellmeldungDruckuhrzeit);
}

@Override
public void postNiederschriftSendungsuhrzeit(BezirkUndWahlID bezirkUndWahlID, LocalDateTime niederschriftSendungsuhrzeit) throws WlsException {
log.info("dummy client postNiederschriftSendungsuhrzeit of {} on {} called instead of monitoring", bezirkUndWahlID, niederschriftSendungsuhrzeit);
}

@Override
public void postNiederschriftDruckuhrzeit(BezirkUndWahlID bezirkUndWahlID, LocalDateTime niederschriftDruckuhrzeit) throws WlsException {
log.info("dummy client postNiederschriftDruckuhrzeit of {} on {} called instead of monitoring", bezirkUndWahlID, niederschriftDruckuhrzeit);
}

MrSebastian marked this conversation as resolved.
Show resolved Hide resolved
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.client;

import de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.configuration.Profiles;
import de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.eai.monitoring.client.WahllokalZustandControllerApi;
import de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.exception.ExceptionConstants;
import de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.service.status.sender.StatusClient;
import de.muenchen.oss.wahllokalsystem.wls.common.exception.WlsException;
import de.muenchen.oss.wahllokalsystem.wls.common.exception.util.ExceptionFactory;
import de.muenchen.oss.wahllokalsystem.wls.common.security.domain.BezirkUndWahlID;
import java.time.LocalDateTime;
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 MonitoringClient implements StatusClient {

private final WahllokalZustandControllerApi wahllokalZustandControllerApi;
private final StatusClientMapper statusClientMapper;
private final ExceptionFactory exceptionFactory;

@Override
public void postSchnellmeldungSendungsuhrzeit(BezirkUndWahlID bezirkUndWahlID, LocalDateTime schnellmeldungSendungsuhrzeit)
throws WlsException {
val schnellmeldungGesendet = statusClientMapper.toSendungsdatenDTO(bezirkUndWahlID, schnellmeldungSendungsuhrzeit);
callApiWithExceptionMapping(() -> wahllokalZustandControllerApi.postSchnellmeldungSendungsuhrzeit(schnellmeldungGesendet));
}

@Override
public void postSchnellmeldungDruckuhrzeit(BezirkUndWahlID bezirkUndWahlID, LocalDateTime schnellmeldungDruckuhrzeit) throws WlsException {
val schellmeldungGedruckt = statusClientMapper.toDruckdatenDTO(bezirkUndWahlID, schnellmeldungDruckuhrzeit);
callApiWithExceptionMapping(() -> wahllokalZustandControllerApi.postSchnellmeldungDruckuhrzeit(schellmeldungGedruckt));
}

@Override
public void postNiederschriftSendungsuhrzeit(BezirkUndWahlID bezirkUndWahlID, LocalDateTime niederschriftSendungsuhrzeit) throws WlsException {
val niederschriftGesendet = statusClientMapper.toSendungsdatenDTO(bezirkUndWahlID, niederschriftSendungsuhrzeit);
callApiWithExceptionMapping(() -> wahllokalZustandControllerApi.postNiederschriftSendungsuhrzeit(niederschriftGesendet));
}

@Override
public void postNiederschriftDruckuhrzeit(BezirkUndWahlID bezirkUndWahlID, LocalDateTime niederschriftDruckuhrzeit) throws WlsException {
val niederschriftGedruckt = statusClientMapper.toDruckdatenDTO(bezirkUndWahlID, niederschriftDruckuhrzeit);
callApiWithExceptionMapping(() -> wahllokalZustandControllerApi.postNiederschriftDruckuhrzeit(niederschriftGedruckt));
}

private void callApiWithExceptionMapping(final Runnable apiCall) {
try {
apiCall.run();
} catch (final WlsException wlsException) {
log.debug("found WlsException: {}", wlsException.getMessage());
throw wlsException;
} catch (final Exception exception) {
throw exceptionFactory.createTechnischeWlsException(ExceptionConstants.KOMMUNIKATIONSFEHLER_MIT_MONITORING);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.client;

import de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.eai.monitoring.model.DruckdatenDTO;
import de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.eai.monitoring.model.SendungsdatenDTO;
import de.muenchen.oss.wahllokalsystem.wls.common.security.domain.BezirkUndWahlID;
import java.time.LocalDateTime;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;

@Mapper
public interface StatusClientMapper {

@Mapping(target = "druckuhrzeit", source = "dateTimeOfEvent")
DruckdatenDTO toDruckdatenDTO(BezirkUndWahlID bezirkUndWahlID, LocalDateTime dateTimeOfEvent);

@Mapping(target = "sendungsuhrzeit", source = "dateTimeOfEvent")
SendungsdatenDTO toSendungsdatenDTO(BezirkUndWahlID bezirkUndWahlID, LocalDateTime dateTimeOfEvent);
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.configuration;

import de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.eai.aou.ApiClient;
import jakarta.annotation.PostConstruct;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
Expand All @@ -13,10 +12,15 @@ public class BasePathConfiguration {
@Value("${service.config.clients.eai.basePath}")
String eaiBasePath;

private final ApiClient eaiApiClient;
@Value("${service.config.clients.monitoring.basePath}")
String monitoringBasePath;

private final de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.eai.aou.ApiClient eaiApiClient;
private final de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.eai.monitoring.ApiClient monitoringApiClient;

@PostConstruct
public void updateBasePaths() {
eaiApiClient.setBasePath(eaiBasePath);
monitoringApiClient.setBasePath(monitoringBasePath);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.domain.status;

import jakarta.persistence.Embeddable;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.validation.constraints.NotNull;
import java.time.LocalDateTime;
import lombok.Data;

@Embeddable
@Data
public class Meldung {
MrSebastian marked this conversation as resolved.
Show resolved Hide resolved

@Enumerated(EnumType.STRING)
@NotNull
private Validierungsstatus validierungsstatus;

@NotNull
private boolean gedruckt;

private Boolean uebermittelt;

private LocalDateTime sendeuhrzeit;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package de.muenchen.oss.wahllokalsystem.ergebnismeldungservice.domain.status;

import de.muenchen.oss.wahllokalsystem.wls.common.security.domain.BezirkUndWahlID;
import jakarta.persistence.AttributeOverride;
import jakarta.persistence.AttributeOverrides;
import jakarta.persistence.Column;
import jakarta.persistence.Embedded;
import jakarta.persistence.EmbeddedId;
import jakarta.persistence.Entity;
import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Entity
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Status {

@EmbeddedId
private BezirkUndWahlID bezirkUndWahlID;

@Embedded
@NotNull
@AttributeOverrides(
{
@AttributeOverride(name = "validierungsstatus", column = @Column(name = "schnellmeldungValidierungsstatus")),
@AttributeOverride(name = "gedruckt", column = @Column(name = "schnellmeldungGedruckt")),
@AttributeOverride(name = "uebermittelt", column = @Column(name = "schnellmeldungUebermittelt")),
@AttributeOverride(name = "sendeuhrzeit", column = @Column(name = "schnellmeldungSendeuhrzeit")),
}
)
private Meldung schnellmeldung;

@Embedded
@NotNull
@AttributeOverrides(
{
@AttributeOverride(name = "validierungsstatus", column = @Column(name = "niederschriftValidierungsstatus")),
@AttributeOverride(name = "gedruckt", column = @Column(name = "niederschriftGedruckt")),
@AttributeOverride(name = "uebermittelt", column = @Column(name = "niederschriftUebermittelt")),
@AttributeOverride(name = "sendeuhrzeit", column = @Column(name = "niederschriftSendeuhrzeit")),
}
)
private Meldung niederschrift;
}
Loading
Loading