This is an automated email from the ASF dual-hosted git repository. klease 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 c5d021c263f CAMEL-20131: Improve SetHeaders EIP implementation and docs (#12143) c5d021c263f is described below commit c5d021c263f3456626b83916f5170d38c984ca1c Author: klease <38634989+kle...@users.noreply.github.com> AuthorDate: Wed Nov 22 17:47:24 2023 +0100 CAMEL-20131: Improve SetHeaders EIP implementation and docs (#12143) Simplify the constructors and allow generic Map. Add more examples in Yaml and using Map.of() in Java DSL. --- .../src/main/docs/modules/eips/nav.adoc | 188 +++++++++++---------- .../docs/modules/eips/pages/setHeader-eip.adoc | 2 + .../docs/modules/eips/pages/setHeaders-eip.adoc | 48 ++++++ .../apache/camel/model/ProcessorDefinition.java | 18 +- .../apache/camel/model/SetHeadersDefinition.java | 52 +++--- .../camel/model/SetHeadersDefinitionTest.java | 15 ++ .../camel/processor/SetHeadersProcessorTest.java | 14 ++ .../apache/camel/dsl/yaml/SetHeadersTest.groovy | 31 +++- 8 files changed, 242 insertions(+), 126 deletions(-) diff --git a/core/camel-core-engine/src/main/docs/modules/eips/nav.adoc b/core/camel-core-engine/src/main/docs/modules/eips/nav.adoc index c4ddc9384fd..dae91c70d8a 100644 --- a/core/camel-core-engine/src/main/docs/modules/eips/nav.adoc +++ b/core/camel-core-engine/src/main/docs/modules/eips/nav.adoc @@ -2,89 +2,105 @@ // make edits in docs/*nav.adoc.template files instead * xref:eips:enterprise-integration-patterns.adoc[Enterprise Integration Patterns] - ** xref:eips:aggregate-eip.adoc[Aggregate] - ** xref:eips:bean-eip.adoc[Bean] - ** xref:eips:change-data-capture.adoc[Change Data Capture] - ** xref:eips:channel-adapter.adoc[Channel Adapter] - ** xref:eips:choice-eip.adoc[Content Based Router] - ** xref:eips:circuitBreaker-eip.adoc[Circuit Breaker] - ** xref:eips:claimCheck-eip.adoc[Claim Check] - ** xref:eips:competing-consumers.adoc[Competing Consumers] - ** xref:eips:composed-message-processor.adoc[Composed Message Processor] - ** xref:eips:content-enricher.adoc[Content Enricher] - ** xref:eips:content-filter-eip.adoc[Content Filter] - ** xref:eips:convertBodyTo-eip.adoc[Convert Body To] - ** xref:eips:correlation-identifier.adoc[Correlation Identifier] - ** xref:eips:dead-letter-channel.adoc[Dead Letter Channel] - ** xref:eips:delay-eip.adoc[Delay] - ** xref:eips:durable-subscriber.adoc[Durable Subscriber] - ** xref:eips:dynamicRouter-eip.adoc[Dynamic Router] - ** xref:eips:enrich-eip.adoc[Enrich] - ** xref:eips:eventDrivenConsumer-eip.adoc[Event Driven Consumer] - ** xref:eips:event-message.adoc[Event Message] - ** xref:eips:filter-eip.adoc[Filter] - ** xref:eips:from-eip.adoc[From] - ** xref:eips:guaranteed-delivery.adoc[Guaranteed Delivery] - ** xref:eips:idempotentConsumer-eip.adoc[Idempotent Consumer] - ** xref:eips:intercept.adoc[Intercept] - ** xref:eips:kamelet-eip.adoc[Kamelet] - ** xref:eips:loadBalance-eip.adoc[Load Balance] - ** xref:eips:log-eip.adoc[Log] - ** xref:eips:loop-eip.adoc[Loop] - ** xref:eips:marshal-eip.adoc[Marshal] - ** xref:eips:message.adoc[Message] - ** xref:eips:message-broker.adoc[Message Broker] - ** xref:eips:message-bus.adoc[Message Bus] - ** xref:eips:message-channel.adoc[Message Channel] - ** xref:eips:message-dispatcher.adoc[Message Dispatcher] - ** xref:eips:message-endpoint.adoc[Message Endpoint] - ** xref:eips:message-expiration.adoc[Message Expiration] - ** xref:eips:message-history.adoc[Message History] - ** xref:eips:message-router.adoc[Message Router] - ** xref:eips:message-translator.adoc[Message Translator] - ** xref:eips:messaging-bridge.adoc[Messaging Bridge] - ** xref:eips:messaging-gateway.adoc[Messaging Gateway] - ** xref:eips:messaging-mapper.adoc[Messaging Mapper] - ** xref:eips:multicast-eip.adoc[Multicast] - ** xref:eips:normalizer.adoc[Normalizer] - ** xref:eips:pipeline-eip.adoc[Pipeline] - ** xref:eips:point-to-point-channel.adoc[Point to Point Channel] - ** xref:eips:pollEnrich-eip.adoc[Poll Enrich] - ** xref:eips:polling-consumer.adoc[Polling Consumer] - ** xref:eips:process-eip.adoc[Process] - ** xref:eips:process-manager.adoc[Process Manager] - ** xref:eips:publish-subscribe-channel.adoc[Publish Subscribe Channel] - ** xref:eips:recipientList-eip.adoc[Recipient List] - ** xref:eips:removeHeader-eip.adoc[Remove Header] - ** xref:eips:removeHeaders-eip.adoc[Remove Headers] - ** xref:eips:removeProperties-eip.adoc[Remove Properties] - ** xref:eips:removeProperty-eip.adoc[Remove Property] - ** xref:eips:requestReply-eip.adoc[Request Reply] - ** xref:eips:resequence-eip.adoc[Resequence] - ** xref:eips:resilience4j-eip.adoc[Resilience4j Circuit Breaker] - ** xref:eips:return-address.adoc[Return Address] - ** xref:eips:rollback-eip.adoc[Rollback] - ** xref:eips:routingSlip-eip.adoc[Routing Slip] - ** xref:eips:saga-eip.adoc[Saga] - ** xref:eips:sample-eip.adoc[Sample] - ** xref:eips:scatter-gather.adoc[Scatter Gather] - ** xref:eips:script-eip.adoc[Script] - ** xref:eips:selective-consumer.adoc[Selective Consumer] - ** xref:eips:service-activator.adoc[Service Activator] - ** xref:eips:serviceCall-eip.adoc[Service Call] - ** xref:eips:setBody-eip.adoc[Set Body] - ** xref:eips:setHeader-eip.adoc[Set Header] - ** xref:eips:setProperty-eip.adoc[Set Property] - ** xref:eips:sort-eip.adoc[Sort] - ** xref:eips:split-eip.adoc[Split] - ** xref:eips:step-eip.adoc[Step] - ** xref:eips:stop-eip.adoc[Stop] - ** xref:eips:threads-eip.adoc[Threads] - ** xref:eips:throttle-eip.adoc[Throttle] - ** xref:eips:to-eip.adoc[To] - ** xref:eips:toD-eip.adoc[To Dynamic] - ** xref:eips:transactional-client.adoc[Transactional Client] - ** xref:eips:transform-eip.adoc[Transform] - ** xref:eips:unmarshal-eip.adoc[Unmarshal] - ** xref:eips:validate-eip.adoc[Validate] - ** xref:eips:wireTap-eip.adoc[Wire Tap] +** xref:eips:aggregate-eip.adoc[Aggregate] +** xref:eips:batch-config-eip.adoc[Batch-config] +** xref:eips:bean-eip.adoc[Bean] +** xref:eips:change-data-capture.adoc[Change Data Capture] +** xref:eips:channel-adapter.adoc[Channel Adapter] +** xref:eips:choice-eip.adoc[Choice] +** xref:eips:circuitBreaker-eip.adoc[Circuit Breaker] +** xref:eips:claimCheck-eip.adoc[Claim Check] +** xref:eips:competing-consumers.adoc[Competing Consumers] +** xref:eips:composed-message-processor.adoc[Composed Message Processor] +** xref:eips:content-enricher.adoc[Content Enricher] +** xref:eips:content-filter-eip.adoc[Content Filter] +** xref:eips:convertBodyTo-eip.adoc[Convert Body To] +** xref:eips:correlation-identifier.adoc[Correlation Identifier] +** xref:eips:customLoadBalancer-eip.adoc[Custom Load Balancer] +** xref:eips:dead-letter-channel.adoc[Dead Letter Channel] +** xref:eips:delay-eip.adoc[Delay] +** xref:eips:durable-subscriber.adoc[Durable Subscriber] +** xref:eips:dynamicRouter-eip.adoc[Dynamic Router] +** xref:eips:enrich-eip.adoc[Enrich] +** xref:eips:enterprise-integration-patterns.adoc[Enterprise Integration Patterns] +** xref:eips:eventDrivenConsumer-eip.adoc[Event Driven Consumer] +** xref:eips:event-message.adoc[Event Message] +** xref:eips:failover-eip.adoc[Failover] +** xref:eips:faultToleranceConfiguration-eip.adoc[Fault Tolerance Configuration] +** xref:eips:fault-tolerance-eip.adoc[Fault Tolerance EIP] +** xref:eips:filter-eip.adoc[Filter] +** xref:eips:from-eip.adoc[From] +** xref:eips:guaranteed-delivery.adoc[Guaranteed Delivery] +** xref:eips:idempotentConsumer-eip.adoc[Idempotent Consumer] +** xref:eips:intercept.adoc[Intercept] +** xref:eips:kamelet-eip.adoc[Kamelet] +** xref:eips:loadBalance-eip.adoc[Load Balance] +** xref:eips:log-eip.adoc[Log] +** xref:eips:loop-eip.adoc[Loop] +** xref:eips:marshal-eip.adoc[Marshal EIP] +** xref:eips:message.adoc[Message] +** xref:eips:message-broker.adoc[Message Broker] +** xref:eips:message-bus.adoc[Message Bus] +** xref:eips:message-channel.adoc[Message Channel] +** xref:eips:message-dispatcher.adoc[Message Dispatcher] +** xref:eips:message-endpoint.adoc[Message Endpoint] +** xref:eips:message-expiration.adoc[Message Expiration] +** xref:eips:message-history.adoc[Message History] +** xref:eips:message-router.adoc[Message Router] +** xref:eips:message-translator.adoc[Message Translator] +** xref:eips:messaging-bridge.adoc[Messaging Bridge] +** xref:eips:messaging-gateway.adoc[Messaging Gateway] +** xref:eips:messaging-mapper.adoc[Messaging Mapper] +** xref:eips:multicast-eip.adoc[Multicast] +** xref:eips:normalizer.adoc[Normalizer] +** xref:eips:onFallback-eip.adoc[On Fallback] +** xref:eips:pipeline-eip.adoc[Pipeline] +** xref:eips:point-to-point-channel.adoc[Point to Point Channel] +** xref:eips:pollEnrich-eip.adoc[Poll Enrich] +** xref:eips:polling-consumer.adoc[Polling Consumer] +** xref:eips:process-eip.adoc[Process] +** xref:eips:process-manager.adoc[Process Manager] +** xref:eips:publish-subscribe-channel.adoc[Publish Subscribe Channel] +** xref:eips:random-eip.adoc[Random] +** xref:eips:recipientList-eip.adoc[Recipient List] +** xref:eips:removeHeader-eip.adoc[Remove Header] +** xref:eips:removeHeaders-eip.adoc[Remove Headers] +** xref:eips:removeProperties-eip.adoc[Remove Properties] +** xref:eips:removeProperty-eip.adoc[Remove Property] +** xref:eips:requestReply-eip.adoc[Request Reply] +** xref:eips:resequence-eip.adoc[Resequence] +** xref:eips:resilience4jConfiguration-eip.adoc[Resilience4j Configuration] +** xref:eips:resilience4j-eip.adoc[Resilience4j EIP] +** xref:eips:resume-strategies.adoc[Resume Strategies] +** xref:eips:return-address.adoc[Return Address] +** xref:eips:rollback-eip.adoc[Rollback] +** xref:eips:roundRobin-eip.adoc[Round Robin] +** xref:eips:routingSlip-eip.adoc[Routing Slip] +** xref:eips:saga-eip.adoc[Saga] +** xref:eips:sample-eip.adoc[Sample] +** xref:eips:scatter-gather.adoc[Scatter Gather] +** xref:eips:script-eip.adoc[Script] +** xref:eips:selective-consumer.adoc[Selective Consumer] +** xref:eips:service-activator.adoc[Service Activator] +** xref:eips:serviceCall-eip.adoc[Service Call] +** xref:eips:setBody-eip.adoc[Set Body] +** xref:eips:setHeader-eip.adoc[Set Header] +** xref:eips:setHeaders-eip.adoc[Set Headers] +** xref:eips:setProperty-eip.adoc[Set Property] +** xref:eips:sort-eip.adoc[Sort] +** xref:eips:split-eip.adoc[Split] +** xref:eips:step-eip.adoc[Step] +** xref:eips:sticky-eip.adoc[Sticky] +** xref:eips:stop-eip.adoc[Stop] +** xref:eips:stream-config-eip.adoc[Stream-config] +** xref:eips:threads-eip.adoc[Threads] +** xref:eips:throttle-eip.adoc[Throttle] +** xref:eips:to-eip.adoc[To] +** xref:eips:toD-eip.adoc[To D] +** xref:eips:topic-eip.adoc[Topic] +** xref:eips:transactional-client.adoc[Transactional Client] +** xref:eips:transform-eip.adoc[Transform] +** xref:eips:unmarshal-eip.adoc[Unmarshal EIP] +** xref:eips:validate-eip.adoc[Validate] +** xref:eips:weighted-eip.adoc[Weighted] +** xref:eips:wireTap-eip.adoc[Wire Tap] diff --git a/core/camel-core-engine/src/main/docs/modules/eips/pages/setHeader-eip.adoc b/core/camel-core-engine/src/main/docs/modules/eips/pages/setHeader-eip.adoc index 773f54a073a..f845e1924cf 100644 --- a/core/camel-core-engine/src/main/docs/modules/eips/pages/setHeader-eip.adoc +++ b/core/camel-core-engine/src/main/docs/modules/eips/pages/setHeader-eip.adoc @@ -113,3 +113,5 @@ XML:: </route> ---- ==== + +If you need to set several headers on the message, see xref:eips:setHeaders-eip.adoc[Set Headers]. diff --git a/core/camel-core-engine/src/main/docs/modules/eips/pages/setHeaders-eip.adoc b/core/camel-core-engine/src/main/docs/modules/eips/pages/setHeaders-eip.adoc index 6cc4013408f..2e5cbe6e288 100644 --- a/core/camel-core-engine/src/main/docs/modules/eips/pages/setHeaders-eip.adoc +++ b/core/camel-core-engine/src/main/docs/modules/eips/pages/setHeaders-eip.adoc @@ -97,6 +97,23 @@ XML:: <to uri="direct:b"/> </route> ---- + +YAML:: ++ +[source,yaml] +---- +- from: + uri: direct:a + steps: + - setHeaders: + headers: + - name: randomNumber + simple: "${random(1,100)}" + - name: body + simple: "${body}" + - to: + uri:direct:b +---- ==== === Setting a header from another header @@ -132,6 +149,25 @@ XML:: <to uri="direct:b"/> </route> ---- + +YAML:: ++ +[source,yaml] +---- +- from: + uri: direct:a + steps: + - setHeaders: + headers: + - name: foo + simple: "${body}" + - name: bar + simple: + expression: "${header.foo} > 10" + resultType: "boolean" + - to: + uri:direct:b +---- ==== === Using a Map with Java DSL @@ -153,3 +189,15 @@ from("direct:startMap") .to("direct:b"); ---- ==== + +If the ordering is not critical, then `Map.of(name1, expr1, name2, expr2...)` can be used. +==== +Java:: ++ +[source,java] +---- +from("direct:startMap") + .setHeaders(Map.of("foo", "ABC", "bar", "XYZ")) + .to("direct:b"); +---- +==== diff --git a/core/camel-core-model/src/main/java/org/apache/camel/model/ProcessorDefinition.java b/core/camel-core-model/src/main/java/org/apache/camel/model/ProcessorDefinition.java index e4f67115463..8f4e1920f4f 100644 --- a/core/camel-core-model/src/main/java/org/apache/camel/model/ProcessorDefinition.java +++ b/core/camel-core-model/src/main/java/org/apache/camel/model/ProcessorDefinition.java @@ -2582,23 +2582,11 @@ public abstract class ProcessorDefinition<Type extends ProcessorDefinition<Type> /** * Adds a processor which sets several headers on the IN message * - * @param headerMap a Map of header names and values - * @return the builder - */ - public Type setHeaders(Map<String, Expression> headerMap) { - SetHeadersDefinition answer = new SetHeadersDefinition(headerMap); - addOutput(answer); - return asType(); - } - - /** - * Adds a processor which sets several headers on the IN message - * - * @param headerNamesAndValues a sequence of header names and values + * @param headerNamesAndValues a sequence of header names and values or a Map containing names and values * @return the builder */ - public Type setHeaders(String headerName, Expression expr, Object... headerNamesAndValues) { - SetHeadersDefinition answer = new SetHeadersDefinition(headerName, expr, headerNamesAndValues); + public Type setHeaders(Object... headerNamesAndValues) { + SetHeadersDefinition answer = new SetHeadersDefinition(headerNamesAndValues); addOutput(answer); return asType(); } diff --git a/core/camel-core-model/src/main/java/org/apache/camel/model/SetHeadersDefinition.java b/core/camel-core-model/src/main/java/org/apache/camel/model/SetHeadersDefinition.java index 08fe85ba65c..28623993966 100644 --- a/core/camel-core-model/src/main/java/org/apache/camel/model/SetHeadersDefinition.java +++ b/core/camel-core-model/src/main/java/org/apache/camel/model/SetHeadersDefinition.java @@ -46,39 +46,43 @@ public class SetHeadersDefinition extends ProcessorDefinition<SetHeadersDefiniti public SetHeadersDefinition() { } - public SetHeadersDefinition(Map<String, Expression> setHeaderDefs) { - for (Entry<String, Expression> entry : setHeaderDefs.entrySet()) { - headers.add(new SetHeaderDefinition(entry.getKey(), entry.getValue())); - } - } - /** * Allow setting multiple headers using a single expression. */ - public SetHeadersDefinition(String header, Expression expr, Object... headerNamesAndExprs) { - createSetHeaderDefinitions(header, expr, headerNamesAndExprs); + public SetHeadersDefinition(Object... headerNamesAndExprs) { + createSetHeaderDefinitions(headerNamesAndExprs); } - private void createSetHeaderDefinitions(String header, Expression expr, Object[] headerNamesAndExprs) { - if (headerNamesAndExprs.length % 2 != 0) { - throw new IllegalArgumentException("Must have an even number of arguments!"); - } - headers.add(new SetHeaderDefinition(header, expr)); - for (int i = 0; i < headerNamesAndExprs.length; i += 2) { - Object key = headerNamesAndExprs[i]; - Object value = headerNamesAndExprs[i + 1]; - if (!(key instanceof String)) { - throw new IllegalArgumentException("Keys must be Strings"); + private void createSetHeaderDefinitions(Object[] headerNamesAndExprs) { + if (headerNamesAndExprs.length == 1 && headerNamesAndExprs[0] instanceof Map) { + createHeadersFromMap((Map<?, ?>) headerNamesAndExprs[0]); + } else if (headerNamesAndExprs.length % 2 != 0) { + throw new IllegalArgumentException("Must be a Map or have an even number of arguments!"); + } else { + for (int i = 0; i < headerNamesAndExprs.length; i += 2) { + addHeader(headerNamesAndExprs[i], headerNamesAndExprs[i + 1]); } - if (value instanceof String) { - value = ExpressionBuilder.constantExpression(value); - } else if (!(value instanceof Expression)) { - throw new IllegalArgumentException("Values must be Expressions or Strings"); - } - headers.add(new SetHeaderDefinition((String) key, (Expression) value)); } } + private void addHeader(Object key, Object value) { + if (!(key instanceof String)) { + throw new IllegalArgumentException("Keys must be Strings"); + } + if (!(value instanceof Expression)) { + // Assume it's a constant of some kind + value = ExpressionBuilder.constantExpression(value); + } + headers.add(new SetHeaderDefinition((String) key, (Expression) value)); + } + + private void createHeadersFromMap(Map<?, ?> headerMap) { + for (Entry<?, ?> entry : headerMap.entrySet()) { + addHeader(entry.getKey(), entry.getValue()); + } + + } + public List<SetHeaderDefinition> getHeaders() { return headers; } diff --git a/core/camel-core/src/test/java/org/apache/camel/model/SetHeadersDefinitionTest.java b/core/camel-core/src/test/java/org/apache/camel/model/SetHeadersDefinitionTest.java index 077dc39326a..3739c16ff4b 100644 --- a/core/camel-core/src/test/java/org/apache/camel/model/SetHeadersDefinitionTest.java +++ b/core/camel-core/src/test/java/org/apache/camel/model/SetHeadersDefinitionTest.java @@ -17,6 +17,7 @@ package org.apache.camel.model; import java.util.Map; +import java.util.Set; import org.apache.camel.Expression; import org.apache.camel.TestSupport; @@ -38,6 +39,20 @@ class SetHeadersDefinitionTest extends TestSupport { assertEquals("isCamel", setHeadersDef.getHeaders().get(1).getName()); } + @Test + void testSetFromMapOf() { + SetHeadersDefinition setHeadersDef = new SetHeadersDefinition( + Map.of("fromBody", body(), + "isCamel", body().contains("Camel"), "isHorse", body().contains("Horse"))); + assertNotNull(setHeadersDef.getHeaders()); + assertEquals(3, setHeadersDef.getHeaders().size()); + Set<String> names = new java.util.HashSet<>(); + for (SetHeaderDefinition setHdrDef : setHeadersDef.getHeaders()) { + names.add(setHdrDef.getName()); + } + assertEquals(names, Set.of("fromBody", "isCamel", "isHorse")); + } + @Test void testSetFromVarargs() { SetHeadersDefinition setHeadersDef = new SetHeadersDefinition( diff --git a/core/camel-core/src/test/java/org/apache/camel/processor/SetHeadersProcessorTest.java b/core/camel-core/src/test/java/org/apache/camel/processor/SetHeadersProcessorTest.java index a5c8c7bcd70..a64929c6410 100644 --- a/core/camel-core/src/test/java/org/apache/camel/processor/SetHeadersProcessorTest.java +++ b/core/camel-core/src/test/java/org/apache/camel/processor/SetHeadersProcessorTest.java @@ -89,6 +89,20 @@ public class SetHeadersProcessorTest extends ContextTestSupport { assertMockEndpointsSatisfied(); } + @Test + public void testUseMapOf() throws Exception { + context.addRoutes(new RouteBuilder() { + public void configure() throws Exception { + from("direct:startMap").setHeaders(Map.of("foo", "ABC", "bar", "XYZ")).to("mock:result"); + } + }); + ; + expected.message(0).header("foo").isEqualTo("ABC"); + expected.message(0).header("bar").isEqualTo("XYZ"); + template.sendBody("direct:startMap", body); + assertMockEndpointsSatisfied(); + } + @Test public void testUseMethod() throws Exception { String hdrInBody = "foo,ABC,bar,XYZ"; diff --git a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/SetHeadersTest.groovy b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/SetHeadersTest.groovy index 6fed86c86af..586156c660a 100644 --- a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/SetHeadersTest.groovy +++ b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/SetHeadersTest.groovy @@ -38,7 +38,7 @@ class SetHeadersTest extends YamlTestSupport { - name: testbody simple: "${body}" - name: testconstant - constant: "ABC" + constant: ABC - to: "mock:result" ''' then: @@ -59,5 +59,34 @@ class SetHeadersTest extends YamlTestSupport { } } } + + def "setHeaders resultType"() { + when: + loadRoutes ''' + - from: + uri: "direct:start" + steps: + - setHeaders: + headers: + - name: foo + simple: "${body}" + - name: bar + simple: + expression: "${header.foo} > 10" + resultType: "boolean" + - to: "mock:result" + ''' + then: + with(context.routeDefinitions[0].outputs[0], SetHeadersDefinition) { + with(it.headers[1], SetHeaderDefinition) { + name == 'bar' + with(expression, ExpressionDefinition) { + language == 'simple' + expression == '${header.foo} > 10' + resultTypeName == 'boolean' + } + } + } + } }