Skip to content

Commit f195a83

Browse files
cachescrubberLars Uffmann
andauthored
[Java/Spring] all-of and one-of Improvements and Fixes (was #12075) (#12089)
* Fix Bug in OneOfImplementorAdditionalData pulling in wrong vars to one-of-implementors. Support parentVars in order to support fluent setter with inherited properties. Squashed commit of the following: commit f945c943777a1a496d7de8fc0a188842d9efb1ac Author: Lars Uffmann <[email protected]> Date: Thu Apr 7 18:22:54 2022 +0200 Polishing commit 23ce1d0ff1faff53e85ca4362f33660962aa6a92 Author: Lars Uffmann <[email protected]> Date: Thu Apr 7 17:15:28 2022 +0200 Add JavaDoc commit fee70fde5709afa67f3aabd4f48ba496df63a884 Author: Lars Uffmann <[email protected]> Date: Thu Apr 7 17:11:17 2022 +0200 Add imports for inherited Properties commit 29509aaac51750fbd33c00a57d32cac34cbcbb90 Author: Lars Uffmann <[email protected]> Date: Thu Apr 7 13:40:36 2022 +0200 Generate Samples commit 1d19d5465137d3af712f2fd3b4ae4474c58af15e Author: Lars Uffmann <[email protected]> Date: Thu Apr 7 13:21:23 2022 +0200 SpringCodegen: Support parentVars in order to support fluent setter with inherited properties. commit 2217a77bb747d0b07ef17407a6b5dd5c624a2551 Author: Lars Uffmann <[email protected]> Date: Thu Apr 7 07:18:50 2022 +0200 Add allVars to omit list in OneOfImplementorAdditionalData commit 90499a3b0a187971bfe25deb6355c3444dcf89a7 Author: Lars Uffmann <[email protected]> Date: Wed Apr 6 16:40:23 2022 +0200 Works exactly as needed for oneOf/allOf in Java/Spring commit b6d496d772e0d0a8d87a3b8cdba8fd3ca4db7f3f Author: Lars Uffmann <[email protected]> Date: Wed Apr 6 15:16:27 2022 +0200 Debug Session: identify critical codep path commit 85722360038107f15841d5acc448d93dee513a06 Author: Lars Uffmann <[email protected]> Date: Tue Apr 5 09:56:38 2022 +0200 Add test case to reproduce issue. commit 14acc5cd974bb5260f3751015558807e2eb1a8a1 Author: Lars Uffmann <[email protected]> Date: Tue Apr 5 06:57:30 2022 +0200 Add config to reproduce the issue * Adjust indentation. Co-authored-by: Lars Uffmann <[email protected]>
1 parent e12100b commit f195a83

File tree

76 files changed

+1431
-222
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

76 files changed

+1431
-222
lines changed

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -989,17 +989,17 @@ hasChildren, isMap, isDeprecated, hasOnlyReadOnly, getExternalDocumentation(), g
989989
@Override
990990
public String toString() {
991991
final StringBuilder sb = new StringBuilder("CodegenModel{");
992-
sb.append("parent='").append(parent).append('\'');
992+
sb.append("name='").append(name).append('\'');
993+
sb.append(", parent='").append(parent).append('\'');
993994
sb.append(", parentSchema='").append(parentSchema).append('\'');
994995
sb.append(", interfaces=").append(interfaces);
996+
sb.append(", interfaceModels=").append(interfaceModels!=null?interfaceModels.size():"[]");
995997
sb.append(", allParents=").append(allParents);
996998
sb.append(", parentModel=").append(parentModel);
997-
sb.append(", interfaceModels=").append(interfaceModels);
998-
sb.append(", children=").append(children);
999+
sb.append(", children=").append(children!=null?children.size():"[]");
9991000
sb.append(", anyOf=").append(anyOf);
10001001
sb.append(", oneOf=").append(oneOf);
10011002
sb.append(", allOf=").append(allOf);
1002-
sb.append(", name='").append(name).append('\'');
10031003
sb.append(", classname='").append(classname).append('\'');
10041004
sb.append(", title='").append(title).append('\'');
10051005
sb.append(", description='").append(description).append('\'');

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3193,7 +3193,7 @@ protected CodegenDiscriminator createDiscriminator(String schemaName, Schema sch
31933193
// FIXME: for now, we assume that the discriminator property is String
31943194
discriminator.setPropertyType(typeMapping.get("string"));
31953195
discriminator.setMapping(sourceDiscriminator.getMapping());
3196-
List<MappedModel> uniqueDescendants = new ArrayList();
3196+
List<MappedModel> uniqueDescendants = new ArrayList<>();
31973197
if (sourceDiscriminator.getMapping() != null && !sourceDiscriminator.getMapping().isEmpty()) {
31983198
for (Entry<String, String> e : sourceDiscriminator.getMapping().entrySet()) {
31993199
String name;

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

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,12 @@
3232
import java.util.Arrays;
3333
import java.util.EnumSet;
3434
import java.util.HashMap;
35+
import java.util.HashSet;
3536
import java.util.List;
3637
import java.util.Locale;
3738
import java.util.Map;
39+
import java.util.Objects;
40+
import java.util.Set;
3841
import java.util.regex.Matcher;
3942
import java.util.stream.Collectors;
4043
import org.apache.commons.lang3.tuple.Pair;
@@ -884,9 +887,67 @@ public CodegenModel fromModel(String name, Schema model) {
884887
codegenModel.imports.remove("ApiModelProperty");
885888
codegenModel.imports.remove("ApiModel");
886889
}
890+
887891
return codegenModel;
888892
}
889893

894+
/**
895+
* Analyse and post process all Models.
896+
* Add parentVars to every Model which has a parent. This allows to generate
897+
* fluent setter methods for inherited properties.
898+
* @param objs the models map.
899+
* @return the processed models map.
900+
*/
901+
@Override
902+
public Map<String, ModelsMap> postProcessAllModels(Map<String, ModelsMap> objs) {
903+
objs = super.postProcessAllModels(objs);
904+
objs = super.updateAllModels(objs);
905+
906+
for (ModelsMap modelsAttrs : objs.values()) {
907+
for (ModelMap mo : modelsAttrs.getModels()) {
908+
CodegenModel codegenModel = mo.getModel();
909+
Set<String> inheritedImports = new HashSet<>();
910+
Map<String, CodegenProperty> propertyHash = new HashMap<>(codegenModel.vars.size());
911+
for (final CodegenProperty property : codegenModel.vars) {
912+
propertyHash.put(property.name, property);
913+
}
914+
CodegenModel parentCodegenModel = codegenModel.parentModel;
915+
while (parentCodegenModel != null) {
916+
for (final CodegenProperty property : parentCodegenModel.vars) {
917+
// helper list of parentVars simplifies templating
918+
if (!propertyHash.containsKey(property.name)) {
919+
propertyHash.put(property.name, property);
920+
final CodegenProperty parentVar = property.clone();
921+
parentVar.isInherited = true;
922+
LOGGER.info("adding parent variable {}", property.name);
923+
codegenModel.parentVars.add(parentVar);
924+
Set<String> imports = parentVar.getImports(true).stream().filter(Objects::nonNull).collect(Collectors.toSet());
925+
for (String imp: imports) {
926+
// Avoid dupes
927+
if (!codegenModel.getImports().contains(imp)) {
928+
inheritedImports.add(imp);
929+
codegenModel.getImports().add(imp);
930+
}
931+
}
932+
}
933+
}
934+
parentCodegenModel = parentCodegenModel.getParentModel();
935+
}
936+
// There must be a better way ...
937+
for (String imp: inheritedImports) {
938+
String qimp = importMapping().get(imp);
939+
if (qimp != null) {
940+
Map<String,String> toAdd = new HashMap<>();
941+
toAdd.put("import", qimp);
942+
modelsAttrs.getImports().add(toAdd);
943+
}
944+
}
945+
}
946+
}
947+
return objs;
948+
}
949+
950+
890951
/*
891952
* Add dynamic imports based on the parameters and vendor extensions of an operation.
892953
* The imports are expanded by the mustache {{import}} tag available to model and api

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

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
11
package org.openapitools.codegen.utils;
22

3+
import java.util.ArrayList;
4+
import java.util.HashMap;
5+
import java.util.HashSet;
6+
import java.util.List;
7+
import java.util.Map;
8+
import java.util.Set;
39
import org.openapitools.codegen.CodegenConfig;
410
import org.openapitools.codegen.CodegenModel;
511
import org.openapitools.codegen.CodegenProperty;
612
import org.slf4j.Logger;
713
import org.slf4j.LoggerFactory;
814

9-
import java.util.ArrayList;
10-
import java.util.HashMap;
11-
import java.util.List;
12-
import java.util.Map;
13-
1415
/**
1516
* This class holds data to add to `oneOf` members. Let's consider this example:
1617
*
@@ -69,15 +70,19 @@ public void addFromInterfaceModel(CodegenModel cm, List<Map<String, String>> mod
6970
// Add all vars defined on cm
7071
// a "oneOf" model (cm) by default inherits all properties from its "interfaceModels",
7172
// but we only want to add properties defined on cm itself
72-
List<CodegenProperty> toAdd = new ArrayList<CodegenProperty>(cm.vars);
73+
List<CodegenProperty> toAdd = new ArrayList<>(cm.vars);
74+
7375
// note that we can't just toAdd.removeAll(m.vars) for every interfaceModel,
7476
// as they might have different value of `hasMore` and thus are not equal
75-
List<String> omitAdding = new ArrayList<String>();
77+
Set<String> omitAdding = new HashSet<>();
7678
if (cm.interfaceModels != null) {
7779
for (CodegenModel m : cm.interfaceModels) {
7880
for (CodegenProperty v : m.vars) {
7981
omitAdding.add(v.baseName);
8082
}
83+
for (CodegenProperty v : m.allVars) {
84+
omitAdding.add(v.baseName);
85+
}
8186
}
8287
}
8388
for (CodegenProperty v : toAdd) {
@@ -89,7 +94,7 @@ public void addFromInterfaceModel(CodegenModel cm, List<Map<String, String>> mod
8994
// Add all imports of cm
9095
for (Map<String, String> importMap : modelsImports) {
9196
// we're ok with shallow clone here, because imports are strings only
92-
additionalImports.add(new HashMap<String, String>(importMap));
97+
additionalImports.add(new HashMap<>(importMap));
9398
}
9499
}
95100

@@ -120,6 +125,7 @@ public void addToImplementor(CodegenConfig cc, CodegenModel implcm, List<Map<Str
120125

121126
// Add oneOf-containing models properties - we need to properly set the hasMore values to make rendering correct
122127
implcm.vars.addAll(additionalProps);
128+
implcm.hasVars = ! implcm.vars.isEmpty();
123129

124130
// Add imports
125131
for (Map<String, String> oneImport : additionalImports) {

modules/openapi-generator/src/main/resources/JavaSpring/pojo.mustache

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,29 @@ public class {{classname}}{{#parent}} extends {{{parent}}}{{/parent}}{{^parent}}
164164
}
165165
{{! end feature: getter and setter }}
166166
{{/vars}}
167+
{{#parentVars}}
168+
169+
{{! begin feature: fluent setter methods for inherited properties }}
170+
public {{classname}} {{name}}({{{datatypeWithEnum}}} {{name}}) {
171+
super.{{setter}}({{name}});
172+
return this;
173+
}
174+
{{#isArray}}
175+
176+
public {{classname}} add{{nameInCamelCase}}Item({{{items.datatypeWithEnum}}} {{name}}Item) {
177+
super.add{{nameInCamelCase}}Item({{name}}Item);
178+
return this;
179+
}
180+
{{/isArray}}
181+
{{#isMap}}
182+
183+
public {{classname}} put{{nameInCamelCase}}Item(String key, {{{items.datatypeWithEnum}}} {{name}}Item) {
184+
super.put{{nameInCamelCase}}Item({{name}}Item);
185+
return this;
186+
}
187+
{{/isMap}}
188+
{{! end feature: fluent setter methods for inherited properties }}
189+
{{/parentVars}}
167190

168191
@Override
169192
public boolean equals(Object o) {
@@ -178,23 +201,27 @@ public class {{classname}}{{#parent}} extends {{{parent}}}{{/parent}}{{^parent}}
178201
{{/-last}}{{/vars}}{{#parent}} &&
179202
super.equals(o){{/parent}};{{/hasVars}}{{^hasVars}}
180203
return true;{{/hasVars}}
181-
}{{#vendorExtensions.x-jackson-optional-nullable-helpers}}
204+
}
205+
{{#vendorExtensions.x-jackson-optional-nullable-helpers}}
182206

183207
private static <T> boolean equalsNullable(JsonNullable<T> a, JsonNullable<T> b) {
184208
return a == b || (a != null && b != null && a.isPresent() && b.isPresent() && Objects.deepEquals(a.get(), b.get()));
185-
}{{/vendorExtensions.x-jackson-optional-nullable-helpers}}
209+
}
210+
{{/vendorExtensions.x-jackson-optional-nullable-helpers}}
186211

187212
@Override
188213
public int hashCode() {
189214
return Objects.hash({{#vars}}{{#vendorExtensions.x-is-jackson-optional-nullable}}hashCodeNullable({{name}}){{/vendorExtensions.x-is-jackson-optional-nullable}}{{^vendorExtensions.x-is-jackson-optional-nullable}}{{^isByteArray}}{{name}}{{/isByteArray}}{{#isByteArray}}Arrays.hashCode({{name}}){{/isByteArray}}{{/vendorExtensions.x-is-jackson-optional-nullable}}{{^-last}}, {{/-last}}{{/vars}}{{#parent}}{{#hasVars}}, {{/hasVars}}super.hashCode(){{/parent}});
190-
}{{#vendorExtensions.x-jackson-optional-nullable-helpers}}
215+
}
216+
{{#vendorExtensions.x-jackson-optional-nullable-helpers}}
191217

192218
private static <T> int hashCodeNullable(JsonNullable<T> a) {
193219
if (a == null) {
194220
return 1;
195221
}
196222
return a.isPresent() ? Arrays.deepHashCode(new Object[]{a.get()}) : 31;
197-
}{{/vendorExtensions.x-jackson-optional-nullable-helpers}}
223+
}
224+
{{/vendorExtensions.x-jackson-optional-nullable-helpers}}
198225

199226
@Override
200227
public String toString() {

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

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -987,6 +987,49 @@ public void oneOf_5381() throws IOException {
987987
assertFileContains(Paths.get(outputPath + "/src/main/java/org/openapitools/model/FooRefOrValue.java"), "public interface FooRefOrValue");
988988
}
989989

990+
@Test
991+
public void oneOf_allOf() throws IOException {
992+
File output = Files.createTempDirectory("test").toFile().getCanonicalFile();
993+
output.deleteOnExit();
994+
String outputPath = output.getAbsolutePath().replace('\\', '/');
995+
OpenAPI openAPI = new OpenAPIParser()
996+
.readLocation("src/test/resources/3_0/oneof_polymorphism_and_inheritance.yaml", null, new ParseOptions()).getOpenAPI();
997+
998+
SpringCodegen codegen = new SpringCodegen();
999+
codegen.setOutputDir(output.getAbsolutePath());
1000+
codegen.additionalProperties().put(CXFServerFeatures.LOAD_TEST_DATA_FROM_FILE, "true");
1001+
codegen.setUseOneOfInterfaces(true);
1002+
1003+
ClientOptInput input = new ClientOptInput();
1004+
input.openAPI(openAPI);
1005+
input.config(codegen);
1006+
1007+
DefaultGenerator generator = new DefaultGenerator();
1008+
codegen.setHateoas(true);
1009+
generator.setGeneratorPropertyDefault(CodegenConstants.MODELS, "true");
1010+
// generator.setGeneratorPropertyDefault(CodegenConstants.USE_ONEOF_DISCRIMINATOR_LOOKUP, "true");
1011+
generator.setGeneratorPropertyDefault(CodegenConstants.LEGACY_DISCRIMINATOR_BEHAVIOR, "false");
1012+
1013+
codegen.setUseOneOfInterfaces(true);
1014+
codegen.setLegacyDiscriminatorBehavior(false);
1015+
1016+
generator.setGeneratorPropertyDefault(CodegenConstants.MODEL_TESTS, "false");
1017+
generator.setGeneratorPropertyDefault(CodegenConstants.MODEL_DOCS, "false");
1018+
generator.setGeneratorPropertyDefault(CodegenConstants.APIS, "true");
1019+
generator.setGeneratorPropertyDefault(CodegenConstants.SUPPORTING_FILES, "false");
1020+
1021+
generator.opts(input).generate();
1022+
1023+
assertFileContains(Paths.get(outputPath + "/src/main/java/org/openapitools/model/Foo.java"), "public class Foo extends Entity implements FooRefOrValue");
1024+
assertFileContains(Paths.get(outputPath + "/src/main/java/org/openapitools/model/FooRef.java"), "public class FooRef extends EntityRef implements FooRefOrValue");
1025+
assertFileContains(Paths.get(outputPath + "/src/main/java/org/openapitools/model/FooRefOrValue.java"), "public interface FooRefOrValue");
1026+
// previous bugs
1027+
assertFileNotContains(Paths.get(outputPath + "/src/main/java/org/openapitools/model/BarRef.java"), "atTypesuper.hashCode");
1028+
assertFileNotContains(Paths.get(outputPath + "/src/main/java/org/openapitools/model/BarRef.java"), "private String atBaseType");
1029+
// imports for inherited properties
1030+
assertFileContains(Paths.get(outputPath + "/src/main/java/org/openapitools/model/PizzaSpeziale.java"), "import java.math.BigDecimal");
1031+
}
1032+
9901033
@Test
9911034
public void testTypeMappings() {
9921035
final SpringCodegen codegen = new SpringCodegen();

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

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,13 +116,15 @@ components:
116116
- $ref: '#/components/schemas/Entity'
117117
FooRef:
118118
type: object
119-
discriminator:
120-
propertyName: '@type'
121119
properties:
122120
foorefPropA:
123121
type: string
124122
allOf:
125123
- $ref: '#/components/schemas/EntityRef'
124+
BarRef:
125+
type: object
126+
allOf:
127+
- $ref: '#/components/schemas/EntityRef'
126128
Bar_Create:
127129
type: object
128130
properties:
@@ -149,6 +151,32 @@ components:
149151
$ref: '#/components/schemas/FooRefOrValue'
150152
allOf:
151153
- $ref: '#/components/schemas/Entity'
154+
BarRefOrValue:
155+
type: object
156+
oneOf:
157+
- $ref: "#/components/schemas/Bar"
158+
- $ref: "#/components/schemas/BarRef"
159+
Pizza:
160+
type: object
161+
properties:
162+
pizzaSize:
163+
type: number
164+
allOf:
165+
- $ref: '#/components/schemas/Entity'
166+
Pasta:
167+
type: object
168+
properties:
169+
vendor:
170+
type: string
171+
allOf:
172+
- $ref: '#/components/schemas/Entity'
173+
PizzaSpeziale:
174+
type: object
175+
properties:
176+
toppings:
177+
type: string
178+
allOf:
179+
- $ref: '#/components/schemas/Pizza'
152180

153181
requestBodies:
154182
Foo:

samples/openapi3/client/petstore/spring-cloud-oas3-fakeapi/src/main/java/org/openapitools/model/BigCat.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,21 @@ public void setKind(KindEnum kind) {
8989
this.kind = kind;
9090
}
9191

92+
public BigCat declawed(Boolean declawed) {
93+
super.setDeclawed(declawed);
94+
return this;
95+
}
96+
97+
public BigCat className(String className) {
98+
super.setClassName(className);
99+
return this;
100+
}
101+
102+
public BigCat color(String color) {
103+
super.setColor(color);
104+
return this;
105+
}
106+
92107
@Override
93108
public boolean equals(Object o) {
94109
if (this == o) {

samples/openapi3/client/petstore/spring-cloud-oas3-fakeapi/src/main/java/org/openapitools/model/Cat.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,16 @@ public void setDeclawed(Boolean declawed) {
5858
this.declawed = declawed;
5959
}
6060

61+
public Cat className(String className) {
62+
super.setClassName(className);
63+
return this;
64+
}
65+
66+
public Cat color(String color) {
67+
super.setColor(color);
68+
return this;
69+
}
70+
6171
@Override
6272
public boolean equals(Object o) {
6373
if (this == o) {

samples/openapi3/client/petstore/spring-cloud-oas3-fakeapi/src/main/java/org/openapitools/model/Dog.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,16 @@ public void setBreed(String breed) {
4949
this.breed = breed;
5050
}
5151

52+
public Dog className(String className) {
53+
super.setClassName(className);
54+
return this;
55+
}
56+
57+
public Dog color(String color) {
58+
super.setColor(color);
59+
return this;
60+
}
61+
5262
@Override
5363
public boolean equals(Object o) {
5464
if (this == o) {

0 commit comments

Comments
 (0)