Skip to content
This repository has been archived by the owner on Aug 2, 2022. It is now read-only.

Commit

Permalink
addressed comments
Browse files Browse the repository at this point in the history
  • Loading branch information
chloe-zh committed Dec 11, 2020
1 parent a2a5024 commit 84f6f84
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 39 deletions.
2 changes: 2 additions & 0 deletions docs/user/interfaces/protocol.rst
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,9 @@ Result set::


The formatter sanitizes the csv result with the following rules:

1. If a header cell or data cell is starting with special character including '+', '-', '=' , '@', the sanitizer will insert a single-quote at the start of the cell.

2. If there exists one or more commas (','), the sanitizer will quote the cell with double quotes.

For example::
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,8 @@
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import lombok.Builder;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Singular;

@RequiredArgsConstructor
public class CsvResponseFormatter implements ResponseFormatter<QueryResult> {
Expand All @@ -42,54 +40,63 @@ public CsvResponseFormatter() {

@Override
public String format(QueryResult response) {
CsvResult result = buildCsvResult(response);
String headers = String.join(INLINE_SEPARATOR, result.getHeaders());
ImmutableList.Builder<String> dataLines = new ImmutableList.Builder<>();
result.getData().forEach(line -> dataLines.add(String.join(INLINE_SEPARATOR, line)));
return String.join(
INTERLINE_SEPARATOR, headers, String.join(INTERLINE_SEPARATOR, dataLines.build()));
CsvResult result = new CsvResult(response, sanitize);
return result.getCsv();
}

@Override
public String format(Throwable t) {
return ErrorFormatter.prettyFormat(t);
}

private CsvResult buildCsvResult(QueryResult response) {
CsvResult.Builder builder = CsvResult.builder();
ImmutableList.Builder<List<String>> dataLines = new ImmutableList.Builder<>();

response.columnNameTypes().forEach((column, type) -> builder.header(column));
response.iterator().forEachRemaining(row -> {
ImmutableList.Builder<String> line = new ImmutableList.Builder<>();
// replace null values with empty string
Arrays.asList(row).forEach(val -> line.add(val == null ? "" : val.toString()));
dataLines.add(line.build());
});
builder.data(dataLines.build());

CsvResult result = builder.build();
return sanitize ? result.sanitize() : result;
}

@Builder(builderClassName = "Builder")
/**
* Sanitize methods are migrated from legacy CSV result.
* To deal with special character in column name and to avoid csv injection.
* Sanitize both headers and data lines by:
* 1) First prepend single quote at the start if first char is sensitive (= - + @);
* 2) Second double quote entire cell if any comma is found.
*/
@Getter
@RequiredArgsConstructor
static class CsvResult {
@Singular("header")
private List<String> headers;
private List<List<String>> data;
private final QueryResult response;
private final boolean sanitize;

public String getCsv() {
List<String> headersAndData = new ArrayList<>();
headersAndData.add(getHeaderLine(response, sanitize));
headersAndData.addAll(getDataLines(response, sanitize));
return String.join(INTERLINE_SEPARATOR, headersAndData);
}

/**
* Migrated from legacy CSV result.
* To deal with special character in column name and to avoid csv injection.
* Sanitize both headers and data lines by:
* 1) First prepend single quote if first char is sensitive (= - + @);
* 2) Second double quote entire cell if any comma found.
*/
public CsvResult sanitize() {
headers = sanitizeHeaders(headers);
data = sanitizeData(data);
return this;
private String getHeaderLine(QueryResult response, boolean sanitize) {
List<String> headers = getHeaders(response, sanitize);
return String.join(INLINE_SEPARATOR, headers);
}

private List<String> getDataLines(QueryResult response, boolean sanitize) {
List<List<String>> data = getData(response, sanitize);
return data.stream().map(v -> String.join(INLINE_SEPARATOR, v)).collect(Collectors.toList());
}

private List<String> getHeaders(QueryResult response, boolean sanitize) {
ImmutableList.Builder<String> headers = ImmutableList.builder();
response.columnNameTypes().forEach((column, type) -> headers.add(column));
List<String> result = headers.build();
return sanitize ? sanitizeHeaders(result) : result;
}

private List<List<String>> getData(QueryResult response, boolean sanitize) {
ImmutableList.Builder<List<String>> dataLines = new ImmutableList.Builder<>();
response.iterator().forEachRemaining(row -> {
ImmutableList.Builder<String> line = new ImmutableList.Builder<>();
// replace null values with empty string
Arrays.asList(row).forEach(val -> line.add(val == null ? "" : val.toString()));
dataLines.add(line.build());
});
List<List<String>> result = dataLines.build();
return sanitize ? sanitizeData(result) : result;
}

/**
Expand Down

0 comments on commit 84f6f84

Please sign in to comment.