Skip to content

Commit

Permalink
[python-fastapi] Fix CLI crash when calling config-help & update do…
Browse files Browse the repository at this point in the history
…cs (OpenAPITools#18816)

* Implement test for uniqueness of CliOptions across all generators

* Refactor existing test to use @dataProvider and fluent assertion

* Remove extraneous cliOption definition for disallowAdditionalPropertiesIfNotPresent

This is already defined (matching exactly in all aspects) in DefaultCodegen.

Resolves OpenAPITools#18810

* Avoid variable declaration & assignment when only used once

* Add additional uniqueness tests for all DefaultCodegen List<> members

Test uniqueness for lists of:
- supportingFiles
- supportedLibraries
- supportedVendorExtensions

* Disable AllGeneratorsTest.noDuplicateSupportingFiles for the time being

Re-enable when OpenAPITools#18831 is fixed

* Generate updated python-fastapi docs
  • Loading branch information
Philzen authored Jun 4, 2024
1 parent 91a1931 commit 35a8820
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 37 deletions.
1 change: 1 addition & 0 deletions docs/generators/python-fastapi.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
<li>except</li>
<li>exec</li>
<li>false</li>
<li>field</li>
<li>finally</li>
<li>float</li>
<li>for</li>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,18 +90,17 @@ public PythonFastAPIServerCodegen() {
super();

modifyFeatureSet(features -> features.includeSecurityFeatures(
SecurityFeature.OAuth2_AuthorizationCode,
SecurityFeature.OAuth2_Password
SecurityFeature.OAuth2_AuthorizationCode,
SecurityFeature.OAuth2_Password
));

generatorMetadata = GeneratorMetadata.newBuilder(generatorMetadata)
.stability(Stability.BETA)
.build();
generatorMetadata = GeneratorMetadata.newBuilder(generatorMetadata).stability(Stability.BETA).build();

SimpleModule simpleModule = new SimpleModule();
simpleModule.addKeySerializer(String.class, new SnakeCaseKeySerializer());
simpleModule.addSerializer(Boolean.class, new PythonBooleanSerializer());
MAPPER.registerModule(simpleModule);
MAPPER.registerModule(
new SimpleModule()
.addKeySerializer(String.class, new SnakeCaseKeySerializer())
.addSerializer(Boolean.class, new PythonBooleanSerializer())
);

/*
* Additional Properties. These values can be passed to the templates and
Expand Down Expand Up @@ -139,19 +138,6 @@ 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));

// option to change how we process + set the data in the 'additionalProperties' keyword.
CliOption disallowAdditionalPropertiesIfNotPresentOpt = CliOption.newBoolean(
CodegenConstants.DISALLOW_ADDITIONAL_PROPERTIES_IF_NOT_PRESENT,
CodegenConstants.DISALLOW_ADDITIONAL_PROPERTIES_IF_NOT_PRESENT_DESC).defaultValue(Boolean.TRUE.toString());
Map<String, String> disallowAdditionalPropertiesIfNotPresentOpts = new HashMap<>();
disallowAdditionalPropertiesIfNotPresentOpts.put("false",
"The 'additionalProperties' implementation is compliant with the OAS and JSON schema specifications.");
disallowAdditionalPropertiesIfNotPresentOpts.put("true",
"Keep the old (incorrect) behaviour that 'additionalProperties' is set to false by default.");
disallowAdditionalPropertiesIfNotPresentOpt.setEnum(disallowAdditionalPropertiesIfNotPresentOpts);
cliOptions.add(disallowAdditionalPropertiesIfNotPresentOpt);
this.setDisallowAdditionalPropertiesIfNotPresent(true);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,39 +17,94 @@
package org.openapitools.codegen;

import org.openapitools.codegen.config.CodegenConfigurator;
import org.testng.Assert;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.stream.Collectors;

import static org.assertj.core.api.Assertions.assertThat;

public class AllGeneratorsTest {

@Test
public void testEachWithPetstore() throws IOException {
for (final CodegenConfig codegenConfig : CodegenConfigLoader.getAll()) {
final File output = Files.createTempDirectory("test").toFile();
@DataProvider(name = "generators") Iterator<CodegenConfig> generators() {
return CodegenConfigLoader.getAll().iterator();
}

@Test(dataProvider = "generators")
public void testEachWithPetstore(CodegenConfig codegenConfig) {
try {
File output = Files.createTempDirectory("test").toFile();
output.deleteOnExit();

final CodegenConfigurator configurator = new CodegenConfigurator()
.setGeneratorName(codegenConfig.getName())
.setInputSpec("src/test/resources/3_0/petstore.yaml")
.setOutputDir(output.getAbsolutePath().replace("\\", "/"));

final ClientOptInput clientOptInput = configurator.toClientOptInput();
List<File> files = List.of();
try {
DefaultGenerator generator = new DefaultGenerator();
files = generator.opts(clientOptInput).generate();
} catch (Exception e) {
Assert.fail("Generator " + codegenConfig.getName() + " threw an exception (generating " + codegenConfig.getInputSpec() + "): " + e.getMessage(), e);
}
List<File> files = new DefaultGenerator().opts(configurator.toClientOptInput()).generate();

// Main intention of this test is to check that nothing crashes. Besides, we check here that
// at least 1 file is generated, besides the common ".openapi-generator-ignore", "FILES" and "VERSION" files.
Assert.assertTrue(files.size() >= 4);
assertThat(files).hasSizeGreaterThanOrEqualTo(4);
} catch (Exception e) {
throw new RuntimeException(
String.format(
Locale.ROOT,
"Generator %s threw an exception (generating %s): %s",
codegenConfig.getName(), codegenConfig.getInputSpec(), e.getMessage()
), e
);
}
}

/**
* Regression test for <a href="https://github.com/OpenAPITools/openapi-generator/issues/18810">#18810</a>
*/
@Test(dataProvider = "generators") void noDuplicateCliOptions(CodegenConfig codegenConfig) {
final List<String> cliOptionKeys = codegenConfig.cliOptions()
.stream().map(CliOption::getOpt).collect(Collectors.toList());

assertThat(cliOptionKeys).allSatisfy(
opt -> assertThat(cliOptionKeys)
.as("Generator '%s' defines CliOption '%s' more than once!", codegenConfig.getName(), opt)
.containsOnlyOnce(opt)
);
}

@Test(dataProvider = "generators") void noDuplicateSupportedLibraries(CodegenConfig codegenConfig) {
final var supportedLibraries = codegenConfig.supportedLibraries().keySet();

assertThat(supportedLibraries).allSatisfy(
lib -> assertThat(supportedLibraries)
.as("Generator '%s' defines '%s' more than once in supportedLibraries!", codegenConfig.getName(), lib)
.containsOnlyOnce(lib)
);
}

@Test(dataProvider = "generators", enabled = false) // re-enable when https://github.com/OpenAPITools/openapi-generator/issues/18831 is fixed
void noDuplicateSupportingFiles(CodegenConfig codegenConfig) {
final List<String> supportingFiles = codegenConfig.supportingFiles()
.stream().map(SupportingFile::toString).collect(Collectors.toList());

assertThat(supportingFiles).allSatisfy(
file -> assertThat(supportingFiles)
.as("Generator '%s' defines '%s' more than once in supportingFiles!", codegenConfig.getName(), file)
.containsOnlyOnce(file)
);
}

@Test(dataProvider = "generators") void noDuplicateSupportedVendorExtensions(CodegenConfig codegenConfig) {
final List<VendorExtension> supportedVendorExtensions = codegenConfig.getSupportedVendorExtensions();

assertThat(supportedVendorExtensions).allSatisfy(
extension -> assertThat(supportedVendorExtensions)
.as("Generator '%s' defines '%s' more than once in supportedVendorExtensions!", codegenConfig.getName(), extension)
.containsOnlyOnce(extension)
);
}
}

0 comments on commit 35a8820

Please sign in to comment.