diff --git a/backend/src/main/java/io/metersphere/api/controller/ApiAutomationController.java b/backend/src/main/java/io/metersphere/api/controller/ApiAutomationController.java index 02f882e070..19ec738175 100644 --- a/backend/src/main/java/io/metersphere/api/controller/ApiAutomationController.java +++ b/backend/src/main/java/io/metersphere/api/controller/ApiAutomationController.java @@ -2,8 +2,10 @@ package io.metersphere.api.controller; import com.github.pagehelper.Page; import com.github.pagehelper.PageHelper; +import io.metersphere.api.dto.ApiScenarioEnvRequest; import io.metersphere.api.dto.ApiTestImportRequest; import io.metersphere.api.dto.JmxInfoDTO; +import io.metersphere.api.dto.ScenarioEnv; import io.metersphere.api.dto.automation.*; import io.metersphere.api.dto.automation.parse.ScenarioImport; import io.metersphere.api.dto.definition.RunDefinitionRequest; @@ -99,6 +101,11 @@ public class ApiAutomationController { return apiAutomationService.getApiScenario(id); } + @PostMapping("/getApiScenarioEnv") + public ScenarioEnv getScenarioDefinition(@RequestBody ApiScenarioEnvRequest request) { + return apiAutomationService.getApiScenarioEnv(request.getDefinition()); + } + @PostMapping("/getApiScenarios") public List getApiScenarios(@RequestBody List ids) { return apiAutomationService.getApiScenarios(ids); diff --git a/backend/src/main/java/io/metersphere/api/dto/ApiScenarioEnvRequest.java b/backend/src/main/java/io/metersphere/api/dto/ApiScenarioEnvRequest.java new file mode 100644 index 0000000000..220870490b --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/ApiScenarioEnvRequest.java @@ -0,0 +1,11 @@ +package io.metersphere.api.dto; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class ApiScenarioEnvRequest { + + private String definition; +} diff --git a/backend/src/main/java/io/metersphere/api/dto/ScenarioEnv.java b/backend/src/main/java/io/metersphere/api/dto/ScenarioEnv.java new file mode 100644 index 0000000000..c5940c4862 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/ScenarioEnv.java @@ -0,0 +1,15 @@ +package io.metersphere.api.dto; + +import lombok.Getter; +import lombok.Setter; + +import java.util.HashSet; +import java.util.Set; + +@Getter +@Setter +public class ScenarioEnv { + + private Set projectIds = new HashSet<>(); + private Boolean fullUrl = true; +} diff --git a/backend/src/main/java/io/metersphere/api/service/ApiAutomationService.java b/backend/src/main/java/io/metersphere/api/service/ApiAutomationService.java index c4c71db6f1..d2e475ea11 100644 --- a/backend/src/main/java/io/metersphere/api/service/ApiAutomationService.java +++ b/backend/src/main/java/io/metersphere/api/service/ApiAutomationService.java @@ -4,18 +4,21 @@ import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONPath; +import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import io.metersphere.api.dto.ApiTestImportRequest; import io.metersphere.api.dto.DeleteAPIReportRequest; import io.metersphere.api.dto.JmxInfoDTO; +import io.metersphere.api.dto.ScenarioEnv; import io.metersphere.api.dto.automation.*; import io.metersphere.api.dto.automation.parse.ScenarioImport; import io.metersphere.api.dto.automation.parse.ScenarioImportParserFactory; import io.metersphere.api.dto.datacount.ApiDataCountResult; import io.metersphere.api.dto.definition.RunDefinitionRequest; import io.metersphere.api.dto.definition.request.*; +import io.metersphere.api.dto.definition.request.sampler.MsHTTPSamplerProxy; import io.metersphere.api.dto.definition.request.unknown.MsJmeterElement; import io.metersphere.api.dto.definition.request.variable.ScenarioVariable; import io.metersphere.api.dto.scenario.environment.EnvironmentConfig; @@ -93,6 +96,10 @@ public class ApiAutomationService { private TestPlanScenarioCaseService testPlanScenarioCaseService; @Resource private EsbApiParamService esbApiParamService; + @Resource + private ApiTestCaseService apiTestCaseService; + @Resource + private ApiDefinitionService apiDefinitionService; public List list(ApiScenarioRequest request) { request = this.initRequest(request, true, true); @@ -365,6 +372,144 @@ public class ApiAutomationService { return null; } + public ScenarioEnv getApiScenarioEnv(String definition) { + ObjectMapper mapper = new ObjectMapper(); + ScenarioEnv env = new ScenarioEnv(); + mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + try { + JSONObject element = JSON.parseObject(definition); + List hashTree = mapper.readValue(element.getString("hashTree"), new TypeReference>(){}); + for (int i = 0; i < hashTree.size(); i++) { + MsTestElement tr = hashTree.get(i); + String referenced = tr.getReferenced(); + if (StringUtils.equals(MsTestElementConstants.REF.name(), referenced)) { + if (StringUtils.equals(tr.getType(), "HTTPSamplerProxy")) { + MsHTTPSamplerProxy http = (MsHTTPSamplerProxy)tr; + String refType = tr.getRefType(); + if (StringUtils.equals(refType, "CASE")) { + http.setUrl(null); + } else { + ApiDefinition apiDefinition = apiDefinitionService.get(tr.getId()); + http.setUrl(apiDefinition.getPath()); + } + if (StringUtils.isBlank(http.getUrl()) || !isURL(http.getUrl())) { + env.getProjectIds().add(http.getProjectId()); + env.setFullUrl(false); + } + } else if (StringUtils.equals(tr.getType(), "TCPSampler")) { + if (StringUtils.equals(tr.getRefType(), "CASE")) { + ApiTestCaseWithBLOBs apiTestCaseWithBLOBs = apiTestCaseService.get(tr.getId()); + env.getProjectIds().add(apiTestCaseWithBLOBs.getProjectId()); + } else { + ApiDefinition apiDefinition = apiDefinitionService.get(tr.getId()); + env.getProjectIds().add(apiDefinition.getProjectId()); + } + } else if (StringUtils.equals(tr.getType(), "scenario")) { + ApiScenarioDTO apiScenario = getApiScenario(tr.getId()); + String scenarioDefinition = apiScenario.getScenarioDefinition(); + JSONObject element1 = JSON.parseObject(scenarioDefinition); + LinkedList hashTree1 = mapper.readValue(element1.getString("hashTree"), new TypeReference>(){}); + tr.setHashTree(hashTree1); + } + } else { + if (StringUtils.equals(tr.getType(), "HTTPSamplerProxy")) { + // 校验是否是全路径 + MsHTTPSamplerProxy httpSamplerProxy = (MsHTTPSamplerProxy)tr; + if (httpSamplerProxy.isEnable()) { + if (StringUtils.isBlank(httpSamplerProxy.getUrl()) || !isURL(httpSamplerProxy.getUrl())) { + env.getProjectIds().add(httpSamplerProxy.getProjectId()); + env.setFullUrl(false); + } + } + } else if (StringUtils.equals(tr.getType(), "TCPSampler")) { + env.getProjectIds().add(tr.getProjectId()); + } + } + if (CollectionUtils.isNotEmpty(tr.getHashTree())) { + getHashTree(tr.getHashTree(), env); + } + } + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + return env; + } + + private void getHashTree(List tree, ScenarioEnv env) { + try { + ObjectMapper mapper = new ObjectMapper(); + mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + for (int i = 0; i < tree.size(); i++) { + MsTestElement tr = tree.get(i); + String referenced = tr.getReferenced(); + if (StringUtils.equals(MsTestElementConstants.REF.name(), referenced)) { + if (StringUtils.equals(tr.getType(), "HTTPSamplerProxy")) { + MsHTTPSamplerProxy http = (MsHTTPSamplerProxy)tr; + String refType = tr.getRefType(); + if (StringUtils.equals(refType, "CASE")) { + http.setUrl(null); + } else { + ApiDefinition apiDefinition = apiDefinitionService.get(tr.getId()); + http.setUrl(apiDefinition.getPath()); + } + if (StringUtils.isBlank(http.getUrl()) || !this.isURL(http.getUrl())) { + env.setFullUrl(false); + env.getProjectIds().add(http.getProjectId()); + } + } else if (StringUtils.equals(tr.getType(), "TCPSampler")) { + if (StringUtils.equals(tr.getRefType(), "CASE")) { + ApiTestCaseWithBLOBs apiTestCaseWithBLOBs = apiTestCaseService.get(tr.getId()); + env.getProjectIds().add(apiTestCaseWithBLOBs.getProjectId()); + } else { + ApiDefinition apiDefinition = apiDefinitionService.get(tr.getId()); + env.getProjectIds().add(apiDefinition.getProjectId()); + } + } else if (StringUtils.equals(tr.getType(), "scenario")) { + ApiScenarioDTO apiScenario = getApiScenario(tr.getId()); + String scenarioDefinition = apiScenario.getScenarioDefinition(); + JSONObject element1 = JSON.parseObject(scenarioDefinition); + LinkedList hashTree1 = mapper.readValue(element1.getString("hashTree"), new TypeReference>(){}); + tr.setHashTree(hashTree1); + } + } else { + if (StringUtils.equals(tr.getType(), "HTTPSamplerProxy")) { + // 校验是否是全路径 + MsHTTPSamplerProxy httpSamplerProxy = (MsHTTPSamplerProxy)tr; + if (httpSamplerProxy.isEnable()) { + if (StringUtils.isBlank(httpSamplerProxy.getUrl()) || !isURL(httpSamplerProxy.getUrl())) { + env.setFullUrl(false); + env.getProjectIds().add(httpSamplerProxy.getProjectId()); + } + } + } else if (StringUtils.equals(tr.getType(), "TCPSampler")) { + env.getProjectIds().add(tr.getProjectId()); + } + } + if (CollectionUtils.isNotEmpty(tr.getHashTree())) { + getHashTree(tr.getHashTree(), env); + } + } + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + } + + private boolean isURL(String str) { + try { + String regex = "^((https|http|ftp|rtsp|mms)?://)" + + "?(([0-9a-z_!~*'().&=+$%-]+: )?[0-9a-z_!~*'().&=+$%-]+@)?" + + "(([0-9]{1,3}\\.){3}[0-9]{1,3}" + "|" + "([0-9a-z_!~*'()-]+\\.)*" + + "([0-9a-z][0-9a-z-]{0,61})?[0-9a-z]\\." + + "[a-z]{2,6})" + + "(:[0-9]{1,5})?" + + "((/?)|" + + "(/[0-9a-z_!~*'().;?:@&=+$,%#-]+)+/?)$"; + return str.matches(regex) || (str.matches("^(http|https|ftp)://.*$") && str.matches(".*://\\$\\{.*$")); + } catch (Exception e) { + return false; + } + } + public List getApiScenarios(List ids) { if (CollectionUtils.isNotEmpty(ids)) { return extApiScenarioMapper.selectIds(ids); diff --git a/frontend/src/business/components/api/automation/scenario/ApiCustomize.vue b/frontend/src/business/components/api/automation/scenario/ApiCustomize.vue index 8b3a3948e2..cfed851d5e 100644 --- a/frontend/src/business/components/api/automation/scenario/ApiCustomize.vue +++ b/frontend/src/business/components/api/automation/scenario/ApiCustomize.vue @@ -61,6 +61,7 @@ this.request.method = row.method; } this.request.resourceId = getUUID(); + this.request.projectId = this.$store.state.projectId; let obj = {}; Object.assign(obj, this.request); this.$emit('addCustomizeApi', obj); diff --git a/frontend/src/business/components/api/automation/scenario/EditApiScenario.vue b/frontend/src/business/components/api/automation/scenario/EditApiScenario.vue index 3dfae6b8f6..2e8e673dae 100644 --- a/frontend/src/business/components/api/automation/scenario/EditApiScenario.vue +++ b/frontend/src/business/components/api/automation/scenario/EditApiScenario.vue @@ -325,6 +325,7 @@ projectList: [], debugResult: new Map, drawer: false, + isFullUrl: true } }, created() { @@ -602,6 +603,11 @@ if (!arr[i].projectId) { // 如果自身没有ID并且场景有ID则赋值场景ID,否则赋值当前项目ID arr[i].projectId = scenarioProjectId ? scenarioProjectId : this.projectId; + } else { + const project = this.projectList.find(p => p.id === arr[i].projectId); + if (!project) { + arr[i].projectId = scenarioProjectId ? scenarioProjectId : this.projectId; + } } if (arr[i].hashTree != undefined && arr[i].hashTree.length > 0) { @@ -625,6 +631,11 @@ // 设置项目ID if (!this.scenarioDefinition[i].projectId) { this.scenarioDefinition[i].projectId = this.projectId; + } else { + const project = this.projectList.find(p => p.id === this.scenarioDefinition[i].projectId); + if (!project) { + this.scenarioDefinition[i].projectId = this.projectId; + } } if (this.scenarioDefinition[i].hashTree != undefined && this.scenarioDefinition[i].hashTree.length > 0) { @@ -647,7 +658,7 @@ this.customizeRequest = {}; this.sort(); this.reload(); - this.initProjectIds(); + // this.initProjectIds(); }, addScenario(arr) { if (arr && arr.length > 0) { @@ -670,7 +681,7 @@ this.isBtnHide = false; this.sort(); this.reload(); - this.initProjectIds(); + // this.initProjectIds(); }, setApiParameter(item, refType, referenced) { let request = {}; @@ -712,7 +723,7 @@ this.isBtnHide = false; this.sort(); this.reload(); - this.initProjectIds(); + // this.initProjectIds(); }, getMaintainerOptions() { let workspaceId = localStorage.getItem(WORKSPACE_ID); @@ -739,7 +750,7 @@ hashTree.splice(index, 1); this.sort(); this.reload(); - this.initProjectIds(); + // this.initProjectIds(); } } }); @@ -766,11 +777,14 @@ this.loading = true this.$nextTick(() => { this.loading = false - }) + }); + let definition = JSON.parse(JSON.stringify(this.currentScenario)); + definition.hashTree = this.scenarioDefinition; + this.getEnv(JSON.stringify(definition)); }, runDebug() { /*触发执行操作*/ - let sign = this.$refs.envPopover.checkEnv(this.scenarioDefinition); + let sign = this.$refs.envPopover.checkEnv(this.isFullUrl); if (!sign) { return; } @@ -961,6 +975,14 @@ }) }); }, + getEnv(definition) { + this.$post("/api/automation/getApiScenarioEnv", {definition: definition}, res => { + if (res.data) { + this.projectIds = new Set(res.data.projectIds); + this.isFullUrl = res.data.fullUrl; + } + }) + }, getApiScenario() { this.loading = true; if (this.currentScenario.tags != undefined && !(this.currentScenario.tags instanceof Array)) { @@ -977,6 +999,7 @@ if (response.data) { this.path = "/api/automation/update"; if (response.data.scenarioDefinition != null) { + this.getEnv(response.data.scenarioDefinition); let obj = JSON.parse(response.data.scenarioDefinition); if (obj) { this.currentEnvironmentId = obj.environmentId; @@ -1015,7 +1038,7 @@ } this.loading = false; this.sort(); - this.initProjectIds(); + // this.initProjectIds(); // this.getEnvironments(); }) } @@ -1085,18 +1108,18 @@ }) }, refReload() { - this.initProjectIds(); + // this.initProjectIds(); this.reload(); }, initProjectIds() { - // 加载环境配置 - this.$nextTick(() => { - this.projectIds.clear(); - this.scenarioDefinition.forEach(data => { - let arr = jsonPath.query(data, "$..projectId"); - arr.forEach(a => this.projectIds.add(a)); - }) - }) + // // 加载环境配置 + // this.$nextTick(() => { + // this.projectIds.clear(); + // this.scenarioDefinition.forEach(data => { + // let arr = jsonPath.query(data, "$..projectId"); + // arr.forEach(a => this.projectIds.add(a)); + // }) + // }) }, detailRefresh(result) { // 把执行结果分发给各个请求 diff --git a/frontend/src/business/components/api/automation/scenario/EnvSelect.vue b/frontend/src/business/components/api/automation/scenario/EnvSelect.vue index 27a4cb1918..1ea30c2a20 100644 --- a/frontend/src/business/components/api/automation/scenario/EnvSelect.vue +++ b/frontend/src/business/components/api/automation/scenario/EnvSelect.vue @@ -243,7 +243,9 @@ } }) } else { - sign = false; + if (!data) { + sign = false; + } } // 校验是否全是全路径 //this.checkFullUrl(data); diff --git a/frontend/src/business/components/api/automation/scenario/component/ApiComponent.vue b/frontend/src/business/components/api/automation/scenario/component/ApiComponent.vue index 22f83ec111..10ce245cec 100644 --- a/frontend/src/business/components/api/automation/scenario/component/ApiComponent.vue +++ b/frontend/src/business/components/api/automation/scenario/component/ApiComponent.vue @@ -258,9 +258,7 @@ this.request.id = response.data.id; this.request.disabled = true; this.request.root = true; - if (!this.request.projectId) { - this.request.projectId = response.data.projectId; - } + this.request.projectId = response.data.projectId; this.reload(); this.sort(); } else { diff --git a/frontend/src/business/components/api/automation/scenario/component/ApiScenarioComponent.vue b/frontend/src/business/components/api/automation/scenario/component/ApiScenarioComponent.vue index 1f2d95a6ba..8d2fc5e00b 100644 --- a/frontend/src/business/components/api/automation/scenario/component/ApiScenarioComponent.vue +++ b/frontend/src/business/components/api/automation/scenario/component/ApiScenarioComponent.vue @@ -68,14 +68,16 @@ obj = JSON.parse(response.data.scenarioDefinition); this.scenario.hashTree = obj.hashTree; } + this.scenario.projectId = response.data.projectId; + const pro = this.projectList.find(p => p.id === response.data.projectId); + if (!pro) { + this.scenario.projectId = this.$store.state.projectId; + } if (this.scenario.hashTree) { - this.setDisabled(this.scenario.hashTree); + this.setDisabled(this.scenario.hashTree, this.scenario.projectId); } //this.scenario.disabled = true; this.scenario.name = response.data.name; - if (!this.scenario.projectId) { - this.scenario.projectId = response.data.projectId; - } this.scenario.headers = obj.headers; this.scenario.variables = obj.variables; this.scenario.environmentMap = obj.environmentMap; @@ -123,25 +125,31 @@ this.loading = false }) }, - recursive(arr) { + recursive(arr, id) { for (let i in arr) { arr[i].disabled = true; - if (!arr[i].projectId) { - arr[i].projectId = getCurrentProjectID(); - } + arr[i].projectId = this.calcProjectId(arr[i].projectId, id); if (arr[i].hashTree != undefined && arr[i].hashTree.length > 0) { - this.recursive(arr[i].hashTree); + this.recursive(arr[i].hashTree, arr[i].projectId); } } }, - setDisabled(scenarioDefinition) { + setDisabled(scenarioDefinition, id) { for (let i in scenarioDefinition) { scenarioDefinition[i].disabled = true; - if (!scenarioDefinition[i].projectId) { - scenarioDefinition[i].projectId = getCurrentProjectID(); - } + scenarioDefinition[i].projectId = this.calcProjectId(scenarioDefinition[i].projectId, id); if (scenarioDefinition[i].hashTree != undefined && scenarioDefinition[i].hashTree.length > 0) { - this.recursive(scenarioDefinition[i].hashTree); + this.recursive(scenarioDefinition[i].hashTree, scenarioDefinition[i].projectId); + } + } + }, + calcProjectId(projectId, parentId) { + if (!projectId) { + return parentId ? parentId : this.$store.state.projectId; + } else { + const project = this.projectList.find(p => p.id === projectId); + if (!project) { + return parentId ? parentId : this.$store.state.projectId; } } },