diff --git a/springdoc-openapi-common/src/main/java/org/springdoc/core/converters/ResponseSupportConverter.java b/springdoc-openapi-common/src/main/java/org/springdoc/core/converters/ResponseSupportConverter.java index f5d758901..43daa8aac 100644 --- a/springdoc-openapi-common/src/main/java/org/springdoc/core/converters/ResponseSupportConverter.java +++ b/springdoc-openapi-common/src/main/java/org/springdoc/core/converters/ResponseSupportConverter.java @@ -49,12 +49,10 @@ public Schema resolve(AnnotatedType type, ModelConverterContext context, Iterato JavaType innerType = javaType.getBindings().getBoundType(0); if (innerType == null) return new StringSchema(); - else if (innerType.getBindings() != null && isResponseTypeWrapper(innerType.getRawClass())) { - type = new AnnotatedType(innerType).jsonViewAnnotation(type.getJsonViewAnnotation()).ctxAnnotations(type.getCtxAnnotations()).resolveAsRef(true); - return this.resolve(type, context, chain); - } - else - type = new AnnotatedType(innerType).jsonViewAnnotation(type.getJsonViewAnnotation()).ctxAnnotations((type.getCtxAnnotations())).resolveAsRef(true); + return context.resolve(new AnnotatedType(innerType) + .jsonViewAnnotation(type.getJsonViewAnnotation()) + .ctxAnnotations((type.getCtxAnnotations())) + .resolveAsRef(true)); } else if (isResponseTypeToIgnore(cls)) return null; @@ -62,4 +60,4 @@ else if (isResponseTypeToIgnore(cls)) return (chain.hasNext()) ? chain.next().resolve(type, context, chain) : null; } -} \ No newline at end of file +} diff --git a/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/app157/Bar.java b/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/app157/Bar.java new file mode 100644 index 000000000..8c9ea08d4 --- /dev/null +++ b/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/app157/Bar.java @@ -0,0 +1,12 @@ +package test.org.springdoc.api.app157; + +/** + * A class without a String in it + */ +public class Bar { + private Object child; + + public Object getChild() { + return this.child; + } +} diff --git a/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/app157/Foo.java b/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/app157/Foo.java new file mode 100644 index 000000000..c139567b2 --- /dev/null +++ b/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/app157/Foo.java @@ -0,0 +1,12 @@ +package test.org.springdoc.api.app157; + +/** + * A class with a String in it + */ +public class Foo { + private String child; + + public String getChild() { + return this.child; + } +} diff --git a/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/app157/HelloController.java b/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/app157/HelloController.java new file mode 100644 index 000000000..10a514ba1 --- /dev/null +++ b/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/app157/HelloController.java @@ -0,0 +1,44 @@ +/* + * + * * Copyright 2019-2020 the original author or authors. + * * + * * Licensed under the Apache License, Version 2.0 (the "License"); + * * you may not use this file except in compliance with the License. + * * You may obtain a copy of the License at + * * + * * https://www.apache.org/licenses/LICENSE-2.0 + * * + * * Unless required by applicable law or agreed to in writing, software + * * distributed under the License is distributed on an "AS IS" BASIS, + * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * * See the License for the specific language governing permissions and + * * limitations under the License. + * + */ + +package test.org.springdoc.api.app157; + +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * Put Foo and Bar in the schema's components, make sure there is an ignored wrapper + * ({@code ResponseEntity}). + */ +@RestController +public class HelloController { + + @GetMapping( "/foo") + public ResponseEntity getFoo() { + return new ResponseEntity(HttpStatus.OK); + } + + @GetMapping( "/bar") + public ResponseEntity getBar() { + return new ResponseEntity(HttpStatus.OK); + } + + +} \ No newline at end of file diff --git a/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/app157/SpringDocApp157Test.java b/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/app157/SpringDocApp157Test.java new file mode 100644 index 000000000..34e02df6a --- /dev/null +++ b/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/app157/SpringDocApp157Test.java @@ -0,0 +1,54 @@ +package test.org.springdoc.api.app157; + +import io.swagger.v3.core.converter.ModelConverters; +import io.swagger.v3.core.util.Json; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springdoc.core.Constants; +import org.springdoc.core.converters.ModelConverterRegistrar; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.test.web.servlet.MvcResult; +import test.org.springdoc.api.AbstractSpringDocTest; + +import java.util.List; + +import static org.hamcrest.Matchers.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +/** + * This test is to make sure that a new model converter can access the parent of a type, even if + * the type is enclosed in an ignored wrapper. We test this by setting up a model converter which + * adds "stringy" to the "required" property of a schema's parent, when the sub schema is a String. + */ +public class SpringDocApp157Test extends AbstractSpringDocTest { + + @SpringBootApplication + static class SpringBootApp {} + + private StringyConverter myConverter = new StringyConverter(); + private ModelConverters converters = ModelConverters.getInstance(); + + @BeforeEach + public void registerConverter() { + converters.addConverter(myConverter); + } + + @AfterEach + public void unregisterConverter() { + converters.removeConverter(myConverter); + } + + @Test + public void testApp() throws Exception { + mockMvc.perform(get(Constants.DEFAULT_API_DOCS_URL)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.openapi", is("3.0.1"))) + .andExpect(jsonPath("$.components.schemas.Foo.required", is(List.of("stringy")))) + .andExpect(jsonPath("$.components.schemas.Bar", not(hasProperty("required")))); + } +} diff --git a/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/app157/StringyConverter.java b/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/app157/StringyConverter.java new file mode 100644 index 000000000..ccc5c18db --- /dev/null +++ b/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/app157/StringyConverter.java @@ -0,0 +1,25 @@ +package test.org.springdoc.api.app157; + +import com.fasterxml.jackson.databind.JavaType; +import io.swagger.v3.core.converter.AnnotatedType; +import io.swagger.v3.core.converter.ModelConverter; +import io.swagger.v3.core.converter.ModelConverterContext; +import io.swagger.v3.core.util.Json; +import io.swagger.v3.oas.models.media.Schema; + +import java.util.Iterator; + +public class StringyConverter implements ModelConverter { + + @Override + public Schema resolve(AnnotatedType type, ModelConverterContext context, + Iterator chain) { + + JavaType javaType = Json.mapper().constructType(type.getType()); + + if (javaType.getRawClass().equals(String.class)) { + type.getParent().addRequiredItem("stringy"); + } + return chain.next().resolve(type, context, chain); + } +}