From a750deaa7ca859be7fb982e4a9a0fda4027fa775 Mon Sep 17 00:00:00 2001
From: Jan Zickermann <jan.zickermann@dataport.de>
Date: Thu, 13 Feb 2025 11:02:52 +0100
Subject: [PATCH] OZG-4097 send-attachment: Rethrow restclientresponse
 exception

---
 .../transfer/PostfachApiFacadeService.java    |  62 +++-
 .../PostfachApiFacadeServiceTest.java         | 293 +++++++++++++++++-
 2 files changed, 332 insertions(+), 23 deletions(-)

diff --git a/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/PostfachApiFacadeService.java b/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/PostfachApiFacadeService.java
index f40594d..1e24151 100644
--- a/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/PostfachApiFacadeService.java
+++ b/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/PostfachApiFacadeService.java
@@ -6,10 +6,12 @@ import java.util.List;
 import java.util.UUID;
 
 import org.springframework.core.io.AbstractResource;
+import org.springframework.web.client.RestClientResponseException;
 
 import de.ozgcloud.nachrichten.postfach.PostfachNachricht;
 import de.ozgcloud.nachrichten.postfach.osiv2.ServiceIfOsi2Enabled;
 import de.ozgcloud.nachrichten.postfach.osiv2.config.Osi2PostfachProperties;
+import de.ozgcloud.nachrichten.postfach.osiv2.exception.Osi2RuntimeException;
 import de.ozgcloud.nachrichten.postfach.osiv2.gen.api.MessageExchangeApi;
 import de.ozgcloud.nachrichten.postfach.osiv2.gen.api.QuarantineApi;
 import de.ozgcloud.nachrichten.postfach.osiv2.model.FileChunkInfo;
