Code Monkey home page Code Monkey logo

apisimulator's Introduction

API Simulator

API Simulator is a configuration-driven tool for developers and testers for modeling and running API simulations - simulations that imitate real APIs or that stub and mock APIs not built yet. Visit API Simulator's web site for more info.

This repository will contain the open source components as we make them available.

Happy API Simulating!

apisimulator's People

Contributors

apisim avatar

Stargazers

 avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

Forkers

r0b3rt24

apisimulator's Issues

Provide a way to match request body against JSON schema

I've read the Body Matching section. I'm aware something similar can be achieved by using JSONPath, but the outcome won't be the same. Moreover, teams already employ JSON schemas or Open API definitions to check end-point contracts โ€” so that's a time saver.

I think it would be great is API Simulator could validate a given request body against a corresponding JSON schema. For instance, MockServer does something similar:

...
  "body": {
    "type": "JSON_SCHEMA",
    "jsonSchema": "schemas/create-entity.schema.json"
  },
...

Augment Template Functions

Template Functions are great. The library reference for random offers several functions, but I think a good addition to it could be something like com.github.javafaker:javafaker in order to get meaningful values for common return types, like: addresses, states, first names, last names, credit card numbers, etc.

I'm aware something similar can be achieved by using a random token, but it's not quite the same. This would be similar to what Postman already have implemented: https://learning.postman.com/docs/writing-scripts/script-references/variables-list/

Tricky Type Inference With External JSON Files

Imagine there is a simulation like this one (excerpt):

---
simlet: Identifier

parameters: # <- why is this going away if it's super-useful to organize all parameters under one key!
  - age:
      from: number
      range:
        min: 20
        max: 40

request:
  ...

response:
  from: template
  template: Simula
  headers:
    - name: content-type
      value: "application/json"
  status: 200
  body:
    file: "${sim.path}/umbrella-api/operation-one/files/response/model-identifier/get-response.json"

...and get-response.json could be something like:

{
  "first_name": "Rahy",
  "last_name": "Hellbleeder",
  "age": "${age}", !!!
  "_id": "${_random.uuid()}"
}

As you can notice by the !!!, age is being sent as a string, instead of a number. The reason it needs to be wrapped into double quotes is to make get-response.json a valid JSON.

It works fine if the double quotes are removed, but then auto-formatting and some checks placed on the project's pipeline for JSON/YAML files will fail.

Is there a work-around for this?

Getting intermittently error "java.net.BindException: Address already in use: connect"

I have setup some simlets runing on my localhost at a specfic port. When I am trying to perform a simple load of 100 users hitting the simulated API, I am getting below error intermittently. Error percentage varries from 0 to 70% in various runs. Can someone please help here, if I need to change some settings for APISimulator or something else.


java.net.BindException: Address already in use: connect
at java.base/sun.nio.ch.Net.connect0(Native Method)
at java.base/sun.nio.ch.Net.connect(Net.java:579)
at java.base/sun.nio.ch.Net.connect(Net.java:568)
at java.base/sun.nio.ch.NioSocketImpl.connect(NioSocketImpl.java:585)
at java.base/java.net.SocksSocketImpl.connect(SocksSocketImpl.java:327)
at java.base/java.net.Socket.connect(Socket.java:633)
at org.apache.http.conn.socket.PlainConnectionSocketFactory.connectSocket(PlainConnectionSocketFactory.java:75)
at org.apache.http.impl.conn.DefaultHttpClientConnectionOperator.connect(DefaultHttpClientConnectionOperator.java:142)
at org.apache.jmeter.protocol.http.sampler.HTTPHC4Impl$JMeterDefaultHttpClientConnectionOperator.connect(HTTPHC4Impl.java:409)
at org.apache.http.impl.conn.PoolingHttpClientConnectionManager.connect(PoolingHttpClientConnectionManager.java:376)
at org.apache.http.impl.execchain.MainClientExec.establishRoute(MainClientExec.java:393)
at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:236)
at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:186)
at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:89)
at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:110)
at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:185)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:83)
at org.apache.jmeter.protocol.http.sampler.HTTPHC4Impl.executeRequest(HTTPHC4Impl.java:940)
at org.apache.jmeter.protocol.http.sampler.HTTPHC4Impl.sample(HTTPHC4Impl.java:651)
at org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy.sample(HTTPSamplerProxy.java:66)
at org.apache.jmeter.protocol.http.sampler.HTTPSamplerBase.sample(HTTPSamplerBase.java:1311)
at org.apache.jmeter.protocol.http.sampler.HTTPSamplerBase.sample(HTTPSamplerBase.java:1300)
at org.apache.jmeter.threads.JMeterThread.doSampling(JMeterThread.java:651)
at org.apache.jmeter.threads.JMeterThread.executeSamplePackage(JMeterThread.java:570)
at org.apache.jmeter.threads.JMeterThread.processSampler(JMeterThread.java:501)
at org.apache.jmeter.threads.JMeterThread.run(JMeterThread.java:268)
at java.base/java.lang.Thread.run(Thread.java:833)

