diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/Swift6ClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/Swift6ClientCodegen.java index 5c665e8e397..b878feab772 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/Swift6ClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/Swift6ClientCodegen.java @@ -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); } diff --git a/modules/openapi-generator/src/main/resources/swift6/modelOneOf.mustache b/modules/openapi-generator/src/main/resources/swift6/modelOneOf.mustache index f3a5101de86..8df0b3ebb98 100644 --- a/modules/openapi-generator/src/main/resources/swift6/modelOneOf.mustache +++ b/modules/openapi-generator/src/main/resources/swift6/modelOneOf.mustache @@ -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}} diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/swift6/Swift6ClientCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/swift6/Swift6ClientCodegenTest.java index 80bc9d62d7b..25b4da37e84 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/swift6/Swift6ClientCodegenTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/swift6/Swift6ClientCodegenTest.java @@ -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 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(); + } + } } diff --git a/modules/openapi-generator/src/test/resources/bugs/issue_20560.yaml b/modules/openapi-generator/src/test/resources/bugs/issue_20560.yaml new file mode 100644 index 00000000000..be8ee6a517d --- /dev/null +++ b/modules/openapi-generator/src/test/resources/bugs/issue_20560.yaml @@ -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