feat(接口测试): 接口管理模块接口 MOCK 模块

This commit is contained in:
lan-yonghui 2023-12-25 18:31:55 +08:00 committed by Craftsman
parent 1a723bc0f7
commit 9b00348b9a
25 changed files with 1477 additions and 3 deletions

View File

@ -260,6 +260,12 @@ public class PermissionConstants {
public static final String PROJECT_API_DEFINITION_CASE_RECOVER = "PROJECT_API_DEFINITION_CASE:READ+RECOVER";
public static final String PROJECT_API_DEFINITION_CASE_EXECUTE = "PROJECT_API_DEFINITION_CASE:READ+EXECUTE";
public static final String PROJECT_API_DEFINITION_MOCK_READ = "PROJECT_API_DEFINITION_MOCK:READ";
public static final String PROJECT_API_DEFINITION_MOCK_ADD = "PROJECT_API_DEFINITION_MOCK:READ+ADD";
public static final String PROJECT_API_DEFINITION_MOCK_UPDATE = "PROJECT_API_DEFINITION_MOCK:READ+UPDATE";
public static final String PROJECT_API_DEFINITION_MOCK_DELETE = "PROJECT_API_DEFINITION_MOCK:READ+DELETE";
public static final String PROJECT_API_DEFINITION_MOCK_EXECUTE = "PROJECT_API_DEFINITION_MOCK:READ+EXECUTE";
/*------ end: API_MANAGEMENT ------*/
//个人中心
/*------ start: PERSONAL_CENTER ------*/

View File

@ -16,6 +16,7 @@ public class FileAssociationSourceUtil {
public static final String SOURCE_TYPE_API_DEBUG = "API_DEBUG";
public static final String SOURCE_TYPE_API_TEST_CASE = "API_TEST_CASE";
public static final String SOURCE_TYPE_API_DEFINITION = "API_DEFINITION";
public static final String SOURCE_TYPE_API_DEFINITION_MOCK = "API_DEFINITION_MOCK";
public static final Map<String, String> QUERY_SQL = new HashMap<>();
static {
@ -24,6 +25,7 @@ public class FileAssociationSourceUtil {
QUERY_SQL.put(SOURCE_TYPE_API_DEBUG, "SELECT id AS sourceId,name AS sourceName FROM api_debug");
QUERY_SQL.put(SOURCE_TYPE_API_TEST_CASE, "SELECT id AS sourceId,name AS sourceName FROM api_test_case");
QUERY_SQL.put(SOURCE_TYPE_API_DEFINITION, "SELECT id AS sourceId,name AS sourceName FROM api_definition");
QUERY_SQL.put(SOURCE_TYPE_API_DEFINITION_MOCK, "SELECT id AS sourceId,name AS sourceName FROM api_definition_mock");
}
public static void validate(String type) {

View File

@ -299,10 +299,12 @@ api_definition_custom_field.field_id.not_blank=自定义字段ID不能为空
api_module.not.exist=模块不存在
permission.api.name=接口测试
permission.api_mock.name=接口 MOCK
api_debug_exist=接口已存在
follow=关注
unfollow=取消关注
api_definition_exist=接口已存在
api_definition_mock_exist=接口 MOCK 已存在
execute_resource_pool_not_config_error=请在【项目管理-应用管理-接口测试】中选择资源池
resource_pool_execute_error=资源池调用失败

View File

@ -305,9 +305,12 @@ api_definition_custom_field.field_id.not_blank=Field ID cannot be empty
api_module.not.exist=The module does not exist
permission.api.name=API Test
permission.api_mock.name=API MOCK
api_debug_exist=The API already exists
follow=Follow
unfollow=Unfollow
api_definition_exist=The API already exists
api_definition_mock_exist=The API MOCK already exists
execute_resource_pool_not_config_error=Select a resource pool in 【Project Management - Application Management - Interface Testing】
resource_pool_execute_error=The resource pool call failed

View File

@ -305,9 +305,12 @@ api_definition_custom_field.field_id.not_blank=自定义字段ID不能为空
api_module.not.exist=模块不存在
permission.api.name=接口测试
permission.api_mock.name=接口 MOCK
api_debug_exist=接口已存在
follow=关注
unfollow=取消关注
api_definition_exist=接口已存在
api_definition_mock_exist=接口 MOCK 已存在
execute_resource_pool_not_config_error=请在【项目管理-应用管理-接口测试】中选择资源池
resource_pool_execute_error=资源池调用失败

View File

@ -305,9 +305,12 @@ api_definition_custom_field.field_id.not_blank=自定義字段ID不能爲空
api_module.not.exist=模塊不存在
permission.api.name=接口測試
permission.api_mock.name=接口 MOCK
api_debug_exist=接口已存在
follow=关注
unfollow=取消关注
api_definition_exist=接口已存在
api_definition_mock_exist=接口 MOCK 已存在
execute_resource_pool_not_config_error=請在【項目管理-應用管理-接口測試】中選擇資源池
resource_pool_execute_error=資源池調用失敗

View File

@ -471,6 +471,7 @@ permission.api_test.name=接口测试
permission.api_debug.name=接口调试
permission.api_definition.name=接口管理
permission.api_case.name=接口用例
permission.api_mock.name=接口Mock
permission.api_definition.import=导入
permission.api_definition.export=导出
permission.api_definition.execute=执行

View File

@ -481,6 +481,7 @@ permission.api_test.name=API Test
permission.api_debug.name=API Debug
permission.api_definition.name=API Management
permission.api_case.name=API Case
permission.api_mock.name=API Mock
permission.api_definition.import=Import
permission.api_definition.export=Export
permission.api_definition.execute=Execute

View File

@ -476,6 +476,8 @@ status_item.bug_rejected=已拒绝
permission.api_test.name=接口测试
permission.api_debug.name=接口调试
permission.api_definition.name=接口管理
permission.api_case.name=接口用例
permission.api_mock.name=接口Mock
permission.api_definition.import=导入
permission.api_definition.export=导出
permission.api_definition.execute=执行

View File

@ -477,6 +477,7 @@ permission.api_test.name=接口測試
permission.api_debug.name=接口調試
permission.api_definition.name=接口管理
permission.api_case.name=接口用例
permission.api_mock.name=接口Mock
permission.api_definition.import=導入
permission.api_definition.export=導出
permission.api_definition.execute=執行

View File

@ -5,5 +5,5 @@ package io.metersphere.api.constants;
* @CreateTime: 2023-11-16 16:57
*/
public enum ApiResourceType {
API_DEBUG, API, API_CASE, API_SCENARIO
API_DEBUG, API, API_CASE, API_SCENARIO, API_MOCK
}

View File

@ -0,0 +1,105 @@
package io.metersphere.api.controller.definition;
import com.github.pagehelper.Page;
import com.github.pagehelper.page.PageMethod;
import io.metersphere.api.domain.ApiDefinitionMock;
import io.metersphere.api.dto.definition.ApiDefinitionMockDTO;
import io.metersphere.api.dto.definition.request.*;
import io.metersphere.api.service.definition.ApiDefinitionMockLogService;
import io.metersphere.api.service.definition.ApiDefinitionMockService;
import io.metersphere.sdk.constants.PermissionConstants;
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.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.Logical;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.util.List;
/**
* @author lan
*/
@RestController
@RequestMapping(value = "/api/definition/mock")
@Tag(name = "接口测试-接口管理-接口定义-Mock")
public class ApiDefinitionMockController {
@Resource
private ApiDefinitionMockService apiDefinitionMockService;
@PostMapping("/page")
@Operation(summary = "接口测试-接口管理-接口 Mock")
@RequiresPermissions(PermissionConstants.PROJECT_API_DEFINITION_MOCK_READ)
public Pager<List<ApiDefinitionMockDTO>> getPage(@Validated @RequestBody ApiDefinitionMockPageRequest request) {
Page<Object> page = PageMethod.startPage(request.getCurrent(), request.getPageSize(),
StringUtils.isNotBlank(request.getSortString()) ? request.getSortString() : "create_time desc");
return PageUtils.setPageInfo(page, apiDefinitionMockService.getPage(request));
}
@PostMapping(value = "/detail")
@Operation(summary = "接口测试-接口管理-获取 Mock 详情")
@RequiresPermissions(PermissionConstants.PROJECT_API_DEFINITION_MOCK_READ)
public ApiDefinitionMockDTO detail(@Validated @RequestBody ApiDefinitionMockRequest request) {
return apiDefinitionMockService.detail(request);
}
@PostMapping(value = "/add")
@Operation(summary = "接口测试-接口管理-添加 Mock")
@RequiresPermissions(PermissionConstants.PROJECT_API_DEFINITION_MOCK_ADD)
@Log(type = OperationLogType.ADD, expression = "#msClass.addLog(#request)", msClass = ApiDefinitionMockLogService.class)
public ApiDefinitionMock add(@Validated @RequestBody ApiDefinitionMockAddRequest request) {
return apiDefinitionMockService.create(request, SessionUtils.getUserId());
}
@PostMapping(value = "/update")
@Operation(summary = "接口测试-接口管理-更新 Mock")
@RequiresPermissions(PermissionConstants.PROJECT_API_DEFINITION_MOCK_UPDATE)
@Log(type = OperationLogType.UPDATE, expression = "#msClass.updateLog(#request)", msClass = ApiDefinitionMockLogService.class)
public ApiDefinitionMock update(@Validated @RequestBody ApiDefinitionMockUpdateRequest request) {
return apiDefinitionMockService.update(request, SessionUtils.getUserId());
}
@GetMapping(value = "/enable/{id}")
@Operation(summary = "接口测试-接口管理-更新 Mock-更新状态")
@RequiresPermissions(PermissionConstants.PROJECT_API_DEFINITION_MOCK_UPDATE)
@Log(type = OperationLogType.UPDATE, expression = "#msClass.updateEnableLog(#id)", msClass = ApiDefinitionMockLogService.class)
public void updateEnable(@PathVariable String id) {
apiDefinitionMockService.updateEnable(id);
}
@PostMapping(value = "/delete")
@Operation(summary = "接口测试-接口管理-删除 Mock")
@RequiresPermissions(PermissionConstants.PROJECT_API_DEFINITION_MOCK_DELETE)
@Log(type = OperationLogType.DELETE, expression = "#msClass.delLog(#request)", msClass = ApiDefinitionMockLogService.class)
public void delete(@Validated @RequestBody ApiDefinitionMockRequest request) {
apiDefinitionMockService.delete(request, SessionUtils.getUserId());
}
@PostMapping(value = "/copy")
@Operation(summary = "接口测试-接口管理-复制 Mock")
@RequiresPermissions(PermissionConstants.PROJECT_API_DEFINITION_MOCK_UPDATE)
@Log(type = OperationLogType.UPDATE, expression = "#msClass.copyLog(#request)", msClass = ApiDefinitionMockLogService.class)
public ApiDefinitionMock copy(@Validated @RequestBody ApiDefinitionMockRequest request) {
return apiDefinitionMockService.copy(request, SessionUtils.getUserId());
}
@PostMapping("/upload/temp/file")
@Operation(summary = "上传接口定义所需的文件资源并返回文件ID")
@RequiresPermissions(logical = Logical.OR, value = {PermissionConstants.PROJECT_API_DEFINITION_MOCK_ADD, PermissionConstants.PROJECT_API_DEFINITION_MOCK_UPDATE})
public String uploadTempFile(@RequestParam("file") MultipartFile file) {
return apiDefinitionMockService.uploadTempFile(file);
}
}

View File

@ -12,7 +12,8 @@ public enum ApiResultCode implements IResultCode {
API_DEFINITION_NOT_EXIST(104003, "resource_not_exist"),
API_DEFINITION_MODULE_NOT_EXIST(10404, "resource_not_exist"),
RESOURCE_POOL_EXECUTE_ERROR(104005, "resource_pool_execute_error"),
EXECUTE_RESOURCE_POOL_NOT_CONFIG(104006, "execute_resource_pool_not_config_error");
EXECUTE_RESOURCE_POOL_NOT_CONFIG(104006, "execute_resource_pool_not_config_error"),
API_DEFINITION_MOCK_EXIST(104007, "api_definition_mock_exist");
private final int code;

View File

@ -0,0 +1,39 @@
package io.metersphere.api.dto.definition;
import io.metersphere.api.domain.ApiDefinitionMock;
import io.metersphere.plugin.api.spi.AbstractMsTestElement;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.List;
/**
* @author lan
*/
@Data
@EqualsAndHashCode(callSuper = false)
public class ApiDefinitionMockDTO extends ApiDefinitionMock {
@Schema(description = "请求内容")
private AbstractMsTestElement matching;
@Schema(description = "响应内容")
private List<HttpResponse> response;
@Schema(description = "创建人名称")
private String createUserName;
@Schema(description = "接口编号")
private Long apiNum;
@Schema(description = "接口名称")
private String apiName;
@Schema(description = "接口路径")
private String apiPath;
@Schema(description = "接口类型")
private String apiMethod;
}

View File

@ -0,0 +1,61 @@
package io.metersphere.api.dto.definition.request;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.LinkedHashSet;
import java.util.List;
/**
* @author lan
*/
@Data
public class ApiDefinitionMockAddRequest implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
@Schema(description = "项目ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{api_definition_mock.project_id.not_blank}")
@Size(min = 1, max = 50, message = "{api_definition_mock.project_id.length_range}")
private String projectId;
@Schema(description = "接口 mock 名称", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{api_definition_mock.name.not_blank}")
@Size(min = 1, max = 255, message = "{api_definition_mock.name.length_range}")
private String name;
@Schema(description = "标签")
private LinkedHashSet<@NotBlank String> tags;
@Schema(description = "请求内容")
@NotBlank
private String matching;
@Schema(description = "请求内容")
@NotBlank
private String response;
@Schema(description = "接口fk", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{api_definition_mock.api_definition_id.not_blank}")
@Size(min = 1, max = 50, message = "{api_definition_mock.api_definition_id.length_range}")
private String apiDefinitionId;
/**
* 新上传的文件ID
* 创建时先按ID创建目录再把文件放入目录
*/
@Schema(description = "新上传的文件ID")
private List<String> uploadFileIds;
/**
* 新关联的文件ID
*/
@Schema(description = "关联文件ID")
private List<String> linkFileIds;
}

View File

@ -0,0 +1,38 @@
package io.metersphere.api.dto.definition.request;
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;
/**
* @author lan
*/
@Data
@EqualsAndHashCode(callSuper = false)
public class ApiDefinitionMockPageRequest extends BasePageRequest {
@Schema(description = "接口 mock pk")
@Size(min = 1, max = 50, message = "{api_definition_mock.id.length_range}")
private String id;
@Schema(description = "接口 mock 名称")
@Size(min = 1, max = 255, message = "{api_definition_mock.name.length_range}")
private String name;
@Schema(description = "项目ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{api_definition_mock.project_id.not_blank}")
@Size(min = 1, max = 50, message = "{api_definition_mock.project_id.length_range}")
private String projectId;
@Schema(description = "启用/禁用(状态为 false 时为禁用)")
private boolean enable = true;
@Schema(description = "接口fk", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{api_definition_mock.api_definition_id.not_blank}")
@Size(min = 1, max = 50, message = "{api_definition_mock.api_definition_id.length_range}")
private String apiDefinitionId;
}

View File

@ -0,0 +1,35 @@
package io.metersphere.api.dto.definition.request;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
* @author lan
*/
@Data
public class ApiDefinitionMockRequest implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
@Schema(description = "接口 mock pk", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{api_definition_mock.id.not_blank}")
@Size(min = 1, max = 50, message = "{api_definition_mock.id.length_range}")
private String id;
@Schema(description = "项目ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{api_definition_mock.project_id.not_blank}")
@Size(min = 1, max = 50, message = "{api_definition_mock.project_id.length_range}")
private String projectId;
@Schema(description = "接口fk", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{api_definition_mock.api_definition_id.not_blank}")
@Size(min = 1, max = 50, message = "{api_definition_mock.api_definition_id.length_range}")
private String apiDefinitionId;
}

View File

@ -0,0 +1,39 @@
package io.metersphere.api.dto.definition.request;
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.Serial;
import java.util.List;
/**
* @author lan
*/
@Data
@EqualsAndHashCode(callSuper = false)
public class ApiDefinitionMockUpdateRequest extends ApiDefinitionMockAddRequest {
@Serial
private static final long serialVersionUID = 1L;
@Schema(description = "接口 mock pk", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{api_definition_mock.id.not_blank}")
@Size(min = 1, max = 50, message = "{api_definition_mock.id.length_range}")
private String id;
/**
* 删除本地上传的文件ID
*/
@Schema(description = "删除的文件ID")
private List<String> deleteFileIds;
/**
* 删除关联的文件ID
*/
@Schema(description = "取消关联文件ID")
private List<String> unLinkRefIds;
}

View File

@ -0,0 +1,13 @@
package io.metersphere.api.mapper;
import io.metersphere.api.dto.definition.ApiDefinitionMockDTO;
import io.metersphere.api.dto.definition.request.ApiDefinitionMockPageRequest;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface ExtApiDefinitionMockMapper {
List<ApiDefinitionMockDTO> list(@Param("request") ApiDefinitionMockPageRequest request);
}

View File

@ -0,0 +1,136 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="io.metersphere.api.mapper.ExtApiDefinitionMockMapper">
<select id="list" resultType="io.metersphere.api.dto.definition.ApiDefinitionMockDTO">
select
m.id, m.create_time, m.update_time, m.create_user, m.`name`, m.tags, m.`enable`, m.expect_num, m.project_id,
m.api_definition_id, u.name as create_user_name
from api_definition_mock m
left join `user` u on u.id = m.create_user
where m.api_definition_id = #{request.apiDefinitionId}
<include refid="queryWhereCondition"/>
</select>
<sql id="queryWhereCondition">
<if test="request.keyword != null and request.keyword != ''">
and (
m.expect_num like concat('%', #{request.keyword},'%')
or m.name like concat('%', #{request.keyword},'%')
or m.tags like JSON_CONTAINS(tags, concat('["',#{request.keyword},'"]'))
)
</if>
<if test="request.projectId != null and request.projectId != ''">
and m.project_id = #{request.projectId}
</if>
<include refid="filters">
<property name="filter" value="request.filter"/>
</include>
<if test="request.combine != null and request.combine != ''">
<include refid="combine">
<property name="condition" value="request.combine"/>
<property name="name" value="request.name"/>
<property name="ObjectTags" value="request.combine.tags"/>
</include>
</if>
</sql>
<sql id="filters">
<if test="${filter} != null and ${filter}.size() > 0">
<foreach collection="${filter}.entrySet()" index="key" item="values">
<if test="values != null and values.size() > 0">
<choose>
<when test="key=='enable'">
and m.enable in
<include refid="io.metersphere.system.mapper.BaseMapper.filterInWrapper"/>
</when>
<when test="key=='api_method'">
and m.api_method in
<include refid="io.metersphere.system.mapper.BaseMapper.filterInWrapper"/>
</when>
<when test="key=='api_path'">
and m.api_path in
<include refid="io.metersphere.system.mapper.BaseMapper.filterInWrapper"/>
</when>
<when test="key=='create_user'">
and m.create_user in
<include refid="io.metersphere.system.mapper.BaseMapper.filterInWrapper"/>
</when>
</choose>
</if>
</foreach>
</if>
</sql>
<sql id="combine">
<if test='${condition}.name != null and (${name} == null or ${name} == "")'>
and m.name
<include refid="io.metersphere.system.mapper.BaseMapper.condition">
<property name="object" value="${condition}.name"/>
</include>
</if>
<if test="${condition}.updateTime != null">
and m.update_time
<include refid="io.metersphere.system.mapper.BaseMapper.condition">
<property name="object" value="${condition}.updateTime"/>
</include>
</if>
<if test="${condition}.createTime != null">
and m.create_time
<include refid="io.metersphere.system.mapper.BaseMapper.condition">
<property name="object" value="${condition}.createTime"/>
</include>
</if>
<if test="${condition}.createUser != null">
and m.create_user
<include refid="io.metersphere.system.mapper.BaseMapper.condition">
<property name="object" value="${condition}.createUser"/>
</include>
</if>
<if test="${condition}.enable != null">
and m.enable
<include refid="io.metersphere.system.mapper.BaseMapper.condition">
<property name="object" value="${condition}.enable"/>
</include>
</if>
<if test="${condition}.apiPath != null">
and m.api_path
<include refid="io.metersphere.system.mapper.BaseMapper.condition">
<property name="object" value="${condition}.apiPath"/>
</include>
</if>
<if test="${condition}.apiMethod != null">
and m.api_method
<include refid="io.metersphere.system.mapper.BaseMapper.condition">
<property name="object" value="${condition}.apiMethod"/>
</include>
</if>
<if test='${condition}.tags != null and ${ObjectTags}.operator == "not like"'>
and (m.tags is null or m.tags
<include refid="io.metersphere.system.mapper.BaseMapper.condition">
<property name="object" value="${condition}.tags"/>
</include>
)
</if>
<if test='${condition}.tags != null and ${ObjectTags}.operator == "like"'>
and m.tags
<include refid="io.metersphere.system.mapper.BaseMapper.condition">
<property name="object" value="${condition}.tags"/>
</include>
</if>
</sql>
</mapper>

View File

@ -0,0 +1,159 @@
package io.metersphere.api.service.definition;
import io.metersphere.api.domain.ApiDefinitionMock;
import io.metersphere.api.dto.definition.ApiDefinitionMockDTO;
import io.metersphere.api.dto.definition.request.ApiDefinitionMockAddRequest;
import io.metersphere.api.dto.definition.request.ApiDefinitionMockRequest;
import io.metersphere.api.dto.definition.request.ApiDefinitionMockUpdateRequest;
import io.metersphere.api.mapper.ApiDefinitionMockMapper;
import io.metersphere.sdk.constants.HttpMethodConstants;
import io.metersphere.sdk.util.BeanUtils;
import io.metersphere.sdk.util.JSON;
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.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
* @author: LAN
* @date: 2023/12/7 17:29
* @version: 1.0
*/
@Service
@Transactional(rollbackFor = Exception.class)
public class ApiDefinitionMockLogService {
@Resource
private ApiDefinitionMockMapper apiDefinitionMockMapper;
@Resource
private ApiDefinitionMockService apiDefinitionMockService;
/**
* 添加日志
*
* @param request
* @return
*/
public LogDTO addLog(ApiDefinitionMockAddRequest request) {
LogDTO dto = new LogDTO(
request.getProjectId(),
null,
null,
null,
OperationLogType.ADD.name(),
OperationLogModule.API_DEFINITION_MOCK,
request.getName());
dto.setHistory(true);
dto.setPath("/api/definition/mock/add");
dto.setMethod(HttpMethodConstants.POST.name());
dto.setOriginalValue(JSON.toJSONBytes(request));
return dto;
}
/**
* 修改日志
*
* @param request
* @return
*/
public LogDTO updateLog(ApiDefinitionMockUpdateRequest request) {
ApiDefinitionMockDTO apiDefinitionMock = getOriginalValue(request.getId());
if(apiDefinitionMock.getId() != null){
LogDTO dto = new LogDTO(
request.getProjectId(),
null,
request.getId(),
null,
OperationLogType.UPDATE.name(),
OperationLogModule.API_DEFINITION_MOCK,
request.getName());
dto.setHistory(true);
dto.setPath("/api/definition/mock/update");
dto.setMethod(HttpMethodConstants.POST.name());
dto.setOriginalValue(JSON.toJSONBytes(apiDefinitionMock));
return dto;
}
return null;
}
public LogDTO updateEnableLog(String id) {
ApiDefinitionMockDTO apiDefinitionMock = getOriginalValue(id);
if(apiDefinitionMock.getId() != null){
LogDTO dto = new LogDTO(
apiDefinitionMock.getProjectId(),
null,
apiDefinitionMock.getId(),
null,
OperationLogType.UPDATE.name(),
OperationLogModule.API_DEFINITION_MOCK,
apiDefinitionMock.getName());
dto.setHistory(true);
dto.setPath("/api/definition/mock/enable/" + id);
dto.setMethod(HttpMethodConstants.GET.name());
dto.setOriginalValue(JSON.toJSONBytes(apiDefinitionMock));
return dto;
}
return null;
}
/**
* 删除日志
*
* @param request
* @return
*/
public LogDTO delLog(ApiDefinitionMockRequest request) {
ApiDefinitionMockDTO apiDefinitionMock = getOriginalValue(request.getId());
if(apiDefinitionMock.getId() != null){
LogDTO dto = new LogDTO(
request.getProjectId(),
null,
request.getId(),
null,
OperationLogType.DELETE.name(),
OperationLogModule.API_DEFINITION_MOCK,
apiDefinitionMock.getName());
dto.setHistory(true);
dto.setPath("/api/definition/mock/delete");
dto.setMethod(HttpMethodConstants.POST.name());
dto.setOriginalValue(JSON.toJSONBytes(apiDefinitionMock));
return dto;
}
return null;
}
public LogDTO copyLog(ApiDefinitionMockRequest request) {
ApiDefinitionMockDTO apiDefinitionMock = getOriginalValue(request.getId());
if(apiDefinitionMock.getId() != null){
LogDTO dto = new LogDTO(
apiDefinitionMock.getProjectId(),
null,
null,
null,
OperationLogType.UPDATE.name(),
OperationLogModule.API_DEFINITION_MOCK,
apiDefinitionMock.getName());
dto.setHistory(true);
dto.setPath("/api/definition/mock/copy");
dto.setMethod(HttpMethodConstants.POST.name());
dto.setOriginalValue(JSON.toJSONBytes(apiDefinitionMock));
return dto;
}
return null;
}
private ApiDefinitionMockDTO getOriginalValue(String id){
ApiDefinitionMockDTO apiDefinitionMockDTO = new ApiDefinitionMockDTO();
ApiDefinitionMock apiDefinitionMock = apiDefinitionMockMapper.selectByPrimaryKey(id);
if(null != apiDefinitionMock){
apiDefinitionMockService.handleMockConfig(id, apiDefinitionMockDTO);
BeanUtils.copyBean(apiDefinitionMockDTO, apiDefinitionMock);
}
return apiDefinitionMockDTO;
}
}

View File

@ -0,0 +1,254 @@
package io.metersphere.api.service.definition;
import io.metersphere.api.constants.ApiResourceType;
import io.metersphere.api.controller.result.ApiResultCode;
import io.metersphere.api.domain.ApiDefinition;
import io.metersphere.api.domain.ApiDefinitionMock;
import io.metersphere.api.domain.ApiDefinitionMockConfig;
import io.metersphere.api.domain.ApiDefinitionMockExample;
import io.metersphere.api.dto.debug.ApiFileResourceUpdateRequest;
import io.metersphere.api.dto.definition.ApiDefinitionMockDTO;
import io.metersphere.api.dto.definition.HttpResponse;
import io.metersphere.api.dto.definition.request.ApiDefinitionMockAddRequest;
import io.metersphere.api.dto.definition.request.ApiDefinitionMockPageRequest;
import io.metersphere.api.dto.definition.request.ApiDefinitionMockRequest;
import io.metersphere.api.dto.definition.request.ApiDefinitionMockUpdateRequest;
import io.metersphere.api.mapper.ApiDefinitionMapper;
import io.metersphere.api.mapper.ApiDefinitionMockConfigMapper;
import io.metersphere.api.mapper.ApiDefinitionMockMapper;
import io.metersphere.api.mapper.ExtApiDefinitionMockMapper;
import io.metersphere.api.service.ApiFileResourceService;
import io.metersphere.api.utils.ApiDataUtils;
import io.metersphere.plugin.api.spi.AbstractMsTestElement;
import io.metersphere.project.service.ProjectService;
import io.metersphere.sdk.constants.ApplicationNumScope;
import io.metersphere.sdk.constants.DefaultRepositoryDir;
import io.metersphere.sdk.exception.MSException;
import io.metersphere.sdk.util.BeanUtils;
import io.metersphere.sdk.util.FileAssociationSourceUtil;
import io.metersphere.sdk.util.JSON;
import io.metersphere.system.log.constants.OperationLogModule;
import io.metersphere.system.uid.IDGenerator;
import io.metersphere.system.uid.NumGenerator;
import io.metersphere.system.utils.ServiceUtils;
import jakarta.annotation.Resource;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import java.util.List;
import java.util.Optional;
/**
* @author: LAN
* @date: 2023/12/7 17:28
* @version: 1.0
*/
@Service
@Transactional(rollbackFor = Exception.class)
public class ApiDefinitionMockService {
@Resource
private ApiDefinitionMockMapper apiDefinitionMockMapper;
@Resource
private ApiDefinitionMapper apiDefinitionMapper;
@Resource
private ExtApiDefinitionMockMapper extApiDefinitionMockMapper;
@Resource
private ApiDefinitionMockConfigMapper apiDefinitionMockConfigMapper;
@Resource
private ApiFileResourceService apiFileResourceService;
public List<ApiDefinitionMockDTO> getPage(ApiDefinitionMockPageRequest request) {
return extApiDefinitionMockMapper.list(request);
}
public ApiDefinitionMockDTO detail(ApiDefinitionMockRequest request) {
ApiDefinitionMock apiDefinitionMock = checkApiDefinitionMock(request.getId());
ApiDefinitionMockDTO apiDefinitionMockDTO = new ApiDefinitionMockDTO();
handleMockConfig(request.getId(), apiDefinitionMockDTO);
handleApiDefinition(request.getApiDefinitionId(), apiDefinitionMockDTO);
BeanUtils.copyBean(apiDefinitionMockDTO, apiDefinitionMock);
return apiDefinitionMockDTO;
}
public void handleMockConfig(String id, ApiDefinitionMockDTO apiDefinitionMockDTO) {
Optional<ApiDefinitionMockConfig> apiDefinitionMockConfigOptional = Optional.ofNullable(apiDefinitionMockConfigMapper.selectByPrimaryKey(id));
apiDefinitionMockConfigOptional.ifPresent(config -> {
apiDefinitionMockDTO.setMatching(ApiDataUtils.parseObject(new String(config.getMatching()), AbstractMsTestElement.class));
apiDefinitionMockDTO.setResponse(ApiDataUtils.parseArray(new String(config.getResponse()), HttpResponse.class));
});
}
public void handleApiDefinition(String id, ApiDefinitionMockDTO apiDefinitionMockDTO) {
Optional.ofNullable(apiDefinitionMapper.selectByPrimaryKey(id)).ifPresent(apiDefinition -> {
apiDefinitionMockDTO.setApiNum(apiDefinition.getNum());
apiDefinitionMockDTO.setApiName(apiDefinition.getName());
apiDefinitionMockDTO.setApiPath(apiDefinition.getPath());
apiDefinitionMockDTO.setApiMethod(apiDefinition.getMethod());
});
}
/**
* 校验Mock是否存在
*
* @param id mock id
*/
private ApiDefinitionMock checkApiDefinitionMock(String id) {
return ServiceUtils.checkResourceExist(apiDefinitionMockMapper.selectByPrimaryKey(id),"permission.api_mock.name");
}
public ApiDefinitionMock create(ApiDefinitionMockAddRequest request, String userId) {
ProjectService.checkResourceExist(request.getProjectId());
ApiDefinitionMock apiDefinitionMock = new ApiDefinitionMock();
BeanUtils.copyBean(apiDefinitionMock, request);
checkAddExist(apiDefinitionMock);
apiDefinitionMock.setId(IDGenerator.nextStr());
apiDefinitionMock.setCreateTime(System.currentTimeMillis());
apiDefinitionMock.setUpdateTime(System.currentTimeMillis());
apiDefinitionMock.setCreateUser(userId);
if (CollectionUtils.isNotEmpty(request.getTags())) {
apiDefinitionMock.setTags(JSON.toJSONString(request.getTags()));
}
apiDefinitionMock.setEnable(true);
ApiDefinition apiDefinition = apiDefinitionMapper.selectByPrimaryKey(apiDefinitionMock.getApiDefinitionId());
apiDefinitionMock.setExpectNum(String.valueOf(NumGenerator.nextNum(request.getProjectId() + "_" + apiDefinition.getNum(), ApplicationNumScope.API_TEST_CASE)));
apiDefinitionMockMapper.insertSelective(apiDefinitionMock);
ApiDefinitionMockConfig apiDefinitionMockConfig = new ApiDefinitionMockConfig();
apiDefinitionMockConfig.setId(apiDefinitionMock.getId());
apiDefinitionMockConfig.setMatching(request.getMatching().getBytes());
apiDefinitionMockConfig.setResponse(request.getResponse().getBytes());
apiDefinitionMockConfigMapper.insertSelective(apiDefinitionMockConfig);
// 处理文件
ApiFileResourceUpdateRequest resourceUpdateRequest = getApiFileResourceRequest(apiDefinitionMock.getId(), apiDefinitionMock.getProjectId(), userId);
resourceUpdateRequest.setUploadFileIds(request.getUploadFileIds());
resourceUpdateRequest.setLinkFileIds(request.getLinkFileIds());
apiFileResourceService.addFileResource(resourceUpdateRequest);
return apiDefinitionMock;
}
private static ApiFileResourceUpdateRequest getApiFileResourceRequest(String sourceId, String projectId, String operator) {
String apiDefinitionMockDir = DefaultRepositoryDir.getApiDefinitionDir(projectId, sourceId);
ApiFileResourceUpdateRequest resourceUpdateRequest = new ApiFileResourceUpdateRequest();
resourceUpdateRequest.setProjectId(projectId);
resourceUpdateRequest.setFolder(apiDefinitionMockDir);
resourceUpdateRequest.setResourceId(sourceId);
resourceUpdateRequest.setApiResourceType(ApiResourceType.API_MOCK);
resourceUpdateRequest.setOperator(operator);
resourceUpdateRequest.setLogModule(OperationLogModule.API_DEFINITION_MOCK);
resourceUpdateRequest.setFileAssociationSourceType(FileAssociationSourceUtil.SOURCE_TYPE_API_DEFINITION_MOCK);
return resourceUpdateRequest;
}
private void checkAddExist(ApiDefinitionMock apiDefinitionMock) {
ApiDefinitionMockExample example = new ApiDefinitionMockExample();
example.createCriteria()
.andNameEqualTo(apiDefinitionMock.getName()).andApiDefinitionIdEqualTo(apiDefinitionMock.getApiDefinitionId());
if (apiDefinitionMockMapper.countByExample(example) > 0) {
throw new MSException(ApiResultCode.API_DEFINITION_MOCK_EXIST);
}
}
private void checkUpdateExist(ApiDefinitionMock apiDefinitionMock) {
ApiDefinitionMockExample example = new ApiDefinitionMockExample();
example.createCriteria()
.andIdNotEqualTo(apiDefinitionMock.getId())
.andNameEqualTo(apiDefinitionMock.getName()).andApiDefinitionIdEqualTo(apiDefinitionMock.getApiDefinitionId());
if (apiDefinitionMockMapper.countByExample(example) > 0) {
throw new MSException(ApiResultCode.API_DEFINITION_MOCK_EXIST);
}
}
public ApiDefinitionMock update(ApiDefinitionMockUpdateRequest request, String userId) {
ProjectService.checkResourceExist(request.getProjectId());
ApiDefinitionMock apiDefinitionMock = checkApiDefinitionMock(request.getId());
BeanUtils.copyBean(apiDefinitionMock, request);
checkUpdateExist(apiDefinitionMock);
apiDefinitionMock.setUpdateTime(System.currentTimeMillis());
if (CollectionUtils.isNotEmpty(request.getTags())) {
apiDefinitionMock.setTags(JSON.toJSONString(request.getTags()));
}
apiDefinitionMockMapper.updateByPrimaryKeySelective(apiDefinitionMock);
ApiDefinitionMockConfig apiDefinitionMockConfig = new ApiDefinitionMockConfig();
apiDefinitionMockConfig.setId(apiDefinitionMock.getId());
apiDefinitionMockConfig.setMatching(request.getMatching().getBytes());
apiDefinitionMockConfig.setResponse(request.getResponse().getBytes());
apiDefinitionMockConfigMapper.updateByPrimaryKeySelective(apiDefinitionMockConfig);
// 处理文件
ApiFileResourceUpdateRequest resourceUpdateRequest = getApiFileResourceRequest(apiDefinitionMock.getId(), apiDefinitionMock.getProjectId(), userId);
resourceUpdateRequest.setUploadFileIds(request.getUploadFileIds());
resourceUpdateRequest.setLinkFileIds(request.getLinkFileIds());
resourceUpdateRequest.setUnLinkRefIds(request.getUnLinkRefIds());
resourceUpdateRequest.setDeleteFileIds(request.getDeleteFileIds());
apiFileResourceService.updateFileResource(resourceUpdateRequest);
return apiDefinitionMock;
}
public void delete(ApiDefinitionMockRequest request, String userId) {
checkApiDefinitionMock(request.getId());
String apiDefinitionMockDir = DefaultRepositoryDir.getApiDefinitionDir(request.getProjectId(), request.getId());
apiFileResourceService.deleteByResourceId(apiDefinitionMockDir, request.getId(), request.getProjectId(), userId, OperationLogModule.API_DEFINITION);
apiDefinitionMockConfigMapper.deleteByPrimaryKey(request.getId());
apiDefinitionMockMapper.deleteByPrimaryKey(request.getId());
}
public ApiDefinitionMock copy(ApiDefinitionMockRequest request, String userId) {
ApiDefinitionMock apiDefinitionMock = checkApiDefinitionMock(request.getId());
apiDefinitionMock.setId(IDGenerator.nextStr());
apiDefinitionMock.setName(getCopyName(apiDefinitionMock.getName()));
apiDefinitionMock.setCreateTime(System.currentTimeMillis());
apiDefinitionMock.setUpdateTime(System.currentTimeMillis());
apiDefinitionMock.setCreateUser(userId);
apiDefinitionMock.setEnable(true);
ApiDefinition apiDefinition = apiDefinitionMapper.selectByPrimaryKey(apiDefinitionMock.getApiDefinitionId());
apiDefinitionMock.setExpectNum(String.valueOf(NumGenerator.nextNum(request.getProjectId() + "_" + apiDefinition.getNum(), ApplicationNumScope.API_TEST_CASE)));
apiDefinitionMockMapper.insertSelective(apiDefinitionMock);
Optional<ApiDefinitionMockConfig> apiDefinitionMockConfigOptional = Optional.ofNullable(apiDefinitionMockConfigMapper.selectByPrimaryKey(request.getId()));
apiDefinitionMockConfigOptional.ifPresent(config -> {
ApiDefinitionMockConfig apiDefinitionMockConfig = new ApiDefinitionMockConfig();
apiDefinitionMockConfig.setId(apiDefinitionMock.getId());
apiDefinitionMockConfig.setMatching(config.getMatching());
apiDefinitionMockConfig.setResponse(config.getResponse());
apiDefinitionMockConfigMapper.insertSelective(apiDefinitionMockConfig);
});
String sourceDir = DefaultRepositoryDir.getApiDefinitionDir(apiDefinitionMock.getProjectId(), request.getId());
String targetDir = DefaultRepositoryDir.getApiDefinitionDir(apiDefinitionMock.getProjectId(), apiDefinitionMock.getId());
apiFileResourceService.copyFileByResourceId(request.getId(), sourceDir, apiDefinitionMock.getId(), targetDir);
return apiDefinitionMock;
}
private String getCopyName(String name) {
String copyName = "copy_" + name ;
if (copyName.length() > 200) {
copyName = copyName.substring(0, 195) + copyName.substring(copyName.length() - 5);
}
return copyName;
}
public void updateEnable(String id) {
ApiDefinitionMock apiDefinitionMock = checkApiDefinitionMock(id);
ApiDefinitionMock update = new ApiDefinitionMock();
update.setId(id);
update.setEnable(!apiDefinitionMock.getEnable());
update.setUpdateTime(System.currentTimeMillis());
apiDefinitionMockMapper.updateByPrimaryKeySelective(update);
}
public String uploadTempFile(MultipartFile file) {
return apiFileResourceService.uploadTempFile(file);
}
}

View File

@ -75,6 +75,27 @@
"id": "PROJECT_API_DEFINITION_CASE:READ+EXECUTE"
}
]
},
{
"id": "PROJECT_API_DEFINITION_MOCK",
"name": "permission.api_mock.name",
"permissions": [
{
"id": "PROJECT_API_DEFINITION_MOCK:READ"
},
{
"id": "PROJECT_API_DEFINITION_MOCK:READ+ADD"
},
{
"id": "PROJECT_API_DEFINITION_MOCK:READ+UPDATE"
},
{
"id": "PROJECT_API_DEFINITION_MOCK:READ+DELETE"
},
{
"id": "PROJECT_API_DEFINITION_MOCK:READ+EXECUTE"
}
]
}
]
}

View File

@ -0,0 +1,548 @@
package io.metersphere.api.controller;
import io.metersphere.api.controller.result.ApiResultCode;
import io.metersphere.api.domain.ApiDefinition;
import io.metersphere.api.domain.ApiDefinitionMock;
import io.metersphere.api.domain.ApiDefinitionMockConfig;
import io.metersphere.api.domain.ApiFileResource;
import io.metersphere.api.dto.definition.ApiDefinitionAddRequest;
import io.metersphere.api.dto.definition.ApiDefinitionMockDTO;
import io.metersphere.api.dto.definition.HttpResponse;
import io.metersphere.api.dto.definition.request.ApiDefinitionMockAddRequest;
import io.metersphere.api.dto.definition.request.ApiDefinitionMockPageRequest;
import io.metersphere.api.dto.definition.request.ApiDefinitionMockRequest;
import io.metersphere.api.dto.definition.request.ApiDefinitionMockUpdateRequest;
import io.metersphere.api.dto.request.http.MsHTTPElement;
import io.metersphere.api.mapper.ApiDefinitionMapper;
import io.metersphere.api.mapper.ApiDefinitionMockConfigMapper;
import io.metersphere.api.mapper.ApiDefinitionMockMapper;
import io.metersphere.api.mapper.ExtApiDefinitionMapper;
import io.metersphere.api.service.ApiFileResourceService;
import io.metersphere.api.utils.ApiDataUtils;
import io.metersphere.plugin.api.spi.AbstractMsTestElement;
import io.metersphere.project.dto.filemanagement.FileInfo;
import io.metersphere.project.dto.filemanagement.request.FileUploadRequest;
import io.metersphere.project.service.FileAssociationService;
import io.metersphere.project.service.FileMetadataService;
import io.metersphere.sdk.constants.DefaultRepositoryDir;
import io.metersphere.sdk.constants.PermissionConstants;
import io.metersphere.sdk.file.FileCenter;
import io.metersphere.sdk.file.FileRequest;
import io.metersphere.sdk.util.BeanUtils;
import io.metersphere.sdk.util.CommonBeanFactory;
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.controller.handler.result.MsHttpResultCode;
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;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.test.context.jdbc.Sql;
import org.springframework.test.context.jdbc.SqlConfig;
import org.springframework.test.web.servlet.MvcResult;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
@SpringBootTest(webEnvironment= SpringBootTest.WebEnvironment.RANDOM_PORT)
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
@AutoConfigureMockMvc
public class ApiDefinitionMockControllerTests extends BaseTest {
private static final String BASE_PATH = "/api/definition/mock/";
private final static String ADD = BASE_PATH + "add";
private final static String UPDATE = BASE_PATH + "update";
private final static String DELETE = BASE_PATH + "delete";
private final static String COPY = BASE_PATH + "copy";
private final static String PAGE = BASE_PATH + "page";
private static final String DETAIL = BASE_PATH + "detail";
private static final String ENABLE = BASE_PATH + "enable/";
private static final String UPLOAD_TEMP_FILE = BASE_PATH + "/upload/temp/file";
private static final String DEFAULT_API_ID = "1001";
private static ApiDefinitionMock apiDefinitionMock;
@Resource
private ApiDefinitionMapper apiDefinitionMapper;
@Resource
private ApiDefinitionMockMapper apiDefinitionMockMapper;
@Resource
private ApiDefinitionMockConfigMapper apiDefinitionMockConfigMapper;
@Resource
private ApiFileResourceService apiFileResourceService;
@Resource
private FileMetadataService fileMetadataService;
private static String fileMetadataId;
private static String uploadFileId;
@Test
@Order(0)
public void uploadTempFile() throws Exception {
// 准备数据上传文件管理文件
uploadFileMetadata();
// @@请求成功
MockMultipartFile file = getMockMultipartFile("file_upload.JPG");
String fileId = doUploadTempFile(file);
// 校验文件存在
FileRequest fileRequest = new FileRequest();
fileRequest.setFolder(DefaultRepositoryDir.getSystemTempDir() + "/" + fileId);
fileRequest.setFileName(file.getOriginalFilename());
Assertions.assertNotNull(FileCenter.getDefaultRepository().getFile(fileRequest));
requestUploadPermissionTest(PermissionConstants.PROJECT_API_DEFINITION_MOCK_ADD, UPLOAD_TEMP_FILE, file);
requestUploadPermissionTest(PermissionConstants.PROJECT_API_DEFINITION_MOCK_UPDATE, UPLOAD_TEMP_FILE, file);
}
private String doUploadTempFile(MockMultipartFile file) throws Exception {
return JSON.parseObject(requestUploadFileWithOkAndReturn(UPLOAD_TEMP_FILE, file)
.getResponse()
.getContentAsString(), ResultHolder.class)
.getData().toString();
}
private static MockMultipartFile getMockMultipartFile(String fileName) {
return new MockMultipartFile(
"file",
fileName,
MediaType.APPLICATION_OCTET_STREAM_VALUE,
"Hello, World!".getBytes()
);
}
/**
* 文件管理插入一条数据
* 便于测试关联文件
*/
private void uploadFileMetadata() throws Exception {
FileUploadRequest fileUploadRequest = new FileUploadRequest();
fileUploadRequest.setProjectId(DEFAULT_PROJECT_ID);
//导入正常文件
MockMultipartFile file = new MockMultipartFile("file", "mock_file_upload.JPG", MediaType.APPLICATION_OCTET_STREAM_VALUE, "mock".getBytes());
fileMetadataId = fileMetadataService.upload(fileUploadRequest, "admin", file);
}
/**
* 校验上传的文件
* @param id
* @param fileIds 全部的文件ID
*/
public static void assertUploadFile(String id, List<String> fileIds) throws Exception {
if (fileIds != null) {
ApiFileResourceService apiFileResourceService = CommonBeanFactory.getBean(ApiFileResourceService.class);
// 验证文件的关联关系以及是否存入对象存储
List<ApiFileResource> apiFileResources = apiFileResourceService.getByResourceId(id);
Assertions.assertEquals(apiFileResources.size(), fileIds.size());
String apiDefinitionDir = DefaultRepositoryDir.getApiDefinitionDir(DEFAULT_PROJECT_ID, id);
FileRequest fileRequest = new FileRequest();
if (!fileIds.isEmpty()) {
for (ApiFileResource apiFileResource : apiFileResources) {
Assertions.assertEquals(DEFAULT_PROJECT_ID, apiFileResource.getProjectId());
fileRequest.setFolder(apiDefinitionDir + "/" + apiFileResource.getFileId());
fileRequest.setFileName(apiFileResource.getFileName());
Assertions.assertNotNull(FileCenter.getDefaultRepository().getFile(fileRequest));
}
fileRequest.setFolder(apiDefinitionDir);
} else {
fileRequest.setFolder(apiDefinitionDir);
Assertions.assertTrue(CollectionUtils.isEmpty(FileCenter.getDefaultRepository().getFolderFileNames(fileRequest)));
}
}
}
/**
* 校验上传的文件
* @param id
* @param fileIds 全部的文件ID
*/
private static void assertLinkFile(String id, List<String> fileIds) {
FileAssociationService fileAssociationService = CommonBeanFactory.getBean(FileAssociationService.class);
List<String> linkFileIds = fileAssociationService.getFiles(id)
.stream()
.map(FileInfo::getFileId)
.toList();
Assertions.assertEquals(fileIds, linkFileIds);
}
@Test
@Order(1)
@Sql(scripts = {"/dml/init_api_definition.sql"}, config = @SqlConfig(encoding = "utf-8", transactionMode = SqlConfig.TransactionMode.ISOLATED))
public void testAdd() throws Exception {
LogUtils.info("create api mock test");
// 创建测试数据
ApiDefinitionMockAddRequest request = new ApiDefinitionMockAddRequest();
request.setName("接口定义test");
request.setProjectId(DEFAULT_PROJECT_ID);
request.setApiDefinitionId(DEFAULT_API_ID);
MsHTTPElement msHttpElement = MsHTTPElementTest.getMsHttpElement();
request.setMatching(ApiDataUtils.toJSONString(msHttpElement));
List<HttpResponse> msHttpResponse = MsHTTPElementTest.getMsHttpResponse();
request.setResponse(ApiDataUtils.toJSONString(msHttpResponse));
uploadFileId = doUploadTempFile(getMockMultipartFile("file_upload.JPG"));
request.setUploadFileIds(List.of(uploadFileId));
request.setLinkFileIds(List.of(fileMetadataId));
// 执行方法调用
MvcResult mvcResult = this.requestPostWithOkAndReturn(ADD, request);
// 校验请求成功数据
ApiDefinitionMock resultData = getResultData(mvcResult, ApiDefinitionMock.class);
apiDefinitionMock = assertAddApiDefinitionMock(request, msHttpElement, resultData.getId());
assertUploadFile(apiDefinitionMock.getId(), List.of(uploadFileId));
assertLinkFile(apiDefinitionMock.getId(), List.of(fileMetadataId));
// 再插入一条数据便于修改时重名校验
request.setName("重名接口定义test");
request.setTags(new LinkedHashSet<>(List.of("tag1", "tag2")));
request.setUploadFileIds(null);
request.setLinkFileIds(null);
mvcResult = this.requestPostWithOkAndReturn(ADD, request);
resultData = getResultData(mvcResult, ApiDefinitionMock.class);
assertAddApiDefinitionMock(request, msHttpElement, resultData.getId());
// @@重名校验异常
assertErrorCode(this.requestPost(ADD, request), ApiResultCode.API_DEFINITION_MOCK_EXIST);
// 校验项目是否存在
request.setProjectId("111");
request.setName("test123");
assertErrorCode(this.requestPost(ADD, request), MsHttpResultCode.NOT_FOUND);
// @@校验日志
this.checkLog(apiDefinitionMock.getId(), OperationLogType.ADD, ADD);
// @@异常参数校验
createdGroupParamValidateTest(ApiDefinitionAddRequest.class, ADD);
// @@校验权限
request.setProjectId(DEFAULT_PROJECT_ID);
request.setName("permission-st-6");
requestPostPermissionTest(PermissionConstants.PROJECT_API_DEFINITION_MOCK_ADD, ADD, request);
}
private ApiDefinitionMockAddRequest createApiDefinitionMockAddRequest() {
ApiDefinitionMockAddRequest request = new ApiDefinitionMockAddRequest();
request.setName("接口定义test");
request.setProjectId(DEFAULT_PROJECT_ID);
request.setTags(new LinkedHashSet<>(List.of("tag1", "tag2")));
request.setApiDefinitionId(DEFAULT_API_ID);
return request;
}
private ApiDefinitionMock assertAddApiDefinitionMock(Object request, MsHTTPElement msHttpElement, String id) {
ApiDefinitionMock apiDefinitionMock = apiDefinitionMockMapper.selectByPrimaryKey(id);
ApiDefinitionMockConfig apiDefinitionMockConfig = apiDefinitionMockConfigMapper.selectByPrimaryKey(id);
ApiDefinitionMock copyApiDefinitionMock = BeanUtils.copyBean(new ApiDefinitionMock(), apiDefinitionMock);
BeanUtils.copyBean(copyApiDefinitionMock, request);
Assertions.assertEquals(apiDefinitionMock, copyApiDefinitionMock);
ApiDataUtils.setResolver(MsHTTPElement.class);
if(apiDefinitionMockConfig != null){
Assertions.assertEquals(msHttpElement, ApiDataUtils.parseObject(new String(apiDefinitionMockConfig.getMatching()), AbstractMsTestElement.class));
}
return apiDefinitionMock;
}
@Test
@Order(2)
public void get() throws Exception {
ApiDefinitionMockRequest apiDefinitionMockRequest = new ApiDefinitionMockRequest();
apiDefinitionMockRequest.setId(apiDefinitionMock.getId());
apiDefinitionMockRequest.setProjectId(DEFAULT_PROJECT_ID);
apiDefinitionMockRequest.setApiDefinitionId(apiDefinitionMock.getApiDefinitionId());
// @@请求成功
MvcResult mvcResult = this.requestPostWithOkAndReturn(DETAIL, apiDefinitionMockRequest);
ApiDataUtils.setResolver(MsHTTPElement.class);
ApiDefinitionMockDTO apiDefinitionMockDTO = ApiDataUtils.parseObject(JSON.toJSONString(parseResponse(mvcResult).get("data")), ApiDefinitionMockDTO.class);
// 校验数据是否正确
ApiDefinitionMockDTO copyApiDefinitionMockDTO = BeanUtils.copyBean(new ApiDefinitionMockDTO(), apiDefinitionMock);
ApiDefinition apiDefinition = apiDefinitionMapper.selectByPrimaryKey(apiDefinitionMock.getApiDefinitionId());
copyApiDefinitionMockDTO.setApiNum(apiDefinition.getNum());
copyApiDefinitionMockDTO.setApiName(apiDefinition.getName());
copyApiDefinitionMockDTO.setApiPath(apiDefinition.getPath());
copyApiDefinitionMockDTO.setApiMethod(apiDefinition.getMethod());
ApiDefinitionMockConfig apiDefinitionMockConfig = apiDefinitionMockConfigMapper.selectByPrimaryKey(apiDefinitionMock.getId());
if(apiDefinitionMockConfig != null){
copyApiDefinitionMockDTO.setMatching(ApiDataUtils.parseObject(new String(apiDefinitionMockConfig.getMatching()), AbstractMsTestElement.class));
copyApiDefinitionMockDTO.setResponse(ApiDataUtils.parseArray(new String(apiDefinitionMockConfig.getResponse()), HttpResponse.class));
}
Assertions.assertEquals(apiDefinitionMockDTO, copyApiDefinitionMockDTO);
apiDefinitionMockRequest.setId("111");
assertErrorCode(this.requestPost(DETAIL, apiDefinitionMockRequest), MsHttpResultCode.NOT_FOUND);
// @@校验权限
requestPostPermissionTest(PermissionConstants.PROJECT_API_DEFINITION_MOCK_READ, DETAIL, apiDefinitionMockRequest);
}
@Test
@Order(3)
public void testUpdate() throws Exception {
LogUtils.info("update api mock test");
ApiDefinitionMockUpdateRequest request = new ApiDefinitionMockUpdateRequest();
BeanUtils.copyBean(request, apiDefinitionMock);
request.setName("test1test1test1test1test1test1");
MsHTTPElement msHttpElement = MsHTTPElementTest.getMsHttpElement();
request.setMatching(ApiDataUtils.toJSONString(msHttpElement));
List<HttpResponse> msHttpResponse = MsHTTPElementTest.getMsHttpResponse();
request.setResponse(ApiDataUtils.toJSONString(msHttpResponse));
// 清除文件的更新
request.setUnLinkRefIds(List.of(fileMetadataId));
request.setDeleteFileIds(List.of(uploadFileId));
this.requestPostWithOk(UPDATE, request);
// 校验请求成功数据
apiDefinitionMock = assertAddApiDefinitionMock(request, msHttpElement, request.getId());
assertUploadFile(apiDefinitionMock.getId(), List.of());
assertLinkFile(apiDefinitionMock.getId(), List.of());
// 带文件的更新
String fileId = doUploadTempFile(getMockMultipartFile("file_upload.JPG"));
request.setUploadFileIds(List.of(fileId));
request.setLinkFileIds(List.of(fileMetadataId));
request.setDeleteFileIds(null);
request.setUnLinkRefIds(null);
this.requestPostWithOk(UPDATE, request);
// 校验请求成功数据
apiDefinitionMock = assertAddApiDefinitionMock(request, msHttpElement, request.getId());
assertUploadFile(apiDefinitionMock.getId(), List.of(fileId));
assertLinkFile(apiDefinitionMock.getId(), List.of(fileMetadataId));
// 删除了上一次上传的文件重新上传一个文件
request.setDeleteFileIds(List.of(fileId));
String newFileId1 = doUploadTempFile(getMockMultipartFile("file_upload.JPG"));
request.setUploadFileIds(List.of(newFileId1));
request.setUnLinkRefIds(List.of(fileMetadataId));
request.setLinkFileIds(List.of(fileMetadataId));
this.requestPostWithOk(UPDATE, request);
apiDefinitionMock = assertAddApiDefinitionMock(request, msHttpElement, request.getId());
assertUploadFile(apiDefinitionMock.getId(), List.of(newFileId1));
assertLinkFile(apiDefinitionMock.getId(), List.of(fileMetadataId));
// 已有一个文件再上传一个文件
String newFileId2 = doUploadTempFile(getMockMultipartFile("file_update_upload.JPG"));
request.setUploadFileIds(List.of(newFileId2));
request.setUnLinkRefIds(null);
request.setDeleteFileIds(null);
request.setLinkFileIds(null);
this.requestPostWithOk(UPDATE, request);
apiDefinitionMock = assertAddApiDefinitionMock(request, msHttpElement, request.getId());
assertUploadFile(apiDefinitionMock.getId(), List.of(newFileId1, newFileId2));
assertLinkFile(apiDefinitionMock.getId(), List.of(fileMetadataId));
// 修改 tags
request.setUploadFileIds(null);
request.setUnLinkRefIds(null);
request.setDeleteFileIds(null);
request.setLinkFileIds(null);
request.setTags(new LinkedHashSet<>(List.of("tag1", "tag2-update")));
request.setName("接口定义test接口定义test接口定义test接口定义test接口定义test接口定义test接口定义test接口定义test接口定义test接口定义test接口定义test接口定义test接口定义test接口定义test接口定义test接口定义test接口定义test接口定义test接口定义test接口定义test接口定义test接口定义test接口定义test接口定义test接口定义test");
MsHTTPElement msHttpElementTag = MsHTTPElementTest.getMsHttpElement();
request.setMatching(ApiDataUtils.toJSONString(msHttpElementTag));
List<HttpResponse> msHttpResponseTag = MsHTTPElementTest.getMsHttpResponse();
request.setResponse(ApiDataUtils.toJSONString(msHttpResponseTag));
this.requestPostWithOk(UPDATE, request);
// 校验请求成功数据
assertAddApiDefinitionMock(request, msHttpElement, request.getId());
request.setName("重名接口定义test");
// @@重名校验异常
assertErrorCode(this.requestPost(UPDATE, request), ApiResultCode.API_DEFINITION_MOCK_EXIST);
// 校验项目是否存在
request.setProjectId("111");
request.setName("test123");
assertErrorCode(this.requestPost(UPDATE, request), MsHttpResultCode.NOT_FOUND);
// 校验数据是否存在
request.setId("111");
request.setName("test123");
assertErrorCode(this.requestPost(UPDATE, request), MsHttpResultCode.NOT_FOUND);
// @@校验日志
checkLog(apiDefinitionMock.getId(), OperationLogType.UPDATE, UPDATE);
// @@异常参数校验
createdGroupParamValidateTest(ApiDefinitionMockUpdateRequest.class, UPDATE);
// @@校验权限
request.setProjectId(DEFAULT_PROJECT_ID);
request.setName("permission-st-6");
requestPostPermissionTest(PermissionConstants.PROJECT_API_DEFINITION_MOCK_UPDATE, UPDATE, request);
}
@Test
@Order(4)
public void testUpdateEnable() throws Exception {
LogUtils.info("update enable api mock test");
// @@关闭
// @@请求成功
this.requestGetWithOk(ENABLE + apiDefinitionMock.getId());
ApiDefinitionMock mock = apiDefinitionMockMapper.selectByPrimaryKey(apiDefinitionMock.getId());
Assertions.assertEquals(apiDefinitionMock.getEnable(),!mock.getEnable());
// @@校验日志
checkLog(apiDefinitionMock.getId(), OperationLogType.UPDATE, ENABLE + apiDefinitionMock.getId());
assertErrorCode(this.requestGet(ENABLE + "111"), MsHttpResultCode.NOT_FOUND);
// @@开启
// @@请求成功
this.requestGetWithOk(ENABLE + apiDefinitionMock.getId());
ApiDefinitionMock mockEnable = apiDefinitionMockMapper.selectByPrimaryKey(apiDefinitionMock.getId());
Assertions.assertEquals(mock.getEnable(),!mockEnable.getEnable());
// @@校验日志
checkLog(apiDefinitionMock.getId(), OperationLogType.UPDATE, ENABLE + apiDefinitionMock.getId());
assertErrorCode(this.requestGet(ENABLE + "111"), MsHttpResultCode.NOT_FOUND);
// @@校验权限
requestGetPermissionTest(PermissionConstants.PROJECT_API_DEFINITION_MOCK_UPDATE, ENABLE + apiDefinitionMock.getId());
}
@Test
@Order(5)
public void copy() throws Exception {
LogUtils.info("copy api test");
ApiDefinitionMockRequest request = new ApiDefinitionMockRequest();
request.setId(apiDefinitionMock.getId());
request.setProjectId(DEFAULT_PROJECT_ID);
request.setApiDefinitionId(apiDefinitionMock.getApiDefinitionId());
MvcResult mvcResult = this.requestPostWithOkAndReturn(COPY, request);
ApiDefinitionMock resultData = getResultData(mvcResult, ApiDefinitionMock.class);
// @数据验证
List<ApiFileResource> sourceFiles = apiFileResourceService.getByResourceId(apiDefinitionMock.getId());
List<ApiFileResource> copyFiles = apiFileResourceService.getByResourceId(resultData.getId());
if(!sourceFiles.isEmpty() && !copyFiles.isEmpty()){
Assertions.assertEquals(sourceFiles.size(), copyFiles.size());
}
Assertions.assertTrue(resultData.getName().contains("copy_"));
ApiDefinitionMockUpdateRequest apiDefinitionMockUpdateRequest = new ApiDefinitionMockUpdateRequest();
BeanUtils.copyBean(apiDefinitionMockUpdateRequest, apiDefinitionMock);
apiDefinitionMockUpdateRequest.setName("test1test1test1test1test1test1");
MsHTTPElement msHttpElement = MsHTTPElementTest.getMsHttpElement();
apiDefinitionMockUpdateRequest.setMatching(ApiDataUtils.toJSONString(msHttpElement));
List<HttpResponse> msHttpResponse = MsHTTPElementTest.getMsHttpResponse();
apiDefinitionMockUpdateRequest.setResponse(ApiDataUtils.toJSONString(msHttpResponse));
this.requestPostWithOk(UPDATE, apiDefinitionMockUpdateRequest);
// 校验请求成功数据
apiDefinitionMock = assertAddApiDefinitionMock(request, msHttpElement, request.getId());
request.setId(apiDefinitionMock.getId());
MvcResult mvcResultCopy = this.requestPostWithOkAndReturn(COPY, request);
ApiDefinitionMock resultDataCopy = getResultData(mvcResultCopy, ApiDefinitionMock.class);
// @数据验证
Assertions.assertTrue(resultDataCopy.getName().contains("copy_"));
// @@校验日志
checkLog(resultData.getId(), OperationLogType.UPDATE);
request.setId("121");
assertErrorCode(this.requestPost(COPY, request), MsHttpResultCode.NOT_FOUND);
// @@校验权限
requestPostPermissionTest(PermissionConstants.PROJECT_API_DEFINITION_MOCK_UPDATE, COPY, request);
}
@Test
@Order(9)
public void getPage() throws Exception {
doApiDefinitionPage("KEYWORD");
doApiDefinitionPage("FILTER");
}
private void doApiDefinitionPage(String search) throws Exception {
ApiDefinitionMockPageRequest request = new ApiDefinitionMockPageRequest();
request.setProjectId(DEFAULT_PROJECT_ID);
request.setApiDefinitionId(apiDefinitionMock.getApiDefinitionId());
request.setCurrent(1);
request.setPageSize(10);
request.setSort(Map.of("createTime", "asc"));
// "ALL", "KEYWORD", "FILTER", "COMBINE", "DELETED"
switch (search) {
case "KEYWORD" -> configureKeywordSearch(request);
case "FILTER" -> configureFilterSearch(request);
default -> {}
}
MvcResult mvcResult = this.requestPostWithOkAndReturn(PAGE, request);
// 获取返回值
String returnData = mvcResult.getResponse().getContentAsString(StandardCharsets.UTF_8);
ResultHolder resultHolder = JSON.parseObject(returnData, ResultHolder.class);
// 返回请求正常
Assertions.assertNotNull(resultHolder);
Pager<?> pageData = JSON.parseObject(JSON.toJSONString(resultHolder.getData()), Pager.class);
// 返回值不为空
Assertions.assertNotNull(pageData);
// 返回值的页码和当前页码相同
Assertions.assertEquals(pageData.getCurrent(), request.getCurrent());
// 返回的数据量不超过规定要返回的数据量相同
Assertions.assertTrue(JSON.parseArray(JSON.toJSONString(pageData.getList())).size() <= request.getPageSize());
}
private void configureKeywordSearch(ApiDefinitionMockPageRequest request) {
request.setKeyword("100");
request.setSort(Map.of("enable", "asc"));
}
private void configureFilterSearch(ApiDefinitionMockPageRequest request) {
Map<String, List<String>> filters = new HashMap<>();
request.setSort(Map.of("updateTime", "asc"));
filters.put("enable", List.of("1"));
filters.put("tags", List.of("tag1"));
request.setFilter(filters);
}
@Test
@Order(12)
public void testDel() throws Exception {
LogUtils.info("delete api mock test");
ApiDefinitionMockRequest apiDefinitionMockRequest = new ApiDefinitionMockRequest();
apiDefinitionMockRequest.setId(apiDefinitionMock.getId());
apiDefinitionMockRequest.setProjectId(DEFAULT_PROJECT_ID);
apiDefinitionMockRequest.setApiDefinitionId(apiDefinitionMock.getApiDefinitionId());
// @@请求成功
this.requestPostWithOkAndReturn(DELETE, apiDefinitionMockRequest);
checkLog(apiDefinitionMock.getId(), OperationLogType.DELETE);
ApiDefinitionMock apiDefinitionMockInfo = apiDefinitionMockMapper.selectByPrimaryKey(apiDefinitionMock.getId());
Assertions.assertNull(apiDefinitionMockInfo);
// config是否删除
ApiDefinitionMockConfig apiDefinitionMockConfig = apiDefinitionMockConfigMapper.selectByPrimaryKey(apiDefinitionMock.getId());
Assertions.assertNull(apiDefinitionMockConfig);
// 文件是否删除
List<ApiFileResource> apiFileResources = apiFileResourceService.getByResourceId(apiDefinitionMock.getId());
Assertions.assertEquals(0, apiFileResources.size());
checkLog(apiDefinitionMockRequest.getId(), OperationLogType.DELETE);
apiDefinitionMockRequest.setId("121");
assertErrorCode(this.requestPost(DELETE, apiDefinitionMockRequest), MsHttpResultCode.NOT_FOUND);
// @@校验权限
requestPostPermissionTest(PermissionConstants.PROJECT_API_DEFINITION_MOCK_DELETE, DELETE, apiDefinitionMockRequest);
}
}

View File

@ -19,6 +19,7 @@ public class OperationLogModule {
public static final String API_AUTOMATION_SCHEDULE = "API_AUTOMATION_SCHEDULE";
public static final String API_AUTOMATION_REPORT = "API_AUTOMATION_REPORT";
public static final String API_DEFINITION = "API_DEFINITION";
public static final String API_DEFINITION_MOCK = "API_DEFINITION_MOCK";
public static final String API_DEFINITION_CASE = "API_DEFINITION_CASE";
public static final String TRACK_TEST_PLAN = "TRACK_TEST_PLAN";
public static final String TRACK_TEST_PLAN_SCHEDULE = "TRACK_TEST_PLAN_SCHEDULE";