Bugfix swift5 code generation 2966 (#7301)

* Bugfix StackOverflow Crash in Swift5 Code Generation (#2966)

* Bugfix StackOverflow Crash in Swift5 Code Generation (#2966)

* Bugfix StackOverflow Crash in Swift5 Code Generation (#2966)
This commit is contained in:
Markus Wandersee
2020-09-06 16:05:16 +02:00
committed by GitHub
parent 681e821b71
commit 06434fcfce
3 changed files with 144 additions and 16 deletions

View File

@@ -1003,15 +1003,15 @@ public class Swift5ClientCodegen extends DefaultCodegen implements CodegenConfig
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
@@ -1041,7 +1041,7 @@ public class Swift5ClientCodegen extends DefaultCodegen implements CodegenConfig
} 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";
@@ -1049,9 +1049,9 @@ public class Swift5ClientCodegen extends DefaultCodegen implements CodegenConfig
}
}
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
@@ -1081,7 +1081,7 @@ public class Swift5ClientCodegen extends DefaultCodegen implements CodegenConfig
} 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\"";
@@ -1089,15 +1089,51 @@ public class Swift5ClientCodegen extends DefaultCodegen implements CodegenConfig
}
}
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);
}
}
}

View File

@@ -22,10 +22,21 @@ import io.swagger.v3.oas.models.Operation;
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 {
@@ -118,6 +129,38 @@ public class Swift5ClientCodegenTest {
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

View File

@@ -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"