Skip to content

Commit

Permalink
Use ReportId to identify metadata for testing purposes (#729)
Browse files Browse the repository at this point in the history
* Initial implementation to get recordid for metadata

* Added error handling when we don't get the recordid

* Fixed failing tests

* Fixed failing end to end tests

* After discussion with the team, decided not break the flow and log it as an error

* 672: test that we log an error message and proceed like normal when we are not sent the RS metadata ID

* Overloaded endpointclient submit method to handle sumissionid

* Refactored to check for null, continue flow if there's an error saving metadata, and improve readability

* Added pending task comment, to be done in another PR

* Added handling and test when RecordId comes back empty in the headers

---------

Co-authored-by: halprin <[email protected]>
  • Loading branch information
basiliskus and halprin authored Dec 19, 2023
1 parent 7a76f85 commit a377f16
Show file tree
Hide file tree
Showing 6 changed files with 124 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ public EndpointClient(String endpoint) {
}

public ClassicHttpResponse submit(String fhirBody, boolean loginFirst) throws IOException {
return submit(fhirBody, null, loginFirst);
}

public ClassicHttpResponse submit(String fhirBody, String submissionId, boolean loginFirst)
throws IOException {

Map<String, String> headers = new HashMap<>();

Expand All @@ -38,6 +43,10 @@ public ClassicHttpResponse submit(String fhirBody, boolean loginFirst) throws IO
headers.put("Authorization", "Bearer " + accessToken);
}

if (submissionId != null) {
headers.put("RecordId", submissionId);
}

return HttpClient.post(endpoint, fhirBody, ContentType.APPLICATION_JSON, headers);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ class OrderTest extends Specification {

def orderClient = new EndpointClient("/v1/etor/orders")
def labOrderJsonFileString = Files.readString(Path.of("../examples/fhir/MN NBS FHIR Order Message.json"))
def submissionId = "submissionId"

def setup() {
SentPayloadReader.delete()
Expand All @@ -20,7 +21,7 @@ class OrderTest extends Specification {
def expectedPatientId = "11102779"

when:
def response = orderClient.submit(labOrderJsonFileString, true)
def response = orderClient.submit(labOrderJsonFileString, submissionId, true)
def parsedJsonBody = JsonParsing.parseContent(response)

then:
Expand All @@ -31,7 +32,7 @@ class OrderTest extends Specification {

def "check that contact info is added to order before sending to report stream"() {
when:
orderClient.submit(labOrderJsonFileString, true)
orderClient.submit(labOrderJsonFileString, submissionId, true)
def sentPayload = SentPayloadReader.read()
def parsedSentPayload = JsonParsing.parse(sentPayload)

Expand All @@ -41,7 +42,7 @@ class OrderTest extends Specification {

def "check that the rest of the message is unchanged except the parts we changed"() {
when:
orderClient.submit(labOrderJsonFileString, true)
orderClient.submit(labOrderJsonFileString, submissionId, true)
def sentPayload = SentPayloadReader.read()
def parsedSentPayload = JsonParsing.parse(sentPayload)
def parsedLabOrderJsonFile = JsonParsing.parse(labOrderJsonFileString)
Expand All @@ -57,7 +58,7 @@ class OrderTest extends Specification {

def "check that message type is converted to OML_O21"() {
when:
orderClient.submit(labOrderJsonFileString, true)
orderClient.submit(labOrderJsonFileString, submissionId, true)
def sentPayload = SentPayloadReader.read()
def parsedSentPayload = JsonParsing.parse(sentPayload)

Expand All @@ -74,7 +75,7 @@ class OrderTest extends Specification {
def invalidJsonRequest = labOrderJsonFileString.substring(1)

when:
def response = orderClient.submit(invalidJsonRequest, true)
def response = orderClient.submit(invalidJsonRequest, submissionId, true)
def parsedJsonBody = JsonParsing.parseContent(response)

then:
Expand All @@ -84,7 +85,7 @@ class OrderTest extends Specification {

def "return a 401 response when making an unauthenticated request"() {
when:
def response = orderClient.submit(labOrderJsonFileString, false)
def response = orderClient.submit(labOrderJsonFileString, submissionId, false)
def parsedJsonBody = JsonParsing.parseContent(response)

then:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,9 +126,15 @@ DomainResponse handleDemographics(DomainRequest request) {
DomainResponse handleOrders(DomainRequest request) {
Order<?> orders;

String submissionId = request.getHeaders().get("RecordId");
if (submissionId == null || submissionId.isEmpty()) {
submissionId = null;
logger.logError("Missing required header or empty: RecordId");
}

try {
orders = orderController.parseOrders(request);
sendOrderUseCase.convertAndSend(orders);
sendOrderUseCase.convertAndSend(orders, submissionId);
} catch (FhirParseException e) {
logger.logError("Unable to parse order request", e);
return domainResponseHelper.constructErrorResponse(400, e);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import gov.hhs.cdc.trustedintermediary.etor.metadata.PartnerMetadata;
import gov.hhs.cdc.trustedintermediary.etor.metadata.PartnerMetadataException;
import gov.hhs.cdc.trustedintermediary.etor.metadata.PartnerMetadataStorage;
import gov.hhs.cdc.trustedintermediary.wrappers.Logger;
import gov.hhs.cdc.trustedintermediary.wrappers.MetricMetadata;
import java.time.Instant;
import javax.inject.Inject;
Expand All @@ -15,21 +16,21 @@ public class SendOrderUseCase {
@Inject OrderSender sender;
@Inject MetricMetadata metadata;
@Inject PartnerMetadataStorage partnerMetadataStorage;
@Inject Logger logger;

private SendOrderUseCase() {}

public static SendOrderUseCase getInstance() {
return INSTANCE;
}

public void convertAndSend(final Order<?> order) throws UnableToSendOrderException {
var partnerMetadata =
new PartnerMetadata(
"uniqueId", "senderName", "receiverName", Instant.now(), "abcd");
public void convertAndSend(final Order<?> order, String submissionId)
throws UnableToSendOrderException {

try {
partnerMetadataStorage.saveMetadata(partnerMetadata);
savePartnerMetadata(submissionId);
} catch (PartnerMetadataException e) {
throw new UnableToSendOrderException("Unable to save metadata for the order", e);
logger.logError("Unable to save metadata for submissionId " + submissionId, e);
}

var omlOrder = converter.convertMetadataToOmlOrder(order);
Expand All @@ -38,4 +39,16 @@ public void convertAndSend(final Order<?> order) throws UnableToSendOrderExcepti
metadata.put(order.getFhirResourceId(), EtorMetadataStep.CONTACT_SECTION_ADDED_TO_PATIENT);
sender.sendOrder(omlOrder);
}

private void savePartnerMetadata(String submissionId) throws PartnerMetadataException {
if (submissionId == null) {
return;
}

// TODO: still need to get metadata from the order: sender, receiver, timeReceived, hash
PartnerMetadata partnerMetadata =
new PartnerMetadata(
submissionId, "senderName", "receiverName", Instant.now(), "abcd");
partnerMetadataStorage.saveMetadata(partnerMetadata);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@ import gov.hhs.cdc.trustedintermediary.etor.orders.SendOrderUseCase
import gov.hhs.cdc.trustedintermediary.etor.orders.UnableToSendOrderException
import gov.hhs.cdc.trustedintermediary.external.jackson.Jackson
import gov.hhs.cdc.trustedintermediary.wrappers.FhirParseException
import gov.hhs.cdc.trustedintermediary.wrappers.Logger
import gov.hhs.cdc.trustedintermediary.wrappers.formatter.Formatter
import gov.hhs.cdc.trustedintermediary.wrappers.formatter.FormatterProcessingException
import spock.lang.Specification

import java.time.Instant
import spock.lang.Specification

class EtorDomainRegistrationTest extends Specification {

Expand Down Expand Up @@ -157,6 +157,9 @@ class EtorDomainRegistrationTest extends Specification {
given:
def expectedStatusCode = 200

def request = new DomainRequest()
request.headers["RecordId"] = "recordId"

def connector = new EtorDomainRegistration()
TestApplicationContext.register(EtorDomainRegistration, connector)

Expand All @@ -176,7 +179,7 @@ class EtorDomainRegistrationTest extends Specification {
TestApplicationContext.injectRegisteredImplementations()

when:
def res = connector.handleOrders(new DomainRequest())
def res = connector.handleOrders(request)
def actualStatusCode = res.statusCode

then:
Expand All @@ -187,6 +190,9 @@ class EtorDomainRegistrationTest extends Specification {
given:
def expectedStatusCode = 400

def request = new DomainRequest()
request.headers["RecordId"] = "recordId"

def domainRegistration = new EtorDomainRegistration()
TestApplicationContext.register(EtorDomainRegistration, domainRegistration)

Expand All @@ -195,7 +201,7 @@ class EtorDomainRegistrationTest extends Specification {
TestApplicationContext.register(OrderController, mockController)

def mockUseCase = Mock(SendOrderUseCase)
mockUseCase.convertAndSend(_ as Order<?>) >> {
mockUseCase.convertAndSend(_ as Order<?>, _ as String) >> {
throw new UnableToSendOrderException("error", new NullPointerException())
}
TestApplicationContext.register(SendOrderUseCase, mockUseCase)
Expand All @@ -207,7 +213,7 @@ class EtorDomainRegistrationTest extends Specification {
TestApplicationContext.injectRegisteredImplementations()

when:
def res = domainRegistration.handleOrders(new DomainRequest())
def res = domainRegistration.handleOrders(request)
def actualStatusCode = res.statusCode

then:
Expand All @@ -218,6 +224,9 @@ class EtorDomainRegistrationTest extends Specification {
given:
def expectedStatusCode = 400

def request = new DomainRequest()
request.headers["RecordId"] = "recordId"

def domainRegistration = new EtorDomainRegistration()
TestApplicationContext.register(EtorDomainRegistration, domainRegistration)

Expand All @@ -232,13 +241,76 @@ class EtorDomainRegistrationTest extends Specification {
TestApplicationContext.injectRegisteredImplementations()

when:
def res = domainRegistration.handleOrders(new DomainRequest())
def res = domainRegistration.handleOrders(request)
def actualStatusCode = res.statusCode

then:
actualStatusCode == expectedStatusCode
}

def "handleOrders logs an error and continues the usecase like normal when the metadata unique ID is missing because we want to know when our integration with RS is broken"() {
given:

def request = new DomainRequest()
request.headers["RecordId"] = null //no metadata unique ID

def domainRegistration = new EtorDomainRegistration()
TestApplicationContext.register(EtorDomainRegistration, domainRegistration)

def mockController = Mock(OrderController)
TestApplicationContext.register(OrderController, mockController)

def mockUseCase = Mock(SendOrderUseCase)
TestApplicationContext.register(SendOrderUseCase, mockUseCase)

def mockResponseHelper = Mock(DomainResponseHelper)
TestApplicationContext.register(DomainResponseHelper, mockResponseHelper)

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

TestApplicationContext.injectRegisteredImplementations()

when:
domainRegistration.handleOrders(request)

then:
1 * mockLogger.logError(_ as String)
1 * mockController.parseOrders(_ as DomainRequest) >> new OrderMock<?>("DogCow", "Moof", "Clarus")
1 * mockUseCase.convertAndSend(_, null)
}

def "handleOrders logs an error and continues the usecase like normal when the metadata unique ID is empty because we want to know when our integration with RS is broken"() {
given:
def request = new DomainRequest()
request.headers["RecordId"] = "" // empty metadata unique ID

def domainRegistration = new EtorDomainRegistration()
TestApplicationContext.register(EtorDomainRegistration, domainRegistration)

def mockController = Mock(OrderController)
TestApplicationContext.register(OrderController, mockController)

def mockUseCase = Mock(SendOrderUseCase)
TestApplicationContext.register(SendOrderUseCase, mockUseCase)

def mockResponseHelper = Mock(DomainResponseHelper)
TestApplicationContext.register(DomainResponseHelper, mockResponseHelper)

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

TestApplicationContext.injectRegisteredImplementations()

when:
domainRegistration.handleOrders(request)

then:
1 * mockLogger.logError(_ as String)
1 * mockController.parseOrders(_ as DomainRequest) >> new OrderMock<?>("DogCow", "Moof", "Clarus")
1 * mockUseCase.convertAndSend(_, null)
}

def "metadata endpoint happy path"() {
given:
def expectedStatusCode = 200
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class SendOrderUsecaseTest extends Specification {
TestApplicationContext.injectRegisteredImplementations()

when:
SendOrderUseCase.getInstance().convertAndSend(mockOrder)
SendOrderUseCase.getInstance().convertAndSend(mockOrder, _ as String)

then:
1 * mockConverter.convertMetadataToOmlOrder(mockOrder)
Expand All @@ -42,7 +42,7 @@ class SendOrderUsecaseTest extends Specification {
TestApplicationContext.injectRegisteredImplementations()

when:
SendOrderUseCase.getInstance().convertAndSend(new OrderMock(null, null, null))
SendOrderUseCase.getInstance().convertAndSend(new OrderMock(null, null, null), _ as String)

then:
1 * SendOrderUseCase.getInstance().metadata.put(_, EtorMetadataStep.ORDER_CONVERTED_TO_OML)
Expand All @@ -55,7 +55,7 @@ class SendOrderUsecaseTest extends Specification {
TestApplicationContext.injectRegisteredImplementations()

when:
SendOrderUseCase.getInstance().convertAndSend(new OrderMock(null, null, null))
SendOrderUseCase.getInstance().convertAndSend(new OrderMock(null, null, null), _ as String)

then:
1 * SendOrderUseCase.getInstance().metadata.put(_, EtorMetadataStep.CONTACT_SECTION_ADDED_TO_PATIENT)
Expand All @@ -73,7 +73,7 @@ class SendOrderUsecaseTest extends Specification {
TestApplicationContext.injectRegisteredImplementations()

when:
SendOrderUseCase.getInstance().convertAndSend(mockOrder)
SendOrderUseCase.getInstance().convertAndSend(mockOrder, _ as String)

then:
thrown(UnableToSendOrderException)
Expand Down

0 comments on commit a377f16

Please sign in to comment.