Merge branch 'master' of https://github.com/metersphere/metersphere
This commit is contained in:
commit
87a074310f
|
@ -0,0 +1,10 @@
|
|||
package io.metersphere.api.dto;
|
||||
|
||||
import io.metersphere.api.dto.scenario.DatabaseConfig;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class EnvironmentDTO {
|
||||
private String environmentId;
|
||||
private DatabaseConfig databaseConfig;
|
||||
}
|
|
@ -11,6 +11,7 @@ import io.metersphere.api.dto.definition.request.assertions.MsAssertions;
|
|||
import io.metersphere.api.dto.definition.request.auth.MsAuthManager;
|
||||
import io.metersphere.api.dto.definition.request.configurations.MsHeaderManager;
|
||||
import io.metersphere.api.dto.definition.request.controller.MsIfController;
|
||||
import io.metersphere.api.dto.definition.request.controller.MsLoopController;
|
||||
import io.metersphere.api.dto.definition.request.extract.MsExtract;
|
||||
import io.metersphere.api.dto.definition.request.processors.MsJSR223Processor;
|
||||
import io.metersphere.api.dto.definition.request.processors.post.MsJSR223PostProcessor;
|
||||
|
@ -56,11 +57,12 @@ import java.util.List;
|
|||
@JsonSubTypes.Type(value = MsConstantTimer.class, name = "ConstantTimer"),
|
||||
@JsonSubTypes.Type(value = MsIfController.class, name = "IfController"),
|
||||
@JsonSubTypes.Type(value = MsScenario.class, name = "scenario"),
|
||||
@JsonSubTypes.Type(value = MsLoopController.class, name = "LoopController"),
|
||||
|
||||
})
|
||||
@JSONType(seeAlso = {MsHTTPSamplerProxy.class, MsHeaderManager.class, MsJSR223Processor.class, MsJSR223PostProcessor.class,
|
||||
MsJSR223PreProcessor.class, MsTestPlan.class, MsThreadGroup.class, AuthManager.class, MsAssertions.class,
|
||||
MsExtract.class, MsTCPSampler.class, MsDubboSampler.class, MsJDBCSampler.class, MsConstantTimer.class, MsIfController.class, MsScenario.class}, typeKey = "type")
|
||||
MsExtract.class, MsTCPSampler.class, MsDubboSampler.class, MsJDBCSampler.class, MsConstantTimer.class, MsIfController.class, MsScenario.class, MsLoopController.class}, typeKey = "type")
|
||||
@Data
|
||||
public abstract class MsTestElement {
|
||||
private String type;
|
||||
|
|
|
@ -0,0 +1,126 @@
|
|||
package io.metersphere.api.dto.definition.request.controller;
|
||||
|
||||
import com.alibaba.fastjson.annotation.JSONField;
|
||||
import com.alibaba.fastjson.annotation.JSONType;
|
||||
import io.metersphere.api.dto.definition.request.MsTestElement;
|
||||
import io.metersphere.api.dto.definition.request.ParameterConfig;
|
||||
import io.metersphere.api.dto.definition.request.controller.loop.CountController;
|
||||
import io.metersphere.api.dto.definition.request.controller.loop.MsForEachController;
|
||||
import io.metersphere.api.dto.definition.request.controller.loop.MsWhileController;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.jmeter.control.ForeachController;
|
||||
import org.apache.jmeter.control.GenericController;
|
||||
import org.apache.jmeter.control.LoopController;
|
||||
import org.apache.jmeter.control.WhileController;
|
||||
import org.apache.jmeter.save.SaveService;
|
||||
import org.apache.jmeter.testelement.TestElement;
|
||||
import org.apache.jorphan.collections.HashTree;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@JSONType(typeName = "LoopController")
|
||||
public class MsLoopController extends MsTestElement {
|
||||
private String type = "LoopController";
|
||||
@JSONField(ordinal = 20)
|
||||
private String loopType;
|
||||
|
||||
@JSONField(ordinal = 21)
|
||||
private CountController countController;
|
||||
|
||||
@JSONField(ordinal = 22)
|
||||
private MsForEachController forEachController;
|
||||
|
||||
@JSONField(ordinal = 23)
|
||||
private MsWhileController whileController;
|
||||
|
||||
public void toHashTree(HashTree tree, List<MsTestElement> hashTree, ParameterConfig config) {
|
||||
if (!this.isEnable()) {
|
||||
return;
|
||||
}
|
||||
GenericController controller = controller();
|
||||
if (controller == null)
|
||||
return;
|
||||
|
||||
final HashTree groupTree = tree.add(controller);
|
||||
if (CollectionUtils.isNotEmpty(hashTree)) {
|
||||
hashTree.forEach(el -> {
|
||||
el.toHashTree(groupTree, el.getHashTree(), config);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private LoopController loopController() {
|
||||
LoopController loopController = new LoopController();
|
||||
loopController.setEnabled(true);
|
||||
loopController.setName(this.getLabel());
|
||||
loopController.setProperty(TestElement.TEST_CLASS, LoopController.class.getName());
|
||||
loopController.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("LoopControlPanel"));
|
||||
loopController.setContinueForever(countController.isProceed());
|
||||
loopController.setLoops(countController.getLoops());
|
||||
return loopController;
|
||||
}
|
||||
|
||||
public String getCondition() {
|
||||
String variable = "\"" + this.whileController.getVariable() + "\"";
|
||||
String operator = this.whileController.getOperator();
|
||||
String value = "\"" + this.whileController.getValue() + "\"";
|
||||
|
||||
if (StringUtils.contains(operator, "~")) {
|
||||
value = "\".*" + this.whileController.getValue() + ".*\"";
|
||||
}
|
||||
|
||||
if (StringUtils.equals(operator, "is empty")) {
|
||||
variable = "empty(" + variable + ")";
|
||||
operator = "";
|
||||
value = "";
|
||||
}
|
||||
|
||||
if (StringUtils.equals(operator, "is not empty")) {
|
||||
variable = "!empty(" + variable + ")";
|
||||
operator = "";
|
||||
value = "";
|
||||
}
|
||||
return "${__jexl3(" + variable + operator + value + ")}";
|
||||
}
|
||||
|
||||
private WhileController whileController() {
|
||||
WhileController controller = new WhileController();
|
||||
controller.setEnabled(true);
|
||||
controller.setName(this.getLabel());
|
||||
controller.setProperty(TestElement.TEST_CLASS, WhileController.class.getName());
|
||||
controller.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("WhileControllerGui"));
|
||||
controller.setCondition(getCondition());
|
||||
return controller;
|
||||
}
|
||||
|
||||
private ForeachController foreachController() {
|
||||
ForeachController controller = new ForeachController();
|
||||
controller.setEnabled(true);
|
||||
controller.setName(this.getLabel());
|
||||
controller.setProperty(TestElement.TEST_CLASS, ForeachController.class.getName());
|
||||
controller.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("ForeachControlPanel"));
|
||||
controller.setInputVal(this.forEachController.getInputVal());
|
||||
controller.setReturnVal(this.forEachController.getReturnVal());
|
||||
controller.setUseSeparator(true);
|
||||
return controller;
|
||||
}
|
||||
|
||||
private GenericController controller() {
|
||||
if (StringUtils.equals(this.loopType, "WHILE") && this.whileController != null) {
|
||||
return whileController();
|
||||
}
|
||||
if (StringUtils.equals(this.loopType, "FOREACH") && this.forEachController != null) {
|
||||
return foreachController();
|
||||
}
|
||||
if (StringUtils.equals(this.loopType, "LOOP_COUNT") && this.countController != null) {
|
||||
return loopController();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
package io.metersphere.api.dto.definition.request.controller.loop;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class CountController {
|
||||
private int loops;
|
||||
private int interval;
|
||||
private boolean proceed;
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
package io.metersphere.api.dto.definition.request.controller.loop;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class MsForEachController {
|
||||
private String inputVal;
|
||||
private String returnVal;
|
||||
private String interval;
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package io.metersphere.api.dto.definition.request.controller.loop;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class MsWhileController {
|
||||
private String variable;
|
||||
private String operator;
|
||||
private String value;
|
||||
private int timeout;
|
||||
}
|
|
@ -325,7 +325,7 @@ public class APIBackendListenerClient extends AbstractBackendListenerClient impl
|
|||
requestResult.addPassAssertions();
|
||||
}
|
||||
//xpath 提取错误会添加断言错误
|
||||
if (!responseAssertionResult.getMessage().contains("The required item type of the first operand of")) {
|
||||
if (StringUtils.isBlank(responseAssertionResult.getMessage()) || !responseAssertionResult.getMessage().contains("The required item type of the first operand of")) {
|
||||
responseResult.getAssertions().add(responseAssertionResult);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -333,6 +333,9 @@ public class Swagger3Parser extends SwaggerAbstractParser {
|
|||
}
|
||||
|
||||
private Object parseSchemaProperties(Schema schema, Set<String> refSet, Map<String, Schema> infoMap) {
|
||||
if (schema == null) {
|
||||
return null;
|
||||
}
|
||||
Map<String, Schema> properties = schema.getProperties();
|
||||
if (MapUtils.isEmpty(properties)) {
|
||||
return null;
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package io.metersphere.api.service;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import io.metersphere.api.dto.EnvironmentDTO;
|
||||
import io.metersphere.api.dto.SaveHistoricalDataUpgrade;
|
||||
import io.metersphere.api.dto.automation.ScenarioStatus;
|
||||
import io.metersphere.api.dto.definition.request.MsScenario;
|
||||
|
@ -17,6 +19,7 @@ import io.metersphere.api.dto.definition.request.sampler.MsTCPSampler;
|
|||
import io.metersphere.api.dto.definition.request.timer.MsConstantTimer;
|
||||
import io.metersphere.api.dto.scenario.Body;
|
||||
import io.metersphere.api.dto.scenario.Scenario;
|
||||
import io.metersphere.api.dto.scenario.environment.EnvironmentConfig;
|
||||
import io.metersphere.api.dto.scenario.request.*;
|
||||
import io.metersphere.base.domain.*;
|
||||
import io.metersphere.base.mapper.ApiScenarioMapper;
|
||||
|
@ -48,6 +51,9 @@ public class HistoricalDataUpgradeService {
|
|||
private ExtApiScenarioMapper extApiScenarioMapper;
|
||||
@Resource
|
||||
SqlSessionFactory sqlSessionFactory;
|
||||
@Resource
|
||||
ApiTestEnvironmentService apiTestEnvironmentService;
|
||||
private Map<String, EnvironmentDTO> environmentDTOMap;
|
||||
|
||||
private int getNextNum(String projectId) {
|
||||
ApiScenario apiScenario = extApiScenarioMapper.getNextNum(projectId);
|
||||
|
@ -110,6 +116,10 @@ public class HistoricalDataUpgradeService {
|
|||
BeanUtils.copyBean(element, request1);
|
||||
((MsHTTPSamplerProxy) element).setProtocol(RequestType.HTTP);
|
||||
((MsHTTPSamplerProxy) element).setArguments(request1.getParameters());
|
||||
if (StringUtils.isEmpty(element.getName())) {
|
||||
element.setName(request1.getPath());
|
||||
}
|
||||
|
||||
element.setType("HTTPSamplerProxy");
|
||||
}
|
||||
if (request instanceof DubboRequest) {
|
||||
|
@ -121,6 +131,11 @@ public class HistoricalDataUpgradeService {
|
|||
element = new MsJDBCSampler();
|
||||
SqlRequest request1 = (SqlRequest) request;
|
||||
BeanUtils.copyBean(element, request1);
|
||||
EnvironmentDTO dto = environmentDTOMap.get(request1.getDataSource());
|
||||
if (dto != null) {
|
||||
((MsJDBCSampler) element).setEnvironmentId(dto.getEnvironmentId());
|
||||
((MsJDBCSampler) element).setDataSource(dto.getDatabaseConfig());
|
||||
}
|
||||
element.setType("JDBCSampler");
|
||||
}
|
||||
if (request instanceof TCPRequest) {
|
||||
|
@ -250,11 +265,13 @@ public class HistoricalDataUpgradeService {
|
|||
if (!end.exists()) {
|
||||
end.mkdir();
|
||||
}
|
||||
for (String temp : filePath) {
|
||||
//添加满足情况的条件
|
||||
if (new File(sourcePathDir + File.separator + temp).isFile()) {
|
||||
//为文件则进行拷贝
|
||||
copyFile(sourcePathDir + File.separator + temp, newPathDir + File.separator + temp);
|
||||
if (filePath != null) {
|
||||
for (String temp : filePath) {
|
||||
//添加满足情况的条件
|
||||
if (new File(sourcePathDir + File.separator + temp).isFile()) {
|
||||
//为文件则进行拷贝
|
||||
copyFile(sourcePathDir + File.separator + temp, newPathDir + File.separator + temp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -310,6 +327,9 @@ public class HistoricalDataUpgradeService {
|
|||
}
|
||||
|
||||
public String upgrade(SaveHistoricalDataUpgrade saveHistoricalDataUpgrade) {
|
||||
// 初始化环境,获取数据源
|
||||
getDataSource(saveHistoricalDataUpgrade.getProjectId());
|
||||
|
||||
ApiTestExample example = new ApiTestExample();
|
||||
example.createCriteria().andIdIn(saveHistoricalDataUpgrade.getTestIds());
|
||||
List<ApiTest> blobs = apiTestMapper.selectByExampleWithBLOBs(example);
|
||||
|
@ -332,4 +352,23 @@ public class HistoricalDataUpgradeService {
|
|||
sqlSession.flushStatements();
|
||||
return null;
|
||||
}
|
||||
|
||||
private void getDataSource(String projectId) {
|
||||
List<ApiTestEnvironmentWithBLOBs> environments = apiTestEnvironmentService.list(projectId);
|
||||
environmentDTOMap = new HashMap<>();
|
||||
if (CollectionUtils.isNotEmpty(environments)) {
|
||||
environments.forEach(environment -> {
|
||||
EnvironmentConfig envConfig = JSONObject.parseObject(environment.getConfig(), EnvironmentConfig.class);
|
||||
if (CollectionUtils.isNotEmpty(envConfig.getDatabaseConfigs())) {
|
||||
envConfig.getDatabaseConfigs().forEach(item -> {
|
||||
EnvironmentDTO dto = new EnvironmentDTO();
|
||||
dto.setDatabaseConfig(item);
|
||||
dto.setEnvironmentId(environment.getId());
|
||||
environmentDTOMap.put(item.getId(), dto);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -152,6 +152,8 @@
|
|||
<ms-api-scenario-component v-if="data.type==='scenario'" :scenario="data" :node="node" @remove="remove" @copyRow="copyRow"/>
|
||||
<!--条件控制器-->
|
||||
<ms-if-controller :controller="data" :node="node" v-if="data.type==='IfController'" @remove="remove" @copyRow="copyRow"/>
|
||||
<!--循环控制器-->
|
||||
<ms-loop-controller :controller="data" :node="node" v-if="data.type==='LoopController'" @remove="remove" @copyRow="copyRow"/>
|
||||
<!--等待控制器-->
|
||||
<ms-constant-timer :timer="data" :node="node" v-if="data.type==='ConstantTimer'" @remove="remove" @copyRow="copyRow"/>
|
||||
<!--自定义脚本-->
|
||||
|
@ -168,7 +170,8 @@
|
|||
<!--提取规则-->
|
||||
<ms-api-extract @remove="remove" @copyRow="copyRow" v-if="data.type==='Extract'" customizeStyle="margin-top: 0px" :extract="data" :node="node"/>
|
||||
<!--API 导入 -->
|
||||
<ms-api-component :request="data" :currentScenario="currentScenario" :currentEnvironmentId="currentEnvironmentId" @remove="remove" @copyRow="copyRow" v-if="data.type==='HTTPSamplerProxy'||data.type==='DubboSampler'||data.type==='JDBCSampler'||data.type==='TCPSampler'" :node="node"/>
|
||||
<ms-api-component :request="data" :currentScenario="currentScenario" :currentEnvironmentId="currentEnvironmentId" @remove="remove" @copyRow="copyRow"
|
||||
v-if="data.type==='HTTPSamplerProxy'||data.type==='DubboSampler'||data.type==='JDBCSampler'||data.type==='TCPSampler'" :node="node"/>
|
||||
</template>
|
||||
</span>
|
||||
</el-tree>
|
||||
|
@ -227,7 +230,7 @@
|
|||
<script>
|
||||
import {API_STATUS, PRIORITY} from "../../definition/model/JsonData";
|
||||
import {WORKSPACE_ID} from '@/common/js/constants';
|
||||
import {Assertions, Extract, IfController, JSR223Processor, ConstantTimer} from "../../definition/model/ApiTestModel";
|
||||
import {Assertions, Extract, IfController, JSR223Processor, ConstantTimer, LoopController} from "../../definition/model/ApiTestModel";
|
||||
import MsJsr233Processor from "./Jsr233Processor";
|
||||
import {parseEnvironment} from "../../definition/model/EnvironmentModel";
|
||||
import MsConstantTimer from "./ConstantTimer";
|
||||
|
@ -241,7 +244,7 @@
|
|||
import ApiEnvironmentConfig from "../../definition/components/environment/ApiEnvironmentConfig";
|
||||
import MsInputTag from "./MsInputTag";
|
||||
import MsRun from "./DebugRun";
|
||||
import MsImportApiScenario from "./ImportApiScenario";
|
||||
import MsLoopController from "./LoopController";
|
||||
import MsApiScenarioComponent from "./ApiScenarioComponent";
|
||||
import MsApiReportDetail from "../report/ApiReportDetail";
|
||||
import MsScenarioParameters from "./ScenarioParameters";
|
||||
|
@ -275,6 +278,7 @@
|
|||
MsApiCustomize,
|
||||
ApiImport,
|
||||
InputTag,
|
||||
MsLoopController,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
@ -387,6 +391,16 @@
|
|||
this.addComponent('IfController')
|
||||
}
|
||||
},
|
||||
{
|
||||
title: this.$t('api_test.automation.loop_controller'),
|
||||
show: this.showButton("LoopController"),
|
||||
titleColor: "#02A7F0",
|
||||
titleBgColor: "#F4F4F5",
|
||||
icon: "next_plan",
|
||||
click: () => {
|
||||
this.addComponent('LoopController')
|
||||
}
|
||||
},
|
||||
{
|
||||
title: this.$t('api_test.automation.wait_controller'),
|
||||
show: this.showButton("ConstantTimer"),
|
||||
|
@ -494,8 +508,11 @@
|
|||
this.customizeRequest = {protocol: "HTTP", type: "API", hashTree: [], referenced: 'Created', active: false};
|
||||
this.customizeVisible = true;
|
||||
break;
|
||||
case ELEMENT_TYPE.LoopController:
|
||||
this.selectedTreeNode != undefined ? this.selectedTreeNode.hashTree.push(new LoopController()) :
|
||||
this.scenarioDefinition.push(new LoopController());
|
||||
break;
|
||||
case ELEMENT_TYPE.scenario:
|
||||
// this.scenarioVisible = true;
|
||||
this.$refs.scenarioRelevance.open();
|
||||
break;
|
||||
default:
|
||||
|
@ -613,8 +630,7 @@
|
|||
this.$post('/user/ws/member/tester/list', {workspaceId: workspaceId}, response => {
|
||||
this.maintainerOptions = response.data;
|
||||
});
|
||||
}
|
||||
,
|
||||
},
|
||||
openTagConfig() {
|
||||
if (!this.projectId) {
|
||||
this.$error(this.$t('api_test.select_project'));
|
||||
|
@ -623,7 +639,8 @@
|
|||
this.$refs.tag.open();
|
||||
},
|
||||
remove(row, node) {
|
||||
this.$alert(this.$t('api_test.definition.request.delete_confirm') + ' ' + row.name + " ?", '', {
|
||||
let name = row.name === undefined ? "" : row.name;
|
||||
this.$alert(this.$t('api_test.definition.request.delete_confirm_step') + ' ' + name + " ?", '', {
|
||||
confirmButtonText: this.$t('commons.confirm'),
|
||||
callback: (action) => {
|
||||
if (action === 'confirm') {
|
||||
|
@ -636,8 +653,7 @@
|
|||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
,
|
||||
},
|
||||
copyRow(row, node) {
|
||||
const parent = node.parent
|
||||
const hashTree = parent.data.hashTree || parent.data;
|
||||
|
@ -654,8 +670,7 @@
|
|||
this.$nextTick(() => {
|
||||
this.loading = false
|
||||
})
|
||||
}
|
||||
,
|
||||
},
|
||||
runDebug() {
|
||||
/*触发执行操作*/
|
||||
if (!this.currentEnvironmentId) {
|
||||
|
@ -668,8 +683,7 @@
|
|||
environmentId: this.currentEnvironmentId, hashTree: this.scenarioDefinition
|
||||
};
|
||||
this.reportId = getUUID().substring(0, 8);
|
||||
}
|
||||
,
|
||||
},
|
||||
getEnvironments() {
|
||||
if (this.projectId) {
|
||||
this.$get('/api/environment/list/' + this.projectId, response => {
|
||||
|
@ -689,16 +703,14 @@
|
|||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
,
|
||||
},
|
||||
openEnvironmentConfig() {
|
||||
if (!this.projectId) {
|
||||
this.$error(this.$t('api_test.select_project'));
|
||||
return;
|
||||
}
|
||||
this.$refs.environmentConfig.open(this.projectId);
|
||||
}
|
||||
,
|
||||
},
|
||||
environmentConfigClose() {
|
||||
this.getEnvironments();
|
||||
}
|
||||
|
@ -712,25 +724,21 @@
|
|||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
,
|
||||
},
|
||||
allowDrag(draggingNode, dropNode, dropType) {
|
||||
this.sort();
|
||||
this.reload();
|
||||
}
|
||||
,
|
||||
},
|
||||
nodeExpand(data) {
|
||||
if (data.resourceId) {
|
||||
this.expandedNode.push(data.resourceId);
|
||||
}
|
||||
}
|
||||
,
|
||||
},
|
||||
nodeCollapse(data) {
|
||||
if (data.resourceId) {
|
||||
this.expandedNode.splice(this.expandedNode.indexOf(data.resourceId), 1);
|
||||
}
|
||||
}
|
||||
,
|
||||
},
|
||||
getPath(id) {
|
||||
if (id === null) {
|
||||
return null;
|
||||
|
@ -739,8 +747,7 @@
|
|||
return item.id === id ? item.path : "";
|
||||
});
|
||||
return path[0].path;
|
||||
}
|
||||
,
|
||||
},
|
||||
setFiles(item, bodyUploadFiles, obj) {
|
||||
if (item.body) {
|
||||
if (item.body.kvs) {
|
||||
|
@ -778,8 +785,7 @@
|
|||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
,
|
||||
},
|
||||
recursiveFile(arr, bodyUploadFiles, obj) {
|
||||
arr.forEach(item => {
|
||||
this.setFiles(item, bodyUploadFiles, obj);
|
||||
|
@ -787,8 +793,7 @@
|
|||
this.recursiveFile(item.hashTree, bodyUploadFiles, obj);
|
||||
}
|
||||
});
|
||||
}
|
||||
,
|
||||
},
|
||||
getBodyUploadFiles(obj) {
|
||||
let bodyUploadFiles = [];
|
||||
obj.bodyUploadIds = [];
|
||||
|
@ -799,8 +804,7 @@
|
|||
}
|
||||
})
|
||||
return bodyUploadFiles;
|
||||
}
|
||||
,
|
||||
},
|
||||
editScenario() {
|
||||
this.$refs['currentScenario'].validate((valid) => {
|
||||
if (valid) {
|
||||
|
|
|
@ -0,0 +1,189 @@
|
|||
<template>
|
||||
<el-card v-loading="loading">
|
||||
<el-row>
|
||||
<div class="el-step__icon is-text ms-api-col">
|
||||
<div class="el-step__icon-inner">{{controller.index}}</div>
|
||||
</div>
|
||||
<el-button class="ms-title-buttion" size="small">{{$t('api_test.automation.loop_controller')}}</el-button>
|
||||
|
||||
<el-radio @change="changeRadio" class="ms-radio" v-model="controller.loopType" label="LOOP_COUNT">{{$t('loop.loops_title')}}</el-radio>
|
||||
<el-radio @change="changeRadio" class="ms-radio" v-model="controller.loopType" label="FOREACH">{{$t('loop.foreach')}}</el-radio>
|
||||
<el-radio @change="changeRadio" class="ms-radio" v-model="controller.loopType" label="WHILE">{{$t('loop.while')}}</el-radio>
|
||||
|
||||
<div style="margin-right: 20px; float: right">
|
||||
<i class="icon el-icon-arrow-right" :class="{'is-active': controller.active}"
|
||||
@click="active(controller)"/>
|
||||
<el-switch v-model="controller.enable" style="margin-left: 10px"/>
|
||||
<el-button size="mini" icon="el-icon-copy-document" circle @click="copyRow" style="margin-left: 10px"/>
|
||||
<el-button size="mini" icon="el-icon-delete" type="danger" circle @click="remove" style="margin-left: 10px"/>
|
||||
</div>
|
||||
</el-row>
|
||||
|
||||
<el-collapse-transition>
|
||||
<div v-if="controller.active" style="margin-top: 20px;">
|
||||
<div v-if="controller.loopType==='LOOP_COUNT'">
|
||||
<el-row>
|
||||
<el-col :span="8">
|
||||
<span class="ms-span ms-radio">{{$t('loop.loops')}}</span>
|
||||
<el-input-number size="small" v-model="controller.countController.loops" :placeholder="$t('commons.millisecond')" :max="1000*10000000" :min="0"/>
|
||||
<span class="ms-span ms-radio">次</span>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<span class="ms-span ms-radio">{{$t('loop.interval')}}</span>
|
||||
<el-input-number size="small" v-model="controller.countController.interval" :placeholder="$t('commons.millisecond')" :max="1000*10000000" :min="0"/>
|
||||
<span class="ms-span ms-radio">秒</span>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<span class="ms-span ms-radio">{{$t('loop.proceed')}}</span>
|
||||
<el-switch v-model="controller.countController.proceed"/>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
|
||||
<div v-else-if="controller.loopType==='FOREACH'">
|
||||
<el-row>
|
||||
<el-col :span="8">
|
||||
<el-input :placeholder="$t('api_test.request.condition_variable')" v-model="controller.forEachController.inputVal" size="small"/>
|
||||
</el-col>
|
||||
<el-col :span="1" style="margin-top: 6px">
|
||||
<span style="margin:10px 10px 10px">in</span>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-input :placeholder="$t('api_test.request.condition_variable')" v-model="controller.forEachController.returnVal" size="small"/>
|
||||
</el-col>
|
||||
<el-col :span="7">
|
||||
<span class="ms-span ms-radio">{{$t('loop.interval')}}</span>
|
||||
<el-input-number size="small" v-model="controller.forEachController.interval" :placeholder="$t('commons.millisecond')" :max="1000*10000000" :min="0"/>
|
||||
<span class="ms-span ms-radio">秒</span>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
<div v-else>
|
||||
<el-input size="small" v-model="controller.whileController.variable" style="width: 20%" :placeholder="$t('api_test.request.condition_variable')"/>
|
||||
|
||||
<el-select v-model="controller.whileController.operator" :placeholder="$t('commons.please_select')" size="small"
|
||||
@change="change" style="width: 10%;margin-left: 10px">
|
||||
<el-option v-for="o in operators" :key="o.value" :label="$t(o.label)" :value="o.value"/>
|
||||
</el-select>
|
||||
<el-input size="small" v-model="controller.whileController.value" :placeholder="$t('api_test.value')" v-if="!hasEmptyOperator" style="width: 20%;margin-left: 20px"/>
|
||||
<span class="ms-span ms-radio">{{$t('loop.timeout')}}</span>
|
||||
<el-input-number size="small" v-model="controller.whileController.timeout" :placeholder="$t('commons.millisecond')" :max="1000*10000000" :min="0"/>
|
||||
<span class="ms-span ms-radio">秒</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</el-collapse-transition>
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "MsLoopController",
|
||||
props: {
|
||||
controller: {},
|
||||
node: {},
|
||||
index: Object,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
operators: {
|
||||
EQ: {
|
||||
label: "commons.adv_search.operators.equals",
|
||||
value: "=="
|
||||
},
|
||||
NE: {
|
||||
label: "commons.adv_search.operators.not_equals",
|
||||
value: "!="
|
||||
},
|
||||
LIKE: {
|
||||
label: "commons.adv_search.operators.like",
|
||||
value: "=~"
|
||||
},
|
||||
NOT_LIKE: {
|
||||
label: "commons.adv_search.operators.not_like",
|
||||
value: "!~"
|
||||
},
|
||||
GT: {
|
||||
label: "commons.adv_search.operators.gt",
|
||||
value: ">"
|
||||
},
|
||||
LT: {
|
||||
label: "commons.adv_search.operators.lt",
|
||||
value: "<"
|
||||
},
|
||||
IS_EMPTY: {
|
||||
label: "commons.adv_search.operators.is_empty",
|
||||
value: "is empty"
|
||||
},
|
||||
IS_NOT_EMPTY: {
|
||||
label: "commons.adv_search.operators.is_not_empty",
|
||||
value: "is not empty"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
remove() {
|
||||
this.$emit('remove', this.controller, this.node);
|
||||
},
|
||||
copyRow() {
|
||||
this.$emit('copyRow', this.controller, this.node);
|
||||
},
|
||||
active(item) {
|
||||
item.active = !item.active;
|
||||
this.reload();
|
||||
},
|
||||
changeRadio() {
|
||||
this.controller.active = true;
|
||||
this.reload();
|
||||
},
|
||||
change(value) {
|
||||
if (value.indexOf("empty") > 0 && !!this.controller.value) {
|
||||
this.controller.value = "";
|
||||
}
|
||||
},
|
||||
reload() {
|
||||
this.loading = true
|
||||
this.$nextTick(() => {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
hasEmptyOperator() {
|
||||
return !!this.controller.operator && this.controller.operator.indexOf("empty") > 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.ms-api-col {
|
||||
background-color: #F4F4F5;
|
||||
border-color: #02A7F0;
|
||||
margin-right: 10px;
|
||||
color: #02A7F0;
|
||||
}
|
||||
|
||||
.ms-title-buttion {
|
||||
background-color: #F4F4F5;
|
||||
margin-right: 20px;
|
||||
color: #02A7F0;
|
||||
}
|
||||
|
||||
.icon.is-active {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
.ms-span {
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
.ms-radio {
|
||||
color: #606266;
|
||||
font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", Arial, sans-serif;
|
||||
font-size: 13px;
|
||||
font-weight: normal;
|
||||
}
|
||||
</style>
|
|
@ -1,5 +1,5 @@
|
|||
export const ELEMENTS = new Map([
|
||||
['ALL', ["scenario", "HTTPSamplerProxy", "DubboSampler", "JDBCSampler", "TCPSampler", "OT_IMPORT", "IfController", "ConstantTimer", "JSR223Processor", "CustomizeReq"]],
|
||||
['ALL', ["scenario", "HTTPSamplerProxy", "DubboSampler", "JDBCSampler", "TCPSampler", "OT_IMPORT", "IfController", "LoopController", "ConstantTimer", "JSR223Processor", "CustomizeReq"]],
|
||||
['scenario', ["HTTPSamplerProxy", "DubboSampler", "JDBCSampler", "TCPSampler", "CASE", "OT_IMPORT", "IfController", "ConstantTimer", "JSR223Processor", "CustomizeReq"]],
|
||||
['HTTPSamplerProxy', ["ConstantTimer", "JSR223PreProcessor", "JSR223PostProcessor", "Assertions", "Extract"]],
|
||||
['DubboSampler', ["ConstantTimer", "JSR223PreProcessor", "JSR223PostProcessor", "Assertions", "Extract"]],
|
||||
|
@ -7,6 +7,7 @@ export const ELEMENTS = new Map([
|
|||
['TCPSampler', ["ConstantTimer", "JSR223PreProcessor", "JSR223PostProcessor", "Assertions", "Extract"]],
|
||||
['OT_IMPORT', ["ConstantTimer", "JSR223PreProcessor", "JSR223PostProcessor", "Assertions", "Extract"]],
|
||||
['IfController', ["IfController", "HTTPSamplerProxy", "DubboSampler", "JDBCSampler", "TCPSampler", "OT_IMPORT", "ConstantTimer", "JSR223Processor", "JSR223PreProcessor", "JSR223PostProcessor", "Assertions", "Extract", "CustomizeReq"]],
|
||||
['LoopController', ["IfController", "HTTPSamplerProxy", "DubboSampler", "JDBCSampler", "TCPSampler", "OT_IMPORT", "ConstantTimer", "JSR223Processor", "JSR223PreProcessor", "JSR223PostProcessor", "Assertions", "Extract", "CustomizeReq"]],
|
||||
['ConstantTimer', []],
|
||||
['JSR223Processor', ["ConstantTimer", "JSR223PreProcessor", "JSR223PostProcessor", "Assertions", "Extract"]],
|
||||
['JSR223PreProcessor', []],
|
||||
|
@ -27,6 +28,7 @@ export const ELEMENT_TYPE = {
|
|||
JSR223PostProcessor: "JSR223PostProcessor",
|
||||
Assertions: "Assertions",
|
||||
Extract: "Extract",
|
||||
CustomizeReq: "CustomizeReq"
|
||||
CustomizeReq: "CustomizeReq",
|
||||
LoopController: "LoopController"
|
||||
}
|
||||
|
||||
|
|
|
@ -1016,6 +1016,38 @@ export class IfController extends Controller {
|
|||
}
|
||||
}
|
||||
|
||||
export class LoopController extends Controller {
|
||||
constructor(options = {}) {
|
||||
super("LoopController", options);
|
||||
this.type = "LoopController";
|
||||
this.active = false;
|
||||
this.loopType = "LOOP_COUNT";
|
||||
this.countController = {loops: 0, interval: 0, proceed: false};
|
||||
this.forEachController = {inputVal: "", returnVal: "", interval: 0};
|
||||
this.whileController = {variable: "", operator: "", value: "", timeout: 0};
|
||||
this.hashTree = [];
|
||||
this.set(options);
|
||||
}
|
||||
|
||||
isValid() {
|
||||
if (!!this.operator && this.operator.indexOf("empty") > 0) {
|
||||
return !!this.variable && !!this.operator;
|
||||
}
|
||||
return !!this.variable && !!this.operator && !!this.value;
|
||||
}
|
||||
|
||||
label() {
|
||||
if (this.isValid()) {
|
||||
let label = this.variable;
|
||||
if (this.operator) label += " " + this.operator;
|
||||
if (this.value) label += " " + this.value;
|
||||
return label;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export class Timer extends BaseConfig {
|
||||
static TYPES = {
|
||||
CONSTANT_TIMER: "Constant Timer",
|
||||
|
|
|
@ -544,6 +544,7 @@ export default {
|
|||
res_param: "Response content",
|
||||
batch_delete: "Batch deletion",
|
||||
delete_confirm: "Confirm deletion",
|
||||
delete_confirm_step: "Confirm deletion step",
|
||||
assertions_rule: "Assertion rule",
|
||||
response_header: "Response header",
|
||||
response_body: "Response body",
|
||||
|
@ -574,6 +575,7 @@ export default {
|
|||
external_import: "External import",
|
||||
wait_controller: "Wait controller",
|
||||
if_controller: "If controller",
|
||||
loop_controller: "Loop Controller",
|
||||
scenario_import: "Scenario import",
|
||||
customize_script: "Customize script",
|
||||
customize_req: "Customize req",
|
||||
|
@ -1259,8 +1261,8 @@ export default {
|
|||
host: 'Host number cannot be empty',
|
||||
port: 'Port cannot be empty',
|
||||
account: 'Account cannot be empty',
|
||||
test_recipients:'Test recipients',
|
||||
tip:'Tip: use as test mail recipient only',
|
||||
test_recipients: 'Test recipients',
|
||||
tip: 'Tip: use as test mail recipient only',
|
||||
|
||||
},
|
||||
i18n: {
|
||||
|
@ -1414,5 +1416,15 @@ export default {
|
|||
nothing: "Nothing",
|
||||
preview: "Preview",
|
||||
add_custom: "Add Custom Prop"
|
||||
},
|
||||
loop: {
|
||||
loops_title: "loops",
|
||||
foreach: "ForEach",
|
||||
while: "While",
|
||||
loops: "loops",
|
||||
interval: "interval",
|
||||
proceed: "proceed",
|
||||
timeout: "timeout",
|
||||
}
|
||||
|
||||
};
|
||||
|
|
|
@ -543,6 +543,7 @@ export default {
|
|||
res_param: "响应内容",
|
||||
batch_delete: "批量删除",
|
||||
delete_confirm: "确认删除接口",
|
||||
delete_confirm_step: "确认删除步骤",
|
||||
assertions_rule: "断言规则",
|
||||
response_header: "响应头",
|
||||
response_body: "响应体",
|
||||
|
@ -573,6 +574,7 @@ export default {
|
|||
external_import: "外部导入",
|
||||
wait_controller: "等待控制器",
|
||||
if_controller: "条件控制器",
|
||||
loop_controller: "循环控制器",
|
||||
scenario_import: "场景导入",
|
||||
customize_script: "自定义脚本",
|
||||
customize_req: "自定义请求",
|
||||
|
@ -1260,8 +1262,8 @@ export default {
|
|||
host: '主机号不能为空',
|
||||
port: '端口号不能为空',
|
||||
account: '账户不能为空',
|
||||
test_recipients:'测试收件人',
|
||||
tip:'提示:仅用来作为测试邮件收件人',
|
||||
test_recipients: '测试收件人',
|
||||
tip: '提示:仅用来作为测试邮件收件人',
|
||||
},
|
||||
i18n: {
|
||||
home: '首页',
|
||||
|
@ -1414,5 +1416,14 @@ export default {
|
|||
nothing: "无",
|
||||
preview: "预览",
|
||||
add_custom: "添加自定义属性"
|
||||
},
|
||||
loop: {
|
||||
loops_title: "次数循环",
|
||||
foreach: "ForEach 循环",
|
||||
while: "While 循环",
|
||||
loops: "循环次数",
|
||||
interval: "循环间隔",
|
||||
proceed: "成功后继续循环",
|
||||
timeout: "循环超时时间",
|
||||
}
|
||||
};
|
||||
|
|
|
@ -543,6 +543,7 @@ export default {
|
|||
res_param: "響應内容",
|
||||
batch_delete: "批量删除",
|
||||
delete_confirm: "確認刪除接口",
|
||||
delete_confirm_step: "確認刪除步骤",
|
||||
assertions_rule: "斷言規則",
|
||||
response_header: "響應頭",
|
||||
response_body: "響應體",
|
||||
|
@ -573,6 +574,7 @@ export default {
|
|||
external_import: "外部導入",
|
||||
wait_controller: "等待控制器",
|
||||
if_controller: "條件控制器",
|
||||
loop_controller: "循环控制器",
|
||||
scenario_import: "場景導入",
|
||||
customize_script: "自定義脚本",
|
||||
customize_req: "自定義請求",
|
||||
|
@ -1259,8 +1261,8 @@ export default {
|
|||
host: '主機號不能為空',
|
||||
port: '端口號不能為空',
|
||||
account: '賬戶不能為空',
|
||||
test_recipients:'測試收件人',
|
||||
tip:'提示:僅用來作為測試郵件收件人',
|
||||
test_recipients: '測試收件人',
|
||||
tip: '提示:僅用來作為測試郵件收件人',
|
||||
},
|
||||
i18n: {
|
||||
home: '首頁',
|
||||
|
@ -1413,5 +1415,15 @@ export default {
|
|||
nothing: "无",
|
||||
preview: "预览",
|
||||
add_custom: "添加自定义属性"
|
||||
},
|
||||
loop: {
|
||||
loops_title: "次數循環",
|
||||
foreach: "ForEach 循環",
|
||||
while: "While 循環",
|
||||
loops: "循環次数",
|
||||
interval: "循環間隔",
|
||||
proceed: "成功後繼續循環",
|
||||
timeout: "循環超時時間",
|
||||
}
|
||||
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue