forked from loafle/openapi-generator-original
[GoClient] Fixed StringIndexOutOfBoundsException when multiple properties has the same required $ref oneOf objects (#11973)
* Fixed StringIndexOutOfBoundsException when the object has multiple required fields has $ref to the same oneOf object * fix bad depth Co-authored-by: Kanda <kanda@synctera.com>
This commit is contained in:
@@ -460,7 +460,7 @@ public class GoClientCodegen extends AbstractGoCodegen {
|
||||
objs = super.postProcessOperationsWithModels(objs, allModels);
|
||||
Map<String, Object> operations = (Map<String, Object>) objs.get("operations");
|
||||
HashMap<String, CodegenModel> modelMaps = new HashMap<String, CodegenModel>();
|
||||
HashMap<String, Integer> processedModelMaps = new HashMap<String, Integer>();
|
||||
HashMap<String, ArrayList<Integer>> processedModelMaps = new HashMap<>();
|
||||
|
||||
for (ModelMap modelMap : allModels) {
|
||||
CodegenModel m = modelMap.getModel();
|
||||
@@ -488,14 +488,14 @@ public class GoClientCodegen extends AbstractGoCodegen {
|
||||
return objs;
|
||||
}
|
||||
|
||||
private String constructExampleCode(CodegenParameter codegenParameter, HashMap<String, CodegenModel> modelMaps, HashMap<String, Integer> processedModelMap) {
|
||||
private String constructExampleCode(CodegenParameter codegenParameter, HashMap<String, CodegenModel> modelMaps, HashMap<String, ArrayList<Integer>> processedModelMap) {
|
||||
if (codegenParameter.isArray) { // array
|
||||
String prefix = codegenParameter.dataType;
|
||||
String dataType = StringUtils.removeStart(codegenParameter.dataType, "[]");
|
||||
if (modelMaps.containsKey(dataType)) {
|
||||
prefix = "[]" + goImportAlias + "." + dataType;
|
||||
}
|
||||
return prefix + "{" + constructExampleCode(codegenParameter.items, modelMaps, processedModelMap) + "}";
|
||||
return prefix + "{" + constructExampleCode(codegenParameter.items, modelMaps, processedModelMap, 0) + "}";
|
||||
} else if (codegenParameter.isMap) {
|
||||
String prefix = codegenParameter.dataType;
|
||||
String dataType = StringUtils.removeStart(codegenParameter.dataType, "map[string][]");
|
||||
@@ -505,7 +505,7 @@ public class GoClientCodegen extends AbstractGoCodegen {
|
||||
if (codegenParameter.items == null) {
|
||||
return prefix + "{ ... }";
|
||||
}
|
||||
return prefix + "{\"key\": " + constructExampleCode(codegenParameter.items, modelMaps, processedModelMap) + "}";
|
||||
return prefix + "{\"key\": " + constructExampleCode(codegenParameter.items, modelMaps, processedModelMap, 0) + "}";
|
||||
} else if (codegenParameter.isPrimitiveType) { // primitive type
|
||||
if (codegenParameter.isString) {
|
||||
if (!StringUtils.isEmpty(codegenParameter.example) && !"null".equals(codegenParameter.example)) {
|
||||
@@ -522,7 +522,9 @@ public class GoClientCodegen extends AbstractGoCodegen {
|
||||
} else if (codegenParameter.isUri) { // URL
|
||||
return "\"https://example.com\"";
|
||||
} else if (codegenParameter.isDateTime || codegenParameter.isDate) { // datetime or date
|
||||
processedModelMap.put("time.Time", 1);
|
||||
ArrayList<Integer> v = new ArrayList<>();
|
||||
v.add(1);
|
||||
processedModelMap.put("time.Time", v);
|
||||
return "time.Now()";
|
||||
} else if (codegenParameter.isFile) {
|
||||
return "os.NewFile(1234, \"some_file\")";
|
||||
@@ -536,7 +538,7 @@ public class GoClientCodegen extends AbstractGoCodegen {
|
||||
} else { // model
|
||||
// look up the model
|
||||
if (modelMaps.containsKey(codegenParameter.dataType)) {
|
||||
return constructExampleCode(modelMaps.get(codegenParameter.dataType), modelMaps, processedModelMap);
|
||||
return constructExampleCode(modelMaps.get(codegenParameter.dataType), modelMaps, processedModelMap, 0);
|
||||
} else if (codegenParameter.isEmail) { // email
|
||||
if (!StringUtils.isEmpty(codegenParameter.example) && !"null".equals(codegenParameter.example)) {
|
||||
return "\"" + codegenParameter.example + "\"";
|
||||
@@ -544,7 +546,9 @@ public class GoClientCodegen extends AbstractGoCodegen {
|
||||
return "\"" + codegenParameter.paramName + "@example.com\"";
|
||||
}
|
||||
} else if (codegenParameter.isDateTime || codegenParameter.isDate) { // datetime or date
|
||||
processedModelMap.put("time.Time", 1);
|
||||
ArrayList<Integer> v = new ArrayList<>();
|
||||
v.add(1);
|
||||
processedModelMap.put("time.Time", v);
|
||||
return "time.Now()";
|
||||
} else {
|
||||
//LOGGER.error("Error in constructing examples. Failed to look up the model " + codegenParameter.dataType);
|
||||
@@ -553,7 +557,7 @@ public class GoClientCodegen extends AbstractGoCodegen {
|
||||
}
|
||||
}
|
||||
|
||||
private String constructExampleCode(CodegenProperty codegenProperty, HashMap<String, CodegenModel> modelMaps, HashMap<String, Integer> processedModelMap) {
|
||||
private String constructExampleCode(CodegenProperty codegenProperty, HashMap<String, CodegenModel> modelMaps, HashMap<String, ArrayList<Integer>> processedModelMap, int depth) {
|
||||
if (codegenProperty.isArray) { // array
|
||||
String prefix = codegenProperty.dataType;
|
||||
String dataType = StringUtils.removeStart(codegenProperty.dataType, "[]");
|
||||
@@ -564,7 +568,7 @@ public class GoClientCodegen extends AbstractGoCodegen {
|
||||
// We can't easily generate a pointer inline, so just use nil in that case
|
||||
return prefix + "{nil}";
|
||||
}
|
||||
return prefix + "{" + constructExampleCode(codegenProperty.items, modelMaps, processedModelMap) + "}";
|
||||
return prefix + "{" + constructExampleCode(codegenProperty.items, modelMaps, processedModelMap, depth+1) + "}";
|
||||
} else if (codegenProperty.isMap) { // map
|
||||
String prefix = codegenProperty.dataType;
|
||||
String dataType = StringUtils.removeStart(codegenProperty.dataType, "map[string][]");
|
||||
@@ -574,7 +578,7 @@ public class GoClientCodegen extends AbstractGoCodegen {
|
||||
if (codegenProperty.items == null) {
|
||||
return prefix + "{ ... }";
|
||||
}
|
||||
return prefix + "{\"key\": " + constructExampleCode(codegenProperty.items, modelMaps, processedModelMap) + "}";
|
||||
return prefix + "{\"key\": " + constructExampleCode(codegenProperty.items, modelMaps, processedModelMap, depth+1) + "}";
|
||||
} else if (codegenProperty.isPrimitiveType) { // primitive type
|
||||
if (codegenProperty.isString) {
|
||||
if (!StringUtils.isEmpty(codegenProperty.example) && !"null".equals(codegenProperty.example)) {
|
||||
@@ -591,7 +595,9 @@ public class GoClientCodegen extends AbstractGoCodegen {
|
||||
} else if (codegenProperty.isUri) { // URL
|
||||
return "\"https://example.com\"";
|
||||
} else if (codegenProperty.isDateTime || codegenProperty.isDate) { // datetime or date
|
||||
processedModelMap.put("time.Time", 1);
|
||||
ArrayList<Integer> v = new ArrayList<>();
|
||||
v.add(1);
|
||||
processedModelMap.put("time.Time", v);
|
||||
return "time.Now()";
|
||||
} else { // numeric
|
||||
String example;
|
||||
@@ -606,7 +612,7 @@ public class GoClientCodegen extends AbstractGoCodegen {
|
||||
} else {
|
||||
// look up the model
|
||||
if (modelMaps.containsKey(codegenProperty.dataType)) {
|
||||
return constructExampleCode(modelMaps.get(codegenProperty.dataType), modelMaps, processedModelMap);
|
||||
return constructExampleCode(modelMaps.get(codegenProperty.dataType), modelMaps, processedModelMap, depth+1);
|
||||
} else if (codegenProperty.isEmail) { // email
|
||||
if (!StringUtils.isEmpty(codegenProperty.example) && !"null".equals(codegenProperty.example)) {
|
||||
return "\"" + codegenProperty.example + "\"";
|
||||
@@ -614,7 +620,9 @@ public class GoClientCodegen extends AbstractGoCodegen {
|
||||
return "\"" + codegenProperty.name + "@example.com\"";
|
||||
}
|
||||
} else if (codegenProperty.isDateTime || codegenProperty.isDate) { // datetime or date
|
||||
processedModelMap.put("time.Time", 1);
|
||||
ArrayList<Integer> v = new ArrayList<>();
|
||||
v.add(1);
|
||||
processedModelMap.put("time.Time", v);
|
||||
return "time.Now()";
|
||||
} else {
|
||||
//LOGGER.error("Error in constructing examples. Failed to look up the model " + codegenProperty.dataType);
|
||||
@@ -623,17 +631,20 @@ public class GoClientCodegen extends AbstractGoCodegen {
|
||||
}
|
||||
}
|
||||
|
||||
private String constructExampleCode(CodegenModel codegenModel, HashMap<String, CodegenModel> modelMaps, HashMap<String, Integer> processedModelMap) {
|
||||
private String constructExampleCode(CodegenModel codegenModel, HashMap<String, CodegenModel> modelMaps, HashMap<String, ArrayList<Integer>> processedModelMap, int depth) {
|
||||
// break infinite recursion. Return, in case a model is already processed in the current context.
|
||||
String model = codegenModel.name;
|
||||
if (processedModelMap.containsKey(model)) {
|
||||
int count = processedModelMap.get(model);
|
||||
if (count == 1) {
|
||||
processedModelMap.put(model, 2);
|
||||
} else if (count == 2) {
|
||||
ArrayList<Integer> depthList = processedModelMap.get(model);
|
||||
if (depthList.size() == 1) {
|
||||
if (depthList.get(0) != depth) {
|
||||
depthList.add(depth);
|
||||
processedModelMap.put(model, depthList);
|
||||
}
|
||||
} else if (depthList.size() == 2) {
|
||||
return "";
|
||||
} else {
|
||||
throw new RuntimeException("Invalid count when constructing example: " + count);
|
||||
throw new RuntimeException("Invalid count when constructing example: " + depthList.size());
|
||||
}
|
||||
} else if (codegenModel.isEnum) {
|
||||
Map<String, Object> allowableValues = codegenModel.allowableValues;
|
||||
@@ -645,16 +656,18 @@ public class GoClientCodegen extends AbstractGoCodegen {
|
||||
return goImportAlias + "." + model + "(" + example + ")";
|
||||
} else if (codegenModel.oneOf != null && !codegenModel.oneOf.isEmpty()) {
|
||||
String subModel = (String) codegenModel.oneOf.toArray()[0];
|
||||
String oneOf = constructExampleCode(modelMaps.get(subModel), modelMaps, processedModelMap).substring(1);
|
||||
String oneOf = constructExampleCode(modelMaps.get(subModel), modelMaps, processedModelMap, depth+1).substring(1);
|
||||
return goImportAlias + "." + model + "{" + subModel + ": " + oneOf + "}";
|
||||
} else {
|
||||
processedModelMap.put(model, 1);
|
||||
ArrayList<Integer> v = new ArrayList<>();
|
||||
v.add(depth);
|
||||
processedModelMap.put(model, v);
|
||||
}
|
||||
|
||||
List<String> propertyExamples = new ArrayList<>();
|
||||
for (CodegenProperty codegenProperty : codegenModel.requiredVars) {
|
||||
propertyExamples.add(constructExampleCode(codegenProperty, modelMaps, processedModelMap));
|
||||
propertyExamples.add(constructExampleCode(codegenProperty, modelMaps, processedModelMap, depth+1));
|
||||
}
|
||||
return "*" + goImportAlias + ".New" + toModelName(model) + "(" + StringUtils.join(propertyExamples, ", ") + ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -158,4 +158,23 @@ public class GoClientCodegenTest {
|
||||
|
||||
TestUtils.assertFileContains(Paths.get(output + "/model_example.go"), "Child NullableChild");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultipleRequiredPropertiesHasSameOneOfObject() throws IOException {
|
||||
File output = Files.createTempDirectory("test").toFile();
|
||||
output.deleteOnExit();
|
||||
|
||||
final CodegenConfigurator configurator = new CodegenConfigurator()
|
||||
.setGeneratorName("go")
|
||||
.setInputSpec("src/test/resources/3_0/petstore-multiple-required-properties-has-same-oneOf-object.yaml")
|
||||
.setOutputDir(output.getAbsolutePath().replace("\\", "/"));
|
||||
|
||||
DefaultGenerator generator = new DefaultGenerator();
|
||||
List<File> files = generator.opts(configurator.toClientOptInput()).generate();
|
||||
System.out.println(files);
|
||||
files.forEach(File::deleteOnExit);
|
||||
|
||||
Path docFile = Paths.get(output + "/docs/PetApi.md");
|
||||
TestUtils.assertFileContains(docFile, "openapiclient.pet{Cat: openapiclient.NewCat(\"Attr_example\")}, openapiclient.pet{Cat: openapiclient.NewCat(\"Attr_example\")}, openapiclient.pet{Cat: openapiclient.NewCat(\"Attr_example\")}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
openapi: 3.0.1
|
||||
info:
|
||||
title: My title
|
||||
description: API under test
|
||||
version: 1.0.7
|
||||
servers:
|
||||
- url: https://localhost:9999/root
|
||||
paths:
|
||||
/pet_preference:
|
||||
post:
|
||||
operationId: postPreference
|
||||
tags:
|
||||
- pet
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/petPreference'
|
||||
responses:
|
||||
201:
|
||||
description: OK
|
||||
components:
|
||||
schemas:
|
||||
dog:
|
||||
type: object
|
||||
required:
|
||||
- attr
|
||||
properties:
|
||||
attr:
|
||||
type: string
|
||||
enum:
|
||||
- DOG
|
||||
cat:
|
||||
type: object
|
||||
required:
|
||||
- attr
|
||||
properties:
|
||||
attr:
|
||||
type: string
|
||||
enum:
|
||||
- CAT
|
||||
|
||||
pet:
|
||||
oneOf:
|
||||
- $ref: '#/components/schemas/dog'
|
||||
- $ref: '#/components/schemas/cat'
|
||||
discriminator:
|
||||
propertyName: attr
|
||||
mapping:
|
||||
DOG: '#/components/schemas/dog'
|
||||
CAT: '#/components/schemas/cat'
|
||||
|
||||
petPreference:
|
||||
type: object
|
||||
required:
|
||||
- pet1
|
||||
- pet2
|
||||
- pet3
|
||||
properties:
|
||||
pet1:
|
||||
$ref: '#/components/schemas/pet'
|
||||
pet2:
|
||||
$ref: '#/components/schemas/pet'
|
||||
pet3:
|
||||
$ref: '#/components/schemas/pet'
|
||||
Reference in New Issue
Block a user