Skip to content

Commit 8bd1411

Browse files
committed
Add support for resolving of notification content from message or message code for exceptions
1 parent 279566d commit 8bd1411

File tree

15 files changed

+212
-29
lines changed

15 files changed

+212
-29
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*
2+
* Copyright 2020-2023 CROZ d.o.o, the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
*/
17+
18+
package net.croz.nrich.core.api.exception;
19+
20+
/**
21+
* Marker interface that uses exception message for notification.
22+
*/
23+
public interface ExceptionWithMessage {
24+
25+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
* Copyright 2020-2023 CROZ d.o.o, the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
*/
17+
18+
package net.croz.nrich.core.api.exception;
19+
20+
/**
21+
* Implementing this interface enables resolving exception message from <pre>{@code MessageSource}</pre> using supplied message code.
22+
*/
23+
public interface ExceptionWithMessageCode {
24+
25+
String getMessageCode();
26+
27+
}

nrich-notification-api/src/main/java/net/croz/nrich/notification/api/service/BaseNotificationResponseService.java

+3-4
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,9 @@ public interface BaseNotificationResponseService<T> {
5555
*
5656
* @param throwable exception for which to resolve notification
5757
* @param additionalNotificationData additional notification data to add to notification
58-
* @param exceptionMessageArgumentList optional exception argument list, will be used when resolving exception message
5958
* @return response with notification
6059
*/
61-
T responseWithExceptionNotification(Throwable throwable, AdditionalNotificationData additionalNotificationData, Object... exceptionMessageArgumentList);
60+
T responseWithExceptionNotification(Throwable throwable, AdditionalNotificationData additionalNotificationData);
6261

6362
/**
6463
* Returns response with {@link net.croz.nrich.notification.api.model.Notification} instance.
@@ -87,8 +86,8 @@ default T responseWithValidationFailureNotification(ConstraintViolationException
8786
return responseWithValidationFailureNotification(exception, AdditionalNotificationData.empty());
8887
}
8988

90-
default T responseWithExceptionNotification(Throwable throwable, Object... additionalMessageArgumentList) {
91-
return responseWithExceptionNotification(throwable, AdditionalNotificationData.empty(), additionalMessageArgumentList);
89+
default T responseWithExceptionNotification(Throwable throwable) {
90+
return responseWithExceptionNotification(throwable, AdditionalNotificationData.empty());
9291
}
9392

9493
default T responseWithNotificationActionResolvedFromRequest() {

nrich-notification-api/src/main/java/net/croz/nrich/notification/api/service/NotificationResolverService.java

+3-4
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,9 @@ public interface NotificationResolverService {
5656
*
5757
* @param throwable exception for which to resolve notification
5858
* @param additionalNotificationData additional notification data to add to notification
59-
* @param exceptionMessageArgumentList optional exception argument list, will be used when resolving exception message
6059
* @return {@link Notification} instance
6160
*/
62-
Notification createNotificationForException(Throwable throwable, AdditionalNotificationData additionalNotificationData, Object... exceptionMessageArgumentList);
61+
Notification createNotificationForException(Throwable throwable, AdditionalNotificationData additionalNotificationData);
6362

6463
/**
6564
* Returns {@link Notification} instance for action. Default severity is <pre>INFO</pre>. Resolved action message is added as notification content.
@@ -79,8 +78,8 @@ default ValidationFailureNotification createNotificationForValidationFailure(Con
7978
return createNotificationForValidationFailure(exception, AdditionalNotificationData.empty());
8079
}
8180

82-
default Notification createNotificationForException(Throwable throwable, Object... exceptionMessageArgumentList) {
83-
return createNotificationForException(throwable, AdditionalNotificationData.empty(), exceptionMessageArgumentList);
81+
default Notification createNotificationForException(Throwable throwable) {
82+
return createNotificationForException(throwable, AdditionalNotificationData.empty());
8483
}
8584

8685
default Notification createNotificationForAction(String actionName) {

nrich-notification/README.md

+11-1
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,11 @@ In [`DefaultNotificationResolverService`][default-notification-resolver-service-
263263

264264
### Custom exception notification data
265265

266-
Depending on the context of notification creation, if a notification is created while resolving an exception, then the action code is a **fully qualified class name for that exception**.
266+
Depending on the context of notification creation, if a notification is created while resolving an exception, then the default action code is a **fully qualified class name for that exception**.
267+
If an exception implements [`ExceptionWithMessageCode`][exception-with-message-code-url] then notification content is resolved through provided message code
268+
and if an exception implements [`ExceptionWithMessage`][exception-with-message-url] then notification content is equal to exceptions message
269+
(title is still resolved from fully qualified class name in both cases). If additional arguments are required when resolving message, exception should implement
270+
[`ExceptionWithArguments`][exception-with-arguments-url].
267271

268272
For example, let's say we have this exception handler:
269273

@@ -500,3 +504,9 @@ where we can see that instead of `{0}` the value was interpolated.
500504
[additional-notification-data-url]: ../nrich-notification-api/src/main/java/net/croz/nrich/notification/api/model/AdditionalNotificationData.java
501505

502506
[default-notification-resolver-service-url]: ../nrich-notification/src/main/java/net/croz/nrich/notification/service/DefaultNotificationResolverService.java
507+
508+
[exception-with-message-code-url]: ../nrich-core-api/src/main/java/net/croz/nrich/core/api/exception/ExceptionWithMessageCode.java
509+
510+
[exception-with-message-url]: ../nrich-core-api/src/main/java/net/croz/nrich/core/api/exception/ExceptionWithMessage.java
511+
512+
[exception-with-arguments-url]: ../nrich-core-api/src/main/java/net/croz/nrich/core/api/exception/ExceptionWithArguments.java

nrich-notification/build.gradle

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
description = "Provides a unified response format for data returned to clients"
22

33
dependencies {
4+
api project(":nrich-core-api")
45
api project(":nrich-notification-api")
56

67
annotationProcessor "org.projectlombok:lombok"

nrich-notification/src/main/java/net/croz/nrich/notification/service/DefaultNotificationResolverService.java

+29-4
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@
1818
package net.croz.nrich.notification.service;
1919

2020
import lombok.RequiredArgsConstructor;
21+
import net.croz.nrich.core.api.exception.ExceptionWithArguments;
22+
import net.croz.nrich.core.api.exception.ExceptionWithMessage;
23+
import net.croz.nrich.core.api.exception.ExceptionWithMessageCode;
2124
import net.croz.nrich.notification.api.model.AdditionalNotificationData;
2225
import net.croz.nrich.notification.api.model.Notification;
2326
import net.croz.nrich.notification.api.model.NotificationSeverity;
@@ -87,15 +90,15 @@ public ValidationFailureNotification createNotificationForValidationFailure(Cons
8790
}
8891

8992
@Override
90-
public Notification createNotificationForException(Throwable throwable, AdditionalNotificationData additionalNotificationData, Object... exceptionMessageArgumentList) {
93+
public Notification createNotificationForException(Throwable throwable, AdditionalNotificationData additionalNotificationData) {
9194
String typeName = throwable.getClass().getName();
9295
String titleCode = String.format(NotificationConstants.PREFIX_MESSAGE_FORMAT, typeName, NotificationConstants.MESSAGE_TITLE_SUFFIX);
9396

9497
String title = notificationMessageResolverService.resolveMessage(toList(titleCode, NotificationConstants.ERROR_OCCURRED_MESSAGE_TITLE_CODE), NotificationConstants.EMPTY_MESSAGE);
95-
String contentCode = String.format(NotificationConstants.PREFIX_MESSAGE_FORMAT, typeName, NotificationConstants.MESSAGE_CONTENT_SUFFIX);
98+
9699
String severityCode = String.format(NotificationConstants.PREFIX_MESSAGE_FORMAT, typeName, NotificationConstants.MESSAGE_SEVERITY_SUFFIX);
100+
String content = resolveExceptionContent(throwable);
97101

98-
String content = notificationMessageResolverService.resolveMessage(toList(contentCode, NotificationConstants.ERROR_OCCURRED_DEFAULT_CODE), toList(exceptionMessageArgumentList), null);
99102
NotificationSeverity severity = Optional.ofNullable(additionalNotificationData.getSeverity()).orElse(resolveExceptionSeverity(severityCode));
100103
List<String> messageList = resolveMessageListFromNotificationData(additionalNotificationData.getMessageListDataMap());
101104

@@ -159,8 +162,30 @@ private NotificationSeverity resolveExceptionSeverity(String messageCode) {
159162
return NotificationSeverity.valueOf(severityValue);
160163
}
161164

165+
private String resolveExceptionContent(Throwable throwable) {
166+
if (throwable instanceof ExceptionWithMessage exceptionWithMessage) {
167+
return exceptionWithMessage.getMessage();
168+
}
169+
170+
String contentCode;
171+
if (throwable instanceof ExceptionWithMessageCode exceptionWithMessageCode) {
172+
contentCode = exceptionWithMessageCode.getMessageCode();
173+
}
174+
else {
175+
String typeName = throwable.getClass().getName();
176+
contentCode = String.format(NotificationConstants.PREFIX_MESSAGE_FORMAT, typeName, NotificationConstants.MESSAGE_CONTENT_SUFFIX);
177+
}
178+
179+
List<Object> argumentList = new ArrayList<>();
180+
if (throwable instanceof ExceptionWithArguments exceptionWithArguments) {
181+
argumentList.addAll(toList(exceptionWithArguments.getArgumentList()));
182+
}
183+
184+
return notificationMessageResolverService.resolveMessage(toList(contentCode, NotificationConstants.ERROR_OCCURRED_DEFAULT_CODE), argumentList, null);
185+
}
186+
162187
@SafeVarargs
163-
private final <T> List<T> toList(T... codeList) {
188+
private <T> List<T> toList(T... codeList) {
164189
if (codeList == null) {
165190
return Collections.emptyList();
166191
}

nrich-notification/src/main/java/net/croz/nrich/notification/service/WebMvcNotificationResponseService.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,8 @@ public NotificationResponse responseWithValidationFailureNotification(Constraint
5555
}
5656

5757
@Override
58-
public NotificationResponse responseWithExceptionNotification(Throwable throwable, AdditionalNotificationData additionalNotificationData, Object... exceptionMessageArgumentList) {
59-
Notification notification = notificationResolverService.createNotificationForException(throwable, additionalNotificationData, exceptionMessageArgumentList);
58+
public NotificationResponse responseWithExceptionNotification(Throwable throwable, AdditionalNotificationData additionalNotificationData) {
59+
Notification notification = notificationResolverService.createNotificationForException(throwable, additionalNotificationData);
6060

6161
return new NotificationResponse(notification);
6262
}

nrich-notification/src/test/java/net/croz/nrich/notification/service/DefaultNotificationResolverServiceTest.java

+34-4
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
import net.croz.nrich.notification.stub.NotificationResolverServiceTestException;
2727
import net.croz.nrich.notification.stub.NotificationResolverServiceTestExceptionWithArguments;
2828
import net.croz.nrich.notification.stub.NotificationResolverServiceTestExceptionWithCustomTitle;
29+
import net.croz.nrich.notification.stub.NotificationResolverServiceTestExceptionWithMessage;
30+
import net.croz.nrich.notification.stub.NotificationResolverServiceTestExceptionWithMessageCode;
2931
import net.croz.nrich.notification.stub.NotificationResolverServiceTestRequest;
3032
import net.croz.nrich.notification.stub.NotificationResolverServiceTestRequestWithCustomTitle;
3133
import net.croz.nrich.notification.stub.NotificationResolverServiceTestWithoutMessageRequest;
@@ -157,10 +159,10 @@ void shouldReturnDefinedExceptionMessageAndSeverity() {
157159
@Test
158160
void shouldAddMessageArgumentsForDefinedExceptions() {
159161
// given
160-
Exception exception = new NotificationResolverServiceTestExceptionWithArguments("message");
162+
Exception exception = new NotificationResolverServiceTestExceptionWithArguments("message", "1");
161163

162164
// when
163-
Notification notification = defaultNotificationResolverService.createNotificationForException(exception, "1");
165+
Notification notification = defaultNotificationResolverService.createNotificationForException(exception);
164166

165167
// then
166168
assertThat(notification).isNotNull();
@@ -322,14 +324,42 @@ void shouldConvertConstraintViolationExceptionTooNotificationErrorResponse() {
322324

323325
@Test
324326
void shouldNotFailWithNullArguments() {
325-
// when
327+
// given
326328
AdditionalNotificationData notificationData = AdditionalNotificationData.builder().build();
327-
Throwable thrown = catchThrowable(() -> defaultNotificationResolverService.createNotificationForException(new RuntimeException(), notificationData, (Object[]) null));
329+
Exception exception = new NotificationResolverServiceTestExceptionWithArguments("", (Object[]) null);
330+
331+
// when
332+
Throwable thrown = catchThrowable(() -> defaultNotificationResolverService.createNotificationForException(exception, notificationData));
328333

329334
// then
330335
assertThat(thrown).isNull();
331336
}
332337

338+
@Test
339+
void shouldResolveNotificationContentFromProvidedMessageCode() {
340+
// given
341+
Exception exception = new NotificationResolverServiceTestExceptionWithMessageCode();
342+
343+
// when
344+
Notification notification = defaultNotificationResolverService.createNotificationForException(exception);
345+
346+
// then
347+
assertThat(notification.getContent()).isEqualTo("Content resolved from message code");
348+
}
349+
350+
@Test
351+
void shouldResolveNotificationContentFromProvidedMessage() {
352+
// given
353+
String messageContent = "Message content";
354+
Exception exception = new NotificationResolverServiceTestExceptionWithMessage(messageContent);
355+
356+
// when
357+
Notification notification = defaultNotificationResolverService.createNotificationForException(exception);
358+
359+
// then
360+
assertThat(notification.getContent()).isEqualTo(messageContent);
361+
}
362+
333363
private BindingResult validate(Object objectToValidate, Map<String, Object> valueMap) {
334364
DataBinder binder = new DataBinder(objectToValidate);
335365

nrich-notification/src/test/java/net/croz/nrich/notification/stub/NotificationResolverServiceTestExceptionWithArguments.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,10 @@
1818
package net.croz.nrich.notification.stub;
1919

2020
import lombok.Getter;
21+
import net.croz.nrich.core.api.exception.ExceptionWithArguments;
2122

2223
@Getter
23-
public class NotificationResolverServiceTestExceptionWithArguments extends RuntimeException {
24+
public class NotificationResolverServiceTestExceptionWithArguments extends RuntimeException implements ExceptionWithArguments {
2425

2526
private final transient Object[] argumentList;
2627

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
* Copyright 2020-2023 CROZ d.o.o, the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
*/
17+
18+
package net.croz.nrich.notification.stub;
19+
20+
import net.croz.nrich.core.api.exception.ExceptionWithMessage;
21+
22+
public class NotificationResolverServiceTestExceptionWithMessage extends RuntimeException implements ExceptionWithMessage {
23+
24+
public NotificationResolverServiceTestExceptionWithMessage(String message) {
25+
super(message);
26+
}
27+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
* Copyright 2020-2023 CROZ d.o.o, the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
*/
17+
18+
package net.croz.nrich.notification.stub;
19+
20+
import net.croz.nrich.core.api.exception.ExceptionWithMessageCode;
21+
22+
public class NotificationResolverServiceTestExceptionWithMessageCode extends RuntimeException implements ExceptionWithMessageCode {
23+
24+
@Override
25+
public String getMessageCode() {
26+
return "notification-with-custom-message-code.content";
27+
}
28+
}

nrich-notification/src/test/resources/messages.properties

+19-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,20 @@
1+
#
2+
# Copyright 2020-2023 CROZ d.o.o, the original author or authors.
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# https://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
#
16+
#
17+
118
notification.success.title=Success
219
notification.success.default-message=Action has been executed
320
notification.validation-failed.title=Validation failed
@@ -20,9 +37,10 @@ net.croz.nrich.notification.stub.NotificationResolverServiceTestException.severi
2037
net.croz.nrich.notification.stub.NotificationResolverServiceTestExceptionWithArguments.content=Error message with arguments: {0}
2138

2239
net.croz.nrich.notification.stub.NotificationResolverServiceTestExceptionWithCustomTitle.title=Custom error title
23-
2440
net.croz.nrich.notification.stub.NotificationResolverServiceTestRequestWithCustomTitle.title=Validation failure custom title
2541

42+
notification-with-custom-message-code.content=Content resolved from message code
43+
2644
upload.finished.content=Upload finished
2745
upload.finished.with-title.content=Upload finished
2846
upload.finished.with-title.title=Custom title

nrich-webmvc/build.gradle

-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
description = "Provides @RestControllerAdvice for exception logging and notification resolving and additional serialization and locale features"
22

33
dependencies {
4-
api project(":nrich-core-api")
54
api project(":nrich-logging-api")
65
api project(":nrich-notification-api")
76
api project(":nrich-webmvc-api")

0 commit comments

Comments
 (0)