接口测试导入- Metersphere(json)

This commit is contained in:
chenjianxing 2020-07-08 11:57:45 +08:00
parent 1ab097bc29
commit 3dcf2f0f2a
11 changed files with 280 additions and 8 deletions

View File

@ -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);
}
}

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -0,0 +1,5 @@
package io.metersphere.commons.constants;
public enum ApiImportPlatform {
Metersphere, Postman
}

View File

@ -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;

View File

@ -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) {

View File

@ -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>

View File

@ -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;