Commit bb1fa37e authored by lidongdai's avatar lidongdai
Browse files

use swagger for api docs

parent dc55de4e
Loading
Loading
Loading
Loading
+10 −5
Original line number Diff line number Diff line
@@ -36,6 +36,8 @@ public class AppConfiguration implements WebMvcConfigurer {
  public static final String LOGIN_INTERCEPTOR_PATH_PATTERN = "/**/*";
  public static final String LOGIN_PATH_PATTERN = "/login";
  public static final String PATH_PATTERN = "/**";
  public static final String LOCALE_LANGUAGE_COOKIE = "language";
  public static final int COOKIE_MAX_AGE = 3600;


  @Bean
@@ -44,21 +46,24 @@ public class AppConfiguration implements WebMvcConfigurer {
  }


  //Cookie
  /**
   * Cookie
   */
  @Bean
  public LocaleResolver localeResolver() {
    CookieLocaleResolver localeResolver = new CookieLocaleResolver();
    localeResolver.setCookieName("localeCookie");
    //设置默认区域
    localeResolver.setCookieName(LOCALE_LANGUAGE_COOKIE);
    /** set default locale **/
    localeResolver.setDefaultLocale(Locale.ENGLISH);
    localeResolver.setCookieMaxAge(3600);//设置cookie有效期.
    /** set cookie max age **/
    localeResolver.setCookieMaxAge(COOKIE_MAX_AGE);
    return localeResolver;
  }

  @Bean
  public LocaleChangeInterceptor localeChangeInterceptor() {
    LocaleChangeInterceptor lci = new LocaleChangeInterceptor();
    // 参数名
    /**  **/
    lci.setParamName("lang");

    return lci;
+109 −0
Original line number Diff line number Diff line
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package cn.escheduler.api.configuration;

import com.google.common.base.MoreObjects;
import com.google.common.base.Optional;
import com.google.common.collect.Lists;
import io.swagger.annotations.ApiImplicitParam;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import springfox.documentation.builders.ParameterBuilder;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.service.AllowableValues;
import springfox.documentation.service.Parameter;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.OperationBuilderPlugin;
import springfox.documentation.spi.service.contexts.OperationContext;
import springfox.documentation.spring.web.DescriptionResolver;
import springfox.documentation.swagger.common.SwaggerPluginSupport;

import java.util.List;
import java.util.Locale;

import static com.google.common.base.Strings.emptyToNull;
import static springfox.documentation.schema.Types.isBaseType;
import static springfox.documentation.swagger.common.SwaggerPluginSupport.SWAGGER_PLUGIN_ORDER;
import static springfox.documentation.swagger.readers.parameter.Examples.examples;
import static springfox.documentation.swagger.schema.ApiModelProperties.allowableValueFromString;

@Component
@Order(Ordered.HIGHEST_PRECEDENCE - 10)
public class SwaggerApiImplicitParamPlugin implements OperationBuilderPlugin {

    @Autowired
    private DescriptionResolver descriptions;

    @Autowired
    private MessageSource messageSource;

    static Parameter implicitParameter(MessageSource messageSource, DescriptionResolver descriptions, ApiImplicitParam param) {
        Locale locale = LocaleContextHolder.getLocale();

        ModelRef modelRef = maybeGetModelRef(param);
        return new ParameterBuilder()
                .name(param.name())
                .description(descriptions.resolve(messageSource.getMessage(param.value(), null, locale)))
                .defaultValue(param.defaultValue())
                .required(param.required())
                .allowMultiple(param.allowMultiple())
                .modelRef(modelRef)
                .allowableValues(allowableValueFromString(param.allowableValues()))
                .parameterType(emptyToNull(param.paramType()))
                .parameterAccess(param.access())
                .order(SWAGGER_PLUGIN_ORDER)
                .scalarExample(param.example())
                .complexExamples(examples(param.examples()))
                .build();
    }

    private static ModelRef maybeGetModelRef(ApiImplicitParam param) {
        String dataType = MoreObjects.firstNonNull(emptyToNull(param.dataType()), "string");
        AllowableValues allowableValues = null;
        if (isBaseType(dataType)) {
            allowableValues = allowableValueFromString(param.allowableValues());
        }
        if (param.allowMultiple()) {
            return new ModelRef("", new ModelRef(dataType, allowableValues));
        }
        return new ModelRef(dataType, allowableValues);
    }

    @Override
    public void apply(OperationContext context) {
        context.operationBuilder().parameters(readParameters(context));
    }

    @Override
    public boolean supports(DocumentationType delimiter) {
        return SwaggerPluginSupport.pluginDoesApply(delimiter);
    }

    private List<Parameter> readParameters(OperationContext context) {
        Optional<ApiImplicitParam> annotation = context.findAnnotation(ApiImplicitParam.class);
        List<Parameter> parameters = Lists.newArrayList();
        if (annotation.isPresent()) {
            parameters.add(implicitParameter(messageSource, descriptions, annotation.get()));
        }
        return parameters;
    }

}
 No newline at end of file
+71 −0
Original line number Diff line number Diff line
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package cn.escheduler.api.configuration;

import com.google.common.base.Optional;
import com.google.common.collect.Lists;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import springfox.documentation.service.Parameter;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.OperationBuilderPlugin;
import springfox.documentation.spi.service.contexts.OperationContext;
import springfox.documentation.spring.web.DescriptionResolver;

import java.util.List;

import static springfox.documentation.swagger.common.SwaggerPluginSupport.pluginDoesApply;

@Component
@Order(Ordered.HIGHEST_PRECEDENCE - 10)
public class SwaggerApiImplicitParamsPlugin implements OperationBuilderPlugin {

    @Autowired
    private DescriptionResolver descriptions;
    @Autowired
    private MessageSource messageSource;

    @Override
    public void apply(OperationContext context) {
        context.operationBuilder().parameters(readParameters(context));
    }

    @Override
    public boolean supports(DocumentationType delimiter) {
        return pluginDoesApply(delimiter);
    }

    private List<Parameter> readParameters(OperationContext context) {
        Optional<ApiImplicitParams> annotation = context.findAnnotation(ApiImplicitParams.class);

        List<Parameter> parameters = Lists.newArrayList();
        if (annotation.isPresent()) {
            for (ApiImplicitParam param : annotation.get().value()) {
                parameters.add(SwaggerApiImplicitParamPlugin.implicitParameter(messageSource, descriptions, param));
            }
        }

        return parameters;
    }


}
 No newline at end of file
+74 −0
Original line number Diff line number Diff line
package cn.escheduler.api.configuration;



import com.fasterxml.classmate.TypeResolver;
import io.swagger.annotations.ApiModel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import springfox.documentation.schema.ModelReference;
import springfox.documentation.schema.TypeNameExtractor;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.schema.ModelBuilderPlugin;
import springfox.documentation.spi.schema.contexts.ModelContext;

import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

import static springfox.documentation.schema.ResolvedTypes.*;
import static springfox.documentation.swagger.common.SwaggerPluginSupport.*;

/**
 * NOTE : not useful
 */
@Component
@Order(Ordered.HIGHEST_PRECEDENCE - 10)
public class SwaggerApiModelPlugin implements ModelBuilderPlugin {

    @Autowired
    private TypeResolver typeResolver;
    @Autowired
    private TypeNameExtractor typeNameExtractor;
    @Autowired
    private MessageSource messageSource;

    @Override
    public void apply(ModelContext context) {
        ApiModel annotation = AnnotationUtils.findAnnotation(forClass(context), ApiModel.class);
        if (annotation != null) {
            List<ModelReference> modelRefs = new ArrayList<ModelReference>();
            for (Class<?> each : annotation.subTypes()) {
                modelRefs.add(modelRefFactory(context, typeNameExtractor)
                        .apply(typeResolver.resolve(each)));
            }
            Locale locale = LocaleContextHolder.getLocale();

            context.getBuilder()
                    .description(messageSource.getMessage(annotation.description(), null, locale))
                    .discriminator(annotation.discriminator())
                    .subTypes(modelRefs);
        }
    }

    private Class<?> forClass(ModelContext context) {
        return typeResolver.resolve(context.getType()).getErasedType();
    }


//    @Override
//    public boolean supports(DocumentationType delimiter) {
//        return pluginDoesApply(delimiter);
//    }

    @Override
    public boolean supports(DocumentationType delimiter) {
        return true;
    }
}
+219 −0
Original line number Diff line number Diff line
package cn.escheduler.api.configuration;


import com.fasterxml.classmate.ResolvedType;
import com.fasterxml.classmate.TypeResolver;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.base.Splitter;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import io.swagger.annotations.ApiModelProperty;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import springfox.documentation.service.AllowableListValues;
import springfox.documentation.service.AllowableRangeValues;
import springfox.documentation.service.AllowableValues;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.schema.ModelPropertyBuilderPlugin;
import springfox.documentation.spi.schema.contexts.ModelPropertyContext;
import springfox.documentation.spring.web.DescriptionResolver;
import springfox.documentation.swagger.common.SwaggerPluginSupport;
import springfox.documentation.swagger.schema.ApiModelProperties;

import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static com.google.common.collect.Lists.newArrayList;
import static org.springframework.util.StringUtils.hasText;
import static springfox.documentation.schema.Annotations.*;
import static springfox.documentation.swagger.schema.ApiModelProperties.*;

@Component
@Order(Ordered.HIGHEST_PRECEDENCE - 10)
public class SwaggerApiModelPropertyPlugin implements ModelPropertyBuilderPlugin {
    private static final Logger LOGGER = LoggerFactory.getLogger(ApiModelProperties.class);
    private static final Pattern RANGE_PATTERN = Pattern.compile("range([\\[(])(.*),(.*)([])])$");

    @Autowired
    private DescriptionResolver descriptions;
    @Autowired
    private MessageSource messageSource;


    @Override
    public void apply(ModelPropertyContext context) {
        Optional<ApiModelProperty> annotation = Optional.absent();

        if (context.getAnnotatedElement().isPresent()) {
            annotation = annotation.or(findApiModePropertyAnnotation(context.getAnnotatedElement().get()));
        }
        if (context.getBeanPropertyDefinition().isPresent()) {
            annotation = annotation.or(findPropertyAnnotation(
                    context.getBeanPropertyDefinition().get(),
                    ApiModelProperty.class));
        }
        if (annotation.isPresent()) {
            context.getBuilder()
                    .allowableValues(annotation.transform(toAllowableValues()).orNull())
                    .required(annotation.transform(toIsRequired()).or(false))
                    .readOnly(annotation.transform(toIsReadOnly()).or(false))
                    .description(annotation.transform(toDescription(descriptions)).orNull())
                    .isHidden(annotation.transform(toHidden()).or(false))
                    .type(annotation.transform(toType(context.getResolver())).orNull())
                    .position(annotation.transform(toPosition()).or(0))
                    .example(annotation.transform(toExample()).orNull());
        }
    }


    static Function<ApiModelProperty, AllowableValues> toAllowableValues() {
        return new Function<ApiModelProperty, AllowableValues>() {
            @Override
            public AllowableValues apply(ApiModelProperty annotation) {
                return allowableValueFromString(annotation.allowableValues());
            }
        };
    }

    public static AllowableValues allowableValueFromString(String allowableValueString) {
        AllowableValues allowableValues = new AllowableListValues(Lists.<String>newArrayList(), "LIST");
        String trimmed = allowableValueString.trim();
        Matcher matcher = RANGE_PATTERN.matcher(trimmed.replaceAll(" ", ""));
        if (matcher.matches()) {
            if (matcher.groupCount() != 4) {
                LOGGER.warn("Unable to parse range specified {} correctly", trimmed);
            } else {
                allowableValues = new AllowableRangeValues(
                        matcher.group(2).contains("infinity") ? null : matcher.group(2),
                        matcher.group(1).equals("("),
                        matcher.group(3).contains("infinity") ? null : matcher.group(3),
                        matcher.group(4).equals(")"));
            }
        } else if (trimmed.contains(",")) {
            Iterable<String> split = Splitter.on(',').trimResults().omitEmptyStrings().split(trimmed);
            allowableValues = new AllowableListValues(newArrayList(split), "LIST");
        } else if (hasText(trimmed)) {
            List<String> singleVal = Collections.singletonList(trimmed);
            allowableValues = new AllowableListValues(singleVal, "LIST");
        }
        return allowableValues;
    }

    static Function<ApiModelProperty, Boolean> toIsRequired() {
        return new Function<ApiModelProperty, Boolean>() {
            @Override
            public Boolean apply(ApiModelProperty annotation) {
                return annotation.required();
            }
        };
    }

    static Function<ApiModelProperty, Integer> toPosition() {
        return new Function<ApiModelProperty, Integer>() {
            @Override
            public Integer apply(ApiModelProperty annotation) {
                return annotation.position();
            }
        };
    }

    static Function<ApiModelProperty, Boolean> toIsReadOnly() {
        return new Function<ApiModelProperty, Boolean>() {
            @Override
            public Boolean apply(ApiModelProperty annotation) {
                return annotation.readOnly();
            }
        };
    }

    static Function<ApiModelProperty, Boolean> toAllowEmptyValue() {
        return new Function<ApiModelProperty, Boolean>() {
            @Override
            public Boolean apply(ApiModelProperty annotation) {
                return annotation.allowEmptyValue();
            }
        };
    }

     Function<ApiModelProperty, String> toDescription(
            final DescriptionResolver descriptions) {
        Locale locale = LocaleContextHolder.getLocale();

        return new Function<ApiModelProperty, String>() {
            @Override
            public String apply(ApiModelProperty annotation) {
                String description = "";
                if (!Strings.isNullOrEmpty(annotation.value())) {
                    description = messageSource.getMessage(annotation.value(), null, "" ,locale);
                } else if (!Strings.isNullOrEmpty(annotation.notes())) {
                    description = messageSource.getMessage(annotation.notes(), null, "" ,locale);
                }
                return descriptions.resolve(description);
            }
        };
    }

    static Function<ApiModelProperty, ResolvedType> toType(final TypeResolver resolver) {
        return new Function<ApiModelProperty, ResolvedType>() {
            @Override
            public ResolvedType apply(ApiModelProperty annotation) {
                try {
                    return resolver.resolve(Class.forName(annotation.dataType()));
                } catch (ClassNotFoundException e) {
                    return resolver.resolve(Object.class);
                }
            }
        };
    }

    public static Optional<ApiModelProperty> findApiModePropertyAnnotation(AnnotatedElement annotated) {
        Optional<ApiModelProperty> annotation = Optional.absent();

        if (annotated instanceof Method) {
            // If the annotated element is a method we can use this information to check superclasses as well
            annotation = Optional.fromNullable(AnnotationUtils.findAnnotation(((Method) annotated), ApiModelProperty.class));
        }

        return annotation.or(Optional.fromNullable(AnnotationUtils.getAnnotation(annotated, ApiModelProperty.class)));
    }

    static Function<ApiModelProperty, Boolean> toHidden() {
        return new Function<ApiModelProperty, Boolean>() {
            @Override
            public Boolean apply(ApiModelProperty annotation) {
                return annotation.hidden();
            }
        };
    }

    static Function<ApiModelProperty, String> toExample() {
        return new Function<ApiModelProperty, String>() {
            @Override
            public String apply(ApiModelProperty annotation) {
                String example = "";
                if (!Strings.isNullOrEmpty(annotation.example())) {
                    example = annotation.example();
                }
                return example;
            }
        };
    }

    @Override
    public boolean supports(DocumentationType delimiter) {
        return SwaggerPluginSupport.pluginDoesApply(delimiter);
    }
}
Loading