Unable to reach host specified in callback

I have a simulation that looks very much like this:

---
simlet: create-character

# ...parameters
character_id:
  from: uuid
  is: parameter

request:
  - header: accept
    equals: application/json
  - header: content-type
    equals: application/json
  - method: POST
  - uriPath: /api/characters

response:
  callback: # https://apisimulator.io/docs/latest/standalone-api-simulator/callback.html
    latency:
      random:
        max: 1000
        min: 250
      timeUnit: milliseconds
    http1:
      body:
        text: |-
          {
            "subscriber_id": ${character_id}
            # ...why we cannot read from JSON request body ๐Ÿ˜ญ
          }
        type: text
      from: template
      headers:
        - name: accept
          value: application/json
        - name: content-type
          value: application/json
      method: POST
      template: Simula
      uri:
        host: https://api-simulator.free.beeceptor.com
        path: /api/v1/subscriptions
        scheme: https
  from: template
  headers:
    - name: location
      value: ${_request.uri.path.value}/${character_id}
  status: 201
  template: Simula

This fails with the following error message:

2021-10-06T01:56:49.368+0000|INFO|11|main|[+--------------------------+]
2021-10-06T01:56:49.371+0000|INFO|11|main|[| A P I  S i m u l a t o r |]
2021-10-06T01:56:49.371+0000|INFO|11|main|[+--------------------------+]
2021-10-06T01:56:49.373+0000|INFO|11|main|[API Simulator initialization in progress...]
2021-10-06T01:56:49.374+0000|INFO|11|main|[API Simulator: {java version:"11.0.10+9", jvm name:"OpenJDK 64-Bit Server VM", jvm version:"11.0.10+9", jvm pid:11}]
2021-10-06T01:56:49.374+0000|INFO|11|main|[API Simulator: implementation {title:"API Simulator",version:"1.9"}]
2021-10-06T01:56:49.374+0000|INFO|11|main|[API Simulator: args=[-c, /apisimulator/apisimulator-http-1.9/config, -s, /home/apisimulator/simulations/simlets, /home/apisimulator/simulations, -retain_dsl_models]]
2021-10-06T01:56:49.387+0000|INFO|11|main|[Loading configuration from '/apisimulator/apisimulator-http-1.9/config/apisim.yaml'...]
2021-10-06T01:56:49.427+0000|INFO|11|main|[Loaded admin configuration]
2021-10-06T01:56:49.434+0000|INFO|11|main|[Loaded simulator configuration]
2021-10-06T01:56:49.438+0000|INFO|11|main|[Loaded simulation configuration]
2021-10-06T01:56:49.468+0000|INFO|11|main|[Loaded 2 simlets in 81 ms]
2021-10-06T01:56:49.469+0000|INFO|11|main|[Loading simlets from '/home/apisimulator/simulations/simlets'...]
2021-10-06T01:56:49.948+0000|INFO|11|main|[Loaded 2 simlets in 478 ms]
2021-10-06T01:56:50.402+0000|INFO|11|API-Simulator-Boss-1-1|[API Simulator Server is listening on 0.0.0.0:6090]
2021-10-06T01:56:50.403+0000|INFO|11|main|[API Simulator started]
2021-10-06T01:56:50.438+0000|INFO|11|AdminServer-Boss-6-1|[Admin HTTP Server is listening on 0.0.0.0:6190]
2021-10-06T01:56:57.446+0000|ERROR|11|API-Simlet-3-1|[com.apisimulator.callback.HttpCallbackExecutor.submit(HttpClient, HttpCallbackSpec, HttpRequest): got an exception: com.apisimulator.http.client.HttpClientException: com.apisimulator.http.client.HttpClientException: com.apisimulator.http.netty.HttpClientBase.bootstrapAndConnect(ChannelHandler channelInitializer): Connecting to https://api-simulator.free.beeceptor.com:443 failed: java.net.UnknownHostException: https://api-simulator.free.beeceptor.com: Name or service not known]

A cURL request to the same URI succeeds:

curl --header "accept: application/json" --header "content-type: application/json" --request POST https://api-simulator.free.beeceptor.com/api/v1/subscriptions | jq 
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100    16    0    16    0     0      6      0 --:--:--  0:00:02 --:--:--     6
{
  "ok": true
}

You can verify the requests by going to: https://beeceptor.com/console/api-simulator โ€” before start sending them.

The first data line in a CSV is not matched/retrieved (data provided through simlet's yaml configuration)

Hi! First of all, APISimulator is a very interesting project and I am checking its features to see if I can integrate it into a new project that I am part of!

Issue

I am having an issue when using the CSV data provided in the simlet's YAML configuration: I can't match/retrieve the first (data) line of the CSV.
It seems the first data line is ignored when matching the results.

Steps to reproduce

  1. I am using a docker-compose that looks like the following:
version: "3.9"

services:
  csv-apisimulator:
    container_name: "csv-apisimulator"
    image: apimastery/apisimulator:1.11
    ports:
      - "6090:6090"
    volumes:
      - "./templates/csv:/templates"
    entrypoint: [ "bash", "-c", "apisimulator start ../../templates"]

  # ... other containers definitions ...
  1. The templates/csv/apisim.yaml looks like the following:

Note:
The file is a copy + paste from the content (zip file) downloaded from the apisimulator website: apisimulator-http-1.11-distro\apisimulator\apisimulator-http-1.11\examples\parameters-from-csv\simlets\read-product-from-config\simlet.yaml
The only change I made was adding the line: simlet: read-products-csv

#----------------------------------------------------------------------
# Simlet-specific configuration.
#----------------------------------------------------------------------
simlet: read-products-csv

ProductID:
  is: parameter
  from: uriPathPattern
  pattern: "/v1.2/product/{ProductID}/**"

# Parameter names must match the name of the columns in the CSV data
ProductColor:
  is: parameter
  from: uriPathPattern
  pattern: "/v1.2/product/*/{Color}/*"

# Parameter names must match the name of the columns in the CSV data
ProductSize:
  is: parameter
  from: uriPathPattern
  pattern: "/v1.2/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. 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
2706414,Men,Hoodies & Sweatshirts,Fleece Pullover Hoodie,Black Charcoal,L,769413609,55.00,39.99
2706414,Men,Hoodies & Sweatshirts,Fleece Pullover Hoodie,Black Charcoal,XL,769413531,55.00,39.99
2706414,Men,Hoodies & Sweatshirts,Fleece Pullover Hoodie,Black Charcoal,XXL,769413579,55.00,39.99
2706414,Men,Hoodies & Sweatshirts,Fleece Pullover Hoodie,Solid Gray,S,769417959,55.00,35.95
2706414,Men,Hoodies & Sweatshirts,Fleece Pullover Hoodie,Solid Gray,M,769417966,55.00,36.96
2706414,Men,Hoodies & Sweatshirts,Fleece Pullover Hoodie,Solid Gray,L,769413623,55.00,37.97
2706414,Men,Hoodies & Sweatshirts,Fleece Pullover Hoodie,Solid Gray,XL,769413692,55.00,38.98
2706414,Men,Hoodies & Sweatshirts,Fleece Pullover Hoodie,Solid Gray,XXL,769413678,55.00,39.99
2592228,Men,Hoodies & Sweatshirts,Full-Zip Hooded Fleece Jacket,Dark Gray,S,769458440,65.00,52.00
2592228,Men,Hoodies & Sweatshirts,Full-Zip Hooded Fleece Jacket,Dark Gray,M,769458501,65.00,52.00
2592228,Men,Hoodies & Sweatshirts,Full-Zip Hooded Fleece Jacket,Dark Gray,L,769458327,65.00,52.00
2592228,Men,Hoodies & Sweatshirts,Full-Zip Hooded Fleece Jacket,Dark Gray,XL,769458303,65.00,52.00
2592228,Men,Hoodies & Sweatshirts,Full-Zip Hooded Fleece Jacket,Dark Gray,XXL,769458426,65.00,52.00
6218877,Children,Shoes,The "Rockets",White,4,669032116,35.59,31.99
`

  # 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


request:
- method: GET

- where: uriPathPattern
  matches: "/v1.2/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['ProductSKU'] }",
    "name": "${  jsonEncode( ProductData['ProductName'] ) }",
    "category": "${ ProductData['ProductCategory'] }",
    "subCategory": "${ ProductData['ProductSubcategory'] }",
    "color": "${ ProductData['ProductColor'] }",
    "size": "${ ProductData['ProductSize'] }",
    "orig_price": ${ ProductData['Original_Price'] },
    "sale_price": ${ ProductData['Sale_Price'] }
  }
}
`
  1. Run docker-compose command:
docker-compose up
  1. Call the endpoint:

4.1. Checking for the product in the first line - Result: 404 Not Found

curl:

curl --location 'http://localhost:6090/v1.2/product/2706414/Black Charcoal/S'

HTTP Request and Response

GET /v1.2/product/2706414/Black%20Charcoal/S HTTP/1.1
User-Agent: PostmanRuntime/7.35.0
Accept: */*
Cache-Control: no-cache
Postman-Token: [REDACTED]
Host: localhost:6090
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
 
HTTP/1.1 404 Not Found
Content-Type: application/text; charset=UTF-8
Server: API Simulator, v1.11
Date: Mon, 27 Nov 2023 14:29:09 GMT
Content-Length: 62
 
API Simulator couldn't find a matching simlet for the request.

4.2 Checking for the product in the second line - Result: 200 OK
curl:

curl --location 'http://localhost:6090/v1.2/product/2706414/Black Charcoal/M'

HTTP Request and Response:

GET /v1.2/product/2706414/Black%20Charcoal/M HTTP/1.1
User-Agent: PostmanRuntime/7.35.0
Accept: */*
Cache-Control: no-cache
Postman-Token: [REDACTED]
Host: localhost:6090
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
 
HTTP/1.1 200 OK
Content-Type: application/json; charset=UTF-8
Server: API Simulator, v1.11
Date: Mon, 27 Nov 2023 14:31:36 GMT
Content-Length: 270
 
{
"product": {
"id": "2706414",
"sku": "769413463",
"name": "Fleece Pullover Hoodie",
"category": "Men",
"subCategory": "Hoodies & Sweatshirts",
"color": "Black Charcoal",
"size": "M",
"orig_price": 55.00,
"sale_price": 39.99
}
}

Observation

If I change the places of the "S" product to the second line, and the "M" product to the first line, then the "M" (medium) product is the one that is not matched:

ProductID,ProductCategory,ProductSubcategory,ProductName,ProductColor,ProductSize,ProductSKU,Original_Price,Sale_Price
2706414,Men,Hoodies & Sweatshirts,Fleece Pullover Hoodie,Black Charcoal,M,769413463,55.00,39.99
2706414,Men,Hoodies & Sweatshirts,Fleece Pullover Hoodie,Black Charcoal,S,769409213,55.00,39.99

So, the behavior seems tied to the first data line of the csv configuration.

Logs

2023-11-27T14:27:50.284+0000|INFO|10|main|[+--------------------------+]
2023-11-27T14:27:50.285+0000|INFO|10|main|[| A P I  S i m u l a t o r |]
2023-11-27T14:27:50.285+0000|INFO|10|main|[+--------------------------+]
2023-11-27T14:27:50.291+0000|INFO|10|main|[API Simulator initialization in progress...]
2023-11-27T14:27:50.291+0000|INFO|10|main|[API Simulator: {java version:"11.0.19+7", jvm name:"OpenJDK 64-Bit Server VM", jvm version:"11.0.19+7", jvm pid:10}]
2023-11-27T14:27:50.291+0000|INFO|10|main|[API Simulator: implementation {title:"API Simulator",version:"1.11"}]
2023-11-27T14:27:50.291+0000|INFO|10|main|[API Simulator: args=[-c, /apisimulator/apisimulator-http-1.11/config, -s, /templates/simlets, /templates]]
2023-11-27T14:27:50.305+0000|INFO|10|main|[Loading configuration from '/apisimulator/apisimulator-http-1.11/config/apisim.yaml'...]
2023-11-27T14:27:50.338+0000|INFO|10|main|[Loaded admin configuration]
2023-11-27T14:27:50.342+0000|INFO|10|main|[Loaded simulator configuration]
2023-11-27T14:27:50.346+0000|INFO|10|main|[Loaded simulation configuration]
2023-11-27T14:27:50.374+0000|INFO|10|main|[Loaded 2 simlets in 69 ms]
2023-11-27T14:27:50.376+0000|INFO|10|main|[Loading configuration from '/templates/apisim.yaml'...]
2023-11-27T14:27:51.410+0000|INFO|10|main|[Loaded 1 simlet in 1034 ms]
2023-11-27T14:27:51.411+0000|INFO|10|main|[Loading simlets from '/templates/simlets'...]
2023-11-27T14:27:51.413+0000|INFO|10|main|[Loaded 0 simlets in 1 ms]
2023-11-27T14:27:51.783+0000|INFO|10|API-Simulator-Boss-1-1|[API Simulator Server is listening on 0.0.0.0:6090]
2023-11-27T14:27:51.783+0000|INFO|10|main|[API Simulator started]
2023-11-27T14:27:51.802+0000|INFO|10|AdminServer-Boss-6-1|[Admin HTTP Server is listening on 0.0.0.0:6190]

Please let me know if any other information is needed!
Thanks!

Does the SQL Data Store feature support other databases than H2?

Hi!

I've been trying the feature documented here SQL Data Store.

When I tested with the H2 database, the feature worked as expected.

But, when I tried other driver classes, I got errors:

When trying to connect to MS SQL Server

java.lang.IllegalStateException: Failed to load JDBC driver class 'com.microsoft.sqlserver.jdbc.SQLServerDriver']

A failure also happened when trying to use org.postgresql.Driver driver class.

The configuration used:

ProductData:
  is: parameter
  from: sql

  connection:
    # Currently, pooled connections are not supported
    uses: driver # pool
    
    # Driver's fully-qualified class name
    driver: "com.microsoft.sqlserver.jdbc.SQLServerDriver"

    ...

So, my question is: Does the SQL Data Store feature support other databases than H2?

Thanks!

Environment variable being ignored when using Docker Compose

I'm using API Simulator 1.7, Docker 19.03.13-ce, build 4484c46d9d, and Docker Compose 1.27.4.

Im trying to setup the logging using APISIMULATOR_LOG_FILE, but it looks like it doesn't have any impact when I start the container.

This is my docker-compose.yml file:

version: "3.8"

services:
  simulator:
    command: ["apisimulator", "start", "/apisims/stubs"]
    container_name: api-simulator
    environment:
      APISIMULATOR_LOG_FILE: config/log4j2.xml
    image: docker.io/apimastery/apisimulator:1.7     # https://registry.hub.docker.com/r/apimastery/apisimulator/
    networks:
      - global-network
    ports:
      - "6090:6090"
    restart: on-failure
    user: "${USER}:${GROUP}"
    volumes:
      - "${PWD}/stubs:/apisims/stubs"

networks:
  global-network:
    driver: bridge
    name: api-simulator-network

...and this is my log4j2.xml configuration file:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration monitorInterval="30">
  <Properties>
    <Property name="log-pattern">%d{MM-dd-yyyy HH:mm:ss.SSS} |- %highlight{%5p}{trace=blue, debug=green, info=green, warn=yellow, error=red, fatal=red} in %style{%C{1}:%L}{cyan} [%style{%t}{magenta}] - %m%n</Property>
  </Properties>

  <Appenders>
    <Console name="Console" target="SYSTEM_OUT" follow="true">
      <PatternLayout pattern="${log-pattern}" />
    </Console>
  </Appenders>

  <!-- Logger levels: all, trace, debug, info, warn, error, fatal, off -->
  <Loggers>
    <AsyncLogger name="com.apisimulator" level="debug" additivity="false" includeLocation="true">
      <AppenderRef ref="Console" />
    </AsyncLogger>

    <Root level="warn">
      <AppenderRef ref="Console" />
    </Root>
  </Loggers>
</Configuration>

When I start the container, all I can see is this:

$ docker logs --follow api-simulator 
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by org.codehaus.groovy.reflection.CachedClass (file:/apisimulator/apisimulator-http-1.7/lib/groovy-2.5.10.jar) to method java.lang.Object.finalize()
WARNING: Please consider reporting this to the maintainers of org.codehaus.groovy.reflection.CachedClass
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release

But there is a file apisimulator.log that always gets created under my stubs directory with a similar content all the time:

2020-10-11T11:13:03.826+0000|INFO|9|main|[+--------------------------+]
2020-10-11T11:13:03.827+0000|INFO|9|main|[| A P I  S i m u l a t o r |]
2020-10-11T11:13:03.827+0000|INFO|9|main|[+--------------------------+]
2020-10-11T11:13:03.828+0000|INFO|9|main|[com.apisimulator.launcher.AbstractLaunchable.init(String[] args): initialization in progress...]
2020-10-11T11:13:03.828+0000|INFO|9|main|[com.apisimulator.launcher.AbstractLaunchable.init(String[] args): {java version:"11.0.8+10", jvm name:"OpenJDK 64-Bit Server VM", jvm version:"11.0.8+10", jvm pid:9}]
2020-10-11T11:13:03.828+0000|INFO|9|main|[com.apisimulator.launcher.AbstractLaunchable.init(String[] args): implementation {title:"HTTP API Simulator",version:"1.7"}]
2020-10-11T11:13:03.828+0000|INFO|9|main|[com.apisimulator.launcher.AbstractLaunchable.init(String[] args): args=[-c, /apisimulator/apisimulator-http-1.7/config, -s, /apisims/stubs/simlets, /apisims/stubs]]
2020-10-11T11:13:03.855+0000|INFO|9|main|[com.apisimulator.config.AppConfigLoader is looking in and under /apisims/stubs/simlets]
2020-10-11T11:13:04.228+0000|INFO|9|main|[com.apisimulator.config.AppConfigLoader loaded 1 file in 373 ms]
2020-10-11T11:13:04.349+0000|INFO|9|main|[com.apisimulator.config.SingleYamlConfigLoader.load(): loaded 2 simlets from '/apisimulator/apisimulator-http-1.7/config/apisim.yaml' in 120 ms]
2020-10-11T11:13:04.495+0000|INFO|9|main|[com.apisimulator.netty.tls.TlsHandlerFactorySupport: ALPN=true; OpenSSL=true]
2020-10-11T11:13:04.749+0000|INFO|9|API-Simulator-Boss-4-1|[com.apisimulator.netty.NettySimulatorServer.start(): listening on 0.0.0.0:6090]
2020-10-11T11:13:04.751+0000|INFO|9|main|[com.apisimulator.launcher.AbstractLaunchable.start(): API Simulator started]
2020-10-11T11:13:04.752+0000|INFO|9|main|[com.apisimulator.launcher.AdminServer: listening on port #6190]

Returning data from multiple files stored in the simulation

Recently, I added API Recorder as an option for a simulation a customer asked for some months ago. Now, they are asking whether the information stored in the *.rec files with the history of requests and responses could be retrieved as an API, specifying for instance the range of dates with the transactions to be displayed. They say that for test automation purposes it would be easier for them to get this information as an API rather than reading or parsing the files.

I searched the documentation and all the examples involving returning data from files only allow you to explicitly select one file, like here:
https://apisimulator.io/docs/1.7/standalone-api-simulator/response-http-codec.html#large-file-download-example

In the Scripting section, I don't see any example or comment saying that it is possible to retrieve the information from multiple files in a simulation folder and parse them on the response. Is it something possible to do currently? If not, are you considering the implementation?

Thank you in advance

Config file for environment-specific settings

As of version 1.9, it's not possible to provide an external config file, or just augment the default one with a (preferable predefined) key in order to read environment-specific settings.

For instance, when doing callbacks, the hostname will usually change between different environments: test, qa, etc.

If the default config file defines a predefined key (e.g.: application) that we can use for such things, then it would be possible to place any values that we need, maintaining the same (YAML-like structure). Eventually, there has to be a way to read these within the simulations...so that's needed as well ๐Ÿคฃ

Something like this:

application: # this is for us, the users ;)
  callbacks:
    - service: foo
      hostname: env.foo.tld
admin: https
host: 1.2.3.4

Actually, that's quite complicated, because the structure for the configuration is not known beforehand, so I'm not sure how to approach this one right away ๐Ÿค”

Enhance DSL to loop from data sources

Say for instance I have a CSV file with data used throughout a set of resources. Currently, I can't find a way to build JSON responses based on (the entire or a subset of) the data. I think probably using the Simula Template Scriptlets.

Would it be possible to enhance the DSL to accomplish such tasks with lists/iterators?

An example might make things more clear:

simlet: retrieve-people

# ...parameters
character:
  file: ${simlets.path}/people.csv
  from: csv
  is: parameter
  keys:
    - person_id

request:
  - header: accept
    equals: application/json
  - method: GET
  - uriPathPattern: /api/people

response:
  body:
    file: ${simlet.path}/people.response.json
  from: template
  headers:
    - name: content-type
      value: application/json
  status: 200
  template: Simula
person_id,first_name,last_name
19b766af-43e5-4695-8593-452b62fdf361,Smith,Johnson
3bd23f11-e668-4c3f-ba74-3d45e6033c46,Arabella,Foster
baf5dade-eabd-4d54-98f7-e90f6953108f,Quentin,Kelley
[
  {
    "first_name": "${person['first_name']}",
    "last_name": "${person['last_name']}",
    "_id": "${person_id}"
  },
]

Reload simulations

The way API Simulator loads the simulations is pretty handy, but once they are loaded and more changes are needed on a given (set of) simulation(s), it needs to be restarted to be able to pick up the changes.

It would be great if there is an end-point, e.g.: __admin/simulations/reload, that can be POSTed to reload/reset all the changes without stopping API Simulator.

As a bonus, it would be also nice to get all the defined simulations, e.g.: __admin/simulations.

Short-Circuit Use of External Files

While reading the Using External Files I was under the impression that simlets.path will resolve to the actual running simulation path โ€” now I know that's not the case ๐Ÿ˜

I'm aware that it's not a big deal, but it would be great if there is a variable like those (e.g.: simlet.path) that refers to the actual running simulation/simlet. Basically, the variable can resolve to the PWD of a given simlet.yaml that's being run/resolved when a request matches it.

Folder organization and 404 on simlet

Our simlets are organized in folders.

We have two folders named :

api-simulator/simlets/register/check-reference
api-simulator/simlets/reset-credentials/check-reference

and we get a 404 (API Simulator doesn't find a simlet for...") on the first one. It works fine when we rename one of them.
We think it's an issue because our routes in our simlet.yaml are diferent.

(not equals) Matching Operations is not working properly

Dear @apisim ,
Am trying to do some matching operations using equals and some other ones you can find attached my sample simulation.
The simulation should be done as below:

  • when you send request without "OTP" it give a response.
  • when you send request with "OTP" with specific value it should give another response.
  • when you send request with "OTP" with specific value it should give another response.
  • otherwise it should give a generic response.
    kindly test it and feedback.

apisim.yaml.txt

Enable HTTPS in APISimulator

I am trying to enable https connection on the APISimulator but with no luck, I had create new keystore and configure it in the tls section on the apisim.yaml file for under simulator: http but when I call the Simulation of the API I had developed using Postman API caller I'm getting error as below:
"Error: write EPROTO 3578294616:error:10000410:SSL routines:OPENSSL_internal:SSLV3_ALERT_HANDSHAKE_FAILURE:../../third_party/boringssl/src/ssl/tls_record.cc:592:SSL alert number 40 3578294616:error:1000009a:SSL routines:OPENSSL_internal:HANDSHAKE_FAILURE_ON_CLIENT_HELLO:../../third_party/boringssl/src/ssl/handshake.cc:596"

can you help?

Better logging when matching simulations

I'm not sure if this is possible today, but I haven't find a meaningful way to see the matched simulations whenever a request is sent. This is one example of what MockServer will print in the logs:

11-06-2020 19:16:17,676 |-  INFO in o.m.log.MockServerEventLog:108 [MockServer-EventLog0] - 1080 received request:

  {
    "method" : "GET",
    "path" : "/api/characters/c6d1413f-84c2-4781-b0ee-47c934600ee6",
    "headers" : {
      "Accept" : [ "application/json" ],
      "User-Agent" : [ "PostmanRuntime/7.26.3" ],
      "Host" : [ "localhost:1080" ],
      "Accept-Encoding" : [ "gzip, deflate, br" ],
      "Connection" : [ "keep-alive" ],
      "content-length" : [ "0" ]
    },
    "keepAlive" : true,
    "secure" : false
  }

11-06-2020 19:16:17,677 |-  INFO in o.m.log.MockServerEventLog:108 [MockServer-EventLog0] - 1080 request:

  {
    "method" : "GET",
    "path" : "/api/characters/c6d1413f-84c2-4781-b0ee-47c934600ee6",
    "headers" : {
      "Accept" : [ "application/json" ],
      "User-Agent" : [ "PostmanRuntime/7.26.3" ],
      "Host" : [ "localhost:1080" ],
      "Accept-Encoding" : [ "gzip, deflate, br" ],
      "Connection" : [ "keep-alive" ],
      "content-length" : [ "0" ]
    },
    "keepAlive" : true,
    "secure" : false
  }

 didn't match expectation:

  Expectation[hashCode=225248708,httpError=<null>,httpForward=<null>,httpForwardClassCallback=<null>,httpForwardObjectCallback=<null>,httpForwardTemplate=<null>,httpOverrideForwardedRequest=<null>,httpRequest=HttpRequest[body=JsonSchemaBody[hashCode=748364778,jsonSchema=schemas/create-character.json,parameterStyles=<null>,hashCode=1887080696,optional=<null>,type=JSON_SCHEMA,hashCode=31,not=<null>],cookies=<null>,hashCode=-550189459,headers={
    "accept" : [ "application/json" ],
    "content-type" : [ "application/json" ]
  },keepAlive=<null>,method=POST,path=/api/characters,pathParameters=<null>,queryStringParameters=<null>,secure=<null>,socketAddress=<null>,logCorrelationId=<null>,hashCode=31,not=<null>],httpResponse={
    "statusCode" : 201,
    "headers" : {
      "location" : [ "/api/characters/0f578684-3221-43b5-af31-7331ea55fbd2" ]
    }
  },httpResponseClassCallback=<null>,httpResponseObjectCallback=<null>,httpResponseTemplate=<null>,priority=0,timeToLive=TimeToLive.1[hashCode=31022,timeToLive=<null>,timeUnit=<null>,unlimited=true],times=Times.1[hashCode=2161,remainingTimes=-1,unlimited=true]]

 because:

  method didn't match

11-06-2020 19:16:17,678 |-  INFO in o.m.log.MockServerEventLog:108 [MockServer-EventLog0] - 1080 request:

  {
    "method" : "GET",
    "path" : "/api/characters/c6d1413f-84c2-4781-b0ee-47c934600ee6",
    "headers" : {
      "Accept" : [ "application/json" ],
      "User-Agent" : [ "PostmanRuntime/7.26.3" ],
      "Host" : [ "localhost:1080" ],
      "Accept-Encoding" : [ "gzip, deflate, br" ],
      "Connection" : [ "keep-alive" ],
      "content-length" : [ "0" ]
    },
    "keepAlive" : true,
    "secure" : false
  }

 didn't match expectation:

  {
    "id" : "1002. Retrieve (Nonexistent) Character",
    "priority" : 0,
    "httpRequest" : {
      "method" : "GET",
      "path" : "/api/characters/4d33ad25-96a9-47d8-b552-4dd2c4c719d1",
      "headers" : {
        "accept" : [ "application/json" ]
      }
    },
    "times" : {
      "unlimited" : true
    },
    "timeToLive" : {
      "unlimited" : true
    },
    "httpResponse" : {
      "statusCode" : 404,
      "headers" : {
        "content-type" : [ "application/json" ]
      },
      "body" : {
        "type" : "JSON",
        "json" : {
          "timestamp" : 1500597044204,
          "status" : 404,
          "error" : "Not Found",
          "message" : null,
          "path" : "/api/characters/4d33ad25-96a9-47d8-b552-4dd2c4c719d1"
        }
      }
    }
  }

 because:

  method matched
  path didn't match

11-06-2020 19:16:17,678 |-  INFO in o.m.log.MockServerEventLog:108 [MockServer-EventLog0] - 1080 request:

  {
    "method" : "GET",
    "path" : "/api/characters/c6d1413f-84c2-4781-b0ee-47c934600ee6",
    "headers" : {
      "Accept" : [ "application/json" ],
      "User-Agent" : [ "PostmanRuntime/7.26.3" ],
      "Host" : [ "localhost:1080" ],
      "Accept-Encoding" : [ "gzip, deflate, br" ],
      "Connection" : [ "keep-alive" ],
      "content-length" : [ "0" ]
    },
    "keepAlive" : true,
    "secure" : false
  }

 matched expectation:

  {
    "id" : "1003. Retrieve (Existing) Character",
    "priority" : 0,
    "httpRequest" : {
      "method" : "GET",
      "path" : "/api/characters/[0-9a-fA-F]{8}(-[0-9a-fA-F]{4}){3}-[0-9a-fA-F]{12}",
      "headers" : {
        "accept" : [ "application/json" ]
      }
    },
    "times" : {
      "unlimited" : true
    },
    "timeToLive" : {
      "unlimited" : true
    },
    "httpResponse" : {
      "statusCode" : 200,
      "headers" : {
        "content-type" : [ "application/json" ]
      },
      "body" : {
        "type" : "JSON",
        "json" : {
          "first_name" : "Guybrush",
          "last_name" : "Threepwood",
          "age" : 38,
          "_id" : "92ee8641-f7b3-4753-9966-7c4dfab4a591"
        }
      }
    }
  }

11-06-2020 19:16:17,678 |-  INFO in o.m.log.MockServerEventLog:108 [MockServer-EventLog0] - 1080 returning response:

  {
    "statusCode" : 200,
    "headers" : {
      "content-type" : [ "application/json" ]
    },
    "body" : {
      "first_name" : "Guybrush",
      "last_name" : "Threepwood",
      "age" : 38,
      "_id" : "92ee8641-f7b3-4753-9966-7c4dfab4a591"
    }
  }

 for request:

  {
    "method" : "GET",
    "path" : "/api/characters/c6d1413f-84c2-4781-b0ee-47c934600ee6",
    "headers" : {
      "Accept" : [ "application/json" ],
      "User-Agent" : [ "PostmanRuntime/7.26.3" ],
      "Host" : [ "localhost:1080" ],
      "Accept-Encoding" : [ "gzip, deflate, br" ],
      "Connection" : [ "keep-alive" ],
      "content-length" : [ "0" ]
    },
    "keepAlive" : true,
    "secure" : false
  }

 for action:

  {
    "statusCode" : 200,
    "headers" : {
      "content-type" : [ "application/json" ]
    },
    "body" : {
      "first_name" : "Guybrush",
      "last_name" : "Threepwood",
      "age" : 38,
      "_id" : "92ee8641-f7b3-4753-9966-7c4dfab4a591"
    }
  }

 from expectation:

  1003. Retrieve (Existing) Character

As shown, it's easier to debug when something doesn't match and things like that. I think that could be a great addition to the logs, and even so, doing something like what WireMock does is even better. Those folks will display the closest (simulation/expectation/mock) match according to the request send, and what was/were the field(s) that didn't match โ€” hence the corresponding response was not sent.

Support for Server-Side Events / Streaming Endpoint

I'm not sure if this is possible, but these days it's very common for an endpoint to provide a stream of data. In order to simulate this, it would require that API Simulator should be able to keep the connection open while real time data is streamed through an HTTP response/endpoint.

For the most part, I read that there is support for HTTP/2, so I guess this could be doable.

Support for gRPC

Teams continues to evolve and adopt modern technologies for efficient cross-service communication. In recent years, gRPC has gained significant traction as a high-performance, language-agnostic framework for building distributed systems.

In that regard, I would like to propose to incorporate gRPC support in APISimulator. This enhancement will align APISimulator with industry best practices (and trends ๐Ÿ˜‰), allowing to efficiently develop, test, and debug services that rely on gRPC endpoints.

Thank you for considering this!

Allow to read request body on the fly

API Simulator has a way to acquire the value from the request body by using "parameters", for instance:

ProductID:
  element: ".product.id"
  from: body
  is: parameter

Is it possible to support this "on the fly"? For instance, we can do ${_request.body.product.id} directly, because we are only interested on this value to put it in a template, but not to use it on a matcher.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.