From c74e88c3f35ded84389672565d2519e02146ec94 Mon Sep 17 00:00:00 2001 From: fit2-zhao Date: Tue, 2 Feb 2021 17:46:00 +0800 Subject: [PATCH] =?UTF-8?q?refactor(=E6=8E=A5=E5=8F=A3=E8=87=AA=E5=8A=A8?= =?UTF-8?q?=E5=8C=96):=20=E5=9C=BA=E6=99=AF=E5=AF=BC=E5=85=A5=20ms?= =?UTF-8?q?=EF=BC=8Cpostman=20=E5=9F=BA=E6=9C=AC=E5=AE=8C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ApiAutomationController.java | 17 +- .../dto/automation/parse/MsPostmanParser.java | 205 ++++++++++++++++++ .../automation/parse/MsScenarioParser.java | 65 ++++++ .../dto/automation/parse/ScenarioImport.java | 12 + .../parse/ScenarioImportAbstractParser.java | 169 +++++++++++++++ .../parse/ScenarioImportParser.java | 9 + .../parse/ScenarioImportParserFactory.java | 18 ++ .../request/variable/ScenarioVariable.java | 7 + .../api/service/ApiAutomationService.java | 122 ++++++++++- .../api/service/ApiScenarioModuleService.java | 27 +++ .../api/automation/ApiAutomation.vue | 38 +++- .../automation/scenario/ApiScenarioModule.vue | 1 + .../scenario/common/ScenarioImport.vue | 19 +- 13 files changed, 689 insertions(+), 20 deletions(-) create mode 100644 backend/src/main/java/io/metersphere/api/dto/automation/parse/MsPostmanParser.java create mode 100644 backend/src/main/java/io/metersphere/api/dto/automation/parse/MsScenarioParser.java create mode 100644 backend/src/main/java/io/metersphere/api/dto/automation/parse/ScenarioImport.java create mode 100644 backend/src/main/java/io/metersphere/api/dto/automation/parse/ScenarioImportAbstractParser.java create mode 100644 backend/src/main/java/io/metersphere/api/dto/automation/parse/ScenarioImportParser.java create mode 100644 backend/src/main/java/io/metersphere/api/dto/automation/parse/ScenarioImportParserFactory.java 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 c23f768f05..85b20fa4e4 100644 --- a/backend/src/main/java/io/metersphere/api/controller/ApiAutomationController.java +++ b/backend/src/main/java/io/metersphere/api/controller/ApiAutomationController.java @@ -2,14 +2,15 @@ package io.metersphere.api.controller; import com.github.pagehelper.Page; import com.github.pagehelper.PageHelper; +import io.metersphere.api.dto.ApiTestImportRequest; import io.metersphere.api.dto.JmxInfoDTO; import io.metersphere.api.dto.automation.*; +import io.metersphere.api.dto.automation.parse.ScenarioImport; import io.metersphere.api.dto.definition.RunDefinitionRequest; import io.metersphere.api.service.ApiAutomationService; import io.metersphere.base.domain.ApiScenario; import io.metersphere.base.domain.ApiScenarioWithBLOBs; import io.metersphere.base.domain.Schedule; -import io.metersphere.commons.constants.ApiRunMode; import io.metersphere.commons.constants.RoleConstants; import io.metersphere.commons.utils.PageUtils; import io.metersphere.commons.utils.Pager; @@ -43,6 +44,13 @@ public class ApiAutomationController { return PageUtils.setPageInfo(page, apiAutomationService.list(request)); } + @PostMapping("/list/all") + @RequiresRoles(value = {RoleConstants.TEST_MANAGER, RoleConstants.TEST_USER, RoleConstants.TEST_VIEWER}, logical = Logical.OR) + public List list(@RequestBody ApiScenarioRequest request) { + request.setWorkspaceId(SessionUtils.getCurrentWorkspaceId()); + return apiAutomationService.get(request); + } + @PostMapping(value = "/create") public ApiScenario create(@RequestPart("request") SaveApiScenarioRequest request, @RequestPart(value = "files") List bodyFiles) { return apiAutomationService.create(request, bodyFiles); @@ -146,5 +154,12 @@ public class ApiAutomationController { .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + fileOperationRequest.getName() + "\"") .body(bytes); } + + @PostMapping(value = "/import", consumes = {"multipart/form-data"}) + @RequiresRoles(value = {RoleConstants.TEST_USER, RoleConstants.TEST_MANAGER}, logical = Logical.OR) + public ScenarioImport scenarioImport(@RequestPart(value = "file", required = false) MultipartFile file, @RequestPart("request") ApiTestImportRequest request) { + return apiAutomationService.scenarioImport(file, request); + } + } diff --git a/backend/src/main/java/io/metersphere/api/dto/automation/parse/MsPostmanParser.java b/backend/src/main/java/io/metersphere/api/dto/automation/parse/MsPostmanParser.java new file mode 100644 index 0000000000..f88bfc339d --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/automation/parse/MsPostmanParser.java @@ -0,0 +1,205 @@ +package io.metersphere.api.dto.automation.parse; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import io.metersphere.api.dto.ApiTestImportRequest; +import io.metersphere.api.dto.definition.request.MsScenario; +import io.metersphere.api.dto.definition.request.MsTestElement; +import io.metersphere.api.dto.definition.request.processors.pre.MsJSR223PreProcessor; +import io.metersphere.api.dto.definition.request.sampler.MsHTTPSamplerProxy; +import io.metersphere.api.dto.definition.request.variable.ScenarioVariable; +import io.metersphere.api.dto.parse.postman.*; +import io.metersphere.api.dto.scenario.Body; +import io.metersphere.api.dto.scenario.KeyValue; +import io.metersphere.base.domain.ApiScenarioModule; +import io.metersphere.base.domain.ApiScenarioWithBLOBs; +import io.metersphere.commons.constants.MsRequestBodyType; +import io.metersphere.commons.constants.PostmanRequestBodyMode; +import io.metersphere.commons.constants.VariableTypeConstants; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.StringUtils; + +import java.io.InputStream; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.UUID; +import java.util.stream.Collectors; + +public class MsPostmanParser extends ScenarioImportAbstractParser { + + @Override + public ScenarioImport parse(InputStream source, ApiTestImportRequest request) { + String testStr = getApiTestStr(source); + PostmanCollection postmanCollection = JSON.parseObject(testStr, PostmanCollection.class); + List variables = postmanCollection.getVariable(); + ScenarioImport scenarioImport = new ScenarioImport(); + // 场景步骤 + LinkedList apiScenarioWithBLOBs = new LinkedList<>(); + PostmanCollectionInfo info = postmanCollection.getInfo(); + ApiScenarioWithBLOBs scenario = new ApiScenarioWithBLOBs(); + scenario.setName(info.getName()); + + MsScenario msScenario = new MsScenario(); + msScenario.setName(info.getName()); + this.projectId = request.getProjectId(); + parseItem(postmanCollection.getItem(), variables, msScenario, apiScenarioWithBLOBs); + // 生成场景对象 + List scenarioWithBLOBs = new LinkedList<>(); + paseScenario(scenarioWithBLOBs, msScenario, request); + scenarioImport.setData(scenarioWithBLOBs); + return scenarioImport; + } + + private void paseScenario(List scenarioWithBLOBsList, MsScenario msScenario, ApiTestImportRequest request) { + ApiScenarioModule module = buildModule(getSelectModule(request.getModuleId()), msScenario.getName()); + ApiScenarioWithBLOBs scenarioWithBLOBs = new ApiScenarioWithBLOBs(); + scenarioWithBLOBs.setName(msScenario.getName()); + scenarioWithBLOBs.setProjectId(request.getProjectId()); + if (msScenario != null && CollectionUtils.isNotEmpty(msScenario.getHashTree())) { + scenarioWithBLOBs.setStepTotal(msScenario.getHashTree().size()); + } + if (module != null) { + scenarioWithBLOBs.setApiScenarioModuleId(module.getId()); + scenarioWithBLOBs.setModulePath("/" + module.getName()); + } + scenarioWithBLOBs.setId(UUID.randomUUID().toString()); + scenarioWithBLOBs.setScenarioDefinition(JSON.toJSONString(msScenario)); + scenarioWithBLOBsList.add(scenarioWithBLOBs); + } + + private void parseItem(List items, List variables, MsScenario scenario, LinkedList results) { + for (PostmanItem item : items) { + List childItems = item.getItem(); + if (childItems != null) { + parseItem(childItems, variables, scenario, results); + } else { + MsHTTPSamplerProxy request = parsePostman(item); + if (request != null) { + results.add(request); + } + } + } + scenario.setVariables(parseScenarioVariable(variables)); + scenario.setHashTree(results); + } + + private MsHTTPSamplerProxy parsePostman(PostmanItem requestItem) { + PostmanRequest requestDesc = requestItem.getRequest(); + if (requestDesc == null) { + return null; + } + requestDesc.getAuth(); // todo 认证方式等待优化 + PostmanUrl url = requestDesc.getUrl(); + MsHTTPSamplerProxy request = buildRequest(requestItem.getName(), url.getRaw(), requestDesc.getMethod()); + if (StringUtils.isNotBlank(request.getPath())) { + String path = request.getPath().split("\\?")[0]; + path = path.replace("{{", "${"); + path = path.replace("}}", "}"); + request.setPath(path); + } else { + request.setPath("/"); + } + parseBody(request.getBody(), requestDesc); + request.setArguments(parseKeyValue(url.getQuery())); + request.setHeaders(parseKeyValue(requestDesc.getHeader())); + addBodyHeader(request); + addPreScript(request, requestItem.getEvent()); + return request; + } + + private void addPreScript(MsHTTPSamplerProxy request, List event) { + if (request != null && CollectionUtils.isNotEmpty(event)) { + StringBuilder scriptStr = new StringBuilder(); + event = event.stream() + .filter(item -> item.getScript() != null) + .collect(Collectors.toList()); + event.forEach(item -> { + PostmanScript script = item.getScript(); + if (script != null) { + List exec = script.getExec(); + if (CollectionUtils.isNotEmpty(exec)) { + exec.forEach(col -> { + if (StringUtils.isNotEmpty(col)) { + scriptStr.append(col + "/n"); + } + }); + } + } + }); + if (StringUtils.isNotBlank(scriptStr)) { + MsJSR223PreProcessor jsr223PreProcessor = new MsJSR223PreProcessor(); + jsr223PreProcessor.setName("JSR223PreProcessor"); + jsr223PreProcessor.setScriptLanguage("javascript"); + jsr223PreProcessor.setScript(scriptStr.toString()); + LinkedList hashTree = new LinkedList<>(); + hashTree.add(jsr223PreProcessor); + request.setHashTree(hashTree); + } + } + } + + private List parseKeyValue(List postmanKeyValues) { + if (postmanKeyValues == null) { + return null; + } + List keyValues = new ArrayList<>(); + postmanKeyValues.forEach(item -> keyValues.add(new KeyValue(item.getKey(), item.getValue(), item.getDescription(), item.getContentType()))); + return keyValues; + } + + private List parseScenarioVariable(List postmanKeyValues) { + if (postmanKeyValues == null) { + return null; + } + List keyValues = new ArrayList<>(); + postmanKeyValues.forEach(item -> keyValues.add(new ScenarioVariable(item.getKey(), item.getValue(), item.getDescription(), VariableTypeConstants.CONSTANT.name()))); + return keyValues; + } + + private void parseBody(Body body, PostmanRequest requestDesc) { + JSONObject postmanBody = requestDesc.getBody(); + if (postmanBody == null) { + return; + } + String bodyMode = postmanBody.getString("mode"); + if (StringUtils.isNotEmpty(bodyMode) && StringUtils.equals(bodyMode, PostmanRequestBodyMode.RAW.value())) { + parseRawBody(body, postmanBody, bodyMode); + } else if (StringUtils.isNotEmpty(bodyMode) && StringUtils.equalsAny(bodyMode, PostmanRequestBodyMode.FORM_DATA.value(), PostmanRequestBodyMode.URLENCODED.value())) { + List postmanKeyValues = JSON.parseArray(postmanBody.getString(bodyMode), PostmanKeyValue.class); + body.setKvs(parseKeyValue(postmanKeyValues)); + if (StringUtils.equals(bodyMode, PostmanRequestBodyMode.FORM_DATA.value())) { + body.setType(Body.FORM_DATA); + } else if (StringUtils.equals(bodyMode, PostmanRequestBodyMode.URLENCODED.value())) { + body.setType(Body.WWW_FROM); + } + } else if (StringUtils.equals(bodyMode, PostmanRequestBodyMode.FILE.value())) { + body.setType(Body.BINARY); + body.setKvs(new ArrayList<>()); + } + } + + private void parseRawBody(Body body, JSONObject postmanBody, String bodyMode) { + body.setRaw(postmanBody.getString(bodyMode)); + body.setType(MsRequestBodyType.RAW.value()); + JSONObject options = postmanBody.getJSONObject("options"); + if (options != null) { + JSONObject raw = options.getJSONObject(PostmanRequestBodyMode.RAW.value()); + if (raw != null && raw.getString("language") != null) { + String bodyType = ""; + switch (raw.getString("language")) { + case "json": + bodyType = Body.JSON; + break; + case "xml": + bodyType = Body.XML; + break; + default: + bodyType = Body.RAW; + } + body.setType(bodyType); + } + } + } + +} diff --git a/backend/src/main/java/io/metersphere/api/dto/automation/parse/MsScenarioParser.java b/backend/src/main/java/io/metersphere/api/dto/automation/parse/MsScenarioParser.java new file mode 100644 index 0000000000..b3b5104971 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/automation/parse/MsScenarioParser.java @@ -0,0 +1,65 @@ +package io.metersphere.api.dto.automation.parse; + +import com.alibaba.fastjson.JSON; +import io.metersphere.api.dto.ApiTestImportRequest; +import io.metersphere.base.domain.ApiScenarioModule; +import io.metersphere.base.domain.ApiScenarioWithBLOBs; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.StringUtils; + +import java.io.InputStream; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.UUID; + +public class MsScenarioParser extends ScenarioImportAbstractParser { + + @Override + public ScenarioImport parse(InputStream source, ApiTestImportRequest request) { + String testStr = getApiTestStr(source); + this.projectId = request.getProjectId(); + ScenarioImport scenarioImport = parseMsFormat(testStr, request); + return scenarioImport; + } + + private ScenarioImport parseMsFormat(String testStr, ApiTestImportRequest importRequest) { + ScenarioImport apiDefinitionImport = JSON.parseObject(testStr, ScenarioImport.class); + List data = apiDefinitionImport.getData(); + if (CollectionUtils.isNotEmpty(data)) { + data.forEach(item -> { + if (StringUtils.isBlank(item.getModulePath())) { + item.setApiScenarioModuleId(null); + } + parseModule(item, importRequest); + item.setId(UUID.randomUUID().toString()); + item.setProjectId(this.projectId); + }); + } + return apiDefinitionImport; + } + + private void parseModule(ApiScenarioWithBLOBs apiDefinition, ApiTestImportRequest importRequest) { + String modulePath = apiDefinition.getModulePath(); + if (StringUtils.isEmpty(modulePath)) { + return; + } + if (modulePath.startsWith("/")) { + modulePath = modulePath.substring(1, modulePath.length()); + } + if (modulePath.endsWith("/")) { + modulePath = modulePath.substring(0, modulePath.length() - 1); + } + List modules = Arrays.asList(modulePath.split("/")); + ApiScenarioModule parent = getSelectModule(importRequest.getModuleId()); + Iterator iterator = modules.iterator(); + while (iterator.hasNext()) { + String item = iterator.next(); + parent = buildModule(parent, item); + if (!iterator.hasNext()) { + apiDefinition.setApiScenarioModuleId(parent.getId()); + } + } + } + +} diff --git a/backend/src/main/java/io/metersphere/api/dto/automation/parse/ScenarioImport.java b/backend/src/main/java/io/metersphere/api/dto/automation/parse/ScenarioImport.java new file mode 100644 index 0000000000..81b6c08139 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/automation/parse/ScenarioImport.java @@ -0,0 +1,12 @@ +package io.metersphere.api.dto.automation.parse; + +import io.metersphere.base.domain.ApiScenarioWithBLOBs; +import lombok.Data; + +import java.util.List; + +@Data +public class ScenarioImport { + private String projectid; + private List data; +} diff --git a/backend/src/main/java/io/metersphere/api/dto/automation/parse/ScenarioImportAbstractParser.java b/backend/src/main/java/io/metersphere/api/dto/automation/parse/ScenarioImportAbstractParser.java new file mode 100644 index 0000000000..3ba5bc40ac --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/automation/parse/ScenarioImportAbstractParser.java @@ -0,0 +1,169 @@ +package io.metersphere.api.dto.automation.parse; + +import io.metersphere.api.dto.automation.ApiScenarioModuleDTO; +import io.metersphere.api.dto.definition.request.sampler.MsHTTPSamplerProxy; +import io.metersphere.api.dto.scenario.Body; +import io.metersphere.api.dto.scenario.KeyValue; +import io.metersphere.api.dto.scenario.request.RequestType; +import io.metersphere.api.service.ApiScenarioModuleService; +import io.metersphere.base.domain.ApiScenarioModule; +import io.metersphere.commons.exception.MSException; +import io.metersphere.commons.utils.BeanUtils; +import io.metersphere.commons.utils.CommonBeanFactory; +import io.metersphere.commons.utils.LogUtil; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.StringUtils; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +public abstract class ScenarioImportAbstractParser implements ScenarioImportParser { + protected ApiScenarioModuleService apiModuleService; + protected String projectId; + + protected ApiScenarioModule getSelectModule(String moduleId) { + apiModuleService = CommonBeanFactory.getBean(ApiScenarioModuleService.class); + if (StringUtils.isNotBlank(moduleId) && !StringUtils.equals("root", moduleId)) { + ApiScenarioModule module = new ApiScenarioModule(); + ApiScenarioModuleDTO moduleDTO = apiModuleService.getNode(moduleId); + if (moduleDTO != null) { + BeanUtils.copyBean(module, moduleDTO); + } + return module; + } + return null; + } + + protected ApiScenarioModule buildModule(ApiScenarioModule parentModule, String name) { + apiModuleService = CommonBeanFactory.getBean(ApiScenarioModuleService.class); + ApiScenarioModule module; + if (parentModule != null) { + module = apiModuleService.getNewModule(name, this.projectId, parentModule.getLevel() + 1); + module.setParentId(parentModule.getId()); + } else { + module = apiModuleService.getNewModule(name, this.projectId, 1); + } + createModule(module); + return module; + } + + protected void createModule(ApiScenarioModule module) { + List apiModules = apiModuleService.selectSameModule(module); + if (CollectionUtils.isEmpty(apiModules)) { + apiModuleService.addNode(module); + } else { + module.setId(apiModules.get(0).getId()); + } + } + + protected String getApiTestStr(InputStream source) { + StringBuilder testStr = null; + try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(source, StandardCharsets.UTF_8))) { + testStr = new StringBuilder(); + String inputStr; + while ((inputStr = bufferedReader.readLine()) != null) { + testStr.append(inputStr); + } + } catch (Exception e) { + MSException.throwException(e.getMessage()); + LogUtil.error(e.getMessage(), e); + } finally { + try { + if (source != null) { + source.close(); + } + } catch (IOException e) { + MSException.throwException(e.getMessage()); + LogUtil.error(e.getMessage(), e); + } + } + return testStr.toString(); + } + + private String formatPath(String url) { + try { + URL urlObject = new URL(url); + StringBuilder pathBuffer = new StringBuilder(urlObject.getPath()); + if (StringUtils.isNotEmpty(urlObject.getQuery())) { + pathBuffer.append("?").append(urlObject.getQuery()); + } + return pathBuffer.toString(); + } catch (Exception ex) { + return url; + } + } + + protected MsHTTPSamplerProxy buildRequest(String name, String path, String method) { + MsHTTPSamplerProxy request = new MsHTTPSamplerProxy(); + request.setName(name); + // 路径去掉域名/IP 地址,保留方法名称及参数 + request.setPath(formatPath(path)); + request.setMethod(method); + request.setProtocol(RequestType.HTTP); + request.setId(UUID.randomUUID().toString()); + request.setHeaders(new ArrayList<>()); + request.setArguments(new ArrayList<>()); + request.setRest(new ArrayList<>()); + request.setAuthManager(null); + Body body = new Body(); + body.initKvs(); + body.initBinary(); + request.setBody(body); + return request; + } + + protected void addHeader(List headers, String key, String value, String description, String contentType, boolean required) { + boolean hasContentType = false; + for (KeyValue header : headers) { + if (StringUtils.equalsIgnoreCase(header.getName(), key)) { + hasContentType = true; + } + } + if (!hasContentType) { + headers.add(new KeyValue(key, value, description, contentType, required)); + } + } + + protected void addHeader(List headers, String key, String value) { + addHeader(headers, key, value, "", "", true); + } + + protected void addContentType(List headers, String contentType) { + addHeader(headers, "Content-Type", contentType); + } + + protected void addBodyHeader(MsHTTPSamplerProxy request) { + String contentType = ""; + if (request.getBody() != null && StringUtils.isNotBlank(request.getBody().getType())) { + switch (request.getBody().getType()) { + case Body.JSON: + contentType = "application/json"; + break; + case Body.WWW_FROM: + contentType = "application/x-www-form-urlencoded"; + break; + case Body.XML: + contentType = "application/xml"; + break; + case Body.BINARY: + contentType = "application/octet-stream"; + break; + } + List headers = request.getHeaders(); + if (headers == null) { + headers = new ArrayList<>(); + request.setHeaders(headers); + } + addContentType(request.getHeaders(), contentType); + + } + } + +} diff --git a/backend/src/main/java/io/metersphere/api/dto/automation/parse/ScenarioImportParser.java b/backend/src/main/java/io/metersphere/api/dto/automation/parse/ScenarioImportParser.java new file mode 100644 index 0000000000..4586440473 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/automation/parse/ScenarioImportParser.java @@ -0,0 +1,9 @@ +package io.metersphere.api.dto.automation.parse; + +import io.metersphere.api.dto.ApiTestImportRequest; + +import java.io.InputStream; + +public interface ScenarioImportParser { + ScenarioImport parse(InputStream source, ApiTestImportRequest request); +} diff --git a/backend/src/main/java/io/metersphere/api/dto/automation/parse/ScenarioImportParserFactory.java b/backend/src/main/java/io/metersphere/api/dto/automation/parse/ScenarioImportParserFactory.java new file mode 100644 index 0000000000..f425fd1dd0 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/automation/parse/ScenarioImportParserFactory.java @@ -0,0 +1,18 @@ +package io.metersphere.api.dto.automation.parse; + +import io.metersphere.commons.constants.ApiImportPlatform; +import org.apache.commons.lang3.StringUtils; + +public class ScenarioImportParserFactory { + public static ScenarioImportParser getImportParser(String platform) { + if (StringUtils.equals(ApiImportPlatform.Metersphere.name(), platform)) { + return new MsScenarioParser(); + } else if (StringUtils.equals(ApiImportPlatform.Postman.name(), platform)) { + return new MsPostmanParser(); + } +// else if (StringUtils.equals(ApiImportPlatform.Swagger2.name(), platform)) { +// return new Swagger2Parser(); +// } + return null; + } +} diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/request/variable/ScenarioVariable.java b/backend/src/main/java/io/metersphere/api/dto/definition/request/variable/ScenarioVariable.java index 417711dfa2..9333699bc6 100644 --- a/backend/src/main/java/io/metersphere/api/dto/definition/request/variable/ScenarioVariable.java +++ b/backend/src/main/java/io/metersphere/api/dto/definition/request/variable/ScenarioVariable.java @@ -40,6 +40,13 @@ public class ScenarioVariable { private String minNumber; private String maxNumber; + public ScenarioVariable(String key, String value, String description, String type) { + this.name = key; + this.value = value; + this.description = description; + this.type = type; + } + public boolean isConstantValid() { if (StringUtils.equals(this.type, VariableTypeConstants.CONSTANT.name()) && StringUtils.isNotEmpty(name) && StringUtils.isNotEmpty(value)) { return 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 7f15aff6e6..f9d1779d31 100644 --- a/backend/src/main/java/io/metersphere/api/service/ApiAutomationService.java +++ b/backend/src/main/java/io/metersphere/api/service/ApiAutomationService.java @@ -5,9 +5,13 @@ import com.alibaba.fastjson.JSONObject; 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.automation.*; +import io.metersphere.api.dto.automation.parse.ScenarioImport; +import io.metersphere.api.dto.automation.parse.ScenarioImportParser; +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.*; @@ -86,23 +90,34 @@ public class ApiAutomationService { private TestPlanScenarioCaseService testPlanScenarioCaseService; public List list(ApiScenarioRequest request) { - request = this.initRequest(request,true,true); + request = this.initRequest(request, true, true); List list = extApiScenarioMapper.list(request); return list; } + public List get(ApiScenarioRequest request) { + ApiScenarioExample example = new ApiScenarioExample(); + if (CollectionUtils.isNotEmpty(request.getIds())) { + example.createCriteria().andIdIn(request.getIds()); + } else { + example.createCriteria().andProjectIdEqualTo(request.getProjectId()); + } + return apiScenarioMapper.selectByExampleWithBLOBs(example); + } + /** * 初始化部分参数 + * * @param request * @param setDefultOrders * @param checkThisWeekData * @return */ - private ApiScenarioRequest initRequest(ApiScenarioRequest request,boolean setDefultOrders, boolean checkThisWeekData) { - if(setDefultOrders){ + private ApiScenarioRequest initRequest(ApiScenarioRequest request, boolean setDefultOrders, boolean checkThisWeekData) { + if (setDefultOrders) { request.setOrders(ServiceUtils.getDefaultOrder(request.getOrders())); } - if(checkThisWeekData){ + if (checkThisWeekData) { if (request.isSelectThisWeedData()) { Map weekFirstTimeAndLastTime = DateUtils.getWeedFirstTimeAndLastTime(new Date()); Date weekFirstTime = weekFirstTimeAndLastTime.get("firstTime"); @@ -217,7 +232,7 @@ public class ApiAutomationService { ids.add(scenarioId); deleteApiScenarioReport(ids); - scheduleService.deleteScheduleAndJobByResourceId(scenarioId,ScheduleGroup.API_SCENARIO_TEST.name()); + scheduleService.deleteScheduleAndJobByResourceId(scenarioId, ScheduleGroup.API_SCENARIO_TEST.name()); TestPlanApiScenarioExample example = new TestPlanApiScenarioExample(); example.createCriteria().andApiScenarioIdEqualTo(scenarioId); List testPlanApiScenarioList = testPlanApiScenarioMapper.selectByExample(example); @@ -284,7 +299,7 @@ public class ApiAutomationService { extApiScenarioMapper.removeToGc(apiIds); //将这些场景的定时任务删除掉 for (String id : apiIds) { - scheduleService.deleteScheduleAndJobByResourceId(id,ScheduleGroup.API_SCENARIO_TEST.name()); + scheduleService.deleteScheduleAndJobByResourceId(id, ScheduleGroup.API_SCENARIO_TEST.name()); } } @@ -412,14 +427,14 @@ public class ApiAutomationService { // 创建场景报告 if (reportIds != null) { //如果是测试计划页面触发的执行方式,生成报告时createScenarioReport第二个参数需要特殊处理 - if(StringUtils.equals(request.getRunMode(),ApiRunMode.SCENARIO_PLAN.name())){ + if (StringUtils.equals(request.getRunMode(), ApiRunMode.SCENARIO_PLAN.name())) { String testPlanScenarioId = item.getId(); - if(request.getScenarioTestPlanIdMap()!=null&&request.getScenarioTestPlanIdMap().containsKey(item.getId())){ + if (request.getScenarioTestPlanIdMap() != null && request.getScenarioTestPlanIdMap().containsKey(item.getId())) { testPlanScenarioId = request.getScenarioTestPlanIdMap().get(item.getId()); } createScenarioReport(group.getName(), testPlanScenarioId, item.getName(), request.getTriggerMode() == null ? ReportTriggerMode.MANUAL.name() : request.getTriggerMode(), request.getExecuteType(), item.getProjectId(), request.getReportUserID()); - }else{ + } else { createScenarioReport(group.getName(), item.getId(), item.getName(), request.getTriggerMode() == null ? ReportTriggerMode.MANUAL.name() : request.getTriggerMode(), request.getExecuteType(), item.getProjectId(), request.getReportUserID()); } @@ -635,10 +650,10 @@ public class ApiAutomationService { } private void addOrUpdateApiScenarioCronJob(Schedule request) { - if(StringUtils.equals(request.getGroup(),ScheduleGroup.TEST_PLAN_TEST.name())){ + if (StringUtils.equals(request.getGroup(), ScheduleGroup.TEST_PLAN_TEST.name())) { scheduleService.addOrUpdateCronJob( request, TestPlanTestJob.getJobKey(request.getResourceId()), TestPlanTestJob.getTriggerKey(request.getResourceId()), TestPlanTestJob.class); - }else{ + } else { scheduleService.addOrUpdateCronJob( request, ApiScenarioTestJob.getJobKey(request.getResourceId()), ApiScenarioTestJob.getTriggerKey(request.getResourceId()), ApiScenarioTestJob.class); } @@ -712,4 +727,89 @@ public class ApiAutomationService { }); } } + + public List getWithBLOBs(ApiScenarioWithBLOBs request) { + ApiScenarioExample example = new ApiScenarioExample(); + example.createCriteria().andNameEqualTo(request.getName()).andProjectIdEqualTo(request.getProjectId()).andStatusNotEqualTo("Trash").andIdNotEqualTo(request.getId()); + return apiScenarioMapper.selectByExampleWithBLOBs(example); + } + + private void _importCreate(List sameRequest, ApiScenarioMapper batchMapper, ApiScenarioWithBLOBs scenarioWithBLOBs, ApiTestImportRequest apiTestImportRequest) { + if (CollectionUtils.isEmpty(sameRequest)) { + scenarioWithBLOBs.setId(UUID.randomUUID().toString()); + batchMapper.insert(scenarioWithBLOBs); + } else { + //如果存在则修改 + scenarioWithBLOBs.setId(sameRequest.get(0).getId()); + batchMapper.updateByPrimaryKeyWithBLOBs(scenarioWithBLOBs); + } + } + + private ApiScenarioWithBLOBs importCreate(ApiScenarioWithBLOBs request, ApiScenarioMapper batchMapper, ApiTestImportRequest apiTestImportRequest) { + final ApiScenarioWithBLOBs scenarioWithBLOBs = new ApiScenarioWithBLOBs(); + BeanUtils.copyBean(scenarioWithBLOBs, request); + scenarioWithBLOBs.setCreateTime(System.currentTimeMillis()); + scenarioWithBLOBs.setUpdateTime(System.currentTimeMillis()); + scenarioWithBLOBs.setStatus(APITestStatus.Underway.name()); + scenarioWithBLOBs.setProjectId(apiTestImportRequest.getProjectId()); + if (StringUtils.isEmpty(request.getPrincipal())) { + scenarioWithBLOBs.setPrincipal(Objects.requireNonNull(SessionUtils.getUser()).getId()); + } + if (request.getUserId() == null) { + scenarioWithBLOBs.setUserId(Objects.requireNonNull(SessionUtils.getUser()).getId()); + } else { + scenarioWithBLOBs.setUserId(request.getUserId()); + } + scenarioWithBLOBs.setDescription(request.getDescription()); + + List sameRequest = getWithBLOBs(scenarioWithBLOBs); + if (StringUtils.equals("fullCoverage", apiTestImportRequest.getModeId())) { + _importCreate(sameRequest, batchMapper, scenarioWithBLOBs, apiTestImportRequest); + } else if (StringUtils.equals("incrementalMerge", apiTestImportRequest.getModeId())) { + if (CollectionUtils.isEmpty(sameRequest)) { + batchMapper.insert(scenarioWithBLOBs); + } + + } else { + _importCreate(sameRequest, batchMapper, scenarioWithBLOBs, apiTestImportRequest); + } + return scenarioWithBLOBs; + } + + private void editScenario(ApiTestImportRequest request, ScenarioImport apiImport) { + SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH); + ApiScenarioMapper batchMapper = sqlSession.getMapper(ApiScenarioMapper.class); + List data = apiImport.getData(); + int num = 0; + if (!CollectionUtils.isEmpty(data) && data.get(0) != null && data.get(0).getProjectId() != null) { + num = getNextNum(data.get(0).getProjectId()); + } + for (int i = 0; i < data.size(); i++) { + ApiScenarioWithBLOBs item = data.get(i); + if (item.getName().length() > 255) { + item.setName(item.getName().substring(0, 255)); + } + item.setNum(num++); + importCreate(item, batchMapper, request); + if (i % 300 == 0) { + sqlSession.flushStatements(); + } + } + sqlSession.flushStatements(); + } + + public ScenarioImport scenarioImport(MultipartFile file, ApiTestImportRequest request) { + ScenarioImportParser apiImportParser = ScenarioImportParserFactory.getImportParser(request.getPlatform()); + ScenarioImport apiImport = null; + try { + apiImport = Objects.requireNonNull(apiImportParser).parse(file == null ? null : file.getInputStream(), request); + } catch (Exception e) { + LogUtil.error(e.getMessage(), e); + MSException.throwException(Translator.get("parse_data_error")); + } + if (apiImport != null) { + editScenario(request, apiImport); + } + return apiImport; + } } diff --git a/backend/src/main/java/io/metersphere/api/service/ApiScenarioModuleService.java b/backend/src/main/java/io/metersphere/api/service/ApiScenarioModuleService.java index 6445f7d13a..6f4ffcb369 100644 --- a/backend/src/main/java/io/metersphere/api/service/ApiScenarioModuleService.java +++ b/backend/src/main/java/io/metersphere/api/service/ApiScenarioModuleService.java @@ -215,6 +215,33 @@ public class ApiScenarioModuleService extends NodeTreeService selectSameModule(ApiScenarioModule node) { + ApiScenarioModuleExample example = new ApiScenarioModuleExample(); + ApiScenarioModuleExample.Criteria criteria = example.createCriteria(); + criteria.andNameEqualTo(node.getName()) + .andProjectIdEqualTo(node.getProjectId()); + if (StringUtils.isNotBlank(node.getParentId())) { + criteria.andParentIdEqualTo(node.getParentId()); + } else { + criteria.andParentIdIsNull(); + } + if (StringUtils.isNotBlank(node.getId())) { + criteria.andIdNotEqualTo(node.getId()); + } + return apiScenarioModuleMapper.selectByExample(example); + } + @Override public void updatePos(String id, Double pos) { extApiScenarioModuleMapper.updatePos(id, pos); diff --git a/frontend/src/business/components/api/automation/ApiAutomation.vue b/frontend/src/business/components/api/automation/ApiAutomation.vue index e806819a7c..05f048cdd0 100644 --- a/frontend/src/business/components/api/automation/ApiAutomation.vue +++ b/frontend/src/business/components/api/automation/ApiAutomation.vue @@ -1,5 +1,5 @@