Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Jenkinsfile.talend
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@ org.apache.camel:camel-drill,\
org.apache.camel:camel-langchain4j-core,\
org.apache.camel:camel-langchain4j-chat,\
org.apache.camel:camel-langchain4j-tools,\
org.apache.camel:camel-neo4j,\
org.apache.camel:camel-salesforce,\
org.apache.camel:camel-spring,\
org.apache.camel:camel-tika,\
org.apache.camel:camel-componentdsl,\
org.apache.camel:camel-endpointdsl\
'''
Expand Down
2 changes: 2 additions & 0 deletions components/camel-ai/camel-neo4j/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,10 @@
<packaging>jar</packaging>
<name>Camel :: AI :: Neo4j</name>
<description>Camel Neo4j support</description>
<version>${revision}</version>

<properties>
<revision>${camel-neo4j.tesb.version}</revision>
<camel.surefire.parallel>true</camel.surefire.parallel>
<camel.surefire.parallel.factor>4</camel.surefire.parallel.factor>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
import java.util.UUID;
import java.util.stream.Collectors;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.camel.Exchange;
import org.apache.camel.InvalidPayloadException;
import org.apache.camel.Message;
Expand Down Expand Up @@ -50,6 +52,10 @@

public class Neo4jProducer extends DefaultProducer {

private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
private static final TypeReference<Map<String, Object>> MAP_TYPE_REF = new TypeReference<>() {
};

private Driver driver;

public Neo4jProducer(Neo4jEndpoint endpoint) {
Expand Down Expand Up @@ -104,15 +110,24 @@ private void createNode(Exchange exchange) throws InvalidPayloadException {

final String databaseName = getEndpoint().getName();

var query = "";
Map<String, Object> properties = null;
// Always use parameterized queries to prevent Cypher injection
var query = String.format("CREATE (%s:%s $props)", alias, label);
Map<String, Object> properties;

if (body instanceof String) {
// Case we get the object in a Json format
query = String.format("CREATE (%s:%s %s)", alias, label, body);
try {
// Convert JSON string to Map for parameterized query
Map<String, Object> bodyMap = OBJECT_MAPPER.readValue((String) body, MAP_TYPE_REF);
properties = Map.of("props", bodyMap);
} catch (Exception e) {
exchange.setException(
new Neo4jOperationException(
Neo4Operation.CREATE_NODE,
new IllegalArgumentException("Failed to parse body as JSON: " + body, e)));
return;
}
} else {
// body should be a list of properties
query = String.format("CREATE (%s:%s $props)", alias, label);
// body should be a Map or similar object
properties = Map.of("props", body);
}

Expand All @@ -126,17 +141,55 @@ private void retrieveNodes(Exchange exchange) throws NoSuchHeaderException {
final String alias = getEndpoint().getConfiguration().getAlias();
ObjectHelper.notNull(alias, "alias");

String matchQuery = exchange.getMessage().getHeader(MATCH_PROPERTIES, String.class);
// in this case we search all nodes
if (matchQuery == null) {
matchQuery = "";
}
String matchProperties = exchange.getMessage().getHeader(MATCH_PROPERTIES, String.class);

final String databaseName = getEndpoint().getName();

var query = String.format("MATCH (%s:%s %s) RETURN %s", alias, label, matchQuery, alias);
String query;
Map<String, Object> queryParams = null;

if (matchProperties == null || matchProperties.isEmpty()) {
// Search all nodes
query = String.format("MATCH (%s:%s) RETURN %s", alias, label, alias);
} else {
try {
// Convert JSON string to Map and build WHERE clause with parameters
Map<String, Object> matchMap = OBJECT_MAPPER.readValue(matchProperties, MAP_TYPE_REF);

if (!matchMap.isEmpty()) {
StringBuilder whereClause = new StringBuilder();
queryParams = new java.util.HashMap<>();
int paramIndex = 0;

for (Map.Entry<String, Object> entry : matchMap.entrySet()) {
if (paramIndex > 0) {
whereClause.append(" AND ");
}
String paramName = "param" + paramIndex;
whereClause.append(alias).append(".").append(entry.getKey())
.append(" = $").append(paramName);
queryParams.put(paramName, entry.getValue());
paramIndex++;
}

query = String.format("MATCH (%s:%s) WHERE %s RETURN %s",
alias, label, whereClause.toString(), alias);
} else {
// Empty map, match all nodes
query = String.format("MATCH (%s:%s) RETURN %s", alias, label, alias);
}
} catch (Exception e) {
exchange.setException(
new Neo4jOperationException(
RETRIEVE_NODES,
new IllegalArgumentException(
"Failed to parse MATCH_PROPERTIES as JSON: " + matchProperties,
e)));
return;
}
}

queryRetriveNodes(exchange, databaseName, null, query, RETRIEVE_NODES);
queryRetriveNodes(exchange, databaseName, queryParams, query, RETRIEVE_NODES);
}

private void retrieveNodesWithCypherQuery(Exchange exchange) throws NoSuchHeaderException {
Expand Down Expand Up @@ -184,19 +237,57 @@ private void deleteNode(Exchange exchange) throws NoSuchHeaderException {
final String alias = getEndpoint().getConfiguration().getAlias();
ObjectHelper.notNull(alias, "alias");

String matchQuery = exchange.getMessage().getHeader(MATCH_PROPERTIES, String.class);
// in this case we search all nodes
if (matchQuery == null) {
matchQuery = "";
}
String matchProperties = exchange.getMessage().getHeader(MATCH_PROPERTIES, String.class);

final String databaseName = getEndpoint().getName();

final String detached = getEndpoint().getConfiguration().isDetachRelationship() ? "DETACH" : "";

var query = String.format("MATCH (%s:%s %s) %s DELETE %s", alias, label, matchQuery, detached, alias);
String query;
Map<String, Object> queryParams = null;

if (matchProperties == null || matchProperties.isEmpty()) {
// Delete all nodes of this label
query = String.format("MATCH (%s:%s) %s DELETE %s", alias, label, detached, alias);
} else {
try {
// Convert JSON string to Map and build WHERE clause with parameters
Map<String, Object> matchMap = OBJECT_MAPPER.readValue(matchProperties, MAP_TYPE_REF);

if (!matchMap.isEmpty()) {
StringBuilder whereClause = new StringBuilder();
queryParams = new java.util.HashMap<>();
int paramIndex = 0;

for (Map.Entry<String, Object> entry : matchMap.entrySet()) {
if (paramIndex > 0) {
whereClause.append(" AND ");
}
String paramName = "param" + paramIndex;
whereClause.append(alias).append(".").append(entry.getKey())
.append(" = $").append(paramName);
queryParams.put(paramName, entry.getValue());
paramIndex++;
}

query = String.format("MATCH (%s:%s) WHERE %s %s DELETE %s",
alias, label, whereClause.toString(), detached, alias);
} else {
// Empty map, delete all nodes of this label
query = String.format("MATCH (%s:%s) %s DELETE %s", alias, label, detached, alias);
}
} catch (Exception e) {
exchange.setException(
new Neo4jOperationException(
Neo4Operation.DELETE_NODE,
new IllegalArgumentException(
"Failed to parse MATCH_PROPERTIES as JSON: " + matchProperties,
e)));
return;
}
}

executeWriteQuery(exchange, query, null, databaseName, Neo4Operation.DELETE_NODE);
executeWriteQuery(exchange, query, queryParams, databaseName, Neo4Operation.DELETE_NODE);
}

private void createVectorIndex(Exchange exchange) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ public class Neo4jNodeIT extends Neo4jTestSupport {
@Order(0)
void createNodeWithJsonObject() {

var body = "{name: 'Alice', email: 'alice@example.com', age: 30}";
var expectedCypherQuery = "CREATE (u1:User {name: 'Alice', email: 'alice@example.com', age: 30})";
var body = "{\"name\": \"Alice\", \"email\": \"alice@example.com\", \"age\": 30}";
var expectedCypherQuery = "CREATE (u1:User $props)";

Exchange result = fluentTemplate.to("neo4j:neo4j?alias=u1&label=User")
.withBodyAs(body, String.class)
Expand Down Expand Up @@ -141,7 +141,7 @@ void testCreateMultipleNodesAndRelationshipWithCypherQuery() {
void testRetrieveNode() {
Exchange result = fluentTemplate.to("neo4j:neo4j?alias=u&label=User")
.withHeader(Neo4jConstants.Headers.OPERATION, Neo4Operation.RETRIEVE_NODES)
.withHeader(Neo4jConstants.Headers.MATCH_PROPERTIES, "{name: 'Alice'}")
.withHeader(Neo4jConstants.Headers.MATCH_PROPERTIES, "{\"name\": \"Alice\"}")
.request(Exchange.class);

assertNotNull(result);
Expand Down Expand Up @@ -193,7 +193,7 @@ void testDeleteNode() {
// delete node
Exchange result = fluentTemplate.to("neo4j:neo4j?alias=u&label=User")
.withHeader(Neo4jConstants.Headers.OPERATION, Neo4Operation.DELETE_NODE)
.withHeader(Neo4jConstants.Headers.MATCH_PROPERTIES, "{name: 'Alice'}")
.withHeader(Neo4jConstants.Headers.MATCH_PROPERTIES, "{\"name\": \"Alice\"}")
.request(Exchange.class);

assertNotNull(result);
Expand All @@ -215,7 +215,7 @@ void testDeleteNode() {

result = fluentTemplate.to("neo4j:neo4j?alias=u&label=User")
.withHeader(Neo4jConstants.Headers.OPERATION, Neo4Operation.RETRIEVE_NODES)
.withHeader(Neo4jConstants.Headers.MATCH_PROPERTIES, "{name: 'Alice'}")
.withHeader(Neo4jConstants.Headers.MATCH_PROPERTIES, "{\"name\": \"Alice\"}")
.request(Exchange.class);

assertNotNull(result);
Expand All @@ -233,7 +233,7 @@ void testDeleteNodeWithExistingRelationship() {
// try to delete user named Diana and this should fail as Diana has a relationship with Ethan
Exchange result = fluentTemplate.to("neo4j:neo4j?alias=u&label=User")
.withHeader(Neo4jConstants.Headers.OPERATION, Neo4Operation.DELETE_NODE)
.withHeader(Neo4jConstants.Headers.MATCH_PROPERTIES, "{name: 'Diana'}")
.withHeader(Neo4jConstants.Headers.MATCH_PROPERTIES, "{\"name\": \"Diana\"}")
.request(Exchange.class);

assertNotNull(result);
Expand All @@ -245,7 +245,7 @@ void testDeleteNodeWithExistingRelationship() {
// delete the Diana by detaching its relationship with Ethan - detachRelationship=true
result = fluentTemplate.to("neo4j:neo4j?alias=u&label=User&detachRelationship=true")
.withHeader(Neo4jConstants.Headers.OPERATION, Neo4Operation.DELETE_NODE)
.withHeader(Neo4jConstants.Headers.MATCH_PROPERTIES, "{name: 'Diana'}")
.withHeader(Neo4jConstants.Headers.MATCH_PROPERTIES, "{\"name\": \"Diana\"}")
.request(Exchange.class);
assertNotNull(result);
assertNull("No exception anymore when deleting relationship at same time", result.getException());
Expand All @@ -269,7 +269,7 @@ void testDeleteNodeWithExistingRelationship() {

result = fluentTemplate.to("neo4j:neo4j?alias=u&label=User")
.withHeader(Neo4jConstants.Headers.OPERATION, Neo4Operation.RETRIEVE_NODES)
.withHeader(Neo4jConstants.Headers.MATCH_PROPERTIES, "{name: 'Diana'}")
.withHeader(Neo4jConstants.Headers.MATCH_PROPERTIES, "{\"name\": \"Diana\"}")
.request(Exchange.class);

assertNotNull(result);
Expand Down Expand Up @@ -311,7 +311,7 @@ void testDeleteNodeWithCypherQuery() {

result = fluentTemplate.to("neo4j:neo4j?alias=u&label=User")
.withHeader(Neo4jConstants.Headers.OPERATION, Neo4Operation.RETRIEVE_NODES)
.withHeader(Neo4jConstants.Headers.MATCH_PROPERTIES, "{name: 'Bob'}")
.withHeader(Neo4jConstants.Headers.MATCH_PROPERTIES, "{\"name\": \"Bob\"}")
.request(Exchange.class);

assertNotNull(result);
Expand Down
11 changes: 7 additions & 4 deletions components/camel-tika/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,11 @@
<packaging>jar</packaging>
<name>Camel :: Tika</name>
<description>This component integrates with Apache Tika to extract content and metadata from thousands of file types.</description>
<version>${revision}</version>

<properties>
<revision>${camel-tika.tesb.version}</revision>
<tika.tesb.version>3.2.3</tika.tesb.version>
</properties>

<dependencies>
Expand All @@ -44,17 +47,17 @@
<dependency>
<groupId>org.apache.tika</groupId>
<artifactId>tika-core</artifactId>
<version>${tika-version}</version>
<version>${tika.tesb.version}</version>
</dependency>
<dependency>
<groupId>org.apache.tika</groupId>
<artifactId>tika-parser-html-commons</artifactId>
<version>${tika-version}</version>
<artifactId>tika-handler-boilerpipe</artifactId>
<version>${tika.tesb.version}</version>
</dependency>
<dependency>
<groupId>org.apache.tika</groupId>
<artifactId>tika-parser-text-module</artifactId>
<version>${tika-version}</version>
<version>${tika.tesb.version}</version>
</dependency>

<!-- test dependencies -->
Expand Down
4 changes: 2 additions & 2 deletions parent/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1948,7 +1948,7 @@
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-neo4j</artifactId>
<version>${upstream.version}</version>
<version>${camel-neo4j.tesb.version}</version>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
Expand Down Expand Up @@ -2483,7 +2483,7 @@
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-tika</artifactId>
<version>${upstream.version}</version>
<version>${camel-tika.tesb.version}</version>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
Expand Down
30 changes: 17 additions & 13 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -104,20 +104,22 @@

<properties>
<upstream.version>4.10.5</upstream.version>
<camel-activemq.tesb.version>4.10.5.20251010</camel-activemq.tesb.version>
<camel-activemq6.tesb.version>4.10.5.20251010</camel-activemq6.tesb.version>
<camel-cxf.tesb.version>4.10.5.20251010</camel-cxf.tesb.version>
<camel-cxf-soap.tesb.version>4.10.5.20251010</camel-cxf-soap.tesb.version>
<camel-cxf-rest.tesb.version>4.10.5.20251010</camel-cxf-rest.tesb.version>
<camel-salesforce.tesb.version>4.10.5.20251010</camel-salesforce.tesb.version>
<camel-spring.tesb.version>4.10.5.20251010</camel-spring.tesb.version>
<camel-activemq.tesb.version>4.10.5.20251224</camel-activemq.tesb.version>
<camel-activemq6.tesb.version>4.10.5.20251224</camel-activemq6.tesb.version>
<camel-cxf.tesb.version>4.10.5.20251224</camel-cxf.tesb.version>
<camel-cxf-soap.tesb.version>4.10.5.20251224</camel-cxf-soap.tesb.version>
<camel-cxf-rest.tesb.version>4.10.5.20251224</camel-cxf-rest.tesb.version>
<camel-salesforce.tesb.version>4.10.5.20251224</camel-salesforce.tesb.version>
<camel-spring.tesb.version>4.10.5.20251224</camel-spring.tesb.version>
<cxf.tesb.version>4.1.3.1</cxf.tesb.version>
<camel-langchain4j-core.tesb.version>4.10.5.20251010</camel-langchain4j-core.tesb.version>
<camel-langchain4j-chat.tesb.version>4.10.5.20251010</camel-langchain4j-chat.tesb.version>
<camel-langchain4j-tools.tesb.version>4.10.5.20251010</camel-langchain4j-tools.tesb.version>
<camel-drill.tesb.version>4.10.5.20251010</camel-drill.tesb.version>
<camel-componentdsl.tesb.version>4.10.5.20251010</camel-componentdsl.tesb.version>
<camel-endpointdsl.tesb.version>4.10.5.20251010</camel-endpointdsl.tesb.version>
<camel-langchain4j-core.tesb.version>4.10.5.20251224</camel-langchain4j-core.tesb.version>
<camel-langchain4j-chat.tesb.version>4.10.5.20251224</camel-langchain4j-chat.tesb.version>
<camel-langchain4j-tools.tesb.version>4.10.5.20251224</camel-langchain4j-tools.tesb.version>
<camel-drill.tesb.version>4.10.5.20251224</camel-drill.tesb.version>
<camel-neo4j.tesb.version>4.10.5.20251224</camel-neo4j.tesb.version>
<camel-tika.tesb.version>4.10.5.20251224</camel-tika.tesb.version>
<camel-componentdsl.tesb.version>4.10.5.20251224</camel-componentdsl.tesb.version>
<camel-endpointdsl.tesb.version>4.10.5.20251224</camel-endpointdsl.tesb.version>

<!-- unify the encoding for all the modules -->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
Expand Down Expand Up @@ -270,6 +272,8 @@
<camel-langchain4j-chat.tesb.version>${camel-langchain4j-chat.tesb.version}</camel-langchain4j-chat.tesb.version>
<camel-langchain4j-tools.tesb.version>${camel-langchain4j-tools.tesb.version}</camel-langchain4j-tools.tesb.version>
<camel-drill.tesb.version>${camel-drill.tesb.version}</camel-drill.tesb.version>
<camel-neo4j.tesb.version>${camel-neo4j.tesb.version}</camel-neo4j.tesb.version>
<camel-tika.tesb.version>${camel-tika.tesb.version}</camel-tika.tesb.version>
<camel-componentdsl.tesb.version>${camel-componentdsl.tesb.version}</camel-componentdsl.tesb.version>
<camel-endpointdsl.tesb.version>${camel-endpointdsl.tesb.version}</camel-endpointdsl.tesb.version>
</properties>
Expand Down