feat(功能用例): 新增功能用例获取接口用例接口

This commit is contained in:
guoyuqi 2023-12-26 18:23:39 +08:00 committed by 刘瑞斌
parent 856345b1aa
commit 8f203d6a44
20 changed files with 434 additions and 74 deletions

View File

@ -16,6 +16,24 @@
<description>provider</description> <description>provider</description>
<properties> <properties>
</properties> </properties>
<dependencies>
<dependency>
<groupId>io.metersphere</groupId>
<artifactId>metersphere-domain</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>io.metersphere</groupId>
<artifactId>metersphere-sdk</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-annotations-jakarta</artifactId>
<version>${swagger.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project> </project>

View File

@ -0,0 +1,13 @@
package io.metersphere.dto;
import io.metersphere.api.domain.ApiTestCase;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
public class ApiTestCaseProviderDTO extends ApiTestCase {
@Schema(description = "版本名称")
private String versionName;
}

View File

@ -0,0 +1,21 @@
package io.metersphere.provider;
import io.metersphere.dto.ApiTestCaseProviderDTO;
import io.metersphere.request.ApiTestCasePageProviderRequest;
import java.util.List;
/**
* @author guoyuqi
*/
public interface BaseAssociateApiProvider {
/**
* 获取尚未关联的接口列表
* @param sourceType 关联关系表表名
* @param sourceName 关联关系表主动关联方字段名称
* @param apiCaseColumnName 接口用例id 在关联关系表的字段名称
* @param apiTestCasePageProviderRequest 接口用例高级搜索条件
* @return List<ApiTestCaseProviderDTO>
*/
List<ApiTestCaseProviderDTO> getApiTestCaseList(String sourceType,String sourceName, String apiCaseColumnName, ApiTestCasePageProviderRequest apiTestCasePageProviderRequest);
}

View File

@ -2,6 +2,13 @@ package io.metersphere.provider;
import java.util.Map; import java.util.Map;
/**
* @author guoyuqi
*/
public interface BaseCaseProvider { public interface BaseCaseProvider {
/**
* 更新用例评审数据
* @param paramMap 更新用例评审所需参数
*/
void updateCaseReview(Map<String, Object> paramMap); void updateCaseReview(Map<String, Object> paramMap);
} }

View File

@ -0,0 +1,94 @@
package io.metersphere.request;
import com.google.common.base.CaseFormat;
import io.metersphere.sdk.constants.ModuleConstants;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.Valid;
import jakarta.validation.constraints.*;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.apache.commons.lang3.StringUtils;
import java.io.Serial;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
/**
* @author guoyuqi
*/
@Data
@EqualsAndHashCode(callSuper = false)
public class ApiTestCasePageProviderRequest implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
@Min(value = 1, message = "当前页码必须大于0")
@Schema(description = "当前页码")
private int current;
@Min(value = 5, message = "每页显示条数必须不小于5")
@Max(value = 500, message = "每页显示条数不能大于500")
@Schema(description = "每页显示条数")
private int pageSize;
@Schema(description = "排序字段model中的字段 : asc/desc")
private Map<@Valid @Pattern(regexp = "^[A-Za-z]+$") String, @Valid @NotBlank String> sort;
@Schema(description = "关键字")
private String keyword;
@Schema(description = "匹配模式 所有/任一", allowableValues = {"AND", "OR"})
private String searchMode = "AND";
@Schema(description = "过滤字段")
private Map<String, List<String>> filter;
@Schema(description = "高级搜索")
private Map<String, Object> combine;
@Schema(description = "关联关系表里主ID eg:功能用例关联接口用例时为功能用例id")
@NotBlank(message = "{api_definition.project_id.not_blank}")
@Size(min = 1, max = 50, message = "{api_definition.project_id.length_range}")
private String sourceId;
@Schema(description = "接口pk")
private String apiDefinitionId;
@Schema(description = "项目ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{api_definition.project_id.not_blank}")
@Size(min = 1, max = 50, message = "{api_definition.project_id.length_range}")
private String projectId;
@Schema(description = "接口协议", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{api_definition.protocol.not_blank}")
@Size(min = 1, max = 20, message = "{api_definition.protocol.length_range}")
private String protocol = ModuleConstants.NODE_PROTOCOL_HTTP;
@Schema(description = "模块ID")
private List<@NotBlank String> moduleIds;
@Schema(description = "版本fk")
private String versionId;
@Schema(description = "版本来源")
private String refId;
public String getSortString() {
if (sort == null || sort.isEmpty()) {
return null;
}
StringBuilder sb = new StringBuilder();
for (Map.Entry<String, String> entry : sort.entrySet()) {
String column = CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, entry.getKey());
sb.append(column)
.append(StringUtils.SPACE)
.append(StringUtils.equalsIgnoreCase(entry.getValue(), "DESC") ? "DESC" : "ASC")
.append(",");
}
return sb.substring(0, sb.length() - 1);
}
}

