simlet: ...
request:
#...
response:
from: ...
template: Simula
latency:
#...
connection:
close: ...
#...
codec:
#...
httpVersion: ...
status: ...
headers:
#...
body: ...
Response Configuration
Overview
Once API Simulator matches a request and determines which simlet it is for, it constructs the response to return based on the simlet response configuration.
Notice that by default API Simulator streams the response back as it is being built. This makes it possible for API Simulator to return very large responses while running with only few MB of memory.
There are two types of responses with respect to the data they return:
-
Static stubs/mocks, and
-
Dynamic responses.
Static Stubs/Mocks
Static stubs/mocks are practically canned responses - responses that don’t change between requests for the same simlet.
Dynamic Responses
Dynamic responses, as their name suggests, are built dynamically per request and can change from one request for a simlet to the next.
Response Configuration
API Simulator looks in the response
section of a simlet configuration for what response to return. The configuration includes fields for data (static or dynamic) and behavior (latency, connection disruption):
Some of the configuration fields are optional, some have sub-configuration, and others are mutually exclusive. A detailed documentation of the various configuration aspects of each field is in Response Stubbing and Response Templating.
It is to note that a single response configuration can produce different outputs when using dynamic responses via templating.
Multiple Responses
Often times a slight variation in a request has to return different response. For example, everything else being the same in a request - e.g. method is GET
and route is /api/some/report
- missing input query string parameters for date range would cause 400
(Bad Request) to be returned, and 200
(OK) with the actual data otherwise (see the example below).
One way to simulate such responses is to define a separate simlet for each such variation. Another could be to resort to writing scripting code.
API Simulator offers you another choice - ability to model multiple responses in the configuration for the same simlet. Here is an example:
simlet: multiple-responses-for-a-request-example
request:
- uriPath: /api/some/report
- method: GET
responses:
- when:
request:
- where: uriQueryParameter
named: fromDate
exists: false
# then
from: stub
status: 400
body: |
{
"status": "error",
"message": "Missing required parameter 'fromDate'"
}
- when:
request:
- where: uriQueryParameter
named: toDate
exists: false
# then
from: stub
template: simula
status: 400
body: |
{
"status": "error",
"message": "Missing required parameter 'toDate'"
}
# otherwise
- from: template
template: simula
status: 200
headers:
- "Content-Type: application/json"
body: |
{
"status": "OK",
"message": "Received request with required parameters fromDate='${_request.uri.query.params.first('fromDate')}' and toDate='${_request.uri.query.params.first('toDate')}'"
}
It is OK if the example above may have triggered some questions about the simlet configuration - the various configuration elements are explained elsewhere in the docs. It should at least be clear that the simlet will return responses with status code 400
(Bad Request) and the response body will contain an error message in JSON when either one of the fromDate
or toDate
query string parameters are missing. For example, for requests like these:
-
GET /api/some/report
-
GET /api/some/report?fromDate=2020-01-01
-
GET /api/some/report?toDate=2020-01-31
(A more complete example would also check for valid dates)
More generally, to model multiple responses in the configuration for the same simlet:
simlet: ...
request:
#...
responses:
- when:
request:
#...<any request matching or sampling rules>...
# then
from: ...
#...<configuration for a single response>...
- when:
request:
#...<any request matching or sampling rules>...
# then
from: ...
#...<configuration for a single response>...
# otherwise - if none of the above match
- from: ...
#...<configuration for a single response>...
The responses
element contains a list of possible responses. Which response is returned is based on evaluating to true some condition configured in when
. The order of responses in the list determines the order in which API Simulator evaluates them to decide which response to return.
The last response in the list usually does not have a when
condition and it is the response that will be returned if no response in the list before it is matched.
If there is not such last response without a when
condition and none of the other responses match, then the rules described in On Request Mismatch apply.
The condition in when
can be any combination of matching rules described in Request Matching, plus rules for Request Sampling.
It is an error to have both response
and responses
elements.
Oh, yeah - the # then
and # otherwise
comments are not required.
Request Sampling
Sampling is a process of selecting a few elements from a large collection. |
In API Simulator, request sampling is a form of request matching.
Random Sampling
Random sampling uses uniform distribution to generate values. An algorithm determines which ones will be part of the sample. The sampling is approximate.
One of the main uses of random sampling in API Simulator is to simulate some of request/response exchanges failing with something like 503
(Service Unavailable). Here is an example:
simlet: random-sampling-example
request:
- uriPath: /api/path
- method: GET
responses:
- when:
request:
- sample: random
percent: 2
# then
from: stub
status: 503
headers:
- "Content-Type: application/json"
body: |
{
"status": "error",
"message": "Service Unavailable"
}
# otherwise
- from: stub
status: 200
headers:
- "Content-Type: application/json"
body: |
{
"status": "OK"
}
About 2% of the responses will return status code 503
(Service Unavailable) and the rest - 200
(OK).
The general format is as follows:
responses:
#...
- when:
request:
#- ...
- sample: random
percent: <fractional-number-between-0.0-and-100.0>
#...
All requests have equal weight. percent
is a fractional number with boundaries between 0.0 and 100.0, inclusive:
-
percent: 10
⇒ approximately 1 in 10 will be selected -
percent: 50.0
⇒ approximately half will be selected -
percent: 67.3
⇒ approximately 67.3 out of 100, or 673 of 1000 will be selected
Request Sequence Number Sampling
This sampling uses request selection based on the sequence number of requests that have matched a simlet. The following comparison operations are supported:
responses:
# ...
- when:
request:
# The first request that matches this simlet has sequence
# number of 1, the second has sequence number of 2, etc.
- sample: sequence
equals: 1
eq: 1 # same as "equals"
not equals: 5
notEquals: 5 # same as "not equals"
neq: 5 # same as "not equals"
lessThan: 10
lt: 10 # same as "lessThan"
lessThanOrEqual: 5
lte: 5 # same as "lessThanOrEqual"
greaterThan: 11
gt: 11 # same as "greaterThan"
greaterThanOrEqual: 10
gte: 10 # same as "greaterThanOrEqual"
in: [1, 3, 10, 22] # non-empty list of whole numbers
not in: [1, 3, 10, 22] # non-empty list of whole numbers
nin: [1, 3, 10, 22] # same as "not in"
# then
from: ...
#...
Sequence sampling supports more than one condition. For example (the # and
comment is not required):
responses:
#...
- when:
request:
- sample: sequence
gte: 3
# and
lte: 5
…results in the requests for this simlet with sequence numbers between 3 and 5, inclusive, being selected.
An example Use Case for Sequence sampling is when the response is to return 503
(Service Unavailable) for the first, say, 3 requests, and 200
(OK) for any request thereafter:
responses:
#...
- when:
request:
- sample: sequence
lte: 3
# then
from: stub
status: 503
# otherwise
- from: stub
status: 200
#...
Another Use Case is a simulation where the order of requests is controlled and always the same; a particular response is to be returned based on the sequence number of the request: some response #1 for request #1, another response #2 for request #2, etc.
Fixed Rate Sampling
With Fixed Rate sampling, every Nth request for the simlet will be selected.
responses:
#...
- when:
request:
#- ...
- sample: fixed
rate: <whole-positive-number>
#...
rate: 1
is the same as taking every request. When rate
is 5 then every 5th request for the simlet will be selected - 5th, 10th, 15th, 20th, etc.
Rate Limiting
Matching a simlet request for rate limiting is request sampling - simlet requests above the predefined rate of calls per second are selected. It is explained in Rate Limiting.
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!