Skip to content

Commit

Permalink
Replace use of apistar package with local API theme files
Browse files Browse the repository at this point in the history
This has already been submitted by @tabotkevin with GH-480, but got lost
for whatever reason.
  • Loading branch information
tabotkevin authored and amotl committed Oct 24, 2024
1 parent b39c539 commit 25c70c1
Show file tree
Hide file tree
Showing 8 changed files with 134 additions and 32 deletions.
12 changes: 9 additions & 3 deletions responder/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
from .formats import get_formats
from .routes import Router
from .staticfiles import StaticFiles
from .statics import DEFAULT_CORS_PARAMS, DEFAULT_SECRET_KEY
from .statics import DEFAULT_API_THEME, DEFAULT_CORS_PARAMS, DEFAULT_SECRET_KEY
from .templates import Templates


Expand All @@ -28,6 +28,7 @@ class API:
:param templates_dir: The directory to use for templates. Will be created for you if it doesn't already exist.
:param auto_escape: If ``True``, HTML and XML templates will automatically be escaped.
:param enable_hsts: If ``True``, send all responses to HTTPS URLs.
:param api_theme: OpenAPI documentation theme, must be one of ``elements``, ``rapidoc``, ``redoc``, ``swagger_ui``
""" # noqa: E501

status_codes = status_codes
Expand All @@ -54,6 +55,7 @@ def __init__(
cors=False,
cors_params=DEFAULT_CORS_PARAMS,
allowed_hosts=None,
api_theme=DEFAULT_API_THEME,
):
self.background = BackgroundQueue()

Expand Down Expand Up @@ -120,6 +122,7 @@ def __init__(
license=license,
openapi_route=openapi_route,
static_route=static_route,
api_theme=api_theme,
)

# TODO: Update docs for templates
Expand Down Expand Up @@ -327,13 +330,14 @@ def template_string(self, source, *args, **kwargs):
""" # noqa: E501
return self.templates.render_string(source, *args, **kwargs)

def serve(self, *, address=None, port=None, **options):
def serve(self, *, address=None, port=None, debug=False, **options):
"""Runs the application with uvicorn. If the ``PORT`` environment
variable is set, requests will be served on that port automatically to all
known hosts.
:param address: The address to bind to.
:param port: The port to bind to. If none is provided, one will be selected at random.
:param debug: Whether to run application in debug mode.
:param options: Additional keyword arguments to send to ``uvicorn.run()``.
""" # noqa: E501

Expand All @@ -348,11 +352,13 @@ def serve(self, *, address=None, port=None, **options):
port = 5042

def spawn():
uvicorn.run(self, host=address, port=port, **options)
uvicorn.run(self, host=address, port=port, debug=debug, **options)

spawn()

def run(self, **kwargs):
if "debug" not in kwargs:
kwargs.update({"debug": self.debug})
self.serve(**kwargs)

async def __call__(self, scope, receive, send):
Expand Down
37 changes: 11 additions & 26 deletions responder/ext/schema/__init__.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import os
from pathlib import Path

import apistar
import jinja2
import yaml
from apispec import APISpec, yaml_utils
from apispec.ext.marshmallow import MarshmallowPlugin

from responder import status_codes
from responder.statics import DEFAULT_API_THEME
from responder.staticfiles import StaticFiles
from responder.statics import API_THEMES, DEFAULT_API_THEME
from responder.templates import Templates


