
com.zlyx.easyapi.extension.ApiReaderExtension Maven / Gradle / Ivy
package com.zlyx.easyapi.extension;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang3.StringUtils;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.type.TypeFactory;
import com.google.common.base.Function;
import com.google.common.base.Splitter;
import com.zlyx.easyapi.context.ApiContext;
import com.zlyx.easyapi.property.ListProperty;
import com.zlyx.easycore.tool.EasyBuffer;
import com.zlyx.easycore.tool.Ops;
import com.zlyx.easycore.utils.MethodUtils;
import com.zlyx.easycore.utils.TypeUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiModelProperty;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;
import io.swagger.annotations.ResponseHeader;
import io.swagger.converter.ModelConverters;
import io.swagger.models.HttpMethod;
import io.swagger.models.Model;
import io.swagger.models.Operation;
import io.swagger.models.Response;
import io.swagger.models.Scheme;
import io.swagger.models.Swagger;
import io.swagger.models.parameters.Parameter;
import io.swagger.models.parameters.QueryParameter;
import io.swagger.models.properties.ArrayProperty;
import io.swagger.models.properties.MapProperty;
import io.swagger.models.properties.Property;
import io.swagger.models.properties.RefProperty;
import io.swagger.util.BaseReaderUtils;
import io.swagger.util.ParameterProcessor;
import io.swagger.util.PathUtils;
import io.swagger.util.ReflectionUtils;
@SuppressWarnings("deprecation")
public class ApiReaderExtension implements ReaderExtension {
@Override
public int getPriority() {
return 0;
}
@Override
public boolean isReadable(ApiContext context) {
Api api = context.getInterfaceCls().getAnnotation(Api.class);
return api == null || !context.isReadHidden() || !api.hidden();
}
@Override
public void applyConsumes(ApiContext context, Operation operation, Method method) {
final Set consumes = new HashSet<>();
final ApiOperation apiOperation = ReflectionUtils.getAnnotation(method, ApiOperation.class);
if (apiOperation != null) {
consumes.addAll(parseStringValues(apiOperation.consumes()));
}
if (consumes.isEmpty()) {
final Api apiAnnotation = context.getCls().getAnnotation(Api.class);
if (apiAnnotation != null) {
consumes.addAll(parseStringValues(apiAnnotation.consumes()));
}
consumes.addAll(context.getParentConsumes());
}
for (String consume : consumes) {
operation.consumes(consume);
}
}
private static List parseStringValues(String str) {
return parseAnnotationValues(str, new Function() {
@Override
public String apply(String value) {
return value;
}
});
}
private static List parseAnnotationValues(String str, Function processor) {
final List result = new ArrayList();
for (String item : Splitter.on(",").trimResults().omitEmptyStrings().split(str)) {
result.add(processor.apply(item));
}
return result;
}
@Override
public void applyProduces(ApiContext context, Operation operation, Method method) {
final List produces = new ArrayList();
final ApiOperation apiOperation = ReflectionUtils.getAnnotation(method, ApiOperation.class);
if (apiOperation != null) {
produces.addAll(parseStringValues(apiOperation.produces()));
}
if (produces.isEmpty()) {
final Api apiAnnotation = context.getCls().getAnnotation(Api.class);
if (apiAnnotation != null) {
produces.addAll(parseStringValues(apiAnnotation.produces()));
}
produces.addAll(context.getParentProduces());
}
for (String produce : produces) {
operation.produces(produce);
}
}
@Override
public String getHttpMethod(ApiContext context, Method method) {
final ApiOperation apiOperation = ReflectionUtils.getAnnotation(method, ApiOperation.class);
return apiOperation == null || StringUtils.isEmpty(apiOperation.httpMethod()) ? HttpMethod.POST.name()
: apiOperation.httpMethod();
}
@Override
public String getPath(ApiContext context, Method method) {
final ApiOperation apiOperation = ReflectionUtils.getAnnotation(method, ApiOperation.class);
String operationId = null == apiOperation ? ""
: StringUtils.isBlank(apiOperation.nickname()) ? null : apiOperation.nickname();
return PathUtils.collectPath(context.getParentPath(), context.getInterfaceCls().getName(), method.getName(),
operationId);
}
@Override
public String applyTags(ApiContext context, Operation operation, Method method) {
String tag = context.getInterfaceCls().getSimpleName();
Api apiAnnotation = method.getDeclaringClass().getAnnotation(Api.class);
if (apiAnnotation != null && StringUtils.isNotBlank(apiAnnotation.value())) {
tag = apiAnnotation.value();
}
operation.tag(tag);
return tag;
}
@Override
public void applyOperationId(Operation operation, Method method) {
operation.operationId(method.getName());
}
@Override
public void applySummary(Operation operation, Method method) {
ApiOperation apiOperation = ReflectionUtils.getAnnotation(method, ApiOperation.class);
if (apiOperation != null && StringUtils.isNotBlank(apiOperation.value())) {
operation.summary(EasyBuffer.newString(method.getName(), "(", apiOperation.value(), ")"));
} else {
operation.summary(method.getName());
}
}
@Override
public void applyDescription(Operation operation, Method method) {
ApiOperation apiOperation = ReflectionUtils.getAnnotation(method, ApiOperation.class);
if (apiOperation != null && StringUtils.isNotBlank(apiOperation.notes())) {
operation.description(apiOperation.notes());
} else {
operation.description(method.getName());
}
}
@Override
public void applySchemes(ApiContext context, Operation operation, Method method) {
final List schemes = new ArrayList();
final ApiOperation apiOperation = ReflectionUtils.getAnnotation(method, ApiOperation.class);
final Api apiAnnotation = context.getCls().getAnnotation(Api.class);
if (apiOperation != null) {
schemes.addAll(parseSchemes(apiOperation.protocols()));
}
if (schemes.isEmpty() && apiAnnotation != null) {
schemes.addAll(parseSchemes(apiAnnotation.protocols()));
}
for (Scheme scheme : schemes) {
operation.scheme(scheme);
}
}
private static List parseSchemes(String schemes) {
final List result = new ArrayList();
for (String item : StringUtils.trimToEmpty(schemes).split(",")) {
final Scheme scheme = Scheme.forValue(StringUtils.trimToNull(item));
if (scheme != null && !result.contains(scheme)) {
result.add(scheme);
}
}
return result;
}
@Override
public void setDeprecated(Operation operation, Method method) {
operation.deprecated(ReflectionUtils.getAnnotation(method, Deprecated.class) != null);
}
@Override
public void applySecurityRequirements(ApiContext context, Operation operation, Method method) {
}
private static final String SUCCESSFUL_OPERATION = "";
private static boolean isValidResponse(Type type) {
final JavaType javaType = TypeFactory.defaultInstance().constructType(type);
return !ReflectionUtils.isVoid(javaType);
}
private static Map parseResponseHeaders(ApiContext context, ResponseHeader[] headers) {
Map responseHeaders = null;
for (ResponseHeader header : headers) {
final String name = header.name();
if (StringUtils.isNotEmpty(name)) {
if (responseHeaders == null) {
responseHeaders = new HashMap();
}
final Class> cls = header.response();
if (!ReflectionUtils.isVoid(cls)) {
final Property property = ModelConverters.getInstance().readAsProperty(cls);
if (property != null) {
final Property responseProperty = ContainerWrapper.wrapContainer(header.responseContainer(),
property, ContainerWrapper.ARRAY, ContainerWrapper.LIST, ContainerWrapper.SET);
responseProperty.setDescription(header.description());
responseHeaders.put(name, responseProperty);
appendModels(context.getSwagger(), cls);
}
}
}
}
return responseHeaders;
}
private static void appendModels(Swagger swagger, Type type) {
final Map models = ModelConverters.getInstance().readAll(type);
for (Map.Entry entry : models.entrySet()) {
swagger.model(entry.getKey(), entry.getValue());
}
}
private static Type getResponseType(Method method) {
final ApiOperation apiOperation = ReflectionUtils.getAnnotation(method, ApiOperation.class);
if (apiOperation != null && !ReflectionUtils.isVoid(apiOperation.response())) {
return apiOperation.response();
} else {
return method.getGenericReturnType();
}
}
private static String getResponseContainer(ApiOperation apiOperation) {
return apiOperation == null ? null : StringUtils.defaultIfBlank(apiOperation.responseContainer(), null);
}
@Override
public void applyResponses(ApiContext context, Operation operation, Method method) {
final Map result = new HashMap();
final ApiOperation apiOperation = ReflectionUtils.getAnnotation(method, ApiOperation.class);
if (apiOperation != null && StringUtils.isNotBlank(apiOperation.responseReference())) {
final Response response = new Response().description(SUCCESSFUL_OPERATION);
response.schema(new RefProperty(apiOperation.responseReference()));
result.put(apiOperation.code(), response);
}
final Type responseType = getResponseType(method);
if (isValidResponse(responseType)) {
final Property property = ModelConverters.getInstance().readAsProperty(responseType);
if (property != null) {
String container = getResponseContainer(apiOperation);
if (Ops.isEmpty(container)) {
container = method.getReturnType().getSimpleName().toLowerCase();
}
final Property responseProperty = ContainerWrapper.wrapContainer(container, property);
final int responseCode = apiOperation == null ? 200 : apiOperation.code();
final Map defaultResponseHeaders = apiOperation == null
? Collections.emptyMap()
: parseResponseHeaders(context, apiOperation.responseHeaders());
final Response response = new Response().description(SUCCESSFUL_OPERATION).schema(responseProperty)
.headers(defaultResponseHeaders);
result.put(responseCode, response);
appendModels(context.getSwagger(), responseType);
}
}
final ApiResponses responseAnnotation = ReflectionUtils.getAnnotation(method, ApiResponses.class);
if (responseAnnotation != null) {
for (ApiResponse apiResponse : responseAnnotation.value()) {
final Map responseHeaders = parseResponseHeaders(context,
apiResponse.responseHeaders());
final Response response = new Response().description(apiResponse.message()).headers(responseHeaders);
if (StringUtils.isNotEmpty(apiResponse.reference())) {
response.schema(new RefProperty(apiResponse.reference()));
} else if (!ReflectionUtils.isVoid(apiResponse.response())) {
final Type type = apiResponse.response();
final Property property = ModelConverters.getInstance().readAsProperty(type);
if (property != null) {
response.schema(ContainerWrapper.wrapContainer(apiResponse.responseContainer(), property));
appendModels(context.getSwagger(), type);
}
}
result.put(apiResponse.code(), response);
}
}
for (Map.Entry responseEntry : result.entrySet()) {
if (responseEntry.getKey() == 0) {
operation.defaultResponse(responseEntry.getValue());
} else {
operation.response(responseEntry.getKey(), responseEntry.getValue());
}
}
}
@Override
public void applyParameters(ApiContext context, Operation operation, Type type, Annotation[] annotations) {
}
@Override
public void applyParameters(ApiContext context, Operation operation, Method method) {
try {
Type[] genericParameterTypes = method.getGenericParameterTypes();
Class>[] parameterTypes = method.getParameterTypes();
java.lang.reflect.Parameter[] parameter = method.getParameters();
String description = null;
boolean required = false;
ApiParam apiParam = null;
for (int i = 0; i < genericParameterTypes.length; i++) {
apiParam = parameter[i].getAnnotation(ApiParam.class);
description = apiParam != null ? apiParam.value() : parameter[i].getName();
required = apiParam != null ? apiParam.required() : false;
applyParametersV2(context, operation, parameter[i].getName(), description, required,
genericParameterTypes[i], parameterTypes[i], null, null);
}
} catch (SecurityException e) {
e.printStackTrace();
}
}
private void applyParametersV2(ApiContext context, Operation operation, String name, String description,
boolean required, Type type, Class> cls, Class> pCls, String pName) {
if (cls.getName().contains("java") || "T".equals(type.getTypeName())
|| TypeUtils.isBaseType(cls.getSimpleName().toLowerCase())) {
final QueryParameter parameter = new QueryParameter();
if (pName != null) {
description = EasyBuffer.newString("类型为", pCls.getSimpleName(), "的参数", pName, "的内部属性:", name);
name = pName + ":" + name;
}
parameter.setName(name);
parameter.setDescription(description);
if ("T".equals(type.getTypeName())) {
parameter.setType("object");
} else if (cls.isAssignableFrom(Collection.class) || cls.isAssignableFrom(Map.class)) {
parameter.setType(type.getTypeName().replace("<", "<").replace(">", ">"));
} else {
parameter.setType(cls.getSimpleName());
}
parameter.setRequired(required);
parameter.setAccess("true");
operation.parameter(parameter);
} else {
Field[] fields = cls.getDeclaredFields();
ApiModelProperty apiModelProperty = null;
String fname = null;
String fdescription = null;
for (Field field : fields) {
if (!Modifier.isStatic(field.getModifiers()) && !Modifier.isStatic(field.getModifiers())) {
apiModelProperty = field.getAnnotation(ApiModelProperty.class);
fname = field.getName();
fdescription = apiModelProperty != null ? apiModelProperty.value() : fname;
required = apiModelProperty != null ? apiModelProperty.required() : required;
applyParametersV2(context, operation, field.getName(), fdescription, required, field.getType(),
field.getType(), cls, name);
}
}
}
}
@Override
public void applyImplicitParameters(ApiContext context, Operation operation, Method method) {
final ApiImplicitParams implicitParams = method.getAnnotation(ApiImplicitParams.class);
if (implicitParams != null && implicitParams.value().length > 0) {
Map parameters = MethodUtils.getParamterMap(method);
for (ApiImplicitParam param : implicitParams.value()) {
final Parameter p = readImplicitParam(context.getSwagger(), parameters, param);
if (p != null) {
operation.parameter(p);
}
}
}
}
private Parameter readImplicitParam(Swagger swagger, Map parameters,
ApiImplicitParam param) {
final Parameter p = new QueryParameter();
Type type = null;
if (!"".equals(param.name())) {
type = ReflectionUtils.typeFromString(param.dataType());
}
if (type == null && parameters.containsKey(param.name())) {
type = parameters.get(param.name()).getType();
}
return ParameterProcessor.applyAnnotations(swagger, p, type == null ? String.class : type,
Collections.singletonList(param));
}
@Override
public void applyExtensions(ApiContext context, Operation operation, Method method) {
final ApiOperation apiOperation = method.getAnnotation(ApiOperation.class);
if (apiOperation != null) {
operation.getVendorExtensions().putAll(BaseReaderUtils.parseExtensions(apiOperation.extensions()));
}
}
enum ContainerWrapper {
LIST("list") {
@Override
protected Property doWrap(Property property) {
return new ListProperty(property);
}
},
ARRAY("array") {
@Override
protected Property doWrap(Property property) {
return new ArrayProperty(property);
}
},
MAP("map") {
@Override
protected Property doWrap(Property property) {
return new MapProperty(property);
}
},
SET("set") {
@Override
protected Property doWrap(Property property) {
ArrayProperty arrayProperty = new ArrayProperty(property);
arrayProperty.setUniqueItems(true);
return arrayProperty;
}
};
private final String container;
ContainerWrapper(String container) {
this.container = container;
}
public static Property wrapContainer(String container, Property property, ContainerWrapper... allowed) {
final Set tmp = allowed.length > 0 ? EnumSet.copyOf(Arrays.asList(allowed))
: EnumSet.allOf(ContainerWrapper.class);
for (ContainerWrapper wrapper : tmp) {
final Property prop = wrapper.wrap(container, property);
if (prop != null) {
return prop;
}
}
return property;
}
public Property wrap(String container, Property property) {
if (this.container.equalsIgnoreCase(container)) {
return doWrap(property);
}
return null;
}
protected abstract Property doWrap(Property property);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy