feat(系统设置): 自定义函数测试执行
This commit is contained in:
parent
4ab568610b
commit
2d7d360ec4
|
@ -2,6 +2,7 @@ package io.metersphere.controller;
|
||||||
|
|
||||||
import com.github.pagehelper.Page;
|
import com.github.pagehelper.Page;
|
||||||
import com.github.pagehelper.PageHelper;
|
import com.github.pagehelper.PageHelper;
|
||||||
|
import io.metersphere.api.dto.definition.RunDefinitionRequest;
|
||||||
import io.metersphere.base.domain.CustomFunction;
|
import io.metersphere.base.domain.CustomFunction;
|
||||||
import io.metersphere.base.domain.CustomFunctionWithBLOBs;
|
import io.metersphere.base.domain.CustomFunctionWithBLOBs;
|
||||||
import io.metersphere.commons.utils.PageUtils;
|
import io.metersphere.commons.utils.PageUtils;
|
||||||
|
@ -54,4 +55,9 @@ public class CustomFunctionController {
|
||||||
public CustomFunctionWithBLOBs get(@PathVariable String id) {
|
public CustomFunctionWithBLOBs get(@PathVariable String id) {
|
||||||
return customFunctionService.get(id);
|
return customFunctionService.get(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PostMapping("/run")
|
||||||
|
public String run(@RequestBody RunDefinitionRequest request) {
|
||||||
|
return customFunctionService.run(request);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,20 @@
|
||||||
package io.metersphere.service;
|
package io.metersphere.service;
|
||||||
|
|
||||||
|
|
||||||
|
import io.metersphere.api.dto.definition.RunDefinitionRequest;
|
||||||
|
import io.metersphere.api.dto.definition.request.ParameterConfig;
|
||||||
|
import io.metersphere.api.jmeter.JMeterService;
|
||||||
import io.metersphere.base.domain.CustomFunction;
|
import io.metersphere.base.domain.CustomFunction;
|
||||||
import io.metersphere.base.domain.CustomFunctionExample;
|
import io.metersphere.base.domain.CustomFunctionExample;
|
||||||
import io.metersphere.base.domain.CustomFunctionWithBLOBs;
|
import io.metersphere.base.domain.CustomFunctionWithBLOBs;
|
||||||
import io.metersphere.base.mapper.CustomFunctionMapper;
|
import io.metersphere.base.mapper.CustomFunctionMapper;
|
||||||
|
import io.metersphere.commons.constants.ApiRunMode;
|
||||||
import io.metersphere.commons.exception.MSException;
|
import io.metersphere.commons.exception.MSException;
|
||||||
import io.metersphere.commons.utils.BeanUtils;
|
import io.metersphere.commons.utils.BeanUtils;
|
||||||
import io.metersphere.commons.utils.SessionUtils;
|
import io.metersphere.commons.utils.SessionUtils;
|
||||||
import io.metersphere.controller.request.CustomFunctionRequest;
|
import io.metersphere.controller.request.CustomFunctionRequest;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.apache.jorphan.collections.HashTree;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
@ -28,6 +33,8 @@ public class CustomFunctionService {
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private CustomFunctionMapper customFunctionMapper;
|
private CustomFunctionMapper customFunctionMapper;
|
||||||
|
@Resource
|
||||||
|
private JMeterService jMeterService;
|
||||||
|
|
||||||
public CustomFunctionWithBLOBs save(CustomFunctionRequest request) {
|
public CustomFunctionWithBLOBs save(CustomFunctionRequest request) {
|
||||||
request.setId(UUID.randomUUID().toString());
|
request.setId(UUID.randomUUID().toString());
|
||||||
|
@ -106,4 +113,13 @@ public class CustomFunctionService {
|
||||||
}
|
}
|
||||||
return customFunctionMapper.selectByPrimaryKey(id);
|
return customFunctionMapper.selectByPrimaryKey(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String run(RunDefinitionRequest request) {
|
||||||
|
ParameterConfig config = new ParameterConfig();
|
||||||
|
config.setProjectId(request.getProjectId());
|
||||||
|
HashTree hashTree = request.getTestElement().generateHashTree(config);
|
||||||
|
String runMode = ApiRunMode.DEFINITION.name();
|
||||||
|
jMeterService.runLocal(request.getId(), hashTree, request.getReportId(), runMode);
|
||||||
|
return request.getId();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,25 +49,34 @@
|
||||||
ref="codeEdit"/>
|
ref="codeEdit"/>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
<el-tab-pane :label="'执行结果'" name="result">
|
<el-tab-pane :label="'执行结果'" name="result">
|
||||||
执行结果
|
<div v-loading="runResult.loading">
|
||||||
|
<ms-code-edit :mode="'text'" :data.sync="console" v-if="isResultAlive" height="330px" ref="funcResult"/>
|
||||||
|
</div>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
</el-tabs>
|
</el-tabs>
|
||||||
</template>
|
</template>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="4" class="script-index">
|
<el-col :span="4" class="script-index">
|
||||||
<ms-dropdown :default-command="form.type" :commands="languages" @command="languageChange"/>
|
<div style="margin-top: -25px; margin-left: 10px;">
|
||||||
<div class="template-title">{{ $t('api_test.request.processor.code_template') }}</div>
|
<div style="margin-bottom: 10px;">
|
||||||
<div v-for="(template, index) in codeTemplates" :key="index" class="code-template">
|
<el-button type="primary" size="mini" style="width: 70px;" @click="handleTest" :disabled="runResult.loading">测试</el-button>
|
||||||
<el-link :disabled="template.disabled" @click="addTemplate(template)">{{ template.title }}</el-link>
|
</div>
|
||||||
|
<ms-dropdown :default-command="form.type" :commands="languages" @command="languageChange"/>
|
||||||
|
<div class="template-title">{{ $t('api_test.request.processor.code_template') }}</div>
|
||||||
|
<div v-for="(template, index) in codeTemplates" :key="index" class="code-template">
|
||||||
|
<el-link :disabled="template.disabled" @click="addTemplate(template)">{{ template.title }}</el-link>
|
||||||
|
</div>
|
||||||
|
<el-link href="https://jmeter.apache.org/usermanual/component_reference.html#BeanShell_PostProcessor"
|
||||||
|
target="componentReferenceDoc" style="margin-top: 10px"
|
||||||
|
type="primary">{{ $t('commons.reference_documentation') }}
|
||||||
|
</el-link>
|
||||||
</div>
|
</div>
|
||||||
<el-link href="https://jmeter.apache.org/usermanual/component_reference.html#BeanShell_PostProcessor"
|
|
||||||
target="componentReferenceDoc" style="margin-top: 10px"
|
|
||||||
type="primary">{{ $t('commons.reference_documentation') }}
|
|
||||||
</el-link>
|
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
</el-form>
|
</el-form>
|
||||||
|
<!-- 执行组件 -->
|
||||||
|
<function-run :report-id="reportId" :run-data="runData" @runRefresh="runRefresh" @errorRefresh="errorRefresh"/>
|
||||||
</div>
|
</div>
|
||||||
<template v-slot:footer>
|
<template v-slot:footer>
|
||||||
<el-button @click="close" size="medium">{{ $t('commons.cancel') }}</el-button>
|
<el-button @click="close" size="medium">{{ $t('commons.cancel') }}</el-button>
|
||||||
|
@ -84,25 +93,38 @@ import FunctionParams from "@/business/components/settings/project/function/Func
|
||||||
import MsCodeEdit from "@/business/components/common/components/MsCodeEdit";
|
import MsCodeEdit from "@/business/components/common/components/MsCodeEdit";
|
||||||
import MsDropdown from "@/business/components/common/components/MsDropdown";
|
import MsDropdown from "@/business/components/common/components/MsDropdown";
|
||||||
import {splicingCustomFunc} from "@/business/components/settings/project/function/custom_function";
|
import {splicingCustomFunc} from "@/business/components/settings/project/function/custom_function";
|
||||||
|
import MsRun from "@/business/components/api/automation/scenario/DebugRun";
|
||||||
|
import {getUUID} from "@/common/js/utils";
|
||||||
|
import {JSR223Processor} from "@/business/components/api/definition/model/ApiTestModel";
|
||||||
|
import FunctionRun from "@/business/components/settings/project/function/FunctionRun";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "EditFunction",
|
name: "EditFunction",
|
||||||
components: {
|
components: {
|
||||||
|
FunctionRun,
|
||||||
MsCodeEdit,
|
MsCodeEdit,
|
||||||
FunctionParams,
|
FunctionParams,
|
||||||
MsInputTag,
|
MsInputTag,
|
||||||
MsDropdown
|
MsDropdown,
|
||||||
|
MsRun
|
||||||
},
|
},
|
||||||
props: {},
|
props: {},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
visible: false,
|
visible: false,
|
||||||
result: {},
|
result: {},
|
||||||
|
runResult: {
|
||||||
|
loading: false
|
||||||
|
},
|
||||||
|
reportId: "",
|
||||||
|
runData: [],
|
||||||
|
isStop: false,
|
||||||
dialogCreateTitle: "创建函数",
|
dialogCreateTitle: "创建函数",
|
||||||
dialogUpdateTitle: "更新函数",
|
dialogUpdateTitle: "更新函数",
|
||||||
activeName: 'code',
|
activeName: 'code',
|
||||||
dialogTitle: "",
|
dialogTitle: "",
|
||||||
isCodeEditAlive: true,
|
isCodeEditAlive: true,
|
||||||
|
isResultAlive: true,
|
||||||
isFormAlive: true,
|
isFormAlive: true,
|
||||||
form: {
|
form: {
|
||||||
params: [],
|
params: [],
|
||||||
|
@ -179,6 +201,10 @@ export default {
|
||||||
disabled: this.isPreProcessor
|
disabled: this.isPreProcessor
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
response: {},
|
||||||
|
request: {},
|
||||||
|
debug: true,
|
||||||
|
console: "无执行结果"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
|
@ -204,6 +230,7 @@ export default {
|
||||||
params.join(",\s");
|
params.join(",\s");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// todo 参数拼接问题 删除参数 逗号未去除
|
||||||
return params;
|
return params;
|
||||||
},
|
},
|
||||||
splicingFunc() {
|
splicingFunc() {
|
||||||
|
@ -215,6 +242,7 @@ export default {
|
||||||
this.reloadCodeEdit();
|
this.reloadCodeEdit();
|
||||||
},
|
},
|
||||||
open(data) {
|
open(data) {
|
||||||
|
this.activeName = "code";
|
||||||
this.visible = true;
|
this.visible = true;
|
||||||
this.form.type = "beanshell";
|
this.form.type = "beanshell";
|
||||||
if (data && data.id) {
|
if (data && data.id) {
|
||||||
|
@ -248,6 +276,7 @@ export default {
|
||||||
type: "beanshell",
|
type: "beanshell",
|
||||||
params: [{}]
|
params: [{}]
|
||||||
};
|
};
|
||||||
|
this.console = "无执行结果";
|
||||||
this.visible = false;
|
this.visible = false;
|
||||||
},
|
},
|
||||||
languageChange(language) {
|
languageChange(language) {
|
||||||
|
@ -274,6 +303,10 @@ export default {
|
||||||
this.isCodeEditAlive = false;
|
this.isCodeEditAlive = false;
|
||||||
this.$nextTick(() => (this.isCodeEditAlive = true));
|
this.$nextTick(() => (this.isCodeEditAlive = true));
|
||||||
},
|
},
|
||||||
|
reloadResult() {
|
||||||
|
this.isResultAlive = false;
|
||||||
|
this.$nextTick(() => (this.isResultAlive = true));
|
||||||
|
},
|
||||||
submit() {
|
submit() {
|
||||||
let param = Object.assign({}, this.form);
|
let param = Object.assign({}, this.form);
|
||||||
param.params = JSON.stringify(this.form.params);
|
param.params = JSON.stringify(this.form.params);
|
||||||
|
@ -298,6 +331,29 @@ export default {
|
||||||
this.$emit("refresh");
|
this.$emit("refresh");
|
||||||
this.$success(this.$t('commons.modify_success'));
|
this.$success(this.$t('commons.modify_success'));
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
handleTest() {
|
||||||
|
this.activeName = "result";
|
||||||
|
this.console = "无执行结果";
|
||||||
|
this.reloadResult();
|
||||||
|
this.runResult.loading = true;
|
||||||
|
|
||||||
|
let jSR223Processor = new JSR223Processor({
|
||||||
|
script: this.form.script
|
||||||
|
});
|
||||||
|
jSR223Processor.id = getUUID().substring(0, 8);
|
||||||
|
this.runData = [];
|
||||||
|
this.runData.push(jSR223Processor);
|
||||||
|
this.reportId = getUUID().substring(0, 8);
|
||||||
|
},
|
||||||
|
runRefresh(data) {
|
||||||
|
this.response = data;
|
||||||
|
this.console = this.response.responseResult.console;
|
||||||
|
this.runResult.loading = false;
|
||||||
|
this.reloadResult();
|
||||||
|
},
|
||||||
|
errorRefresh() {
|
||||||
|
this.runResult.loading = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -308,6 +364,7 @@ export default {
|
||||||
margin-bottom: 5px;
|
margin-bottom: 5px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
|
margin-top: 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 滚动条样式 */
|
/* 滚动条样式 */
|
||||||
|
|
|
@ -0,0 +1,101 @@
|
||||||
|
<template>
|
||||||
|
<span></span>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import {getCurrentProjectID, strMapToObj} from "@/common/js/utils";
|
||||||
|
import {TYPE_TO_C} from "@/business/components/api/automation/scenario/Setting";
|
||||||
|
import TestPlan from "@/business/components/api/definition/components/jmeter/components/test-plan";
|
||||||
|
import ThreadGroup from "@/business/components/api/definition/components/jmeter/components/thread-group";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'FunctionRun',
|
||||||
|
components: {},
|
||||||
|
props: {
|
||||||
|
environment: Object,
|
||||||
|
debug: Boolean,
|
||||||
|
reportId: String,
|
||||||
|
runData: Array,
|
||||||
|
type: String,
|
||||||
|
envMap: Map,
|
||||||
|
isStop: Boolean,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
result: {},
|
||||||
|
loading: false,
|
||||||
|
runId: "",
|
||||||
|
reqNumber: 0,
|
||||||
|
websocket: {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
watch: {
|
||||||
|
// 初始化
|
||||||
|
reportId() {
|
||||||
|
this.run()
|
||||||
|
},
|
||||||
|
isStop() {
|
||||||
|
if (!this.isStop && this.websocket && this.websocket.close instanceof Function) {
|
||||||
|
this.websocket.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
initWebSocket() {
|
||||||
|
let protocol = "ws://";
|
||||||
|
if (window.location.protocol === 'https:') {
|
||||||
|
protocol = "wss://";
|
||||||
|
}
|
||||||
|
const uri = protocol + window.location.host + "/api/definition/run/report/" + this.runId + "/debug";
|
||||||
|
this.websocket = new WebSocket(uri);
|
||||||
|
this.websocket.onmessage = this.onMessage;
|
||||||
|
},
|
||||||
|
onMessage(e) {
|
||||||
|
if (e.data) {
|
||||||
|
let data = JSON.parse(e.data);
|
||||||
|
this.websocket.close();
|
||||||
|
this.$emit('runRefresh', data);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
sort(stepArray) {
|
||||||
|
if (stepArray) {
|
||||||
|
for (let i in stepArray) {
|
||||||
|
if (!stepArray[i].clazzName) {
|
||||||
|
stepArray[i].clazzName = TYPE_TO_C.get(stepArray[i].type);
|
||||||
|
}
|
||||||
|
if (stepArray[i].hashTree && stepArray[i].hashTree.length > 0) {
|
||||||
|
this.sort(stepArray[i].hashTree);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
run() {
|
||||||
|
let projectId = getCurrentProjectID();
|
||||||
|
|
||||||
|
let testPlan = new TestPlan();
|
||||||
|
testPlan.clazzName = TYPE_TO_C.get(testPlan.type);
|
||||||
|
let threadGroup = new ThreadGroup();
|
||||||
|
threadGroup.clazzName = TYPE_TO_C.get(threadGroup.type);
|
||||||
|
threadGroup.hashTree = [];
|
||||||
|
testPlan.hashTree = [threadGroup];
|
||||||
|
this.runData.forEach(item => {
|
||||||
|
item.projectId = projectId;
|
||||||
|
if (!item.clazzName) {
|
||||||
|
item.clazzName = TYPE_TO_C.get(item.type);
|
||||||
|
}
|
||||||
|
threadGroup.hashTree.push(item);
|
||||||
|
})
|
||||||
|
this.sort(testPlan.hashTree);
|
||||||
|
let reqObj = {id: this.reportId, testElement: testPlan, type: this.type, clazzName: this.clazzName ? this.clazzName : TYPE_TO_C.get(this.type), projectId: projectId, environmentMap: strMapToObj(this.envMap)};
|
||||||
|
let url = "/custom/func/run";
|
||||||
|
reqObj.reportId = this.reportId;
|
||||||
|
this.$post(url, reqObj, res => {
|
||||||
|
this.runId = res.data;
|
||||||
|
this.initWebSocket();
|
||||||
|
}, () => {
|
||||||
|
this.$emit('errorRefresh', {});
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -11,7 +11,7 @@ export function generateFuncFirstLine(funcLanguage, funcName, funcParams) {
|
||||||
let funcFirstLine = "";
|
let funcFirstLine = "";
|
||||||
switch (funcLanguage) {
|
switch (funcLanguage) {
|
||||||
case "beanshell":
|
case "beanshell":
|
||||||
funcFirstLine = "function " + funcName + "(" + funcParams + ") " + "{";
|
funcFirstLine = "public static void " + funcName + "(" + funcParams + ") " + "{";
|
||||||
break;
|
break;
|
||||||
case "python":
|
case "python":
|
||||||
break;
|
break;
|
||||||
|
@ -27,7 +27,7 @@ export function generateFuncFirstLine(funcLanguage, funcName, funcParams) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const regex = {
|
const regex = {
|
||||||
beanshell: /^function\s.*\(.*\)\s\{/,
|
beanshell: /^public static void\s.*\(.*\)\s\{/,
|
||||||
python: /^function\s.*\(.*\)\s\{/,
|
python: /^function\s.*\(.*\)\s\{/,
|
||||||
groovy: /^function\s.*\(.*\)\s\{/,
|
groovy: /^function\s.*\(.*\)\s\{/,
|
||||||
nashornScript: /^function\s.*\(.*\)\s\{/,
|
nashornScript: /^function\s.*\(.*\)\s\{/,
|
||||||
|
|
Loading…
Reference in New Issue