Skip to content
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
1 change: 1 addition & 0 deletions docs/generators/python-fastapi.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|sortModelPropertiesByRequiredFlag|Sort model properties to place required parameters before optional parameters.| |true|
|sortParamsByRequiredFlag|Sort method arguments to place required parameters before optional parameters.| |true|
|sourceFolder|directory for generated python source code| |src|
|useExternalImplementationPackage|If true, fastapiImplementationPackage is a fully qualified python package that already exists outside the generated package: it is imported as-is (not prefixed with packageName) and its __init__.py is not generated.| |false|

## IMPORT MAPPING

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,9 @@ public static enum ENUM_PROPERTY_NAMING_TYPE {camelCase, PascalCase, snake_case,

public static final String FASTAPI_IMPLEMENTATION_PACKAGE = "fastapiImplementationPackage";

public static final String USE_EXTERNAL_IMPLEMENTATION_PACKAGE = "useExternalImplementationPackage";
public static final String USE_EXTERNAL_IMPLEMENTATION_PACKAGE_DESC = "If true, fastapiImplementationPackage is a fully qualified python package that already exists outside the generated package: it is imported as-is (not prefixed with packageName) and its __init__.py is not generated.";

public static final String WITH_XML = "withXml";

public static final String WITH_GO_MOD = "withGoMod";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ public class PythonFastAPIServerCodegen extends AbstractPythonCodegen {
private static final String X_FASTAPI_REQUEST_BODY_EXAMPLE = "x-python-fastapi-request-body-example";

private String implPackage;
private boolean useExternalImplementationPackage = false;

@Override
public CodegenType getTag() {
Expand Down Expand Up @@ -128,6 +129,8 @@ public PythonFastAPIServerCodegen() {
.defaultValue(DEFAULT_SOURCE_FOLDER));
cliOptions.add(new CliOption(CodegenConstants.FASTAPI_IMPLEMENTATION_PACKAGE, "python package name for the implementation code (convention: snake_case).")
.defaultValue(implPackage));
cliOptions.add(CliOption.newBoolean(CodegenConstants.USE_EXTERNAL_IMPLEMENTATION_PACKAGE, CodegenConstants.USE_EXTERNAL_IMPLEMENTATION_PACKAGE_DESC)
.defaultValue(Boolean.FALSE.toString()));
}

@Override
Expand Down Expand Up @@ -172,16 +175,22 @@ public void processOpts() {
this.sourceFolder = ((String) additionalProperties.get(CodegenConstants.SOURCE_FOLDER));
}

if (additionalProperties.containsKey(CodegenConstants.USE_EXTERNAL_IMPLEMENTATION_PACKAGE)) {
this.useExternalImplementationPackage = convertPropertyToBooleanAndWriteBack(CodegenConstants.USE_EXTERNAL_IMPLEMENTATION_PACKAGE);
}

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);
}

modelPackage = packageName + "." + modelPackage;
apiPackage = packageName + "." + apiPackage;
implPackage = packageName + "." + implPackage;
if (!useExternalImplementationPackage) {
// The implementation package is a sub-package of the generated one
implPackage = packageName + "." + implPackage;
}
// Templating value: the fully qualified implementation package
additionalProperties.put(CodegenConstants.FASTAPI_IMPLEMENTATION_PACKAGE, implPackage);

supportingFiles.add(new SupportingFile("README.mustache", "", "README.md"));
supportingFiles.add(new SupportingFile("openapi.mustache", "", "openapi.yaml"));
Expand All @@ -200,7 +209,10 @@ public void processOpts() {
}
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 (!useExternalImplementationPackage) {
// An external implementation package already exists and is owned by the user: nothing to generate in it
supportingFiles.add(new SupportingFile("__init__.mustache", StringUtils.substringAfter(apiImplFileFolder(), outputFolder), "__init__.py"));
}

supportingFiles.add(new SupportingFile("conftest.mustache", testPackage.replace('.', File.separatorChar), "conftest.py"));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,39 @@ public void testAdditionalPropertiesPutForConfigValues() throws Exception {
files.forEach(File::deleteOnExit);

TestUtils.assertFileExists(Paths.get(output.getAbsolutePath(), "/src", "/nodesc", IMPL_PKG, "__init__.py"));
TestUtils.assertFileContains(Paths.get(output + "/src/nodesc/apis/nodesc_api.py"),
"import nodesc." + IMPL_PKG + "\n");
}

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

// A fully qualified package living outside the generated one, owned by the user
final String EXTERNAL_IMPL_PKG = "my_company.handlers";
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, EXTERNAL_IMPL_PKG)
.addAdditionalProperty(CodegenConstants.USE_EXTERNAL_IMPLEMENTATION_PACKAGE, true);

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

// Imported as-is, not prefixed with the package name
final String apiFile = output + "/src/nodesc/apis/nodesc_api.py";
TestUtils.assertFileContains(Paths.get(apiFile), "import " + EXTERNAL_IMPL_PKG + "\n");
TestUtils.assertFileContains(Paths.get(apiFile), "ns_pkg = " + EXTERNAL_IMPL_PKG + "\n");
TestUtils.assertFileNotContains(Paths.get(apiFile), "nodesc." + EXTERNAL_IMPL_PKG);

// Nothing is generated in a package the generator does not own
TestUtils.assertFileNotExists(Paths.get(output.getAbsolutePath(), "/src", "/nodesc", "impl", "__init__.py"));
TestUtils.assertFileNotExists(Paths.get(output.getAbsolutePath(), "/src", "/my_company", "/handlers", "__init__.py"));
TestUtils.assertFileNotExists(Paths.get(output.getAbsolutePath(), "/src", "/nodesc", "/my_company", "/handlers", "__init__.py"));
}

@Test
Expand Down
Loading