forked from loafle/openapi-generator-original
Add isCircularReference to properties (#4553)
This commit is contained in:
committed by
William Cheng
parent
00abb4780c
commit
a695748805
+7
-3
@@ -62,6 +62,7 @@ public class CodegenProperty implements Cloneable {
|
||||
public boolean isWriteOnly;
|
||||
public boolean isNullable;
|
||||
public boolean isSelfReference;
|
||||
public boolean isCircularReference;
|
||||
public List<String> _enum;
|
||||
public Map<String, Object> allowableValues;
|
||||
public CodegenProperty items;
|
||||
@@ -498,6 +499,7 @@ public class CodegenProperty implements Cloneable {
|
||||
sb.append(", isWriteOnly=").append(isWriteOnly);
|
||||
sb.append(", isNullable=").append(isNullable);
|
||||
sb.append(", isSelfReference=").append(isSelfReference);
|
||||
sb.append(", isCircularReference=").append(isCircularReference);
|
||||
sb.append(", _enum=").append(_enum);
|
||||
sb.append(", allowableValues=").append(allowableValues);
|
||||
sb.append(", items=").append(items);
|
||||
@@ -558,6 +560,7 @@ public class CodegenProperty implements Cloneable {
|
||||
isWriteOnly == that.isWriteOnly &&
|
||||
isNullable == that.isNullable &&
|
||||
isSelfReference == that.isSelfReference &&
|
||||
isCircularReference == that.isCircularReference &&
|
||||
hasValidation == that.hasValidation &&
|
||||
isInherited == that.isInherited &&
|
||||
isXmlAttribute == that.isXmlAttribute &&
|
||||
@@ -613,8 +616,9 @@ public class CodegenProperty implements Cloneable {
|
||||
hasMoreNonReadOnly, isPrimitiveType, isModel, isContainer, isString, isNumeric, isInteger,
|
||||
isLong, isNumber, isFloat, isDouble, isByteArray, isBinary, isFile, isBoolean, isDate, isDateTime,
|
||||
isUuid, isUri, isEmail, isFreeFormObject, isListContainer, isMapContainer, isEnum, isReadOnly,
|
||||
isWriteOnly, isNullable, isSelfReference, _enum, allowableValues, items, mostInnerItems,
|
||||
vendorExtensions, hasValidation, isInherited, discriminatorValue, nameInCamelCase, nameInSnakeCase,
|
||||
enumName, maxItems, minItems, isXmlAttribute, xmlPrefix, xmlName, xmlNamespace, isXmlWrapped);
|
||||
isWriteOnly, isNullable, isSelfReference, isCircularReference, _enum, allowableValues, items,
|
||||
mostInnerItems, vendorExtensions, hasValidation, isInherited, discriminatorValue, nameInCamelCase,
|
||||
nameInSnakeCase, enumName, maxItems, minItems, isXmlAttribute, xmlPrefix, xmlName, xmlNamespace,
|
||||
isXmlWrapped);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -328,10 +328,61 @@ public class DefaultCodegen implements CodegenConfig {
|
||||
}
|
||||
}
|
||||
}
|
||||
setCircularReferences(allModels);
|
||||
|
||||
return objs;
|
||||
}
|
||||
|
||||
public void setCircularReferences(Map<String, CodegenModel> models) {
|
||||
final Map<String, List<CodegenProperty>> dependencyMap = models.entrySet().stream()
|
||||
.collect(Collectors.toMap(Entry::getKey, entry -> getModelDependencies(entry.getValue())));
|
||||
|
||||
models.keySet().forEach(name -> setCircularReferencesOnProperties(name, dependencyMap));
|
||||
}
|
||||
|
||||
private List<CodegenProperty> getModelDependencies(CodegenModel model) {
|
||||
return model.getAllVars().stream()
|
||||
.map(prop -> {
|
||||
if (prop.isContainer) {
|
||||
return prop.items.dataType == null ? null : prop;
|
||||
}
|
||||
return prop.dataType == null ? null : prop;
|
||||
})
|
||||
.filter(prop -> prop != null)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private void setCircularReferencesOnProperties(final String root,
|
||||
final Map<String, List<CodegenProperty>> dependencyMap) {
|
||||
dependencyMap.getOrDefault(root, new ArrayList<>()).stream()
|
||||
.forEach(prop -> {
|
||||
final List<String> unvisited =
|
||||
Collections.singletonList(prop.isContainer ? prop.items.dataType : prop.dataType);
|
||||
prop.isCircularReference = isCircularReference(root,
|
||||
new HashSet<>(),
|
||||
new ArrayList<>(unvisited),
|
||||
dependencyMap);
|
||||
});
|
||||
}
|
||||
|
||||
private boolean isCircularReference(final String root,
|
||||
final Set<String> visited,
|
||||
final List<String> unvisited,
|
||||
final Map<String, List<CodegenProperty>> dependencyMap) {
|
||||
for (int i = 0; i < unvisited.size(); i++) {
|
||||
final String next = unvisited.get(i);
|
||||
if (!visited.contains(next)) {
|
||||
if (next.equals(root)) {
|
||||
return true;
|
||||
}
|
||||
dependencyMap.getOrDefault(next, new ArrayList<>())
|
||||
.forEach(prop -> unvisited.add(prop.isContainer ? prop.items.dataType : prop.dataType));
|
||||
visited.add(next);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// override with any special post-processing
|
||||
@SuppressWarnings("static-method")
|
||||
public Map<String, Object> postProcessModels(Map<String, Object> objs) {
|
||||
|
||||
+1
@@ -347,6 +347,7 @@ public class ElmClientCodegen extends DefaultCodegen implements CodegenConfig {
|
||||
});
|
||||
}
|
||||
}
|
||||
setCircularReferences(allModels);
|
||||
for (Map.Entry<String, Object> entry : objs.entrySet()) {
|
||||
Map<String, Object> inner = (Map<String, Object>) entry.getValue();
|
||||
List<Map<String, Object>> models = (List<Map<String, Object>>) inner.get("models");
|
||||
|
||||
+51
@@ -1113,4 +1113,55 @@ public class DefaultCodegenTest {
|
||||
Assert.assertFalse(result);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCircularReferencesDetection() {
|
||||
// given
|
||||
DefaultCodegen codegen = new DefaultCodegen();
|
||||
final CodegenProperty inboundOut = new CodegenProperty();
|
||||
inboundOut.baseName = "out";
|
||||
inboundOut.dataType = "RoundA";
|
||||
final CodegenProperty roundANext = new CodegenProperty();
|
||||
roundANext.baseName = "next";
|
||||
roundANext.dataType = "RoundB";
|
||||
final CodegenProperty roundBNext = new CodegenProperty();
|
||||
roundBNext.baseName = "next";
|
||||
roundBNext.dataType = "RoundC";
|
||||
final CodegenProperty roundCNext = new CodegenProperty();
|
||||
roundCNext.baseName = "next";
|
||||
roundCNext.dataType = "RoundA";
|
||||
final CodegenProperty roundCOut = new CodegenProperty();
|
||||
roundCOut.baseName = "out";
|
||||
roundCOut.dataType = "Outbound";
|
||||
final CodegenModel inboundModel = new CodegenModel();
|
||||
inboundModel.setDataType("Inbound");
|
||||
inboundModel.setAllVars(Collections.singletonList(inboundOut));
|
||||
final CodegenModel roundAModel = new CodegenModel();
|
||||
roundAModel.setDataType("RoundA");
|
||||
roundAModel.setAllVars(Collections.singletonList(roundANext));
|
||||
final CodegenModel roundBModel = new CodegenModel();
|
||||
roundBModel.setDataType("RoundB");
|
||||
roundBModel.setAllVars(Collections.singletonList(roundBNext));
|
||||
final CodegenModel roundCModel = new CodegenModel();
|
||||
roundCModel.setDataType("RoundC");
|
||||
roundCModel.setAllVars(Arrays.asList(roundCNext, roundCOut));
|
||||
final CodegenModel outboundModel = new CodegenModel();
|
||||
outboundModel.setDataType("Outbound");
|
||||
final Map<String, CodegenModel> models = new HashMap<>();
|
||||
models.put("Inbound", inboundModel);
|
||||
models.put("RoundA", roundAModel);
|
||||
models.put("RoundB", roundBModel);
|
||||
models.put("RoundC", roundCModel);
|
||||
models.put("Outbound", outboundModel);
|
||||
|
||||
// when
|
||||
codegen.setCircularReferences(models);
|
||||
|
||||
// then
|
||||
Assert.assertFalse(inboundOut.isCircularReference);
|
||||
Assert.assertTrue(roundANext.isCircularReference);
|
||||
Assert.assertTrue(roundBNext.isCircularReference);
|
||||
Assert.assertTrue(roundCNext.isCircularReference);
|
||||
Assert.assertFalse(roundCOut.isCircularReference);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
openapi: 3.0.0
|
||||
info:
|
||||
description: Test
|
||||
version: 1.0.0
|
||||
title: OpenAPI
|
||||
paths:
|
||||
/foo:
|
||||
post:
|
||||
description: ''
|
||||
responses:
|
||||
'200':
|
||||
description: Response
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Foo'
|
||||
components:
|
||||
schemas:
|
||||
Foo:
|
||||
type: object
|
||||
properties:
|
||||
foo:
|
||||
$ref: '#/components/schemas/Foo'
|
||||
Bar:
|
||||
type: object
|
||||
properties:
|
||||
baz:
|
||||
$ref: '#/components/schemas/Baz'
|
||||
Baz:
|
||||
type: object
|
||||
properties:
|
||||
bar:
|
||||
$ref: '#/components/schemas/Bar'
|
||||
Reference in New Issue
Block a user