[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])"
This commit is contained in:
Alejandro Ramirez 2025-02-01 23:35:50 +01:00 committed by GitHub
parent 187af2ec4e
commit e553180c06
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 226 additions and 3 deletions

View File

@ -17,6 +17,7 @@
package org.openapitools.codegen.languages;
import com.samskivert.mustache.Mustache;
import io.swagger.v3.oas.models.media.Schema;
import lombok.Getter;
import lombok.Setter;
@ -608,6 +609,11 @@ public class Swift6ClientCodegen extends DefaultCodegen implements CodegenConfig
}
additionalProperties.put(COMBINE_DEFERRED, combineDeferred);
additionalProperties.put("transformArrayType", (Mustache.Lambda) (frag, out) -> {
String type = frag.execute();
out.write(transformArrayTypeName(type));
});
// infrastructure destination folder
final String infrastructureFolder = sourceFolder + File.separator + "Infrastructure";
@ -1090,6 +1096,17 @@ public class Swift6ClientCodegen extends DefaultCodegen implements CodegenConfig
LOWERCASE_FIRST_LETTER);
}
public String transformArrayTypeName(String type) {
if (!type.startsWith("[") || !type.endsWith("]")) {
return type;
}
String innerType = type.substring(1, type.length() - 1);
String transformed = transformArrayTypeName(innerType);
return "ArrayOf" + transformed;
}
private Boolean isLanguageSpecificType(String name) {
return languageSpecificPrimitives.contains(name);
}

View File

