Dynamic responses

« Back to documentation home

Generate responses

You can use several types of extensions to add dynamic content to response headers and response body.

For example, if you create a response with body { "Number": "{{randomInteger}}", "Date": "{{httpNow}}" } it will be rendered as { "Number": "165007562", "Date": "Sat, 02 Apr 2017 18:41:35 GMT" }

Below you will find all available extensions.

Tutorial: Dynamic responses

For a basic introduction to dynamic responses with examples have a look at Dynamic responses tutorial.

Load CSV data file

Look up by row and column

Save your data in trafficparrot-release-x.y.z/data/data.csv in CSV format.

To load the data in the response use {{csvDataFile 'row,col'}} in the response definition.

For example, {{csvDataFile '2,4'}} will read file trafficparrot-release-x.y.z/data/data.csv then load the 2nd row in the file and then lookup the 4th column value.

Select data using condition

For more advanced CSV data loading, you can put your CSV file with any name in the trafficparrot-release-x.y.z/data directory.

To select data from a CSV use {{select 'a from x.csv where b equals' value}} in the response definition.

For example, {{select 'title from jobs.csv where name equals' 'example' }} will read the file trafficparrot-release-x.y.z/data/titles.csv then load the value of the column title where the value of the column name is equal to the value example.

The value parameter passed to the {{select}} can be a dynamic value that is taken from the request e.g. xPath or jsonPath.

Generate random values

To generate a random value in the response use one of the following:

  1. {{randomInteger}} - A random integer, e.g. 1543243211
  2. {{randomInteger min max}} - A random integer between the given min and max, e.g. 10
  3. {{randomDouble}} - A random double, e.g. -0.3476573
  4. {{randomString}} - A random String, e.g. lW7H2gRfFqALsev0zDoJ4o3qGMtNYYgs
  5. {{randomUUID}} - A random UUID, e.g. 5d0d4989-b5c1-412e-bbe6-457fd5d58493

Use request data in response

HTTP*

To use HTTP request attributes in the response:

  1. {{request.url}} - URL
  2. {{request.path}} - Path
  3. {{request.path.[n]}} - N-th path element
  4. {{request.query.parameterName}} - Parameter value
  5. {{request.query.parameterName.[n]}} - N-th parameter value
  6. {{request.headers.headerName}} - First value of a header
  7. {{request.headers.[headerName]}} - First value of a header
  8. {{request.headers.headerName.[n]}}- N-th value of header
  9. {{request.cookies.cookieName}} - Cookie value
  10. {{request.body}} - Request body

* Implemented with ResponseTemplateTransformer

JMS

To use JMS request message attributes in the JMS response message:

  1. {{request.body}} - JMS request message body

Files

To use request file attributes in the response file:

  1. {{request.body}} - Request message body
  2. {{request.fileName}} - Request file name

gRPC

To use gRPC request attributes in the response:

  1. {{request.body}} - Request body as a JSON object

Handlebars helpers

Transform strings

All of jknack Handlebars string helpers are available for use in both HTTP and JMS response headers and body.

  1. {{defaultIfEmpty value ["default value"]}}} - Render a default value if the specified value is not available, for example {{defaultIfEmpty request.query.username "Username parameter was not present in the request"}}
  2. {{substring value start end}} - Render the beginning of the string value, for example {{defaultIfEmpty request.query.username 0 3}}
  3. {{substring value start}} - Render the end of the string value, for example {{defaultIfEmpty request.query.username 5}}
  4. {{replace value "aaa" "bbb"}} - Replace string aaa with bbb
  5. {{abbreviate value 5}} - Render a truncated version of a string. Minimum value is 4.
  6. {{yesno value [yes="yes"] [no="no"] maybe=["maybe"]}} - Convert a boolean "true", "false" to a string representation
  7. {{capitalize value [fully=false]}} - Capitalize a string
  8. {{stripTags value}} - Remove all HTML or XML tags from the string
  9. {{stringFormat string param0 param1 ... paramN}} - Format the string
  10. {{slugify value}} - Create a slug, for example "a b c" will be rendered as "a-b-c"
  11. {{numberFormat number ["format"] [locale=default]}} - Format a number for a given locale or format
  12. {{now ["format"] [tz=timeZone|timeZoneId]}} - Date is a specified format and timezone
  13. {{upper value}} - Uppercase string
  14. {{lower value}} - Lowercase string
  15. {{rjust value 20 [pad=" "]}} - Right adjust string
  16. {{ljust value 20 [pad=" "]}} - Left adjust string
  17. {{cut value [" "]}} - Cut out all the occurrences in the string
  18. {{center value size=19 [pad="char"]}} - Center string value padded with the specified character
  19. {{capitalizeFirst value}} - Capitalize first word
  20. {{join value "," [prefix="aPrefix"] [suffix="aSuffix"]}} - Join an array of values

