feat(接口测试): 添加用例批量同步以及忽略变更相关日志

--task=1015860 --user=陈建星 【接口测试】接口用例支持同步更新接口变更-后端-批量同步更新接口 https://www.tapd.cn/55049933/s/1559954
This commit is contained in:
AgAngle 2024-08-08 20:38:11 +08:00 committed by Craftsman
parent d7d10f5f05
commit d7927a663a
13 changed files with 126 additions and 23 deletions

View File

@ -445,3 +445,6 @@ api_definition.status.completed=已完成
api_definition.status.abandoned=已废弃 api_definition.status.abandoned=已废弃
api_definition.status.continuous=连调中 api_definition.status.continuous=连调中
api_test_case.clear.api_change=忽略本次变更差异
api_test_case.ignore.api_change=忽略全部变更差异

View File

@ -450,3 +450,6 @@ api_definition.status.ongoing=Underway
api_definition.status.completed=Completed api_definition.status.completed=Completed
api_definition.status.abandoned=Abandoned api_definition.status.abandoned=Abandoned
api_definition.status.continuous=Continuous api_definition.status.continuous=Continuous
api_test_case.clear.api_change=Ignore the differences in this change
api_test_case.ignore.api_change=Ignore all change differences

View File

@ -418,3 +418,6 @@ api_definition.status.ongoing=进行中
api_definition.status.completed=已完成 api_definition.status.completed=已完成
api_definition.status.abandoned=已废弃 api_definition.status.abandoned=已废弃
api_definition.status.continuous=连调中 api_definition.status.continuous=连调中
api_test_case.clear.api_change=忽略本次变更差异
api_test_case.ignore.api_change=忽略全部变更差异

View File

@ -418,3 +418,6 @@ api_definition.status.ongoing=進行中
api_definition.status.completed=已完成 api_definition.status.completed=已完成
api_definition.status.abandoned=已作廢 api_definition.status.abandoned=已作廢
api_definition.status.continuous=持續中 api_definition.status.continuous=持續中
api_test_case.clear.api_change=忽略本次變更差異
api_test_case.ignore.api_change=忽略全部變更差異

View File

