diff --git a/backend/src/main/java/io/metersphere/service/CustomFunctionService.java b/backend/src/main/java/io/metersphere/service/CustomFunctionService.java
index 14d85f8a4d..195dc8635f 100644
--- a/backend/src/main/java/io/metersphere/service/CustomFunctionService.java
+++ b/backend/src/main/java/io/metersphere/service/CustomFunctionService.java
@@ -78,7 +78,14 @@ public class CustomFunctionService {
projectId = SessionUtils.getCurrentProjectId();
}
CustomFunctionExample example = new CustomFunctionExample();
- example.createCriteria().andProjectIdEqualTo(projectId);
+ CustomFunctionExample.Criteria criteria = example.createCriteria();
+ criteria.andProjectIdEqualTo(projectId);
+ if (StringUtils.isNotBlank(request.getType())) {
+ criteria.andTypeEqualTo(request.getType());
+ }
+ if (StringUtils.isNotBlank(request.getName())) {
+ criteria.andNameEqualTo(request.getName());
+ }
return customFunctionMapper.selectByExample(example);
}
diff --git a/backend/src/main/resources/db/migration/V95__v1.13_release.sql b/backend/src/main/resources/db/migration/V95__v1.13_release.sql
index 0072e4c252..57956864dd 100644
--- a/backend/src/main/resources/db/migration/V95__v1.13_release.sql
+++ b/backend/src/main/resources/db/migration/V95__v1.13_release.sql
@@ -45,3 +45,25 @@ ALTER TABLE test_plan_api_case ADD `order` bigint(20) NOT NULL COMMENT '自定
ALTER TABLE test_plan_api_scenario ADD `order` bigint(20) NOT NULL COMMENT '自定义排序,间隔5000';
ALTER TABLE test_plan_load_case ADD `order` bigint(20) NOT NULL COMMENT '自定义排序,间隔5000';
+
+create table if not exists custom_function
+(
+ id varchar(50) not null
+ primary key,
+ name varchar(255) null comment '函数名',
+ tags varchar(1000) null comment '标签',
+ description varchar(1000) null comment '函数描述',
+ type varchar(255) null comment '脚本语言类型',
+ params longtext null comment '参数列表',
+ script longtext null comment '函数体',
+ result longtext null comment '执行结果',
+ create_user varchar(100) null comment '创建人',
+ create_time bigint(13) null comment '创建时间',
+ update_time bigint(13) null comment '更新时间',
+ project_id varchar(50) null comment '所属项目ID'
+)
+ ENGINE = InnoDB
+ DEFAULT CHARSET = utf8mb4
+ COLLATE utf8mb4_general_ci;
+
+
diff --git a/frontend/src/business/components/api/automation/scenario/common/Jsr233ProcessorContent.vue b/frontend/src/business/components/api/automation/scenario/common/Jsr233ProcessorContent.vue
index 5e2190e26e..3389898756 100644
--- a/frontend/src/business/components/api/automation/scenario/common/Jsr233ProcessorContent.vue
+++ b/frontend/src/business/components/api/automation/scenario/common/Jsr233ProcessorContent.vue
@@ -14,10 +14,15 @@
{{ template.title }}
+
+ {{ funcLink.title }}
+
{{ $t('commons.reference_documentation') }}
+
+
@@ -26,9 +31,10 @@
diff --git a/frontend/src/business/components/settings/project/function/CustomFunction.vue b/frontend/src/business/components/settings/project/function/CustomFunction.vue
index a33e956e20..952454347b 100644
--- a/frontend/src/business/components/settings/project/function/CustomFunction.vue
+++ b/frontend/src/business/components/settings/project/function/CustomFunction.vue
@@ -110,9 +110,6 @@ export default {
if (item.tags && item.tags.length > 0) {
item.tags = JSON.parse(item.tags);
}
- if (item.params && item.params.length > 0) {
- item.params = JSON.parse(item.params);
- }
})
});
},
diff --git a/frontend/src/business/components/settings/project/function/CustomFunctionRelate.vue b/frontend/src/business/components/settings/project/function/CustomFunctionRelate.vue
new file mode 100644
index 0000000000..56fb7052c4
--- /dev/null
+++ b/frontend/src/business/components/settings/project/function/CustomFunctionRelate.vue
@@ -0,0 +1,137 @@
+
+
+
+
+
+
+
+
+
+
+ {{ scope.row.description }}
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ scope.row.createTime | timestampFormatDate }}
+
+
+
+
+
+
+
+ {{ $t('commons.cancel') }}
+
+ {{ $t('commons.confirm') }}
+
+
+
+
+
+
+
+
diff --git a/frontend/src/business/components/settings/project/function/EditFunction.vue b/frontend/src/business/components/settings/project/function/EditFunction.vue
index 1800cc9f1b..d1f8075478 100644
--- a/frontend/src/business/components/settings/project/function/EditFunction.vue
+++ b/frontend/src/business/components/settings/project/function/EditFunction.vue
@@ -28,11 +28,11 @@
-
-
-
-
-
+
+
+
+
+
@@ -42,7 +42,7 @@
{{ template.title }}
+
+ {{ funcLink.title }}
+
{{ $t('commons.reference_documentation') }}
@@ -77,6 +80,7 @@
+
{{ $t('commons.cancel') }}
@@ -92,15 +96,17 @@ import MsInputTag from "@/business/components/api/automation/scenario/MsInputTag
import FunctionParams from "@/business/components/settings/project/function/FunctionParams";
import MsCodeEdit from "@/business/components/common/components/MsCodeEdit";
import MsDropdown from "@/business/components/common/components/MsDropdown";
-import {splicingCustomFunc} from "@/business/components/settings/project/function/custom_function";
+import {FUNC_TEMPLATE, 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";
+import CustomFunctionRelate from "@/business/components/settings/project/function/CustomFunctionRelate";
export default {
name: "EditFunction",
components: {
+ CustomFunctionRelate,
FunctionRun,
MsCodeEdit,
FunctionParams,
@@ -139,7 +145,11 @@ export default {
},
modes: ['java', 'python'],
languages: [
- 'beanshell', "python", "groovy", "nashornScript", "rhinoScript"
+ 'beanshell',
+ "python",
+ "groovy",
+ "nashornScript",
+ "rhinoScript"
],
codeEditModeMap: {
beanshell: 'java',
@@ -199,48 +209,58 @@ export default {
' }\n' +
'}',
disabled: this.isPreProcessor
+ },
+ {
+ title: "终止测试",
+ value: 'ctx.getEngine().stopThreadNow(ctx.getThread().getThreadName())'
}
],
+ funcLinks: [
+ {
+ title: "插入自定义函数",
+ command: "custom_function",
+ index: "custom_function"
+ }
+
+ ],
response: {},
request: {},
debug: true,
console: "无执行结果"
}
},
- watch: {
- 'form.name'() {
- this.splicingFunc();
- },
- 'form.params': {
- handler() {
- this.splicingFunc();
- },
- deep: true
- }
- },
+ // watch: {
+ // 'form.name'() {
+ // this.splicingFunc();
+ // },
+ // 'form.params': {
+ // handler() {
+ // this.splicingFunc();
+ // },
+ // deep: true
+ // }
+ // },
methods: {
- _parseFuncParam(funcObj) {
- let params = undefined;
- if (funcObj.params) {
- params = funcObj.params.map(p => p.name);
- if (params.length > 0) {
- params = params.filter(p => {
- return p !== undefined
- });
- params.join(",\s");
- }
- }
- // todo 参数拼接问题 删除参数 逗号未去除
- return params;
- },
- splicingFunc() {
- let funcObj = this.form;
- let funcName = funcObj.name;
- let funcLanguage = this.form.type || "beanshell";
- let funcParams = this._parseFuncParam(funcObj);
- this.form.script = splicingCustomFunc(funcLanguage, this.form.script, funcName, funcParams);
- this.reloadCodeEdit();
- },
+ // _parseFuncParam(funcObj) {
+ // let params = undefined;
+ // if (funcObj.params) {
+ // params = funcObj.params.map(p => p.name);
+ // if (params.length > 0) {
+ // params = params.filter(p => {
+ // return p !== undefined
+ // });
+ // params.join(",\s");
+ // }
+ // }
+ // // todo 参数拼接问题 删除参数 逗号未去除
+ // return params;
+ // },
+ // splicingFunc() {
+ // let funcObj = this.form;
+ // let funcParams = this._parseFuncParam(funcObj);
+ // this.form.script = splicingCustomFunc(funcObj, funcParams);
+ // this.reloadCodeEdit();
+ // },
open(data) {
this.activeName = "code";
this.visible = true;
@@ -250,6 +270,8 @@ export default {
this.initFunc(data.id);
this.dialogTitle = this.dialogUpdateTitle;
} else {
+ this.form.script = FUNC_TEMPLATE[this.form.type];
+ this.reloadCodeEdit();
this.form.tags = [];
this.form.params = [{}];
this.dialogTitle = this.dialogCreateTitle;
@@ -263,11 +285,11 @@ export default {
} else {
this.form.tags = JSON.parse(this.form.tags);
}
- if (!this.form.params) {
- this.form.params = [];
- } else {
- this.form.params = JSON.parse(this.form.params);
- }
+ // if (!this.form.params) {
+ // this.form.params = [];
+ // } else {
+ // this.form.params = JSON.parse(this.form.params);
+ // }
this.reload();
})
},
@@ -281,7 +303,10 @@ export default {
},
languageChange(language) {
this.form.type = language;
- this.$emit("languageChange");
+ if (!this.form.script) {
+ this.form.script = FUNC_TEMPLATE[language];
+ this.reloadCodeEdit();
+ }
},
addTemplate(template) {
if (!this.form.script) {
@@ -293,6 +318,11 @@ export default {
}
this.reloadCodeEdit();
},
+ doFuncLink(funcLink) {
+ if (funcLink.command === 'custom_function') {
+ this.$refs.customFunctionRelate.open(this.form.type);
+ }
+ },
reload() {
this.isFormAlive = false;
this.$nextTick(() => {
@@ -337,8 +367,8 @@ export default {
this.console = "无执行结果";
this.reloadResult();
this.runResult.loading = true;
-
let jSR223Processor = new JSR223Processor({
+ scriptLanguage: this.form.type,
script: this.form.script
});
jSR223Processor.id = getUUID().substring(0, 8);
@@ -354,6 +384,10 @@ export default {
},
errorRefresh() {
this.runResult.loading = false;
+ },
+ addCustomFuncScript(script) {
+ this.form.script = this.form.script + '\n\n' + script;
+ this.reloadCodeEdit();
}
}
}
diff --git a/frontend/src/business/components/settings/project/function/custom_function.js b/frontend/src/business/components/settings/project/function/custom_function.js
index ee61e96da4..afc6f1b166 100644
--- a/frontend/src/business/components/settings/project/function/custom_function.js
+++ b/frontend/src/business/components/settings/project/function/custom_function.js
@@ -1,36 +1,49 @@
-export function splicingCustomFunc(funcLanguage, funcObjScript, funcName, funcParams) {
+export const FUNC_TEMPLATE = {
+ beanshell: "public static void test() {\n\n\n}",
+ groovy: "public static void test() {\n\n\n}",
+ python: "def test():\n",
+ nashornScript: "function test() {\n\n\n}",
+ rhinoScript: "function test() {\n\n\n}"
+}
+
+
+// 拼接函数
+export function splicingCustomFunc(funcObj, funcParams) {
+ let funcLanguage = funcObj.type || "beanshell";
+ let funcObjScript = funcObj.script;
+ let funcName = funcObj.name;
let funcFirstLine = generateFuncFirstLine(funcLanguage, funcName, funcParams);
- if (!funcObjScript) {
- funcObjScript = funcFirstLine + "\n\n\n}";
+ if (!funcObjScript && funcName) {
+ funcObjScript = funcLanguage === "python" ? funcFirstLine : funcFirstLine + "\n\n\n}";
+ }
+ if (funcObjScript) {
+ funcObjScript = funcObjScript.replace(regex[funcLanguage], funcFirstLine);
}
- funcObjScript = funcObjScript.replace(regex[funcLanguage], funcFirstLine);
return funcObjScript;
}
export function generateFuncFirstLine(funcLanguage, funcName, funcParams) {
- let funcFirstLine = "";
- switch (funcLanguage) {
- case "beanshell":
- funcFirstLine = "public static void " + funcName + "(" + funcParams + ") " + "{";
- break;
- case "python":
- break;
- case "groovy":
- break;
- case "nashornScript":
- break;
- case "rhinoScript":
- break;
- default:
- }
- return funcFirstLine;
+ let funcEnd = funcLanguage === "python" ? ":" : "{";
+ return scriptFuncDefinition[funcLanguage] + " " + funcName + "(" + funcParams + ") " + funcEnd;
}
+const scriptFuncDefinition = {
+ beanshell: "public static void",
+ python: "def",
+ groovy: "public static void",
+ // nashornScript: "",
+ // rhinoScript: "",
+}
+
+const firstFuncRegex = RegExp(".*\(.*\)\\s\{\\r?");
const regex = {
- beanshell: /^public static void\s.*\(.*\)\s\{/,
- python: /^function\s.*\(.*\)\s\{/,
- groovy: /^function\s.*\(.*\)\s\{/,
- nashornScript: /^function\s.*\(.*\)\s\{/,
- rhinoScript: /^function\s.*\(.*\)\s\{/,
+ beanshell: calcRegex(scriptFuncDefinition.beanshell),
+ python: RegExp("^def\\s.*\(.*\)\\s\:"),
+ groovy: calcRegex(scriptFuncDefinition.groovy),
+ // nashornScript: calcRegex(scriptFuncDefinition.nashornScript),
+ // rhinoScript: calcRegex(scriptFuncDefinition.rhinoScript),
}
+function calcRegex(str) {
+ return RegExp("^" + str + "\\s.*\(.*\)\\s\{");
+}