Skip to content

Commit ff5dc0f

Browse files
committed
Models with additionalProperties and properties that are complex now generate correct code
1 parent 9c11fcf commit ff5dc0f

3 files changed

Lines changed: 89 additions & 0 deletions

File tree

modules/openapi-generator/src/main/java/org/openapitools/codegen/OpenAPINormalizer.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -728,6 +728,9 @@ public Schema normalizeSchema(Schema schema, Set<Schema> visitedSchemas) {
728728
} else if (schema.getAdditionalProperties() instanceof Schema) { // map
729729
normalizeMapSchema(schema);
730730
normalizeSchema((Schema) schema.getAdditionalProperties(), visitedSchemas);
731+
if (schema.getProperties() != null && !schema.getProperties().isEmpty()) {
732+
normalizeProperties(schema.getProperties(), visitedSchemas);
733+
}
731734
} else if (ModelUtils.isOneOf(schema)) { // oneOf
732735
return normalizeOneOf(schema, visitedSchemas);
733736
} else if (ModelUtils.isAnyOf(schema)) { // anyOf

modules/openapi-generator/src/test/java/org/openapitools/codegen/DefaultCodegenTest.java

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
import org.junit.jupiter.api.Assertions;
4141
import org.openapitools.codegen.config.CodegenConfigurator;
4242
import org.openapitools.codegen.config.GlobalSettings;
43+
import org.openapitools.codegen.java.assertions.JavaFileAssert;
4344
import org.openapitools.codegen.languages.SpringCodegen;
4445
import org.openapitools.codegen.model.ModelMap;
4546
import org.openapitools.codegen.model.ModelsMap;
@@ -55,6 +56,7 @@
5556
import java.nio.file.Files;
5657
import java.util.*;
5758
import java.util.concurrent.*;
59+
import java.util.function.Function;
5860
import java.util.stream.Collectors;
5961

6062
import static org.assertj.core.api.Assertions.assertThat;
@@ -5017,4 +5019,58 @@ public void testQueryIsJsonMimeType() {
50175019

50185020
assertTrue(codegenOperation.queryParams.stream().allMatch(p -> p.queryIsJsonMimeType));
50195021
}
5022+
5023+
@Test(description = "Issue #20213")
5024+
public void testModelAdditionalPropertiesWithNullableProperty() throws Exception {
5025+
File output = Files.createTempDirectory("test").toFile();
5026+
output.deleteOnExit();
5027+
File spring = new File(output, "spring");
5028+
5029+
final CodegenConfigurator configurator = new CodegenConfigurator()
5030+
.setGeneratorName("spring")
5031+
.setInputSpec("src/test/resources/3_1/issue_20213.yaml")
5032+
.setOutputDir(spring.getAbsolutePath().replace("\\", "/"));
5033+
5034+
final ClientOptInput clientOptInput = configurator.toClientOptInput();
5035+
DefaultGenerator generator = new DefaultGenerator();
5036+
Map<String, File> files = generator.opts(clientOptInput).generate()
5037+
.stream().collect(Collectors.toMap(File::getName, Function.identity()));
5038+
5039+
JavaFileAssert.assertThat(files.get("SampleObjectWithAdditionalFalse.java"))
5040+
.assertProperty("someString")
5041+
.withType("JsonNullable<String>");
5042+
assertFalse(files.containsKey("SampleObjectWithAdditionalFalseSomeString.java"));
5043+
5044+
5045+
File tsAngular = new File(output, "ts-angular");
5046+
final CodegenConfigurator tsConfigurator = new CodegenConfigurator()
5047+
.setGeneratorName("typescript-angular")
5048+
.setInputSpec("src/test/resources/3_1/issue_20213.yaml")
5049+
.setOutputDir(tsAngular.getAbsolutePath().replace("\\", "/"));
5050+
5051+
final ClientOptInput tsClientOptInput = tsConfigurator.toClientOptInput();
5052+
DefaultGenerator tsGenerator = new DefaultGenerator();
5053+
Map<String, File> tsFiles = tsGenerator.opts(tsClientOptInput).generate()
5054+
.stream().collect(Collectors.toMap(File::getName, Function.identity()));
5055+
5056+
System.out.println(Files.readString(tsFiles.get("sampleObjectWithAdditionalFalse.ts").toPath()));
5057+
TestUtils.assertFileContains(tsFiles.get("sampleObjectWithAdditionalFalse.ts").toPath(), "someString?: string");
5058+
assertFalse(tsFiles.containsKey("sampleObjectWithAdditionalFalseSomeString.ts"));
5059+
5060+
File javaClient = new File(output, "java");
5061+
final CodegenConfigurator javaClientConfigurator = new CodegenConfigurator()
5062+
.setGeneratorName("java")
5063+
.setInputSpec("src/test/resources/3_1/issue_20213.yaml")
5064+
.setOutputDir(javaClient.getAbsolutePath().replace("\\", "/"));
5065+
5066+
final ClientOptInput javaClientClientOptInput = javaClientConfigurator.toClientOptInput();
5067+
DefaultGenerator javaClientGenerator = new DefaultGenerator();
5068+
Map<String, File> javaClientFiles = javaClientGenerator.opts(javaClientClientOptInput).generate()
5069+
.stream().collect(Collectors.toMap(File::getName, Function.identity()));
5070+
5071+
JavaFileAssert.assertThat(javaClientFiles.get("SampleObjectWithAdditionalFalse.java"))
5072+
.assertProperty("someString")
5073+
.withType("String");
5074+
assertFalse(javaClientFiles.containsKey("SampleObjectWithAdditionalFalseSomeString.java"));
5075+
}
50205076
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# filename: nullable-properties-with-additional-properties-false.yaml
2+
openapi: 3.1.0
3+
info:
4+
title: ""
5+
version: 1.0.0
6+
components:
7+
schemas:
8+
# For reference, an object without additionalProperties: false, but with nullable properties
9+
SampleObject:
10+
properties:
11+
someString:
12+
anyOf:
13+
- type: string
14+
- type: 'null'
15+
16+
# For reference, an object with additionalProperties: false, but without any nullable properties
17+
ReferenceObject:
18+
additionalProperties: false
19+
properties:
20+
someString:
21+
type: string
22+
23+
# The broken case: an object with additionalProperties: false and nullable properties
24+
SampleObjectWithAdditionalFalse:
25+
additionalProperties: false
26+
properties:
27+
someString:
28+
anyOf:
29+
- type: string
30+
- type: 'null'

0 commit comments

Comments
 (0)