GithubHelp home page GithubHelp logo

friendsofgo / killgrave Goto Github PK

View Code? Open in Web Editor NEW
506.0 9.0 96.0 1.04 MB

Simple way to generate mock servers written in Go

Home Page: https://friendsofgo.github.io/killgrave/

License: MIT License

Go 98.16% Makefile 0.40% Dockerfile 1.44%
golang go golang-application http mock mock-server hacktoberfest proxy-server imposter

killgrave's Introduction

Golang Killgrave

Killgrave

Killgrave is a simulator for HTTP-based APIs, in simple words a Mock Server, very easy to use, made in Go.

Github actions Version Go Report Card FriendsOfGo

Table of Content

Overview

Killgrave is a tool that provides a simple way to create a powerful simulator for HTTP-based APIs.

The Killgrave's philosophy is to provide an easy way to configure your mock server, ensuring that you don't need to learn another tooling language. For that reason we use json and yaml to generate all necessary configurations.

Killgrave provides:

  • An easy way to create imposters files, using json
  • The possibility to validate requests against json schemas.
  • Validation of request headers.
  • Using regex to allow different parameters in headers and urls.
  • Custom body and dynamic body responses.
  • Using all content-types bodies, (application/json, text/html, text/plain, image/jpeg, etc. )
  • Configure response headers.
  • Configure CORS.
  • Simulate network issues and server high loads with imposter responses delay.
  • Using configurable proxy server to call to the original server.
  • Run the tool using flags or using a config file.
  • Run your mock server using a watcher to reload on configuration changes.

Concepts

Imposters

Imposters are the most important concept of the Killgrave tool. They define the rules that determine how the server should respond to a request.

You can identify a Killgrave imposter file by its extension: .imp.json.

You can learn more about how to configure imposters in the Imposter Configuration Section.

Installing

⚠️ Even though Killgrave is a very robust tool and is being used by some companies in production environments, it's still in initial development. Therefore, 'minor' version numbers are used to signify breaking changes and 'patch' version numbers are used for non-breaking changes or bugfixing. As soon as v1.0.0 is released, Killgrave will start to use SemVer as usual.

You can install Killgrave in different ways, but all of them are very simple:

Homebrew

If you are a Mac user, you can install Killgrave using Homebrew:

$ brew install friendsofgo/tap/killgrave

⚠️ If you are installing via Homebrew, you always get the latest Killgrave version, we hope to fix this soon.

Docker

The application is also available through Docker.

docker run -it --rm -p 3000:3000 -v $PWD/:/home -w /home friendsofgo/killgrave --host 0.0.0.0

