-
Notifications
You must be signed in to change notification settings - Fork 29
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
2,155 additions
and
0 deletions.
There are no files selected for viewing
178 changes: 178 additions & 0 deletions
178
...talog-generator/src/main/java/io/kaoto/camelcatalog/generators/CamelYAMLSchemaReader.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,178 @@ | ||
package io.kaoto.camelcatalog.generators; | ||
|
||
import com.fasterxml.jackson.core.JsonProcessingException; | ||
import com.fasterxml.jackson.databind.ObjectMapper; | ||
import com.fasterxml.jackson.databind.node.ObjectNode; | ||
|
||
public class CamelYAMLSchemaReader { | ||
|
||
ObjectMapper jsonMapper = new ObjectMapper(); | ||
ObjectNode camelYamlSchemaNode; | ||
|
||
public CamelYAMLSchemaReader(ObjectNode camelYamlSchemaNode) throws JsonProcessingException { | ||
this.camelYamlSchemaNode = camelYamlSchemaNode; | ||
} | ||
|
||
/** | ||
* Get the JSON schema for a given EIP | ||
* The Camel YAML DSL schema is a JSON schema that describes the structure of the | ||
* Camel YAML DSL. | ||
* The steps are: | ||
* 1. Get the JSON schema for a given EIP from the Camel catalog | ||
* 2. Resolve the initial $ref | ||
* 3. Evaluate all fields recursively and inline all the required definitions | ||
* | ||
* @param eipName the name of the EIP to get the JSON schema for | ||
* @return the JSON schema for a given EIP, with the initial $ref resolved and all the required definitions inlined | ||
*/ | ||
public ObjectNode getJSONSchema(String eipName) { | ||
var eipNodeRef = (ObjectNode) camelYamlSchemaNode.get("items") | ||
.get("definitions") | ||
.get("org.apache.camel.model.ProcessorDefinition") | ||
.get("properties") | ||
.get(eipName); | ||
if (eipNodeRef.isNull() || !eipNodeRef.has("$ref")) { | ||
return null; | ||
} | ||
|
||
var eipSchemaNode = jsonMapper.createObjectNode(); | ||
|
||
|
||
|
||
|
||
// TODO: Bring the definitions from the Camel YAML DSL schema | ||
// See: io.kaoto.camelcatalog.generator.CamelYamlDslSchemaProcessor.relocateToRootDefinitions | ||
|
||
|
||
|
||
|
||
|
||
// var resolvedNode = getResolvedNode(eipNodeRef); | ||
// eipSchemaNode.setAll(resolvedNode); | ||
// | ||
// setRequiredPropertyIfNeeded(eipSchemaNode); | ||
// extractSingleOneOfFromAnyOf(eipSchemaNode); | ||
// inlineDefinitions(eipSchemaNode); | ||
|
||
|
||
return eipSchemaNode; | ||
} | ||
|
||
|
||
|
||
/** | ||
* Resolve the initial $ref | ||
* Given a node, resolve the initial $ref and return the resolved node | ||
* | ||
* @param node the node to resolve the initial $ref | ||
* @return the resolved node | ||
*/ | ||
ObjectNode getResolvedNode(ObjectNode node) { | ||
if (node.has("$ref")) { | ||
String ref = node.get("$ref").asText(); | ||
String[] refPath = ref.split("/"); | ||
|
||
ObjectNode currentNode = camelYamlSchemaNode; | ||
for (String path : refPath) { | ||
if (path.equals("#")) { | ||
currentNode = camelYamlSchemaNode; | ||
} else if (!path.isEmpty()) { | ||
currentNode = (ObjectNode) currentNode.get(path); | ||
} | ||
} | ||
|
||
return currentNode; | ||
} | ||
|
||
return node; | ||
} | ||
|
||
/** | ||
* Initialize the required property if it does not exist | ||
* | ||
* @param node the node to set the required property if it does not exist | ||
*/ | ||
void setRequiredPropertyIfNeeded(ObjectNode node) { | ||
if (!node.has("required")) { | ||
node.set("required", jsonMapper.createArrayNode()); | ||
} | ||
} | ||
|
||
/** | ||
* Extract single OneOf definition from AnyOf definition and put it into the | ||
* root definitions. | ||
* It's a workaround for the current Camel YAML DSL JSON schema, where some | ||
* AnyOf definition | ||
* contains only one OneOf definition. | ||
* This is done mostly for the errorHandler definition, f.i. | ||
* ``` | ||
* { | ||
* anyOf: [ | ||
* { | ||
* oneOf: [ | ||
* { type: "object", ... }, | ||
* { type: "object", ... }, | ||
* ] | ||
* }, | ||
* ] | ||
* } | ||
* ``` | ||
* will be transformed into | ||
* ``` | ||
* { | ||
* oneOf: [ | ||
* { type: "object", ... }, | ||
* { type: "object", ... }, | ||
* ] | ||
* } | ||
*/ | ||
private void extractSingleOneOfFromAnyOf(ObjectNode node) { | ||
if (!node.has("anyOf")) { | ||
return; | ||
} | ||
var anyOfArray = node.withArray("/anyOf"); | ||
if (anyOfArray.size() != 1) { | ||
return; | ||
} | ||
|
||
var anyOfOneOf = anyOfArray.get(0).withArray("/oneOf"); | ||
node.set("oneOf", anyOfOneOf); | ||
node.remove("anyOf"); | ||
} | ||
|
||
/** | ||
* Inline all the definitions | ||
* Given a node, inline the required definitions from the Camel YAML DSL schema if needed. | ||
* For instance, when a property has a $ref | ||
* { | ||
* "property1": { | ||
* "$ref": "#/definitions/UserDefinition" | ||
* } | ||
* } | ||
* will be transformed into | ||
* { | ||
* "user": { | ||
* "type": "object", | ||
* "properties": { | ||
* "name": { | ||
* "type": "string" | ||
* } | ||
* | ||
* @param node the node to inline the required definitions from the Camel YAML DSL schema | ||
*/ | ||
void inlineDefinitions(ObjectNode node) { | ||
if (!node.has("properties")) { | ||
return; | ||
} | ||
|
||
var properties = (ObjectNode) node.get("properties"); | ||
properties.fields().forEachRemaining(entry -> { | ||
var property = (ObjectNode) entry.getValue(); | ||
if (property.has("$ref")) { | ||
var resolvedNode = getResolvedNode(property); | ||
property.setAll(resolvedNode); | ||
inlineDefinitions(property); | ||
} | ||
}); | ||
} | ||
} |
68 changes: 68 additions & 0 deletions
68
packages/catalog-generator/src/main/java/io/kaoto/camelcatalog/generators/EIPGenerator.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
package io.kaoto.camelcatalog.generators; | ||
|
||
import com.fasterxml.jackson.core.JsonProcessingException; | ||
import com.fasterxml.jackson.databind.ObjectMapper; | ||
import com.fasterxml.jackson.databind.SerializationFeature; | ||
import com.fasterxml.jackson.databind.node.ObjectNode; | ||
import org.apache.camel.catalog.CamelCatalog; | ||
|
||
import java.util.ArrayList; | ||
import java.util.HashMap; | ||
import java.util.List; | ||
import java.util.Map; | ||
|
||
public class EIPGenerator { | ||
CamelCatalog camelCatalog; | ||
String camelYamlSchema; | ||
ObjectMapper jsonMapper = new ObjectMapper() | ||
.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true); | ||
ObjectNode camelYamlSchemaNode; | ||
CamelYAMLSchemaReader camelYAMLSchemaReader; | ||
|
||
public EIPGenerator(CamelCatalog camelCatalog, String camelYamlSchema) throws JsonProcessingException { | ||
this.camelCatalog = camelCatalog; | ||
this.camelYamlSchema = camelYamlSchema; | ||
this.camelYamlSchemaNode = (ObjectNode) jsonMapper.readTree(camelYamlSchema); | ||
this.camelYAMLSchemaReader = new CamelYAMLSchemaReader(camelYamlSchemaNode); | ||
} | ||
|
||
public Map<String, ObjectNode> generate() { | ||
Map<String, ObjectNode> eipMap = new HashMap<>(); | ||
|
||
getEIPNames().forEach(eipName -> { | ||
var eipJSON = getEIPJson(eipName); | ||
var eipJSONSchema = camelYAMLSchemaReader.getJSONSchema(eipName); | ||
|
||
eipJSON.set("propertiesSchema", eipJSONSchema); | ||
|
||
eipMap.put(eipName, eipJSON); | ||
}); | ||
|
||
return eipMap; | ||
} | ||
|
||
List<String> getEIPNames() { | ||
var iterator = this.camelYamlSchemaNode.get("items").get("definitions") | ||
.get("org.apache.camel.model.ProcessorDefinition") | ||
.get("properties") | ||
.fields(); | ||
|
||
List<String> eipNames = new ArrayList<>(); | ||
while (iterator.hasNext()) { | ||
var entry = iterator.next(); | ||
eipNames.add(entry.getKey()); | ||
} | ||
|
||
return eipNames; | ||
} | ||
|
||
ObjectNode getEIPJson(String eipName) { | ||
String eipJson = camelCatalog.modelJSonSchema(eipName); | ||
|
||
try { | ||
return (ObjectNode) jsonMapper.readTree(eipJson); | ||
} catch (JsonProcessingException e) { | ||
throw new RuntimeException(String.format("Cannot load %s JSON model", eipName), e); | ||
} | ||
} | ||
} |
5 changes: 5 additions & 0 deletions
5
packages/catalog-generator/src/main/java/io/kaoto/camelcatalog/generators/Generator.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
package io.kaoto.camelcatalog.generators; | ||
|
||
public interface Generator { | ||
|
||
} |
63 changes: 63 additions & 0 deletions
63
...g-generator/src/test/java/io/kaoto/camelcatalog/generators/CamelYAMLSchemaReaderTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
package io.kaoto.camelcatalog.generators; | ||
|
||
import com.fasterxml.jackson.databind.ObjectMapper; | ||
import com.fasterxml.jackson.databind.SerializationFeature; | ||
import com.fasterxml.jackson.databind.node.ObjectNode; | ||
import org.apache.camel.dsl.yaml.YamlRoutesBuilderLoader; | ||
import org.junit.jupiter.api.BeforeEach; | ||
import org.junit.jupiter.api.Test; | ||
|
||
import java.io.IOException; | ||
import java.nio.charset.StandardCharsets; | ||
|
||
import static org.junit.jupiter.api.Assertions.assertNotNull; | ||
import static org.junit.jupiter.api.Assertions.assertTrue; | ||
|
||
class CamelYAMLSchemaReaderTest { | ||
CamelYAMLSchemaReader camelYAMLSchemaReader; | ||
|
||
@BeforeEach | ||
void setUp() throws IOException { | ||
ObjectMapper jsonMapper = new ObjectMapper() | ||
.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true); | ||
|
||
ObjectNode camelYamlSchemaNode; | ||
try (var is = YamlRoutesBuilderLoader.class.getClassLoader().getResourceAsStream("schema/camelYamlDsl.json")) { | ||
assert is != null; | ||
var camelYamlSchema = new String(is.readAllBytes(), StandardCharsets.UTF_8); | ||
camelYamlSchemaNode = (ObjectNode) jsonMapper.readTree(camelYamlSchema); | ||
} | ||
|
||
camelYAMLSchemaReader = new CamelYAMLSchemaReader(camelYamlSchemaNode); | ||
|
||
var resequenceSchemaNode = (ObjectNode) jsonMapper.readTree( | ||
getClass().getClassLoader().getResourceAsStream("camel-4.9.0-resequence-schema.json")); | ||
} | ||
|
||
@Test | ||
void shouldReturnJSONSchemaForEIP() { | ||
var eipName = "resequence"; | ||
var eipSchema = camelYAMLSchemaReader.getJSONSchema(eipName); | ||
|
||
assertNotNull(eipSchema); | ||
assertTrue(eipSchema.has("type")); | ||
assertTrue(eipSchema.has("properties")); | ||
assertTrue(eipSchema.has("required")); | ||
} | ||
|
||
@Test | ||
void shouldInlineDefinitions() { | ||
var eipName = "resequence"; | ||
var eipSchema = camelYAMLSchemaReader.getJSONSchema(eipName); | ||
|
||
assertTrue(eipSchema.has("items")); | ||
var itemsNode = (ObjectNode) eipSchema.get("items"); | ||
|
||
assertTrue(itemsNode.has("definitions")); | ||
var definitionsNode = (ObjectNode) itemsNode.get("definitions"); | ||
|
||
assertTrue(definitionsNode.has("org.apache.camel.model.config.BatchResequencerConfig")); | ||
assertTrue(definitionsNode.has("org.apache.camel.model.config.StreamResequencerConfig")); | ||
assertTrue(definitionsNode.has("org.apache.camel.model.language.ExpressionDefinition")); | ||
} | ||
} |
47 changes: 47 additions & 0 deletions
47
...es/catalog-generator/src/test/java/io/kaoto/camelcatalog/generators/EIPGeneratorTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
package io.kaoto.camelcatalog.generators; | ||
|
||
import org.apache.camel.catalog.CamelCatalog; | ||
import org.apache.camel.catalog.DefaultCamelCatalog; | ||
import org.apache.camel.dsl.yaml.YamlRoutesBuilderLoader; | ||
import org.junit.jupiter.api.BeforeEach; | ||
import org.junit.jupiter.api.Test; | ||
|
||
import java.io.IOException; | ||
import java.nio.charset.StandardCharsets; | ||
|
||
import static org.junit.jupiter.api.Assertions.assertTrue; | ||
|
||
class EIPGeneratorTest { | ||
EIPGenerator eipGenerator; | ||
|
||
@BeforeEach | ||
void setUp() throws IOException { | ||
CamelCatalog camelCatalog = new DefaultCamelCatalog(); | ||
String camelYamlSchema; | ||
try (var is = YamlRoutesBuilderLoader.class.getClassLoader().getResourceAsStream("schema/camelYamlDsl.json")) { | ||
assert is != null; | ||
camelYamlSchema = new String(is.readAllBytes(), StandardCharsets.UTF_8); | ||
} | ||
|
||
eipGenerator = new EIPGenerator(camelCatalog, camelYamlSchema); | ||
} | ||
|
||
@Test | ||
void shouldContainAListOfEips() { | ||
var eipsMap = eipGenerator.generate(); | ||
|
||
assertTrue(eipsMap.containsKey("aggregate")); | ||
assertTrue(eipsMap.containsKey("to")); | ||
assertTrue(eipsMap.containsKey("setHeader")); | ||
assertTrue(eipsMap.containsKey("setHeaders")); | ||
|
||
assertTrue(eipsMap.containsKey("choice")); | ||
assertTrue(eipsMap.containsKey("doTry")); | ||
|
||
/* These are special cases, while they are processors, they cannot be used directly */ | ||
assertTrue(eipsMap.containsKey("when")); | ||
assertTrue(eipsMap.containsKey("otherwise")); | ||
assertTrue(eipsMap.containsKey("doCatch")); | ||
assertTrue(eipsMap.containsKey("doFinally")); | ||
} | ||
} |
Oops, something went wrong.