Skip to content

Commit e553180

Browse files
authored
[swift][bug] Fix generation of multiple cases with associated values, fix #20560 (#20568)
* Cases previously generated as "case type[Int]([Int])" become "case typeArrayOfInt([Int])"
1 parent 187af2e commit e553180

File tree

4 files changed

+226
-3
lines changed

4 files changed

+226
-3
lines changed

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

+17
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
package org.openapitools.codegen.languages;
1919

20+
import com.samskivert.mustache.Mustache;
2021
import io.swagger.v3.oas.models.media.Schema;
2122
import lombok.Getter;
2223
import lombok.Setter;
@@ -608,6 +609,11 @@ public void processOpts() {
608609
}
609610
additionalProperties.put(COMBINE_DEFERRED, combineDeferred);
610611

612+
additionalProperties.put("transformArrayType", (Mustache.Lambda) (frag, out) -> {
613+
String type = frag.execute();
614+
out.write(transformArrayTypeName(type));
615+
});
616+
611617
// infrastructure destination folder
612618
final String infrastructureFolder = sourceFolder + File.separator + "Infrastructure";
613619

@@ -1090,6 +1096,17 @@ public String toEnumVarName(String name, String datatype) {
10901096
LOWERCASE_FIRST_LETTER);
10911097
}
10921098

1099+
public String transformArrayTypeName(String type) {
1100+
if (!type.startsWith("[") || !type.endsWith("]")) {
1101+
return type;
1102+
}
1103+
String innerType = type.substring(1, type.length() - 1);
1104+
String transformed = transformArrayTypeName(innerType);
1105+
1106+
return "ArrayOf" + transformed;
1107+
}
1108+
1109+
10931110
private Boolean isLanguageSpecificType(String name) {
10941111
return languageSpecificPrimitives.contains(name);
10951112
}