@ -315,6 +315,7 @@ public class ApiTestCaseController {
@Operation(summary = "清除接口参数变更标识") @Operation(summary = "清除接口参数变更标识")
@RequiresPermissions(logical = Logical.OR, value = {PermissionConstants.PROJECT_API_DEFINITION_CASE_ADD, PermissionConstants.PROJECT_API_DEFINITION_CASE_UPDATE}) @RequiresPermissions(logical = Logical.OR, value = {PermissionConstants.PROJECT_API_DEFINITION_CASE_ADD, PermissionConstants.PROJECT_API_DEFINITION_CASE_UPDATE})
@CheckOwner(resourceId = "#id", resourceType = "api_test_case") @CheckOwner(resourceId = "#id", resourceType = "api_test_case")
@Log(type = OperationLogType.UPDATE, expression = "#msClass.clearApiChangeLog(#id)", msClass = ApiTestCaseLogService.class)
public void clearApiChange(@PathVariable String id) { public void clearApiChange(@PathVariable String id) {
apiTestCaseService.clearApiChange(id); apiTestCaseService.clearApiChange(id);
} }
@ -323,6 +324,7 @@ public class ApiTestCaseController {
@Operation(summary = "忽略接口变更提示") @Operation(summary = "忽略接口变更提示")
@RequiresPermissions(logical = Logical.OR, value = {PermissionConstants.PROJECT_API_DEFINITION_CASE_ADD, PermissionConstants.PROJECT_API_DEFINITION_CASE_UPDATE}) @RequiresPermissions(logical = Logical.OR, value = {PermissionConstants.PROJECT_API_DEFINITION_CASE_ADD, PermissionConstants.PROJECT_API_DEFINITION_CASE_UPDATE})
@CheckOwner(resourceId = "#id", resourceType = "api_test_case") @CheckOwner(resourceId = "#id", resourceType = "api_test_case")
@Log(type = OperationLogType.UPDATE, expression = "#msClass.ignoreApiChange(#id)", msClass = ApiTestCaseLogService.class)
public void ignoreApiChange(@PathVariable String id, @RequestParam(name = "ignore") boolean ignore) { public void ignoreApiChange(@PathVariable String id, @RequestParam(name = "ignore") boolean ignore) {
apiTestCaseService.ignoreApiChange(id, ignore); apiTestCaseService.ignoreApiChange(id, ignore);
} }

View File

@ -111,6 +111,4 @@ public interface ExtApiTestCaseMapper {
List<ApiTestCase> getCaseListBySelectIds(@Param("isRepeat") boolean isRepeat, @Param("projectId") String projectId, @Param("ids") List<String> ids, @Param("testPlanId") String testPlanId, @Param("protocols") List<String> protocols); List<ApiTestCase> getCaseListBySelectIds(@Param("isRepeat") boolean isRepeat, @Param("projectId") String projectId, @Param("ids") List<String> ids, @Param("testPlanId") String testPlanId, @Param("protocols") List<String> protocols);
void setApiChangeByApiDefinitionId(@Param("apiDefinitionId") String apiDefinitionId); void setApiChangeByApiDefinitionId(@Param("apiDefinitionId") String apiDefinitionId);
List<ApiTestCase> getApiCaseForBatchSync(@Param("ids") List<String> ids);
} }

View File

@ -727,14 +727,4 @@
) )
</if> </if>
</select> </select>
<select id="getApiCaseForBatchSync" resultType="io.metersphere.api.domain.ApiTestCase">
select id, api_definition_id
from api_test_case
where id in
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</select>
</mapper> </mapper>

View File

