diff --git a/CHANGELOG.md b/CHANGELOG.md index 511317afd..bde9b2ffe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,25 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [1.6.14] - 2022-12-16 + +### Added + +- #1965 - Prevents premature initialisation of factory-beans +- #2003 - Resolve property descriptions for arrays + +### Changed + +- Upgrade spring-boot to 2.7.6 + +### Fixed + +- #1957 - AdditionalModelsConverter Schema params rewriting +- #1962 - override-with-generic-response shouldn't shallow copy +- #1985 - IllegalStateException: Duplicate key when two endpoints at the same URL with same header exist +- #1992 - Java enumeration and Spring Converter no longer generates enum drop-down. +- #2001 - Enum Collection parameter missing type info in Spring Data Rest search method + ## [1.6.13] - 2022-11-20 ### Added diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index abb9e2846..95b91f24b 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -1,4 +1,4 @@ -= Contributor Code of Conduct +## Contributor Code of Conduct As contributors and maintainers of this project, and in the interest of fostering an open and welcoming community, we pledge to respect all people who contribute through reporting @@ -34,6 +34,6 @@ This Code of Conduct applies both within project spaces and in public spaces whe individual is representing the project or its community. This Code of Conduct is adapted from the -https://contributor-covenant.org[Contributor Covenant], version 1.3.0, available at -https://contributor-covenant.org/version/1/3/0/[contributor-covenant.org/version/1/3/0/] and https://github.com/spring-projects/spring-boot/blob/master/CODE_OF_CONDUCT.adoc[spring-boot -Contributor Code of Conduct] +[Contributor Covenant](https://contributor-covenant.org), version 1.3.0, available at +[contributor-covenant.org/version/1/3/0/](https://contributor-covenant.org/version/1/3/0/) and [spring-boot +Contributor Code of Conduct](https://github.com/spring-projects/spring-boot/blob/master/CODE_OF_CONDUCT.adoc) diff --git a/README.md b/README.md index bc44c7a0d..6884eb746 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,8 @@ This library supports: * Swagger-ui * Oauth 2 +For *spring-boot v3* support, make sure you use [springdoc-openapi v2](https://springdoc.org/v2) + The following video introduces the Library: * [https://youtu.be/utRxyPfFlDw](https://youtu.be/utRxyPfFlDw) @@ -78,8 +80,8 @@ Pivotal) * Automatically deploys swagger-ui to a Spring Boot 2.x application * Documentation will be available in HTML format, using the official [swagger-ui jars](https://github.com/swagger-api/swagger-ui.git). -* The Swagger UI page should then be available at http://server: - port/context-path/swagger-ui.html and the OpenAPI description will be available at the +* The Swagger UI page should then be available at +http://server:port/context-path/swagger-ui.html and the OpenAPI description will be available at the following url for json format: http://server:port/context-path/v3/api-docs * `server`: The server name or IP * `port`: The server port @@ -123,8 +125,7 @@ springdoc.swagger-ui.path=/swagger-ui.html ## Integration of the library in a Spring Boot 2.x.x project without the swagger-ui: -* Documentation will be available at the following url for json format: http://server: - port/context-path/v3/api-docs +* Documentation will be available at the following url for json format: http://server:port/context-path/v3/api-docs * `server`: The server name or IP * `port`: The server port * `context-path`: The context path of the application @@ -218,7 +219,7 @@ its [contributors](https://github.com/springdoc/springdoc-openapi/graphs/contrib -Thanks you all for your support! +Thank you all for your support! ## Additional Support diff --git a/SECURITY.md b/SECURITY.md index 0099d9342..a92ff7b6e 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -8,7 +8,7 @@ ## Reporting a Vulnerability -If you think you have found a security vulnerability in Spring Boot please *DO NOT* +If you think you have found a security vulnerability in springdoc-openapi please *DO NOT* disclose it publicly until we've had a chance to fix it. Please don't report security vulnerabilities using GitHub issues, instead head over to support@springdoc.org and learn how to disclose them responsibly. diff --git a/pom.xml b/pom.xml index 2642716e6..ed611b0dd 100644 --- a/pom.xml +++ b/pom.xml @@ -2,7 +2,7 @@ 4.0.0 org.springdoc springdoc-openapi - 1.6.13 + 1.6.14 pom Spring openapi documentation Spring openapi documentation @@ -11,7 +11,7 @@ org.springframework.boot spring-boot-starter-parent - 2.7.5 + 2.7.6 @@ -36,7 +36,7 @@ scm:git:git@github.com:springdoc/springdoc-openapi.git scm:git:git@github.com:springdoc/springdoc-openapi.git - v1.6.13 + v1.6.14 diff --git a/springdoc-openapi-common/pom.xml b/springdoc-openapi-common/pom.xml index 8a73325df..de2cd69ce 100644 --- a/springdoc-openapi-common/pom.xml +++ b/springdoc-openapi-common/pom.xml @@ -3,7 +3,7 @@ org.springdoc springdoc-openapi - 1.6.13 + 1.6.14 springdoc-openapi-common diff --git a/springdoc-openapi-common/src/main/java/org/springdoc/api/AbstractOpenApiResource.java b/springdoc-openapi-common/src/main/java/org/springdoc/api/AbstractOpenApiResource.java index 6cb33ab86..a81fca353 100644 --- a/springdoc-openapi-common/src/main/java/org/springdoc/api/AbstractOpenApiResource.java +++ b/springdoc-openapi-common/src/main/java/org/springdoc/api/AbstractOpenApiResource.java @@ -1050,7 +1050,15 @@ private void fillParametersList(Operation operation, Map queryPa if (parametersList == null) parametersList = new ArrayList<>(); Collection headersMap = AbstractRequestService.getHeaders(methodAttributes, new LinkedHashMap<>()); - parametersList.addAll(headersMap); + headersMap.forEach(parameter -> { + Optional existingParam; + if (!CollectionUtils.isEmpty(operation.getParameters())){ + existingParam = operation.getParameters().stream().filter(p -> parameter.getName().equals(p.getName())).findAny(); + if (!existingParam.isPresent()) + operation.addParametersItem(parameter); + } + }); + if (!CollectionUtils.isEmpty(queryParams)) { for (Map.Entry entry : queryParams.entrySet()) { io.swagger.v3.oas.models.parameters.Parameter parameter = new io.swagger.v3.oas.models.parameters.Parameter(); diff --git a/springdoc-openapi-common/src/main/java/org/springdoc/core/GenericParameterService.java b/springdoc-openapi-common/src/main/java/org/springdoc/core/GenericParameterService.java index 2ec295e4a..c4192f5c7 100644 --- a/springdoc-openapi-common/src/main/java/org/springdoc/core/GenericParameterService.java +++ b/springdoc-openapi-common/src/main/java/org/springdoc/core/GenericParameterService.java @@ -78,6 +78,7 @@ /** * The type Generic parameter builder. + * * @author bnasslahsen, coutin */ @SuppressWarnings("rawtypes") @@ -355,8 +356,11 @@ Schema calculateSchema(Components components, ParameterInfo parameterInfo, Reque Type type = ReturnTypeParser.getType(methodParameter); if (type instanceof Class && optionalWebConversionServiceProvider.isPresent()) { WebConversionServiceProvider webConversionServiceProvider = optionalWebConversionServiceProvider.get(); - if (!MethodParameterPojoExtractor.isSwaggerPrimitiveType((Class) type) && methodParameter.getParameterType().getAnnotation(io.swagger.v3.oas.annotations.media.Schema.class) == null) - type = webConversionServiceProvider.getSpringConvertedType(methodParameter.getParameterType()); + if (!MethodParameterPojoExtractor.isSwaggerPrimitiveType((Class) type) && methodParameter.getParameterType().getAnnotation(io.swagger.v3.oas.annotations.media.Schema.class) == null) { + Class springConvertedType = webConversionServiceProvider.getSpringConvertedType(methodParameter.getParameterType()); + if (!(String.class.equals(springConvertedType) && ((Class) type).isEnum())) + type = springConvertedType; + } } schemaN = SpringDocAnnotationsUtils.extractSchema(components, type, jsonView, methodParameter.getParameterAnnotations()); } @@ -569,6 +573,7 @@ public Optional getOptionalWebConversionServicePro /** * Resolve the given annotation-specified value, * potentially containing placeholders and expressions. + * * @param value the value * @return the object */ diff --git a/springdoc-openapi-common/src/main/java/org/springdoc/core/GenericResponseService.java b/springdoc-openapi-common/src/main/java/org/springdoc/core/GenericResponseService.java index 26c8c0f3b..b39a5dc82 100644 --- a/springdoc-openapi-common/src/main/java/org/springdoc/core/GenericResponseService.java +++ b/springdoc-openapi-common/src/main/java/org/springdoc/core/GenericResponseService.java @@ -29,6 +29,7 @@ import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; @@ -41,6 +42,8 @@ import java.util.stream.Stream; import com.fasterxml.jackson.annotation.JsonView; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; import io.swagger.v3.core.util.AnnotationsUtils; import io.swagger.v3.oas.models.Components; import io.swagger.v3.oas.models.Operation; @@ -50,7 +53,10 @@ import io.swagger.v3.oas.models.responses.ApiResponses; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springdoc.core.providers.JavadocProvider; +import org.springdoc.core.providers.ObjectMapperProvider; import org.springframework.core.MethodParameter; import org.springframework.core.ResolvableType; @@ -74,6 +80,7 @@ /** * The type Generic response builder. + * * @author bnasslahsen */ public class GenericResponseService { @@ -118,6 +125,11 @@ public class GenericResponseService { */ private final List controllerAdviceInfos = new ArrayList<>(); + /** + * The constant LOGGER. + */ + private static final Logger LOGGER = LoggerFactory.getLogger(GenericResponseService.class); + /** * Instantiates a new Generic response builder. * @@ -242,9 +254,9 @@ private Map filterAndEnrichGenericMapResponseByDeclarations JavadocProvider javadocProvider = operationService.getJavadocProvider(); for (Map.Entry genericResponse : genericMapResponse.entrySet()) { Map extensions = genericResponse.getValue().getExtensions(); - Set> genericExceptions = (Set>) extensions.get(EXTENSION_EXCEPTION_CLASSES); + Collection genericExceptions = (Collection) extensions.get(EXTENSION_EXCEPTION_CLASSES); for (Class declaredException : handlerMethod.getMethod().getExceptionTypes()) { - if (genericExceptions.contains(declaredException)) { + if (genericExceptions.contains(declaredException.getName())) { Map javadocThrows = javadocProvider.getMethodJavadocThrows(handlerMethod.getMethod()); String description = javadocThrows.get(declaredException.getName()); if (description == null) @@ -675,7 +687,16 @@ private synchronized Map getGenericMapResponse(Class bea }); } - return genericApiResponseMap; + LinkedHashMap genericApiResponsesClone; + try { + ObjectMapper objectMapper = ObjectMapperProvider.createJson(springDocConfigProperties); + genericApiResponsesClone = objectMapper.readValue(objectMapper.writeValueAsString(genericApiResponseMap), ApiResponses.class); + return genericApiResponsesClone; + } + catch (JsonProcessingException e) { + LOGGER.warn("Json Processing Exception occurred: {}", e.getMessage()); + return genericApiResponseMap; + } } /** diff --git a/springdoc-openapi-common/src/main/java/org/springdoc/core/MethodAttributes.java b/springdoc-openapi-common/src/main/java/org/springdoc/core/MethodAttributes.java index ab7dffaa4..0741eb1d3 100644 --- a/springdoc-openapi-common/src/main/java/org/springdoc/core/MethodAttributes.java +++ b/springdoc-openapi-common/src/main/java/org/springdoc/core/MethodAttributes.java @@ -31,6 +31,7 @@ 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.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.util.CollectionUtils; @@ -385,9 +386,16 @@ public Map getHeaders() { private void setHeaders(String[] headers) { if (ArrayUtils.isNotEmpty(headers)) for (String header : headers) { - String[] keyValueHeader = header.split("="); - String headerValue = keyValueHeader.length > 1 ? keyValueHeader[1] : ""; - this.headers.put(keyValueHeader[0], headerValue); + if (!header.contains("!=")) { + String[] keyValueHeader = header.split("="); + String headerValue = keyValueHeader.length > 1 ? keyValueHeader[1] : ""; + this.headers.put(keyValueHeader[0], headerValue); + } + else { + String[] keyValueHeader = header.split("!="); + if (!this.headers.containsKey(keyValueHeader[0])) + this.headers.put(keyValueHeader[0], StringUtils.EMPTY); + } } } diff --git a/springdoc-openapi-common/src/main/java/org/springdoc/core/MultipleOpenApiGroupsCondition.java b/springdoc-openapi-common/src/main/java/org/springdoc/core/MultipleOpenApiGroupsCondition.java index 0210f6db8..fa5ec7853 100644 --- a/springdoc-openapi-common/src/main/java/org/springdoc/core/MultipleOpenApiGroupsCondition.java +++ b/springdoc-openapi-common/src/main/java/org/springdoc/core/MultipleOpenApiGroupsCondition.java @@ -22,8 +22,6 @@ package org.springdoc.core; -import java.util.Collection; - import org.springframework.boot.autoconfigure.condition.AnyNestedCondition; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; diff --git a/springdoc-openapi-common/src/main/java/org/springdoc/core/PropertyResolverUtils.java b/springdoc-openapi-common/src/main/java/org/springdoc/core/PropertyResolverUtils.java index 97c4bcc5c..a1b6db47b 100644 --- a/springdoc-openapi-common/src/main/java/org/springdoc/core/PropertyResolverUtils.java +++ b/springdoc-openapi-common/src/main/java/org/springdoc/core/PropertyResolverUtils.java @@ -34,6 +34,7 @@ import io.swagger.v3.oas.models.info.Contact; import io.swagger.v3.oas.models.info.Info; import io.swagger.v3.oas.models.info.License; +import io.swagger.v3.oas.models.media.ArraySchema; import io.swagger.v3.oas.models.media.Schema; import io.swagger.v3.oas.models.servers.Server; import org.apache.commons.lang3.StringUtils; @@ -174,6 +175,10 @@ public Schema resolveProperties(Schema schema, Locale locale) { if (!CollectionUtils.isEmpty(properties)) { LinkedHashMap resolvedSchemas = properties.entrySet().stream().map(es -> { es.setValue(resolveProperties(es.getValue(), locale)); + if(es.getValue() instanceof ArraySchema){ + ArraySchema arraySchema = (ArraySchema) es.getValue(); + resolveProperties(arraySchema.getItems(), locale); + } return es; }).collect(Collectors.toMap(Entry::getKey, Entry::getValue, (e1, e2) -> e2, LinkedHashMap::new)); diff --git a/springdoc-openapi-common/src/main/java/org/springdoc/core/SpringDocUtils.java b/springdoc-openapi-common/src/main/java/org/springdoc/core/SpringDocUtils.java index 0a05f9542..b657e0cba 100644 --- a/springdoc-openapi-common/src/main/java/org/springdoc/core/SpringDocUtils.java +++ b/springdoc-openapi-common/src/main/java/org/springdoc/core/SpringDocUtils.java @@ -33,6 +33,7 @@ /** * The type Spring doc utils. + * * @author bnasslahsen */ public class SpringDocUtils { @@ -323,5 +324,27 @@ public SpringDocUtils setModelAndViewClass(Class clazz) { AbstractOpenApiResource.setModelAndViewClass(clazz); return this; } + + /** + * Remove from schema map spring doc utils. + * + * @param clazzs the clazzs + * @return the spring doc utils + */ + public SpringDocUtils removeFromSchemaMap(Class clazzs) { + AdditionalModelsConverter.removeFromSchemaMap(clazzs); + return this; + } + + /** + * Remove from schema class spring doc utils. + * + * @param clazzs the clazzs + * @return the spring doc utils + */ + public SpringDocUtils removeFromSchemaClass(Class clazzs) { + AdditionalModelsConverter.removeFromClassMap(clazzs); + return this; + } } diff --git a/springdoc-openapi-common/src/main/java/org/springdoc/core/SpringdocBeanFactoryConfigurer.java b/springdoc-openapi-common/src/main/java/org/springdoc/core/SpringdocBeanFactoryConfigurer.java index ae1e6ed6c..63a304a8d 100644 --- a/springdoc-openapi-common/src/main/java/org/springdoc/core/SpringdocBeanFactoryConfigurer.java +++ b/springdoc-openapi-common/src/main/java/org/springdoc/core/SpringdocBeanFactoryConfigurer.java @@ -58,9 +58,9 @@ public class SpringdocBeanFactoryConfigurer implements EnvironmentAware, BeanFac * @param beanFactory the bean factory */ public static void initBeanFactoryPostProcessor(ConfigurableListableBeanFactory beanFactory) { - for (String beanName : beanFactory.getBeanNamesForType(OpenAPIService.class)) + for (String beanName : beanFactory.getBeanNamesForType(OpenAPIService.class, true, false)) beanFactory.getBeanDefinition(beanName).setScope(SCOPE_PROTOTYPE); - for (String beanName : beanFactory.getBeanNamesForType(OpenAPI.class)) + for (String beanName : beanFactory.getBeanNamesForType(OpenAPI.class, true, false)) beanFactory.getBeanDefinition(beanName).setScope(SCOPE_PROTOTYPE); } diff --git a/springdoc-openapi-common/src/main/java/org/springdoc/core/converters/AdditionalModelsConverter.java b/springdoc-openapi-common/src/main/java/org/springdoc/core/converters/AdditionalModelsConverter.java index 617a22ee1..a2e8fbc18 100644 --- a/springdoc-openapi-common/src/main/java/org/springdoc/core/converters/AdditionalModelsConverter.java +++ b/springdoc-openapi-common/src/main/java/org/springdoc/core/converters/AdditionalModelsConverter.java @@ -26,15 +26,20 @@ import java.util.Iterator; import java.util.Map; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; 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.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springdoc.core.providers.ObjectMapperProvider; /** * The type Additional models converter. + * * @author bnasslahsen */ public class AdditionalModelsConverter implements ModelConverter { @@ -42,18 +47,23 @@ public class AdditionalModelsConverter implements ModelConverter { /** * The constant modelToClassMap. */ - private static final Map modelToClassMap = new HashMap<>(); + private static Map modelToClassMap = new HashMap<>(); /** * The constant modelToSchemaMap. */ - private static final Map modelToSchemaMap = new HashMap<>(); + private static Map modelToSchemaMap = new HashMap<>(); /** * The constant paramObjectReplacementMap. */ private static final Map paramObjectReplacementMap = new HashMap<>(); + /** + * The constant LOGGER. + */ + private static final Logger LOGGER = LoggerFactory.getLogger(AdditionalModelsConverter.class); + /** * The Spring doc object mapper. */ @@ -130,12 +140,36 @@ public Schema resolve(AnnotatedType type, ModelConverterContext context, Iterato JavaType javaType = springDocObjectMapper.jsonMapper().constructType(type.getType()); if (javaType != null) { Class cls = javaType.getRawClass(); - if (modelToSchemaMap.containsKey(cls)) - return modelToSchemaMap.get(cls); + if (modelToSchemaMap.containsKey(cls)) { + try { + return springDocObjectMapper.jsonMapper() + .readValue(springDocObjectMapper.jsonMapper().writeValueAsString(modelToSchemaMap.get(cls)), new TypeReference() {}); + } + catch (JsonProcessingException e) { + LOGGER.warn("Json Processing Exception occurred: {}", e.getMessage()); + } + } if (modelToClassMap.containsKey(cls)) type = new AnnotatedType(modelToClassMap.get(cls)).resolveAsRef(true); } return (chain.hasNext()) ? chain.next().resolve(type, context, chain) : null; } + /** + * Remove from schema map. + * + * @param clazz the clazz + */ + public static void removeFromSchemaMap(Class clazz) { + modelToSchemaMap.remove(clazz); + } + + /** + * Remove from class map. + * + * @param clazz the clazz + */ + public static void removeFromClassMap(Class clazz) { + modelToClassMap.remove(clazz); + } } \ No newline at end of file diff --git a/springdoc-openapi-data-rest/pom.xml b/springdoc-openapi-data-rest/pom.xml index e45500021..88277af6e 100644 --- a/springdoc-openapi-data-rest/pom.xml +++ b/springdoc-openapi-data-rest/pom.xml @@ -4,7 +4,7 @@ springdoc-openapi org.springdoc - 1.6.13 + 1.6.14 springdoc-openapi-data-rest diff --git a/springdoc-openapi-data-rest/src/main/java/org/springdoc/data/rest/core/DataRestOperationService.java b/springdoc-openapi-data-rest/src/main/java/org/springdoc/data/rest/core/DataRestOperationService.java index 9998cc832..8576fe1a3 100644 --- a/springdoc-openapi-data-rest/src/main/java/org/springdoc/data/rest/core/DataRestOperationService.java +++ b/springdoc-openapi-data-rest/src/main/java/org/springdoc/data/rest/core/DataRestOperationService.java @@ -24,6 +24,7 @@ import java.lang.reflect.Field; import java.lang.reflect.Method; +import java.lang.reflect.Type; import java.util.Arrays; import io.swagger.v3.oas.annotations.enums.ParameterIn; @@ -39,6 +40,7 @@ import org.springdoc.core.SpringDocAnnotationsUtils; import org.springframework.core.MethodParameter; +import org.springframework.core.ResolvableType; import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.data.repository.query.Param; import org.springframework.data.rest.core.mapping.MethodResourceMapping; @@ -54,6 +56,7 @@ /** * The type Data rest operation builder. + * * @author bnasslahsen */ public class DataRestOperationService { @@ -202,17 +205,8 @@ private Operation buildSearchOperation(HandlerMethod handlerMethod, DataRestRepo String pName = parameterMetadatum.getName(); ResourceDescription description = parameterMetadatum.getDescription(); if (description instanceof TypedResourceDescription) { - TypedResourceDescription typedResourceDescription = (TypedResourceDescription) description; - Field fieldType = FieldUtils.getField(TypedResourceDescription.class, "type", true); - Class type; - try { - type = (Class) fieldType.get(typedResourceDescription); - } - catch (IllegalAccessException e) { - LOGGER.warn(e.getMessage()); - type = String.class; - } - Schema schema = SpringDocAnnotationsUtils.resolveSchemaFromType(type, openAPI.getComponents(), null, null); + Type type = getParameterType(pName,method,description); + Schema schema = SpringDocAnnotationsUtils.extractSchema(openAPI.getComponents(), type, null, null); Parameter parameter = getParameterFromAnnotations(openAPI, methodAttributes, method, pName); if (parameter == null) parameter = new Parameter().name(pName).in(ParameterIn.QUERY.toString()).schema(schema); @@ -231,6 +225,39 @@ private Operation buildSearchOperation(HandlerMethod handlerMethod, DataRestRepo return operation; } + /** + * Gets parameter type. + * + * @param pName the p name + * @param method the method + * @param description the description + * @return the parameter type + */ + private Type getParameterType(String pName, Method method, ResourceDescription description) { + Type type = null; + java.lang.reflect.Parameter[] parameters = method.getParameters(); + for (int i = 0; i < parameters.length; i++) { + java.lang.reflect.Parameter parameter = parameters[i]; + if (pName.equals(parameter.getName()) || pName.equals(parameter.getAnnotation(Param.class).value())) { + ResolvableType resolvableType = ResolvableType.forMethodParameter(method, i); + type = resolvableType.getType(); + break; + } + } + if (type == null) { + TypedResourceDescription typedResourceDescription = (TypedResourceDescription) description; + Field fieldType = FieldUtils.getField(TypedResourceDescription.class, "type", true); + try { + type = (Type) fieldType.get(typedResourceDescription); + } + catch (IllegalAccessException e) { + LOGGER.warn(e.getMessage()); + type = String.class; + } + } + return type; + } + /** * Update parameter from annotations parameter. * @@ -279,6 +306,7 @@ private Operation initOperation(HandlerMethod handlerMethod, Class domainType /** * Add operation description. + * * @param operation the operation * @param requestMethod the request method * @param entity the entity diff --git a/springdoc-openapi-data-rest/src/test/java/test/org/springdoc/api/app36/EnumFieldHolder.java b/springdoc-openapi-data-rest/src/test/java/test/org/springdoc/api/app36/EnumFieldHolder.java new file mode 100644 index 000000000..1354beb6f --- /dev/null +++ b/springdoc-openapi-data-rest/src/test/java/test/org/springdoc/api/app36/EnumFieldHolder.java @@ -0,0 +1,52 @@ +/* + * + * * 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.app36; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; + +@Entity +public class EnumFieldHolder { + + public enum EnumField { + + FOO, BAR; + + } + + @Id + @GeneratedValue + private Long id; + + private EnumField enumField; + + public Long getId() { + return id; + } + + public EnumField getEnumField() { + return enumField; + } + + public void setEnumField(EnumField enumField) { + this.enumField = enumField; + } + +} diff --git a/springdoc-openapi-data-rest/src/test/java/test/org/springdoc/api/app36/EnumFieldHolderRepository.java b/springdoc-openapi-data-rest/src/test/java/test/org/springdoc/api/app36/EnumFieldHolderRepository.java new file mode 100644 index 000000000..88a2b518d --- /dev/null +++ b/springdoc-openapi-data-rest/src/test/java/test/org/springdoc/api/app36/EnumFieldHolderRepository.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.app36; + +import java.util.List; + +import org.springframework.data.repository.Repository; +import org.springframework.data.repository.query.Param; +import org.springframework.data.rest.core.annotation.RepositoryRestResource; +import org.springframework.data.util.Streamable; + +@RepositoryRestResource +public interface EnumFieldHolderRepository extends Repository { + + Streamable findAllByEnumField(@Param("enumField") EnumFieldHolder.EnumField enumField); + + Streamable findAllByEnumFieldIn(@Param("enumFields") List enumFields); + +} diff --git a/springdoc-openapi-data-rest/src/test/java/test/org/springdoc/api/app36/SpringDocApp36Test.java b/springdoc-openapi-data-rest/src/test/java/test/org/springdoc/api/app36/SpringDocApp36Test.java new file mode 100644 index 000000000..4bef9476f --- /dev/null +++ b/springdoc-openapi-data-rest/src/test/java/test/org/springdoc/api/app36/SpringDocApp36Test.java @@ -0,0 +1,32 @@ +/* + * + * * 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.app36; + +import test.org.springdoc.api.AbstractSpringDocTest; + +import org.springframework.boot.autoconfigure.SpringBootApplication; + +public class SpringDocApp36Test extends AbstractSpringDocTest { + + @SpringBootApplication + static class SpringDocTestApp { + + } + +} diff --git a/springdoc-openapi-data-rest/src/test/resources/results/app36.json b/springdoc-openapi-data-rest/src/test/resources/results/app36.json new file mode 100644 index 000000000..3aabae473 --- /dev/null +++ b/springdoc-openapi-data-rest/src/test/resources/results/app36.json @@ -0,0 +1,167 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "OpenAPI definition", + "version": "v0" + }, + "servers": [ + { + "url": "http://localhost", + "description": "Generated server url" + } + ], + "paths": { + "/enumFieldHolders/search/findAllByEnumField": { + "get": { + "tags": [ + "enum-field-holder-search-controller" + ], + "operationId": "executeSearch-enumfieldholder-get", + "parameters": [ + { + "name": "enumField", + "in": "query", + "schema": { + "type": "string", + "enum": [ + "FOO", + "BAR" + ] + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/hal+json": { + "schema": { + "$ref": "#/components/schemas/CollectionModelEntityModelEnumFieldHolder" + } + } + } + }, + "404": { + "description": "Not Found" + } + } + } + }, + "/enumFieldHolders/search/findAllByEnumFieldIn": { + "get": { + "tags": [ + "enum-field-holder-search-controller" + ], + "operationId": "executeSearch-enumfieldholder-get_1", + "parameters": [ + { + "name": "enumFields", + "in": "query", + "schema": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "FOO", + "BAR" + ] + } + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/hal+json": { + "schema": { + "$ref": "#/components/schemas/CollectionModelEntityModelEnumFieldHolder" + } + } + } + }, + "404": { + "description": "Not Found" + } + } + } + } + }, + "components": { + "schemas": { + "CollectionModelEntityModelEnumFieldHolder": { + "type": "object", + "properties": { + "_embedded": { + "type": "object", + "properties": { + "enumFieldHolders": { + "type": "array", + "items": { + "$ref": "#/components/schemas/EntityModelEnumFieldHolder" + } + } + } + }, + "_links": { + "$ref": "#/components/schemas/Links" + } + } + }, + "EntityModelEnumFieldHolder": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "enumField": { + "type": "string", + "enum": [ + "FOO", + "BAR" + ] + }, + "_links": { + "$ref": "#/components/schemas/Links" + } + } + }, + "Link": { + "type": "object", + "properties": { + "deprecation": { + "type": "string" + }, + "href": { + "type": "string" + }, + "hreflang": { + "type": "string" + }, + "name": { + "type": "string" + }, + "profile": { + "type": "string" + }, + "templated": { + "type": "boolean" + }, + "title": { + "type": "string" + }, + "type": { + "type": "string" + } + } + }, + "Links": { + "type": "object", + "additionalProperties": { + "$ref": "#/components/schemas/Link" + } + } + } + } +} diff --git a/springdoc-openapi-groovy/pom.xml b/springdoc-openapi-groovy/pom.xml index e7470a05e..ef1ebec50 100644 --- a/springdoc-openapi-groovy/pom.xml +++ b/springdoc-openapi-groovy/pom.xml @@ -4,7 +4,7 @@ springdoc-openapi org.springdoc - 1.6.13 + 1.6.14 springdoc-openapi-groovy diff --git a/springdoc-openapi-hateoas/pom.xml b/springdoc-openapi-hateoas/pom.xml index 0d417bf83..c3d827bcc 100644 --- a/springdoc-openapi-hateoas/pom.xml +++ b/springdoc-openapi-hateoas/pom.xml @@ -4,7 +4,7 @@ springdoc-openapi org.springdoc - 1.6.13 + 1.6.14 springdoc-openapi-hateoas diff --git a/springdoc-openapi-javadoc/pom.xml b/springdoc-openapi-javadoc/pom.xml index df56e2718..fa6c32152 100644 --- a/springdoc-openapi-javadoc/pom.xml +++ b/springdoc-openapi-javadoc/pom.xml @@ -3,7 +3,7 @@ springdoc-openapi org.springdoc - 1.6.13 + 1.6.14 4.0.0 diff --git a/springdoc-openapi-javadoc/src/test/resources/results/app162.json b/springdoc-openapi-javadoc/src/test/resources/results/app162.json index 885ee8568..bd5968cda 100644 --- a/springdoc-openapi-javadoc/src/test/resources/results/app162.json +++ b/springdoc-openapi-javadoc/src/test/resources/results/app162.json @@ -38,8 +38,8 @@ } ], "responses": { - "400": { - "description": "the @throws NonUniqueResultException javadoc for the #findStartsBy(String) method", + "404": { + "description": "the @throws NoResultException javadoc for the #find(String) method", "content": { "*/*": { "schema": { @@ -48,11 +48,11 @@ } }, "x-exception-class": [ - "test.org.springdoc.api.app162.exception.NonUniqueResultException" + "test.org.springdoc.api.app162.exception.NoResultException" ] }, - "404": { - "description": "the @throws NoResultException javadoc for the #findStartsBy(String) method", + "400": { + "description": "the return javadoc for the #handleNonUniqueResultException(NonUniqueResultException) method", "content": { "*/*": { "schema": { @@ -61,7 +61,7 @@ } }, "x-exception-class": [ - "test.org.springdoc.api.app162.exception.NoResultException" + "test.org.springdoc.api.app162.exception.NonUniqueResultException" ] }, "200": { @@ -105,8 +105,8 @@ "required": true }, "responses": { - "400": { - "description": "the @throws NonUniqueResultException javadoc for the #findStartsBy(String) method", + "404": { + "description": "the return javadoc for the #handleNotFoundException(NoResultException) method", "content": { "*/*": { "schema": { @@ -115,11 +115,11 @@ } }, "x-exception-class": [ - "test.org.springdoc.api.app162.exception.NonUniqueResultException" + "test.org.springdoc.api.app162.exception.NoResultException" ] }, - "404": { - "description": "the @throws NoResultException javadoc for the #findStartsBy(String) method", + "400": { + "description": "the return javadoc for the #handleNonUniqueResultException(NonUniqueResultException) method", "content": { "*/*": { "schema": { @@ -128,7 +128,7 @@ } }, "x-exception-class": [ - "test.org.springdoc.api.app162.exception.NoResultException" + "test.org.springdoc.api.app162.exception.NonUniqueResultException" ] }, "200": { @@ -153,8 +153,8 @@ "description": "This is the list method's javadoc.\n The method's signature: #list()", "operationId": "list", "responses": { - "400": { - "description": "the @throws NonUniqueResultException javadoc for the #findStartsBy(String) method", + "404": { + "description": "the return javadoc for the #handleNotFoundException(NoResultException) method", "content": { "*/*": { "schema": { @@ -163,11 +163,11 @@ } }, "x-exception-class": [ - "test.org.springdoc.api.app162.exception.NonUniqueResultException" + "test.org.springdoc.api.app162.exception.NoResultException" ] }, - "404": { - "description": "the @throws NoResultException javadoc for the #findStartsBy(String) method", + "400": { + "description": "the return javadoc for the #handleNonUniqueResultException(NonUniqueResultException) method", "content": { "*/*": { "schema": { @@ -176,7 +176,7 @@ } }, "x-exception-class": [ - "test.org.springdoc.api.app162.exception.NoResultException" + "test.org.springdoc.api.app162.exception.NonUniqueResultException" ] }, "200": { @@ -213,8 +213,8 @@ "required": true }, "responses": { - "400": { - "description": "the @throws NonUniqueResultException javadoc for the #findStartsBy(String) method", + "404": { + "description": "the return javadoc for the #handleNotFoundException(NoResultException) method", "content": { "*/*": { "schema": { @@ -223,11 +223,11 @@ } }, "x-exception-class": [ - "test.org.springdoc.api.app162.exception.NonUniqueResultException" + "test.org.springdoc.api.app162.exception.NoResultException" ] }, - "404": { - "description": "the @throws NoResultException javadoc for the #findStartsBy(String) method", + "400": { + "description": "the return javadoc for the #handleNonUniqueResultException(NonUniqueResultException) method", "content": { "*/*": { "schema": { @@ -236,7 +236,7 @@ } }, "x-exception-class": [ - "test.org.springdoc.api.app162.exception.NoResultException" + "test.org.springdoc.api.app162.exception.NonUniqueResultException" ] }, "201": { @@ -272,8 +272,8 @@ } ], "responses": { - "400": { - "description": "the @throws NonUniqueResultException javadoc for the #findStartsBy(String) method", + "404": { + "description": "the @throws NoResultException javadoc for the #findStartsBy(String) method", "content": { "*/*": { "schema": { @@ -282,11 +282,11 @@ } }, "x-exception-class": [ - "test.org.springdoc.api.app162.exception.NonUniqueResultException" + "test.org.springdoc.api.app162.exception.NoResultException" ] }, - "404": { - "description": "the @throws NoResultException javadoc for the #findStartsBy(String) method", + "400": { + "description": "the @throws NonUniqueResultException javadoc for the #findStartsBy(String) method", "content": { "*/*": { "schema": { @@ -295,7 +295,7 @@ } }, "x-exception-class": [ - "test.org.springdoc.api.app162.exception.NoResultException" + "test.org.springdoc.api.app162.exception.NonUniqueResultException" ] }, "200": { diff --git a/springdoc-openapi-javadoc/src/test/resources/results/app163.json b/springdoc-openapi-javadoc/src/test/resources/results/app163.json index 913041c3a..da02ff9bc 100644 --- a/springdoc-openapi-javadoc/src/test/resources/results/app163.json +++ b/springdoc-openapi-javadoc/src/test/resources/results/app163.json @@ -48,8 +48,8 @@ "required": true }, "responses": { - "400": { - "description": "the @throws NonUniqueResultException javadoc for the #findStartsBy(String) method", + "404": { + "description": "the return javadoc for the #handleNotFoundException(NoResultException) method", "content": { "*/*": { "schema": { @@ -58,11 +58,11 @@ } }, "x-exception-class": [ - "test.org.springdoc.api.app163.exception.NonUniqueResultException" + "test.org.springdoc.api.app163.exception.NoResultException" ] }, - "404": { - "description": "the @throws NoResultException javadoc for the #findStartsBy(String) method", + "400": { + "description": "the return javadoc for the #handleNonUniqueResultException(NonUniqueResultException) method", "content": { "*/*": { "schema": { @@ -71,7 +71,7 @@ } }, "x-exception-class": [ - "test.org.springdoc.api.app163.exception.NoResultException" + "test.org.springdoc.api.app163.exception.NonUniqueResultException" ] }, "200": { @@ -107,8 +107,8 @@ "required": true }, "responses": { - "400": { - "description": "the @throws NonUniqueResultException javadoc for the #findStartsBy(String) method", + "404": { + "description": "the return javadoc for the #handleNotFoundException(NoResultException) method", "content": { "*/*": { "schema": { @@ -117,11 +117,11 @@ } }, "x-exception-class": [ - "test.org.springdoc.api.app163.exception.NonUniqueResultException" + "test.org.springdoc.api.app163.exception.NoResultException" ] }, - "404": { - "description": "the @throws NoResultException javadoc for the #findStartsBy(String) method", + "400": { + "description": "the return javadoc for the #handleNonUniqueResultException(NonUniqueResultException) method", "content": { "*/*": { "schema": { @@ -130,7 +130,7 @@ } }, "x-exception-class": [ - "test.org.springdoc.api.app163.exception.NoResultException" + "test.org.springdoc.api.app163.exception.NonUniqueResultException" ] }, "default": { @@ -166,9 +166,6 @@ } ], "responses": { - "400": { - "description": "API Response 400 for #findStartsBy(prefix)" - }, "404": { "description": "the @throws NoResultException javadoc for the #findStartsBy(String) method", "content": { @@ -182,6 +179,9 @@ "test.org.springdoc.api.app163.exception.NoResultException" ] }, + "400": { + "description": "API Response 400 for #findStartsBy(prefix)" + }, "200": { "description": "API Response 200 for #findStartsBy(prefix)", "content": { diff --git a/springdoc-openapi-kotlin/pom.xml b/springdoc-openapi-kotlin/pom.xml index dee6479ad..2790cb410 100644 --- a/springdoc-openapi-kotlin/pom.xml +++ b/springdoc-openapi-kotlin/pom.xml @@ -3,7 +3,7 @@ org.springdoc springdoc-openapi - 1.6.13 + 1.6.14 5.3.6 diff --git a/springdoc-openapi-native/pom.xml b/springdoc-openapi-native/pom.xml index c9975210b..5fb01b5fe 100644 --- a/springdoc-openapi-native/pom.xml +++ b/springdoc-openapi-native/pom.xml @@ -3,7 +3,7 @@ springdoc-openapi org.springdoc - 1.6.13 + 1.6.14 4.0.0 diff --git a/springdoc-openapi-security/pom.xml b/springdoc-openapi-security/pom.xml index 0d61eae9f..e52614115 100644 --- a/springdoc-openapi-security/pom.xml +++ b/springdoc-openapi-security/pom.xml @@ -4,7 +4,7 @@ springdoc-openapi org.springdoc - 1.6.13 + 1.6.14 springdoc-openapi-security diff --git a/springdoc-openapi-ui/pom.xml b/springdoc-openapi-ui/pom.xml index 548eb9f34..b758353c8 100644 --- a/springdoc-openapi-ui/pom.xml +++ b/springdoc-openapi-ui/pom.xml @@ -3,7 +3,7 @@ org.springdoc springdoc-openapi - 1.6.13 + 1.6.14 springdoc-openapi-ui diff --git a/springdoc-openapi-webflux-core/pom.xml b/springdoc-openapi-webflux-core/pom.xml index 72237f186..1b755fb74 100644 --- a/springdoc-openapi-webflux-core/pom.xml +++ b/springdoc-openapi-webflux-core/pom.xml @@ -3,7 +3,7 @@ org.springdoc springdoc-openapi - 1.6.13 + 1.6.14 springdoc-openapi-webflux-core diff --git a/springdoc-openapi-webflux-ui/pom.xml b/springdoc-openapi-webflux-ui/pom.xml index 7ae3267d0..294cb4143 100644 --- a/springdoc-openapi-webflux-ui/pom.xml +++ b/springdoc-openapi-webflux-ui/pom.xml @@ -3,7 +3,7 @@ org.springdoc springdoc-openapi - 1.6.13 + 1.6.14 springdoc-openapi-webflux-ui diff --git a/springdoc-openapi-webmvc-core/pom.xml b/springdoc-openapi-webmvc-core/pom.xml index 9192e5917..173705224 100644 --- a/springdoc-openapi-webmvc-core/pom.xml +++ b/springdoc-openapi-webmvc-core/pom.xml @@ -3,7 +3,7 @@ org.springdoc springdoc-openapi - 1.6.13 + 1.6.14 springdoc-openapi-webmvc-core diff --git a/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/v30/app114/SpringDocApp114Test.java b/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/v30/app114/SpringDocApp114Test.java index 47b6bb07c..5144735c1 100644 --- a/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/v30/app114/SpringDocApp114Test.java +++ b/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/v30/app114/SpringDocApp114Test.java @@ -22,6 +22,8 @@ package test.org.springdoc.api.v30.app114; +import java.math.BigDecimal; + import javax.money.MonetaryAmount; import org.springdoc.core.SpringDocUtils; @@ -33,7 +35,9 @@ public class SpringDocApp114Test extends AbstractSpringDocV30Test { static { - SpringDocUtils.getConfig().replaceWithClass(MonetaryAmount.class, org.springdoc.core.converters.models.MonetaryAmount.class); + SpringDocUtils.getConfig() + .removeFromSchemaMap(BigDecimal.class) + .replaceWithClass(MonetaryAmount.class, org.springdoc.core.converters.models.MonetaryAmount.class); } @SpringBootApplication diff --git a/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/v30/app164/SampleResponseClass.java b/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/v30/app164/SampleResponseClass.java index ecc647772..e72141e1f 100644 --- a/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/v30/app164/SampleResponseClass.java +++ b/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/v30/app164/SampleResponseClass.java @@ -22,6 +22,11 @@ package test.org.springdoc.api.v30.app164; +import java.util.List; + +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Schema; + public class SampleResponseClass { private String idAsFirstParameter; @@ -32,6 +37,18 @@ public class SampleResponseClass { private boolean booleanValueAsFourthParameter; + @ArraySchema( arraySchema = @Schema( description = "${blahDescription.value}" ), + schema = @Schema( description = "${blahDescription.value}" ) ) + private List listBlah; + + public List getListBlah() { + return listBlah; + } + + public void setListBlah(List listBlah) { + this.listBlah = listBlah; + } + public String getIdAsFirstParameter() { return idAsFirstParameter; } diff --git a/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/v30/app198/HelloController.java b/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/v30/app198/HelloController.java new file mode 100644 index 000000000..6b7b19591 --- /dev/null +++ b/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/v30/app198/HelloController.java @@ -0,0 +1,15 @@ +package test.org.springdoc.api.v30.app198; + +import java.math.BigDecimal; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController("/") +public class HelloController { + + @GetMapping + public Response test() { + return new Response(BigDecimal.ONE, BigDecimal.ONE, BigDecimal.ONE); + } +} \ No newline at end of file diff --git a/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/v30/app198/Response.java b/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/v30/app198/Response.java new file mode 100644 index 000000000..feb9159be --- /dev/null +++ b/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/v30/app198/Response.java @@ -0,0 +1,49 @@ +package test.org.springdoc.api.v30.app198; + +import java.math.BigDecimal; + +import io.swagger.v3.oas.annotations.media.Schema; + +/** + * @author bnasslahsen + */ +public class Response { + + public Response(BigDecimal val1, BigDecimal val2, BigDecimal val3) { + this.val1 = val1; + this.val2 = val2; + this.val3 = val3; + } + + @Schema(deprecated = true) + @Deprecated + private BigDecimal val1; + + private BigDecimal val2; + + private BigDecimal val3; + + public BigDecimal getVal1() { + return val1; + } + + public void setVal1(BigDecimal val1) { + this.val1 = val1; + } + + public BigDecimal getVal2() { + return val2; + } + + public void setVal2(BigDecimal val2) { + this.val2 = val2; + } + + public BigDecimal getVal3() { + return val3; + } + + public void setVal3(BigDecimal val3) { + this.val3 = val3; + } +} diff --git a/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/v30/app198/SpringDocApp198Test.java b/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/v30/app198/SpringDocApp198Test.java new file mode 100644 index 000000000..c968c9cf1 --- /dev/null +++ b/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/v30/app198/SpringDocApp198Test.java @@ -0,0 +1,45 @@ +/* + * + * * + * * * + * * * * 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.app198; + +import java.math.BigDecimal; + +import io.swagger.v3.oas.models.media.Schema; +import org.springdoc.core.SpringDocUtils; +import test.org.springdoc.api.v30.AbstractSpringDocV30Test; + +import org.springframework.boot.autoconfigure.SpringBootApplication; + +public class SpringDocApp198Test extends AbstractSpringDocV30Test { + + static { + SpringDocUtils.getConfig().replaceWithSchema( + BigDecimal.class, + new Schema().type("string").format("decimal") + ); + } + + @SpringBootApplication + static class SpringDocTestApp {} + +} diff --git a/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/v30/app199/CustomExceptionHandler.java b/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/v30/app199/CustomExceptionHandler.java new file mode 100644 index 000000000..87d700197 --- /dev/null +++ b/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/v30/app199/CustomExceptionHandler.java @@ -0,0 +1,23 @@ +package test.org.springdoc.api.v30.app199; + +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.ResponseStatus; + +import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR; + +@ControllerAdvice +public class CustomExceptionHandler { + + static public class MyInternalException extends Exception {} + + @ResponseStatus( value = INTERNAL_SERVER_ERROR ) + @ExceptionHandler( MyInternalException.class ) + @ResponseBody + public ErrorDto handleMyInternalException( final MyInternalException ex ) { + return new ErrorDto( ex.getMessage() ); + } + + +} diff --git a/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/v30/app199/ErrorDto.java b/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/v30/app199/ErrorDto.java new file mode 100644 index 000000000..20b4a0e73 --- /dev/null +++ b/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/v30/app199/ErrorDto.java @@ -0,0 +1,12 @@ +package test.org.springdoc.api.v30.app199; + +public class ErrorDto { + private String message; + + public ErrorDto( final String message ) { + this.message = message; + } + + public String getMessage() { return message; } + public void setMessage( final String message ) { this.message = message; } +} diff --git a/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/v30/app199/HelloController.java b/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/v30/app199/HelloController.java new file mode 100644 index 000000000..6054b7dcc --- /dev/null +++ b/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/v30/app199/HelloController.java @@ -0,0 +1,106 @@ +/* + * + * * + * * * + * * * * 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.app199; + + +import java.util.LinkedHashMap; +import java.util.Map; + +import com.fasterxml.jackson.databind.ObjectMapper; +import io.swagger.v3.oas.models.Operation; +import io.swagger.v3.oas.models.examples.Example; +import org.springdoc.core.customizers.OperationCustomizer; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.method.HandlerMethod; + +import static org.springframework.http.MediaType.ALL_VALUE; +import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; + +@RestController +public class HelloController { + + @Autowired + ObjectMapper defaultObjectMapper; + + @GetMapping( + value = "/first", + produces = APPLICATION_JSON_VALUE + ) + public void first() {} + + @GetMapping( + value = "/second", + produces = APPLICATION_JSON_VALUE + ) + public void second() {} + + @Bean + public OperationCustomizer operationCustomizer() + { + return ( Operation operation, HandlerMethod handlerMethod ) -> + { + final io.swagger.v3.oas.models.media.MediaType mediaType = operation + .getResponses() + .get( "500" ) + .getContent() + .get( ALL_VALUE ); + + mediaType.setExamples( mediaType.getExamples() != null + ? mediaType.getExamples() + : new LinkedHashMap<>() ); + + final Map examples = mediaType.getExamples(); + + switch ( handlerMethod.getMethod().getName() ) { + + case "first" : + examples.put( + "First case example", + new Example().value( + defaultObjectMapper.valueToTree( + new ErrorDto( "An ErrorDto sample specific to /first endpoint" ) + ) + ) + ); + break; + + case "second" : + examples.put( + "Second case example", + new Example().value( + defaultObjectMapper.valueToTree( + new ErrorDto( "An ErrorDto sample specific to /second endpoint" ) + ) + ) + ); + break; + } + + return operation; + }; + } +} \ No newline at end of file diff --git a/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/v30/app199/SpringDocApp199Test.java b/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/v30/app199/SpringDocApp199Test.java new file mode 100644 index 000000000..4e526e1d6 --- /dev/null +++ b/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/v30/app199/SpringDocApp199Test.java @@ -0,0 +1,34 @@ +/* + * + * * + * * * + * * * * 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.app199; + +import test.org.springdoc.api.v30.AbstractSpringDocV30Test; + +import org.springframework.boot.autoconfigure.SpringBootApplication; + +public class SpringDocApp199Test extends AbstractSpringDocV30Test { + + @SpringBootApplication + static class SpringDocTestApp {} + +} \ No newline at end of file diff --git a/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/v30/app200/FooBar.java b/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/v30/app200/FooBar.java new file mode 100644 index 000000000..a333aa533 --- /dev/null +++ b/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/v30/app200/FooBar.java @@ -0,0 +1,39 @@ +package test.org.springdoc.api.v30.app200; + + +import javax.annotation.Generated; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +@Generated(value = "org.openapitools.codegen.languages.SpringCodegen") +public enum FooBar { + FOO("foo"), + BAR("bar"); + + private String value; + + FooBar(String value) { + this.value = value; + } + + @JsonValue + public String getValue() { + return value; + } + + @Override + public String toString() { + return String.valueOf(value); + } + + @JsonCreator + public static FooBar fromValue(String value) { + for (FooBar b : FooBar.values()) { + if (b.value.equals(value)) { + return b; + } + } + throw new IllegalArgumentException("Unexpected value '" + value + "'"); + } +} \ No newline at end of file diff --git a/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/v30/app200/FooBarController.java b/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/v30/app200/FooBarController.java new file mode 100644 index 000000000..d979a032c --- /dev/null +++ b/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/v30/app200/FooBarController.java @@ -0,0 +1,14 @@ +package test.org.springdoc.api.v30.app200; + + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class FooBarController { + @GetMapping(value = "/example/{fooBar}") + public String getFooBar(@PathVariable FooBar fooBar) { + return fooBar.name(); + } +} \ No newline at end of file diff --git a/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/v30/app200/FooBarConverter.java b/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/v30/app200/FooBarConverter.java new file mode 100644 index 000000000..123558637 --- /dev/null +++ b/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/v30/app200/FooBarConverter.java @@ -0,0 +1,15 @@ +package test.org.springdoc.api.v30.app200; + + +import org.springframework.core.convert.converter.Converter; +import org.springframework.stereotype.Component; + +@Component +public class FooBarConverter implements Converter { + + @Override + public FooBar convert(String source) { + return FooBar.fromValue(source); + } + +} \ No newline at end of file diff --git a/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/v30/app200/SpringDocApp200Test.java b/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/v30/app200/SpringDocApp200Test.java new file mode 100644 index 000000000..c2055b1d6 --- /dev/null +++ b/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/v30/app200/SpringDocApp200Test.java @@ -0,0 +1,34 @@ +/* + * + * * + * * * + * * * * 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.app200; + +import test.org.springdoc.api.v30.AbstractSpringDocV30Test; + +import org.springframework.boot.autoconfigure.SpringBootApplication; + +public class SpringDocApp200Test extends AbstractSpringDocV30Test { + + @SpringBootApplication + static class SpringDocTestApp {} + +} \ No newline at end of file diff --git a/springdoc-openapi-webmvc-core/src/test/resources/application-test.properties b/springdoc-openapi-webmvc-core/src/test/resources/application-test.properties index b88c99251..a2ac8faaf 100644 --- a/springdoc-openapi-webmvc-core/src/test/resources/application-test.properties +++ b/springdoc-openapi-webmvc-core/src/test/resources/application-test.properties @@ -1,3 +1,4 @@ spring.main.banner-mode=off logging.level.root=OFF -spring.main.lazy-initialization=true \ No newline at end of file +spring.main.lazy-initialization=true +blahDescription.value=test \ No newline at end of file diff --git a/springdoc-openapi-webmvc-core/src/test/resources/results/3.0.1/app164.json b/springdoc-openapi-webmvc-core/src/test/resources/results/3.0.1/app164.json index c6720a02b..2300c41b9 100644 --- a/springdoc-openapi-webmvc-core/src/test/resources/results/3.0.1/app164.json +++ b/springdoc-openapi-webmvc-core/src/test/resources/results/3.0.1/app164.json @@ -48,9 +48,17 @@ }, "booleanValueAsFourthParameter": { "type": "boolean" + }, + "listBlah": { + "type": "array", + "description": "test", + "items": { + "type": "string", + "description": "test" + } } } } } } -} \ No newline at end of file +} diff --git a/springdoc-openapi-webmvc-core/src/test/resources/results/3.0.1/app198.json b/springdoc-openapi-webmvc-core/src/test/resources/results/3.0.1/app198.json new file mode 100644 index 000000000..7a0d6620f --- /dev/null +++ b/springdoc-openapi-webmvc-core/src/test/resources/results/3.0.1/app198.json @@ -0,0 +1,57 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "OpenAPI definition", + "version": "v0" + }, + "servers": [ + { + "url": "http://localhost", + "description": "Generated server url" + } + ], + "paths": { + "/": { + "get": { + "tags": [ + "hello-controller" + ], + "operationId": "test", + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/Response" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "Response": { + "type": "object", + "properties": { + "val1": { + "type": "string", + "format": "decimal", + "deprecated": true + }, + "val2": { + "type": "string", + "format": "decimal" + }, + "val3": { + "type": "string", + "format": "decimal" + } + } + } + } + } +} \ No newline at end of file diff --git a/springdoc-openapi-webmvc-core/src/test/resources/results/3.0.1/app199.json b/springdoc-openapi-webmvc-core/src/test/resources/results/3.0.1/app199.json new file mode 100644 index 000000000..7471940f0 --- /dev/null +++ b/springdoc-openapi-webmvc-core/src/test/resources/results/3.0.1/app199.json @@ -0,0 +1,87 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "OpenAPI definition", + "version": "v0" + }, + "servers": [ + { + "url": "http://localhost", + "description": "Generated server url" + } + ], + "paths": { + "/second": { + "get": { + "tags": [ + "hello-controller" + ], + "operationId": "second", + "responses": { + "500": { + "description": "Internal Server Error", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/ErrorDto" + }, + "examples": { + "Second case example": { + "value": { + "message": "An ErrorDto sample specific to /second endpoint" + } + } + } + } + } + }, + "200": { + "description": "OK" + } + } + } + }, + "/first": { + "get": { + "tags": [ + "hello-controller" + ], + "operationId": "first", + "responses": { + "500": { + "description": "Internal Server Error", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/ErrorDto" + }, + "examples": { + "First case example": { + "value": { + "message": "An ErrorDto sample specific to /first endpoint" + } + } + } + } + } + }, + "200": { + "description": "OK" + } + } + } + } + }, + "components": { + "schemas": { + "ErrorDto": { + "type": "object", + "properties": { + "message": { + "type": "string" + } + } + } + } + } +} \ No newline at end of file diff --git a/springdoc-openapi-webmvc-core/src/test/resources/results/3.0.1/app200.json b/springdoc-openapi-webmvc-core/src/test/resources/results/3.0.1/app200.json new file mode 100644 index 000000000..b64c8dc8b --- /dev/null +++ b/springdoc-openapi-webmvc-core/src/test/resources/results/3.0.1/app200.json @@ -0,0 +1,50 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "OpenAPI definition", + "version": "v0" + }, + "servers": [ + { + "url": "http://localhost", + "description": "Generated server url" + } + ], + "paths": { + "/example/{fooBar}": { + "get": { + "tags": [ + "foo-bar-controller" + ], + "operationId": "getFooBar", + "parameters": [ + { + "name": "fooBar", + "in": "path", + "required": true, + "schema": { + "type": "string", + "enum": [ + "foo", + "bar" + ] + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "type": "string" + } + } + } + } + } + } + } + }, + "components": {} +}