diff --git a/springdoc-openapi-common/src/main/java/org/springdoc/core/SpringDocAnnotationsUtils.java b/springdoc-openapi-common/src/main/java/org/springdoc/core/SpringDocAnnotationsUtils.java index 29107047f..0a3527ffc 100644 --- a/springdoc-openapi-common/src/main/java/org/springdoc/core/SpringDocAnnotationsUtils.java +++ b/springdoc-openapi-common/src/main/java/org/springdoc/core/SpringDocAnnotationsUtils.java @@ -129,9 +129,16 @@ public static Schema extractSchema(Components components, Type returnType, JsonV componentSchemas.putAll(schemaMap); } else - for (Map.Entry entry : schemaMap.entrySet()) - if (!componentSchemas.containsKey(entry.getKey())) + for (Map.Entry entry : schemaMap.entrySet()) { + if (!componentSchemas.containsKey(entry.getKey())) { componentSchemas.put(entry.getKey(), entry.getValue()); + // If we've seen this schema before but find later it should be polymorphic, + // replace the existing schema with this richer version. + } else if (entry.getValue() instanceof ComposedSchema && + !(componentSchemas.get(entry.getKey()) instanceof ComposedSchema)) { + componentSchemas.put(entry.getKey(), entry.getValue()); + } + } components.setSchemas(componentSchemas); } if (resolvedSchema.schema != null) { diff --git a/springdoc-openapi-data-rest/src/test/java/test/org/springdoc/api/app31/Cat.java b/springdoc-openapi-data-rest/src/test/java/test/org/springdoc/api/app31/Cat.java new file mode 100644 index 000000000..5143cdd12 --- /dev/null +++ b/springdoc-openapi-data-rest/src/test/java/test/org/springdoc/api/app31/Cat.java @@ -0,0 +1,24 @@ +package test.org.springdoc.api.app31; + +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema +public class Cat extends Pet { + + private final boolean meows; + + public Cat() { + super(); + this.meows = false; + } + + public Cat(boolean meows, String name) { + super(name); + this.meows = meows; + } + + public boolean getMeows() { + return meows; + } + +} diff --git a/springdoc-openapi-data-rest/src/test/java/test/org/springdoc/api/app31/Dog.java b/springdoc-openapi-data-rest/src/test/java/test/org/springdoc/api/app31/Dog.java new file mode 100644 index 000000000..6c8598b31 --- /dev/null +++ b/springdoc-openapi-data-rest/src/test/java/test/org/springdoc/api/app31/Dog.java @@ -0,0 +1,24 @@ +package test.org.springdoc.api.app31; + +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema +public class Dog extends Pet { + + private final boolean barks; + + public Dog() { + super(); + this.barks = false; + } + + public Dog(boolean barks, String name) { + super(name); + this.barks = barks; + } + + public boolean getBarks() { + return barks; + } + +} diff --git a/springdoc-openapi-data-rest/src/test/java/test/org/springdoc/api/app31/Pet.java b/springdoc-openapi-data-rest/src/test/java/test/org/springdoc/api/app31/Pet.java new file mode 100644 index 000000000..dd3c228db --- /dev/null +++ b/springdoc-openapi-data-rest/src/test/java/test/org/springdoc/api/app31/Pet.java @@ -0,0 +1,23 @@ +package test.org.springdoc.api.app31; + +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; + +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") +@JsonSubTypes({ + @JsonSubTypes.Type(Dog.class), + @JsonSubTypes.Type(Cat.class) +}) +public class Pet { + + public final String name; + + public Pet() { + this.name = null; + } + + public Pet(String name) { + this.name = name; + } + +} diff --git a/springdoc-openapi-data-rest/src/test/java/test/org/springdoc/api/app31/PetController.java b/springdoc-openapi-data-rest/src/test/java/test/org/springdoc/api/app31/PetController.java new file mode 100644 index 000000000..38d320d90 --- /dev/null +++ b/springdoc-openapi-data-rest/src/test/java/test/org/springdoc/api/app31/PetController.java @@ -0,0 +1,18 @@ +package test.org.springdoc.api.app31; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class PetController { + + @GetMapping("/any") + public Pet getAnyPet() { + return new Cat(true, "cat"); + } + + @GetMapping("/dog") + public Dog getDog() { + return new Dog(true, "dog"); + } +} diff --git a/springdoc-openapi-data-rest/src/test/java/test/org/springdoc/api/app31/SpringDocApp31Test.java b/springdoc-openapi-data-rest/src/test/java/test/org/springdoc/api/app31/SpringDocApp31Test.java new file mode 100644 index 000000000..5a4f65661 --- /dev/null +++ b/springdoc-openapi-data-rest/src/test/java/test/org/springdoc/api/app31/SpringDocApp31Test.java @@ -0,0 +1,12 @@ +package test.org.springdoc.api.app31; + +import test.org.springdoc.api.AbstractSpringDocTest; + +import org.springframework.boot.autoconfigure.SpringBootApplication; + +public class SpringDocApp31Test extends AbstractSpringDocTest { + + @SpringBootApplication + static class SpringDocTestApp {} + +} \ No newline at end of file diff --git a/springdoc-openapi-data-rest/src/test/resources/results/app31.json b/springdoc-openapi-data-rest/src/test/resources/results/app31.json new file mode 100644 index 000000000..edc0fc551 --- /dev/null +++ b/springdoc-openapi-data-rest/src/test/resources/results/app31.json @@ -0,0 +1,100 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "OpenAPI definition", + "version": "v0" + }, + "servers": [{ + "url": "http://localhost", + "description": "Generated server url" + }], + "paths": { + "/dog": { + "get": { + "tags": ["pet-controller"], + "operationId": "getDog", + "responses": { + "200": { + "description": "OK", + "content": { + "application/hal+json": { + "schema": { + "$ref": "#/components/schemas/Dog" + } + } + } + } + } + } + }, + "/any": { + "get": { + "tags": ["pet-controller"], + "operationId": "getAnyPet", + "responses": { + "200": { + "description": "OK", + "content": { + "application/hal+json": { + "schema": { + "oneOf": [{ + "$ref": "#/components/schemas/Pet" + }, { + "$ref": "#/components/schemas/Cat" + }, { + "$ref": "#/components/schemas/Dog" + }] + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "Dog": { + "type": "object", + "allOf": [{ + "$ref": "#/components/schemas/Pet" + }, { + "type": "object", + "properties": { + "barks": { + "type": "boolean" + } + } + }] + }, + "Cat": { + "type": "object", + "allOf": [{ + "$ref": "#/components/schemas/Pet" + }, { + "type": "object", + "properties": { + "meows": { + "type": "boolean" + } + } + }] + }, + "Pet": { + "required": ["type"], + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "discriminator": { + "propertyName": "type" + } + } + } + } +} \ No newline at end of file