Skip to content

Commit 3cd0f7c

Browse files
committed
Read the data type in order of precedence
1. dataTypeClass 2. dataType using forName 3. explicit type and format (3388)
1 parent e7e98e9 commit 3cd0f7c

5 files changed

Lines changed: 174 additions & 41 deletions

File tree

springfox-spring-webmvc/src/test/groovy/springfox/documentation/spring/web/readers/parameter/ParameterReaderSpec.groovy

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,8 @@ class ParameterReaderSpec
7878
where:
7979
parameterPlugin | resultProperty | springParameterMethod | methodReturnValue | apiParamAnnotation | reqParamAnnot | expected
8080
new ParameterDefaultReader(description) | 'defaultValue' | 'none' | 'any' | null | null | null
81-
new ParameterDefaultReader(description) | 'defaultValue' | 'none' | 'any' | apiParam([defaultValue: { -> 'defl' }]) | null | null
82-
new ParameterDefaultReader(description) | 'defaultValue' | 'none' | 'any' | null | reqParam([defaultValue: { -> 'defr' }]) | 'defr'
81+
new ParameterDefaultReader(description) | 'defaultValue' | 'none' | 'any' | apiParam([defaultValue: { -> 'defl' }])| null | null
82+
new ParameterDefaultReader(description) | 'defaultValue' | 'none' | 'any' | null | reqParam([defaultValue: { -> 'defr' }]) | 'defr'
8383
}
8484

8585
@Unroll

springfox-swagger-common/src/main/java/springfox/documentation/swagger/readers/operation/OperationImplicitParameterReader.java

Lines changed: 50 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import org.springframework.beans.factory.annotation.Autowired;
2626
import org.springframework.core.annotation.Order;
2727
import org.springframework.stereotype.Component;
28+
import org.springframework.util.ClassUtils;
2829
import springfox.documentation.builders.ExampleBuilder;
2930
import springfox.documentation.builders.ModelSpecificationBuilder;
3031
import springfox.documentation.builders.RequestParameterBuilder;
@@ -33,6 +34,7 @@
3334
import springfox.documentation.schema.ModelKey;
3435
import springfox.documentation.schema.ModelKeyBuilder;
3536
import springfox.documentation.schema.ModelSpecification;
37+
import springfox.documentation.schema.ScalarType;
3638
import springfox.documentation.schema.ScalarTypes;
3739
import springfox.documentation.service.AllowableValues;
3840
import springfox.documentation.service.CollectionFormat;
@@ -46,9 +48,12 @@
4648

4749
import java.util.ArrayList;
4850
import java.util.List;
51+
import java.util.Objects;
4952
import java.util.Optional;
53+
import java.util.function.Function;
5054
import java.util.function.Predicate;
5155
import java.util.stream.Collectors;
56+
import java.util.stream.Stream;
5257

5358
import static java.util.Optional.*;
5459
import static org.slf4j.LoggerFactory.*;
@@ -135,8 +140,8 @@ static Compatibility<springfox.documentation.service.Parameter, RequestParameter
135140
);
136141
}
137142

138-
private static Compatibility<springfox.documentation.schema.ModelRef, ModelSpecification>
139-
maybeGetModelRef(ApiImplicitParam param) {
143+
private static Compatibility<springfox.documentation.schema.ModelRef, ModelSpecification> maybeGetModelRef(
144+
ApiImplicitParam param) {
140145
String dataType = ofNullable(param.dataType())
141146
.filter(((Predicate<String>) String::isEmpty).negate())
142147
.orElse("string");
@@ -156,45 +161,60 @@ static Compatibility<springfox.documentation.service.Parameter, RequestParameter
156161
new springfox.documentation.schema.ModelRef(dataType, allowableValues), modelSpecification);
157162
}
158163

