-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Move NotificationClientException handling out of default.
- Loading branch information
1 parent
773aab9
commit d35d8e6
Showing
4 changed files
with
230 additions
and
26 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
81 changes: 81 additions & 0 deletions
81
src/main/java/uk/gov/hmcts/probate/exception/handler/NotificationClientExceptionHandler.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
package uk.gov.hmcts.probate.exception.handler; | ||
|
||
import com.fasterxml.jackson.core.JsonProcessingException; | ||
import com.fasterxml.jackson.databind.JsonNode; | ||
import com.fasterxml.jackson.databind.ObjectMapper; | ||
import lombok.extern.slf4j.Slf4j; | ||
import org.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.http.HttpHeaders; | ||
import org.springframework.http.MediaType; | ||
import org.springframework.http.ResponseEntity; | ||
import org.springframework.web.bind.annotation.ControllerAdvice; | ||
import org.springframework.web.bind.annotation.ExceptionHandler; | ||
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; | ||
import uk.gov.hmcts.probate.model.ccd.raw.response.CallbackResponse; | ||
import uk.gov.service.notify.NotificationClientException; | ||
|
||
import java.util.ArrayList; | ||
import java.util.List; | ||
|
||
@Slf4j | ||
@ControllerAdvice | ||
public class NotificationClientExceptionHandler extends ResponseEntityExceptionHandler { | ||
public static final String UNABLE_TO_SEND_EMAIL = "Unable to send email"; | ||
|
||
private final ObjectMapper objectMapper; | ||
|
||
@Autowired | ||
public NotificationClientExceptionHandler(ObjectMapper objectMapper) { | ||
this.objectMapper = objectMapper; | ||
} | ||
|
||
@ExceptionHandler(value = NotificationClientException.class) | ||
public ResponseEntity<CallbackResponse> handle(NotificationClientException exception) { | ||
log.warn("Notification service exception", exception); | ||
final List<String> errors = List.copyOf(getNotifyErrors(exception.getMessage())); | ||
|
||
final CallbackResponse errorResponse = CallbackResponse.builder() | ||
.errors(errors) | ||
.build(); | ||
HttpHeaders headers = new HttpHeaders(); | ||
headers.setContentType(MediaType.APPLICATION_JSON); | ||
|
||
return ResponseEntity.ok() | ||
.headers(headers) | ||
.body(errorResponse); | ||
} | ||
|
||
/** | ||
* This relies on the behaviour of NotifyClient#performPostRequest passing the connection's error stream as a json | ||
* string into the exception message, so is liable to break. | ||
*/ | ||
private List<String> getNotifyErrors(final String exMessage) { | ||
final String exJson = exMessage.replaceFirst("[^{]*", ""); | ||
|
||
final List<String> errors = new ArrayList<>(); | ||
errors.add(UNABLE_TO_SEND_EMAIL); | ||
|
||
final JsonNode outerJson; | ||
try { | ||
outerJson = objectMapper.readTree(exJson); | ||
} catch (JsonProcessingException e) { | ||
return errors; | ||
} | ||
|
||
if (!outerJson.isObject()) { | ||
return errors; | ||
} | ||
if (!outerJson.has("errors") || !outerJson.get("errors").isArray()) { | ||
return errors; | ||
} | ||
|
||
final JsonNode errorsJson = outerJson.get("errors"); | ||
for (final JsonNode errorJson : errorsJson) { | ||
if (errorJson.isObject() && errorJson.has("message") && errorJson.get("message").isTextual()) { | ||
final String message = errorJson.get("message").asText(); | ||
errors.add(message); | ||
} | ||
} | ||
return errors; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
149 changes: 149 additions & 0 deletions
149
...t/java/uk/gov/hmcts/probate/exception/handler/NotificationClientExceptionHandlerTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
package uk.gov.hmcts.probate.exception.handler; | ||
|
||
import com.fasterxml.jackson.core.JsonParseException; | ||
import com.fasterxml.jackson.core.JsonProcessingException; | ||
import com.fasterxml.jackson.databind.ObjectMapper; | ||
import org.junit.jupiter.api.BeforeEach; | ||
import org.junit.jupiter.api.Test; | ||
import org.springframework.http.ResponseEntity; | ||
import uk.gov.hmcts.probate.model.ccd.raw.response.CallbackResponse; | ||
import uk.gov.service.notify.NotificationClientException; | ||
|
||
import static org.junit.jupiter.api.Assertions.assertEquals; | ||
import static org.mockito.ArgumentMatchers.any; | ||
import static org.mockito.ArgumentMatchers.anyString; | ||
import static org.mockito.Mockito.mock; | ||
import static org.mockito.Mockito.spy; | ||
import static org.mockito.Mockito.when; | ||
import static org.springframework.http.HttpStatus.OK; | ||
|
||
public class NotificationClientExceptionHandlerTest { | ||
NotificationClientExceptionHandler underTest; | ||
|
||
ObjectMapper objectMapperSpy; | ||
|
||
@BeforeEach | ||
void setUp() { | ||
objectMapperSpy = spy(ObjectMapper.class); | ||
underTest = new NotificationClientExceptionHandler(objectMapperSpy); | ||
} | ||
|
||
@Test | ||
void shouldReturnNotificationClientException() { | ||
final String firstError = "first_error"; | ||
final String secondError = "second_error"; | ||
final String exceptMsg = new StringBuilder() | ||
.append("Status code: 400 ") | ||
.append("{\"errors\":[") | ||
.append("{\"message\":\"").append(firstError).append("\"},") | ||
.append("{\"message\":\"").append(secondError).append("\"}") | ||
.append("]}") | ||
.toString(); | ||
|
||
NotificationClientException notificationClientException = mock(NotificationClientException.class); | ||
when(notificationClientException.getMessage()).thenReturn(exceptMsg); | ||
|
||
ResponseEntity<CallbackResponse> response = underTest.handle(notificationClientException); | ||
|
||
assertEquals(OK, response.getStatusCode(), "Expected HTTP OK (200) response status"); | ||
assertEquals(3, response.getBody().getErrors().size(), "Expected three errors"); | ||
assertEquals(NotificationClientExceptionHandler.UNABLE_TO_SEND_EMAIL, response.getBody().getErrors().get(0), | ||
"expected first error to be from handler"); | ||
assertEquals(firstError, response.getBody().getErrors().get(1), | ||
"expected second error to match from exception message"); | ||
assertEquals(secondError, response.getBody().getErrors().get(2), | ||
"expected third error to match from exception message"); | ||
} | ||
|
||
@Test | ||
void shouldReturnNotificationClientExceptionWhenInvalidJson() throws JsonProcessingException { | ||
final String exceptMsg = ""; | ||
|
||
NotificationClientException notificationClientException = mock(NotificationClientException.class); | ||
when(notificationClientException.getMessage()).thenReturn(exceptMsg); | ||
|
||
when(objectMapperSpy.readTree(anyString())).thenThrow(new JsonParseException(exceptMsg)); | ||
|
||
ResponseEntity<CallbackResponse> response = underTest.handle(notificationClientException); | ||
|
||
assertEquals(OK, response.getStatusCode(), "Expected HTTP OK (200) response status"); | ||
assertEquals(1, response.getBody().getErrors().size(), "Expected only one error on json parse failure"); | ||
assertEquals(NotificationClientExceptionHandler.UNABLE_TO_SEND_EMAIL, response.getBody().getErrors().get(0), "expected first error to be from handler"); | ||
} | ||
|
||
@Test | ||
void shouldReturnNotificationClientExceptionWhenJsonArray() throws JsonProcessingException { | ||
final String exceptMsg = "[]"; | ||
|
||
NotificationClientException notificationClientException = mock(NotificationClientException.class); | ||
when(notificationClientException.getMessage()).thenReturn(exceptMsg); | ||
|
||
ResponseEntity<CallbackResponse> response = underTest.handle(notificationClientException); | ||
|
||
assertEquals(OK, response.getStatusCode(), "Expected HTTP OK (200) response status"); | ||
assertEquals(1, response.getBody().getErrors().size(), "Expected only one error on json parse failure"); | ||
assertEquals(NotificationClientExceptionHandler.UNABLE_TO_SEND_EMAIL, response.getBody().getErrors().get(0), "expected first error to be from handler"); | ||
} | ||
|
||
@Test | ||
void shouldReturnNotificationClientExceptionWhenJsonMissingErrors() throws JsonProcessingException { | ||
final String firstError = "first_error"; | ||
final String secondError = "second_error"; | ||
final String exceptMsg = "{}"; | ||
|
||
NotificationClientException notificationClientException = mock(NotificationClientException.class); | ||
when(notificationClientException.getMessage()).thenReturn(exceptMsg); | ||
|
||
ResponseEntity<CallbackResponse> response = underTest.handle(notificationClientException); | ||
|
||
assertEquals(OK, response.getStatusCode(), "Expected HTTP OK (200) response status"); | ||
assertEquals(1, response.getBody().getErrors().size(), "Expected only one error on json parse failure"); | ||
assertEquals(NotificationClientExceptionHandler.UNABLE_TO_SEND_EMAIL, response.getBody().getErrors().get(0), "expected first error to be from handler"); | ||
} | ||
|
||
@Test | ||
void shouldReturnNotificationClientExceptionWhenJsonErrorsIsObject() throws JsonProcessingException { | ||
final String firstError = "first_error"; | ||
final String secondError = "second_error"; | ||
final String exceptMsg = "{\"errors\":{}}"; | ||
|
||
NotificationClientException notificationClientException = mock(NotificationClientException.class); | ||
when(notificationClientException.getMessage()).thenReturn(exceptMsg); | ||
|
||
ResponseEntity<CallbackResponse> response = underTest.handle(notificationClientException); | ||
|
||
assertEquals(OK, response.getStatusCode(), "Expected HTTP OK (200) response status"); | ||
assertEquals(1, response.getBody().getErrors().size(), "Expected only one error on json parse failure"); | ||
assertEquals(NotificationClientExceptionHandler.UNABLE_TO_SEND_EMAIL, response.getBody().getErrors().get(0), "expected first error to be from handler"); | ||
} | ||
|
||
@Test | ||
void shouldReturnNotificationClientExceptionWhenJsonErrorsContainsUnexpected() throws JsonProcessingException { | ||
final String firstError = "first_error"; | ||
final String secondError = "second_error"; | ||
final String exceptMsg = new StringBuilder() | ||
.append("{\"errors\":[") | ||
.append("{\"message\":\"").append(firstError).append("\"},") | ||
.append("[],") | ||
.append("{},") | ||
.append("{\"message\": 0},") | ||
.append("{\"message\":\"").append(secondError).append("\"}") | ||
.append("]}") | ||
.toString(); | ||
|
||
NotificationClientException notificationClientException = mock(NotificationClientException.class); | ||
when(notificationClientException.getMessage()).thenReturn(exceptMsg); | ||
|
||
ResponseEntity<CallbackResponse> response = underTest.handle(notificationClientException); | ||
|
||
|
||
assertEquals(OK, response.getStatusCode(), "Expected HTTP OK (200) response status"); | ||
assertEquals(3, response.getBody().getErrors().size(), "Expected three errors"); | ||
assertEquals(NotificationClientExceptionHandler.UNABLE_TO_SEND_EMAIL, response.getBody().getErrors().get(0), | ||
"expected first error to be from handler"); | ||
assertEquals(firstError, response.getBody().getErrors().get(1), | ||
"expected second error to match from exception message"); | ||
assertEquals(secondError, response.getBody().getErrors().get(2), | ||
"expected third error to match from exception message"); | ||
} | ||
} |