feat(接口测试): 增加分页查询接口

This commit is contained in:
wxg0103 2023-11-16 17:08:26 +08:00 committed by wxg0103
parent d67cd54ac7
commit 030259d11e
16 changed files with 671 additions and 43 deletions

View File

@ -11,8 +11,6 @@ public class MsFileUtils {
public static final String PLUGIN_DIR_NAME = "plugins";
public static final String PLUGIN_DIR = DATA_ROOT_DIR + "/" + PLUGIN_DIR_NAME;
public static final String FUNCTIONAL_CASE_DIR_NAME = "functionalCase";
public static final String API_CASE_DIR_NAME = "apiCase";
public static final String API_CASE_DIR = DATA_ROOT_DIR + "/" + API_CASE_DIR_NAME;
public static final String BUG_MANAGEMENT_DIR = "bug";
public static void validateFileName(String... fileNames) {

View File

@ -58,7 +58,7 @@ public class ApiDebugController {
@Operation(summary = "更新接口调试")
@RequiresPermissions(PermissionConstants.PROJECT_API_DEBUG_UPDATE)
@Log(type = OperationLogType.UPDATE, expression = "#msClass.updateLog(#request)", msClass = ApiDebugLogService.class)
public ApiDebug update(@Validated @RequestBody ApiDebugUpdateRequest request) {;
public ApiDebug update(@Validated @RequestBody ApiDebugUpdateRequest request) {
return apiDebugService.update(request, SessionUtils.getUserId());
}

View File

@ -1,8 +1,12 @@
package io.metersphere.api.controller.definition;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import io.metersphere.api.domain.ApiTestCase;
import io.metersphere.api.dto.definition.ApiTestCaseAddRequest;
import io.metersphere.api.dto.definition.ApiTestCaseDTO;
import io.metersphere.api.dto.definition.ApiTestCaseUpdateRequest;
import io.metersphere.api.dto.request.ApiTestCasePageRequest;
import io.metersphere.api.service.definition.ApiTestCaseLogService;
import io.metersphere.api.service.definition.ApiTestCaseNoticeService;
import io.metersphere.api.service.definition.ApiTestCaseService;
@ -11,10 +15,13 @@ import io.metersphere.system.log.annotation.Log;
import io.metersphere.system.log.constants.OperationLogType;
import io.metersphere.system.notice.annotation.SendNotice;
import io.metersphere.system.notice.constants.NoticeConstants;
import io.metersphere.system.utils.PageUtils;
import io.metersphere.system.utils.Pager;
import io.metersphere.system.utils.SessionUtils;
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.RequiresPermissions;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
@ -86,4 +93,30 @@ public class ApiTestCaseController {
apiTestCaseService.delete(id);
}
@PostMapping(value = "/update")
@Operation(summary = "接口测试-接口管理-接口用例-更新")
@RequiresPermissions(PermissionConstants.PROJECT_API_DEFINITION_CASE_UPDATE)
@Log(type = OperationLogType.UPDATE, expression = "#msClass.updateLog(#request)", msClass = ApiTestCaseLogService.class)
public ApiTestCase update(@Validated @RequestPart("request") ApiTestCaseUpdateRequest request, @RequestPart(value = "files", required = false) List<MultipartFile> files) {
return apiTestCaseService.update(request, files, SessionUtils.getUserId());
}
@GetMapping(value = "/update-status/{id}/{status}")
@Operation(summary = "接口测试-接口管理-接口用例-更新状态")
@RequiresPermissions(PermissionConstants.PROJECT_API_DEFINITION_CASE_UPDATE)
@Log(type = OperationLogType.UPDATE, expression = "#msClass.updateLog(#id)", msClass = ApiTestCaseLogService.class)
public void updateStatus(@PathVariable String id, @PathVariable String status) {
apiTestCaseService.updateStatus(id, status, SessionUtils.getUserId());
}
@PostMapping(value = "/page")
@Operation(summary = "接口测试-接口管理-接口用例-分页查询")
@RequiresPermissions(PermissionConstants.PROJECT_API_DEFINITION_CASE_READ)
public Pager<List<ApiTestCaseDTO>> page(@Validated @RequestBody ApiTestCasePageRequest request) {
Page<Object> page = PageHelper.startPage(request.getCurrent(), request.getPageSize(),
StringUtils.isNotBlank(request.getSortString()) ? request.getSortString() : "create_time desc");
return PageUtils.setPageInfo(page, apiTestCaseService.page(request));
}
}

View File

@ -8,10 +8,6 @@ import lombok.Data;
import java.io.Serializable;
import java.util.List;
/**
* @Author: jianxing
* @CreateTime: 2023-11-06 10:22
*/
@Data
public class ApiTestCaseAddRequest implements Serializable {

View File

@ -1,14 +1,86 @@
package io.metersphere.api.dto.definition;
import io.metersphere.api.domain.ApiTestCase;
import io.metersphere.plugin.api.spi.AbstractMsTestElement;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
@Data
public class ApiTestCaseDTO extends ApiTestCase {
public class ApiTestCaseDTO {
@Schema(description = "接口用例pk")
private String id;
@Schema(description = "接口用例名称")
private String name;
@Schema(description = "用例等级")
private String priority;
@Schema(description = "接口用例编号id")
private Long num;
@Schema(description = "用例状态")
private String status;
@Schema(description = "最新执行结果状态")
private String lastReportStatus;
@Schema(description = "最后执行结果报告fk")
private String lastReportId;
@Schema(description = "项目fk")
private String projectId;
@Schema(description = "接口fk")
private String apiDefinitionId;
@Schema(description = "环境fk")
private String environmentId;
@Schema(description = "创建时间")
private Long createTime;
@Schema(description = "创建人")
private String createUser;
@Schema(description = "更新时间")
private Long updateTime;
@Schema(description = "更新人")
private String updateUser;
@Schema(description = "删除时间")
private Long deleteTime;
@Schema(description = "删除人")
private String deleteUser;
@Schema(description = "是否关注")
private Boolean follow;
@Schema(description = "请求方法")
private String method;
@Schema(description = "请求路径")
private String path;
@Schema(description = "环境名称")
private String environmentName;
@Schema(description = "创建人")
private String createName;
@Schema(description = "更新人")
private String updateName;
@Schema(description = "删除人")
private String deleteName;
@Schema(description = "标签")
private List<String> tags = new ArrayList<>();
@Schema(description = "请求内容")
private AbstractMsTestElement request;
}

View File

@ -0,0 +1,48 @@
package io.metersphere.api.dto.definition;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
@Data
public class ApiTestCaseUpdateRequest implements Serializable {
private static final long serialVersionUID = 1L;
@Schema(description = "用例Id", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{api_debug.project_id.not_blank}")
@Size(min = 1, max = 50, message = "{api_debug.project_id.length_range}")
private String id;
@Schema(description = "用例名称", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{api_debug.name.not_blank}")
@Size(min = 1, max = 255, message = "{api_debug.name.length_range}")
private String name;
@Schema(description = "用例等级", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{api_test_case.priority.not_blank}")
@Size(min = 1, max = 50, message = "{api_test_case.priority.length_range}")
private String priority;
@Schema(description = "用例状态 Underway Prepare Completed", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{api_test_case.status.not_blank}")
@Size(min = 1, max = 20, message = "{api_test_case.status.length_range}")
private String status;
@Schema(description = "标签")
private List<
@NotBlank
String> tags;
@Schema(description = "环境fk")
private String environmentId;
@Schema(description = "请求内容")
@NotBlank
private String request;
}

View File

@ -0,0 +1,44 @@
package io.metersphere.api.dto.request;
import io.metersphere.sdk.constants.ModuleConstants;
import io.metersphere.system.dto.sdk.BasePageRequest;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.List;
/**
* @author lan
*/
@Data
@EqualsAndHashCode(callSuper = false)
public class ApiTestCasePageRequest extends BasePageRequest {
private static final long serialVersionUID = 1L;
@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<String> moduleIds;
@Schema(description = "版本fk")
private String versionId;
@Schema(description = "版本来源")
private String refId;
}

View File

@ -1,9 +1,13 @@
package io.metersphere.api.mapper;
import io.metersphere.api.dto.definition.ApiTestCaseDTO;
import io.metersphere.api.dto.request.ApiTestCasePageRequest;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* @author jianxing
* @date : 2023-11-6
@ -12,4 +16,6 @@ import org.apache.ibatis.annotations.Param;
public interface ExtApiTestCaseMapper {
Long getPos(@Param("projectId") String projectId);
List<ApiTestCaseDTO> listByRequest(@Param("request") ApiTestCasePageRequest request, @Param("deleted") boolean deleted);
}

View File

@ -11,4 +11,115 @@
LIMIT 1;
</select>
<select id="listByRequest" resultType="io.metersphere.api.dto.definition.ApiTestCaseDTO">
SELECT
t1.id,
t1.project_id,
t1.name,
t1.status,
t1.api_definition_id,
t1.priority,
t1.create_user,
t1.update_user,
t1.create_time,
t1.update_time,
t1.num,
t1.last_report_status,
t1.last_report_id,
t1.delete_time,
t1.environment_id
FROM
api_test_case t1
LEFT JOIN api_report t3 ON t1.last_report_id = t3.id
INNER JOIN api_definition a ON t1.api_definition_id = a.id
WHERE t1.deleted =#{deleted}
<include refid="queryWhereCondition"/>
</select>
<sql id="queryWhereCondition">
<if test="request.protocol != null and request.protocol!=''">
and a.protocol = #{request.protocol}
</if>
<if test="request.apiDefinitionId != null and request.apiDefinitionId!=''">
and t1.api_definition_id = #{request.apiDefinitionId}
</if>
<if test="request.projectId != null and request.projectId!=''">
and t1.project_id = #{request.projectId}
</if>
<if test="request.keyword != null">
and (
t1.name like concat('%', #{request.keyword},'%')
or t1.num like concat('%', #{request.keyword},'%')
or t1.tags like JSON_CONTAINS(t1.tags, concat('["',#{request.keyword},'"]'))
)
</if>
<if test="request.moduleIds != null and request.moduleIds.size() > 0">
and a.module_id in
<foreach collection="request.moduleIds" item="nodeId" separator="," open="(" close=")">
#{nodeId}
</foreach>
</if>
<include refid="filters">
<property name="filter" value="request.filter"/>
</include>
<include refid="queryVersionCondition">
<property name="versionTable" value="t1"/>
</include>
</sql>
<sql id="filters">
<if test="request.filter != null and request.filter.size() > 0">
<foreach collection="request.filter.entrySet()" index="key" item="values">
<if test="values != null and values.size() > 0">
<choose>
<when test="key == 'priority'">
and t1.priority in
<foreach collection="values" item="value" separator="," open="(" close=")">
#{value}
</foreach>
</when>
<when test="key=='status'">
and t1.status in
<foreach collection="values" item="value" separator="," open="(" close=")">
#{value}
</foreach>
</when>
<when test="key=='caseStatus' or key=='case_status'">
and t1.case_status in
<foreach collection="values" item="value" separator="," open="(" close=")">
#{value}
</foreach>
</when>
<when test="key=='last_execute_result'">
and t1.last_execute_result in
<foreach collection="values" item="value" separator="," open="(" close=")">
#{value}
</foreach>
</when>
<when test="key=='create_user' or key=='createUser'">
and t1.create_user in
<foreach collection="values" item="value" separator="," open="(" close=")">
(#{value})
</foreach>
</when>
<when test="key=='update_user' or key=='updateUser'">
and t1.update_user in
<foreach collection="values" item="value" separator="," open="(" close=")">
(#{value})
</foreach>
</when>
</choose>
</if>
</foreach>
</if>
</sql>
<sql id="queryVersionCondition">
<if test="request.versionId != null">
and ${versionTable}.version_id = #{request.versionId}
</if>
<if test="request.versionId == null and request.apiDefinitionId == null">
AND a.latest = 1
</if>
</sql>
</mapper>

View File

@ -2,6 +2,7 @@ package io.metersphere.api.service.definition;
import io.metersphere.api.domain.ApiTestCase;
import io.metersphere.api.dto.definition.ApiTestCaseAddRequest;
import io.metersphere.api.dto.definition.ApiTestCaseUpdateRequest;
import io.metersphere.api.mapper.ApiTestCaseMapper;
import io.metersphere.project.domain.Project;
import io.metersphere.project.mapper.ProjectMapper;
@ -135,4 +136,40 @@ public class ApiTestCaseLogService {
return dto;
}
public LogDTO updateLog(ApiTestCaseUpdateRequest request) {
ApiTestCase apiTestCase = apiTestCaseMapper.selectByPrimaryKey(request.getId());
Project project = projectMapper.selectByPrimaryKey(apiTestCase.getProjectId());
LogDTO dto = new LogDTO(
apiTestCase.getProjectId(),
project.getOrganizationId(),
request.getId(),
null,
OperationLogType.UPDATE.name(),
OperationLogModule.API_DEFINITION_CASE,
request.getName());
dto.setPath("/api/testCase/update");
dto.setMethod(HttpMethodConstants.POST.name());
dto.setOriginalValue(JSON.toJSONBytes(request));
return dto;
}
public LogDTO updateLog(String id) {
ApiTestCase apiTestCase = apiTestCaseMapper.selectByPrimaryKey(id);
Project project = projectMapper.selectByPrimaryKey(apiTestCase.getProjectId());
LogDTO dto = new LogDTO(
apiTestCase.getProjectId(),
project.getOrganizationId(),
id,
null,
OperationLogType.UPDATE.name(),
OperationLogModule.API_DEFINITION_CASE,
apiTestCase.getName());
dto.setPath("/api/testCase/update");
dto.setMethod(HttpMethodConstants.POST.name());
dto.setOriginalValue(JSON.toJSONBytes(apiTestCase));
return dto;
}
}

View File

@ -3,6 +3,8 @@ package io.metersphere.api.service.definition;
import io.metersphere.api.domain.*;
import io.metersphere.api.dto.definition.ApiTestCaseAddRequest;
import io.metersphere.api.dto.definition.ApiTestCaseDTO;
import io.metersphere.api.dto.definition.ApiTestCaseUpdateRequest;
import io.metersphere.api.dto.request.ApiTestCasePageRequest;
import io.metersphere.api.mapper.*;
import io.metersphere.api.util.ApiDataUtils;
import io.metersphere.plugin.api.spi.AbstractMsTestElement;
@ -10,24 +12,39 @@ import io.metersphere.project.domain.Project;
import io.metersphere.project.mapper.ProjectMapper;
import io.metersphere.sdk.constants.ApplicationNumScope;
import io.metersphere.sdk.constants.StorageType;
import io.metersphere.sdk.domain.Environment;
import io.metersphere.sdk.domain.EnvironmentExample;
import io.metersphere.sdk.exception.MSException;
import io.metersphere.sdk.util.*;
import io.metersphere.sdk.mapper.EnvironmentMapper;
import io.metersphere.sdk.util.BeanUtils;
import io.metersphere.sdk.util.JSON;
import io.metersphere.sdk.util.LogUtils;
import io.metersphere.sdk.util.Translator;
import io.metersphere.system.file.FileRequest;
import io.metersphere.system.file.MinioRepository;
import io.metersphere.system.service.UserLoginService;
import io.metersphere.system.uid.IDGenerator;
import io.metersphere.system.uid.NumGenerator;
import jakarta.annotation.Resource;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@Service
public class ApiTestCaseService {
public static final Long ORDER_STEP = 5000L;
private static final String MAIN_FOLDER_PROJECT = "project";
private static final String APP_NAME_API_CASE = "apiCase";
@Resource
private ApiTestCaseMapper apiTestCaseMapper;
@Resource
@ -42,6 +59,10 @@ public class ApiTestCaseService {
private ApiTestCaseFollowerMapper apiTestCaseFollowerMapper;
@Resource
private MinioRepository minioRepository;
@Resource
private UserLoginService userLoginService;
@Resource
private EnvironmentMapper environmentMapper;
public ApiTestCase addCase(ApiTestCaseAddRequest request, List<MultipartFile> files, String userId) {
ApiTestCase testCase = new ApiTestCase();
@ -114,7 +135,7 @@ public class ApiTestCaseService {
files.forEach(file -> {
FileRequest fileRequest = new FileRequest();
fileRequest.setFileName(file.getName());
fileRequest.setProjectId(StringUtils.join(MsFileUtils.API_CASE_DIR, projectId));
fileRequest.setFolder(minioPath(projectId));
fileRequest.setResourceId(caseId);
fileRequest.setStorage(StorageType.MINIO.name());
try {
@ -140,6 +161,14 @@ public class ApiTestCaseService {
ApiTestCase testCase = checkResourceExist(id);
ApiTestCaseBlob testCaseBlob = apiTestCaseBlobMapper.selectByPrimaryKey(id);
BeanUtils.copyBean(apiTestCaseDTO, testCase);
if (StringUtils.isNotBlank(testCase.getTags())) {
apiTestCaseDTO.setTags(JSON.parseArray(testCase.getTags(), String.class));
} else {
apiTestCaseDTO.setTags(new ArrayList<>());
}
ApiDefinition apiDefinition = getApiDefinition(testCase.getApiDefinitionId());
apiTestCaseDTO.setMethod(apiDefinition.getMethod());
apiTestCaseDTO.setPath(apiDefinition.getPath());
ApiTestCaseFollowerExample example = new ApiTestCaseFollowerExample();
example.createCriteria().andCaseIdEqualTo(id).andUserIdEqualTo(userId);
List<ApiTestCaseFollower> followers = apiTestCaseFollowerMapper.selectByExample(example);
@ -197,12 +226,76 @@ public class ApiTestCaseService {
apiTestCaseFollowerMapper.deleteByExample(example);
try {
FileRequest request = new FileRequest();
request.setProjectId(StringUtils.join(MsFileUtils.API_CASE_DIR, id));
request.setProjectId(StringUtils.join(MsFileUtils.API_CASE_DIR, apiCase.getProjectId()));
request.setFolder(minioPath(apiCase.getProjectId()));
request.setResourceId(id);
minioRepository.deleteFolder(request);
} catch (Exception e) {
LogUtils.info("删除body文件失败: 文件名称:" + id, e);
}
}
public ApiTestCase update(ApiTestCaseUpdateRequest request, List<MultipartFile> files, String userId) {
ApiTestCase testCase = checkResourceExist(request.getId());
BeanUtils.copyBean(testCase, request);
checkNameExist(testCase);
testCase.setUpdateUser(userId);
testCase.setUpdateTime(System.currentTimeMillis());
if (CollectionUtils.isNotEmpty(request.getTags())) {
testCase.setTags(JSON.toJSONString(request.getTags()));
} else {
testCase.setTags(null);
}
apiTestCaseMapper.updateByPrimaryKey(testCase);
ApiTestCaseBlob apiTestCaseBlob = new ApiTestCaseBlob();
apiTestCaseBlob.setId(request.getId());
apiTestCaseBlob.setRequest(request.getRequest().getBytes());
apiTestCaseBlobMapper.updateByPrimaryKeySelective(apiTestCaseBlob);
uploadBodyFile(files, request.getId(), testCase.getProjectId());
return testCase;
}
public void updateStatus(String id, String status, String userId) {
checkResourceExist(id);
ApiTestCase update = new ApiTestCase();
update.setId(id);
update.setStatus(status);
update.setUpdateUser(userId);
update.setUpdateTime(System.currentTimeMillis());
apiTestCaseMapper.updateByPrimaryKeySelective(update);
}
public List<ApiTestCaseDTO> page(ApiTestCasePageRequest request) {
List<ApiTestCaseDTO> apiCaseLists = extApiTestCaseMapper.listByRequest(request, false);
buildApiTestCaseDTO(apiCaseLists);
return apiCaseLists;
}
private void buildApiTestCaseDTO(List<ApiTestCaseDTO> apiCaseLists) {
if (CollectionUtils.isNotEmpty(apiCaseLists)) {
List<String> userIds = new ArrayList<>();
userIds.addAll(apiCaseLists.stream().map(ApiTestCaseDTO::getCreateUser).toList());
userIds.addAll(apiCaseLists.stream().map(ApiTestCaseDTO::getUpdateUser).toList());
userIds.addAll(apiCaseLists.stream().map(ApiTestCaseDTO::getDeleteUser).toList());
Map<String, String> userMap = userLoginService.getUserNameMap(userIds.stream().filter(StringUtils::isNotBlank).distinct().toList());
List<String> envIds = apiCaseLists.stream().map(ApiTestCaseDTO::getEnvironmentId).toList();
EnvironmentExample environmentExample = new EnvironmentExample();
environmentExample.createCriteria().andIdIn(envIds);
List<Environment> environments = environmentMapper.selectByExample(environmentExample);
Map<String, String> envMap = environments.stream().collect(Collectors.toMap(Environment::getId, Environment::getName));
apiCaseLists.forEach(apiCase -> {
apiCase.setCreateName(userMap.get(apiCase.getCreateUser()));
apiCase.setUpdateName(userMap.get(apiCase.getUpdateUser()));
apiCase.setDeleteName(userMap.get(apiCase.getDeleteUser()));
if (StringUtils.isNotBlank(apiCase.getEnvironmentId())
&& MapUtils.isNotEmpty(envMap)
&& envMap.containsKey(apiCase.getEnvironmentId())) {
apiCase.setEnvironmentName(envMap.get(apiCase.getEnvironmentId()));
}
});
}
}
private String minioPath(String projectId) {
return StringUtils.join(MAIN_FOLDER_PROJECT, "/", projectId, "/", APP_NAME_API_CASE);
}
}

View File

@ -4,18 +4,28 @@ import io.metersphere.api.controller.param.ApiTestCaseAddRequestDefinition;
import io.metersphere.api.domain.*;
import io.metersphere.api.dto.definition.ApiTestCaseAddRequest;
import io.metersphere.api.dto.definition.ApiTestCaseDTO;
import io.metersphere.api.dto.definition.ApiTestCaseUpdateRequest;
import io.metersphere.api.dto.request.ApiTestCasePageRequest;
import io.metersphere.api.mapper.*;
import io.metersphere.api.util.ApiDataUtils;
import io.metersphere.plugin.api.spi.AbstractMsTestElement;
import io.metersphere.sdk.constants.ApplicationNumScope;
import io.metersphere.sdk.constants.PermissionConstants;
import io.metersphere.sdk.constants.SessionConstants;
import io.metersphere.sdk.domain.Environment;
import io.metersphere.sdk.domain.EnvironmentExample;
import io.metersphere.sdk.dto.api.request.http.MsHTTPElement;
import io.metersphere.sdk.mapper.OperationLogMapper;
import io.metersphere.sdk.mapper.EnvironmentMapper;
import io.metersphere.sdk.util.BeanUtils;
import io.metersphere.sdk.util.CommonBeanFactory;
import io.metersphere.sdk.util.JSON;
import io.metersphere.system.base.BaseTest;
import io.metersphere.system.controller.handler.ResultHolder;
import io.metersphere.system.file.FileRequest;
import io.metersphere.system.file.MinioRepository;
import io.metersphere.system.log.constants.OperationLogType;
import io.metersphere.system.uid.NumGenerator;
import io.metersphere.system.utils.Pager;
import jakarta.annotation.Resource;
import org.apache.commons.collections.CollectionUtils;
import org.junit.jupiter.api.*;
@ -25,19 +35,20 @@ import org.springframework.http.MediaType;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.ResultMatcher;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.util.LinkedMultiValueMap;
import org.testcontainers.shaded.org.apache.commons.lang3.StringUtils;
import java.io.File;
import java.io.FileInputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
/**
* @author jianxing
* @date : 2023-11-7
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureMockMvc
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
@ -50,8 +61,13 @@ public class ApiTestCaseControllerTests extends BaseTest {
private static final String FOLLOW = BASE_PATH + "follow/";
private static final String UNFOLLOW = BASE_PATH + "unfollow/";
private static final String DELETE = BASE_PATH + "delete/";
private static final String UPDATE = BASE_PATH + "update";
private static final String PAGE = BASE_PATH + "page";
private static final String UPDATE_STATUS = BASE_PATH + "update-status";
private static final ResultMatcher ERROR_REQUEST_MATCHER = status().is5xxServerError();
private static ApiTestCase apiTestCase;
private static ApiTestCase anotherApiTestCase;
@Resource
private ApiDefinitionMapper apiDefinitionMapper;
@Resource
@ -63,7 +79,29 @@ public class ApiTestCaseControllerTests extends BaseTest {
@Resource
private ApiTestCaseFollowerMapper apiTestCaseFollowerMapper;
@Resource
private OperationLogMapper operationLogMapper;
private EnvironmentMapper environmentMapper;
public static <T> T parseObjectFromMvcResult(MvcResult mvcResult, Class<T> parseClass) {
try {
String returnData = mvcResult.getResponse().getContentAsString(StandardCharsets.UTF_8);
ResultHolder resultHolder = JSON.parseObject(returnData, ResultHolder.class);
//返回请求正常
Assertions.assertNotNull(resultHolder);
return JSON.parseObject(JSON.toJSONString(resultHolder.getData()), parseClass);
} catch (Exception ignore) {
}
return null;
}
private MvcResult responsePost(String url, Object param) throws Exception {
return mockMvc.perform(MockMvcRequestBuilders.post(url)
.header(SessionConstants.HEADER_TOKEN, sessionId)
.header(SessionConstants.CSRF_TOKEN, csrfToken)
.content(JSON.toJSONString(param))
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON)).andReturn();
}
public void initApiData() {
ApiDefinition apiDefinition = new ApiDefinition();
@ -90,12 +128,19 @@ public class ApiTestCaseControllerTests extends BaseTest {
apiDefinitionBlob.setRequest(new byte[0]);
apiDefinitionBlob.setResponse(new byte[0]);
apiDefinitionBlobMapper.insertSelective(apiDefinitionBlob);
apiDefinition.setId("apiDefinitionId1");
apiDefinition.setModuleId("moduleId1");
apiDefinitionMapper.insertSelective(apiDefinition);
}
@Test
@Order(1)
public void add() throws Exception {
initApiData();
EnvironmentExample environmentExample = new EnvironmentExample();
environmentExample.createCriteria().andProjectIdEqualTo(DEFAULT_PROJECT_ID).andMockEqualTo(true);
List<Environment> environments = environmentMapper.selectByExample(environmentExample);
// @@请求成功
ApiTestCaseAddRequest request = new ApiTestCaseAddRequest();
request.setApiDefinitionId("apiDefinitionId");
@ -103,8 +148,8 @@ public class ApiTestCaseControllerTests extends BaseTest {
request.setProjectId(DEFAULT_PROJECT_ID);
request.setPriority("P0");
request.setStatus("Underway");
request.setRequest("request");
request.setTags(List.of("tag1", "tag2"));
request.setEnvironmentId(environments.get(0).getId());
MsHTTPElement msHttpElement = MsHTTPElementTest.getMsHttpElement();
request.setRequest(ApiDataUtils.toJSONString(msHttpElement));
LinkedMultiValueMap<String, Object> paramMap = new LinkedMultiValueMap<>();
@ -121,10 +166,12 @@ public class ApiTestCaseControllerTests extends BaseTest {
// 再插入一条数据便于修改时重名校验
request.setName("test1");
request.setTags(new ArrayList<>());
paramMap.clear();
paramMap.add("request", JSON.toJSONString(request));
mvcResult = this.requestMultipartWithOkAndReturn(ADD, paramMap);
resultData = getResultData(mvcResult, ApiTestCase.class);
anotherApiTestCase = apiTestCaseMapper.selectByPrimaryKey(resultData.getId());
assertUpdateApiDebug(request, msHttpElement, resultData.getId());
// @@重名校验异常
@ -176,7 +223,16 @@ public class ApiTestCaseControllerTests extends BaseTest {
ApiDataUtils.setResolver(MsHTTPElement.class);
ApiTestCaseDTO apiDebugDTO = ApiDataUtils.parseObject(JSON.toJSONString(parseResponse(mvcResult).get("data")), ApiTestCaseDTO.class);
// 校验数据是否正确
ApiTestCaseDTO copyApiDebugDTO = BeanUtils.copyBean(new ApiTestCaseDTO(), apiTestCaseMapper.selectByPrimaryKey(apiTestCase.getId()));
ApiTestCase testCase = apiTestCaseMapper.selectByPrimaryKey(apiTestCase.getId());
ApiTestCaseDTO copyApiDebugDTO = BeanUtils.copyBean(new ApiTestCaseDTO(), testCase);
if (StringUtils.isNotEmpty(testCase.getTags())) {
copyApiDebugDTO.setTags(JSON.parseArray(testCase.getTags(), String.class));
} else {
copyApiDebugDTO.setTags(new ArrayList<>());
}
ApiDefinition apiDefinition = apiDefinitionMapper.selectByPrimaryKey(apiTestCase.getApiDefinitionId());
copyApiDebugDTO.setMethod(apiDefinition.getMethod());
copyApiDebugDTO.setPath(apiDefinition.getPath());
ApiTestCaseBlob apiDebugBlob = apiTestCaseBlobMapper.selectByPrimaryKey(apiTestCase.getId());
ApiTestCaseFollowerExample example = new ApiTestCaseFollowerExample();
example.createCriteria().andCaseIdEqualTo(apiTestCase.getId()).andUserIdEqualTo("admin");
@ -184,6 +240,8 @@ public class ApiTestCaseControllerTests extends BaseTest {
copyApiDebugDTO.setFollow(CollectionUtils.isNotEmpty(followers));
copyApiDebugDTO.setRequest(ApiDataUtils.parseObject(new String(apiDebugBlob.getRequest()), AbstractMsTestElement.class));
Assertions.assertEquals(apiDebugDTO, copyApiDebugDTO);
this.requestGetWithOk(GET + anotherApiTestCase.getId())
.andReturn();
this.requestGet(GET + "111").andExpect(ERROR_REQUEST_MATCHER);
@ -256,6 +314,129 @@ public class ApiTestCaseControllerTests extends BaseTest {
@Test
@Order(7)
public void update() throws Exception {
// @@请求成功
ApiTestCaseUpdateRequest request = new ApiTestCaseUpdateRequest();
request.setId(apiTestCase.getId());
request.setName("update");
request.setPriority("P1");
request.setStatus("Underway");
request.setTags(List.of("tag1", "tag2"));
request.setEnvironmentId(null);
MsHTTPElement msHttpElement = MsHTTPElementTest.getMsHttpElement();
request.setRequest(ApiDataUtils.toJSONString(msHttpElement));
LinkedMultiValueMap<String, Object> paramMap = new LinkedMultiValueMap<>();
paramMap.add("request", JSON.toJSONString(request));
MvcResult mvcResult = this.requestMultipartWithOkAndReturn(UPDATE, paramMap);
// 校验请求成功数据
ApiTestCase resultData = getResultData(mvcResult, ApiTestCase.class);
assertUpdateApiDebug(request, msHttpElement, resultData.getId());
request.setTags(new ArrayList<>());
paramMap.clear();
paramMap.add("request", JSON.toJSONString(request));
mvcResult = this.requestMultipartWithOkAndReturn(UPDATE, paramMap);
resultData = getResultData(mvcResult, ApiTestCase.class);
assertUpdateApiDebug(request, msHttpElement, resultData.getId());
// @@重名校验异常
request.setName("update");
request.setId(anotherApiTestCase.getId());
paramMap.clear();
paramMap.add("request", JSON.toJSONString(request));
this.requestMultipart(UPDATE, paramMap).andExpect(ERROR_REQUEST_MATCHER);
// 校验接口是否存在
request.setId("111");
paramMap.clear();
paramMap.add("request", JSON.toJSONString(request));
this.requestMultipart(UPDATE, paramMap).andExpect(ERROR_REQUEST_MATCHER);
// @@校验日志
checkLog(apiTestCase.getId(), OperationLogType.UPDATE);
// @@异常参数校验
createdGroupParamValidateTest(ApiTestCaseUpdateRequest.class, UPDATE);
// @@校验权限
requestMultipartPermissionTest(PermissionConstants.PROJECT_API_DEFINITION_CASE_UPDATE, UPDATE, paramMap);
}
@Test
@Order(8)
public void page() throws Exception {
// @@请求成功
ApiTestCaseAddRequest request = new ApiTestCaseAddRequest();
request.setApiDefinitionId("apiDefinitionId1");
request.setName("testApiDefinitionId1");
request.setProjectId(DEFAULT_PROJECT_ID);
request.setPriority("P0");
request.setStatus("Underway");
request.setTags(List.of("tag1", "tag2"));
MsHTTPElement msHttpElement = MsHTTPElementTest.getMsHttpElement();
request.setRequest(ApiDataUtils.toJSONString(msHttpElement));
LinkedMultiValueMap<String, Object> paramMap = new LinkedMultiValueMap<>();
paramMap.add("request", JSON.toJSONString(request));
this.requestMultipartWithOkAndReturn(ADD, paramMap);
ApiTestCasePageRequest pageRequest = new ApiTestCasePageRequest();
pageRequest.setProjectId(DEFAULT_PROJECT_ID);
pageRequest.setPageSize(10);
pageRequest.setCurrent(1);
MvcResult mvcResult = responsePost(PAGE, pageRequest);
Pager<?> returnPager = parseObjectFromMvcResult(mvcResult, Pager.class);
//返回值不为空
Assertions.assertNotNull(returnPager);
//返回值的页码和当前页码相同
Assertions.assertEquals(returnPager.getCurrent(), pageRequest.getCurrent());
//返回的数据量不超过规定要返回的数据量相同
Assertions.assertTrue(((List<ApiTestCaseDTO>) returnPager.getList()).size() <= pageRequest.getPageSize());
//查询apiDefinitionId1的数据
pageRequest.setApiDefinitionId("apiDefinitionId1");
mvcResult = responsePost(PAGE, pageRequest);
returnPager = parseObjectFromMvcResult(mvcResult, Pager.class);
//返回值不为空
Assertions.assertNotNull(returnPager);
//返回值的页码和当前页码相同
Assertions.assertEquals(returnPager.getCurrent(), pageRequest.getCurrent());
List<ApiTestCaseDTO> caseDTOS = JSON.parseArray(JSON.toJSONString(returnPager.getList()), ApiTestCaseDTO.class);
caseDTOS.forEach(caseDTO -> Assertions.assertEquals(caseDTO.getApiDefinitionId(), "apiDefinitionId1"));
//查询模块为moduleId1的数据
pageRequest.setApiDefinitionId(null);
pageRequest.setModuleIds(List.of("moduleId1"));
mvcResult = responsePost(PAGE, pageRequest);
returnPager = parseObjectFromMvcResult(mvcResult, Pager.class);
//返回值不为空
Assertions.assertNotNull(returnPager);
//返回值的页码和当前页码相同
Assertions.assertEquals(returnPager.getCurrent(), pageRequest.getCurrent());
caseDTOS = JSON.parseArray(JSON.toJSONString(returnPager.getList()), ApiTestCaseDTO.class);
caseDTOS.forEach(caseDTO -> Assertions.assertEquals(apiDefinitionMapper.selectByPrimaryKey(caseDTO.getApiDefinitionId()).getModuleId(), "moduleId1"));
pageRequest.setModuleIds(null);
pageRequest.setSort(new HashMap<>() {{
put("createTime", "asc");
}});
responsePost(PAGE, pageRequest);
//校验权限
requestPostPermissionTest(PermissionConstants.PROJECT_API_DEFINITION_CASE_READ, PAGE, pageRequest);
}
@Test
@Order(9)
public void updateStatus() throws Exception {
// @@请求成功
this.requestGetWithOk(UPDATE_STATUS + "/" + apiTestCase.getId() + "/Underway");
ApiTestCase apiCase = apiTestCaseMapper.selectByPrimaryKey(apiTestCase.getId());
Assertions.assertEquals(apiCase.getStatus(), "Underway");
// @@校验日志
checkLog(apiTestCase.getId(), OperationLogType.UPDATE);
this.requestGet(UPDATE_STATUS + "/" + "11111" + "/Underway").andExpect(ERROR_REQUEST_MATCHER);
// @@校验权限
requestGetPermissionTest(PermissionConstants.PROJECT_API_DEFINITION_CASE_UPDATE, UPDATE_STATUS + "/" + apiTestCase.getId() + "/Underway");
}
@Test
@Order(20)
public void delete() throws Exception {
// @@请求成功
this.requestGetWithOk(DELETE + apiTestCase.getId());
@ -266,6 +447,15 @@ public class ApiTestCaseControllerTests extends BaseTest {
example.createCriteria().andCaseIdEqualTo(apiTestCase.getId());
List<ApiTestCaseFollower> followers = apiTestCaseFollowerMapper.selectByExample(example);
Assertions.assertTrue(CollectionUtils.isEmpty(followers));
//校验minio文件为空
FileRequest request = new FileRequest();
request.setFolder("/meterSphere/" + DEFAULT_PROJECT_ID + "/apiCase/");
request.setResourceId(apiTestCase.getId());
MinioRepository minioRepository = CommonBeanFactory.getBean(MinioRepository.class);
assert minioRepository != null;
List<String> fileNames = minioRepository.getFolderFileNames(request);
//校验文件
Assertions.assertEquals(0, fileNames.size());
// @@校验日志
checkLog(apiTestCase.getId(), OperationLogType.DELETE);
this.requestGet(DELETE + "111").andExpect(ERROR_REQUEST_MATCHER);
@ -273,4 +463,5 @@ public class ApiTestCaseControllerTests extends BaseTest {
requestGetPermissionTest(PermissionConstants.PROJECT_API_DEFINITION_CASE_DELETE, DELETE + apiTestCase.getId());
}
}

View File

@ -63,12 +63,14 @@ public class EnvironmentService {
@Resource
private ExtEnvironmentMapper extEnvironmentMapper;
private static final String DIR_PATH = "/project-management/environment/";
private static final String USERNAME = "user";
private static final String PASSWORD = "password";
private static final String PATH = "/project/environment/import";
private static final String MOCK_EVN_SOCKET = "/api/mock/";
private static final String MAIN_FOLDER_PROJECT = "project";
private static final String APP_NAME_ENVIRONMENT = "environment";
public List<OptionDTO> getDriverOptions(String organizationId) {
return jdbcDriverPluginService.getJdbcDriverOption(organizationId);
}
@ -95,7 +97,7 @@ public class EnvironmentService {
}
//删除环境的ssl文件
FileRequest fileRequest = new FileRequest();
fileRequest.setProjectId(StringUtils.join(DIR_PATH, environment.getProjectId()));
fileRequest.setFolder(minioEnvPath(environment.getProjectId()));
fileRequest.setResourceId(id);
try {
minioRepository.deleteFolder(fileRequest);
@ -128,11 +130,16 @@ public class EnvironmentService {
environmentBlob.setId(environment.getId());
environmentBlob.setConfig(JSON.toJSONBytes(request.getConfig()));
environmentBlobMapper.insert(environmentBlob);
uploadFileToMinio(sslFiles, environment);
return request;
}
private void uploadFileToMinio(List<MultipartFile> sslFiles, Environment environment) {
if (CollectionUtils.isNotEmpty(sslFiles)) {
sslFiles.forEach(sslFile -> {
FileRequest fileRequest = new FileRequest();
fileRequest.setFileName(sslFile.getOriginalFilename());
fileRequest.setProjectId(StringUtils.join(DIR_PATH, environment.getProjectId()));
fileRequest.setFolder(minioEnvPath(environment.getProjectId()));
fileRequest.setFileName(sslFile.getName());
fileRequest.setResourceId(environment.getId());
try {
minioRepository.saveFile(sslFile, fileRequest);
@ -142,7 +149,6 @@ public class EnvironmentService {
}
});
}
return request;
}
public EnvironmentRequest get(String environmentId) {
@ -296,20 +302,12 @@ public class EnvironmentService {
environmentBlob.setId(environment.getId());
environmentBlob.setConfig(JSON.toJSONBytes(request.getConfig()));
environmentBlobMapper.updateByPrimaryKeySelective(environmentBlob);
if (CollectionUtils.isNotEmpty(sslFiles)) {
sslFiles.forEach(sslFile -> {
FileRequest fileRequest = new FileRequest();
fileRequest.setFileName(sslFile.getOriginalFilename());
fileRequest.setProjectId(StringUtils.join(DIR_PATH, environment.getProjectId()));
fileRequest.setResourceId(request.getId());
try {
minioRepository.saveFile(sslFile, fileRequest);
} catch (Exception e) {
LogUtils.info("上传ssl文件失败: 文件名称:" + sslFile.getOriginalFilename(), e);
}
});
}
uploadFileToMinio(sslFiles, environment);
return request;
}
private String minioEnvPath(String projectId) {
return StringUtils.join(MAIN_FOLDER_PROJECT, "/", projectId, "/", APP_NAME_ENVIRONMENT);
}
}

View File

@ -84,7 +84,6 @@ public class EnvironmentControllerTests extends BaseTest {
private static final String getOptions = prefix + "/database/driver-options/";
private static final ResultMatcher BAD_REQUEST_MATCHER = status().isBadRequest();
private static final ResultMatcher ERROR_REQUEST_MATCHER = status().is5xxServerError();
private static final String DIR_PATH = "/project-management/environment/";
private static String MOCKID;
@Resource
private MockMvc mockMvc;
@ -910,7 +909,7 @@ public class EnvironmentControllerTests extends BaseTest {
this.requestGet(delete + id);
//查询文件
FileRequest fileRequest = new FileRequest();
fileRequest.setProjectId(StringUtils.join(DIR_PATH, DEFAULT_PROJECT_ID));
fileRequest.setFolder("/meterSphere/" + DEFAULT_PROJECT_ID + "/" + "environment");
fileRequest.setResourceId(id);
MinioRepository minioRepository = CommonBeanFactory.getBean(MinioRepository.class);
assert minioRepository != null;

View File

@ -31,7 +31,8 @@ public class CleanOrganizationJob {
public void cleanOrganization() {
LogUtils.info("clean up organization start.");
try {
LocalDate date = LocalDate.now().minusMonths(1);
//TODO 为了测试改成一天 正是后改成一个月
LocalDate date = LocalDate.now().minusDays(1);
long timestamp = date.atStartOfDay(ZoneId.systemDefault()).toInstant().toEpochMilli();
this.doCleanupOrganization(timestamp);
} catch (Exception e) {

View File

@ -28,7 +28,8 @@ public class CleanProjectJob {
@QuartzScheduled(cron = "0 0 3 * * ?")
public void cleanupProject() {
LogUtils.info("clean up project start.");
LocalDate date = LocalDate.now().minusMonths(1);
//TODO 为了测试改成一天 正是后改成一个月
LocalDate date = LocalDate.now().minusDays(1);
long timestamp = date.atStartOfDay(ZoneId.systemDefault()).toInstant().toEpochMilli();
this.doCleanupProject(timestamp);
LogUtils.info("clean up project end.");