Skip to content

Commit 305317c

Browse files
committed
feat: use typed ecto schemas in generated models
1 parent 4cffd32 commit 305317c

60 files changed

Lines changed: 927 additions & 744 deletions

Some content is hidden

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

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

Lines changed: 168 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ public class ElixirClientCodegen extends DefaultCodegen {
6060
String supportedElixirVersion = "1.18";
6161
List<String> extraApplications = Arrays.asList(":logger");
6262
List<String> deps = Arrays.asList(
63+
"{:ecto, \"~> 3.12\"}",
6364
"{:tesla, \"~> 1.14\"}",
6465
"{:ex_doc, \"~> 0.37.3\", only: :dev, runtime: false}",
6566
"{:dialyxir, \"~> 1.4\", only: [:dev, :test], runtime: false}");
@@ -336,10 +337,6 @@ public void preprocessOpenAPI(OpenAPI openAPI) {
336337
supportingFiles.add(new SupportingFile("request_builder.ex.mustache",
337338
sourceFolder(),
338339
"request_builder.ex"));
339-
340-
supportingFiles.add(new SupportingFile("deserializer.ex.mustache",
341-
sourceFolder(),
342-
"deserializer.ex"));
343340
}
344341

345342
@Override
@@ -384,7 +381,52 @@ public OperationsMap postProcessOperationsWithModels(OperationsMap objs, List<Mo
384381
@Override
385382
public CodegenModel fromModel(String name, Schema model) {
386383
CodegenModel cm = super.fromModel(name, model);
387-
return new ExtendedCodegenModel(cm);
384+
ExtendedCodegenModel ecm = new ExtendedCodegenModel(cm);
385+
386+
// Convert all CodegenProperty objects to ExtendedCodegenProperty
387+
List<CodegenProperty> vars = new ArrayList<>(ecm.vars);
388+
ecm.vars.clear();
389+
for (CodegenProperty var : vars) {
390+
ecm.vars.add(new ExtendedCodegenProperty(var));
391+
}
392+
393+
List<CodegenProperty> allVars = new ArrayList<>(ecm.allVars);
394+
ecm.allVars.clear();
395+
for (CodegenProperty var : allVars) {
396+
ecm.allVars.add(new ExtendedCodegenProperty(var));
397+
}
398+
399+
List<CodegenProperty> requiredVars = new ArrayList<>(ecm.requiredVars);
400+
ecm.requiredVars.clear();
401+
for (CodegenProperty var : requiredVars) {
402+
ecm.requiredVars.add(new ExtendedCodegenProperty(var));
403+
}
404+
405+
List<CodegenProperty> optionalVars = new ArrayList<>(ecm.optionalVars);
406+
ecm.optionalVars.clear();
407+
for (CodegenProperty var : optionalVars) {
408+
ecm.optionalVars.add(new ExtendedCodegenProperty(var));
409+
}
410+
411+
List<CodegenProperty> ectoFields = new ArrayList<>(ecm.ectoFields);
412+
ecm.ectoFields.clear();
413+
for (CodegenProperty field : ectoFields) {
414+
ecm.ectoFields.add(new ExtendedCodegenProperty(field));
415+
}
416+
417+
List<CodegenProperty> ectoEmbeds = new ArrayList<>(ecm.ectoEmbeds);
418+
ecm.ectoEmbeds.clear();
419+
for (CodegenProperty embed : ectoEmbeds) {
420+
ecm.ectoEmbeds.add(new ExtendedCodegenProperty(embed));
421+
}
422+
423+
List<CodegenProperty> requiredEctoFields = new ArrayList<>(ecm.requiredEctoFields);
424+
ecm.requiredEctoFields.clear();
425+
for (CodegenProperty field : requiredEctoFields) {
426+
ecm.requiredEctoFields.add(new ExtendedCodegenProperty(field));
427+
}
428+
429+
return ecm;
388430
}
389431

390432
@Override
@@ -865,6 +907,9 @@ private boolean getRequiresHttpcWorkaround() {
865907

866908
class ExtendedCodegenModel extends CodegenModel {
867909
public boolean hasImports;
910+
public List<CodegenProperty> ectoFields = new ArrayList<>();
911+
public List<CodegenProperty> ectoEmbeds = new ArrayList<>();
912+
public List<CodegenProperty> requiredEctoFields = new ArrayList<>();
868913

869914
public ExtendedCodegenModel(CodegenModel cm) {
870915
super();
@@ -918,15 +963,127 @@ public ExtendedCodegenModel(CodegenModel cm) {
918963
this.additionalPropertiesType = cm.additionalPropertiesType;
919964

920965
this.hasImports = !this.imports.isEmpty();
921-
}
922966

923-
public boolean hasComplexVars() {
924-
for (CodegenProperty p : vars) {
925-
if (!p.isPrimitiveType) {
926-
return true;
967+
for (CodegenProperty var : this.vars) {
968+
if (var.isPrimitiveType || var.isMap) {
969+
this.ectoFields.add(var);
970+
if (var.required) {
971+
this.requiredEctoFields.add(var);
972+
}
973+
} else {
974+
this.ectoEmbeds.add(var);
927975
}
928976
}
929-
return false;
977+
}
978+
}
979+
980+
class ExtendedCodegenProperty extends CodegenProperty {
981+
public ExtendedCodegenProperty(CodegenProperty cp) {
982+
super();
983+
984+
// Copy all fields of CodegenProperty
985+
this.openApiType = cp.openApiType;
986+
this.baseName = cp.baseName;
987+
this.complexType = cp.complexType;
988+
this.getter = cp.getter;
989+
this.setter = cp.setter;
990+
this.description = cp.description;
991+
this.dataType = cp.dataType;
992+
this.datatypeWithEnum = cp.datatypeWithEnum;
993+
this.dataFormat = cp.dataFormat;
994+
this.name = cp.name;
995+
this.min = cp.min;
996+
this.max = cp.max;
997+
this.defaultValue = cp.defaultValue;
998+
this.defaultValueWithParam = cp.defaultValueWithParam;
999+
this.baseType = cp.baseType;
1000+
this.containerType = cp.containerType;
1001+
this.title = cp.title;
1002+
this.unescapedDescription = cp.unescapedDescription;
1003+
this.maxLength = cp.maxLength;
1004+
this.minLength = cp.minLength;
1005+
this.pattern = cp.pattern;
1006+
this.example = cp.example;
1007+
this.jsonSchema = cp.jsonSchema;
1008+
this.minimum = cp.minimum;
1009+
this.maximum = cp.maximum;
1010+
this.exclusiveMinimum = cp.exclusiveMinimum;
1011+
this.exclusiveMaximum = cp.exclusiveMaximum;
1012+
this.required = cp.required;
1013+
this.deprecated = cp.deprecated;
1014+
this.isPrimitiveType = cp.isPrimitiveType;
1015+
this.isModel = cp.isModel;
1016+
this.isContainer = cp.isContainer;
1017+
this.isString = cp.isString;
1018+
this.isNumeric = cp.isNumeric;
1019+
this.isInteger = cp.isInteger;
1020+
this.isLong = cp.isLong;
1021+
this.isNumber = cp.isNumber;
1022+
this.isFloat = cp.isFloat;
1023+
this.isDouble = cp.isDouble;
1024+
this.isByteArray = cp.isByteArray;
1025+
this.isBinary = cp.isBinary;
1026+
this.isFile = cp.isFile;
1027+
this.isBoolean = cp.isBoolean;
1028+
this.isDate = cp.isDate;
1029+
this.isDateTime = cp.isDateTime;
1030+
this.isUuid = cp.isUuid;
1031+
this.isEmail = cp.isEmail;
1032+
this.isModel = cp.isModel;
1033+
this.isNull = cp.isNull;
1034+
this.isArray = cp.isArray;
1035+
this.isMap = cp.isMap;
1036+
this.isEnum = cp.isEnum;
1037+
this.isReadOnly = cp.isReadOnly;
1038+
this.isWriteOnly = cp.isWriteOnly;
1039+
this.isNullable = cp.isNullable;
1040+
this._enum = cp._enum;
1041+
this.allowableValues = cp.allowableValues;
1042+
this.items = cp.items;
1043+
this.additionalProperties = cp.additionalProperties;
1044+
this.vars = cp.vars;
1045+
this.requiredVars = cp.requiredVars;
1046+
this.vendorExtensions = cp.vendorExtensions;
1047+
}
1048+
1049+
public String ectoType() {
1050+
String ectoType = ectoType(this);
1051+
1052+
if (":any".equals(ectoType)) {
1053+
return ectoType + ", virtual: true";
1054+
}
1055+
1056+
return ectoType;
1057+
}
1058+
1059+
private String ectoType(CodegenProperty property) {
1060+
String baseType = property.baseType;
1061+
switch (baseType) {
1062+
case "integer()":
1063+
return ":integer";
1064+
case "float()":
1065+
return ":float";
1066+
case "number()":
1067+
return ":float";
1068+
case "boolean()":
1069+
return ":boolean";
1070+
case "String.t":
1071+
return ":string";
1072+
case "Date.t":
1073+
return ":date";
1074+
case "DateTime.t":
1075+
return ":utc_datetime";
1076+
case "binary()":
1077+
return ":binary";
1078+
case "list()":
1079+
return "{:array, " + ectoType(property.items) + "}";
1080+
case "map()":
1081+
return ":map";
1082+
case "nil":
1083+
return ":any";
1084+
default:
1085+
return ":any";
1086+
}
9301087
}
9311088
}
9321089

modules/openapi-generator/src/main/resources/elixir/deserializer.ex.mustache

Lines changed: 0 additions & 114 deletions
This file was deleted.

modules/openapi-generator/src/main/resources/elixir/model.mustache

Lines changed: 19 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,34 +3,32 @@
33
@moduledoc """
44
{{&description}}
55
"""
6-
7-
@derive JSON.Encoder
8-
defstruct [
9-
{{#vars}}{{#atom}}{{&baseName}}{{/atom}}{{^-last}},
10-
{{/-last}}{{/vars}}
11-
]
6+
use Ecto.Schema
127

138
@type t :: %__MODULE__{
149
{{#vars}}{{#atom}}{{&baseName}}{{/atom}} => {{{datatype}}}{{#isNullable}} | nil{{/isNullable}}{{^isNullable}}{{^required}} | nil{{/required}}{{/isNullable}}{{^-last}},
1510
{{/-last}}{{/vars}}
1611
}
1712

18-
{{#hasComplexVars}}
19-
alias {{&moduleName}}.Deserializer
13+
@derive {JSON.Encoder, only: [{{#vars}}{{#atom}}{{&baseName}}{{/atom}}{{^-last}}, {{/-last}}{{/vars}}]}
14+
@primary_key false
15+
embedded_schema do
16+
{{#ectoFields}}
17+
field {{#atom}}{{&baseName}}{{/atom}}, {{{ectoType}}}
18+
{{/ectoFields}}
19+
{{#ectoEmbeds}}
20+
{{^isArray}}embeds_one{{/isArray}}{{#isArray}}embeds_many{{/isArray}} {{#atom}}{{&baseName}}{{/atom}}, {{&moduleName}}.Model.{{^isArray}}{{{baseType}}}{{/isArray}}{{#isArray}}{{{items.baseType}}}{{/isArray}}
21+
{{/ectoEmbeds}}
22+
end
2023

21-
def decode(value) do
22-
value
23-
{{#vars}}
24-
{{^isPrimitiveType}}
25-
{{#baseType}} |> Deserializer.deserialize({{#atom}}{{&baseName}}{{/atom}}, {{#isArray}}:list, {{&moduleName}}.Model.{{{items.baseType}}}{{/isArray}}{{#isMap}}:map, {{&moduleName}}.Model.{{{items.baseType}}}{{/isMap}}{{#isDate}}:date, nil{{/isDate}}{{#isDateTime}}:datetime, nil{{/isDateTime}}{{^isDate}}{{^isDateTime}}{{^isMap}}{{^isArray}}:struct, {{moduleName}}.Model.{{baseType}}{{/isArray}}{{/isMap}}{{/isDateTime}}{{/isDate}})
26-
{{/baseType}}
27-
{{/isPrimitiveType}}
28-
{{/vars}}
29-
{{/hasComplexVars}}
30-
{{^hasComplexVars}}
31-
def decode(value) do
32-
value
33-
{{/hasComplexVars}}
24+
@spec changeset(t(), map()) :: Ecto.Changeset.t()
25+
def changeset(%__MODULE__{} = struct, params) do
26+
struct
27+
|> Ecto.Changeset.cast(params, [{{#ectoFields}}{{#atom}}{{&baseName}}{{/atom}}{{^-last}}, {{/-last}}{{/ectoFields}}])
28+
|> Ecto.Changeset.validate_required([{{#requiredEctoFields}}{{#atom}}{{&baseName}}{{/atom}}{{^-last}}, {{/-last}}{{/requiredEctoFields}}])
29+
{{#ectoEmbeds}}
30+
|> Ecto.Changeset.cast_embed({{#atom}}{{&baseName}}{{/atom}}{{#required}}, required: true{{/required}})
31+
{{/ectoEmbeds}}
3432
end
3533
end
3634
{{/model}}{{/models}}

0 commit comments

Comments
 (0)