Skip to content

Commit

Permalink
[python-fastapi] return 500 if not implemented, added some unittests (#…
Browse files Browse the repository at this point in the history
…19196)

* [python-fastapi] Added some tests for FastAPI generator

1. Checks the generation of the implementation package.
2. Checks if the endpoints with and without descriptions generate correct
   output.

Signed-off-by: Nikita Vakula <[email protected]>

* [python-fastapi] Raise 500 if there is no implementation

Signed-off-by: Nikita Vakula <[email protected]>

---------

Signed-off-by: Nikita Vakula <[email protected]>
  • Loading branch information
krjakbrjak authored Jul 19, 2024
1 parent f44bc30 commit e542b06
Show file tree
Hide file tree
Showing 7 changed files with 141 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ from fastapi import ( # noqa: F401
Depends,
Form,
Header,
HTTPException,
Path,
Query,
Response,
Expand Down Expand Up @@ -65,7 +66,9 @@ async def {{operationId}}(
{{/hasAuthMethods}}
) -> {{returnType}}{{^returnType}}None{{/returnType}}:
{{#notes}}"""{{.}}"""
{{/notes}}return await Base{{classname}}.subclasses[0]().{{operationId}}({{#allParams}}{{>impl_argument}}{{^-last}}, {{/-last}}{{/allParams}})
{{/notes}}if not Base{{classname}}.subclasses:
raise HTTPException(status_code=500, detail="Not implemented")
return await Base{{classname}}.subclasses[0]().{{operationId}}({{#allParams}}{{>impl_argument}}{{^-last}}, {{/-last}}{{/allParams}})
{{^-last}}


Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package org.openapitools.codegen.python;

import org.openapitools.codegen.DefaultGenerator;
import org.openapitools.codegen.TestUtils;
import org.openapitools.codegen.config.CodegenConfigurator;
import org.testng.annotations.Test;
import org.openapitools.codegen.CodegenConstants;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;

public class PythonFastapiCodegenTest {
@Test
public void testAdditionalPropertiesPutForConfigValues() throws Exception {
File output = Files.createTempDirectory("test").toFile();
output.deleteOnExit();

final String IMPL_PKG = "impl_package";
final CodegenConfigurator configurator = new CodegenConfigurator()
.setGeneratorName("python-fastapi")
.setPackageName("nodesc")
.setOutputDir(output.getAbsolutePath().replace("\\", "/"))
.setInputSpec("src/test/resources/3_1/nodesc.yaml")
.addAdditionalProperty(CodegenConstants.FASTAPI_IMPLEMENTATION_PACKAGE, IMPL_PKG);

DefaultGenerator generator = new DefaultGenerator();
List<File> files = generator.opts(configurator.toClientOptInput()).generate();
files.forEach(File::deleteOnExit);

TestUtils.assertFileExists(Paths.get(output.getAbsolutePath(), "/src", IMPL_PKG, "__init__.py"));
}

@Test
public void testEndpointSpecsWithoutDescription() throws IOException {
File output = Files.createTempDirectory("test").toFile();
output.deleteOnExit();

final CodegenConfigurator configurator = new CodegenConfigurator()
.setGeneratorName("python-fastapi")
.setPackageName("nodesc")
.setOutputDir(output.getAbsolutePath().replace("\\", "/"))
.setInputSpec("src/test/resources/3_1/nodesc.yaml");

DefaultGenerator generator = new DefaultGenerator();
List<File> files = generator.opts(configurator.toClientOptInput()).generate();
files.forEach(File::deleteOnExit);

TestUtils.assertFileContains(Paths.get(output + "/src/nodesc/apis/nodesc_api.py"),
"return await BaseNodescApi.subclasses[0]().nodesc()\n");
TestUtils.assertFileContains(Paths.get(output + "/src/nodesc/apis/desc_api.py"),
"return await BaseDescApi.subclasses[0]().desc()\n");
}
}
35 changes: 35 additions & 0 deletions modules/openapi-generator/src/test/resources/3_1/nodesc.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
openapi: 3.1.0
info:
description: >-
Yet another example.
version: 1.0.0
title: OpenAPI example
tags:
- name: nodesc
description: Endpoints have no description
- name: desc
description: Endpoints have description
paths:
/nodesc:
post:
tags:
- nodesc
summary: dummy operation
operationId: nodesc
responses:
'200':
description: successful operation
'405':
description: Invalid input
/desc:
post:
tags:
- desc
summary: dummy operation
description: 'Description'
operationId: desc
responses:
'200':
description: successful operation
'405':
description: Invalid input
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
Depends,
Form,
Header,
HTTPException,
Path,
Query,
Response,
Expand Down Expand Up @@ -46,4 +47,6 @@ async def fake_query_param_default(
no_default: str = Query(None, description="no default value", alias="noDefault"),
) -> None:
""""""
if not BaseFakeApi.subclasses:
raise HTTPException(status_code=500, detail="Not implemented")
return await BaseFakeApi.subclasses[0]().fake_query_param_default(has_default, no_default)
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
Depends,
Form,
Header,
HTTPException,
Path,
Query,
Response,
Expand Down Expand Up @@ -50,6 +51,8 @@ async def add_pet(
),
) -> Pet:
""""""
if not BasePetApi.subclasses:
raise HTTPException(status_code=500, detail="Not implemented")
return await BasePetApi.subclasses[0]().add_pet(pet)


Expand All @@ -70,6 +73,8 @@ async def delete_pet(
),
) -> None:
""""""
if not BasePetApi.subclasses:
raise HTTPException(status_code=500, detail="Not implemented")
return await BasePetApi.subclasses[0]().delete_pet(petId, api_key)


Expand All @@ -90,6 +95,8 @@ async def find_pets_by_status(
),
) -> List[Pet]:
"""Multiple status values can be provided with comma separated strings"""
if not BasePetApi.subclasses:
raise HTTPException(status_code=500, detail="Not implemented")
return await BasePetApi.subclasses[0]().find_pets_by_status(status)


Expand All @@ -110,6 +117,8 @@ async def find_pets_by_tags(
),
) -> List[Pet]:
"""Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing."""
if not BasePetApi.subclasses:
raise HTTPException(status_code=500, detail="Not implemented")
return await BasePetApi.subclasses[0]().find_pets_by_tags(tags)


Expand All @@ -131,6 +140,8 @@ async def get_pet_by_id(
),
) -> Pet:
"""Returns a single pet"""
if not BasePetApi.subclasses:
raise HTTPException(status_code=500, detail="Not implemented")
return await BasePetApi.subclasses[0]().get_pet_by_id(petId)


Expand All @@ -153,6 +164,8 @@ async def update_pet(
),
) -> Pet:
""""""
if not BasePetApi.subclasses:
raise HTTPException(status_code=500, detail="Not implemented")
return await BasePetApi.subclasses[0]().update_pet(pet)


Expand All @@ -174,6 +187,8 @@ async def update_pet_with_form(
),
) -> None:
""""""
if not BasePetApi.subclasses:
raise HTTPException(status_code=500, detail="Not implemented")
return await BasePetApi.subclasses[0]().update_pet_with_form(petId, name, status)


Expand All @@ -195,4 +210,6 @@ async def upload_file(
),
) -> ApiResponse:
""""""
if not BasePetApi.subclasses:
raise HTTPException(status_code=500, detail="Not implemented")
return await BasePetApi.subclasses[0]().upload_file(petId, additional_metadata, file)
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
Depends,
Form,
Header,
HTTPException,
Path,
Query,
Response,
Expand Down Expand Up @@ -46,6 +47,8 @@ async def delete_order(
orderId: str = Path(..., description="ID of the order that needs to be deleted"),
) -> None:
"""For valid response try integer IDs with value &lt; 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)


Expand All @@ -64,6 +67,8 @@ async def get_inventory(
),
) -> Dict[str, int]:
"""Returns a map of status codes to quantities"""
if not BaseStoreApi.subclasses:
raise HTTPException(status_code=500, detail="Not implemented")
return await BaseStoreApi.subclasses[0]().get_inventory()


Expand All @@ -82,6 +87,8 @@ async def get_order_by_id(
orderId: int = Path(..., description="ID of pet that needs to be fetched", ge=1, le=5),
) -> Order:
"""For valid response try integer IDs with value &lt;&#x3D; 5 or &gt; 10. Other values will generate exceptions"""
if not BaseStoreApi.subclasses:
raise HTTPException(status_code=500, detail="Not implemented")
return await BaseStoreApi.subclasses[0]().get_order_by_id(orderId)


Expand All @@ -99,4 +106,6 @@ async def place_order(
order: Order = 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)
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
Depends,
Form,
Header,
HTTPException,
Path,
Query,
Response,
Expand Down Expand Up @@ -48,6 +49,8 @@ async def create_user(
),
) -> 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]().create_user(user)