View File

@ -18,6 +18,11 @@
<artifactId>metersphere-sdk</artifactId> <artifactId>metersphere-sdk</artifactId>
<version>${revision}</version> <version>${revision}</version>
</dependency> </dependency>
<dependency>
<groupId>io.metersphere</groupId>
<artifactId>metersphere-provider</artifactId>
<version>${revision}</version>
</dependency>
<dependency> <dependency>
<groupId>io.metersphere</groupId> <groupId>io.metersphere</groupId>
<artifactId>metersphere-system-setting</artifactId> <artifactId>metersphere-system-setting</artifactId>

View File

@ -6,6 +6,8 @@ import io.metersphere.api.dto.definition.ApiTestCaseBatchRequest;
import io.metersphere.api.dto.definition.ApiTestCaseDTO; import io.metersphere.api.dto.definition.ApiTestCaseDTO;
import io.metersphere.api.dto.definition.ApiTestCasePageRequest; import io.metersphere.api.dto.definition.ApiTestCasePageRequest;
import io.metersphere.api.dto.definition.CasePassDTO; import io.metersphere.api.dto.definition.CasePassDTO;
import io.metersphere.dto.ApiTestCaseProviderDTO;
import io.metersphere.request.ApiTestCasePageProviderRequest;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Param;
@ -39,4 +41,7 @@ public interface ExtApiTestCaseMapper {
List<String> selectIdsByCaseIds(@Param("ids") List<String> ids); List<String> selectIdsByCaseIds(@Param("ids") List<String> ids);
List<String> getCaseIds(@Param("ids")List<String> ids, @Param("deleted")boolean deleted); List<String> getCaseIds(@Param("ids")List<String> ids, @Param("deleted")boolean deleted);
List<ApiTestCaseProviderDTO> listByProviderRequest(@Param("table") String resourceType, @Param("sourceName") String sourceName, @Param("targetName") String targetName,@Param("request") ApiTestCasePageProviderRequest request, @Param("deleted") boolean deleted);
} }

View File

@ -142,6 +142,26 @@
</foreach> </foreach>
</select> </select>
<select id="listByProviderRequest" resultType="io.metersphere.dto.ApiTestCaseProviderDTO">
SELECT
t1.id,
t1.project_id,
t1.name,
t1.priority,
t1.tags,
v.name as versionName
FROM
api_test_case t1
LEFT JOIN project_version v ON t1.version_id = v.id
INNER JOIN api_definition a ON t1.api_definition_id = a.id
WHERE t1.deleted =#{deleted}
and t1.id not in
(
select associate.${targetName} from ${table} associate where associate.${sourceName} = #{request.sourceId}
)
<include refid="queryWhereCondition"/>
</select>
<sql id="queryWhereConditionByBatch"> <sql id="queryWhereConditionByBatch">
<if test="request.protocol != null and request.protocol!=''"> <if test="request.protocol != null and request.protocol!=''">
and a.protocol = #{request.protocol} and a.protocol = #{request.protocol}

View File

@ -0,0 +1,22 @@
package io.metersphere.api.provider;
import io.metersphere.api.mapper.ExtApiTestCaseMapper;
import io.metersphere.dto.ApiTestCaseProviderDTO;
import io.metersphere.provider.BaseAssociateApiProvider;
import io.metersphere.request.ApiTestCasePageProviderRequest;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class AssociateApiProvider implements BaseAssociateApiProvider {
@Resource
private ExtApiTestCaseMapper extApiTestCaseMapper;
@Override
public List<ApiTestCaseProviderDTO> getApiTestCaseList(String sourceType, String sourceName, String apiCaseColumnName, ApiTestCasePageProviderRequest apiTestCasePageProviderRequest) {
return extApiTestCaseMapper.listByProviderRequest(sourceType, sourceName, apiCaseColumnName, apiTestCasePageProviderRequest, false);
}
}

