From 30cf3974baeb98119912b6679019b5a9f81505c0 Mon Sep 17 00:00:00 2001 From: SaranshSinha <31996102+SaranshSinha@users.noreply.github.com> Date: Thu, 19 Feb 2026 14:47:47 +0530 Subject: [PATCH 1/7] Add NPE check while extracting body contents and add a meaningful message to the response Added a map to associate HTTP status codes with error messages and updated error handling to utilize this map. --- .../einsteinbot/sdk/util/WebClientUtil.java | 38 +++++++++++++++---- 1 file changed, 31 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/salesforce/einsteinbot/sdk/util/WebClientUtil.java b/src/main/java/com/salesforce/einsteinbot/sdk/util/WebClientUtil.java index b32b0fa..b714e46 100644 --- a/src/main/java/com/salesforce/einsteinbot/sdk/util/WebClientUtil.java +++ b/src/main/java/com/salesforce/einsteinbot/sdk/util/WebClientUtil.java @@ -9,11 +9,16 @@ import static com.salesforce.einsteinbot.sdk.util.UtilFunctions.maskAuthorizationHeader; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; import java.util.function.Function; import com.salesforce.einsteinbot.sdk.model.Error; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; import org.springframework.http.ReactiveHttpInputMessage; import org.springframework.http.client.reactive.ClientHttpResponse; import org.springframework.web.reactive.function.BodyExtractor; @@ -33,6 +38,14 @@ public class WebClientUtil { private static final Logger logger = LoggerFactory.getLogger(WebClientUtil.class); + private static final Map STATUS_CODE_TO_ERROR_MESSAGE = createStatusCodeToErrorMessageMap(); + + private static Map createStatusCodeToErrorMessageMap() { + Map map = new HashMap<>(); + map.put(429, "Too many requests"); + return Collections.unmodifiableMap(map); + } + public static Mono createLoggingRequestProcessor(ClientRequest clientRequest) { logger.info("Making {} Request to URI {} with Headers : {}", clientRequest.method(), clientRequest.url(), maskAuthorizationHeader(clientRequest.headers())); @@ -60,8 +73,9 @@ public static ExchangeFilterFunction createFilter( public static BodyExtractor, ReactiveHttpInputMessage> errorBodyExtractor() { BodyExtractor, ReactiveHttpInputMessage> extractor = (inputMessage, context) -> { - String contentType = inputMessage.getHeaders().getContentType().toString(); - if (contentType.contains("application/json")) { + HttpHeaders headers = inputMessage.getHeaders(); + MediaType contentType = headers != null ? headers.getContentType() : null; + if (contentType != null && contentType.toString().contains("application/json")) { return BodyExtractors.toMono(Error.class) .extract(inputMessage, context); } else { @@ -75,10 +89,20 @@ private static Mono buildErrorFromClientResponseBodyString(ReactiveHttpIn ClientHttpResponse response = (ClientHttpResponse) clientResponse; Mono bodyString = BodyExtractors.toMono(String.class). extract(clientResponse, context); - return bodyString.map(errorMessage -> new Error() - .status(response.getRawStatusCode()) - .message("This Response content type is not 'application/json', " + - "See the 'error' field for actual error returned by the server.") - .error(errorMessage)); + return bodyString.map(errorMessage -> { + Integer statusCode = response.getRawStatusCode(); + String finalErrorMessage = errorMessage; + + if (statusCode != null && STATUS_CODE_TO_ERROR_MESSAGE.containsKey(statusCode)) { + finalErrorMessage = STATUS_CODE_TO_ERROR_MESSAGE.get(statusCode); + } + + return new Error() + .status(statusCode) + .message("This Response content type is not 'application/json', " + + "See the 'error' field for actual error returned by the server.") + .error(finalErrorMessage); + }); } } + From 97e6d01ec781c521015cc71c4ac925130a69fec9 Mon Sep 17 00:00:00 2001 From: SaranshSinha <31996102+SaranshSinha@users.noreply.github.com> Date: Thu, 19 Feb 2026 14:55:36 +0530 Subject: [PATCH 2/7] Enhance error message handling in WebClientUtil Add relevant message handling for associated error codes. --- .../com/salesforce/einsteinbot/sdk/util/WebClientUtil.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/salesforce/einsteinbot/sdk/util/WebClientUtil.java b/src/main/java/com/salesforce/einsteinbot/sdk/util/WebClientUtil.java index b714e46..5315b69 100644 --- a/src/main/java/com/salesforce/einsteinbot/sdk/util/WebClientUtil.java +++ b/src/main/java/com/salesforce/einsteinbot/sdk/util/WebClientUtil.java @@ -92,7 +92,8 @@ private static Mono buildErrorFromClientResponseBodyString(ReactiveHttpIn return bodyString.map(errorMessage -> { Integer statusCode = response.getRawStatusCode(); String finalErrorMessage = errorMessage; - + + // Add relevant message with the associated error code if (statusCode != null && STATUS_CODE_TO_ERROR_MESSAGE.containsKey(statusCode)) { finalErrorMessage = STATUS_CODE_TO_ERROR_MESSAGE.get(statusCode); } From 9d76891067079a0e7fc244cc7047eda113a8f1f1 Mon Sep 17 00:00:00 2001 From: SaranshSinha <31996102+SaranshSinha@users.noreply.github.com> Date: Thu, 19 Feb 2026 16:17:20 +0530 Subject: [PATCH 3/7] Add relevant test cases --- .../sdk/client/ClientApiWireMockTest.java | 39 ++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/src/test/java/com/salesforce/einsteinbot/sdk/client/ClientApiWireMockTest.java b/src/test/java/com/salesforce/einsteinbot/sdk/client/ClientApiWireMockTest.java index c29b3fc..327e708 100644 --- a/src/test/java/com/salesforce/einsteinbot/sdk/client/ClientApiWireMockTest.java +++ b/src/test/java/com/salesforce/einsteinbot/sdk/client/ClientApiWireMockTest.java @@ -407,4 +407,41 @@ private void stubVersionsResponse(String responseBodyFile, int statusCode) { ); } -} \ No newline at end of file + @Test + public void test429ResponseWithNullContentType() { + wireMock.stubFor( + get(VERSIONS_URI) + .willReturn( + aResponse() + .withStatus(HttpStatus.TOO_MANY_REQUESTS.value()) + .withBody("Too many requests") + ) + ); + + Throwable exceptionThrown = assertThrows(RuntimeException.class, + () -> client.getSupportedVersions()); + + ChatbotResponseException chatbotResponseException = validateAndGetCause(exceptionThrown, + ChatbotResponseException.class); + assertEquals(HttpStatus.TOO_MANY_REQUESTS.value(), chatbotResponseException.getStatus()); + } + + @Test + public void test200ResponseWithApplicationJsonContentType() throws Exception { + String responseBodyFile = "versionsResponse.json"; + wireMock.stubFor( + get(VERSIONS_URI) + .willReturn( + aResponse() + .withStatus(HttpStatus.OK.value()) + .withHeader("Content-Type", "application/json;charset=UTF-8") + .withBodyFile(TEST_MOCK_DIR + responseBodyFile) + ) + ); + + SupportedVersions versions = client.getSupportedVersions(); + + verifyResponseEnvelope(responseBodyFile, versions); + } + +} From 560bf1397e6698dd3f9e21a056853f21b42a2002 Mon Sep 17 00:00:00 2001 From: SaranshSinha <31996102+SaranshSinha@users.noreply.github.com> Date: Thu, 19 Feb 2026 22:52:32 +0530 Subject: [PATCH 4/7] Refactor error handling in WebClientUtil Removed the status code to error message mapping and simplified error handling in the buildErrorFromClientResponseBodyString method. --- .../einsteinbot/sdk/util/WebClientUtil.java | 33 ++++--------------- 1 file changed, 6 insertions(+), 27 deletions(-) diff --git a/src/main/java/com/salesforce/einsteinbot/sdk/util/WebClientUtil.java b/src/main/java/com/salesforce/einsteinbot/sdk/util/WebClientUtil.java index 5315b69..f337142 100644 --- a/src/main/java/com/salesforce/einsteinbot/sdk/util/WebClientUtil.java +++ b/src/main/java/com/salesforce/einsteinbot/sdk/util/WebClientUtil.java @@ -9,9 +9,6 @@ import static com.salesforce.einsteinbot.sdk.util.UtilFunctions.maskAuthorizationHeader; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; import java.util.function.Function; import com.salesforce.einsteinbot.sdk.model.Error; @@ -37,14 +34,6 @@ public class WebClientUtil { private static final Logger logger = LoggerFactory.getLogger(WebClientUtil.class); - - private static final Map STATUS_CODE_TO_ERROR_MESSAGE = createStatusCodeToErrorMessageMap(); - - private static Map createStatusCodeToErrorMessageMap() { - Map map = new HashMap<>(); - map.put(429, "Too many requests"); - return Collections.unmodifiableMap(map); - } public static Mono createLoggingRequestProcessor(ClientRequest clientRequest) { logger.info("Making {} Request to URI {} with Headers : {}", clientRequest.method(), @@ -86,24 +75,14 @@ public static BodyExtractor, ReactiveHttpInputMessage> errorBodyExtr } private static Mono buildErrorFromClientResponseBodyString(ReactiveHttpInputMessage clientResponse, BodyExtractor.Context context) { - ClientHttpResponse response = (ClientHttpResponse) clientResponse; + ClientHttpResponse response = (ClientHttpResponse) clientResponse; Mono bodyString = BodyExtractors.toMono(String.class). extract(clientResponse, context); - return bodyString.map(errorMessage -> { - Integer statusCode = response.getRawStatusCode(); - String finalErrorMessage = errorMessage; - - // Add relevant message with the associated error code - if (statusCode != null && STATUS_CODE_TO_ERROR_MESSAGE.containsKey(statusCode)) { - finalErrorMessage = STATUS_CODE_TO_ERROR_MESSAGE.get(statusCode); - } - - return new Error() - .status(statusCode) - .message("This Response content type is not 'application/json', " + - "See the 'error' field for actual error returned by the server.") - .error(finalErrorMessage); - }); + return bodyString.map(errorMessage -> new Error() + .status(response.getRawStatusCode()) + .message("This Response content type is not 'application/json', " + + "See the 'error' field for actual error returned by the server.") + .error(errorMessage)); } } From 06b3b2e8f5ef649f6cb865a0fd4d452e8678872a Mon Sep 17 00:00:00 2001 From: SaranshSinha <31996102+SaranshSinha@users.noreply.github.com> Date: Fri, 20 Feb 2026 16:52:00 +0530 Subject: [PATCH 5/7] Make content type check case insensitive for JSON --- .../java/com/salesforce/einsteinbot/sdk/util/WebClientUtil.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/salesforce/einsteinbot/sdk/util/WebClientUtil.java b/src/main/java/com/salesforce/einsteinbot/sdk/util/WebClientUtil.java index f337142..eb77956 100644 --- a/src/main/java/com/salesforce/einsteinbot/sdk/util/WebClientUtil.java +++ b/src/main/java/com/salesforce/einsteinbot/sdk/util/WebClientUtil.java @@ -64,7 +64,7 @@ public static BodyExtractor, ReactiveHttpInputMessage> errorBodyExtr BodyExtractor, ReactiveHttpInputMessage> extractor = (inputMessage, context) -> { HttpHeaders headers = inputMessage.getHeaders(); MediaType contentType = headers != null ? headers.getContentType() : null; - if (contentType != null && contentType.toString().contains("application/json")) { + if (contentType != null && contentType.toString().toLowerCase().contains("application/json")) { return BodyExtractors.toMono(Error.class) .extract(inputMessage, context); } else { From d2404e60b393608c370b5d53b20d5008b74cc383 Mon Sep 17 00:00:00 2001 From: SaranshSinha <31996102+SaranshSinha@users.noreply.github.com> Date: Fri, 20 Feb 2026 16:53:54 +0530 Subject: [PATCH 6/7] Fix formatting --- .../einsteinbot/sdk/client/ClientApiWireMockTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/com/salesforce/einsteinbot/sdk/client/ClientApiWireMockTest.java b/src/test/java/com/salesforce/einsteinbot/sdk/client/ClientApiWireMockTest.java index 327e708..00f162d 100644 --- a/src/test/java/com/salesforce/einsteinbot/sdk/client/ClientApiWireMockTest.java +++ b/src/test/java/com/salesforce/einsteinbot/sdk/client/ClientApiWireMockTest.java @@ -407,7 +407,7 @@ private void stubVersionsResponse(String responseBodyFile, int statusCode) { ); } - @Test + @Test public void test429ResponseWithNullContentType() { wireMock.stubFor( get(VERSIONS_URI) From 23f3d6337b61c6a0b4e128a4229764223b46bda7 Mon Sep 17 00:00:00 2001 From: SaranshSinha <31996102+SaranshSinha@users.noreply.github.com> Date: Fri, 20 Feb 2026 17:06:46 +0530 Subject: [PATCH 7/7] Refactor WebClientUtil for formatting improvements Removed unnecessary blank line and fixed formatting in the WebClientUtil class. --- .../com/salesforce/einsteinbot/sdk/util/WebClientUtil.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/salesforce/einsteinbot/sdk/util/WebClientUtil.java b/src/main/java/com/salesforce/einsteinbot/sdk/util/WebClientUtil.java index eb77956..6e8cae0 100644 --- a/src/main/java/com/salesforce/einsteinbot/sdk/util/WebClientUtil.java +++ b/src/main/java/com/salesforce/einsteinbot/sdk/util/WebClientUtil.java @@ -34,7 +34,6 @@ public class WebClientUtil { private static final Logger logger = LoggerFactory.getLogger(WebClientUtil.class); - public static Mono createLoggingRequestProcessor(ClientRequest clientRequest) { logger.info("Making {} Request to URI {} with Headers : {}", clientRequest.method(), clientRequest.url(), maskAuthorizationHeader(clientRequest.headers())); @@ -76,13 +75,12 @@ public static BodyExtractor, ReactiveHttpInputMessage> errorBodyExtr private static Mono buildErrorFromClientResponseBodyString(ReactiveHttpInputMessage clientResponse, BodyExtractor.Context context) { ClientHttpResponse response = (ClientHttpResponse) clientResponse; - Mono bodyString = BodyExtractors.toMono(String.class). + Mono bodyString = BodyExtractors.toMono(String.class). extract(clientResponse, context); - return bodyString.map(errorMessage -> new Error() + return bodyString.map(errorMessage -> new Error() .status(response.getRawStatusCode()) .message("This Response content type is not 'application/json', " + "See the 'error' field for actual error returned by the server.") .error(errorMessage)); } } -