https://github.com/stoplightio/examplechooserprismproxy
proxy server that adds example-selection logic to an upstream Stoplight Prism mock server
https://github.com/stoplightio/examplechooserprismproxy
Last synced: 12 months ago
JSON representation
proxy server that adds example-selection logic to an upstream Stoplight Prism mock server
- Host: GitHub
- URL: https://github.com/stoplightio/examplechooserprismproxy
- Owner: stoplightio
- License: unlicense
- Created: 2023-03-11T15:29:29.000Z (over 3 years ago)
- Default Branch: main
- Last Pushed: 2024-07-05T23:34:20.000Z (almost 2 years ago)
- Last Synced: 2025-06-26T09:18:01.966Z (about 1 year ago)
- Language: Python
- Size: 111 KB
- Stars: 5
- Watchers: 2
- Forks: 0
- Open Issues: 5
-
Metadata Files:
- Readme: README.md
- License: LICENSE.md
Awesome Lists containing this project
README
- [Goal](#goal)
- [Context](#context)
- [Solution](#solution)
- [Prerequisites](#prerequisites)
- [Quick Start](#quick-start)
- [Customize](#customize)
---------------------------------------------------------------------
## Goal
Provide an example proxy server that allows arbitrarily complex
example-selection logic to an upstream [Stoplight
Prism](https://github.com/stoplightio/prism) server in "mock" mode.
## Context
For the full discussion, see https://github.com/stoplightio/prism/issues/1838.
[Stoplight Prism](https://github.com/stoplightio/prism) offers [the limited
ability to dynamically select
examples](https://docs.stoplight.io/docs/prism/83dbbd75532cf-http-mocking#dynamic-response-generation).
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](https://docs.stoplight.io/docs/prism/83dbbd75532cf-http-mocking#prism-decision-engine)
to accommodate partial or contradictory matches, and
- Greater flexibility for a variety of client, server, and network constraints.
## Solution
```mermaid
sequenceDiagram
participant client as Client
participant proxy as Example-Chooser Proxy
participant prism as prism mock -d
client->>+proxy: GET /pets/1
(without any headers)
note over proxy: custom logic inspects the request
and adds the "Prefer" header
proxy->>+prism: GET /pets/1
Prefer: example=cat
prism->>-proxy: {"id":1, "name":"Tiger"}
note over proxy: the response is forwarded unchanged
proxy->>-client: {"id":1, "name":"Tiger"}
```
### Prerequisites
- [Docker Compose](https://docs.docker.com/compose/install/#installation-scenarios)
### 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).
```sh
docker compose -f docker-compose.yaml up
```
In a different terminal, run the following commands:
```sh
% 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](./app.py) is very simple in this example.
```python
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:
- [named examples](https://swagger.io/docs/specification/adding-examples/),
- [extension properties](https://swagger.io/docs/specification/openapi-extensions/), or
- matching other properties of pre-made examples to the request
Notice that the container built by [Dockerfile](./Dockerfile) is:
- **Poorly suited to rapid-feedback development**; you'll probably want to use
[Flask's debug mode](https://flask.palletsprojects.com/en/2.2.x/cli/#debug-mode)
for debugging and/or automatic reloading;
- **Poorly suited to any Internet-facing, public deployment**; this is an
internal development tool only.