diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PythonFastAPIServerCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PythonFastAPIServerCodegen.java index 802cd4353485..d1e8b2e881be 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PythonFastAPIServerCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PythonFastAPIServerCodegen.java @@ -89,6 +89,11 @@ public String getHelp() { public PythonFastAPIServerCodegen() { super(); + // Skip sorting of operations to preserve the order found in the OpenAPI spec file. See + // https://fastapi.tiangolo.com/tutorial/path-params/?h=path#order-matters for details on why order matters. + LOGGER.info("Skipping sorting of path operations, order matters, let the developer decide via their specification file."); + setSkipSortingOperations(true); + modifyFeatureSet(features -> features.includeSecurityFeatures( SecurityFeature.OAuth2_AuthorizationCode, SecurityFeature.OAuth2_Password diff --git a/samples/server/petstore/python-fastapi/src/openapi_server/apis/pet_api.py b/samples/server/petstore/python-fastapi/src/openapi_server/apis/pet_api.py index a4aa3a6e71ec..416272bcd61d 100644 --- a/samples/server/petstore/python-fastapi/src/openapi_server/apis/pet_api.py +++ b/samples/server/petstore/python-fastapi/src/openapi_server/apis/pet_api.py @@ -37,17 +37,19 @@ importlib.import_module(name) -@router.post( +@router.put( "/pet", responses={ 200: {"model": Pet, "description": "successful operation"}, - 405: {"description": "Invalid input"}, + 400: {"description": "Invalid ID supplied"}, + 404: {"description": "Pet not found"}, + 405: {"description": "Validation exception"}, }, tags=["pet"], - summary="Add a new pet to the store", + summary="Update an existing pet", response_model_by_alias=True, ) -async def add_pet( +async def update_pet( 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"), token_petstore_auth: TokenModel = Security( get_token_petstore_auth, scopes=["write:pets", "read:pets"] @@ -56,29 +58,29 @@ async def add_pet( """""" if not BasePetApi.subclasses: raise HTTPException(status_code=500, detail="Not implemented") - return await BasePetApi.subclasses[0]().add_pet(pet) + return await BasePetApi.subclasses[0]().update_pet(pet) -@router.delete( - "/pet/{petId}", +@router.post( + "/pet", responses={ - 400: {"description": "Invalid pet value"}, + 200: {"model": Pet, "description": "successful operation"}, + 405: {"description": "Invalid input"}, }, tags=["pet"], - summary="Deletes a pet", + summary="Add a new pet to the store", response_model_by_alias=True, ) -async def delete_pet( - petId: Annotated[StrictInt, Field(description="Pet id to delete")] = Path(..., description="Pet id to delete"), - api_key: Optional[StrictStr] = Header(None, description=""), +async def add_pet( + 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"), token_petstore_auth: TokenModel = Security( get_token_petstore_auth, scopes=["write:pets", "read:pets"] ), -) -> None: +) -> Pet: """""" if not BasePetApi.subclasses: raise HTTPException(status_code=500, detail="Not implemented") - return await BasePetApi.subclasses[0]().delete_pet(petId, api_key) + return await BasePetApi.subclasses[0]().add_pet(pet) @router.get( @@ -148,43 +150,41 @@ async def get_pet_by_id( return await BasePetApi.subclasses[0]().get_pet_by_id(petId) -@router.put( - "/pet", +@router.post( + "/pet/{petId}", responses={ - 200: {"model": Pet, "description": "successful operation"}, - 400: {"description": "Invalid ID supplied"}, - 404: {"description": "Pet not found"}, - 405: {"description": "Validation exception"}, + 405: {"description": "Invalid input"}, }, tags=["pet"], - summary="Update an existing pet", + summary="Updates a pet in the store with form data", response_model_by_alias=True, ) -async def update_pet( - 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"), +async def update_pet_with_form( + petId: Annotated[StrictInt, Field(description="ID of pet that needs to be updated")] = Path(..., description="ID of pet that needs to be updated"), + name: Annotated[Optional[StrictStr], Field(description="Updated name of the pet")] = Form(None, description="Updated name of the pet"), + status: Annotated[Optional[StrictStr], Field(description="Updated status of the pet")] = Form(None, description="Updated status of the pet"), token_petstore_auth: TokenModel = Security( get_token_petstore_auth, scopes=["write:pets", "read:pets"] ), -) -> Pet: +) -> None: """""" if not BasePetApi.subclasses: raise HTTPException(status_code=500, detail="Not implemented") - return await BasePetApi.subclasses[0]().update_pet(pet) + return await BasePetApi.subclasses[0]().update_pet_with_form(petId, name, status) -@router.post( +@router.delete( "/pet/{petId}", responses={ - 405: {"description": "Invalid input"}, + 400: {"description": "Invalid pet value"}, }, tags=["pet"], - summary="Updates a pet in the store with form data", + summary="Deletes a pet", response_model_by_alias=True, ) -async def update_pet_with_form( - petId: Annotated[StrictInt, Field(description="ID of pet that needs to be updated")] = Path(..., description="ID of pet that needs to be updated"), - name: Annotated[Optional[StrictStr], Field(description="Updated name of the pet")] = Form(None, description="Updated name of the pet"), - status: Annotated[Optional[StrictStr], Field(description="Updated status of the pet")] = Form(None, description="Updated status of the pet"), +async def delete_pet( + petId: Annotated[StrictInt, Field(description="Pet id to delete")] = Path(..., description="Pet id to delete"), + api_key: Optional[StrictStr] = Header(None, description=""), token_petstore_auth: TokenModel = Security( get_token_petstore_auth, scopes=["write:pets", "read:pets"] ), @@ -192,7 +192,7 @@ async def update_pet_with_form( """""" if not BasePetApi.subclasses: raise HTTPException(status_code=500, detail="Not implemented") - return await BasePetApi.subclasses[0]().update_pet_with_form(petId, name, status) + return await BasePetApi.subclasses[0]().delete_pet(petId, api_key) @router.post( diff --git a/samples/server/petstore/python-fastapi/src/openapi_server/apis/pet_api_base.py b/samples/server/petstore/python-fastapi/src/openapi_server/apis/pet_api_base.py index 608762c7a8db..0d1fb77e442d 100644 --- a/samples/server/petstore/python-fastapi/src/openapi_server/apis/pet_api_base.py +++ b/samples/server/petstore/python-fastapi/src/openapi_server/apis/pet_api_base.py @@ -15,7 +15,7 @@ class BasePetApi: def __init_subclass__(cls, **kwargs): super().__init_subclass__(**kwargs) BasePetApi.subclasses = BasePetApi.subclasses + (cls,) - async def add_pet( + async def update_pet( self, pet: Annotated[Pet, Field(description="Pet object that needs to be added to the store")], ) -> Pet: @@ -23,11 +23,10 @@ async def add_pet( ... - async def delete_pet( + async def add_pet( self, - petId: Annotated[StrictInt, Field(description="Pet id to delete")], - api_key: Optional[StrictStr], - ) -> None: + pet: Annotated[Pet, Field(description="Pet object that needs to be added to the store")], + ) -> Pet: """""" ... @@ -56,19 +55,20 @@ async def get_pet_by_id( ... - async def update_pet( + async def update_pet_with_form( self, - pet: Annotated[Pet, Field(description="Pet object that needs to be added to the store")], - ) -> Pet: + petId: Annotated[StrictInt, Field(description="ID of pet that needs to be updated")], + name: Annotated[Optional[StrictStr], Field(description="Updated name of the pet")], + status: Annotated[Optional[StrictStr], Field(description="Updated status of the pet")], + ) -> None: """""" ... - async def update_pet_with_form( + async def delete_pet( self, - petId: Annotated[StrictInt, Field(description="ID of pet that needs to be updated")], - name: Annotated[Optional[StrictStr], Field(description="Updated name of the pet")], - status: Annotated[Optional[StrictStr], Field(description="Updated status of the pet")], + petId: Annotated[StrictInt, Field(description="Pet id to delete")], + api_key: Optional[StrictStr], ) -> None: """""" ... diff --git a/samples/server/petstore/python-fastapi/src/openapi_server/apis/store_api.py b/samples/server/petstore/python-fastapi/src/openapi_server/apis/store_api.py index 21d2aceb380d..3d2744c2e028 100644 --- a/samples/server/petstore/python-fastapi/src/openapi_server/apis/store_api.py +++ b/samples/server/petstore/python-fastapi/src/openapi_server/apis/store_api.py @@ -36,25 +36,6 @@ importlib.import_module(name) -@router.delete( - "/store/order/{orderId}", - responses={ - 400: {"description": "Invalid ID supplied"}, - 404: {"description": "Order not found"}, - }, - tags=["store"], - summary="Delete purchase order by ID", - response_model_by_alias=True, -) -async def delete_order( - 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"), -) -> None: - """For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors""" - if not BaseStoreApi.subclasses: - raise HTTPException(status_code=500, detail="Not implemented") - return await BaseStoreApi.subclasses[0]().delete_order(orderId) - - @router.get( "/store/inventory", responses={ @@ -75,6 +56,25 @@ async def get_inventory( return await BaseStoreApi.subclasses[0]().get_inventory() +@router.post( + "/store/order", + responses={ + 200: {"model": Order, "description": "successful operation"}, + 400: {"description": "Invalid Order"}, + }, + tags=["store"], + summary="Place an order for a pet", + response_model_by_alias=True, +) +async def place_order( + order: Annotated[Order, Field(description="order placed for purchasing the pet")] = Body(None, description="order placed for purchasing the pet"), +) -> Order: + """""" + if not BaseStoreApi.subclasses: + raise HTTPException(status_code=500, detail="Not implemented") + return await BaseStoreApi.subclasses[0]().place_order(order) + + @router.get( "/store/order/{orderId}", responses={ @@ -95,20 +95,20 @@ async def get_order_by_id( return await BaseStoreApi.subclasses[0]().get_order_by_id(orderId) -@router.post( - "/store/order", +@router.delete( + "/store/order/{orderId}", responses={ - 200: {"model": Order, "description": "successful operation"}, - 400: {"description": "Invalid Order"}, + 400: {"description": "Invalid ID supplied"}, + 404: {"description": "Order not found"}, }, tags=["store"], - summary="Place an order for a pet", + summary="Delete purchase order by ID", response_model_by_alias=True, ) -async def place_order( - order: Annotated[Order, Field(description="order placed for purchasing the pet")] = Body(None, description="order placed for purchasing the pet"), -) -> Order: - """""" +async def delete_order( + 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"), +) -> None: + """For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors""" if not BaseStoreApi.subclasses: raise HTTPException(status_code=500, detail="Not implemented") - return await BaseStoreApi.subclasses[0]().place_order(order) + return await BaseStoreApi.subclasses[0]().delete_order(orderId) diff --git a/samples/server/petstore/python-fastapi/src/openapi_server/apis/store_api_base.py b/samples/server/petstore/python-fastapi/src/openapi_server/apis/store_api_base.py index 20629628e50e..84d9b639c1d3 100644 --- a/samples/server/petstore/python-fastapi/src/openapi_server/apis/store_api_base.py +++ b/samples/server/petstore/python-fastapi/src/openapi_server/apis/store_api_base.py @@ -14,18 +14,18 @@ class BaseStoreApi: def __init_subclass__(cls, **kwargs): super().__init_subclass__(**kwargs) BaseStoreApi.subclasses = BaseStoreApi.subclasses + (cls,) - async def delete_order( + async def get_inventory( self, - orderId: Annotated[StrictStr, Field(description="ID of the order that needs to be deleted")], - ) -> None: - """For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors""" + ) -> Dict[str, int]: + """Returns a map of status codes to quantities""" ... - async def get_inventory( + async def place_order( self, - ) -> Dict[str, int]: - """Returns a map of status codes to quantities""" + order: Annotated[Order, Field(description="order placed for purchasing the pet")], + ) -> Order: + """""" ... @@ -37,9 +37,9 @@ async def get_order_by_id( ... - async def place_order( + async def delete_order( self, - order: Annotated[Order, Field(description="order placed for purchasing the pet")], - ) -> Order: - """""" + orderId: Annotated[StrictStr, Field(description="ID of the order that needs to be deleted")], + ) -> None: + """For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors""" ... diff --git a/samples/server/petstore/python-fastapi/src/openapi_server/apis/user_api.py b/samples/server/petstore/python-fastapi/src/openapi_server/apis/user_api.py index efad9b7d18f3..47fd026bbc6e 100644 --- a/samples/server/petstore/python-fastapi/src/openapi_server/apis/user_api.py +++ b/samples/server/petstore/python-fastapi/src/openapi_server/apis/user_api.py @@ -99,48 +99,6 @@ async def create_users_with_list_input( return await BaseUserApi.subclasses[0]().create_users_with_list_input(user) -@router.delete( - "/user/{username}", - responses={ - 400: {"description": "Invalid username supplied"}, - 404: {"description": "User not found"}, - }, - tags=["user"], - summary="Delete user", - response_model_by_alias=True, -) -async def delete_user( - username: Annotated[StrictStr, Field(description="The name that needs to be deleted")] = Path(..., description="The name that needs to be deleted"), - token_api_key: TokenModel = Security( - get_token_api_key - ), -) -> None: - """This can only be done by the logged in user.""" - if not BaseUserApi.subclasses: - raise HTTPException(status_code=500, detail="Not implemented") - return await BaseUserApi.subclasses[0]().delete_user(username) - - -@router.get( - "/user/{username}", - responses={ - 200: {"model": User, "description": "successful operation"}, - 400: {"description": "Invalid username supplied"}, - 404: {"description": "User not found"}, - }, - tags=["user"], - summary="Get user by user name", - response_model_by_alias=True, -) -async def get_user_by_name( - username: Annotated[StrictStr, Field(description="The name that needs to be fetched. Use user1 for testing.")] = Path(..., description="The name that needs to be fetched. Use user1 for testing."), -) -> User: - """""" - if not BaseUserApi.subclasses: - raise HTTPException(status_code=500, detail="Not implemented") - return await BaseUserApi.subclasses[0]().get_user_by_name(username) - - @router.get( "/user/login", responses={ @@ -181,6 +139,26 @@ async def logout_user( return await BaseUserApi.subclasses[0]().logout_user() +@router.get( + "/user/{username}", + responses={ + 200: {"model": User, "description": "successful operation"}, + 400: {"description": "Invalid username supplied"}, + 404: {"description": "User not found"}, + }, + tags=["user"], + summary="Get user by user name", + response_model_by_alias=True, +) +async def get_user_by_name( + username: Annotated[StrictStr, Field(description="The name that needs to be fetched. Use user1 for testing.")] = Path(..., description="The name that needs to be fetched. Use user1 for testing."), +) -> User: + """""" + if not BaseUserApi.subclasses: + raise HTTPException(status_code=500, detail="Not implemented") + return await BaseUserApi.subclasses[0]().get_user_by_name(username) + + @router.put( "/user/{username}", responses={ @@ -202,3 +180,25 @@ async def update_user( if not BaseUserApi.subclasses: raise HTTPException(status_code=500, detail="Not implemented") return await BaseUserApi.subclasses[0]().update_user(username, user) + + +@router.delete( + "/user/{username}", + responses={ + 400: {"description": "Invalid username supplied"}, + 404: {"description": "User not found"}, + }, + tags=["user"], + summary="Delete user", + response_model_by_alias=True, +) +async def delete_user( + username: Annotated[StrictStr, Field(description="The name that needs to be deleted")] = Path(..., description="The name that needs to be deleted"), + token_api_key: TokenModel = Security( + get_token_api_key + ), +) -> None: + """This can only be done by the logged in user.""" + if not BaseUserApi.subclasses: + raise HTTPException(status_code=500, detail="Not implemented") + return await BaseUserApi.subclasses[0]().delete_user(username) diff --git a/samples/server/petstore/python-fastapi/src/openapi_server/apis/user_api_base.py b/samples/server/petstore/python-fastapi/src/openapi_server/apis/user_api_base.py index fb86c924a58f..752960411104 100644 --- a/samples/server/petstore/python-fastapi/src/openapi_server/apis/user_api_base.py +++ b/samples/server/petstore/python-fastapi/src/openapi_server/apis/user_api_base.py @@ -38,22 +38,6 @@ async def create_users_with_list_input( ... - async def delete_user( - self, - username: Annotated[StrictStr, Field(description="The name that needs to be deleted")], - ) -> None: - """This can only be done by the logged in user.""" - ... - - - async def get_user_by_name( - self, - username: Annotated[StrictStr, Field(description="The name that needs to be fetched. Use user1 for testing.")], - ) -> User: - """""" - ... - - async def login_user( self, username: Annotated[str, Field(strict=True, description="The user name for login")], @@ -70,6 +54,14 @@ async def logout_user( ... + async def get_user_by_name( + self, + username: Annotated[StrictStr, Field(description="The name that needs to be fetched. Use user1 for testing.")], + ) -> User: + """""" + ... + + async def update_user( self, username: Annotated[StrictStr, Field(description="name that need to be deleted")], @@ -77,3 +69,11 @@ async def update_user( ) -> None: """This can only be done by the logged in user.""" ... + + + async def delete_user( + self, + username: Annotated[StrictStr, Field(description="The name that needs to be deleted")], + ) -> None: + """This can only be done by the logged in user.""" + ...