Skip to content

Commit ad5b0d4

Browse files
iliaxiliax
andauthored
Ksql select header's response parsing improved, tests added (#3377)
Co-authored-by: iliax <[email protected]>
1 parent ea34810 commit ad5b0d4

File tree

2 files changed

+55
-7
lines changed

2 files changed

+55
-7
lines changed

kafka-ui-api/src/main/java/com/provectus/kafka/ui/service/ksql/response/ResponseParser.java

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,13 @@
33
import com.fasterxml.jackson.databind.JsonNode;
44
import com.fasterxml.jackson.databind.json.JsonMapper;
55
import com.fasterxml.jackson.databind.node.TextNode;
6+
import com.google.common.annotations.VisibleForTesting;
67
import com.google.common.collect.Lists;
78
import com.provectus.kafka.ui.exception.KsqlApiException;
89
import com.provectus.kafka.ui.service.ksql.KsqlApiClient;
910
import java.util.ArrayList;
10-
import java.util.Arrays;
1111
import java.util.List;
1212
import java.util.Optional;
13-
import java.util.stream.Collectors;
1413
import org.springframework.web.reactive.function.client.WebClientResponseException;
1514

1615
public class ResponseParser {
@@ -24,11 +23,7 @@ public static Optional<KsqlApiClient.KsqlResponseTable> parseSelectResponse(Json
2423
return Optional.of(
2524
KsqlApiClient.KsqlResponseTable.builder()
2625
.header("Schema")
27-
.columnNames(
28-
Arrays.stream(jsonNode.get("header").get("schema").asText().split(","))
29-
.map(String::trim)
30-
.collect(Collectors.toList())
31-
)
26+
.columnNames(parseSelectHeadersString(jsonNode.get("header").get("schema").asText()))
3227
.build());
3328
}
3429
if (arrayFieldNonEmpty(jsonNode, "row")) {
@@ -46,6 +41,34 @@ public static Optional<KsqlApiClient.KsqlResponseTable> parseSelectResponse(Json
4641
return Optional.empty();
4742
}
4843

44+
@VisibleForTesting
45+
static List<String> parseSelectHeadersString(String str) {
46+
List<String> headers = new ArrayList<>();
47+
int structNesting = 0;
48+
boolean quotes = false;
49+
var headerBuilder = new StringBuilder();
50+
for (char ch : str.toCharArray()) {
51+
if (ch == '<') {
52+
structNesting++;
53+
} else if (ch == '>') {
54+
structNesting--;
55+
} else if (ch == '`') {
56+
quotes = !quotes;
57+
} else if (ch == ' ' && headerBuilder.isEmpty()) {
58+
continue; //skipping leading & training whitespaces
59+
} else if (ch == ',' && structNesting == 0 && !quotes) {
60+
headers.add(headerBuilder.toString());
61+
headerBuilder = new StringBuilder();
62+
continue;
63+
}
64+
headerBuilder.append(ch);
65+
}
66+
if (!headerBuilder.isEmpty()) {
67+
headers.add(headerBuilder.toString());
68+
}
69+
return headers;
70+
}
71+
4972
public static KsqlApiClient.KsqlResponseTable errorTableWithTextMsg(String errorText) {
5073
return KsqlApiClient.KsqlResponseTable.builder()
5174
.header("Execution error")
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package com.provectus.kafka.ui.service.ksql.response;
2+
3+
import static org.assertj.core.api.Assertions.assertThat;
4+
5+
import org.junit.jupiter.api.Test;
6+
7+
class ResponseParserTest {
8+
9+
@Test
10+
void parsesSelectHeaderIntoColumnNames() {
11+
assertThat(ResponseParser.parseSelectHeadersString("`inQuotes` INT, notInQuotes INT"))
12+
.containsExactly("`inQuotes` INT", "notInQuotes INT");
13+
14+
assertThat(ResponseParser.parseSelectHeadersString("`name with comma,` INT, name2 STRING"))
15+
.containsExactly("`name with comma,` INT", "name2 STRING");
16+
17+
assertThat(ResponseParser.parseSelectHeadersString(
18+
"`topLvl` INT, `struct` STRUCT<`nested1` STRING, anotherName STRUCT<nested2 INT>>"))
19+
.containsExactly(
20+
"`topLvl` INT",
21+
"`struct` STRUCT<`nested1` STRING, anotherName STRUCT<nested2 INT>>"
22+
);
23+
}
24+
25+
}

0 commit comments

Comments
 (0)