Merge remote-tracking branch 'origin/master'

This commit is contained in:
wenyann 2021-01-13 14:42:57 +08:00
commit fd651e3b34
15 changed files with 263 additions and 141 deletions

View File

@ -68,7 +68,10 @@
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId> <artifactId>spring-boot-starter-mail</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.projectlombok</groupId> <groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId> <artifactId>lombok</artifactId>

View File

@ -6,6 +6,7 @@ import io.metersphere.api.dto.definition.request.ParameterConfig;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.CollectionUtils;
import org.apache.jmeter.extractor.JSR223PostProcessor;
import org.apache.jmeter.extractor.RegexExtractor; import org.apache.jmeter.extractor.RegexExtractor;
import org.apache.jmeter.extractor.XPath2Extractor; import org.apache.jmeter.extractor.XPath2Extractor;
import org.apache.jmeter.extractor.json.jsonpath.JSONPostProcessor; import org.apache.jmeter.extractor.json.jsonpath.JSONPostProcessor;
@ -14,6 +15,8 @@ import org.apache.jmeter.testelement.TestElement;
import org.apache.jorphan.collections.HashTree; import org.apache.jorphan.collections.HashTree;
import java.util.List; import java.util.List;
import java.util.Optional;
import java.util.StringJoiner;
@Data @Data
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
@ -32,24 +35,34 @@ public class MsExtract extends MsTestElement {
} }
private void addRequestExtractors(HashTree samplerHashTree) { private void addRequestExtractors(HashTree samplerHashTree) {
StringJoiner extract = new StringJoiner(";");
if (CollectionUtils.isNotEmpty(this.getRegex())) { if (CollectionUtils.isNotEmpty(this.getRegex())) {
this.getRegex().stream().filter(MsExtractRegex::isValid).forEach(extractRegex -> this.getRegex().stream().filter(MsExtractRegex::isValid).forEach(extractRegex ->
samplerHashTree.add(regexExtractor(extractRegex)) samplerHashTree.add(regexExtractor(extractRegex, extract))
); );
} }
if (CollectionUtils.isNotEmpty(this.getXpath())) { if (CollectionUtils.isNotEmpty(this.getXpath())) {
this.getXpath().stream().filter(MsExtractCommon::isValid).forEach(extractXPath -> this.getXpath().stream().filter(MsExtractCommon::isValid).forEach(extractXPath ->
samplerHashTree.add(xPath2Extractor(extractXPath)) samplerHashTree.add(xPath2Extractor(extractXPath, extract))
); );
} }
if (CollectionUtils.isNotEmpty(this.getJson())) { if (CollectionUtils.isNotEmpty(this.getJson())) {
this.getJson().stream().filter(MsExtractCommon::isValid).forEach(extractJSONPath -> this.getJson().stream().filter(MsExtractCommon::isValid).forEach(extractJSONPath ->
samplerHashTree.add(jsonPostProcessor(extractJSONPath)) samplerHashTree.add(jsonPostProcessor(extractJSONPath, extract))
); );
} }
if (Optional.ofNullable(extract).orElse(extract).length() > 0) {
JSR223PostProcessor shell = new JSR223PostProcessor();
shell.setEnabled(true);
shell.setName(this.getName());
shell.setProperty("script", "io.metersphere.api.jmeter.JMeterVars.addVars(prev.hashCode(),vars," + "\"" + extract.toString() + "\"" + ");");
samplerHashTree.add(shell);
}
} }
private RegexExtractor regexExtractor(MsExtractRegex extractRegex) { private RegexExtractor regexExtractor(MsExtractRegex extractRegex, StringJoiner extract) {
RegexExtractor extractor = new RegexExtractor(); RegexExtractor extractor = new RegexExtractor();
extractor.setEnabled(true); extractor.setEnabled(true);
extractor.setName(extractRegex.getVariable() + " RegexExtractor"); extractor.setName(extractRegex.getVariable() + " RegexExtractor");
@ -62,10 +75,12 @@ public class MsExtract extends MsTestElement {
extractor.setMatchNumber(-1); extractor.setMatchNumber(-1);
} }
extractor.setTemplate("$1$"); extractor.setTemplate("$1$");
extract.add(extractor.getRefName());
return extractor; return extractor;
} }
private XPath2Extractor xPath2Extractor(MsExtractXPath extractXPath) { private XPath2Extractor xPath2Extractor(MsExtractXPath extractXPath, StringJoiner extract) {
XPath2Extractor extractor = new XPath2Extractor(); XPath2Extractor extractor = new XPath2Extractor();
extractor.setEnabled(true); extractor.setEnabled(true);
extractor.setName(extractXPath.getVariable() + " XPath2Extractor"); extractor.setName(extractXPath.getVariable() + " XPath2Extractor");
@ -76,10 +91,11 @@ public class MsExtract extends MsTestElement {
if (extractXPath.isMultipleMatching()) { if (extractXPath.isMultipleMatching()) {
extractor.setMatchNumber(-1); extractor.setMatchNumber(-1);
} }
extract.add(extractor.getRefName());
return extractor; return extractor;
} }
private JSONPostProcessor jsonPostProcessor(MsExtractJSONPath extractJSONPath) { private JSONPostProcessor jsonPostProcessor(MsExtractJSONPath extractJSONPath, StringJoiner extract) {
JSONPostProcessor extractor = new JSONPostProcessor(); JSONPostProcessor extractor = new JSONPostProcessor();
extractor.setEnabled(true); extractor.setEnabled(true);
extractor.setName(extractJSONPath.getVariable() + " JSONExtractor"); extractor.setName(extractJSONPath.getVariable() + " JSONExtractor");
@ -90,6 +106,7 @@ public class MsExtract extends MsTestElement {
if (extractJSONPath.isMultipleMatching()) { if (extractJSONPath.isMultipleMatching()) {
extractor.setMatchNumbers("-1"); extractor.setMatchNumbers("-1");
} }
extract.add(extractor.getRefNames());
return extractor; return extractor;
} }
} }

