diff --git a/codex-process-data-transfer/pom.xml b/codex-process-data-transfer/pom.xml index 364a6523..efd17551 100644 --- a/codex-process-data-transfer/pom.xml +++ b/codex-process-data-transfer/pom.xml @@ -65,6 +65,17 @@ mockito-core test + + org.mockito + mockito-junit-jupiter + test + + + org.springframework + spring-test + 6.0.11 + test + org.apache.logging.log4j log4j-slf4j2-impl @@ -102,7 +113,7 @@ false - + @@ -263,4 +274,4 @@ - \ No newline at end of file + diff --git a/codex-process-data-transfer/src/main/java/de/netzwerk_universitaetsmedizin/codex/processes/data_transfer/ConstantsDataTransfer.java b/codex-process-data-transfer/src/main/java/de/netzwerk_universitaetsmedizin/codex/processes/data_transfer/ConstantsDataTransfer.java index c6bd6309..0a6cc660 100644 --- a/codex-process-data-transfer/src/main/java/de/netzwerk_universitaetsmedizin/codex/processes/data_transfer/ConstantsDataTransfer.java +++ b/codex-process-data-transfer/src/main/java/de/netzwerk_universitaetsmedizin/codex/processes/data_transfer/ConstantsDataTransfer.java @@ -43,6 +43,7 @@ public interface ConstantsDataTransfer String CODESYSTEM_NUM_CODEX_DATA_TRANSFER_VALUE_EXPORT_TO = "export-to"; String CODESYSTEM_NUM_CODEX_DATA_TRANSFER_VALUE_DATA_REFERENCE = "data-reference"; String CODESYSTEM_NUM_CODEX_DATA_TRANSFER_VALUE_DRY_RUN = "dry-run"; + String CODESYSTEM_NUM_CODEX_DATA_TRANSFER_VALUE_STUDY_ID = "study-id"; String CODESYSTEM_NUM_CODEX_DATA_TRANSFER_VALUE_ENCRYPTED_BUNDLE_SIZE = "encrypted-bundle-size"; String CODESYSTEM_NUM_CODEX_DATA_TRANSFER_VALUE_LOCAL_VALIDATION_SUCCESSFUL = "local-validation-successful"; String CODESYSTEM_NUM_CODEX_DATA_TRANSFER_VALUE_ENCRYPTED_BUNDLE_RESOURCES_COUNT = "encrypted-bundle-resources-count"; diff --git a/codex-process-data-transfer/src/main/java/de/netzwerk_universitaetsmedizin/codex/processes/data_transfer/DataTransferProcessPluginDefinition.java b/codex-process-data-transfer/src/main/java/de/netzwerk_universitaetsmedizin/codex/processes/data_transfer/DataTransferProcessPluginDefinition.java index 85965d8c..f6b76bfa 100644 --- a/codex-process-data-transfer/src/main/java/de/netzwerk_universitaetsmedizin/codex/processes/data_transfer/DataTransferProcessPluginDefinition.java +++ b/codex-process-data-transfer/src/main/java/de/netzwerk_universitaetsmedizin/codex/processes/data_transfer/DataTransferProcessPluginDefinition.java @@ -5,14 +5,7 @@ import java.util.List; import java.util.Map; -import de.netzwerk_universitaetsmedizin.codex.processes.data_transfer.spring.config.ProcessPluginDeploymentConfig; -import de.netzwerk_universitaetsmedizin.codex.processes.data_transfer.spring.config.ReceiveConfig; -import de.netzwerk_universitaetsmedizin.codex.processes.data_transfer.spring.config.SendConfig; -import de.netzwerk_universitaetsmedizin.codex.processes.data_transfer.spring.config.TransferDataConfig; -import de.netzwerk_universitaetsmedizin.codex.processes.data_transfer.spring.config.TransferDataSerializerConfig; -import de.netzwerk_universitaetsmedizin.codex.processes.data_transfer.spring.config.TranslateConfig; -import de.netzwerk_universitaetsmedizin.codex.processes.data_transfer.spring.config.TriggerConfig; -import de.netzwerk_universitaetsmedizin.codex.processes.data_transfer.spring.config.ValidationConfig; +import de.netzwerk_universitaetsmedizin.codex.processes.data_transfer.spring.config.*; import dev.dsf.bpe.v1.ProcessPluginDefinition; public class DataTransferProcessPluginDefinition implements ProcessPluginDefinition @@ -49,7 +42,7 @@ public List> getSpringConfigurations() { return List.of(TransferDataConfig.class, TransferDataSerializerConfig.class, ValidationConfig.class, TriggerConfig.class, SendConfig.class, TranslateConfig.class, ReceiveConfig.class, - ProcessPluginDeploymentConfig.class); + ReceiveDataStoreConfig.class, ProcessPluginDeploymentConfig.class); } @Override diff --git a/codex-process-data-transfer/src/main/java/de/netzwerk_universitaetsmedizin/codex/processes/data_transfer/client/DataStoreClientFactory.java b/codex-process-data-transfer/src/main/java/de/netzwerk_universitaetsmedizin/codex/processes/data_transfer/client/DataStoreClientFactory.java index a2ebd6cb..5dfdb2ad 100644 --- a/codex-process-data-transfer/src/main/java/de/netzwerk_universitaetsmedizin/codex/processes/data_transfer/client/DataStoreClientFactory.java +++ b/codex-process-data-transfer/src/main/java/de/netzwerk_universitaetsmedizin/codex/processes/data_transfer/client/DataStoreClientFactory.java @@ -9,6 +9,7 @@ import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; +import java.util.Map; import java.util.UUID; import org.bouncycastle.pkcs.PKCSException; @@ -20,6 +21,7 @@ import de.netzwerk_universitaetsmedizin.codex.processes.data_transfer.client.fhir.DataStoreFhirClient; import de.netzwerk_universitaetsmedizin.codex.processes.data_transfer.client.fhir.DataStoreFhirClientStub; import de.netzwerk_universitaetsmedizin.codex.processes.data_transfer.logging.DataLogger; +import de.netzwerk_universitaetsmedizin.codex.processes.data_transfer.spring.config.ReceiveDataStoreConfig; import de.rwh.utils.crypto.CertificateHelper; import de.rwh.utils.crypto.io.CertificateReader; import de.rwh.utils.crypto.io.PemIo; @@ -27,6 +29,7 @@ public class DataStoreClientFactory { private static final Logger logger = LoggerFactory.getLogger(DataStoreClientFactory.class); + private static final String DEFAULT_DATA_STORE = "default"; private static final class DataStoreClientStub implements DataStoreClient { @@ -91,10 +94,7 @@ public boolean shouldUseChainedParameterNotLogicalReference() private final int socketTimeout; private final int connectionRequestTimeout; - private final String dataStoreServerBase; - private final String dataStoreServerBasicAuthUsername; - private final String dataStoreServerBasicAuthPassword; - private final String dataStoreServerBearerToken; + private final Map dataStoreConnectionMap; private final String proxyUrl; private final String proxyUsername; @@ -112,7 +112,8 @@ public boolean shouldUseChainedParameterNotLogicalReference() public DataStoreClientFactory(Path trustStorePath, Path certificatePath, Path privateKeyPath, char[] privateKeyPassword, int connectTimeout, int socketTimeout, int connectionRequestTimeout, String dataStoreServerBase, String dataStoreServerBasicAuthUsername, - String dataStoreServerBasicAuthPassword, String dataStoreServerBearerToken, String proxyUrl, + String dataStoreServerBasicAuthPassword, String dataStoreServerBearerToken, + Map dataStoreConnectionMap, String proxyUrl, String proxyUsername, String proxyPassword, boolean hapiClientVerbose, FhirContext fhirContext, Path searchBundleOverride, Class dataStoreFhirClientClass, boolean useChainedParameterNotLogicalReference, DataLogger dataLogger) @@ -126,10 +127,11 @@ public DataStoreClientFactory(Path trustStorePath, Path certificatePath, Path pr this.socketTimeout = socketTimeout; this.connectionRequestTimeout = connectionRequestTimeout; - this.dataStoreServerBase = dataStoreServerBase; - this.dataStoreServerBasicAuthUsername = dataStoreServerBasicAuthUsername; - this.dataStoreServerBasicAuthPassword = dataStoreServerBasicAuthPassword; - this.dataStoreServerBearerToken = dataStoreServerBearerToken; + this.dataStoreConnectionMap = dataStoreConnectionMap; + this.dataStoreConnectionMap.put(DEFAULT_DATA_STORE, + new ReceiveDataStoreConfig.DataStoreConnectionValues(dataStoreServerBase, + dataStoreServerBasicAuthUsername, dataStoreServerBasicAuthPassword, + dataStoreServerBearerToken)); this.proxyUrl = proxyUrl; this.proxyUsername = proxyUsername; @@ -146,21 +148,25 @@ public DataStoreClientFactory(Path trustStorePath, Path certificatePath, Path pr public String getServerBase() { - return dataStoreServerBase; + return dataStoreConnectionMap.get(DEFAULT_DATA_STORE).getBaseUrl(); } public void testConnection() { try { - logger.info( - "Testing connection to Data Store FHIR server with {trustStorePath: {}, certificatePath: {}, privateKeyPath: {}, privateKeyPassword: {}," - + " basicAuthUsername: {}, basicAuthPassword: {}, bearerToken: {}, serverBase: {}, proxy: values from 'DEV_DSF_PROXY'... config}", - trustStorePath, certificatePath, privateKeyPath, privateKeyPassword != null ? "***" : "null", - dataStoreServerBasicAuthUsername, dataStoreServerBasicAuthPassword != null ? "***" : "null", - dataStoreServerBearerToken != null ? "***" : "null", dataStoreServerBase); - - getDataStoreClient().testConnection(); + for (String client : dataStoreConnectionMap.keySet()) + { + final ReceiveDataStoreConfig.DataStoreConnectionValues value = dataStoreConnectionMap.get(client); + logger.info( + "Testing connection to Data Store FHIR server with {trustStorePath: {}, certificatePath: {}, privateKeyPath: {}, privateKeyPassword: {}," + + " basicAuthUsername: {}, basicAuthPassword: {}, bearerToken: {}, serverBase: {}, proxy: values from 'DEV_DSF_PROXY'... config}", + trustStorePath, certificatePath, privateKeyPath, privateKeyPassword != null ? "***" : "null", + value.getBaseUrl(), value.getPassword() != null ? "***" : "null", + value.getBearerToken() != null ? "***" : "null", value.getUsername()); + + getDataStoreClient(client).testConnection(); + } } catch (Exception e) { @@ -170,18 +176,29 @@ public void testConnection() public DataStoreClient getDataStoreClient() { - if (configured()) - return createDataStoreClient(); + return getDataStoreClient(DEFAULT_DATA_STORE); + } + + public DataStoreClient getDataStoreClient(String client) + { + if (configured(client)) + return createDataStoreClient(client); else return new DataStoreClientStub(fhirContext, dataLogger); } - private boolean configured() + private boolean configured(String client) { - return dataStoreServerBase != null && !dataStoreServerBase.isBlank(); + return dataStoreConnectionMap.get(client).getBaseUrl() != null + && !dataStoreConnectionMap.get(client).getBaseUrl().isBlank(); } protected DataStoreClient createDataStoreClient() + { + return createDataStoreClient(DEFAULT_DATA_STORE); + } + + protected DataStoreClient createDataStoreClient(String dataStore) { KeyStore trustStore = null; char[] keyStorePassword = null; @@ -200,9 +217,11 @@ protected DataStoreClient createDataStoreClient() keyStore = readKeyStore(certificatePath, privateKeyPath, privateKeyPassword, keyStorePassword); } + final ReceiveDataStoreConfig.DataStoreConnectionValues dataStoreConfig = dataStoreConnectionMap.get(dataStore); + return new DataStoreClientImpl(trustStore, keyStore, keyStorePassword, connectTimeout, socketTimeout, - connectionRequestTimeout, dataStoreServerBasicAuthUsername, dataStoreServerBasicAuthPassword, - dataStoreServerBearerToken, dataStoreServerBase, proxyUrl, proxyUsername, proxyPassword, + connectionRequestTimeout, dataStoreConfig.getUsername(), dataStoreConfig.getPassword(), + dataStoreConfig.getBearerToken(), dataStoreConfig.getBaseUrl(), proxyUrl, proxyUsername, proxyPassword, hapiClientVerbose, fhirContext, searchBundleOverride, dataStoreFhirClientClass, useChainedParameterNotLogicalReference, dataLogger); } diff --git a/codex-process-data-transfer/src/main/java/de/netzwerk_universitaetsmedizin/codex/processes/data_transfer/service/receive/InsertDataIntoCodex.java b/codex-process-data-transfer/src/main/java/de/netzwerk_universitaetsmedizin/codex/processes/data_transfer/service/receive/InsertDataIntoCodex.java index 064396d0..a86d6e52 100644 --- a/codex-process-data-transfer/src/main/java/de/netzwerk_universitaetsmedizin/codex/processes/data_transfer/service/receive/InsertDataIntoCodex.java +++ b/codex-process-data-transfer/src/main/java/de/netzwerk_universitaetsmedizin/codex/processes/data_transfer/service/receive/InsertDataIntoCodex.java @@ -1,14 +1,14 @@ package de.netzwerk_universitaetsmedizin.codex.processes.data_transfer.service.receive; -import static de.netzwerk_universitaetsmedizin.codex.processes.data_transfer.ConstantsDataTransfer.BPMN_EXECUTION_VARIABLE_BUNDLE; -import static de.netzwerk_universitaetsmedizin.codex.processes.data_transfer.ConstantsDataTransfer.BPMN_EXECUTION_VARIABLE_CONTINUE_STATUS; -import static de.netzwerk_universitaetsmedizin.codex.processes.data_transfer.ConstantsDataTransfer.CODESYSTEM_NUM_CODEX_DATA_TRANSFER_ERROR_VALUE_INSERT_INTO_CRR_FHIR_REPOSITORY_FAILED; +import static de.netzwerk_universitaetsmedizin.codex.processes.data_transfer.ConstantsDataTransfer.*; import java.util.Objects; +import java.util.Optional; import org.camunda.bpm.engine.delegate.BpmnError; import org.camunda.bpm.engine.delegate.DelegateExecution; import org.hl7.fhir.r4.model.Bundle; +import org.hl7.fhir.r4.model.Task; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -18,6 +18,7 @@ import de.netzwerk_universitaetsmedizin.codex.processes.data_transfer.service.ContinueStatus; import dev.dsf.bpe.v1.ProcessPluginApi; import dev.dsf.bpe.v1.activity.AbstractServiceDelegate; +import dev.dsf.bpe.v1.service.TaskHelper; import dev.dsf.bpe.v1.variables.Variables; public class InsertDataIntoCodex extends AbstractServiceDelegate @@ -49,14 +50,21 @@ protected void doExecute(DelegateExecution execution, Variables variables) throw { Bundle bundle = variables.getResource(BPMN_EXECUTION_VARIABLE_BUNDLE); + String studyId = getStudyId(variables.getStartTask()); + if (studyId == null || studyId.isEmpty()) + { + logger.error("Unable to receive, {} is empty", CODESYSTEM_NUM_CODEX_DATA_TRANSFER_VALUE_STUDY_ID); + throw new IllegalArgumentException(CODESYSTEM_NUM_CODEX_DATA_TRANSFER_VALUE_STUDY_ID + " is empty"); + } + try { try { - logger.info("Executing bundle against FHIR store ..."); + logger.info("Executing bundle against FHIR store '{}'", studyId); dataLogger.logData("Received bundle", bundle); - dataClientFactory.getDataStoreClient().getFhirClient().storeBundle(bundle); + dataClientFactory.getDataStoreClient(studyId).getFhirClient().storeBundle(bundle); execution.setVariable(BPMN_EXECUTION_VARIABLE_CONTINUE_STATUS, ContinueStatus.SUCCESS); } @@ -69,9 +77,18 @@ protected void doExecute(DelegateExecution execution, Variables variables) throw } catch (Exception e) { - logger.warn("Unable to insert data into CRR: {} - {}", e.getClass().getName(), e.getMessage()); + logger.warn("Unable to insert data into '{}': {} - {}", studyId, e.getClass().getName(), e.getMessage()); throw new BpmnError(CODESYSTEM_NUM_CODEX_DATA_TRANSFER_ERROR_VALUE_INSERT_INTO_CRR_FHIR_REPOSITORY_FAILED, - "Unable to insert data into CRR"); + "Unable to insert data into '" + studyId + "'"); } } + + private String getStudyId(Task task) + { + TaskHelper taskHelper = this.api.getTaskHelper(); + Optional studyId = taskHelper.getFirstInputParameterStringValue(task, + CODESYSTEM_NUM_CODEX_DATA_TRANSFER, CODESYSTEM_NUM_CODEX_DATA_TRANSFER_VALUE_STUDY_ID); + + return studyId.orElse(""); + } } diff --git a/codex-process-data-transfer/src/main/java/de/netzwerk_universitaetsmedizin/codex/processes/data_transfer/spring/config/ReceiveConfig.java b/codex-process-data-transfer/src/main/java/de/netzwerk_universitaetsmedizin/codex/processes/data_transfer/spring/config/ReceiveConfig.java index 7f1311ec..af3b508e 100644 --- a/codex-process-data-transfer/src/main/java/de/netzwerk_universitaetsmedizin/codex/processes/data_transfer/spring/config/ReceiveConfig.java +++ b/codex-process-data-transfer/src/main/java/de/netzwerk_universitaetsmedizin/codex/processes/data_transfer/spring/config/ReceiveConfig.java @@ -9,15 +9,7 @@ import de.netzwerk_universitaetsmedizin.codex.processes.data_transfer.message.ContinueTranslateProcess; import de.netzwerk_universitaetsmedizin.codex.processes.data_transfer.message.ContinueTranslateProcessWithError; import de.netzwerk_universitaetsmedizin.codex.processes.data_transfer.message.ContinueTranslateProcessWithValidationError; -import de.netzwerk_universitaetsmedizin.codex.processes.data_transfer.service.receive.DecryptData; -import de.netzwerk_universitaetsmedizin.codex.processes.data_transfer.service.receive.DeleteValidationErrorForDts; -import de.netzwerk_universitaetsmedizin.codex.processes.data_transfer.service.receive.DownloadDataFromDts; -import de.netzwerk_universitaetsmedizin.codex.processes.data_transfer.service.receive.EncryptValidationError; -import de.netzwerk_universitaetsmedizin.codex.processes.data_transfer.service.receive.InsertDataIntoCodex; -import de.netzwerk_universitaetsmedizin.codex.processes.data_transfer.service.receive.LogError; -import de.netzwerk_universitaetsmedizin.codex.processes.data_transfer.service.receive.LogSuccess; -import de.netzwerk_universitaetsmedizin.codex.processes.data_transfer.service.receive.LogValidationError; -import de.netzwerk_universitaetsmedizin.codex.processes.data_transfer.service.receive.StoreValidationErrorForDts; +import de.netzwerk_universitaetsmedizin.codex.processes.data_transfer.service.receive.*; import dev.dsf.bpe.v1.ProcessPluginApi; @Configuration diff --git a/codex-process-data-transfer/src/main/java/de/netzwerk_universitaetsmedizin/codex/processes/data_transfer/spring/config/ReceiveDataStoreConfig.java b/codex-process-data-transfer/src/main/java/de/netzwerk_universitaetsmedizin/codex/processes/data_transfer/spring/config/ReceiveDataStoreConfig.java new file mode 100644 index 00000000..25cf555d --- /dev/null +++ b/codex-process-data-transfer/src/main/java/de/netzwerk_universitaetsmedizin/codex/processes/data_transfer/spring/config/ReceiveDataStoreConfig.java @@ -0,0 +1,110 @@ +package de.netzwerk_universitaetsmedizin.codex.processes.data_transfer.spring.config; + +import java.util.HashMap; +import java.util.Map; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.PropertySource; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; + +import jakarta.annotation.PostConstruct; + +@Configuration +@PropertySource(ignoreResourceNotFound = true, value = "file:process/rdp-application.properties") +public class ReceiveDataStoreConfig +{ + private static final Logger logger = LoggerFactory.getLogger(ReceiveDataStoreConfig.class); + public static final String INVALID_CONFIG_MESSAGE = "Invalid Client Config map"; + public static final String VALID_CONFIG_MESSAGE = "Client Config found: {}"; + + @Autowired + private ObjectMapper objectMapper; + + @Value("${dataStores:#{null}}") + private String dataStoresProperty; + private Map dataStoreConnectionConfigs = new HashMap<>(); + + @PostConstruct + protected void convertClientMap() + { + if (dataStoresProperty == null || dataStoresProperty.isEmpty()) + { + return; + } + + try + { + dataStoreConnectionConfigs = objectMapper.readValue(dataStoresProperty, + new TypeReference>() + { + }); + + logger.info(VALID_CONFIG_MESSAGE, dataStoreConnectionConfigs.keySet()); + } + catch (JsonProcessingException e) + { + logger.error(INVALID_CONFIG_MESSAGE); + throw new IllegalArgumentException(INVALID_CONFIG_MESSAGE, e); + } + } + + @Bean + public Map getDataStoreConnectionConfigs() + { + return dataStoreConnectionConfigs; + } + + public static class DataStoreConnectionValues + { + private String baseUrl; + private String username; + private String password; + private String bearerToken; + + public DataStoreConnectionValues() + { + } + + public DataStoreConnectionValues(String baseUrl, String username, String password, String bearerToken) + { + this.baseUrl = baseUrl; + this.username = username; + this.password = password; + this.bearerToken = bearerToken; + } + + public String getBaseUrl() + { + return baseUrl; + } + + public String getUsername() + { + return username; + } + + public String getPassword() + { + return password; + } + + public String getBearerToken() + { + return bearerToken; + } + + @Override + public String toString() + { + return "RdpClientConfigValues{" + "baseUrl='" + baseUrl + '\'' + ", Username='" + username + '\'' + '}'; + } + } +} diff --git a/codex-process-data-transfer/src/main/java/de/netzwerk_universitaetsmedizin/codex/processes/data_transfer/spring/config/TransferDataConfig.java b/codex-process-data-transfer/src/main/java/de/netzwerk_universitaetsmedizin/codex/processes/data_transfer/spring/config/TransferDataConfig.java index 6d6266e3..7e014613 100644 --- a/codex-process-data-transfer/src/main/java/de/netzwerk_universitaetsmedizin/codex/processes/data_transfer/spring/config/TransferDataConfig.java +++ b/codex-process-data-transfer/src/main/java/de/netzwerk_universitaetsmedizin/codex/processes/data_transfer/spring/config/TransferDataConfig.java @@ -29,6 +29,9 @@ public class TransferDataConfig @Autowired private ProcessPluginApi api; + @Autowired + private ReceiveDataStoreConfig receiveDataStoreConfig; + @ProcessDocumentation(description = "PEM encoded file with trusted certificates to validate the server-certificate of the data FHIR server", processNames = { "wwwnetzwerk-universitaetsmedizinde_dataSend", "wwwnetzwerk-universitaetsmedizinde_dataReceive" }, recommendation = "Use docker secret file to configure", example = "/run/secrets/data_fhir_server_ca.pem") @@ -318,9 +321,9 @@ public DataStoreClientFactory dataStoreClientFactory() return new DataStoreClientFactory(trustStorePath, certificatePath, privateKeyPath, fhirStorePrivateKeyPassword, fhirStoreConnectTimeout, fhirStoreSocketTimeout, fhirStoreConnectionRequestTimeout, fhirStoreBaseUrl, fhirStoreUsername, fhirStorePassword, - fhirStoreBearerToken, proxyUrl, proxyUsername, proxyPassword, fhirStoreHapiClientVerbose, - api.getFhirContext(), searchBundleOverride, - (Class) Class.forName(fhirStoreClientClass), + fhirStoreBearerToken, receiveDataStoreConfig.getDataStoreConnectionConfigs(), proxyUrl, + proxyUsername, proxyPassword, fhirStoreHapiClientVerbose, api.getFhirContext(), + searchBundleOverride, (Class) Class.forName(fhirStoreClientClass), fhirStoreUseChainedParameterNotLogicalReference, dataLogger()); } catch (ClassNotFoundException e) diff --git a/codex-process-data-transfer/src/main/resources/fhir/Task/TaskStartDataSendWithIdentifierReference.xml b/codex-process-data-transfer/src/main/resources/fhir/Task/TaskStartDataSendWithIdentifierReference.xml index f011a571..40d12aa0 100644 --- a/codex-process-data-transfer/src/main/resources/fhir/Task/TaskStartDataSendWithIdentifierReference.xml +++ b/codex-process-data-transfer/src/main/resources/fhir/Task/TaskStartDataSendWithIdentifierReference.xml @@ -51,7 +51,7 @@ - @@ -62,7 +62,7 @@ - @@ -73,8 +73,8 @@ - @@ -85,4 +85,14 @@ - \ No newline at end of file + + diff --git a/codex-process-data-transfer/src/test/java/de/netzwerk_universitaetsmedizin/codex/processes/data_transfer/service/receive/InsertDataIntoCodexTest.java b/codex-process-data-transfer/src/test/java/de/netzwerk_universitaetsmedizin/codex/processes/data_transfer/service/receive/InsertDataIntoCodexTest.java new file mode 100644 index 00000000..1c4ab7a2 --- /dev/null +++ b/codex-process-data-transfer/src/test/java/de/netzwerk_universitaetsmedizin/codex/processes/data_transfer/service/receive/InsertDataIntoCodexTest.java @@ -0,0 +1,95 @@ +package de.netzwerk_universitaetsmedizin.codex.processes.data_transfer.service.receive; + +import static de.netzwerk_universitaetsmedizin.codex.processes.data_transfer.ConstantsDataTransfer.*; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.*; + +import java.util.Optional; + +import org.camunda.bpm.engine.delegate.DelegateExecution; +import org.hl7.fhir.r4.model.*; +import org.junit.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnitRunner; +import org.mockito.junit.jupiter.MockitoExtension; + +import de.netzwerk_universitaetsmedizin.codex.processes.data_transfer.client.DataStoreClient; +import de.netzwerk_universitaetsmedizin.codex.processes.data_transfer.client.DataStoreClientFactory; +import de.netzwerk_universitaetsmedizin.codex.processes.data_transfer.client.fhir.DataStoreFhirClient; +import de.netzwerk_universitaetsmedizin.codex.processes.data_transfer.logging.DataLogger; +import dev.dsf.bpe.v1.ProcessPluginApi; +import dev.dsf.bpe.v1.service.TaskHelper; +import dev.dsf.bpe.v1.variables.Variables; + +@ExtendWith(MockitoExtension.class) +@RunWith(MockitoJUnitRunner.class) +public class InsertDataIntoCodexTest +{ + + @Test + public void testDoExecuteWithStudyId() throws Exception + { + ProcessPluginApi api = mock(ProcessPluginApi.class); + DataStoreClientFactory dataStoreClientFactory = mock(DataStoreClientFactory.class); + DataLogger dataLogger = mock(DataLogger.class); + InsertDataIntoCodex insertDataIntoCodex = new InsertDataIntoCodex(api, dataStoreClientFactory, dataLogger); + + Variables variables = mock(Variables.class); + DelegateExecution execution = mock(DelegateExecution.class); + Bundle bundle = new Bundle(); + when(api.getVariables(execution)).thenReturn(variables); + when(variables.getResource(BPMN_EXECUTION_VARIABLE_BUNDLE)).thenReturn(bundle); + + Task task = createTask(); + when(variables.getStartTask()).thenReturn(task); + TaskHelper taskHelper = mock(TaskHelper.class); + when(api.getTaskHelper()).thenReturn(taskHelper); + + when(taskHelper.getFirstInputParameterStringValue(task, CODESYSTEM_NUM_CODEX_DATA_TRANSFER, + CODESYSTEM_NUM_CODEX_DATA_TRANSFER_VALUE_STUDY_ID)).thenReturn(Optional.of("dataStore")); + + DataStoreClient dataStoreClient = mock(DataStoreClient.class); + DataStoreFhirClient fhirClient = mock(DataStoreFhirClient.class); + when(dataStoreClientFactory.getDataStoreClient("dataStore")).thenReturn(dataStoreClient); + when(dataStoreClient.getFhirClient()).thenReturn(fhirClient); + + insertDataIntoCodex.execute(execution); + + verify(dataStoreClientFactory, times(1)).getDataStoreClient("dataStore"); + } + + @Test + public void testDoExecuteWithoutStudyId() throws Exception + { + ProcessPluginApi api = mock(ProcessPluginApi.class); + DataStoreClientFactory dataStoreClientFactory = mock(DataStoreClientFactory.class); + DataLogger dataLogger = mock(DataLogger.class); + InsertDataIntoCodex insertDataIntoCodex = new InsertDataIntoCodex(api, dataStoreClientFactory, dataLogger); + + Variables variables = mock(Variables.class); + DelegateExecution execution = mock(DelegateExecution.class); + Bundle bundle = new Bundle(); + when(variables.getResource(BPMN_EXECUTION_VARIABLE_BUNDLE)).thenReturn(bundle); + + Task task = createTask(); + when(variables.getStartTask()).thenReturn(task); + TaskHelper taskHelper = mock(TaskHelper.class); + when(api.getTaskHelper()).thenReturn(taskHelper); + + when(taskHelper.getFirstInputParameterStringValue(task, CODESYSTEM_NUM_CODEX_DATA_TRANSFER, + CODESYSTEM_NUM_CODEX_DATA_TRANSFER_VALUE_STUDY_ID)).thenReturn(Optional.of("")); + + assertThrows(IllegalArgumentException.class, () -> insertDataIntoCodex.doExecute(execution, variables)); + verify(dataStoreClientFactory, never()); + } + + private Task createTask() + { + Task task = new Task(); + Coding code = new Coding(); + code.setSystem(CODESYSTEM_NUM_CODEX_DATA_TRANSFER_VALUE_STUDY_ID); + task.addInput(new Task.ParameterComponent(new CodeableConcept(code), new StringType("study_id"))); + return task; + } +} diff --git a/codex-process-data-transfer/src/test/java/de/netzwerk_universitaetsmedizin/codex/processes/data_transfer/spring/config/ReceiveDataStoreConfigTest.java b/codex-process-data-transfer/src/test/java/de/netzwerk_universitaetsmedizin/codex/processes/data_transfer/spring/config/ReceiveDataStoreConfigTest.java new file mode 100644 index 00000000..ec659502 --- /dev/null +++ b/codex-process-data-transfer/src/test/java/de/netzwerk_universitaetsmedizin/codex/processes/data_transfer/spring/config/ReceiveDataStoreConfigTest.java @@ -0,0 +1,80 @@ +package de.netzwerk_universitaetsmedizin.codex.processes.data_transfer.spring.config; + +import static org.mockito.Mockito.*; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Objects; +import java.util.Properties; + +import org.junit.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Spy; +import org.mockito.junit.MockitoJUnitRunner; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.test.util.ReflectionTestUtils; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.MapperFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.json.JsonMapper; + +import junit.framework.TestCase; + +@ExtendWith(MockitoExtension.class) +@RunWith(MockitoJUnitRunner.class) +public class ReceiveDataStoreConfigTest extends TestCase +{ + + public static final String VALID_PROPERTIES = "applicationProperties/valid-application.properties"; + public static final String INVALID_PROPERTIES = "applicationProperties/invalid-application.properties"; + + @InjectMocks + private ReceiveDataStoreConfig receiveDataStoreConfig; + + @Spy + private ObjectMapper objectMapper = JsonMapper.builder().serializationInclusion(JsonInclude.Include.NON_NULL) + .serializationInclusion(JsonInclude.Include.NON_EMPTY).disable(MapperFeature.AUTO_DETECT_CREATORS) + .disable(MapperFeature.AUTO_DETECT_FIELDS).disable(MapperFeature.AUTO_DETECT_SETTERS).build(); + + @Test + public void testConvertClientMapValidMap() throws IOException + { + String fileContent = readPropertyFile(VALID_PROPERTIES); + + receiveDataStoreConfig.convertClientMap(); + verify(objectMapper, times(1)).readValue(eq(fileContent), any(TypeReference.class)); + } + + @Test(expected = IllegalArgumentException.class) + public void testConvertClientMapInvalidMap() throws IOException + { + String fileContent = readPropertyFile(INVALID_PROPERTIES); + + receiveDataStoreConfig.convertClientMap(); + verify(objectMapper, times(1)).readValue(eq(fileContent), any(TypeReference.class)); + } + + @Test + public void testConvertClientMapWithoutMap() throws IOException + { + receiveDataStoreConfig.convertClientMap(); + verify(objectMapper, never()).readValue(anyString(), any(TypeReference.class)); + } + + private String readPropertyFile(String resourcePath) throws IOException + { + Properties props = new Properties(); + + ClassLoader classLoader = this.getClass().getClassLoader(); + InputStream propsStream = Objects.requireNonNull(classLoader.getResourceAsStream(resourcePath)); + props.load(propsStream); + propsStream.close(); + String dataStores = (String) props.get("dataStores"); + ReflectionTestUtils.setField(receiveDataStoreConfig, "dataStoresProperty", dataStores); + return dataStores; + } +} diff --git a/codex-process-data-transfer/src/test/resources/applicationProperties/invalid-application.properties b/codex-process-data-transfer/src/test/resources/applicationProperties/invalid-application.properties new file mode 100644 index 00000000..049b6ca1 --- /dev/null +++ b/codex-process-data-transfer/src/test/resources/applicationProperties/invalid-application.properties @@ -0,0 +1,3 @@ +dataStores={\ + "invalid"\ +} diff --git a/codex-process-data-transfer/src/test/resources/applicationProperties/valid-application.properties b/codex-process-data-transfer/src/test/resources/applicationProperties/valid-application.properties new file mode 100644 index 00000000..7980cec2 --- /dev/null +++ b/codex-process-data-transfer/src/test/resources/applicationProperties/valid-application.properties @@ -0,0 +1,8 @@ +dataStores={\ + "validDataStore": {\ + "baseUrl": "http://fhir-store:8080/fhir",\ + "username": "user",\ + "password": "strongPassword1",\ + "bearerToken": "valid123"\ + }\ +} diff --git a/codex-process-data-transfer/src/test/resources/fhir/Task/TaskStartDataSendWithAbsoluteReference.xml b/codex-process-data-transfer/src/test/resources/fhir/Task/TaskStartDataSendWithAbsoluteReference.xml index 325c15e3..16121807 100644 --- a/codex-process-data-transfer/src/test/resources/fhir/Task/TaskStartDataSendWithAbsoluteReference.xml +++ b/codex-process-data-transfer/src/test/resources/fhir/Task/TaskStartDataSendWithAbsoluteReference.xml @@ -1,8 +1,8 @@ - + - + @@ -44,8 +44,8 @@ - @@ -56,8 +56,8 @@ - - \ No newline at end of file + diff --git a/codex-process-data-transfer/src/test/resources/fhir/Task/TaskStartDataSendWithIdentifierReference.xml b/codex-process-data-transfer/src/test/resources/fhir/Task/TaskStartDataSendWithIdentifierReference.xml index 6f57fd86..0cde2d33 100644 --- a/codex-process-data-transfer/src/test/resources/fhir/Task/TaskStartDataSendWithIdentifierReference.xml +++ b/codex-process-data-transfer/src/test/resources/fhir/Task/TaskStartDataSendWithIdentifierReference.xml @@ -1,8 +1,8 @@ - + - + @@ -32,7 +32,7 @@ @@ -49,8 +49,8 @@ - @@ -61,8 +61,8 @@ - - \ No newline at end of file + + diff --git a/codex-process-data-transfer/src/test/resources/fhir/Task/TaskStartDataTrigger.xml b/codex-process-data-transfer/src/test/resources/fhir/Task/TaskStartDataTrigger.xml index 9255df41..8cf13dc7 100644 --- a/codex-process-data-transfer/src/test/resources/fhir/Task/TaskStartDataTrigger.xml +++ b/codex-process-data-transfer/src/test/resources/fhir/Task/TaskStartDataTrigger.xml @@ -1,8 +1,8 @@ - + - + @@ -31,7 +31,7 @@ - - \ No newline at end of file + diff --git a/codex-process-data-transfer/src/test/resources/fhir/Task/TaskStopDataTrigger.xml b/codex-process-data-transfer/src/test/resources/fhir/Task/TaskStopDataTrigger.xml index a2311792..8dce74be 100644 --- a/codex-process-data-transfer/src/test/resources/fhir/Task/TaskStopDataTrigger.xml +++ b/codex-process-data-transfer/src/test/resources/fhir/Task/TaskStopDataTrigger.xml @@ -1,8 +1,8 @@ - + - + diff --git a/codex-processes-ap1-docker-test-setup/README.md b/codex-processes-ap1-docker-test-setup/README.md index 2b177e28..96301f94 100644 --- a/codex-processes-ap1-docker-test-setup/README.md +++ b/codex-processes-ap1-docker-test-setup/README.md @@ -9,53 +9,58 @@ mvn clean package Add entries to your hosts file ``` 127.0.0.1 dic -127.0.0.1 gth +127.0.0.1 dts 127.0.0.1 crr ``` -*Start docker-compose commands from sub-folder:* `codex-processes-ap1/codex-processes-ap1-docker-test-setup` +*Start docker compose commands from sub-folder:* `codex-processes-ap1/codex-processes-ap1-docker-test-setup` Console 1: Start DIC HAPI FHIR Server or DIC blaze FHIR Server ```sh -docker-compose up dic-fhir-store-hapi -docker-compose up dic-fhir-store-blaze +docker compose up dic-fhir-store-hapi +docker compose up dic-fhir-store-blaze ``` Access at http://localhost:8080/fhir/ Console 2: Start CRR fhir-bridge server ```sh -docker-compose up crr-fhir-bridge +docker compose up crr-fhir-bridge ``` Access at http://localhost:8888/fhir-bridge/fhir/ Console 3: Start DIC DSF FHIR Server and wait till started ```sh -docker-compose up -d dic-fhir && docker-compose logs -f dic-fhir +docker compose up -d dic-fhir && docker compose logs -f dic-fhir ``` Console 3: Disconnect from log output (Ctrl-C) if server started Console 3: Start DIC DSF BPE Server ```sh -docker-compose up -d dic-bpe && docker-compose logs -f dic-fhir dic-bpe +docker compose up -d dic-bpe && docker compose logs -f dic-fhir dic-bpe ```` Console 4: Start GTH DSF FHIR Server and wait till started ```sh -docker-compose up -d gth-fhir && docker-compose logs -f gth-fhir +docker compose up -d dts-fhir && docker compose logs -f dts-fhir ``` Console 4: Disconnect from log output (Ctrl-C) if server started Console 4: Start GTH DSF BPE Server ```sh -docker-compose up -d gth-bpe && docker-compose logs -f gth-fhir gth-bpe +docker compose up -d dts-bpe && docker compose logs -f dts-fhir dts-bpe ```` -Console 5: Start CRR DSF FHIR Server and wait till started +Console 5: Start CRR HAPI FHIR Server ```sh -docker-compose up -d crr-fhir && docker-compose logs -f crr-fhir +docker compose up crr-fhir-store-hapi ``` -Console 5: Dicconnect from log output (Ctrl-C) if server started -Console 5: Start CRR DSF BPE Server + +Console 6: Start CRR DSF FHIR Server and wait till started +```sh +docker compose up -d crr-fhir && docker compose logs -f crr-fhir +``` +Console 6: Dicconnect from log output (Ctrl-C) if server started +Console 6: Start CRR DSF BPE Server ```sh -docker-compose up -d crr-bpe && docker-compose logs -f crr-fhir crr-bpe +docker compose up -d crr-bpe && docker compose logs -f crr-fhir crr-bpe ```` org.apache.commons @@ -131,7 +131,7 @@ crypto-utils 3.8.0 - + org.slf4j slf4j-api @@ -157,6 +157,12 @@ mockito-core 4.8.0 + + org.mockito + mockito-junit-jupiter + 5.12.0 + test + org.apache.logging.log4j log4j-slf4j2-impl @@ -197,7 +203,7 @@ org.apache.maven.plugins - maven-shade-plugin + maven-shade-plugin 3.5.1 @@ -268,8 +274,8 @@ - github @@ -343,4 +349,4 @@ - \ No newline at end of file +