@@ -29,6 +31,14 @@ public class PostfachApiFacadeService {
 	private final Osi2PostfachProperties.ApiConfiguration apiConfiguration;
 
 	public void sendMessage(PostfachNachricht nachricht, List<Osi2FileUpload> attachments) {
+		try {
+			sendMessageRaw(nachricht, attachments);
+		} catch (RestClientResponseException e) {
+			throw new Osi2RuntimeException("sendMessage failed!", e);
+		}
+	}
+
+	void sendMessageRaw(PostfachNachricht nachricht, List<Osi2FileUpload> attachments) {
 		messageExchangeApi.sendMessage(
 				requestMapper.mapMailboxId(nachricht),
 				requestMapper.mapOutSendMessageRequestV2(nachricht, attachments)
@@ -36,32 +46,72 @@ public class PostfachApiFacadeService {
 	}
 
 	public <T extends AbstractResource> void uploadChunk(FileChunkInfo chunkInfo, T chunkResource) {
-			quarantineApi.uploadChunk(
-					requestMapper.mapDomainChunkMetaData(chunkInfo),
-					apiConfiguration.getTenant(),
-					apiConfiguration.getNameIdentifier(),
-					chunkResource
-			);
+		try {
+			uploadChunkRaw(chunkInfo, chunkResource);
+		} catch (RestClientResponseException e) {
+			throw new Osi2RuntimeException("uploadChunk failed!", e);
+		}
+	}
+
+	<T extends AbstractResource> void uploadChunkRaw(FileChunkInfo chunkInfo, T chunkResource) {
+		quarantineApi.uploadChunk(
+				requestMapper.mapDomainChunkMetaData(chunkInfo),
+				apiConfiguration.getTenant(),
+				apiConfiguration.getNameIdentifier(),
+				chunkResource
+		);
 	}
 
 	public boolean checkUploadSuccessful(final String messageId) {
+		try {
+			return checkUploadSuccessfulRaw(messageId);
+		} catch (RestClientResponseException e) {
+			throw new Osi2RuntimeException("checkUploadSuccessful failed!", e);
+		}
+	}
+
+	boolean checkUploadSuccessfulRaw(final String messageId) {
 		return responseMapper.isSafe(
 				quarantineApi.getUploadStatus(UUID.fromString(messageId))
 		);
 	}
 
 	public List<String> fetchPendingMessageIds() {
+		try {
+			return fetchPendingMessageIdsRaw();
+		} catch (RestClientResponseException e) {
+			throw new Osi2RuntimeException("fetchPendingMessageIds failed!", e);
+		}
+	}
+
+	List<String> fetchPendingMessageIdsRaw() {
 		return responseMapper.toMessageIds(
 				messageExchangeApi.receiveMessages(MAX_NUMBER_RECEIVED_MESSAGES, 0)
 		);
 	}
 
 	public PostfachNachricht fetchMessageById(final String messageId) {
+		try {
+			return fetchMessageByIdRaw(messageId);
+		} catch (RestClientResponseException e) {
+			throw new Osi2RuntimeException("fetchMessageById failed!", e);
+		}
+	}
+
+	PostfachNachricht fetchMessageByIdRaw(final String messageId) {
 		var messageReply = messageExchangeApi.getMessage(UUID.fromString(messageId));
 		return responseMapper.toPostfachNachricht(messageReply);
 	}
 
 	public void deleteMessage(final String messageId) {
+		try {
+			deleteMessageRaw(messageId);
+		} catch (RestClientResponseException e) {
+			throw new Osi2RuntimeException("deleteMessage failed!", e);
+		}
+	}
+
+	void deleteMessageRaw(final String messageId) {
 		messageExchangeApi.deleteMessage(UUID.fromString(messageId));
 	}
 }
diff --git a/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/PostfachApiFacadeServiceTest.java b/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/PostfachApiFacadeServiceTest.java
index 2ce7a47..97fc19a 100644
--- a/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/PostfachApiFacadeServiceTest.java
+++ b/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/PostfachApiFacadeServiceTest.java
@@ -16,9 +16,11 @@ import org.junit.jupiter.api.Test;
 import org.mockito.InjectMocks;
 import org.mockito.Mock;
 import org.mockito.Spy;
+import org.springframework.web.client.RestClientResponseException;
 
 import de.ozgcloud.nachrichten.postfach.PostfachNachricht;
 import de.ozgcloud.nachrichten.postfach.osiv2.config.Osi2PostfachProperties;
+import de.ozgcloud.nachrichten.postfach.osiv2.exception.Osi2RuntimeException;
 import de.ozgcloud.nachrichten.postfach.osiv2.factory.FileChunkInfoTestFactory;
 import de.ozgcloud.nachrichten.postfach.osiv2.factory.MessageExchangeReceiveMessagesResponseTestFactory;
 import de.ozgcloud.nachrichten.postfach.osiv2.factory.PostfachNachrichtTestFactory;
@@ -39,7 +41,7 @@ class PostfachApiFacadeServiceTest {
 
 	@InjectMocks
 	@Spy
-	PostfachApiFacadeService postfachApiFacadeService;
+	PostfachApiFacadeService service;
 
 	@Mock
 	MessageExchangeApi messageExchangeApi;
@@ -59,6 +61,46 @@ class PostfachApiFacadeServiceTest {
 	@DisplayName("send message")
 	@Nested
 	class TestSendMessage {
+		private final List<Osi2FileUpload> files = List.of(Osi2FileUploadTestFactory.create());
+
+		private final PostfachNachricht nachricht = PostfachNachrichtTestFactory.create();
+
+		@DisplayName("without exception")
+		@Nested
+		class TestWithoutException {
+
+			@DisplayName("should call sendMessage")
+			@Test
+			void shouldCallSendMessage() {
+				doNothing().when(service).sendMessageRaw(any(), any());
+
+				service.sendMessage(nachricht, files);
+
+				verify(service).sendMessageRaw(nachricht, files);
+			}
+		}
+
+		@DisplayName("with restclient response exception")
+		@Nested
+		class TestWithRestclientResponseException {
+			@Mock
+			RestClientResponseException restClientResponseException;
+
+			@DisplayName("should rethrow as osi2 runtime exception")
+			@Test
+			void shouldRethrowAsOsi2RuntimeException() {
+				doThrow(restClientResponseException).when(service).sendMessageRaw(any(), any());
+
+				assertThatThrownBy(() -> service.sendMessage(nachricht, files))
+						.isInstanceOf(Osi2RuntimeException.class)
+						.hasCause(restClientResponseException);
+			}
+		}
+	}
+
+	@DisplayName("send message raw")
+	@Nested
+	class TestSendMessageRaw {
 
 		@Mock
 		OutSendMessageRequestV2 outSendMessageRequestV2;
@@ -72,23 +114,76 @@ class PostfachApiFacadeServiceTest {
 
 		@BeforeEach
 		void mock() {
-			when(osi2RequestMapper.mapMailboxId(nachricht)).thenReturn(MAILBOX_ID);
-			when(osi2RequestMapper.mapOutSendMessageRequestV2(nachricht, files)).thenReturn(outSendMessageRequestV2);
+			when(osi2RequestMapper.mapMailboxId(any())).thenReturn(MAILBOX_ID);
+			when(osi2RequestMapper.mapOutSendMessageRequestV2(any(), any())).thenReturn(outSendMessageRequestV2);
 			when(messageExchangeApi.sendMessage(any(), any())).thenReturn(messageExchangeSendMessageResponse);
 		}
 
+		@DisplayName("should call mapMailboxId")
+		@Test
+		void shouldCallMapMailboxId() {
+			service.sendMessageRaw(nachricht, files);
+
+			verify(osi2RequestMapper).mapMailboxId(nachricht);
+		}
+
+		@DisplayName("should call mapOutSendMessageRequestV2")
+		@Test
+		void shouldCallMapOutSendMessageRequestV2() {
+			service.sendMessageRaw(nachricht, files);
+
+			verify(osi2RequestMapper).mapOutSendMessageRequestV2(nachricht, files);
+		}
+
 		@DisplayName("should call sendMessage")
 		@Test
 		void shouldCallSendMessage() {
-			postfachApiFacadeService.sendMessage(nachricht, files);
+			service.sendMessageRaw(nachricht, files);
 
 			verify(messageExchangeApi).sendMessage(MAILBOX_ID, outSendMessageRequestV2);
 		}
+
 	}
 
 	@DisplayName("fetch pending message ids")
 	@Nested
 	class TestFetchPendingMessageIds {
+		@DisplayName("without exception")
+		@Nested
+		class TestWithoutException {
+
+			@DisplayName("should return fetchPendingMessageIdsRaw")
+			@Test
+			void shouldReturnFetchPendingMessageIdsRaw() {
+				doReturn(List.of(MESSAGE_ID_1, MESSAGE_ID_2)).when(service).fetchPendingMessageIdsRaw();
+
+				var result = service.fetchPendingMessageIds();
+
+				assertThat(result).containsExactly(MESSAGE_ID_1, MESSAGE_ID_2);
+			}
+		}
+
+		@DisplayName("with restclient response exception")
+		@Nested
+		class TestWithRestclientResponseException {
+			@Mock
+			RestClientResponseException restClientResponseException;
+
+			@DisplayName("should rethrow as osi2 runtime exception")
+			@Test
+			void shouldRethrowAsOsi2RuntimeException() {
+				doThrow(restClientResponseException).when(service).fetchPendingMessageIdsRaw();
+
+				assertThatThrownBy(() -> service.fetchPendingMessageIds())
+						.isInstanceOf(Osi2RuntimeException.class)
+						.hasCause(restClientResponseException);
+			}
+		}
+	}
+
+	@DisplayName("fetch pending message ids raw")
+	@Nested
+	class TestFetchPendingMessageIdsRaw {
 
 		@DisplayName("with two pending messages")
 		@Nested
@@ -150,15 +245,64 @@ class PostfachApiFacadeServiceTest {
 		}
 
 		private List<String> fetchMessageIds() {
-			return postfachApiFacadeService.fetchPendingMessageIds();
+			return service.fetchPendingMessageIds();
 		}
 
 	}
 
-	@DisplayName("fetch Message by Id")
+	@DisplayName("fetch message by id")
 	@Nested
 	class TestFetchMessageById {
 
+		private final PostfachNachricht nachricht = PostfachNachrichtTestFactory.create();
+
+		@DisplayName("without exception")
+		@Nested
+		class TestWithoutException {
+			@BeforeEach
+			void mock() {
+				doReturn(nachricht).when(service).fetchMessageByIdRaw(MESSAGE_ID_1);
+			}
+
+			@DisplayName("should call fetchMessageByIdRaw")
+			@Test
+			void shouldCallFetchMessageByIdRaw() {
+				service.fetchMessageById(MESSAGE_ID_1);
+
+				verify(service).fetchMessageByIdRaw(MESSAGE_ID_1);
+			}
+
+			@DisplayName("should return")
+			@Test
+			void shouldReturn() {
+				var result = service.fetchMessageById(MESSAGE_ID_1);
+
+				assertThat(result).isEqualTo(nachricht);
+			}
+		}
+
+		@DisplayName("with restclient response exception")
+		@Nested
+		class TestWithRestclientResponseException {
+			@Mock
+			RestClientResponseException restClientResponseException;
+
+			@DisplayName("should rethrow as osi2 runtime exception")
+			@Test
+			void shouldRethrowAsOsi2RuntimeException() {
+				doThrow(restClientResponseException).when(service).fetchMessageByIdRaw(any());
+
+				assertThatThrownBy(() -> service.fetchMessageById(MESSAGE_ID_1))
+						.isInstanceOf(Osi2RuntimeException.class)
+						.hasCause(restClientResponseException);
+			}
+		}
+	}
+
+	@DisplayName("fetch Message by Id raw")
+	@Nested
+	class TestFetchMessageByIdRaw {
+
 		@Mock
 		V1ReplyMessage replyMessage;
 
@@ -166,7 +310,7 @@ class PostfachApiFacadeServiceTest {
 		void shouldCallGetMessage() {
 			when(messageExchangeApi.getMessage(any())).thenReturn(replyMessage);
 
-			postfachApiFacadeService.fetchMessageById(MESSAGE_ID_1);
+			service.fetchMessageByIdRaw(MESSAGE_ID_1);
 
 			verify(messageExchangeApi).getMessage(any());
 		}
@@ -176,7 +320,7 @@ class PostfachApiFacadeServiceTest {
 			when(messageExchangeApi.getMessage(any())).thenReturn(replyMessage);
 			when(osi2ResponseMapper.toPostfachNachricht(any())).thenReturn(PostfachNachrichtTestFactory.create());
 
-			postfachApiFacadeService.fetchMessageById(MESSAGE_ID_1);
+			service.fetchMessageByIdRaw(MESSAGE_ID_1);
 
 			verify(osi2ResponseMapper).toPostfachNachricht(any());
 		}
@@ -186,7 +330,7 @@ class PostfachApiFacadeServiceTest {
 			when(messageExchangeApi.getMessage(any())).thenReturn(replyMessage);
 			when(osi2ResponseMapper.toPostfachNachricht(any())).thenReturn(PostfachNachrichtTestFactory.create());
 
-			var postfachNachricht = postfachApiFacadeService.fetchMessageById(MESSAGE_ID_1);
+			var postfachNachricht = service.fetchMessageByIdRaw(MESSAGE_ID_1);
 
 			assertThat(postfachNachricht).isInstanceOf(PostfachNachricht.class);
 		}
@@ -195,6 +339,43 @@ class PostfachApiFacadeServiceTest {
 	@DisplayName("upload chunk")
 	@Nested
 	class TestUploadChunk {
+
+		@Mock
+		FileChunkResource chunkResource;
+
+		private final FileChunkInfo info = FileChunkInfoTestFactory.create();
+
+		@DisplayName("should call uploadChunkRaw")
+		@Test
+		void shouldCallUploadChunkRaw() {
+			doNothing().when(service).uploadChunkRaw(any(), any());
+
+			service.uploadChunk(info, chunkResource);
+
+			verify(service).uploadChunkRaw(info, chunkResource);
+		}
+
+		@DisplayName("with restclient response exception")
+		@Nested
+		class TestWithRestclientResponseException {
+			@Mock
+			RestClientResponseException restClientResponseException;
+
+			@DisplayName("should rethrow as osi2 runtime exception")
+			@Test
+			void shouldRethrowAsOsi2RuntimeException() {
+				doThrow(restClientResponseException).when(service).uploadChunkRaw(any(), any());
+
+				assertThatThrownBy(() -> service.uploadChunk(info, chunkResource))
+						.isInstanceOf(Osi2RuntimeException.class)
+						.hasCause(restClientResponseException);
+			}
+		}
+	}
+
+	@DisplayName("upload chunk raw")
+	@Nested
+	class TestUploadChunkRaw {
 		@Mock
 		FileChunkResource chunkResource;
 
@@ -216,7 +397,7 @@ class PostfachApiFacadeServiceTest {
 		@DisplayName("should call mapDomainChunkMetadata")
 		@Test
 		void shouldCallMapDomainChunkMetadata() {
-			postfachApiFacadeService.uploadChunk(info, chunkResource);
+			service.uploadChunkRaw(info, chunkResource);
 
 			verify(osi2RequestMapper).mapDomainChunkMetaData(info);
 		}
@@ -224,7 +405,7 @@ class PostfachApiFacadeServiceTest {
 		@DisplayName("should call uploadChunk")
 		@Test
 		void shouldCallUploadChunk() {
-			postfachApiFacadeService.uploadChunk(info, chunkResource);
+			service.uploadChunkRaw(info, chunkResource);
 
 			verify(quarantineApi).uploadChunk(domainChunkMetaData, TENANT, NAME_IDENTIFIER, chunkResource);
 		}
@@ -234,6 +415,52 @@ class PostfachApiFacadeServiceTest {
 	@Nested
 	class TestCheckUploadSuccessful {
 
+		@DisplayName("without exception")
+		@Nested
+		class TestWithoutException {
+			@BeforeEach
+			void mock() {
+				doReturn(true).when(service).checkUploadSuccessfulRaw(any());
+			}
+
+			@DisplayName("should call checkUploadSuccessfulRaw")
+			@Test
+			void shouldCallCheckUploadSuccessfulRaw() {
+				service.checkUploadSuccessful(MESSAGE_ID_1);
+
+				verify(service).checkUploadSuccessfulRaw(MESSAGE_ID_1);
+			}
+
+			@DisplayName("should return")
+			@Test
+			void shouldReturn() {
+				var result = service.checkUploadSuccessful(MESSAGE_ID_1);
+
+				assertThat(result).isTrue();
+			}
+		}
+
+		@DisplayName("with restclient response exception")
+		@Nested
+		class TestWithRestclientResponseException {
+			@Mock
+			RestClientResponseException restClientResponseException;
+
+			@DisplayName("should rethrow as osi2 runtime exception")
+			@Test
+			void shouldRethrowAsOsi2RuntimeException() {
+				doThrow(restClientResponseException).when(service).checkUploadSuccessfulRaw(any());
+
+				assertThatThrownBy(() -> service.checkUploadSuccessful(MESSAGE_ID_1))
+						.isInstanceOf(Osi2RuntimeException.class)
+						.hasCause(restClientResponseException);
+			}
+		}
+	}
+
+	@DisplayName("check upload successful raw")
+	@Nested
+	class TestCheckUploadSuccessfulRaw {
 
 		private final QuarantineStatus uploadStatus = QuarantineStatus.SAFE;
 
@@ -246,7 +473,7 @@ class PostfachApiFacadeServiceTest {
 		@DisplayName("should call getUploadStatus")
 		@Test
 		void shouldCallGetUploadStatus() {
-			postfachApiFacadeService.checkUploadSuccessful(MESSAGE_ID_1);
+			service.checkUploadSuccessfulRaw(MESSAGE_ID_1);
 
 			verify(quarantineApi).getUploadStatus(UUID.fromString(MESSAGE_ID_1));
 		}
@@ -254,7 +481,7 @@ class PostfachApiFacadeServiceTest {
 		@DisplayName("should call isSafe")
 		@Test
 		void shouldCallIsSafe() {
-			postfachApiFacadeService.checkUploadSuccessful(MESSAGE_ID_1);
+			service.checkUploadSuccessfulRaw(MESSAGE_ID_1);
 
 			verify(osi2ResponseMapper).isSafe(uploadStatus);
 		}
@@ -262,16 +489,48 @@ class PostfachApiFacadeServiceTest {
 		@DisplayName("should return true")
 		@Test
 		void shouldReturnTrue() {
-			var result = postfachApiFacadeService.checkUploadSuccessful(MESSAGE_ID_1);
+			var result = service.checkUploadSuccessfulRaw(MESSAGE_ID_1);
 
 			assertThat(result).isTrue();
 		}
 
 	}
 
-	@DisplayName("Delete Message")
+	@DisplayName("delete message")
 	@Nested
 	class TestDeleteMessage {
+
+		@DisplayName("should call deleteMessage")
+		@Test
+		void shouldCallDeleteMessage() {
+			doNothing().when(service).deleteMessageRaw(any());
+
+			service.deleteMessage(MESSAGE_ID_1);
+
+			verify(service).deleteMessageRaw(MESSAGE_ID_1);
+		}
+
+		@DisplayName("with restclient response exception")
+		@Nested
+		class TestWithRestclientResponseException {
+			@Mock
+			RestClientResponseException restClientResponseException;
+
+			@DisplayName("should rethrow as osi2 runtime exception")
+			@Test
+			void shouldRethrowAsOsi2RuntimeException() {
+				doThrow(restClientResponseException).when(service).deleteMessageRaw(any());
+
+				assertThatThrownBy(() -> service.deleteMessage(MESSAGE_ID_1))
+						.isInstanceOf(Osi2RuntimeException.class)
+						.hasCause(restClientResponseException);
+			}
+		}
+	}
+
+	@DisplayName("Delete Message raw")
+	@Nested
+	class TestDeleteMessageRaw {
 		@Mock
 		MessageExchangeDeleteMessageResponse replyMessage;
 
@@ -279,9 +538,9 @@ class PostfachApiFacadeServiceTest {
 		void shouldCallDeleteMessage() {
 			when(messageExchangeApi.deleteMessage(any())).thenReturn(replyMessage);
 
-			postfachApiFacadeService.deleteMessage(UUID.randomUUID().toString());
+			service.deleteMessage(MESSAGE_ID_1);
 
-			verify(messageExchangeApi).deleteMessage(any());
+			verify(messageExchangeApi).deleteMessage(UUID.fromString(MESSAGE_ID_1));
 		}
 	}
 
-- 
GitLab