feat(接口测试): 接口定义导入

This commit is contained in:
chenjianxing 2020-12-01 21:07:32 +08:00
parent 4e607cfa7b
commit b08775dbb9
4 changed files with 83 additions and 163 deletions

View File

@ -19,11 +19,15 @@ import io.metersphere.base.mapper.ext.ExtApiDefinitionMapper;
import io.metersphere.commons.constants.APITestStatus; import io.metersphere.commons.constants.APITestStatus;
import io.metersphere.commons.constants.ApiRunMode; import io.metersphere.commons.constants.ApiRunMode;
import io.metersphere.commons.exception.MSException; import io.metersphere.commons.exception.MSException;
import io.metersphere.commons.utils.BeanUtils;
import io.metersphere.commons.utils.LogUtil; import io.metersphere.commons.utils.LogUtil;
import io.metersphere.commons.utils.ServiceUtils; import io.metersphere.commons.utils.ServiceUtils;
import io.metersphere.commons.utils.SessionUtils; import io.metersphere.commons.utils.SessionUtils;
import io.metersphere.i18n.Translator; import io.metersphere.i18n.Translator;
import io.metersphere.service.FileService; import io.metersphere.service.FileService;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.jorphan.collections.HashTree; import org.apache.jorphan.collections.HashTree;
import org.aspectj.util.FileUtil; import org.aspectj.util.FileUtil;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@ -55,6 +59,8 @@ public class ApiDefinitionService {
private ExtApiDefinitionExecResultMapper extApiDefinitionExecResultMapper; private ExtApiDefinitionExecResultMapper extApiDefinitionExecResultMapper;
@Resource @Resource
private JMeterService jMeterService; private JMeterService jMeterService;
@Resource
private SqlSessionFactory sqlSessionFactory;
private static Cache cache = Cache.newHardMemoryCache(0, 3600 * 24); private static Cache cache = Cache.newHardMemoryCache(0, 3600 * 24);
@ -218,39 +224,27 @@ public class ApiDefinitionService {
return test; return test;
} }
private ApiDefinition createTest(ApiDefinitionResult request) { private ApiDefinition createTest(ApiDefinitionResult request, ApiDefinitionMapper batchMapper) {
SaveApiDefinitionRequest saveReq = new SaveApiDefinitionRequest(); SaveApiDefinitionRequest saveReq = new SaveApiDefinitionRequest();
BeanUtils.copyBean(saveReq, request);
saveReq.setId(UUID.randomUUID().toString()); saveReq.setId(UUID.randomUUID().toString());
saveReq.setName(request.getName());
saveReq.setProtocol(request.getProtocol());
saveReq.setProjectId(request.getProjectId());
saveReq.setPath(request.getPath());
checkNameExist(saveReq); checkNameExist(saveReq);
final ApiDefinitionWithBLOBs test = new ApiDefinitionWithBLOBs(); final ApiDefinitionWithBLOBs test = new ApiDefinitionWithBLOBs();
test.setId(request.getId()); BeanUtils.copyBean(test, request);
test.setName(request.getName());
test.setProtocol(request.getProtocol());
test.setMethod(request.getMethod());
test.setPath(request.getPath());
test.setModuleId(request.getModuleId());
test.setProjectId(request.getProjectId());
test.setRequest(request.getRequest());
test.setCreateTime(System.currentTimeMillis()); test.setCreateTime(System.currentTimeMillis());
test.setUpdateTime(System.currentTimeMillis()); test.setUpdateTime(System.currentTimeMillis());
test.setStatus(APITestStatus.Underway.name()); test.setStatus(APITestStatus.Underway.name());
test.setModulePath(request.getModulePath());
test.setResponse(request.getResponse());
test.setEnvironmentId(request.getEnvironmentId());
if (request.getUserId() == null) { if (request.getUserId() == null) {
test.setUserId(Objects.requireNonNull(SessionUtils.getUser()).getId()); test.setUserId(Objects.requireNonNull(SessionUtils.getUser()).getId());
} else { } else {
test.setUserId(request.getUserId()); test.setUserId(request.getUserId());
} }
test.setDescription(request.getDescription()); test.setDescription(request.getDescription());
apiDefinitionMapper.insert(test); batchMapper.insert(test);
return test; return test;
} }
private void deleteFileByTestId(String apiId) { private void deleteFileByTestId(String apiId) {
ApiTestFileExample apiTestFileExample = new ApiTestFileExample(); ApiTestFileExample apiTestFileExample = new ApiTestFileExample();
apiTestFileExample.createCriteria().andTestIdEqualTo(apiId); apiTestFileExample.createCriteria().andTestIdEqualTo(apiId);
@ -337,15 +331,23 @@ public class ApiDefinitionService {
} }
private void importApiTest(ApiTestImportRequest importRequest, ApiDefinitionImport apiImport) { private void importApiTest(ApiTestImportRequest importRequest, ApiDefinitionImport apiImport) {
apiImport.getData().forEach(item -> { SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
ApiDefinitionMapper batchMapper = sqlSession.getMapper(ApiDefinitionMapper.class);
List<ApiDefinitionResult> data = apiImport.getData();
for (int i = 0; i < data.size(); i++) {
ApiDefinitionResult item = data.get(i);
item.setProjectId(importRequest.getProjectId()); item.setProjectId(importRequest.getProjectId());
item.setModuleId(importRequest.getModuleId()); item.setModuleId(importRequest.getModuleId());
item.setModulePath(importRequest.getModulePath()); item.setModulePath(importRequest.getModulePath());
item.setEnvironmentId(importRequest.getEnvironmentId()); item.setEnvironmentId(importRequest.getEnvironmentId());
item.setId(UUID.randomUUID().toString()); item.setId(UUID.randomUUID().toString());
item.setUserId(null); item.setUserId(null);
createTest(item); createTest(item, batchMapper);
}); if (i % 300 == 0) {
sqlSession.flushStatements();
}
}
sqlSession.flushStatements();
} }
} }

