Skip to content
This repository has been archived by the owner on Feb 7, 2025. It is now read-only.

FilePartnerMetadataStorage implementation #704

Merged
merged 8 commits into from
Dec 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package gov.hhs.cdc.trustedintermediary.etor.metadata;

/** Custom exception class use to catch partner metadata exceptions */
public class PartnerMetadataException extends Exception {

public PartnerMetadataException(String message, Throwable cause) {
super(message, cause);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@

/** Interface to store and retrieve our partner-facing metadata. */
public interface PartnerMetadataStorage {
PartnerMetadata readMetadata(String uniqueId);
PartnerMetadata readMetadata(String uniqueId) throws PartnerMetadataException;

/**
* This method will do "upserts". If the record doesn't exist, it is created. If the record
* exists, it is updated.
*
* @param metadata The metadata to save.
*/
void saveMetadata(PartnerMetadata metadata);
void saveMetadata(PartnerMetadata metadata) throws PartnerMetadataException;
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import gov.hhs.cdc.trustedintermediary.etor.metadata.EtorMetadataStep;
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.MetricMetadata;
import java.time.Instant;
Expand All @@ -25,7 +26,11 @@ public void convertAndSend(final Order<?> order) throws UnableToSendOrderExcepti
var partnerMetadata =
new PartnerMetadata(
"uniqueId", "senderName", "receiverName", Instant.now(), "abcd");
partnerMetadataStorage.saveMetadata(partnerMetadata);
try {
partnerMetadataStorage.saveMetadata(partnerMetadata);
} catch (PartnerMetadataException e) {
throw new UnableToSendOrderException("Unable to save metadata for the order", e);
}

var omlOrder = converter.convertMetadataToOmlOrder(order);
metadata.put(order.getFhirResourceId(), EtorMetadataStep.ORDER_CONVERTED_TO_OML);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,71 @@
package gov.hhs.cdc.trustedintermediary.external.localfile;

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.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.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.FileAttribute;
import java.nio.file.attribute.PosixFilePermissions;
import javax.inject.Inject;

/** Implements the {@link PartnerMetadataStorage} using local files. */
public class FilePartnerMetadataStorage implements PartnerMetadataStorage {

private static final FilePartnerMetadataStorage INSTANCE = new FilePartnerMetadataStorage();

@Inject Formatter formatter;
@Inject Logger logger;

private static final Path metadataTempDirectory;

static {
try {
FileAttribute<?> onlyOwnerAttrs =
PosixFilePermissions.asFileAttribute(
PosixFilePermissions.fromString("rwx------"));
metadataTempDirectory = Files.createTempDirectory("metadata", onlyOwnerAttrs);
} catch (Exception e) {
throw new RuntimeException(e);
}
}

private FilePartnerMetadataStorage() {}

public static FilePartnerMetadataStorage getInstance() {
return INSTANCE;
}

@Override
public PartnerMetadata readMetadata(final String uniqueId) {
return null;
public PartnerMetadata readMetadata(final String uniqueId) throws PartnerMetadataException {
Path filePath = getFilePath(uniqueId);
try {
String content = Files.readString(filePath);
return formatter.convertJsonToObject(content, new TypeReference<>() {});
} catch (IOException | FormatterProcessingException e) {
throw new PartnerMetadataException("Unable to read the metadata file", e);
}
}

@Override
public void saveMetadata(final PartnerMetadata metadata) {}
public void saveMetadata(final PartnerMetadata metadata) throws PartnerMetadataException {
Path metadataFilePath = getFilePath(metadata.uniqueId());
try {
String content = formatter.convertToJsonString(metadata);
Files.writeString(metadataFilePath, content);
logger.logInfo("Saved metadata for " + metadata.uniqueId() + " to " + metadataFilePath);
} catch (IOException | FormatterProcessingException e) {
throw new PartnerMetadataException(
"Error saving metadata for " + metadata.uniqueId(), e);
}
}

private Path getFilePath(String uniqueId) {
return metadataTempDirectory.resolve(uniqueId + ".json");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package gov.hhs.cdc.trustedintermediary.etor.metadata


import spock.lang.Specification

class PartnerMetadataExceptionTest extends Specification {
def "constructor works"() {

given:
def message = "something blew up!"
def cause = new NullPointerException()

when:
def exception = new PartnerMetadataException(message, cause)

then:
exception.getMessage() == message
exception.getCause() == cause
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package gov.hhs.cdc.trustedintermediary.etor.metadata


import gov.hhs.cdc.trustedintermediary.PojoTestUtils
import java.time.Instant
import spock.lang.Specification

class PartnerMetadataTest extends Specification {
def "test getters and setters"() {
when:
PojoTestUtils.validateGettersAndSetters(PartnerMetadata)

then:
noExceptionThrown()
}

def "test constructor"() {
given:
def uniqueId = "uniqueId"
def sender = "sender"
def receiver = "receiver"
def timeReceived = Instant.now()
def hash = "abcd"

when:
def metadata = new PartnerMetadata(uniqueId, sender, receiver, timeReceived, hash)

then:
metadata.uniqueId() == uniqueId
metadata.sender() == sender
metadata.receiver() == receiver
metadata.timeReceived() == timeReceived
metadata.hash() == hash
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
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.external.jackson.Jackson
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 spock.lang.Specification

import java.time.Instant

class FilePartnerMetadataStorageTest extends Specification {

def setup() {
TestApplicationContext.reset()
TestApplicationContext.init()
TestApplicationContext.register(FilePartnerMetadataStorage, FilePartnerMetadataStorage.getInstance())
}

def "save and read metadata successfully"() {
given:
def expectedUniqueId = "uniqueId"
PartnerMetadata metadata = new PartnerMetadata(expectedUniqueId, "sender", "receiver", Instant.parse("2023-12-04T18:51:48.941875Z"), "abcd")

TestApplicationContext.register(Formatter, Jackson.getInstance())
TestApplicationContext.injectRegisteredImplementations()

when:
FilePartnerMetadataStorage.getInstance().saveMetadata(metadata)
def actualMetadata = FilePartnerMetadataStorage.getInstance().readMetadata(expectedUniqueId)

then:
actualMetadata == metadata
}

def "saveMetadata throws PartnerMetadataException when unable to save file"() {
given:
PartnerMetadata metadata = new PartnerMetadata("uniqueId", "sender", "receiver", Instant.now(), "abcd")

def mockFormatter = Mock(Formatter)
mockFormatter.convertToJsonString(_ as PartnerMetadata) >> {throw new FormatterProcessingException("error", new Exception())}
TestApplicationContext.register(Formatter, mockFormatter)
TestApplicationContext.injectRegisteredImplementations()

when:
FilePartnerMetadataStorage.getInstance().saveMetadata(metadata)

then:
thrown(PartnerMetadataException)
}

def "readMetadata throws PartnerMetadataException when unable to read file"() {
given:
def mockFormatter = Mock(Formatter)
mockFormatter.convertJsonToObject(_ as String, _ as TypeReference) >> {throw new FormatterProcessingException("error", new Exception())}
TestApplicationContext.register(Formatter, mockFormatter)
TestApplicationContext.injectRegisteredImplementations()

when:
FilePartnerMetadataStorage.getInstance().readMetadata("uniqueId")

then:
thrown(PartnerMetadataException)
}
}
1 change: 1 addition & 0 deletions shared/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ dependencies {
implementation 'com.fasterxml.jackson.core:jackson-core:2.16.0'
implementation 'com.fasterxml.jackson.core:jackson-databind:2.16.0'
implementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.16.0'
implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.16.0'

//fhir
api 'ca.uhn.hapi.fhir:hapi-fhir-base:6.10.0'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import com.fasterxml.jackson.dataformat.yaml.YAMLGenerator;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import gov.hhs.cdc.trustedintermediary.wrappers.Logger;
import gov.hhs.cdc.trustedintermediary.wrappers.YamlCombiner;
import gov.hhs.cdc.trustedintermediary.wrappers.YamlCombinerException;
Expand All @@ -30,6 +32,14 @@ public class Jackson implements Formatter, YamlCombiner {

@Inject Logger logger;

static {
JavaTimeModule javaTimeModule = new JavaTimeModule();
JSON_OBJECT_MAPPER.registerModule(javaTimeModule);
YAML_OBJECT_MAPPER.registerModule(javaTimeModule);
JSON_OBJECT_MAPPER.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
YAML_OBJECT_MAPPER.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
}

private Jackson() {}

public static Jackson getInstance() {
Expand Down
Loading