Skip to content

Commit 04c1378

Browse files
committed
feat(props enhancer): expose endpointProps for EIP/entity
1 parent 21f5ee9 commit 04c1378

File tree

6 files changed

+320
-6
lines changed

6 files changed

+320
-6
lines changed

src/main/java/io/kaoto/camelcatalog/generators/CamelCatalogSchemaEnhancer.java

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,97 @@ private void populateJavaTypeToModelNameMap() {
370370
});
371371
}
372372

373+
/**
374+
* Enhance the parameters property in the schema for specific Camel model types.
375+
* <p>
376+
* This method adds or updates the "parameters" property in JSON schemas for endpoint-related
377+
* definitions (e.g., from, to, kamelet). The parameters property provides metadata about
378+
* endpoint configuration options that can be dynamically set at runtime.
379+
* <p>
380+
*
381+
* @param javaType the fully qualified Java type of the Camel model (e.g., "org.apache.camel.model.ToDefinition")
382+
* @param schema the JSON schema node to enhance
383+
*/
384+
public void enhanceParametersProperty(String javaType, ObjectNode schema) {
385+
if (javaType == null) {
386+
return;
387+
}
388+
389+
// Define the set of Camel model types that support dynamic endpoint parameters
390+
// These are typically endpoint-related definitions that accept URI parameters
391+
Set<String> allowedJavaTypes = Set.of(
392+
"org.apache.camel.dsl.yaml.deserializers.OutputAwareFromDefinition",
393+
"org.apache.camel.dsl.yaml.deserializers.RouteFromDefinitionDeserializer",
394+
"org.apache.camel.model.FromDefinition",
395+
"org.apache.camel.model.KameletDefinition",
396+
"org.apache.camel.model.PollDefinition",
397+
"org.apache.camel.model.SagaActionUriDefinition",
398+
"org.apache.camel.model.ToDefinition",
399+
"org.apache.camel.model.ToDynamicDefinition",
400+
"org.apache.camel.model.WireTapDefinition"
401+
);
402+
403+
boolean isInAllowedList = allowedJavaTypes.contains(javaType);
404+
405+
// Handle schemas with oneOf (multiple possible schema variants)
406+
// Each variant needs to be enhanced independently
407+
if (schema.has("oneOf")) {
408+
ArrayNode oneOfArray = (ArrayNode) schema.get("oneOf");
409+
oneOfArray.forEach(option -> {
410+
if (option.isObject()) {
411+
enhanceParametersInNode((ObjectNode) option, isInAllowedList);
412+
}
413+
});
414+
} else {
415+
// Handle simple schema without oneOf
416+
enhanceParametersInNode(schema, isInAllowedList);
417+
}
418+
}
419+
420+
/**
421+
* Enhance or create the parameters property within a schema node.
422+
* <p>
423+
* This method handles the actual enhancement of the "parameters" property:
424+
* - For allowed types without parameters: creates a new parameters object
425+
* - For nodes with existing parameters: updates the parameters metadata
426+
* <p>
427+
*
428+
* @param node the JSON schema node to enhance
429+
* @param isInAllowedList whether this node's type is in the allowed list for parameter enhancement
430+
*/
431+
private void enhanceParametersInNode(ObjectNode node, boolean isInAllowedList) {
432+
if (!node.has("properties")) {
433+
return;
434+
}
435+
436+
ObjectNode properties = node.withObject("/properties");
437+
438+
if (isInAllowedList && !properties.has("parameters")) {
439+
ObjectNode parameters = jsonMapper.createObjectNode();
440+
properties.set("parameters", parameters);
441+
setParametersMetadata(parameters);
442+
}
443+
else if (properties.has("parameters")) {
444+
ObjectNode parameters = (ObjectNode) properties.get("parameters");
445+
setParametersMetadata(parameters);
446+
}
447+
}
448+
449+
/**
450+
* Set the standard metadata for an endpoint properties parameters object.
451+
* <p>
452+
* Sets only the essential metadata fields. The 'properties' and 'required' fields
453+
* are intentionally omitted here as they should be populated dynamically based on
454+
* the actual endpoint component being used.
455+
*
456+
* @param parameters the parameters object node to configure
457+
*/
458+
private void setParametersMetadata(ObjectNode parameters) {
459+
parameters.put("type", "object");
460+
parameters.put("title", "Endpoint Properties");
461+
parameters.put("description", "Endpoint properties description");
462+
}
463+
373464
private void addGroupInfo(BaseOptionModel modelOption, ObjectNode propertyNode) {
374465
String group =
375466
modelOption.getGroup() != null ? modelOption.getGroup() : modelOption.getLabel();

src/main/java/io/kaoto/camelcatalog/generators/EIPGenerator.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,11 @@ private void enhanceJSONSchema(String processorName, ObjectNode processorJSONSch
112112
camelCatalogSchemaEnhancer.sortPropertiesAccordingToCatalog(processorName, processorJSONSchema);
113113
camelCatalogSchemaEnhancer.fillPropertiesInformation(processorName, processorJSONSchema);
114114
camelCatalogSchemaEnhancer.fillModelFormatInOneOf(processorJSONSchema);
115+
116+
EipModel mainModel = camelCatalog.eipModel(processorName);
117+
if (mainModel != null) {
118+
camelCatalogSchemaEnhancer.enhanceParametersProperty(mainModel.getJavaType(), processorJSONSchema);
119+
}
115120

116121
if (processorJSONSchema.has("definitions")) {
117122
iterateOverDefinitions(processorJSONSchema.withObject("definitions"), (model, node) -> {
@@ -123,6 +128,7 @@ private void enhanceJSONSchema(String processorName, ObjectNode processorJSONSch
123128
camelCatalogSchemaEnhancer.sortPropertiesAccordingToCatalog(model, node);
124129
camelCatalogSchemaEnhancer.fillPropertiesInformation(model, node);
125130
camelCatalogSchemaEnhancer.fillModelFormatInOneOf(node);
131+
camelCatalogSchemaEnhancer.enhanceParametersProperty(model.getJavaType(), node);
126132
});
127133
}
128134
}

src/main/java/io/kaoto/camelcatalog/generators/EntityGenerator.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,11 @@ private void enhanceJSONSchema(String processorName, ObjectNode processorJSONSch
113113
camelCatalogSchemaEnhancer.fillPropertiesInformation(processorName, processorJSONSchema);
114114
camelCatalogSchemaEnhancer.fillModelFormatInOneOf(processorJSONSchema);
115115

116+
var mainModel = camelCatalog.model(Kind.model, processorName);
117+
if (mainModel != null) {
118+
camelCatalogSchemaEnhancer.enhanceParametersProperty(mainModel.getJavaType(), processorJSONSchema);
119+
}
120+
116121
if (processorJSONSchema.has("definitions")) {
117122
iterateOverDefinitions(processorJSONSchema.withObject("definitions"), (model, node) -> {
118123
if (model == null) {
@@ -123,6 +128,7 @@ private void enhanceJSONSchema(String processorName, ObjectNode processorJSONSch
123128
camelCatalogSchemaEnhancer.sortPropertiesAccordingToCatalog(model, node);
124129
camelCatalogSchemaEnhancer.fillPropertiesInformation(model, node);
125130
camelCatalogSchemaEnhancer.fillModelFormatInOneOf(node);
131+
camelCatalogSchemaEnhancer.enhanceParametersProperty(model.getJavaType(), node);
126132
});
127133
}
128134
}

src/main/java/io/kaoto/camelcatalog/generators/SchemaPropertyFilter.java

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,16 @@
1111
public class SchemaPropertyFilter {
1212

1313
private final Map<String, List<String>> processorPropertyBlockList;
14-
14+
/**
15+
* Deleted properties from the schema so that in the construction
16+
* of the forms they don't influence the creation of container
17+
* i.e. adding an extra component or extra container named steps
18+
* as steps include inside the components and EIPs
19+
*/
1520
public SchemaPropertyFilter() {
1621
this.processorPropertyBlockList = Map.ofEntries(
1722
Map.entry("choice", List.of("when", "otherwise")),
1823
Map.entry("doTry", List.of("doCatch", "doFinally", "steps")),
19-
Map.entry("to", List.of("parameters")),
20-
Map.entry("toD", List.of("parameters")),
21-
Map.entry("wireTap", List.of("parameters")),
2224
Map.entry("when", List.of("steps")),
2325
Map.entry("otherwise", List.of("steps")),
2426
Map.entry("doCatch", List.of("steps")),
@@ -49,7 +51,7 @@ public SchemaPropertyFilter() {
4951
Map.entry("onCompletion", List.of("steps")),
5052
Map.entry("onException", List.of("steps"))
5153
);
52-
}
54+
}
5355

5456
void schemaPropertyFilter(String eipName, ObjectNode node) {
5557
if (!processorPropertyBlockList.containsKey(eipName)) return;
@@ -75,7 +77,7 @@ void filterProperties(String eipName, ObjectNode node) {
7577
if (node.has("properties")) {
7678
var properties = (ObjectNode) node.get("properties");
7779
Set<String> propToRemove = new HashSet<>();
78-
properties.fields().forEachRemaining(entry -> {
80+
properties.properties().forEach(entry -> {
7981
if (processorPropertyBlockList.get(eipName).contains(entry.getKey())) {
8082
propToRemove.add(entry.getKey());
8183
}

src/test/java/io/kaoto/camelcatalog/generators/CamelCatalogSchemaEnhancerTest.java

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -628,4 +628,104 @@ void shouldNotModifyPropertiesWithoutType() {
628628
var defaultValue = propertyNode.get("default");
629629
assertTrue(defaultValue.isTextual());
630630
}
631+
632+
@Test
633+
void shouldEnhanceParametersPropertyForAllowedJavaType() {
634+
ObjectMapper jsonMapper = new ObjectMapper();
635+
ObjectNode schema = jsonMapper.createObjectNode();
636+
ObjectNode properties = jsonMapper.createObjectNode();
637+
schema.set("properties", properties);
638+
639+
camelCatalogSchemaEnhancer.enhanceParametersProperty("org.apache.camel.model.ToDefinition", schema);
640+
641+
assertTrue(properties.has("parameters"));
642+
ObjectNode parameters = (ObjectNode) properties.get("parameters");
643+
assertEquals("object", parameters.get("type").asText());
644+
assertEquals("Endpoint Properties", parameters.get("title").asText());
645+
assertEquals("Endpoint properties description", parameters.get("description").asText());
646+
}
647+
648+
@Test
649+
void shouldNotCreateParametersPropertyForNonAllowedJavaTypeWithoutExistingParameters() {
650+
ObjectMapper jsonMapper = new ObjectMapper();
651+
ObjectNode schema = jsonMapper.createObjectNode();
652+
ObjectNode properties = jsonMapper.createObjectNode();
653+
schema.set("properties", properties);
654+
655+
camelCatalogSchemaEnhancer.enhanceParametersProperty("org.apache.camel.model.ChoiceDefinition", schema);
656+
657+
assertFalse(properties.has("parameters"));
658+
}
659+
660+
@Test
661+
void shouldEnhanceExistingParametersForNonAllowedJavaType() {
662+
ObjectMapper jsonMapper = new ObjectMapper();
663+
ObjectNode schema = jsonMapper.createObjectNode();
664+
ObjectNode properties = jsonMapper.createObjectNode();
665+
ObjectNode existingParameters = jsonMapper.createObjectNode();
666+
existingParameters.put("existingField", "value");
667+
properties.set("parameters", existingParameters);
668+
schema.set("properties", properties);
669+
670+
camelCatalogSchemaEnhancer.enhanceParametersProperty("org.apache.camel.model.ChoiceDefinition", schema);
671+
672+
ObjectNode parameters = (ObjectNode) properties.get("parameters");
673+
assertEquals("object", parameters.get("type").asText());
674+
assertEquals("Endpoint Properties", parameters.get("title").asText());
675+
assertEquals("Endpoint properties description", parameters.get("description").asText());
676+
assertTrue(parameters.has("existingField"));
677+
}
678+
679+
@Test
680+
void shouldNotEnhanceParametersPropertyForNullJavaType() {
681+
ObjectMapper jsonMapper = new ObjectMapper();
682+
ObjectNode schema = jsonMapper.createObjectNode();
683+
ObjectNode properties = jsonMapper.createObjectNode();
684+
schema.set("properties", properties);
685+
686+
camelCatalogSchemaEnhancer.enhanceParametersProperty(null, schema);
687+
688+
assertFalse(properties.has("parameters"));
689+
}
690+
691+
@Test
692+
void shouldEnhanceParametersPropertyWithOneOf() {
693+
ObjectMapper jsonMapper = new ObjectMapper();
694+
ObjectNode schema = jsonMapper.createObjectNode();
695+
var oneOfArray = schema.putArray("oneOf");
696+
697+
ObjectNode option1 = jsonMapper.createObjectNode();
698+
ObjectNode properties1 = jsonMapper.createObjectNode();
699+
option1.set("properties", properties1);
700+
oneOfArray.add(option1);
701+
702+
ObjectNode option2 = jsonMapper.createObjectNode();
703+
ObjectNode properties2 = jsonMapper.createObjectNode();
704+
option2.set("properties", properties2);
705+
oneOfArray.add(option2);
706+
707+
camelCatalogSchemaEnhancer.enhanceParametersProperty("org.apache.camel.model.ToDefinition", schema);
708+
709+
assertTrue(properties1.has("parameters"));
710+
assertTrue(properties2.has("parameters"));
711+
}
712+
713+
@Test
714+
void shouldUpdateExistingParametersMetadata() {
715+
ObjectMapper jsonMapper = new ObjectMapper();
716+
ObjectNode schema = jsonMapper.createObjectNode();
717+
ObjectNode properties = jsonMapper.createObjectNode();
718+
ObjectNode existingParameters = jsonMapper.createObjectNode();
719+
existingParameters.put("existingField", "value");
720+
properties.set("parameters", existingParameters);
721+
schema.set("properties", properties);
722+
723+
camelCatalogSchemaEnhancer.enhanceParametersProperty("org.apache.camel.model.ToDefinition", schema);
724+
725+
ObjectNode parameters = (ObjectNode) properties.get("parameters");
726+
assertEquals("object", parameters.get("type").asText());
727+
assertEquals("Endpoint Properties", parameters.get("title").asText());
728+
assertEquals("Endpoint properties description", parameters.get("description").asText());
729+
assertTrue(parameters.has("existingField"));
730+
}
631731
}
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
/*
2+
* Copyright (C) 2025 Red Hat, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package io.kaoto.camelcatalog.generators;
17+
18+
import com.fasterxml.jackson.databind.ObjectMapper;
19+
import com.fasterxml.jackson.databind.node.ArrayNode;
20+
import com.fasterxml.jackson.databind.node.ObjectNode;
21+
import org.junit.jupiter.api.BeforeEach;
22+
import org.junit.jupiter.api.Test;
23+
24+
import static org.junit.jupiter.api.Assertions.*;
25+
26+
class SchemaPropertyFilterTest {
27+
private SchemaPropertyFilter schemaPropertyFilter;
28+
private ObjectMapper objectMapper;
29+
30+
@BeforeEach
31+
void setUp() {
32+
schemaPropertyFilter = new SchemaPropertyFilter();
33+
objectMapper = new ObjectMapper();
34+
}
35+
36+
@Test
37+
void shouldFilterMultipleBlocklistedProperties() {
38+
ObjectNode node = createNodeWithProperties("when", "otherwise", "id", "description");
39+
40+
schemaPropertyFilter.schemaPropertyFilter("choice", node);
41+
42+
ObjectNode properties = (ObjectNode) node.get("properties");
43+
assertFalse(properties.has("when"), "Blocklisted property 'when' should be filtered");
44+
assertFalse(properties.has("otherwise"), "Blocklisted property 'otherwise' should be filtered");
45+
assertTrue(properties.has("id"), "Non-blocklisted property should remain");
46+
assertTrue(properties.has("description"), "Non-blocklisted property should remain");
47+
}
48+
49+
@Test
50+
void shouldNotFilterWhenEipNotInBlocklist() {
51+
ObjectNode node = createNodeWithProperties("steps", "when", "otherwise", "id");
52+
53+
schemaPropertyFilter.schemaPropertyFilter("unknownEip", node);
54+
55+
ObjectNode properties = (ObjectNode) node.get("properties");
56+
assertTrue(properties.has("steps"), "All properties should remain when EIP not in blocklist");
57+
assertTrue(properties.has("when"), "All properties should remain when EIP not in blocklist");
58+
assertTrue(properties.has("otherwise"), "All properties should remain when EIP not in blocklist");
59+
assertTrue(properties.has("id"), "All properties should remain when EIP not in blocklist");
60+
}
61+
62+
@Test
63+
void shouldFilterPropertiesInOneOfArray() {
64+
ObjectNode node = objectMapper.createObjectNode();
65+
ArrayNode oneOfArray = node.putArray("oneOf");
66+
67+
ObjectNode option = objectMapper.createObjectNode();
68+
ObjectNode properties = option.putObject("properties");
69+
properties.putObject("steps");
70+
properties.putObject("id");
71+
oneOfArray.add(option);
72+
73+
schemaPropertyFilter.schemaPropertyFilter("filter", node);
74+
75+
ObjectNode resultProperties = (ObjectNode) oneOfArray.get(0).get("properties");
76+
assertFalse(resultProperties.has("steps"), "Blocklisted property should be filtered in oneOf");
77+
assertTrue(resultProperties.has("id"), "Non-blocklisted property should remain in oneOf");
78+
}
79+
80+
@Test
81+
void shouldFilterPropertiesInAnyOfArray() {
82+
ObjectNode node = objectMapper.createObjectNode();
83+
ArrayNode anyOfArray = node.putArray("anyOf");
84+
85+
ObjectNode option = objectMapper.createObjectNode();
86+
ObjectNode properties = option.putObject("properties");
87+
properties.putObject("steps");
88+
properties.putObject("id");
89+
anyOfArray.add(option);
90+
91+
schemaPropertyFilter.schemaPropertyFilter("filter", node);
92+
93+
ObjectNode resultProperties = (ObjectNode) anyOfArray.get(0).get("properties");
94+
assertFalse(resultProperties.has("steps"), "Blocklisted property should be filtered in anyOf");
95+
assertTrue(resultProperties.has("id"), "Non-blocklisted property should remain in anyOf");
96+
}
97+
98+
/**
99+
* Helper method to create a node with properties
100+
*/
101+
private ObjectNode createNodeWithProperties(String... propertyNames) {
102+
ObjectNode node = objectMapper.createObjectNode();
103+
ObjectNode properties = node.putObject("properties");
104+
for (String propertyName : propertyNames) {
105+
properties.putObject(propertyName);
106+
}
107+
return node;
108+
}
109+
}

0 commit comments

Comments
 (0)