feat(backend): add custom openapi generator (#10200)

This PR introduces a custom function for generating unique operation IDs
for OpenAPI specifications to improve auto-generated client code
quality.

## Why This Change?
**Better Auto-Generated Clients**: Default FastAPI operation IDs create
unclear method names in generated clients. Our custom generator produces
clean, readable operation IDs that translate to intuitive method names.

- **Before**: `get_items_api_v1_items_get` → unclear generated methods
- **After**: `get_users_list` → clean, descriptive method names

## Changes
-  **Added**: `custom_generate_unique_id` utility function
  - Generates IDs using pattern: `{method}_{tag}_{summary}`
  - Ensures uniqueness and readability
- 🔧 **Updated**: FastAPI app configuration to use custom generator

## Checklist
- [x] I have clearly listed my changes in the PR description
- [x] I have made a test plan
- [x] I have tested my changes according to the test plan:
   - [x] OpenAPI docs reflect new operation ID format
   - [x] Tested various HTTP methods, tags, and summaries
   - [x] Verified app startup functionality 
   - [x] Validated improved client generation output
This commit is contained in:
Abhimanyu Yadav
2025-06-20 19:02:39 +05:30
committed by GitHub
parent 91ea322887
commit aab40fe225

View File

@@ -1,5 +1,6 @@
import contextlib
import logging
from enum import Enum
from typing import Any, Optional
import autogpt_libs.auth.models
@@ -14,6 +15,7 @@ from autogpt_libs.feature_flag.client import (
)
from autogpt_libs.logging.utils import generate_uvicorn_config
from fastapi.exceptions import RequestValidationError
from fastapi.routing import APIRoute
import backend.data.block
import backend.data.db
@@ -67,6 +69,33 @@ async def lifespan_context(app: fastapi.FastAPI):
await backend.data.db.disconnect()
def custom_generate_unique_id(route: APIRoute):
"""Generate clean operation IDs for OpenAPI spec following the format:
{method}{tag}{summary}
"""
if not route.tags or not route.methods:
return f"{route.name}"
method = list(route.methods)[0].lower()
first_tag = route.tags[0]
if isinstance(first_tag, Enum):
tag_str = first_tag.name
else:
tag_str = str(first_tag)
tag = "".join(word.capitalize() for word in tag_str.split("_")) # v1/v2
summary = (
route.summary if route.summary else route.name
) # need to be unique, a different version could have the same summary
summary = "".join(word.capitalize() for word in str(summary).split("_"))
if tag:
return f"{method}{tag}{summary}"
else:
return f"{method}{summary}"
docs_url = (
"/docs"
if settings.config.app_env == backend.util.settings.AppEnvironment.LOCAL
@@ -82,6 +111,7 @@ app = fastapi.FastAPI(
version="0.1",
lifespan=lifespan_context,
docs_url=docs_url,
generate_unique_id_function=custom_generate_unique_id,
)