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..927a3a6c2c 100644 --- a/backend/src/main/java/io/metersphere/api/controller/ApiAutomationController.java +++ b/backend/src/main/java/io/metersphere/api/controller/ApiAutomationController.java @@ -2,9 +2,11 @@ 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.definition.RunDefinitionRequest; +import io.metersphere.api.dto.definition.parse.ApiDefinitionImport; import io.metersphere.api.service.ApiAutomationService; import io.metersphere.base.domain.ApiScenario; import io.metersphere.base.domain.ApiScenarioWithBLOBs; @@ -146,5 +148,11 @@ 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 ApiDefinitionImport testCaseImport(@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/ApiTestImportRequest.java b/backend/src/main/java/io/metersphere/api/dto/ApiTestImportRequest.java index 932c0ac674..e76fa7403e 100644 --- a/backend/src/main/java/io/metersphere/api/dto/ApiTestImportRequest.java +++ b/backend/src/main/java/io/metersphere/api/dto/ApiTestImportRequest.java @@ -13,8 +13,6 @@ public class ApiTestImportRequest { private String projectId; private String platform; private Boolean useEnvironment; - // 来自场景的导入不需要存储 - private boolean saved = true; private String swaggerUrl; //导入策略 private String modeId; diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/parse/ApiDefinitionImport.java b/backend/src/main/java/io/metersphere/api/dto/definition/parse/ApiDefinitionImport.java index 7a35339222..b1f3ddba8d 100644 --- a/backend/src/main/java/io/metersphere/api/dto/definition/parse/ApiDefinitionImport.java +++ b/backend/src/main/java/io/metersphere/api/dto/definition/parse/ApiDefinitionImport.java @@ -1,5 +1,6 @@ package io.metersphere.api.dto.definition.parse; +import io.metersphere.api.dto.definition.request.MsScenario; import io.metersphere.base.domain.ApiDefinitionWithBLOBs; import io.metersphere.base.domain.ApiTestCaseWithBLOBs; import lombok.Data; @@ -12,6 +13,9 @@ public class ApiDefinitionImport { private String protocol; private List data; + //导入场景 + private MsScenario scenarioDefinition; + // 新版本带用例导出 private List cases; } diff --git a/backend/src/main/java/io/metersphere/api/parse/ApiImportAbstractParser.java b/backend/src/main/java/io/metersphere/api/parse/ApiImportAbstractParser.java index 12f7519ef9..e5d2729d8b 100644 --- a/backend/src/main/java/io/metersphere/api/parse/ApiImportAbstractParser.java +++ b/backend/src/main/java/io/metersphere/api/parse/ApiImportAbstractParser.java @@ -75,7 +75,7 @@ public abstract class ApiImportAbstractParser implements ApiImportParser { return null; } - protected ApiModule buildModule(ApiModule parentModule, String name, boolean isSaved) { + protected ApiModule buildModule(ApiModule parentModule, String name) { apiModuleService = CommonBeanFactory.getBean(ApiModuleService.class); ApiModule module; if (parentModule != null) { @@ -84,9 +84,7 @@ public abstract class ApiImportAbstractParser implements ApiImportParser { } else { module = apiModuleService.getNewModule(name, this.projectId, 1); } - if (isSaved) { - createModule(module); - } + createModule(module); return module; } diff --git a/backend/src/main/java/io/metersphere/api/parse/ApiScenarioImportParserFactory.java b/backend/src/main/java/io/metersphere/api/parse/ApiScenarioImportParserFactory.java new file mode 100644 index 0000000000..8c9b5fc51b --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/parse/ApiScenarioImportParserFactory.java @@ -0,0 +1,15 @@ +package io.metersphere.api.parse; + +import io.metersphere.commons.constants.ApiImportPlatform; +import org.apache.commons.lang3.StringUtils; + +public class ApiScenarioImportParserFactory { + public static ApiImportParser getApiImportParser(String platform) { + if (StringUtils.equals(ApiImportPlatform.Metersphere.name(), platform)) { + return new MsParser(); + } else if (StringUtils.equals(ApiImportPlatform.Postman.name(), platform)) { + return new ScenarioPostmanParser(); + } + return null; + } +} diff --git a/backend/src/main/java/io/metersphere/api/parse/MsParser.java b/backend/src/main/java/io/metersphere/api/parse/MsParser.java index 82f691a6c8..7b939a84af 100644 --- a/backend/src/main/java/io/metersphere/api/parse/MsParser.java +++ b/backend/src/main/java/io/metersphere/api/parse/MsParser.java @@ -73,7 +73,7 @@ public class MsParser extends ApiImportAbstractParser { testObject.keySet().forEach(tag -> { ApiModule parentModule = getSelectModule(importRequest.getModuleId()); - ApiModule module = buildModule(parentModule, tag, importRequest.isSaved()); + ApiModule module = buildModule(parentModule, tag); JSONObject requests = testObject.getJSONObject(tag); requests.keySet().forEach(requestName -> { @@ -181,7 +181,7 @@ public class MsParser extends ApiImportAbstractParser { Iterator iterator = modules.iterator(); while (iterator.hasNext()) { String item = iterator.next(); - parent = buildModule(parent, item, importRequest.isSaved()); + parent = buildModule(parent, item); if (!iterator.hasNext()) { apiDefinition.setModuleId(parent.getId()); } diff --git a/backend/src/main/java/io/metersphere/api/parse/PostmanParser.java b/backend/src/main/java/io/metersphere/api/parse/PostmanParser.java index 0b7d3d5862..393b07a3e3 100644 --- a/backend/src/main/java/io/metersphere/api/parse/PostmanParser.java +++ b/backend/src/main/java/io/metersphere/api/parse/PostmanParser.java @@ -33,17 +33,20 @@ public class PostmanParser extends ApiImportAbstractParser { List variables = postmanCollection.getVariable(); ApiDefinitionImport apiImport = new ApiDefinitionImport(); List results = new ArrayList<>(); - parseItem(postmanCollection.getItem(), variables, results, buildModule(getSelectModule(request.getModuleId()), postmanCollection.getInfo().getName(), request.isSaved()), request.isSaved()); + parseItem(postmanCollection.getItem(), variables, results, buildModule(getSelectModule(request.getModuleId()), postmanCollection.getInfo().getName()), true); apiImport.setData(results); return apiImport; } - private void parseItem(List items, List variables, List results, ApiModule parentModule, boolean isSaved) { + protected void parseItem(List items, List variables, List results, ApiModule parentModule, Boolean isCreateModule) { for (PostmanItem item : items) { List childItems = item.getItem(); if (childItems != null) { - ApiModule module = buildModule(parentModule, item.getName() , isSaved); - parseItem(childItems, variables, results, module, isSaved); + ApiModule module = null; + if (isCreateModule) { + module = buildModule(parentModule, item.getName()); + } + parseItem(childItems, variables, results, module, isCreateModule); } else { ApiDefinitionWithBLOBs request = parsePostman(item); if (request != null) { diff --git a/backend/src/main/java/io/metersphere/api/parse/ScenarioPostmanParser.java b/backend/src/main/java/io/metersphere/api/parse/ScenarioPostmanParser.java new file mode 100644 index 0000000000..cc45f434ee --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/parse/ScenarioPostmanParser.java @@ -0,0 +1,40 @@ +package io.metersphere.api.parse; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import io.metersphere.api.dto.ApiTestImportRequest; +import io.metersphere.api.dto.definition.parse.ApiDefinitionImport; +import io.metersphere.api.dto.definition.request.MsScenario; +import io.metersphere.api.dto.definition.request.MsTestElement; +import io.metersphere.api.dto.definition.request.sampler.MsHTTPSamplerProxy; +import io.metersphere.api.dto.parse.postman.PostmanCollection; +import io.metersphere.base.domain.ApiDefinitionWithBLOBs; +import io.metersphere.base.domain.ApiModule; + +import java.io.InputStream; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; + +public class ScenarioPostmanParser extends PostmanParser { + + @Override + public ApiDefinitionImport parse(InputStream source, ApiTestImportRequest request) { + this.projectId = request.getProjectId(); + ApiDefinitionImport apiImport = new ApiDefinitionImport(); + List results = new ArrayList<>(); + PostmanCollection postmanCollection = JSON.parseObject(getApiTestStr(source), PostmanCollection.class); + parseItem(postmanCollection.getItem(), postmanCollection.getVariable(), results, null, false); + + MsScenario msScenario = new MsScenario(); + LinkedList msHTTPSamplerProxies = new LinkedList<>(); + results.forEach(res -> { + msHTTPSamplerProxies.add(JSONObject.parseObject(res.getRequest(), MsHTTPSamplerProxy.class)); + }); + msScenario.setHashTree(msHTTPSamplerProxies); + msScenario.setType("scenario"); + msScenario.setName(postmanCollection.getInfo().getName()); + apiImport.setScenarioDefinition(msScenario); + return apiImport; + } +} diff --git a/backend/src/main/java/io/metersphere/api/parse/Swagger2Parser.java b/backend/src/main/java/io/metersphere/api/parse/Swagger2Parser.java index d2d2823dd2..00765494e9 100644 --- a/backend/src/main/java/io/metersphere/api/parse/Swagger2Parser.java +++ b/backend/src/main/java/io/metersphere/api/parse/Swagger2Parser.java @@ -71,7 +71,7 @@ public class Swagger2Parser extends SwaggerAbstractParser { addBodyHeader(request); apiDefinition.setRequest(JSON.toJSONString(request)); apiDefinition.setResponse(JSON.toJSONString(parseResponse(operation, operation.getResponses()))); - buildModule(parentNode, apiDefinition, operation.getTags(), importRequest.isSaved()); + buildModule(parentNode, apiDefinition, operation.getTags()); results.add(apiDefinition); } } diff --git a/backend/src/main/java/io/metersphere/api/parse/Swagger3Parser.java b/backend/src/main/java/io/metersphere/api/parse/Swagger3Parser.java index b189c89534..80b1b75684 100644 --- a/backend/src/main/java/io/metersphere/api/parse/Swagger3Parser.java +++ b/backend/src/main/java/io/metersphere/api/parse/Swagger3Parser.java @@ -103,7 +103,7 @@ public class Swagger3Parser extends SwaggerAbstractParser { addBodyHeader(request); apiDefinition.setRequest(JSON.toJSONString(request)); apiDefinition.setResponse(JSON.toJSONString(parseResponse(operation.getResponses()))); - buildModule(parentNode, apiDefinition, operation.getTags(), importRequest.isSaved()); + buildModule(parentNode, apiDefinition, operation.getTags()); results.add(apiDefinition); } } diff --git a/backend/src/main/java/io/metersphere/api/parse/SwaggerAbstractParser.java b/backend/src/main/java/io/metersphere/api/parse/SwaggerAbstractParser.java index 2a6fdf9f7a..187550d4eb 100644 --- a/backend/src/main/java/io/metersphere/api/parse/SwaggerAbstractParser.java +++ b/backend/src/main/java/io/metersphere/api/parse/SwaggerAbstractParser.java @@ -7,10 +7,10 @@ import java.util.List; public abstract class SwaggerAbstractParser extends ApiImportAbstractParser { - protected void buildModule(ApiModule parentModule, ApiDefinitionWithBLOBs apiDefinition, List tags, boolean isSaved) { + protected void buildModule(ApiModule parentModule, ApiDefinitionWithBLOBs apiDefinition, List tags) { if (tags != null) { tags.forEach(tag -> { - ApiModule module = buildModule(parentModule, tag, isSaved); + ApiModule module = buildModule(parentModule, tag); apiDefinition.setModuleId(module.getId()); }); } 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 3de0a658c2..4bb57218e2 100644 --- a/backend/src/main/java/io/metersphere/api/service/ApiAutomationService.java +++ b/backend/src/main/java/io/metersphere/api/service/ApiAutomationService.java @@ -5,15 +5,19 @@ 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.datacount.ApiDataCountResult; import io.metersphere.api.dto.definition.RunDefinitionRequest; +import io.metersphere.api.dto.definition.parse.ApiDefinitionImport; import io.metersphere.api.dto.definition.request.*; import io.metersphere.api.dto.definition.request.variable.ScenarioVariable; import io.metersphere.api.dto.scenario.environment.EnvironmentConfig; import io.metersphere.api.jmeter.JMeterService; +import io.metersphere.api.parse.ApiImportParser; +import io.metersphere.api.parse.ApiScenarioImportParserFactory; import io.metersphere.base.domain.*; import io.metersphere.base.mapper.ApiScenarioMapper; import io.metersphere.base.mapper.ApiScenarioReportMapper; @@ -143,32 +147,11 @@ public class ApiAutomationService { request.setId(UUID.randomUUID().toString()); checkNameExist(request); - final ApiScenarioWithBLOBs scenario = new ApiScenarioWithBLOBs(); - scenario.setId(request.getId()); - scenario.setName(request.getName()); - scenario.setProjectId(request.getProjectId()); - scenario.setTags(request.getTags()); - scenario.setApiScenarioModuleId(request.getApiScenarioModuleId()); - scenario.setModulePath(request.getModulePath()); - scenario.setLevel(request.getLevel()); - scenario.setFollowPeople(request.getFollowPeople()); - scenario.setPrincipal(request.getPrincipal()); - scenario.setStepTotal(request.getStepTotal()); - scenario.setScenarioDefinition(JSON.toJSONString(request.getScenarioDefinition())); + final ApiScenarioWithBLOBs scenario = buildSaveScenario(request); + scenario.setCreateTime(System.currentTimeMillis()); - scenario.setUpdateTime(System.currentTimeMillis()); scenario.setNum(getNextNum(request.getProjectId())); - if (StringUtils.isNotEmpty(request.getStatus())) { - scenario.setStatus(request.getStatus()); - } else { - scenario.setStatus(ScenarioStatus.Underway.name()); - } - if (request.getUserId() == null) { - scenario.setUserId(SessionUtils.getUserId()); - } else { - scenario.setUserId(request.getUserId()); - } - scenario.setDescription(request.getDescription()); + apiScenarioMapper.insert(scenario); List bodyUploadIds = request.getBodyUploadIds(); @@ -190,7 +173,12 @@ public class ApiAutomationService { List bodyUploadIds = request.getBodyUploadIds(); FileUtils.createBodyFiles(bodyUploadIds, bodyFiles); - final ApiScenarioWithBLOBs scenario = new ApiScenarioWithBLOBs(); + final ApiScenarioWithBLOBs scenario = buildSaveScenario(request); + apiScenarioMapper.updateByPrimaryKeySelective(scenario); + } + + public ApiScenarioWithBLOBs buildSaveScenario(SaveApiScenarioRequest request) { + ApiScenarioWithBLOBs scenario = new ApiScenarioWithBLOBs(); scenario.setId(request.getId()); scenario.setName(request.getName()); scenario.setProjectId(request.getProjectId()); @@ -201,16 +189,19 @@ public class ApiAutomationService { scenario.setFollowPeople(request.getFollowPeople()); scenario.setPrincipal(request.getPrincipal()); scenario.setStepTotal(request.getStepTotal()); - scenario.setScenarioDefinition(JSON.toJSONString(request.getScenarioDefinition())); scenario.setUpdateTime(System.currentTimeMillis()); + scenario.setScenarioDefinition(JSON.toJSONString(request.getScenarioDefinition())); if (StringUtils.isNotEmpty(request.getStatus())) { scenario.setStatus(request.getStatus()); } else { scenario.setStatus(ScenarioStatus.Underway.name()); } - scenario.setUserId(request.getUserId()); - scenario.setDescription(request.getDescription()); - apiScenarioMapper.updateByPrimaryKeySelective(scenario); + if (request.getUserId() == null) { + scenario.setUserId(SessionUtils.getUserId()); + } else { + scenario.setUserId(request.getUserId()); + } + return scenario; } public void delete(String id) { @@ -732,4 +723,27 @@ public class ApiAutomationService { }); } } + + public ApiDefinitionImport scenarioImport(MultipartFile file, ApiTestImportRequest request) { + ApiImportParser apiImportParser = ApiScenarioImportParserFactory.getApiImportParser(request.getPlatform()); + ApiDefinitionImport apiImport = null; + try { + apiImport = apiImportParser.parse(file == null ? null : file.getInputStream(), request); + } catch (Exception e) { + LogUtil.error(e.getMessage(), e); + MSException.throwException(Translator.get("parse_data_error")); + } + SaveApiScenarioRequest saveReq = new SaveApiScenarioRequest(); + saveReq.setScenarioDefinition(apiImport.getScenarioDefinition()); + saveReq.setName(saveReq.getScenarioDefinition().getName()); + saveReq.setProjectId(request.getProjectId()); + saveReq.setApiScenarioModuleId(request.getModuleId()); + if (StringUtils.isNotBlank(request.getUserId())) { + saveReq.setPrincipal(request.getUserId()); + } else { + saveReq.setPrincipal(SessionUtils.getUserId()); + } + create(saveReq, new ArrayList<>()); + return apiImport; + } } diff --git a/backend/src/main/java/io/metersphere/api/service/ApiDefinitionService.java b/backend/src/main/java/io/metersphere/api/service/ApiDefinitionService.java index d6d46b2ffa..2a89c74dad 100644 --- a/backend/src/main/java/io/metersphere/api/service/ApiDefinitionService.java +++ b/backend/src/main/java/io/metersphere/api/service/ApiDefinitionService.java @@ -498,9 +498,7 @@ public class ApiDefinitionService { LogUtil.error(e.getMessage(), e); MSException.throwException(Translator.get("parse_data_error")); } - if (request.isSaved()) { - importApi(request, apiImport); - } + importApi(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..ae8d9cb847 100644 --- a/backend/src/main/java/io/metersphere/api/service/ApiScenarioModuleService.java +++ b/backend/src/main/java/io/metersphere/api/service/ApiScenarioModuleService.java @@ -46,10 +46,6 @@ public class ApiScenarioModuleService extends NodeTreeService @@ -41,10 +43,12 @@ import {getCurrentProjectID} from "@/common/js/utils"; import MsNodeTree from "../../../track/common/NodeTree"; import {buildNodePath} from "../../definition/model/NodeTree"; import ModuleTrashButton from "../../definition/components/module/ModuleTrashButton"; +import ApiScenarioModuleHeader from "@/business/components/api/automation/scenario/module/ApiScenarioModuleHeader"; export default { name: 'MsApiScenarioModule', components: { + ApiScenarioModuleHeader, ModuleTrashButton, MsNodeTree, MsAddBasisScenario, @@ -175,9 +179,9 @@ export default { this.$emit("nodeSelectEvent", node, nodeIds, pNodes); } }, - // exportAPI() { - // this.$emit('exportAPI'); - // }, + exportAPI() { + this.$emit('exportAPI'); + }, // debug() { // this.$emit('debug'); // }, diff --git a/frontend/src/business/components/api/automation/scenario/module/ApiScenarioModuleHeader.vue b/frontend/src/business/components/api/automation/scenario/module/ApiScenarioModuleHeader.vue new file mode 100644 index 0000000000..3d3059d91d --- /dev/null +++ b/frontend/src/business/components/api/automation/scenario/module/ApiScenarioModuleHeader.vue @@ -0,0 +1,111 @@ + + + + diff --git a/frontend/src/business/components/api/definition/components/import/ApiImport.vue b/frontend/src/business/components/api/definition/components/import/ApiImport.vue index 29be9ed509..5dc21b3441 100644 --- a/frontend/src/business/components/api/definition/components/import/ApiImport.vue +++ b/frontend/src/business/components/api/definition/components/import/ApiImport.vue @@ -5,7 +5,7 @@
{{ $t('api_test.api_import.data_format') }}
- {{ item.name }} + {{ item.name }}
@@ -26,14 +26,14 @@ - + @@ -42,7 +42,7 @@ - + @@ -56,7 +56,7 @@ + v-if="selectedPlatformValue != 'Swagger2' || (selectedPlatformValue == 'Swagger2' && !swaggerUrlEnable)"> { if (valid) { - if ((this.selectedPlatformValue != 'Swagger2' || (this.selectedPlatformValue == 'Swagger2' && !this.swaggerUrlEable)) && !this.formData.file) { + if ((this.selectedPlatformValue != 'Swagger2' || (this.selectedPlatformValue == 'Swagger2' && !this.swaggerUrlEnable)) && !this.formData.file) { this.$warning(this.$t('commons.please_upload')); return; } + let url = '/api/definition/import'; + if (this.isScenarioModel) { + url = '/api/automation/import'; + } let param = this.buildParam(); - this.result = this.$fileUpload('/api/definition/import', param.file, null, this.buildParam(), response => { + this.result = this.$fileUpload(url, param.file, null, this.buildParam(), response => { let res = response.data; this.$success(this.$t('test_track.case.import.success')); this.visible = false; @@ -243,6 +253,7 @@ export default { Object.assign(param, this.formData); param.platform = this.selectedPlatformValue; param.saved = this.saved; + param.model = this.model; if (this.currentModule) { param.moduleId = this.formData.moduleId this.moduleOptions.filter(item => { @@ -253,7 +264,7 @@ export default { param.modeId = this.formData.modeId } param.projectId = getCurrentProjectID(); - if (!this.swaggerUrlEable) { + if (!this.swaggerUrlEnable) { param.swaggerUrl = undefined; } return param;