feat(接口测试): 接口管理接口增删改复制相关接口

This commit is contained in:
lan-yonghui 2023-11-19 14:20:33 +08:00 committed by Craftsman
parent 4bf12605ad
commit e70df8a60f
25 changed files with 1573 additions and 225 deletions

View File

@ -294,3 +294,4 @@ permission.api.name=接口测试
api_debug_exist=接口已存在
follow=关注
unfollow=取消关注
api_definition_exist=接口已存在

View File

@ -302,3 +302,4 @@ permission.api.name=API Test
api_debug_exist=The API already exists
follow=Follow
unfollow=Unfollow
api_definition_exist=The API already exists

View File

@ -301,4 +301,5 @@ api_module.not.exist=模块不存在
permission.api.name=接口测试
api_debug_exist=接口已存在
follow=关注
unfollow=取消关注
unfollow=取消关注
api_definition_exist=接口已存在

View File

@ -302,3 +302,4 @@ permission.api.name=接口測試
api_debug_exist=接口已存在
follow=关注
unfollow=取消关注
api_definition_exist=接口已存在

View File

@ -2,8 +2,8 @@ package io.metersphere.api.controller.definition;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import io.metersphere.api.dto.definition.ApiDefinitionDTO;
import io.metersphere.api.dto.request.ApiDefinitionPageRequest;
import io.metersphere.api.domain.ApiDefinition;
import io.metersphere.api.dto.definition.*;
import io.metersphere.api.service.definition.ApiDefinitionLogService;
import io.metersphere.api.service.definition.ApiDefinitionService;
import io.metersphere.sdk.constants.PermissionConstants;
@ -11,13 +11,12 @@ import io.metersphere.system.log.annotation.Log;
import io.metersphere.system.log.constants.OperationLogType;
import io.metersphere.system.utils.PageUtils;
import io.metersphere.system.utils.Pager;
import io.metersphere.validation.groups.Created;
import io.metersphere.validation.groups.Updated;
import io.metersphere.system.utils.SessionUtils;
import io.swagger.v3.oas.annotations.Operation;
import jakarta.annotation.Resource;
import jakarta.validation.constraints.NotBlank;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.http.MediaType;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
@ -25,6 +24,9 @@ import org.springframework.web.multipart.MultipartFile;
import java.util.List;
/**
* @author lan
*/
@RestController
@RequestMapping(value = "/api/definition")
public class ApiDefinitionController {
@ -32,43 +34,86 @@ public class ApiDefinitionController {
private ApiDefinitionService apiDefinitionService;
@PostMapping(value = "/add")
@RequiresPermissions(PermissionConstants.PROJECT_API_DEFINITION_READ_ADD_API)
@Operation(summary = "接口测试-接口管理-添加接口定义")
@RequiresPermissions(PermissionConstants.PROJECT_API_DEFINITION_ADD)
// 添加接口Log示例
@Log(type = OperationLogType.ADD, expression = "#msClass.addLog(#request)", msClass = ApiDefinitionLogService.class)
public ApiDefinitionDTO add(@Validated({Created.class}) @RequestPart("request") ApiDefinitionDTO request,
@RequestPart(value = "files", required = false) List<MultipartFile> bodyFiles) {
return apiDefinitionService.create(request, bodyFiles);
public ApiDefinition add(@Validated @RequestPart("request") ApiDefinitionAddRequest request,
@RequestPart(value = "files", required = false) List<MultipartFile> bodyFiles) {
return apiDefinitionService.create(request, bodyFiles, SessionUtils.getUserId());
}
@PostMapping(value = "/update", consumes = MediaType.APPLICATION_JSON_VALUE)
@RequiresPermissions(PermissionConstants.PROJECT_API_DEFINITION_READ_ADD_API)
@PostMapping(value = "/update")
@Operation(summary = "接口测试-接口管理-更新接口定义")
@RequiresPermissions(PermissionConstants.PROJECT_API_DEFINITION_UPDATE)
// 添加修改Log示例
@Log(type = OperationLogType.UPDATE, expression = "#msClass.updateLog(#request)", msClass = ApiDefinitionLogService.class)
public ApiDefinitionDTO update(@Validated({Updated.class}) @RequestBody ApiDefinitionDTO request) {
return request;
public ApiDefinition update(@Validated @RequestPart("request") ApiDefinitionUpdateRequest request,
@RequestPart(value = "files", required = false) List<MultipartFile> bodyFiles) {
return apiDefinitionService.update(request, bodyFiles, SessionUtils.getUserId());
}
@PostMapping(value = "/batch-update", consumes = MediaType.APPLICATION_JSON_VALUE)
@RequiresPermissions(PermissionConstants.PROJECT_API_DEFINITION_READ_ADD_API)
@PostMapping(value = "/batch-update")
@Operation(summary = "接口测试-接口管理-批量更新接口定义")
@RequiresPermissions(PermissionConstants.PROJECT_API_DEFINITION_UPDATE)
// 添加修改Log示例
@Log(type = OperationLogType.UPDATE, expression = "#msClass.batchUpdateLog(#ids)", msClass = ApiDefinitionLogService.class)
public ApiDefinitionDTO batchUpdate(@RequestBody List<String> ids) {
return null;
@Log(type = OperationLogType.UPDATE, expression = "#msClass.batchUpdateLog(#request)", msClass = ApiDefinitionLogService.class)
public void batchUpdate(@Validated @RequestBody ApiDefinitionBatchUpdateRequest request) {
apiDefinitionService.batchUpdate(request, SessionUtils.getUserId());
}
@PostMapping(value = "/delete")
@RequiresPermissions(PermissionConstants.PROJECT_API_REPORT_READ_DELETE)
@Log(type = OperationLogType.DELETE, expression = "#msClass.delLog(#id)", msClass = ApiDefinitionLogService.class)
public void batchDelete(@RequestBody String id) {
@Operation(summary = "接口测试-接口管理-删除接口定义")
@RequiresPermissions(PermissionConstants.PROJECT_API_DEFINITION_DELETE)
@Log(type = OperationLogType.DELETE, expression = "#msClass.delLog(#request)", msClass = ApiDefinitionLogService.class)
public void delete(@Validated @RequestBody ApiDefinitionDeleteRequest request) {
apiDefinitionService.delete(request, SessionUtils.getUserId());
}
@PostMapping(value = "/batch-del")
@RequiresPermissions(PermissionConstants.PROJECT_API_REPORT_READ_DELETE)
@Log(type = OperationLogType.DELETE, expression = "#msClass.batchDelLog(#ids)", msClass = ApiDefinitionLogService.class)
public void batchDelete(@RequestBody List<String> ids) {
apiDefinitionService.batchDelete(ids);
@Operation(summary = "接口测试-接口管理-批量删除接口定义")
@RequiresPermissions(PermissionConstants.PROJECT_API_DEFINITION_DELETE)
@Log(type = OperationLogType.DELETE, expression = "#msClass.batchDelLog(#request)", msClass = ApiDefinitionLogService.class)
public void batchDelete(@Validated @RequestBody ApiDefinitionBatchRequest request) {
apiDefinitionService.batchDelete(request, SessionUtils.getUserId());
}
@PostMapping(value = "/copy")
@Operation(summary = "接口测试-接口管理-复制接口定义")
@RequiresPermissions(PermissionConstants.PROJECT_API_DEFINITION_UPDATE)
@Log(type = OperationLogType.UPDATE, expression = "#msClass.copyLog(#request)", msClass = ApiDefinitionLogService.class)
public ApiDefinition copy(@Validated @RequestBody ApiDefinitionCopyRequest request) {
return apiDefinitionService.copy(request, SessionUtils.getUserId());
}
@PostMapping("/batch-move")
@Operation(summary = "接口测试-接口管理-批量移动接口定义")
@RequiresPermissions(PermissionConstants.PROJECT_API_DEFINITION_UPDATE)
@Log(type = OperationLogType.UPDATE, expression = "#msClass.batchMoveLog(#request)", msClass = ApiDefinitionLogService.class)
public void batchMove(@Validated @RequestBody ApiDefinitionBatchMoveRequest request) {
apiDefinitionService.batchMove(request, SessionUtils.getUserId());
}
@GetMapping("/version/{id}")
@Operation(summary = "接口测试-接口管理-版本信息(接口是否存在多版本)")
@RequiresPermissions(PermissionConstants.PROJECT_API_DEFINITION_READ)
public List<ApiDefinitionVersionDTO> getApiDefinitionVersion(@PathVariable @NotBlank(message = "{api_definition.id.not_blank}") String id) {
return apiDefinitionService.getApiDefinitionVersion(id);
}
@GetMapping(value = "/get-detail/{id}")
@Operation(summary = "接口测试-接口管理-获取接口详情")
@RequiresPermissions(PermissionConstants.PROJECT_API_DEFINITION_READ)
public ApiDefinitionDTO get(@PathVariable String id) {
return apiDefinitionService.get(id, SessionUtils.getUserId());
}
@GetMapping("/follow/{id}")
@Operation(summary = "接口测试-接口管理-关注/取消关注用例")
@Log(type = OperationLogType.UPDATE, expression = "#msClass.followLog(#id)", msClass = ApiDefinitionLogService.class)
@RequiresPermissions(PermissionConstants.PROJECT_API_DEFINITION_UPDATE)
public void follow(@PathVariable String id) {
apiDefinitionService.follow(id, SessionUtils.getUserId());
}
@PostMapping("/page")
@Operation(summary = "接口测试-接口管理-接口列表")

View File

@ -7,7 +7,9 @@ import io.metersphere.sdk.exception.IResultCode;
*/
public enum ApiResultCode implements IResultCode {
API_DEBUG_EXIST(104001, "api_debug_exist");
API_DEBUG_EXIST(104001, "api_debug_exist"),
API_DEFINITION_EXIST(104002, "api_definition_exist"),
API_DEFINITION_NOT_EXIST(104003, "resource_not_exist");
private final int code;

View File

@ -0,0 +1,64 @@
package io.metersphere.api.dto.definition;
import io.metersphere.sdk.constants.ModuleConstants;
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;
/**
* @author lan
*/
@Data
public class ApiDefinitionAddRequest implements Serializable {
private static final long serialVersionUID = 1L;
@Schema(description = "接口名称", requiredMode = Schema.RequiredMode.REQUIRED)
@Size(min = 1, max = 255, message = "{api_definition.name.length_range}")
private String name;
@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", 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 = "http协议类型post/get/其它协议则是协议名(mqtt)")
private String method;
@Schema(description = "http协议路径/其它协议则为空")
private String path;
@Schema(description = "接口状态/进行中/已完成", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{api_definition.status.not_blank}")
@Size(min = 1, max = 50, message = "{api_definition.status.length_range}")
private String status;
@Schema(description = "模块fk", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{api_definition.module_id.not_blank}")
@Size(min = 1, max = 50, message = "{api_definition.module_id.length_range}")
private String moduleId;
@Schema(description = "版本fk")
@Size(min = 1, max = 50, message = "{api_definition.version_id.length_range}")
private String versionId;
@Schema(description = "描述")
private String description;
@Schema(description = "标签")
private List<@NotBlank String> tags;
@Schema(description = "请求内容")
@NotBlank
private String request;
}

View File

@ -0,0 +1,19 @@
package io.metersphere.api.dto.definition;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* @author lan
*/
@Data
@EqualsAndHashCode(callSuper = false)
public class ApiDefinitionBatchMoveRequest extends ApiDefinitionBatchRequest {
private static final long serialVersionUID = 1L;
@Schema(description = "模块ID", requiredMode = Schema.RequiredMode.REQUIRED)
private String moduleId;
}

View File

@ -0,0 +1,28 @@
package io.metersphere.api.dto.definition;
import io.metersphere.system.dto.table.TableBatchProcessDTO;
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.io.Serializable;
/**
* @author lan
*/
@Data
@EqualsAndHashCode(callSuper = false)
public class ApiDefinitionBatchRequest extends TableBatchProcessDTO implements Serializable {
private static final long serialVersionUID = 1L;
@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 = "删除列表版本/删除全部版本")
private Boolean deleteAll = false;
}

View File

@ -0,0 +1,37 @@
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 lombok.EqualsAndHashCode;
import java.util.List;
/**
* @author lan
*/
@Data
@EqualsAndHashCode(callSuper = false)
public class ApiDefinitionBatchUpdateRequest extends ApiDefinitionBatchRequest {
private static final long serialVersionUID = 1L;
@Schema(description = "http协议类型post/get/其它协议则是协议名(mqtt)")
private String method;
@Schema(description = "接口状态/进行中/已完成")
@Size(min = 1, max = 50, message = "{api_definition.status.length_range}")
private String status;
@Schema(description = "版本fk")
@Size(min = 1, max = 50, message = "{api_definition.version_id.length_range}")
private String versionId;
@Schema(description = "标签")
private List<@NotBlank String> tags;
@Schema(description = "是否追加", requiredMode = Schema.RequiredMode.REQUIRED)
private boolean append = false;
}

View File

@ -0,0 +1,23 @@
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;
/**
* @author lan
*/
@Data
public class ApiDefinitionCopyRequest implements Serializable {
private static final long serialVersionUID = 1L;
@Schema(description = "接口pk", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{api_definition.id.not_blank}")
@Size(min = 1, max = 50, message = "{api_definition.id.length_range}")
private String id;
}

View File

@ -1,22 +1,23 @@
package io.metersphere.api.dto.definition;
import io.metersphere.api.domain.ApiDefinition;
import io.metersphere.plugin.api.spi.AbstractMsTestElement;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* @author lan
*/
@Data
@EqualsAndHashCode(callSuper = false)
public class ApiDefinitionDTO extends ApiDefinition{
@Schema(description = "请求内容")
private byte[] request;
@Schema(description = "请求内容")
private AbstractMsTestElement request;
@Schema(description = "响应内容")
private byte[] response;
@Schema(description = "备注")
private byte[] remark;
@Schema(description = "响应内容")
private String response;
@Schema(description = "创建人名称")
private String createUserName;
@ -39,4 +40,7 @@ public class ApiDefinitionDTO extends ApiDefinition{
@Schema(description = "用例执行结果")
private String caseStatus;
@Schema(description = "是否关注")
private Boolean follow;
}

View File

@ -0,0 +1,32 @@
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;
/**
* @author lan
*/
@Data
public class ApiDefinitionDeleteRequest implements Serializable {
private static final long serialVersionUID = 1L;
@Schema(description = "接口pk", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{api_definition.id.not_blank}")
@Size(min = 1, max = 50, message = "{api_definition.id.length_range}")
private String id;
@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 = "删除列表版本/删除全部版本")
private Boolean deleteAll;
}

View File

@ -1,4 +1,4 @@
package io.metersphere.api.dto.request;
package io.metersphere.api.dto.definition;
import io.metersphere.sdk.constants.ModuleConstants;
import io.metersphere.system.dto.sdk.BasePageRequest;

View File

@ -0,0 +1,23 @@
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 lombok.EqualsAndHashCode;
/**
* @author lan
*/
@Data
@EqualsAndHashCode(callSuper = false)
public class ApiDefinitionUpdateRequest extends ApiDefinitionAddRequest {
private static final long serialVersionUID = 1L;
@Schema(description = "接口pk", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{api_definition.id.not_blank}")
@Size(min = 1, max = 50, message = "{api_definition.id.length_range}")
private String id;
}

View File

@ -0,0 +1,35 @@
package io.metersphere.api.dto.definition;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
* @author: LAN
* @date: 2023/11/8 19:17
* @version: 1.0
*/
@Data
public class ApiDefinitionVersionDTO implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
@Schema(description = "接口ID")
private String id;
@Schema(description = "名称")
private String name;
@Schema(description = "版本id")
private String versionId;
@Schema(description = "版本引用id")
private String refId;
@Schema(description = "项目id")
private String projectId;
}

View File

@ -1,24 +0,0 @@
package io.metersphere.api.dto.definition;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
@Data
public class ListRequestDTO {
private String name;
private String path;
private String method;
private String protocol;
@NotBlank
private String projectId;
private String OrderColumn;
@Min(value = 1, message = "当前页码必须大于0")
private int current;
@Min(value = 5, message = "每页显示条数必须不小于5")
private int pageSize;
}

View File

@ -0,0 +1,24 @@
package io.metersphere.api.enums;
/**
* @author: LAN
* @date: 2023/11/16 10:42
* @version: 1.0
*/
public enum ApiDefinitionStatus {
PREPARE("Prepare"),
UNDERWAY("Underway"),
DEBUGGING("Debugging"),
OBSOLETE("Obsolete"),
COMPLETED("Completed");
private String value;
ApiDefinitionStatus(String value) {
this.value = value;
}
public String getValue() {
return this.value;
}
}

View File

@ -1,8 +1,8 @@
package io.metersphere.api.mapper;
import io.metersphere.api.dto.definition.ApiCaseComputeDTO;
import io.metersphere.api.dto.definition.ApiDefinitionDTO;
import io.metersphere.api.dto.request.ApiDefinitionPageRequest;
import io.metersphere.api.domain.ApiDefinition;
import io.metersphere.api.dto.definition.*;
import io.metersphere.system.dto.table.TableBatchProcessDTO;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@ -14,4 +14,21 @@ public interface ExtApiDefinitionMapper {
List<ApiCaseComputeDTO> selectApiCaseByIdsAndStatusIsNotTrash(@Param("ids") List<String> ids, @Param("projectId") String projectId);
Long getPos(@Param("projectId") String projectId);
List<String> getIds(@Param("request") TableBatchProcessDTO request, @Param("projectId") String projectId, @Param("deleted") boolean deleted);
List<String> getRefIds(@Param("ids") List<String> ids);
List<ApiDefinition> getTagsByIds(@Param("ids") List<String> ids);
List<ApiDefinitionVersionDTO> getApiDefinitionByRefId(@Param("refId") String refId);
void batchMove(@Param("request") ApiDefinitionBatchMoveRequest request, @Param("ids") List<String> ids, @Param("userId") String userId);
void batchDelete(@Param("ids") List<String> ids, @Param("userId") String userId);
void clearLatestVersion(@Param("refId") String refId, @Param("projectId") String projectId);
void updateLatestVersion(@Param("id") String id, @Param("projectId") String projectId);
}

View File

@ -49,6 +49,96 @@
#{value}
</foreach>
</select>
<select id="getPos" resultType="java.lang.Long">
SELECT pos
FROM api_definition
WHERE project_id = #{projectId}
ORDER BY pos DESC
LIMIT 1;
</select>
<select id="getIds" resultType="java.lang.String">
SELECT id
FROM api_definition
where project_id = #{projectId} and deleted = #{deleted}
<include refid="queryWhereConditionByBaseQueryRequest"/>
</select>
<select id="getRefIds" resultType="java.lang.String">
SELECT
ref_id
FROM
api_definition
where id in
<foreach collection="ids" item="id" open="(" separator="," close=")">
#{id}
</foreach>
and deleted = false
group by ref_id
</select>
<select id="getApiDefinitionByRefId" resultType="io.metersphere.api.dto.definition.ApiDefinitionVersionDTO">
SELECT
id,
`name`,
version_id,
ref_id,
project_id
FROM
api_definition
WHERE
ref_id = #{refId}
</select>
<select id="getTagsByIds" resultType="io.metersphere.api.domain.ApiDefinition">
select id, tags from api_definition
where id in
<foreach collection="ids" item="id" open="(" separator="," close=")">
#{id}
</foreach>
and deleted = false
</select>
<update id="batchDelete">
update api_definition
set deleted = 1,
delete_user = #{userId},
delete_time = UNIX_TIMESTAMP()*1000
where id in
<foreach collection="ids" item="id" open="(" separator="," close=")">
#{id}
</foreach>
and deleted = false
</update>
<update id="batchMove">
update api_definition
set module_id = #{request.moduleId},
update_user = #{userId},
update_time = UNIX_TIMESTAMP()*1000
where id in
<foreach collection="ids" item="id" open="(" separator="," close=")">
#{id}
</foreach>
and deleted = false
and project_id = #{request.projectId}
</update>
<update id="clearLatestVersion">
update api_definition
set latest = 0
where ref_id = #{refId}
and project_id = #{projectId}
</update>
<update id="updateLatestVersion">
update api_definition
set latest = 1
where id = #{id}
and project_id = #{projectId}
</update>
<sql id="queryWhereCondition">
<if test="request.keyword != null and request.keyword != ''">
and (
@ -185,6 +275,22 @@
</if>
</sql>
<sql id="queryWhereConditionByBaseQueryRequest">
<if test="request.condition.combine != null">
<include refid="combine">
<property name="condition" value="request.condition.combine"/>
</include>
</if>
<if test="request.condition.keyword != null">
and (
api_definition.num like concat('%', #{request.condition.keyword},'%')
or api_definition.name like concat('%', #{request.condition.keyword},'%')
or api_definition.tags like JSON_CONTAINS(tags, concat('["',#{request.condition.keyword},'"]'))
)
</if>
<include refid="filters">
<property name="filter" value="request.condition.filter"/>
</include>
</sql>
</mapper>

View File

@ -2,14 +2,18 @@ package io.metersphere.api.service.definition;
import io.metersphere.api.domain.ApiDefinition;
import io.metersphere.api.domain.ApiDefinitionExample;
import io.metersphere.api.dto.definition.ApiDefinitionDTO;
import io.metersphere.api.dto.definition.*;
import io.metersphere.api.mapper.ApiDefinitionMapper;
import io.metersphere.project.domain.Project;
import io.metersphere.project.mapper.ProjectMapper;
import io.metersphere.sdk.constants.HttpMethodConstants;
import io.metersphere.sdk.util.JSON;
import io.metersphere.sdk.util.Translator;
import io.metersphere.system.log.constants.OperationLogModule;
import io.metersphere.system.log.constants.OperationLogType;
import io.metersphere.system.log.dto.LogDTO;
import jakarta.annotation.Resource;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
@ -21,18 +25,24 @@ public class ApiDefinitionLogService {
@Resource
private ApiDefinitionMapper apiDefinitionMapper;
@Resource
private ApiDefinitionService apiDefinitionService;
@Resource
private ProjectMapper projectMapper;
/**
* 添加接口日志
*
* @param request
* @return
*/
public LogDTO addLog(ApiDefinitionDTO request) {
public LogDTO addLog(ApiDefinitionAddRequest request) {
LogDTO dto = new LogDTO(
request.getProjectId(),
"",
request.getId(),
request.getCreateUser(),
null,
null,
null,
OperationLogType.ADD.name(),
OperationLogModule.API_DEFINITION,
request.getName());
@ -49,25 +59,24 @@ public class ApiDefinitionLogService {
* @param request
* @return
*/
public LogDTO updateLog(ApiDefinitionDTO request) {
public LogDTO updateLog(ApiDefinitionUpdateRequest request) {
ApiDefinition apiDefinition = apiDefinitionMapper.selectByPrimaryKey(request.getId());
if(apiDefinition != null){
LogDTO dto = new LogDTO(
request.getProjectId(),
null,
request.getId(),
null,
OperationLogType.UPDATE.name(),
OperationLogModule.API_DEFINITION,
request.getName());
LogDTO dto = new LogDTO(
request.getProjectId(),
"",
request.getId(),
request.getCreateUser(),
OperationLogType.UPDATE.name(),
OperationLogModule.API_DEFINITION,
request.getName());
dto.setPath("/api/definition/update");
dto.setMethod(HttpMethodConstants.POST.name());
if (apiDefinition == null) {
dto.setPath("/api/definition/update");
dto.setMethod(HttpMethodConstants.POST.name());
dto.setOriginalValue(JSON.toJSONBytes(apiDefinition));
return dto;
}
dto.setOriginalValue(JSON.toJSONBytes(apiDefinition));
return dto;
return null;
}
/**
@ -76,24 +85,25 @@ public class ApiDefinitionLogService {
* @param request
* @return
*/
public LogDTO delLog(ApiDefinitionDTO request) {
public LogDTO delLog(ApiDefinitionDeleteRequest request) {
ApiDefinition apiDefinition = apiDefinitionMapper.selectByPrimaryKey(request.getId());
LogDTO dto = new LogDTO(
request.getProjectId(),
"",
request.getId(),
request.getCreateUser(),
OperationLogType.DELETE.name(),
OperationLogModule.API_DEFINITION,
request.getName());
if(apiDefinition != null){
LogDTO dto = new LogDTO(
request.getProjectId(),
null,
request.getId(),
null,
OperationLogType.DELETE.name(),
OperationLogModule.API_DEFINITION,
apiDefinition.getName());
dto.setPath("/api/definition/delete");
dto.setMethod(HttpMethodConstants.POST.name());
if (apiDefinition == null) {
dto.setPath("/api/definition/delete");
dto.setMethod(HttpMethodConstants.POST.name());
dto.setOriginalValue(JSON.toJSONBytes(apiDefinition));
return dto;
}
dto.setOriginalValue(JSON.toJSONBytes(apiDefinition));
return dto;
return null;
}
/**
@ -101,26 +111,30 @@ public class ApiDefinitionLogService {
*
* @return
*/
public List<LogDTO> batchDelLog(List<String> ids) {
ApiDefinitionExample example = new ApiDefinitionExample();
example.createCriteria().andIdIn(ids);
List<ApiDefinition> apiDefinitions = apiDefinitionMapper.selectByExample(example);
public List<LogDTO> batchDelLog(ApiDefinitionBatchRequest request) {
List<String> ids = apiDefinitionService.getBatchApiIds(request, request.getProjectId());
List<LogDTO> dtoList = new ArrayList<>();
apiDefinitions.forEach(item -> {
LogDTO dto = new LogDTO(
item.getProjectId(),
"",
item.getId(),
item.getCreateUser(),
OperationLogType.DELETE.name(),
OperationLogModule.API_DEFINITION,
item.getName());
if (CollectionUtils.isNotEmpty(ids)) {
ApiDefinitionExample example = new ApiDefinitionExample();
example.createCriteria().andIdIn(ids).andDeletedEqualTo(false);
List<ApiDefinition> apiDefinitions = apiDefinitionMapper.selectByExample(example);
apiDefinitions.forEach(item -> {
LogDTO dto = new LogDTO(
item.getProjectId(),
"",
item.getId(),
item.getCreateUser(),
OperationLogType.DELETE.name(),
OperationLogModule.API_DEFINITION,
item.getName());
dto.setPath("/api/definition/batch-delete");
dto.setMethod(HttpMethodConstants.POST.name());
dto.setOriginalValue(JSON.toJSONBytes(item));
dtoList.add(dto);
});
}
dto.setPath("/api/definition/batch-delete");
dto.setMethod(HttpMethodConstants.POST.name());
dto.setOriginalValue(JSON.toJSONBytes(item));
dtoList.add(dto);
});
return dtoList;
}
@ -129,7 +143,8 @@ public class ApiDefinitionLogService {
*
* @return
*/
public List<LogDTO> batchUpdateLog(List<String> ids) {
public List<LogDTO> batchUpdateLog(ApiDefinitionBatchUpdateRequest request) {
List<String> ids = apiDefinitionService.getBatchApiIds(request, request.getProjectId());
ApiDefinitionExample example = new ApiDefinitionExample();
example.createCriteria().andIdIn(ids);
List<ApiDefinition> apiDefinitions = apiDefinitionMapper.selectByExample(example);
@ -151,4 +166,66 @@ public class ApiDefinitionLogService {
});
return dtoList;
}
public LogDTO copyLog(ApiDefinitionCopyRequest request) {
ApiDefinition apiDefinition = apiDefinitionMapper.selectByPrimaryKey(request.getId());
if(apiDefinition != null){
LogDTO dto = new LogDTO(
apiDefinition.getProjectId(),
null,
null,
null,
OperationLogType.UPDATE.name(),
OperationLogModule.API_DEFINITION,
apiDefinition.getName());
dto.setPath("/api/definition/copy");
dto.setMethod(HttpMethodConstants.POST.name());
dto.setOriginalValue(JSON.toJSONBytes(apiDefinition));
return dto;
}
return null;
}
public List<LogDTO> batchMoveLog(ApiDefinitionBatchMoveRequest request) {
List<String> ids = apiDefinitionService.getBatchApiIds(request, request.getProjectId());
ApiDefinitionExample example = new ApiDefinitionExample();
example.createCriteria().andIdIn(ids);
List<ApiDefinition> apiDefinitions = apiDefinitionMapper.selectByExample(example);
List<LogDTO> dtoList = new ArrayList<>();
apiDefinitions.forEach(item -> {
LogDTO dto = new LogDTO(
item.getProjectId(),
"",
item.getId(),
item.getCreateUser(),
OperationLogType.UPDATE.name(),
OperationLogModule.API_DEFINITION,
item.getName());
dto.setPath("/api/definition/batch-move");
dto.setMethod(HttpMethodConstants.POST.name());
dto.setOriginalValue(JSON.toJSONBytes(item));
dtoList.add(dto);
});
return dtoList;
}
public LogDTO followLog(String id) {
ApiDefinition apiDefinition = apiDefinitionMapper.selectByPrimaryKey(id);
Project project = projectMapper.selectByPrimaryKey(apiDefinition.getProjectId());
LogDTO dto = new LogDTO(
apiDefinition.getProjectId(),
project.getOrganizationId(),
id,
null,
OperationLogType.UPDATE.name(),
OperationLogModule.API_DEFINITION,
Translator.get("follow") + apiDefinition.getName());
dto.setPath("/api/definition/follow/" + id);
dto.setMethod(HttpMethodConstants.GET.name());
dto.setOriginalValue(JSON.toJSONBytes(apiDefinition));
return dto;
}
}

View File

@ -1,19 +1,35 @@
package io.metersphere.api.service.definition;
import io.metersphere.api.domain.ApiDefinition;
import io.metersphere.api.domain.ApiDefinitionExample;
import io.metersphere.api.dto.definition.ApiCaseComputeDTO;
import io.metersphere.api.dto.definition.ApiDefinitionDTO;
import io.metersphere.api.dto.definition.ListRequestDTO;
import io.metersphere.api.dto.request.ApiDefinitionPageRequest;
import io.metersphere.api.controller.result.ApiResultCode;
import io.metersphere.api.domain.*;
import io.metersphere.api.dto.definition.*;
import io.metersphere.api.enums.ApiReportStatus;
import io.metersphere.api.mapper.ApiDefinitionBlobMapper;
import io.metersphere.api.mapper.ApiDefinitionFollowerMapper;
import io.metersphere.api.mapper.ApiDefinitionMapper;
import io.metersphere.api.mapper.ExtApiDefinitionMapper;
import io.metersphere.api.util.ApiDataUtils;
import io.metersphere.plugin.api.spi.AbstractMsTestElement;
import io.metersphere.project.mapper.ExtBaseProjectVersionMapper;
import io.metersphere.project.mapper.ProjectMapper;
import io.metersphere.project.service.ProjectService;
import io.metersphere.sdk.constants.ApplicationNumScope;
import io.metersphere.sdk.constants.StorageType;
import io.metersphere.sdk.exception.MSException;
import io.metersphere.sdk.util.*;
import io.metersphere.system.dto.table.TableBatchProcessDTO;
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 jakarta.validation.constraints.NotNull;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionUtils;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
@ -25,36 +41,37 @@ import java.util.stream.Stream;
@Service
public class ApiDefinitionService {
public static final Long ORDER_STEP = 5000L;
private static final String MAIN_FOLDER_PROJECT = "project";
private static final String APP_NAME_API_DEFINITION = "apiDefinition";
@Resource
private ApiDefinitionMapper apiDefinitionMapper;
@Resource
private ExtApiDefinitionMapper extApiDefinitionMapper;
@Resource
private ApiDefinitionFollowerMapper apiDefinitionFollowerMapper;
@Resource
private ProjectMapper projectMapper;
@Resource
private ExtBaseProjectVersionMapper extBaseProjectVersionMapper;
@Resource
private ApiDefinitionBlobMapper apiDefinitionBlobMapper;
@Resource
private UserLoginService userLoginService;
@Resource
private MinioRepository minioRepository;
public ApiDefinitionDTO create(ApiDefinitionDTO request, List<MultipartFile> bodyFiles) {
return request;
}
public List<ApiDefinition> list(@NotNull ListRequestDTO request) {
ApiDefinitionExample example = new ApiDefinitionExample();
ApiDefinitionExample.Criteria criteria = example.createCriteria();
criteria.andProjectIdEqualTo(request.getProjectId());
if (StringUtils.isNotBlank(request.getName())) {
criteria.andNameLike("%" + request.getName() + "%");
}
if (StringUtils.isNotBlank(request.getProtocol())) {
criteria.andProtocolEqualTo(request.getProtocol());
}
return apiDefinitionMapper.selectByExample(example);
}
public void batchDelete(List<String> ids) {
}
@Resource
private SqlSessionFactory sqlSessionFactory;
public List<ApiDefinitionDTO> getApiDefinitionPage(ApiDefinitionPageRequest request, Boolean deleted){
List<ApiDefinitionDTO> list = extApiDefinitionMapper.list(request, deleted);
@ -64,6 +81,159 @@ public class ApiDefinitionService {
}
return list;
}
public ApiDefinitionDTO get(String id, String userId){
ApiDefinitionDTO apiDefinitionDTO = new ApiDefinitionDTO();
ApiDefinition apiDefinition = checkApiDefinition(id);
ApiDefinitionBlob apiDefinitionBlob = apiDefinitionBlobMapper.selectByPrimaryKey(id);
BeanUtils.copyBean(apiDefinitionDTO,apiDefinition);
ApiDefinitionFollowerExample example = new ApiDefinitionFollowerExample();
example.createCriteria().andApiDefinitionIdEqualTo(id).andUserIdEqualTo(userId);
List<ApiDefinitionFollower> followers = apiDefinitionFollowerMapper.selectByExample(example);
apiDefinitionDTO.setFollow(CollectionUtils.isNotEmpty(followers));
if(apiDefinitionBlob != null){
apiDefinitionDTO.setRequest(ApiDataUtils.parseObject(new String(apiDefinitionBlob.getRequest()), AbstractMsTestElement.class));
}
return apiDefinitionDTO;
}
public ApiDefinition create(ApiDefinitionAddRequest request, List<MultipartFile> bodyFiles, String userId) {
ProjectService.checkResourceExist(request.getProjectId());
ApiDefinition apiDefinition = new ApiDefinition();
BeanUtils.copyBean(apiDefinition, request);
checkAddExist(apiDefinition);
apiDefinition.setId(IDGenerator.nextStr());
apiDefinition.setNum(getNextNum(request.getProjectId()));
apiDefinition.setPos(getNextOrder(request.getProjectId()));
apiDefinition.setLatest(true);
apiDefinition.setStatus(request.getStatus());
apiDefinition.setCreateUser(userId);
apiDefinition.setUpdateUser(userId);
apiDefinition.setCreateTime(System.currentTimeMillis());
apiDefinition.setUpdateTime(System.currentTimeMillis());
apiDefinition.setVersionId(StringUtils.defaultIfBlank(request.getVersionId(), extBaseProjectVersionMapper.getDefaultVersion(request.getProjectId())));
apiDefinition.setRefId(apiDefinition.getId());
if (CollectionUtils.isNotEmpty(request.getTags())) {
apiDefinition.setTags(JSON.toJSONString(request.getTags()));
}
apiDefinitionMapper.insertSelective(apiDefinition);
ApiDefinitionBlob apiDefinitionBlob = new ApiDefinitionBlob();
apiDefinitionBlob.setId(apiDefinition.getId());
apiDefinitionBlob.setRequest(request.getRequest().getBytes());
apiDefinitionBlobMapper.insertSelective(apiDefinitionBlob);
// TODO 文件只是上传到minio不需要入库吗
uploadBodyFile(bodyFiles, apiDefinition.getId(), request.getProjectId());
return apiDefinition;
}
public ApiDefinition update(ApiDefinitionUpdateRequest request, List<MultipartFile> bodyFiles, String userId) {
ProjectService.checkResourceExist(request.getProjectId());
ApiDefinition originApiDefinition = checkApiDefinition(request.getId());
ApiDefinition apiDefinition = new ApiDefinition();
BeanUtils.copyBean(apiDefinition, request);
checkUpdateExist(apiDefinition, originApiDefinition);
apiDefinition.setStatus(request.getStatus());
apiDefinition.setUpdateUser(userId);
apiDefinition.setUpdateTime(System.currentTimeMillis());
apiDefinition.setVersionId(request.getVersionId());
if (CollectionUtils.isNotEmpty(request.getTags())) {
apiDefinition.setTags(JSON.toJSONString(request.getTags()));
}
apiDefinitionMapper.updateByPrimaryKeySelective(apiDefinition);
ApiDefinitionBlob apiDefinitionBlob = new ApiDefinitionBlob();
apiDefinitionBlob.setId(apiDefinition.getId());
apiDefinitionBlob.setRequest(request.getRequest().getBytes());
apiDefinitionBlobMapper.updateByPrimaryKeySelective(apiDefinitionBlob);
// TODO 需要处理之前的文件
uploadBodyFile(bodyFiles, request.getId(), request.getProjectId());
return apiDefinition;
}
public void batchUpdate(ApiDefinitionBatchUpdateRequest request, String userId) {
ProjectService.checkResourceExist(request.getProjectId());
List<String> ids = getBatchApiIds(request, request.getProjectId());
if (CollectionUtils.isNotEmpty(ids)) {
if (CollectionUtils.isNotEmpty(request.getTags())) {
handleTags(request, userId, ids);
} else {
ApiDefinition apiDefinition = new ApiDefinition();
BeanUtils.copyBean(apiDefinition, request);
apiDefinition.setUpdateUser(userId);
apiDefinition.setUpdateTime(System.currentTimeMillis());
ApiDefinitionExample apiDefinitionExample = new ApiDefinitionExample();
apiDefinitionExample.createCriteria().andIdIn(ids);
apiDefinitionMapper.updateByExampleSelective(apiDefinition, apiDefinitionExample);
}
}
}
public ApiDefinition copy(ApiDefinitionCopyRequest request, String userId) {
ApiDefinition copyApiDefinition = checkApiDefinition(request.getId());
ApiDefinition apiDefinition = new ApiDefinition();
BeanUtils.copyBean(apiDefinition, copyApiDefinition);
apiDefinition.setId(IDGenerator.nextStr());
apiDefinition.setName(getCopyName(copyApiDefinition.getName()));
apiDefinition.setStatus(copyApiDefinition.getStatus());
apiDefinition.setNum(getNextNum(copyApiDefinition.getProjectId()));
apiDefinition.setPos(getNextOrder(copyApiDefinition.getProjectId()));
apiDefinition.setRefId(apiDefinition.getId());
apiDefinition.setCreateUser(userId);
apiDefinition.setUpdateUser(userId);
apiDefinition.setCreateTime(System.currentTimeMillis());
apiDefinition.setUpdateTime(System.currentTimeMillis());
apiDefinitionMapper.insertSelective(apiDefinition);
ApiDefinitionBlob copyApiDefinitionBlob = getApiDefinitionBlob(request.getId());
ApiDefinitionBlob apiDefinitionBlob = new ApiDefinitionBlob();
if(copyApiDefinitionBlob != null){
apiDefinitionBlob.setId(apiDefinition.getId());
apiDefinitionBlob.setRequest(copyApiDefinitionBlob.getRequest());
apiDefinitionBlob.setResponse(copyApiDefinitionBlob.getResponse());
apiDefinitionBlobMapper.insertSelective(apiDefinitionBlob);
}
// TODO 复制的时候文件需要复制一份
return apiDefinition;
}
public void batchCopy(ApiDefinitionBatchUpdateRequest request, String userId) {
List<String> ids = getBatchApiIds(request, request.getProjectId());
if (CollectionUtils.isNotEmpty(ids)) {
// TODO 批量复制
List<String> refId = extApiDefinitionMapper.getRefIds(ids);
}
}
public void delete(ApiDefinitionDeleteRequest request, String userId) {
checkApiDefinition(request.getId());
handleDeleteApiDefinition(Collections.singletonList(request.getId()),request.getDeleteAll(), userId);
}
public void batchDelete(ApiDefinitionBatchRequest request, String userId) {
List<String> ids = getBatchApiIds(request, request.getProjectId());
if (CollectionUtils.isNotEmpty(ids)) {
handleDeleteApiDefinition(ids, request.getDeleteAll(), userId);
}
}
public void batchMove(ApiDefinitionBatchMoveRequest request, String userId) {
List<String> ids = getBatchApiIds(request, request.getProjectId());
if (CollectionUtils.isNotEmpty(ids)) {
List<String> refId = extApiDefinitionMapper.getRefIds(ids);
extApiDefinitionMapper.batchMove(request, refId, userId);
}
}
public void recycleDelete(){
// todo 删除接口用例
// todo 是否有自定义字段关联关系删除自定义字段关系
// todo 是否删除关注人如果删除回收站恢复的时候如何处理
}
private void convertUserIdToName(List<ApiDefinitionDTO> list) {
Set<String> userIds = extractUserIds(list);
Map<String, String> userMap = userLoginService.getUserNameMap(new ArrayList<>(userIds));
@ -92,7 +262,7 @@ public class ApiDefinitionService {
item.setCaseTotal(apiCaseComputeDTO.getCaseTotal());
item.setCasePassRate(apiCaseComputeDTO.getCasePassRate());
// 状态优先级 未执行未通过误报FAKE_ERROR通过
if ((apiCaseComputeDTO.getError() + apiCaseComputeDTO.getSuccess()) < apiCaseComputeDTO.getCaseTotal()) {
if ((apiCaseComputeDTO.getError() + apiCaseComputeDTO.getFakeError() + apiCaseComputeDTO.getSuccess()) < apiCaseComputeDTO.getCaseTotal()) {
item.setCaseStatus(ApiReportStatus.PENDING.name());
} else if (apiCaseComputeDTO.getError() > 0) {
item.setCaseStatus(ApiReportStatus.ERROR.name());
@ -109,4 +279,252 @@ public class ApiDefinitionService {
});
}
public Long getNextOrder(String projectId) {
Long pos = extApiDefinitionMapper.getPos(projectId);
return (pos == null ? 0 : pos) + ORDER_STEP;
}
public long getNextNum(String projectId) {
return NumGenerator.nextNum(projectId, ApplicationNumScope.API_DEFINITION);
}
public void uploadBodyFile(List<MultipartFile> files, String apiId, String projectId) {
if (CollectionUtils.isNotEmpty(files)) {
files.forEach(file -> {
FileRequest fileRequest = new FileRequest();
fileRequest.setFileName(file.getName());
fileRequest.setFolder(minioPath(projectId));
fileRequest.setProjectId(projectId);
fileRequest.setResourceId(apiId);
fileRequest.setStorage(StorageType.MINIO.name());
try {
minioRepository.saveFile(file, fileRequest);
} catch (Exception e) {
LogUtils.info("上传body文件失败: 文件名称:" + file.getName(), e);
throw new MSException(Translator.get("file_upload_fail"));
}
});
}
}
/**
* 校验接口是否存在
*
* @param apiId
*/
private ApiDefinition checkApiDefinition(String apiId) {
ApiDefinition apiDefinition = apiDefinitionMapper.selectByPrimaryKey(apiId);
if (apiDefinition == null) {
throw new MSException(ApiResultCode.API_DEFINITION_NOT_EXIST);
}
return apiDefinition;
}
private void checkAddExist(ApiDefinition apiDefinition) {
ApiDefinitionExample example = new ApiDefinitionExample();
example.createCriteria()
.andNameEqualTo(apiDefinition.getName())
.andModuleIdEqualTo(apiDefinition.getModuleId());
if (CollectionUtils.isNotEmpty(apiDefinitionMapper.selectByExample(example))) {
throw new MSException(ApiResultCode.API_DEFINITION_EXIST);
}
}
private void checkUpdateExist(ApiDefinition apiDefinition, ApiDefinition originApiDefinition) {
if (StringUtils.isBlank(apiDefinition.getName())) {
return;
}
ApiDefinitionExample example = new ApiDefinitionExample();
example.createCriteria()
.andIdNotEqualTo(apiDefinition.getId())
.andModuleIdEqualTo(apiDefinition.getModuleId() == null ? originApiDefinition.getModuleId() : apiDefinition.getModuleId())
.andNameEqualTo(apiDefinition.getName());
if (CollectionUtils.isNotEmpty(apiDefinitionMapper.selectByExample(example))) {
throw new MSException(ApiResultCode.API_DEFINITION_EXIST);
}
}
private ApiDefinitionBlob getApiDefinitionBlob(String apiId) {
ApiDefinitionBlobExample apiDefinitionBlobExample = new ApiDefinitionBlobExample();
apiDefinitionBlobExample.createCriteria().andIdEqualTo(apiId);
return apiDefinitionBlobMapper.selectByPrimaryKey(apiId);
}
public <T> List<String> getBatchApiIds(T dto, String projectId) {
TableBatchProcessDTO request = (TableBatchProcessDTO) dto;
if (request.isSelectAll()) {
List<String> ids = extApiDefinitionMapper.getIds(request, projectId, false);
if (CollectionUtils.isNotEmpty(request.getExcludeIds())) {
ids.removeAll(request.getExcludeIds());
}
return ids;
} else {
return request.getSelectIds();
}
}
/**
* 根据接口id 获取接口是否存在多个版本
*
* @param apiId
* @return
*/
public List<ApiDefinitionVersionDTO> getApiDefinitionVersion(String apiId) {
ApiDefinition apiDefinition = checkApiDefinition(apiId);
return extApiDefinitionMapper.getApiDefinitionByRefId(apiDefinition.getRefId());
}
public void follow(String apiId, String userId) {
checkApiDefinition(apiId);
ApiDefinitionFollowerExample example = new ApiDefinitionFollowerExample();
example.createCriteria().andApiDefinitionIdEqualTo(apiId).andUserIdEqualTo(userId);
if (apiDefinitionFollowerMapper.countByExample(example) > 0) {
deleteFollower(apiId, userId);
} else {
ApiDefinitionFollower apiDefinitionFollower = new ApiDefinitionFollower();
apiDefinitionFollower.setApiDefinitionId(apiId);
apiDefinitionFollower.setUserId(userId);
apiDefinitionFollowerMapper.insertSelective(apiDefinitionFollower);
}
}
private void handleTags(ApiDefinitionBatchUpdateRequest request, String userId, List<String> ids) {
if (request.isAppend()) {
//追加标签
Map<String, ApiDefinition> collect = extApiDefinitionMapper.getTagsByIds(ids)
.stream()
.collect(Collectors.toMap(ApiDefinition::getId, Function.identity()));
try (SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH)) {
ApiDefinitionMapper definitionMapper = sqlSession.getMapper(ApiDefinitionMapper.class);
ids.forEach(id -> {
ApiDefinition apiDefinition = new ApiDefinition();
if (StringUtils.isNotBlank(collect.get(id).getTags())) {
List<String> tags = JSON.parseArray(collect.get(id).getTags(), String.class);
tags.addAll(request.getTags());
apiDefinition.setTags(JSON.toJSONString(tags));
} else {
apiDefinition.setTags(JSON.toJSONString(request.getTags()));
}
apiDefinition.setId(id);
apiDefinition.setUpdateTime(System.currentTimeMillis());
apiDefinition.setUpdateUser(userId);
definitionMapper.updateByPrimaryKeySelective(apiDefinition);
});
sqlSession.flushStatements();
SqlSessionUtils.closeSqlSession(sqlSession, sqlSessionFactory);
}
} else {
//替换标签
ApiDefinition apiDefinition = new ApiDefinition();
apiDefinition.setTags(JSON.toJSONString(request.getTags()));
apiDefinition.setProjectId(request.getProjectId());
apiDefinition.setUpdateTime(System.currentTimeMillis());
apiDefinition.setUpdateUser(userId);
ApiDefinitionExample apiDefinitionExample = new ApiDefinitionExample();
apiDefinitionExample.createCriteria().andIdIn(ids).andDeletedEqualTo(false).andProjectIdEqualTo(request.getProjectId());
apiDefinitionMapper.updateByExampleSelective(apiDefinition, apiDefinitionExample);
}
}
private String getCopyName(String name) {
String copyName = "copy_" + name ;
if (copyName.length() > 255) {
copyName = copyName.substring(0, 250) + copyName.substring(copyName.length() - 5);
}
return copyName;
}
public Map<String, ApiDefinitionBlob> copyBlobInfo(List<String> ids) {
ApiDefinitionBlobExample blobExample = new ApiDefinitionBlobExample();
blobExample.createCriteria().andIdIn(ids);
List<ApiDefinitionBlob> apiDefinitionBlobs = apiDefinitionBlobMapper.selectByExampleWithBLOBs(blobExample);
return apiDefinitionBlobs.stream().collect(Collectors.toMap(ApiDefinitionBlob::getId, apiDefinitionBlob -> apiDefinitionBlob));
}
private void handleDeleteApiDefinition(List<String> ids, Boolean deleteAll, String userId) {
if (deleteAll) {
//全部删除 进入回收站
List<String> refId = extApiDefinitionMapper.getRefIds(ids);
extApiDefinitionMapper.batchDelete(refId, userId);
} else {
//列表删除 需要判断是否存在多个版本问题
ids.forEach(id -> {
ApiDefinition apiDefinition = checkApiDefinition(id);
// 选中的数据直接放入回收站
doDelete(id, userId);
// 删除的数据是否为最新版本的数据如果是则需要查询是否有多版本数据存在需要去除当前删除的数据更新剩余版本数据中最近的一条数据为最新的数据
if(apiDefinition.getLatest()){
List<ApiDefinitionVersionDTO> apiDefinitionVersions = extApiDefinitionMapper.getApiDefinitionByRefId(apiDefinition.getRefId());
if (apiDefinitionVersions.size() > 1) {
deleteAfterAction(apiDefinitionVersions);
}
}
});
}
}
private void deleteAfterAction(List<ApiDefinitionVersionDTO> apiDefinitionVersions){
apiDefinitionVersions.forEach(item->{
clearLatestVersion(item.getRefId(),item.getProjectId());
ApiDefinition latestData = getLatestData(item.getRefId(), item.getProjectId());
updateLatestVersion(latestData.getId(),latestData.getProjectId());
});
}
// 清除多版本最新标识
private void clearLatestVersion(String refId, String projectId){
extApiDefinitionMapper.clearLatestVersion(refId, projectId);
}
// 获取多版本最新一条数据
private ApiDefinition getLatestData(String refId, String projectId){
ApiDefinitionExample apiDefinitionExample = new ApiDefinitionExample();
apiDefinitionExample.createCriteria().andRefIdEqualTo(refId).andDeletedEqualTo(false).andProjectIdEqualTo(projectId);
apiDefinitionExample.setOrderByClause("update_time DESC");
ApiDefinition apiDefinition = apiDefinitionMapper.selectByExample(apiDefinitionExample).stream().findFirst().orElse(null);
if (apiDefinition == null) {
throw new MSException(ApiResultCode.API_DEFINITION_EXIST);
}
return apiDefinition;
}
// 更新最新版本标识
private void updateLatestVersion(String id, String projectId){
extApiDefinitionMapper.updateLatestVersion(id, projectId);
}
private void deleteAll(String apiDefinitionId, String userId) {
// 查找全部版本
List<ApiDefinitionVersionDTO> apiDefinitionVersions = getApiDefinitionVersion(apiDefinitionId);
if (!apiDefinitionVersions.isEmpty()) {
// 获取所有版本的ID
List<String> ids = apiDefinitionVersions.stream()
.map(ApiDefinitionVersionDTO::getId)
.toList();
// 删除所有版本
ids.forEach(id -> doDelete(id, userId));
}
}
private void doDelete(String id, String userId) {
ApiDefinition apiDefinition = new ApiDefinition();
apiDefinition.setDeleted(true);
apiDefinition.setId(id);
apiDefinition.setDeleteUser(userId);
apiDefinition.setDeleteTime(System.currentTimeMillis());
apiDefinitionMapper.updateByPrimaryKeySelective(apiDefinition);
}
private void deleteFollower(String apiId, String userId) {
apiDefinitionFollowerMapper.deleteByPrimaryKey(apiId, userId);
}
private String minioPath(String projectId) {
return StringUtils.join(MAIN_FOLDER_PROJECT, "/", projectId, "/", APP_NAME_API_DEFINITION);
}
}

View File

@ -1,14 +1,25 @@
package io.metersphere.api.controller;
import io.metersphere.api.dto.definition.ApiDefinitionDTO;
import io.metersphere.api.dto.request.ApiDefinitionPageRequest;
import io.metersphere.sdk.constants.ApplicationNumScope;
import io.metersphere.api.controller.result.ApiResultCode;
import io.metersphere.api.domain.*;
import io.metersphere.api.dto.definition.*;
import io.metersphere.api.enums.ApiDefinitionStatus;
import io.metersphere.api.mapper.*;
import io.metersphere.api.util.ApiDataUtils;
import io.metersphere.plugin.api.spi.AbstractMsTestElement;
import io.metersphere.project.mapper.ExtBaseProjectVersionMapper;
import io.metersphere.sdk.constants.PermissionConstants;
import io.metersphere.sdk.dto.api.request.http.MsHTTPElement;
import io.metersphere.sdk.util.BeanUtils;
import io.metersphere.sdk.util.JSON;
import io.metersphere.sdk.util.LogUtils;
import io.metersphere.system.base.BaseTest;
import io.metersphere.system.controller.handler.ResultHolder;
import io.metersphere.system.uid.NumGenerator;
import io.metersphere.system.dto.sdk.BaseCondition;
import io.metersphere.system.log.constants.OperationLogType;
import io.metersphere.system.utils.Pager;
import jakarta.annotation.Resource;
import org.apache.commons.collections.CollectionUtils;
import org.junit.jupiter.api.*;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
@ -18,122 +29,521 @@ import org.springframework.test.context.jdbc.Sql;
import org.springframework.test.context.jdbc.SqlConfig;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import java.io.File;
import java.io.FileInputStream;
import java.nio.charset.StandardCharsets;
import java.util.*;
import static io.metersphere.system.controller.handler.result.MsHttpResultCode.NOT_FOUND;
@SpringBootTest(webEnvironment= SpringBootTest.WebEnvironment.RANDOM_PORT)
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
@AutoConfigureMockMvc
public class ApiDefinitionControllerTests extends BaseTest {
private final static String URL_DEFINITION_ADD = "/api/definition/add";
private final static String URL_DEFINITION_UPDATE = "/api/definition/update";
private final static String URL_DEFINITION_BATCH_UPDATE = "/api/definition/batch-update";
private final static String URL_DEFINITION_DELETE = "/api/definition/delete";
private final static String URL_DEFINITION_BATCH_DELETE= "/api/definition/batch-del";
private final static String URL_DEFINITION_PAGE= "/api/definition/page";
private static final String BASE_PATH = "/api/definition/";
private final static String ADD = BASE_PATH + "add";
private final static String UPDATE = BASE_PATH + "update";
private final static String BATCH_UPDATE = BASE_PATH + "batch-update";
private final static String DELETE = BASE_PATH + "delete";
private final static String BATCH_DELETE= BASE_PATH + "batch-del";
private final static String COPY= BASE_PATH + "copy";
private final static String BATCH_MOVE= BASE_PATH + "batch-move";
private final static String DEFAULT_PROJECT_ID = "100001100001";
private final static String PAGE= BASE_PATH + "page";
private static final String GET = BASE_PATH + "get-detail/";
private static final String FOLLOW = BASE_PATH + "follow/";
private static final String VERSION = BASE_PATH + "version/";
private static final String DEFAULT_MODULE_ID = "10001";
@Resource
private ApiDefinitionMapper apiDefinitionMapper;
@Resource
private ApiDefinitionBlobMapper apiDefinitionBlobMapper;
@Resource
private ExtApiDefinitionMapper extApiDefinitionMapper;
@Resource
private ApiDefinitionFollowerMapper apiDefinitionFollowerMapper;
@Resource
private ApiDefinitionModuleMapper apiDefinitionModuleMapper;
@Resource
private ExtBaseProjectVersionMapper extBaseProjectVersionMapper;
@Test
@Order(0)
@Sql(scripts = {"/dml/init_api_definition.sql"}, config = @SqlConfig(encoding = "utf-8", transactionMode = SqlConfig.TransactionMode.ISOLATED))
public void initData(){
LogUtils.info("init api test data");
ApiDefinitionModule request = new ApiDefinitionModule();
request.setProjectId(DEFAULT_PROJECT_ID);
request.setName("a1");
request.setId(DEFAULT_MODULE_ID);
request.setProtocol("HTTP");
request.setParentId("root");
request.setPos(10L);
request.setCreateTime(0L);
request.setUpdateTime(0L);
request.setUpdateUser("admin");
request.setCreateUser("admin");
apiDefinitionModuleMapper.insertSelective(request);
}
@Test
@Order(1)
public void testAdd() throws Exception {
LogUtils.info("create api test");
// 创建一个 MockMultipartFile 对象用于模拟文件上传
MockMultipartFile file = new MockMultipartFile("files", "files", MediaType.APPLICATION_OCTET_STREAM_VALUE, "Test content".getBytes());
// 创建一个 ApiDefinitionDTO 对象用于模拟请求的一部分
ApiDefinitionDTO request = new ApiDefinitionDTO();
// 补充属性内容
request.setId("test-api-id");
request.setCreateUser("test-user");
request.setCreateTime(System.currentTimeMillis());
request.setUpdateUser("test-api-id");
request.setUpdateTime(System.currentTimeMillis());
request.setProjectId("test-project-id");
request.setName("test-api-name");
request.setProtocol("test-api-protocol");
request.setPos(1l);
request.setLatest(true);
request.setStatus("test-api-status");
request.setVersionId("test-api-version");
request.setRefId("test-api-version");
request.setDeleted(false);
request.setModuleId("test-api-module-id");
request.setNum(NumGenerator.nextNum("test-project-id", ApplicationNumScope.API_DEFINITION));
LinkedMultiValueMap<String, Object> paramMap = new LinkedMultiValueMap<>();
paramMap = new LinkedMultiValueMap<>();
// 创建测试数据
ApiDefinitionAddRequest request = createApiDefinitionAddRequest();
MsHTTPElement msHttpElement = MsHTTPElementTest.getMsHttpElement();
request.setRequest(ApiDataUtils.toJSONString(msHttpElement));
// 构造请求参数
MultiValueMap<String, Object> paramMap = new LinkedMultiValueMap<>();
paramMap.add("request", JSON.toJSONString(request));
paramMap.add("files", file);
this.requestMultipartWithOkAndReturn(URL_DEFINITION_ADD, paramMap);
FileInputStream inputStream = new FileInputStream(new File(
Objects.requireNonNull(this.getClass().getClassLoader().getResource("file/file_upload.JPG"))
.getPath()));
MockMultipartFile file = new MockMultipartFile("file_upload.JPG", "file_upload.JPG", MediaType.APPLICATION_OCTET_STREAM_VALUE, inputStream);
paramMap.add("files", List.of(file));
paramMap.add("request", JSON.toJSONString(request));
// 执行方法调用
MvcResult mvcResult = this.requestMultipartWithOkAndReturn(ADD, paramMap);
// 校验请求成功数据
ApiDefinition resultData = getResultData(mvcResult, ApiDefinition.class);
ApiDefinition apiDefinition = assertAddApiDefinition(request, msHttpElement, resultData.getId());
// 再插入一条数据便于修改时重名校验
request.setName("接口定义test1");
paramMap.clear();
paramMap.add("request", JSON.toJSONString(request));
mvcResult = this.requestMultipartWithOkAndReturn(ADD, paramMap);
resultData = getResultData(mvcResult, ApiDefinition.class);
assertAddApiDefinition(request, msHttpElement, resultData.getId());
// @@重名校验异常
assertErrorCode(this.requestMultipart(ADD, paramMap), ApiResultCode.API_DEFINITION_EXIST);
// 校验项目是否存在
request.setProjectId("111");
request.setName("test123");
paramMap.clear();
paramMap.add("request", JSON.toJSONString(request));
assertErrorCode(this.requestMultipart(ADD, paramMap), NOT_FOUND);
// @@校验日志
checkLog(apiDefinition.getId(), OperationLogType.ADD);
// @@异常参数校验
createdGroupParamValidateTest(ApiDefinitionAddRequest.class, ADD);
// @@校验权限
request.setProjectId(DEFAULT_PROJECT_ID);
paramMap.clear();
request.setName("permission");
paramMap.add("request", JSON.toJSONString(request));
requestMultipartPermissionTest(PermissionConstants.PROJECT_API_DEFINITION_ADD, ADD, paramMap);
}
private ApiDefinitionAddRequest createApiDefinitionAddRequest() {
// 创建并返回一个 ApiDefinitionAddRequest 对象用于测试
String defaultVersion = extBaseProjectVersionMapper.getDefaultVersion(DEFAULT_PROJECT_ID);
ApiDefinitionAddRequest request = new ApiDefinitionAddRequest();
request.setName("接口定义");
request.setProtocol("HTTP");
request.setProjectId(DEFAULT_PROJECT_ID);
request.setMethod("POST");
request.setPath("/api/admin/posts");
request.setStatus(ApiDefinitionStatus.PREPARE.getValue());
request.setModuleId("root");
request.setVersionId(defaultVersion);
request.setDescription("描述内容");
request.setTags(List.of("tag1", "tag2"));
return request;
}
private ApiDefinition assertAddApiDefinition(Object request, MsHTTPElement msHttpElement, String id) {
ApiDefinition apiDefinition = apiDefinitionMapper.selectByPrimaryKey(id);
ApiDefinitionBlob apiDefinitionBlob = apiDefinitionBlobMapper.selectByPrimaryKey(id);
ApiDefinition copyApiDefinition = BeanUtils.copyBean(new ApiDefinition(), apiDefinition);
BeanUtils.copyBean(copyApiDefinition, request);
Assertions.assertEquals(apiDefinition, copyApiDefinition);
ApiDataUtils.setResolver(MsHTTPElement.class);
if(apiDefinitionBlob != null){
Assertions.assertEquals(msHttpElement, ApiDataUtils.parseObject(new String(apiDefinitionBlob.getRequest()), AbstractMsTestElement.class));
}
return apiDefinition;
}
@Test
@Order(2)
@Sql(scripts = {"/dml/init_api_definition.sql"}, config = @SqlConfig(encoding = "utf-8", transactionMode = SqlConfig.TransactionMode.ISOLATED))
public void get() throws Exception {
ApiDefinition apiDefinition = apiDefinitionMapper.selectByPrimaryKey("1001");
// @@请求成功
MvcResult mvcResult = this.requestGetWithOkAndReturn(GET + apiDefinition.getId());
ApiDataUtils.setResolver(MsHTTPElement.class);
ApiDefinitionDTO apiDefinitionDTO = getResultData(mvcResult, ApiDefinitionDTO.class);
// 校验数据是否正确
ApiDefinitionDTO copyApiDefinitionDTO = BeanUtils.copyBean(new ApiDefinitionDTO(), apiDefinitionMapper.selectByPrimaryKey(apiDefinition.getId()));
ApiDefinitionBlob apiDefinitionBlob = apiDefinitionBlobMapper.selectByPrimaryKey(apiDefinition.getId());
ApiDefinitionFollowerExample example = new ApiDefinitionFollowerExample();
example.createCriteria().andApiDefinitionIdEqualTo(apiDefinition.getId()).andUserIdEqualTo("admin");
List<ApiDefinitionFollower> followers = apiDefinitionFollowerMapper.selectByExample(example);
copyApiDefinitionDTO.setFollow(CollectionUtils.isNotEmpty(followers));
if(apiDefinitionBlob != null){
copyApiDefinitionDTO.setRequest(ApiDataUtils.parseObject(new String(apiDefinitionBlob.getRequest()), AbstractMsTestElement.class));
}
Assertions.assertEquals(apiDefinitionDTO, copyApiDefinitionDTO);
assertErrorCode(this.requestGet(GET + "111"), ApiResultCode.API_DEFINITION_NOT_EXIST);
// @@校验权限
requestGetPermissionTest(PermissionConstants.PROJECT_API_DEFINITION_READ, GET + apiDefinition.getId());
}
@Test
@Order(3)
@Sql(scripts = {"/dml/init_api_definition.sql"}, config = @SqlConfig(encoding = "utf-8", transactionMode = SqlConfig.TransactionMode.ISOLATED))
public void testUpdate() throws Exception {
LogUtils.info("delete api test");
ApiDefinitionDTO request = new ApiDefinitionDTO();
// 补充属性内容
request.setId("test-api-id");
request.setCreateUser("test-user");
request.setCreateTime(System.currentTimeMillis());
request.setUpdateUser("test-api-id");
request.setUpdateTime(System.currentTimeMillis());
request.setProjectId("test-project-id");
request.setName("test-api-name");
request.setProtocol("test-api-protocol");
request.setPos(1l);
request.setLatest(true);
request.setStatus("test-api-status");
request.setVersionId("test-api-version");
request.setDeleted(false);
ApiDefinition apiDefinition = apiDefinitionMapper.selectByPrimaryKey("1001");
ApiDefinitionUpdateRequest request = new ApiDefinitionUpdateRequest();
BeanUtils.copyBean(request, apiDefinition);
request.setPath("test.com/admin/test");
request.setName("test1");
request.setMethod("POST");
request.setModuleId("default1");
MsHTTPElement msHttpElement = MsHTTPElementTest.getMsHttpElement();
request.setRequest(ApiDataUtils.toJSONString(msHttpElement));
this.requestPostWithOk(URL_DEFINITION_UPDATE, request);
}
// 构造请求参数
MultiValueMap<String, Object> paramMap = new LinkedMultiValueMap<>();
paramMap.add("request", JSON.toJSONString(request));
FileInputStream inputStream = new FileInputStream(new File(
Objects.requireNonNull(this.getClass().getClassLoader().getResource("file/file_update_upload.JPG"))
.getPath()));
MockMultipartFile file = new MockMultipartFile("file_update_upload.JPG", "file_update_upload.JPG", MediaType.APPLICATION_OCTET_STREAM_VALUE, inputStream);
paramMap.add("files", List.of(file));
paramMap.add("request", JSON.toJSONString(request));
@Test
@Order(2)
public void testBatchUpdate() throws Exception {
LogUtils.info("delete api test");
List<String> tests = new ArrayList<>();
tests.add("test-api-id");
// 执行方法调用
MvcResult mvcResult = this.requestMultipartWithOkAndReturn(UPDATE, paramMap);
// 校验请求成功数据
ApiDefinition resultData = getResultData(mvcResult, ApiDefinition.class);
apiDefinition = assertAddApiDefinition(request, msHttpElement, resultData.getId());
// 校验数据是否存在
request.setId("111");
request.setName("test123");
paramMap.clear();
paramMap.add("request", JSON.toJSONString(request));
assertErrorCode(this.requestMultipart(UPDATE, paramMap), ApiResultCode.API_DEFINITION_NOT_EXIST);
this.requestPostWithOk(URL_DEFINITION_BATCH_UPDATE, tests);
// 校验项目是否存在
request.setProjectId("111");
request.setName("test123");
paramMap.clear();
paramMap.add("request", JSON.toJSONString(request));
assertErrorCode(this.requestMultipart(UPDATE, paramMap), NOT_FOUND);
// @@校验日志
checkLog(apiDefinition.getId(), OperationLogType.UPDATE);
// @@异常参数校验
createdGroupParamValidateTest(ApiDefinitionUpdateRequest.class, UPDATE);
// @@校验权限
request.setProjectId(DEFAULT_PROJECT_ID);
paramMap.clear();
request.setName("permission");
paramMap.add("request", JSON.toJSONString(request));
requestMultipartPermissionTest(PermissionConstants.PROJECT_API_DEFINITION_UPDATE, UPDATE, paramMap);
}
@Test
@Order(4)
public void testDel() throws Exception {
LogUtils.info("delete api test");
List<String> tests = new ArrayList<>();
tests.add("test-api-id");
@Sql(scripts = {"/dml/init_api_definition.sql"}, config = @SqlConfig(encoding = "utf-8", transactionMode = SqlConfig.TransactionMode.ISOLATED))
public void testBatchUpdate() throws Exception {
LogUtils.info("batch update api test");
ApiDefinitionBatchUpdateRequest apiDefinitionBatchUpdateRequest = new ApiDefinitionBatchUpdateRequest();
apiDefinitionBatchUpdateRequest.setProjectId(DEFAULT_PROJECT_ID);
apiDefinitionBatchUpdateRequest.setSelectIds(List.of("1001","1002","1005"));
apiDefinitionBatchUpdateRequest.setExcludeIds(List.of("1005"));
apiDefinitionBatchUpdateRequest.setSelectAll(false);
// 修改标签追加
apiDefinitionBatchUpdateRequest.setSelectIds(List.of("1001","1002"));
apiDefinitionBatchUpdateRequest.setTags(List.of("tag-append","tag-append1"));
apiDefinitionBatchUpdateRequest.setAppend(true);
this.requestPostWithOk(BATCH_UPDATE, apiDefinitionBatchUpdateRequest);
assertBatchUpdateApiDefinition(apiDefinitionBatchUpdateRequest, List.of("1001","1002"));
// 修改标签覆盖
apiDefinitionBatchUpdateRequest.setSelectIds(List.of("1003","1004"));
apiDefinitionBatchUpdateRequest.setTags(List.of("tag-append","tag-append1"));
apiDefinitionBatchUpdateRequest.setAppend(false);
this.requestPostWithOk(BATCH_UPDATE, apiDefinitionBatchUpdateRequest);
assertBatchUpdateApiDefinition(apiDefinitionBatchUpdateRequest, List.of("1003","1004"));
// 修改协议类型
apiDefinitionBatchUpdateRequest.setMethod("batch-method");
this.requestPostWithOk(BATCH_UPDATE, apiDefinitionBatchUpdateRequest);
// 修改状态
apiDefinitionBatchUpdateRequest.setStatus(ApiDefinitionStatus.DEBUGGING.getValue());
this.requestPostWithOk(BATCH_UPDATE, apiDefinitionBatchUpdateRequest);
// 修改版本
apiDefinitionBatchUpdateRequest.setVersionId("batch-version");
this.requestPostWithOk(BATCH_UPDATE, apiDefinitionBatchUpdateRequest);
// 修改全部
apiDefinitionBatchUpdateRequest.setSelectAll(true);
BaseCondition baseCondition = new BaseCondition();
baseCondition.setKeyword("st-6");
apiDefinitionBatchUpdateRequest.setCondition(baseCondition);
this.requestPostWithOk(BATCH_UPDATE, apiDefinitionBatchUpdateRequest);
// 校验项目是否存在
apiDefinitionBatchUpdateRequest.setProjectId("111");
apiDefinitionBatchUpdateRequest.setMethod("test123");
this.requestPostWithOk(URL_DEFINITION_DELETE, tests);
assertErrorCode(this.requestPost(BATCH_UPDATE, apiDefinitionBatchUpdateRequest), NOT_FOUND);
// @@校验日志
checkLog("1001", OperationLogType.UPDATE);
checkLog("1002", OperationLogType.UPDATE);
checkLog("1003", OperationLogType.UPDATE);
checkLog("1004", OperationLogType.UPDATE);
// @@异常参数校验
createdGroupParamValidateTest(ApiDefinitionBatchRequest.class, BATCH_UPDATE);
// @@校验权限
apiDefinitionBatchUpdateRequest.setProjectId(DEFAULT_PROJECT_ID);
apiDefinitionBatchUpdateRequest.setMethod("permission");
requestPostPermissionTest(PermissionConstants.PROJECT_API_DEFINITION_UPDATE, BATCH_UPDATE, apiDefinitionBatchUpdateRequest);
}
private void assertBatchUpdateApiDefinition(ApiDefinitionBatchUpdateRequest request, List<String> ids) {
ApiDefinitionExample apiDefinitionExample = new ApiDefinitionExample();
apiDefinitionExample.createCriteria().andIdIn(ids);
List<ApiDefinition> apiDefinitions = apiDefinitionMapper.selectByExample(apiDefinitionExample);
apiDefinitions.forEach(item -> {
if(request.getStatus() != null){
Assertions.assertEquals(item.getStatus(), request.getStatus());
}
if(request.getMethod() != null){
Assertions.assertEquals(item.getMethod(), request.getMethod());
}
if(request.getVersionId() != null) {
Assertions.assertEquals(item.getVersionId(), request.getVersionId());
}
if(request.getTags() != null) {
if (request.isAppend()) {
Assertions.assertTrue(JSON.parseArray(item.getTags(), String.class).containsAll(request.getTags()));
} else {
Assertions.assertEquals(item.getTags(), JSON.toJSONString(request.getTags()));
}
}
});
}
@Test
@Order(5)
public void testBatchDel() throws Exception {
LogUtils.info("delete api test");
List<String> tests = new ArrayList<>();
tests.add("test-api-id");
this.requestPostWithOk(URL_DEFINITION_BATCH_DELETE, tests);
@Sql(scripts = {"/dml/init_api_definition.sql"}, config = @SqlConfig(encoding = "utf-8", transactionMode = SqlConfig.TransactionMode.ISOLATED))
public void copy() throws Exception {
LogUtils.info("copy api test");
ApiDefinition apiDefinition = apiDefinitionMapper.selectByPrimaryKey("1001");
ApiDefinitionCopyRequest request = new ApiDefinitionCopyRequest();
request.setId(apiDefinition.getId());
MvcResult mvcResult = this.requestPostWithOkAndReturn(COPY, request);
ApiDefinition resultData = getResultData(mvcResult, ApiDefinition.class);
// @@校验日志
checkLog(resultData.getId(), OperationLogType.UPDATE);
request.setId("121");
assertErrorCode(this.requestPost(COPY, request), ApiResultCode.API_DEFINITION_NOT_EXIST);
// @@校验权限
requestPostPermissionTest(PermissionConstants.PROJECT_API_DEFINITION_UPDATE, COPY, request);
}
@Test
@Order(6)
@Sql(scripts = {"/dml/init_api_definition.sql"}, config = @SqlConfig(encoding = "utf-8", transactionMode = SqlConfig.TransactionMode.ISOLATED))
public void batchMove() throws Exception {
LogUtils.info("batch move api test");
ApiDefinitionBatchMoveRequest request = new ApiDefinitionBatchMoveRequest();
request.setModuleId(DEFAULT_MODULE_ID);
request.setProjectId(DEFAULT_PROJECT_ID);
// 移动选中
request.setSelectIds(List.of("1001","1002","1005"));
request.setExcludeIds(List.of("1005"));
request.setDeleteAll(false);
request.setSelectAll(false);
this.requestPostWithOkAndReturn(BATCH_MOVE, request);
// @@校验日志
checkLog("1001", OperationLogType.UPDATE);
checkLog("1002", OperationLogType.UPDATE);
checkLog("1005", OperationLogType.UPDATE);
// 移动全部 条件为关键字为st-6的数据
request.setSelectAll(true);
BaseCondition baseCondition = new BaseCondition();
baseCondition.setKeyword("st-6");
request.setCondition(baseCondition);
this.requestPostWithOkAndReturn(BATCH_MOVE, request);
// @@校验日志
checkLog("1006", OperationLogType.UPDATE);
// @@校验权限
requestPostPermissionTest(PermissionConstants.PROJECT_API_DEFINITION_UPDATE, BATCH_MOVE, request);
}
@Test
@Order(7)
@Sql(scripts = {"/dml/init_api_definition.sql"}, config = @SqlConfig(encoding = "utf-8", transactionMode = SqlConfig.TransactionMode.ISOLATED))
public void follow() throws Exception {
ApiDefinition apiDefinition = apiDefinitionMapper.selectByPrimaryKey("1006");
// @@关注
// @@请求成功
this.requestGetWithOk(FOLLOW + apiDefinition.getId());
ApiDefinitionFollowerExample example = new ApiDefinitionFollowerExample();
example.createCriteria().andApiDefinitionIdEqualTo(apiDefinition.getId()).andUserIdEqualTo("admin");
List<ApiDefinitionFollower> followers = apiDefinitionFollowerMapper.selectByExample(example);
Assertions.assertTrue(CollectionUtils.isNotEmpty(followers));
// @@校验日志
checkLog(apiDefinition.getId(), OperationLogType.UPDATE);
assertErrorCode(this.requestGet(FOLLOW + "111"), ApiResultCode.API_DEFINITION_NOT_EXIST);
// @@取消关注
// @@请求成功
this.requestGetWithOk(FOLLOW + apiDefinition.getId());
ApiDefinitionFollowerExample unFollowerExample = new ApiDefinitionFollowerExample();
example.createCriteria().andApiDefinitionIdEqualTo(apiDefinition.getId()).andUserIdEqualTo("admin");
List<ApiDefinitionFollower> unFollowers = apiDefinitionFollowerMapper.selectByExample(unFollowerExample);
Assertions.assertTrue(CollectionUtils.isEmpty(unFollowers));
// @@校验日志
checkLog(apiDefinition.getId(), OperationLogType.UPDATE);
assertErrorCode(this.requestGet(FOLLOW + "111"), ApiResultCode.API_DEFINITION_NOT_EXIST);
// @@校验权限
requestGetPermissionTest(PermissionConstants.PROJECT_API_DEFINITION_UPDATE, FOLLOW + apiDefinition.getId());
}
@Test
@Order(8)
@Sql(scripts = {"/dml/init_api_definition.sql"}, config = @SqlConfig(encoding = "utf-8", transactionMode = SqlConfig.TransactionMode.ISOLATED))
public void version() throws Exception {
ApiDefinition apiDefinition = apiDefinitionMapper.selectByPrimaryKey("1001");
// @@请求成功
MvcResult mvcResult = this.requestGetWithOk(VERSION + apiDefinition.getId()).andReturn();
ApiDataUtils.setResolver(MsHTTPElement.class);
List<ApiDefinitionVersionDTO> apiDefinitionVersionDTO = getResultDataArray(mvcResult, ApiDefinitionVersionDTO.class);
// 校验数据是否正确
List<ApiDefinitionVersionDTO> copyApiDefinitionVersionDTO = extApiDefinitionMapper.getApiDefinitionByRefId(apiDefinition.getRefId());
Assertions.assertEquals(apiDefinitionVersionDTO, copyApiDefinitionVersionDTO);
assertErrorCode(this.requestGet(VERSION + "111"), ApiResultCode.API_DEFINITION_NOT_EXIST);
// @@校验权限
requestGetPermissionTest(PermissionConstants.PROJECT_API_DEFINITION_READ, VERSION + apiDefinition.getId());
}
@Test
@Order(9)
@Sql(scripts = {"/dml/init_api_definition.sql"}, config = @SqlConfig(encoding = "utf-8", transactionMode = SqlConfig.TransactionMode.ISOLATED))
public void testDel() throws Exception {
LogUtils.info("delete api test");
ApiDefinition apiDefinition = apiDefinitionMapper.selectByPrimaryKey("1001");
// @只存在一个版本
ApiDefinitionDeleteRequest apiDefinitionDeleteRequest = new ApiDefinitionDeleteRequest();
apiDefinitionDeleteRequest.setId(apiDefinition.getId());
apiDefinitionDeleteRequest.setProjectId(DEFAULT_PROJECT_ID);
apiDefinitionDeleteRequest.setDeleteAll(false);
// @@请求成功
this.requestPostWithOkAndReturn(DELETE, apiDefinitionDeleteRequest);
checkLog(apiDefinition.getId(), OperationLogType.DELETE);
ApiDefinition apiDefinitionInfo = apiDefinitionMapper.selectByPrimaryKey(apiDefinition.getId());
Assertions.assertTrue(apiDefinitionInfo.getDeleted());
Assertions.assertEquals(apiDefinitionInfo.getDeleteUser(), "admin");
Assertions.assertNotNull(apiDefinitionInfo.getDeleteTime());
// @存在多个版本
MvcResult mvcResult = this.requestGetWithOk(VERSION + apiDefinition.getId()).andReturn();
ApiDataUtils.setResolver(MsHTTPElement.class);
List<ApiDefinitionVersionDTO> apiDefinitionVersionDTO = getResultDataArray(mvcResult, ApiDefinitionVersionDTO.class);
if(!apiDefinitionVersionDTO.isEmpty()){
ApiDefinition delApiDefinition = apiDefinitionMapper.selectByPrimaryKey(apiDefinitionDeleteRequest.getId());
// 列表删除
apiDefinitionDeleteRequest.setDeleteAll(false);
apiDefinitionDeleteRequest.setId("1004");
this.requestPostWithOkAndReturn(DELETE, apiDefinitionDeleteRequest);
checkLog(apiDefinitionDeleteRequest.getId(), OperationLogType.DELETE);
Assertions.assertTrue(delApiDefinition.getDeleted());
Assertions.assertEquals(delApiDefinition.getDeleteUser(), "admin");
Assertions.assertNotNull(delApiDefinition.getDeleteTime());
// 删除的数据为最新版本需要更新最近一条数据为最新数据
if(delApiDefinition.getLatest()){
ApiDefinitionExample example = new ApiDefinitionExample();
example.createCriteria().andRefIdEqualTo(delApiDefinition.getRefId()).andDeletedEqualTo(false).andProjectIdEqualTo(delApiDefinition.getProjectId());
example.setOrderByClause("update_time DESC");
apiDefinitionMapper.selectByExample(example).stream().findFirst().ifPresent(updateApiDefinition -> Assertions.assertTrue(updateApiDefinition.getLatest()));
}
// 全部删除
apiDefinitionDeleteRequest.setDeleteAll(true);
apiDefinitionDeleteRequest.setId("1006");
// @@请求成功
this.requestPostWithOkAndReturn(DELETE, apiDefinitionDeleteRequest);
List<String> ids = extApiDefinitionMapper.getApiDefinitionByRefId(apiDefinitionDeleteRequest.getId()).stream().map(ApiDefinitionVersionDTO::getId).toList();
ApiDefinitionExample apiDefinitionExample = new ApiDefinitionExample();
apiDefinitionExample.createCriteria().andIdIn(ids).andDeletedEqualTo(false);
List<ApiDefinition> apiDefinitions = apiDefinitionMapper.selectByExample(apiDefinitionExample);
if(CollectionUtils.isNotEmpty(apiDefinitions)){
apiDefinitions.forEach(item -> {
Assertions.assertTrue(item.getDeleted());
Assertions.assertEquals(item.getDeleteUser(), "admin");
Assertions.assertNotNull(item.getDeleteTime());
});
}
checkLog(apiDefinitionDeleteRequest.getId(), OperationLogType.DELETE);
}
// @@校验权限
requestPostPermissionTest(PermissionConstants.PROJECT_API_DEFINITION_DELETE, DELETE, apiDefinitionDeleteRequest);
}
@Test
@Order(10)
@Sql(scripts = {"/dml/init_api_definition.sql"}, config = @SqlConfig(encoding = "utf-8", transactionMode = SqlConfig.TransactionMode.ISOLATED))
public void testBatchDel() throws Exception {
LogUtils.info("batch delete api test");
ApiDefinitionBatchRequest request = new ApiDefinitionBatchRequest();
request.setProjectId(DEFAULT_PROJECT_ID);
// 删除选中
request.setSelectIds(List.of("1001","1002","1005"));
request.setExcludeIds(List.of("1005"));
request.setDeleteAll(false);
request.setSelectAll(false);
this.requestPostWithOkAndReturn(BATCH_DELETE, request);
// @@校验日志
checkLog("1001", OperationLogType.DELETE);
checkLog("1002", OperationLogType.DELETE);
checkLog("1005", OperationLogType.DELETE);
// 删除全部 条件为关键字为st-6的数据
request.setSelectAll(true);
BaseCondition baseCondition = new BaseCondition();
baseCondition.setKeyword("st-6");
request.setCondition(baseCondition);
this.requestPostWithOkAndReturn(BATCH_DELETE, request);
// @@校验日志
checkLog("1006", OperationLogType.DELETE);
// @@校验权限
requestPostPermissionTest(PermissionConstants.PROJECT_API_DEFINITION_DELETE, BATCH_DELETE, request);
}
@Test
@Order(11)
@Sql(scripts = {"/dml/init_api_definition.sql"}, config = @SqlConfig(encoding = "utf-8", transactionMode = SqlConfig.TransactionMode.ISOLATED))
public void getPage() throws Exception {
ApiDefinitionPageRequest request = new ApiDefinitionPageRequest();
request.setProjectId(DEFAULT_PROJECT_ID);
request.setCurrent(1);
request.setPageSize(10);
this.requestPost(URL_DEFINITION_PAGE, request);
this.requestPost(PAGE, request);
request.setSort(new HashMap<>() {{
put("createTime", "asc");
}});
@ -178,7 +588,7 @@ public class ApiDefinitionControllerTests extends BaseTest {
break;
}
MvcResult mvcResult = this.requestPostWithOkAndReturn(URL_DEFINITION_PAGE, request);
MvcResult mvcResult = this.requestPostWithOkAndReturn(PAGE, request);
// 获取返回值
String returnData = mvcResult.getResponse().getContentAsString(StandardCharsets.UTF_8);
ResultHolder resultHolder = JSON.parseObject(returnData, ResultHolder.class);

View File

@ -1,12 +1,16 @@
-- 插入测试数据
TRUNCATE TABLE api_definition;
TRUNCATE TABLE project_version;
TRUNCATE TABLE api_test_case;
TRUNCATE TABLE api_report;
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 ('1001', 'test-1', 'HTTP', 'POST', '/api/admin/1', 'Prepare', 1001, NULL, 1, '100001100001', 'root', b'1', '100570499574136985', '34', NULL, 1699500298164, 'admin', 1699500298164, 'admin', NULL, NULL, b'0');
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 ('1002', 'test-2', 'HTTP', 'GET', '/api/admin/2', 'Underway', 1002, NULL, 1, '100001100001', 'root', b'1', '1005704995741369851', '34', NULL, 1699500298164, 'admin', 1699500298164, 'admin', NULL, NULL, b'0');
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 ('1003', 'test-3', 'HTTP', 'POST', '/api/admin/3', 'Completed', 1003, NULL, 1, '100001100001', 'root', b'1', '100570499574136985', '34', NULL, 1699500298164, 'admin', 1699500298164, 'admin', NULL, NULL, b'0');
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 ('1004', 'test-4', 'HTTP', 'GET', '/api/admin/4', 'Prepare', 1004, NULL, 1, '100001100001', 'root', b'1', '1005704995741369851', '34', NULL, 1699500298164, 'admin', 1699500298164, 'admin', NULL, NULL, b'0');
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 ('1005', 'test-5', 'HTTP', 'POST', '/api/admin/5', 'Underway', 1005, NULL, 1, '100001100001', 'root', b'1', '1005704995741369851', '34', NULL, 1699500298164, 'admin', 1699500298164, 'admin', NULL, NULL, b'0');
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 ('1006', 'test-6', 'HTTP', 'GET', '/api/admin/6', 'Completed', 1006, NULL, 1, '100001100001', 'root', b'1', '100570499574136985', '34', NULL, 1699500298164, 'admin', 1699500298164, 'admin', NULL, NULL, b'0');
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 ('1001', 'test-1', 'HTTP', 'POST', '/api/admin/1', 'Prepare', 1001, '[\"test1\",\"te\"]', 1, '100001100001', 'root', b'1', '100570499574136985', '1001', NULL, 1699500298164, 'admin', 1699500298164, 'admin', NULL, NULL, b'0');
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 ('1002', 'test-2', 'HTTP', 'GET', '/api/admin/2', 'Underway', 1002, '[\"test2\",\"te\"]', 1, '100001100001', 'root', b'1', '1005704995741369851', '1002', NULL, 1699500298164, 'admin', 1699500298164, 'admin', NULL, NULL, b'0');
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 ('1003', 'test-3', 'HTTP', 'POST', '/api/admin/3', 'Completed', 1003, '[\"test3\",\"te\"]', 1, '100001100001', 'root', b'1', '100570499574136985', '1002', NULL, 1699500298164, 'admin', 1699500298164, 'admin', NULL, NULL, b'0');
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 ('1004', 'test-4', 'HTTP', 'GET', '/api/admin/4', 'Prepare', 1004, '[\"test4\",\"te\"]', 1, '100001100001', 'root', b'1', '1005704995741369851', '1004', NULL, 1699500298164, 'admin', 1699500298164, 'admin', NULL, NULL, b'0');
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 ('1005', 'test-5', 'HTTP', 'POST', '/api/admin/5', 'Underway', 1005, '[\"test5\",\"te\"]', 1, '100001100001', 'root', b'0', '1005704995741369851', '1004', NULL, 1699500298164, 'admin', 1699500298164, 'admin', NULL, NULL, b'0');
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 ('1006', 'test-6', 'HTTP', 'GET', '/api/admin/6', 'Completed', 1006, '[\"test6\",\"te\"]', 1, '100001100001', 'root', b'1', '100570499574136985', '1006', NULL, 1699500298164, 'admin', 1699500298164, 'admin', NULL, NULL, b'0');
INSERT INTO project_version (id, project_id, name, description, status, latest, publish_time, start_time, end_time, create_time, create_user) VALUES ('100570499574136985', '100001100001', 'v1.0.0', NULL, 'open', 1, UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000, 'admin');

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB