Skip to content
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

[All] Fix some inheritance/composition issues and add allOf unit tests #3637

Merged
merged 1 commit into from
Oct 3, 2016
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 @@ -100,6 +100,7 @@ public class DefaultCodegen {
protected List<CliOption> cliOptions = new ArrayList<CliOption>();
protected boolean skipOverwrite;
protected boolean supportsInheritance;
protected boolean supportsMixins;
protected Map<String, String> supportedLibraries = new LinkedHashMap<String, String>();
protected String library;
protected Boolean sortParamsByRequiredFlag = true;
Expand Down Expand Up @@ -1233,7 +1234,7 @@ public CodegenModel fromModel(String name, Model model, Map<String, Model> allDe
List<String> required = new ArrayList<String>();
Map<String, Property> allProperties;
List<String> allRequired;
if (supportsInheritance) {
if (supportsInheritance || supportsMixins) {
allProperties = new LinkedHashMap<String, Property>();
allRequired = new ArrayList<String>();
m.allVars = new ArrayList<CodegenProperty>();
Expand All @@ -1254,17 +1255,20 @@ public CodegenModel fromModel(String name, Model model, Map<String, Model> allDe
interfaceModel = allDefinitions.get(_interface.getSimpleRef());
}
// 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;
} else {
final String interfaceRef = toModelName(_interface.getSimpleRef());
m.interfaces.add(interfaceRef);
addImport(m, interfaceRef);
if (allDefinitions != null) {
if (!supportsMixins) {
addProperties(properties, required, interfaceModel, allDefinitions);
}
if (supportsInheritance) {
addProperties(allProperties, allRequired, interfaceModel, allDefinitions);
} else {
addProperties(properties, required, interfaceModel, allDefinitions);
}
}
}
Expand Down Expand Up @@ -1323,6 +1327,30 @@ public CodegenModel fromModel(String name, Model model, Map<String, Model> allDe
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) {
MapProperty mapProperty = new MapProperty(swaggerModel.getAdditionalProperties());
addParentContainer(codegenModel, codegenModel.name, mapProperty);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,7 @@ public void processOpts() {
setUseInheritance(Boolean.parseBoolean((String)additionalProperties.get(USE_INHERITANCE)));
} else {
supportsInheritance = true;
supportsMixins = true;
}
if (additionalProperties.containsKey(EMIT_MODEL_METHODS)) {
setEmitModelMethods(Boolean.parseBoolean((String)additionalProperties.get(EMIT_MODEL_METHODS)));
Expand Down Expand Up @@ -386,6 +387,7 @@ public void setUsePromises(boolean usePromises) {

public void setUseInheritance(boolean useInheritance) {
this.supportsInheritance = useInheritance;
this.supportsMixins = useInheritance;
}

public void setEmitModelMethods(boolean emitModelMethods) {
Expand Down Expand Up @@ -889,7 +891,7 @@ public Map<String, Object> postProcessModels(Map<String, Object> objs) {
// 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.
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-all-required", allRequired);

Expand All @@ -903,7 +905,7 @@ public Map<String, Object> postProcessModels(Map<String, Object> objs) {
}
}

if (supportsInheritance) {
if (supportsInheritance || supportsMixins) {
for (CodegenProperty var : cm.allVars) {
if (Boolean.TRUE.equals(var.required)) {
allRequired.add(var);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.swagger.codegen;

import io.swagger.models.Model;
import io.swagger.models.Operation;
import io.swagger.models.Swagger;
import io.swagger.models.properties.Property;
Expand Down Expand Up @@ -187,6 +188,129 @@ public void discriminatorTest() {
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")
public void localConsumesAndProducesTest() {
final Swagger model = parseAndPrepareSwagger("src/test/resources/2_0/globalConsumesAndProduces.json");
Expand Down
104 changes: 104 additions & 0 deletions modules/swagger-codegen/src/test/resources/2_0/allOfTest.yaml
Original file line number Diff line number Diff line change
@@ -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