Skip to content

Commit ccd43a5

Browse files
authored
[python-fastapi] Fix: Skip sorting of path operations (#22163) (#22166)
* [python-fastapi] Fix: Skip sorting of path operations (#22163) Make use of helpful code added in 243f501 to skip sorting of path parameters. In FastAPI, order matters, see link for details: https://fastapi.tiangolo.com/tutorial/path-params/?h=path#order-matters Issue: #22163 * Update samples after previous commit Reading comprehension is hard. I missed the part of step 3 where samples would be updated in response to the change I had previous submitted. Via this commit, update samples to match expectations. The order of various endpoint implementations is now changed in the sample, matchcing the order in the yaml files that created them.
1 parent 592c262 commit ccd43a5

File tree

7 files changed

+148
-143
lines changed

7 files changed

+148
-143
lines changed

modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PythonFastAPIServerCodegen.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,11 @@ public String getHelp() {
8989
public PythonFastAPIServerCodegen() {
9090
super();
9191

92+
// Skip sorting of operations to preserve the order found in the OpenAPI spec file. See
93+
// https://fastapi.tiangolo.com/tutorial/path-params/?h=path#order-matters for details on why order matters.
94+
LOGGER.info("Skipping sorting of path operations, order matters, let the developer decide via their specification file.");
95+
setSkipSortingOperations(true);
96+
9297
modifyFeatureSet(features -> features.includeSecurityFeatures(
9398
SecurityFeature.OAuth2_AuthorizationCode,
9499
SecurityFeature.OAuth2_Password

samples/server/petstore/python-fastapi/src/openapi_server/apis/pet_api.py

Lines changed: 33 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -37,17 +37,19 @@
3737
importlib.import_module(name)
3838

3939

40-
@router.post(
40+
@router.put(
4141
"/pet",
4242
responses={
4343
200: {"model": Pet, "description": "successful operation"},
44-
405: {"description": "Invalid input"},
44+
400: {"description": "Invalid ID supplied"},
45+
404: {"description": "Pet not found"},
46+
405: {"description": "Validation exception"},
4547
},
4648
tags=["pet"],
47-
summary="Add a new pet to the store",
49+
summary="Update an existing pet",
4850
response_model_by_alias=True,
4951
)
50-
async def add_pet(
52+
async def update_pet(
5153
pet: Annotated[Pet, Field(description="Pet object that needs to be added to the store")] = Body(None, description="Pet object that needs to be added to the store"),
5254
token_petstore_auth: TokenModel = Security(
5355
get_token_petstore_auth, scopes=["write:pets", "read:pets"]
@@ -56,29 +58,29 @@ async def add_pet(
5658
""""""
5759
if not BasePetApi.subclasses:
5860
raise HTTPException(status_code=500, detail="Not implemented")
59-
return await BasePetApi.subclasses[0]().add_pet(pet)
61+
return await BasePetApi.subclasses[0]().update_pet(pet)
6062

6163

62-
@router.delete(
63-
"/pet/{petId}",
64+
@router.post(
65+
"/pet",
6466
responses={
65-
400: {"description": "Invalid pet value"},
67+
200: {"model": Pet, "description": "successful operation"},
68+
405: {"description": "Invalid input"},
6669
},
6770
tags=["pet"],
68-
summary="Deletes a pet",
71+
summary="Add a new pet to the store",
6972
response_model_by_alias=True,
7073
)
71-
async def delete_pet(
72-
petId: Annotated[StrictInt, Field(description="Pet id to delete")] = Path(..., description="Pet id to delete"),
73-
api_key: Optional[StrictStr] = Header(None, description=""),
74+
async def add_pet(
75+
pet: Annotated[Pet, Field(description="Pet object that needs to be added to the store")] = Body(None, description="Pet object that needs to be added to the store"),
7476
token_petstore_auth: TokenModel = Security(
7577
get_token_petstore_auth, scopes=["write:pets", "read:pets"]
7678
),
77-
) -> None:
79+
) -> Pet:
7880
""""""
7981
if not BasePetApi.subclasses:
8082
raise HTTPException(status_code=500, detail="Not implemented")
81-
return await BasePetApi.subclasses[0]().delete_pet(petId, api_key)
83+
return await BasePetApi.subclasses[0]().add_pet(pet)
8284

8385

8486
@router.get(
@@ -148,51 +150,49 @@ async def get_pet_by_id(
148150
return await BasePetApi.subclasses[0]().get_pet_by_id(petId)
149151

150152

151-
@router.put(
152-
"/pet",
153+
@router.post(
154+
"/pet/{petId}",
153155
responses={
154-
200: {"model": Pet, "description": "successful operation"},
155-
400: {"description": "Invalid ID supplied"},
156-
404: {"description": "Pet not found"},
157-
405: {"description": "Validation exception"},
156+
405: {"description": "Invalid input"},
158157
},
159158
tags=["pet"],
160-
summary="Update an existing pet",
159+
summary="Updates a pet in the store with form data",
161160
response_model_by_alias=True,
162161
)
163-
async def update_pet(
164-
pet: Annotated[Pet, Field(description="Pet object that needs to be added to the store")] = Body(None, description="Pet object that needs to be added to the store"),
162+
async def update_pet_with_form(
163+
petId: Annotated[StrictInt, Field(description="ID of pet that needs to be updated")] = Path(..., description="ID of pet that needs to be updated"),
164+
name: Annotated[Optional[StrictStr], Field(description="Updated name of the pet")] = Form(None, description="Updated name of the pet"),
165+
status: Annotated[Optional[StrictStr], Field(description="Updated status of the pet")] = Form(None, description="Updated status of the pet"),
165166
token_petstore_auth: TokenModel = Security(
166167
get_token_petstore_auth, scopes=["write:pets", "read:pets"]
167168
),
168-
) -> Pet:
169+
) -> None:
169170
""""""
170171
if not BasePetApi.subclasses:
171172
raise HTTPException(status_code=500, detail="Not implemented")
172-
return await BasePetApi.subclasses[0]().update_pet(pet)
173+
return await BasePetApi.subclasses[0]().update_pet_with_form(petId, name, status)
173174

174175

175-
@router.post(
176+
@router.delete(
176177
"/pet/{petId}",
177178
responses={
178-
405: {"description": "Invalid input"},
179+
400: {"description": "Invalid pet value"},
179180
},
180181
tags=["pet"],
181-
summary="Updates a pet in the store with form data",
182+
summary="Deletes a pet",
182183
response_model_by_alias=True,
183184
)
184-
async def update_pet_with_form(
185-
petId: Annotated[StrictInt, Field(description="ID of pet that needs to be updated")] = Path(..., description="ID of pet that needs to be updated"),
186-
name: Annotated[Optional[StrictStr], Field(description="Updated name of the pet")] = Form(None, description="Updated name of the pet"),
187-
status: Annotated[Optional[StrictStr], Field(description="Updated status of the pet")] = Form(None, description="Updated status of the pet"),
185+
async def delete_pet(
186+
petId: Annotated[StrictInt, Field(description="Pet id to delete")] = Path(..., description="Pet id to delete"),
187+
api_key: Optional[StrictStr] = Header(None, description=""),
188188
token_petstore_auth: TokenModel = Security(
189189
get_token_petstore_auth, scopes=["write:pets", "read:pets"]
190190
),
191191
) -> None:
192192
""""""
193193
if not BasePetApi.subclasses:
194194
raise HTTPException(status_code=500, detail="Not implemented")
195-
return await BasePetApi.subclasses[0]().update_pet_with_form(petId, name, status)
195+
return await BasePetApi.subclasses[0]().delete_pet(petId, api_key)
196196

197197

198198
@router.post(

samples/server/petstore/python-fastapi/src/openapi_server/apis/pet_api_base.py

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,18 @@ class BasePetApi:
1515
def __init_subclass__(cls, **kwargs):
1616
super().__init_subclass__(**kwargs)
1717
BasePetApi.subclasses = BasePetApi.subclasses + (cls,)
18-
async def add_pet(
18+
async def update_pet(
1919
self,
2020
pet: Annotated[Pet, Field(description="Pet object that needs to be added to the store")],
2121
) -> Pet:
2222
""""""
2323
...
2424

2525

26-
async def delete_pet(
26+
async def add_pet(
2727
self,
28-
petId: Annotated[StrictInt, Field(description="Pet id to delete")],
29-
api_key: Optional[StrictStr],
30-
) -> None:
28+
pet: Annotated[Pet, Field(description="Pet object that needs to be added to the store")],
29+
) -> Pet:
3130
""""""
3231
...
3332

@@ -56,19 +55,20 @@ async def get_pet_by_id(
5655
...
5756

5857

59-
async def update_pet(
58+
async def update_pet_with_form(
6059
self,
61-
pet: Annotated[Pet, Field(description="Pet object that needs to be added to the store")],
62-
) -> Pet:
60+
petId: Annotated[StrictInt, Field(description="ID of pet that needs to be updated")],
61+
name: Annotated[Optional[StrictStr], Field(description="Updated name of the pet")],
62+
status: Annotated[Optional[StrictStr], Field(description="Updated status of the pet")],
63+
) -> None:
6364
""""""
6465
...
6566

6667

67-
async def update_pet_with_form(
68+
async def delete_pet(
6869
self,
69-
petId: Annotated[StrictInt, Field(description="ID of pet that needs to be updated")],
70-
name: Annotated[Optional[StrictStr], Field(description="Updated name of the pet")],
71-
status: Annotated[Optional[StrictStr], Field(description="Updated status of the pet")],
70+
petId: Annotated[StrictInt, Field(description="Pet id to delete")],
71+
api_key: Optional[StrictStr],
7272
) -> None:
7373
""""""
7474
...

samples/server/petstore/python-fastapi/src/openapi_server/apis/store_api.py

Lines changed: 29 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -36,25 +36,6 @@
3636
importlib.import_module(name)
3737

3838

39-
@router.delete(
40-
"/store/order/{orderId}",
41-
responses={
42-
400: {"description": "Invalid ID supplied"},
43-
404: {"description": "Order not found"},
44-
},
45-
tags=["store"],
46-
summary="Delete purchase order by ID",
47-
response_model_by_alias=True,
48-
)
49-
async def delete_order(
50-
orderId: Annotated[StrictStr, Field(description="ID of the order that needs to be deleted")] = Path(..., description="ID of the order that needs to be deleted"),
51-
) -> None:
52-
"""For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors"""
53-
if not BaseStoreApi.subclasses:
54-
raise HTTPException(status_code=500, detail="Not implemented")
55-
return await BaseStoreApi.subclasses[0]().delete_order(orderId)
56-
57-
5839
@router.get(
5940
"/store/inventory",
6041
responses={
@@ -75,6 +56,25 @@ async def get_inventory(
7556
return await BaseStoreApi.subclasses[0]().get_inventory()
7657

7758

59+
@router.post(
60+
"/store/order",
61+
responses={
62+
200: {"model": Order, "description": "successful operation"},
63+
400: {"description": "Invalid Order"},
64+
},
65+
tags=["store"],
66+
summary="Place an order for a pet",
67+
response_model_by_alias=True,
68+
)
69+
async def place_order(
70+
order: Annotated[Order, Field(description="order placed for purchasing the pet")] = Body(None, description="order placed for purchasing the pet"),
71+
) -> Order:
72+
""""""
73+
if not BaseStoreApi.subclasses:
74+
raise HTTPException(status_code=500, detail="Not implemented")
75+
return await BaseStoreApi.subclasses[0]().place_order(order)
76+
77+
7878
@router.get(
7979
"/store/order/{orderId}",
8080
responses={
@@ -95,20 +95,20 @@ async def get_order_by_id(
9595
return await BaseStoreApi.subclasses[0]().get_order_by_id(orderId)
9696

9797

98-
@router.post(
99-
"/store/order",
98+
@router.delete(
99+
"/store/order/{orderId}",
100100
responses={
101-
200: {"model": Order, "description": "successful operation"},
102-
400: {"description": "Invalid Order"},
101+
400: {"description": "Invalid ID supplied"},
102+
404: {"description": "Order not found"},
103103
},
104104
tags=["store"],
105-
summary="Place an order for a pet",
105+
summary="Delete purchase order by ID",
106106
response_model_by_alias=True,
107107
)
108-
async def place_order(
109-
order: Annotated[Order, Field(description="order placed for purchasing the pet")] = Body(None, description="order placed for purchasing the pet"),
110-
) -> Order:
111-
""""""
108+
async def delete_order(
109+
orderId: Annotated[StrictStr, Field(description="ID of the order that needs to be deleted")] = Path(..., description="ID of the order that needs to be deleted"),
110+
) -> None:
111+
"""For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors"""
112112
if not BaseStoreApi.subclasses:
113113
raise HTTPException(status_code=500, detail="Not implemented")
114-
return await BaseStoreApi.subclasses[0]().place_order(order)
114+
return await BaseStoreApi.subclasses[0]().delete_order(orderId)

samples/server/petstore/python-fastapi/src/openapi_server/apis/store_api_base.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,18 @@ class BaseStoreApi:
1414
def __init_subclass__(cls, **kwargs):
1515
super().__init_subclass__(**kwargs)
1616
BaseStoreApi.subclasses = BaseStoreApi.subclasses + (cls,)
17-
async def delete_order(
17+
async def get_inventory(
1818
self,
19-
orderId: Annotated[StrictStr, Field(description="ID of the order that needs to be deleted")],
20-
) -> None:
21-
"""For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors"""
19+
) -> Dict[str, int]:
20+
"""Returns a map of status codes to quantities"""
2221
...
2322

2423

25-
async def get_inventory(
24+
async def place_order(
2625
self,
27-
) -> Dict[str, int]:
28-
"""Returns a map of status codes to quantities"""
26+
order: Annotated[Order, Field(description="order placed for purchasing the pet")],
27+
) -> Order:
28+
""""""
2929
...
3030

3131

@@ -37,9 +37,9 @@ async def get_order_by_id(
3737
...
3838

3939

40-
async def place_order(
40+
async def delete_order(
4141
self,
42-
order: Annotated[Order, Field(description="order placed for purchasing the pet")],
43-
) -> Order:
44-
""""""
42+
orderId: Annotated[StrictStr, Field(description="ID of the order that needs to be deleted")],
43+
) -> None:
44+
"""For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors"""
4545
...

0 commit comments

Comments
 (0)