Skip to content

Commit f17b647

Browse files
committed
add implementation and unit test for schemaImplements
1 parent 681aabf commit f17b647

3 files changed

Lines changed: 73 additions & 6 deletions

File tree

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

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7153,6 +7153,26 @@ public Map<String, String> getPropertyAsStringMap(String propertyKey) {
71537153
return Collections.emptyMap();
71547154
}
71557155

7156+
public Map<String, List<String>> getPropertyAsStringListMap(String propertyKey) {
7157+
final Object value = additionalProperties.get(propertyKey);
7158+
if (value instanceof Map) {
7159+
Map<?, ?> rawMap = (Map<?, ?>) value;
7160+
Map<String, List<String>> stringMap = new HashMap<>();
7161+
for (Map.Entry<?, ?> entry : rawMap.entrySet()) {
7162+
Object entryValue = entry.getValue();
7163+
if (entryValue instanceof List) {
7164+
List<String> stringList = ((List<?>) entryValue).stream()
7165+
.map(String::valueOf)
7166+
.collect(Collectors.toList());
7167+
stringMap.put(String.valueOf(entry.getKey()), stringList);
7168+
} else {
7169+
stringMap.put(String.valueOf(entry.getKey()), Collections.singletonList(String.valueOf(entryValue)));
7170+
}
7171+
}
7172+
return stringMap;
7173+
}
7174+
return Collections.emptyMap();
7175+
}
71567176

