api: add better response documentation

This commit is contained in:
lilly 2026-05-07 21:38:22 +02:00
commit 745dfaf19f
Signed by: lilly
SSH key fingerprint: SHA256:y9T5GFw2A20WVklhetIxG1+kcg/Ce0shnQmbu1LQ37g
4 changed files with 97 additions and 16 deletions

View file

@ -1,16 +1,16 @@
from typing import Optional
from typing import Optional, List
import logging
import secrets
import sys
from datetime import datetime, UTC
from fastapi import FastAPI, Request, Response
from fastapi import FastAPI, Request, Response, HTTPException, status
from fastapi.responses import RedirectResponse
from contextlib import asynccontextmanager
from simple_openid_connect.client import OpenidClient
from simple_openid_connect.data import TokenSuccessResponse
from cachetools import TTLCache
from dooris_api import deps
from dooris_api.models import UserStatus, UserInfo
from dooris_api import deps, models, exceptions
logger = logging.getLogger(__name__)
@ -40,23 +40,24 @@ app = FastAPI(
title="Dooris", summary="API for interacting with HomeMatic door locks in CCCHH", docs_url="/api/docs",
lifespan=lifespan,
)
app.add_exception_handler(exceptions.HttpProblemException, exceptions.problem_exception_handler)
@app.get("/api/user-info/", name="get-user-info")
async def get_user_info(req: Request, current_user: deps.CurrentUser) -> UserStatus:
@app.get("/api/user-info/", name="get-user-info", tags=["auth"], responses={status.HTTP_401_UNAUTHORIZED: {"model": models.HttpProblemDetail}})
async def get_user_info(req: Request, current_user: deps.CurrentUser) -> models.UserStatus:
if current_user is None:
return UserStatus(is_logged_in=False, user_info=None)
return models.UserStatus(is_logged_in=False, user_info=None, guaranteed_session_until=None)
else:
return UserStatus(
return models.UserStatus(
is_logged_in=True,
guaranteed_session_until=datetime.fromtimestamp(current_user.id_token.exp, UTC),
user_info=UserInfo(
user_info=models.UserInfo(
username=current_user.id_token.preferred_username,
)
)
@app.get("/auth/login", response_class=RedirectResponse, status_code=302)
@app.get("/auth/login", tags=["auth"], response_class=RedirectResponse, status_code=302)
async def login_init(req: Request, resp: Response, oidc_client: deps.OpenidClient, next: Optional[str] = "") -> str:
logger.debug("starting user authentication with upstream identity provider")
@ -80,7 +81,7 @@ async def login_init(req: Request, resp: Response, oidc_client: deps.OpenidClien
return oidc_client.authorization_code_flow.start_authentication(state=state, nonce=nonce)
@app.get("/auth/login-callback", response_class=RedirectResponse, status_code=302)
@app.get("/auth/login-callback", tags=["auth"], response_class=RedirectResponse, status_code=302)
async def login_callback(req: Request, resp: Response, oidc_client: deps.OpenidClient):
# check that the user is currently in an authenticating state
# these cookies are set by the login_init() view
@ -109,4 +110,15 @@ async def login_callback(req: Request, resp: Response, oidc_client: deps.OpenidC
return f"/auth/login-error?{req.query_params}"
@app.get("/api/doors/", tags=["doors"], responses={status.HTTP_401_UNAUTHORIZED: {"model": models.HttpProblemDetail}})
async def list_doors(cache: deps.Cache, _user: deps.CurrentUser) -> List[models.Door]:
return [
models.Door(name="door1", description="A static door for testing", status="unknown"),
models.Door(name="door2", description="another static door for testing", status="unknown"),
]
@app.get("/api/doors/{name}", tags=["doors"], responses={status.HTTP_404_NOT_FOUND: {"model": models.HttpProblemDetail}, status.HTTP_401_UNAUTHORIZED: {"model": models.HttpProblemDetail}})
async def get_door(name: str, req: Request, cache: deps.Cache, _user: deps.CurrentUser) -> Optional[models.Door]:
raise exceptions.HttpProblemException(models.HttpProblemDetail.new_door_not_found(name, req.url))