From d7927a663a7d8f4daba275d268725c70ca78cbe3 Mon Sep 17 00:00:00 2001 From: AgAngle <1323481023@qq.com> Date: Thu, 8 Aug 2024 20:38:11 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E6=8E=A5=E5=8F=A3=E6=B5=8B=E8=AF=95):=20?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E7=94=A8=E4=BE=8B=E6=89=B9=E9=87=8F=E5=90=8C?= =?UTF-8?q?=E6=AD=A5=E4=BB=A5=E5=8F=8A=E5=BF=BD=E7=95=A5=E5=8F=98=E6=9B=B4?= =?UTF-8?q?=E7=9B=B8=E5=85=B3=E6=97=A5=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --task=1015860 --user=陈建星 【接口测试】接口用例支持同步更新接口变更-后端-批量同步更新接口 https://www.tapd.cn/55049933/s/1559954 --- .../src/main/resources/i18n/api.properties | 3 + .../main/resources/i18n/api_en_US.properties | 5 +- .../main/resources/i18n/api_zh_CN.properties | 5 +- .../main/resources/i18n/api_zh_TW.properties | 5 +- .../definition/ApiTestCaseController.java | 2 + .../api/mapper/ExtApiTestCaseMapper.java | 2 - .../api/mapper/ExtApiTestCaseMapper.xml | 10 --- .../definition/ApiTestCaseLogService.java | 61 ++++++++++++++++++- .../definition/ApiTestCaseService.java | 29 +++++++-- .../metersphere/api/utils/ApiDataUtils.java | 8 +++ .../api/utils/HttpRequestParamDiffUtils.java | 2 +- .../ApiTestCaseControllerTests.java | 10 +++ .../utils/HttpRequestParamDiffUtilsTests.java | 7 +++ 13 files changed, 126 insertions(+), 23 deletions(-) diff --git a/backend/framework/sdk/src/main/resources/i18n/api.properties b/backend/framework/sdk/src/main/resources/i18n/api.properties index 97cbbc5cd3..faef9f6839 100644 --- a/backend/framework/sdk/src/main/resources/i18n/api.properties +++ b/backend/framework/sdk/src/main/resources/i18n/api.properties @@ -445,3 +445,6 @@ api_definition.status.completed=已完成 api_definition.status.abandoned=已废弃 api_definition.status.continuous=连调中 +api_test_case.clear.api_change=忽略本次变更差异 +api_test_case.ignore.api_change=忽略全部变更差异 + diff --git a/backend/framework/sdk/src/main/resources/i18n/api_en_US.properties b/backend/framework/sdk/src/main/resources/i18n/api_en_US.properties index d508f74610..54954161ec 100644 --- a/backend/framework/sdk/src/main/resources/i18n/api_en_US.properties +++ b/backend/framework/sdk/src/main/resources/i18n/api_en_US.properties @@ -449,4 +449,7 @@ report.status.fake_error=Fake error api_definition.status.ongoing=Underway api_definition.status.completed=Completed api_definition.status.abandoned=Abandoned -api_definition.status.continuous=Continuous \ No newline at end of file +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 \ No newline at end of file diff --git a/backend/framework/sdk/src/main/resources/i18n/api_zh_CN.properties b/backend/framework/sdk/src/main/resources/i18n/api_zh_CN.properties index c358b0a1ad..ee13a0110a 100644 --- a/backend/framework/sdk/src/main/resources/i18n/api_zh_CN.properties +++ b/backend/framework/sdk/src/main/resources/i18n/api_zh_CN.properties @@ -417,4 +417,7 @@ report.status.fake_error=误报 api_definition.status.ongoing=进行中 api_definition.status.completed=已完成 api_definition.status.abandoned=已废弃 -api_definition.status.continuous=连调中 \ No newline at end of file +api_definition.status.continuous=连调中 + +api_test_case.clear.api_change=忽略本次变更差异 +api_test_case.ignore.api_change=忽略全部变更差异 \ No newline at end of file diff --git a/backend/framework/sdk/src/main/resources/i18n/api_zh_TW.properties b/backend/framework/sdk/src/main/resources/i18n/api_zh_TW.properties index b0a6b01d13..d214cbb8bb 100644 --- a/backend/framework/sdk/src/main/resources/i18n/api_zh_TW.properties +++ b/backend/framework/sdk/src/main/resources/i18n/api_zh_TW.properties @@ -417,4 +417,7 @@ report.status.fake_error=誤報 api_definition.status.ongoing=進行中 api_definition.status.completed=已完成 api_definition.status.abandoned=已作廢 -api_definition.status.continuous=持續中 \ No newline at end of file +api_definition.status.continuous=持續中 + +api_test_case.clear.api_change=忽略本次變更差異 +api_test_case.ignore.api_change=忽略全部變更差異 \ No newline at end of file diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/controller/definition/ApiTestCaseController.java b/backend/services/api-test/src/main/java/io/metersphere/api/controller/definition/ApiTestCaseController.java index 802b3c09c2..89dc73161f 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/controller/definition/ApiTestCaseController.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/controller/definition/ApiTestCaseController.java @@ -315,6 +315,7 @@ public class ApiTestCaseController { @Operation(summary = "清除接口参数变更标识") @RequiresPermissions(logical = Logical.OR, value = {PermissionConstants.PROJECT_API_DEFINITION_CASE_ADD, PermissionConstants.PROJECT_API_DEFINITION_CASE_UPDATE}) @CheckOwner(resourceId = "#id", resourceType = "api_test_case") + @Log(type = OperationLogType.UPDATE, expression = "#msClass.clearApiChangeLog(#id)", msClass = ApiTestCaseLogService.class) public void clearApiChange(@PathVariable String id) { apiTestCaseService.clearApiChange(id); } @@ -323,6 +324,7 @@ public class ApiTestCaseController { @Operation(summary = "忽略接口变更提示") @RequiresPermissions(logical = Logical.OR, value = {PermissionConstants.PROJECT_API_DEFINITION_CASE_ADD, PermissionConstants.PROJECT_API_DEFINITION_CASE_UPDATE}) @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) { apiTestCaseService.ignoreApiChange(id, ignore); } diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/mapper/ExtApiTestCaseMapper.java b/backend/services/api-test/src/main/java/io/metersphere/api/mapper/ExtApiTestCaseMapper.java index 317e67dfb7..99ca8b93aa 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/mapper/ExtApiTestCaseMapper.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/mapper/ExtApiTestCaseMapper.java @@ -111,6 +111,4 @@ public interface ExtApiTestCaseMapper { List getCaseListBySelectIds(@Param("isRepeat") boolean isRepeat, @Param("projectId") String projectId, @Param("ids") List ids, @Param("testPlanId") String testPlanId, @Param("protocols") List protocols); void setApiChangeByApiDefinitionId(@Param("apiDefinitionId") String apiDefinitionId); - - List getApiCaseForBatchSync(@Param("ids") List ids); } \ No newline at end of file diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/mapper/ExtApiTestCaseMapper.xml b/backend/services/api-test/src/main/java/io/metersphere/api/mapper/ExtApiTestCaseMapper.xml index d3f12079ab..338cc194b5 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/mapper/ExtApiTestCaseMapper.xml +++ b/backend/services/api-test/src/main/java/io/metersphere/api/mapper/ExtApiTestCaseMapper.xml @@ -727,14 +727,4 @@ ) - - - \ No newline at end of file diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiTestCaseLogService.java b/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiTestCaseLogService.java index c7345a57b8..510b179125 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiTestCaseLogService.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiTestCaseLogService.java @@ -61,7 +61,7 @@ public class ApiTestCaseLogService { OperationLogModule.API_TEST_MANAGEMENT_CASE, request.getName()); dto.setMethod(HttpMethodConstants.POST.name()); - dto.setOriginalValue(JSON.toJSONBytes(request)); + dto.setOriginalValue(ApiDataUtils.toJSONBytes(request)); dto.setHistory(true); return dto; } @@ -131,6 +131,36 @@ public class ApiTestCaseLogService { 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) { ApiTestCase apiTestCase = apiTestCaseMapper.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); } + public void batchSyncLog(Map originMap, Map modifiedMap) { + List 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 apiTestCases, String operator, String operationType, boolean isHistory, String logModule) { Project project = projectMapper.selectByPrimaryKey(projectId); //取出apiTestCases所有的id为新的list @@ -241,7 +298,7 @@ public class ApiTestCaseLogService { .sourceId(item.getId()) .content(item.getName()) .createUser(operator) - .originalValue(JSON.toJSONBytes(apiTestCaseDTO)) + .originalValue(ApiDataUtils.toJSONBytes(apiTestCaseDTO)) .build().getLogDTO(); dto.setHistory(isHistory); logs.add(dto); diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiTestCaseService.java b/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiTestCaseService.java index e35531dba1..ff34d5bcf1 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiTestCaseService.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiTestCaseService.java @@ -1005,9 +1005,11 @@ public class ApiTestCaseService extends MoveNodeService { } public void doBatchSyncApiChange(ApiCaseBatchSyncRequest request, List ids, String userId) { - List apiTestCases = extApiTestCaseMapper.getApiCaseForBatchSync(ids); + ApiTestCaseExample example = new ApiTestCaseExample(); + example.createCriteria().andIdIn(ids); + List apiTestCases = apiTestCaseMapper.selectByExample(example); Set apiDefinitionIds = apiTestCases.stream().map(ApiTestCase::getApiDefinitionId).collect(Collectors.toSet()); - Map apiTestCaseMap = apiTestCases.stream().collect(Collectors.toMap(ApiTestCase::getApiDefinitionId, Function.identity())); + SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH); ApiTestCaseBlobMapper apiTestCaseBlobBatchMapper = sqlSession.getMapper(ApiTestCaseBlobMapper.class); ApiTestCaseMapper apiTestCaseBatchMapper = sqlSession.getMapper(ApiTestCaseMapper.class); @@ -1015,15 +1017,27 @@ public class ApiTestCaseService extends MoveNodeService { ApiCaseSyncRequest apiCaseSyncRequest = new ApiCaseSyncRequest(); apiCaseSyncRequest.setSyncItems(request.getSyncItems()); apiCaseSyncRequest.setDeleteRedundantParam(request.getDeleteRedundantParam()); + + Map originMap = new HashMap<>(); + Map modifiedMap = new HashMap<>(); + + ApiDefinitionBlobExample apiDefinitionBlobExample = new ApiDefinitionBlobExample(); + apiDefinitionBlobExample.createCriteria().andIdIn(new ArrayList<>(apiDefinitionIds)); + Map apiDefinitionBlobMap = apiDefinitionBlobMapper.selectByExampleWithBLOBs(apiDefinitionBlobExample) + .stream() + .collect(Collectors.toMap(ApiDefinitionBlob::getId, Function.identity())); try { - for (String apiDefinitionId : apiDefinitionIds) { - ApiDefinitionBlob apiDefinitionBlob = apiDefinitionBlobMapper.selectByPrimaryKey(apiDefinitionId); + for (ApiTestCase apiTestCase : apiTestCases) { + ApiDefinitionBlob apiDefinitionBlob = apiDefinitionBlobMap.get(apiTestCase.getApiDefinitionId()); AbstractMsTestElement apiMsTestElement = getApiMsTestElement(apiDefinitionBlob); - ApiTestCase apiTestCase = apiTestCaseMap.get(apiDefinitionId); ApiTestCaseBlob apiTestCaseBlob = apiTestCaseBlobMapper.selectByPrimaryKey(apiTestCase.getId()); AbstractMsTestElement apiTestCaseMsTestElement = getTestElement(apiTestCaseBlob); boolean requestParamDifferent = HttpRequestParamDiffUtils.isRequestParamDiff(request.getSyncItems(), apiMsTestElement, apiTestCaseMsTestElement); if (requestParamDifferent) { + ApiTestCaseLogDTO originCase = BeanUtils.copyBean(new ApiTestCaseLogDTO(), apiTestCase); + originCase.setRequest(apiTestCaseMsTestElement); + originMap.put(apiTestCase.getId(), originCase); + apiTestCase.setUpdateTime(System.currentTimeMillis()); apiTestCase.setUpdateUser(userId); apiTestCase.setApiChange(false); @@ -1031,8 +1045,13 @@ public class ApiTestCaseService extends MoveNodeService { apiTestCaseMsTestElement = HttpRequestParamDiffUtils.syncRequestDiff(apiCaseSyncRequest, apiMsTestElement, apiTestCaseMsTestElement); apiTestCaseBlob.setRequest(ApiDataUtils.toJSONString(apiTestCaseMsTestElement).getBytes()); apiTestCaseBlobBatchMapper.updateByPrimaryKeySelective(apiTestCaseBlob); + + ApiTestCaseLogDTO modifiedCase = BeanUtils.copyBean(new ApiTestCaseLogDTO(), apiTestCase); + modifiedCase.setRequest(apiTestCaseMsTestElement); + modifiedMap.put(apiTestCase.getId(), originCase); } } + apiTestCaseLogService.batchSyncLog(originMap, modifiedMap); } finally { sqlSession.flushStatements(); SqlSessionUtils.closeSqlSession(sqlSession, sqlSessionFactory); diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/utils/ApiDataUtils.java b/backend/services/api-test/src/main/java/io/metersphere/api/utils/ApiDataUtils.java index e58c7a2bd5..a2b70b5a78 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/utils/ApiDataUtils.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/utils/ApiDataUtils.java @@ -165,4 +165,12 @@ public class ApiDataUtils { throw new MSException(e); } } + + public static byte[] toJSONBytes(Object value) { + try { + return objectMapper.writeValueAsBytes(value); + } catch (IOException e) { + throw new RuntimeException(e); + } + } } diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/utils/HttpRequestParamDiffUtils.java b/backend/services/api-test/src/main/java/io/metersphere/api/utils/HttpRequestParamDiffUtils.java index 15d11f4079..505c2a6be7 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/utils/HttpRequestParamDiffUtils.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/utils/HttpRequestParamDiffUtils.java @@ -67,7 +67,7 @@ public class HttpRequestParamDiffUtils { if (body1 == null || body2 == null) { return true; } - if (body1.getBodyType() != body2.getBodyType()) { + if (!StringUtils.equals(body1.getBodyType(), body2.getBodyType())) { // 类型不一样,则发生变更 return true; } diff --git a/backend/services/api-test/src/test/java/io/metersphere/api/controller/ApiTestCaseControllerTests.java b/backend/services/api-test/src/test/java/io/metersphere/api/controller/ApiTestCaseControllerTests.java index 1e672d8955..286c797a5b 100644 --- a/backend/services/api-test/src/test/java/io/metersphere/api/controller/ApiTestCaseControllerTests.java +++ b/backend/services/api-test/src/test/java/io/metersphere/api/controller/ApiTestCaseControllerTests.java @@ -75,6 +75,7 @@ import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; +import java.text.MessageFormat; import java.util.*; 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.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_UPDATE, API_CHANGE_CLEAR, apiTestCase.getId()); @@ -490,6 +494,9 @@ public class ApiTestCaseControllerTests extends BaseTest { Assertions.assertTrue(result.getIgnoreApiDiff()); 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_UPDATE, API_CHANGE_IGNORE, apiTestCase.getId(), false); @@ -509,6 +516,9 @@ public class ApiTestCaseControllerTests extends BaseTest { ApiTestCase result = apiTestCaseMapper.selectByPrimaryKey(apiTestCase.getId()); 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); } diff --git a/backend/services/api-test/src/test/java/io/metersphere/api/utils/HttpRequestParamDiffUtilsTests.java b/backend/services/api-test/src/test/java/io/metersphere/api/utils/HttpRequestParamDiffUtilsTests.java index f86f7f5978..138840d57d 100644 --- a/backend/services/api-test/src/test/java/io/metersphere/api/utils/HttpRequestParamDiffUtilsTests.java +++ b/backend/services/api-test/src/test/java/io/metersphere/api/utils/HttpRequestParamDiffUtilsTests.java @@ -228,6 +228,13 @@ public class HttpRequestParamDiffUtilsTests { Body body1 = 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()); body2.setBodyType(Body.BodyType.RAW.name()); Assertions.assertTrue(HttpRequestParamDiffUtils.isBodyDiff(body1, body2));