[elm] Major refactoring; add discriminator support (#1104)

* Use the same name for all exposed encoder & decoders;
* Improve imports (limit exposures);
* Add support for OAS3 discriminators;
* Distinct between 0.18 & latest mustache files.
This commit is contained in:
Erik Timmers
2018-09-29 12:13:04 +02:00
committed by GitHub
parent cbddb08468
commit 345b7ec7e5
38 changed files with 315 additions and 354 deletions

View File

@@ -53,6 +53,7 @@ import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.stream.Collectors;
public class ElmClientCodegen extends DefaultCodegen implements CodegenConfig {
private static final Logger LOGGER = LoggerFactory.getLogger(ElmClientCodegen.class);
@@ -62,7 +63,7 @@ public class ElmClientCodegen extends DefaultCodegen implements CodegenConfig {
private static final String ELM_VERSION = "elmVersion";
private static final String ENCODER = "elmEncoder";
private static final String DECODER = "elmDecoder";
private static final String X_DISCRIMINATOR_TYPE = "x-discriminator-value";
private static final String DISCRIMINATOR_NAME = "discriminatorName";
private static final String UNION_TYPE = "elmUnionType";
protected String packageName = "openapi";
@@ -187,10 +188,18 @@ public class ElmClientCodegen extends DefaultCodegen implements CodegenConfig {
case ELM_018:
LOGGER.info("Elm version = 0.18");
additionalProperties.put("isElm018", true);
supportingFiles.add(new SupportingFile("DateOnly018.mustache", "src", "DateOnly.elm"));
supportingFiles.add(new SupportingFile("DateTime018.mustache", "src", "DateTime.elm"));
supportingFiles.add(new SupportingFile("elm-package018.mustache", "", "elm-package.json"));
supportingFiles.add(new SupportingFile("Main018.mustache", "src", "Main.elm"));
break;
case ELM_019:
LOGGER.info("Elm version = 0.19");
additionalProperties.put("isElm019", true);
supportingFiles.add(new SupportingFile("DateOnly.mustache", "src", "DateOnly.elm"));
supportingFiles.add(new SupportingFile("DateTime.mustache", "src", "DateTime.elm"));
supportingFiles.add(new SupportingFile("elm.mustache", "", "elm.json"));
supportingFiles.add(new SupportingFile("Main.mustache", "src", "Main.elm"));
break;
default:
throw new RuntimeException("Undefined Elm version");
@@ -199,19 +208,6 @@ public class ElmClientCodegen extends DefaultCodegen implements CodegenConfig {
supportingFiles.add(new SupportingFile("Byte.mustache", "src", "Byte.elm"));
supportingFiles.add(new SupportingFile("gitignore.mustache", "", ".gitignore"));
supportingFiles.add(new SupportingFile("README.mustache", "", "README.md"));
if (ElmVersion.ELM_018.equals(elmVersion)) {
supportingFiles.add(new SupportingFile("DateOnly018.mustache", "src", "DateOnly.elm"));
supportingFiles.add(new SupportingFile("DateTime018.mustache", "src", "DateTime.elm"));
supportingFiles.add(new SupportingFile("elm-package.mustache", "", "elm-package.json"));
supportingFiles.add(new SupportingFile("Main018.mustache", "src", "Main.elm"));
}
if (ElmVersion.ELM_019.equals(elmVersion)) {
supportingFiles.add(new SupportingFile("DateOnly019.mustache", "src", "DateOnly.elm"));
supportingFiles.add(new SupportingFile("DateTime019.mustache", "src", "DateTime.elm"));
supportingFiles.add(new SupportingFile("elm.mustache", "", "elm.json"));
supportingFiles.add(new SupportingFile("Main019.mustache", "src", "Main.elm"));
}
}
@Override
@@ -339,55 +335,40 @@ public class ElmClientCodegen extends DefaultCodegen implements CodegenConfig {
for (Map<String, Object> mo : models) {
CodegenModel cm = (CodegenModel) mo.get("model");
if (cm.isEnum) {
this.addEncoderAndDecoder(cm.vendorExtensions, cm.classname, false, false);
addEncoderAndDecoder(cm.vendorExtensions, cm.classname, DataTypeExposure.EXPOSED);
cm.vendorExtensions.put(UNION_TYPE, cm.classname);
} else if (cm.isAlias) {
this.addEncoderAndDecoder(cm.vendorExtensions, cm.dataType, false, true);
addEncoderAndDecoder(cm.vendorExtensions, cm.dataType, DataTypeExposure.EXPOSED);
}
List<ElmImport> elmImports = new ArrayList<>();
for (CodegenProperty property : cm.allVars) {
if (property.complexType != null) {
elmImports.add(createPropertyImport(property));
final ElmImport elmImport = createImport(property.complexType);
elmImports.add(elmImport);
}
}
if (cm.isArrayModel) {
if (cm.arrayModelType != null) {
// add type imports
final ElmImport elmImport = new ElmImport();
final String modulePrefix = customPrimitives.contains(cm.arrayModelType) ? "" : "Data.";
elmImport.moduleName = modulePrefix + cm.arrayModelType;
elmImport.exposures = new TreeSet<>();
elmImport.exposures.add(cm.arrayModelType);
elmImport.exposures.add(org.openapitools.codegen.utils.StringUtils.camelize(cm.arrayModelType, true) + "Decoder");
elmImport.exposures.add(org.openapitools.codegen.utils.StringUtils.camelize(cm.arrayModelType, true) + "Encoder");
elmImport.hasExposures = true;
final ElmImport elmImport = createImport(cm.arrayModelType);
elmImports.add(elmImport);
}
}
if (cm.discriminator != null) {
for (CodegenModel child : cm.children) {
// add child imports
final ElmImport elmImport = new ElmImport();
final String modulePrefix = customPrimitives.contains(child.classname) ? "" : "Data.";
elmImport.moduleName = modulePrefix + child.classname;
elmImport.exposures = new TreeSet<>();
elmImport.exposures.add(child.classname);
elmImport.exposures.add(child.classVarName + "Decoder");
elmImport.exposures.add(child.classVarName + "Encoder");
elmImport.hasExposures = true;
final ElmImport elmImport = createImport(child.classname);
elmImports.add(elmImport);
// set discriminator value to all children (recursively)
this.setDiscriminatorValue(child, cm.getDiscriminatorName(), this.getDiscriminatorValue(child));
final String propertyName = cm.discriminator.getPropertyName();
final List<CodegenProperty> allVars = child.allVars.stream()
.filter(var -> !var.baseName.equals(propertyName))
.collect(Collectors.toList());
child.allVars.clear();
child.allVars.addAll(allVars);
// add all non-discriminator vars
int index = 0;
for (CodegenProperty property : cm.vars) {
if (!cm.discriminator.equals(property.baseName)) {
child.vars.add(index++, property);
}
}
child.vendorExtensions.put(DISCRIMINATOR_NAME, propertyName);
}
}
inner.put("elmImports", elmImports);
@@ -396,42 +377,16 @@ public class ElmClientCodegen extends DefaultCodegen implements CodegenConfig {
return objs;
}
private void setDiscriminatorValue(CodegenModel model, String baseName, String value) {
for (CodegenProperty prop : model.vars) {
if (prop.baseName.equals(baseName)) {
prop.discriminatorValue = value;
}
}
for (CodegenProperty prop : model.allVars) {
if (prop.baseName.equals(baseName)) {
prop.discriminatorValue = value;
}
}
if (model.children != null) {
final boolean newDiscriminator = model.discriminator != null;
for (CodegenModel child : model.children) {
this.setDiscriminatorValue(child, baseName, newDiscriminator ? value : this.getDiscriminatorValue(child));
}
}
}
private String getDiscriminatorValue(CodegenModel model) {
return model.vendorExtensions.containsKey(X_DISCRIMINATOR_TYPE) ?
(String) model.vendorExtensions.get(X_DISCRIMINATOR_TYPE) : model.classname;
}
private ElmImport createPropertyImport(final CodegenProperty property) {
private ElmImport createImport(final String name) {
final ElmImport elmImport = new ElmImport();
final String modulePrefix = customPrimitives.contains(property.complexType) ? "" : "Data.";
elmImport.moduleName = modulePrefix + property.complexType;
final boolean isData = !customPrimitives.contains(name);
final String modulePrefix = isData ? "Data." : "";
elmImport.moduleName = modulePrefix + name;
if (isData) {
elmImport.as = name;
}
elmImport.exposures = new TreeSet<>();
elmImport.exposures.add(property.complexType);
if (property.vendorExtensions.containsKey(DECODER)) {
elmImport.exposures.add((String) property.vendorExtensions.get(DECODER));
}
if (property.vendorExtensions.containsKey(ENCODER)) {
elmImport.exposures.add((String) property.vendorExtensions.get(ENCODER));
}
elmImport.exposures.add(name);
elmImport.hasExposures = true;
return elmImport;
}
@@ -463,7 +418,6 @@ public class ElmClientCodegen extends DefaultCodegen implements CodegenConfig {
if (!dependencies.containsKey(op.bodyParam.dataType)) {
dependencies.put(op.bodyParam.dataType, new TreeSet<String>());
}
dependencies.get(op.bodyParam.dataType).add(encoder);
}
}
for (CodegenResponse resp : op.responses) {
@@ -475,7 +429,6 @@ public class ElmClientCodegen extends DefaultCodegen implements CodegenConfig {
if (!dependencies.containsKey(resp.dataType)) {
dependencies.put(resp.dataType, new TreeSet<String>());
}
dependencies.get(resp.dataType).add(decoder);
}
}
}
@@ -485,6 +438,7 @@ public class ElmClientCodegen extends DefaultCodegen implements CodegenConfig {
final ElmImport elmImport = new ElmImport();
final String key = entry.getKey();
elmImport.moduleName = "Data." + key;
elmImport.as = key;
elmImport.exposures = entry.getValue();
elmImport.exposures.add(key);
elmImport.hasExposures = true;
@@ -582,10 +536,12 @@ public class ElmClientCodegen extends DefaultCodegen implements CodegenConfig {
public CodegenProperty fromProperty(String name, Schema p) {
final CodegenProperty property = super.fromProperty(name, p);
final String dataType = property.isEnum ? property.baseName : property.dataType;
addEncoderAndDecoder(property.vendorExtensions, dataType, property.isMapContainer, property.isPrimitiveType && !property.isEnum);
if (property.isEnum) {
addEncoderAndDecoder(property.vendorExtensions, property.baseName, DataTypeExposure.INTERNAL);
property.vendorExtensions.put(UNION_TYPE, property.datatypeWithEnum);
} else {
final boolean isPrimitiveType = property.isMapContainer ? isPrimitiveDataType(property.dataType) : property.isPrimitiveType;
addEncoderAndDecoder(property.vendorExtensions, property.dataType, isPrimitiveType ? DataTypeExposure.PRIMITIVE : DataTypeExposure.EXTERNAL);
}
return property;
@@ -595,33 +551,46 @@ public class ElmClientCodegen extends DefaultCodegen implements CodegenConfig {
public CodegenResponse fromResponse(OpenAPI openAPI, String responseCode, ApiResponse resp) {
final CodegenResponse response = super.fromResponse(openAPI, responseCode, resp);
if (response.dataType != null) {
addEncoderAndDecoder(response.vendorExtensions, response.dataType, response.isMapContainer, response.primitiveType);
final boolean isPrimitiveType = response.isMapContainer ? isPrimitiveDataType(response.dataType) : response.primitiveType;
addEncoderAndDecoder(response.vendorExtensions, response.dataType, isPrimitiveType ? DataTypeExposure.PRIMITIVE : DataTypeExposure.EXTERNAL);
}
return response;
}
@Override
public void postProcessParameter(CodegenParameter parameter) {
addEncoderAndDecoder(parameter.vendorExtensions, parameter.dataType, parameter.isMapContainer, parameter.isPrimitiveType);
final boolean isPrimitiveType = parameter.isMapContainer ? isPrimitiveDataType(parameter.dataType) : parameter.isPrimitiveType;
addEncoderAndDecoder(parameter.vendorExtensions, parameter.dataType, isPrimitiveType ? DataTypeExposure.PRIMITIVE : DataTypeExposure.EXTERNAL);
}
private boolean isPrimitiveDataType(String dataType) {
return languageSpecificPrimitives.contains(dataType);
}
private void addEncoderAndDecoder(Map<String, Object> vendorExtensions, String dataType, Boolean isMapContainer, Boolean isPrimitiveType) {
if (isMapContainer) {
isPrimitiveType = isPrimitiveDataType(dataType);
}
private void addEncoderAndDecoder(final Map<String, Object> vendorExtensions, final String dataType, final DataTypeExposure dataTypeExposure) {
final String baseName = org.openapitools.codegen.utils.StringUtils.camelize(dataType, true);
String encoderName;
String decoderName;
if (isPrimitiveType) {
encoderName = "Encode." + baseName;
decoderName = "Decode." + baseName;
} else {
encoderName = baseName + "Encoder";
decoderName = baseName + "Decoder";
switch (dataTypeExposure) {
case EXPOSED:
decoderName = "decoder";
encoderName = "encoder";
break;
case INTERNAL:
encoderName = baseName + "Encoder";
decoderName = baseName + "Decoder";
break;
case EXTERNAL:
encoderName = dataType + ".encoder";
decoderName = dataType + ".decoder";
break;
case PRIMITIVE:
encoderName = "Encode." + baseName;
decoderName = "Decode." + baseName;
break;
default:
encoderName = "";
decoderName = "";
}
if (!vendorExtensions.containsKey(ENCODER)) {
vendorExtensions.put(ENCODER, encoderName);
@@ -631,6 +600,13 @@ public class ElmClientCodegen extends DefaultCodegen implements CodegenConfig {
}
}
private enum DataTypeExposure {
EXPOSED,
INTERNAL,
EXTERNAL,
PRIMITIVE
}
private static class ElmImport {
public String moduleName;
public String as;