All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.hzlinks.swagger.rpc.reader.DubboReaderExtension Maven / Gradle / Ivy

There is a newer version: 1.0.7
Show newest version
package com.hzlinks.swagger.rpc.reader;


import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.type.TypeFactory;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.base.Splitter;
import com.google.common.collect.Collections2;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
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.AbstractSerializableParameter;
import io.swagger.models.parameters.BodyParameter;
import io.swagger.models.parameters.FormParameter;
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.PrimitiveType;
import io.swagger.util.ReflectionUtils;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
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.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang3.StringUtils;

public class DubboReaderExtension implements ReaderExtension {

    @Override
    public int getPriority() {
        return 0;
    }

    @Override
    public boolean isReadable(ReaderContext context) {
        final Api apiAnnotation = context.getCls().getAnnotation(Api.class);
        // read do not have Api annotation cls, skip hidden cls
        return apiAnnotation == null || (context.isReadHidden() || !apiAnnotation.hidden());
    }

    @Override
    public void applyConsumes(ReaderContext context, Operation operation, Method method) {
        final List consumes = new ArrayList();
        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(ReaderContext 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(ReaderContext 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(ReaderContext 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 void applyOperationId(Operation operation, Method method) {
        operation.operationId(method.getName());

    }

    @Override
    public void applySummary(Operation operation, Method method) {
        final ApiOperation apiOperation = ReflectionUtils.getAnnotation(method, ApiOperation.class);
        if (apiOperation != null && StringUtils.isNotBlank(apiOperation.value())) {
            operation.summary(apiOperation.value());
        }

    }

    @Override
    public void applyDescription(Operation operation, Method method) {
        final ApiOperation apiOperation = ReflectionUtils.getAnnotation(method, ApiOperation.class);
        if (apiOperation != null && StringUtils.isNotBlank(apiOperation.notes())) {
            operation.description(apiOperation.notes());
        }
        operation.description(operation.getDescription() == null ? outputMethod(method)
                : (outputMethod(method) + operation.getDescription()));
    }

    private String outputMethod(Method method) {
        try {
            Class[] types = method.getParameterTypes();
            Class[] exceptionTypes = method.getExceptionTypes();
            StringBuilder sb = new StringBuilder();

            sb.append(method.getReturnType().getSimpleName()).append(" ").append(method.getName());

            sb.append('(');
            for (int j = 0; j < types.length; j++) {
                sb.append(types[j].getName());
                if (j < (types.length - 1)) {
                    sb.append(",");
                }
            }
            sb.append(')');
            if (exceptionTypes.length > 0) {
                sb.append(" throws ");
                for (int j = 0; j < exceptionTypes.length; j++) {
                    sb.append(exceptionTypes[j].getSimpleName());
                    if (j < (exceptionTypes.length - 1)) {
                        sb.append(",");
                    }
                }
            }
            return sb.toString();
        } catch (Exception e) {
            return "<" + e + ">";
        }

    }

    @Override
    public void applySchemes(ReaderContext 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(ReaderContext context, Operation operation,
            Method method) {
    }

    @Override
    public void applyTags(ReaderContext context, Operation operation, Method method) {
        final List tags = new ArrayList();

        final Api apiAnnotation = context.getCls().getAnnotation(Api.class);
        if (apiAnnotation != null) {
            Collection filter = Collections2.filter(Arrays.asList(apiAnnotation.tags()),
                    new Predicate() {
                        @Override
                        public boolean apply(String input) {
                            return StringUtils.isNotBlank(input);
                        }
                    });
            if (filter.isEmpty()) {
                tags.add(context.getInterfaceCls().getSimpleName());
            } else {
                tags.addAll(filter);
            }
        } else {
            tags.add(context.getInterfaceCls().getSimpleName());
        }
        tags.addAll(context.getParentTags());

        final ApiOperation apiOperation = ReflectionUtils.getAnnotation(method, ApiOperation.class);
        if (apiOperation != null) {
            tags.addAll(Collections2.filter(Arrays.asList(apiOperation.tags()),
                    new Predicate() {
                        @Override
                        public boolean apply(String input) {
                            return StringUtils.isNotBlank(input);
                        }
                    }));
        }

        for (String tag : tags) {
            operation.tag(tag);
        }

    }

    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(ReaderContext 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(ReaderContext 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) {
                final Property responseProperty = ContainerWrapper
                        .wrapContainer(getResponseContainer(apiOperation), 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(ReaderContext context, Operation operation, Type type,
            Annotation[] annotations) {
    }

    private void applyParametersV2(ReaderContext context, Operation operation, String name,
            Type type, Class cls, Annotation[] annotations, Annotation[] interfaceParamAnnotations) {
        Annotation apiParam = null;
        if (annotations != null) {
            for (Annotation annotation : interfaceParamAnnotations) {
                if (annotation instanceof ApiParam) {
                    apiParam = annotation;
                    break;
                }
            }
            if (null == apiParam) {
                for (Annotation annotation : annotations) {
                    if (annotation instanceof ApiParam) {
                        apiParam = annotation;
                        break;
                    }
                }
            }
        }
        final Parameter parameter = readParam(context.getSwagger(), type, cls,
                null == apiParam ? null : (ApiParam) apiParam);
        if (parameter != null) {
            parameter.setName(null == name ? parameter.getName() : name);
            operation.parameter(parameter);
        }
    }

    private Parameter readParam(Swagger swagger, Type type, Class cls, ApiParam param) {
        PrimitiveType fromType = PrimitiveType.fromType(type);
        final Parameter para = null == fromType ? new BodyParameter() : new QueryParameter();
        Parameter parameter;
        if (type == null) {
            if (null == param) {
                parameter = ParameterProcessor.applyAnnotations(swagger, para,
                        String.class, new ArrayList());
            } else {
                parameter = ParameterProcessor.applyAnnotations(swagger, para,
                        String.class, Collections.singletonList(param));
            }
        } else {
            if (null == param) {
                parameter = ParameterProcessor.applyAnnotations(swagger, para,
                        type, new ArrayList());
            } else {
                parameter = ParameterProcessor.applyAnnotations(swagger, para,
                        type, Collections.singletonList(param));
            }
        }
        if (parameter instanceof AbstractSerializableParameter) {
            final AbstractSerializableParameter p = (AbstractSerializableParameter) parameter;
            if (p.getType() == null) {
                p.setType(null == fromType ? "string" : fromType.getCommonName());
            }
            p.setRequired(p.getRequired() == true || cls.isPrimitive());
        } else {
            //hack: Get the from data model paramter from BodyParameter
            BodyParameter bp = (BodyParameter) parameter;
            bp.setIn("formData");
        }
        return parameter;
    }

    @Override
    public void applyParameters(ReaderContext context, Operation operation, Method method,
            Method interfaceMethod) {
        try {
            String[] parameterNames = NameDiscover.parameterNameDiscover.getParameterNames(method);
            Type[] genericParameterTypes = method.getGenericParameterTypes();
            Class[] parameterTypes = method.getParameterTypes();
            Annotation[][] parameterAnnotations = method.getParameterAnnotations();
            Annotation[][] interfaceParamAnnotations = interfaceMethod.getParameterAnnotations();
            for (int i = 0; i < genericParameterTypes.length; i++) {
                applyParametersV2(context, operation,
                        null == parameterNames ? null : parameterNames[i], genericParameterTypes[i], parameterTypes[i],
                        parameterAnnotations[i], interfaceParamAnnotations[i]);
            }
        } catch (SecurityException e) {
            e.printStackTrace();
        }

    }

    @Override
    public void applyImplicitParameters(ReaderContext context, Operation operation, Method method) {
        final ApiImplicitParams implicitParams = method.getAnnotation(ApiImplicitParams.class);
        if (implicitParams != null && implicitParams.value().length > 0) {
            for (ApiImplicitParam param : implicitParams.value()) {
                final Parameter p = readImplicitParam(context.getSwagger(), param);
                if (p != null) {
                    operation.parameter(p);
                }
            }
        }
    }

    private Parameter readImplicitParam(Swagger swagger, ApiImplicitParam param) {
        PrimitiveType fromType = PrimitiveType.fromName(param.paramType());
        final Parameter p = null == fromType ? new FormParameter() : new QueryParameter();
        final Type type = ReflectionUtils.typeFromString(param.dataType());
        return ParameterProcessor.applyAnnotations(swagger, p, type == null ? String.class : type,
                Collections.singletonList(param));
    }

    @Override
    public void applyExtensions(ReaderContext 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 ArrayProperty(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