feat(接口测试): 新增导入更新加操作记录

--story=1011790 --user=郭雨琦
https://www.tapd.cn/55049933/prong/stories/view/1155049933001011790
This commit is contained in:
guoyuqi 2023-04-24 14:06:56 +08:00 committed by fit2-zhao
parent 0008fb5e74
commit 0368daed51
9 changed files with 120 additions and 30 deletions

View File

@ -4,6 +4,8 @@ import io.metersphere.api.dto.ApiTestImportRequest;
import io.metersphere.api.dto.mock.config.MockConfigImportDTO;
import io.metersphere.base.domain.ApiDefinitionWithBLOBs;
import io.metersphere.base.domain.ApiTestCaseWithBLOBs;
import io.metersphere.base.mapper.OperatingLogMapper;
import io.metersphere.base.mapper.OperatingLogResourceMapper;
import lombok.Getter;
import lombok.Setter;
@ -19,14 +21,11 @@ public class ApiDefinitionImportParamDTO {
private List<ApiTestCaseWithBLOBs> caseList;
private List<ApiDefinitionWithBLOBs> repeatList;
private String importType;
private String scheduleId;
public ApiDefinitionImportParamDTO() {
}
private OperatingLogMapper operatingLogMapper;
private OperatingLogResourceMapper operatingLogResourceMapper;
public ApiDefinitionImportParamDTO(ApiDefinitionWithBLOBs apiDefinition, ApiTestImportRequest apiTestImportRequest, List<MockConfigImportDTO> mocks, List<ApiDefinitionWithBLOBs> updateList, List<ApiTestCaseWithBLOBs> caseList) {
this.apiDefinition = apiDefinition;

View File

@ -5,6 +5,7 @@ import io.metersphere.api.dto.definition.request.sampler.MsHTTPSamplerProxy;
import io.metersphere.api.dto.definition.request.sampler.MsJDBCSampler;
import io.metersphere.api.dto.definition.request.sampler.MsTCPSampler;
import io.metersphere.api.dto.scenario.Body;
import io.metersphere.api.dto.scenario.KeyValue;
import io.metersphere.commons.constants.ElementConstants;
import io.metersphere.log.utils.ApiDefinitionDiffUtil;
import io.metersphere.log.utils.ReflexObjectUtil;
@ -17,6 +18,7 @@ import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.json.JSONObject;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@ -94,11 +96,12 @@ public class ApiTestDefinitionDiffUtilImpl implements ApiDefinitionDiffUtil {
private static void diffHttp(MsHTTPSamplerProxy httpNew, MsHTTPSamplerProxy httpOld, JsonDiff jsonDiff, Map<String, String> diffMap) {
// 请求头对比 old/new
if (CollectionUtils.isNotEmpty(httpNew.getHeaders()) && CollectionUtils.isNotEmpty(httpOld.getHeaders())) {
httpNew.getHeaders().remove(httpNew.getHeaders().size() - 1);
httpOld.getHeaders().remove(httpOld.getHeaders().size() - 1);
if (CollectionUtils.isNotEmpty(httpNew.getHeaders())) {
removeSpaceName(httpNew.getHeaders());
}
if (CollectionUtils.isNotEmpty(httpOld.getHeaders())) {
removeSpaceName(httpOld.getHeaders());
}
String headerNew = StringUtils.join(JSON_START, JSON.toJSONString(httpNew.getHeaders()), JSON_END);
String headerOld = StringUtils.join(JSON_START, JSON.toJSONString(httpOld.getHeaders()), JSON_END);
if (!StringUtils.equals(headerNew, headerOld)) {
@ -109,9 +112,11 @@ public class ApiTestDefinitionDiffUtilImpl implements ApiDefinitionDiffUtil {
}
}
// 对比QUERY参数
if (CollectionUtils.isNotEmpty(httpNew.getArguments()) && CollectionUtils.isNotEmpty(httpOld.getArguments())) {
httpNew.getArguments().remove(httpNew.getArguments().size() - 1);
httpOld.getArguments().remove(httpOld.getArguments().size() - 1);
if (CollectionUtils.isNotEmpty(httpNew.getArguments())) {
removeSpaceName(httpNew.getArguments());
}
if (CollectionUtils.isNotEmpty(httpOld.getArguments())) {
removeSpaceName(httpOld.getArguments());
}
String queryNew = StringUtils.join(JSON_START, JSON.toJSONString(httpNew.getArguments()), JSON_END);
String queryOld = StringUtils.join(JSON_START, JSON.toJSONString(httpOld.getArguments()), JSON_END);
@ -123,9 +128,11 @@ public class ApiTestDefinitionDiffUtilImpl implements ApiDefinitionDiffUtil {
}
}
// 对比REST参数
if (CollectionUtils.isNotEmpty(httpNew.getRest()) && CollectionUtils.isNotEmpty(httpOld.getRest())) {
httpNew.getRest().remove(httpNew.getRest().size() - 1);
httpOld.getRest().remove(httpOld.getRest().size() - 1);
if (CollectionUtils.isNotEmpty(httpNew.getRest())) {
removeSpaceName(httpNew.getRest());
}
if (CollectionUtils.isNotEmpty(httpOld.getRest())) {
removeSpaceName(httpOld.getRest());
}
String restNew = StringUtils.join(JSON_START, JSON.toJSONString(httpNew.getRest()), JSON_END);
String restOld = StringUtils.join(JSON_START, JSON.toJSONString(httpOld.getRest()), JSON_END);
@ -142,21 +149,23 @@ public class ApiTestDefinitionDiffUtilImpl implements ApiDefinitionDiffUtil {
String bodyOld = JSON.toJSONString(httpOld.getBody());
if (!StringUtils.equals(bodyNew, bodyOld)) {
String patch = jsonDiff.diff(bodyOld, bodyNew);
String diff = jsonDiff.apply(bodyNew, patch);
String diff = jsonDiff.apply(bodyOld, patch);
if (StringUtils.isNotEmpty(diff)) {
diffMap.put(BODY, diff);
}
}
// 对比BODY-FORM参数
if (CollectionUtils.isNotEmpty(httpNew.getBody().getKvs()) && CollectionUtils.isNotEmpty(httpOld.getBody().getKvs())) {
httpNew.getBody().getKvs().remove(httpNew.getBody().getKvs().size() - 1);
httpOld.getBody().getKvs().remove(httpOld.getBody().getKvs().size() - 1);
if (CollectionUtils.isNotEmpty(httpNew.getBody().getKvs())) {
removeSpaceName(httpNew.getBody().getKvs());
}
if (CollectionUtils.isNotEmpty(httpOld.getBody().getKvs())) {
removeSpaceName(httpOld.getBody().getKvs());
}
String bodyFormNew = StringUtils.join(JSON_START, JSON.toJSONString(httpNew.getBody().getKvs()), JSON_END);
String bodyFormOld = StringUtils.join(JSON_START, JSON.toJSONString(httpOld.getBody().getKvs()), JSON_END);
if (!StringUtils.equals(bodyFormNew, bodyFormOld)) {
String patch = jsonDiff.diff(bodyFormOld, bodyFormNew);
String diff = jsonDiff.apply(bodyFormNew, patch);
String diff = jsonDiff.apply(bodyFormOld, patch);
if (StringUtils.isNotEmpty(diff)) {
diffMap.put(BODY_FORM, diff);
}
@ -194,6 +203,16 @@ public class ApiTestDefinitionDiffUtilImpl implements ApiDefinitionDiffUtil {
}
}
private static void removeSpaceName(List<KeyValue> keyValues) {
Iterator<KeyValue> iterator = keyValues.iterator();
while (iterator.hasNext()) {
KeyValue next = iterator.next();
if (StringUtils.isBlank(next.getName())) {
iterator.remove();
}
}
}
private static void diffHttpResponse(JSONObject httpNew, JSONObject httpOld, JsonDiff jsonDiff, Map<String, String> diffMap) {
// 请求头对比 old/new
if (httpNew.get(HEADS) != null && httpOld.get(HEADS) != null) {

View File

@ -8,20 +8,22 @@ import io.metersphere.api.dto.definition.request.sampler.MsTCPSampler;
import io.metersphere.api.dto.mock.config.MockConfigImportDTO;
import io.metersphere.api.parse.api.ApiDefinitionImport;
import io.metersphere.base.domain.*;
import io.metersphere.base.mapper.ApiDefinitionMapper;
import io.metersphere.base.mapper.ApiModuleMapper;
import io.metersphere.base.mapper.ApiTestCaseMapper;
import io.metersphere.base.mapper.ProjectMapper;
import io.metersphere.base.mapper.*;
import io.metersphere.base.mapper.ext.BaseProjectVersionMapper;
import io.metersphere.base.mapper.ext.ExtApiDefinitionMapper;
import io.metersphere.base.mapper.ext.ExtApiTestCaseMapper;
import io.metersphere.commons.constants.*;
import io.metersphere.commons.enums.ApiTestDataStatus;
import io.metersphere.commons.exception.MSException;
import io.metersphere.commons.user.SessionUser;
import io.metersphere.commons.utils.*;
import io.metersphere.dto.ProjectConfig;
import io.metersphere.dto.UserDTO;
import io.metersphere.i18n.Translator;
import io.metersphere.log.utils.ReflexObjectUtil;
import io.metersphere.log.vo.DetailColumn;
import io.metersphere.log.vo.OperatingLogDetails;
import io.metersphere.log.vo.api.DefinitionReference;
import io.metersphere.notice.sender.NoticeModel;
import io.metersphere.notice.service.NoticeSendService;
import io.metersphere.service.BaseProjectApplicationService;
@ -36,6 +38,7 @@ 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.jetbrains.annotations.NotNull;
import org.json.JSONObject;
import org.mybatis.spring.SqlSessionUtils;
import org.springframework.stereotype.Service;
@ -76,6 +79,8 @@ public class ApiDefinitionImportUtilService {
private ApiTestCaseService apiTestCaseService;
@Resource
private BaseUserService baseUserService;
@Resource
private ScheduleMapper scheduleMapper;
public void checkUrl(ApiTestImportRequest request, Project project) {
@ -198,7 +203,8 @@ public class ApiDefinitionImportUtilService {
ApiDefinitionMapper batchMapper = sqlSession.getMapper(ApiDefinitionMapper.class);
ExtApiDefinitionMapper extApiDefinitionMapper = sqlSession.getMapper(ExtApiDefinitionMapper.class);
ApiModuleMapper apiModuleMapper = sqlSession.getMapper(ApiModuleMapper.class);
OperatingLogMapper operatingLogMapper = sqlSession.getMapper(OperatingLogMapper.class);
OperatingLogResourceMapper operatingLogResourceMapper = sqlSession.getMapper(OperatingLogResourceMapper.class);
//系统内需要做更新操作的数据
List<ApiDefinitionWithBLOBs> toUpdateList = new ArrayList<>();
//与当前导入接口重复的系统内所有数据
@ -275,6 +281,10 @@ public class ApiDefinitionImportUtilService {
ApiDefinitionImportParamDTO apiDefinitionImportParam = new ApiDefinitionImportParamDTO(item, request, apiImport.getMocks(), toUpdateList, caseList);
apiDefinitionImportParam.setRepeatList(sameRefIds);
apiDefinitionImportParam.setImportType(request.getType());
apiDefinitionImportParam.setScheduleId(request.getResourceId());
apiDefinitionImportParam.setOperatingLogMapper(operatingLogMapper);
apiDefinitionImportParam.setOperatingLogResourceMapper(operatingLogResourceMapper);
ApiImportSendNoticeDTO apiImportSendNoticeDTO = importCreate(batchMapper, apiDefinitionImportParam);
if (apiImportSendNoticeDTO != null) {
apiImportSendNoticeDTOS.add(apiImportSendNoticeDTO);
@ -1267,6 +1277,8 @@ public class ApiDefinitionImportUtilService {
apiDefinition.setOrder(existApi.getOrder());
reSetImportMocksApiId(mocks, originId, apiDefinition.getId(), apiDefinition.getNum());
batchMapper.updateByPrimaryKeyWithBLOBs(apiDefinition);
//记录导入使得数据产生变更的操作记录
setOperatingLog(existApi, apiDefinition, apiDefinitionImportParamDTO);
ApiDefinitionResult apiDefinitionResult = ApiDefinitionImportUtil.getApiDefinitionResult(apiDefinition, true);
apiImportSendNoticeDTO.setApiDefinitionResult(apiDefinitionResult);
List<ApiTestCaseDTO> apiTestCaseDTOS = importCase(apiDefinition, caseList);
@ -1279,6 +1291,58 @@ public class ApiDefinitionImportUtilService {
return apiImportSendNoticeDTO;
}
private void setOperatingLog(ApiDefinitionWithBLOBs existApi, ApiDefinitionWithBLOBs apiDefinition, ApiDefinitionImportParamDTO apiDefinitionImportParamDTO) {
OperatingLogWithBLOBs msOperLog = new OperatingLogWithBLOBs();
msOperLog.setId(UUID.randomUUID().toString());
msOperLog.setProjectId(existApi.getProjectId());
msOperLog.setOperTitle(existApi.getName());
msOperLog.setOperType(OperLogConstants.UPDATE.toString());
setOperAndCreateUser(apiDefinitionImportParamDTO, msOperLog);
msOperLog.setOperModule(OperLogModule.API_DEFINITION);
msOperLog.setOperMethod("io.metersphere.api.controller.ApiDefinitionController.testCaseImport");
msOperLog.setOperPath("/api/definition/import");
OperatingLogDetails newDetails = getOperatingLogDetails(existApi, apiDefinition);
msOperLog.setOperContent(JSON.toJSONString(newDetails));
msOperLog.setCreateUser(newDetails.getCreateUser());
msOperLog.setOperTime(System.currentTimeMillis());
OperatingLogResource operatingLogResource = new OperatingLogResource();
operatingLogResource.setOperatingLogId(msOperLog.getId());
operatingLogResource.setId(UUID.randomUUID().toString());
operatingLogResource.setSourceId(existApi.getId());
apiDefinitionImportParamDTO.getOperatingLogResourceMapper().insert(operatingLogResource);
apiDefinitionImportParamDTO.getOperatingLogMapper().insert(msOperLog);
}
private void setOperAndCreateUser(ApiDefinitionImportParamDTO apiDefinitionImportParamDTO, OperatingLogWithBLOBs msOperLog) {
if (StringUtils.equals(apiDefinitionImportParamDTO.getImportType(), SCHEDULE)) {
ScheduleExample schedule = new ScheduleExample();
schedule.createCriteria().andResourceIdEqualTo(apiDefinitionImportParamDTO.getScheduleId());
List<Schedule> list = scheduleMapper.selectByExample(schedule);
if (list.size() > 0) {
User user = baseUserService.getUserDTO(list.get(0).getUserId());
msOperLog.setOperUser(user.getName() + Translator.get("timing_synchronization"));
msOperLog.setCreateUser(user.getId());
} else {
msOperLog.setOperUser(Translator.get("timing_synchronization"));
}
} else {
SessionUser user = SessionUtils.getUser();
msOperLog.setOperUser(user.getName() + Translator.get("import_file"));
msOperLog.setCreateUser(user.getId());
}
}
@NotNull
private static OperatingLogDetails getOperatingLogDetails(ApiDefinitionWithBLOBs existApi, ApiDefinitionWithBLOBs apiDefinition) {
List<DetailColumn> oldColumns = ReflexObjectUtil.getColumns(existApi, DefinitionReference.definitionColumns);
OperatingLogDetails oldDetails = new OperatingLogDetails(JSON.toJSONString(existApi.getId()), existApi.getProjectId(), existApi.getName(), existApi.getCreateUser(), oldColumns);
List<DetailColumn> newColumns = ReflexObjectUtil.getColumns(apiDefinition, DefinitionReference.definitionColumns);
OperatingLogDetails newDetails = new OperatingLogDetails(JSON.toJSONString(apiDefinition.getId()), apiDefinition.getProjectId(), apiDefinition.getName(), apiDefinition.getCreateUser(), newColumns);
List<DetailColumn> columns = ReflexObjectUtil.compared(oldDetails, newDetails, OperLogModule.API_DEFINITION);
newDetails.setColumns(columns);
return newDetails;
}
private static List<ApiTestCaseWithBLOBs> setRequestAndAddNewCase(ApiDefinitionWithBLOBs apiDefinition, List<ApiTestCaseWithBLOBs> caseList, boolean newCreate) {
boolean createCase = false;

View File

@ -129,7 +129,9 @@ public class OperatingLogService {
if (CollectionUtils.isEmpty(logWithBLOB.getDetails().getColumns())) {
dtos.add(logWithBLOB);
}
if (StringUtils.isBlank(logWithBLOB.getUserName()) && StringUtils.isNotBlank(logWithBLOB.getOperUser())) {
logWithBLOB.setUserName(logWithBLOB.getOperUser());
}
}
}
if (CollectionUtils.isNotEmpty(dtos)) {

View File

@ -340,7 +340,7 @@ public class JsonDiff {
if (childKey.startsWith("-")) {
instruction.key = childKey.substring(1);
//如果是删除多列 diff数据的key都是-1 会把数据给覆盖所以这里 ke+下标 做新的index
instruction.index = isIndexed(instruction.key) == 1 ? isIndexed(instruction.key) + i : isIndexed(instruction.key);
instruction.index = isIndexed(instruction.key) == 1 ? isIndexed(instruction.key) : isIndexed(instruction.key) + i;
instruction.oper = Oper.DELETE;
} else if (childKey.startsWith("+")) {
instruction.key = childKey.substring(1);

View File

@ -134,7 +134,7 @@ class Leaf implements Comparable<Leaf> {
String key = child.parent.toString();
String reIndexedKey = key;
if (child.parent instanceof ArrNode) {
((ArrNode) child.parent).index = i - deletes;
//((ArrNode) child.parent).index = i - deletes;
reIndexedKey = child.parent.toString();
}
JzonObject insert = factory.createJsonObject();

View File

@ -88,6 +88,8 @@ illegal_xml_format=illegal XML format
api_report_is_null="Report is null, can't update"
api_test_environment_already_exists="Api test environment already exists"
api_test=API Test
import_file=Import File
timing_synchronization=Timing Synchronization
#test case
test_case_node_level=level
test_case_node_level_tip=The node tree maximum depth is

View File

@ -88,6 +88,8 @@ illegal_xml_format=不合法的 XML 格式
api_report_is_null="测试报告是未生成,无法更新"
api_test_environment_already_exists="已存在该名称的环境配置"
api_test=接口测试
import_file=文件导入
timing_synchronization=定时同步
#test case
test_case_node_level=
test_case_node_level_tip=模块树最大深度为

View File

@ -88,6 +88,8 @@ illegal_xml_format=不合法的 XML 格式
api_report_is_null="測試報告是未生成,無法更新"
api_test_environment_already_exists="已存在該名稱的環境配置"
api_test=接口測試
import_file=文件導入
timing_synchronization=定時同步
#test case
test_case_node_level=
test_case_node_level_tip=模塊樹最大深度為