diff --git a/pom.xml b/pom.xml index a5c49cdbb..0f2d9d8ba 100644 --- a/pom.xml +++ b/pom.xml @@ -2,7 +2,7 @@ 4.0.0 org.springdoc springdoc-openapi - 2.5.0 + 2.6.0 pom Spring openapi documentation Spring openapi documentation @@ -11,7 +11,7 @@ org.springframework.boot spring-boot-starter-parent - 3.2.4 + 3.3.0 @@ -35,7 +35,7 @@ scm:git:git@github.com:springdoc/springdoc-openapi.git scm:git:git@github.com:springdoc/springdoc-openapi.git - v2.5.0 + v2.6.0 @@ -60,15 +60,13 @@ 1.6 2.5.3 1.6.8 - 2.2.21 - 5.13.0 + 2.2.22 + 5.17.14 1.13.1 - 2.1 - 1.1 0.9.1 0.15.0 - 4.0.0 - 1.0.1 + 4.1.2 + 1.3.0 diff --git a/springdoc-openapi-starter-common/pom.xml b/springdoc-openapi-starter-common/pom.xml index 958f25193..bdd9a305d 100644 --- a/springdoc-openapi-starter-common/pom.xml +++ b/springdoc-openapi-starter-common/pom.xml @@ -3,7 +3,7 @@ org.springdoc springdoc-openapi - 2.5.0 + 2.6.0 springdoc-openapi-starter-common @@ -51,6 +51,11 @@ spring-security-oauth2-authorization-server true + + org.springframework.security + spring-security-oauth2-client + true + com.fasterxml.jackson.module diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/conditions/MultipleOpenApiGroupsCondition.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/conditions/MultipleOpenApiGroupsCondition.java index 63a1ab686..66df9e4ad 100644 --- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/conditions/MultipleOpenApiGroupsCondition.java +++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/conditions/MultipleOpenApiGroupsCondition.java @@ -24,6 +24,8 @@ package org.springdoc.core.conditions; +import java.util.Collection; + import org.springdoc.core.models.GroupedOpenApi; import org.springframework.boot.autoconfigure.condition.AnyNestedCondition; @@ -59,4 +61,9 @@ static class OnGroupedOpenApiBean {} @ConditionalOnProperty(name = GROUP_CONFIG_FIRST_PROPERTY) static class OnGroupConfigProperty {} + /** + * The type On list grouped open api bean. + */ + @ConditionalOnBean(value = GroupedOpenApi.class, parameterizedContainer = Collection.class) + static class OnListGroupedOpenApiBean {} } \ No newline at end of file diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocKotlinConfiguration.kt b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocKotlinConfiguration.kt index 7c7dd6070..fc09e6465 100644 --- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocKotlinConfiguration.kt +++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocKotlinConfiguration.kt @@ -92,9 +92,13 @@ class SpringDocKotlinConfiguration() { // parameter is not required if a default value is provided in @RequestParam else if (requestParam != null && requestParam.defaultValue != ValueConstants.DEFAULT_NONE) parameterModel.required = false - else + else{ + val isJavaNullableAnnotationPresent = methodParameter.parameterAnnotations.any { + it.annotationClass.qualifiedName == "jakarta.annotation.Nullable" + } parameterModel.required = - kParameter.type.isMarkedNullable == false + kParameter.type.isMarkedNullable == false && !isJavaNullableAnnotationPresent + } } } return@ParameterCustomizer parameterModel diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocPageableConfiguration.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocPageableConfiguration.java index 0dd0a9e50..b12aeb55b 100644 --- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocPageableConfiguration.java +++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocPageableConfiguration.java @@ -26,6 +26,7 @@ import java.util.Optional; +import org.springdoc.core.converters.PageOpenAPIConverter; import org.springdoc.core.converters.PageableOpenAPIConverter; import org.springdoc.core.customizers.DataRestDelegatingMethodParameterCustomizer; import org.springdoc.core.customizers.DelegatingMethodParameterCustomizer; @@ -42,6 +43,9 @@ import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Lazy; import org.springframework.data.domain.Pageable; +import org.springframework.data.web.PagedModel; +import org.springframework.data.web.config.EnableSpringDataWebSupport; +import org.springframework.data.web.config.SpringDataWebSettings; import static org.springdoc.core.utils.Constants.SPRINGDOC_ENABLED; import static org.springdoc.core.utils.Constants.SPRINGDOC_PAGEABLE_CONVERTER_ENABLED; @@ -75,6 +79,23 @@ PageableOpenAPIConverter pageableOpenAPIConverter(ObjectMapperProvider objectMap return new PageableOpenAPIConverter(objectMapperProvider); } + /** + * Page open api converter. + * @param objectMapperProvider the object mapper provider + * @return the page open api converter + */ + @Bean + @ConditionalOnMissingBean + @ConditionalOnClass({ PagedModel.class, SpringDataWebSettings.class }) + @Lazy(false) + PageOpenAPIConverter pageOpenAPIConverter(Optional settings, + ObjectMapperProvider objectMapperProvider) { + boolean replacePageWithPagedModel = settings.map(SpringDataWebSettings::pageSerializationMode) + .map(EnableSpringDataWebSupport.PageSerializationMode.VIA_DTO::equals) + .orElse(false); + return new PageOpenAPIConverter(replacePageWithPagedModel, objectMapperProvider); + } + /** * Delegating method parameter customizer delegating method parameter customizer. * diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocSecurityConfiguration.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocSecurityConfiguration.java index 465a9bc4a..d577f12fd 100644 --- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocSecurityConfiguration.java +++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocSecurityConfiguration.java @@ -58,6 +58,7 @@ import org.springframework.http.HttpStatus; import org.springframework.security.core.Authentication; import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.security.oauth2.client.annotation.RegisteredOAuth2AuthorizedClient; import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService; import org.springframework.security.web.FilterChainProxy; import org.springframework.security.web.SecurityFilterChain; @@ -169,6 +170,9 @@ OpenApiCustomizer springSecurityLoginEndpointCustomiser(ApplicationContext appli } } + /** + * The type Spring doc security o auth 2 configuration. + */ @Lazy(false) @Configuration(proxyBeanMethods = false) @ConditionalOnClass(OAuth2AuthorizationService.class) @@ -186,4 +190,19 @@ GlobalOpenApiCustomizer springDocSecurityOAuth2Customizer() { return new SpringDocSecurityOAuth2Customizer(); } } + + /** + * The type Spring doc security o auth 2 client configuration. + */ + @Lazy(false) + @Configuration(proxyBeanMethods = false) + @ConditionalOnClass(RegisteredOAuth2AuthorizedClient.class) + class SpringDocSecurityOAuth2ClientConfiguration { + + static { + getConfig() + .addAnnotationsToIgnore(RegisteredOAuth2AuthorizedClient.class); + } + + } } \ No newline at end of file diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocSecurityOAuth2Customizer.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocSecurityOAuth2Customizer.java index 871ebdbb6..d155b9459 100644 --- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocSecurityOAuth2Customizer.java +++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocSecurityOAuth2Customizer.java @@ -4,6 +4,8 @@ import io.swagger.v3.core.util.AnnotationsUtils; import io.swagger.v3.oas.annotations.enums.ParameterIn; +import io.swagger.v3.oas.annotations.enums.SecuritySchemeType; +import io.swagger.v3.oas.annotations.security.SecurityRequirements; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.Operation; import io.swagger.v3.oas.models.PathItem; @@ -18,9 +20,14 @@ import io.swagger.v3.oas.models.media.StringSchema; import io.swagger.v3.oas.models.parameters.HeaderParameter; import io.swagger.v3.oas.models.parameters.Parameter; +import io.swagger.v3.oas.models.parameters.PathParameter; import io.swagger.v3.oas.models.parameters.RequestBody; import io.swagger.v3.oas.models.responses.ApiResponse; import io.swagger.v3.oas.models.responses.ApiResponses; +import io.swagger.v3.oas.models.security.SecurityRequirement; +import io.swagger.v3.oas.models.security.SecurityScheme; +import io.swagger.v3.oas.models.security.SecurityScheme.In; +import io.swagger.v3.oas.models.security.SecurityScheme.Type; import org.apache.commons.lang3.reflect.FieldUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -55,6 +62,7 @@ import org.springframework.security.web.util.matcher.AntPathRequestMatcher; import org.springframework.security.web.util.matcher.OrRequestMatcher; import org.springframework.security.web.util.matcher.RequestMatcher; +import org.springframework.util.ReflectionUtils; import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; import static org.springframework.http.MediaType.TEXT_HTML_VALUE; @@ -110,7 +118,7 @@ public void customise(OpenAPI openAPI) { * @param securityFilterChain the security filter chain * @param openapi31 the openapi 31 */ - private void getOAuth2TokenRevocationEndpointFilter(OpenAPI openAPI, SecurityFilterChain securityFilterChain, boolean openapi31) { + private void getOAuth2TokenRevocationEndpointFilter(OpenAPI openAPI, SecurityFilterChain securityFilterChain, boolean openapi31) { Object oAuth2EndpointFilter = new SpringDocSecurityOAuth2EndpointUtils(OAuth2TokenRevocationEndpointFilter.class).findEndpoint(securityFilterChain); if (oAuth2EndpointFilter != null) { @@ -168,14 +176,24 @@ private void getOAuth2TokenIntrospectionEndpointFilter(OpenAPI openAPI, Security * @param openapi31 the openapi 31 */ private void getOAuth2AuthorizationServerMetadataEndpoint(OpenAPI openAPI, SecurityFilterChain securityFilterChain, boolean openapi31) { + ClassauthorizationServerMetadataEndpointClass = OAuth2AuthorizationServerMetadataEndpointFilter.class; Object oAuth2EndpointFilter = - new SpringDocSecurityOAuth2EndpointUtils(OAuth2AuthorizationServerMetadataEndpointFilter.class).findEndpoint(securityFilterChain); + new SpringDocSecurityOAuth2EndpointUtils(authorizationServerMetadataEndpointClass).findEndpoint(securityFilterChain); if (oAuth2EndpointFilter != null) { ApiResponses apiResponses = new ApiResponses(); buildApiResponsesOnSuccess(apiResponses, AnnotationsUtils.resolveSchemaFromType(SpringDocOAuth2AuthorizationServerMetadata.class, openAPI.getComponents(), null, openapi31)); buildApiResponsesOnInternalServerError(apiResponses); Operation operation = buildOperation(apiResponses); - buildPath(oAuth2EndpointFilter, REQUEST_MATCHER, openAPI, operation, HttpMethod.GET); + Field field = ReflectionUtils.findField(authorizationServerMetadataEndpointClass, "DEFAULT_OAUTH2_AUTHORIZATION_SERVER_METADATA_ENDPOINT_URI"); + if (field != null) { + ReflectionUtils.makeAccessible(field); + String defaultOauth2MetadataUri = (String) ReflectionUtils.getField(field, null); + openAPI.getPaths().addPathItem(defaultOauth2MetadataUri , new PathItem().get(operation)); + operation = buildOperation(apiResponses); + operation.addParametersItem(new PathParameter().name("subpath").schema(new StringSchema())); + operation.summary("Valid when multiple issuers are allowed"); + openAPI.getPaths().addPathItem(defaultOauth2MetadataUri+"/{subpath}" , new PathItem().get(operation)); + } } } @@ -245,7 +263,7 @@ private void getOAuth2TokenEndpoint(OpenAPI openAPI, SecurityFilterChain securit String mediaType = org.springframework.http.MediaType.APPLICATION_FORM_URLENCODED_VALUE; RequestBody requestBody = new RequestBody().content(new Content().addMediaType(mediaType, new MediaType().schema(requestSchema))); operation.setRequestBody(requestBody); - operation.addParametersItem(new HeaderParameter().name("Authorization")); + operation.addParametersItem(new HeaderParameter().name("Authorization").schema(new StringSchema())); buildPath(oAuth2EndpointFilter, "tokenEndpointMatcher", openAPI, operation, HttpMethod.POST); } @@ -288,15 +306,26 @@ private void getOAuth2AuthorizationEndpoint(OpenAPI openAPI, SecurityFilterChain * @param openapi31 the openapi 31 */ private void getOidcProviderConfigurationEndpoint(OpenAPI openAPI, SecurityFilterChain securityFilterChain, boolean openapi31) { + Class oidcProviderConfigurationEndpointFilterClass = OidcProviderConfigurationEndpointFilter.class; Object oAuth2EndpointFilter = - new SpringDocSecurityOAuth2EndpointUtils(OidcProviderConfigurationEndpointFilter.class).findEndpoint(securityFilterChain); + new SpringDocSecurityOAuth2EndpointUtils(oidcProviderConfigurationEndpointFilterClass).findEndpoint(securityFilterChain); if (oAuth2EndpointFilter != null) { ApiResponses apiResponses = new ApiResponses(); buildApiResponsesOnSuccess(apiResponses, AnnotationsUtils.resolveSchemaFromType(SpringDocOidcProviderConfiguration.class, openAPI.getComponents(), null, openapi31)); buildApiResponsesOnInternalServerError(apiResponses); Operation operation = buildOperation(apiResponses); - buildPath(oAuth2EndpointFilter, REQUEST_MATCHER, openAPI, operation, HttpMethod.GET); + + Field field = ReflectionUtils.findField(oidcProviderConfigurationEndpointFilterClass, "DEFAULT_OIDC_PROVIDER_CONFIGURATION_ENDPOINT_URI"); + if (field != null) { + ReflectionUtils.makeAccessible(field); + String defaultOidcConfigUri = (String) ReflectionUtils.getField(field, null); + openAPI.getPaths().addPathItem(defaultOidcConfigUri , new PathItem().get(operation)); + operation = buildOperation(apiResponses); + operation.addParametersItem(new PathParameter().name("subpath").schema(new StringSchema())); + operation.summary("Valid when multiple issuers are allowed"); + openAPI.getPaths().addPathItem("/{subpath}"+defaultOidcConfigUri , new PathItem().get(operation)); + } } } @@ -346,7 +375,7 @@ private void getOidcClientRegistrationEndpoint(OpenAPI openAPI, SecurityFilterCh String mediaType = APPLICATION_JSON_VALUE; RequestBody requestBody = new RequestBody().content(new Content().addMediaType(mediaType, new MediaType().schema(schema))); operation.setRequestBody(requestBody); - operation.addParametersItem(new HeaderParameter().name("Authorization")); + operation.addParametersItem(new HeaderParameter().name("Authorization").schema(new StringSchema())); buildPath(oAuth2EndpointFilter, "clientRegistrationEndpointMatcher", openAPI, operation, HttpMethod.POST); } 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..63aaca0a8 --- /dev/null +++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/PageOpenAPIConverter.java @@ -0,0 +1,109 @@ +/* + * + * * + * * * + * * * * 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 java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.Iterator; + +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; + +/** + * 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/java/org/springdoc/core/converters/PolymorphicModelConverter.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/PolymorphicModelConverter.java index 2f04a52c8..e243f7e1a 100644 --- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/PolymorphicModelConverter.java +++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/PolymorphicModelConverter.java @@ -25,7 +25,9 @@ package org.springdoc.core.converters; import java.lang.reflect.Modifier; +import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.Iterator; import java.util.List; @@ -41,6 +43,7 @@ /** * The type Polymorphic model converter. + * * @author bnasslahsen */ public class PolymorphicModelConverter implements ModelConverter { @@ -50,6 +53,17 @@ public class PolymorphicModelConverter implements ModelConverter { */ private final ObjectMapperProvider springDocObjectMapper; + /** + * The constant PARENT_TYPES_TO_IGNORE. + */ + private static final List PARENT_TYPES_TO_IGNORE = Collections.synchronizedList(new ArrayList<>()); + + static { + PARENT_TYPES_TO_IGNORE.add("JsonSchema"); + PARENT_TYPES_TO_IGNORE.add("Pageable"); + PARENT_TYPES_TO_IGNORE.add("EntityModel"); + } + /** * Instantiates a new Polymorphic model converter. * @@ -59,12 +73,23 @@ public PolymorphicModelConverter(ObjectMapperProvider springDocObjectMapper) { this.springDocObjectMapper = springDocObjectMapper; } - private static Schema getResolvedSchema(JavaType javaType, Schema resolvedSchema) { + /** + * Add parent type. + * + * @param parentTypes the parent types + */ + public static void addParentType(String... parentTypes) { + PARENT_TYPES_TO_IGNORE.addAll(List.of(parentTypes)); + } + + private Schema getResolvedSchema(JavaType javaType, Schema resolvedSchema) { if (resolvedSchema instanceof ObjectSchema && resolvedSchema.getProperties() != null) { - if (resolvedSchema.getProperties().containsKey(javaType.getRawClass().getName())) + if (resolvedSchema.getProperties().containsKey(javaType.getRawClass().getName())) { resolvedSchema = resolvedSchema.getProperties().get(javaType.getRawClass().getName()); - else if (resolvedSchema.getProperties().containsKey(javaType.getRawClass().getSimpleName())) + } + else if (resolvedSchema.getProperties().containsKey(javaType.getRawClass().getSimpleName())) { resolvedSchema = resolvedSchema.getProperties().get(javaType.getRawClass().getSimpleName()); + } } return resolvedSchema; } @@ -74,6 +99,9 @@ public Schema resolve(AnnotatedType type, ModelConverterContext context, Iterato JavaType javaType = springDocObjectMapper.jsonMapper().constructType(type.getType()); if (javaType != null) { if (chain.hasNext()) { + if (!type.isResolveAsRef() && type.getParent() != null + && PARENT_TYPES_TO_IGNORE.stream().noneMatch(ignore -> type.getParent().getName().startsWith(ignore))) + type.resolveAsRef(true); Schema resolvedSchema = chain.next().resolve(type, context, chain); resolvedSchema = getResolvedSchema(javaType, resolvedSchema); if (resolvedSchema == null || resolvedSchema.get$ref() == null) @@ -87,13 +115,31 @@ public Schema resolve(AnnotatedType type, ModelConverterContext context, Iterato /** * Compose polymorphic schema. * - * @param type the type - * @param schema the schema + * @param type the type + * @param schema the schema * @param schemas the schemas * @return the schema */ private Schema composePolymorphicSchema(AnnotatedType type, Schema schema, Collection schemas) { String ref = schema.get$ref(); + List composedSchemas = findComposedSchemas(ref, schemas); + + if (composedSchemas.isEmpty()) return schema; + + ComposedSchema result = new ComposedSchema(); + if (isConcreteClass(type)) result.addOneOfItem(schema); + composedSchemas.forEach(result::addOneOfItem); + return result; + } + + /** + * Find composed schemas recursively. + * + * @param ref the reference of the schema + * @param schemas the collection of schemas to search in + * @return the list of composed schemas + */ + private List findComposedSchemas(String ref, Collection schemas) { List composedSchemas = schemas.stream() .filter(ComposedSchema.class::isInstance) .map(ComposedSchema.class::cast) @@ -101,12 +147,15 @@ private Schema composePolymorphicSchema(AnnotatedType type, Schema schema, Colle .filter(s -> s.getAllOf().stream().anyMatch(s2 -> ref.equals(s2.get$ref()))) .map(s -> new Schema().$ref(AnnotationsUtils.COMPONENTS_REF + s.getName())) .toList(); - if (composedSchemas.isEmpty()) return schema; - ComposedSchema result = new ComposedSchema(); - if (isConcreteClass(type)) result.addOneOfItem(schema); - composedSchemas.forEach(result::addOneOfItem); - return result; + List resultSchemas = new ArrayList<>(composedSchemas); + + for (Schema childSchema : composedSchemas) { + String childSchemaRef = childSchema.get$ref(); + resultSchemas.addAll(findComposedSchemas(childSchemaRef, schemas)); + } + + return resultSchemas; } /** diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/ServerBaseUrlCustomizer.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/ServerBaseUrlCustomizer.java index b209ba72e..47c5d3891 100644 --- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/ServerBaseUrlCustomizer.java +++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/ServerBaseUrlCustomizer.java @@ -24,6 +24,8 @@ package org.springdoc.core.customizers; +import org.springframework.http.HttpRequest; + /** * The interface Server Base URL customiser. * @author skylar -stark @@ -35,7 +37,8 @@ public interface ServerBaseUrlCustomizer { * Customise. * * @param serverBaseUrl the serverBaseUrl. + * @param request the request. * @return the customised serverBaseUrl */ - String customize(String serverBaseUrl); + String customize(String serverBaseUrl, HttpRequest request); } diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/data/DataRestRequestService.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/data/DataRestRequestService.java index b756e7a87..fd67c75c0 100644 --- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/data/DataRestRequestService.java +++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/data/DataRestRequestService.java @@ -175,7 +175,7 @@ else if (methodParameter.getParameterAnnotation(BackendId.class) != null) { parameterInfo.setParameterModel(parameter); } if (!ArrayUtils.isEmpty(methodParameter.getParameterAnnotations())) - parameter = requestBuilder.buildParams(parameterInfo, openAPI.getComponents(), requestMethod, null, + parameter = requestBuilder.buildParams(parameterInfo, openAPI.getComponents(), requestMethod, methodAttributes, openAPI.getOpenapi()); addParameters(openAPI, requestMethod, methodAttributes, operation, methodParameter, parameterInfo, parameter); } diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/extractor/MethodParameterPojoExtractor.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/extractor/MethodParameterPojoExtractor.java index ff8e3f0c1..c040264c2 100644 --- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/extractor/MethodParameterPojoExtractor.java +++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/extractor/MethodParameterPojoExtractor.java @@ -88,6 +88,7 @@ public class MethodParameterPojoExtractor { SIMPLE_TYPES.add(Iterable.class); SIMPLE_TYPES.add(Duration.class); SIMPLE_TYPES.add(LocalTime.class); + SIMPLE_TYPES.add(Class.class); SIMPLE_TYPE_PREDICATES.add(Class::isPrimitive); SIMPLE_TYPE_PREDICATES.add(Class::isEnum); diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/models/MethodAttributes.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/models/MethodAttributes.java index bb2e4b69a..433df153a 100644 --- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/models/MethodAttributes.java +++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/models/MethodAttributes.java @@ -25,18 +25,22 @@ package org.springdoc.core.models; import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.HashSet; import java.util.LinkedHashMap; import java.util.Locale; import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; import com.fasterxml.jackson.annotation.JsonView; import io.swagger.v3.oas.models.responses.ApiResponse; import io.swagger.v3.oas.models.responses.ApiResponses; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; +import org.jetbrains.annotations.Nullable; import org.springframework.core.annotation.AnnotatedElementUtils; -import org.springframework.util.CollectionUtils; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; @@ -275,28 +279,38 @@ else if (reqMappingClass != null) { * @param headers the headers */ private void fillMethods(String[] produces, String[] consumes, String[] headers) { - if (ArrayUtils.isEmpty(methodProduces)) { - if (ArrayUtils.isNotEmpty(produces)) - methodProduces = produces; - else if (ArrayUtils.isNotEmpty(classProduces)) - methodProduces = classProduces; - else - methodProduces = new String[] { defaultProducesMediaType }; - } - - if (ArrayUtils.isEmpty(methodConsumes)) { - if (ArrayUtils.isNotEmpty(consumes)) - methodConsumes = consumes; - else if (ArrayUtils.isNotEmpty(classConsumes)) - methodConsumes = classConsumes; - else - methodConsumes = new String[] { defaultConsumesMediaType }; - } - - if (CollectionUtils.isEmpty(this.headers)) - setHeaders(headers); + if (ArrayUtils.isNotEmpty(produces)) { + methodProduces = mergeArrays(methodProduces, produces); + } else if (ArrayUtils.isNotEmpty(classProduces)) { + methodProduces = mergeArrays(methodProduces, classProduces); + } else if (ArrayUtils.isEmpty(methodProduces)) { + methodProduces = new String[] {defaultProducesMediaType}; + } + + if (ArrayUtils.isNotEmpty(consumes)) { + methodConsumes = mergeArrays(methodConsumes, consumes); + } else if (ArrayUtils.isNotEmpty(classConsumes)) { + methodConsumes = mergeArrays(methodConsumes, classConsumes); + } else if (ArrayUtils.isEmpty(methodConsumes)) { + methodConsumes = new String[] {defaultConsumesMediaType}; + } + + setHeaders(headers); } + /** + * Merge string arrays into one array with unique values + * + * @param array1 the array1 + * @param array2 the array2 + * @return the string [ ] + */ + private String[] mergeArrays(@Nullable String[] array1, String[] array2) { + Set uniqueValues = array1 == null ? new HashSet<>() : Arrays.stream(array1).collect(Collectors.toSet()); + uniqueValues.addAll(Arrays.asList(array2)); + return uniqueValues.toArray(new String[0]); + } + /** * Is method overloaded boolean. * diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/properties/SwaggerUiConfigParameters.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/properties/SwaggerUiConfigParameters.java index 8fab65098..c4b5a881f 100644 --- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/properties/SwaggerUiConfigParameters.java +++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/properties/SwaggerUiConfigParameters.java @@ -304,9 +304,9 @@ public Map getConfigParameters() { private void put(String urls, Set swaggerUrls, Map params) { Comparator swaggerUrlComparator; if (groupsOrder.isAscending()) - swaggerUrlComparator = Comparator.comparing(SwaggerUrl::getName); + swaggerUrlComparator = Comparator.comparing(SwaggerUrl::getDisplayName); else - swaggerUrlComparator = (h1, h2) -> h2.getName().compareTo(h1.getName()); + swaggerUrlComparator = (h1, h2) -> h2.getDisplayName().compareTo(h1.getDisplayName()); swaggerUrls = swaggerUrls.stream().sorted(swaggerUrlComparator).filter(elt -> StringUtils.isNotEmpty(elt.getUrl())).collect(Collectors.toCollection(LinkedHashSet::new)); if (!CollectionUtils.isEmpty(swaggerUrls)) { diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/service/AbstractRequestService.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/service/AbstractRequestService.java index 7fe94dad5..6a53cc4e3 100644 --- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/service/AbstractRequestService.java +++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/service/AbstractRequestService.java @@ -25,6 +25,7 @@ package org.springdoc.core.service; import java.lang.annotation.Annotation; +import java.lang.reflect.Field; import java.lang.reflect.Method; import java.math.BigDecimal; import java.util.ArrayList; @@ -88,12 +89,14 @@ import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.context.request.WebRequest; import org.springframework.web.method.HandlerMethod; +import org.springframework.web.multipart.MultipartFile; import org.springframework.web.util.UriComponentsBuilder; import static org.springdoc.core.converters.SchemaPropertyDeprecatingConverter.containsDeprecatedAnnotation; import static org.springdoc.core.service.GenericParameterService.isFile; import static org.springdoc.core.utils.Constants.OPENAPI_ARRAY_TYPE; import static org.springdoc.core.utils.Constants.OPENAPI_STRING_TYPE; +import static org.springframework.http.MediaType.MULTIPART_FORM_DATA_VALUE; /** * The type Abstract request builder. @@ -323,7 +326,7 @@ public Operation build(HandlerMethod handlerMethod, RequestMethod requestMethod, } if (!isParamToIgnore(methodParameter)) { - parameter = buildParams(parameterInfo, components, requestMethod, methodAttributes.getJsonViewAnnotation(), openAPI.getOpenapi()); + parameter = buildParams(parameterInfo, components, requestMethod, methodAttributes, openAPI.getOpenapi()); // Merge with the operation parameters parameter = GenericParameterService.mergeParameter(operationParameters, parameter); List parameterAnnotations = Arrays.asList(methodParameter.getParameterAnnotations()); @@ -353,7 +356,7 @@ else if (!RequestMethod.GET.equals(requestMethod) || OpenApiVersion.OPENAPI_3_1. // support form-data if (defaultSupportFormData && requestBody != null && requestBody.getContent() != null - && requestBody.getContent().containsKey(org.springframework.http.MediaType.MULTIPART_FORM_DATA_VALUE)) { + && requestBody.getContent().containsKey(MULTIPART_FORM_DATA_VALUE)) { Iterator> it = map.entrySet().iterator(); while (it.hasNext()) { Entry entry = it.next(); @@ -496,28 +499,28 @@ public boolean isValidParameter(Parameter parameter) { /** * Build params parameter. * - * @param parameterInfo the parameter info - * @param components the components - * @param requestMethod the request method - * @param jsonView the json view - * @param openApiVersion the open api version + * @param parameterInfo the parameter info + * @param components the components + * @param requestMethod the request method + * @param methodAttributes the method attributes + * @param openApiVersion the open api version * @return the parameter */ public Parameter buildParams(ParameterInfo parameterInfo, Components components, - RequestMethod requestMethod, JsonView jsonView, String openApiVersion) { + RequestMethod requestMethod, MethodAttributes methodAttributes, String openApiVersion) { MethodParameter methodParameter = parameterInfo.getMethodParameter(); if (parameterInfo.getParamType() != null) { if (!ValueConstants.DEFAULT_NONE.equals(parameterInfo.getDefaultValue())) parameterInfo.setRequired(false); else parameterInfo.setDefaultValue(null); - return this.buildParam(parameterInfo, components, jsonView); + return this.buildParam(parameterInfo, components, methodAttributes.getJsonViewAnnotation()); } // By default - if (!isRequestBodyParam(requestMethod, parameterInfo, openApiVersion)) { + if (!isRequestBodyParam(requestMethod, parameterInfo, openApiVersion, methodAttributes)) { parameterInfo.setRequired(!((DelegatingMethodParameter) methodParameter).isNotRequired() && !methodParameter.isOptional()); parameterInfo.setDefaultValue(null); - return this.buildParam(parameterInfo, components, jsonView); + return this.buildParam(parameterInfo, components, methodAttributes.getJsonViewAnnotation()); } return null; } @@ -631,7 +634,7 @@ public RequestBodyService getRequestBodyBuilder() { public boolean isDefaultFlatParamObject() { return defaultFlatParamObject; } - + /** * Calculate size. * @@ -722,12 +725,13 @@ private void applyValidationsToSchema(Map annos, Schema s /** * Is RequestBody param boolean. * - * @param requestMethod the request method - * @param parameterInfo the parameter info - * @param openApiVersion the open api version + * @param requestMethod the request method + * @param parameterInfo the parameter info + * @param openApiVersion the open api version + * @param methodAttributes the method attributes * @return the boolean */ - private boolean isRequestBodyParam(RequestMethod requestMethod, ParameterInfo parameterInfo, String openApiVersion) { + private boolean isRequestBodyParam(RequestMethod requestMethod, ParameterInfo parameterInfo, String openApiVersion, MethodAttributes methodAttributes) { MethodParameter methodParameter = parameterInfo.getMethodParameter(); DelegatingMethodParameter delegatingMethodParameter = (DelegatingMethodParameter) methodParameter; boolean isBodyAllowed = !RequestMethod.GET.equals(requestMethod) || OpenApiVersion.OPENAPI_3_1.getVersion().equals(openApiVersion); @@ -739,8 +743,7 @@ private boolean isRequestBodyParam(RequestMethod requestMethod, ParameterInfo pa || AnnotatedElementUtils.findMergedAnnotation(Objects.requireNonNull(methodParameter.getMethod()), io.swagger.v3.oas.annotations.parameters.RequestBody.class) != null) || checkOperationRequestBody(methodParameter) || checkFile(methodParameter) - - ); + || Arrays.asList(methodAttributes.getMethodConsumes()).contains(MULTIPART_FORM_DATA_VALUE)); } /** @@ -767,7 +770,7 @@ else if (methodParameter.getParameterAnnotation(org.springframework.web.bind.ann private boolean checkOperationRequestBody(MethodParameter methodParameter) { if (AnnotatedElementUtils.findMergedAnnotation(Objects.requireNonNull(methodParameter.getMethod()), io.swagger.v3.oas.annotations.Operation.class) != null) { io.swagger.v3.oas.annotations.Operation operation = AnnotatedElementUtils.findMergedAnnotation(Objects.requireNonNull(methodParameter.getMethod()), io.swagger.v3.oas.annotations.Operation.class); - if(operation!=null){ + if (operation != null) { io.swagger.v3.oas.annotations.parameters.RequestBody requestBody = operation.requestBody(); if (StringUtils.isNotBlank(requestBody.description())) return true; diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/service/GenericResponseService.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/service/GenericResponseService.java index c54898caf..dbe542e00 100644 --- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/service/GenericResponseService.java +++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/service/GenericResponseService.java @@ -66,6 +66,7 @@ import org.springdoc.core.parsers.ReturnTypeParser; import org.springdoc.core.properties.SpringDocConfigProperties; import org.springdoc.core.providers.JavadocProvider; +import org.springdoc.core.providers.ObjectMapperProvider; import org.springdoc.core.utils.PropertyResolverUtils; import org.springdoc.core.utils.SpringDocAnnotationsUtils; @@ -397,7 +398,7 @@ private Map computeResponseFromDoc(Components components, M apiResponse.extensions(extensions); } } - AnnotationsUtils.getHeaders(apiResponseAnnotations.headers(), methodAttributes.getJsonViewAnnotation(), openapi31) + AnnotationsUtils.getHeaders(apiResponseAnnotations.headers(), components, methodAttributes.getJsonViewAnnotation(), openapi31) .ifPresent(apiResponse::headers); apiResponsesOp.addApiResponse(httpCode, apiResponse); } @@ -736,7 +737,7 @@ private Map getGenericMapResponse(HandlerMethod handlerMeth LinkedHashMap genericApiResponsesClone; try { - ObjectMapper objectMapper = new ObjectMapper(); + ObjectMapper objectMapper = ObjectMapperProvider.createJson(springDocConfigProperties); genericApiResponsesClone = objectMapper.readValue(objectMapper.writeValueAsString(genericApiResponseMap), ApiResponses.class); return genericApiResponsesClone; } diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/service/OpenAPIService.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/service/OpenAPIService.java index 4a3fe8ad8..bb69b1a6c 100644 --- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/service/OpenAPIService.java +++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/service/OpenAPIService.java @@ -81,6 +81,7 @@ import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.core.type.filter.AnnotationTypeFilter; +import org.springframework.http.HttpRequest; import org.springframework.stereotype.Controller; import org.springframework.util.CollectionUtils; import org.springframework.web.bind.annotation.ControllerAdvice; @@ -490,12 +491,12 @@ public Schema resolveProperties(Schema schema, Locale locale) { * * @param serverBaseUrl the server base url */ - public void setServerBaseUrl(String serverBaseUrl) { + public void setServerBaseUrl(String serverBaseUrl, HttpRequest httpRequest) { String customServerBaseUrl = serverBaseUrl; if (serverBaseUrlCustomizers.isPresent()) { for (ServerBaseUrlCustomizer customizer : serverBaseUrlCustomizers.get()) { - customServerBaseUrl = customizer.customize(customServerBaseUrl); + customServerBaseUrl = customizer.customize(customServerBaseUrl, httpRequest); } } diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/service/OperationService.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/service/OperationService.java index e673e6b1c..5a539b6b8 100644 --- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/service/OperationService.java +++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/service/OperationService.java @@ -3,7 +3,7 @@ * * * * * * * * * - * * * * * Copyright 2019-2022 the original author or authors. + * * * * * 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. @@ -412,7 +412,7 @@ private Optional getApiResponses( buildResponseContent(methodAttributes, components, classProduces, methodProduces, apiResponsesOp, response, apiResponseObject); - AnnotationsUtils.getHeaders(response.headers(), null, propertyResolverUtils.isOpenapi31()).ifPresent(apiResponseObject::headers); + AnnotationsUtils.getHeaders(response.headers(), components, null, propertyResolverUtils.isOpenapi31()).ifPresent(apiResponseObject::headers); // Make schema as string if empty calculateHeader(apiResponseObject); if (isResponseObject(apiResponseObject)) { diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/utils/PropertyResolverUtils.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/utils/PropertyResolverUtils.java index e70e9666e..dfa282e82 100644 --- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/utils/PropertyResolverUtils.java +++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/utils/PropertyResolverUtils.java @@ -143,7 +143,7 @@ private int resolveMinIndent(String[] lines) { private int countLeadingSpaces(String line) { int count = 0; for (char ch : line.toCharArray()) { - if (ch != ' ') break; + if (ch != ' ' && ch != '\t') break; count++; } return count; @@ -222,4 +222,4 @@ public Map resolveExtensions(Locale locale, Map else return extensions; } -} \ No newline at end of file +} diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/utils/SpringDocAnnotationsUtils.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/utils/SpringDocAnnotationsUtils.java index c532a679a..0c3750eea 100644 --- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/utils/SpringDocAnnotationsUtils.java +++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/utils/SpringDocAnnotationsUtils.java @@ -31,9 +31,11 @@ import java.util.Arrays; import java.util.Collections; import java.util.LinkedHashMap; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.Set; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonView; @@ -65,6 +67,7 @@ /** * The type Spring doc annotations utils. + * * @author bnasslahsen */ @SuppressWarnings({ "rawtypes" }) @@ -142,10 +145,21 @@ public static Schema extractSchema(Components components, Type returnType, JsonV for (Map.Entry entry : schemaMap.entrySet()) { // If we've seen this schema before but find later it should be polymorphic, // replace the existing schema with this richer version. + Schema existingSchema = componentSchemas.get(entry.getKey()); if (!componentSchemas.containsKey(entry.getKey()) || - (!entry.getValue().getClass().equals(componentSchemas.get(entry.getKey()).getClass()) && entry.getValue().getAllOf() != null)) { + (!entry.getValue().getClass().equals(existingSchema.getClass()) && entry.getValue().getAllOf() != null)) { componentSchemas.put(entry.getKey(), entry.getValue()); } + else if (componentSchemas.containsKey(entry.getKey()) && schemaMap.containsKey(entry.getKey())) { + // Check to merge polymorphic types + Set existingAllOf = new LinkedHashSet<>(); + if(existingSchema.getAllOf() != null) + existingAllOf.addAll(existingSchema.getAllOf()); + if (schemaMap.get(entry.getKey()).getAllOf() != null){ + existingAllOf.addAll(schemaMap.get(entry.getKey()).getAllOf()); + existingSchema.setAllOf(new ArrayList<>(existingAllOf)); + } + } } components.setSchemas(componentSchemas); } @@ -207,8 +221,8 @@ public static Optional getContent(io.swagger.v3.oas.annotations.media.C * Merge schema. * * @param existingContent the existing content - * @param schemaN the schema n - * @param mediaTypeStr the media type str + * @param schemaN the schema n + * @param mediaTypeStr the media type str */ public static void mergeSchema(Content existingContent, Schema schemaN, String mediaTypeStr) { if (existingContent.containsKey(mediaTypeStr)) { @@ -322,7 +336,7 @@ private static void addExtension(io.swagger.v3.oas.annotations.media.Content ann * Sets examples. * * @param mediaType the media type - * @param examples the examples + * @param examples the examples */ private static void setExamples(MediaType mediaType, ExampleObject[] examples) { if (examples.length == 1 && StringUtils.isBlank(examples[0].name())) { @@ -436,7 +450,7 @@ private static boolean isArray(io.swagger.v3.oas.annotations.media.Content annot * Resolve default value object. * * @param defaultValueStr the default value str - * @param objectMapper the object mapper + * @param objectMapper the object mapper * @return the object */ public static Object resolveDefaultValue(String defaultValueStr, ObjectMapper objectMapper) { diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/utils/SpringDocUtils.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/utils/SpringDocUtils.java index 09175e6e4..f1023e3c6 100644 --- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/utils/SpringDocUtils.java +++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/utils/SpringDocUtils.java @@ -32,6 +32,7 @@ import org.springdoc.api.AbstractOpenApiResource; import org.springdoc.core.converters.AdditionalModelsConverter; import org.springdoc.core.converters.ConverterUtils; +import org.springdoc.core.converters.PolymorphicModelConverter; import org.springdoc.core.converters.SchemaPropertyDeprecatingConverter; import org.springdoc.core.extractor.MethodParameterPojoExtractor; import org.springdoc.core.service.AbstractRequestService; @@ -388,5 +389,16 @@ public static boolean isValidPath(String path) { return true; return false; } + + /** + * Add parent type spring doc utils. + * + * @param parentTypes the parent types + * @return the spring doc utils + */ + public SpringDocUtils addParentType(String ...parentTypes) { + PolymorphicModelConverter.addParentType(parentTypes); + return this; + } } diff --git a/springdoc-openapi-starter-common/src/test/java/org/springdoc/api/AbstractOpenApiResourceTest.java b/springdoc-openapi-starter-common/src/test/java/org/springdoc/api/AbstractOpenApiResourceTest.java index fdf75d7b0..10ec555b0 100644 --- a/springdoc-openapi-starter-common/src/test/java/org/springdoc/api/AbstractOpenApiResourceTest.java +++ b/springdoc-openapi-starter-common/src/test/java/org/springdoc/api/AbstractOpenApiResourceTest.java @@ -60,6 +60,7 @@ import org.springframework.beans.factory.ObjectFactory; import org.springframework.context.ApplicationContext; +import org.springframework.mock.http.client.MockClientHttpRequest; import org.springframework.test.util.ReflectionTestUtils; import org.springframework.web.bind.annotation.RequestMethod; @@ -190,7 +191,7 @@ void preLoadingModeShouldNotOverwriteServers() throws InterruptedException { doCallRealMethod().when(openAPIService).updateServers(any()); when(openAPIService.getCachedOpenAPI(any())).thenCallRealMethod(); doAnswer(new CallsRealMethods()).when(openAPIService).setServersPresent(true); - doAnswer(new CallsRealMethods()).when(openAPIService).setServerBaseUrl(any()); + doAnswer(new CallsRealMethods()).when(openAPIService).setServerBaseUrl(any(), any()); doAnswer(new CallsRealMethods()).when(openAPIService).setCachedOpenAPI(any(), any()); String customUrl = "https://custom.com"; @@ -212,7 +213,7 @@ properties, springDocProviders, new SpringDocCustomizers(Optional.of(singletonLi Thread.sleep(1_000); // emulate generating base url - openAPIService.setServerBaseUrl(generatedUrl); + openAPIService.setServerBaseUrl(generatedUrl, new MockClientHttpRequest()); openAPIService.updateServers(openAPI); Locale locale = Locale.US; OpenAPI after = resource.getOpenApi(locale); @@ -224,7 +225,7 @@ properties, springDocProviders, new SpringDocCustomizers(Optional.of(singletonLi void serverBaseUrlCustomisersTest() throws InterruptedException { doCallRealMethod().when(openAPIService).updateServers(any()); when(openAPIService.getCachedOpenAPI(any())).thenCallRealMethod(); - doAnswer(new CallsRealMethods()).when(openAPIService).setServerBaseUrl(any()); + doAnswer(new CallsRealMethods()).when(openAPIService).setServerBaseUrl(any(), any()); doAnswer(new CallsRealMethods()).when(openAPIService).setCachedOpenAPI(any(), any()); SpringDocConfigProperties properties = new SpringDocConfigProperties(); @@ -247,37 +248,37 @@ springDocProviders, new SpringDocCustomizers(Optional.empty(),Optional.empty(),O // Test that setting generated URL works fine with no customizers present String generatedUrl = "https://generated-url.com/context-path"; - openAPIService.setServerBaseUrl(generatedUrl); + openAPIService.setServerBaseUrl(generatedUrl, new MockClientHttpRequest()); openAPIService.updateServers(openAPI); OpenAPI after = resource.getOpenApi(locale); assertThat(after.getServers().get(0).getUrl(), is(generatedUrl)); // Test that adding a serverBaseUrlCustomizer has the desired effect - ServerBaseUrlCustomizer serverBaseUrlCustomizer = serverBaseUrl -> serverBaseUrl.replace("/context-path", ""); + ServerBaseUrlCustomizer serverBaseUrlCustomizer = (serverBaseUrl, request) -> serverBaseUrl.replace("/context-path", ""); List serverBaseUrlCustomizerList = new ArrayList<>(); serverBaseUrlCustomizerList.add(serverBaseUrlCustomizer); ReflectionTestUtils.setField(openAPIService, "serverBaseUrlCustomizers", Optional.of(serverBaseUrlCustomizerList)); - openAPIService.setServerBaseUrl(generatedUrl); + openAPIService.setServerBaseUrl(generatedUrl, new MockClientHttpRequest()); openAPIService.updateServers(openAPI); after = resource.getOpenApi(locale); assertThat(after.getServers().get(0).getUrl(), is("https://generated-url.com")); // Test that serverBaseUrlCustomisers are performed in order generatedUrl = "https://generated-url.com/context-path/second-path"; - ServerBaseUrlCustomizer serverBaseUrlCustomiser2 = serverBaseUrl -> serverBaseUrl.replace("/context-path/second-path", ""); + ServerBaseUrlCustomizer serverBaseUrlCustomiser2 = (serverBaseUrl, request) -> serverBaseUrl.replace("/context-path/second-path", ""); serverBaseUrlCustomizerList.add(serverBaseUrlCustomiser2); - openAPIService.setServerBaseUrl(generatedUrl); + openAPIService.setServerBaseUrl(generatedUrl, new MockClientHttpRequest()); openAPIService.updateServers(openAPI); after = resource.getOpenApi(locale); assertThat(after.getServers().get(0).getUrl(), is("https://generated-url.com/second-path")); // Test that all serverBaseUrlCustomisers in the List are performed - ServerBaseUrlCustomizer serverBaseUrlCustomiser3 = serverBaseUrl -> serverBaseUrl.replace("/second-path", ""); + ServerBaseUrlCustomizer serverBaseUrlCustomiser3 = (serverBaseUrl, request) -> serverBaseUrl.replace("/second-path", ""); serverBaseUrlCustomizerList.add(serverBaseUrlCustomiser3); - openAPIService.setServerBaseUrl(generatedUrl); + openAPIService.setServerBaseUrl(generatedUrl, new MockClientHttpRequest()); openAPIService.updateServers(openAPI); after = resource.getOpenApi(locale); assertThat(after.getServers().get(0).getUrl(), is("https://generated-url.com")); diff --git a/springdoc-openapi-starter-webflux-api/pom.xml b/springdoc-openapi-starter-webflux-api/pom.xml index b802dee83..550254788 100644 --- a/springdoc-openapi-starter-webflux-api/pom.xml +++ b/springdoc-openapi-starter-webflux-api/pom.xml @@ -3,7 +3,7 @@ org.springdoc springdoc-openapi - 2.5.0 + 2.6.0 springdoc-openapi-starter-webflux-api diff --git a/springdoc-openapi-starter-webflux-api/src/main/java/org/springdoc/webflux/api/OpenApiActuatorResource.java b/springdoc-openapi-starter-webflux-api/src/main/java/org/springdoc/webflux/api/OpenApiActuatorResource.java index 75ad34354..3bee371a5 100644 --- a/springdoc-openapi-starter-webflux-api/src/main/java/org/springdoc/webflux/api/OpenApiActuatorResource.java +++ b/springdoc-openapi-starter-webflux-api/src/main/java/org/springdoc/webflux/api/OpenApiActuatorResource.java @@ -131,7 +131,7 @@ public Mono openapiYaml(ServerHttpRequest serverHttpRequest, Locale loca protected void calculateServerUrl(ServerHttpRequest serverHttpRequest, String apiDocsUrl, Locale locale) { super.initOpenAPIBuilder(locale); URI uri = getActuatorURI(serverHttpRequest.getURI().getScheme(), serverHttpRequest.getURI().getHost()); - openAPIService.setServerBaseUrl(uri.toString()); + openAPIService.setServerBaseUrl(uri.toString(), serverHttpRequest); } @Override diff --git a/springdoc-openapi-starter-webflux-api/src/main/java/org/springdoc/webflux/api/OpenApiResource.java b/springdoc-openapi-starter-webflux-api/src/main/java/org/springdoc/webflux/api/OpenApiResource.java index acbefce90..0b29503d1 100644 --- a/springdoc-openapi-starter-webflux-api/src/main/java/org/springdoc/webflux/api/OpenApiResource.java +++ b/springdoc-openapi-starter-webflux-api/src/main/java/org/springdoc/webflux/api/OpenApiResource.java @@ -229,7 +229,7 @@ protected void getWebFluxRouterFunctionPaths(Locale locale, OpenAPI openAPI) { protected void calculateServerUrl(ServerHttpRequest serverHttpRequest, String apiDocsUrl, Locale locale) { initOpenAPIBuilder(locale); String serverUrl = getServerUrl(serverHttpRequest, apiDocsUrl); - openAPIService.setServerBaseUrl(serverUrl); + openAPIService.setServerBaseUrl(serverUrl, serverHttpRequest); } /** diff --git a/springdoc-openapi-starter-webflux-api/src/test/resources/logback-test.xml b/springdoc-openapi-starter-webflux-api/src/test/resources/logback-test.xml index 24cd4646e..3fd46cfab 100644 --- a/springdoc-openapi-starter-webflux-api/src/test/resources/logback-test.xml +++ b/springdoc-openapi-starter-webflux-api/src/test/resources/logback-test.xml @@ -1,6 +1,5 @@ - \ No newline at end of file diff --git a/springdoc-openapi-starter-webflux-ui/pom.xml b/springdoc-openapi-starter-webflux-ui/pom.xml index 0b4aa79fb..0f5a75883 100644 --- a/springdoc-openapi-starter-webflux-ui/pom.xml +++ b/springdoc-openapi-starter-webflux-ui/pom.xml @@ -3,7 +3,7 @@ org.springdoc springdoc-openapi - 2.5.0 + 2.6.0 springdoc-openapi-starter-webflux-ui diff --git a/springdoc-openapi-starter-webmvc-api/pom.xml b/springdoc-openapi-starter-webmvc-api/pom.xml index 9b8ac4d47..0fafe6f0b 100644 --- a/springdoc-openapi-starter-webmvc-api/pom.xml +++ b/springdoc-openapi-starter-webmvc-api/pom.xml @@ -3,7 +3,7 @@ org.springdoc springdoc-openapi - 2.5.0 + 2.6.0 springdoc-openapi-starter-webmvc-api diff --git a/springdoc-openapi-starter-webmvc-api/src/main/java/org/springdoc/webmvc/api/OpenApiResource.java b/springdoc-openapi-starter-webmvc-api/src/main/java/org/springdoc/webmvc/api/OpenApiResource.java index 4b5fd61a7..288496a80 100644 --- a/springdoc-openapi-starter-webmvc-api/src/main/java/org/springdoc/webmvc/api/OpenApiResource.java +++ b/springdoc-openapi-starter-webmvc-api/src/main/java/org/springdoc/webmvc/api/OpenApiResource.java @@ -55,6 +55,7 @@ import org.springframework.aop.support.AopUtils; import org.springframework.beans.factory.ObjectFactory; +import org.springframework.http.server.ServletServerHttpRequest; import org.springframework.util.CollectionUtils; import org.springframework.util.MimeType; import org.springframework.web.bind.annotation.RequestMethod; @@ -244,7 +245,8 @@ private Comparator byReversedRequestMappingInfos() { protected void calculateServerUrl(HttpServletRequest request, String apiDocsUrl, Locale locale) { super.initOpenAPIBuilder(locale); String calculatedUrl = getServerUrl(request, apiDocsUrl); - openAPIService.setServerBaseUrl(calculatedUrl); + ServletServerHttpRequest serverRequest = request != null ? new ServletServerHttpRequest(request) : null; + openAPIService.setServerBaseUrl(calculatedUrl, serverRequest); } /** diff --git a/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app177/AnnotatedController.java b/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app177/AnnotatedController.java index fca639d4a..74cc1c134 100644 --- a/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app177/AnnotatedController.java +++ b/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app177/AnnotatedController.java @@ -26,6 +26,8 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.Arrays; +import java.util.List; import org.springdoc.core.filters.OpenApiMethodFilter; import org.springdoc.core.models.GroupedOpenApi; @@ -65,27 +67,23 @@ public String notAnnotatedPost() { } @Bean - public GroupedOpenApi group1OpenApi() { - return GroupedOpenApi.builder() + public List apis() { + GroupedOpenApi group1OpenApi = GroupedOpenApi.builder() .group("annotatedGroup1") .addOpenApiMethodFilter(method -> method.isAnnotationPresent(Group1.class)) .build(); - } - @Bean - public GroupedOpenApi group2OpenApi() { - return GroupedOpenApi.builder() + GroupedOpenApi group2OpenApi = GroupedOpenApi.builder() .group("annotatedGroup2") .addOpenApiMethodFilter(method -> method.isAnnotationPresent(Group2.class)) .build(); - } - @Bean - public GroupedOpenApi group3OpenApi() { - return GroupedOpenApi.builder() + GroupedOpenApi group3OpenApi = GroupedOpenApi.builder() .group("annotatedCombinedGroup") .addOpenApiMethodFilter(method -> method.isAnnotationPresent(Group1.class) || method.isAnnotationPresent(Group2.class)) .build(); + + return Arrays.asList(group1OpenApi, group2OpenApi, group3OpenApi); } @Bean diff --git a/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app185/Pet.java b/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app185/Pet.java index f02de23cc..93dc30461 100644 --- a/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app185/Pet.java +++ b/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app185/Pet.java @@ -26,12 +26,14 @@ import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonTypeInfo; +import io.swagger.v3.oas.annotations.media.Schema; @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") @JsonSubTypes({ @JsonSubTypes.Type(Dog.class), @JsonSubTypes.Type(Cat.class) }) +@Schema(description = "This is a Pet") public class Pet { public final String name; diff --git a/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app217/SpringDocApp217Test.java b/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app217/SpringDocApp217Test.java index fd4bb77ab..820e5ce8e 100644 --- a/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app217/SpringDocApp217Test.java +++ b/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app217/SpringDocApp217Test.java @@ -20,13 +20,11 @@ import org.junit.jupiter.api.Test; import org.springdoc.core.customizers.SpecPropertiesCustomizer; -import org.springdoc.core.models.GroupedOpenApi; import org.springdoc.core.utils.Constants; import test.org.springdoc.api.v30.AbstractSpringDocV30Test; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.context.annotation.Bean; import org.springframework.test.context.ActiveProfiles; import static org.hamcrest.Matchers.is; diff --git a/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app218/HelloController.java b/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app218/HelloController.java new file mode 100644 index 000000000..e753b54f0 --- /dev/null +++ b/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app218/HelloController.java @@ -0,0 +1,64 @@ +/* + * + * * + * * * + * * * * 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.v30.app218; + + +import java.net.URI; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.headers.Header; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; + +import org.springframework.http.HttpHeaders; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + + +@RestController +@RequestMapping("/") +public class HelloController { + + @Operation( + summary = "Summary", + description = "This is description.", + tags = {"Sample"}, + responses = { + @ApiResponse( + responseCode = "201", + description = "201 (Created)", + headers = + @Header( + name = HttpHeaders.LOCATION, + description = "Sample endpoint", + schema = @Schema(implementation = URI.class) + ) + ) + } + ) + @GetMapping + public String get() { + return "Hello World!"; + } +} diff --git a/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app218/SpringDocApp218Test.java b/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app218/SpringDocApp218Test.java new file mode 100644 index 000000000..65013aff1 --- /dev/null +++ b/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app218/SpringDocApp218Test.java @@ -0,0 +1,37 @@ +/* + * + * * 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.v30.app218; + +import org.springdoc.core.customizers.SpecPropertiesCustomizer; +import test.org.springdoc.api.v30.AbstractSpringDocV30Test; + +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.test.context.SpringBootTest; + +/** + *

+ * A test for {@link SpecPropertiesCustomizer} + */ +@SpringBootTest +public class SpringDocApp218Test extends AbstractSpringDocV30Test { + + @SpringBootApplication + static class SpringDocTestApp {} + +} diff --git a/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app219/HelloController.java b/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app219/HelloController.java new file mode 100644 index 000000000..0b2b37d5c --- /dev/null +++ b/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app219/HelloController.java @@ -0,0 +1,42 @@ +/* + * + * * + * * * + * * * * + * * * * * Copyright 2019-2022 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.app219; + +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping(value = "/api", produces = {"application/xml"}, consumes = {"application/json"}) +public class HelloController { + + @RequestMapping(value = "/testpost", method = RequestMethod.POST, produces = {"application/json"}, + consumes = {"application/json;charset=UTF-8", "application/json; charset=UTF-8"}) + public ResponseEntity testpost(@RequestBody TestObject dto) { + return ResponseEntity.ok(dto); + } +} diff --git a/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app219/SpringDocApp219Test.java b/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app219/SpringDocApp219Test.java new file mode 100644 index 000000000..a9afcaf7b --- /dev/null +++ b/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app219/SpringDocApp219Test.java @@ -0,0 +1,37 @@ +/* + * + * * + * * * + * * * * + * * * * * Copyright 2019-2022 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.app219; + +import test.org.springdoc.api.v30.AbstractSpringDocV30Test; + +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.test.context.ActiveProfiles; + +@ActiveProfiles("219") +public class SpringDocApp219Test extends AbstractSpringDocV30Test { + + @SpringBootApplication + static class SpringDocTestApp {} +} diff --git a/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app219/TestObject.java b/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app219/TestObject.java new file mode 100644 index 000000000..ca336cca3 --- /dev/null +++ b/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app219/TestObject.java @@ -0,0 +1,49 @@ +/* + * + * * + * * * + * * * * + * * * * * Copyright 2019-2022 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.app219; + +import java.time.LocalDateTime; + +public class TestObject { + public String stringValue; + + public LocalDateTime localDateTime; + + public String getStringValue() { + return stringValue; + } + + public void setStringValue(String stringValue) { + this.stringValue = stringValue; + } + + public LocalDateTime getLocalDateTime() { + return localDateTime; + } + + public void setLocalDateTime(LocalDateTime localDateTime) { + this.localDateTime = localDateTime; + } +} diff --git a/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app220/HelloController.java b/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app220/HelloController.java new file mode 100644 index 000000000..f5b17bc2b --- /dev/null +++ b/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app220/HelloController.java @@ -0,0 +1,63 @@ +package test.org.springdoc.api.v30.app220; + + +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonSubTypes.Type; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import io.swagger.v3.oas.annotations.media.Schema; + +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class HelloController { + + @PostMapping("/parent") + public void parentEndpoint(@RequestBody Superclass parent) { + + } + +} + +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "@type") +@JsonSubTypes({ + @Type(value = IntermediateClass.class, name = IntermediateClass.SCHEMA_NAME), +}) +sealed class Superclass permits IntermediateClass { + + public Superclass() {} +} + +@Schema(name = IntermediateClass.SCHEMA_NAME) +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "@type") +@JsonSubTypes({ + @Type(value = FirstChildClass.class, name = FirstChildClass.SCHEMA_NAME), + @Type(value = SecondChildClass.class, name = SecondChildClass.SCHEMA_NAME) +}) +sealed class IntermediateClass extends Superclass permits FirstChildClass, SecondChildClass { + + public static final String SCHEMA_NAME = "IntermediateClass"; +} + +@Schema(name = FirstChildClass.SCHEMA_NAME) +final class FirstChildClass extends IntermediateClass { + + public static final String SCHEMA_NAME = "Image"; +} + +@Schema(name = SecondChildClass.SCHEMA_NAME) +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "@type") +@JsonSubTypes({ + @Type(value = ThirdChildClass.class, name = ThirdChildClass.SCHEMA_NAME) +}) +sealed class SecondChildClass extends IntermediateClass { + + public static final String SCHEMA_NAME = "Mail"; +} + +@Schema(name = ThirdChildClass.SCHEMA_NAME) +final class ThirdChildClass extends SecondChildClass { + + public static final String SCHEMA_NAME = "Home"; +} \ No newline at end of file diff --git a/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app220/SpringDocApp220Test.java b/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app220/SpringDocApp220Test.java new file mode 100644 index 000000000..0e5f57595 --- /dev/null +++ b/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app220/SpringDocApp220Test.java @@ -0,0 +1,35 @@ +/* + * + * * + * * * + * * * * + * * * * * Copyright 2019-2022 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.app220; + +import test.org.springdoc.api.v30.AbstractSpringDocV30Test; + +import org.springframework.boot.autoconfigure.SpringBootApplication; + +public class SpringDocApp220Test extends AbstractSpringDocV30Test { + + @SpringBootApplication + static class SpringDocTestApp {} +} diff --git a/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app221/HomeApi.java b/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app221/HomeApi.java new file mode 100644 index 000000000..40506c3da --- /dev/null +++ b/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app221/HomeApi.java @@ -0,0 +1,23 @@ +package test.org.springdoc.api.v30.app221; + +import java.io.IOException; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import test.org.springdoc.api.v30.app221.HomeController.HelloDto; +import test.org.springdoc.api.v30.app221.HomeController.HelloUploadDto; + +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; + +import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; +import static org.springframework.http.MediaType.MULTIPART_FORM_DATA_VALUE; + +@Tag(name = "Hello World Api", description = "This is a test api") +@RequestMapping("api/hello") +public interface HomeApi { + + @Operation(summary = "Upload new content", description = "Upload test content") + @PostMapping(produces = APPLICATION_JSON_VALUE, consumes = MULTIPART_FORM_DATA_VALUE) + HelloDto uploadContent(HelloUploadDto contentUploadDto) throws IOException; +} diff --git a/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app221/HomeController.java b/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app221/HomeController.java new file mode 100644 index 000000000..3c4115e94 --- /dev/null +++ b/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app221/HomeController.java @@ -0,0 +1,24 @@ +package test.org.springdoc.api.v30.app221; + +import java.io.IOException; + +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; + +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; + +@RestController +public class HomeController implements HomeApi { + + @Override + public HelloDto uploadContent(@Valid HelloUploadDto uploadDto) + throws IOException { + var fileBytes = uploadDto.file.getBytes(); + return new HelloDto(uploadDto.title, fileBytes); + } + + public record HelloDto(String title, byte[] file) {} + + public record HelloUploadDto(@NotNull String title, MultipartFile file) {} +} diff --git a/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app221/SpringDocApp221Test.java b/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app221/SpringDocApp221Test.java new file mode 100644 index 000000000..d53ee8dbf --- /dev/null +++ b/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app221/SpringDocApp221Test.java @@ -0,0 +1,35 @@ +/* + * + * * + * * * + * * * * + * * * * * Copyright 2019-2022 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.app221; + +import test.org.springdoc.api.v30.AbstractSpringDocV30Test; + +import org.springframework.boot.autoconfigure.SpringBootApplication; + +public class SpringDocApp221Test extends AbstractSpringDocV30Test { + + @SpringBootApplication + static class SpringDocTestApp {} +} diff --git a/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app222/HelloController.java b/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app222/HelloController.java new file mode 100644 index 000000000..0415f7911 --- /dev/null +++ b/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app222/HelloController.java @@ -0,0 +1,28 @@ +package test.org.springdoc.api.v30.app222; + + + +import test.org.springdoc.api.v30.app222.SpringDocApp222Test.FirstHierarchyUser; +import test.org.springdoc.api.v30.app222.SpringDocApp222Test.SecondHierarchyUser; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author bnasslahsen + */ + +@RestController +class HelloController { + + @GetMapping("/hello1") + public FirstHierarchyUser getItems1() { + return null; + } + + @GetMapping("/hello2") + public SecondHierarchyUser getItems2() { + return null; + } + +} \ No newline at end of file diff --git a/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app222/SpringDocApp222Test.java b/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app222/SpringDocApp222Test.java new file mode 100644 index 000000000..d25dc5598 --- /dev/null +++ b/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app222/SpringDocApp222Test.java @@ -0,0 +1,55 @@ +/* + * + * * + * * * + * * * * + * * * * * Copyright 2019-2022 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.app222; + +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonSubTypes.Type; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeInfo.Id; +import test.org.springdoc.api.v30.AbstractSpringDocV30Test; + +import org.springframework.boot.autoconfigure.SpringBootApplication; + +public class SpringDocApp222Test extends AbstractSpringDocV30Test { + + @SpringBootApplication + static class SpringDocTestApp {} + + @JsonTypeInfo(use = Id.NAME, property = "@type") + @JsonSubTypes(@Type(CommonImplementor.class)) + interface FirstHierarchy {} + + @JsonTypeInfo(use = Id.NAME, property = "@type") + @JsonSubTypes(@Type(CommonImplementor.class)) + interface SecondHierarchy {} + + class CommonImplementor implements FirstHierarchy, SecondHierarchy {} + + record CommonImplementorUser(FirstHierarchy firstHierarchy, SecondHierarchy secondHierarchy) {} + + record FirstHierarchyUser(FirstHierarchy firstHierarchy) {} + + record SecondHierarchyUser(SecondHierarchy secondHierarchy) {} +} diff --git a/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app223/ARestController.java b/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app223/ARestController.java new file mode 100644 index 000000000..8c672eb38 --- /dev/null +++ b/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app223/ARestController.java @@ -0,0 +1,23 @@ +package test.org.springdoc.api.v30.app223; + + +import test.org.springdoc.api.v30.app223.apiobjects.AbstractChild; +import test.org.springdoc.api.v30.app223.apiobjects.AbstractParent; +import test.org.springdoc.api.v30.app223.apiobjects.Response; + +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class ARestController { + @PostMapping("/parent") + public Response parentEndpoint(@RequestBody AbstractParent parent) { + return null; + } + + @PostMapping("/child") + public Response childEndpoint(@RequestBody AbstractChild child) { + return null; + } +} diff --git a/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app223/SpringDocApp223Test.java b/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app223/SpringDocApp223Test.java new file mode 100644 index 000000000..ce55a35c6 --- /dev/null +++ b/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app223/SpringDocApp223Test.java @@ -0,0 +1,35 @@ +/* + * + * * + * * * + * * * * + * * * * * Copyright 2019-2022 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.app223; + +import test.org.springdoc.api.v30.AbstractSpringDocV30Test; + +import org.springframework.boot.autoconfigure.SpringBootApplication; + +public class SpringDocApp223Test extends AbstractSpringDocV30Test { + + @SpringBootApplication + static class SpringDocTestApp {} +} diff --git a/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app223/apiobjects/AbstractChild.java b/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app223/apiobjects/AbstractChild.java new file mode 100644 index 000000000..de53786c3 --- /dev/null +++ b/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app223/apiobjects/AbstractChild.java @@ -0,0 +1,61 @@ +package test.org.springdoc.api.v30.app223.apiobjects; + +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonSubTypes.Type; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeInfo.Id; + +@JsonTypeInfo(use = Id.NAME, property = "type") +@JsonSubTypes({ + @Type(ChildType1.class), + @Type(ChildType2.class) +}) +public abstract class AbstractChild { + private int id; + + public AbstractChild(int id) { + this.id = id; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } +} + + class ChildType1 extends AbstractChild { + private String childType1Param; + + public ChildType1(int id, String childType1Param) { + super(id); + this.childType1Param = childType1Param; + } + + public String getChildType1Param() { + return childType1Param; + } + + public void setChildType1Param(String childType1Param) { + this.childType1Param = childType1Param; + } +} + +class ChildType2 extends AbstractChild { + private String childType2Param; + + public ChildType2(int id, String childType2Param) { + super(id); + this.childType2Param = childType2Param; + } + + public String getChildType2Param() { + return childType2Param; + } + + public void setChildType2Param(String childType2Param) { + this.childType2Param = childType2Param; + } +} \ No newline at end of file diff --git a/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app223/apiobjects/AbstractParent.java b/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app223/apiobjects/AbstractParent.java new file mode 100644 index 000000000..73795f768 --- /dev/null +++ b/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app223/apiobjects/AbstractParent.java @@ -0,0 +1,72 @@ +package test.org.springdoc.api.v30.app223.apiobjects; + +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonSubTypes.Type; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeInfo.Id; + + +@JsonTypeInfo(use = Id.NAME, property = "type") +@JsonSubTypes({ + @Type(ParentType1.class), + @Type(ParentType2.class) +}) +public abstract class AbstractParent { + private int id; + + public AbstractParent(int id) { + this.id = id; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } +} + +class ParentType1 extends AbstractParent { + private String parentType1Param; + private AbstractChild abstractChild; + + public ParentType1(int id, String parentType1Param, AbstractChild abstractChild) { + super(id); + this.parentType1Param = parentType1Param; + this.abstractChild = abstractChild; + } + + public String getParentType1Param() { + return parentType1Param; + } + + public void setParentType1Param(String parentType1Param) { + this.parentType1Param = parentType1Param; + } + + public AbstractChild getAbstractChild() { + return abstractChild; + } + + public void setAbstractChild(AbstractChild abstractChild) { + this.abstractChild = abstractChild; + } +} + +class ParentType2 extends AbstractParent { + private String parentType2Param; + + public ParentType2(int id, String parentType2Param) { + super(id); + this.parentType2Param = parentType2Param; + } + + public String getParentType2Param() { + return parentType2Param; + } + + public void setParentType2Param(String parentType2Param) { + this.parentType2Param = parentType2Param; + } +} diff --git a/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app223/apiobjects/Response.java b/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app223/apiobjects/Response.java new file mode 100644 index 000000000..71dfdf576 --- /dev/null +++ b/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app223/apiobjects/Response.java @@ -0,0 +1,4 @@ +package test.org.springdoc.api.v30.app223.apiobjects; + +public record Response(AbstractParent abstractParent, AbstractChild abstractChild) { +} \ No newline at end of file diff --git a/springdoc-openapi-starter-webmvc-api/src/test/resources/results/3.0.1/app185.json b/springdoc-openapi-starter-webmvc-api/src/test/resources/results/3.0.1/app185.json index 699f9026b..78b116b1d 100644 --- a/springdoc-openapi-starter-webmvc-api/src/test/resources/results/3.0.1/app185.json +++ b/springdoc-openapi-starter-webmvc-api/src/test/resources/results/3.0.1/app185.json @@ -109,6 +109,7 @@ "type": "string" } }, + "description": "This is a Pet", "discriminator": { "propertyName": "type" } diff --git a/springdoc-openapi-starter-webmvc-api/src/test/resources/results/3.0.1/app218.json b/springdoc-openapi-starter-webmvc-api/src/test/resources/results/3.0.1/app218.json new file mode 100644 index 000000000..4aeec31eb --- /dev/null +++ b/springdoc-openapi-starter-webmvc-api/src/test/resources/results/3.0.1/app218.json @@ -0,0 +1,48 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "OpenAPI definition", + "version": "v0" + }, + "servers": [ + { + "url": "http://localhost", + "description": "Generated server url" + } + ], + "paths": { + "/": { + "get": { + "tags": [ + "Sample" + ], + "summary": "Summary", + "description": "This is description.", + "operationId": "get", + "responses": { + "201": { + "description": "201 (Created)", + "headers": { + "Location": { + "description": "Sample endpoint", + "style": "simple", + "schema": { + "type": "string", + "format": "uri" + } + } + }, + "content": { + "*/*": { + "schema": { + "type": "string" + } + } + } + } + } + } + } + }, + "components": {} +} diff --git a/springdoc-openapi-starter-webmvc-api/src/test/resources/results/3.0.1/app219.json b/springdoc-openapi-starter-webmvc-api/src/test/resources/results/3.0.1/app219.json new file mode 100644 index 000000000..70b2fb8d5 --- /dev/null +++ b/springdoc-openapi-starter-webmvc-api/src/test/resources/results/3.0.1/app219.json @@ -0,0 +1,76 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "OpenAPI definition", + "version": "v0" + }, + "servers": [ + { + "url": "http://localhost", + "description": "Generated server url" + } + ], + "paths": { + "/api/testpost": { + "post": { + "tags": [ + "hello-controller" + ], + "operationId": "testpost", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TestObject" + } + }, + "application/json;charset=UTF-8": { + "schema": { + "$ref": "#/components/schemas/TestObject" + } + }, + "application/json; charset=UTF-8": { + "schema": { + "$ref": "#/components/schemas/TestObject" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/xml": { + "schema": { + "$ref": "#/components/schemas/TestObject" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/TestObject" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "TestObject": { + "type": "object", + "properties": { + "stringValue": { + "type": "string" + }, + "localDateTime": { + "type": "string", + "format": "date-time" + } + } + } + } + } +} diff --git a/springdoc-openapi-starter-webmvc-api/src/test/resources/results/3.0.1/app220.json b/springdoc-openapi-starter-webmvc-api/src/test/resources/results/3.0.1/app220.json new file mode 100644 index 000000000..de2244501 --- /dev/null +++ b/springdoc-openapi-starter-webmvc-api/src/test/resources/results/3.0.1/app220.json @@ -0,0 +1,132 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "OpenAPI definition", + "version": "v0" + }, + "servers": [ + { + "url": "http://localhost", + "description": "Generated server url" + } + ], + "paths": { + "/parent": { + "post": { + "tags": [ + "hello-controller" + ], + "operationId": "parentEndpoint", + "requestBody": { + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/Superclass" + }, + { + "$ref": "#/components/schemas/IntermediateClass" + }, + { + "$ref": "#/components/schemas/Image" + }, + { + "$ref": "#/components/schemas/Mail" + }, + { + "$ref": "#/components/schemas/Home" + } + ] + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK" + } + } + } + } + }, + "components": { + "schemas": { + "Home": { + "type": "object", + "allOf": [ + { + "$ref": "#/components/schemas/Mail" + } + ] + }, + "Image": { + "type": "object", + "allOf": [ + { + "$ref": "#/components/schemas/IntermediateClass" + } + ] + }, + "IntermediateClass": { + "required": [ + "@type" + ], + "type": "object", + "discriminator": { + "propertyName": "@type" + }, + "allOf": [ + { + "$ref": "#/components/schemas/Superclass" + }, + { + "type": "object", + "properties": { + "@type": { + "type": "string" + } + } + } + ] + }, + "Mail": { + "required": [ + "@type" + ], + "type": "object", + "discriminator": { + "propertyName": "@type" + }, + "allOf": [ + { + "$ref": "#/components/schemas/IntermediateClass" + }, + { + "type": "object", + "properties": { + "@type": { + "type": "string" + } + } + } + ] + }, + "Superclass": { + "required": [ + "@type" + ], + "type": "object", + "properties": { + "@type": { + "type": "string" + } + }, + "discriminator": { + "propertyName": "@type" + } + } + } + } +} diff --git a/springdoc-openapi-starter-webmvc-api/src/test/resources/results/3.0.1/app221.json b/springdoc-openapi-starter-webmvc-api/src/test/resources/results/3.0.1/app221.json new file mode 100644 index 000000000..2554fd2f9 --- /dev/null +++ b/springdoc-openapi-starter-webmvc-api/src/test/resources/results/3.0.1/app221.json @@ -0,0 +1,83 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "OpenAPI definition", + "version": "v0" + }, + "servers": [ + { + "url": "http://localhost", + "description": "Generated server url" + } + ], + "tags": [ + { + "name": "Hello World Api", + "description": "This is a test api" + } + ], + "paths": { + "/api/hello": { + "post": { + "tags": [ + "Hello World Api" + ], + "summary": "Upload new content", + "description": "Upload test content", + "operationId": "uploadContent", + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/HelloUploadDto" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HelloDto" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "HelloUploadDto": { + "required": [ + "title" + ], + "type": "object", + "properties": { + "title": { + "type": "string" + }, + "file": { + "type": "string", + "format": "binary" + } + } + }, + "HelloDto": { + "type": "object", + "properties": { + "title": { + "type": "string" + }, + "file": { + "type": "string", + "format": "byte" + } + } + } + } + } +} diff --git a/springdoc-openapi-starter-webmvc-api/src/test/resources/results/3.0.1/app222.json b/springdoc-openapi-starter-webmvc-api/src/test/resources/results/3.0.1/app222.json new file mode 100644 index 000000000..cd4de9975 --- /dev/null +++ b/springdoc-openapi-starter-webmvc-api/src/test/resources/results/3.0.1/app222.json @@ -0,0 +1,122 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "OpenAPI definition", + "version": "v0" + }, + "servers": [ + { + "url": "http://localhost", + "description": "Generated server url" + } + ], + "paths": { + "/hello2": { + "get": { + "tags": [ + "hello-controller" + ], + "operationId": "getItems2", + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/SecondHierarchyUser" + } + } + } + } + } + } + }, + "/hello1": { + "get": { + "tags": [ + "hello-controller" + ], + "operationId": "getItems1", + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/FirstHierarchyUser" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "CommonImplementor": { + "type": "object", + "allOf": [ + { + "$ref": "#/components/schemas/SecondHierarchy" + }, + { + "$ref": "#/components/schemas/FirstHierarchy" + } + ] + }, + "SecondHierarchy": { + "required": [ + "@type" + ], + "type": "object", + "properties": { + "@type": { + "type": "string" + } + }, + "discriminator": { + "propertyName": "@type" + } + }, + "SecondHierarchyUser": { + "type": "object", + "properties": { + "secondHierarchy": { + "oneOf": [ + { + "$ref": "#/components/schemas/CommonImplementor" + } + ] + } + } + }, + "FirstHierarchy": { + "required": [ + "@type" + ], + "type": "object", + "properties": { + "@type": { + "type": "string" + } + }, + "discriminator": { + "propertyName": "@type" + } + }, + "FirstHierarchyUser": { + "type": "object", + "properties": { + "firstHierarchy": { + "oneOf": [ + { + "$ref": "#/components/schemas/CommonImplementor" + } + ] + } + } + } + } + } +} diff --git a/springdoc-openapi-starter-webmvc-api/src/test/resources/results/3.0.1/app223.json b/springdoc-openapi-starter-webmvc-api/src/test/resources/results/3.0.1/app223.json new file mode 100644 index 000000000..31ab9d5f6 --- /dev/null +++ b/springdoc-openapi-starter-webmvc-api/src/test/resources/results/3.0.1/app223.json @@ -0,0 +1,228 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "OpenAPI definition", + "version": "v0" + }, + "servers": [ + { + "url": "http://localhost", + "description": "Generated server url" + } + ], + "paths": { + "/parent": { + "post": { + "tags": [ + "a-rest-controller" + ], + "operationId": "parentEndpoint", + "requestBody": { + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/ParentType1" + }, + { + "$ref": "#/components/schemas/ParentType2" + } + ] + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/Response" + } + } + } + } + } + } + }, + "/child": { + "post": { + "tags": [ + "a-rest-controller" + ], + "operationId": "childEndpoint", + "requestBody": { + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/ChildType1" + }, + { + "$ref": "#/components/schemas/ChildType2" + } + ] + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/Response" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "AbstractChild": { + "required": [ + "type" + ], + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int32" + }, + "type": { + "type": "string" + } + }, + "discriminator": { + "propertyName": "type" + } + }, + "AbstractParent": { + "required": [ + "type" + ], + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int32" + }, + "type": { + "type": "string" + } + }, + "discriminator": { + "propertyName": "type" + } + }, + "ChildType1": { + "type": "object", + "allOf": [ + { + "$ref": "#/components/schemas/AbstractChild" + }, + { + "type": "object", + "properties": { + "childType1Param": { + "type": "string" + } + } + } + ] + }, + "ChildType2": { + "type": "object", + "allOf": [ + { + "$ref": "#/components/schemas/AbstractChild" + }, + { + "type": "object", + "properties": { + "childType2Param": { + "type": "string" + } + } + } + ] + }, + "ParentType1": { + "type": "object", + "allOf": [ + { + "$ref": "#/components/schemas/AbstractParent" + }, + { + "type": "object", + "properties": { + "parentType1Param": { + "type": "string" + }, + "abstractChild": { + "oneOf": [ + { + "$ref": "#/components/schemas/ChildType1" + }, + { + "$ref": "#/components/schemas/ChildType2" + } + ] + } + } + } + ] + }, + "ParentType2": { + "type": "object", + "allOf": [ + { + "$ref": "#/components/schemas/AbstractParent" + }, + { + "type": "object", + "properties": { + "parentType2Param": { + "type": "string" + } + } + } + ] + }, + "Response": { + "type": "object", + "properties": { + "abstractParent": { + "oneOf": [ + { + "$ref": "#/components/schemas/ParentType1" + }, + { + "$ref": "#/components/schemas/ParentType2" + } + ] + }, + "abstractChild": { + "oneOf": [ + { + "$ref": "#/components/schemas/ChildType1" + }, + { + "$ref": "#/components/schemas/ChildType2" + } + ] + } + } + } + } + } +} diff --git a/springdoc-openapi-starter-webmvc-api/src/test/resources/results/3.0.1/app38.json b/springdoc-openapi-starter-webmvc-api/src/test/resources/results/3.0.1/app38.json index e28206a0b..0de67a730 100644 --- a/springdoc-openapi-starter-webmvc-api/src/test/resources/results/3.0.1/app38.json +++ b/springdoc-openapi-starter-webmvc-api/src/test/resources/results/3.0.1/app38.json @@ -23,11 +23,8 @@ "content": { "*/*": { "schema": { - "type": "array", - "items": { - "type": "string", - "format": "byte" - } + "type": "string", + "format": "byte" } } } diff --git a/springdoc-openapi-starter-webmvc-ui/pom.xml b/springdoc-openapi-starter-webmvc-ui/pom.xml index d987eb1c8..3d5f5fd3d 100644 --- a/springdoc-openapi-starter-webmvc-ui/pom.xml +++ b/springdoc-openapi-starter-webmvc-ui/pom.xml @@ -3,7 +3,7 @@ org.springdoc springdoc-openapi - 2.5.0 + 2.6.0 springdoc-openapi-starter-webmvc-ui diff --git a/springdoc-openapi-starter-webmvc-ui/src/test/java/test/org/springdoc/ui/app4/SpringDocApp4Test.java b/springdoc-openapi-starter-webmvc-ui/src/test/java/test/org/springdoc/ui/app4/SpringDocApp4Test.java index 1d809ef9f..53e2d8a1d 100644 --- a/springdoc-openapi-starter-webmvc-ui/src/test/java/test/org/springdoc/ui/app4/SpringDocApp4Test.java +++ b/springdoc-openapi-starter-webmvc-ui/src/test/java/test/org/springdoc/ui/app4/SpringDocApp4Test.java @@ -37,10 +37,10 @@ public void swagger_config_for_multiple_groups() throws Exception { .andExpect(status().isOk()) .andExpect(jsonPath("configUrl", equalTo("/v3/api-docs/swagger-config"))) .andExpect(jsonPath("url").doesNotExist()) - .andExpect(jsonPath("urls[0].url", equalTo("/v3/api-docs/stores"))) - .andExpect(jsonPath("urls[0].name", equalTo("stores"))) - .andExpect(jsonPath("urls[1].url", equalTo("/v3/api-docs/pets"))) - .andExpect(jsonPath("urls[1].name", equalTo("The pets"))) + .andExpect(jsonPath("urls[1].url", equalTo("/v3/api-docs/stores"))) + .andExpect(jsonPath("urls[1].name", equalTo("stores"))) + .andExpect(jsonPath("urls[0].url", equalTo("/v3/api-docs/pets"))) + .andExpect(jsonPath("urls[0].name", equalTo("zpets"))) .andExpect(jsonPath("$['urls.primaryName']", equalTo("pets"))); } } \ No newline at end of file diff --git a/springdoc-openapi-starter-webmvc-ui/src/test/java/test/org/springdoc/ui/app4/SpringDocTestApp.java b/springdoc-openapi-starter-webmvc-ui/src/test/java/test/org/springdoc/ui/app4/SpringDocTestApp.java index f8d22037f..13506213c 100644 --- a/springdoc-openapi-starter-webmvc-ui/src/test/java/test/org/springdoc/ui/app4/SpringDocTestApp.java +++ b/springdoc-openapi-starter-webmvc-ui/src/test/java/test/org/springdoc/ui/app4/SpringDocTestApp.java @@ -47,7 +47,7 @@ public GroupedOpenApi groupOpenApi() { String[] paths = { "/pet/**" }; return GroupedOpenApi.builder() .group("pets") - .displayName("The pets") + .displayName("zpets") .pathsToMatch(paths) .build(); } diff --git a/springdoc-openapi-tests/pom.xml b/springdoc-openapi-tests/pom.xml index 83ac3f9a5..7db59be14 100644 --- a/springdoc-openapi-tests/pom.xml +++ b/springdoc-openapi-tests/pom.xml @@ -2,7 +2,7 @@ springdoc-openapi org.springdoc - 2.5.0 + 2.6.0 pom 4.0.0 diff --git a/springdoc-openapi-tests/springdoc-openapi-actuator-webflux-tests/pom.xml b/springdoc-openapi-tests/springdoc-openapi-actuator-webflux-tests/pom.xml index 40ce8b31b..e4773b39b 100644 --- a/springdoc-openapi-tests/springdoc-openapi-actuator-webflux-tests/pom.xml +++ b/springdoc-openapi-tests/springdoc-openapi-actuator-webflux-tests/pom.xml @@ -2,7 +2,7 @@ springdoc-openapi-tests org.springdoc - 2.5.0 + 2.6.0 4.0.0 diff --git a/springdoc-openapi-tests/springdoc-openapi-actuator-webflux-tests/src/test/java/test/org/springdoc/api/AbstractCommonTest.java b/springdoc-openapi-tests/springdoc-openapi-actuator-webflux-tests/src/test/java/test/org/springdoc/api/AbstractCommonTest.java index 17446b334..14e357531 100644 --- a/springdoc-openapi-tests/springdoc-openapi-actuator-webflux-tests/src/test/java/test/org/springdoc/api/AbstractCommonTest.java +++ b/springdoc-openapi-tests/springdoc-openapi-actuator-webflux-tests/src/test/java/test/org/springdoc/api/AbstractCommonTest.java @@ -7,13 +7,17 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springdoc.core.utils.Constants; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.TestPropertySource; +import org.springframework.test.web.reactive.server.EntityExchangeResult; import org.springframework.test.web.reactive.server.WebTestClient; +import static org.skyscreamer.jsonassert.JSONAssert.assertEquals; + @AutoConfigureWebTestClient(timeout = "3600000") @ActiveProfiles("test") @TestPropertySource(properties = { "management.endpoints.enabled-by-default=false" }) @@ -34,4 +38,19 @@ protected String getContent(String fileName) { throw new RuntimeException("Failed to read file: " + fileName, e); } } + + protected void testApp(String testId, String groupName) throws Exception{ + String result = null; + try { + EntityExchangeResult getResult = webTestClient.get().uri(Constants.DEFAULT_API_DOCS_URL + "/" + groupName).exchange() + .expectStatus().isOk().expectBody().returnResult(); + result = new String(getResult.getResponseBody()); + String expected = getContent("results/app" + testId + ".json"); + assertEquals(expected, result, true); + } + catch (AssertionError e) { + LOGGER.error(result); + throw e; + } + } } diff --git a/springdoc-openapi-tests/springdoc-openapi-actuator-webflux-tests/src/test/java/test/org/springdoc/api/AbstractSpringDocActuatorTest.java b/springdoc-openapi-tests/springdoc-openapi-actuator-webflux-tests/src/test/java/test/org/springdoc/api/AbstractSpringDocActuatorTest.java index b2a483d38..fa81f9c09 100644 --- a/springdoc-openapi-tests/springdoc-openapi-actuator-webflux-tests/src/test/java/test/org/springdoc/api/AbstractSpringDocActuatorTest.java +++ b/springdoc-openapi-tests/springdoc-openapi-actuator-webflux-tests/src/test/java/test/org/springdoc/api/AbstractSpringDocActuatorTest.java @@ -30,6 +30,8 @@ import org.springframework.test.context.TestPropertySource; import org.springframework.web.reactive.function.client.WebClient; +import static org.skyscreamer.jsonassert.JSONAssert.assertEquals; + @TestPropertySource(properties = { "management.endpoints.enabled-by-default=true" }) public abstract class AbstractSpringDocActuatorTest extends AbstractCommonTest { @@ -43,4 +45,18 @@ void init() { webClient = WebClient.builder().baseUrl("http://localhost:" + this.managementPort) .build(); } + + protected void testWithWebClient(String testId, String uri) throws Exception{ + String result = null; + try { + result = webClient.get().uri(uri).retrieve() + .bodyToMono(String.class).block(); + String expected = getContent("results/app"+testId+".json"); + assertEquals(expected, result, true); + } + catch (AssertionError e) { + LOGGER.error(result); + throw e; + } + } } diff --git a/springdoc-openapi-tests/springdoc-openapi-actuator-webflux-tests/src/test/java/test/org/springdoc/api/AbstractSpringDocTest.java b/springdoc-openapi-tests/springdoc-openapi-actuator-webflux-tests/src/test/java/test/org/springdoc/api/AbstractSpringDocTest.java index 4b1df181d..b93e551df 100644 --- a/springdoc-openapi-tests/springdoc-openapi-actuator-webflux-tests/src/test/java/test/org/springdoc/api/AbstractSpringDocTest.java +++ b/springdoc-openapi-tests/springdoc-openapi-actuator-webflux-tests/src/test/java/test/org/springdoc/api/AbstractSpringDocTest.java @@ -18,41 +18,20 @@ package test.org.springdoc.api; +import org.apache.commons.lang3.StringUtils; import org.junit.jupiter.api.Test; -import org.springdoc.core.utils.Constants; import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest; -import org.springframework.test.web.reactive.server.EntityExchangeResult; -import org.springframework.web.reactive.function.server.HandlerFunction; -import org.springframework.web.reactive.function.server.ServerResponse; - -import static org.skyscreamer.jsonassert.JSONAssert.assertEquals; @WebFluxTest public abstract class AbstractSpringDocTest extends AbstractCommonTest { - public static final HandlerFunction HANDLER_FUNCTION = request -> ServerResponse.ok().build(); - - protected String groupName = ""; - - @Test public void testApp() throws Exception { - String result = null; - try { - EntityExchangeResult getResult = webTestClient.get().uri(Constants.DEFAULT_API_DOCS_URL + groupName).exchange() - .expectStatus().isOk().expectBody().returnResult(); - - result = new String(getResult.getResponseBody()); - String className = getClass().getSimpleName(); - String testNumber = className.replaceAll("[^0-9]", ""); - String expected = getContent("results/app" + testNumber + ".json"); - assertEquals(expected, result, true); - } - catch (AssertionError e) { - LOGGER.error(result); - throw e; - } + String className = getClass().getSimpleName(); + String testId = className.replaceAll("[^0-9]", ""); + testApp(testId, StringUtils.EMPTY); } + } diff --git a/springdoc-openapi-tests/springdoc-openapi-actuator-webflux-tests/src/test/java/test/org/springdoc/api/app146/SpringDocApp146Test.java b/springdoc-openapi-tests/springdoc-openapi-actuator-webflux-tests/src/test/java/test/org/springdoc/api/app146/SpringDocApp146Test.java index 42a72b1d5..53a87de9f 100644 --- a/springdoc-openapi-tests/springdoc-openapi-actuator-webflux-tests/src/test/java/test/org/springdoc/api/app146/SpringDocApp146Test.java +++ b/springdoc-openapi-tests/springdoc-openapi-actuator-webflux-tests/src/test/java/test/org/springdoc/api/app146/SpringDocApp146Test.java @@ -24,10 +24,7 @@ import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.web.reactive.server.EntityExchangeResult; - -import static org.skyscreamer.jsonassert.JSONAssert.assertEquals; - +import org.springframework.context.annotation.ComponentScan; @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, properties = { "management.endpoints.web.exposure.include=*", @@ -40,31 +37,16 @@ public class SpringDocApp146Test extends AbstractSpringDocActuatorTest { @Test public void testApp() throws Exception { - EntityExchangeResult getResult = webTestClient.get().uri(Constants.DEFAULT_API_DOCS_URL + "/" + Constants.ACTUATOR_DEFAULT_GROUP) - .exchange() - .expectStatus().isOk() - .expectBody() - .jsonPath("$.openapi").isEqualTo("3.0.1") - .returnResult(); - String result = new String(getResult.getResponseBody()); - String expected = getContent("results/app146-1.json"); - assertEquals(expected, result, true); + super.testApp("146-1", Constants.ACTUATOR_DEFAULT_GROUP); } @Test public void testApp1() throws Exception { - EntityExchangeResult getResult = webTestClient.get().uri(Constants.DEFAULT_API_DOCS_URL + "/" + Constants.DEFAULT_GROUP_NAME) - .exchange() - .expectStatus().isOk() - .expectBody() - .jsonPath("$.openapi").isEqualTo("3.0.1") - .returnResult(); - String result = new String(getResult.getResponseBody()); - String expected = getContent("results/app146-2.json"); - assertEquals(expected, result, true); + super.testApp("146-2", Constants.DEFAULT_GROUP_NAME); } @SpringBootApplication + @ComponentScan(basePackages = { "org.springdoc", "test.org.springdoc.api.app146" }) static class SpringDocTestApp {} } diff --git a/springdoc-openapi-tests/springdoc-openapi-actuator-webflux-tests/src/test/java/test/org/springdoc/api/app147/SpringDocApp147Test.java b/springdoc-openapi-tests/springdoc-openapi-actuator-webflux-tests/src/test/java/test/org/springdoc/api/app147/SpringDocApp147Test.java index 065edeb43..83a884cbf 100644 --- a/springdoc-openapi-tests/springdoc-openapi-actuator-webflux-tests/src/test/java/test/org/springdoc/api/app147/SpringDocApp147Test.java +++ b/springdoc-openapi-tests/springdoc-openapi-actuator-webflux-tests/src/test/java/test/org/springdoc/api/app147/SpringDocApp147Test.java @@ -24,9 +24,6 @@ import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.web.reactive.server.EntityExchangeResult; - -import static org.skyscreamer.jsonassert.JSONAssert.assertEquals; @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, @@ -40,28 +37,13 @@ public class SpringDocApp147Test extends AbstractSpringDocActuatorTest { @Test public void testApp() throws Exception { - EntityExchangeResult getResult = webTestClient.get().uri(Constants.DEFAULT_API_DOCS_URL + "/" + Constants.ACTUATOR_DEFAULT_GROUP) - .exchange() - .expectStatus().isOk() - .expectBody() - .jsonPath("$.openapi").isEqualTo("3.0.1") - .returnResult(); - String result = new String(getResult.getResponseBody()); - String expected = getContent("results/app147-1.json"); - assertEquals(expected, result, true); + super.testApp("147-1", Constants.ACTUATOR_DEFAULT_GROUP); } @Test public void testApp1() throws Exception { - EntityExchangeResult getResult = webTestClient.get().uri(Constants.DEFAULT_API_DOCS_URL + "/users") - .exchange() - .expectStatus().isOk() - .expectBody() - .jsonPath("$.openapi").isEqualTo("3.0.1") - .returnResult(); - String result = new String(getResult.getResponseBody()); - String expected = getContent("results/app147-2.json"); - assertEquals(expected, result, true); + super.testApp("147-2", Constants.ACTUATOR_DEFAULT_GROUP); + } @Test diff --git a/springdoc-openapi-tests/springdoc-openapi-actuator-webflux-tests/src/test/java/test/org/springdoc/api/app148/SpringDocApp148Test.java b/springdoc-openapi-tests/springdoc-openapi-actuator-webflux-tests/src/test/java/test/org/springdoc/api/app148/SpringDocApp148Test.java index d761b9d25..11b004e45 100644 --- a/springdoc-openapi-tests/springdoc-openapi-actuator-webflux-tests/src/test/java/test/org/springdoc/api/app148/SpringDocApp148Test.java +++ b/springdoc-openapi-tests/springdoc-openapi-actuator-webflux-tests/src/test/java/test/org/springdoc/api/app148/SpringDocApp148Test.java @@ -30,7 +30,6 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; -import static org.skyscreamer.jsonassert.JSONAssert.assertEquals; @SpringBootTest(webEnvironment = WebEnvironment.DEFINED_PORT, @@ -46,18 +45,12 @@ public class SpringDocApp148Test extends AbstractSpringDocActuatorTest { @Test public void testApp() throws Exception { - String result = webClient.get().uri("/test/application/openapi/users").retrieve() - .bodyToMono(String.class).block(); - String expected = getContent("results/app148-1.json"); - assertEquals(expected, result, true); + super.testWithWebClient("148-1","/test/application/openapi/users"); } @Test public void testApp2() throws Exception { - String result = webClient.get().uri("/test/application/openapi/x-actuator").retrieve() - .bodyToMono(String.class).block(); - String expected = getContent("results/app148-2.json"); - assertEquals(expected, result, true); + super.testWithWebClient("148-2","/test/application/openapi/x-actuator"); } @Test diff --git a/springdoc-openapi-tests/springdoc-openapi-actuator-webflux-tests/src/test/java/test/org/springdoc/api/app186/SpringDocApp186Test.java b/springdoc-openapi-tests/springdoc-openapi-actuator-webflux-tests/src/test/java/test/org/springdoc/api/app186/SpringDocApp186Test.java index 989f9be43..8282d1e3a 100644 --- a/springdoc-openapi-tests/springdoc-openapi-actuator-webflux-tests/src/test/java/test/org/springdoc/api/app186/SpringDocApp186Test.java +++ b/springdoc-openapi-tests/springdoc-openapi-actuator-webflux-tests/src/test/java/test/org/springdoc/api/app186/SpringDocApp186Test.java @@ -27,7 +27,6 @@ import org.junit.jupiter.api.Test; import org.springdoc.core.customizers.ActuatorOpenApiCustomizer; -import org.springdoc.core.customizers.OpenApiCustomizer; import org.springdoc.core.customizers.OperationCustomizer; import org.springdoc.core.models.GroupedOpenApi; import org.springdoc.core.utils.Constants; @@ -59,21 +58,21 @@ public void testApp() throws Exception { } @Test - public void testGroupActuatorAsCodeCheckBackwardsCompatibility() throws Exception { + public void testGroupActuatorAsCodeCheckBackwardsCompatibility() { webTestClient.get().uri(Constants.DEFAULT_API_DOCS_URL + "/group-actuator-as-code-check-backwards-compatibility").exchange() .expectStatus().isOk() .expectBody().json(getContent("results/app186.json"), true); } @Test - public void testGroupActuatorAsCode() throws Exception { + public void testGroupActuatorAsCode() { webTestClient.get().uri(Constants.DEFAULT_API_DOCS_URL + "/group-actuator-as-code").exchange() .expectStatus().isOk() .expectBody().json(getContent("results/app186.json"), true); } @Test - public void testGroupActuatorAsProperties() throws Exception { + public void testGroupActuatorAsProperties() { webTestClient.get().uri(Constants.DEFAULT_API_DOCS_URL + "/group-actuator-as-properties").exchange() .expectStatus().isOk() .expectBody().json(getContent("results/app186.json"), true); diff --git a/springdoc-openapi-tests/springdoc-openapi-actuator-webflux-tests/src/test/java/test/org/springdoc/api/app76/SpringDocApp76Test.java b/springdoc-openapi-tests/springdoc-openapi-actuator-webflux-tests/src/test/java/test/org/springdoc/api/app76/SpringDocApp76Test.java index b0f4c1508..9f877e497 100644 --- a/springdoc-openapi-tests/springdoc-openapi-actuator-webflux-tests/src/test/java/test/org/springdoc/api/app76/SpringDocApp76Test.java +++ b/springdoc-openapi-tests/springdoc-openapi-actuator-webflux-tests/src/test/java/test/org/springdoc/api/app76/SpringDocApp76Test.java @@ -35,14 +35,9 @@ }) public class SpringDocApp76Test extends AbstractSpringDocTest { - public SpringDocApp76Test() { - this.groupName = "/actuator"; - } - - @Test public void testApp() throws Exception { - webTestClient.get().uri(Constants.DEFAULT_API_DOCS_URL + groupName).exchange().expectStatus().isOk().expectBody() + webTestClient.get().uri(Constants.DEFAULT_API_DOCS_URL + "/actuator").exchange().expectStatus().isOk().expectBody() .jsonPath("$.openapi").isEqualTo("3.0.1") .jsonPath("$.paths./actuator/health.get.operationId").exists(); } diff --git a/springdoc-openapi-tests/springdoc-openapi-actuator-webflux-tests/src/test/resources/logback-test.xml b/springdoc-openapi-tests/springdoc-openapi-actuator-webflux-tests/src/test/resources/logback-test.xml index 24cd4646e..3fd46cfab 100644 --- a/springdoc-openapi-tests/springdoc-openapi-actuator-webflux-tests/src/test/resources/logback-test.xml +++ b/springdoc-openapi-tests/springdoc-openapi-actuator-webflux-tests/src/test/resources/logback-test.xml @@ -1,6 +1,5 @@ - \ No newline at end of file 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..bcfb2e81e 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 @@ -176,6 +176,68 @@ } } }, + "/application/sbom/{id}": { + "get": { + "tags": [ + "Actuator" + ], + "summary": "Actuator web endpoint 'sbom-id'", + "operationId": "sbom-id", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/octet-stream": { + "schema": { + "type": "object" + } + } + } + } + } + } + }, + "/application/sbom": { + "get": { + "tags": [ + "Actuator" + ], + "summary": "Actuator web endpoint 'sbom'", + "operationId": "sbom", + "responses": { + "200": { + "description": "OK", + "content": { + "application/vnd.spring-boot.actuator.v3+json": { + "schema": { + "type": "object" + } + }, + "application/vnd.spring-boot.actuator.v2+json": { + "schema": { + "type": "object" + } + }, + "application/json": { + "schema": { + "type": "object" + } + } + } + } + } + } + }, "/application/metrics/{requiredMetricName}": { "get": { "tags": [ @@ -217,6 +279,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..33d159196 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 @@ -176,6 +176,68 @@ } } }, + "/application/sbom/{id}": { + "get": { + "tags": [ + "Actuator" + ], + "summary": "Actuator web endpoint 'sbom-id'", + "operationId": "sbom-id", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/octet-stream": { + "schema": { + "type": "object" + } + } + } + } + } + } + }, + "/application/sbom": { + "get": { + "tags": [ + "Actuator" + ], + "summary": "Actuator web endpoint 'sbom'", + "operationId": "sbom", + "responses": { + "200": { + "description": "OK", + "content": { + "application/vnd.spring-boot.actuator.v3+json": { + "schema": { + "type": "object" + } + }, + "application/vnd.spring-boot.actuator.v2+json": { + "schema": { + "type": "object" + } + }, + "application/json": { + "schema": { + "type": "object" + } + } + } + } + } + } + }, "/application/metrics/{requiredMetricName}": { "get": { "tags": [ @@ -217,6 +279,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-2.json b/springdoc-openapi-tests/springdoc-openapi-actuator-webflux-tests/src/test/resources/results/app147-2.json index e7c346c11..ebc856a6f 100644 --- a/springdoc-openapi-tests/springdoc-openapi-actuator-webflux-tests/src/test/resources/results/app147-2.json +++ b/springdoc-openapi-tests/springdoc-openapi-actuator-webflux-tests/src/test/resources/results/app147-2.json @@ -6,24 +6,833 @@ }, "servers": [ { - "url": "", + "url": "http://localhost:9297/test", "description": "Generated server url" } ], + "tags": [ + { + "name": "Actuator", + "description": "Monitor and interact", + "externalDocs": { + "description": "Spring Boot Actuator Web API Documentation", + "url": "https://docs.spring.io/spring-boot/docs/current/actuator-api/html/" + } + } + ], "paths": { - "/persons": { + "/application/loggers/{name}": { + "get": { + "tags": [ + "Actuator" + ], + "summary": "Actuator web endpoint 'loggers-name'", + "operationId": "loggers-name", + "parameters": [ + { + "name": "name", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/vnd.spring-boot.actuator.v3+json": { + "schema": { + "type": "object" + } + }, + "application/vnd.spring-boot.actuator.v2+json": { + "schema": { + "type": "object" + } + }, + "application/json": { + "schema": { + "type": "object" + } + } + } + } + } + }, + "post": { + "tags": [ + "Actuator" + ], + "summary": "Actuator web endpoint 'loggers-name'", + "operationId": "loggers-name_2", + "parameters": [ + { + "name": "name", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "string", + "enum": [ + "TRACE", + "DEBUG", + "INFO", + "WARN", + "ERROR", + "FATAL", + "OFF" + ] + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "type": "object" + } + } + } + } + } + } + }, + "/application/threaddump": { + "get": { + "tags": [ + "Actuator" + ], + "summary": "Actuator web endpoint 'threaddump'", + "operationId": "threaddump", + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain;charset=UTF-8": { + "schema": { + "type": "object" + } + }, + "application/vnd.spring-boot.actuator.v3+json": { + "schema": { + "type": "object" + } + }, + "application/vnd.spring-boot.actuator.v2+json": { + "schema": { + "type": "object" + } + }, + "application/json": { + "schema": { + "type": "object" + } + } + } + } + } + } + }, + "/application/scheduledtasks": { + "get": { + "tags": [ + "Actuator" + ], + "summary": "Actuator web endpoint 'scheduledtasks'", + "operationId": "scheduledtasks", + "responses": { + "200": { + "description": "OK", + "content": { + "application/vnd.spring-boot.actuator.v3+json": { + "schema": { + "type": "object" + } + }, + "application/vnd.spring-boot.actuator.v2+json": { + "schema": { + "type": "object" + } + }, + "application/json": { + "schema": { + "type": "object" + } + } + } + } + } + } + }, + "/application/sbom/{id}": { + "get": { + "tags": [ + "Actuator" + ], + "summary": "Actuator web endpoint 'sbom-id'", + "operationId": "sbom-id", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/octet-stream": { + "schema": { + "type": "object" + } + } + } + } + } + } + }, + "/application/sbom": { + "get": { + "tags": [ + "Actuator" + ], + "summary": "Actuator web endpoint 'sbom'", + "operationId": "sbom", + "responses": { + "200": { + "description": "OK", + "content": { + "application/vnd.spring-boot.actuator.v3+json": { + "schema": { + "type": "object" + } + }, + "application/vnd.spring-boot.actuator.v2+json": { + "schema": { + "type": "object" + } + }, + "application/json": { + "schema": { + "type": "object" + } + } + } + } + } + } + }, + "/application/metrics/{requiredMetricName}": { + "get": { + "tags": [ + "Actuator" + ], + "summary": "Actuator web endpoint 'metrics-requiredMetricName'", + "operationId": "metrics-requiredMetricName", + "parameters": [ + { + "name": "requiredMetricName", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/vnd.spring-boot.actuator.v3+json": { + "schema": { + "type": "object" + } + }, + "application/vnd.spring-boot.actuator.v2+json": { + "schema": { + "type": "object" + } + }, + "application/json": { + "schema": { + "type": "object" + } + } + } + } + } + } + }, + "/application/metrics": { + "get": { + "tags": [ + "Actuator" + ], + "summary": "Actuator web endpoint 'metrics'", + "operationId": "metrics", + "responses": { + "200": { + "description": "OK", + "content": { + "application/vnd.spring-boot.actuator.v3+json": { + "schema": { + "type": "object" + } + }, + "application/vnd.spring-boot.actuator.v2+json": { + "schema": { + "type": "object" + } + }, + "application/json": { + "schema": { + "type": "object" + } + } + } + } + } + } + }, + "/application/mappings": { + "get": { + "tags": [ + "Actuator" + ], + "summary": "Actuator web endpoint 'mappings'", + "operationId": "mappings", + "responses": { + "200": { + "description": "OK", + "content": { + "application/vnd.spring-boot.actuator.v3+json": { + "schema": { + "type": "object" + } + }, + "application/vnd.spring-boot.actuator.v2+json": { + "schema": { + "type": "object" + } + }, + "application/json": { + "schema": { + "type": "object" + } + } + } + } + } + } + }, + "/application/loggers": { + "get": { + "tags": [ + "Actuator" + ], + "summary": "Actuator web endpoint 'loggers'", + "operationId": "loggers", + "responses": { + "200": { + "description": "OK", + "content": { + "application/vnd.spring-boot.actuator.v3+json": { + "schema": { + "type": "object" + } + }, + "application/vnd.spring-boot.actuator.v2+json": { + "schema": { + "type": "object" + } + }, + "application/json": { + "schema": { + "type": "object" + } + } + } + } + } + } + }, + "/application/info": { + "get": { + "tags": [ + "Actuator" + ], + "summary": "Actuator web endpoint 'info'", + "operationId": "info", + "responses": { + "200": { + "description": "OK", + "content": { + "application/vnd.spring-boot.actuator.v3+json": { + "schema": { + "type": "object" + } + }, + "application/vnd.spring-boot.actuator.v2+json": { + "schema": { + "type": "object" + } + }, + "application/json": { + "schema": { + "type": "object" + } + } + } + } + } + } + }, + "/application/heapdump": { + "get": { + "tags": [ + "Actuator" + ], + "summary": "Actuator web endpoint 'heapdump'", + "operationId": "heapdump", + "responses": { + "200": { + "description": "OK", + "content": { + "application/octet-stream": { + "schema": { + "type": "object" + } + } + } + } + } + } + }, + "/application/health": { + "get": { + "tags": [ + "Actuator" + ], + "summary": "Actuator web endpoint 'health'", + "operationId": "health", + "responses": { + "200": { + "description": "OK", + "content": { + "application/vnd.spring-boot.actuator.v3+json": { + "schema": { + "type": "object" + } + }, + "application/vnd.spring-boot.actuator.v2+json": { + "schema": { + "type": "object" + } + }, + "application/json": { + "schema": { + "type": "object" + } + } + } + } + } + } + }, + "/application/env/{toMatch}": { "get": { "tags": [ - "hello-controller" + "Actuator" + ], + "summary": "Actuator web endpoint 'env-toMatch'", + "operationId": "env-toMatch", + "parameters": [ + { + "name": "toMatch", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } ], - "operationId": "persons", + "responses": { + "200": { + "description": "OK", + "content": { + "application/vnd.spring-boot.actuator.v3+json": { + "schema": { + "type": "object" + } + }, + "application/vnd.spring-boot.actuator.v2+json": { + "schema": { + "type": "object" + } + }, + "application/json": { + "schema": { + "type": "object" + } + } + } + } + } + } + }, + "/application/env": { + "get": { + "tags": [ + "Actuator" + ], + "summary": "Actuator web endpoint 'env'", + "operationId": "env", + "responses": { + "200": { + "description": "OK", + "content": { + "application/vnd.spring-boot.actuator.v3+json": { + "schema": { + "type": "object" + } + }, + "application/vnd.spring-boot.actuator.v2+json": { + "schema": { + "type": "object" + } + }, + "application/json": { + "schema": { + "type": "object" + } + } + } + } + } + } + }, + "/application/configprops/{prefix}": { + "get": { + "tags": [ + "Actuator" + ], + "summary": "Actuator web endpoint 'configprops-prefix'", + "operationId": "configprops-prefix", + "parameters": [ + { + "name": "prefix", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/vnd.spring-boot.actuator.v3+json": { + "schema": { + "type": "object" + } + }, + "application/vnd.spring-boot.actuator.v2+json": { + "schema": { + "type": "object" + } + }, + "application/json": { + "schema": { + "type": "object" + } + } + } + } + } + } + }, + "/application/configprops": { + "get": { + "tags": [ + "Actuator" + ], + "summary": "Actuator web endpoint 'configprops'", + "operationId": "configprops", + "responses": { + "200": { + "description": "OK", + "content": { + "application/vnd.spring-boot.actuator.v3+json": { + "schema": { + "type": "object" + } + }, + "application/vnd.spring-boot.actuator.v2+json": { + "schema": { + "type": "object" + } + }, + "application/json": { + "schema": { + "type": "object" + } + } + } + } + } + } + }, + "/application/conditions": { + "get": { + "tags": [ + "Actuator" + ], + "summary": "Actuator web endpoint 'conditions'", + "operationId": "conditions", + "responses": { + "200": { + "description": "OK", + "content": { + "application/vnd.spring-boot.actuator.v3+json": { + "schema": { + "type": "object" + } + }, + "application/vnd.spring-boot.actuator.v2+json": { + "schema": { + "type": "object" + } + }, + "application/json": { + "schema": { + "type": "object" + } + } + } + } + } + } + }, + "/application/caches/{cache}": { + "get": { + "tags": [ + "Actuator" + ], + "summary": "Actuator web endpoint 'caches-cache'", + "operationId": "caches-cache", + "parameters": [ + { + "name": "cache", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/vnd.spring-boot.actuator.v3+json": { + "schema": { + "type": "object" + } + }, + "application/vnd.spring-boot.actuator.v2+json": { + "schema": { + "type": "object" + } + }, + "application/json": { + "schema": { + "type": "object" + } + } + } + } + } + }, + "delete": { + "tags": [ + "Actuator" + ], + "summary": "Actuator web endpoint 'caches-cache'", + "operationId": "caches-cache_2", + "parameters": [ + { + "name": "cache", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/vnd.spring-boot.actuator.v3+json": { + "schema": { + "type": "object" + } + }, + "application/vnd.spring-boot.actuator.v2+json": { + "schema": { + "type": "object" + } + }, + "application/json": { + "schema": { + "type": "object" + } + } + } + } + } + } + }, + "/application/caches": { + "get": { + "tags": [ + "Actuator" + ], + "summary": "Actuator web endpoint 'caches'", + "operationId": "caches", + "responses": { + "200": { + "description": "OK", + "content": { + "application/vnd.spring-boot.actuator.v3+json": { + "schema": { + "type": "object" + } + }, + "application/vnd.spring-boot.actuator.v2+json": { + "schema": { + "type": "object" + } + }, + "application/json": { + "schema": { + "type": "object" + } + } + } + } + } + }, + "delete": { + "tags": [ + "Actuator" + ], + "summary": "Actuator web endpoint 'caches'", + "operationId": "caches_2", "responses": { "200": { "description": "OK", "content": { "*/*": { "schema": { - "type": "string" + "type": "object" + } + } + } + } + } + } + }, + "/application/beans": { + "get": { + "tags": [ + "Actuator" + ], + "summary": "Actuator web endpoint 'beans'", + "operationId": "beans", + "responses": { + "200": { + "description": "OK", + "content": { + "application/vnd.spring-boot.actuator.v3+json": { + "schema": { + "type": "object" + } + }, + "application/vnd.spring-boot.actuator.v2+json": { + "schema": { + "type": "object" + } + }, + "application/json": { + "schema": { + "type": "object" + } + } + } + } + } + } + }, + "/application": { + "get": { + "tags": [ + "Actuator" + ], + "summary": "Actuator root web endpoint", + "operationId": "links", + "responses": { + "200": { + "description": "OK", + "content": { + "application/vnd.spring-boot.actuator.v3+json": { + "schema": { + "type": "object", + "additionalProperties": { + "type": "object", + "additionalProperties": { + "$ref": "#/components/schemas/Link" + } + } + } + }, + "application/vnd.spring-boot.actuator.v2+json": { + "schema": { + "type": "object", + "additionalProperties": { + "type": "object", + "additionalProperties": { + "$ref": "#/components/schemas/Link" + } + } + } + }, + "application/json": { + "schema": { + "type": "object", + "additionalProperties": { + "type": "object", + "additionalProperties": { + "$ref": "#/components/schemas/Link" + } + } } } } @@ -32,5 +841,19 @@ } } }, - "components": {} + "components": { + "schemas": { + "Link": { + "type": "object", + "properties": { + "href": { + "type": "string" + }, + "templated": { + "type": "boolean" + } + } + } + } + } } 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..4b706fa5c 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 @@ -176,6 +176,68 @@ } } }, + "/application/sbom/{id}": { + "get": { + "tags": [ + "Actuator" + ], + "summary": "Actuator web endpoint 'sbom-id'", + "operationId": "sbom-id", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/octet-stream": { + "schema": { + "type": "object" + } + } + } + } + } + } + }, + "/application/sbom": { + "get": { + "tags": [ + "Actuator" + ], + "summary": "Actuator web endpoint 'sbom'", + "operationId": "sbom", + "responses": { + "200": { + "description": "OK", + "content": { + "application/vnd.spring-boot.actuator.v3+json": { + "schema": { + "type": "object" + } + }, + "application/vnd.spring-boot.actuator.v2+json": { + "schema": { + "type": "object" + } + }, + "application/json": { + "schema": { + "type": "object" + } + } + } + } + } + } + }, "/application/metrics/{requiredMetricName}": { "get": { "tags": [ @@ -217,6 +279,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..afc2239c1 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 @@ -176,6 +176,68 @@ } } }, + "/actuator/sbom/{id}": { + "get": { + "tags": [ + "Actuator" + ], + "summary": "Actuator web endpoint 'sbom-id'", + "operationId": "sbom-id", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/octet-stream": { + "schema": { + "type": "object" + } + } + } + } + } + } + }, + "/actuator/sbom": { + "get": { + "tags": [ + "Actuator" + ], + "summary": "Actuator web endpoint 'sbom'", + "operationId": "sbom", + "responses": { + "200": { + "description": "OK", + "content": { + "application/vnd.spring-boot.actuator.v3+json": { + "schema": { + "type": "object" + } + }, + "application/vnd.spring-boot.actuator.v2+json": { + "schema": { + "type": "object" + } + }, + "application/json": { + "schema": { + "type": "object" + } + } + } + } + } + } + }, "/actuator/metrics/{requiredMetricName}": { "get": { "tags": [ @@ -217,6 +279,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/pom.xml b/springdoc-openapi-tests/springdoc-openapi-actuator-webmvc-tests/pom.xml index 120f11ccd..62fe7665c 100644 --- a/springdoc-openapi-tests/springdoc-openapi-actuator-webmvc-tests/pom.xml +++ b/springdoc-openapi-tests/springdoc-openapi-actuator-webmvc-tests/pom.xml @@ -2,7 +2,7 @@ springdoc-openapi-tests org.springdoc - 2.5.0 + 2.6.0 4.0.0 diff --git a/springdoc-openapi-tests/springdoc-openapi-actuator-webmvc-tests/src/test/java/test/org/springdoc/api/AbstractSpringDocActuatorTest.java b/springdoc-openapi-tests/springdoc-openapi-actuator-webmvc-tests/src/test/java/test/org/springdoc/api/AbstractSpringDocActuatorTest.java index d76fc9525..4173589c8 100644 --- a/springdoc-openapi-tests/springdoc-openapi-actuator-webmvc-tests/src/test/java/test/org/springdoc/api/AbstractSpringDocActuatorTest.java +++ b/springdoc-openapi-tests/springdoc-openapi-actuator-webmvc-tests/src/test/java/test/org/springdoc/api/AbstractSpringDocActuatorTest.java @@ -25,6 +25,8 @@ package test.org.springdoc.api; import jakarta.annotation.PostConstruct; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.web.server.LocalManagementPort; @@ -32,9 +34,13 @@ import org.springframework.test.context.TestPropertySource; import org.springframework.web.client.RestTemplate; +import static org.skyscreamer.jsonassert.JSONAssert.assertEquals; + @TestPropertySource(properties = { "management.endpoints.enabled-by-default=true" }) public abstract class AbstractSpringDocActuatorTest extends AbstractCommonTest { + protected static final Logger LOGGER = LoggerFactory.getLogger(AbstractCommonTest.class); + protected RestTemplate actuatorRestTemplate; @LocalManagementPort @@ -48,4 +54,18 @@ void init() { actuatorRestTemplate = restTemplateBuilder .rootUri("http://localhost:" + this.managementPort).build(); } + + protected void testWithRestTemplate(String testId, String uri) throws Exception { + String result = null; + try { + result = actuatorRestTemplate.getForObject(uri, String.class); + String expected = getContent("results/app" + testId + ".json"); + assertEquals(expected, result, true); + } + catch (AssertionError e) { + LOGGER.error(result); + throw e; + } + } + } diff --git a/springdoc-openapi-tests/springdoc-openapi-actuator-webmvc-tests/src/test/java/test/org/springdoc/api/app148/SpringDocApp148Test.java b/springdoc-openapi-tests/springdoc-openapi-actuator-webmvc-tests/src/test/java/test/org/springdoc/api/app148/SpringDocApp148Test.java index 41241a573..3d9c4a593 100644 --- a/springdoc-openapi-tests/springdoc-openapi-actuator-webmvc-tests/src/test/java/test/org/springdoc/api/app148/SpringDocApp148Test.java +++ b/springdoc-openapi-tests/springdoc-openapi-actuator-webmvc-tests/src/test/java/test/org/springdoc/api/app148/SpringDocApp148Test.java @@ -30,7 +30,6 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; -import static org.skyscreamer.jsonassert.JSONAssert.assertEquals; @SpringBootTest(webEnvironment = WebEnvironment.DEFINED_PORT, @@ -48,16 +47,12 @@ public class SpringDocApp148Test extends AbstractSpringDocActuatorTest { @Test public void testApp() throws Exception { - String result = actuatorRestTemplate.getForObject("/test/application/openapi/users", String.class); - String expected = getContent("results/app148-1.json"); - assertEquals(expected, result, true); + super.testWithRestTemplate("148-1","/test/application/openapi/users"); } @Test public void testApp2() throws Exception { - String result = actuatorRestTemplate.getForObject("/test/application/openapi/x-actuator", String.class); - String expected = getContent("results/app148-2.json"); - assertEquals(expected, result, true); + super.testWithRestTemplate("148-2","/test/application/openapi/x-actuator"); } @Test diff --git a/springdoc-openapi-tests/springdoc-openapi-actuator-webmvc-tests/src/test/resources/application-test.properties b/springdoc-openapi-tests/springdoc-openapi-actuator-webmvc-tests/src/test/resources/application-test.properties index b88c99251..9d2df2929 100644 --- a/springdoc-openapi-tests/springdoc-openapi-actuator-webmvc-tests/src/test/resources/application-test.properties +++ b/springdoc-openapi-tests/springdoc-openapi-actuator-webmvc-tests/src/test/resources/application-test.properties @@ -1,3 +1,4 @@ spring.main.banner-mode=off logging.level.root=OFF +logging.pattern.console=%m%n spring.main.lazy-initialization=true \ No newline at end of file diff --git a/springdoc-openapi-tests/springdoc-openapi-actuator-webmvc-tests/src/test/resources/logback-test.xml b/springdoc-openapi-tests/springdoc-openapi-actuator-webmvc-tests/src/test/resources/logback-test.xml index 608cb302d..3fd46cfab 100644 --- a/springdoc-openapi-tests/springdoc-openapi-actuator-webmvc-tests/src/test/resources/logback-test.xml +++ b/springdoc-openapi-tests/springdoc-openapi-actuator-webmvc-tests/src/test/resources/logback-test.xml @@ -1,4 +1,5 @@ - + + \ No newline at end of file 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..4483a9f2c 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 @@ -225,6 +225,68 @@ } } }, + "/application/sbom": { + "get": { + "tags": [ + "Actuator" + ], + "summary": "Actuator web endpoint 'sbom'", + "operationId": "sbom", + "responses": { + "200": { + "description": "OK", + "content": { + "application/vnd.spring-boot.actuator.v3+json": { + "schema": { + "type": "object" + } + }, + "application/vnd.spring-boot.actuator.v2+json": { + "schema": { + "type": "object" + } + }, + "application/json": { + "schema": { + "type": "object" + } + } + } + } + } + } + }, + "/application/sbom/{id}": { + "get": { + "tags": [ + "Actuator" + ], + "summary": "Actuator web endpoint 'sbom-id'", + "operationId": "sbom-id", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/octet-stream": { + "schema": { + "type": "object" + } + } + } + } + } + } + }, "/application/metrics": { "get": { "tags": [ @@ -297,6 +359,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..69726213f 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 @@ -225,6 +225,68 @@ } } }, + "/application/sbom": { + "get": { + "tags": [ + "Actuator" + ], + "summary": "Actuator web endpoint 'sbom'", + "operationId": "sbom", + "responses": { + "200": { + "description": "OK", + "content": { + "application/vnd.spring-boot.actuator.v3+json": { + "schema": { + "type": "object" + } + }, + "application/vnd.spring-boot.actuator.v2+json": { + "schema": { + "type": "object" + } + }, + "application/json": { + "schema": { + "type": "object" + } + } + } + } + } + } + }, + "/application/sbom/{id}": { + "get": { + "tags": [ + "Actuator" + ], + "summary": "Actuator web endpoint 'sbom-id'", + "operationId": "sbom-id", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/octet-stream": { + "schema": { + "type": "object" + } + } + } + } + } + } + }, "/application/metrics": { "get": { "tags": [ @@ -297,6 +359,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..7ecd73866 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 @@ -225,6 +225,68 @@ } } }, + "/actuator/sbom": { + "get": { + "tags": [ + "Actuator" + ], + "summary": "Actuator web endpoint 'sbom'", + "operationId": "sbom", + "responses": { + "200": { + "description": "OK", + "content": { + "application/vnd.spring-boot.actuator.v3+json": { + "schema": { + "type": "object" + } + }, + "application/vnd.spring-boot.actuator.v2+json": { + "schema": { + "type": "object" + } + }, + "application/json": { + "schema": { + "type": "object" + } + } + } + } + } + } + }, + "/actuator/sbom/{id}": { + "get": { + "tags": [ + "Actuator" + ], + "summary": "Actuator web endpoint 'sbom-id'", + "operationId": "sbom-id", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/octet-stream": { + "schema": { + "type": "object" + } + } + } + } + } + } + }, "/actuator/metrics": { "get": { "tags": [ @@ -297,6 +359,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": [ @@ -825,4 +949,4 @@ } } } -} \ No newline at end of file +} diff --git a/springdoc-openapi-tests/springdoc-openapi-data-rest-tests/pom.xml b/springdoc-openapi-tests/springdoc-openapi-data-rest-tests/pom.xml index 00f4a96f2..6d65af1dc 100644 --- a/springdoc-openapi-tests/springdoc-openapi-data-rest-tests/pom.xml +++ b/springdoc-openapi-tests/springdoc-openapi-data-rest-tests/pom.xml @@ -2,7 +2,7 @@ springdoc-openapi-tests org.springdoc - 2.5.0 + 2.6.0 4.0.0 springdoc-openapi-data-rest-tests diff --git a/springdoc-openapi-tests/springdoc-openapi-function-webflux-tests/pom.xml b/springdoc-openapi-tests/springdoc-openapi-function-webflux-tests/pom.xml index 07d7babc8..059a207db 100644 --- a/springdoc-openapi-tests/springdoc-openapi-function-webflux-tests/pom.xml +++ b/springdoc-openapi-tests/springdoc-openapi-function-webflux-tests/pom.xml @@ -2,7 +2,7 @@ springdoc-openapi-tests org.springdoc - 2.5.0 + 2.6.0 4.0.0 diff --git a/springdoc-openapi-tests/springdoc-openapi-function-webflux-tests/src/test/java/test/org/springdoc/api/AbstractSpringDocFunctionTest.java b/springdoc-openapi-tests/springdoc-openapi-function-webflux-tests/src/test/java/test/org/springdoc/api/AbstractSpringDocFunctionTest.java index 9b5b08e9e..78e2db9f8 100644 --- a/springdoc-openapi-tests/springdoc-openapi-function-webflux-tests/src/test/java/test/org/springdoc/api/AbstractSpringDocFunctionTest.java +++ b/springdoc-openapi-tests/springdoc-openapi-function-webflux-tests/src/test/java/test/org/springdoc/api/AbstractSpringDocFunctionTest.java @@ -24,8 +24,6 @@ import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient; import org.springframework.cloud.function.context.test.FunctionalSpringBootTest; import org.springframework.test.web.reactive.server.EntityExchangeResult; -import org.springframework.web.reactive.function.server.HandlerFunction; -import org.springframework.web.reactive.function.server.ServerResponse; import static org.skyscreamer.jsonassert.JSONAssert.assertEquals; @@ -34,13 +32,11 @@ @AutoConfigureWebTestClient(timeout = "3600000") public abstract class AbstractSpringDocFunctionTest extends AbstractCommonTest { - public static final HandlerFunction HANDLER_FUNCTION = request -> ServerResponse.ok().build(); - protected String groupName = ""; @Test - public void testApp() throws Exception { + public void testApp() { String result = null; try { EntityExchangeResult getResult = webTestClient.get().uri(Constants.DEFAULT_API_DOCS_URL + groupName).exchange() diff --git a/springdoc-openapi-tests/springdoc-openapi-function-webflux-tests/src/test/resources/logback-test.xml b/springdoc-openapi-tests/springdoc-openapi-function-webflux-tests/src/test/resources/logback-test.xml index 24cd4646e..3fd46cfab 100644 --- a/springdoc-openapi-tests/springdoc-openapi-function-webflux-tests/src/test/resources/logback-test.xml +++ b/springdoc-openapi-tests/springdoc-openapi-function-webflux-tests/src/test/resources/logback-test.xml @@ -1,6 +1,5 @@ - \ No newline at end of file diff --git a/springdoc-openapi-tests/springdoc-openapi-function-webmvc-tests/pom.xml b/springdoc-openapi-tests/springdoc-openapi-function-webmvc-tests/pom.xml index 5e46d7d54..e3b01e3ba 100644 --- a/springdoc-openapi-tests/springdoc-openapi-function-webmvc-tests/pom.xml +++ b/springdoc-openapi-tests/springdoc-openapi-function-webmvc-tests/pom.xml @@ -2,7 +2,7 @@ springdoc-openapi-tests org.springdoc - 2.5.0 + 2.6.0 4.0.0 diff --git a/springdoc-openapi-tests/springdoc-openapi-groovy-tests/pom.xml b/springdoc-openapi-tests/springdoc-openapi-groovy-tests/pom.xml index d89bef434..f37fe303c 100644 --- a/springdoc-openapi-tests/springdoc-openapi-groovy-tests/pom.xml +++ b/springdoc-openapi-tests/springdoc-openapi-groovy-tests/pom.xml @@ -3,7 +3,7 @@ org.springdoc springdoc-openapi-tests - 2.5.0 + 2.6.0 springdoc-openapi-groovy-tests diff --git a/springdoc-openapi-tests/springdoc-openapi-groovy-tests/src/test/resources/results/app1.json b/springdoc-openapi-tests/springdoc-openapi-groovy-tests/src/test/resources/results/app1.json index 802d051cf..a7376fb6b 100644 --- a/springdoc-openapi-tests/springdoc-openapi-groovy-tests/src/test/resources/results/app1.json +++ b/springdoc-openapi-tests/springdoc-openapi-groovy-tests/src/test/resources/results/app1.json @@ -151,13 +151,13 @@ "instance": { "$ref": "#/components/schemas/AnnotatedNode" }, + "groovydoc": { + "$ref": "#/components/schemas/Groovydoc" + }, "hasNoRealSourcePosition": { "type": "boolean", "writeOnly": true }, - "groovydoc": { - "$ref": "#/components/schemas/Groovydoc" - }, "text": { "type": "string" }, @@ -424,8 +424,9 @@ "void": { "type": "boolean" }, - "typeDescription": { - "type": "string" + "superClassDistance": { + "type": "integer", + "format": "int32" }, "newMetaMethods": { "type": "array", @@ -436,9 +437,8 @@ "callSiteLoader": { "$ref": "#/components/schemas/CallSiteClassLoader" }, - "superClassDistance": { - "type": "integer", - "format": "int32" + "typeDescription": { + "type": "string" } } }, @@ -460,20 +460,6 @@ "items": { "type": "object", "properties": { - "name": { - "type": "string" - }, - "bounds": { - "type": "array", - "items": { - "type": "object", - "properties": { - "typeName": { - "type": "string" - } - } - } - }, "genericDeclaration": { "type": "object", "properties": { @@ -694,20 +680,6 @@ "items": { "type": "object", "properties": { - "name": { - "type": "string" - }, - "bounds": { - "type": "array", - "items": { - "type": "object", - "properties": { - "typeName": { - "type": "string" - } - } - } - }, "genericDeclaration": { "type": "object" }, @@ -739,6 +711,20 @@ } } }, + "name": { + "type": "string" + }, + "bounds": { + "type": "array", + "items": { + "type": "object", + "properties": { + "typeName": { + "type": "string" + } + } + } + }, "typeName": { "type": "string" }, @@ -992,6 +978,20 @@ } } }, + "name": { + "type": "string" + }, + "bounds": { + "type": "array", + "items": { + "type": "object", + "properties": { + "typeName": { + "type": "string" + } + } + } + }, "typeName": { "type": "string" }, @@ -1220,20 +1220,6 @@ "items": { "type": "object", "properties": { - "name": { - "type": "string" - }, - "bounds": { - "type": "array", - "items": { - "type": "object", - "properties": { - "typeName": { - "type": "string" - } - } - } - }, "genericDeclaration": { "type": "object" }, @@ -1265,6 +1251,20 @@ } } }, + "name": { + "type": "string" + }, + "bounds": { + "type": "array", + "items": { + "type": "object", + "properties": { + "typeName": { + "type": "string" + } + } + } + }, "typeName": { "type": "string" }, @@ -1614,20 +1614,6 @@ "items": { "type": "object", "properties": { - "name": { - "type": "string" - }, - "bounds": { - "type": "array", - "items": { - "type": "object", - "properties": { - "typeName": { - "type": "string" - } - } - } - }, "annotatedBounds": { "type": "array", "items": { @@ -1656,6 +1642,20 @@ } } }, + "name": { + "type": "string" + }, + "bounds": { + "type": "array", + "items": { + "type": "object", + "properties": { + "typeName": { + "type": "string" + } + } + } + }, "typeName": { "type": "string" }, @@ -1876,20 +1876,6 @@ "items": { "type": "object", "properties": { - "name": { - "type": "string" - }, - "bounds": { - "type": "array", - "items": { - "type": "object", - "properties": { - "typeName": { - "type": "string" - } - } - } - }, "genericDeclaration": { "type": "object" }, @@ -1921,6 +1907,20 @@ } } }, + "name": { + "type": "string" + }, + "bounds": { + "type": "array", + "items": { + "type": "object", + "properties": { + "typeName": { + "type": "string" + } + } + } + }, "typeName": { "type": "string" }, @@ -3114,21 +3114,34 @@ "text": { "type": "string" }, - "outerClass": { - "$ref": "#/components/schemas/ClassNode" + "allDeclaredMethods": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MethodNode" + } }, - "genericsPlaceHolder": { - "type": "boolean" + "allInterfaces": { + "uniqueItems": true, + "type": "array", + "items": { + "$ref": "#/components/schemas/ClassNode" + } }, - "unresolvedSuperClass": { - "$ref": "#/components/schemas/ClassNode" + "abstractMethods": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MethodNode" + } }, - "plainNodeReference": { + "outerClass": { "$ref": "#/components/schemas/ClassNode" }, "redirectNode": { "type": "boolean" }, + "genericsPlaceHolder": { + "type": "boolean" + }, "primaryClassNode": { "type": "boolean" }, @@ -3150,6 +3163,9 @@ "derivedFromGroovyObject": { "type": "boolean" }, + "unresolvedSuperClass": { + "$ref": "#/components/schemas/ClassNode" + }, "unresolvedInterfaces": { "type": "array", "items": { @@ -3169,24 +3185,8 @@ "annotationDefinition": { "type": "boolean" }, - "abstractMethods": { - "type": "array", - "items": { - "$ref": "#/components/schemas/MethodNode" - } - }, - "allInterfaces": { - "uniqueItems": true, - "type": "array", - "items": { - "$ref": "#/components/schemas/ClassNode" - } - }, - "allDeclaredMethods": { - "type": "array", - "items": { - "$ref": "#/components/schemas/MethodNode" - } + "plainNodeReference": { + "$ref": "#/components/schemas/ClassNode" }, "objectInitializerStatements": { "type": "array", @@ -3197,13 +3197,13 @@ "instance": { "$ref": "#/components/schemas/AnnotatedNode" }, + "groovydoc": { + "$ref": "#/components/schemas/Groovydoc" + }, "hasNoRealSourcePosition": { "type": "boolean", "writeOnly": true }, - "groovydoc": { - "$ref": "#/components/schemas/Groovydoc" - }, "sourcePosition": { "$ref": "#/components/schemas/ASTNode" }, @@ -3256,21 +3256,15 @@ "type": "string" }, "encoded": { - "type": "array", - "items": { - "type": "string", - "format": "byte" - } + "type": "string", + "format": "byte" }, "publicKey": { "type": "object", "properties": { "encoded": { - "type": "array", - "items": { - "type": "string", - "format": "byte" - } + "type": "string", + "format": "byte" }, "format": { "type": "string" @@ -3291,6 +3285,12 @@ "signerCertPath": { "type": "object", "properties": { + "type": { + "type": "string" + }, + "encodings": { + "type": "object" + }, "certificates": { "type": "array", "items": { @@ -3300,21 +3300,15 @@ "type": "string" }, "encoded": { - "type": "array", - "items": { - "type": "string", - "format": "byte" - } + "type": "string", + "format": "byte" }, "publicKey": { "type": "object", "properties": { "encoded": { - "type": "array", - "items": { - "type": "string", - "format": "byte" - } + "type": "string", + "format": "byte" }, "format": { "type": "string" @@ -3327,18 +3321,9 @@ } } }, - "type": { - "type": "string" - }, "encoded": { - "type": "array", - "items": { - "type": "string", - "format": "byte" - } - }, - "encodings": { - "type": "object" + "type": "string", + "format": "byte" } } }, @@ -3352,6 +3337,12 @@ "signerCertPath": { "type": "object", "properties": { + "type": { + "type": "string" + }, + "encodings": { + "type": "object" + }, "certificates": { "type": "array", "items": { @@ -3361,21 +3352,15 @@ "type": "string" }, "encoded": { - "type": "array", - "items": { - "type": "string", - "format": "byte" - } + "type": "string", + "format": "byte" }, "publicKey": { "type": "object", "properties": { "encoded": { - "type": "array", - "items": { - "type": "string", - "format": "byte" - } + "type": "string", + "format": "byte" }, "format": { "type": "string" @@ -3388,18 +3373,9 @@ } } }, - "type": { - "type": "string" - }, "encoded": { - "type": "array", - "items": { - "type": "string", - "format": "byte" - } - }, - "encodings": { - "type": "object" + "type": "string", + "format": "byte" } } } @@ -3689,10 +3665,6 @@ "text": { "type": "string" }, - "annotationDefault": { - "type": "boolean", - "writeOnly": true - }, "voidMethod": { "type": "boolean" }, @@ -3708,16 +3680,20 @@ "staticConstructor": { "type": "boolean" }, + "annotationDefault": { + "type": "boolean", + "writeOnly": true + }, "instance": { "$ref": "#/components/schemas/AnnotatedNode" }, + "groovydoc": { + "$ref": "#/components/schemas/Groovydoc" + }, "hasNoRealSourcePosition": { "type": "boolean", "writeOnly": true }, - "groovydoc": { - "$ref": "#/components/schemas/Groovydoc" - }, "sourcePosition": { "$ref": "#/components/schemas/ASTNode" }, @@ -3747,16 +3723,16 @@ "configuration": { "$ref": "#/components/schemas/CompilerConfiguration" }, + "errorCount": { + "type": "integer", + "format": "int32" + }, "warningCount": { "type": "integer", "format": "int32" }, "lastError": { "$ref": "#/components/schemas/Message" - }, - "errorCount": { - "type": "integer", - "format": "int32" } } }, @@ -3816,6 +3792,9 @@ "type": "integer", "format": "int32" }, + "classInfo": { + "$ref": "#/components/schemas/ClassInfo" + }, "groovyObject": { "type": "boolean" }, @@ -3864,20 +3843,6 @@ "items": { "type": "object", "properties": { - "name": { - "type": "string" - }, - "bounds": { - "type": "array", - "items": { - "type": "object", - "properties": { - "typeName": { - "type": "string" - } - } - } - }, "annotatedBounds": { "type": "array", "items": { @@ -3906,6 +3871,20 @@ } } }, + "name": { + "type": "string" + }, + "bounds": { + "type": "array", + "items": { + "type": "object", + "properties": { + "typeName": { + "type": "string" + } + } + } + }, "typeName": { "type": "string" }, @@ -4126,20 +4105,6 @@ "items": { "type": "object", "properties": { - "name": { - "type": "string" - }, - "bounds": { - "type": "array", - "items": { - "type": "object", - "properties": { - "typeName": { - "type": "string" - } - } - } - }, "genericDeclaration": { "type": "object" }, @@ -4171,6 +4136,20 @@ } } }, + "name": { + "type": "string" + }, + "bounds": { + "type": "array", + "items": { + "type": "object", + "properties": { + "typeName": { + "type": "string" + } + } + } + }, "typeName": { "type": "string" }, @@ -4436,20 +4415,6 @@ "items": { "type": "object", "properties": { - "name": { - "type": "string" - }, - "bounds": { - "type": "array", - "items": { - "type": "object", - "properties": { - "typeName": { - "type": "string" - } - } - } - }, "annotatedBounds": { "type": "array", "items": { @@ -4478,6 +4443,20 @@ } } }, + "name": { + "type": "string" + }, + "bounds": { + "type": "array", + "items": { + "type": "object", + "properties": { + "typeName": { + "type": "string" + } + } + } + }, "typeName": { "type": "string" }, @@ -4698,20 +4677,6 @@ "items": { "type": "object", "properties": { - "name": { - "type": "string" - }, - "bounds": { - "type": "array", - "items": { - "type": "object", - "properties": { - "typeName": { - "type": "string" - } - } - } - }, "genericDeclaration": { "type": "object" }, @@ -4743,6 +4708,20 @@ } } }, + "name": { + "type": "string" + }, + "bounds": { + "type": "array", + "items": { + "type": "object", + "properties": { + "typeName": { + "type": "string" + } + } + } + }, "typeName": { "type": "string" }, @@ -4995,9 +4974,6 @@ } } } - }, - "classInfo": { - "$ref": "#/components/schemas/ClassInfo" } } }, @@ -5038,13 +5014,13 @@ "instance": { "$ref": "#/components/schemas/AnnotatedNode" }, + "groovydoc": { + "$ref": "#/components/schemas/Groovydoc" + }, "hasNoRealSourcePosition": { "type": "boolean", "writeOnly": true }, - "groovydoc": { - "$ref": "#/components/schemas/Groovydoc" - }, "text": { "type": "string" }, @@ -5155,13 +5131,13 @@ "instance": { "$ref": "#/components/schemas/AnnotatedNode" }, + "groovydoc": { + "$ref": "#/components/schemas/Groovydoc" + }, "hasNoRealSourcePosition": { "type": "boolean", "writeOnly": true }, - "groovydoc": { - "$ref": "#/components/schemas/Groovydoc" - }, "text": { "type": "string" }, @@ -5688,13 +5664,13 @@ "instance": { "$ref": "#/components/schemas/AnnotatedNode" }, + "groovydoc": { + "$ref": "#/components/schemas/Groovydoc" + }, "hasNoRealSourcePosition": { "type": "boolean", "writeOnly": true }, - "groovydoc": { - "$ref": "#/components/schemas/Groovydoc" - }, "sourcePosition": { "$ref": "#/components/schemas/ASTNode" }, @@ -5891,18 +5867,31 @@ "text": { "type": "string" }, - "genericsPlaceHolder": { - "type": "boolean" + "allDeclaredMethods": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MethodNode" + } }, - "unresolvedSuperClass": { - "$ref": "#/components/schemas/ClassNode" + "allInterfaces": { + "uniqueItems": true, + "type": "array", + "items": { + "$ref": "#/components/schemas/ClassNode" + } }, - "plainNodeReference": { - "$ref": "#/components/schemas/ClassNode" + "abstractMethods": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MethodNode" + } }, "redirectNode": { "type": "boolean" }, + "genericsPlaceHolder": { + "type": "boolean" + }, "primaryClassNode": { "type": "boolean" }, @@ -5924,6 +5913,9 @@ "derivedFromGroovyObject": { "type": "boolean" }, + "unresolvedSuperClass": { + "$ref": "#/components/schemas/ClassNode" + }, "unresolvedInterfaces": { "type": "array", "items": { @@ -5943,24 +5935,8 @@ "annotationDefinition": { "type": "boolean" }, - "abstractMethods": { - "type": "array", - "items": { - "$ref": "#/components/schemas/MethodNode" - } - }, - "allInterfaces": { - "uniqueItems": true, - "type": "array", - "items": { - "$ref": "#/components/schemas/ClassNode" - } - }, - "allDeclaredMethods": { - "type": "array", - "items": { - "$ref": "#/components/schemas/MethodNode" - } + "plainNodeReference": { + "$ref": "#/components/schemas/ClassNode" }, "objectInitializerStatements": { "type": "array", @@ -5971,13 +5947,13 @@ "instance": { "$ref": "#/components/schemas/AnnotatedNode" }, + "groovydoc": { + "$ref": "#/components/schemas/Groovydoc" + }, "hasNoRealSourcePosition": { "type": "boolean", "writeOnly": true }, - "groovydoc": { - "$ref": "#/components/schemas/Groovydoc" - }, "sourcePosition": { "$ref": "#/components/schemas/ASTNode" }, @@ -6226,10 +6202,6 @@ "text": { "type": "string" }, - "annotationDefault": { - "type": "boolean", - "writeOnly": true - }, "voidMethod": { "type": "boolean" }, @@ -6245,16 +6217,20 @@ "staticConstructor": { "type": "boolean" }, + "annotationDefault": { + "type": "boolean", + "writeOnly": true + }, "instance": { "$ref": "#/components/schemas/AnnotatedNode" }, + "groovydoc": { + "$ref": "#/components/schemas/Groovydoc" + }, "hasNoRealSourcePosition": { "type": "boolean", "writeOnly": true }, - "groovydoc": { - "$ref": "#/components/schemas/Groovydoc" - }, "sourcePosition": { "$ref": "#/components/schemas/ASTNode" }, @@ -6439,21 +6415,34 @@ "text": { "type": "string" }, - "outerClass": { - "$ref": "#/components/schemas/ClassNode" + "allDeclaredMethods": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MethodNode" + } }, - "genericsPlaceHolder": { - "type": "boolean" + "allInterfaces": { + "uniqueItems": true, + "type": "array", + "items": { + "$ref": "#/components/schemas/ClassNode" + } }, - "unresolvedSuperClass": { - "$ref": "#/components/schemas/ClassNode" + "abstractMethods": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MethodNode" + } }, - "plainNodeReference": { + "outerClass": { "$ref": "#/components/schemas/ClassNode" }, "redirectNode": { "type": "boolean" }, + "genericsPlaceHolder": { + "type": "boolean" + }, "primaryClassNode": { "type": "boolean" }, @@ -6475,6 +6464,9 @@ "derivedFromGroovyObject": { "type": "boolean" }, + "unresolvedSuperClass": { + "$ref": "#/components/schemas/ClassNode" + }, "unresolvedInterfaces": { "type": "array", "items": { @@ -6494,24 +6486,8 @@ "annotationDefinition": { "type": "boolean" }, - "abstractMethods": { - "type": "array", - "items": { - "$ref": "#/components/schemas/MethodNode" - } - }, - "allInterfaces": { - "uniqueItems": true, - "type": "array", - "items": { - "$ref": "#/components/schemas/ClassNode" - } - }, - "allDeclaredMethods": { - "type": "array", - "items": { - "$ref": "#/components/schemas/MethodNode" - } + "plainNodeReference": { + "$ref": "#/components/schemas/ClassNode" }, "objectInitializerStatements": { "type": "array", @@ -6522,13 +6498,13 @@ "instance": { "$ref": "#/components/schemas/AnnotatedNode" }, + "groovydoc": { + "$ref": "#/components/schemas/Groovydoc" + }, "hasNoRealSourcePosition": { "type": "boolean", "writeOnly": true }, - "groovydoc": { - "$ref": "#/components/schemas/Groovydoc" - }, "sourcePosition": { "$ref": "#/components/schemas/ASTNode" }, @@ -6617,12 +6593,12 @@ "statementBlock": { "$ref": "#/components/schemas/BlockStatement" }, - "empty": { - "type": "boolean" - }, "packageName": { "type": "string" }, + "empty": { + "type": "boolean" + }, "package": { "$ref": "#/components/schemas/PackageNode" }, @@ -6695,13 +6671,13 @@ "instance": { "$ref": "#/components/schemas/AnnotatedNode" }, + "groovydoc": { + "$ref": "#/components/schemas/Groovydoc" + }, "hasNoRealSourcePosition": { "type": "boolean", "writeOnly": true }, - "groovydoc": { - "$ref": "#/components/schemas/Groovydoc" - }, "sourcePosition": { "$ref": "#/components/schemas/ASTNode" }, @@ -6781,13 +6757,13 @@ "instance": { "$ref": "#/components/schemas/AnnotatedNode" }, + "groovydoc": { + "$ref": "#/components/schemas/Groovydoc" + }, "hasNoRealSourcePosition": { "type": "boolean", "writeOnly": true }, - "groovydoc": { - "$ref": "#/components/schemas/Groovydoc" - }, "text": { "type": "string" }, @@ -6869,6 +6845,9 @@ "type": "integer", "format": "int32" }, + "type": { + "$ref": "#/components/schemas/ClassNode" + }, "name": { "type": "string" }, @@ -6878,15 +6857,9 @@ "public": { "type": "boolean" }, - "type": { - "$ref": "#/components/schemas/ClassNode" - }, "private": { "type": "boolean" }, - "dynamicTyped": { - "type": "boolean" - }, "initialExpression": { "$ref": "#/components/schemas/Expression" }, @@ -6900,6 +6873,9 @@ "originType": { "$ref": "#/components/schemas/ClassNode" }, + "dynamicTyped": { + "type": "boolean" + }, "getterNameOrDefault": { "type": "string" }, @@ -6909,13 +6885,13 @@ "instance": { "$ref": "#/components/schemas/AnnotatedNode" }, + "groovydoc": { + "$ref": "#/components/schemas/Groovydoc" + }, "hasNoRealSourcePosition": { "type": "boolean", "writeOnly": true }, - "groovydoc": { - "$ref": "#/components/schemas/Groovydoc" - }, "text": { "type": "string" }, @@ -6933,12 +6909,12 @@ "ReaderSource": { "type": "object", "properties": { - "reader": { - "type": "object" - }, "uri": { "type": "string", "format": "uri" + }, + "reader": { + "type": "object" } } }, @@ -6988,13 +6964,13 @@ "instance": { "$ref": "#/components/schemas/AnnotatedNode" }, + "groovydoc": { + "$ref": "#/components/schemas/Groovydoc" + }, "hasNoRealSourcePosition": { "type": "boolean", "writeOnly": true }, - "groovydoc": { - "$ref": "#/components/schemas/Groovydoc" - }, "text": { "type": "string" }, @@ -7185,9 +7161,6 @@ "type": { "$ref": "#/components/schemas/ClassNode" }, - "dynamicTyped": { - "type": "boolean" - }, "initialExpression": { "$ref": "#/components/schemas/Expression" }, @@ -7199,6 +7172,9 @@ }, "originType": { "$ref": "#/components/schemas/ClassNode" + }, + "dynamicTyped": { + "type": "boolean" } } }, diff --git a/springdoc-openapi-tests/springdoc-openapi-hateoas-tests/pom.xml b/springdoc-openapi-tests/springdoc-openapi-hateoas-tests/pom.xml index fc5b3cb86..75a2200e1 100644 --- a/springdoc-openapi-tests/springdoc-openapi-hateoas-tests/pom.xml +++ b/springdoc-openapi-tests/springdoc-openapi-hateoas-tests/pom.xml @@ -2,7 +2,7 @@ springdoc-openapi-tests org.springdoc - 2.5.0 + 2.6.0 4.0.0 springdoc-openapi-hateoas-tests 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..00d143fbf --- /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 java.util.List; + +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; + +@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..4dba111c1 --- /dev/null +++ b/springdoc-openapi-tests/springdoc-openapi-hateoas-tests/src/test/java/test/org/springdoc/api/app10/SpringDocApp10DirectTest.java @@ -0,0 +1,55 @@ +/* + * + * * + * * * + * * * * 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 test.org.springdoc.api.AbstractSpringDocTest; + +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.data.web.config.EnableSpringDataWebSupport; + +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.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +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..91985d75e --- /dev/null +++ b/springdoc-openapi-tests/springdoc-openapi-hateoas-tests/src/test/java/test/org/springdoc/api/app10/SpringDocApp10NotSpecifiedTest.java @@ -0,0 +1,68 @@ +/* + * + * * + * * * + * * * * 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 java.util.Optional; + +import io.swagger.v3.core.converter.ModelConverter; +import io.swagger.v3.core.converter.ModelConverters; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.springdoc.core.converters.PageOpenAPIConverter; +import org.springdoc.core.utils.Constants; +import test.org.springdoc.api.AbstractSpringDocTest; + +import org.springframework.boot.autoconfigure.SpringBootApplication; + +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.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +public class SpringDocApp10NotSpecifiedTest extends AbstractSpringDocTest { + + + @BeforeAll + public static void init() { + Optional pageOpenAPIConverter = + ModelConverters.getInstance().getConverters() + .stream().filter(modelConverter -> modelConverter instanceof PageOpenAPIConverter).findAny(); + pageOpenAPIConverter.ifPresent(ModelConverters.getInstance()::removeConverter); + } + + @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..22e8cc2ea --- /dev/null +++ b/springdoc-openapi-tests/springdoc-openapi-hateoas-tests/src/test/java/test/org/springdoc/api/app10/SpringDocApp10ViaDtoTest.java @@ -0,0 +1,55 @@ +/* + * + * * + * * * + * * * * 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 test.org.springdoc.api.AbstractSpringDocTest; + +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.data.web.config.EnableSpringDataWebSupport; + +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.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +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/java/test/org/springdoc/api/app9/FooConfiguration.java b/springdoc-openapi-tests/springdoc-openapi-hateoas-tests/src/test/java/test/org/springdoc/api/app9/FooConfiguration.java index 11065e4b9..c5a21f1c1 100644 --- a/springdoc-openapi-tests/springdoc-openapi-hateoas-tests/src/test/java/test/org/springdoc/api/app9/FooConfiguration.java +++ b/springdoc-openapi-tests/springdoc-openapi-hateoas-tests/src/test/java/test/org/springdoc/api/app9/FooConfiguration.java @@ -1,5 +1,7 @@ package test.org.springdoc.api.app9; +import java.util.List; + import io.swagger.v3.oas.models.Components; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.info.Contact; @@ -8,12 +10,10 @@ import io.swagger.v3.oas.models.media.Schema; import io.swagger.v3.oas.models.security.SecurityScheme; import io.swagger.v3.oas.models.tags.Tag; -import org.springdoc.core.models.GroupedOpenApi; + import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import java.util.List; - @Configuration public class FooConfiguration { @Bean diff --git a/springdoc-openapi-tests/springdoc-openapi-hateoas-tests/src/test/java/test/org/springdoc/api/app9/SpringDocApp9Test.java b/springdoc-openapi-tests/springdoc-openapi-hateoas-tests/src/test/java/test/org/springdoc/api/app9/SpringDocApp9Test.java index ba02d6e00..2e4cab1b2 100644 --- a/springdoc-openapi-tests/springdoc-openapi-hateoas-tests/src/test/java/test/org/springdoc/api/app9/SpringDocApp9Test.java +++ b/springdoc-openapi-tests/springdoc-openapi-hateoas-tests/src/test/java/test/org/springdoc/api/app9/SpringDocApp9Test.java @@ -18,9 +18,10 @@ package test.org.springdoc.api.app9; -import org.springframework.boot.autoconfigure.SpringBootApplication; import test.org.springdoc.api.AbstractSpringDocTest; +import org.springframework.boot.autoconfigure.SpringBootApplication; + public class SpringDocApp9Test extends AbstractSpringDocTest { @SpringBootApplication diff --git a/springdoc-openapi-tests/springdoc-openapi-hateoas-tests/src/test/java/test/org/springdoc/api/app9/application/FooController.java b/springdoc-openapi-tests/springdoc-openapi-hateoas-tests/src/test/java/test/org/springdoc/api/app9/application/FooController.java index dd5864ae1..8600ded15 100644 --- a/springdoc-openapi-tests/springdoc-openapi-hateoas-tests/src/test/java/test/org/springdoc/api/app9/application/FooController.java +++ b/springdoc-openapi-tests/springdoc-openapi-hateoas-tests/src/test/java/test/org/springdoc/api/app9/application/FooController.java @@ -1,17 +1,22 @@ package test.org.springdoc.api.app9.application; +import java.util.List; +import java.util.UUID; + import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.web.bind.annotation.*; -import test.org.springdoc.api.app9.application.dto.ResponseData; import test.org.springdoc.api.app9.application.dto.FeedResponse; +import test.org.springdoc.api.app9.application.dto.ResponseData; -import java.util.List; -import java.util.UUID; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestController; @Tag(name = "ResponseDataController") @RestController @RequestMapping(value = "/some-route", produces = MediaType.APPLICATION_JSON_VALUE) diff --git a/springdoc-openapi-tests/springdoc-openapi-hateoas-tests/src/test/java/test/org/springdoc/api/app9/application/dto/FeedResponse.java b/springdoc-openapi-tests/springdoc-openapi-hateoas-tests/src/test/java/test/org/springdoc/api/app9/application/dto/FeedResponse.java index 9fc1e3e66..a9819e55d 100644 --- a/springdoc-openapi-tests/springdoc-openapi-hateoas-tests/src/test/java/test/org/springdoc/api/app9/application/dto/FeedResponse.java +++ b/springdoc-openapi-tests/springdoc-openapi-hateoas-tests/src/test/java/test/org/springdoc/api/app9/application/dto/FeedResponse.java @@ -1,15 +1,19 @@ package test.org.springdoc.api.app9.application.dto; +import java.util.List; +import java.util.UUID; + import com.fasterxml.jackson.annotation.JsonProperty; import jakarta.validation.constraints.NotNull; -import lombok.*; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.EqualsAndHashCode; +import lombok.Getter; import lombok.experimental.Accessors; -import org.springframework.hateoas.IanaLinkRelations; -import org.springframework.hateoas.RepresentationModel; import test.org.springdoc.api.app9.application.FooController; -import java.util.List; -import java.util.UUID; +import org.springframework.hateoas.IanaLinkRelations; +import org.springframework.hateoas.RepresentationModel; import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.linkTo; import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.methodOn; diff --git a/springdoc-openapi-tests/springdoc-openapi-hateoas-tests/src/test/java/test/org/springdoc/api/app9/application/dto/ResponseData.java b/springdoc-openapi-tests/springdoc-openapi-hateoas-tests/src/test/java/test/org/springdoc/api/app9/application/dto/ResponseData.java index 7d0860484..4481194c6 100644 --- a/springdoc-openapi-tests/springdoc-openapi-hateoas-tests/src/test/java/test/org/springdoc/api/app9/application/dto/ResponseData.java +++ b/springdoc-openapi-tests/springdoc-openapi-hateoas-tests/src/test/java/test/org/springdoc/api/app9/application/dto/ResponseData.java @@ -1,13 +1,13 @@ package test.org.springdoc.api.app9.application.dto; +import java.time.LocalDate; +import java.util.UUID; + import com.fasterxml.jackson.annotation.JsonProperty; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotNull; import lombok.Builder; -import java.time.LocalDate; -import java.util.UUID; - @Builder public record ResponseData( @JsonProperty(value = "DATA_ID", required = true) 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 diff --git a/springdoc-openapi-tests/springdoc-openapi-javadoc-tests/pom.xml b/springdoc-openapi-tests/springdoc-openapi-javadoc-tests/pom.xml index e3c0655f7..5499ce7d1 100644 --- a/springdoc-openapi-tests/springdoc-openapi-javadoc-tests/pom.xml +++ b/springdoc-openapi-tests/springdoc-openapi-javadoc-tests/pom.xml @@ -2,7 +2,7 @@ org.springdoc springdoc-openapi-tests - 2.5.0 + 2.6.0 4.0.0 diff --git a/springdoc-openapi-tests/springdoc-openapi-javadoc-tests/src/test/resources/results/app105-3.json b/springdoc-openapi-tests/springdoc-openapi-javadoc-tests/src/test/resources/results/app105-3.json index bccc4af1b..ff9c8392e 100644 --- a/springdoc-openapi-tests/springdoc-openapi-javadoc-tests/src/test/resources/results/app105-3.json +++ b/springdoc-openapi-tests/springdoc-openapi-javadoc-tests/src/test/resources/results/app105-3.json @@ -34,12 +34,12 @@ "requestBody": { "description": "Pet object that needs to be added to the store", "content": { - "application/json": { + "application/xml": { "schema": { "$ref": "#/components/schemas/Pet" } }, - "application/xml": { + "application/json": { "schema": { "$ref": "#/components/schemas/Pet" } @@ -87,12 +87,12 @@ "requestBody": { "description": "Pet object that needs to be added to the store", "content": { - "application/json": { + "application/xml": { "schema": { "$ref": "#/components/schemas/Pet" } }, - "application/xml": { + "application/json": { "schema": { "$ref": "#/components/schemas/Pet" } @@ -128,142 +128,6 @@ ] } }, - "/pet/findByStatus": { - "get": { - "tags": [ - "pet" - ], - "summary": "Finds Pets by status", - "description": "Multiple status values can be provided with comma separated strings", - "operationId": "findPetsByStatus", - "parameters": [ - { - "name": "status", - "in": "query", - "description": "Status values that need to be considered for filter", - "required": true, - "schema": { - "type": "array", - "items": { - "type": "string" - } - } - } - ], - "responses": { - "200": { - "description": "successful operation", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Pet" - } - } - }, - "application/xml": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Pet" - } - } - } - } - }, - "400": { - "description": "Invalid status value", - "content": { - "*/*": { - "schema": { - "type": "object", - "additionalProperties": { - "type": "object" - } - } - } - } - } - }, - "security": [ - { - "petstore_auth": [ - "write:pets", - "read:pets" - ] - } - ] - } - }, - "/pet/findByTags": { - "get": { - "tags": [ - "pet" - ], - "summary": "Finds Pets by tags", - "description": "Muliple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.", - "operationId": "findPetsByTags", - "parameters": [ - { - "name": "tags", - "in": "query", - "description": "Tags to filter by", - "required": true, - "schema": { - "type": "array", - "items": { - "type": "string" - } - } - } - ], - "responses": { - "200": { - "description": "successful operation", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Pet" - } - } - }, - "application/xml": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Pet" - } - } - } - } - }, - "400": { - "description": "Invalid tag value", - "content": { - "*/*": { - "schema": { - "type": "object", - "additionalProperties": { - "type": "object" - } - } - } - } - } - }, - "security": [ - { - "petstore_auth": [ - "write:pets", - "read:pets" - ] - } - ] - } - }, "/pet/{petId}": { "get": { "tags": [ @@ -285,21 +149,6 @@ } ], "responses": { - "200": { - "description": "successful operation", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Pet" - } - }, - "application/xml": { - "schema": { - "$ref": "#/components/schemas/Pet" - } - } - } - }, "400": { "description": "Invalid ID supplied", "content": { @@ -316,15 +165,30 @@ "404": { "description": "Pet not found", "content": { - "application/json": { + "application/xml": { "schema": { "$ref": "#/components/schemas/Pet" } }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/Pet" + } + } + } + }, + "200": { + "description": "successful operation", + "content": { "application/xml": { "schema": { "$ref": "#/components/schemas/Pet" } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/Pet" + } } } } @@ -504,6 +368,19 @@ } }, "responses": { + "400": { + "description": "the map", + "content": { + "*/*": { + "schema": { + "type": "object", + "additionalProperties": { + "type": "object" + } + } + } + } + }, "200": { "description": "successful operation", "content": { @@ -513,9 +390,111 @@ } } } + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/pet/findByTags": { + "get": { + "tags": [ + "pet" + ], + "summary": "Finds Pets by tags", + "description": "Muliple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.", + "operationId": "findPetsByTags", + "parameters": [ + { + "name": "tags", + "in": "query", + "description": "Tags to filter by", + "required": true, + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + } + ], + "responses": { + "400": { + "description": "Invalid tag value", + "content": { + "*/*": { + "schema": { + "type": "object", + "additionalProperties": { + "type": "object" + } + } + } + } }, + "200": { + "description": "successful operation", + "content": { + "application/xml": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Pet" + } + } + }, + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Pet" + } + } + } + } + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/pet/findByStatus": { + "get": { + "tags": [ + "pet" + ], + "summary": "Finds Pets by status", + "description": "Multiple status values can be provided with comma separated strings", + "operationId": "findPetsByStatus", + "parameters": [ + { + "name": "status", + "in": "query", + "description": "Status values that need to be considered for filter", + "required": true, + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + } + ], + "responses": { "400": { - "description": "the map", + "description": "Invalid status value", "content": { "*/*": { "schema": { @@ -526,6 +505,27 @@ } } } + }, + "200": { + "description": "successful operation", + "content": { + "application/xml": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Pet" + } + } + }, + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Pet" + } + } + } + } } }, "security": [ @@ -553,26 +553,8 @@ "type": "string", "description": "The Name." } - } - }, - "ModelApiResponse": { - "type": "object", - "properties": { - "code": { - "type": "integer", - "description": "The Code.", - "format": "int32" - }, - "message": { - "type": "string", - "description": "The Message." - }, - "type": { - "type": "string", - "description": "The Type." - } }, - "description": "The type Model api response." + "description": "The type Category." }, "Pet": { "required": [ @@ -581,14 +563,14 @@ ], "type": "object", "properties": { - "category": { - "$ref": "#/components/schemas/Category" - }, "id": { "type": "integer", "description": "The Id.", "format": "int64" }, + "category": { + "$ref": "#/components/schemas/Category" + }, "name": { "type": "string", "description": "The Name.", @@ -601,6 +583,13 @@ "type": "string" } }, + "tags": { + "type": "array", + "description": "The Tags.", + "items": { + "$ref": "#/components/schemas/Tag" + } + }, "status": { "type": "string", "description": "pet status in the store", @@ -609,13 +598,6 @@ "pending", "sold" ] - }, - "tags": { - "type": "array", - "description": "The Tags.", - "items": { - "$ref": "#/components/schemas/Tag" - } } }, "description": "The type Pet." @@ -632,7 +614,27 @@ "type": "string", "description": "The Name." } - } + }, + "description": "The type Tag." + }, + "ModelApiResponse": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "description": "The Code.", + "format": "int32" + }, + "type": { + "type": "string", + "description": "The Type." + }, + "message": { + "type": "string", + "description": "The Message." + } + }, + "description": "The type Model api response." } }, "securitySchemes": { @@ -646,8 +648,8 @@ "implicit": { "authorizationUrl": "http://petstore.swagger.io/oauth/dialog", "scopes": { - "read:pets": "read your pets", - "write:pets": "modify pets in your account" + "write:pets": "modify pets in your account", + "read:pets": "read your pets" } } } diff --git a/springdoc-openapi-tests/springdoc-openapi-javadoc-tests/src/test/resources/results/app2.json b/springdoc-openapi-tests/springdoc-openapi-javadoc-tests/src/test/resources/results/app2.json index a0c5620f9..3a0de8a56 100644 --- a/springdoc-openapi-tests/springdoc-openapi-javadoc-tests/src/test/resources/results/app2.json +++ b/springdoc-openapi-tests/springdoc-openapi-javadoc-tests/src/test/resources/results/app2.json @@ -193,12 +193,12 @@ "requestBody": { "description": "Pet object that needs to be added to the store", "content": { - "application/json": { + "application/xml": { "schema": { "$ref": "#/components/schemas/Pet" } }, - "application/xml": { + "application/json": { "schema": { "$ref": "#/components/schemas/Pet" } @@ -246,12 +246,12 @@ "requestBody": { "description": "Pet object that needs to be added to the store", "content": { - "application/json": { + "application/xml": { "schema": { "$ref": "#/components/schemas/Pet" } }, - "application/xml": { + "application/json": { "schema": { "$ref": "#/components/schemas/Pet" } @@ -1163,7 +1163,8 @@ "type": "string", "description": "The Name." } - } + }, + "description": "The type Category." }, "Pet": { "required": [ @@ -1223,7 +1224,8 @@ "type": "string", "description": "The Name." } - } + }, + "description": "The type Tag." }, "Order": { "type": "object", diff --git a/springdoc-openapi-tests/springdoc-openapi-javadoc-tests/src/test/resources/results/app38.json b/springdoc-openapi-tests/springdoc-openapi-javadoc-tests/src/test/resources/results/app38.json index b6127e78c..d1e0f9d27 100644 --- a/springdoc-openapi-tests/springdoc-openapi-javadoc-tests/src/test/resources/results/app38.json +++ b/springdoc-openapi-tests/springdoc-openapi-javadoc-tests/src/test/resources/results/app38.json @@ -31,11 +31,8 @@ "content": { "*/*": { "schema": { - "type": "array", - "items": { - "type": "string", - "format": "byte" - } + "type": "string", + "format": "byte" } } } @@ -45,4 +42,4 @@ } }, "components": {} -} \ No newline at end of file +} diff --git a/springdoc-openapi-tests/springdoc-openapi-kotlin-webflux-tests/pom.xml b/springdoc-openapi-tests/springdoc-openapi-kotlin-webflux-tests/pom.xml index 0b104114e..c16a98152 100644 --- a/springdoc-openapi-tests/springdoc-openapi-kotlin-webflux-tests/pom.xml +++ b/springdoc-openapi-tests/springdoc-openapi-kotlin-webflux-tests/pom.xml @@ -2,7 +2,7 @@ springdoc-openapi-tests org.springdoc - 2.5.0 + 2.6.0 4.0.0 springdoc-openapi-kotlin-webflux-tests diff --git a/springdoc-openapi-tests/springdoc-openapi-kotlin-webmvc-tests/pom.xml b/springdoc-openapi-tests/springdoc-openapi-kotlin-webmvc-tests/pom.xml index 58eb8b1e3..d1fc090ec 100644 --- a/springdoc-openapi-tests/springdoc-openapi-kotlin-webmvc-tests/pom.xml +++ b/springdoc-openapi-tests/springdoc-openapi-kotlin-webmvc-tests/pom.xml @@ -2,7 +2,7 @@ springdoc-openapi-tests org.springdoc - 2.5.0 + 2.6.0 4.0.0 springdoc-openapi-kotlin-webmvc-tests @@ -78,6 +78,36 @@ + + org.apache.maven.plugins + maven-compiler-plugin + + + + default-compile + none + + + + default-testCompile + none + + + java-compile + compile + + compile + + + + java-test-compile + test-compile + + testCompile + + + + \ No newline at end of file diff --git a/springdoc-openapi-tests/springdoc-openapi-kotlin-webmvc-tests/src/test/java/test/org/springdoc/api/app12/EnumController.java b/springdoc-openapi-tests/springdoc-openapi-kotlin-webmvc-tests/src/test/java/test/org/springdoc/api/app12/EnumController.java new file mode 100644 index 000000000..7bdd2ffe2 --- /dev/null +++ b/springdoc-openapi-tests/springdoc-openapi-kotlin-webmvc-tests/src/test/java/test/org/springdoc/api/app12/EnumController.java @@ -0,0 +1,18 @@ +package test.org.springdoc.api.app12; + +import jakarta.annotation.Nullable; +import test.org.springdoc.api.app12.SpringDocApp12Test.MyEnum; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author bnasslahsen + */ +@RestController +public class EnumController { + @GetMapping("/test-enum-2") + String testEnum2(@Nullable MyEnum e) { + return ""; + } +} \ No newline at end of file diff --git a/springdoc-openapi-tests/springdoc-openapi-kotlin-webmvc-tests/src/test/kotlin/test/org/springdoc/api/app12/SpringDocApp12Test.kt b/springdoc-openapi-tests/springdoc-openapi-kotlin-webmvc-tests/src/test/kotlin/test/org/springdoc/api/app12/SpringDocApp12Test.kt new file mode 100644 index 000000000..f522f7de3 --- /dev/null +++ b/springdoc-openapi-tests/springdoc-openapi-kotlin-webmvc-tests/src/test/kotlin/test/org/springdoc/api/app12/SpringDocApp12Test.kt @@ -0,0 +1,35 @@ +/* + * + * * Copyright 2019-2023 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.app12 + +import org.springframework.boot.autoconfigure.SpringBootApplication +import org.springframework.context.annotation.ComponentScan +import test.org.springdoc.api.AbstractKotlinSpringDocMVCTest + +class SpringDocApp12Test : AbstractKotlinSpringDocMVCTest() { + + @SpringBootApplication + @ComponentScan(basePackages = ["org.springdoc", "test.org.springdoc.api.app12"]) + class DemoApplication + + enum class MyEnum { + A, B; + } + +} diff --git a/springdoc-openapi-tests/springdoc-openapi-kotlin-webmvc-tests/src/test/resources/results/app12.json b/springdoc-openapi-tests/springdoc-openapi-kotlin-webmvc-tests/src/test/resources/results/app12.json new file mode 100644 index 000000000..407e5620a --- /dev/null +++ b/springdoc-openapi-tests/springdoc-openapi-kotlin-webmvc-tests/src/test/resources/results/app12.json @@ -0,0 +1,50 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "OpenAPI definition", + "version": "v0" + }, + "servers": [ + { + "url": "http://localhost", + "description": "Generated server url" + } + ], + "paths": { + "/test-enum-2": { + "get": { + "tags": [ + "enum-controller" + ], + "operationId": "testEnum2", + "parameters": [ + { + "name": "e", + "in": "query", + "required": false, + "schema": { + "type": "string", + "enum": [ + "A", + "B" + ] + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "type": "string" + } + } + } + } + } + } + } + }, + "components": {} +} diff --git a/springdoc-openapi-tests/springdoc-openapi-security-tests/pom.xml b/springdoc-openapi-tests/springdoc-openapi-security-tests/pom.xml index 9beae556a..ee932d0be 100644 --- a/springdoc-openapi-tests/springdoc-openapi-security-tests/pom.xml +++ b/springdoc-openapi-tests/springdoc-openapi-security-tests/pom.xml @@ -3,7 +3,7 @@ org.springdoc springdoc-openapi-tests - 2.5.0 + 2.6.0 springdoc-openapi-security-tests @@ -20,6 +20,11 @@ spring-security-config test + + org.springframework.security + spring-security-oauth2-client + test + jakarta.servlet jakarta.servlet-api @@ -31,16 +36,6 @@ ${project.version} test - - javax.xml - jaxb-impl - test - - - javax.jws - javax.jws-api - test - io.jsonwebtoken jjwt diff --git a/springdoc-openapi-tests/springdoc-openapi-security-tests/src/test/java/test/org/springdoc/api/app1/HelloController.java b/springdoc-openapi-tests/springdoc-openapi-security-tests/src/test/java/test/org/springdoc/api/app1/HelloController.java index 8dbff03ac..063133377 100644 --- a/springdoc-openapi-tests/springdoc-openapi-security-tests/src/test/java/test/org/springdoc/api/app1/HelloController.java +++ b/springdoc-openapi-tests/springdoc-openapi-security-tests/src/test/java/test/org/springdoc/api/app1/HelloController.java @@ -20,6 +20,8 @@ import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.security.core.userdetails.User; +import org.springframework.security.oauth2.client.OAuth2AuthorizedClient; +import org.springframework.security.oauth2.client.annotation.RegisteredOAuth2AuthorizedClient; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; @@ -38,4 +40,11 @@ public String personsWithUser(@RequestBody() Person person, return "OK"; } + @PostMapping(value = "/persons-with-oauth2-user") + public String personsWithUser(@RequestBody() Person person, + @RegisteredOAuth2AuthorizedClient OAuth2AuthorizedClient authorizedClient, + @AuthenticationPrincipal User user) { + return "OK"; + } + } \ No newline at end of file diff --git a/springdoc-openapi-tests/springdoc-openapi-security-tests/src/test/resources/results/app1.json b/springdoc-openapi-tests/springdoc-openapi-security-tests/src/test/resources/results/app1.json index 0b91f6ec3..0260adb6d 100644 --- a/springdoc-openapi-tests/springdoc-openapi-security-tests/src/test/resources/results/app1.json +++ b/springdoc-openapi-tests/springdoc-openapi-security-tests/src/test/resources/results/app1.json @@ -74,6 +74,36 @@ } } } + }, + "/persons-with-oauth2-user": { + "post": { + "tags": [ + "hello-controller" + ], + "operationId": "personsWithUser_1", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Person" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "type": "string" + } + } + } + } + } + } } }, "components": { diff --git a/springdoc-openapi-tests/springdoc-openapi-security-tests/src/test/resources/results/app10.json b/springdoc-openapi-tests/springdoc-openapi-security-tests/src/test/resources/results/app10.json index c53212833..9ca244228 100644 --- a/springdoc-openapi-tests/springdoc-openapi-security-tests/src/test/resources/results/app10.json +++ b/springdoc-openapi-tests/springdoc-openapi-security-tests/src/test/resources/results/app10.json @@ -56,6 +56,9 @@ } } }, + "500": { + "description": "Internal Server Error" + }, "400": { "description": "Bad Request", "content": { @@ -65,6 +68,25 @@ } } } + } + } + } + }, + "/.well-known/oauth-authorization-server": { + "get": { + "tags": [ + "authorization-server-endpoints" + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OAuth2AuthorizationServerMetadata" + } + } + } }, "500": { "description": "Internal Server Error" @@ -72,11 +94,22 @@ } } }, - "/.well-known/oauth-authorization-server": { + "/.well-known/oauth-authorization-server/{subpath}": { "get": { "tags": [ "authorization-server-endpoints" ], + "summary": "Valid when multiple issuers are allowed", + "parameters": [ + { + "name": "subpath", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], "responses": { "200": { "description": "OK", @@ -101,8 +134,11 @@ ], "parameters": [ { + "name": "Authorization", "in": "header", - "name": "Authorization" + "schema": { + "type": "string" + } } ], "requestBody": { @@ -165,6 +201,9 @@ } } }, + "500": { + "description": "Internal Server Error" + }, "400": { "description": "Bad Request", "content": { @@ -184,9 +223,6 @@ } } } - }, - "500": { - "description": "Internal Server Error" } } } @@ -215,15 +251,8 @@ "text/html": {} } }, - "302": { - "description": "Moved Temporarily", - "headers": { - "Location": { - "schema": { - "type": "string" - } - } - } + "500": { + "description": "Internal Server Error" }, "400": { "description": "Bad Request", @@ -235,8 +264,15 @@ } } }, - "500": { - "description": "Internal Server Error" + "302": { + "description": "Moved Temporarily", + "headers": { + "Location": { + "schema": { + "type": "string" + } + } + } } } } @@ -280,6 +316,9 @@ } } }, + "500": { + "description": "Internal Server Error" + }, "400": { "description": "Bad Request", "content": { @@ -289,9 +328,6 @@ } } } - }, - "500": { - "description": "Internal Server Error" } } } @@ -322,6 +358,9 @@ "200": { "description": "OK" }, + "500": { + "description": "Internal Server Error" + }, "400": { "description": "Bad Request", "content": { @@ -331,9 +370,6 @@ } } } - }, - "500": { - "description": "Internal Server Error" } } } @@ -358,9 +394,6 @@ "OAuth2AuthorizationServerMetadata": { "type": "object", "properties": { - "issuer": { - "type": "string" - }, "token_endpoint_auth_methods_supported": { "type": "array", "items": { @@ -397,16 +430,19 @@ "type": "string" } }, + "introspection_endpoint": { + "type": "string" + }, + "revocation_endpoint": { + "type": "string" + }, "grant_types_supported": { "type": "array", "items": { "type": "string" } }, - "revocation_endpoint": { - "type": "string" - }, - "introspection_endpoint": { + "issuer": { "type": "string" }, "jwks_uri": { @@ -424,13 +460,13 @@ "type": "integer", "format": "int64" }, - "access_token": { + "token_type": { "type": "string" }, - "refresh_token": { + "access_token": { "type": "string" }, - "token_type": { + "refresh_token": { "type": "string" } } @@ -438,26 +474,21 @@ "OAuth2TokenIntrospection": { "type": "object", "properties": { - "nbf": { - "type": "integer", - "format": "int64" - }, "scope": { "type": "string" }, "jti": { "type": "string" }, - "client_id": { - "type": "string" - }, - "username": { - "type": "string" + "exp": { + "type": "integer", + "format": "int64" }, - "active": { - "type": "boolean" + "nbf": { + "type": "integer", + "format": "int64" }, - "iss": { + "token_type": { "type": "string" }, "aud": { @@ -466,19 +497,24 @@ "type": "string" } }, - "token_type": { + "client_id": { "type": "string" }, - "exp": { - "type": "integer", - "format": "int64" + "username": { + "type": "string" }, - "sub": { + "iss": { "type": "string" }, + "active": { + "type": "boolean" + }, "iat": { "type": "integer", "format": "int64" + }, + "sub": { + "type": "string" } } } diff --git a/springdoc-openapi-tests/springdoc-openapi-security-tests/src/test/resources/results/app11.json b/springdoc-openapi-tests/springdoc-openapi-security-tests/src/test/resources/results/app11.json index 1f8119986..8d7a90106 100644 --- a/springdoc-openapi-tests/springdoc-openapi-security-tests/src/test/resources/results/app11.json +++ b/springdoc-openapi-tests/springdoc-openapi-security-tests/src/test/resources/results/app11.json @@ -56,6 +56,9 @@ } } }, + "500": { + "description": "Internal Server Error" + }, "400": { "description": "Bad Request", "content": { @@ -65,6 +68,25 @@ } } } + } + } + } + }, + "/.well-known/oauth-authorization-server": { + "get": { + "tags": [ + "authorization-server-endpoints" + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OAuth2AuthorizationServerMetadata" + } + } + } }, "500": { "description": "Internal Server Error" @@ -72,11 +94,22 @@ } } }, - "/.well-known/oauth-authorization-server": { + "/.well-known/oauth-authorization-server/{subpath}": { "get": { "tags": [ "authorization-server-endpoints" ], + "summary": "Valid when multiple issuers are allowed", + "parameters": [ + { + "name": "subpath", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], "responses": { "200": { "description": "OK", @@ -101,8 +134,11 @@ ], "parameters": [ { + "name": "Authorization", "in": "header", - "name": "Authorization" + "schema": { + "type": "string" + } } ], "requestBody": { @@ -165,6 +201,9 @@ } } }, + "500": { + "description": "Internal Server Error" + }, "400": { "description": "Bad Request", "content": { @@ -184,9 +223,6 @@ } } } - }, - "500": { - "description": "Internal Server Error" } } } @@ -215,15 +251,8 @@ "text/html": {} } }, - "302": { - "description": "Moved Temporarily", - "headers": { - "Location": { - "schema": { - "type": "string" - } - } - } + "500": { + "description": "Internal Server Error" }, "400": { "description": "Bad Request", @@ -235,8 +264,15 @@ } } }, - "500": { - "description": "Internal Server Error" + "302": { + "description": "Moved Temporarily", + "headers": { + "Location": { + "schema": { + "type": "string" + } + } + } } } } @@ -280,6 +316,9 @@ } } }, + "500": { + "description": "Internal Server Error" + }, "400": { "description": "Bad Request", "content": { @@ -289,9 +328,6 @@ } } } - }, - "500": { - "description": "Internal Server Error" } } } @@ -322,6 +358,9 @@ "200": { "description": "OK" }, + "500": { + "description": "Internal Server Error" + }, "400": { "description": "Bad Request", "content": { @@ -331,6 +370,25 @@ } } } + } + } + } + }, + "/.well-known/openid-configuration": { + "get": { + "tags": [ + "authorization-server-endpoints" + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OidcProviderConfiguration" + } + } + } }, "500": { "description": "Internal Server Error" @@ -338,11 +396,22 @@ } } }, - "/.well-known/openid-configuration": { + "/{subpath}/.well-known/openid-configuration": { "get": { "tags": [ "authorization-server-endpoints" ], + "summary": "Valid when multiple issuers are allowed", + "parameters": [ + { + "name": "subpath", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], "responses": { "200": { "description": "OK", @@ -405,9 +474,6 @@ "OAuth2AuthorizationServerMetadata": { "type": "object", "properties": { - "issuer": { - "type": "string" - }, "token_endpoint_auth_methods_supported": { "type": "array", "items": { @@ -444,16 +510,19 @@ "type": "string" } }, + "introspection_endpoint": { + "type": "string" + }, + "revocation_endpoint": { + "type": "string" + }, "grant_types_supported": { "type": "array", "items": { "type": "string" } }, - "revocation_endpoint": { - "type": "string" - }, - "introspection_endpoint": { + "issuer": { "type": "string" }, "jwks_uri": { @@ -471,13 +540,13 @@ "type": "integer", "format": "int64" }, - "access_token": { + "token_type": { "type": "string" }, - "refresh_token": { + "access_token": { "type": "string" }, - "token_type": { + "refresh_token": { "type": "string" } } @@ -485,26 +554,21 @@ "OAuth2TokenIntrospection": { "type": "object", "properties": { - "nbf": { - "type": "integer", - "format": "int64" - }, "scope": { "type": "string" }, "jti": { "type": "string" }, - "client_id": { - "type": "string" - }, - "username": { - "type": "string" + "exp": { + "type": "integer", + "format": "int64" }, - "active": { - "type": "boolean" + "nbf": { + "type": "integer", + "format": "int64" }, - "iss": { + "token_type": { "type": "string" }, "aud": { @@ -513,25 +577,33 @@ "type": "string" } }, - "token_type": { + "client_id": { "type": "string" }, - "exp": { - "type": "integer", - "format": "int64" + "username": { + "type": "string" }, - "sub": { + "iss": { "type": "string" }, + "active": { + "type": "boolean" + }, "iat": { "type": "integer", "format": "int64" + }, + "sub": { + "type": "string" } } }, "OidcProviderConfiguration": { "type": "object", "properties": { + "id_token_signing_alg_values_supported": { + "type": "string" + }, "token_endpoint_auth_methods_supported": { "type": "array", "items": { @@ -556,22 +628,19 @@ "type": "string" } }, - "id_token_signing_alg_values_supported": { - "type": "string" - }, - "authorization_endpoint": { + "subject_types_supported": { "type": "string" }, - "token_endpoint": { + "scopes_supported": { "type": "string" }, "userinfo_endpoint": { "type": "string" }, - "subject_types_supported": { + "authorization_endpoint": { "type": "string" }, - "scopes_supported": { + "token_endpoint": { "type": "string" }, "response_types_supported": { @@ -580,18 +649,18 @@ "type": "string" } }, + "introspection_endpoint": { + "type": "string" + }, + "revocation_endpoint": { + "type": "string" + }, "grant_types_supported": { "type": "array", "items": { "type": "string" } }, - "revocation_endpoint": { - "type": "string" - }, - "introspection_endpoint": { - "type": "string" - }, "issuer": { "type": "string" }, diff --git a/springdoc-openapi-tests/springdoc-openapi-security-tests/src/test/resources/results/app12.json b/springdoc-openapi-tests/springdoc-openapi-security-tests/src/test/resources/results/app12.json index 8925e7038..a45d84f60 100644 --- a/springdoc-openapi-tests/springdoc-openapi-security-tests/src/test/resources/results/app12.json +++ b/springdoc-openapi-tests/springdoc-openapi-security-tests/src/test/resources/results/app12.json @@ -56,6 +56,9 @@ } } }, + "500": { + "description": "Internal Server Error" + }, "400": { "description": "Bad Request", "content": { @@ -65,6 +68,25 @@ } } } + } + } + } + }, + "/.well-known/oauth-authorization-server": { + "get": { + "tags": [ + "authorization-server-endpoints" + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OAuth2AuthorizationServerMetadata" + } + } + } }, "500": { "description": "Internal Server Error" @@ -72,11 +94,22 @@ } } }, - "/.well-known/oauth-authorization-server": { + "/.well-known/oauth-authorization-server/{subpath}": { "get": { "tags": [ "authorization-server-endpoints" ], + "summary": "Valid when multiple issuers are allowed", + "parameters": [ + { + "name": "subpath", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], "responses": { "200": { "description": "OK", @@ -101,8 +134,11 @@ ], "parameters": [ { + "name": "Authorization", "in": "header", - "name": "Authorization" + "schema": { + "type": "string" + } } ], "requestBody": { @@ -165,6 +201,9 @@ } } }, + "500": { + "description": "Internal Server Error" + }, "400": { "description": "Bad Request", "content": { @@ -184,9 +223,6 @@ } } } - }, - "500": { - "description": "Internal Server Error" } } } @@ -215,15 +251,8 @@ "text/html": {} } }, - "302": { - "description": "Moved Temporarily", - "headers": { - "Location": { - "schema": { - "type": "string" - } - } - } + "500": { + "description": "Internal Server Error" }, "400": { "description": "Bad Request", @@ -235,8 +264,15 @@ } } }, - "500": { - "description": "Internal Server Error" + "302": { + "description": "Moved Temporarily", + "headers": { + "Location": { + "schema": { + "type": "string" + } + } + } } } } @@ -280,6 +316,9 @@ } } }, + "500": { + "description": "Internal Server Error" + }, "400": { "description": "Bad Request", "content": { @@ -289,9 +328,6 @@ } } } - }, - "500": { - "description": "Internal Server Error" } } } @@ -322,6 +358,9 @@ "200": { "description": "OK" }, + "500": { + "description": "Internal Server Error" + }, "400": { "description": "Bad Request", "content": { @@ -331,6 +370,25 @@ } } } + } + } + } + }, + "/.well-known/openid-configuration": { + "get": { + "tags": [ + "authorization-server-endpoints" + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OidcProviderConfiguration" + } + } + } }, "500": { "description": "Internal Server Error" @@ -338,11 +396,22 @@ } } }, - "/.well-known/openid-configuration": { + "/{subpath}/.well-known/openid-configuration": { "get": { "tags": [ "authorization-server-endpoints" ], + "summary": "Valid when multiple issuers are allowed", + "parameters": [ + { + "name": "subpath", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], "responses": { "200": { "description": "OK", @@ -393,7 +462,10 @@ "parameters": [ { "name": "Authorization", - "in": "header" + "in": "header", + "schema": { + "type": "string" + } } ], "requestBody": { @@ -416,6 +488,9 @@ } } }, + "500": { + "description": "Internal Server Error" + }, "400": { "description": "Bad Request", "content": { @@ -445,9 +520,6 @@ } } } - }, - "500": { - "description": "Internal Server Error" } } } @@ -472,9 +544,6 @@ "OAuth2AuthorizationServerMetadata": { "type": "object", "properties": { - "issuer": { - "type": "string" - }, "token_endpoint_auth_methods_supported": { "type": "array", "items": { @@ -511,16 +580,19 @@ "type": "string" } }, + "introspection_endpoint": { + "type": "string" + }, + "revocation_endpoint": { + "type": "string" + }, "grant_types_supported": { "type": "array", "items": { "type": "string" } }, - "revocation_endpoint": { - "type": "string" - }, - "introspection_endpoint": { + "issuer": { "type": "string" }, "jwks_uri": { @@ -538,13 +610,13 @@ "type": "integer", "format": "int64" }, - "access_token": { + "token_type": { "type": "string" }, - "refresh_token": { + "access_token": { "type": "string" }, - "token_type": { + "refresh_token": { "type": "string" } } @@ -552,26 +624,21 @@ "OAuth2TokenIntrospection": { "type": "object", "properties": { - "nbf": { - "type": "integer", - "format": "int64" - }, "scope": { "type": "string" }, "jti": { "type": "string" }, - "client_id": { - "type": "string" - }, - "username": { - "type": "string" + "exp": { + "type": "integer", + "format": "int64" }, - "active": { - "type": "boolean" + "nbf": { + "type": "integer", + "format": "int64" }, - "iss": { + "token_type": { "type": "string" }, "aud": { @@ -580,25 +647,33 @@ "type": "string" } }, - "token_type": { + "client_id": { "type": "string" }, - "exp": { - "type": "integer", - "format": "int64" + "username": { + "type": "string" }, - "sub": { + "iss": { "type": "string" }, + "active": { + "type": "boolean" + }, "iat": { "type": "integer", "format": "int64" + }, + "sub": { + "type": "string" } } }, "OidcProviderConfiguration": { "type": "object", "properties": { + "id_token_signing_alg_values_supported": { + "type": "string" + }, "token_endpoint_auth_methods_supported": { "type": "array", "items": { @@ -623,22 +698,19 @@ "type": "string" } }, - "id_token_signing_alg_values_supported": { - "type": "string" - }, - "authorization_endpoint": { + "subject_types_supported": { "type": "string" }, - "token_endpoint": { + "scopes_supported": { "type": "string" }, "userinfo_endpoint": { "type": "string" }, - "subject_types_supported": { + "authorization_endpoint": { "type": "string" }, - "scopes_supported": { + "token_endpoint": { "type": "string" }, "response_types_supported": { @@ -647,18 +719,18 @@ "type": "string" } }, + "introspection_endpoint": { + "type": "string" + }, + "revocation_endpoint": { + "type": "string" + }, "grant_types_supported": { "type": "array", "items": { "type": "string" } }, - "revocation_endpoint": { - "type": "string" - }, - "introspection_endpoint": { - "type": "string" - }, "issuer": { "type": "string" }, @@ -676,22 +748,13 @@ "id_token_signed_response_alg": { "type": "string" }, - "response_types": { - "type": "array", - "items": { - "type": "string" - } - }, - "client_secret": { + "registration_access_token": { "type": "string" }, "client_secret_expires_at": { "type": "integer", "format": "int64" }, - "registration_access_token": { - "type": "string" - }, "registration_client_uri": { "type": "string" }, @@ -705,12 +768,27 @@ "type": "string" } }, - "scope": { + "client_secret": { "type": "string" }, + "response_types": { + "type": "array", + "items": { + "type": "string" + } + }, "client_id": { "type": "string" }, + "client_name": { + "type": "string" + }, + "jwks_uri": { + "type": "string" + }, + "scope": { + "type": "string" + }, "token_endpoint_auth_signing_alg": { "type": "string" }, @@ -719,12 +797,6 @@ "items": { "type": "string" } - }, - "jwks_uri": { - "type": "string" - }, - "client_name": { - "type": "string" } } }, @@ -737,15 +809,6 @@ "id_token_signed_response_alg": { "type": "string" }, - "response_types": { - "type": "array", - "items": { - "type": "string" - } - }, - "client_secret": { - "type": "string" - }, "client_secret_expires_at": { "type": "string", "format": "date-time" @@ -760,12 +823,27 @@ "type": "string" } }, - "scope": { + "client_secret": { "type": "string" }, + "response_types": { + "type": "array", + "items": { + "type": "string" + } + }, "client_id": { "type": "string" }, + "client_name": { + "type": "string" + }, + "jwks_uri": { + "type": "string" + }, + "scope": { + "type": "string" + }, "token_endpoint_auth_signing_alg": { "type": "string" }, @@ -774,12 +852,6 @@ "items": { "type": "string" } - }, - "jwks_uri": { - "type": "string" - }, - "client_name": { - "type": "string" } } }