Commit 5d01d643 by inrgihc

支持requestBody入参

parent 2852b697
...@@ -69,19 +69,13 @@ ...@@ -69,19 +69,13 @@
- (1) 接口响应出参列表及说明文档 - (1) 接口响应出参列表及说明文档
> 通过人工配制或接口调试,以构建接口出参及其说明文档。 > 通过人工配制或接口调试,以构建接口出参及其说明文档。
- (2) 支持更广泛的入参格式(如JSON入参/文件入参等) - (2) 支持接口的缓存配置功能
> 支持POST/PUT等JSON格式的接口入参。
- (3) 支持接口的缓存配置功能
> 基于分布式缓存等构建支持接口的缓存配置功能。 > 基于分布式缓存等构建支持接口的缓存配置功能。
- (4) SQL结果集的出参格式转换 - (3) SQL结果集的出参格式转换
> SQL查询结果集到接口出参相应的格式转换。 > SQL查询结果集到接口出参相应的格式转换。
- (5) 接口入参的校验 - (4) 前端界面中拓扑结构美化
> 支持接口入参的类型、是否可空等的校验功能。
- (6) 前端界面中拓扑结构美化
> 美化签到界面的拓扑结构图的展示。 > 美化签到界面的拓扑结构图的展示。
## 二、编译打包 ## 二、编译打包
......
...@@ -50,6 +50,13 @@ eureka: ...@@ -50,6 +50,13 @@ eureka:
service-url: service-url:
defaultZone: http://${MANAGER_HOST}:${MANAGER_PORT}/eureka/ defaultZone: http://${MANAGER_HOST}:${MANAGER_PORT}/eureka/
springfox:
documentation:
enabled: true
auto-startup: false
swagger-ui:
enabled: false
datasource: datasource:
driver: driver:
base-path: ${APP_DRIVERS_PATH} base-path: ${APP_DRIVERS_PATH}
...@@ -22,6 +22,12 @@ public abstract class Constants { ...@@ -22,6 +22,12 @@ public abstract class Constants {
public static final String SYS_PARAM_KEY_API_DOC_OPEN = "apiDocOpen"; public static final String SYS_PARAM_KEY_API_DOC_OPEN = "apiDocOpen";
public static final String SYS_PARAM_KEY_SWAGGER_INFO_TITLE = "apiDocInfoTitle";
public static final String SYS_PARAM_KEY_SWAGGER_INFO_VERSION = "apiDocInfoVersion";
public static final String SYS_PARAM_KEY_SWAGGER_INFO_DESCRIPTION = "apiDocInfoDescription";
public static final String getResourceName(String method, String path) { public static final String getResourceName(String method, String path) {
return String.format("/%s/%s[%s]", Constants.API_PATH_PREFIX, path, method); return String.format("/%s/%s[%s]", Constants.API_PATH_PREFIX, path, method);
} }
......
package com.gitee.sqlrest.common.enums; package com.gitee.sqlrest.common.enums;
public enum HttpMethodEnum { public enum HttpMethodEnum {
GET, HEAD, PUT, POST, DELETE, GET(false), HEAD(false), PUT(true), POST(true), DELETE(true),
; ;
private boolean hasBody;
HttpMethodEnum(boolean hasBody) {
this.hasBody = hasBody;
}
public boolean isHasBody() {
return hasBody;
}
public static boolean exists(String method) { public static boolean exists(String method) {
for (HttpMethodEnum methodEnum : values()) { for (HttpMethodEnum methodEnum : values()) {
if (methodEnum.name().equals(method)) { if (methodEnum.name().equals(method)) {
......
package com.gitee.sqlrest.common.enums; package com.gitee.sqlrest.common.enums;
public enum ParamLocationEnum { public enum ParamLocationEnum {
REQUEST_HEADER("请求头"), REQUEST_HEADER("header", "请求头"),
REQUEST_BODY("请求体"), REQUEST_FORM("query", "表单数据"),
FORM_DATA("表单数据"), REQUEST_BODY("body", "请求体"),
; ;
private String name; private String name;
private String in;
ParamLocationEnum(String name) { ParamLocationEnum(String in, String name) {
this.name = name; this.name = name;
this.in = in;
} }
public String getName() { public String getName() {
return name; return name;
} }
public String getIn() {
return in;
}
public boolean isParameter() {
return this == REQUEST_FORM || this == REQUEST_HEADER;
}
public boolean isHeader() {
return this == REQUEST_HEADER;
}
public boolean isRequestBody() {
return this == REQUEST_BODY;
}
} }
package com.gitee.sqlrest.core.dto;
import io.swagger.annotations.ApiModel;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.TreeSet;
/**
* Swagger接口信息
*/
@ApiModel("Swagger的数据接口")
public class SwaggerEntity {
private String swagger = "2.0";
private String host;
private String basePath;
private Info info;
private List<String> schemes=new ArrayList<>();
private final Map<String, Object> securityDefinitions = new HashMap<>();
private final List<Map<String, Object>> security = new ArrayList<>();
private final Set<Tag> tags = new TreeSet<>(Comparator.comparing(Tag::getName));
private final Map<String, Object> definitions = new HashMap<>();
private final Map<String, Map<String, Path>> paths = new HashMap<>();
private static Map<String, Object> doProcessSchema(Object target) {
Map<String, Object> result = new HashMap<>(3);
result.put("type", getType(target));
if (target instanceof List) {
List<?> targetList = (List<?>) target;
if (targetList.size() > 0) {
result.put("items", doProcessSchema(targetList.get(0)));
} else {
result.put("items", Collections.emptyList());
}
} else if (target instanceof Map) {
Set<Map.Entry> entries = ((Map) target).entrySet();
Map<String, Map<String, Object>> properties = new HashMap<>(entries.size());
for (Map.Entry entry : entries) {
properties.put(Objects.toString(entry.getKey()), doProcessSchema(entry.getValue()));
}
result.put("properties", properties);
} else {
result.put("example", target == null ? "" : target);
result.put("description", target == null ? "" : target);
}
return result;
}
private static String getType(Object object) {
if (object instanceof Number) {
return "number";
}
if (object instanceof String) {
return "string";
}
if (object instanceof Boolean) {
return "boolean";
}
if (object instanceof List) {
return "array";
}
if (object instanceof Map) {
return "object";
}
return "string";
}
public static Map<String, Object> createParameter(boolean required, String name, String in, String type,
String description, Object example) {
Map<String, Object> parameter = new HashMap<>();
parameter.put("required", required);
parameter.put("name", name);
parameter.put("in", in);
parameter.put("description", description);
if ("body".equalsIgnoreCase(in)) {
Map<String, Object> schema = new HashMap<>();
schema.put("type", type);
schema.put("example", example);
parameter.put("schema", schema);
} else {
parameter.put("x-example", example);
parameter.put("type", type);
}
return parameter;
}
public Info getInfo() {
return info;
}
public void setInfo(Info info) {
this.info = info;
}
public void addPath(String path, String method, Path pathInfo) {
Map<String, Path> map = paths.computeIfAbsent(path, k -> new HashMap<>());
map.put(method.toLowerCase(), pathInfo);
}
public void addTag(String name, String description) {
this.tags.add(new Tag(name, description));
}
public List<String> getSchemes() {
return schemes;
}
public void setSchemes(List<String> schemes) {
this.schemes = schemes;
}
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
public String getSwagger() {
return swagger;
}
public void setSwagger(String swagger) {
this.swagger = swagger;
}
public String getBasePath() {
return basePath;
}
public void setBasePath(String basePath) {
this.basePath = basePath;
}
public Map<String, Object> getDefinitions() {
return definitions;
}
public void addDefinitions(String path, Object definition) {
definitions.put(path, definition);
}
public Set<Tag> getTags() {
return tags;
}
public Map<String, Map<String, Path>> getPaths() {
return paths;
}
public Map<String, Object> getSecurityDefinitions() {
return securityDefinitions;
}
public List<Map<String, Object>> getSecurity() {
return security;
}
public void addSecurityDefinitions(Map<String, Object> map) {
securityDefinitions.putAll(map);
}
public void addSecurity(Map<String, Object> map) {
security.add(map);
}
public static class Concat {
private String name;
private String url;
private String email;
public Concat() {
}
public Concat(String name, String url, String email) {
this.name = name;
this.url = url;
this.email = email;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
public static class Info {
private String description;
private String version;
private String title;
private License license;
private Concat concat;
public Info(String description, String version, String title, License license, Concat concat) {
this.description = description;
this.version = version;
this.title = title;
this.license = license;
this.concat = concat;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public License getLicense() {
return license;
}
public void setLicense(License license) {
this.license = license;
}
public Concat getConcat() {
return concat;
}
public void setConcat(Concat concat) {
this.concat = concat;
}
}
public static class Path {
private List<String> tags = new ArrayList<>();
private String summary;
private String description;
private final String operationId;
private Map<String,Object> requestBody= new HashMap<>();
private List<String> produces = new ArrayList<>();
private List<String> consumes = new ArrayList<>();
private List<Map<String, Object>> parameters = new ArrayList<>();
private Map<String, Object> responses = new HashMap<>();
public Path(String operationId) {
this.operationId = operationId;
}
public void setRequestBody(String description, String acceptType, Map<String, Object> schema) {
Map<String, Object> content = new HashMap<>();
content.put(acceptType, schema);
requestBody.put("description", description);
requestBody.put("content", content);
}
public void addProduce(String produce) {
this.produces.add(produce);
}
public void addConsume(String consume) {
this.consumes.add(consume);
}
public void addParameter(Map<String, Object> parameter) {
this.parameters.add(parameter);
}
public String getOperationId() {
return operationId;
}
public void addResponse(String status, Object object) {
Map<String, Object> response = new HashMap<>();
response.put("description", "OK");
response.put("schema", doProcessSchema(object));
response.put("example", object);
this.responses.put(status, response);
}
public void addResponse(String status, String description, Map schema) {
Map<String, Object> response = new HashMap<>();
response.put("description", description);
response.put("schema", schema);
this.responses.put(status, response);
}
public List<String> getTags() {
return tags;
}
public void setTags(List<String> tags) {
this.tags = tags;
}
public void addTag(String tag) {
this.tags.add(tag);
}
public String getSummary() {
return summary;
}
public void setSummary(String summary) {
this.summary = summary;
}
public List<String> getProduces() {
return produces;
}
public void setProduces(List<String> produces) {
this.produces = produces;
}
public List<String> getConsumes() {
return consumes;
}
public void setConsumes(List<String> consumes) {
this.consumes = consumes;
}
public List<Map<String, Object>> getParameters() {
return parameters;
}
public void setParameters(List<Map<String, Object>> parameters) {
this.parameters = parameters;
}
public Map<String, Object> getResponses() {
return responses;
}
public void setResponses(Map<String, Object> responses) {
this.responses = responses;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
public static class Parameter {
private String name;
private String in;
private boolean required = false;
private String type;
private Object schema;
private String description;
private Object example;
public Parameter(boolean required, String name, String in, String type, String description, Object example) {
this.name = name;
this.in = in;
this.type = type;
this.description = description;
this.required = required;
if ("body".equalsIgnoreCase(in)) {
this.schema = "";
} else {
this.example = example;
/*
* fix swagger文档使用knife4j时无法显示接口详情的问题(query类型参数)
* schema 需设置为空字符串,否则请求参数中数据类型字段显示不正确
*/
this.schema = "";
}
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getIn() {
return in;
}
public void setIn(String in) {
this.in = in;
}
public boolean isRequired() {
return required;
}
public void setRequired(boolean required) {
this.required = required;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public Object getSchema() {
return schema;
}
public void setSchema(Object schema) {
this.schema = schema;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Object getExample() {
return example;
}
public void setExample(Object example) {
this.example = example;
}
}
public static class Tag {
private String name;
private String description;
public Tag(String name, String description) {
this.name = name;
this.description = description;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Tag tag = (Tag) o;
return Objects.equals(name, tag.name);
}
@Override
public int hashCode() {
return Objects.hash(name);
}
}
public static class License {
private String name;
private String url;
public License(String name, String url) {
this.name = name;
this.url = url;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
}
public static class BasicAuth {
public final static String KEY_NAME = "BasicAuth";
/**
* 类型,默认值
*/
private String type = "basic";
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
public static class BearerAuth {
public final static String KEY_NAME = "Bearer";
private String type = "apiKey";
private String name = "Authorization";
private String in = "header";
private String description ="用户登录后获取的Token(头部需加上Bearer )";
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getIn() {
return in;
}
public void setIn(String in) {
this.in = in;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
public static class OAuth2 {
public final static String KEY_NAME = "OAuth2";
private String type = "oauth2";
private String flow;
private String authorizationUrl;
private String tokenUrl;
private Map<String, String> scopes;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getFlow() {
return flow;
}
public void setFlow(String flow) {
this.flow = flow;
}
public String getAuthorizationUrl() {
return authorizationUrl;
}
public void setAuthorizationUrl(String authorizationUrl) {
this.authorizationUrl = authorizationUrl;
}
public String getTokenUrl() {
return tokenUrl;
}
public void setTokenUrl(String tokenUrl) {
this.tokenUrl = tokenUrl;
}
public Map<String, String> getScopes() {
return scopes;
}
public void setScopes(Map<String, String> scopes) {
this.scopes = scopes;
}
}
}
...@@ -19,6 +19,7 @@ import com.google.common.base.Charsets; ...@@ -19,6 +19,7 @@ import com.google.common.base.Charsets;
import com.zaxxer.hikari.HikariDataSource; import com.zaxxer.hikari.HikariDataSource;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
...@@ -26,11 +27,11 @@ import java.util.Iterator; ...@@ -26,11 +27,11 @@ import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.Optional;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import javax.annotation.Resource; import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
...@@ -51,7 +52,11 @@ public class ApiExecuteService { ...@@ -51,7 +52,11 @@ public class ApiExecuteService {
log.warn("Error for handle api[id={}],information:{}", config.getId(), message); log.warn("Error for handle api[id={}],information:{}", config.getId(), message);
return ResultEntity.failed(ResponseErrorCode.ERROR_RESOURCE_NOT_EXISTS, message); return ResultEntity.failed(ResponseErrorCode.ERROR_RESOURCE_NOT_EXISTS, message);
} }
Map<String, Object> paramValues = obtainParameterValues(request, config.getParams()); List<ItemParam> invalidArgs = new ArrayList<>();
Map<String, Object> paramValues = obtainParameterValues(request, config.getParams(), invalidArgs);
if (invalidArgs.size() > 0) {
return ResultEntity.failed(ResponseErrorCode.ERROR_INVALID_ARGUMENT, convertInvalidArgs(invalidArgs));
}
File driverPath = driverLoadService.getVersionDriverFile(dsEntity.getType(), dsEntity.getVersion()); File driverPath = driverLoadService.getVersionDriverFile(dsEntity.getType(), dsEntity.getVersion());
HikariDataSource dataSource = DataSourceUtils.getHikariDataSource(dsEntity, driverPath.getAbsolutePath()); HikariDataSource dataSource = DataSourceUtils.getHikariDataSource(dsEntity, driverPath.getAbsolutePath());
Object result = ApiExecutorEngineFactory Object result = ApiExecutorEngineFactory
...@@ -63,22 +68,60 @@ public class ApiExecuteService { ...@@ -63,22 +68,60 @@ public class ApiExecuteService {
} }
} }
private Map<String, Object> obtainParameterValues(HttpServletRequest request, List<ItemParam> params) { private String convertInvalidArgs(List<ItemParam> invalidArgs) {
return "无效参数," + invalidArgs.stream().map(
p -> p.getIsArray() ? "数组" : "" + "参数'" + p.getName() + "'位于" + p.getLocation().getIn()
).collect(Collectors.joining(";"));
}
private Map<String, Object> obtainParameterValues(HttpServletRequest request, List<ItemParam> params,
List<ItemParam> invalidArgs) {
if (CollectionUtils.isEmpty(params)) {
return Collections.emptyMap();
}
Map<String, Object> map = new HashMap<>(); Map<String, Object> map = new HashMap<>();
if (null != params && params.size() > 0) {
Map<String, Object> bodyMap = getRequestBodyMap(request); Map<String, Object> bodyMap = getRequestBodyMap(request);
for (ItemParam param : params) { for (ItemParam param : params) {
String name = param.getName(); String name = param.getName();
ParamTypeEnum type = param.getType(); ParamTypeEnum type = param.getType();
ParamLocationEnum location = param.getLocation(); ParamLocationEnum location = param.getLocation();
Boolean isArray = param.getIsArray();
Boolean required = param.getRequired();
String defaultValue = param.getDefaultValue(); String defaultValue = param.getDefaultValue();
if (location == ParamLocationEnum.REQUEST_HEADER) { if (location == ParamLocationEnum.REQUEST_HEADER) {
map.put(name, request.getHeader(name)); List<Object> hv = Collections.list(request.getHeaders(name))
.stream().map(v -> type.getConverter().apply(v))
.collect(Collectors.toList());
if (isArray) {
if (CollectionUtils.isEmpty(hv)) {
invalidArgs.add(param);
} else {
map.put(name, hv);
}
} else {
if (CollectionUtils.isEmpty(hv)) {
if (required) {
invalidArgs.add(param);
} else {
map.put(name, type.getConverter().apply(defaultValue));
}
} else {
map.put(name, hv.get(0));
}
}
} else if (location == ParamLocationEnum.REQUEST_BODY) { } else if (location == ParamLocationEnum.REQUEST_BODY) {
map.put(name, bodyMap.get(name)); Object paramValue = bodyMap.get(name);
if (null == paramValue) {
if (required) {
invalidArgs.add(param);
} else {
map.put(name, type.getConverter().apply(defaultValue));
}
} else {
map.put(name, paramValue);
}
} else { } else {
boolean isArray = Optional.ofNullable(param.getIsArray()).orElse(false);
Boolean required = Optional.ofNullable(param.getRequired()).orElse(false);
if (isArray) { if (isArray) {
String[] values = request.getParameterValues(name); String[] values = request.getParameterValues(name);
if (null != values && values.length > 0) { if (null != values && values.length > 0) {
...@@ -87,13 +130,17 @@ public class ApiExecuteService { ...@@ -87,13 +130,17 @@ public class ApiExecuteService {
.collect(Collectors.toList()); .collect(Collectors.toList());
map.put(name, list); map.put(name, list);
} else { } else {
map.put(name, null); invalidArgs.add(param);
} }
} else { } else {
String value = request.getParameter(name); String value = request.getParameter(name);
if (!required && null == value) { if (null == value) {
value = defaultValue; if (required) {
invalidArgs.add(param);
} else {
map.put(name, type.getConverter().apply(defaultValue));
} }
} else {
map.put(name, type.getConverter().apply(value)); map.put(name, type.getConverter().apply(value));
} }
} }
...@@ -103,6 +150,10 @@ public class ApiExecuteService { ...@@ -103,6 +150,10 @@ public class ApiExecuteService {
} }
public Map<String, Object> getRequestBodyMap(HttpServletRequest request) { public Map<String, Object> getRequestBodyMap(HttpServletRequest request) {
if (null == request.getContentType() || !request.getContentType().contains("application/json")) {
return Collections.emptyMap();
}
ObjectMapper mapper = new ObjectMapper(); ObjectMapper mapper = new ObjectMapper();
try { try {
String jsonString = IoUtil.read(request.getInputStream(), Charsets.UTF_8); String jsonString = IoUtil.read(request.getInputStream(), Charsets.UTF_8);
......
...@@ -8,6 +8,7 @@ import com.gitee.sqlrest.common.dto.ResultEntity; ...@@ -8,6 +8,7 @@ import com.gitee.sqlrest.common.dto.ResultEntity;
import com.gitee.sqlrest.common.enums.DataTypeFormatEnum; import com.gitee.sqlrest.common.enums.DataTypeFormatEnum;
import com.gitee.sqlrest.common.enums.NamingStrategyEnum; import com.gitee.sqlrest.common.enums.NamingStrategyEnum;
import com.gitee.sqlrest.common.enums.OnOffEnum; import com.gitee.sqlrest.common.enums.OnOffEnum;
import com.gitee.sqlrest.common.enums.ParamLocationEnum;
import com.gitee.sqlrest.common.enums.ParamTypeEnum; import com.gitee.sqlrest.common.enums.ParamTypeEnum;
import com.gitee.sqlrest.common.exception.CommonException; import com.gitee.sqlrest.common.exception.CommonException;
import com.gitee.sqlrest.common.exception.ResponseErrorCode; import com.gitee.sqlrest.common.exception.ResponseErrorCode;
...@@ -18,7 +19,6 @@ import com.gitee.sqlrest.core.dto.ApiAssignmentSaveRequest; ...@@ -18,7 +19,6 @@ import com.gitee.sqlrest.core.dto.ApiAssignmentSaveRequest;
import com.gitee.sqlrest.core.dto.ApiDebugExecuteRequest; import com.gitee.sqlrest.core.dto.ApiDebugExecuteRequest;
import com.gitee.sqlrest.core.dto.AssignmentSearchRequest; import com.gitee.sqlrest.core.dto.AssignmentSearchRequest;
import com.gitee.sqlrest.core.dto.DataTypeFormatMapValue; import com.gitee.sqlrest.core.dto.DataTypeFormatMapValue;
import com.gitee.sqlrest.core.dto.NameValueRemarkResponse;
import com.gitee.sqlrest.core.dto.ScriptEditorCompletion; import com.gitee.sqlrest.core.dto.ScriptEditorCompletion;
import com.gitee.sqlrest.core.dto.SqlParamParseResponse; import com.gitee.sqlrest.core.dto.SqlParamParseResponse;
import com.gitee.sqlrest.core.exec.ApiExecuteService; import com.gitee.sqlrest.core.exec.ApiExecuteService;
...@@ -42,8 +42,6 @@ import java.io.File; ...@@ -42,8 +42,6 @@ import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
...@@ -56,7 +54,6 @@ import javax.annotation.Resource; ...@@ -56,7 +54,6 @@ import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
...@@ -140,7 +137,7 @@ public class ApiAssignmentService { ...@@ -140,7 +137,7 @@ public class ApiAssignmentService {
String paramName = entry.getKey(); String paramName = entry.getKey();
List<ParamValue> value = entry.getValue(); List<ParamValue> value = entry.getValue();
ParamTypeEnum type = value.get(0).getType(); ParamTypeEnum type = value.get(0).getType();
if (value.size() > 1) { if (value.get(0).getIsArray()) {
List<Object> values = value.stream().map(ParamValue::getValue) List<Object> values = value.stream().map(ParamValue::getValue)
.map(s -> type.getConverter().apply(s)) .map(s -> type.getConverter().apply(s))
.collect(Collectors.toList()); .collect(Collectors.toList());
...@@ -183,6 +180,28 @@ public class ApiAssignmentService { ...@@ -183,6 +180,28 @@ public class ApiAssignmentService {
String message = String.format("path=[%s]%s", request.getMethod().name(), request.getPath()); String message = String.format("path=[%s]%s", request.getMethod().name(), request.getPath());
throw new CommonException(ResponseErrorCode.ERROR_RESOURCE_ALREADY_EXISTS, message); throw new CommonException(ResponseErrorCode.ERROR_RESOURCE_ALREADY_EXISTS, message);
} }
if (!CollectionUtils.isEmpty(request.getParams())) {
if (!request.getMethod().isHasBody()) {
if (request.getParams().stream().anyMatch(i -> ParamLocationEnum.REQUEST_BODY == i.getLocation())) {
throw new CommonException(ResponseErrorCode.ERROR_INVALID_ARGUMENT,
"Request with GET/HEAD method cannot have body.");
}
} else {
if ("application/json".equals(request.getContentType())) {
if (request.getParams().stream().filter(i -> ParamLocationEnum.REQUEST_HEADER != i.getLocation())
.anyMatch(i -> ParamLocationEnum.REQUEST_BODY != i.getLocation())) {
throw new CommonException(ResponseErrorCode.ERROR_INVALID_ARGUMENT,
"Request with 'application/json' content-type must use body parameter.");
}
} else {
if (request.getParams().stream().filter(i -> ParamLocationEnum.REQUEST_HEADER != i.getLocation())
.anyMatch(i -> ParamLocationEnum.REQUEST_FORM != i.getLocation())) {
throw new CommonException(ResponseErrorCode.ERROR_INVALID_ARGUMENT,
"Request with '" + request.getContentType() + "' content-type must use form parameter.");
}
}
}
}
if (CollectionUtils.isEmpty(request.getContextList())) { if (CollectionUtils.isEmpty(request.getContextList())) {
throw new CommonException(ResponseErrorCode.ERROR_INVALID_ARGUMENT, "contextList"); throw new CommonException(ResponseErrorCode.ERROR_INVALID_ARGUMENT, "contextList");
} }
...@@ -222,6 +241,12 @@ public class ApiAssignmentService { ...@@ -222,6 +241,12 @@ public class ApiAssignmentService {
if (null == exists) { if (null == exists) {
throw new CommonException(ResponseErrorCode.ERROR_RESOURCE_NOT_EXISTS, "id=" + request.getId()); throw new CommonException(ResponseErrorCode.ERROR_RESOURCE_NOT_EXISTS, "id=" + request.getId());
} }
if (exists.getMethod() != request.getMethod()) {
throw new CommonException(ResponseErrorCode.ERROR_INVALID_ARGUMENT, "can't update method");
}
if (!StringUtils.equals(exists.getPath(), request.getPath())) {
throw new CommonException(ResponseErrorCode.ERROR_INVALID_ARGUMENT, "can't update path");
}
if (exists.getStatus()) { if (exists.getStatus()) {
throw new CommonException(ResponseErrorCode.ERROR_EDIT_ALREADY_PUBLISHED, "id=" + request.getId()); throw new CommonException(ResponseErrorCode.ERROR_EDIT_ALREADY_PUBLISHED, "id=" + request.getId());
} }
...@@ -231,7 +256,28 @@ public class ApiAssignmentService { ...@@ -231,7 +256,28 @@ public class ApiAssignmentService {
if (CollectionUtils.isEmpty(request.getContextList())) { if (CollectionUtils.isEmpty(request.getContextList())) {
throw new CommonException(ResponseErrorCode.ERROR_INVALID_ARGUMENT, "sqlTextList"); throw new CommonException(ResponseErrorCode.ERROR_INVALID_ARGUMENT, "sqlTextList");
} }
if (!CollectionUtils.isEmpty(request.getParams())) {
if (!request.getMethod().isHasBody()) {
if (request.getParams().stream().anyMatch(i -> ParamLocationEnum.REQUEST_BODY == i.getLocation())) {
throw new CommonException(ResponseErrorCode.ERROR_INVALID_ARGUMENT,
"Request with GET/HEAD method cannot have body.");
}
} else {
if ("application/json".equals(request.getContentType())) {
if (request.getParams().stream().filter(i -> ParamLocationEnum.REQUEST_HEADER != i.getLocation())
.anyMatch(i -> ParamLocationEnum.REQUEST_BODY != i.getLocation())) {
throw new CommonException(ResponseErrorCode.ERROR_INVALID_ARGUMENT,
"Request with 'application/json' content-type must use body parameter.");
}
} else {
if (request.getParams().stream().filter(i -> ParamLocationEnum.REQUEST_HEADER != i.getLocation())
.anyMatch(i -> ParamLocationEnum.REQUEST_FORM != i.getLocation())) {
throw new CommonException(ResponseErrorCode.ERROR_INVALID_ARGUMENT,
"Request with '" + request.getContentType() + "' content-type must use form parameter.");
}
}
}
}
if (null == request.getNamingStrategy()) { if (null == request.getNamingStrategy()) {
request.setNamingStrategy(NamingStrategyEnum.CAMEL_CASE); request.setNamingStrategy(NamingStrategyEnum.CAMEL_CASE);
} }
......
...@@ -2,136 +2,306 @@ package com.gitee.sqlrest.core.servlet; ...@@ -2,136 +2,306 @@ package com.gitee.sqlrest.core.servlet;
import com.gitee.sqlrest.common.consts.Constants; import com.gitee.sqlrest.common.consts.Constants;
import com.gitee.sqlrest.common.dto.ItemParam; import com.gitee.sqlrest.common.dto.ItemParam;
import com.gitee.sqlrest.common.dto.ResultEntity; import com.gitee.sqlrest.common.enums.HttpMethodEnum;
import com.gitee.sqlrest.common.exception.ResponseErrorCode; import com.gitee.sqlrest.common.enums.ParamTypeEnum;
import com.gitee.sqlrest.core.dto.SwaggerEntity;
import com.gitee.sqlrest.persistence.dao.ApiAssignmentDao; import com.gitee.sqlrest.persistence.dao.ApiAssignmentDao;
import com.gitee.sqlrest.persistence.dao.ApiModuleDao; import com.gitee.sqlrest.persistence.dao.ApiModuleDao;
import com.gitee.sqlrest.persistence.dao.SystemParamDao;
import com.gitee.sqlrest.persistence.entity.ApiAssignmentEntity; import com.gitee.sqlrest.persistence.entity.ApiAssignmentEntity;
import com.gitee.sqlrest.persistence.entity.ApiModuleEntity; import com.gitee.sqlrest.persistence.entity.ApiModuleEntity;
import com.google.common.collect.Lists; import com.gitee.sqlrest.persistence.entity.SystemParamEntity;
import com.hazelcast.com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.Operation;
import io.swagger.v3.oas.models.PathItem;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.media.ArraySchema;
import io.swagger.v3.oas.models.media.Content;
import io.swagger.v3.oas.models.media.MediaType;
import io.swagger.v3.oas.models.media.NumberSchema;
import io.swagger.v3.oas.models.media.ObjectSchema;
import io.swagger.v3.oas.models.media.Schema;
import io.swagger.v3.oas.models.media.StringSchema;
import io.swagger.v3.oas.models.parameters.HeaderParameter;
import io.swagger.v3.oas.models.parameters.Parameter;
import io.swagger.v3.oas.models.parameters.QueryParameter;
import io.swagger.v3.oas.models.parameters.RequestBody;
import io.swagger.v3.oas.models.responses.ApiResponse;
import io.swagger.v3.oas.models.responses.ApiResponses;
import io.swagger.v3.oas.models.security.SecurityRequirement;
import io.swagger.v3.oas.models.security.SecurityScheme;
import io.swagger.v3.oas.models.security.SecurityScheme.Type;
import io.swagger.v3.oas.models.tags.Tag;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import javax.annotation.Resource; import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.http.MediaType; import org.apache.commons.lang3.tuple.Triple;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
/** /**
* https://juejin.cn/post/6996227860058865700 * 文档格式说明:https://openapi.apifox.cn/
* <p>
* SwaggerEditor: https://editor.swagger.io/
* </p>
*/ */
@Slf4j
@Service @Service
public class ApiSwaggerService { public class ApiSwaggerService {
private static final String INFO_TITLE = "SQLREST在线接口文档"; private static final String INFO_TITLE = "SQLREST在线接口文档";
private static final String INFO_VERSION = "1.0"; private static final String INFO_VERSION = "1.0";
private static final String INFO_DESCRIPTION = "基于SQLREST配置生成的Swagger在线接口文档";
private static final String TOKEN_MODEL = "TOKEN认证授权"; private static final String TOKEN_MODEL = "TOKEN认证授权";
private static final String BASE_PATH = "/"; private static final String APPLICATION_JSON = "application/json";
private static final String VAR_NAME_QUERY = "query"; private static final String AUTHORIZATION = "Authorization";
private static final String BODY_EMPTY = "{}";
@Resource @Resource
private ApiAssignmentDao apiAssignmentDao; private ApiAssignmentDao apiAssignmentDao;
@Resource @Resource
private ApiModuleDao apiModuleDao; private ApiModuleDao apiModuleDao;
@Resource
private SystemParamDao systemParamDao;
private String getApiUrlPrefix() { private String getApiUrlPrefix() {
return String.format("/%s/", Constants.API_PATH_PREFIX); return String.format("/%s/", Constants.API_PATH_PREFIX);
} }
public SwaggerEntity getSwaggerJson(HttpServletRequest request) { private Triple<String, String, String> getOpenApiInfo() {
SwaggerEntity.Info info = new SwaggerEntity.Info(INFO_TITLE, INFO_VERSION, INFO_TITLE, null, null); SystemParamEntity title = systemParamDao.getByParamKey(Constants.SYS_PARAM_KEY_SWAGGER_INFO_TITLE);
Map<String, Object> securityDefinitionMap = new HashMap<>(); SystemParamEntity version = systemParamDao.getByParamKey(Constants.SYS_PARAM_KEY_SWAGGER_INFO_VERSION);
Map<String, Object> securityMap = new HashMap<>(); SystemParamEntity description = systemParamDao.getByParamKey(Constants.SYS_PARAM_KEY_SWAGGER_INFO_DESCRIPTION);
return Triple.of(
Optional.ofNullable(title).map(SystemParamEntity::getParamValue).orElse(INFO_TITLE),
Optional.ofNullable(version).map(SystemParamEntity::getParamValue).orElse(INFO_VERSION),
Optional.ofNullable(description).map(SystemParamEntity::getParamValue).orElse(INFO_DESCRIPTION)
);
}
// Bearer Token public OpenAPI getSwaggerJson(HttpServletRequest request) {
securityDefinitionMap.put(SwaggerEntity.BearerAuth.KEY_NAME, new SwaggerEntity.BearerAuth()); Triple<String, String, String> entity = getOpenApiInfo();
securityMap.put(SwaggerEntity.BearerAuth.KEY_NAME, new String[]{}); Info info = new Info().title(entity.getLeft())
.version(entity.getMiddle())
.description(entity.getRight());
SwaggerEntity swaggerEntity = new SwaggerEntity(); OpenAPI openAPI = new OpenAPI();
swaggerEntity.setInfo(info); openAPI.info(info);
swaggerEntity.setSchemes(Lists.newArrayList("http"));
swaggerEntity.setBasePath(BASE_PATH);
swaggerEntity.addSecurityDefinitions(securityDefinitionMap);
swaggerEntity.addSecurity(securityMap);
// 模块
List<ApiModuleEntity> moduleEntities = apiModuleDao.listAll(); List<ApiModuleEntity> moduleEntities = apiModuleDao.listAll();
for (ApiModuleEntity module : moduleEntities) {
openAPI.addTagsItem(new Tag().name(module.getName()));
}
//token获取接口
openAPI.path("/token/generate", getTokenGeneratePathItem());
// 自定义的接口
Map<Long, ApiModuleEntity> moduleIdMap = moduleEntities.stream() Map<Long, ApiModuleEntity> moduleIdMap = moduleEntities.stream()
.collect(Collectors.toMap(ApiModuleEntity::getId, .collect(Collectors.toMap(ApiModuleEntity::getId,
Function.identity(), (a, b) -> a)); Function.identity(), (a, b) -> a));
moduleEntities.forEach(module -> swaggerEntity.addTag(module.getName(), module.getName()));
swaggerEntity.addTag(TOKEN_MODEL, TOKEN_MODEL);
String urlPrefix = getApiUrlPrefix(); String urlPrefix = getApiUrlPrefix();
for (ApiAssignmentEntity assignment : apiAssignmentDao.listAll()) { for (ApiAssignmentEntity assignment : apiAssignmentDao.listAll()) {
if (!assignment.getStatus()) { if (!assignment.getStatus()) {
// 过滤掉未发布的 // 过滤掉未发布的
continue; continue;
} }
String path = urlPrefix + assignment.getPath(); String path = urlPrefix + assignment.getPath();
String method = assignment.getMethod().name(); HttpMethodEnum method = assignment.getMethod();
SwaggerEntity.Path pathInfo = new SwaggerEntity.Path(String.valueOf(assignment.getId()));
pathInfo.addTag(moduleIdMap.get(assignment.getModuleId()).getName()); PathItem pathItem = new PathItem();
pathInfo.addProduce(assignment.getContentType()); pathItem.setSummary(assignment.getName());
pathInfo.setSummary(assignment.getName()); pathItem.setDescription(StringUtils.defaultIfBlank(assignment.getDescription(), assignment.getName()));
pathInfo.setDescription(StringUtils.defaultIfBlank(assignment.getDescription(), assignment.getName()));
pathInfo.addProduce(MediaType.APPLICATION_JSON_VALUE); Operation operation = new Operation();
operation.setOperationId(String.valueOf(assignment.getId()));
operation.addTagsItem(moduleIdMap.get(assignment.getModuleId()).getName());
// 入参 // 入参
List<ItemParam> params = assignment.getParams(); List<ItemParam> params = assignment.getParams();
if (!CollectionUtils.isEmpty(params)) { if (!CollectionUtils.isEmpty(params)) {
for (ItemParam param : params) { List<ItemParam> paramList = params.stream()
boolean required = param.getRequired(); .filter(i -> i.getLocation().isParameter())
String name = param.getName(); .collect(Collectors.toList());
String type = param.getType().getJsType(); for (ItemParam param : paramList) {
String description = param.getRemark(); ParamTypeEnum type = param.getType();
Map<String, Object> mapParams = SwaggerEntity
.createParameter(required, name, VAR_NAME_QUERY, type, description, param.getType().getExample()); Parameter parameter =
pathInfo.addParameter(mapParams); param.getLocation().isHeader()
? new HeaderParameter()
: new QueryParameter();
parameter.setName(param.getName());
Schema schema = new Schema().type(type.getJsType());
if (param.getIsArray()) {
ArraySchema arraySchema = new ArraySchema().items(schema);
parameter.setSchema(arraySchema);
} else {
parameter.setSchema(schema);
}
parameter.setDescription(param.getRemark());
parameter.setRequired(param.getRequired());
parameter.setDeprecated(false);
parameter.allowEmptyValue(true);
parameter.setExample(param.getType().getExample());
operation.addParametersItem(parameter);
}
List<ItemParam> requestBodyList = params.stream()
.filter(i -> i.getLocation().isRequestBody())
.collect(Collectors.toList());
if (requestBodyList.size() > 0) {
RequestBody requestBody = new RequestBody();
requestBody.setRequired(true);
requestBody.setDescription(assignment.getName());
Schema objectSchema = new ObjectSchema().name("type");
for (ItemParam param : requestBodyList) {
ParamTypeEnum type = param.getType();
Schema schema = new Schema().type(type.getJsType());
if (param.getIsArray()) {
ArraySchema arraySchema = new ArraySchema().items(schema);
objectSchema.addProperties(param.getName(), arraySchema);
} else {
objectSchema.addProperties(param.getName(), schema);
}
}
Content content = new Content();
content.addMediaType(
assignment.getContentType(),
new MediaType().schema(objectSchema)
);
requestBody.setContent(content);
operation.setRequestBody(requestBody);
} }
} }
if (HttpMethodEnum.GET == method) {
pathItem.setGet(operation);
} else if (HttpMethodEnum.HEAD == method) {
pathItem.setHead(operation);
} else if (HttpMethodEnum.PUT == method) {
pathItem.setPut(operation);
} else if (HttpMethodEnum.POST == method) {
pathItem.setPost(operation);
} else if (HttpMethodEnum.DELETE == method) {
pathItem.setDelete(operation);
} else {
continue;
}
// 响应 // 响应
String unique = assignment.getId().toString(); operation.setResponses(getApiResponses());
pathInfo.addResponse("200", "OK", ImmutableMap.of("$ref", "#/definitions/" + unique)); operation.security(Collections.singletonList(new SecurityRequirement().addList(AUTHORIZATION)));
pathInfo.addResponse("401", ResultEntity.failed(ResponseErrorCode.ERROR_TOKEN_EXPIRED));
pathInfo.addResponse("403", ResultEntity.failed(ResponseErrorCode.ERROR_ACCESS_FORBIDDEN)); openAPI.path(path, pathItem);
pathInfo.addResponse("404", ResultEntity.failed(ResponseErrorCode.ERROR_PATH_NOT_EXISTS)); }
swaggerEntity.addDefinitions(unique,
ImmutableMap.of("type", "object", Map<String, SecurityScheme> securitySchemes = ImmutableMap.of(
"properties", Collections.emptyMap(), AUTHORIZATION,
"title", unique + "|" + assignment.getName()) new SecurityScheme()
.type(Type.HTTP)
.scheme("bearer")
.bearerFormat("JWT")
); );
openAPI.setComponents(new Components().securitySchemes(securitySchemes));
swaggerEntity.addPath(path, method, pathInfo); return openAPI;
} }
//token获取接口 private PathItem getTokenGeneratePathItem() {
String path = "/token/generate"; PathItem pathItem = new PathItem();
String method = "POST"; pathItem.setSummary(TOKEN_MODEL);
SwaggerEntity.Path pathInfo = new SwaggerEntity.Path("0"); pathItem.setDescription(TOKEN_MODEL);
pathInfo.addTag(TOKEN_MODEL);
pathInfo.addProduce(MediaType.APPLICATION_JSON_VALUE); RequestBody requestBody = new RequestBody();
pathInfo.setSummary(TOKEN_MODEL); requestBody.setRequired(true);
pathInfo.setDescription(TOKEN_MODEL); requestBody.setDescription(TOKEN_MODEL);
Map<String, Object> mapParamsUser = SwaggerEntity Content content = new Content();
.createParameter(true, "clientId", VAR_NAME_QUERY, "string", "账号", null); content.addMediaType(APPLICATION_JSON,
pathInfo.addParameter(mapParamsUser); new MediaType().schema(
Map<String, Object> mapParamsPwd = SwaggerEntity new ObjectSchema().name("type")
.createParameter(true, "secret", VAR_NAME_QUERY, "string", "密码", null); .addProperties("clientId", new StringSchema())
pathInfo.addParameter(mapParamsPwd); .addProperties("secret", new StringSchema())
pathInfo.addResponse("200", BODY_EMPTY); )
swaggerEntity.addPath(path, method, pathInfo); );
requestBody.setContent(content);
return swaggerEntity;
Operation operation = new Operation();
operation.setOperationId("0");
operation.addTagsItem(TOKEN_MODEL);
operation.setRequestBody(requestBody);
ApiResponses apiResponses = new ApiResponses();
apiResponses.addApiResponse("200",
new ApiResponse().description("OK")
.content(
new Content().addMediaType(
"application/json",
new MediaType().schema(
new Schema().name("type")
.type("object")
.addProperties("accessToken", new StringSchema())
.addProperties("expireSeconds", new NumberSchema())
)
)
)
);
apiResponses.addApiResponse("201",
new ApiResponse().description("Created")
);
apiResponses.addApiResponse("401",
new ApiResponse().description("Unauthorized")
);
apiResponses.addApiResponse("403",
new ApiResponse().description("Forbidden")
);
apiResponses.addApiResponse("404",
new ApiResponse().description("Not Found")
);
operation.setResponses(apiResponses);
pathItem.setPost(operation);
return pathItem;
}
private ApiResponses getApiResponses() {
ApiResponses apiResponses = new ApiResponses();
apiResponses.addApiResponse("200",
new ApiResponse().description("OK").content(
new Content().addMediaType(
"*/*",
new MediaType().schema(
new Schema().name("type")
.type("object")
)
)
)
);
apiResponses.addApiResponse("201",
new ApiResponse().description("Created")
);
apiResponses.addApiResponse("401",
new ApiResponse().description("Unauthorized")
);
apiResponses.addApiResponse("403",
new ApiResponse().description("Forbidden")
);
apiResponses.addApiResponse("404",
new ApiResponse().description("Not Found")
);
return apiResponses;
} }
} }
...@@ -64,9 +64,13 @@ public class ClientTokenService { ...@@ -64,9 +64,13 @@ public class ClientTokenService {
public AccessToken generateToken(String clientId, String clientSecret) { public AccessToken generateToken(String clientId, String clientSecret) {
AppClientEntity appClient = appClientDao.getByAppKey(clientId); AppClientEntity appClient = appClientDao.getByAppKey(clientId);
if (null == appClient || !StringUtils.equals(appClient.getAppSecret(), clientSecret)) { if (null == appClient) {
return null; throw new CommonException(ResponseErrorCode.ERROR_CLIENT_FORBIDDEN, "clientId invalid");
}
if (!StringUtils.equals(appClient.getAppSecret(), clientSecret)) {
throw new CommonException(ResponseErrorCode.ERROR_CLIENT_FORBIDDEN, "secret invalid");
} }
String token = TokenUtils.generateValue(); String token = TokenUtils.generateValue();
AccessToken clientToken = AccessToken.builder() AccessToken clientToken = AccessToken.builder()
.realName(appClient.getName()) .realName(appClient.getName())
......
package com.gitee.sqlrest.executor.controller; package com.gitee.sqlrest.executor.controller;
import com.gitee.sqlrest.common.consts.Constants; import com.gitee.sqlrest.common.consts.Constants;
import com.gitee.sqlrest.core.dto.SwaggerEntity;
import com.gitee.sqlrest.core.servlet.ApiSwaggerService; import com.gitee.sqlrest.core.servlet.ApiSwaggerService;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
import io.swagger.v3.oas.models.OpenAPI;
import javax.annotation.Resource; import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import springfox.documentation.spring.web.json.Json;
import springfox.documentation.spring.web.json.JsonSerializer;
@Api(tags = {"Swagger接口文档"}) @Api(tags = {"Swagger接口文档"})
@RestController @RestController
...@@ -18,9 +22,12 @@ public class ApiSwaggerController { ...@@ -18,9 +22,12 @@ public class ApiSwaggerController {
@Resource @Resource
private ApiSwaggerService apiSwaggerService; private ApiSwaggerService apiSwaggerService;
@Resource
private JsonSerializer jsonSerializer;
@GetMapping(value = "/swagger.json", produces = MediaType.APPLICATION_JSON_VALUE) @GetMapping(value = "/swagger.json", produces = MediaType.APPLICATION_JSON_VALUE)
public SwaggerEntity getSwaggerJson(HttpServletRequest request) { public ResponseEntity<Json> getSwaggerJson(HttpServletRequest request) {
return apiSwaggerService.getSwaggerJson(request); OpenAPI oas = apiSwaggerService.getSwaggerJson(request);
return new ResponseEntity(this.jsonSerializer.toJson(oas), HttpStatus.OK);
} }
} }
package com.gitee.sqlrest.executor.controller; package com.gitee.sqlrest.executor.controller;
import com.gitee.sqlrest.common.dto.AccessToken; import com.gitee.sqlrest.common.dto.AccessToken;
import com.gitee.sqlrest.common.dto.ResultEntity;
import com.gitee.sqlrest.common.exception.ResponseErrorCode;
import com.gitee.sqlrest.core.servlet.ClientTokenService; import com.gitee.sqlrest.core.servlet.ClientTokenService;
import java.util.Map;
import javax.annotation.Resource; import javax.annotation.Resource;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
...@@ -18,11 +18,7 @@ public class ClientTokenController { ...@@ -18,11 +18,7 @@ public class ClientTokenController {
private ClientTokenService clientTokenService; private ClientTokenService clientTokenService;
@PostMapping(value = "/generate", produces = MediaType.APPLICATION_JSON_VALUE) @PostMapping(value = "/generate", produces = MediaType.APPLICATION_JSON_VALUE)
public ResultEntity<AccessToken> generateToken(String clientId, String secret) { public AccessToken generateToken(@RequestBody Map<String, String> body) {
AccessToken token = clientTokenService.generateToken(clientId, secret); return clientTokenService.generateToken(body.get("clientId"), body.get("secret"));
if (null == token) {
return ResultEntity.failed(ResponseErrorCode.ERROR_RESOURCE_NOT_EXISTS);
}
return ResultEntity.success(token);
} }
} }
...@@ -65,7 +65,9 @@ public class SentinelFlowControlManager implements FlowControlManger { ...@@ -65,7 +65,9 @@ public class SentinelFlowControlManager implements FlowControlManger {
} }
if (rules.size() > 0) { if (rules.size() > 0) {
FlowRuleManager.loadRules(rules); FlowRuleManager.loadRules(rules);
log.info("Success refresh flow rules count: {}", rules.size()); if (log.isDebugEnabled()) {
log.debug("Success refresh flow rules count: {}", rules.size());
}
} }
} }
......
...@@ -50,6 +50,14 @@ eureka: ...@@ -50,6 +50,14 @@ eureka:
service-url: service-url:
defaultZone: http://${MANAGER_HOST}:${MANAGER_PORT}/eureka/ defaultZone: http://${MANAGER_HOST}:${MANAGER_PORT}/eureka/
springfox:
documentation:
enabled: true
auto-startup: false
swagger-ui:
enabled: false
datasource: datasource:
driver: driver:
base-path: ${APP_DRIVERS_PATH} base-path: ${APP_DRIVERS_PATH}
\ No newline at end of file
...@@ -154,6 +154,20 @@ ...@@ -154,6 +154,20 @@
:disabled="isOnlyShowDetail"> </el-input> :disabled="isOnlyShowDetail"> </el-input>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="参数位置"
min-width="25%">
<template slot-scope="scope">
<el-select v-model="scope.row.location"
:disabled="isOnlyShowDetail">
<el-option label='header'
value='REQUEST_HEADER'></el-option>
<el-option label='body'
value='REQUEST_BODY'></el-option>
<el-option label='query'
value='REQUEST_FORM'></el-option>
</el-select>
</template>
</el-table-column>
<el-table-column label="参数类型" <el-table-column label="参数类型"
min-width="25%"> min-width="25%">
<template slot-scope="scope"> <template slot-scope="scope">
...@@ -568,7 +582,7 @@ export default { ...@@ -568,7 +582,7 @@ export default {
groupList: [], groupList: [],
moduleList: [], moduleList: [],
connectionList: [], connectionList: [],
contentTypes: ['application/x-www-form-urlencoded'], contentTypes: ['application/x-www-form-urlencoded', 'application/json'],
showTree: true, showTree: true,
createParam: { createParam: {
id: null, id: null,
...@@ -579,7 +593,7 @@ export default { ...@@ -579,7 +593,7 @@ export default {
module: null, module: null,
method: null, method: null,
path: null, path: null,
contentType: 'application/x-www-form-urlencoded', contentType: null,
engine: 'SQL', engine: 'SQL',
sqls: [], sqls: [],
script: '', script: '',
...@@ -1044,7 +1058,7 @@ export default { ...@@ -1044,7 +1058,7 @@ export default {
this.inputParams.push( this.inputParams.push(
{ {
name: item.name, name: item.name,
location: 'FORM_DATA', location: 'REQUEST_FORM',
type: "STRING", type: "STRING",
isArray: item.isArray, isArray: item.isArray,
required: true, required: true,
...@@ -1070,7 +1084,7 @@ export default { ...@@ -1070,7 +1084,7 @@ export default {
this.inputParams.push( this.inputParams.push(
{ {
name: "", name: "",
location: 'FORM_DATA', location: 'REQUEST_FORM',
type: "STRING", type: "STRING",
isArray: false, isArray: false,
required: true, required: true,
...@@ -1087,7 +1101,7 @@ export default { ...@@ -1087,7 +1101,7 @@ export default {
{ {
name: "apiPageNum", name: "apiPageNum",
type: "LONG", type: "LONG",
location: 'FORM_DATA', location: 'REQUEST_FORM',
isArray: false, isArray: false,
required: true, required: true,
defaultValue: "1", defaultValue: "1",
...@@ -1101,7 +1115,7 @@ export default { ...@@ -1101,7 +1115,7 @@ export default {
{ {
name: "apiPageSize", name: "apiPageSize",
type: "LONG", type: "LONG",
location: 'FORM_DATA', location: 'REQUEST_FORM',
isArray: false, isArray: false,
required: true, required: true,
defaultValue: "10", defaultValue: "10",
......
...@@ -62,6 +62,8 @@ CREATE TABLE `SQLREST_API_ASSIGNMENT` ...@@ -62,6 +62,8 @@ CREATE TABLE `SQLREST_API_ASSIGNMENT`
`status` tinyint(1) not null default 0 comment '是否发布', `status` tinyint(1) not null default 0 comment '是否发布',
`open` tinyint(1) not null default 0 comment '是否公开', `open` tinyint(1) not null default 0 comment '是否公开',
`engine` varchar(16) not null default 'SQL' comment '执行引擎', `engine` varchar(16) not null default 'SQL' comment '执行引擎',
`response_format` tinytext default null comment '响应格式配置',
`naming_strategy` varchar(16) not null default 'NONE' comment '响应命名策略',
`flow_status` tinyint(1) not null default 0 comment '是否开启流量控制', `flow_status` tinyint(1) not null default 0 comment '是否开启流量控制',
`flow_grade` bigint(20) unsigned default null comment '流控类型', `flow_grade` bigint(20) unsigned default null comment '流控类型',
`flow_count` bigint(20) unsigned default null comment '流控阈值', `flow_count` bigint(20) unsigned default null comment '流控阈值',
......
...@@ -16,6 +16,10 @@ VALUES ('1', '测试', '测试使用', 'test', 'test', 'FOR_EVER', '-1', md5(uui ...@@ -16,6 +16,10 @@ VALUES ('1', '测试', '测试使用', 'test', 'test', 'FOR_EVER', '-1', md5(uui
INSERT INTO `SQLREST_CLIENT_GROUP` (`id`, `client_id`, `group_id`) INSERT INTO `SQLREST_CLIENT_GROUP` (`id`, `client_id`, `group_id`)
VALUES ('1', '1', '1'); VALUES ('1', '1', '1');
INSERT INTO `SQLREST_SYSTEM_PARAM` (`param_key`, `param_type`, `param_value`) VALUES ('apiDocOpen', 'BOOLEAN', 'true');
INSERT INTO `SQLREST_SYSTEM_PARAM` (`param_key`, `param_type`, `param_value`) VALUES ('apiDocInfoTitle', 'STRING', '在线接口文档');
INSERT INTO `SQLREST_SYSTEM_PARAM` (`param_key`, `param_type`, `param_value`) VALUES ('apiDocInfoVersion', 'STRING', '1.0');
INSERT INTO `SQLREST_SYSTEM_PARAM` (`param_key`, `param_type`, `param_value`) VALUES ('apiDocInfoDescription', 'STRING', 'Swagger在线接口文档');
ALTER TABLE `SQLREST_API_ASSIGNMENT`
ADD COLUMN `response_format` tinytext NULL comment '响应格式配置' AFTER `engine`,
ADD COLUMN `naming_strategy` varchar(16) NOT NULL DEFAULT 'NONE' comment '命名策略' AFTER `response_format` ;
UPDATE SQLREST_API_ASSIGNMENT set response_format = '{"LOCAL_DATE":"yyyy-MM-dd","DATE":"yyyy-MM-dd","TIMESTAMP":"yyyy-MM-dd HH:mm","LOCAL_DATE_TIME":"yyyy-MM-dd HH:mm","TIME":"HH:mm:ss","BIG_DECIMAL":"6"}' where response_format is null;
INSERT INTO `SQLREST_SYSTEM_PARAM` (`param_key`, `param_type`, `param_value`) VALUES ('apiDocOpen', 'BOOLEAN', 'true');
<!DOCTYPE html><html><head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><title>SQLREST工具</title><link href=/static/css/app.5c8fbd832dc67e072771290ff1dfeb72.css rel=stylesheet></head><body><div id=app></div><script type=text/javascript src=/static/js/manifest.4cf28694c2d9979f6f9e.js></script><script type=text/javascript src=/static/js/vendor.b8089f9fd73f8896df25.js></script><script type=text/javascript src=/static/js/app.f6411b5ec606284ab8a0.js></script></body></html> <!DOCTYPE html><html><head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><title>SQLREST工具</title><link href=/static/css/app.7e1ab6e082220ffa6c8de7c8d41c355c.css rel=stylesheet></head><body><div id=app></div><script type=text/javascript src=/static/js/manifest.2d58d9aaeea67d24a4fa.js></script><script type=text/javascript src=/static/js/vendor.b8089f9fd73f8896df25.js></script><script type=text/javascript src=/static/js/app.f6411b5ec606284ab8a0.js></script></body></html>
\ No newline at end of file \ No newline at end of file
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
!function(e){var n=window.webpackJsonp;window.webpackJsonp=function(r,o,a){for(var d,f,i,u=0,b=[];u<r.length;u++)f=r[u],t[f]&&b.push(t[f][0]),t[f]=0;for(d in o)Object.prototype.hasOwnProperty.call(o,d)&&(e[d]=o[d]);for(n&&n(r,o,a);b.length;)b.shift()();if(a)for(u=0;u<a.length;u++)i=c(c.s=a[u]);return i};var r={},t={22:0};function c(n){if(r[n])return r[n].exports;var t=r[n]={i:n,l:!1,exports:{}};return e[n].call(t.exports,t,t.exports,c),t.l=!0,t.exports}c.e=function(e){var n=t[e];if(0===n)return new Promise(function(e){e()});if(n)return n[2];var r=new Promise(function(r,c){n=t[e]=[r,c]});n[2]=r;var o=document.getElementsByTagName("head")[0],a=document.createElement("script");a.type="text/javascript",a.charset="utf-8",a.async=!0,a.timeout=12e4,c.nc&&a.setAttribute("nonce",c.nc),a.src=c.p+"static/js/"+e+"."+{0:"04668914d5dbd2646823",1:"b17200cccd46e216dcb3",2:"140338f6a5528feea1a3",3:"8d7b8f0b413a10853aad",4:"0fbd7bc32b56153939ab",5:"f69840e8bd74f4d4e92b",6:"8f85de06573e2a5f9562",7:"7ea6008d16a44e79a428",8:"7483ee6d3a25506eb489",9:"1f165c58c9933d0da8a7",10:"cdd03027e5c73f31170c",11:"cdde61370dec5108c322",12:"57d1188c7336fe654844",13:"cdc0bd22251263ac4669",14:"42cdbd66a7803b30c641",15:"3b3f0c03ff4fed9903cc",16:"4de955682c1f7710c7ea",17:"819547b2361d544d3b8b",18:"5e7f065a8d031847e833",19:"3936346cb7e30aa279e2"}[e]+".js";var d=setTimeout(f,12e4);function f(){a.onerror=a.onload=null,clearTimeout(d);var n=t[e];0!==n&&(n&&n[1](new Error("Loading chunk "+e+" failed.")),t[e]=void 0)}return a.onerror=a.onload=f,o.appendChild(a),r},c.m=e,c.c=r,c.d=function(e,n,r){c.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:r})},c.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return c.d(n,"a",n),n},c.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},c.p="/",c.oe=function(e){throw console.error(e),e}}([]); !function(e){var n=window.webpackJsonp;window.webpackJsonp=function(r,o,a){for(var f,d,i,u=0,b=[];u<r.length;u++)d=r[u],t[d]&&b.push(t[d][0]),t[d]=0;for(f in o)Object.prototype.hasOwnProperty.call(o,f)&&(e[f]=o[f]);for(n&&n(r,o,a);b.length;)b.shift()();if(a)for(u=0;u<a.length;u++)i=c(c.s=a[u]);return i};var r={},t={22:0};function c(n){if(r[n])return r[n].exports;var t=r[n]={i:n,l:!1,exports:{}};return e[n].call(t.exports,t,t.exports,c),t.l=!0,t.exports}c.e=function(e){var n=t[e];if(0===n)return new Promise(function(e){e()});if(n)return n[2];var r=new Promise(function(r,c){n=t[e]=[r,c]});n[2]=r;var o=document.getElementsByTagName("head")[0],a=document.createElement("script");a.type="text/javascript",a.charset="utf-8",a.async=!0,a.timeout=12e4,c.nc&&a.setAttribute("nonce",c.nc),a.src=c.p+"static/js/"+e+"."+{0:"c6b968f702956d23cafc",1:"b17200cccd46e216dcb3",2:"140338f6a5528feea1a3",3:"8d7b8f0b413a10853aad",4:"0fbd7bc32b56153939ab",5:"f69840e8bd74f4d4e92b",6:"8f85de06573e2a5f9562",7:"7ea6008d16a44e79a428",8:"7483ee6d3a25506eb489",9:"1f165c58c9933d0da8a7",10:"cdd03027e5c73f31170c",11:"cdde61370dec5108c322",12:"57d1188c7336fe654844",13:"cdc0bd22251263ac4669",14:"42cdbd66a7803b30c641",15:"3b3f0c03ff4fed9903cc",16:"4de955682c1f7710c7ea",17:"819547b2361d544d3b8b",18:"5e7f065a8d031847e833",19:"3936346cb7e30aa279e2"}[e]+".js";var f=setTimeout(d,12e4);function d(){a.onerror=a.onload=null,clearTimeout(f);var n=t[e];0!==n&&(n&&n[1](new Error("Loading chunk "+e+" failed.")),t[e]=void 0)}return a.onerror=a.onload=d,o.appendChild(a),r},c.m=e,c.c=r,c.d=function(e,n,r){c.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:r})},c.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return c.d(n,"a",n),n},c.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},c.p="/",c.oe=function(e){throw console.error(e),e}}([]);
//# sourceMappingURL=manifest.4cf28694c2d9979f6f9e.js.map //# sourceMappingURL=manifest.2d58d9aaeea67d24a4fa.js.map
\ No newline at end of file \ No newline at end of file
{"version":3,"sources":["webpack:///webpack/bootstrap bcd72596f1dc96aa35da"],"names":["parentJsonpFunction","window","chunkIds","moreModules","executeModules","moduleId","chunkId","result","i","resolves","length","installedChunks","push","Object","prototype","hasOwnProperty","call","modules","shift","__webpack_require__","s","installedModules","22","exports","module","l","e","installedChunkData","Promise","resolve","promise","reject","head","document","getElementsByTagName","script","createElement","type","charset","async","timeout","nc","setAttribute","src","p","0","1","2","3","4","5","6","7","8","9","10","11","12","13","14","15","16","17","18","19","setTimeout","onScriptComplete","onerror","onload","clearTimeout","chunk","Error","undefined","appendChild","m","c","d","name","getter","o","defineProperty","configurable","enumerable","get","n","__esModule","object","property","oe","err","console","error"],"mappings":"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","file":"static/js/manifest.4cf28694c2d9979f6f9e.js","sourcesContent":[" \t// install a JSONP callback for chunk loading\n \tvar parentJsonpFunction = window[\"webpackJsonp\"];\n \twindow[\"webpackJsonp\"] = function webpackJsonpCallback(chunkIds, moreModules, executeModules) {\n \t\t// add \"moreModules\" to the modules object,\n \t\t// then flag all \"chunkIds\" as loaded and fire callback\n \t\tvar moduleId, chunkId, i = 0, resolves = [], result;\n \t\tfor(;i < chunkIds.length; i++) {\n \t\t\tchunkId = chunkIds[i];\n \t\t\tif(installedChunks[chunkId]) {\n \t\t\t\tresolves.push(installedChunks[chunkId][0]);\n \t\t\t}\n \t\t\tinstalledChunks[chunkId] = 0;\n \t\t}\n \t\tfor(moduleId in moreModules) {\n \t\t\tif(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {\n \t\t\t\tmodules[moduleId] = moreModules[moduleId];\n \t\t\t}\n \t\t}\n \t\tif(parentJsonpFunction) parentJsonpFunction(chunkIds, moreModules, executeModules);\n \t\twhile(resolves.length) {\n \t\t\tresolves.shift()();\n \t\t}\n \t\tif(executeModules) {\n \t\t\tfor(i=0; i < executeModules.length; i++) {\n \t\t\t\tresult = __webpack_require__(__webpack_require__.s = executeModules[i]);\n \t\t\t}\n \t\t}\n \t\treturn result;\n \t};\n\n \t// The module cache\n \tvar installedModules = {};\n\n \t// objects to store loaded and loading chunks\n \tvar installedChunks = {\n \t\t22: 0\n \t};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n \t// This file contains only the entry chunk.\n \t// The chunk loading function for additional chunks\n \t__webpack_require__.e = function requireEnsure(chunkId) {\n \t\tvar installedChunkData = installedChunks[chunkId];\n \t\tif(installedChunkData === 0) {\n \t\t\treturn new Promise(function(resolve) { resolve(); });\n \t\t}\n\n \t\t// a Promise means \"currently loading\".\n \t\tif(installedChunkData) {\n \t\t\treturn installedChunkData[2];\n \t\t}\n\n \t\t// setup Promise in chunk cache\n \t\tvar promise = new Promise(function(resolve, reject) {\n \t\t\tinstalledChunkData = installedChunks[chunkId] = [resolve, reject];\n \t\t});\n \t\tinstalledChunkData[2] = promise;\n\n \t\t// start chunk loading\n \t\tvar head = document.getElementsByTagName('head')[0];\n \t\tvar script = document.createElement('script');\n \t\tscript.type = \"text/javascript\";\n \t\tscript.charset = 'utf-8';\n \t\tscript.async = true;\n \t\tscript.timeout = 120000;\n\n \t\tif (__webpack_require__.nc) {\n \t\t\tscript.setAttribute(\"nonce\", __webpack_require__.nc);\n \t\t}\n \t\tscript.src = __webpack_require__.p + \"static/js/\" + chunkId + \".\" + {\"0\":\"04668914d5dbd2646823\",\"1\":\"b17200cccd46e216dcb3\",\"2\":\"140338f6a5528feea1a3\",\"3\":\"8d7b8f0b413a10853aad\",\"4\":\"0fbd7bc32b56153939ab\",\"5\":\"f69840e8bd74f4d4e92b\",\"6\":\"8f85de06573e2a5f9562\",\"7\":\"7ea6008d16a44e79a428\",\"8\":\"7483ee6d3a25506eb489\",\"9\":\"1f165c58c9933d0da8a7\",\"10\":\"cdd03027e5c73f31170c\",\"11\":\"cdde61370dec5108c322\",\"12\":\"57d1188c7336fe654844\",\"13\":\"cdc0bd22251263ac4669\",\"14\":\"42cdbd66a7803b30c641\",\"15\":\"3b3f0c03ff4fed9903cc\",\"16\":\"4de955682c1f7710c7ea\",\"17\":\"819547b2361d544d3b8b\",\"18\":\"5e7f065a8d031847e833\",\"19\":\"3936346cb7e30aa279e2\"}[chunkId] + \".js\";\n \t\tvar timeout = setTimeout(onScriptComplete, 120000);\n \t\tscript.onerror = script.onload = onScriptComplete;\n \t\tfunction onScriptComplete() {\n \t\t\t// avoid mem leaks in IE.\n \t\t\tscript.onerror = script.onload = null;\n \t\t\tclearTimeout(timeout);\n \t\t\tvar chunk = installedChunks[chunkId];\n \t\t\tif(chunk !== 0) {\n \t\t\t\tif(chunk) {\n \t\t\t\t\tchunk[1](new Error('Loading chunk ' + chunkId + ' failed.'));\n \t\t\t\t}\n \t\t\t\tinstalledChunks[chunkId] = undefined;\n \t\t\t}\n \t\t};\n \t\thead.appendChild(script);\n\n \t\treturn promise;\n \t};\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, {\n \t\t\t\tconfigurable: false,\n \t\t\t\tenumerable: true,\n \t\t\t\tget: getter\n \t\t\t});\n \t\t}\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"/\";\n\n \t// on error function for async loading\n \t__webpack_require__.oe = function(err) { console.error(err); throw err; };\n\n\n\n// WEBPACK FOOTER //\n// webpack/bootstrap bcd72596f1dc96aa35da"],"sourceRoot":""} {"version":3,"sources":["webpack:///webpack/bootstrap c0b65d6aca69717a6bf2"],"names":["parentJsonpFunction","window","chunkIds","moreModules","executeModules","moduleId","chunkId","result","i","resolves","length","installedChunks","push","Object","prototype","hasOwnProperty","call","modules","shift","__webpack_require__","s","installedModules","22","exports","module","l","e","installedChunkData","Promise","resolve","promise","reject","head","document","getElementsByTagName","script","createElement","type","charset","async","timeout","nc","setAttribute","src","p","0","1","2","3","4","5","6","7","8","9","10","11","12","13","14","15","16","17","18","19","setTimeout","onScriptComplete","onerror","onload","clearTimeout","chunk","Error","undefined","appendChild","m","c","d","name","getter","o","defineProperty","configurable","enumerable","get","n","__esModule","object","property","oe","err","console","error"],"mappings":"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","file":"static/js/manifest.2d58d9aaeea67d24a4fa.js","sourcesContent":[" \t// install a JSONP callback for chunk loading\n \tvar parentJsonpFunction = window[\"webpackJsonp\"];\n \twindow[\"webpackJsonp\"] = function webpackJsonpCallback(chunkIds, moreModules, executeModules) {\n \t\t// add \"moreModules\" to the modules object,\n \t\t// then flag all \"chunkIds\" as loaded and fire callback\n \t\tvar moduleId, chunkId, i = 0, resolves = [], result;\n \t\tfor(;i < chunkIds.length; i++) {\n \t\t\tchunkId = chunkIds[i];\n \t\t\tif(installedChunks[chunkId]) {\n \t\t\t\tresolves.push(installedChunks[chunkId][0]);\n \t\t\t}\n \t\t\tinstalledChunks[chunkId] = 0;\n \t\t}\n \t\tfor(moduleId in moreModules) {\n \t\t\tif(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {\n \t\t\t\tmodules[moduleId] = moreModules[moduleId];\n \t\t\t}\n \t\t}\n \t\tif(parentJsonpFunction) parentJsonpFunction(chunkIds, moreModules, executeModules);\n \t\twhile(resolves.length) {\n \t\t\tresolves.shift()();\n \t\t}\n \t\tif(executeModules) {\n \t\t\tfor(i=0; i < executeModules.length; i++) {\n \t\t\t\tresult = __webpack_require__(__webpack_require__.s = executeModules[i]);\n \t\t\t}\n \t\t}\n \t\treturn result;\n \t};\n\n \t// The module cache\n \tvar installedModules = {};\n\n \t// objects to store loaded and loading chunks\n \tvar installedChunks = {\n \t\t22: 0\n \t};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n \t// This file contains only the entry chunk.\n \t// The chunk loading function for additional chunks\n \t__webpack_require__.e = function requireEnsure(chunkId) {\n \t\tvar installedChunkData = installedChunks[chunkId];\n \t\tif(installedChunkData === 0) {\n \t\t\treturn new Promise(function(resolve) { resolve(); });\n \t\t}\n\n \t\t// a Promise means \"currently loading\".\n \t\tif(installedChunkData) {\n \t\t\treturn installedChunkData[2];\n \t\t}\n\n \t\t// setup Promise in chunk cache\n \t\tvar promise = new Promise(function(resolve, reject) {\n \t\t\tinstalledChunkData = installedChunks[chunkId] = [resolve, reject];\n \t\t});\n \t\tinstalledChunkData[2] = promise;\n\n \t\t// start chunk loading\n \t\tvar head = document.getElementsByTagName('head')[0];\n \t\tvar script = document.createElement('script');\n \t\tscript.type = \"text/javascript\";\n \t\tscript.charset = 'utf-8';\n \t\tscript.async = true;\n \t\tscript.timeout = 120000;\n\n \t\tif (__webpack_require__.nc) {\n \t\t\tscript.setAttribute(\"nonce\", __webpack_require__.nc);\n \t\t}\n \t\tscript.src = __webpack_require__.p + \"static/js/\" + chunkId + \".\" + {\"0\":\"c6b968f702956d23cafc\",\"1\":\"b17200cccd46e216dcb3\",\"2\":\"140338f6a5528feea1a3\",\"3\":\"8d7b8f0b413a10853aad\",\"4\":\"0fbd7bc32b56153939ab\",\"5\":\"f69840e8bd74f4d4e92b\",\"6\":\"8f85de06573e2a5f9562\",\"7\":\"7ea6008d16a44e79a428\",\"8\":\"7483ee6d3a25506eb489\",\"9\":\"1f165c58c9933d0da8a7\",\"10\":\"cdd03027e5c73f31170c\",\"11\":\"cdde61370dec5108c322\",\"12\":\"57d1188c7336fe654844\",\"13\":\"cdc0bd22251263ac4669\",\"14\":\"42cdbd66a7803b30c641\",\"15\":\"3b3f0c03ff4fed9903cc\",\"16\":\"4de955682c1f7710c7ea\",\"17\":\"819547b2361d544d3b8b\",\"18\":\"5e7f065a8d031847e833\",\"19\":\"3936346cb7e30aa279e2\"}[chunkId] + \".js\";\n \t\tvar timeout = setTimeout(onScriptComplete, 120000);\n \t\tscript.onerror = script.onload = onScriptComplete;\n \t\tfunction onScriptComplete() {\n \t\t\t// avoid mem leaks in IE.\n \t\t\tscript.onerror = script.onload = null;\n \t\t\tclearTimeout(timeout);\n \t\t\tvar chunk = installedChunks[chunkId];\n \t\t\tif(chunk !== 0) {\n \t\t\t\tif(chunk) {\n \t\t\t\t\tchunk[1](new Error('Loading chunk ' + chunkId + ' failed.'));\n \t\t\t\t}\n \t\t\t\tinstalledChunks[chunkId] = undefined;\n \t\t\t}\n \t\t};\n \t\thead.appendChild(script);\n\n \t\treturn promise;\n \t};\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, {\n \t\t\t\tconfigurable: false,\n \t\t\t\tenumerable: true,\n \t\t\t\tget: getter\n \t\t\t});\n \t\t}\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"/\";\n\n \t// on error function for async loading\n \t__webpack_require__.oe = function(err) { console.error(err); throw err; };\n\n\n\n// WEBPACK FOOTER //\n// webpack/bootstrap c0b65d6aca69717a6bf2"],"sourceRoot":""}
\ No newline at end of file \ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment