Skip to content

Commit 356647d

Browse files
committed
feat: better handling of enums
1 parent 7f4e885 commit 356647d

19 files changed

Lines changed: 124 additions & 163 deletions

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

Lines changed: 81 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,28 @@ public void execute(Template.Fragment fragment, Writer writer) throws IOExceptio
304304
writer.write(text.toUpperCase(Locale.ROOT));
305305
}
306306
});
307+
additionalProperties.put("quoteIfString", new Mustache.Lambda() {
308+
@Override
309+
public void execute(Template.Fragment fragment, Writer writer) throws IOException {
310+
String text = fragment.execute();
311+
if (text != null) {
312+
try {
313+
// Try to parse as a number
314+
Double.parseDouble(text);
315+
// If parsing succeeds, it's a number, so write it as is
316+
writer.write(text);
317+
} catch (NumberFormatException e) {
318+
// Check if it's a boolean
319+
if (text.equals("true") || text.equals("false")) {
320+
writer.write(text);
321+
} else {
322+
// It's not a number or boolean, so it's a string - quote it
323+
writer.write("\"" + text + "\"");
324+
}
325+
}
326+
}
327+
}
328+
});
307329

308330
if (additionalProperties.containsKey(CodegenConstants.INVOKER_PACKAGE)) {
309331
setModuleName((String) additionalProperties.get(CodegenConstants.INVOKER_PACKAGE));
@@ -426,6 +448,12 @@ public CodegenModel fromModel(String name, Schema model) {
426448
for (CodegenProperty field : requiredEctoFields) {
427449
ecm.requiredEctoFields.add(new ExtendedCodegenProperty(field));
428450
}
451+
452+
List<CodegenProperty> ectoEnums = new ArrayList<>(ecm.ectoEnums);
453+
ecm.ectoEnums.clear();
454+
for (CodegenProperty field : ectoEnums) {
455+
ecm.ectoEnums.add(new ExtendedCodegenProperty(field));
456+
}
429457

430458
return ecm;
431459
}
@@ -910,6 +938,7 @@ class ExtendedCodegenModel extends CodegenModel {
910938
public boolean hasImports;
911939
public List<CodegenProperty> ectoFields = new ArrayList<>();
912940
public List<CodegenProperty> ectoEmbeds = new ArrayList<>();
941+
public List<CodegenProperty> ectoEnums = new ArrayList<>();
913942
public List<CodegenProperty> requiredEctoFields = new ArrayList<>();
914943

915944
public ExtendedCodegenModel(CodegenModel cm) {
@@ -966,11 +995,14 @@ public ExtendedCodegenModel(CodegenModel cm) {
966995
this.hasImports = !this.imports.isEmpty();
967996

968997
for (CodegenProperty var : this.vars) {
969-
if (var.isPrimitiveType || var.isMap) {
998+
if (var.isPrimitiveType || var.isMap || var.isEnum || var.isEnumRef) {
970999
this.ectoFields.add(var);
9711000
if (var.required) {
9721001
this.requiredEctoFields.add(var);
9731002
}
1003+
if (var.isEnum || var.isEnumRef) {
1004+
this.ectoEnums.add(var);
1005+
}
9741006
} else {
9751007
this.ectoEmbeds.add(var);
9761008
}
@@ -979,6 +1011,8 @@ public ExtendedCodegenModel(CodegenModel cm) {
9791011
}
9801012

9811013
class ExtendedCodegenProperty extends CodegenProperty {
1014+
public String enumBaseType;
1015+
9821016
public ExtendedCodegenProperty(CodegenProperty cp) {
9831017
super();
9841018

@@ -1035,6 +1069,7 @@ public ExtendedCodegenProperty(CodegenProperty cp) {
10351069
this.isArray = cp.isArray;
10361070
this.isMap = cp.isMap;
10371071
this.isEnum = cp.isEnum;
1072+
this.isEnumRef = cp.isEnumRef;
10381073
this.isReadOnly = cp.isReadOnly;
10391074
this.isWriteOnly = cp.isWriteOnly;
10401075
this.isNullable = cp.isNullable;
@@ -1045,19 +1080,62 @@ public ExtendedCodegenProperty(CodegenProperty cp) {
10451080
this.vars = cp.vars;
10461081
this.requiredVars = cp.requiredVars;
10471082
this.vendorExtensions = cp.vendorExtensions;
1083+
1084+
// For enum references, determine the base type from the enum values
1085+
if (cp.isEnumRef && cp.allowableValues != null && cp.allowableValues.get("values") != null) {
1086+
List<Object> values = (List<Object>) cp.allowableValues.get("values");
1087+
if (!values.isEmpty()) {
1088+
Object firstValue = values.get(0);
1089+
if (firstValue instanceof String) {
1090+
this.enumBaseType = "String.t";
1091+
} else if (firstValue instanceof Integer || firstValue instanceof Long) {
1092+
this.enumBaseType = "integer()";
1093+
} else if (firstValue instanceof Float || firstValue instanceof Double) {
1094+
this.enumBaseType = "float()";
1095+
} else if (firstValue instanceof Boolean) {
1096+
this.enumBaseType = "boolean()";
1097+
} else {
1098+
// Default to string for unknown types
1099+
this.enumBaseType = "String.t";
1100+
}
1101+
} else {
1102+
// No values, default to string
1103+
this.enumBaseType = "String.t";
1104+
}
1105+
}
10481106
}
10491107

10501108
public String ectoType() {
10511109
String ectoType = ectoType(this);
1052-
10531110
if (":any".equals(ectoType)) {
10541111
return ectoType + ", virtual: true";
10551112
}
1056-
10571113
return ectoType;
10581114
}
10591115

10601116
private String ectoType(CodegenProperty property) {
1117+
if (property.isEnumRef) {
1118+
List<Object> values = (List<Object>) property.allowableValues.get("values");
1119+
if (!values.isEmpty()) {
1120+
Object firstValue = values.get(0);
1121+
if (firstValue instanceof String) {
1122+
return ":string";
1123+
} else if (firstValue instanceof Integer || firstValue instanceof Long) {
1124+
return ":integer";
1125+
} else if (firstValue instanceof Float || firstValue instanceof Double) {
1126+
return ":float";
1127+
} else if (firstValue instanceof Boolean) {
1128+
return ":boolean";
1129+
} else {
1130+
// Default to string for unknown types
1131+
return ":string";
1132+
}
1133+
} else {
1134+
// No values, default to string
1135+
return ":string";
1136+
}
1137+
}
1138+
10611139
String baseType = property.baseType;
10621140
switch (baseType) {
10631141
case "integer()":

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

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
{{>licenseInfo}}
2-
{{#models}}{{#model}}defmodule {{moduleName}}.Model.{{classname}} do
2+
{{#models}}{{#model}}{{^isEnum}}defmodule {{moduleName}}.Model.{{classname}} do
33
@moduledoc """
44
{{&description}}
55
"""
66
use Ecto.Schema
77

88
@type t :: %__MODULE__{
9-
{{#vars}}{{#atom}}{{&baseName}}{{/atom}} => {{{datatype}}}{{#isNullable}} | nil{{/isNullable}}{{^isNullable}}{{^required}} | nil{{/required}}{{/isNullable}}{{^-last}},
9+
{{#vars}}{{#atom}}{{&baseName}}{{/atom}} => {{#isEnumRef}}{{{enumBaseType}}}{{/isEnumRef}}{{^isEnumRef}}{{{datatype}}}{{/isEnumRef}}{{#isNullable}} | nil{{/isNullable}}{{^isNullable}}{{^required}} | nil{{/required}}{{/isNullable}}{{^-last}},
1010
{{/-last}}{{/vars}}
1111
}
1212

@@ -26,9 +26,12 @@
2626
struct
2727
|> Ecto.Changeset.cast(params, [{{#ectoFields}}{{#atom}}{{&baseName}}{{/atom}}{{^-last}}, {{/-last}}{{/ectoFields}}])
2828
|> Ecto.Changeset.validate_required([{{#requiredEctoFields}}{{#atom}}{{&baseName}}{{/atom}}{{^-last}}, {{/-last}}{{/requiredEctoFields}}])
29+
{{#ectoEnums}}
30+
|> Ecto.Changeset.validate_inclusion({{#atom}}{{&baseName}}{{/atom}}, [{{#allowableValues}}{{#values}}{{#quoteIfString}}{{.}}{{/quoteIfString}}{{^-last}}, {{/-last}}{{/values}}{{/allowableValues}}])
31+
{{/ectoEnums}}
2932
{{#ectoEmbeds}}
3033
|> Ecto.Changeset.cast_embed({{#atom}}{{&baseName}}{{/atom}}{{#required}}, required: true{{/required}})
3134
{{/ectoEmbeds}}
3235
end
3336
end
34-
{{/model}}{{/models}}
37+
{{/isEnum}}{{/model}}{{/models}}

samples/client/petstore/elixir/lib/openapi_petstore/model/all_of_with_single_ref.ex

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,22 +9,22 @@ defmodule OpenapiPetstore.Model.AllOfWithSingleRef do
99

1010
@type t :: %__MODULE__{
1111
:username => String.t | nil,
12-
:SingleRefType => OpenapiPetstore.Model.SingleRefType.t | nil
12+
:SingleRefType => String.t | nil
1313
}
1414

1515
@derive {JSON.Encoder, only: [:username, :SingleRefType]}
1616
@primary_key false
1717
embedded_schema do
1818
field :username, :string
19-
embeds_one :SingleRefType, OpenapiPetstore.Model.SingleRefType
19+
field :SingleRefType, :string
2020
end
2121

2222
@spec changeset(t(), map()) :: Ecto.Changeset.t()
2323
def changeset(%__MODULE__{} = struct, params) do
2424
struct
25-
|> Ecto.Changeset.cast(params, [:username])
25+
|> Ecto.Changeset.cast(params, [:username, :SingleRefType])
2626
|> Ecto.Changeset.validate_required([])
27-
|> Ecto.Changeset.cast_embed(:SingleRefType)
27+
|> Ecto.Changeset.validate_inclusion(:SingleRefType, ["admin", "user"])
2828
end
2929
end
3030

samples/client/petstore/elixir/lib/openapi_petstore/model/child_with_nullable.ex

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ defmodule OpenapiPetstore.Model.ChildWithNullable do
2626
struct
2727
|> Ecto.Changeset.cast(params, [:type, :nullableProperty, :otherProperty])
2828
|> Ecto.Changeset.validate_required([])
29+
|> Ecto.Changeset.validate_inclusion(:type, ["ChildWithNullable"])
2930
end
3031
end
3132

samples/client/petstore/elixir/lib/openapi_petstore/model/enum_arrays.ex

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ defmodule OpenapiPetstore.Model.EnumArrays do
2424
struct
2525
|> Ecto.Changeset.cast(params, [:just_symbol, :array_enum])
2626
|> Ecto.Changeset.validate_required([])
27+
|> Ecto.Changeset.validate_inclusion(:just_symbol, ["&gt;&#x3D;", "$"])
28+
|> Ecto.Changeset.validate_inclusion(:array_enum, ["fish", "crab"])
2729
end
2830
end
2931

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,4 @@
11
# NOTE: This file is auto generated by OpenAPI Generator 7.14.0-SNAPSHOT (https://openapi-generator.tech).
22
# Do not edit this file manually.
33

4-
defmodule OpenapiPetstore.Model.EnumClass do
5-
@moduledoc """
6-
7-
"""
8-
use Ecto.Schema
9-
10-
@type t :: %__MODULE__{
11-
12-
}
13-
14-
@derive {JSON.Encoder, only: []}
15-
@primary_key false
16-
embedded_schema do
17-
end
18-
19-
@spec changeset(t(), map()) :: Ecto.Changeset.t()
20-
def changeset(%__MODULE__{} = struct, params) do
21-
struct
22-
|> Ecto.Changeset.cast(params, [])
23-
|> Ecto.Changeset.validate_required([])
24-
end
25-
end
264

samples/client/petstore/elixir/lib/openapi_petstore/model/enum_test.ex

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@ defmodule OpenapiPetstore.Model.EnumTest do
1212
:enum_string_required => String.t,
1313
:enum_integer => integer() | nil,
1414
:enum_number => float() | nil,
15-
:outerEnum => OpenapiPetstore.Model.OuterEnum.t | nil,
16-
:outerEnumInteger => OpenapiPetstore.Model.OuterEnumInteger.t | nil,
17-
:outerEnumDefaultValue => OpenapiPetstore.Model.OuterEnumDefaultValue.t | nil,
18-
:outerEnumIntegerDefaultValue => OpenapiPetstore.Model.OuterEnumIntegerDefaultValue.t | nil
15+
:outerEnum => String.t | nil,
16+
:outerEnumInteger => integer() | nil,
17+
:outerEnumDefaultValue => String.t | nil,
18+
:outerEnumIntegerDefaultValue => integer() | nil
1919
}
2020

2121
@derive {JSON.Encoder, only: [:enum_string, :enum_string_required, :enum_integer, :enum_number, :outerEnum, :outerEnumInteger, :outerEnumDefaultValue, :outerEnumIntegerDefaultValue]}
@@ -25,21 +25,25 @@ defmodule OpenapiPetstore.Model.EnumTest do
2525
field :enum_string_required, :string
2626
field :enum_integer, :integer
2727
field :enum_number, :float
28-
embeds_one :outerEnum, OpenapiPetstore.Model.OuterEnum
29-
embeds_one :outerEnumInteger, OpenapiPetstore.Model.OuterEnumInteger
30-
embeds_one :outerEnumDefaultValue, OpenapiPetstore.Model.OuterEnumDefaultValue
31-
embeds_one :outerEnumIntegerDefaultValue, OpenapiPetstore.Model.OuterEnumIntegerDefaultValue
28+
field :outerEnum, :string
29+
field :outerEnumInteger, :integer
30+
field :outerEnumDefaultValue, :string
31+
field :outerEnumIntegerDefaultValue, :integer
3232
end
3333

3434
@spec changeset(t(), map()) :: Ecto.Changeset.t()
3535
def changeset(%__MODULE__{} = struct, params) do
3636
struct
37-
|> Ecto.Changeset.cast(params, [:enum_string, :enum_string_required, :enum_integer, :enum_number])
37+
|> Ecto.Changeset.cast(params, [:enum_string, :enum_string_required, :enum_integer, :enum_number, :outerEnum, :outerEnumInteger, :outerEnumDefaultValue, :outerEnumIntegerDefaultValue])
3838
|> Ecto.Changeset.validate_required([:enum_string_required])
39-
|> Ecto.Changeset.cast_embed(:outerEnum)
40-
|> Ecto.Changeset.cast_embed(:outerEnumInteger)
41-
|> Ecto.Changeset.cast_embed(:outerEnumDefaultValue)
42-
|> Ecto.Changeset.cast_embed(:outerEnumIntegerDefaultValue)
39+
|> Ecto.Changeset.validate_inclusion(:enum_string, ["UPPER", "lower", ""])
40+
|> Ecto.Changeset.validate_inclusion(:enum_string_required, ["UPPER", "lower", ""])
41+
|> Ecto.Changeset.validate_inclusion(:enum_integer, [1, -1])
42+
|> Ecto.Changeset.validate_inclusion(:enum_number, [1.1, -1.2])
43+
|> Ecto.Changeset.validate_inclusion(:outerEnum, ["placed", "approved", "delivered"])
44+
|> Ecto.Changeset.validate_inclusion(:outerEnumInteger, [0, 1, 2])
45+
|> Ecto.Changeset.validate_inclusion(:outerEnumDefaultValue, ["placed", "approved", "delivered"])
46+
|> Ecto.Changeset.validate_inclusion(:outerEnumIntegerDefaultValue, [0, 1, 2])
4347
end
4448
end
4549

samples/client/petstore/elixir/lib/openapi_petstore/model/map_test.ex

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ defmodule OpenapiPetstore.Model.MapTest do
2828
struct
2929
|> Ecto.Changeset.cast(params, [:map_map_of_string, :map_of_enum_string, :direct_map, :indirect_map])
3030
|> Ecto.Changeset.validate_required([])
31+
|> Ecto.Changeset.validate_inclusion(:map_of_enum_string, ["UPPER", "lower"])
3132
end
3233
end
3334

samples/client/petstore/elixir/lib/openapi_petstore/model/order.ex

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ defmodule OpenapiPetstore.Model.Order do
3232
struct
3333
|> Ecto.Changeset.cast(params, [:id, :petId, :quantity, :shipDate, :status, :complete])
3434
|> Ecto.Changeset.validate_required([])
35+
|> Ecto.Changeset.validate_inclusion(:status, ["placed", "approved", "delivered"])
3536
end
3637
end
3738

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,4 @@
11
# NOTE: This file is auto generated by OpenAPI Generator 7.14.0-SNAPSHOT (https://openapi-generator.tech).
22
# Do not edit this file manually.
33

4-
defmodule OpenapiPetstore.Model.OuterEnum do
5-
@moduledoc """
6-
7-
"""
8-
use Ecto.Schema
9-
10-
@type t :: %__MODULE__{
11-
12-
}
13-
14-
@derive {JSON.Encoder, only: []}
15-
@primary_key false
16-
embedded_schema do
17-
end
18-
19-
@spec changeset(t(), map()) :: Ecto.Changeset.t()
20-
def changeset(%__MODULE__{} = struct, params) do
21-
struct
22-
|> Ecto.Changeset.cast(params, [])
23-
|> Ecto.Changeset.validate_required([])
24-
end
25-
end
264

0 commit comments

Comments
 (0)