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 bf3d0eaef..7b68b66aa 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.metadata.PartnerMetadataException; import gov.hhs.cdc.trustedintermediary.etor.metadata.PartnerMetadataOrchestrator; import gov.hhs.cdc.trustedintermediary.etor.metadata.PartnerMetadataStorage; +import gov.hhs.cdc.trustedintermediary.etor.metadata.partner.PartnerMetadataStatus; import gov.hhs.cdc.trustedintermediary.etor.operationoutcomes.FhirMetadata; import gov.hhs.cdc.trustedintermediary.etor.orders.Order; import gov.hhs.cdc.trustedintermediary.etor.orders.OrderController; @@ -155,15 +156,27 @@ DomainResponse handleOrders(DomainRequest request) { logger.logError("Missing required header or empty: RecordId"); } + var markMetadataAsFailed = false; try { orders = orderController.parseOrders(request); sendOrderUseCase.convertAndSend(orders, receivedSubmissionId); } catch (FhirParseException e) { logger.logError("Unable to parse order request", e); + markMetadataAsFailed = true; return domainResponseHelper.constructErrorResponse(400, e); } catch (UnableToSendOrderException e) { logger.logError("Unable to send order", e); + markMetadataAsFailed = true; return domainResponseHelper.constructErrorResponse(400, e); + } finally { + if (markMetadataAsFailed) { + try { + partnerMetadataOrchestrator.setMetadataStatus( + receivedSubmissionId, PartnerMetadataStatus.FAILED); + } catch (PartnerMetadataException innerE) { + logger.logError("Unable to update metadata status", innerE); + } + } } OrderResponse orderResponse = new OrderResponse(orders); diff --git a/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/metadata/PartnerMetadata.java b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/metadata/PartnerMetadata.java index 069070102..a2ee1e6b6 100644 --- a/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/metadata/PartnerMetadata.java +++ b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/metadata/PartnerMetadata.java @@ -43,6 +43,10 @@ public PartnerMetadata(String receivedSubmissionId, String hash) { this(receivedSubmissionId, null, null, null, null, hash, null); } + public PartnerMetadata(String receivedSubmissionId, PartnerMetadataStatus deliveryStatus) { + this(receivedSubmissionId, null, null, null, null, null, deliveryStatus); + } + public PartnerMetadata withSentSubmissionId(String sentSubmissionId) { return new PartnerMetadata( this.receivedSubmissionId, @@ -64,4 +68,15 @@ public PartnerMetadata withReceiver(String receiver) { this.hash, this.deliveryStatus); } + + public PartnerMetadata withDeliveryStatus(PartnerMetadataStatus deliveryStatus) { + return new PartnerMetadata( + this.receivedSubmissionId, + this.sentSubmissionId, + this.sender, + this.receiver, + this.timeReceived, + this.hash, + deliveryStatus); + } } diff --git a/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/metadata/PartnerMetadataOrchestrator.java b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/metadata/PartnerMetadataOrchestrator.java index 13bf4f30d..2519557ec 100644 --- a/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/metadata/PartnerMetadataOrchestrator.java +++ b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/metadata/PartnerMetadataOrchestrator.java @@ -142,6 +142,33 @@ public Optional getMetadata(String receivedSubmissionId) return Optional.of(partnerMetadata); } + public void setMetadataStatus(String submissionId, PartnerMetadataStatus metadataStatus) + throws PartnerMetadataException { + if (submissionId == null) { + return; + } + + Optional optionalPartnerMetadata = + partnerMetadataStorage.readMetadata(submissionId); + PartnerMetadata partnerMetadata; + if (optionalPartnerMetadata.isEmpty()) { + // there wasn't any metadata given the submission ID, so make one with the status + partnerMetadata = new PartnerMetadata(submissionId, metadataStatus); + } else { + partnerMetadata = optionalPartnerMetadata.get(); + if (partnerMetadata.deliveryStatus().equals(metadataStatus)) { + return; + } + } + + logger.logInfo( + "Updating metadata delivery status {} with submissionId: {}", + metadataStatus, + submissionId); + partnerMetadata = partnerMetadata.withDeliveryStatus(metadataStatus); + partnerMetadataStorage.saveMetadata(partnerMetadata); + } + String getReceiverName(String responseBody) throws FormatterProcessingException { // the expected json structure for the response is: // { 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 3f3117830..932eddd74 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 @@ -213,6 +213,9 @@ class EtorDomainRegistrationTest extends Specification { mockResponseHelper.constructErrorResponse(expectedStatusCode, _ as Exception) >> new DomainResponse(expectedStatusCode) TestApplicationContext.register(DomainResponseHelper, mockResponseHelper) + def mockPartnerMetadataOrchestrator = Mock(PartnerMetadataOrchestrator) + TestApplicationContext.register(PartnerMetadataOrchestrator,mockPartnerMetadataOrchestrator) + TestApplicationContext.injectRegisteredImplementations() when: @@ -241,6 +244,49 @@ class EtorDomainRegistrationTest extends Specification { mockResponseHelper.constructErrorResponse(expectedStatusCode, _ as Exception) >> new DomainResponse(expectedStatusCode) TestApplicationContext.register(DomainResponseHelper, mockResponseHelper) + def mockPartnerMetadataOrchestrator = Mock(PartnerMetadataOrchestrator) + TestApplicationContext.register(PartnerMetadataOrchestrator,mockPartnerMetadataOrchestrator) + + TestApplicationContext.injectRegisteredImplementations() + + when: + def res = domainRegistration.handleOrders(request) + def actualStatusCode = res.statusCode + + then: + actualStatusCode == expectedStatusCode + } + + def "handleOrders generates an error response when not being able to send the order even when setMetadataStatus throws an exception"() { + given: + def expectedStatusCode = 400 + + def request = new DomainRequest() + request.headers["recordid"] = "recordId" + + def domainRegistration = new EtorDomainRegistration() + TestApplicationContext.register(EtorDomainRegistration, domainRegistration) + + def mockController = Mock(OrderController) + mockController.parseOrders(_ as DomainRequest) >> new OrderMock(null, null, null) + TestApplicationContext.register(OrderController, mockController) + + def mockUseCase = Mock(SendOrderUseCase) + mockUseCase.convertAndSend(_ as Order, _ as String) >> { + throw new UnableToSendOrderException("error", new NullPointerException()) + } + TestApplicationContext.register(SendOrderUseCase, mockUseCase) + + def mockResponseHelper = Mock(DomainResponseHelper) + mockResponseHelper.constructErrorResponse(expectedStatusCode, _ as UnableToSendOrderException) >> new DomainResponse(expectedStatusCode) + TestApplicationContext.register(DomainResponseHelper, mockResponseHelper) + + def mockPartnerMetadataOrchestrator = Mock(PartnerMetadataOrchestrator) + mockPartnerMetadataOrchestrator.setMetadataStatus(_, PartnerMetadataStatus.FAILED) >> { + throw new PartnerMetadataException("error") + } + TestApplicationContext.register(PartnerMetadataOrchestrator,mockPartnerMetadataOrchestrator) + TestApplicationContext.injectRegisteredImplementations() when: diff --git a/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/etor/metadata/PartnerMetadataOrchestratorTest.groovy b/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/etor/metadata/PartnerMetadataOrchestratorTest.groovy index 3dcc755cc..9b35d8632 100644 --- a/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/etor/metadata/PartnerMetadataOrchestratorTest.groovy +++ b/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/etor/metadata/PartnerMetadataOrchestratorTest.groovy @@ -265,6 +265,61 @@ class PartnerMetadataOrchestratorTest extends Specification { 1 * mockPartnerMetadataStorage.saveMetadata(expectedMetadata) } + def "setMetadataStatus sets status to Pending"(){ + given: + def submissionId = "13425" + def metadataStatus = PartnerMetadataStatus.PENDING + def optional = Optional.of(new PartnerMetadata("","","","",Instant.now(),"",PartnerMetadataStatus.FAILED)) + mockPartnerMetadataStorage.readMetadata(submissionId) >> optional + + when: + PartnerMetadataOrchestrator.getInstance().setMetadataStatus(submissionId,metadataStatus) + + then: + 1 * mockPartnerMetadataStorage.saveMetadata(_ as PartnerMetadata) >> { PartnerMetadata partnerMetadata -> + assert partnerMetadata.deliveryStatus() == metadataStatus + } + } + + def "setMetadataStatus doesn't update status if status is the same"(){ + given: + def submissionId = "13425" + def metadataStatus = PartnerMetadataStatus.PENDING + def optional = Optional.of(new PartnerMetadata("","","","",Instant.now(),"",metadataStatus)) + mockPartnerMetadataStorage.readMetadata(submissionId) >> optional + + when: + PartnerMetadataOrchestrator.getInstance().setMetadataStatus(submissionId,metadataStatus) + + then: + 0 * mockPartnerMetadataStorage.saveMetadata(_ as PartnerMetadata) + } + + def "setMetadataStatus doesn't update when submissionId is null"(){ + when: + PartnerMetadataOrchestrator.getInstance().setMetadataStatus(null,PartnerMetadataStatus.DELIVERED) + + then: + 0 * mockPartnerMetadataStorage.saveMetadata(_ as PartnerMetadata) + } + + def "setMetadataStatus sets status to Pending when there is no metadata"(){ + given: + def submissionId = "13425" + def metadataStatus = PartnerMetadataStatus.DELIVERED + def optional = Optional.empty() + mockPartnerMetadataStorage.readMetadata(submissionId) >> optional + + when: + PartnerMetadataOrchestrator.getInstance().setMetadataStatus(submissionId,metadataStatus) + + then: + 1 * mockPartnerMetadataStorage.saveMetadata(_ as PartnerMetadata) >> { PartnerMetadata partnerMetadata -> + assert partnerMetadata.deliveryStatus() == metadataStatus + assert partnerMetadata.receivedSubmissionId() == submissionId + } + } + def "getReceiverName returns correct receiver name from valid JSON response"() { given: def validJson = "{\"destinations\": [{\"organization_id\": \"org_id\", \"service\": \"service_name\"}]}" diff --git a/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/etor/metadata/PartnerMetadataTest.groovy b/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/etor/metadata/PartnerMetadataTest.groovy index 827d25a0b..f304c3831 100644 --- a/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/etor/metadata/PartnerMetadataTest.groovy +++ b/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/etor/metadata/PartnerMetadataTest.groovy @@ -78,6 +78,24 @@ class PartnerMetadataTest extends Specification { metadata.deliveryStatus() == PartnerMetadataStatus.PENDING } + def "test constructor with only received submission ID and status"() { + given: + def receivedSubmissionId = "receivedSubmissionId" + def deliverStatus = PartnerMetadataStatus.DELIVERED + + when: + def metadata = new PartnerMetadata(receivedSubmissionId, deliverStatus) + + then: + metadata.receivedSubmissionId() == receivedSubmissionId + metadata.sentSubmissionId() == null + metadata.sender() == null + metadata.receiver() == null + metadata.timeReceived() == null + metadata.hash() == null + metadata.deliveryStatus() == deliverStatus + } + def "test withSentSubmissionId and withReceiver to update PartnerMetadata"() { given: def receivedSubmissionId = "receivedSubmissionId" @@ -101,4 +119,28 @@ class PartnerMetadataTest extends Specification { updatedMetadata.hash() == hash updatedMetadata.deliveryStatus() == status } + + def "test withDeliveryStatus to update PartnerMetadata"() { + given: + def receivedSubmissionId = "receivedSubmissionId" + def sentSubmissionId = "sentSubmissionId" + def sender = "sender" + def receiver = "receiver" + def timeReceived = Instant.now() + def hash = "abcd" + def metadata = new PartnerMetadata(receivedSubmissionId, sentSubmissionId, sender, receiver, timeReceived, hash, PartnerMetadataStatus.PENDING) + + when: + def newStatus = PartnerMetadataStatus.DELIVERED + def updatedMetadata = metadata.withSentSubmissionId(sentSubmissionId).withDeliveryStatus(newStatus) + + then: + updatedMetadata.receivedSubmissionId() == receivedSubmissionId + updatedMetadata.sentSubmissionId() == sentSubmissionId + updatedMetadata.sender() == sender + updatedMetadata.receiver() == receiver + updatedMetadata.timeReceived() == timeReceived + updatedMetadata.hash() == hash + updatedMetadata.deliveryStatus() == newStatus + } }