Simula Template Placeholders

Overview

One type of fragments in a Simula template that can produce variable content is the placeholders.

definition

A placeholder gets resolved to an actual value as part of rendering the template.

By default, ${ and } denote the beginning and end of a placeholder.

The following example is for a simlet that simulates the HTTP response to a request to create new product; product data like Product ID, name, etc. are in the body of the request:

request:
- method: PUT

- where: uriPath
  equals: "/v3/products"

- header: Content-Type
  equals: "application/json"

- where: body
  element: ".product.id"
  exists: true


# This defines a single parameter named 'ProductID'
# with value from the HTTP request body and extracted
# from the element at the given JSON path.
ProductID:
  is: parameter
  from: body
  element: ".product.id"


# The HTTP response is rendered from a Simula template
response:
  from: template
  template: Simula

  # The HTTP response status will be '201 Created'
  status: 201

  # A 'Location' header will be returned denoting the endpoint
  # to use to get details for the newly created product
  headers:
  - "Location: /v3/products/${ProductID}/details"

In the example above, ${ProductID} is a placeholder for the actual Product ID. Its value will be resolved upon rendering the template by trying to look it up in the list of placeholders (see below) first and, if not found there, to find it in the list of parameters next.

In this way, a single simlet simulates the creation of different products without having to "know" all possible Product IDs in advance. The simlet extracts the Product ID from the request and puts it into a parameter that a placeholder then uses to produce dynamic output.

The ${ProductID} placeholder doesn’t have any formatting - the template engine will replace ${ProductID} with the "raw" parameter value.

It is possible to apply formatting to a placeholder’s value. Moreover, API Simulator supports encoding of placeholder values so that special characters in them won’t break the output. JSON, XML, HTML, URL, Base64, and Base64Url encoders are currently supported.

API Simulator doesn’t automatically escape all placeholder values. That makes it possible to inject invalid content in a given context (e.g. JSON body) and test how API clients deal with it.

API Simulator will error out if it can’t find the parameter for a placeholder, log an "Unresolvable token=<name-here>" error, and return 500 Internal Server Error response.

Template Placeholders can also contain simple expressions. In the unlikely case that a ${ } placeholder expression is to contain { or } but not both - { with corresponding } - use a scriptlet instead.

It is to note that the placeholders list field has been deprecated and it is not required anymore to add placeholders to that list in order to format the value and/or to apply special encoding. The support for the placeholders field will be removed in a future release. This doesn’t concern you if you’ve never heard of the placeholders field 😉

Template Functions

Template Functions offer great flexibility. They supplement the declarative style parameters and provide an easy way to format values, extract values from JSON using JSONPath or from XML with XPath, generate random values, and more.

To use template functions, import the respective libraries first:

simlet: make-a-payment

import:
  random: _random
  json: _json
  ...

...

For the random functions, this reads as "…​import the library of random functions and reference them using the '_random' handle".

A few things to take into account:

  • The import can be anywhere in the configuration but for readability it is recommended to be at the top, before the actual usage of the functions.

  • The handle name must start with an underscore _ followed by a lower case letter, and then optionally with more letters or digits for up to 32 characters total.

  • The scope of template functions libraries is within the simlet where they are imported. This is important to keep in mind for simlets defined in a single file.

  • It is not allowed to have duplicate handle names in the same simlet configuration.

To use the template functions from the imported libraries, reference them by their handle. For example:

simlet: make-a-payment

import:
  random: _random

request:
  - ...

response:
  ...
  body: `
{
"token": ${ _random.token('[A-Z][0-9A-Z]{12}') }
}`

See below for how to import and use each of the built-in template functions. Notice that using Scripting is always an option, too.

Global Functions

The following applies to all global template functions:

  • There is no need to import the global functions.

  • The function’s first argument is the value on which to apply the function.

  • Unless explicitly specified, all functions treat input value of null on which to apply a function as an empty string.

  • Functions can be nested. That is - the argument for a function call can be a function call, too.

