feat(接口测试): 场景导出JMX
This commit is contained in:
parent
d629d607ea
commit
225ceea3e4
|
@ -151,7 +151,11 @@ public class TempFileUtils {
|
|||
}
|
||||
}
|
||||
try {
|
||||
if (exportResponse instanceof String exportResponseStr) {
|
||||
FileUtils.writeByteArrayToFile(createFile, exportResponseStr.getBytes());
|
||||
} else {
|
||||
FileUtils.writeByteArrayToFile(createFile, JSON.toJSONString(exportResponse).getBytes());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LogUtils.error(e);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
package io.metersphere.api.dto.export;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author wx
|
||||
*/
|
||||
@Data
|
||||
public class JMeterApiScenarioExportResponse extends ApiScenarioExportResponse {
|
||||
Map<String, String> scenarioJmxMap = new HashMap<>();
|
||||
|
||||
public void addJmx(String key, String jmx) {
|
||||
scenarioJmxMap.put(key, jmx);
|
||||
}
|
||||
}
|
|
@ -6,14 +6,19 @@ import io.metersphere.api.constants.ApiScenarioStepRefType;
|
|||
import io.metersphere.api.constants.ApiScenarioStepType;
|
||||
import io.metersphere.api.domain.*;
|
||||
import io.metersphere.api.dto.ApiFile;
|
||||
import io.metersphere.api.dto.ApiScenarioParamConfig;
|
||||
import io.metersphere.api.dto.ApiScenarioParseTmpParam;
|
||||
import io.metersphere.api.dto.converter.ApiDefinitionDetail;
|
||||
import io.metersphere.api.dto.converter.ApiDefinitionExportDetail;
|
||||
import io.metersphere.api.dto.converter.ApiScenarioImportParseResult;
|
||||
import io.metersphere.api.dto.converter.ApiScenarioPreImportAnalysisResult;
|
||||
import io.metersphere.api.dto.debug.ApiFileResourceUpdateRequest;
|
||||
import io.metersphere.api.dto.debug.ApiResourceRunRequest;
|
||||
import io.metersphere.api.dto.definition.*;
|
||||
import io.metersphere.api.dto.export.ApiScenarioExportResponse;
|
||||
import io.metersphere.api.dto.export.JMeterApiScenarioExportResponse;
|
||||
import io.metersphere.api.dto.export.MetersphereApiScenarioExportResponse;
|
||||
import io.metersphere.api.dto.request.MsScenario;
|
||||
import io.metersphere.api.dto.scenario.*;
|
||||
import io.metersphere.api.mapper.*;
|
||||
import io.metersphere.api.parser.ApiScenarioImportParser;
|
||||
|
@ -24,6 +29,7 @@ import io.metersphere.api.service.definition.ApiDefinitionModuleService;
|
|||
import io.metersphere.api.service.definition.ApiTestCaseService;
|
||||
import io.metersphere.api.service.scenario.ApiScenarioLogService;
|
||||
import io.metersphere.api.service.scenario.ApiScenarioModuleService;
|
||||
import io.metersphere.api.service.scenario.ApiScenarioRunService;
|
||||
import io.metersphere.api.service.scenario.ApiScenarioService;
|
||||
import io.metersphere.api.utils.ApiDataUtils;
|
||||
import io.metersphere.api.utils.ApiDefinitionImportUtils;
|
||||
|
@ -39,6 +45,7 @@ import io.metersphere.project.service.PermissionCheckService;
|
|||
import io.metersphere.project.utils.FileDownloadUtils;
|
||||
import io.metersphere.sdk.constants.*;
|
||||
import io.metersphere.sdk.dto.ExportMsgDTO;
|
||||
import io.metersphere.sdk.dto.api.task.ApiRunRetryConfig;
|
||||
import io.metersphere.sdk.exception.MSException;
|
||||
import io.metersphere.sdk.file.FileRequest;
|
||||
import io.metersphere.sdk.util.*;
|
||||
|
@ -118,6 +125,12 @@ public class ApiScenarioDataTransferService {
|
|||
private ExtApiTestCaseMapper extApiTestCaseMapper;
|
||||
@Resource
|
||||
private ExtFileAssociationMapper extFileAssociationMapper;
|
||||
@Resource
|
||||
private ApiDefinitionBlobMapper apiDefinitionBlobMapper;
|
||||
@Resource
|
||||
private ApiTestCaseBlobMapper apiTestCaseBlobMapper;
|
||||
@Resource
|
||||
private ApiScenarioBlobMapper apiScenarioBlobMapper;
|
||||
|
||||
@Resource
|
||||
private FileService fileService;
|
||||
|
@ -1129,14 +1142,25 @@ public class ApiScenarioDataTransferService {
|
|||
if (CollectionUtils.isEmpty(ids)) {
|
||||
return null;
|
||||
}
|
||||
Map<String, String> moduleMap = this.apiScenarioModuleService.getImportTreeNodeList(request.getProjectId()).stream().collect(Collectors.toMap(BaseTreeNode::getId, BaseTreeNode::getPath));
|
||||
|
||||
Map<String, String> moduleMap;
|
||||
if (StringUtils.equalsIgnoreCase(exportType, "metersphere")) {
|
||||
moduleMap = this.apiScenarioModuleService.getImportTreeNodeList(request.getProjectId()).stream().collect(Collectors.toMap(BaseTreeNode::getId, BaseTreeNode::getPath));
|
||||
} else {
|
||||
moduleMap = null;
|
||||
}
|
||||
String fileFolder = tmpDir.getPath() + File.separatorChar + request.getFileId();
|
||||
AtomicInteger fileIndex = new AtomicInteger(1);
|
||||
SubListUtils.dealForSubList(ids, 500, subList -> {
|
||||
request.setSelectIds(subList);
|
||||
ApiScenarioExportResponse exportResponse = this.genMetersphereExportResponse(request, moduleMap);
|
||||
if (StringUtils.equalsIgnoreCase(exportType, "metersphere")) {
|
||||
ApiScenarioExportResponse exportResponse = this.genMetersphereExportResponseByMetersphere(request, moduleMap);
|
||||
TempFileUtils.writeExportFile(fileFolder + File.separatorChar + "scenario_" + fileIndex.getAndIncrement() + ".ms", exportResponse);
|
||||
} else {
|
||||
JMeterApiScenarioExportResponse exportResponse = this.genMetersphereExportResponseByJmx(request);
|
||||
exportResponse.getScenarioJmxMap().forEach((k, v) -> {
|
||||
TempFileUtils.writeExportFile(fileFolder + File.separatorChar + k + ".jmx", v);
|
||||
});
|
||||
}
|
||||
});
|
||||
File zipFile = MsFileUtils.zipFile(tmpDir.getPath(), request.getFileId());
|
||||
if (zipFile == null) {
|
||||
|
@ -1167,7 +1191,93 @@ public class ApiScenarioDataTransferService {
|
|||
}
|
||||
}
|
||||
|
||||
private ApiScenarioExportResponse genMetersphereExportResponse(ApiScenarioBatchExportRequest request, Map<String, String> moduleMap) {
|
||||
@Resource
|
||||
private ApiScenarioRunService apiScenarioRunService;
|
||||
@Resource
|
||||
private ApiExecuteService apiExecuteService;
|
||||
|
||||
private JMeterApiScenarioExportResponse genMetersphereExportResponseByJmx(ApiScenarioBatchExportRequest request) {
|
||||
JMeterApiScenarioExportResponse response = new JMeterApiScenarioExportResponse();
|
||||
List<String> apiIdList = new ArrayList<>();
|
||||
List<String> apiCaseList = new ArrayList<>();
|
||||
List<String> scenarioList = new ArrayList<>();
|
||||
|
||||
List<ApiScenarioDetail> apiScenarioDetailList = new ArrayList<>();
|
||||
|
||||
request.getSelectIds().forEach(id -> {
|
||||
ApiScenarioDetail apiScenarioDetail = apiScenarioRunService.getForRun(id);
|
||||
apiScenarioDetailList.add(apiScenarioDetail);
|
||||
if (CollectionUtils.isNotEmpty(apiScenarioDetail.getSteps())) {
|
||||
apiScenarioDetail.getSteps().forEach(step -> {
|
||||
if (StringUtils.equalsIgnoreCase(step.getStepType(), ApiScenarioStepType.API.name())) {
|
||||
apiIdList.add(step.getResourceId());
|
||||
} else if (StringUtils.equalsIgnoreCase(step.getStepType(), ApiScenarioStepType.API_CASE.name())) {
|
||||
apiCaseList.add(step.getResourceId());
|
||||
} else if (StringUtils.equalsIgnoreCase(step.getStepType(), ApiScenarioStepType.API_SCENARIO.name())) {
|
||||
scenarioList.add(step.getResourceId());
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
Map<String, ApiDefinitionBlob> apiBlobMap = new HashMap<>();
|
||||
Map<String, ApiTestCaseBlob> apiTestCaseBlobMap = new HashMap<>();
|
||||
Map<String, ApiScenarioBlob> scenarioBlobMap = new HashMap<>();
|
||||
|
||||
if (CollectionUtils.isNotEmpty(apiIdList)) {
|
||||
ApiDefinitionBlobExample apiDefinitionBlobExample = new ApiDefinitionBlobExample();
|
||||
apiDefinitionBlobExample.createCriteria().andIdIn(apiIdList);
|
||||
apiBlobMap = apiDefinitionBlobMapper.selectByExampleWithBLOBs(apiDefinitionBlobExample)
|
||||
.stream().collect(Collectors.toMap(ApiDefinitionBlob::getId, Function.identity()));
|
||||
}
|
||||
|
||||
if (CollectionUtils.isNotEmpty(apiCaseList)) {
|
||||
ApiTestCaseBlobExample example = new ApiTestCaseBlobExample();
|
||||
example.createCriteria().andIdIn(apiCaseList);
|
||||
apiTestCaseBlobMap = apiTestCaseBlobMapper.selectByExampleWithBLOBs(example)
|
||||
.stream().collect(Collectors.toMap(ApiTestCaseBlob::getId, Function.identity()));
|
||||
}
|
||||
|
||||
if (CollectionUtils.isNotEmpty(scenarioList)) {
|
||||
ApiScenarioBlobExample example = new ApiScenarioBlobExample();
|
||||
example.createCriteria().andIdIn(scenarioList);
|
||||
scenarioBlobMap = apiScenarioBlobMapper.selectByExampleWithBLOBs(example)
|
||||
.stream().collect(Collectors.toMap(ApiScenarioBlob::getId, Function.identity()));
|
||||
}
|
||||
|
||||
for (ApiScenarioDetail apiScenarioDetail : apiScenarioDetailList) {
|
||||
String jmx = getJmx(apiScenarioDetail, apiBlobMap, apiTestCaseBlobMap, scenarioBlobMap);
|
||||
response.addJmx(apiScenarioDetail.getNum() + "_" + apiScenarioDetail.getName(), jmx);
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
private String getJmx(ApiScenarioDetail apiScenarioDetail, Map<String, ApiDefinitionBlob> apiBlobMap,
|
||||
Map<String, ApiTestCaseBlob> apiTestCaseBlobMap, Map<String, ApiScenarioBlob> scenarioBlobMap) {
|
||||
|
||||
String envId = apiScenarioDetail.getEnvironmentId();
|
||||
boolean envGroup = apiScenarioDetail.getGrouped();
|
||||
|
||||
// 解析生成待执行的场景树
|
||||
MsScenario msScenario = apiScenarioRunService.getMsScenario(apiScenarioDetail);
|
||||
|
||||
ApiScenarioParseParam parseParam = apiScenarioRunService.getApiScenarioParseParam(apiScenarioDetail);
|
||||
parseParam.setEnvironmentId(envId);
|
||||
parseParam.setGrouped(envGroup);
|
||||
msScenario.setResourceId(apiScenarioDetail.getId());
|
||||
|
||||
ApiScenarioParseTmpParam tmpParam = apiScenarioRunService.parse(msScenario, apiBlobMap, apiTestCaseBlobMap, scenarioBlobMap, apiScenarioDetail.getSteps(), parseParam);
|
||||
|
||||
ApiResourceRunRequest runRequest = apiScenarioRunService.getApiResourceRunRequest(msScenario, tmpParam);
|
||||
|
||||
ApiScenarioParamConfig parseConfig = apiScenarioRunService.getApiScenarioParamConfig(apiScenarioDetail.getProjectId(), parseParam, tmpParam.getScenarioParseEnvInfo());
|
||||
parseConfig.setReportId(StringUtils.EMPTY);
|
||||
parseConfig.setTaskItemId(StringUtils.EMPTY);
|
||||
parseConfig.setRetryConfig(new ApiRunRetryConfig());
|
||||
|
||||
return apiExecuteService.parseExecuteScript(runRequest.getTestElement(), parseConfig);
|
||||
}
|
||||
|
||||
private ApiScenarioExportResponse genMetersphereExportResponseByMetersphere(ApiScenarioBatchExportRequest request, Map<String, String> moduleMap) {
|
||||
Project project = projectMapper.selectByPrimaryKey(request.getProjectId());
|
||||
MetersphereApiScenarioExportResponse response = apiScenarioService.selectAndSortScenarioDetailWithIds(request.getSelectIds(), moduleMap);
|
||||
response.setProjectId(project.getId());
|
||||
|
|
|
@ -646,6 +646,59 @@ public class ApiScenarioRunService {
|
|||
return tmpParam;
|
||||
}
|
||||
|
||||
public ApiScenarioParseTmpParam parse(MsScenario msScenario,
|
||||
Map<String, ApiDefinitionBlob> apiBlobMap,
|
||||
Map<String, ApiTestCaseBlob> apiTestCaseBlobMap,
|
||||
Map<String, ApiScenarioBlob> scenarioBlobMap,
|
||||
List<? extends ApiScenarioStepCommonDTO> steps,
|
||||
ApiScenarioParseParam parseParam) {
|
||||
ApiScenarioParseTmpParam tmpParam = new ApiScenarioParseTmpParam();
|
||||
|
||||
Map<String, List<String>> refResourceMap = new HashMap<>();
|
||||
buildRefResourceIdMap(steps, refResourceMap);
|
||||
Map<String, String> resourceBlobMap = new HashMap<>();
|
||||
List<String> apiIds = refResourceMap.get(ApiScenarioStepType.API.name());
|
||||
if (CollectionUtils.isNotEmpty(apiIds)) {
|
||||
apiIds.forEach(apiId -> {
|
||||
ApiDefinitionBlob blob = apiBlobMap.get(apiId);
|
||||
if (blob != null) {
|
||||
resourceBlobMap.put(blob.getId(), new String(blob.getRequest()));
|
||||
}
|
||||
});
|
||||
}
|
||||
List<String> apiCaseIds = refResourceMap.get(ApiScenarioStepType.API_CASE.name());
|
||||
if (CollectionUtils.isNotEmpty(apiCaseIds)) {
|
||||
apiCaseIds.forEach(apiCaseId -> {
|
||||
ApiTestCaseBlob blob = apiTestCaseBlobMap.get(apiCaseId);
|
||||
if (blob != null) {
|
||||
resourceBlobMap.put(blob.getId(), new String(blob.getRequest()));
|
||||
}
|
||||
});
|
||||
}
|
||||
List<String> apiScenarioIds = refResourceMap.get(ApiScenarioStepType.API_SCENARIO.name());
|
||||
if (CollectionUtils.isNotEmpty(apiScenarioIds)) {
|
||||
apiScenarioIds.forEach(apiScenarioId -> {
|
||||
ApiScenarioBlob blob = scenarioBlobMap.get(apiScenarioId);
|
||||
if (blob != null) {
|
||||
resourceBlobMap.put(blob.getId(), new String(blob.getConfig()));
|
||||
}
|
||||
});
|
||||
}
|
||||
tmpParam.setResourceDetailMap(resourceBlobMap);
|
||||
// 查询复制的步骤详情
|
||||
tmpParam.setStepDetailMap(getStepDetailMap(steps, parseParam.getStepDetails()));
|
||||
// 获取场景环境相关配置
|
||||
tmpParam.setScenarioParseEnvInfo(getScenarioParseEnvInfo(refResourceMap, parseParam.getEnvironmentId(), parseParam.getGrouped()));
|
||||
parseStep2MsElement(msScenario, steps, tmpParam, msScenario.getResourceId());
|
||||
// 设置 HttpElement 的模块信息
|
||||
setApiDefinitionExecuteInfo(tmpParam.getUniqueIdStepMap(), tmpParam.getStepTypeHttpElementMap());
|
||||
// 设置使用脚本前后置的公共脚本信息
|
||||
apiCommonService.setCommonElementEnableCommonScriptInfo(tmpParam.getCommonElements());
|
||||
apiCommonService.setScriptElementEnableCommonScriptInfo(tmpParam.getScriptElements());
|
||||
|
||||
return tmpParam;
|
||||
}
|
||||
|
||||
private void buildRefResourceIdMap(List<? extends ApiScenarioStepCommonDTO> steps, Map<String, List<String>> refResourceIdMap) {
|
||||
for (ApiScenarioStepCommonDTO step : steps) {
|
||||
if (apiScenarioService.isRefOrPartialRef(step.getRefType()) && BooleanUtils.isTrue(step.getEnable())) {
|
||||
|
|
|
@ -169,6 +169,40 @@ public class ApiScenarioControllerImportAndExportTests extends BaseTest {
|
|||
|
||||
MsFileUtils.deleteDir("/tmp/api-scenario-export/");
|
||||
}
|
||||
|
||||
// jmx export
|
||||
ApiScenarioBatchExportRequest exportRequest = new ApiScenarioBatchExportRequest();
|
||||
String fileId = IDGenerator.nextStr();
|
||||
exportRequest.setProjectId(project.getId());
|
||||
exportRequest.setFileId(fileId);
|
||||
exportRequest.setSelectAll(true);
|
||||
exportRequest.setExportAllRelatedData(false);
|
||||
MvcResult mvcResult = this.requestPostWithOkAndReturn(URL_POST_EXPORT + "jmeter", exportRequest);
|
||||
String returnData = mvcResult.getResponse().getContentAsString(StandardCharsets.UTF_8);
|
||||
JSON.parseObject(returnData, ResultHolder.class).getData().toString();
|
||||
Assertions.assertTrue(StringUtils.isNotBlank(fileId));
|
||||
List<ExportTask> taskList = exportTaskManager.getExportTasks(exportRequest.getProjectId(), null, null, "admin", fileId);
|
||||
while (CollectionUtils.isEmpty(taskList)) {
|
||||
Thread.sleep(1000);
|
||||
taskList = exportTaskManager.getExportTasks(exportRequest.getProjectId(), null, null, "admin", fileId);
|
||||
}
|
||||
ExportTask task = taskList.getFirst();
|
||||
while (!StringUtils.equalsIgnoreCase(task.getState(), ExportConstants.ExportState.SUCCESS.name())) {
|
||||
Thread.sleep(1000);
|
||||
task = exportTaskManager.getExportTasks(exportRequest.getProjectId(), null, null, "admin", fileId).getFirst();
|
||||
}
|
||||
|
||||
mvcResult = this.download(exportRequest.getProjectId(), fileId);
|
||||
|
||||
byte[] fileBytes = mvcResult.getResponse().getContentAsByteArray();
|
||||
|
||||
File zipFile = new File("/tmp/api-scenario-export/downloadFiles.zip");
|
||||
FileUtils.writeByteArrayToFile(zipFile, fileBytes);
|
||||
|
||||
File[] files = MsFileUtils.unZipFile(zipFile, "/tmp/api-scenario-export/unzip/");
|
||||
assert files != null;
|
||||
Assertions.assertEquals(files.length, 6);
|
||||
MsFileUtils.deleteDir("/tmp/api-scenario-export/");
|
||||
}
|
||||
|
||||
private MvcResult download(String projectId, String fileId) throws Exception {
|
||||
|
|
|
@ -6,7 +6,18 @@
|
|||
class="ms-modal-upload ms-modal-medium"
|
||||
:width="400"
|
||||
>
|
||||
<div class="mb-[16px] flex items-center gap-[8px]">
|
||||
<div class="mb-[16px] flex items-center gap-[16px]">
|
||||
<div
|
||||
v-for="item of platformList"
|
||||
:key="item.value"
|
||||
:class="`import-item ${exportPlatform === item.value ? 'import-item--active' : ''}`"
|
||||
@click="() => setActiveImportFormat(item.value)"
|
||||
>
|
||||
<div class="text-[var(--color-text-1)]">{{ item.name }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-show="exportPlatform === 'MeterSphere'" class="mb-[16px] flex items-center gap-[8px]">
|
||||
<a-switch v-model:model-value="exportTypeRadio" size="small"></a-switch>
|
||||
{{ t('apiScenario.export.type.all') }}
|
||||
<a-tooltip :content="t('apiScenario.export.simple.tooltip')" position="tl">
|
||||
|
@ -47,6 +58,18 @@
|
|||
import useAppStore from '@/store/modules/app';
|
||||
import { downloadByteFile, getGenerateId } from '@/utils';
|
||||
|
||||
import { RequestImportFormat } from '@/enums/apiEnum';
|
||||
|
||||
const platformList: { name: string; value: RequestImportFormat.MeterSphere | RequestImportFormat.Jmeter }[] = [
|
||||
{
|
||||
name: 'MeterSphere',
|
||||
value: RequestImportFormat.MeterSphere,
|
||||
},
|
||||
{
|
||||
name: 'Jmeter',
|
||||
value: RequestImportFormat.Jmeter,
|
||||
},
|
||||
];
|
||||
const appStore = useAppStore();
|
||||
const { t } = useI18n();
|
||||
|
||||
|
@ -61,11 +84,13 @@
|
|||
|
||||
const exportLoading = ref(false);
|
||||
const exportTypeRadio = ref(false);
|
||||
|
||||
const exportPlatform = ref(RequestImportFormat.MeterSphere);
|
||||
function cancelExport() {
|
||||
visible.value = false;
|
||||
}
|
||||
|
||||
function setActiveImportFormat(format: RequestImportFormat.MeterSphere | RequestImportFormat.Jmeter) {
|
||||
exportPlatform.value = format;
|
||||
}
|
||||
const websocket = ref<WebSocket>();
|
||||
const reportId = ref('');
|
||||
const isShowExportingMessage = ref(false); // 正在导出提示显示中
|
||||
|
@ -192,7 +217,7 @@
|
|||
sort: props.sorter || {},
|
||||
fileId: reportId.value,
|
||||
},
|
||||
'METERSPHERE'
|
||||
exportPlatform.value
|
||||
);
|
||||
showExportingMessage(res);
|
||||
visible.value = false;
|
||||
|
|
Loading…
Reference in New Issue