Skip to content

refactor(event-handler): Inject CSS and JS files into SwaggerUI route when no custom CDN is used. #3562

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Jan 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 6 additions & 23 deletions aws_lambda_powertools/event_handler/api_gateway.py
Original file line number Diff line number Diff line change
Expand Up @@ -1595,26 +1595,6 @@ def enable_swagger(
from aws_lambda_powertools.event_handler.openapi.compat import model_json
from aws_lambda_powertools.event_handler.openapi.models import Server

if not swagger_base_url:

@self.get("/swagger.js", include_in_schema=False)
def swagger_js():
body = Path.open(Path(__file__).parent / "openapi" / "swagger_ui" / "swagger-ui-bundle.min.js").read()
return Response(
status_code=200,
content_type="text/javascript",
body=body,
)

@self.get("/swagger.css", include_in_schema=False)
def swagger_css():
body = Path.open(Path(__file__).parent / "openapi" / "swagger_ui" / "swagger-ui.min.css").read()
return Response(
status_code=200,
content_type="text/css",
body=body,
)

@self.get(path, middlewares=middlewares, include_in_schema=False)
def swagger_handler():
base_path = self._get_base_path()
Expand All @@ -1623,8 +1603,11 @@ def swagger_handler():
swagger_js = f"{swagger_base_url}/swagger-ui-bundle.min.js"
swagger_css = f"{swagger_base_url}/swagger-ui.min.css"
else:
swagger_js = f"{base_path}/swagger.js"
swagger_css = f"{base_path}/swagger.css"
# We now inject CSS and JS into the SwaggerUI file
swagger_js = Path.open(
Path(__file__).parent / "openapi" / "swagger_ui" / "swagger-ui-bundle.min.js",
).read()
swagger_css = Path.open(Path(__file__).parent / "openapi" / "swagger_ui" / "swagger-ui.min.css").read()

openapi_servers = servers or [Server(url=(base_path or "/"))]

Expand Down Expand Up @@ -1662,7 +1645,7 @@ def swagger_handler():
body=escaped_spec,
)

body = generate_swagger_html(escaped_spec, path, swagger_js, swagger_css)
body = generate_swagger_html(escaped_spec, path, swagger_js, swagger_css, swagger_base_url)

return Response(
status_code=200,
Expand Down
15 changes: 12 additions & 3 deletions aws_lambda_powertools/event_handler/openapi/swagger_ui/html.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
def generate_swagger_html(spec: str, path: str, js_url: str, css_url: str) -> str:
def generate_swagger_html(spec: str, path: str, swagger_js: str, swagger_css: str, swagger_base_url: str) -> str:
"""
Generate Swagger UI HTML page

Expand All @@ -14,6 +14,15 @@ def generate_swagger_html(spec: str, path: str, js_url: str, css_url: str) -> st
The URL to the Swagger UI CSS file
"""

# If Swagger base URL is present, generate HTML content with linked CSS and JavaScript files
# If no Swagger base URL is provided, include CSS and JavaScript directly in the HTML
if swagger_base_url:
swagger_css_content = f"<link rel='stylesheet' type='text/css' href='{swagger_css}'>"
swagger_js_content = f"<script src='{swagger_js}'></script>"
else:
swagger_css_content = f"<style>{swagger_css}</style>"
swagger_js_content = f"<script>{swagger_js}</script>"

return f"""
<!DOCTYPE html>
<html>
Expand All @@ -24,7 +33,7 @@ def generate_swagger_html(spec: str, path: str, js_url: str, css_url: str) -> st
http-equiv="Cache-control"
content="no-cache, no-store, must-revalidate"
/>
<link rel="stylesheet" type="text/css" href="{css_url}">
{swagger_css_content}
</head>

<body>
Expand All @@ -33,7 +42,7 @@ def generate_swagger_html(spec: str, path: str, js_url: str, css_url: str) -> st
</div>
</body>

<script src="{js_url}"></script>
{swagger_js_content}

<script>
var swaggerUIOptions = {{
Expand Down

Large diffs are not rendered by default.

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion docs/core/event_handler/api_gateway.md
Original file line number Diff line number Diff line change
Expand Up @@ -484,7 +484,7 @@ There are some important **caveats** that you should know before enabling it:
| ------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Swagger UI is **publicly accessible by default** | When using `enable_swagger` method, you can [protect sensitive API endpoints by implementing a custom middleware](#customizing-swagger-ui) using your preferred authorization mechanism. |
| **No micro-functions support** yet | Swagger UI is enabled on a per resolver instance which will limit its accuracy here. |
| You need to expose **new routes** | You'll need to expose the following paths to Lambda: `/swagger`, `/swagger.css`, `/swagger.js`; ignore if you're routing all paths already. |
| You need to expose a **new route** | You'll need to expose the following path to Lambda: `/swagger`; ignore if you're routing this path already. |

```python hl_lines="12-13" title="enabling_swagger.py"
--8<-- "examples/event_handler_rest/src/enabling_swagger.py"
Expand Down
10 changes: 0 additions & 10 deletions examples/event_handler_rest/sam/template.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -67,13 +67,3 @@ Resources:
# Properties:
# Path: /swagger
# Method: GET
# SwaggerUICSS:
# Type: Api
# Properties:
# Path: /swagger.css
# Method: GET
# SwaggerUIJS:
# Type: Api
# Properties:
# Path: /swagger.js
# Method: GET
26 changes: 0 additions & 26 deletions tests/functional/event_handler/test_openapi_swagger.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,32 +17,6 @@ def test_openapi_swagger():
assert result["statusCode"] == 200
assert result["multiValueHeaders"]["Content-Type"] == ["text/html"]

# Using our embedded assets
assert "/swagger.css" in result["body"]
assert "/swagger.js" in result["body"]


def test_openapi_embedded_js():
app = APIGatewayRestResolver(enable_validation=True)
app.enable_swagger()

LOAD_GW_EVENT["path"] = "/swagger.js"

result = app(LOAD_GW_EVENT, {})
assert result["statusCode"] == 200
assert result["multiValueHeaders"]["Content-Type"] == ["text/javascript"]


def test_openapi_embedded_css():
app = APIGatewayRestResolver(enable_validation=True)
app.enable_swagger()

LOAD_GW_EVENT["path"] = "/swagger.css"

result = app(LOAD_GW_EVENT, {})
assert result["statusCode"] == 200
assert result["multiValueHeaders"]["Content-Type"] == ["text/css"]


def test_openapi_swagger_with_custom_base_url():
app = APIGatewayRestResolver(enable_validation=True)
Expand Down