When the HTTP response body is in JSON format and one wants to use a template function that expects a string argument but the value of the argument itself contains single quotes, then escape the single quotes inside the argument’s value (\'). For example:

{
  "create_time": "${ formatDateTime(CreateTimeParm, 'yyyy-MM-dd\'T\'HH:mm:ss.SSS\'Z\'', 'GMT') }"
}

These are the currently supported global functions:

String trim(s)
Trims leading and trailing spaces from the input string s. Safely handles a null value.

String format(value, pattern)
Formats the input value using the given pattern. Uses the default locale for the JVM in which APU Simulator is running. See General Formatting for pattern details

String format(value, pattern, language)
Formats the input value using the given pattern and language for locale-specific formatting. See General Formatting for pattern details.

String format(value, pattern, language, country)
Formats the input value using the given pattern, and language and country for locale-specific formatting. See General Formatting for pattern details.

String formatDateTime(value, pattern)
Deprecated. Please use the corresponding format function from Date/Time Functions.

String formatDateTime(value, pattern, timeZone)
Deprecated. Please use the corresponding format function from Date/Time Functions.

String httpDateTime(value)
Formats the input value as a date/time string per the HTTP specification using E, dd MMM yyyy HH:mm:ss z pattern and GMT time zone. The input value is expected to be a Datetime which internally is a long number representing the number of milliseconds since epoch.

String jsonEncode(s)
Deprecated. Please use the corresponding encode function from JSON Data Functions.

String xmlEncode(s)
Deprecated. Please use the corresponding encode function from XML Data Functions.

String htmlEncode(s)

String urlEncode(s)

String base64Encode(s)

String base64Encode(s, charset)

String base64UrlEncode(s)
Defaults to UTF-8 charset.

String base64UrlEncode(s, charset)

General Formatting

The format of specifiers for types other than datetime has the following syntax:

%[flags][width][.precision]type
Type

The type is required. The following table describes the expected type codes and their meaning:

Type Code Type Description

b, B

boolean

If the value is null, then the result is false. If the value is a boolean, then the result is true or false. Otherwise, the result is true.

s, S

string

If the value is null, then the result is null. Otherwise, the value is treated as a string.

c, C

character

The result is a Unicode character.

d

integer

The result is formatted as a decimal integer.

o

integer

The result is formatted as an octal integer.

x, X

integer

The result is formatted as a hexadecimal integer.

e, E

floating-point

The result is formatted as a decimal number in computerized scientific notation.

f

floating-point

The result is formatted as a decimal number.

g, G

floating-point

The value is formatted using computerized scientific notation or decimal format, depending on the precision and the value after rounding.

Flags

The optional flags is a set of characters that modify the output format. The set of valid flags depends on the type:

  • - : For all, the result will be left-justified.

  • + : For integer and floating-point types, the result will always include a sign.

  • ' ' (space) : For integer and floating-point types, the result will include a leading space for positive values.

  • 0 : For integer and floating-point types, the result will be zero-padded.

  • , : For integer and floating-point types, the result will include locale-specific grouping separators.

  • ( : For integer and floating-point types, The result will enclose negative numbers in parentheses.

Width

The optional width is a non-negative integer indicating the minimum number of characters to be written to the output for the placeholder value.

Precision

The optional precision is a non-negative integer usually used to restrict the number of characters:

  • For values of string type, the precision is the maximum number of characters to be written to the output.

  • For the floating-point conversions e, E, and f the precision is the number of digits after the decimal separator. If the conversion is g or G, then the precision is the total number of digits in the resulting magnitude after rounding.

  • The precision is not applicable for character and integer types.

(NOTE: The formatting rules and pattern documentation is largely based on the online Java™ Platform, Standard Edition 7 API Specification, albeit distilled and simplified)

Random Data Functions

There are a few functions at your disposal to help with generating random numbers, date/time values, UUIDs, string tokens…​

First, import the random data functions library:

import:
  random: _random

Then, call the functions using the _random handle (aka library reference).

Long dateTimeInRange(String fromDate, String toDate, String format)
Randomly generates a datetime value (number of milliseconds since epoch - 00:00:00 UTC on 1 January 1970) between fromDate and toDate; the format argument applies to both fromDate and toDate and follows the rules outlined in Datetime Patterns.

Long dateTimeFromNow(long min, long max, String timeUnit)
Randomly generates a new datetime value (number of milliseconds since epoch - 00:00:00 UTC on 1 January 1970) that is in the range of ["now" + min, "now" + max], inclusive. Negative values for 'min' or max' will be subtracted from "now". The timeUnit argument is expected to be one of the following case-insensitive values: nanoseconds, microseconds, milliseconds, seconds, minutes, hours, and days. The same timeUnit applies to both min and max.

Double decimalInRange(long min, long max, int scale)
Randomly generates a decimal number which whole part is between min and max, inclusive, and having the given scale (the number of digits to the right of the decimal point). For instance,

${ _random.decimalInRange(1, 100, 2) }

…​will generate a decimal number in the range of 1.00 and 100.00. Some numbers that could be generated are 99.99, 4.6, 57.08, but not 100.01 or 0.99. Use the global format function to have the generated number be padded with zeros:

${ format(_random.decimalInRange(1, 100, 2), '%.2f') }

The above will produce 4.60 when the generated number is 4.6.

Long numberInRange(long min, long max)
Randomly generates a whole number between min and max, inclusive.

String token(String pattern)
Generates a token (sequence of symbols) according to the given pattern. See Random Token for information about the pattern.

String uuid()
Generates a UUID (Universally Unique Identifier), aka GUID (Globally Unique Identifier), that is guaranteed to be unique across time and space. Here is an example for a UUID: 707e6337-4298-46b8-8fe7-f7ce396d404.

JSON Data Functions

There are several functions to help you work with JSON data. Import the json library and reference the functions via the _json handle:

import:
  json: _json

String encode(String s)
Returns a JSON-encoded value that is safe to be included in a JSON string. It will return an empty string if the input argument is null.

Object evalJsonPath(String jsonText, String jsonPath)
Evaluates jsonPath on the jsonText JSON and returns the value at that path or null if there isn’t a match for the JSONPath. Here is an example:

"id": ${ _json.evalJsonPath(_request.bodyText, '.product.id') }

Object evalJsonPath(Object document, String jsonPath)
The document argument is expected to be a value from calling parse(String jsonText).

Object parse(String jsonText)
If there will be multiple JSONPath evaluations on the same text, then parse it first to a document and then run the JSONPath evaluations on the parsed document. For example:

...
response:
  ...
  body: `
<% jsonDoc = _json.parse(_request.bodyText) %>
{
  "id"  : ${ _json.evalJsonPath(jsonDoc, '.product.id') }
  "name": ${ _json.evalJsonPath(jsonDoc, '.product.name') }
}
`

XML Data Functions

Here are functions for manipulating and working with XML. Import the xml library and call the functions via the _xml handle like in this example:

import:
  xml: _xml
...
${ _xml.evalXPath('<product><id>56789</id><name>The Rocket Shoes</name></product>', '/product/id') }

String encode(String s)
Returns an XML-encoded value that is safe to be included in an XML string. It will return an empty string if the input argument is null.

Object evalXPath(String xmlText, String xpath)
Evaluates the xpath on the xmlText and returns the value at the XPath or null if the XPath didn’t match an element in the XML.

Object evalXPath(String xmlText, String xpath, Map<String, String> namespaces)
Evaluates xpath on the xmlText for the given namespace context and returns the value at the XPath or null if the XPath didn’t match an element in the XML. For example:

${ _xml.evalXPath(_request.bodyText, 's:Envelope/s:Body/ws:ValidateAddress/ValidateAddressInput/Address/Line1', [s: 'http://schemas.xmlsoap.org/soap/envelope/', ws: 'http://ws.v1_0.avs.example.com/']) }

The above has to be on a single line when using it in a ${ } placeholder. To break it into multiple lines for better readability, use it in a scriptlet, or a combination of scriptlet and placeholder.

Object evalXPath(Object document, String xpath)
Evaluates xpath on the XML document and returns the value at the XPath or null if the XPath didn’t match an element in the XML. The document should have been returned as result of calling the parse method (see below).

Object evalXPath(Object document, String xpath, Map<String, String> namespaces)
Evaluates xpath on the XML document for the given namespace context and returns the value at the XPath or null if the XPath didn’t match an element in the XML. The document should have been returned as result of calling the parse method (see below).

Object parse(String xmlText)
If there will be multiple XPath evaluations on the same text, then call this method to parse the text first to a document and then run the XPath evaluations on the parsed document.

Date/Time Functions

Import the dateTime library of Date/Time helper functions and call them with the _dateTime handle. For example:

import:
  dateTime: _dateTime
...
{
  "updatedOn": "${ _dateTime.format(_dateTime.now(), 'yyyy-MM-dd\'T\'HH:mm:ss.SSS\'Z\'', 'GMT') }"
  "createdOn": "${ _dateTime.format(_random.dateTimeFromNow(-60, -30, 'days'), 'yyyy-MM-dd\'T\'HH:mm:ss.SSS\'Z\'', 'GMT') }"
}

Long now()
Current date and time as the number of milliseconds since epoch (00:00:00 UTC on 1 January 1970).

String format(value, pattern)
Formats the input value as a date/time string using the given pattern. The input value is expected to be a long number representing the number of milliseconds since epoch. See Datetime Patterns for pattern details.

String format(value, pattern, timeZone)
Formats the input value as a date/time string using the given pattern and timeZone identifier. The input value is expected to be a long number representing the number of milliseconds since epoch. See Datetime Patterns for pattern details.

Long parse(value, pattern)
Returns the number of milliseconds since epoch representation of the input String value parsed based on the pattern, or null if the input value couldn’t be parsed.

Datetime Patterns

A pattern determines how a date/time value is formatted to a String or parsed from a String.

Within a pattern string, unquoted letters from A to Z and from a to z are interpreted as pattern letters representing the components of a datetime. To avoid interpretation, text can be quoted using single quotes '. Two single quotes '' represents a single quote. All other characters are not interpreted - they are simply copied into the output.

The following pattern letters are defined:

Letter Date or Time Component Presentation Examples

G

Era designator

Text

AD

y

Year

Year

1996; 96

Y

Week year

Year

2009; 09

M

Month in year

Month

July; Jul; 07

w

Week in year

Number

27

W

Week in month

Number

2

D

Day in year

Number

189

d

Day in month

Number

10

F

Day of week in month

Number

2

E

Day name in week

Text

Tuesday; Tue

u

Day number of week (1 = Monday, …​, 7 = Sunday)

Number

1

a

AM/PM marker

Text

PM

H

Hour in day (0-23)

Number

0

k

Hour in day (1-24)

Number

24

K

Hour in am/pm (0-11)

Number

0

h

Hour in am/pm (1-12)

Number

12

m

Minute in hour

Number

30

s

Second in minute

Number

55

S

Millisecond

Number

978

z

Time zone

General time zone

Pacific Standard Time; PST; GMT-08:00

Z

Time zone

RFC 822 time zone

-0800

X

Time zone

ISO 8601 time zone

-08; -0800; -08:00

When pattern letters are repeated, their count determines the exact presentation as follows:

  • Text: For formatting, if the number of pattern letters is 4 or more, the full form is used; otherwise, a short or abbreviated form is used if available. For parsing, both forms are accepted, independent of the number of pattern letters.

  • Number: The number of pattern letters is the minimum number of digits, and shorter numbers are zero-padded to this amount.

  • Year: If the number of pattern letters is 2, the year is truncated to 2 digits; otherwise, it is interpreted as a number.

  • Month: If the number of pattern letters is 3 or more, the month is interpreted as text; otherwise, it is interpreted as a number.

  • General time zone: Time zones are interpreted as text if they have names. For time zones representing a GMT offset value, the following syntax is used:

    • GMT Sign Hours : Minutes

    • Sign: + | -

    • Hours: Digit | Digit Digit

      Hours must be between 0 and 23

    • Minutes: Digit | Digit

      Minutes must be between 00 and 59.

    • Digit: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9

  • RFC 822 time zone: It uses 4-digit time zone.

  • ISO 8601 Time zone

(NOTE: The formatting rules and pattern documentation is largely based on the online Java™ Platform, Standard Edition 7 API Specification, distilled and simplified)

Complex Values

Placeholder values may be of a scalar data type - string, boolean, integer, floating point number (decimal), or datetime - or they can have complex data structure. Placeholders in a Simula template can use expressions to access properties and elements of such complex values. For example, to access a list element by its index/position in the list or to access a map element by key (map is a data structure with key/value pairs for elements and where a value - scalar or complex - is identified by its key).

This can be very useful when used on its own or when used in scriptlets.

The following examples demonstrates working with URI Query String Parameters. A URI Query String Parameters parameter is like a map with elements having Query String Parameter names as keys and values are lists of zero or more values of type string:

# An example that shows how to work with placeholder values of a complex data type

request:
- where: method
  equals: GET

- where: uriPath
  equals: "/api/places/nearby"

- where: uriQueryParameter
  named: "postalCode"
  equals: "12345"

- where: uriQueryParameter
  named: "radius"
  equals: "10"

# Supposedly, an indicator that the requests are for debugging
- where: uriQueryParameter
  named: "debug"
  exists: true

# At least one query string parameter with name 'placeType' and value 'restaurant'
- where: uriQueryParameter
  named: "placeType"
  equals: "restaurant"


PlaceTypeParm:
  is: parameter
  # There can be more than one query string parameter with the same name.
  # This parameter will have them all in a list with items accessible by index.
  from: uriQueryParameter
  named: "placeType"

QueryStringParms:
  is: parameter
  from: uriQueryParameters


response:
  from: template
  template: Simula

  status: 200
  headers:
  - "Content-Type: application/yaml"

  # Use single and not double quotes for map keys!
  # Also, notice the mix of single and double quotes
  body: `
query:
  queryStringParms:
    total: ${ QueryStringParms.count() }
    postalCode-count: ${ QueryStringParms.count('postalCode') }
    placeTypes-count: ${ QueryStringParms.count('placeType') }
    placeTypes-first: '${ QueryStringParms.get('placeType', 0) }'
    placeTypes-last: "${ QueryStringParms.get('placeType', QueryStringParms.count('placeType') - 1) }"
  placeTypeParm:
    count: ${ PlaceTypeParm.count() }
    first: "${ PlaceTypeParm.get(0) }"
    last: '${ PlaceTypeParm[PlaceTypeParm.count() - 1] }'
`

Assuming this GET request:

GET /api/places/nearby?postalCode=12345&radius=10&placeType=restaurant&placeType=cafe&debug

…​here is how the response body built with the template will look like:

query:
  queryStringParms:
    total: 4
    postalCode-count: 1
    placeTypes-count: 2
    placeTypes-first: 'restaurant'
    placeTypes-last: "cafe"
  placeTypeParm:
    count: 2
    first: "restaurant"
    last: 'cafe'

The example demonstrated calling methods inside placeholders like count() and get(name, index), accessing an element in a list by index - .get(index) or [index]…​and even an embedded calculation - computing the last index (when we know that there’s at least one element).

See also Request Object Reference for all the methods valid for URI Query String Parameters or an individual URI Query String Parameter.

Defaulting a Value

When a variable or parameter is not defined or is null, this is a way to specify a default value for it:

${ parm ?: defautValue }

Placeholders and Scriptlets

While it is possible to have ${ …​ } placeholders inside <% …​ %> scriptlets, it is an error to place <% …​ %> scriptlets inside ${ …​ } placeholders.


We would love to hear your feedback! Shoot us an email to [feedback at APISimulator.com] about anything that is on your mind.

Happy API Simulating!