View File

@ -16,6 +16,7 @@ import io.metersphere.notice.sender.NoticeModel;
import io.metersphere.notice.service.NoticeSendService; import io.metersphere.notice.service.NoticeSendService;
import io.metersphere.service.SystemParameterService; import io.metersphere.service.SystemParameterService;
import io.metersphere.track.service.TestPlanTestCaseService; import io.metersphere.track.service.TestPlanTestCaseService;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.jmeter.assertions.AssertionResult; import org.apache.jmeter.assertions.AssertionResult;
import org.apache.jmeter.protocol.http.sampler.HTTPSampleResult; import org.apache.jmeter.protocol.http.sampler.HTTPSampleResult;
@ -303,20 +304,14 @@ public class APIBackendListenerClient extends AbstractBackendListenerClient impl
responseResult.setResponseSize(result.getResponseData().length); responseResult.setResponseSize(result.getResponseData().length);
responseResult.setResponseTime(result.getTime()); responseResult.setResponseTime(result.getTime());
responseResult.setResponseMessage(result.getResponseMessage()); responseResult.setResponseMessage(result.getResponseMessage());
if (JMeterVars.get(result.hashCode()) != null && CollectionUtils.isNotEmpty(JMeterVars.get(result.hashCode()).entrySet())) {
if (JMeterVars.get(result.hashCode()) != null) { StringBuilder builder = new StringBuilder();
List<String> vars = new LinkedList<>(); for (Map.Entry<String, Object> entry : JMeterVars.get(result.hashCode()).entrySet()) {
JMeterVars.get(result.hashCode()).entrySet().parallelStream().reduce(vars, (first, second) -> { builder.append(entry.getKey()).append("").append(entry.getValue()).append("\n");
first.add(second.getKey() + "" + second.getValue()); }
return first; if (StringUtils.isNotEmpty(builder)) {
}, (first, second) -> { responseResult.setVars(builder.toString());
if (first == second) { }
return first;
}
first.addAll(second);
return first;
});
responseResult.setVars(StringUtils.join(vars, "\n"));
JMeterVars.remove(result.hashCode()); JMeterVars.remove(result.hashCode());
} }
for (AssertionResult assertionResult : result.getAssertionResults()) { for (AssertionResult assertionResult : result.getAssertionResults()) {

View File

@ -91,7 +91,6 @@ public class JMeterService {
public void runDefinition(String testId, HashTree testPlan, String debugReportId, String runMode) { public void runDefinition(String testId, HashTree testPlan, String debugReportId, String runMode) {
try { try {
init(); init();
JMeterVars.addJSR223PostProcessor(testPlan);
addBackendListener(testId, debugReportId, runMode, testPlan); addBackendListener(testId, debugReportId, runMode, testPlan);
LocalRunner runner = new LocalRunner(testPlan); LocalRunner runner = new LocalRunner(testPlan);
runner.run(); runner.run();

View File

@ -85,6 +85,7 @@ public class HistoricalDataUpgradeService {
ifController.setType("IfController"); ifController.setType("IfController");
ifController.setName("IfController"); ifController.setName("IfController");
ifController.setIndex(index + ""); ifController.setIndex(index + "");
ifController.setHashTree(new LinkedList<>());
ifController.setResourceId(UUID.randomUUID().toString()); ifController.setResourceId(UUID.randomUUID().toString());
} }
// 等待控制器 // 等待控制器
@ -93,6 +94,7 @@ public class HistoricalDataUpgradeService {
BeanUtils.copyBean(constantTimer, request.getTimer()); BeanUtils.copyBean(constantTimer, request.getTimer());
constantTimer.setType("ConstantTimer"); constantTimer.setType("ConstantTimer");
constantTimer.setIndex(index + ""); constantTimer.setIndex(index + "");
constantTimer.setHashTree(new LinkedList<>());
constantTimer.setResourceId(UUID.randomUUID().toString()); constantTimer.setResourceId(UUID.randomUUID().toString());
testElements.add(constantTimer); testElements.add(constantTimer);
} }
@ -119,7 +121,6 @@ public class HistoricalDataUpgradeService {
if (StringUtils.isEmpty(element.getName())) { if (StringUtils.isEmpty(element.getName())) {
element.setName(request1.getPath()); element.setName(request1.getPath());
} }
element.setType("HTTPSamplerProxy"); element.setType("HTTPSamplerProxy");
} }
if (request instanceof DubboRequest) { if (request instanceof DubboRequest) {
@ -147,6 +148,7 @@ public class HistoricalDataUpgradeService {
} }
element.setIndex(index + ""); element.setIndex(index + "");
element.setResourceId(UUID.randomUUID().toString()); element.setResourceId(UUID.randomUUID().toString());
element.setHashTree(new LinkedList<>());
LinkedList<MsTestElement> msTestElements = new LinkedList<>(); LinkedList<MsTestElement> msTestElements = new LinkedList<>();
// 断言规则 // 断言规则
if (request.getAssertions() != null && ((request.getAssertions().getDuration() != null && request.getAssertions().getDuration().getValue() > 0) || if (request.getAssertions() != null && ((request.getAssertions().getDuration() != null && request.getAssertions().getDuration().getValue() > 0) ||
@ -157,6 +159,7 @@ public class HistoricalDataUpgradeService {
msAssertions.setType("Assertions"); msAssertions.setType("Assertions");
msAssertions.setIndex(index + ""); msAssertions.setIndex(index + "");
msAssertions.setResourceId(UUID.randomUUID().toString()); msAssertions.setResourceId(UUID.randomUUID().toString());
msAssertions.setHashTree(new LinkedList<>());
msTestElements.add(msAssertions); msTestElements.add(msAssertions);
} }
// 提取规则 // 提取规则
@ -166,6 +169,7 @@ public class HistoricalDataUpgradeService {
MsExtract extract = JSON.parseObject(extractJson, MsExtract.class); MsExtract extract = JSON.parseObject(extractJson, MsExtract.class);
extract.setType("Extract"); extract.setType("Extract");
extract.setIndex(index + ""); extract.setIndex(index + "");
extract.setHashTree(new LinkedList<>());
extract.setResourceId(UUID.randomUUID().toString()); extract.setResourceId(UUID.randomUUID().toString());
msTestElements.add(extract); msTestElements.add(extract);
} }
@ -175,6 +179,7 @@ public class HistoricalDataUpgradeService {
MsJSR223PreProcessor preProcessor = JSON.parseObject(preJson, MsJSR223PreProcessor.class); MsJSR223PreProcessor preProcessor = JSON.parseObject(preJson, MsJSR223PreProcessor.class);
preProcessor.setType("JSR223PreProcessor"); preProcessor.setType("JSR223PreProcessor");
preProcessor.setIndex(index + ""); preProcessor.setIndex(index + "");
preProcessor.setHashTree(new LinkedList<>());
preProcessor.setResourceId(UUID.randomUUID().toString()); preProcessor.setResourceId(UUID.randomUUID().toString());
msTestElements.add(preProcessor); msTestElements.add(preProcessor);
} }
@ -184,6 +189,7 @@ public class HistoricalDataUpgradeService {
MsJSR223PostProcessor preProcessor = JSON.parseObject(preJson, MsJSR223PostProcessor.class); MsJSR223PostProcessor preProcessor = JSON.parseObject(preJson, MsJSR223PostProcessor.class);
preProcessor.setType("JSR223PostProcessor"); preProcessor.setType("JSR223PostProcessor");
preProcessor.setIndex(index + ""); preProcessor.setIndex(index + "");
preProcessor.setHashTree(new LinkedList<>());
preProcessor.setResourceId(UUID.randomUUID().toString()); preProcessor.setResourceId(UUID.randomUUID().toString());
msTestElements.add(preProcessor); msTestElements.add(preProcessor);
} }

View File

@ -0,0 +1,32 @@
package io.metersphere.commons.consumer;
import com.alibaba.fastjson.JSON;
import io.metersphere.Application;
import io.metersphere.base.domain.LoadTestReport;
import io.metersphere.commons.utils.CommonBeanFactory;
import io.metersphere.commons.utils.LogUtil;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.reflections8.Reflections;
import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.stereotype.Service;
import java.util.Set;
@Service
public class LoadTestConsumer {
public static final String CONSUME_ID = "load-test-data";
@KafkaListener(id = CONSUME_ID, topics = "${kafka.test.topic}", groupId = "${spring.kafka.consumer.group-id}")
public void consume(ConsumerRecord<?, String> record) {
LoadTestReport loadTestReport = JSON.parseObject(record.value(), LoadTestReport.class);
Reflections reflections = new Reflections(Application.class);
Set<Class<? extends LoadTestFinishEvent>> subTypes = reflections.getSubTypesOf(LoadTestFinishEvent.class);
subTypes.forEach(s -> {
try {
CommonBeanFactory.getBean(s).execute(loadTestReport);
} catch (Exception e) {
LogUtil.error(e);
}
});
}
}

View File

@ -0,0 +1,7 @@
package io.metersphere.commons.consumer;
import io.metersphere.base.domain.LoadTestReport;
public interface LoadTestFinishEvent {
void execute(LoadTestReport loadTestReport);
}

View File

@ -1,10 +1,9 @@
package io.metersphere.performance.notice; package io.metersphere.performance.notice;
import io.metersphere.base.domain.LoadTestReportWithBLOBs; import io.metersphere.base.domain.LoadTestReport;
import io.metersphere.base.mapper.LoadTestReportMapper;
import io.metersphere.commons.constants.NoticeConstants; import io.metersphere.commons.constants.NoticeConstants;
import io.metersphere.commons.constants.PerformanceTestStatus; import io.metersphere.commons.constants.PerformanceTestStatus;
import io.metersphere.commons.utils.LogUtil; import io.metersphere.commons.consumer.LoadTestFinishEvent;
import io.metersphere.dto.BaseSystemConfigDTO; import io.metersphere.dto.BaseSystemConfigDTO;
import io.metersphere.i18n.Translator; import io.metersphere.i18n.Translator;
import io.metersphere.notice.sender.NoticeModel; import io.metersphere.notice.sender.NoticeModel;
@ -13,53 +12,18 @@ import io.metersphere.service.SystemParameterService;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import javax.annotation.PreDestroy;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@Component @Component
public class PerformanceNoticeTask { public class PerformanceNoticeEvent implements LoadTestFinishEvent {
@Resource @Resource
private SystemParameterService systemParameterService; private SystemParameterService systemParameterService;
@Resource @Resource
private LoadTestReportMapper loadTestReportMapper;
@Resource
private NoticeSendService noticeSendService; private NoticeSendService noticeSendService;
private final ExecutorService executorService = Executors.newFixedThreadPool(20); public void sendNotice(LoadTestReport loadTestReport) {
private boolean isRunning = false;
@PreDestroy
public void preDestroy() {
isRunning = false;
}
public void registerNoticeTask(LoadTestReportWithBLOBs loadTestReport) {
isRunning = true;
executorService.submit(() -> {
LogUtil.info("性能测试定时任务");
while (isRunning) {
LoadTestReportWithBLOBs loadTestReportFromDatabase = loadTestReportMapper.selectByPrimaryKey(loadTestReport.getId());
if (StringUtils.equalsAny(loadTestReportFromDatabase.getStatus(),
PerformanceTestStatus.Completed.name(), PerformanceTestStatus.Error.name())) {
sendNotice(loadTestReportFromDatabase);
return;
}
try {
//查询定时任务是否关闭
Thread.sleep(1000 * 10);// 检查 loadtest 的状态
} catch (InterruptedException e) {
LogUtil.error(e.getMessage(), e);
}
}
});
}
public void sendNotice(LoadTestReportWithBLOBs loadTestReport) {
BaseSystemConfigDTO baseSystemConfigDTO = systemParameterService.getBaseInfo(); BaseSystemConfigDTO baseSystemConfigDTO = systemParameterService.getBaseInfo();
String url = baseSystemConfigDTO.getUrl() + "/#/performance/report/view/" + loadTestReport.getId(); String url = baseSystemConfigDTO.getUrl() + "/#/performance/report/view/" + loadTestReport.getId();
String successContext = ""; String successContext = "";
@ -102,4 +66,14 @@ public class PerformanceNoticeTask {
.build(); .build();
noticeSendService.send(loadTestReport.getTriggerMode(), noticeModel); noticeSendService.send(loadTestReport.getTriggerMode(), noticeModel);
} }
@Override
public void execute(LoadTestReport loadTestReport) {
if (StringUtils.equals(NoticeConstants.Mode.API, loadTestReport.getTriggerMode()) || StringUtils.equals(NoticeConstants.Mode.SCHEDULE, loadTestReport.getTriggerMode())) {
if (StringUtils.equalsAny(loadTestReport.getStatus(),
PerformanceTestStatus.Completed.name(), PerformanceTestStatus.Error.name())) {
sendNotice(loadTestReport);
}
}
}
} }

View File

@ -21,7 +21,6 @@ import io.metersphere.i18n.Translator;
import io.metersphere.job.sechedule.PerformanceTestJob; import io.metersphere.job.sechedule.PerformanceTestJob;
import io.metersphere.performance.engine.Engine; import io.metersphere.performance.engine.Engine;
import io.metersphere.performance.engine.EngineFactory; import io.metersphere.performance.engine.EngineFactory;
import io.metersphere.performance.notice.PerformanceNoticeTask;
import io.metersphere.service.FileService; import io.metersphere.service.FileService;
import io.metersphere.service.QuotaService; import io.metersphere.service.QuotaService;
import io.metersphere.service.ScheduleService; import io.metersphere.service.ScheduleService;
@ -75,8 +74,6 @@ public class PerformanceTestService {
@Resource @Resource
private TestCaseService testCaseService; private TestCaseService testCaseService;
@Resource @Resource
private PerformanceNoticeTask performanceNoticeTask;
@Resource
private TestResourcePoolMapper testResourcePoolMapper; private TestResourcePoolMapper testResourcePoolMapper;
public List<LoadTestDTO> list(QueryTestPlanRequest request) { public List<LoadTestDTO> list(QueryTestPlanRequest request) {
@ -240,10 +237,6 @@ public class PerformanceTestService {
startEngine(loadTest, engine, request.getTriggerMode()); startEngine(loadTest, engine, request.getTriggerMode());
LoadTestReportWithBLOBs loadTestReport = loadTestReportMapper.selectByPrimaryKey(engine.getReportId());
if (StringUtils.equals(NoticeConstants.Mode.API, loadTestReport.getTriggerMode()) || StringUtils.equals(NoticeConstants.Mode.SCHEDULE, loadTestReport.getTriggerMode())) {
performanceNoticeTask.registerNoticeTask(loadTestReport);
}
return engine.getReportId(); return engine.getReportId();
} }

View File

@ -22,12 +22,12 @@ public class ApiKeyFilter extends AnonymousFilter {
if (LogUtil.getLogger().isDebugEnabled()) { if (LogUtil.getLogger().isDebugEnabled()) {
LogUtil.getLogger().debug("user auth: " + userId); LogUtil.getLogger().debug("user auth: " + userId);
} }
SecurityUtils.getSubject().login(new MsUserToken(userId, ApiKeySessionHandler.random, "APIKEY")); SecurityUtils.getSubject().login(new MsUserToken(userId, ApiKeySessionHandler.random, "LOCAL"));
} }
} else { } else {
if (ApiKeyHandler.isApiKeyCall(WebUtils.toHttp(request))) { if (ApiKeyHandler.isApiKeyCall(WebUtils.toHttp(request))) {
String userId = ApiKeyHandler.getUser(WebUtils.toHttp(request)); String userId = ApiKeyHandler.getUser(WebUtils.toHttp(request));
SecurityUtils.getSubject().login(new MsUserToken(userId, ApiKeySessionHandler.random, "APIKEY")); SecurityUtils.getSubject().login(new MsUserToken(userId, ApiKeySessionHandler.random, "LOCAL"));
} }
} }

View File

@ -12,7 +12,9 @@ spring.datasource.hikari.pool-name=DatebookHikariCP
spring.datasource.hikari.max-lifetime=1800000 spring.datasource.hikari.max-lifetime=1800000
spring.datasource.hikari.connection-timeout=30000 spring.datasource.hikari.connection-timeout=30000
spring.datasource.hikari.connection-test-query=SELECT 1 spring.datasource.hikari.connection-test-query=SELECT 1
#kafka
spring.kafka.bootstrap-servers=${kafka.bootstrap-servers}
spring.kafka.consumer.group-id=metersphere_group_id
# mybatis # mybatis
mybatis.configuration.cache-enabled=true mybatis.configuration.cache-enabled=true
mybatis.configuration.lazy-loading-enabled=false mybatis.configuration.lazy-loading-enabled=false

View File

@ -156,11 +156,11 @@
<!--等待控制器--> <!--等待控制器-->
<ms-constant-timer :timer="data" :node="node" v-if="data.type==='ConstantTimer'" @remove="remove" @copyRow="copyRow"/> <ms-constant-timer :timer="data" :node="node" v-if="data.type==='ConstantTimer'" @remove="remove" @copyRow="copyRow"/>
<!--自定义脚本--> <!--自定义脚本-->
<ms-jsr233-processor v-if="data.type==='JSR223Processor'" @remove="remove" @copyRow="copyRow" :title="$t('api_test.automation.customize_script')" :jsr223-processor="data" :node="node"/> <ms-jsr233-processor :draggable="true" v-if="data.type==='JSR223Processor'" @remove="remove" @copyRow="copyRow" :title="$t('api_test.automation.customize_script')" :jsr223-processor="data" :node="node"/>
<!--前置脚本--> <!--前置脚本-->
<ms-jsr233-processor v-if="data.type==='JSR223PreProcessor'" @remove="remove" @copyRow="copyRow" :title="$t('api_test.definition.request.pre_script')" :jsr223-processor="data" :node="node"/> <ms-jsr233-processor :draggable="true" v-if="data.type==='JSR223PreProcessor'" @remove="remove" @copyRow="copyRow" :title="$t('api_test.definition.request.pre_script')" :jsr223-processor="data" :node="node"/>
<!--后置脚本--> <!--后置脚本-->
<ms-jsr233-processor v-if="data.type==='JSR223PostProcessor'" @remove="remove" @copyRow="copyRow" :title="$t('api_test.definition.request.post_script')" :jsr223-processor="data" :node="node"/> <ms-jsr233-processor :draggable="true" v-if="data.type==='JSR223PostProcessor'" @remove="remove" @copyRow="copyRow" :title="$t('api_test.definition.request.post_script')" :jsr223-processor="data" :node="node"/>
<!--断言规则--> <!--断言规则-->
<ms-api-assertions :draggable="true" @suggestClick="suggestClick(node)" :response="response" v-if="data.type==='Assertions'" @remove="remove" @copyRow="copyRow" :assertions="data" :node="node"/> <ms-api-assertions :draggable="true" @suggestClick="suggestClick(node)" :response="response" v-if="data.type==='Assertions'" @remove="remove" @copyRow="copyRow" :assertions="data" :node="node"/>
<!--提取规则--> <!--提取规则-->

View File

@ -29,63 +29,63 @@
</div> </div>
<el-collapse-transition> <el-collapse-transition>
<div v-if="data.active && showCollapse" draggable> <div v-if="data.active && showCollapse" :draggable="draggable">
<el-divider></el-divider> <el-divider></el-divider>
<slot></slot> <slot></slot>
</div> </div>
</el-collapse-transition> </el-collapse-transition>
</el-card> </el-card>
</template> </template>
<script> <script>
export default { export default {
name: "ApiBaseComponent", name: "ApiBaseComponent",
data() { data() {
return { return {
isShowInput: false isShowInput: false
}
},
props: {
draggable: Boolean,
data: {
type: Object,
default() {
return {}
},
},
color: {
type: String,
default() {
return "#B8741A"
} }
}, },
props: { backgroundColor: {
draggable: Boolean, type: String,
data: { default() {
type: Object, return "#F9F1EA"
default() {
return {}
},
},
color: {
type: String,
default() {
return "#B8741A"
}
},
backgroundColor: {
type: String,
default() {
return "#F9F1EA"
}
},
showCollapse: {
type: Boolean,
default() {
return true
}
},
title: String
},
methods: {
active() {
this.$set(this.data, 'active', !this.data.active);
this.$emit('active');
},
copyRow() {
this.$emit('copy');
},
remove() {
this.$emit('remove');
} }
},
showCollapse: {
type: Boolean,
default() {
return true
}
},
title: String
},
methods: {
active() {
this.$set(this.data, 'active', !this.data.active);
this.$emit('active');
},
copyRow() {
this.$emit('copy');
},
remove() {
this.$emit('remove');
} }
} }
}
</script> </script>
<style scoped> <style scoped>

View File

@ -3,6 +3,7 @@
@copy="copyRow" @copy="copyRow"
@remove="remove" @remove="remove"
:data="extract" :data="extract"
:draggable="draggable"
color="#015478" color="#015478"
background-color="#E6EEF2" background-color="#E6EEF2"
:title="$t('api_test.definition.request.extract_param')"> :title="$t('api_test.definition.request.extract_param')">
@ -46,6 +47,7 @@
import MsApiJsonpathSuggest from "../assertion/ApiJsonpathSuggest"; import MsApiJsonpathSuggest from "../assertion/ApiJsonpathSuggest";
import {ExtractJSONPath} from "../../../test/model/ScenarioModel"; import {ExtractJSONPath} from "../../../test/model/ScenarioModel";
import ApiBaseComponent from "../../../automation/scenario/common/ApiBaseComponent"; import ApiBaseComponent from "../../../automation/scenario/common/ApiBaseComponent";
export default { export default {
name: "MsApiExtract", name: "MsApiExtract",
components: { components: {
@ -66,6 +68,10 @@
isReadOnly: { isReadOnly: {
type: Boolean, type: Boolean,
default: false default: false
},
draggable: {
type: Boolean,
default: false,
} }
}, },
data() { data() {
@ -139,15 +145,18 @@
font-size: 13px; font-size: 13px;
margin-bottom: 10px; margin-bottom: 10px;
} }
.extract-item { .extract-item {
width: 100%; width: 100%;
} }
.extract-add { .extract-add {
padding: 10px; padding: 10px;
border: #DCDFE6 solid 1px; border: #DCDFE6 solid 1px;
margin: 5px 0; margin: 5px 0;
border-radius: 5px; border-radius: 5px;
} }
/deep/ .el-card__body { /deep/ .el-card__body {
padding: 15px; padding: 15px;
} }

View File

@ -7,13 +7,24 @@
:destroy-on-close="true" :destroy-on-close="true"
show-close show-close
@closed="handleClose" v-loading="loading"> @closed="handleClose" v-loading="loading">
<el-form :model="ruleForm" label-position="right" label-width="80px" size="small" :rules="rule">
<el-form-item :label="$t('test_track.module.module')" prop="apiScenarioModuleId"> <ms-node-tree
<el-select size="small" style="width: 80%" v-model="apiScenarioModuleId"> v-loading="result.loading"
<el-option v-for="item in moduleOptions" :key="item.id" :label="item.path" :value="item.id"/> :tree-nodes="data"
</el-select> @add="add"
</el-form-item> :type="'edit'"
</el-form> @edit="edit"
@drag="drag"
@remove="remove"
@nodeSelectEvent="nodeChange"
ref="nodeTree">
<template v-slot:header>
<el-input :placeholder="$t('test_track.module.search')" v-model="condition.filterText" size="small">
</el-input>
</template>
</ms-node-tree>
<span slot="footer" class="dialog-footer"> <span slot="footer" class="dialog-footer">
<ms-dialog-footer <ms-dialog-footer
@cancel="oneClickOperationVisible = false" @cancel="oneClickOperationVisible = false"
@ -28,31 +39,32 @@
import MsApiReportStatus from "../report/ApiReportStatus"; import MsApiReportStatus from "../report/ApiReportStatus";
import MsApiReportDialog from "./ApiReportDialog"; import MsApiReportDialog from "./ApiReportDialog";
import {getUUID, getCurrentProjectID} from "@/common/js/utils"; import {getUUID, getCurrentProjectID} from "@/common/js/utils";
import SelectMenu from "../../track/common/SelectMenu";
import MsNodeTree from "../../track/common/NodeTree";
import {buildNodePath} from "../definition/model/NodeTree"; import {buildNodePath} from "../definition/model/NodeTree";
export default { export default {
name: "MsUpgrade", name: "MsUpgrade",
components: { components: {
MsApiReportDialog, MsApiReportStatus, MsApiScenarioConfig, MsDialogFooter MsApiReportDialog, MsApiReportStatus, MsApiScenarioConfig,
MsDialogFooter,
MsNodeTree,
SelectMenu,
}, },
data() { data() {
return { return {
oneClickOperationVisible: false, oneClickOperationVisible: false,
apiScenarioModuleId: "",
moduleOptions: [], moduleOptions: [],
ruleForm: {},
loading: false, loading: false,
rule: { result: {},
apiScenarioModuleId: [ data: [],
{required: true, message: this.$t('test_track.module.module'), trigger: 'blur'}, condition: {
], filterText: "",
} trashEnable: false
},
currentModule: undefined,
}; };
}, },
created() {
this.initModule();
},
props: { props: {
selectIds: { selectIds: {
type: Set type: Set
@ -64,8 +76,14 @@
type: Set type: Set
} }
}, },
watch: {
'condition.filterText'(val) {
this.$refs.nodeTree.filter(val);
},
},
methods: { methods: {
openOneClickOperation() { openOneClickOperation() {
this.initModule();
this.oneClickOperationVisible = true; this.oneClickOperationVisible = true;
}, },
getPath(id) { getPath(id) {
@ -75,9 +93,13 @@
return path[0].path; return path[0].path;
}, },
confirm() { confirm() {
if (!this.currentModule) {
this.$warning("请选择一个模块");
return;
}
this.loading = true; this.loading = true;
let arr = Array.from(this.selectIds); let arr = Array.from(this.selectIds);
let obj = {testIds: arr, projectId: getCurrentProjectID(), modulePath: this.getPath(this.apiScenarioModuleId), moduleId: this.apiScenarioModuleId}; let obj = {testIds: arr, projectId: getCurrentProjectID(), modulePath: this.getPath(this.currentModule.id), moduleId: this.currentModule.id};
this.$post("/api/historicalDataUpgrade", obj, response => { this.$post("/api/historicalDataUpgrade", obj, response => {
this.loading = false; this.loading = false;
this.$success(this.$t('organization.integration.successful_operation')); this.$success(this.$t('organization.integration.successful_operation'));
@ -100,6 +122,69 @@
handleClose() { handleClose() {
this.ruleForm = {} this.ruleForm = {}
}, },
edit(param) {
param.projectId = getCurrentProjectID();
param.protocol = this.condition.protocol;
this.$post("/api/automation/module/edit", param, () => {
this.$success(this.$t('commons.save_success'));
this.initModule();
this.refresh();
}, (error) => {
this.initModule();
});
},
add(param) {
param.projectId = getCurrentProjectID();
param.protocol = this.condition.protocol;
this.$post("/api/automation/module/add", param, () => {
this.$success(this.$t('commons.save_success'));
this.initModule();
}, (error) => {
this.initModule();
});
},
remove(nodeIds) {
this.$post("/api/automation/module/delete", nodeIds, () => {
this.initModule();
this.refresh();
}, (error) => {
this.initModule();
});
},
drag(param, list) {
this.$post("/api/automation/module/drag", param, () => {
this.$post("/api/automation/module/pos", list, () => {
this.initModule();
});
}, (error) => {
this.initModule();
});
},
nodeChange(node, nodeIds, pNodes) {
this.currentModule = node.data;
this.condition.trashEnable = false;
if (node.data.id === 'root') {
this.$emit("nodeSelectEvent", node, [], pNodes);
} else {
this.$emit("nodeSelectEvent", node, nodeIds, pNodes);
}
},
saveAsEdit(data) {
this.$emit('saveAsEdit', data);
},
refresh() {
this.$emit("refreshTable");
},
addScenario() {
if (!getCurrentProjectID()) {
this.$warning(this.$t('commons.check_project_tip'));
return;
}
this.$refs.basisScenario.open(this.currentModule);
},
enableTrash() {
this.condition.trashEnable = true;
}
} }
} }
</script> </script>