diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/PageOpenAPIConverter.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/PageOpenAPIConverter.java index 9d517981e..33b419f07 100644 --- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/PageOpenAPIConverter.java +++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/PageOpenAPIConverter.java @@ -94,7 +94,7 @@ public Schema resolve(AnnotatedType type, ModelConverterContext context, Iterato Class cls = javaType.getRawClass(); if (replacePageWithPagedModel && PAGE_TO_REPLACE.equals(cls.getCanonicalName())) { if (!type.isSchemaProperty()) - type = resolvePagedModelType(javaType); + type = resolvePagedModelType(javaType, type); else type.name(getParentTypeName(type, cls)); } @@ -108,13 +108,15 @@ public Schema resolve(AnnotatedType type, ModelConverterContext context, Iterato * @param type the type * @return the annotated type */ - private AnnotatedType resolvePagedModelType(JavaType type) { + private AnnotatedType resolvePagedModelType(JavaType type, AnnotatedType originalType) { if (type.hasGenericTypes()) { JavaType innerType = type.containedType(0); Type pagedModelType = ResolvableType .forClassWithGenerics(PagedModel.class, ResolvableType.forType(innerType)) .getType(); - return new AnnotatedType(pagedModelType).resolveAsRef(true); + return new AnnotatedType(pagedModelType) + .resolveAsRef(true) + .ctxAnnotations(originalType.getCtxAnnotations()); } else { return PAGED_MODEL; diff --git a/springdoc-openapi-starter-webmvc-api/pom.xml b/springdoc-openapi-starter-webmvc-api/pom.xml index fd38e4079..570ecc4ef 100644 --- a/springdoc-openapi-starter-webmvc-api/pom.xml +++ b/springdoc-openapi-starter-webmvc-api/pom.xml @@ -34,6 +34,12 @@ money-api test + + + org.springframework.data + spring-data-commons + test + diff --git a/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app245/HelloController.java b/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app245/HelloController.java new file mode 100644 index 000000000..ea5be9b7a --- /dev/null +++ b/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app245/HelloController.java @@ -0,0 +1,48 @@ +/* + * + * * + * * * + * * * * + * * * * * Copyright 2019-2025 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.v30.app245; + +import java.util.List; + +import com.fasterxml.jackson.annotation.JsonView; +import org.springdoc.core.annotations.ParameterObject; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.Pageable; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; + +@RestController +public class HelloController { + + @JsonView(Views.Hello.class) + @GetMapping(value = "/hello-paged", produces = APPLICATION_JSON_VALUE) + public ResponseEntity> getHelloWorldPaged(@ParameterObject Pageable pageable) { + return ResponseEntity.ok(new PageImpl<>(List.of(new HelloWorld("hello", "world")), pageable, 1)); + } +} diff --git a/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app245/HelloWorld.java b/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app245/HelloWorld.java new file mode 100644 index 000000000..8accd850a --- /dev/null +++ b/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app245/HelloWorld.java @@ -0,0 +1,48 @@ +/* + * + * * + * * * + * * * * + * * * * * Copyright 2019-2025 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.v30.app245; + +import com.fasterxml.jackson.annotation.JsonView; + +public class HelloWorld { + + @JsonView(Views.Hello.class) + private String hello; + + @JsonView(Views.World.class) + private String world; + + public HelloWorld(String hello, String world) { + this.hello = hello; + this.world = world; + } + + public String getHello() { + return hello; + } + + public String getWorld() { + return world; + } +} diff --git a/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app245/SpringDocApp245Test.java b/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app245/SpringDocApp245Test.java new file mode 100644 index 000000000..b1ca224fc --- /dev/null +++ b/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app245/SpringDocApp245Test.java @@ -0,0 +1,33 @@ +/* + * + * * + * * * + * * * * + * * * * * Copyright 2019-2025 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.v30.app245; + +import test.org.springdoc.api.v30.AbstractSpringDocV30Test; + +import org.springframework.boot.autoconfigure.SpringBootApplication; + +public class SpringDocApp245Test extends AbstractSpringDocV30Test { + @SpringBootApplication + static class SpringDocTestApp {} +} diff --git a/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app245/Views.java b/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app245/Views.java new file mode 100644 index 000000000..811a21d01 --- /dev/null +++ b/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app245/Views.java @@ -0,0 +1,29 @@ +/* + * + * * + * * * + * * * * + * * * * * Copyright 2019-2025 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.v30.app245; + +public class Views { + public static class Hello {} + public static class World {} +} diff --git a/springdoc-openapi-starter-webmvc-api/src/test/resources/results/3.0.1/app245.json b/springdoc-openapi-starter-webmvc-api/src/test/resources/results/3.0.1/app245.json new file mode 100644 index 000000000..42595c74b --- /dev/null +++ b/springdoc-openapi-starter-webmvc-api/src/test/resources/results/3.0.1/app245.json @@ -0,0 +1,169 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "OpenAPI definition", + "version": "v0" + }, + "servers": [ + { + "url": "http://localhost", + "description": "Generated server url" + } + ], + "paths": { + "/hello-paged": { + "get": { + "tags": [ + "hello-controller" + ], + "operationId": "getHelloWorldPaged", + "parameters": [ + { + "name": "page", + "in": "query", + "description": "Zero-based page index (0..N)", + "required": false, + "schema": { + "minimum": 0, + "type": "integer", + "default": 0 + } + }, + { + "name": "size", + "in": "query", + "description": "The size of the page to be returned", + "required": false, + "schema": { + "minimum": 1, + "type": "integer", + "default": 20 + } + }, + { + "name": "sort", + "in": "query", + "description": "Sorting criteria in the format: property,(asc|desc). Default sort order is ascending. Multiple sort criteria are supported.", + "required": false, + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PageHelloWorld_Hello" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "HelloWorld_Hello": { + "type": "object", + "properties": { + "hello": { + "type": "string" + } + } + }, + "PageableObject_Hello": { + "type": "object", + "properties": { + "pageNumber": { + "type": "integer", + "format": "int32" + }, + "pageSize": { + "type": "integer", + "format": "int32" + }, + "paged": { + "type": "boolean" + }, + "unpaged": { + "type": "boolean" + }, + "sort": { + "$ref": "#/components/schemas/SortObject_Hello" + }, + "offset": { + "type": "integer", + "format": "int64" + } + } + }, + "PageHelloWorld_Hello": { + "type": "object", + "properties": { + "totalPages": { + "type": "integer", + "format": "int32" + }, + "totalElements": { + "type": "integer", + "format": "int64" + }, + "size": { + "type": "integer", + "format": "int32" + }, + "content": { + "type": "array", + "items": { + "$ref": "#/components/schemas/HelloWorld_Hello" + } + }, + "number": { + "type": "integer", + "format": "int32" + }, + "sort": { + "$ref": "#/components/schemas/SortObject_Hello" + }, + "pageable": { + "$ref": "#/components/schemas/PageableObject_Hello" + }, + "numberOfElements": { + "type": "integer", + "format": "int32" + }, + "first": { + "type": "boolean" + }, + "last": { + "type": "boolean" + }, + "empty": { + "type": "boolean" + } + } + }, + "SortObject_Hello": { + "type": "object", + "properties": { + "empty": { + "type": "boolean" + }, + "sorted": { + "type": "boolean" + }, + "unsorted": { + "type": "boolean" + } + } + } + } + } +} \ No newline at end of file