Skip to content

Commit

Permalink
Merge pull request OpenAPITools#3 from Thecrazyskull/master
Browse files Browse the repository at this point in the history
Add support for multi level inheritance
  • Loading branch information
dzolnai committed Sep 17, 2020
2 parents 05c1080 + 1120c81 commit 4bea7ae
Show file tree
Hide file tree
Showing 6 changed files with 48 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ public class CodegenProperty implements Cloneable, IJsonSchemaValidationProperti
public Map<String, Object> vendorExtensions = new HashMap<String, Object>();
public boolean hasValidation; // true if pattern, maximum, etc are set (only used in the mustache template)
public boolean isInherited;
public String parentClassName;
public String discriminatorValue;
public String nameInLowerCase; // property name in lower case
public String nameInCamelCase; // property name in camel case
Expand Down Expand Up @@ -674,6 +675,7 @@ public String toString() {
sb.append(", vendorExtensions=").append(vendorExtensions);
sb.append(", hasValidation=").append(hasValidation);
sb.append(", isInherited=").append(isInherited);
sb.append(", parentClassName=").append(parentClassName).append('\'');
sb.append(", discriminatorValue='").append(discriminatorValue).append('\'');
sb.append(", nameInCamelCase='").append(nameInCamelCase).append('\'');
sb.append(", nameInSnakeCase='").append(nameInSnakeCase).append('\'');
Expand Down Expand Up @@ -768,6 +770,7 @@ public boolean equals(Object o) {
Objects.equals(items, that.items) &&
Objects.equals(mostInnerItems, that.mostInnerItems) &&
Objects.equals(vendorExtensions, that.vendorExtensions) &&
Objects.equals(parentClassName, that.parentClassName) &&
Objects.equals(discriminatorValue, that.discriminatorValue) &&
Objects.equals(nameInCamelCase, that.nameInCamelCase) &&
Objects.equals(nameInSnakeCase, that.nameInSnakeCase) &&
Expand All @@ -793,7 +796,7 @@ public int hashCode() {
isBoolean, isDate, isDateTime, isUuid, isUri, isEmail, isFreeFormObject,
isListContainer, isMapContainer, isEnum, isReadOnly, isWriteOnly, isNullable,
isSelfReference, isCircularReference, isDiscriminator, _enum, allowableValues, items, mostInnerItems,
vendorExtensions, hasValidation, isInherited, discriminatorValue, nameInCamelCase,
vendorExtensions, hasValidation, isInherited, parentClassName, discriminatorValue, nameInCamelCase,
nameInSnakeCase, enumName, maxItems, minItems, isXmlAttribute, xmlPrefix, xmlName,
xmlNamespace, isXmlWrapped);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,16 @@ apiTemplateFiles are for API outputs only (controllers/handlers).
* keyword. For example, the Java code generator may generate 'extends HashMap'.
*/
protected boolean supportsInheritance;
/**
* True if the code generator requires a discriminator for proper multi-level inheritance.
* This is used for models which have multi-level inheritance where the discriminator uses a
* different type compared to the regular model type.
* For example, the Kotlin code generator uses "interface" for discriminator types and a
* "data class" for regular models. "data class" does not support inheritance
* which is why we need a discriminator to properly transform the "data class" to an interface.
*/
protected boolean requiresDiscriminatorInheritance;

/**
* True if the language generator supports the 'additionalProperties' keyword
* as sibling of a composed (allOf/anyOf/oneOf) schema.
Expand Down Expand Up @@ -499,6 +509,7 @@ public Map<String, Object> updateAllModels(Map<String, Object> objs) {
for (String name : allModels.keySet()) {
CodegenModel cm = allModels.get(name);
CodegenModel parent = allModels.get(cm.getParent());

// if a discriminator exists on the parent, don't add this child to the inheritance hierarchy
// TODO Determine what to do if the parent discriminator name == the grandparent discriminator name
while (parent != null) {
Expand All @@ -509,6 +520,15 @@ public Map<String, Object> updateAllModels(Map<String, Object> objs) {
parent.hasChildren = true;
Schema parentSchema = this.openAPI.getComponents().getSchemas().get(parent.name);
if (parentSchema.getDiscriminator() == null) {
// If the parent has a grandparent, it is a child itself.
// Since it's also a parent, it needs to have some sort of discriminator for proper inheritance.
// That's why we copy the grandparent's discriminator when the parent has no discriminator
CodegenModel grandParent = allModels.get(parent.getParent());
if (requiresDiscriminatorInheritance && grandParent != null
&& parent.getDiscriminator() == null) {
parent.setDiscriminator(grandParent.getDiscriminator());
}

parent = allModels.get(parent.getParent());
} else {
parent = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ public enum SERIALIZATION_LIBRARY_TYPE {moshi, gson, jackson}
public AbstractKotlinCodegen() {
super();
supportsInheritance = true;
requiresDiscriminatorInheritance = true;
setSortModelPropertiesByRequiredFlag(true);

languageSpecificPrimitives = new HashSet<String>(Arrays.asList(
Expand Down Expand Up @@ -765,8 +766,16 @@ private CodegenModel reconcileProperties(CodegenModel codegenModel,
// Because the only way we have inheritance is with discriminator interface, in
// which it is mandatory to implement and override all parent properties.


// Get the properties for the parent and child models
final List<CodegenProperty> parentModelCodegenProperties = parentCodegenModel.vars;
final List<CodegenProperty> parentModelCodegenProperties = parentCodegenModel.allVars;

// This list includes the grandparent properties, if applicable
final List<CodegenProperty> grandParentModelCodegenProperties = new ArrayList<CodegenProperty>();
grandParentModelCodegenProperties.addAll(parentModelCodegenProperties);
grandParentModelCodegenProperties
.removeAll(parentCodegenModel.vars);

List<CodegenProperty> codegenProperties = codegenModel.vars;
codegenModel.allVars = new ArrayList<CodegenProperty>(codegenProperties);
codegenModel.parentVars = parentCodegenModel.allVars;
Expand All @@ -785,6 +794,16 @@ private CodegenModel reconcileProperties(CodegenModel codegenModel,
// so mark it as inherited & copy the data type.
codegenProperty.isInherited = true;
codegenProperty.isEnum = parentModelCodegenProperty.isEnum;

// Save the parent class name if the properties match since we use it for the enum definition
if (grandParentModelCodegenProperties
.stream()
.map(CodegenProperty::getBaseName)
.collect(Collectors.toSet())
.contains(codegenProperty.baseName)) {
codegenProperty.parentClassName = parentCodegenModel.parent;
}

codegenProperty.baseType = parentModelCodegenProperty.baseType;
codegenProperty.dataType = parentModelCodegenProperty.dataType;
codegenProperty.datatypeWithEnum = parentModelCodegenProperty.datatypeWithEnum;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,4 @@
{{#deprecated}}
@Deprecated(message = "This property is deprecated.")
{{/deprecated}}
{{#multiplatform}}@SerialName(value = "{{{vendorExtensions.x-base-name-literal}}}") @Required {{/multiplatform}}{{#isInherited}}override {{/isInherited}}{{>modelMutable}} {{{name}}}: {{#isEnum}}{{#isListContainer}}{{#isList}}kotlin.collections.List{{/isList}}{{^isList}}kotlin.Array{{/isList}}<{{#isInherited}}{{{parent}}}{{/isInherited}}{{^isInherited}}{{classname}}{{/isInherited}}.{{{nameInCamelCase}}}>{{/isListContainer}}{{^isListContainer}}{{#isInherited}}{{{parent}}}{{/isInherited}}{{^isInherited}}{{classname}}{{/isInherited}}.{{{nameInCamelCase}}}{{/isListContainer}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}{{#isNullable}}?{{/isNullable}}
{{#multiplatform}}@SerialName(value = "{{{vendorExtensions.x-base-name-literal}}}") @Required {{/multiplatform}}{{#isInherited}}override {{/isInherited}}{{>modelMutable}} {{{name}}}: {{#isEnum}}{{#isListContainer}}{{#isList}}kotlin.collections.List{{/isList}}{{^isList}}kotlin.Array{{/isList}}<{{#isInherited}}{{{parent}}}{{/isInherited}}{{^isInherited}}{{classname}}{{/isInherited}}.{{{nameInCamelCase}}}>{{/isListContainer}}{{^isListContainer}}{{#isInherited}}{{#parentClassName}}{{{parentClassName}}}{{/parentClassName}}{{^parentClassName}}{{{parent}}}{{/parentClassName}}{{/isInherited}}{{^isInherited}}{{classname}}{{/isInherited}}.{{{nameInCamelCase}}}{{/isListContainer}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}{{#isNullable}}?{{/isNullable}}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import kotlinx.serialization.internal.StringDescriptor
httpClientEngine: HttpClientEngine? = null,
json: Json = Json {
ignoreUnknownKeys = true
isLenient = true
}
) : this(baseUrl, httpClientEngine, KotlinxSerializer(json))

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import io.ktor.client.engine.HttpClientEngine
import io.ktor.client.features.json.JsonFeature
import io.ktor.client.features.json.JsonSerializer
import io.ktor.client.features.json.serializer.KotlinxSerializer
import io.ktor.client.features.logging.*
import io.ktor.client.request.accept
import io.ktor.client.request.forms.FormDataContent
import io.ktor.client.request.forms.MultiPartFormDataContent
Expand Down Expand Up @@ -35,6 +34,7 @@ import {{packageName}}.auth.*
httpClientEngine: HttpClientEngine?,
json: Json = Json {
ignoreUnknownKeys = true
isLenient = true
}
) : this(baseUrl, httpClientEngine, KotlinxSerializer(json))

Expand All @@ -44,14 +44,7 @@ import {{packageName}}.auth.*

private val client: HttpClient by lazy {
val jsonConfig: JsonFeature.Config.() -> Unit = { this.serializer = this@ApiClient.serializer }
val loggingConfig: Logging.Config.() -> Unit = {
this.logger = Logger.SIMPLE
this.level = LogLevel.ALL
}
val clientConfig: (HttpClientConfig<*>) -> Unit = {
it.install(JsonFeature, jsonConfig)
it.install(Logging, loggingConfig)
}
val clientConfig: (HttpClientConfig<*>) -> Unit = { it.install(JsonFeature, jsonConfig) }
httpClientEngine?.let { HttpClient(it, clientConfig) } ?: HttpClient(clientConfig)
}
{{#hasAuthMethods}}
Expand Down

0 comments on commit 4bea7ae

Please sign in to comment.