From b848274bbc854781b0cf2e9fe0c21e99c4e3b0ea Mon Sep 17 00:00:00 2001 From: jcrichlake <145698165+jcrichlake@users.noreply.github.com> Date: Fri, 12 Jan 2024 12:28:43 -0500 Subject: [PATCH] Story 610 metadata order status (#766) * Initial commit with fetching and enum added * Save the delivery status to the database * Refactored delivery status to avoid null pointer exception * Updated metadataStorage with delivery status * Updated PartnerMetadataOchestrator unit tests * Populating ReST endpoint with status data * Fixing test * Defaulting status to PENDING --------- Co-authored-by: tjohnson7021 <86614374+tjohnson7021@users.noreply.github.com> --- .../workflows/terraform-deploy_reusable.yml | 2 +- .../etor/EtorDomainRegistration.java | 4 +-- .../etor/metadata/PartnerMetadata.java | 28 +++++++++++++++---- .../metadata/PartnerMetadataOrchestrator.java | 8 +++++- .../partner/PartnerMetadataStatus.java | 11 ++++++++ .../DatabasePartnerMetadataStorage.java | 4 +-- .../external/database}/DbDao.java | 6 ++-- .../external/database/PostgresDao.java | 17 ++++++++--- .../external/hapi/HapiOrderConverter.java | 6 ++++ .../etor/EtorDomainRegistrationTest.groovy | 3 +- .../PartnerMetadataOrchestratorTest.groovy | 18 ++++++------ .../etor/metadata/PartnerMetadataTest.groovy | 17 ++++++++--- ...geAccountPartnerMetadataStorageTest.groovy | 11 +++++--- .../DatabasePartnerMetadataStorageTest.groovy | 16 +++++++---- .../external/database/PostgresDaoTest.groovy | 15 ++++++---- .../hapi/HapiOrderConverterTest.groovy | 23 +++++++++++++++ .../FilePartnerMetadataStorageTest.groovy | 5 ++-- 17 files changed, 145 insertions(+), 49 deletions(-) create mode 100644 etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/metadata/partner/PartnerMetadataStatus.java rename {shared/src/main/java/gov/hhs/cdc/trustedintermediary/wrappers => etor/src/main/java/gov/hhs/cdc/trustedintermediary/external/database}/DbDao.java (63%) diff --git a/.github/workflows/terraform-deploy_reusable.yml b/.github/workflows/terraform-deploy_reusable.yml index 2d6505239..755194540 100644 --- a/.github/workflows/terraform-deploy_reusable.yml +++ b/.github/workflows/terraform-deploy_reusable.yml @@ -74,7 +74,7 @@ jobs: - name: Run Db migration run: | export PGPASSWORD=$(az account get-access-token --resource-type oss-rdbms --query "[accessToken]" -o tsv) - psql "host=$(terraform output -raw database_hostname) port=5432 dbname=postgres user=cdcti-github sslmode=require" -c "CREATE TABLE IF NOT EXISTS metadata (received_message_id varchar(40) PRIMARY KEY, sent_message_id varchar(40), sender varchar(30), receiver varchar(30), hash_of_order varchar(1000), time_received timestamptz); GRANT ALL ON metadata TO azure_pg_admin; ALTER TABLE metadata OWNER TO azure_pg_admin;" + psql "host=$(terraform output -raw database_hostname) port=5432 dbname=postgres user=cdcti-github sslmode=require" -c "CREATE TYPE message_status AS ENUM ('PENDING', 'DELIVERED', 'FAILED'); CREATE TABLE IF NOT EXISTS metadata (received_message_id varchar(40) PRIMARY KEY, sent_message_id varchar(40), sender varchar(30), receiver varchar(30), hash_of_order varchar(1000), time_received timestamptz, delivery_status message_status); GRANT ALL ON metadata TO azure_pg_admin; ALTER TABLE metadata OWNER TO azure_pg_admin;" - id: export-terraform-output name: Export Terraform Output 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 5158fbd0f..bf3d0eaef 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 @@ -26,6 +26,7 @@ import gov.hhs.cdc.trustedintermediary.external.azure.AzureClient; import gov.hhs.cdc.trustedintermediary.external.azure.AzureStorageAccountPartnerMetadataStorage; import gov.hhs.cdc.trustedintermediary.external.database.DatabasePartnerMetadataStorage; +import gov.hhs.cdc.trustedintermediary.external.database.DbDao; import gov.hhs.cdc.trustedintermediary.external.database.EtorSqlDriverManager; import gov.hhs.cdc.trustedintermediary.external.database.PostgresDao; import gov.hhs.cdc.trustedintermediary.external.hapi.HapiOrderConverter; @@ -33,7 +34,6 @@ import gov.hhs.cdc.trustedintermediary.external.localfile.MockRSEndpointClient; import gov.hhs.cdc.trustedintermediary.external.reportstream.ReportStreamEndpointClient; import gov.hhs.cdc.trustedintermediary.external.reportstream.ReportStreamOrderSender; -import gov.hhs.cdc.trustedintermediary.wrappers.DbDao; import gov.hhs.cdc.trustedintermediary.wrappers.FhirParseException; import gov.hhs.cdc.trustedintermediary.wrappers.HapiFhir; import gov.hhs.cdc.trustedintermediary.wrappers.Logger; @@ -108,9 +108,9 @@ public Map> domainRegistra } else { ApplicationContext.register( RSEndpointClient.class, ReportStreamEndpointClient.getInstance()); + ApplicationContext.register(AzureClient.class, AzureClient.getInstance()); } - return endpoints; } 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 6a453bc62..069070102 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 @@ -1,5 +1,6 @@ package gov.hhs.cdc.trustedintermediary.etor.metadata; +import gov.hhs.cdc.trustedintermediary.etor.metadata.partner.PartnerMetadataStatus; import java.time.Instant; /** @@ -11,6 +12,7 @@ * @param receiver The name of the receiver of the message. * @param timeReceived The time the message was received. * @param hash The hash of the message. + * @param deliveryStatus the status of the message based on an enum */ public record PartnerMetadata( String receivedSubmissionId, @@ -18,15 +20,27 @@ public record PartnerMetadata( String sender, String receiver, Instant timeReceived, - String hash) { + String hash, + PartnerMetadataStatus deliveryStatus) { + + // Below is for defaulting status when null + public PartnerMetadata { + if (deliveryStatus == null) { + deliveryStatus = PartnerMetadataStatus.PENDING; + } + } public PartnerMetadata( - String receivedSubmissionId, String sender, Instant timeReceived, String hash) { - this(receivedSubmissionId, null, sender, null, timeReceived, hash); + String receivedSubmissionId, + String sender, + Instant timeReceived, + String hash, + PartnerMetadataStatus deliveryStatus) { + this(receivedSubmissionId, null, sender, null, timeReceived, hash, deliveryStatus); } public PartnerMetadata(String receivedSubmissionId, String hash) { - this(receivedSubmissionId, null, null, null, null, hash); + this(receivedSubmissionId, null, null, null, null, hash, null); } public PartnerMetadata withSentSubmissionId(String sentSubmissionId) { @@ -36,7 +50,8 @@ public PartnerMetadata withSentSubmissionId(String sentSubmissionId) { this.sender, this.receiver, this.timeReceived, - this.hash); + this.hash, + this.deliveryStatus); } public PartnerMetadata withReceiver(String receiver) { @@ -46,6 +61,7 @@ public PartnerMetadata withReceiver(String receiver) { this.sender, receiver, this.timeReceived, - this.hash); + this.hash, + this.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 9381cb704..13bf4f30d 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 @@ -1,6 +1,7 @@ package gov.hhs.cdc.trustedintermediary.etor.metadata; import gov.hhs.cdc.trustedintermediary.etor.RSEndpointClient; +import gov.hhs.cdc.trustedintermediary.etor.metadata.partner.PartnerMetadataStatus; import gov.hhs.cdc.trustedintermediary.external.reportstream.ReportStreamEndpointClientException; import gov.hhs.cdc.trustedintermediary.wrappers.Logger; import gov.hhs.cdc.trustedintermediary.wrappers.formatter.Formatter; @@ -71,7 +72,12 @@ public void updateMetadataForReceivedOrder(String receivedSubmissionId, String o sender, timeReceived); PartnerMetadata partnerMetadata = - new PartnerMetadata(receivedSubmissionId, sender, timeReceived, orderHash); + new PartnerMetadata( + receivedSubmissionId, + sender, + timeReceived, + orderHash, + PartnerMetadataStatus.PENDING); partnerMetadataStorage.saveMetadata(partnerMetadata); } diff --git a/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/metadata/partner/PartnerMetadataStatus.java b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/metadata/partner/PartnerMetadataStatus.java new file mode 100644 index 000000000..c4d8a8a18 --- /dev/null +++ b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/metadata/partner/PartnerMetadataStatus.java @@ -0,0 +1,11 @@ +package gov.hhs.cdc.trustedintermediary.etor.metadata.partner; + +/** + * Enum for tracking the status of a message for diagnostic purposes. We store the status in our + * database + */ +public enum PartnerMetadataStatus { + PENDING, + DELIVERED, + FAILED +} diff --git a/etor/src/main/java/gov/hhs/cdc/trustedintermediary/external/database/DatabasePartnerMetadataStorage.java b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/external/database/DatabasePartnerMetadataStorage.java index 44b424102..b3374188c 100644 --- a/etor/src/main/java/gov/hhs/cdc/trustedintermediary/external/database/DatabasePartnerMetadataStorage.java +++ b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/external/database/DatabasePartnerMetadataStorage.java @@ -3,7 +3,6 @@ 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.DbDao; import gov.hhs.cdc.trustedintermediary.wrappers.Logger; import java.sql.SQLException; import java.util.Optional; @@ -45,7 +44,8 @@ public void saveMetadata(final PartnerMetadata metadata) throws PartnerMetadataE metadata.sender(), metadata.receiver(), metadata.hash(), - metadata.timeReceived()); + metadata.timeReceived(), + metadata.deliveryStatus()); } catch (SQLException e) { throw new PartnerMetadataException("Error saving metadata", e); } diff --git a/shared/src/main/java/gov/hhs/cdc/trustedintermediary/wrappers/DbDao.java b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/external/database/DbDao.java similarity index 63% rename from shared/src/main/java/gov/hhs/cdc/trustedintermediary/wrappers/DbDao.java rename to etor/src/main/java/gov/hhs/cdc/trustedintermediary/external/database/DbDao.java index f7642eba8..b342dc3bc 100644 --- a/shared/src/main/java/gov/hhs/cdc/trustedintermediary/wrappers/DbDao.java +++ b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/external/database/DbDao.java @@ -1,5 +1,6 @@ -package gov.hhs.cdc.trustedintermediary.wrappers; +package gov.hhs.cdc.trustedintermediary.external.database; +import gov.hhs.cdc.trustedintermediary.etor.metadata.partner.PartnerMetadataStatus; import java.sql.SQLException; import java.time.Instant; @@ -11,7 +12,8 @@ void upsertMetadata( String sender, String receiver, String hash, - Instant timeReceived) + Instant timeReceived, + PartnerMetadataStatus deliveryStatus) throws SQLException; Object fetchMetadata(String uniqueId) throws SQLException; diff --git a/etor/src/main/java/gov/hhs/cdc/trustedintermediary/external/database/PostgresDao.java b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/external/database/PostgresDao.java index a1852398a..506a6f80c 100644 --- a/etor/src/main/java/gov/hhs/cdc/trustedintermediary/external/database/PostgresDao.java +++ b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/external/database/PostgresDao.java @@ -2,8 +2,8 @@ import gov.hhs.cdc.trustedintermediary.context.ApplicationContext; import gov.hhs.cdc.trustedintermediary.etor.metadata.PartnerMetadata; +import gov.hhs.cdc.trustedintermediary.etor.metadata.partner.PartnerMetadataStatus; import gov.hhs.cdc.trustedintermediary.external.azure.AzureClient; -import gov.hhs.cdc.trustedintermediary.wrappers.DbDao; import gov.hhs.cdc.trustedintermediary.wrappers.Logger; import gov.hhs.cdc.trustedintermediary.wrappers.SqlDriverManager; import java.sql.Connection; @@ -86,14 +86,15 @@ public synchronized void upsertMetadata( String sender, String receiver, String hash, - Instant timeReceived) + Instant timeReceived, + PartnerMetadataStatus deliveryStatus) throws SQLException { try (Connection conn = connect(); PreparedStatement statement = conn.prepareStatement( """ - INSERT INTO metadata VALUES (?, ?, ?, ?, ?, ?) + INSERT INTO metadata VALUES (?, ?, ?, ?, ?, ?, ?) ON CONFLICT (received_message_id) DO UPDATE SET receiver = EXCLUDED.receiver, sent_message_id = EXCLUDED.sent_message_id """)) { @@ -109,6 +110,13 @@ ON CONFLICT (received_message_id) DO UPDATE SET receiver = EXCLUDED.receiver, se } statement.setTimestamp(6, timestamp); + String deliveryStatusString = null; + if (deliveryStatus != null) { + deliveryStatusString = deliveryStatus.toString(); + } + + statement.setString(7, deliveryStatusString); + statement.executeUpdate(); } } @@ -141,7 +149,8 @@ public synchronized PartnerMetadata fetchMetadata(String receivedSubmissionId) result.getString("sender"), result.getString("receiver"), timeReceived, - result.getString("hash_of_order")); + result.getString("hash_of_order"), + PartnerMetadataStatus.valueOf(result.getString("delivery_status"))); } } } 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 f2a1c56d1..dfd8fa0e8 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 @@ -225,6 +225,12 @@ public FhirMetadata extractPublicMetadataToOperationOutcome(PartnerMetadata m .add(createInformationIssueComponent("order ingestion", orderIngestion)); operation.getIssue().add(createInformationIssueComponent("payload hash", metadata.hash())); + operation + .getIssue() + .add( + createInformationIssueComponent( + "delivery status", metadata.deliveryStatus().toString())); + return new HapiFhirMetadata(operation); } 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 1c8369ee9..3f3117830 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 @@ -14,6 +14,7 @@ import gov.hhs.cdc.trustedintermediary.etor.demographics.PatientDemographicsResp import gov.hhs.cdc.trustedintermediary.etor.metadata.PartnerMetadata import gov.hhs.cdc.trustedintermediary.etor.metadata.PartnerMetadataException import gov.hhs.cdc.trustedintermediary.etor.metadata.PartnerMetadataOrchestrator +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 @@ -324,7 +325,7 @@ class EtorDomainRegistrationTest extends Specification { request.setPathParams(["id": "metadataId"]) def mockPartnerMetadataOrchestrator = Mock(PartnerMetadataOrchestrator) - mockPartnerMetadataOrchestrator.getMetadata(_ as String) >> Optional.ofNullable(new PartnerMetadata("receivedSubmissionId", "sender", Instant.now(), "hash")) + mockPartnerMetadataOrchestrator.getMetadata(_ as String) >> Optional.ofNullable(new PartnerMetadata("receivedSubmissionId", "sender", Instant.now(), "hash", PartnerMetadataStatus.DELIVERED)) TestApplicationContext.register(PartnerMetadataOrchestrator, mockPartnerMetadataOrchestrator) def mockResponseHelper = Mock(DomainResponseHelper) 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 4157b9340..3dcc755cc 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 @@ -2,6 +2,7 @@ package gov.hhs.cdc.trustedintermediary.etor.metadata import gov.hhs.cdc.trustedintermediary.context.TestApplicationContext import gov.hhs.cdc.trustedintermediary.etor.RSEndpointClient +import gov.hhs.cdc.trustedintermediary.etor.metadata.partner.PartnerMetadataStatus import gov.hhs.cdc.trustedintermediary.etor.orders.OrderConverter import gov.hhs.cdc.trustedintermediary.external.hapi.HapiOrderConverter import gov.hhs.cdc.trustedintermediary.external.jackson.Jackson @@ -43,6 +44,7 @@ class PartnerMetadataOrchestratorTest extends Specification { def hashCode = "123" def bearerToken = "token" def rsHistoryApiResponse = "{\"sender\": \"${sender}\", \"timestamp\": \"${timestamp}\"}" + def deliveryStatus = PartnerMetadataStatus.PENDING mockFormatter.convertJsonToObject(rsHistoryApiResponse, _ as TypeReference) >> [sender: sender, timestamp: timestamp] @@ -52,7 +54,7 @@ class PartnerMetadataOrchestratorTest extends Specification { then: 1 * mockClient.getRsToken() >> bearerToken 1 * mockClient.requestHistoryEndpoint(receivedSubmissionId, bearerToken) >> rsHistoryApiResponse - 1 * mockPartnerMetadataStorage.saveMetadata(new PartnerMetadata(receivedSubmissionId, sender, Instant.parse(timestamp), hashCode)) + 1 * mockPartnerMetadataStorage.saveMetadata(new PartnerMetadata(receivedSubmissionId, sender, Instant.parse(timestamp), hashCode, deliveryStatus)) } def "updateMetadataForSentOrder test case when sentSubmissionId is null"() { @@ -147,7 +149,7 @@ class PartnerMetadataOrchestratorTest extends Specification { given: def receivedSubmissionId = "receivedSubmissionId" def sentSubmissionId = "sentSubmissionId" - def partnerMetadata = new PartnerMetadata(receivedSubmissionId, "sender", Instant.now(), "hash") + def partnerMetadata = new PartnerMetadata(receivedSubmissionId, "sender", Instant.now(), "hash", PartnerMetadataStatus.PENDING) def updatedPartnerMetadata = partnerMetadata.withSentSubmissionId(sentSubmissionId) when: @@ -174,7 +176,7 @@ class PartnerMetadataOrchestratorTest extends Specification { given: def receivedSubmissionId = "receivedSubmissionId" def sentSubmissionId = "sentSubmissionId" - def partnerMetadata = new PartnerMetadata(receivedSubmissionId, sentSubmissionId, "sender", null, Instant.now(), "hash") + def partnerMetadata = new PartnerMetadata(receivedSubmissionId, sentSubmissionId, "sender", null, Instant.now(), "hash", PartnerMetadataStatus.PENDING) mockPartnerMetadataStorage.readMetadata(receivedSubmissionId) >> Optional.of(partnerMetadata) mockClient.getRsToken() >> "token" @@ -192,7 +194,7 @@ class PartnerMetadataOrchestratorTest extends Specification { def receivedSubmissionId = "receivedSubmissionId" def sentSubmissionId = "sentSubmissionId" def rsHistoryApiResponse = "{\"destinations\": [{\"organization_id\": \"org\", \"service\": \"service\"}]}" - def partnerMetadata = new PartnerMetadata(receivedSubmissionId, sentSubmissionId, "sender", null, Instant.now(), "hash") + def partnerMetadata = new PartnerMetadata(receivedSubmissionId, sentSubmissionId, "sender", null, Instant.now(), "hash", PartnerMetadataStatus.PENDING) mockPartnerMetadataStorage.readMetadata(receivedSubmissionId) >> Optional.of(partnerMetadata) mockClient.getRsToken() >> "token" @@ -209,7 +211,7 @@ class PartnerMetadataOrchestratorTest extends Specification { def "getMetadata retrieves metadata successfully with the sender already filled"() { given: def receivedSubmissionId = "receivedSubmissionId" - def metadata = new PartnerMetadata(receivedSubmissionId, "sentSubmissionId", "sender", "receiver", Instant.now(), "hash") + def metadata = new PartnerMetadata(receivedSubmissionId, "sentSubmissionId", "sender", "receiver", Instant.now(), "hash", PartnerMetadataStatus.DELIVERED) when: def result = PartnerMetadataOrchestrator.getInstance().getMetadata(receivedSubmissionId) @@ -224,7 +226,7 @@ class PartnerMetadataOrchestratorTest extends Specification { def "getMetadata retrieves metadata successfully when receiver is present and sentSubmissionId is missing"() { given: def receivedSubmissionId = "receivedSubmissionId" - def metadata = new PartnerMetadata(receivedSubmissionId, null, "sender", "receiver", Instant.now(), "hash") + def metadata = new PartnerMetadata(receivedSubmissionId, null, "sender", "receiver", Instant.now(), "hash", PartnerMetadataStatus.DELIVERED) when: def result = PartnerMetadataOrchestrator.getInstance().getMetadata(receivedSubmissionId) @@ -244,8 +246,8 @@ class PartnerMetadataOrchestratorTest extends Specification { def hashCode = "123" def bearerToken = "token" def rsHistoryApiResponse = "{\"destinations\": [{\"organization_id\": \"org\", \"service\": \"service\"}]}" - def missingReceiverMetadata = new PartnerMetadata(receivedSubmissionId, sentSubmissionId, sender, null, timestamp, hashCode) - def expectedMetadata = new PartnerMetadata(receivedSubmissionId, sentSubmissionId, sender, "org.service", timestamp, hashCode) + def missingReceiverMetadata = new PartnerMetadata(receivedSubmissionId, sentSubmissionId, sender, null, timestamp, hashCode, PartnerMetadataStatus.DELIVERED) + def expectedMetadata = new PartnerMetadata(receivedSubmissionId, sentSubmissionId, sender, "org.service", timestamp, hashCode, PartnerMetadataStatus.DELIVERED) mockClient.getRsToken() >> bearerToken mockClient.requestHistoryEndpoint(sentSubmissionId, bearerToken) >> rsHistoryApiResponse 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 4bf7d1c85..827d25a0b 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 @@ -2,6 +2,8 @@ package gov.hhs.cdc.trustedintermediary.etor.metadata import gov.hhs.cdc.trustedintermediary.PojoTestUtils +import gov.hhs.cdc.trustedintermediary.etor.metadata.partner.PartnerMetadataStatus + import java.time.Instant import spock.lang.Specification @@ -22,9 +24,10 @@ class PartnerMetadataTest extends Specification { def receiver = "receiver" def timeReceived = Instant.now() def hash = "abcd" + def status = PartnerMetadataStatus.DELIVERED when: - def metadata = new PartnerMetadata(receivedSubmissionId, sentSubmissionId, sender, receiver, timeReceived, hash) + def metadata = new PartnerMetadata(receivedSubmissionId, sentSubmissionId, sender, receiver, timeReceived, hash, PartnerMetadataStatus.DELIVERED) then: metadata.receivedSubmissionId() == receivedSubmissionId @@ -33,6 +36,7 @@ class PartnerMetadataTest extends Specification { metadata.receiver() == receiver metadata.timeReceived() == timeReceived metadata.hash() == hash + metadata.deliveryStatus() == status } def "test overloaded constructor"() { @@ -41,9 +45,9 @@ class PartnerMetadataTest extends Specification { def sender = "sender" def timeReceived = Instant.now() def hash = "abcd" - + def status = PartnerMetadataStatus.DELIVERED when: - def metadata = new PartnerMetadata(receivedSubmissionId, sender, timeReceived, hash) + def metadata = new PartnerMetadata(receivedSubmissionId, sender, timeReceived, hash, PartnerMetadataStatus.DELIVERED) then: metadata.receivedSubmissionId() == receivedSubmissionId @@ -52,6 +56,7 @@ class PartnerMetadataTest extends Specification { metadata.receiver() == null metadata.timeReceived() == timeReceived metadata.hash() == hash + metadata.deliveryStatus() == status } def "test constructor with only received submission ID and hash"() { @@ -69,6 +74,8 @@ class PartnerMetadataTest extends Specification { metadata.receiver() == null metadata.timeReceived() == null metadata.hash() == hash + //Status should default to PENDING + metadata.deliveryStatus() == PartnerMetadataStatus.PENDING } def "test withSentSubmissionId and withReceiver to update PartnerMetadata"() { @@ -79,7 +86,8 @@ class PartnerMetadataTest extends Specification { def receiver = "receiver" def timeReceived = Instant.now() def hash = "abcd" - def metadata = new PartnerMetadata(receivedSubmissionId, sender, timeReceived, hash) + def status = PartnerMetadataStatus.DELIVERED + def metadata = new PartnerMetadata(receivedSubmissionId, sender, timeReceived, hash, status) when: def updatedMetadata = metadata.withSentSubmissionId(sentSubmissionId).withReceiver(receiver) @@ -91,5 +99,6 @@ class PartnerMetadataTest extends Specification { updatedMetadata.receiver() == receiver updatedMetadata.timeReceived() == timeReceived updatedMetadata.hash() == hash + updatedMetadata.deliveryStatus() == status } } diff --git a/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/external/azure/AzureStorageAccountPartnerMetadataStorageTest.groovy b/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/external/azure/AzureStorageAccountPartnerMetadataStorageTest.groovy index 53e77395a..8df14e7e5 100644 --- a/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/external/azure/AzureStorageAccountPartnerMetadataStorageTest.groovy +++ b/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/external/azure/AzureStorageAccountPartnerMetadataStorageTest.groovy @@ -6,6 +6,7 @@ import com.azure.storage.blob.BlobClient import gov.hhs.cdc.trustedintermediary.context.TestApplicationContext import gov.hhs.cdc.trustedintermediary.etor.metadata.PartnerMetadata import gov.hhs.cdc.trustedintermediary.etor.metadata.PartnerMetadataException +import gov.hhs.cdc.trustedintermediary.etor.metadata.partner.PartnerMetadataStatus import gov.hhs.cdc.trustedintermediary.external.jackson.Jackson import gov.hhs.cdc.trustedintermediary.wrappers.formatter.Formatter import java.time.Instant @@ -27,8 +28,9 @@ class AzureStorageAccountPartnerMetadataStorageTest extends Specification { def expectedReceiver = "receiver" def expectedTimestamp = Instant.parse("2023-12-04T18:51:48.941875Z") def expectedHash = "abcd" + PartnerMetadataStatus status = PartnerMetadataStatus.PENDING - PartnerMetadata expectedMetadata = new PartnerMetadata(expectedReceivedSubmissionId, expectedSentSubmissionId, expectedSender, expectedReceiver, expectedTimestamp, expectedHash) + PartnerMetadata expectedMetadata = new PartnerMetadata(expectedReceivedSubmissionId, expectedSentSubmissionId, expectedSender, expectedReceiver, expectedTimestamp, expectedHash, status) String simulatedMetadataJson = """{ "receivedSubmissionId": "${expectedReceivedSubmissionId}", @@ -36,7 +38,8 @@ class AzureStorageAccountPartnerMetadataStorageTest extends Specification { "sender": "${expectedSender}", "receiver": "${expectedReceiver}", "timeReceived": "${expectedTimestamp}", - "hash": "${expectedHash}" + "hash": "${expectedHash}", + "deliveryStatus": "${status}" }""" def mockBlobClient = Mock(BlobClient) @@ -97,7 +100,7 @@ class AzureStorageAccountPartnerMetadataStorageTest extends Specification { def "successfully save metadata"() { given: - PartnerMetadata partnerMetadata = new PartnerMetadata("receivedSubmissionId", "sentSubmissionId", "sender", "receiver", Instant.now(), "abcd") + PartnerMetadata partnerMetadata = new PartnerMetadata("receivedSubmissionId", "sentSubmissionId", "sender", "receiver", Instant.now(), "abcd", PartnerMetadataStatus.DELIVERED) def mockBlobClient = Mock(BlobClient) def azureClient = Mock(AzureClient) @@ -119,7 +122,7 @@ class AzureStorageAccountPartnerMetadataStorageTest extends Specification { def "failed to save metadata"() { given: - PartnerMetadata partnerMetadata = new PartnerMetadata("receivedSubmissionId", "sentSubmissionId", "sender", "receiver", Instant.now(), "abcd") + PartnerMetadata partnerMetadata = new PartnerMetadata("receivedSubmissionId", "sentSubmissionId", "sender", "receiver", Instant.now(), "abcd", PartnerMetadataStatus.DELIVERED) def mockBlobClient = Mock(BlobClient) mockBlobClient.upload(_ as BinaryData, true) >> { throw new AzureException("upload failed") } diff --git a/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/external/database/DatabasePartnerMetadataStorageTest.groovy b/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/external/database/DatabasePartnerMetadataStorageTest.groovy index 61aa37d7e..13188047a 100644 --- a/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/external/database/DatabasePartnerMetadataStorageTest.groovy +++ b/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/external/database/DatabasePartnerMetadataStorageTest.groovy @@ -4,7 +4,7 @@ import gov.hhs.cdc.trustedintermediary.context.TestApplicationContext 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.DbDao +import gov.hhs.cdc.trustedintermediary.etor.metadata.partner.PartnerMetadataStatus import spock.lang.Specification import java.sql.SQLException @@ -28,7 +28,7 @@ class DatabasePartnerMetadataStorageTest extends Specification { def "readMetadata happy path works"() { given: def receivedSubmissionId = "receivedSubmissionId" - def mockMetadata = new PartnerMetadata(receivedSubmissionId, "sentSubmissionId", "sender", "receiver", Instant.now(), "hash") + def mockMetadata = new PartnerMetadata(receivedSubmissionId, "sentSubmissionId", "sender", "receiver", Instant.now(), "hash", PartnerMetadataStatus.PENDING) def expectedResult = Optional.of(mockMetadata) mockDao.fetchMetadata(_ as String) >> mockMetadata @@ -61,7 +61,8 @@ class DatabasePartnerMetadataStorageTest extends Specification { "sender", "receiver", Instant.now(), - "hash" + "hash", + PartnerMetadataStatus.PENDING ) when: @@ -74,7 +75,8 @@ class DatabasePartnerMetadataStorageTest extends Specification { mockMetadata.sender(), mockMetadata.receiver(), mockMetadata.hash(), - mockMetadata.timeReceived() + mockMetadata.timeReceived(), + mockMetadata.deliveryStatus() ) } @@ -87,7 +89,8 @@ class DatabasePartnerMetadataStorageTest extends Specification { "sender", "receiver", Instant.now(), - "hash" + "hash", + PartnerMetadataStatus.FAILED ) mockDao.upsertMetadata( @@ -96,7 +99,8 @@ class DatabasePartnerMetadataStorageTest extends Specification { mockMetadata.sender(), mockMetadata.receiver(), mockMetadata.hash(), - mockMetadata.timeReceived() + mockMetadata.timeReceived(), + mockMetadata.deliveryStatus() ) >> { throw new SQLException("Something went wrong!") } when: diff --git a/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/external/database/PostgresDaoTest.groovy b/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/external/database/PostgresDaoTest.groovy index f85d367a6..348223528 100644 --- a/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/external/database/PostgresDaoTest.groovy +++ b/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/external/database/PostgresDaoTest.groovy @@ -2,6 +2,7 @@ package gov.hhs.cdc.trustedintermediary.external.database import gov.hhs.cdc.trustedintermediary.context.TestApplicationContext import gov.hhs.cdc.trustedintermediary.etor.metadata.PartnerMetadata +import gov.hhs.cdc.trustedintermediary.etor.metadata.partner.PartnerMetadataStatus import gov.hhs.cdc.trustedintermediary.external.azure.AzureClient import gov.hhs.cdc.trustedintermediary.wrappers.SqlDriverManager import spock.lang.Specification @@ -72,7 +73,7 @@ class PostgresDaoTest extends Specification { TestApplicationContext.injectRegisteredImplementations() when: - PostgresDao.getInstance().upsertMetadata("mock_id_receiver", "mock_id_sender", "mock_sender", "mock_receiver", "mock_hash", Instant.now()) + PostgresDao.getInstance().upsertMetadata("mock_id_receiver", "mock_id_sender", "mock_sender", "mock_receiver", "mock_hash", Instant.now(), PartnerMetadataStatus.PENDING) then: 1 * mockPreparedStatement.executeUpdate() @@ -87,7 +88,7 @@ class PostgresDaoTest extends Specification { TestApplicationContext.injectRegisteredImplementations() when: - PostgresDao.getInstance().upsertMetadata("mock_id_receiver", "mock_id_sender", "mock_sender", "mock_receiver", "mock_hash", Instant.now()) + PostgresDao.getInstance().upsertMetadata("mock_id_receiver", "mock_id_sender", "mock_sender", "mock_receiver", "mock_hash", Instant.now(), PartnerMetadataStatus.DELIVERED) then: thrown(SQLException) @@ -102,7 +103,7 @@ class PostgresDaoTest extends Specification { TestApplicationContext.injectRegisteredImplementations() when: - PostgresDao.getInstance().upsertMetadata("mock_id_receiver", "mock_id_sender", "mock_sender", "mock_receiver", "mock_hash", null) + PostgresDao.getInstance().upsertMetadata("mock_id_receiver", "mock_id_sender", "mock_sender", "mock_receiver", "mock_hash", null, PartnerMetadataStatus.DELIVERED) then: mockPreparedStatement.setTimestamp(_ as Integer, _) >> { Integer parameterIndex, Timestamp timestamp -> @@ -117,7 +118,7 @@ class PostgresDaoTest extends Specification { mockPreparedStatement.executeQuery() >> mockResultSet mockResultSet.next() >> true mockResultSet.getTimestamp(_ as String) >> Timestamp.from(Instant.now()) - + mockResultSet.getString("delivery_status") >> "DELIVERED" TestApplicationContext.register(SqlDriverManager, mockDriver) TestApplicationContext.injectRegisteredImplementations() @@ -170,7 +171,8 @@ class PostgresDaoTest extends Specification { Timestamp timestampForMock = Timestamp.from(Instant.parse("2024-01-03T15:45:33.30Z")) Instant timeReceived = timestampForMock.toInstant() def hash = sender.hashCode().toString() - def expected = new PartnerMetadata(receivedMessageId, sentMessageId, sender, null, timeReceived, hash) + def status = PartnerMetadataStatus.PENDING + def expected = new PartnerMetadata(receivedMessageId, sentMessageId, sender, null, timeReceived, hash, status) mockDriver.getConnection(_ as String, _ as Properties) >> mockConn mockConn.prepareStatement(_ as String) >> mockPreparedStatement @@ -181,6 +183,7 @@ class PostgresDaoTest extends Specification { mockResultSet.getString("receiver") >> null mockResultSet.getTimestamp("time_received") >> timestampForMock mockResultSet.getString("hash_of_order") >> hash + mockResultSet.getString("delivery_status") >> status.toString() mockPreparedStatement.executeQuery() >> mockResultSet TestApplicationContext.register(SqlDriverManager, mockDriver) @@ -200,7 +203,7 @@ class PostgresDaoTest extends Specification { mockPreparedStatement.executeQuery() >> mockResultSet mockResultSet.next() >> true mockResultSet.getTimestamp("time_received") >> null - + mockResultSet.getString("delivery_status") >> "DELIVERED" TestApplicationContext.register(SqlDriverManager, mockDriver) TestApplicationContext.injectRegisteredImplementations() 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 7c9cd8d54..f499e227d 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 @@ -5,6 +5,7 @@ import gov.hhs.cdc.trustedintermediary.OrderMock import gov.hhs.cdc.trustedintermediary.context.TestApplicationContext import gov.hhs.cdc.trustedintermediary.etor.metadata.PartnerMetadata import gov.hhs.cdc.trustedintermediary.etor.orders.OrderConverter +import gov.hhs.cdc.trustedintermediary.etor.metadata.partner.PartnerMetadataStatus import org.hl7.fhir.r4.model.Address import org.hl7.fhir.r4.model.ContactPoint import org.hl7.fhir.r4.model.Extension @@ -297,4 +298,26 @@ 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" + PartnerMetadata metadata = new PartnerMetadata( + "receivedSubmissionId", "sentSubmissionId", sender, receiver, time, hash, PartnerMetadataStatus.DELIVERED) + + when: + def result = HapiOrderConverter.getInstance().extractPublicMetadataToOperationOutcome(metadata).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 == PartnerMetadataStatus.DELIVERED.toString() + } } diff --git a/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/external/localfile/FilePartnerMetadataStorageTest.groovy b/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/external/localfile/FilePartnerMetadataStorageTest.groovy index c0e59f017..8e2ffb2ac 100644 --- a/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/external/localfile/FilePartnerMetadataStorageTest.groovy +++ b/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/external/localfile/FilePartnerMetadataStorageTest.groovy @@ -3,6 +3,7 @@ package gov.hhs.cdc.trustedintermediary.external.localfile import gov.hhs.cdc.trustedintermediary.context.TestApplicationContext import gov.hhs.cdc.trustedintermediary.etor.metadata.PartnerMetadata import gov.hhs.cdc.trustedintermediary.etor.metadata.PartnerMetadataException +import gov.hhs.cdc.trustedintermediary.etor.metadata.partner.PartnerMetadataStatus import gov.hhs.cdc.trustedintermediary.external.jackson.Jackson import gov.hhs.cdc.trustedintermediary.wrappers.formatter.Formatter import gov.hhs.cdc.trustedintermediary.wrappers.formatter.FormatterProcessingException @@ -23,7 +24,7 @@ class FilePartnerMetadataStorageTest extends Specification { given: def expectedReceivedSubmissionId = "receivedSubmissionId" def expectedSentSubmissionId = "receivedSubmissionId" - PartnerMetadata metadata = new PartnerMetadata(expectedReceivedSubmissionId, expectedSentSubmissionId, "sender", "receiver", Instant.parse("2023-12-04T18:51:48.941875Z"), "abcd") + PartnerMetadata metadata = new PartnerMetadata(expectedReceivedSubmissionId, expectedSentSubmissionId, "sender", "receiver", Instant.parse("2023-12-04T18:51:48.941875Z"), "abcd", PartnerMetadataStatus.DELIVERED) TestApplicationContext.register(Formatter, Jackson.getInstance()) TestApplicationContext.injectRegisteredImplementations() @@ -38,7 +39,7 @@ class FilePartnerMetadataStorageTest extends Specification { def "saveMetadata throws PartnerMetadataException when unable to save file"() { given: - PartnerMetadata metadata = new PartnerMetadata("receivedSubmissionId", "sentSubmissionId","sender", "receiver", Instant.now(), "abcd") + PartnerMetadata metadata = new PartnerMetadata("receivedSubmissionId", "sentSubmissionId","sender", "receiver", Instant.now(), "abcd", PartnerMetadataStatus.DELIVERED) def mockFormatter = Mock(Formatter) mockFormatter.convertToJsonString(_ as PartnerMetadata) >> {throw new FormatterProcessingException("error", new Exception())}