diff --git a/README.md b/README.md index b096fc70dc..b8bcefc10c 100755 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# MeterSphere 开源持续测试平台 +# MeterSphere 一站式开源持续测试平台 [![Codacy Badge](https://api.codacy.com/project/badge/Grade/176186d132df448b955f8bdd5e6ef9c0)](https://app.codacy.com/gh/metersphere/metersphere?utm_source=github.com&utm_medium=referral&utm_content=metersphere/metersphere&utm_campaign=Badge_Grade_Dashboard) [![GitHub release (latest SemVer)](https://img.shields.io/github/v/release/metersphere/metersphere)](https://github.com/metersphere/metersphere/releases/latest) @@ -11,7 +11,7 @@ | ------------------------------------------------------------------------------------------------------------ | | 我们正在寻找开发者,欢迎加入我们共同打造更好用、更强大的 MeterSphere。联系我们: [metersphere@fit2cloud.com](mailto:metersphere@fit2cloud.com) | -MeterSphere 是一站式的开源企业级持续测试平台,涵盖测试跟踪、接口测试、性能测试、团队协作等功能,兼容JMeter 等开源标准,有效助力开发和测试团队充分利用云弹性进行高度可扩展的自动化测试,加速高质量软件的交付。 +MeterSphere 是一站式开源持续测试平台,涵盖测试跟踪、接口测试、性能测试、团队协作等功能,兼容JMeter 等开源标准,有效助力开发和测试团队充分利用云弹性进行高度可扩展的自动化测试,加速高质量软件的交付。 - 测试跟踪: 远超 TestLink 的使用体验; - 接口测试: 类似 Postman 的体验; diff --git a/ROADMAP.md b/ROADMAP.md index a6d51bbbb4..440f6fdbc5 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -55,11 +55,17 @@ - [x] 测试跟踪:支持对接禅道同步缺陷 - [x] 其他:Jenkins 插件支持 pipeline 方式调用 +## v1.6 (开发中) +- [ ] 新增接口管理功能 +- [ ] 全新接口自动化使用方式 +- [ ] 测试跟踪测试计划分类型展示 +- [ ] 优化消息通知配置及实现方式 + ## 规划中 - [ ] 接口测试支持添加 WebSocket 协议请求 -- [ ] 接口管理功能 - [ ] 集成云平台动态管理测试资源池 -- [ ] 支持 K8s 集群作为测试资源池 +- [ ] 测试跟踪测试用例及接口测试增加版本管理 +- [ ] 测试跟踪测试用例增加思维导图展示形式 - [ ] 移动端测试支持 - [ ] UI 功能测试支持 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 f4452ed120..710f2d3aa3 100644 --- a/backend/src/main/java/io/metersphere/api/controller/ApiAutomationController.java +++ b/backend/src/main/java/io/metersphere/api/controller/ApiAutomationController.java @@ -58,6 +58,11 @@ public class ApiAutomationController { apiAutomationService.removeToGc(ids); } + @PostMapping("/reduction") + public void reduction(@RequestBody List ids) { + apiAutomationService.reduction(ids); + } + @GetMapping("/getApiScenario/{id}") public ApiScenario getScenarioDefinition(@PathVariable String id) { return apiAutomationService.getApiScenario(id); diff --git a/backend/src/main/java/io/metersphere/api/controller/ApiDefinitionController.java b/backend/src/main/java/io/metersphere/api/controller/ApiDefinitionController.java index b27b4737b6..9aa8fea318 100644 --- a/backend/src/main/java/io/metersphere/api/controller/ApiDefinitionController.java +++ b/backend/src/main/java/io/metersphere/api/controller/ApiDefinitionController.java @@ -6,10 +6,7 @@ import io.metersphere.api.dto.APIReportResult; import io.metersphere.api.dto.ApiTestImportRequest; import io.metersphere.api.dto.automation.ApiScenarioRequest; import io.metersphere.api.dto.automation.ReferenceDTO; -import io.metersphere.api.dto.definition.ApiDefinitionRequest; -import io.metersphere.api.dto.definition.ApiDefinitionResult; -import io.metersphere.api.dto.definition.RunDefinitionRequest; -import io.metersphere.api.dto.definition.SaveApiDefinitionRequest; +import io.metersphere.api.dto.definition.*; import io.metersphere.api.service.ApiDefinitionService; import io.metersphere.base.domain.ApiDefinition; import io.metersphere.commons.constants.RoleConstants; @@ -64,6 +61,11 @@ public class ApiDefinitionController { apiDefinitionService.removeToGc(ids); } + @PostMapping("/reduction") + public void reduction(@RequestBody List ids) { + apiDefinitionService.reduction(ids); + } + @GetMapping("/get/{id}") public ApiDefinition get(@PathVariable String id) { return apiDefinitionService.get(id); @@ -100,4 +102,10 @@ public class ApiDefinitionController { return apiDefinitionService.getReference(request); } + @PostMapping("/batch/edit") + @RequiresRoles(value = {RoleConstants.TEST_USER, RoleConstants.TEST_MANAGER}, logical = Logical.OR) + public void editApiBath(@RequestBody ApiBatchRequest request) { + apiDefinitionService.editApiBath(request); + } + } diff --git a/backend/src/main/java/io/metersphere/api/dto/automation/ApiScenarioDTO.java b/backend/src/main/java/io/metersphere/api/dto/automation/ApiScenarioDTO.java index 173f7897da..efecbd4e2b 100644 --- a/backend/src/main/java/io/metersphere/api/dto/automation/ApiScenarioDTO.java +++ b/backend/src/main/java/io/metersphere/api/dto/automation/ApiScenarioDTO.java @@ -4,11 +4,13 @@ import io.metersphere.base.domain.ApiScenario; import lombok.Getter; import lombok.Setter; +import java.util.List; + @Getter @Setter public class ApiScenarioDTO extends ApiScenario { private String projectName; private String userName; - private String tagName; + private List tagNames; } diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/ApiBatchRequest.java b/backend/src/main/java/io/metersphere/api/dto/definition/ApiBatchRequest.java new file mode 100644 index 0000000000..e69f300944 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/definition/ApiBatchRequest.java @@ -0,0 +1,16 @@ +package io.metersphere.api.dto.definition; + +import io.metersphere.base.domain.ApiDefinitionWithBLOBs; +import io.metersphere.controller.request.OrderRequest; +import lombok.Getter; +import lombok.Setter; + +import java.util.List; + +@Getter +@Setter +public class ApiBatchRequest extends ApiDefinitionWithBLOBs { + private List ids; + private List orders; + private String projectId; +} diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/request/MsScenario.java b/backend/src/main/java/io/metersphere/api/dto/definition/request/MsScenario.java index 6be0d1fe99..92290a861e 100644 --- a/backend/src/main/java/io/metersphere/api/dto/definition/request/MsScenario.java +++ b/backend/src/main/java/io/metersphere/api/dto/definition/request/MsScenario.java @@ -17,6 +17,7 @@ import io.metersphere.commons.utils.CommonBeanFactory; import lombok.Data; import lombok.EqualsAndHashCode; import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.jmeter.save.SaveService; import org.apache.jmeter.testelement.TestElement; import org.apache.jorphan.collections.HashTree; @@ -47,7 +48,7 @@ public class MsScenario extends MsTestElement { if (!this.isEnable()) { return; } - if (environmentId != null) { + if (StringUtils.isNotEmpty(environmentId)) { ApiTestEnvironmentService environmentService = CommonBeanFactory.getBean(ApiTestEnvironmentService.class); ApiTestEnvironmentWithBLOBs environment = environmentService.get(environmentId); config.setConfig(JSONObject.parseObject(environment.getConfig(), EnvironmentConfig.class)); 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 21b416fb4e..90c436f519 100644 --- a/backend/src/main/java/io/metersphere/api/service/ApiAutomationService.java +++ b/backend/src/main/java/io/metersphere/api/service/ApiAutomationService.java @@ -22,6 +22,7 @@ import io.metersphere.commons.constants.APITestStatus; import io.metersphere.commons.constants.ApiRunMode; import io.metersphere.commons.constants.ReportTriggerMode; import io.metersphere.commons.exception.MSException; +import io.metersphere.commons.utils.ServiceUtils; import io.metersphere.commons.utils.SessionUtils; import io.metersphere.i18n.Translator; import io.metersphere.track.dto.TestPlanDTO; @@ -67,11 +68,12 @@ public class ApiAutomationService { private static final String BODY_FILE_DIR = "/opt/metersphere/data/body"; public List list(ApiScenarioRequest request) { + request.setOrders(ServiceUtils.getDefaultOrder(request.getOrders())); + List list = extApiScenarioMapper.list(request); ApiTagExample example = new ApiTagExample(); example.createCriteria().andProjectIdEqualTo(request.getProjectId()); List tags = apiTagMapper.selectByExample(example); Map tagMap = tags.stream().collect(Collectors.toMap(ApiTag::getId, ApiTag::getName)); - List list = extApiScenarioMapper.list(request); Gson gs = new Gson(); list.forEach(item -> { if (item.getTagId() != null) { @@ -81,7 +83,11 @@ public class ApiAutomationService { buf.append(","); }); if (buf != null && buf.length() > 0) { - item.setTagName(buf.toString().substring(0, buf.toString().length() - 1)); + String tagNames = buf.toString().substring(0, buf.toString().length() - 1); + List tagList = Arrays.asList(tagNames.split(",")); + item.setTagNames(tagList); + } else { + item.setTagNames(new ArrayList<>()); } } }); @@ -124,7 +130,7 @@ public class ApiAutomationService { scenario.setDescription(request.getDescription()); apiScenarioMapper.insert(scenario); - List bodyUploadIds = new ArrayList<>(request.getBodyUploadIds()); + List bodyUploadIds = request.getBodyUploadIds(); apiDefinitionService.createBodyFiles(bodyUploadIds, bodyFiles); } @@ -166,12 +172,12 @@ public class ApiAutomationService { apiScenarioMapper.deleteByExample(example); } - public void removeToGc(List ids) { - ApiScenario record = new ApiScenario(); - record.setStatus(ScenarioStatus.Trash.name()); - ApiScenarioExample example = new ApiScenarioExample(); - example.createCriteria().andIdIn(ids); - apiScenarioMapper.updateByExampleSelective(record, example); + public void removeToGc(List apiIds) { + extApiScenarioMapper.removeToGc(apiIds); + } + + public void reduction(List apiIds) { + extApiScenarioMapper.reduction(apiIds); } private void checkNameExist(SaveApiScenarioRequest request) { @@ -242,12 +248,18 @@ public class ApiAutomationService { JSONObject element = JSON.parseObject(item.getScenarioDefinition()); MsScenario scenario = JSONObject.parseObject(item.getScenarioDefinition(), MsScenario.class); // 多态JSON普通转换会丢失内容,需要通过 ObjectMapper 获取 - LinkedList elements = mapper.readValue(element.getString("hashTree"), - new TypeReference>() {}); - LinkedList variables = mapper.readValue(element.getString("variables"), - new TypeReference>() {}); - scenario.setHashTree(elements); - scenario.setVariables(variables); + if (element!= null && StringUtils.isNotEmpty(element.getString("hashTree"))) { + LinkedList elements = mapper.readValue(element.getString("hashTree"), + new TypeReference>() { + }); + scenario.setHashTree(elements); + } + if (StringUtils.isNotEmpty(element.getString("variables"))) { + LinkedList variables = mapper.readValue(element.getString("variables"), + new TypeReference>() { + }); + scenario.setVariables(variables); + } LinkedList scenarios = new LinkedList<>(); scenarios.add(scenario); group.setHashTree(scenarios); 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 5851ca24ec..789009212a 100644 --- a/backend/src/main/java/io/metersphere/api/service/ApiDefinitionService.java +++ b/backend/src/main/java/io/metersphere/api/service/ApiDefinitionService.java @@ -163,6 +163,10 @@ public class ApiDefinitionService { extApiDefinitionMapper.removeToGc(apiIds); } + public void reduction(List apiIds) { + extApiDefinitionMapper.reduction(apiIds); + } + public void deleteBodyFiles(String apiId) { File file = new File(BODY_FILE_DIR + "/" + apiId); FileUtil.deleteContents(file); @@ -371,4 +375,15 @@ public class ApiDefinitionService { dto.setTestPlanList(extTestPlanMapper.selectReference(planRequest)); return dto; } + + public void editApiBath(ApiBatchRequest request) { + ApiDefinitionExample definitionExample = new ApiDefinitionExample(); + definitionExample.createCriteria().andIdIn(request.getIds()); + + ApiDefinitionWithBLOBs definitionWithBLOBs = new ApiDefinitionWithBLOBs(); + BeanUtils.copyBean(definitionWithBLOBs, request); + definitionWithBLOBs.setUpdateTime(System.currentTimeMillis()); + apiDefinitionMapper.updateByExampleSelective(definitionWithBLOBs, definitionExample); + } + } 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 ffbdb54759..374f6a8c58 100644 --- a/backend/src/main/java/io/metersphere/api/service/ApiScenarioModuleService.java +++ b/backend/src/main/java/io/metersphere/api/service/ApiScenarioModuleService.java @@ -21,11 +21,10 @@ import org.apache.ibatis.session.SqlSessionFactory; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import javax.annotation.Resource; import java.util.*; import java.util.stream.Collectors; -import javax.annotation.Resource; - @Service @Transactional(rollbackFor = Exception.class) public class ApiScenarioModuleService { @@ -124,16 +123,17 @@ public class ApiScenarioModuleService { } } - private List queryByModuleIds(List nodeIds) { + private List queryByModuleIds(DragApiScenarioModuleRequest request) { ApiScenarioRequest apiScenarioRequest = new ApiScenarioRequest(); - apiScenarioRequest.setModuleIds(nodeIds); + apiScenarioRequest.setProjectId(request.getProjectId()); + apiScenarioRequest.setModuleIds(request.getNodeIds()); return apiAutomationService.list(apiScenarioRequest); } public int editNode(DragApiScenarioModuleRequest request) { request.setUpdateTime(System.currentTimeMillis()); checkApiScenarioModuleExist(request); - List apiScenarios = queryByModuleIds(request.getNodeIds()); + List apiScenarios = queryByModuleIds(request); apiScenarios.forEach(apiScenario -> { StringBuilder path = new StringBuilder(apiScenario.getModulePath()); @@ -172,7 +172,7 @@ public class ApiScenarioModuleService { List nodeIds = request.getNodeIds(); - List apiScenarios = queryByModuleIds(nodeIds); + List apiScenarios = queryByModuleIds(request); ApiScenarioModuleDTO nodeTree = request.getNodeTree(); diff --git a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiDefinitionMapper.java b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiDefinitionMapper.java index 320d5b4664..ba560aeebe 100644 --- a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiDefinitionMapper.java +++ b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiDefinitionMapper.java @@ -15,4 +15,6 @@ public interface ExtApiDefinitionMapper { int removeToGc(@Param("ids") List ids); + int reduction(@Param("ids") List ids); + } \ No newline at end of file diff --git a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiDefinitionMapper.xml b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiDefinitionMapper.xml index ab5b4dac15..545bc9b71a 100644 --- a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiDefinitionMapper.xml +++ b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiDefinitionMapper.xml @@ -255,4 +255,15 @@ #{v} + + + update api_definition + set + status = 'Underway' + where id in + + #{v} + + + \ No newline at end of file diff --git a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiScenarioMapper.java b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiScenarioMapper.java index bec0fbde89..fce72e2e7b 100644 --- a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiScenarioMapper.java +++ b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiScenarioMapper.java @@ -16,4 +16,7 @@ public interface ExtApiScenarioMapper { List selectReference(@Param("request") ApiScenarioRequest request); + int removeToGc(@Param("ids") List ids); + + int reduction(@Param("ids") List ids); } diff --git a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiScenarioMapper.xml b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiScenarioMapper.xml index da36ff4df5..e7c638e10c 100644 --- a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiScenarioMapper.xml +++ b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiScenarioMapper.xml @@ -88,5 +88,25 @@ + + update api_scenario + set + status = 'Trash' + where id in + + #{v} + + + + + update api_scenario + set + status = 'Underway' + where id in + + #{v} + + + \ No newline at end of file diff --git a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtLoadTestMapper.xml b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtLoadTestMapper.xml index 032229be0a..3ee03e4735 100644 --- a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtLoadTestMapper.xml +++ b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtLoadTestMapper.xml @@ -106,7 +106,7 @@