Skip to content

Commit dabc858

Browse files
committed
Add auto name generation for paths missing operationId. Closes #92
1 parent 2966ebe commit dabc858

File tree

3 files changed

+69
-11
lines changed

3 files changed

+69
-11
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1515

1616
- Properly remove spaces from generated Enum keys (#198). Thanks @bowenwr!
1717

18-
### Added
18+
### Additions
1919

20+
- Endpoints without operationIds will have a name generated from their method and path (#92). Thanks @Kerybas & @dtkav!
2021
- Note to README about supported OpenAPI versions (#176). Thanks @filippog!
2122

2223
## 0.6.0 - 2020-09-21

openapi_python_client/parser/openapi.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,16 @@ def from_data(*, data: Dict[str, oai.PathItem]) -> Dict[str, "EndpointCollection
6363
return endpoints_by_tag
6464

6565

66+
def generate_operation_id(*, path: str, method: str) -> str:
67+
""" Generate an operationId from a path """
68+
clean_path = path.replace("{", "").replace("}", "").replace("/", "_")
69+
if clean_path.startswith("_"):
70+
clean_path = clean_path[1:]
71+
if clean_path.endswith("_"):
72+
clean_path = clean_path[:-1]
73+
return f"{method}_{clean_path}"
74+
75+
6676
@dataclass
6777
class Endpoint:
6878
"""
@@ -188,13 +198,15 @@ def from_data(*, data: oai.Operation, path: str, method: str, tag: str) -> Union
188198
""" Construct an endpoint from the OpenAPI data """
189199

190200
if data.operationId is None:
191-
return ParseError(data=data, detail="Path operations with operationId are not yet supported")
201+
name = generate_operation_id(path=path, method=method)
202+
else:
203+
name = data.operationId
192204

193205
endpoint = Endpoint(
194206
path=path,
195207
method=method,
196208
description=utils.remove_string_escapes(data.description) if data.description else "",
197-
name=data.operationId,
209+
name=name,
198210
requires_security=bool(data.security),
199211
tag=tag,
200212
)

tests/test_openapi_parser/test_openapi.py

Lines changed: 53 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -578,7 +578,7 @@ def test_from_data_bad_responses(self, mocker):
578578

579579
assert result == parse_error
580580

581-
def test_from_data(self, mocker):
581+
def test_from_data_standard(self, mocker):
582582
from openapi_python_client.parser.openapi import Endpoint
583583

584584
path = mocker.MagicMock()
@@ -613,8 +613,56 @@ def test_from_data(self, mocker):
613613
_add_responses.assert_called_once_with(_add_parameters.return_value, data.responses)
614614
_add_body.assert_called_once_with(_add_responses.return_value, data)
615615

616-
data.security = None
617-
_add_parameters.reset_mock()
616+
def test_from_data_no_operation_id(self, mocker):
617+
from openapi_python_client.parser.openapi import Endpoint
618+
619+
path = "/path/with/{param}/"
620+
method = "get"
621+
_add_parameters = mocker.patch.object(Endpoint, "_add_parameters")
622+
_add_responses = mocker.patch.object(Endpoint, "_add_responses")
623+
_add_body = mocker.patch.object(Endpoint, "_add_body")
624+
data = oai.Operation.construct(
625+
description=mocker.MagicMock(),
626+
operationId=None,
627+
security={"blah": "bloo"},
628+
responses=mocker.MagicMock(),
629+
)
630+
631+
mocker.patch("openapi_python_client.utils.remove_string_escapes", return_value=data.description)
632+
633+
endpoint = Endpoint.from_data(data=data, path=path, method=method, tag="default")
634+
635+
assert endpoint == _add_body.return_value
636+
637+
_add_parameters.assert_called_once_with(
638+
Endpoint(
639+
path=path,
640+
method=method,
641+
description=data.description,
642+
name="get_path_with_param",
643+
requires_security=True,
644+
tag="default",
645+
),
646+
data,
647+
)
648+
_add_responses.assert_called_once_with(_add_parameters.return_value, data.responses)
649+
_add_body.assert_called_once_with(_add_responses.return_value, data)
650+
651+
def test_from_data_no_security(self, mocker):
652+
from openapi_python_client.parser.openapi import Endpoint
653+
654+
data = oai.Operation.construct(
655+
description=mocker.MagicMock(),
656+
operationId=mocker.MagicMock(),
657+
security=None,
658+
responses=mocker.MagicMock(),
659+
)
660+
_add_parameters = mocker.patch.object(Endpoint, "_add_parameters")
661+
_add_responses = mocker.patch.object(Endpoint, "_add_responses")
662+
_add_body = mocker.patch.object(Endpoint, "_add_body")
663+
path = mocker.MagicMock()
664+
method = mocker.MagicMock()
665+
mocker.patch("openapi_python_client.utils.remove_string_escapes", return_value=data.description)
618666

619667
Endpoint.from_data(data=data, path=path, method=method, tag="a")
620668

@@ -629,11 +677,8 @@ def test_from_data(self, mocker):
629677
),
630678
data,
631679
)
632-
633-
data.operationId = None
634-
assert Endpoint.from_data(data=data, path=path, method=method, tag="a") == ParseError(
635-
data=data, detail="Path operations with operationId are not yet supported"
636-
)
680+
_add_responses.assert_called_once_with(_add_parameters.return_value, data.responses)
681+
_add_body.assert_called_once_with(_add_responses.return_value, data)
637682

638683

639684
class TestImportStringFromReference:

0 commit comments

Comments
 (0)