Skip to content

Commit ea4dbdb

Browse files
committed
handle maps with nested models
1 parent a3edf68 commit ea4dbdb

6 files changed

Lines changed: 93 additions & 0 deletions

File tree

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

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,10 @@ public void preprocessOpenAPI(OpenAPI openAPI) {
360360
supportingFiles.add(new SupportingFile("request_builder.ex.mustache",
361361
sourceFolder(),
362362
"request_builder.ex"));
363+
364+
supportingFiles.add(new SupportingFile("ecto_utils.ex.mustache",
365+
sourceFolder(),
366+
"ecto_utils.ex"));
363367
}
364368

365369
@Override
@@ -454,6 +458,13 @@ public CodegenModel fromModel(String name, Schema model) {
454458
for (CodegenProperty field : ectoEnums) {
455459
ecm.ectoEnums.add(new ExtendedCodegenProperty(field));
456460
}
461+
462+
463+
List<CodegenProperty> ectoMaps = new ArrayList<>(ecm.ectoMaps);
464+
ecm.ectoMaps.clear();
465+
for (CodegenProperty field : ectoMaps) {
466+
ecm.ectoMaps.add(new ExtendedCodegenProperty(field));
467+
}
457468

458469
return ecm;
459470
}
@@ -939,6 +950,7 @@ class ExtendedCodegenModel extends CodegenModel {
939950
public List<CodegenProperty> ectoFields = new ArrayList<>();
940951
public List<CodegenProperty> ectoEmbeds = new ArrayList<>();
941952
public List<CodegenProperty> ectoEnums = new ArrayList<>();
953+
public List<CodegenProperty> ectoMaps = new ArrayList<>();
942954
public List<CodegenProperty> requiredEctoFields = new ArrayList<>();
943955

944956
public ExtendedCodegenModel(CodegenModel cm) {
@@ -1003,6 +1015,9 @@ public ExtendedCodegenModel(CodegenModel cm) {
10031015
if (var.isEnum || var.isEnumRef) {
10041016
this.ectoEnums.add(var);
10051017
}
1018+
if (var.isMap && !var.isFreeFormObject && var.additionalProperties != null && var.additionalProperties.isModel) {
1019+
this.ectoMaps.add(var);
1020+
}
10061021
} else {
10071022
this.ectoEmbeds.add(var);
10081023
}
@@ -1012,6 +1027,8 @@ public ExtendedCodegenModel(CodegenModel cm) {
10121027

10131028
class ExtendedCodegenProperty extends CodegenProperty {
10141029
public String enumBaseType;
1030+
public String mapValueType;
1031+
public boolean isMapWithSchema;
10151032

10161033
public ExtendedCodegenProperty(CodegenProperty cp) {
10171034
super();
@@ -1104,6 +1121,17 @@ public ExtendedCodegenProperty(CodegenProperty cp) {
11041121
this.enumBaseType = "String.t";
11051122
}
11061123
}
1124+
// For map properties, extract the model name from additionalProperties
1125+
if (cp.isMap && cp.additionalProperties != null) {
1126+
// Check if the map has a schema (model) or is a free-form map
1127+
if (cp.additionalProperties.isModel) {
1128+
// Extract just the model name without the full type declaration
1129+
this.mapValueType = cp.additionalProperties.complexType;
1130+
this.isMapWithSchema = true;
1131+
} else {
1132+
this.isMapWithSchema = false;
1133+
}
1134+
}
11071135
}
11081136

11091137
public String ectoType() {
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
{{>licenseInfo}}
2+
defmodule {{moduleName}}.EctoUtils do
3+
def cast_nested_map(changeset, field, schema) do
4+
values = Map.get(changeset.changes, field) || %{}
5+
6+
{result_map, errors} =
7+
Enum.reduce(values, {%{}, []}, fn {key, params}, {acc, errs} ->
8+
changeset = schema.changeset(struct(schema), params)
9+
10+
case Ecto.Changeset.apply_action(changeset, changeset.action || :insert) do
11+
{:ok, struct} ->
12+
{Map.put(acc, key, struct), errs}
13+
14+
{:error, nested_cs} ->
15+
{Map.put(acc, key, nested_cs), [{key, nested_cs} | errs]}
16+
end
17+
end)
18+
19+
changeset =
20+
Ecto.Changeset.put_change(changeset, field, result_map)
21+
22+
if errors == [] do
23+
changeset
24+
else
25+
Ecto.Changeset.add_error(changeset, field, "contains invalid nested entries")
26+
|> Map.put(:valid?, false)
27+
end
28+
end
29+
end

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@
2929
{{#ectoEnums}}
3030
|> Ecto.Changeset.validate_inclusion({{#atom}}{{&baseName}}{{/atom}}, [{{#allowableValues}}{{#values}}{{#quoteIfString}}{{.}}{{/quoteIfString}}{{^-last}}, {{/-last}}{{/values}}{{/allowableValues}}])
3131
{{/ectoEnums}}
32+
{{#ectoMaps}}
33+
|> {{moduleName}}.EctoUtils.cast_nested_map({{#atom}}{{&baseName}}{{/atom}}, {{&moduleName}}.Model.{{{mapValueType}}})
34+
{{/ectoMaps}}
3235
{{#ectoEmbeds}}
3336
|> Ecto.Changeset.cast_embed({{#atom}}{{&baseName}}{{/atom}}{{#required}}, required: true{{/required}})
3437
{{/ectoEmbeds}}

samples/client/petstore/elixir/.openapi-generator/FILES

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ lib/openapi_petstore/api/pet.ex
1111
lib/openapi_petstore/api/store.ex
1212
lib/openapi_petstore/api/user.ex
1313
lib/openapi_petstore/connection.ex
14+
lib/openapi_petstore/ecto_utils.ex
1415
lib/openapi_petstore/model/_foo_get_default_response.ex
1516
lib/openapi_petstore/model/_special_model_name_.ex
1617
lib/openapi_petstore/model/additional_properties_class.ex
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# NOTE: This file is auto generated by OpenAPI Generator 7.14.0-SNAPSHOT (https://openapi-generator.tech).
2+
# Do not edit this file manually.
3+
4+
defmodule OpenapiPetstore.EctoUtils do
5+
def cast_nested_map(changeset, field, schema) do
6+
values = Map.get(changeset.changes, field) || %{}
7+
8+
{result_map, errors} =
9+
Enum.reduce(values, {%{}, []}, fn {key, params}, {acc, errs} ->
10+
changeset = schema.changeset(struct(schema), params)
11+
12+
case Ecto.Changeset.apply_action(changeset, changeset.action || :insert) do
13+
{:ok, struct} ->
14+
{Map.put(acc, key, struct), errs}
15+
16+
{:error, nested_cs} ->
17+
{Map.put(acc, key, nested_cs), [{key, nested_cs} | errs]}
18+
end
19+
end)
20+
21+
changeset =
22+
Ecto.Changeset.put_change(changeset, field, result_map)
23+
24+
if errors == [] do
25+
changeset
26+
else
27+
Ecto.Changeset.add_error(changeset, field, "contains invalid nested entries")
28+
|> Map.put(:valid?, false)
29+
end
30+
end
31+
end

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ defmodule OpenapiPetstore.Model.MixedPropertiesAndAdditionalPropertiesClass do
2626
struct
2727
|> Ecto.Changeset.cast(params, [:uuid, :dateTime, :map])
2828
|> Ecto.Changeset.validate_required([])
29+
|> OpenapiPetstore.EctoUtils.cast_nested_map(:map, OpenapiPetstore.Model.Animal)
2930
end
3031
end
3132

0 commit comments

Comments
 (0)