From e8941a2e23589dacd358037862da5884b911f13e Mon Sep 17 00:00:00 2001 From: Basilio Bogado <541149+basiliskus@users.noreply.github.com> Date: Fri, 26 Jan 2024 01:20:38 +0800 Subject: [PATCH] SendResultUseCase implementation (#803) * results endpoint skeleton * Added scheleton for SendResultUseCase * Reverted change * result interface (#804) * Renamed message => messages to follow naming convention * edit name getUnderlyingOrder to getUnderlyingReslut * Abstracted ResultSender for more generic MessageSender and added skeleton implementation * Removed unused ResultConverter * Added test coverage * Added java docs * Added missing java docs --------- Co-authored-by: jorge Lopez Co-authored-by: Jorge Lopez <49923512+jorg3lopez@users.noreply.github.com> --- .../etor/messages/MessageSender.java | 8 ++++ .../etor/messages/SendMessageUseCase.java | 13 ++++++ .../UnableToSendMessageException.java | 8 ++++ .../etor/results/Result.java | 14 ++++++ .../etor/results/SendResultUseCase.java | 24 ++++++++++ .../ReportStreamResultSender.java | 27 +++++++++++ .../cdc/trustedintermediary/ResultMock.groovy | 34 ++++++++++++++ .../UnableToSendMessageExceptionTest.groovy | 20 +++++++++ .../etor/results/SendResultUseCaseTest.groovy | 45 +++++++++++++++++++ .../ReportStreamResultSenderTest.groovy | 23 ++++++++++ 10 files changed, 216 insertions(+) create mode 100644 etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/messages/MessageSender.java create mode 100644 etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/messages/SendMessageUseCase.java create mode 100644 etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/messages/UnableToSendMessageException.java create mode 100644 etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/results/Result.java create mode 100644 etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/results/SendResultUseCase.java create mode 100644 etor/src/main/java/gov/hhs/cdc/trustedintermediary/external/reportstream/ReportStreamResultSender.java create mode 100644 etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/ResultMock.groovy create mode 100644 etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/etor/messages/UnableToSendMessageExceptionTest.groovy create mode 100644 etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/etor/results/SendResultUseCaseTest.groovy create mode 100644 etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/external/reportstream/ReportStreamResultSenderTest.groovy diff --git a/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/messages/MessageSender.java b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/messages/MessageSender.java new file mode 100644 index 000000000..b88dad30a --- /dev/null +++ b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/messages/MessageSender.java @@ -0,0 +1,8 @@ +package gov.hhs.cdc.trustedintermediary.etor.messages; + +import java.util.Optional; + +/** Interface for sending a generic message. */ +public interface MessageSender { + Optional send(T message) throws UnableToSendMessageException; +} diff --git a/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/messages/SendMessageUseCase.java b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/messages/SendMessageUseCase.java new file mode 100644 index 000000000..d57c42cf2 --- /dev/null +++ b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/messages/SendMessageUseCase.java @@ -0,0 +1,13 @@ +package gov.hhs.cdc.trustedintermediary.etor.messages; + +/** + * This interface represents a use case for sending a generic message. It provides a method to + * convert and send the message. + * + * @param the type of message to be sent + */ +public interface SendMessageUseCase { + + void convertAndSend(final T message, String receivedSubmissionId) + throws UnableToSendMessageException; +} diff --git a/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/messages/UnableToSendMessageException.java b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/messages/UnableToSendMessageException.java new file mode 100644 index 000000000..64c9b9791 --- /dev/null +++ b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/messages/UnableToSendMessageException.java @@ -0,0 +1,8 @@ +package gov.hhs.cdc.trustedintermediary.etor.messages; + +/** This exception is thrown when there is an error sending a message. */ +public class UnableToSendMessageException extends Exception { + public UnableToSendMessageException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/results/Result.java b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/results/Result.java new file mode 100644 index 000000000..3c5202868 --- /dev/null +++ b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/results/Result.java @@ -0,0 +1,14 @@ +package gov.hhs.cdc.trustedintermediary.etor.results; + +/** + * Interface to wrap a third-party lab result class (Ex: Hapi FHIR Bundle) + * + * @param The underlying FHIR lab result type. + */ +public interface Result { + T getUnderlyingResult(); + + String getFhirResourceId(); + + String getPatientId(); +} diff --git a/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/results/SendResultUseCase.java b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/results/SendResultUseCase.java new file mode 100644 index 000000000..c3b24b57f --- /dev/null +++ b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/results/SendResultUseCase.java @@ -0,0 +1,24 @@ +package gov.hhs.cdc.trustedintermediary.etor.results; + +import gov.hhs.cdc.trustedintermediary.etor.messages.MessageSender; +import gov.hhs.cdc.trustedintermediary.etor.messages.SendMessageUseCase; +import gov.hhs.cdc.trustedintermediary.etor.messages.UnableToSendMessageException; +import javax.inject.Inject; + +/** Use case for converting and sending a lab result message. */ +public class SendResultUseCase implements SendMessageUseCase> { + private static final SendResultUseCase INSTANCE = new SendResultUseCase(); + @Inject MessageSender> sender; + + private SendResultUseCase() {} + + public static SendResultUseCase getInstance() { + return INSTANCE; + } + + @Override + public void convertAndSend(Result result, String receivedSubmissionId) + throws UnableToSendMessageException { + sender.send(result); + } +} diff --git a/etor/src/main/java/gov/hhs/cdc/trustedintermediary/external/reportstream/ReportStreamResultSender.java b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/external/reportstream/ReportStreamResultSender.java new file mode 100644 index 000000000..4d8096f71 --- /dev/null +++ b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/external/reportstream/ReportStreamResultSender.java @@ -0,0 +1,27 @@ +package gov.hhs.cdc.trustedintermediary.external.reportstream; + +import gov.hhs.cdc.trustedintermediary.etor.messages.MessageSender; +import gov.hhs.cdc.trustedintermediary.etor.messages.UnableToSendMessageException; +import gov.hhs.cdc.trustedintermediary.etor.results.Result; +import java.util.Optional; + +/** + * This class is responsible for sending results to the ReportStream service and receiving a + * response back. + */ +public class ReportStreamResultSender implements MessageSender> { + + private static final ReportStreamResultSender INSTANCE = new ReportStreamResultSender(); + + public static ReportStreamResultSender getInstance() { + return INSTANCE; + } + + private ReportStreamResultSender() {} + + @Override + public Optional send(Result result) throws UnableToSendMessageException { + // todo: implement in #616 + return Optional.empty(); + } +} diff --git a/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/ResultMock.groovy b/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/ResultMock.groovy new file mode 100644 index 000000000..49511ceb5 --- /dev/null +++ b/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/ResultMock.groovy @@ -0,0 +1,34 @@ +package gov.hhs.cdc.trustedintermediary + +import gov.hhs.cdc.trustedintermediary.etor.results.Result + +/** + * A mock implementation of the {@link gov.hhs.cdc.trustedintermediary.etor.results.Result} interface that is easy to use in tests. + */ +class ResultMock implements Result { + + private String fhirResourceId + private String patientId + private T underlyingResult + + ResultMock(String fhirResourceId, String patientId, T underlyingOrders) { + this.fhirResourceId = fhirResourceId + this.patientId = patientId + this.underlyingResult = underlyingOrders + } + + @Override + T getUnderlyingResult() { + return underlyingResult + } + + @Override + String getFhirResourceId() { + return fhirResourceId + } + + @Override + String getPatientId() { + return patientId + } +} diff --git a/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/etor/messages/UnableToSendMessageExceptionTest.groovy b/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/etor/messages/UnableToSendMessageExceptionTest.groovy new file mode 100644 index 000000000..6eb550d71 --- /dev/null +++ b/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/etor/messages/UnableToSendMessageExceptionTest.groovy @@ -0,0 +1,20 @@ +package gov.hhs.cdc.trustedintermediary.etor.messages + +import gov.hhs.cdc.trustedintermediary.wrappers.HttpClientException +import spock.lang.Specification + +class UnableToSendMessageExceptionTest extends Specification { + def "contructor works"() { + + given: + def message = "something blew up!" + def cause = new HttpClientException(message, new IOException()) + + when: + def exception = new UnableToSendMessageException(message, cause) + + then: + exception.getMessage() == message + exception.getCause() == cause + } +} diff --git a/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/etor/results/SendResultUseCaseTest.groovy b/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/etor/results/SendResultUseCaseTest.groovy new file mode 100644 index 000000000..af3b6355f --- /dev/null +++ b/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/etor/results/SendResultUseCaseTest.groovy @@ -0,0 +1,45 @@ +package gov.hhs.cdc.trustedintermediary.etor.results + +import gov.hhs.cdc.trustedintermediary.ResultMock +import gov.hhs.cdc.trustedintermediary.context.TestApplicationContext +import gov.hhs.cdc.trustedintermediary.etor.messages.MessageSender +import gov.hhs.cdc.trustedintermediary.etor.messages.SendMessageUseCase +import gov.hhs.cdc.trustedintermediary.etor.messages.UnableToSendMessageException +import spock.lang.Specification + +class SendResultUseCaseTest extends Specification { + + def mockSender = Mock(MessageSender) + + def setup() { + TestApplicationContext.reset() + TestApplicationContext.init() + TestApplicationContext.register(SendMessageUseCase, SendResultUseCase.getInstance()) + TestApplicationContext.register(MessageSender, mockSender) + } + + def "convertAndSend works"() { + given: + def mockResult = new ResultMock(null, null, "Mock result") + TestApplicationContext.injectRegisteredImplementations() + + when: + SendResultUseCase.getInstance().convertAndSend(mockResult, _ as String) + + then: + 1 * mockSender.send(mockResult) >> Optional.empty() + noExceptionThrown() + } + + def "convertAndSend throws exception when send fails"() { + given: + mockSender.send(_) >> { throw new UnableToSendMessageException("DogCow", new NullPointerException()) } + TestApplicationContext.injectRegisteredImplementations() + + when: + SendResultUseCase.getInstance().convertAndSend(Mock(Result), _ as String) + + then: + thrown(UnableToSendMessageException) + } +} diff --git a/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/external/reportstream/ReportStreamResultSenderTest.groovy b/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/external/reportstream/ReportStreamResultSenderTest.groovy new file mode 100644 index 000000000..46af8cb07 --- /dev/null +++ b/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/external/reportstream/ReportStreamResultSenderTest.groovy @@ -0,0 +1,23 @@ +package gov.hhs.cdc.trustedintermediary.external.reportstream + +import gov.hhs.cdc.trustedintermediary.ResultMock +import gov.hhs.cdc.trustedintermediary.context.TestApplicationContext +import gov.hhs.cdc.trustedintermediary.etor.messages.MessageSender +import spock.lang.Specification + +class ReportStreamResultSenderTest extends Specification { + + def setup() { + TestApplicationContext.reset() + TestApplicationContext.init() + TestApplicationContext.register(MessageSender, ReportStreamResultSender.getInstance()) + } + + def "send results works"() { + when: + ReportStreamResultSender.getInstance().send(new ResultMock(null, null, "Mock result")) + + then: + noExceptionThrown() + } +}