Skip to content

Commit dd30b9d

Browse files
Replace Page with PagedModel when pageSerializationMode is set to VIA_DTO
1 parent a9bd320 commit dd30b9d

File tree

10 files changed

+1095
-0
lines changed

10 files changed

+1095
-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
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
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 test.org.springdoc.api.app10;
24+
25+
import lombok.AllArgsConstructor;
26+
import lombok.Data;
27+
28+
@Data
29+
@AllArgsConstructor
30+
public class Dummy<T> {
31+
32+
private T value;
33+
34+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
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 test.org.springdoc.api.app10;
24+
25+
import org.springframework.data.domain.Page;
26+
import org.springframework.data.domain.PageImpl;
27+
import org.springframework.data.web.PagedModel;
28+
import org.springframework.web.bind.annotation.GetMapping;
29+
import org.springframework.web.bind.annotation.RestController;
30+
31+
import java.util.List;
32+
33+
@SuppressWarnings("rawtypes")
34+
@RestController
35+
public class HelloController {
36+
37+
@GetMapping("/page-simple")
38+
public Page<String> pageSimple() {
39+
return pageImpl("test");
40+
}
41+
42+
@GetMapping("/paged-model-simple")
43+
public PagedModel<String> pagedModelSimple() {
44+
return pagedModel("test");
45+
}
46+
47+
@GetMapping("/page-complex")
48+
public Page<Dummy<List<String>>> pageComplex() {
49+
return pageImpl(new Dummy<>(List.of("test")));
50+
}
51+
52+
@GetMapping("/paged-model-complex")
53+
public PagedModel<Dummy<List<String>>> pagedModelComplex() {
54+
return pagedModel(new Dummy<>(List.of("test")));
55+
}
56+
57+
@GetMapping("/page-raw")
58+
public Page pageRaw() {
59+
return pageSimple();
60+
}
61+
62+
@GetMapping("/paged-model-raw")
63+
public PagedModel pagedModelRaw() {
64+
return pagedModelSimple();
65+
}
66+
67+
private <T> PagedModel<T> pagedModel(T value) {
68+
return new PagedModel<>(pageImpl(value));
69+
}
70+
71+
private <T> Page<T> pageImpl(T value) {
72+
return new PageImpl<>(List.of(value));
73+
}
74+
75+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
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 test.org.springdoc.api.app10;
24+
25+
import org.junit.jupiter.api.Test;
26+
import org.springdoc.core.utils.Constants;
27+
import org.springframework.boot.SpringBootConfiguration;
28+
import org.springframework.boot.autoconfigure.SpringBootApplication;
29+
import org.springframework.data.web.config.EnableSpringDataWebSupport;
30+
import test.org.springdoc.api.AbstractSpringDocTest;
31+
32+
import static org.hamcrest.Matchers.is;
33+
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
34+
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
35+
36+
public class SpringDocApp10DirectTest extends AbstractSpringDocTest {
37+
38+
@Override
39+
@Test
40+
public void testApp() throws Exception {
41+
mockMvc.perform(get(Constants.DEFAULT_API_DOCS_URL))
42+
.andExpect(status().isOk())
43+
.andExpect(jsonPath("$.openapi", is("3.0.1")))
44+
.andExpect(content().json(getContent("results/app10-direct.json"), true));
45+
}
46+
47+
@SpringBootApplication
48+
@EnableSpringDataWebSupport(pageSerializationMode = EnableSpringDataWebSupport.PageSerializationMode.DIRECT)
49+
public static class SpringDocTestApp {
50+
51+
}
52+
53+
}

0 commit comments

Comments
 (0)