Skip to content

Commit 9cf7231

Browse files
author
Черемушкин Вячеслав
committed
fix path params schema
1 parent 35cd39f commit 9cf7231

File tree

4 files changed

+83
-11
lines changed

4 files changed

+83
-11
lines changed

star_resty/apidocs/setup.py

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import inspect
22
import logging
3-
import re
43
from typing import Dict, Optional, Sequence, Type, Union
54

65
from apispec import APISpec
@@ -13,7 +12,7 @@
1312
from star_resty.method import Method
1413
from star_resty.method.meta import MethodMetaOptions
1514
from star_resty.method.request_parser import RequestParser
16-
from .utils import resolve_schema_name
15+
from .utils import convert_path, resolve_schema_name
1716

1817
logger = logging.getLogger(__name__)
1918

@@ -52,9 +51,12 @@ def generate_api_docs(_: Request):
5251
return UJSONResponse(spec.to_dict())
5352

5453

55-
def get_open_api_version(openapi_version: str) -> int:
56-
v = openapi_version.split('.', maxsplit=1)[0]
57-
return int(v)
54+
def get_open_api_version(version: str) -> int:
55+
v = version.split('.', maxsplit=1)[0]
56+
try:
57+
return int(v)
58+
except (ValueError, TypeError):
59+
raise ValueError(f'Invalid open api version: {version}')
5860

5961

6062
def setup_paths(app: Starlette, spec: APISpec, version: int = 2,
@@ -187,7 +189,3 @@ def create_error_schema_by_exc(e: Union[Exception, Type[Exception]]) -> Dict:
187189
schema['schema'] = error_schema
188190

189191
return schema
190-
191-
192-
def convert_path(path: str) -> str:
193-
return re.sub(r'<([^>]+)>', r'{\1}', path)

star_resty/apidocs/utils.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import inspect
2+
import re
23
from typing import Any
34

4-
__all__ = ('resolve_schema_name',)
5+
__all__ = ('resolve_schema_name', 'convert_path')
56

67

78
def resolve_schema_name(schema: Any) -> str:
@@ -12,3 +13,7 @@ def resolve_schema_name(schema: Any) -> str:
1213

1314
name = f'{cls.__module__}.{cls.__qualname__}'
1415
return name
16+
17+
18+
def convert_path(path: str) -> str:
19+
return re.sub(r'{([^:]+).*}', r'{\1}', path)

tests/test_apidocs.py

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
from starlette.applications import Starlette
2+
from starlette.routing import Mount, Route, Router
23
from starlette.testclient import TestClient
34

45
from star_resty.apidocs import setup_spec
5-
from .utils.method import CreateUser
6+
from .utils.method import CreateUser, GetUser, SearchUser
67

78

89
def test_generate_api_docs():
@@ -44,3 +45,62 @@ def test_generate_api_docs():
4445
'type': 'integer'},
4546
'name': {'type': 'string'}},
4647
'type': 'object'}}
48+
49+
50+
def test_generate_api_docs_for_router():
51+
routes = [
52+
Mount('/v1', Router([
53+
Route('/users', CreateUser.as_endpoint(), methods=['POST']),
54+
Route('/users', SearchUser.as_endpoint(), methods=['GET'])
55+
]))
56+
]
57+
app = Starlette(routes=routes)
58+
59+
setup_spec(app, title='test')
60+
61+
client = TestClient(app)
62+
resp = client.get('/apidocs.json')
63+
assert resp is not None
64+
body = resp.json()
65+
assert body is not None
66+
assert body.get('paths') == {
67+
'/v1/users': {
68+
'post': {'tags': ['users'], 'description': 'create user', 'produces': ['application/json'],
69+
'parameters': [
70+
{'in': 'path', 'name': 'id', 'required': True, 'type': 'integer', 'format': 'int32'},
71+
{'in': 'body', 'required': False, 'name': 'body',
72+
'schema': {'$ref': '#/definitions/tests.utils.method.BodySchema'}}],
73+
'responses': {
74+
'201': {'schema': {'$ref': '#/definitions/tests.utils.method.CreateUserResponse'}},
75+
'400': {'description': 'Bad request'}}},
76+
'get': {'tags': ['default'], 'produces': ['application/json'],
77+
'parameters': [
78+
{'in': 'path', 'name': 'id', 'required': True, 'type': 'integer', 'format': 'int32'},
79+
{'in': 'query', 'name': 'q', 'required': False, 'type': 'string'}],
80+
'responses': {
81+
'200': {'schema': {'$ref': '#/definitions/tests.utils.method.SearchUserResponse'}},
82+
'400': {'description': 'Bad request'}}}}}
83+
84+
85+
def test_generate_api_docs_for_path():
86+
app = Starlette()
87+
88+
setup_spec(app, title='test')
89+
app.add_route('/users/{user_id:int}', GetUser.as_endpoint(), methods=['POST'])
90+
91+
client = TestClient(app)
92+
resp = client.get('/apidocs.json')
93+
assert resp is not None
94+
body = resp.json()
95+
assert body is not None
96+
assert body.get('paths') == {
97+
'/users/{user_id}': {
98+
'post': {
99+
'tags': ['users'], 'description': 'get user', 'produces': ['application/json'],
100+
'parameters': [
101+
{'in': 'path', 'name': 'id', 'required': True, 'type': 'integer', 'format': 'int32'},
102+
{'in': 'body', 'required': False, 'name': 'body',
103+
'schema': {'$ref': '#/definitions/tests.utils.method.BodySchema'}}],
104+
'responses': {
105+
'200': {'schema': {'$ref': '#/definitions/tests.utils.method.CreateUserResponse'}},
106+
'400': {'description': 'Bad request'}}}}}

tests/utils/method.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,15 @@ async def execute(self, user: path(PathParams),
3737
return {'id': user['id'], **payload}
3838

3939

40+
class GetUser(Method):
41+
meta = Operation(tag='users', description='get user')
42+
response_schema = CreateUserResponse
43+
44+
async def execute(self, user: path(PathParams),
45+
payload: json_payload(BodySchema)):
46+
return {'id': 1}
47+
48+
4049
class SearchUser(Method):
4150
mata = Operation(tag='users', description='search user')
4251
response_schema = SearchUserResponse

0 commit comments

Comments
 (0)