View File

@ -0,0 +1,46 @@
package io.metersphere.api.controller;
import io.metersphere.api.provider.AssociateApiProvider;
import io.metersphere.dto.ApiTestCaseProviderDTO;
import io.metersphere.request.ApiTestCasePageProviderRequest;
import io.metersphere.sdk.util.JSON;
import io.metersphere.system.base.BaseTest;
import jakarta.annotation.Resource;
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.jdbc.Sql;
import org.springframework.test.context.jdbc.SqlConfig;
import java.util.HashMap;
import java.util.List;
@SpringBootTest(webEnvironment= SpringBootTest.WebEnvironment.RANDOM_PORT)
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
@AutoConfigureMockMvc
public class AssociateApiProviderTest extends BaseTest {
@Resource
private AssociateApiProvider provider;
@Test
@Order(1)
@Sql(scripts = {"/dml/init_functional_case_test.sql"}, config = @SqlConfig(encoding = "utf-8", transactionMode = SqlConfig.TransactionMode.ISOLATED))
public void getApiTestCaseListSuccess() throws Exception {
ApiTestCasePageProviderRequest request = new ApiTestCasePageProviderRequest();
request.setSourceId("gyq_associate_case_id_1");
request.setProjectId("project-associate-case-test");
request.setCurrent(1);
request.setPageSize(10);
request.setSort(new HashMap<>() {{
put("createTime", "desc");
}});
List<ApiTestCaseProviderDTO> apiTestCaseList = provider.getApiTestCaseList("functional_case_test", "case_id", "source_id", request);
String jsonString = JSON.toJSONString(apiTestCaseList);
System.out.println(jsonString);
}
}

View File

@ -0,0 +1,24 @@
INSERT INTO organization(id, num, name, description, create_time, update_time, create_user, update_user, deleted,
delete_user, delete_time) VALUE
('organization-associate-case-test', null, 'organization-associate-case-test', 'organization-associate-case-test',
UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000, 'admin', 'admin', 0, null, null);
INSERT INTO project (id, num, organization_id, name, description, create_user, update_user, create_time, update_time)
VALUES ('project-associate-case-test', null, 'organization-associate-case-test', '用例评论项目', '系统默认创建的项目',
'admin', 'admin', UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000);
INSERT INTO functional_case(id, num, module_id, project_id, template_id, name, review_status, tags, case_edit_type, pos, version_id, ref_id, last_execute_result, deleted, public_case, latest, create_user, update_user, delete_user, create_time, update_time, delete_time)
VALUES ('gyq_associate_case_id_1', 100, 'TEST_MODULE_ID', 'project-associate-case-test', '100001', '测试', 'UN_REVIEWED', NULL, 'STEP', 0, 'v1.0.0', 'v1.0.0', 'UN_EXECUTED', b'0', b'0', b'1', 'admin', 'admin', '', 1698058347559, 1698058347559, NULL);
INSERT INTO functional_case_blob(id, steps, text_description, expected_result, prerequisite, description) VALUES ('gyq_associate_case_id_1', 'STEP', '1111', '', '', 'TEST');
INSERT INTO api_test_case(id, name, priority, num, tags, status, last_report_status, last_report_id, pos, project_id, api_definition_id, version_id, environment_id, create_time, create_user, update_time, update_user, delete_time, delete_user, deleted)
VALUES ('gyq_associate_api_case_id_1','gyq_associate_api_case_id_1','P0',1001, null, 'Underway', null, null, 100, 'project-associate-case-test', 'gyq_associate_api_definition_id_1', 'gyq_associate_version_id', 'gyq_associate_env_id', 1698058347559, 'admin',1698058347559,'admin',null,null,false);
INSERT INTO api_definition(id, name, protocol, method, path, status, num, tags, pos, project_id, module_id, latest, version_id, ref_id, description, create_time, create_user, update_time, update_user, delete_user, delete_time, deleted)
VALUES ('gyq_associate_api_definition_id_1', 'gyq_associate_api_definition_id_1', 'HTTP', 'POST','api/test','test-api-status', 1000001, null, 1, '100001100001' , 'test_module', true, 'v1.10','aspect_gyq_api_one', null, UNIX_TIMESTAMP() * 1000,'admin', UNIX_TIMESTAMP() * 1000,'admin', null,null,false);
INSERT INTO project_version(id, project_id, name, description, status, latest, publish_time, start_time, end_time, create_time, create_user)
values ('gyq_associate_version_id','project-associate-case-test','v1.0.0', null, 'open', 1, UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000, 'admin');

