Skip to content

Commit

Permalink
Read the data type in order of precedence
Browse files Browse the repository at this point in the history
1. dataTypeClass
2. dataType using forName
3. explicit type and format

(3388)
  • Loading branch information
dilipkrish committed Jul 15, 2020
1 parent e7e98e9 commit 3cd0f7c
Show file tree
Hide file tree
Showing 5 changed files with 174 additions and 41 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,8 @@ class ParameterReaderSpec
where:
parameterPlugin | resultProperty | springParameterMethod | methodReturnValue | apiParamAnnotation | reqParamAnnot | expected
new ParameterDefaultReader(description) | 'defaultValue' | 'none' | 'any' | null | null | null
new ParameterDefaultReader(description) | 'defaultValue' | 'none' | 'any' | apiParam([defaultValue: { -> 'defl' }]) | null | null
new ParameterDefaultReader(description) | 'defaultValue' | 'none' | 'any' | null | reqParam([defaultValue: { -> 'defr' }]) | 'defr'
new ParameterDefaultReader(description) | 'defaultValue' | 'none' | 'any' | apiParam([defaultValue: { -> 'defl' }])| null | null
new ParameterDefaultReader(description) | 'defaultValue' | 'none' | 'any' | null | reqParam([defaultValue: { -> 'defr' }]) | 'defr'
}

@Unroll
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.util.ClassUtils;
import springfox.documentation.builders.ExampleBuilder;
import springfox.documentation.builders.ModelSpecificationBuilder;
import springfox.documentation.builders.RequestParameterBuilder;
Expand All @@ -33,6 +34,7 @@
import springfox.documentation.schema.ModelKey;
import springfox.documentation.schema.ModelKeyBuilder;
import springfox.documentation.schema.ModelSpecification;
import springfox.documentation.schema.ScalarType;
import springfox.documentation.schema.ScalarTypes;
import springfox.documentation.service.AllowableValues;
import springfox.documentation.service.CollectionFormat;
Expand All @@ -46,9 +48,12 @@

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static java.util.Optional.*;
import static org.slf4j.LoggerFactory.*;
Expand Down Expand Up @@ -135,8 +140,8 @@ static Compatibility<springfox.documentation.service.Parameter, RequestParameter
);
}

private static Compatibility<springfox.documentation.schema.ModelRef, ModelSpecification>
maybeGetModelRef(ApiImplicitParam param) {
private static Compatibility<springfox.documentation.schema.ModelRef, ModelSpecification> maybeGetModelRef(
ApiImplicitParam param) {
String dataType = ofNullable(param.dataType())
.filter(((Predicate<String>) String::isEmpty).negate())
.orElse("string");
Expand All @@ -156,45 +161,60 @@ static Compatibility<springfox.documentation.service.Parameter, RequestParameter
new springfox.documentation.schema.ModelRef(dataType, allowableValues), modelSpecification);
}