159-
private static ModelSpecification modelSpecification(
160-
ApiImplicitParam param) {
161-
Class<?> clazz;
164+
static ModelSpecification modelSpecification(ApiImplicitParam param) {
165+
ModelSpecification scalarModel = null;
166+
ModelSpecification referenceModel = null;
162167
try {
163-
param.dataTypeClass();
168+
Class<?> clazz;
164169
if (param.dataTypeClass() != Void.class) {
165170
clazz = param.dataTypeClass();
166171
} else {
167-
clazz = Class.forName(param.dataType());
172+
clazz = ClassUtils.forName(param.dataType(), null);
168173
}
174+
scalarModel = ScalarTypes.builtInScalarType(clazz)
175+
.map(scalarModel(param))
176+
.orElse(null);
177+
178+
ModelKey dataTypeKey = new ModelKeyBuilder()
179+
.qualifiedModelName(q ->
180+
q.namespace(safeGetPackageName(clazz))
181+
.name(clazz.getSimpleName()))
182+
.build();
183+
referenceModel = referenceModelSpecification(dataTypeKey, param.allowMultiple());
184+
169185
} catch (ClassNotFoundException e) {
170186
LOGGER.warn(
171187
"Unable to interpret the implicit parameter configuration with dataType: {}, dataTypeClass: {}",
172188
param.dataType(),
173189
param.dataTypeClass());
174-
return null;
175190
}
176-
ModelSpecification modelSpecification = ScalarTypes.builtInScalarType(clazz)
177-
.map(scalar -> {
178-
if (param.allowMultiple()) {
179-
return new ModelSpecificationBuilder()
180-
.collectionModel(c ->
181-
c.model(m ->
182-
m.scalarModel(scalar))
183-
.collectionType(CollectionType.LIST))
184-
.build();
185-
}
186-
return new ModelSpecificationBuilder()
187-
.scalarModel(scalar)
188-
.build();
189-
})
191+
ModelSpecification scalarFromType = ScalarType.from(param.type(), param.format())
192+
.map(scalarModel(param))
190193
.orElse(null);
191-
if (modelSpecification == null) {
192-
ModelKey dataTypeKey = new ModelKeyBuilder()
193-
.qualifiedModelName(q -> q.namespace(safeGetPackageName(clazz)).name(clazz.getSimpleName()))
194+
195+
return Stream.of(scalarModel, referenceModel, scalarFromType)
196+
.filter(Objects::nonNull)
197+
.findFirst()
198+
.orElse(null);
199+
}
200+
201+
private static Function<ScalarType, ModelSpecification> scalarModel(ApiImplicitParam param) {
202+
return scalar -> {
203+
if (scalar == null) {
204+
return null;
205+
}
206+
if (param.allowMultiple()) {
207+
return new ModelSpecificationBuilder()
208+
.collectionModel(c ->
209+
c.model(m ->
210+
m.scalarModel(scalar))
211+
.collectionType(CollectionType.LIST))
212+
.build();
213+
}
214+
return new ModelSpecificationBuilder()
215+
.scalarModel(scalar)
194216
.build();
195-
modelSpecification = referenceModelSpecification(dataTypeKey, param.allowMultiple());
196-
}
197-
return modelSpecification;
217+
};
198218
}
199219

200220
private static ModelSpecification referenceModelSpecification(
@@ -215,8 +235,8 @@ private static ModelSpecification referenceModelSpecification(
215235
.build();
216236
}
217237

218-
private List<Compatibility<springfox.documentation.service.Parameter, RequestParameter>>
219-
readParameters(OperationContext context) {
238+
private List<Compatibility<springfox.documentation.service.Parameter, RequestParameter>> readParameters(
239+
OperationContext context) {
220240
Optional<ApiImplicitParam> annotation = context.findAnnotation(ApiImplicitParam.class);
221241
List<Compatibility<springfox.documentation.service.Parameter, RequestParameter>> parameters = new ArrayList<>();
222242
annotation.ifPresent(
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package springfox.documentation.swagger.readers.operation
2+
3+
import spock.lang.Specification
4+
import spock.lang.Unroll
5+
import springfox.documentation.builders.ModelSpecificationBuilder
6+
import springfox.documentation.builders.ReferenceModelSpecificationBuilder
7+
import springfox.documentation.schema.Example
8+
import springfox.documentation.schema.ScalarType
9+
import springfox.documentation.swagger.readers.parameter.ApiImplicitParamAnnotationSupport
10+
11+
class OperationImplicitParameterReaderSpec extends Specification implements ApiImplicitParamAnnotationSupport {
12+
@Unroll
13+
def "Implicit params are evaluated correctly"() {
14+
when:
15+
def model = OperationImplicitParameterReader.modelSpecification(implicitParamAnnotation)
16+
17+
then:
18+
model?.scalar?.orElse(null)?.type == expectedScalar
19+
model?.reference?.orElse(null) == expectedReference
20+
model?.collection
21+
?.map { c -> c.model }
22+
?.orElse(null) == collectionItemSpecification(expectedCollectionType)
23+
24+
where:
25+
implicitParamAnnotation | expectedScalar | expectedCollectionType | expectedReference
26+
apiImplicitParam() | null | null | null
27+
apiImplicitParam("string") | ScalarType.STRING | null | null
28+
apiImplicitParam("string", "byte") | ScalarType.BYTE | null | null
29+
apiImplicitParam("string", "byte", "int") | ScalarType.INTEGER | null | null
30+
apiImplicitParam("string", "byte", "int", Long) | ScalarType.LONG | null | null
31+
apiImplicitParam("string", "byte", "int", Example) | null | null | reference(Example)
32+
collectionApiImplicitParam() | null | null | null
33+
collectionApiImplicitParam("string") | null | ScalarType.STRING | null
34+
collectionApiImplicitParam("string", "byte") | null | ScalarType.BYTE | null
35+
collectionApiImplicitParam("string", "byte", "int") | null | ScalarType.INTEGER | null
36+
collectionApiImplicitParam("string", "byte", "int", Long) | null | ScalarType.LONG | null
37+
collectionApiImplicitParam("string", "byte", "int", Example) | null | reference(Example) | null
38+
39+
}
40+
41+
def reference(Class clazz) {
42+
new ReferenceModelSpecificationBuilder()
43+
.key { k ->
44+
k.qualifiedModelName {
45+
q ->
46+
q.name(clazz.simpleName)
47+
.namespace(clazz.packageName)
48+
}
49+
}.build()
50+
}
51+
52+
def collectionItemSpecification(type) {
53+
if (type == null) {
54+
return null
55+
}
56+
if (type instanceof ScalarType) {
57+
return new ModelSpecificationBuilder()
58+
.scalarModel(type)
59+
.build()
60+
}
61+
return new ModelSpecificationBuilder()
62+
.referenceModel {
63+
r ->
64+
r.copyOf(type)
65+
}.build()
66+
}
67+
}

springfox-swagger-common/src/test/groovy/springfox/documentation/swagger/readers/operation/OperationImplicitParamsReaderSpec.groovy

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -78,14 +78,14 @@ class OperationImplicitParamsReaderSpec extends DocumentationContextSpec impleme
7878
operationImplicitParameterReader.supports(DocumentationType.SWAGGER_12)
7979
operationImplicitParameterReader.supports(DocumentationType.SWAGGER_2)
8080
where:
81-
handlerMethod | expectedSize
82-
dummyHandlerMethod('dummyMethod') | 0
83-
dummyHandlerMethod('methodWithApiImplicitParam') | 1
84-
dummyHandlerMethod('methodWithApiImplicitParamAndInteger', Integer.class) | 2
85-
dummyHandlerMethod('methodWithApiImplicitParamAndExample', Integer.class) | 2
86-
dummyHandlerMethod('methodWithApiImplicitParamAndAllowMultiple', Integer.class) | 2
87-
dummyHandlerMethod('methodWithApiImplicitParams', Integer.class) | 3
88-
handlerMethodIn(apiImplicitParamsClass(), 'methodWithApiImplicitParam') | 2
89-
dummyHandlerMethodIn(apiImplicitParamsAllowMultipleClass(), 'methodWithApiImplicitParam') | 3
81+
handlerMethod | expectedSize
82+
dummyHandlerMethod('dummyMethod') | 0
83+
dummyHandlerMethod('methodWithApiImplicitParam') | 1
84+
dummyHandlerMethod('methodWithApiImplicitParamAndInteger', Integer.class) | 2
85+
dummyHandlerMethod('methodWithApiImplicitParamAndExample', Integer.class) | 2
86+
dummyHandlerMethod('methodWithApiImplicitParamAndAllowMultiple', Integer.class) | 2
87+
dummyHandlerMethod('methodWithApiImplicitParams', Integer.class) | 3
88+
handlerMethodIn(apiImplicitParamsClass(), 'methodWithApiImplicitParam') | 2
89+
dummyHandlerMethodIn(apiImplicitParamsAllowMultipleClass(), 'methodWithApiImplicitParam') | 3
9090
}
9191
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package springfox.documentation.swagger.readers.parameter
2+
3+
import io.swagger.annotations.ApiImplicitParam
4+
5+
trait ApiImplicitParamAnnotationSupport {
6+
ApiImplicitParam apiParamWithType(type, format) {
7+
apiImplicitParam(type, format)
8+
}
9+
10+
ApiImplicitParam apiParamWithDataType(dataType) {
11+
apiImplicitParam("", "", dataType)
12+
}
13+
14+
ApiImplicitParam apiParamWithDataTypeClass(dataTypeClass) {
15+
apiImplicitParam("", "", "", dataTypeClass)
16+
}
17+
18+
ApiImplicitParam collectionApiImplicitParam(
19+
type = "",
20+
format = "",
21+
dataType = "",
22+
dataTypeClass = Void.class) {
23+
[name : { -> "test" },
24+
type : { -> type },
25+
format : { -> format },
26+
dataType : { -> dataType },
27+
dataTypeClass: { -> dataTypeClass },
28+
allowMultiple: { -> true }
29+
] as ApiImplicitParam
30+
}
31+
32+
ApiImplicitParam apiImplicitParam(
33+
type = "",
34+
format = "",
35+
dataType = "",
36+
dataTypeClass = Void.class) {
37+
[name : { -> "test" },
38+
type : { -> type },
39+
format : { -> format },
40+
dataType : { -> dataType },
41+
dataTypeClass: { -> dataTypeClass },
42+
allowMultiple: { -> false }
43+
] as ApiImplicitParam
44+
}
45+
46+
}

0 commit comments

Comments
 (0)