@ -1,6 +1,6 @@
{{#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}} {
{{#oneOf}}
case type{{.}}({{.}})
case type{{#transformArrayType}}{{.}}{{/transformArrayType}}({{.}})
{{/oneOf}}
{{#oneOfUnknownDefaultCase}}
case unknownDefaultOpenApi
@ -10,7 +10,7 @@
var container = encoder.singleValueContainer()
switch self {
{{#oneOf}}
case .type{{.}}(let value):
case .type{{#transformArrayType}}{{.}}{{/transformArrayType}}(let value):
try container.encode(value)
{{/oneOf}}
{{#oneOfUnknownDefaultCase}}
@ -29,7 +29,7 @@
{{^-first}}
} else if let value = try? container.decode({{.}}.self) {
{{/-first}}
self = .type{{.}}(value)
self = .type{{#transformArrayType}}{{.}}{{/transformArrayType}}(value)
{{/oneOf}}
} else {
{{#oneOfUnknownDefaultCase}}

View File

@ -65,6 +65,13 @@ public class Swift6ClientCodegenTest {
Assert.assertEquals(swiftCodegen.toEnumVarName("value", null), "value");
}
@Test(enabled = true)
public void testArrayTypeTransformations() throws Exception {
Assert.assertEquals(swiftCodegen.transformArrayTypeName("[Int]"), "ArrayOfInt");
Assert.assertEquals(swiftCodegen.transformArrayTypeName("[[Int]]"), "ArrayOfArrayOfInt");
Assert.assertEquals(swiftCodegen.transformArrayTypeName("String"), "String");
}
@Test(enabled = true)
public void testCapitalsWithUnderscore() throws Exception {
Assert.assertEquals(swiftCodegen.toEnumVarName("ENTRY_NAME", null), "entryName");
@ -319,4 +326,41 @@ public class Swift6ClientCodegenTest {
}
@Test(description = "Array type name transformation in oneOf schema", enabled = true)
public void oneOfArrayTypeNamesTest() throws IOException {
Path target = Files.createTempDirectory("test");
File output = target.toFile();
try {
final CodegenConfigurator configurator = new CodegenConfigurator()
.setGeneratorName("swift6")
.setValidateSpec(false)
.setInputSpec("src/test/resources/bugs/issue_20560.yaml")
.setEnablePostProcessFile(true)
.setOutputDir(target.toAbsolutePath().toString());
final ClientOptInput clientOptInput = configurator.toClientOptInput();
DefaultGenerator generator = new DefaultGenerator(false);
generator.setGeneratorPropertyDefault(CodegenConstants.MODELS, "true");
generator.setGeneratorPropertyDefault(CodegenConstants.MODEL_TESTS, "false");
generator.setGeneratorPropertyDefault(CodegenConstants.MODEL_DOCS, "false");
generator.setGeneratorPropertyDefault(CodegenConstants.APIS, "false");
generator.setGeneratorPropertyDefault(CodegenConstants.SUPPORTING_FILES, "false");
List<File> files = generator.opts(clientOptInput).generate();
File modelFile = files.stream()
.filter(f -> f.getName().contains("CreateCompletionRequestPrompt"))
.findFirst()
.get();
String content = Files.readString(modelFile.toPath());
Assert.assertTrue(content.contains("case typeString(String)"));
Assert.assertTrue(content.contains("case typeArrayOfInt([Int])"));
Assert.assertTrue(content.contains("case typeArrayOfString([String])"));
Assert.assertTrue(content.contains("case typeArrayOfArrayOfInt([[Int]])"));
} finally {
output.deleteOnExit();
}
}
}

View File

@ -0,0 +1,162 @@
openapi: 3.0.0
info:
title: Completions API
version: 1.0.0
description: API for generating text completions
paths:
/completions:
post:
summary: Create a completion
operationId: createCompletion
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateCompletionRequest'
responses:
'200':
description: Successful completion response
content:
application/json:
schema:
type: object
# Note: Full response schema would be defined here
components:
schemas:
CreateCompletionRequest:
type: object
required:
- model
- prompt
properties:
model:
description: ID of the model to use
anyOf:
- type: string
- type: string
enum:
- gpt-3.5-turbo-instruct
- davinci-002
- babbage-002
prompt:
description: The prompt(s) to generate completions for
default: <|endoftext|>
oneOf:
- type: string
default: ""
example: This is a test.
- type: array
items:
type: string
default: ""
example: This is a test.
- type: array
minItems: 1
items:
type: integer
example: "[1212, 318, 257, 1332, 13]"
- type: array
minItems: 1
items:
type: array
minItems: 1
items:
type: integer
example: "[[1212, 318, 257, 1332, 13]]"
max_tokens:
type: integer
minimum: 0
default: 16
nullable: true
description: The maximum number of tokens that can be generated in the completion
temperature:
type: number
minimum: 0
maximum: 2
default: 1
nullable: true
description: What sampling temperature to use, between 0 and 2
top_p:
type: number
minimum: 0
maximum: 1
default: 1
nullable: true
description: An alternative to sampling with temperature, called nucleus sampling
n:
type: integer
minimum: 1
maximum: 128
default: 1
nullable: true
description: How many completions to generate for each prompt
stream:
type: boolean
nullable: true
default: false
description: Whether to stream back partial progress
logprobs:
type: integer
minimum: 0
maximum: 5
nullable: true
description: Include the log probabilities on the most likely output tokens
echo:
type: boolean
default: false
nullable: true
description: Echo back the prompt in addition to the completion
stop:
description: Up to 4 sequences where the API will stop generating further tokens
nullable: true
oneOf:
- type: string
example: "\n"
- type: array
minItems: 1
maxItems: 4
items:
type: string
presence_penalty:
type: number
default: 0
minimum: -2
maximum: 2
nullable: true
description: Number between -2.0 and 2.0 for presence-based penalty
frequency_penalty:
type: number
default: 0
minimum: -2
maximum: 2
nullable: true
description: Number between -2.0 and 2.0 for frequency-based penalty
best_of:
type: integer
default: 1
minimum: 0
maximum: 20
nullable: true
description: Number of completions to generate server-side
logit_bias:
type: object
nullable: true
additionalProperties:
type: integer
description: Modify the likelihood of specified tokens appearing
user:
type: string
example: user-1234
description: A unique identifier representing your end-user
seed:
type: integer
format: int64
nullable: true
description: Seed for deterministic sampling
suffix:
type: string
nullable: true
description: The suffix that comes after a completion of inserted text