diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/DefaultCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/DefaultCodegen.java index 3ddba67f0d8..fd8e28d8a9a 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/DefaultCodegen.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/DefaultCodegen.java @@ -100,6 +100,7 @@ public class DefaultCodegen { protected List cliOptions = new ArrayList(); protected boolean skipOverwrite; protected boolean supportsInheritance; + protected boolean supportsMixins; protected Map supportedLibraries = new LinkedHashMap(); protected String library; protected Boolean sortParamsByRequiredFlag = true; @@ -1233,7 +1234,7 @@ public CodegenModel fromModel(String name, Model model, Map allDe List required = new ArrayList(); Map allProperties; List allRequired; - if (supportsInheritance) { + if (supportsInheritance || supportsMixins) { allProperties = new LinkedHashMap(); allRequired = new ArrayList(); m.allVars = new ArrayList(); @@ -1254,17 +1255,20 @@ public CodegenModel fromModel(String name, Model model, Map 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); } } } @@ -1323,6 +1327,30 @@ public CodegenModel fromModel(String name, Model model, Map allDe return m; } + /** + * Recursively look for a discriminator in the interface tree + */ + private boolean isDiscriminatorInInterfaceTree(ComposedModel model, Map 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); diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/JavascriptClientCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/JavascriptClientCodegen.java index 950212197b7..549c1988dfb 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/JavascriptClientCodegen.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/JavascriptClientCodegen.java @@ -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))); @@ -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) { @@ -889,7 +891,7 @@ public Map postProcessModels(Map 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 required = new ArrayList<>(); - List allRequired = supportsInheritance ? new ArrayList() : required; + List allRequired = supportsInheritance || supportsMixins ? new ArrayList() : required; cm.vendorExtensions.put("x-required", required); cm.vendorExtensions.put("x-all-required", allRequired); @@ -903,7 +905,7 @@ public Map postProcessModels(Map objs) { } } - if (supportsInheritance) { + if (supportsInheritance || supportsMixins) { for (CodegenProperty var : cm.allVars) { if (Boolean.TRUE.equals(var.required)) { allRequired.add(var); diff --git a/modules/swagger-codegen/src/test/java/io/swagger/codegen/CodegenTest.java b/modules/swagger-codegen/src/test/java/io/swagger/codegen/CodegenTest.java index 9842258fb92..e96ad452806 100644 --- a/modules/swagger-codegen/src/test/java/io/swagger/codegen/CodegenTest.java +++ b/modules/swagger-codegen/src/test/java/io/swagger/codegen/CodegenTest.java @@ -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; @@ -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"); diff --git a/modules/swagger-codegen/src/test/resources/2_0/allOfTest.yaml b/modules/swagger-codegen/src/test/resources/2_0/allOfTest.yaml new file mode 100644 index 00000000000..2636d4e7aa9 --- /dev/null +++ b/modules/swagger-codegen/src/test/resources/2_0/allOfTest.yaml @@ -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 \ No newline at end of file