feat(接口测试): 接口管理接口增删改复制代码优化
This commit is contained in:
parent
e70df8a60f
commit
a556d02625
|
@ -63,14 +63,14 @@ public class ApiDefinitionController {
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping(value = "/delete")
|
@PostMapping(value = "/delete")
|
||||||
@Operation(summary = "接口测试-接口管理-删除接口定义")
|
@Operation(summary = "接口测试-接口管理-删除接口定义到回收站")
|
||||||
@RequiresPermissions(PermissionConstants.PROJECT_API_DEFINITION_DELETE)
|
@RequiresPermissions(PermissionConstants.PROJECT_API_DEFINITION_DELETE)
|
||||||
@Log(type = OperationLogType.DELETE, expression = "#msClass.delLog(#request)", msClass = ApiDefinitionLogService.class)
|
@Log(type = OperationLogType.DELETE, expression = "#msClass.delLog(#request)", msClass = ApiDefinitionLogService.class)
|
||||||
public void delete(@Validated @RequestBody ApiDefinitionDeleteRequest request) {
|
public void delete(@Validated @RequestBody ApiDefinitionDeleteRequest request) {
|
||||||
apiDefinitionService.delete(request, SessionUtils.getUserId());
|
apiDefinitionService.delete(request, SessionUtils.getUserId());
|
||||||
}
|
}
|
||||||
@PostMapping(value = "/batch-del")
|
@PostMapping(value = "/batch-del")
|
||||||
@Operation(summary = "接口测试-接口管理-批量删除接口定义")
|
@Operation(summary = "接口测试-接口管理-批量删除接口定义到回收站")
|
||||||
@RequiresPermissions(PermissionConstants.PROJECT_API_DEFINITION_DELETE)
|
@RequiresPermissions(PermissionConstants.PROJECT_API_DEFINITION_DELETE)
|
||||||
@Log(type = OperationLogType.DELETE, expression = "#msClass.batchDelLog(#request)", msClass = ApiDefinitionLogService.class)
|
@Log(type = OperationLogType.DELETE, expression = "#msClass.batchDelLog(#request)", msClass = ApiDefinitionLogService.class)
|
||||||
public void batchDelete(@Validated @RequestBody ApiDefinitionBatchRequest request) {
|
public void batchDelete(@Validated @RequestBody ApiDefinitionBatchRequest request) {
|
||||||
|
|
|
@ -3,13 +3,18 @@ package io.metersphere.api.dto.definition;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author: LAN
|
* @author: LAN
|
||||||
* @date: 2023/11/8 19:17
|
* @date: 2023/11/8 19:17
|
||||||
* @version: 1.0
|
* @version: 1.0
|
||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
public class ApiCaseComputeDTO {
|
public class ApiCaseComputeDTO implements Serializable {
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
@Schema(description = "接口ID")
|
@Schema(description = "接口ID")
|
||||||
private String apiDefinitionId;
|
private String apiDefinitionId;
|
||||||
|
|
|
@ -6,8 +6,9 @@ import jakarta.validation.constraints.NotBlank;
|
||||||
import jakarta.validation.constraints.Size;
|
import jakarta.validation.constraints.Size;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.List;
|
import java.util.LinkedHashSet;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author lan
|
* @author lan
|
||||||
|
@ -15,6 +16,7 @@ import java.util.List;
|
||||||
@Data
|
@Data
|
||||||
public class ApiDefinitionAddRequest implements Serializable {
|
public class ApiDefinitionAddRequest implements Serializable {
|
||||||
|
|
||||||
|
@Serial
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
|
||||||
|
@ -56,7 +58,7 @@ public class ApiDefinitionAddRequest implements Serializable {
|
||||||
private String description;
|
private String description;
|
||||||
|
|
||||||
@Schema(description = "标签")
|
@Schema(description = "标签")
|
||||||
private List<@NotBlank String> tags;
|
private LinkedHashSet<@NotBlank String> tags;
|
||||||
|
|
||||||
@Schema(description = "请求内容")
|
@Schema(description = "请求内容")
|
||||||
@NotBlank
|
@NotBlank
|
||||||
|
|
|
@ -4,6 +4,8 @@ import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author lan
|
* @author lan
|
||||||
*/
|
*/
|
||||||
|
@ -11,6 +13,7 @@ import lombok.EqualsAndHashCode;
|
||||||
@EqualsAndHashCode(callSuper = false)
|
@EqualsAndHashCode(callSuper = false)
|
||||||
public class ApiDefinitionBatchMoveRequest extends ApiDefinitionBatchRequest {
|
public class ApiDefinitionBatchMoveRequest extends ApiDefinitionBatchRequest {
|
||||||
|
|
||||||
|
@Serial
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
@Schema(description = "模块ID", requiredMode = Schema.RequiredMode.REQUIRED)
|
@Schema(description = "模块ID", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package io.metersphere.api.dto.definition;
|
package io.metersphere.api.dto.definition;
|
||||||
|
|
||||||
|
import io.metersphere.sdk.constants.ModuleConstants;
|
||||||
import io.metersphere.system.dto.table.TableBatchProcessDTO;
|
import io.metersphere.system.dto.table.TableBatchProcessDTO;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import jakarta.validation.constraints.NotBlank;
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
@ -7,7 +8,9 @@ import jakarta.validation.constraints.Size;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author lan
|
* @author lan
|
||||||
|
@ -16,6 +19,7 @@ import java.io.Serializable;
|
||||||
@EqualsAndHashCode(callSuper = false)
|
@EqualsAndHashCode(callSuper = false)
|
||||||
public class ApiDefinitionBatchRequest extends TableBatchProcessDTO implements Serializable {
|
public class ApiDefinitionBatchRequest extends TableBatchProcessDTO implements Serializable {
|
||||||
|
|
||||||
|
@Serial
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
@Schema(description = "项目ID", requiredMode = Schema.RequiredMode.REQUIRED)
|
@Schema(description = "项目ID", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
|
@ -23,6 +27,14 @@ public class ApiDefinitionBatchRequest extends TableBatchProcessDTO implements S
|
||||||
@Size(min = 1, max = 50, message = "{api_definition.project_id.length_range}")
|
@Size(min = 1, max = 50, message = "{api_definition.project_id.length_range}")
|
||||||
private String projectId;
|
private String projectId;
|
||||||
|
|
||||||
|
@Schema(description = "接口协议", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
|
@NotBlank(message = "{api_debug.protocol.not_blank}")
|
||||||
|
@Size(min = 1, max = 20, message = "{api_debug.protocol.length_range}")
|
||||||
|
private String protocol = ModuleConstants.NODE_PROTOCOL_HTTP;
|
||||||
|
|
||||||
|
@Schema(description = "模块ID(根据模块树查询时要把当前节点以及子节点都放在这里。)")
|
||||||
|
private List<String> moduleIds;
|
||||||
|
|
||||||
@Schema(description = "删除列表版本/删除全部版本")
|
@Schema(description = "删除列表版本/删除全部版本")
|
||||||
private Boolean deleteAll = false;
|
private Boolean deleteAll = false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ import jakarta.validation.constraints.Size;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.LinkedHashSet;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author lan
|
* @author lan
|
||||||
|
@ -17,6 +17,9 @@ public class ApiDefinitionBatchUpdateRequest extends ApiDefinitionBatchRequest {
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@Schema(description = "所需更新的字段名", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
|
private String type;
|
||||||
|
|
||||||
@Schema(description = "http协议类型post/get/其它协议则是协议名(mqtt)")
|
@Schema(description = "http协议类型post/get/其它协议则是协议名(mqtt)")
|
||||||
private String method;
|
private String method;
|
||||||
|
|
||||||
|
@ -29,7 +32,7 @@ public class ApiDefinitionBatchUpdateRequest extends ApiDefinitionBatchRequest {
|
||||||
private String versionId;
|
private String versionId;
|
||||||
|
|
||||||
@Schema(description = "标签")
|
@Schema(description = "标签")
|
||||||
private List<@NotBlank String> tags;
|
private LinkedHashSet<@NotBlank String> tags;
|
||||||
|
|
||||||
@Schema(description = "是否追加", requiredMode = Schema.RequiredMode.REQUIRED)
|
@Schema(description = "是否追加", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
private boolean append = false;
|
private boolean append = false;
|
||||||
|
|
|
@ -5,6 +5,7 @@ import jakarta.validation.constraints.NotBlank;
|
||||||
import jakarta.validation.constraints.Size;
|
import jakarta.validation.constraints.Size;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -13,6 +14,7 @@ import java.io.Serializable;
|
||||||
@Data
|
@Data
|
||||||
public class ApiDefinitionCopyRequest implements Serializable {
|
public class ApiDefinitionCopyRequest implements Serializable {
|
||||||
|
|
||||||
|
@Serial
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
@Schema(description = "接口pk", requiredMode = Schema.RequiredMode.REQUIRED)
|
@Schema(description = "接口pk", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
|
|
|
@ -5,6 +5,7 @@ import jakarta.validation.constraints.NotBlank;
|
||||||
import jakarta.validation.constraints.Size;
|
import jakarta.validation.constraints.Size;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -13,6 +14,7 @@ import java.io.Serializable;
|
||||||
@Data
|
@Data
|
||||||
public class ApiDefinitionDeleteRequest implements Serializable {
|
public class ApiDefinitionDeleteRequest implements Serializable {
|
||||||
|
|
||||||
|
@Serial
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
@Schema(description = "接口pk", requiredMode = Schema.RequiredMode.REQUIRED)
|
@Schema(description = "接口pk", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
|
|
|
@ -8,6 +8,8 @@ import jakarta.validation.constraints.Size;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author lan
|
* @author lan
|
||||||
*/
|
*/
|
||||||
|
@ -15,8 +17,6 @@ import lombok.EqualsAndHashCode;
|
||||||
@EqualsAndHashCode(callSuper = false)
|
@EqualsAndHashCode(callSuper = false)
|
||||||
public class ApiDefinitionPageRequest extends BasePageRequest {
|
public class ApiDefinitionPageRequest extends BasePageRequest {
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
@Schema(description = "接口pk")
|
@Schema(description = "接口pk")
|
||||||
@Size(min = 1, max = 50, message = "{api_definition.id.length_range}")
|
@Size(min = 1, max = 50, message = "{api_definition.id.length_range}")
|
||||||
private String id;
|
private String id;
|
||||||
|
@ -42,4 +42,7 @@ public class ApiDefinitionPageRequest extends BasePageRequest {
|
||||||
@Schema(description = "版本引用fk")
|
@Schema(description = "版本引用fk")
|
||||||
@Size(min = 1, max = 50, message = "{api_definition.ref_id.length_range}")
|
@Size(min = 1, max = 50, message = "{api_definition.ref_id.length_range}")
|
||||||
private String refId;
|
private String refId;
|
||||||
|
|
||||||
|
@Schema(description = "模块ID(根据模块树查询时要把当前节点以及子节点都放在这里。)")
|
||||||
|
private List<String> moduleIds;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,8 @@ import jakarta.validation.constraints.Size;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author lan
|
* @author lan
|
||||||
*/
|
*/
|
||||||
|
@ -13,6 +15,7 @@ import lombok.EqualsAndHashCode;
|
||||||
@EqualsAndHashCode(callSuper = false)
|
@EqualsAndHashCode(callSuper = false)
|
||||||
public class ApiDefinitionUpdateRequest extends ApiDefinitionAddRequest {
|
public class ApiDefinitionUpdateRequest extends ApiDefinitionAddRequest {
|
||||||
|
|
||||||
|
@Serial
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
@Schema(description = "接口pk", requiredMode = Schema.RequiredMode.REQUIRED)
|
@Schema(description = "接口pk", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
|
|
|
@ -16,7 +16,7 @@ public interface ExtApiDefinitionMapper {
|
||||||
|
|
||||||
Long getPos(@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> getIds(@Param("request") TableBatchProcessDTO request, @Param("projectId") String projectId, @Param("protocol") String protocol, @Param("deleted") boolean deleted);
|
||||||
|
|
||||||
List<String> getRefIds(@Param("ids") List<String> ids);
|
List<String> getRefIds(@Param("ids") List<String> ids);
|
||||||
|
|
||||||
|
@ -29,6 +29,7 @@ public interface ExtApiDefinitionMapper {
|
||||||
void batchDelete(@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 clearLatestVersion(@Param("refId") String refId, @Param("projectId") String projectId);
|
||||||
|
|
||||||
void updateLatestVersion(@Param("id") String id, @Param("projectId") String projectId);
|
void updateLatestVersion(@Param("id") String id, @Param("projectId") String projectId);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,7 +61,7 @@
|
||||||
<select id="getIds" resultType="java.lang.String">
|
<select id="getIds" resultType="java.lang.String">
|
||||||
SELECT id
|
SELECT id
|
||||||
FROM api_definition
|
FROM api_definition
|
||||||
where project_id = #{projectId} and deleted = #{deleted}
|
where project_id = #{projectId} AND protocol = #{protocol} and deleted = #{deleted}
|
||||||
<include refid="queryWhereConditionByBaseQueryRequest"/>
|
<include refid="queryWhereConditionByBaseQueryRequest"/>
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
@ -153,6 +153,12 @@
|
||||||
<if test="request.protocol != null and request.protocol != ''">
|
<if test="request.protocol != null and request.protocol != ''">
|
||||||
AND api_definition.protocol = #{request.protocol}
|
AND api_definition.protocol = #{request.protocol}
|
||||||
</if>
|
</if>
|
||||||
|
<if test="request.moduleIds != null and request.moduleIds.size() > 0">
|
||||||
|
and api_definition.module_id in
|
||||||
|
<foreach collection="request.moduleIds" item="nodeId" separator="," open="(" close=")">
|
||||||
|
#{nodeId}
|
||||||
|
</foreach>
|
||||||
|
</if>
|
||||||
<include refid="filters">
|
<include refid="filters">
|
||||||
<property name="filter" value="request.filter"/>
|
<property name="filter" value="request.filter"/>
|
||||||
</include>
|
</include>
|
||||||
|
|
|
@ -112,7 +112,7 @@ public class ApiDefinitionLogService {
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public List<LogDTO> batchDelLog(ApiDefinitionBatchRequest request) {
|
public List<LogDTO> batchDelLog(ApiDefinitionBatchRequest request) {
|
||||||
List<String> ids = apiDefinitionService.getBatchApiIds(request, request.getProjectId());
|
List<String> ids = apiDefinitionService.getBatchApiIds(request, request.getProjectId(), request.getProtocol());
|
||||||
List<LogDTO> dtoList = new ArrayList<>();
|
List<LogDTO> dtoList = new ArrayList<>();
|
||||||
if (CollectionUtils.isNotEmpty(ids)) {
|
if (CollectionUtils.isNotEmpty(ids)) {
|
||||||
ApiDefinitionExample example = new ApiDefinitionExample();
|
ApiDefinitionExample example = new ApiDefinitionExample();
|
||||||
|
@ -144,7 +144,7 @@ public class ApiDefinitionLogService {
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public List<LogDTO> batchUpdateLog(ApiDefinitionBatchUpdateRequest request) {
|
public List<LogDTO> batchUpdateLog(ApiDefinitionBatchUpdateRequest request) {
|
||||||
List<String> ids = apiDefinitionService.getBatchApiIds(request, request.getProjectId());
|
List<String> ids = apiDefinitionService.getBatchApiIds(request, request.getProjectId(), request.getProtocol());
|
||||||
ApiDefinitionExample example = new ApiDefinitionExample();
|
ApiDefinitionExample example = new ApiDefinitionExample();
|
||||||
example.createCriteria().andIdIn(ids);
|
example.createCriteria().andIdIn(ids);
|
||||||
List<ApiDefinition> apiDefinitions = apiDefinitionMapper.selectByExample(example);
|
List<ApiDefinition> apiDefinitions = apiDefinitionMapper.selectByExample(example);
|
||||||
|
@ -188,7 +188,7 @@ public class ApiDefinitionLogService {
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<LogDTO> batchMoveLog(ApiDefinitionBatchMoveRequest request) {
|
public List<LogDTO> batchMoveLog(ApiDefinitionBatchMoveRequest request) {
|
||||||
List<String> ids = apiDefinitionService.getBatchApiIds(request, request.getProjectId());
|
List<String> ids = apiDefinitionService.getBatchApiIds(request, request.getProjectId(), request.getProtocol());
|
||||||
ApiDefinitionExample example = new ApiDefinitionExample();
|
ApiDefinitionExample example = new ApiDefinitionExample();
|
||||||
example.createCriteria().andIdIn(ids);
|
example.createCriteria().andIdIn(ids);
|
||||||
List<ApiDefinition> apiDefinitions = apiDefinitionMapper.selectByExample(example);
|
List<ApiDefinition> apiDefinitions = apiDefinitionMapper.selectByExample(example);
|
||||||
|
|
|
@ -11,12 +11,14 @@ import io.metersphere.api.mapper.ExtApiDefinitionMapper;
|
||||||
import io.metersphere.api.util.ApiDataUtils;
|
import io.metersphere.api.util.ApiDataUtils;
|
||||||
import io.metersphere.plugin.api.spi.AbstractMsTestElement;
|
import io.metersphere.plugin.api.spi.AbstractMsTestElement;
|
||||||
import io.metersphere.project.mapper.ExtBaseProjectVersionMapper;
|
import io.metersphere.project.mapper.ExtBaseProjectVersionMapper;
|
||||||
import io.metersphere.project.mapper.ProjectMapper;
|
|
||||||
import io.metersphere.project.service.ProjectService;
|
import io.metersphere.project.service.ProjectService;
|
||||||
import io.metersphere.sdk.constants.ApplicationNumScope;
|
import io.metersphere.sdk.constants.ApplicationNumScope;
|
||||||
import io.metersphere.sdk.constants.StorageType;
|
import io.metersphere.sdk.constants.StorageType;
|
||||||
import io.metersphere.sdk.exception.MSException;
|
import io.metersphere.sdk.exception.MSException;
|
||||||
import io.metersphere.sdk.util.*;
|
import io.metersphere.sdk.util.BeanUtils;
|
||||||
|
import io.metersphere.sdk.util.JSON;
|
||||||
|
import io.metersphere.sdk.util.LogUtils;
|
||||||
|
import io.metersphere.sdk.util.Translator;
|
||||||
import io.metersphere.system.dto.table.TableBatchProcessDTO;
|
import io.metersphere.system.dto.table.TableBatchProcessDTO;
|
||||||
import io.metersphere.system.file.FileRequest;
|
import io.metersphere.system.file.FileRequest;
|
||||||
import io.metersphere.system.file.MinioRepository;
|
import io.metersphere.system.file.MinioRepository;
|
||||||
|
@ -55,9 +57,6 @@ public class ApiDefinitionService {
|
||||||
@Resource
|
@Resource
|
||||||
private ApiDefinitionFollowerMapper apiDefinitionFollowerMapper;
|
private ApiDefinitionFollowerMapper apiDefinitionFollowerMapper;
|
||||||
|
|
||||||
@Resource
|
|
||||||
private ProjectMapper projectMapper;
|
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private ExtBaseProjectVersionMapper extBaseProjectVersionMapper;
|
private ExtBaseProjectVersionMapper extBaseProjectVersionMapper;
|
||||||
|
|
||||||
|
@ -73,11 +72,13 @@ public class ApiDefinitionService {
|
||||||
@Resource
|
@Resource
|
||||||
private SqlSessionFactory sqlSessionFactory;
|
private SqlSessionFactory sqlSessionFactory;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private ApiTestCaseService apiTestCaseService;
|
||||||
|
|
||||||
public List<ApiDefinitionDTO> getApiDefinitionPage(ApiDefinitionPageRequest request, Boolean deleted){
|
public List<ApiDefinitionDTO> getApiDefinitionPage(ApiDefinitionPageRequest request, Boolean deleted){
|
||||||
List<ApiDefinitionDTO> list = extApiDefinitionMapper.list(request, deleted);
|
List<ApiDefinitionDTO> list = extApiDefinitionMapper.list(request, deleted);
|
||||||
if (!CollectionUtils.isEmpty(list)) {
|
if (!CollectionUtils.isEmpty(list)) {
|
||||||
convertUserIdToName(list);
|
processApiDefinitions(list, request.getProjectId());
|
||||||
calculateApiCase(list, request.getProjectId());
|
|
||||||
}
|
}
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
@ -152,9 +153,9 @@ public class ApiDefinitionService {
|
||||||
|
|
||||||
public void batchUpdate(ApiDefinitionBatchUpdateRequest request, String userId) {
|
public void batchUpdate(ApiDefinitionBatchUpdateRequest request, String userId) {
|
||||||
ProjectService.checkResourceExist(request.getProjectId());
|
ProjectService.checkResourceExist(request.getProjectId());
|
||||||
List<String> ids = getBatchApiIds(request, request.getProjectId());
|
List<String> ids = getBatchApiIds(request, request.getProjectId(), request.getProtocol());
|
||||||
if (CollectionUtils.isNotEmpty(ids)) {
|
if (CollectionUtils.isNotEmpty(ids)) {
|
||||||
if (CollectionUtils.isNotEmpty(request.getTags())) {
|
if (request.getType().equals("tags")) {
|
||||||
handleTags(request, userId, ids);
|
handleTags(request, userId, ids);
|
||||||
} else {
|
} else {
|
||||||
ApiDefinition apiDefinition = new ApiDefinition();
|
ApiDefinition apiDefinition = new ApiDefinition();
|
||||||
|
@ -197,66 +198,43 @@ public class ApiDefinitionService {
|
||||||
return apiDefinition;
|
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) {
|
public void delete(ApiDefinitionDeleteRequest request, String userId) {
|
||||||
checkApiDefinition(request.getId());
|
checkApiDefinition(request.getId());
|
||||||
handleDeleteApiDefinition(Collections.singletonList(request.getId()),request.getDeleteAll(), userId);
|
handleDeleteApiDefinition(Collections.singletonList(request.getId()),request.getDeleteAll(), userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void batchDelete(ApiDefinitionBatchRequest request, String userId) {
|
public void batchDelete(ApiDefinitionBatchRequest request, String userId) {
|
||||||
List<String> ids = getBatchApiIds(request, request.getProjectId());
|
List<String> ids = getBatchApiIds(request, request.getProjectId(), request.getProtocol());
|
||||||
if (CollectionUtils.isNotEmpty(ids)) {
|
if (CollectionUtils.isNotEmpty(ids)) {
|
||||||
handleDeleteApiDefinition(ids, request.getDeleteAll(), userId);
|
handleDeleteApiDefinition(ids, request.getDeleteAll(), userId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void batchMove(ApiDefinitionBatchMoveRequest request, String userId) {
|
public void batchMove(ApiDefinitionBatchMoveRequest request, String userId) {
|
||||||
List<String> ids = getBatchApiIds(request, request.getProjectId());
|
List<String> ids = getBatchApiIds(request, request.getProjectId(), request.getProtocol());
|
||||||
if (CollectionUtils.isNotEmpty(ids)) {
|
if (CollectionUtils.isNotEmpty(ids)) {
|
||||||
List<String> refId = extApiDefinitionMapper.getRefIds(ids);
|
List<String> refId = extApiDefinitionMapper.getRefIds(ids);
|
||||||
extApiDefinitionMapper.batchMove(request, refId, userId);
|
extApiDefinitionMapper.batchMove(request, refId, userId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void processApiDefinitions(List<ApiDefinitionDTO> list, String projectId) {
|
||||||
public void recycleDelete(){
|
|
||||||
// todo 删除接口用例
|
|
||||||
|
|
||||||
// todo 是否有自定义字段关联关系,删除自定义字段关系
|
|
||||||
|
|
||||||
// todo 是否删除关注人(如果删除,回收站恢复的时候如何处理)
|
|
||||||
}
|
|
||||||
|
|
||||||
private void convertUserIdToName(List<ApiDefinitionDTO> list) {
|
|
||||||
Set<String> userIds = extractUserIds(list);
|
Set<String> userIds = extractUserIds(list);
|
||||||
Map<String, String> userMap = userLoginService.getUserNameMap(new ArrayList<>(userIds));
|
Map<String, String> userMap = userLoginService.getUserNameMap(new ArrayList<>(userIds));
|
||||||
|
|
||||||
list.forEach(item -> {
|
List<String> apiDefinitionIds = list.stream().map(ApiDefinitionDTO::getId).toList();
|
||||||
item.setCreateUserName(userMap.get(item.getCreateUser()));
|
List<ApiCaseComputeDTO> apiCaseComputeList = extApiDefinitionMapper.selectApiCaseByIdsAndStatusIsNotTrash(apiDefinitionIds, projectId);
|
||||||
item.setDeleteUserName(userMap.get(item.getDeleteUser()));
|
|
||||||
item.setUpdateUserName(userMap.get(item.getUpdateUser()));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private Set<String> extractUserIds(List<ApiDefinitionDTO> list) {
|
|
||||||
return list.stream()
|
|
||||||
.flatMap(apiDefinition -> Stream.of(apiDefinition.getUpdateUser(), apiDefinition.getDeleteUser(), apiDefinition.getCreateUser()))
|
|
||||||
.collect(Collectors.toSet());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void calculateApiCase(List<ApiDefinitionDTO> list, String projectId) {
|
|
||||||
List<String> ids = list.stream().map(ApiDefinitionDTO::getId).toList();
|
|
||||||
List<ApiCaseComputeDTO> apiCaseComputeList = extApiDefinitionMapper.selectApiCaseByIdsAndStatusIsNotTrash(ids, projectId);
|
|
||||||
Map<String, ApiCaseComputeDTO> resultMap = apiCaseComputeList.stream().collect(Collectors.toMap(ApiCaseComputeDTO::getApiDefinitionId, Function.identity()));
|
Map<String, ApiCaseComputeDTO> resultMap = apiCaseComputeList.stream().collect(Collectors.toMap(ApiCaseComputeDTO::getApiDefinitionId, Function.identity()));
|
||||||
|
|
||||||
list.forEach(item -> {
|
list.forEach(item -> {
|
||||||
|
// Convert User IDs to Names
|
||||||
|
item.setCreateUserName(userMap.get(item.getCreateUser()));
|
||||||
|
item.setDeleteUserName(userMap.get(item.getDeleteUser()));
|
||||||
|
item.setUpdateUserName(userMap.get(item.getUpdateUser()));
|
||||||
|
|
||||||
|
// Convert tags
|
||||||
|
|
||||||
|
// Calculate API Case Metrics
|
||||||
ApiCaseComputeDTO apiCaseComputeDTO = resultMap.get(item.getId());
|
ApiCaseComputeDTO apiCaseComputeDTO = resultMap.get(item.getId());
|
||||||
if (apiCaseComputeDTO != null) {
|
if (apiCaseComputeDTO != null) {
|
||||||
item.setCaseTotal(apiCaseComputeDTO.getCaseTotal());
|
item.setCaseTotal(apiCaseComputeDTO.getCaseTotal());
|
||||||
|
@ -279,6 +257,12 @@ public class ApiDefinitionService {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Set<String> extractUserIds(List<ApiDefinitionDTO> list) {
|
||||||
|
return list.stream()
|
||||||
|
.flatMap(apiDefinition -> Stream.of(apiDefinition.getUpdateUser(), apiDefinition.getDeleteUser(), apiDefinition.getCreateUser()))
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
}
|
||||||
|
|
||||||
public Long getNextOrder(String projectId) {
|
public Long getNextOrder(String projectId) {
|
||||||
Long pos = extApiDefinitionMapper.getPos(projectId);
|
Long pos = extApiDefinitionMapper.getPos(projectId);
|
||||||
return (pos == null ? 0 : pos) + ORDER_STEP;
|
return (pos == null ? 0 : pos) + ORDER_STEP;
|
||||||
|
@ -350,10 +334,10 @@ public class ApiDefinitionService {
|
||||||
return apiDefinitionBlobMapper.selectByPrimaryKey(apiId);
|
return apiDefinitionBlobMapper.selectByPrimaryKey(apiId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public <T> List<String> getBatchApiIds(T dto, String projectId) {
|
public <T> List<String> getBatchApiIds(T dto, String projectId, String protocol) {
|
||||||
TableBatchProcessDTO request = (TableBatchProcessDTO) dto;
|
TableBatchProcessDTO request = (TableBatchProcessDTO) dto;
|
||||||
if (request.isSelectAll()) {
|
if (request.isSelectAll()) {
|
||||||
List<String> ids = extApiDefinitionMapper.getIds(request, projectId, false);
|
List<String> ids = extApiDefinitionMapper.getIds(request, projectId, protocol, false);
|
||||||
if (CollectionUtils.isNotEmpty(request.getExcludeIds())) {
|
if (CollectionUtils.isNotEmpty(request.getExcludeIds())) {
|
||||||
ids.removeAll(request.getExcludeIds());
|
ids.removeAll(request.getExcludeIds());
|
||||||
}
|
}
|
||||||
|
@ -399,7 +383,7 @@ public class ApiDefinitionService {
|
||||||
ids.forEach(id -> {
|
ids.forEach(id -> {
|
||||||
ApiDefinition apiDefinition = new ApiDefinition();
|
ApiDefinition apiDefinition = new ApiDefinition();
|
||||||
if (StringUtils.isNotBlank(collect.get(id).getTags())) {
|
if (StringUtils.isNotBlank(collect.get(id).getTags())) {
|
||||||
List<String> tags = JSON.parseArray(collect.get(id).getTags(), String.class);
|
LinkedHashSet<String> tags = new LinkedHashSet<>((JSON.parseArray(collect.get(id).getTags(), String.class)));
|
||||||
tags.addAll(request.getTags());
|
tags.addAll(request.getTags());
|
||||||
apiDefinition.setTags(JSON.toJSONString(tags));
|
apiDefinition.setTags(JSON.toJSONString(tags));
|
||||||
} else {
|
} else {
|
||||||
|
@ -494,22 +478,6 @@ public class ApiDefinitionService {
|
||||||
extApiDefinitionMapper.updateLatestVersion(id, 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) {
|
private void doDelete(String id, String userId) {
|
||||||
ApiDefinition apiDefinition = new ApiDefinition();
|
ApiDefinition apiDefinition = new ApiDefinition();
|
||||||
apiDefinition.setDeleted(true);
|
apiDefinition.setDeleted(true);
|
||||||
|
|
|
@ -162,7 +162,7 @@ public class ApiDefinitionControllerTests extends BaseTest {
|
||||||
request.setModuleId("root");
|
request.setModuleId("root");
|
||||||
request.setVersionId(defaultVersion);
|
request.setVersionId(defaultVersion);
|
||||||
request.setDescription("描述内容");
|
request.setDescription("描述内容");
|
||||||
request.setTags(List.of("tag1", "tag2"));
|
request.setTags(new LinkedHashSet<>(List.of("tag1", "tag2")));
|
||||||
return request;
|
return request;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -272,25 +272,29 @@ public class ApiDefinitionControllerTests extends BaseTest {
|
||||||
apiDefinitionBatchUpdateRequest.setSelectIds(List.of("1001","1002","1005"));
|
apiDefinitionBatchUpdateRequest.setSelectIds(List.of("1001","1002","1005"));
|
||||||
apiDefinitionBatchUpdateRequest.setExcludeIds(List.of("1005"));
|
apiDefinitionBatchUpdateRequest.setExcludeIds(List.of("1005"));
|
||||||
apiDefinitionBatchUpdateRequest.setSelectAll(false);
|
apiDefinitionBatchUpdateRequest.setSelectAll(false);
|
||||||
|
apiDefinitionBatchUpdateRequest.setType("tags");
|
||||||
// 修改标签,追加
|
// 修改标签,追加
|
||||||
apiDefinitionBatchUpdateRequest.setSelectIds(List.of("1001","1002"));
|
apiDefinitionBatchUpdateRequest.setSelectIds(List.of("1001","1002"));
|
||||||
apiDefinitionBatchUpdateRequest.setTags(List.of("tag-append","tag-append1"));
|
apiDefinitionBatchUpdateRequest.setTags(new LinkedHashSet<>(List.of("tag-append","tag-append1")));
|
||||||
apiDefinitionBatchUpdateRequest.setAppend(true);
|
apiDefinitionBatchUpdateRequest.setAppend(true);
|
||||||
this.requestPostWithOk(BATCH_UPDATE, apiDefinitionBatchUpdateRequest);
|
this.requestPostWithOk(BATCH_UPDATE, apiDefinitionBatchUpdateRequest);
|
||||||
assertBatchUpdateApiDefinition(apiDefinitionBatchUpdateRequest, List.of("1001","1002"));
|
assertBatchUpdateApiDefinition(apiDefinitionBatchUpdateRequest, List.of("1001","1002"));
|
||||||
// 修改标签,覆盖
|
// 修改标签,覆盖
|
||||||
apiDefinitionBatchUpdateRequest.setSelectIds(List.of("1003","1004"));
|
apiDefinitionBatchUpdateRequest.setSelectIds(List.of("1003","1004"));
|
||||||
apiDefinitionBatchUpdateRequest.setTags(List.of("tag-append","tag-append1"));
|
apiDefinitionBatchUpdateRequest.setTags(new LinkedHashSet<>(List.of("tag-append","tag-append1")));
|
||||||
apiDefinitionBatchUpdateRequest.setAppend(false);
|
apiDefinitionBatchUpdateRequest.setAppend(false);
|
||||||
this.requestPostWithOk(BATCH_UPDATE, apiDefinitionBatchUpdateRequest);
|
this.requestPostWithOk(BATCH_UPDATE, apiDefinitionBatchUpdateRequest);
|
||||||
assertBatchUpdateApiDefinition(apiDefinitionBatchUpdateRequest, List.of("1003","1004"));
|
assertBatchUpdateApiDefinition(apiDefinitionBatchUpdateRequest, List.of("1003","1004"));
|
||||||
// 修改协议类型
|
// 修改协议类型
|
||||||
|
apiDefinitionBatchUpdateRequest.setType("method");
|
||||||
apiDefinitionBatchUpdateRequest.setMethod("batch-method");
|
apiDefinitionBatchUpdateRequest.setMethod("batch-method");
|
||||||
this.requestPostWithOk(BATCH_UPDATE, apiDefinitionBatchUpdateRequest);
|
this.requestPostWithOk(BATCH_UPDATE, apiDefinitionBatchUpdateRequest);
|
||||||
// 修改状态
|
// 修改状态
|
||||||
|
apiDefinitionBatchUpdateRequest.setType("status");
|
||||||
apiDefinitionBatchUpdateRequest.setStatus(ApiDefinitionStatus.DEBUGGING.getValue());
|
apiDefinitionBatchUpdateRequest.setStatus(ApiDefinitionStatus.DEBUGGING.getValue());
|
||||||
this.requestPostWithOk(BATCH_UPDATE, apiDefinitionBatchUpdateRequest);
|
this.requestPostWithOk(BATCH_UPDATE, apiDefinitionBatchUpdateRequest);
|
||||||
// 修改版本
|
// 修改版本
|
||||||
|
apiDefinitionBatchUpdateRequest.setType("version");
|
||||||
apiDefinitionBatchUpdateRequest.setVersionId("batch-version");
|
apiDefinitionBatchUpdateRequest.setVersionId("batch-version");
|
||||||
this.requestPostWithOk(BATCH_UPDATE, apiDefinitionBatchUpdateRequest);
|
this.requestPostWithOk(BATCH_UPDATE, apiDefinitionBatchUpdateRequest);
|
||||||
// 修改全部
|
// 修改全部
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
-- 插入测试数据
|
-- 插入测试数据
|
||||||
|
|
||||||
TRUNCATE TABLE api_definition;
|
TRUNCATE TABLE api_definition;
|
||||||
TRUNCATE TABLE project_version;
|
|
||||||
TRUNCATE TABLE api_test_case;
|
TRUNCATE TABLE api_test_case;
|
||||||
TRUNCATE TABLE api_report;
|
TRUNCATE TABLE api_report;
|
||||||
|
|
||||||
|
@ -12,6 +11,7 @@ INSERT INTO `api_definition` (`id`, `name`, `protocol`, `method`, `path`, `statu
|
||||||
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 ('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 `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');
|
||||||
|
|
||||||
|
DELETE FROM `project_version` WHERE `id` = '100570499574136985';
|
||||||
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');
|
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');
|
||||||
|
|
||||||
INSERT INTO `api_test_case` (`id`, `name`, `priority`, `num`, `tags`, `status`, `last_report_status`, `last_report_id`, `pos`, `project_id`, `api_definition_id`, `version_id`, `environment_id`, `create_time`, `create_user`, `update_time`, `update_user`, `delete_time`, `delete_user`, `deleted`) VALUES ('12df5721-c5e6-a38b-e999-3eafcb992094', '查询windows主机', 'P0', 100002001, NULL, 'PENDING', NULL, '10001', 10000, '100001100001', '1001', '100570499574136985', 'admin', 1699500298164, 'admin', 1699500298164, 'admin', NULL, NULL, b'0');
|
INSERT INTO `api_test_case` (`id`, `name`, `priority`, `num`, `tags`, `status`, `last_report_status`, `last_report_id`, `pos`, `project_id`, `api_definition_id`, `version_id`, `environment_id`, `create_time`, `create_user`, `update_time`, `update_user`, `delete_time`, `delete_user`, `deleted`) VALUES ('12df5721-c5e6-a38b-e999-3eafcb992094', '查询windows主机', 'P0', 100002001, NULL, 'PENDING', NULL, '10001', 10000, '100001100001', '1001', '100570499574136985', 'admin', 1699500298164, 'admin', 1699500298164, 'admin', NULL, NULL, b'0');
|
||||||
|
|
Loading…
Reference in New Issue