|
20 | 20 | "https://fonts.googleapis.com/css?family=Montserrat:300,400,700|Roboto:300,400,700" |
21 | 21 | ) |
22 | 22 |
|
| 23 | +SCALAR_UI_JS_URL = "https://cdn.jsdelivr.net/npm/@scalar/api-reference@1.28.8/dist/browser/standalone.min.js" |
| 24 | +SCALAR_UI_CSS_URL = ( |
| 25 | + "https://cdn.jsdelivr.net/npm/@scalar/api-reference@1.28.8/dist/style.min.css" |
| 26 | +) |
| 27 | +SCALAR_UI_FONT = None |
| 28 | + |
23 | 29 |
|
24 | 30 | @dataclass |
25 | 31 | class UIFilesOptions: |
@@ -153,3 +159,93 @@ def get_open_api_ui(request: Request) -> Response: |
153 | 159 | @property |
154 | 160 | def default_ui_files(self) -> UIFilesOptions: |
155 | 161 | return UIFilesOptions(REDOC_UI_JS_URL, REDOC_UI_CSS_URL, REDOC_UI_FONT_URL) |
| 162 | + |
| 163 | +class ScalarUIProvider(UIProvider): |
| 164 | + """ |
| 165 | + UI provider for Scalar API Reference. |
| 166 | + Scalar is a modern, interactive API documentation tool. |
| 167 | + """ |
| 168 | + |
| 169 | + def __init__( |
| 170 | + self, ui_path: str = "/scalar", ui_files: UIFilesOptions | None = None |
| 171 | + ) -> None: |
| 172 | + super().__init__(ui_path, ui_files) |
| 173 | + |
| 174 | + self._ui_html: bytes = b"" |
| 175 | + |
| 176 | + def get_openapi_ui_html(self, options: UIOptions) -> str: |
| 177 | + """ |
| 178 | + Returns the HTML response to serve the Scalar API Reference UI. |
| 179 | +
|
| 180 | + Parameters: |
| 181 | + options (UIOptions): Configuration options for the UI |
| 182 | +
|
| 183 | + Returns: |
| 184 | + str: HTML content for the Scalar UI |
| 185 | + """ |
| 186 | + return f"""<!DOCTYPE html> |
| 187 | +<html> |
| 188 | +<head> |
| 189 | + <title>{options.page_title}</title> |
| 190 | + <link rel="icon" href="{options.favicon_url}"/> |
| 191 | + <meta charset="utf-8" /> |
| 192 | + <meta name="viewport" content="width=device-width, initial-scale=1" /> |
| 193 | + <link href="{self.ui_files.css_url or ""}" rel="stylesheet"> |
| 194 | +</head> |
| 195 | +<body> |
| 196 | + <script |
| 197 | + id="api-reference" |
| 198 | + data-url="{options.spec_url}"></script> |
| 199 | + |
| 200 | + <script> |
| 201 | + var configuration = {{ |
| 202 | + theme: 'default', |
| 203 | + }} |
| 204 | +
|
| 205 | + document.getElementById('api-reference').dataset.configuration = |
| 206 | + JSON.stringify(configuration) |
| 207 | + </script> |
| 208 | +
|
| 209 | + <script src="{self.ui_files.js_url}"></script> |
| 210 | +</body> |
| 211 | +</html>""" |
| 212 | + |
| 213 | + def build_ui(self, options: UIOptions) -> None: |
| 214 | + """ |
| 215 | + Prepares the UI that will be served by the UI route. |
| 216 | +
|
| 217 | + Parameters: |
| 218 | + options (UIOptions): Configuration options for the UI |
| 219 | + """ |
| 220 | + self._ui_html = self.get_openapi_ui_html(options).encode("utf8") |
| 221 | + |
| 222 | + def get_ui_handler(self) -> Callable[[Request], Response]: |
| 223 | + """ |
| 224 | + Returns a request handler for the route that serves the Scalar UI. |
| 225 | +
|
| 226 | + Returns: |
| 227 | + Callable: Request handler function |
| 228 | + """ |
| 229 | + current_time = utcnow().timestamp() |
| 230 | + |
| 231 | + def get_open_api_ui(request: Request) -> Response: |
| 232 | + path = request.path |
| 233 | + |
| 234 | + if not path.endswith("/"): |
| 235 | + return moved_permanently(f"/{path.strip('/')}/") |
| 236 | + |
| 237 | + return get_response_for_static_content( |
| 238 | + request, b"text/html; charset=utf-8", self._ui_html, current_time |
| 239 | + ) |
| 240 | + |
| 241 | + return get_open_api_ui |
| 242 | + |
| 243 | + @property |
| 244 | + def default_ui_files(self) -> UIFilesOptions: |
| 245 | + """ |
| 246 | + Returns the default UI files options for Scalar. |
| 247 | +
|
| 248 | + Returns: |
| 249 | + UIFilesOptions: Default CDN URLs for Scalar UI |
| 250 | + """ |
| 251 | + return UIFilesOptions(SCALAR_UI_JS_URL, SCALAR_UI_CSS_URL, SCALAR_UI_FONT) |
0 commit comments