Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 035cf88

Browse files
Thomas-McKannaThomasleandrodamascenaThomas
authoredJan 10, 2024
feat(event_handler): add support to download OpenAPI spec file (#3571)
* Add option to download OpenAPI spec file * Remove Optional from enable_download_spec Co-authored-by: Leandro Damascena <[email protected]> Signed-off-by: Thomas McKanna <[email protected]> * Add test for enable_download_spec * Add test for custom path with enable_download_spec * Show OpenAPI spec with conditional query string * Resolving conflicts and minor changes --------- Signed-off-by: Thomas McKanna <[email protected]> Co-authored-by: Thomas <[email protected]> Co-authored-by: Leandro Damascena <[email protected]> Co-authored-by: Thomas <[email protected]>
1 parent e34f719 commit 035cf88

File tree

3 files changed

+60
-22
lines changed

3 files changed

+60
-22
lines changed
 

‎aws_lambda_powertools/event_handler/api_gateway.py

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1592,6 +1592,7 @@ def enable_swagger(
15921592
middlewares: List[Callable[..., Response]], optional
15931593
List of middlewares to be used for the swagger route.
15941594
"""
1595+
from aws_lambda_powertools.event_handler.openapi.compat import model_json
15951596
from aws_lambda_powertools.event_handler.openapi.models import Server
15961597

15971598
if not swagger_base_url:
@@ -1640,7 +1641,28 @@ def swagger_handler():
16401641
license_info=license_info,
16411642
)
16421643

1643-
body = generate_swagger_html(spec, swagger_js, swagger_css)
1644+
# The .replace('</', '<\\/') part is necessary to prevent a potential issue where the JSON string contains
1645+
# </script> or similar tags. Escaping the forward slash in </ as <\/ ensures that the JSON does not
1646+
# inadvertently close the script tag, and the JSON remains a valid string within the JavaScript code.
1647+
escaped_spec = model_json(
1648+
spec,
1649+
by_alias=True,
1650+
exclude_none=True,
1651+
indent=2,
1652+
).replace("</", "<\\/")
1653+
1654+
# Check for query parameters; if "format" is specified as "json",
1655+
# respond with the JSON used in the OpenAPI spec
1656+
# Example: https://www.example.com/swagger?format=json
1657+
query_params = self.current_event.query_string_parameters or {}
1658+
if query_params.get("format") == "json":
1659+
return Response(
1660+
status_code=200,
1661+
content_type="application/json",
1662+
body=escaped_spec,
1663+
)
1664+
1665+
body = generate_swagger_html(escaped_spec, path, swagger_js, swagger_css)
16441666

16451667
return Response(
16461668
status_code=200,

‎aws_lambda_powertools/event_handler/openapi/swagger_ui/html.py

Lines changed: 6 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,19 @@
1-
from typing import TYPE_CHECKING
2-
3-
if TYPE_CHECKING:
4-
from aws_lambda_powertools.event_handler.openapi.models import OpenAPI
5-
6-
7-
def generate_swagger_html(spec: "OpenAPI", js_url: str, css_url: str) -> str:
1+
def generate_swagger_html(spec: str, path: str, js_url: str, css_url: str) -> str:
82
"""
93
Generate Swagger UI HTML page
104
115
Parameters
126
----------
13-
spec: OpenAPI
7+
spec: str
148
The OpenAPI spec
9+
path: str
10+
The path to the Swagger documentation
1511
js_url: str
1612
The URL to the Swagger UI JavaScript file
1713
css_url: str
1814
The URL to the Swagger UI CSS file
1915
"""
2016

21-
from aws_lambda_powertools.event_handler.openapi.compat import model_json
22-
23-
# The .replace('</', '<\\/') part is necessary to prevent a potential issue where the JSON string contains
24-
# </script> or similar tags. Escaping the forward slash in </ as <\/ ensures that the JSON does not inadvertently
25-
# close the script tag, and the JSON remains a valid string within the JavaScript code.
26-
escaped_spec = model_json(
27-
spec,
28-
by_alias=True,
29-
exclude_none=True,
30-
indent=2,
31-
).replace("</", "<\\/")
32-
3317
return f"""
3418
<!DOCTYPE html>
3519
<html>
@@ -60,7 +44,7 @@ def generate_swagger_html(spec: "OpenAPI", js_url: str, css_url: str) -> str:
6044
layout: "BaseLayout",
6145
showExtensions: true,
6246
showCommonExtensions: true,
63-
spec: {escaped_spec},
47+
spec: {spec},
6448
presets: [
6549
SwaggerUIBundle.presets.apis,
6650
SwaggerUIBundle.SwaggerUIStandalonePreset
@@ -71,6 +55,7 @@ def generate_swagger_html(spec: "OpenAPI", js_url: str, css_url: str) -> str:
7155
}}
7256
7357
var ui = SwaggerUIBundle(swaggerUIOptions)
58+
ui.specActions.updateUrl('{path}?format=json');
7459
</script>
7560
</html>
7661
""".strip()

‎tests/functional/event_handler/test_openapi_swagger.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
import json
2+
from typing import Dict
3+
14
from aws_lambda_powertools.event_handler import APIGatewayRestResolver
25
from tests.functional.utils import load_event
36

@@ -68,3 +71,31 @@ def test_openapi_swagger_with_custom_base_url_no_embedded_assets():
6871
LOAD_GW_EVENT["path"] = "/swagger.js"
6972
result = app(LOAD_GW_EVENT, {})
7073
assert result["statusCode"] == 404
74+
75+
76+
def test_openapi_swagger_json_view_with_default_path():
77+
app = APIGatewayRestResolver(enable_validation=True)
78+
app.enable_swagger(title="OpenAPI JSON View")
79+
LOAD_GW_EVENT["path"] = "/swagger"
80+
LOAD_GW_EVENT["queryStringParameters"] = {"format": "json"}
81+
82+
result = app(LOAD_GW_EVENT, {})
83+
84+
assert result["statusCode"] == 200
85+
assert result["multiValueHeaders"]["Content-Type"] == ["application/json"]
86+
assert isinstance(json.loads(result["body"]), Dict)
87+
assert "OpenAPI JSON View" in result["body"]
88+
89+
90+
def test_openapi_swagger_json_view_with_custom_path():
91+
app = APIGatewayRestResolver(enable_validation=True)
92+
app.enable_swagger(path="/fizzbuzz/foobar", title="OpenAPI JSON View")
93+
LOAD_GW_EVENT["path"] = "/fizzbuzz/foobar"
94+
LOAD_GW_EVENT["queryStringParameters"] = {"format": "json"}
95+
96+
result = app(LOAD_GW_EVENT, {})
97+
98+
assert result["statusCode"] == 200
99+
assert result["multiValueHeaders"]["Content-Type"] == ["application/json"]
100+
assert isinstance(json.loads(result["body"]), Dict)
101+
assert "OpenAPI JSON View" in result["body"]

0 commit comments

Comments
 (0)
Please sign in to comment.