-p 3000:3000 publishes port 3000 (Killgrave's default port) inside the container to port 3000 on the host machine.

--host 0.0.0.0 is necessary to allow Killgrave to listen and respond to requests from outside the container (the default, localhost, will not capture requests from the host network).

Compile by yourself

If you want to use Killgrave from the source code, first you will need to clone the repository:

git clone [email protected]:friendsofgo/killgrave.git

Select the branch you want to use (main by default), and then compile Killgrave:

make build

This command will create an executable into the path bin/killgrave. We highly recommended the usage of this command to compile the software because parameters such as version are added to the compilation, which are necessary when reporting a bug.

Other

Windows and Linux users can download binaries from the Github Releases page.

Getting Started

To start Killgrave, you simply run the following.

$ killgrave

While you are welcome to provide your own configuration, Killgrave will default to the following configuration:

  • imposters path: imposters
  • host: localhost
  • port: 3000
  • CORS: []
  • proxy: none
  • watcher: false

Using Killgrave from the command line

Killgrave takes the following command line options. Killgrave is almost fully configurable through the command line, except for CORS, which can only be configured using the config file.

$ killgrave -h

Simple way to generate mock servers

Usage:
  killgrave [flags]

Flags:
  -c, --config string       Path to your configuration file
  -h, --help                Help for Killgrave
  -H, --host string         Set a different host than localhost (default "localhost")
  -i, --imposters string    Directory where your imposters are located (default "imposters")
  -P, --port int            Port to run the server (default 3000)
  -m, --proxy-mode string   Proxy mode, the options are all, missing or none (default "none")
  -u, --proxy-url string    The url where the proxy will redirect to
  -s, --secure              Run mock server using TLS (https)
  -v, --version             Version of Killgrave
  -w, --watcher             File watcher will reload the server on each file change

Using Killgrave by config file

If we want a more permanent configuration, we could use the option -config to specify the location of a configuration file.

The config file must be a YAML file with the following structure.

#config.yml

imposters_path: "imposters"
port: 3000
host: "localhost"
proxy:
  url: https://example.com
  mode: missing
watcher: true
cors:
  methods: ["GET"]
  headers: ["Content-Type"]
  exposed_headers: ["Cache-Control"]
  origins: ["*"]
  allow_credentials: true
watcher: true
secure: true

As you can see, you can configure all the options in a very easy way. For the above example, the file tree looks as follows, with the current working directory being mymock.

mymock/
    imposters/
        config.yml
        swapi_people.imp.json
        swapi_planets.imp.json
    Dockerfile
    MakeFile

Then in your config file, you will need to set the -imposters flag to . because the imposters folder is located in the same folder.

Historically, the options imposters_path, port, host were mandatory when using a configuration file. However, since the last version, they are no longer needed, so you can simply override those options if you want to. Furthermore, in previous versions, the imposters_path option refered to the path where the app was launched, but in the last version this is relative to the location of the config file.

The option cors is still optional and its options can be an empty array. If you want more information about the CORS options, visit the CORS section.

The watcher configuration field is optional. With this setting you can enable hot-reloads on imposter changes. Disabled by default.

The secure configuration field is optional. With this setting you can run your server using TLS options with a dummy certificate, so as to make it work with the HTTPS protocol. Disabled by default.

The option proxy-mode allows you to configure the mock in proxy mode. When this mode is enabled, Killgrave will forward any unconfigured requests to another server. More information: Proxy Section

How to use

Configure CORS

If you want to use killgrave from a client application you should consider configuring CORS.

In the CORS section of the file you can find the following options:

  • methods (string array)

    Represents the Access-Control-Request-Method header, if you don't specify it or if you leave it as an empty array, the default values will be:

    "GET", "HEAD", "POST", "PUT", "OPTIONS", "DELETE", "PATCH", "TRACE", "CONNECT"

  • headers (string array)

    Represents the Access-Control-Request-Headers header, if you don't specify it or if you leave it as an empty array, the default values will be:

    "X-Requested-With", "Content-Type", "Authorization"

  • exposed_headers (string array)

    Represents the Access-Control-Expose-Headers header, if you don't specify it or if you leave it as an empty array, the default values will be:

    "Cache-Control", "Content-Language", "Content-Type", "Expires", "Last-Modified", "Pragma"

  • origins (string array)

    Represents the Access-Control-Allow-Origin header, if you don't specify it or if you leave it as an empty array this option has not default value

  • allow_credentials (boolean)

    Enables or disables the Access-Control-Allow-Credentials header.

Preparing Killgrave for Proxy Mode

You can use Killgrave in proxy mode using the flags proxy-mode and proxy-url or their equivalent fields in the configuration file. The following three proxy modes are available:

  • none: Default. Killgrave will not behave as a proxy and the mock server will only use the configured imposters.
  • missing: With this mode the mock server will try to match the request with a configured imposter, but if no matching endpoint was found, the mock server will call to the real server, declared in the proxy-url configuration variable.
  • all: The mock server will always call to the real server, declared in the proxy-url configuration variable.

The proxy-url must be the root path of the proxied server. For example, if we have an API running on http://example.com/things, the proxy-url will be http://example.com.

Creating an Imposter

At least one imposter must be configured in order to run Killgrave. Files with the .imp.json extension in the imposters folder (default "imposters") will be interpreted as imposter files.

We use a rule-based system to match requests to imposters. Therefore, you have to organize your imposters from most restrictive to least. Here's an example of an imposter.

[
    {
        "request": {
            "method": "GET",
            "endpoint": "/gophers/01D8EMQ185CA8PRGE20DKZTGSR"            
        },
        "response": {
            "status": 200,
            "headers": {
                "Content-Type": "application/json"
            },
            "body": "{\"data\":{\"type\":\"gophers\",\"id\":\"01D8EMQ185CA8PRGE20DKZTGSR\",\"attributes\":{\"name\":\"Zebediah\",\"color\":\"Purples\",\"age\":55}}}"
        }
    }
]

This a very simple example. Killgrave has more possibilities for configuring imposters. Let's take a look at some of them in the next sections.

⚠️ Remember that you will need to escape any special char, in the properties that admit text.

Imposters Structure

The imposter object can be divided in two parts:

Request

This part defines how Killgrave should determine whether an incoming request matches the imposter or not. The request object has the following properties:

  • method (mandatory): The HTTP method of the incoming request.
  • endpoint (mandatory): Path of the endpoint relative to the base. Supports regex.
  • schemaFile: A JSON schema to validate the incoming request against.
  • params: Restrict incoming requests by query parameters. More info can be found here. Supports regex.
  • headers: Restrict incoming requests by HTTP header. More info can be found here.

Response

This part defines how Killgrave should respond to the incoming request. The response object has the following properties:

  • status (mandatory): Integer defining the HTTP status to return.
  • body or bodyFile: The response body. Either a literal string (body) or a path to a file (bodyFile). bodyFile is especially useful in the case of large outputs. This property is optional: if not response body should be returned it should be removed or left empty.
  • headers: Headers to return in the response.
  • delay: Time the server waits before responding. This can help simulate network issues, or high server load. Uses the Go ParseDuration format. Also, you can specify minimum and maximum delays separated by ':'. The response delay will be chosen at random between these values. Default value is "0s" (no delay).

Using regex in imposters

Regex in the endpoint

Killgrave uses the gorilla/mux regex format for endpoint regex matching.

In the next example, we have configured an endpoint to match with any kind of ULID ID:

[
  {
    "request": {
      "method": "GET",
      "endpoint": "/gophers/{_id:[\\w]{26}}"
    },
    "response": {
      "status": 200,
      "headers": {
        "Content-Type": "application/json"
      },
      "body": "{\"data\":{\"type\":\"gophers\",\"id\":\"01D8EMQ185CA8PRGE20DKZTGSR\",\"attributes\":{\"name\":\"Zebediah\",\"color\":\"Purples\",\"age\":55}}}"
    }
  }
]

Regex in the query parameters:

Killgrave uses the gorilla/mux regex format for query parameter regex matching.

In this example, we have configured an imposter that only matches if we receive an apiKey as query parameter:

[
  {
    "request": {
      "method": "GET",
      "endpoint": "/gophers/{_id:[\\w]{26}}",
      "params": {
        "apiKey": "{_apiKey:[\\w]+}"
      }
    },
    "response": {
      "status": 200,
      "headers": {
        "Content-Type": "application/json"
      },
      "body": "{\"data\":{\"type\":\"gophers\",\"id\":\"01D8EMQ185CA8PRGE20DKZTGSR\",\"attributes\":{\"name\":\"Zebediah\",\"color\":\"Purples\",\"age\":55}}}"
    }
  }
]

Regex in the headers:

In this case we will not need the gorilla mux nomenclature to write our regex.

In the next example, we have configured an imposter that uses regex to match an Authorization header.

[
  {
    "request": {
      "method": "GET",
      "endpoint": "/gophers/{id:[\\w]{26}}",
      "headers": {
        "Authorization": "\\w+"
      }
    },
    "response": {
      "status": 200,
      "headers": {
        "Content-Type": "application/json"
      },
      "body": "{\"data\":{\"type\":\"gophers\",\"id\":\"01D8EMQ185CA8PRGE20DKZTGSR\",\"attributes\":{\"name\":\"Zebediah\",\"color\":\"Purples\",\"age\":55}}}"
    }
  }
]

Creating an imposter using JSON Schema

Sometimes, we need to validate our request more thoroughly. In cases like this we can create an imposter that only matches with a valid json schema.

To do that we will need to define our json schema first:

imposters/schemas/create_gopher_request.json

{
    "type": "object",
    "properties": {
        "data": {
            "type": "object",
            "properties": {
                "type": {
                    "type": "string",
                    "enum": [
                        "gophers"
                    ]
                },
                "attributes": {
                    "type": "object",
                    "properties": {
                        "name": {
                            "type": "string"
                        },
                        "color": {
                            "type": "string"
                        },
                        "age": {
                            "type": "integer"
                        }
                    },
                    "required": [
                        "name",
                        "color",
                        "age"
                    ]
                }
            },
            "required": [
                "type",
                "attributes"
            ]
        }
    },
    "required": [
        "data"
    ]
}

With this json schema, we expect a request like this:

{
    "data": {
        "type": "gophers",
        "attributes": {
            "name": "Zebediah",
            "color": "Purples",
            "age": 55
        }
    }
}

Then our imposter will be configured as follows:

[
  {
    "request": {
        "method": "POST",
        "endpoint": "/gophers",
        "schemaFile": "schemas/create_gopher_request.json",
        "headers": {
            "Content-Type": "application/json"
        }
    },
    "response": {
        "status": 201,
        "headers": {
            "Content-Type": "application/json"
        }
    }
  }
]

The path where the schema is located is relative to where the imposters are.

Creating an imposter with delay

If we want to simulate a problem with the network, or create a more realistic response, we can use the delay property.

The delay property can take duration in the Go ParseDuration format. The server response will be delayed by the specified duration.

Alternatively, the delay property can take a range of two durations, separated by a ':'. In this case, the server will respond with a random delay in this range.

For example, we can modify our previous POST call to add a delay to determine that we want our response to be delayed by 1 to 5 seconds:

[
  {
    "request": {
        "method": "POST",
        "endpoint": "/gophers",
        "schemaFile": "schemas/create_gopher_request.json",
        "headers": {
            "Content-Type": "application/json"
        }
    },
    "response": {
        "status": 201,
        "headers": {
            "Content-Type": "application/json"
        },
        "delay": "1s:5s"
    }
  }
]

Creating an imposter with dynamic responses

Killgrave allows dynamic responses. Using this feature, Killgrave can return different responses on the same endpoint.

To do this, all imposters need to be ordered from most restrictive to least. Killgrave tries to match the request with each of the imposters in sequence, stopping at the first imposter that matches the request.

In the following example, we have defined multiple imposters for the POST /gophers endpoint. Let's say an incoming request does not match the JSON schema specified in the first imposter's schemaFile. Therefore, Killgrave skips this imposter and tries to match the request against the second imposter. This imposter is much less restrictive, so the request matches and the associated response is returned.

[
  {
    "request": {
        "method": "POST",
        "endpoint": "/gophers",
        "schemaFile": "schemas/create_gopher_request.json",
        "headers": {
            "Content-Type": "application/json"
        }
    },
    "response": {
        "status": 201,
        "headers": {
            "Content-Type": "application/json"
        }
    }
  },
  {
      "request": {
          "method": "POST",
          "endpoint": "/gophers"
      },
      "response": {
          "status": 400,
          "headers": {
              "Content-Type": "application/json"
          },
          "body": "{\"errors\":\"bad request\"}"
      }
  }
]

Contributing

Contributions are more than welcome, if you are interested please follow our guidelines to help you get started.

License

MIT License, see LICENSE

killgrave's People

Contributors

alexon1234 avatar amasanelli avatar aperezg avatar attron avatar bunsenmcdubbs avatar darkclainer avatar dependabot-preview[bot] avatar dependabot[bot] avatar exelban avatar flabatut avatar hmasood-px avatar jarimayenburg avatar joanlopez avatar marioarranzr avatar martin-dos-santos avatar mihirsavjani avatar sbouchardet avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

killgrave's Issues

Deploying killgrave in kubernetes ecosystem

Hi,
I'm trying to deploy killgrave in a pod on a kubernetes (Tanzu) plateform.
It deploys well with the following logs :
2022/10/18 13:42:53 imposter /etc/killgrave/imposters/..2022_10_18_13_42_52.903949163/gopher.imp.json loaded
2022/10/18 13:42:53 imposter /etc/killgrave/imposters/gopher.imp.json loaded
2022/10/18 13:42:53 The fake server is on tap now: 0.0.0.0:3000

But any http call from outside the container ends up in a time out.
Here's a call from a pod in the same namespace. My service maps the 300035 as a NodePort to the 3000 by the way :
bash-4.2$ curl -kv -H "Content-Type: text/json" mos-mock-ged.mos.svc.cluster.local:30035/gophers

  • About to connect() to mos-mock-ged.mos.svc.cluster.local port 30035 (#0)
  • Trying 100.28.252.169...
  • Connection timed out
  • Failed connect to mos-mock-ged.mos.svc.cluster.local:30035; Connection timed out
  • Closing connection 0
    curl: (7) Failed connect to mos-mock-ged.mos.svc.cluster.local:30035; Connection timed out

Is there any subtelty in the configuration other than the host : 0.0.0.0 configuration I should try ?

Mocking gRPC Servers

Context

Killgrave was born like an easy way to create mocks servers to HTTP APIs, so the philosophy of Killgrave was to make mocking easy and accessible to as many people as possible.

But now not only we have the HTTP APIs, there a lot of project that using gRPC to create theirs communications, so will be very interesting that find a way to do the Killgrave the way to mocking a gRPC server.

Proposed implementation

We will need to differentiate between an HTTP or gRPC server is calling, also we want to maintain the way to do the thing easy, so we need to find a solution that was easy to understand to everyone, so if it possible we want to continue using json to configure the imposters for the gRPC server.

Also we need that the config.yml could work for HTTP or gRPC

If you have any propose, please let us know.

Bad English in the README

The README and other documentation files contain some bad English. It would be good to clean that up.

Create imposters from Swagger responses schemas

Context

A lot of API that run on the world using Swagger to create a specification to document it. Is for that reason that we want to get the possibility to read the swagger files to create the imposters.

Proposed implementation

The idea is create a new flag called swagger-gen with the address of the swagger responses schema, we need to process those files to create all the necessary imposters on the directory that the user selected with the flag imposters.

We need create as many imposters for each endpoints as responses have, for example:

paths:
  /user/{userId}:
    get:
      summary: Returns a user by ID.
      parameters:
        - name: userId
          in: path
          required: true
          description: The ID of the user to return.
          schema:
            type: integer
            format: int64
            minimum: 1
      responses:
        '200':
          description: A user object.
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: integer
                    format: int64
                    example: 4
                  name:
                    type: string
                    example: Jessica Smith
        '400':
          description: The specified user ID is invalid (not a number).
        '404':
          description: A user with the specified ID was not found.
        default:
          description: Unexpected error

In this case we will need to create for the endpoint /user/{userId} with for imposters, one for 200 response, another for 400 and so on.

We need be compatible with OpenAPI 3 Specification and Open API 2 Specification we could do that letting to the user decide wich version want to use with two differents flags to select the path, swagger-gen and swagger3-gen

File Watcher

This is a major feature for Killgrave, since now when you made any changes above the imposters file, you need to restar manually the server, this isn't be a problem if you already have a very stable mock server, but when you are creating and configurating your server this action is very frecuently.

For that with this PR I want to add this new feature, using the flag -watcher. The server would be reload with any changes above the imposters path that you choosed on starting.

Project status

I start by telling you that your project is fantastic, I just wanted to know its status since there are some PR not included yet and the last commit is from September 2021.

Thanks so much

Allow XML schema validation on requests

Context

Killgrave was initially developed as a mock server focused on JSON HTTP APIs. However, as the responses configuration is highly flexible, it could be used to return responses of almost any type. That could allow Killgrave's users to use it also to mock XML HTTP APIs.

So, to provide those potential users a great experience, we also want to allow them configure some schema (XML) validations the same way as it's currently done for JSON requests.

Proposed implementation

The field (within imposter) to specify the file used for validation's definition can still being schemaFile (as it is currently for JSON validations).

The schema's file type (JSON vs XML) can be deduced from its extension (.json vs .xml) or a schemaType field can be eventually added.

Any suggestion about that will be welcome.

Improve the documentation

Context

Right now the tool Killgrave is using for differents companies and users, but the documentation on the repository is very poor with a very little examples. We need to improve that point to bring closer this amazing tool to more people.

Proposed implementation

The idea will be to create a doc/ directory, with an index on the main README.md listing all the features that Killgrave have.

So inside of doc/ folder we will create a pages for this features using markdown, giving a examples of how to use the tool in each case.

In this two issues, we have examples of how to use features that doesn't have any documentation yet:

If you are insteresing on participate in this task, let us know we could create a group on discord, slack or whatever to discuss the topic and organize the documentation.

How to mock a all sub paths?

Hi,

i've just found your project and really like the simplicity.
But i have trouble to mock responses for requests with unknown paths.
For example i want to mock the following requests:

- /api
- /api/23
- /something/...

and so on..
and i have tried a pattern like this

...
"endpoint": "/{*}"
...

But this would only match /api and not its sub-paths.
So is this currently possible? Something like a "catch-all" endpoint?

Thanks

A `correct_request_schema` fails on Windows

When executing tests on Windows the TestMatcherBySchema/correct_request_schema test fails. I'm not sure what the behaviour should be though. However if someone can tell me that the currently expected behaviour is, I will happily open a pull request to fix it.

$ go test -v ./...
?       github.com/friendsofgo/killgrave/cmd/killgrave  [no test files]
=== RUN   TestNewConfig
=== RUN   TestNewConfig/empty_config_file
=== RUN   TestNewConfig/valid_config_file
=== RUN   TestNewConfig/file_not_found
=== RUN   TestNewConfig/wrong_yaml_file
--- PASS: TestNewConfig (0.00s)
    --- PASS: TestNewConfig/empty_config_file (0.00s)
    --- PASS: TestNewConfig/valid_config_file (0.00s)
    --- PASS: TestNewConfig/file_not_found (0.00s)
    --- PASS: TestNewConfig/wrong_yaml_file (0.00s)
=== RUN   TestProxyModeParseString
=== RUN   TestProxyModeParseString/valid_mode
=== RUN   TestProxyModeParseString/unknown_mode
--- PASS: TestProxyModeParseString (0.00s)
    --- PASS: TestProxyModeParseString/valid_mode (0.00s)
    --- PASS: TestProxyModeParseString/unknown_mode (0.00s)
=== RUN   TestProxyModeUnmarshal
=== RUN   TestProxyModeUnmarshal/valid_mode_none
=== RUN   TestProxyModeUnmarshal/empty_mode
=== RUN   TestProxyModeUnmarshal/invalid_mode
=== RUN   TestProxyModeUnmarshal/error_input
=== RUN   TestProxyModeUnmarshal/valid_mode_all
=== RUN   TestProxyModeUnmarshal/valid_mode_missing
--- PASS: TestProxyModeUnmarshal (0.00s)
    --- PASS: TestProxyModeUnmarshal/valid_mode_none (0.00s)
    --- PASS: TestProxyModeUnmarshal/empty_mode (0.00s)
    --- PASS: TestProxyModeUnmarshal/invalid_mode (0.00s)
    --- PASS: TestProxyModeUnmarshal/error_input (0.00s)
    --- PASS: TestProxyModeUnmarshal/valid_mode_all (0.00s)
    --- PASS: TestProxyModeUnmarshal/valid_mode_missing (0.00s)
=== RUN   TestProxyMode_String
=== RUN   TestProxyMode_String/ProxyNone_must_be_return_none_string
=== RUN   TestProxyMode_String/ProxyNone_must_be_return_missing_string
=== RUN   TestProxyMode_String/ProxyNone_must_be_return_all_string
=== RUN   TestProxyMode_String/An_invalid_mode_must_return_none_string
--- PASS: TestProxyMode_String (0.00s)
    --- PASS: TestProxyMode_String/ProxyNone_must_be_return_none_string (0.00s)
    --- PASS: TestProxyMode_String/ProxyNone_must_be_return_missing_string (0.00s)
    --- PASS: TestProxyMode_String/ProxyNone_must_be_return_all_string (0.00s)
    --- PASS: TestProxyMode_String/An_invalid_mode_must_return_none_string (0.00s)
=== RUN   TestAttachWatcher
--- PASS: TestAttachWatcher (0.11s)
=== RUN   TestInitializeWatcher
=== RUN   TestInitializeWatcher/intialize_valid_watcher
=== RUN   TestInitializeWatcher/invalid_directory_to_watch
--- PASS: TestInitializeWatcher (0.00s)
    --- PASS: TestInitializeWatcher/intialize_valid_watcher (0.00s)
    --- PASS: TestInitializeWatcher/invalid_directory_to_watch (0.00s)
PASS
ok      github.com/friendsofgo/killgrave/internal       (cached)
=== RUN   TestImposterHandler
=== RUN   TestImposterHandler/valid_imposter_with_body
=== RUN   TestImposterHandler/valid_imposter_with_bodyFile
=== RUN   TestImposterHandler/valid_imposter_with_not_exists_bodyFile
--- PASS: TestImposterHandler (0.00s)
    --- PASS: TestImposterHandler/valid_imposter_with_body (0.00s)
    --- PASS: TestImposterHandler/valid_imposter_with_bodyFile (0.00s)
    --- PASS: TestImposterHandler/valid_imposter_with_not_exists_bodyFile (0.00s)
=== RUN   TestInvalidRequestWithSchema
=== RUN   TestInvalidRequestWithSchema/valid_request_no_schema
--- PASS: TestInvalidRequestWithSchema (0.00s)
    --- PASS: TestInvalidRequestWithSchema/valid_request_no_schema (0.00s)
=== RUN   TestNewProxy
=== RUN   TestNewProxy/valid_all
=== RUN   TestNewProxy/valid_mode_none
=== RUN   TestNewProxy/error_rawUrl
--- PASS: TestNewProxy (0.00s)
    --- PASS: TestNewProxy/valid_all (0.00s)
    --- PASS: TestNewProxy/valid_mode_none (0.00s)
    --- PASS: TestNewProxy/error_rawUrl (0.00s)
=== RUN   TestProxyHandler
--- PASS: TestProxyHandler (0.00s)
=== RUN   TestResponseDelayUnmarshal
=== RUN   TestResponseDelayUnmarshal/Invalid_type
=== RUN   TestResponseDelayUnmarshal/Valid_range_delay
=== RUN   TestResponseDelayUnmarshal/Range_delay_with_incorrect_delimiter
=== RUN   TestResponseDelayUnmarshal/Range_delay_with_extra_field
=== RUN   TestResponseDelayUnmarshal/Range_delay_where_second_point_is_before_first
=== RUN   TestResponseDelayUnmarshal/Valid_empty_delay
=== RUN   TestResponseDelayUnmarshal/Valid_fixed_delay
=== RUN   TestResponseDelayUnmarshal/Fixed_delay_without_unit_suffix
=== RUN   TestResponseDelayUnmarshal/Range_delay_where_second_point_invalid
--- PASS: TestResponseDelayUnmarshal (0.00s)
    --- PASS: TestResponseDelayUnmarshal/Invalid_type (0.00s)
    --- PASS: TestResponseDelayUnmarshal/Valid_range_delay (0.00s)
    --- PASS: TestResponseDelayUnmarshal/Range_delay_with_incorrect_delimiter (0.00s)
    --- PASS: TestResponseDelayUnmarshal/Range_delay_with_extra_field (0.00s)
    --- PASS: TestResponseDelayUnmarshal/Range_delay_where_second_point_is_before_first (0.00s)
    --- PASS: TestResponseDelayUnmarshal/Valid_empty_delay (0.00s)
    --- PASS: TestResponseDelayUnmarshal/Valid_fixed_delay (0.00s)
    --- PASS: TestResponseDelayUnmarshal/Fixed_delay_without_unit_suffix (0.00s)
    --- PASS: TestResponseDelayUnmarshal/Range_delay_where_second_point_invalid (0.00s)
=== RUN   TestResponseDelay
=== RUN   TestResponseDelay/Empty_delay
=== RUN   TestResponseDelay/Fixed_delay
=== RUN   TestResponseDelay/Range_delay
--- PASS: TestResponseDelay (0.00s)
    --- PASS: TestResponseDelay/Empty_delay (0.00s)
    --- PASS: TestResponseDelay/Fixed_delay (0.00s)
    --- PASS: TestResponseDelay/Range_delay (0.00s)
=== RUN   TestMatcherBySchema
=== RUN   TestMatcherBySchema/non-existing_schema_file
=== RUN   TestMatcherBySchema/empty_body_with_required_schema_file
=== RUN   TestMatcherBySchema/invalid_request_body
=== RUN   TestMatcherBySchema/correct_request_schema
    route_matchers_test.go:69: error while matching by request schema - expected: true, given: false
=== RUN   TestMatcherBySchema/imposter_without_request_schema
=== RUN   TestMatcherBySchema/malformatted_schema_file
=== RUN   TestMatcherBySchema/incorrect_request_schema
--- FAIL: TestMatcherBySchema (0.00s)
    --- PASS: TestMatcherBySchema/non-existing_schema_file (0.00s)
    --- PASS: TestMatcherBySchema/empty_body_with_required_schema_file (0.00s)
    --- PASS: TestMatcherBySchema/invalid_request_body (0.00s)
    --- FAIL: TestMatcherBySchema/correct_request_schema (0.00s)
    --- PASS: TestMatcherBySchema/imposter_without_request_schema (0.00s)
    --- PASS: TestMatcherBySchema/malformatted_schema_file (0.00s)
    --- PASS: TestMatcherBySchema/incorrect_request_schema (0.00s)
=== RUN   TestServer_Build
=== RUN   TestServer_Build/imposter_directory_not_found
=== RUN   TestServer_Build/malformatted_json
=== RUN   TestServer_Build/valid_imposter
--- PASS: TestServer_Build (0.00s)
    --- PASS: TestServer_Build/imposter_directory_not_found (0.00s)
    --- PASS: TestServer_Build/malformatted_json (0.00s)
    --- PASS: TestServer_Build/valid_imposter (0.00s)
=== RUN   TestBuildProxyMode
=== RUN   TestBuildProxyMode/ProxyAll
=== RUN   TestBuildProxyMode/ProxyMissing_Hit
=== RUN   TestBuildProxyMode/ProxyMissing_Proxied
=== RUN   TestBuildProxyMode/ProxyNone_Hit
=== RUN   TestBuildProxyMode/ProxyNone_Missing
--- PASS: TestBuildProxyMode (0.01s)
    --- PASS: TestBuildProxyMode/ProxyAll (0.00s)
    --- PASS: TestBuildProxyMode/ProxyMissing_Hit (0.00s)
    --- PASS: TestBuildProxyMode/ProxyMissing_Proxied (0.00s)
    --- PASS: TestBuildProxyMode/ProxyNone_Hit (0.00s)
    --- PASS: TestBuildProxyMode/ProxyNone_Missing (0.00s)
=== RUN   TestServer_AccessControl
--- PASS: TestServer_AccessControl (0.00s)
FAIL
FAIL    github.com/friendsofgo/killgrave/internal/server/http   0.171s
FAIL

Is there any way to configure the value of the response?

Hi,

I would like to use this to mock an external API, my question is I can configure the response, for example:

POST Request with Body {"element_id": 2}
Response {"element_id": 2, "deleted":true}

Where value of element_id match in request and response.

Proxy server feature

Context

One of the most requested features by our users is to implement a "proxy server" feature
(reasons below).

On the one hand, as the mock server is something you need to build up (provide) as you develop your API, sometimes there are some endpoints that are not already defined on the mock server configuration, so you might want to let the mock do a request to the "real" implementation when that happens.

OTOH, you might even want to have a "flag" that allows you to switch on-off the mock server.

Proposed implementation

Both situations can be solved by providing a configuration section named proxy_server with two attributes.

  • The first one named mode (or similar) with the following choices:

    • all (all the requests will be redirected)
    • missing (only the missing requests will be redirected)
    • none -or off (no proxy, default value).
  • The second one named base_url (or similar) to configure the origin of the "real" implementation.

For the proxy's implementation itself, there are many examples over internet about how to implement a proxy server with Go. Ideally, we won't want to add new third-party dependencies.

Any suggestion about that will be welcome.

Explore possible integrations with Bruno

Bruno (https://github.com/usebruno/bruno) is an alternative to Postman (and similar tools) that's gaining popularity, with its own language, also with support for defining test suites over the HTTP APIs.

So, could worth exploring if there's any kind of integration that would make sense, like any automatic translation from/to Bruno definitions, equivalent to OAPI/Swagger.

Response headers

Estaría bien que fuera posible definir los headers de la response, tal y como ya se hace para la request.

Allow setting different responses per request (repeats)

Context

A quite common feature on some of the mock servers of the market is the possibility to define a different response for each time an imposter is requested.

It basically will consists on returning the response A the first X times the imposter is requested, then returning the response B the next times and so on (as much flexible as possible).

Proposed implementation

As this is an issue tagged as just an idea, there's no implementation proposal.
Then, there's a single requirement: to keep Killgrave as simple as possible.

Example

Just as a proposal example,

It could be implemented by changing the response section (within imposter) from an object to an array of objects, adding to each of them a field times (or named similar) that would allow the user to set those different responses for each request.

However, it's entirely open, so feel free to write down a comment with your point-of-view and to start the discussion.

Callback Service Call

When the mock server gets a request after a while sends a request to the given url. This feature might be useful for 3rd API service which works with send notifications. I prepared case:

    {
        "request": {
            "method": "GET",
            "endpoint": "/gophers/01D8EMQ185CA8PRGE20DKZTGSR"            
        },
        "callback": {
            "request": {
                "method": "GET",
                "endpoint": "http://localhost:8089/v1",
                "headers": {
                    "Content-Type": "application/json"
                },
                "body": "{\"test\": \"test\"}"
                    
            },
            "delay" : "5s:10s"
        },
        "response": {
            "status": 200,
            "headers": {
                "Content-Type": "application/json"
            },
            "body": "{\"data\":{\"type\":\"gophers\",\"id\":\"01D8EMQ185CA8PRGE20DKZTGSR\",\"attributes\":{\"name\":\"Zebediah\",\"color\":\"Purples\",\"age\":55}}}"
        }
    }
]

when request get by mock server after given delay, it will send request to "http://localhost:8089/v1""
As mentioned I prepared codes then it works.

Interactive mode

Although it might be a bit out of the scope of this project, it could be interesting to enable an interactive mode in order to let the user to manually respond to some specific endpoints (applying same rules as regular imposters).

Since HTTP is a plaintext-based protocol, it shouldn't be harder than reading input from stdin and determining when then payload is finished (double /n or similar).

Thanks!!

Remove pkg/errors library

Since go1.13 version the library pkg/errors is depecrated in favor to errors on 1.13. IMHO I think that we don't need the extra library to handle the errors of our application, so I think that can be change all errors uses for new errors packages, using sentinels or wrapping it if it's necessary.

Problems using the Docker way

I have the next problem; you say to use this line to can run the Docker image
docker run -it --rm -p 3000:3000 -v $PWD/:/home -w /home friendsofgo/killgrave -h 0.0.0.0

but when y run this with this tree configuration

mymock/
-- imposters/
-- -- config.yml
-- -- swapi_people.imp.json
-- -- swapi_planets.imp.json

the image logs return the next message
image

Return bad request when you uses an invalid json schema

If you're declaring a json schema to validate to request body, when failed the mock could be more inteligent and return a 400 Bad Request instead to a 404 because the route is not found it.

Maybe we could add a param to handle this errors on the response... or return the error in a string in the response.

Allow run Killgrave with a config file

As improvement, it will be fine allow a optional config file with arguments to run your mock server like:

  • port
  • host
  • imposters file
  • CORS configuration

schemaFile with array producing false positives

Trying to mock some ElasticSearch requests containing arrays, many of them with mixed type:

  "sort": [
    {
      "date": "desc"
    },
    "_score"
  ],
  "query": {   }

With a schemaFile like this or similar variations:

{
  "type": "object",
  "properties": {
    "sort": {
      "type": "array",
      "items": [
        {
          "type": "object",
          "properties": {
            "date": {
              "const": "desc"
            }
          }
        }
      ]
    }
  },
  "required": [
    "sort"
  ]
}

Any simple request like:

  "sort": [
    {
      "date": "desc"
    }
  ]

or changing to dateINVALID": "desc" they are always a match, array contents don't make a difference at all
https://json-schema.org/understanding-json-schema/reference/array#items

Same with prefixItems https://json-schema.org/understanding-json-schema/reference/array#tupleValidation

How can we use a schemaFile to filter by array contents?

Improve the way to get the Version

Context

Today if you run Killgrave with a compiled version and you use the flag version you will get the last version that you have installed on your system. But if you want to compile Killgrave by your own or using go get command you will get dev.

This is happens because we are using the ldflags to configure the version when we build the realease version.

Proposed implementation

We need a easy way to store the version of the application and getting when the user use the flag version, although the version was compiled by the own user.

Add shell completions for Killgrave CLI

Based on the most recent changes present on the branch v0.5.0, it seems Killgrave is introducing the use of Cobra as the library used to build and run the CLI commands.

Thus would be good to also add the shell (Bash, Zsh, fish, PowerShell) completions supported by the library.
More information here.

Watcher option is not configured on the config.file

Context

All the options that the command line tool can do, the config file should be replicate also, but the watcher options is not parsed when the config is load.

Proposed implementation

Add the watcher option on the parser for config file.

docker compose documentation

Getting killgrave to work within docker compose, doing the volume mount, specifying the config file, isn't obvious. Can you add a docker compose example in the docs please? Thanks.

Query params

Además de indicar el path del endpoint, sería útil que se pudiera añadir query params.

Sugerencia: Según he visto en la documentación de Gorilla Mux (https://github.com/gorilla/mux#matching-routes), se pueden añadir query params al router de la siguiente manera:

r.Queries("key", "value")

For any JSON schema I get 404 error

Even the schema that was provided in documentation also gives me the below error in terminal:

parse "file://D:\killgrave-master\killgrave-master/internal\server\http\test\testdata\imposters/schemas/create_gopher_request.json": invalid port ":\killgrave-master\killgrave-master" after host: error validating the json schema

In postman, I get 404-Not found.
I have attached files of imposter and schema with txt extension.
create_gopher.imp.txt
create_gopher_request.txt

Please help me out if I am missing something

Where is the Debug?

The docs says:

debugger:
   enabled: false
   address: localhost:3030

but i can't find any option to enable debugging via CLI

Examples about dynamic responses

Hi guys
I was messing around your server mocker, and I was planning using it in one of our projects :)
I miss some examples, in specific, about how deal with dynamic responses based on regex endpoint or request schema. So, based in a value from request, I want to get that value and reflect it into the generated response.
If possible, could you provide some kind of example about that feature?
Thanks a lot in advance

The endpoint should work with final `/` or without it

If we have a imposter like this:

[
    {
        "request": {
            "method": "GET",
            "endpoint": "/api/people/1"
        },
        "response": {
            "status": 200,
            "headers": {
                "Content-Type": "application/json"
            },
            "bodyFile": "swapi_luke.json"
        }
    }
]

And will try to call to this endpoint on this way: http://localhost:3000/api/people/1/ the mock server will return a 404 because it's not matching with the path.

So the normal behaviour is that url ignoring if have a / or not and have the same behaviour

Config file paths suggestion

Hello,

Currently, if you want to use a config file, the paths on it are relative to the path where you are running the application. This does not make sense, because it implies the config should be changed depending on the way you run it.

IMHO, it would be better to specify the config file path relative to the path where you are running the application, but making the paths on the config file relative to the config path.

What do you think?

Thanks!

Record proxy server feature

(Straightly related with #33)

Context

A really very-well feature for Killgrave would be to be able to create "responses" (configuration) itself on-the-fly.

The idea is to let the user to configure only the imposter's request and then let Killgrave do the magic: fill the response section (within imposter) by going to the "real" implementation and getting the response.

Proposed implementation

That feature could be implemented on a two-steps development:

  • Firstly, developing a module responsible of that magic (able to create configuration from an HTTP response).
  • Secondly, allowing a new choice (record) for the proxy_server's mode configuration that would allow the user to enable that recording (just for the missing endpoints).

Structured logging

I think this project can hugely benefit from structured logging.
User should be able to see what request server handled and how (maybe why they haven't handled by supposed handler, what errors of validation happened. It can be difficult to inspect this manually).
So structured logging can help to deal with possibly huge logs that current project can generate.

We can use some dependency for this functionality such as:
https://github.com/sirupsen/logrus (very user friendly, also can be used instead of standard log)
https://github.com/uber-go/zap

Dockerization

Hello,

Just a suggestion:

It would be quite interesting to be able to run killgrave through Docker containers.

Thanks!

how to specify regex endpoints?

I'm trying to configure an endpoint that handles requests matching a regex.
Is there a sample configuration using this feature?

Refactor remove go core flag by cobra

Context

When Killgrave was born the idea was to create a simple mock server without multiple features, today, Killgrave is used by some companies in a large number of projects, for that reason Killgrave has been evolving and adding new features to make the tool more versatile.

Go flags is not very scalable and when you want to add more and more options could be a little inferno, for that reason we want to migrate all the flag handle of the cli to cobra one of more powerful tool inside of go community to handle and build very robust and scalables cli

Proposed implementation

Refactor the cli to start use cobra instead go flags, trying to apply SOLID architecture and cleaning the main.go

Brew installation (MacOS) does not work

Hi,

I tried to install Killgrave by using brew on a MacOs Monterey (i5) and it fails:

> brew install friendsofgo/tap/killgrave
Error: friendsofgo/tap/killgrave: Calling bottle :unneeded is disabled! There is no replacement.
Please report this issue to the friendsofgo/tap tap (not Homebrew/brew or Homebrew/core):
  /usr/local/Homebrew/Library/Taps/friendsofgo/homebrew-tap/Formula/killgrave.rb:10

So, would be good to do some research to understand why it's failing and how to fix it.

Thanks! 🙏🏻

Add the possibility to run the mock server using HTTPS

We need to give the possibility to run the mock server using HTTPs, for that we need to add a new flag, for example, secure with a default certificate.

Also will be fine is we could add the option to add the own certificates for the users, via string and via file.

Add a delay option to add latency to the response

Context

One of the common benefits of using a mock server is its ability to add latency to its responses in order to simulate network issues. However, that option is not available yet on Killgrave, so it's a really simple but very worthy feature to implement.

Proposed implementation (UPDATE: #32 (comment))

A new field delay under the response section (within imposter) can be created and taken into account when returning a response.

The initial implementation can be as simple as posible, for example, assuming the configuration value is always defined in a predefined unit (let's say milliseconds -ms-).

Then, that implementation can be improved in order to allow the user define the value unit (ns, ms, s...) with a predefined format (i.e. 500ms).

For those interested on doing it directly that "best way", please let us know (here on the issue) what's your proposal (the one mentioned above -500ms- or some other) to define the delay value and unit.

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.