feat(接口测试): 接口列表支持批量复制指定版本的case/mock数据
--story=1010822 --user=宋天阳 接口列表支持批量复制指定版本的case/mock数据 https://www.tapd.cn/55049933/s/1322714
This commit is contained in:
parent
693dc9bbd0
commit
48e52d6d3a
|
@ -0,0 +1,17 @@
|
||||||
|
package io.metersphere.api.dto.definition;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
public class BatchDataCopyRequest {
|
||||||
|
private List<String> ids;
|
||||||
|
private boolean copyCase;
|
||||||
|
private boolean copyMock;
|
||||||
|
private String versionId;
|
||||||
|
|
||||||
|
private ApiDefinitionRequest condition;
|
||||||
|
}
|
|
@ -139,6 +139,13 @@ public class ApiDefinitionController {
|
||||||
apiDefinitionService.deleteByParams(request);
|
apiDefinitionService.deleteByParams(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PostMapping("/copy/by/version")
|
||||||
|
@RequiresPermissions(PermissionConstants.PROJECT_API_DEFINITION_READ_EDIT_API)
|
||||||
|
@MsAuditLog(module = OperLogModule.API_DEFINITION, type = OperLogConstants.UPDATE, beforeEvent = "#msClass.getLogDetails(#request.ids)", title = "#request.name", content = "#msClass.getLogDetails(#request.ids)", msClass = ApiDefinitionService.class)
|
||||||
|
public void copyByVersion(@RequestBody BatchDataCopyRequest request) {
|
||||||
|
apiDefinitionService.copyCaseOrMockByVersion(request);
|
||||||
|
}
|
||||||
|
|
||||||
@PostMapping("/move-gc")
|
@PostMapping("/move-gc")
|
||||||
@RequiresPermissions(PermissionConstants.PROJECT_API_DEFINITION_READ_DELETE_API)
|
@RequiresPermissions(PermissionConstants.PROJECT_API_DEFINITION_READ_DELETE_API)
|
||||||
@MsAuditLog(module = OperLogModule.API_DEFINITION, type = OperLogConstants.GC, beforeEvent = "#msClass.getLogDetails(#ids)", msClass = ApiDefinitionService.class)
|
@MsAuditLog(module = OperLogModule.API_DEFINITION, type = OperLogConstants.GC, beforeEvent = "#msClass.getLogDetails(#ids)", msClass = ApiDefinitionService.class)
|
||||||
|
|
|
@ -87,6 +87,12 @@ public class MockConfigService {
|
||||||
return extMockExpectConfigMapper.selectByApiId(apiId);
|
return extMockExpectConfigMapper.selectByApiId(apiId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<MockExpectConfig> selectSimpleMockExpectConfigByMockConfigId(String mockConfigId) {
|
||||||
|
MockExpectConfigExample example = new MockExpectConfigExample();
|
||||||
|
example.createCriteria().andMockConfigIdEqualTo(mockConfigId);
|
||||||
|
return mockExpectConfigMapper.selectByExample(example);
|
||||||
|
}
|
||||||
|
|
||||||
public List<MockConfigImportDTO> selectMockExpectConfigByApiIdIn(List<String> apiIds) {
|
public List<MockConfigImportDTO> selectMockExpectConfigByApiIdIn(List<String> apiIds) {
|
||||||
if (CollectionUtils.isNotEmpty(apiIds)) {
|
if (CollectionUtils.isNotEmpty(apiIds)) {
|
||||||
List<MockConfigImportDTO> returnDTO = new ArrayList<>();
|
List<MockConfigImportDTO> returnDTO = new ArrayList<>();
|
||||||
|
@ -263,15 +269,22 @@ public class MockConfigService {
|
||||||
return mockExpectConfigMapper.selectByPrimaryKey(model.getId());
|
return mockExpectConfigMapper.selectByPrimaryKey(model.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getMockExpectId(String mockConfigId) {
|
public String getMockExpectId(String mockConfigId) {
|
||||||
List<String> savedExpectNumber = extMockExpectConfigMapper.selectExlectNumByMockConfigId(mockConfigId);
|
List<String> savedExpectNumber = this.selectExpectNumberByConfigId(mockConfigId);
|
||||||
String apiNum = extMockExpectConfigMapper.selectApiNumberByMockConfigId(mockConfigId);
|
String apiNum = extMockExpectConfigMapper.selectApiNumberByMockConfigId(mockConfigId);
|
||||||
|
return this.getMockExpectId(apiNum, savedExpectNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> selectExpectNumberByConfigId(String mockConfigId) {
|
||||||
|
return extMockExpectConfigMapper.selectExlectNumByMockConfigId(mockConfigId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMockExpectId(String apiNum, List<String> savedExpectNumber) {
|
||||||
if (StringUtils.isEmpty(apiNum)) {
|
if (StringUtils.isEmpty(apiNum)) {
|
||||||
apiNum = StringUtils.EMPTY;
|
apiNum = StringUtils.EMPTY;
|
||||||
} else {
|
} else {
|
||||||
apiNum = apiNum + "_";
|
apiNum = apiNum + "_";
|
||||||
}
|
}
|
||||||
|
|
||||||
int index = 1;
|
int index = 1;
|
||||||
for (String expectNum : savedExpectNumber) {
|
for (String expectNum : savedExpectNumber) {
|
||||||
if (StringUtils.startsWith(expectNum, apiNum)) {
|
if (StringUtils.startsWith(expectNum, apiNum)) {
|
||||||
|
@ -1153,7 +1166,7 @@ public class MockConfigService {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private MockConfig selectMockConfigByApiId(String apiId) {
|
public MockConfig selectMockConfigByApiId(String apiId) {
|
||||||
MockConfigExample example = new MockConfigExample();
|
MockConfigExample example = new MockConfigExample();
|
||||||
example.createCriteria().andApiIdEqualTo(apiId);
|
example.createCriteria().andApiIdEqualTo(apiId);
|
||||||
List<MockConfig> mockConfigList = this.mockConfigMapper.selectByExample(example);
|
List<MockConfig> mockConfigList = this.mockConfigMapper.selectByExample(example);
|
||||||
|
|
|
@ -50,6 +50,7 @@ import io.metersphere.service.ext.ExtApiScheduleService;
|
||||||
import io.metersphere.service.ext.ExtFileAssociationService;
|
import io.metersphere.service.ext.ExtFileAssociationService;
|
||||||
import io.metersphere.service.plan.TestPlanApiCaseService;
|
import io.metersphere.service.plan.TestPlanApiCaseService;
|
||||||
import io.metersphere.service.scenario.ApiScenarioService;
|
import io.metersphere.service.scenario.ApiScenarioService;
|
||||||
|
import io.metersphere.utils.BatchProcessingUtil;
|
||||||
import io.metersphere.xpack.api.service.ApiCaseBatchSyncService;
|
import io.metersphere.xpack.api.service.ApiCaseBatchSyncService;
|
||||||
import io.metersphere.xpack.api.service.ApiDefinitionSyncService;
|
import io.metersphere.xpack.api.service.ApiDefinitionSyncService;
|
||||||
import io.metersphere.xpack.quota.service.QuotaService;
|
import io.metersphere.xpack.quota.service.QuotaService;
|
||||||
|
@ -133,6 +134,9 @@ public class ApiDefinitionService {
|
||||||
@Lazy
|
@Lazy
|
||||||
@Resource
|
@Resource
|
||||||
private ApiModuleService apiModuleService;
|
private ApiModuleService apiModuleService;
|
||||||
|
@Lazy
|
||||||
|
@Resource
|
||||||
|
private MockConfigService mockConfigService;
|
||||||
@Resource
|
@Resource
|
||||||
private BaseEnvironmentService apiTestEnvironmentService;
|
private BaseEnvironmentService apiTestEnvironmentService;
|
||||||
@Lazy
|
@Lazy
|
||||||
|
@ -2143,4 +2147,217 @@ public class ApiDefinitionService {
|
||||||
allSourceIdCount = apiExecutionInfoService.countSourceIdByProjectIdIsNull();
|
allSourceIdCount = apiExecutionInfoService.countSourceIdByProjectIdIsNull();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void copyCaseOrMockByVersion(BatchDataCopyRequest request) {
|
||||||
|
if (!request.isCopyCase() && !request.isCopyMock()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (request.getCondition() == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ServiceUtils.getSelectAllIds(request, request.getCondition(), (query) -> extApiDefinitionMapper.selectIds(query));
|
||||||
|
// ServiceUtils.getSelectAllIds(request, request.getCondition(), (query) -> extApiDefinitionMapper.selectIds(query));
|
||||||
|
|
||||||
|
if (StringUtils.isBlank(request.getVersionId()) || CollectionUtils.isEmpty(request.getCondition().getIds())) {
|
||||||
|
MSException.throwException(Translator.get("invalid_parameter"));
|
||||||
|
}
|
||||||
|
request.setIds(request.getCondition().getIds());
|
||||||
|
|
||||||
|
//函数是批量操作,可能会出现数据太多导致的sql过长错误,采用批量处理
|
||||||
|
BatchProcessingUtil.batchProcessingByDataCopy(request, this::batchCopyCaseOrMockByVersion);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void batchCopyCaseOrMockByVersion(BatchDataCopyRequest request) {
|
||||||
|
|
||||||
|
if (sqlSessionFactory != null && CollectionUtils.isNotEmpty(request.getIds())) {
|
||||||
|
Map<String, ApiDefinition> refIdMap = new HashMap<>();
|
||||||
|
List<ApiDefinition> apiDefinitionList = this.selectByIds(request.getIds());
|
||||||
|
apiDefinitionList.forEach(item -> {
|
||||||
|
//过滤到自身的引用
|
||||||
|
if (!StringUtils.equals(item.getVersionId(), request.getVersionId())) {
|
||||||
|
refIdMap.put(item.getRefId(), item);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (MapUtils.isNotEmpty(refIdMap)) {
|
||||||
|
ApiDefinitionExample apiExample = new ApiDefinitionExample();
|
||||||
|
apiExample.createCriteria().andStatusNotEqualTo(ApiTestDataStatus.TRASH.getValue()).andRefIdIn(new ArrayList<>(refIdMap.keySet())).andVersionIdEqualTo(request.getVersionId());
|
||||||
|
List<ApiDefinition> versionApiList = apiDefinitionMapper.selectByExample(apiExample);
|
||||||
|
Map<String, String> sourceApiIdRefIdMap = new HashMap<>();
|
||||||
|
versionApiList.forEach(item -> sourceApiIdRefIdMap.put(item.getId(), item.getRefId()));
|
||||||
|
if (MapUtils.isEmpty(sourceApiIdRefIdMap)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SqlSession batchSqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
|
||||||
|
if (request.isCopyCase()) {
|
||||||
|
this.copyCaseByVersion(request.getIds(), sourceApiIdRefIdMap, refIdMap, batchSqlSession);
|
||||||
|
}
|
||||||
|
if (request.isCopyMock()) {
|
||||||
|
this.copyMockByVersion(sourceApiIdRefIdMap, refIdMap, batchSqlSession);
|
||||||
|
}
|
||||||
|
batchSqlSession.flushStatements();
|
||||||
|
if (sqlSessionFactory != null) {
|
||||||
|
SqlSessionUtils.closeSqlSession(batchSqlSession, sqlSessionFactory);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void copyMockByVersion(Map<String, String> sourceApiIdRefIdMap, Map<String, ApiDefinition> refIdMap, SqlSession batchSqlSession) {
|
||||||
|
long timeStamp = System.currentTimeMillis();
|
||||||
|
MockConfigExample mockConfigExample = new MockConfigExample();
|
||||||
|
mockConfigExample.createCriteria().andApiIdIn(new ArrayList<>(sourceApiIdRefIdMap.keySet()));
|
||||||
|
List<MockConfig> mockConfigList = mockConfigMapper.selectByExample(mockConfigExample);
|
||||||
|
if (CollectionUtils.isNotEmpty(mockConfigList)) {
|
||||||
|
List<String> mockIdList = mockConfigList.stream().map(MockConfig::getId).collect(Collectors.toList());
|
||||||
|
MockExpectConfigExample mockExpectConfigExample = new MockExpectConfigExample();
|
||||||
|
mockExpectConfigExample.createCriteria().andMockConfigIdIn(mockIdList);
|
||||||
|
List<MockExpectConfigWithBLOBs> mockExpectConfigWithBLOBsList = mockExpectConfigMapper.selectByExampleWithBLOBs(mockExpectConfigExample);
|
||||||
|
Map<String, List<MockExpectConfigWithBLOBs>> mockConfigIdExpectMap = mockExpectConfigWithBLOBsList.stream().collect(Collectors.groupingBy(MockExpectConfigWithBLOBs::getMockConfigId));
|
||||||
|
|
||||||
|
List<MockConfig> saveMockList = new ArrayList<>();
|
||||||
|
|
||||||
|
List<MockExpectConfigWithBLOBs> saveMockExpectList = new ArrayList<>();
|
||||||
|
List<MockExpectConfigWithBLOBs> updateMockExpectList = new ArrayList<>();
|
||||||
|
|
||||||
|
mockConfigList.forEach(item -> {
|
||||||
|
String oldApiId = item.getApiId();
|
||||||
|
String refId = sourceApiIdRefIdMap.get(oldApiId);
|
||||||
|
if (StringUtils.isNotBlank(refId)) {
|
||||||
|
ApiDefinition api = refIdMap.get(refId);
|
||||||
|
if (api != null) {
|
||||||
|
MockConfig baseMockConfig = mockConfigService.selectMockConfigByApiId(api.getId());
|
||||||
|
String mockConfigId = UUID.randomUUID().toString();
|
||||||
|
|
||||||
|
Map<String, MockExpectConfig> oldMockExpectConfig = new HashMap<>();
|
||||||
|
//已经存储的mock期望编号
|
||||||
|
List<String> saveExpectNumList = new ArrayList<>();
|
||||||
|
|
||||||
|
if (baseMockConfig == null) {
|
||||||
|
MockConfig mockConfig = new MockConfig();
|
||||||
|
BeanUtils.copyBean(mockConfig, item);
|
||||||
|
mockConfig.setApiId(api.getId());
|
||||||
|
mockConfig.setId(mockConfigId);
|
||||||
|
mockConfig.setCreateTime(timeStamp);
|
||||||
|
mockConfig.setUpdateTime(timeStamp);
|
||||||
|
saveMockList.add(mockConfig);
|
||||||
|
} else {
|
||||||
|
mockConfigId = baseMockConfig.getId();
|
||||||
|
saveExpectNumList = mockConfigService.selectExpectNumberByConfigId(mockConfigId);
|
||||||
|
List<MockExpectConfig> oldMockExpectList = mockConfigService.selectSimpleMockExpectConfigByMockConfigId(mockConfigId);
|
||||||
|
oldMockExpectList.forEach(mockExpectConfig -> {
|
||||||
|
oldMockExpectConfig.put(StringUtils.trim(mockExpectConfig.getName()), mockExpectConfig);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
List<MockExpectConfigWithBLOBs> mockExpectConfigList = mockConfigIdExpectMap.get(item.getId());
|
||||||
|
if (CollectionUtils.isNotEmpty(mockExpectConfigList)) {
|
||||||
|
String finalMockConfigId = mockConfigId;
|
||||||
|
List<String> finalSaveExpectNumList = saveExpectNumList;
|
||||||
|
mockExpectConfigList.forEach(mockExpectConfigWithBLOBs -> {
|
||||||
|
MockExpectConfig oldExpect = oldMockExpectConfig.get(StringUtils.trim(mockExpectConfigWithBLOBs.getName()));
|
||||||
|
MockExpectConfigWithBLOBs expectConfigWithBLOBs = new MockExpectConfigWithBLOBs();
|
||||||
|
BeanUtils.copyBean(expectConfigWithBLOBs, mockExpectConfigWithBLOBs);
|
||||||
|
if (oldExpect == null) {
|
||||||
|
String newMockExpectNum = mockConfigService.getMockExpectId(String.valueOf(api.getNum()), finalSaveExpectNumList);
|
||||||
|
finalSaveExpectNumList.add(newMockExpectNum);
|
||||||
|
|
||||||
|
expectConfigWithBLOBs.setId(UUID.randomUUID().toString());
|
||||||
|
expectConfigWithBLOBs.setExpectNum(newMockExpectNum);
|
||||||
|
expectConfigWithBLOBs.setCreateTime(timeStamp);
|
||||||
|
expectConfigWithBLOBs.setUpdateTime(timeStamp);
|
||||||
|
expectConfigWithBLOBs.setMockConfigId(finalMockConfigId);
|
||||||
|
saveMockExpectList.add(expectConfigWithBLOBs);
|
||||||
|
} else {
|
||||||
|
expectConfigWithBLOBs.setId(oldExpect.getId());
|
||||||
|
expectConfigWithBLOBs.setCreateTime(oldExpect.getCreateTime());
|
||||||
|
expectConfigWithBLOBs.setUpdateTime(timeStamp);
|
||||||
|
updateMockExpectList.add(expectConfigWithBLOBs);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (CollectionUtils.isNotEmpty(saveMockList)) {
|
||||||
|
MockConfigMapper mockConfigBatchMapper = batchSqlSession.getMapper(MockConfigMapper.class);
|
||||||
|
saveMockList.forEach(mockConfigBatchMapper::insert);
|
||||||
|
}
|
||||||
|
MockExpectConfigMapper mockExpectConfigBatchMapper = batchSqlSession.getMapper(MockExpectConfigMapper.class);
|
||||||
|
if (CollectionUtils.isNotEmpty(saveMockExpectList)) {
|
||||||
|
saveMockExpectList.forEach(mockExpectConfigBatchMapper::insert);
|
||||||
|
}
|
||||||
|
if (CollectionUtils.isNotEmpty(updateMockExpectList)) {
|
||||||
|
updateMockExpectList.forEach(mockExpectConfigBatchMapper::updateByPrimaryKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void copyCaseByVersion(List<String> chooseApiIdList, Map<String, String> sourceApiIdRefIdMap, Map<String, ApiDefinition> refIdMap, SqlSession batchSqlSession) {
|
||||||
|
long timeStamp = System.currentTimeMillis();
|
||||||
|
List<ApiTestCaseWithBLOBs> sourceApiCaseList = apiTestCaseService.selectCasesBydApiIds(new ArrayList<>(sourceApiIdRefIdMap.keySet()));
|
||||||
|
List<ApiTestCase> caseInChooseApi = apiTestCaseService.selectSimpleCasesBydApiIds(chooseApiIdList);
|
||||||
|
Map<String, Map<String, ApiTestCase>> apiIdOldCaseMap = new HashMap<>();
|
||||||
|
caseInChooseApi.forEach(item -> {
|
||||||
|
String caseName = StringUtils.trim(item.getName());
|
||||||
|
if (StringUtils.isNotBlank(caseName)) {
|
||||||
|
if (apiIdOldCaseMap.containsKey(item.getApiDefinitionId())) {
|
||||||
|
apiIdOldCaseMap.get(item.getApiDefinitionId()).put(caseName, item);
|
||||||
|
} else {
|
||||||
|
apiIdOldCaseMap.put(item.getApiDefinitionId(), new HashMap<>() {{
|
||||||
|
this.put(caseName, item);
|
||||||
|
}});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
List<ApiTestCaseWithBLOBs> saveCaseList = new ArrayList<>();
|
||||||
|
List<ApiTestCaseWithBLOBs> updateCaseList = new ArrayList<>();
|
||||||
|
Map<String, Integer> lastCaseNumMap = new LinkedHashMap<>();
|
||||||
|
sourceApiCaseList.forEach(item -> {
|
||||||
|
String oldApiId = item.getApiDefinitionId();
|
||||||
|
String refId = sourceApiIdRefIdMap.get(oldApiId);
|
||||||
|
if (StringUtils.isNotBlank(refId)) {
|
||||||
|
ApiDefinition api = refIdMap.get(refId);
|
||||||
|
if (api != null) {
|
||||||
|
//通过用例名称检查是否需要覆盖
|
||||||
|
ApiTestCase oldCase = null;
|
||||||
|
if (apiIdOldCaseMap.containsKey(api.getId())) {
|
||||||
|
oldCase = apiIdOldCaseMap.get(api.getId()).get(StringUtils.trim(item.getName()));
|
||||||
|
}
|
||||||
|
ApiTestCaseWithBLOBs newCase = new ApiTestCaseWithBLOBs();
|
||||||
|
BeanUtils.copyBean(newCase, item);
|
||||||
|
newCase.setApiDefinitionId(api.getId());
|
||||||
|
newCase.setVersionId(api.getVersionId());
|
||||||
|
if (oldCase == null) {
|
||||||
|
int lastCaseNum = 0;
|
||||||
|
if (lastCaseNumMap.containsKey(api.getId())) {
|
||||||
|
lastCaseNum = lastCaseNumMap.get(api.getId());
|
||||||
|
} else {
|
||||||
|
lastCaseNum = apiTestCaseService.getNextNum(api.getId());
|
||||||
|
}
|
||||||
|
int caseNum = apiTestCaseService.getNextNum(lastCaseNum);
|
||||||
|
newCase.setNum(caseNum);
|
||||||
|
newCase.setId(UUID.randomUUID().toString());
|
||||||
|
newCase.setCreateTime(timeStamp);
|
||||||
|
newCase.setUpdateTime(timeStamp);
|
||||||
|
|
||||||
|
lastCaseNumMap.put(api.getId(), caseNum);
|
||||||
|
saveCaseList.add(newCase);
|
||||||
|
} else {
|
||||||
|
newCase.setId(oldCase.getId());
|
||||||
|
newCase.setNum(oldCase.getNum());
|
||||||
|
newCase.setCreateTime(oldCase.getCreateTime());
|
||||||
|
newCase.setUpdateTime(timeStamp);
|
||||||
|
updateCaseList.add(newCase);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
ApiTestCaseMapper apiTestCaseBatchMapper = batchSqlSession.getMapper(ApiTestCaseMapper.class);
|
||||||
|
if (CollectionUtils.isNotEmpty(saveCaseList)) {
|
||||||
|
saveCaseList.forEach(apiTestCaseBatchMapper::insert);
|
||||||
|
}
|
||||||
|
if (CollectionUtils.isNotEmpty(updateCaseList)) {
|
||||||
|
updateCaseList.forEach(apiTestCaseBatchMapper::updateByPrimaryKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -480,15 +480,19 @@ public class ApiTestCaseService {
|
||||||
|
|
||||||
public int getNextNum(String definitionId) {
|
public int getNextNum(String definitionId) {
|
||||||
ApiTestCase apiTestCase = extApiTestCaseMapper.getNextNum(definitionId);
|
ApiTestCase apiTestCase = extApiTestCaseMapper.getNextNum(definitionId);
|
||||||
ApiDefinitionWithBLOBs apiDefinitionWithBLOBs = apiDefinitionMapper.selectByPrimaryKey(definitionId);
|
|
||||||
if (apiTestCase == null) {
|
if (apiTestCase == null) {
|
||||||
|
ApiDefinitionWithBLOBs apiDefinitionWithBLOBs = apiDefinitionMapper.selectByPrimaryKey(definitionId);
|
||||||
int n = apiDefinitionWithBLOBs.getNum();
|
int n = apiDefinitionWithBLOBs.getNum();
|
||||||
return n * 1000 + 1;
|
return n * 1000 + 1;
|
||||||
} else {
|
} else {
|
||||||
return Optional.of(apiTestCase.getNum() + 1).orElse(apiDefinitionWithBLOBs.getNum() * 1000 + 1);
|
return this.getNextNum(apiTestCase.getNum());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getNextNum(Integer lastApiCaseNum) {
|
||||||
|
return Optional.of(lastApiCaseNum + 1).orElse(lastApiCaseNum * 1000 + 1);
|
||||||
|
}
|
||||||
|
|
||||||
public int getNextNum(String definitionId, Integer definitionNum, String projectId) {
|
public int getNextNum(String definitionId, Integer definitionNum, String projectId) {
|
||||||
ApiTestCase apiTestCase = extApiTestCaseMapper.getNextNum(definitionId);
|
ApiTestCase apiTestCase = extApiTestCaseMapper.getNextNum(definitionId);
|
||||||
ApiDefinitionWithBLOBs apiDefinitionWithBLOBs = apiDefinitionMapper.selectByPrimaryKey(definitionId);
|
ApiDefinitionWithBLOBs apiDefinitionWithBLOBs = apiDefinitionMapper.selectByPrimaryKey(definitionId);
|
||||||
|
@ -607,6 +611,12 @@ public class ApiTestCaseService {
|
||||||
return apiTestCaseMapper.selectByExampleWithBLOBs(example);
|
return apiTestCaseMapper.selectByExampleWithBLOBs(example);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<ApiTestCase> selectSimpleCasesBydApiIds(List<String> apiIds) {
|
||||||
|
ApiTestCaseExample example = new ApiTestCaseExample();
|
||||||
|
example.createCriteria().andApiDefinitionIdIn(apiIds).andStatusNotEqualTo("Trash");
|
||||||
|
return apiTestCaseMapper.selectByExample(example);
|
||||||
|
}
|
||||||
|
|
||||||
public Map<String, String> getRequest(ApiTestCaseRequest request) {
|
public Map<String, String> getRequest(ApiTestCaseRequest request) {
|
||||||
List<ApiTestCaseInfo> list = extApiTestCaseMapper.getRequest(request);
|
List<ApiTestCaseInfo> list = extApiTestCaseMapper.getRequest(request);
|
||||||
return list.stream().collect(Collectors.toMap(ApiTestCaseWithBLOBs::getId, ApiTestCaseWithBLOBs::getRequest));
|
return list.stream().collect(Collectors.toMap(ApiTestCaseWithBLOBs::getId, ApiTestCaseWithBLOBs::getRequest));
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
package io.metersphere.utils;
|
||||||
|
|
||||||
|
import io.metersphere.api.dto.definition.BatchDataCopyRequest;
|
||||||
|
import io.metersphere.commons.utils.BeanUtils;
|
||||||
|
import org.apache.commons.collections.CollectionUtils;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量处理工具
|
||||||
|
*/
|
||||||
|
public class BatchProcessingUtil {
|
||||||
|
|
||||||
|
private static final int BATCH_PROCESS_QUANTITY = 1000;
|
||||||
|
|
||||||
|
public static void batchProcessingByDataCopy(BatchDataCopyRequest paramRequest, Consumer<BatchDataCopyRequest> func) {
|
||||||
|
List<String> paramList = paramRequest.getIds();
|
||||||
|
if (CollectionUtils.isNotEmpty(paramList)) {
|
||||||
|
BatchDataCopyRequest queryRequest = new BatchDataCopyRequest();
|
||||||
|
BeanUtils.copyBean(queryRequest, paramRequest);
|
||||||
|
int unProcessingCount = paramList.size();
|
||||||
|
while (paramList.size() > BATCH_PROCESS_QUANTITY) {
|
||||||
|
List<String> processingList = new ArrayList<>();
|
||||||
|
for (int i = 0; i < BATCH_PROCESS_QUANTITY; i++) {
|
||||||
|
processingList.add(paramList.get(i));
|
||||||
|
}
|
||||||
|
//函数处理
|
||||||
|
queryRequest.setIds(processingList);
|
||||||
|
func.accept(queryRequest);
|
||||||
|
|
||||||
|
paramList.removeAll(processingList);
|
||||||
|
if (paramList.size() == unProcessingCount) {
|
||||||
|
//如果剩余数量没有发生变化,则跳出循环。防止出现死循环的情况
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
unProcessingCount = paramList.size();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CollectionUtils.isNotEmpty(paramList)) {
|
||||||
|
//剩余待处理数据进行处理
|
||||||
|
queryRequest.setIds(paramList);
|
||||||
|
func.accept(queryRequest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
import { fileDownload, fileUpload } from '@/api/base-network';
|
import { fileUpload } from '@/api/base-network';
|
||||||
import { get, post } from 'metersphere-frontend/src/plugins/request';
|
import { get, post } from 'metersphere-frontend/src/plugins/request';
|
||||||
|
|
||||||
export function getRelationshipCountApi(id) {
|
export function getRelationshipCountApi(id) {
|
||||||
|
@ -137,6 +137,10 @@ export function removeToGcByParams(params) {
|
||||||
return post('/api/definition/move-gc-batch', params);
|
return post('/api/definition/move-gc-batch', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function copyDataByVersion(params) {
|
||||||
|
return post('/api/definition/copy/by/version', params);
|
||||||
|
}
|
||||||
|
|
||||||
export function removeToGcByIds(params) {
|
export function removeToGcByIds(params) {
|
||||||
return post('/api/definition/move-gc', params);
|
return post('/api/definition/move-gc', params);
|
||||||
}
|
}
|
||||||
|
|
|
@ -225,6 +225,8 @@
|
||||||
:data-count="$refs.table ? $refs.table.selectDataCounts : 0"
|
:data-count="$refs.table ? $refs.table.selectDataCounts : 0"
|
||||||
:typeArr="typeArr"
|
:typeArr="typeArr"
|
||||||
:value-arr="valueArr" />
|
:value-arr="valueArr" />
|
||||||
|
<!--从指定版本复制数据-->
|
||||||
|
<version-selector @handleSave="handleCopyDataFromVersion" ref="versionSelector" />
|
||||||
<!--高级搜索-->
|
<!--高级搜索-->
|
||||||
<ms-table-adv-search-bar :condition.sync="condition" :showLink="false" ref="searchBar" @search="search" />
|
<ms-table-adv-search-bar :condition.sync="condition" :showLink="false" ref="searchBar" @search="search" />
|
||||||
<case-batch-move @refresh="initTable" @moveSave="moveSave" ref="testCaseBatchMove" />
|
<case-batch-move @refresh="initTable" @moveSave="moveSave" ref="testCaseBatchMove" />
|
||||||
|
@ -239,6 +241,7 @@
|
||||||
import {
|
import {
|
||||||
batchCopyByParams,
|
batchCopyByParams,
|
||||||
batchEditByParams,
|
batchEditByParams,
|
||||||
|
copyDataByVersion,
|
||||||
definitionReduction,
|
definitionReduction,
|
||||||
delDefinition,
|
delDefinition,
|
||||||
delDefinitionByRefId,
|
delDefinitionByRefId,
|
||||||
|
@ -292,6 +295,7 @@ import { getGraphByCondition } from '@/api/graph';
|
||||||
import ListItemDeleteConfirm from 'metersphere-frontend/src/components/ListItemDeleteConfirm';
|
import ListItemDeleteConfirm from 'metersphere-frontend/src/components/ListItemDeleteConfirm';
|
||||||
import MsSearch from 'metersphere-frontend/src/components/search/MsSearch';
|
import MsSearch from 'metersphere-frontend/src/components/search/MsSearch';
|
||||||
import { buildNodePath } from 'metersphere-frontend/src/model/NodeTree';
|
import { buildNodePath } from 'metersphere-frontend/src/model/NodeTree';
|
||||||
|
import VersionSelector from '@/business/definition/components/version/VersionSelector';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ApiList',
|
name: 'ApiList',
|
||||||
|
@ -316,6 +320,7 @@ export default {
|
||||||
MsTable,
|
MsTable,
|
||||||
MsTableColumn,
|
MsTableColumn,
|
||||||
MsSearch,
|
MsSearch,
|
||||||
|
VersionSelector,
|
||||||
MsApiReportStatus: () => import('../../../automation/report/ApiReportStatus'),
|
MsApiReportStatus: () => import('../../../automation/report/ApiReportStatus'),
|
||||||
MxRelationshipGraphDrawer: () => import('metersphere-frontend/src/components/graph/MxRelationshipGraphDrawer'),
|
MxRelationshipGraphDrawer: () => import('metersphere-frontend/src/components/graph/MxRelationshipGraphDrawer'),
|
||||||
},
|
},
|
||||||
|
@ -357,6 +362,12 @@ export default {
|
||||||
handleClick: this.handleBatchCopy,
|
handleClick: this.handleBatchCopy,
|
||||||
permissions: ['PROJECT_API_DEFINITION:READ+CREATE_API'],
|
permissions: ['PROJECT_API_DEFINITION:READ+CREATE_API'],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: this.$t('api_definition.copy_data_from_other_version'),
|
||||||
|
isXPack: true,
|
||||||
|
handleClick: this.batchCopyDataFromVersion,
|
||||||
|
permissions: ['PROJECT_API_DEFINITION:READ+EDIT_API'],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: this.$t('test_track.case.generate_dependencies'),
|
name: this.$t('test_track.case.generate_dependencies'),
|
||||||
isXPack: true,
|
isXPack: true,
|
||||||
|
@ -646,6 +657,11 @@ export default {
|
||||||
this.isMoveBatch = false;
|
this.isMoveBatch = false;
|
||||||
this.$refs.testCaseBatchMove.open(this.moduleTree, this.$refs.table.selectIds, this.moduleOptionsNew);
|
this.$refs.testCaseBatchMove.open(this.moduleTree, this.$refs.table.selectIds, this.moduleOptionsNew);
|
||||||
},
|
},
|
||||||
|
batchCopyDataFromVersion() {
|
||||||
|
if (this.$refs.versionSelector) {
|
||||||
|
this.$refs.versionSelector.open(this.projectId);
|
||||||
|
}
|
||||||
|
},
|
||||||
closeCaseModel() {
|
closeCaseModel() {
|
||||||
//关闭案例弹窗
|
//关闭案例弹窗
|
||||||
if (this.$refs.caseList) {
|
if (this.$refs.caseList) {
|
||||||
|
@ -849,7 +865,7 @@ export default {
|
||||||
this.search();
|
this.search();
|
||||||
},
|
},
|
||||||
search() {
|
search() {
|
||||||
this.$EventBus.$emit("apiConditionBus", this.condition)
|
this.$EventBus.$emit('apiConditionBus', this.condition);
|
||||||
this.changeSelectDataRangeAll();
|
this.changeSelectDataRangeAll();
|
||||||
this.initTable();
|
this.initTable();
|
||||||
},
|
},
|
||||||
|
@ -1181,6 +1197,35 @@ export default {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
//从指定版本拷贝数据
|
||||||
|
handleCopyDataFromVersion(selectVersionId, copyCase, copyMock) {
|
||||||
|
let copyParam = {};
|
||||||
|
// let param = {};
|
||||||
|
// if (vueObj.selectRows) {
|
||||||
|
// param.ids = selectIds ? selectIds : Array.from(vueObj.selectRows).map(row => row.id);
|
||||||
|
// } else {
|
||||||
|
// param.ids = selectIds;
|
||||||
|
// }
|
||||||
|
// param.projectId = projectId ? projectId : getCurrentProjectID();
|
||||||
|
// param.condition = vueObj.condition;
|
||||||
|
// return param;
|
||||||
|
|
||||||
|
// copyParam.versionId = selectVersionId;
|
||||||
|
copyParam.copyCase = copyCase;
|
||||||
|
copyParam.copyMock = copyMock;
|
||||||
|
copyParam.versionId = selectVersionId;
|
||||||
|
copyParam.condition = this.condition;
|
||||||
|
copyParam.condition.ids = this.$refs.table.selectIds;
|
||||||
|
copyParam.condition.protocol = this.currentProtocol;
|
||||||
|
|
||||||
|
copyDataByVersion(copyParam).then(() => {
|
||||||
|
this.$success(this.$t('commons.copy_success'));
|
||||||
|
if (this.$refs.versionSelector) {
|
||||||
|
this.$refs.versionSelector.handleClose();
|
||||||
|
}
|
||||||
|
this.initTable();
|
||||||
|
});
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -0,0 +1,92 @@
|
||||||
|
<template>
|
||||||
|
<ms-edit-dialog
|
||||||
|
:visible.sync="visible"
|
||||||
|
width="300px"
|
||||||
|
:title="$t('commons.delete_all_version')"
|
||||||
|
:with-footer="false"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
@close="handleClose">
|
||||||
|
<el-row>
|
||||||
|
<div v-loading="loading">
|
||||||
|
<el-row>
|
||||||
|
<el-select v-model="versionId" size="small" :placeholder="$t('project.version.please_input_version')">
|
||||||
|
<el-option v-for="v in versionData" :key="v.id" :label="v.name" :value="v.id" />
|
||||||
|
</el-select>
|
||||||
|
</el-row>
|
||||||
|
<el-row style="margin-top: 10px">
|
||||||
|
<el-checkbox v-model="selectCase" v-permission="['PROJECT_API_DEFINITION:READ+CREATE_CASE']">{{ $t('commons.api_case') }}</el-checkbox>
|
||||||
|
<el-checkbox v-model="selectMock">{{ $t('commons.mock') }}</el-checkbox>
|
||||||
|
</el-row>
|
||||||
|
</div>
|
||||||
|
</el-row>
|
||||||
|
<template v-slot:footer>
|
||||||
|
<el-button type="primary" :loading="saving" size="small" @click="save" @keydown.enter.native.prevent>{{
|
||||||
|
$t('commons.save')
|
||||||
|
}}</el-button>
|
||||||
|
</template>
|
||||||
|
</ms-edit-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import MsEditDialog from '@/business/commons/MsEditDialog';
|
||||||
|
import { getProjectVersions } from '@/api/xpack';
|
||||||
|
export default {
|
||||||
|
name: 'VersionSelector',
|
||||||
|
components: { MsEditDialog },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loading: false,
|
||||||
|
visible: false,
|
||||||
|
saving:false,
|
||||||
|
versionId: '',
|
||||||
|
versionData: [],
|
||||||
|
selectCase: true,
|
||||||
|
selectMock: true,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
tips: String,
|
||||||
|
projectId: String,
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
open(projectId) {
|
||||||
|
this.saving = false;
|
||||||
|
this.selectMock = true;
|
||||||
|
this.selectCase = true;
|
||||||
|
this.versionId = '';
|
||||||
|
this.versionData = [];
|
||||||
|
this.visible = true;
|
||||||
|
this.loading = true;
|
||||||
|
|
||||||
|
getProjectVersions(projectId)
|
||||||
|
.then((response) => {
|
||||||
|
if (response.data) {
|
||||||
|
this.versionData = response.data;
|
||||||
|
} else {
|
||||||
|
this.versionData = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
this.loading = false;
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
this.loading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
handleClose() {
|
||||||
|
this.saving = false;
|
||||||
|
this.$emit('handleClose');
|
||||||
|
this.visible = false;
|
||||||
|
},
|
||||||
|
save() {
|
||||||
|
if (!this.versionId || this.versionId === '') {
|
||||||
|
this.$error(this.$t('project.version.please_input_version'));
|
||||||
|
} else {
|
||||||
|
this.saving = true;
|
||||||
|
this.$emit('handleSave', this.versionId,this.selectCase,this.selectMock);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped></style>
|
|
@ -18,6 +18,7 @@ const message = {
|
||||||
type: 'Type',
|
type: 'Type',
|
||||||
default_value: 'Default value',
|
default_value: 'Default value',
|
||||||
},
|
},
|
||||||
|
copy_data_from_other_version: 'Copy data from other version',
|
||||||
body: {
|
body: {
|
||||||
json_format_error: 'JSON format error',
|
json_format_error: 'JSON format error',
|
||||||
},
|
},
|
||||||
|
|
|
@ -17,6 +17,7 @@ const message = {
|
||||||
type: '类型',
|
type: '类型',
|
||||||
default_value: '默认值',
|
default_value: '默认值',
|
||||||
},
|
},
|
||||||
|
copy_data_from_other_version: '复制版本数据',
|
||||||
body: {
|
body: {
|
||||||
json_format_error: 'JSON格式错误',
|
json_format_error: 'JSON格式错误',
|
||||||
},
|
},
|
||||||
|
|
|
@ -17,6 +17,7 @@ const message = {
|
||||||
type: '類型',
|
type: '類型',
|
||||||
default_value: '默認值',
|
default_value: '默認值',
|
||||||
},
|
},
|
||||||
|
copy_data_from_other_version: '複製版本數據',
|
||||||
body: {
|
body: {
|
||||||
json_format_error: 'JSON格式錯誤',
|
json_format_error: 'JSON格式錯誤',
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in New Issue