Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[swift][bug] Fix generation of cases with associated values (#20560) #20568

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -608,6 +609,11 @@ public void processOpts() {
}
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";

Expand Down Expand Up @@ -1090,6 +1096,17 @@ public String toEnumVarName(String name, String datatype) {
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);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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}}
Expand All @@ -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}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,13 @@ public void testSingleWordLowercase() throws Exception {
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");
Expand Down Expand Up @@ -319,4 +326,41 @@ public void oneOfFormParameterTest() {

}

@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();
}
}
}
162 changes: 162 additions & 0 deletions modules/openapi-generator/src/test/resources/bugs/issue_20560.yaml
Original file line number Diff line number Diff line change
@@ -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