Skip to content

Commit 0462bed

Browse files
authored
fix: wrong typespec generation for all-of with single ref (#21139)
1 parent d02c0f4 commit 0462bed

File tree

15 files changed

+169
-154
lines changed

15 files changed

+169
-154
lines changed

docs/generators/elixir.md

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -45,19 +45,18 @@ These options may be applied as additional-properties (cli) or configOptions (pl
4545
## LANGUAGE PRIMITIVES
4646

4747
<ul class="column-ul">
48-
<li>AnyType</li>
49-
<li>Atom</li>
50-
<li>Boolean</li>
51-
<li>Decimal</li>
52-
<li>Float</li>
53-
<li>Integer</li>
54-
<li>List</li>
55-
<li>Map</li>
56-
<li>PID</li>
57-
<li>String</li>
58-
<li>Tuple</li>
48+
<li>Date.t</li>
49+
<li>DateTime.t</li>
50+
<li>String.t</li>
5951
<li>any()</li>
52+
<li>binary()</li>
53+
<li>boolean()</li>
54+
<li>float()</li>
55+
<li>integer()</li>
56+
<li>list()</li>
6057
<li>map()</li>
58+
<li>nil</li>
59+
<li>number()</li>
6160
</ul>
6261

6362
## RESERVED WORDS

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

Lines changed: 59 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -180,44 +180,55 @@ public ElixirClientCodegen() {
180180
*/
181181
languageSpecificPrimitives = new HashSet<>(
182182
Arrays.asList(
183-
"Integer",
184-
"Float",
185-
"Decimal",
186-
"Boolean",
187-
"String",
188-
"List",
189-
"Atom",
190-
"Map",
191-
"AnyType",
192-
"Tuple",
193-
"PID",
194-
// This is a workaround, since the DefaultCodeGen uses our elixir TypeSpec
195-
// datetype to evaluate the primitive
183+
"integer()",
184+
"float()",
185+
"number()",
186+
"boolean()",
187+
"String.t",
188+
"Date.t",
189+
"DateTime.t",
190+
"binary()",
191+
"list()",
196192
"map()",
197-
"any()"));
193+
"any()",
194+
"nil"));
198195

199196
// ref:
200197
// https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#data-types
201198
typeMapping = new HashMap<>();
202-
typeMapping.put("integer", "Integer");
203-
typeMapping.put("long", "Integer");
204-
typeMapping.put("number", "Float");
205-
typeMapping.put("float", "Float");
206-
typeMapping.put("double", "Float");
207-
typeMapping.put("string", "String");
208-
typeMapping.put("byte", "Integer");
209-
typeMapping.put("boolean", "Boolean");
210-
typeMapping.put("Date", "Date");
211-
typeMapping.put("DateTime", "DateTime");
212-
typeMapping.put("file", "String");
213-
typeMapping.put("map", "Map");
214-
typeMapping.put("array", "List");
215-
typeMapping.put("list", "List");
216-
typeMapping.put("object", "Map");
217-
typeMapping.put("binary", "String");
218-
typeMapping.put("ByteArray", "String");
219-
typeMapping.put("UUID", "String");
220-
typeMapping.put("URI", "String");
199+
// primitive types
200+
typeMapping.put("string", "String.t");
201+
typeMapping.put("number", "number()");
202+
typeMapping.put("integer", "integer()");
203+
typeMapping.put("boolean", "boolean()");
204+
typeMapping.put("array", "list()");
205+
typeMapping.put("object", "map()");
206+
typeMapping.put("map", "map()");
207+
typeMapping.put("null", "nil");
208+
// string formats
209+
typeMapping.put("byte", "String.t");
210+
typeMapping.put("binary", "binary()");
211+
typeMapping.put("password", "String.t");
212+
typeMapping.put("uuid", "String.t");
213+
typeMapping.put("email", "String.t");
214+
typeMapping.put("uri", "String.t");
215+
typeMapping.put("file", "String.t");
216+
// integer formats
217+
typeMapping.put("int32", "integer()");
218+
typeMapping.put("int64", "integer()");
219+
typeMapping.put("long", "integer()");
220+
// float formats
221+
typeMapping.put("float", "float()");
222+
typeMapping.put("double", "float()");
223+
typeMapping.put("decimal", "float()");
224+
// date-time formats
225+
typeMapping.put("date", "Date.t");
226+
typeMapping.put("date-time", "DateTime.t");
227+
// other
228+
typeMapping.put("ByteArray", "binary()");
229+
typeMapping.put("DateTime", "DateTime.t");
230+
typeMapping.put("UUID", "String.t");
231+
221232

222233
cliOptions.add(new CliOption(CodegenConstants.INVOKER_PACKAGE,
223234
"The main namespace to use for all classes. e.g. Yay.Pets"));
@@ -570,49 +581,19 @@ public String toOperationId(String operationId) {
570581
*/
571582
@Override
572583
public String getTypeDeclaration(Schema p) {
573-
if (ModelUtils.isAnyType(p)) {
574-
return "any()";
575-
} else if(ModelUtils.isFreeFormObject(p, null)) {
576-
return "%{optional(String.t) => any()}";
577-
} else if (ModelUtils.isArraySchema(p)) {
584+
if (ModelUtils.isArraySchema(p)) {
578585
Schema inner = ModelUtils.getSchemaItems(p);
579586
return "[" + getTypeDeclaration(inner) + "]";
580587
} else if (ModelUtils.isMapSchema(p)) {
581588
Schema inner = ModelUtils.getAdditionalProperties(p);
582589
return "%{optional(String.t) => " + getTypeDeclaration(inner) + "}";
583-
} else if (ModelUtils.isPasswordSchema(p)) {
584-
return "String.t";
585-
} else if (ModelUtils.isEmailSchema(p)) {
586-
return "String.t";
587-
} else if (ModelUtils.isByteArraySchema(p)) {
588-
return "binary()";
589-
} else if (ModelUtils.isUUIDSchema(p)) {
590-
return "String.t";
591-
} else if (ModelUtils.isDateSchema(p)) {
592-
return "Date.t";
593-
} else if (ModelUtils.isDateTimeSchema(p)) {
594-
return "DateTime.t";
595-
} else if (ModelUtils.isObjectSchema(p)) {
596-
return "map()";
597-
} else if (ModelUtils.isIntegerSchema(p)) {
598-
return "integer()";
599-
} else if (ModelUtils.isNumberSchema(p)) {
600-
return "float()";
601-
} else if (ModelUtils.isBinarySchema(p) || ModelUtils.isFileSchema(p)) {
602-
return "String.t";
603-
} else if (ModelUtils.isBooleanSchema(p)) {
604-
return "boolean()";
605590
} else if (!StringUtils.isEmpty(p.get$ref())) {
606-
switch (super.getTypeDeclaration(p)) {
607-
case "String":
608-
return "String.t";
609-
default:
610-
return this.moduleName + ".Model." + super.getTypeDeclaration(p) + ".t";
591+
String refType = super.getTypeDeclaration(p);
592+
if (languageSpecificPrimitives.contains(refType)) {
593+
return refType;
594+
} else {
595+
return this.moduleName + ".Model." + refType + ".t";
611596
}
612-
} else if (ModelUtils.isFileSchema(p)) {
613-
return "String.t";
614-
} else if (ModelUtils.isStringSchema(p)) {
615-
return "String.t";
616597
} else if (p.getType() == null) {
617598
return "any()";
618599
}
@@ -630,14 +611,11 @@ public String getTypeDeclaration(Schema p) {
630611
@Override
631612
public String getSchemaType(Schema p) {
632613
String openAPIType = super.getSchemaType(p);
633-
String type = null;
634614
if (typeMapping.containsKey(openAPIType)) {
635-
type = typeMapping.get(openAPIType);
636-
if (languageSpecificPrimitives.contains(type))
637-
return toModelName(type);
638-
} else
639-
type = openAPIType;
640-
return toModelName(type);
615+
return typeMapping.get(openAPIType);
616+
} else {
617+
return toModelName(openAPIType);
618+
}
641619
}
642620

643621
class ExtendedCodegenResponse extends CodegenResponse {
@@ -784,24 +762,6 @@ public ExtendedCodegenOperation(CodegenOperation o) {
784762
this.operationIdCamelCase = o.operationIdCamelCase;
785763
}
786764

787-
private void translateBaseType(StringBuilder returnEntry, String baseType) {
788-
switch (baseType) {
789-
case "AnyType":
790-
returnEntry.append("any()");
791-
break;
792-
case "Boolean":
793-
returnEntry.append("boolean()");
794-
break;
795-
case "Float":
796-
returnEntry.append("float()");
797-
break;
798-
default:
799-
returnEntry.append(baseType);
800-
returnEntry.append(".t");
801-
break;
802-
}
803-
}
804-
805765
public String typespec() {
806766
StringBuilder sb = new StringBuilder("@spec ");
807767
sb.append(underscore(operationId));
@@ -819,16 +779,10 @@ public String typespec() {
819779
for (CodegenResponse response : this.responses) {
820780
ExtendedCodegenResponse exResponse = (ExtendedCodegenResponse) response;
821781
StringBuilder returnEntry = new StringBuilder();
822-
if (exResponse.baseType == null) {
823-
returnEntry.append("nil");
824-
} else if (exResponse.containerType == null) { // not container (array, map, set)
825-
returnEntry.append(normalizeTypeName(exResponse.dataType, exResponse.primitiveType));
782+
if (exResponse.schema != null) {
783+
returnEntry.append(getTypeDeclaration((Schema) exResponse.schema));
826784
} else {
827-
if (exResponse.containerType.equals("array") || exResponse.containerType.equals("set")) {
828-
returnEntry.append(exResponse.dataType);
829-
} else if (exResponse.containerType.equals("map")) {
830-
returnEntry.append("map()");
831-
}
785+
returnEntry.append(normalizeTypeName(exResponse.dataType, exResponse.primitiveType));
832786
}
833787
uniqueResponseTypes.add(returnEntry.toString());
834788
}
@@ -845,14 +799,11 @@ private String normalizeTypeName(String baseType, boolean isPrimitive) {
845799
if (baseType == null) {
846800
return "nil";
847801
}
848-
if (isPrimitive || "String.t".equals(baseType)) {
802+
if (isPrimitive || languageSpecificPrimitives.contains(baseType)) {
849803
return baseType;
850804
}
851805
if (!baseType.startsWith(moduleName + ".Model.")) {
852-
baseType = moduleName + ".Model." + baseType;
853-
}
854-
if (!baseType.endsWith(".t")) {
855-
baseType += ".t";
806+
baseType = moduleName + ".Model." + baseType + ".t";
856807
}
857808
return baseType;
858809
}

modules/openapi-generator/src/test/resources/3_0/elixir/petstore-with-fake-endpoints-models-for-testing.yaml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1337,6 +1337,29 @@ paths:
13371337
responses:
13381338
200:
13391339
description: The instance started successfully
1340+
/fake/all-of-with-local-single-ref:
1341+
get:
1342+
tags:
1343+
- fake
1344+
responses:
1345+
200:
1346+
description: Successful operation
1347+
content:
1348+
application/json:
1349+
schema:
1350+
allOf:
1351+
- $ref: "#/components/schemas/Foo"
1352+
/fake/all-of-with-remote-single-ref:
1353+
get:
1354+
tags:
1355+
- fake
1356+
responses:
1357+
200:
1358+
description: Successful operation
1359+
content:
1360+
application/json:
1361+
schema:
1362+
$ref: "#/components/schemas/AllOfWithSingleRef"
13401363
servers:
13411364
- url: "http://{server}.swagger.io:{port}/v2"
13421365
description: petstore server

samples/client/petstore/elixir/lib/openapi_petstore/api/fake.ex

Lines changed: 60 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,60 @@ defmodule OpenapiPetstore.Api.Fake do
99
alias OpenapiPetstore.Connection
1010
import OpenapiPetstore.RequestBuilder
1111

12+
@doc """
13+
14+
### Parameters
15+
16+
- `connection` (OpenapiPetstore.Connection): Connection to server
17+
- `opts` (keyword): Optional parameters
18+
19+
### Returns
20+
21+
- `{:ok, OpenapiPetstore.Model.Foo.t}` on success
22+
- `{:error, Tesla.Env.t}` on failure
23+
"""
24+
@spec fake_all_of_with_local_single_ref_get(Tesla.Env.client, keyword()) :: {:ok, any()} | {:error, Tesla.Env.t}
25+
def fake_all_of_with_local_single_ref_get(connection, _opts \\ []) do
26+
request =
27+
%{}
28+
|> method(:get)
29+
|> url("/fake/all-of-with-local-single-ref")
30+
|> Enum.into([])
31+
32+
connection
33+
|> Connection.request(request)
34+
|> evaluate_response([
35+
{200, OpenapiPetstore.Model.Foo}
36+
])
37+
end
38+
39+
@doc """
40+
41+
### Parameters
42+
43+
- `connection` (OpenapiPetstore.Connection): Connection to server
44+
- `opts` (keyword): Optional parameters
45+
46+
### Returns
47+
48+
- `{:ok, OpenapiPetstore.Model.AllOfWithSingleRef.t}` on success
49+
- `{:error, Tesla.Env.t}` on failure
50+
"""
51+
@spec fake_all_of_with_remote_single_ref_get(Tesla.Env.client, keyword()) :: {:ok, OpenapiPetstore.Model.AllOfWithSingleRef.t} | {:error, Tesla.Env.t}
52+
def fake_all_of_with_remote_single_ref_get(connection, _opts \\ []) do
53+
request =
54+
%{}
55+
|> method(:get)
56+
|> url("/fake/all-of-with-remote-single-ref")
57+
|> Enum.into([])
58+
59+
connection
60+
|> Connection.request(request)
61+
|> evaluate_response([
62+
{200, OpenapiPetstore.Model.AllOfWithSingleRef}
63+
])
64+
end
65+
1266
@doc """
1367
for Java apache and Java native, test toUrlQueryString for maps with BegDecimal keys
1468
@@ -180,14 +234,14 @@ defmodule OpenapiPetstore.Api.Fake do
180234
181235
- `connection` (OpenapiPetstore.Connection): Connection to server
182236
- `opts` (keyword): Optional parameters
183-
- `:body` (float()): Input number as post body
237+
- `:body` (number()): Input number as post body
184238
185239
### Returns
186240
187-
- `{:ok, float()}` on success
241+
- `{:ok, number()}` on success
188242
- `{:error, Tesla.Env.t}` on failure
189243
"""
190-
@spec fake_outer_number_serialize(Tesla.Env.client, keyword()) :: {:ok, float()} | {:error, Tesla.Env.t}
244+
@spec fake_outer_number_serialize(Tesla.Env.client, keyword()) :: {:ok, number()} | {:error, Tesla.Env.t}
191245
def fake_outer_number_serialize(connection, opts \\ []) do
192246
optional_params = %{
193247
:body => :body
@@ -464,7 +518,7 @@ defmodule OpenapiPetstore.Api.Fake do
464518
### Parameters
465519
466520
- `connection` (OpenapiPetstore.Connection): Connection to server
467-
- `number` (float()): None
521+
- `number` (number()): None
468522
- `double` (float()): None
469523
- `pattern_without_delimiter` (String.t): None
470524
- `byte` (binary()): None
@@ -485,7 +539,7 @@ defmodule OpenapiPetstore.Api.Fake do
485539
- `{:ok, nil}` on success
486540
- `{:error, Tesla.Env.t}` on failure
487541
"""
488-
@spec test_endpoint_parameters(Tesla.Env.client, float(), float(), String.t, binary(), keyword()) :: {:ok, nil} | {:error, Tesla.Env.t}
542+
@spec test_endpoint_parameters(Tesla.Env.client, number(), float(), String.t, binary(), keyword()) :: {:ok, nil} | {:error, Tesla.Env.t}
489543
def test_endpoint_parameters(connection, number, double, pattern_without_delimiter, byte, opts \\ []) do
490544
optional_params = %{
491545
:integer => :form,
@@ -623,7 +677,7 @@ defmodule OpenapiPetstore.Api.Fake do
623677
### Parameters
624678
625679
- `connection` (OpenapiPetstore.Connection): Connection to server
626-
- `body` (%{optional(String.t) => any()}): request body
680+
- `body` (map()): request body
627681
- `opts` (keyword): Optional parameters
628682
629683
### Returns

0 commit comments

Comments
 (0)