Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
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);
Expand All @@ -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";
Expand Down Expand Up @@ -187,10 +188,18 @@ public void processOpts() {
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");
Expand All @@ -199,19 +208,6 @@ public void processOpts() {
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
Expand Down Expand Up @@ -339,55 +335,40 @@ public int compare(CodegenModel cm1, CodegenModel cm2) {
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);
Expand All @@ -396,42 +377,16 @@ public int compare(CodegenModel cm1, CodegenModel cm2) {
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;
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));
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(name);
elmImport.hasExposures = true;
return elmImport;
}
Expand Down Expand Up @@ -463,7 +418,6 @@ public Map<String, Object> postProcessOperationsWithModels(Map<String, Object> o
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) {
Expand All @@ -475,7 +429,6 @@ public Map<String, Object> postProcessOperationsWithModels(Map<String, Object> o
if (!dependencies.containsKey(resp.dataType)) {
dependencies.put(resp.dataType, new TreeSet<String>());
}
dependencies.get(resp.dataType).add(decoder);
}
}
}
Expand All @@ -485,6 +438,7 @@ public Map<String, Object> postProcessOperationsWithModels(Map<String, Object> o
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;
Expand Down Expand Up @@ -582,10 +536,12 @@ public String getTypeDeclaration(Schema p) {
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;
Expand All @@ -595,33 +551,46 @@ public CodegenProperty fromProperty(String name, Schema p) {
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);
Expand All @@ -631,6 +600,13 @@ private void addEncoderAndDecoder(Map<String, Object> vendorExtensions, String d
}
}

private enum DataTypeExposure {
EXPOSED,
INTERNAL,
EXTERNAL,
PRIMITIVE
}

private static class ElmImport {
public String moduleName;
public String as;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
module DateOnly exposing (DateOnly, dateOnlyDecoder, dateOnlyEncoder)
module DateOnly exposing (DateOnly, decoder, encoder)

import Iso8601
import Json.Decode as Decode exposing (Decoder)
Expand All @@ -11,14 +11,14 @@ type alias DateOnly =
Time.Posix


dateOnlyDecoder : Decoder DateOnly
dateOnlyDecoder =
decoder : Decoder DateOnly
decoder =
Decode.string
|> Decode.andThen decodeIsoString


dateOnlyEncoder : DateOnly -> Encode.Value
dateOnlyEncoder model =
encoder : DateOnly -> Encode.Value
encoder model =
Iso8601.fromTime model
|> String.left 10
|> Encode.string
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
module DateOnly exposing (DateOnly, dateOnlyDecoder, dateOnlyEncoder)
module DateOnly exposing (DateOnly, decoder, encoder)

import Date
import Date.Extra exposing (fromIsoString, toFormattedString)
Expand All @@ -11,14 +11,14 @@ type alias DateOnly =
Date.Date


dateOnlyDecoder : Decoder DateOnly
dateOnlyDecoder =
decoder : Decoder DateOnly
decoder =
Decode.string
|> Decode.andThen decodeIsoString


dateOnlyEncoder : DateOnly -> Encode.Value
dateOnlyEncoder model =
encoder : DateOnly -> Encode.Value
encoder model =
Encode.string <| toFormattedString "yyyy-MM-dd" model


Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
module DateTime exposing (DateTime, dateTimeDecoder, dateTimeEncoder)
module DateTime exposing (DateTime, decoder, encoder)

import Iso8601
import Json.Decode as Decode exposing (Decoder)
Expand All @@ -11,14 +11,14 @@ type alias DateTime =
Time.Posix


dateTimeDecoder : Decoder DateTime
dateTimeDecoder =
decoder : Decoder DateTime
decoder =
Decode.string
|> Decode.andThen decodeIsoString


dateTimeEncoder : DateTime -> Encode.Value
dateTimeEncoder model =
encoder : DateTime -> Encode.Value
encoder model =
Encode.string <| Iso8601.fromTime model


Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
module DateTime exposing (DateTime, dateTimeDecoder, dateTimeEncoder)
module DateTime exposing (DateTime, decoder, encoder)

import Date
import Date.Extra exposing (fromIsoString, toIsoString)
Expand All @@ -11,14 +11,14 @@ type alias DateTime =
Date.Date


dateTimeDecoder : Decoder DateTime
dateTimeDecoder =
decoder : Decoder DateTime
decoder =
Decode.string
|> Decode.andThen decodeIsoString


dateTimeEncoder : DateTime -> Encode.Value
dateTimeEncoder model =
encoder : DateTime -> Encode.Value
encoder model =
Encode.string <| toIsoString model


Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
{{classVarName}}Decoder : Decoder {{classname}}
{{classVarName}}Decoder =
decoder : Decoder {{classname}}
decoder =
{{#isElm018}}decode{{/isElm018}}{{#isElm019}}Decode.succeed{{/isElm019}} {{classname}}
{{#allVars}}
{{^discriminatorValue}} |> {{>fieldDecoder}}
{{/discriminatorValue}}
|> {{>fieldDecoder}}
{{/allVars}}
Loading