Skip to content

feat: springfox compatible #1744

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,12 @@

import com.fasterxml.jackson.annotation.JsonView;
import io.swagger.v3.core.util.PrimitiveType;
import io.swagger.v3.oas.annotations.enums.Explode;
import io.swagger.v3.oas.annotations.enums.ParameterIn;
import io.swagger.v3.oas.annotations.enums.ParameterStyle;
import io.swagger.v3.oas.annotations.extensions.Extension;
import io.swagger.v3.oas.annotations.media.ArraySchema;
import io.swagger.v3.oas.annotations.media.ExampleObject;
import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.Operation;
Expand Down Expand Up @@ -164,6 +169,18 @@ public abstract class AbstractRequestService {
*/
private final Optional<List<ParameterCustomizer>> parameterCustomizers;

private final DelegatingMethodParameterProvider delegatingMethodParameterProvider;

/**
*
*/
private final boolean supportFormData;

/**
*
*/
private final boolean schemaToParameter;

/**
* Instantiates a new Abstract request builder.
*
Expand All @@ -175,14 +192,19 @@ public abstract class AbstractRequestService {
*/
protected AbstractRequestService(GenericParameterService parameterBuilder, RequestBodyService requestBodyService,
OperationService operationService, Optional<List<ParameterCustomizer>> parameterCustomizers,
LocalVariableTableParameterNameDiscoverer localSpringDocParameterNameDiscoverer) {
LocalVariableTableParameterNameDiscoverer localSpringDocParameterNameDiscoverer,
DelegatingMethodParameterProvider delegatingMethodParameterProvider,
SpringDocConfigProperties springDocConfigProperties) {
super();
this.parameterBuilder = parameterBuilder;
this.requestBodyService = requestBodyService;
this.operationService = operationService;
parameterCustomizers.ifPresent(customizers -> customizers.removeIf(Objects::isNull));
this.parameterCustomizers = parameterCustomizers;
this.localSpringDocParameterNameDiscoverer = localSpringDocParameterNameDiscoverer;
this.delegatingMethodParameterProvider = delegatingMethodParameterProvider;
this.supportFormData = springDocConfigProperties.isDefaultSupportFormData();
this.schemaToParameter = springDocConfigProperties.isDefaultSupportFormData();
}

/**
Expand Down Expand Up @@ -237,7 +259,7 @@ public Operation build(HandlerMethod handlerMethod, RequestMethod requestMethod,
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());
parameters = delegatingMethodParameterProvider.customize(pNames, parameters, parameterBuilder.getDelegatingMethodParameterCustomizer());
RequestBodyInfo requestBodyInfo = new RequestBodyInfo();
List<Parameter> operationParameters = (operation.getParameters() != null) ? operation.getParameters() : new ArrayList<>();
Map<String, io.swagger.v3.oas.annotations.Parameter> parametersDocMap = getApiParameters(handlerMethod.getMethod());
Expand All @@ -257,6 +279,17 @@ public Operation build(HandlerMethod handlerMethod, RequestMethod requestMethod,

if (parameterDoc == null)
parameterDoc = parametersDocMap.get(parameterInfo.getpName());

if(this.schemaToParameter) {
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 = generateParameterBySchema(schema);
}
}
}

// use documentation as reference
if (parameterDoc != null) {
if (parameterDoc.hidden() || parameterDoc.schema().hidden())
Expand Down Expand Up @@ -300,6 +333,24 @@ else if (!RequestMethod.GET.equals(requestMethod)) {
}

LinkedHashMap<String, Parameter> map = getParameterLinkedHashMap(components, methodAttributes, operationParameters, parametersDocMap);
if(this.supportFormData) {
RequestBody body = requestBodyInfo.getRequestBody();
if (body != null && body.getContent() != null && body.getContent().containsKey("multipart/form-data")) {
Set<String> keys = map.keySet();
io.swagger.v3.oas.models.media.Schema mergedSchema = requestBodyInfo.getMergedSchema();
for (String key : keys) {
Parameter parameter = map.get(key);
io.swagger.v3.oas.models.media.Schema itemSchema = new io.swagger.v3.oas.models.media.Schema();
itemSchema.setName(key);
itemSchema.setDescription(parameter.getDescription());
itemSchema.setDeprecated(parameter.getDeprecated());
itemSchema.setExample(parameter.getExample());
itemSchema.setNullable(parameter.getAllowEmptyValue());
mergedSchema.addProperty(key, itemSchema);
}
map.clear();
}
}
setParams(operation, new ArrayList<>(map.values()), requestBodyInfo);
return operation;
}
Expand All @@ -313,7 +364,7 @@ else if (!RequestMethod.GET.equals(requestMethod)) {
* @param parametersDocMap the parameters doc map
* @return the parameter linked hash map
*/
private LinkedHashMap<String, Parameter> getParameterLinkedHashMap(Components components, MethodAttributes methodAttributes, List<Parameter> operationParameters, Map<String, io.swagger.v3.oas.annotations.Parameter> parametersDocMap) {
protected LinkedHashMap<String, Parameter> getParameterLinkedHashMap(Components components, MethodAttributes methodAttributes, List<Parameter> operationParameters, Map<String, io.swagger.v3.oas.annotations.Parameter> parametersDocMap) {
LinkedHashMap<String, Parameter> map = operationParameters.stream()
.collect(Collectors.toMap(
parameter -> parameter.getName() != null ? parameter.getName() : Integer.toString(parameter.hashCode()),
Expand Down Expand Up @@ -400,7 +451,7 @@ public boolean isParamToIgnore(MethodParameter parameter) {
* @param parameter the parameter
* @return the boolean
*/
private boolean isRequiredAnnotation(MethodParameter parameter) {
protected 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);
Expand All @@ -416,7 +467,7 @@ private boolean isRequiredAnnotation(MethodParameter parameter) {
* @param operationParameters the operation parameters
* @param requestBodyInfo the request body info
*/
private void setParams(Operation operation, List<Parameter> operationParameters, RequestBodyInfo requestBodyInfo) {
protected void setParams(Operation operation, List<Parameter> operationParameters, RequestBodyInfo requestBodyInfo) {
if (!CollectionUtils.isEmpty(operationParameters))
operation.setParameters(operationParameters);
if (requestBodyInfo.getRequestBody() != null)
Expand Down Expand Up @@ -560,7 +611,7 @@ public void applyBeanValidatorAnnotations(final RequestBody requestBody, final L
* @param annos the annos
* @param schema the schema
*/
private void calculateSize(Map<String, Annotation> annos, Schema<?> schema) {
protected void calculateSize(Map<String, Annotation> annos, Schema<?> schema) {
if (annos.containsKey(Size.class.getSimpleName())) {
Size size = (Size) annos.get(Size.class.getSimpleName());
if (OPENAPI_ARRAY_TYPE.equals(schema.getType())) {
Expand Down Expand Up @@ -589,7 +640,7 @@ public RequestBodyService getRequestBodyBuilder() {
* @param method the method
* @return the api parameters
*/
private Map<String, io.swagger.v3.oas.annotations.Parameter> getApiParameters(Method method) {
protected Map<String, io.swagger.v3.oas.annotations.Parameter> getApiParameters(Method method) {
Class<?> declaringClass = method.getDeclaringClass();

Set<io.swagger.v3.oas.annotations.Parameters> apiParametersDoc = AnnotatedElementUtils
Expand Down Expand Up @@ -628,7 +679,7 @@ private Map<String, io.swagger.v3.oas.annotations.Parameter> getApiParameters(Me
* @param annos the annos
* @param schema the schema
*/
private void applyValidationsToSchema(Map<String, Annotation> annos, Schema<?> schema) {
protected void applyValidationsToSchema(Map<String, Annotation> annos, Schema<?> schema) {
if (annos.containsKey(Min.class.getSimpleName())) {
Min min = (Min) annos.get(Min.class.getSimpleName());
schema.setMinimum(BigDecimal.valueOf(min.value()));
Expand Down Expand Up @@ -669,7 +720,7 @@ private void applyValidationsToSchema(Map<String, Annotation> annos, Schema<?> s
* @param parameterInfo the parameter info
* @return the boolean
*/
private boolean isRequestBodyParam(RequestMethod requestMethod, ParameterInfo parameterInfo) {
protected boolean isRequestBodyParam(RequestMethod requestMethod, ParameterInfo parameterInfo) {
MethodParameter methodParameter = parameterInfo.getMethodParameter();
DelegatingMethodParameter delegatingMethodParameter = (DelegatingMethodParameter) methodParameter;

Expand All @@ -690,7 +741,7 @@ private boolean isRequestBodyParam(RequestMethod requestMethod, ParameterInfo pa
* @param pName the p name
* @return the param javadoc
*/
private String getParamJavadoc(JavadocProvider javadocProvider, MethodParameter methodParameter, String pName) {
protected String getParamJavadoc(JavadocProvider javadocProvider, MethodParameter methodParameter, String pName) {
DelegatingMethodParameter delegatingMethodParameter = (DelegatingMethodParameter) methodParameter;
final String paramJavadocDescription;
if (delegatingMethodParameter.isParameterObject()) {
Expand All @@ -707,4 +758,98 @@ private String getParamJavadoc(JavadocProvider javadocProvider, MethodParameter
return paramJavadocDescription;
}

protected io.swagger.v3.oas.annotations.Parameter generateParameterBySchema(io.swagger.v3.oas.annotations.media.Schema schema) {
return new io.swagger.v3.oas.annotations.Parameter() {

@Override
public Class<? extends Annotation> annotationType() {
return io.swagger.v3.oas.annotations.Parameter.class;
}

@Override
public String name() {
return schema.name();
}

@Override
public ParameterIn in() {
return ParameterIn.DEFAULT;
}

@Override
public String description() {
return schema.description();
}

@Override
public boolean required() {
return schema.required();
}

@Override
public boolean deprecated() {
return schema.deprecated();
}

@Override
public boolean allowEmptyValue() {
return schema.nullable();
}

@Override
public ParameterStyle style() {
return ParameterStyle.DEFAULT;
}

@Override
public Explode explode() {
return Explode.DEFAULT;
}

@Override
public boolean allowReserved() {
return false;
}

@Override
public io.swagger.v3.oas.annotations.media.Schema schema() {
return schema;
}

@Override
public ArraySchema array() {
return null;
}

@Override
public io.swagger.v3.oas.annotations.media.Content[] content() {
return new io.swagger.v3.oas.annotations.media.Content[0];
}

@Override
public boolean hidden() {
return schema.hidden();
}

@Override
public ExampleObject[] examples() {
return new ExampleObject[0];
}

@Override
public String example() {
return schema.example();
}

@Override
public Extension[] extensions() {
return schema.extensions();
}

@Override
public String ref() {
return schema.ref();
}
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ public class DelegatingMethodParameter extends MethodParameter {
* @param isParameterObject the is parameter object
* @param isNotRequired the is required
*/
DelegatingMethodParameter(MethodParameter delegate, String parameterName, Annotation[] additionalParameterAnnotations, boolean isParameterObject, boolean isNotRequired) {
public DelegatingMethodParameter(MethodParameter delegate, String parameterName, Annotation[] additionalParameterAnnotations, boolean isParameterObject, boolean isNotRequired) {
super(delegate);
this.delegate = delegate;
this.additionalParameterAnnotations = additionalParameterAnnotations;
Expand All @@ -103,34 +103,6 @@ public class DelegatingMethodParameter extends MethodParameter {
this.isNotRequired = isNotRequired;
}

/**
* Customize method parameter [ ].
*
* @param pNames the p names
* @param parameters the parameters
* @param optionalDelegatingMethodParameterCustomizer the optional delegating method parameter customizer
* @return the method parameter [ ]
*/
public static MethodParameter[] customize(String[] pNames, MethodParameter[] parameters, Optional<DelegatingMethodParameterCustomizer> optionalDelegatingMethodParameterCustomizer) {
List<MethodParameter> explodedParameters = new ArrayList<>();
for (int i = 0; i < parameters.length; ++i) {
MethodParameter p = parameters[i];
Class<?> paramClass = AdditionalModelsConverter.getParameterObjectReplacement(p.getParameterType());

if (!MethodParameterPojoExtractor.isSimpleType(paramClass) && (p.hasParameterAnnotation(ParameterObject.class) || AnnotatedElementUtils.isAnnotated(paramClass, ParameterObject.class))) {
MethodParameterPojoExtractor.extractFrom(paramClass).forEach(methodParameter -> {
optionalDelegatingMethodParameterCustomizer.ifPresent(customizer -> customizer.customize(p, methodParameter));
explodedParameters.add(methodParameter);
});
}
else {
String name = pNames != null ? pNames[i] : p.getParameterName();
explodedParameters.add(new DelegatingMethodParameter(p, name, null, false, false));
}
}
return explodedParameters.toArray(new MethodParameter[0]);
}

@Override
@NonNull
public Annotation[] getParameterAnnotations() {
Expand Down Expand Up @@ -251,27 +223,4 @@ public int hashCode() {
public boolean isParameterObject() {
return isParameterObject;
}

/**
* Return a variant of this {@code MethodParameter} which refers to the
* given containing class.
* @param methodParameter the method parameter
* @param containingClass a specific containing class (potentially a subclass of the declaring class, e.g. substituting a type variable) A copy of spring withContainingClass, to keep compatibility with older spring versions
* @return the method parameter
* @see #getParameterType() #getParameterType()
*/
public static MethodParameter changeContainingClass(MethodParameter methodParameter, @Nullable Class<?> containingClass) {
MethodParameter result = methodParameter.clone();
try {
Field containingClassField = FieldUtils.getDeclaredField(result.getClass(), "containingClass", true);
containingClassField.set(result, containingClass);
Field parameterTypeField = FieldUtils.getDeclaredField(result.getClass(), "parameterType", true);
parameterTypeField.set(result, null);
}
catch (IllegalAccessException e) {
LOGGER.warn(e.getMessage());
}
return result;
}

}
}
Loading