modules/openapi-generator/src/main/resources/swift6/modelOneOf.mustache

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}public{{/nonPublicApi}} enum {{classname}}: {{^useClasses}}Sendable, {{/useClasses}}{{#useClasses}}{{#readonlyProperties}}Sendable, {{/readonlyProperties}}{{/useClasses}}{{#useVapor}}Content{{/useVapor}}{{^useVapor}}Codable, JSONEncodable{{#vendorExtensions.x-swift-hashable}}, Hashable{{/vendorExtensions.x-swift-hashable}}{{/useVapor}} {
22
{{#oneOf}}
3-
case type{{.}}({{.}})
3+
case type{{#transformArrayType}}{{.}}{{/transformArrayType}}({{.}})
44
{{/oneOf}}
55
{{#oneOfUnknownDefaultCase}}
66
case unknownDefaultOpenApi
@@ -10,7 +10,7 @@
1010
var container = encoder.singleValueContainer()
1111
switch self {
1212
{{#oneOf}}
13-
case .type{{.}}(let value):
13+
case .type{{#transformArrayType}}{{.}}{{/transformArrayType}}(let value):
1414
try container.encode(value)
1515
{{/oneOf}}
1616
{{#oneOfUnknownDefaultCase}}
@@ -29,7 +29,7 @@
2929
{{^-first}}
3030
} else if let value = try? container.decode({{.}}.self) {
3131
{{/-first}}
32-
self = .type{{.}}(value)
32+
self = .type{{#transformArrayType}}{{.}}{{/transformArrayType}}(value)
3333
{{/oneOf}}
3434
} else {
3535
{{#oneOfUnknownDefaultCase}}

modules/openapi-generator/src/test/java/org/openapitools/codegen/swift6/Swift6ClientCodegenTest.java

+44
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,13 @@ public void testSingleWordLowercase() throws Exception {
6565
Assert.assertEquals(swiftCodegen.toEnumVarName("value", null), "value");
6666
}
6767

68+
@Test(enabled = true)
69+
public void testArrayTypeTransformations() throws Exception {
70+
Assert.assertEquals(swiftCodegen.transformArrayTypeName("[Int]"), "ArrayOfInt");
71+
Assert.assertEquals(swiftCodegen.transformArrayTypeName("[[Int]]"), "ArrayOfArrayOfInt");
72+
Assert.assertEquals(swiftCodegen.transformArrayTypeName("String"), "String");
73+
}
74+
6875
@Test(enabled = true)
6976
public void testCapitalsWithUnderscore() throws Exception {
7077
Assert.assertEquals(swiftCodegen.toEnumVarName("ENTRY_NAME", null), "entryName");
@@ -319,4 +326,41 @@ public void oneOfFormParameterTest() {
319326

320327
}
321328

329+
@Test(description = "Array type name transformation in oneOf schema", enabled = true)
330+
public void oneOfArrayTypeNamesTest() throws IOException {
331+
Path target = Files.createTempDirectory("test");
332+
File output = target.toFile();
333+
try {
334+
final CodegenConfigurator configurator = new CodegenConfigurator()
335+
.setGeneratorName("swift6")
336+
.setValidateSpec(false)
337+
.setInputSpec("src/test/resources/bugs/issue_20560.yaml")
338+
.setEnablePostProcessFile(true)
339+
.setOutputDir(target.toAbsolutePath().toString());
340+
341+
final ClientOptInput clientOptInput = configurator.toClientOptInput();
342+
DefaultGenerator generator = new DefaultGenerator(false);
343+
344+
generator.setGeneratorPropertyDefault(CodegenConstants.MODELS, "true");
345+
generator.setGeneratorPropertyDefault(CodegenConstants.MODEL_TESTS, "false");
346+
generator.setGeneratorPropertyDefault(CodegenConstants.MODEL_DOCS, "false");
347+
generator.setGeneratorPropertyDefault(CodegenConstants.APIS, "false");
348+
generator.setGeneratorPropertyDefault(CodegenConstants.SUPPORTING_FILES, "false");
349+
350+
List<File> files = generator.opts(clientOptInput).generate();
351+
352+
File modelFile = files.stream()
353+
.filter(f -> f.getName().contains("CreateCompletionRequestPrompt"))
354+
.findFirst()
355+
.get();
356+
357+
String content = Files.readString(modelFile.toPath());
358+
Assert.assertTrue(content.contains("case typeString(String)"));
359+
Assert.assertTrue(content.contains("case typeArrayOfInt([Int])"));
360+
Assert.assertTrue(content.contains("case typeArrayOfString([String])"));
361+
Assert.assertTrue(content.contains("case typeArrayOfArrayOfInt([[Int]])"));
362+
} finally {
363+
output.deleteOnExit();
364+
}
365+
}
322366
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
openapi: 3.0.0
2+
info:
3+
title: Completions API
4+
version: 1.0.0
5+
description: API for generating text completions
6+
7+
paths:
8+
/completions:
9+
post:
10+
summary: Create a completion
11+
operationId: createCompletion
12+
requestBody:
13+
required: true
14+
content:
15+
application/json:
16+
schema:
17+
$ref: '#/components/schemas/CreateCompletionRequest'
18+
responses:
19+
'200':
20+
description: Successful completion response
21+
content:
22+
application/json:
23+
schema:
24+
type: object
25+
# Note: Full response schema would be defined here
26+
27+
components:
28+
schemas:
29+
CreateCompletionRequest:
30+
type: object
31+
required:
32+
- model
33+
- prompt
34+
properties:
35+
model:
36+
description: ID of the model to use
37+
anyOf:
38+
- type: string
39+
- type: string
40+
enum:
41+
- gpt-3.5-turbo-instruct
42+
- davinci-002
43+
- babbage-002
44+
prompt:
45+
description: The prompt(s) to generate completions for
46+
default: <|endoftext|>
47+
oneOf:
48+
- type: string
49+
default: ""
50+
example: This is a test.
51+
- type: array
52+
items:
53+
type: string
54+
default: ""
55+
example: This is a test.
56+
- type: array
57+
minItems: 1
58+
items:
59+
type: integer
60+
example: "[1212, 318, 257, 1332, 13]"
61+
- type: array
62+
minItems: 1
63+
items:
64+
type: array
65+
minItems: 1
66+
items:
67+
type: integer
68+
example: "[[1212, 318, 257, 1332, 13]]"
69+
max_tokens:
70+
type: integer
71+
minimum: 0
72+
default: 16
73+
nullable: true
74+
description: The maximum number of tokens that can be generated in the completion
75+
temperature:
76+
type: number
77+
minimum: 0
78+
maximum: 2
79+
default: 1
80+
nullable: true
81+
description: What sampling temperature to use, between 0 and 2
82+
top_p:
83+
type: number
84+
minimum: 0
85+
maximum: 1
86+
default: 1
87+
nullable: true
88+
description: An alternative to sampling with temperature, called nucleus sampling
89+
n:
90+
type: integer
91+
minimum: 1
92+
maximum: 128
93+
default: 1
94+
nullable: true
95+
description: How many completions to generate for each prompt
96+
stream:
97+
type: boolean
98+
nullable: true
99+
default: false
100+
description: Whether to stream back partial progress
101+
logprobs:
102+
type: integer
103+
minimum: 0
104+
maximum: 5
105+
nullable: true
106+
description: Include the log probabilities on the most likely output tokens
107+
echo:
108+
type: boolean
109+
default: false
110+
nullable: true
111+
description: Echo back the prompt in addition to the completion
112+
stop:
113+
description: Up to 4 sequences where the API will stop generating further tokens
114+
nullable: true
115+
oneOf:
116+
- type: string
117+
example: "\n"
118+
- type: array
119+
minItems: 1
120+
maxItems: 4
121+
items:
122+
type: string
123+
presence_penalty:
124+
type: number
125+
default: 0
126+
minimum: -2
127+
maximum: 2
128+
nullable: true
129+
description: Number between -2.0 and 2.0 for presence-based penalty
130+
frequency_penalty:
131+
type: number
132+
default: 0
133+
minimum: -2
134+
maximum: 2
135+
nullable: true
136+
description: Number between -2.0 and 2.0 for frequency-based penalty
137+
best_of:
138+
type: integer
139+
default: 1
140+
minimum: 0
141+
maximum: 20
142+
nullable: true
143+
description: Number of completions to generate server-side
144+
logit_bias:
145+
type: object
146+
nullable: true
147+
additionalProperties:
148+
type: integer
149+
description: Modify the likelihood of specified tokens appearing
150+
user:
151+
type: string
152+
example: user-1234
153+
description: A unique identifier representing your end-user
154+
seed:
155+
type: integer
156+
format: int64
157+
nullable: true
158+
description: Seed for deterministic sampling
159+
suffix:
160+
type: string
161+
nullable: true
162+
description: The suffix that comes after a completion of inserted text

0 commit comments

Comments
 (0)