From 0fa92e30608d087f930ad77751cf36b1c534a30f Mon Sep 17 00:00:00 2001 From: Gilles <43683714+corp-0@users.noreply.github.com> Date: Sun, 2 Nov 2025 20:18:38 -0300 Subject: [PATCH] feat: annotate endpoint responses for auto documentation --- src/baby_serverlist/api/serializers.py | 38 +++++++++++++-- src/baby_serverlist/api/views.py | 66 +++++++++++++++++++++++--- 2 files changed, 94 insertions(+), 10 deletions(-) diff --git a/src/baby_serverlist/api/serializers.py b/src/baby_serverlist/api/serializers.py index d381fa3..945b57c 100644 --- a/src/baby_serverlist/api/serializers.py +++ b/src/baby_serverlist/api/serializers.py @@ -22,9 +22,41 @@ class ServerStatusSerializer(serializers.Serializer): GoodFileVersion = serializers.CharField() -class ActiveServersSerializer(serializers.Serializer): - CashDateTime = serializers.DateTimeField() - servers = ServerStatusSerializer(many=True) +class CachedServerStatusSerializer(serializers.Serializer): + Passworded = serializers.BooleanField() + ServerName = serializers.CharField() + ForkName = serializers.CharField() + BuildVersion = serializers.IntegerField() + CurrentMap = serializers.CharField() + GameMode = serializers.CharField() + IngameTime = serializers.CharField() + RoundTime = serializers.CharField() + PlayerCount = serializers.IntegerField() + PlayerCountMax = serializers.IntegerField() + ServerIP = serializers.CharField() + ServerPort = serializers.IntegerField() + WinDownload = serializers.CharField() + OSXDownload = serializers.CharField() + LinuxDownload = serializers.CharField() + fps = serializers.IntegerField() + GoodFileVersion = serializers.CharField() + + +class OwnedBabyServerSerializer(serializers.Serializer): + id = serializers.UUIDField() + whitelisted = serializers.BooleanField() + live = serializers.BooleanField() + status = CachedServerStatusSerializer(allow_null=True) + + +class BabyServerTokenSerializer(serializers.Serializer): + id = serializers.UUIDField() + serverlist_token = serializers.CharField() + whitelisted = serializers.BooleanField() + + +class BabyServerStatusListSerializer(serializers.Serializer): + servers = CachedServerStatusSerializer(many=True) class RegenerateServerlistTokenSerializer(serializers.Serializer): diff --git a/src/baby_serverlist/api/views.py b/src/baby_serverlist/api/views.py index 4b054a6..411f3a9 100644 --- a/src/baby_serverlist/api/views.py +++ b/src/baby_serverlist/api/views.py @@ -4,6 +4,7 @@ from typing import cast from django.core import signing +from drf_spectacular.utils import OpenApiResponse, extend_schema, extend_schema_view from rest_framework import status from rest_framework.generics import GenericAPIView, ListAPIView from rest_framework.permissions import AllowAny @@ -12,19 +13,38 @@ from accounts.models import Account from baby_serverlist.models import SERVERLIST_TOKEN_SALT, BabyServer from commons.cache import ( + get_baby_server_status, get_many_baby_server_statuses, set_baby_server_heartbeat, set_baby_server_status, ) from commons.error_response import ErrorResponse -from .serializers import RegenerateServerlistTokenSerializer, ServerStatusSerializer +from .serializers import ( + BabyServerStatusListSerializer, + BabyServerTokenSerializer, + OwnedBabyServerSerializer, + RegenerateServerlistTokenSerializer, + ServerStatusSerializer, +) logger = logging.getLogger(__name__) +@extend_schema_view( + post=extend_schema( + request=ServerStatusSerializer, + responses={ + 200: OpenApiResponse(description="Status accepted and cached"), + 400: OpenApiResponse(description="Invalid payload or signature"), + }, + ) +) class PostServerStatusView(GenericAPIView): - """Accepts signed status payloads from baby servers and stores the latest state in cache.""" + """Accepts signed status payloads from baby servers and stores the latest state in cache. + + *** Public Endpoint *** + """ serializer_class = ServerStatusSerializer permission_classes = (AllowAny,) @@ -59,14 +79,21 @@ def post(self, request, *args, **kwargs): return Response(status=status.HTTP_200_OK) +@extend_schema_view( + post=extend_schema( + responses={201: BabyServerTokenSerializer}, + ) +) class CreateBabyServerView(GenericAPIView): - """Creates a new baby server for the authenticated user and returns the freshly minted token.""" + """Creates a new baby server for the authenticated user and returns the freshly minted token. + + *** Requires Token Authentication. *** + """ queryset = BabyServer.objects.all() def post(self, request, *args, **kwargs): user = cast(Account, request.user) - baby_server = BabyServer.objects.create(owner=user) return Response( @@ -79,8 +106,16 @@ def post(self, request, *args, **kwargs): ) +@extend_schema_view( + list=extend_schema( + responses={200: OwnedBabyServerSerializer(many=True)}, + ) +) class ListOwnedBabyServersView(ListAPIView): - """Lists the caller's baby servers with a derived `live` flag based on recent heartbeats.""" + """Lists the caller's baby servers with a derived `live` flag based on recent heartbeats. + + *** Requires Token Authentication. *** + """ def get_queryset(self): user = cast(Account, self.request.user) @@ -94,14 +129,23 @@ def list(self, request, *args, **kwargs): "id": str(server.id), "whitelisted": server.whitelisted, "live": server.is_live(), + "status": get_baby_server_status(server.id), } for server in queryset ] return Response(data, status=status.HTTP_200_OK) +@extend_schema_view( + list=extend_schema( + responses={200: BabyServerStatusListSerializer}, + ) +) class ListBabyServersView(ListAPIView): - """Return cached status payloads for all baby servers that have reported recently.""" + """Return cached status payloads for all baby servers that have reported recently. + + *** Public Endpoint *** + """ permission_classes = (AllowAny,) @@ -117,8 +161,16 @@ def list(self, request, *args, **kwargs): return Response({"servers": data}, status=status.HTTP_200_OK) +@extend_schema_view( + post=extend_schema( + responses={200: BabyServerTokenSerializer}, + ) +) class RegenerateServerlistTokenView(GenericAPIView): - """Regenerates a server's signed token after validating ownership.""" + """Regenerates a server's signed token after validating ownership. + + *** Requires Token Authentication. *** + """ serializer_class = RegenerateServerlistTokenSerializer queryset = BabyServer.objects.all()