This is an automated email from the ASF dual-hosted git repository. lburgazzoli pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/main by this push: new 3096762b96a camel-jq: add a function to retrieve value from Exchange properties 3096762b96a is described below commit 3096762b96aee19841d83124bb95518f68d6a46c Author: Luca Burgazzoli <lburgazz...@gmail.com> AuthorDate: Thu Jun 16 19:46:23 2022 +0200 camel-jq: add a function to retrieve value from Exchange properties --- .../org/apache/camel/language/jq/JqExpression.java | 17 +++- .../org/apache/camel/language/jq/JqFunctions.java | 104 +++++++++++++++++++-- ...ava => JqExpressionFromHeaderAsStringTest.java} | 4 +- ...erTest.java => JqExpressionFromHeaderTest.java} | 28 +++++- ...erFnTest.java => JqExpressionHeaderFnTest.java} | 4 +- ...elloPojoTest.java => JqExpressionPojoTest.java} | 4 +- ...erTest.java => JqExpressionPropertyFnTest.java} | 16 ++-- ...qHelloTest.java => JqExpressionSimpleTest.java} | 4 +- .../apache/camel/language/jq/JqExpressionTest.java | 41 ++++++++ 9 files changed, 193 insertions(+), 29 deletions(-) diff --git a/components/camel-jq/src/main/java/org/apache/camel/language/jq/JqExpression.java b/components/camel-jq/src/main/java/org/apache/camel/language/jq/JqExpression.java index d30343a3b00..97777d72724 100644 --- a/components/camel-jq/src/main/java/org/apache/camel/language/jq/JqExpression.java +++ b/components/camel-jq/src/main/java/org/apache/camel/language/jq/JqExpression.java @@ -29,6 +29,8 @@ import net.thisptr.jackson.jq.Versions; import net.thisptr.jackson.jq.exception.JsonQueryException; import org.apache.camel.CamelContext; import org.apache.camel.Exchange; +import org.apache.camel.InvalidPayloadException; +import org.apache.camel.NoSuchHeaderException; import org.apache.camel.RuntimeCamelException; import org.apache.camel.TypeConverter; import org.apache.camel.spi.ExpressionResultTypeAware; @@ -162,10 +164,19 @@ public class JqExpression extends ExpressionAdapter implements ExpressionResultT JqFunctions.EXCHANGE_LOCAL.set(exchange); final List<JsonNode> outputs = new ArrayList<>(1); + final JsonNode payload; - final JsonNode payload = headerName == null - ? exchange.getMessage().getMandatoryBody(JsonNode.class) - : exchange.getMessage().getHeader(headerName, JsonNode.class); + if (headerName == null) { + payload = exchange.getMessage().getBody(JsonNode.class); + if (payload == null) { + throw new InvalidPayloadException(exchange, JsonNode.class); + } + } else { + payload = exchange.getMessage().getHeader(headerName, JsonNode.class); + if (payload == null) { + throw new NoSuchHeaderException(exchange, headerName, JsonNode.class); + } + } this.query.apply(scope, payload, outputs::add); diff --git a/components/camel-jq/src/main/java/org/apache/camel/language/jq/JqFunctions.java b/components/camel-jq/src/main/java/org/apache/camel/language/jq/JqFunctions.java index 1b6264a6f21..114d0312a84 100644 --- a/components/camel-jq/src/main/java/org/apache/camel/language/jq/JqFunctions.java +++ b/components/camel-jq/src/main/java/org/apache/camel/language/jq/JqFunctions.java @@ -82,6 +82,32 @@ public final class JqFunctions { public static void loadLocal(Scope scope) { scope.addFunction(Header.NAME, 1, new Header()); scope.addFunction(Header.NAME, 2, new Header()); + scope.addFunction(Property.NAME, 1, new Property()); + scope.addFunction(Property.NAME, 2, new Property()); + } + + public abstract static class ExchangeAwareFunction implements Function { + + @Override + public void apply(Scope scope, List<Expression> args, JsonNode in, Path path, PathOutput output, Version version) + throws JsonQueryException { + + Exchange exchange = EXCHANGE_LOCAL.get(); + + if (exchange != null) { + doApply(scope, args, in, path, output, version, exchange); + } + } + + protected abstract void doApply( + Scope scope, + List<Expression> args, + JsonNode in, + Path path, + PathOutput output, + Version version, + Exchange exchange) + throws JsonQueryException; } /** @@ -98,30 +124,90 @@ public final class JqFunctions { * </pre> * */ - public static class Header implements Function { + public static class Header extends ExchangeAwareFunction { public static final String NAME = "header"; @Override - public void apply(Scope scope, List<Expression> args, JsonNode in, Path path, PathOutput output, Version version) + protected void doApply( + Scope scope, + List<Expression> args, + JsonNode in, + Path path, + PathOutput output, + Version version, + Exchange exchange) throws JsonQueryException { - Exchange exchange = EXCHANGE_LOCAL.get(); + args.get(0).apply(scope, in, name -> { + if (args.size() == 2) { + args.get(1).apply(scope, in, defval -> { + extract( + exchange, + name.asText(), + defval.asText(), + output); + }); + } else { + extract( + exchange, + name.asText(), + null, + output); + } + }); + } - if (exchange == null) { - return; + private void extract(Exchange exchange, String headerName, String headerValue, PathOutput output) + throws JsonQueryException { + String header = exchange.getMessage().getHeader(headerName, headerValue, String.class); + + if (header == null) { + output.emit(NullNode.getInstance(), null); + } else { + output.emit(new TextNode(header), null); } + } + } + + /** + * A function that allow to retrieve an {@link org.apache.camel.Message} property value as part of JQ expression + * evaluation. + * + * As example, the following JQ expression sets the {@code .name} property to the value of the header named + * {@code CommitterName}. + * + * <pre> + * {@code + * .name = proeprty(\"CommitterName\")" + * } + * </pre> + * + */ + public static class Property extends ExchangeAwareFunction { + public static final String NAME = "property"; + + @Override + protected void doApply( + Scope scope, + List<Expression> args, + JsonNode in, + Path path, + PathOutput output, + Version version, + Exchange exchange) + throws JsonQueryException { args.get(0).apply(scope, in, name -> { if (args.size() == 2) { args.get(1).apply(scope, in, defval -> { - doApply( + extract( exchange, name.asText(), defval.asText(), output); }); } else { - doApply( + extract( exchange, name.asText(), null, @@ -130,9 +216,9 @@ public final class JqFunctions { }); } - private void doApply(Exchange exchange, String headerName, String headerValue, PathOutput output) + private void extract(Exchange exchange, String propertyName, String propertyValue, PathOutput output) throws JsonQueryException { - String header = exchange.getMessage().getHeader(headerName, headerValue, String.class); + String header = exchange.getProperty(propertyName, propertyValue, String.class); if (header == null) { output.emit(NullNode.getInstance(), null); diff --git a/components/camel-jq/src/test/java/org/apache/camel/language/jq/JqHelloFromHeaderAsStringTest.java b/components/camel-jq/src/test/java/org/apache/camel/language/jq/JqExpressionFromHeaderAsStringTest.java similarity index 92% rename from components/camel-jq/src/test/java/org/apache/camel/language/jq/JqHelloFromHeaderAsStringTest.java rename to components/camel-jq/src/test/java/org/apache/camel/language/jq/JqExpressionFromHeaderAsStringTest.java index 52017d965d7..596d37c8f3c 100644 --- a/components/camel-jq/src/test/java/org/apache/camel/language/jq/JqHelloFromHeaderAsStringTest.java +++ b/components/camel-jq/src/test/java/org/apache/camel/language/jq/JqExpressionFromHeaderAsStringTest.java @@ -20,7 +20,7 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import org.apache.camel.builder.RouteBuilder; import org.junit.jupiter.api.Test; -public class JqHelloFromHeaderAsStringTest extends JqTestSupport { +public class JqExpressionFromHeaderAsStringTest extends JqTestSupport { @Override protected RouteBuilder createRouteBuilder() { return new RouteBuilder() { @@ -34,7 +34,7 @@ public class JqHelloFromHeaderAsStringTest extends JqTestSupport { } @Test - public void testHelloHeader() throws Exception { + public void testExpression() throws Exception { getMockEndpoint("mock:result").expectedBodiesReceived("bar"); ObjectNode node = MAPPER.createObjectNode(); diff --git a/components/camel-jq/src/test/java/org/apache/camel/language/jq/JqHelloFromHeaderTest.java b/components/camel-jq/src/test/java/org/apache/camel/language/jq/JqExpressionFromHeaderTest.java similarity index 65% copy from components/camel-jq/src/test/java/org/apache/camel/language/jq/JqHelloFromHeaderTest.java copy to components/camel-jq/src/test/java/org/apache/camel/language/jq/JqExpressionFromHeaderTest.java index dd443d0c865..718d0e3bee5 100644 --- a/components/camel-jq/src/test/java/org/apache/camel/language/jq/JqHelloFromHeaderTest.java +++ b/components/camel-jq/src/test/java/org/apache/camel/language/jq/JqExpressionFromHeaderTest.java @@ -18,26 +18,33 @@ package org.apache.camel.language.jq; import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.node.TextNode; +import org.apache.camel.NoSuchHeaderException; import org.apache.camel.builder.RouteBuilder; import org.junit.jupiter.api.Test; -public class JqHelloFromHeaderTest extends JqTestSupport { +public class JqExpressionFromHeaderTest extends JqTestSupport { @Override protected RouteBuilder createRouteBuilder() { return new RouteBuilder() { @Override public void configure() { from("direct:start") + .doTry() .transform().jq(".foo", "Content") - .to("mock:result"); + .to("mock:result") + .doCatch(NoSuchHeaderException.class) + .to("mock:fail"); + } }; } @Test - public void testHelloHeader() throws Exception { + public void testExpressionFromHeader() throws Exception { getMockEndpoint("mock:result") .expectedBodiesReceived(new TextNode("bar")); + getMockEndpoint("mock:fail") + .expectedMessageCount(0); ObjectNode node = MAPPER.createObjectNode(); node.put("foo", "bar"); @@ -46,4 +53,19 @@ public class JqHelloFromHeaderTest extends JqTestSupport { assertMockEndpointsSatisfied(); } + + @Test + public void testExpressionFromHeaderFail() throws Exception { + getMockEndpoint("mock:result") + .expectedMessageCount(0); + getMockEndpoint("mock:fail") + .expectedMessageCount(1); + + ObjectNode node = MAPPER.createObjectNode(); + node.put("foo", "bar"); + + template.sendBody("direct:start", node); + + assertMockEndpointsSatisfied(); + } } diff --git a/components/camel-jq/src/test/java/org/apache/camel/language/jq/JqHelloHeaderFnTest.java b/components/camel-jq/src/test/java/org/apache/camel/language/jq/JqExpressionHeaderFnTest.java similarity index 93% rename from components/camel-jq/src/test/java/org/apache/camel/language/jq/JqHelloHeaderFnTest.java rename to components/camel-jq/src/test/java/org/apache/camel/language/jq/JqExpressionHeaderFnTest.java index e161413995e..f45e3075323 100644 --- a/components/camel-jq/src/test/java/org/apache/camel/language/jq/JqHelloHeaderFnTest.java +++ b/components/camel-jq/src/test/java/org/apache/camel/language/jq/JqExpressionHeaderFnTest.java @@ -20,7 +20,7 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import org.apache.camel.builder.RouteBuilder; import org.junit.jupiter.api.Test; -public class JqHelloHeaderFnTest extends JqTestSupport { +public class JqExpressionHeaderFnTest extends JqTestSupport { @Override protected RouteBuilder createRouteBuilder() { return new RouteBuilder() { @@ -34,7 +34,7 @@ public class JqHelloHeaderFnTest extends JqTestSupport { } @Test - public void testHelloHeader() throws Exception { + public void testExpression() throws Exception { getMockEndpoint("mock:result") .expectedBodiesReceived(MAPPER.createObjectNode().put("foo", "MyValue")); diff --git a/components/camel-jq/src/test/java/org/apache/camel/language/jq/JqHelloPojoTest.java b/components/camel-jq/src/test/java/org/apache/camel/language/jq/JqExpressionPojoTest.java similarity index 96% rename from components/camel-jq/src/test/java/org/apache/camel/language/jq/JqHelloPojoTest.java rename to components/camel-jq/src/test/java/org/apache/camel/language/jq/JqExpressionPojoTest.java index 99315c17cb3..87d6ad49f49 100644 --- a/components/camel-jq/src/test/java/org/apache/camel/language/jq/JqHelloPojoTest.java +++ b/components/camel-jq/src/test/java/org/apache/camel/language/jq/JqExpressionPojoTest.java @@ -24,7 +24,7 @@ import org.apache.camel.builder.RouteBuilder; import org.apache.camel.component.jackson.JacksonConstants; import org.junit.jupiter.api.Test; -public class JqHelloPojoTest extends JqTestSupport { +public class JqExpressionPojoTest extends JqTestSupport { @Override protected CamelContext createCamelContext() throws Exception { @@ -48,7 +48,7 @@ public class JqHelloPojoTest extends JqTestSupport { } @Test - public void testHello() throws Exception { + public void testExpression() throws Exception { getMockEndpoint("mock:result").expectedBodiesReceived(new Book("foo", "bar")); ObjectNode node = MAPPER.createObjectNode(); diff --git a/components/camel-jq/src/test/java/org/apache/camel/language/jq/JqHelloFromHeaderTest.java b/components/camel-jq/src/test/java/org/apache/camel/language/jq/JqExpressionPropertyFnTest.java similarity index 73% rename from components/camel-jq/src/test/java/org/apache/camel/language/jq/JqHelloFromHeaderTest.java rename to components/camel-jq/src/test/java/org/apache/camel/language/jq/JqExpressionPropertyFnTest.java index dd443d0c865..eeccf381c34 100644 --- a/components/camel-jq/src/test/java/org/apache/camel/language/jq/JqHelloFromHeaderTest.java +++ b/components/camel-jq/src/test/java/org/apache/camel/language/jq/JqExpressionPropertyFnTest.java @@ -17,32 +17,36 @@ package org.apache.camel.language.jq; import com.fasterxml.jackson.databind.node.ObjectNode; -import com.fasterxml.jackson.databind.node.TextNode; import org.apache.camel.builder.RouteBuilder; import org.junit.jupiter.api.Test; -public class JqHelloFromHeaderTest extends JqTestSupport { +public class JqExpressionPropertyFnTest extends JqTestSupport { @Override protected RouteBuilder createRouteBuilder() { return new RouteBuilder() { @Override public void configure() { from("direct:start") - .transform().jq(".foo", "Content") + .transform().jq(".foo = property(\"MyProperty\")") .to("mock:result"); } }; } @Test - public void testHelloHeader() throws Exception { + public void testExpression() throws Exception { getMockEndpoint("mock:result") - .expectedBodiesReceived(new TextNode("bar")); + .expectedBodiesReceived(MAPPER.createObjectNode().put("foo", "MyPropertyValue")); ObjectNode node = MAPPER.createObjectNode(); node.put("foo", "bar"); - template.sendBodyAndHeader("direct:start", null, "Content", node); + fluentTemplate.to("direct:start") + .withProcessor(e -> { + e.setProperty("MyProperty", "MyPropertyValue"); + e.getMessage().setBody(node); + }) + .send(); assertMockEndpointsSatisfied(); } diff --git a/components/camel-jq/src/test/java/org/apache/camel/language/jq/JqHelloTest.java b/components/camel-jq/src/test/java/org/apache/camel/language/jq/JqExpressionSimpleTest.java similarity index 93% rename from components/camel-jq/src/test/java/org/apache/camel/language/jq/JqHelloTest.java rename to components/camel-jq/src/test/java/org/apache/camel/language/jq/JqExpressionSimpleTest.java index 61a40cd48ca..5757c8042bc 100644 --- a/components/camel-jq/src/test/java/org/apache/camel/language/jq/JqHelloTest.java +++ b/components/camel-jq/src/test/java/org/apache/camel/language/jq/JqExpressionSimpleTest.java @@ -21,7 +21,7 @@ import com.fasterxml.jackson.databind.node.TextNode; import org.apache.camel.builder.RouteBuilder; import org.junit.jupiter.api.Test; -public class JqHelloTest extends JqTestSupport { +public class JqExpressionSimpleTest extends JqTestSupport { @Override protected RouteBuilder createRouteBuilder() { return new RouteBuilder() { @@ -35,7 +35,7 @@ public class JqHelloTest extends JqTestSupport { } @Test - public void testHello() throws Exception { + public void testExpression() throws Exception { getMockEndpoint("mock:result").expectedBodiesReceived(new TextNode("bar")); ObjectNode node = MAPPER.createObjectNode(); diff --git a/components/camel-jq/src/test/java/org/apache/camel/language/jq/JqExpressionTest.java b/components/camel-jq/src/test/java/org/apache/camel/language/jq/JqExpressionTest.java index a17a6034ffd..04996682291 100644 --- a/components/camel-jq/src/test/java/org/apache/camel/language/jq/JqExpressionTest.java +++ b/components/camel-jq/src/test/java/org/apache/camel/language/jq/JqExpressionTest.java @@ -53,6 +53,24 @@ public class JqExpressionTest { } } + @Test + public void extractProperty() throws Exception { + try (CamelContext context = new DefaultCamelContext()) { + Exchange exchange = new DefaultExchange(context); + exchange.getMessage().setBody(MAPPER.createObjectNode()); + exchange.setProperty("CommitterName", "Andrea"); + + JqExpression expression = new JqExpression("property(\"CommitterName\")"); + expression.init(context); + + JsonNode result = expression.evaluate(exchange, JsonNode.class); + + assertThatJson(result) + .isString() + .isEqualTo("Andrea"); + } + } + @Test public void extractHeaderWithDefault() throws Exception { try (CamelContext context = new DefaultCamelContext()) { @@ -184,6 +202,29 @@ public class JqExpressionTest { } } + @Test + public void setFieldFromProperty() throws Exception { + try (CamelContext context = new DefaultCamelContext()) { + ObjectNode node = MAPPER.createObjectNode(); + node.with("commit").put("name", "Nicolas Williams"); + node.with("commit").put("message", "Reject all overlong UTF8 sequences."); + + Exchange exchange = new DefaultExchange(context); + exchange.setProperty("CommitterName", "Andrea"); + exchange.getMessage().setBody(node); + + JqExpression expression = new JqExpression(".commit.name = property(\"CommitterName\")"); + expression.init(context); + + JsonNode result = expression.evaluate(exchange, JsonNode.class); + + assertThatJson(result) + .inPath("$.commit.name") + .isString() + .isEqualTo("Andrea"); + } + } + @Test public void removeField() throws Exception { try (CamelContext context = new DefaultCamelContext()) {