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
The following commit(s) were added to refs/heads/main by this push:
new a9487d588336 docs: camel-keycloak - the audit trail example gives the
JDBC component an SQL insert, not the JSON record (#28336)
a9487d588336 is described below
commit a9487d5883367582b738d0066250ed822427ba7a
Author: Claus Ibsen <[email protected]>
AuthorDate: Sun Oct 25 16:16:22 2026 +0100
docs: camel-keycloak - the audit trail example gives the JDBC component an
SQL insert, not the JSON record (#28336)
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 doc says the event time is in epoch
milliseconds.
- 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 | 55 +++++++++++++++-------
.../src/main/docs/keycloak-consumer.adoc | 55 +++++++++++++++-------
2 files changed, 78 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..3f9c0a668ab9 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,12 @@ 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. The `timestamp` is the time of the event as
Keycloak gives it, in milliseconds since the
+epoch, the same in the JSON record and in the table, so the `event_time`
column is a `BIGINT`.
+
[tabs]
====
Java::
@@ -466,23 +472,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 +509,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..3f9c0a668ab9 100644
--- a/components/camel-keycloak/src/main/docs/keycloak-consumer.adoc
+++ b/components/camel-keycloak/src/main/docs/keycloak-consumer.adoc
@@ -452,6 +452,12 @@ 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. The `timestamp` is the time of the event as
Keycloak gives it, in milliseconds since the
+epoch, the same in the JSON record and in the table, so the `event_time`
column is a `BIGINT`.
+
[tabs]
====
Java::
@@ -466,23 +472,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 +509,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: