Skip to content

Commit e838b41

Browse files
author
bnasslahsen
committed
Merge branch 'zarebski-m-master'
2 parents ef918f0 + 8fd9f35 commit e838b41

File tree

15 files changed

+561
-36
lines changed

15 files changed

+561
-36
lines changed
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package org.springdoc.api.annotations;
2+
3+
import java.lang.annotation.ElementType;
4+
import java.lang.annotation.Retention;
5+
import java.lang.annotation.RetentionPolicy;
6+
import java.lang.annotation.Target;
7+
8+
@Target(ElementType.PARAMETER)
9+
@Retention(RetentionPolicy.RUNTIME)
10+
public @interface ParameterObject {}

springdoc-openapi-common/src/main/java/org/springdoc/core/AbstractRequestBuilder.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -163,16 +163,16 @@ public Operation build(HandlerMethod handlerMethod, RequestMethod requestMethod,
163163
String[] reflectionParametersNames = Arrays.stream(parameters).map(MethodParameter::getParameterName).toArray(String[]::new);
164164
if (pNames == null)
165165
pNames = reflectionParametersNames;
166+
parameters = DelegatingMethodParameter.customize(pNames, parameters);
166167
RequestBodyInfo requestBodyInfo = new RequestBodyInfo();
167168
List<Parameter> operationParameters = (operation.getParameters() != null) ? operation.getParameters() : new ArrayList<>();
168169
Map<String, io.swagger.v3.oas.annotations.Parameter> parametersDocMap = getApiParameters(handlerMethod.getMethod());
169170
Components components = openAPI.getComponents();
170171

171-
for (int i = 0; i < pNames.length; i++) {
172+
for (MethodParameter methodParameter : parameters) {
172173
// check if query param
173174
Parameter parameter = null;
174-
final String pName = pNames[i] == null ? reflectionParametersNames[i] : pNames[i];
175-
MethodParameter methodParameter = parameters[i];
175+
final String pName = methodParameter.getParameterName();
176176
io.swagger.v3.oas.annotations.Parameter parameterDoc = methodParameter.getParameterAnnotation(io.swagger.v3.oas.annotations.Parameter.class);
177177
if (parameterDoc == null)
178178
parameterDoc = parametersDocMap.get(pName);
@@ -205,7 +205,7 @@ else if (!RequestMethod.GET.equals(requestMethod)) {
205205
}
206206

207207
LinkedHashMap<String, Parameter> map = getParameterLinkedHashMap(components, methodAttributes, operationParameters, parametersDocMap);
208-
setParams(operation, new ArrayList<Parameter>(map.values()), requestBodyInfo);
208+
setParams(operation, new ArrayList<>(map.values()), requestBodyInfo);
209209
// allow for customisation
210210
return customiseOperation(operation, handlerMethod);
211211
}
@@ -297,7 +297,7 @@ else if (pathVar != null) {
297297
String name = StringUtils.isBlank(pathVar.value()) ? pName : pathVar.value();
298298
parameterInfo.setpName(name);
299299
// check if PATH PARAM
300-
requestInfo = new RequestInfo(ParameterIn.PATH.toString(), pathVar.value(), Boolean.TRUE, null);
300+
requestInfo = new RequestInfo(ParameterIn.PATH.toString(), pathVar.value(), !methodParameter.isOptional(), null);
301301
parameter = buildParam(parameterInfo, components, requestInfo, jsonView);
302302
}
303303
else if (cookieValue != null) {
@@ -307,7 +307,7 @@ else if (cookieValue != null) {
307307
}
308308
// By default
309309
if (RequestMethod.GET.equals(requestMethod) || (parameterInfo.getParameterModel() != null && ParameterIn.PATH.toString().equals(parameterInfo.getParameterModel().getIn())))
310-
parameter = this.buildParam(QUERY_PARAM, components, parameterInfo, Boolean.TRUE, null, jsonView);
310+
parameter = this.buildParam(QUERY_PARAM, components, parameterInfo, !methodParameter.isOptional(), null, jsonView);
311311

312312
return parameter;
313313
}
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
package org.springdoc.core;
2+
3+
import java.beans.IntrospectionException;
4+
import java.beans.Introspector;
5+
import java.beans.PropertyDescriptor;
6+
import java.lang.annotation.Annotation;
7+
import java.lang.reflect.AnnotatedElement;
8+
import java.lang.reflect.Constructor;
9+
import java.lang.reflect.Executable;
10+
import java.lang.reflect.Field;
11+
import java.lang.reflect.Member;
12+
import java.lang.reflect.Method;
13+
import java.lang.reflect.Type;
14+
import java.util.ArrayList;
15+
import java.util.List;
16+
import java.util.Objects;
17+
import java.util.stream.Stream;
18+
19+
import org.apache.commons.lang3.ArrayUtils;
20+
import org.springdoc.api.annotations.ParameterObject;
21+
import org.springdoc.core.converters.AdditionalModelsConverter;
22+
23+
import org.springframework.core.MethodParameter;
24+
import org.springframework.core.ParameterNameDiscoverer;
25+
import org.springframework.lang.NonNull;
26+
import org.springframework.lang.Nullable;
27+
28+
/**
29+
* @author zarebski.m
30+
*/
31+
class DelegatingMethodParameter extends MethodParameter {
32+
private volatile MethodParameter delegate;
33+
34+
private Annotation[] additionalParameterAnnotations;
35+
36+
private String parameterName;
37+
38+
DelegatingMethodParameter(MethodParameter delegate, String parameterName, Annotation[] additionalParameterAnnotations) {
39+
super(delegate);
40+
this.delegate = delegate;
41+
this.additionalParameterAnnotations = additionalParameterAnnotations;
42+
this.parameterName = parameterName;
43+
}
44+
45+
public static MethodParameter[] customize(String[] pNames, MethodParameter[] parameters) {
46+
List<MethodParameter> explodedParameters = new ArrayList<>();
47+
for (int i = 0; i < parameters.length; ++i) {
48+
MethodParameter p = parameters[i];
49+
if (p.hasParameterAnnotation(ParameterObject.class)) {
50+
Class<?> paramClass = AdditionalModelsConverter.getReplacement(p.getParameterType());
51+
Stream.of(paramClass.getDeclaredFields())
52+
.map(f -> fromGetterOfField(paramClass, f))
53+
.filter(Objects::nonNull)
54+
.forEach(explodedParameters::add);
55+
}
56+
else {
57+
String name = pNames != null ? pNames[i] : p.getParameterName();
58+
explodedParameters.add(new DelegatingMethodParameter(p, name, null));
59+
}
60+
}
61+
return explodedParameters.toArray(new MethodParameter[0]);
62+
}
63+
64+
@Override
65+
@NonNull
66+
public Annotation[] getParameterAnnotations() {
67+
return ArrayUtils.addAll(delegate.getParameterAnnotations(), additionalParameterAnnotations);
68+
}
69+
70+
@Override
71+
public String getParameterName() {
72+
return parameterName;
73+
}
74+
75+
@Override
76+
public Method getMethod() {
77+
return delegate.getMethod();
78+
}
79+
80+
@Override
81+
public Constructor<?> getConstructor() {
82+
return delegate.getConstructor();
83+
}
84+
85+
@Override
86+
public Class<?> getDeclaringClass() {
87+
return delegate.getDeclaringClass();
88+
}
89+
90+
@Override
91+
public Member getMember() {
92+
return delegate.getMember();
93+
}
94+
95+
@Override
96+
public AnnotatedElement getAnnotatedElement() {
97+
return delegate.getAnnotatedElement();
98+
}
99+
100+
@Override
101+
public Executable getExecutable() {
102+
return delegate.getExecutable();
103+
}
104+
105+
@Override
106+
public MethodParameter withContainingClass(Class<?> containingClass) {
107+
return delegate.withContainingClass(containingClass);
108+
}
109+
110+
@Override
111+
public Class<?> getContainingClass() {
112+
return delegate.getContainingClass();
113+
}
114+
115+
@Override
116+
public Class<?> getParameterType() {
117+
return delegate.getParameterType();
118+
}
119+
120+
@Override
121+
public Type getGenericParameterType() {
122+
return delegate.getGenericParameterType();
123+
}
124+
125+
@Override
126+
public Class<?> getNestedParameterType() {
127+
return delegate.getNestedParameterType();
128+
}
129+
130+
@Override
131+
public Type getNestedGenericParameterType() {
132+
return delegate.getNestedGenericParameterType();
133+
}
134+
135+
@Override
136+
public void initParameterNameDiscovery(ParameterNameDiscoverer parameterNameDiscoverer) {
137+
delegate.initParameterNameDiscovery(parameterNameDiscoverer);
138+
}
139+
140+
@Nullable
141+
static MethodParameter fromGetterOfField(Class<?> paramClass, Field field) {
142+
try {
143+
return Stream.of(Introspector.getBeanInfo(paramClass).getPropertyDescriptors())
144+
.filter(d -> d.getName().equals(field.getName()))
145+
.map(PropertyDescriptor::getReadMethod)
146+
.filter(Objects::nonNull)
147+
.findFirst()
148+
.map(method -> new MethodParameter(method, -1))
149+
.map(param -> new DelegatingMethodParameter(param, field.getName(), field.getDeclaredAnnotations()))
150+
.orElse(null);
151+
}
152+
catch (IntrospectionException e) {
153+
return null;
154+
}
155+
}
156+
}

springdoc-openapi-common/src/main/java/org/springdoc/core/converters/AdditionalModelsConverter.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818

1919
package org.springdoc.core.converters;
2020

21-
2221
import java.util.HashMap;
2322
import java.util.Iterator;
2423
import java.util.Map;
@@ -44,6 +43,10 @@ public static void replaceWithSchema(Class source, Schema target) {
4443
modelToSchemaMap.put(source, target);
4544
}
4645

46+
public static Class getReplacement(Class clazz) {
47+
return modelToClassMap.getOrDefault(clazz, clazz);
48+
}
49+
4750
@Override
4851
public Schema resolve(AnnotatedType type, ModelConverterContext context, Iterator<ModelConverter> chain) {
4952
JavaType javaType = Json.mapper().constructType(type.getType());

springdoc-openapi-data-rest/src/main/java/org/springdoc/core/converters/Pageable.java

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -18,47 +18,54 @@
1818

1919
package org.springdoc.core.converters;
2020

21-
import java.util.List;
22-
import java.util.Objects;
21+
import io.swagger.v3.oas.annotations.media.ArraySchema;
22+
import io.swagger.v3.oas.annotations.media.Schema;
23+
import org.springframework.lang.Nullable;
2324

2425
import javax.validation.constraints.Max;
2526
import javax.validation.constraints.Min;
2627
import javax.validation.constraints.NotNull;
28+
import java.util.List;
29+
import java.util.Objects;
2730

2831
@NotNull
2932
public class Pageable {
3033

31-
@NotNull
34+
@Nullable
3235
@Min(0)
33-
private int page;
36+
@Schema(description = "Zero-based page index (0..N)", defaultValue = "0")
37+
private Integer page;
3438

35-
@NotNull
39+
@Nullable
3640
@Min(1)
3741
@Max(2000)
38-
private int size;
42+
@Schema(description = "The size of the page to be returned", defaultValue = "20")
43+
private Integer size;
3944

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

43-
public Pageable(@NotNull @Min(0) int page, @NotNull @Min(1) @Max(2000) int size, List<String> sort) {
50+
public Pageable(int page, int size, List<String> sort) {
4451
this.page = page;
4552
this.size = size;
4653
this.sort = sort;
4754
}
4855

49-
public int getPage() {
56+
public Integer getPage() {
5057
return page;
5158
}
5259

53-
public void setPage(int page) {
60+
public void setPage(Integer page) {
5461
this.page = page;
5562
}
5663

57-
public int getSize() {
64+
public Integer getSize() {
5865
return size;
5966
}
6067

61-
public void setSize(int size) {
68+
public void setSize(Integer size) {
6269
this.size = size;
6370
}
6471

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

6976
public void setSort(List<String> sort) {
70-
if (sort == null)
77+
if (sort == null) {
7178
this.sort.clear();
72-
else
79+
} else {
7380
this.sort = sort;
81+
}
7482
}
7583

7684
public void addSort(String sort) {
@@ -82,8 +90,8 @@ public boolean equals(Object o) {
8290
if (this == o) return true;
8391
if (o == null || getClass() != o.getClass()) return false;
8492
Pageable pageable = (Pageable) o;
85-
return page == pageable.page &&
86-
size == pageable.size &&
93+
return Objects.equals(page, pageable.page) &&
94+
Objects.equals(size, pageable.size) &&
8795
Objects.equals(sort, pageable.sort);
8896
}
8997

springdoc-openapi-data-rest/src/main/java/org/springdoc/core/converters/PageableAsQueryParam.java

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,23 @@
1818

1919
package org.springdoc.core.converters;
2020

21-
import java.lang.annotation.ElementType;
22-
import java.lang.annotation.Retention;
23-
import java.lang.annotation.RetentionPolicy;
24-
import java.lang.annotation.Target;
25-
2621
import io.swagger.v3.oas.annotations.Parameter;
2722
import io.swagger.v3.oas.annotations.enums.ParameterIn;
2823
import io.swagger.v3.oas.annotations.media.ArraySchema;
2924
import io.swagger.v3.oas.annotations.media.Content;
3025
import io.swagger.v3.oas.annotations.media.Schema;
3126

32-
@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE })
27+
import java.lang.annotation.ElementType;
28+
import java.lang.annotation.Retention;
29+
import java.lang.annotation.RetentionPolicy;
30+
import java.lang.annotation.Target;
31+
32+
/**
33+
* @deprecated Use {@link org.springdoc.api.annotations.ParameterObject} annotation
34+
* on {@link org.springframework.data.domain.Pageable} method parameter instead.
35+
*/
36+
@Deprecated
37+
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
3338
@Retention(RetentionPolicy.RUNTIME)
3439
@Parameter(in = ParameterIn.QUERY
3540
, description = "Zero-based page index (0..N)"
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*
2+
*
3+
* * Copyright 2019-2020 the original author or authors.
4+
* *
5+
* * Licensed under the Apache License, Version 2.0 (the "License");
6+
* * you may not use this file except in compliance with the License.
7+
* * You may obtain a copy of the License at
8+
* *
9+
* * https://www.apache.org/licenses/LICENSE-2.0
10+
* *
11+
* * Unless required by applicable law or agreed to in writing, software
12+
* * distributed under the License is distributed on an "AS IS" BASIS,
13+
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* * See the License for the specific language governing permissions and
15+
* * limitations under the License.
16+
*
17+
*/
18+
19+
package test.org.springdoc.api.app7;
20+
21+
import org.springdoc.api.annotations.ParameterObject;
22+
import org.springframework.data.domain.Pageable;
23+
import org.springframework.http.ResponseEntity;
24+
import org.springframework.web.bind.annotation.GetMapping;
25+
import org.springframework.web.bind.annotation.RestController;
26+
27+
import javax.validation.constraints.NotNull;
28+
import java.util.List;
29+
30+
@RestController
31+
public class HelloController {
32+
33+
@GetMapping(value = "/search", produces = { "application/xml", "application/json" })
34+
public ResponseEntity<List<PersonDTO>> getAllPets(@NotNull @ParameterObject Pageable pageable) {
35+
return null;
36+
}
37+
38+
}

0 commit comments

Comments
 (0)