diff --git a/e2e/src/test/groovy/gov/hhs/cdc/trustedintermediary/e2e/MetadataTest.groovy b/e2e/src/test/groovy/gov/hhs/cdc/trustedintermediary/e2e/MetadataTest.groovy index 9e064d5b2..cd2147db2 100644 --- a/e2e/src/test/groovy/gov/hhs/cdc/trustedintermediary/e2e/MetadataTest.groovy +++ b/e2e/src/test/groovy/gov/hhs/cdc/trustedintermediary/e2e/MetadataTest.groovy @@ -1,5 +1,6 @@ package gov.hhs.cdc.trustedintermediary.e2e + import spock.lang.Specification import java.nio.file.Files @@ -17,7 +18,6 @@ class MetadataTest extends Specification { given: def expectedStatusCode = 200 def inboundSubmissionId = UUID.randomUUID().toString() - def outboundSubmissionId = "1234567890" def orderClient = new EndpointClient("/v1/etor/orders") def labOrderJsonFileString = Files.readString(Path.of("../examples/Test/Orders/002_ORM_O01.fhir")) @@ -29,8 +29,9 @@ class MetadataTest extends Specification { when: def inboundMetadataResponse = metadataClient.get(inboundSubmissionId, true) - def outboundMetadataResponse = metadataClient.get(outboundSubmissionId, true) def inboundParsedJsonBody = JsonParsing.parseContent(inboundMetadataResponse) + def outboundSubmissionId = inboundParsedJsonBody.issue[8].diagnostics + def outboundMetadataResponse = metadataClient.get(outboundSubmissionId, true) def outboundParsedJsonBody = JsonParsing.parseContent(outboundMetadataResponse) then: @@ -42,10 +43,13 @@ class MetadataTest extends Specification { [ "sender name", "receiver name", - "order ingestion", + "ingestion", "payload hash", "delivery status", - "status message" + "status message", + "message type", + "outbound submission id", + "inbound submission id" ].each { String metadataKey -> def issue = (inboundParsedJsonBody.issue as List).find( {issue -> issue.details.text == metadataKey }) assert issue != null @@ -58,7 +62,6 @@ class MetadataTest extends Specification { given: def expectedStatusCode = 200 def inboundSubmissionId = UUID.randomUUID().toString() - def outboundSubmissionId = "1234567890" def resultClient = new EndpointClient("/v1/etor/results") def labResult = Files.readString(Path.of("../examples/Test/Results/001_ORU_R01.fhir")) @@ -70,8 +73,9 @@ class MetadataTest extends Specification { when: def inboundMetadataResponse = metadataClient.get(inboundSubmissionId, true) - def outboundMetadataResponse = metadataClient.get(outboundSubmissionId, true) def inboundParsedJsonBody = JsonParsing.parseContent(inboundMetadataResponse) + def outboundSubmissionId = inboundParsedJsonBody.issue[8].diagnostics + def outboundMetadataResponse = metadataClient.get(outboundSubmissionId, true) def outboundParsedJsonBody = JsonParsing.parseContent(outboundMetadataResponse) then: @@ -83,10 +87,13 @@ class MetadataTest extends Specification { [ "sender name", "receiver name", - "result ingestion", + "ingestion", "payload hash", "delivery status", - "status message" + "status message", + "message type", + "outbound submission id", + "inbound submission id" ].each { String metadataKey -> def issue = (inboundParsedJsonBody.issue as List).find( {issue -> issue.details.text == metadataKey }) assert issue != null diff --git a/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/EtorDomainRegistration.java b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/EtorDomainRegistration.java index b5e12f788..d4409575a 100644 --- a/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/EtorDomainRegistration.java +++ b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/EtorDomainRegistration.java @@ -15,6 +15,7 @@ import gov.hhs.cdc.trustedintermediary.etor.messages.SendMessageHelper; import gov.hhs.cdc.trustedintermediary.etor.messages.UnableToSendMessageException; import gov.hhs.cdc.trustedintermediary.etor.metadata.partner.PartnerMetadata; +import gov.hhs.cdc.trustedintermediary.etor.metadata.partner.PartnerMetadataConverter; import gov.hhs.cdc.trustedintermediary.etor.metadata.partner.PartnerMetadataException; import gov.hhs.cdc.trustedintermediary.etor.metadata.partner.PartnerMetadataOrchestrator; import gov.hhs.cdc.trustedintermediary.etor.metadata.partner.PartnerMetadataStorage; @@ -38,6 +39,7 @@ import gov.hhs.cdc.trustedintermediary.external.database.PostgresDao; import gov.hhs.cdc.trustedintermediary.external.hapi.HapiMessageConverterHelper; import gov.hhs.cdc.trustedintermediary.external.hapi.HapiOrderConverter; +import gov.hhs.cdc.trustedintermediary.external.hapi.HapiPartnerMetadataConverter; import gov.hhs.cdc.trustedintermediary.external.hapi.HapiResultConverter; import gov.hhs.cdc.trustedintermediary.external.localfile.FilePartnerMetadataStorage; import gov.hhs.cdc.trustedintermediary.external.localfile.MockRSEndpointClient; @@ -81,8 +83,7 @@ public class EtorDomainRegistration implements DomainConnector { @Inject Logger logger; @Inject DomainResponseHelper domainResponseHelper; @Inject PartnerMetadataOrchestrator partnerMetadataOrchestrator; - - @Inject OrderConverter orderConverter; + @Inject PartnerMetadataConverter partnerMetadataConverter; @Inject HapiFhir fhir; @@ -122,6 +123,8 @@ public Map> domainRegistra // Metadata ApplicationContext.register( PartnerMetadataOrchestrator.class, PartnerMetadataOrchestrator.getInstance()); + ApplicationContext.register( + PartnerMetadataConverter.class, HapiPartnerMetadataConverter.getInstance()); // Validation rules ApplicationContext.register(RuleLoader.class, RuleLoader.getInstance()); ApplicationContext.register(RuleEngine.class, RuleEngine.getInstance()); @@ -214,7 +217,7 @@ DomainResponse handleMetadata(DomainRequest request) { } FhirMetadata responseObject = - orderConverter.extractPublicMetadataToOperationOutcome( + partnerMetadataConverter.extractPublicMetadataToOperationOutcome( metadata.get(), metadataId); return domainResponseHelper.constructOkResponseFromString( diff --git a/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/metadata/partner/PartnerMetadataConverter.java b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/metadata/partner/PartnerMetadataConverter.java new file mode 100644 index 000000000..04531e94c --- /dev/null +++ b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/metadata/partner/PartnerMetadataConverter.java @@ -0,0 +1,17 @@ +package gov.hhs.cdc.trustedintermediary.etor.metadata.partner; + +import gov.hhs.cdc.trustedintermediary.etor.operationoutcomes.FhirMetadata; + +public interface PartnerMetadataConverter { + + /** + * This method will convert {@link PartnerMetadata} and convert it into an {@link + * org.hl7.fhir.r4.model.OperationOutcome} + * + * @param metadata + * @param requestedId + * @return + */ + FhirMetadata extractPublicMetadataToOperationOutcome( + PartnerMetadata metadata, String requestedId); +} diff --git a/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/orders/OrderConverter.java b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/orders/OrderConverter.java index 8ab155978..684652130 100644 --- a/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/orders/OrderConverter.java +++ b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/orders/OrderConverter.java @@ -1,8 +1,6 @@ package gov.hhs.cdc.trustedintermediary.etor.orders; import gov.hhs.cdc.trustedintermediary.etor.demographics.Demographics; -import gov.hhs.cdc.trustedintermediary.etor.metadata.partner.PartnerMetadata; -import gov.hhs.cdc.trustedintermediary.etor.operationoutcomes.FhirMetadata; /** Interface for converting things to orders and things in orders. */ public interface OrderConverter { @@ -13,7 +11,4 @@ public interface OrderConverter { Order addContactSectionToPatientResource(Order order); Order addEtorProcessingTag(Order message); - - FhirMetadata extractPublicMetadataToOperationOutcome( - PartnerMetadata metadata, String requestedId); } diff --git a/etor/src/main/java/gov/hhs/cdc/trustedintermediary/external/hapi/HapiOrderConverter.java b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/external/hapi/HapiOrderConverter.java index 47c17fb61..fdb0ff7d9 100644 --- a/etor/src/main/java/gov/hhs/cdc/trustedintermediary/external/hapi/HapiOrderConverter.java +++ b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/external/hapi/HapiOrderConverter.java @@ -1,9 +1,6 @@ package gov.hhs.cdc.trustedintermediary.external.hapi; import gov.hhs.cdc.trustedintermediary.etor.demographics.Demographics; -import gov.hhs.cdc.trustedintermediary.etor.metadata.partner.PartnerMetadata; -import gov.hhs.cdc.trustedintermediary.etor.operationoutcomes.FhirMetadata; -import gov.hhs.cdc.trustedintermediary.etor.operationoutcomes.HapiFhirMetadata; import gov.hhs.cdc.trustedintermediary.etor.orders.Order; import gov.hhs.cdc.trustedintermediary.etor.orders.OrderConverter; import gov.hhs.cdc.trustedintermediary.wrappers.Logger; @@ -18,7 +15,6 @@ import org.hl7.fhir.r4.model.Identifier; import org.hl7.fhir.r4.model.MessageHeader; import org.hl7.fhir.r4.model.Meta; -import org.hl7.fhir.r4.model.OperationOutcome; import org.hl7.fhir.r4.model.Patient; import org.hl7.fhir.r4.model.Provenance; import org.hl7.fhir.r4.model.Reference; @@ -206,68 +202,4 @@ private Provenance createProvenanceResource(Date orderDate) { return provenance; } - - @Override - public FhirMetadata extractPublicMetadataToOperationOutcome( - PartnerMetadata metadata, String requestedId) { - var operation = new OperationOutcome(); - - operation.setId(requestedId); - operation.getIssue().add(createInformationIssueComponent("sender name", metadata.sender())); - operation - .getIssue() - .add(createInformationIssueComponent("receiver name", metadata.receiver())); - - String ingestion = null; - String delivered = null; - if (metadata.timeReceived() != null) { - ingestion = metadata.timeReceived().toString(); - } - if (metadata.timeDelivered() != null) { - delivered = metadata.timeDelivered().toString(); - } - - operation - .getIssue() - .add( - createInformationIssueComponent( - String.format( - "%s ingestion", - metadata.messageType().toString().toLowerCase()), - ingestion)); - - operation.getIssue().add(createInformationIssueComponent("payload hash", metadata.hash())); - operation - .getIssue() - .add( - createInformationIssueComponent( - String.format( - "%s delivered", - metadata.messageType().toString().toLowerCase()), - delivered)); - operation - .getIssue() - .add( - createInformationIssueComponent( - "delivery status", metadata.deliveryStatus().toString())); - - operation - .getIssue() - .add(createInformationIssueComponent("status message", metadata.failureReason())); - - return new HapiFhirMetadata(operation); - } - - protected OperationOutcome.OperationOutcomeIssueComponent createInformationIssueComponent( - String details, String diagnostics) { - OperationOutcome.OperationOutcomeIssueComponent issue = - new OperationOutcome.OperationOutcomeIssueComponent(); - - issue.setSeverity(OperationOutcome.IssueSeverity.INFORMATION); - issue.setCode(OperationOutcome.IssueType.INFORMATIONAL); - issue.getDetails().setText(details); - issue.setDiagnostics(diagnostics); - - return issue; - } } diff --git a/etor/src/main/java/gov/hhs/cdc/trustedintermediary/external/hapi/HapiPartnerMetadataConverter.java b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/external/hapi/HapiPartnerMetadataConverter.java new file mode 100644 index 000000000..63b10ba95 --- /dev/null +++ b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/external/hapi/HapiPartnerMetadataConverter.java @@ -0,0 +1,86 @@ +package gov.hhs.cdc.trustedintermediary.external.hapi; + +import gov.hhs.cdc.trustedintermediary.etor.metadata.partner.PartnerMetadata; +import gov.hhs.cdc.trustedintermediary.etor.metadata.partner.PartnerMetadataConverter; +import gov.hhs.cdc.trustedintermediary.etor.operationoutcomes.FhirMetadata; +import gov.hhs.cdc.trustedintermediary.etor.operationoutcomes.HapiFhirMetadata; +import org.hl7.fhir.r4.model.OperationOutcome; + +public class HapiPartnerMetadataConverter implements PartnerMetadataConverter { + + private static final HapiPartnerMetadataConverter INSTANCE = new HapiPartnerMetadataConverter(); + + public static HapiPartnerMetadataConverter getInstance() { + return INSTANCE; + } + + private HapiPartnerMetadataConverter() {} + + @Override + public FhirMetadata extractPublicMetadataToOperationOutcome( + PartnerMetadata metadata, String requestedId) { + var operation = new OperationOutcome(); + + operation.setId(requestedId); + operation.getIssue().add(createInformationIssueComponent("sender name", metadata.sender())); + operation + .getIssue() + .add(createInformationIssueComponent("receiver name", metadata.receiver())); + + String ingestion = null; + String delivered = null; + if (metadata.timeReceived() != null) { + ingestion = metadata.timeReceived().toString(); + } + if (metadata.timeDelivered() != null) { + delivered = metadata.timeDelivered().toString(); + } + + operation.getIssue().add(createInformationIssueComponent("ingestion", ingestion)); + + operation.getIssue().add(createInformationIssueComponent("payload hash", metadata.hash())); + operation.getIssue().add(createInformationIssueComponent("delivered", delivered)); + operation + .getIssue() + .add( + createInformationIssueComponent( + "delivery status", metadata.deliveryStatus().toString())); + + operation + .getIssue() + .add(createInformationIssueComponent("status message", metadata.failureReason())); + + operation + .getIssue() + .add( + createInformationIssueComponent( + "message type", metadata.messageType().toString())); + + operation + .getIssue() + .add( + createInformationIssueComponent( + "outbound submission id", metadata.sentSubmissionId())); + + operation + .getIssue() + .add( + createInformationIssueComponent( + "inbound submission id", metadata.receivedSubmissionId())); + + return new HapiFhirMetadata(operation); + } + + protected OperationOutcome.OperationOutcomeIssueComponent createInformationIssueComponent( + String details, String diagnostics) { + OperationOutcome.OperationOutcomeIssueComponent issue = + new OperationOutcome.OperationOutcomeIssueComponent(); + + issue.setSeverity(OperationOutcome.IssueSeverity.INFORMATION); + issue.setCode(OperationOutcome.IssueType.INFORMATIONAL); + issue.getDetails().setText(details); + issue.setDiagnostics(diagnostics); + + return issue; + } +} diff --git a/etor/src/main/java/gov/hhs/cdc/trustedintermediary/external/localfile/MockRSEndpointClient.java b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/external/localfile/MockRSEndpointClient.java index 92205613d..d16bc7077 100644 --- a/etor/src/main/java/gov/hhs/cdc/trustedintermediary/external/localfile/MockRSEndpointClient.java +++ b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/external/localfile/MockRSEndpointClient.java @@ -6,6 +6,7 @@ import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Paths; +import java.util.UUID; /** * A mock implementation of the RSEndpointClient interface that doesn't require a connection to @@ -37,7 +38,7 @@ public String requestWatersEndpoint(String body, String bearerToken) } catch (IOException e) { throw new ReportStreamEndpointClientException("Error writing the lab order", e); } - return "{ \"submissionId\": \"1234567890\" }"; + return "{ \"submissionId\": \"" + UUID.randomUUID() + "\" }"; } @Override diff --git a/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/etor/EtorDomainRegistrationTest.groovy b/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/etor/EtorDomainRegistrationTest.groovy index e8fb4d76b..d8992342a 100644 --- a/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/etor/EtorDomainRegistrationTest.groovy +++ b/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/etor/EtorDomainRegistrationTest.groovy @@ -15,6 +15,7 @@ import gov.hhs.cdc.trustedintermediary.etor.demographics.PatientDemographicsResp import gov.hhs.cdc.trustedintermediary.etor.messages.MessageRequestHandler import gov.hhs.cdc.trustedintermediary.etor.messages.UnableToSendMessageException import gov.hhs.cdc.trustedintermediary.etor.metadata.partner.PartnerMetadata +import gov.hhs.cdc.trustedintermediary.etor.metadata.partner.PartnerMetadataConverter import gov.hhs.cdc.trustedintermediary.etor.metadata.partner.PartnerMetadataException import gov.hhs.cdc.trustedintermediary.etor.metadata.partner.PartnerMetadataMessageType import gov.hhs.cdc.trustedintermediary.etor.metadata.partner.PartnerMetadataOrchestrator @@ -240,9 +241,9 @@ class EtorDomainRegistrationTest extends Specification { mockResponseHelper.constructOkResponseFromString(_ as String) >> new DomainResponse(expectedStatusCode) TestApplicationContext.register(DomainResponseHelper, mockResponseHelper) - def mockOrderConverter = Mock(OrderConverter) - mockOrderConverter.extractPublicMetadataToOperationOutcome(_ as PartnerMetadata, _ as String) >> Mock(FhirMetadata) - TestApplicationContext.register(OrderConverter, mockOrderConverter) + def mockPartnerMetadataConverter = Mock(PartnerMetadataConverter) + mockPartnerMetadataConverter.extractPublicMetadataToOperationOutcome(_ as PartnerMetadata, _ as String) >> Mock(FhirMetadata) + TestApplicationContext.register(PartnerMetadataConverter, mockPartnerMetadataConverter) def mockFhir = Mock(HapiFhir) mockFhir.encodeResourceToJson(_) >> "" diff --git a/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/external/hapi/HapiMetadataConverterTest.groovy b/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/external/hapi/HapiMetadataConverterTest.groovy new file mode 100644 index 000000000..937ef38eb --- /dev/null +++ b/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/external/hapi/HapiMetadataConverterTest.groovy @@ -0,0 +1,50 @@ +package gov.hhs.cdc.trustedintermediary.external.hapi + +import gov.hhs.cdc.trustedintermediary.etor.metadata.partner.PartnerMetadata +import gov.hhs.cdc.trustedintermediary.etor.metadata.partner.PartnerMetadataMessageType +import gov.hhs.cdc.trustedintermediary.etor.metadata.partner.PartnerMetadataStatus +import org.hl7.fhir.r4.model.OperationOutcome +import spock.lang.Specification + +import java.time.Instant + +class HapiMetadataConverterTest extends Specification { + def "creating an issue returns a valid OperationOutcomeIssueComponent with Information level severity and code" () { + when: + def output = HapiPartnerMetadataConverter.getInstance().createInformationIssueComponent("test_details", "test_diagnostics") + then: + output.getSeverity() == OperationOutcome.IssueSeverity.INFORMATION + output.getCode() == OperationOutcome.IssueType.INFORMATIONAL + output.getDetails().getText() == "test_details" + output.getDiagnostics() == "test_diagnostics" + } + + def "ExtractPublicMetadata to OperationOutcome returns FHIR metadata"() { + given: + + def sender = "sender" + def receiver = "receiver" + def time = Instant.now() + def hash = "hash" + def failureReason = "timed_out" + def messageType = PartnerMetadataMessageType.ORDER + PartnerMetadata metadata = new PartnerMetadata( + "receivedSubmissionId", "sentSubmissionId", sender, receiver, time, time, hash, PartnerMetadataStatus.DELIVERED, failureReason, messageType) + + when: + def result = HapiPartnerMetadataConverter.getInstance().extractPublicMetadataToOperationOutcome(metadata, "receivedSubmissionId").getUnderlyingOutcome() as OperationOutcome + + then: + result.getId() == "receivedSubmissionId" + result.getIssue().get(0).diagnostics == sender + result.getIssue().get(1).diagnostics == receiver + result.getIssue().get(2).diagnostics == time.toString() + result.getIssue().get(3).diagnostics == hash + result.getIssue().get(4).diagnostics == time.toString() + result.getIssue().get(5).diagnostics == PartnerMetadataStatus.DELIVERED.toString() + result.getIssue().get(6).diagnostics == failureReason + result.getIssue().get(7).diagnostics == messageType.toString() + result.getIssue().get(8).diagnostics == "sentSubmissionId" + result.getIssue().get(9).diagnostics == "receivedSubmissionId" + } +} diff --git a/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/external/hapi/HapiOrderConverterTest.groovy b/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/external/hapi/HapiOrderConverterTest.groovy index b6db07157..3c2a8d3a2 100644 --- a/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/external/hapi/HapiOrderConverterTest.groovy +++ b/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/external/hapi/HapiOrderConverterTest.groovy @@ -273,17 +273,6 @@ class HapiOrderConverterTest extends Specification { !contactSection.hasName() } - - def "creating an issue returns a valid OperationOutcomeIssueComponent with Information level severity and code" () { - when: - def output = HapiOrderConverter.getInstance().createInformationIssueComponent("test_details", "test_diagnostics") - then: - output.getSeverity() == OperationOutcome.IssueSeverity.INFORMATION - output.getCode() == OperationOutcome.IssueType.INFORMATIONAL - output.getDetails().getText() == "test_details" - output.getDiagnostics() == "test_diagnostics" - } - Patient fakePatientResource(boolean addHumanName) { def patient = new Patient() @@ -326,31 +315,4 @@ class HapiOrderConverterTest extends Specification { return patient } - - def "ExtractPublicMetadata to OperationOutcome returns FHIR metadata"() { - given: - - def sender = "sender" - def receiver = "receiver" - def time = Instant.now() - def hash = "hash" - def failureReason = "timed_out" - def messageType = PartnerMetadataMessageType.ORDER - PartnerMetadata metadata = new PartnerMetadata( - "receivedSubmissionId", "sentSubmissionId", sender, receiver, time, time, hash, PartnerMetadataStatus.DELIVERED, failureReason, messageType) - - when: - def result = HapiOrderConverter.getInstance().extractPublicMetadataToOperationOutcome(metadata, "receivedSubmissionId").getUnderlyingOutcome() as OperationOutcome - - then: - result.getId() == "receivedSubmissionId" - result.getIssue().get(0).diagnostics == sender - result.getIssue().get(1).diagnostics == receiver - result.getIssue().get(2).diagnostics == time.toString() - result.getIssue().get(3).diagnostics == hash - result.getIssue().get(4).diagnostics == time.toString() - result.getIssue().get(5).diagnostics == PartnerMetadataStatus.DELIVERED.toString() - result.getIssue().get(6).diagnostics == failureReason - result.getIssue().get(4).details.text.contains(messageType.toString().toLowerCase()) - } }