Skip to content
Merged
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
11 changes: 11 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
root = true

[*]
charset = utf-8
end_of_line = lf
tab_width = 4
indent_style = tab

[*.json]
indent_style = space
indent_size = 2
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package org.springdoc.api.annotations;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface ParameterObject {}
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@
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.api.annotations.ParameterObject;
import org.springdoc.core.converters.AdditionalModelsConverter;
import org.springdoc.core.customizers.OperationCustomizer;
import org.springdoc.core.customizers.ParameterCustomizer;

Expand Down Expand Up @@ -160,22 +162,35 @@ public Operation build(HandlerMethod handlerMethod, RequestMethod requestMethod,
// requests
String[] pNames = this.localSpringDocParameterNameDiscoverer.getParameterNames(handlerMethod.getMethod());
MethodParameter[] parameters = handlerMethod.getMethodParameters();
String[] reflectionParametersNames = Arrays.stream(parameters).map(MethodParameter::getParameterName).toArray(String[]::new);
if (pNames == null)
pNames = reflectionParametersNames;

List<MethodParameter> explodedParameters = new ArrayList<>();
for (int i = 0; i < parameters.length; ++i) {
MethodParameter p = parameters[i];
if (p.hasParameterAnnotation(ParameterObject.class)) {
Class<?> paramClass = AdditionalModelsConverter.getReplacement(p.getParameterType());
Stream.of(paramClass.getDeclaredFields())
.map(f -> DelegatingMethodParameter.fromGetterOfField(paramClass, f))
.filter(Objects::nonNull)
.forEach(explodedParameters::add);
}
else {
String name = pNames != null ? pNames[i] : p.getParameterName();
explodedParameters.add(new DelegatingMethodParameter(p, name, null));
}
}
parameters = explodedParameters.toArray(new MethodParameter[0]);

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());
Components components = openAPI.getComponents();

