Skip to content

[Swift5] fix issues causing stack overflow #7301

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

Merged
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 @@ -1002,15 +1002,15 @@ public Map<String, Object> postProcessOperationsWithModels(Map<String, Object> o
List<CodegenOperation> operations = (List<CodegenOperation>) objectMap.get("operation");
for (CodegenOperation operation : operations) {
for (CodegenParameter cp : operation.allParams) {
cp.vendorExtensions.put("x-swift-example", constructExampleCode(cp, modelMaps));
cp.vendorExtensions.put("x-swift-example", constructExampleCode(cp, modelMaps, new ExampleCodeGenerationContext()));
}
}
return objs;
}

public String constructExampleCode(CodegenParameter codegenParameter, HashMap<String, CodegenModel> modelMaps) {
public String constructExampleCode(CodegenParameter codegenParameter, HashMap<String, CodegenModel> modelMaps, ExampleCodeGenerationContext context) {
if (codegenParameter.isListContainer) { // array
return "[" + constructExampleCode(codegenParameter.items, modelMaps) + "]";
return "[" + constructExampleCode(codegenParameter.items, modelMaps, context) + "]";
} else if (codegenParameter.isMapContainer) { // TODO: map, file type
return "\"TODO\"";
} else if (languageSpecificPrimitives.contains(codegenParameter.dataType)) { // primitive type
Expand Down Expand Up @@ -1040,17 +1040,17 @@ public String constructExampleCode(CodegenParameter codegenParameter, HashMap<St
} else { // model
// look up the model
if (modelMaps.containsKey(codegenParameter.dataType)) {
return constructExampleCode(modelMaps.get(codegenParameter.dataType), modelMaps);
return constructExampleCode(modelMaps.get(codegenParameter.dataType), modelMaps, context);
} else {
//LOGGER.error("Error in constructing examples. Failed to look up the model " + codegenParameter.dataType);
return "TODO";
}
}
}

public String constructExampleCode(CodegenProperty codegenProperty, HashMap<String, CodegenModel> modelMaps) {
private String constructExampleCode(CodegenProperty codegenProperty, HashMap<String, CodegenModel> modelMaps, ExampleCodeGenerationContext context) {
if (codegenProperty.isListContainer) { // array
return "[" + constructExampleCode(codegenProperty.items, modelMaps) + "]";
return "[" + constructExampleCode(codegenProperty.items, modelMaps, context) + "]";
} else if (codegenProperty.isMapContainer) { // TODO: map, file type
return "\"TODO\"";
} else if (languageSpecificPrimitives.contains(codegenProperty.dataType)) { // primitive type
Expand Down Expand Up @@ -1080,23 +1080,59 @@ public String constructExampleCode(CodegenProperty codegenProperty, HashMap<Stri
} else {
// look up the model
if (modelMaps.containsKey(codegenProperty.dataType)) {
return constructExampleCode(modelMaps.get(codegenProperty.dataType), modelMaps);
return constructExampleCode(modelMaps.get(codegenProperty.dataType), modelMaps, context);
} else {
//LOGGER.error("Error in constructing examples. Failed to look up the model " + codegenProperty.dataType);
return "\"TODO\"";
}
}
}

public String constructExampleCode(CodegenModel codegenModel, HashMap<String, CodegenModel> modelMaps) {
String example;
example = codegenModel.name + "(";
List<String> propertyExamples = new ArrayList<>();
for (CodegenProperty codegenProperty : codegenModel.vars) {
propertyExamples.add(codegenProperty.name + ": " + constructExampleCode(codegenProperty, modelMaps));
private String constructExampleCode(CodegenModel codegenModel, HashMap<String, CodegenModel> modelMaps, ExampleCodeGenerationContext context) {
if (context.isTypeVisted(codegenModel.dataType)) {
String exampleCode = context.getExampleCode(codegenModel.dataType);
if (exampleCode != null) {
// Reuse already generated exampleCode
return exampleCode;
} else {
// Visited but no Example Code. Circuit Breaker --> No StackOverflow
return "{...}";
}
} else {
context.visitType(codegenModel.dataType);
String example = codegenModel.name + "(";
List<String> propertyExamples = new ArrayList<>();
for (CodegenProperty codegenProperty : codegenModel.vars) {
String propertyExample = constructExampleCode(codegenProperty, modelMaps, context);
propertyExamples.add(codegenProperty.name + ": " + propertyExample);
}
example += StringUtils.join(propertyExamples, ", ");
example += ")";

context.setExampleCode(codegenModel.dataType, example);
return example;
}
example += StringUtils.join(propertyExamples, ", ");
example += ")";
return example;
}

private static class ExampleCodeGenerationContext {

private Map<String, String> modelExampleCode = new HashMap<>();

public boolean isTypeVisted(String type) {
return modelExampleCode.containsKey(type);
}

public void visitType(String type) {
modelExampleCode.put(type, null);
}

public void setExampleCode(String type, String code) {
modelExampleCode.put(type, code);
}

public String getExampleCode(String type) {
return modelExampleCode.get(type);
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,21 @@
import org.openapitools.codegen.CodegenOperation;
import org.openapitools.codegen.DefaultCodegen;
import org.openapitools.codegen.TestUtils;
import org.openapitools.codegen.*;
import org.openapitools.codegen.config.CodegenConfigurator;
import org.openapitools.codegen.languages.Swift5ClientCodegen;
import org.testng.Assert;
import org.testng.annotations.Test;

import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.Arrays;
import java.util.List;


public class Swift5ClientCodegenTest {

Expand Down Expand Up @@ -118,6 +129,38 @@ public void dateTest() {
Assert.assertEquals(op.bodyParam.dataType, "Date");
}

@Test(description = "Bug example code generation", enabled = true)
public void crashSwift5ExampleCodeGenerationStackOverflowTest() throws IOException {
//final OpenAPI openAPI = TestUtils.parseFlattenSpec("src/test/resources/bugs/Swift5CodeGenerationStackOverflow#2966.yaml");
Path target = Files.createTempDirectory("test");
File output = target.toFile();
try {
final CodegenConfigurator configurator = new CodegenConfigurator()
.setGeneratorName("swift5")
.setValidateSpec(false)
.setInputSpec("src/test/resources/bugs/Swift5CodeGenerationStackOverflow#2966.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, "true");
generator.setGeneratorPropertyDefault(CodegenConstants.MODEL_DOCS, "true");
generator.setGeneratorPropertyDefault(CodegenConstants.APIS, "true");
generator.setGeneratorPropertyDefault(CodegenConstants.SUPPORTING_FILES, "true");
generator.setGeneratorPropertyDefault(CodegenConstants.API_DOCS, "true");
generator.setGeneratorPropertyDefault(CodegenConstants.ENABLE_POST_PROCESS_FILE, "true");

List<File> files = generator.opts(clientOptInput).generate();
Assert.assertTrue(files.size() > 0, "No files generated");
} finally {
output.delete();
}
}


@Test(enabled = true)
public void testDefaultPodAuthors() throws Exception {
// Given
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
openapi: 3.0.0
info:
description: This spec is mainly for testing Petstore server
version: 1.0.0
title: OpenAPI Petstore
license:
name: Apache-2.0
url: http://www.apache.org/licenses/LICENSE-2.0.html
tags:
- name: pet
description: Everything about your Pets
paths:
/pet:
post:
tags:
- pet
summary: Add a new pet to the store
description: ""
operationId: addPet
requestBody:
content:
application/json:
schema:
$ref: "#/components/schemas/Pet"
description: Pet object that needs to be added to the store
required: true
responses:
"400":
description: Invalid input
servers:
- url: http://petstore.swagger.io:80/v2
components:
schemas:
Pet:
type: object
required:
- name
properties:
id:
type: integer
format: int64
x-is-unique: true
name:
type: string
example: doggie
pets:
type: array
items:
$ref: "#/components/schemas/Pet"