feat(接口测试): 增加导入功能

This commit is contained in:
wxg0103 2023-12-27 16:10:45 +08:00 committed by Craftsman
parent bf68a4e688
commit 3f6ba6680c
32 changed files with 5026 additions and 115 deletions

View File

@ -206,7 +206,6 @@ public class PermissionConstants {
/*------ end: PROJECT_TEMPLATE ------*/
/*------ start: FUNCTIONAL_CASE ------*/
public static final String FUNCTIONAL_CASE_READ = "FUNCTIONAL_CASE:READ";
public static final String FUNCTIONAL_CASE_READ_ADD = "FUNCTIONAL_CASE:READ+ADD";
@ -278,12 +277,10 @@ public class PermissionConstants {
public static final String PROJECT_API_SCENARIO_DEBUG = "PROJECT_API_SCENARIO:READ+DEBUG";
/*------ end: API_MANAGEMENT ------*/
//个人中心
/*------ start: PERSONAL_CENTER ------*/
public static final String SYSTEM_PERSONAL_API_KEY_ADD = "SYSTEM_PERSONAL_API_KEYD:READ+ADD";
public static final String SYSTEM_PERSONAL_API_KEY_ADD = "SYSTEM_PERSONAL_API_KEY:READ+ADD";
public static final String SYSTEM_PERSONAL_API_KEY_DELETE = "SYSTEM_PERSONAL_API_KEY:READ+DELETE";
public static final String SYSTEM_PERSONAL_API_KEY_READ = "SYSTEM_PERSONAL_API_KEY:READ";
public static final String SYSTEM_PERSONAL_API_KEY_UPDATE = "SYSTEM_PERSONAL_API_KEY:READ+UPDATE";

View File

@ -304,3 +304,4 @@ api_definition_exist=接口已存在
api_definition_mock_exist=接口 MOCK 已存在
execute_resource_pool_not_config_error=请在【项目管理-应用管理-接口测试】中选择资源池
resource_pool_execute_error=资源池调用失败
api_swagger_url_error=Swagger url无法连通

View File

@ -308,3 +308,4 @@ api_definition_exist=The API already exists
api_definition_mock_exist=The API MOCK already exists
execute_resource_pool_not_config_error=Select a resource pool in 【Project Management - Application Management - Interface Testing】
resource_pool_execute_error=The resource pool call failed
api_swagger_url_error=Swagger url unable to connect

View File

@ -308,3 +308,4 @@ api_definition_exist=接口已存在
api_definition_mock_exist=接口 MOCK 已存在
execute_resource_pool_not_config_error=请在【项目管理-应用管理-接口测试】中选择资源池
resource_pool_execute_error=资源池调用失败
api_swagger_url_error=Swagger url无法连通

View File

@ -308,3 +308,4 @@ api_definition_exist=接口已存在
api_definition_mock_exist=接口 MOCK 已存在
execute_resource_pool_not_config_error=請在【項目管理-應用管理-接口測試】中選擇資源池
resource_pool_execute_error=資源池調用失敗
api_swagger_url_error=Swagger url無法調解

View File

@ -491,4 +491,6 @@ priority_is_null=用例等级不能为空
apikey_has_expired=ApiKey 已过期
user_key.id.not_blank=ApiKey ID不能为空
expire_time_not_null=过期时间不能为空
permission.organization.name=组织
permission.organization.name=组织
swagger_parse_error_with_auth=Swagger 解析失败,请确认认证信息是否正确或文件格式是否正确!
swagger_parse_error=Swagger 解析失败,请确认文件格式是否正确!

View File

@ -501,4 +501,6 @@ priority_is_null=Priority is null
apikey_has_expired=ApiKey has expired
user_key.id.not_blank=User key id can not blank
expire_time_not_null=Expire time can not null
permission.organization.name=Organization
permission.organization.name=Organization
swagger_parse_error_with_auth=Swagger parsing failed, please confirm whether the verification information is correct or the file format is correct!
swagger_parse_error=Swagger parsing failed or file format is incorrect!

View File

@ -497,4 +497,6 @@ priority_is_null=用例等级不能为空
apikey_has_expired=ApiKey 已过期
user_key.id.not_blank=ApiKey ID不能为空
expire_time_not_null=过期时间不能为空
permission.organization.name=组织
permission.organization.name=组织
swagger_parse_error_with_auth=Swagger 解析失败,请确认认证信息是否正确或文件格式是否正确!
swagger_parse_error=Swagger 解析失败,请确认文件格式是否正确!

View File

@ -497,4 +497,6 @@ priority_is_null=優先級不能為空
apikey_has_expired=ApiKey 已過期
user_key.id.not_blank=ApiKey ID不能为空
expire_time_not_null=過期時間不能為空
permission.organization.name=組織
permission.organization.name=組織
swagger_parse_error_with_auth=Swagger 解析失敗,請檢查 Swagger 接口是否需要認證
swagger_parse_error=Swagger 解析失敗

View File

@ -4,6 +4,8 @@ import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import io.metersphere.api.domain.ApiDefinition;
import io.metersphere.api.dto.definition.*;
import io.metersphere.api.dto.definition.importdto.ApiDefinitionImport;
import io.metersphere.api.dto.request.ImportRequest;
import io.metersphere.api.service.definition.ApiDefinitionLogService;
import io.metersphere.api.service.definition.ApiDefinitionService;
import io.metersphere.sdk.constants.PermissionConstants;
@ -73,6 +75,7 @@ public class ApiDefinitionController {
public void delete(@Validated @RequestBody ApiDefinitionDeleteRequest request) {
apiDefinitionService.delete(request, SessionUtils.getUserId());
}
@PostMapping(value = "/batch-del")
@Operation(summary = "接口测试-接口管理-批量删除接口定义到回收站")
@RequiresPermissions(PermissionConstants.PROJECT_API_DEFINITION_DELETE)
@ -141,6 +144,7 @@ public class ApiDefinitionController {
public void recover(@Validated @RequestBody ApiDefinitionDeleteRequest request) {
apiDefinitionService.recover(request, SessionUtils.getUserId());
}
@PostMapping(value = "/trash-del")
@Operation(summary = "接口测试-接口管理-删除回收站接口定义")
@RequiresPermissions(PermissionConstants.PROJECT_API_DEFINITION_DELETE)
@ -149,6 +153,7 @@ public class ApiDefinitionController {
public void trashDel(@Validated @RequestBody ApiDefinitionDeleteRequest request) {
apiDefinitionService.trashDel(request, SessionUtils.getUserId());
}
@PostMapping(value = "/batch-recover")
@Operation(summary = "接口测试-接口管理-批量从回收站恢复接口定义")
@RequiresPermissions(PermissionConstants.PROJECT_API_DEFINITION_RECOVER)
@ -190,4 +195,11 @@ public class ApiDefinitionController {
return apiDefinitionService.getDocInfo(request);
}
@PostMapping(value = "/import", consumes = {"multipart/form-data"})
@RequiresPermissions(PermissionConstants.PROJECT_API_DEFINITION_IMPORT)
@Operation(summary = "接口测试-接口管理-导入接口定义")
public ApiDefinitionImport testCaseImport(@RequestPart(value = "file", required = false) MultipartFile file, @RequestPart("request") ImportRequest request) {
return apiDefinitionService.apiTestImport(file, request);
}
}

View File

@ -0,0 +1,22 @@
package io.metersphere.api.dto.definition.importdto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Data
public class ApiDeatlWithData {
@Schema(description = "相同的数据的key")
List<String> sameList = new ArrayList<>();
@Schema(description = "不同的数据的key")
List<String> differenceList = new ArrayList<>();
@Schema(description = "数据库中存在的数据")
Map<String, ApiDefinitionImportDTO> apiDateMap = new HashMap<>();
@Schema(description = "导入的数据")
Map<String, ApiDefinitionImportDTO> importDataMap = new HashMap<>();
}

View File

@ -0,0 +1,21 @@
package io.metersphere.api.dto.definition.importdto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@Data
public class ApiDeatlWithDataUpdate {
@Schema(description = "需要更新模块的数据")
List<ApiDefinitionImportDTO> updateModuleData = new ArrayList<>();
@Schema(description = "需要更新接口的数据")
List<ApiDefinitionImportDTO> updateRequestData = new ArrayList<>();
@Schema(description = "需要新增的接口数据")
List<ApiDefinitionImportDTO> addModuleData = new ArrayList<>();
@Schema(description = "需要新增的日志数据")
Map<String, ApiDefinitionImportDTO> logData;
}

View File

@ -0,0 +1,18 @@
package io.metersphere.api.dto.definition.importdto;
import io.metersphere.api.dto.definition.ApiTestCaseDTO;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
@Data
public class ApiDefinitionImport {
private String projectName;
private String protocol;
private List<ApiDefinitionImportDTO> data;
// 新版本带用例导出
private List<ApiTestCaseDTO> cases = new ArrayList<>();
}

View File

@ -1,4 +1,4 @@
package io.metersphere.api.dto.importdto;
package io.metersphere.api.dto.definition.importdto;
import io.metersphere.api.domain.ApiDefinition;
import io.metersphere.api.dto.definition.HttpResponse;

View File

@ -1,31 +1,45 @@
package io.metersphere.api.dto.request;
import io.metersphere.api.dto.request.http.Header;
import io.metersphere.api.dto.request.http.QueryParam;
import io.metersphere.api.dto.request.http.auth.HTTPAuth;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.List;
@Data
public class ImportRequest {
private String id;
private String name;
private String moduleId;
private String environmentId;
private String projectId;
private String platform;
@Schema(description = "导入的swagger地址")
private String swaggerUrl;
//导入策略
private String modeId;
@Schema(description = "如果是定时任务的时候 需要传入创建人id")
private String userId;
private String versionId; // 新导入选择的版本
private String updateVersionId; // 覆盖导入已存在的接口选择的版本
private String defaultVersion;
private String platform;
//调用类型
private String type;
// 是否开启自定义ID
private Boolean openCustomNum = false;
// 是否覆盖模块
@Schema(description = "是否覆盖模块")
private Boolean coverModule;
@Schema(description = "是否同步导入用例")
private Boolean syncCase;
@Schema(description = "是否覆盖数据")
private Boolean coverData;
// 当前协议
private String protocol;
//上传文件来源目前用于辨别是否是idea插件
private String origin;
@Schema(description = "swagger的请求头参数")
private List<Header> headers;
@Schema(description = "swagger的请求参数")
private List<QueryParam> arguments;
@Schema(description = "swagger的认证参数")
private HTTPAuth authManager;
@Schema(description = "唯一标识 默认是Method & Path 后续估计会补充")
private String uniquelyIdentifies = "Method & Path";
}

View File

@ -1,5 +1,6 @@
package io.metersphere.api.dto.request.http.body;
import io.metersphere.api.dto.schema.JsonSchemaItem;
import lombok.Data;
/**
@ -20,5 +21,9 @@ public class JsonBody {
* 启用 json-schema 时的参数对象
* todo json-schema 编辑器待调研暂时使用 Object 类型
*/
private Object jsonSchema;
private JsonSchemaItem jsonSchema;
/**
* 是否开启转换
*/
private Boolean enable = false;
}

View File

@ -0,0 +1,58 @@
package io.metersphere.api.dto.schema;
import lombok.Data;
import java.math.BigDecimal;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@Data
public class JsonSchemaItem {
private Object example;
private String id;
private String title;
private String type = "string";
private String description;
private JsonSchemaItem items;
private Map<String, Object> mock;
private Map<String, JsonSchemaItem> properties;
private JsonSchemaItem additionalProperties;
private List<String> required;
private String pattern;
private Integer maxLength;
private Integer minLength;
private BigDecimal minimum;
private BigDecimal maximum;
private String schema;
private String format;
private List<String> enumString;
private List<Number> enumInteger;
private List<BigDecimal> enumNumber;
private Map<String, Object> extensions = null;
public JsonSchemaItem() {
this.mock = new LinkedHashMap<>();
this.mock.put("mock", "");
}
public JsonSchemaItem(String type) {
this.type = type;
this.initParam(type);
}
public void setType(String type) {
this.type = type;
this.initParam(type);
}
private void initParam(String type) {
if (type.equals("object")) {
this.properties = new LinkedHashMap<>();
} else if (type.equals("array")) {
this.items = new JsonSchemaItem();
}
}
}

View File

@ -0,0 +1,33 @@
package io.metersphere.api.enums;
public class PropertyConstant {
public final static String REQUIRED = "required";
public final static String ALL_OF = "allOf";
public final static String PROPERTIES = "properties";
public final static String ADDITIONAL_PROPERTIES = "additionalProperties";
public final static String TYPE = "type";
public final static String MS_OBJECT = "MS-OBJECT";
public final static String ARRAY = "array";
public final static String OBJECT = "object";
public final static String DEFAULT = "default";
public final static String STRING = "string";
public final static String BOOLEAN = "boolean";
public final static String NUMBER = "number";
public final static String INTEGER = "integer";
public final static String MOCK = "mock";
public final static String NULL = "null";
public final static String ENUM = "enum";
public final static String ITEMS = "items";
public final static String ITEM = "_item";
public final static String NONE = "none";
public final static String ROOT = "root";
public final static String XML_PATH = "XML_PATH";
public final static String ASS_OPTION = "ASS_OPTION";
public final static String EXPECTED_VALUE = "EXPECTED_VALUE";
public final static String ELEMENT_CONDITION = "ElementCondition";
public final static String ENVIRONMENT = "useEnvironment";
public final static String ENVIRONMENT_ID = "environmentId";
public final static String DATASOURCE_ID = "dataSourceId";
public final static String PROJECT_ID = "projectId";
}

View File

@ -2,7 +2,7 @@ package io.metersphere.api.mapper;
import io.metersphere.api.domain.ApiDefinition;
import io.metersphere.api.dto.definition.*;
import io.metersphere.api.dto.importdto.ApiDefinitionImportDTO;
import io.metersphere.api.dto.definition.importdto.ApiDefinitionImportDTO;
import io.metersphere.system.dto.table.TableBatchProcessDTO;
import org.apache.ibatis.annotations.Param;

View File

@ -129,14 +129,14 @@
</foreach>
and deleted = #{deleted}
</select>
<select id="importList" resultType="io.metersphere.api.dto.importdto.ApiDefinitionImportDTO">
<select id="importList" resultType="io.metersphere.api.dto.definition.importdto.ApiDefinitionImportDTO">
select
api_definition.id, api_definition.`name`, api_definition.protocol, api_definition.`method`,
api_definition.`path`, api_definition.version_id,
api_definition.ref_id
api_definition.ref_id, api_definition.module_id
from api_definition
LEFT JOIN project_version ON project_version.id = api_definition.version_id
where api_definition.deleted = true
where api_definition.deleted = false
<include refid="queryWhereCondition"/>
</select>
<select id="selectIdsByIdsAndDeleted" resultType="java.lang.String">

View File

@ -1,23 +1,686 @@
package io.metersphere.api.parser.api;
import io.metersphere.api.dto.definition.HttpResponse;
import io.metersphere.api.dto.definition.importdto.ApiDefinitionImport;
import io.metersphere.api.dto.definition.importdto.ApiDefinitionImportDTO;
import io.metersphere.api.dto.request.ImportRequest;
import io.metersphere.api.dto.request.http.*;
import io.metersphere.api.dto.request.http.auth.NoAuth;
import io.metersphere.api.dto.request.http.body.*;
import io.metersphere.api.dto.schema.JsonSchemaItem;
import io.metersphere.api.enums.PropertyConstant;
import io.metersphere.api.parser.ImportParser;
import io.metersphere.api.utils.ApiDataUtils;
import io.metersphere.sdk.exception.MSException;
import io.metersphere.sdk.util.LogUtils;
import io.metersphere.sdk.util.Translator;
import io.metersphere.system.utils.SessionUtils;
import io.swagger.parser.OpenAPIParser;
import io.swagger.v3.oas.models.*;
import io.swagger.v3.oas.models.media.*;
import io.swagger.v3.oas.models.parameters.*;
import io.swagger.v3.oas.models.responses.ApiResponses;
import io.swagger.v3.parser.core.models.AuthorizationValue;
import io.swagger.v3.parser.core.models.SwaggerParseResult;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URI;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.*;
public class Swagger3Parser<T> implements ImportParser<T> {
public class Swagger3Parser<T> implements ImportParser<ApiDefinitionImport> {
protected String projectId;
private Components components;
public static final String PATH = "path";
public static final String FORM_DATA = "formData";
public static final String FILE = "file";
public static final String HEADER = "header";
public static final String BODY = "body";
public static final String COOKIE = "cookie";
public static final String QUERY = "query";
@Override
public T parse(InputStream source, ImportRequest request) throws Exception {
public ApiDefinitionImport parse(InputStream source, ImportRequest request) throws Exception {
LogUtils.info("Swagger3Parser parse");
String apiTestStr = getApiTestStr(source);
List<AuthorizationValue> auths = setAuths(request);
SwaggerParseResult result = null;
if (StringUtils.isNotBlank(request.getSwaggerUrl())) {
result = new OpenAPIParser().readLocation(request.getSwaggerUrl(), auths, null);
if (result == null || result.getOpenAPI() == null || !result.getOpenAPI().getOpenapi().startsWith("3.0") || result.isOpenapi31()) {
throw new MSException(Translator.get("swagger_parse_error_with_auth"));
}
} else {
result = new OpenAPIParser().readContents(apiTestStr, null, null);
if (result == null || result.getOpenAPI() == null || !result.getOpenAPI().getOpenapi().startsWith("3.0") || result.isOpenapi31()) {
throw new MSException(Translator.get("swagger_parse_error"));
}
}
ApiDefinitionImport apiDefinitionImport = new ApiDefinitionImport();
OpenAPI openAPI = result.getOpenAPI();
apiDefinitionImport.setData(parseRequests(openAPI, request));
return apiDefinitionImport;
}
// todo: 检查swagger文件版本
private List<AuthorizationValue> setAuths(ImportRequest request) {
List<AuthorizationValue> auths = new ArrayList<>();
// TODO 如果有 BaseAuth 参数base64 编码后转换成 headers
// todo检查文件的合规性
// 设置 headers
if (!CollectionUtils.isEmpty(request.getHeaders())) {
for (Header keyValue : request.getHeaders()) {
// 当有 key 时才进行设置
if (keyValue.getValue() != null) {
AuthorizationValue authorizationValue = new AuthorizationValue();
authorizationValue.setType("header");
authorizationValue.setKeyName(keyValue.getKey());
authorizationValue.setValue(String.valueOf(keyValue.getValue()));
authorizationValue.setUrlMatcher((url) -> true);
auths.add(authorizationValue);
}
}
}
// 设置 query 参数
if (!CollectionUtils.isEmpty(request.getArguments())) {
StringBuilder pathBuilder = new StringBuilder();
pathBuilder.append(request.getSwaggerUrl());
if (StringUtils.isNotBlank(request.getSwaggerUrl()) && !request.getSwaggerUrl().contains("?")) {
pathBuilder.append("?");
}
for (QueryParam keyValue : request.getArguments()) {
if (StringUtils.isNotBlank(keyValue.getKey())) {
AuthorizationValue authorizationValue = new AuthorizationValue();
authorizationValue.setType("query");
authorizationValue.setKeyName(keyValue.getKey());
try {
authorizationValue.setValue(URLEncoder.encode(String.valueOf(keyValue.getValue()), StandardCharsets.UTF_8));
} catch (Exception e) {
LogUtils.info("swagger3 url encode error: " + e);
}
pathBuilder.append(keyValue.getKey()).append("=").append(authorizationValue.getValue()).append("&");
}
}
request.setSwaggerUrl(pathBuilder.substring(0, pathBuilder.length() - 1));
}
return CollectionUtils.size(auths) == 0 ? null : auths;
}
// todo解析文件pojo
protected String getApiTestStr(InputStream source) {
StringBuilder testStr = null;
try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(source, StandardCharsets.UTF_8))) {
testStr = new StringBuilder();
String inputStr;
while ((inputStr = bufferedReader.readLine()) != null) {
testStr.append(inputStr);
}
source.close();
} catch (Exception e) {
LogUtils.error(e.getMessage(), e);
throw new MSException(e.getMessage());
}
return StringUtils.isNotBlank(testStr) ? testStr.toString() : StringUtils.EMPTY;
}
private List<ApiDefinitionImportDTO> parseRequests(OpenAPI openAPI, ImportRequest importRequest) {
Paths paths = openAPI.getPaths();
Set<String> pathNames = paths.keySet();
this.components = openAPI.getComponents();
List<ApiDefinitionImportDTO> results = new ArrayList<>();
for (String pathName : pathNames) {
PathItem pathItem = paths.get(pathName);
Map<String, Operation> operationsMap = new HashMap<>();
operationsMap.put(HttpMethod.GET.name(), pathItem.getGet());
operationsMap.put(HttpMethod.POST.name(), pathItem.getPost());
operationsMap.put(HttpMethod.DELETE.name(), pathItem.getDelete());
operationsMap.put(HttpMethod.PUT.name(), pathItem.getPut());
operationsMap.put(HttpMethod.PATCH.name(), pathItem.getPatch());
operationsMap.put(HttpMethod.HEAD.name(), pathItem.getHead());
operationsMap.put(HttpMethod.OPTIONS.name(), pathItem.getOptions());
operationsMap.put(HttpMethod.TRACE.name(), pathItem.getTrace());
for (String method : operationsMap.keySet()) {
Operation operation = operationsMap.get(method);
if (operation != null) {
//构建基本请求
ApiDefinitionImportDTO apiDefinitionDTO = buildApiDefinition(operation, pathName, method, importRequest);
//构建请求参数
MsHTTPElement request = buildRequest(apiDefinitionDTO.getName(), pathName, method);
parseParameters(operation, request);
parseParameters(pathItem, request);
//构建请求体
parseRequestBody(operation.getRequestBody(), request.getBody());
apiDefinitionDTO.setRequest(request);
//解析请求内容
parseResponse(operation.getResponses(), apiDefinitionDTO.getResponse());
results.add(apiDefinitionDTO);
}
}
}
return results;
}
private void parseRequestBody(RequestBody requestBody, Body body) {
if (requestBody != null) {
Content content = requestBody.getContent();
if (content != null) {
content.forEach((key, value) -> {
setBodyData(key, value, body);
});
}
} else {
body.setBodyType(Body.BodyType.NONE.name());
body.setNoneBody(new NoneBody());
}
}
private void parseWWWFormBody(JsonSchemaItem item, Body body) {
WWWFormBody wwwFormBody = new WWWFormBody();
List<String> required = item.getRequired();
List<FormDataKV> formDataKVS = new ArrayList<>();
item.getProperties().forEach((key, value) -> {
if (value != null && !StringUtils.equals(PropertyConstant.OBJECT, value.getType())) {
FormDataKV formDataKV = new FormDataKV();
formDataKV.setKey(key);
formDataKV.setValue(String.valueOf(value.getExample()));
formDataKV.setRequired(CollectionUtils.isNotEmpty(required) && required.contains(key));
formDataKV.setDescription(value.getDescription());
formDataKV.setParamType(value.getType());
formDataKV.setMinLength(value.getMinLength());
formDataKV.setMaxLength(value.getMaxLength());
formDataKVS.add(formDataKV);
}
});
wwwFormBody.setFromValues(formDataKVS);
body.setWwwFormBody(wwwFormBody);
}
private void parseResponse(ApiResponses responseBody, List<HttpResponse> response) {
if (responseBody != null) {
responseBody.forEach((key, value) -> {
HttpResponse httpResponse = new HttpResponse();
//TODO headers
httpResponse.setStatusCode(key);
Body body = new Body();
Map<String, io.swagger.v3.oas.models.headers.Header> headers = value.getHeaders();
if (MapUtils.isNotEmpty(headers)) {
List<Header> headerList = new ArrayList<>();
headers.forEach((k, v) -> {
Header header = new Header();
header.setKey(k);
header.setValue(String.valueOf(v.getExample()));
header.setDescription(getDefaultStringValue(v.getDescription()));
headerList.add(header);
});
httpResponse.setHeaders(headerList);
}
if (value.getContent() != null) {
value.getContent().forEach((k, v) -> {
setBodyData(k, v, body);
});
} else {
body.setBodyType(Body.BodyType.NONE.name());
}
httpResponse.setBody(body);
response.add(httpResponse);
});
}
}
private void setBodyData(String k, io.swagger.v3.oas.models.media.MediaType value, Body body) {
//TODO body 默认如果json格式
JsonSchemaItem jsonSchemaItem = parseSchema(value.getSchema());
switch (k) {
case MediaType.APPLICATION_JSON_VALUE, MediaType.ALL_VALUE -> {
body.setBodyType(Body.BodyType.JSON.name());
JsonBody jsonBody = new JsonBody();
jsonBody.setJsonSchema(jsonSchemaItem);
jsonBody.setEnableJsonSchema(true);
if (ObjectUtils.isNotEmpty(value.getExample())) {
jsonBody.setJsonValue(ApiDataUtils.toJSONString(value.getExample()));
}
body.setJsonBody(jsonBody);
}
case MediaType.APPLICATION_XML_VALUE -> {
if (StringUtils.isBlank(body.getBodyType())) {
body.setBodyType(Body.BodyType.XML.name());
}
XmlBody xml = new XmlBody();
//xml.setValue(XMLUtils.jsonToXmlStr(jsonValue));
body.setXmlBody(xml);
}
case MediaType.APPLICATION_FORM_URLENCODED_VALUE -> {
if (StringUtils.isBlank(body.getBodyType())) {
body.setBodyType(Body.BodyType.WWW_FORM.name());
}
parseWWWFormBody(jsonSchemaItem, body);
}
case MediaType.MULTIPART_FORM_DATA_VALUE -> {
if (StringUtils.isBlank(body.getBodyType())) {
body.setBodyType(Body.BodyType.FORM_DATA.name());
}
}
case MediaType.APPLICATION_OCTET_STREAM_VALUE -> {
if (StringUtils.isBlank(body.getBodyType())) {
body.setBodyType(Body.BodyType.BINARY.name());
}
}
case MediaType.TEXT_PLAIN_VALUE -> {
if (StringUtils.isBlank(body.getBodyType())) {
body.setBodyType(Body.BodyType.RAW.name());
}
RawBody rawBody = new RawBody();
body.setRawBody(rawBody);
}
default -> body.setBodyType(Body.BodyType.NONE.name());
}
}
private ApiDefinitionImportDTO buildApiDefinition(Operation operation, String path, String
method, ImportRequest importRequest) {
String name;
if (StringUtils.isNotBlank(operation.getSummary())) {
name = operation.getSummary();
} else if (StringUtils.isNotBlank(operation.getOperationId())) {
name = operation.getOperationId();
} else {
name = path;
}
ApiDefinitionImportDTO apiDefinition = new ApiDefinitionImportDTO();
apiDefinition.setName(name);
apiDefinition.setPath(formatPath(path));
apiDefinition.setProtocol("HTTP");
apiDefinition.setMethod(method);
apiDefinition.setProjectId(this.projectId);
if (StringUtils.equalsIgnoreCase("schedule", importRequest.getType())) {
apiDefinition.setCreateUser(importRequest.getUserId());
} else {
apiDefinition.setCreateUser(SessionUtils.getUserId());
}
apiDefinition.setModulePath(CollectionUtils.isNotEmpty(operation.getTags()) ? StringUtils.join("/", operation.getTags().get(0)) : StringUtils.EMPTY);
apiDefinition.setResponse(new ArrayList<>());
return apiDefinition;
}
protected MsHTTPElement buildRequest(String name, String path, String method) {
MsHTTPElement request = new MsHTTPElement();
request.setName(name);
// 路径去掉域名/IP 地址保留方法名称及参数
request.setPath(formatPath(path));
request.setMethod(method);
request.setHeaders(new ArrayList<>());
request.setQuery(new ArrayList<>());
request.setRest(new ArrayList<>());
request.setBody(new Body());
MsHTTPConfig httpConfig = new MsHTTPConfig();
httpConfig.setConnectTimeout(60000L);
httpConfig.setResponseTimeout(60000L);
request.setOtherConfig(httpConfig);
request.setAuthConfig(new NoAuth());
return request;
}
private void parseParameters(Operation operation, MsHTTPElement request) {
List<Parameter> parameters = operation.getParameters();
if (CollectionUtils.isEmpty(parameters)) {
return;
}
parameters.forEach(parameter -> {
if (parameter instanceof QueryParameter queryParameter) {
parseQueryParameters(queryParameter, request.getQuery());
} else if (parameter instanceof PathParameter pathParameter) {
parsePathParameters(pathParameter, request.getRest());
} else if (parameter instanceof HeaderParameter headerParameter) {
parseHeaderParameters(headerParameter, request.getHeaders());
} else if (parameter instanceof CookieParameter cookieParameter) {
parseCookieParameters(cookieParameter, request.getHeaders());
}
});
}
private void parseParameters(PathItem path, MsHTTPElement request) {
if (path.getParameters() == null) {
return;
}
List<Parameter> parameters = path.getParameters();
// 处理特殊格式 rest参数是和请求平级的情况
for (Parameter parameter : parameters) {
if (StringUtils.isNotBlank(parameter.getIn())) {
switch (parameter.getIn()) {
case PATH -> parsePathParameters((PathParameter) parameter, request.getRest());
case QUERY -> parseQueryParameters((QueryParameter) parameter, request.getQuery());
case HEADER -> parseHeaderParameters((HeaderParameter) parameter, request.getHeaders());
case COOKIE -> parseCookieParameters((CookieParameter) parameter, request.getHeaders());
default -> {
return;
}
}
}
}
}
private void parseQueryParameters(QueryParameter queryParameter, List<QueryParam> arguments) {
QueryParam queryParam = new QueryParam();
queryParam.setKey(getDefaultStringValue(queryParameter.getName()));
queryParam.setRequired(queryParameter.getRequired());
queryParam.setDescription(getDefaultStringValue(queryParameter.getDescription()));
if (queryParameter.getSchema() != null) {
queryParam.setParamType(queryParameter.getSchema().getType());
queryParam.setValue(getDefaultStringValue(String.valueOf(queryParameter.getSchema().getExample())));
queryParam.setMinLength(queryParameter.getSchema().getMinLength());
queryParam.setMaxLength(queryParameter.getSchema().getMaxLength());
}
arguments.add(queryParam);
}
private void parseCookieParameters(CookieParameter cookieParameter, List<Header> headers) {
Header headerParams = new Header();
headerParams.setKey(getDefaultStringValue(cookieParameter.getName()));
headerParams.setDescription(getDefaultStringValue(cookieParameter.getDescription()));
if (cookieParameter.getSchema() != null) {
headerParams.setValue(getDefaultStringValue(String.valueOf(cookieParameter.getSchema().getExample())));
}
headers.add(headerParams);
}
private void parseHeaderParameters(HeaderParameter headerParameter, List<Header> headers) {
Header headerParams = new Header();
headerParams.setKey(getDefaultStringValue(headerParameter.getName()));
headerParams.setDescription(getDefaultStringValue(headerParameter.getDescription()));
if (headerParameter.getSchema() != null) {
headerParams.setValue(getDefaultStringValue(String.valueOf(headerParameter.getSchema().getExample())));
}
headers.add(headerParams);
}
private void parsePathParameters(PathParameter parameter, List<RestParam> rest) {
RestParam restParam = new RestParam();
restParam.setKey(getDefaultStringValue(parameter.getName()));
restParam.setRequired(parameter.getRequired());
restParam.setDescription(getDefaultStringValue(parameter.getDescription()));
if (parameter.getSchema() != null) {
restParam.setParamType(parameter.getSchema().getType());
restParam.setValue(getDefaultStringValue(String.valueOf(parameter.getSchema().getExample())));
restParam.setMinLength(parameter.getSchema().getMinLength());
restParam.setMaxLength(parameter.getSchema().getMaxLength());
}
rest.add(restParam);
}
private String getDefaultStringValue(String val) {
return StringUtils.isBlank(val) ? StringUtils.EMPTY : val;
}
private Schema getModelByRef(String ref) {
if (StringUtils.isBlank(ref)) {
return null;
}
if (ref.split("/").length > 3) {
ref = ref.replace("#/components/schemas/", StringUtils.EMPTY);
}
if (this.components.getSchemas() != null) return this.components.getSchemas().get(ref);
return null;
}
private JsonSchemaItem parseSchema(Schema schema) {
if (schema != null) {
String refName = schema.get$ref();
Schema modelByRef = null;
if (StringUtils.isNotBlank(refName)) {
modelByRef = getModelByRef(refName);
} else {
modelByRef = schema;
}
if (modelByRef != null) {
if (modelByRef instanceof ArraySchema arraySchema) {
return parseArraySchema(arraySchema.getItems());
} else if (modelByRef instanceof ObjectSchema objectSchema) {
return parseObject(objectSchema);
}
}
}
return null;
}
private JsonSchemaItem parseMapObject(MapSchema mapSchema) {
JsonSchemaItem jsonSchemaItem = new JsonSchemaItem();
jsonSchemaItem.setType(PropertyConstant.OBJECT);
jsonSchemaItem.setRequired(mapSchema.getRequired());
jsonSchemaItem.setDescription(mapSchema.getDescription());
Object value = mapSchema.getAdditionalProperties();
Map<String, JsonSchemaItem> jsonSchemaProperties = new LinkedHashMap<>();
if (ObjectUtils.isEmpty(value)) {
return jsonSchemaItem;
}
JsonSchemaItem item = new JsonSchemaItem();
if (value instanceof IntegerSchema integerSchema) {
item = parseInteger(integerSchema);
} else if (value instanceof StringSchema stringSchema) {
item = parseString(stringSchema);
} else if (value instanceof NumberSchema numberSchema) {
item = parseNumber(numberSchema);
} else if (value instanceof BooleanSchema booleanSchema) {
item = parseBoolean(booleanSchema);
} else if (value instanceof ArraySchema arraySchema) {
item = parseArraySchema(arraySchema.getItems());
} else if (value instanceof ObjectSchema objectSchemaItem) {
item = parseObject(objectSchemaItem);
}
jsonSchemaProperties.put(StringUtils.EMPTY, item);
jsonSchemaItem.setProperties(jsonSchemaProperties);
return jsonSchemaItem;
}
private JsonSchemaItem parseObject(ObjectSchema objectSchema) {
JsonSchemaItem jsonSchemaItem = new JsonSchemaItem();
jsonSchemaItem.setType(PropertyConstant.OBJECT);
jsonSchemaItem.setRequired(objectSchema.getRequired());
jsonSchemaItem.setDescription(objectSchema.getDescription());
Map<String, Schema> properties = objectSchema.getProperties();
Map<String, JsonSchemaItem> jsonSchemaProperties = new LinkedHashMap<>();
if (MapUtils.isNotEmpty(properties)) {
properties.forEach((key, value) -> {
JsonSchemaItem item = new JsonSchemaItem();
if (value instanceof IntegerSchema integerSchema) {
item = parseInteger(integerSchema);
} else if (value instanceof StringSchema stringSchema) {
item = parseString(stringSchema);
} else if (value instanceof NumberSchema numberSchema) {
item = parseNumber(numberSchema);
} else if (value instanceof BooleanSchema booleanSchema) {
item = parseBoolean(booleanSchema);
} else if (value instanceof ArraySchema arraySchema) {
if (!isRef(arraySchema.getItems(), 0)) {
JsonSchemaItem arrayItem = new JsonSchemaItem();
arrayItem.setType(PropertyConstant.ARRAY);
arrayItem.setItems(new JsonSchemaItem());
item = arrayItem;
jsonSchemaProperties.put(key, item);
return;
}
item = parseArraySchema(arraySchema.getItems());
} else if (value instanceof ObjectSchema objectSchemaItem) {
if (!isRef(objectSchemaItem, 0)) {
JsonSchemaItem objectItem = new JsonSchemaItem();
objectItem.setType(PropertyConstant.OBJECT);
objectItem.setProperties(new LinkedHashMap<>());
item = objectItem;
jsonSchemaProperties.put(key, item);
return;
}
item = parseObject(objectSchemaItem);
} else if (StringUtils.equals(value.getType(), "null")) {
item = parseNull();
} else if (value instanceof MapSchema mapSchema) {
item = parseMapObject(mapSchema);
} else if (value instanceof Schema<?> items) {
/*if (StringUtils.isNotBlank(items.get$ref()) && StringUtils.equals(items.get$ref(), ref)) {
return;
}*/
item = parseSchema(items);
}
jsonSchemaProperties.put(key, item);
});
}
jsonSchemaItem.setProperties(jsonSchemaProperties);
return jsonSchemaItem;
}
//判断对象是否存在一直引用
private boolean isRef(Schema schema, int level) {
if (schema == null) {
return false;
}
if (level > 20) {
return true;
}
if (StringUtils.isNotBlank(schema.get$ref())) {
return false;
}
if (schema instanceof ArraySchema arraySchema) {
return isRef(arraySchema.getItems(), level + 1);
}
if (schema instanceof ObjectSchema objectSchema) {
Map<String, Schema> properties = objectSchema.getProperties();
if (MapUtils.isNotEmpty(properties)) {
int finalLevel = level;
properties.forEach((key, value) -> {
if (value instanceof ArraySchema arraySchemaItem) {
isRef(arraySchemaItem.getItems(), finalLevel + 1);
}
if (value instanceof ObjectSchema objectSchemaItem) {
isRef(objectSchemaItem, finalLevel + 1);
}
});
}
}
return false;
}
private JsonSchemaItem parseString(StringSchema stringSchema) {
JsonSchemaItem jsonSchemaString = new JsonSchemaItem();
jsonSchemaString.setType(PropertyConstant.STRING);
jsonSchemaString.setFormat(StringUtils.isNotBlank(stringSchema.getFormat()) ? stringSchema.getFormat() : StringUtils.EMPTY);
jsonSchemaString.setDescription(getDefaultStringValue(stringSchema.getDescription()));
jsonSchemaString.setExample(stringSchema.getExample());
if (stringSchema.getMaxLength() != null) {
jsonSchemaString.setMaxLength(stringSchema.getMaxLength());
}
if (stringSchema.getMinLength() != null) {
jsonSchemaString.setMinLength(stringSchema.getMinLength());
}
jsonSchemaString.setPattern(stringSchema.getPattern());
jsonSchemaString.setEnumString(stringSchema.getEnum());
if (stringSchema.getExample() == null && CollectionUtils.isNotEmpty(stringSchema.getEnum())) {
jsonSchemaString.setExample(stringSchema.getEnum().get(0));
}
return jsonSchemaString;
}
private JsonSchemaItem parseInteger(IntegerSchema integerSchema) {
JsonSchemaItem jsonSchemaInteger = new JsonSchemaItem();
jsonSchemaInteger.setType(PropertyConstant.INTEGER);
jsonSchemaInteger.setFormat(StringUtils.isNotBlank(integerSchema.getFormat()) ? integerSchema.getFormat() : StringUtils.EMPTY);
jsonSchemaInteger.setDescription(StringUtils.isNotBlank(integerSchema.getDescription()) ? integerSchema.getDescription() : StringUtils.EMPTY);
jsonSchemaInteger.setExample(integerSchema.getExample());
jsonSchemaInteger.setMaximum(integerSchema.getMaximum());
jsonSchemaInteger.setMinimum(integerSchema.getMinimum());
jsonSchemaInteger.setEnumInteger(integerSchema.getEnum());
return jsonSchemaInteger;
}
private JsonSchemaItem parseNumber(NumberSchema numberSchema) {
JsonSchemaItem jsonSchemaNumber = new JsonSchemaItem();
jsonSchemaNumber.setType(PropertyConstant.NUMBER);
jsonSchemaNumber.setDescription(StringUtils.isNotBlank(numberSchema.getDescription()) ? numberSchema.getDescription() : StringUtils.EMPTY);
jsonSchemaNumber.setExample(numberSchema.getExample());
jsonSchemaNumber.setEnumNumber(numberSchema.getEnum());
return jsonSchemaNumber;
}
private JsonSchemaItem parseBoolean(BooleanSchema booleanSchema) {
JsonSchemaItem jsonSchemaBoolean = new JsonSchemaItem();
jsonSchemaBoolean.setType(PropertyConstant.BOOLEAN);
jsonSchemaBoolean.setDescription(getDefaultStringValue(booleanSchema.getDescription()));
jsonSchemaBoolean.setExample(booleanSchema.getExample());
return jsonSchemaBoolean;
}
private JsonSchemaItem parseNull() {
JsonSchemaItem jsonSchemaNull = new JsonSchemaItem();
jsonSchemaNull.setType(PropertyConstant.NULL);
return jsonSchemaNull;
}
private JsonSchemaItem parseArraySchema(Schema<?> items) {
JsonSchemaItem jsonSchemaArray = new JsonSchemaItem();
jsonSchemaArray.setType(PropertyConstant.ARRAY);
Schema itemsSchema = null;
if (StringUtils.isNotBlank(items.get$ref())) {
itemsSchema = getModelByRef(items.get$ref());
} else {
itemsSchema = items;
}
if (itemsSchema instanceof IntegerSchema integerSchema) {
jsonSchemaArray.setItems(parseInteger(integerSchema));
} else if (itemsSchema instanceof StringSchema stringSchema) {
jsonSchemaArray.setItems(parseString(stringSchema));
} else if (itemsSchema instanceof NumberSchema numberSchema) {
jsonSchemaArray.setItems(parseNumber(numberSchema));
} else if (itemsSchema instanceof BooleanSchema booleanSchema) {
jsonSchemaArray.setItems(parseBoolean(booleanSchema));
} else if (itemsSchema instanceof ArraySchema arraySchema) {
jsonSchemaArray.setItems(parseArraySchema(arraySchema.getItems()));
} else if (itemsSchema instanceof ObjectSchema objectSchema) {
jsonSchemaArray.setItems(parseObject(objectSchema));
} else if (ObjectUtils.isNotEmpty(itemsSchema) && StringUtils.equals("null", itemsSchema.getType())) {
jsonSchemaArray.setItems(parseNull());
}
return jsonSchemaArray;
}
private String formatPath(String url) {
try {
URI urlObject = new URI(url);
String path = StringUtils.isBlank(urlObject.getPath()) ? "/" : urlObject.getPath();
StringBuilder pathBuffer = new StringBuilder(path);
if (StringUtils.isNotEmpty(urlObject.getQuery())) {
pathBuffer.append("?").append(urlObject.getQuery());
}
return pathBuffer.toString();
} catch (Exception ex) {
return url;
}
}
}

View File

@ -38,6 +38,7 @@ public class MsJsonBodyConverter extends MsBodyConverter<JsonBody> {
/**
* 将json中的 @xxx 转换成 ${__Mock(@xxx)}
*
* @param jsonStr
* @return
*/

View File

@ -0,0 +1,647 @@
package io.metersphere.api.service.definition;
import io.metersphere.api.domain.ApiDefinition;
import io.metersphere.api.domain.ApiDefinitionBlob;
import io.metersphere.api.domain.ApiDefinitionBlobExample;
import io.metersphere.api.domain.ApiDefinitionModule;
import io.metersphere.api.dto.definition.ApiDefinitionDTO;
import io.metersphere.api.dto.definition.ApiDefinitionPageRequest;
import io.metersphere.api.dto.definition.ApiModuleRequest;
import io.metersphere.api.dto.definition.importdto.ApiDeatlWithData;
import io.metersphere.api.dto.definition.importdto.ApiDeatlWithDataUpdate;
import io.metersphere.api.dto.definition.importdto.ApiDefinitionImport;
import io.metersphere.api.dto.definition.importdto.ApiDefinitionImportDTO;
import io.metersphere.api.dto.request.ImportRequest;
import io.metersphere.api.dto.request.http.Header;
import io.metersphere.api.dto.request.http.MsHTTPElement;
import io.metersphere.api.dto.request.http.QueryParam;
import io.metersphere.api.dto.request.http.RestParam;
import io.metersphere.api.dto.request.http.body.*;
import io.metersphere.api.dto.schema.JsonSchemaItem;
import io.metersphere.api.enums.PropertyConstant;
import io.metersphere.api.mapper.*;
import io.metersphere.api.utils.ApiDataUtils;
import io.metersphere.project.domain.Project;
import io.metersphere.project.mapper.ExtBaseProjectVersionMapper;
import io.metersphere.project.mapper.ProjectApplicationMapper;
import io.metersphere.project.mapper.ProjectMapper;
import io.metersphere.sdk.constants.ApplicationNumScope;
import io.metersphere.sdk.constants.HttpMethodConstants;
import io.metersphere.sdk.constants.ModuleConstants;
import io.metersphere.sdk.exception.MSException;
import io.metersphere.sdk.util.BeanUtils;
import io.metersphere.sdk.util.JSON;
import io.metersphere.sdk.util.Translator;
import io.metersphere.system.dto.sdk.BaseTreeNode;
import io.metersphere.system.log.constants.OperationLogModule;
import io.metersphere.system.log.constants.OperationLogType;
import io.metersphere.system.log.dto.LogDTO;
import io.metersphere.system.log.service.OperationLogService;
import io.metersphere.system.uid.IDGenerator;
import io.metersphere.system.uid.NumGenerator;
import jakarta.annotation.Resource;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
import java.util.stream.Collectors;
@Service
public class ApiDefinitionImportUtilService {
private static final String UNPLANNED_API = "api_unplanned_request";
private static final String MODULE = "modulePath";
public static final Long ORDER_STEP = 5000L;
private final ThreadLocal<Long> currentApiOrder = new ThreadLocal<>();
private final ThreadLocal<Long> currentApiCaseOrder = new ThreadLocal<>();
private final ThreadLocal<Long> currentModuleOrder = new ThreadLocal<>();
@Resource
private SqlSessionFactory sqlSessionFactory;
@Resource
private ExtBaseProjectVersionMapper extBaseProjectVersionMapper;
@Resource
private ProjectApplicationMapper projectApplicationMapper;
@Resource
private ExtApiDefinitionModuleMapper extApiDefinitionModuleMapper;
@Resource
private ExtApiDefinitionMapper extApiDefinitionMapper;
@Resource
private ApiDefinitionBlobMapper apiDefinitionBlobMapper;
@Resource
private ApiTestCaseService apiTestCaseService;
@Resource
private ApiDefinitionModuleService apiDefinitionModuleService;
@Resource
private ProjectMapper projectMapper;
@Resource
private OperationLogService operationLogService;
public void checkFileSuffixName(ImportRequest request, String suffixName) {
if ("jmx".equalsIgnoreCase(suffixName)) {
if (!"JMeter".equalsIgnoreCase(request.getPlatform())) {
throw new MSException(Translator.get("file_format_does_not_meet_requirements"));
}
}
if ("har".equalsIgnoreCase(suffixName)) {
if (!"Har".equalsIgnoreCase(request.getPlatform())) {
throw new MSException(Translator.get("file_format_does_not_meet_requirements"));
}
}
if ("json".equalsIgnoreCase(suffixName)) {
if ("Har".equalsIgnoreCase(request.getPlatform()) || "Jmeter".equalsIgnoreCase(request.getPlatform())) {
throw new MSException(Translator.get("file_format_does_not_meet_requirements"));
}
}
}
public void importApi(ImportRequest request, ApiDefinitionImport apiImport) {
String defaultVersion = extBaseProjectVersionMapper.getDefaultVersion(request.getProjectId());
request.setDefaultVersion(defaultVersion);
if (request.getVersionId() == null) {
request.setVersionId(defaultVersion);
}
List<ApiDefinitionImportDTO> initData = apiImport.getData();
//TODO 查询项目菜单参数
/*ProjectApplicationExample applicationExample = new ProjectApplicationExample();
applicationExample.createCriteria().andProjectIdEqualTo(request.getProjectId()).andTypeEqualTo("API_URL_REPEATABLE");
List<ProjectApplication> projectApplications = projectApplicationMapper.selectByExample(applicationExample);
if (CollectionUtils.isNotEmpty(projectApplications)) {
String typeValue = projectApplications.get(0).getTypeValue();
}*/
//过滤(一次只导入一个协议)
List<ApiDefinitionImportDTO> filterData = initData.stream().filter(t -> t.getProtocol().equals(request.getProtocol())).collect(Collectors.toList());
if (filterData.isEmpty()) {
return;
}
//处理数据判断数据是否重复
dealWithData(request, filterData);
}
private void dealWithData(ImportRequest request, List<ApiDefinitionImportDTO> importData) {
//查询数据库中所有的数据 用于判断是否重复
ApiDefinitionPageRequest pageRequest = new ApiDefinitionPageRequest();
pageRequest.setProjectId(request.getProjectId());
pageRequest.setProtocol(request.getProtocol());
//TODO 如果是有版本的话 需要加上版本的判断
List<ApiDefinitionImportDTO> apiLists = extApiDefinitionMapper.importList(pageRequest);
List<BaseTreeNode> apiModules = this.buildTreeData(request.getProjectId(), request.getProtocol());
//将apiModules转换成新的map 要求key是attachInfo中的modulePath 使用stream实现
Map<String, BaseTreeNode> modulePathMap = apiModules.stream().collect(Collectors.toMap(t -> t.getAttachInfo().get(MODULE), t -> t));
Map<String, BaseTreeNode> idModuleMap = apiModules.stream().collect(Collectors.toMap(BaseTreeNode::getId, apiModuleDTO -> apiModuleDTO));
//如果导入的时候选择了模块需要把所有的导入数据的模块路径前面拼接上选择的模块路径
if (StringUtils.isNotBlank(request.getModuleId())) {
BaseTreeNode baseTreeNode = idModuleMap.get(request.getModuleId());
String modulePath = baseTreeNode.getAttachInfo().get(MODULE);
importData.forEach(t -> {
t.setModulePath(modulePath + t.getModulePath());
});
}
//去掉apiLists中不存在的模块数据
apiLists = apiLists.stream().filter(t -> modulePathMap.containsKey(t.getModulePath())).toList();
apiLists.forEach(t -> {
t.setModulePath(idModuleMap.get(t.getModuleId()) != null ? idModuleMap.get(t.getModuleId()).getAttachInfo().get(MODULE) : StringUtils.EMPTY);
});
ApiDeatlWithData apiDeatlWithData = new ApiDeatlWithData();
//判断数据是否是唯一的
checkApiDataOnly(request, importData, apiLists, apiDeatlWithData);
ApiDeatlWithDataUpdate apiDeatlWithDataUpdate = new ApiDeatlWithDataUpdate();
getNeedUpdateData(request, apiDeatlWithData, apiDeatlWithDataUpdate);
//数据入库
insertData(modulePathMap, idModuleMap, apiDeatlWithDataUpdate, request);
}
public Long getNextOrder(String projectId) {
Long pos = extApiDefinitionMapper.getPos(projectId);
return (pos == null ? 0 : pos) + ORDER_STEP;
}
public Long getImportNextOrder(String projectId) {
Long order = currentApiOrder.get();
if (order == null) {
order = getNextOrder(projectId);
}
order = order + ORDER_STEP;
currentApiOrder.set(order);
return order;
}
public Long getImportNextCaseOrder(String projectId) {
Long order = currentApiCaseOrder.get();
if (order == null) {
order = apiTestCaseService.getNextOrder(projectId);
}
order = order + ORDER_STEP;
currentApiCaseOrder.set(order);
return order;
}
public Long getImportNextModuleOrder(String projectId) {
Long order = currentModuleOrder.get();
if (order == null) {
order = apiDefinitionModuleService.getNextOrder(projectId);
}
order = order + ORDER_STEP;
currentModuleOrder.set(order);
return order;
}
@Transactional(rollbackFor = Exception.class)
public void insertData(Map<String, BaseTreeNode> modulePathMap,
Map<String, BaseTreeNode> idModuleMap,
ApiDeatlWithDataUpdate apiDeatlWithDataUpdate,
ImportRequest request) {
//先判断是否需要新增模块
List<ApiDefinitionImportDTO> addModuleData = apiDeatlWithDataUpdate.getAddModuleData();
List<ApiDefinitionImportDTO> updateModuleData = apiDeatlWithDataUpdate.getUpdateModuleData();
//取addModuleData的模块放到set中 生成一个新的set
Set<String> moduleSet = addModuleData.stream().map(ApiDefinitionImportDTO::getModulePath).collect(Collectors.toSet());
//取updateModuleData的模块放到set中 生成一个新的set
Set<String> updateModuleSet = updateModuleData.stream().map(ApiDefinitionImportDTO::getModulePath).collect(Collectors.toSet());
moduleSet.addAll(updateModuleSet);
//将modulePathMap的key转成set
Set<String> modulePathSet = modulePathMap.keySet();
//取modulePathSet中不存在的
Set<String> differenceSet = moduleSet.stream().filter(t -> !modulePathSet.contains(t)).collect(Collectors.toSet());
//不存在的需要新增
List<BaseTreeNode> addModuleList = new ArrayList<>();
currentApiCaseOrder.remove();
currentApiOrder.remove();
currentModuleOrder.remove();
differenceSet.forEach(item -> {
//解析modulePath 格式为/a/b/c
String[] split = item.split("/");
//一层一层的创建
for (int i = 0; i < split.length; i++) {
String path = StringUtils.join(split, "/", 0, i + 1);
BaseTreeNode baseTreeNode = modulePathMap.get(path);
if (baseTreeNode == null) {
//创建模块
BaseTreeNode module = new BaseTreeNode();
module.setId(IDGenerator.nextStr());
module.setName(split[i]);
module.setParentId(i == 0 ? ModuleConstants.ROOT_NODE_PARENT_ID : modulePathMap.get(StringUtils.join(split, "/", 0, i)).getId());
addModuleList.add(module);
modulePathMap.put(path, module);
idModuleMap.put(module.getId(), module);
}
}
});
//创建模块
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
ApiDefinitionModuleMapper moduleMapper = sqlSession.getMapper(ApiDefinitionModuleMapper.class);
ApiDefinitionMapper apiMapper = sqlSession.getMapper(ApiDefinitionMapper.class);
ApiDefinitionBlobMapper apiBlobMapper = sqlSession.getMapper(ApiDefinitionBlobMapper.class);
addModuleList.forEach(t -> {
ApiDefinitionModule module = new ApiDefinitionModule();
module.setId(t.getId());
module.setName(t.getName());
module.setParentId(t.getParentId());
module.setProjectId(request.getProjectId());
module.setCreateUser(request.getUserId());
module.setPos(getImportNextModuleOrder(request.getProjectId()));
module.setCreateTime(System.currentTimeMillis());
module.setUpdateUser(request.getUserId());
module.setUpdateTime(System.currentTimeMillis());
moduleMapper.insertSelective(module);
});
//取出需要更新的数据的id
List<String> updateModuleLists = updateModuleData.stream().map(ApiDefinitionImportDTO::getId).toList();
//更新模块数据
updateModuleData.forEach(t -> {
ApiDefinition apiDefinition = new ApiDefinition();
apiDefinition.setId(t.getId());
apiDefinition.setModuleId(modulePathMap.get(t.getModulePath()).getId());
apiDefinition.setUpdateUser(request.getUserId());
apiDefinition.setUpdateTime(System.currentTimeMillis());
apiMapper.updateByPrimaryKeySelective(apiDefinition);
});
List<LogDTO> operationLogs = new ArrayList<>();
List<ApiDefinitionImportDTO> updateRequestData = apiDeatlWithDataUpdate.getUpdateRequestData();
updateRequestData.forEach(t -> {
if (CollectionUtils.isNotEmpty(updateModuleLists) && updateModuleLists.contains(t.getId())) {
ApiDefinition apiDefinition = new ApiDefinition();
apiDefinition.setId(t.getId());
apiDefinition.setUpdateUser(request.getUserId());
apiDefinition.setUpdateTime(System.currentTimeMillis());
apiMapper.updateByPrimaryKeySelective(apiDefinition);
}
//更新blob数据
ApiDefinitionBlob apiDefinitionBlob = new ApiDefinitionBlob();
apiDefinitionBlob.setId(t.getId());
apiDefinitionBlob.setRequest(JSON.toJSONBytes(t.getRequest()));
apiDefinitionBlob.setResponse(JSON.toJSONBytes(t.getResponse()));
apiBlobMapper.updateByPrimaryKeySelective(apiDefinitionBlob);
});
Map<String, ApiDefinitionImportDTO> logData = apiDeatlWithDataUpdate.getLogData();
Project project = projectMapper.selectByPrimaryKey(request.getProjectId());
if (MapUtils.isNotEmpty(logData)) {
logData.forEach((k, v) -> {
ApiDefinitionDTO apiDefinitionDTO = new ApiDefinitionDTO();
BeanUtils.copyBean(apiDefinitionDTO, v);
LogDTO dto = new LogDTO(
project.getId(),
project.getOrganizationId(),
v.getId(),
request.getUserId(),
OperationLogType.IMPORT.name(),
OperationLogModule.API_DEFINITION,
v.getName());
dto.setHistory(true);
dto.setPath("/api/definition/import");
dto.setMethod(HttpMethodConstants.POST.name());
dto.setOriginalValue(JSON.toJSONBytes(apiDefinitionDTO));
operationLogs.add(dto);
});
}
addModuleData.forEach(t -> {
ApiDefinition apiDefinition = new ApiDefinition();
BeanUtils.copyBean(apiDefinition, t);
apiDefinition.setId(IDGenerator.nextStr());
apiDefinition.setModuleId(modulePathMap.get(t.getModulePath()).getId());
apiDefinition.setProjectId(request.getProjectId());
apiDefinition.setProtocol(request.getProtocol());
apiDefinition.setCreateUser(request.getUserId());
apiDefinition.setPos(getImportNextOrder(request.getProjectId()));
apiDefinition.setCreateTime(System.currentTimeMillis());
apiDefinition.setUpdateUser(request.getUserId());
apiDefinition.setUpdateTime(System.currentTimeMillis());
apiDefinition.setNum(NumGenerator.nextNum(request.getProjectId(), ApplicationNumScope.API_DEFINITION));
apiDefinition.setLatest(true);
apiDefinition.setStatus("Underway");
apiDefinition.setRefId(apiDefinition.getId());
apiDefinition.setVersionId(request.getVersionId());
apiMapper.insertSelective(apiDefinition);
//插入blob数据
ApiDefinitionBlob apiDefinitionBlob = new ApiDefinitionBlob();
apiDefinitionBlob.setId(apiDefinition.getId());
apiDefinitionBlob.setRequest(JSON.toJSONBytes(t.getRequest()));
apiDefinitionBlob.setResponse(JSON.toJSONBytes(t.getResponse()));
apiBlobMapper.insertSelective(apiDefinitionBlob);
ApiDefinitionDTO apiDefinitionDTO = new ApiDefinitionDTO();
BeanUtils.copyBean(apiDefinitionDTO, t);
LogDTO dto = new LogDTO(
project.getId(),
project.getOrganizationId(),
t.getId(),
request.getUserId(),
OperationLogType.IMPORT.name(),
OperationLogModule.API_DEFINITION,
t.getName());
dto.setHistory(true);
dto.setPath("/api/definition/import");
dto.setMethod(HttpMethodConstants.POST.name());
dto.setOriginalValue(JSON.toJSONBytes(apiDefinitionDTO));
});
sqlSession.flushStatements();
SqlSessionUtils.closeSqlSession(sqlSession, sqlSessionFactory);
operationLogService.batchAdd(operationLogs);
}
private void getNeedUpdateData(ImportRequest request, ApiDeatlWithData apiDeatlWithData, ApiDeatlWithDataUpdate apiDeatlWithDataUpdate) {
List<String> sameList = apiDeatlWithData.getSameList();
List<String> differenceList = apiDeatlWithData.getDifferenceList();
Map<String, ApiDefinitionImportDTO> apiDateMap = apiDeatlWithData.getApiDateMap();
Map<String, ApiDefinitionImportDTO> importDataMap = apiDeatlWithData.getImportDataMap();
List<ApiDefinitionImportDTO> updateModuleData = new ArrayList<>();
List<ApiDefinitionImportDTO> updateRequestData = new ArrayList<>();
List<ApiDefinitionImportDTO> addData = new ArrayList<>();
//判断参数是否一样 一样的参数需要判断是否需要覆盖模块 如果需要就要update数据 如果不需要 就直接跳过
if (CollectionUtils.isNotEmpty(sameList) && getFullCoverage(request.getCoverData())) {
//需要覆盖数据的 会判断是否需要覆盖模块
List<ApiDefinitionImportDTO> sameData = sameList.stream().map(apiDateMap::get).toList();
//取所有id为新的list 需要取查询blob的数据
List<String> sameIds = sameData.stream().map(ApiDefinitionImportDTO::getId).toList();
ApiDefinitionBlobExample blobExample = new ApiDefinitionBlobExample();
blobExample.createCriteria().andIdIn(sameIds);
List<ApiDefinitionBlob> apiDefinitionBlobs = apiDefinitionBlobMapper.selectByExampleWithBLOBs(blobExample);
Map<String, ApiDefinitionImportDTO> logMap = new HashMap<>();
Map<String, ApiDefinitionBlob> blobMap = apiDefinitionBlobs.stream().collect(Collectors.toMap(ApiDefinitionBlob::getId, t -> t));
//判断参数是否一样
for (ApiDefinitionImportDTO apiDefinitionDTO : sameData) {
ApiDefinitionImportDTO importDTO = importDataMap.get(apiDefinitionDTO.getMethod() + apiDefinitionDTO.getPath());
ApiDefinitionBlob apiDefinitionBlob = blobMap.get(apiDefinitionDTO.getId());
if (apiDefinitionBlob != null) {
MsHTTPElement dbRequest = ApiDataUtils.parseObject(new String(apiDefinitionBlob.getRequest()), MsHTTPElement.class);
//判断参数是否一样 参数类型有 请求头 请求参数 请求体
boolean isSame = dataIsSame(dbRequest, (MsHTTPElement) importDTO.getRequest());
//判断是否开启模块覆盖
if (getFullCoverage(request.getCoverModule())) {
//判断模块是否一样
if (!StringUtils.equals(apiDefinitionDTO.getModulePath(), importDTO.getModulePath())) {
//不一样的模块需要更
apiDefinitionDTO.setModulePath(apiDefinitionDTO.getModulePath());
updateModuleData.add(apiDefinitionDTO);
logMap.put(apiDefinitionDTO.getId(), importDTO);
}
}
//不相同的数据需要覆盖 所以这里记录id就可以
if (!isSame) {
importDTO.setId(apiDefinitionDTO.getId());
updateRequestData.add(importDTO);
logMap.put(apiDefinitionDTO.getId(), importDTO);
}
}
}
}
//不存在的数据是肯定要插入的 TODO 这里需要判断是否需要创建模块
if (CollectionUtils.isNotEmpty(differenceList)) {
addData = differenceList.stream().map(importDataMap::get).toList();
}
apiDeatlWithDataUpdate.setUpdateModuleData(updateModuleData);
apiDeatlWithDataUpdate.setUpdateRequestData(updateRequestData);
apiDeatlWithDataUpdate.setAddModuleData(addData);
}
private void checkApiDataOnly(ImportRequest request,
List<ApiDefinitionImportDTO> importData,
List<ApiDefinitionImportDTO> apiLists,
ApiDeatlWithData apiDeatlWithData) {
//判断是否是同一接口 需要返回的数据 需要insert的 update的
switch (request.getUniquelyIdentifies()) {
case "Method & Path" -> methodAndPath(importData, apiLists, apiDeatlWithData);
default -> {
}
}
}
public void methodAndPath(List<ApiDefinitionImportDTO> importData,
List<ApiDefinitionImportDTO> lists,
ApiDeatlWithData apiDeatlWithData) {
Map<String, ApiDefinitionImportDTO> apiDateMap = lists.stream().collect(Collectors.toMap(t -> t.getMethod() + t.getPath(), t -> t));
Map<String, ApiDefinitionImportDTO> importDataMap = importData.stream().collect(Collectors.toMap(t -> t.getMethod() + t.getPath(), t -> t));
//判断是否重复
List<String> orgList = apiDateMap.keySet().stream().toList();
List<String> importList = importDataMap.keySet().stream().toList();
//取交集数据
List<String> sameList = importList.stream().filter(orgList::contains).toList();
// 不同接口的数据
List<String> differenceList = importList.stream().filter(t -> !orgList.contains(t)).toList();
apiDeatlWithData.setSameList(sameList);
apiDeatlWithData.setDifferenceList(differenceList);
apiDeatlWithData.setApiDateMap(apiDateMap);
apiDeatlWithData.setImportDataMap(importDataMap);
}
public boolean dataIsSame(MsHTTPElement dbRequest, MsHTTPElement importRequest) {
boolean same = true;
//判断请求头是否一样
List<Header> dbHeaders = dbRequest.getHeaders();
List<Header> importHeaders = importRequest.getHeaders();
if (CollectionUtils.isNotEmpty(dbHeaders) || CollectionUtils.isNotEmpty(importHeaders)) {
List<String> dbHeaderKeys = dbHeaders.stream().map(Header::getKey).toList();
List<String> importHeaderKeys = importHeaders.stream().map(Header::getKey).toList();
if (!paramsIsSame(dbHeaderKeys, importHeaderKeys)) {
return false;
}
}
//判断请求参数是否一样 query rest body
List<QueryParam> dbQuery = dbRequest.getQuery();
List<QueryParam> importQuery = importRequest.getQuery();
if (CollectionUtils.isNotEmpty(dbQuery) || CollectionUtils.isNotEmpty(importQuery)) {
List<String> dbQueryKeys = dbQuery.stream().map(QueryParam::getKey).toList();
List<String> importQueryKeys = importQuery.stream().map(QueryParam::getKey).toList();
if (!paramsIsSame(dbQueryKeys, importQueryKeys)) {
return false;
}
}
List<RestParam> dbRest = dbRequest.getRest();
List<RestParam> importRest = importRequest.getRest();
if (CollectionUtils.isNotEmpty(dbRest) || CollectionUtils.isNotEmpty(importRest)) {
List<String> dbRestKeys = dbRest.stream().map(RestParam::getKey).toList();
List<String> importRestKeys = importRest.stream().map(RestParam::getKey).toList();
if (!paramsIsSame(dbRestKeys, importRestKeys)) {
return false;
}
}
//判断请求体是否一样
if (dbRequest.getBody() != null || importRequest.getBody() != null) {
//判断请求体的参数
Body dbbody = dbRequest.getBody();
Body importBody = importRequest.getBody();
if (dbbody != null && importBody != null) {
//判断null类型
StringUtils.equals(String.valueOf(dbbody.getNoneBody()), String.valueOf(importBody.getNoneBody()));
//判断form类型
FormDataBody formDataBody = dbbody.getFormDataBody();
FormDataBody importFormDataBody = importBody.getFormDataBody();
if (ObjectUtils.isNotEmpty(formDataBody) || ObjectUtils.isNotEmpty(importFormDataBody)) {
List<FormDataKV> fromValues = formDataBody.getFromValues();
List<FormDataKV> importFromValues = importFormDataBody.getFromValues();
if (CollectionUtils.isNotEmpty(fromValues) || CollectionUtils.isNotEmpty(importFromValues)) {
List<String> dbFormKeys = fromValues.stream().map(FormDataKV::getKey).toList();
List<String> importFormKeys = importFromValues.stream().map(FormDataKV::getKey).toList();
if (!paramsIsSame(dbFormKeys, importFormKeys)) {
return false;
}
}
}
//判读www类型
WWWFormBody wwwBody = dbbody.getWwwFormBody();
WWWFormBody importWwwBody = importBody.getWwwFormBody();
if (ObjectUtils.isNotEmpty(wwwBody) || ObjectUtils.isNotEmpty(importWwwBody)) {
List<FormDataKV> wwwValues = wwwBody.getFromValues();
List<FormDataKV> importWwwValues = importWwwBody.getFromValues();
if (CollectionUtils.isNotEmpty(wwwValues) || CollectionUtils.isNotEmpty(importWwwValues)) {
List<String> dbWwwKeys = wwwValues.stream().map(FormDataKV::getKey).toList();
List<String> importWwwKeys = importWwwValues.stream().map(FormDataKV::getKey).toList();
if (!paramsIsSame(dbWwwKeys, importWwwKeys)) {
return false;
}
}
}
//TODO 判断binary类型
//判断raw类型
RawBody rawBody = dbbody.getRawBody();
RawBody importRawBody = importBody.getRawBody();
if (ObjectUtils.isNotEmpty(rawBody) || ObjectUtils.isNotEmpty(importRawBody)) {
return false;
}
//判断json类型
JsonBody jsonBody = dbbody.getJsonBody();
JsonBody importJsonBody = importBody.getJsonBody();
if (ObjectUtils.isNotEmpty(jsonBody) || ObjectUtils.isNotEmpty(importJsonBody)) {
if (StringUtils.isNotBlank(jsonBody.getJsonValue()) || StringUtils.isNotBlank(importJsonBody.getJsonValue())) {
return false;
}
//判断jsonschema
JsonSchemaItem jsonSchema = jsonBody.getJsonSchema();
JsonSchemaItem importJsonSchema = importJsonBody.getJsonSchema();
if (jsonSchema != null && importJsonSchema != null) {
return jsonSchemaIsSame(jsonSchema, importJsonSchema);
}
}
}
}
return same;
}
//判断jsonschema的参数是否一样
private static boolean jsonSchemaIsSame(JsonSchemaItem jsonSchema, JsonSchemaItem importJsonSchema) {
boolean same = true;
if (jsonSchema == null && importJsonSchema == null) {
return true;
}
if (!StringUtils.equals(jsonSchema.getType(), importJsonSchema.getType())) {
return false;
}
if (StringUtils.equals(jsonSchema.getType(), PropertyConstant.OBJECT)) {
Map<String, JsonSchemaItem> properties = jsonSchema.getProperties();
Map<String, JsonSchemaItem> importProperties = importJsonSchema.getProperties();
if (MapUtils.isNotEmpty(properties) || MapUtils.isNotEmpty(importProperties)) {
List<String> dbJsonKeys = properties.keySet().stream().toList();
List<String> importJsonKeys = importProperties.keySet().stream().toList();
if (!paramsIsSame(dbJsonKeys, importJsonKeys)) {
return false;
}
//遍历判断每个参数是否一样
for (String key : dbJsonKeys) {
JsonSchemaItem jsonSchemaItem = properties.get(key);
JsonSchemaItem importJsonSchemaItem = importProperties.get(key);
if (!jsonSchemaIsSame(jsonSchemaItem, importJsonSchemaItem)) {
same = false;
break;
}
}
}
}
if (StringUtils.equals(jsonSchema.getType(), PropertyConstant.ARRAY)) {
JsonSchemaItem items = jsonSchema.getItems();
JsonSchemaItem importItems = importJsonSchema.getItems();
if (items != null && importItems != null) {
if (!jsonSchemaIsSame(items, importItems)) {
return false;
}
}
}
return same;
}
private static boolean paramsIsSame(List<String> dbRestKeys, List<String> importRestKeys) {
if (dbRestKeys.size() != importRestKeys.size()) {
return false;
}
//看看是否有差集
List<String> differenceRest = dbRestKeys.stream().filter(t -> !importRestKeys.contains(t)).toList();
return CollectionUtils.isEmpty(differenceRest);
}
private Boolean getFullCoverage(Boolean fullCoverage) {
if (fullCoverage == null) {
fullCoverage = false;
}
return fullCoverage;
}
/**
* 构造树结构 但是这里需要module的path
*
* @param projectId
* @param protocol
* @return
*/
public List<BaseTreeNode> buildTreeData(String projectId, String protocol) {
ApiModuleRequest request = new ApiModuleRequest();
request.setProjectId(projectId);
request.setProtocol(protocol);
List<BaseTreeNode> fileModuleList = extApiDefinitionModuleMapper.selectBaseByRequest(request);
return this.buildTreeAndCountResource(fileModuleList, true, Translator.get(UNPLANNED_API));
}
public List<BaseTreeNode> buildTreeAndCountResource(List<BaseTreeNode> traverseList, boolean haveVirtualRootNode, String virtualRootName) {
List<BaseTreeNode> baseTreeNodeList = new ArrayList<>();
if (haveVirtualRootNode) {
BaseTreeNode defaultNode = new BaseTreeNode(ModuleConstants.DEFAULT_NODE_ID, virtualRootName, ModuleConstants.NODE_TYPE_DEFAULT, ModuleConstants.ROOT_NODE_PARENT_ID);
defaultNode.setAttachInfo(Map.of(MODULE, StringUtils.join("/", defaultNode.getName())));
baseTreeNodeList.add(defaultNode);
}
int lastSize = 0;
Map<String, BaseTreeNode> baseTreeNodeMap = new HashMap<>();
while (CollectionUtils.isNotEmpty(traverseList) && traverseList.size() != lastSize) {
lastSize = traverseList.size();
List<BaseTreeNode> notMatchedList = new ArrayList<>();
for (BaseTreeNode treeNode : traverseList) {
if (StringUtils.equalsIgnoreCase(treeNode.getParentId(), ModuleConstants.ROOT_NODE_PARENT_ID)) {
BaseTreeNode node = new BaseTreeNode(treeNode.getId(), treeNode.getName(), treeNode.getType(), treeNode.getParentId());
node.setAttachInfo(Map.of(MODULE, StringUtils.join("/", node.getName())));
baseTreeNodeList.add(node);
baseTreeNodeMap.put(treeNode.getId(), node);
} else {
if (baseTreeNodeMap.containsKey(treeNode.getParentId())) {
BaseTreeNode node = new BaseTreeNode(treeNode.getId(), treeNode.getName(), treeNode.getType(), treeNode.getParentId());
node.setAttachInfo(Map.of(MODULE, StringUtils.join(baseTreeNodeMap.get(treeNode.getParentId()).getAttachInfo().get(MODULE), "/", node.getName())));
baseTreeNodeList.add(node);
}
}
}
traverseList = notMatchedList;
}
return baseTreeNodeList;
}
}

View File

@ -6,11 +6,17 @@ import io.metersphere.api.controller.result.ApiResultCode;
import io.metersphere.api.domain.*;
import io.metersphere.api.dto.debug.ApiFileResourceUpdateRequest;
import io.metersphere.api.dto.definition.*;
import io.metersphere.api.dto.definition.importdto.ApiDefinitionImport;
import io.metersphere.api.dto.definition.importdto.ApiDefinitionImportDTO;
import io.metersphere.api.dto.request.ImportRequest;
import io.metersphere.api.mapper.*;
import io.metersphere.api.parser.ImportParser;
import io.metersphere.api.parser.ImportParserFactory;
import io.metersphere.api.service.ApiFileResourceService;
import io.metersphere.api.utils.ApiDataUtils;
import io.metersphere.plugin.api.spi.AbstractMsTestElement;
import io.metersphere.project.mapper.ExtBaseProjectVersionMapper;
import io.metersphere.project.mapper.ProjectMapper;
import io.metersphere.project.service.ProjectService;
import io.metersphere.sdk.constants.ApiReportStatus;
import io.metersphere.sdk.constants.ApplicationNumScope;
@ -93,11 +99,15 @@ public class ApiDefinitionService {
@Resource
private ApiDefinitionLogService apiDefinitionLogService;
@Resource
private ApiDefinitionImportUtilService apiDefinitionImportUtilService;
@Resource
private ProjectMapper projectMapper;
@Resource
private ApiDefinitionMockService apiDefinitionMockService;
public List<ApiDefinitionDTO> getApiDefinitionPage(ApiDefinitionPageRequest request, String userId){
public List<ApiDefinitionDTO> getApiDefinitionPage(ApiDefinitionPageRequest request, String userId) {
CustomFieldUtils.setBaseQueryRequestCustomMultipleFields(request, userId);
List<ApiDefinitionDTO> list = extApiDefinitionMapper.list(request);
if (!CollectionUtils.isEmpty(list)) {
@ -106,7 +116,7 @@ public class ApiDefinitionService {
return list;
}
public List<ApiDefinitionDTO> getDocPage(ApiDefinitionPageRequest request, String userId){
public List<ApiDefinitionDTO> getDocPage(ApiDefinitionPageRequest request, String userId) {
CustomFieldUtils.setBaseQueryRequestCustomMultipleFields(request, userId);
List<ApiDefinitionDTO> list = extApiDefinitionMapper.list(request);
if (!CollectionUtils.isEmpty(list)) {
@ -115,7 +125,7 @@ public class ApiDefinitionService {
return list;
}
private void processApiDefinitionsDoc(List<ApiDefinitionDTO> list){
private void processApiDefinitionsDoc(List<ApiDefinitionDTO> list) {
Set<String> userIds = extractUserIds(list);
Map<String, String> userMap = userLoginService.getUserNameMap(new ArrayList<>(userIds));
@ -137,7 +147,7 @@ public class ApiDefinitionService {
});
}
public ApiDefinitionDTO get(String id, String userId){
public ApiDefinitionDTO get(String id, String userId) {
// 1. 避免重复查询数据库将查询结果传递给get方法
ApiDefinition apiDefinition = checkApiDefinition(id);
return getApiDefinitionInfo(id, userId, apiDefinition);
@ -204,7 +214,7 @@ public class ApiDefinitionService {
ApiDefinition originApiDefinition = checkApiDefinition(request.getId());
ApiDefinition apiDefinition = new ApiDefinition();
BeanUtils.copyBean(apiDefinition, request);
if(request.getProtocol().equals(ModuleConstants.NODE_PROTOCOL_HTTP)){
if (request.getProtocol().equals(ModuleConstants.NODE_PROTOCOL_HTTP)) {
checkUpdateExist(apiDefinition);
}
apiDefinition.setStatus(request.getStatus());
@ -243,7 +253,7 @@ public class ApiDefinitionService {
if (CollectionUtils.isNotEmpty(ids)) {
if (request.getType().equals("tags")) {
handleTags(request, userId, ids);
} else if(request.getType().equals("customs")){
} else if (request.getType().equals("customs")) {
// 自定义字段处理
ApiDefinitionCustomFieldDTO customField = request.getCustomField();
Map<String, String> customFieldMap = Collections.singletonMap(customField.getId(), customField.getValue());
@ -254,7 +264,7 @@ public class ApiDefinitionService {
apiDefinitionUpdateRequest.setId(id);
handleUpdateCustomFields(apiDefinitionUpdateRequest, request.isAppend());
});
}else {
} else {
ApiDefinition apiDefinition = new ApiDefinition();
BeanUtils.copyBean(apiDefinition, request);
apiDefinition.setUpdateUser(userId);
@ -347,7 +357,7 @@ public class ApiDefinitionService {
ApiDefinitionBlob copyApiDefinitionBlob = getApiDefinitionBlob(request.getId());
ApiDefinitionBlob apiDefinitionBlob = new ApiDefinitionBlob();
if(copyApiDefinitionBlob != null){
if (copyApiDefinitionBlob != null) {
apiDefinitionBlob.setId(apiDefinition.getId());
apiDefinitionBlob.setRequest(copyApiDefinitionBlob.getRequest());
apiDefinitionBlob.setResponse(copyApiDefinitionBlob.getResponse());
@ -363,7 +373,7 @@ public class ApiDefinitionService {
public void delete(ApiDefinitionDeleteRequest request, String userId) {
checkApiDefinition(request.getId());
handleDeleteApiDefinition(Collections.singletonList(request.getId()),request.getDeleteAll(), request.getProjectId(), userId, false);
handleDeleteApiDefinition(Collections.singletonList(request.getId()), request.getDeleteAll(), request.getProjectId(), userId, false);
}
public void batchDelete(ApiDefinitionBatchRequest request, String userId) {
@ -548,7 +558,7 @@ public class ApiDefinitionService {
}
private String getCopyName(String name) {
String copyName = "copy_" + name ;
String copyName = "copy_" + name;
if (copyName.length() > 255) {
copyName = copyName.substring(0, 250) + copyName.substring(copyName.length() - 5);
}
@ -559,17 +569,17 @@ public class ApiDefinitionService {
if (deleteAll) {
//全部删除 进入回收站
List<String> refIds = extApiDefinitionMapper.getRefIds(ids, false);
if(CollectionUtils.isNotEmpty(refIds)){
if (CollectionUtils.isNotEmpty(refIds)) {
SubListUtils.dealForSubList(refIds, 2000, subRefIds -> {
List<String> delApiIds = extApiDefinitionMapper.getIdsByRefId(subRefIds, false);
SubListUtils.dealForSubList(delApiIds, 2000, subList -> {
if(CollectionUtils.isNotEmpty(delApiIds)){
if (CollectionUtils.isNotEmpty(delApiIds)) {
// 删除接口相关数据到回收站
deleteApiRelatedData(subList, userId, projectId);
}
});
// 记录删除到回收站的日志, 单条注解记录
if(isBatch){
if (isBatch) {
apiDefinitionLogService.batchDelLog(delApiIds, userId, projectId);
}
extApiDefinitionMapper.batchDeleteByRefId(subRefIds, userId, projectId);
@ -583,21 +593,21 @@ public class ApiDefinitionService {
}
}
private void deleteAfterAction(List<ApiDefinitionVersionDTO> apiDefinitionVersions){
apiDefinitionVersions.forEach(item->{
clearLatestVersion(item.getRefId(),item.getProjectId());
private void deleteAfterAction(List<ApiDefinitionVersionDTO> apiDefinitionVersions) {
apiDefinitionVersions.forEach(item -> {
clearLatestVersion(item.getRefId(), item.getProjectId());
ApiDefinition latestData = getLatestData(item.getRefId(), item.getProjectId());
updateLatestVersion(latestData.getId(),latestData.getProjectId());
updateLatestVersion(latestData.getId(), latestData.getProjectId());
});
}
// 清除多版本最新标识
private void clearLatestVersion(String refId, String projectId){
private void clearLatestVersion(String refId, String projectId) {
extApiDefinitionMapper.clearLatestVersion(refId, projectId);
}
// 获取多版本最新一条数据
private ApiDefinition getLatestData(String refId, String projectId){
private ApiDefinition getLatestData(String refId, String projectId) {
ApiDefinitionExample apiDefinitionExample = new ApiDefinitionExample();
apiDefinitionExample.createCriteria().andRefIdEqualTo(refId).andDeletedEqualTo(false).andProjectIdEqualTo(projectId);
apiDefinitionExample.setOrderByClause("update_time DESC");
@ -609,17 +619,17 @@ public class ApiDefinitionService {
}
// 更新最新版本标识
private void updateLatestVersion(String id, String projectId){
private void updateLatestVersion(String id, String projectId) {
extApiDefinitionMapper.updateLatestVersion(id, projectId);
}
private void doDelete(List<String> ids, String userId, String projectId, boolean isBatch) {
if(CollectionUtils.isNotEmpty(ids)){
if (CollectionUtils.isNotEmpty(ids)) {
// 需要判断是否存在多个版本问题
ids.forEach(id -> {
ApiDefinition apiDefinition = checkApiDefinition(id);
// 删除的数据是否为最新版本的数据如果是则需要查询是否有多版本数据存在需要去除当前删除的数据更新剩余版本数据中最近的一条数据为最新的数据
if(apiDefinition.getLatest()){
if (apiDefinition.getLatest()) {
List<ApiDefinitionVersionDTO> apiDefinitionVersions = extApiDefinitionMapper.getApiDefinitionByRefId(apiDefinition.getRefId());
if (apiDefinitionVersions.size() > 1) {
deleteAfterAction(apiDefinitionVersions);
@ -629,7 +639,7 @@ public class ApiDefinitionService {
// 删除 case
deleteApiRelatedData(ids, userId, projectId);
// 记录删除到回收站的日志, 单条注解记录
if(isBatch){
if (isBatch) {
apiDefinitionLogService.batchDelLog(ids, userId, projectId);
}
// 删除接口到回收站
@ -638,10 +648,10 @@ public class ApiDefinitionService {
}
private void deleteApiRelatedData(List<String> apiIds, String userId, String projectId){
private void deleteApiRelatedData(List<String> apiIds, String userId, String projectId) {
// 是否存在 case 删除 case
List<ApiTestCase> caseLists = extApiTestCaseMapper.getCaseInfoByApiIds(apiIds, false);
if(CollectionUtils.isNotEmpty(caseLists)){
if (CollectionUtils.isNotEmpty(caseLists)) {
List<String> caseIds = caseLists.stream().map(ApiTestCase::getId).distinct().toList();
apiTestCaseService.batchDeleteToGc(caseIds, userId, projectId, true);
}
@ -655,7 +665,8 @@ public class ApiDefinitionService {
// 恢复接口到接口列表
handleRecoverApiDefinition(Collections.singletonList(request.getId()), userId, request.getProjectId(), false);
}
public void handleRecoverApiDefinition(List<String> ids, String userId, String projectId, boolean isBatch){
public void handleRecoverApiDefinition(List<String> ids, String userId, String projectId, boolean isBatch) {
if (CollectionUtils.isNotEmpty(ids)) {
SubListUtils.dealForSubList(ids, 2000, subList -> doRecover(subList, userId, projectId, isBatch));
}
@ -663,7 +674,7 @@ public class ApiDefinitionService {
private void doRecover(List<String> apiIds, String userId, String projectId, boolean isBatch) {
// 记录恢复数据之前的原数据日志单条通过注解记录日志
if(isBatch){
if (isBatch) {
apiDefinitionLogService.batchRecoverLog(apiIds, userId, projectId);
}
extApiDefinitionMapper.batchRecoverById(apiIds, userId, projectId);
@ -693,7 +704,7 @@ public class ApiDefinitionService {
}
private void handleModule(List<String> updateApiIds) {
if(!updateApiIds.isEmpty()){
if (!updateApiIds.isEmpty()) {
ApiDefinition updateApiDefinition = new ApiDefinition();
updateApiDefinition.setModuleId(ModuleConstants.DEFAULT_NODE_ID);
ApiDefinitionExample apiDefinitionExample = new ApiDefinitionExample();
@ -715,13 +726,14 @@ public class ApiDefinitionService {
}
}
private void recoverApiRelatedData(List<String> apiIds, String userId, String projectId){
private void recoverApiRelatedData(List<String> apiIds, String userId, String projectId) {
// 是否存在 case 恢复 case
List<ApiTestCase> caseLists = extApiTestCaseMapper.getCaseInfoByApiIds(apiIds, true);
if(CollectionUtils.isNotEmpty(caseLists)) {
if (CollectionUtils.isNotEmpty(caseLists)) {
apiTestCaseService.batchRecover(caseLists, userId, projectId);
}
}
public void trashDel(ApiDefinitionDeleteRequest request, String userId) {
handleTrashDelApiDefinition(Collections.singletonList(request.getId()), userId, request.getProjectId(), false);
}
@ -740,14 +752,14 @@ public class ApiDefinitionService {
}
}
private void handleTrashDelApiDefinition(List<String> ids, String userId, String projectId, boolean isBatch){
private void handleTrashDelApiDefinition(List<String> ids, String userId, String projectId, boolean isBatch) {
if (CollectionUtils.isNotEmpty(ids)) {
SubListUtils.dealForSubList(ids, 2000, subList -> doTrashDel(subList, userId, projectId, isBatch));
}
}
private void doTrashDel(List<String> ids, String userId, String projectId, boolean isBatch){
if(CollectionUtils.isNotEmpty(ids)){
private void doTrashDel(List<String> ids, String userId, String projectId, boolean isBatch) {
if (CollectionUtils.isNotEmpty(ids)) {
// 删除上传的文件
ids.forEach(id -> {
String apiDefinitionDir = DefaultRepositoryDir.getApiDefinitionDir(projectId, id);
@ -762,7 +774,7 @@ public class ApiDefinitionService {
trashDelApiRelatedData(ids, userId, projectId);
// 记录批量删除日志单条删除通过注解记录
if(isBatch){
if (isBatch) {
apiDefinitionLogService.batchTrashDelLog(ids, userId, projectId);
}
// 删除接口
@ -773,13 +785,14 @@ public class ApiDefinitionService {
}
}
private void trashDelApiRelatedData(List<String> apiIds, String userId, String projectId){
private void trashDelApiRelatedData(List<String> apiIds, String userId, String projectId) {
// 是否存在 case 删除 case
List<ApiTestCase> caseLists = extApiTestCaseMapper.getCaseInfoByApiIds(apiIds, true);
if(CollectionUtils.isNotEmpty(caseLists)) {
if (CollectionUtils.isNotEmpty(caseLists)) {
List<String> caseIds = caseLists.stream().map(ApiTestCase::getId).distinct().toList();
// case 批量删除回收站
apiTestCaseService.deleteResourceByIds(caseIds, projectId, userId);
}
// 删除 mock
apiDefinitionMockService.deleteByApiIds(apiIds, userId);
@ -856,4 +869,45 @@ public class ApiDefinitionService {
return apiDefinitionDocDTO;
}
public ApiDefinitionImport apiTestImport(MultipartFile file, ImportRequest request) {
if (file != null) {
String originalFilename = file.getOriginalFilename();
if (StringUtils.isNotBlank(originalFilename)) {
String suffixName = originalFilename.substring(originalFilename.indexOf(".") + 1);
apiDefinitionImportUtilService.checkFileSuffixName(request, suffixName);
}
}
ImportParser<?> runService = ImportParserFactory.getImportParser(request.getPlatform());
ApiDefinitionImport apiImport = null;
if (StringUtils.equals(request.getType(), "SCHEDULE")) {
request.setProtocol("HTTP");
}
try {
apiImport = (ApiDefinitionImport) Objects.requireNonNull(runService).parse(file == null ? null : file.getInputStream(), request);
//TODO 处理mock数据
// 发送通知
} catch (Exception e) {
// TODO 发送通知
LogUtils.error(e.getMessage(), e);
throw new MSException(Translator.get("parse_data_error"));
}
try {
apiDefinitionImportUtilService.importApi(request, apiImport);
apiDefinitionMapper.selectByExample(new ApiDefinitionExample());
if (CollectionUtils.isNotEmpty(apiImport.getData())) {
List<String> names = apiImport.getData().stream().map(ApiDefinitionImportDTO::getName).collect(Collectors.toList());
request.setName(String.join(",", names));
List<String> ids = apiImport.getData().stream().map(ApiDefinitionImportDTO::getId).collect(Collectors.toList());
request.setId(JSON.toJSONString(ids));
}
// 发送通知
//apiDefinitionImportUtilService.sendImportNotice(request, apiImportSendNoticeDTOS, project);
} catch (Exception e) {
//apiDefinitionImportUtilService.sendFailMessage(request, project);
LogUtils.error(e);
throw new MSException(Translator.get("user_import_format_wrong"));
}
return apiImport;
}
}

View File

@ -5,6 +5,7 @@ import io.metersphere.api.constants.ApiDefinitionStatus;
import io.metersphere.api.controller.result.ApiResultCode;
import io.metersphere.api.domain.*;
import io.metersphere.api.dto.definition.*;
import io.metersphere.api.dto.request.ImportRequest;
import io.metersphere.api.dto.request.http.MsHTTPElement;
import io.metersphere.api.mapper.*;
import io.metersphere.api.model.CheckLogModel;
@ -18,6 +19,7 @@ import io.metersphere.project.service.FileAssociationService;
import io.metersphere.project.service.FileMetadataService;
import io.metersphere.sdk.constants.DefaultRepositoryDir;
import io.metersphere.sdk.constants.PermissionConstants;
import io.metersphere.sdk.constants.SessionConstants;
import io.metersphere.sdk.exception.MSException;
import io.metersphere.sdk.file.FileCenter;
import io.metersphere.sdk.file.FileRequest;
@ -34,17 +36,30 @@ import org.apache.commons.lang3.StringUtils;
import org.junit.jupiter.api.*;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.test.context.jdbc.Sql;
import org.springframework.test.context.jdbc.SqlConfig;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.ResultMatcher;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
import org.springframework.test.web.servlet.request.MockMultipartHttpServletRequestBuilder;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;
@SpringBootTest(webEnvironment= SpringBootTest.WebEnvironment.RANDOM_PORT)
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
@AutoConfigureMockMvc
public class ApiDefinitionControllerTests extends BaseTest {
@ -226,7 +241,7 @@ public class ApiDefinitionControllerTests extends BaseTest {
BeanUtils.copyBean(copyApiDefinition, request);
Assertions.assertEquals(apiDefinition, copyApiDefinition);
ApiDataUtils.setResolver(MsHTTPElement.class);
if(apiDefinitionBlob != null){
if (apiDefinitionBlob != null) {
Assertions.assertEquals(msHttpElement, ApiDataUtils.parseObject(new String(apiDefinitionBlob.getRequest()), AbstractMsTestElement.class));
}
return apiDefinition;
@ -236,7 +251,7 @@ public class ApiDefinitionControllerTests extends BaseTest {
@Order(2)
@Sql(scripts = {"/dml/init_api_definition.sql"}, config = @SqlConfig(encoding = "utf-8", transactionMode = SqlConfig.TransactionMode.ISOLATED))
public void get() throws Exception {
if(apiDefinition == null){
if (apiDefinition == null) {
apiDefinition = apiDefinitionMapper.selectByPrimaryKey("1001");
}
// @@请求成功
@ -252,11 +267,11 @@ public class ApiDefinitionControllerTests extends BaseTest {
copyApiDefinitionDTO.setFollow(CollectionUtils.isNotEmpty(followers));
List<ApiDefinitionCustomFieldDTO> customFields = extApiDefinitionCustomFieldMapper.getApiCustomFields(Collections.singletonList(apiDefinition.getId()), apiDefinition.getProjectId());
if(!customFields.isEmpty()) {
if (!customFields.isEmpty()) {
Map<String, List<ApiDefinitionCustomFieldDTO>> customFieldMap = customFields.stream().collect(Collectors.groupingBy(ApiDefinitionCustomFieldDTO::getApiId));
copyApiDefinitionDTO.setCustomFields(customFieldMap.get(apiDefinition.getId()));
}
if(apiDefinitionBlob != null){
if (apiDefinitionBlob != null) {
copyApiDefinitionDTO.setRequest(ApiDataUtils.parseObject(new String(apiDefinitionBlob.getRequest()), AbstractMsTestElement.class));
copyApiDefinitionDTO.setResponse(ApiDataUtils.parseArray(new String(apiDefinitionBlob.getResponse()), HttpResponse.class));
}
@ -273,7 +288,7 @@ public class ApiDefinitionControllerTests extends BaseTest {
@Sql(scripts = {"/dml/init_api_definition.sql"}, config = @SqlConfig(encoding = "utf-8", transactionMode = SqlConfig.TransactionMode.ISOLATED))
public void testUpdate() throws Exception {
LogUtils.info("update api test");
if(apiDefinition == null){
if (apiDefinition == null) {
apiDefinition = apiDefinitionMapper.selectByPrimaryKey("1001");
}
ApiDefinitionUpdateRequest request = new ApiDefinitionUpdateRequest();
@ -382,6 +397,7 @@ public class ApiDefinitionControllerTests extends BaseTest {
/**
* 校验上传的文件
*
* @param id
* @param fileIds 全部的文件ID
*/
@ -411,6 +427,7 @@ public class ApiDefinitionControllerTests extends BaseTest {
/**
* 校验上传的文件
*
* @param id
* @param fileIds 全部的文件ID
*/
@ -430,25 +447,25 @@ public class ApiDefinitionControllerTests extends BaseTest {
LogUtils.info("batch update api test");
ApiDefinitionBatchUpdateRequest apiDefinitionBatchUpdateRequest = new ApiDefinitionBatchUpdateRequest();
apiDefinitionBatchUpdateRequest.setProjectId(DEFAULT_PROJECT_ID);
apiDefinitionBatchUpdateRequest.setSelectIds(List.of("1001","1002","1005"));
apiDefinitionBatchUpdateRequest.setSelectIds(List.of("1001", "1002", "1005"));
apiDefinitionBatchUpdateRequest.setExcludeIds(List.of("1005"));
apiDefinitionBatchUpdateRequest.setSelectAll(false);
apiDefinitionBatchUpdateRequest.setType("tags");
// 修改标签追加
apiDefinitionBatchUpdateRequest.setSelectIds(List.of("1001","1002"));
apiDefinitionBatchUpdateRequest.setTags(new LinkedHashSet<>(List.of("tag-append","tag-append1")));
apiDefinitionBatchUpdateRequest.setSelectIds(List.of("1001", "1002"));
apiDefinitionBatchUpdateRequest.setTags(new LinkedHashSet<>(List.of("tag-append", "tag-append1")));
apiDefinitionBatchUpdateRequest.setAppend(true);
this.requestPostWithOk(BATCH_UPDATE, apiDefinitionBatchUpdateRequest);
assertBatchUpdateApiDefinition(apiDefinitionBatchUpdateRequest, List.of("1001","1002"));
assertBatchUpdateApiDefinition(apiDefinitionBatchUpdateRequest, List.of("1001", "1002"));
// 修改标签覆盖
apiDefinitionBatchUpdateRequest.setSelectIds(List.of("1003","1004"));
apiDefinitionBatchUpdateRequest.setTags(new LinkedHashSet<>(List.of("tag-append","tag-append1")));
apiDefinitionBatchUpdateRequest.setSelectIds(List.of("1003", "1004"));
apiDefinitionBatchUpdateRequest.setTags(new LinkedHashSet<>(List.of("tag-append", "tag-append1")));
apiDefinitionBatchUpdateRequest.setAppend(false);
this.requestPostWithOk(BATCH_UPDATE, apiDefinitionBatchUpdateRequest);
assertBatchUpdateApiDefinition(apiDefinitionBatchUpdateRequest, List.of("1003","1004"));
assertBatchUpdateApiDefinition(apiDefinitionBatchUpdateRequest, List.of("1003", "1004"));
// 自定义字段追加
apiDefinitionBatchUpdateRequest.setType("customs");
apiDefinitionBatchUpdateRequest.setSelectIds(List.of("1002","1003","1004"));
apiDefinitionBatchUpdateRequest.setSelectIds(List.of("1002", "1003", "1004"));
ApiDefinitionCustomFieldDTO field = new ApiDefinitionCustomFieldDTO();
field.setId("test_field");
field.setValue(JSON.toJSONString(List.of("test1-batch")));
@ -500,16 +517,16 @@ public class ApiDefinitionControllerTests extends BaseTest {
apiDefinitionExample.createCriteria().andIdIn(ids);
List<ApiDefinition> apiDefinitions = apiDefinitionMapper.selectByExample(apiDefinitionExample);
apiDefinitions.forEach(item -> {
if(request.getStatus() != null){
if (request.getStatus() != null) {
Assertions.assertEquals(item.getStatus(), request.getStatus());
}
if(request.getMethod() != null){
if (request.getMethod() != null) {
Assertions.assertEquals(item.getMethod(), request.getMethod());
}
if(request.getVersionId() != null) {
if (request.getVersionId() != null) {
Assertions.assertEquals(item.getVersionId(), request.getVersionId());
}
if(request.getTags() != null) {
if (request.getTags() != null) {
if (request.isAppend()) {
Assertions.assertTrue(JSON.parseArray(item.getTags(), String.class).containsAll(request.getTags()));
} else {
@ -530,7 +547,7 @@ public class ApiDefinitionControllerTests extends BaseTest {
// @数据验证
List<ApiFileResource> sourceFiles = apiFileResourceService.getByResourceId(apiDefinition.getId());
List<ApiFileResource> copyFiles = apiFileResourceService.getByResourceId(resultData.getId());
if(!sourceFiles.isEmpty() && !copyFiles.isEmpty()){
if (!sourceFiles.isEmpty() && !copyFiles.isEmpty()) {
Assertions.assertEquals(sourceFiles.size(), copyFiles.size());
}
Assertions.assertTrue(resultData.getName().contains("copy_"));
@ -558,7 +575,7 @@ public class ApiDefinitionControllerTests extends BaseTest {
request.setProjectId(DEFAULT_PROJECT_ID);
// 移动选中
request.setSelectIds(List.of("1001","1002","1005"));
request.setSelectIds(List.of("1001", "1002", "1005"));
request.setExcludeIds(List.of("1005"));
request.setDeleteAll(false);
request.setSelectAll(false);
@ -655,7 +672,8 @@ public class ApiDefinitionControllerTests extends BaseTest {
case "FILTER" -> configureFilterSearch(request);
case "COMBINE" -> configureCombineSearch(request);
case "DELETED" -> configureDeleteSearch(request);
default -> {}
default -> {
}
}
MvcResult mvcResult = this.requestPostWithOkAndReturn(url, request);
@ -714,7 +732,7 @@ public class ApiDefinitionControllerTests extends BaseTest {
custom.put("id", "test_field");
custom.put("operator", "in");
custom.put("type", "multipleSelect");
custom.put("value", JSON.toJSONString(List.of("test", "default")));
custom.put("value", JSON.toJSONString(List.of("test", "default")));
customs.add(custom);
Map<String, Object> currentUserCustom = new HashMap<>();
currentUserCustom.put("id", "test_field");
@ -759,7 +777,7 @@ public class ApiDefinitionControllerTests extends BaseTest {
ApiDefinitionDocDTO copyApiDefinitionDocDTO = new ApiDefinitionDocDTO();
ApiDefinitionDTO copyApiDefinitionDTO = BeanUtils.copyBean(new ApiDefinitionDTO(), apiDefinition);
ApiDefinitionBlob apiDefinitionBlob = apiDefinitionBlobMapper.selectByPrimaryKey(apiDefinition.getId());
if(apiDefinitionBlob != null){
if (apiDefinitionBlob != null) {
copyApiDefinitionDTO.setRequest(ApiDataUtils.parseObject(new String(apiDefinitionBlob.getRequest()), AbstractMsTestElement.class));
copyApiDefinitionDTO.setResponse(ApiDataUtils.parseArray(new String(apiDefinitionBlob.getResponse()), HttpResponse.class));
}
@ -784,10 +802,10 @@ public class ApiDefinitionControllerTests extends BaseTest {
// 校验数据是否正确
ApiDefinitionDocDTO copyModuleApiDefinitionDocDTO = new ApiDefinitionDocDTO();
List<ApiDefinitionDTO> list = extApiDefinitionMapper.listDoc(request);
if(null != list){
if (null != list) {
ApiDefinitionDTO first = list.stream().findFirst().orElseThrow(() -> new MSException(ApiResultCode.API_DEFINITION_NOT_EXIST));
ApiDefinitionBlob moduleApiDefinitionBlob = apiDefinitionBlobMapper.selectByPrimaryKey(first.getId());
if(moduleApiDefinitionBlob != null){
if (moduleApiDefinitionBlob != null) {
first.setRequest(ApiDataUtils.parseObject(new String(moduleApiDefinitionBlob.getRequest()), AbstractMsTestElement.class));
first.setResponse(ApiDataUtils.parseArray(new String(moduleApiDefinitionBlob.getResponse()), HttpResponse.class));
}
@ -816,14 +834,14 @@ public class ApiDefinitionControllerTests extends BaseTest {
// 校验数据是否正确
ApiDefinitionDocDTO copyAllApiDefinitionDocDTO = new ApiDefinitionDocDTO();
List<ApiDefinitionDTO> allList = extApiDefinitionMapper.listDoc(request);
if(null != allList){
if (null != allList) {
ApiDefinitionDTO info = allList.stream().findFirst().orElseThrow(() -> new MSException(ApiResultCode.API_DEFINITION_NOT_EXIST));
ApiDefinitionBlob allApiDefinitionBlob = apiDefinitionBlobMapper.selectByPrimaryKey(info.getId());
if(allApiDefinitionBlob != null){
if (allApiDefinitionBlob != null) {
info.setRequest(ApiDataUtils.parseObject(new String(allApiDefinitionBlob.getRequest()), AbstractMsTestElement.class));
info.setResponse(ApiDataUtils.parseArray(new String(allApiDefinitionBlob.getResponse()), HttpResponse.class));
}
if(StringUtils.isBlank(copyAllApiDefinitionDocDTO.getDocTitle())){
if (StringUtils.isBlank(copyAllApiDefinitionDocDTO.getDocTitle())) {
copyAllApiDefinitionDocDTO.setDocTitle(Translator.get(ALL_API));
}
copyAllApiDefinitionDocDTO.setType(ApiDefinitionDocType.ALL.name());
@ -853,10 +871,10 @@ public class ApiDefinitionControllerTests extends BaseTest {
// 校验数据是否正确
ApiDefinitionDocDTO copyModuleApiDefinitionDocDTO = new ApiDefinitionDocDTO();
List<ApiDefinitionDTO> list = extApiDefinitionMapper.listDoc(request);
if(null != list){
if (null != list) {
ApiDefinitionDTO first = list.stream().findFirst().orElseThrow(() -> new MSException(ApiResultCode.API_DEFINITION_NOT_EXIST));
ApiDefinitionBlob moduleApiDefinitionBlob = apiDefinitionBlobMapper.selectByPrimaryKey(first.getId());
if(moduleApiDefinitionBlob != null){
if (moduleApiDefinitionBlob != null) {
first.setRequest(ApiDataUtils.parseObject(new String(moduleApiDefinitionBlob.getRequest()), AbstractMsTestElement.class));
first.setResponse(ApiDataUtils.parseArray(new String(moduleApiDefinitionBlob.getResponse()), HttpResponse.class));
}
@ -900,23 +918,23 @@ public class ApiDefinitionControllerTests extends BaseTest {
ApiDefinition delApiDefinition = apiDefinitionMapper.selectByPrimaryKey(apiDefinitionDeleteRequest.getId());
MvcResult mvcResult = this.requestGetWithOk(VERSION + apiDefinitionDeleteRequest.getId()).andReturn();
ApiDataUtils.setResolver(MsHTTPElement.class);
List<ApiDefinitionVersionDTO> apiDefinitionVersionDTO = getResultDataArray(mvcResult, ApiDefinitionVersionDTO.class);
if(!apiDefinitionVersionDTO.isEmpty()){
List<ApiDefinitionVersionDTO> apiDefinitionVersionDTO = getResultDataArray(mvcResult, ApiDefinitionVersionDTO.class);
if (!apiDefinitionVersionDTO.isEmpty()) {
this.requestPostWithOkAndReturn(DELETE, apiDefinitionDeleteRequest);
// 效验数据
// 删除的数据为最新版本需要更新最近一条数据为最新数据
if(delApiDefinition.getLatest()){
if (delApiDefinition.getLatest()) {
ApiDefinitionExample example = new ApiDefinitionExample();
example.createCriteria().andRefIdEqualTo(delApiDefinition.getRefId()).andDeletedEqualTo(false).andProjectIdEqualTo(delApiDefinition.getProjectId());
example.setOrderByClause("update_time DESC");
ApiDefinition updateApiDefinition = apiDefinitionMapper.selectByExample(example).stream().findFirst().orElse(null);
if(updateApiDefinition != null) {
if (updateApiDefinition != null) {
Assertions.assertTrue(updateApiDefinition.getLatest());
Assertions.assertFalse(updateApiDefinition.getDeleted());
}
}
ApiDefinition delApiDefinitionInfo = apiDefinitionMapper.selectByPrimaryKey(apiDefinitionDeleteRequest.getId());
if(delApiDefinitionInfo != null){
if (delApiDefinitionInfo != null) {
Assertions.assertTrue(delApiDefinitionInfo.getDeleted());
Assertions.assertEquals("admin", delApiDefinitionInfo.getDeleteUser());
Assertions.assertNotNull(delApiDefinitionInfo.getDeleteTime());
@ -933,7 +951,7 @@ public class ApiDefinitionControllerTests extends BaseTest {
ApiDefinitionExample apiDefinitionExample = new ApiDefinitionExample();
apiDefinitionExample.createCriteria().andIdIn(ids);
List<ApiDefinition> apiDefinitions = apiDefinitionMapper.selectByExample(apiDefinitionExample);
if(CollectionUtils.isNotEmpty(apiDefinitions)){
if (CollectionUtils.isNotEmpty(apiDefinitions)) {
apiDefinitions.forEach(item -> {
Assertions.assertTrue(item.getDeleted());
Assertions.assertEquals("admin", item.getDeleteUser());
@ -1002,16 +1020,16 @@ public class ApiDefinitionControllerTests extends BaseTest {
// @查询恢复的数据版本是否为默认版本
String defaultVersion = extBaseProjectVersionMapper.getDefaultVersion(apiDefinition.getProjectId());
if(defaultVersion.equals(apiDefinition.getVersionId())){
if (defaultVersion.equals(apiDefinition.getVersionId())) {
// 需要处理此数据为最新标识
// 此数据不为最新版本验证是否更新为最新版本
if(!apiDefinition.getLatest()){
if (!apiDefinition.getLatest()) {
Assertions.assertTrue(apiDefinitionInfo.getLatest());
}
}
// 效验 关联数据
List<ApiTestCase> caseLists = extApiTestCaseMapper.getCaseInfoByApiIds(Collections.singletonList(apiDefinition.getId()), false);
if(!caseLists.isEmpty()) {
if (!caseLists.isEmpty()) {
caseLists.forEach(item -> {
Assertions.assertNull(item.getDeleteUser());
Assertions.assertNull(item.getDeleteTime());
@ -1035,7 +1053,7 @@ public class ApiDefinitionControllerTests extends BaseTest {
ApiDefinitionBatchRequest request = new ApiDefinitionBatchRequest();
request.setProjectId(DEFAULT_PROJECT_ID);
// 恢复选中
request.setSelectIds(List.of("1002","1004","1005"));
request.setSelectIds(List.of("1002", "1004", "1005"));
request.setExcludeIds(List.of("1005"));
request.setSelectAll(false);
this.requestPostWithOk(BATCH_RESTORE, request);
@ -1044,14 +1062,14 @@ public class ApiDefinitionControllerTests extends BaseTest {
ApiDefinitionExample apiDefinitionExample = new ApiDefinitionExample();
apiDefinitionExample.createCriteria().andIdIn(request.getSelectIds());
List<ApiDefinition> apiDefinitions = apiDefinitionMapper.selectByExample(apiDefinitionExample);
if(!apiDefinitions.isEmpty()){
if (!apiDefinitions.isEmpty()) {
apiDefinitions.forEach(item -> {
Assertions.assertFalse(item.getDeleted());
Assertions.assertNull(item.getDeleteUser());
Assertions.assertNull(item.getDeleteTime());
// 效验 关联数据
List<ApiTestCase> caseLists = extApiTestCaseMapper.getCaseInfoByApiIds(Collections.singletonList(item.getId()), false);
if(!caseLists.isEmpty()) {
if (!caseLists.isEmpty()) {
caseLists.forEach(test -> {
Assertions.assertNull(test.getDeleteUser());
Assertions.assertNull(test.getDeleteTime());
@ -1081,7 +1099,7 @@ public class ApiDefinitionControllerTests extends BaseTest {
public void testTrashDel() throws Exception {
LogUtils.info("trashDel api test");
apiDefinition = apiDefinitionMapper.selectByPrimaryKey("1001");
if(!apiDefinition.getDeleted()){
if (!apiDefinition.getDeleted()) {
ApiDefinitionDeleteRequest apiDefinitionDeleteRequest = new ApiDefinitionDeleteRequest();
apiDefinitionDeleteRequest.setId(apiDefinition.getId());
apiDefinitionDeleteRequest.setProjectId(DEFAULT_PROJECT_ID);
@ -1146,7 +1164,7 @@ public class ApiDefinitionControllerTests extends BaseTest {
// @@校验日志
apiDefinition = apiDefinitionMapper.selectByPrimaryKey("1006");
if(!apiDefinition.getDeleted()){
if (!apiDefinition.getDeleted()) {
ApiDefinitionDeleteRequest apiDefinitionDeleteRequest = new ApiDefinitionDeleteRequest();
apiDefinitionDeleteRequest.setId(apiDefinition.getId());
apiDefinitionDeleteRequest.setProjectId(DEFAULT_PROJECT_ID);
@ -1188,5 +1206,123 @@ public class ApiDefinitionControllerTests extends BaseTest {
}
}
@Test
@Order(102)
public void testImport() throws Exception {
LogUtils.info("import api test");
ApiDefinitionModule apiDefinitionModule = new ApiDefinitionModule();
apiDefinitionModule.setId("test-import");
apiDefinitionModule.setName("test-import");
apiDefinitionModule.setProjectId(DEFAULT_PROJECT_ID);
apiDefinitionModule.setCreateUser("admin");
apiDefinitionModule.setUpdateUser("admin");
apiDefinitionModule.setPos(1L);
apiDefinitionModule.setCreateTime(System.currentTimeMillis());
apiDefinitionModule.setUpdateTime(System.currentTimeMillis());
apiDefinitionModuleMapper.insertSelective(apiDefinitionModule);
ImportRequest request = new ImportRequest();
request.setProjectId(DEFAULT_PROJECT_ID);
request.setPlatform("Swagger3");
request.setCoverData(true);
request.setCoverModule(true);
request.setModuleId(apiDefinitionModule.getId());
request.setProtocol("HTTP");
request.setUserId("admin");
MultiValueMap<String, Object> paramMap = new LinkedMultiValueMap<>();
paramMap.add("request", JSON.toJSONString(request));
FileInputStream inputStream = new FileInputStream(new File(
this.getClass().getClassLoader().getResource("file/openapi.json")
.getPath()));
MockMultipartFile file = new MockMultipartFile("file", "openapi.json", MediaType.APPLICATION_OCTET_STREAM_VALUE, inputStream);
paramMap.add("file", file);
this.requestMultipartWithOkAndReturn("/api/definition/import", paramMap);
request.setCoverModule(false);
request.setCoverData(false);
this.requestMultipartWithOkAndReturn("/api/definition/import", paramMap);
paramMap.clear();
inputStream = new FileInputStream(new File(
this.getClass().getClassLoader().getResource("file/openapi1.json")
.getPath()));
file = new MockMultipartFile("file", "openapi1.json", MediaType.APPLICATION_OCTET_STREAM_VALUE, inputStream);
paramMap.add("file", file);
request.setCoverModule(true);
request.setCoverData(true);
paramMap.add("request", JSON.toJSONString(request));
this.requestMultipartWithOkAndReturn("/api/definition/import", paramMap);
paramMap.clear();
inputStream = new FileInputStream(new File(
this.getClass().getClassLoader().getResource("file/openapi2.json")
.getPath()));
file = new MockMultipartFile("file", "openapi2.json", MediaType.APPLICATION_OCTET_STREAM_VALUE, inputStream);
paramMap.add("file", file);
request.setCoverModule(false);
request.setCoverData(false);
paramMap.add("request", JSON.toJSONString(request));
this.requestMultipart("/api/definition/import", paramMap, status().is5xxServerError());
paramMap.clear();
inputStream = new FileInputStream(new File(
this.getClass().getClassLoader().getResource("file/openapi3.json")
.getPath()));
file = new MockMultipartFile("file", "openapi3.json", MediaType.APPLICATION_OCTET_STREAM_VALUE, inputStream);
paramMap.add("file", file);
request.setCoverModule(false);
request.setCoverData(false);
paramMap.add("request", JSON.toJSONString(request));
this.requestMultipartWithOkAndReturn("/api/definition/import", paramMap);
paramMap.clear();
paramMap.add("file", file);
request.setCoverModule(false);
request.setCoverData(false);
request.setSwaggerUrl("http://localhost:8080/v2/api-docs");
paramMap.add("request", JSON.toJSONString(request));
this.requestMultipart("/api/definition/import", paramMap, status().is5xxServerError());
}
protected MvcResult requestMultipart(String url, MultiValueMap<String, Object> paramMap, ResultMatcher resultMatcher) throws Exception {
MockMultipartHttpServletRequestBuilder requestBuilder = getMultipartRequestBuilderWithParam(url, paramMap);
MockHttpServletRequestBuilder header = requestBuilder
.header(SessionConstants.HEADER_TOKEN, sessionId)
.header(SessionConstants.CSRF_TOKEN, csrfToken)
.header(HttpHeaders.ACCEPT_LANGUAGE, "zh-CN");
return mockMvc.perform(header)
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
.andExpect(resultMatcher).andReturn();
}
private MockMultipartHttpServletRequestBuilder getMultipartRequestBuilderWithParam(String url, MultiValueMap<String, Object> paramMap) {
MockMultipartHttpServletRequestBuilder requestBuilder =
MockMvcRequestBuilders.multipart(getBasePath() + url);
paramMap.forEach((key, value) -> {
List list = value;
for (Object o : list) {
if (o instanceof List fileList) {
fileList.forEach(o1 -> {
if (o1 instanceof MockMultipartFile mockMultipartFile) {
try {
MockMultipartFile mockMultipartFile1 = null;
mockMultipartFile1 = new MockMultipartFile(key, mockMultipartFile.getOriginalFilename(),
MediaType.APPLICATION_OCTET_STREAM_VALUE, mockMultipartFile.getBytes());
requestBuilder.file(mockMultipartFile1);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
});
} else {
MockMultipartFile multipartFile = null;
multipartFile = new MockMultipartFile(key, null,
MediaType.APPLICATION_JSON_VALUE, o.toString().getBytes());
requestBuilder.file(multipartFile);
}
}
});
return requestBuilder;
}
}

View File

@ -15,6 +15,7 @@ import io.metersphere.api.dto.request.processors.extract.JSONPathExtract;
import io.metersphere.api.dto.request.processors.extract.RegexExtract;
import io.metersphere.api.dto.request.processors.extract.ResultMatchingExtract;
import io.metersphere.api.dto.request.processors.extract.XPathExtract;
import io.metersphere.api.dto.schema.JsonSchemaItem;
import io.metersphere.api.parser.TestElementParser;
import io.metersphere.api.parser.TestElementParserFactory;
import io.metersphere.api.utils.ApiDataUtils;
@ -76,7 +77,7 @@ public class MsHTTPElementTest {
body.setWwwFormBody(wwwFormBody);
JsonBody jsonBody = new JsonBody();
jsonBody.setJsonSchema("{}");
jsonBody.setJsonSchema(new JsonSchemaItem());
jsonBody.setEnableJsonSchema(false);
body.setJsonBody(jsonBody);

View File

@ -16,12 +16,6 @@ import java.util.Objects;
@AutoConfigureMockMvc
public class ParserTests {
@Test
@Order(1)
public void testImportParserSwagger() throws Exception {
Objects.requireNonNull(ImportParserFactory.getImportParser(ApiImportPlatform.Swagger3.name())).parse(null, null);
}
@Test
@Order(2)
public void testImportParserPostman() throws Exception {

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,772 @@
{
"openapi": "3.1.0",
"info": {
"title": "合规问询",
"version": "1.0"
},
"paths": {
"/tasks/start/enquiry": {
"post": {
"summary": "开始问询",
"description": "**Request parameters**\n\n| 参数 | 必须 | 类型 | 说明 |\n|:---|:---|:---|:---|\n|customer_ids|是|list| 问询用户 customer_id 列表|\n|user_ids|是|list| 问询用户 user_ids 列表会查询用户的Omnibus账户|\n|name|是|String|问询名称|\n|type|是|String| 问询类型 CDD ST ONGOING TM|\n|reason|是|String| 问询原因(需要把用户勾选的原因拼起来)|\n|remark|是|String| 备注|\n|restrict|是|boolean| 是否清算 true:是|\n|content|是|json| 问询内容|\n|pre\\_answer_id|否|String| 上次问询answer_id|\n|pre_status|否|String| 上次问询结果UNSUBMIT,SUCCESS,REJECTED|\n\n**Response fields**\n\n\n\n\n\n```json\n\n\n{\n \"status\": \"ok\",\n \"msg\": \"ok\",\n \"timestamp\": 1606477343900\n}\n\n\n\n```\n",
"operationId": "post--tasks-start-enquiry",
"tags": [
"合规问询"
],
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"customer_ids": {
"type": "array",
"x-stoplight": {
"id": "kbjft55jellci"
},
"description": "问询客户Id列表与user_ids二选一",
"items": {
"x-stoplight": {
"id": "48rmyqxcgaenj"
},
"type": "string"
}
},
"user_ids": {
"type": "array",
"x-stoplight": {
"id": "k2vz6vj9bcb1i"
},
"description": "问询用户Id列表与customer_ids二选一",
"items": {
"x-stoplight": {
"id": "sv3rgfiwghwlz"
},
"type": "string"
}
},
"name": {
"type": "string",
"x-stoplight": {
"id": "k0e99u1kks179"
},
"description": "问询名称"
},
"pre_answer_id": {
"type": "string",
"x-stoplight": {
"id": "7fg4vu00xa2xa"
},
"description": "前一个问询id"
},
"type": {
"type": "string",
"x-stoplight": {
"id": "ejbuowi29ntqm"
}
},
"reason": {
"type": "string",
"x-stoplight": {
"id": "mykt2dh4d7c78"
}
},
"pre_answer_status": {
"type": "string",
"x-stoplight": {
"id": "nok8zrvkugh85"
}
},
"content": {
"type": "object",
"x-stoplight": {
"id": "pbk4zgl8aq3vk"
},
"description": "问询内容",
"required": [
"questions"
],
"properties": {
"questions": {
"type": "array",
"x-stoplight": {
"id": "5a0g6bpavd3p9"
},
"items": {
"x-stoplight": {
"id": "tzanfcdtus3zk"
},
"type": "object",
"properties": {
"type": {
"type": "string",
"x-stoplight": {
"id": "dadmpn80vj277"
}
},
"title": {
"type": "object",
"x-stoplight": {
"id": "vxxr5fco5x5qd"
},
"properties": {
"en_US": {
"type": "string",
"x-stoplight": {
"id": "ynddq0wrd59yl"
}
},
"zh_TW": {
"type": "string",
"x-stoplight": {
"id": "gq1soegk1bv1z"
}
},
"zh_CN": {
"type": "string",
"x-stoplight": {
"id": "skxaai7a1tgbn"
}
}
}
}
},
"required": [
"type",
"title"
]
}
},
"": {
"type": "string",
"x-stoplight": {
"id": "lyxhp63oqftol"
}
}
}
}
},
"required": [
"type",
"reason",
"content"
]
}
}
},
"description": ""
},
"responses": {},
"security": [],
"parameters": [
{
"$ref": "#/components/parameters/Authorization"
}
]
}
},
"/tasks/count/enquire": {
"get": {
"summary": "任务计数",
"description": "**权限** \npage:accounts:enquiry\n\n\n**Request parameters**\n\n| 参数 | 必须 | 类型 | 说明 |\n|:---|:---|:---|:---|\n|target|是|String| ACCOUNT|\n\n\n**Response fields**\n\n\n```json\n\n\n{\n \"status\": \"ok\",\n \"msg\": \"ok\",\n \"data\": {\n \"ENQUIRY_CDD\": 10,\n \"ENQUIRY_ECDD\": 140,\n \"ENQUIRY_TM\": 20,\n \"ENQUIRY_ST\": 1,\n \"MINE\": 11\n },\n \"timestamp\": 1620908408415\n}\n\n\n```\n",
"operationId": "get--tasks-count-enquire",
"tags": [
"合规问询"
],
"requestBody": {},
"responses": {
"200": {
"description": "OK",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"status": {
"type": "string",
"x-stoplight": {
"id": "8ef7olao3cjcr"
}
},
"msg": {
"type": "string",
"x-stoplight": {
"id": "cxmgh6dzrif3a"
}
},
"data": {
"type": "object",
"x-stoplight": {
"id": "809qtdhwnaaua"
},
"properties": {
"ENQUIRY_CDD": {
"type": "integer",
"x-stoplight": {
"id": "xmsl5222h8sro"
}
},
"ENQUIRY_ECDD": {
"type": "integer",
"x-stoplight": {
"id": "hgibemphi4r8j"
}
},
"ENQUIRY_TM": {
"type": "integer",
"x-stoplight": {
"id": "12ftglelyzcon"
}
},
"ENQUIRY_ST": {
"type": "integer",
"x-stoplight": {
"id": "3la5n5yagh6nq"
}
},
"ENQUIRY_ONGOING": {
"type": "integer",
"x-stoplight": {
"id": "tagt0zejkzmaa"
}
},
"MINE": {
"type": "integer",
"x-stoplight": {
"id": "3p2iub9ajdblw"
}
}
}
}
}
}
}
}
}
},
"parameters": [
{
"$ref": "#/components/parameters/Authorization"
},
{
"schema": {
"type": "string"
},
"in": "query",
"name": "target",
"description": "审核目标"
}
]
}
},
"/tasks/{answerId}/approve/enquiry/offline": {
"post": {
"summary": "线下通过问询",
"description": "**Request parameters**\n\n| 参数 | 必须 | 类型 | 说明 |\n|:---|:---|:---|:---|\n|answerId|是|String| 问询id|\n|content|是|Object| 问询内容|\n\n\n\n```\n\n{\n \"answerId\": 1,\n \"content\": {\n \"answers\": [\n {\n \"value\": \"第一个问题的回答\"\n },\n {\n \"value\": [\"/file/1.jpg\", \"/file/2.jpg\"]\n }\n ]\n }\n}\n```\n\n**Response fields**\n\n```json\n\n\n{\n \"status\": \"ok\",\n \"msg\": \"ok\",\n \"timestamp\": 1606477343900\n}\n\n\n```\n",
"operationId": "post--tasks-{answerId}-approve-enquiry-offline",
"tags": [
"合规问询"
],
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"answer_id": {
"type": "string",
"x-stoplight": {
"id": "hdza6stym83jj"
}
},
"content": {
"type": "object",
"x-stoplight": {
"id": "308kahe1s8nk0"
},
"properties": {
"answers": {
"type": "array",
"x-stoplight": {
"id": "h309c4jqgkduz"
},
"items": {
"x-stoplight": {
"id": "h87o04ulbgs3e"
},
"type": "object",
"properties": {
"value": {
"type": "string",
"x-stoplight": {
"id": "h0ehc5t1ckc87"
}
}
}
}
}
}
}
}
}
}
},
"description": ""
},
"responses": {},
"parameters": [
{
"$ref": "#/components/parameters/Authorization"
}
]
},
"parameters": [
{
"schema": {
"type": "string"
},
"name": "answerId",
"in": "path",
"required": true,
"description": "问询ID"
}
]
},
"/enquiry/list": {
"get": {
"summary": "问询历史记录列表",
"tags": [],
"responses": {
"200": {
"description": "OK",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"data": {
"type": "object",
"properties": {
"itemCount": {
"type": "number"
},
"items": {
"type": "array",
"items": {
"type": "object",
"properties": {
"customerId": {
"type": "number"
},
"userId": {
"type": "number"
},
"realName": {
"type": "string"
},
"accountId": {
"type": "string"
},
"accountType": {
"type": "string"
},
"enquiryType": {
"type": "string"
},
"answerId": {
"type": "string"
},
"preAnswerId": {
"type": "string"
},
"operator": {
"type": "string"
},
"enquiryStartDate": {
"type": "number"
},
"enquiryDeadline": {
"type": "number"
},
"enquiryReplyDate": {
"type": "number"
}
}
}
}
}
},
"msg": {
"type": "string"
},
"status": {
"type": "string"
}
}
}
}
}
}
},
"operationId": "get-enquiry-list",
"parameters": [
{
"schema": {
"type": "string",
"enum": [
"UNSUBMIT",
"SUCCESS",
"REJECTED",
"PENDING"
]
},
"in": "query",
"name": "enquiryStatus",
"description": "问询状态"
},
{
"schema": {
"type": "string",
"enum": [
"CDD",
"ONGOING",
"TM",
"ST"
]
},
"in": "query",
"name": "enquiryType",
"description": "问询类型"
},
{
"schema": {
"type": "string"
},
"in": "query",
"name": "email",
"description": "邮箱"
},
{
"schema": {
"type": "number"
},
"in": "query",
"name": "userIds",
"description": "支持多个userid逗号隔开"
},
{
"schema": {
"type": "string"
},
"in": "query",
"name": "phone",
"description": "手机号"
},
{
"schema": {
"type": "string"
},
"in": "query",
"name": "telCode",
"description": "区号"
},
{
"schema": {
"type": "number"
},
"in": "query",
"name": "customerId"
},
{
"schema": {
"type": "string"
},
"in": "query",
"name": "accountId",
"description": "账号id"
},
{
"schema": {
"type": "string"
},
"in": "query",
"name": "tag",
"description": "用户标签"
},
{
"schema": {
"type": "string",
"enum": [
"OPENED",
"AUDITING"
]
},
"in": "query",
"name": "accountStatus",
"description": "开户状态"
},
{
"schema": {
"type": "string",
"enum": [
"ENQUIRY_DATE",
"OPEN_DATE",
"APPLICATION_DATE",
"ENQUIRE_DEADLINE_DATE"
]
},
"in": "query",
"name": "dateRangeType",
"description": "时间范围类型"
},
{
"schema": {
"type": "string"
},
"in": "query",
"name": "dateStart",
"description": "开始时间"
},
{
"schema": {
"type": "string"
},
"in": "query",
"name": "dateEnd",
"description": "结束时间"
},
{
"schema": {
"type": "string"
},
"in": "query",
"name": "offset",
"description": "数据偏移量"
},
{
"schema": {
"type": "string"
},
"in": "query",
"name": "limit",
"description": "每页数量"
},
{
"schema": {
"type": "string",
"enum": [
"ASCENDING",
"DESCENDING"
]
},
"in": "query",
"name": "order",
"description": "排序类型"
},
{
"schema": {
"type": "string"
},
"in": "query",
"name": "realName",
"description": "姓名"
},
{
"$ref": "#/components/parameters/Authorization"
}
]
}
},
"/tasks/{taskId}/enquiry": {
"get": {
"summary": "问询详情",
"description": "**Request parameters**\n\n| 参数 | 必须 | 类型 | 说明 |\n|:---|:---|:---|:---|\n|taskId|是|int| 任务ID|\n\n**Response fields**\n\n| 字段 | 类型 | 说明 |\n|:------|:-----|:------|\n| answerId | string | 答案ID |\n| realName | string | 姓名 |\n\n\n```json\n\n\n{\n \"data\": {\n \"accountInfo\": {\n \"userInfo\": {\n \"realName\": \"张三\"\n }\n },\n \"enquiryInfo\": {\n \"answerId\": \"123123\"\n }\n }\n}\n\n```\n",
"operationId": "get--tasks-{taskId}-enquiry",
"tags": [
"合规问询"
],
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {}
}
}
}
},
"responses": {},
"parameters": [
{
"$ref": "#/components/parameters/Authorization"
}
]
},
"parameters": [
{
"schema": {
"type": "integer"
},
"name": "taskId",
"in": "path",
"required": true,
"description": "任务ID"
}
]
},
"/tasks/{taskId}/claim/enquiry": {
"post": {
"summary": "认领任务",
"description": "**权限** \naccounts:enquiry:cdd \naccounts:enquiry:st \naccounts:enquiry:ecdd \naccounts:enquiry:tm \n\n\n**Response fields**\n\n\n```json\n\n\n{\n \"status\": \"ok\",\n \"msg\": \"ok\",\n \"timestamp\": 1606477343900\n}\n\n\n```\n",
"operationId": "post--tasks-{taskId}-claim-enquiry",
"tags": [
"合规问询"
],
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "object"
}
}
}
},
"responses": {},
"parameters": [
{
"$ref": "#/components/parameters/Authorization"
}
]
},
"parameters": [
{
"schema": {
"type": "string"
},
"name": "taskId",
"in": "path",
"required": true,
"description": "任务ID"
}
]
},
"/tasks/{taskId}/abort": {
"post": {
"summary": "放回任务",
"description": "**权限** \naccounts:enquiry:cdd \naccounts:enquiry:st \naccounts:enquiry:ecdd \naccounts:enquiry:tm \n\n**Request parameters**\n\n| 参数 | 必须 | 类型 | 说明 |\n|:---|:---|:---|:---|\n|taskId|是|int| 任务ID|\n\n**Response fields**\n\n\n```json\n\n\n{\n \"status\": \"ok\",\n \"msg\": \"ok\",\n \"timestamp\": 1606477343900\n}\n\n\n```",
"operationId": "post--tasks-{taskId}-abort",
"tags": [
"合规问询"
],
"requestBody": {},
"responses": {},
"parameters": [
{
"$ref": "#/components/parameters/Authorization"
}
]
},
"parameters": [
{
"schema": {
"type": "string"
},
"name": "taskId",
"in": "path",
"required": true
}
]
},
"/tasks/{taskId}/approve/enquiry": {
"post": {
"summary": "问询通过",
"description": "**权限** \naccounts:enquiry:cdd \naccounts:enquiry:st \naccounts:enquiry:ecdd \naccounts:enquiry:tm \n\n**Request parameters**\n\n| 参数 | 必须 | 类型 | 说明 |\n|:---|:---|:---|:---|\n|taskId|是|int| 任务ID|\n\n**Response fields**\n\n\n```json\n\n\n{\n \"status\": \"ok\",\n \"msg\": \"ok\",\n \"timestamp\": 1606477343900\n}\n\n\n```",
"operationId": "post--tasks-{taskId}-approve-enquiry",
"tags": [
"合规问询"
],
"requestBody": {},
"responses": {},
"parameters": [
{
"$ref": "#/components/parameters/Authorization"
}
]
},
"parameters": [
{
"schema": {
"type": "string"
},
"name": "taskId",
"in": "path",
"required": true,
"description": "任务Id"
}
]
},
"/tasks/{taskId}/reject/enquiry": {
"post": {
"summary": "问询打回",
"description": "**权限** \naccounts:enquiry:cdd \naccounts:enquiry:st \naccounts:enquiry:ecdd \naccounts:enquiry:tm \n\n\n**Request parameters**\n\n| 参数 | 必须 | 类型 | 说明 |\n|:---|:---|:---|:---|\n|taskId|是|int| ID|\n|rejectReasons|是|String| 打回理由|\n|comments|否|String| 备注|\n\n**打回原因对应操作**\n\n| 原因短语 | 操作 | \n|:------|:-----|\n| REJECT\\_CUSTOMER | 再次询问/打回用户申请 |\n| REJECT\\_FIRST\\_REVIEW | 打回初审(限制用户未开户) |\n| REJECT\\_OPEN | 拒绝开户 |\n\n\n**Response fields**\n\n```json\n\n\n{\n \"status\": \"ok\",\n \"msg\": \"ok\",\n \"timestamp\": 1606477343900\n}\n\n\n```\n",
"operationId": "post--tasks-{taskId}-reject-enquiry",
"tags": [
"合规问询"
],
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"rejectReasons": {
"type": "string",
"x-stoplight": {
"id": "uqsyov82j5jks"
}
},
"comments": {
"type": "string",
"x-stoplight": {
"id": "rv9d5xuujthmm"
}
}
}
}
}
}
},
"responses": {},
"parameters": [
{
"$ref": "#/components/parameters/Authorization"
}
]
},
"parameters": [
{
"schema": {
"type": "string"
},
"name": "taskId",
"in": "path",
"required": true,
"description": "任务ID"
}
]
}
},
"components": {
"securitySchemes": {},
"parameters": {
"Authorization": {
"name": "Authorization",
"in": "header",
"required": false,
"schema": {
"type": "string"
}
}
}
}
}

View File

@ -0,0 +1 @@
{"openapi":"3.0.1","info":{"title":"个人项目","description":"","version":"1.0.0"},"tags":[],"paths":{"/ad/config_v2":{"post":{"summary":"config_v2","x-apifox-folder":"","x-apifox-status":"developing","deprecated":false,"description":"","tags":[],"parameters":[{"name":"Content-Type","in":"header","description":"","required":true,"example":"application/json; charset=UTF-8","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"disable_personal_recommend":{"type":"integer"},"appid":{"type":"string"},"h_dt":{"type":"integer"},"sdk_ver":{"type":"string"},"h_mac":{"type":"string"},"third_sdk_ver":{"type":"object","properties":{"tencent_sdk":{"type":"string"},"zgtech_sdk":{"type":"string"},"baidu_sdk":{"type":"string"},"tanx_sdk":{"type":"string"},"qumeng_sdk":{"type":"string"},"homemade_sdk":{"type":"string"},"kuaishou_sdk":{"type":"string"},"jd_sdk":{"type":"string"},"xingu_sdk":{"type":"string"},"toutiao_sdk":{"type":"string"},"mimo_sdk":{"type":"string"}},"required":["tencent_sdk","zgtech_sdk","baidu_sdk","tanx_sdk","qumeng_sdk","homemade_sdk","kuaishou_sdk","jd_sdk","xingu_sdk","toutiao_sdk","mimo_sdk"],"x-apifox-orders":["tencent_sdk","zgtech_sdk","baidu_sdk","tanx_sdk","qumeng_sdk","homemade_sdk","kuaishou_sdk","jd_sdk","xingu_sdk","toutiao_sdk","mimo_sdk"],"x-apifox-ignore-properties":[]}},"required":["disable_personal_recommend","appid","h_dt","sdk_ver","h_mac","third_sdk_ver"],"x-apifox-orders":["disable_personal_recommend","appid","h_dt","sdk_ver","h_mac","third_sdk_ver"],"x-apifox-ignore-properties":[]},"example":{"disable_personal_recommend":0,"ext":{"mid":"7466112968446","did":"9e17fa1e40b52024","app_ver":"2.89.10"},"h_carrier":"","h_ids":{"android_id":"9e17fa1e40b52024","oaid":"dfb91568eb92cf4e"},"h_model":"MI 8 Lite","h_os_ver":"10","h_os":"29","resolution":"1080x2154","h_nt":1,"h_did":"9e17fa1e40b52024","manufacturer":"Xiaomi","appid":"100028","h_dt":0,"sdk_ver":"4.0.07.19","h_mac":"70:BB:E9:9C:49:19","third_sdk_ver":{"tencent_sdk":"4.542.1412","zgtech_sdk":"2.2.5-1-20231122","baidu_sdk":"9.324","tanx_sdk":"2.9.3","qumeng_sdk":"3.457.13.422x","homemade_sdk":"1.0.68.6","kuaishou_sdk":"3.3.55","jd_sdk":"2.2.16_20230515","xingu_sdk":"12103_202309211453","toutiao_sdk":"5.6.1.5","mimo_sdk":"5.2.7"}}}}},"responses":{"200":{"description":"成功","content":{"application/json":{"schema":{"type":"object","properties":{},"x-apifox-orders":[],"x-apifox-ignore-properties":[]}}}}},"x-run-in-apifox":"https://www.apifox.cn/web/project/2037509/apis/api-135591150-run","security":[]}}},"components":{"schemas":{},"securitySchemes":{}},"servers":[]}

View File

@ -120,7 +120,7 @@ public class BaseUserRoleService {
put("READ+RECOVER", "permission.recover");
put("READ+EXPORT", "permission.export");
put("READ+EXECUTE", "permission.execute");
put("READ+DEBUG", "permission.download");
put("READ+DEBUG", "permission.debug");
}};
return Translator.get(translationMap.get(permissionKey));
}