for (int i = 0; i < pNames.length; i++) {
for (MethodParameter methodParameter : parameters) {
// check if query param
Parameter parameter = null;
final String pName = pNames[i] == null ? reflectionParametersNames[i] : pNames[i];
MethodParameter methodParameter = parameters[i];
io.swagger.v3.oas.annotations.Parameter parameterDoc = methodParameter.getParameterAnnotation(io.swagger.v3.oas.annotations.Parameter.class);
if (parameterDoc == null)
parameterDoc = parametersDocMap.get(pName);
parameterDoc = parametersDocMap.get(methodParameter.getParameterName());
// use documentation as reference
if (parameterDoc != null) {
if (parameterDoc.hidden())
Expand All @@ -185,7 +200,7 @@ public Operation build(HandlerMethod handlerMethod, RequestMethod requestMethod,
}

if (!isParamToIgnore(methodParameter)) {
ParameterInfo parameterInfo = new ParameterInfo(pName, methodParameter, parameter);
ParameterInfo parameterInfo = new ParameterInfo(methodParameter.getParameterName(), methodParameter, parameter);
parameter = buildParams(parameterInfo, components, requestMethod,
methodAttributes.getJsonViewAnnotation());
// Merge with the operation parameters
Expand All @@ -205,7 +220,7 @@ else if (!RequestMethod.GET.equals(requestMethod)) {
}

LinkedHashMap<String, Parameter> map = getParameterLinkedHashMap(components, methodAttributes, operationParameters, parametersDocMap);
setParams(operation, new ArrayList<Parameter>(map.values()), requestBodyInfo);
setParams(operation, new ArrayList<>(map.values()), requestBodyInfo);
// allow for customisation
return customiseOperation(operation, handlerMethod);
}
Expand Down Expand Up @@ -297,7 +312,7 @@ else if (pathVar != null) {
String name = StringUtils.isBlank(pathVar.value()) ? pName : pathVar.value();
parameterInfo.setpName(name);
// check if PATH PARAM
requestInfo = new RequestInfo(ParameterIn.PATH.toString(), pathVar.value(), Boolean.TRUE, null);
requestInfo = new RequestInfo(ParameterIn.PATH.toString(), pathVar.value(), !methodParameter.isOptional(), null);
parameter = buildParam(parameterInfo, components, requestInfo, jsonView);
}
else if (cookieValue != null) {
Expand All @@ -307,7 +322,7 @@ else if (cookieValue != null) {
}
// By default
if (RequestMethod.GET.equals(requestMethod) || (parameterInfo.getParameterModel() != null && ParameterIn.PATH.toString().equals(parameterInfo.getParameterModel().getIn())))
parameter = this.buildParam(QUERY_PARAM, components, parameterInfo, Boolean.TRUE, null, jsonView);
parameter = this.buildParam(QUERY_PARAM, components, parameterInfo, !methodParameter.isOptional(), null, jsonView);

return parameter;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
package org.springdoc.core;

import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Constructor;
import java.lang.reflect.Executable;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.Objects;
import java.util.stream.Stream;

import org.apache.commons.lang3.ArrayUtils;

import org.springframework.core.MethodParameter;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;

/**
* @author zarebski.m
*/
class DelegatingMethodParameter extends MethodParameter {
private volatile MethodParameter delegate;

private Annotation[] additionalParameterAnnotations;

private String parameterName;

DelegatingMethodParameter(MethodParameter delegate, String parameterName, Annotation[] additionalParameterAnnotations) {
super(delegate);
this.delegate = delegate;
this.additionalParameterAnnotations = additionalParameterAnnotations;
this.parameterName = parameterName;
}

@Nullable
static MethodParameter fromGetterOfField(Class<?> paramClass, Field field) {
try {
return Stream.of(Introspector.getBeanInfo(paramClass).getPropertyDescriptors())
.filter(d -> d.getName().equals(field.getName()))
.map(PropertyDescriptor::getReadMethod)
.filter(Objects::nonNull)
.findFirst()
.map(method -> new MethodParameter(method, -1))
.map(param -> new DelegatingMethodParameter(param, field.getName(), field.getDeclaredAnnotations()))
.orElse(null);
}
catch (IntrospectionException e) {
return null;
}
}

@Override
@NonNull
public Annotation[] getParameterAnnotations() {
return ArrayUtils.addAll(delegate.getParameterAnnotations(), additionalParameterAnnotations);
}

@Override
public String getParameterName() {
return parameterName;
}

@Override
public Method getMethod() {
return delegate.getMethod();
}

@Override
public Constructor<?> getConstructor() {
return delegate.getConstructor();
}

@Override
public Class<?> getDeclaringClass() {
return delegate.getDeclaringClass();
}

@Override
public Member getMember() {
return delegate.getMember();
}

@Override
public AnnotatedElement getAnnotatedElement() {
return delegate.getAnnotatedElement();
}

@Override
public Executable getExecutable() {
return delegate.getExecutable();
}

@Override
public MethodParameter withContainingClass(Class<?> containingClass) {
return delegate.withContainingClass(containingClass);
}

@Override
public Class<?> getContainingClass() {
return delegate.getContainingClass();
}

@Override
public Class<?> getParameterType() {
return delegate.getParameterType();
}

@Override
public Type getGenericParameterType() {
return delegate.getGenericParameterType();
}

@Override
public Class<?> getNestedParameterType() {
return delegate.getNestedParameterType();
}

@Override
public Type getNestedGenericParameterType() {
return delegate.getNestedGenericParameterType();
}

@Override
public void initParameterNameDiscovery(ParameterNameDiscoverer parameterNameDiscoverer) {
delegate.initParameterNameDiscovery(parameterNameDiscoverer);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@

package org.springdoc.core.converters;


import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
Expand All @@ -44,6 +43,10 @@ public static void replaceWithSchema(Class source, Schema target) {
modelToSchemaMap.put(source, target);
}

public static Class getReplacement(Class clazz) {
return modelToClassMap.getOrDefault(clazz, clazz);
}

@Override
public Schema resolve(AnnotatedType type, ModelConverterContext context, Iterator<ModelConverter> chain) {
JavaType javaType = Json.mapper().constructType(type.getType());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,47 +18,54 @@

package org.springdoc.core.converters;

import java.util.List;
import java.util.Objects;
import io.swagger.v3.oas.annotations.media.ArraySchema;
import io.swagger.v3.oas.annotations.media.Schema;
import org.springframework.lang.Nullable;

import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import java.util.List;
import java.util.Objects;

@NotNull
public class Pageable {

@NotNull
@Nullable
@Min(0)
private int page;
@Schema(description = "Zero-based page index (0..N)", defaultValue = "0")
private Integer page;

@NotNull
@Nullable
@Min(1)
@Max(2000)
private int size;
@Schema(description = "The size of the page to be returned", defaultValue = "20")
private Integer size;

@NotNull
@Nullable
@ArraySchema(arraySchema = @Schema(description = "Sorting criteria in the format: property(,asc|desc). "
+ "Default sort order is ascending. " + "Multiple sort criteria are supported."))
private List<String> sort;

public Pageable(@NotNull @Min(0) int page, @NotNull @Min(1) @Max(2000) int size, List<String> sort) {
public Pageable(int page, int size, List<String> sort) {
this.page = page;
this.size = size;
this.sort = sort;
}

public int getPage() {
public Integer getPage() {
return page;
}

public void setPage(int page) {
public void setPage(Integer page) {
this.page = page;
}

public int getSize() {
public Integer getSize() {
return size;
}

public void setSize(int size) {
public void setSize(Integer size) {
this.size = size;
}

Expand All @@ -67,10 +74,11 @@ public List<String> getSort() {
}

public void setSort(List<String> sort) {
if (sort == null)
if (sort == null) {
this.sort.clear();
else
} else {
this.sort = sort;
}
}

public void addSort(String sort) {
Expand All @@ -82,8 +90,8 @@ public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Pageable pageable = (Pageable) o;
return page == pageable.page &&
size == pageable.size &&
return Objects.equals(page, pageable.page) &&
Objects.equals(size, pageable.size) &&
Objects.equals(sort, pageable.sort);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,23 @@

package org.springdoc.core.converters;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.enums.ParameterIn;
import io.swagger.v3.oas.annotations.media.ArraySchema;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;

@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE })
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* @deprecated Use {@link org.springdoc.api.annotations.ParameterObject} annotation
* on {@link org.springframework.data.domain.Pageable} method parameter instead.
*/
@Deprecated
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Parameter(in = ParameterIn.QUERY
, description = "Zero-based page index (0..N)"
Expand Down
Loading