feat(接口测试): 接口管理接口增删改复制相关接口
This commit is contained in:
parent
4bf12605ad
commit
e70df8a60f
|
@ -294,3 +294,4 @@ permission.api.name=接口测试
|
|||
api_debug_exist=接口已存在
|
||||
follow=关注
|
||||
unfollow=取消关注
|
||||
api_definition_exist=接口已存在
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -301,4 +301,5 @@ api_module.not.exist=模块不存在
|
|||
permission.api.name=接口测试
|
||||
api_debug_exist=接口已存在
|
||||
follow=关注
|
||||
unfollow=取消关注
|
||||
unfollow=取消关注
|
||||
api_definition_exist=接口已存在
|
||||
|
|
|
@ -302,3 +302,4 @@ permission.api.name=接口測試
|
|||
api_debug_exist=接口已存在
|
||||
follow=关注
|
||||
unfollow=取消关注
|
||||
api_definition_exist=接口已存在
|
||||
|
|
|
@ -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 = "接口测试-接口管理-接口列表")
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
|
||||
}
|
|
@ -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;
|
||||
|
||||
}
|
|
@ -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;
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
||||
}
|
|
@ -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;
|
|
@ -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;
|
||||
|
||||
}
|
|
@ -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;
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 |
Loading…
Reference in New Issue