Skip to content

Commit

Permalink
Temp: Add new EIPGenerator
Browse files Browse the repository at this point in the history
  • Loading branch information
lordrip committed Jan 15, 2025
1 parent 6da8b04 commit 716877e
Show file tree
Hide file tree
Showing 6 changed files with 2,155 additions and 0 deletions.
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);
}
});
}
}
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);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package io.kaoto.camelcatalog.generators;

public interface Generator {

}
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"));
}
}
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"));
}
}
Loading

0 comments on commit 716877e

Please sign in to comment.