-
Notifications
You must be signed in to change notification settings - Fork 5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
621: Generic Read Metadata #1019
Changes from all commits
6d651cc
cf5ac75
8d4a5a1
9ceabf8
8ee28a7
9a52b40
6a67e1d
820636f
08897e4
6a0ab81
aadc45b
2d438de
0a15c0e
6905389
7d623bc
b3d6631
13c8842
a9537cd
341dd78
abe1f86
47aa2e9
deeb4be
ef72e8e
faae0fa
8c86655
a7f1860
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,28 +2,38 @@ | |
|
||
import gov.hhs.cdc.trustedintermediary.etor.metadata.partner.PartnerMetadata; | ||
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.PartnerMetadataStatus; | ||
import gov.hhs.cdc.trustedintermediary.etor.metadata.partner.PartnerMetadataStorage; | ||
import gov.hhs.cdc.trustedintermediary.wrappers.Logger; | ||
import gov.hhs.cdc.trustedintermediary.wrappers.formatter.Formatter; | ||
import gov.hhs.cdc.trustedintermediary.wrappers.formatter.FormatterProcessingException; | ||
import gov.hhs.cdc.trustedintermediary.wrappers.formatter.TypeReference; | ||
import java.sql.PreparedStatement; | ||
import java.sql.ResultSet; | ||
import java.sql.SQLException; | ||
import java.sql.Timestamp; | ||
import java.sql.Types; | ||
import java.time.Instant; | ||
import java.util.List; | ||
import java.util.Optional; | ||
import java.util.Set; | ||
import java.util.stream.Collectors; | ||
import javax.inject.Inject; | ||
|
||
/** Implements the {@link PartnerMetadataStorage} using a database. */ | ||
public class DatabasePartnerMetadataStorage implements PartnerMetadataStorage { | ||
|
||
private static final DatabasePartnerMetadataStorage INSTANCE = | ||
new DatabasePartnerMetadataStorage(); | ||
|
||
private static final String METADATA_TABLE_RECEIVED_MESSAGE_ID = "received_message_id"; | ||
|
||
@Inject DbDao dao; | ||
|
||
@Inject Logger logger; | ||
|
||
@Inject Formatter formatter; | ||
private static final DatabasePartnerMetadataStorage INSTANCE = | ||
new DatabasePartnerMetadataStorage(); | ||
|
||
private DatabasePartnerMetadataStorage() {} | ||
|
||
|
@@ -35,12 +45,25 @@ public static DatabasePartnerMetadataStorage getInstance() { | |
public Optional<PartnerMetadata> readMetadata(final String uniqueId) | ||
throws PartnerMetadataException { | ||
try { | ||
PartnerMetadata data = (PartnerMetadata) dao.fetchMetadata(uniqueId); | ||
return Optional.ofNullable(data); | ||
PartnerMetadata metadata = | ||
dao.fetchFirstData( | ||
connection -> { | ||
try { | ||
PreparedStatement statement = | ||
connection.prepareStatement( | ||
"SELECT * FROM metadata where received_message_id = ? OR sent_message_id = ?"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. At some point I think we should separate SQL code from Java code. In the meantime I've been placing the sql code in a variable at the start of the method where it's used, but we should probably start putting them in a more centralized place soon. It could be external files or java constants |
||
statement.setString(1, uniqueId); | ||
statement.setString(2, uniqueId); | ||
return statement; | ||
} catch (SQLException e) { | ||
throw new RuntimeException(e); | ||
} | ||
}, | ||
this::partnerMetadataFromResultSet); | ||
|
||
return Optional.ofNullable(metadata); | ||
} catch (SQLException e) { | ||
throw new PartnerMetadataException("Error retrieving metadata", e); | ||
} catch (FormatterProcessingException e) { | ||
throw new PartnerMetadataException("Error formatting metadata", e); | ||
} | ||
} | ||
|
||
|
@@ -50,7 +73,7 @@ public void saveMetadata(final PartnerMetadata metadata) throws PartnerMetadataE | |
|
||
try { | ||
List<DbColumn> columns = createDbColumnsFromMetadata(metadata); | ||
dao.upsertData("metadata", columns, "(received_message_id)"); | ||
dao.upsertData("metadata", columns, "(" + METADATA_TABLE_RECEIVED_MESSAGE_ID + ")"); | ||
} catch (SQLException e) { | ||
throw new PartnerMetadataException("Error saving metadata", e); | ||
} catch (FormatterProcessingException e) { | ||
|
@@ -63,32 +86,113 @@ public Set<PartnerMetadata> readMetadataForSender(String sender) | |
throws PartnerMetadataException { | ||
Set<PartnerMetadata> consolidatedMetadata; | ||
try { | ||
consolidatedMetadata = dao.fetchMetadataForSender(sender); | ||
consolidatedMetadata = | ||
dao.fetchManyData( | ||
connection -> { | ||
try { | ||
PreparedStatement statement = | ||
connection.prepareStatement( | ||
"SELECT * FROM metadata WHERE sender = ?"); | ||
statement.setString(1, sender); | ||
return statement; | ||
} catch (SQLException e) { | ||
throw new RuntimeException(e); | ||
} | ||
}, | ||
this::partnerMetadataFromResultSet, | ||
Collectors.toSet()); | ||
|
||
return consolidatedMetadata; | ||
|
||
} catch (SQLException e) { | ||
throw new PartnerMetadataException("Error retrieving consolidated metadata", e); | ||
} catch (FormatterProcessingException e) { | ||
throw new PartnerMetadataException("Error formatting consolidated metadata", e); | ||
} | ||
return consolidatedMetadata; | ||
} | ||
|
||
@Override | ||
public Set<PartnerMetadata> readMetadataForMessageLinking(String submissionId) | ||
throws PartnerMetadataException { | ||
|
||
Set<PartnerMetadata> metadataSet; | ||
try { | ||
metadataSet = dao.fetchMetadataForMessageLinking(submissionId); | ||
} catch (SQLException | FormatterProcessingException e) { | ||
metadataSet = | ||
dao.fetchManyData( | ||
connection -> { | ||
try { | ||
PreparedStatement statement = | ||
connection.prepareStatement( | ||
""" | ||
SELECT m2.* | ||
FROM metadata m1 | ||
JOIN metadata m2 | ||
ON m1.placer_order_number = m2.placer_order_number | ||
AND m1.sending_application_id = m2.sending_application_id | ||
AND m1.sending_facility_id = m2.sending_facility_id | ||
WHERE m1.sent_message_id = ?; | ||
"""); | ||
statement.setString(1, submissionId); | ||
return statement; | ||
} catch (SQLException e) { | ||
throw new RuntimeException(e); | ||
} | ||
}, | ||
this::partnerMetadataFromResultSet, | ||
Collectors.toSet()); | ||
|
||
return metadataSet; | ||
} catch (SQLException e) { | ||
throw new PartnerMetadataException("Error retrieving metadata", e); | ||
} | ||
return metadataSet; | ||
} | ||
|
||
PartnerMetadata partnerMetadataFromResultSet(ResultSet resultSet) { | ||
try { | ||
Instant timeReceived = null; | ||
Instant timeDelivered = null; | ||
Timestamp timestampReceived = resultSet.getTimestamp("time_received"); | ||
Timestamp timestampDelivered = resultSet.getTimestamp("time_delivered"); | ||
if (timestampReceived != null) { | ||
timeReceived = timestampReceived.toInstant(); | ||
} | ||
|
||
if (timestampDelivered != null) { | ||
timeDelivered = timestampDelivered.toInstant(); | ||
} | ||
|
||
return new PartnerMetadata( | ||
resultSet.getString(METADATA_TABLE_RECEIVED_MESSAGE_ID), | ||
resultSet.getString("sent_message_id"), | ||
resultSet.getString("sender"), | ||
resultSet.getString("receiver"), | ||
timeReceived, | ||
timeDelivered, | ||
resultSet.getString("hash_of_message"), | ||
PartnerMetadataStatus.valueOf(resultSet.getString("delivery_status")), | ||
resultSet.getString("failure_reason"), | ||
PartnerMetadataMessageType.valueOf(resultSet.getString("message_type")), | ||
formatter.convertJsonToObject( | ||
resultSet.getString("sending_application_details"), | ||
new TypeReference<>() {}), | ||
formatter.convertJsonToObject( | ||
resultSet.getString("sending_facility_details"), | ||
new TypeReference<>() {}), | ||
formatter.convertJsonToObject( | ||
resultSet.getString("receiving_application_details"), | ||
new TypeReference<>() {}), | ||
formatter.convertJsonToObject( | ||
resultSet.getString("receiving_facility_details"), | ||
new TypeReference<>() {}), | ||
resultSet.getString("placer_order_number")); | ||
} catch (SQLException | FormatterProcessingException e) { | ||
throw new RuntimeException(e); | ||
} | ||
} | ||
|
||
private List<DbColumn> createDbColumnsFromMetadata(PartnerMetadata metadata) | ||
throws FormatterProcessingException { | ||
return List.of( | ||
new DbColumn( | ||
"received_message_id", | ||
METADATA_TABLE_RECEIVED_MESSAGE_ID, | ||
metadata.receivedSubmissionId(), | ||
false, | ||
Types.VARCHAR), | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is nice. For some time I've been thinking of having a wrapper class that will handle the prepared statements. Thoughts on having the wrapper class so that both storage classes can use it with the dao?