接口测试导入- Metersphere(json)
This commit is contained in:
parent
1ab097bc29
commit
3dcf2f0f2a
|
@ -96,4 +96,10 @@ public class APITestController {
|
|||
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 io.metersphere.api.dto.APITestResult;
|
||||
import io.metersphere.api.dto.parse.ApiImport;
|
||||
import io.metersphere.api.dto.QueryAPITestRequest;
|
||||
import io.metersphere.api.dto.SaveAPITestRequest;
|
||||
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.mapper.ApiTestFileMapper;
|
||||
import io.metersphere.base.mapper.ApiTestMapper;
|
||||
import io.metersphere.base.mapper.ext.ExtApiTestMapper;
|
||||
import io.metersphere.commons.constants.APITestStatus;
|
||||
import io.metersphere.commons.constants.FileType;
|
||||
import io.metersphere.commons.constants.ScheduleGroup;
|
||||
import io.metersphere.commons.constants.ScheduleType;
|
||||
import io.metersphere.commons.exception.MSException;
|
||||
|
@ -26,8 +31,7 @@ import org.springframework.util.CollectionUtils;
|
|||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.*;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
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) {
|
||||
checkNameExist(request);
|
||||
final ApiTest test = new ApiTest();
|
||||
|
@ -246,4 +259,38 @@ public class APITestService {
|
|||
private void addOrUpdateApiTestCronJob(Schedule request) {
|
||||
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;
|
||||
|
||||
public enum FileType {
|
||||
JMX(".jmx"), CSV(".csv");
|
||||
JMX(".jmx"), CSV(".csv"), JSON(".json");
|
||||
|
||||
// 保存后缀
|
||||
private String suffix;
|
||||
|
|
|
@ -39,12 +39,18 @@
|
|||
<el-dropdown-item command="performance" :disabled="create || isReadOnly">
|
||||
{{$t('api_test.create_performance_test')}}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item command="export" :disabled="isDisabled || isReadOnly">
|
||||
<el-dropdown-item command="export" :disabled="isReadOnly || create">
|
||||
{{$t('api_test.export_config')}}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item command="import" :disabled="isReadOnly">
|
||||
导入
|
||||
<!-- {{$t('api_test.export_config')}}-->
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
|
||||
<api-import :project-id="test.projectId" ref="apiImport"/>
|
||||
|
||||
<ms-api-report-dialog :test-id="id" ref="reportDialog"/>
|
||||
|
||||
<ms-schedule-config :schedule="test.schedule" :save="saveCronExpression" @scheduleChange="saveSchedule" :check-open="checkScheduleEdit"/>
|
||||
|
@ -64,11 +70,12 @@
|
|||
import MsApiReportDialog from "./ApiReportDialog";
|
||||
import {checkoutTestManagerOrTestUser, downloadFile} from "../../../../common/js/utils";
|
||||
import MsScheduleConfig from "../../common/components/MsScheduleConfig";
|
||||
import ApiImport from "./components/import/ApiImport";
|
||||
|
||||
export default {
|
||||
name: "MsApiTestConfig",
|
||||
|
||||
components: {MsScheduleConfig, MsApiReportDialog, MsApiReportStatus, MsApiScenarioConfig},
|
||||
components: {ApiImport, MsScheduleConfig, MsApiReportDialog, MsApiReportStatus, MsApiScenarioConfig},
|
||||
|
||||
props: ["id"],
|
||||
|
||||
|
@ -211,6 +218,9 @@
|
|||
case "export":
|
||||
downloadFile(this.test.name + ".json", this.test.export());
|
||||
break;
|
||||
case "import":
|
||||
this.$refs.apiImport.open();
|
||||
break;
|
||||
}
|
||||
},
|
||||
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"
|
||||
action=""
|
||||
:on-exceed="handleExceed"
|
||||
:beforeUpload="UploadValidate"
|
||||
:beforeUpload="uploadValidate"
|
||||
:on-error="handleError"
|
||||
:show-file-list="false"
|
||||
:http-request="upload"
|
||||
|
@ -66,8 +66,8 @@
|
|||
handleExceed(files, fileList) {
|
||||
this.$warning(this.$t('test_track.case.import.upload_limit_count'));
|
||||
},
|
||||
UploadValidate(file) {
|
||||
var suffix =file.name.substring(file.name.lastIndexOf('.') + 1);
|
||||
uploadValidate(file) {
|
||||
let suffix = file.name.substring(file.name.lastIndexOf('.') + 1);
|
||||
if (suffix != 'xls' && suffix != 'xlsx') {
|
||||
this.$warning(this.$t('test_track.case.import.upload_limit_format'));
|
||||
return false;
|
||||
|
|
Loading…
Reference in New Issue