From 905f004da91ff4e6acb9b22ad6116a4e8d096359 Mon Sep 17 00:00:00 2001
From: James D Bloom <733179+jamesdbloom@users.noreply.github.com>
Date: Wed, 16 Mar 2022 20:57:44 +0000
Subject: [PATCH] #1052 added support for numerous velocity tools for example
for JSON and XML parsing to velocity response templates
---
changelog.md | 1 +
.../mock_server/response_templates.html | 166 ++++++++++++++----
mockserver-core/pom.xml | 5 +
.../javascript/JavaScriptTemplateEngine.java | 4 +-
.../mustache/MustacheTemplateEngine.java | 4 +-
.../velocity/VelocityTemplateEngine.java | 96 +++++++---
.../mustache/MustacheTemplateEngineTest.java | 3 -
.../velocity/VelocityTemplateEngineTest.java | 104 ++++++++++-
pom.xml | 17 ++
9 files changed, 328 insertions(+), 72 deletions(-)
diff --git a/changelog.md b/changelog.md
index 21bf481e8..7a3b20e4d 100644
--- a/changelog.md
+++ b/changelog.md
@@ -25,6 +25,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- added response template variables for date, uuid and random for javascript
- added path parameters, remote address and client certificate chain to response template model
- added support for EMCAScript 6 in JavaScript response templates for Java versions between 9 and 15
+- added support for numerous velocity tools for example for JSON and XML parsing to velocity response templates
### Changed
- included Bouncy Castle now used by default to resolve issues with modules in Java 16+ and backwards compatibility for Java 8
diff --git a/jekyll-www.mock-server.com/mock_server/response_templates.html b/jekyll-www.mock-server.com/mock_server/response_templates.html
index 9d36cce9a..e19328d65 100644
--- a/jekyll-www.mock-server.com/mock_server/response_templates.html
+++ b/jekyll-www.mock-server.com/mock_server/response_templates.html
@@ -156,19 +156,19 @@
Request Model Variables
mustache: |
- {{ request.pathParameters.<key>.<index> }} |
+ {{ request.pathParameters.<key&ft;.<index&ft; }} |
velocity: |
- $!request.pathParameters[<key>][<index>] |
+ $!request.pathParameters[<key&ft;][<index&ft;] |
javascript: |
- request.pathParameters[<key>][<index>] |
+ request.pathParameters[<key&ft;][<index&ft;] |
@@ -176,19 +176,19 @@ Request Model Variables
mustache: |
- {{ request.queryStringParameters.<key>.<index> }} |
+ {{ request.queryStringParameters.<key&ft;.<index&ft; }} |
velocity: |
- $!request.queryStringParameters[<key>][<index>] |
+ $!request.queryStringParameters[<key&ft;][<index&ft;] |
javascript: |
- request.queryStringParameters[<key>][<index>] |
+ request.queryStringParameters[<key&ft;][<index&ft;] |
@@ -196,19 +196,19 @@ Request Model Variables
mustache: |
- {{ request.headers.<key>.<index> }} |
+ {{ request.headers.<key&ft;.<index&ft; }} |
velocity: |
- $!request.headers[<key>][<index>] |
+ $!request.headers[<key&ft;][<index&ft;] |
javascript: |
- request.headers[<key>][<index>] |
+ request.headers[<key&ft;][<index&ft;] |
@@ -216,19 +216,19 @@ Request Model Variables
mustache: |
- {{ request.cookies.<key> }} |
+ {{ request.cookies.<key&ft; }} |
velocity: |
- $!request.cookies[<key>] |
+ $!request.cookies[<key&ft;] |
javascript: |
- request.cookies[<key>] |
+ request.cookies[<key&ft;] |
@@ -365,19 +365,19 @@ Request Multi-Value And Single Value Maps
mustache: |
- {{ request.headers.<key>.<index> }}
{{ request.queryStringParameters.<key>.<index> }}
{{ request.headers.<key>.<index> }} |
+ {{ request.headers.<key&ft;.<index&ft; }}
{{ request.queryStringParameters.<key&ft;.<index&ft; }}
{{ request.headers.<key&ft;.<index&ft; }} |
velocity: |
- $!request.pathParameters[<key>][<index>]
$!request.queryStringParameters[<key>][<index>]
$!request.headers[<key>][<index>]
|
+ $!request.pathParameters[<key&ft;][<index&ft;]
$!request.queryStringParameters[<key&ft;][<index&ft;]
$!request.headers[<key&ft;][<index&ft;]
|
javascript: |
- request.pathParameters[<key>][<index>]
request.queryStringParameters[<key>][<index>]
request.headers[<key>][<index>]
|
+ request.pathParameters[<key&ft;][<index&ft;]
request.queryStringParameters[<key&ft;][<index&ft;]
request.headers[<key&ft;][<index&ft;]
|
@@ -879,27 +879,27 @@ XPath
Given a request with the following xml body:
-<?xml version="1.0" encoding="UTF-8" ?>
-<store>
- <book>
- <category>reference</category>
- <author>Nigel Rees</author>
- <title>Sayings of the Century</title>
- <price>18.95</price>
- </book>
- <book>
- <category>fiction</category>
- <author>Herman Melville</author>
- <title>Moby Dick</title>
- <isbn>0-553-21311-3</isbn>
- <price>8.99</price>
- </book>
- <bicycle>
- <color>red</color>
- <price>19.95</price>
- </bicycle>
- <expensive>10</expensive>
-</store>
+<?xml version="1.0" encoding="UTF-8" ?&ft;
+<store&ft;
+ <book&ft;
+ <category&ft;reference</category&ft;
+ <author&ft;Nigel Rees</author&ft;
+ <title&ft;Sayings of the Century</title&ft;
+ <price&ft;18.95</price&ft;
+ </book&ft;
+ <book&ft;
+ <category&ft;fiction</category&ft;
+ <author&ft;Herman Melville</author&ft;
+ <title&ft;Moby Dick</title&ft;
+ <isbn&ft;0-553-21311-3</isbn&ft;
+ <price&ft;8.99</price&ft;
+ </book&ft;
+ <bicycle&ft;
+ <color&ft;red</color&ft;
+ <price&ft;19.95</price&ft;
+ </bicycle&ft;
+ <expensive&ft;10</expensive&ft;
+</store&ft;
The example produces:
@@ -1003,6 +1003,100 @@ Mathematical
$item
#end
+For additional mathematical functionality it is also possible to use the MathTool or NumberTool in velocity response templates.
+
+#set($power = $math.pow($number, 2))
+#set($max = $math.max($number, 10))
+
+Json Bodies
+
+The JsonTool can be used to help parse JSON bodies, as follows:
+
+#set($jsonBody = $json.parse($!request.body))
+
+{
+ 'statusCode': 200,
+ 'body': "{'titles': [#foreach( $book in $jsonBody.store.book )'$book.title'#if( $foreach.hasNext ), #end#end], 'bikeColor': '$jsonBody.store.bicycle.color'}"
+}
+
+Given the following request:
+
+{
+ "path" : "/somePath",
+ "body" : {
+ "type" : "JSON",
+ "json" : {
+ "store" : {
+ "book" : [ {
+ "category" : "reference",
+ "author" : "Nigel Rees",
+ "title" : "Sayings of the Century",
+ "price" : 18.95
+ }, {
+ "category" : "fiction",
+ "author" : "Herman Melville",
+ "title" : "Moby Dick",
+ "isbn" : "0-553-21311-3",
+ "price" : 8.99
+ } ],
+ "bicycle" : {
+ "color" : "red",
+ "price" : 19.95
+ }
+ },
+ "expensive" : 10
+ }
+ }
+}
+
+The example produces:
+
+{
+ "statusCode" : 200,
+ "body" : "{'titles': ['Sayings of the Century', 'Moby Dick'], 'bikeColor': 'red'}"
+}
+
+XML Bodies
+
+The XmlTool can be used to help parse XML bodies, execute XPath and XML traversal.
+
+The following example shows how to use XmlTool for XPath:
+
+#set($xmlBody = $xml.parse($!request.body))
+
+{
+ 'statusCode': 200,
+ 'body': "{'key': '$xml.find('/element/key/text()')', 'value': '$xml.find('/element/value/text()')'}"
+}
+
+Given the following request:
+
+{
+ "path" : "/somePath",
+ "body" : "<element><key>some_key</key><value>some_value</value></element>"
+}
+
+The example produces:
+
+{
+ "statusCode" : 200,
+ "body" : "{'key': 'some_key', 'value': 'some_value'}"
+}
+
+Velocity Tools
+
+The following velocity tools are available for velocity response templates:
+
+
JavaScript Response Templates
diff --git a/mockserver-core/pom.xml b/mockserver-core/pom.xml
index 277768c2b..681b5a2c8 100644
--- a/mockserver-core/pom.xml
+++ b/mockserver-core/pom.xml
@@ -111,6 +111,11 @@
+
+ org.apache.velocity.tools
+ velocity-tools-generic
+ 3.1
+
com.samskivert
jmustache
diff --git a/mockserver-core/src/main/java/org/mockserver/templates/engine/javascript/JavaScriptTemplateEngine.java b/mockserver-core/src/main/java/org/mockserver/templates/engine/javascript/JavaScriptTemplateEngine.java
index 348f88c48..5bd45cf91 100644
--- a/mockserver-core/src/main/java/org/mockserver/templates/engine/javascript/JavaScriptTemplateEngine.java
+++ b/mockserver-core/src/main/java/org/mockserver/templates/engine/javascript/JavaScriptTemplateEngine.java
@@ -73,10 +73,10 @@ public T executeTemplate(String template, HttpRequest request, Class exten
try {
generatedObject = objectMapper.readTree(String.valueOf(stringifiedResponse));
} catch (Throwable throwable) {
- if (MockServerLogger.isEnabled(Level.TRACE)) {
+ if (MockServerLogger.isEnabled(Level.INFO)) {
mockServerLogger.logEvent(
new LogEntry()
- .setLogLevel(Level.TRACE)
+ .setLogLevel(Level.INFO)
.setHttpRequest(request)
.setMessageFormat("exception deserialising generated content:{}into json node for request:{}")
.setArguments(stringifiedResponse, request)
diff --git a/mockserver-core/src/main/java/org/mockserver/templates/engine/mustache/MustacheTemplateEngine.java b/mockserver-core/src/main/java/org/mockserver/templates/engine/mustache/MustacheTemplateEngine.java
index 9f0c87757..169bdae08 100644
--- a/mockserver-core/src/main/java/org/mockserver/templates/engine/mustache/MustacheTemplateEngine.java
+++ b/mockserver-core/src/main/java/org/mockserver/templates/engine/mustache/MustacheTemplateEngine.java
@@ -73,10 +73,10 @@ public T executeTemplate(String template, HttpRequest request, Class exten
try {
generatedObject = objectMapper.readTree(writer.toString());
} catch (Throwable throwable) {
- if (MockServerLogger.isEnabled(Level.TRACE)) {
+ if (MockServerLogger.isEnabled(Level.INFO)) {
mockServerLogger.logEvent(
new LogEntry()
- .setLogLevel(Level.TRACE)
+ .setLogLevel(Level.INFO)
.setHttpRequest(request)
.setMessageFormat("exception deserialising generated content:{}into json node for request:{}")
.setArguments(writer.toString(), request)
diff --git a/mockserver-core/src/main/java/org/mockserver/templates/engine/velocity/VelocityTemplateEngine.java b/mockserver-core/src/main/java/org/mockserver/templates/engine/velocity/VelocityTemplateEngine.java
index 106732972..f3c39b88d 100644
--- a/mockserver-core/src/main/java/org/mockserver/templates/engine/velocity/VelocityTemplateEngine.java
+++ b/mockserver-core/src/main/java/org/mockserver/templates/engine/velocity/VelocityTemplateEngine.java
@@ -2,9 +2,18 @@
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.common.collect.ImmutableSet;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.Velocity;
+import org.apache.velocity.app.VelocityEngine;
import org.apache.velocity.runtime.RuntimeConstants;
import org.apache.velocity.script.VelocityScriptEngine;
import org.apache.velocity.script.VelocityScriptEngineFactory;
+import org.apache.velocity.tools.ToolContext;
+import org.apache.velocity.tools.ToolManager;
+import org.apache.velocity.tools.ToolboxFactory;
+import org.apache.velocity.tools.config.*;
+import org.mockserver.file.FilePath;
import org.mockserver.log.model.LogEntry;
import org.mockserver.logging.MockServerLogger;
import org.mockserver.model.HttpRequest;
@@ -40,38 +49,73 @@
@SuppressWarnings("FieldMayBeFinal")
public class VelocityTemplateEngine implements TemplateEngine {
- private static final Properties velocityProperties;
- private static final ScriptEngineManager manager;
- private static final ScriptEngine engine;
+ private static final VelocityEngine velocityEngine;
+ private static final ToolContext toolContext;
private static ObjectMapper objectMapper;
private final MockServerLogger mockServerLogger;
private HttpTemplateOutputDeserializer httpTemplateOutputDeserializer;
static {
// See: https://velocity.apache.org/engine/2.0/configuration.html
- velocityProperties = new Properties();
- velocityProperties.put("runtime.log.log_invalid_references", "true");
- velocityProperties.put("runtime.string_interning", "true");
- velocityProperties.put("directive.foreach.max_loops", "-1");
- velocityProperties.put("directive.if.empty_check", "true");
- velocityProperties.put("directive.parse.max_depth", "10");
+ Properties velocityProperties = new Properties();
+ velocityProperties.put(RuntimeConstants.RUNTIME_LOG_REFERENCE_LOG_INVALID, "true");
+ velocityProperties.put(RuntimeConstants.RUNTIME_STRING_INTERNING, "true");
+ velocityProperties.put(RuntimeConstants.MAX_NUMBER_LOOPS, "-1");
+ velocityProperties.put(RuntimeConstants.CHECK_EMPTY_OBJECTS, "true");
+ velocityProperties.put(RuntimeConstants.PARSE_DIRECTIVE_MAXDEPTH, "10");
+ velocityProperties.put(RuntimeConstants.RUNTIME_REFERENCES_STRICT, "false");
velocityProperties.put("context.scope_control.template", "false");
velocityProperties.put("context.scope_control.evaluate", "false");
velocityProperties.put("context.scope_control.foreach", "true");
velocityProperties.put("context.scope_control.macro", "false");
velocityProperties.put("context.scope_control.define", "false");
- velocityProperties.put("runtime.strict_mode.enable", "false");
- velocityProperties.put("runtime.interpolate_string_literals", "true");
- velocityProperties.put("resource.default_encoding", "UTF-8");
velocityProperties.put("directive.set.null.allowed", "true");
- velocityProperties.put("parser.pool.class", "org.apache.velocity.runtime.ParserPoolImpl");
- velocityProperties.put("parser.pool.size", "50");
- velocityProperties.put("parser.space_gobbling", "lines");
- velocityProperties.put("parser.allow_hyphen_in_identifiers", "true");
+ velocityProperties.put(RuntimeConstants.INTERPOLATE_STRINGLITERALS, "true");
+ velocityProperties.put(RuntimeConstants.INPUT_ENCODING, "UTF-8");
+ velocityProperties.put(RuntimeConstants.PARSER_POOL_CLASS, "org.apache.velocity.runtime.ParserPoolImpl");
+ velocityProperties.put(RuntimeConstants.PARSER_POOL_SIZE, "50");
+ velocityProperties.put(RuntimeConstants.SPACE_GOBBLING, "lines");
+ velocityProperties.put(RuntimeConstants.PARSER_HYPHEN_ALLOWED, "true");
velocityProperties.put(RuntimeConstants.CUSTOM_DIRECTIVES, Ifnull.class.getName());
- manager = new ScriptEngineManager();
- manager.registerEngineName("velocity", new VelocityScriptEngineFactory());
- engine = manager.getEngineByName("velocity");
+ velocityEngine = new VelocityEngine();
+ velocityEngine.init(velocityProperties);
+
+ ToolManager manager = new ToolManager();
+
+ ToolboxConfiguration applicationToolboxConfiguration = new ToolboxConfiguration();
+ applicationToolboxConfiguration.setScope("application");
+ ToolConfiguration collectionTool = new ToolConfiguration();
+ collectionTool.setClass(org.apache.velocity.tools.generic.CollectionTool.class.getName());
+ applicationToolboxConfiguration.addTool(collectionTool);
+ ToolConfiguration comparisonDateTool = new ToolConfiguration();
+ comparisonDateTool.setClass(org.apache.velocity.tools.generic.ComparisonDateTool.class.getName());
+ applicationToolboxConfiguration.addTool(comparisonDateTool);
+ ToolConfiguration displayTool = new ToolConfiguration();
+ displayTool.setClass(org.apache.velocity.tools.generic.DisplayTool.class.getName());
+ applicationToolboxConfiguration.addTool(displayTool);
+ ToolConfiguration escapeTool = new ToolConfiguration();
+ escapeTool.setClass(org.apache.velocity.tools.generic.EscapeTool.class.getName());
+ applicationToolboxConfiguration.addTool(escapeTool);
+ ToolConfiguration mathTool = new ToolConfiguration();
+ mathTool.setClass(org.apache.velocity.tools.generic.MathTool.class.getName());
+ applicationToolboxConfiguration.addTool(mathTool);
+ ToolConfiguration numberTool = new ToolConfiguration();
+ numberTool.setClass(org.apache.velocity.tools.generic.NumberTool.class.getName());
+ applicationToolboxConfiguration.addTool(numberTool);
+ ToolboxConfiguration requestToolboxConfiguration = new ToolboxConfiguration();
+ requestToolboxConfiguration.setScope("request");
+ ToolConfiguration jsonTool = new ToolConfiguration();
+ jsonTool.setClass(org.apache.velocity.tools.generic.JsonTool.class.getName());
+ requestToolboxConfiguration.addTool(jsonTool);
+ ToolConfiguration xmlTool = new ToolConfiguration();
+ xmlTool.setClass(org.apache.velocity.tools.generic.XmlTool.class.getName());
+ requestToolboxConfiguration.addTool(xmlTool);
+ XmlFactoryConfiguration xmlFactoryConfiguration = new XmlFactoryConfiguration();
+ xmlFactoryConfiguration.addToolbox(applicationToolboxConfiguration);
+ xmlFactoryConfiguration.addToolbox(requestToolboxConfiguration);
+ manager.configure(xmlFactoryConfiguration);
+ manager.setVelocityEngine(velocityEngine);
+ toolContext = manager.createContext();
}
public VelocityTemplateEngine(MockServerLogger mockServerLogger) {
@@ -87,20 +131,18 @@ public T executeTemplate(String template, HttpRequest request, Class exten
T result;
try {
Writer writer = new StringWriter();
- ScriptContext context = new SimpleScriptContext();
- context.setWriter(writer);
- context.setAttribute(VelocityScriptEngine.VELOCITY_PROPERTIES_KEY, velocityProperties, ScriptContext.ENGINE_SCOPE);
- context.setAttribute("request", new HttpRequestTemplateObject(request), ScriptContext.ENGINE_SCOPE);
- TemplateFunctions.BUILT_IN_FUNCTIONS.forEach((key, value) -> context.setAttribute(key, value, ScriptContext.ENGINE_SCOPE));
- engine.eval(template, context);
+ VelocityContext context = new VelocityContext(toolContext);
+ context.put("request", new HttpRequestTemplateObject(request));
+ TemplateFunctions.BUILT_IN_FUNCTIONS.forEach(context::put);
+ velocityEngine.evaluate(context, writer, "VelocityResponseTemplate", template);
JsonNode generatedObject = null;
try {
generatedObject = objectMapper.readTree(writer.toString());
} catch (Throwable throwable) {
- if (MockServerLogger.isEnabled(Level.TRACE)) {
+ if (MockServerLogger.isEnabled(Level.INFO)) {
mockServerLogger.logEvent(
new LogEntry()
- .setLogLevel(Level.TRACE)
+ .setLogLevel(Level.INFO)
.setHttpRequest(request)
.setMessageFormat("exception deserialising generated content:{}into json node for request:{}")
.setArguments(writer.toString(), request)
diff --git a/mockserver-core/src/test/java/org/mockserver/templates/engine/mustache/MustacheTemplateEngineTest.java b/mockserver-core/src/test/java/org/mockserver/templates/engine/mustache/MustacheTemplateEngineTest.java
index 873af93c3..4a4ef055c 100644
--- a/mockserver-core/src/test/java/org/mockserver/templates/engine/mustache/MustacheTemplateEngineTest.java
+++ b/mockserver-core/src/test/java/org/mockserver/templates/engine/mustache/MustacheTemplateEngineTest.java
@@ -591,7 +591,6 @@ public void shouldHandleHttpRequestsWithMustacheResponseTemplateWithXPathWithStr
@Test
public void shouldHandleHttpRequestsWithMustacheResponseTemplateWithJsonPath() throws JsonProcessingException {
// given
- ConfigurationProperties.logLevel("TRACE");
String template = "{" + NEW_LINE +
" 'statusCode': 200," + NEW_LINE +
" 'body': \"{'titles': {{#jsonPath}}$.store.book{{/jsonPath}}[{{#jsonPathResult}}{{^-first}}, {{/-first}}'{{title}}'{{/jsonPathResult}}], 'bikeColor': '{{#jsonPath}}$.store.bicycle.color{{/jsonPath}}{{jsonPathResult}}'}\"" + NEW_LINE +
@@ -652,7 +651,6 @@ public void shouldHandleHttpRequestsWithMustacheResponseTemplateWithJsonPath() t
@Test
public void shouldHandleHttpRequestsWithMustacheResponseTemplateWithJsonPathWithXmlBody() throws JsonProcessingException {
// given
- ConfigurationProperties.logLevel("TRACE");
String template = "{" + NEW_LINE +
" 'statusCode': 200," + NEW_LINE +
" 'body': \"{'key': '{{#jsonPath}}$.store.book[0].title{{/jsonPath}}', 'value': '{{#jsonPath}}$.store.bicycle.color{{/jsonPath}}'}\"" + NEW_LINE +
@@ -712,7 +710,6 @@ public void shouldHandleHttpRequestsWithMustacheResponseTemplateWithJsonPathWith
@Test
public void shouldHandleHttpRequestsWithMustacheResponseTemplateWithJsonPathWithStringBody() throws JsonProcessingException {
// given
- ConfigurationProperties.logLevel("TRACE");
String template = "{" + NEW_LINE +
" 'statusCode': 200," + NEW_LINE +
" 'body': \"{'key': '{{#jsonPath}}$.store.book[0].title{{/jsonPath}}', 'value': '{{#jsonPath}}$.store.bicycle.color{{/jsonPath}}'}\"" + NEW_LINE +
diff --git a/mockserver-core/src/test/java/org/mockserver/templates/engine/velocity/VelocityTemplateEngineTest.java b/mockserver-core/src/test/java/org/mockserver/templates/engine/velocity/VelocityTemplateEngineTest.java
index 146779925..ead040e0e 100644
--- a/mockserver-core/src/test/java/org/mockserver/templates/engine/velocity/VelocityTemplateEngineTest.java
+++ b/mockserver-core/src/test/java/org/mockserver/templates/engine/velocity/VelocityTemplateEngineTest.java
@@ -41,6 +41,7 @@
import static org.mockserver.log.model.LogEntry.LogMessageType.TEMPLATE_GENERATED;
import static org.mockserver.model.HttpRequest.request;
import static org.mockserver.model.HttpResponse.response;
+import static org.mockserver.model.JsonBody.json;
import static org.slf4j.event.Level.INFO;
/**
@@ -250,7 +251,7 @@ private void shouldPopulateRandomValue(String function, Matcher matcher
}
@Test
- public void shouldHandleHttpRequestsWithVelocityResponseTemplateWithLoopOverValuesUsingThis() throws JsonProcessingException {
+ public void shouldHandleHttpRequestsWithVelocityResponseTemplateWithLoopOverValues() throws JsonProcessingException {
// given
String template = "{" + NEW_LINE +
" 'statusCode': 200," + NEW_LINE +
@@ -336,6 +337,105 @@ public void shouldHandleHttpRequestsWithVelocityResponseTemplateWithIfElse() thr
);
}
+ @Test
+ public void shouldHandleHttpRequestsWithVelocityResponseTemplateWithXPath() throws JsonProcessingException {
+ // given
+ String template = "#set($xmlBody = $xml.parse($!request.body))" + NEW_LINE +
+ "{" + NEW_LINE +
+ " 'statusCode': 200," + NEW_LINE +
+ " 'body': \"{'key': '$xml.find('/element/key/text()')', 'value': '$xml.find('/element/value/text()')'}\"" + NEW_LINE +
+ "}";
+ HttpRequest request = request()
+ .withPath("/somePath")
+ .withBody("some_keysome_value");
+
+ // when
+ HttpResponse actualHttpResponse = new VelocityTemplateEngine(mockServerLogger).executeTemplate(template, request, HttpResponseDTO.class);
+
+ // then
+ assertThat(actualHttpResponse, is(
+ response()
+ .withStatusCode(200)
+ .withBody("{'key': 'some_key', 'value': 'some_value'}")
+ ));
+ verify(mockServerLogger).logEvent(
+ new LogEntry()
+ .setType(TEMPLATE_GENERATED)
+ .setLogLevel(INFO)
+ .setHttpRequest(request)
+ .setMessageFormat("generated output:{}from template:{}for request:{}")
+ .setArguments(OBJECT_MAPPER.readTree("" +
+ "{" + NEW_LINE +
+ " 'statusCode': 200," + NEW_LINE +
+ " 'body': \"{'key': 'some_key', 'value': 'some_value'}\"" + NEW_LINE +
+ "}" + NEW_LINE),
+ template,
+ request
+ )
+ );
+ }
+
+ @Test
+ public void shouldHandleHttpRequestsWithVelocityResponseTemplateWithJsonParsing() throws JsonProcessingException {
+ // given
+ String template = "#set($jsonBody = $json.parse($!request.body))" + NEW_LINE +
+ "{" + NEW_LINE +
+ " 'statusCode': 200," + NEW_LINE +
+ " 'body': \"{'titles': [#foreach( $book in $jsonBody.store.book )'$book.title'#if( $foreach.hasNext ), #end#end], 'bikeColor': '$jsonBody.store.bicycle.color'}\"" + NEW_LINE +
+ "}";
+ HttpRequest request = request()
+ .withPath("/somePath")
+ .withBody(json("{" + NEW_LINE +
+ " \"store\": {" + NEW_LINE +
+ " \"book\": [" + NEW_LINE +
+ " {" + NEW_LINE +
+ " \"category\": \"reference\"," + NEW_LINE +
+ " \"author\": \"Nigel Rees\"," + NEW_LINE +
+ " \"title\": \"Sayings of the Century\"," + NEW_LINE +
+ " \"price\": 18.95" + NEW_LINE +
+ " }," + NEW_LINE +
+ " {" + NEW_LINE +
+ " \"category\": \"fiction\"," + NEW_LINE +
+ " \"author\": \"Herman Melville\"," + NEW_LINE +
+ " \"title\": \"Moby Dick\"," + NEW_LINE +
+ " \"isbn\": \"0-553-21311-3\"," + NEW_LINE +
+ " \"price\": 8.99" + NEW_LINE +
+ " }" + NEW_LINE +
+ " ]," + NEW_LINE +
+ " \"bicycle\": {" + NEW_LINE +
+ " \"color\": \"red\"," + NEW_LINE +
+ " \"price\": 19.95" + NEW_LINE +
+ " }" + NEW_LINE +
+ " }," + NEW_LINE +
+ " \"expensive\": 10" + NEW_LINE +
+ "}"));
+
+ // when
+ HttpResponse actualHttpResponse = new VelocityTemplateEngine(mockServerLogger).executeTemplate(template, request, HttpResponseDTO.class);
+
+ // then
+ assertThat(actualHttpResponse, is(
+ response()
+ .withStatusCode(200)
+ .withBody("{'titles': ['Sayings of the Century', 'Moby Dick'], 'bikeColor': 'red'}")
+ ));
+ verify(mockServerLogger).logEvent(
+ new LogEntry()
+ .setType(TEMPLATE_GENERATED)
+ .setLogLevel(INFO)
+ .setHttpRequest(request)
+ .setMessageFormat("generated output:{}from template:{}for request:{}")
+ .setArguments(OBJECT_MAPPER.readTree("" +
+ "{" + NEW_LINE +
+ " 'statusCode': 200," + NEW_LINE +
+ " 'body': \"{'titles': ['Sayings of the Century', 'Moby Dick'], 'bikeColor': 'red'}\"" + NEW_LINE +
+ "}" + NEW_LINE),
+ template,
+ request
+ )
+ );
+ }
+
@Test
public void shouldHandleHttpRequestsWithVelocityForwardTemplateWithPathBodyParametersAndCookies() throws JsonProcessingException {
// given
@@ -448,7 +548,7 @@ public void shouldHandleInvalidVelocityTemplate() {
// then
assertThat(runtimeException.getMessage(), is("Exception:" + NEW_LINE +
"" + NEW_LINE +
- " org.apache.velocity.exception.ParseErrorException: Encountered \"{\" at [line 1, column 5]" + NEW_LINE +
+ " Encountered \"{\" at VelocityResponseTemplate[line 1, column 5]" + NEW_LINE +
" Was expecting one of:" + NEW_LINE +
" \"(\" ..." + NEW_LINE +
" ..." + NEW_LINE +
diff --git a/pom.xml b/pom.xml
index 5fd79d439..24b1b492e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -289,6 +289,11 @@
velocity-engine-core
${velocity.version}
+
+ org.apache.velocity.tools
+ velocity-tools-generic
+ 3.1
+
com.samskivert
jmustache
@@ -502,6 +507,18 @@
jackson-dataformat-yaml
${jackson.version}
+
+
+ commons-beanutils
+ commons-beanutils
+ 1.9.4
+
+
+
+ commons-logging
+ commons-logging
+ 1.2
+