Simula Template Scriptlets
Overview
Scriptlets are template fragments that can produce variable content. They are practically code. |
The Simula template engine executes the scriptlets code upon rendering the template. The code may produce output or drive its production by using, for example, control statements (loops, if…else, etc.).
Let’s not get into a debate around logic-less templates vs. templates that may contain explicit logic. The Simula templating engine does support scriptlets and you may as well take advantage of this powerful feature, if you need it.
Syntax
Currently, scriptlet’s code is code in the Groovy scripting language. Don’t worry if learning a new scripting language isn’t on your to-do list - a little experience in any other programming language will be more than enough to make you feel comfortable writing statements in Groovy to simulate parts of API responses. And again - only if you need to.
The syntax for a scriptlet is
<%
Groovy-language-scripting-statements
%>
The delimiters - <%
and %>
- can be configured to be something different.
It is to note that it isn’t possible to have nested scriptlets, that’s it - a scriptlet inside another scriptlet, or a scriptlet inside a ${ … }
placeholder.
Scriptlets in the Body
Let’s look at the following simlet example to demonstrate some of the aspects of using scriptlets. The comments in the scriptlet code provide additional information (even for things that may be obvious):
request:
- where: method
equals: GET
- where: UriPath
equals: "/api/places/nearby"
- where: uriQueryParameter
named: "postalCode"
equals: "54321"
- where: uriQueryParameter
named: "radius"
equals: "10"
# At least one query string parameter with name 'placeType' and value 'restaurant'
- where: uriQueryParameter
named: "placeType"
equals: "restaurant"
QueryStringParms:
is: parameter
from: uriQueryParameters
OriginHeaderParm:
is: parameter
from: header
named: "Origin"
response:
from: template
template: Simula
# Optional field for the template scriptlets language.
# It currently must be 'groovy', if field is specified
lang: groovy
status: 200
headers:
- "Content-Type: application/json; charset=UTF-8"
# Configuration for optional header field - no name or value are
# to be output unless some condition is met. Requirements:
# > The header field value must end with "\r\n" line break;
# > Set 'optional: true':
- header: "<% if (${OriginHeaderParm} != null && ${OriginHeaderParm.trim().length()} > 0) { %>Allow-Origin: ${OriginHeaderParm}\r\n<% } %>"
# "optional" is needed because at the time the template is constructed, it is
# unknown if the header field name and value will be empty or not when resolved.
# (The template placeholder resolving process doesn't know what is what). So,
# can't add "\r\n" line break automatically when needed but it is not present.
optional: true
body: |+
{
"queryStringParms": {
"total": ${ QueryStringParms.count() },
"postalCode": "${ QueryStringParms.first('postalCode') }",
"placeType": [
<%
// Single-line comment in the scriptlet
/*
Multi-line
comment
*/
// Defining a local variable. It is visible in all scriptlets of this template
// Here, ${...} surrounds QueryStringParms to denote it is a placeholder
placeTypeParms = ${ QueryStringParms.get('placeType') }
// Loop over the collection of 'placeType' query string parameter values
for (i = 0; i < placeTypeParms.count(); i++)
{
// Adding semicolon ';' at the end is required only
// when using multiple statements on the same line
placeTypeValue = placeTypeParms[i];
if (i > 0)
{
// Mixing scriptlet code with direct text output
// Line break followed by spaces to achieve pretty printing
%>,\n <%
}
// Writing out the result of string concatenation.
// Parenthesis () are optional
write ('"' + placeTypeValue + '"')
}
%>
],
<%
/*
This is a scriptlet with a multi-line comment only and no other code.
It actually causes one empty line in the output because of
where its left and right delimiters appear with respect to
the text surrounding it.
The scriptlet below to produce value for "isDebug1"
doesn't use ${...} to denote it is a placeholder.
*/
%>
"isDebug1": <% if (QueryStringParms.contain('debug')) { %>true<% } else { %>false<% } %>,
"isDebug2": <%
// Ternary operator. Also, this shows coercing
// of non-null object references to true
write (QueryStringParms.get('debug') ? true : false)
%>
}
}
The following HTTP request
GET /api/places/nearby?postalCode=54321&radius=10&placeType=restaurant&placeType=cafe&debug
will be matched by the matchers defined in the simlet above. This is then the body of the HTTP response produced after rendering the template:
{
"queryStringParms": {
"total": 4,
"postalCode": "54321",
"placeType": [
"restaurant",
"cafe"
],
"isDebug1": true,
"isDebug2": true
}
}
Scriptlets in Header Fields
Scriptlets can be used not only in the HTTP response body definition but also in header field configuration.
The following example demonstrated the configuration of a conditional header field - header field that is output only when some condition is met:
#...
OriginHeaderParm:
is: parameter
from: header
named: "Origin"
response:
from: template
template: Simula
status: 200
headers:
- "Content-Type: application/json; charset=UTF-8"
# Configuration for optional header field - no name or value are
# to be output unless some condition is met. Requirements:
# > The header field value must end with "\r\n" line break;
# > Set 'optional: true':
- header: "<% if (OriginHeaderParm != null && OriginHeaderParm.trim().length() > 0)
{ %>Access-Control-Allow-Origin: ${OriginHeaderParm}\r\n<% } %>"
# "optional" is needed because at the time the template is constructed, it is
# unknown if the header field name and value will be empty or not when resolved.
# (The template placeholder resolving process doesn't know what is what). So,
# can't add "\r\n" line break automatically when needed but it is not present.
optional: true
#...
There is a lot more that scriptlets can do. They give you extreme power and flexibility in modeling API simulation responses.
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!