View File

@ -0,0 +1,45 @@
package io.metersphere.functional.controller;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import io.metersphere.dto.ApiTestCaseProviderDTO;
import io.metersphere.functional.service.FunctionalTestCaseService;
import io.metersphere.request.ApiTestCasePageProviderRequest;
import io.metersphere.sdk.constants.PermissionConstants;
import io.metersphere.system.security.CheckOwner;
import io.metersphere.system.utils.PageUtils;
import io.metersphere.system.utils.Pager;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authz.annotation.Logical;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@Tag(name = "用例管理-功能用例-关联其他用例")
@RestController
@RequestMapping("/functional/case/test")
public class FunctionalTestCaseController {
@Resource
private FunctionalTestCaseService functionalTestCaseService;
@PostMapping("/associate/page")
@Operation(summary = "用例管理-功能用例-关联其他用例-获取接口用例列表")
@RequiresPermissions(value = {PermissionConstants.FUNCTIONAL_CASE_READ_ADD, PermissionConstants.FUNCTIONAL_CASE_READ_UPDATE, PermissionConstants.FUNCTIONAL_CASE_READ_DELETE}, logical = Logical.OR)
@CheckOwner(resourceId = "#request.getProjectId()", resourceType = "project")
public Pager<List<ApiTestCaseProviderDTO>> associateCase(@Validated @RequestBody ApiTestCasePageProviderRequest request) {
Page<Object> page = PageHelper.startPage(request.getCurrent(), request.getPageSize(),
StringUtils.isNotBlank(request.getSortString()) ? request.getSortString() : "create_time desc");
return PageUtils.setPageInfo(page, functionalTestCaseService.page(request));
}
}

View File

@ -1,14 +0,0 @@
package io.metersphere.functional.service;
import org.springframework.stereotype.Service;
/**
* 用例评审关注人表服务实现类
*
* @date : 2023-5-17
*/
@Service
public class CaseReviewFollowService {
}

View File

@ -1,14 +0,0 @@
package io.metersphere.functional.service;
import org.springframework.stereotype.Service;
/**
* 功能用例评审和评审人的中间表服务实现类
*
* @date : 2023-5-17
*/
@Service
public class CaseReviewFunctionalCaseUserService {
}

View File

@ -1,14 +0,0 @@
package io.metersphere.functional.service;
import org.springframework.stereotype.Service;
/**
* 评审和评审人中间表服务实现类
*
* @date : 2023-5-17
*/
@Service
public class CaseReviewUserService {
}

View File

@ -1,16 +0,0 @@
/**
* @filename:FunctionalCaseBlobServiceImpl 2023年5月17日
* @project ms V3.x
* Copyright(c) 2018 wx Co. Ltd.
* All right reserved.
*/
package io.metersphere.functional.service;
import org.springframework.stereotype.Service;
@Service
public class FunctionalCaseBlobService {
}

View File

@ -1,16 +0,0 @@
/**
* @filename:FunctionalCaseRelationshipEdgeServiceImpl 2023年5月17日
* @project ms V3.x
* Copyright(c) 2018 wx Co. Ltd.
* All right reserved.
*/
package io.metersphere.functional.service;
import org.springframework.stereotype.Service;
@Service
public class FunctionalCaseRelationshipEdgeService {
}

View File

