Merge branch 'master' of https://github.com/metersphere/metersphere
This commit is contained in:
commit
cdf1b73253
|
@ -10,6 +10,7 @@ import io.metersphere.api.dto.definition.*;
|
||||||
import io.metersphere.api.dto.definition.parse.ApiDefinitionImport;
|
import io.metersphere.api.dto.definition.parse.ApiDefinitionImport;
|
||||||
import io.metersphere.api.service.ApiDefinitionService;
|
import io.metersphere.api.service.ApiDefinitionService;
|
||||||
import io.metersphere.base.domain.ApiDefinition;
|
import io.metersphere.base.domain.ApiDefinition;
|
||||||
|
import io.metersphere.base.domain.Schedule;
|
||||||
import io.metersphere.commons.constants.RoleConstants;
|
import io.metersphere.commons.constants.RoleConstants;
|
||||||
import io.metersphere.commons.json.JSONSchemaGenerator;
|
import io.metersphere.commons.json.JSONSchemaGenerator;
|
||||||
import io.metersphere.commons.utils.PageUtils;
|
import io.metersphere.commons.utils.PageUtils;
|
||||||
|
@ -139,6 +140,11 @@ public class ApiDefinitionController {
|
||||||
return apiDefinitionService.apiTestImport(file, request);
|
return apiDefinitionService.apiTestImport(file, request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PostMapping(value = "/schedule/create")
|
||||||
|
public void createSchedule(@RequestBody Schedule request) {
|
||||||
|
apiDefinitionService.createSchedule(request);
|
||||||
|
}
|
||||||
|
|
||||||
@PostMapping("/getReference")
|
@PostMapping("/getReference")
|
||||||
public ReferenceDTO getReference(@RequestBody ApiScenarioRequest request) {
|
public ReferenceDTO getReference(@RequestBody ApiScenarioRequest request) {
|
||||||
return apiDefinitionService.getReference(request);
|
return apiDefinitionService.getReference(request);
|
||||||
|
@ -165,5 +171,4 @@ public class ApiDefinitionController {
|
||||||
public String preview(@RequestBody String jsonSchema) {
|
public String preview(@RequestBody String jsonSchema) {
|
||||||
return JSONSchemaGenerator.getJson(jsonSchema);
|
return JSONSchemaGenerator.getJson(jsonSchema);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,10 +24,14 @@ import io.metersphere.base.mapper.ext.ExtApiScenarioMapper;
|
||||||
import io.metersphere.base.mapper.ext.ExtTestPlanMapper;
|
import io.metersphere.base.mapper.ext.ExtTestPlanMapper;
|
||||||
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.constants.ScheduleGroup;
|
||||||
|
import io.metersphere.commons.constants.ScheduleType;
|
||||||
import io.metersphere.commons.exception.MSException;
|
import io.metersphere.commons.exception.MSException;
|
||||||
import io.metersphere.commons.utils.*;
|
import io.metersphere.commons.utils.*;
|
||||||
import io.metersphere.i18n.Translator;
|
import io.metersphere.i18n.Translator;
|
||||||
|
import io.metersphere.job.sechedule.SwaggerUrlImportJob;
|
||||||
import io.metersphere.service.FileService;
|
import io.metersphere.service.FileService;
|
||||||
|
import io.metersphere.service.ScheduleService;
|
||||||
import io.metersphere.track.request.testcase.ApiCaseRelevanceRequest;
|
import io.metersphere.track.request.testcase.ApiCaseRelevanceRequest;
|
||||||
import io.metersphere.track.request.testcase.QueryTestPlanRequest;
|
import io.metersphere.track.request.testcase.QueryTestPlanRequest;
|
||||||
import org.apache.commons.collections.CollectionUtils;
|
import org.apache.commons.collections.CollectionUtils;
|
||||||
|
@ -73,6 +77,8 @@ public class ApiDefinitionService {
|
||||||
private ExtTestPlanMapper extTestPlanMapper;
|
private ExtTestPlanMapper extTestPlanMapper;
|
||||||
@Resource
|
@Resource
|
||||||
private ProjectMapper projectMapper;
|
private ProjectMapper projectMapper;
|
||||||
|
@Resource
|
||||||
|
private ScheduleService scheduleService;
|
||||||
|
|
||||||
private static Cache cache = Cache.newHardMemoryCache(0, 3600 * 24);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -1,8 +1,9 @@
|
||||||
package io.metersphere.base.domain;
|
package io.metersphere.base.domain;
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
public class Schedule implements Serializable {
|
public class Schedule implements Serializable {
|
||||||
private String id;
|
private String id;
|
||||||
|
@ -35,4 +36,6 @@ public class Schedule implements Serializable {
|
||||||
|
|
||||||
//定时任务来源: 测试计划/测试场景
|
//定时任务来源: 测试计划/测试场景
|
||||||
private String scheduleFrom;
|
private String scheduleFrom;
|
||||||
|
|
||||||
|
private String swaggerUrl;
|
||||||
}
|
}
|
|
@ -33,4 +33,6 @@ public interface ApiDefinitionExecResultMapper {
|
||||||
int updateByPrimaryKeyWithBLOBs(ApiDefinitionExecResult record);
|
int updateByPrimaryKeyWithBLOBs(ApiDefinitionExecResult record);
|
||||||
|
|
||||||
int updateByPrimaryKey(ApiDefinitionExecResult record);
|
int updateByPrimaryKey(ApiDefinitionExecResult record);
|
||||||
|
|
||||||
|
String selectExecResult(String resourceId);
|
||||||
}
|
}
|
|
@ -210,6 +210,9 @@
|
||||||
<include refid="Example_Where_Clause" />
|
<include refid="Example_Where_Clause" />
|
||||||
</if>
|
</if>
|
||||||
</select>
|
</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 id="updateByExampleSelective" parameterType="map">
|
||||||
update api_definition_exec_result
|
update api_definition_exec_result
|
||||||
<set>
|
<set>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
package io.metersphere.commons.constants;
|
package io.metersphere.commons.constants;
|
||||||
|
|
||||||
public enum ScheduleGroup {
|
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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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());
|
||||||
|
}
|
||||||
|
}
|
|
@ -104,6 +104,7 @@ public class ScheduleService {
|
||||||
|
|
||||||
public void startEnableSchedules() {
|
public void startEnableSchedules() {
|
||||||
List<Schedule> Schedules = getEnableSchedule();
|
List<Schedule> Schedules = getEnableSchedule();
|
||||||
|
|
||||||
Schedules.forEach(schedule -> {
|
Schedules.forEach(schedule -> {
|
||||||
try {
|
try {
|
||||||
if (schedule.getEnable()) {
|
if (schedule.getEnable()) {
|
||||||
|
@ -122,7 +123,7 @@ public class ScheduleService {
|
||||||
public Schedule buildApiTestSchedule(Schedule request) {
|
public Schedule buildApiTestSchedule(Schedule request) {
|
||||||
Schedule schedule = new Schedule();
|
Schedule schedule = new Schedule();
|
||||||
schedule.setResourceId(request.getResourceId());
|
schedule.setResourceId(request.getResourceId());
|
||||||
schedule.setEnable(request.getEnable());
|
schedule.setEnable(true);
|
||||||
schedule.setValue(request.getValue().trim());
|
schedule.setValue(request.getValue().trim());
|
||||||
schedule.setKey(request.getResourceId());
|
schedule.setKey(request.getResourceId());
|
||||||
schedule.setUserId(SessionUtils.getUser().getId());
|
schedule.setUserId(SessionUtils.getUser().getId());
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit c2ed883e9be6fc7e01589f81916bf4ddc62148c0
|
Subproject commit 7f7808c6f0457dd2df6b19a1622558f3f8122646
|
|
@ -30,10 +30,13 @@
|
||||||
<el-table-column prop="level" :label="$t('api_test.automation.case_level')"
|
<el-table-column prop="level" :label="$t('api_test.automation.case_level')"
|
||||||
show-overflow-tooltip>
|
show-overflow-tooltip>
|
||||||
<template v-slot:default="scope">
|
<template v-slot:default="scope">
|
||||||
<ms-tag v-if="scope.row.level == 'P0'" type="info" effect="plain" content="P0"/>
|
<priority-table-item :value="scope.row.level"/>
|
||||||
<ms-tag v-if="scope.row.level == 'P1'" type="warning" effect="plain" content="P1"/>
|
</template>
|
||||||
<ms-tag v-if="scope.row.level == 'P2'" type="success" effect="plain" content="P2"/>
|
</el-table-column>
|
||||||
<ms-tag v-if="scope.row.level == 'P3'" type="danger" effect="plain" content="P3"/>
|
<el-table-column prop="status" :label="$t('test_track.plan.plan_status')"
|
||||||
|
show-overflow-tooltip>
|
||||||
|
<template v-slot:default="scope">
|
||||||
|
<plan-status-table-item :value="scope.row.status"/>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="tags" :label="$t('api_test.automation.tag')" width="200px">
|
<el-table-column prop="tags" :label="$t('api_test.automation.tag')" width="200px">
|
||||||
|
@ -115,10 +118,14 @@
|
||||||
import MsTableSelectAll from "../../../common/components/table/MsTableSelectAll";
|
import MsTableSelectAll from "../../../common/components/table/MsTableSelectAll";
|
||||||
import {API_CASE_CONFIGS} from "@/business/components/common/components/search/search-components";
|
import {API_CASE_CONFIGS} from "@/business/components/common/components/search/search-components";
|
||||||
import MsTableOperatorButton from "@/business/components/common/components/MsTableOperatorButton";
|
import MsTableOperatorButton from "@/business/components/common/components/MsTableOperatorButton";
|
||||||
|
import PriorityTableItem from "../../../track/common/tableItems/planview/PriorityTableItem";
|
||||||
|
import PlanStatusTableItem from "../../../track/common/tableItems/plan/PlanStatusTableItem";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "MsApiScenarioList",
|
name: "MsApiScenarioList",
|
||||||
components: {
|
components: {
|
||||||
|
PlanStatusTableItem,
|
||||||
|
PriorityTableItem,
|
||||||
MsTableSelectAll,
|
MsTableSelectAll,
|
||||||
MsTablePagination,
|
MsTablePagination,
|
||||||
MsTableMoreBtn,
|
MsTableMoreBtn,
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
{{description}}
|
{{description}}
|
||||||
</span>
|
</span>
|
||||||
<div class="kv-row" v-for="(item, index) in items" :key="index">
|
<div class="kv-row" v-for="(item, index) in items" :key="index">
|
||||||
<el-row type="flex" :gutter="20" justify="space-between" align="middle">
|
<el-row type="flex" :gutter="5" justify="space-between" align="middle">
|
||||||
<el-col class="kv-checkbox">
|
<el-col class="kv-checkbox">
|
||||||
<input type="checkbox" v-if="!isDisable(index)" @change="change" :value="item.uuid" v-model="item.enable"
|
<input type="checkbox" v-if="!isDisable(index)" @change="change" :value="item.uuid" v-model="item.enable"
|
||||||
:disabled="isDisable(index) || isReadOnly"/>
|
:disabled="isDisable(index) || isReadOnly"/>
|
||||||
|
@ -18,6 +18,15 @@
|
||||||
<el-input :disabled="isReadOnly" v-model="item.value" size="small" @change="change"
|
<el-input :disabled="isReadOnly" v-model="item.value" size="small" @change="change"
|
||||||
:placeholder="$t('api_test.value')" show-word-limit/>
|
:placeholder="$t('api_test.value')" show-word-limit/>
|
||||||
</el-col>
|
</el-col>
|
||||||
|
<el-col>
|
||||||
|
<el-input v-model="item.description" size="small" maxlength="200"
|
||||||
|
:placeholder="$t('commons.description')" show-word-limit>
|
||||||
|
</el-input>
|
||||||
|
</el-col>
|
||||||
|
<el-col class="kv-copy">
|
||||||
|
<el-button size="mini" class="el-icon-document-copy" circle @click="copy(item, index)"
|
||||||
|
:disabled="isDisable(index) || isReadOnly"/>
|
||||||
|
</el-col>
|
||||||
<el-col class="kv-delete">
|
<el-col class="kv-delete">
|
||||||
<el-button size="mini" class="el-icon-delete-solid" circle @click="remove(index)"
|
<el-button size="mini" class="el-icon-delete-solid" circle @click="remove(index)"
|
||||||
:disabled="isDisable(index) || isReadOnly"/>
|
:disabled="isDisable(index) || isReadOnly"/>
|
||||||
|
@ -59,6 +68,11 @@
|
||||||
this.items.splice(index, 1);
|
this.items.splice(index, 1);
|
||||||
this.$emit('change', this.items);
|
this.$emit('change', this.items);
|
||||||
},
|
},
|
||||||
|
copy: function (item, index) {
|
||||||
|
let copy = {};
|
||||||
|
Object.assign(copy, item);
|
||||||
|
this.items.splice(index + 1, 0, copy);
|
||||||
|
},
|
||||||
change: function () {
|
change: function () {
|
||||||
let isNeedCreate = true;
|
let isNeedCreate = true;
|
||||||
let removeIndex = -1;
|
let removeIndex = -1;
|
||||||
|
@ -105,7 +119,7 @@
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.kv-delete {
|
.kv-delete,.kv-copy {
|
||||||
width: 60px;
|
width: 60px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -3,17 +3,17 @@
|
||||||
:visible.sync="visible" class="api-import" v-loading="result.loading" @close="close">
|
: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>
|
||||||
<el-radio-group v-model="selectedPlatformValue">
|
<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>
|
</el-radio-group>
|
||||||
|
|
||||||
<div class="operate-button">
|
<div class="operate-button">
|
||||||
<el-button class="save-button" type="primary" plain @click="save">
|
<el-button class="save-button" type="primary" plain @click="save">
|
||||||
{{$t('commons.save')}}
|
{{ $t('commons.save') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button class="cancel-button" type="warning" plain @click="visible = false">
|
<el-button class="cancel-button" type="warning" plain @click="visible = false">
|
||||||
{{$t('commons.cancel')}}
|
{{ $t('commons.cancel') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -34,7 +34,7 @@
|
||||||
multiple>
|
multiple>
|
||||||
<i class="el-icon-upload"></i>
|
<i class="el-icon-upload"></i>
|
||||||
<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-form-item :label="'Swagger URL'" prop="wgerUrl" v-if="isSwagger2 && swaggerUrlEable" class="swagger-url">
|
<el-form-item :label="'Swagger URL'" prop="wgerUrl" v-if="isSwagger2 && swaggerUrlEable" class="swagger-url">
|
||||||
|
@ -48,14 +48,23 @@
|
||||||
</el-switch>
|
</el-switch>
|
||||||
</el-form-item>
|
</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>
|
</el-form>
|
||||||
|
|
||||||
<div class="format-tip">
|
<div class="format-tip">
|
||||||
<div>
|
<div>
|
||||||
<span>{{$t('api_test.api_import.tip')}}:{{selectedPlatform.tip}}</span>
|
<span>{{ $t('api_test.api_import.tip') }}:{{ selectedPlatform.tip }}</span>
|
||||||
</div>
|
</div>
|
||||||
<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>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -63,217 +72,223 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import MsDialogFooter from "../../../../common/components/MsDialogFooter";
|
import MsDialogFooter from "../../../../common/components/MsDialogFooter";
|
||||||
import {listenGoBack, removeGoBackListener} from "@/common/js/utils";
|
import {listenGoBack, removeGoBackListener} from "@/common/js/utils";
|
||||||
import {getCurrentProjectID} from "../../../../../../common/js/utils";
|
import {getCurrentProjectID} from "../../../../../../common/js/utils";
|
||||||
|
import ScheduleImport from "@/business/components/api/definition/components/import/ImportScheduleEdit";
|
||||||
export default {
|
export default {
|
||||||
name: "ApiImport",
|
name: "ApiImport",
|
||||||
components: {MsDialogFooter},
|
components: {ScheduleImport, MsDialogFooter},
|
||||||
props: {
|
props: {
|
||||||
saved: {
|
saved: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: true,
|
default: true,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
visible: false,
|
visible: false,
|
||||||
swaggerUrlEable: false,
|
swaggerUrlEable: false,
|
||||||
showEnvironmentSelect: true,
|
swaggerSynchronization:false,
|
||||||
platforms: [
|
showEnvironmentSelect: true,
|
||||||
{
|
platforms: [
|
||||||
name: 'Metersphere',
|
{
|
||||||
value: 'Metersphere',
|
name: 'Metersphere',
|
||||||
tip: this.$t('api_test.api_import.ms_tip'),
|
value: 'Metersphere',
|
||||||
exportTip: this.$t('api_test.api_import.ms_export_tip'),
|
tip: this.$t('api_test.api_import.ms_tip'),
|
||||||
suffixes: new Set(['json'])
|
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: ''
|
|
||||||
},
|
},
|
||||||
rules: {},
|
{
|
||||||
currentModule: {},
|
name: 'Postman',
|
||||||
fileList: []
|
value: 'Postman',
|
||||||
}
|
tip: this.$t('api_test.api_import.postman_tip'),
|
||||||
},
|
exportTip: this.$t('api_test.api_import.post_export_tip'),
|
||||||
activated() {
|
suffixes: new Set(['json'])
|
||||||
this.selectedPlatform = this.platforms[0];
|
},
|
||||||
},
|
{
|
||||||
watch: {
|
name: 'Swagger',
|
||||||
selectedPlatformValue() {
|
value: 'Swagger2',
|
||||||
for (let i in this.platforms) {
|
tip: this.$t('api_test.api_import.swagger_tip'),
|
||||||
if (this.platforms[i].value === this.selectedPlatformValue) {
|
exportTip: this.$t('api_test.api_import.swagger_export_tip'),
|
||||||
this.selectedPlatform = this.platforms[i];
|
suffixes: new Set(['json'])
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
],
|
||||||
|
selectedPlatform: {},
|
||||||
|
selectedPlatformValue: 'Metersphere',
|
||||||
|
result: {},
|
||||||
|
projects: [],
|
||||||
|
environments: [],
|
||||||
|
useEnvironment: false,
|
||||||
|
formData: {
|
||||||
|
file: undefined,
|
||||||
|
swaggerUrl: ''
|
||||||
},
|
},
|
||||||
},
|
rules: {},
|
||||||
computed: {
|
currentModule: {},
|
||||||
isSwagger2() {
|
fileList: []
|
||||||
return this.selectedPlatformValue === 'Swagger2';
|
}
|
||||||
|
},
|
||||||
|
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) {
|
computed: {
|
||||||
this.currentModule = module;
|
isSwagger2() {
|
||||||
this.visible = true;
|
return this.selectedPlatformValue === 'Swagger2';
|
||||||
listenGoBack(this.close);
|
}
|
||||||
},
|
},
|
||||||
upload(file) {
|
methods: {
|
||||||
this.formData.file = file.file;
|
scheduleEdit(){
|
||||||
},
|
if(this.swaggerSynchronization){
|
||||||
handleExceed(files, fileList) {
|
this.$refs.scheduleEdit.open(this.buildParam());
|
||||||
this.$warning(this.$t('test_track.case.import.upload_limit_count'));
|
}
|
||||||
},
|
},
|
||||||
handleRemove(file, fileList) {
|
open(module) {
|
||||||
this.formData.file = undefined;
|
this.currentModule = module;
|
||||||
},
|
this.visible = true;
|
||||||
uploadValidate(file, fileList) {
|
listenGoBack(this.close);
|
||||||
let suffix = file.name.substring(file.name.lastIndexOf('.') + 1);
|
},
|
||||||
if (this.selectedPlatform.suffixes && !this.selectedPlatform.suffixes.has(suffix)) {
|
upload(file) {
|
||||||
this.$warning(this.$t('api_test.api_import.suffixFormatErr'));
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
if (file.size / 1024 / 1024 > 20) {
|
});
|
||||||
this.$warning(this.$t('test_track.case.import.upload_limit_size'));
|
},
|
||||||
return false;
|
buildParam() {
|
||||||
}
|
let param = {};
|
||||||
return true;
|
Object.assign(param, this.formData);
|
||||||
},
|
param.platform = this.selectedPlatformValue;
|
||||||
save() {
|
param.saved = this.saved;
|
||||||
this.$refs.form.validate(valid => {
|
if (this.currentModule) {
|
||||||
if (valid) {
|
param.moduleId = this.currentModule.id;
|
||||||
if ((this.selectedPlatformValue != 'Swagger2' || (this.selectedPlatformValue == 'Swagger2' && !this.swaggerUrlEable)) && !this.formData.file) {
|
param.modulePath = this.currentModule.path;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
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>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|
||||||
.api-import >>> .el-dialog {
|
.api-import >>> .el-dialog {
|
||||||
min-width: 700px;
|
min-width: 700px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.format-tip {
|
.format-tip {
|
||||||
background: #EDEDED;
|
background: #EDEDED;
|
||||||
}
|
}
|
||||||
|
|
||||||
.api-upload {
|
.api-upload {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
margin: auto 0;
|
margin: auto 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.api-upload >>> .el-upload {
|
.api-upload >>> .el-upload {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 350px;
|
max-width: 350px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.api-upload >>> .el-upload-dragger {
|
.api-upload >>> .el-upload-dragger {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-radio-group {
|
.el-radio-group {
|
||||||
margin: 10px 0;
|
margin: 10px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.header-bar, .format-tip, .el-form {
|
.header-bar, .format-tip, .el-form {
|
||||||
border: solid #E1E1E1 1px;
|
border: solid #E1E1E1 1px;
|
||||||
margin: 10px 0;
|
margin: 10px 0;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.header-bar {
|
.header-bar {
|
||||||
padding: 10px 30px;
|
padding: 10px 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.api-import >>> .el-dialog__body {
|
.api-import >>> .el-dialog__body {
|
||||||
padding: 15px 25px;
|
padding: 15px 25px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.operate-button {
|
.operate-button {
|
||||||
float: right;
|
float: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
.save-button {
|
.save-button {
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-form {
|
.el-form {
|
||||||
padding: 30px 10px;
|
padding: 30px 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dialog-footer {
|
.dialog-footer {
|
||||||
float: right;
|
float: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
.swagger-url-disable {
|
.swagger-url-disable {
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
|
|
||||||
margin-left: 80px;
|
margin-left: 80px;
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -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>
|
|
@ -829,6 +829,9 @@ export default {
|
||||||
swagger_export_tip: "Export jSON-formatted files via Swagger website",
|
swagger_export_tip: "Export jSON-formatted files via Swagger website",
|
||||||
suffixFormatErr: "The file format does not meet the requirements",
|
suffixFormatErr: "The file format does not meet the requirements",
|
||||||
swagger_url_import: "Import using URL",
|
swagger_url_import: "Import using URL",
|
||||||
|
timing_synchronization:"Timing synchronization",
|
||||||
|
next_synchronization_time:"Next synchronization time",
|
||||||
|
|
||||||
},
|
},
|
||||||
home_page: {
|
home_page: {
|
||||||
unit_of_measurement: "",
|
unit_of_measurement: "",
|
||||||
|
|
|
@ -831,6 +831,10 @@ export default {
|
||||||
swagger_export_tip: "通过 Swagger 页面导出",
|
swagger_export_tip: "通过 Swagger 页面导出",
|
||||||
suffixFormatErr: "文件格式不符合要求",
|
suffixFormatErr: "文件格式不符合要求",
|
||||||
swagger_url_import: "使用URL导入",
|
swagger_url_import: "使用URL导入",
|
||||||
|
timing_synchronization:"定时同步",
|
||||||
|
next_synchronization_time:"下次同步时间"
|
||||||
|
|
||||||
|
|
||||||
},
|
},
|
||||||
home_page: {
|
home_page: {
|
||||||
unit_of_measurement: "个",
|
unit_of_measurement: "个",
|
||||||
|
|
|
@ -830,6 +830,9 @@ export default {
|
||||||
swagger_export_tip: "通過 Swagger 頁面導出",
|
swagger_export_tip: "通過 Swagger 頁面導出",
|
||||||
suffixFormatErr: "文件格式不符合要求",
|
suffixFormatErr: "文件格式不符合要求",
|
||||||
swagger_url_import: "使用URL導入",
|
swagger_url_import: "使用URL導入",
|
||||||
|
timing_synchronization:"定時同步",
|
||||||
|
next_synchronization_time:"下次同步時間",
|
||||||
|
|
||||||
},
|
},
|
||||||
home_page: {
|
home_page: {
|
||||||
unit_of_measurement: "個",
|
unit_of_measurement: "個",
|
||||||
|
|
Loading…
Reference in New Issue