Skip to content

Commit 3ae1ecf

Browse files
committed
Merge branch 'page-serialization' of https://github.com/EvaristeGalois11/springdoc-openapi into EvaristeGalois11-page-serialization
2 parents 479fbb7 + dd30b9d commit 3ae1ecf

File tree

17 files changed

+1529
-0
lines changed

17 files changed

+1529
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
/*
2+
*
3+
* *
4+
* * *
5+
* * * * Copyright 2019-2024 the original author or authors.
6+
* * * *
7+
* * * * Licensed under the Apache License, Version 2.0 (the "License");
8+
* * * * you may not use this file except in compliance with the License.
9+
* * * * You may obtain a copy of the License at
10+
* * * *
11+
* * * * https://www.apache.org/licenses/LICENSE-2.0
12+
* * * *
13+
* * * * Unless required by applicable law or agreed to in writing, software
14+
* * * * distributed under the License is distributed on an "AS IS" BASIS,
15+
* * * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* * * * See the License for the specific language governing permissions and
17+
* * * * limitations under the License.
18+
* * *
19+
* *
20+
*
21+
*/
22+
23+
package org.springdoc.core.configuration;
24+
25+
import org.springdoc.core.converters.PageOpenAPIConverter;
26+
import org.springdoc.core.converters.SortOpenAPIConverter;
27+
import org.springdoc.core.converters.models.SortObject;
28+
import org.springdoc.core.customizers.DataRestDelegatingMethodParameterCustomizer;
29+
import org.springdoc.core.customizers.DelegatingMethodParameterCustomizer;
30+
import org.springdoc.core.providers.ObjectMapperProvider;
31+
import org.springdoc.core.providers.RepositoryRestConfigurationProvider;
32+
import org.springdoc.core.providers.SpringDataWebPropertiesProvider;
33+
import org.springframework.boot.autoconfigure.condition.*;
34+
import org.springframework.context.annotation.Bean;
35+
import org.springframework.context.annotation.Configuration;
36+
import org.springframework.context.annotation.Lazy;
37+
import org.springframework.data.domain.Page;
38+
import org.springframework.data.domain.Sort;
39+
import org.springframework.data.web.PagedModel;
40+
import org.springframework.data.web.config.EnableSpringDataWebSupport;
41+
import org.springframework.data.web.config.SpringDataWebSettings;
42+
43+
import java.util.Optional;
44+
45+
import static org.springdoc.core.utils.Constants.SPRINGDOC_ENABLED;
46+
import static org.springdoc.core.utils.Constants.SPRINGDOC_SORT_CONVERTER_ENABLED;
47+
import static org.springdoc.core.utils.SpringDocUtils.getConfig;
48+
49+
/**
50+
* The type Spring doc page configuration.
51+
*
52+
* @author Claudio Nave
53+
*/
54+
@Lazy(false)
55+
@Configuration(proxyBeanMethods = false)
56+
@ConditionalOnProperty(name = SPRINGDOC_ENABLED, matchIfMissing = true)
57+
@ConditionalOnClass({ Page.class, PagedModel.class })
58+
@ConditionalOnWebApplication
59+
@ConditionalOnBean(SpringDocConfiguration.class)
60+
public class SpringDocPageConfiguration {
61+
62+
/**
63+
* Page open api converter.
64+
* @param objectMapperProvider the object mapper provider
65+
* @return the page open api converter
66+
*/
67+
@Bean
68+
@ConditionalOnMissingBean
69+
@ConditionalOnBean(SpringDataWebSettings.class)
70+
@Lazy(false)
71+
PageOpenAPIConverter pageOpenAPIConverter(SpringDataWebSettings settings,
72+
ObjectMapperProvider objectMapperProvider) {
73+
return new PageOpenAPIConverter(
74+
settings.pageSerializationMode() == EnableSpringDataWebSupport.PageSerializationMode.VIA_DTO,
75+
objectMapperProvider);
76+
}
77+
78+
/**
79+
* Delegating method parameter customizer delegating method parameter customizer.
80+
* @param optionalSpringDataWebPropertiesProvider the optional spring data web
81+
* properties
82+
* @param optionalRepositoryRestConfiguration the optional repository rest
83+
* configuration
84+
* @return the delegating method parameter customizer
85+
*/
86+
@Bean
87+
@ConditionalOnMissingBean
88+
@Lazy(false)
89+
DelegatingMethodParameterCustomizer delegatingMethodParameterCustomizer(
90+
Optional<SpringDataWebPropertiesProvider> optionalSpringDataWebPropertiesProvider,
91+
Optional<RepositoryRestConfigurationProvider> optionalRepositoryRestConfiguration) {
92+
return new DataRestDelegatingMethodParameterCustomizer(optionalSpringDataWebPropertiesProvider,
93+
optionalRepositoryRestConfiguration);
94+
}
95+
96+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
/*
2+
*
3+
* *
4+
* * *
5+
* * * * Copyright 2019-2024 the original author or authors.
6+
* * * *
7+
* * * * Licensed under the Apache License, Version 2.0 (the "License");
8+
* * * * you may not use this file except in compliance with the License.
9+
* * * * You may obtain a copy of the License at
10+
* * * *
11+
* * * * https://www.apache.org/licenses/LICENSE-2.0
12+
* * * *
13+
* * * * Unless required by applicable law or agreed to in writing, software
14+
* * * * distributed under the License is distributed on an "AS IS" BASIS,
15+
* * * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* * * * See the License for the specific language governing permissions and
17+
* * * * limitations under the License.
18+
* * *
19+
* *
20+
*
21+
*/
22+
23+
package org.springdoc.core.converters;
24+
25+
import com.fasterxml.jackson.databind.JavaType;
26+
import io.swagger.v3.core.converter.AnnotatedType;
27+
import io.swagger.v3.core.converter.ModelConverter;
28+
import io.swagger.v3.core.converter.ModelConverterContext;
29+
import io.swagger.v3.oas.models.media.Schema;
30+
import org.apache.commons.lang3.StringUtils;
31+
import org.springdoc.core.providers.ObjectMapperProvider;
32+
import org.springframework.core.ResolvableType;
33+
import org.springframework.data.web.PagedModel;
34+
35+
import java.lang.reflect.ParameterizedType;
36+
import java.lang.reflect.Type;
37+
import java.util.Iterator;
38+
39+
/**
40+
* The Spring Data Page type model converter.
41+
*
42+
* @author Claudio Nave
43+
*/
44+
public class PageOpenAPIConverter implements ModelConverter {
45+
46+
private static final String PAGE_TO_REPLACE = "org.springframework.data.domain.Page";
47+
48+
/**
49+
* The constant PAGED_MODEL.
50+
*/
51+
private static final AnnotatedType PAGED_MODEL = new AnnotatedType(PagedModel.class).resolveAsRef(true);
52+
53+
/**
54+
* The Spring doc object mapper.
55+
*/
56+
private final ObjectMapperProvider springDocObjectMapper;
57+
/**
58+
* Flag to replace Page with PagedModel or not.
59+
*/
60+
private final boolean replacePageWithPagedModel;
61+
62+
/**
63+
* Instantiates a new Page open api converter.
64+
* @param replacePageWithPagedModel flag to replace Page with PagedModel or not
65+
* @param springDocObjectMapper the spring doc object mapper
66+
*/
67+
public PageOpenAPIConverter(boolean replacePageWithPagedModel, ObjectMapperProvider springDocObjectMapper) {
68+
this.replacePageWithPagedModel = replacePageWithPagedModel;
69+
this.springDocObjectMapper = springDocObjectMapper;
70+
}
71+
72+
/**
73+
* Resolve schema.
74+
* @param type the type
75+
* @param context the context
76+
* @param chain the chain
77+
* @return the schema
78+
*/
79+
@Override
80+
public Schema resolve(AnnotatedType type, ModelConverterContext context, Iterator<ModelConverter> chain) {
81+
JavaType javaType = springDocObjectMapper.jsonMapper().constructType(type.getType());
82+
if (javaType != null) {
83+
Class<?> cls = javaType.getRawClass();
84+
if (replacePageWithPagedModel && PAGE_TO_REPLACE.equals(cls.getCanonicalName())) {
85+
if (!type.isSchemaProperty())
86+
type = resolvePagedModelType(type);
87+
else
88+
type.name(cls.getSimpleName() + StringUtils.capitalize(type.getParent().getType()));
89+
}
90+
}
91+
return (chain.hasNext()) ? chain.next().resolve(type, context, chain) : null;
92+
}
93+
94+
private AnnotatedType resolvePagedModelType(AnnotatedType type) {
95+
Type pageType = type.getType();
96+
if (pageType instanceof ParameterizedType) {
97+
Type argumentType = ((ParameterizedType) type.getType()).getActualTypeArguments()[0];
98+
Type pagedModelType = ResolvableType
99+
.forClassWithGenerics(PagedModel.class, ResolvableType.forType(argumentType))
100+
.getType();
101+
return new AnnotatedType(pagedModelType).resolveAsRef(true);
102+
}
103+
else {
104+
return PAGED_MODEL;
105+
}
106+
}
107+
108+
}

springdoc-openapi-starter-common/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ org.springdoc.core.configuration.SpringDocFunctionCatalogConfiguration
77
org.springdoc.core.configuration.SpringDocHateoasConfiguration
88
org.springdoc.core.configuration.SpringDocPageableConfiguration
99
org.springdoc.core.configuration.SpringDocSortConfiguration
10+
org.springdoc.core.configuration.SpringDocPageConfiguration
1011
org.springdoc.core.configuration.SpringDocSpecPropertiesConfiguration
1112
org.springdoc.core.configuration.SpringDocDataRestConfiguration
1213
org.springdoc.core.configuration.SpringDocKotlinConfiguration

springdoc-openapi-tests/springdoc-openapi-actuator-webflux-tests/src/test/resources/results/app146-1.json

+62
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,68 @@
279279
}
280280
}
281281
},
282+
"/application/sbom": {
283+
"get": {
284+
"operationId": "sbom",
285+
"responses": {
286+
"200": {
287+
"content": {
288+
"application/json": {
289+
"schema": {
290+
"type": "object"
291+
}
292+
},
293+
"application/vnd.spring-boot.actuator.v2+json": {
294+
"schema": {
295+
"type": "object"
296+
}
297+
},
298+
"application/vnd.spring-boot.actuator.v3+json": {
299+
"schema": {
300+
"type": "object"
301+
}
302+
}
303+
},
304+
"description": "OK"
305+
}
306+
},
307+
"summary": "Actuator web endpoint 'sbom'",
308+
"tags": [
309+
"Actuator"
310+
]
311+
}
312+
},
313+
"/application/sbom/{id}": {
314+
"get": {
315+
"operationId": "sbom-id",
316+
"parameters": [
317+
{
318+
"in": "path",
319+
"name": "id",
320+
"required": true,
321+
"schema": {
322+
"type": "string"
323+
}
324+
}
325+
],
326+
"responses": {
327+
"200": {
328+
"content": {
329+
"application/octet-stream": {
330+
"schema": {
331+
"type": "object"
332+
}
333+
}
334+
},
335+
"description": "OK"
336+
}
337+
},
338+
"summary": "Actuator web endpoint 'sbom-id'",
339+
"tags": [
340+
"Actuator"
341+
]
342+
}
343+
},
282344
"/application/metrics": {
283345
"get": {
284346
"tags": [

springdoc-openapi-tests/springdoc-openapi-actuator-webflux-tests/src/test/resources/results/app147-1.json

+62
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,68 @@
279279
}
280280
}
281281
},
282+
"/application/sbom": {
283+
"get": {
284+
"operationId": "sbom",
285+
"responses": {
286+
"200": {
287+
"content": {
288+
"application/json": {
289+
"schema": {
290+
"type": "object"
291+
}
292+
},
293+
"application/vnd.spring-boot.actuator.v2+json": {
294+
"schema": {
295+
"type": "object"
296+
}
297+
},
298+
"application/vnd.spring-boot.actuator.v3+json": {
299+
"schema": {
300+
"type": "object"
301+
}
302+
}
303+
},
304+
"description": "OK"
305+
}
306+
},
307+
"summary": "Actuator web endpoint 'sbom'",
308+
"tags": [
309+
"Actuator"
310+
]
311+
}
312+
},
313+
"/application/sbom/{id}": {
314+
"get": {
315+
"operationId": "sbom-id",
316+
"parameters": [
317+
{
318+
"in": "path",
319+
"name": "id",
320+
"required": true,
321+
"schema": {
322+
"type": "string"
323+
}
324+
}
325+
],
326+
"responses": {
327+
"200": {
328+
"content": {
329+
"application/octet-stream": {
330+
"schema": {
331+
"type": "object"
332+
}
333+
}
334+
},
335+
"description": "OK"
336+
}
337+
},
338+
"summary": "Actuator web endpoint 'sbom-id'",
339+
"tags": [
340+
"Actuator"
341+
]
342+
}
343+
},
282344
"/application/metrics": {
283345
"get": {
284346
"tags": [

0 commit comments

Comments
 (0)