Skip to content

Commit

Permalink
feat: integrates the HD dictionary file from RKI and a JSON-RPC servi…
Browse files Browse the repository at this point in the history
…ce to search a hd for a zip code

* Adds the XML `TransmittingSiteSearchText.xml` from RKI reformatted to the resources.
* Adds a JSON-RPC service to search a hd for a zip code. The new methode is `searchForHd`.
* Collect the data files (XML and `central-configuration.json`) in `masterdata`.
* Extends the central configuration for the `rkiCode` and reorganize the configuration to separate the concerns.
* Uses XML Beam to read the XML file and adds this to the dependencies.
* Also adds a Github workflow to download the XML periodically and create a pull request.

Refs iris-connect/iris-backlog#263
PR #263
  • Loading branch information
jekutzsche authored Jan 18, 2022
1 parent b6bae50 commit cd7a052
Show file tree
Hide file tree
Showing 23 changed files with 65,895 additions and 39 deletions.
80 changes: 80 additions & 0 deletions .github/workflows/fetch-rki-xml.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
name: Fetch RKI XML

on:
workflow_dispatch:
schedule:
- cron: '30 2 * * 1'

jobs:
fetch_rki_xml:
name: Fetch RKI XML
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/[email protected]

- name: Download and Unzip
uses: schmiddim/action-wget-unzip@v2
with:
url: 'https://survnet.rki.de/Content/Service/DownloadHandler.ashx?Id=82'
destination: 'iris-backend-service/src/main/resources/masterdata'

- name: Fix encoding and formatting
uses: pipeline-components/[email protected]
with:
directory: iris-backend-service/src/main/resources/masterdata/TransmittingSiteSearchText.xml
options: --format --encode utf8 -o iris-backend-service/src/main/resources/masterdata/TransmittingSiteSearchText.xml

- name: Setup Java
uses: actions/setup-java@v2
with:
distribution: 'liberica'
java-version: '17'
cache: 'maven'

- name: build and test backend service
run: mvn -B clean verify -am -pl iris-backend-service

- name: Upload Test Reports
if: always()
uses: actions/upload-artifact@v2
with:
name: test-reports
path: |
**/surefire-reports/*.xml
**/failsafe-reports/*.xml
!**/failsafe-reports/failsafe-summary.xml
- name: Create Pull Request
uses: peter-evans/create-pull-request@v3
with:
add-paths: iris-backend-service/src/main/resources/masterdata/TransmittingSiteSearchText.xml
commit-message: 'fix: updates the RKI XML for HD search'
branch: chore/update_rki_xml
delete-branch: true
title: 'Updates the RKI XML for HD search'
body: |
Updates the RKI XML for HD search with the current version from
https://survnet.rki.de/Content/Service/DownloadHandler.ashx?Id=82
Auto-generated by [create-pull-request]
publish-test-results:
name: Publish Test Results
needs: fetch_rki_xml
runs-on: ubuntu-latest
# the build-and-test job might be skipped, we don't need to run this job then
if: success() || failure()
steps:
- name: Download Artifacts
uses: actions/download-artifact@v2
with:
name: test-reports
path: reports

- name: Publish Test Results
uses: EnricoMi/publish-unit-test-result-action@v1
with:
files: reports/**/*.xml
report_individual_runs: true
check_run_annotations_branch: "*"
8 changes: 8 additions & 0 deletions iris-backend-service/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
<docker.image.prefix>inoeg</docker.image.prefix>
<version.tag>${project.version}</version.tag>
<hibernate.search.version>6.0.7.Final</hibernate.search.version>
<xmlprojector.version>1.4.20</xmlprojector.version>
</properties>
<dependencies>
<dependency>
Expand Down Expand Up @@ -121,6 +122,13 @@
<version>2.17.1</version>
</dependency>

<!-- For XML projection of RKI PLZTool data -->
<dependency>
<groupId>org.xmlbeam</groupId>
<artifactId>xmlprojector</artifactId>
<version>${xmlprojector.version}</version>
</dependency>

<!-- jsonrpc4j -->
<dependency>
<groupId>com.github.briandilley.jsonrpc4j</groupId>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package iris.backend_service.config;

