Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implementation to send the FHIR result to RS Waters endpoint #859

Merged
merged 8 commits into from
Feb 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import gov.hhs.cdc.trustedintermediary.external.reportstream.ReportStreamEndpointClient;
import gov.hhs.cdc.trustedintermediary.external.reportstream.ReportStreamOrderSender;
import gov.hhs.cdc.trustedintermediary.external.reportstream.ReportStreamResultSender;
import gov.hhs.cdc.trustedintermediary.external.reportstream.ReportStreamSenderHelper;
import gov.hhs.cdc.trustedintermediary.wrappers.FhirParseException;
import gov.hhs.cdc.trustedintermediary.wrappers.HapiFhir;
import gov.hhs.cdc.trustedintermediary.wrappers.Logger;
Expand Down Expand Up @@ -104,6 +105,8 @@ public Map<HttpEndpoint, Function<DomainRequest, DomainResponse>> domainRegistra
ApplicationContext.register(ResultSender.class, ReportStreamResultSender.getInstance());
ApplicationContext.register(ResultController.class, ResultController.getInstance());
ApplicationContext.register(SendResultUseCase.class, SendResultUseCase.getInstance());
ApplicationContext.register(
ReportStreamSenderHelper.class, ReportStreamSenderHelper.getInstance());

if (ApplicationContext.getProperty("DB_URL") != null) {
ApplicationContext.register(DbDao.class, PostgresDao.getInstance());
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,10 @@
package gov.hhs.cdc.trustedintermediary.external.reportstream;

import gov.hhs.cdc.trustedintermediary.etor.RSEndpointClient;
import gov.hhs.cdc.trustedintermediary.etor.messages.UnableToSendMessageException;
import gov.hhs.cdc.trustedintermediary.etor.metadata.EtorMetadataStep;
import gov.hhs.cdc.trustedintermediary.etor.orders.Order;
import gov.hhs.cdc.trustedintermediary.etor.orders.OrderSender;
import gov.hhs.cdc.trustedintermediary.wrappers.HapiFhir;
import gov.hhs.cdc.trustedintermediary.wrappers.Logger;
import gov.hhs.cdc.trustedintermediary.wrappers.MetricMetadata;
import gov.hhs.cdc.trustedintermediary.wrappers.formatter.Formatter;
import gov.hhs.cdc.trustedintermediary.wrappers.formatter.FormatterProcessingException;
import gov.hhs.cdc.trustedintermediary.wrappers.formatter.TypeReference;
import java.util.Map;
import java.util.Optional;
import javax.inject.Inject;

Expand All @@ -20,11 +13,9 @@ public class ReportStreamOrderSender implements OrderSender {

private static final ReportStreamOrderSender INSTANCE = new ReportStreamOrderSender();

@Inject private RSEndpointClient rsclient;
@Inject private Formatter formatter;
@Inject private HapiFhir fhir;
@Inject private Logger logger;
@Inject MetricMetadata metadata;
@Inject ReportStreamSenderHelper sender;
@Inject HapiFhir fhir;
@Inject Logger logger;

public static ReportStreamOrderSender getInstance() {
return INSTANCE;
Expand All @@ -35,40 +26,7 @@ private ReportStreamOrderSender() {}
@Override
public Optional<String> send(final Order<?> order) throws UnableToSendMessageException {
logger.logInfo("Sending the order to ReportStream");

String json = fhir.encodeResourceToJson(order.getUnderlyingOrder());
String bearerToken;
String rsResponseBody;

try {
bearerToken = rsclient.getRsToken();
rsResponseBody = rsclient.requestWatersEndpoint(json, bearerToken);
} catch (ReportStreamEndpointClientException e) {
throw new UnableToSendMessageException("Unable to send order to ReportStream", e);
}

logger.logInfo("Order successfully sent to ReportStream");
metadata.put(order.getFhirResourceId(), EtorMetadataStep.SENT_TO_REPORT_STREAM);

Optional<String> sentSubmissionId = getSubmissionId(rsResponseBody);
if (sentSubmissionId.isEmpty()) {
logger.logError("Unable to retrieve sentSubmissionId from ReportStream response");
} else {
logger.logInfo("ReportStream response's sentSubmissionId={}", sentSubmissionId);
}

return sentSubmissionId;
}

protected Optional<String> getSubmissionId(String rsResponseBody) {
try {
Map<String, Object> rsResponse =
formatter.convertJsonToObject(rsResponseBody, new TypeReference<>() {});
return Optional.ofNullable(rsResponse.get("submissionId").toString());
} catch (FormatterProcessingException e) {
logger.logError("Unable to get the submissionId", e);
}

return Optional.empty();
return sender.sendOrderToReportStream(json, order.getFhirResourceId());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@
import gov.hhs.cdc.trustedintermediary.etor.messages.UnableToSendMessageException;
import gov.hhs.cdc.trustedintermediary.etor.results.Result;
import gov.hhs.cdc.trustedintermediary.etor.results.ResultSender;
import gov.hhs.cdc.trustedintermediary.wrappers.HapiFhir;
import gov.hhs.cdc.trustedintermediary.wrappers.Logger;
import java.util.Optional;
import javax.inject.Inject;

/**
* This class is responsible for sending results to the ReportStream service and receiving a
Expand All @@ -13,6 +16,10 @@ public class ReportStreamResultSender implements ResultSender {

private static final ReportStreamResultSender INSTANCE = new ReportStreamResultSender();

@Inject ReportStreamSenderHelper sender;
@Inject HapiFhir fhir;
@Inject Logger logger;

public static ReportStreamResultSender getInstance() {
return INSTANCE;
}
Expand All @@ -21,7 +28,8 @@ private ReportStreamResultSender() {}

@Override
public Optional<String> send(Result<?> result) throws UnableToSendMessageException {
// todo: implement in #616
return Optional.empty();
logger.logInfo("Sending results to ReportStream");
String json = fhir.encodeResourceToJson(result.getUnderlyingResult());
return sender.sendResultToReportStream(json, result.getFhirResourceId());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package gov.hhs.cdc.trustedintermediary.external.reportstream;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like that we're using a helper class instead of inheritance.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool, we're also using a helper class as well in our other PR for the Order/Result Conversion use cases.


import gov.hhs.cdc.trustedintermediary.etor.RSEndpointClient;
import gov.hhs.cdc.trustedintermediary.etor.messages.UnableToSendMessageException;
import gov.hhs.cdc.trustedintermediary.etor.metadata.EtorMetadataStep;
import gov.hhs.cdc.trustedintermediary.wrappers.Logger;
import gov.hhs.cdc.trustedintermediary.wrappers.MetricMetadata;
import gov.hhs.cdc.trustedintermediary.wrappers.formatter.Formatter;
import gov.hhs.cdc.trustedintermediary.wrappers.formatter.FormatterProcessingException;
import gov.hhs.cdc.trustedintermediary.wrappers.formatter.TypeReference;
import java.util.Map;
import java.util.Optional;
import javax.inject.Inject;

/** Helper class for sending messages to ReportStream */
public class ReportStreamSenderHelper {
private static final ReportStreamSenderHelper INSTANCE = new ReportStreamSenderHelper();

@Inject RSEndpointClient rsclient;
@Inject Formatter formatter;
@Inject Logger logger;
@Inject MetricMetadata metadata;

private ReportStreamSenderHelper() {}

public static ReportStreamSenderHelper getInstance() {
return INSTANCE;
}

public Optional<String> sendOrderToReportStream(String body, String fhirResourceId)
throws UnableToSendMessageException {
return sendToReportStream(body, fhirResourceId, "order");
}

public Optional<String> sendResultToReportStream(String body, String fhirResourceId)
throws UnableToSendMessageException {
return sendToReportStream(body, fhirResourceId, "result");
}

protected Optional<String> sendToReportStream(
String body, String fhirResourceId, String messageType)
throws UnableToSendMessageException {
String bearerToken;
String rsResponseBody;

try {
bearerToken = rsclient.getRsToken();
rsResponseBody = rsclient.requestWatersEndpoint(body, bearerToken);
} catch (ReportStreamEndpointClientException e) {
throw new UnableToSendMessageException(
"Unable to send " + messageType + " to ReportStream", e);
}

logger.logInfo("{} successfully sent to ReportStream", messageType);
metadata.put(fhirResourceId, EtorMetadataStep.SENT_TO_REPORT_STREAM);

Optional<String> sentSubmissionId = getSubmissionId(rsResponseBody);
if (sentSubmissionId.isEmpty()) {
logger.logError("Unable to retrieve sentSubmissionId from ReportStream response");
} else {
logger.logInfo("ReportStream response's sentSubmissionId={}", sentSubmissionId);
}

return sentSubmissionId;
}

protected Optional<String> getSubmissionId(String rsResponseBody) {
try {
Map<String, Object> rsResponse =
formatter.convertJsonToObject(rsResponseBody, new TypeReference<>() {});
return Optional.ofNullable(rsResponse.get("submissionId").toString());
} catch (FormatterProcessingException e) {
logger.logError("Unable to get the submissionId", e);
}

return Optional.empty();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ class ResultMock<T> implements Result<T> {
private String fhirResourceId
private T underlyingResult

ResultMock(String fhirResourceId, T underlyingOrders) {
ResultMock(String fhirResourceId, T underlyingResult) {
this.fhirResourceId = fhirResourceId
this.underlyingResult = underlyingOrders
this.underlyingResult = underlyingResult
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,10 @@ package gov.hhs.cdc.trustedintermediary.external.reportstream
import gov.hhs.cdc.trustedintermediary.OrderMock
import gov.hhs.cdc.trustedintermediary.context.TestApplicationContext
import gov.hhs.cdc.trustedintermediary.etor.RSEndpointClient
import gov.hhs.cdc.trustedintermediary.etor.metadata.EtorMetadataStep
import gov.hhs.cdc.trustedintermediary.etor.orders.OrderSender
import gov.hhs.cdc.trustedintermediary.external.jackson.Jackson
import gov.hhs.cdc.trustedintermediary.external.localfile.MockRSEndpointClient
import gov.hhs.cdc.trustedintermediary.wrappers.AuthEngine
import gov.hhs.cdc.trustedintermediary.wrappers.Cache
import gov.hhs.cdc.trustedintermediary.wrappers.HapiFhir
import gov.hhs.cdc.trustedintermediary.wrappers.HttpClient
import gov.hhs.cdc.trustedintermediary.wrappers.Logger
import gov.hhs.cdc.trustedintermediary.wrappers.MetricMetadata
import gov.hhs.cdc.trustedintermediary.wrappers.Secrets
import gov.hhs.cdc.trustedintermediary.wrappers.formatter.Formatter
import gov.hhs.cdc.trustedintermediary.wrappers.formatter.FormatterProcessingException
import gov.hhs.cdc.trustedintermediary.wrappers.formatter.TypeReference
import spock.lang.Specification

class ReportStreamOrderSenderTest extends Specification {
Expand All @@ -31,106 +21,24 @@ class ReportStreamOrderSenderTest extends Specification {

def "send order works"() {
given:
def mockAuthEngine = Mock(AuthEngine)
TestApplicationContext.register(AuthEngine, mockAuthEngine)
def fhirResourceId = null
def underlyingOrder = "Mock order"
def mockOrder = new OrderMock(fhirResourceId, "patient-id", underlyingOrder)

def mockSecrets = Mock(Secrets)
TestApplicationContext.register(Secrets, mockSecrets)

def mockClient = Mock(HttpClient)
mockClient.post(_ as String, _ as Map, _ as String) >> """{"submissionId": "fake-id", "key": "value"}"""
TestApplicationContext.register(HttpClient, mockClient)
def senderHelper = Mock(ReportStreamSenderHelper)
senderHelper.sendOrderToReportStream(underlyingOrder, fhirResourceId) >> Optional.of("fake-id")
TestApplicationContext.register(ReportStreamSenderHelper, senderHelper)

def mockFhir = Mock(HapiFhir)
mockFhir.encodeResourceToJson(_ as String) >> "Mock order"
mockFhir.encodeResourceToJson(_ as String) >> underlyingOrder
TestApplicationContext.register(HapiFhir, mockFhir)

def mockFormatter = Mock(Formatter)
mockFormatter.convertJsonToObject(_ as String, _ as TypeReference) >> Map.of("submissionId", "fake-id")
TestApplicationContext.register(Formatter, mockFormatter)

def mockCache = Mock(Cache)
TestApplicationContext.register(Cache, mockCache)

TestApplicationContext.injectRegisteredImplementations()

when:
ReportStreamOrderSender.getInstance().send(new OrderMock(null, null, "Mock order"))
ReportStreamOrderSender.getInstance().send(mockOrder)

then:
noExceptionThrown()
}

def "log the step to metadata when send order is called"() {
given:

def mockAuthEngine = Mock(AuthEngine)
TestApplicationContext.register(AuthEngine, mockAuthEngine)

def mockSecrets = Mock(Secrets)
TestApplicationContext.register(Secrets, mockSecrets)

def mockClient = Mock(HttpClient)
mockClient.post(_ as String, _ as Map, _ as String) >> """{"submissionId": "fake-id", "key": "value"}"""
TestApplicationContext.register(HttpClient, mockClient)

def mockFhir = Mock(HapiFhir)
mockFhir.encodeResourceToJson(_ as String) >> "Mock order"
TestApplicationContext.register(HapiFhir, mockFhir)

def mockFormatter = Mock(Formatter)
mockFormatter.convertJsonToObject(_ as String, _ as TypeReference) >> Map.of("submissionId", "fake-id")
TestApplicationContext.register(Formatter, mockFormatter)

def mockCache = Mock(Cache)
TestApplicationContext.register(Cache, mockCache)

TestApplicationContext.injectRegisteredImplementations()

when:
ReportStreamOrderSender.getInstance().send(new OrderMock(null, null, "Mock order"))

then:
1 * ReportStreamOrderSender.getInstance().metadata.put(_, EtorMetadataStep.SENT_TO_REPORT_STREAM)
}

def "getSubmissionId logs submissionId if convertJsonToObject is successful"() {
given:
def mockSubmissionId = "fake-id"
def mockResponseBody = """{"submissionId": "${mockSubmissionId}", "key": "value"}"""

TestApplicationContext.register(Formatter, Jackson.getInstance())

def mockLogger = Mock(Logger)
TestApplicationContext.register(Logger, mockLogger)

TestApplicationContext.injectRegisteredImplementations()

when:
def submissionId = ReportStreamOrderSender.getInstance().getSubmissionId(mockResponseBody)

then:
submissionId.get() == mockSubmissionId
}

def "getSubmissionId logs error if convertJsonToObject fails"() {
given:
def mockResponseBody = '{"submissionId": "fake-id", "key": "value"}'
def exception = new FormatterProcessingException("couldn't convert json", new Exception())

def mockFormatter = Mock(Formatter)
mockFormatter.convertJsonToObject(_ as String, _ as TypeReference) >> { throw exception }
TestApplicationContext.register(Formatter, mockFormatter)

def mockLogger = Mock(Logger)
TestApplicationContext.register(Logger, mockLogger)

TestApplicationContext.injectRegisteredImplementations()

when:
ReportStreamOrderSender.getInstance().getSubmissionId(mockResponseBody)

then:
1 * mockLogger.logError(_ as String, exception)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ 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.results.ResultSender
import gov.hhs.cdc.trustedintermediary.wrappers.HapiFhir
import spock.lang.Specification

class ReportStreamResultSenderTest extends Specification {
Expand All @@ -14,8 +15,23 @@ class ReportStreamResultSenderTest extends Specification {
}

def "send results works"() {
given:
def fhirResourceId = null
def underlyingResult = "Mock result"
def mockResult = new ResultMock(fhirResourceId, underlyingResult)

def senderHelper = Mock(ReportStreamSenderHelper)
senderHelper.sendResultToReportStream(underlyingResult, fhirResourceId) >> Optional.of("fake-id")
TestApplicationContext.register(ReportStreamSenderHelper, senderHelper)

def mockFhir = Mock(HapiFhir)
mockFhir.encodeResourceToJson(_ as String) >> underlyingResult
TestApplicationContext.register(HapiFhir, mockFhir)

TestApplicationContext.injectRegisteredImplementations()

when:
ReportStreamResultSender.getInstance().send(new ResultMock(null, "Mock result"))
ReportStreamResultSender.getInstance().send(mockResult)

then:
noExceptionThrown()
Expand Down
Loading
Loading