Extract data from request fields and attributes

The following additional helpers are available in both HTTP, JMS and File response templates.

xPath

You can use it to extract a value from a field using an XPath.

Syntax:
{{xPath sourceAttribute 'TheXPathValue'}}
For example, if you use the following handlebar in a HTTP response body:
Request foobar value was: {{xPath request.body '/foo/bar/text()'}}
and then send a HTTP request:
<foo><bar>Long live mocking!</bar></foo>
you will see a HTTP response:
Request foobar value was: Long live mocking!
xPathList

You can use it to extract a list of nodes from a field using an XPath. Can be combined with loop iterators to transform a request body.

Syntax:
{{xPathList sourceAttribute 'TheXPathValue'}}
For example, if you use the following handlebar in a HTTP response body:
<items>
  {{#each (xPathList request.body '/request/items') }}
    <item id="{{ xPath this '/item/@id' }}" status="OK">{{ xPath this '/item/text()' }}</item>
  {{/each}}
</items>
and send a HTTP request:
<request>
    <items>
        <item id="one" ignored="any">body1</item>
        <item id="two" ignored="any">body2</item>
    </items>
</request>
you will see a HTTP response:
<items>
    <item id="one" status="OK">body1</item>
    <item id="two" status="OK">body2</item>
</items>
jsonPath

You can use it to extract a value from a field using an JsonPath.

Syntax:
{{jsonPath sourceAttribute 'TheJsonPathValue'}}
For example, if you use the following handlebar in a HTTP response body:
Request foobar value was: {{jsonPath request.body '$.foo.bar'}}
and then send a HTTP request:
{"foo":{"bar":"Long live mocking!"}}
you will see a HTTP response:
Request foobar value was: Long live mocking!
jsonPathList

You can use it to extract a list of values from a field using an JsonPath. Can be combined with loop iterators to transform a request body.

Syntax:
{{jsonPathList sourceAttribute 'TheJsonPathValue'}}
For example, if you use the following handlebar in a HTTP response body:
[
  {{#each (jsonPathList request.body '$.items') }}
    { id: {{ jsonPath this '$.id' }}", status: "OK" }{{#unless @last}},{{/unless}}
  {{/each}}
]
and send a HTTP request:
{
    items: [
        { id: 1234, ignored: "any" },
        { id: 1235, ignored: "any" }
    ]
}
you will see a HTTP response:
[
    { id: 1234, status: "OK" },
    { id: 1235, status: "OK" }
]
regex

You can use it to extract a value from a field using a single regular expression capturing group.

Syntax:
{{regex sourceAttribute 'TheRegex(.*)Value'}}
For example, if you use the following handlebar in a HTTP response body:
Request value was: {{regex request.body '.+? ([0-9A-Za-z]+Camel-[0-9A-Za-z]+).*'}}
and then send a HTTP request:
before 001116059c5549a0tOACamel-116059c554a20tOH after
you will see a HTTP response:
Request value was: 001116059c5549a0tOACamel-116059c554a20tOH

Nested handlebars helpers

You can call helpers inside other helpers. You just have to wrap them in parenthesis.

For example, if you use the following handlebar in a HTTP response body:
Bar was equal to xxx: {{#equal (jsonPath request.body '$.foo.bar') 'xxx'}}true{{else}}false{{/equal}}'
and then send a HTTP request:
{"foo":{"bar":"xxx"}}
you will see a HTTP response:
Bar was equal to xxx: true

Loops and iterators

All block helpers defined in the handlebars specification are available. As an example the following use of {{#each}} in a response body will render all request headers:
<requestHeaders>
  {{#each request.headers}}
    <name>{{@key}}</name>
    <value>{{this}}</value>
  {{/each}}
</requestHeaders>

Variables

The standard {{#with}}defined in the handlebars specification is available. As an example the following use of {{#with}} in a response body allows reuse of the same variable multiple times in the block:
{{#with (jsonPath request.body '$.internalCode') as |internalCode|}}
  "internalCode": {{ internalCode }},
  "isoCountryCode": "{{select 'isoCountryCode from isoCountryCodes.csv where internalCode equals' internalCode }}"
{{/with}}

Conditionals (if, else)

Standard Handlebars block helpers

All block helpers defined in handlebars specification are available. As an example the following use of {{#if}} in a response body will render a different response based on availability of a request paramater:
{{#if request.query.password}}
  Password was present in the request.
{{else}}
  Please provide a password!
{{/if}}

Additional block helpers

There are also additional helpers available:
equal

You can use it to compare two values.

Syntax:
{{#equal attributeValue1 attributeValue2}} result if true {{else}} result if false {{/equal}}
For example, if you use the following handlebar:
Is user Bob? {{#equal request.query.user 'Bob'}} Yes! {{else}} No! {{/equal}}
ifEven

You can use it to see if a number is even or odd.

Syntax:
{{#ifEven attributeValue}} result if true {{else}} result if false {{/equal}}
For example, use the following handlebar to see if the number of order items in the request body (which is json) is odd or even:
{{#ifEven (jsonPath request.body '$.orderItem')}}number is even{{else}}number is odd{{/ifEven}}

Assorted

  1. {{httpNow}} - Date in RFC 1123 format (HTTP date format), e.g. Date: {{httpNow}} can be rendered as Date: Sat, 01 Apr 2017 10:55:05 GMT. Typically used as a value for the "Date" header.
  2. {{size attributeName}} - Size or length of an array or collection, for example {{size (jsonPathList request.body '$.items')}}

Ask for new extensions

If you need new extensions just email us at support@trafficparrot.com we will will do our best to create them for you.

Disabling {{...}}

If you would like to use {{...}} notation in your templates and not having it interpreted as an attempt to use an extension you need to do one of the following things:
  1. For individual responses, use escaped syntax: \{{...}}
  2. Or, disable extensions globally by setting both trafficparrot.jms.handlebars.enabled=false and trafficparrot.http.handlebars.enabled=false in trafficparrot.properties and restarting Traffic Parrot

Create your own custom extensions

Introduction

In order to create your own HTTP extensions basic Java programming language knowledge is required. We use Java to write extensions because it is a simple to use and yet very powerful language. It is also the most popular programming language in the world. If this is not something you can do, we can create the extensions for you instead.

There are two types of extensions you can create:
  • Custom handlebar helpers. Simple, for basic usage. These extensions can be used to transform or generate dynamically the response content.
  • Custom response transformers for HTTP and JMS. Complex, for advanced usage. These extensions can do advanced transformations, routing of responses, and many more.

Custom handlebar helpers

Traffic Parrot allows for Handlebars notation in the responses. They are typically used to add dynamic content to the responses. They can be used for both HTTP and JMS.

For example, all of these existing extensions are handlebar helpers: If you would like to add new Handlebars Helpers, have a look at the following example:
  1. Download and open the SDK workspace project in an IDE like IntelliJ IDEA or Eclipse
  2. Explore the project and the Java classes available in the SDK. Look for usages of TrafficParrotHelper They provide example usages of Handlebars Helpers. Have a look at both main and test classes.
  3. Create a new extension class based on the examples provided in the SDK (by extending TrafficParrotHelper class). For example:
    package com.trafficparrot.sdk.example.http.template;
    
    import com.github.jknack.handlebars.Handlebars;
    import com.github.jknack.handlebars.Options;
    import com.trafficparrot.sdk.handlebars.TrafficParrotHelper;
    
    import java.io.IOException;
    import java.util.Random;
    
    public class RandomInteger extends TrafficParrotHelper<Object> {
        @Override
        public Handlebars.SafeString doApply(Object context, Options options) throws IOException {
            return new Handlebars.SafeString(String.valueOf(new Random().nextInt()));
        }
    }
  4. Build the project ./mvnw clean install
  5. Copy target/trafficparrot-sdk-workspace-x.y.z.jar
    to directory trafficparrot-enterprise-release-x.y.z/lib/virtualservice-x.y.z to use with HTTP
    or to directory trafficparrot-enterprise-release-x.y.z/lib/external to use with JMS
  6. Look for properties trafficparrot.http.handlebars.helpers and trafficparrot.jms.handlebars.helpers in trafficparrot.properties and add your new class to the list of extension classes there. (Why do I have to edit the extensions properties every time?)
  7. Stop Traffic Parrot
  8. Start Traffic Parrot
  9. If the name of the class you have created is RandomInteger the extension you will use in response templates will be called {{randomInteger}} by default. The name can be changed by implementing the TrafficParrotHelper.getName method.
  10. Edit an existing mapping and put {{randomInteger}} anywhere in the response body.
  11. Make a request to the virtual service
  12. The response you receive should contain the randomly generated integer.

Custom HTTP response transformers

Traffic Parrot allows for creating custom HTTP response transformers. They allow for altering any part of the HTTP response in any way you like, and have access to the request data.

If you would like to add a new HTTP response transformer, have a look at the following example:
  1. Download and open the SDK workspace project in an IDE like IntelliJ IDEA or Eclipse
  2. Explore the project and the Java classes available in the SDK. They provide example usages of HTTP response transformers. Look for usages of HttpResponseTransformer and HttpResponseDefinitionTransformer. Have a look at both main and test classes.
  3. Create a new extension class based on the examples provided in the SDK (by extending HttpResponseTransformer class). For example:
    package com.trafficparrot.sdk.example.http.responsetransformer;
    
    import com.github.tomakehurst.wiremock.common.FileSource;
    import com.github.tomakehurst.wiremock.extension.Parameters;
    import com.github.tomakehurst.wiremock.http.Request;
    import com.github.tomakehurst.wiremock.http.Response;
    import com.trafficparrot.sdk.http.HttpResponseTransformer;
    
    import java.util.Random;
    
    import static com.github.tomakehurst.wiremock.http.HttpHeaders.noHeaders;
    
    public class RandomServerFailure extends HttpResponseTransformer {
        @Override
        protected Response doTransform(Request request, Response response, FileSource fileSource, Parameters parameters) {
            if (new Random().nextBoolean()) {
                return Response.Builder
                        .like(response)
                        .but()
                        .status(500)
                        .headers(noHeaders())
                        .body("Server error!")
                        .build();
            } else {
                return response;
            }
        }
    
        @Override
        public String getName() {
            return getClass().getSimpleName();
        }
    }
  4. Build the project ./mvnw clean install
  5. Copy target/trafficparrot-sdk-workspace-x.y.z.jar to directory trafficparrot-enterprise-release-x.y.z/lib/virtualservice-x.y.z
  6. Look for property trafficparrot.http.responsetransformers in trafficparrot.properties and add your new class to the list of extension classes there. (Why do I have to edit the extensions properties every time?)
  7. Stop Traffic Parrot
  8. Start Traffic Parrot
  9. If the name of the class you have created is RandomServerFailure the extension you will use in response templates will be automatically called {{randomServerFailure}}
  10. Edit an existing mapping and put {{randomServerFailure}} anywhere in the response body.
  11. Make a few requests to the virtual service
  12. The response you receive should randomly fail, as defined in the response transformer RandomServerFailure.

Custom JMS response transformers

Traffic Parrot allows for creating custom JMS response transformers. They allow for altering any part of the JMS response message in any way you like, and have access to the request data.

If you would like to add a new JMS response transformer, have a look at the following example:
  1. Download and open the SDK workspace project in an IDE like IntelliJ IDEA or Eclipse
  2. Explore the project and the Java classes available in the SDK. They provide example usages of JMS response transformers. Look for usages of JmsResponseTransformer and TextJmsResponseTransformer. Have a look at both main and test classes.
  3. Create a new extension class based on the examples provided in the SDK (by extending TextJmsResponseTransformer class). For example:
    package com.trafficparrot.sdk.example.jms;
    
    import com.github.tomakehurst.wiremock.common.Json;
    import com.trafficparrot.sdk.jms.Destination;
    import com.trafficparrot.sdk.jms.JmsResponse;
    import com.trafficparrot.sdk.jms.JmsResponseBuilder;
    import com.trafficparrot.sdk.jms.TextJmsResponseTransformer;
    
    import javax.jms.JMSException;
    import javax.jms.TextMessage;
    
    public class ChooseResponseQueueBasedOnRequestMessageBody extends TextJmsResponseTransformer {
        @Override
        protected JmsResponse doTransform(Destination requestDestination, TextMessage requestMessage, JmsResponse response) throws JMSException {
            Req payment = Json.read(requestMessage.getText(), Req.class);
            String newDestinationName;
            if ("foo".equals(payment.requestField)) {
                newDestinationName = "response_queue_1";
            } else if ("bar".equals(payment.requestField)) {
                newDestinationName = "response_queue_2";
            } else {
                logger.error("Destination not configured for req.requestField '" + payment.requestField + "'");
                throw new UnsupportedOperationException(payment.requestField);
            }
            logger.info("Redirecting message to " + newDestinationName);
    
            return new JmsResponseBuilder()
                    .like(response)
                    .withDestination(new Destination(newDestinationName, response.destination.type))
                    .build();
        }
    }
    
    class Req {
        public String requestField;
    }
  4. Build the project ./mvnw clean install
  5. Copy target/trafficparrot-sdk-workspace-x.y.z.jar to directory trafficparrot-enterprise-release-x.y.z/lib/external
  6. Look for property trafficparrot.jms.responsetransformers in trafficparrot.properties and add your new class to the list of extension classes there. (Why do I have to edit the extensions properties every time?)
  7. Stop Traffic Parrot
  8. Start Traffic Parrot
  9. Edit an existing JMS mapping and select ChooseResponseQueueBasedOnRequestMessageBody from the "Response transformer" dropdown.
  10. Start the JMS virtual service in replay mode and send a JMS request with content {"requestField": "foo"}
  11. The response message should be sent to queue response_queue_1
  12. Send a JMS request with content {"requestField": "bar"}
  13. The response message should be sent to queue response_queue_2

Video

The following video describes usage of Traffic Parrot extensions in version 3.7.x and below. Note: property names and class names have changed since.
If you cannot see a video frame below please download it here
 

Old version warning!

This documentation is for an old version of Traffic Parrot. There is a more recent Traffic Parrot version available for download at trafficparrot.com

Browse documentation for recent version