diff --git a/pom.xml b/pom.xml index 11339c18a..19bd4b57c 100644 --- a/pom.xml +++ b/pom.xml @@ -11,7 +11,7 @@ org.springframework.boot spring-boot-starter-parent - 3.2.4 + 3.3.0 diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocPageConfiguration.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocPageConfiguration.java new file mode 100644 index 000000000..6df0b467a --- /dev/null +++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocPageConfiguration.java @@ -0,0 +1,96 @@ +/* + * + * * + * * * + * * * * Copyright 2019-2024 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 org.springdoc.core.configuration; + +import org.springdoc.core.converters.PageOpenAPIConverter; +import org.springdoc.core.converters.SortOpenAPIConverter; +import org.springdoc.core.converters.models.SortObject; +import org.springdoc.core.customizers.DataRestDelegatingMethodParameterCustomizer; +import org.springdoc.core.customizers.DelegatingMethodParameterCustomizer; +import org.springdoc.core.providers.ObjectMapperProvider; +import org.springdoc.core.providers.RepositoryRestConfigurationProvider; +import org.springdoc.core.providers.SpringDataWebPropertiesProvider; +import org.springframework.boot.autoconfigure.condition.*; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Lazy; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Sort; +import org.springframework.data.web.PagedModel; +import org.springframework.data.web.config.EnableSpringDataWebSupport; +import org.springframework.data.web.config.SpringDataWebSettings; + +import java.util.Optional; + +import static org.springdoc.core.utils.Constants.SPRINGDOC_ENABLED; +import static org.springdoc.core.utils.Constants.SPRINGDOC_SORT_CONVERTER_ENABLED; +import static org.springdoc.core.utils.SpringDocUtils.getConfig; + +/** + * The type Spring doc page configuration. + * + * @author Claudio Nave + */ +@Lazy(false) +@Configuration(proxyBeanMethods = false) +@ConditionalOnProperty(name = SPRINGDOC_ENABLED, matchIfMissing = true) +@ConditionalOnClass({ Page.class, PagedModel.class }) +@ConditionalOnWebApplication +@ConditionalOnBean(SpringDocConfiguration.class) +public class SpringDocPageConfiguration { + + /** + * Page open api converter. + * @param objectMapperProvider the object mapper provider + * @return the page open api converter + */ + @Bean + @ConditionalOnMissingBean + @ConditionalOnBean(SpringDataWebSettings.class) + @Lazy(false) + PageOpenAPIConverter pageOpenAPIConverter(SpringDataWebSettings settings, + ObjectMapperProvider objectMapperProvider) { + return new PageOpenAPIConverter( + settings.pageSerializationMode() == EnableSpringDataWebSupport.PageSerializationMode.VIA_DTO, + objectMapperProvider); + } + + /** + * Delegating method parameter customizer delegating method parameter customizer. + * @param optionalSpringDataWebPropertiesProvider the optional spring data web + * properties + * @param optionalRepositoryRestConfiguration the optional repository rest + * configuration + * @return the delegating method parameter customizer + */ + @Bean + @ConditionalOnMissingBean + @Lazy(false) + DelegatingMethodParameterCustomizer delegatingMethodParameterCustomizer( + Optional optionalSpringDataWebPropertiesProvider, + Optional optionalRepositoryRestConfiguration) { + return new DataRestDelegatingMethodParameterCustomizer(optionalSpringDataWebPropertiesProvider, + optionalRepositoryRestConfiguration); + } + +} \ No newline at end of file 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 new file mode 100644 index 000000000..3fcada041 --- /dev/null +++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/PageOpenAPIConverter.java @@ -0,0 +1,108 @@ +/* + * + * * + * * * + * * * * Copyright 2019-2024 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 org.springdoc.core.converters; + +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.oas.models.media.Schema; +import org.apache.commons.lang3.StringUtils; +import org.springdoc.core.providers.ObjectMapperProvider; +import org.springframework.core.ResolvableType; +import org.springframework.data.web.PagedModel; + +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.Iterator; + +/** + * The Spring Data Page type model converter. + * + * @author Claudio Nave + */ +public class PageOpenAPIConverter implements ModelConverter { + + private static final String PAGE_TO_REPLACE = "org.springframework.data.domain.Page"; + + /** + * The constant PAGED_MODEL. + */ + private static final AnnotatedType PAGED_MODEL = new AnnotatedType(PagedModel.class).resolveAsRef(true); + + /** + * The Spring doc object mapper. + */ + private final ObjectMapperProvider springDocObjectMapper; + /** + * Flag to replace Page with PagedModel or not. + */ + private final boolean replacePageWithPagedModel; + + /** + * Instantiates a new Page open api converter. + * @param replacePageWithPagedModel flag to replace Page with PagedModel or not + * @param springDocObjectMapper the spring doc object mapper + */ + public PageOpenAPIConverter(boolean replacePageWithPagedModel, ObjectMapperProvider springDocObjectMapper) { + this.replacePageWithPagedModel = replacePageWithPagedModel; + this.springDocObjectMapper = springDocObjectMapper; + } + + /** + * Resolve schema. + * @param type the type + * @param context the context + * @param chain the chain + * @return the schema + */ + @Override + public Schema resolve(AnnotatedType type, ModelConverterContext context, Iterator chain) { + JavaType javaType = springDocObjectMapper.jsonMapper().constructType(type.getType()); + if (javaType != null) { + Class cls = javaType.getRawClass(); + if (replacePageWithPagedModel && PAGE_TO_REPLACE.equals(cls.getCanonicalName())) { + if (!type.isSchemaProperty()) + type = resolvePagedModelType(type); + else + type.name(cls.getSimpleName() + StringUtils.capitalize(type.getParent().getType())); + } + } + return (chain.hasNext()) ? chain.next().resolve(type, context, chain) : null; + } + + private AnnotatedType resolvePagedModelType(AnnotatedType type) { + Type pageType = type.getType(); + if (pageType instanceof ParameterizedType) { + Type argumentType = ((ParameterizedType) type.getType()).getActualTypeArguments()[0]; + Type pagedModelType = ResolvableType + .forClassWithGenerics(PagedModel.class, ResolvableType.forType(argumentType)) + .getType(); + return new AnnotatedType(pagedModelType).resolveAsRef(true); + } + else { + return PAGED_MODEL; + } + } + +} \ No newline at end of file diff --git a/springdoc-openapi-starter-common/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/springdoc-openapi-starter-common/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports index 12694c003..ad61f1fa5 100644 --- a/springdoc-openapi-starter-common/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ b/springdoc-openapi-starter-common/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -7,6 +7,7 @@ org.springdoc.core.configuration.SpringDocFunctionCatalogConfiguration org.springdoc.core.configuration.SpringDocHateoasConfiguration org.springdoc.core.configuration.SpringDocPageableConfiguration org.springdoc.core.configuration.SpringDocSortConfiguration +org.springdoc.core.configuration.SpringDocPageConfiguration org.springdoc.core.configuration.SpringDocSpecPropertiesConfiguration org.springdoc.core.configuration.SpringDocDataRestConfiguration org.springdoc.core.configuration.SpringDocKotlinConfiguration diff --git a/springdoc-openapi-tests/springdoc-openapi-actuator-webflux-tests/src/test/resources/results/app146-1.json b/springdoc-openapi-tests/springdoc-openapi-actuator-webflux-tests/src/test/resources/results/app146-1.json index 4d21f4405..dc3884dd2 100644 --- a/springdoc-openapi-tests/springdoc-openapi-actuator-webflux-tests/src/test/resources/results/app146-1.json +++ b/springdoc-openapi-tests/springdoc-openapi-actuator-webflux-tests/src/test/resources/results/app146-1.json @@ -217,6 +217,68 @@ } } }, + "/application/sbom": { + "get": { + "operationId": "sbom", + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "type": "object" + } + }, + "application/vnd.spring-boot.actuator.v2+json": { + "schema": { + "type": "object" + } + }, + "application/vnd.spring-boot.actuator.v3+json": { + "schema": { + "type": "object" + } + } + }, + "description": "OK" + } + }, + "summary": "Actuator web endpoint 'sbom'", + "tags": [ + "Actuator" + ] + } + }, + "/application/sbom/{id}": { + "get": { + "operationId": "sbom-id", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/octet-stream": { + "schema": { + "type": "object" + } + } + }, + "description": "OK" + } + }, + "summary": "Actuator web endpoint 'sbom-id'", + "tags": [ + "Actuator" + ] + } + }, "/application/metrics": { "get": { "tags": [ diff --git a/springdoc-openapi-tests/springdoc-openapi-actuator-webflux-tests/src/test/resources/results/app147-1.json b/springdoc-openapi-tests/springdoc-openapi-actuator-webflux-tests/src/test/resources/results/app147-1.json index cbcf7fd50..2140650d2 100644 --- a/springdoc-openapi-tests/springdoc-openapi-actuator-webflux-tests/src/test/resources/results/app147-1.json +++ b/springdoc-openapi-tests/springdoc-openapi-actuator-webflux-tests/src/test/resources/results/app147-1.json @@ -217,6 +217,68 @@ } } }, + "/application/sbom": { + "get": { + "operationId": "sbom", + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "type": "object" + } + }, + "application/vnd.spring-boot.actuator.v2+json": { + "schema": { + "type": "object" + } + }, + "application/vnd.spring-boot.actuator.v3+json": { + "schema": { + "type": "object" + } + } + }, + "description": "OK" + } + }, + "summary": "Actuator web endpoint 'sbom'", + "tags": [ + "Actuator" + ] + } + }, + "/application/sbom/{id}": { + "get": { + "operationId": "sbom-id", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/octet-stream": { + "schema": { + "type": "object" + } + } + }, + "description": "OK" + } + }, + "summary": "Actuator web endpoint 'sbom-id'", + "tags": [ + "Actuator" + ] + } + }, "/application/metrics": { "get": { "tags": [ diff --git a/springdoc-openapi-tests/springdoc-openapi-actuator-webflux-tests/src/test/resources/results/app148-2.json b/springdoc-openapi-tests/springdoc-openapi-actuator-webflux-tests/src/test/resources/results/app148-2.json index 837455a90..ab8724aca 100644 --- a/springdoc-openapi-tests/springdoc-openapi-actuator-webflux-tests/src/test/resources/results/app148-2.json +++ b/springdoc-openapi-tests/springdoc-openapi-actuator-webflux-tests/src/test/resources/results/app148-2.json @@ -217,6 +217,68 @@ } } }, + "/application/sbom": { + "get": { + "operationId": "sbom", + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "type": "object" + } + }, + "application/vnd.spring-boot.actuator.v2+json": { + "schema": { + "type": "object" + } + }, + "application/vnd.spring-boot.actuator.v3+json": { + "schema": { + "type": "object" + } + } + }, + "description": "OK" + } + }, + "summary": "Actuator web endpoint 'sbom'", + "tags": [ + "Actuator" + ] + } + }, + "/application/sbom/{id}": { + "get": { + "operationId": "sbom-id", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/octet-stream": { + "schema": { + "type": "object" + } + } + }, + "description": "OK" + } + }, + "summary": "Actuator web endpoint 'sbom-id'", + "tags": [ + "Actuator" + ] + } + }, "/application/metrics": { "get": { "tags": [ diff --git a/springdoc-openapi-tests/springdoc-openapi-actuator-webflux-tests/src/test/resources/results/app186.json b/springdoc-openapi-tests/springdoc-openapi-actuator-webflux-tests/src/test/resources/results/app186.json index 6f5111c7a..dc6ace088 100644 --- a/springdoc-openapi-tests/springdoc-openapi-actuator-webflux-tests/src/test/resources/results/app186.json +++ b/springdoc-openapi-tests/springdoc-openapi-actuator-webflux-tests/src/test/resources/results/app186.json @@ -217,6 +217,68 @@ } } }, + "/actuator/sbom": { + "get": { + "operationId": "sbom", + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "type": "object" + } + }, + "application/vnd.spring-boot.actuator.v2+json": { + "schema": { + "type": "object" + } + }, + "application/vnd.spring-boot.actuator.v3+json": { + "schema": { + "type": "object" + } + } + }, + "description": "OK" + } + }, + "summary": "Actuator web endpoint 'sbom'", + "tags": [ + "Actuator" + ] + } + }, + "/actuator/sbom/{id}": { + "get": { + "operationId": "sbom-id", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/octet-stream": { + "schema": { + "type": "object" + } + } + }, + "description": "OK" + } + }, + "summary": "Actuator web endpoint 'sbom-id'", + "tags": [ + "Actuator" + ] + } + }, "/actuator/metrics": { "get": { "tags": [ diff --git a/springdoc-openapi-tests/springdoc-openapi-actuator-webmvc-tests/src/test/resources/results/app147-1.json b/springdoc-openapi-tests/springdoc-openapi-actuator-webmvc-tests/src/test/resources/results/app147-1.json index 601c304ca..5cd279fa0 100644 --- a/springdoc-openapi-tests/springdoc-openapi-actuator-webmvc-tests/src/test/resources/results/app147-1.json +++ b/springdoc-openapi-tests/springdoc-openapi-actuator-webmvc-tests/src/test/resources/results/app147-1.json @@ -297,6 +297,68 @@ } } }, + "/application/sbom": { + "get": { + "operationId": "sbom", + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "type": "object" + } + }, + "application/vnd.spring-boot.actuator.v2+json": { + "schema": { + "type": "object" + } + }, + "application/vnd.spring-boot.actuator.v3+json": { + "schema": { + "type": "object" + } + } + }, + "description": "OK" + } + }, + "summary": "Actuator web endpoint 'sbom'", + "tags": [ + "Actuator" + ] + } + }, + "/application/sbom/{id}": { + "get": { + "operationId": "sbom-id", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/octet-stream": { + "schema": { + "type": "object" + } + } + }, + "description": "OK" + } + }, + "summary": "Actuator web endpoint 'sbom-id'", + "tags": [ + "Actuator" + ] + } + }, "/application/mappings": { "get": { "tags": [ diff --git a/springdoc-openapi-tests/springdoc-openapi-actuator-webmvc-tests/src/test/resources/results/app148-2.json b/springdoc-openapi-tests/springdoc-openapi-actuator-webmvc-tests/src/test/resources/results/app148-2.json index 1d726c0ba..210744fd1 100644 --- a/springdoc-openapi-tests/springdoc-openapi-actuator-webmvc-tests/src/test/resources/results/app148-2.json +++ b/springdoc-openapi-tests/springdoc-openapi-actuator-webmvc-tests/src/test/resources/results/app148-2.json @@ -297,6 +297,68 @@ } } }, + "/application/sbom": { + "get": { + "operationId": "sbom", + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "type": "object" + } + }, + "application/vnd.spring-boot.actuator.v2+json": { + "schema": { + "type": "object" + } + }, + "application/vnd.spring-boot.actuator.v3+json": { + "schema": { + "type": "object" + } + } + }, + "description": "OK" + } + }, + "summary": "Actuator web endpoint 'sbom'", + "tags": [ + "Actuator" + ] + } + }, + "/application/sbom/{id}": { + "get": { + "operationId": "sbom-id", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/octet-stream": { + "schema": { + "type": "object" + } + } + }, + "description": "OK" + } + }, + "summary": "Actuator web endpoint 'sbom-id'", + "tags": [ + "Actuator" + ] + } + }, "/application/mappings": { "get": { "tags": [ diff --git a/springdoc-openapi-tests/springdoc-openapi-actuator-webmvc-tests/src/test/resources/results/app186.json b/springdoc-openapi-tests/springdoc-openapi-actuator-webmvc-tests/src/test/resources/results/app186.json index c136df41f..77e236426 100644 --- a/springdoc-openapi-tests/springdoc-openapi-actuator-webmvc-tests/src/test/resources/results/app186.json +++ b/springdoc-openapi-tests/springdoc-openapi-actuator-webmvc-tests/src/test/resources/results/app186.json @@ -297,6 +297,68 @@ } } }, + "/actuator/sbom": { + "get": { + "operationId": "sbom", + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "type": "object" + } + }, + "application/vnd.spring-boot.actuator.v2+json": { + "schema": { + "type": "object" + } + }, + "application/vnd.spring-boot.actuator.v3+json": { + "schema": { + "type": "object" + } + } + }, + "description": "OK" + } + }, + "summary": "Actuator web endpoint 'sbom'", + "tags": [ + "Actuator" + ] + } + }, + "/actuator/sbom/{id}": { + "get": { + "operationId": "sbom-id", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/octet-stream": { + "schema": { + "type": "object" + } + } + }, + "description": "OK" + } + }, + "summary": "Actuator web endpoint 'sbom-id'", + "tags": [ + "Actuator" + ] + } + }, "/actuator/mappings": { "get": { "tags": [ diff --git a/springdoc-openapi-tests/springdoc-openapi-hateoas-tests/src/test/java/test/org/springdoc/api/app10/Dummy.java b/springdoc-openapi-tests/springdoc-openapi-hateoas-tests/src/test/java/test/org/springdoc/api/app10/Dummy.java new file mode 100644 index 000000000..761794ae2 --- /dev/null +++ b/springdoc-openapi-tests/springdoc-openapi-hateoas-tests/src/test/java/test/org/springdoc/api/app10/Dummy.java @@ -0,0 +1,34 @@ +/* + * + * * + * * * + * * * * Copyright 2019-2024 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.app10; + +import lombok.AllArgsConstructor; +import lombok.Data; + +@Data +@AllArgsConstructor +public class Dummy { + + private T value; + +} diff --git a/springdoc-openapi-tests/springdoc-openapi-hateoas-tests/src/test/java/test/org/springdoc/api/app10/HelloController.java b/springdoc-openapi-tests/springdoc-openapi-hateoas-tests/src/test/java/test/org/springdoc/api/app10/HelloController.java new file mode 100644 index 000000000..0597d1fd4 --- /dev/null +++ b/springdoc-openapi-tests/springdoc-openapi-hateoas-tests/src/test/java/test/org/springdoc/api/app10/HelloController.java @@ -0,0 +1,75 @@ +/* + * + * * + * * * + * * * * Copyright 2019-2024 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.app10; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.web.PagedModel; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +@SuppressWarnings("rawtypes") +@RestController +public class HelloController { + + @GetMapping("/page-simple") + public Page pageSimple() { + return pageImpl("test"); + } + + @GetMapping("/paged-model-simple") + public PagedModel pagedModelSimple() { + return pagedModel("test"); + } + + @GetMapping("/page-complex") + public Page>> pageComplex() { + return pageImpl(new Dummy<>(List.of("test"))); + } + + @GetMapping("/paged-model-complex") + public PagedModel>> pagedModelComplex() { + return pagedModel(new Dummy<>(List.of("test"))); + } + + @GetMapping("/page-raw") + public Page pageRaw() { + return pageSimple(); + } + + @GetMapping("/paged-model-raw") + public PagedModel pagedModelRaw() { + return pagedModelSimple(); + } + + private PagedModel pagedModel(T value) { + return new PagedModel<>(pageImpl(value)); + } + + private Page pageImpl(T value) { + return new PageImpl<>(List.of(value)); + } + +} diff --git a/springdoc-openapi-tests/springdoc-openapi-hateoas-tests/src/test/java/test/org/springdoc/api/app10/SpringDocApp10DirectTest.java b/springdoc-openapi-tests/springdoc-openapi-hateoas-tests/src/test/java/test/org/springdoc/api/app10/SpringDocApp10DirectTest.java new file mode 100644 index 000000000..6ced6708d --- /dev/null +++ b/springdoc-openapi-tests/springdoc-openapi-hateoas-tests/src/test/java/test/org/springdoc/api/app10/SpringDocApp10DirectTest.java @@ -0,0 +1,53 @@ +/* + * + * * + * * * + * * * * Copyright 2019-2024 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.app10; + +import org.junit.jupiter.api.Test; +import org.springdoc.core.utils.Constants; +import org.springframework.boot.SpringBootConfiguration; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.data.web.config.EnableSpringDataWebSupport; +import test.org.springdoc.api.AbstractSpringDocTest; + +import static org.hamcrest.Matchers.is; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +public class SpringDocApp10DirectTest extends AbstractSpringDocTest { + + @Override + @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(content().json(getContent("results/app10-direct.json"), true)); + } + + @SpringBootApplication + @EnableSpringDataWebSupport(pageSerializationMode = EnableSpringDataWebSupport.PageSerializationMode.DIRECT) + public static class SpringDocTestApp { + + } + +} diff --git a/springdoc-openapi-tests/springdoc-openapi-hateoas-tests/src/test/java/test/org/springdoc/api/app10/SpringDocApp10NotSpecifiedTest.java b/springdoc-openapi-tests/springdoc-openapi-hateoas-tests/src/test/java/test/org/springdoc/api/app10/SpringDocApp10NotSpecifiedTest.java new file mode 100644 index 000000000..c6f6856dd --- /dev/null +++ b/springdoc-openapi-tests/springdoc-openapi-hateoas-tests/src/test/java/test/org/springdoc/api/app10/SpringDocApp10NotSpecifiedTest.java @@ -0,0 +1,52 @@ +/* + * + * * + * * * + * * * * Copyright 2019-2024 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.app10; + +import org.junit.jupiter.api.Test; +import org.springdoc.core.utils.Constants; +import org.springframework.boot.SpringBootConfiguration; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.data.web.config.EnableSpringDataWebSupport; +import test.org.springdoc.api.AbstractSpringDocTest; + +import static org.hamcrest.Matchers.is; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +public class SpringDocApp10NotSpecifiedTest extends AbstractSpringDocTest { + + @Override + @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(content().json(getContent("results/app10-direct.json"), true)); + } + + @SpringBootApplication + public static class SpringDocTestApp { + + } + +} diff --git a/springdoc-openapi-tests/springdoc-openapi-hateoas-tests/src/test/java/test/org/springdoc/api/app10/SpringDocApp10ViaDtoTest.java b/springdoc-openapi-tests/springdoc-openapi-hateoas-tests/src/test/java/test/org/springdoc/api/app10/SpringDocApp10ViaDtoTest.java new file mode 100644 index 000000000..7d37e9f38 --- /dev/null +++ b/springdoc-openapi-tests/springdoc-openapi-hateoas-tests/src/test/java/test/org/springdoc/api/app10/SpringDocApp10ViaDtoTest.java @@ -0,0 +1,54 @@ +/* + * + * * + * * * + * * * * Copyright 2019-2024 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.app10; + +import org.junit.jupiter.api.Test; +import org.springdoc.core.utils.Constants; +import org.springframework.boot.SpringBootConfiguration; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.data.web.config.EnableSpringDataWebSupport; +import test.org.springdoc.api.AbstractSpringDocTest; + +import static org.hamcrest.Matchers.is; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +public class SpringDocApp10ViaDtoTest extends AbstractSpringDocTest { + + @Override + @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(content().json(getContent("results/app10-via_dto.json"), true)); + } + + @SpringBootApplication + @EnableSpringDataWebSupport(pageSerializationMode = EnableSpringDataWebSupport.PageSerializationMode.VIA_DTO) + public static class SpringDocTestApp { + + } + +} diff --git a/springdoc-openapi-tests/springdoc-openapi-hateoas-tests/src/test/resources/results/app10-direct.json b/springdoc-openapi-tests/springdoc-openapi-hateoas-tests/src/test/resources/results/app10-direct.json new file mode 100644 index 000000000..886b96496 --- /dev/null +++ b/springdoc-openapi-tests/springdoc-openapi-hateoas-tests/src/test/resources/results/app10-direct.json @@ -0,0 +1,409 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "OpenAPI definition", + "version": "v0" + }, + "servers": [ + { + "url": "http://localhost", + "description": "Generated server url" + } + ], + "paths": { + "/paged-model-simple": { + "get": { + "tags": [ + "hello-controller" + ], + "operationId": "pagedModelSimple", + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/PagedModelString" + } + } + } + } + } + } + }, + "/paged-model-raw": { + "get": { + "tags": [ + "hello-controller" + ], + "operationId": "pagedModelRaw", + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/PagedModel" + } + } + } + } + } + } + }, + "/paged-model-complex": { + "get": { + "tags": [ + "hello-controller" + ], + "operationId": "pagedModelComplex", + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/PagedModelDummyListString" + } + } + } + } + } + } + }, + "/page-simple": { + "get": { + "tags": [ + "hello-controller" + ], + "operationId": "pageSimple", + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/PageString" + } + } + } + } + } + } + }, + "/page-raw": { + "get": { + "tags": [ + "hello-controller" + ], + "operationId": "pageRaw", + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/Page" + } + } + } + } + } + } + }, + "/page-complex": { + "get": { + "tags": [ + "hello-controller" + ], + "operationId": "pageComplex", + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/PageDummyListString" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "PageMetadata": { + "type": "object", + "properties": { + "size": { + "type": "integer", + "format": "int64" + }, + "number": { + "type": "integer", + "format": "int64" + }, + "totalElements": { + "type": "integer", + "format": "int64" + }, + "totalPages": { + "type": "integer", + "format": "int64" + } + } + }, + "PagedModelString": { + "type": "object", + "properties": { + "content": { + "type": "array", + "items": { + "type": "string" + } + }, + "page": { + "$ref": "#/components/schemas/PageMetadata" + } + } + }, + "PagedModel": { + "type": "object", + "properties": { + "content": { + "type": "array", + "items": { + "type": "object" + } + }, + "page": { + "$ref": "#/components/schemas/PageMetadata" + } + } + }, + "DummyListString": { + "type": "object", + "properties": { + "value": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "PagedModelDummyListString": { + "type": "object", + "properties": { + "content": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DummyListString" + } + }, + "page": { + "$ref": "#/components/schemas/PageMetadata" + } + } + }, + "PageString": { + "type": "object", + "properties": { + "totalPages": { + "type": "integer", + "format": "int32" + }, + "totalElements": { + "type": "integer", + "format": "int64" + }, + "pageable": { + "$ref": "#/components/schemas/PageableObject" + }, + "first": { + "type": "boolean" + }, + "last": { + "type": "boolean" + }, + "size": { + "type": "integer", + "format": "int32" + }, + "content": { + "type": "array", + "items": { + "type": "string" + } + }, + "number": { + "type": "integer", + "format": "int32" + }, + "sort": { + "type": "array", + "items": { + "$ref": "#/components/schemas/SortObject" + } + }, + "numberOfElements": { + "type": "integer", + "format": "int32" + }, + "empty": { + "type": "boolean" + } + } + }, + "PageableObject": { + "type": "object", + "properties": { + "paged": { + "type": "boolean" + }, + "pageNumber": { + "type": "integer", + "format": "int32" + }, + "pageSize": { + "type": "integer", + "format": "int32" + }, + "offset": { + "type": "integer", + "format": "int64" + }, + "sort": { + "type": "array", + "items": { + "$ref": "#/components/schemas/SortObject" + } + }, + "unpaged": { + "type": "boolean" + } + } + }, + "SortObject": { + "type": "object", + "properties": { + "direction": { + "type": "string" + }, + "nullHandling": { + "type": "string" + }, + "ascending": { + "type": "boolean" + }, + "property": { + "type": "string" + }, + "ignoreCase": { + "type": "boolean" + } + } + }, + "Page": { + "type": "object", + "properties": { + "totalPages": { + "type": "integer", + "format": "int32" + }, + "totalElements": { + "type": "integer", + "format": "int64" + }, + "pageable": { + "$ref": "#/components/schemas/PageableObject" + }, + "first": { + "type": "boolean" + }, + "last": { + "type": "boolean" + }, + "size": { + "type": "integer", + "format": "int32" + }, + "content": { + "type": "array", + "items": { + "type": "object" + } + }, + "number": { + "type": "integer", + "format": "int32" + }, + "sort": { + "type": "array", + "items": { + "$ref": "#/components/schemas/SortObject" + } + }, + "numberOfElements": { + "type": "integer", + "format": "int32" + }, + "empty": { + "type": "boolean" + } + } + }, + "PageDummyListString": { + "type": "object", + "properties": { + "totalPages": { + "type": "integer", + "format": "int32" + }, + "totalElements": { + "type": "integer", + "format": "int64" + }, + "pageable": { + "$ref": "#/components/schemas/PageableObject" + }, + "first": { + "type": "boolean" + }, + "last": { + "type": "boolean" + }, + "size": { + "type": "integer", + "format": "int32" + }, + "content": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DummyListString" + } + }, + "number": { + "type": "integer", + "format": "int32" + }, + "sort": { + "type": "array", + "items": { + "$ref": "#/components/schemas/SortObject" + } + }, + "numberOfElements": { + "type": "integer", + "format": "int32" + }, + "empty": { + "type": "boolean" + } + } + } + } + } +} \ No newline at end of file diff --git a/springdoc-openapi-tests/springdoc-openapi-hateoas-tests/src/test/resources/results/app10-via_dto.json b/springdoc-openapi-tests/springdoc-openapi-hateoas-tests/src/test/resources/results/app10-via_dto.json new file mode 100644 index 000000000..32cad61c1 --- /dev/null +++ b/springdoc-openapi-tests/springdoc-openapi-hateoas-tests/src/test/resources/results/app10-via_dto.json @@ -0,0 +1,213 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "OpenAPI definition", + "version": "v0" + }, + "servers": [ + { + "url": "http://localhost", + "description": "Generated server url" + } + ], + "paths": { + "/paged-model-simple": { + "get": { + "tags": [ + "hello-controller" + ], + "operationId": "pagedModelSimple", + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/PagedModelString" + } + } + } + } + } + } + }, + "/paged-model-raw": { + "get": { + "tags": [ + "hello-controller" + ], + "operationId": "pagedModelRaw", + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/PagedModel" + } + } + } + } + } + } + }, + "/paged-model-complex": { + "get": { + "tags": [ + "hello-controller" + ], + "operationId": "pagedModelComplex", + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/PagedModelDummyListString" + } + } + } + } + } + } + }, + "/page-simple": { + "get": { + "tags": [ + "hello-controller" + ], + "operationId": "pageSimple", + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/PagedModelString" + } + } + } + } + } + } + }, + "/page-raw": { + "get": { + "tags": [ + "hello-controller" + ], + "operationId": "pageRaw", + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/PagedModel" + } + } + } + } + } + } + }, + "/page-complex": { + "get": { + "tags": [ + "hello-controller" + ], + "operationId": "pageComplex", + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/PagedModelDummyListString" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "PageMetadata": { + "type": "object", + "properties": { + "size": { + "type": "integer", + "format": "int64" + }, + "number": { + "type": "integer", + "format": "int64" + }, + "totalElements": { + "type": "integer", + "format": "int64" + }, + "totalPages": { + "type": "integer", + "format": "int64" + } + } + }, + "PagedModelString": { + "type": "object", + "properties": { + "content": { + "type": "array", + "items": { + "type": "string" + } + }, + "page": { + "$ref": "#/components/schemas/PageMetadata" + } + } + }, + "PagedModel": { + "type": "object", + "properties": { + "content": { + "type": "array", + "items": { + "type": "object" + } + }, + "page": { + "$ref": "#/components/schemas/PageMetadata" + } + } + }, + "DummyListString": { + "type": "object", + "properties": { + "value": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "PagedModelDummyListString": { + "type": "object", + "properties": { + "content": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DummyListString" + } + }, + "page": { + "$ref": "#/components/schemas/PageMetadata" + } + } + } + } + } +} \ No newline at end of file