This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch docs/keycloak-audit-trail-example
in repository https://gitbox.apache.org/repos/asf/camel.git

commit be059a7090cda239551cc756b47c97ff8d4eb485
Author: Claus Ibsen <[email protected]>
AuthorDate: Fri Oct 23 10:54:54 2026 +0200

    docs: camel-keycloak - the audit trail example gives the JDBC component an 
SQL insert, not the JSON record
    
    The audit trail example of the consumer page sent the JSON audit record to
    jdbc:dataSource as the message body, which the JDBC component runs as SQL.
    The example now sets an SQL insert as the body and passes the record in the
    CamelJdbcParameters header as the values of its named parameters.
    
    - Every named parameter gets a value, so an admin event without auth details
      inserts nulls instead of failing on a missing key.
    - The YAML tab builds the record with the Simple map function, instead of a
      JSON text that marshal then encoded a second time, and reads the auth
      details with the null-safe form ${body?.authDetails?.userId}.
    - The timestamp is the epoch millis of the event, as in the JSON, so it
      binds on any JDBC driver.
    - The catalog copy of the page is updated.
    
    Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
    Signed-off-by: Claus Ibsen <[email protected]>
---
 .../camel/catalog/docs/keycloak-consumer.adoc      | 54 +++++++++++++++-------
 .../src/main/docs/keycloak-consumer.adoc           | 54 +++++++++++++++-------
 2 files changed, 76 insertions(+), 32 deletions(-)

diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/keycloak-consumer.adoc
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/keycloak-consumer.adoc
index 7764059ee2e0..afa07f060f6f 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/keycloak-consumer.adoc
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/keycloak-consumer.adoc
@@ -452,6 +452,11 @@ YAML::
 
 === Audit Trail
 
+The route below sends each admin event as a JSON audit record to Kafka, and 
inserts the same record into an
+`audit_trail` table with the xref:ROOT:jdbc-component.adoc[JDBC] component. 
The JDBC component runs the SQL in the
+message body, and with `useHeadersAsParameters=true` it takes the values of 
the named parameters (`:?name`) from the
+`CamelJdbcParameters` header.
+
 [tabs]
 ====
 Java::
@@ -466,23 +471,26 @@ from("keycloak:adminEvents"
      + "&delay=10000")
     .process(exchange -> {
         AdminEventRepresentation event = 
exchange.getIn().getBody(AdminEventRepresentation.class);
+        AuthDetailsRepresentation authDetails = event.getAuthDetails();
 
-        // Build audit record
+        // Build audit record (every named parameter of the SQL insert needs a 
value, null included)
         Map<String, Object> auditRecord = new HashMap<>();
-        auditRecord.put("timestamp", new Date(event.getTime()));
+        auditRecord.put("timestamp", event.getTime());
         auditRecord.put("operation", event.getOperationType());
         auditRecord.put("resourceType", event.getResourceType());
         auditRecord.put("resourcePath", event.getResourcePath());
-
-        if (event.getAuthDetails() != null) {
-            auditRecord.put("userId", event.getAuthDetails().getUserId());
-            auditRecord.put("ipAddress", 
event.getAuthDetails().getIpAddress());
-        }
+        auditRecord.put("userId", authDetails != null ? 
authDetails.getUserId() : null);
+        auditRecord.put("ipAddress", authDetails != null ? 
authDetails.getIpAddress() : null);
 
         exchange.getIn().setBody(auditRecord);
+        // the values of the named parameters of the SQL insert
+        exchange.getIn().setHeader("CamelJdbcParameters", auditRecord);
     })
     .marshal().json()
     .to("kafka:audit-trail?brokers=localhost:9092")
+    // the JDBC component runs the SQL in the message body
+    .setBody(constant("insert into audit_trail (event_time, operation, 
resource_type, resource_path, user_id, ip_address)"
+                      + " values (:?timestamp, :?operation, :?resourceType, 
:?resourcePath, :?userId, :?ipAddress)"))
     .to("jdbc:dataSource?useHeadersAsParameters=true");
 ----
 
@@ -500,22 +508,36 @@ YAML::
         maxResults=100&
         delay=10000
       steps:
+        # build the audit record (every named parameter of the SQL insert 
needs a value, null included)
         - setBody:
-            simple: >
-              {
-                "timestamp": ${body.time},
-                "operation": "${body.operationType}",
-                "resourceType": "${body.resourceType}",
-                "resourcePath": "${body.resourcePath}",
-                "userId": "${body.authDetails.userId}",
-                "ipAddress": "${body.authDetails.ipAddress}"
-              }
+            expression:
+              simple:
+                expression: >-
+                  ${map(timestamp,${body.time},
+                  operation,${body.operationType},
+                  resourceType,${body.resourceType},
+                  resourcePath,${body.resourcePath},
+                  userId,${body?.authDetails?.userId},
+                  ipAddress,${body?.authDetails?.ipAddress})}
+        # the values of the named parameters of the SQL insert
+        - setHeader:
+            name: CamelJdbcParameters
+            expression:
+              simple:
+                expression: "${body}"
         - marshal:
             json: {}
         - to:
             uri: kafka:audit-trail
             parameters:
               brokers: "localhost:9092"
