@ -0,0 +1,109 @@ |
|||||||
|
/* |
||||||
|
* 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; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,71 @@ |
|||||||
|
/* |
||||||
|
* 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; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
} |
@ -0,0 +1,74 @@ |
|||||||
|
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; |
||||||
|
} |
||||||
|
} |
||||||
|
|
@ -0,0 +1,219 @@ |
|||||||
|
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); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,141 @@ |
|||||||
|
package cn.escheduler.api.configuration; |
||||||
|
|
||||||
|
import java.util.List; |
||||||
|
import java.util.Locale; |
||||||
|
import java.util.Set; |
||||||
|
|
||||||
|
import com.google.common.base.Function; |
||||||
|
import com.google.common.base.Optional; |
||||||
|
import com.google.common.base.Splitter; |
||||||
|
import com.google.common.collect.Sets; |
||||||
|
import io.swagger.annotations.Api; |
||||||
|
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.Order; |
||||||
|
import org.springframework.stereotype.Component; |
||||||
|
|
||||||
|
import io.swagger.annotations.ApiOperation; |
||||||
|
import org.springframework.util.StringUtils; |
||||||
|
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.spring.web.readers.operation.DefaultTagsProvider; |
||||||
|
import springfox.documentation.swagger.common.SwaggerPluginSupport; |
||||||
|
|
||||||
|
import static com.google.common.base.Strings.nullToEmpty; |
||||||
|
import static com.google.common.collect.FluentIterable.from; |
||||||
|
import static com.google.common.collect.Lists.newArrayList; |
||||||
|
import static com.google.common.collect.Sets.*; |
||||||
|
import static springfox.documentation.service.Tags.emptyTags; |
||||||
|
|
||||||
|
|
||||||
|
@Component |
||||||
|
@Order(Ordered.HIGHEST_PRECEDENCE - 10) |
||||||
|
public class SwaggerApiOperationPlugin implements OperationBuilderPlugin { |
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(SwaggerApiOperationPlugin.class); |
||||||
|
|
||||||
|
@Autowired |
||||||
|
private DescriptionResolver descriptions; |
||||||
|
@Autowired |
||||||
|
private DefaultTagsProvider tagsProvider; |
||||||
|
|
||||||
|
@Autowired |
||||||
|
private MessageSource messageSource; |
||||||
|
|
||||||
|
@Override |
||||||
|
public void apply(OperationContext context) { |
||||||
|
|
||||||
|
Locale locale = LocaleContextHolder.getLocale(); |
||||||
|
|
||||||
|
Set<String> defaultTags = tagsProvider.tags(context); |
||||||
|
Sets.SetView<String> tags = union(operationTags(context), controllerTags(context)); |
||||||
|
if (tags.isEmpty()) { |
||||||
|
context.operationBuilder().tags(defaultTags); |
||||||
|
} else { |
||||||
|
context.operationBuilder().tags(tags); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
Optional<ApiOperation> apiOperationAnnotation = context.findAnnotation(ApiOperation.class); |
||||||
|
if (apiOperationAnnotation.isPresent()) { |
||||||
|
ApiOperation operation = apiOperationAnnotation.get(); |
||||||
|
|
||||||
|
if (StringUtils.hasText(operation.nickname())) { |
||||||
|
// Populate the value of nickname annotation into uniqueId
|
||||||
|
context.operationBuilder().uniqueId(operation.nickname()); |
||||||
|
context.operationBuilder().codegenMethodNameStem(operation.nickname()); |
||||||
|
} |
||||||
|
|
||||||
|
if (StringUtils.hasText(apiOperationAnnotation.get().notes())) { |
||||||
|
context.operationBuilder().notes(descriptions.resolve(messageSource.getMessage(apiOperationAnnotation.get().notes(), null, "", locale))); |
||||||
|
} |
||||||
|
|
||||||
|
if (apiOperationAnnotation.get().position() > 0) { |
||||||
|
context.operationBuilder().position(apiOperationAnnotation.get().position()); |
||||||
|
} |
||||||
|
|
||||||
|
if (StringUtils.hasText(apiOperationAnnotation.get().value())) { |
||||||
|
context.operationBuilder().summary(descriptions.resolve(apiOperationAnnotation.get().value())); |
||||||
|
} |
||||||
|
|
||||||
|
context.operationBuilder().consumes(asSet(nullToEmpty(apiOperationAnnotation.get().consumes()))); |
||||||
|
context.operationBuilder().produces(asSet(nullToEmpty(apiOperationAnnotation.get().produces()))); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
private Set<String> controllerTags(OperationContext context) { |
||||||
|
Optional<Api> controllerAnnotation = context.findControllerAnnotation(Api.class); |
||||||
|
return controllerAnnotation.transform(tagsFromController()).or(Sets.<String>newHashSet()); |
||||||
|
} |
||||||
|
|
||||||
|
private Set<String> operationTags(OperationContext context) { |
||||||
|
Optional<ApiOperation> annotation = context.findAnnotation(ApiOperation.class); |
||||||
|
return annotation.transform(tagsFromOperation()).or(Sets.<String>newHashSet()); |
||||||
|
} |
||||||
|
|
||||||
|
private Function<ApiOperation, Set<String>> tagsFromOperation() { |
||||||
|
return new Function<ApiOperation, Set<String>>() { |
||||||
|
@Override |
||||||
|
public Set<String> apply(ApiOperation input) { |
||||||
|
Set<String> tags = newTreeSet(); |
||||||
|
tags.addAll(from(newArrayList(input.tags())).filter(emptyTags()).toSet()); |
||||||
|
return tags; |
||||||
|
} |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
private Function<Api, Set<String>> tagsFromController() { |
||||||
|
return new Function<Api, Set<String>>() { |
||||||
|
@Override |
||||||
|
public Set<String> apply(Api input) { |
||||||
|
Set<String> tags = newTreeSet(); |
||||||
|
tags.addAll(from(newArrayList(input.tags())).filter(emptyTags()).toSet()); |
||||||
|
return tags; |
||||||
|
} |
||||||
|
}; |
||||||
|
} |
||||||
|
private Set<String> asSet(String mediaTypes) { |
||||||
|
return newHashSet(Splitter.on(',') |
||||||
|
.trimResults() |
||||||
|
.omitEmptyStrings() |
||||||
|
.splitToList(mediaTypes)); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean supports(DocumentationType delimiter) { |
||||||
|
return SwaggerPluginSupport.pluginDoesApply(delimiter); |
||||||
|
// return true;
|
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,115 @@ |
|||||||
|
/* |
||||||
|
* |
||||||
|
* Copyright 2015-2019 the original author or authors. |
||||||
|
* |
||||||
|
* Licensed 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.fasterxml.classmate.ResolvedType; |
||||||
|
import com.google.common.base.Function; |
||||||
|
import com.google.common.base.Optional; |
||||||
|
import io.swagger.annotations.ApiParam; |
||||||
|
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.schema.Collections; |
||||||
|
import springfox.documentation.schema.Enums; |
||||||
|
import springfox.documentation.schema.Example; |
||||||
|
import springfox.documentation.service.AllowableValues; |
||||||
|
import springfox.documentation.spi.DocumentationType; |
||||||
|
import springfox.documentation.spi.schema.EnumTypeDeterminer; |
||||||
|
import springfox.documentation.spi.service.ParameterBuilderPlugin; |
||||||
|
import springfox.documentation.spi.service.contexts.ParameterContext; |
||||||
|
import springfox.documentation.spring.web.DescriptionResolver; |
||||||
|
import springfox.documentation.swagger.schema.ApiModelProperties; |
||||||
|
|
||||||
|
import java.util.Locale; |
||||||
|
|
||||||
|
import static com.google.common.base.Strings.emptyToNull; |
||||||
|
import static com.google.common.base.Strings.isNullOrEmpty; |
||||||
|
import static springfox.documentation.swagger.common.SwaggerPluginSupport.SWAGGER_PLUGIN_ORDER; |
||||||
|
import static springfox.documentation.swagger.common.SwaggerPluginSupport.pluginDoesApply; |
||||||
|
import static springfox.documentation.swagger.readers.parameter.Examples.examples; |
||||||
|
|
||||||
|
@Component |
||||||
|
@Order(Ordered.HIGHEST_PRECEDENCE - 10) |
||||||
|
public class SwaggerApiParamPlugin implements ParameterBuilderPlugin { |
||||||
|
@Autowired |
||||||
|
private DescriptionResolver descriptions; |
||||||
|
@Autowired |
||||||
|
private EnumTypeDeterminer enumTypeDeterminer; |
||||||
|
@Autowired |
||||||
|
private MessageSource messageSource; |
||||||
|
|
||||||
|
@Override |
||||||
|
public void apply(ParameterContext context) { |
||||||
|
Optional<ApiParam> apiParam = context.resolvedMethodParameter().findAnnotation(ApiParam.class); |
||||||
|
context.parameterBuilder() |
||||||
|
.allowableValues(allowableValues( |
||||||
|
context.alternateFor(context.resolvedMethodParameter().getParameterType()), |
||||||
|
apiParam.transform(toAllowableValue()).or(""))); |
||||||
|
if (apiParam.isPresent()) { |
||||||
|
Locale locale = LocaleContextHolder.getLocale(); |
||||||
|
|
||||||
|
ApiParam annotation = apiParam.get(); |
||||||
|
context.parameterBuilder().name(emptyToNull(annotation.name())) |
||||||
|
.description(emptyToNull(descriptions.resolve(messageSource.getMessage(annotation.value(), null, "",locale)))) |
||||||
|
.parameterAccess(emptyToNull(annotation.access())) |
||||||
|
.defaultValue(emptyToNull(annotation.defaultValue())) |
||||||
|
.allowMultiple(annotation.allowMultiple()) |
||||||
|
.allowEmptyValue(annotation.allowEmptyValue()) |
||||||
|
.required(annotation.required()) |
||||||
|
.scalarExample(new Example(annotation.example())) |
||||||
|
.complexExamples(examples(annotation.examples())) |
||||||
|
.hidden(annotation.hidden()) |
||||||
|
.collectionFormat(annotation.collectionFormat()) |
||||||
|
.order(SWAGGER_PLUGIN_ORDER); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private Function<ApiParam, String> toAllowableValue() { |
||||||
|
return new Function<ApiParam, String>() { |
||||||
|
@Override |
||||||
|
public String apply(ApiParam input) { |
||||||
|
return input.allowableValues(); |
||||||
|
} |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
private AllowableValues allowableValues(ResolvedType parameterType, String allowableValueString) { |
||||||
|
AllowableValues allowableValues = null; |
||||||
|
if (!isNullOrEmpty(allowableValueString)) { |
||||||
|
allowableValues = ApiModelProperties.allowableValueFromString(allowableValueString); |
||||||
|
} else { |
||||||
|
if (enumTypeDeterminer.isEnum(parameterType.getErasedType())) { |
||||||
|
allowableValues = Enums.allowableValues(parameterType.getErasedType()); |
||||||
|
} |
||||||
|
if (Collections.isContainerType(parameterType)) { |
||||||
|
allowableValues = Enums.allowableValues(Collections.collectionElementType(parameterType).getErasedType()); |
||||||
|
} |
||||||
|
} |
||||||
|
return allowableValues; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean supports(DocumentationType delimiter) { |
||||||
|
return pluginDoesApply(delimiter); |
||||||
|
} |
||||||
|
} |
||||||
|
|
@ -0,0 +1,74 @@ |
|||||||
|
/* |
||||||
|
* 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 io.swagger.annotations.Api; |
||||||
|
import org.apache.commons.lang.StringUtils; |
||||||
|
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.Order; |
||||||
|
import org.springframework.stereotype.Component; |
||||||
|
import springfox.documentation.spi.DocumentationType; |
||||||
|
import springfox.documentation.spi.service.OperationBuilderPlugin; |
||||||
|
import springfox.documentation.spi.service.contexts.OperationContext; |
||||||
|
|
||||||
|
import java.util.HashSet; |
||||||
|
import java.util.List; |
||||||
|
import java.util.Locale; |
||||||
|
import java.util.Set; |
||||||
|
|
||||||
|
@Component |
||||||
|
@Order(Ordered.HIGHEST_PRECEDENCE - 10) |
||||||
|
public class SwaggerApiPlugin implements OperationBuilderPlugin { |
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(SwaggerApiPlugin.class); |
||||||
|
|
||||||
|
@Autowired |
||||||
|
private MessageSource messageSource; |
||||||
|
|
||||||
|
@Override |
||||||
|
public void apply(OperationContext context) { |
||||||
|
Locale locale = LocaleContextHolder.getLocale(); |
||||||
|
|
||||||
|
List<Api> list = context.findAllAnnotations(Api.class); |
||||||
|
if (list.size() > 0) { |
||||||
|
Api api = list.get(0); |
||||||
|
|
||||||
|
Set<String> tagsSet = new HashSet<>(1); |
||||||
|
|
||||||
|
if(api.tags() != null && api.tags().length > 0){ |
||||||
|
tagsSet.add(StringUtils.isNotBlank(api.tags()[0]) ? messageSource.getMessage(api.tags()[0], null, locale) : " "); |
||||||
|
} |
||||||
|
|
||||||
|
context.operationBuilder().hidden(api.hidden()) |
||||||
|
.tags(tagsSet).build(); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean supports(DocumentationType delimiter) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -1,52 +0,0 @@ |
|||||||
package cn.escheduler.api.configuration; |
|
||||||
|
|
||||||
import java.util.List; |
|
||||||
import java.util.Locale; |
|
||||||
import com.fasterxml.classmate.TypeResolver; |
|
||||||
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.Order; |
|
||||||
import org.springframework.stereotype.Component; |
|
||||||
|
|
||||||
import io.swagger.annotations.ApiOperation; |
|
||||||
import springfox.documentation.spi.DocumentationType; |
|
||||||
import springfox.documentation.spi.service.OperationBuilderPlugin; |
|
||||||
import springfox.documentation.spi.service.contexts.OperationContext; |
|
||||||
|
|
||||||
|
|
||||||
@Component |
|
||||||
@Order(Ordered.HIGHEST_PRECEDENCE - 10) |
|
||||||
public class SwaggerI18nPlugin implements OperationBuilderPlugin { |
|
||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(SwaggerI18nPlugin.class); |
|
||||||
|
|
||||||
@Autowired |
|
||||||
private MessageSource messageSource; |
|
||||||
|
|
||||||
@Override |
|
||||||
public void apply(OperationContext context) { |
|
||||||
|
|
||||||
Locale locale = LocaleContextHolder.getLocale(); |
|
||||||
|
|
||||||
List<ApiOperation> list = context.findAllAnnotations(ApiOperation.class); |
|
||||||
if (list.size() > 0) { |
|
||||||
for(ApiOperation api : list){ |
|
||||||
context.operationBuilder().summary(messageSource.getMessage(api.value(), null, locale)); |
|
||||||
context.operationBuilder().notes(messageSource.getMessage(api.notes(), null, locale)); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
@Override |
|
||||||
public boolean supports(DocumentationType delimiter) { |
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -1,5 +1,98 @@ |
|||||||
welcome=hello, welcome ! |
QUERY_SCHEDULE_LIST_NOTES=query schedule list |
||||||
test=test |
SCHEDULE=schedule |
||||||
userName=user name |
WARNING_TYPE=warning type(sending strategy) |
||||||
userPassword=user password |
WARNING_GROUP_ID=warning group id |
||||||
loginNotes=login notes |
FAILURE_STRATEGY=failure strategy |
||||||
|
RECEIVERS=receivers |
||||||
|
RECEIVERS_CC=receivers cc |
||||||
|
WORKER_GROUP_ID=worker server group id |
||||||
|
PROCESS_INSTANCE_PRIORITY=process instance priority |
||||||
|
UPDATE_SCHEDULE_NOTES=update schedule |
||||||
|
SCHEDULE_ID=schedule id |
||||||
|
ONLINE_SCHEDULE_NOTES=online schedule |
||||||
|
OFFLINE_SCHEDULE_NOTES=offline schedule |
||||||
|
QUERY_SCHEDULE_NOTES=query schedule |
||||||
|
QUERY_SCHEDULE_LIST_PAGING_NOTES=query schedule list paging |
||||||
|
LOGIN_TAG=User login related operations |
||||||
|
USER_NAME=user name |
||||||
|
PROJECT_NAME=project name |
||||||
|
CREATE_PROCESS_DEFINITION_NOTES=create process definition |
||||||
|
PROCESS_DEFINITION_NAME=process definition name |
||||||
|
PROCESS_DEFINITION_JSON=process definition detail info (json format) |
||||||
|
PROCESS_DEFINITION_LOCATIONS=process definition node locations info (json format) |
||||||
|
PROCESS_INSTANCE_LOCATIONS=process instance node locations info (json format) |
||||||
|
PROCESS_DEFINITION_CONNECTS=process definition node connects info (json format) |
||||||
|
PROCESS_INSTANCE_CONNECTS=process instance node connects info (json format) |
||||||
|
PROCESS_DEFINITION_DESC=process definition desc |
||||||
|
PROCESS_DEFINITION_TAG=process definition related opertation |
||||||
|
SIGNOUT_NOTES=logout |
||||||
|
USER_PASSWORD=user password |
||||||
|
UPDATE_PROCESS_INSTANCE_NOTES=update process instance |
||||||
|
QUERY_PROCESS_INSTANCE_LIST_NOTES=query process instance list |
||||||
|
VERIFY_PROCCESS_DEFINITION_NAME_NOTES=verify proccess definition name |
||||||
|
LOGIN_NOTES=user login |
||||||
|
UPDATE_PROCCESS_DEFINITION_NOTES=update proccess definition |
||||||
|
PROCESS_DEFINITION_ID=process definition id |
||||||
|
RELEASE_PROCCESS_DEFINITION_NOTES=release proccess definition |
||||||
|
QUERY_PROCCESS_DEFINITION_BY_ID_NOTES=query proccess definition by id |
||||||
|
QUERY_PROCCESS_DEFINITION_LIST_NOTES=query proccess definition list |
||||||
|
QUERY_PROCCESS_DEFINITION_LIST_PAGING_NOTES=query proccess definition list paging |
||||||
|
PAGE_NO=page no |
||||||
|
PROCESS_INSTANCE_ID=process instance id |
||||||
|
PROCESS_INSTANCE_JSON=process instance info(json format) |
||||||
|
SCHEDULE_TIME=schedule time |
||||||
|
SYNC_DEFINE=update the information of the process instance to the process definition\ |
||||||
|
|
||||||
|
RECOVERY_PROCESS_INSTANCE_FLAG=whether to recovery process instance |
||||||
|
SEARCH_VAL=search val |
||||||
|
USER_ID=user id |
||||||
|
PAGE_SIZE=page size |
||||||
|
LIMIT=limit |
||||||
|
VIEW_TREE_NOTES=view tree |
||||||
|
GET_NODE_LIST_BY_DEFINITION_ID_NOTES=get task node list by process definition id |
||||||
|
PROCESS_DEFINITION_ID_LIST=PROCESS DEFINITION ID LIST |
||||||
|
QUERY_PROCESS_INSTANCE_BY_ID_NOTES=query process instance by process instance id |
||||||
|
DELETE_PROCESS_INSTANCE_BY_ID_NOTES=delete process instance by process instance id |
||||||
|
TASK_ID=TASK INSTANCE ID |
||||||
|
SKIP_LINE_NUM=skip line num |
||||||
|
QUERY_TASK_INSTANCE_LOG_NOTES=QUERY TASK INSTANCE LOG |
||||||
|
DOWNLOAD_TASK_INSTANCE_LOG_NOTES=download task instance log |
||||||
|
USERS_TAG=users related operation |
||||||
|
SCHEDULER_TAG=scheduler related operation |
||||||
|
CREATE_SCHEDULE_NOTES=CREATE SCHEDULE |
||||||
|
CREATE_USER_NOTES=create user |
||||||
|
TENANT_ID=tenant id |
||||||
|
QUEUE=queue |
||||||
|
EMAIL=email |
||||||
|
PHONE=phone |
||||||
|
QUERY_USER_LIST_NOTES=query user list |
||||||
|
UPDATE_USER_NOTES=update user |
||||||
|
DELETE_USER_BY_ID_NOTES=delete user by id |
||||||
|
GRANT_PROJECT_NOTES=GRANT PROJECT |
||||||
|
PROJECT_IDS=project ids(string format, multiple projects separated by ",") |
||||||
|
GRANT_RESOURCE_NOTES=grant resource file |
||||||
|
RESOURCE_IDS=resource ids(string format, multiple resources separated by ",") |
||||||
|
GET_USER_INFO_NOTES=get user info |
||||||
|
LIST_USER_NOTES=list user |
||||||
|
VERIFY_USER_NAME_NOTES=verify user name |
||||||
|
UNAUTHORIZED_USER_NOTES=cancel authorization |
||||||
|
ALERT_GROUP_ID=alert group id |
||||||
|
AUTHORIZED_USER_NOTES=authorized user |
||||||
|
GRANT_UDF_FUNC_NOTES=grant udf function |
||||||
|
UDF_IDS=udf ids(string format, multiple udf functions separated by ",") |
||||||
|
GRANT_DATASOURCE_NOTES=GRANT DATASOURCE |
||||||
|
DATASOURCE_IDS=datasource ids(string format, multiple datasources separated by ",") |
||||||
|
QUERY_SUBPROCESS_INSTANCE_BY_TASK_ID_NOTES=query subprocess instance by task instance id |
||||||
|
QUERY_PARENT_PROCESS_INSTANCE_BY_SUB_PROCESS_INSTANCE_ID_NOTES=query parent process instance info by sub process instance id |
||||||
|
QUERY_PROCESS_INSTANCE_GLOBAL_VARIABLES_AND_LOCAL_VARIABLES_NOTES=query process instance global variables and local variables |
||||||
|
VIEW_GANTT_NOTES=view gantt |
||||||
|
SUB_PROCESS_INSTANCE_ID=sub process instance id |
||||||
|
TASK_NAME=task instance name |
||||||
|
TASK_INSTANCE_TAG=task instance related operation |
||||||
|
LOGGER_TAG=log related operation |
||||||
|
PROCESS_INSTANCE_TAG=process instance related operation |
||||||
|
EXECUTION_STATUS=runing status for workflow and task nodes |
||||||
|
HOST=ip address of running task |
||||||
|
START_DATE=start date |
||||||
|
END_DATE=end date |
||||||
|
QUERY_TASK_LIST_BY_PROCESS_INSTANCE_ID_NOTES=query task list by process instance id |
@ -1,5 +1,98 @@ |
|||||||
welcome=hello, welcome ! |
QUERY_SCHEDULE_LIST_NOTES=query schedule list |
||||||
test=test |
SCHEDULE=schedule |
||||||
userName=user name |
WARNING_TYPE=warning type(sending strategy) |
||||||
userPassword=user password |
WARNING_GROUP_ID=warning group id |
||||||
loginNotes=login notes |
FAILURE_STRATEGY=failure strategy |
||||||
|
RECEIVERS=receivers |
||||||
|
RECEIVERS_CC=receivers cc |
||||||
|
WORKER_GROUP_ID=worker server group id |
||||||
|
PROCESS_INSTANCE_PRIORITY=process instance priority |
||||||
|
UPDATE_SCHEDULE_NOTES=update schedule |
||||||
|
SCHEDULE_ID=schedule id |
||||||
|
ONLINE_SCHEDULE_NOTES=online schedule |
||||||
|
OFFLINE_SCHEDULE_NOTES=offline schedule |
||||||
|
QUERY_SCHEDULE_NOTES=query schedule |
||||||
|
QUERY_SCHEDULE_LIST_PAGING_NOTES=query schedule list paging |
||||||
|
LOGIN_TAG=User login related operations |
||||||
|
USER_NAME=user name |
||||||
|
PROJECT_NAME=project name |
||||||
|
CREATE_PROCESS_DEFINITION_NOTES=create process definition |
||||||
|
PROCESS_DEFINITION_NAME=process definition name |
||||||
|
PROCESS_DEFINITION_JSON=process definition detail info (json format) |
||||||
|
PROCESS_DEFINITION_LOCATIONS=process definition node locations info (json format) |
||||||
|
PROCESS_INSTANCE_LOCATIONS=process instance node locations info (json format) |
||||||
|
PROCESS_DEFINITION_CONNECTS=process definition node connects info (json format) |
||||||
|
PROCESS_INSTANCE_CONNECTS=process instance node connects info (json format) |
||||||
|
PROCESS_DEFINITION_DESC=process definition desc |
||||||
|
PROCESS_DEFINITION_TAG=process definition related opertation |
||||||
|
SIGNOUT_NOTES=logout |
||||||
|
USER_PASSWORD=user password |
||||||
|
UPDATE_PROCESS_INSTANCE_NOTES=update process instance |
||||||
|
QUERY_PROCESS_INSTANCE_LIST_NOTES=query process instance list |
||||||
|
VERIFY_PROCCESS_DEFINITION_NAME_NOTES=verify proccess definition name |
||||||
|
LOGIN_NOTES=user login |
||||||
|
UPDATE_PROCCESS_DEFINITION_NOTES=update proccess definition |
||||||
|
PROCESS_DEFINITION_ID=process definition id |
||||||
|
RELEASE_PROCCESS_DEFINITION_NOTES=release proccess definition |
||||||
|
QUERY_PROCCESS_DEFINITION_BY_ID_NOTES=query proccess definition by id |
||||||
|
QUERY_PROCCESS_DEFINITION_LIST_NOTES=query proccess definition list |
||||||
|
QUERY_PROCCESS_DEFINITION_LIST_PAGING_NOTES=query proccess definition list paging |
||||||
|
PAGE_NO=page no |
||||||
|
PROCESS_INSTANCE_ID=process instance id |
||||||
|
PROCESS_INSTANCE_JSON=process instance info(json format) |
||||||
|
SCHEDULE_TIME=schedule time |
||||||
|
SYNC_DEFINE=update the information of the process instance to the process definition\ |
||||||
|
|
||||||
|
RECOVERY_PROCESS_INSTANCE_FLAG=whether to recovery process instance |
||||||
|
SEARCH_VAL=search val |
||||||
|
USER_ID=user id |
||||||
|
PAGE_SIZE=page size |
||||||
|
LIMIT=limit |
||||||
|
VIEW_TREE_NOTES=view tree |
||||||
|
GET_NODE_LIST_BY_DEFINITION_ID_NOTES=get task node list by process definition id |
||||||
|
PROCESS_DEFINITION_ID_LIST=PROCESS DEFINITION ID LIST |
||||||
|
QUERY_PROCESS_INSTANCE_BY_ID_NOTES=query process instance by process instance id |
||||||
|
DELETE_PROCESS_INSTANCE_BY_ID_NOTES=delete process instance by process instance id |
||||||
|
TASK_ID=TASK INSTANCE ID |
||||||
|
SKIP_LINE_NUM=skip line num |
||||||
|
QUERY_TASK_INSTANCE_LOG_NOTES=QUERY TASK INSTANCE LOG |
||||||
|
DOWNLOAD_TASK_INSTANCE_LOG_NOTES=download task instance log |
||||||
|
USERS_TAG=users related operation |
||||||
|
SCHEDULER_TAG=scheduler related operation |
||||||
|
CREATE_SCHEDULE_NOTES=CREATE SCHEDULE |
||||||
|
CREATE_USER_NOTES=create user |
||||||
|
TENANT_ID=tenant id |
||||||
|
QUEUE=queue |
||||||
|
EMAIL=email |
||||||
|
PHONE=phone |
||||||
|
QUERY_USER_LIST_NOTES=query user list |
||||||
|
UPDATE_USER_NOTES=update user |
||||||
|
DELETE_USER_BY_ID_NOTES=delete user by id |
||||||
|
GRANT_PROJECT_NOTES=GRANT PROJECT |
||||||
|
PROJECT_IDS=project ids(string format, multiple projects separated by ",") |
||||||
|
GRANT_RESOURCE_NOTES=grant resource file |
||||||
|
RESOURCE_IDS=resource ids(string format, multiple resources separated by ",") |
||||||
|
GET_USER_INFO_NOTES=get user info |
||||||
|
LIST_USER_NOTES=list user |
||||||
|
VERIFY_USER_NAME_NOTES=verify user name |
||||||
|
UNAUTHORIZED_USER_NOTES=cancel authorization |
||||||
|
ALERT_GROUP_ID=alert group id |
||||||
|
AUTHORIZED_USER_NOTES=authorized user |
||||||
|
GRANT_UDF_FUNC_NOTES=grant udf function |
||||||
|
UDF_IDS=udf ids(string format, multiple udf functions separated by ",") |
||||||
|
GRANT_DATASOURCE_NOTES=GRANT DATASOURCE |
||||||
|
DATASOURCE_IDS=datasource ids(string format, multiple datasources separated by ",") |
||||||
|
QUERY_SUBPROCESS_INSTANCE_BY_TASK_ID_NOTES=query subprocess instance by task instance id |
||||||
|
QUERY_PARENT_PROCESS_INSTANCE_BY_SUB_PROCESS_INSTANCE_ID_NOTES=query parent process instance info by sub process instance id |
||||||
|
QUERY_PROCESS_INSTANCE_GLOBAL_VARIABLES_AND_LOCAL_VARIABLES_NOTES=query process instance global variables and local variables |
||||||
|
VIEW_GANTT_NOTES=view gantt |
||||||
|
SUB_PROCESS_INSTANCE_ID=sub process instance id |
||||||
|
TASK_NAME=task instance name |
||||||
|
TASK_INSTANCE_TAG=task instance related operation |
||||||
|
LOGGER_TAG=log related operation |
||||||
|
PROCESS_INSTANCE_TAG=process instance related operation |
||||||
|
EXECUTION_STATUS=runing status for workflow and task nodes |
||||||
|
HOST=ip address of running task |
||||||
|
START_DATE=start date |
||||||
|
END_DATE=end date |
||||||
|
QUERY_TASK_LIST_BY_PROCESS_INSTANCE_ID_NOTES=query task list by process instance id |
||||||
|
@ -1,5 +1,97 @@ |
|||||||
welcome=您好,欢迎你! |
QUERY_SCHEDULE_LIST_NOTES=查询定时列表 |
||||||
test=测试 |
SCHEDULE=定时 |
||||||
userName=用户名 |
WARNING_TYPE=发送策略 |
||||||
userPassword=用户密码 |
WARNING_GROUP_ID=发送组ID |
||||||
loginNotes=登录xxx |
FAILURE_STRATEGY=失败策略 |
||||||
|
RECEIVERS=收件人 |
||||||
|
RECEIVERS_CC=收件人(抄送) |
||||||
|
WORKER_GROUP_ID=Worker Server分组ID |
||||||
|
PROCESS_INSTANCE_PRIORITY=流程实例优先级 |
||||||
|
UPDATE_SCHEDULE_NOTES=更新定时 |
||||||
|
SCHEDULE_ID=定时ID |
||||||
|
ONLINE_SCHEDULE_NOTES=定时上线 |
||||||
|
OFFLINE_SCHEDULE_NOTES=定时下线 |
||||||
|
QUERY_SCHEDULE_NOTES=查询定时 |
||||||
|
QUERY_SCHEDULE_LIST_PAGING_NOTES=分页查询定时 |
||||||
|
LOGIN_TAG=用户登录相关操作 |
||||||
|
USER_NAME=用户名 |
||||||
|
PROJECT_NAME=项目名称 |
||||||
|
CREATE_PROCESS_DEFINITION_NOTES=创建流程定义 |
||||||
|
PROCESS_DEFINITION_NAME=流程定义名称 |
||||||
|
PROCESS_DEFINITION_JSON=流程定义详细信息(json格式) |
||||||
|
PROCESS_DEFINITION_LOCATIONS=流程定义节点坐标位置信息(json格式) |
||||||
|
PROCESS_INSTANCE_LOCATIONS=流程实例节点坐标位置信息(json格式) |
||||||
|
PROCESS_DEFINITION_CONNECTS=流程定义节点图标连接信息(json格式) |
||||||
|
PROCESS_INSTANCE_CONNECTS=流程实例节点图标连接信息(json格式) |
||||||
|
PROCESS_DEFINITION_DESC=流程定义描述信息 |
||||||
|
PROCESS_DEFINITION_TAG=流程定义相关操作 |
||||||
|
SIGNOUT_NOTES=退出登录 |
||||||
|
USER_PASSWORD=用户密码 |
||||||
|
UPDATE_PROCESS_INSTANCE_NOTES=更新流程实例 |
||||||
|
QUERY_PROCESS_INSTANCE_LIST_NOTES=查询流程实例列表 |
||||||
|
VERIFY_PROCCESS_DEFINITION_NAME_NOTES=验证流程定义名字 |
||||||
|
LOGIN_NOTES=用户登录 |
||||||
|
UPDATE_PROCCESS_DEFINITION_NOTES=更新流程定义 |
||||||
|
PROCESS_DEFINITION_ID=流程定义ID |
||||||
|
RELEASE_PROCCESS_DEFINITION_NOTES=发布流程定义 |
||||||
|
QUERY_PROCCESS_DEFINITION_BY_ID_NOTES=查询流程定义通过流程定义ID |
||||||
|
QUERY_PROCCESS_DEFINITION_LIST_NOTES=查询流程定义列表 |
||||||
|
QUERY_PROCCESS_DEFINITION_LIST_PAGING_NOTES=分页查询流程定义列表 |
||||||
|
PAGE_NO=页码号 |
||||||
|
PROCESS_INSTANCE_ID=流程实例ID |
||||||
|
PROCESS_INSTANCE_JSON=流程实例信息(json格式) |
||||||
|
SCHEDULE_TIME=定时时间 |
||||||
|
SYNC_DEFINE=更新流程实例的信息是否同步到流程定义 |
||||||
|
RECOVERY_PROCESS_INSTANCE_FLAG=是否恢复流程实例 |
||||||
|
SEARCH_VAL=搜索值 |
||||||
|
USER_ID=用户ID |
||||||
|
PAGE_SIZE=页大小 |
||||||
|
LIMIT=显示多少条 |
||||||
|
VIEW_TREE_NOTES=树状图 |
||||||
|
GET_NODE_LIST_BY_DEFINITION_ID_NOTES=获得任务节点列表通过流程定义ID |
||||||
|
PROCESS_DEFINITION_ID_LIST=流程定义id列表 |
||||||
|
QUERY_PROCESS_INSTANCE_BY_ID_NOTES=查询流程实例通过流程实例ID |
||||||
|
DELETE_PROCESS_INSTANCE_BY_ID_NOTES=删除流程实例通过流程实例ID |
||||||
|
TASK_ID=任务实例ID |
||||||
|
SKIP_LINE_NUM=忽略行数 |
||||||
|
QUERY_TASK_INSTANCE_LOG_NOTES=查询任务实例日志 |
||||||
|
DOWNLOAD_TASK_INSTANCE_LOG_NOTES=下载任务实例日志 |
||||||
|
USERS_TAG=用户相关操作 |
||||||
|
SCHEDULER_TAG=定时相关操作 |
||||||
|
CREATE_SCHEDULE_NOTES=创建定时 |
||||||
|
CREATE_USER_NOTES=创建用户 |
||||||
|
TENANT_ID=租户ID |
||||||
|
QUEUE=使用的队列 |
||||||
|
EMAIL=邮箱 |
||||||
|
PHONE=手机号 |
||||||
|
QUERY_USER_LIST_NOTES=查询用户列表 |
||||||
|
UPDATE_USER_NOTES=更新用户 |
||||||
|
DELETE_USER_BY_ID_NOTES=删除用户通过ID |
||||||
|
GRANT_PROJECT_NOTES=授权项目 |
||||||
|
PROJECT_IDS=项目IDS(字符串格式,多个项目以","分割) |
||||||
|
GRANT_RESOURCE_NOTES=授权资源文件 |
||||||
|
RESOURCE_IDS=资源ID列表(字符串格式,多个资源ID以","分割) |
||||||
|
GET_USER_INFO_NOTES=获取用户信息 |
||||||
|
LIST_USER_NOTES=用户列表 |
||||||
|
VERIFY_USER_NAME_NOTES=验证用户名 |
||||||
|
UNAUTHORIZED_USER_NOTES=取消授权 |
||||||
|
ALERT_GROUP_ID=报警组ID |
||||||
|
AUTHORIZED_USER_NOTES=授权用户 |
||||||
|
GRANT_UDF_FUNC_NOTES=授权udf函数 |
||||||
|
UDF_IDS=udf函数id列表(字符串格式,多个udf函数ID以","分割) |
||||||
|
GRANT_DATASOURCE_NOTES=授权数据源 |
||||||
|
DATASOURCE_IDS=数据源ID列表(字符串格式,多个数据源ID以","分割) |
||||||
|
QUERY_SUBPROCESS_INSTANCE_BY_TASK_ID_NOTES=查询子流程实例通过任务实例ID |
||||||
|
QUERY_PARENT_PROCESS_INSTANCE_BY_SUB_PROCESS_INSTANCE_ID_NOTES=查询父流程实例信息通过子流程实例ID |
||||||
|
QUERY_PROCESS_INSTANCE_GLOBAL_VARIABLES_AND_LOCAL_VARIABLES_NOTES=查询流程实例全局变量和局部变量 |
||||||
|
VIEW_GANTT_NOTES=浏览Gantt图 |
||||||
|
SUB_PROCESS_INSTANCE_ID=子流程是咧ID |
||||||
|
TASK_NAME=任务实例名 |
||||||
|
TASK_INSTANCE_TAG=任务实例相关操作 |
||||||
|
LOGGER_TAG=日志相关操作 |
||||||
|
PROCESS_INSTANCE_TAG=流程实例相关操作 |
||||||
|
EXECUTION_STATUS=工作流和任务节点的运行状态 |
||||||
|
HOST=运行任务的主机IP地址 |
||||||
|
START_DATE=开始时间 |
||||||
|
END_DATE=结束时间 |
||||||
|
QUERY_TASK_LIST_BY_PROCESS_INSTANCE_ID_NOTES=通过流程实例ID查询任务列表 |
||||||
|
Before Width: | Height: | Size: 550 B |
Before Width: | Height: | Size: 5.0 KiB |
Before Width: | Height: | Size: 6.2 KiB |
Before Width: | Height: | Size: 4.1 KiB |
Before Width: | Height: | Size: 4.2 KiB |
Before Width: | Height: | Size: 434 KiB |
Before Width: | Height: | Size: 123 KiB |
Before Width: | Height: | Size: 7.8 KiB |
Before Width: | Height: | Size: 5.8 KiB |
Before Width: | Height: | Size: 3.5 KiB |
Before Width: | Height: | Size: 586 B |
Before Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 3.6 KiB |
Before Width: | Height: | Size: 3.4 KiB |
Before Width: | Height: | Size: 3.5 KiB |
Before Width: | Height: | Size: 3.2 KiB |
Before Width: | Height: | Size: 4.1 KiB |
Before Width: | Height: | Size: 2.9 KiB |
Before Width: | Height: | Size: 2.3 KiB |
@ -1,7 +0,0 @@ |
|||||||
<!doctype html><html><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"><meta http-equiv="Cache-Control" content="no-siteapp"><meta name="apple-mobile-web-app-capable" content="yes"><meta name="apple-mobile-web-app-title" content="标题"><meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"><meta name="format-detection" content="telphone=no, email=no"><meta name="screen-orientation" content="portrait"><meta name="x5-orientation" content="portrait"><meta name="theme-color" content="#4a8dee"><meta name="msapplication-navbutton-color" content="#4a8dee"><meta name="viewport" content="width=device-width,user-scalable=no,initial-scale=1,maximum-scale=1,minimum-scale=1"><link rel="shortcut icon" href="/images/favicon.ico"><link href="/combo/1.0.0/base.css?v1.0.0.1" rel="stylesheet"><link href="/combo/1.0.0/3rd.css?v1.0.0.1" rel="stylesheet"><!--[if lt IE 9]> |
|
||||||
<script src="/combo/1.0.0/es5.js"></script> |
|
||||||
<![endif]--><script>let NODE_ENV = 'true'</script><title>EasyScheduler</title><link href="/css/common.8ba9af7.css" rel="stylesheet"><link href="/css/home/index.b444b91.css" rel="stylesheet"></head><body><div id="app"></div><div id="contextmenu" class="contextmenu"></div><div class="global-loading"><div class="svg-box"><svg class="lds-gears" width="54px" height="54px" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid" style="background: none;"><g transform="translate(50 50)"><g transform="translate(-19 -19) scale(0.6)"><g transform="rotate(107.866)"><animateTransform attributeName="transform" type="rotate" values="0;360" keyTimes="0;1" dur="1s" begin="0s" repeatCount="indefinite"></animateTransform><path |
|
||||||
d="M37.3496987939662 -7 L47.3496987939662 -7 L47.3496987939662 7 L37.3496987939662 7 A38 38 0 0 1 31.359972760794346 21.46047782418268 L31.359972760794346 21.46047782418268 L38.431040572659825 28.531545636048154 L28.531545636048154 38.431040572659825 L21.46047782418268 31.359972760794346 A38 38 0 0 1 7.0000000000000036 37.3496987939662 L7.0000000000000036 37.3496987939662 L7.000000000000004 47.3496987939662 L-6.999999999999999 47.3496987939662 L-7 37.3496987939662 A38 38 0 0 1 -21.46047782418268 31.35997276079435 L-21.46047782418268 31.35997276079435 L-28.531545636048154 38.431040572659825 L-38.43104057265982 28.531545636048158 L-31.359972760794346 21.460477824182682 A38 38 0 0 1 -37.3496987939662 7.000000000000007 L-37.3496987939662 7.000000000000007 L-47.3496987939662 7.000000000000008 L-47.3496987939662 -6.9999999999999964 L-37.3496987939662 -6.999999999999997 A38 38 0 0 1 -31.35997276079435 -21.460477824182675 L-31.35997276079435 -21.460477824182675 L-38.431040572659825 -28.531545636048147 L-28.53154563604818 -38.4310405726598 L-21.4604778241827 -31.35997276079433 A38 38 0 0 1 -6.999999999999992 -37.3496987939662 L-6.999999999999992 -37.3496987939662 L-6.999999999999994 -47.3496987939662 L6.999999999999977 -47.3496987939662 L6.999999999999979 -37.3496987939662 A38 38 0 0 1 21.460477824182686 -31.359972760794342 L21.460477824182686 -31.359972760794342 L28.531545636048158 -38.43104057265982 L38.4310405726598 -28.53154563604818 L31.35997276079433 -21.4604778241827 A38 38 0 0 1 37.3496987939662 -6.999999999999995 M0 -23A23 23 0 1 0 0 23 A23 23 0 1 0 0 -23" |
|
||||||
fill="#0097e0"></path></g></g><g transform="translate(19 19) scale(0.6)"><g transform="rotate(229.634)"><animateTransform attributeName="transform" type="rotate" values="360;0" keyTimes="0;1" dur="1s" begin="-0.0625s" repeatCount="indefinite"></animateTransform><path |
|
||||||
d="M37.3496987939662 -7 L47.3496987939662 -7 L47.3496987939662 7 L37.3496987939662 7 A38 38 0 0 1 31.359972760794346 21.46047782418268 L31.359972760794346 21.46047782418268 L38.431040572659825 28.531545636048154 L28.531545636048154 38.431040572659825 L21.46047782418268 31.359972760794346 A38 38 0 0 1 7.0000000000000036 37.3496987939662 L7.0000000000000036 37.3496987939662 L7.000000000000004 47.3496987939662 L-6.999999999999999 47.3496987939662 L-7 37.3496987939662 A38 38 0 0 1 -21.46047782418268 31.35997276079435 L-21.46047782418268 31.35997276079435 L-28.531545636048154 38.431040572659825 L-38.43104057265982 28.531545636048158 L-31.359972760794346 21.460477824182682 A38 38 0 0 1 -37.3496987939662 7.000000000000007 L-37.3496987939662 7.000000000000007 L-47.3496987939662 7.000000000000008 L-47.3496987939662 -6.9999999999999964 L-37.3496987939662 -6.999999999999997 A38 38 0 0 1 -31.35997276079435 -21.460477824182675 L-31.35997276079435 -21.460477824182675 L-38.431040572659825 -28.531545636048147 L-28.53154563604818 -38.4310405726598 L-21.4604778241827 -31.35997276079433 A38 38 0 0 1 -6.999999999999992 -37.3496987939662 L-6.999999999999992 -37.3496987939662 L-6.999999999999994 -47.3496987939662 L6.999999999999977 -47.3496987939662 L6.999999999999979 -37.3496987939662 A38 38 0 0 1 21.460477824182686 -31.359972760794342 L21.460477824182686 -31.359972760794342 L28.531545636048158 -38.43104057265982 L38.4310405726598 -28.53154563604818 L31.35997276079433 -21.4604778241827 A38 38 0 0 1 37.3496987939662 -6.999999999999995 M0 -23A23 23 0 1 0 0 23 A23 23 0 1 0 0 -23" |
|
||||||
fill="#7f8b95"></path></g></g></g></svg> <span class="sp1">Loading ...</span></div></div><script src="/combo/1.0.0/3rd.js?v1.0.0.1"></script><script src="/js/common.6428411.js"></script><script src="/js/home/index.1b09c2f.js"></script></body></html> |
|