接口测试导入- Metersphere(json)
This commit is contained in:
parent
1ab097bc29
commit
3dcf2f0f2a
|
@ -96,4 +96,10 @@ public class APITestController {
|
||||||
return apiTestService.run(request);
|
return apiTestService.run(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PostMapping("/import/{platform}/{projectId}")
|
||||||
|
@RequiresRoles(value = {RoleConstants.TEST_USER, RoleConstants.TEST_MANAGER}, logical = Logical.OR)
|
||||||
|
public ApiTest testCaseImport(MultipartFile file, @PathVariable String platform, @PathVariable String projectId) {
|
||||||
|
return apiTestService.apiTestImport(file, platform, projectId);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
package io.metersphere.api.dto.parse;
|
||||||
|
|
||||||
|
import io.metersphere.api.dto.scenario.Scenario;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class ApiImport {
|
||||||
|
private List<Scenario> scenarios;
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
package io.metersphere.api.parse;
|
||||||
|
|
||||||
|
import io.metersphere.api.dto.parse.ApiImport;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
public interface ApiImportParser {
|
||||||
|
ApiImport parse(InputStream source);
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
package io.metersphere.api.parse;
|
||||||
|
|
||||||
|
import io.metersphere.commons.constants.ApiImportPlatform;
|
||||||
|
import io.metersphere.commons.constants.FileType;
|
||||||
|
import io.metersphere.performance.parse.EngineSourceParser;
|
||||||
|
import io.metersphere.performance.parse.xml.XmlEngineSourceParse;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
public class ApiImportParserFactory {
|
||||||
|
public static ApiImportParser getApiImportParser(String platform) {
|
||||||
|
if (StringUtils.equals(ApiImportPlatform.Metersphere.name(), platform)) {
|
||||||
|
return new MsParser();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
package io.metersphere.api.parse;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson.JSON;
|
||||||
|
import io.metersphere.api.dto.parse.ApiImport;
|
||||||
|
import io.metersphere.commons.exception.MSException;
|
||||||
|
import io.metersphere.commons.utils.LogUtil;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
|
||||||
|
public class MsParser implements ApiImportParser {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ApiImport parse(InputStream source) {BufferedReader bufferedReader = null;
|
||||||
|
StringBuilder testStr = null;
|
||||||
|
try {
|
||||||
|
bufferedReader = new BufferedReader(new InputStreamReader(source, "UTF-8"));
|
||||||
|
testStr = new StringBuilder();
|
||||||
|
String inputStr = null;
|
||||||
|
while ((inputStr = bufferedReader.readLine()) != null) {
|
||||||
|
testStr.append(inputStr);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
MSException.throwException(e.getMessage());
|
||||||
|
LogUtil.error(e.getMessage(), e);
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
source.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
MSException.throwException(e.getMessage());
|
||||||
|
LogUtil.error(e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return JSON.parseObject(testStr.toString(), ApiImport.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -2,14 +2,19 @@ package io.metersphere.api.service;
|
||||||
|
|
||||||
import com.alibaba.fastjson.JSONObject;
|
import com.alibaba.fastjson.JSONObject;
|
||||||
import io.metersphere.api.dto.APITestResult;
|
import io.metersphere.api.dto.APITestResult;
|
||||||
|
import io.metersphere.api.dto.parse.ApiImport;
|
||||||
import io.metersphere.api.dto.QueryAPITestRequest;
|
import io.metersphere.api.dto.QueryAPITestRequest;
|
||||||
import io.metersphere.api.dto.SaveAPITestRequest;
|
import io.metersphere.api.dto.SaveAPITestRequest;
|
||||||
import io.metersphere.api.jmeter.JMeterService;
|
import io.metersphere.api.jmeter.JMeterService;
|
||||||
|
import io.metersphere.api.parse.ApiImportParser;
|
||||||
|
import io.metersphere.api.parse.ApiImportParserFactory;
|
||||||
|
import io.metersphere.api.parse.MsParser;
|
||||||
import io.metersphere.base.domain.*;
|
import io.metersphere.base.domain.*;
|
||||||
import io.metersphere.base.mapper.ApiTestFileMapper;
|
import io.metersphere.base.mapper.ApiTestFileMapper;
|
||||||
import io.metersphere.base.mapper.ApiTestMapper;
|
import io.metersphere.base.mapper.ApiTestMapper;
|
||||||
import io.metersphere.base.mapper.ext.ExtApiTestMapper;
|
import io.metersphere.base.mapper.ext.ExtApiTestMapper;
|
||||||
import io.metersphere.commons.constants.APITestStatus;
|
import io.metersphere.commons.constants.APITestStatus;
|
||||||
|
import io.metersphere.commons.constants.FileType;
|
||||||
import io.metersphere.commons.constants.ScheduleGroup;
|
import io.metersphere.commons.constants.ScheduleGroup;
|
||||||
import io.metersphere.commons.constants.ScheduleType;
|
import io.metersphere.commons.constants.ScheduleType;
|
||||||
import io.metersphere.commons.exception.MSException;
|
import io.metersphere.commons.exception.MSException;
|
||||||
|
@ -26,8 +31,7 @@ import org.springframework.util.CollectionUtils;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.*;
|
||||||
import java.io.InputStream;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
@ -163,6 +167,15 @@ public class APITestService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Boolean isNameExist(SaveAPITestRequest request) {
|
||||||
|
ApiTestExample example = new ApiTestExample();
|
||||||
|
example.createCriteria().andNameEqualTo(request.getName()).andProjectIdEqualTo(request.getProjectId()).andIdNotEqualTo(request.getId());
|
||||||
|
if (apiTestMapper.countByExample(example) > 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private ApiTest updateTest(SaveAPITestRequest request) {
|
private ApiTest updateTest(SaveAPITestRequest request) {
|
||||||
checkNameExist(request);
|
checkNameExist(request);
|
||||||
final ApiTest test = new ApiTest();
|
final ApiTest test = new ApiTest();
|
||||||
|
@ -246,4 +259,38 @@ public class APITestService {
|
||||||
private void addOrUpdateApiTestCronJob(Schedule request) {
|
private void addOrUpdateApiTestCronJob(Schedule request) {
|
||||||
scheduleService.addOrUpdateCronJob(request, ApiTestJob.getJobKey(request.getResourceId()), ApiTestJob.getTriggerKey(request.getResourceId()), ApiTestJob.class);
|
scheduleService.addOrUpdateCronJob(request, ApiTestJob.getJobKey(request.getResourceId()), ApiTestJob.getTriggerKey(request.getResourceId()), ApiTestJob.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ApiTest apiTestImport(MultipartFile file, String platform, String projectId) {
|
||||||
|
try {
|
||||||
|
|
||||||
|
ApiImportParser apiImportParser = ApiImportParserFactory.getApiImportParser(platform);
|
||||||
|
ApiImport apiImport = apiImportParser.parse(file.getInputStream());
|
||||||
|
SaveAPITestRequest request = getImportApiTest(file, projectId, apiImport);
|
||||||
|
ApiTest test = createTest(request);
|
||||||
|
return test;
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private SaveAPITestRequest getImportApiTest(MultipartFile file, String projectId, ApiImport apiImport) {
|
||||||
|
SaveAPITestRequest request = new SaveAPITestRequest();
|
||||||
|
request.setName(file.getOriginalFilename());
|
||||||
|
request.setProjectId(projectId);
|
||||||
|
request.setScenarioDefinition(apiImport.getScenarios());
|
||||||
|
request.setUserId(SessionUtils.getUser().getId());
|
||||||
|
request.setId(UUID.randomUUID().toString());
|
||||||
|
for (FileType fileType : FileType.values()) {
|
||||||
|
String suffix = fileType.suffix();
|
||||||
|
String name = request.getName();
|
||||||
|
if (name.endsWith(suffix)) {
|
||||||
|
request.setName(name.substring(0, name.length() - suffix.length()));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if (isNameExist(request)) {
|
||||||
|
request.setName(request.getName() + "_" + request.getId().substring(0, 5));
|
||||||
|
}
|
||||||
|
return request;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
package io.metersphere.commons.constants;
|
||||||
|
|
||||||
|
public enum ApiImportPlatform {
|
||||||
|
Metersphere, Postman
|
||||||
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
package io.metersphere.commons.constants;
|
package io.metersphere.commons.constants;
|
||||||
|
|
||||||
public enum FileType {
|
public enum FileType {
|
||||||
JMX(".jmx"), CSV(".csv");
|
JMX(".jmx"), CSV(".csv"), JSON(".json");
|
||||||
|
|
||||||
// 保存后缀
|
// 保存后缀
|
||||||
private String suffix;
|
private String suffix;
|
||||||
|
|
|
@ -39,12 +39,18 @@
|
||||||
<el-dropdown-item command="performance" :disabled="create || isReadOnly">
|
<el-dropdown-item command="performance" :disabled="create || isReadOnly">
|
||||||
{{$t('api_test.create_performance_test')}}
|
{{$t('api_test.create_performance_test')}}
|
||||||
</el-dropdown-item>
|
</el-dropdown-item>
|
||||||
<el-dropdown-item command="export" :disabled="isDisabled || isReadOnly">
|
<el-dropdown-item command="export" :disabled="isReadOnly || create">
|
||||||
{{$t('api_test.export_config')}}
|
{{$t('api_test.export_config')}}
|
||||||
</el-dropdown-item>
|
</el-dropdown-item>
|
||||||
|
<el-dropdown-item command="import" :disabled="isReadOnly">
|
||||||
|
导入
|
||||||
|
<!-- {{$t('api_test.export_config')}}-->
|
||||||
|
</el-dropdown-item>
|
||||||
</el-dropdown-menu>
|
</el-dropdown-menu>
|
||||||
</el-dropdown>
|
</el-dropdown>
|
||||||
|
|
||||||
|
<api-import :project-id="test.projectId" ref="apiImport"/>
|
||||||
|
|
||||||
<ms-api-report-dialog :test-id="id" ref="reportDialog"/>
|
<ms-api-report-dialog :test-id="id" ref="reportDialog"/>
|
||||||
|
|
||||||
<ms-schedule-config :schedule="test.schedule" :save="saveCronExpression" @scheduleChange="saveSchedule" :check-open="checkScheduleEdit"/>
|
<ms-schedule-config :schedule="test.schedule" :save="saveCronExpression" @scheduleChange="saveSchedule" :check-open="checkScheduleEdit"/>
|
||||||
|
@ -64,11 +70,12 @@
|
||||||
import MsApiReportDialog from "./ApiReportDialog";
|
import MsApiReportDialog from "./ApiReportDialog";
|
||||||
import {checkoutTestManagerOrTestUser, downloadFile} from "../../../../common/js/utils";
|
import {checkoutTestManagerOrTestUser, downloadFile} from "../../../../common/js/utils";
|
||||||
import MsScheduleConfig from "../../common/components/MsScheduleConfig";
|
import MsScheduleConfig from "../../common/components/MsScheduleConfig";
|
||||||
|
import ApiImport from "./components/import/ApiImport";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "MsApiTestConfig",
|
name: "MsApiTestConfig",
|
||||||
|
|
||||||
components: {MsScheduleConfig, MsApiReportDialog, MsApiReportStatus, MsApiScenarioConfig},
|
components: {ApiImport, MsScheduleConfig, MsApiReportDialog, MsApiReportStatus, MsApiScenarioConfig},
|
||||||
|
|
||||||
props: ["id"],
|
props: ["id"],
|
||||||
|
|
||||||
|
@ -211,6 +218,9 @@
|
||||||
case "export":
|
case "export":
|
||||||
downloadFile(this.test.name + ".json", this.test.export());
|
downloadFile(this.test.name + ".json", this.test.export());
|
||||||
break;
|
break;
|
||||||
|
case "import":
|
||||||
|
this.$refs.apiImport.open();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
saveCronExpression(cronExpression) {
|
saveCronExpression(cronExpression) {
|
||||||
|
|
|
@ -0,0 +1,132 @@
|
||||||
|
<template>
|
||||||
|
<el-dialog :title="'接口测试导入'" :visible.sync="visible" class="api-import" v-loading="result.loading">
|
||||||
|
|
||||||
|
<div class="data-format">
|
||||||
|
<div>数据格式</div>
|
||||||
|
<el-radio-group v-model="selectedPlatformValue">
|
||||||
|
<el-radio v-for="(item, index) in platforms" :key="index" :label="item.value">{{item.name}}</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="api-upload">
|
||||||
|
<el-upload
|
||||||
|
drag
|
||||||
|
action=""
|
||||||
|
:http-request="upload"
|
||||||
|
:limit="1"
|
||||||
|
:beforeUpload="uploadValidate"
|
||||||
|
:show-file-list="false"
|
||||||
|
:file-list="fileList"
|
||||||
|
multiple>
|
||||||
|
<i class="el-icon-upload"></i>
|
||||||
|
<div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
|
||||||
|
<div class="el-upload__tip" slot="tip">文件大小不超过 20 M</div>
|
||||||
|
</el-upload>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="format-tip">
|
||||||
|
<div>
|
||||||
|
<span>说明:{{selectedPlatform.tip}}</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>导出方法:{{selectedPlatform.exportTip}}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import MsDialogFooter from "../../../../common/components/MsDialogFooter";
|
||||||
|
export default {
|
||||||
|
name: "ApiImport",
|
||||||
|
components: {MsDialogFooter},
|
||||||
|
props: ['projectId'],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
visible: false,
|
||||||
|
platforms: [
|
||||||
|
{
|
||||||
|
name: 'Metersphere',
|
||||||
|
value: 'Metersphere',
|
||||||
|
tip: '支持 Metersphere json 格式',
|
||||||
|
exportTip: '通过 Metersphere Api 测试页面或者浏览器插件导出 json 格式文件',
|
||||||
|
suffixes: new Set(['json'])
|
||||||
|
}
|
||||||
|
],
|
||||||
|
selectedPlatform: {},
|
||||||
|
selectedPlatformValue: 'Metersphere',
|
||||||
|
fileList: [],
|
||||||
|
result: {},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.selectedPlatform = this.platforms[0];
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
selectedPlatformId() {
|
||||||
|
for (let i in this.platforms) {
|
||||||
|
if (this.platforms[i].id === this.selectedPlatformValue) {
|
||||||
|
this.selectedPlatform = this.platforms[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
open() {
|
||||||
|
this.visible = true;
|
||||||
|
},
|
||||||
|
upload(file) {
|
||||||
|
this.fileList.push(file.file);
|
||||||
|
this.result = this.$fileUpload('/api/import/' + this.selectedPlatformValue + '/' + this.projectId, this.fileList, response => {
|
||||||
|
let res = response.data;
|
||||||
|
this.$success(this.$t('test_track.case.import.success'));
|
||||||
|
this.visible = false;
|
||||||
|
this.$router.push({path: '/api/test/edit', query: {id: res.id}});
|
||||||
|
});
|
||||||
|
this.fileList = [];
|
||||||
|
},
|
||||||
|
uploadValidate(file, fileList) {
|
||||||
|
let suffix = file.name.substring(file.name.lastIndexOf('.') + 1);
|
||||||
|
if (!this.selectedPlatform.suffixes.has(suffix)) {
|
||||||
|
this.$warning("格式错误");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (file.size / 1024 / 1024 > 20) {
|
||||||
|
this.$warning(this.$t('test_track.case.import.upload_limit_size'));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
.format-tip {
|
||||||
|
background: #EDEDED;
|
||||||
|
}
|
||||||
|
|
||||||
|
.api-upload {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-radio-group {
|
||||||
|
margin: 10px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.data-format,.format-tip,.api-upload {
|
||||||
|
border: solid #E1E1E1 1px;
|
||||||
|
margin: 10px 0;
|
||||||
|
padding: 10px;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.api-import >>> .el-dialog__body {
|
||||||
|
padding: 15px 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
|
@ -16,7 +16,7 @@
|
||||||
:limit="1"
|
:limit="1"
|
||||||
action=""
|
action=""
|
||||||
:on-exceed="handleExceed"
|
:on-exceed="handleExceed"
|
||||||
:beforeUpload="UploadValidate"
|
:beforeUpload="uploadValidate"
|
||||||
:on-error="handleError"
|
:on-error="handleError"
|
||||||
:show-file-list="false"
|
:show-file-list="false"
|
||||||
:http-request="upload"
|
:http-request="upload"
|
||||||
|
@ -66,8 +66,8 @@
|
||||||
handleExceed(files, fileList) {
|
handleExceed(files, fileList) {
|
||||||
this.$warning(this.$t('test_track.case.import.upload_limit_count'));
|
this.$warning(this.$t('test_track.case.import.upload_limit_count'));
|
||||||
},
|
},
|
||||||
UploadValidate(file) {
|
uploadValidate(file) {
|
||||||
var suffix =file.name.substring(file.name.lastIndexOf('.') + 1);
|
let suffix = file.name.substring(file.name.lastIndexOf('.') + 1);
|
||||||
if (suffix != 'xls' && suffix != 'xlsx') {
|
if (suffix != 'xls' && suffix != 'xlsx') {
|
||||||
this.$warning(this.$t('test_track.case.import.upload_limit_format'));
|
this.$warning(this.$t('test_track.case.import.upload_limit_format'));
|
||||||
return false;
|
return false;
|
||||||
|
|
Loading…
Reference in New Issue