private static ModelSpecification modelSpecification(
ApiImplicitParam param) {
Class<?> clazz;
static ModelSpecification modelSpecification(ApiImplicitParam param) {
ModelSpecification scalarModel = null;
ModelSpecification referenceModel = null;
try {
param.dataTypeClass();
Class<?> clazz;
if (param.dataTypeClass() != Void.class) {
clazz = param.dataTypeClass();
} else {
clazz = Class.forName(param.dataType());
clazz = ClassUtils.forName(param.dataType(), null);
}
scalarModel = ScalarTypes.builtInScalarType(clazz)
.map(scalarModel(param))
.orElse(null);

ModelKey dataTypeKey = new ModelKeyBuilder()
.qualifiedModelName(q ->
q.namespace(safeGetPackageName(clazz))
.name(clazz.getSimpleName()))
.build();
referenceModel = referenceModelSpecification(dataTypeKey, param.allowMultiple());

} catch (ClassNotFoundException e) {
LOGGER.warn(
"Unable to interpret the implicit parameter configuration with dataType: {}, dataTypeClass: {}",
param.dataType(),
param.dataTypeClass());
return null;
}
ModelSpecification modelSpecification = ScalarTypes.builtInScalarType(clazz)
.map(scalar -> {
if (param.allowMultiple()) {
return new ModelSpecificationBuilder()
.collectionModel(c ->
c.model(m ->
m.scalarModel(scalar))
.collectionType(CollectionType.LIST))
.build();
}
return new ModelSpecificationBuilder()
.scalarModel(scalar)
.build();
})
ModelSpecification scalarFromType = ScalarType.from(param.type(), param.format())
.map(scalarModel(param))
.orElse(null);
if (modelSpecification == null) {
ModelKey dataTypeKey = new ModelKeyBuilder()
.qualifiedModelName(q -> q.namespace(safeGetPackageName(clazz)).name(clazz.getSimpleName()))

return Stream.of(scalarModel, referenceModel, scalarFromType)
.filter(Objects::nonNull)
.findFirst()
.orElse(null);
}

private static Function<ScalarType, ModelSpecification> scalarModel(ApiImplicitParam param) {
return scalar -> {
if (scalar == null) {
return null;
}
if (param.allowMultiple()) {
return new ModelSpecificationBuilder()
.collectionModel(c ->
c.model(m ->
m.scalarModel(scalar))
.collectionType(CollectionType.LIST))
.build();
}
return new ModelSpecificationBuilder()
.scalarModel(scalar)
.build();
modelSpecification = referenceModelSpecification(dataTypeKey, param.allowMultiple());
}
return modelSpecification;
};
}

private static ModelSpecification referenceModelSpecification(
Expand All @@ -215,8 +235,8 @@ private static ModelSpecification referenceModelSpecification(
.build();
}

private List<Compatibility<springfox.documentation.service.Parameter, RequestParameter>>
readParameters(OperationContext context) {
private List<Compatibility<springfox.documentation.service.Parameter, RequestParameter>> readParameters(
OperationContext context) {
Optional<ApiImplicitParam> annotation = context.findAnnotation(ApiImplicitParam.class);
List<Compatibility<springfox.documentation.service.Parameter, RequestParameter>> parameters = new ArrayList<>();
annotation.ifPresent(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package springfox.documentation.swagger.readers.operation

import spock.lang.Specification
import spock.lang.Unroll
import springfox.documentation.builders.ModelSpecificationBuilder
import springfox.documentation.builders.ReferenceModelSpecificationBuilder
import springfox.documentation.schema.Example
import springfox.documentation.schema.ScalarType
import springfox.documentation.swagger.readers.parameter.ApiImplicitParamAnnotationSupport

class OperationImplicitParameterReaderSpec extends Specification implements ApiImplicitParamAnnotationSupport {
@Unroll
def "Implicit params are evaluated correctly"() {
when:
def model = OperationImplicitParameterReader.modelSpecification(implicitParamAnnotation)

then:
model?.scalar?.orElse(null)?.type == expectedScalar
model?.reference?.orElse(null) == expectedReference
model?.collection
?.map { c -> c.model }
?.orElse(null) == collectionItemSpecification(expectedCollectionType)

where:
implicitParamAnnotation | expectedScalar | expectedCollectionType | expectedReference
apiImplicitParam() | null | null | null
apiImplicitParam("string") | ScalarType.STRING | null | null
apiImplicitParam("string", "byte") | ScalarType.BYTE | null | null
apiImplicitParam("string", "byte", "int") | ScalarType.INTEGER | null | null
apiImplicitParam("string", "byte", "int", Long) | ScalarType.LONG | null | null
apiImplicitParam("string", "byte", "int", Example) | null | null | reference(Example)
collectionApiImplicitParam() | null | null | null
collectionApiImplicitParam("string") | null | ScalarType.STRING | null
collectionApiImplicitParam("string", "byte") | null | ScalarType.BYTE | null
collectionApiImplicitParam("string", "byte", "int") | null | ScalarType.INTEGER | null
collectionApiImplicitParam("string", "byte", "int", Long) | null | ScalarType.LONG | null
collectionApiImplicitParam("string", "byte", "int", Example) | null | reference(Example) | null

}

def reference(Class clazz) {
new ReferenceModelSpecificationBuilder()
.key { k ->
k.qualifiedModelName {
q ->
q.name(clazz.simpleName)
.namespace(clazz.packageName)
}
}.build()
}

def collectionItemSpecification(type) {
if (type == null) {
return null
}
if (type instanceof ScalarType) {
return new ModelSpecificationBuilder()
.scalarModel(type)
.build()
}
return new ModelSpecificationBuilder()
.referenceModel {
r ->
r.copyOf(type)
}.build()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -78,14 +78,14 @@ class OperationImplicitParamsReaderSpec extends DocumentationContextSpec impleme
operationImplicitParameterReader.supports(DocumentationType.SWAGGER_12)
operationImplicitParameterReader.supports(DocumentationType.SWAGGER_2)
where:
handlerMethod | expectedSize
dummyHandlerMethod('dummyMethod') | 0
dummyHandlerMethod('methodWithApiImplicitParam') | 1
dummyHandlerMethod('methodWithApiImplicitParamAndInteger', Integer.class) | 2
dummyHandlerMethod('methodWithApiImplicitParamAndExample', Integer.class) | 2
dummyHandlerMethod('methodWithApiImplicitParamAndAllowMultiple', Integer.class) | 2
dummyHandlerMethod('methodWithApiImplicitParams', Integer.class) | 3
handlerMethodIn(apiImplicitParamsClass(), 'methodWithApiImplicitParam') | 2
dummyHandlerMethodIn(apiImplicitParamsAllowMultipleClass(), 'methodWithApiImplicitParam') | 3
handlerMethod | expectedSize
dummyHandlerMethod('dummyMethod') | 0
dummyHandlerMethod('methodWithApiImplicitParam') | 1
dummyHandlerMethod('methodWithApiImplicitParamAndInteger', Integer.class) | 2
dummyHandlerMethod('methodWithApiImplicitParamAndExample', Integer.class) | 2
dummyHandlerMethod('methodWithApiImplicitParamAndAllowMultiple', Integer.class) | 2
dummyHandlerMethod('methodWithApiImplicitParams', Integer.class) | 3
handlerMethodIn(apiImplicitParamsClass(), 'methodWithApiImplicitParam') | 2
dummyHandlerMethodIn(apiImplicitParamsAllowMultipleClass(), 'methodWithApiImplicitParam') | 3
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package springfox.documentation.swagger.readers.parameter

import io.swagger.annotations.ApiImplicitParam

trait ApiImplicitParamAnnotationSupport {
ApiImplicitParam apiParamWithType(type, format) {
apiImplicitParam(type, format)
}

ApiImplicitParam apiParamWithDataType(dataType) {
apiImplicitParam("", "", dataType)
}

ApiImplicitParam apiParamWithDataTypeClass(dataTypeClass) {
apiImplicitParam("", "", "", dataTypeClass)
}

ApiImplicitParam collectionApiImplicitParam(
type = "",
format = "",
dataType = "",
dataTypeClass = Void.class) {
[name : { -> "test" },
type : { -> type },
format : { -> format },
dataType : { -> dataType },
dataTypeClass: { -> dataTypeClass },
allowMultiple: { -> true }
] as ApiImplicitParam
}

ApiImplicitParam apiImplicitParam(
type = "",
format = "",
dataType = "",
dataTypeClass = Void.class) {
[name : { -> "test" },
type : { -> type },
format : { -> format },
dataType : { -> dataType },
dataTypeClass: { -> dataTypeClass },
allowMultiple: { -> false }
] as ApiImplicitParam
}

}

1 comment on commit 3cd0f7c

@esfomeado
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When will be 3.0.1 release to include this fix?

Thanks

Please sign in to comment.