From 2fa721003ac2c1886d3250334affe0c2c00eb7fe Mon Sep 17 00:00:00 2001 From: aayushbaluni <73417844+aayushbaluni@users.noreply.github.com> Date: Sat, 2 May 2026 00:03:28 +0530 Subject: [PATCH 1/2] fix: encode Google Sheets batchGet range query params for + in sheet names Use application/x-www-form-urlencoded encoding for ranges on values:batchGet so a literal + in sheet titles is sent as %2B instead of being decoded as space. Fixes #41536 --- .../external/config/GetStructureMethod.java | 8 +++++-- .../com/external/config/RowsGetMethod.java | 8 +++++-- .../utils/GoogleSheetsApiEncoding.java | 21 +++++++++++++++++++ .../utils/GoogleSheetsApiEncodingTest.java | 16 ++++++++++++++ 4 files changed, 49 insertions(+), 4 deletions(-) create mode 100644 app/server/appsmith-plugins/googleSheetsPlugin/src/main/java/com/external/utils/GoogleSheetsApiEncoding.java create mode 100644 app/server/appsmith-plugins/googleSheetsPlugin/src/test/java/com/external/utils/GoogleSheetsApiEncodingTest.java diff --git a/app/server/appsmith-plugins/googleSheetsPlugin/src/main/java/com/external/config/GetStructureMethod.java b/app/server/appsmith-plugins/googleSheetsPlugin/src/main/java/com/external/config/GetStructureMethod.java index ca6925c9b59f..4504b559cb9c 100644 --- a/app/server/appsmith-plugins/googleSheetsPlugin/src/main/java/com/external/config/GetStructureMethod.java +++ b/app/server/appsmith-plugins/googleSheetsPlugin/src/main/java/com/external/config/GetStructureMethod.java @@ -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; @@ -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 { @@ -67,15 +69,17 @@ public boolean validateExecutionMethodRequest(MethodConfig methodConfig) { public WebClient.RequestHeadersSpec getExecutionClient(WebClient webClient, MethodConfig methodConfig) { final List ranges = validateInputs(methodConfig); + final List 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()); } diff --git a/app/server/appsmith-plugins/googleSheetsPlugin/src/main/java/com/external/config/RowsGetMethod.java b/app/server/appsmith-plugins/googleSheetsPlugin/src/main/java/com/external/config/RowsGetMethod.java index 155fbe5c2efc..070421fdb6f0 100644 --- a/app/server/appsmith-plugins/googleSheetsPlugin/src/main/java/com/external/config/RowsGetMethod.java +++ b/app/server/appsmith-plugins/googleSheetsPlugin/src/main/java/com/external/config/RowsGetMethod.java @@ -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; @@ -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 @@ -97,15 +99,17 @@ public boolean validateExecutionMethodRequest(MethodConfig methodConfig) { public WebClient.RequestHeadersSpec getExecutionClient(WebClient webClient, MethodConfig methodConfig) { final List ranges = validateInputs(methodConfig); + final List 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()); } diff --git a/app/server/appsmith-plugins/googleSheetsPlugin/src/main/java/com/external/utils/GoogleSheetsApiEncoding.java b/app/server/appsmith-plugins/googleSheetsPlugin/src/main/java/com/external/utils/GoogleSheetsApiEncoding.java new file mode 100644 index 000000000000..ae90229773e0 --- /dev/null +++ b/app/server/appsmith-plugins/googleSheetsPlugin/src/main/java/com/external/utils/GoogleSheetsApiEncoding.java @@ -0,0 +1,21 @@ +package com.external.utils; + +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; + +/** + * Encoding helpers for Google Sheets HTTP APIs. Query parameters are often parsed as + * {@code application/x-www-form-urlencoded}, where a literal '+' must be sent as {@code %2B}. + */ +public final class GoogleSheetsApiEncoding { + + private GoogleSheetsApiEncoding() {} + + /** + * Encodes a string for use as a Google Sheets API query parameter value (e.g. {@code ranges} + * on {@code spreadsheets.values.batchGet}). + */ + public static String encodeQueryParameter(String value) { + return URLEncoder.encode(value, StandardCharsets.UTF_8); + } +} diff --git a/app/server/appsmith-plugins/googleSheetsPlugin/src/test/java/com/external/utils/GoogleSheetsApiEncodingTest.java b/app/server/appsmith-plugins/googleSheetsPlugin/src/test/java/com/external/utils/GoogleSheetsApiEncodingTest.java new file mode 100644 index 000000000000..20b338c55984 --- /dev/null +++ b/app/server/appsmith-plugins/googleSheetsPlugin/src/test/java/com/external/utils/GoogleSheetsApiEncodingTest.java @@ -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); + } +} From 38cac25cb8a802d02d85a86559906bf64186e328 Mon Sep 17 00:00:00 2001 From: aayushbaluni <73417844+aayushbaluni@users.noreply.github.com> Date: Sat, 2 May 2026 00:03:28 +0530 Subject: [PATCH 2/2] docs: add Javadoc to GoogleSheetsApiEncoding and callers --- .../external/config/GetStructureMethod.java | 5 +++ .../com/external/config/RowsGetMethod.java | 5 +++ .../utils/GoogleSheetsApiEncoding.java | 33 +++++++++++++++---- 3 files changed, 37 insertions(+), 6 deletions(-) diff --git a/app/server/appsmith-plugins/googleSheetsPlugin/src/main/java/com/external/config/GetStructureMethod.java b/app/server/appsmith-plugins/googleSheetsPlugin/src/main/java/com/external/config/GetStructureMethod.java index 4504b559cb9c..1eef17abf6f0 100644 --- a/app/server/appsmith-plugins/googleSheetsPlugin/src/main/java/com/external/config/GetStructureMethod.java +++ b/app/server/appsmith-plugins/googleSheetsPlugin/src/main/java/com/external/config/GetStructureMethod.java @@ -65,6 +65,11 @@ 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) { diff --git a/app/server/appsmith-plugins/googleSheetsPlugin/src/main/java/com/external/config/RowsGetMethod.java b/app/server/appsmith-plugins/googleSheetsPlugin/src/main/java/com/external/config/RowsGetMethod.java index 070421fdb6f0..e7d87c945d37 100644 --- a/app/server/appsmith-plugins/googleSheetsPlugin/src/main/java/com/external/config/RowsGetMethod.java +++ b/app/server/appsmith-plugins/googleSheetsPlugin/src/main/java/com/external/config/RowsGetMethod.java @@ -95,6 +95,11 @@ 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) { diff --git a/app/server/appsmith-plugins/googleSheetsPlugin/src/main/java/com/external/utils/GoogleSheetsApiEncoding.java b/app/server/appsmith-plugins/googleSheetsPlugin/src/main/java/com/external/utils/GoogleSheetsApiEncoding.java index ae90229773e0..fb3327209440 100644 --- a/app/server/appsmith-plugins/googleSheetsPlugin/src/main/java/com/external/utils/GoogleSheetsApiEncoding.java +++ b/app/server/appsmith-plugins/googleSheetsPlugin/src/main/java/com/external/utils/GoogleSheetsApiEncoding.java @@ -4,18 +4,39 @@ import java.nio.charset.StandardCharsets; /** - * Encoding helpers for Google Sheets HTTP APIs. Query parameters are often parsed as - * {@code application/x-www-form-urlencoded}, where a literal '+' must be sent as {@code %2B}. + * Utility class providing URL encoding helpers for Google Sheets API query parameters. + *

+ * 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. + *

*/ public final class GoogleSheetsApiEncoding { - private 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 use as a Google Sheets API query parameter value (e.g. {@code ranges} - * on {@code spreadsheets.values.batchGet}). + * Encodes a string for safe use as a Google Sheets API query parameter value. + *

+ * 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)}). + *

+ * + * @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); + return URLEncoder.encode(value, StandardCharsets.UTF_8) + .replace("+", "%20"); } }