Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion docs/openapi.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,11 @@ OxyRoute maintains a small **OpenAPI 3.0**-shaped JSON document in Rust while ro
Served vs built: one flag, two ways to set it.

- **`include_openapi`** (constructor) and **`set_openapi_served(enabled)`** both update the same native field (`AppState::include_openapi`). Use the constructor to choose the default; use **`set_openapi_served(False)`** to turn off HTTP serving later (or `True` to re-enable) without recreating the app.
- **Order does not change the in-memory spec:** routes are merged into the OpenAPI document as you register them, no matter when you flip the serving toggle. You can call **`set_openapi_served` before or after** adding routes; only HTTP exposure of `GET/HEAD /openapi.json` changes.
- **Re-enable:** an app created with **`include_openapi=False`** can still call **`set_openapi_served(True)`** to start serving the spec at `GET/HEAD /openapi.json` (same for turning back on after `set_openapi_served(False)`).
- **When serving is on** (`include_openapi=True` and not since disabled): the dispatcher answers **`GET /openapi.json`** and **`HEAD /openapi.json`** in an early check, **before** the normal router. A user-registered route on exactly `/openapi.json` will **not** run for those methods while the built-in is active.
- **While serving is off** (`include_openapi=False` at build time, or after `set_openapi_served(False)`):
- The engine does **not** return the spec for **`GET /openapi.json`** or **`HEAD /openapi.json`**. The request is **not** special-cased, so it goes through normal routing. Unless you add your own handler for that path, the client usually gets **404 Not Found**.
- The engine does **not** return the spec for **`GET /openapi.json`** or **`HEAD /openapi.json`**. The request is **not** special-cased, so it goes through normal routing. Unless you add your own handler for that path, the client usually gets **404 Not Found**. If you **do** register a handler for `/openapi.json`, that handler can serve a custom response.
- Route registration still **merges** operations into the in-memory OpenAPI document. The document is not discarded.
- **Export:** **`openapi_json()`** on the Python `App` still returns the current JSON as a string (handy in tests, admin tools, or a custom response), regardless of the serving toggle.

Expand Down
20 changes: 20 additions & 0 deletions tests/test_openapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,26 @@ async def _run() -> None:
assert "/z" in json.loads(app.openapi_json())["paths"]


def test_include_openapi_false_reenabled_with_set_openapi_served_get_200() -> None:
app = App(title="R", include_openapi=False)
app.set_openapi_served(True)

@app.get("/b")
def b() -> str:
return "b"

async def _run() -> None:
transport = httpx.ASGITransport(app=app)
async with httpx.AsyncClient(transport=transport, base_url="http://test") as c:
r = await c.get("/openapi.json")
assert r.status_code == 200, r.text
doc = r.json()
assert doc["info"]["title"] == "R"
assert "/b" in doc["paths"]

asyncio.run(_run())


def test_openapi_post_request_body_from_pydantic() -> None:
pydantic = pytest.importorskip("pydantic")

Expand Down
Loading