ParameterProcessor.java
package io.swagger.v3.core.util;
import com.fasterxml.jackson.annotation.JsonView;
import io.swagger.v3.core.converter.AnnotatedType;
import io.swagger.v3.core.converter.ModelConverters;
import io.swagger.v3.core.converter.ResolvedSchema;
import io.swagger.v3.core.jackson.ModelResolver;
import io.swagger.v3.oas.annotations.enums.Explode;
import io.swagger.v3.oas.annotations.media.ExampleObject;
import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.examples.Example;
import io.swagger.v3.oas.models.media.ArraySchema;
import io.swagger.v3.oas.models.media.Content;
import io.swagger.v3.oas.models.media.Schema;
import io.swagger.v3.oas.models.parameters.Parameter;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.validation.constraints.Size;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import static io.swagger.v3.core.util.ValidationAnnotationsUtils.JAVAX_SIZE;
import static io.swagger.v3.core.util.ValidationAnnotationsUtils.applySizeConstraint;
public class ParameterProcessor {
private static final String VALUE_METHOD = "value";
private static final String FORM_PARAMETER = "form";
static Logger LOGGER = LoggerFactory.getLogger(ParameterProcessor.class);
public static Parameter applyAnnotations(Parameter parameter, Type type, List<Annotation> annotations, Components components, String[] classTypes, String[] methodTypes, JsonView jsonViewAnnotation) {
return applyAnnotations(parameter, type, annotations, components, classTypes, methodTypes, jsonViewAnnotation, false);
}
public static Parameter applyAnnotations(
Parameter parameter,
Type type,
List<Annotation> annotations,
Components components,
String[] classTypes,
String[] methodTypes,
JsonView jsonViewAnnotation,
boolean openapi31) {
return applyAnnotations(parameter, type, annotations, components, classTypes, methodTypes, jsonViewAnnotation, openapi31, null);
}
public static Parameter applyAnnotations(
Parameter parameter,
Type type,
List<Annotation> annotations,
Components components,
String[] classTypes,
String[] methodTypes,
JsonView jsonViewAnnotation,
boolean openapi31,
Schema.SchemaResolution schemaResolution) {
Configuration configuration = new Configuration();
configuration.setOpenAPI31(openapi31);
configuration.setSchemaResolution(schemaResolution);
return applyAnnotations(parameter, type, annotations, components, classTypes, methodTypes, jsonViewAnnotation, configuration);
}
public static Parameter applyAnnotations(
Parameter parameter,
Type type,
List<Annotation> annotations,
Components components,
String[] classTypes,
String[] methodTypes,
JsonView jsonViewAnnotation,
Configuration configuration) {
boolean openapi31 = configuration != null && configuration.isOpenAPI31() != null && configuration.isOpenAPI31();
Schema.SchemaResolution schemaResolution = configuration.getSchemaResolution();;
final AnnotationsHelper helper = new AnnotationsHelper(annotations, type);
if (helper.isContext()) {
return null;
}
if (parameter == null) {
// consider it to be body param
parameter = new Parameter();
}
// first handle schema
List<Annotation> reworkedAnnotations = new ArrayList<>(annotations);
Annotation paramSchemaOrArrayAnnotation = getParamSchemaAnnotation(annotations);
if (paramSchemaOrArrayAnnotation != null) {
reworkedAnnotations.add(paramSchemaOrArrayAnnotation);
}
io.swagger.v3.oas.annotations.media.Schema ctxSchema = AnnotationsUtils.getSchemaAnnotation(annotations.toArray(new Annotation[0]));
io.swagger.v3.oas.annotations.media.ArraySchema ctxArraySchema = AnnotationsUtils.getArraySchemaAnnotation(annotations.toArray(new Annotation[0]));
Annotation[] ctxAnnotation31 = null;
if (Schema.SchemaResolution.ALL_OF.equals(schemaResolution) || Schema.SchemaResolution.ALL_OF_REF.equals(schemaResolution)) {
List<Annotation> ctxAnnotations31List = new ArrayList<>();
if (annotations != null) {
for (Annotation a : annotations) {
if (
!(a instanceof io.swagger.v3.oas.annotations.media.Schema) &&
!(a instanceof io.swagger.v3.oas.annotations.media.ArraySchema)) {
ctxAnnotations31List.add(a);
}
}
ctxAnnotation31 = ctxAnnotations31List.toArray(new Annotation[ctxAnnotations31List.size()]);
}
}
AnnotatedType annotatedType = new AnnotatedType()
.type(type)
.resolveAsRef(true)
.skipOverride(true)
.jsonViewAnnotation(jsonViewAnnotation);
if (Schema.SchemaResolution.ALL_OF.equals(schemaResolution) || Schema.SchemaResolution.ALL_OF_REF.equals(schemaResolution)) {
annotatedType.ctxAnnotations(ctxAnnotation31);
} else {
annotatedType.ctxAnnotations(reworkedAnnotations.toArray(new Annotation[reworkedAnnotations.size()]));
}
final ResolvedSchema resolvedSchema = ModelConverters.getInstance(configuration).resolveAsResolvedSchema(annotatedType);
if (resolvedSchema.schema != null) {
Schema resSchema = AnnotationsUtils.clone(resolvedSchema.schema, openapi31);
Schema ctxSchemaObject = null;
if (Schema.SchemaResolution.ALL_OF.equals(schemaResolution) || Schema.SchemaResolution.ALL_OF_REF.equals(schemaResolution)) {
Optional<Schema> reResolvedSchema = AnnotationsUtils.getSchemaFromAnnotation(ctxSchema, annotatedType.getComponents(), null, openapi31, null, schemaResolution, null);
if (reResolvedSchema.isPresent()) {
ctxSchemaObject = reResolvedSchema.get();
}
reResolvedSchema = AnnotationsUtils.getArraySchema(ctxArraySchema, annotatedType.getComponents(), null, openapi31, ctxSchemaObject);
if (reResolvedSchema.isPresent()) {
ctxSchemaObject = reResolvedSchema.get();
}
}
if (Schema.SchemaResolution.ALL_OF.equals(schemaResolution) && ctxSchemaObject != null) {
resSchema = new Schema()
.addAllOfItem(ctxSchemaObject)
.addAllOfItem(resolvedSchema.schema);
} else if (Schema.SchemaResolution.ALL_OF_REF.equals(schemaResolution) && ctxSchemaObject != null) {
resSchema = ctxSchemaObject
.addAllOfItem(resolvedSchema.schema);
}
parameter.setSchema(resSchema);
}
resolvedSchema.referencedSchemas.forEach(components::addSchemas);
// handle first FormParam as it affects Explode resolving
for (Annotation annotation : annotations) {
if (annotation.annotationType().getName().equals("javax.ws.rs.FormParam")) {
try {
String name = (String) annotation.annotationType().getMethod(VALUE_METHOD).invoke(annotation);
if (StringUtils.isNotBlank(name)) {
parameter.setName(name);
}
} catch (Exception e) {
}
// set temporarily to "form" to inform caller that we need to further process along other form parameters
parameter.setIn(FORM_PARAMETER);
} else if (annotation.annotationType().getName().endsWith("FormDataParam")) {
try {
String name = (String) annotation.annotationType().getMethod(VALUE_METHOD).invoke(annotation);
if (StringUtils.isNotBlank(name)) {
parameter.setName(name);
}
} catch (Exception e) {
}
// set temporarily to "form" to inform caller that we need to further process along other form parameters
parameter.setIn(FORM_PARAMETER);
}
}
for (Annotation annotation : annotations) {
if (annotation instanceof io.swagger.v3.oas.annotations.Parameter) {
io.swagger.v3.oas.annotations.Parameter p = (io.swagger.v3.oas.annotations.Parameter) annotation;
if (p.hidden()) {
return null;
}
if (StringUtils.isNotBlank(p.ref())) {
parameter = new Parameter().$ref(p.ref());
return parameter;
}
if (StringUtils.isNotBlank(p.description())) {
parameter.setDescription(p.description());
}
if (StringUtils.isNotBlank(p.name())) {
parameter.setName(p.name());
}
if (StringUtils.isNotBlank(p.in().toString())) {
parameter.setIn(p.in().toString());
}
if (StringUtils.isNotBlank(p.example())) {
try {
parameter.setExample(Json.mapper().readTree(p.example()));
} catch (IOException e) {
parameter.setExample(p.example());
}
}
if (p.deprecated()) {
parameter.setDeprecated(p.deprecated());
}
if (p.required()) {
parameter.setRequired(p.required());
}
if (p.allowEmptyValue()) {
parameter.setAllowEmptyValue(p.allowEmptyValue());
}
if (p.allowReserved()) {
parameter.setAllowReserved(p.allowReserved());
}
Map<String, Example> exampleMap = new LinkedHashMap<>();
if (p.examples().length == 1 && StringUtils.isBlank(p.examples()[0].name())) {
Optional<Example> exampleOptional = AnnotationsUtils.getExample(p.examples()[0], true);
if (exampleOptional.isPresent()) {
parameter.setExample(exampleOptional.get());
}
} else {
for (ExampleObject exampleObject : p.examples()) {
AnnotationsUtils.getExample(exampleObject).ifPresent(example -> exampleMap.put(exampleObject.name(), example));
}
}
if (exampleMap.size() > 0) {
parameter.setExamples(exampleMap);
}
if (p.extensions().length > 0) {
Map<String, Object> extensionMap = AnnotationsUtils.getExtensions(openapi31, p.extensions());
if (extensionMap != null && ! extensionMap.isEmpty()) {
extensionMap.forEach(parameter::addExtension);
}
}
Optional<Content> content = AnnotationsUtils.getContent(p.content(), classTypes, methodTypes, parameter.getSchema(), null, jsonViewAnnotation);
if (content.isPresent()) {
parameter.setContent(content.get());
parameter.setSchema(null);
}
setParameterStyle(parameter, p);
setParameterExplode(parameter, p);
} else if (annotation.annotationType().getName().equals("javax.ws.rs.PathParam")) {
try {
String name = (String) annotation.annotationType().getMethod(VALUE_METHOD).invoke(annotation);
if (StringUtils.isNotBlank(name)) {
parameter.setName(name);
}
} catch (Exception ignored) {
}
} else if (annotation.annotationType().getName().equals(JAVAX_SIZE)) {
try {
if (parameter.getSchema() == null) {
parameter.setSchema(new ArraySchema());
}
if (isArraySchema(parameter.getSchema())) {
Schema as = parameter.getSchema();
Size size = (Size) annotation;
applySizeConstraint(as, size);
}
} catch (Exception e) {
LOGGER.error("failed on " + annotation.annotationType().getName(), e);
}
} else if (ModelResolver.NOT_NULL_ANNOTATIONS.contains(annotation.annotationType().getSimpleName())) {
parameter.setRequired(true);
}
}
final String defaultValue = helper.getDefaultValue();
Schema paramSchema = parameter.getSchema();
if (paramSchema == null) {
if (parameter.getContent() != null && !parameter.getContent().values().isEmpty()) {
paramSchema = parameter.getContent().values().iterator().next().getSchema();
}
}
if (paramSchema != null) {
if (isArraySchema(paramSchema)) {
if (defaultValue != null) {
paramSchema.getItems().setDefault(defaultValue);
}
} else {
if (defaultValue != null) {
paramSchema.setDefault(defaultValue);
}
}
}
return parameter;
}
public static boolean isArraySchema(Schema schema) {
return SchemaTypeUtils.isArraySchema(schema);
}
public static void setParameterExplode(Parameter parameter, io.swagger.v3.oas.annotations.Parameter p) {
if (isExplodable(p, parameter)) {
if (Explode.TRUE.equals(p.explode())) {
parameter.setExplode(Boolean.TRUE);
} else if (Explode.FALSE.equals(p.explode())) {
parameter.setExplode(Boolean.FALSE);
}
}
}
private static boolean isExplodable(io.swagger.v3.oas.annotations.Parameter p, Parameter parameter) {
io.swagger.v3.oas.annotations.media.Schema schema = AnnotationsUtils.hasArrayAnnotation(p.array()) ? p.array().schema() : p.schema();
boolean explode = true;
if (schema != null) {
Class implementation = schema.implementation();
if (implementation == Void.class) {
if (!AnnotationsUtils.isExplodableOAS30(schema) && !schema.type().isEmpty()) {
explode = false;
}
if (schema.types().length != 0 && !AnnotationsUtils.isExplodableOAS31(schema)) {
explode = false;
}
}
}
return explode;
}
public static void setParameterStyle(Parameter parameter, io.swagger.v3.oas.annotations.Parameter p) {
if (StringUtils.isNotBlank(p.style().toString())) {
parameter.setStyle(Parameter.StyleEnum.valueOf(p.style().toString().toUpperCase()));
}
}
public static Annotation getParamSchemaAnnotation(List<Annotation> annotations) {
if (annotations == null) {
return null;
}
io.swagger.v3.oas.annotations.media.Schema rootSchema = null;
io.swagger.v3.oas.annotations.media.ArraySchema rootArraySchema = null;
io.swagger.v3.oas.annotations.media.Schema contentSchema = null;
io.swagger.v3.oas.annotations.media.Schema paramSchema = null;
io.swagger.v3.oas.annotations.media.ArraySchema paramArraySchema = null;
for (Annotation annotation : annotations) {
if (annotation instanceof io.swagger.v3.oas.annotations.media.Schema) {
rootSchema = (io.swagger.v3.oas.annotations.media.Schema) annotation;
} else if (annotation instanceof io.swagger.v3.oas.annotations.media.ArraySchema) {
rootArraySchema = (io.swagger.v3.oas.annotations.media.ArraySchema) annotation;
} else if (annotation instanceof io.swagger.v3.oas.annotations.Parameter) {
io.swagger.v3.oas.annotations.Parameter paramAnnotation = (io.swagger.v3.oas.annotations.Parameter) annotation;
if (paramAnnotation.content().length > 0) {
if (AnnotationsUtils.hasSchemaAnnotation(paramAnnotation.content()[0].schema())) {
contentSchema = paramAnnotation.content()[0].schema();
}
}
if (AnnotationsUtils.hasSchemaAnnotation(paramAnnotation.schema())) {
paramSchema = paramAnnotation.schema();
}
if (AnnotationsUtils.hasArrayAnnotation(paramAnnotation.array())) {
paramArraySchema = paramAnnotation.array();
}
}
}
if (rootSchema != null || rootArraySchema != null) {
return null;
}
if (contentSchema != null) {
return contentSchema;
}
if (paramSchema != null) {
return paramSchema;
}
if (paramArraySchema != null) {
return paramArraySchema;
}
return null;
}
public static Type getParameterType(io.swagger.v3.oas.annotations.Parameter paramAnnotation) {
return getParameterType(paramAnnotation, false);
}
public static Type getParameterType(io.swagger.v3.oas.annotations.Parameter paramAnnotation, boolean nullIfNotFound) {
if (paramAnnotation == null) {
return null;
}
io.swagger.v3.oas.annotations.media.Schema contentSchema = null;
io.swagger.v3.oas.annotations.media.Schema paramSchema = null;
io.swagger.v3.oas.annotations.media.ArraySchema paramArraySchema = null;
if (paramAnnotation.content().length > 0) {
if (AnnotationsUtils.hasSchemaAnnotation(paramAnnotation.content()[0].schema())) {
contentSchema = paramAnnotation.content()[0].schema();
}
}
if (AnnotationsUtils.hasSchemaAnnotation(paramAnnotation.schema())) {
paramSchema = paramAnnotation.schema();
}
if (AnnotationsUtils.hasArrayAnnotation(paramAnnotation.array())) {
paramArraySchema = paramAnnotation.array();
}
if (contentSchema != null) {
return AnnotationsUtils.getSchemaType(contentSchema, nullIfNotFound);
}
if (paramSchema != null) {
return AnnotationsUtils.getSchemaType(paramSchema, nullIfNotFound);
}
if (paramArraySchema != null) {
return AnnotationsUtils.getSchemaType(paramArraySchema.schema(), nullIfNotFound);
}
if (nullIfNotFound) {
return null;
}
return String.class;
}
public static final String MEDIA_TYPE = "*/*";
/**
* The <code>AnnotationsHelper</code> class defines helper methods for
* accessing supported parameter annotations.
*/
private static class AnnotationsHelper {
private boolean context;
private String defaultValue;
/**
* Constructs an instance.
*
* @param annotations array or parameter annotations
*/
public AnnotationsHelper(List<Annotation> annotations, Type _type) {
String rsDefault = null;
if (annotations != null) {
for (Annotation item : annotations) {
if ("javax.ws.rs.core.Context".equals(item.annotationType().getName())) {
context = true;
} else if ("javax.ws.rs.DefaultValue".equals(item.annotationType().getName())) {
try {
rsDefault = (String) item.annotationType().getMethod(VALUE_METHOD).invoke(item);
} catch (Exception ex) {
LOGGER.error("Invocation of value method failed", ex);
}
}
}
}
defaultValue = rsDefault;
}
/**
*/
public boolean isContext() {
return context;
}
/**
* Returns default value from annotation.
*
* @return default value from annotation
*/
public String getDefaultValue() {
return defaultValue;
}
}
}