Skip to content

Commit e542b06

Browse files
authored
[python-fastapi] return 500 if not implemented, added some unittests (#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]>
1 parent f44bc30 commit e542b06

File tree

7 files changed

+141
-1
lines changed

7 files changed

+141
-1
lines changed

Diff for: modules/openapi-generator/src/main/resources/python-fastapi/api.mustache

+4-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ from fastapi import ( # noqa: F401
1414
Depends,
1515
Form,
1616
Header,
17+
HTTPException,
1718
Path,
1819
Query,
1920
Response,
@@ -65,7 +66,9 @@ async def {{operationId}}(
6566
{{/hasAuthMethods}}
6667
) -> {{returnType}}{{^returnType}}None{{/returnType}}:
6768
{{#notes}}"""{{.}}"""
68-
{{/notes}}return await Base{{classname}}.subclasses[0]().{{operationId}}({{#allParams}}{{>impl_argument}}{{^-last}}, {{/-last}}{{/allParams}})
69+
{{/notes}}if not Base{{classname}}.subclasses:
70+
raise HTTPException(status_code=500, detail="Not implemented")
71+
return await Base{{classname}}.subclasses[0]().{{operationId}}({{#allParams}}{{>impl_argument}}{{^-last}}, {{/-last}}{{/allParams}})
6972
{{^-last}}
7073

7174

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package org.openapitools.codegen.python;
2+
3+
import org.openapitools.codegen.DefaultGenerator;
4+
import org.openapitools.codegen.TestUtils;
5+
import org.openapitools.codegen.config.CodegenConfigurator;
6+
import org.testng.annotations.Test;
7+
import org.openapitools.codegen.CodegenConstants;
8+
9+
import java.io.File;
10+
import java.io.IOException;
11+
import java.nio.file.Files;
12+
import java.nio.file.Paths;
13+
import java.util.List;
14+
15+
public class PythonFastapiCodegenTest {
16+
@Test
17+
public void testAdditionalPropertiesPutForConfigValues() throws Exception {
18+
File output = Files.createTempDirectory("test").toFile();
19+
output.deleteOnExit();
20+
21+
final String IMPL_PKG = "impl_package";
22+
final CodegenConfigurator configurator = new CodegenConfigurator()
23+
.setGeneratorName("python-fastapi")
24+
.setPackageName("nodesc")
25+
.setOutputDir(output.getAbsolutePath().replace("\\", "/"))
26+
.setInputSpec("src/test/resources/3_1/nodesc.yaml")
27+
.addAdditionalProperty(CodegenConstants.FASTAPI_IMPLEMENTATION_PACKAGE, IMPL_PKG);
28+
29+
DefaultGenerator generator = new DefaultGenerator();
30+
List<File> files = generator.opts(configurator.toClientOptInput()).generate();
31+
files.forEach(File::deleteOnExit);
32+
33+
TestUtils.assertFileExists(Paths.get(output.getAbsolutePath(), "/src", IMPL_PKG, "__init__.py"));
34+
}
35+
36+
@Test
37+
public void testEndpointSpecsWithoutDescription() throws IOException {
38+
File output = Files.createTempDirectory("test").toFile();
39+
output.deleteOnExit();
40+
41+
final CodegenConfigurator configurator = new CodegenConfigurator()
42+
.setGeneratorName("python-fastapi")
43+
.setPackageName("nodesc")
44+
.setOutputDir(output.getAbsolutePath().replace("\\", "/"))
45+
.setInputSpec("src/test/resources/3_1/nodesc.yaml");
46+
47+
DefaultGenerator generator = new DefaultGenerator();
48+
List<File> files = generator.opts(configurator.toClientOptInput()).generate();
49+
files.forEach(File::deleteOnExit);
50+
51+
TestUtils.assertFileContains(Paths.get(output + "/src/nodesc/apis/nodesc_api.py"),
52+
"return await BaseNodescApi.subclasses[0]().nodesc()\n");
53+
TestUtils.assertFileContains(Paths.get(output + "/src/nodesc/apis/desc_api.py"),
54+
"return await BaseDescApi.subclasses[0]().desc()\n");
55+
}
56+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
openapi: 3.1.0
2+
info:
3+
description: >-
4+
Yet another example.
5+
version: 1.0.0
6+
title: OpenAPI example
7+
tags:
8+
- name: nodesc
9+
description: Endpoints have no description
10+
- name: desc
11+
description: Endpoints have description
12+
paths:
13+
/nodesc:
14+
post:
15+
tags:
16+
- nodesc
17+
summary: dummy operation
18+
operationId: nodesc
19+
responses:
20+
'200':
21+
description: successful operation
22+
'405':
23+
description: Invalid input
24+
/desc:
25+
post:
26+
tags:
27+
- desc
28+
summary: dummy operation
29+
description: 'Description'
30+
operationId: desc
31+
responses:
32+
'200':
33+
description: successful operation
34+
'405':
35+
description: Invalid input

Diff for: samples/server/petstore/python-fastapi/src/openapi_server/apis/fake_api.py

+3
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
Depends,
1515
Form,
1616
Header,
17+
HTTPException,
1718
Path,
1819
Query,
1920
Response,
@@ -46,4 +47,6 @@ async def fake_query_param_default(
4647
no_default: str = Query(None, description="no default value", alias="noDefault"),
4748
) -> None:
4849
""""""
50+
if not BaseFakeApi.subclasses:
51+
raise HTTPException(status_code=500, detail="Not implemented")
4952
return await BaseFakeApi.subclasses[0]().fake_query_param_default(has_default, no_default)

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

+17
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
Depends,
1515
Form,
1616
Header,
17+
HTTPException,
1718
Path,
1819
Query,
1920
Response,
@@ -50,6 +51,8 @@ async def add_pet(
5051
),
5152
) -> Pet:
5253
""""""
54+
if not BasePetApi.subclasses:
55+
raise HTTPException(status_code=500, detail="Not implemented")
5356
return await BasePetApi.subclasses[0]().add_pet(pet)
5457

5558

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

7580

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

95102

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

115124

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

136147

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

158171

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

179194

@@ -195,4 +210,6 @@ async def upload_file(
195210
),
196211
) -> ApiResponse:
197212
""""""
213+
if not BasePetApi.subclasses:
214+
raise HTTPException(status_code=500, detail="Not implemented")
198215
return await BasePetApi.subclasses[0]().upload_file(petId, additional_metadata, file)

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

+9
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
Depends,
1515
Form,
1616
Header,
17+
HTTPException,
1718
Path,
1819
Query,
1920
Response,
@@ -46,6 +47,8 @@ async def delete_order(
4647
orderId: str = Path(..., description="ID of the order that needs to be deleted"),
4748
) -> None:
4849
"""For valid response try integer IDs with value &lt; 1000. Anything above 1000 or nonintegers will generate API errors"""
50+
if not BaseStoreApi.subclasses:
51+
raise HTTPException(status_code=500, detail="Not implemented")
4952
return await BaseStoreApi.subclasses[0]().delete_order(orderId)
5053

5154

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

6974

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

8794

@@ -99,4 +106,6 @@ async def place_order(
99106
order: Order = Body(None, description="order placed for purchasing the pet"),
100107
) -> Order:
101108
""""""
109+
if not BaseStoreApi.subclasses:
110+
raise HTTPException(status_code=500, detail="Not implemented")
102111
return await BaseStoreApi.subclasses[0]().place_order(order)

Diff for: samples/server/petstore/python-fastapi/src/openapi_server/apis/user_api.py

+17
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
Depends,
1515
Form,
1616
Header,
17+
HTTPException,
1718
Path,
1819
Query,
1920
Response,
@@ -48,6 +49,8 @@ async def create_user(
4849
),
4950
) -> None:
5051
"""This can only be done by the logged in user."""
52+
if not BaseUserApi.subclasses:
53+
raise HTTPException(status_code=500, detail="Not implemented")
5154
return await BaseUserApi.subclasses[0]().create_user(user)
5255

5356

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

7277

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

9198

@@ -106,6 +113,8 @@ async def delete_user(
106113
),
107114
) -> None:
108115
"""This can only be done by the logged in user."""
116+
if not BaseUserApi.subclasses:
117+
raise HTTPException(status_code=500, detail="Not implemented")
109118
return await BaseUserApi.subclasses[0]().delete_user(username)
110119

111120

@@ -124,6 +133,8 @@ async def get_user_by_name(
124133
username: str = Path(..., description="The name that needs to be fetched. Use user1 for testing."),
125134
) -> User:
126135
""""""
136+
if not BaseUserApi.subclasses:
137+
raise HTTPException(status_code=500, detail="Not implemented")
127138
return await BaseUserApi.subclasses[0]().get_user_by_name(username)
128139

129140

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

147160

@@ -160,6 +173,8 @@ async def logout_user(
160173
),
161174
) -> None:
162175
""""""
176+
if not BaseUserApi.subclasses:
177+
raise HTTPException(status_code=500, detail="Not implemented")
163178
return await BaseUserApi.subclasses[0]().logout_user()
164179

165180

@@ -181,4 +196,6 @@ async def update_user(
181196
),
182197
) -> None:
183198
"""This can only be done by the logged in user."""
199+
if not BaseUserApi.subclasses:
200+
raise HTTPException(status_code=500, detail="Not implemented")
184201
return await BaseUserApi.subclasses[0]().update_user(username, user)

0 commit comments

Comments
 (0)