Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[python-fastapi] Added a base class for the actual implementation #14470

Merged
merged 1 commit into from
May 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -415,4 +415,6 @@ public static enum ENUM_PROPERTY_NAMING_TYPE {camelCase, PascalCase, snake_case,
"setting this to true. You can do that by:<ul>" +
"<li>defining the propertyName as an enum with only one value in the schemas that are in your discriminator map</li>" +
"<li>setting additionalProperties: false in your schemas</li></ul>";

public static final String FASTAPI_IMPLEMENTATION_PACKAGE = "fastapiImplementationPackage";
}
Original file line number Diff line number Diff line change
Expand Up @@ -65,13 +65,16 @@ public void serialize(Boolean value, JsonGenerator gen, SerializerProvider seria

protected String sourceFolder;

private static final String BASE_CLASS_SUFFIX = "base";
private static final String SERVER_PORT = "serverPort";
private static final String NAME = "python-fastapi";
private static final int DEFAULT_SERVER_PORT = 8080;
private static final String DEFAULT_PACKAGE_NAME = "openapi_server";
private static final String DEFAULT_SOURCE_FOLDER = "src";
private static final String DEFAULT_PACKAGE_VERSION = "1.0.0";

private String implPackage;

@Override
public CodegenType getTag() {
return CodegenType.SERVER;
Expand Down Expand Up @@ -99,8 +102,10 @@ public PythonFastAPIServerCodegen() {
* are available in models, apis, and supporting files
*/
additionalProperties.put("serverPort", DEFAULT_SERVER_PORT);
additionalProperties.put("baseSuffix", BASE_CLASS_SUFFIX);
additionalProperties.put(CodegenConstants.SOURCE_FOLDER, DEFAULT_SOURCE_FOLDER);
additionalProperties.put(CodegenConstants.PACKAGE_NAME, DEFAULT_PACKAGE_NAME);
additionalProperties.put(CodegenConstants.FASTAPI_IMPLEMENTATION_PACKAGE, DEFAULT_PACKAGE_NAME.concat(".impl"));

languageSpecificPrimitives.add("List");
languageSpecificPrimitives.add("Dict");
Expand All @@ -110,10 +115,12 @@ public PythonFastAPIServerCodegen() {
outputFolder = "generated-code" + File.separator + NAME;
modelTemplateFiles.put("model.mustache", ".py");
apiTemplateFiles.put("api.mustache", ".py");
apiTemplateFiles.put("base_api.mustache", "_".concat(BASE_CLASS_SUFFIX).concat(".py"));
embeddedTemplateDir = templateDir = NAME;
apiPackage = "apis";
modelPackage = "models";
testPackage = "tests";
implPackage = DEFAULT_PACKAGE_NAME.concat(".impl");
apiTestTemplateFiles().put("api_test.mustache", ".py");

cliOptions.add(new CliOption(CodegenConstants.PACKAGE_NAME, "python package name (convention: snake_case).")
Expand All @@ -124,6 +131,8 @@ public PythonFastAPIServerCodegen() {
.defaultValue(String.valueOf(DEFAULT_SERVER_PORT)));
cliOptions.add(new CliOption(CodegenConstants.SOURCE_FOLDER, "directory for generated python source code")
.defaultValue(DEFAULT_SOURCE_FOLDER));
cliOptions.add(new CliOption(CodegenConstants.FASTAPI_IMPLEMENTATION_PACKAGE, "python package name for the implementation code (convention: snake_case).")
.defaultValue(DEFAULT_PACKAGE_NAME.concat(".impl")));

}

Expand All @@ -139,6 +148,10 @@ public void processOpts() {
this.sourceFolder = ((String) additionalProperties.get(CodegenConstants.SOURCE_FOLDER));
}

if (additionalProperties.containsKey(CodegenConstants.FASTAPI_IMPLEMENTATION_PACKAGE)) {
this.implPackage = ((String) additionalProperties.get(CodegenConstants.FASTAPI_IMPLEMENTATION_PACKAGE));
}

modelPackage = packageName + "." + modelPackage;
apiPackage = packageName + "." + apiPackage;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
# coding: utf-8

from typing import Dict, List # noqa: F401
import importlib
import pkgutil

from {{apiPackage}}.{{classFilename}}_{{baseSuffix}} import Base{{classname}}
import {{fastapiImplementationPackage}}

from fastapi import ( # noqa: F401
APIRouter,
Expand All @@ -24,6 +29,10 @@ from {{modelPackage}}.extra_models import TokenModel # noqa: F401

router = APIRouter()

ns_pkg = {{fastapiImplementationPackage}}
for _, name, _ in pkgutil.iter_modules(ns_pkg.__path__, ns_pkg.__name__ + "."):
importlib.import_module(name)


{{#operations}}
{{#operation}}
Expand Down Expand Up @@ -56,7 +65,7 @@ async def {{operationId}}(
{{/hasAuthMethods}}
) -> {{returnType}}{{^returnType}}None{{/returnType}}:
{{#notes}}"""{{.}}"""
...{{/notes}}{{^notes}}...{{/notes}}
return Base{{classname}}.subclasses[0]().{{operationId}}({{#allParams}}{{>impl_argument}}{{^-last}}, {{/-last}}{{/allParams}}){{/notes}}{{^notes}}...{{/notes}}
{{^-last}}


Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# coding: utf-8

from typing import ClassVar, Dict, List, Tuple # noqa: F401

{{#imports}}
{{import}}
{{/imports}}
{{#securityImports.0}}from {{packageName}}.security_api import {{#securityImports}}get_token_{{.}}{{^-last}}, {{/-last}}{{/securityImports}}{{/securityImports.0}}

class Base{{classname}}:
subclasses: ClassVar[Tuple] = ()

def __init_subclass__(cls, **kwargs):
super().__init_subclass__(**kwargs)
Base{{classname}}.subclasses = Base{{classname}}.subclasses + (cls,)
{{#operations}}
{{#operation}}
def {{operationId}}(
self,
{{#allParams}}
{{>impl_argument_definition}},
{{/allParams}}
) -> {{returnType}}{{^returnType}}None{{/returnType}}:
{{#notes}}"""{{.}}"""
...{{/notes}}{{^notes}}...{{/notes}}
{{^-last}}


{{/-last}}
{{/operation}}
{{/operations}}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{{#isPathParam}}{{baseName}}{{/isPathParam}}{{^isPathParam}}{{paramName}}{{/isPathParam}}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{{#isPathParam}}{{baseName}}{{/isPathParam}}{{^isPathParam}}{{paramName}}{{/isPathParam}}: {{>param_type}}
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,11 @@ requirements.txt
setup.cfg
src/openapi_server/apis/__init__.py
src/openapi_server/apis/pet_api.py
src/openapi_server/apis/pet_api_base.py
src/openapi_server/apis/store_api.py
src/openapi_server/apis/store_api_base.py
src/openapi_server/apis/user_api.py
src/openapi_server/apis/user_api_base.py
src/openapi_server/main.py
src/openapi_server/models/__init__.py
src/openapi_server/models/api_response.py
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
# coding: utf-8

from typing import Dict, List # noqa: F401
import importlib
import pkgutil

from openapi_server.apis.pet_api_base import BasePetApi
import openapi_server.impl

from fastapi import ( # noqa: F401
APIRouter,
Expand All @@ -23,6 +28,10 @@

router = APIRouter()

ns_pkg = openapi_server.impl
for _, name, _ in pkgutil.iter_modules(ns_pkg.__path__, ns_pkg.__name__ + "."):
importlib.import_module(name)


@router.post(
"/pet",
Expand All @@ -41,7 +50,7 @@ async def add_pet(
),
) -> Pet:
""""""
...
return BasePetApi.subclasses[0]().add_pet(pet)


@router.delete(
Expand All @@ -61,7 +70,7 @@ async def delete_pet(
),
) -> None:
""""""
...
return BasePetApi.subclasses[0]().delete_pet(petId, api_key)


@router.get(
Expand All @@ -81,7 +90,7 @@ async def find_pets_by_status(
),
) -> List[Pet]:
"""Multiple status values can be provided with comma separated strings"""
...
return BasePetApi.subclasses[0]().find_pets_by_status(status)


@router.get(
Expand All @@ -101,7 +110,7 @@ async def find_pets_by_tags(
),
) -> List[Pet]:
"""Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing."""
...
return BasePetApi.subclasses[0]().find_pets_by_tags(tags)


@router.get(
Expand All @@ -122,7 +131,7 @@ async def get_pet_by_id(
),
) -> Pet:
"""Returns a single pet"""
...
return BasePetApi.subclasses[0]().get_pet_by_id(petId)


@router.put(
Expand All @@ -144,7 +153,7 @@ async def update_pet(
),
) -> Pet:
""""""
...
return BasePetApi.subclasses[0]().update_pet(pet)


@router.post(
Expand All @@ -165,7 +174,7 @@ async def update_pet_with_form(
),
) -> None:
""""""
...
return BasePetApi.subclasses[0]().update_pet_with_form(petId, name, status)


@router.post(
Expand All @@ -186,4 +195,4 @@ async def upload_file(
),
) -> ApiResponse:
""""""
...
return BasePetApi.subclasses[0]().upload_file(petId, additional_metadata, file)
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# coding: utf-8

from typing import ClassVar, Dict, List, Tuple # noqa: F401

from openapi_server.models.api_response import ApiResponse
from openapi_server.models.pet import Pet
from openapi_server.security_api import get_token_petstore_auth, get_token_api_key

class BasePetApi:
subclasses: ClassVar[Tuple] = ()

def __init_subclass__(cls, **kwargs):
super().__init_subclass__(**kwargs)
BasePetApi.subclasses = BasePetApi.subclasses + (cls,)
def add_pet(
self,
pet: Pet,
) -> Pet:
""""""
...


def delete_pet(
self,
petId: int,
api_key: str,
) -> None:
""""""
...


def find_pets_by_status(
self,
status: List[str],
) -> List[Pet]:
"""Multiple status values can be provided with comma separated strings"""
...


def find_pets_by_tags(
self,
tags: List[str],
) -> List[Pet]:
"""Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing."""
...


def get_pet_by_id(
self,
petId: int,
) -> Pet:
"""Returns a single pet"""
...


def update_pet(
self,
pet: Pet,
) -> Pet:
""""""
...


def update_pet_with_form(
self,
petId: int,
name: str,
status: str,
) -> None:
""""""
...


def upload_file(
self,
petId: int,
additional_metadata: str,
file: str,
) -> ApiResponse:
""""""
...
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
# coding: utf-8

from typing import Dict, List # noqa: F401
import importlib
import pkgutil

from openapi_server.apis.store_api_base import BaseStoreApi
import openapi_server.impl

from fastapi import ( # noqa: F401
APIRouter,
Expand All @@ -22,6 +27,10 @@

router = APIRouter()

ns_pkg = openapi_server.impl
for _, name, _ in pkgutil.iter_modules(ns_pkg.__path__, ns_pkg.__name__ + "."):
importlib.import_module(name)


@router.delete(
"/store/order/{orderId}",
Expand All @@ -37,7 +46,7 @@ async def delete_order(
orderId: str = Path(None, 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"""
...
return BaseStoreApi.subclasses[0]().delete_order(orderId)


@router.get(
Expand All @@ -55,7 +64,7 @@ async def get_inventory(
),
) -> Dict[str, int]:
"""Returns a map of status codes to quantities"""
...
return BaseStoreApi.subclasses[0]().get_inventory()


@router.get(
Expand All @@ -73,7 +82,7 @@ async def get_order_by_id(
orderId: int = Path(None, 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"""
...
return BaseStoreApi.subclasses[0]().get_order_by_id(orderId)


@router.post(
Expand All @@ -90,4 +99,4 @@ async def place_order(
order: Order = Body(None, description="order placed for purchasing the pet"),
) -> Order:
""""""
...
return BaseStoreApi.subclasses[0]().place_order(order)
Loading