Skip to content
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
Expand Up @@ -3,6 +3,7 @@
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import lombok.SneakyThrows;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
Expand All @@ -15,27 +16,45 @@
import static java.nio.charset.StandardCharsets.UTF_8;

@Service
@Slf4j
@AllArgsConstructor(onConstructor = @__(@Autowired))
public class StorageConnectorService {
private final StorageConnector storageConnector;
private final ObjectMapper objectMapper;

@SneakyThrows({JsonProcessingException.class, IOException.class})
public void uploadFile(StorageDataWrapper response, String filename) {
String jsonStringResponse = objectMapper.writeValueAsString(response);
var responseBytes = jsonStringResponse.getBytes(UTF_8);
LOGGER.info("Uploading file to storage: filename={}", filename);
try {
String jsonStringResponse = objectMapper.writeValueAsString(response);
var responseBytes = jsonStringResponse.getBytes(UTF_8);
LOGGER.debug("Serialized response payload size: {} bytes", responseBytes.length);

try (var responseInputStream = new ByteArrayInputStream(responseBytes)) {
storageConnector.uploadToStorage(responseInputStream, responseBytes.length, filename);
try (var responseInputStream = new ByteArrayInputStream(responseBytes)) {
storageConnector.uploadToStorage(responseInputStream, responseBytes.length, filename);
LOGGER.info("File uploaded successfully: filename={}", filename);
}
} catch (Exception e) {
LOGGER.error("Failed to upload file: filename={}", filename, e);
throw e;
}
}

@SneakyThrows
public StorageDataWrapper downloadFile(String filename) {
String stringDownload;
try (var inputStream = storageConnector.downloadFromStorage(filename)) {
stringDownload = IOUtils.toString(inputStream, StandardCharsets.UTF_8);
LOGGER.info("Downloading file from storage: filename={}", filename);
try {
String stringDownload;
try (var inputStream = storageConnector.downloadFromStorage(filename)) {
stringDownload = IOUtils.toString(inputStream, StandardCharsets.UTF_8);
LOGGER.debug("Downloaded file size: {} characters", stringDownload.length());
}
StorageDataWrapper result = objectMapper.readValue(stringDownload, StorageDataWrapper.class);
LOGGER.info("File downloaded and deserialized successfully: filename={}", filename);
return result;
} catch (Exception e) {
LOGGER.error("Failed to download file: filename={}", filename, e);
throw e;
}
return objectMapper.readValue(stringDownload, StorageDataWrapper.class);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import org.apache.tika.mime.MimeTypes;
import org.springframework.beans.factory.annotation.Autowired;
Expand All @@ -16,6 +17,7 @@
import java.util.Collections;

@Component
@Slf4j
@AllArgsConstructor(onConstructor = @__(@Autowired))
public class DocumentToMHSTranslator {

Expand All @@ -25,10 +27,14 @@ public class DocumentToMHSTranslator {
public String translateGpcResponseToMhsOutboundRequestData(
DocumentTaskDefinition taskDefinition, String base64Content, String contentType
) {
LOGGER.info("Translating GPC response to MHS outbound payload for conversation {} and document {}",
taskDefinition.getConversationId(), taskDefinition.getDocumentId());
return createOutboundMessage(taskDefinition, base64Content, contentType);
}

public String translateFileContentToMhsOutboundRequestData(DocumentTaskDefinition taskDefinition, String base64Content) {
LOGGER.info("Translating file content to MHS outbound payload for conversation {} and document {}",
taskDefinition.getConversationId(), taskDefinition.getDocumentId());
return createOutboundMessage(taskDefinition, base64Content, MediaType.TEXT_PLAIN_VALUE);
}

Expand All @@ -46,6 +52,8 @@ private String createOutboundMessage(DocumentTaskDefinition taskDefinition, Stri
)
);
} catch (JsonProcessingException e) {
LOGGER.error("Unable to create outbound MHS message for conversation {} and document {}",
taskDefinition.getConversationId(), taskDefinition.getDocumentId(), e);
throw new IllegalArgumentException(e);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,22 @@

import static java.nio.charset.StandardCharsets.UTF_8;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import java.io.InputStream;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import com.fasterxml.jackson.databind.ObjectMapper;

import lombok.SneakyThrows;


@ExtendWith(MockitoExtension.class)
class StorageConnectorServiceTest {
private static final String TEST_ID = "SOME_ID";
Expand Down Expand Up @@ -54,6 +51,21 @@ void When_ValidStorageDataWrapperIsPass_Expect_UploadStorageParamsHaveCorrectVal
);

var actualContent = new String(inputStreamArgumentCaptor.getValue().readAllBytes(), UTF_8);
assertThat(actualContent).isEqualTo("response");
assertEquals("response", actualContent);
}

@Test
@SneakyThrows
void When_DownloadFileSucceeds_Expect_CorrectDeserialization() {
String jsonContent = "{\"id\": \"test\"}";
InputStream mockInputStream = new java.io.ByteArrayInputStream(jsonContent.getBytes(UTF_8));

when(storageConnector.downloadFromStorage("test-file.json")).thenReturn(mockInputStream);
when(objectMapper.readValue(jsonContent, StorageDataWrapper.class)).thenReturn(anyStorageDataWrapper);

StorageDataWrapper result = storageConnectorService.downloadFile("test-file.json");

assertEquals(anyStorageDataWrapper, result);
verify(storageConnector).downloadFromStorage("test-file.json");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package uk.nhs.adaptors.gp2gp.gpc;

import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.read.ListAppender;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.slf4j.LoggerFactory;
import uk.nhs.adaptors.gp2gp.ehr.EhrDocumentMapper;

import java.util.List;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;

@ExtendWith(MockitoExtension.class)
class DocumentToMHSTranslatorLoggingTest {

private static final String CONVERSATION_ID = "conversation_123";
private static final String DOCUMENT_ID = "doc_123";
private static final String MESSAGE_ID = "message_123";
private static final String CONTENT_TYPE = "application/pdf";
private static final String BASE64_CONTENT = "dGVzdA==";

@Mock
private ObjectMapper objectMapper;

@Mock
private EhrDocumentMapper ehrDocumentMapper;

private DocumentToMHSTranslator translator;
private ListAppender<ILoggingEvent> logAppender;
private Logger logger;

@BeforeEach
void setUp() {
translator = new DocumentToMHSTranslator(objectMapper, ehrDocumentMapper);
logger = (Logger) LoggerFactory.getLogger(DocumentToMHSTranslator.class);
logAppender = new ListAppender<>();
logAppender.start();
logger.addAppender(logAppender);
}

@AfterEach
void tearDown() {
logger.detachAppender(logAppender);
logAppender.stop();
}

@Test
void When_TranslatingGpcResponse_Expect_InfoLogWithConversationAndDocument() throws Exception {

GetGpcDocumentTaskDefinition taskDefinition = createTaskDefinition();
when(ehrDocumentMapper.generateMhsPayload(any(), any(), any(), any())).thenReturn("<xml/>");
when(objectMapper.writeValueAsString(any())).thenReturn("{\"payload\":\"ok\"}");

String result = translator.translateGpcResponseToMhsOutboundRequestData(taskDefinition, BASE64_CONTENT, CONTENT_TYPE);

assertEquals("{\"payload\":\"ok\"}", result);
assertContains(Level.INFO,
"Translating GPC response to MHS outbound payload for conversation " + CONVERSATION_ID + " and document " + DOCUMENT_ID);
}

@Test
void When_TranslatingFileContent_Expect_InfoLogWithConversationAndDocument() throws Exception {

GetGpcDocumentTaskDefinition taskDefinition = createTaskDefinition();
when(ehrDocumentMapper.generateMhsPayload(any(), any(), any(), any())).thenReturn("<xml/>");
when(objectMapper.writeValueAsString(any())).thenReturn("{\"payload\":\"ok\"}");

String result = translator.translateFileContentToMhsOutboundRequestData(taskDefinition, BASE64_CONTENT);

assertEquals("{\"payload\":\"ok\"}", result);
assertContains(Level.INFO,
"Translating file content to MHS outbound payload for conversation " + CONVERSATION_ID + " and document " + DOCUMENT_ID);
}

@Test
void When_JsonSerializationFails_Expect_ErrorLogWithConversationAndDocument() throws Exception {

GetGpcDocumentTaskDefinition taskDefinition = createTaskDefinition();
when(ehrDocumentMapper.generateMhsPayload(any(), any(), any(), any())).thenReturn("<xml/>");
when(objectMapper.writeValueAsString(any())).thenThrow(new JsonProcessingException("serialization failed") { });

assertThrows(IllegalArgumentException.class,
() -> translator.translateGpcResponseToMhsOutboundRequestData(taskDefinition, BASE64_CONTENT, CONTENT_TYPE));

assertContains(Level.ERROR,
"Unable to create outbound MHS message for conversation " + CONVERSATION_ID + " and document " + DOCUMENT_ID);
}

private GetGpcDocumentTaskDefinition createTaskDefinition() {
return GetGpcDocumentTaskDefinition.builder()
.conversationId(CONVERSATION_ID)
.documentId(DOCUMENT_ID)
.messageId(MESSAGE_ID)
.build();
}

private void assertContains(Level level, String expectedMessage) {
List<String> messages = logAppender.list.stream()
.filter(event -> event.getLevel() == level)
.map(ILoggingEvent::getFormattedMessage)
.toList();
assertThat(messages).anyMatch(message -> message.contains(expectedMessage));
}
}
Loading