From 98f76b53ab2bdf9d444025891cc8454c5fbf7e52 Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Fri, 3 Jul 2026 22:15:13 +0000 Subject: [PATCH] =?UTF-8?q?chore(openapi):=20OpenAPI=20=EC=8A=A4=ED=82=A4?= =?UTF-8?q?=EB=A7=88=20=EC=98=88=EC=A0=9C=20=EC=B6=94=EA=B0=80=20=EB=B0=8F?= =?UTF-8?q?=20=EB=B6=88=ED=95=84=EC=9A=94=ED=95=9C=20Required(...)=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .Jules/palette.md | 3 +++ src/newsdom_api/main.py | 4 +-- src/newsdom_api/schemas.py | 53 +++++++++++++++++++++++++++++--------- 3 files changed, 45 insertions(+), 15 deletions(-) diff --git a/.Jules/palette.md b/.Jules/palette.md index 98b9446..d81e0fd 100644 --- a/.Jules/palette.md +++ b/.Jules/palette.md @@ -25,3 +25,6 @@ ## 2024-05-18 - Improve Swagger UI DX with swagger_ui_parameters **Learning:** The default Swagger UI configuration for FastAPI applications often lacks helpful features like request duration display or dark-themed syntax highlighting, and requires users to manually click "Try it out" for every endpoint. **Action:** Always configure `swagger_ui_parameters` in the `FastAPI()` instantiation with options like `{"displayRequestDuration": True, "syntaxHighlight.theme": "monokai", "tryItOutEnabled": True}` to significantly improve the Developer Experience (DX) when interacting with the API documentation. +## 2026-07-03 - Optimize OpenAPI Examples and Required Fields in Pydantic V2 +**Learning:** In Pydantic V2 and FastAPI, adding an explicit ellipsis `...` to fields without defaults is unnecessary as they are automatically required. Furthermore, when defining OpenAPI examples, using `json_schema_extra={"example": }` avoids `PydanticDeprecatedSince20` warnings associated with passing `example` directly to `Field()` and ensures standard Swagger UI functionality. +**Action:** Remove unnecessary `...` on required fields and use `json_schema_extra={"example": }` for singular OpenAPI examples to significantly improve Developer Experience (DX) while maintaining strict `PYTHONWARNINGS` CI compatibility. diff --git a/src/newsdom_api/main.py b/src/newsdom_api/main.py index 41acaac..6b7ffd4 100644 --- a/src/newsdom_api/main.py +++ b/src/newsdom_api/main.py @@ -140,9 +140,7 @@ def _validate_pdf_structure(pdf_bytes: bytes) -> None: tags=["Parser"], ) async def parse( - file: Annotated[ - UploadFile, File(..., description="The newspaper PDF file to parse.") - ], + file: Annotated[UploadFile, File(description="The newspaper PDF file to parse.")], ) -> ParseResponse: """Parse an uploaded PDF into the canonical DOM response model.""" diff --git a/src/newsdom_api/schemas.py b/src/newsdom_api/schemas.py index 9c5a88b..9e799e4 100644 --- a/src/newsdom_api/schemas.py +++ b/src/newsdom_api/schemas.py @@ -10,16 +10,31 @@ class BoundingBox(BaseModel): """Axis-aligned bounding box expressed in page coordinates.""" - x0: float = Field(..., description="Leftmost X coordinate of the bounding box.") - y0: float = Field(..., description="Topmost Y coordinate of the bounding box.") - x1: float = Field(..., description="Rightmost X coordinate of the bounding box.") - y1: float = Field(..., description="Bottommost Y coordinate of the bounding box.") + x0: float = Field( + description="Leftmost X coordinate of the bounding box.", + json_schema_extra={"example": 72.5}, + ) + y0: float = Field( + description="Topmost Y coordinate of the bounding box.", + json_schema_extra={"example": 100.0}, + ) + x1: float = Field( + description="Rightmost X coordinate of the bounding box.", + json_schema_extra={"example": 400.5}, + ) + y1: float = Field( + description="Bottommost Y coordinate of the bounding box.", + json_schema_extra={"example": 250.0}, + ) class CaptionNode(BaseModel): """Caption text associated with an image or figure.""" - text: str = Field(..., description="Text content of the caption.") + text: str = Field( + description="Text content of the caption.", + json_schema_extra={"example": "Figure 1: Economic growth chart for Q3."}, + ) bbox: Optional[BoundingBox] = Field( default=None, description="Bounding box of the caption when parser coordinates are available.", @@ -29,7 +44,10 @@ class CaptionNode(BaseModel): class ImageNode(BaseModel): """Image metadata preserved in the canonical page structure.""" - path: str = Field(..., description="Relative path to the extracted image asset.") + path: str = Field( + description="Relative path to the extracted image asset.", + json_schema_extra={"example": "images/page1_img1.jpg"}, + ) media_type: str = Field( default="image", description="Media type label for the extracted image node.", @@ -52,9 +70,13 @@ class ArticleNode(BaseModel): """Article-level grouping of headline, body blocks, and related media.""" article_id: str = Field( - ..., description="Stable identifier for the article within the parsed document." + description="Stable identifier for the article within the parsed document.", + json_schema_extra={"example": "article-1a2b"}, + ) + headline: str = Field( + description="Primary headline text for the article.", + json_schema_extra={"example": "Local Elections Conclude Successfully"}, ) - headline: str = Field(..., description="Primary headline text for the article.") bbox: Optional[BoundingBox] = Field( default=None, description="Bounding box enclosing the article when parser coordinates are available.", @@ -81,7 +103,8 @@ class PageNode(BaseModel): """Single parsed page including article, ad, and header groupings.""" page_number: int = Field( - ..., description="One-based page number from the parsed PDF." + description="One-based page number from the parsed PDF.", + json_schema_extra={"example": 1}, ) width: Optional[float] = Field( default=None, @@ -117,10 +140,14 @@ class ParseQuality(BaseModel): """Quality metadata describing parser provenance and warnings.""" status: str = Field( - default="success", description="Parsing operation status indicator." + default="success", + description="Parsing operation status indicator.", + json_schema_extra={"example": "success"}, ) parser: str = Field( - default="mineru", description="The underlying engine used to parse the PDF." + default="mineru", + description="The underlying engine used to parse the PDF.", + json_schema_extra={"example": "mineru"}, ) warnings: List[str] = Field( default_factory=list, @@ -132,7 +159,8 @@ class ParseResponse(BaseModel): """Top-level API response for a parsed document.""" document_id: str = Field( - ..., description="Unique identifier for the parsed document." + description="Unique identifier for the parsed document.", + json_schema_extra={"example": "doc-8f9e1a"}, ) pages: List[PageNode] = Field( default_factory=list, @@ -150,4 +178,5 @@ class HealthResponse(BaseModel): status: str = Field( default="ok", description="Current operational status of the service.", + json_schema_extra={"example": "ok"}, )