diff --git a/build.gradle b/build.gradle index f709a08..f9441b6 100644 --- a/build.gradle +++ b/build.gradle @@ -25,9 +25,9 @@ ext { onkostarVersion = '2.14.1' springVersion = '4.3.8.RELEASE' slf4jVersion = '1.7.2' - junitVersion = '5.12.1' + junitVersion = '5.13.3' assertjVersion = '3.27.3' - mockitoVersion = '5.16.1' + mockitoVersion = '5.18.0' } dependencies { @@ -35,11 +35,14 @@ dependencies { implementation "org.springframework:spring-beans:${springVersion}" implementation "org.springframework:spring-context:${springVersion}" + implementation "org.springframework:spring-web:${springVersion}" implementation "org.springframework:spring-jdbc:${springVersion}" implementation "org.slf4j:slf4j-api:${slf4jVersion}" implementation("dev.pcvolkmer.onco:mv64e-onkostar-data:0.1.0-SNAPSHOT") { changing = true } + testRuntimeOnly 'org.junit.platform:junit-platform-launcher:1.13.3' + testImplementation "org.springframework:spring-test:${springVersion}" testImplementation "org.junit.jupiter:junit-jupiter-engine:${junitVersion}" testImplementation "org.junit.jupiter:junit-jupiter-params:${junitVersion}" diff --git a/src/main/java/dev/pcvolkmer/onco/dnpmexport/ExportAnalyzer.java b/src/main/java/dev/pcvolkmer/onco/dnpmexport/ExportAnalyzer.java index 2ef53e5..87acea9 100644 --- a/src/main/java/dev/pcvolkmer/onco/dnpmexport/ExportAnalyzer.java +++ b/src/main/java/dev/pcvolkmer/onco/dnpmexport/ExportAnalyzer.java @@ -6,12 +6,19 @@ import de.itc.onkostar.api.Procedure; import de.itc.onkostar.api.analysis.AnalyzerRequirement; import de.itc.onkostar.api.analysis.IProcedureAnalyzer; import de.itc.onkostar.api.analysis.OnkostarPluginType; +import dev.pcvolkmer.mv64e.mtb.Mtb; import dev.pcvolkmer.onco.datamapper.mapper.MtbDataMapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; import org.springframework.stereotype.Component; +import org.springframework.web.client.RestClientException; +import org.springframework.web.client.RestTemplate; -import javax.sql.DataSource; +import java.net.URI; +import java.util.Base64; /** * Analyzer to start export if DNPM Klinik/Anamnese or DNPM Therapieplan form gets locked @@ -26,11 +33,16 @@ public class ExportAnalyzer implements IProcedureAnalyzer { private final IOnkostarApi onkostarApi; private final MtbDataMapper mtbDataMapper; + private final RestTemplate restTemplate; - public ExportAnalyzer(final IOnkostarApi onkostarApi, final DataSource dataSource) { + public ExportAnalyzer( + final IOnkostarApi onkostarApi, + final MtbDataMapper mtbDataMapper, + final RestTemplate restTemplate + ) { this.onkostarApi = onkostarApi; - // Reuse default Onkostar DataSource for MtbDataMapper - this.mtbDataMapper = MtbDataMapper.create(dataSource); + this.mtbDataMapper = mtbDataMapper; + this.restTemplate = restTemplate; } @Override @@ -79,28 +91,55 @@ public class ExportAnalyzer implements IProcedureAnalyzer { logger.info("Starting export for procedure {}", procedure.getId()); try { - String caseId = ""; + String caseId; switch (procedure.getFormName()) { case "DNPM Klinik/Anamnese": - caseId = procedure.getValue("FallnummrMV").getString(); + caseId = procedure.getValue("FallnummerMV").getString(); break; case "DNPM Therapieplan": var kpaProcedure = onkostarApi.getProcedure( procedure.getValue("ref_dnpm_klinikanamnese").getInt() ); - caseId = kpaProcedure.getValue("FallnummrMV").getString(); + caseId = kpaProcedure.getValue("FallnummerMV").getString(); break; default: logger.info("Cannot handle procedure form {}", procedure.getFormName()); return; } - var mtb = mtbDataMapper.getByCaseId(caseId); - // TODO: Send MTB data to remote application + var mtb = mtbDataMapper.getByCaseId(caseId); + sendMtbFileRequest(mtb); } catch (Exception e) { logger.error("Could export mtb data for procedure {}", procedure.getId(), e); } } + private void sendMtbFileRequest(Mtb mtb) { + var exportUrl = onkostarApi.getGlobalSetting("dnpmexport_url"); + + try { + var uri = URI.create(exportUrl); + var headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + if (uri.getUserInfo() != null) { + headers.set(HttpHeaders.AUTHORIZATION, "Basic " + Base64.getEncoder().encodeToString(uri.getUserInfo().getBytes())); + } + + var entityReq = new HttpEntity<>(mtb, headers); + + var r = restTemplate.postForEntity(uri, entityReq, String.class); + if (!r.getStatusCode().is2xxSuccessful()) { + logger.warn("Error sending to remote system: {}", r.getBody()); + throw new RuntimeException("Kann Daten nicht an das externe System senden"); + } + } catch (IllegalArgumentException e) { + logger.error("Not a valid URI to export to: '{}'", exportUrl); + throw new RuntimeException("Keine gültige Adresse für das externe System"); + } catch (RestClientException e) { + logger.error("Cannot send data to remote system", e); + throw new RuntimeException("Kann Daten nicht an das externe System senden"); + } + } + } diff --git a/src/main/java/dev/pcvolkmer/onco/dnpmexport/ExportAnalyzerConfig.java b/src/main/java/dev/pcvolkmer/onco/dnpmexport/ExportAnalyzerConfig.java new file mode 100644 index 0000000..de2d052 --- /dev/null +++ b/src/main/java/dev/pcvolkmer/onco/dnpmexport/ExportAnalyzerConfig.java @@ -0,0 +1,18 @@ +package dev.pcvolkmer.onco.dnpmexport; + +import dev.pcvolkmer.onco.datamapper.mapper.MtbDataMapper; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import javax.sql.DataSource; + +@Configuration +public class ExportAnalyzerConfig { + + @Bean + public MtbDataMapper mtbDataMapper(final DataSource dataSource) { + // Reuse default Onkostar DataSource for MtbDataMapper + return MtbDataMapper.create(dataSource); + } + +} diff --git a/src/test/java/dev/pcvolkmer/onco/dnpmexport/ExportAnalyzerTest.java b/src/test/java/dev/pcvolkmer/onco/dnpmexport/ExportAnalyzerTest.java index b57ef6a..7d1b4b2 100644 --- a/src/test/java/dev/pcvolkmer/onco/dnpmexport/ExportAnalyzerTest.java +++ b/src/test/java/dev/pcvolkmer/onco/dnpmexport/ExportAnalyzerTest.java @@ -1,34 +1,121 @@ package dev.pcvolkmer.onco.dnpmexport; import de.itc.onkostar.api.IOnkostarApi; +import de.itc.onkostar.api.Item; +import de.itc.onkostar.api.Procedure; +import dev.pcvolkmer.mv64e.mtb.Mtb; +import dev.pcvolkmer.onco.datamapper.mapper.MtbDataMapper; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.http.ResponseEntity; +import org.springframework.web.client.RestTemplate; -import javax.sql.DataSource; +import java.net.URI; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.*; @ExtendWith(MockitoExtension.class) class ExportAnalyzerTest { private IOnkostarApi onkostarApi; - private DataSource dataSource; + private MtbDataMapper mtbDataMapper; + private RestTemplate restTemplate; private ExportAnalyzer analyzer; @BeforeEach void setup( @Mock IOnkostarApi onkostarApi, - @Mock DataSource dataSource + @Mock MtbDataMapper mtbDataMapper, + @Mock RestTemplate restTemplate ) { this.onkostarApi = onkostarApi; - this.dataSource = dataSource; - this.analyzer = new ExportAnalyzer(onkostarApi, dataSource); + this.mtbDataMapper = mtbDataMapper; + this.restTemplate = restTemplate; + this.analyzer = new ExportAnalyzer(onkostarApi, mtbDataMapper, restTemplate); + + doAnswer(invocationOnMock -> { + var name = invocationOnMock.getArgument(0, String.class); + return defaultSetting(name); + }).when(this.onkostarApi).getGlobalSetting(anyString()); + } + + private static String defaultSetting(String name) { + switch (name) { + case "dnpmexport_url": + return "http://localhost:9000/mtb/etl/patient-record"; + case "dnpmexport_prefix": + return "TEST"; + default: + return null; + } } @Test - void testShouldTestSomeAnalyzerImplementation() { - // Implement your first test + void shouldExtractMtbDataForKlinikAnamnese() { + when(mtbDataMapper.getByCaseId(anyString())).thenReturn(Mtb.builder().build()); + when(this.restTemplate.postForEntity(any(URI.class), any(), any())).thenReturn(ResponseEntity.accepted().build()); + + var procedure = new Procedure(onkostarApi); + procedure.setId(1); + procedure.setFormName("DNPM Klinik/Anamnese"); + procedure.setValue("FallnummerMV", new Item("FallnummerMV", "1600012345")); + + this.analyzer.analyze(procedure, null); + + var caseIdCaptor = ArgumentCaptor.forClass(String.class); + verify(mtbDataMapper, times(1)).getByCaseId(caseIdCaptor.capture()); + assertThat(caseIdCaptor.getValue()).isEqualTo("1600012345"); + } + + @Test + void shouldExtractMtbDataForTherapieplan() { + doAnswer(invocationOnMock -> { + var procedure = new Procedure(onkostarApi); + procedure.setId(1); + procedure.setFormName("DNPM Klinik/Anamnese"); + procedure.setValue("FallnummerMV", new Item("FallnummerMV", "1600012345")); + return procedure; + }).when(this.onkostarApi).getProcedure(anyInt()); + + when(mtbDataMapper.getByCaseId(anyString())).thenReturn(Mtb.builder().build()); + when(this.restTemplate.postForEntity(any(URI.class), any(), any())).thenReturn(ResponseEntity.accepted().build()); + + var procedure = new Procedure(onkostarApi); + procedure.setId(2); + procedure.setFormName("DNPM Therapieplan"); + procedure.setValue("ref_dnpm_klinikanamnese", new Item("ref_dnpm_klinikanamnese", 1)); + + this.analyzer.analyze(procedure, null); + + var kpaIdCaptor = ArgumentCaptor.forClass(Integer.class); + verify(onkostarApi, times(1)).getProcedure(kpaIdCaptor.capture()); + assertThat(kpaIdCaptor.getValue()).isEqualTo(1); + + var caseIdCaptor = ArgumentCaptor.forClass(String.class); + verify(mtbDataMapper, times(1)).getByCaseId(caseIdCaptor.capture()); + assertThat(caseIdCaptor.getValue()).isEqualTo("1600012345"); + } + + @Test + void shouldSendHttpRequestWithMtbData() { + when(mtbDataMapper.getByCaseId(anyString())).thenReturn(Mtb.builder().build()); + when(this.restTemplate.postForEntity(any(URI.class), any(), any())).thenReturn(ResponseEntity.accepted().build()); + + var procedure = new Procedure(onkostarApi); + procedure.setId(1); + procedure.setFormName("DNPM Klinik/Anamnese"); + procedure.setValue("FallnummerMV", new Item("FallnummerMV", "1600012345")); + + this.analyzer.analyze(procedure, null); + + verify(restTemplate, times(1)).postForEntity(any(URI.class), any(), any()); } }