Skip to content

Commit 29555dd

Browse files
committed
Changes report: fix: propagate JsonView context when resolving Page<T> schema #3226
1 parent 1a3762a commit 29555dd

File tree

7 files changed

+332
-4
lines changed

7 files changed

+332
-4
lines changed

CHANGELOG.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2323

2424
- #3232 – Gracefully handle springdoc endpoint paths during API version resolution
2525
- #3230 – Scalar source URLs resolve to `null/<groupName>` on second request when using `GroupedOpenApi`
26-
- #3226 – Propagate `JsonView` context when resolving `Page<T>` schema
2726
- #3228 – springdoc-openapi-starter 3.x doesn't depend on `org.springframework.boot:spring-boot-starter`
2827
- #3220 – Reachability metadata not compatible with GraalVM 25
2928
- #3195 – Application won't compile when OpenApi and spring-boot-data-rest is present

springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/PageOpenAPIConverter.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ public Schema resolve(AnnotatedType type, ModelConverterContext context, Iterato
9494
Class<?> cls = javaType.getRawClass();
9595
if (replacePageWithPagedModel && PAGE_TO_REPLACE.equals(cls.getCanonicalName())) {
9696
if (!type.isSchemaProperty())
97-
type = resolvePagedModelType(javaType);
97+
type = resolvePagedModelType(javaType, type);
9898
else
9999
type.name(getParentTypeName(type, cls));
100100
}
@@ -108,13 +108,15 @@ public Schema resolve(AnnotatedType type, ModelConverterContext context, Iterato
108108
* @param type the type
109109
* @return the annotated type
110110
*/
111-
private AnnotatedType resolvePagedModelType(JavaType type) {
111+
private AnnotatedType resolvePagedModelType(JavaType type, AnnotatedType originalType) {
112112
if (type.hasGenericTypes()) {
113113
JavaType innerType = type.containedType(0);
114114
Type pagedModelType = ResolvableType
115115
.forClassWithGenerics(PagedModel.class, ResolvableType.forType(innerType))
116116
.getType();
117-
return new AnnotatedType(pagedModelType).resolveAsRef(true);
117+
return new AnnotatedType(pagedModelType)
118+
.resolveAsRef(true)
119+
.ctxAnnotations(originalType.getCtxAnnotations());
118120
}
119121
else {
120122
return PAGED_MODEL;
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
*
3+
* *
4+
* * *
5+
* * * *
6+
* * * * * Copyright 2019-2026 the original author or authors.
7+
* * * * *
8+
* * * * * Licensed under the Apache License, Version 2.0 (the "License");
9+
* * * * * you may not use this file except in compliance with the License.
10+
* * * * * You may obtain a copy of the License at
11+
* * * * *
12+
* * * * * https://www.apache.org/licenses/LICENSE-2.0
13+
* * * * *
14+
* * * * * Unless required by applicable law or agreed to in writing, software
15+
* * * * * distributed under the License is distributed on an "AS IS" BASIS,
16+
* * * * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17+
* * * * * See the License for the specific language governing permissions and
18+
* * * * * limitations under the License.
19+
* * * *
20+
* * *
21+
* *
22+
*
23+
*/
24+
package test.org.springdoc.api.v30.app249;
25+
26+
import java.util.List;
27+
28+
import com.fasterxml.jackson.annotation.JsonView;
29+
import org.springdoc.core.annotations.ParameterObject;
30+
31+
import org.springframework.data.domain.Page;
32+
import org.springframework.data.domain.PageImpl;
33+
import org.springframework.data.domain.Pageable;
34+
import org.springframework.http.ResponseEntity;
35+
import org.springframework.web.bind.annotation.GetMapping;
36+
import org.springframework.web.bind.annotation.RestController;
37+
38+
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
39+
40+
@RestController
41+
public class HelloController {
42+
43+
@JsonView(Views.Hello.class)
44+
@GetMapping(value = "/hello-paged", produces = APPLICATION_JSON_VALUE)
45+
public ResponseEntity<Page<HelloWorld>> getHelloWorldPaged(@ParameterObject Pageable pageable) {
46+
return ResponseEntity.ok(new PageImpl<>(List.of(new HelloWorld("hello", "world")), pageable, 1));
47+
}
48+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
*
3+
* *
4+
* * *
5+
* * * *
6+
* * * * * Copyright 2019-2026 the original author or authors.
7+
* * * * *
8+
* * * * * Licensed under the Apache License, Version 2.0 (the "License");
9+
* * * * * you may not use this file except in compliance with the License.
10+
* * * * * You may obtain a copy of the License at
11+
* * * * *
12+
* * * * * https://www.apache.org/licenses/LICENSE-2.0
13+
* * * * *
14+
* * * * * Unless required by applicable law or agreed to in writing, software
15+
* * * * * distributed under the License is distributed on an "AS IS" BASIS,
16+
* * * * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17+
* * * * * See the License for the specific language governing permissions and
18+
* * * * * limitations under the License.
19+
* * * *
20+
* * *
21+
* *
22+
*
23+
*/
24+
package test.org.springdoc.api.v30.app249;
25+
26+
import com.fasterxml.jackson.annotation.JsonView;
27+
28+
public class HelloWorld {
29+
30+
@JsonView(Views.Hello.class)
31+
private String hello;
32+
33+
@JsonView(Views.World.class)
34+
private String world;
35+
36+
public HelloWorld(String hello, String world) {
37+
this.hello = hello;
38+
this.world = world;
39+
}
40+
41+
public String getHello() {
42+
return hello;
43+
}
44+
45+
public String getWorld() {
46+
return world;
47+
}
48+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
*
3+
* *
4+
* * *
5+
* * * *
6+
* * * * * Copyright 2019-2026 the original author or authors.
7+
* * * * *
8+
* * * * * Licensed under the Apache License, Version 2.0 (the "License");
9+
* * * * * you may not use this file except in compliance with the License.
10+
* * * * * You may obtain a copy of the License at
11+
* * * * *
12+
* * * * * https://www.apache.org/licenses/LICENSE-2.0
13+
* * * * *
14+
* * * * * Unless required by applicable law or agreed to in writing, software
15+
* * * * * distributed under the License is distributed on an "AS IS" BASIS,
16+
* * * * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17+
* * * * * See the License for the specific language governing permissions and
18+
* * * * * limitations under the License.
19+
* * * *
20+
* * *
21+
* *
22+
*
23+
*/
24+
package test.org.springdoc.api.v30.app249;
25+
26+
import test.org.springdoc.api.v30.AbstractSpringDocV30Test;
27+
28+
import org.springframework.boot.autoconfigure.SpringBootApplication;
29+
30+
public class SpringDocApp249Test extends AbstractSpringDocV30Test {
31+
@SpringBootApplication
32+
static class SpringDocTestApp {}
33+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
*
3+
* *
4+
* * *
5+
* * * *
6+
* * * * * Copyright 2019-2026 the original author or authors.
7+
* * * * *
8+
* * * * * Licensed under the Apache License, Version 2.0 (the "License");
9+
* * * * * you may not use this file except in compliance with the License.
10+
* * * * * You may obtain a copy of the License at
11+
* * * * *
12+
* * * * * https://www.apache.org/licenses/LICENSE-2.0
13+
* * * * *
14+
* * * * * Unless required by applicable law or agreed to in writing, software
15+
* * * * * distributed under the License is distributed on an "AS IS" BASIS,
16+
* * * * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17+
* * * * * See the License for the specific language governing permissions and
18+
* * * * * limitations under the License.
19+
* * * *
20+
* * *
21+
* *
22+
*
23+
*/
24+
package test.org.springdoc.api.v30.app249;
25+
26+
public class Views {
27+
public static class Hello {}
28+
public static class World {}
29+
}
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
{
2+
"openapi": "3.0.1",
3+
"info": {
4+
"title": "OpenAPI definition",
5+
"version": "v0"
6+
},
7+
"servers": [
8+
{
9+
"url": "http://localhost",
10+
"description": "Generated server url"
11+
}
12+
],
13+
"paths": {
14+
"/hello-paged": {
15+
"get": {
16+
"tags": [
17+
"hello-controller"
18+
],
19+
"operationId": "getHelloWorldPaged",
20+
"parameters": [
21+
{
22+
"name": "page",
23+
"in": "query",
24+
"description": "Zero-based page index (0..N)",
25+
"required": false,
26+
"schema": {
27+
"minimum": 0,
28+
"type": "integer",
29+
"default": 0
30+
}
31+
},
32+
{
33+
"name": "size",
34+
"in": "query",
35+
"description": "The size of the page to be returned",
36+
"required": false,
37+
"schema": {
38+
"minimum": 1,
39+
"type": "integer",
40+
"default": 20
41+
}
42+
},
43+
{
44+
"name": "sort",
45+
"in": "query",
46+
"description": "Sorting criteria in the format: property,(asc|desc). Default sort order is ascending. Multiple sort criteria are supported.",
47+
"required": false,
48+
"schema": {
49+
"type": "array",
50+
"items": {
51+
"type": "string"
52+
}
53+
}
54+
}
55+
],
56+
"responses": {
57+
"200": {
58+
"description": "OK",
59+
"content": {
60+
"application/json": {
61+
"schema": {
62+
"$ref": "#/components/schemas/PageHelloWorld_Hello"
63+
}
64+
}
65+
}
66+
}
67+
}
68+
}
69+
}
70+
},
71+
"components": {
72+
"schemas": {
73+
"HelloWorld_Hello": {
74+
"type": "object",
75+
"properties": {
76+
"hello": {
77+
"type": "string"
78+
}
79+
}
80+
},
81+
"PageableObject_Hello": {
82+
"type": "object",
83+
"properties": {
84+
"pageNumber": {
85+
"type": "integer",
86+
"format": "int32"
87+
},
88+
"pageSize": {
89+
"type": "integer",
90+
"format": "int32"
91+
},
92+
"paged": {
93+
"type": "boolean"
94+
},
95+
"unpaged": {
96+
"type": "boolean"
97+
},
98+
"sort": {
99+
"$ref": "#/components/schemas/SortObject_Hello"
100+
},
101+
"offset": {
102+
"type": "integer",
103+
"format": "int64"
104+
}
105+
}
106+
},
107+
"PageHelloWorld_Hello": {
108+
"type": "object",
109+
"properties": {
110+
"totalPages": {
111+
"type": "integer",
112+
"format": "int32"
113+
},
114+
"totalElements": {
115+
"type": "integer",
116+
"format": "int64"
117+
},
118+
"size": {
119+
"type": "integer",
120+
"format": "int32"
121+
},
122+
"content": {
123+
"type": "array",
124+
"items": {
125+
"$ref": "#/components/schemas/HelloWorld_Hello"
126+
}
127+
},
128+
"number": {
129+
"type": "integer",
130+
"format": "int32"
131+
},
132+
"sort": {
133+
"$ref": "#/components/schemas/SortObject_Hello"
134+
},
135+
"pageable": {
136+
"$ref": "#/components/schemas/PageableObject_Hello"
137+
},
138+
"numberOfElements": {
139+
"type": "integer",
140+
"format": "int32"
141+
},
142+
"first": {
143+
"type": "boolean"
144+
},
145+
"last": {
146+
"type": "boolean"
147+
},
148+
"empty": {
149+
"type": "boolean"
150+
}
151+
}
152+
},
153+
"SortObject_Hello": {
154+
"type": "object",
155+
"properties": {
156+
"empty": {
157+
"type": "boolean"
158+
},
159+
"sorted": {
160+
"type": "boolean"
161+
},
162+
"unsorted": {
163+
"type": "boolean"
164+
}
165+
}
166+
}
167+
}
168+
}
169+
}

0 commit comments

Comments
 (0)