import iris.backend_service.configurations.ConfigurationRpcService;
import iris.backend_service.hd_search.HdSearchRpcService;
import iris.backend_service.jsonrpc.HealthRPC;
import iris.backend_service.locations.jsonrpc.LocationRPC;
import iris.backend_service.messages.MessageRPC;
Expand All @@ -21,12 +22,13 @@ public class ServiceEndpointsConfig {
private final LocationRPC locations;
private final MessageRPC alerts;
private final ConfigurationRpcService configurations;
private final HdSearchRpcService hdSearch;

@Bean(name = ENDPOINT)
public CompositeJsonServiceExporter jsonRpcServiceImplExporter() {

var compositeJsonServiceExporter = new CompositeJsonServiceExporter();
compositeJsonServiceExporter.setServices(new Object[] { health, locations, alerts, configurations });
compositeJsonServiceExporter.setServices(new Object[] { health, locations, alerts, configurations, hdSearch });

return compositeJsonServiceExporter;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public class CentralConfiguration {

private final @NonNull ObjectMapper objectMapper;

@Value("classpath:central-configuration.json")
@Value("classpath:masterdata/central-configuration.json")
Resource configBaseResource;

@Value("${central-configuration.profil_resource}")
Expand All @@ -42,15 +42,32 @@ private void initialize() throws IOException {
configurationData = updater.readValue(configProfilResource.getInputStream());
}

Optional<HdProxyConfig> getHdProxyConfigFor(String client) {
public Optional<String> getHdNameFor(String rkiCode) {

return Optional.ofNullable(configurationData.hdProxyConfigs.get(client));
return configurationData.hdConfigs.values().stream()
.filter(it -> rkiCode.equals(it.rkiCode))
.findFirst()
.map(HdConfig::getHdName);
}

record ConfigurationData(@JsonMerge Map<String, HdProxyConfig> hdProxyConfigs) {}
Optional<ProxyConfig> getHdProxyConfigFor(String client) {

return Optional.ofNullable(configurationData.hdConfigs.get(client))
.map(HdConfig::getProxyConfig);
}

record ConfigurationData(@JsonMerge Map<String, HdConfig> hdConfigs) {}

@Data
static class HdConfig {

String hdName;
String rkiCode;
ProxyConfig proxyConfig;
}

@Data
static class HdProxyConfig {
static class ProxyConfig {

String abbreviation;
String proxySubDomain;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import iris.backend_service.jsonrpc.JsonRpcClientDto;
import iris.backend_service.messages.AlertService;
import lombok.AllArgsConstructor;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import javax.validation.Valid;
Expand All @@ -13,7 +13,7 @@
import org.springframework.stereotype.Service;

@Service
@AllArgsConstructor
@RequiredArgsConstructor
@Slf4j
class ConfigurationRpcServieImpl implements ConfigurationRpcService {

Expand All @@ -24,7 +24,7 @@ class ConfigurationRpcServieImpl implements ConfigurationRpcService {
@Override
public Configuration getHdConfiguration(@Valid JsonRpcClientDto client) {

var hdProxyConfig = config.getHdProxyConfigFor(client.getName())
var proxyConfig = config.getHdProxyConfigFor(client.getName())
.orElseThrow(() -> {

alerts.createAlertMessage("Missing HD configuration",
Expand All @@ -36,7 +36,7 @@ public Configuration getHdConfiguration(@Valid JsonRpcClientDto client) {

log.debug("JSON-RPC - Get HD configuration for client: {}", client.getName());

return new Configuration(hdProxyConfig.getAbbreviation(), hdProxyConfig.getProxySubDomain(),
tokenProps.getCatSalt(), tokenProps.getDatSalt(), tokenProps.getCatLength(), tokenProps.getDatLength());
return new Configuration(proxyConfig.getAbbreviation(), proxyConfig.getProxySubDomain(), tokenProps.getCatSalt(),
tokenProps.getDatSalt(), tokenProps.getCatLength(), tokenProps.getDatLength());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package iris.backend_service.hd_search;

import iris.backend_service.jsonrpc.JsonRpcClientDto;

import java.util.List;

import javax.validation.Valid;

import com.googlecode.jsonrpc4j.JsonRpcParam;

public interface HdSearchRpcService {

List<HealthDepartmentDto> searchForHd(@Valid @JsonRpcParam(value = "_client") JsonRpcClientDto client,
@JsonRpcParam(value = "searchKeyword") String searchKeyword);

record HealthDepartmentDto(String name,
String rkiCode,
String epsName,
String department,
Address address,
ContactData contactData,
ContactData covid19ContactData,
ContactData entryContactData) {}

record Address(String street, String zipCode, String city) {}

record ContactData(String phone, String fax, String eMail) {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package iris.backend_service.hd_search;

import iris.backend_service.configurations.CentralConfiguration;
import iris.backend_service.hd_search.HealthDepartments.HealthDepartment;
import iris.backend_service.jsonrpc.JsonRpcClientDto;
import iris.backend_service.messages.AlertService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import java.util.List;

import javax.validation.Valid;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;

import org.springframework.stereotype.Service;

@Service
@RequiredArgsConstructor
@Slf4j
class HdSearchRpcServieImpl implements HdSearchRpcService {

private final @NotNull HealthDepartments healthDepartments;
private final @NotNull CentralConfiguration config;
private final @NotNull AlertService alerts;

@Override
public List<HealthDepartmentDto> searchForHd(@Valid JsonRpcClientDto client, @NotEmpty String searchKeyword) {

var hds = healthDepartments.findDepartmentsContains(searchKeyword);

log.debug("JSON-RPC - Search HD for: {} ⇒ found: {}", searchKeyword, hds.size());

return hds.stream().map(this::mapToDto).toList();
}

private HealthDepartmentDto mapToDto(HealthDepartment hd) {

var epsName = config.getHdNameFor(hd.getCode()).orElse(null);

var address = new Address(hd.getAddress().getStreet(), hd.getAddress().getZipCode(), hd.getAddress().getPlace());
var contact = new ContactData(hd.getPhone(), hd.getFax(), hd.getEmail());
var covidContact = new ContactData(hd.getCovid19ContactData().getHotline(), hd.getCovid19ContactData().getFax(),
hd.getCovid19ContactData().getEmail());
var entryContact = new ContactData(hd.getEinreiseContactData().getHotline(),
hd.getEinreiseContactData().getFax(),
hd.getEinreiseContactData().getEmail());

return new HealthDepartmentDto(hd.getName(), hd.getCode(), epsName, hd.getDepartment(), address, contact,
covidContact, entryContact);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package iris.backend_service.hd_search;

import java.util.List;
import java.util.Optional;

import org.xmlbeam.annotation.XBDocURL;
import org.xmlbeam.annotation.XBRead;

@XBDocURL("resource://masterdata/TransmittingSiteSearchText.xml")
public interface HealthDepartments {

// Without NamespacePhilosophy.AGNOSTIC the namespace of an element must be declared!
// @XBRead("/xbdefaultns:TransmittingSites/xbdefaultns:TransmittingSite")
@XBRead("/TransmittingSites/TransmittingSite")
List<HealthDepartment> getAll();

@XBRead("//SearchText[@Value='{0}']/..")
Optional<HealthDepartment> findDepartmentWithExact(String searchString);

@XBRead("boolean(//SearchText[@Value='{0}'])")
boolean hasDepartmentWithExact(String searchString);

@XBRead("//SearchText[contains(@Value, '{0}')]/..")
List<HealthDepartment> findDepartmentsContains(String searchString);

public interface HealthDepartment {
/**
* @return Often the administrative unit (e.g. the county)
*/
@XBRead("@Name")
String getName();

/**
* @return The code from RKI
*/
@XBRead("@Code")
String getCode();

/**
* @return The department of the administrative unit.
*/
@XBRead("@Department")
String getDepartment();

@XBRead(".")
Address getAddress();

@XBRead("@Phone")
String getPhone();

@XBRead("@Fax")
String getFax();

@XBRead("@Email")
String getEmail();

@XBRead(".")
Covid19ContactData getCovid19ContactData();

@XBRead(".")
EinreiseContactData getEinreiseContactData();

@XBRead("SearchText/@Value")
List<String> getSearchTexts();

public interface Address {
@XBRead("@Street")
String getStreet();

@XBRead("@Postalcode")
String getZipCode();

@XBRead("@Place")
String getPlace();
}

public interface Covid19ContactData {

@XBRead("@Covid19Hotline")
String getHotline();

@XBRead("@Covid19Fax")
String getFax();

@XBRead("@Covid19EMail")
String getEmail();
}

public interface EinreiseContactData {

@XBRead("@EinreiseAnmeldungHotline")
String getHotline();

@XBRead("@EinreiseAnmeldungFax")
String getFax();

@XBRead("@EinreiseAnmeldungEMail")
String getEmail();
}
}
}
Loading

0 comments on commit cd7a052

Please sign in to comment.