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.

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.

Template Functions

Template Functions offer great flexibility in 'shaping' the placeholder values before they are rendered as part of a template.

The following applies to all template 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. For example, to format the placeholder value and then to apply JSON encoding to the formatted value:

NumberValueParm:
  is: parameter
  from: constant
  value: 456
  as: int32


response:
  from: template
  template: Simula

  # ...

  body: |+
    {
      "result": "${ jsonEncode( format(NumberValueParm, '"%06d"') ) }"
    }

…​will output in the response body this properly encoded JSON string:

{
  "result": "\"000456\""
}

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') }"
}

Supported Functions

More functions will be added over time. These are the currently supported functions:

String trim(s)
Trims leading and trailing spaces from the input string s.

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)

Formats the input value as a date/time string using the given pattern. The input value is expected to be a Datetime which internally is a long number representing the number of milliseconds since epoch. See Datetime Formatting for pattern details. String formatDateTime(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 Datetime which internally is a long number representing the number of milliseconds since epoch. See Datetime Formatting for pattern details.

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)

String xmlEncode(s)

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)

Datetime Formatting

Datetime values use special formatting that allows for more options and flexibility - one can define time zone and locale-specific setting in addition to pattern.

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, albeit distilled and simplified)

Complex Placeholder Values

Placeholder values may be of a scalar data type - string, boolean, integer, floating point number, 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 Query String Parameter 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.

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 a quick email to [feedback at APISimulator.com] to let us know what you think.

Happy API Simulating!