@ -0,0 +1,26 @@
package io.metersphere.functional.service;
import io.metersphere.dto.ApiTestCaseProviderDTO;
import io.metersphere.provider.BaseAssociateApiProvider;
import io.metersphere.request.ApiTestCasePageProviderRequest;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
/**
* @author guoyuqi
* 功能用例关联其他用例服务实现类
*/
@Service
@Transactional(rollbackFor = Exception.class)
public class FunctionalTestCaseService {
@Resource
private BaseAssociateApiProvider provider;
public List<ApiTestCaseProviderDTO> page(ApiTestCasePageProviderRequest request) {
return provider.getApiTestCaseList("functional_case_test", "case_id", "source_id", request);
}
}

View File

@ -0,0 +1,13 @@
package io.metersphere.functional.config;
import io.metersphere.provider.BaseAssociateApiProvider;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.boot.test.mock.mockito.MockBean;
@TestConfiguration
public class CaseTestConfiguration {
@MockBean
BaseAssociateApiProvider provider;
}

View File

@ -0,0 +1,75 @@
package io.metersphere.functional.controller;
import io.metersphere.dto.ApiTestCaseProviderDTO;
import io.metersphere.provider.BaseAssociateApiProvider;
import io.metersphere.request.ApiTestCasePageProviderRequest;
import io.metersphere.sdk.util.JSON;
import io.metersphere.system.base.BaseTest;
import io.metersphere.system.controller.handler.ResultHolder;
import jakarta.annotation.Resource;
import org.junit.jupiter.api.*;
import org.mockito.Mockito;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.web.servlet.MvcResult;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
@AutoConfigureMockMvc
public class FunctionalTestCaseControllerTests extends BaseTest {
private static final String URL_CASE_PAGE = "/functional/case/test/associate/page";
@Resource
BaseAssociateApiProvider provider;
@Test
@Order(1)
public void getPageSuccess() throws Exception {
ApiTestCasePageProviderRequest request = new ApiTestCasePageProviderRequest();
request.setSourceId("gyq_associate_case_id_1");
request.setProjectId("project_gyq_associate_test");
request.setCurrent(1);
request.setPageSize(10);
request.setSort(new HashMap<>() {{
put("createTime", "desc");
}});
ApiTestCaseProviderDTO apiTestCaseProviderDTO = new ApiTestCaseProviderDTO();
apiTestCaseProviderDTO.setName("第一个");
List<ApiTestCaseProviderDTO> operations = new ArrayList<>();
operations.add(apiTestCaseProviderDTO);
Mockito.when(provider.getApiTestCaseList("functional_case_test", "case_id", "source_id", request)).thenReturn(operations);
List<ApiTestCaseProviderDTO> apiTestCaseList = provider.getApiTestCaseList("functional_case_test", "case_id", "source_id", request);
MvcResult mvcResult = this.requestPostWithOkAndReturn(URL_CASE_PAGE, request);
String returnData = mvcResult.getResponse().getContentAsString(StandardCharsets.UTF_8);
ResultHolder resultHolder = JSON.parseObject(returnData, ResultHolder.class);
List<ApiTestCaseProviderDTO> apiTestCaseProviderDTOS = JSON.parseArray(JSON.toJSONString(resultHolder.getData()), ApiTestCaseProviderDTO.class);
Assertions.assertNotNull(apiTestCaseProviderDTOS);
System.out.println(JSON.toJSONString(apiTestCaseList));
}
@Test
@Order(2)
public void getPageSuccessT() throws Exception {
ApiTestCasePageProviderRequest request = new ApiTestCasePageProviderRequest();
request.setSourceId("gyq_associate_case_id_1");
request.setProjectId("project_gyq_associate_test");
request.setCurrent(1);
request.setPageSize(10);
List<ApiTestCaseProviderDTO> apiTestCaseList = provider.getApiTestCaseList("functional_case_test", "case_id", "source_id", request);
MvcResult mvcResult = this.requestPostWithOkAndReturn(URL_CASE_PAGE, request);
String returnData = mvcResult.getResponse().getContentAsString(StandardCharsets.UTF_8);
ResultHolder resultHolder = JSON.parseObject(returnData, ResultHolder.class);
List<ApiTestCaseProviderDTO> apiTestCaseProviderDTOS = JSON.parseArray(JSON.toJSONString(resultHolder.getData()), ApiTestCaseProviderDTO.class);
Assertions.assertNotNull(apiTestCaseProviderDTOS);
System.out.println(JSON.toJSONString(apiTestCaseList));
}
}