Skip to content

Commit 8df2a59

Browse files
committed
(v2) Fix build of OpenAPI Console UI URL if servers/url is missing or "/"
Fixes #2093 Changes proposed in this pull request: - Use "/" if the escaped app base path is the empty string, to adjust for new behavior of Flask and/or Werkzeug, which raises this error if the swagger.yaml file has no servers/url entry or servers/url with value "/": werkzeug.routing.exceptions.BuildError: Could not build url for endpoint '/._openapi_json'. - Add new test case to check blueprint and url prefix of open API specification with servers/url value "/" - Add new test case to confirm Swagger UI is returned when the specification (either OpenAPI or Swagger) has a trivial base path
1 parent 7c087f4 commit 8df2a59

5 files changed

Lines changed: 62 additions & 6 deletions

File tree

connexion/apis/flask_api.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -323,7 +323,7 @@ def console_ui_home(self):
323323
:return:
324324
"""
325325
openapi_json_route_name = "{blueprint}.{prefix}_openapi_json"
326-
escaped = flask_utils.flaskify_endpoint(self.base_path)
326+
escaped = flask_utils.flaskify_endpoint(self.base_path) or "/"
327327
openapi_json_route_name = openapi_json_route_name.format(
328328
blueprint=escaped, prefix=escaped
329329
)

tests/api/test_bootstrap.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,16 @@ def test_swagger_ui(simple_api_spec_dir, spec):
9898
assert b"swagger-ui-config.json" not in swagger_ui.data
9999

100100

101+
@pytest.mark.parametrize("spec", ["basepath-slash.yaml", "servers-url-slash.yaml"])
102+
def test_swagger_ui_for_basepath_slash(simple_api_spec_dir, spec):
103+
"""Verify the Swagger UI is returned when spec has trivial base path."""
104+
app = App(__name__, port=5001, specification_dir=simple_api_spec_dir, debug=True)
105+
app.add_api(spec)
106+
app_client = app.app.test_client()
107+
swagger_ui = app_client.get("/ui/") # type: flask.Response
108+
assert swagger_ui.status_code == 200
109+
110+
101111
@pytest.mark.parametrize("spec", SPECS)
102112
def test_swagger_ui_with_config(simple_api_spec_dir, spec):
103113
swagger_ui_config = {"displayOperationId": True}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
openapi: 3.0.0
2+
info:
3+
title: "Test servers/url == /"
4+
version: '1.0'
5+
6+
servers:
7+
- url: /
8+
9+
paths:
10+
'/greeting/{name}':
11+
post:
12+
summary: Generate greeting
13+
description: Generates a greeting message.
14+
operationId: fakeapi.hello.post_greeting
15+
responses:
16+
'200':
17+
description: greeting response
18+
content:
19+
'application/json':
20+
schema:
21+
type: object
22+
parameters:
23+
- name: name
24+
in: path
25+
description: Name of the person to greet.
26+
required: true
27+
schema:
28+
type: string

tests/test_api.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@ def test_api_base_path_slash():
4242
assert api.blueprint.name == "/"
4343
assert api.blueprint.url_prefix == ""
4444

45+
api2 = FlaskApi(TEST_FOLDER / "fixtures/simple/servers-url-slash.yaml")
46+
assert api2.blueprint.name == "/"
47+
assert api2.blueprint.url_prefix == ""
48+
4549

4650
def test_template():
4751
api1 = FlaskApi(

tox.ini

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,14 @@
1+
# Run all tests:
2+
# tox
3+
# Run all tests in one file in all test environments:
4+
# tox -- tests/test_api.py
5+
# Run one test in all test environments:
6+
# tox -- tests/test_api.py::test_api
7+
# Run all tests in one file in one test environment:
8+
# tox -e py39 -- tests/test_api.py
9+
# Run one test in one test environment:
10+
# tox -e py39 -- tests/test_api.py::test_api
11+
112
[flake8]
213
exclude=connexion/__init__.py
314
rst-roles=class,mod,obj
@@ -9,18 +20,21 @@ extend-ignore=E203,RST303
920
[tox]
1021
isolated_build = True
1122
envlist =
12-
{py39,py310,py311,py312}-pypi
23+
py38-min
24+
{py38,py39,py310,py311,py312}-pypi
1325
pre-commit
14-
# min test modifies pyproject.toml so run it last
15-
py39-min
1626

1727
[gh-actions]
1828
python =
19-
3.9: py39-min,py39-pypi
29+
3.8: py38-min,py38-pypi
30+
3.9: py39-pypi
2031
3.10: py310-pypi
2132
3.11: py311-pypi,pre-commit
2233
3.12: py312-pypi
2334

35+
[pytest]
36+
testpaths = tests
37+
2438
[testenv]
2539
setenv=PYTHONPATH = {toxinidir}:{toxinidir}
2640
deps=
@@ -34,7 +48,7 @@ commands=
3448
poetry lock
3549
poetry install --all-extras --with tests
3650
poetry show
37-
poetry run python -m pytest tests --cov connexion --cov-report term-missing
51+
poetry run python -m pytest --cov connexion --cov-report term-missing {posargs}
3852
min: mv -f pyproject.toml.bak pyproject.toml
3953

4054
[testenv:pre-commit]

0 commit comments

Comments
 (0)