View File

@ -15,7 +15,7 @@ CREATE TABLE IF NOT EXISTS `api_module` (
CREATE TABLE IF NOT EXISTS `api_definition` ( CREATE TABLE IF NOT EXISTS `api_definition` (
`id` varchar(50) NOT NULL COMMENT 'Test ID', `id` varchar(50) NOT NULL COMMENT 'Test ID',
`project_id` varchar(50) NOT NULL COMMENT 'Project ID this test belongs to', `project_id` varchar(50) NOT NULL COMMENT 'Project ID this test belongs to',
`name` varchar(64) NOT NULL COMMENT 'Test name', `name` varchar(255) NOT NULL COMMENT 'Test name',
`method` varchar(64) NOT NULL COMMENT 'method', `method` varchar(64) NOT NULL COMMENT 'method',
`protocol` varchar(255) NOT NULL COMMENT 'request protocol', `protocol` varchar(255) NOT NULL COMMENT 'request protocol',
`path` varchar(255) DEFAULT NULL COMMENT 'request path', `path` varchar(255) DEFAULT NULL COMMENT 'request path',

View File

@ -95,7 +95,7 @@
</el-tree> </el-tree>
<ms-add-basis-api :current-protocol="protocol" ref="basisApi"></ms-add-basis-api> <ms-add-basis-api :current-protocol="protocol" ref="basisApi"></ms-add-basis-api>
<api-import ref="apiImport" @refresh="refresh"/> <api-import ref="apiImport" :project-id="currentProject.id" @refresh="refresh"/>
</div> </div>

View File

@ -1,5 +1,6 @@
<template> <template>
<el-dialog :close-on-click-modal="false" :title="$t('api_test.api_import.title')" :visible.sync="visible" class="api-import" v-loading="result.loading" @close="close"> <el-dialog :close-on-click-modal="false" :title="$t('api_test.api_import.title')" width="30%"
:visible.sync="visible" class="api-import" v-loading="result.loading" @close="close">
<div class="header-bar"> <div class="header-bar">
<div>{{$t('api_test.api_import.data_format')}}</div> <div>{{$t('api_test.api_import.data_format')}}</div>
@ -18,48 +19,9 @@
</div> </div>
<el-form :model="formData" :rules="rules" label-width="100px" v-loading="result.loading" ref="form"> <el-form :model="formData" :rules="rules" label-width="100px" v-loading="result.loading" ref="form">
<el-row>
<el-col :span="11">
<el-form-item :label="$t('commons.name')" prop="name">
<el-input size="small" class="name-input" v-model="formData.name" clearable show-word-limit/>
</el-form-item>
<el-form-item :label="$t('commons.project')" prop="projectId">
<el-select size="small" v-model="formData.projectId" class="project-select" clearable>
<el-option v-for="(project, index) in projects" :key="index" :label="project.name" :value="project.id"/>
</el-select>
</el-form-item>
<el-form-item v-if="useEnvironment || selectedPlatformValue == 'Swagger2'" :label="$t('api_test.environment.environment_config')" prop="environmentId">
<el-select v-if="showEnvironmentSelect" size="small" v-model="formData.environmentId" class="environment-select" clearable>
<el-option v-for="(environment, index) in environments" :key="index" :label="environment.name" :value="environment.id"/>
<el-button class="environment-button" size="mini" type="primary" @click="openEnvironmentConfig">{{$t('api_test.environment.environment_config')}}</el-button>
<template v-slot:empty>
<div class="empty-environment">
<el-button :disabled="formData.projectId == ''" class="environment-button" size="mini" type="primary" @click="openEnvironmentConfig">{{$t('api_test.environment.environment_config')}}</el-button>
</div>
</template>
</el-select>
</el-form-item>
<el-form-item v-if="selectedPlatformValue != 'Swagger2'" prop="useEnvironment">
<el-checkbox v-model="useEnvironment">{{$t('api_test.environment.config_environment')}}</el-checkbox>
</el-form-item>
<el-form-item :label="'Swagger URL'" prop="wgerUrl" v-if="selectedPlatformValue == 'Swagger2' && swaggerUrlEable">
<el-input size="small" v-model="formData.swaggerUrl" clearable show-word-limit/>
</el-form-item>
<el-form-item v-if="selectedPlatformValue == 'Swagger2'">
<el-switch
v-model="swaggerUrlEable"
:active-text="$t('api_test.api_import.swagger_url_import')">
</el-switch>
</el-form-item>
</el-col>
<el-col :span="1" v-if="selectedPlatformValue != 'Swagger2' || (selectedPlatformValue == 'Swagger2' && !swaggerUrlEable)">
<el-divider direction="vertical"/>
</el-col>
<el-col :span="12" v-if="selectedPlatformValue != 'Swagger2' || (selectedPlatformValue == 'Swagger2' && !swaggerUrlEable)">
<el-upload <el-upload
v-if="selectedPlatformValue != 'Swagger2' || (selectedPlatformValue == 'Swagger2' && !swaggerUrlEable)"
class="api-upload" class="api-upload"
drag drag
action="" action=""
@ -74,8 +36,18 @@
<div class="el-upload__text" v-html="$t('load_test.upload_tips')"></div> <div class="el-upload__text" v-html="$t('load_test.upload_tips')"></div>
<div class="el-upload__tip" slot="tip">{{$t('api_test.api_import.file_size_limit')}}</div> <div class="el-upload__tip" slot="tip">{{$t('api_test.api_import.file_size_limit')}}</div>
</el-upload> </el-upload>
</el-col>
</el-row> <el-form-item :label="'Swagger URL'" prop="wgerUrl" v-if="isSwagger2 && swaggerUrlEable" class="swagger-url">
<el-input size="small" v-model="formData.swaggerUrl" clearable show-word-limit/>
</el-form-item>
<el-form-item v-if="isSwagger2" class="swagger-enable" :class="{'swagger-url-disable': !swaggerUrlEable}">
<el-switch
v-model="swaggerUrlEable"
:active-text="$t('api_test.api_import.swagger_url_import')">
</el-switch>
</el-form-item>
</el-form> </el-form>
<div class="format-tip"> <div class="format-tip">
@ -87,19 +59,16 @@
</div> </div>
</div> </div>
<api-environment-config ref="environmentConfig" @close="getEnvironments"/>
</el-dialog> </el-dialog>
</template> </template>
<script> <script>
import MsDialogFooter from "../../../../common/components/MsDialogFooter"; import MsDialogFooter from "../../../../common/components/MsDialogFooter";
import ApiEnvironmentConfig from "../environment/ApiEnvironmentConfig";
import {listenGoBack, removeGoBackListener} from "@/common/js/utils"; import {listenGoBack, removeGoBackListener} from "@/common/js/utils";
export default { export default {
name: "ApiImport", name: "ApiImport",
components: {ApiEnvironmentConfig, MsDialogFooter}, components: {MsDialogFooter},
data() { data() {
return { return {
visible: false, visible: false,
@ -135,31 +104,18 @@
environments: [], environments: [],
useEnvironment: false, useEnvironment: false,
formData: { formData: {
name: '',
environmentId: '',
projectId: '', projectId: '',
file: undefined, file: undefined,
swaggerUrl: '' swaggerUrl: ''
}, },
rules: {},
currentModule: {}, currentModule: {},
rules: {
name: [
{required: true, message: this.$t('commons.input_name'), trigger: 'blur'},
{max: 60, message: this.$t('commons.input_limit', [1, 60]), trigger: 'blur'}
],
environmentId: [
{required: true, message: this.$t('api_test.environment.select_environment'), trigger: 'blur'},
],
projectId: [
{required: true, message: this.$t('api_test.select_project'), trigger: 'blur'},
]
},
fileList: [] fileList: []
} }
}, },
props: ['projectId'],
activated() { activated() {
this.selectedPlatform = this.platforms[0]; this.selectedPlatform = this.platforms[0];
this.getProjects();
}, },
watch: { watch: {
selectedPlatformValue() { selectedPlatformValue() {
@ -170,8 +126,10 @@
} }
} }
}, },
'formData.projectId'() { },
this.getEnvironments(); computed: {
isSwagger2() {
return this.selectedPlatformValue === 'Swagger2';
} }
}, },
methods: { methods: {
@ -195,63 +153,21 @@
this.$warning(this.$t('api_test.api_import.suffixFormatErr')); this.$warning(this.$t('api_test.api_import.suffixFormatErr'));
return false; return false;
} }
if (file.size / 1024 / 1024 > 20) { if (file.size / 1024 / 1024 > 20) {
this.$warning(this.$t('test_track.case.import.upload_limit_size')); this.$warning(this.$t('test_track.case.import.upload_limit_size'));
return false; return false;
} }
return true; return true;
}, },
getEnvironments() {
if (this.formData.projectId) {
this.$get('/api/environment/list/' + this.formData.projectId, response => {
this.environments = response.data;
let hasEnvironmentId = false;
this.environments.forEach(env => {
if (env.id === this.formData.environmentId) {
hasEnvironmentId = true;
}
});
if (!hasEnvironmentId) {
this.formData.environmentId = '';
}
});
} else {
this.environments = [];
this.formData.environmentId = '';
}
},
getProjects() {
this.result = this.$get("/project/listAll", response => {
this.projects = response.data;
})
},
openEnvironmentConfig() {
if (!this.formData.projectId) {
this.$error(this.$t('api_test.select_project'));
return;
}
this.showEnvironmentSelect = false;
this.$refs.environmentConfig.open(this.formData.projectId);
this.showEnvironmentSelect = true;
},
save() { save() {
this.$refs.form.validate(valid => { this.$refs.form.validate(valid => {
if (valid) { if (valid) {
let param = {};
Object.assign(param, this.formData);
param.platform = this.selectedPlatformValue;
param.useEnvironment = this.useEnvironment;
param.moduleId = this.currentModule.id;
param.modulePath = this.currentModule.path;
if ((this.selectedPlatformValue != 'Swagger2' || (this.selectedPlatformValue == 'Swagger2' && !this.swaggerUrlEable)) && !this.formData.file) { if ((this.selectedPlatformValue != 'Swagger2' || (this.selectedPlatformValue == 'Swagger2' && !this.swaggerUrlEable)) && !this.formData.file) {
this.$warning(this.$t('commons.please_upload')); this.$warning(this.$t('commons.please_upload'));
return; return;
} }
if (!this.swaggerUrlEable) { let param = this.buildParam();
param.swaggerUrl = undefined; this.result = this.$fileUpload('/api/definition/import', param.file, null, this.buildParam(), response => {
}
this.result = this.$fileUpload('/api/definition/import', param.file, null, param, response => {
let res = response.data; let res = response.data;
this.$success(this.$t('test_track.case.import.success')); this.$success(this.$t('test_track.case.import.success'));
this.visible = false; this.visible = false;
@ -262,11 +178,20 @@
} }
}); });
}, },
buildParam() {
let param = {};
Object.assign(param, this.formData);
param.platform = this.selectedPlatformValue;
param.moduleId = this.currentModule.id;
param.modulePath = this.currentModule.path;
param.projectId = this.projectId;
if (!this.swaggerUrlEable) {
param.swaggerUrl = undefined;
}
return param;
},
close() { close() {
this.formData = { this.formData = {
name: '',
environmentId: '',
projectId: '',
file: undefined, file: undefined,
swaggerUrl: '' swaggerUrl: ''
}; };
@ -280,6 +205,10 @@
<style scoped> <style scoped>
.api-import >>> .el-dialog {
min-width: 700px;
}
.format-tip { .format-tip {
background: #EDEDED; background: #EDEDED;
} }
@ -325,29 +254,18 @@
margin-left: 10px; margin-left: 10px;
} }
.environment-button {
margin-left: 20px;
padding: 7px;
}
.empty-environment {
padding: 10px 0px;
}
.el-form { .el-form {
padding: 30px 10px; padding: 30px 10px;
} }
.el-divider {
height: 200px;
}
.name-input {
max-width: 195px;
}
.dialog-footer { .dialog-footer {
float: right; float: right;
} }
.swagger-url-disable {
margin-top: 10px;
margin-left: 80px;
}
</style> </style>