feat(接口自动化): 完成定时任务
This commit is contained in:
parent
6a8a718ff2
commit
2ebfcc2db1
|
@ -9,6 +9,7 @@ import io.metersphere.api.dto.automation.SaveApiScenarioRequest;
|
|||
import io.metersphere.api.dto.definition.RunDefinitionRequest;
|
||||
import io.metersphere.api.service.ApiAutomationService;
|
||||
import io.metersphere.base.domain.ApiScenario;
|
||||
import io.metersphere.base.domain.Schedule;
|
||||
import io.metersphere.commons.constants.RoleConstants;
|
||||
import io.metersphere.commons.utils.PageUtils;
|
||||
import io.metersphere.commons.utils.Pager;
|
||||
|
@ -46,6 +47,16 @@ public class ApiAutomationController {
|
|||
apiAutomationService.update(request, bodyFiles);
|
||||
}
|
||||
|
||||
@PostMapping(value = "/schedule/update")
|
||||
public void updateSchedule(@RequestBody Schedule schedule) {
|
||||
apiAutomationService.updateSchedule(schedule);
|
||||
}
|
||||
|
||||
@PostMapping(value = "/schedule/create")
|
||||
public void createSchedule(@RequestBody Schedule schedule) {
|
||||
apiAutomationService.createSchedule(schedule);
|
||||
}
|
||||
|
||||
@GetMapping("/delete/{id}")
|
||||
public void delete(@PathVariable String id) {
|
||||
apiAutomationService.delete(id);
|
||||
|
|
|
@ -15,5 +15,7 @@ public class RunScenarioRequest {
|
|||
|
||||
private String environmentId;
|
||||
|
||||
private String triggerMode;
|
||||
|
||||
private List<String> scenarioIds;
|
||||
}
|
||||
|
|
|
@ -17,12 +17,14 @@ import io.metersphere.base.domain.*;
|
|||
import io.metersphere.base.mapper.ApiScenarioMapper;
|
||||
import io.metersphere.base.mapper.ApiTagMapper;
|
||||
import io.metersphere.base.mapper.ext.ExtApiScenarioMapper;
|
||||
import io.metersphere.commons.constants.APITestStatus;
|
||||
import io.metersphere.commons.constants.ApiRunMode;
|
||||
import io.metersphere.commons.constants.*;
|
||||
import io.metersphere.commons.exception.MSException;
|
||||
import io.metersphere.commons.utils.LogUtil;
|
||||
import io.metersphere.commons.utils.SessionUtils;
|
||||
import io.metersphere.i18n.Translator;
|
||||
import io.metersphere.job.sechedule.ApiTestJob;
|
||||
import io.metersphere.job.sechedule.ScenarioJob;
|
||||
import io.metersphere.service.ScheduleService;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.jorphan.collections.HashTree;
|
||||
|
@ -55,6 +57,8 @@ public class ApiAutomationService {
|
|||
private ApiTestEnvironmentService environmentService;
|
||||
@Resource
|
||||
private ApiScenarioReportService apiReportService;
|
||||
@Resource
|
||||
private ScheduleService scheduleService;
|
||||
|
||||
private static final String BODY_FILE_DIR = "/opt/metersphere/data/body";
|
||||
|
||||
|
@ -120,6 +124,27 @@ public class ApiAutomationService {
|
|||
createBodyFiles(bodyUploadIds, bodyFiles);
|
||||
}
|
||||
|
||||
private Schedule buildApiTestSchedule(Schedule request) {
|
||||
Schedule schedule = scheduleService.buildApiTestSchedule(request);
|
||||
schedule.setJob(ScenarioJob.class.getName());
|
||||
schedule.setGroup(ScheduleGroup.SCENARIO_TEST.name());
|
||||
schedule.setType(ScheduleType.CRON.name());
|
||||
return schedule;
|
||||
}
|
||||
|
||||
private void addOrUpdateApiTestCronJob(Schedule request) {
|
||||
scheduleService.addOrUpdateCronJob(request, ApiTestJob.getJobKey(request.getResourceId()), ApiTestJob.getTriggerKey(request.getResourceId()), ApiTestJob.class);
|
||||
}
|
||||
|
||||
public void updateSchedule(Schedule schedule) {
|
||||
scheduleService.addSchedule(buildApiTestSchedule(schedule));
|
||||
addOrUpdateApiTestCronJob(schedule);
|
||||
}
|
||||
public void createSchedule(Schedule request) {
|
||||
scheduleService.addSchedule(buildApiTestSchedule(request));
|
||||
addOrUpdateApiTestCronJob(request);
|
||||
}
|
||||
|
||||
public void update(SaveApiScenarioRequest request, List<MultipartFile> bodyFiles) {
|
||||
checkNameExist(request);
|
||||
List<String> bodyUploadIds = new ArrayList<>(request.getBodyUploadIds());
|
||||
|
@ -218,7 +243,7 @@ public class ApiAutomationService {
|
|||
}
|
||||
}
|
||||
|
||||
private void createAPIReportResult(String id) {
|
||||
private void createAPIReportResult(String id, String triggerMode) {
|
||||
APIReportResult report = new APIReportResult();
|
||||
report.setId(id);
|
||||
report.setTestId(id);
|
||||
|
@ -228,6 +253,7 @@ public class ApiAutomationService {
|
|||
report.setUpdateTime(System.currentTimeMillis());
|
||||
report.setStatus(APITestStatus.Running.name());
|
||||
report.setUserId(SessionUtils.getUserId());
|
||||
report.setTriggerMode(triggerMode);
|
||||
apiReportService.addResult(report);
|
||||
|
||||
}
|
||||
|
@ -254,9 +280,11 @@ public class ApiAutomationService {
|
|||
MsScenario scenario = JSONObject.parseObject(item.getScenarioDefinition(), MsScenario.class);
|
||||
// 多态JSON普通转换会丢失内容,需要通过 ObjectMapper 获取
|
||||
LinkedList<MsTestElement> elements = mapper.readValue(element.getString("hashTree"),
|
||||
new TypeReference<LinkedList<MsTestElement>>() {});
|
||||
new TypeReference<LinkedList<MsTestElement>>() {
|
||||
});
|
||||
LinkedList<KeyValue> variables = mapper.readValue(element.getString("variables"),
|
||||
new TypeReference<LinkedList<KeyValue>>() {});
|
||||
new TypeReference<LinkedList<KeyValue>>() {
|
||||
});
|
||||
scenario.setHashTree(elements);
|
||||
scenario.setVariables(variables);
|
||||
LinkedList<MsTestElement> scenarios = new LinkedList<>();
|
||||
|
@ -270,7 +298,8 @@ public class ApiAutomationService {
|
|||
testPlan.toHashTree(jmeterTestPlanHashTree, testPlan.getHashTree(), new ParameterConfig());
|
||||
// 调用执行方法
|
||||
jMeterService.runDefinition(request.getId(), jmeterTestPlanHashTree, request.getReportId(), ApiRunMode.SCENARIO.name());
|
||||
createAPIReportResult(request.getId());
|
||||
|
||||
createAPIReportResult(request.getId(), request.getTriggerMode() == null ? ReportTriggerMode.API.name() : request.getTriggerMode());
|
||||
return request.getId();
|
||||
}
|
||||
|
||||
|
@ -297,7 +326,7 @@ public class ApiAutomationService {
|
|||
|
||||
// 调用执行方法
|
||||
jMeterService.runDefinition(request.getId(), hashTree, request.getReportId(), ApiRunMode.SCENARIO.name());
|
||||
createAPIReportResult(request.getId());
|
||||
createAPIReportResult(request.getId(), ReportTriggerMode.MANUAL.name());
|
||||
return request.getId();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
package io.metersphere.commons.constants;
|
||||
|
||||
public enum ScheduleGroup {
|
||||
API_TEST, PERFORMANCE_TEST
|
||||
API_TEST, PERFORMANCE_TEST,SCENARIO_TEST
|
||||
}
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
package io.metersphere.job.sechedule;
|
||||
|
||||
import io.metersphere.api.dto.automation.RunScenarioRequest;
|
||||
import io.metersphere.api.service.ApiAutomationService;
|
||||
import io.metersphere.commons.constants.ReportTriggerMode;
|
||||
import io.metersphere.commons.constants.ScheduleGroup;
|
||||
import io.metersphere.commons.utils.CommonBeanFactory;
|
||||
import org.quartz.JobExecutionContext;
|
||||
import org.quartz.JobKey;
|
||||
import org.quartz.TriggerKey;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
public class ScenarioJob extends MsScheduleJob {
|
||||
|
||||
ApiAutomationService apiAutomationService;
|
||||
|
||||
public ScenarioJob() {
|
||||
apiAutomationService = (ApiAutomationService) CommonBeanFactory.getBean(ApiAutomationService.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
void businessExecute(JobExecutionContext context) {
|
||||
RunScenarioRequest request = new RunScenarioRequest();
|
||||
request.setId(resourceId);
|
||||
List<String> ids = new ArrayList<>();
|
||||
ids.add(resourceId);
|
||||
request.setScenarioIds(ids);
|
||||
request.setTriggerMode(ReportTriggerMode.SCHEDULE.name());
|
||||
apiAutomationService.run(request);
|
||||
}
|
||||
|
||||
public static JobKey getJobKey(String testId) {
|
||||
return new JobKey(testId, ScheduleGroup.SCENARIO_TEST.name());
|
||||
}
|
||||
|
||||
public static TriggerKey getTriggerKey(String testId) {
|
||||
return new TriggerKey(testId, ScheduleGroup.SCENARIO_TEST.name());
|
||||
}
|
||||
}
|
|
@ -64,8 +64,8 @@
|
|||
|
||||
<!--要生成的数据库表 -->
|
||||
|
||||
<table tableName="api_scenario"/>
|
||||
<table tableName="api_scenario_report"/>
|
||||
<table tableName="api_scenario_report_detail"/>
|
||||
|
||||
</context>
|
||||
</generatorConfiguration>
|
|
@ -45,13 +45,14 @@
|
|||
</el-table-column>
|
||||
<el-table-column prop="passRate" :label="$t('api_test.automation.passing_rate')"
|
||||
show-overflow-tooltip/>
|
||||
<el-table-column :label="$t('commons.operating')" width="180">
|
||||
<el-table-column :label="$t('commons.operating')" width="160">
|
||||
<template v-slot:default="{row}">
|
||||
<el-button type="text" @click="edit(row)">{{ $t('api_test.automation.edit') }}</el-button>
|
||||
<el-button type="text" @click="execute(row)">{{ $t('api_test.automation.execute') }}</el-button>
|
||||
<el-button type="text" @click="copy(row)">{{ $t('api_test.automation.copy') }}</el-button>
|
||||
<el-button type="text" @click="remove(row)">{{ $t('api_test.automation.remove') }}</el-button>
|
||||
<ms-table-more-btn :is-show="true" :row="row" :buttons="tableButtons"/>
|
||||
</template>
|
||||
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<ms-table-pagination :change="search" :current-page.sync="currentPage" :page-size.sync="pageSize"
|
||||
|
@ -62,6 +63,13 @@
|
|||
<el-drawer :visible.sync="runVisible" :destroy-on-close="true" direction="ltr" :withHeader="false" :title="$t('test_track.plan_view.test_result')" :modal="false" size="90%">
|
||||
<ms-api-report-detail @refresh="search" :infoDb="infoDb" :report-id="reportId" :currentProjectId="currentProject!=undefined ? currentProject.id:''"/>
|
||||
</el-drawer>
|
||||
|
||||
<!--定时任务-->
|
||||
<!--<ms-schedule-config :schedule="currentScenario.schedule" :is-read-only="false" :save="saveCronExpression"-->
|
||||
<!--@scheduleChange="saveSchedule" :test-id="currentScenario.id" :check-open="checkScheduleEdit"/>-->
|
||||
<ms-schedule-edit :is-read-only="false" :schedule="schedule" :test-id="currentScenario.id" :save="saveCronExpression"
|
||||
ref="scheduleEdit"/>
|
||||
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
|
@ -75,10 +83,13 @@
|
|||
import MsTag from "../../../common/components/MsTag";
|
||||
import {getUUID} from "@/common/js/utils";
|
||||
import MsApiReportDetail from "../report/ApiReportDetail";
|
||||
import MsTableMoreBtn from "./TableMoreBtn";
|
||||
import MsScheduleConfig from "../../../common/components/MsScheduleConfig";
|
||||
import MsScheduleEdit from "../../../common/components/MsScheduleEdit";
|
||||
|
||||
export default {
|
||||
name: "MsApiScenarioList",
|
||||
components: {ShowMoreBtn, MsTablePagination, MsTableHeader, MsTag, MsApiReportDetail},
|
||||
components: {ShowMoreBtn, MsTablePagination, MsTableHeader, MsTag, MsApiReportDetail, MsTableMoreBtn, MsScheduleConfig, MsScheduleEdit},
|
||||
props: {
|
||||
currentProject: Object,
|
||||
currentModule: Object,
|
||||
|
@ -87,6 +98,8 @@
|
|||
return {
|
||||
result: {},
|
||||
condition: {},
|
||||
currentScenario: {},
|
||||
schedule: {},
|
||||
selectAll: false,
|
||||
selection: [],
|
||||
tableData: [],
|
||||
|
@ -104,6 +117,15 @@
|
|||
name: this.$t('api_test.automation.batch_execute'), handleClick: this.handleBatchExecute
|
||||
}
|
||||
],
|
||||
tableButtons: [
|
||||
{
|
||||
name: this.$t('api_test.automation.remove'), handleClick: this.remove
|
||||
}, {
|
||||
name: '查看引用', handleClick: this.handleQuote
|
||||
}, {
|
||||
name: this.$t('commons.trigger_mode.schedule'), handleClick: this.handleSchedule
|
||||
}
|
||||
],
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
|
@ -202,6 +224,42 @@
|
|||
this.infoDb = true;
|
||||
this.reportId = row.reportId;
|
||||
},
|
||||
handleQuote() {
|
||||
|
||||
},
|
||||
handleSchedule(row) {
|
||||
this.currentScenario = row;
|
||||
if (row.schedule) {
|
||||
if (Object.prototype.toString.call(row.schedule).match(/\[object (\w+)\]/)[1].toLowerCase() === 'object') {
|
||||
this.schedule = row.schedule;
|
||||
} else {
|
||||
this.schedule = JSON.parse(row.schedule);
|
||||
}
|
||||
}
|
||||
this.$refs.scheduleEdit.open();
|
||||
},
|
||||
saveCronExpression(cronExpression) {
|
||||
this.schedule.enable = true;
|
||||
this.schedule.value = cronExpression;
|
||||
this.saveSchedule();
|
||||
},
|
||||
saveSchedule() {
|
||||
this.checkScheduleEdit();
|
||||
let param = {};
|
||||
param = this.schedule;
|
||||
param.resourceId = this.currentScenario.id;
|
||||
let url = '/api/automation/schedule/create';
|
||||
if (param.id) {
|
||||
url = '/api/automation/schedule/update';
|
||||
}
|
||||
this.$post(url, param, () => {
|
||||
this.$success(this.$t('commons.save_success'));
|
||||
this.search();
|
||||
});
|
||||
},
|
||||
checkScheduleEdit() {
|
||||
return true;
|
||||
},
|
||||
remove(row) {
|
||||
if (this.currentModule !== undefined && this.currentModule != null && this.currentModule.id === "gc") {
|
||||
this.$get('/api/automation/delete/' + row.id, () => {
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
<template>
|
||||
<div v-if="isShow" style="float: right">
|
||||
<el-dropdown placement="bottom" trigger="click" size="medium">
|
||||
<div @click.stop class="show-more-btn">
|
||||
<i class="el-icon-more ms-icon-more"/>
|
||||
</div>
|
||||
<el-dropdown-menu slot="dropdown" class="dropdown-menu-class">
|
||||
<el-dropdown-item v-for="(btn,index) in buttons" :key="index" @click.native.stop="click(btn)">
|
||||
{{btn.name}}
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "ShowMoreBtn",
|
||||
props: {
|
||||
isShow: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
buttons: Array,
|
||||
row: Object,
|
||||
size: Number
|
||||
},
|
||||
methods: {
|
||||
click(btn) {
|
||||
if (btn.handleClick instanceof Function) {
|
||||
btn.handleClick(this.row);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.ms-icon-more {
|
||||
margin-top: 15px;
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
.show-more-btn {
|
||||
width: 20px;
|
||||
height: 25px;
|
||||
line-height: 25px;
|
||||
}
|
||||
|
||||
.show-more-btn-title {
|
||||
color: #696969;
|
||||
background-color: #e2e2e2;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.dropdown-menu-class {
|
||||
padding: 1px 0;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
Loading…
Reference in New Issue