This commit is contained in:
chenjianxing 2021-01-20 15:04:13 +08:00
commit 65ad9b1e80
19 changed files with 613 additions and 240 deletions

View File

@ -10,6 +10,7 @@ import io.metersphere.api.dto.definition.*;
import io.metersphere.api.dto.definition.parse.ApiDefinitionImport;
import io.metersphere.api.service.ApiDefinitionService;
import io.metersphere.base.domain.ApiDefinition;
import io.metersphere.base.domain.Schedule;
import io.metersphere.commons.constants.RoleConstants;
import io.metersphere.commons.json.JSONSchemaGenerator;
import io.metersphere.commons.utils.PageUtils;
@ -139,6 +140,11 @@ public class ApiDefinitionController {
return apiDefinitionService.apiTestImport(file, request);
}
@PostMapping(value = "/schedule/create")
public void createSchedule(@RequestBody Schedule request) {
apiDefinitionService.createSchedule(request);
}
@PostMapping("/getReference")
public ReferenceDTO getReference(@RequestBody ApiScenarioRequest request) {
return apiDefinitionService.getReference(request);
@ -165,5 +171,4 @@ public class ApiDefinitionController {
public String preview(@RequestBody String jsonSchema) {
return JSONSchemaGenerator.getJson(jsonSchema);
}
}

View File

@ -8,7 +8,7 @@ import org.apache.commons.lang3.StringUtils;
@Data
public class MsExtractCommon extends MsExtractType{
private String variable;
private String value; // value: ${variable}
private String value;
private String expression;
private String description;
private boolean multipleMatching;

View File

@ -24,10 +24,14 @@ import io.metersphere.base.mapper.ext.ExtApiScenarioMapper;
import io.metersphere.base.mapper.ext.ExtTestPlanMapper;
import io.metersphere.commons.constants.APITestStatus;
import io.metersphere.commons.constants.ApiRunMode;
import io.metersphere.commons.constants.ScheduleGroup;
import io.metersphere.commons.constants.ScheduleType;
import io.metersphere.commons.exception.MSException;
import io.metersphere.commons.utils.*;
import io.metersphere.i18n.Translator;
import io.metersphere.job.sechedule.SwaggerUrlImportJob;
import io.metersphere.service.FileService;
import io.metersphere.service.ScheduleService;
import io.metersphere.track.request.testcase.ApiCaseRelevanceRequest;
import io.metersphere.track.request.testcase.QueryTestPlanRequest;
import org.apache.commons.collections.CollectionUtils;
@ -73,6 +77,8 @@ public class ApiDefinitionService {
private ExtTestPlanMapper extTestPlanMapper;
@Resource
private ProjectMapper projectMapper;
@Resource
private ScheduleService scheduleService;
private static Cache cache = Cache.newHardMemoryCache(0, 3600 * 24);
@ -587,4 +593,19 @@ public class ApiDefinitionService {
}
}
}
/*swagger定时导入*/
public void createSchedule(Schedule request) {
Schedule schedule = scheduleService.buildApiTestSchedule(request);
schedule.setJob(SwaggerUrlImportJob.class.getName());
schedule.setGroup(ScheduleGroup.SWAGGER_IMPORT.name());
schedule.setType(ScheduleType.CRON.name());
scheduleService.addSchedule(schedule);
this.addOrUpdateSwaggerImportCronJob(request);
}
private void addOrUpdateSwaggerImportCronJob(Schedule request) {
scheduleService.addOrUpdateCronJob(request, SwaggerUrlImportJob.getJobKey(request.getResourceId()), SwaggerUrlImportJob.getTriggerKey(request.getResourceId()), SwaggerUrlImportJob.class);
}
}

View File

