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 69345633bc00 CAMEL-24710: docs - YAML examples pass the catalog
checks, guarded by CatalogDocExamplesTest
69345633bc00 is described below
commit 69345633bc00e61010af7251c1af6269dc1f0765
Author: Claus Ibsen <[email protected]>
AuthorDate: Wed Sep 16 11:02:33 2026 +0200
CAMEL-24710: docs - YAML examples pass the catalog checks, guarded by
CatalogDocExamplesTest
The four page families already validated against the YAML schema in the
build; this covers the second half of the ticket: the catalog checks
(endpoint options, Simple, headers) that camel validate yaml and the
authoring tools run after the schema. Re-measured on main: 1,820 YAML
route examples, 87 failing the catalog checks on 28 pages; after this
change 0, outside the pages skipped for the CAMEL-24748 metadata
mismatches.
CatalogDocExamplesTest in camel-jbang-core runs every YAML route example
of every catalog page through SourceValidator.validateCamelYaml, with a
skip table that cannot go stale. The checkers no longer misread trailing
comments, block scalars, nested Map options, dotted or prefixed header
names, or name: under setProperty; validateEndpointProperties accepts the
map-entry property-binding syntax. Missing header metadata added for
camel-crypto, camel-solr and camel-box; four doc pages corrected
(xmlSecurity, metrics, fop, openai). Five further metadata-vs-runtime
mismatches reported to CAMEL-24748.
Closes #26494
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Signed-off-by: Claus Ibsen <[email protected]>
---
.../org/apache/camel/catalog/components/box.json | 3 +
.../apache/camel/catalog/components/crypto.json | 5 +-
.../apache/camel/catalog/components/openai.json | 2 +-
.../org/apache/camel/catalog/components/solr.json | 4 +-
.../apache/camel/catalog/docs/fop-component.adoc | 8 +-
.../camel/catalog/docs/metrics-component.adoc | 8 +-
.../camel/catalog/docs/openai-component.adoc | 16 +--
.../camel/catalog/docs/xmlSecurity-dataformat.adoc | 20 ++--
.../org/apache/camel/catalog/CamelCatalogTest.java | 13 +++
.../org/apache/camel/component/openai/openai.json | 2 +-
.../src/main/docs/openai-component.adoc | 16 +--
.../component/openai/OpenAIConfiguration.java | 2 +-
.../org/apache/camel/component/box/box.json | 3 +
.../apache/camel/component/box/BoxEndpoint.java | 3 +-
.../camel/component/box/internal/BoxConstants.java | 7 +-
.../org/apache/camel/component/crypto/crypto.json | 5 +-
.../crypto/DigitalSignatureConstants.java | 3 +
.../camel-fop/src/main/docs/fop-component.adoc | 8 +-
.../src/main/docs/metrics-component.adoc | 8 +-
.../org/apache/camel/component/solr/solr.json | 4 +-
.../apache/camel/component/solr/SolrConstants.java | 6 +
.../src/main/docs/xmlSecurity-dataformat.adoc | 20 ++--
.../camel/catalog/impl/AbstractCamelCatalog.java | 20 +++-
.../builder/endpoint/EndpointHeaderBuilders.java | 14 +++
.../endpoint/dsl/BoxEndpointBuilderFactory.java | 38 ++++++
.../DigitalSignatureEndpointBuilderFactory.java | 14 +++
.../endpoint/dsl/OpenAIEndpointBuilderFactory.java | 8 +-
.../endpoint/dsl/SolrEndpointBuilderFactory.java | 26 +++++
.../dsl/jbang/core/commands/ai/EndpointChecks.java | 30 ++++-
.../dsl/jbang/core/commands/ai/HeaderChecks.java | 54 ++++++++-
.../dsl/jbang/core/commands/ai/YamlLines.java | 27 +++++
.../core/commands/ai/CatalogDocExamplesTest.java | 128 +++++++++++++++++++++
.../commands/ai/SourceValidatorEndpointTest.java | 58 ++++++++++
.../dsl/yaml/validator/EipDocExamplesTest.java | 11 +-
34 files changed, 511 insertions(+), 83 deletions(-)
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/box.json
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/box.json
index 7327874b58e2..f17f0102eb31 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/box.json
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/box.json
@@ -45,6 +45,9 @@
"userName": { "index": 17, "kind": "property", "displayName": "User Name",
"group": "security", "label": "security", "required": false, "type": "string",
"javaType": "java.lang.String", "deprecated": false, "autowired": false,
"secret": true, "security": "secret", "configurationClass":
"org.apache.camel.component.box.BoxConfiguration", "configurationField":
"configuration", "description": "Box user name, MUST be provided" },
"userPassword": { "index": 18, "kind": "property", "displayName": "User
Password", "group": "security", "label": "security", "required": false, "type":
"string", "javaType": "java.lang.String", "deprecated": false, "autowired":
false, "secret": true, "security": "secret", "configurationClass":
"org.apache.camel.component.box.BoxConfiguration", "configurationField":
"configuration", "description": "Box user password, MUST be provided if
authSecureStorage is not set, or returns null on [...]
},
+ "headers": {
+ "CamelBox.": { "index": 0, "kind": "header", "displayName": "", "group":
"common", "label": "", "required": false, "javaType": "Object", "deprecated":
false, "deprecationNote": "", "autowired": false, "secret": false,
"description": "The prefix of the headers that carry the parameters of the API
method: the header CamelBox.fileName holds the fileName parameter.",
"constantName":
"org.apache.camel.component.box.internal.BoxConstants#PROPERTY_PREFIX" }
+ },
"properties": {
"apiName": { "index": 0, "kind": "path", "displayName": "Api Name",
"group": "common", "label": "", "required": true, "type": "enum", "javaType":
"org.apache.camel.component.box.internal.BoxApiName", "enum": [
"COLLABORATIONS", "COMMENTS", "EVENT_LOGS", "FILES", "FOLDERS", "GROUPS",
"EVENTS", "SEARCH", "TASKS", "USERS" ], "deprecated": false, "deprecationNote":
"", "autowired": false, "secret": false, "configurationClass":
"org.apache.camel.component.box.BoxConfiguration", "configura [...]
"methodName": { "index": 1, "kind": "path", "displayName": "Method Name",
"group": "common", "label": "", "required": true, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "configurationClass":
"org.apache.camel.component.box.BoxConfiguration", "configurationField":
"configuration", "description": "What sub operation to use for the selected
operation" },
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/crypto.json
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/crypto.json
index 17f736dc943c..9f14e382ba45 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/crypto.json
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/crypto.json
@@ -49,8 +49,9 @@
"headers": {
"CamelSignaturePrivateKey": { "index": 0, "kind": "header", "displayName":
"", "group": "producer", "label": "", "required": false, "javaType":
"java.security.PrivateKey", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "description": "The PrivateKey that should
be used to sign the message", "constantName":
"org.apache.camel.component.crypto.DigitalSignatureConstants#SIGNATURE_PRIVATE_KEY"
},
"CamelSignaturePublicKeyOrCert": { "index": 1, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType": "Certificate or PublicKey", "deprecated": false, "deprecationNote":
"", "autowired": false, "secret": false, "description": "The Certificate or
PublicKey that should be used to verify the signature", "constantName":
"org.apache.camel.component.crypto.DigitalSignatureConstants#SIGNATURE_PUBLIC_KEY_OR_CERT"
},
- "CamelSignatureKeyStoreAlias": { "index": 2, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType": "String", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "The alias used to query the KeyStore
for keys and Certificates to be used in signing and verifying exchanges",
"constantName":
"org.apache.camel.component.crypto.DigitalSignatureConstants#KEYSTORE_ALIAS" },
- "CamelSignatureKeyStorePassword": { "index": 3, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType": "char[]", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "The password used to access an aliased
PrivateKey in the KeyStore.", "constantName":
"org.apache.camel.component.crypto.DigitalSignatureConstants#KEYSTORE_PASSWORD"
}
+ "CamelDigitalSignature": { "index": 2, "kind": "header", "displayName":
"", "group": "producer", "label": "", "required": false, "javaType": "byte[]
(Base64 encoded)", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "The signature of the message, set by
the sign operation and read by the verify operation (the signatureHeaderName
option changes the header name)", "constantName":
"org.apache.camel.component.crypto.DigitalSignatureConstant [...]
+ "CamelSignatureKeyStoreAlias": { "index": 3, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType": "String", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "The alias used to query the KeyStore
for keys and Certificates to be used in signing and verifying exchanges",
"constantName":
"org.apache.camel.component.crypto.DigitalSignatureConstants#KEYSTORE_ALIAS" },
+ "CamelSignatureKeyStorePassword": { "index": 4, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType": "char[]", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "The password used to access an aliased
PrivateKey in the KeyStore.", "constantName":
"org.apache.camel.component.crypto.DigitalSignatureConstants#KEYSTORE_PASSWORD"
}
},
"properties": {
"cryptoOperation": { "index": 0, "kind": "path", "displayName": "Crypto
Operation", "group": "producer", "label": "", "required": true, "type": "enum",
"javaType": "org.apache.camel.component.crypto.CryptoOperation", "enum": [
"sign", "verify" ], "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "configurationClass":
"org.apache.camel.component.crypto.DigitalSignatureConfiguration",
"configurationField": "configuration", "description": "Set the Crypto o [...]
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/openai.json
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/openai.json
index bcf5441c2d8c..1d7b30fbebc3 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/openai.json
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/openai.json
@@ -123,7 +123,7 @@
"operation": { "index": 0, "kind": "path", "displayName": "Operation",
"group": "producer", "label": "", "required": true, "type": "enum", "javaType":
"org.apache.camel.component.openai.OpenAIOperations", "enum": [
"chat-completion", "responses", "responses-retrieve", "responses-cancel",
"embeddings", "tool-execution", "audio-transcription", "audio-translation",
"audio-speech", "moderation", "image-generation", "image-edit" ], "deprecated":
false, "deprecationNote": "", "autowired": [...]
"additionalBodyProperty": { "index": 1, "kind": "parameter",
"displayName": "Additional Body Property", "group": "producer", "label": "",
"required": false, "type": "object", "javaType":
"java.util.Map<java.lang.String, java.lang.Object>", "prefix":
"additionalBodyProperty.", "multiValue": true, "deprecated": false,
"deprecationNote": "", "autowired": false, "secret": false,
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration",
"configurationField": "configur [...]
"additionalHeader": { "index": 2, "kind": "parameter", "displayName":
"Additional Header", "group": "producer", "label": "", "required": false,
"type": "object", "javaType": "java.util.Map<java.lang.String,
java.lang.Object>", "prefix": "additionalHeader.", "multiValue": true,
"deprecated": false, "deprecationNote": "", "autowired": false, "secret": true,
"security": "secret", "configurationClass":
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField":
"config [...]
- "additionalResponseHeader": { "index": 3, "kind": "parameter",
"displayName": "Additional Response Header", "group": "producer", "label": "",
"required": false, "type": "object", "javaType":
"java.util.Map<java.lang.String, java.lang.Object>", "prefix":
"additionalResponseHeader.", "multiValue": true, "deprecated": false,
"deprecationNote": "", "autowired": false, "secret": false,
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration",
"configurationField": "co [...]
+ "additionalResponseHeader": { "index": 3, "kind": "parameter",
"displayName": "Additional Response Header", "group": "producer", "label": "",
"required": false, "type": "object", "javaType":
"java.util.Map<java.lang.String, java.lang.Object>", "prefix":
"additionalResponseHeader.", "multiValue": true, "deprecated": false,
"deprecationNote": "", "autowired": false, "secret": false,
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration",
"configurationField": "co [...]
"apiKey": { "index": 4, "kind": "parameter", "displayName": "Api Key",
"group": "producer", "label": "", "required": false, "type": "string",
"javaType": "java.lang.String", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": true, "security": "secret", "configurationClass":
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField":
"configuration", "description": "OpenAI API key. Can also be set via
OPENAI_API_KEY environment variable." },
"audioLanguage": { "index": 5, "kind": "parameter", "displayName": "Audio
Language", "group": "producer", "label": "", "required": false, "type":
"string", "javaType": "java.lang.String", "deprecated": false,
"deprecationNote": "", "autowired": false, "secret": false,
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration",
"configurationField": "configuration", "description": "The language of the
input audio in ISO-639-1 format (e.g., 'en'). Improves accuracy a [...]
"audioModel": { "index": 6, "kind": "parameter", "displayName": "Audio
Model", "group": "producer", "label": "", "required": false, "type": "string",
"javaType": "java.lang.String", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "configurationClass":
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField":
"configuration", "description": "The model to use for audio transcription
(e.g., whisper-1, gpt-4o-transcribe)" },
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/solr.json
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/solr.json
index 97c66fc517d0..299fa0fe908c 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/solr.json
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/solr.json
@@ -47,7 +47,9 @@
"CamelSolrFrom": { "index": 5, "kind": "header", "displayName": "",
"group": "producer", "label": "", "required": false, "javaType": "Integer",
"deprecated": false, "deprecationNote": "", "autowired": false, "secret":
false, "description": "The starting index of the response.", "constantName":
"org.apache.camel.component.solr.SolrConstants#PARAM_FROM" },
"CamelSolrParams": { "index": 6, "kind": "header", "displayName": "",
"group": "producer", "label": "", "required": false, "javaType":
"org.apache.solr.common.params.SolrParams", "deprecated": false,
"deprecationNote": "", "autowired": false, "secret": false, "description": "The
solr parameters to use for the request.", "constantName":
"org.apache.camel.component.solr.SolrConstants#PARAM_SOLR_PARAMS" },
"CamelSolrDeleteByQuery": { "index": 7, "kind": "header", "displayName":
"", "group": "producer", "label": "", "required": false, "javaType": "boolean",
"deprecated": false, "deprecationNote": "", "autowired": false, "secret":
false, "defaultValue": "false", "description": "For the delete instruction,
interpret body as query\/queries instead of id\/ids.", "constantName":
"org.apache.camel.component.solr.SolrConstants#PARAM_DELETE_BY_QUERY" },
- "Content-Type": { "index": 8, "kind": "header", "displayName": "",
"group": "producer", "label": "", "required": false, "javaType": "String",
"deprecated": false, "deprecationNote": "", "autowired": false, "secret":
false, "description": "The content type is used to identify the type when
inserting files.", "constantName":
"org.apache.camel.component.solr.SolrConstants#PARAM_CONTENT_TYPE" }
+ "Content-Type": { "index": 8, "kind": "header", "displayName": "",
"group": "producer", "label": "", "required": false, "javaType": "String",
"deprecated": false, "deprecationNote": "", "autowired": false, "secret":
false, "description": "The content type is used to identify the type when
inserting files.", "constantName":
"org.apache.camel.component.solr.SolrConstants#PARAM_CONTENT_TYPE" },
+ "CamelSolrField.": { "index": 9, "kind": "header", "displayName": "",
"group": "producer", "label": "", "required": false, "javaType": "Object",
"deprecated": false, "deprecationNote": "", "autowired": false, "secret":
false, "description": "The prefix of the headers that carry the fields of the
document to insert: the header CamelSolrField.id holds the id field.",
"constantName":
"org.apache.camel.component.solr.SolrConstants#HEADER_FIELD_PREFIX" },
+ "CamelSolrParam.": { "index": 10, "kind": "header", "displayName": "",
"group": "producer", "label": "", "required": false, "javaType": "String",
"deprecated": false, "deprecationNote": "", "autowired": false, "secret":
false, "description": "The prefix of the headers that carry extra request
parameters: the header CamelSolrParam.commit holds the commit parameter.",
"constantName":
"org.apache.camel.component.solr.SolrConstants#HEADER_PARAM_PREFIX" }
},
"properties": {
"host": { "index": 0, "kind": "path", "displayName": "Host", "group":
"producer", "label": "", "required": true, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "configurationClass":
"org.apache.camel.component.solr.SolrConfiguration", "configurationField":
"configuration", "description": "The solr instance host name (set to 'default'
to use the host name defined on component level)" },
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/fop-component.adoc
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/fop-component.adoc
index 4b8140c6875c..d71438459ecb 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/fop-component.adoc
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/fop-component.adoc
@@ -72,6 +72,8 @@ supported:
The complete list of valid output formats can be found in the
`MimeConstants.java` source file.
+The endpoint URI takes either the name in lower case (`fop:pdf`) or the output
format (`fop:application/pdf`).
+
// component options: START
include::partial$component-configure-options.adoc[]
@@ -137,7 +139,7 @@ Java::
----
from("file:source/data/xml")
.to("xslt:xslt/template.xsl")
- .to("fop:application/pdf")
+ .to("fop:pdf")
.to("file:target/data");
----
@@ -148,7 +150,7 @@ XML::
<route>
<from uri="file:source/data/xml"/>
<to uri="xslt:xslt/template.xsl"/>
- <to uri="fop:application/pdf"/>
+ <to uri="fop:pdf"/>
<to uri="file:target/data"/>
</route>
----
@@ -164,7 +166,7 @@ YAML::
- to:
uri: xslt:xslt/template.xsl
- to:
- uri: fop:application/pdf
+ uri: fop:pdf
- to:
uri: file:target/data
----
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/metrics-component.adoc
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/metrics-component.adoc
index a70cb8c85cbc..e84bfe571880 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/metrics-component.adoc
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/metrics-component.adoc
@@ -543,9 +543,9 @@ Java::
+
[source,java]
----
-// marks simple.meter without value
+// marks the simple.meter meter without value
from("direct:in")
- .to("metrics:simple.meter")
+ .to("metrics:meter:simple.meter")
.to("direct:out");
----
@@ -555,7 +555,7 @@ XML::
----
<route>
<from uri="direct:in"/>
- <to uri="metrics:simple.meter"/>
+ <to uri="metrics:meter:simple.meter"/>
<to uri="direct:out"/>
</route>
----
@@ -569,7 +569,7 @@ YAML::
uri: direct:in
steps:
- to:
- uri: metrics:simple.meter
+ uri: metrics:meter:simple.meter
- to:
uri: direct:out
----
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/openai-component.adoc
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/openai-component.adoc
index 79bd9c3949fa..039ce1f67b1b 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/openai-component.adoc
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/openai-component.adoc
@@ -1046,9 +1046,9 @@ Java::
----
from("direct:chat")
.to("openai:chat-completion?model=qwen3"
- + "&additionalResponseHeader.reasoning_content=CamelMyReasoning"
- + "&additionalResponseHeader.custom_field=CamelMyCustomField")
- .log("Custom reasoning: ${header.CamelMyReasoning}");
+ + "&additionalResponseHeader.reasoning_content=MyReasoning"
+ + "&additionalResponseHeader.custom_field=MyCustomField")
+ .log("Custom reasoning: ${header.MyReasoning}");
----
XML::
@@ -1057,8 +1057,8 @@ XML::
----
<route>
<from uri="direct:chat"/>
- <to
uri="openai:chat-completion?model=qwen3&additionalResponseHeader.reasoning_content=CamelMyReasoning&additionalResponseHeader.custom_field=CamelMyCustomField"/>
- <log message="Custom reasoning: ${header.CamelMyReasoning}"/>
+ <to
uri="openai:chat-completion?model=qwen3&additionalResponseHeader.reasoning_content=MyReasoning&additionalResponseHeader.custom_field=MyCustomField"/>
+ <log message="Custom reasoning: ${header.MyReasoning}"/>
</route>
----
@@ -1074,10 +1074,10 @@ YAML::
uri: openai:chat-completion
parameters:
model: qwen3
- additionalResponseHeader.reasoning_content: CamelMyReasoning
- additionalResponseHeader.custom_field: CamelMyCustomField
+ additionalResponseHeader.reasoning_content: MyReasoning
+ additionalResponseHeader.custom_field: MyCustomField
- log:
- message: "Custom reasoning: ${header.CamelMyReasoning}"
+ message: "Custom reasoning: ${header.MyReasoning}"
----
====
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/xmlSecurity-dataformat.adoc
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/xmlSecurity-dataformat.adoc
index eaf5a86d81c7..ced411c85754 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/xmlSecurity-dataformat.adoc
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/xmlSecurity-dataformat.adoc
@@ -208,7 +208,7 @@ Java::
----
from("direct:start")
.marshal().xmlSecurity("//cheese:cheesesites/italy", namespaces, true,
- "recipient", XMLCipher.AES_128_CBC, XMLCipher.RSA_v1dot5,
trustStoreParams)
+ "recipient", XMLCipher.AES_128, XMLCipher.RSA_v1dot5,
trustStoreParams)
.to("...");
----
@@ -227,8 +227,8 @@ XML::
<marshal>
<xmlSecurity secureTag="//cheese:cheesesites/italy"
secureTagContents="true"
-
xmlCipherAlgorithm="http://www.w3.org/2001/04/xmlenc#aes128-cbc"
-
keyCipherAlgorithm="http://www.w3.org/2001/04/xmlenc#rsa-1_5"
+ xmlCipherAlgorithm="AES_128"
+ keyCipherAlgorithm="RSA_v1dot5"
recipientKeyAlias="recipient"
keyOrTrustStoreParameters="trustStoreParams"/>
</marshal>
@@ -247,8 +247,8 @@ YAML::
xmlSecurity:
secureTag: "//cheese:cheesesites/italy"
secureTagContents: true
- xmlCipherAlgorithm: "http://www.w3.org/2001/04/xmlenc#aes128-cbc"
- keyCipherAlgorithm: "http://www.w3.org/2001/04/xmlenc#rsa-1_5"
+ xmlCipherAlgorithm: AES_128
+ keyCipherAlgorithm: RSA_v1dot5
recipientKeyAlias: recipient
keyOrTrustStoreParameters: "#trustStoreParams"
----
@@ -265,7 +265,7 @@ Java::
----
from("direct:encrypted")
.unmarshal().xmlSecurity("//cheese:cheesesites/italy", namespaces, true,
- "recipient", XMLCipher.AES_128_CBC, XMLCipher.RSA_v1dot5,
keyStoreParams)
+ "recipient", XMLCipher.AES_128, XMLCipher.RSA_v1dot5,
keyStoreParams)
.to("...");
----
@@ -284,8 +284,8 @@ XML::
<unmarshal>
<xmlSecurity secureTag="//cheese:cheesesites/italy"
secureTagContents="true"
-
xmlCipherAlgorithm="http://www.w3.org/2001/04/xmlenc#aes128-cbc"
-
keyCipherAlgorithm="http://www.w3.org/2001/04/xmlenc#rsa-1_5"
+ xmlCipherAlgorithm="AES_128"
+ keyCipherAlgorithm="RSA_v1dot5"
recipientKeyAlias="recipient"
keyOrTrustStoreParameters="keyStoreParams"
keyPassword="privateKeyPassword" />
@@ -305,8 +305,8 @@ YAML::
xmlSecurity:
secureTag: "//cheese:cheesesites/italy"
secureTagContents: true
- xmlCipherAlgorithm: "http://www.w3.org/2001/04/xmlenc#aes128-cbc"
- keyCipherAlgorithm: "http://www.w3.org/2001/04/xmlenc#rsa-1_5"
+ xmlCipherAlgorithm: AES_128
+ keyCipherAlgorithm: RSA_v1dot5
recipientKeyAlias: recipient
keyOrTrustStoreParameters: "#keyStoreParams"
keyPassword: privateKeyPassword
diff --git
a/catalog/camel-catalog/src/test/java/org/apache/camel/catalog/CamelCatalogTest.java
b/catalog/camel-catalog/src/test/java/org/apache/camel/catalog/CamelCatalogTest.java
index 4fd62a61fc57..e5a7079f4a03 100644
---
a/catalog/camel-catalog/src/test/java/org/apache/camel/catalog/CamelCatalogTest.java
+++
b/catalog/camel-catalog/src/test/java/org/apache/camel/catalog/CamelCatalogTest.java
@@ -593,6 +593,19 @@ public class CamelCatalogTest {
assertEquals("beer", map.get("myPrefix.baz"));
}
+ @Test
+ public void validateMapOptionEntries() {
+ // userMetadata is a Map option: userMetadata.key=value fills an entry
of the map, as property binding does
+ EndpointValidationResult result = catalog.validateEndpointProperties(
+
"spring-ai-chat:assistant?chatModel=#myModel&userMetadata.messageId=abc&userMetadata.priority=high");
+ assertTrue(result.isSuccess(), result.summaryErrorMessage(false));
+
+ // the option before the dot must be a Map
+ result =
catalog.validateEndpointProperties("spring-ai-chat:assistant?chatModel=#myModel&chatModel.foo=abc");
+ assertFalse(result.isSuccess());
+ assertTrue(result.getUnknown().contains("chatModel.foo"));
+ }
+
@Test
public void testEndpointPropertiesPlaceholders() throws Exception {
Map<String, String> map =
catalog.endpointProperties("timer:foo?period={{howoften}}&repeatCount=5");
diff --git
a/components/camel-ai/camel-openai/src/generated/resources/META-INF/org/apache/camel/component/openai/openai.json
b/components/camel-ai/camel-openai/src/generated/resources/META-INF/org/apache/camel/component/openai/openai.json
index bcf5441c2d8c..1d7b30fbebc3 100644
---
a/components/camel-ai/camel-openai/src/generated/resources/META-INF/org/apache/camel/component/openai/openai.json
+++
b/components/camel-ai/camel-openai/src/generated/resources/META-INF/org/apache/camel/component/openai/openai.json
@@ -123,7 +123,7 @@
"operation": { "index": 0, "kind": "path", "displayName": "Operation",
"group": "producer", "label": "", "required": true, "type": "enum", "javaType":
"org.apache.camel.component.openai.OpenAIOperations", "enum": [
"chat-completion", "responses", "responses-retrieve", "responses-cancel",
"embeddings", "tool-execution", "audio-transcription", "audio-translation",
"audio-speech", "moderation", "image-generation", "image-edit" ], "deprecated":
false, "deprecationNote": "", "autowired": [...]
"additionalBodyProperty": { "index": 1, "kind": "parameter",
"displayName": "Additional Body Property", "group": "producer", "label": "",
"required": false, "type": "object", "javaType":
"java.util.Map<java.lang.String, java.lang.Object>", "prefix":
"additionalBodyProperty.", "multiValue": true, "deprecated": false,
"deprecationNote": "", "autowired": false, "secret": false,
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration",
"configurationField": "configur [...]
"additionalHeader": { "index": 2, "kind": "parameter", "displayName":
"Additional Header", "group": "producer", "label": "", "required": false,
"type": "object", "javaType": "java.util.Map<java.lang.String,
java.lang.Object>", "prefix": "additionalHeader.", "multiValue": true,
"deprecated": false, "deprecationNote": "", "autowired": false, "secret": true,
"security": "secret", "configurationClass":
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField":
"config [...]
- "additionalResponseHeader": { "index": 3, "kind": "parameter",
"displayName": "Additional Response Header", "group": "producer", "label": "",
"required": false, "type": "object", "javaType":
"java.util.Map<java.lang.String, java.lang.Object>", "prefix":
"additionalResponseHeader.", "multiValue": true, "deprecated": false,
"deprecationNote": "", "autowired": false, "secret": false,
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration",
"configurationField": "co [...]
+ "additionalResponseHeader": { "index": 3, "kind": "parameter",
"displayName": "Additional Response Header", "group": "producer", "label": "",
"required": false, "type": "object", "javaType":
"java.util.Map<java.lang.String, java.lang.Object>", "prefix":
"additionalResponseHeader.", "multiValue": true, "deprecated": false,
"deprecationNote": "", "autowired": false, "secret": false,
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration",
"configurationField": "co [...]
"apiKey": { "index": 4, "kind": "parameter", "displayName": "Api Key",
"group": "producer", "label": "", "required": false, "type": "string",
"javaType": "java.lang.String", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": true, "security": "secret", "configurationClass":
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField":
"configuration", "description": "OpenAI API key. Can also be set via
OPENAI_API_KEY environment variable." },
"audioLanguage": { "index": 5, "kind": "parameter", "displayName": "Audio
Language", "group": "producer", "label": "", "required": false, "type":
"string", "javaType": "java.lang.String", "deprecated": false,
"deprecationNote": "", "autowired": false, "secret": false,
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration",
"configurationField": "configuration", "description": "The language of the
input audio in ISO-639-1 format (e.g., 'en'). Improves accuracy a [...]
"audioModel": { "index": 6, "kind": "parameter", "displayName": "Audio
Model", "group": "producer", "label": "", "required": false, "type": "string",
"javaType": "java.lang.String", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "configurationClass":
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField":
"configuration", "description": "The model to use for audio transcription
(e.g., whisper-1, gpt-4o-transcribe)" },
diff --git
a/components/camel-ai/camel-openai/src/main/docs/openai-component.adoc
b/components/camel-ai/camel-openai/src/main/docs/openai-component.adoc
index 79bd9c3949fa..039ce1f67b1b 100644
--- a/components/camel-ai/camel-openai/src/main/docs/openai-component.adoc
+++ b/components/camel-ai/camel-openai/src/main/docs/openai-component.adoc
@@ -1046,9 +1046,9 @@ Java::
----
from("direct:chat")
.to("openai:chat-completion?model=qwen3"
- + "&additionalResponseHeader.reasoning_content=CamelMyReasoning"
- + "&additionalResponseHeader.custom_field=CamelMyCustomField")
- .log("Custom reasoning: ${header.CamelMyReasoning}");
+ + "&additionalResponseHeader.reasoning_content=MyReasoning"
+ + "&additionalResponseHeader.custom_field=MyCustomField")
+ .log("Custom reasoning: ${header.MyReasoning}");
----
XML::
@@ -1057,8 +1057,8 @@ XML::
----
<route>
<from uri="direct:chat"/>
- <to
uri="openai:chat-completion?model=qwen3&additionalResponseHeader.reasoning_content=CamelMyReasoning&additionalResponseHeader.custom_field=CamelMyCustomField"/>
- <log message="Custom reasoning: ${header.CamelMyReasoning}"/>
+ <to
uri="openai:chat-completion?model=qwen3&additionalResponseHeader.reasoning_content=MyReasoning&additionalResponseHeader.custom_field=MyCustomField"/>
+ <log message="Custom reasoning: ${header.MyReasoning}"/>
</route>
----
@@ -1074,10 +1074,10 @@ YAML::
uri: openai:chat-completion
parameters:
model: qwen3
- additionalResponseHeader.reasoning_content: CamelMyReasoning
- additionalResponseHeader.custom_field: CamelMyCustomField
+ additionalResponseHeader.reasoning_content: MyReasoning
+ additionalResponseHeader.custom_field: MyCustomField
- log:
- message: "Custom reasoning: ${header.CamelMyReasoning}"
+ message: "Custom reasoning: ${header.MyReasoning}"
----
====
diff --git
a/components/camel-ai/camel-openai/src/main/java/org/apache/camel/component/openai/OpenAIConfiguration.java
b/components/camel-ai/camel-openai/src/main/java/org/apache/camel/component/openai/OpenAIConfiguration.java
index 654052bad1b3..b086656cd54f 100644
---
a/components/camel-ai/camel-openai/src/main/java/org/apache/camel/component/openai/OpenAIConfiguration.java
+++
b/components/camel-ai/camel-openai/src/main/java/org/apache/camel/component/openai/OpenAIConfiguration.java
@@ -205,7 +205,7 @@ public class OpenAIConfiguration implements Cloneable {
@UriParam(prefix = "additionalResponseHeader.", multiValue = true)
@Metadata(description = "Map additional fields from the response message
to Camel headers. "
+ "The key is the field name in the API response,
the value is the Camel header name "
- + "(e.g.
additionalResponseHeader.reasoning_content=CamelMyReasoningHeader)")
+ + "(e.g.
additionalResponseHeader.reasoning_content=MyReasoningHeader)")
private Map<String, Object> additionalResponseHeader;
@UriParam(prefix = "mcpServer.", multiValue = true)
diff --git
a/components/camel-box/camel-box-component/src/generated/resources/META-INF/org/apache/camel/component/box/box.json
b/components/camel-box/camel-box-component/src/generated/resources/META-INF/org/apache/camel/component/box/box.json
index 7327874b58e2..f17f0102eb31 100644
---
a/components/camel-box/camel-box-component/src/generated/resources/META-INF/org/apache/camel/component/box/box.json
+++
b/components/camel-box/camel-box-component/src/generated/resources/META-INF/org/apache/camel/component/box/box.json
@@ -45,6 +45,9 @@
"userName": { "index": 17, "kind": "property", "displayName": "User Name",
"group": "security", "label": "security", "required": false, "type": "string",
"javaType": "java.lang.String", "deprecated": false, "autowired": false,
"secret": true, "security": "secret", "configurationClass":
"org.apache.camel.component.box.BoxConfiguration", "configurationField":
"configuration", "description": "Box user name, MUST be provided" },
"userPassword": { "index": 18, "kind": "property", "displayName": "User
Password", "group": "security", "label": "security", "required": false, "type":
"string", "javaType": "java.lang.String", "deprecated": false, "autowired":
false, "secret": true, "security": "secret", "configurationClass":
"org.apache.camel.component.box.BoxConfiguration", "configurationField":
"configuration", "description": "Box user password, MUST be provided if
authSecureStorage is not set, or returns null on [...]
},
+ "headers": {
+ "CamelBox.": { "index": 0, "kind": "header", "displayName": "", "group":
"common", "label": "", "required": false, "javaType": "Object", "deprecated":
false, "deprecationNote": "", "autowired": false, "secret": false,
"description": "The prefix of the headers that carry the parameters of the API
method: the header CamelBox.fileName holds the fileName parameter.",
"constantName":
"org.apache.camel.component.box.internal.BoxConstants#PROPERTY_PREFIX" }
+ },
"properties": {
"apiName": { "index": 0, "kind": "path", "displayName": "Api Name",
"group": "common", "label": "", "required": true, "type": "enum", "javaType":
"org.apache.camel.component.box.internal.BoxApiName", "enum": [
"COLLABORATIONS", "COMMENTS", "EVENT_LOGS", "FILES", "FOLDERS", "GROUPS",
"EVENTS", "SEARCH", "TASKS", "USERS" ], "deprecated": false, "deprecationNote":
"", "autowired": false, "secret": false, "configurationClass":
"org.apache.camel.component.box.BoxConfiguration", "configura [...]
"methodName": { "index": 1, "kind": "path", "displayName": "Method Name",
"group": "common", "label": "", "required": true, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "configurationClass":
"org.apache.camel.component.box.BoxConfiguration", "configurationField":
"configuration", "description": "What sub operation to use for the selected
operation" },
diff --git
a/components/camel-box/camel-box-component/src/main/java/org/apache/camel/component/box/BoxEndpoint.java
b/components/camel-box/camel-box-component/src/main/java/org/apache/camel/component/box/BoxEndpoint.java
index c533c6c67efb..66e9231ef9f2 100644
---
a/components/camel-box/camel-box-component/src/main/java/org/apache/camel/component/box/BoxEndpoint.java
+++
b/components/camel-box/camel-box-component/src/main/java/org/apache/camel/component/box/BoxEndpoint.java
@@ -50,7 +50,8 @@ import
org.apache.camel.support.component.ApiMethodPropertiesHelper;
*/
@UriEndpoint(firstVersion = "2.14.0", scheme = "box", title = "Box", syntax =
"box:apiName/methodName",
apiSyntax = "apiName/methodName",
- consumerPrefix = "consumer", category = { Category.CLOUD,
Category.FILE, Category.API }, lenientProperties = true)
+ consumerPrefix = "consumer", category = { Category.CLOUD,
Category.FILE, Category.API }, lenientProperties = true,
+ headersClass = BoxConstants.class)
@Metadata(excludeProperties =
"startScheduler,initialDelay,delay,timeUnit,useFixedDelay,pollStrategy,runLoggingLevel,sendEmptyMessageWhenIdle"
+
",greedy,scheduler,schedulerProperties,scheduledExecutorService,backoffMultiplier,backoffIdleThreshold,backoffErrorThreshold,repeatCount,bridgeErrorHandler")
public class BoxEndpoint extends AbstractApiEndpoint<BoxApiName,
BoxConfiguration> {
diff --git
a/components/camel-box/camel-box-component/src/main/java/org/apache/camel/component/box/internal/BoxConstants.java
b/components/camel-box/camel-box-component/src/main/java/org/apache/camel/component/box/internal/BoxConstants.java
index 61223d730729..71653b33851a 100644
---
a/components/camel-box/camel-box-component/src/main/java/org/apache/camel/component/box/internal/BoxConstants.java
+++
b/components/camel-box/camel-box-component/src/main/java/org/apache/camel/component/box/internal/BoxConstants.java
@@ -16,12 +16,17 @@
*/
package org.apache.camel.component.box.internal;
+import org.apache.camel.spi.Metadata;
+
/**
* Constants for Box component.
*/
public interface BoxConstants {
- // suffix for parameters when passed as exchange header properties
+ // prefix for parameters when passed as exchange header properties
+ @Metadata(description = "The prefix of the headers that carry the
parameters of the API method: the header"
+ + " CamelBox.fileName holds the fileName
parameter.",
+ javaType = "Object")
String PROPERTY_PREFIX = "CamelBox.";
// thread profile name for this component
diff --git
a/components/camel-crypto/src/generated/resources/META-INF/org/apache/camel/component/crypto/crypto.json
b/components/camel-crypto/src/generated/resources/META-INF/org/apache/camel/component/crypto/crypto.json
index 17f736dc943c..9f14e382ba45 100644
---
a/components/camel-crypto/src/generated/resources/META-INF/org/apache/camel/component/crypto/crypto.json
+++
b/components/camel-crypto/src/generated/resources/META-INF/org/apache/camel/component/crypto/crypto.json
@@ -49,8 +49,9 @@
"headers": {
"CamelSignaturePrivateKey": { "index": 0, "kind": "header", "displayName":
"", "group": "producer", "label": "", "required": false, "javaType":
"java.security.PrivateKey", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "description": "The PrivateKey that should
be used to sign the message", "constantName":
"org.apache.camel.component.crypto.DigitalSignatureConstants#SIGNATURE_PRIVATE_KEY"
},
"CamelSignaturePublicKeyOrCert": { "index": 1, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType": "Certificate or PublicKey", "deprecated": false, "deprecationNote":
"", "autowired": false, "secret": false, "description": "The Certificate or
PublicKey that should be used to verify the signature", "constantName":
"org.apache.camel.component.crypto.DigitalSignatureConstants#SIGNATURE_PUBLIC_KEY_OR_CERT"
},
- "CamelSignatureKeyStoreAlias": { "index": 2, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType": "String", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "The alias used to query the KeyStore
for keys and Certificates to be used in signing and verifying exchanges",
"constantName":
"org.apache.camel.component.crypto.DigitalSignatureConstants#KEYSTORE_ALIAS" },
- "CamelSignatureKeyStorePassword": { "index": 3, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType": "char[]", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "The password used to access an aliased
PrivateKey in the KeyStore.", "constantName":
"org.apache.camel.component.crypto.DigitalSignatureConstants#KEYSTORE_PASSWORD"
}
+ "CamelDigitalSignature": { "index": 2, "kind": "header", "displayName":
"", "group": "producer", "label": "", "required": false, "javaType": "byte[]
(Base64 encoded)", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "The signature of the message, set by
the sign operation and read by the verify operation (the signatureHeaderName
option changes the header name)", "constantName":
"org.apache.camel.component.crypto.DigitalSignatureConstant [...]
+ "CamelSignatureKeyStoreAlias": { "index": 3, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType": "String", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "The alias used to query the KeyStore
for keys and Certificates to be used in signing and verifying exchanges",
"constantName":
"org.apache.camel.component.crypto.DigitalSignatureConstants#KEYSTORE_ALIAS" },
+ "CamelSignatureKeyStorePassword": { "index": 4, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType": "char[]", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "The password used to access an aliased
PrivateKey in the KeyStore.", "constantName":
"org.apache.camel.component.crypto.DigitalSignatureConstants#KEYSTORE_PASSWORD"
}
},
"properties": {
"cryptoOperation": { "index": 0, "kind": "path", "displayName": "Crypto
Operation", "group": "producer", "label": "", "required": true, "type": "enum",
"javaType": "org.apache.camel.component.crypto.CryptoOperation", "enum": [
"sign", "verify" ], "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "configurationClass":
"org.apache.camel.component.crypto.DigitalSignatureConfiguration",
"configurationField": "configuration", "description": "Set the Crypto o [...]
diff --git
a/components/camel-crypto/src/main/java/org/apache/camel/component/crypto/DigitalSignatureConstants.java
b/components/camel-crypto/src/main/java/org/apache/camel/component/crypto/DigitalSignatureConstants.java
index d94677d737b4..8dea9fda1d68 100644
---
a/components/camel-crypto/src/main/java/org/apache/camel/component/crypto/DigitalSignatureConstants.java
+++
b/components/camel-crypto/src/main/java/org/apache/camel/component/crypto/DigitalSignatureConstants.java
@@ -27,6 +27,9 @@ public final class DigitalSignatureConstants {
@Metadata(description = "The Certificate or PublicKey that should be used
to verify the signature",
javaType = "Certificate or PublicKey")
public static final String SIGNATURE_PUBLIC_KEY_OR_CERT =
"CamelSignaturePublicKeyOrCert";
+ @Metadata(description = "The signature of the message, set by the sign
operation and read by the verify operation"
+ + " (the signatureHeaderName option changes the
header name)",
+ javaType = "byte[] (Base64 encoded)")
public static final String SIGNATURE = "CamelDigitalSignature";
@Metadata(description = "The alias used to query the KeyStore for keys and
Certificates to be\n" +
" used in signing and verifying exchanges",
diff --git a/components/camel-fop/src/main/docs/fop-component.adoc
b/components/camel-fop/src/main/docs/fop-component.adoc
index 4b8140c6875c..d71438459ecb 100644
--- a/components/camel-fop/src/main/docs/fop-component.adoc
+++ b/components/camel-fop/src/main/docs/fop-component.adoc
@@ -72,6 +72,8 @@ supported:
The complete list of valid output formats can be found in the
`MimeConstants.java` source file.
+The endpoint URI takes either the name in lower case (`fop:pdf`) or the output
format (`fop:application/pdf`).
+
// component options: START
include::partial$component-configure-options.adoc[]
@@ -137,7 +139,7 @@ Java::
----
from("file:source/data/xml")
.to("xslt:xslt/template.xsl")
- .to("fop:application/pdf")
+ .to("fop:pdf")
.to("file:target/data");
----
@@ -148,7 +150,7 @@ XML::
<route>
<from uri="file:source/data/xml"/>
<to uri="xslt:xslt/template.xsl"/>
- <to uri="fop:application/pdf"/>
+ <to uri="fop:pdf"/>
<to uri="file:target/data"/>
</route>
----
@@ -164,7 +166,7 @@ YAML::
- to:
uri: xslt:xslt/template.xsl
- to:
- uri: fop:application/pdf
+ uri: fop:pdf
- to:
uri: file:target/data
----
diff --git a/components/camel-metrics/src/main/docs/metrics-component.adoc
b/components/camel-metrics/src/main/docs/metrics-component.adoc
index a70cb8c85cbc..e84bfe571880 100644
--- a/components/camel-metrics/src/main/docs/metrics-component.adoc
+++ b/components/camel-metrics/src/main/docs/metrics-component.adoc
@@ -543,9 +543,9 @@ Java::
+
[source,java]
----
-// marks simple.meter without value
+// marks the simple.meter meter without value
from("direct:in")
- .to("metrics:simple.meter")
+ .to("metrics:meter:simple.meter")
.to("direct:out");
----
@@ -555,7 +555,7 @@ XML::
----
<route>
<from uri="direct:in"/>
- <to uri="metrics:simple.meter"/>
+ <to uri="metrics:meter:simple.meter"/>
<to uri="direct:out"/>
</route>
----
@@ -569,7 +569,7 @@ YAML::
uri: direct:in
steps:
- to:
- uri: metrics:simple.meter
+ uri: metrics:meter:simple.meter
- to:
uri: direct:out
----
diff --git
a/components/camel-solr/src/generated/resources/META-INF/org/apache/camel/component/solr/solr.json
b/components/camel-solr/src/generated/resources/META-INF/org/apache/camel/component/solr/solr.json
index 97c66fc517d0..299fa0fe908c 100644
---
a/components/camel-solr/src/generated/resources/META-INF/org/apache/camel/component/solr/solr.json
+++
b/components/camel-solr/src/generated/resources/META-INF/org/apache/camel/component/solr/solr.json
@@ -47,7 +47,9 @@
"CamelSolrFrom": { "index": 5, "kind": "header", "displayName": "",
"group": "producer", "label": "", "required": false, "javaType": "Integer",
"deprecated": false, "deprecationNote": "", "autowired": false, "secret":
false, "description": "The starting index of the response.", "constantName":
"org.apache.camel.component.solr.SolrConstants#PARAM_FROM" },
"CamelSolrParams": { "index": 6, "kind": "header", "displayName": "",
"group": "producer", "label": "", "required": false, "javaType":
"org.apache.solr.common.params.SolrParams", "deprecated": false,
"deprecationNote": "", "autowired": false, "secret": false, "description": "The
solr parameters to use for the request.", "constantName":
"org.apache.camel.component.solr.SolrConstants#PARAM_SOLR_PARAMS" },
"CamelSolrDeleteByQuery": { "index": 7, "kind": "header", "displayName":
"", "group": "producer", "label": "", "required": false, "javaType": "boolean",
"deprecated": false, "deprecationNote": "", "autowired": false, "secret":
false, "defaultValue": "false", "description": "For the delete instruction,
interpret body as query\/queries instead of id\/ids.", "constantName":
"org.apache.camel.component.solr.SolrConstants#PARAM_DELETE_BY_QUERY" },
- "Content-Type": { "index": 8, "kind": "header", "displayName": "",
"group": "producer", "label": "", "required": false, "javaType": "String",
"deprecated": false, "deprecationNote": "", "autowired": false, "secret":
false, "description": "The content type is used to identify the type when
inserting files.", "constantName":
"org.apache.camel.component.solr.SolrConstants#PARAM_CONTENT_TYPE" }
+ "Content-Type": { "index": 8, "kind": "header", "displayName": "",
"group": "producer", "label": "", "required": false, "javaType": "String",
"deprecated": false, "deprecationNote": "", "autowired": false, "secret":
false, "description": "The content type is used to identify the type when
inserting files.", "constantName":
"org.apache.camel.component.solr.SolrConstants#PARAM_CONTENT_TYPE" },
+ "CamelSolrField.": { "index": 9, "kind": "header", "displayName": "",
"group": "producer", "label": "", "required": false, "javaType": "Object",
"deprecated": false, "deprecationNote": "", "autowired": false, "secret":
false, "description": "The prefix of the headers that carry the fields of the
document to insert: the header CamelSolrField.id holds the id field.",
"constantName":
"org.apache.camel.component.solr.SolrConstants#HEADER_FIELD_PREFIX" },
+ "CamelSolrParam.": { "index": 10, "kind": "header", "displayName": "",
"group": "producer", "label": "", "required": false, "javaType": "String",
"deprecated": false, "deprecationNote": "", "autowired": false, "secret":
false, "description": "The prefix of the headers that carry extra request
parameters: the header CamelSolrParam.commit holds the commit parameter.",
"constantName":
"org.apache.camel.component.solr.SolrConstants#HEADER_PARAM_PREFIX" }
},
"properties": {
"host": { "index": 0, "kind": "path", "displayName": "Host", "group":
"producer", "label": "", "required": true, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "configurationClass":
"org.apache.camel.component.solr.SolrConfiguration", "configurationField":
"configuration", "description": "The solr instance host name (set to 'default'
to use the host name defined on component level)" },
diff --git
a/components/camel-solr/src/main/java/org/apache/camel/component/solr/SolrConstants.java
b/components/camel-solr/src/main/java/org/apache/camel/component/solr/SolrConstants.java
index d5bfc3073005..1a786de7f013 100644
---
a/components/camel-solr/src/main/java/org/apache/camel/component/solr/SolrConstants.java
+++
b/components/camel-solr/src/main/java/org/apache/camel/component/solr/SolrConstants.java
@@ -42,7 +42,13 @@ public interface SolrConstants {
@Metadata(description = "The content type is used to identify the type
when inserting files.", javaType = "String")
String PARAM_CONTENT_TYPE = Exchange.CONTENT_TYPE;
+ @Metadata(description = "The prefix of the headers that carry the fields
of the document to insert: the header"
+ + " CamelSolrField.id holds the id field.",
+ javaType = "Object")
String HEADER_FIELD_PREFIX = "CamelSolrField.";
+ @Metadata(description = "The prefix of the headers that carry extra
request parameters: the header"
+ + " CamelSolrParam.commit holds the commit
parameter.",
+ javaType = "String")
String HEADER_PARAM_PREFIX = "CamelSolrParam.";
String PROPERTY_ACTION_CONTEXT = "SolrActionContext";
diff --git
a/components/camel-xmlsecurity/src/main/docs/xmlSecurity-dataformat.adoc
b/components/camel-xmlsecurity/src/main/docs/xmlSecurity-dataformat.adoc
index eaf5a86d81c7..ced411c85754 100644
--- a/components/camel-xmlsecurity/src/main/docs/xmlSecurity-dataformat.adoc
+++ b/components/camel-xmlsecurity/src/main/docs/xmlSecurity-dataformat.adoc
@@ -208,7 +208,7 @@ Java::
----
from("direct:start")
.marshal().xmlSecurity("//cheese:cheesesites/italy", namespaces, true,
- "recipient", XMLCipher.AES_128_CBC, XMLCipher.RSA_v1dot5,
trustStoreParams)
+ "recipient", XMLCipher.AES_128, XMLCipher.RSA_v1dot5,
trustStoreParams)
.to("...");
----
@@ -227,8 +227,8 @@ XML::
<marshal>
<xmlSecurity secureTag="//cheese:cheesesites/italy"
secureTagContents="true"
-
xmlCipherAlgorithm="http://www.w3.org/2001/04/xmlenc#aes128-cbc"
-
keyCipherAlgorithm="http://www.w3.org/2001/04/xmlenc#rsa-1_5"
+ xmlCipherAlgorithm="AES_128"
+ keyCipherAlgorithm="RSA_v1dot5"
recipientKeyAlias="recipient"
keyOrTrustStoreParameters="trustStoreParams"/>
</marshal>
@@ -247,8 +247,8 @@ YAML::
xmlSecurity:
secureTag: "//cheese:cheesesites/italy"
secureTagContents: true
- xmlCipherAlgorithm: "http://www.w3.org/2001/04/xmlenc#aes128-cbc"
- keyCipherAlgorithm: "http://www.w3.org/2001/04/xmlenc#rsa-1_5"
+ xmlCipherAlgorithm: AES_128
+ keyCipherAlgorithm: RSA_v1dot5
recipientKeyAlias: recipient
keyOrTrustStoreParameters: "#trustStoreParams"
----
@@ -265,7 +265,7 @@ Java::
----
from("direct:encrypted")
.unmarshal().xmlSecurity("//cheese:cheesesites/italy", namespaces, true,
- "recipient", XMLCipher.AES_128_CBC, XMLCipher.RSA_v1dot5,
keyStoreParams)
+ "recipient", XMLCipher.AES_128, XMLCipher.RSA_v1dot5,
keyStoreParams)
.to("...");
----
@@ -284,8 +284,8 @@ XML::
<unmarshal>
<xmlSecurity secureTag="//cheese:cheesesites/italy"
secureTagContents="true"
-
xmlCipherAlgorithm="http://www.w3.org/2001/04/xmlenc#aes128-cbc"
-
keyCipherAlgorithm="http://www.w3.org/2001/04/xmlenc#rsa-1_5"
+ xmlCipherAlgorithm="AES_128"
+ keyCipherAlgorithm="RSA_v1dot5"
recipientKeyAlias="recipient"
keyOrTrustStoreParameters="keyStoreParams"
keyPassword="privateKeyPassword" />
@@ -305,8 +305,8 @@ YAML::
xmlSecurity:
secureTag: "//cheese:cheesesites/italy"
secureTagContents: true
- xmlCipherAlgorithm: "http://www.w3.org/2001/04/xmlenc#aes128-cbc"
- keyCipherAlgorithm: "http://www.w3.org/2001/04/xmlenc#rsa-1_5"
+ xmlCipherAlgorithm: AES_128
+ keyCipherAlgorithm: RSA_v1dot5
recipientKeyAlias: recipient
keyOrTrustStoreParameters: "#keyStoreParams"
keyPassword: privateKeyPassword
diff --git
a/core/camel-core-catalog/src/main/java/org/apache/camel/catalog/impl/AbstractCamelCatalog.java
b/core/camel-core-catalog/src/main/java/org/apache/camel/catalog/impl/AbstractCamelCatalog.java
index 2f44c34ceb89..8d61163bf2ff 100644
---
a/core/camel-core-catalog/src/main/java/org/apache/camel/catalog/impl/AbstractCamelCatalog.java
+++
b/core/camel-core-catalog/src/main/java/org/apache/camel/catalog/impl/AbstractCamelCatalog.java
@@ -304,8 +304,8 @@ public abstract class AbstractCamelCatalog {
boolean valuePlaceholder = value.startsWith("{{") ||
value.startsWith("${") || value.startsWith("$simple{");
boolean lookup = value.startsWith("#") && value.length() > 1;
// we cannot evaluate multi values as strict as the others, as
we don't know their expected types
- boolean multiValue = prefix != null &&
originalName.startsWith(prefix)
- && row.isMultiValue();
+ boolean multiValue = (prefix != null &&
originalName.startsWith(prefix) && row.isMultiValue())
+ || isMapEntry(row, originalName);
// default value
Object defaultValue = row.getDefaultValue();
@@ -1844,9 +1844,25 @@ public abstract class AbstractCamelCatalog {
return row.getName();
}
}
+ int dot = name.indexOf('.');
+ if (dot > 0) {
+ BaseOptionModel row = rows.get(name.substring(0, dot));
+ if (row != null && isMapEntry(row, name)) {
+ return row.getName();
+ }
+ }
return null;
}
+ /**
+ * Whether the name sets an entry of a Map option
(userMetadata.messageId=x fills the userMetadata map), as property
+ * binding does.
+ */
+ private static boolean isMapEntry(BaseOptionModel row, String name) {
+ String javaType = row.getJavaType();
+ return javaType != null && javaType.startsWith("java.util.Map") &&
name.startsWith(row.getName() + ".");
+ }
+
/**
* Converts the string from dash format into camel case (hello-great-world
-> helloGreatWorld)
*
diff --git
a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/EndpointHeaderBuilders.java
b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/EndpointHeaderBuilders.java
index 8953cc8da21e..104188aba41c 100644
---
a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/EndpointHeaderBuilders.java
+++
b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/EndpointHeaderBuilders.java
@@ -839,6 +839,20 @@ public class EndpointHeaderBuilders {
public static QueueEndpointBuilderFactory.QueueHeaderNameBuilder
azureStorageQueue() {
return QueueEndpointBuilderFactory.QueueHeaderNameBuilder.INSTANCE;
}
+ /**
+ * Box (camel-box)
+ * Upload, download and manage files, folders, groups, collaborations, etc.
+ * on box.com.
+ *
+ * Category: cloud,file,api
+ * Since: 2.14
+ * Maven coordinates: org.apache.camel:camel-box
+ *
+ * @return the dsl builder for the headers' name.
+ */
+ public static BoxEndpointBuilderFactory.BoxHeaderNameBuilder box() {
+ return BoxEndpointBuilderFactory.BoxHeaderNameBuilder.INSTANCE;
+ }
/**
* Caffeine Cache (camel-caffeine)
* Perform caching operations using Caffeine Cache.
diff --git
a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/BoxEndpointBuilderFactory.java
b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/BoxEndpointBuilderFactory.java
index e0b3719adac0..8a8e7f7292e7 100644
---
a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/BoxEndpointBuilderFactory.java
+++
b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/BoxEndpointBuilderFactory.java
@@ -1150,6 +1150,20 @@ public interface BoxEndpointBuilderFactory {
}
public interface BoxBuilders {
+ /**
+ * Box (camel-box)
+ * Upload, download and manage files, folders, groups, collaborations,
+ * etc. on box.com.
+ *
+ * Category: cloud,file,api
+ * Since: 2.14
+ * Maven coordinates: org.apache.camel:camel-box
+ *
+ * @return the dsl builder for the headers' name.
+ */
+ default BoxHeaderNameBuilder box() {
+ return BoxHeaderNameBuilder.INSTANCE;
+ }
/**
* Box (camel-box)
* Upload, download and manage files, folders, groups, collaborations,
@@ -1206,6 +1220,30 @@ public interface BoxEndpointBuilderFactory {
}
}
+ /**
+ * The builder of headers' name for the Box component.
+ */
+ public static class BoxHeaderNameBuilder {
+ /**
+ * The internal instance of the builder used to access to all the
+ * methods representing the name of headers.
+ */
+ public static final BoxHeaderNameBuilder INSTANCE = new
BoxHeaderNameBuilder();
+
+ /**
+ * The prefix of the headers that carry the parameters of the API
+ * method: the header CamelBox.fileName holds the fileName parameter.
+ *
+ * The option is a: {@code Object} type.
+ *
+ * Group: common
+ *
+ * @return the name of the header {@code Box.}.
+ */
+ public String box() {
+ return "CamelBox.";
+ }
+ }
static BoxEndpointBuilder endpointBuilder(String componentName, String
path) {
class BoxEndpointBuilderImpl extends AbstractEndpointBuilder
implements BoxEndpointBuilder, AdvancedBoxEndpointBuilder {
public BoxEndpointBuilderImpl(String path) {
diff --git
a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/DigitalSignatureEndpointBuilderFactory.java
b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/DigitalSignatureEndpointBuilderFactory.java
index a6f5bce2df50..18a8da68f095 100644
---
a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/DigitalSignatureEndpointBuilderFactory.java
+++
b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/DigitalSignatureEndpointBuilderFactory.java
@@ -630,6 +630,20 @@ public interface DigitalSignatureEndpointBuilderFactory {
public String signaturePublicKeyOrCert() {
return "CamelSignaturePublicKeyOrCert";
}
+ /**
+ * The signature of the message, set by the sign operation and read by
+ * the verify operation (the signatureHeaderName option changes the
+ * header name).
+ *
+ * The option is a: {@code byte[] (Base64 encoded)} type.
+ *
+ * Group: producer
+ *
+ * @return the name of the header {@code DigitalSignature}.
+ */
+ public String digitalSignature() {
+ return "CamelDigitalSignature";
+ }
/**
* The alias used to query the KeyStore for keys and Certificates to be
* used in signing and verifying exchanges.
diff --git
a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/OpenAIEndpointBuilderFactory.java
b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/OpenAIEndpointBuilderFactory.java
index ea7ea1add6b1..054189633b23 100644
---
a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/OpenAIEndpointBuilderFactory.java
+++
b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/OpenAIEndpointBuilderFactory.java
@@ -133,8 +133,8 @@ public interface OpenAIEndpointBuilderFactory {
* Map additional fields from the response message to Camel headers.
The
* key is the field name in the API response, the value is the Camel
* header name (e.g.
- * additionalResponseHeader.reasoning_content=CamelMyReasoningHeader).
- * This is a multi-value option with prefix: additionalResponseHeader.
+ * additionalResponseHeader.reasoning_content=MyReasoningHeader). This
+ * is a multi-value option with prefix: additionalResponseHeader.
*
* The option is a: <code>java.util.Map<java.lang.String,
* java.lang.Object></code> type.
@@ -156,8 +156,8 @@ public interface OpenAIEndpointBuilderFactory {
* Map additional fields from the response message to Camel headers.
The
* key is the field name in the API response, the value is the Camel
* header name (e.g.
- * additionalResponseHeader.reasoning_content=CamelMyReasoningHeader).
- * This is a multi-value option with prefix: additionalResponseHeader.
+ * additionalResponseHeader.reasoning_content=MyReasoningHeader). This
+ * is a multi-value option with prefix: additionalResponseHeader.
*
* The option is a: <code>java.util.Map<java.lang.String,
* java.lang.Object></code> type.
diff --git
a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/SolrEndpointBuilderFactory.java
b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/SolrEndpointBuilderFactory.java
index 81a85bbfd441..8c8b9c6498cb 100644
---
a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/SolrEndpointBuilderFactory.java
+++
b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/SolrEndpointBuilderFactory.java
@@ -719,6 +719,32 @@ public interface SolrEndpointBuilderFactory {
public String contentType() {
return "Content-Type";
}
+ /**
+ * The prefix of the headers that carry the fields of the document to
+ * insert: the header CamelSolrField.id holds the id field.
+ *
+ * The option is a: {@code Object} type.
+ *
+ * Group: producer
+ *
+ * @return the name of the header {@code SolrField.}.
+ */
+ public String solrfield() {
+ return "CamelSolrField.";
+ }
+ /**
+ * The prefix of the headers that carry extra request parameters: the
+ * header CamelSolrParam.commit holds the commit parameter.
+ *
+ * The option is a: {@code String} type.
+ *
+ * Group: producer
+ *
+ * @return the name of the header {@code SolrParam.}.
+ */
+ public String solrparam() {
+ return "CamelSolrParam.";
+ }
}
static SolrEndpointBuilder endpointBuilder(String componentName, String
path) {
class SolrEndpointBuilderImpl extends AbstractEndpointBuilder
implements SolrEndpointBuilder, AdvancedSolrEndpointBuilder {
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/EndpointChecks.java
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/EndpointChecks.java
index ccb1273dbef8..9c0aceb7905b 100644
---
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/EndpointChecks.java
+++
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/EndpointChecks.java
@@ -34,6 +34,7 @@ import static
org.apache.camel.dsl.jbang.core.commands.ai.YamlLines.YAML_URI_PAT
import static
org.apache.camel.dsl.jbang.core.commands.ai.YamlLines.countLeadingSpaces;
import static
org.apache.camel.dsl.jbang.core.commands.ai.YamlLines.extractEipFromLine;
import static
org.apache.camel.dsl.jbang.core.commands.ai.YamlLines.findParentEip;
+import static
org.apache.camel.dsl.jbang.core.commands.ai.YamlLines.stripComment;
import static org.apache.camel.dsl.jbang.core.commands.ai.YamlLines.unquote;
/**
@@ -150,6 +151,9 @@ final class EndpointChecks {
String nextTrimmed = next.trim();
if (nextIndent == lineIndent &&
nextTrimmed.startsWith("parameters:")) {
int paramBlockIndent = nextIndent;
+ int blockScalarIndent = -1;
+ String mapKey = null;
+ int mapIndent = -1;
for (int k = j + 1; k < lines.length; k++) {
String paramLine = lines[k];
if (paramLine.isBlank()) {
@@ -159,11 +163,33 @@ final class EndpointChecks {
if (paramIndent <= paramBlockIndent) {
break;
}
+ if (blockScalarIndent >= 0 && paramIndent >
blockScalarIndent) {
+ // the lines of a block scalar (argSchema: |
followed by JSON) are its value, not options
+ continue;
+ }
+ blockScalarIndent = -1;
+ if (mapKey != null && paramIndent <= mapIndent) {
+ mapKey = null;
+ }
String paramTrimmed = paramLine.trim();
int colonPos = paramTrimmed.indexOf(':');
if (colonPos > 0) {
- String key = paramTrimmed.substring(0,
colonPos).trim();
- String val =
unquote(paramTrimmed.substring(colonPos + 1).trim());
+ String key = unquote(paramTrimmed.substring(0,
colonPos).trim());
+ String val =
unquote(stripComment(paramTrimmed.substring(colonPos + 1).trim()));
+ if (mapKey != null) {
+ // headers: with foo: bar under it is the
entry foo of the headers map option
+ key = mapKey + "." + key;
+ } else if (val.isEmpty() && k + 1 < lines.length
+ && countLeadingSpaces(lines[k + 1]) >
paramIndent) {
+ mapKey = key;
+ mapIndent = paramIndent;
+ continue;
+ }
+ if (YamlLines.isBlockScalarIndicator(val)) {
+ // the option is checked by name; its value is
the block that follows
+ blockScalarIndent = paramIndent;
+ val = "";
+ }
char sep = hasParams ? '&' : '?';
uriBuilder.append(sep).append(key).append('=').append(val);
hasParams = true;
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/HeaderChecks.java
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/HeaderChecks.java
index 15d785f8aebf..23fd298f82d2 100644
---
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/HeaderChecks.java
+++
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/HeaderChecks.java
@@ -40,7 +40,7 @@ final class HeaderChecks {
}
static final Pattern CAMEL_HEADER_REF_PATTERN = Pattern.compile(
-
"(?:\\$\\{headers?\\.|headers\\.|headers\\[['\"]|header\\(['\"]|name:\\s*['\"]?)(Camel[A-Z][A-Za-z0-9]*)");
+
"(?:\\$\\{headers?\\.|headers\\.|headers\\[['\"]|header\\(['\"]|(name):\\s*['\"]?)(Camel[A-Z][A-Za-z0-9]*(?:\\.[A-Za-z0-9_]+)*)");
static final Pattern SCHEME_IN_URI_PATTERN =
Pattern.compile("uri:\\s*\"?([a-zA-Z][a-zA-Z0-9+.-]*):");
@@ -100,8 +100,12 @@ final class HeaderChecks {
for (int i = 0; i < lines.length; i++) {
Matcher m = CAMEL_HEADER_REF_PATTERN.matcher(lines[i]);
while (m.find()) {
- String name = m.group(1);
- if (common.contains(name) || reported.contains(name)) {
+ if (m.group(1) != null && !isHeaderName(lines, i)) {
+ // name: of a setProperty, setVariable, bean...: not a
header
+ continue;
+ }
+ String name = m.group(2);
+ if (common.contains(name) || reported.contains(name) ||
isKnown(name, known)) {
continue;
}
String propertyOwner = null;
@@ -117,7 +121,7 @@ final class HeaderChecks {
+ ", not a header (the header would be null):
write ${exchangeProperty." + name + "}");
continue;
}
- if (known.contains(name) || !reported.add(name)) {
+ if (!reported.add(name)) {
continue;
}
String best = closestName(name, new ArrayList<>(known));
@@ -142,4 +146,46 @@ final class HeaderChecks {
return msgs;
}
+ /**
+ * A header the metadata lists: as is (CamelBox.fileName), by a prefix the
component documents with a trailing dot
+ * (CamelSolrField. for CamelSolrField.id), or as the head of an OGNL path
(CamelFileName.length()).
+ */
+ static boolean isKnown(String name, Set<String> known) {
+ String candidate = name;
+ while (true) {
+ if (known.contains(candidate)) {
+ return true;
+ }
+ for (String k : known) {
+ if (k.endsWith(".") && candidate.startsWith(k)) {
+ return true;
+ }
+ }
+ int dot = candidate.lastIndexOf('.');
+ if (dot < 0) {
+ return false;
+ }
+ candidate = candidate.substring(0, dot);
+ }
+ }
+
+ /** Whether the name: on this line belongs to a setHeader or removeHeader
step, not a setProperty, a bean... */
+ static boolean isHeaderName(String[] lines, int index) {
+ int indent = YamlLines.countLeadingSpaces(lines[index]);
+ for (int j = index - 1; j >= 0; j--) {
+ String line = lines[j];
+ if (line.isBlank() || line.trim().startsWith("#")) {
+ continue;
+ }
+ if (YamlLines.countLeadingSpaces(line) < indent) {
+ String step = line.trim();
+ if (step.startsWith("- ")) {
+ step = step.substring(2).trim();
+ }
+ return step.startsWith("setHeader:") ||
step.startsWith("removeHeader:");
+ }
+ }
+ return false;
+ }
+
}
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/YamlLines.java
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/YamlLines.java
index 0220b2ee7b86..0f245b07c3d7 100644
---
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/YamlLines.java
+++
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/YamlLines.java
@@ -130,6 +130,33 @@ final class YamlLines {
return unquote(trimmed.substring(prefix.length()).trim());
}
+ /**
+ * A scalar without the YAML comment that may follow it: {@code 30000 #
the default is 300000} is 30000, and a
+ * quoted scalar keeps a # inside its quotes.
+ */
+ static String stripComment(String val) {
+ if (val.isEmpty()) {
+ return val;
+ }
+ char quote = val.charAt(0);
+ if (quote == '"' || quote == '\'') {
+ int close = val.indexOf(quote, 1);
+ return close > 0 ? val.substring(0, close + 1) : val;
+ }
+ for (int i = 1; i < val.length(); i++) {
+ if (val.charAt(i) == '#' && Character.isWhitespace(val.charAt(i -
1))) {
+ return val.substring(0, i).stripTrailing();
+ }
+ }
+ return val;
+ }
+
+ /** Whether a value is the indicator of a block scalar: |, >, and the
variants with a chomping or indent hint. */
+ static boolean isBlockScalarIndicator(String val) {
+ return !val.isEmpty() && (val.charAt(0) == '|' || val.charAt(0) ==
'>') && val.length() <= 3
+ && val.substring(1).chars().allMatch(c -> c == '-' || c == '+'
|| Character.isDigit(c));
+ }
+
static String unquote(String val) {
if (val.length() >= 2 && val.startsWith("\"") && val.endsWith("\"")) {
return val.substring(1, val.length() - 1);
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogDocExamplesTest.java
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogDocExamplesTest.java
new file mode 100644
index 000000000000..609a2682d77b
--- /dev/null
+++
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogDocExamplesTest.java
@@ -0,0 +1,128 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.dsl.jbang.core.commands.ai;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.camel.catalog.CamelCatalog;
+import org.apache.camel.catalog.DefaultCamelCatalog;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * CAMEL-24710: every YAML route example of the documentation passes the
checks {@code camel validate yaml} and the
+ * authoring tools run after the schema: endpoint URIs and their options,
Simple expressions and Camel headers against
+ * the catalog. The schema itself is guarded by EipDocExamplesTest in
camel-yaml-dsl-validator.
+ */
+class CatalogDocExamplesTest {
+
+ private static final Pattern YAML_BLOCK =
Pattern.compile("\\[source,yaml\\]\\n-{4}\\n(.*?)\\n-{4}", Pattern.DOTALL);
+
+ /**
+ * Pages whose examples are right for the runtime but fail the catalog
because the component metadata disagrees with
+ * the component (CAMEL-24748 and the notes on it); checked again when the
metadata is fixed.
+ */
+ private static final Map<String, String> PAGES_SKIPPED = Map.ofEntries(
+ Map.entry("azure-storage-datalake-component", "CAMEL-24748: the
operation enum lists 2 of 15 values"),
+ Map.entry("google-secret-manager-component", "CAMEL-24748: the
operation enum lists 1 of 4 values"),
+ Map.entry("google-storage-component", "CAMEL-24748: objectName is
labelled producer, the consumer uses it"),
+ Map.entry("web3j-component", "CAMEL-24748: operation is labelled
producer, the consumer uses it"),
+ Map.entry("grpc-component", "CAMEL-24748: forwardOnError and
forwardOnCompleted are labelled consumer"),
+ Map.entry("netty-http-component", "CAMEL-24748:
bootstrapConfiguration is not in the metadata"),
+ Map.entry("jt400-component", "CAMEL-24748: the output options have
no @UriParam; the syntax has literal"
+ + " segments (QSYS.LIB,
objectPath.type) the catalog reads as options"),
+ Map.entry("olingo2-component", "CAMEL-24748: the syntax is
apiName/methodName, the runtime reads"
+ + " methodName/resourcePath with
the api name implicit"),
+ Map.entry("olingo4-component", "CAMEL-24748: the syntax is
apiName/methodName, the runtime reads"
+ + " methodName/resourcePath with
the api name implicit"),
+ Map.entry("xmpp-component", "CAMEL-24748: port is required in the
metadata, the runtime defaults it and"
+ + " takes a user@ prefix on the host"),
+ Map.entry("huggingface-component", "CAMEL-24748: task is an enum
in the metadata, the runtime takes any"
+ + " name with predictorBean"),
+ Map.entry("salesforce-rest-api", "CAMEL-24748: approval.* has no
@UriParam"));
+
+ /** Examples that show what only the runtime knows, by page and a text
found in the example. */
+ private static final Map<String, String> EXAMPLES_SKIPPED = Map.of(
+ "simple-advanced", "${foo", // a custom simple function registered
at runtime
+ "yaml-dsl", "myStep:"); // a step contributed by a custom YAML
deserializer
+
+ private static CamelCatalog catalog;
+
+ private record DocExamples(int examples, List<String> failures) {
+ }
+
+ @BeforeAll
+ static void setup() {
+ catalog = new DefaultCamelCatalog();
+ }
+
+ @Test
+ void everyYamlExampleOfTheDocumentationPassesTheCatalogChecks() {
+ List<String> pages = new ArrayList<>();
+ for (String name : catalog.findDocNames()) {
+ if (!PAGES_SKIPPED.containsKey(name)) {
+ pages.add(name);
+ }
+ }
+ DocExamples result = validate(pages);
+ assertThat(result.examples()).as("YAML route examples found in the
documentation").isGreaterThan(1700);
+ assertThat(result.failures()).as("Documentation examples that fail the
catalog checks").isEmpty();
+ }
+
+ @Test
+ void theSkippedPagesStillExist() {
+ for (String page : PAGES_SKIPPED.keySet()) {
+ assertThat(catalog.asciiDoc(page)).as("the skipped page %s (drop
the entry when it is gone)", page).isNotNull();
+ }
+ }
+
+ private static DocExamples validate(List<String> pages) {
+ int examples = 0;
+ List<String> failures = new ArrayList<>();
+ for (String page : pages) {
+ String doc = catalog.asciiDoc(page);
+ if (doc == null) {
+ continue;
+ }
+ int n = 0;
+ Matcher m = YAML_BLOCK.matcher(doc);
+ while (m.find()) {
+ String yaml = m.group(1).stripTrailing() + "\n";
+ if (!yaml.stripLeading().startsWith("- ")) {
+ // a fragment (an option list, a snippet), not a route file
+ continue;
+ }
+ n++;
+ String skipped = EXAMPLES_SKIPPED.get(page);
+ if (skipped != null && yaml.contains(skipped)) {
+ continue;
+ }
+ examples++;
+ for (String msg : SourceValidator.validateCamelYaml(yaml,
catalog)) {
+ failures.add(page + " example " + n + ": " + msg);
+ }
+ }
+ }
+ return new DocExamples(examples, failures);
+ }
+}
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/SourceValidatorEndpointTest.java
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/SourceValidatorEndpointTest.java
index 911d3a3c7fa3..a9694ee8908b 100644
---
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/SourceValidatorEndpointTest.java
+++
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/SourceValidatorEndpointTest.java
@@ -398,6 +398,64 @@ class SourceValidatorEndpointTest {
.contains("${exchangeProperty.CamelTimerCounter}");
}
+ @Test
+ void aPropertyOrBeanNameIsNotAHeader() {
+ // CAMEL-24710: name: counts as a header only under
setHeader/removeHeader; a dotted name is looked up as is,
+ // by the prefix the component documents (CamelSolrField.), and as the
head of an OGNL path
+ List<String> msgs = SourceValidator.validateKnownHeaders("""
+ - beans:
+ - name: CamelMyBean
+ type: "#class:com.example.MyBean"
+ - from:
+ uri: "file:in?noop=true"
+ steps:
+ - setProperty:
+ name: CamelAwsSqsDeleteFiltered
+ constant: "true"
+ - setHeader:
+ name: CamelSolrField.id
+ simple: "${body}"
+ - log: "${header.CamelFileName.length()}
${header.CamelFileNam.length()}"
+ - to:
+ uri: solr:localhost:8983/mycollection
+ """, catalog);
+ assertThat(msgs).hasSize(1);
+ assertThat(msgs.get(0)).contains("header CamelFileNam.length is not
set by").contains("did you mean CamelFileLength");
+ }
+
+ @Test
+ void aCommentABlockScalarAndANestedMapInTheParametersAreRead() {
+ // CAMEL-24710: a comment after a value is not the value; the body of
a block scalar is not options; the keys
+ // under a Map option are its entries
+ List<String> msgs = SourceValidator.validateYamlEndpoints("""
+ - from:
+ uri: ai-tool:createOrder
+ parameters:
+ description: "Create an order"
+ argSchema: |
+ {
+ "type": "object",
+ "properties": { "id": { "type": "string" } }
+ }
+ steps:
+ - to:
+ uri: log:out
+ parameters:
+ groupSize: 30000 # the default is none
+ showAll: true
+ - to:
+ uri: once
+ parameters:
+ name: hello
+ headers:
+ foo: foolish
+ bar: 456
+ unknownOne: x
+ """, catalog);
+ assertThat(msgs).hasSize(1);
+ assertThat(msgs.get(0)).contains("Unknown option 'unknownOne'");
+ }
+
@Test
void aProducerOnlyComponentInFromIsNamed() {
List<String> msgs = SourceValidator.validateYamlEndpoints("""
diff --git
a/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/java/org/apache/camel/dsl/yaml/validator/EipDocExamplesTest.java
b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/java/org/apache/camel/dsl/yaml/validator/EipDocExamplesTest.java
index 505ec1386e95..248ea4363421 100644
---
a/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/java/org/apache/camel/dsl/yaml/validator/EipDocExamplesTest.java
+++
b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/java/org/apache/camel/dsl/yaml/validator/EipDocExamplesTest.java
@@ -19,7 +19,6 @@ package org.apache.camel.dsl.yaml.validator;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
-import java.util.Set;
import java.util.function.Predicate;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -48,13 +47,6 @@ class EipDocExamplesTest {
"point-to-point-channel", "publish-subscribe-channel",
"return-address", "scatter-gather",
"selective-consumer", "service-activator", "transactional-client");
- /**
- * The xmlSecurity examples write the cipher algorithms as the URIs the
data format hands to XMLCipher, while the
- * model's enums list the XMLCipher constant names, which XMLCipher
rejects (CAMEL-24716). The page is checked again
- * once the model accepts what the data format does.
- */
- private static final Set<String> DATA_FORMAT_PAGES_SKIPPED =
Set.of("xmlSecurity-dataformat");
-
private static final Pattern YAML_BLOCK =
Pattern.compile("\\[source,yaml\\]\\n-{4}\\n(.*?)\\n-{4}", Pattern.DOTALL);
/**
@@ -100,8 +92,7 @@ class EipDocExamplesTest {
@Test
void everyYamlExampleOfTheDataFormatDocumentationValidates() throws
Exception {
- List<String> pages
- = docNames(name -> name.endsWith("-dataformat") &&
!DATA_FORMAT_PAGES_SKIPPED.contains(name));
+ List<String> pages = docNames(name -> name.endsWith("-dataformat"));
DocExamples result = validate(pages);