Simlet Parameters
Overview
Parameters are the input values a simlet uses to produce HTTP response from a template. |
They are used in Simula template placeholders and scriptlets to render dynamic output.
Configuring Parameters
The general YAML DSL for defining a parameter for a simlet is as follows:
SomeParameterName:
is: parameter
from: <source>
#...source-specific configuration fields here...
# The (text) value from the source is converted to this type
as: <byte | int16 | int32 | int64 | float | double | bool | dateTime>
# Only for 'as: dateTime', the format of the datetime string to help with parsing it
format: "dateTime-format-string"
# The parameter will default to this value if the value from the source is null.
# The type of the default value should be the same as in 'as', if 'as' is specified.
default: <default value>
# 'isSnapshot' is a boolean flag if the value of this parameter will be the same (snapshot)
# or change between uses of the parameter in the building of a response. Defaults to 'true'.
isSnapshot: false | true
eval: "<expression>"
It is to note that the parameters:
list field has been deprecated and it is not required anymore to add parameters to that list in order to be able to access and use them in template placeholders. The support for the parameters:
field will be removed in a future release.
Here is an example for configuring a parameter and using it in a matcher and the response template:
# This defines a parameter once that is then used in
# a matcher as well as in a template placeholder
ProductID:
is: parameter
from: body
element: "//product/id"
matchers:
- method: PUT
- where: uriPath
equals: "/api/v1/products"
# Parameter as a matcher
- where: parameter
named: ProductID
exists: true
response:
from: template
template: Simula
status: 201
headers:
# Template placeholder that uses the 'ProductID' parameter
- "Location: /api/v1/products/${ProductID}/details"
Parameter Name
Parameter names must follow these rules:
-
Start with a letter.
-
Contain zero or more letters, digits, and underscore
_
characters after the first letter. -
Case-insensitive.
-
Unique within a simlet.
API Simulator doesn’t error out when it detects a duplicate parameter name - it just logs a warning.
Parameter Type
Parameters can come from a variety of sources where each source type has some required and optional configuration fields. A section below - Parameter Sources - describes them in detail.
With HTTP being a text-based protocol, by default, all parameters from HTTP request elements (path, body, etc.) are strings. Usually there isn’t a need to convert the text to another type unless the parameter value has to be formatted in a template placeholder differently than the original value. Using as
instructs API Simulator to convert the string value to one of these types:
-
byte - 8-bit signed integer number in the range from
-128
to+127
, inclusive. -
int16 - 16-bit signed integer number in the range from
-32,768
to32,767
, inclusive. -
int32 - 32-bit signed integer number in the range from
-231
(-2,147,483,648
) to231 - 1
(+2,147,483,647
), inclusive. -
int64 - 64-bit signed integer number in the range from
-263
(-9,223,372,036,854,775,808
) to263 - 1
(+9,223,372,036,854,775,807
), inclusive. -
float - single-precision 32-bit IEEE 754 floating point number.
-
double - double-precision 64-bit IEEE 754 floating point number.
-
boolean - a boolean true/false.
-
dateTime - the internal representation is an int64 number denoting the number of milliseconds since the standard base time known as "the epoch", namely
January 1, 1970, 00:00:00 GMT
.
For proper parsing when a parameter’s value comes from a datetime represented as text, API Simulator needs to know the datetime format.
For example, yyyy-MM-dd HH:mm:ss z
is the format for a datetime that has:
-
4 digits for the year (
yyyy
), -
2 digits for the month (
MM
), -
2 digits for the day (
dd
), -
a space, followed by
-
two digits for the hour in the day in 24-hour format (
HH
), -
two digits for the minutes (
mm
), -
two digits for the seconds (
ss
), -
one space,
-
then the time zone name (e.g.
GMT
,CST
,PST
…)
…and all elements will be padded with zero (0) as needed.
Each one of the following case-insensitive value is considered to be boolean true
: true
, on
, yes
, and 1
.
Each one of the following case-insensitive value is considered to be boolean false
: false
, off
, no
, and 0
.
Failure to convert a parameter value to the desired type will result in null
value.
Parameter Evaluation
Sometimes a source value needs additional processing to get to the final parameter value. That is what eval
is for.
Using eval
allows for great flexibility in configuring parameters. In essence, eval
is a small script expression that is executed and whatever it returns becomes value for the parameter.
These are the steps API Simulator goes through in determining the final value for a parameter:
-
Get the value from the respective source;
-
Convert that value to the type given in
as
, if specified; -
If the value is
null
then it assumes what’s indefault
, if configured. -
Apply additional evaluation to the value by executing the scripting code in
eval
, if present.
Whatever isreturn
-ed fromeval
becomes value for the parameter even if it has nothing to do with the value from the source - the script ineval
has the freedom to return anything and of any type. -
If
isSnapshot
istrue
then the value at this point will be snapshotted (see Snapshotting).
Let’s look at an example:
matchers:
#. ...
- header: "Content-Type"
equals: "text/csv"
- where: parameter
named: ProductID
exists: true
UriPath:
is: parameter
from: uriPath
# Parameter as result of processing the body content
CsvParms:
is: parameter
from: body
eval: |-
if (null == _ || _.trim().length() == 0 || _.indexOf(',') < 0) return null
return _.split(',')
# Parameter extracted from the processed body content
ProductID:
is: parameter
from: parameter
named: CsvParms
eval: |-
return (_ != null && _.size() > 0 ? _[0] : null)
response:
from: template
template: Simula
status: 201
headers:
- "Location: ${ UriPath }/${ ProductID }/details"
The explanation follows:
-
CsvParms
is a parameter which value is an array obtained as result of parsing the request body. -
The underscore
_
is a variable for the parameter value at the point of time of starting the execution of theeval
expression. -
The
ProductID
parameter becomes the first element from theCsvParms
array, if there is such element, ornull
otherwise.
It is to note that only "simple" scripting can be used in eval
- no functions/methods/classes/closures/etc.
Snapshotting
The boolean isSnapshot
field makes sense for parameters, which values can change from one use of the parameter to the next in the rendering of the same response. Such parameters, for example, are those which values are randomly generated. Setting isSnapshot: false
allows a parameter to be defined once and used multiple times in building a response and each time it will return a potentially different value.
Parameter Sources
Parameters can come from a variety of sources - the HTTP request, a data store, scripts, randomly generated values, current data/time, some computation based on another parameter, and more.
URI
SomeParameterName:
is: parameter
# One of the 'from' sources below
# The HTTP method: GET, POST, PUT, etc.
from: httpMethod
# The whole URI
from: uri
# The scheme - http/https - if present in the URI
from: uriScheme
# The path part of the URI only
from: uriPath
from: UriPathPattern
pattern: "/uri/path/{pattern}/here"
# The user info (userid and/or password) part of the URI
from: uriUserInfo
# The host part of the URI
from: uriHost
# The port number part of the URI
from: uriPort
# Query String Parameters
# A map of all Query String Parameters. Each map entry is keyed by the query
# string parameter name and the value is a list with zero or more elements
from: uriQueryParameters
# A Query String Parameter
# There can be more than one Query String Parameter with the same name.
# This parameter will have them all in a list of items accessible by index.
from: uriQueryParameter
named: "query-string-parameter-name"
# The fragment (the part after '#') in the URI
from: uriFragment
# The HTTP version the request uses (e.g. HTTP/1.1)
from: httpVersion
URI Path Pattern
Parameters from URI path pattern deserve a bit more details to explain the flexibility in extracting such parameters that represent values from URI path segments.
In the following example, API clients submit HTTP GET requests like this:
GET /v1/products/2706414/Black+Charcoal/XL
By varying the values after /v1/products
, the API returns data for a specific product based on the product’s SKU, color, and size.
Let’s configure a simlet for such requests and that uses parameters for the SKU, color, and size:
matchers:
- method: GET
- where: uriPathPattern
matches: "/v1/products/{sku}/{color}/{size}"
SkuParm:
is: parameter
from: uriPathPattern
# Two wildcard characters '**' match zero or more directory segments in a path
# This parameter will have a value for sure because of the matcher defined above
pattern: "/v1/products/{sku}/**"
ColorParm:
is: parameter
from: uriPathPattern
# A single wildcard character '*' like in this pattern matches a directory segment
pattern: "/v1/products/*/{color}/*"
SizeParm:
is: parameter
from: uriPathPattern
pattern: "/v1/products/*/*/{size}"
#...
The names of the path segments surrounded by {
and }
- {sku}
, {color}
, and {size}
- need not to match the name of the parameter in which definition they appear.
Cookies
HTTP requests may contain cookies in an HTTP Header field called Cookie
. A single header field can have multiple cookies separated by semi-colon ;
.
API Simulator supports configuring parameters from cookies so you don’t have to extract them yourself. The implementation is compliant with the most recent RFC6265 "HTTP State Management Mechanism". In particular, cookie values are retrieved from a Cookie
header regardless of whether they have any $Path
or $Version
as defined in the older and now-obsolete RFC2965.
Here are examples for how to define parameters from cookies:
SessionIDParm:
is: parameter
from: cookie
named: "SessionID"
LanguageParm:
is: parameter
from: cookie
named: "lang"
default: "en-US"
HTTP Body
It is possible to treat the body of requests as unstructured content or as structured text (JSON, XML), if applicable.
Parameters from Unstructured Body Content
The following configures a parameter which value is the whole body content from the request:
RequestBody:
is: parameter
from: body
API Simulator recognizes JSON and XML body payloads as structured content. Any other format is treated as of it is unstructured. The next example demonstrates how to perform custom extract of pieces of data from a comma-separated values text:
# Parameter as result of processing the body content
CsvParms:
is: parameter
from: body
eval: |-
if (null == _ || _.trim().length() == 0 || _.indexOf(',') < 0) return null
return _.split(',')
# Parameter extracted from the processed body content
ProductID:
is: parameter
from: parameter
named: CsvParms
eval: |-
return (_ != null && _.size() > 0 ? _[0] : null)
Notice that the above isn’t a robust CSV processing that takes into account, for example, commas inside the values, but demonstrates a way to process unstructured body content.
Parameters from Body Elements
To identify an exact element in structured body content (JSON, XML), use path expressions as those used for matching body elements:
ParameterName:
is: parameter
# Use the same path expressions as the ones to identify matching body elements
from: body
element: "path-to-the-body-element"
# XML body with namespaces
from: body
element: "path-to-the-body-element"
namespaces:
prefix1: "<uri1>"
...
prefixN: "<uriN>"
Constant Value
Constant value parameters have a value that doesn’t change and can be used as a placeholder, in computations, etc. Here is how to define one:
ParameterName:
is: parameter
from: constant
value: <some-constant-value>
UUID
Sometimes API calls return UUIDs. There is a special parameter type in API Simulator for UUIDs. Here is how to define one named PaymentID:
PaymentID:
is: parameter
from: uuid
…then use it in a template as any other parameter.
When generated according to standard algorithms, UUID (Universally Unique Identifier), aka GUID (Globally Unique Identifier), is guaranteed to be unique across time and space. Here is an example for a UUID:
707e6337-4298-46b8-8fe7-f7ce396d404f
Current Datetime
A datetime value may contain date and/or time portion. Here’s how to configure a parameter named DateTimeNow
for the current datetime:
DateTimeNow:
is: parameter
from: dateTime
See also Random Datetime parameters.
Time-based Dependency
Here is an example of a datetime parameter which value is computed with respect to the value of another "base" datetime parameter:
CurrentDateTime:
is: parameter
from: dateTime
# Let's read the definition out loud:
# > this 'ExpireDateTime' parameter value comes from/depends on another parameter.
# > the name of the parameter this one depends on is 'CurrentDateTime'.
# > the dependency is time computation - amount of a time unit added or subtracted.
ExpireDateTime:
is: parameter
from: parameter
named: CurrentDateTime
dependency:
kind: time
amount: 86400
unit: seconds
The "unit" value is case-insensitive and expected to be one of the following: days
, day
, hours
, hour
, minutes
, minute
, seconds
, second
, milliseconds
, and millisecond
.
The time-based dependency works also with randomly generated datetime parameters as base.
Parameter As Source
Pretty much any dependency between parameters can be defined using eval
. Here is a configuration the result of which is equivalent to the Time-based Dependency presented above:
CurrentDateTime:
is: parameter
from: dateTime
# Let's read the definition out loud:
# This 'ExpireDateTime' parameter value comes from another parameter named
# 'CurrentDateTime' evaluated by adding 86400 seconds to its value.
ExpireDateTime:
is: parameter
from: parameter
named: CurrentDateTime
eval: return _ + (86400 * 1000L)
The parenthesis are not required but just to help with reading the expression: "return the current parameter value added to the result from multiplying 86400 by 1000".
Lists
Lists can be a source for parameters. Here is how to define such parameters:
# The parameter's value is the whole list, unchanged
LastNameList:
is: parameter
from: list
list: [ "Smith", "Johnson" ]
Given that this is YAML, the lists’s elements can also be specified using the one-item-per-line format:
LastNameList:
is: parameter
from: list
list:
- "Smith"
- "Johnson"
Random Number
The random number parameters contain randomly generated integer (whole) numbers:
ParameterName:
is: parameter
from: number
range:
min: <minimum-value>
max: <maximum-value>
To use in a template a randomly generated floating-point number, generate two random integer numbers and concatenate them in the template with a dot .
or comma ,
(as appropriate for the locale).
Random Datetime
Random datetime between dates in a given range can be defined as follows:
OrderDate:
from: dateTime
range:
# At least one of the 'min' or 'max' range boundaries is required.
# Missing range boundary is assumed to be the current data and time,
# possibly causing switching the values to assure 'min' < 'max'.
min: "2018-01-01 00:00:00 UTC"
max: "2018-01-31 11:59:59 UTC"
# The same format applies to both 'min' and 'max'
format: "yyyy-MM-dd HH:mm:ss z"
Notice that the same construct can be used to define a date between now and some other day in the past or future.
Sometimes there’s the requirement to use a date that is certain number of days before or after "now". Here it is how to configure such random datetime generation:
OrderDate:
from: dateTime
range:
# Randomly generates a new datetime value that is in the range of
# [ "now" + min, "now" + max]
# Negative values for 'min' or max' will be subtracted from "now".
# At least one of the 'min' or 'max' range boundaries is required.
# Missing range boundary is assumed to have value of 0 (zero),
# possibly causing switching the values to assure 'min' < 'max'.
min: -60
max: -30
# The same time unit applies to both 'min' and 'max'
timeUnit: days
The "timeUnit" can be any of the following case-insensitive values: nanoseconds
, microseconds
, milliseconds
, seconds
, minutes
, hours
, and days
.
Random Token
Here is an example for configuring a parameter which value is a random token:
PaymentNumber:
is: parameter
from: token
pattern: "PAY-[0-9A-Z]{25}"
The "pattern" uses the following constructs and rules:
-
Literals = any digits, letters, other characters and symbols to be used verbatim in the resulting token.
-
[]
= a group; the token generator will randomly use one of the elements from the group surrounded by square brackets. -
{n}
= exact repetition; placed after a group it determines how many times the token generator will randomly pick an element from the group. -
{n,m}
= repetition range; after a group, it tells the token generator to randomly pick an element from the group at leastn
times but not more thanm
times, where0
< =n
< =m
. -
There must not be any whitespace character, like a space, between the end of a group
]
and{
start of a repetition for that group. -
[a-d]
= range of letters inside a group; a shortcut to avoid listing all elements from the range individually. -
[0-9]
= range of digits inside a group; a shortcut to avoid listing all elements from the range individually. -
[?a-z!0-9.]
= a group can have multiple ranges mixed with individual elements as well. -
To escape
]
and-
inside a group, prefix them with/
. Notice that[
inside a group doesn’t have to be escaped. -
To escape a
/
inside a group, prefix it with/
-//
. -
To escape a
-
inside a group, prefix it with/
-/-
. -
Special characters for whitespaces:
\n
,\r
,\t
, etc.
Examples:
-
[PaRsE]
- one of theP
,R
, andE
capital letters, and thea
ands
small letters. -
[a-c]
- any small letter betweena
andc
, that’s it -a
,b
, orc
. -
[A-Z]
- any capital letter betweenA
andZ
. -
[0-9]
- any digit between0
and9
. -
PAY-[0-9A-Z]{25}
- the textPAY-
followed by 25 randomly picked digits and capital letters. -
[_$a-z][a-z0-9_]{0,9}
- a token that starts with underscore_
, dollar sign$
, or a small letter betweena
andz
, followed by no more than nine small letters, digits, or underscore_
character.
Random List Elements
The Lists section introduced lists as parameter values. An extended syntax for list parameter configuration - adding a pick
field - is used to define parameters with individual elements or sublists randomly selected from a pre-defined list of valid values. The following explains the various options:
# Parameter for a single element randomly picked from the list
RegionAZ:
is: parameter
from: list
list:
- "us-central1-a"
- "us-central1-b"
- "us-central1-c"
- "us-central1-f"
# The same as 'pick: 1'
pick: any
# The parameter is a list containing all elements randomly reordered!
AvailableZones:
is: parameter
from: list
list:
- "us-central1-a"
- "us-central1-b"
- "us-central1-c"
- "us-central1-f"
# The same as 'pick: 4' (e.g. the total number of list items)
pick: all
# The parameter is a sub-list containing 2 randomly picked elements
FirstNameList:
is: parameter
from: list
list: [ "Anna", "Bill", "Mark", "Sofia" ]
pick: 2
CSV File
Another aspect of Test Data Management (TDM) in API Simulator is the capability to use parameters from CSV (comma-separate values) data sources. This powerful feature makes it very easy to model flexible API simulations that return dynamic responses from larger datasets with minimal configuration.
CSV data can come from files or be provided in the simlet configuration. The following demonstrates the use of parameters with values that come from a CSV file:
ProductID:
is: parameter
from: UriPathPattern
pattern: "/v1.1/product/{ProductID}/**"
Color:
is: parameter
from: UriPathPattern
pattern: "/v1.1/product/*/{Color}/*"
Size:
is: parameter
from: UriPathPattern
pattern: "/v1.1/product/*/*/{Size}"
ProductData:
is: parameter
from: csv
# The first row in the CSV file is expected to contain unique column names
# with no spaces or special characters in the names other than underscore.
# A column value that contains a comma or double quote(s) is to be surrounded
# by double quotes. Each double quote inside of a column value is to be escaped
# with another double quote
file: "${simlets.path}/product-data.csv"
# List of keys from parameters to use in querying the CSV data.
# A parameter as key may appear more than once in the list.
# Parameter names must match the column names in the CSV file
keys:
- ProductID
- Color
- Size
matchers:
- method: GET
- where: UriPathPattern
matches: "/v1.1/product/{id}/{color}/{size}"
- where: parameter
named: ProductData
exists: true
response:
from: template
template: Simula
status: 200
headers:
- "Content-Type: application/json; charset=UTF-8"
body: |+
{
"product": {
"id": "${ProductData['ProductID']}",
"sku": "${ProductData['SKU']}",
"name": "${ProductData['Name']}",
"category": "${ProductData['Category']}",
"subCategory": "${ProductData['Subcategory']}",
"color": "${ProductData['Color']}",
"size": "${ProductData['Size']}",
"orig_price": ${ProductData['Original_Price']},
"sale_price": ${ProductData['Sale_Price']}
}
}
In addition to the ability to read in and use CSV data from a file, API Simulator supports CSV data provided in the simlet’s YAML configuration. For example:
ProductData:
is: parameter
from: csv
# The first row in the CSV file is expected to contain unique column names
# with no spaces or special characters in the names other than underscore.
# A column value that contains a comma or double quote(s) is to be surrounded
# by double quotes. Each double quote inside of a column value is to be escaped
# with another double quote. This is what file export to CSV from MS Excel does
data: |-
ProductID,ProductCategory,ProductSubcategory,ProductName,ProductColor,ProductSize,ProductSKU,Original_Price,Sale_Price
2706414,Men,Hoodies & Sweatshirts,Fleece Pullover Hoodie,Black Charcoal,S,769409213,55.00,39.99
2706414,Men,Hoodies & Sweatshirts,Fleece Pullover Hoodie,Black Charcoal,M,769413463,55.00,39.99
2592228,Men,Hoodies & Sweatshirts,Full-Zip Hooded Fleece Jacket,Dark Gray,XXL,769458426,65.00,52.00
# List of keys from parameters to use in querying the CSV data.
# A parameter as key may appear more than once in the list.
# Parameter names must match the column names in the CSV data
keys:
- ProductID
- ProductColor
- ProductSize
#...
Download and check out the examples in the Standalone API Simulator distro.
There is also working code that uses Excel as parameters source but it requires Apache POI (poi-ooxml in particular) which isn’t currently included in the API Simulator distribution. Please give us a shout if for some reason exporting Excel files to CSV is not an option for your use cases.
SQL Data Store
API Simulator offers the capability to use parameters retrieved from a Relational Database Management System (RDBMS) by means of SELECT
SQL statements. Again, this powerful feature allows one to easily model flexible API simulations that return dynamic responses from very large datasets with minimal configuration.
The SQL statements can be parameterized with ?
to denote placeholders for query arguments. The argument values themselves come from standalone parameters defined in the simlet.
Below is a complete configuration example - found also in the Standalone API Simulator distro - that demonstrates the use of an external H2 database as the source for parameters in rendering dynamic responses:
ProductID:
is: parameter
from: UriPathPattern
pattern: "/v1.1/product/{productID}/**"
ProductColor:
is: parameter
from: UriPathPattern
pattern: "/v1.1/product/*/{color}/*"
ProductSize:
is: parameter
from: UriPathPattern
pattern: "/v1.1/product/*/*/{size}"
ProductData:
is: parameter
from: sql
connection:
# Currently, pooled connections are not supported
uses: driver
# Driver's fully-qualified class name
driver: "org.h2.Driver"
# Using "/./" seems to be required for H2
url: "jdbc:h2:${simlets.path}/./product-data"
# Credentials must be provided if the database is password-protected
username:
password:
# Optional other driver-specific name/value properties
props:
propName: propValue
# Parameterized SQL query
query: |-
SELECT p.*, c.*
FROM PRODUCT p, PRICE c
WHERE p.SKU = c.SKU
AND p.ProductID=?
AND p.Color=?
AND p.Size=?
# Ordered list of parameters to use in querying the SQL database.
# A parameter may appear more than once in the list.
# The parameter names don't have to match the database column names
parms:
- ProductID
- ProductColor
- ProductSize
matchers:
- method: GET
- where: uriPathPattern
matches: "/v1.1/product/{id}/{color}/{size}"
- where: parameter
named: ProductData
exists: true
response:
from: template
template: Simula
status: 200
headers:
- "Content-Type: application/json; charset=UTF-8"
body: |+
{
"product": {
"id": "${ProductData['ProductID']}",
"sku": "${ProductData['SKU']}",
"name": "${ProductData['Name']}",
"category": "${ProductData['Category']}",
"subCategory": "${ProductData['Subcategory']}",
"color": "${ProductData['Color']}",
"size": "${ProductData['Size']}",
"orig_price": ${ProductData['Original_Price']},
"sale_price": ${ProductData['Sale_Price']}
}
}
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!