Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import com.external.constants.ErrorMessages;
import com.external.domains.RowObject;
import com.external.plugins.exceptions.GSheetsPluginError;
import com.external.utils.GoogleSheetsApiEncoding;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
Expand All @@ -23,6 +24,7 @@
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

@Slf4j
public class GetStructureMethod implements ExecutionMethod, TriggerMethod {
Expand Down Expand Up @@ -63,19 +65,26 @@ public boolean validateExecutionMethodRequest(MethodConfig methodConfig) {
return true;
}

/**
* Builds the GET request for {@code spreadsheets.values.batchGet} used to infer sheet structure.
* Range strings are encoded with {@link GoogleSheetsApiEncoding#encodeQueryParameter(String)}
* so sheet names containing characters such as '+' are sent correctly in the query string.
*/
@Override
public WebClient.RequestHeadersSpec<?> getExecutionClient(WebClient webClient, MethodConfig methodConfig) {

final List<String> ranges = validateInputs(methodConfig);
final List<String> encodedRanges =
ranges.stream().map(GoogleSheetsApiEncoding::encodeQueryParameter).collect(Collectors.toList());

UriComponentsBuilder uriBuilder = getBaseUriBuilder(
this.BASE_SHEETS_API_URL, methodConfig.getSpreadsheetId() /* spreadsheet Id */ + "/values:batchGet");
uriBuilder.queryParam("majorDimension", "ROWS");
uriBuilder.queryParam("ranges", ranges);
uriBuilder.queryParam("ranges", encodedRanges);

return webClient
.method(HttpMethod.GET)
.uri(uriBuilder.build(false).toUri())
.uri(uriBuilder.build(true).toUri())
.body(BodyInserters.empty());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import com.external.constants.ErrorMessages;
import com.external.domains.RowObject;
import com.external.plugins.exceptions.GSheetsPluginError;
import com.external.utils.GoogleSheetsApiEncoding;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
Expand All @@ -24,6 +25,7 @@
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
* API reference: https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets.values/get
Expand Down Expand Up @@ -93,19 +95,26 @@ public boolean validateExecutionMethodRequest(MethodConfig methodConfig) {
return true;
}

/**
* Builds the GET request for {@code spreadsheets.values.batchGet} to fetch row data.
* Range strings are encoded with {@link GoogleSheetsApiEncoding#encodeQueryParameter(String)}
* so sheet names containing characters such as '+' are sent correctly in the query string.
*/
@Override
public WebClient.RequestHeadersSpec<?> getExecutionClient(WebClient webClient, MethodConfig methodConfig) {

final List<String> ranges = validateInputs(methodConfig);
final List<String> encodedRanges =
ranges.stream().map(GoogleSheetsApiEncoding::encodeQueryParameter).collect(Collectors.toList());

UriComponentsBuilder uriBuilder = getBaseUriBuilder(
this.BASE_SHEETS_API_URL, methodConfig.getSpreadsheetId() /* spreadsheet Id */ + "/values:batchGet");
uriBuilder.queryParam("majorDimension", "ROWS");
uriBuilder.queryParam("ranges", ranges);
uriBuilder.queryParam("ranges", encodedRanges);

return webClient
.method(HttpMethod.GET)
.uri(uriBuilder.build(false).toUri())
.uri(uriBuilder.build(true).toUri())
.body(BodyInserters.empty());
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.external.utils;

import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;

/**
* Utility class providing URL encoding helpers for Google Sheets API query parameters.
* <p>
* Google Sheets API range parameters (e.g., A1 notation with sheet names containing
* special characters like '+') must be percent-encoded before being appended as query
* parameters. Standard URI builders may leave '+' unencoded, causing the API to
* misinterpret it as a space.
* </p>
*/
public final class GoogleSheetsApiEncoding {

/**
* Private constructor to prevent instantiation of this utility class.
*
* @throws UnsupportedOperationException always, since this class should not be instantiated
*/
private GoogleSheetsApiEncoding() {
throw new UnsupportedOperationException("Utility class — do not instantiate");
}

/**
* Encodes a string for safe use as a Google Sheets API query parameter value.
* <p>
* Uses {@link URLEncoder#encode(String, java.nio.charset.Charset)} with UTF-8
* and additionally replaces '+' (which URLEncoder uses for spaces) with '%20'
* so the value can be placed inside a URI that is already considered pre-encoded
* (i.e., built with {@code UriComponentsBuilder.build(true)}).
* </p>
*
* @param value the raw query parameter value to encode; must not be {@code null}
* @return the percent-encoded value safe for URI query parameters
*/
public static String encodeQueryParameter(String value) {
return URLEncoder.encode(value, StandardCharsets.UTF_8)
.replace("+", "%20");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.external.utils;

import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;

public class GoogleSheetsApiEncodingTest {

@Test
public void encodeQueryParameter_plusInSheetName_usesPercent2BNotLiteralPlus() {
String encoded = GoogleSheetsApiEncoding.encodeQueryParameter("'Data+A'!1:1");
assertTrue(encoded.contains("%2B"), encoded);
assertFalse(encoded.contains("+"), encoded);
}
}