diff --git a/docs/generators/python-fastapi.md b/docs/generators/python-fastapi.md index 0beee68d757d..2ca99719228d 100644 --- a/docs/generators/python-fastapi.md +++ b/docs/generators/python-fastapi.md @@ -24,6 +24,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl |ensureUniqueParams|Whether to ensure parameter names are unique in an operation (rename parameters that are not).| |true| |enumUnknownDefaultCase|If the server adds new enum cases, that are unknown by an old spec/client, the client will fail to parse the network response.With this option enabled, each enum will have a new case, 'unknown_default_open_api', so that when the server sends an enum case that is not known by the client/spec, they can safely fallback to this case.|
**false**
No changes to the enum's are made, this is the default option.
**true**
With this option enabled, each enum will have a new case, 'unknown_default_open_api', so that when the enum case sent by the server is not known by the client/spec, can safely be decoded to this case.
|false| |fastapiImplementationPackage|python package name for the implementation code (convention: snake_case).| |impl| +|isLibrary|whether to generate minimal python code to be published as a separate library or not| |false| |legacyDiscriminatorBehavior|Set to false for generators with better support for discriminators. (Python, Java, Go, PowerShell, C# have this enabled by default).|
**true**
The mapping in the discriminator includes descendent schemas that allOf inherit from self and the discriminator mapping schemas in the OAS document.
**false**
The mapping in the discriminator includes any descendent schemas that allOf inherit from self, any oneOf schemas, any anyOf schemas, any x-discriminator-values, and the discriminator mapping schemas in the OAS document AND Codegen validates that oneOf and anyOf schemas contain the required discriminator and throws an error if the discriminator is missing.
|true| |packageName|python package name (convention: snake_case).| |openapi_server| |packageVersion|python package version.| |1.0.0| 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 5b14b738f077..56a47b9906a6 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 @@ -63,7 +63,9 @@ public void serialize(Boolean value, JsonGenerator gen, SerializerProvider seria final Logger LOGGER = LoggerFactory.getLogger(PythonFastAPIServerCodegen.class); + private String implPackage; protected String sourceFolder; + protected boolean isLibrary = false; private static final String BASE_CLASS_SUFFIX = "base"; private static final String SERVER_PORT = "serverPort"; @@ -73,8 +75,7 @@ public void serialize(Boolean value, JsonGenerator gen, SerializerProvider seria private static final String DEFAULT_SOURCE_FOLDER = "src"; private static final String DEFAULT_IMPL_FOLDER = "impl"; private static final String DEFAULT_PACKAGE_VERSION = "1.0.0"; - - private String implPackage; + private static final String IS_LIBRARY = "isLibrary"; @Override public CodegenType getTag() { @@ -86,6 +87,18 @@ public String getHelp() { return "Generates a Python FastAPI server (beta). Models are defined with the pydantic library"; } + public void setIsLibrary(final boolean isLibrary) { + this.isLibrary = isLibrary; + } + + public void setImplPackage(final String implPackage) { + this.implPackage = implPackage; + } + + public void setSourceFolder(final String sourceFolder) { + this.sourceFolder = sourceFolder; + } + public PythonFastAPIServerCodegen() { super(); @@ -128,36 +141,41 @@ public PythonFastAPIServerCodegen() { implPackage = DEFAULT_IMPL_FOLDER; apiTestTemplateFiles().put("api_test.mustache", ".py"); - cliOptions.add(new CliOption(CodegenConstants.PACKAGE_NAME, "python package name (convention: snake_case).") - .defaultValue(DEFAULT_PACKAGE_NAME)); - cliOptions.add(new CliOption(CodegenConstants.PACKAGE_VERSION, "python package version.") - .defaultValue(DEFAULT_PACKAGE_VERSION)); - cliOptions.add(new CliOption(SERVER_PORT, "TCP port to listen to in app.run") - .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(implPackage)); + // Adds the following options in the codegen CLI + addOption(CodegenConstants.PACKAGE_NAME, + "python package name (convention: snake_case).", + DEFAULT_PACKAGE_NAME); + + addOption(CodegenConstants.PACKAGE_VERSION, + "python package version.", + DEFAULT_PACKAGE_VERSION); + + addOption(SERVER_PORT, + "TCP port to listen to in app.run", + String.valueOf(DEFAULT_SERVER_PORT)); + + addOption(CodegenConstants.SOURCE_FOLDER, + "directory for generated python source code", + DEFAULT_SOURCE_FOLDER); + + addOption(CodegenConstants.FASTAPI_IMPLEMENTATION_PACKAGE, + "python package name for the implementation code (convention: snake_case).", + implPackage); + + addSwitch(IS_LIBRARY, + "whether to generate minimal python code to be published as a separate library or not", + isLibrary); } @Override public void processOpts() { super.processOpts(); - if (additionalProperties.containsKey(CodegenConstants.PACKAGE_NAME)) { - setPackageName((String) additionalProperties.get(CodegenConstants.PACKAGE_NAME)); - } - - if (additionalProperties.containsKey(CodegenConstants.SOURCE_FOLDER)) { - this.sourceFolder = ((String) additionalProperties.get(CodegenConstants.SOURCE_FOLDER)); - } - - if (additionalProperties.containsKey(CodegenConstants.FASTAPI_IMPLEMENTATION_PACKAGE)) { - this.implPackage = ((String) additionalProperties.get(CodegenConstants.FASTAPI_IMPLEMENTATION_PACKAGE)); - // Prefix templating value with the package name - additionalProperties.put(CodegenConstants.FASTAPI_IMPLEMENTATION_PACKAGE, - this.packageName + "." + this.implPackage); - } + // converts additional property values into corresponding type and passes them to setter + convertPropertyToStringAndWriteBack(CodegenConstants.PACKAGE_NAME, this::setPackageName); + convertPropertyToStringAndWriteBack(CodegenConstants.SOURCE_FOLDER, this::setSourceFolder); + convertPropertyToStringAndWriteBack(CodegenConstants.FASTAPI_IMPLEMENTATION_PACKAGE, this::setImplPackage); + convertPropertyToBooleanAndWriteBack(IS_LIBRARY, this::setIsLibrary); modelPackage = packageName + "." + modelPackage; apiPackage = packageName + "." + apiPackage; @@ -166,8 +184,12 @@ public void processOpts() { supportingFiles.add(new SupportingFile("README.mustache", "", "README.md")); supportingFiles.add(new SupportingFile("openapi.mustache", "", "openapi.yaml")); supportingFiles.add(new SupportingFile("main.mustache", String.join(File.separator, new String[]{sourceFolder, packageName.replace('.', File.separatorChar)}), "main.py")); - supportingFiles.add(new SupportingFile("docker-compose.mustache", "", "docker-compose.yaml")); - supportingFiles.add(new SupportingFile("Dockerfile.mustache", "", "Dockerfile")); + + if (!this.isLibrary) { + supportingFiles.add(new SupportingFile("docker-compose.mustache", "", "docker-compose.yaml")); + supportingFiles.add(new SupportingFile("Dockerfile.mustache", "", "Dockerfile")); + } + supportingFiles.add(new SupportingFile("requirements.mustache", "", "requirements.txt")); supportingFiles.add(new SupportingFile("security_api.mustache", String.join(File.separator, new String[]{sourceFolder, packageName.replace('.', File.separatorChar)}), "security_api.py")); supportingFiles.add(new SupportingFile("extra_models.mustache", StringUtils.substringAfter(modelFileFolder(), outputFolder), "extra_models.py")); @@ -178,12 +200,15 @@ public void processOpts() { namespacePackagePath.append(File.separator).append(tmp); supportingFiles.add(new SupportingFile("__init__.mustache", namespacePackagePath.toString(), "__init__.py")); } + supportingFiles.add(new SupportingFile("__init__.mustache", StringUtils.substringAfter(modelFileFolder(), outputFolder), "__init__.py")); supportingFiles.add(new SupportingFile("__init__.mustache", StringUtils.substringAfter(apiFileFolder(), outputFolder), "__init__.py")); - supportingFiles.add(new SupportingFile("__init__.mustache", StringUtils.substringAfter(apiImplFileFolder(), outputFolder), "__init__.py")); + if (!this.isLibrary) { + supportingFiles.add(new SupportingFile("__init__.mustache", StringUtils.substringAfter(apiImplFileFolder(), outputFolder), "__init__.py")); + } + supportingFiles.add(new SupportingFile("conftest.mustache", testPackage.replace('.', File.separatorChar), "conftest.py")); - supportingFiles.add(new SupportingFile("gitignore.mustache", "", ".gitignore")); supportingFiles.add(new SupportingFile("pyproject_toml.mustache", "", "pyproject.toml")); supportingFiles.add(new SupportingFile("setup_cfg.mustache", "", "setup.cfg")); diff --git a/modules/openapi-generator/src/main/resources/python-fastapi/api.mustache b/modules/openapi-generator/src/main/resources/python-fastapi/api.mustache index 7c87ef7543cd..78b8f3a0352f 100644 --- a/modules/openapi-generator/src/main/resources/python-fastapi/api.mustache +++ b/modules/openapi-generator/src/main/resources/python-fastapi/api.mustache @@ -5,7 +5,10 @@ import importlib import pkgutil from {{apiPackage}}.{{classFilename}}_{{baseSuffix}} import Base{{classname}} +{{^isLibrary}}{{#fastapiImplementationPackage}} import {{fastapiImplementationPackage}} +{{/fastapiImplementationPackage}}{{/isLibrary}} + from fastapi import ( # noqa: F401 APIRouter, @@ -30,10 +33,13 @@ from {{modelPackage}}.extra_models import TokenModel # noqa: F401 router = APIRouter() +{{^isLibrary}} +{{#fastapiImplementationPackage}} ns_pkg = {{fastapiImplementationPackage}} for _, name, _ in pkgutil.iter_modules(ns_pkg.__path__, ns_pkg.__name__ + "."): importlib.import_module(name) - +{{/fastapiImplementationPackage}} +{{/isLibrary}} {{#operations}} {{#operation}} diff --git a/modules/openapi-generator/src/main/resources/python-fastapi/base_api.mustache b/modules/openapi-generator/src/main/resources/python-fastapi/base_api.mustache index d1d95c12eff8..908efdb6476f 100644 --- a/modules/openapi-generator/src/main/resources/python-fastapi/base_api.mustache +++ b/modules/openapi-generator/src/main/resources/python-fastapi/base_api.mustache @@ -1,20 +1,25 @@ # coding: utf-8 from typing import ClassVar, Dict, List, Tuple # noqa: F401 +{{#isLibrary}} +from abc import ABC, abstractmethod +{{/isLibrary}} {{#imports}} {{import}} {{/imports}} {{#securityImports.0}}from {{packageName}}.security_api import {{#securityImports}}get_token_{{.}}{{^-last}}, {{/-last}}{{/securityImports}}{{/securityImports.0}} -class Base{{classname}}: +class Base{{classname}}{{#isLibrary}}(ABC){{/isLibrary}}: subclasses: ClassVar[Tuple] = () def __init_subclass__(cls, **kwargs): super().__init_subclass__(**kwargs) Base{{classname}}.subclasses = Base{{classname}}.subclasses + (cls,) + {{#operations}} -{{#operation}} +{{#operation}}{{#isLibrary}} + @abstractmethod{{/isLibrary}} async def {{operationId}}( self, {{#allParams}} @@ -25,7 +30,7 @@ class Base{{classname}}: ...{{/notes}}{{^notes}}...{{/notes}} {{^-last}} - {{/-last}} {{/operation}} {{/operations}} + diff --git a/modules/openapi-generator/src/main/resources/python-fastapi/security_api.mustache b/modules/openapi-generator/src/main/resources/python-fastapi/security_api.mustache index 4fda1a8f91db..99a411583922 100644 --- a/modules/openapi-generator/src/main/resources/python-fastapi/security_api.mustache +++ b/modules/openapi-generator/src/main/resources/python-fastapi/security_api.mustache @@ -1,6 +1,7 @@ # coding: utf-8 from typing import List +{{#isLibrary}}from abc import ABC, abstractmethod{{/isLibrary}} from fastapi import Depends, Security # noqa: F401 from fastapi.openapi.models import OAuthFlowImplicit, OAuthFlows # noqa: F401 @@ -56,10 +57,22 @@ oauth2_implicit = OAuth2( ) ) {{/isImplicit}} +{{#isLibrary}} +{{>security_base_cls_init}} + @abstractmethod + def get_token(self, security_scopes: SecurityScopes, token: str) -> TokenModel: + ... + + @abstractmethod + def validate_scope(self, required_scopes: SecurityScopes, token_scopes: List[str]) -> bool: + ... + +{{/isLibrary}} def get_token_{{name}}( - security_scopes: SecurityScopes, token: str = Depends(oauth2_{{#isPassword}}password{{/isPassword}}{{#isCode}}code{{/isCode}}{{#isImplicit}}implicit{{/isImplicit}}) + security_scopes: SecurityScopes, + token: str = Depends(oauth2_{{#isPassword}}password{{/isPassword}}{{#isCode}}code{{/isCode}}{{#isImplicit}}implicit{{/isImplicit}}) ) -> TokenModel: """ Validate and decode token. @@ -69,9 +82,8 @@ def get_token_{{name}}( :return: Decoded token information or None if token is invalid :rtype: TokenModel | None """ - - ... - + {{^isLibrary}}...{{/isLibrary}} + {{#isLibrary}}return Base{{name}}.subclasses[0]().get_token(security_scopes, token){{/isLibrary}} def validate_scope_{{name}}( required_scopes: SecurityScopes, token_scopes: List[str] @@ -86,12 +98,24 @@ def validate_scope_{{name}}( :return: True if access to called API is allowed :rtype: bool """ - - return False - + {{^isLibrary}}return False{{/isLibrary}} + {{#isLibrary}}return Base{{name}}.subclasses[0]().validate_scope(required_scopes, token_scopes){{/isLibrary}} {{/isOAuth}} {{#isApiKey}} +{{#isLibrary}} + +{{>security_base_cls_init}} + + @abstractmethod + def get_token( + self,{{#isKeyInHeader}} + token_api_key_header: str,{{/isKeyInHeader}}{{#isKeyInCookie}} + token_api_key_cookie: str,{{/isKeyInCookie}}{{#isKeyInQuery}} + token_api_key_query: str,{{/isKeyInQuery}} + ) -> TokenModel: + ... +{{/isLibrary}} def get_token_{{name}}( {{#isKeyInHeader}}token_api_key_header: str = Security( APIKeyHeader(name="{{keyParamName}}", auto_error=False) @@ -113,18 +137,25 @@ def get_token_{{name}}( :return: Information attached to provided api_key or None if api_key is invalid or does not allow access to called API :rtype: TokenModel | None """ - - ... - + {{^isLibrary}}...{{/isLibrary}} + {{#isLibrary}}return Base{{name}}.subclasses[0]().get_token( + {{#isKeyInHeader}}token_api_key_header,{{/isKeyInHeader}}{{#isKeyInCookie}} + token_api_key_cookie,{{/isKeyInCookie}}{{#isKeyInQuery}} + token_api_key_query,{{/isKeyInQuery}} + ){{/isLibrary}} {{/isApiKey}} {{#isBasicBasic}} - basic_auth = HTTPBasic() +{{#isLibrary}} +{{>security_base_cls_init}} -def get_token_{{name}}( - credentials: HTTPBasicCredentials = Depends(basic_auth) -) -> TokenModel: + @abstractmethod + def get_token(self, credentials: HTTPBasicCredentials) -> TokenModel: + ... + +{{/isLibrary}} +def get_token_{{name}}(credentials: HTTPBasicCredentials = Depends(basic_auth)) -> TokenModel: """ Check and retrieve authentication information from basic auth. @@ -132,15 +163,20 @@ def get_token_{{name}}( :type credentials: HTTPBasicCredentials :rtype: TokenModel | None """ - - ... - + {{^isLibrary}}...{{/isLibrary}} + {{#isLibrary}}return Base{{name}}.subclasses[0]().get_token(credentials){{/isLibrary}} {{/isBasicBasic}} {{#isBasicBearer}} - bearer_auth = HTTPBearer() +{{#isLibrary}} + +{{>security_base_cls_init}} + @abstractmethod + def get_token(self, credentials: HTTPAuthorizationCredentials) -> TokenModel: + ... +{{/isLibrary}} def get_token_{{name}}(credentials: HTTPAuthorizationCredentials = Depends(bearer_auth)) -> TokenModel: """ Check and retrieve authentication information from custom bearer token. @@ -150,8 +186,7 @@ def get_token_{{name}}(credentials: HTTPAuthorizationCredentials = Depends(beare :return: Decoded token information or None if token is invalid :rtype: TokenModel | None """ - - ... - + {{^isLibrary}}...{{/isLibrary}} + {{#isLibrary}}return Base{{name}}.subclasses[0]().get_token(credentials){{/isLibrary}} {{/isBasicBearer}} {{/authMethods}} diff --git a/modules/openapi-generator/src/main/resources/python-fastapi/security_base_cls_init.mustache b/modules/openapi-generator/src/main/resources/python-fastapi/security_base_cls_init.mustache new file mode 100644 index 000000000000..9cbe22e0d45e --- /dev/null +++ b/modules/openapi-generator/src/main/resources/python-fastapi/security_base_cls_init.mustache @@ -0,0 +1,6 @@ +class Base{{name}}(ABC): + subclasses: ClassVar[Tuple] = () + + def __init_subclass__(cls, **kwargs): + super().__init_subclass__(**kwargs) + Base{{name}}.subclasses = Base{{name}}.subclasses + (cls,) \ No newline at end of file diff --git a/samples/server/petstore/python-fastapi/src/openapi_server/apis/fake_api.py b/samples/server/petstore/python-fastapi/src/openapi_server/apis/fake_api.py index 0e3f8d51b319..f3a400fa4433 100644 --- a/samples/server/petstore/python-fastapi/src/openapi_server/apis/fake_api.py +++ b/samples/server/petstore/python-fastapi/src/openapi_server/apis/fake_api.py @@ -5,8 +5,11 @@ import pkgutil from openapi_server.apis.fake_api_base import BaseFakeApi + import openapi_server.impl + + from fastapi import ( # noqa: F401 APIRouter, Body, @@ -34,7 +37,6 @@ for _, name, _ in pkgutil.iter_modules(ns_pkg.__path__, ns_pkg.__name__ + "."): importlib.import_module(name) - @router.get( "/fake/query_param_default", responses={ diff --git a/samples/server/petstore/python-fastapi/src/openapi_server/apis/fake_api_base.py b/samples/server/petstore/python-fastapi/src/openapi_server/apis/fake_api_base.py index 1c71537aa944..7838330564b9 100644 --- a/samples/server/petstore/python-fastapi/src/openapi_server/apis/fake_api_base.py +++ b/samples/server/petstore/python-fastapi/src/openapi_server/apis/fake_api_base.py @@ -13,6 +13,8 @@ class BaseFakeApi: def __init_subclass__(cls, **kwargs): super().__init_subclass__(**kwargs) BaseFakeApi.subclasses = BaseFakeApi.subclasses + (cls,) + + async def fake_query_param_default( self, has_default: Annotated[Optional[StrictStr], Field(description="has default value")], @@ -20,3 +22,4 @@ async def fake_query_param_default( ) -> None: """""" ... + 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..03c0971abd9b 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 @@ -5,8 +5,11 @@ import pkgutil from openapi_server.apis.pet_api_base import BasePetApi + import openapi_server.impl + + from fastapi import ( # noqa: F401 APIRouter, Body, @@ -36,7 +39,6 @@ for _, name, _ in pkgutil.iter_modules(ns_pkg.__path__, ns_pkg.__name__ + "."): importlib.import_module(name) - @router.post( "/pet", responses={ 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..0c317fb0a446 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,6 +15,8 @@ class BasePetApi: def __init_subclass__(cls, **kwargs): super().__init_subclass__(**kwargs) BasePetApi.subclasses = BasePetApi.subclasses + (cls,) + + async def add_pet( self, pet: Annotated[Pet, Field(description="Pet object that needs to be added to the store")], @@ -82,3 +84,4 @@ async def upload_file( ) -> ApiResponse: """""" ... + 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..c222e0cdcd8b 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 @@ -5,8 +5,11 @@ import pkgutil from openapi_server.apis.store_api_base import BaseStoreApi + import openapi_server.impl + + from fastapi import ( # noqa: F401 APIRouter, Body, @@ -35,7 +38,6 @@ for _, name, _ in pkgutil.iter_modules(ns_pkg.__path__, ns_pkg.__name__ + "."): importlib.import_module(name) - @router.delete( "/store/order/{orderId}", responses={ 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..b6dbdbf7fa05 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,6 +14,8 @@ class BaseStoreApi: def __init_subclass__(cls, **kwargs): super().__init_subclass__(**kwargs) BaseStoreApi.subclasses = BaseStoreApi.subclasses + (cls,) + + async def delete_order( self, orderId: Annotated[StrictStr, Field(description="ID of the order that needs to be deleted")], @@ -43,3 +45,4 @@ async def place_order( ) -> Order: """""" ... + 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 8f848f0131de..1975ab0f2859 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 @@ -5,8 +5,11 @@ import pkgutil from openapi_server.apis.user_api_base import BaseUserApi + import openapi_server.impl + + from fastapi import ( # noqa: F401 APIRouter, Body, @@ -35,7 +38,6 @@ for _, name, _ in pkgutil.iter_modules(ns_pkg.__path__, ns_pkg.__name__ + "."): importlib.import_module(name) - @router.post( "/user", responses={ 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..7a16c71c6982 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 @@ -14,6 +14,8 @@ class BaseUserApi: def __init_subclass__(cls, **kwargs): super().__init_subclass__(**kwargs) BaseUserApi.subclasses = BaseUserApi.subclasses + (cls,) + + async def create_user( self, user: Annotated[User, Field(description="Created user object")], @@ -77,3 +79,4 @@ async def update_user( ) -> None: """This can only be done by the logged in user.""" ... + diff --git a/samples/server/petstore/python-fastapi/src/openapi_server/security_api.py b/samples/server/petstore/python-fastapi/src/openapi_server/security_api.py index d216ee4271ea..2e6462c4d59a 100644 --- a/samples/server/petstore/python-fastapi/src/openapi_server/security_api.py +++ b/samples/server/petstore/python-fastapi/src/openapi_server/security_api.py @@ -2,6 +2,7 @@ from typing import List + from fastapi import Depends, Security # noqa: F401 from fastapi.openapi.models import OAuthFlowImplicit, OAuthFlows # noqa: F401 from fastapi.security import ( # noqa: F401 @@ -29,10 +30,9 @@ ) ) ) - - def get_token_petstore_auth( - security_scopes: SecurityScopes, token: str = Depends(oauth2_implicit) + security_scopes: SecurityScopes, + token: str = Depends(oauth2_implicit) ) -> TokenModel: """ Validate and decode token. @@ -42,9 +42,8 @@ def get_token_petstore_auth( :return: Decoded token information or None if token is invalid :rtype: TokenModel | None """ - ... - + def validate_scope_petstore_auth( required_scopes: SecurityScopes, token_scopes: List[str] @@ -59,10 +58,8 @@ def validate_scope_petstore_auth( :return: True if access to called API is allowed :rtype: bool """ - return False - - + def get_token_api_key( token_api_key_header: str = Security( APIKeyHeader(name="api_key", auto_error=False) @@ -78,6 +75,5 @@ def get_token_api_key( :return: Information attached to provided api_key or None if api_key is invalid or does not allow access to called API :rtype: TokenModel | None """ - ... - +