org.springdoc.core.AbstractRequestService Maven / Gradle / Ivy
The newest version!
/*
*
* *
* * *
* * * * 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 org.springdoc.core;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.validation.constraints.DecimalMax;
import javax.validation.constraints.DecimalMin;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;
import com.fasterxml.jackson.annotation.JsonView;
import io.swagger.v3.core.util.PrimitiveType;
import io.swagger.v3.oas.annotations.enums.ParameterIn;
import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.Operation;
import io.swagger.v3.oas.models.media.Content;
import io.swagger.v3.oas.models.media.MediaType;
import io.swagger.v3.oas.models.media.Schema;
import io.swagger.v3.oas.models.media.StringSchema;
import io.swagger.v3.oas.models.parameters.Parameter;
import io.swagger.v3.oas.models.parameters.RequestBody;
import org.apache.commons.lang3.StringUtils;
import org.springdoc.core.SpringDocConfigProperties.ApiDocs.OpenApiVersion;
import org.springdoc.core.customizers.ParameterCustomizer;
import org.springdoc.core.providers.JavadocProvider;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.http.HttpMethod;
import org.springframework.util.CollectionUtils;
import org.springframework.validation.BindingResult;
import org.springframework.validation.Errors;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestAttribute;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ValueConstants;
import org.springframework.web.bind.support.SessionStatus;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.util.UriComponentsBuilder;
import static org.springdoc.core.Constants.OPENAPI_ARRAY_TYPE;
import static org.springdoc.core.Constants.OPENAPI_STRING_TYPE;
import static org.springdoc.core.GenericParameterService.isFile;
import static org.springdoc.core.converters.SchemaPropertyDeprecatingConverter.containsDeprecatedAnnotation;
/**
* The type Abstract request builder.
* @author bnasslahsen
*/
public abstract class AbstractRequestService {
/**
* The constant PARAM_TYPES_TO_IGNORE.
*/
private static final List> PARAM_TYPES_TO_IGNORE = Collections.synchronizedList(new ArrayList<>());
/**
* The constant ANNOTATIONS_FOR_REQUIRED.
*/
// using string litterals to support both validation-api v1 and v2
private static final String[] ANNOTATIONS_FOR_REQUIRED = { "NotNull", "NonNull", "NotBlank", "NotEmpty" };
/**
* The constant POSITIVE_OR_ZERO.
*/
private static final String POSITIVE_OR_ZERO = "PositiveOrZero";
/**
* The constant NEGATIVE_OR_ZERO.
*/
private static final String NEGATIVE_OR_ZERO = "NegativeOrZero";
static {
PARAM_TYPES_TO_IGNORE.add(WebRequest.class);
PARAM_TYPES_TO_IGNORE.add(NativeWebRequest.class);
PARAM_TYPES_TO_IGNORE.add(java.security.Principal.class);
PARAM_TYPES_TO_IGNORE.add(HttpMethod.class);
PARAM_TYPES_TO_IGNORE.add(java.util.Locale.class);
PARAM_TYPES_TO_IGNORE.add(java.util.TimeZone.class);
PARAM_TYPES_TO_IGNORE.add(java.io.InputStream.class);
PARAM_TYPES_TO_IGNORE.add(java.time.ZoneId.class);
PARAM_TYPES_TO_IGNORE.add(java.io.Reader.class);
PARAM_TYPES_TO_IGNORE.add(java.io.OutputStream.class);
PARAM_TYPES_TO_IGNORE.add(java.io.Writer.class);
PARAM_TYPES_TO_IGNORE.add(java.util.Map.class);
PARAM_TYPES_TO_IGNORE.add(org.springframework.ui.Model.class);
PARAM_TYPES_TO_IGNORE.add(org.springframework.ui.ModelMap.class);
PARAM_TYPES_TO_IGNORE.add(Errors.class);
PARAM_TYPES_TO_IGNORE.add(BindingResult.class);
PARAM_TYPES_TO_IGNORE.add(SessionStatus.class);
PARAM_TYPES_TO_IGNORE.add(UriComponentsBuilder.class);
PARAM_TYPES_TO_IGNORE.add(RequestAttribute.class);
}
/**
* The Parameter builder.
*/
private final GenericParameterService parameterBuilder;
/**
* The Request body builder.
*/
private final RequestBodyService requestBodyService;
/**
* The Operation builder.
*/
private final OperationService operationService;
/**
* The Local spring doc parameter name discoverer.
*/
private final LocalVariableTableParameterNameDiscoverer localSpringDocParameterNameDiscoverer;
/**
* The Parameter customizers.
*/
private final Optional> parameterCustomizers;
/**
* The Default flat param object.
*/
private final boolean defaultFlatParamObject;
/**
* The Default support form data.
*/
private boolean defaultSupportFormData;
/**
* Instantiates a new Abstract request builder.
*
* @param parameterBuilder the parameter builder
* @param requestBodyService the request body builder
* @param operationService the operation builder
* @param parameterCustomizers the parameter customizers
* @param localSpringDocParameterNameDiscoverer the local spring doc parameter name discoverer
*/
protected AbstractRequestService(GenericParameterService parameterBuilder, RequestBodyService requestBodyService, OperationService operationService, Optional> parameterCustomizers, LocalVariableTableParameterNameDiscoverer localSpringDocParameterNameDiscoverer) {
super();
this.parameterBuilder = parameterBuilder;
this.requestBodyService = requestBodyService;
this.operationService = operationService;
parameterCustomizers.ifPresent(customizers -> customizers.removeIf(Objects::isNull));
this.parameterCustomizers = parameterCustomizers;
this.localSpringDocParameterNameDiscoverer = localSpringDocParameterNameDiscoverer;
this.defaultFlatParamObject = parameterBuilder.getPropertyResolverUtils().getSpringDocConfigProperties().isDefaultFlatParamObject();
this.defaultSupportFormData = parameterBuilder.getPropertyResolverUtils().getSpringDocConfigProperties().isDefaultSupportFormData();
}
/**
* Add request wrapper to ignore.
*
* @param classes the classes
*/
public static void addRequestWrapperToIgnore(Class>... classes) {
PARAM_TYPES_TO_IGNORE.addAll(Arrays.asList(classes));
}
/**
* Remove request wrapper to ignore.
*
* @param classes the classes
*/
public static void removeRequestWrapperToIgnore(Class>... classes) {
List> classesToIgnore = Arrays.asList(classes);
if (PARAM_TYPES_TO_IGNORE.containsAll(classesToIgnore))
PARAM_TYPES_TO_IGNORE.removeAll(Arrays.asList(classes));
}
/**
* Is request type to ignore boolean.
*
* @param rawClass the raw class
* @return the boolean
*/
public static boolean isRequestTypeToIgnore(Class> rawClass) {
return PARAM_TYPES_TO_IGNORE.stream().anyMatch(clazz -> clazz.isAssignableFrom(rawClass));
}
/**
* Gets headers.
*
* @param methodAttributes the method attributes
* @param map the map
* @return the headers
*/
@SuppressWarnings("unchecked")
public static Collection getHeaders(MethodAttributes methodAttributes, Map map) {
for (Map.Entry entry : methodAttributes.getHeaders().entrySet()) {
StringSchema schema = new StringSchema();
if (StringUtils.isNotEmpty(entry.getValue()))
schema.addEnumItem(entry.getValue());
Parameter parameter = new Parameter().in(ParameterIn.HEADER.toString()).name(entry.getKey()).schema(schema);
ParameterId parameterId = new ParameterId(parameter);
if (map.containsKey(parameterId)) {
parameter = map.get(parameterId);
List existingEnum = null;
if (parameter.getSchema() != null && !CollectionUtils.isEmpty(parameter.getSchema().getEnum()))
existingEnum = parameter.getSchema().getEnum();
if (StringUtils.isNotEmpty(entry.getValue()) && (existingEnum == null || !existingEnum.contains(entry.getValue())))
parameter.getSchema().addEnumItemObject(entry.getValue());
parameter.setSchema(parameter.getSchema());
}
map.put(parameterId, parameter);
}
return map.values();
}
/**
* Build operation.
*
* @param handlerMethod the handler method
* @param requestMethod the request method
* @param operation the operation
* @param methodAttributes the method attributes
* @param openAPI the open api
* @return the operation
*/
public Operation build(HandlerMethod handlerMethod, RequestMethod requestMethod, Operation operation, MethodAttributes methodAttributes, OpenAPI openAPI) {
// Documentation
String operationId = operationService.getOperationId(handlerMethod.getMethod().getName(), operation.getOperationId(), openAPI);
operation.setOperationId(operationId);
// requests
String[] pNames = this.localSpringDocParameterNameDiscoverer.getParameterNames(handlerMethod.getMethod());
MethodParameter[] parameters = handlerMethod.getMethodParameters();
String[] reflectionParametersNames = Arrays.stream(handlerMethod.getMethod().getParameters()).map(java.lang.reflect.Parameter::getName).toArray(String[]::new);
if (pNames == null || Arrays.stream(pNames).anyMatch(Objects::isNull))
pNames = reflectionParametersNames;
parameters = DelegatingMethodParameter.customize(pNames, parameters, parameterBuilder.getDelegatingMethodParameterCustomizer(), this.defaultFlatParamObject);
RequestBodyInfo requestBodyInfo = new RequestBodyInfo();
List operationParameters = (operation.getParameters() != null) ? operation.getParameters() : new ArrayList<>();
Map parametersDocMap = getApiParameters(handlerMethod.getMethod());
Components components = openAPI.getComponents();
JavadocProvider javadocProvider = parameterBuilder.getJavadocProvider();
for (MethodParameter methodParameter : parameters) {
// check if query param
Parameter parameter;
io.swagger.v3.oas.annotations.Parameter parameterDoc = AnnotatedElementUtils.findMergedAnnotation(AnnotatedElementUtils.forAnnotations(methodParameter.getParameterAnnotations()), io.swagger.v3.oas.annotations.Parameter.class);
final String pName = methodParameter.getParameterName();
ParameterInfo parameterInfo = new ParameterInfo(pName, methodParameter, parameterBuilder, parameterDoc);
if (parameterDoc == null)
parameterDoc = parametersDocMap.get(parameterInfo.getParameterId());
if (parameterDoc == null) {
io.swagger.v3.oas.annotations.media.Schema schema = AnnotatedElementUtils.findMergedAnnotation(AnnotatedElementUtils.forAnnotations(methodParameter.getParameterAnnotations()), io.swagger.v3.oas.annotations.media.Schema.class);
if (schema != null) {
parameterDoc = parameterBuilder.generateParameterBySchema(schema);
}
}
// use documentation as reference
if (parameterDoc != null) {
if (parameterDoc.hidden() || parameterDoc.schema().hidden()) continue;
parameter = parameterBuilder.buildParameterFromDoc(parameterDoc, components, methodAttributes.getJsonViewAnnotation(), methodAttributes.getLocale());
parameterInfo.setParameterModel(parameter);
}
if (!isParamToIgnore(methodParameter)) {
parameter = buildParams(parameterInfo, components, requestMethod, methodAttributes.getJsonViewAnnotation(), openAPI.getOpenapi());
// Merge with the operation parameters
parameter = GenericParameterService.mergeParameter(operationParameters, parameter);
List parameterAnnotations = Arrays.asList(methodParameter.getParameterAnnotations());
if (isValidParameter(parameter)) {
// Add param javadoc
if (StringUtils.isBlank(parameter.getDescription()) && javadocProvider != null) {
String paramJavadocDescription = parameterBuilder.getParamJavadoc(javadocProvider, methodParameter);
if (!StringUtils.isBlank(paramJavadocDescription)) {
parameter.setDescription(paramJavadocDescription);
}
}
applyBeanValidatorAnnotations(parameter, parameterAnnotations);
}
else if (!RequestMethod.GET.equals(requestMethod) || OpenApiVersion.OPENAPI_3_1.getVersion().equals(openAPI.getOpenapi())) {
if (operation.getRequestBody() != null)
requestBodyInfo.setRequestBody(operation.getRequestBody());
requestBodyService.calculateRequestBodyInfo(components, methodAttributes, parameterInfo, requestBodyInfo);
applyBeanValidatorAnnotations(requestBodyInfo.getRequestBody(), parameterAnnotations, methodParameter.isOptional());
}
customiseParameter(parameter, parameterInfo, operationParameters);
}
}
LinkedHashMap map = getParameterLinkedHashMap(components, methodAttributes, operationParameters, parametersDocMap);
RequestBody requestBody = requestBodyInfo.getRequestBody();
// support form-data
if (defaultSupportFormData && requestBody != null && requestBody.getContent() != null && requestBody.getContent().containsKey(org.springframework.http.MediaType.MULTIPART_FORM_DATA_VALUE)) {
Iterator> it = map.entrySet().iterator();
while (it.hasNext()) {
Entry entry = it.next();
Parameter parameter = entry.getValue();
if (!ParameterIn.PATH.toString().equals(parameter.getIn()) && !ParameterIn.HEADER.toString().equals(parameter.getIn()) && !ParameterIn.COOKIE.toString().equals(parameter.getIn())) {
io.swagger.v3.oas.models.media.Schema> itemSchema = new io.swagger.v3.oas.models.media.Schema<>();
itemSchema.setName(entry.getKey().getpName());
itemSchema.setDescription(parameter.getDescription());
itemSchema.setDeprecated(parameter.getDeprecated());
if (parameter.getExample() != null)
itemSchema.setExample(parameter.getExample());
requestBodyInfo.addProperties(entry.getKey().getpName(), itemSchema);
it.remove();
}
}
}
setParams(operation, new ArrayList<>(map.values()), requestBodyInfo);
return operation;
}
/**
* Gets parameter linked hash map.
*
* @param components the components
* @param methodAttributes the method attributes
* @param operationParameters the operation parameters
* @param parametersDocMap the parameters doc map
* @return the parameter linked hash map
*/
private LinkedHashMap getParameterLinkedHashMap(Components components, MethodAttributes methodAttributes, List operationParameters, Map parametersDocMap) {
LinkedHashMap map = operationParameters.stream().collect(Collectors.toMap(ParameterId::new, parameter -> parameter, (u, v) -> {
throw new IllegalStateException(String.format("Duplicate key %s", u));
}, LinkedHashMap::new));
for (Map.Entry entry : parametersDocMap.entrySet()) {
ParameterId parameterId = entry.getKey();
if (parameterId != null && !map.containsKey(parameterId) && !entry.getValue().hidden()) {
Parameter parameter = parameterBuilder.buildParameterFromDoc(entry.getValue(), components, methodAttributes.getJsonViewAnnotation(), methodAttributes.getLocale());
//proceed with the merge if possible
if (map.containsKey(parameterId)) {
GenericParameterService.mergeParameter(map.get(parameterId), parameter);
map.put(parameterId, parameter);
}
else {
long mumParamsWithName = map.keySet().stream().filter(parameterId1 -> parameterId.getpName().equals(parameterId1.getpName())).count();
long mumParamsDocWithName = parametersDocMap.keySet().stream().filter(parameterId1 -> parameterId.getpName().equals(parameterId1.getpName())).count();
if (mumParamsWithName == 1 && mumParamsDocWithName == 1) {
Optional parameterIdWithSameNameOptional = map.keySet().stream().filter(parameterId1 -> parameterId.getpName().equals(parameterId1.getpName())).findAny();
parameterIdWithSameNameOptional.ifPresent(parameterIdWithSameName -> {
GenericParameterService.mergeParameter(map.get(parameterIdWithSameName), parameter);
map.put(parameterIdWithSameName, parameter);
});
}
else
map.put(parameterId, parameter);
}
}
}
getHeaders(methodAttributes, map);
map.forEach((parameterId, parameter) -> {
if (StringUtils.isBlank(parameter.getIn()) && StringUtils.isBlank(parameter.get$ref()))
parameter.setIn(ParameterIn.QUERY.toString());
});
return map;
}
/**
* Customise parameter parameter.
*
* @param parameter the parameter
* @param parameterInfo the parameter info
* @param operationParameters the operation parameters
*/
protected void customiseParameter(Parameter parameter, ParameterInfo parameterInfo, List operationParameters) {
if (parameterCustomizers.isPresent()) {
List parameterCustomizerList = parameterCustomizers.get();
int index = operationParameters.indexOf(parameter);
for (ParameterCustomizer parameterCustomizer : parameterCustomizerList)
parameter = parameterCustomizer.customize(parameter, parameterInfo.getMethodParameter());
if (index != -1) operationParameters.set(index, parameter);
}
}
/**
* Is param to ignore boolean.
*
* @param parameter the parameter
* @return the boolean
*/
public boolean isParamToIgnore(MethodParameter parameter) {
if (SpringDocAnnotationsUtils.isAnnotationToIgnore(parameter)) return true;
if (isRequiredAnnotation(parameter)) return false;
return isRequestTypeToIgnore(parameter.getParameterType());
}
/**
* Is required annotation boolean.
*
* @param parameter the parameter
* @return the boolean
*/
private boolean isRequiredAnnotation(MethodParameter parameter) {
RequestParam requestParam = parameter.getParameterAnnotation(RequestParam.class);
PathVariable pathVariable = parameter.getParameterAnnotation(PathVariable.class);
org.springframework.web.bind.annotation.RequestBody requestBody = parameter.getParameterAnnotation(org.springframework.web.bind.annotation.RequestBody.class);
return (requestParam != null && requestParam.required()) || (pathVariable != null && pathVariable.required()) || (requestBody != null && requestBody.required());
}
/**
* Sets params.
*
* @param operation the operation
* @param operationParameters the operation parameters
* @param requestBodyInfo the request body info
*/
private void setParams(Operation operation, List operationParameters, RequestBodyInfo requestBodyInfo) {
if (!CollectionUtils.isEmpty(operationParameters))
operation.setParameters(operationParameters);
if (requestBodyInfo.getRequestBody() != null)
operation.setRequestBody(requestBodyInfo.getRequestBody());
}
/**
* Is valid parameter boolean.
*
* @param parameter the parameter
* @return the boolean
*/
public boolean isValidParameter(Parameter parameter) {
return parameter != null && (parameter.getName() != null || parameter.get$ref() != null);
}
/**
* 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
* @return the parameter
*/
public Parameter buildParams(ParameterInfo parameterInfo, Components components, RequestMethod requestMethod, JsonView jsonView, 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);
}
// By default
if (!isRequestBodyParam(requestMethod, parameterInfo, openApiVersion)) {
parameterInfo.setRequired(!((DelegatingMethodParameter) methodParameter).isNotRequired() && !methodParameter.isOptional());
parameterInfo.setDefaultValue(null);
return this.buildParam(parameterInfo, components, jsonView);
}
return null;
}
/**
* Build param parameter.
*
* @param parameterInfo the parameter info
* @param components the components
* @param jsonView the json view
* @return the parameter
*/
public Parameter buildParam(ParameterInfo parameterInfo, Components components, JsonView jsonView) {
Parameter parameter = parameterInfo.getParameterModel();
String name = parameterInfo.getpName();
if (parameter == null) {
parameter = new Parameter();
parameterInfo.setParameterModel(parameter);
}
if (StringUtils.isBlank(parameter.getName())) parameter.setName(name);
if (StringUtils.isBlank(parameter.getIn()))
parameter.setIn(parameterInfo.getParamType());
if (parameter.getRequired() == null)
parameter.setRequired(parameterInfo.isRequired());
if (containsDeprecatedAnnotation(parameterInfo.getMethodParameter().getParameterAnnotations()))
parameter.setDeprecated(true);
if (parameter.getSchema() == null && parameter.getContent() == null) {
Schema> schema = parameterBuilder.calculateSchema(components, parameterInfo, null, jsonView);
if (parameterInfo.getDefaultValue() != null && schema != null) {
Object defaultValue = parameterInfo.getDefaultValue();
// Cast default value
PrimitiveType primitiveType = PrimitiveType.fromTypeAndFormat(schema.getType(), schema.getFormat());
if (primitiveType != null) {
Schema> primitiveSchema = primitiveType.createProperty();
primitiveSchema.setDefault(parameterInfo.getDefaultValue());
defaultValue = primitiveSchema.getDefault();
}
schema.setDefault(defaultValue);
}
parameter.setSchema(schema);
}
return parameter;
}
/**
* Apply bean validator annotations.
*
* @param parameter the parameter
* @param annotations the annotations
*/
public void applyBeanValidatorAnnotations(final Parameter parameter, final List annotations) {
Map annos = new HashMap<>();
if (annotations != null)
annotations.forEach(annotation -> annos.put(annotation.annotationType().getSimpleName(), annotation));
boolean annotationExists = Arrays.stream(ANNOTATIONS_FOR_REQUIRED).anyMatch(annos::containsKey);
if (annotationExists) parameter.setRequired(true);
Schema> schema = parameter.getSchema();
applyValidationsToSchema(annos, schema);
}
/**
* Apply bean validator annotations.
*
* @param requestBody the request body
* @param annotations the annotations
* @param isOptional the is optional
*/
public void applyBeanValidatorAnnotations(final RequestBody requestBody, final List annotations, boolean isOptional) {
Map annos = new HashMap<>();
boolean requestBodyRequired = false;
if (!CollectionUtils.isEmpty(annotations)) {
annotations.forEach(annotation -> annos.put(annotation.annotationType().getSimpleName(), annotation));
requestBodyRequired = annotations.stream().filter(annotation -> org.springframework.web.bind.annotation.RequestBody.class.equals(annotation.annotationType())).anyMatch(annotation -> ((org.springframework.web.bind.annotation.RequestBody) annotation).required());
}
boolean validationExists = Arrays.stream(ANNOTATIONS_FOR_REQUIRED).anyMatch(annos::containsKey);
if (validationExists || (!isOptional && requestBodyRequired))
requestBody.setRequired(true);
Content content = requestBody.getContent();
for (MediaType mediaType : content.values()) {
Schema> schema = mediaType.getSchema();
applyValidationsToSchema(annos, schema);
}
}
/**
* Gets request body builder.
*
* @return the request body builder
*/
public RequestBodyService getRequestBodyBuilder() {
return requestBodyService;
}
/**
* Is default flat param object boolean.
*
* @return the boolean
*/
public boolean isDefaultFlatParamObject() {
return defaultFlatParamObject;
}
/**
* Calculate size.
*
* @param annos the annos
* @param schema the schema
*/
private void calculateSize(Map annos, Schema> schema) {
if (annos.containsKey(Size.class.getSimpleName())) {
Size size = (Size) annos.get(Size.class.getSimpleName());
if (OPENAPI_ARRAY_TYPE.equals(schema.getType())) {
schema.setMinItems(size.min());
schema.setMaxItems(size.max());
}
else if (OPENAPI_STRING_TYPE.equals(schema.getType())) {
schema.setMinLength(size.min());
schema.setMaxLength(size.max());
}
}
}
/**
* Gets api parameters.
*
* @param method the method
* @return the api parameters
*/
private Map getApiParameters(Method method) {
Class> declaringClass = method.getDeclaringClass();
Set apiParametersDoc = AnnotatedElementUtils.findAllMergedAnnotations(method, io.swagger.v3.oas.annotations.Parameters.class);
LinkedHashMap apiParametersMap = apiParametersDoc.stream().flatMap(x -> Stream.of(x.value())).collect(Collectors.toMap(ParameterId::new, x -> x, (e1, e2) -> e2, LinkedHashMap::new));
Set apiParametersDocDeclaringClass = AnnotatedElementUtils.findAllMergedAnnotations(declaringClass, io.swagger.v3.oas.annotations.Parameters.class);
LinkedHashMap apiParametersDocDeclaringClassMap = apiParametersDocDeclaringClass.stream().flatMap(x -> Stream.of(x.value())).collect(Collectors.toMap(ParameterId::new, x -> x, (e1, e2) -> e2, LinkedHashMap::new));
apiParametersMap.putAll(apiParametersDocDeclaringClassMap);
Set apiParameterDoc = AnnotatedElementUtils.findAllMergedAnnotations(method, io.swagger.v3.oas.annotations.Parameter.class);
LinkedHashMap apiParameterDocMap = apiParameterDoc.stream().collect(Collectors.toMap(ParameterId::new, x -> x, (e1, e2) -> e2, LinkedHashMap::new));
apiParametersMap.putAll(apiParameterDocMap);
Set apiParameterDocDeclaringClass = AnnotatedElementUtils.findAllMergedAnnotations(declaringClass, io.swagger.v3.oas.annotations.Parameter.class);
LinkedHashMap apiParameterDocDeclaringClassMap = apiParameterDocDeclaringClass.stream().collect(Collectors.toMap(ParameterId::new, x -> x, (e1, e2) -> e2, LinkedHashMap::new));
apiParametersMap.putAll(apiParameterDocDeclaringClassMap);
return apiParametersMap;
}
/**
* Apply validations to schema.
*
* @param annos the annos
* @param schema the schema
*/
private void applyValidationsToSchema(Map annos, Schema> schema) {
if (annos.containsKey(Min.class.getSimpleName())) {
Min min = (Min) annos.get(Min.class.getSimpleName());
schema.setMinimum(BigDecimal.valueOf(min.value()));
}
if (annos.containsKey(Max.class.getSimpleName())) {
Max max = (Max) annos.get(Max.class.getSimpleName());
schema.setMaximum(BigDecimal.valueOf(max.value()));
}
calculateSize(annos, schema);
if (annos.containsKey(DecimalMin.class.getSimpleName())) {
DecimalMin min = (DecimalMin) annos.get(DecimalMin.class.getSimpleName());
if (min.inclusive())
schema.setMinimum(BigDecimal.valueOf(Double.parseDouble(min.value())));
else schema.setExclusiveMinimum(true);
}
if (annos.containsKey(DecimalMax.class.getSimpleName())) {
DecimalMax max = (DecimalMax) annos.get(DecimalMax.class.getSimpleName());
if (max.inclusive())
schema.setMaximum(BigDecimal.valueOf(Double.parseDouble(max.value())));
else schema.setExclusiveMaximum(true);
}
if (annos.containsKey(POSITIVE_OR_ZERO)) schema.setMinimum(BigDecimal.ZERO);
if (annos.containsKey(NEGATIVE_OR_ZERO)) schema.setMaximum(BigDecimal.ZERO);
if (annos.containsKey(Pattern.class.getSimpleName())) {
Pattern pattern = (Pattern) annos.get(Pattern.class.getSimpleName());
schema.setPattern(pattern.regexp());
}
}
/**
* Is RequestBody param boolean.
*
* @param requestMethod the request method
* @param parameterInfo the parameter info
* @param openApiVersion the open api version
* @return the boolean
*/
private boolean isRequestBodyParam(RequestMethod requestMethod, ParameterInfo parameterInfo, String openApiVersion) {
MethodParameter methodParameter = parameterInfo.getMethodParameter();
DelegatingMethodParameter delegatingMethodParameter = (DelegatingMethodParameter) methodParameter;
boolean isBodyAllowed = !RequestMethod.GET.equals(requestMethod) || OpenApiVersion.OPENAPI_3_1.getVersion().equals(openApiVersion);
return (isBodyAllowed && (parameterInfo.getParameterModel() == null
|| parameterInfo.getParameterModel().getIn() == null) && !delegatingMethodParameter.isParameterObject()) && ((methodParameter.getParameterAnnotation(io.swagger.v3.oas.annotations.parameters.RequestBody.class) != null
|| methodParameter.getParameterAnnotation(org.springframework.web.bind.annotation.RequestBody.class) != null
|| AnnotatedElementUtils.findMergedAnnotation(Objects.requireNonNull(methodParameter.getMethod()), io.swagger.v3.oas.annotations.parameters.RequestBody.class) != null)
|| checkOperationRequestBody(methodParameter)
|| checkFile(methodParameter));
}
/**
* Check file boolean.
*
* @param methodParameter the method parameter
* @return the boolean
*/
private boolean checkFile(MethodParameter methodParameter) {
if (methodParameter.getParameterAnnotation(org.springframework.web.bind.annotation.RequestPart.class) != null)
return true;
else if (methodParameter.getParameterAnnotation(org.springframework.web.bind.annotation.RequestParam.class) != null) {
return isFile(methodParameter.getParameterType());
}
return false;
}
/**
* Check operation request body boolean.
*
* @param methodParameter the method parameter
* @return the boolean
*/
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);
io.swagger.v3.oas.annotations.parameters.RequestBody requestBody = operation.requestBody();
if (StringUtils.isNotBlank(requestBody.description()))
return true;
else if (StringUtils.isNotBlank(requestBody.ref()))
return true;
else if (requestBody.required())
return true;
else if (requestBody.useParameterTypeSchema())
return true;
else if (requestBody.content().length > 0)
return true;
else
return requestBody.extensions().length > 0;
}
return false;
}
}