This is an automated email from the ASF dual-hosted git repository.
oscerd pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel-kamelets.git
The following commit(s) were added to refs/heads/main by this push:
new b618b1429 Fix #929: read the headers a Kamelet declares inside a data
type (#3075)
b618b1429 is described below
commit b618b1429247effb01e3cb41f9f60e84b5e6cf4b
Author: Andrea Cosentino <[email protected]>
AuthorDate: Thu Oct 1 10:39:16 2026 +0200
Fix #929: read the headers a Kamelet declares inside a data type (#3075)
#3061 made getKameletSupportedHeaders prefer what a Kamelet declares
over what its component can emit, but it read only the side-level
spec.dataTypes.<in|out>.headers. Four Kamelets put their headers one
level deeper, inside a data type, and those declarations were ignored:
slack-source 5 CloudEvent headers reported 0
azure-cosmosdb-source 5 CloudEvent headers component fallback
google-sheets-sink 5 GoogleSheets headers component fallback
aws-ddb-sink 2 DDB headers component fallback
slack-source is the plainest case: the test asserted zero supported
headers for a Kamelet that declares five. So #3061 fixed 15 of the 19
Kamelets that declare anything, and this covers the rest.
Read per-type headers as a per-side fallback, not as a union. Where a
side declares a top-level headers block that stays the answer, because
those are the headers the Kamelet emits whichever data type is in use,
while a per-type header appears only when its type is selected. Merging
the two would over-report in exactly the way the component list does --
unioning them took aws-s3-source from its 4 declared S3 headers to 9 by
adding the cloudevents ones, and testDeclaredHeadersWinOverTheComponent
from #3061 failed on it, which is the rule working as intended.
toHeaderModel now takes the fields rather than the object: the
side-level and type-level header POJOs are generated separately and
share no supertype.
Tests: assert the four counts above, so the behaviour is pinned rather
than implied. aws-s3-source stays at 4, unchanged.
Signed-off-by: Andrea Cosentino <[email protected]>
Co-authored-by: Claude Opus 5 <[email protected]>
---
.../camel/kamelets/catalog/KameletsCatalog.java | 73 +++++++++++++++++-----
.../kamelets/catalog/KameletsCatalogTest.java | 7 ++-
2 files changed, 64 insertions(+), 16 deletions(-)
diff --git
a/library/camel-kamelets-catalog/src/main/java/org/apache/camel/kamelets/catalog/KameletsCatalog.java
b/library/camel-kamelets-catalog/src/main/java/org/apache/camel/kamelets/catalog/KameletsCatalog.java
index 7bb6cbc48..1df9502c8 100644
---
a/library/camel-kamelets-catalog/src/main/java/org/apache/camel/kamelets/catalog/KameletsCatalog.java
+++
b/library/camel-kamelets-catalog/src/main/java/org/apache/camel/kamelets/catalog/KameletsCatalog.java
@@ -22,6 +22,7 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
+import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
@@ -42,6 +43,7 @@ import org.apache.camel.tooling.model.ComponentModel;
import org.apache.camel.util.ObjectHelper;
import org.apache.camel.v1.Kamelet;
import org.apache.camel.v1.kameletspec.datatypes.Headers;
+import org.apache.camel.v1.kameletspec.datatypes.Types;
import org.apache.camel.v1.kameletspec.DataTypes;
import org.apache.camel.v1.kameletspec.Definition;
import org.apache.camel.v1.kameletspec.Template;
@@ -276,32 +278,73 @@ public class KameletsCatalog {
* template actually emits or consumes rather than what its component
supports.
*/
private List<ComponentModel.EndpointHeaderModel>
getDeclaredHeaders(Kamelet kamelet) {
- List<ComponentModel.EndpointHeaderModel> declared = new ArrayList<>();
if (kamelet.getSpec() == null || kamelet.getSpec().getDataTypes() ==
null) {
- return declared;
+ return new ArrayList<>();
}
+ // Keyed by name, because the same header commonly repeats across the
data types
+ // of one side.
+ Map<String, ComponentModel.EndpointHeaderModel> declared = new
LinkedHashMap<>();
for (DataTypes dataType : kamelet.getSpec().getDataTypes().values()) {
- if (dataType == null || dataType.getHeaders() == null) {
+ if (dataType == null) {
continue;
}
- for (Map.Entry<String, Headers> entry :
dataType.getHeaders().entrySet()) {
- declared.add(toHeaderModel(entry.getKey(), entry.getValue()));
+ if (dataType.getHeaders() != null &&
!dataType.getHeaders().isEmpty()) {
+ // The side-level block is the authoritative summary for that
side: it is
+ // what the Kamelet always emits or consumes, whichever data
type is in
+ // use. Where it exists it is the answer, and the per-type
blocks below
+ // are deliberately not merged into it -- those headers appear
only when
+ // their type is selected, so adding them would over-report
exactly the
+ // way the component list does.
+ for (Map.Entry<String, Headers> entry :
dataType.getHeaders().entrySet()) {
+ Headers header = entry.getValue();
+ declared.computeIfAbsent(entry.getKey(), n ->
toHeaderModel(n,
+ header == null ? null : header.getTitle(),
+ header == null ? null : header.getDescription(),
+ header == null ? null : header.getType(),
+ header == null ? null : header.get_default(),
+ header == null ? null : header.getRequired()));
+ }
+ continue;
+ }
+ // No side-level block, so fall back to what its data types
declare before
+ // falling back to the component. A Kamelet that only transforms
its payload
+ // puts its headers there, and reading nothing made the catalog
report that
+ // such a Kamelet supports no headers at all.
+ if (dataType.getTypes() != null) {
+ for (Types type : dataType.getTypes().values()) {
+ if (type == null || type.getHeaders() == null) {
+ continue;
+ }
+ for (Map.Entry<String,
org.apache.camel.v1.kameletspec.datatypes.types.Headers> entry
+ : type.getHeaders().entrySet()) {
+
org.apache.camel.v1.kameletspec.datatypes.types.Headers header =
entry.getValue();
+ declared.computeIfAbsent(entry.getKey(), n ->
toHeaderModel(n,
+ header == null ? null : header.getTitle(),
+ header == null ? null :
header.getDescription(),
+ header == null ? null : header.getType(),
+ header == null ? null : header.get_default(),
+ header == null ? null : header.getRequired()));
+ }
+ }
}
}
- return declared;
+ return new ArrayList<>(declared.values());
}
- private ComponentModel.EndpointHeaderModel toHeaderModel(String name,
Headers header) {
+ /**
+ * The side-level and type-level header POJOs are generated separately and
share no
+ * supertype, so the fields are passed in rather than the object.
+ */
+ private ComponentModel.EndpointHeaderModel toHeaderModel(
+ String name, String title, String description, String type, String
defaultValue, Boolean required) {
ComponentModel.EndpointHeaderModel model = new
ComponentModel.EndpointHeaderModel();
model.setName(name);
- if (header != null) {
- model.setDisplayName(header.getTitle());
- model.setDescription(header.getDescription());
- model.setType(header.getType());
- model.setJavaType(header.getType());
- model.setDefaultValue(header.get_default());
- model.setRequired(Boolean.TRUE.equals(header.getRequired()));
- }
+ model.setDisplayName(title);
+ model.setDescription(description);
+ model.setType(type);
+ model.setJavaType(type);
+ model.setDefaultValue(defaultValue);
+ model.setRequired(Boolean.TRUE.equals(required));
return model;
}
diff --git
a/library/camel-kamelets-catalog/src/test/java/org/apache/camel/kamelets/catalog/KameletsCatalogTest.java
b/library/camel-kamelets-catalog/src/test/java/org/apache/camel/kamelets/catalog/KameletsCatalogTest.java
index 394fd3937..77e482d86 100644
---
a/library/camel-kamelets-catalog/src/test/java/org/apache/camel/kamelets/catalog/KameletsCatalogTest.java
+++
b/library/camel-kamelets-catalog/src/test/java/org/apache/camel/kamelets/catalog/KameletsCatalogTest.java
@@ -277,7 +277,12 @@ public class KameletsCatalogTest {
verifyHeaders("sftp-sink", 6);
verifyHeaders("sftp-source", 15);
verifyHeaders("slack-sink", 0);
- verifyHeaders("slack-source", 0);
+ // Declares five CloudEvent headers under
spec.dataTypes.out.types.cloudevents.headers,
+ // so it reports those rather than falling back to the camel-slack
component.
+ verifyHeaders("slack-source", 5);
+ verifyHeaders("azure-cosmosdb-source", 5);
+ verifyHeaders("google-sheets-sink", 5);
+ verifyHeaders("aws-ddb-sink", 2);
verifyHeaders("splunk-hec-sink", 1);
verifyHeaders("splunk-sink", 0);
verifyHeaders("splunk-source", 0);