fix some inheritance/composition issues and add allOf unit tests

Fix #3629 #3544 #3474 #3636
This commit is contained in:
cbornet 2016-08-24 17:11:45 +02:00
parent 54fe7a731c
commit 194c389a06
4 changed files with 264 additions and 6 deletions

View File

@ -100,6 +100,7 @@ public class DefaultCodegen {
protected List<CliOption> cliOptions = new ArrayList<CliOption>(); protected List<CliOption> cliOptions = new ArrayList<CliOption>();
protected boolean skipOverwrite; protected boolean skipOverwrite;
protected boolean supportsInheritance; protected boolean supportsInheritance;
protected boolean supportsMixins;
protected Map<String, String> supportedLibraries = new LinkedHashMap<String, String>(); protected Map<String, String> supportedLibraries = new LinkedHashMap<String, String>();
protected String library; protected String library;
protected Boolean sortParamsByRequiredFlag = true; protected Boolean sortParamsByRequiredFlag = true;
@ -1233,7 +1234,7 @@ public class DefaultCodegen {
List<String> required = new ArrayList<String>(); List<String> required = new ArrayList<String>();
Map<String, Property> allProperties; Map<String, Property> allProperties;
List<String> allRequired; List<String> allRequired;
if (supportsInheritance) { if (supportsInheritance || supportsMixins) {
allProperties = new LinkedHashMap<String, Property>(); allProperties = new LinkedHashMap<String, Property>();
allRequired = new ArrayList<String>(); allRequired = new ArrayList<String>();
m.allVars = new ArrayList<CodegenProperty>(); m.allVars = new ArrayList<CodegenProperty>();
@ -1254,17 +1255,20 @@ public class DefaultCodegen {
interfaceModel = allDefinitions.get(_interface.getSimpleRef()); interfaceModel = allDefinitions.get(_interface.getSimpleRef());
} }
// set first interface with discriminator found as parent // set first interface with discriminator found as parent
if (parent == null && interfaceModel instanceof ModelImpl && ((ModelImpl) interfaceModel).getDiscriminator() != null) { if (parent == null
&& ((interfaceModel instanceof ModelImpl && ((ModelImpl) interfaceModel).getDiscriminator() != null)
|| (interfaceModel instanceof ComposedModel && isDiscriminatorInInterfaceTree((ComposedModel) interfaceModel, allDefinitions)))) {
parent = _interface; parent = _interface;
} else { } else {
final String interfaceRef = toModelName(_interface.getSimpleRef()); final String interfaceRef = toModelName(_interface.getSimpleRef());
m.interfaces.add(interfaceRef); m.interfaces.add(interfaceRef);
addImport(m, interfaceRef); addImport(m, interfaceRef);
if (allDefinitions != null) { if (allDefinitions != null) {
if (!supportsMixins) {
addProperties(properties, required, interfaceModel, allDefinitions);
}
if (supportsInheritance) { if (supportsInheritance) {
addProperties(allProperties, allRequired, interfaceModel, allDefinitions); addProperties(allProperties, allRequired, interfaceModel, allDefinitions);
} else {
addProperties(properties, required, interfaceModel, allDefinitions);
} }
} }
} }
@ -1323,6 +1327,30 @@ public class DefaultCodegen {
return m; return m;
} }
/**
* Recursively look for a discriminator in the interface tree
*/
private boolean isDiscriminatorInInterfaceTree(ComposedModel model, Map<String, Model> allDefinitions) {
if (model == null || allDefinitions == null)
return false;
Model child = ((ComposedModel) model).getChild();
if (child instanceof ModelImpl && ((ModelImpl) child).getDiscriminator() != null) {
return true;
}
for (RefModel _interface : model.getInterfaces()) {
Model interfaceModel = allDefinitions.get(_interface.getSimpleRef());
if (interfaceModel instanceof ModelImpl && ((ModelImpl) interfaceModel).getDiscriminator() != null) {
return true;
}
if (interfaceModel instanceof ComposedModel) {
return isDiscriminatorInInterfaceTree((ComposedModel) interfaceModel, allDefinitions);
}
}
return false;
}
protected void addAdditionPropertiesToCodeGenModel(CodegenModel codegenModel, ModelImpl swaggerModel) { protected void addAdditionPropertiesToCodeGenModel(CodegenModel codegenModel, ModelImpl swaggerModel) {
MapProperty mapProperty = new MapProperty(swaggerModel.getAdditionalProperties()); MapProperty mapProperty = new MapProperty(swaggerModel.getAdditionalProperties());
addParentContainer(codegenModel, codegenModel.name, mapProperty); addParentContainer(codegenModel, codegenModel.name, mapProperty);

View File

@ -223,6 +223,7 @@ public class JavascriptClientCodegen extends DefaultCodegen implements CodegenCo
setUseInheritance(Boolean.parseBoolean((String)additionalProperties.get(USE_INHERITANCE))); setUseInheritance(Boolean.parseBoolean((String)additionalProperties.get(USE_INHERITANCE)));
} else { } else {
supportsInheritance = true; supportsInheritance = true;
supportsMixins = true;
} }
if (additionalProperties.containsKey(EMIT_MODEL_METHODS)) { if (additionalProperties.containsKey(EMIT_MODEL_METHODS)) {
setEmitModelMethods(Boolean.parseBoolean((String)additionalProperties.get(EMIT_MODEL_METHODS))); setEmitModelMethods(Boolean.parseBoolean((String)additionalProperties.get(EMIT_MODEL_METHODS)));
@ -386,6 +387,7 @@ public class JavascriptClientCodegen extends DefaultCodegen implements CodegenCo
public void setUseInheritance(boolean useInheritance) { public void setUseInheritance(boolean useInheritance) {
this.supportsInheritance = useInheritance; this.supportsInheritance = useInheritance;
this.supportsMixins = useInheritance;
} }
public void setEmitModelMethods(boolean emitModelMethods) { public void setEmitModelMethods(boolean emitModelMethods) {
@ -889,7 +891,7 @@ public class JavascriptClientCodegen extends DefaultCodegen implements CodegenCo
// NOTE: can't use 'mandatory' as it is built from ModelImpl.getRequired(), which sorts names // NOTE: can't use 'mandatory' as it is built from ModelImpl.getRequired(), which sorts names
// alphabetically and in any case the document order of 'required' and 'properties' can differ. // alphabetically and in any case the document order of 'required' and 'properties' can differ.
List<CodegenProperty> required = new ArrayList<>(); List<CodegenProperty> required = new ArrayList<>();
List<CodegenProperty> allRequired = supportsInheritance ? new ArrayList<CodegenProperty>() : required; List<CodegenProperty> allRequired = supportsInheritance || supportsMixins ? new ArrayList<CodegenProperty>() : required;
cm.vendorExtensions.put("x-required", required); cm.vendorExtensions.put("x-required", required);
cm.vendorExtensions.put("x-all-required", allRequired); cm.vendorExtensions.put("x-all-required", allRequired);
@ -903,7 +905,7 @@ public class JavascriptClientCodegen extends DefaultCodegen implements CodegenCo
} }
} }
if (supportsInheritance) { if (supportsInheritance || supportsMixins) {
for (CodegenProperty var : cm.allVars) { for (CodegenProperty var : cm.allVars) {
if (Boolean.TRUE.equals(var.required)) { if (Boolean.TRUE.equals(var.required)) {
allRequired.add(var); allRequired.add(var);

View File

@ -1,5 +1,6 @@
package io.swagger.codegen; package io.swagger.codegen;
import io.swagger.models.Model;
import io.swagger.models.Operation; import io.swagger.models.Operation;
import io.swagger.models.Swagger; import io.swagger.models.Swagger;
import io.swagger.models.properties.Property; import io.swagger.models.properties.Property;
@ -187,6 +188,129 @@ public class CodegenTest {
Assert.assertEquals(op.discriminator, "className"); Assert.assertEquals(op.discriminator, "className");
} }
@Test(description = "handle simple composition")
public void simpleCompositionTest() {
final Swagger swagger = parseAndPrepareSwagger("src/test/resources/2_0/allOfTest.yaml");
final DefaultCodegen codegen = new DefaultCodegen();
codegen.supportsInheritance = true;
final Model model = swagger.getDefinitions().get("SimpleComposition");
CodegenModel composed = codegen.fromModel("SimpleComposition", model, swagger.getDefinitions());
Assert.assertEquals(composed.vars.size(), 3);
Assert.assertEquals(composed.vars.get(0).baseName, "modelOneProp");
Assert.assertEquals(composed.vars.get(1).baseName, "modelTwoProp");
Assert.assertEquals(composed.vars.get(2).baseName, "simpleCompositionProp");
Assert.assertNull(composed.parent);
}
@Test(description = "handle multi level composition")
public void multiCompositionTest() {
final Swagger swagger = parseAndPrepareSwagger("src/test/resources/2_0/allOfTest.yaml");
final DefaultCodegen codegen = new DefaultCodegen();
codegen.supportsInheritance = true;
final Model model = swagger.getDefinitions().get("CompositionOfSimpleComposition");
CodegenModel composed = codegen.fromModel("CompositionOfSimpleComposition", model, swagger.getDefinitions());
Assert.assertEquals(composed.vars.size(), 5);
Assert.assertEquals(composed.vars.get(0).baseName, "modelOneProp");
Assert.assertEquals(composed.vars.get(1).baseName, "modelTwoProp");
Assert.assertEquals(composed.vars.get(2).baseName, "simpleCompositionProp");
Assert.assertEquals(composed.vars.get(3).baseName, "modelThreeProp");
Assert.assertEquals(composed.vars.get(4).baseName, "compositionOfSimpleCompositionProp");
Assert.assertNull(composed.parent);
}
@Test(description = "handle simple inheritance")
public void simpleInheritanceTest() {
final Swagger swagger = parseAndPrepareSwagger("src/test/resources/2_0/allOfTest.yaml");
final DefaultCodegen codegen = new DefaultCodegen();
codegen.supportsInheritance = true;
final Model model = swagger.getDefinitions().get("ChildOfSimpleParent");
CodegenModel child = codegen.fromModel("ChildOfSimpleParent", model, swagger.getDefinitions());
Assert.assertEquals(child.vars.size(), 2);
Assert.assertEquals(child.vars.get(0).baseName, "modelOneProp");
Assert.assertEquals(child.vars.get(1).baseName, "childOfSimpleParentProp");
Assert.assertEquals(child.parent, "SimpleParent");
}
@Test(description = "handle multi level inheritance")
public void multiInheritanceTest() {
final Swagger swagger = parseAndPrepareSwagger("src/test/resources/2_0/allOfTest.yaml");
final DefaultCodegen codegen = new DefaultCodegen();
codegen.supportsInheritance = true;
final Model model = swagger.getDefinitions().get("ChildOfChildOfSimpleParent");
CodegenModel child = codegen.fromModel("ChildOfChildOfSimpleParent", model, swagger.getDefinitions());
Assert.assertEquals(child.vars.size(), 1);
Assert.assertEquals(child.vars.get(0).baseName, "childOfChildOfSimpleParentProp");
Assert.assertEquals(child.parent, "ChildOfSimpleParent");
}
@Test(description = "copy properties in multi level inheritance if supportsInheritance is false")
public void noSupportsInheritanceTest() {
final Swagger swagger = parseAndPrepareSwagger("src/test/resources/2_0/allOfTest.yaml");
final DefaultCodegen codegen = new DefaultCodegen();
final Model model = swagger.getDefinitions().get("ChildOfChildOfSimpleParent");
CodegenModel child = codegen.fromModel("ChildOfChildOfSimpleParent", model, swagger.getDefinitions());
Assert.assertEquals(child.vars.size(), 5);
Assert.assertEquals(child.vars.get(0).baseName, "modelOneProp");
Assert.assertEquals(child.vars.get(1).baseName, "disc");
Assert.assertEquals(child.vars.get(2).baseName, "simpleParentProp");
Assert.assertEquals(child.vars.get(3).baseName, "childOfSimpleParentProp");
Assert.assertEquals(child.vars.get(4).baseName, "childOfChildOfSimpleParentProp");
Assert.assertEquals(child.parent, "ChildOfSimpleParent");
}
@Test(description = "don't copy interfaces properties if supportsMixins is true")
public void supportsMixinsTest() {
final Swagger swagger = parseAndPrepareSwagger("src/test/resources/2_0/allOfTest.yaml");
final DefaultCodegen codegen = new DefaultCodegen();
codegen.supportsInheritance = true;
codegen.supportsMixins = true;
final Model model = swagger.getDefinitions().get("ChildOfChildOfSimpleParent");
CodegenModel child = codegen.fromModel("ChildOfChildOfSimpleParent", model, swagger.getDefinitions());
Assert.assertEquals(child.vars.size(), 1);
Assert.assertEquals(child.vars.get(0).baseName, "childOfChildOfSimpleParentProp");
Assert.assertEquals(child.allVars.size(), 5);
Assert.assertEquals(child.allVars.get(0).baseName, "modelOneProp");
Assert.assertEquals(child.allVars.get(1).baseName, "disc");
Assert.assertEquals(child.allVars.get(2).baseName, "simpleParentProp");
Assert.assertEquals(child.allVars.get(3).baseName, "childOfSimpleParentProp");
Assert.assertEquals(child.allVars.get(4).baseName, "childOfChildOfSimpleParentProp");
Assert.assertEquals(child.parent, "ChildOfSimpleParent");
}
@Test(description = "handle inheritance from composed model")
public void inheritanceOfComposedModelTest() {
final Swagger swagger = parseAndPrepareSwagger("src/test/resources/2_0/allOfTest.yaml");
final DefaultCodegen codegen = new DefaultCodegen();
codegen.supportsInheritance = true;
final Model model = swagger.getDefinitions().get("ChildOfComposedParent");
CodegenModel child = codegen.fromModel("ChildOfComposedParent", model, swagger.getDefinitions());
Assert.assertEquals(child.vars.size(), 1);
Assert.assertEquals(child.vars.get(0).baseName, "childOfComposedParentProp");
Assert.assertEquals(child.parent, "ComposedParent");
}
@Test(description = "handle multi level inheritance from composed model")
public void multiInheritanceOfComposedModelTest() {
final Swagger swagger = parseAndPrepareSwagger("src/test/resources/2_0/allOfTest.yaml");
final DefaultCodegen codegen = new DefaultCodegen();
codegen.supportsInheritance = true;
final Model model = swagger.getDefinitions().get("ChildOfChildOfComposedParent");
CodegenModel child = codegen.fromModel("ChildOfChildOfComposedParent", model, swagger.getDefinitions());
Assert.assertEquals(child.vars.size(), 1);
Assert.assertEquals(child.vars.get(0).baseName, "childOfChildOfComposedParentProp");
Assert.assertEquals(child.parent, "ChildOfComposedParent");
}
@Test(description = "use operation consumes and produces") @Test(description = "use operation consumes and produces")
public void localConsumesAndProducesTest() { public void localConsumesAndProducesTest() {
final Swagger model = parseAndPrepareSwagger("src/test/resources/2_0/globalConsumesAndProduces.json"); final Swagger model = parseAndPrepareSwagger("src/test/resources/2_0/globalConsumesAndProduces.json");

View File

@ -0,0 +1,104 @@
swagger: '2.0'
info:
version: 0.0.0
title: Simple API
paths:
/:
get:
responses:
200:
description: OK
definitions:
ModelOne:
type: object
properties:
modelOneProp:
type: string
ModelTwo:
type: object
properties:
modelTwoProp:
type: string
ModelThree:
type: object
properties:
modelThreeProp:
type: string
SimpleComposition:
allOf:
- $ref: '#/definitions/ModelOne'
- $ref: '#/definitions/ModelTwo'
- type: object
properties:
simpleCompositionProp:
type: string
CompositionOfSimpleComposition:
allOf:
- $ref: '#/definitions/SimpleComposition'
- $ref: '#/definitions/ModelThree'
- type: object
properties:
compositionOfSimpleCompositionProp:
type: string
SimpleParent:
type: object
discriminator: disc
properties:
disc:
type: string
simpleParentProp:
type: string
required: [disc]
ChildOfSimpleParent:
allOf:
- $ref: '#/definitions/ModelOne'
- $ref: '#/definitions/SimpleParent'
- type: object
properties:
childOfSimpleParentProp:
type: string
ChildOfChildOfSimpleParent:
allOf:
- $ref: '#/definitions/ChildOfSimpleParent'
- type: object
properties:
childOfChildOfSimpleParentProp:
type: string
ComposedParent:
allOf:
- $ref: '#/definitions/ModelOne'
- $ref: '#/definitions/ModelTwo'
- type: object
discriminator: disc
properties:
disc:
type: string
composedParentProp:
type: string
required: [disc]
ChildOfComposedParent:
allOf:
- $ref: '#/definitions/ComposedParent'
- type: object
properties:
childOfComposedParentProp:
type: string
ChildOfChildOfComposedParent:
allOf:
- $ref: '#/definitions/ChildOfComposedParent'
- type: object
properties:
childOfChildOfComposedParentProp:
type: string