Expand All @@ -67,6 +70,8 @@ async def create_users_with_array_input(
),
) -> None:
""""""
if not BaseUserApi.subclasses:
raise HTTPException(status_code=500, detail="Not implemented")
return await BaseUserApi.subclasses[0]().create_users_with_array_input(user)


Expand All @@ -86,6 +91,8 @@ async def create_users_with_list_input(
),
) -> None:
""""""
if not BaseUserApi.subclasses:
raise HTTPException(status_code=500, detail="Not implemented")
return await BaseUserApi.subclasses[0]().create_users_with_list_input(user)


Expand All @@ -106,6 +113,8 @@ async def delete_user(
),
) -> 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)


Expand All @@ -124,6 +133,8 @@ async def get_user_by_name(
username: str = 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)


Expand All @@ -142,6 +153,8 @@ async def login_user(
password: str = Query(None, description="The password for login in clear text", alias="password"),
) -> str:
""""""
if not BaseUserApi.subclasses:
raise HTTPException(status_code=500, detail="Not implemented")
return await BaseUserApi.subclasses[0]().login_user(username, password)


Expand All @@ -160,6 +173,8 @@ async def logout_user(
),
) -> None:
""""""
if not BaseUserApi.subclasses:
raise HTTPException(status_code=500, detail="Not implemented")
return await BaseUserApi.subclasses[0]().logout_user()


Expand All @@ -181,4 +196,6 @@ async def update_user(
),
) -> 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]().update_user(username, user)

0 comments on commit e542b06

Please sign in to comment.