@ -83,7 +83,7 @@ public class ApiTestCaseService {
}
public List<ApiTestCaseDTO> listSimple(ApiTestCaseRequest request) {
request = this.initRequest(request,true,true);
request = this.initRequest(request, true, true);
List<ApiTestCaseDTO> apiTestCases = extApiTestCaseMapper.listSimple(request);
if (CollectionUtils.isEmpty(apiTestCases)) {
@ -95,16 +95,17 @@ public class ApiTestCaseService {
/**
* 初始化部分参数
*
* @param request
* @param setDefultOrders
* @param checkThisWeekData
* @return
*/
private ApiTestCaseRequest initRequest(ApiTestCaseRequest request, boolean setDefultOrders, boolean checkThisWeekData) {
if(setDefultOrders){
if (setDefultOrders) {
request.setOrders(ServiceUtils.getDefaultOrder(request.getOrders()));
}
if(checkThisWeekData){
if (checkThisWeekData) {
if (request.isSelectThisWeedData()) {
Map<String, Date> weekFirstTimeAndLastTime = DateUtils.getWeedFirstTimeAndLastTime(new Date());
Date weekFirstTime = weekFirstTimeAndLastTime.get("firstTime");
@ -412,23 +413,38 @@ public class ApiTestCaseService {
apiDefinitionWithBLOBs.setUpdateTime(System.currentTimeMillis());
apiTestCaseMapper.updateByExampleSelective(apiDefinitionWithBLOBs, apiDefinitionExample);
}
if ((StringUtils.isNotEmpty(request.getMethod()) || StringUtils.isNotEmpty(request.getPath())) && request.getProtocol().equals(RequestType.HTTP)) {
if ((StringUtils.isNotEmpty(request.getMethod()) || StringUtils.isNotEmpty(request.getPath())) && RequestType.HTTP.equals(request.getProtocol())) {
List<ApiTestCaseWithBLOBs> bloBs = apiTestCaseMapper.selectByExampleWithBLOBs(apiDefinitionExample);
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
ApiTestCaseMapper batchMapper = sqlSession.getMapper(ApiTestCaseMapper.class);
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
bloBs.forEach(apiTestCase -> {
MsHTTPSamplerProxy req = JSON.parseObject(apiTestCase.getRequest(), MsHTTPSamplerProxy.class);
if (StringUtils.isNotEmpty(request.getMethod())) {
req.setMethod(request.getMethod());
}
if (StringUtils.isNotEmpty(request.getPath())) {
req.setPath(request.getPath());
try {
JSONObject element = JSON.parseObject(apiTestCase.getRequest());
if (element != null && StringUtils.isNotEmpty(element.getString("hashTree"))) {
LinkedList<MsTestElement> elements = mapper.readValue(element.getString("hashTree"), new TypeReference<LinkedList<MsTestElement>>() {
});
req.setHashTree(elements);
}
if (StringUtils.isNotEmpty(request.getMethod())) {
req.setMethod(request.getMethod());
}
if (StringUtils.isNotEmpty(request.getPath())) {
req.setPath(request.getPath());
}
} catch (Exception e) {
e.printStackTrace();
LogUtil.error(e.getMessage());
}
String requestStr = JSON.toJSONString(req);
apiTestCase.setRequest(requestStr);
batchMapper.updateByPrimaryKeySelective(apiTestCase);
});
sqlSession.flushStatements();
}
}

View File

@ -1,8 +1,9 @@
package io.metersphere.base.domain;
import java.io.Serializable;
import lombok.Data;
import java.io.Serializable;
@Data
public class Schedule implements Serializable {
private String id;
@ -35,4 +36,6 @@ public class Schedule implements Serializable {
//定时任务来源 测试计划/测试场景
private String scheduleFrom;
private String swaggerUrl;
}

View File

@ -33,4 +33,6 @@ public interface ApiDefinitionExecResultMapper {
int updateByPrimaryKeyWithBLOBs(ApiDefinitionExecResult record);
int updateByPrimaryKey(ApiDefinitionExecResult record);
String selectExecResult(String resourceId);
}

View File

@ -210,6 +210,9 @@
<include refid="Example_Where_Clause" />
</if>
</select>
<select id="selectExecResult" resultType="java.lang.String">
select ader.status from api_definition_exec_result ader where ader.resource_id=#{resourceId}
</select>
<update id="updateByExampleSelective" parameterType="map">
update api_definition_exec_result
<set>

View File

@ -1,5 +1,5 @@
package io.metersphere.commons.constants;
public enum ScheduleGroup {
API_TEST, PERFORMANCE_TEST, API_SCENARIO_TEST,TEST_PLAN_TEST
API_TEST, PERFORMANCE_TEST, API_SCENARIO_TEST, TEST_PLAN_TEST, SWAGGER_IMPORT
}

View File

@ -0,0 +1,37 @@
package io.metersphere.job.sechedule;
import io.metersphere.api.dto.ApiTestImportRequest;
import io.metersphere.api.service.ApiDefinitionService;
import io.metersphere.commons.constants.ScheduleGroup;
import io.metersphere.commons.utils.CommonBeanFactory;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.quartz.JobKey;
import org.quartz.TriggerKey;
public class SwaggerUrlImportJob extends MsScheduleJob {
private ApiDefinitionService apiDefinitionService;
public SwaggerUrlImportJob() {
apiDefinitionService = (ApiDefinitionService) CommonBeanFactory.getBean(ApiDefinitionService.class);
}
@Override
void businessExecute(JobExecutionContext context) {
JobDataMap jobDataMap = context.getJobDetail().getJobDataMap();
String resourceId = jobDataMap.getString("resourceId");
String swaggerUrl = jobDataMap.getString("swaggerUrl");
ApiTestImportRequest request = new ApiTestImportRequest();
request.setProjectId(resourceId);
request.setSwaggerUrl(swaggerUrl);
apiDefinitionService.apiTestImport(null, request);
}
public static JobKey getJobKey(String resourceId) {
return new JobKey(resourceId, ScheduleGroup.SWAGGER_IMPORT.name());
}
public static TriggerKey getTriggerKey(String resourceId) {
return new TriggerKey(resourceId, ScheduleGroup.SWAGGER_IMPORT.name());
}
}

View File

@ -104,6 +104,7 @@ public class ScheduleService {
public void startEnableSchedules() {
List<Schedule> Schedules = getEnableSchedule();
Schedules.forEach(schedule -> {
try {
if (schedule.getEnable()) {
@ -122,7 +123,7 @@ public class ScheduleService {
public Schedule buildApiTestSchedule(Schedule request) {
Schedule schedule = new Schedule();
schedule.setResourceId(request.getResourceId());
schedule.setEnable(request.getEnable());
schedule.setEnable(true);
schedule.setValue(request.getValue().trim());
schedule.setKey(request.getResourceId());
schedule.setUserId(SessionUtils.getUser().getId());

@ -1 +1 @@
Subproject commit c2ed883e9be6fc7e01589f81916bf4ddc62148c0
Subproject commit 7f7808c6f0457dd2df6b19a1622558f3f8122646

View File

@ -56,7 +56,8 @@
@command="handleCommand" v-tester>
+{{$t('api_test.definition.request.case')}}
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="run">{{$t('commons.test')}}</el-dropdown-item>
<el-dropdown-item command="run">{{$t('api_test.automation.batch_execute')}}</el-dropdown-item>
<el-dropdown-item command="batch_edit_case">{{$t('test_track.case.batch_edit_case')}}</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</el-col>
@ -169,6 +170,9 @@
if (e === "run") {
this.$emit('batchRun');
}
if (e === 'batch_edit_case') {
this.$emit('batchEditCase');
}
},
getColor(enable, method) {
if (enable) {

View File

@ -9,6 +9,7 @@
@addCase="addCase"
@batchRun="batchRun"
@selectAll="selectAll"
@batchEditCase="batchEditCase"
:condition="condition"
:priorities="priorities"
:apiCaseList="apiCaseList"
@ -39,9 +40,9 @@
<!-- 执行组件 -->
<ms-run :debug="false" :environment="environment" :reportId="reportId" :run-data="runData"
@runRefresh="runRefresh" ref="runTest"/>
<!--批量编辑-->
<ms-batch-edit ref="batchEdit" @batchEdit="batchEdit" :typeArr="typeArr" :value-arr="valueArr"/>
</div>
</template>
<script>
@ -52,6 +53,8 @@
import MsDrawer from "../../../../common/components/MsDrawer";
import {CASE_ORDER} from "../../model/JsonData";
import {API_CASE_CONFIGS} from "@/business/components/common/components/search/search-components";
import MsBatchEdit from "../basis/BatchEdit";
import {CASE_PRIORITY, REQ_METHOD} from "../../model/JsonData";
export default {
name: 'ApiCaseList',
@ -60,6 +63,7 @@
MsRun,
ApiCaseHeader,
ApiCaseItem,
MsBatchEdit
},
props: {
createCase: String,
@ -82,6 +86,7 @@
singleLoading: false,
singleRunId: "",
runData: [],
selectdCases: [],
reportId: "",
projectId: "",
testCaseId: "",
@ -90,7 +95,22 @@
condition: {
components: API_CASE_CONFIGS
},
api: {}
api: {},
typeArr: [
{id: 'priority', name: this.$t('test_track.case.priority')},
{id: 'method', name: this.$t('api_test.definition.api_type')},
{id: 'path', name: this.$t('api_test.request.path')},
],
priorityFilters: [
{text: 'P0', value: 'P0'},
{text: 'P1', value: 'P1'},
{text: 'P2', value: 'P2'},
{text: 'P3', value: 'P3'}
],
valueArr: {
priority: CASE_PRIORITY,
method: REQ_METHOD,
},
}
},
watch: {
@ -108,8 +128,6 @@
this.projectId = getCurrentProjectID();
if (this.createCase) {
this.sysAddition();
} else {
this.getApiTest();
}
},
computed: {
@ -133,11 +151,19 @@
this.condition.projectId = this.projectId;
this.condition.apiDefinitionId = this.api.id;
this.$post("/api/testcase/list", this.condition, response => {
for (let index in response.data) {
let test = response.data[index];
test.request = JSON.parse(test.request);
}
this.apiCaseList = response.data;
this.apiCaseList.forEach(apiCase => {
if (apiCase.tags && apiCase.tags.length > 0) {
apiCase.tags = JSON.parse(apiCase.tags);
this.$set(apiCase, 'selected', false);
}
if (Object.prototype.toString.call(apiCase.request).match(/\[object (\w+)\]/)[1].toLowerCase() != 'object') {
apiCase.request = JSON.parse(apiCase.request);
}
if (!apiCase.request.hashTree) {
apiCase.request.hashTree = [];
}
})
this.addCase();
});
},
@ -180,24 +206,22 @@
this.condition.apiDefinitionId = this.api.id;
}
this.result = this.$post("/api/testcase/list", this.condition, response => {
for (let index in response.data) {
let test = response.data[index];
test.request = JSON.parse(test.request);
if (!test.request.hashTree) {
test.request.hashTree = [];
}
}
this.apiCaseList = response.data;
if (addCase && this.apiCaseList.length == 0 && !this.loaded) {
this.addCase();
}
this.apiCaseList.forEach(apiCase => {
if (apiCase.tags && apiCase.tags.length > 0) {
apiCase.tags = JSON.parse(apiCase.tags);
this.$set(apiCase, 'selected', false);
}
if (Object.prototype.toString.call(apiCase.request).match(/\[object (\w+)\]/)[1].toLowerCase() != 'object') {
apiCase.request = JSON.parse(apiCase.request);
}
if (!apiCase.request.hashTree) {
apiCase.request.hashTree = [];
}
})
if (addCase && this.apiCaseList.length == 0 && !this.loaded) {
this.addCase();
}
});
}
},
@ -249,6 +273,7 @@
return;
}
this.runData = [];
this.batchLoadingIds = [];
if (this.apiCaseList.length > 0) {
this.apiCaseList.forEach(item => {
if (item.selected && item.id) {
@ -268,6 +293,37 @@
this.$warning("没有可执行的用例!");
}
},
batchEditCase() {
if (this.apiCaseList.length > 0) {
this.apiCaseList.forEach(item => {
if (item.selected && item.id) {
this.selectdCases.push(item.id);
}
})
}
if (this.selectdCases.length == 0) {
this.$warning("请选择用例!");
return;
}
this.$refs.batchEdit.open();
},
batchEdit(form) {
let param = {};
param[form.type] = form.value;
param.ids = this.selectdCases;
param.projectId = getCurrentProjectID();
if (this.api) {
param.protocol = this.api.protocol;
}
param.selectAllDate = this.isSelectAllDate;
param.unSelectIds = this.unSelection;
param = Object.assign(param, this.condition);
this.$post('/api/testcase/batch/editByParam', param, () => {
this.$success(this.$t('commons.save_success'));
this.selectdCases = [];
this.getApiTest();
});
},
}
}
</script>

View File

@ -3,17 +3,17 @@
:visible.sync="visible" class="api-import" v-loading="result.loading" @close="close">
<div class="header-bar">
<div>{{$t('api_test.api_import.data_format')}}</div>
<div>{{ $t('api_test.api_import.data_format') }}</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 v-for="(item, index) in platforms" :key="index" :label="item.value">{{ item.name }}</el-radio>
</el-radio-group>
<div class="operate-button">
<el-button class="save-button" type="primary" plain @click="save">
{{$t('commons.save')}}
{{ $t('commons.save') }}
</el-button>
<el-button class="cancel-button" type="warning" plain @click="visible = false">
{{$t('commons.cancel')}}
{{ $t('commons.cancel') }}
</el-button>
</div>
</div>
@ -34,7 +34,7 @@
multiple>
<i class="el-icon-upload"></i>
<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-form-item :label="'Swagger URL'" prop="wgerUrl" v-if="isSwagger2 && swaggerUrlEable" class="swagger-url">
@ -48,14 +48,23 @@
</el-switch>
</el-form-item>
<el-form-item v-if="isSwagger2 && swaggerUrlEable">
<el-switch
v-model="swaggerSynchronization"
@click.native="scheduleEdit"
:active-text="$t('api_test.api_import.timing_synchronization')">
</el-switch>
</el-form-item>
<schedule-import ref="scheduleEdit"></schedule-import>
</el-form>
<div class="format-tip">
<div>
<span>{{$t('api_test.api_import.tip')}}{{selectedPlatform.tip}}</span>
<span>{{ $t('api_test.api_import.tip') }}{{ selectedPlatform.tip }}</span>
</div>
<div>
<span>{{$t('api_test.api_import.export_tip')}}{{selectedPlatform.exportTip}}</span>
<span>{{ $t('api_test.api_import.export_tip') }}{{ selectedPlatform.exportTip }}</span>
</div>
</div>
@ -63,217 +72,223 @@
</template>
<script>
import MsDialogFooter from "../../../../common/components/MsDialogFooter";
import {listenGoBack, removeGoBackListener} from "@/common/js/utils";
import {getCurrentProjectID} from "../../../../../../common/js/utils";
export default {
name: "ApiImport",
components: {MsDialogFooter},
props: {
saved: {
type: Boolean,
default: true,
}
},
data() {
return {
visible: false,
swaggerUrlEable: false,
showEnvironmentSelect: true,
platforms: [
{
name: 'Metersphere',
value: 'Metersphere',
tip: this.$t('api_test.api_import.ms_tip'),
exportTip: this.$t('api_test.api_import.ms_export_tip'),
suffixes: new Set(['json'])
},
{
name: 'Postman',
value: 'Postman',
tip: this.$t('api_test.api_import.postman_tip'),
exportTip: this.$t('api_test.api_import.post_export_tip'),
suffixes: new Set(['json'])
},
{
name: 'Swagger',
value: 'Swagger2',
tip: this.$t('api_test.api_import.swagger_tip'),
exportTip: this.$t('api_test.api_import.swagger_export_tip'),
suffixes: new Set(['json'])
}
],
selectedPlatform: {},
selectedPlatformValue: 'Metersphere',
result: {},
projects: [],
environments: [],
useEnvironment: false,
formData: {
file: undefined,
swaggerUrl: ''
import MsDialogFooter from "../../../../common/components/MsDialogFooter";
import {listenGoBack, removeGoBackListener} from "@/common/js/utils";
import {getCurrentProjectID} from "../../../../../../common/js/utils";
import ScheduleImport from "@/business/components/api/definition/components/import/ImportScheduleEdit";
export default {
name: "ApiImport",
components: {ScheduleImport, MsDialogFooter},
props: {
saved: {
type: Boolean,
default: true,
}
},
data() {
return {
visible: false,
swaggerUrlEable: false,
swaggerSynchronization:false,
showEnvironmentSelect: true,
platforms: [
{
name: 'Metersphere',
value: 'Metersphere',
tip: this.$t('api_test.api_import.ms_tip'),
exportTip: this.$t('api_test.api_import.ms_export_tip'),
suffixes: new Set(['json'])
},
rules: {},
currentModule: {},
fileList: []
}
},
activated() {
this.selectedPlatform = this.platforms[0];
},
watch: {
selectedPlatformValue() {
for (let i in this.platforms) {
if (this.platforms[i].value === this.selectedPlatformValue) {
this.selectedPlatform = this.platforms[i];
break;
}
{
name: 'Postman',
value: 'Postman',
tip: this.$t('api_test.api_import.postman_tip'),
exportTip: this.$t('api_test.api_import.post_export_tip'),
suffixes: new Set(['json'])
},
{
name: 'Swagger',
value: 'Swagger2',
tip: this.$t('api_test.api_import.swagger_tip'),
exportTip: this.$t('api_test.api_import.swagger_export_tip'),
suffixes: new Set(['json'])
}
],
selectedPlatform: {},
selectedPlatformValue: 'Metersphere',
result: {},
projects: [],
environments: [],
useEnvironment: false,
formData: {
file: undefined,
swaggerUrl: ''
},
},
computed: {
isSwagger2() {
return this.selectedPlatformValue === 'Swagger2';
rules: {},
currentModule: {},
fileList: []
}
},
activated() {
this.selectedPlatform = this.platforms[0];
},
watch: {
selectedPlatformValue() {
for (let i in this.platforms) {
if (this.platforms[i].value === this.selectedPlatformValue) {
this.selectedPlatform = this.platforms[i];
break;
}
}
},
methods: {
open(module) {
this.currentModule = module;
this.visible = true;
listenGoBack(this.close);
},
upload(file) {
this.formData.file = file.file;
},
handleExceed(files, fileList) {
this.$warning(this.$t('test_track.case.import.upload_limit_count'));
},
handleRemove(file, fileList) {
this.formData.file = undefined;
},
uploadValidate(file, fileList) {
let suffix = file.name.substring(file.name.lastIndexOf('.') + 1);
if (this.selectedPlatform.suffixes && !this.selectedPlatform.suffixes.has(suffix)) {
this.$warning(this.$t('api_test.api_import.suffixFormatErr'));
},
computed: {
isSwagger2() {
return this.selectedPlatformValue === 'Swagger2';
}
},
methods: {
scheduleEdit(){
if(this.swaggerSynchronization){
this.$refs.scheduleEdit.open(this.buildParam());
}
},
open(module) {
this.currentModule = module;
this.visible = true;
listenGoBack(this.close);
},
upload(file) {
this.formData.file = file.file;
},
handleExceed(files, fileList) {
this.$warning(this.$t('test_track.case.import.upload_limit_count'));
},
handleRemove(file, fileList) {
this.formData.file = undefined;
},
uploadValidate(file, fileList) {
let suffix = file.name.substring(file.name.lastIndexOf('.') + 1);
if (this.selectedPlatform.suffixes && !this.selectedPlatform.suffixes.has(suffix)) {
this.$warning(this.$t('api_test.api_import.suffixFormatErr'));
return false;
}
if (file.size / 1024 / 1024 > 20) {
this.$warning(this.$t('test_track.case.import.upload_limit_size'));
return false;
}
return true;
},
save() {
this.$refs.form.validate(valid => {
if (valid) {
if ((this.selectedPlatformValue != 'Swagger2' || (this.selectedPlatformValue == 'Swagger2' && !this.swaggerUrlEable)) && !this.formData.file) {
this.$warning(this.$t('commons.please_upload'));
return;
}
let param = this.buildParam();
this.result = this.$fileUpload('/api/definition/import', param.file, null, this.buildParam(), response => {
let res = response.data;
this.$success(this.$t('test_track.case.import.success'));
this.visible = false;
this.$emit('refresh', res);
});
} else {
return false;
}
if (file.size / 1024 / 1024 > 20) {
this.$warning(this.$t('test_track.case.import.upload_limit_size'));
return false;
}
return true;
},
save() {
this.$refs.form.validate(valid => {
if (valid) {
if ((this.selectedPlatformValue != 'Swagger2' || (this.selectedPlatformValue == 'Swagger2' && !this.swaggerUrlEable)) && !this.formData.file) {
this.$warning(this.$t('commons.please_upload'));
return;
}
let param = this.buildParam();
this.result = this.$fileUpload('/api/definition/import', param.file, null, this.buildParam(), response => {
let res = response.data;
this.$success(this.$t('test_track.case.import.success'));
this.visible = false;
this.$emit('refresh', res);
});
} else {
return false;
}
});
},
buildParam() {
let param = {};
Object.assign(param, this.formData);
param.platform = this.selectedPlatformValue;
param.saved = this.saved;
if (this.currentModule) {
param.moduleId = this.currentModule.id;
param.modulePath = this.currentModule.path;
}
param.projectId = getCurrentProjectID();
if (!this.swaggerUrlEable) {
param.swaggerUrl = undefined;
}
return param;
},
close() {
this.formData = {
file: undefined,
swaggerUrl: ''
};
this.fileList = [];
removeGoBackListener(this.close);
this.visible = false;
});
},
buildParam() {
let param = {};
Object.assign(param, this.formData);
param.platform = this.selectedPlatformValue;
param.saved = this.saved;
if (this.currentModule) {
param.moduleId = this.currentModule.id;
param.modulePath = this.currentModule.path;
}
param.projectId = getCurrentProjectID();
if (!this.swaggerUrlEable) {
param.swaggerUrl = undefined;
}
return param;
},
close() {
this.formData = {
file: undefined,
swaggerUrl: ''
};
this.fileList = [];
removeGoBackListener(this.close);
this.visible = false;
}
}
}
</script>
<style scoped>
.api-import >>> .el-dialog {
min-width: 700px;
}
.api-import >>> .el-dialog {
min-width: 700px;
}
.format-tip {
background: #EDEDED;
}
.format-tip {
background: #EDEDED;
}
.api-upload {
text-align: center;
margin: auto 0;
}
.api-upload {
text-align: center;
margin: auto 0;
}
.api-upload >>> .el-upload {
width: 100%;
max-width: 350px;
}
.api-upload >>> .el-upload {
width: 100%;
max-width: 350px;
}
.api-upload >>> .el-upload-dragger {
width: 100%;
}
.api-upload >>> .el-upload-dragger {
width: 100%;
}
.el-radio-group {
margin: 10px 0;
}
.el-radio-group {
margin: 10px 0;
}
.header-bar, .format-tip, .el-form {
border: solid #E1E1E1 1px;
margin: 10px 0;
padding: 10px;
border-radius: 3px;
}
.header-bar, .format-tip, .el-form {
border: solid #E1E1E1 1px;
margin: 10px 0;
padding: 10px;
border-radius: 3px;
}
.header-bar {
padding: 10px 30px;
}
.header-bar {
padding: 10px 30px;
}
.api-import >>> .el-dialog__body {
padding: 15px 25px;
}
.api-import >>> .el-dialog__body {
padding: 15px 25px;
}
.operate-button {
float: right;
}
.operate-button {
float: right;
}
.save-button {
margin-left: 10px;
}
.save-button {
margin-left: 10px;
}
.el-form {
padding: 30px 10px;
}
.el-form {
padding: 30px 10px;
}
.dialog-footer {
float: right;
}
.dialog-footer {
float: right;
}
.swagger-url-disable {
margin-top: 10px;
.swagger-url-disable {
margin-top: 10px;
margin-left: 80px;
}
margin-left: 80px;
}
</style>

View File

@ -0,0 +1,213 @@
<template>
<el-dialog :close-on-click-modal="false" width="60%" class="schedule-edit" :visible.sync="dialogVisible"
append-to-body @close="close">
<template>
<div>
<el-tabs v-model="activeName">
<el-tab-pane :label="$t('schedule.edit_timer_task')" name="first">
<el-form :model="form" :rules="rules" ref="from">
<el-form-item
prop="cronValue">
<el-input :disabled="isReadOnly" v-model="form.cronValue" class="inp"
:placeholder="$t('schedule.please_input_cron_expression')"/>
<el-button :disabled="isReadOnly" type="primary" @click="saveCron" v-tester>{{
$t('commons.save')
}}
</el-button>
</el-form-item>
<el-form-item>
<el-link :disabled="isReadOnly" type="primary" @click="showCronDialog">
{{ $t('schedule.generate_expression') }}
</el-link>
</el-form-item>
<crontab-result :ex="form.cronValue" ref="crontabResult"/>
</el-form>
<el-dialog width="60%" :title="$t('schedule.generate_expression')" :visible.sync="showCron"
:modal="false">
<crontab @hide="showCron=false" @fill="crontabFill" :expression="schedule.value"
ref="crontab"/>
</el-dialog>
</el-tab-pane>
</el-tabs>
</div>
</template>
</el-dialog>
</template>
<script>
import {checkoutTestManagerOrTestUser, getCurrentUser, listenGoBack, removeGoBackListener} from "@/common/js/utils";
import Crontab from "@/business/components/common/cron/Crontab";
import CrontabResult from "@/business/components/common/cron/CrontabResult";
import {cronValidate} from "@/common/js/cron";
function defaultCustomValidate() {
return {pass: true};
}
export default {
name: "ImportScheduleEdit",
components: {CrontabResult, Crontab},
props: {
customValidate: {
type: Function,
default: defaultCustomValidate
},
isReadOnly: {
type: Boolean,
default: false
},
},
watch: {
'schedule.value'() {
this.form.cronValue = this.schedule.value;
}
},
data() {
const validateCron = (rule, cronValue, callback) => {
let customValidate = this.customValidate(this.getIntervalTime());
if (!cronValue) {
callback(new Error(this.$t('commons.input_content')));
} else if (!cronValidate(cronValue)) {
callback(new Error(this.$t('schedule.cron_expression_format_error')));
} else if (!customValidate.pass) {
callback(new Error(customValidate.info));
} else {
callback();
}
};
return {
operation: true,
dialogVisible: false,
schedule: {
value: "",
},
showCron: false,
form: {
cronValue: ""
},
activeName: 'first',
swaggerUrl:String,
projectId:String,
rules: {
cronValue: [{required: true, validator: validateCron, trigger: 'blur'}],
}
}
},
methods: {
currentUser: () => {
return getCurrentUser();
},
open(param) {
let paramTestId = "";
paramTestId=param.projectId
this.findSchedule(paramTestId);
this.project=param.projectId;
this.swaggerUrl=param.swaggerUrl;
this.dialogVisible = true;
this.form.cronValue = this.schedule.value;
listenGoBack(this.close);
this.activeName = 'first';
},
findSchedule(paramTestId) {
let scheduleResourceID = paramTestId;
let taskType="SWAGGER_IMPORT";
this.result = this.$get("/schedule/findOne/" + scheduleResourceID + "/" +taskType, response => {
if (response.data != null) {
this.schedule = response.data;
} else {
this.schedule = {};
}
});
},
crontabFill(value, resultList) {
//
this.form.cronValue = value;
this.$refs.crontabResult.resultList = resultList;
this.$refs['from'].validate();
},
showCronDialog() {
this.showCron = true;
},
saveCron() {
this.$refs['from'].validate((valid) => {
if (valid) {
this.intervalShortValidate();
let formCronValue = this.form.cronValue
this.schedule.enable = true;
this.schedule.value = formCronValue;
this.saveSchedule();
this.dialogVisible = false;
} else {
return false;
}
});
},
saveSchedule() {
this.checkScheduleEdit();
let param = {};
param = this.schedule;
param.resourceId =this.project;
param.swaggerUrl=this.swaggerUrl;
let url = '/api/definition/schedule/create';
this.$post(url, param, () => {
this.$success(this.$t('commons.save_success'));
});
},
checkScheduleEdit() {
if (this.create) {
this.$message(this.$t('api_test.environment.please_save_test'));
return false;
}
return true;
},
close() {
this.dialogVisible = false;
this.form.cronValue = '';
this.$refs['from'].resetFields();
if (!this.schedule.value) {
this.$refs.crontabResult.resultList = [];
}
removeGoBackListener(this.close);
},
intervalShortValidate() {
if (this.getIntervalTime() < 3 * 60 * 1000) {
// return false;
this.$info(this.$t('schedule.cron_expression_interval_short_error'));
}
return true;
},
resultListChange() {
this.$refs['from'].validate();
},
getIntervalTime() {
let resultList = this.$refs.crontabResult.resultList;
let time1 = new Date(resultList[0]);
let time2 = new Date(resultList[1]);
return time2 - time1;
},
},
computed: {
isTesterPermission() {
return checkoutTestManagerOrTestUser();
}
}
}
</script>
<style scoped>
.inp {
width: 50%;
margin-right: 20px;
}
.el-form-item {
margin-bottom: 10px;
}
</style>

View File

@ -413,19 +413,6 @@
this.initTable();
});
return;
// }
// this.$alert(this.$t('api_test.definition.request.delete_confirm') + ' ' + apiCase.name + " ", '', {
// confirmButtonText: this.$t('commons.confirm'),
// callback: (action) => {
// if (action === 'confirm') {
// let ids = [apiCase.id];
// this.$post('/api/testcase/removeToGc/', ids, () => {
// this.$success(this.$t('commons.delete_success'));
// this.initTable();
// });
// }
// }
// });
},
setEnvironment(data) {
this.environmentId = data.id;

View File

@ -829,6 +829,9 @@ export default {
swagger_export_tip: "Export jSON-formatted files via Swagger website",
suffixFormatErr: "The file format does not meet the requirements",
swagger_url_import: "Import using URL",
timing_synchronization:"Timing synchronization",
next_synchronization_time:"Next synchronization time",
},
home_page: {
unit_of_measurement: "",

View File

@ -831,6 +831,10 @@ export default {
swagger_export_tip: "通过 Swagger 页面导出",
suffixFormatErr: "文件格式不符合要求",
swagger_url_import: "使用URL导入",
timing_synchronization:"定时同步",
next_synchronization_time:"下次同步时间"
},
home_page: {
unit_of_measurement: "个",

View File

@ -830,6 +830,9 @@ export default {
swagger_export_tip: "通過 Swagger 頁面導出",
suffixFormatErr: "文件格式不符合要求",
swagger_url_import: "使用URL導入",
timing_synchronization:"定時同步",
next_synchronization_time:"下次同步時間",
},
home_page: {
unit_of_measurement: "個",