diff --git a/README.md b/README.md index 0d055bce..be2f66d6 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,9 @@ In this repository you will find the ping-pong process for connection testing, which can be deployed on the [DSF](https://github.com/datasharingframework/dsf). +## Version 2.x release +Version 2.x of the ping-pong process (dubbed "Fat-Ping") includes the ability to test whether downloading FHIR resources from other DSF instances is possible. This also includes approximately measuring the network speed of resource downloads with larger download sizes (~100MB) returning more accurate results. It retains the ability to make connection tests without downloading resource like ping-pong 1.x. Documentation on configuration is available in the [wiki](https://github.com/datasharingframework/dsf-process-ping-pong/wiki). + ## Development Branching follows the git-flow model, for the latest development version see branch [develop](https://github.com/datasharingframework/dsf-process-ping-pong/tree/develop). diff --git a/pom.xml b/pom.xml index 180ae42d..0904d48a 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ dev.dsf dsf-process-ping-pong - 1.0.1.0-SNAPSHOT + 2.0.0.0-RC2 jar @@ -13,7 +13,7 @@ 17 17 - 1.2.0-RC1 + 1.2.0 ../dsf diff --git a/src/main/java/dev/dsf/bpe/ConstantsPing.java b/src/main/java/dev/dsf/bpe/ConstantsPing.java index 0f8c12f1..2eb4057e 100644 --- a/src/main/java/dev/dsf/bpe/ConstantsPing.java +++ b/src/main/java/dev/dsf/bpe/ConstantsPing.java @@ -1,59 +1,188 @@ package dev.dsf.bpe; +import java.util.List; -public interface ConstantsPing +import jakarta.ws.rs.core.MediaType; + +public final class ConstantsPing { - String PROCESS_NAME_PING_AUTOSTART = "pingAutostart"; - String PROCESS_NAME_PING = "ping"; - String PROCESS_NAME_PONG = "pong"; - - String PROCESS_NAME_FULL_PING_AUTOSTART = "dsfdev_" + PROCESS_NAME_PING_AUTOSTART; - String PROCESS_NAME_FULL_PING = "dsfdev_" + PROCESS_NAME_PING; - String PROCESS_NAME_FULL_PONG = "dsfdev_" + PROCESS_NAME_PONG; - - String PROCESS_DSF_URI_BASE = "http://dsf.dev/bpe/Process/"; - - String PROFILE_DSF_TASK_START_PING_AUTOSTART = "http://dsf.dev/fhir/StructureDefinition/task-start-ping-autostart"; - String PROFILE_DSF_TASK_START_PING_AUTOSTART_PROCESS_URI = PROCESS_DSF_URI_BASE + PROCESS_NAME_PING_AUTOSTART; - String PROFILE_DSF_TASK_START_PING_AUTOSTART_MESSAGE_NAME = "startPingAutostart"; - - String PROFILE_DSF_TASK_STOP_PING_AUTOSTART = "http://dsf.dev/fhir/StructureDefinition/task-stop-ping-autostart"; - String PROFILE_DSF_TASK_STOP_PING_AUTOSTART_PROCESS_URI = PROCESS_DSF_URI_BASE + PROCESS_NAME_PING_AUTOSTART; - String PROFILE_DSF_TASK_STOP_PING_AUTOSTART_MESSAGE_NAME = "stopPingAutostart"; - - String PROFILE_DSF_TASK_START_PING = "http://dsf.dev/fhir/StructureDefinition/task-start-ping"; - String PROFILE_DSF_TASK_START_PING_MESSAGE_NAME = "startPing"; - - String PROFILE_DSF_TASK_PING = "http://dsf.dev/fhir/StructureDefinition/task-ping"; - String PROFILE_DSF_TASK_PING_PROCESS_URI = PROCESS_DSF_URI_BASE + PROCESS_NAME_PING; - String PROFILE_DSF_TASK_PING_MESSAGE_NAME = "ping"; - - String PROFILE_DSF_TASK_PONG_TASK = "http://dsf.dev/fhir/StructureDefinition/task-pong"; - String PROFILE_DSF_TASK_PONG_PROCESS_URI = PROCESS_DSF_URI_BASE + PROCESS_NAME_PONG; - String PROFILE_DSF_TASK_PONG_MESSAGE_NAME = "pong"; - - String CODESYSTEM_DSF_PING = "http://dsf.dev/fhir/CodeSystem/ping"; - String CODESYSTEM_DSF_PING_VALUE_PING_STATUS = "ping-status"; - String CODESYSTEM_DSF_PING_VALUE_PONG_STATUS = "pong-status"; - String CODESYSTEM_DSF_PING_VALUE_ENDPOINT_IDENTIFIER = "endpoint-identifier"; - String CODESYSTEM_DSF_PING_VALUE_TARGET_ENDPOINTS = "target-endpoints"; - String CODESYSTEM_DSF_PING_VALUE_TIMER_INTERVAL = "timer-interval"; - - String CODESYSTEM_DSF_PING_STATUS = "http://dsf.dev/fhir/CodeSystem/ping-status"; - String CODESYSTEM_DSF_PING_STATUS_VALUE_NOT_ALLOWED = "not-allowed"; - String CODESYSTEM_DSF_PING_STATUS_VALUE_NOT_REACHABLE = "not-reachable"; - String CODESYSTEM_DSF_PING_STATUS_VALUE_PONG_MISSING = "pong-missing"; - String CODESYSTEM_DSF_PING_STATUS_VALUE_PONG_RECEIVED = "pong-received"; - String CODESYSTEM_DSF_PING_STATUS_VALUE_PONG_SEND = "pong-send"; - - String EXTENSION_URL_PING_STATUS = "http://dsf.dev/fhir/StructureDefinition/extension-ping-status"; - String EXTENSION_URL_CORRELATION_KEY = "correlation-key"; - String EXTENSION_URL_ORGANIZATION_IDENTIFIER = "organization-identifier"; - String EXTENSION_URL_ENDPOINT_IDENTIFIER = "endpoint-identifier"; - String EXTENSION_URL_ERROR_MESSAGE = "error-message"; - - String BPMN_EXECUTION_VARIABLE_TIMER_INTERVAL = "timerInterval"; - String BPMN_EXECUTION_VARIABLE_STOP_TIMER = "stopTimer"; - - String TIMER_INTERVAL_DEFAULT_VALUE = "PT24H"; + private ConstantsPing() + { + } + + public static final String PROCESS_NAME_PING_AUTOSTART = "pingAutostart"; + public static final String PROCESS_NAME_PING = "ping"; + public static final String PROCESS_NAME_PONG = "pong"; + + public static final String PROCESS_NAME_FULL_PING_AUTOSTART = "dsfdev_" + PROCESS_NAME_PING_AUTOSTART; + public static final String PROCESS_NAME_FULL_PING = "dsfdev_" + PROCESS_NAME_PING; + public static final String PROCESS_NAME_FULL_PONG = "dsfdev_" + PROCESS_NAME_PONG; + + public static final String PROCESS_DSF_URI_BASE = "http://dsf.dev/bpe/Process/"; + + public static final String PROFILE_DSF_TASK_START_PING_AUTOSTART = "http://dsf.dev/fhir/StructureDefinition/task-start-ping-autostart"; + public static final String PROFILE_DSF_TASK_START_PING_AUTOSTART_PROCESS_URI = PROCESS_DSF_URI_BASE + + PROCESS_NAME_PING_AUTOSTART; + public static final String PROFILE_DSF_TASK_START_PING_AUTOSTART_MESSAGE_NAME = "startPingAutostart"; + + public static final String PROFILE_DSF_TASK_STOP_PING_AUTOSTART = "http://dsf.dev/fhir/StructureDefinition/task-stop-ping-autostart"; + public static final String PROFILE_DSF_TASK_STOP_PING_AUTOSTART_PROCESS_URI = PROCESS_DSF_URI_BASE + + PROCESS_NAME_PING_AUTOSTART; + public static final String PROFILE_DSF_TASK_STOP_PING_AUTOSTART_MESSAGE_NAME = "stopPingAutostart"; + + public static final String PROFILE_DSF_TASK_START_PING = "http://dsf.dev/fhir/StructureDefinition/task-start-ping"; + public static final String PROFILE_DSF_TASK_START_PING_MESSAGE_NAME = "startPing"; + + public static final String PROFILE_DSF_TASK_PING = "http://dsf.dev/fhir/StructureDefinition/task-ping"; + public static final String PROFILE_DSF_TASK_PING_PROCESS_URI = PROCESS_DSF_URI_BASE + PROCESS_NAME_PING; + public static final String PROFILE_DSF_TASK_PING_MESSAGE_NAME = "ping"; + + public static final String PROFILE_DSF_TASK_PONG_TASK = "http://dsf.dev/fhir/StructureDefinition/task-pong"; + public static final String PROFILE_DSF_TASK_PONG_PROCESS_URI = PROCESS_DSF_URI_BASE + PROCESS_NAME_PONG; + public static final String PROFILE_DSF_TASK_PONG_MESSAGE_NAME = "pong"; + + public static final String PROFILE_DSF_TASK_CLEANUP_PONG = "http://dsf.dev/fhir/StructureDefinition/task-cleanup-pong"; + public static final String PROFILE_DSF_TASK_CLEANUP_PONG_PROCESS_URI = PROCESS_DSF_URI_BASE + PROCESS_NAME_PONG; + public static final String PROFILE_DSF_TASK_CLEANUP_PONG_MESSAGE_NAME = "cleanupPong"; + + public static final String CODESYSTEM_DSF_PING = "http://dsf.dev/fhir/CodeSystem/ping-v2"; + public static final String CODESYSTEM_DSF_PING_VALUE_PING_STATUS = "ping-status"; + public static final String CODESYSTEM_DSF_PING_VALUE_PONG_STATUS = "pong-status"; + public static final String CODESYSTEM_DSF_PING_VALUE_ENDPOINT_IDENTIFIER = "endpoint-identifier"; + public static final String CODESYSTEM_DSF_PING_VALUE_TARGET_ENDPOINTS = "target-endpoints"; + public static final String CODESYSTEM_DSF_PING_VALUE_TIMER_INTERVAL = "timer-interval"; + public static final String CODESYSTEM_DSF_PING_VALUE_DOWNLOAD_RESOURCE_SIZE_BYTES = "download-resource-size-bytes"; + public static final String CODESYSTEM_DSF_PING_VALUE_DOWNLOADED_DURATION_MILLIS = "downloaded-duration-millis"; + public static final String CODESYSTEM_DSF_PING_VALUE_DOWNLOADED_BYTES = "downloaded-bytes"; + public static final String CODESYSTEM_DSF_PING_VALUE_DOWNLOAD_RESOURCE_REFERENCE = "download-resource-reference"; + + public static final String CODESYSTEM_DSF_PING_STATUS = "http://dsf.dev/fhir/CodeSystem/ping-status-v2"; + public static final String CODESYSTEM_DSF_PING_STATUS_VALUE_NOT_ALLOWED = "not-allowed"; + public static final String CODESYSTEM_DSF_PING_STATUS_VALUE_NOT_REACHABLE = "not-reachable"; + public static final String CODESYSTEM_DSF_PING_STATUS_VALUE_PONG_MISSING = "pong-missing"; + public static final String CODESYSTEM_DSF_PING_STATUS_VALUE_PONG_RECEIVED = "pong-received"; + public static final String CODESYSTEM_DSF_PING_STATUS_VALUE_PONG_SENT = "pong-sent"; + public static final String CODESYSTEM_DSF_PING_STATUS_VALUE_ERROR_MESSAGE = "error-message"; + public static final String CODESYSTEM_DSF_PING_STATUS_VALUE_RESOURCE_DOWNLOADED = "resource-downloaded"; + + public static final String CODESYSTEM_DSF_PING_UNITS = "http://dsf.dev/fhir/CodeSystem/ping-units-v2"; + public static final String CODESYSTEM_DSF_PING_UNITS_VALUE_BITS_PER_SECOND = "bits-per-second"; + public static final String CODESYSTEM_DSF_PING_UNITS_VALUE_BYTES_PER_SECOND = "bytes-per-second"; + public static final String CODESYSTEM_DSF_PING_UNITS_VALUE_MEGABITS_PER_SECOND = "megabits-per-second"; + public static final String CODESYSTEM_DSF_PING_UNITS_VALUE_MEGABYTES_PER_SECOND = "megabytes-per-second"; + + public static final List CODESYSTEM_DSF_PING_UNITS_VALUES = List.of( + CODESYSTEM_DSF_PING_UNITS_VALUE_BITS_PER_SECOND, CODESYSTEM_DSF_PING_UNITS_VALUE_BYTES_PER_SECOND, + CODESYSTEM_DSF_PING_UNITS_VALUE_MEGABITS_PER_SECOND, CODESYSTEM_DSF_PING_UNITS_VALUE_MEGABYTES_PER_SECOND); + + public static final String CODESYSTEM_READ_ACCESS_TAG = "http://dsf.dev/fhir/CodeSystem/read-access-tag"; + public static final String CODESYSTEM_READ_ACCESS_TAG_VALUE_ALL = "ALL"; + + public static final String EXTENSION_URL_PING_STATUS = "http://dsf.dev/fhir/StructureDefinition/extension-ping-status-v2"; + public static final String EXTENSION_URL_NETWORK_SPEED = "http://dsf.dev/fhir/StructureDefinition/extension-network-speed"; + public static final String EXTENSION_URL_CORRELATION_KEY = "correlation-key"; + public static final String EXTENSION_URL_ORGANIZATION_IDENTIFIER = "organization-identifier"; + public static final String EXTENSION_URL_ENDPOINT_IDENTIFIER = "endpoint-identifier"; + public static final String EXTENSION_URL_ERROR_MESSAGE = "error-message"; + public static final String EXTENSION_URL_DOWNLOAD_SPEED = "download-speed"; + public static final String EXTENSION_URL_UPLOAD_SPEED = "upload-speed"; + public static final String EXTENSION_URL_NETWORK_SPEED_UNIT = "unit"; + public static final String EXTENSION_URL_NETWORK_SPEED_VALUE = "network-speed"; + + public static final String BPMN_EXECUTION_VARIABLE_TIMER_INTERVAL = "timerInterval"; + public static final String BPMN_EXECUTION_VARIABLE_STOP_TIMER = "stopTimer"; + public static final String BPMN_EXECUTION_VARIABLE_DOWNLOAD_RESOURCE_SIZE_BYTES = "downloadResourceSizeBytes"; + public static final String BPMN_EXECUTION_VARIABLE_DOWNLOAD_RESOURCE = "downloadResource"; + public static final String BPMN_EXECUTION_VARIABLE_DOWNLOAD_RESOURCE_REFERENCE = "downloadResourceReference"; + private static final String BPMN_EXECUTION_VARIABLE_STATUS_CODE = "statusCode"; + private static final String BPMN_EXECUTION_VARIABLE_ERROR_MESSAGE = "errorMessage"; + private static final String BPMN_EXECUTION_VARIABLE_ERROR_MESSAGE_LIST = "errorMessages"; + private static final String BPMN_EXECUTION_VARIABLE_DOWNLOADED_BYTES = "downloadedBytes"; + private static final String BPMN_EXECUTION_VARIABLE_DOWNLOADED_DURATION_MILLIS = "downloadedDurationMillis"; + public static final String BPMN_EXECUTION_VARIABLE_PONG_TARGET_ENDPOINT_IDENTIFIER = "targetEndpointIdentifier"; + private static final String BPMN_EXECUTION_VARIABLE_UPLOADED_BYTES = "uploadedBytes"; + private static final String BPMN_EXECUTION_VARIABLE_UPLOADED_DURATION_MILLIS = "uploadedDurationMillis"; + public static final String BPMN_EXECUTION_VARIABLE_RESOURCE_DOWNLOAD_ERROR_MESSAGE = "resourceDownloadErrorMessage"; + public static final String BPMN_EXECUTION_VARIABLE_RESOURCE_UPLOAD_ERROR_MESSAGE = "resourceUploadErrorMessage"; + + public static final String BPMN_ERROR_CODE_RESOURCE_DOWNLOAD_ERROR = "resourceDownloadError"; + public static final String BPMN_ERROR_CODE_RESOURCE_UPLOAD_ERROR = "resourceUploadError"; + + public static final String PONG_ERROR_MESSAGE_CLEANUP_TIMEOUT = "Timeout while waiting for cleanup message"; + + public static final int DOWNLOAD_RESOURCE_SIZE_BYTES_DEFAULT = 10000000; + + public static final MediaType DOWNLOAD_RESOURCE_MIME_TYPE = MediaType.APPLICATION_OCTET_STREAM_TYPE; + + public static final String TIMER_INTERVAL_DEFAULT_VALUE = "PT24H"; + + public static String getBpmnExecutionVariableStatusCode() + { + return BPMN_EXECUTION_VARIABLE_STATUS_CODE; + } + + public static String getBpmnExecutionVariableStatusCode(String correlationKey) + { + return BPMN_EXECUTION_VARIABLE_STATUS_CODE + "_" + correlationKey; + } + + public static String getBpmnExecutionVariableErrorMessage() + { + return BPMN_EXECUTION_VARIABLE_ERROR_MESSAGE; + } + + public static String getBpmnExecutionVariableErrorMessage(String correlationKey) + { + return BPMN_EXECUTION_VARIABLE_ERROR_MESSAGE + "_" + correlationKey; + } + + public static String getBpmnExecutionVariableDownloadedBytes() + { + return BPMN_EXECUTION_VARIABLE_DOWNLOADED_BYTES; + } + + public static String getBpmnExecutionVariableDownloadedBytes(String correlationKey) + { + return BPMN_EXECUTION_VARIABLE_DOWNLOADED_BYTES + "_" + correlationKey; + } + + public static String getBpmnExecutionVariableDownloadedDurationMillis() + { + return BPMN_EXECUTION_VARIABLE_DOWNLOADED_DURATION_MILLIS; + } + + public static String getBpmnExecutionVariableDownloadedDurationMillis(String correlationKey) + { + return BPMN_EXECUTION_VARIABLE_DOWNLOADED_DURATION_MILLIS + "_" + correlationKey; + } + + public static String getBpmnExecutionVariableUploadedBytes() + { + return BPMN_EXECUTION_VARIABLE_UPLOADED_BYTES; + } + + public static String getBpmnExecutionVariableUploadedBytes(String correlationKey) + { + return BPMN_EXECUTION_VARIABLE_UPLOADED_BYTES + "_" + correlationKey; + } + + public static String getBpmnExecutionVariableUploadedDurationMillis() + { + return BPMN_EXECUTION_VARIABLE_UPLOADED_DURATION_MILLIS; + } + + public static String getBpmnExecutionVariableUploadedDurationMillis(String correlationKey) + { + return BPMN_EXECUTION_VARIABLE_UPLOADED_DURATION_MILLIS + "_" + correlationKey; + } + + public static String getBpmnExecutionVariableErrorMessageList() + { + return BPMN_EXECUTION_VARIABLE_ERROR_MESSAGE_LIST; + } + + public static String getBpmnExecutionVariableErrorMessageList(String correlationKey) + { + return getBpmnExecutionVariableErrorMessageList() + "_" + correlationKey; + } } diff --git a/src/main/java/dev/dsf/bpe/PingProcessPluginDefinition.java b/src/main/java/dev/dsf/bpe/PingProcessPluginDefinition.java index 1cf39ba8..0a1b65b8 100644 --- a/src/main/java/dev/dsf/bpe/PingProcessPluginDefinition.java +++ b/src/main/java/dev/dsf/bpe/PingProcessPluginDefinition.java @@ -10,8 +10,8 @@ public class PingProcessPluginDefinition implements ProcessPluginDefinition { - public static final String VERSION = "1.0.1.0"; - public static final LocalDate RELEASE_DATE = LocalDate.of(2023, 9, 5); + public static final String VERSION = "2.0.0.0"; + public static final LocalDate RELEASE_DATE = LocalDate.of(2023, 9, 12); @Override public String getName() @@ -52,6 +52,7 @@ public Map> getFhirResourcesByProcessId() var cPing = "fhir/CodeSystem/dsf-ping.xml"; var cPingStatus = "fhir/CodeSystem/dsf-ping-status.xml"; + var cPingUnits = "fhir/CodeSystem/dsf-ping-units.xml"; var sPingStatus = "fhir/StructureDefinition/dsf-extension-ping-status.xml"; var sPing = "fhir/StructureDefinition/dsf-task-ping.xml"; @@ -59,22 +60,25 @@ public Map> getFhirResourcesByProcessId() var sStartPing = "fhir/StructureDefinition/dsf-task-start-ping.xml"; var sStartPingAutostart = "fhir/StructureDefinition/dsf-task-start-ping-autostart.xml"; var sStopPingAutostart = "fhir/StructureDefinition/dsf-task-stop-ping-autostart.xml"; + var sCleanupPong = "fhir/StructureDefinition/dsf-task-cleanup-pong.xml"; + var sNetworkSpeedExtension = "fhir/StructureDefinition/dsf-extension-network-speed.xml"; var tStartPing = "fhir/Task/dsf-task-start-ping.xml"; var tStartPingAutoStart = "fhir/Task/dsf-task-start-ping-autostart.xml"; var tStopPingAutoStart = "fhir/Task/dsf-task-stop-ping-autostart.xml"; var vPing = "fhir/ValueSet/dsf-ping.xml"; + var vPingUnits = "fhir/ValueSet/dsf-ping-units.xml"; var vPingStatus = "fhir/ValueSet/dsf-ping-status.xml"; var vPongStatus = "fhir/ValueSet/dsf-pong-status.xml"; return Map.of(ConstantsPing.PROCESS_NAME_FULL_PING, - Arrays.asList( - aPing, cPing, cPingStatus, sPingStatus, sStartPing, sPong, tStartPing, vPing, vPingStatus), + Arrays.asList(aPing, cPing, cPingStatus, cPingUnits, sPingStatus, sStartPing, sPong, sCleanupPong, + sNetworkSpeedExtension, tStartPing, vPing, vPingStatus, vPingUnits), ConstantsPing.PROCESS_NAME_FULL_PING_AUTOSTART, Arrays.asList(aPingAutostart, cPing, sStartPingAutostart, sStopPingAutostart, tStartPingAutoStart, tStopPingAutoStart, vPing), - ConstantsPing.PROCESS_NAME_FULL_PONG, - Arrays.asList(aPong, cPing, cPingStatus, sPingStatus, sPing, vPing, vPongStatus)); + ConstantsPing.PROCESS_NAME_FULL_PONG, Arrays.asList(aPong, cPing, cPingStatus, cPingUnits, sPingStatus, + sPing, sNetworkSpeedExtension, vPing, vPongStatus, vPingUnits)); } } diff --git a/src/main/java/dev/dsf/bpe/listener/PingPongDeploymentStateListener.java b/src/main/java/dev/dsf/bpe/listener/PingPongDeploymentStateListener.java new file mode 100644 index 00000000..6f6f4489 --- /dev/null +++ b/src/main/java/dev/dsf/bpe/listener/PingPongDeploymentStateListener.java @@ -0,0 +1,73 @@ +package dev.dsf.bpe.listener; + +import java.util.List; +import java.util.Objects; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.InitializingBean; + +import dev.dsf.bpe.ConstantsPing; +import dev.dsf.bpe.spring.config.PingConfig; +import dev.dsf.bpe.v1.ProcessPluginDeploymentStateListener; + +public class PingPongDeploymentStateListener implements ProcessPluginDeploymentStateListener, InitializingBean +{ + private static final Logger logger = LoggerFactory.getLogger(PingPongDeploymentStateListener.class); + + private final PingConfig pingConfig; + private final String networkSpeedUnit; + + public PingPongDeploymentStateListener(PingConfig pingConfig) + { + this.pingConfig = pingConfig; + this.networkSpeedUnit = pingConfig.getNetworkSpeedUnit(); + } + + @Override + public void onProcessesDeployed(List processes) + { + logger.debug("Validating plugin configuration..."); + if (ConstantsPing.CODESYSTEM_DSF_PING_UNITS_VALUES.contains(networkSpeedUnit)) + { + logger.debug("Network speed unit is valid: {}", networkSpeedUnit); + } + else + { + pingConfig.setNetworkSpeedUnit(ConstantsPing.CODESYSTEM_DSF_PING_UNITS_VALUE_MEGABYTES_PER_SECOND); + logger.debug("Network speed unit \"{}\" is not valid. Valid values are: {}. Defaulting to \"{}\"", + networkSpeedUnit, ConstantsPing.CODESYSTEM_DSF_PING_UNITS_VALUES, + ConstantsPing.CODESYSTEM_DSF_PING_UNITS_VALUE_MEGABYTES_PER_SECOND); + } + + // TODO: fixme + int maxDownloadSizeBytes = pingConfig.getMaxDownloadSizeBytes(); + int maxDownloadSizeBytesHeapFix = 100000000; + if (maxDownloadSizeBytes > maxDownloadSizeBytesHeapFix) + { + pingConfig.setMaxDownloadSizeBytes(maxDownloadSizeBytesHeapFix); + logger.debug( + "MaxDownloadSizeBytes is too large. Setting maxDownloadSizeBytes to {}. This avoids Java running out of memory and will be fixed in a future release", + maxDownloadSizeBytesHeapFix); + } + + int maxUploadSizeBytes = pingConfig.getMaxUploadSizeBytes(); + int maxUploadSizeBytesHeapFix = 100000000; + if (maxUploadSizeBytes > maxUploadSizeBytesHeapFix) + { + pingConfig.setMaxUploadSizeBytes(maxUploadSizeBytesHeapFix); + logger.debug( + "MaxUploadSizeBytes is too large. Setting maxUploadSizeBytes to {}. This avoids Java running out of memory and will be fixed in a future release", + maxUploadSizeBytesHeapFix); + } + + logger.debug("Configuration validation complete."); + } + + @Override + public void afterPropertiesSet() throws Exception + { + Objects.requireNonNull(networkSpeedUnit); + Objects.requireNonNull(pingConfig); + } +} diff --git a/src/main/java/dev/dsf/bpe/listener/SetCorrelationKeyListener.java b/src/main/java/dev/dsf/bpe/listener/SetCorrelationKeyListener.java index 52089288..ec487ac1 100644 --- a/src/main/java/dev/dsf/bpe/listener/SetCorrelationKeyListener.java +++ b/src/main/java/dev/dsf/bpe/listener/SetCorrelationKeyListener.java @@ -6,6 +6,7 @@ import org.camunda.bpm.engine.delegate.ExecutionListener; import org.springframework.beans.factory.InitializingBean; +import dev.dsf.bpe.util.logging.PingPongLogger; import dev.dsf.bpe.v1.ProcessPluginApi; import dev.dsf.bpe.v1.constants.BpmnExecutionVariables; import dev.dsf.bpe.v1.variables.Target; @@ -29,6 +30,9 @@ public void afterPropertiesSet() throws Exception @Override public void notify(DelegateExecution execution) throws Exception { + PingPongLogger logger = new PingPongLogger(SetCorrelationKeyListener.class, + api.getVariables(execution).getStartTask()); + logger.debug("Setting correlation key for subprocess instance {}", execution.getProcessInstanceId()); Variables variables = api.getVariables(execution); Target target = variables.getTarget(); diff --git a/src/main/java/dev/dsf/bpe/message/CleanupPong.java b/src/main/java/dev/dsf/bpe/message/CleanupPong.java new file mode 100644 index 00000000..27eb0deb --- /dev/null +++ b/src/main/java/dev/dsf/bpe/message/CleanupPong.java @@ -0,0 +1,80 @@ +package dev.dsf.bpe.message; + +import java.util.stream.Stream; + +import org.camunda.bpm.engine.delegate.DelegateExecution; +import org.hl7.fhir.r4.model.Task; + +import dev.dsf.bpe.ConstantsPing; +import dev.dsf.bpe.util.task.input.generator.DownloadedBytesGenerator; +import dev.dsf.bpe.util.task.input.generator.DownloadedDurationMillisGenerator; +import dev.dsf.bpe.util.task.input.generator.NetworkSpeedMetricGenerator; +import dev.dsf.bpe.v1.ProcessPluginApi; +import dev.dsf.bpe.v1.activity.AbstractTaskMessageSend; +import dev.dsf.bpe.v1.variables.Target; +import dev.dsf.bpe.v1.variables.Variables; + +public class CleanupPong extends AbstractTaskMessageSend +{ + public CleanupPong(ProcessPluginApi api) + { + super(api); + } + + @Override + protected Stream getAdditionalInputParameters(DelegateExecution execution, + Variables variables) + { + Target target = variables.getTarget(); + String correlationKey = target.getCorrelationKey(); + Integer downloadedBytes = variables + .getInteger(ConstantsPing.getBpmnExecutionVariableDownloadedBytes(correlationKey)); + Long downloadedDurationMillis = variables + .getLong(ConstantsPing.getBpmnExecutionVariableDownloadedDurationMillis(correlationKey)); + + Stream downloadedBytesParameter = downloadedBytes != null + ? Stream.of(DownloadedBytesGenerator.create(downloadedBytes)) + : Stream.empty(); + Stream downloadedDurationMillisParameter = downloadedDurationMillis != null + ? Stream.of(DownloadedDurationMillisGenerator.create(downloadedDurationMillis)) + : Stream.empty(); + + return Stream.of(downloadedBytesParameter, downloadedDurationMillisParameter).flatMap(s -> s); + } + + @Override + protected void sendTask(DelegateExecution execution, Variables variables, Target target, + String instantiatesCanonical, String messageName, String businessKey, String profile, + Stream additionalInputParameters) + { + Target newTarget = new Target() + { + @Override + public String getOrganizationIdentifierValue() + { + return target.getOrganizationIdentifierValue(); + } + + @Override + public String getEndpointIdentifierValue() + { + return target.getEndpointIdentifierValue(); + } + + @Override + public String getEndpointUrl() + { + return target.getEndpointUrl(); + } + + @Override + public String getCorrelationKey() + { + return null; + } + }; + + super.sendTask(execution, variables, newTarget, instantiatesCanonical, messageName, businessKey, profile, + additionalInputParameters); + } +} diff --git a/src/main/java/dev/dsf/bpe/message/SendPing.java b/src/main/java/dev/dsf/bpe/message/SendPing.java index 421fc176..1c9248b4 100644 --- a/src/main/java/dev/dsf/bpe/message/SendPing.java +++ b/src/main/java/dev/dsf/bpe/message/SendPing.java @@ -8,10 +8,15 @@ import org.hl7.fhir.r4.model.ResourceType; import org.hl7.fhir.r4.model.Task; import org.hl7.fhir.r4.model.Task.ParameterComponent; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import dev.dsf.bpe.ConstantsPing; +import dev.dsf.bpe.util.task.input.generator.DownloadResourceReferenceGenerator; +import dev.dsf.bpe.util.task.input.generator.DownloadResourceSizeGenerator; import dev.dsf.bpe.v1.ProcessPluginApi; import dev.dsf.bpe.v1.activity.AbstractTaskMessageSend; +import dev.dsf.bpe.v1.variables.Target; import dev.dsf.bpe.v1.variables.Variables; import jakarta.ws.rs.WebApplicationException; import jakarta.ws.rs.core.Response; @@ -19,6 +24,8 @@ public class SendPing extends AbstractTaskMessageSend { + private static final Logger logger = LoggerFactory.getLogger(SendPing.class); + public SendPing(ProcessPluginApi api) { super(api); @@ -27,23 +34,38 @@ public SendPing(ProcessPluginApi api) @Override protected Stream getAdditionalInputParameters(DelegateExecution execution, Variables variables) { - return Stream.of(api.getTaskHelper().createInput( + String downloadResourceReference = variables + .getString(ConstantsPing.BPMN_EXECUTION_VARIABLE_DOWNLOAD_RESOURCE_REFERENCE); + int downloadResourceSizeBytes = variables + .getInteger(ConstantsPing.BPMN_EXECUTION_VARIABLE_DOWNLOAD_RESOURCE_SIZE_BYTES); + + Stream downloadResourceReferenceStream = downloadResourceReference == null ? Stream.empty() + : Stream.of(DownloadResourceReferenceGenerator.create(downloadResourceReference)); + Stream downloadResourceSizeBytesStream = Stream + .of(DownloadResourceSizeGenerator.create(downloadResourceSizeBytes)); + Stream endpointIdentifierStream = Stream.of(api.getTaskHelper().createInput( new Reference().setIdentifier(getLocalEndpointIdentifier()).setType(ResourceType.Endpoint.name()), ConstantsPing.CODESYSTEM_DSF_PING, ConstantsPing.CODESYSTEM_DSF_PING_VALUE_ENDPOINT_IDENTIFIER)); + + return Stream.concat(endpointIdentifierStream, + Stream.concat(downloadResourceReferenceStream, downloadResourceSizeBytesStream)); } @Override - protected void handleIntermediateThrowEventError(DelegateExecution execution, Variables variables, - Exception exception, String errorMessage) + protected void handleSendTaskError(DelegateExecution execution, Variables variables, Exception exception, + String errorMessage) { + Target target = variables.getTarget(); + String statusCode = exception instanceof WebApplicationException w && w.getResponse() != null && w.getResponse().getStatus() == Response.Status.FORBIDDEN.getStatusCode() ? ConstantsPing.CODESYSTEM_DSF_PING_STATUS_VALUE_NOT_ALLOWED : ConstantsPing.CODESYSTEM_DSF_PING_STATUS_VALUE_NOT_REACHABLE; - execution.setVariableLocal("statusCode", statusCode); + execution.setVariableLocal(ConstantsPing.getBpmnExecutionVariableStatusCode(), statusCode); String specialErrorMessage = createErrorMessage(exception); - execution.setVariableLocal("errorMessage", specialErrorMessage); + execution.setVariableLocal(ConstantsPing.getBpmnExecutionVariableErrorMessage(), specialErrorMessage); + logger.info("Request to {} resulted in status {}", target.getEndpointUrl(), statusCode); } @Override @@ -58,10 +80,11 @@ private String createErrorMessage(Exception exception) && (exception.getMessage() == null || exception.getMessage().isBlank())) { StatusType statusInfo = w.getResponse().getStatusInfo(); - return statusInfo.getStatusCode() + " " + statusInfo.getReasonPhrase(); + return "Error when sending ping message: " + statusInfo.getStatusCode() + " " + + statusInfo.getReasonPhrase(); } else - return exception.getMessage(); + return "Error when sending ping message: " + exception.getMessage(); } private Identifier getLocalEndpointIdentifier() diff --git a/src/main/java/dev/dsf/bpe/message/SendPong.java b/src/main/java/dev/dsf/bpe/message/SendPong.java index 123a3441..0c738c35 100644 --- a/src/main/java/dev/dsf/bpe/message/SendPong.java +++ b/src/main/java/dev/dsf/bpe/message/SendPong.java @@ -1,13 +1,21 @@ package dev.dsf.bpe.message; +import java.util.List; import java.util.Objects; +import java.util.stream.Stream; import org.camunda.bpm.engine.delegate.DelegateExecution; import org.hl7.fhir.r4.model.Task; import dev.dsf.bpe.ConstantsPing; import dev.dsf.bpe.mail.ErrorMailService; -import dev.dsf.bpe.util.PingStatusGenerator; +import dev.dsf.bpe.util.ErrorMessageListUtils; +import dev.dsf.bpe.util.logging.PingPongLogger; +import dev.dsf.bpe.util.task.input.generator.DownloadResourceReferenceGenerator; +import dev.dsf.bpe.util.task.input.generator.DownloadedBytesGenerator; +import dev.dsf.bpe.util.task.input.generator.DownloadedDurationMillisGenerator; +import dev.dsf.bpe.util.task.input.generator.ErrorMessageGenerator; +import dev.dsf.bpe.util.task.output.generator.PingStatusGenerator; import dev.dsf.bpe.v1.ProcessPluginApi; import dev.dsf.bpe.v1.activity.AbstractTaskMessageSend; import dev.dsf.bpe.v1.variables.Target; @@ -18,14 +26,12 @@ public class SendPong extends AbstractTaskMessageSend { - private final PingStatusGenerator statusGenerator; private final ErrorMailService errorMailService; - public SendPong(ProcessPluginApi api, PingStatusGenerator statusGenerator, ErrorMailService errorMailService) + public SendPong(ProcessPluginApi api, ErrorMailService errorMailService) { super(api); - this.statusGenerator = statusGenerator; this.errorMailService = errorMailService; } @@ -34,48 +40,77 @@ public void afterPropertiesSet() throws Exception { super.afterPropertiesSet(); - Objects.requireNonNull(statusGenerator, "statusGenerator"); Objects.requireNonNull(errorMailService, "errorMailService"); } @Override - protected void doExecute(DelegateExecution execution, Variables variables) throws Exception + protected Stream getAdditionalInputParameters(DelegateExecution execution, + Variables variables) { - super.doExecute(execution, variables); + List errorList = ErrorMessageListUtils.getErrorMessageList(execution); + int downloadResourceSizeBytes = variables + .getInteger(ConstantsPing.BPMN_EXECUTION_VARIABLE_DOWNLOAD_RESOURCE_SIZE_BYTES); + if (downloadResourceSizeBytes >= 0) + { + Integer downloadedBytes = variables.getInteger(ConstantsPing.getBpmnExecutionVariableDownloadedBytes()); + Long downloadedDurationMillis = variables + .getLong(ConstantsPing.getBpmnExecutionVariableDownloadedDurationMillis()); + String downloadResourceReference = variables + .getString(ConstantsPing.BPMN_EXECUTION_VARIABLE_DOWNLOAD_RESOURCE_REFERENCE); + + Stream downloadedBytesParameter = downloadedBytes != null + ? Stream.of(DownloadedBytesGenerator.create(downloadedBytes)) + : Stream.empty(); + Stream downloadedDurationMillisParameter = downloadedDurationMillis != null + ? Stream.of(DownloadedDurationMillisGenerator.create(downloadedDurationMillis)) + : Stream.empty(); + Stream downloadedResourceReferenceParameter = downloadResourceReference != null + ? Stream.of(DownloadResourceReferenceGenerator.create(downloadResourceReference)) + : Stream.empty(); + + return Stream + .of(downloadedBytesParameter, downloadedDurationMillisParameter, + downloadedResourceReferenceParameter, ErrorMessageGenerator.create(errorList).stream()) + .flatMap(stream -> stream); + } + else + { + return ErrorMessageGenerator.create(errorList).stream(); + } + } + @Override + protected void doExecute(DelegateExecution execution, Variables variables) throws Exception + { Target target = variables.getTarget(); Task mainTask = variables.getStartTask(); - mainTask.addOutput(statusGenerator.createPongStatusOutput(target, - ConstantsPing.CODESYSTEM_DSF_PING_STATUS_VALUE_PONG_SEND)); + PingStatusGenerator.updatePongStatusOutput(mainTask, target); + PingStatusGenerator.updatePongStatusOutput(mainTask, ConstantsPing.CODESYSTEM_DSF_PING_STATUS_VALUE_PONG_SENT); variables.updateTask(mainTask); + super.doExecute(execution, variables); } @Override - protected void handleEndEventError(DelegateExecution execution, Variables variables, Exception exception, + protected void handleSendTaskError(DelegateExecution execution, Variables variables, Exception exception, String errorMessage) { + PingPongLogger logger = new PingPongLogger(SendPong.class, variables.getStartTask()); Target target = variables.getTarget(); - Task mainTask = variables.getStartTask(); + Task startTask = variables.getStartTask(); - if (mainTask != null) - { - String statusCode = exception instanceof WebApplicationException w && w.getResponse() != null - && w.getResponse().getStatus() == Response.Status.FORBIDDEN.getStatusCode() - ? ConstantsPing.CODESYSTEM_DSF_PING_STATUS_VALUE_NOT_ALLOWED - : ConstantsPing.CODESYSTEM_DSF_PING_STATUS_VALUE_NOT_REACHABLE; - - String specialErrorMessage = createErrorMessage(exception); + String statusCode = exception instanceof WebApplicationException w && w.getResponse() != null + && w.getResponse().getStatus() == Response.Status.FORBIDDEN.getStatusCode() + ? ConstantsPing.CODESYSTEM_DSF_PING_STATUS_VALUE_NOT_ALLOWED + : ConstantsPing.CODESYSTEM_DSF_PING_STATUS_VALUE_NOT_REACHABLE; + execution.setVariable(ConstantsPing.getBpmnExecutionVariableStatusCode(), statusCode); - mainTask.addOutput(statusGenerator.createPongStatusOutput(target, statusCode, specialErrorMessage)); - variables.updateTask(mainTask); - - if (ConstantsPing.CODESYSTEM_DSF_PING_STATUS_VALUE_NOT_REACHABLE.equals(statusCode)) - errorMailService.endpointNotReachableForPong(mainTask.getIdElement(), target, specialErrorMessage); - else if (ConstantsPing.CODESYSTEM_DSF_PING_STATUS_VALUE_NOT_ALLOWED.equals(statusCode)) - errorMailService.endpointReachablePongForbidden(mainTask.getIdElement(), target, specialErrorMessage); - } + String specialErrorMessage = createErrorMessage(exception); + ErrorMessageListUtils.add(specialErrorMessage, execution); + PingStatusGenerator.updatePongStatusOutput(startTask, statusCode); + variables.setString(ConstantsPing.getBpmnExecutionVariableStatusCode(), statusCode); - super.handleEndEventError(execution, variables, exception, errorMessage); + logger.info("Request to {} resulted in status {}", target.getEndpointUrl(), statusCode); + variables.updateTask(startTask); } private String createErrorMessage(Exception exception) @@ -84,9 +119,10 @@ private String createErrorMessage(Exception exception) && (exception.getMessage() == null || exception.getMessage().isBlank())) { StatusType statusInfo = w.getResponse().getStatusInfo(); - return statusInfo.getStatusCode() + " " + statusInfo.getReasonPhrase(); + return "Error when sending pong message: " + statusInfo.getStatusCode() + " " + + statusInfo.getReasonPhrase(); } else - return exception.getMessage(); + return "Error when sending ping message: " + exception.getMessage(); } } diff --git a/src/main/java/dev/dsf/bpe/message/SendStartPing.java b/src/main/java/dev/dsf/bpe/message/SendStartPing.java index 196d828b..c7cdeadc 100644 --- a/src/main/java/dev/dsf/bpe/message/SendStartPing.java +++ b/src/main/java/dev/dsf/bpe/message/SendStartPing.java @@ -1,5 +1,6 @@ package dev.dsf.bpe.message; +import java.util.UUID; import java.util.stream.Stream; import org.camunda.bpm.engine.delegate.DelegateExecution; @@ -9,6 +10,7 @@ import dev.dsf.bpe.ConstantsPing; import dev.dsf.bpe.v1.ProcessPluginApi; import dev.dsf.bpe.v1.activity.AbstractTaskMessageSend; +import dev.dsf.bpe.v1.variables.Target; import dev.dsf.bpe.v1.variables.Variables; public class SendStartPing extends AbstractTaskMessageSend @@ -21,9 +23,36 @@ public SendStartPing(ProcessPluginApi api) @Override protected Stream getAdditionalInputParameters(DelegateExecution execution, Variables variables) { - return variables.getStartTask().getInput().stream().filter(Task.ParameterComponent::hasType) - .filter(i -> i.getType().getCoding().stream() + return Stream.concat( + variables.getStartTask().getInput().stream().filter(Task.ParameterComponent::hasType).filter(i -> i + .getType().getCoding().stream() .anyMatch(c -> ConstantsPing.CODESYSTEM_DSF_PING.equals(c.getSystem()) - && ConstantsPing.CODESYSTEM_DSF_PING_VALUE_TARGET_ENDPOINTS.equals(c.getCode()))); + && ConstantsPing.CODESYSTEM_DSF_PING_VALUE_TARGET_ENDPOINTS.equals(c.getCode()))), + Stream.of(getDownloadResourceSizeInputParameter(variables))); + } + + private ParameterComponent getDownloadResourceSizeInputParameter(Variables variables) + { + return variables.getStartTask().getInput().stream().filter(this::isDownloadResourceSizeParameter).findFirst() + .orElseThrow(); + } + + private boolean isDownloadResourceSizeParameter(ParameterComponent parameterComponent) + { + return parameterComponent.getType().getCoding().stream() + .anyMatch(t -> ConstantsPing.CODESYSTEM_DSF_PING.equals(t.getSystem()) + && ConstantsPing.CODESYSTEM_DSF_PING_VALUE_DOWNLOAD_RESOURCE_SIZE_BYTES.equals(t.getCode())); + } + + @Override + protected void sendTask(DelegateExecution execution, Variables variables, Target target, + String instantiatesCanonical, String messageName, String businessKey, String profile, + Stream additionalInputParameters) + { + // different business-key for every start-ping execution + businessKey = UUID.randomUUID().toString(); + + super.sendTask(execution, variables, target, instantiatesCanonical, messageName, businessKey, profile, + additionalInputParameters); } } diff --git a/src/main/java/dev/dsf/bpe/service/Cleanup.java b/src/main/java/dev/dsf/bpe/service/Cleanup.java new file mode 100644 index 00000000..064632a0 --- /dev/null +++ b/src/main/java/dev/dsf/bpe/service/Cleanup.java @@ -0,0 +1,41 @@ +package dev.dsf.bpe.service; + +import org.camunda.bpm.engine.delegate.BpmnError; +import org.camunda.bpm.engine.delegate.DelegateExecution; +import org.hl7.fhir.r4.model.Binary; +import org.hl7.fhir.r4.model.IdType; + +import dev.dsf.bpe.ConstantsPing; +import dev.dsf.bpe.util.logging.PingPongLogger; +import dev.dsf.bpe.v1.ProcessPluginApi; +import dev.dsf.bpe.v1.activity.AbstractServiceDelegate; +import dev.dsf.bpe.v1.variables.Variables; + +public class Cleanup extends AbstractServiceDelegate +{ + public Cleanup(ProcessPluginApi api) + { + super(api); + } + + @Override + protected void doExecute(DelegateExecution delegateExecution, Variables variables) throws BpmnError, Exception + { + PingPongLogger logger = new PingPongLogger(Cleanup.class, variables.getStartTask()); + logger.debug("Cleaning up..."); + String downloadResourceId = new IdType( + variables.getString(ConstantsPing.BPMN_EXECUTION_VARIABLE_DOWNLOAD_RESOURCE_REFERENCE)).getIdPart(); + if (downloadResourceId != null) + { + api.getFhirWebserviceClientProvider().getLocalWebserviceClient().delete(Binary.class, downloadResourceId); + api.getFhirWebserviceClientProvider().getLocalWebserviceClient().deletePermanently(Binary.class, + downloadResourceId); + logger.debug("Deleted Binary resource with ID {}", downloadResourceId); + } + else + { + logger.debug("Nothing to do"); + } + logger.debug("Cleanup complete."); + } +} diff --git a/src/main/java/dev/dsf/bpe/service/GenerateAndStoreResource.java b/src/main/java/dev/dsf/bpe/service/GenerateAndStoreResource.java new file mode 100644 index 00000000..d842d60f --- /dev/null +++ b/src/main/java/dev/dsf/bpe/service/GenerateAndStoreResource.java @@ -0,0 +1,85 @@ +package dev.dsf.bpe.service; + +import java.io.ByteArrayInputStream; +import java.util.Random; + +import org.camunda.bpm.engine.delegate.BpmnError; +import org.camunda.bpm.engine.delegate.DelegateExecution; +import org.hl7.fhir.r4.model.IdType; + +import dev.dsf.bpe.ConstantsPing; +import dev.dsf.bpe.util.logging.PingPongLogger; +import dev.dsf.bpe.v1.ProcessPluginApi; +import dev.dsf.bpe.v1.activity.AbstractServiceDelegate; +import dev.dsf.bpe.v1.variables.Variables; +import jakarta.ws.rs.WebApplicationException; + +public class GenerateAndStoreResource extends AbstractServiceDelegate +{ + private final int maxUploadSizeBytes; + + public GenerateAndStoreResource(ProcessPluginApi api, int maxUploadSizeBytes) + { + super(api); + this.maxUploadSizeBytes = maxUploadSizeBytes; + } + + @Override + protected void doExecute(DelegateExecution delegateExecution, Variables variables) throws BpmnError, Exception + { + PingPongLogger logger = new PingPongLogger(GenerateAndStoreResource.class, variables.getStartTask()); + logger.debug("Generating resource..."); + int downloadResourceSizeBytes = getDownloadResourceSize(variables); + + byte[] resourceContent = generateRandomBinaryContent(downloadResourceSizeBytes, logger); + variables.setInteger(ConstantsPing.BPMN_EXECUTION_VARIABLE_DOWNLOAD_RESOURCE_SIZE_BYTES, + resourceContent.length); + logger.debug("Generated resource."); + logger.debug("Storing binary resource for download..."); + + try + { + IdType downloadResource = storeBinary(resourceContent); + + String reference = downloadResource.getValueAsString(); + + variables.setString(ConstantsPing.BPMN_EXECUTION_VARIABLE_DOWNLOAD_RESOURCE_REFERENCE, reference); + + logger.debug("Stored binary resource for download"); + } + catch (Exception e) + { + throw new BpmnError(ConstantsPing.BPMN_ERROR_CODE_RESOURCE_UPLOAD_ERROR, e.getMessage()); + } + } + + private byte[] generateRandomBinaryContent(int desiredSizeBytes, PingPongLogger logger) + { + int sizeBytes = Math.min(maxUploadSizeBytes, desiredSizeBytes); + byte[] bytes = generateRandomByteArray(sizeBytes); + logger.info( + "Generated binary content for network speed measurement. Requested size was: {} bytes, generated size was : {}", + desiredSizeBytes, bytes.length); + return bytes; + } + + private byte[] generateRandomByteArray(int sizeBytes) + { + Random rand = new Random(); + byte[] randomBytes = new byte[sizeBytes]; + rand.nextBytes(randomBytes); + return randomBytes; + } + + private int getDownloadResourceSize(Variables variables) + { + return variables.getInteger(ConstantsPing.BPMN_EXECUTION_VARIABLE_DOWNLOAD_RESOURCE_SIZE_BYTES); + } + + private IdType storeBinary(byte[] downloadResourceContent) + { + return api.getFhirWebserviceClientProvider().getLocalWebserviceClient().withMinimalReturn().createBinary( + new ByteArrayInputStream(downloadResourceContent), ConstantsPing.DOWNLOAD_RESOURCE_MIME_TYPE, + api.getOrganizationProvider().getLocalOrganization().get().getIdElement().getValue()); + } +} diff --git a/src/main/java/dev/dsf/bpe/service/LogNoResponse.java b/src/main/java/dev/dsf/bpe/service/LogNoResponse.java deleted file mode 100644 index b8ec0cc3..00000000 --- a/src/main/java/dev/dsf/bpe/service/LogNoResponse.java +++ /dev/null @@ -1,34 +0,0 @@ -package dev.dsf.bpe.service; - -import org.camunda.bpm.engine.delegate.BpmnError; -import org.camunda.bpm.engine.delegate.DelegateExecution; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import dev.dsf.bpe.ConstantsPing; -import dev.dsf.bpe.v1.ProcessPluginApi; -import dev.dsf.bpe.v1.activity.AbstractServiceDelegate; -import dev.dsf.bpe.v1.variables.Target; -import dev.dsf.bpe.v1.variables.Variables; - -public class LogNoResponse extends AbstractServiceDelegate -{ - private static final Logger logger = LoggerFactory.getLogger(LogNoResponse.class); - - public LogNoResponse(ProcessPluginApi api) - { - super(api); - } - - @Override - protected void doExecute(DelegateExecution execution, Variables variables) throws BpmnError, Exception - { - Target target = variables.getTarget(); - - logger.warn("PONG from organization {} (endpoint {}) missing", target.getOrganizationIdentifierValue(), - target.getEndpointIdentifierValue()); - - variables.setString("statusCode_" + target.getCorrelationKey(), - ConstantsPing.CODESYSTEM_DSF_PING_STATUS_VALUE_PONG_MISSING); - } -} diff --git a/src/main/java/dev/dsf/bpe/service/LogPong.java b/src/main/java/dev/dsf/bpe/service/LogPong.java deleted file mode 100644 index 72fa6168..00000000 --- a/src/main/java/dev/dsf/bpe/service/LogPong.java +++ /dev/null @@ -1,36 +0,0 @@ -package dev.dsf.bpe.service; - -import org.camunda.bpm.engine.delegate.BpmnError; -import org.camunda.bpm.engine.delegate.DelegateExecution; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import dev.dsf.bpe.ConstantsPing; -import dev.dsf.bpe.v1.ProcessPluginApi; -import dev.dsf.bpe.v1.activity.AbstractServiceDelegate; -import dev.dsf.bpe.v1.variables.Target; -import dev.dsf.bpe.v1.variables.Variables; - -public class LogPong extends AbstractServiceDelegate -{ - private static final Logger logger = LoggerFactory.getLogger(LogPong.class); - - public LogPong(ProcessPluginApi api) - { - super(api); - - } - - @Override - protected void doExecute(DelegateExecution execution, Variables variables) throws BpmnError, Exception - { - Target target = variables.getTarget(); - - logger.info("PONG from {} (endpoint: {})", target.getOrganizationIdentifierValue(), - target.getEndpointIdentifierValue()); - - execution.removeVariable("statusCode"); - variables.setString("statusCode_" + target.getCorrelationKey(), - ConstantsPing.CODESYSTEM_DSF_PING_STATUS_VALUE_PONG_RECEIVED); - } -} diff --git a/src/main/java/dev/dsf/bpe/service/LogSendError.java b/src/main/java/dev/dsf/bpe/service/LogSendError.java deleted file mode 100644 index ed06bfcc..00000000 --- a/src/main/java/dev/dsf/bpe/service/LogSendError.java +++ /dev/null @@ -1,35 +0,0 @@ -package dev.dsf.bpe.service; - -import org.camunda.bpm.engine.delegate.BpmnError; -import org.camunda.bpm.engine.delegate.DelegateExecution; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import dev.dsf.bpe.v1.ProcessPluginApi; -import dev.dsf.bpe.v1.activity.AbstractServiceDelegate; -import dev.dsf.bpe.v1.variables.Target; -import dev.dsf.bpe.v1.variables.Variables; - -public class LogSendError extends AbstractServiceDelegate -{ - private static final Logger logger = LoggerFactory.getLogger(LogSendError.class); - - public LogSendError(ProcessPluginApi api) - { - super(api); - } - - @Override - protected void doExecute(DelegateExecution execution, Variables variables) throws BpmnError, Exception - { - Target target = variables.getTarget(); - String statusCode = (String) execution.getVariableLocal("statusCode"); - String errorMessage = (String) execution.getVariableLocal("errorMessage"); - - logger.warn("Unable to send PING to {} (endpoint: {}): {}", target.getOrganizationIdentifierValue(), - target.getEndpointIdentifierValue(), errorMessage); - - variables.setString("statusCode_" + target.getCorrelationKey(), statusCode); - variables.setString("errorMessage_" + target.getCorrelationKey(), errorMessage); - } -} diff --git a/src/main/java/dev/dsf/bpe/service/SaveResults.java b/src/main/java/dev/dsf/bpe/service/SaveResults.java deleted file mode 100644 index 1edc5575..00000000 --- a/src/main/java/dev/dsf/bpe/service/SaveResults.java +++ /dev/null @@ -1,77 +0,0 @@ -package dev.dsf.bpe.service; - -import java.util.Comparator; -import java.util.Objects; - -import org.camunda.bpm.engine.delegate.BpmnError; -import org.camunda.bpm.engine.delegate.DelegateExecution; -import org.hl7.fhir.r4.model.Task; -import org.springframework.beans.factory.InitializingBean; - -import dev.dsf.bpe.ConstantsPing; -import dev.dsf.bpe.mail.ErrorMailService; -import dev.dsf.bpe.util.PingStatusGenerator; -import dev.dsf.bpe.v1.ProcessPluginApi; -import dev.dsf.bpe.v1.activity.AbstractServiceDelegate; -import dev.dsf.bpe.v1.variables.Target; -import dev.dsf.bpe.v1.variables.Targets; -import dev.dsf.bpe.v1.variables.Variables; - -public class SaveResults extends AbstractServiceDelegate implements InitializingBean -{ - private final PingStatusGenerator statusGenerator; - private final ErrorMailService errorMailService; - - public SaveResults(ProcessPluginApi api, PingStatusGenerator statusGenerator, ErrorMailService errorMailService) - { - super(api); - - this.statusGenerator = statusGenerator; - this.errorMailService = errorMailService; - } - - @Override - public void afterPropertiesSet() throws Exception - { - super.afterPropertiesSet(); - - Objects.requireNonNull(statusGenerator, "statusGenerator"); - Objects.requireNonNull(errorMailService, "errorMailService"); - } - - @Override - protected void doExecute(DelegateExecution execution, Variables variables) throws BpmnError, Exception - { - Task task = variables.getStartTask(); - Targets targets = variables.getTargets(); - - targets.getEntries().stream().sorted(Comparator.comparing(Target::getEndpointIdentifierValue)).forEach(target -> - { - String correlationKey = target.getCorrelationKey(); - - String statusCode = variables.getString("statusCode_" + correlationKey); - if (ConstantsPing.CODESYSTEM_DSF_PING_STATUS_VALUE_NOT_REACHABLE.equals(statusCode) - || ConstantsPing.CODESYSTEM_DSF_PING_STATUS_VALUE_NOT_ALLOWED.equals(statusCode)) - { - String errorMessage = variables.getString("errorMessage_" + correlationKey); - task.addOutput(statusGenerator.createPingStatusOutput(target, statusCode, errorMessage)); - - if (ConstantsPing.CODESYSTEM_DSF_PING_STATUS_VALUE_NOT_REACHABLE.equals(statusCode)) - errorMailService.endpointNotReachableForPing(task.getIdElement(), target, errorMessage); - else if (ConstantsPing.CODESYSTEM_DSF_PING_STATUS_VALUE_NOT_ALLOWED.equals(statusCode)) - errorMailService.endpointReachablePingForbidden(task.getIdElement(), target, errorMessage); - } - else - { - task.addOutput(statusGenerator.createPingStatusOutput(target, statusCode)); - - if (ConstantsPing.CODESYSTEM_DSF_PING_STATUS_VALUE_PONG_MISSING.equals(statusCode)) - errorMailService.pongMessageNotReceived(task.getIdElement(), target); - } - }); - - // TODO only send one combined status mail - - variables.updateTask(task); - } -} diff --git a/src/main/java/dev/dsf/bpe/service/SetDownloadResourceSize.java b/src/main/java/dev/dsf/bpe/service/SetDownloadResourceSize.java new file mode 100644 index 00000000..31e5d7c1 --- /dev/null +++ b/src/main/java/dev/dsf/bpe/service/SetDownloadResourceSize.java @@ -0,0 +1,46 @@ +package dev.dsf.bpe.service; + +import java.util.Optional; + +import org.camunda.bpm.engine.delegate.BpmnError; +import org.camunda.bpm.engine.delegate.DelegateExecution; +import org.hl7.fhir.r4.model.IntegerType; + +import dev.dsf.bpe.ConstantsPing; +import dev.dsf.bpe.util.logging.PingPongLogger; +import dev.dsf.bpe.v1.ProcessPluginApi; +import dev.dsf.bpe.v1.activity.AbstractServiceDelegate; +import dev.dsf.bpe.v1.variables.Variables; + +public class SetDownloadResourceSize extends AbstractServiceDelegate +{ + private final int maxDownloadResourceSizeBytes; + + public SetDownloadResourceSize(ProcessPluginApi api, int maxDownloadResourceSizeBytes) + { + super(api); + this.maxDownloadResourceSizeBytes = maxDownloadResourceSizeBytes; + } + + @Override + protected void doExecute(DelegateExecution delegateExecution, Variables variables) throws BpmnError, Exception + { + PingPongLogger logger = new PingPongLogger(SetDownloadResourceSize.class, variables.getStartTask()); + logger.debug("Setting download resource size..."); + + variables.setInteger(ConstantsPing.BPMN_EXECUTION_VARIABLE_DOWNLOAD_RESOURCE_SIZE_BYTES, + getDownloadResourceSize(variables)); + + logger.debug("Set download resource size to " + maxDownloadResourceSizeBytes); + } + + private int getDownloadResourceSize(Variables variables) + { + Optional downloadResourceSizeType = api.getTaskHelper().getFirstInputParameterValue( + variables.getStartTask(), ConstantsPing.CODESYSTEM_DSF_PING, + ConstantsPing.CODESYSTEM_DSF_PING_VALUE_DOWNLOAD_RESOURCE_SIZE_BYTES, IntegerType.class); + + return downloadResourceSizeType.isPresent() ? downloadResourceSizeType.get().getValue() + : Math.min(maxDownloadResourceSizeBytes, ConstantsPing.DOWNLOAD_RESOURCE_SIZE_BYTES_DEFAULT); + } +} diff --git a/src/main/java/dev/dsf/bpe/service/SetTargetAndConfigureTimer.java b/src/main/java/dev/dsf/bpe/service/autostart/SetTargetAndConfigureTimer.java similarity index 97% rename from src/main/java/dev/dsf/bpe/service/SetTargetAndConfigureTimer.java rename to src/main/java/dev/dsf/bpe/service/autostart/SetTargetAndConfigureTimer.java index 4df0f1a4..a594a7e1 100644 --- a/src/main/java/dev/dsf/bpe/service/SetTargetAndConfigureTimer.java +++ b/src/main/java/dev/dsf/bpe/service/autostart/SetTargetAndConfigureTimer.java @@ -1,4 +1,4 @@ -package dev.dsf.bpe.service; +package dev.dsf.bpe.service.autostart; import org.camunda.bpm.engine.delegate.BpmnError; import org.camunda.bpm.engine.delegate.DelegateExecution; diff --git a/src/main/java/dev/dsf/bpe/service/ping/DownloadResourceAndMeasureSpeedInSubProcess.java b/src/main/java/dev/dsf/bpe/service/ping/DownloadResourceAndMeasureSpeedInSubProcess.java new file mode 100644 index 00000000..6538c8e3 --- /dev/null +++ b/src/main/java/dev/dsf/bpe/service/ping/DownloadResourceAndMeasureSpeedInSubProcess.java @@ -0,0 +1,61 @@ +package dev.dsf.bpe.service.ping; + +import org.camunda.bpm.engine.delegate.BpmnError; +import org.camunda.bpm.engine.delegate.DelegateExecution; +import org.hl7.fhir.r4.model.Task; + +import dev.dsf.bpe.ConstantsPing; +import dev.dsf.bpe.util.BinaryResourceDownloader; +import dev.dsf.bpe.util.logging.PingPongLogger; +import dev.dsf.bpe.v1.ProcessPluginApi; +import dev.dsf.bpe.v1.activity.AbstractServiceDelegate; +import dev.dsf.bpe.v1.variables.Target; +import dev.dsf.bpe.v1.variables.Variables; + +public class DownloadResourceAndMeasureSpeedInSubProcess extends AbstractServiceDelegate +{ + private final int maxDownloadSizeBytes; + + public DownloadResourceAndMeasureSpeedInSubProcess(ProcessPluginApi api, int maxDownloadSizeBytes) + { + super(api); + this.maxDownloadSizeBytes = maxDownloadSizeBytes; + } + + @Override + protected void doExecute(DelegateExecution delegateExecution, Variables variables) throws BpmnError, Exception + { + PingPongLogger logger = new PingPongLogger(DownloadResourceAndMeasureSpeedInSubProcess.class, + variables.getStartTask()); + logger.debug("Starting resource download to measure speed..."); + + Task task = variables.getLatestTask(); + Target target = variables.getTarget(); + String correlationKey = target.getCorrelationKey(); + + try + { + BinaryResourceDownloader.DownloadResult downloadResult = new BinaryResourceDownloader(logger) + .download(variables, api, task, maxDownloadSizeBytes); + + if (downloadResult.getErrorMessage() == null) + { + variables.setInteger(ConstantsPing.getBpmnExecutionVariableDownloadedBytes(correlationKey), + downloadResult.getDownloadedBytes()); + variables.setLong(ConstantsPing.getBpmnExecutionVariableDownloadedDurationMillis(correlationKey), + downloadResult.getDownloadedDurationMillis()); + } + else + { + throw new BpmnError(ConstantsPing.BPMN_ERROR_CODE_RESOURCE_DOWNLOAD_ERROR, + downloadResult.getErrorMessage()); + } + + logger.debug("Completed resource download and measured speed."); + } + catch (Exception e) + { + throw new BpmnError(ConstantsPing.BPMN_ERROR_CODE_RESOURCE_DOWNLOAD_ERROR, e.getMessage()); + } + } +} diff --git a/src/main/java/dev/dsf/bpe/service/ping/LogAndSaveError.java b/src/main/java/dev/dsf/bpe/service/ping/LogAndSaveError.java new file mode 100644 index 00000000..77cd38b0 --- /dev/null +++ b/src/main/java/dev/dsf/bpe/service/ping/LogAndSaveError.java @@ -0,0 +1,33 @@ +package dev.dsf.bpe.service.ping; + +import org.camunda.bpm.engine.delegate.BpmnError; +import org.camunda.bpm.engine.delegate.DelegateExecution; + +import dev.dsf.bpe.ConstantsPing; +import dev.dsf.bpe.util.ErrorMessageListUtils; +import dev.dsf.bpe.util.logging.PingPongLogger; +import dev.dsf.bpe.v1.ProcessPluginApi; +import dev.dsf.bpe.v1.activity.AbstractServiceDelegate; +import dev.dsf.bpe.v1.variables.Target; +import dev.dsf.bpe.v1.variables.Variables; + +public class LogAndSaveError extends AbstractServiceDelegate +{ + public LogAndSaveError(ProcessPluginApi api) + { + super(api); + } + + @Override + protected void doExecute(DelegateExecution delegateExecution, Variables variables) throws BpmnError, Exception + { + PingPongLogger logger = new PingPongLogger(LogAndSaveError.class, variables.getStartTask()); + Target target = variables.getTarget(); + + String errorMessage = variables + .getString(ConstantsPing.BPMN_EXECUTION_VARIABLE_RESOURCE_DOWNLOAD_ERROR_MESSAGE); + ErrorMessageListUtils.add(errorMessage, delegateExecution, target.getCorrelationKey()); + + logger.info("Error while trying to download resource from {}: {}", target.getEndpointUrl(), errorMessage); + } +} diff --git a/src/main/java/dev/dsf/bpe/service/ping/LogAndSaveNoResponse.java b/src/main/java/dev/dsf/bpe/service/ping/LogAndSaveNoResponse.java new file mode 100644 index 00000000..28f6c6f1 --- /dev/null +++ b/src/main/java/dev/dsf/bpe/service/ping/LogAndSaveNoResponse.java @@ -0,0 +1,37 @@ +package dev.dsf.bpe.service.ping; + +import org.camunda.bpm.engine.delegate.BpmnError; +import org.camunda.bpm.engine.delegate.DelegateExecution; + +import dev.dsf.bpe.ConstantsPing; +import dev.dsf.bpe.util.logging.PingPongLogger; +import dev.dsf.bpe.v1.ProcessPluginApi; +import dev.dsf.bpe.v1.activity.AbstractServiceDelegate; +import dev.dsf.bpe.v1.variables.Target; +import dev.dsf.bpe.v1.variables.Variables; + +public class LogAndSaveNoResponse extends AbstractServiceDelegate +{ + public LogAndSaveNoResponse(ProcessPluginApi api) + { + super(api); + } + + @Override + protected void doExecute(DelegateExecution delegateExecution, Variables variables) throws BpmnError, Exception + { + PingPongLogger logger = new PingPongLogger(LogAndSaveNoResponse.class, variables.getStartTask()); + logger.debug("Saving no response to process execution..."); + + Target target = variables.getTarget(); + logger.info("No PONG received from endpoint '{}'", target.getEndpointIdentifierValue()); + + String correlationKey = target.getCorrelationKey(); + delegateExecution.removeVariable("statusCode"); + variables.setString(ConstantsPing.getBpmnExecutionVariableStatusCode(correlationKey), + ConstantsPing.CODESYSTEM_DSF_PING_STATUS_VALUE_PONG_MISSING); + + logger.debug("Saved '{}' to process execution for correlation key '{}'", + ConstantsPing.CODESYSTEM_DSF_PING_STATUS_VALUE_PONG_MISSING, correlationKey); + } +} diff --git a/src/main/java/dev/dsf/bpe/service/ping/LogAndSaveSendError.java b/src/main/java/dev/dsf/bpe/service/ping/LogAndSaveSendError.java new file mode 100644 index 00000000..11e7ec12 --- /dev/null +++ b/src/main/java/dev/dsf/bpe/service/ping/LogAndSaveSendError.java @@ -0,0 +1,36 @@ +package dev.dsf.bpe.service.ping; + +import org.camunda.bpm.engine.delegate.BpmnError; +import org.camunda.bpm.engine.delegate.DelegateExecution; + +import dev.dsf.bpe.ConstantsPing; +import dev.dsf.bpe.util.ErrorMessageListUtils; +import dev.dsf.bpe.util.logging.PingPongLogger; +import dev.dsf.bpe.v1.ProcessPluginApi; +import dev.dsf.bpe.v1.activity.AbstractServiceDelegate; +import dev.dsf.bpe.v1.variables.Variables; + +public class LogAndSaveSendError extends AbstractServiceDelegate +{ + public LogAndSaveSendError(ProcessPluginApi api) + { + super(api); + } + + @Override + protected void doExecute(DelegateExecution execution, Variables variables) throws BpmnError, Exception + { + PingPongLogger logger = new PingPongLogger(LogAndSaveSendError.class, variables.getStartTask()); + + String correlationKey = variables.getTarget().getCorrelationKey(); + String statusCode = (String) execution.getVariableLocal(ConstantsPing.getBpmnExecutionVariableStatusCode()); + String errorMessage = (String) execution.getVariableLocal(ConstantsPing.getBpmnExecutionVariableErrorMessage()); + + variables.setString(ConstantsPing.getBpmnExecutionVariableStatusCode(correlationKey), statusCode); + ErrorMessageListUtils.add(errorMessage, execution, correlationKey); + variables.setInteger(ConstantsPing.getBpmnExecutionVariableUploadedBytes(correlationKey), 0); + variables.setLong(ConstantsPing.getBpmnExecutionVariableDownloadedDurationMillis(correlationKey), 0L); + logger.debug("Saved error when trying to send ping message. Status: {}, error message: {}", statusCode, + errorMessage); + } +} diff --git a/src/main/java/dev/dsf/bpe/service/ping/LogAndSaveUploadErrorPing.java b/src/main/java/dev/dsf/bpe/service/ping/LogAndSaveUploadErrorPing.java new file mode 100644 index 00000000..c1600bc4 --- /dev/null +++ b/src/main/java/dev/dsf/bpe/service/ping/LogAndSaveUploadErrorPing.java @@ -0,0 +1,32 @@ +package dev.dsf.bpe.service.ping; + +import org.camunda.bpm.engine.delegate.BpmnError; +import org.camunda.bpm.engine.delegate.DelegateExecution; +import org.hl7.fhir.r4.model.Task; + +import dev.dsf.bpe.ConstantsPing; +import dev.dsf.bpe.util.ErrorMessageListUtils; +import dev.dsf.bpe.util.logging.PingPongLogger; +import dev.dsf.bpe.v1.ProcessPluginApi; +import dev.dsf.bpe.v1.activity.AbstractServiceDelegate; +import dev.dsf.bpe.v1.variables.Variables; + +public class LogAndSaveUploadErrorPing extends AbstractServiceDelegate +{ + public LogAndSaveUploadErrorPing(ProcessPluginApi api) + { + super(api); + } + + @Override + protected void doExecute(DelegateExecution execution, Variables variables) throws BpmnError, Exception + { + Task startTask = variables.getStartTask(); + PingPongLogger logger = new PingPongLogger(LogAndSaveUploadErrorPing.class, startTask); + + String errorMessage = variables.getString(ConstantsPing.BPMN_EXECUTION_VARIABLE_RESOURCE_UPLOAD_ERROR_MESSAGE); + ErrorMessageListUtils.add(errorMessage, execution); + + logger.info("Error while storing binary resource for download: {}", errorMessage); + } +} diff --git a/src/main/java/dev/dsf/bpe/service/ping/SavePong.java b/src/main/java/dev/dsf/bpe/service/ping/SavePong.java new file mode 100644 index 00000000..6528c336 --- /dev/null +++ b/src/main/java/dev/dsf/bpe/service/ping/SavePong.java @@ -0,0 +1,66 @@ +package dev.dsf.bpe.service.ping; + +import java.util.List; +import java.util.Optional; + +import org.camunda.bpm.engine.delegate.BpmnError; +import org.camunda.bpm.engine.delegate.DelegateExecution; +import org.hl7.fhir.r4.model.DecimalType; +import org.hl7.fhir.r4.model.IntegerType; +import org.hl7.fhir.r4.model.PrimitiveType; +import org.hl7.fhir.r4.model.StringType; +import org.hl7.fhir.r4.model.Task; + +import dev.dsf.bpe.ConstantsPing; +import dev.dsf.bpe.util.ErrorMessageListUtils; +import dev.dsf.bpe.util.logging.PingPongLogger; +import dev.dsf.bpe.v1.ProcessPluginApi; +import dev.dsf.bpe.v1.activity.AbstractServiceDelegate; +import dev.dsf.bpe.v1.variables.Target; +import dev.dsf.bpe.v1.variables.Variables; + +public class SavePong extends AbstractServiceDelegate +{ + public SavePong(ProcessPluginApi api) + { + super(api); + } + + @Override + protected void doExecute(DelegateExecution delegateExecution, Variables variables) throws BpmnError, Exception + { + PingPongLogger logger = new PingPongLogger(SavePong.class, variables.getStartTask()); + + Target target = variables.getTarget(); + logger.debug("Pong received from {}. Saving pong information...", target.getEndpointUrl()); + String correlationKey = target.getCorrelationKey(); + delegateExecution.removeVariable("statusCode"); + variables.setString(ConstantsPing.getBpmnExecutionVariableStatusCode(correlationKey), + ConstantsPing.CODESYSTEM_DSF_PING_STATUS_VALUE_PONG_RECEIVED); + + Task pong = variables.getLatestTask(); + + Optional optDownloadedDurationMillis = api.getTaskHelper().getFirstInputParameterValue(pong, + ConstantsPing.CODESYSTEM_DSF_PING, ConstantsPing.CODESYSTEM_DSF_PING_VALUE_DOWNLOADED_DURATION_MILLIS, + DecimalType.class); + optDownloadedDurationMillis.ifPresent(decimalType -> variables.setLong( + ConstantsPing.getBpmnExecutionVariableUploadedDurationMillis(correlationKey), + decimalType.getValue().longValue())); + + Optional optDownloadedBytes = api.getTaskHelper().getFirstInputParameterValue(pong, + ConstantsPing.CODESYSTEM_DSF_PING, ConstantsPing.CODESYSTEM_DSF_PING_VALUE_DOWNLOADED_BYTES, + IntegerType.class); + optDownloadedBytes.ifPresent(integerType -> variables.setInteger( + ConstantsPing.getBpmnExecutionVariableUploadedBytes(correlationKey), integerType.getValue())); + + + List errorList = api.getTaskHelper() + .getInputParameterValues(pong, ConstantsPing.CODESYSTEM_DSF_PING, + ConstantsPing.CODESYSTEM_DSF_PING_STATUS_VALUE_ERROR_MESSAGE, StringType.class) + .map(PrimitiveType::getValue).map(string -> "Pong error: " + string).toList(); + + ErrorMessageListUtils.addAll(errorList, delegateExecution, correlationKey); + + logger.debug("Saved pong information."); + } +} diff --git a/src/main/java/dev/dsf/bpe/service/SelectPingTargets.java b/src/main/java/dev/dsf/bpe/service/ping/SelectPingTargets.java similarity index 89% rename from src/main/java/dev/dsf/bpe/service/SelectPingTargets.java rename to src/main/java/dev/dsf/bpe/service/ping/SelectPingTargets.java index ff52d009..28563877 100644 --- a/src/main/java/dev/dsf/bpe/service/SelectPingTargets.java +++ b/src/main/java/dev/dsf/bpe/service/ping/SelectPingTargets.java @@ -1,4 +1,4 @@ -package dev.dsf.bpe.service; +package dev.dsf.bpe.service.ping; import java.util.Collections; import java.util.HashMap; @@ -22,13 +22,12 @@ import org.hl7.fhir.r4.model.Organization; import org.hl7.fhir.r4.model.Resource; import org.hl7.fhir.r4.model.Task; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.beans.factory.InitializingBean; import org.springframework.web.util.UriComponents; import org.springframework.web.util.UriComponentsBuilder; import dev.dsf.bpe.ConstantsPing; +import dev.dsf.bpe.util.logging.PingPongLogger; import dev.dsf.bpe.v1.ProcessPluginApi; import dev.dsf.bpe.v1.activity.AbstractServiceDelegate; import dev.dsf.bpe.v1.constants.NamingSystems.EndpointIdentifier; @@ -38,7 +37,6 @@ public class SelectPingTargets extends AbstractServiceDelegate implements InitializingBean { - private static final Logger logger = LoggerFactory.getLogger(SelectPingTargets.class); private static final Pattern endpointResouceTypes = Pattern.compile( "Endpoint|HealthcareService|ImagingStudy|InsurancePlan|Location|Organization|OrganizationAffiliation|PractitionerRole"); @@ -51,8 +49,10 @@ public SelectPingTargets(ProcessPluginApi api) @Override protected void doExecute(DelegateExecution execution, Variables variables) throws BpmnError, Exception { - Stream targetEndpoints = getTargetEndpointsSearchParameter(variables).map(this::searchForEndpoints) - .orElse(allEndpoints()).filter(isLocalEndpoint().negate()); + Task startTask = variables.getStartTask(); + Stream targetEndpoints = getTargetEndpointsSearchParameter(variables) + .map(uriComponents -> searchForEndpoints(uriComponents, startTask)).orElse(allEndpoints()) + .filter(isLocalEndpoint().negate()); List remoteOrganizations = api.getOrganizationProvider().getRemoteOrganizations(); Map organizationIdentifierByOrganizationId = remoteOrganizations.stream().collect( @@ -83,17 +83,18 @@ private Optional getTargetEndpointsSearchParameter(Variables vari .map(requestUrl -> UriComponentsBuilder.fromUriString(requestUrl).build()); } - private Stream searchForEndpoints(UriComponents searchParameters) + private Stream searchForEndpoints(UriComponents searchParameters, Task startTask) { - return searchForEndpoints(searchParameters, 1, 0); + return searchForEndpoints(searchParameters, 1, 0, startTask); } - private Stream searchForEndpoints(UriComponents searchParameters, int page, int currentTotal) + private Stream searchForEndpoints(UriComponents searchParameters, int page, int currentTotal, + Task startTask) { if (searchParameters.getPathSegments().isEmpty()) return Stream.empty(); - Optional> resourceType = getResourceType(searchParameters); + Optional> resourceType = getResourceType(searchParameters, startTask); if (resourceType.isEmpty()) return Stream.empty(); @@ -105,14 +106,14 @@ private Stream searchForEndpoints(UriComponents searchParameters, int .searchWithStrictHandling(resourceType.get(), queryParameters); if (searchResult.getTotal() > currentTotal + searchResult.getEntry().size()) - return Stream.concat(toEndpoints(searchResult), - searchForEndpoints(searchParameters, page + 1, currentTotal + searchResult.getEntry().size())); + return Stream.concat(toEndpoints(searchResult), searchForEndpoints(searchParameters, page + 1, + currentTotal + searchResult.getEntry().size(), startTask)); else return toEndpoints(searchResult); } @SuppressWarnings("unchecked") - private Optional> getResourceType(UriComponents searchParameters) + private Optional> getResourceType(UriComponents searchParameters, Task startTask) { if (searchParameters.getPathSegments().isEmpty()) return Optional.empty(); @@ -127,6 +128,7 @@ private Optional> getResourceType(UriComponents search } catch (ClassNotFoundException e) { + PingPongLogger logger = new PingPongLogger(SelectPingTargets.class, startTask); logger.error("Unable to find class for FHIR resource type " + type, e); return Optional.empty(); } diff --git a/src/main/java/dev/dsf/bpe/service/ping/StoreResults.java b/src/main/java/dev/dsf/bpe/service/ping/StoreResults.java new file mode 100644 index 00000000..f79860a3 --- /dev/null +++ b/src/main/java/dev/dsf/bpe/service/ping/StoreResults.java @@ -0,0 +1,124 @@ +package dev.dsf.bpe.service.ping; + +import java.math.BigDecimal; +import java.util.Comparator; +import java.util.List; +import java.util.Objects; + +import org.camunda.bpm.engine.delegate.BpmnError; +import org.camunda.bpm.engine.delegate.DelegateExecution; +import org.hl7.fhir.r4.model.Task; +import org.springframework.beans.factory.InitializingBean; + +import dev.dsf.bpe.ConstantsPing; +import dev.dsf.bpe.mail.ErrorMailService; +import dev.dsf.bpe.util.ErrorMessageListUtils; +import dev.dsf.bpe.util.logging.PingPongLogger; +import dev.dsf.bpe.util.task.NetworkSpeedCalculator; +import dev.dsf.bpe.util.task.output.generator.ErrorMessageGenerator; +import dev.dsf.bpe.util.task.output.generator.PingStatusGenerator; +import dev.dsf.bpe.v1.ProcessPluginApi; +import dev.dsf.bpe.v1.activity.AbstractServiceDelegate; +import dev.dsf.bpe.v1.variables.Target; +import dev.dsf.bpe.v1.variables.Targets; +import dev.dsf.bpe.v1.variables.Variables; + +public class StoreResults extends AbstractServiceDelegate implements InitializingBean +{ + private final ErrorMailService errorMailService; + private final String networkSpeedUnit; + + public StoreResults(ProcessPluginApi api, ErrorMailService errorMailService, String networkSpeedUnit) + { + super(api); + this.networkSpeedUnit = networkSpeedUnit; + this.errorMailService = errorMailService; + } + + @Override + public void afterPropertiesSet() throws Exception + { + super.afterPropertiesSet(); + + Objects.requireNonNull(errorMailService, "errorMailService"); + } + + @Override + protected void doExecute(DelegateExecution execution, Variables variables) throws BpmnError, Exception + { + PingPongLogger logger = new PingPongLogger(StoreResults.class, variables.getStartTask()); + + logger.debug("Storing results for process started with Task {}", + variables.getStartTask().getIdElement().getValue()); + Task task = variables.getStartTask(); + Targets targets = variables.getTargets(); + + ErrorMessageGenerator.create(ErrorMessageListUtils.getErrorMessageList(execution)).forEach(task::addOutput); + + targets.getEntries().stream().sorted(Comparator.comparing(Target::getEndpointIdentifierValue)).forEach(target -> + { + String correlationKey = target.getCorrelationKey(); + + String statusCode = variables.getString(ConstantsPing.getBpmnExecutionVariableStatusCode(correlationKey)); + if (ConstantsPing.CODESYSTEM_DSF_PING_STATUS_VALUE_NOT_REACHABLE.equals(statusCode) + || ConstantsPing.CODESYSTEM_DSF_PING_STATUS_VALUE_NOT_ALLOWED.equals(statusCode)) + { + List errorMessages = ErrorMessageListUtils.getErrorMessageList(execution, correlationKey); + String errorMessage = errorMessages.get(0); + task.addOutput(PingStatusGenerator.createPingStatusOutput(target, statusCode, errorMessages)); + + if (ConstantsPing.CODESYSTEM_DSF_PING_STATUS_VALUE_NOT_REACHABLE.equals(statusCode)) + errorMailService.endpointNotReachableForPing(task.getIdElement(), target, errorMessage); + else if (ConstantsPing.CODESYSTEM_DSF_PING_STATUS_VALUE_NOT_ALLOWED.equals(statusCode)) + errorMailService.endpointReachablePingForbidden(task.getIdElement(), target, errorMessage); + } + else if (ConstantsPing.CODESYSTEM_DSF_PING_STATUS_VALUE_PONG_MISSING.equals(statusCode)) + { + List errorMessages = ErrorMessageListUtils.getErrorMessageList(execution, correlationKey); + task.addOutput(PingStatusGenerator.createPingStatusOutput(target, statusCode, errorMessages)); + + errorMailService.pongMessageNotReceived(task.getIdElement(), target); + } + else + { + int downloadResourceSizeBytes = variables + .getInteger(ConstantsPing.BPMN_EXECUTION_VARIABLE_DOWNLOAD_RESOURCE_SIZE_BYTES); + List errorMessageList = ErrorMessageListUtils.getErrorMessageList(execution, correlationKey); + if (downloadResourceSizeBytes >= 0) // if fat-ping + { + Integer downloadedBytes = variables + .getInteger(ConstantsPing.getBpmnExecutionVariableDownloadedBytes(correlationKey)); + Long downloadedDurationMillis = variables + .getLong(ConstantsPing.getBpmnExecutionVariableDownloadedDurationMillis(correlationKey)); + + BigDecimal downloadSpeed = downloadedBytes != null && downloadedDurationMillis != null + ? NetworkSpeedCalculator.calculate(downloadedBytes, downloadedDurationMillis, + networkSpeedUnit) + : null; + + Integer uploadedBytes = variables + .getInteger(ConstantsPing.getBpmnExecutionVariableUploadedBytes(correlationKey)); + Long uploadedDurationMillis = variables + .getLong(ConstantsPing.getBpmnExecutionVariableUploadedDurationMillis(correlationKey)); + + BigDecimal uploadSpeed = uploadedBytes != null && uploadedDurationMillis != null + ? NetworkSpeedCalculator.calculate(uploadedBytes, uploadedDurationMillis, networkSpeedUnit) + : null; + + task.addOutput(PingStatusGenerator.createPingStatusOutput(target, statusCode, errorMessageList, + downloadSpeed, uploadSpeed, networkSpeedUnit)); + } + else // if slim-ping + { + task.addOutput(PingStatusGenerator.createPingStatusOutput(target, statusCode)); + } + } + }); + + // TODO only send one combined status mail + + variables.updateTask(task); + + logger.debug("Successfully stored results for task {}", variables.getStartTask().getIdElement().getValue()); + } +} diff --git a/src/main/java/dev/dsf/bpe/service/pong/DownloadResourceAndMeasureSpeed.java b/src/main/java/dev/dsf/bpe/service/pong/DownloadResourceAndMeasureSpeed.java new file mode 100644 index 00000000..6692790d --- /dev/null +++ b/src/main/java/dev/dsf/bpe/service/pong/DownloadResourceAndMeasureSpeed.java @@ -0,0 +1,57 @@ +package dev.dsf.bpe.service.pong; + +import org.camunda.bpm.engine.delegate.BpmnError; +import org.camunda.bpm.engine.delegate.DelegateExecution; +import org.hl7.fhir.r4.model.Task; + +import dev.dsf.bpe.ConstantsPing; +import dev.dsf.bpe.util.BinaryResourceDownloader; +import dev.dsf.bpe.util.logging.PingPongLogger; +import dev.dsf.bpe.v1.ProcessPluginApi; +import dev.dsf.bpe.v1.activity.AbstractServiceDelegate; +import dev.dsf.bpe.v1.variables.Variables; + +public class DownloadResourceAndMeasureSpeed extends AbstractServiceDelegate +{ + private final int maxDownloadSizeBytes; + + public DownloadResourceAndMeasureSpeed(ProcessPluginApi api, int maxDownloadSizeBytes) + { + super(api); + this.maxDownloadSizeBytes = maxDownloadSizeBytes; + } + + @Override + protected void doExecute(DelegateExecution delegateExecution, Variables variables) throws BpmnError, Exception + { + PingPongLogger logger = new PingPongLogger(DownloadResourceAndMeasureSpeed.class, variables.getStartTask()); + logger.debug("Starting resource download to measure speed..."); + + Task task = variables.getStartTask(); + + try + { + BinaryResourceDownloader.DownloadResult downloadResult = new BinaryResourceDownloader(logger) + .download(variables, api, task, maxDownloadSizeBytes); + + if (downloadResult.getErrorMessage() == null) + { + variables.setInteger(ConstantsPing.getBpmnExecutionVariableDownloadedBytes(), + downloadResult.getDownloadedBytes()); + variables.setLong(ConstantsPing.getBpmnExecutionVariableDownloadedDurationMillis(), + downloadResult.getDownloadedDurationMillis()); + } + else + { + throw new BpmnError(ConstantsPing.BPMN_ERROR_CODE_RESOURCE_DOWNLOAD_ERROR, + downloadResult.getErrorMessage()); + } + + logger.debug("Completed resource download and measured speed."); + } + catch (Exception e) + { + throw new BpmnError(ConstantsPing.BPMN_ERROR_CODE_RESOURCE_DOWNLOAD_ERROR, e.getMessage()); + } + } +} diff --git a/src/main/java/dev/dsf/bpe/service/pong/EstimateCleanupTimerDuration.java b/src/main/java/dev/dsf/bpe/service/pong/EstimateCleanupTimerDuration.java new file mode 100644 index 00000000..49fffc6f --- /dev/null +++ b/src/main/java/dev/dsf/bpe/service/pong/EstimateCleanupTimerDuration.java @@ -0,0 +1,41 @@ +package dev.dsf.bpe.service.pong; + +import java.time.Duration; +import java.util.Optional; + +import org.camunda.bpm.engine.delegate.BpmnError; +import org.camunda.bpm.engine.delegate.DelegateExecution; + +import dev.dsf.bpe.ConstantsPing; +import dev.dsf.bpe.util.logging.PingPongLogger; +import dev.dsf.bpe.v1.ProcessPluginApi; +import dev.dsf.bpe.v1.activity.AbstractServiceDelegate; +import dev.dsf.bpe.v1.variables.Variables; + +public class EstimateCleanupTimerDuration extends AbstractServiceDelegate +{ + public EstimateCleanupTimerDuration(ProcessPluginApi api) + { + super(api); + } + + @Override + protected void doExecute(DelegateExecution delegateExecution, Variables variables) throws BpmnError, Exception + { + PingPongLogger logger = new PingPongLogger(EstimateCleanupTimerDuration.class, variables.getStartTask()); + + logger.debug("Estimating cleanup timer duration..."); + final long minTimerDurationMillis = 20000; + long downloadedDurationMillis = Optional + .ofNullable(variables.getLong(ConstantsPing.getBpmnExecutionVariableDownloadedDurationMillis())) + .orElse(0L); + long timerDurationMillis = downloadedDurationMillis > Long.MAX_VALUE / 10 - minTimerDurationMillis + ? Long.MAX_VALUE + : downloadedDurationMillis * 10 + minTimerDurationMillis; + + String cleanUpTimerDuration = Duration.ofMillis(timerDurationMillis).toString(); + variables.setString("cleanupTimerDuration", cleanUpTimerDuration); + + logger.debug("Estimated cleanup timer duration as {}", cleanUpTimerDuration); + } +} diff --git a/src/main/java/dev/dsf/bpe/service/pong/LogAndSaveAndStoreError.java b/src/main/java/dev/dsf/bpe/service/pong/LogAndSaveAndStoreError.java new file mode 100644 index 00000000..d4734d3c --- /dev/null +++ b/src/main/java/dev/dsf/bpe/service/pong/LogAndSaveAndStoreError.java @@ -0,0 +1,39 @@ +package dev.dsf.bpe.service.pong; + +import org.camunda.bpm.engine.delegate.BpmnError; +import org.camunda.bpm.engine.delegate.DelegateExecution; +import org.hl7.fhir.r4.model.Task; + +import dev.dsf.bpe.ConstantsPing; +import dev.dsf.bpe.util.ErrorMessageListUtils; +import dev.dsf.bpe.util.logging.PingPongLogger; +import dev.dsf.bpe.util.task.output.generator.PingStatusGenerator; +import dev.dsf.bpe.v1.ProcessPluginApi; +import dev.dsf.bpe.v1.activity.AbstractServiceDelegate; +import dev.dsf.bpe.v1.variables.Target; +import dev.dsf.bpe.v1.variables.Variables; + +public class LogAndSaveAndStoreError extends AbstractServiceDelegate +{ + public LogAndSaveAndStoreError(ProcessPluginApi api) + { + super(api); + } + + @Override + protected void doExecute(DelegateExecution delegateExecution, Variables variables) throws BpmnError, Exception + { + PingPongLogger logger = new PingPongLogger(LogAndSaveAndStoreError.class, variables.getStartTask()); + Target target = variables.getTarget(); + Task startTask = variables.getStartTask(); + + String errorMessage = variables + .getString(ConstantsPing.BPMN_EXECUTION_VARIABLE_RESOURCE_DOWNLOAD_ERROR_MESSAGE); + ErrorMessageListUtils.add(errorMessage, delegateExecution); + PingStatusGenerator.updatePongStatusOutput(startTask, + ErrorMessageListUtils.getErrorMessageList(delegateExecution)); + variables.updateTask(startTask); + + logger.info("Error while trying to download resource from {}: {}", target.getEndpointUrl(), errorMessage); + } +} diff --git a/src/main/java/dev/dsf/bpe/service/pong/LogAndSaveUploadErrorPong.java b/src/main/java/dev/dsf/bpe/service/pong/LogAndSaveUploadErrorPong.java new file mode 100644 index 00000000..6ee624ba --- /dev/null +++ b/src/main/java/dev/dsf/bpe/service/pong/LogAndSaveUploadErrorPong.java @@ -0,0 +1,32 @@ +package dev.dsf.bpe.service.pong; + +import org.camunda.bpm.engine.delegate.BpmnError; +import org.camunda.bpm.engine.delegate.DelegateExecution; +import org.hl7.fhir.r4.model.Task; + +import dev.dsf.bpe.ConstantsPing; +import dev.dsf.bpe.util.ErrorMessageListUtils; +import dev.dsf.bpe.util.logging.PingPongLogger; +import dev.dsf.bpe.v1.ProcessPluginApi; +import dev.dsf.bpe.v1.activity.AbstractServiceDelegate; +import dev.dsf.bpe.v1.variables.Variables; + +public class LogAndSaveUploadErrorPong extends AbstractServiceDelegate +{ + public LogAndSaveUploadErrorPong(ProcessPluginApi api) + { + super(api); + } + + @Override + protected void doExecute(DelegateExecution execution, Variables variables) throws BpmnError, Exception + { + Task startTask = variables.getStartTask(); + PingPongLogger logger = new PingPongLogger(LogAndSaveUploadErrorPong.class, startTask); + + String errorMessage = variables.getString(ConstantsPing.BPMN_EXECUTION_VARIABLE_RESOURCE_UPLOAD_ERROR_MESSAGE); + ErrorMessageListUtils.add(errorMessage, execution); + + logger.info("Error while storing binary resource for download: {}", errorMessage); + } +} diff --git a/src/main/java/dev/dsf/bpe/service/LogPing.java b/src/main/java/dev/dsf/bpe/service/pong/LogPing.java similarity index 86% rename from src/main/java/dev/dsf/bpe/service/LogPing.java rename to src/main/java/dev/dsf/bpe/service/pong/LogPing.java index cbfc2867..54ce1e91 100644 --- a/src/main/java/dev/dsf/bpe/service/LogPing.java +++ b/src/main/java/dev/dsf/bpe/service/pong/LogPing.java @@ -1,22 +1,19 @@ -package dev.dsf.bpe.service; +package dev.dsf.bpe.service.pong; import org.camunda.bpm.engine.delegate.BpmnError; import org.camunda.bpm.engine.delegate.DelegateExecution; import org.hl7.fhir.r4.model.Identifier; import org.hl7.fhir.r4.model.Reference; import org.hl7.fhir.r4.model.Task; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import dev.dsf.bpe.ConstantsPing; +import dev.dsf.bpe.util.logging.PingPongLogger; import dev.dsf.bpe.v1.ProcessPluginApi; import dev.dsf.bpe.v1.activity.AbstractServiceDelegate; import dev.dsf.bpe.v1.variables.Variables; public class LogPing extends AbstractServiceDelegate { - private static final Logger logger = LoggerFactory.getLogger(LogPing.class); - public LogPing(ProcessPluginApi api) { super(api); @@ -25,6 +22,8 @@ public LogPing(ProcessPluginApi api) @Override protected void doExecute(DelegateExecution execution, Variables variables) throws BpmnError, Exception { + PingPongLogger logger = new PingPongLogger(LogPing.class, variables.getStartTask()); + Task task = variables.getLatestTask(); logger.info("PING from {} (endpoint: {})", task.getRequester().getIdentifier().getValue(), diff --git a/src/main/java/dev/dsf/bpe/service/pong/SaveTimeoutError.java b/src/main/java/dev/dsf/bpe/service/pong/SaveTimeoutError.java new file mode 100644 index 00000000..eba52fa3 --- /dev/null +++ b/src/main/java/dev/dsf/bpe/service/pong/SaveTimeoutError.java @@ -0,0 +1,34 @@ +package dev.dsf.bpe.service.pong; + +import org.camunda.bpm.engine.delegate.BpmnError; +import org.camunda.bpm.engine.delegate.DelegateExecution; +import org.hl7.fhir.r4.model.Task; + +import dev.dsf.bpe.ConstantsPing; +import dev.dsf.bpe.util.ErrorMessageListUtils; +import dev.dsf.bpe.util.logging.PingPongLogger; +import dev.dsf.bpe.v1.ProcessPluginApi; +import dev.dsf.bpe.v1.activity.AbstractServiceDelegate; +import dev.dsf.bpe.v1.variables.Variables; + +public class SaveTimeoutError extends AbstractServiceDelegate +{ + public SaveTimeoutError(ProcessPluginApi api) + { + super(api); + } + + @Override + protected void doExecute(DelegateExecution execution, Variables variables) throws BpmnError, Exception + { + Task startTask = variables.getStartTask(); + PingPongLogger logger = new PingPongLogger(SaveTimeoutError.class, startTask); + logger.debug("Storing timeout error..."); + + String errorMessage = ConstantsPing.PONG_ERROR_MESSAGE_CLEANUP_TIMEOUT; + + ErrorMessageListUtils.add(errorMessage, execution); + + logger.debug("Stored timeout error: {}", errorMessage); + } +} diff --git a/src/main/java/dev/dsf/bpe/service/SelectPongTarget.java b/src/main/java/dev/dsf/bpe/service/pong/SelectPongTarget.java similarity index 70% rename from src/main/java/dev/dsf/bpe/service/SelectPongTarget.java rename to src/main/java/dev/dsf/bpe/service/pong/SelectPongTarget.java index 7e3f0d3d..efd40d45 100644 --- a/src/main/java/dev/dsf/bpe/service/SelectPongTarget.java +++ b/src/main/java/dev/dsf/bpe/service/pong/SelectPongTarget.java @@ -1,15 +1,13 @@ -package dev.dsf.bpe.service; +package dev.dsf.bpe.service.pong; import org.camunda.bpm.engine.delegate.BpmnError; import org.camunda.bpm.engine.delegate.DelegateExecution; -import org.hl7.fhir.r4.model.Identifier; -import org.hl7.fhir.r4.model.Reference; import org.hl7.fhir.r4.model.Task; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.beans.factory.InitializingBean; import dev.dsf.bpe.ConstantsPing; +import dev.dsf.bpe.service.ping.SelectPingTargets; +import dev.dsf.bpe.util.logging.PingPongLogger; import dev.dsf.bpe.v1.ProcessPluginApi; import dev.dsf.bpe.v1.activity.AbstractServiceDelegate; import dev.dsf.bpe.v1.constants.CodeSystems.BpmnMessage; @@ -17,8 +15,6 @@ public class SelectPongTarget extends AbstractServiceDelegate implements InitializingBean { - private static final Logger logger = LoggerFactory.getLogger(SelectPongTarget.class); - public SelectPongTarget(ProcessPluginApi api) { super(api); @@ -27,12 +23,16 @@ public SelectPongTarget(ProcessPluginApi api) @Override protected void doExecute(DelegateExecution execution, Variables variables) throws BpmnError, Exception { + PingPongLogger logger = new PingPongLogger(SelectPingTargets.class, variables.getStartTask()); + logger.debug("Selecting pong targets..."); + Task task = variables.getStartTask(); String correlationKey = api.getTaskHelper() .getFirstInputParameterStringValue(task, BpmnMessage.URL, BpmnMessage.Codes.CORRELATION_KEY).get(); String targetOrganizationIdentifierValue = task.getRequester().getIdentifier().getValue(); - String targetEndpointIdentifierValue = getEndpointIdentifierValue(task); + String targetEndpointIdentifierValue = variables + .getString(ConstantsPing.BPMN_EXECUTION_VARIABLE_PONG_TARGET_ENDPOINT_IDENTIFIER); String targetEndpointAddress = api.getEndpointProvider().getEndpointAddress(targetEndpointIdentifierValue) .orElseThrow(() -> @@ -45,13 +45,6 @@ protected void doExecute(DelegateExecution execution, Variables variables) throw variables.setTarget(variables.createTarget(targetOrganizationIdentifierValue, targetEndpointIdentifierValue, targetEndpointAddress, correlationKey)); - } - - private String getEndpointIdentifierValue(Task task) - { - return api.getTaskHelper() - .getFirstInputParameterValue(task, ConstantsPing.CODESYSTEM_DSF_PING, - ConstantsPing.CODESYSTEM_DSF_PING_VALUE_ENDPOINT_IDENTIFIER, Reference.class) - .map(Reference::getIdentifier).map(Identifier::getValue).get(); + logger.debug("Selected pong targets."); } } diff --git a/src/main/java/dev/dsf/bpe/service/pong/SetEndpointIdentifier.java b/src/main/java/dev/dsf/bpe/service/pong/SetEndpointIdentifier.java new file mode 100644 index 00000000..e2673630 --- /dev/null +++ b/src/main/java/dev/dsf/bpe/service/pong/SetEndpointIdentifier.java @@ -0,0 +1,43 @@ +package dev.dsf.bpe.service.pong; + +import org.camunda.bpm.engine.delegate.BpmnError; +import org.camunda.bpm.engine.delegate.DelegateExecution; +import org.hl7.fhir.r4.model.Identifier; +import org.hl7.fhir.r4.model.Reference; +import org.hl7.fhir.r4.model.Task; + +import dev.dsf.bpe.ConstantsPing; +import dev.dsf.bpe.util.logging.PingPongLogger; +import dev.dsf.bpe.v1.ProcessPluginApi; +import dev.dsf.bpe.v1.activity.AbstractServiceDelegate; +import dev.dsf.bpe.v1.variables.Variables; + +public class SetEndpointIdentifier extends AbstractServiceDelegate +{ + public SetEndpointIdentifier(ProcessPluginApi api) + { + super(api); + } + + @Override + protected void doExecute(DelegateExecution execution, Variables variables) throws BpmnError, Exception + { + PingPongLogger logger = new PingPongLogger(SetEndpointIdentifier.class, variables.getStartTask()); + logger.debug("Setting endpoint identifier..."); + + Task task = variables.getStartTask(); + String endpointIdentifierValue = getEndpointIdentifierValue(task); + variables.setString(ConstantsPing.BPMN_EXECUTION_VARIABLE_PONG_TARGET_ENDPOINT_IDENTIFIER, + endpointIdentifierValue); + + logger.debug("Set endpoint identifier to " + endpointIdentifierValue); + } + + private String getEndpointIdentifierValue(Task task) + { + return api.getTaskHelper() + .getFirstInputParameterValue(task, ConstantsPing.CODESYSTEM_DSF_PING, + ConstantsPing.CODESYSTEM_DSF_PING_VALUE_ENDPOINT_IDENTIFIER, Reference.class) + .map(Reference::getIdentifier).map(Identifier::getValue).get(); + } +} diff --git a/src/main/java/dev/dsf/bpe/service/pong/StoreDownloadSpeed.java b/src/main/java/dev/dsf/bpe/service/pong/StoreDownloadSpeed.java new file mode 100644 index 00000000..afc3d91d --- /dev/null +++ b/src/main/java/dev/dsf/bpe/service/pong/StoreDownloadSpeed.java @@ -0,0 +1,48 @@ +package dev.dsf.bpe.service.pong; + +import java.math.BigDecimal; + +import org.camunda.bpm.engine.delegate.BpmnError; +import org.camunda.bpm.engine.delegate.DelegateExecution; +import org.hl7.fhir.r4.model.Task; + +import dev.dsf.bpe.ConstantsPing; +import dev.dsf.bpe.util.logging.PingPongLogger; +import dev.dsf.bpe.util.task.NetworkSpeedCalculator; +import dev.dsf.bpe.util.task.output.generator.PingStatusGenerator; +import dev.dsf.bpe.v1.ProcessPluginApi; +import dev.dsf.bpe.v1.activity.AbstractServiceDelegate; +import dev.dsf.bpe.v1.variables.Variables; + +public class StoreDownloadSpeed extends AbstractServiceDelegate +{ + private final String networkSpeedUnit; + + public StoreDownloadSpeed(ProcessPluginApi api, String networkSpeedUnit) + { + super(api); + this.networkSpeedUnit = networkSpeedUnit; + } + + @Override + protected void doExecute(DelegateExecution execution, Variables variables) throws BpmnError, Exception + { + Task startTask = variables.getStartTask(); + PingPongLogger logger = new PingPongLogger(StoreDownloadSpeed.class, startTask); + logger.debug("Storing download speed..."); + + int downloadedBytes = variables.getInteger(ConstantsPing.getBpmnExecutionVariableDownloadedBytes()); + long downloadedDurationMillis = variables + .getLong(ConstantsPing.getBpmnExecutionVariableDownloadedDurationMillis()); + + BigDecimal downloadSpeed = NetworkSpeedCalculator.calculate(downloadedBytes, downloadedDurationMillis, + networkSpeedUnit); + + PingStatusGenerator.updatePongStatusOutput(startTask, + ConstantsPing.CODESYSTEM_DSF_PING_STATUS_VALUE_RESOURCE_DOWNLOADED); + PingStatusGenerator.updatePongStatusOutputDownloadSpeed(startTask, downloadSpeed, networkSpeedUnit); + + variables.updateTask(startTask); + logger.debug("Stored download speed: " + downloadSpeed + " " + networkSpeedUnit); + } +} diff --git a/src/main/java/dev/dsf/bpe/service/pong/StoreErrors.java b/src/main/java/dev/dsf/bpe/service/pong/StoreErrors.java new file mode 100644 index 00000000..59e8a1a1 --- /dev/null +++ b/src/main/java/dev/dsf/bpe/service/pong/StoreErrors.java @@ -0,0 +1,33 @@ +package dev.dsf.bpe.service.pong; + +import org.camunda.bpm.engine.delegate.BpmnError; +import org.camunda.bpm.engine.delegate.DelegateExecution; +import org.hl7.fhir.r4.model.Task; + +import dev.dsf.bpe.util.ErrorMessageListUtils; +import dev.dsf.bpe.util.logging.PingPongLogger; +import dev.dsf.bpe.util.task.output.generator.PingStatusGenerator; +import dev.dsf.bpe.v1.ProcessPluginApi; +import dev.dsf.bpe.v1.activity.AbstractServiceDelegate; +import dev.dsf.bpe.v1.variables.Variables; + +public class StoreErrors extends AbstractServiceDelegate +{ + public StoreErrors(ProcessPluginApi api) + { + super(api); + } + + @Override + protected void doExecute(DelegateExecution execution, Variables variables) throws BpmnError, Exception + { + Task startTask = variables.getStartTask(); + PingPongLogger logger = new PingPongLogger(StoreErrors.class, startTask); + logger.debug("Storing errors..."); + + PingStatusGenerator.updatePongStatusOutput(startTask, ErrorMessageListUtils.getErrorMessageList(execution)); + + variables.updateTask(startTask); + logger.debug("Stored errors in task: " + startTask.getIdElement().getValue()); + } +} diff --git a/src/main/java/dev/dsf/bpe/service/pong/StoreUploadSpeed.java b/src/main/java/dev/dsf/bpe/service/pong/StoreUploadSpeed.java new file mode 100644 index 00000000..e38b4d8f --- /dev/null +++ b/src/main/java/dev/dsf/bpe/service/pong/StoreUploadSpeed.java @@ -0,0 +1,65 @@ +package dev.dsf.bpe.service.pong; + +import java.math.BigDecimal; +import java.util.Optional; + +import org.camunda.bpm.engine.delegate.BpmnError; +import org.camunda.bpm.engine.delegate.DelegateExecution; +import org.hl7.fhir.r4.model.DecimalType; +import org.hl7.fhir.r4.model.IntegerType; +import org.hl7.fhir.r4.model.PrimitiveType; +import org.hl7.fhir.r4.model.Task; + +import dev.dsf.bpe.ConstantsPing; +import dev.dsf.bpe.util.logging.PingPongLogger; +import dev.dsf.bpe.util.task.NetworkSpeedCalculator; +import dev.dsf.bpe.util.task.output.generator.PingStatusGenerator; +import dev.dsf.bpe.v1.ProcessPluginApi; +import dev.dsf.bpe.v1.activity.AbstractServiceDelegate; +import dev.dsf.bpe.v1.variables.Variables; + +public class StoreUploadSpeed extends AbstractServiceDelegate +{ + private final String networkSpeedUnit; + + public StoreUploadSpeed(ProcessPluginApi api, String networkSpeedUnit) + { + super(api); + this.networkSpeedUnit = networkSpeedUnit; + } + + @Override + protected void doExecute(DelegateExecution delegateExecution, Variables variables) throws BpmnError, Exception + { + Task startTask = variables.getStartTask(); + Task cleanup = variables.getLatestTask(); + PingPongLogger logger = new PingPongLogger(LogPing.class, startTask); + logger.debug("Storing upload speed..."); + + Optional uploadedBytesTaskInput = getUploadedBytes(cleanup); + Optional uploadedDurationMillisTaskInput = getUploadedDurationMillis(cleanup); + int uploadedBytes = uploadedBytesTaskInput.map(PrimitiveType::getValue).orElse(0); + long uploadedDurationMillis = uploadedDurationMillisTaskInput + .map(decimalType -> decimalType.getValue().longValue()).orElse(0L); + + BigDecimal uploadSpeed = NetworkSpeedCalculator.calculate(uploadedBytes, uploadedDurationMillis, + networkSpeedUnit); + + PingStatusGenerator.updatePongStatusOutputUploadSpeed(startTask, uploadSpeed, networkSpeedUnit); + + variables.updateTask(startTask); + logger.debug("Stored upload speed: " + uploadSpeed + " " + networkSpeedUnit); + } + + private Optional getUploadedBytes(Task task) + { + return api.getTaskHelper().getFirstInputParameterValue(task, ConstantsPing.CODESYSTEM_DSF_PING, + ConstantsPing.CODESYSTEM_DSF_PING_VALUE_DOWNLOADED_BYTES, IntegerType.class); + } + + private Optional getUploadedDurationMillis(Task task) + { + return api.getTaskHelper().getFirstInputParameterValue(task, ConstantsPing.CODESYSTEM_DSF_PING, + ConstantsPing.CODESYSTEM_DSF_PING_VALUE_DOWNLOADED_DURATION_MILLIS, DecimalType.class); + } +} diff --git a/src/main/java/dev/dsf/bpe/spring/config/PingConfig.java b/src/main/java/dev/dsf/bpe/spring/config/PingConfig.java index 85c02919..164d6145 100644 --- a/src/main/java/dev/dsf/bpe/spring/config/PingConfig.java +++ b/src/main/java/dev/dsf/bpe/spring/config/PingConfig.java @@ -7,20 +7,35 @@ import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Scope; +import dev.dsf.bpe.listener.PingPongDeploymentStateListener; import dev.dsf.bpe.listener.SetCorrelationKeyListener; import dev.dsf.bpe.mail.ErrorMailService; +import dev.dsf.bpe.message.CleanupPong; import dev.dsf.bpe.message.SendPing; import dev.dsf.bpe.message.SendPong; import dev.dsf.bpe.message.SendStartPing; -import dev.dsf.bpe.service.LogNoResponse; -import dev.dsf.bpe.service.LogPing; -import dev.dsf.bpe.service.LogPong; -import dev.dsf.bpe.service.LogSendError; -import dev.dsf.bpe.service.SaveResults; -import dev.dsf.bpe.service.SelectPingTargets; -import dev.dsf.bpe.service.SelectPongTarget; -import dev.dsf.bpe.service.SetTargetAndConfigureTimer; -import dev.dsf.bpe.util.PingStatusGenerator; +import dev.dsf.bpe.service.Cleanup; +import dev.dsf.bpe.service.GenerateAndStoreResource; +import dev.dsf.bpe.service.SetDownloadResourceSize; +import dev.dsf.bpe.service.autostart.SetTargetAndConfigureTimer; +import dev.dsf.bpe.service.ping.DownloadResourceAndMeasureSpeedInSubProcess; +import dev.dsf.bpe.service.ping.LogAndSaveError; +import dev.dsf.bpe.service.ping.LogAndSaveNoResponse; +import dev.dsf.bpe.service.ping.LogAndSaveUploadErrorPing; +import dev.dsf.bpe.service.ping.SavePong; +import dev.dsf.bpe.service.ping.SelectPingTargets; +import dev.dsf.bpe.service.ping.StoreResults; +import dev.dsf.bpe.service.pong.DownloadResourceAndMeasureSpeed; +import dev.dsf.bpe.service.pong.EstimateCleanupTimerDuration; +import dev.dsf.bpe.service.pong.LogAndSaveAndStoreError; +import dev.dsf.bpe.service.pong.LogAndSaveUploadErrorPong; +import dev.dsf.bpe.service.pong.LogPing; +import dev.dsf.bpe.service.pong.SaveTimeoutError; +import dev.dsf.bpe.service.pong.SelectPongTarget; +import dev.dsf.bpe.service.pong.SetEndpointIdentifier; +import dev.dsf.bpe.service.pong.StoreDownloadSpeed; +import dev.dsf.bpe.service.pong.StoreErrors; +import dev.dsf.bpe.service.pong.StoreUploadSpeed; import dev.dsf.bpe.v1.ProcessPluginApi; import dev.dsf.bpe.v1.documentation.ProcessDocumentation; @@ -30,14 +45,58 @@ public class PingConfig @Autowired private ProcessPluginApi api; - @ProcessDocumentation(description = "To enable a mail being send if the ping process fails, set to 'true'. This requires the SMPT mail service client to be configured in the DSF", processNames = "dsfdev_ping") + @ProcessDocumentation(description = "To enable a mail being sent if the ping process fails, set to 'true'. This requires the SMPT mail service client to be configured in the DSF", processNames = "dsfdev_ping") @Value("${dev.dsf.dsf.bpe.ping.mail.onPingProcessFailed:false}") private boolean sendPingProcessFailedMail; - @ProcessDocumentation(description = "To enable a mail being send if the pong process fails, set to 'true'. This requires the SMPT mail service client to be configured in the DSF", processNames = "dsfdev_pong") + @ProcessDocumentation(description = "To enable a mail being sent if the pong process fails, set to 'true'. This requires the SMPT mail service client to be configured in the DSF", processNames = "dsfdev_pong") @Value("${dev.dsf.dsf.bpe.ping.mail.onPongProcessFailed:false}") private boolean sendPongProcessFailedMail; + @ProcessDocumentation(description = "Sets the download limit on resource downloads, essentially limiting the amount of data downloaded from other ping instances. Setting this to a negative value will disable resource downloads, effectively resulting in running the slim (\"old\") ping process.", processNames = "dsfdev_ping, dsfdev_pong") + @Value("${dev.dsf.bpe.ping.maxDownloadSizeBytes:10000000}") + private int maxDownloadSizeBytes; + + @ProcessDocumentation(description = "Sets the upload limit on resource uploads, essentially limiting the amount of data other ping instances are able to download from this instance.", processNames = { + "dsfdev_ping", "dsfdev_pong" }) + @Value("${dev.dsf.bpe.ping.maxUploadSizeBytes:10000000}") + private int maxUploadSizeBytes; + + @ProcessDocumentation(description = "Unit to display upload and download speeds in. Eligible values be: \"bits-per-second\", \"bytes-per-second\", \"megabits-per-second\", \"megabytes-per-second\". Default is \"megabytes-per-second\".", processNames = { + "dsfdev_ping", "dsfdev_pong" }) + @Value("${dev.dsf.bpe.ping.networkSpeedUnit:megabytes-per-second}") + private String networkSpeedUnit; + + public String getNetworkSpeedUnit() + { + return networkSpeedUnit; + } + + public void setNetworkSpeedUnit(String networkSpeedUnit) + { + this.networkSpeedUnit = networkSpeedUnit; + } + + public int getMaxDownloadSizeBytes() + { + return maxDownloadSizeBytes; + } + + public void setMaxDownloadSizeBytes(int maxDownloadSizeBytes) + { + this.maxDownloadSizeBytes = maxDownloadSizeBytes; + } + + public int getMaxUploadSizeBytes() + { + return maxUploadSizeBytes; + } + + public void setMaxUploadSizeBytes(int maxUploadSizeBytes) + { + this.maxUploadSizeBytes = maxUploadSizeBytes; + } + @Bean @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) public SetTargetAndConfigureTimer setTargetAndConfigureTimer() @@ -52,12 +111,6 @@ public SendStartPing sendStartPing() return new SendStartPing(api); } - @Bean - public PingStatusGenerator responseGenerator() - { - return new PingStatusGenerator(); - } - @Bean public ErrorMailService errorLogger() { @@ -88,50 +141,169 @@ public SetCorrelationKeyListener setCorrelationKeyListener() @Bean @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) - public LogPong logPong() + public StoreResults savePingResults() + { + return new StoreResults(api, errorLogger(), networkSpeedUnit); + } + + @Bean + @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) + public LogPing logPing() { - return new LogPong(api); + return new LogPing(api); } @Bean @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) - public LogNoResponse logNoResponse() + public SelectPongTarget selectPongTarget() { - return new LogNoResponse(api); + return new SelectPongTarget(api); } @Bean @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) - public LogSendError logSendError() + public SendPong sendPong() { - return new LogSendError(api); + return new SendPong(api, errorLogger()); } @Bean @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) - public SaveResults savePingResults() + public LogAndSaveNoResponse logAndSaveNoResponse() { - return new SaveResults(api, responseGenerator(), errorLogger()); + return new LogAndSaveNoResponse(api); } @Bean @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) - public LogPing logPing() + public CleanupPong cleanupPong() { - return new LogPing(api); + return new CleanupPong(api); } @Bean @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) - public SelectPongTarget selectPongTarget() + public DownloadResourceAndMeasureSpeed downloadResourceAndMeasureSpeed() { - return new SelectPongTarget(api); + return new DownloadResourceAndMeasureSpeed(api, (int) maxDownloadSizeBytes); } @Bean @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) - public SendPong sendPong() + public DownloadResourceAndMeasureSpeedInSubProcess downloadResourceAndMeasureSpeedInSubProcess() + { + return new DownloadResourceAndMeasureSpeedInSubProcess(api, (int) maxDownloadSizeBytes); + } + + @Bean + @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) + public Cleanup cleanup() + { + return new Cleanup(api); + } + + @Bean + @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) + public LogAndSaveAndStoreError logAndSaveAndStoreError() + { + return new LogAndSaveAndStoreError(api); + } + + @Bean + @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) + public LogAndSaveError logAndSaveError() + { + return new LogAndSaveError(api); + } + + @Bean + @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) + public StoreUploadSpeed storeDownloadSpeeds() + { + return new StoreUploadSpeed(api, networkSpeedUnit); + } + + @Bean + @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) + public EstimateCleanupTimerDuration estimateCleanupTimerDuration() + { + return new EstimateCleanupTimerDuration(api); + } + + @Bean + @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) + public SetDownloadResourceSize setDownloadResourceSize() + { + return new SetDownloadResourceSize(api, maxDownloadSizeBytes); + } + + @Bean + @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) + public SavePong savePong() + { + return new SavePong(api); + } + + @Bean + @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) + public SetEndpointIdentifier setEndpointIdentifier() + { + return new SetEndpointIdentifier(api); + } + + @Bean + @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) + public PingPongDeploymentStateListener pingPongDeploymentStateListener() + { + return new PingPongDeploymentStateListener(this); + } + + @Bean + @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) + public StoreDownloadSpeed storeDownloadSpeed() + { + return new StoreDownloadSpeed(api, networkSpeedUnit); + } + + @Bean + @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) + public dev.dsf.bpe.service.ping.LogAndSaveSendError logAndSaveSendError() + { + return new dev.dsf.bpe.service.ping.LogAndSaveSendError(api); + } + + @Bean + @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) + public GenerateAndStoreResource generateAndStoreResource() + { + return new GenerateAndStoreResource(api, maxUploadSizeBytes); + } + + @Bean + @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) + public SaveTimeoutError saveTimeoutError() + { + return new SaveTimeoutError(api); + } + + @Bean + @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) + public StoreErrors storeErrors() + { + return new StoreErrors(api); + } + + @Bean + @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) + public LogAndSaveUploadErrorPing logAndSaveUploadErrorPing() + { + return new LogAndSaveUploadErrorPing(api); + } + + @Bean + @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) + public LogAndSaveUploadErrorPong logAndSaveUploadErrorPong() { - return new SendPong(api, responseGenerator(), errorLogger()); + return new LogAndSaveUploadErrorPong(api); } } diff --git a/src/main/java/dev/dsf/bpe/util/BinaryResourceDownloader.java b/src/main/java/dev/dsf/bpe/util/BinaryResourceDownloader.java new file mode 100644 index 00000000..39179fad --- /dev/null +++ b/src/main/java/dev/dsf/bpe/util/BinaryResourceDownloader.java @@ -0,0 +1,129 @@ +package dev.dsf.bpe.util; + +import java.io.IOException; +import java.io.InputStream; + +import org.hl7.fhir.r4.model.IdType; +import org.hl7.fhir.r4.model.Reference; +import org.hl7.fhir.r4.model.Task; + +import dev.dsf.bpe.ConstantsPing; +import dev.dsf.bpe.util.logging.PingPongLogger; +import dev.dsf.bpe.v1.ProcessPluginApi; +import dev.dsf.bpe.v1.variables.Variables; +import jakarta.ws.rs.WebApplicationException; + +public class BinaryResourceDownloader +{ + private final PingPongLogger logger; + + public BinaryResourceDownloader(PingPongLogger logger) + { + this.logger = logger; + } + + public DownloadResult download(Variables variables, ProcessPluginApi api, Task task, int maxDownloadSizeBytes) + throws Exception + { + DownloadResult downloadResult; + + int downloadResourceSizeBytes = variables + .getInteger(ConstantsPing.BPMN_EXECUTION_VARIABLE_DOWNLOAD_RESOURCE_SIZE_BYTES); + + Reference downloadResourceReference = api.getTaskHelper() + .getFirstInputParameterValue(task, ConstantsPing.CODESYSTEM_DSF_PING, + ConstantsPing.CODESYSTEM_DSF_PING_VALUE_DOWNLOAD_RESOURCE_REFERENCE, Reference.class) + .orElseThrow(() -> new Exception("Unable to download resource. No reference provided in message.")); + + IdType downloadResourceReferenceIdType = new IdType(downloadResourceReference.getReference()); + String downloadResourceReferenceId = downloadResourceReferenceIdType.getIdPart(); + String webserviceUrl = downloadResourceReferenceIdType.getBaseUrl(); + try + { + InputStream binaryResourceInputStream = api.getFhirWebserviceClientProvider() + .getWebserviceClient(webserviceUrl) + .readBinary(downloadResourceReferenceId, ConstantsPing.DOWNLOAD_RESOURCE_MIME_TYPE); + + try (binaryResourceInputStream) + { + logger.info( + "Downloading resource for: '{}'. Requested resource size is {} bytes, maximum downloadable size is {} bytes...", + downloadResourceReference.getReference(), downloadResourceSizeBytes, maxDownloadSizeBytes); + long downloadStartTime = System.currentTimeMillis(); + int numBytes = Math.min(downloadResourceSizeBytes, maxDownloadSizeBytes); + binaryResourceInputStream.skipNBytes(numBytes); + long downloadEndTime = System.currentTimeMillis(); + long downloadedDurationMillis = downloadEndTime - downloadStartTime; + downloadResult = new DownloadResult(numBytes, downloadedDurationMillis); + logger.info("Finished downloading {} bytes. Took {}", numBytes, + toHoursMinutesSecondsMilliseconds(downloadedDurationMillis)); + + } + catch (IOException e) + { + String error = "Encountered an error while downloading resource: " + e.getMessage(); + logger.error(error); + downloadResult = new DownloadResult(error); + } + } + catch (WebApplicationException e) + { + String error = "Encountered an error while trying to download resource: " + + e.getResponse().getStatusInfo().getStatusCode() + " " + e.getMessage(); + logger.error(error); + downloadResult = new DownloadResult(error); + } + catch (Exception e) + { + String error = "Encountered an error while trying to download resource: " + e.getMessage(); + logger.error(error); + downloadResult = new DownloadResult(error); + } + return downloadResult; + } + + private String toHoursMinutesSecondsMilliseconds(long millis) + { + long hours = (millis / 1000) / 60 / 60 % 24; + long minutes = (millis / 1000) / 60 % 60; + long seconds = (millis / 1000) % 60; + long milliSeconds = millis % 1000; + return String.format("%02d:%02d:%02d:%03d (h:m:s:ms)", hours, minutes, seconds, milliSeconds); + } + + public static class DownloadResult + { + private final int downloadedBytes; + private final long downloadedDurationMillis; + private final String errorMessage; + + public DownloadResult(int downloadedBytes, long downloadedDurationMillis) + { + this.downloadedBytes = downloadedBytes; + this.downloadedDurationMillis = downloadedDurationMillis; + errorMessage = null; + } + + public DownloadResult(String errorMessage) + { + downloadedBytes = 0; + downloadedDurationMillis = 0; + this.errorMessage = errorMessage; + } + + public int getDownloadedBytes() + { + return downloadedBytes; + } + + public long getDownloadedDurationMillis() + { + return downloadedDurationMillis; + } + + public String getErrorMessage() + { + return errorMessage; + } + } +} diff --git a/src/main/java/dev/dsf/bpe/util/ErrorMessageListUtils.java b/src/main/java/dev/dsf/bpe/util/ErrorMessageListUtils.java new file mode 100644 index 00000000..f2857330 --- /dev/null +++ b/src/main/java/dev/dsf/bpe/util/ErrorMessageListUtils.java @@ -0,0 +1,88 @@ +package dev.dsf.bpe.util; + +import java.util.List; +import java.util.Vector; + +import org.camunda.bpm.engine.delegate.DelegateExecution; + +import dev.dsf.bpe.ConstantsPing; + +public class ErrorMessageListUtils +{ + public static List addAll(List errors, DelegateExecution execution) + { + List errorList = getErrorMessageList(execution); + if (errors == null) + return errorList; + errorList.addAll(errors); + return errorList; + } + + public static List addAll(List errors, DelegateExecution execution, String correlationKey) + { + List errorList = correlationKey != null ? getErrorMessageList(execution, correlationKey) + : getErrorMessageList(execution); + if (errors == null) + return errorList; + errorList.addAll(errors); + return errorList; + } + + public static List add(String error, DelegateExecution execution) + { + return add(error, execution, null); + } + + public static List add(String error, DelegateExecution execution, String correlationKey) + { + if (correlationKey != null) + { + return add(error, ConstantsPing.getBpmnExecutionVariableErrorMessageList(correlationKey), execution); + } + else + { + return add(error, ConstantsPing.getBpmnExecutionVariableErrorMessageList(), execution); + } + } + + public static List getErrorMessageList(DelegateExecution execution) + { + return getErrorMessageList(execution, null); + } + + @SuppressWarnings("unchecked") + public static List getErrorMessageList(DelegateExecution execution, String correlationKey) + { + List errorMessages = correlationKey != null + ? (List) execution + .getVariable(ConstantsPing.getBpmnExecutionVariableErrorMessageList(correlationKey)) + : (List) execution.getVariable(ConstantsPing.getBpmnExecutionVariableErrorMessageList()); + if (errorMessages == null) + { + errorMessages = new Vector<>(); + if (correlationKey != null) + { + execution.setVariable(ConstantsPing.getBpmnExecutionVariableErrorMessageList(correlationKey), + errorMessages); + } + else + { + execution.setVariable(ConstantsPing.getBpmnExecutionVariableErrorMessageList(), errorMessages); + } + } + return errorMessages; + } + + @SuppressWarnings("unchecked") + public static List add(String error, String variableName, DelegateExecution execution) + { + List errorMessages = (List) execution.getVariable(variableName); + if (errorMessages == null) + { + errorMessages = new Vector<>(); + execution.setVariable(variableName, errorMessages); + } + errorMessages.add(error); + return errorMessages; + } +} diff --git a/src/main/java/dev/dsf/bpe/util/PingStatusGenerator.java b/src/main/java/dev/dsf/bpe/util/PingStatusGenerator.java deleted file mode 100644 index 142ded7d..00000000 --- a/src/main/java/dev/dsf/bpe/util/PingStatusGenerator.java +++ /dev/null @@ -1,57 +0,0 @@ -package dev.dsf.bpe.util; - -import org.hl7.fhir.r4.model.Coding; -import org.hl7.fhir.r4.model.Extension; -import org.hl7.fhir.r4.model.StringType; -import org.hl7.fhir.r4.model.Task.TaskOutputComponent; - -import dev.dsf.bpe.ConstantsPing; -import dev.dsf.bpe.v1.constants.NamingSystems.EndpointIdentifier; -import dev.dsf.bpe.v1.constants.NamingSystems.OrganizationIdentifier; -import dev.dsf.bpe.v1.variables.Target; - -public class PingStatusGenerator -{ - public TaskOutputComponent createPingStatusOutput(Target target, String statusCode) - { - return createPingStatusOutput(target, statusCode, null); - } - - public TaskOutputComponent createPingStatusOutput(Target target, String statusCode, String errorMessage) - { - return createStatusOutput(target, ConstantsPing.CODESYSTEM_DSF_PING_VALUE_PING_STATUS, statusCode, - errorMessage); - } - - public TaskOutputComponent createPongStatusOutput(Target target, String statusCode) - { - return createPongStatusOutput(target, statusCode, null); - } - - public TaskOutputComponent createPongStatusOutput(Target target, String statusCode, String errorMessage) - { - return createStatusOutput(target, ConstantsPing.CODESYSTEM_DSF_PING_VALUE_PONG_STATUS, statusCode, - errorMessage); - } - - private TaskOutputComponent createStatusOutput(Target target, String outputParameter, String statusCode, - String errorMessage) - { - TaskOutputComponent output = new TaskOutputComponent(); - output.setValue(new Coding().setSystem(ConstantsPing.CODESYSTEM_DSF_PING_STATUS).setCode(statusCode)); - output.getType().addCoding().setSystem(ConstantsPing.CODESYSTEM_DSF_PING).setCode(outputParameter); - - Extension extension = output.addExtension(); - extension.setUrl(ConstantsPing.EXTENSION_URL_PING_STATUS); - extension.addExtension(ConstantsPing.EXTENSION_URL_CORRELATION_KEY, new StringType(target.getCorrelationKey())); - extension.addExtension().setUrl(ConstantsPing.EXTENSION_URL_ORGANIZATION_IDENTIFIER) - .setValue(OrganizationIdentifier.withValue(target.getOrganizationIdentifierValue())); - extension.addExtension().setUrl(ConstantsPing.EXTENSION_URL_ENDPOINT_IDENTIFIER) - .setValue(EndpointIdentifier.withValue(target.getEndpointIdentifierValue())); - if (errorMessage != null) - extension.addExtension().setUrl(ConstantsPing.EXTENSION_URL_ERROR_MESSAGE) - .setValue(new StringType(errorMessage)); - - return output; - } -} diff --git a/src/main/java/dev/dsf/bpe/util/ReadAccessTagGenerator.java b/src/main/java/dev/dsf/bpe/util/ReadAccessTagGenerator.java new file mode 100644 index 00000000..cf2c51df --- /dev/null +++ b/src/main/java/dev/dsf/bpe/util/ReadAccessTagGenerator.java @@ -0,0 +1,16 @@ +package dev.dsf.bpe.util; + +import org.hl7.fhir.r4.model.Coding; + +import dev.dsf.bpe.ConstantsPing; + +public class ReadAccessTagGenerator +{ + public static Coding create(String accessLevel) + { + Coding tag = new Coding(); + tag.setSystem(ConstantsPing.CODESYSTEM_READ_ACCESS_TAG); + tag.setCode(accessLevel); + return tag; + } +} diff --git a/src/main/java/dev/dsf/bpe/util/VersionUtils.java b/src/main/java/dev/dsf/bpe/util/VersionUtils.java new file mode 100644 index 00000000..b112adef --- /dev/null +++ b/src/main/java/dev/dsf/bpe/util/VersionUtils.java @@ -0,0 +1,16 @@ +package dev.dsf.bpe.util; + +import dev.dsf.bpe.PingProcessPluginDefinition; + +public class VersionUtils +{ + public static String appendFhirResourceVersion(String toAppend) + { + return toAppend + "|" + getFhirResourceVersion(); + } + + public static String getFhirResourceVersion() + { + return new PingProcessPluginDefinition().getVersion().substring(0, 3); + } +} diff --git a/src/main/java/dev/dsf/bpe/util/logging/PingPongLogger.java b/src/main/java/dev/dsf/bpe/util/logging/PingPongLogger.java new file mode 100644 index 00000000..e7cf5cdb --- /dev/null +++ b/src/main/java/dev/dsf/bpe/util/logging/PingPongLogger.java @@ -0,0 +1,42 @@ +package dev.dsf.bpe.util.logging; + +import org.hl7.fhir.r4.model.Task; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class PingPongLogger +{ + private final Logger logger; + private Task task; + + public PingPongLogger(Class clazz, Task task) + { + this.logger = LoggerFactory.getLogger(clazz); + this.task = task; + } + + public void info(String message, Object... args) + { + logger.info(prependProcessInfo(message), args); + } + + public void warn(String message, Object... args) + { + logger.warn(prependProcessInfo(message), args); + } + + public void error(String message, Object... args) + { + logger.error(prependProcessInfo(message), args); + } + + public void debug(String message, Object... args) + { + logger.debug(prependProcessInfo(message), args); + } + + private String prependProcessInfo(String message) + { + return "Process for Task " + task.getIdElement().getValue() + ": " + message; + } +} diff --git a/src/main/java/dev/dsf/bpe/util/task/NetworkSpeedCalculator.java b/src/main/java/dev/dsf/bpe/util/task/NetworkSpeedCalculator.java new file mode 100644 index 00000000..fe8389be --- /dev/null +++ b/src/main/java/dev/dsf/bpe/util/task/NetworkSpeedCalculator.java @@ -0,0 +1,46 @@ +package dev.dsf.bpe.util.task; + +import java.math.BigDecimal; +import java.math.RoundingMode; + +import dev.dsf.bpe.ConstantsPing; + +public class NetworkSpeedCalculator +{ + public static BigDecimal calculate(int bytes, long duration, String unit) + { + if (bytes == 0) + return BigDecimal.ZERO; + if (duration == 0) + return BigDecimal.valueOf(Long.MAX_VALUE); + + BigDecimal seconds = BigDecimal.valueOf(duration).setScale(3, RoundingMode.HALF_UP) + .divide(BigDecimal.valueOf(1000).setScale(3, RoundingMode.HALF_UP), RoundingMode.HALF_UP); + + return switch (unit) + { + case ConstantsPing.CODESYSTEM_DSF_PING_UNITS_VALUE_BITS_PER_SECOND -> + { + BigDecimal bits = new BigDecimal(bytes * 8L).setScale(3, RoundingMode.HALF_UP); + yield bits.divide(seconds, 2, RoundingMode.HALF_UP); + } + case ConstantsPing.CODESYSTEM_DSF_PING_UNITS_VALUE_MEGABITS_PER_SECOND -> + { + BigDecimal megabits = new BigDecimal(bytes * 8L).divide(BigDecimal.valueOf(1000000), + RoundingMode.HALF_UP); + yield megabits.divide(seconds, 2, RoundingMode.HALF_UP); + } + case ConstantsPing.CODESYSTEM_DSF_PING_UNITS_VALUE_BYTES_PER_SECOND -> + { + BigDecimal bytesLocal = new BigDecimal(bytes).setScale(3, RoundingMode.HALF_UP); + yield bytesLocal.divide(seconds, 2, RoundingMode.HALF_UP); + } + case ConstantsPing.CODESYSTEM_DSF_PING_UNITS_VALUE_MEGABYTES_PER_SECOND -> + { + BigDecimal megabytes = new BigDecimal(bytes).divide(BigDecimal.valueOf(1000000), RoundingMode.HALF_UP); + yield megabytes.divide(seconds, 2, RoundingMode.HALF_UP); + } + default -> BigDecimal.ZERO; + }; + } +} diff --git a/src/main/java/dev/dsf/bpe/util/task/input/generator/DownloadResourceReferenceGenerator.java b/src/main/java/dev/dsf/bpe/util/task/input/generator/DownloadResourceReferenceGenerator.java new file mode 100644 index 00000000..0857e824 --- /dev/null +++ b/src/main/java/dev/dsf/bpe/util/task/input/generator/DownloadResourceReferenceGenerator.java @@ -0,0 +1,25 @@ +package dev.dsf.bpe.util.task.input.generator; + +import org.hl7.fhir.r4.model.Coding; +import org.hl7.fhir.r4.model.Reference; +import org.hl7.fhir.r4.model.Task; + +import dev.dsf.bpe.ConstantsPing; + +public class DownloadResourceReferenceGenerator +{ + public static Task.ParameterComponent create(String uri) + { + Reference reference = new Reference(uri); + reference.setType("Binary"); + return create(reference); + } + + public static Task.ParameterComponent create(Reference reference) + { + Task.ParameterComponent param = new Task.ParameterComponent(); + param.setValue(reference).getType().addCoding(new Coding(ConstantsPing.CODESYSTEM_DSF_PING, + ConstantsPing.CODESYSTEM_DSF_PING_VALUE_DOWNLOAD_RESOURCE_REFERENCE, null)); + return param; + } +} diff --git a/src/main/java/dev/dsf/bpe/util/task/input/generator/DownloadResourceSizeGenerator.java b/src/main/java/dev/dsf/bpe/util/task/input/generator/DownloadResourceSizeGenerator.java new file mode 100644 index 00000000..8071fc20 --- /dev/null +++ b/src/main/java/dev/dsf/bpe/util/task/input/generator/DownloadResourceSizeGenerator.java @@ -0,0 +1,18 @@ +package dev.dsf.bpe.util.task.input.generator; + +import org.hl7.fhir.r4.model.Coding; +import org.hl7.fhir.r4.model.IntegerType; +import org.hl7.fhir.r4.model.Task; + +import dev.dsf.bpe.ConstantsPing; + +public class DownloadResourceSizeGenerator +{ + public static Task.ParameterComponent create(int sizeBytes) + { + Task.ParameterComponent param = new Task.ParameterComponent(); + param.setValue(new IntegerType(sizeBytes)).getType().addCoding(new Coding(ConstantsPing.CODESYSTEM_DSF_PING, + ConstantsPing.CODESYSTEM_DSF_PING_VALUE_DOWNLOAD_RESOURCE_SIZE_BYTES, null)); + return param; + } +} diff --git a/src/main/java/dev/dsf/bpe/util/task/input/generator/DownloadedBytesGenerator.java b/src/main/java/dev/dsf/bpe/util/task/input/generator/DownloadedBytesGenerator.java new file mode 100644 index 00000000..e75d0705 --- /dev/null +++ b/src/main/java/dev/dsf/bpe/util/task/input/generator/DownloadedBytesGenerator.java @@ -0,0 +1,17 @@ +package dev.dsf.bpe.util.task.input.generator; + +import org.hl7.fhir.r4.model.IntegerType; +import org.hl7.fhir.r4.model.Task; + +import dev.dsf.bpe.ConstantsPing; + +public class DownloadedBytesGenerator +{ + public static Task.ParameterComponent create(int bytes) + { + Task.ParameterComponent param = new Task.ParameterComponent(); + param.setValue(new IntegerType(bytes)).getType().addCoding().setSystem(ConstantsPing.CODESYSTEM_DSF_PING) + .setCode(ConstantsPing.CODESYSTEM_DSF_PING_VALUE_DOWNLOADED_BYTES); + return param; + } +} diff --git a/src/main/java/dev/dsf/bpe/util/task/input/generator/DownloadedDurationMillisGenerator.java b/src/main/java/dev/dsf/bpe/util/task/input/generator/DownloadedDurationMillisGenerator.java new file mode 100644 index 00000000..313155c3 --- /dev/null +++ b/src/main/java/dev/dsf/bpe/util/task/input/generator/DownloadedDurationMillisGenerator.java @@ -0,0 +1,18 @@ +package dev.dsf.bpe.util.task.input.generator; + +import org.hl7.fhir.r4.model.DecimalType; +import org.hl7.fhir.r4.model.Task; + +import dev.dsf.bpe.ConstantsPing; + +public class DownloadedDurationMillisGenerator +{ + public static Task.ParameterComponent create(long durationMillis) + { + Task.ParameterComponent param = new Task.ParameterComponent(); + param.setValue(new DecimalType(durationMillis)).getType().addCoding() + .setSystem(ConstantsPing.CODESYSTEM_DSF_PING) + .setCode(ConstantsPing.CODESYSTEM_DSF_PING_VALUE_DOWNLOADED_DURATION_MILLIS); + return param; + } +} diff --git a/src/main/java/dev/dsf/bpe/util/task/input/generator/ErrorMessageGenerator.java b/src/main/java/dev/dsf/bpe/util/task/input/generator/ErrorMessageGenerator.java new file mode 100644 index 00000000..d7c62b67 --- /dev/null +++ b/src/main/java/dev/dsf/bpe/util/task/input/generator/ErrorMessageGenerator.java @@ -0,0 +1,27 @@ +package dev.dsf.bpe.util.task.input.generator; + +import java.util.List; +import java.util.stream.Collectors; + +import org.hl7.fhir.r4.model.StringType; +import org.hl7.fhir.r4.model.Task; + +import dev.dsf.bpe.ConstantsPing; + +public class ErrorMessageGenerator +{ + public static List create(List errorMessages) + { + if (errorMessages == null || errorMessages.isEmpty()) + return List.of(); + return errorMessages.stream().map(ErrorMessageGenerator::create).collect(Collectors.toList()); + } + + public static Task.ParameterComponent create(String errorMessage) + { + Task.ParameterComponent param = new Task.ParameterComponent(); + param.setValue(new StringType(errorMessage)).getType().addCoding().setSystem(ConstantsPing.CODESYSTEM_DSF_PING) + .setCode(ConstantsPing.CODESYSTEM_DSF_PING_STATUS_VALUE_ERROR_MESSAGE); + return param; + } +} diff --git a/src/main/java/dev/dsf/bpe/util/task/input/generator/NetworkSpeedMetricGenerator.java b/src/main/java/dev/dsf/bpe/util/task/input/generator/NetworkSpeedMetricGenerator.java new file mode 100644 index 00000000..b1d33761 --- /dev/null +++ b/src/main/java/dev/dsf/bpe/util/task/input/generator/NetworkSpeedMetricGenerator.java @@ -0,0 +1,28 @@ +package dev.dsf.bpe.util.task.input.generator; + +import org.hl7.fhir.r4.model.Coding; +import org.hl7.fhir.r4.model.DecimalType; +import org.hl7.fhir.r4.model.IntegerType; +import org.hl7.fhir.r4.model.Task; + +import dev.dsf.bpe.ConstantsPing; + +public class NetworkSpeedMetricGenerator +{ + public static Task.ParameterComponent createDownloadedDurationMillis(long duration) + { + Task.ParameterComponent downloadedDuration = new Task.ParameterComponent(); + downloadedDuration.setValue(new DecimalType(duration)).getType() + .addCoding(new Coding(ConstantsPing.CODESYSTEM_DSF_PING, + ConstantsPing.CODESYSTEM_DSF_PING_VALUE_DOWNLOADED_DURATION_MILLIS, null)); + return downloadedDuration; + } + + public static Task.ParameterComponent createDownloadedBytes(int bytes) + { + Task.ParameterComponent downloadedBytes = new Task.ParameterComponent(); + downloadedBytes.setValue(new IntegerType(bytes)).getType().addCoding(new Coding( + ConstantsPing.CODESYSTEM_DSF_PING, ConstantsPing.CODESYSTEM_DSF_PING_VALUE_DOWNLOADED_BYTES, null)); + return downloadedBytes; + } +} diff --git a/src/main/java/dev/dsf/bpe/util/task/output/generator/ErrorMessageGenerator.java b/src/main/java/dev/dsf/bpe/util/task/output/generator/ErrorMessageGenerator.java new file mode 100644 index 00000000..ab34226b --- /dev/null +++ b/src/main/java/dev/dsf/bpe/util/task/output/generator/ErrorMessageGenerator.java @@ -0,0 +1,27 @@ +package dev.dsf.bpe.util.task.output.generator; + +import java.util.List; +import java.util.stream.Collectors; + +import org.hl7.fhir.r4.model.StringType; +import org.hl7.fhir.r4.model.Task; + +import dev.dsf.bpe.ConstantsPing; + +public class ErrorMessageGenerator +{ + public static List create(List errorMessages) + { + if (errorMessages == null || errorMessages.isEmpty()) + return List.of(); + return errorMessages.stream().map(ErrorMessageGenerator::create).collect(Collectors.toList()); + } + + public static Task.TaskOutputComponent create(String errorMessage) + { + Task.TaskOutputComponent param = new Task.TaskOutputComponent(); + param.setValue(new StringType(errorMessage)).getType().addCoding().setSystem(ConstantsPing.CODESYSTEM_DSF_PING) + .setCode(ConstantsPing.CODESYSTEM_DSF_PING_STATUS_VALUE_ERROR_MESSAGE); + return param; + } +} diff --git a/src/main/java/dev/dsf/bpe/util/task/output/generator/PingStatusGenerator.java b/src/main/java/dev/dsf/bpe/util/task/output/generator/PingStatusGenerator.java new file mode 100644 index 00000000..8cd25bfc --- /dev/null +++ b/src/main/java/dev/dsf/bpe/util/task/output/generator/PingStatusGenerator.java @@ -0,0 +1,1008 @@ +package dev.dsf.bpe.util.task.output.generator; + +import static dev.dsf.bpe.util.VersionUtils.appendFhirResourceVersion; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.hl7.fhir.r4.model.Coding; +import org.hl7.fhir.r4.model.DecimalType; +import org.hl7.fhir.r4.model.Extension; +import org.hl7.fhir.r4.model.StringType; +import org.hl7.fhir.r4.model.Task; +import org.hl7.fhir.r4.model.Task.TaskOutputComponent; +import org.hl7.fhir.r4.model.Type; + +import dev.dsf.bpe.ConstantsPing; +import dev.dsf.bpe.v1.constants.NamingSystems.EndpointIdentifier; +import dev.dsf.bpe.v1.constants.NamingSystems.OrganizationIdentifier; +import dev.dsf.bpe.v1.variables.Target; + +public class PingStatusGenerator +{ + public static Task updatePingStatusOutput(Task task, String correlationKey, List errorMessages) + { + List outputs = filterByCorrelationKey( + getOutputsByExtensionUrlAndCodes(task, ConstantsPing.CODESYSTEM_DSF_PING_VALUE_PING_STATUS), + correlationKey); + + if (outputs.isEmpty()) + { + task.addOutput(updateStatusOutput(new TaskOutputComponent(), errorMessages)); + } + else + { + if (outputs.size() == 1) + { + updateStatusOutput(outputs.get(0), errorMessages); + } + else + { + throw new RuntimeException("There is more than one ping/pong status output for task " + task.getId() + + " with correlation key " + correlationKey); + } + } + return task; + } + + public static Task updatePongStatusOutput(Task task, List errorMessages) + { + List pongStatusOutputs = getOutputsByExtensionUrlAndCodes(task, + ConstantsPing.CODESYSTEM_DSF_PING_VALUE_PONG_STATUS); + if (pongStatusOutputs.isEmpty()) + { + task.addOutput(updateStatusOutput(new TaskOutputComponent(), errorMessages)); + } + else + { + if (pongStatusOutputs.size() == 1) + { + updateStatusOutput(pongStatusOutputs.get(0), errorMessages); + } + else + { + throw new RuntimeException("There is more than one pong status output for task " + task.getId()); + } + } + + return task; + } + + public static TaskOutputComponent updateStatusOutput(TaskOutputComponent output, List errorMessages) + { + if (output != null) + { + Extension pingStatusExtension = getOrCreatePingStatusExtension(output); + List errorMessageExtensions = pingStatusExtension.getExtension().stream() + .filter(extension -> ConstantsPing.EXTENSION_URL_ERROR_MESSAGE.equals(extension.getUrl())).toList(); + if (errorMessageExtensions.isEmpty()) + { + addErrorMessages(output, errorMessages); + } + else + { + Set existingErrors = errorMessageExtensions.stream() + .map(extension -> ((StringType) extension.getValue()).getValue()).collect(Collectors.toSet()); + existingErrors.addAll(errorMessages); + List nonErrorMessageExtensions = pingStatusExtension.getExtension().stream() + .filter(extension -> !ConstantsPing.EXTENSION_URL_ERROR_MESSAGE.equals(extension.getUrl())) + .toList(); + List updatedErrorMessageExtensions = existingErrors.stream() + .map(errorMessage -> new Extension().setUrl(ConstantsPing.EXTENSION_URL_ERROR_MESSAGE) + .setValue(new StringType(errorMessage))) + .collect(Collectors.toCollection(ArrayList::new)); + updatedErrorMessageExtensions.addAll(nonErrorMessageExtensions); + pingStatusExtension.setExtension(updatedErrorMessageExtensions); + } + } + + return output; + } + + public static Task updatePingStatusOutput(Task task, String correlationKey, String statusCode) + { + List pingStatusOutputs = filterByCorrelationKey( + getOutputsByExtensionUrlAndCodes(task, ConstantsPing.CODESYSTEM_DSF_PING_VALUE_PING_STATUS), + correlationKey); + if (pingStatusOutputs.isEmpty()) + { + task.addOutput(updatePingStatusOutput(new TaskOutputComponent(), statusCode)); + } + else + { + if (pingStatusOutputs.size() == 1) + { + updatePingStatusOutput(pingStatusOutputs.get(0), statusCode); + } + else + { + throw new RuntimeException("There is more than one ping status output for task " + task.getId() + + " with correlation key " + correlationKey); + } + } + + return task; + } + + public static TaskOutputComponent updatePingStatusOutput(TaskOutputComponent outputComponent, String statusCode) + { + if (hasStatusCodeSet(outputComponent)) + { + updateStatus(outputComponent, ConstantsPing.CODESYSTEM_DSF_PING_VALUE_PING_STATUS, statusCode); + } + else + { + addStatus(outputComponent, ConstantsPing.CODESYSTEM_DSF_PING_VALUE_PING_STATUS, statusCode); + } + + return outputComponent; + } + + public static Task updatePongStatusOutput(Task task, String statusCode) + { + List pongStatusOutputs = getOutputsByExtensionUrlAndCodes(task, + ConstantsPing.CODESYSTEM_DSF_PING_VALUE_PONG_STATUS); + if (pongStatusOutputs.isEmpty()) + { + task.addOutput(updatePongStatusOutput(new TaskOutputComponent(), statusCode)); + } + else + { + if (pongStatusOutputs.size() == 1) + { + updatePongStatusOutput(pongStatusOutputs.get(0), statusCode); + } + else + { + throw new RuntimeException("There is more than one pong status output for task " + task.getId()); + } + } + + return task; + } + + public static TaskOutputComponent updatePongStatusOutput(TaskOutputComponent outputComponent, String statusCode) + { + if (hasStatusCodeSet(outputComponent)) + { + updateStatus(outputComponent, ConstantsPing.CODESYSTEM_DSF_PING_VALUE_PONG_STATUS, statusCode); + } + else + { + addStatus(outputComponent, ConstantsPing.CODESYSTEM_DSF_PING_VALUE_PONG_STATUS, statusCode); + } + + return outputComponent; + } + + public static Task updatePingStatusOutput(Task task, Target target) + { + List outputs = filterByCorrelationKey( + getOutputsByExtensionUrlAndCodes(task, ConstantsPing.CODESYSTEM_DSF_PING_VALUE_PING_STATUS), + target.getCorrelationKey()); + if (outputs.isEmpty()) + { + task.addOutput(updateStatusOutput(new TaskOutputComponent(), target)); + } + else + { + if (outputs.size() == 1) + { + updateStatusOutput(outputs.get(0), target); + } + else + { + throw new RuntimeException("There is more than one ping/pong status output for task " + task.getId() + + " with correlation key " + target.getCorrelationKey()); + } + } + return task; + } + + public static Task updatePongStatusOutput(Task task, Target target) + { + List outputs = getOutputsByExtensionUrlAndCodes(task, + ConstantsPing.CODESYSTEM_DSF_PING_VALUE_PONG_STATUS); + if (outputs.isEmpty()) + { + task.addOutput(updateStatusOutput(new TaskOutputComponent(), target)); + } + else + { + if (outputs.size() == 1) + { + updateStatusOutput(outputs.get(0), target); + } + else + { + throw new RuntimeException("There is more than one pong status output for task " + task.getId()); + } + } + + return task; + } + + public static TaskOutputComponent updateStatusOutput(TaskOutputComponent outputComponent, Target target) + { + if (hasTargetSet(outputComponent)) + { + updateTarget(outputComponent, target); + } + else + { + addTarget(outputComponent, target); + } + return outputComponent; + } + + public static Task updatePingStatusOutput(Task task, String correlationKey, BigDecimal downloadSpeed, + BigDecimal uploadSpeed, String statusCode) + { + List outputs = filterByCorrelationKey( + getOutputsByExtensionUrlAndCodes(task, ConstantsPing.CODESYSTEM_DSF_PING_VALUE_PING_STATUS), + correlationKey); + if (outputs.isEmpty()) + { + task.addOutput(updateStatusOutput(new TaskOutputComponent(), downloadSpeed, uploadSpeed, statusCode)); + } + else + { + if (outputs.size() == 1) + { + updateStatusOutput(outputs.get(0), downloadSpeed, uploadSpeed, statusCode); + } + else + { + throw new RuntimeException("There is more than one ping/pong status output for task " + task.getId() + + " with correlation key " + correlationKey); + } + } + return task; + } + + public static Task updatePongStatusOutput(Task task, BigDecimal downloadSpeed, BigDecimal uploadSpeed, + String statusCode) + { + List outputs = getOutputsByExtensionUrlAndCodes(task, + ConstantsPing.CODESYSTEM_DSF_PING_VALUE_PONG_STATUS); + if (outputs.isEmpty()) + { + task.addOutput(updateStatusOutput(new TaskOutputComponent(), downloadSpeed, uploadSpeed, statusCode)); + } + else + { + if (outputs.size() == 1) + { + updateStatusOutput(outputs.get(0), downloadSpeed, uploadSpeed, statusCode); + } + else + { + throw new RuntimeException("There is more than one ping/pong status output for task " + task.getId()); + } + } + + return task; + } + + public static TaskOutputComponent updateStatusOutput(TaskOutputComponent outputComponent, BigDecimal downloadSpeed, + BigDecimal uploadSpeed, String networkSpeedUnit) + { + if (hasDownloadSpeedSet(outputComponent)) + { + updateDownloadSpeed(outputComponent, downloadSpeed, networkSpeedUnit); + } + else + { + addDownloadSpeed(outputComponent, downloadSpeed, networkSpeedUnit); + } + + if (hasUploadSpeedSet(outputComponent)) + { + updateUploadSpeed(outputComponent, uploadSpeed, networkSpeedUnit); + } + else + { + addUploadSpeed(outputComponent, uploadSpeed, networkSpeedUnit); + } + + return outputComponent; + } + + public static Task updatePingStatusOutputDownloadSpeed(Task task, String correlationKey, BigDecimal downloadSpeed, + String networkSpeedUnit) + { + List outputs = filterByCorrelationKey( + getOutputsByExtensionUrlAndCodes(task, ConstantsPing.CODESYSTEM_DSF_PING_VALUE_PING_STATUS), + correlationKey); + if (outputs.isEmpty()) + { + task.addOutput(updateStatusOutputDownloadSpeed(new TaskOutputComponent(), downloadSpeed, networkSpeedUnit)); + } + else + { + if (outputs.size() == 1) + { + updateStatusOutputDownloadSpeed(outputs.get(0), downloadSpeed, networkSpeedUnit); + } + else + { + throw new RuntimeException("There is more than one ping/pong status output for task " + task.getId() + + " with correlation key " + correlationKey); + } + } + return task; + } + + public static Task updatePongStatusOutputDownloadSpeed(Task task, BigDecimal downloadSpeed, String networkSpeedUnit) + { + List outputs = getOutputsByExtensionUrlAndCodes(task, + ConstantsPing.CODESYSTEM_DSF_PING_VALUE_PONG_STATUS); + if (outputs.isEmpty()) + { + task.addOutput(updateStatusOutputDownloadSpeed(new TaskOutputComponent(), downloadSpeed, networkSpeedUnit)); + } + else + { + if (outputs.size() == 1) + { + updateStatusOutputDownloadSpeed(outputs.get(0), downloadSpeed, networkSpeedUnit); + } + else + { + throw new RuntimeException("There is more than one ping/pong status output for task " + task.getId()); + } + } + + return task; + } + + public static TaskOutputComponent updateStatusOutputDownloadSpeed(TaskOutputComponent outputComponent, + BigDecimal downloadSpeed, String networkSpeedUnit) + { + if (hasDownloadSpeedSet(outputComponent)) + { + updateDownloadSpeed(outputComponent, downloadSpeed, networkSpeedUnit); + } + else + { + addDownloadSpeed(outputComponent, downloadSpeed, networkSpeedUnit); + } + + return outputComponent; + } + + public static Task updatePingStatusOutputUploadSpeed(Task task, String correlationKey, BigDecimal uploadSpeed, + String networkSpeedUnit) + { + List outputs = filterByCorrelationKey( + getOutputsByExtensionUrlAndCodes(task, ConstantsPing.CODESYSTEM_DSF_PING_VALUE_PING_STATUS), + correlationKey); + if (outputs.isEmpty()) + { + task.addOutput(updateStatusOutputUploadSpeed(new TaskOutputComponent(), uploadSpeed, networkSpeedUnit)); + } + else + { + if (outputs.size() == 1) + { + updateStatusOutputUploadSpeed(outputs.get(0), uploadSpeed, networkSpeedUnit); + } + else + { + throw new RuntimeException("There is more than one ping/pong status output for task " + task.getId() + + " with correlation key " + correlationKey); + } + } + + return task; + } + + public static Task updatePongStatusOutputUploadSpeed(Task task, BigDecimal uploadSpeed, String networkSpeedUnit) + { + List outputs = getOutputsByExtensionUrlAndCodes(task, + ConstantsPing.CODESYSTEM_DSF_PING_VALUE_PONG_STATUS); + if (outputs.isEmpty()) + { + task.addOutput(updateStatusOutputUploadSpeed(new TaskOutputComponent(), uploadSpeed, networkSpeedUnit)); + } + else + { + if (outputs.size() == 1) + { + updateStatusOutputUploadSpeed(outputs.get(0), uploadSpeed, networkSpeedUnit); + } + else + { + throw new RuntimeException("There is more than one ping/pong status output for task " + task.getId()); + } + } + + return task; + } + + public static TaskOutputComponent updateStatusOutputUploadSpeed(TaskOutputComponent outputComponent, + BigDecimal uploadSpeed, String networkSpeedUnit) + { + if (hasDownloadSpeedSet(outputComponent)) + { + updateUploadSpeed(outputComponent, uploadSpeed, networkSpeedUnit); + } + else + { + addUploadSpeed(outputComponent, uploadSpeed, networkSpeedUnit); + } + + return outputComponent; + } + + private static boolean hasTargetSet(TaskOutputComponent outputComponent) + { + List correlationKeyExtensions = outputComponent + .getExtensionsByUrl(ConstantsPing.EXTENSION_URL_CORRELATION_KEY); + List organizationIdentifierExtensions = outputComponent + .getExtensionsByUrl(ConstantsPing.EXTENSION_URL_ORGANIZATION_IDENTIFIER); + List endpointIdentifierExtensions = outputComponent + .getExtensionsByUrl(ConstantsPing.EXTENSION_URL_ENDPOINT_IDENTIFIER); + return !correlationKeyExtensions.isEmpty() || !organizationIdentifierExtensions.isEmpty() + || !endpointIdentifierExtensions.isEmpty(); + } + + private static boolean hasStatusCodeSet(TaskOutputComponent outputComponent) + { + Type valueType = outputComponent.getValue(); + List outputTypeCodings = outputComponent.getType().getCoding(); + + return (valueType instanceof Coding coding + && ConstantsPing.CODESYSTEM_DSF_PING_STATUS.equals(coding.getSystem())) + || outputTypeCodings.stream() + .anyMatch(coding -> ConstantsPing.CODESYSTEM_DSF_PING.equals(coding.getSystem())); + } + + private static boolean hasNetworkSpeedSet(TaskOutputComponent outputComponent) + { + return hasDownloadSpeedSet(outputComponent) && hasUploadSpeedSet(outputComponent); + } + + private static boolean hasDownloadSpeedSet(TaskOutputComponent outputComponent) + { + Extension extension = getOrCreatePingStatusExtension(outputComponent); + Extension downloadSpeedExtension = extension.getExtensionByUrl(ConstantsPing.EXTENSION_URL_DOWNLOAD_SPEED); + + return downloadSpeedExtension != null; + } + + private static boolean hasUploadSpeedSet(TaskOutputComponent outputComponent) + { + Extension extension = getOrCreatePingStatusExtension(outputComponent); + Extension uploadSpeedExtension = extension.getExtensionByUrl(ConstantsPing.EXTENSION_URL_UPLOAD_SPEED); + + return uploadSpeedExtension != null; + } + + public static TaskOutputComponent createPingStatusOutput(Target target, String statusCode) + { + return createPingStatusOutput(target, statusCode, null); + } + + public static TaskOutputComponent createPingStatusOutput(Target target, String statusCode, BigDecimal downloadSpeed, + BigDecimal uploadSpeed, String unit) + { + return createPingStatusOutput(target, statusCode, null, downloadSpeed, uploadSpeed, unit); + } + + public static TaskOutputComponent createPingStatusOutput(Target target, String statusCode, + List errorMessages) + { + return createStatusOutput(target, ConstantsPing.CODESYSTEM_DSF_PING_VALUE_PING_STATUS, statusCode, + errorMessages, null, null, null); + } + + public static TaskOutputComponent createPingStatusOutput(Target target, String statusCode, + List errorMessages, BigDecimal downloadSpeed, BigDecimal uploadSpeed, String unit) + { + return createStatusOutput(target, ConstantsPing.CODESYSTEM_DSF_PING_VALUE_PING_STATUS, statusCode, + errorMessages, downloadSpeed, uploadSpeed, unit); + } + + public static TaskOutputComponent createPongStatusOutput(Target target, String statusCode) + { + return createPongStatusOutput(target, statusCode, null); + } + + public static TaskOutputComponent createPongStatusOutput(Target target, String statusCode, BigDecimal downloadSpeed, + BigDecimal uploadSpeed, String unit) + { + return createPongStatusOutput(target, statusCode, null, downloadSpeed, uploadSpeed, unit); + } + + public static TaskOutputComponent createPongStatusOutput(Target target, String statusCode, + List errorMessages) + { + return createStatusOutput(target, ConstantsPing.CODESYSTEM_DSF_PING_VALUE_PONG_STATUS, statusCode, + errorMessages, null, null, null); + } + + public static TaskOutputComponent createPongStatusOutput(Target target, String statusCode, + List errorMessages, BigDecimal downloadSpeed, BigDecimal uploadSpeed, String unit) + { + return createStatusOutput(target, ConstantsPing.CODESYSTEM_DSF_PING_VALUE_PONG_STATUS, statusCode, + errorMessages, downloadSpeed, uploadSpeed, unit); + } + + private static TaskOutputComponent createStatusOutput(Target target, String outputParameter, String statusCode, + List errorMessages, BigDecimal downloadSpeed, BigDecimal uploadSpeed, String unit) + { + TaskOutputComponent output = new TaskOutputComponent(); + addStatus(output, outputParameter, statusCode); + addTarget(output, target); + addErrorMessages(output, errorMessages); + addNetworkSpeed(output, downloadSpeed, uploadSpeed, unit); + + return output; + } + + private static TaskOutputComponent addStatus(TaskOutputComponent outputComponent, String outputParameter, + String statusCode) + { + if (outputParameter != null && statusCode != null) + { + outputComponent + .setValue(new Coding().setSystem(ConstantsPing.CODESYSTEM_DSF_PING_STATUS).setCode(statusCode)); + outputComponent.getType().addCoding().setSystem(ConstantsPing.CODESYSTEM_DSF_PING).setCode(outputParameter); + sortStatusOutputExtensions(outputComponent); + } + + return outputComponent; + } + + private static TaskOutputComponent updateStatus(TaskOutputComponent outputComponent, String outputParameter, + String statusCode) + { + Type valueType = outputComponent.getValue(); + if (valueType instanceof Coding coding) + { + coding.setSystem(ConstantsPing.CODESYSTEM_DSF_PING_STATUS).setCode(statusCode); + } + else + { + outputComponent + .setValue(new Coding().setSystem(ConstantsPing.CODESYSTEM_DSF_PING_STATUS).setCode(statusCode)); + } + + List outputTypeCodings = outputComponent.getType().getCoding(); + if (outputTypeCodings.isEmpty()) + { + outputComponent.getType().addCoding().setSystem(ConstantsPing.CODESYSTEM_DSF_PING).setCode(outputParameter); + } + else + { + if (outputTypeCodings.size() == 1) + { + Coding coding = outputTypeCodings.get(0); + coding.setSystem(ConstantsPing.CODESYSTEM_DSF_PING).setCode(outputParameter); + } + else + { + outputComponent.getType().setCoding(null); + outputComponent.getType().addCoding().setSystem(ConstantsPing.CODESYSTEM_DSF_PING) + .setCode(outputParameter); + } + } + sortStatusOutputExtensions(outputComponent); + + return outputComponent; + } + + private static TaskOutputComponent addTarget(TaskOutputComponent outputComponent, Target target) + { + if (target != null) + { + Extension extension = getOrCreatePingStatusExtension(outputComponent); + + extension.addExtension(ConstantsPing.EXTENSION_URL_CORRELATION_KEY, + new StringType(target.getCorrelationKey())); + extension.addExtension().setUrl(ConstantsPing.EXTENSION_URL_ORGANIZATION_IDENTIFIER) + .setValue(OrganizationIdentifier.withValue(target.getOrganizationIdentifierValue())); + extension.addExtension().setUrl(ConstantsPing.EXTENSION_URL_ENDPOINT_IDENTIFIER) + .setValue(EndpointIdentifier.withValue(target.getEndpointIdentifierValue())); + sortStatusOutputExtensions(outputComponent); + } + + return outputComponent; + } + + private static TaskOutputComponent updateTarget(TaskOutputComponent outputComponent, Target target) + { + Extension extension = getOrCreatePingStatusExtension(outputComponent); + + Extension correlationKeyExtension = extension.getExtensionByUrl(ConstantsPing.EXTENSION_URL_CORRELATION_KEY); + if (correlationKeyExtension != null) + { + correlationKeyExtension.setValue(new StringType(target.getCorrelationKey())); + } + else + { + extension.addExtension(ConstantsPing.EXTENSION_URL_CORRELATION_KEY, + new StringType(target.getCorrelationKey())); + } + + Extension organizationIdentifierExtension = extension + .getExtensionByUrl(ConstantsPing.EXTENSION_URL_ORGANIZATION_IDENTIFIER); + if (organizationIdentifierExtension != null) + { + organizationIdentifierExtension.setValue(new StringType(target.getOrganizationIdentifierValue())); + } + else + { + extension.addExtension().setUrl(ConstantsPing.EXTENSION_URL_ORGANIZATION_IDENTIFIER) + .setValue(OrganizationIdentifier.withValue(target.getOrganizationIdentifierValue())); + } + + Extension urlEndpointIdentifier = extension.getExtensionByUrl(ConstantsPing.EXTENSION_URL_ENDPOINT_IDENTIFIER); + if (urlEndpointIdentifier != null) + { + urlEndpointIdentifier.setValue(new StringType(target.getEndpointIdentifierValue())); + } + else + { + extension.addExtension().setUrl(ConstantsPing.EXTENSION_URL_ENDPOINT_IDENTIFIER) + .setValue(EndpointIdentifier.withValue(target.getEndpointIdentifierValue())); + } + sortStatusOutputExtensions(outputComponent); + + return outputComponent; + } + + private static TaskOutputComponent addErrorMessages(TaskOutputComponent outputComponent, List errorMessages) + { + if (errorMessages != null) + { + Extension extension = getOrCreatePingStatusExtension(outputComponent); + for (String errorMessage : errorMessages) + { + extension.addExtension().setUrl(ConstantsPing.EXTENSION_URL_ERROR_MESSAGE) + .setValue(new StringType(errorMessage)); + } + } + sortStatusOutputExtensions(outputComponent); + return outputComponent; + } + + private static TaskOutputComponent updateErrorMessages(TaskOutputComponent outputComponent, + List errorMessages) + { + Extension extension = getOrCreatePingStatusExtension(outputComponent); + List nonErrorExtensions = extension.getExtension().stream() + .filter(extension1 -> !ConstantsPing.EXTENSION_URL_ERROR_MESSAGE.equals(extension1.getUrl())) + .collect(Collectors.toCollection(ArrayList::new)); + + if (errorMessages != null) + { + List newErrorExtensions = errorMessages.stream() + .map(errorMessage -> new Extension(ConstantsPing.EXTENSION_URL_ERROR_MESSAGE, + new StringType(errorMessage))) + .collect(Collectors.toCollection(ArrayList::new)); + nonErrorExtensions.addAll(newErrorExtensions); + extension.setExtension(newErrorExtensions); + } + else + { + extension.setExtension(nonErrorExtensions); + } + sortStatusOutputExtensions(outputComponent); + + return outputComponent; + } + + private static TaskOutputComponent addNetworkSpeed(TaskOutputComponent outputComponent, BigDecimal downloadSpeed, + BigDecimal uploadSpeed, String unit) + { + addDownloadSpeed(outputComponent, downloadSpeed, unit); + addUploadSpeed(outputComponent, uploadSpeed, unit); + + return outputComponent; + } + + private static TaskOutputComponent updateNetworkSpeed(TaskOutputComponent outputComponent, BigDecimal downloadSpeed, + BigDecimal uploadSpeed, String unit) + { + updateDownloadSpeed(outputComponent, downloadSpeed, unit); + updateUploadSpeed(outputComponent, uploadSpeed, unit); + return outputComponent; + } + + private static TaskOutputComponent addDownloadSpeed(TaskOutputComponent outputComponent, BigDecimal downloadSpeed, + String unit) + { + if (downloadSpeed != null && unit != null) + { + Extension extension = getOrCreatePingStatusExtension(outputComponent); + Extension downloadSpeedExtension = extension.addExtension() + .setUrl(ConstantsPing.EXTENSION_URL_DOWNLOAD_SPEED); + Extension networkSpeed = downloadSpeedExtension.addExtension() + .setUrl(ConstantsPing.EXTENSION_URL_NETWORK_SPEED); + networkSpeed.addExtension().setUrl(ConstantsPing.EXTENSION_URL_NETWORK_SPEED_VALUE) + .setValue(new DecimalType(downloadSpeed)); + networkSpeed.addExtension().setUrl(ConstantsPing.EXTENSION_URL_NETWORK_SPEED_UNIT) + .setValue(new Coding(ConstantsPing.CODESYSTEM_DSF_PING_UNITS, unit, null)); + } + sortStatusOutputExtensions(outputComponent); + + return outputComponent; + } + + private static TaskOutputComponent updateDownloadSpeed(TaskOutputComponent outputComponent, + BigDecimal downloadSpeed, String unit) + { + if (downloadSpeed != null && unit != null) + { + Extension extension = getOrCreatePingStatusExtension(outputComponent); + Extension downloadSpeedExtension = extension.getExtensionByUrl(ConstantsPing.EXTENSION_URL_DOWNLOAD_SPEED); + if (downloadSpeedExtension != null) + { + Extension networkSpeedExtension = downloadSpeedExtension + .getExtensionByUrl(ConstantsPing.EXTENSION_URL_NETWORK_SPEED); + if (networkSpeedExtension != null) + { + networkSpeedExtension.setExtension(new ArrayList<>()); + List extensions = networkSpeedExtension.getExtension(); + extensions.add(new Extension(ConstantsPing.EXTENSION_URL_NETWORK_SPEED_VALUE, + new DecimalType(downloadSpeed))); + extensions.add(new Extension(ConstantsPing.EXTENSION_URL_NETWORK_SPEED_UNIT, + new Coding(ConstantsPing.CODESYSTEM_DSF_PING_UNITS, unit, null))); + } + } + else + { + downloadSpeedExtension = extension.addExtension().setUrl(ConstantsPing.EXTENSION_URL_DOWNLOAD_SPEED); + Extension networkSpeed = downloadSpeedExtension.addExtension() + .setUrl(ConstantsPing.EXTENSION_URL_NETWORK_SPEED); + networkSpeed.addExtension().setUrl(ConstantsPing.EXTENSION_URL_NETWORK_SPEED_VALUE) + .setValue(new DecimalType(downloadSpeed)); + networkSpeed.addExtension().setUrl(ConstantsPing.EXTENSION_URL_NETWORK_SPEED_UNIT) + .setValue(new Coding(ConstantsPing.CODESYSTEM_DSF_PING_UNITS, unit, null)); + } + } + sortStatusOutputExtensions(outputComponent); + + return outputComponent; + } + + private static TaskOutputComponent addUploadSpeed(TaskOutputComponent outputComponent, BigDecimal uploadSpeed, + String unit) + { + if (uploadSpeed != null && unit != null) + { + Extension extension = getOrCreatePingStatusExtension(outputComponent); + Extension uploadSpeedExtension = extension.addExtension().setUrl(ConstantsPing.EXTENSION_URL_UPLOAD_SPEED); + Extension networkSpeed = uploadSpeedExtension.addExtension() + .setUrl(ConstantsPing.EXTENSION_URL_NETWORK_SPEED); + networkSpeed.addExtension().setUrl(ConstantsPing.EXTENSION_URL_NETWORK_SPEED_VALUE) + .setValue(new DecimalType(uploadSpeed)); + networkSpeed.addExtension().setUrl(ConstantsPing.EXTENSION_URL_NETWORK_SPEED_UNIT) + .setValue(new Coding(ConstantsPing.CODESYSTEM_DSF_PING_UNITS, unit, null)); + } + sortStatusOutputExtensions(outputComponent); + + return outputComponent; + } + + private static TaskOutputComponent updateUploadSpeed(TaskOutputComponent outputComponent, BigDecimal uploadSpeed, + String unit) + { + if (uploadSpeed != null && unit != null) + { + Extension extension = getOrCreatePingStatusExtension(outputComponent); + Extension uploadSpeedExtension = extension.getExtensionByUrl(ConstantsPing.EXTENSION_URL_UPLOAD_SPEED); + if (uploadSpeedExtension != null) + { + Extension networkSpeedExtension = uploadSpeedExtension + .getExtensionByUrl(ConstantsPing.EXTENSION_URL_NETWORK_SPEED); + if (networkSpeedExtension != null) + { + networkSpeedExtension.setExtension(new ArrayList<>()); + List extensions = networkSpeedExtension.getExtension(); + extensions.add(new Extension(ConstantsPing.EXTENSION_URL_NETWORK_SPEED_VALUE, + new DecimalType(uploadSpeed))); + extensions.add(new Extension(ConstantsPing.EXTENSION_URL_NETWORK_SPEED_UNIT, + new Coding(ConstantsPing.CODESYSTEM_DSF_PING_UNITS, unit, null))); + } + } + else + { + uploadSpeedExtension = extension.addExtension().setUrl(ConstantsPing.EXTENSION_URL_UPLOAD_SPEED); + Extension networkSpeed = uploadSpeedExtension.addExtension() + .setUrl(ConstantsPing.EXTENSION_URL_NETWORK_SPEED); + networkSpeed.addExtension().setUrl(ConstantsPing.EXTENSION_URL_NETWORK_SPEED_VALUE) + .setValue(new DecimalType(uploadSpeed)); + networkSpeed.addExtension().setUrl(ConstantsPing.EXTENSION_URL_NETWORK_SPEED_UNIT) + .setValue(new Coding(ConstantsPing.CODESYSTEM_DSF_PING_UNITS, unit, null)); + } + } + sortStatusOutputExtensions(outputComponent); + + return outputComponent; + } + + private static Extension getOrCreatePingStatusExtension(TaskOutputComponent outputComponent) + { + Optional optionalExtension = getPingStatusExtension(outputComponent); + if (optionalExtension.isPresent()) + { + return optionalExtension.get(); + } + else + { + Extension extension = outputComponent.addExtension(); + extension.setUrl(appendFhirResourceVersion(ConstantsPing.EXTENSION_URL_PING_STATUS)); + return extension; + } + } + + private static Optional getPingStatusExtension(Task task) + { + Optional optPingStatusOutput = task.getOutput().stream() + .filter(outputComponent -> outputComponent.getExtension().stream() + .anyMatch(extension -> ConstantsPing.EXTENSION_URL_PING_STATUS.equals(extension.getUrl()))) + .findFirst(); + if (optPingStatusOutput.isPresent()) + { + return getPingStatusExtension(optPingStatusOutput.get()); + } + else + { + return Optional.empty(); + } + } + + private static Optional getPingStatusExtension(TaskOutputComponent outputComponent) + { + List pingStatusExtensions = outputComponent.getExtension().stream() + .filter(extension -> appendFhirResourceVersion(ConstantsPing.EXTENSION_URL_PING_STATUS) + .equals(extension.getUrl())) + .toList(); + if (pingStatusExtensions.isEmpty()) + { + return Optional.empty(); + } + else + { + if (pingStatusExtensions.size() == 1) + { + return Optional.of(pingStatusExtensions.get(0)); + } + else + { + throw new RuntimeException( + "Only one ping status extension is allowed but found " + pingStatusExtensions.size()); + } + } + } + + private static List getOutputsByExtensionUrlAndCodes(Task task, String... codes) + { + return task + .getOutput().stream().filter( + outputComponent -> outputComponent.getType().getCoding().stream() + .anyMatch(coding -> ConstantsPing.CODESYSTEM_DSF_PING.equals(coding.getSystem()) + && Stream.of(codes).anyMatch(code -> code.equals(coding.getCode()))) + || outputComponent.getExtension().stream() + .anyMatch(extension -> appendFhirResourceVersion( + ConstantsPing.EXTENSION_URL_PING_STATUS).equals(extension.getUrl()))) + .collect(Collectors.toCollection(ArrayList::new)); + } + + private static List filterByCorrelationKey(List outputs, + String correlationKey) + { + return outputs.stream().filter(outputComponent -> + { + List outputExtensions = outputComponent.getExtension(); + if (outputExtensions.isEmpty()) + return false; + List pingStatusExtensions = outputExtensions.stream() + .filter(extension -> ConstantsPing.EXTENSION_URL_PING_STATUS.equals(extension.getUrl())).toList(); + if (pingStatusExtensions.isEmpty()) + return false; + List extensionsMatchingCorrelationKey = pingStatusExtensions.stream() + .filter(extension -> extension.getExtension().stream().anyMatch( + extension1 -> ConstantsPing.EXTENSION_URL_CORRELATION_KEY.equals(extension1.getUrl()) + && correlationKey.equals(((StringType) extension1.getValue()).getValue()))) + .toList(); + if (extensionsMatchingCorrelationKey.isEmpty()) + return false; + if (extensionsMatchingCorrelationKey.size() == 1) + { + return true; + } + else + { + throw new RuntimeException( + "Only one Task.output.extension.extension with correlationKey is allowed but found " + + extensionsMatchingCorrelationKey.size()); + } + }).collect(Collectors.toCollection(ArrayList::new)); + } + + private static void sortStatusOutputExtensions(Task task) + { + List outputs = task.getOutput().stream() + .filter(outputComponent -> ConstantsPing.CODESYSTEM_DSF_PING_VALUE_PING_STATUS + .equals(outputComponent.getType().getCodingFirstRep().getCode()) + || ConstantsPing.CODESYSTEM_DSF_PING_VALUE_PONG_STATUS + .equals(outputComponent.getType().getCodingFirstRep().getCode())) + .toList(); + outputs.forEach(PingStatusGenerator::sortStatusOutputExtensions); + } + + private static void sortStatusOutputExtensions(TaskOutputComponent outputComponent) + { + Optional optPingStatusExtension = getPingStatusExtension(outputComponent); + if (optPingStatusExtension.isPresent()) + { + Extension pingStatusExtension = optPingStatusExtension.get(); + List extensions = pingStatusExtension.getExtension(); + List sortedExtensions = new ArrayList<>(); + + // Extensions representing Target + Optional correlationKeyExtension = extensions.stream() + .filter(extension -> ConstantsPing.EXTENSION_URL_CORRELATION_KEY.equals(extension.getUrl())) + .findFirst(); + Optional organizationIdentifierExtension = extensions.stream() + .filter(extension -> ConstantsPing.EXTENSION_URL_ORGANIZATION_IDENTIFIER.equals(extension.getUrl())) + .findFirst(); + Optional endpointIdentifierExtension = extensions.stream() + .filter(extension -> ConstantsPing.EXTENSION_URL_ENDPOINT_IDENTIFIER.equals(extension.getUrl())) + .findFirst(); + if (correlationKeyExtension.isPresent()) + { + extensions.remove(correlationKeyExtension.get()); + sortedExtensions.add(correlationKeyExtension.get()); + } + if (organizationIdentifierExtension.isPresent()) + { + extensions.remove(organizationIdentifierExtension.get()); + sortedExtensions.add(organizationIdentifierExtension.get()); + } + if (endpointIdentifierExtension.isPresent()) + { + extensions.remove(endpointIdentifierExtension.get()); + sortedExtensions.add(endpointIdentifierExtension.get()); + } + + Optional downloadSpeedExtension = extensions.stream() + .filter(extension -> ConstantsPing.EXTENSION_URL_DOWNLOAD_SPEED.equals(extension.getUrl())) + .findFirst(); + if (downloadSpeedExtension.isPresent()) + { + extensions.remove(downloadSpeedExtension.get()); + sortedExtensions.add(downloadSpeedExtension.get()); + } + + Optional uploadSpeedExtension = extensions.stream() + .filter(extension -> ConstantsPing.EXTENSION_URL_UPLOAD_SPEED.equals(extension.getUrl())) + .findFirst(); + if (uploadSpeedExtension.isPresent()) + { + extensions.remove(uploadSpeedExtension.get()); + sortedExtensions.add(uploadSpeedExtension.get()); + } + + List errorMessageExtensions = extensions.stream() + .filter(extension -> ConstantsPing.EXTENSION_URL_ERROR_MESSAGE.equals(extension.getUrl())).toList(); + if (!errorMessageExtensions.isEmpty()) + { + extensions.removeAll(errorMessageExtensions); + sortedExtensions.addAll(errorMessageExtensions); + } + + sortedExtensions.addAll(extensions); + pingStatusExtension.setExtension(sortedExtensions); + } + } +} diff --git a/src/main/resources/bpe/ping-autostart.bpmn b/src/main/resources/bpe/ping-autostart.bpmn index c77a2c4d..c23a2b2f 100644 --- a/src/main/resources/bpe/ping-autostart.bpmn +++ b/src/main/resources/bpe/ping-autostart.bpmn @@ -100,7 +100,7 @@ - + no diff --git a/src/main/resources/bpe/ping.bpmn b/src/main/resources/bpe/ping.bpmn index 5d1ba29e..9f1f5703 100644 --- a/src/main/resources/bpe/ping.bpmn +++ b/src/main/resources/bpe/ping.bpmn @@ -1,265 +1,567 @@ - - - - - - SequenceFlow_0k1j79c - Flow_0j92st0 - + + + + + - Flow_0j92st0 - Flow_099pk09 + Flow_1bfs68o R3/PT5S - Flow_0v2ascf + Flow_10epxa2 - - - - - + Flow_0y9usku Flow_1fjeq2h - - PT20S + + PT200S - - + + + R3/PT5S + Flow_1lghrxh - Flow_03hkxbe - + Flow_1ewmc79 + Flow_1j54c2s - Flow_1lghrxh Flow_0y9usku + Flow_1lghrxh - - + Flow_1fjeq2h Flow_136htek - - - Flow_03hkxbe - Flow_0wmpprs - - Flow_1ipvu5v + Flow_08vgmf6 Flow_1j54c2s Flow_101sqed - - - Flow_101sqed - Flow_16ssf4a - - - ${execution.hasVariable('statusCode') && (statusCode == 'not-allowed' || statusCode == 'not-reachable')} - - - - Flow_0v2ascf - Flow_1ipvu5v - - - - http://dsf.dev/bpe/Process/pong|#{version} - - - http://dsf.dev/fhir/StructureDefinition/task-ping|#{version} - - - ping - - - - - - - - Flow_0wmpprs Flow_136htek - Flow_16ssf4a + Flow_0upu487 Flow_1ho1hys - - - R3/PT5S Flow_1ho1hys + + + + http://dsf.dev/bpe/Process/pong|#{version} + + + ping + + + http://dsf.dev/fhir/StructureDefinition/task-ping|#{version} + + + Flow_10epxa2 + Flow_08vgmf6 + + + Flow_1jwekqw + Flow_13u1nzy + Flow_1yt9547 + + + Flow_1yt9547 + Flow_1v37fff + + + + + http://dsf.dev/fhir/StructureDefinition/task-cleanup-pong|#{version} + + + cleanupPong + + + http://dsf.dev/bpe/Process/pong|#{version} + + + Flow_1v37fff + Flow_0jcfur3 + Flow_0klalf8 + + + Flow_10h6pqh + Flow_0jcfur3 + + + Flow_13u1nzy + Flow_0klalf8 + Flow_03nx6rk + + + + R3/PT5S + + Flow_03nx6rk + + + Flow_10h6pqh + + + + + + + + + + + ${execution.hasVariable('statusCode') && (statusCode == 'not-allowed' || statusCode == 'not-reachable')} + + + ${downloadResourceSizeBytes < 0} + + + + + + + + + Flow_1ewmc79 + Flow_1jwekqw + + + + + + Flow_101sqed + Flow_0upu487 + + + Information in message: +- dowloadResourceSize: long +- downloadResourceReference: Reference + + + Information in message: +- downloadDurationMillis: long +- downloadedBytes: long + + + downloads up to maxDownloadSizeBytes + + + + + + Flow_152nb9r + Flow_1qz7z39 + + + Flow_1bfs68o + Flow_0gubpgz + + + Flow_0gubpgz + Flow_1du5wys + Flow_1du5wys + + Flow_0iuuyo1 + Flow_1qz7z39 + Flow_0ejw9k5 + Flow_1c15ef2 + + + Flow_1c15ef2 + Flow_0j92st0 + + + Flow_0fxlsv3 + Flow_152nb9r + Flow_0iuuyo1 + - SequenceFlow_0k1j79c - + Flow_0e35w2m + - + + + + - - - Flow_099pk09 - Flow_1du5wys + + ${downloadResourceSizeBytes < 0} + + + + + Flow_0e35w2m + Flow_0fxlsv3 + + + Flow_0ql2dtr + + + + + + Flow_0ql2dtr + Flow_0ejw9k5 + + + Includes download speeds and errors of all pongs + + + generates up to maxUploadSizeBystes + + + Process Parameters +- downloadResourceSizeBytes read from Input Paramater (default tbd) +- maxDownloadSizeBytes read from environment variable (default tbd) +- maxUpdloadSizeBytes read from environment variables (default tbd) + + + Cleanup steps: +- delete generated resource + + + + + + Log = logger +Save = save as execution variable +Store = store on DSF FHIR server either as separate resource or output parameter + - - + - - - - - - + + - - + + - - + + - + - - + + - + - - + + - - + + - - + + + + + + + + + + + - - + + - - + + - - - - - + + + + + + + + + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + - - - - + + + - - - + + + + - - - + + + - - - + + + - - - + + + - - - - + + + + + + + + + + + + - + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + - - - + + + - - - - + + + - - - - + + + + - - - + + + - - + + + + + + + + + + + + + + + + + + + + + - - + + + + + + + + + + + + - + - - + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + - - - + + + - - - + + + diff --git a/src/main/resources/bpe/pong.bpmn b/src/main/resources/bpe/pong.bpmn index 39fdb0be..1fae2a96 100644 --- a/src/main/resources/bpe/pong.bpmn +++ b/src/main/resources/bpe/pong.bpmn @@ -1,97 +1,521 @@ - - + + - SequenceFlow_07w11cw - + Flow_19b3cp4 + - - - - SequenceFlow_1ism9wt - - - - http://dsf.dev/fhir/StructureDefinition/task-pong|#{version} - - - pong - - - http://dsf.dev/bpe/Process/ping|#{version} - - - + + Flow_1gap0hi + Flow_10z0d4x + Flow_08gidyv + + + Flow_10z0d4x + Flow_0crmxc2 + + + Flow_08gidyv + Flow_0gvrnxd + Flow_0dp8f59 + Flow_1o3n9u6 + + + + + http://dsf.dev/bpe/Process/ping|#{version} + + + pong + + + http://dsf.dev/fhir/StructureDefinition/task-pong|#{version} + + + Flow_1o3n9u6 + Flow_0fzmjzb + + + Flow_0fzmjzb + Flow_0h8flp6 + Flow_1bzjspe + Flow_00t1ck1 + + + Flow_1bzjspe + Flow_0rj915n + Flow_17x98wg + + + Flow_0rj915n + Flow_1ttsk1o + + + + Flow_17x98wg + Flow_1xfk4ds + + ${execution.hasVariable("cleanupTimerDuration") ? cleanupTimerDuration : "PT20S"} + + + + Flow_1jehvly + Flow_0yujsot + Flow_0x7t1ii + + + Flow_0uj7rm3 + Flow_1jehvly + + + Flow_00t1ck1 + Flow_1lfcycx + Flow_0h8flp6 + Flow_0zib7wr + + + Flow_0x7t1ii + Flow_1w09zt7 + + + Flow_1w09zt7 + Flow_0gvrnxd + + + Flow_0uj7rm3 + + + + + ${downloadResourceSizeBytes < 0} + + + + + + ${execution.hasVariable('statusCode') && statusCode == 'not-reachable'} + + + + ${downloadResourceSizeBytes < 0} + + + + + + + + + Flow_1j5lf0u + Flow_1oo1n55 + + + Flow_1oo1n55 + Flow_1fzloso + + + + Flow_1bgedez - - - SequenceFlow_07w11cw - SequenceFlow_09i9zb8 + + + + Flow_1eh8lho + Flow_1n4fb8d + Flow_1lfcycx + + + + + Flow_1ttsk1o + Flow_1eh8lho + + + + + + Flow_0crmxc2 + Flow_0yujsot + + + + + Flow_1xfk4ds + Flow_1n4fb8d - - SequenceFlow_09i9zb8 - SequenceFlow_1ism9wt + + + Flow_0zib7wr + Flow_1bgedez + + + Flow_19b3cp4 + Flow_1j5lf0u + + + + Flow_0mgrru4 + + + + Flow_0mgrru4 + Flow_0dp8f59 - - - Flow_0yr2pmf + + + + Flow_1fzloso + Flow_1gap0hi + + + Flow_0qxt5zo - - Flow_0yr2pmf - + + Flow_0qxt5zo + - + + + + + Log = logger +Save = save as execution variable +Store = store on DSF FHIR server either as separate resource or output parameter + + + downloads up to maxDownloadSizeBytes + + + Information in message: +- downloadedBytes: long +- downloadedDurationMillis: long +- downloadResourceReference: Reference +- errors: List + + + Cleanup steps: +- delete generated resource + + + Estimation based on download duration e.g. 10x or 20x download duration + + + generates up to maxUploadSizeBystes + + + Process Parameters +- downloadResourceSizeBytes read from Input Paramater (no default, value from message expected) +- maxDownloadSizeBytes read from environment variable (default tbd) +- maxUpdloadSizeBytes read from environment variables (default tbd) + + + + + + - + + + - + - + - - + + + + + + + + + + + + + + + + + + + + + + - + - - + + + + + + + + - - + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + - - - + + + - - - - + + + diff --git a/src/main/resources/fhir/ActivityDefinition/dsf-pong.xml b/src/main/resources/fhir/ActivityDefinition/dsf-pong.xml index 1a877ce9..efde8372 100644 --- a/src/main/resources/fhir/ActivityDefinition/dsf-pong.xml +++ b/src/main/resources/fhir/ActivityDefinition/dsf-pong.xml @@ -31,6 +31,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/fhir/CodeSystem/dsf-ping-status.xml b/src/main/resources/fhir/CodeSystem/dsf-ping-status.xml index 1045752c..eed24337 100644 --- a/src/main/resources/fhir/CodeSystem/dsf-ping-status.xml +++ b/src/main/resources/fhir/CodeSystem/dsf-ping-status.xml @@ -5,7 +5,7 @@ - + @@ -42,8 +42,13 @@ - - + + + + + + + \ No newline at end of file diff --git a/src/main/resources/fhir/CodeSystem/dsf-ping-units.xml b/src/main/resources/fhir/CodeSystem/dsf-ping-units.xml new file mode 100644 index 00000000..b6109468 --- /dev/null +++ b/src/main/resources/fhir/CodeSystem/dsf-ping-units.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + <!-- status managed by bpe --> + <status value="unknown" /> + <experimental value="false" /> + <!-- date managed by bpe --> + <date value="#{date}" /> + <publisher value="DSF" /> + <description value="CodeSystem with ping/pong download speed units" /> + <caseSensitive value="true" /> + <hierarchyMeaning value="grouped-by" /> + <versionNeeded value="false" /> + <content value="complete" /> + <concept> + <code value="bits-per-second"/> + <display value="bps"/> + <definition value="Download speed unit in bits per second" /> + </concept> + <concept> + <code value="bytes-per-second"/> + <display value="Bps"/> + <definition value="Download speed unit in bytes per second" /> + </concept> + <concept> + <code value="megabits-per-second"/> + <display value="Mbps"/> + <definition value="Download speed unit in megabits per second" /> + </concept> + <concept> + <code value="megabytes-per-second"/> + <display value="MBps"/> + <definition value="Download speed unit in megabytes per second" /> + </concept> +</CodeSystem> \ No newline at end of file diff --git a/src/main/resources/fhir/CodeSystem/dsf-ping.xml b/src/main/resources/fhir/CodeSystem/dsf-ping.xml index a79fc556..aa93700c 100644 --- a/src/main/resources/fhir/CodeSystem/dsf-ping.xml +++ b/src/main/resources/fhir/CodeSystem/dsf-ping.xml @@ -5,7 +5,7 @@ <code value="ALL" /> </tag> </meta> - <url value="http://dsf.dev/fhir/CodeSystem/ping" /> + <url value="http://dsf.dev/fhir/CodeSystem/ping-v2" /> <!-- version managed by bpe --> <version value="#{version}" /> <name value="DSF_Ping" /> @@ -46,4 +46,34 @@ <display value="Timer Interval" /> <definition value="Interval between two autostarts of the ping process" /> </concept> + <concept> + <code value="download-resource-size-bytes"/> + <display value="Download Resource Size Bytes"/> + <definition value="Size of the resource to download for speed measurements in bytes"/> + </concept> + <concept> + <code value="download-resource-reference" /> + <display value="Download Resource Reference"/> + <definition value="Reference to the resource to be downloaded for measuring network speed" /> + </concept> + <concept> + <code value="network-speed"/> + <display value="Network Speed"/> + <definition value="Network speed in both upload and download of a specific endpoint"/> + </concept> + <concept> + <code value="downloaded-bytes"/> + <display value="Downloaded Bytes"/> + <definition value="Amount of bytes downloaded to measure network speed"/> + </concept> + <concept> + <code value="downloaded-duration-millis"/> + <display value="Downloaded Duration Millis"/> + <definition value="Duration it took for the resource to be downloaded to measure network speed"/> + </concept> + <concept> + <code value="error-message"/> + <display value="Error Message"/> + <definition value="Error message to be added as an output parameter in the Start Ping Task"/> + </concept> </CodeSystem> \ No newline at end of file diff --git a/src/main/resources/fhir/StructureDefinition/dsf-extension-network-speed.xml b/src/main/resources/fhir/StructureDefinition/dsf-extension-network-speed.xml new file mode 100644 index 00000000..c82ca295 --- /dev/null +++ b/src/main/resources/fhir/StructureDefinition/dsf-extension-network-speed.xml @@ -0,0 +1,95 @@ +<StructureDefinition xmlns="http://hl7.org/fhir"> + <meta> + <tag> + <system value="http://dsf.dev/fhir/CodeSystem/read-access-tag"/> + <code value="ALL"/> + </tag> + </meta> + <url value="http://dsf.dev/fhir/StructureDefinition/extension-network-speed"/> + <!-- version managed by bpe --> + <version value="#{version}" /> + <name value="NetworkSpeed"/> + <!-- status managed by bpe --> + <status value="unknown" /> + <experimental value="false" /> + <!-- date managed by bpe --> + <date value="#{date}" /> + <fhirVersion value="4.0.1"/> + <kind value="complex-type"/> + <abstract value="false"/> + <context> + <type value="element"/> + <expression value="Extension.extension"/> + </context> + <type value="Extension"/> + <baseDefinition value="http://hl7.org/fhir/StructureDefinition/Extension"/> + <derivation value="constraint"/> + <differential> + <element id="Extension.extension"> + <path value="Extension.extension"/> + <slicing> + <discriminator> + <type value="value"/> + <path value="url"/> + </discriminator> + <rules value="open"/> + </slicing> + <min value="2"/> + <max value="2"/> + </element> + <element id="Extension.extension:network-speed"> + <path value="Extension.extension"/> + <sliceName value="network-speed"/> + <min value="1"/> + <max value="1"/> + </element> + <element id="Extension.extension:network-speed.url"> + <path value="Extension.extension.url"/> + <fixedUri value="network-speed"/> + </element> + <element id="Extension.extension:network-speed.value[x]"> + <path value="Extension.extension.value[x]"/> + <type> + <code value="decimal"/> + </type> + </element> + <element id="Extension.extension:unit"> + <path value="Extension.extension"/> + <sliceName value="unit"/> + <min value="1"/> + <max value="1"/> + </element> + <element id="Extension.extension:unit.url"> + <path value="Extension.extension.url"/> + <fixedUri value="unit"/> + </element> + <element id="Extension.extension:unit.value[x]"> + <path value="Extension.extension.value[x]"/> + <min value="1"/> + <type> + <code value="Coding"/> + </type> + </element> + <element id="Extension.extension:unit.value[x].system"> + <path value="Extension.extension.value[x].system"/> + <min value="1"/> + <fixedUri value="http://dsf.dev/fhir/CodeSystem/ping-units-v2"/> + </element> + <element id="Extension.extension:unit.value[x].code"> + <path value="Extension.extension.value[x].code"/> + <min value="1"/> + <binding> + <strength value="required"/> + <valueSet value="http://dsf.dev/fhir/ValueSet/ping-units-v2"/> + </binding> + </element> + <element id="Extension.url"> + <path value="Extension.url"/> + <fixedUri value="http://dsf.dev/fhir/StructureDefinition/extension-network-speed"/> + </element> + <element id="Extension.value[x]"> + <path value="Extension.value[x]"/> + <max value="0"/> + </element> + </differential> +</StructureDefinition> \ No newline at end of file diff --git a/src/main/resources/fhir/StructureDefinition/dsf-extension-ping-status.xml b/src/main/resources/fhir/StructureDefinition/dsf-extension-ping-status.xml index 83251d5a..acf99fbe 100644 --- a/src/main/resources/fhir/StructureDefinition/dsf-extension-ping-status.xml +++ b/src/main/resources/fhir/StructureDefinition/dsf-extension-ping-status.xml @@ -5,7 +5,7 @@ <code value="ALL"/> </tag> </meta> - <url value="http://dsf.dev/fhir/StructureDefinition/extension-ping-status"/> + <url value="http://dsf.dev/fhir/StructureDefinition/extension-ping-status-v2"/> <!-- version managed by bpe --> <version value="#{version}" /> <name value="PingResponse"/> @@ -109,7 +109,6 @@ <path value="Extension.extension"/> <sliceName value="error-message"/> <min value="0"/> - <max value="1"/> </element> <element id="Extension.extension:error-message.url"> <path value="Extension.extension.url"/> @@ -122,9 +121,79 @@ <code value="string"/> </type> </element> + <element id="Extension.extension:download-speed"> + <path value="Extension.extension"/> + <sliceName value="download-speed"/> + <min value="0"/> + <max value="1"/> + </element> + <element id="Extension.extension:download-speed.extension"> + <path value="Extension.extension.extension" /> + <slicing> + <discriminator> + <type value="value" /> + <path value="url" /> + </discriminator> + <rules value="open" /> + </slicing> + <min value="1"/> + <max value="1"/> + </element> + <element id="Extension.extension:download-speed.extension:network-speed-extension"> + <path value="Extension.extension.extension" /> + <sliceName value="network-speed-extension" /> + <min value="1" /> + <type> + <code value="Extension" /> + <profile value="http://dsf.dev/fhir/StructureDefinition/extension-network-speed|#{version}" /> + </type> + </element> + <element id="Extension.extension:download-speed.url"> + <path value="Extension.extension.url"/> + <fixedUri value="download-speed"/> + </element> + <element id="Extension.extension:download-speed.value[x]"> + <path value="Extension.extension.value[x]"/> + <max value="0"/> + </element> + <element id="Extension.extension:upload-speed"> + <path value="Extension.extension"/> + <sliceName value="upload-speed"/> + <min value="0"/> + <max value="1"/> + </element> + <element id="Extension.extension:upload-speed.extension"> + <path value="Extension.extension.extension" /> + <slicing> + <discriminator> + <type value="value" /> + <path value="url" /> + </discriminator> + <rules value="open" /> + </slicing> + <min value="1"/> + <max value="1"/> + </element> + <element id="Extension.extension:upload-speed.extension:network-speed-extension"> + <path value="Extension.extension.extension" /> + <sliceName value="network-speed-extension" /> + <min value="1" /> + <type> + <code value="Extension" /> + <profile value="http://dsf.dev/fhir/StructureDefinition/extension-network-speed|#{version}" /> + </type> + </element> + <element id="Extension.extension:upload-speed.url"> + <path value="Extension.extension.url"/> + <fixedUri value="upload-speed"/> + </element> + <element id="Extension.extension:upload-speed.value[x]"> + <path value="Extension.extension.value[x]"/> + <max value="0"/> + </element> <element id="Extension.url"> <path value="Extension.url"/> - <fixedUri value="http://dsf.dev/fhir/StructureDefinition/extension-ping-status"/> + <fixedUri value="http://dsf.dev/fhir/StructureDefinition/extension-ping-status-v2|#{version}"/> </element> <element id="Extension.value[x]"> <path value="Extension.value[x]"/> diff --git a/src/main/resources/fhir/StructureDefinition/dsf-task-cleanup-pong.xml b/src/main/resources/fhir/StructureDefinition/dsf-task-cleanup-pong.xml new file mode 100644 index 00000000..a021340a --- /dev/null +++ b/src/main/resources/fhir/StructureDefinition/dsf-task-cleanup-pong.xml @@ -0,0 +1,136 @@ +<StructureDefinition xmlns="http://hl7.org/fhir"> + <meta> + <tag> + <system value="http://dsf.dev/fhir/CodeSystem/read-access-tag" /> + <code value="ALL" /> + </tag> + </meta> + <url value="http://dsf.dev/fhir/StructureDefinition/task-cleanup-pong" /> + <!-- version managed by bpe --> + <version value="#{version}" /> + <name value="TaskCleanupPong" /> + <!-- status managed by bpe --> + <status value="unknown" /> + <experimental value="false" /> + <!-- date managed by bpe --> + <date value="#{date}" /> + <fhirVersion value="4.0.1" /> + <kind value="resource" /> + <abstract value="false" /> + <type value="Task" /> + <baseDefinition value="http://dsf.dev/fhir/StructureDefinition/task-base" /> + <derivation value="constraint" /> + <differential> + <element id="Task.instantiatesCanonical"> + <path value="Task.instantiatesCanonical" /> + <fixedCanonical value="http://dsf.dev/bpe/Process/pong|#{version}" /> + </element> + <element id="Task.input"> + <extension url="http://hl7.org/fhir/StructureDefinition/structuredefinition-explicit-type-name"> + <valueString value="Parameter" /> + </extension> + <path value="Task.input" /> + <min value="2" /> + <max value="4" /> + </element> + <element id="Task.input:message-name"> + <extension url="http://hl7.org/fhir/StructureDefinition/structuredefinition-explicit-type-name"> + <valueString value="Parameter" /> + </extension> + <path value="Task.input" /> + <sliceName value="message-name" /> + </element> + <element id="Task.input:message-name.value[x]"> + <path value="Task.input.value[x]" /> + <fixedString value="cleanupPong" /> + </element> + <element id="Task.input:business-key"> + <extension url="http://hl7.org/fhir/StructureDefinition/structuredefinition-explicit-type-name"> + <valueString value="Parameter" /> + </extension> + <path value="Task.input" /> + <sliceName value="business-key" /> + <min value="1" /> + </element> + <element id="Task.input:correlation-key"> + <extension url="http://hl7.org/fhir/StructureDefinition/structuredefinition-explicit-type-name"> + <valueString value="Parameter" /> + </extension> + <path value="Task.input" /> + <sliceName value="correlation-key" /> + <max value="0"/> + </element> + <element id="Task.input:downloaded-bytes"> + <path value="Task.input"/> + <sliceName value="downloaded-bytes"/> + <min value="0"/> + <max value="1"/> + </element> + <element id="Task.input:downloaded-bytes.type"> + <path value="Task.input.type"/> + <min value="1"/> + <max value="1"/> + </element> + <element id="Task.input:downloaded-bytes.type.coding"> + <path value="Task.input.type.coding"/> + <min value="1"/> + <max value="1"/> + </element> + <element id="Task.input:downloaded-bytes.type.coding.system"> + <path value="Task.input.type.coding.system"/> + <min value="1"/> + <max value="1"/> + <fixedUri value="http://dsf.dev/fhir/CodeSystem/ping-v2"/> + </element> + <element id="Task.input:downloaded-bytes.type.coding.code"> + <path value="Task.input.type.coding.code"/> + <min value="1"/> + <max value="1"/> + <fixedCode value="downloaded-bytes"/> + </element> + <element id="Task.input:downloaded-bytes.value[x]"> + <path value="Task.input.value[x]"/> + <min value="1"/> + <max value="1"/> + <type> + <code value="integer"/> + </type> + </element> + <element id="Task.input:downloaded-duration-millis"> + <path value="Task.input"/> + <sliceName value="downloaded-duration-millis"/> + <min value="0"/> + <max value="1"/> + </element> + <element id="Task.input:downloaded-duration-millis.type"> + <path value="Task.input.type"/> + <min value="1"/> + <max value="1"/> + </element> + <element id="Task.input:downloaded-duration-millis.type.coding"> + <path value="Task.input.type.coding"/> + <min value="1"/> + <max value="1"/> + </element> + <element id="Task.input:downloaded-duration-millis.type.coding.system"> + <path value="Task.input.type.coding.system"/> + <min value="1"/> + <max value="1"/> + <fixedUri value="http://dsf.dev/fhir/CodeSystem/ping-v2"/> + </element> + <element id="Task.input:downloaded-duration-millis.type.coding.code"> + <path value="Task.input.type.coding.code"/> + <min value="1"/> + <max value="1"/> + <fixedCode value="downloaded-duration-millis"/> + </element> + <element id="Task.input:downloaded-duration-millis.value[x]"> + <path value="Task.input.value[x]"/> + <min value="1"/> + <max value="1"/> + <type> + <code value="decimal"/> + </type> + </element> + </differential> +</StructureDefinition> \ No newline at end of file diff --git a/src/main/resources/fhir/StructureDefinition/dsf-task-ping.xml b/src/main/resources/fhir/StructureDefinition/dsf-task-ping.xml index 89346167..5acfec2e 100644 --- a/src/main/resources/fhir/StructureDefinition/dsf-task-ping.xml +++ b/src/main/resources/fhir/StructureDefinition/dsf-task-ping.xml @@ -31,7 +31,7 @@ </extension> <path value="Task.input" /> <min value="4" /> - <max value="4" /> + <max value="6" /> </element> <element id="Task.input:message-name"> <extension url="http://hl7.org/fhir/StructureDefinition/structuredefinition-explicit-type-name"> @@ -70,7 +70,7 @@ <path value="Task.input.type" /> <binding> <strength value="required" /> - <valueSet value="http://dsf.dev/fhir/ValueSet/ping|#{version}" /> + <valueSet value="http://dsf.dev/fhir/ValueSet/ping-v2|#{version}" /> </binding> </element> <element id="Task.input:endpoint-identifier.type.coding"> @@ -81,7 +81,7 @@ <element id="Task.input:endpoint-identifier.type.coding.system"> <path value="Task.input.type.coding.system" /> <min value="1" /> - <fixedUri value="http://dsf.dev/fhir/CodeSystem/ping" /> + <fixedUri value="http://dsf.dev/fhir/CodeSystem/ping-v2" /> </element> <element id="Task.input:endpoint-identifier.type.coding.code"> <path value="Task.input.type.coding.code" /> @@ -117,6 +117,98 @@ <path value="Task.input.value[x].identifier.value" /> <min value="1" /> </element> + <element id="Task.input:download-resource-size-bytes"> + <path value="Task.input"/> + <sliceName value="download-resource-size-bytes"/> + <min value="0"/> + <max value="1"/> + </element> + <element id="Task.input:download-resource-size-bytes.type"> + <path value="Task.input.type"/> + <min value="1"/> + <max value="1"/> + <binding> + <strength value="required"/> + <valueSet value="http://dsf.dev/fhir/ValueSet/ping-v2"/> + </binding> + </element> + <element id="Task.input:download-resource-size-bytes.type.coding"> + <path value="Task.input.type.coding"/> + <min value="1"/> + <max value="1"/> + </element> + <element id="Task.input:download-resource-size-bytes.type.coding.system"> + <path value="Task.input.type.coding.system"/> + <min value="1"/> + <max value="1"/> + <fixedUri value="http://dsf.dev/fhir/CodeSystem/ping-v2"/> + </element> + <element id="Task.input:download-resource-size-bytes.type.coding.code"> + <path value="Task.input.type.coding.code"/> + <min value="1"/> + <max value="1"/> + <fixedCode value="download-resource-size-bytes"/> + </element> + <element id="Task.input:download-resource-size-bytes.value[x]"> + <path value="Task.input.value[x]"/> + <min value="1"/> + <max value="1"/> + <type> + <code value="integer"/> + </type> + </element> + <element id="Task.input:download-resource-reference"> + <path value="Task.input"/> + <sliceName value="download-resource-reference"/> + <min value="0"/> + <max value="1"/> + </element> + <element id="Task.input:download-resource-reference.type"> + <path value="Task.input.type"/> + <binding> + <strength value="required"/> + <valueSet value="http://dsf.dev/fhir/ValueSet/ping-v2|#{version}"/> + </binding> + </element> + <element id="Task.input:download-resource-reference.type.coding"> + <path value="Task.input.type.coding"/> + <min value="1"/> + <max value="1"/> + </element> + <element id="Task.input:download-resource-reference.type.coding.system"> + <path value="Task.input.type.coding.system"/> + <min value="1"/> + <max value="1"/> + <fixedUri value="http://dsf.dev/fhir/CodeSystem/ping-v2"/> + </element> + <element id="Task.input:download-resource-reference.type.coding.code"> + <path value="Task.input.type.coding.code"/> + <min value="1"/> + <max value="1"/> + <fixedCode value="download-resource-reference"/> + </element> + <element id="Task.input:download-resource-reference.value[x]"> + <path value="Task.input.value[x]"/> + <type> + <code value="Reference" /> + <targetProfile value="http://hl7.org/fhir/StructureDefinition/Binary" /> + </type> + </element> + <element id="Task.input:download-resource-reference.value[x].reference"> + <path value="Task.input.value[x].reference"/> + <min value="1"/> + <max value="1"/> + </element> + <element id="Task.input:download-resource-reference.value[x].type"> + <path value="Task.input.value[x].type"/> + <min value="1"/> + <max value="1"/> + <fixedUri value="Binary"/> + </element> + <element id="Task.input:download-resource-reference.value[x].identifier"> + <path value="Task.input.value[x].identifier"/> + <max value="0"/> + </element> <element id="Task.output:pong-status"> <path value="Task.output"/> <sliceName value="pong-status"/> @@ -137,14 +229,14 @@ <min value="1" /> <type> <code value="Extension" /> - <profile value="http://dsf.dev/fhir/StructureDefinition/extension-ping-status|#{version}" /> + <profile value="http://dsf.dev/fhir/StructureDefinition/extension-ping-status-v2|#{version}" /> </type> </element> <element id="Task.output:pong-status.type"> <path value="Task.output.type"/> <binding> <strength value="required"/> - <valueSet value="http://dsf.dev/fhir/ValueSet/ping|#{version}"/> + <valueSet value="http://dsf.dev/fhir/ValueSet/ping-v2|#{version}"/> </binding> </element> <element id="Task.output:pong-status.type.coding"> @@ -155,7 +247,7 @@ <element id="Task.output:pong-status.type.coding.system"> <path value="Task.output.type.coding.system"/> <min value="1"/> - <fixedUri value="http://dsf.dev/fhir/CodeSystem/ping"/> + <fixedUri value="http://dsf.dev/fhir/CodeSystem/ping-v2"/> </element> <element id="Task.output:pong-status.type.coding.code"> <path value="Task.output.type.coding.code"/> @@ -171,14 +263,14 @@ <element id="Task.output:pong-status.value[x].system"> <path value="Task.output.value[x].system"/> <min value="1"/> - <fixedUri value="http://dsf.dev/fhir/CodeSystem/ping-status"/> + <fixedUri value="http://dsf.dev/fhir/CodeSystem/ping-status-v2"/> </element> <element id="Task.output:pong-status.value[x].code"> <path value="Task.output.value[x].code"/> <min value="1"/> <binding> <strength value="required"/> - <valueSet value="http://dsf.dev/fhir/ValueSet/pong-status|#{version}"/> + <valueSet value="http://dsf.dev/fhir/ValueSet/pong-status-v2|#{version}"/> </binding> </element> </differential> diff --git a/src/main/resources/fhir/StructureDefinition/dsf-task-pong.xml b/src/main/resources/fhir/StructureDefinition/dsf-task-pong.xml index 225c13bb..50e1915e 100644 --- a/src/main/resources/fhir/StructureDefinition/dsf-task-pong.xml +++ b/src/main/resources/fhir/StructureDefinition/dsf-task-pong.xml @@ -31,7 +31,6 @@ </extension> <path value="Task.input" /> <min value="3" /> - <max value="3" /> </element> <element id="Task.input:message-name"> <extension url="http://hl7.org/fhir/StructureDefinition/structuredefinition-explicit-type-name"> @@ -60,5 +59,164 @@ <sliceName value="correlation-key" /> <min value="1" /> </element> + <element id="Task.input:download-resource-reference"> + <path value="Task.input"/> + <sliceName value="download-resource-reference"/> + <min value="0"/> + <max value="1"/> + </element> + <element id="Task.input:download-resource-reference.type"> + <path value="Task.input.type"/> + <binding> + <strength value="required"/> + <valueSet value="http://dsf.dev/fhir/ValueSet/ping-v2|#{version}"/> + </binding> + </element> + <element id="Task.input:download-resource-reference.type.coding"> + <path value="Task.input.type.coding"/> + <min value="1"/> + <max value="1"/> + </element> + <element id="Task.input:download-resource-reference.type.coding.system"> + <path value="Task.input.type.coding.system"/> + <min value="1"/> + <max value="1"/> + <fixedUri value="http://dsf.dev/fhir/CodeSystem/ping-v2"/> + </element> + <element id="Task.input:download-resource-reference.type.coding.code"> + <path value="Task.input.type.coding.code"/> + <min value="1"/> + <max value="1"/> + <fixedCode value="download-resource-reference"/> + </element> + <element id="Task.input:download-resource-reference.value[x]"> + <path value="Task.input.value[x]"/> + <type> + <code value="Reference" /> + <targetProfile value="http://hl7.org/fhir/StructureDefinition/Binary" /> + </type> + </element> + <element id="Task.input:download-resource-reference.value[x].reference"> + <path value="Task.input.value[x].reference"/> + <min value="1"/> + <max value="1"/> + </element> + <element id="Task.input:download-resource-reference.value[x].type"> + <path value="Task.input.value[x].type"/> + <min value="1"/> + <max value="1"/> + <fixedUri value="Binary"/> + </element> + <element id="Task.input:download-resource-reference.value[x].identifier"> + <path value="Task.input.value[x].identifier"/> + <max value="0"/> + </element> + <element id="Task.input:downloaded-bytes"> + <path value="Task.input"/> + <sliceName value="downloaded-bytes"/> + <min value="0"/> + <max value="1"/> + </element> + <element id="Task.input:downloaded-bytes.type"> + <path value="Task.input.type"/> + <min value="1"/> + <max value="1"/> + </element> + <element id="Task.input:downloaded-bytes.type.coding"> + <path value="Task.input.type.coding"/> + <min value="1"/> + <max value="1"/> + </element> + <element id="Task.input:downloaded-bytes.type.coding.system"> + <path value="Task.input.type.coding.system"/> + <min value="1"/> + <max value="1"/> + <fixedUri value="http://dsf.dev/fhir/CodeSystem/ping-v2"/> + </element> + <element id="Task.input:downloaded-bytes.type.coding.code"> + <path value="Task.input.type.coding.code"/> + <min value="1"/> + <max value="1"/> + <fixedCode value="downloaded-bytes"/> + </element> + <element id="Task.input:downloaded-bytes.value[x]"> + <path value="Task.input.value[x]"/> + <min value="1"/> + <max value="1"/> + <type> + <code value="integer"/> + </type> + </element> + <element id="Task.input:downloaded-duration-millis"> + <path value="Task.input"/> + <sliceName value="downloaded-duration-millis"/> + <min value="0"/> + <max value="1"/> + </element> + <element id="Task.input:downloaded-duration-millis.type"> + <path value="Task.input.type"/> + <min value="1"/> + <max value="1"/> + </element> + <element id="Task.input:downloaded-duration-millis.type.coding"> + <path value="Task.input.type.coding"/> + <min value="1"/> + <max value="1"/> + </element> + <element id="Task.input:downloaded-duration-millis.type.coding.system"> + <path value="Task.input.type.coding.system"/> + <min value="1"/> + <max value="1"/> + <fixedUri value="http://dsf.dev/fhir/CodeSystem/ping-v2"/> + </element> + <element id="Task.input:downloaded-duration-millis.type.coding.code"> + <path value="Task.input.type.coding.code"/> + <min value="1"/> + <max value="1"/> + <fixedCode value="downloaded-duration-millis"/> + </element> + <element id="Task.input:downloaded-duration-millis.value[x]"> + <path value="Task.input.value[x]"/> + <min value="1"/> + <max value="1"/> + <type> + <code value="decimal"/> + </type> + </element> + <element id="Task.input:error-message"> + <path value="Task.input"/> + <sliceName value="error-message"/> + <min value="0"/> + </element> + <element id="Task.input:error-message.type"> + <path value="Task.input.type"/> + <min value="1"/> + <max value="1"/> + </element> + <element id="Task.input:error-message.type.coding"> + <path value="Task.input.type.coding"/> + <min value="1"/> + <max value="1"/> + </element> + <element id="Task.input:error-message.type.coding.system"> + <path value="Task.input.type.coding.system"/> + <min value="1"/> + <max value="1"/> + <fixedUri value="http://dsf.dev/fhir/CodeSystem/ping-v2"/> + </element> + <element id="Task.input:error-message.type.coding.code"> + <path value="Task.input.type.coding.code"/> + <min value="1"/> + <max value="1"/> + <fixedCode value="error-message"/> + </element> + <element id="Task.input:error-message.value[x]"> + <path value="Task.input.value[x]"/> + <min value="1"/> + <max value="1"/> + <type> + <code value="string"/> + </type> + </element> </differential> </StructureDefinition> \ No newline at end of file diff --git a/src/main/resources/fhir/StructureDefinition/dsf-task-start-ping-autostart.xml b/src/main/resources/fhir/StructureDefinition/dsf-task-start-ping-autostart.xml index 0683d5a6..969e6229 100644 --- a/src/main/resources/fhir/StructureDefinition/dsf-task-start-ping-autostart.xml +++ b/src/main/resources/fhir/StructureDefinition/dsf-task-start-ping-autostart.xml @@ -30,8 +30,8 @@ <valueString value="Parameter" /> </extension> <path value="Task.input" /> - <min value="1" /> - <max value="4" /> + <min value="2" /> + <max value="5" /> </element> <element id="Task.input:message-name"> <extension url="http://hl7.org/fhir/StructureDefinition/structuredefinition-explicit-type-name"> @@ -68,7 +68,7 @@ <valueString value="TaskInputParameterType" /> </extension> <strength value="required" /> - <valueSet value="http://dsf.dev/fhir/ValueSet/ping|#{version}" /> + <valueSet value="http://dsf.dev/fhir/ValueSet/ping-v2|#{version}" /> </binding> </element> <element id="Task.input:target-endpoints.type.coding"> @@ -79,7 +79,7 @@ <element id="Task.input:target-endpoints.type.coding.system"> <path value="Task.input.type.coding.system" /> <min value="1" /> - <fixedUri value="http://dsf.dev/fhir/CodeSystem/ping" /> + <fixedUri value="http://dsf.dev/fhir/CodeSystem/ping-v2" /> </element> <element id="Task.input:target-endpoints.type.coding.code"> <path value="Task.input.type.coding.code" /> @@ -108,7 +108,7 @@ <valueString value="TaskInputParameterType" /> </extension> <strength value="required" /> - <valueSet value="http://dsf.dev/fhir/ValueSet/ping|#{version}" /> + <valueSet value="http://dsf.dev/fhir/ValueSet/ping-v2|#{version}" /> </binding> </element> <element id="Task.input:timer-interval.type.coding"> @@ -119,7 +119,7 @@ <element id="Task.input:timer-interval.type.coding.system"> <path value="Task.input.type.coding.system" /> <min value="1" /> - <fixedUri value="http://dsf.dev/fhir/CodeSystem/ping" /> + <fixedUri value="http://dsf.dev/fhir/CodeSystem/ping-v2" /> </element> <element id="Task.input:timer-interval.type.coding.code"> <path value="Task.input.type.coding.code" /> @@ -138,5 +138,34 @@ <expression value="matches('^P(?:([0-9]+)Y)?(?:([0-9]+)M)?(?:([0-9]+)D)?(T(?:([0-9]+)H)?(?:([0-9]+)M)?(?:([0-9]+)(?:[.,]([0-9]{0,9}))?S)?)?$')" /> </constraint> </element> + <element id="Task.input:download-resource-size-bytes"> + <extension url="http://hl7.org/fhir/StructureDefinition/structuredefinition-explicit-type-name"> + <valueString value="Parameter" /> + </extension> + <path value="Task.input" /> + <sliceName value="download-resource-size-bytes" /> + <min value="1" /> + <max value="1" /> + </element> + <element id="Task.input:download-resource-size-bytes.type"> + <path value="Task.input.type" /> + <binding> + <extension url="http://hl7.org/fhir/StructureDefinition/elementdefinition-bindingName"> + <valueString value="TaskInputParameterType" /> + </extension> + <strength value="required" /> + <valueSet value="http://dsf.dev/fhir/ValueSet/ping-v2|#{version}" /> + </binding> + </element> + <element id="Task.input:download-resource-size-bytes.type.coding.system"> + <path value="Task.input.type.coding.system" /> + <min value="1" /> + <fixedUri value="http://dsf.dev/fhir/CodeSystem/ping-v2" /> + </element> + <element id="Task.input:download-resource-size-bytes.type.coding.code"> + <path value="Task.input.type.coding.code" /> + <min value="1" /> + <fixedCode value="download-resource-size-bytes" /> + </element> </differential> </StructureDefinition> \ No newline at end of file diff --git a/src/main/resources/fhir/StructureDefinition/dsf-task-start-ping.xml b/src/main/resources/fhir/StructureDefinition/dsf-task-start-ping.xml index cf4363e7..ffe47756 100644 --- a/src/main/resources/fhir/StructureDefinition/dsf-task-start-ping.xml +++ b/src/main/resources/fhir/StructureDefinition/dsf-task-start-ping.xml @@ -31,7 +31,7 @@ </extension> <path value="Task.input" /> <min value="1" /> - <max value="3" /> + <max value="4" /> </element> <element id="Task.input:message-name"> <extension url="http://hl7.org/fhir/StructureDefinition/structuredefinition-explicit-type-name"> @@ -68,7 +68,7 @@ <valueString value="TaskInputParameterType" /> </extension> <strength value="required" /> - <valueSet value="http://dsf.dev/fhir/ValueSet/ping|#{version}" /> + <valueSet value="http://dsf.dev/fhir/ValueSet/ping-v2|#{version}" /> </binding> </element> <element id="Task.input:target-endpoints.type.coding"> @@ -79,7 +79,7 @@ <element id="Task.input:target-endpoints.type.coding.system"> <path value="Task.input.type.coding.system" /> <min value="1" /> - <fixedUri value="http://dsf.dev/fhir/CodeSystem/ping" /> + <fixedUri value="http://dsf.dev/fhir/CodeSystem/ping-v2" /> </element> <element id="Task.input:target-endpoints.type.coding.code"> <path value="Task.input.type.coding.code" /> @@ -92,6 +92,70 @@ <code value="string" /> </type> </element> + <element id="Task.input:download-resource-size-bytes"> + <extension url="http://hl7.org/fhir/StructureDefinition/structuredefinition-explicit-type-name"> + <valueString value="Parameter" /> + </extension> + <path value="Task.input" /> + <sliceName value="download-resource-size-bytes" /> + <min value="0" /> + <max value="1" /> + </element> + <element id="Task.input:download-resource-size-bytes.type"> + <path value="Task.input.type" /> + <binding> + <extension url="http://hl7.org/fhir/StructureDefinition/elementdefinition-bindingName"> + <valueString value="TaskInputParameterType" /> + </extension> + <strength value="required" /> + <valueSet value="http://dsf.dev/fhir/ValueSet/ping-v2|#{version}" /> + </binding> + </element> + <element id="Task.input:download-resource-size-bytes.type.coding.system"> + <path value="Task.input.type.coding.system" /> + <min value="1" /> + <fixedUri value="http://dsf.dev/fhir/CodeSystem/ping-v2" /> + </element> + <element id="Task.input:download-resource-size-bytes.type.coding.code"> + <path value="Task.input.type.coding.code" /> + <min value="1" /> + <fixedCode value="download-resource-size-bytes" /> + </element> + <element id="Task.output:error-message"> + <path value="Task.output"/> + <sliceName value="error-message"/> + <min value="0"/> + </element> + <element id="Task.output:error-message.type"> + <path value="Task.output.type"/> + <min value="1"/> + <max value="1"/> + </element> + <element id="Task.output:error-message.type.coding"> + <path value="Task.output.type.coding" /> + <min value="1"/> + <max value="1"/> + </element> + <element id="Task.output:error-message.type.coding.system"> + <path value="Task.output.type.coding.system"/> + <min value="1"/> + <max value="1"/> + <fixedUri value="http://dsf.dev/fhir/CodeSystem/ping-v2" /> + </element> + <element id="Task.output:error-message.type.coding.code"> + <path value="Task.output.type.coding.code"/> + <min value="1"/> + <max value="1"/> + <fixedCode value="error-message" /> + </element> + <element id="Task.output:error-message.value[x]"> + <path value="Task.output.value[x]"/> + <min value="1"/> + <max value="1"/> + <type> + <code value="string"/> + </type> + </element> <element id="Task.output:ping-status"> <path value="Task.output"/> <sliceName value="ping-status"/> @@ -112,14 +176,14 @@ <min value="1" /> <type> <code value="Extension" /> - <profile value="http://dsf.dev/fhir/StructureDefinition/extension-ping-status|#{version}" /> + <profile value="http://dsf.dev/fhir/StructureDefinition/extension-ping-status-v2|#{version}" /> </type> </element> <element id="Task.output:ping-status.type"> <path value="Task.output.type"/> <binding> <strength value="required"/> - <valueSet value="http://dsf.dev/fhir/ValueSet/ping|#{version}"/> + <valueSet value="http://dsf.dev/fhir/ValueSet/ping-v2|#{version}"/> </binding> </element> <element id="Task.output:ping-status.type.coding"> @@ -130,7 +194,7 @@ <element id="Task.output:ping-status.type.coding.system"> <path value="Task.output.type.coding.system"/> <min value="1"/> - <fixedUri value="http://dsf.dev/fhir/CodeSystem/ping"/> + <fixedUri value="http://dsf.dev/fhir/CodeSystem/ping-v2"/> </element> <element id="Task.output:ping-status.type.coding.code"> <path value="Task.output.type.coding.code"/> @@ -146,14 +210,14 @@ <element id="Task.output:ping-status.value[x].system"> <path value="Task.output.value[x].system"/> <min value="1"/> - <fixedUri value="http://dsf.dev/fhir/CodeSystem/ping-status"/> + <fixedUri value="http://dsf.dev/fhir/CodeSystem/ping-status-v2"/> </element> <element id="Task.output:ping-status.value[x].code"> <path value="Task.output.value[x].code"/> <min value="1"/> <binding> <strength value="required"/> - <valueSet value="http://dsf.dev/fhir/ValueSet/ping-status|#{version}"/> + <valueSet value="http://dsf.dev/fhir/ValueSet/ping-status-v2|#{version}"/> </binding> </element> </differential> diff --git a/src/main/resources/fhir/Task/dsf-task-start-ping-autostart.xml b/src/main/resources/fhir/Task/dsf-task-start-ping-autostart.xml index 60d7e8f5..0cff04de 100644 --- a/src/main/resources/fhir/Task/dsf-task-start-ping-autostart.xml +++ b/src/main/resources/fhir/Task/dsf-task-start-ping-autostart.xml @@ -38,19 +38,28 @@ <input> <type> <coding> - <system value="http://dsf.dev/fhir/CodeSystem/ping"></system> - <code value="target-endpoints"></code> + <system value="http://dsf.dev/fhir/CodeSystem/ping-v2"/> + <code value="target-endpoints"/> </coding> </type> - <valueString value="Endpoint?status=active&identifier=http://dsf.dev/sid/endpoint-identifier|"></valueString> + <valueString value="Endpoint?status=active&identifier=http://dsf.dev/sid/endpoint-identifier|"/> </input> <input> <type> <coding> - <system value="http://dsf.dev/fhir/CodeSystem/ping"></system> - <code value="timer-interval"></code> + <system value="http://dsf.dev/fhir/CodeSystem/ping-v2"/> + <code value="timer-interval"/> </coding> </type> - <valueString value="PT24H"></valueString> + <valueString value="PT24H"/> </input> + <input> + <type> + <coding> + <system value="http://dsf.dev/fhir/CodeSystem/ping-v2"/> + <code value="download-resource-size-bytes"/> + </coding> + </type> + <valueInteger value="10000000"/> + </input> </Task> diff --git a/src/main/resources/fhir/Task/dsf-task-start-ping.xml b/src/main/resources/fhir/Task/dsf-task-start-ping.xml index d8ef908b..29011e26 100644 --- a/src/main/resources/fhir/Task/dsf-task-start-ping.xml +++ b/src/main/resources/fhir/Task/dsf-task-start-ping.xml @@ -35,13 +35,22 @@ </type> <valueString value="startPing"/> </input> + <input> + <type> + <coding> + <system value="http://dsf.dev/fhir/CodeSystem/ping-v2"/> + <code value="download-resource-size-bytes" /> + </coding> + </type> + <valueInteger value="1000000"/> + </input> <input> <type> <coding> - <system value="http://dsf.dev/fhir/CodeSystem/ping"></system> - <code value="target-endpoints"></code> + <system value="http://dsf.dev/fhir/CodeSystem/ping-v2"/> + <code value="target-endpoints"/> </coding> </type> - <valueString value="Endpoint?status=active&identifier=http://dsf.dev/sid/endpoint-identifier|"></valueString> + <valueString value="Endpoint?status=active&identifier=http://dsf.dev/sid/endpoint-identifier|"/> </input> </Task> diff --git a/src/main/resources/fhir/ValueSet/dsf-ping-status.xml b/src/main/resources/fhir/ValueSet/dsf-ping-status.xml index 0729a89a..9bed9fec 100644 --- a/src/main/resources/fhir/ValueSet/dsf-ping-status.xml +++ b/src/main/resources/fhir/ValueSet/dsf-ping-status.xml @@ -6,7 +6,7 @@ <code value="ALL" /> </tag> </meta> - <url value="http://dsf.dev/fhir/ValueSet/ping-status" /> + <url value="http://dsf.dev/fhir/ValueSet/ping-status-v2" /> <!-- version managed by bpe --> <version value="#{version}" /> <name value="DSF_Ping_Status" /> @@ -21,7 +21,7 @@ <immutable value="true" /> <compose> <include> - <system value="http://dsf.dev/fhir/CodeSystem/ping-status" /> + <system value="http://dsf.dev/fhir/CodeSystem/ping-status-v2" /> <version value="#{version}" /> <concept> <code value="not-allowed" /> diff --git a/src/main/resources/fhir/ValueSet/dsf-ping-units.xml b/src/main/resources/fhir/ValueSet/dsf-ping-units.xml new file mode 100644 index 00000000..fa619072 --- /dev/null +++ b/src/main/resources/fhir/ValueSet/dsf-ping-units.xml @@ -0,0 +1,28 @@ +<ValueSet xmlns="http://hl7.org/fhir"> + <meta> + <tag> + <system + value="http://dsf.dev/fhir/CodeSystem/read-access-tag" /> + <code value="ALL" /> + </tag> + </meta> + <url value="http://dsf.dev/fhir/ValueSet/ping-units-v2" /> + <!-- version managed by bpe --> + <version value="#{version}" /> + <name value="DSF_Ping_Units" /> + <title value="DSF Ping Units" /> + <!-- status managed by bpe --> + <status value="unknown" /> + <experimental value="false" /> + <!-- date managed by bpe --> + <date value="#{date}" /> + <publisher value="DSF" /> + <description value="ValueSet with ping download speed units" /> + <immutable value="true" /> + <compose> + <include> + <system value="http://dsf.dev/fhir/CodeSystem/ping-units-v2" /> + <version value="#{version}" /> + </include> + </compose> +</ValueSet> \ No newline at end of file diff --git a/src/main/resources/fhir/ValueSet/dsf-ping.xml b/src/main/resources/fhir/ValueSet/dsf-ping.xml index 0fef6dbf..4984f57a 100644 --- a/src/main/resources/fhir/ValueSet/dsf-ping.xml +++ b/src/main/resources/fhir/ValueSet/dsf-ping.xml @@ -5,7 +5,7 @@ <code value="ALL" /> </tag> </meta> - <url value="http://dsf.dev/fhir/ValueSet/ping" /> + <url value="http://dsf.dev/fhir/ValueSet/ping-v2" /> <!-- version managed by bpe --> <version value="#{version}" /> <name value="DSF_Ping" /> @@ -20,7 +20,7 @@ <immutable value="true" /> <compose> <include> - <system value="http://dsf.dev/fhir/CodeSystem/ping" /> + <system value="http://dsf.dev/fhir/CodeSystem/ping-v2" /> <version value="#{version}" /> </include> </compose> diff --git a/src/main/resources/fhir/ValueSet/dsf-pong-status.xml b/src/main/resources/fhir/ValueSet/dsf-pong-status.xml index 09860584..97c471b7 100644 --- a/src/main/resources/fhir/ValueSet/dsf-pong-status.xml +++ b/src/main/resources/fhir/ValueSet/dsf-pong-status.xml @@ -5,7 +5,7 @@ <code value="ALL" /> </tag> </meta> - <url value="http://dsf.dev/fhir/ValueSet/pong-status" /> + <url value="http://dsf.dev/fhir/ValueSet/pong-status-v2" /> <!-- version managed by bpe --> <version value="#{version}" /> <name value="DSF_Pong_Status" /> @@ -20,7 +20,7 @@ <immutable value="true" /> <compose> <include> - <system value="http://dsf.dev/fhir/CodeSystem/ping-status" /> + <system value="http://dsf.dev/fhir/CodeSystem/ping-status-v2" /> <version value="#{version}" /> <concept> <code value="not-allowed" /> @@ -31,8 +31,12 @@ <display value="Not reachable" /> </concept> <concept> - <code value="pong-send" /> - <display value="Pong send" /> + <code value="pong-sent" /> + <display value="Pong sent" /> + </concept> + <concept> + <code value="resource-downloaded"/> + <display value="Resource downloaded"/> </concept> </include> </compose> diff --git a/src/test/java/dev/dsf/bpe/PingProcessPluginDefinitionTest.java b/src/test/java/dev/dsf/bpe/PingProcessPluginDefinitionTest.java index 978fd13e..55e289ab 100644 --- a/src/test/java/dev/dsf/bpe/PingProcessPluginDefinitionTest.java +++ b/src/test/java/dev/dsf/bpe/PingProcessPluginDefinitionTest.java @@ -20,7 +20,7 @@ public void testResourceLoading() throws Exception var ping = resourcesByProcessId.get(ConstantsPing.PROCESS_NAME_FULL_PING); assertNotNull(ping); - assertEquals(9, ping.stream().filter(this::exists).count()); + assertEquals(13, ping.stream().filter(this::exists).count()); var pingAutostart = resourcesByProcessId.get(ConstantsPing.PROCESS_NAME_FULL_PING_AUTOSTART); assertNotNull(pingAutostart); @@ -28,7 +28,7 @@ public void testResourceLoading() throws Exception var pong = resourcesByProcessId.get(ConstantsPing.PROCESS_NAME_FULL_PONG); assertNotNull(pong); - assertEquals(7, pong.stream().filter(this::exists).count()); + assertEquals(10, pong.stream().filter(this::exists).count()); } private boolean exists(String file) diff --git a/src/test/java/dev/dsf/fhir/profiles/TaskProfileTest.java b/src/test/java/dev/dsf/fhir/profiles/TaskProfileTest.java index 75c355aa..90c383a9 100644 --- a/src/test/java/dev/dsf/fhir/profiles/TaskProfileTest.java +++ b/src/test/java/dev/dsf/fhir/profiles/TaskProfileTest.java @@ -2,10 +2,19 @@ import static org.junit.Assert.assertEquals; +import java.io.IOException; +import java.io.InputStream; +import java.math.BigDecimal; +import java.time.Instant; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; import java.util.Arrays; import java.util.Date; +import java.util.List; +import java.util.TimeZone; import java.util.UUID; +import org.hl7.fhir.r4.model.IntegerType; import org.hl7.fhir.r4.model.Reference; import org.hl7.fhir.r4.model.ResourceType; import org.hl7.fhir.r4.model.StringType; @@ -17,11 +26,17 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.parser.IParser; import ca.uhn.fhir.validation.ResultSeverityEnum; import ca.uhn.fhir.validation.ValidationResult; import dev.dsf.bpe.ConstantsPing; import dev.dsf.bpe.PingProcessPluginDefinition; -import dev.dsf.bpe.util.PingStatusGenerator; +import dev.dsf.bpe.util.task.input.generator.DownloadResourceReferenceGenerator; +import dev.dsf.bpe.util.task.input.generator.DownloadResourceSizeGenerator; +import dev.dsf.bpe.util.task.input.generator.ErrorMessageGenerator; +import dev.dsf.bpe.util.task.input.generator.NetworkSpeedMetricGenerator; +import dev.dsf.bpe.util.task.output.generator.PingStatusGenerator; import dev.dsf.bpe.v1.constants.CodeSystems.BpmnMessage; import dev.dsf.bpe.v1.constants.NamingSystems.EndpointIdentifier; import dev.dsf.bpe.v1.constants.NamingSystems.OrganizationIdentifier; @@ -39,13 +54,15 @@ public class TaskProfileTest @ClassRule public static final ValidationSupportRule validationRule = new ValidationSupportRule(def.getResourceVersion(), def.getResourceReleaseDate(), - Arrays.asList("dsf-task-base-1.0.0.xml", "dsf-extension-ping-status.xml", "dsf-task-ping.xml", - "dsf-task-pong.xml", "dsf-task-start-ping.xml", "dsf-task-start-ping-autostart.xml", - "dsf-task-stop-ping-autostart.xml"), - Arrays.asList("dsf-read-access-tag-1.0.0.xml", "dsf-bpmn-message-1.0.0.xml", "dsf-ping.xml", - "dsf-ping-status.xml"), - Arrays.asList("dsf-read-access-tag-1.0.0.xml", "dsf-bpmn-message-1.0.0.xml", "dsf-ping.xml", - "dsf-ping-status.xml", "dsf-pong-status.xml")); + Arrays.asList("dsf-task-base-1.0.0.xml", "dsf-extension-network-speed.xml", "dsf-extension-ping-status.xml", + "dsf-extension-ping-status-1_0.xml", "dsf-task-ping.xml", "dsf-task-pong.xml", + "dsf-task-start-ping.xml", "dsf-task-start-ping-autostart.xml", "dsf-task-stop-ping-autostart.xml", + "dsf-task-cleanup-pong.xml"), + Arrays.asList("dsf-read-access-tag-1.0.0.xml", "dsf-bpmn-message-1.0.0.xml", "dsf-ping-1_0.xml", + "dsf-ping.xml", "dsf-ping-status-1_0.xml", "dsf-ping-status.xml", "dsf-ping-units.xml"), + Arrays.asList("dsf-read-access-tag-1.0.0.xml", "dsf-bpmn-message-1.0.0.xml", "dsf-ping-1_0.xml", + "dsf-ping.xml", "dsf-ping-status-1_0.xml", "dsf-ping-status.xml", "dsf-pong-status-1_0.xml", + "dsf-pong-status.xml", "dsf-ping-units.xml")); private ResourceValidator resourceValidator = new ResourceValidatorImpl(validationRule.getFhirContext(), validationRule.getValidationSupport()); @@ -126,6 +143,8 @@ private Task createValidTaskStartAutostartProcess() task.addInput().setValue(new StringType(ConstantsPing.PROFILE_DSF_TASK_START_PING_AUTOSTART_MESSAGE_NAME)) .getType().addCoding(BpmnMessage.messageName()); + task.addInput(DownloadResourceSizeGenerator.create(0)); + return task; } @@ -173,6 +192,21 @@ public void testTaskStartPingProcessProfileValid() throws Exception || ResultSeverityEnum.FATAL.equals(m.getSeverity())).count()); } + @Test + public void testTaskStartPingProcessProfileValidWithErrorMessages() throws Exception + { + Task task = createValidTaskStartPingProcess(); + + dev.dsf.bpe.util.task.output.generator.ErrorMessageGenerator.create(List.of("Foo", "Bar")) + .forEach(task::addOutput); + + ValidationResult result = resourceValidator.validate(task); + ValidationSupportRule.logValidationMessages(logger, result); + + assertEquals(0, result.getMessages().stream().filter(m -> ResultSeverityEnum.ERROR.equals(m.getSeverity()) + || ResultSeverityEnum.FATAL.equals(m.getSeverity())).count()); + } + @Test public void testTaskStartPingProcessProfileValidWithTargetEndpoints() throws Exception { @@ -235,10 +269,55 @@ public String getCorrelationKey() }; Task task = createValidTaskStartPingProcess(); - task.addOutput().setValue(new StringType(UUID.randomUUID().toString())).getType() + task.addInput().setValue(new StringType(UUID.randomUUID().toString())).getType() .addCoding(BpmnMessage.businessKey()); - task.addOutput(new PingStatusGenerator().createPingStatusOutput(target, - ConstantsPing.CODESYSTEM_DSF_PING_STATUS_VALUE_NOT_REACHABLE, "some error message")); + task.addOutput(PingStatusGenerator.createPingStatusOutput(target, + ConstantsPing.CODESYSTEM_DSF_PING_STATUS_VALUE_NOT_REACHABLE, List.of("some error message"))); + + ValidationResult result = resourceValidator.validate(task); + ValidationSupportRule.logValidationMessages(logger, result); + + assertEquals(0, result.getMessages().stream().filter(m -> ResultSeverityEnum.ERROR.equals(m.getSeverity()) + || ResultSeverityEnum.FATAL.equals(m.getSeverity())).count()); + } + + @Test + public void testTaskStartPingProcessProfileValidWithBusinessKeyAndPingStatusOutputWithDownloadAndUploadSpeeds() + throws Exception + { + Target target = new Target() + { + @Override + public String getOrganizationIdentifierValue() + { + return "target.org"; + } + + @Override + public String getEndpointUrl() + { + return "https://endpoint.target.org/fhir"; + } + + @Override + public String getEndpointIdentifierValue() + { + return "endpoint.target.org"; + } + + @Override + public String getCorrelationKey() + { + return UUID.randomUUID().toString(); + } + }; + + Task task = createValidTaskStartPingProcess(); + task.addInput().setValue(new StringType(UUID.randomUUID().toString())).getType() + .addCoding(BpmnMessage.businessKey()); + task.addOutput(PingStatusGenerator.createPingStatusOutput(target, + ConstantsPing.CODESYSTEM_DSF_PING_STATUS_VALUE_PONG_RECEIVED, BigDecimal.ZERO, BigDecimal.ZERO, + ConstantsPing.CODESYSTEM_DSF_PING_UNITS_VALUE_BITS_PER_SECOND)); ValidationResult result = resourceValidator.validate(task); ValidationSupportRule.logValidationMessages(logger, result); @@ -301,6 +380,8 @@ private Task createValidTaskStartPingProcess() task.addInput().setValue(new StringType(ConstantsPing.PROFILE_DSF_TASK_START_PING_MESSAGE_NAME)).getType() .addCoding(BpmnMessage.messageName()); + task.addInput().setValue(new IntegerType(1)).getType().addCoding().setSystem(ConstantsPing.CODESYSTEM_DSF_PING) + .setCode(ConstantsPing.CODESYSTEM_DSF_PING_VALUE_DOWNLOAD_RESOURCE_SIZE_BYTES); return task; } @@ -348,7 +429,51 @@ public String getCorrelationKey() }; Task task = createValidTaskPing(); task.addOutput(new PingStatusGenerator().createPongStatusOutput(target, - ConstantsPing.CODESYSTEM_DSF_PING_STATUS_VALUE_PONG_SEND)); + ConstantsPing.CODESYSTEM_DSF_PING_STATUS_VALUE_PONG_SENT)); + + ValidationResult result = resourceValidator.validate(task); + ValidationSupportRule.logValidationMessages(logger, result); + + assertEquals(0, result.getMessages().stream().filter(m -> ResultSeverityEnum.ERROR.equals(m.getSeverity()) + || ResultSeverityEnum.FATAL.equals(m.getSeverity())).count()); + } + + @Test + public void testTaskPingValidWithPingStatusOutputAndDownloadResourceSizeAndDownloadResourceReference() + throws Exception + { + Target target = new Target() + { + @Override + public String getOrganizationIdentifierValue() + { + return "target.org"; + } + + @Override + public String getEndpointUrl() + { + return "https://endpoint.target.org/fhir"; + } + + @Override + public String getEndpointIdentifierValue() + { + return "endpoint.target.org"; + } + + @Override + public String getCorrelationKey() + { + return UUID.randomUUID().toString(); + } + }; + Task task = createValidTaskPing(); + task.addOutput(new PingStatusGenerator().createPongStatusOutput(target, + ConstantsPing.CODESYSTEM_DSF_PING_STATUS_VALUE_PONG_SENT)); + + task.addInput(DownloadResourceSizeGenerator.create(1000)); + task.addInput(DownloadResourceReferenceGenerator.create("https://test.endpoint.org/fhir/Binary")); ValidationResult result = resourceValidator.validate(task); ValidationSupportRule.logValidationMessages(logger, result); @@ -397,6 +522,52 @@ public void testTaskPongValid() throws Exception || ResultSeverityEnum.FATAL.equals(m.getSeverity())).count()); } + @Test + public void testTaskPongValidWithReferenceAndDownloadedDurationMillisAndDownloadedBytesPresent() + { + Task task = createValidTaskPong(); + + task.addInput(DownloadResourceReferenceGenerator.create("https://test.endpoint.org/fhir/Binary")); + task.addInput(NetworkSpeedMetricGenerator.createDownloadedBytes(1000)); + task.addInput(NetworkSpeedMetricGenerator.createDownloadedDurationMillis(1000)); + + ValidationResult result = resourceValidator.validate(task); + ValidationSupportRule.logValidationMessages(logger, result); + + assertEquals(0, result.getMessages().stream().filter(m -> ResultSeverityEnum.ERROR.equals(m.getSeverity()) + || ResultSeverityEnum.FATAL.equals(m.getSeverity())).count()); + } + + @Test + public void testTaskPongValidWithMultipleErrorMessages() + { + Task task = createValidTaskPong(); + + task.addInput(DownloadResourceReferenceGenerator.create("https://test.endpoint.org/fhir/Binary")); + task.addInput(NetworkSpeedMetricGenerator.createDownloadedBytes(1000)); + task.addInput(NetworkSpeedMetricGenerator.createDownloadedDurationMillis(1000)); + task.addInput(ErrorMessageGenerator.create("Something went wrong")); + task.addInput(ErrorMessageGenerator.create("Something went wrong really badly")); + + ValidationResult result = resourceValidator.validate(task); + ValidationSupportRule.logValidationMessages(logger, result); + + assertEquals(0, result.getMessages().stream().filter(m -> ResultSeverityEnum.ERROR.equals(m.getSeverity()) + || ResultSeverityEnum.FATAL.equals(m.getSeverity())).count()); + } + + @Test + public void testTaskCleanupPongValid() + { + Task task = createValidTaskCleanupPong(); + + ValidationResult result = resourceValidator.validate(task); + ValidationSupportRule.logValidationMessages(logger, result); + + assertEquals(0, result.getMessages().stream().filter(m -> ResultSeverityEnum.ERROR.equals(m.getSeverity()) + || ResultSeverityEnum.FATAL.equals(m.getSeverity())).count()); + } + private Task createValidTaskPong() { Task task = new Task(); @@ -419,4 +590,97 @@ private Task createValidTaskPong() return task; } + + private Task createValidTaskCleanupPong() + { + Task task = new Task(); + task.getMeta().addProfile(ConstantsPing.PROFILE_DSF_TASK_CLEANUP_PONG); + task.setInstantiatesCanonical( + ConstantsPing.PROFILE_DSF_TASK_CLEANUP_PONG_PROCESS_URI + "|" + def.getResourceVersion()); + task.setStatus(TaskStatus.REQUESTED); + task.setIntent(TaskIntent.ORDER); + task.setAuthoredOn(new Date()); + task.getRequester().setType(ResourceType.Organization.name()) + .setIdentifier(OrganizationIdentifier.withValue("DIC 1")); + task.getRestriction().addRecipient().setType(ResourceType.Organization.name()) + .setIdentifier(OrganizationIdentifier.withValue("TTP")); + + task.addInput().setValue(new StringType(ConstantsPing.PROFILE_DSF_TASK_CLEANUP_PONG_MESSAGE_NAME)).getType() + .addCoding(BpmnMessage.messageName()); + task.addInput().setValue(new StringType(UUID.randomUUID().toString())).getType() + .addCoding(BpmnMessage.businessKey()); + + task.addInput(NetworkSpeedMetricGenerator.createDownloadedBytes(1000)); + task.addInput(NetworkSpeedMetricGenerator.createDownloadedDurationMillis(1000)); + return task; + } + + @Test + public void testDraftTaskStartPingValid() throws IOException + { + FhirContext ctx = FhirContext.forR4(); + InputStream fileInputStream = getClass().getClassLoader() + .getResourceAsStream("fhir/Task/dsf-task-start-ping.xml"); + String xml = new String(fileInputStream.readAllBytes()); + xml = fillPlaceholders(xml); + fileInputStream.close(); + + IParser parser = ctx.newXmlParser(); + Task task = parser.parseResource(Task.class, xml); + + ValidationResult result = resourceValidator.validate(task); + ValidationSupportRule.logValidationMessages(logger, result); + + assertEquals(0, result.getMessages().stream().filter(m -> ResultSeverityEnum.ERROR.equals(m.getSeverity()) + || ResultSeverityEnum.FATAL.equals(m.getSeverity())).count()); + } + + @Test + public void testDraftTaskStartPingAutostartValid() throws IOException + { + FhirContext ctx = FhirContext.forR4(); + InputStream fileInputStream = getClass().getClassLoader() + .getResourceAsStream("fhir/Task/dsf-task-start-ping-autostart.xml"); + String xml = new String(fileInputStream.readAllBytes()); + xml = fillPlaceholders(xml); + fileInputStream.close(); + + IParser parser = ctx.newXmlParser(); + Task task = parser.parseResource(Task.class, xml); + + ValidationResult result = resourceValidator.validate(task); + ValidationSupportRule.logValidationMessages(logger, result); + + assertEquals(0, result.getMessages().stream().filter(m -> ResultSeverityEnum.ERROR.equals(m.getSeverity()) + || ResultSeverityEnum.FATAL.equals(m.getSeverity())).count()); + } + + @Test + public void testDraftTaskStopPingAutostartValid() throws IOException + { + FhirContext ctx = FhirContext.forR4(); + InputStream fileInputStream = getClass().getClassLoader() + .getResourceAsStream("fhir/Task/dsf-task-stop-ping-autostart.xml"); + String xml = new String(fileInputStream.readAllBytes()); + xml = fillPlaceholders(xml); + fileInputStream.close(); + + IParser parser = ctx.newXmlParser(); + Task task = parser.parseResource(Task.class, xml); + + ValidationResult result = resourceValidator.validate(task); + ValidationSupportRule.logValidationMessages(logger, result); + + assertEquals(0, result.getMessages().stream().filter(m -> ResultSeverityEnum.ERROR.equals(m.getSeverity()) + || ResultSeverityEnum.FATAL.equals(m.getSeverity())).count()); + } + + private String fillPlaceholders(String xml) + { + DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd"); + xml = xml.replaceAll("#\\{version}", def.getResourceVersion()); + xml = xml.replaceAll("#\\{date}", + dtf.format(LocalDate.ofInstant(Instant.now(), TimeZone.getDefault().toZoneId()))); + return xml; + } } diff --git a/src/test/java/dev/dsf/library/InputStreamTest.java b/src/test/java/dev/dsf/library/InputStreamTest.java new file mode 100644 index 00000000..8ca977ac --- /dev/null +++ b/src/test/java/dev/dsf/library/InputStreamTest.java @@ -0,0 +1,51 @@ +package dev.dsf.library; + +import static org.junit.Assert.assertThrows; + +import java.io.ByteArrayInputStream; +import java.io.EOFException; +import java.io.IOException; +import java.io.InputStream; +import java.util.Random; + +import org.junit.Test; + +public class InputStreamTest +{ + @Test + public void testSkipNBytesSuccessWhenAllSkipped() throws IOException + { + int length = 1000; + int toSkip = length; + byte[] data = randomData(length); + InputStream in = new ByteArrayInputStream(data); + in.skipNBytes(toSkip); + } + + @Test + public void testSkipNBytesSuccessWhenSomeSkipped() throws IOException + { + int length = 1000; + int toSkip = length / 2; + byte[] data = randomData(length); + InputStream in = new ByteArrayInputStream(data); + in.skipNBytes(toSkip); + } + + @Test + public void testSkipNBytesFailOnSkipTooMany() throws IOException + { + int length = 1000; + int toSkip = length + 1; + byte[] data = randomData(length); + InputStream in = new ByteArrayInputStream(data); + assertThrows(EOFException.class, () -> in.skipNBytes(toSkip)); + } + + private byte[] randomData(int length) + { + byte[] data = new byte[length]; + new Random().nextBytes(data); + return data; + } +} diff --git a/src/test/resources/fhir/CodeSystem/dsf-ping-1_0.xml b/src/test/resources/fhir/CodeSystem/dsf-ping-1_0.xml new file mode 100644 index 00000000..630c9073 --- /dev/null +++ b/src/test/resources/fhir/CodeSystem/dsf-ping-1_0.xml @@ -0,0 +1,49 @@ +<CodeSystem xmlns="http://hl7.org/fhir"> + <meta> + <tag> + <system value="http://dsf.dev/fhir/CodeSystem/read-access-tag" /> + <code value="ALL" /> + </tag> + </meta> + <url value="http://dsf.dev/fhir/CodeSystem/ping-v2" /> + <!-- version managed by bpe --> + <version value="1.0" /> + <name value="DSF_Ping" /> + <title value="DSF Ping" /> + <!-- status managed by bpe --> + <status value="unknown" /> + <experimental value="false" /> + <!-- date managed by bpe --> + <date value="#{date}" /> + <publisher value="DSF" /> + <description value="CodeSystem with standard values for the process ping/pong" /> + <caseSensitive value="true" /> + <hierarchyMeaning value="grouped-by" /> + <versionNeeded value="false" /> + <content value="complete" /> + <concept> + <code value="ping-status" /> + <display value="Ping Status" /> + <definition value="Ping status of target organization" /> + </concept> + <concept> + <code value="pong-status" /> + <display value="Pong Status" /> + <definition value="Pong status of target organization" /> + </concept> + <concept> + <code value="endpoint-identifier" /> + <display value="Endpoint Identifier" /> + <definition value="Identifier of endpoint expecting pong (response) message" /> + </concept> + <concept> + <code value="target-endpoints" /> + <display value="Target Endpoints" /> + <definition value="Ping target endpoints, search query resulting in match or include results with Endpoint resources" /> + </concept> + <concept> + <code value="timer-interval" /> + <display value="Timer Interval" /> + <definition value="Interval between two autostarts of the ping process" /> + </concept> +</CodeSystem> \ No newline at end of file diff --git a/src/test/resources/fhir/CodeSystem/dsf-ping-status-1_0.xml b/src/test/resources/fhir/CodeSystem/dsf-ping-status-1_0.xml new file mode 100644 index 00000000..62ebe1cb --- /dev/null +++ b/src/test/resources/fhir/CodeSystem/dsf-ping-status-1_0.xml @@ -0,0 +1,49 @@ +<CodeSystem xmlns="http://hl7.org/fhir"> + <meta> + <tag> + <system value="http://dsf.dev/fhir/CodeSystem/read-access-tag" /> + <code value="ALL" /> + </tag> + </meta> + <url value="http://dsf.dev/fhir/CodeSystem/ping-status-v2" /> + <!-- version managed by bpe --> + <version value="1.0" /> + <name value="DSF_Ping_Status" /> + <title value="DSF Ping Status" /> + <!-- status managed by bpe --> + <status value="unknown" /> + <experimental value="false" /> + <!-- date managed by bpe --> + <date value="#{date}" /> + <publisher value="DSF" /> + <description value="CodeSystem with ping/pong status values" /> + <caseSensitive value="true" /> + <hierarchyMeaning value="grouped-by" /> + <versionNeeded value="false" /> + <content value="complete" /> + <concept> + <code value="not-allowed" /> + <display value="Not allowed" /> + <definition value="Not allowed to start pong-process at target organization" /> + </concept> + <concept> + <code value="not-reachable" /> + <display value="Not reachable" /> + <definition value="Ping could not be sent to target organization" /> + </concept> + <concept> + <code value="pong-missing" /> + <display value="Pong missing" /> + <definition value="No pong received from target organization" /> + </concept> + <concept> + <code value="pong-received" /> + <display value="Pong received" /> + <definition value="Pong received from target organization" /> + </concept> + <concept> + <code value="pong-send" /> + <display value="Pong send" /> + <definition value="Pong successfully sent to target organization" /> + </concept> +</CodeSystem> \ No newline at end of file diff --git a/src/test/resources/fhir/StructureDefinition/dsf-extension-ping-status-1_0.xml b/src/test/resources/fhir/StructureDefinition/dsf-extension-ping-status-1_0.xml new file mode 100644 index 00000000..3917b793 --- /dev/null +++ b/src/test/resources/fhir/StructureDefinition/dsf-extension-ping-status-1_0.xml @@ -0,0 +1,134 @@ +<StructureDefinition xmlns="http://hl7.org/fhir"> + <meta> + <tag> + <system value="http://dsf.dev/fhir/CodeSystem/read-access-tag"/> + <code value="ALL"/> + </tag> + </meta> + <url value="http://dsf.dev/fhir/StructureDefinition/extension-ping-status"/> + <!-- version managed by bpe --> + <version value="1.0" /> + <name value="PingResponse"/> + <!-- status managed by bpe --> + <status value="unknown" /> + <experimental value="false" /> + <!-- date managed by bpe --> + <date value="#{date}" /> + <fhirVersion value="4.0.1"/> + <kind value="complex-type"/> + <abstract value="false"/> + <context> + <type value="element"/> + <expression value="Task.output"/> + </context> + <type value="Extension"/> + <baseDefinition value="http://hl7.org/fhir/StructureDefinition/Extension"/> + <derivation value="constraint"/> + <differential> + <element id="Extension.extension"> + <path value="Extension.extension"/> + <slicing> + <discriminator> + <type value="value"/> + <path value="url"/> + </discriminator> + <rules value="open"/> + </slicing> + <min value="3"/> + </element> + <element id="Extension.extension:correlation-key"> + <path value="Extension.extension"/> + <sliceName value="correlation-key"/> + <min value="1"/> + <max value="1"/> + </element> + <element id="Extension.extension:correlation-key.url"> + <path value="Extension.extension.url"/> + <fixedUri value="correlation-key"/> + </element> + <element id="Extension.extension:correlation-key.value[x]"> + <path value="Extension.extension.value[x]"/> + <min value="1"/> + <type> + <code value="string"/> + </type> + </element> + <element id="Extension.extension:organization-identifier"> + <path value="Extension.extension"/> + <sliceName value="organization-identifier"/> + <min value="1"/> + <max value="1"/> + </element> + <element id="Extension.extension:organization-identifier.url"> + <path value="Extension.extension.url"/> + <fixedUri value="organization-identifier"/> + </element> + <element id="Extension.extension:organization-identifier.value[x]"> + <path value="Extension.extension.value[x]"/> + <min value="1"/> + <type> + <code value="Identifier"/> + </type> + </element> + <element id="Extension.extension:organization-identifier.value[x].system"> + <path value="Extension.extension.value[x].system"/> + <min value="1"/> + <fixedUri value="http://dsf.dev/sid/organization-identifier"/> + </element> + <element id="Extension.extension:organization-identifier.value[x].value"> + <path value="Extension.extension.value[x].value"/> + <min value="1"/> + </element> + <element id="Extension.extension:endpoint-identifier"> + <path value="Extension.extension"/> + <sliceName value="endpoint-identifier"/> + <min value="1"/> + <max value="1"/> + </element> + <element id="Extension.extension:endpoint-identifier.url"> + <path value="Extension.extension.url"/> + <fixedUri value="endpoint-identifier"/> + </element> + <element id="Extension.extension:endpoint-identifier.value[x]"> + <path value="Extension.extension.value[x]"/> + <min value="1"/> + <type> + <code value="Identifier"/> + </type> + </element> + <element id="Extension.extension:endpoint-identifier.value[x].system"> + <path value="Extension.extension.value[x].system"/> + <min value="1"/> + <fixedUri value="http://dsf.dev/sid/endpoint-identifier"/> + </element> + <element id="Extension.extension:endpoint-identifier.value[x].value"> + <path value="Extension.extension.value[x].value"/> + <min value="1"/> + </element> + <element id="Extension.extension:error-message"> + <path value="Extension.extension"/> + <sliceName value="error-message"/> + <min value="0"/> + <max value="1"/> + </element> + <element id="Extension.extension:error-message.url"> + <path value="Extension.extension.url"/> + <fixedUri value="error-message"/> + </element> + <element id="Extension.extension:error-message.value[x]"> + <path value="Extension.extension.value[x]"/> + <min value="1"/> + <type> + <code value="string"/> + </type> + </element> + <element id="Extension.url"> + <path value="Extension.url"/> + <fixedUri value="http://dsf.dev/fhir/StructureDefinition/extension-ping-status"/> + </element> + <element id="Extension.value[x]"> + <path value="Extension.value[x]"/> + <max value="0"/> + </element> + </differential> +</StructureDefinition> \ No newline at end of file diff --git a/src/test/resources/fhir/ValueSet/dsf-ping-1_0.xml b/src/test/resources/fhir/ValueSet/dsf-ping-1_0.xml new file mode 100644 index 00000000..4784f28a --- /dev/null +++ b/src/test/resources/fhir/ValueSet/dsf-ping-1_0.xml @@ -0,0 +1,27 @@ +<ValueSet xmlns="http://hl7.org/fhir"> + <meta> + <tag> + <system value="http://dsf.dev/fhir/CodeSystem/read-access-tag" /> + <code value="ALL" /> + </tag> + </meta> + <url value="http://dsf.dev/fhir/ValueSet/ping-v2" /> + <!-- version managed by bpe --> + <version value="1.0" /> + <name value="DSF_Ping" /> + <title value="DSF Ping" /> + <!-- status managed by bpe --> + <status value="unknown" /> + <experimental value="false" /> + <!-- date managed by bpe --> + <date value="#{date}" /> + <publisher value="DSF" /> + <description value="ValueSet with standard values for the process ping/pong" /> + <immutable value="true" /> + <compose> + <include> + <system value="http://dsf.dev/fhir/CodeSystem/ping-v2" /> + <version value="1.0" /> + </include> + </compose> +</ValueSet> \ No newline at end of file diff --git a/src/test/resources/fhir/ValueSet/dsf-ping-status-1_0.xml b/src/test/resources/fhir/ValueSet/dsf-ping-status-1_0.xml new file mode 100644 index 00000000..cae323ec --- /dev/null +++ b/src/test/resources/fhir/ValueSet/dsf-ping-status-1_0.xml @@ -0,0 +1,44 @@ +<ValueSet xmlns="http://hl7.org/fhir"> + <meta> + <tag> + <system + value="http://dsf.dev/fhir/CodeSystem/read-access-tag" /> + <code value="ALL" /> + </tag> + </meta> + <url value="http://dsf.dev/fhir/ValueSet/ping-status-v2" /> + <!-- version managed by bpe --> + <version value="1.0" /> + <name value="DSF_Ping_Status" /> + <title value="DSF Ping Status" /> + <!-- status managed by bpe --> + <status value="unknown" /> + <experimental value="false" /> + <!-- date managed by bpe --> + <date value="#{date}" /> + <publisher value="DSF" /> + <description value="ValueSet with ping status values" /> + <immutable value="true" /> + <compose> + <include> + <system value="http://dsf.dev/fhir/CodeSystem/ping-status-v2" /> + <version value="1.0" /> + <concept> + <code value="not-allowed" /> + <display value="Not allowed" /> + </concept> + <concept> + <code value="not-reachable" /> + <display value="Not reachable" /> + </concept> + <concept> + <code value="pong-missing" /> + <display value="Pong missing" /> + </concept> + <concept> + <code value="pong-received" /> + <display value="Pong received" /> + </concept> + </include> + </compose> +</ValueSet> \ No newline at end of file diff --git a/src/test/resources/fhir/ValueSet/dsf-pong-status-1_0.xml b/src/test/resources/fhir/ValueSet/dsf-pong-status-1_0.xml new file mode 100644 index 00000000..38a2020a --- /dev/null +++ b/src/test/resources/fhir/ValueSet/dsf-pong-status-1_0.xml @@ -0,0 +1,39 @@ +<ValueSet xmlns="http://hl7.org/fhir"> + <meta> + <tag> + <system value="http://dsf.dev/fhir/CodeSystem/read-access-tag" /> + <code value="ALL" /> + </tag> + </meta> + <url value="http://dsf.dev/fhir/ValueSet/pong-status-v2" /> + <!-- version managed by bpe --> + <version value="1.0" /> + <name value="DSF_Pong_Status" /> + <title value="DSF Pong Status" /> + <!-- status managed by bpe --> + <status value="unknown" /> + <experimental value="false" /> + <!-- date managed by bpe --> + <date value="#{date}" /> + <publisher value="DSF" /> + <description value="ValueSet with pong status values" /> + <immutable value="true" /> + <compose> + <include> + <system value="http://dsf.dev/fhir/CodeSystem/ping-status-v2" /> + <version value="1.0" /> + <concept> + <code value="not-allowed" /> + <display value="Not allowed" /> + </concept> + <concept> + <code value="not-reachable" /> + <display value="Not reachable" /> + </concept> + <concept> + <code value="pong-send" /> + <display value="Pong send" /> + </concept> + </include> + </compose> +</ValueSet> \ No newline at end of file