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
34 changes: 34 additions & 0 deletions backend/app/core/openapi.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
from typing import Any

from pydantic import TypeAdapter

from app.session.enums import ChairEvents, DelegateEvents
from app.session.schemas import (
ClientSessionMessage,
ServerSessionMessage,
SessionEvent,
)


def add_websocket_message_schemas(openapi_schema: dict[str, Any]) -> None:
"""Add WebSocket-only message contracts to the generated OpenAPI document.

FastAPI only discovers schemas referenced by HTTP routes. These contracts are
validated on the WebSocket endpoint, so export them explicitly instead of
maintaining a fake HTTP route solely for schema generation.
"""
components = openapi_schema.setdefault("components", {}).setdefault("schemas", {})

for name, message_type in {
"ChairEvents": ChairEvents,
"DelegateEvents": DelegateEvents,
"SessionEvent": SessionEvent,
"ClientSessionMessage": ClientSessionMessage,
"ServerSessionMessage": ServerSessionMessage,
}.items():
schema = TypeAdapter(message_type).json_schema(
ref_template="#/components/schemas/{model}"
)
definitions = schema.pop("$defs", {})
components.update(definitions)
components[name] = schema
20 changes: 20 additions & 0 deletions backend/app/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@

from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from fastapi.openapi.utils import get_openapi

from app.access.views import router as access_router
from app.core.config import get_settings
from app.core.database import create_db
from app.core.openapi import add_websocket_message_schemas
from app.session.engine import SessionEngine
from app.session.manager import ConnectionManager
from app.session.views import router as session_router
Expand Down Expand Up @@ -46,3 +48,21 @@ async def lifespan(app: FastAPI):
# include commitees here?
app.include_router(session_router, prefix="/committees", tags=["committees"])
app.include_router(access_router, prefix="/access", tags=["access"])


def custom_openapi():
if app.openapi_schema:
return app.openapi_schema

openapi_schema = get_openapi(
title=app.title,
version=app.version,
description=app.description,
routes=app.routes,
)
add_websocket_message_schemas(openapi_schema)
app.openapi_schema = openapi_schema
return openapi_schema


app.openapi = custom_openapi
Loading
Loading