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 2519557ec..8de48f715 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 @@ -118,24 +118,31 @@ public Optional getMetadata(String receivedSubmissionId) PartnerMetadata partnerMetadata = optionalPartnerMetadata.get(); var sentSubmissionId = partnerMetadata.sentSubmissionId(); - if (partnerMetadata.receiver() == null && sentSubmissionId != null) { + if ((partnerMetadata.receiver() == null + || partnerMetadata.deliveryStatus() == PartnerMetadataStatus.PENDING) + && sentSubmissionId != null) { logger.logInfo( - "Receiver name not found in metadata, looking up {} from RS history API", + "Receiver name not found in metadata or delivery status still pending, looking up {} from RS history API", sentSubmissionId); String receiver; + String rsStatus; try { String bearerToken = rsclient.getRsToken(); String responseBody = rsclient.requestHistoryEndpoint(sentSubmissionId, bearerToken); - receiver = getReceiverName(responseBody); + var parsedResponseBody = getReceiverAndStatus(responseBody); + receiver = parsedResponseBody[0]; + rsStatus = parsedResponseBody[1]; } catch (ReportStreamEndpointClientException | FormatterProcessingException e) { throw new PartnerMetadataException( "Unable to retrieve metadata from RS history API", e); } - logger.logInfo("Updating metadata with receiver: {}", receiver); - partnerMetadata = partnerMetadata.withReceiver(receiver); + var ourStatus = ourStatusFromReportStreamStatus(rsStatus); + + logger.logInfo("Updating metadata with receiver {} and status {}", receiver, ourStatus); + partnerMetadata = partnerMetadata.withReceiver(receiver).withDeliveryStatus(ourStatus); partnerMetadataStorage.saveMetadata(partnerMetadata); } @@ -169,10 +176,12 @@ public void setMetadataStatus(String submissionId, PartnerMetadataStatus metadat partnerMetadataStorage.saveMetadata(partnerMetadata); } - String getReceiverName(String responseBody) throws FormatterProcessingException { + String[] getReceiverAndStatus(String responseBody) throws FormatterProcessingException { // the expected json structure for the response is: // { // ... + // "overallStatus": "Waiting to Deliver", + // ... // "destinations" : [ { // ... // "organization_id" : "flexion", @@ -182,23 +191,46 @@ String getReceiverName(String responseBody) throws FormatterProcessingException // ... // } - String organizationId; - String service; + Map responseObject = + formatter.convertJsonToObject(responseBody, new TypeReference<>() {}); + + String receiver; try { - Map responseObject = - formatter.convertJsonToObject(responseBody, new TypeReference<>() {}); ArrayList destinations = (ArrayList) responseObject.get("destinations"); Map destination = (Map) destinations.get(0); - organizationId = destination.get("organization_id").toString(); - service = destination.get("service").toString(); + String organizationId = destination.get("organization_id").toString(); + String service = destination.get("service").toString(); + receiver = organizationId + "." + service; } catch (IndexOutOfBoundsException e) { // the destinations have not been determined yet by RS - return null; + receiver = null; } catch (Exception e) { throw new FormatterProcessingException( "Unable to extract receiver name from response due to unexpected format", e); } - return organizationId + "." + service; + String overallStatus; + try { + overallStatus = (String) responseObject.get("overallStatus"); + } catch (Exception e) { + throw new FormatterProcessingException( + "Unable to extract overallStatus from response due to unexpected format", e); + } + + return new String[] {receiver, overallStatus}; + } + + PartnerMetadataStatus ourStatusFromReportStreamStatus(String rsStatus) { + if (rsStatus == null) { + return PartnerMetadataStatus.PENDING; + } + + // based off of the Status enum in the SubmissionHistory.kt code in RS + // https://github.com/CDCgov/prime-reportstream/blob/master/prime-router/src/main/kotlin/history/SubmissionHistory.kt + return switch (rsStatus) { + case "Error", "Not Delivering" -> PartnerMetadataStatus.FAILED; + case "Delivered" -> PartnerMetadataStatus.DELIVERED; + default -> PartnerMetadataStatus.PENDING; + }; } } 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 9b35d8632..5c0af8931 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 @@ -251,7 +251,40 @@ class PartnerMetadataOrchestratorTest extends Specification { mockClient.getRsToken() >> bearerToken mockClient.requestHistoryEndpoint(sentSubmissionId, bearerToken) >> rsHistoryApiResponse - mockFormatter.convertJsonToObject(rsHistoryApiResponse, _ as TypeReference) >> [destinations: [ + mockFormatter.convertJsonToObject(rsHistoryApiResponse, _ as TypeReference) >> [ + overallStatus: "Delivered", + destinations: [ + [organization_id: "org", service: "service"] + ]] + + when: + Optional result = PartnerMetadataOrchestrator.getInstance().getMetadata(receivedSubmissionId) + + then: + result.isPresent() + result.get() == expectedMetadata + 1 * mockPartnerMetadataStorage.readMetadata(receivedSubmissionId) >> Optional.of(missingReceiverMetadata) + 1 * mockPartnerMetadataStorage.saveMetadata(expectedMetadata) + } + + def "getMetadata gets status if still pending in metadata"() { + given: + def receivedSubmissionId = "receivedSubmissionId" + def sentSubmissionId = "sentSubmissionId" + def sender = "senderName" + def receiver = "org.service" + def timestamp = Instant.now() + def hashCode = "123" + def bearerToken = "token" + def rsHistoryApiResponse = "{\"destinations\": [{\"organization_id\": \"org\", \"service\": \"service\"}]}" + def missingReceiverMetadata = new PartnerMetadata(receivedSubmissionId, sentSubmissionId, sender, receiver, timestamp, hashCode, PartnerMetadataStatus.PENDING) + def expectedMetadata = new PartnerMetadata(receivedSubmissionId, sentSubmissionId, sender, receiver, timestamp, hashCode, PartnerMetadataStatus.FAILED) + + mockClient.getRsToken() >> bearerToken + mockClient.requestHistoryEndpoint(sentSubmissionId, bearerToken) >> rsHistoryApiResponse + mockFormatter.convertJsonToObject(rsHistoryApiResponse, _ as TypeReference) >> [ + overallStatus: "Not Delivering", + destinations: [ [organization_id: "org", service: "service"] ]] @@ -320,42 +353,46 @@ class PartnerMetadataOrchestratorTest extends Specification { } } - def "getReceiverName returns correct receiver name from valid JSON response"() { + def "getReceiverAndStatus returns correct status name and receiver name from valid JSON response"() { given: - def validJson = "{\"destinations\": [{\"organization_id\": \"org_id\", \"service\": \"service_name\"}]}" + def organization = "org_id" + def sender = "service_name" + def status = "Not Delivering" + def validJson = """{"overallStatus": "${status}", "destinations": [{"organization_id": "${organization}", "service": "${sender}"}]}""" TestApplicationContext.register(Formatter, Jackson.getInstance()) TestApplicationContext.injectRegisteredImplementations() when: - def receiverName = PartnerMetadataOrchestrator.getInstance().getReceiverName(validJson) + def parsedResponse = PartnerMetadataOrchestrator.getInstance().getReceiverAndStatus(validJson) then: - receiverName == "org_id.service_name" + parsedResponse[0] == "${organization}.${sender}" + parsedResponse[1] == status } - def "getReceiverName throws FormatterProcessingException or returns null for unexpected format response"() { + def "getReceiverAndStatus throws FormatterProcessingException or returns null for unexpected format response"() { given: TestApplicationContext.register(Formatter, Jackson.getInstance()) TestApplicationContext.injectRegisteredImplementations() when: def invalidJson = "invalid JSON" - PartnerMetadataOrchestrator.getInstance().getReceiverName(invalidJson) + PartnerMetadataOrchestrator.getInstance().getReceiverAndStatus(invalidJson) then: thrown(FormatterProcessingException) when: def emptyJson = "{}" - PartnerMetadataOrchestrator.getInstance().getReceiverName(emptyJson) + PartnerMetadataOrchestrator.getInstance().getReceiverAndStatus(emptyJson) then: thrown(FormatterProcessingException) when: def jsonWithoutDestinations = "{\"someotherkey\": \"value\"}" - PartnerMetadataOrchestrator.getInstance().getReceiverName(jsonWithoutDestinations) + PartnerMetadataOrchestrator.getInstance().getReceiverAndStatus(jsonWithoutDestinations) then: thrown(FormatterProcessingException) @@ -363,23 +400,73 @@ class PartnerMetadataOrchestratorTest extends Specification { when: def jsonWithEmptyDestinations = "{\"destinations\": []}" - def receiverName = PartnerMetadataOrchestrator.getInstance().getReceiverName(jsonWithEmptyDestinations) + def parsedData = PartnerMetadataOrchestrator.getInstance().getReceiverAndStatus(jsonWithEmptyDestinations) + + then: + parsedData[0] == null + + when: + + def jsonWithNoStatus = "{\"destinations\": []}" + parsedData = PartnerMetadataOrchestrator.getInstance().getReceiverAndStatus(jsonWithNoStatus) then: - receiverName == null + parsedData[1] == null when: def jsonWithoutOrgId = "{\"destinations\":[{\"service\":\"service\"}]}" - PartnerMetadataOrchestrator.getInstance().getReceiverName(jsonWithoutOrgId) + PartnerMetadataOrchestrator.getInstance().getReceiverAndStatus(jsonWithoutOrgId) then: thrown(FormatterProcessingException) when: def jsonWithoutService = "{\"destinations\":[{\"organization_id\":\"org_id\"}]}" - PartnerMetadataOrchestrator.getInstance().getReceiverName(jsonWithoutService) + PartnerMetadataOrchestrator.getInstance().getReceiverAndStatus(jsonWithoutService) then: thrown(FormatterProcessingException) } + + def "ourStatusFromReportStreamStatus returns FAILED"() { + when: + def ourStatus = PartnerMetadataOrchestrator.getInstance().ourStatusFromReportStreamStatus("Error") + + then: + ourStatus == PartnerMetadataStatus.FAILED + + when: + ourStatus = PartnerMetadataOrchestrator.getInstance().ourStatusFromReportStreamStatus("Not Delivering") + + then: + ourStatus == PartnerMetadataStatus.FAILED + } + + def "ourStatusFromReportStreamStatus returns DELIVERED"() { + when: + def ourStatus = PartnerMetadataOrchestrator.getInstance().ourStatusFromReportStreamStatus("Delivered") + + then: + ourStatus == PartnerMetadataStatus.DELIVERED + } + + def "ourStatusFromReportStreamStatus returns PENDING"() { + when: + def ourStatus = PartnerMetadataOrchestrator.getInstance().ourStatusFromReportStreamStatus("Waiting to Deliver") + + then: + ourStatus == PartnerMetadataStatus.PENDING + + when: + ourStatus = PartnerMetadataOrchestrator.getInstance().ourStatusFromReportStreamStatus("DogCow") + + then: + ourStatus == PartnerMetadataStatus.PENDING + + when: + ourStatus = PartnerMetadataOrchestrator.getInstance().ourStatusFromReportStreamStatus(null) + + then: + ourStatus == PartnerMetadataStatus.PENDING + } }