Skip to content

Commit d507a9f

Browse files
authored
BE: KC: Expose validation errors to UI (#705)
1 parent 318bcc9 commit d507a9f

File tree

3 files changed

+41
-9
lines changed

3 files changed

+41
-9
lines changed

Diff for: api/src/main/java/io/kafbat/ui/client/RetryingKafkaConnectClient.java

+21-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package io.kafbat.ui.client;
22

3+
import com.fasterxml.jackson.annotation.JsonProperty;
34
import io.kafbat.ui.config.ClustersProperties;
45
import io.kafbat.ui.connect.ApiClient;
56
import io.kafbat.ui.connect.api.KafkaConnectClientApi;
@@ -14,9 +15,11 @@
1415
import io.kafbat.ui.exception.KafkaConnectConflictReponseException;
1516
import io.kafbat.ui.exception.ValidationException;
1617
import io.kafbat.ui.util.WebClientConfigurator;
18+
import jakarta.validation.constraints.NotNull;
1719
import java.time.Duration;
1820
import java.util.List;
1921
import java.util.Map;
22+
import java.util.Objects;
2023
import javax.annotation.Nullable;
2124
import lombok.extern.slf4j.Slf4j;
2225
import org.springframework.http.ResponseEntity;
@@ -58,10 +61,24 @@ private static <T> Flux<T> withRetryOnConflict(Flux<T> publisher) {
5861

5962
private static <T> Mono<T> withBadRequestErrorHandling(Mono<T> publisher) {
6063
return publisher
61-
.onErrorResume(WebClientResponseException.BadRequest.class, e ->
62-
Mono.error(new ValidationException("Invalid configuration")))
63-
.onErrorResume(WebClientResponseException.InternalServerError.class, e ->
64-
Mono.error(new ValidationException("Invalid configuration")));
64+
.onErrorResume(WebClientResponseException.BadRequest.class,
65+
RetryingKafkaConnectClient::parseConnectErrorMessage)
66+
.onErrorResume(WebClientResponseException.InternalServerError.class,
67+
RetryingKafkaConnectClient::parseConnectErrorMessage);
68+
}
69+
70+
// Adapted from https://github.com/apache/kafka/blob/a0a501952b6d61f6f273bdb8f842346b51e9dfce/connect/runtime/src/main/java/org/apache/kafka/connect/runtime/rest/entities/ErrorMessage.java
71+
// Adding the connect runtime dependency for this single class seems excessive
72+
private record ErrorMessage(@NotNull @JsonProperty("message") String message) {
73+
}
74+
75+
private static <T> @NotNull Mono<T> parseConnectErrorMessage(WebClientResponseException parseException) {
76+
final var errorMessage = parseException.getResponseBodyAs(ErrorMessage.class);
77+
return Mono.error(new ValidationException(
78+
Objects.requireNonNull(errorMessage,
79+
// see https://github.com/apache/kafka/blob/a0a501952b6d61f6f273bdb8f842346b51e9dfce/connect/runtime/src/main/java/org/apache/kafka/connect/runtime/rest/errors/ConnectExceptionMapper.java
80+
"This should not happen according to the ConnectExceptionMapper")
81+
.message()));
6582
}
6683

6784
@Override

Diff for: api/src/test/java/io/kafbat/ui/KafkaConnectServiceTests.java

+15-5
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import static org.assertj.core.api.Assertions.assertThat;
55
import static org.junit.jupiter.api.Assertions.assertEquals;
66

7+
import io.kafbat.ui.api.model.ErrorResponse;
78
import io.kafbat.ui.model.ConnectorDTO;
89
import io.kafbat.ui.model.ConnectorPluginConfigDTO;
910
import io.kafbat.ui.model.ConnectorPluginConfigValidationResponseDTO;
@@ -268,19 +269,28 @@ public void shouldReturn400WhenConnectReturns500ForInvalidConfigCreate() {
268269

269270

270271
@Test
272+
@SuppressWarnings("checkstyle:LineLength")
271273
public void shouldReturn400WhenConnectReturns400ForInvalidConfigUpdate() {
272274
webTestClient.put()
273275
.uri("/api/clusters/{clusterName}/connects/{connectName}/connectors/{connectorName}/config",
274276
LOCAL, connectName, connectorName)
275277
.bodyValue(Map.of(
276-
"connector.class", "org.apache.kafka.connect.file.FileStreamSinkConnector",
277-
"tasks.max", "invalid number",
278-
"topics", "another-topic",
279-
"file", "/tmp/test"
278+
"connector.class", "org.apache.kafka.connect.file.FileStreamSinkConnector",
279+
"tasks.max", "invalid number",
280+
"topics", "another-topic",
281+
"file", "/tmp/test"
280282
)
281283
)
282284
.exchange()
283-
.expectStatus().isBadRequest();
285+
.expectStatus().isBadRequest()
286+
.expectBody(ErrorResponse.class)
287+
.value(response -> assertThat(response.getMessage()).isEqualTo(
288+
"""
289+
Connector configuration is invalid and contains the following 2 error(s):
290+
Invalid value invalid number for configuration tasks.max: Not a number of type INT
291+
Invalid value null for configuration tasks.max: Value must be non-null
292+
You can also find the above list of errors at the endpoint `/connector-plugins/{connectorType}/config/validate`"""
293+
));
284294

285295
webTestClient.get()
286296
.uri("/api/clusters/{clusterName}/connects/{connectName}/connectors/{connectorName}/config",

Diff for: etc/checkstyle/checkstyle.xml

+5
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,12 @@
4646
<property name="ignorePattern" value="^package.*|^import.*|a href|href|http://|https://|ftp://"/>
4747
</module>
4848

49+
<!-- Needed to use the SuppressWarningsHolder configured below. See https://checkstyle.sourceforge.io/filters/suppresswarningsfilter.html -->
50+
<module name="SuppressWarningsFilter" />
51+
4952
<module name="TreeWalker">
53+
<!-- Make the @SuppressWarnings annotations available to Checkstyle -->
54+
<module name="SuppressWarningsHolder"/>
5055
<module name="OuterTypeFilename"/>
5156
<module name="IllegalTokenText">
5257
<property name="tokens" value="STRING_LITERAL, CHAR_LITERAL"/>

0 commit comments

Comments
 (0)