Coverage for polar/openapi.py: 93%
28 statements
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-07 12:42 +0000
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-07 12:42 +0000
1from enum import StrEnum
2from typing import Any, NotRequired, TypedDict
4from fastapi import FastAPI
5from fastapi.openapi.utils import get_openapi
7from polar.config import Environment, settings
8from polar.kit.metadata import add_metadata_query_schema
9from polar.oauth2.schemas import add_oauth2_form_schemas
12class OpenAPIExternalDoc(TypedDict):
13 description: NotRequired[str]
14 url: str
17class OpenAPITag(TypedDict):
18 name: str
19 description: NotRequired[str]
20 externalDocs: NotRequired[dict[str, str]]
23class APITag(StrEnum):
24 """
25 Tags used by our documentation to better organize the endpoints.
27 They should be set after the "group" tag, which is used to group the endpoints
28 in the generated documentation.
30 **Example**
32 ```py
33 router = APIRouter(prefix="/products", tags=["products", APITag.public])
34 ```
35 """
37 public = "public"
38 private = "private"
39 mcp = "mcp"
41 @classmethod
42 def metadata(cls) -> list[OpenAPITag]:
43 return [
44 {
45 "name": cls.public,
46 "description": (
47 "Endpoints shown and documented in the Polar API documentation "
48 "and available in our SDKs."
49 ),
50 },
51 {
52 "name": cls.private,
53 "description": (
54 "Endpoints that should appear in the schema only "
55 "in development to generate our internal JS SDK."
56 ),
57 },
58 {
59 "name": cls.mcp,
60 "description": "Endpoints enabled in the MCP server.",
61 },
62 ]
65class OpenAPIParameters(TypedDict):
66 title: str
67 summary: str
68 version: str
69 description: str
70 docs_url: str | None
71 redoc_url: str | None
72 openapi_tags: list[dict[str, Any]]
73 servers: list[dict[str, Any]] | None
76OPENAPI_PARAMETERS: OpenAPIParameters = {
77 "title": "Polar API",
78 "summary": "Polar HTTP and Webhooks API",
79 "version": "0.1.0",
80 "description": "Read the docs at https://polar.sh/docs/api-reference",
81 "docs_url": None
82 if settings.is_environment({Environment.sandbox, Environment.production})
83 else "/docs",
84 "redoc_url": None
85 if settings.is_environment({Environment.sandbox, Environment.production})
86 else "/redoc",
87 "openapi_tags": APITag.metadata(), # type: ignore
88 "servers": [
89 {
90 "url": "https://api.polar.sh",
91 "description": "Production environment",
92 "x-speakeasy-server-id": "production",
93 },
94 {
95 "url": "https://sandbox-api.polar.sh",
96 "description": "Sandbox environment",
97 "x-speakeasy-server-id": "sandbox",
98 },
99 ],
100}
103def set_openapi_generator(app: FastAPI) -> None:
104 def _openapi_generator() -> dict[str, Any]:
105 if app.openapi_schema: 105 ↛ 106line 105 didn't jump to line 106 because the condition on line 105 was never true
106 return app.openapi_schema
108 openapi_schema = get_openapi(
109 title=app.title,
110 version=app.version,
111 openapi_version=app.openapi_version,
112 summary=app.summary,
113 description=app.description,
114 terms_of_service=app.terms_of_service,
115 contact=app.contact,
116 license_info=app.license_info,
117 routes=app.routes,
118 webhooks=app.webhooks.routes,
119 tags=app.openapi_tags,
120 servers=app.servers,
121 separate_input_output_schemas=app.separate_input_output_schemas,
122 )
124 openapi_schema = add_metadata_query_schema(openapi_schema)
125 openapi_schema = add_oauth2_form_schemas(openapi_schema)
127 return openapi_schema
129 app.openapi = _openapi_generator # type: ignore[method-assign]
132__all__ = [
133 "OPENAPI_PARAMETERS",
134 "APITag",
135 "set_openapi_generator",
136]