+        # the JDBC component runs the SQL in the message body
+        - setBody:
+            expression:
+              constant:
+                expression: >-
+                  insert into audit_trail (event_time, operation, 
resource_type, resource_path, user_id, ip_address)
+                  values (:?timestamp, :?operation, :?resourceType, 
:?resourcePath, :?userId, :?ipAddress)
         - to:
             uri: jdbc:dataSource
             parameters:
diff --git a/components/camel-keycloak/src/main/docs/keycloak-consumer.adoc 
b/components/camel-keycloak/src/main/docs/keycloak-consumer.adoc
index 7764059ee2e0..afa07f060f6f 100644
--- a/components/camel-keycloak/src/main/docs/keycloak-consumer.adoc
+++ b/components/camel-keycloak/src/main/docs/keycloak-consumer.adoc
@@ -452,6 +452,11 @@ YAML::
 
 === Audit Trail
 
+The route below sends each admin event as a JSON audit record to Kafka, and 
inserts the same record into an
+`audit_trail` table with the xref:ROOT:jdbc-component.adoc[JDBC] component. 
The JDBC component runs the SQL in the
+message body, and with `useHeadersAsParameters=true` it takes the values of 
the named parameters (`:?name`) from the
+`CamelJdbcParameters` header.
+
 [tabs]
 ====
 Java::
@@ -466,23 +471,26 @@ from("keycloak:adminEvents"
      + "&delay=10000")
     .process(exchange -> {
         AdminEventRepresentation event = 
exchange.getIn().getBody(AdminEventRepresentation.class);
+        AuthDetailsRepresentation authDetails = event.getAuthDetails();
 
-        // Build audit record
+        // Build audit record (every named parameter of the SQL insert needs a 
value, null included)
         Map<String, Object> auditRecord = new HashMap<>();
-        auditRecord.put("timestamp", new Date(event.getTime()));
+        auditRecord.put("timestamp", event.getTime());
         auditRecord.put("operation", event.getOperationType());
         auditRecord.put("resourceType", event.getResourceType());
         auditRecord.put("resourcePath", event.getResourcePath());
-
-        if (event.getAuthDetails() != null) {
-            auditRecord.put("userId", event.getAuthDetails().getUserId());
-            auditRecord.put("ipAddress", 
event.getAuthDetails().getIpAddress());
-        }
+        auditRecord.put("userId", authDetails != null ? 
authDetails.getUserId() : null);
+        auditRecord.put("ipAddress", authDetails != null ? 
authDetails.getIpAddress() : null);
 
         exchange.getIn().setBody(auditRecord);
+        // the values of the named parameters of the SQL insert
+        exchange.getIn().setHeader("CamelJdbcParameters", auditRecord);
     })
     .marshal().json()
     .to("kafka:audit-trail?brokers=localhost:9092")
+    // the JDBC component runs the SQL in the message body
+    .setBody(constant("insert into audit_trail (event_time, operation, 
resource_type, resource_path, user_id, ip_address)"
+                      + " values (:?timestamp, :?operation, :?resourceType, 
:?resourcePath, :?userId, :?ipAddress)"))
     .to("jdbc:dataSource?useHeadersAsParameters=true");
 ----
 
@@ -500,22 +508,36 @@ YAML::
         maxResults=100&
         delay=10000
       steps:
+        # build the audit record (every named parameter of the SQL insert 
needs a value, null included)
         - setBody:
-            simple: >
-              {
-                "timestamp": ${body.time},
-                "operation": "${body.operationType}",
-                "resourceType": "${body.resourceType}",
-                "resourcePath": "${body.resourcePath}",
-                "userId": "${body.authDetails.userId}",
-                "ipAddress": "${body.authDetails.ipAddress}"
-              }
+            expression:
+              simple:
+                expression: >-
+                  ${map(timestamp,${body.time},
+                  operation,${body.operationType},
+                  resourceType,${body.resourceType},
+                  resourcePath,${body.resourcePath},
+                  userId,${body?.authDetails?.userId},
+                  ipAddress,${body?.authDetails?.ipAddress})}
+        # the values of the named parameters of the SQL insert
+        - setHeader:
+            name: CamelJdbcParameters
+            expression:
+              simple:
+                expression: "${body}"
         - marshal:
             json: {}
         - to:
             uri: kafka:audit-trail
             parameters:
               brokers: "localhost:9092"
+        # the JDBC component runs the SQL in the message body
+        - setBody:
+            expression:
+              constant:
+                expression: >-
+                  insert into audit_trail (event_time, operation, 
resource_type, resource_path, user_id, ip_address)
+                  values (:?timestamp, :?operation, :?resourceType, 
:?resourcePath, :?userId, :?ipAddress)
         - to:
             uri: jdbc:dataSource
             parameters:

Reply via email to