@ -61,7 +61,7 @@ public class ApiTestCaseLogService {
OperationLogModule.API_TEST_MANAGEMENT_CASE, OperationLogModule.API_TEST_MANAGEMENT_CASE,
request.getName()); request.getName());
dto.setMethod(HttpMethodConstants.POST.name()); dto.setMethod(HttpMethodConstants.POST.name());
dto.setOriginalValue(JSON.toJSONBytes(request)); dto.setOriginalValue(ApiDataUtils.toJSONBytes(request));
dto.setHistory(true); dto.setHistory(true);
return dto; return dto;
} }
@ -131,6 +131,36 @@ public class ApiTestCaseLogService {
return dto; return dto;
} }
public LogDTO clearApiChangeLog(String id) {
ApiTestCase apiTestCase = apiTestCaseMapper.selectByPrimaryKey(id);
Project project = projectMapper.selectByPrimaryKey(apiTestCase.getProjectId());
LogDTO dto = new LogDTO(
apiTestCase.getProjectId(),
project.getOrganizationId(),
id,
null,
OperationLogType.UPDATE.name(),
OperationLogModule.API_TEST_MANAGEMENT_CASE,
Translator.get("api_test_case.clear.api_change") + '_' + apiTestCase.getName());
dto.setOriginalValue(JSON.toJSONBytes(apiTestCase));
return dto;
}
public LogDTO ignoreApiChange(String id) {
ApiTestCase apiTestCase = apiTestCaseMapper.selectByPrimaryKey(id);
Project project = projectMapper.selectByPrimaryKey(apiTestCase.getProjectId());
LogDTO dto = new LogDTO(
apiTestCase.getProjectId(),
project.getOrganizationId(),
id,
null,
OperationLogType.UPDATE.name(),
OperationLogModule.API_TEST_MANAGEMENT_CASE,
Translator.get("api_test_case.ignore.api_change") + '_' + apiTestCase.getName());
dto.setOriginalValue(JSON.toJSONBytes(apiTestCase));
return dto;
}
public LogDTO updateLog(ApiTestCaseUpdateRequest request) { public LogDTO updateLog(ApiTestCaseUpdateRequest request) {
ApiTestCase apiTestCase = apiTestCaseMapper.selectByPrimaryKey(request.getId()); ApiTestCase apiTestCase = apiTestCaseMapper.selectByPrimaryKey(request.getId());
ApiTestCaseBlob apiTestCaseBlob = apiTestCaseBlobMapper.selectByPrimaryKey(request.getId()); ApiTestCaseBlob apiTestCaseBlob = apiTestCaseBlobMapper.selectByPrimaryKey(request.getId());
@ -206,6 +236,33 @@ public class ApiTestCaseLogService {
saveBatchLog(projectId, apiTestCases, operator, OperationLogType.RECOVER.name(), false, OperationLogModule.API_TEST_MANAGEMENT_RECYCLE); saveBatchLog(projectId, apiTestCases, operator, OperationLogType.RECOVER.name(), false, OperationLogModule.API_TEST_MANAGEMENT_RECYCLE);
} }
public void batchSyncLog(Map<String, ApiTestCaseLogDTO> originMap, Map<String, ApiTestCaseLogDTO> modifiedMap) {
List<LogDTO> logs = new ArrayList<>();
originMap.forEach((id, origin) -> {
ApiTestCaseLogDTO modified = modifiedMap.get(id);
if (modified == null) {
return;
}
Project project = projectMapper.selectByPrimaryKey(origin.getProjectId());
LogDTO dto = LogDTOBuilder.builder()
.projectId(project.getId())
.organizationId(project.getOrganizationId())
.type(OperationLogType.UPDATE.name())
.module(OperationLogModule.API_TEST_MANAGEMENT_CASE)
.method(HttpMethodConstants.POST.name())
.sourceId(id)
.content(origin.getName())
.createUser(null)
.path(OperationLogAspect.getPath())
.originalValue(ApiDataUtils.toJSONBytes(origin))
.modifiedValue(ApiDataUtils.toJSONBytes(modified))
.build().getLogDTO();
dto.setHistory(true);
logs.add(dto);
});
operationLogService.batchAdd(logs);
}
private void saveBatchLog(String projectId, List<ApiTestCase> apiTestCases, String operator, String operationType, boolean isHistory, String logModule) { private void saveBatchLog(String projectId, List<ApiTestCase> apiTestCases, String operator, String operationType, boolean isHistory, String logModule) {
Project project = projectMapper.selectByPrimaryKey(projectId); Project project = projectMapper.selectByPrimaryKey(projectId);
//取出apiTestCases所有的id为新的list //取出apiTestCases所有的id为新的list
@ -241,7 +298,7 @@ public class ApiTestCaseLogService {
.sourceId(item.getId()) .sourceId(item.getId())
.content(item.getName()) .content(item.getName())
.createUser(operator) .createUser(operator)
.originalValue(JSON.toJSONBytes(apiTestCaseDTO)) .originalValue(ApiDataUtils.toJSONBytes(apiTestCaseDTO))
.build().getLogDTO(); .build().getLogDTO();
dto.setHistory(isHistory); dto.setHistory(isHistory);
logs.add(dto); logs.add(dto);

View File

@ -1005,9 +1005,11 @@ public class ApiTestCaseService extends MoveNodeService {
} }
public void doBatchSyncApiChange(ApiCaseBatchSyncRequest request, List<String> ids, String userId) { public void doBatchSyncApiChange(ApiCaseBatchSyncRequest request, List<String> ids, String userId) {
List<ApiTestCase> apiTestCases = extApiTestCaseMapper.getApiCaseForBatchSync(ids); ApiTestCaseExample example = new ApiTestCaseExample();
example.createCriteria().andIdIn(ids);
List<ApiTestCase> apiTestCases = apiTestCaseMapper.selectByExample(example);
Set<String> apiDefinitionIds = apiTestCases.stream().map(ApiTestCase::getApiDefinitionId).collect(Collectors.toSet()); Set<String> apiDefinitionIds = apiTestCases.stream().map(ApiTestCase::getApiDefinitionId).collect(Collectors.toSet());
Map<String, ApiTestCase> apiTestCaseMap = apiTestCases.stream().collect(Collectors.toMap(ApiTestCase::getApiDefinitionId, Function.identity()));
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH); SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
ApiTestCaseBlobMapper apiTestCaseBlobBatchMapper = sqlSession.getMapper(ApiTestCaseBlobMapper.class); ApiTestCaseBlobMapper apiTestCaseBlobBatchMapper = sqlSession.getMapper(ApiTestCaseBlobMapper.class);
ApiTestCaseMapper apiTestCaseBatchMapper = sqlSession.getMapper(ApiTestCaseMapper.class); ApiTestCaseMapper apiTestCaseBatchMapper = sqlSession.getMapper(ApiTestCaseMapper.class);
@ -1015,15 +1017,27 @@ public class ApiTestCaseService extends MoveNodeService {
ApiCaseSyncRequest apiCaseSyncRequest = new ApiCaseSyncRequest(); ApiCaseSyncRequest apiCaseSyncRequest = new ApiCaseSyncRequest();
apiCaseSyncRequest.setSyncItems(request.getSyncItems()); apiCaseSyncRequest.setSyncItems(request.getSyncItems());
apiCaseSyncRequest.setDeleteRedundantParam(request.getDeleteRedundantParam()); apiCaseSyncRequest.setDeleteRedundantParam(request.getDeleteRedundantParam());
Map<String, ApiTestCaseLogDTO> originMap = new HashMap<>();
Map<String, ApiTestCaseLogDTO> modifiedMap = new HashMap<>();
ApiDefinitionBlobExample apiDefinitionBlobExample = new ApiDefinitionBlobExample();
apiDefinitionBlobExample.createCriteria().andIdIn(new ArrayList<>(apiDefinitionIds));
Map<String, ApiDefinitionBlob> apiDefinitionBlobMap = apiDefinitionBlobMapper.selectByExampleWithBLOBs(apiDefinitionBlobExample)
.stream()
.collect(Collectors.toMap(ApiDefinitionBlob::getId, Function.identity()));
try { try {
for (String apiDefinitionId : apiDefinitionIds) { for (ApiTestCase apiTestCase : apiTestCases) {
ApiDefinitionBlob apiDefinitionBlob = apiDefinitionBlobMapper.selectByPrimaryKey(apiDefinitionId); ApiDefinitionBlob apiDefinitionBlob = apiDefinitionBlobMap.get(apiTestCase.getApiDefinitionId());
AbstractMsTestElement apiMsTestElement = getApiMsTestElement(apiDefinitionBlob); AbstractMsTestElement apiMsTestElement = getApiMsTestElement(apiDefinitionBlob);
ApiTestCase apiTestCase = apiTestCaseMap.get(apiDefinitionId);
ApiTestCaseBlob apiTestCaseBlob = apiTestCaseBlobMapper.selectByPrimaryKey(apiTestCase.getId()); ApiTestCaseBlob apiTestCaseBlob = apiTestCaseBlobMapper.selectByPrimaryKey(apiTestCase.getId());
AbstractMsTestElement apiTestCaseMsTestElement = getTestElement(apiTestCaseBlob); AbstractMsTestElement apiTestCaseMsTestElement = getTestElement(apiTestCaseBlob);
boolean requestParamDifferent = HttpRequestParamDiffUtils.isRequestParamDiff(request.getSyncItems(), apiMsTestElement, apiTestCaseMsTestElement); boolean requestParamDifferent = HttpRequestParamDiffUtils.isRequestParamDiff(request.getSyncItems(), apiMsTestElement, apiTestCaseMsTestElement);
if (requestParamDifferent) { if (requestParamDifferent) {
ApiTestCaseLogDTO originCase = BeanUtils.copyBean(new ApiTestCaseLogDTO(), apiTestCase);
originCase.setRequest(apiTestCaseMsTestElement);
originMap.put(apiTestCase.getId(), originCase);
apiTestCase.setUpdateTime(System.currentTimeMillis()); apiTestCase.setUpdateTime(System.currentTimeMillis());
apiTestCase.setUpdateUser(userId); apiTestCase.setUpdateUser(userId);
apiTestCase.setApiChange(false); apiTestCase.setApiChange(false);
@ -1031,8 +1045,13 @@ public class ApiTestCaseService extends MoveNodeService {
apiTestCaseMsTestElement = HttpRequestParamDiffUtils.syncRequestDiff(apiCaseSyncRequest, apiMsTestElement, apiTestCaseMsTestElement); apiTestCaseMsTestElement = HttpRequestParamDiffUtils.syncRequestDiff(apiCaseSyncRequest, apiMsTestElement, apiTestCaseMsTestElement);
apiTestCaseBlob.setRequest(ApiDataUtils.toJSONString(apiTestCaseMsTestElement).getBytes()); apiTestCaseBlob.setRequest(ApiDataUtils.toJSONString(apiTestCaseMsTestElement).getBytes());
apiTestCaseBlobBatchMapper.updateByPrimaryKeySelective(apiTestCaseBlob); apiTestCaseBlobBatchMapper.updateByPrimaryKeySelective(apiTestCaseBlob);
ApiTestCaseLogDTO modifiedCase = BeanUtils.copyBean(new ApiTestCaseLogDTO(), apiTestCase);
modifiedCase.setRequest(apiTestCaseMsTestElement);
modifiedMap.put(apiTestCase.getId(), originCase);
} }
} }
apiTestCaseLogService.batchSyncLog(originMap, modifiedMap);
} finally { } finally {
sqlSession.flushStatements(); sqlSession.flushStatements();
SqlSessionUtils.closeSqlSession(sqlSession, sqlSessionFactory); SqlSessionUtils.closeSqlSession(sqlSession, sqlSessionFactory);

View File

@ -165,4 +165,12 @@ public class ApiDataUtils {
throw new MSException(e); throw new MSException(e);
} }
} }
public static byte[] toJSONBytes(Object value) {
try {
return objectMapper.writeValueAsBytes(value);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
} }

View File

@ -67,7 +67,7 @@ public class HttpRequestParamDiffUtils {
if (body1 == null || body2 == null) { if (body1 == null || body2 == null) {
return true; return true;
} }
if (body1.getBodyType() != body2.getBodyType()) { if (!StringUtils.equals(body1.getBodyType(), body2.getBodyType())) {
// 类型不一样则发生变更 // 类型不一样则发生变更
return true; return true;
} }

View File

@ -75,6 +75,7 @@ import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.text.MessageFormat;
import java.util.*; import java.util.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
@ -467,6 +468,9 @@ public class ApiTestCaseControllerTests extends BaseTest {
Assertions.assertFalse(apiTestCaseMapper.selectByPrimaryKey(apiTestCase.getId()).getApiChange()); Assertions.assertFalse(apiTestCaseMapper.selectByPrimaryKey(apiTestCase.getId()).getApiChange());
Assertions.assertTrue(apiTestCaseMapper.selectByPrimaryKey(apiTestCase.getId()).getIgnoreApiDiff()); Assertions.assertTrue(apiTestCaseMapper.selectByPrimaryKey(apiTestCase.getId()).getIgnoreApiDiff());
//校验日志
checkLog(apiTestCase.getId(), OperationLogType.UPDATE, getBasePath() + MessageFormat.format(API_CHANGE_CLEAR, apiTestCase.getId()));
// @@校验权限 // @@校验权限
requestGetPermissionTest(PermissionConstants.PROJECT_API_DEFINITION_CASE_ADD, API_CHANGE_CLEAR, apiTestCase.getId()); requestGetPermissionTest(PermissionConstants.PROJECT_API_DEFINITION_CASE_ADD, API_CHANGE_CLEAR, apiTestCase.getId());
requestGetPermissionTest(PermissionConstants.PROJECT_API_DEFINITION_CASE_UPDATE, API_CHANGE_CLEAR, apiTestCase.getId()); requestGetPermissionTest(PermissionConstants.PROJECT_API_DEFINITION_CASE_UPDATE, API_CHANGE_CLEAR, apiTestCase.getId());
@ -490,6 +494,9 @@ public class ApiTestCaseControllerTests extends BaseTest {
Assertions.assertTrue(result.getIgnoreApiDiff()); Assertions.assertTrue(result.getIgnoreApiDiff());
Assertions.assertFalse(result.getIgnoreApiChange()); Assertions.assertFalse(result.getIgnoreApiChange());
//校验日志
checkLog(apiTestCase.getId(), OperationLogType.UPDATE, getBasePath() + MessageFormat.format(API_CHANGE_IGNORE, apiTestCase.getId(), true));
// @@校验权限 // @@校验权限
requestGetPermissionTest(PermissionConstants.PROJECT_API_DEFINITION_CASE_ADD, API_CHANGE_IGNORE, apiTestCase.getId(), true); requestGetPermissionTest(PermissionConstants.PROJECT_API_DEFINITION_CASE_ADD, API_CHANGE_IGNORE, apiTestCase.getId(), true);
requestGetPermissionTest(PermissionConstants.PROJECT_API_DEFINITION_CASE_UPDATE, API_CHANGE_IGNORE, apiTestCase.getId(), false); requestGetPermissionTest(PermissionConstants.PROJECT_API_DEFINITION_CASE_UPDATE, API_CHANGE_IGNORE, apiTestCase.getId(), false);
@ -509,6 +516,9 @@ public class ApiTestCaseControllerTests extends BaseTest {
ApiTestCase result = apiTestCaseMapper.selectByPrimaryKey(apiTestCase.getId()); ApiTestCase result = apiTestCaseMapper.selectByPrimaryKey(apiTestCase.getId());
Assertions.assertFalse(result.getApiChange()); Assertions.assertFalse(result.getApiChange());
//校验日志
checkLog(apiTestCase.getId(), OperationLogType.UPDATE, getBasePath() + BATCH_API_CHANGE_SYNC);
// @@校验权限 // @@校验权限
requestPostPermissionTest(PermissionConstants.PROJECT_API_DEFINITION_CASE_UPDATE, BATCH_API_CHANGE_SYNC, request); requestPostPermissionTest(PermissionConstants.PROJECT_API_DEFINITION_CASE_UPDATE, BATCH_API_CHANGE_SYNC, request);
} }

View File

@ -228,6 +228,13 @@ public class HttpRequestParamDiffUtilsTests {
Body body1 = new Body(); Body body1 = new Body();
Body body2 = new Body(); Body body2 = new Body();
body1.setBodyType(Body.BodyType.FORM_DATA.name());
body1.setFormDataBody(new FormDataBody());
body2.setBodyType(Body.BodyType.FORM_DATA.name());
body2.setFormDataBody(new FormDataBody());
Assertions.assertFalse(HttpRequestParamDiffUtils.isBodyDiff(body1, body2));
body1.setBodyType(Body.BodyType.FORM_DATA.name()); body1.setBodyType(Body.BodyType.FORM_DATA.name());
body2.setBodyType(Body.BodyType.RAW.name()); body2.setBodyType(Body.BodyType.RAW.name());
Assertions.assertTrue(HttpRequestParamDiffUtils.isBodyDiff(body1, body2)); Assertions.assertTrue(HttpRequestParamDiffUtils.isBodyDiff(body1, body2));