Skip to content

proxy server that adds example-selection logic to an upstream Stoplight Prism mock server

License

Notifications You must be signed in to change notification settings

stoplightio/ExampleChooserPrismProxy

Repository files navigation


Goal

Provide an example proxy server that allows arbitrarily complex example-selection logic to an upstream Stoplight Prism server in "mock" mode.

Context

For the full discussion, see stoplightio/prism#1838.

Stoplight Prism offers the limited ability to dynamically select examples. Some users have requested:

  • More complex example selection logic within Prism, or
  • The ability to select examples based only on query string or request body content

So far, these enhancements lie outside the bounds of Prism's goals.

An alternative approach is to add a proxy server that includes this complex behavior and use it to add the necessary Prefer: HTTP header to the request to Prism. This approach has several advantages:

  • We don't need a YAML/JSON DSL embedded in OpenAPI documents suitable to all users,
  • No need to further complicate the existing Prism decision engine to accommodate partial or contradictory matches, and
  • Greater flexibility for a variety of client, server, and network constraints.

Solution

sequenceDiagram
participant client as Client
participant proxy as Example-Chooser Proxy
participant prism as prism mock -d

client->>+proxy: GET /pets/1 <br> (without any headers)
note over proxy: custom logic inspects the request <br> and adds the "Prefer" header
proxy->>+prism: GET /pets/1 <br> Prefer: example=cat
prism->>-proxy: {"id":1, "name":"Tiger"}
note over proxy: the response is forwarded unchanged
proxy->>-client: {"id":1, "name":"Tiger"}
Loading

Prerequisites

Quick Start

Start the example-chooser proxy with the following command. (Later you'll probably want the --detach option, but omitting it now makes it easy to see how the proxy interacts with Prism).

docker compose -f docker-compose.yaml up

In a different terminal, run the following commands:

% curl -s http://localhost:4010/pets/1
{"id":1,"name":"Fluffy"}

% curl -s http://localhost:4010/pets/2
{"id":2,"name":"Spot"}

% curl -v http://localhost:4010/pets/3
{"id":-14714018,"name":"lorem ipsum"}

Your docker-compose session should have logged something like the following:

[+] Running 2/0
 ⠿ Container prism           Created                                                                          0.0s
 ⠿ Container examplechooser  Recreated                                                                        0.1s
Attaching to examplechooser, prism
examplechooser  |  * Debug mode: off
examplechooser  | WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
examplechooser  |  * Running on all addresses (0.0.0.0)
examplechooser  |  * Running on http://127.0.0.1:5000
examplechooser  |  * Running on http://192.168.128.2:5000
examplechooser  | Press CTRL+C to quit
prism           | [2:40:46 AM] › [CLI] …  awaiting  Starting Prism…
prism           | [2:40:49 AM] › [CLI] ℹ  info      GET        http://0.0.0.0:4010/pets/neque
prism           | [2:40:49 AM] › [CLI] ▶  start     Prism is listening on http://0.0.0.0:4010
examplechooser  | [2023-03-11 02:40:51,100] INFO in app: added Prefer: example=cat
prism           | [2:40:51 AM] › [HTTP SERVER] get /pets/1 ℹ  info      Request received
prism           | [2:40:51 AM] ›     [NEGOTIATOR] ℹ  info      Request contains an accept header: */*
prism           | [2:40:51 AM] ›     [VALIDATOR] ✔  success   The request passed the validation rules. Looking for the best response
prism           | [2:40:51 AM] ›     [NEGOTIATOR] ✔  success   Found a compatible content for */*
prism           | [2:40:51 AM] ›     [NEGOTIATOR] ✔  success   Responding with the requested status code 200
examplechooser  | 192.168.128.1 - - [11/Mar/2023 02:40:51] "GET /pets/1 HTTP/1.1" 200 -
examplechooser  | [2023-03-11 02:41:04,121] INFO in app: added Prefer: example=dog
prism           | [2:41:04 AM] › [HTTP SERVER] get /pets/2 ℹ  info      Request received
prism           | [2:41:04 AM] ›     [NEGOTIATOR] ℹ  info      Request contains an accept header: */*
prism           | [2:41:04 AM] ›     [VALIDATOR] ✔  success   The request passed the validation rules. Looking for the best response
prism           | [2:41:04 AM] ›     [NEGOTIATOR] ✔  success   Found a compatible content for */*
prism           | [2:41:04 AM] ›     [NEGOTIATOR] ✔  success   Responding with the requested status code 200
examplechooser  | 192.168.128.1 - - [11/Mar/2023 02:41:04] "GET /pets/2 HTTP/1.1" 200 -
prism           | [2:41:13 AM] › [HTTP SERVER] get /pets/3 ℹ  info      Request received
prism           | [2:41:13 AM] ›     [NEGOTIATOR] ℹ  info      Request contains an accept header: */*
prism           | [2:41:13 AM] ›     [VALIDATOR] ✔  success   The request passed the validation rules. Looking for the best response
prism           | [2:41:13 AM] ›     [NEGOTIATOR] ✔  success   Found a compatible content for */*
prism           | [2:41:13 AM] ›     [NEGOTIATOR] ✔  success   Responding with the requested status code 200
examplechooser  | 192.168.128.1 - - [11/Mar/2023 02:41:13] "GET /pets/3 HTTP/1.1" 200 -

In particular, notice the log entries like the following, where the example-chooser proxy has added a Prefer: header to the request it forwarded to Prism.

examplechooser  | [2023-03-11 02:40:51,100] INFO in app: added Prefer: example=cat

Customize

The logic in app.py is very simple in this example.

def example_name(request):
    if request.path.endswith('/1'):
        return 'cat'
    elif request.path.endswith('/2'):
        return 'dog'
    else:
        return None

Prism users have expressed interest in varying example responses based on:

  • HTTP headers
  • query parameters
  • request body content

Additionally, you could load in the same OpenAPI file used by Prism and write logic that depends on:

Notice that the container built by Dockerfile is:

  • Poorly suited to rapid-feedback development; you'll probably want to use Flask's debug mode for debugging and/or automatic reloading;
  • Poorly suited to any Internet-facing, public deployment; this is an internal development tool only.

About

proxy server that adds example-selection logic to an upstream Stoplight Prism mock server

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published