71577177
public boolean convertPropertyToBoolean(String propertyKey) {
71587178
final Object booleanValue = additionalProperties.get(propertyKey);

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

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ public abstract class AbstractJavaCodegen extends DefaultCodegen implements Code
9494
public static final String ADDITIONAL_MODEL_TYPE_ANNOTATIONS = "additionalModelTypeAnnotations";
9595
public static final String X_IMPLEMENTS_OVERRIDES = "xImplementsOverrides";
9696
private static final String X_IMPLEMENTS_OVERRIDES_SKIP = "skip";
97+
public static final String SCHEMA_IMPLEMENTS = "schemaImplements";
9798
public static final String ADDITIONAL_ONE_OF_TYPE_ANNOTATIONS = "additionalOneOfTypeAnnotations";
9899
public static final String ADDITIONAL_ENUM_TYPE_ANNOTATIONS = "additionalEnumTypeAnnotations";
99100
public static final String DISCRIMINATOR_CASE_SENSITIVE = "discriminatorCaseSensitive";
@@ -185,6 +186,9 @@ protected enum ENUM_PROPERTY_NAMING_TYPE {MACRO_CASE, legacy, original}
185186
@Getter
186187
@Setter
187188
protected Map<String, String> xImplementsOverrides = new HashMap<>();
189+
@Getter
190+
@Setter
191+
protected Map<String, List<String>> schemaImplements = new HashMap<>();
188192
protected Map<String, Boolean> lombokAnnotations = null;
189193
@Getter @Setter
190194
protected List<String> additionalOneOfTypeAnnotations = new LinkedList<>();
@@ -459,6 +463,9 @@ public void processOpts() {
459463
if (additionalProperties.containsKey(X_IMPLEMENTS_OVERRIDES)) {
460464
this.setXImplementsOverrides(getPropertyAsStringMap(X_IMPLEMENTS_OVERRIDES));
461465
}
466+
if (additionalProperties.containsKey(SCHEMA_IMPLEMENTS)) {
467+
this.setSchemaImplements(getPropertyAsStringListMap(SCHEMA_IMPLEMENTS));
468+
}
462469

463470
if (additionalProperties.containsKey(CodegenConstants.INVOKER_PACKAGE)) {
464471
this.setInvokerPackage((String) additionalProperties.get(CodegenConstants.INVOKER_PACKAGE));
@@ -2029,10 +2036,10 @@ public ModelsMap postProcessModels(ModelsMap objs) {
20292036
.filter(entry -> xImplementsInModelOriginal.contains(entry.getKey()))
20302037
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
20312038
if (!xImplementsInModelSkipped.isEmpty()) {
2032-
LOGGER.info("Following interfaces will be skipped for model {}: {}", cm.classname, xImplementsInModelSkipped);
2039+
LOGGER.info("Following interfaces configured via additional property '{}' will be skipped for model {}: {}", cm.classname, X_IMPLEMENTS_OVERRIDES, xImplementsInModelSkipped);
20332040
}
20342041
if (!xImplementsInModelSubstituteFromTo.isEmpty()) {
2035-
LOGGER.info("Following interfaces will be replaced for model {}: {}", cm.classname, xImplementsInModelSubstituteFromTo.entrySet().stream()
2042+
LOGGER.info("Following interfaces configured via additional property '{}' will be replaced for model {}: {}", cm.classname, X_IMPLEMENTS_OVERRIDES, xImplementsInModelSubstituteFromTo.entrySet().stream()
20362043
.map(entry -> " from [" + entry.getKey() + "] to [" + entry.getValue() + "]")
20372044
.collect(Collectors.joining(",")));
20382045
}
@@ -2045,6 +2052,22 @@ public ModelsMap postProcessModels(ModelsMap objs) {
20452052
}
20462053
}
20472054
}
2055+
// add interfaces defined outside of open api spec
2056+
if (!this.schemaImplements.isEmpty()) {
2057+
for (ModelMap mo : objs.getModels()) {
2058+
CodegenModel cm = mo.getModel();
2059+
if (this.schemaImplements.containsKey(cm.getSchemaName())) {
2060+
LOGGER.info("Adding interface(s) {} configured via additional property '{}' to model {}", this.schemaImplements.get(cm.getSchemaName()), SCHEMA_IMPLEMENTS, cm.classname);
2061+
cm.getVendorExtensions().putIfAbsent(X_IMPLEMENTS, new ArrayList<String>());
2062+
List<String> xImplementsInModel = (List<String>) cm.getVendorExtensions().get(X_IMPLEMENTS);
2063+
List<String> schemaImplements = this.schemaImplements.get(cm.getSchemaName());
2064+
List<String> combinedSchemaImplements = Stream.concat(xImplementsInModel.stream(), schemaImplements.stream())
2065+
.collect(Collectors.toList());
2066+
// Add all the interfaces combined
2067+
cm.getVendorExtensions().replace(X_IMPLEMENTS, combinedSchemaImplements);
2068+
}
2069+
}
2070+
}
20482071

20492072
// add x-implements for serializable to all models
20502073
for (ModelMap mo : objs.getModels()) {

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

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -834,12 +834,36 @@ public void testXImplements() throws IOException {
834834
public void testXImplementsOverrides() throws IOException {
835835
final SpringCodegen codegen = new SpringCodegen();
836836

837-
codegen.additionalProperties().put(X_IMPLEMENTS_OVERRIDES, Map.of("com.custompackage.InterfaceToSubstitute", "com.custompackage.SubstitutedInterface", "com.custompackage.InterfaceToSkip", "skip"));
837+
String interfaceToSubstitute = "com.custompackage.InterfaceToSubstitute";
838+
String substitutedInterface = "com.custompackage.SubstitutedInterface";
839+
String interfaceToSkip = "com.custompackage.InterfaceToSkip";
840+
String skipKeyword = "skip";
841+
codegen.additionalProperties().put(X_IMPLEMENTS_OVERRIDES, Map.of(interfaceToSubstitute, substitutedInterface, interfaceToSkip, skipKeyword));
838842

839843
final Map<String, File> files = generateFiles(codegen, "src/test/resources/3_0/spring/petstore-with-fake-endpoints-models-for-testing-x-implements.yaml");
840844
JavaFileAssert.assertThat(files.get("Animal.java"))
841-
.implementsInterfaces("com.custompackage.InterfaceToKeep", "com.custompackage.SubstitutedInterface")
842-
.doesNotImplementInterfaces("com.custompackage.InterfaceToSubstitute", "com.custompackage.InterfaceToSkip");
845+
.implementsInterfaces("com.custompackage.InterfaceToKeep", substitutedInterface)
846+
.doesNotImplementInterfaces(interfaceToSubstitute, interfaceToSkip);
847+
}
848+
849+
@Test
850+
public void testSchemaImplements() throws IOException {
851+
final SpringCodegen codegen = new SpringCodegen();
852+
853+
String fooInterface = "com.custompackage.FooInterface";
854+
String fooAnotherInterface = "com.custompackage.FooAnotherInterface";
855+
String anotherInterface = "com.custompackage.AnimalAnotherInterface";
856+
codegen.additionalProperties().put(SCHEMA_IMPLEMENTS, Map.of(
857+
"Foo", List.of(fooInterface, fooAnotherInterface), /* add multiple interfaces (as list) */
858+
"Animal", anotherInterface)); /* add just one interface */
859+
860+
final Map<String, File> files = generateFiles(codegen, "src/test/resources/3_0/spring/petstore-with-fake-endpoints-models-for-testing-x-implements.yaml");
861+
JavaFileAssert.assertThat(files.get("Animal.java"))
862+
.implementsInterfaces(anotherInterface, "com.custompackage.InterfaceToSubstitute", "com.custompackage.InterfaceToKeep", "com.custompackage.InterfaceToSkip")
863+
.doesNotImplementInterfaces("com.custompackage.SubstitutedInterface");
864+
865+
JavaFileAssert.assertThat(files.get("Foo.java"))
866+
.implementsInterfaces(fooInterface, fooAnotherInterface);
843867
}
844868

845869
@Test
@@ -1147,7 +1171,7 @@ private Map<String, File> generateFiles(SpringCodegen codegen, String filePath)
11471171
generator.setGenerateMetadata(false); // skip metadata generation
11481172
List<File> files = generator.opts(input).generate();
11491173

1150-
return files.stream().collect(Collectors.toMap(e -> e.getName().replace(outputPath, ""), i -> i));
1174+
return files.stream().sorted().collect(Collectors.toMap(e -> e.getName().replace(outputPath, ""), i -> i));
11511175
}
11521176

11531177
/*

0 commit comments

Comments
 (0)