This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/camel.git
commit 843ab12452c01f1f9cf2993c8c598e58230dda2b Author: Claus Ibsen <[email protected]> AuthorDate: Sat Oct 24 20:26:36 2026 +0200 CAMEL-26028: camel-support - the error of a failed API component call names its arguments, without their values (#28310) Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]> Signed-off-by: Claus Ibsen <[email protected]> --- .../support/component/ApiMethodHelperTest.java | 26 +++++++++++++++++ .../camel/support/component/ApiMethodHelper.java | 34 ++++++++++++++++++++-- .../ROOT/pages/camel-4x-upgrade-guide-4_18.adoc | 7 +++++ .../ROOT/pages/camel-4x-upgrade-guide-4_22.adoc | 7 +++++ .../ROOT/pages/camel-4x-upgrade-guide-4_24.adoc | 7 +++++ 5 files changed, 78 insertions(+), 3 deletions(-) diff --git a/core/camel-core/src/test/java/org/apache/camel/support/component/ApiMethodHelperTest.java b/core/camel-core/src/test/java/org/apache/camel/support/component/ApiMethodHelperTest.java index cac6e63b9f07..2a658a78268c 100644 --- a/core/camel-core/src/test/java/org/apache/camel/support/component/ApiMethodHelperTest.java +++ b/core/camel-core/src/test/java/org/apache/camel/support/component/ApiMethodHelperTest.java @@ -24,6 +24,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; +import org.apache.camel.RuntimeCamelException; import org.junit.jupiter.api.Test; import static org.apache.camel.support.component.ApiMethodArg.arg; @@ -189,6 +190,31 @@ public class ApiMethodHelperTest { assertEquals("Howdy Dave", ApiMethodHelper.invokeMethod(proxy, TestMethod.SAYHI_1, properties), "Derived sayHi(name)"); } + @Test + public void testInvokeMethodErrorShowsNoArgumentValues() { + // the arguments hold the endpoint options, such as a password, and the message data + TestProxy proxy = new TestProxy() { + @Override + public String sayHi(String name) { + throw new IllegalStateException("boom"); + } + }; + final Map<String, Object> properties = new HashMap<>(); + properties.put("name", "Dave"); + properties.put("password", "Secret123"); + + RuntimeCamelException e = assertThrows(RuntimeCamelException.class, + () -> ApiMethodHelper.invokeMethod(proxy, TestMethod.SAYHI_1, properties)); + assertEquals("Error invoking sayHi with [name, password]: boom", e.getMessage()); + + // an argument of the wrong type fails in the invocation itself + properties.put("name", 42); + e = assertThrows(RuntimeCamelException.class, + () -> ApiMethodHelper.invokeMethod(proxy, TestMethod.SAYHI_1, properties)); + assertTrue(e.getMessage().startsWith("Error invoking sayHi with [name, password]: "), e.getMessage()); + assertFalse(e.getMessage().contains("Secret123"), e.getMessage()); + } + enum TestMethod implements ApiMethod { SAYHI(String.class, "sayHi"), diff --git a/core/camel-support/src/main/java/org/apache/camel/support/component/ApiMethodHelper.java b/core/camel-support/src/main/java/org/apache/camel/support/component/ApiMethodHelper.java index 48578e162d8e..fef772a9e0b0 100644 --- a/core/camel-support/src/main/java/org/apache/camel/support/component/ApiMethodHelper.java +++ b/core/camel-support/src/main/java/org/apache/camel/support/component/ApiMethodHelper.java @@ -26,11 +26,14 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.TreeMap; +import java.util.TreeSet; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.camel.RuntimeCamelException; import org.apache.camel.util.ObjectHelper; +import org.apache.camel.util.SensitiveUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -475,7 +478,7 @@ public final class ApiMethodHelper<T extends Enum<T> & ApiMethod> { throws RuntimeCamelException { if (LOG.isDebugEnabled()) { - LOG.debug("Invoking {} with arguments {}", method.getName(), properties); + LOG.debug("Invoking {} with arguments {}", method.getName(), masked(properties)); } final List<String> argNames = method.getArgNames(); @@ -530,14 +533,39 @@ public final class ApiMethodHelper<T extends Enum<T> & ApiMethod> { String message = cause != null ? cause.getMessage() : e.getMessage(); throw new RuntimeCamelException( - String.format("Error invoking %s with %s: %s", method.getName(), properties, message), + String.format("Error invoking %s with %s: %s", method.getName(), names(properties), message), cause != null ? cause : e); } catch (Exception e) { throw new RuntimeCamelException( - String.format("Error invoking %s with %s: %s", method.getName(), properties, e.getMessage()), e); + String.format("Error invoking %s with %s: %s", method.getName(), names(properties), e.getMessage()), e); } } + /** + * The names of the arguments, for an error message: their values hold the endpoint options, such as a password, and + * the message data, which an error message must not show. + */ + private static Set<String> names(Map<String, Object> properties) { + return new TreeSet<>(properties.keySet()); + } + + /** + * The arguments with the values of the sensitive options masked, such as a password or a token, for the debug log. + */ + private static Map<String, Object> masked(Map<String, Object> properties) { + Map<String, Object> answer = new TreeMap<>(); + for (Map.Entry<String, Object> entry : properties.entrySet()) { + Object value = entry.getValue(); + if (SensitiveUtils.containsSensitive(entry.getKey())) { + value = "xxxxxx"; + } else if (value instanceof String text) { + value = SensitiveUtils.maskUserInfoCredentials(text, "xxxxxx"); + } + answer.put(entry.getKey(), value); + } + return answer; + } + public enum MatchType { EXACT, SUBSET, diff --git a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_18.adoc b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_18.adoc index c73149809cd3..e345d9cdea54 100644 --- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_18.adoc +++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_18.adoc @@ -443,6 +443,13 @@ hexadecimal characters, and the path of the file from the new `CamelGitHubFilePa the content of the file at that commit. Previously the body was taken as the commit when the header was not set, and the operation could not give the content of a file. +=== camel-support - the error of a failed API component call names its arguments only + +When the call of an API component fails, such as one of fhir, box, google-drive or twilio, the error message now names +the arguments of the call, as in `Error invoking resourceById with [password, resourceClass, serverUrl, stringId, +username]: ...`, and no longer shows their values. The arguments hold the options of the endpoint, such as a password or +a token, and the message data. The debug log of the call masks the values of the sensitive options. + == Upgrading from 4.18.4 to 4.18.5 === camel-azure-eventhubs diff --git a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_22.adoc b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_22.adoc index ac26b12e3d84..e39078eb48c4 100644 --- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_22.adoc +++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_22.adoc @@ -243,6 +243,13 @@ hexadecimal characters, and the path of the file from the new `CamelGitHubFilePa the content of the file at that commit. Previously the body was taken as the commit when the header was not set, and the operation could not give the content of a file. +=== camel-support - the error of a failed API component call names its arguments only + +When the call of an API component fails, such as one of fhir, box, google-drive or twilio, the error message now names +the arguments of the call, as in `Error invoking resourceById with [password, resourceClass, serverUrl, stringId, +username]: ...`, and no longer shows their values. The arguments hold the options of the endpoint, such as a password or +a token, and the message data. The debug log of the call masks the values of the sensitive options. + == Upgrading from 4.22.1 to 4.22.2 === camel-lra - only an LRA hosted by the configured coordinator is joined diff --git a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc index 00a397857c62..c4a87ac1fcb7 100644 --- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc +++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc @@ -3433,3 +3433,10 @@ script, now give a `byte[]` instead of a `List` of numbers. A script that return A JavaScript `Set` returned by a `quickjs` script now gives a `List`, a JavaScript `Map` a `Map` with its keys as text, and a typed array or an `ArrayBuffer` a `List` of numbers, also when nested. Before, a `Set` and a `Map` gave an empty `Map`, and a typed array a `Map` of index to value. + +=== camel-support - the error of a failed API component call names its arguments only + +When the call of an API component fails, such as one of fhir, box, google-drive or twilio, the error message now names +the arguments of the call, as in `Error invoking resourceById with [password, resourceClass, serverUrl, stringId, +username]: ...`, and no longer shows their values. The arguments hold the options of the endpoint, such as a password or +a token, and the message data. The debug log of the call masks the values of the sensitive options.