class OpenAPISchema:
Expand All @@ -26,6 +27,7 @@ def __init__(
openapi_route="/schema.yml",
docs_route="/docs/",
static_route="/static",
api_theme=DEFAULT_API_THEME,
):
self.app = app
self.schemas = {}
Expand All @@ -39,7 +41,7 @@ def __init__(
self.openapi_version = openapi
self.openapi_route = openapi_route

self.docs_theme = DEFAULT_API_THEME
self.docs_theme = api_theme if api_theme in API_THEMES else DEFAULT_API_THEME
self.docs_route = docs_route

self.plugins = [MarshmallowPlugin()] if plugins is None else plugins
Expand All @@ -50,14 +52,11 @@ def __init__(
if self.docs_route is not None:
self.app.add_route(self.docs_route, self.docs_response)

theme_path = (
Path(apistar.__file__).parent / "themes" / self.docs_theme / "static"
).resolve()
theme_path = (Path(__file__).parent / "docs").resolve()
self.templates = Templates(directory=theme_path)

self.static_route = static_route

self.app.static_app.add_directory(theme_path)

@property
def _apispec(self):
info = {}
Expand Down Expand Up @@ -120,24 +119,10 @@ def decorator(f):

@property
def docs(self):
loader = jinja2.PrefixLoader(
{
self.docs_theme: jinja2.PackageLoader(
"apistar", os.path.join("themes", self.docs_theme, "templates")
)
}
)
env = jinja2.Environment(autoescape=True, loader=loader)
document = apistar.document.Document()
document.content = yaml.safe_load(self.openapi)

template = env.get_template("/".join([self.docs_theme, "index.html"]))

return template.render(
document=document,
langs=["javascript", "python"],
code_style=None,
static_url=self.static_url,
return self.templates.render(
f"{self.docs_theme}.html",
title=self.title,
version=self.version,
schema_url="/schema.yml",
)

Expand Down
24 changes: 24 additions & 0 deletions responder/ext/schema/docs/elements.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1, shrink-to-fit=no"
/>
<title>{{ title }} {{ version }}</title>
<!-- Embed elements Elements via Web Component -->
<script src="https://unpkg.com/@stoplight/elements/web-components.min.js"></script>
<link
rel="stylesheet"
href="https://unpkg.com/@stoplight/elements/styles.min.css"
/>
</head>
<body>
<elements-api
apiDescriptionUrl="{{ schema_url }}"
router="hash"
layout="sidebar"
/>
</body>
</html>
16 changes: 16 additions & 0 deletions responder/ext/schema/docs/rapidoc.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<!DOCTYPE html>
<!-- Important: must specify -->
<html>
<head>
<title>{{ title }} {{ version }}</title>
<meta charset="utf-8" />
<!-- Important: rapi-doc uses utf8 characters -->
<script
type="module"
src="https://unpkg.com/rapidoc/dist/rapidoc-min.js"
></script>
</head>
<body>
<rapi-doc spec-url="{{ schema_url }}" show-header="false"> </rapi-doc>
</body>
</html>
23 changes: 23 additions & 0 deletions responder/ext/schema/docs/redoc.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<!DOCTYPE html>
<html>
<head>
<title>{{ title }} {{ version }}</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link
href="https://fonts.googleapis.com/css?family=Montserrat:300,400,700|Roboto:300,400,700"
rel="stylesheet"
/>

<style>
body {
margin: 0;
padding: 0;
}
</style>
</head>
<body>
<redoc spec-url="{{ schema_url }}"></redoc>
<script src="https://cdn.jsdelivr.net/npm/redoc@next/bundles/redoc.standalone.js"></script>
</body>
</html>
49 changes: 49 additions & 0 deletions responder/ext/schema/docs/swagger_ui.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>{{ title }} {{ version }}</title>
<link
rel="stylesheet"
type="text/css"
href="https://unpkg.com/swagger-ui-dist/swagger-ui.css"
/>
<style>
html {
box-sizing: border-box;
overflow: -moz-scrollbars-vertical;
overflow-y: scroll;
}

*,
*:before,
*:after {
box-sizing: inherit;
}

body {
margin: 0;
background: #fafafa;
}
</style>
</head>

<body>
<div id="swagger-ui"></div>

<script src="https://unpkg.com/swagger-ui-dist/swagger-ui-bundle.js"></script>
<script src="https://unpkg.com/swagger-ui-dist/swagger-ui-standalone-preset.js"></script>
<script>
window.onload = function () {
const ui = SwaggerUIBundle({
url: "{{ schema_url }}",
dom_id: "#swagger-ui",
deepLinking: true,
presets: [SwaggerUIBundle.presets.apis, SwaggerUIStandalonePreset],
plugins: [SwaggerUIBundle.plugins.DownloadUrl],
layout: "BaseLayout",
});
};
</script>
</body>
</html>
3 changes: 2 additions & 1 deletion responder/statics.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
API_THEMES = ["elements", "rapidoc", "redoc", "swagger_ui"]
DEFAULT_ENCODING = "utf-8"
DEFAULT_API_THEME = "swaggerui"
DEFAULT_API_THEME = "swagger_ui"
DEFAULT_SESSION_COOKIE = "Responder-Session"
DEFAULT_SECRET_KEY = "NOTASECRET" # noqa: S105

Expand Down
2 changes: 0 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,12 @@
required = [
"aiofiles",
"apispec>=1.0.0b1",
"apistar",
"chardet",
"docopt-ng",
"marshmallow",
"requests",
"requests-toolbelt",
"rfc3986",
"typesystem<0.3",
"starlette[full]",
"uvicorn[standard]",
"whitenoise",
Expand Down

0 comments on commit 25c70c1

Please sign in to comment.