Skip to content

Commit 18ac063

Browse files
committed
Work in progress
1 parent bf1dc5f commit 18ac063

11 files changed

Lines changed: 515 additions & 41 deletions

File tree

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

Lines changed: 56 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -644,6 +644,10 @@ protected void normalizeComponentsSchemas() {
644644

645645
// normalize the schemas
646646
schemas.put(schemaName, normalizeSchema(schema, new HashSet<>()));
647+
648+
if (getRule(REPLACE_ONE_OF_BY_DISCRIMINATOR_MAPPING)) {
649+
ensureInheritance(schema, schemaName);
650+
}
647651
}
648652
}
649653
}
@@ -1577,32 +1581,75 @@ protected Schema processSimplifyOneOf(Schema schema) {
15771581
}
15781582

15791583

1584+
/**
1585+
* Ensure inheritance is correctly defined for OneOf and Discriminators.
1586+
*
1587+
* For schemas containing oneOf and discriminator.propertyName:
1588+
* Create the mappings as $refs
1589+
* Remove OneOf
1590+
*
1591+
* For referenced schemas, ensure that there is an allOf with this schema.
1592+
*/
15801593
protected Schema processReplaceOneOfByMapping(Schema schema) {
15811594
if (!getRule(REPLACE_ONE_OF_BY_DISCRIMINATOR_MAPPING)) {
15821595
return schema;
15831596
}
1584-
1585-
if (schema.getDiscriminator() != null) {
1586-
Discriminator discriminator = schema.getDiscriminator();
1597+
Discriminator discriminator = schema.getDiscriminator();
1598+
if (discriminator != null) {
15871599
if (discriminator.getMapping() == null) {
1588-
Map<String, String> mapping = new TreeMap<>();
1589-
discriminator.setMapping(mapping);
1600+
Map<String, String> mappings = new TreeMap<>();
1601+
discriminator.setMapping(mappings);
15901602
for (Object oneOfObject : schema.getOneOf()) {
15911603
Schema oneOf = (Schema) oneOfObject;
1592-
String ref = oneOf.get$ref();
1593-
if (ref != null) {
1594-
String name = ref.contains("/") ? ref.substring(ref.lastIndexOf('/') + 1) : ref;
1595-
mapping.put(name, oneOf.get$ref());
1604+
String refSchema = oneOf.get$ref();
1605+
if (refSchema != null) {
1606+
String name = refSchema.contains("/") ? refSchema.substring(refSchema.lastIndexOf('/') + 1) : refSchema;
1607+
mappings.put(name, refSchema);
15961608
}
15971609
}
15981610
}
15991611

1612+
16001613
schema.setOneOf(null);
16011614
}
16021615
return schema;
16031616
}
16041617

16051618

1619+
protected void ensureInheritance(Schema parent, String parentName) {
1620+
Discriminator discriminator = parent.getDiscriminator();
1621+
if (discriminator != null && discriminator.getMapping() != null) {
1622+
for (String mapping : discriminator.getMapping().keySet()) {
1623+
Schema child = ModelUtils.getSchema(openAPI, mapping);
1624+
if (child != null) {
1625+
ensureInheritance(parent, child, parentName);
1626+
}
1627+
}
1628+
}
1629+
}
1630+
1631+
protected void ensureInheritance(Schema parent, Schema child, String parentName) {
1632+
List<Schema> allOf = child.getAllOf();
1633+
if (allOf != null) {
1634+
if (hasParent(child, parent)) {
1635+
return;
1636+
}
1637+
} else {
1638+
allOf = new ArrayList<>();
1639+
child.setAllOf(allOf);
1640+
}
1641+
Schema refToParent = new Schema<>().$ref("#/components/schemas/" + parentName);
1642+
allOf.add(refToParent);
1643+
}
1644+
1645+
private boolean hasParent(Schema child, Schema parent) {
1646+
List<Schema> allOf = child.getAllOf();
1647+
if (allOf != null) {
1648+
return allOf.stream().anyMatch(s -> hasParent(s, parent));
1649+
}
1650+
return false;
1651+
}
1652+
16061653
/**
16071654
* Set nullable to true in array/set if needed.
16081655
*

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -845,10 +845,15 @@ public void preprocessOpenAPI(OpenAPI openAPI) {
845845

846846
@Override
847847
public OperationsMap postProcessOperationsWithModels(OperationsMap objs, List<ModelMap> allModels) {
848+
if (isSpringCodegen()) {
849+
Map<String, String> newImport = Map.of("import", importMapping.get("Nullable"));
850+
objs.getImports().add(newImport);
851+
}
848852
final OperationMap operations = objs.getOperations();
849853
if (operations != null) {
850854
final List<CodegenOperation> ops = operations.getOperation();
851855
for (final CodegenOperation operation : ops) {
856+
852857
final List<CodegenResponse> responses = operation.responses;
853858
if (responses != null) {
854859
for (final CodegenResponse resp : responses) {
@@ -894,6 +899,7 @@ public void setIsVoid(boolean isVoid) {
894899

895900
prepareVersioningParameters(ops);
896901
handleImplicitHeaders(operation);
902+
897903
}
898904
// The tag for the controller is the first tag of the first operation
899905
final CodegenOperation firstOperation = ops.get(0);

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

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import io.swagger.v3.oas.models.media.Schema;
1616
import io.swagger.v3.oas.models.servers.Server;
1717
import io.swagger.v3.parser.core.models.ParseOptions;
18+
import org.apache.commons.io.FileUtils;
1819
import org.openapitools.codegen.java.assertions.JavaFileAssert;
1920
import org.openapitools.codegen.model.ModelMap;
2021
import org.openapitools.codegen.model.ModelsMap;
@@ -342,14 +343,23 @@ public static ModelsMap createCodegenModelWrapper(CodegenModel cm) {
342343
}
343344

344345
public static Path newTempFolder() {
345-
final Path tempDir;
346+
File file = new File("c:\\temp\\generated");
347+
file.mkdir();
346348
try {
347-
tempDir = Files.createTempDirectory("test");
349+
FileUtils.cleanDirectory(file);
348350
} catch (IOException e) {
349351
throw new RuntimeException(e);
350352
}
351-
tempDir.toFile().deleteOnExit();
352353

353-
return tempDir;
354+
return file.toPath();
355+
// final Path tempDir;
356+
// try {
357+
// tempDir = Files.createTempDirectory("test");
358+
// } catch (IOException e) {
359+
// throw new RuntimeException(e);
360+
// }
361+
// tempDir.toFile().deleteOnExit();
362+
//
363+
// return tempDir;
354364
}
355365
}

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

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4337,7 +4337,7 @@ public Object[][] replaceOneOf() {
43374337
}
43384338

43394339
@Test(dataProvider = "replaceOneOf" )
4340-
void replaceOneOfByDiscriminatorMapping(String file) throws IOException {
4340+
void replaceOneOfByDiscriminatorMapping(String file) {
43414341
Map<String, File> files = generateFromContract(file, APACHE, Map.of(),
43424342
codegen -> codegen.addOpenapiNormalizer("REPLACE_ONE_OF_BY_DISCRIMINATOR_MAPPING", "true"));
43434343

@@ -4355,4 +4355,64 @@ void replaceOneOfByDiscriminatorMapping(String file) throws IOException {
43554355
.fileContains("List<Double> coordinates");
43564356

43574357
}
4358+
@Test
4359+
void issue_23276() {
4360+
Map<String, File> files = generateFromContract("src/test/resources/3_0/java/issue_23276.yaml", APACHE, Map.of(),
4361+
codegen -> codegen.addOpenapiNormalizer("REPLACE_ONE_OF_BY_DISCRIMINATOR_MAPPING", "true"));
4362+
4363+
JavaFileAssert.assertThat(files.get("GeoJsonObject.java"))
4364+
.isNormalClass()
4365+
.doesNotExtendsClasses()
4366+
.fileContains("String type")
4367+
.fileDoesNotContain("coordinates")
4368+
.assertTypeAnnotations()
4369+
.containsWithName("JsonSubTypes");
4370+
4371+
JavaFileAssert.assertThat(files.get("Polygon.java"))
4372+
.extendsClass("GeoJsonObject")
4373+
.doesNotImplementInterfaces("GeoJsonObject")
4374+
.fileContains("List<Double> coordinates");
4375+
}
4376+
4377+
@Test
4378+
void issue_15() {
4379+
Map<String, File> files = generateFromContract("src/test/resources/3_0/composed-oneof.yaml", APACHE,
4380+
Map.of(),
4381+
codegen -> codegen.addOpenapiNormalizer("REPLACE_ONE_OF_BY_DISCRIMINATOR_MAPPING", "true")
4382+
.addInlineSchemaOption("REFACTOR_ALLOF_INLINE_SCHEMAS", "true"));
4383+
4384+
JavaFileAssert.assertThat(files.get("GeoJsonObject.java"))
4385+
.isNormalClass()
4386+
.doesNotExtendsClasses()
4387+
.fileContains("String type")
4388+
.fileDoesNotContain("coordinates")
4389+
.assertTypeAnnotations()
4390+
.containsWithName("JsonSubTypes");
4391+
4392+
JavaFileAssert.assertThat(files.get("Polygon.java"))
4393+
.extendsClass("GeoJsonObject")
4394+
.doesNotImplementInterfaces("GeoJsonObject")
4395+
.fileContains("List<Double> coordinates");
4396+
}
4397+
4398+
4399+
@Test
4400+
void issue_912() {
4401+
Map<String, File> files = generateFromContract("src/test/resources/3_0/java/issue_912.yaml", APACHE,
4402+
Map.of(),
4403+
codegen -> codegen.addOpenapiNormalizer("REPLACE_ONE_OF_BY_DISCRIMINATOR_MAPPING", "true")
4404+
.addInlineSchemaOption("REFACTOR_ALLOF_INLINE_SCHEMAS", "true"));
4405+
4406+
4407+
}
4408+
4409+
@Test
4410+
void issue_19261() {
4411+
Map<String, File> files = generateFromContract("src/test/resources/3_0/java/issue_19261.yaml", APACHE,
4412+
Map.of(),
4413+
codegen -> codegen.addOpenapiNormalizer("REPLACE_ONE_OF_BY_DISCRIMINATOR_MAPPING", "true")
4414+
.addInlineSchemaOption("REFACTOR_ALLOF_INLINE_SCHEMAS", "true"));
4415+
4416+
}
4417+
43584418
}

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@
6262
import static org.openapitools.codegen.TestUtils.*;
6363
import static org.openapitools.codegen.languages.AbstractJavaCodegen.GENERATE_BUILDERS;
6464
import static org.openapitools.codegen.languages.AbstractJavaCodegen.GENERATE_CONSTRUCTOR_WITH_ALL_ARGS;
65+
import static org.openapitools.codegen.languages.JavaClientCodegen.APACHE;
6566
import static org.openapitools.codegen.languages.JavaClientCodegen.USE_SPRING_BOOT4;
6667
import static org.openapitools.codegen.languages.SpringCodegen.*;
6768
import static org.openapitools.codegen.languages.features.DocumentationProviderFeatures.ANNOTATION_LIBRARY;
@@ -6695,4 +6696,21 @@ void replaceOneOfByDiscriminatorMapping(String file) throws IOException {
66956696
.fileContains("List<Double> coordinates");
66966697

66976698
}
6699+
6700+
@Test
6701+
void issue_19261() throws IOException {
6702+
Map<String, File> files = generateFromContract("src/test/resources/3_0/java/issue_19261.yaml", SPRING_BOOT,
6703+
Map.of(),
6704+
codegen -> codegen.addOpenapiNormalizer("REPLACE_ONE_OF_BY_DISCRIMINATOR_MAPPING", "true"));
6705+
6706+
}
6707+
6708+
@Test
6709+
void issue_22013() throws IOException {
6710+
Map<String, File> files = generateFromContract("src/test/resources/3_0/java/issue_22013.yaml", SPRING_BOOT,
6711+
Map.of(),
6712+
codegen -> codegen.addOpenapiNormalizer("REPLACE_ONE_OF_BY_DISCRIMINATOR_MAPPING", "true"));
6713+
6714+
assertFileContains(files.get("SomeEndponintApiController.java").toPath(),".Nullable");
6715+
}
66986716
}

modules/openapi-generator/src/test/resources/3_0/composed-oneof.yaml

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -11,34 +11,34 @@ paths:
1111
responses:
1212
'200':
1313
description: OK
14-
content:
15-
application/json:
16-
schema:
17-
oneOf:
18-
- $ref: '#/components/schemas/ObjA'
19-
- $ref: '#/components/schemas/ObjB'
20-
- $ref: '#/components/schemas/ObjD'
21-
discriminator:
22-
propertyName: realtype
23-
mapping:
24-
a-type: '#/components/schemas/ObjA'
25-
b-type: '#/components/schemas/ObjB'
26-
d-type: '#/components/schemas/ObjD'
14+
# content:
15+
# application/json:
16+
# schema:
17+
# oneOf:
18+
# - $ref: '#/components/schemas/ObjA'
19+
# - $ref: '#/components/schemas/ObjB'
20+
# - $ref: '#/components/schemas/ObjD'
21+
# discriminator:
22+
# propertyName: realtype
23+
# mapping:
24+
# a-type: '#/components/schemas/ObjA'
25+
# b-type: '#/components/schemas/ObjB'
26+
# d-type: '#/components/schemas/ObjD'
2727
post:
2828
operationId: createState
29-
requestBody:
30-
content:
31-
application/json:
32-
schema:
33-
oneOf:
34-
- $ref: '#/components/schemas/ObjA'
35-
- $ref: '#/components/schemas/ObjB'
36-
discriminator:
37-
propertyName: realtype
38-
mapping:
39-
a-type: '#/components/schemas/ObjA'
40-
b-type: '#/components/schemas/ObjB'
41-
required: true
29+
# requestBody:
30+
# content:
31+
# application/json:
32+
# schema:
33+
# oneOf:
34+
# - $ref: '#/components/schemas/ObjA'
35+
# - $ref: '#/components/schemas/ObjB'
36+
# discriminator:
37+
# propertyName: realtype
38+
# mapping:
39+
# a-type: '#/components/schemas/ObjA'
40+
# b-type: '#/components/schemas/ObjB'
41+
# required: true
4242
responses:
4343
'201':
4444
description: OK

0 commit comments

Comments
 (0)