feat(接口调试): 处理接口中上传的文件

This commit is contained in:
AgAngle 2023-11-21 17:18:16 +08:00 committed by jianxing
parent 93518ad555
commit 8674c0a49f
17 changed files with 1510 additions and 47 deletions

View File

@ -0,0 +1,118 @@
package io.metersphere.api.domain;
import io.metersphere.validation.groups.*;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.*;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import lombok.Data;
@Data
public class ApiFileResource implements Serializable {
@Schema(description = "资源ID(接口用例等)", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{api_file_resource.resource_id.not_blank}", groups = {Created.class})
@Size(min = 1, max = 50, message = "{api_file_resource.resource_id.length_range}", groups = {Created.class, Updated.class})
private String resourceId;
@Schema(description = "文件ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{api_file_resource.file_id.not_blank}", groups = {Created.class})
@Size(min = 1, max = 50, message = "{api_file_resource.file_id.length_range}", groups = {Created.class, Updated.class})
private String fileId;
@Schema(description = "文件名称", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{api_file_resource.file_name.not_blank}", groups = {Created.class})
@Size(min = 1, max = 50, message = "{api_file_resource.file_name.length_range}", groups = {Created.class, Updated.class})
private String fileName;
@Schema(description = "资源类型(API_DEBUG,API,API_CASE,API_SCENARIO)", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{api_file_resource.resource_type.not_blank}", groups = {Created.class})
@Size(min = 1, max = 50, message = "{api_file_resource.resource_type.length_range}", groups = {Created.class, Updated.class})
private String resourceType;
@Schema(description = "创建时间")
private Long createTime;
@Schema(description = "项目ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{api_file_resource.project_id.not_blank}", groups = {Created.class})
@Size(min = 1, max = 50, message = "{api_file_resource.project_id.length_range}", groups = {Created.class, Updated.class})
private String projectId;
private static final long serialVersionUID = 1L;
public enum Column {
resourceId("resource_id", "resourceId", "VARCHAR", false),
fileId("file_id", "fileId", "VARCHAR", false),
fileName("file_name", "fileName", "VARCHAR", false),
resourceType("resource_type", "resourceType", "VARCHAR", false),
createTime("create_time", "createTime", "BIGINT", false),
projectId("project_id", "projectId", "VARCHAR", false);
private static final String BEGINNING_DELIMITER = "`";
private static final String ENDING_DELIMITER = "`";
private final String column;
private final boolean isColumnNameDelimited;
private final String javaProperty;
private final String jdbcType;
public String value() {
return this.column;
}
public String getValue() {
return this.column;
}
public String getJavaProperty() {
return this.javaProperty;
}
public String getJdbcType() {
return this.jdbcType;
}
Column(String column, String javaProperty, String jdbcType, boolean isColumnNameDelimited) {
this.column = column;
this.javaProperty = javaProperty;
this.jdbcType = jdbcType;
this.isColumnNameDelimited = isColumnNameDelimited;
}
public String desc() {
return this.getEscapedColumnName() + " DESC";
}
public String asc() {
return this.getEscapedColumnName() + " ASC";
}
public static Column[] excludes(Column ... excludes) {
ArrayList<Column> columns = new ArrayList<>(Arrays.asList(Column.values()));
if (excludes != null && excludes.length > 0) {
columns.removeAll(new ArrayList<>(Arrays.asList(excludes)));
}
return columns.toArray(new Column[]{});
}
public static Column[] all() {
return Column.values();
}
public String getEscapedColumnName() {
if (this.isColumnNameDelimited) {
return new StringBuilder().append(BEGINNING_DELIMITER).append(this.column).append(ENDING_DELIMITER).toString();
} else {
return this.column;
}
}
public String getAliasedEscapedColumnName() {
return this.getEscapedColumnName();
}
}
}

View File

@ -0,0 +1,610 @@
package io.metersphere.api.domain;
import java.util.ArrayList;
import java.util.List;
public class ApiFileResourceExample {
protected String orderByClause;
protected boolean distinct;
protected List<Criteria> oredCriteria;
public ApiFileResourceExample() {
oredCriteria = new ArrayList<Criteria>();
}
public void setOrderByClause(String orderByClause) {
this.orderByClause = orderByClause;
}
public String getOrderByClause() {
return orderByClause;
}
public void setDistinct(boolean distinct) {
this.distinct = distinct;
}
public boolean isDistinct() {
return distinct;
}
public List<Criteria> getOredCriteria() {
return oredCriteria;
}
public void or(Criteria criteria) {
oredCriteria.add(criteria);
}
public Criteria or() {
Criteria criteria = createCriteriaInternal();
oredCriteria.add(criteria);
return criteria;
}
public Criteria createCriteria() {
Criteria criteria = createCriteriaInternal();
if (oredCriteria.size() == 0) {
oredCriteria.add(criteria);
}
return criteria;
}
protected Criteria createCriteriaInternal() {
Criteria criteria = new Criteria();
return criteria;
}
public void clear() {
oredCriteria.clear();
orderByClause = null;
distinct = false;
}
protected abstract static class GeneratedCriteria {
protected List<Criterion> criteria;
protected GeneratedCriteria() {
super();
criteria = new ArrayList<Criterion>();
}
public boolean isValid() {
return criteria.size() > 0;
}
public List<Criterion> getAllCriteria() {
return criteria;
}
public List<Criterion> getCriteria() {
return criteria;
}
protected void addCriterion(String condition) {
if (condition == null) {
throw new RuntimeException("Value for condition cannot be null");
}
criteria.add(new Criterion(condition));
}
protected void addCriterion(String condition, Object value, String property) {
if (value == null) {
throw new RuntimeException("Value for " + property + " cannot be null");
}
criteria.add(new Criterion(condition, value));
}
protected void addCriterion(String condition, Object value1, Object value2, String property) {
if (value1 == null || value2 == null) {
throw new RuntimeException("Between values for " + property + " cannot be null");
}
criteria.add(new Criterion(condition, value1, value2));
}
public Criteria andResourceIdIsNull() {
addCriterion("resource_id is null");
return (Criteria) this;
}
public Criteria andResourceIdIsNotNull() {
addCriterion("resource_id is not null");
return (Criteria) this;
}
public Criteria andResourceIdEqualTo(String value) {
addCriterion("resource_id =", value, "resourceId");
return (Criteria) this;
}
public Criteria andResourceIdNotEqualTo(String value) {
addCriterion("resource_id <>", value, "resourceId");
return (Criteria) this;
}
public Criteria andResourceIdGreaterThan(String value) {
addCriterion("resource_id >", value, "resourceId");
return (Criteria) this;
}
public Criteria andResourceIdGreaterThanOrEqualTo(String value) {
addCriterion("resource_id >=", value, "resourceId");
return (Criteria) this;
}
public Criteria andResourceIdLessThan(String value) {
addCriterion("resource_id <", value, "resourceId");
return (Criteria) this;
}
public Criteria andResourceIdLessThanOrEqualTo(String value) {
addCriterion("resource_id <=", value, "resourceId");
return (Criteria) this;
}
public Criteria andResourceIdLike(String value) {
addCriterion("resource_id like", value, "resourceId");
return (Criteria) this;
}
public Criteria andResourceIdNotLike(String value) {
addCriterion("resource_id not like", value, "resourceId");
return (Criteria) this;
}
public Criteria andResourceIdIn(List<String> values) {
addCriterion("resource_id in", values, "resourceId");
return (Criteria) this;
}
public Criteria andResourceIdNotIn(List<String> values) {
addCriterion("resource_id not in", values, "resourceId");
return (Criteria) this;
}
public Criteria andResourceIdBetween(String value1, String value2) {
addCriterion("resource_id between", value1, value2, "resourceId");
return (Criteria) this;
}
public Criteria andResourceIdNotBetween(String value1, String value2) {
addCriterion("resource_id not between", value1, value2, "resourceId");
return (Criteria) this;
}
public Criteria andFileIdIsNull() {
addCriterion("file_id is null");
return (Criteria) this;
}
public Criteria andFileIdIsNotNull() {
addCriterion("file_id is not null");
return (Criteria) this;
}
public Criteria andFileIdEqualTo(String value) {
addCriterion("file_id =", value, "fileId");
return (Criteria) this;
}
public Criteria andFileIdNotEqualTo(String value) {
addCriterion("file_id <>", value, "fileId");
return (Criteria) this;
}
public Criteria andFileIdGreaterThan(String value) {
addCriterion("file_id >", value, "fileId");
return (Criteria) this;
}
public Criteria andFileIdGreaterThanOrEqualTo(String value) {
addCriterion("file_id >=", value, "fileId");
return (Criteria) this;
}
public Criteria andFileIdLessThan(String value) {
addCriterion("file_id <", value, "fileId");
return (Criteria) this;
}
public Criteria andFileIdLessThanOrEqualTo(String value) {
addCriterion("file_id <=", value, "fileId");
return (Criteria) this;
}
public Criteria andFileIdLike(String value) {
addCriterion("file_id like", value, "fileId");
return (Criteria) this;
}
public Criteria andFileIdNotLike(String value) {
addCriterion("file_id not like", value, "fileId");
return (Criteria) this;
}
public Criteria andFileIdIn(List<String> values) {
addCriterion("file_id in", values, "fileId");
return (Criteria) this;
}
public Criteria andFileIdNotIn(List<String> values) {
addCriterion("file_id not in", values, "fileId");
return (Criteria) this;
}
public Criteria andFileIdBetween(String value1, String value2) {
addCriterion("file_id between", value1, value2, "fileId");
return (Criteria) this;
}
public Criteria andFileIdNotBetween(String value1, String value2) {
addCriterion("file_id not between", value1, value2, "fileId");
return (Criteria) this;
}
public Criteria andFileNameIsNull() {
addCriterion("file_name is null");
return (Criteria) this;
}
public Criteria andFileNameIsNotNull() {
addCriterion("file_name is not null");
return (Criteria) this;
}
public Criteria andFileNameEqualTo(String value) {
addCriterion("file_name =", value, "fileName");
return (Criteria) this;
}
public Criteria andFileNameNotEqualTo(String value) {
addCriterion("file_name <>", value, "fileName");
return (Criteria) this;
}
public Criteria andFileNameGreaterThan(String value) {
addCriterion("file_name >", value, "fileName");
return (Criteria) this;
}
public Criteria andFileNameGreaterThanOrEqualTo(String value) {
addCriterion("file_name >=", value, "fileName");
return (Criteria) this;
}
public Criteria andFileNameLessThan(String value) {
addCriterion("file_name <", value, "fileName");
return (Criteria) this;
}
public Criteria andFileNameLessThanOrEqualTo(String value) {
addCriterion("file_name <=", value, "fileName");
return (Criteria) this;
}
public Criteria andFileNameLike(String value) {
addCriterion("file_name like", value, "fileName");
return (Criteria) this;
}
public Criteria andFileNameNotLike(String value) {
addCriterion("file_name not like", value, "fileName");
return (Criteria) this;
}
public Criteria andFileNameIn(List<String> values) {
addCriterion("file_name in", values, "fileName");
return (Criteria) this;
}
public Criteria andFileNameNotIn(List<String> values) {
addCriterion("file_name not in", values, "fileName");
return (Criteria) this;
}
public Criteria andFileNameBetween(String value1, String value2) {
addCriterion("file_name between", value1, value2, "fileName");
return (Criteria) this;
}
public Criteria andFileNameNotBetween(String value1, String value2) {
addCriterion("file_name not between", value1, value2, "fileName");
return (Criteria) this;
}
public Criteria andResourceTypeIsNull() {
addCriterion("resource_type is null");
return (Criteria) this;
}
public Criteria andResourceTypeIsNotNull() {
addCriterion("resource_type is not null");
return (Criteria) this;
}
public Criteria andResourceTypeEqualTo(String value) {
addCriterion("resource_type =", value, "resourceType");
return (Criteria) this;
}
public Criteria andResourceTypeNotEqualTo(String value) {
addCriterion("resource_type <>", value, "resourceType");
return (Criteria) this;
}
public Criteria andResourceTypeGreaterThan(String value) {
addCriterion("resource_type >", value, "resourceType");
return (Criteria) this;
}
public Criteria andResourceTypeGreaterThanOrEqualTo(String value) {
addCriterion("resource_type >=", value, "resourceType");
return (Criteria) this;
}
public Criteria andResourceTypeLessThan(String value) {
addCriterion("resource_type <", value, "resourceType");
return (Criteria) this;
}
public Criteria andResourceTypeLessThanOrEqualTo(String value) {
addCriterion("resource_type <=", value, "resourceType");
return (Criteria) this;
}
public Criteria andResourceTypeLike(String value) {
addCriterion("resource_type like", value, "resourceType");
return (Criteria) this;
}
public Criteria andResourceTypeNotLike(String value) {
addCriterion("resource_type not like", value, "resourceType");
return (Criteria) this;
}
public Criteria andResourceTypeIn(List<String> values) {
addCriterion("resource_type in", values, "resourceType");
return (Criteria) this;
}
public Criteria andResourceTypeNotIn(List<String> values) {
addCriterion("resource_type not in", values, "resourceType");
return (Criteria) this;
}
public Criteria andResourceTypeBetween(String value1, String value2) {
addCriterion("resource_type between", value1, value2, "resourceType");
return (Criteria) this;
}
public Criteria andResourceTypeNotBetween(String value1, String value2) {
addCriterion("resource_type not between", value1, value2, "resourceType");
return (Criteria) this;
}
public Criteria andCreateTimeIsNull() {
addCriterion("create_time is null");
return (Criteria) this;
}
public Criteria andCreateTimeIsNotNull() {
addCriterion("create_time is not null");
return (Criteria) this;
}
public Criteria andCreateTimeEqualTo(Long value) {
addCriterion("create_time =", value, "createTime");
return (Criteria) this;
}
public Criteria andCreateTimeNotEqualTo(Long value) {
addCriterion("create_time <>", value, "createTime");
return (Criteria) this;
}
public Criteria andCreateTimeGreaterThan(Long value) {
addCriterion("create_time >", value, "createTime");
return (Criteria) this;
}
public Criteria andCreateTimeGreaterThanOrEqualTo(Long value) {
addCriterion("create_time >=", value, "createTime");
return (Criteria) this;
}
public Criteria andCreateTimeLessThan(Long value) {
addCriterion("create_time <", value, "createTime");
return (Criteria) this;
}
public Criteria andCreateTimeLessThanOrEqualTo(Long value) {
addCriterion("create_time <=", value, "createTime");
return (Criteria) this;
}
public Criteria andCreateTimeIn(List<Long> values) {
addCriterion("create_time in", values, "createTime");
return (Criteria) this;
}
public Criteria andCreateTimeNotIn(List<Long> values) {
addCriterion("create_time not in", values, "createTime");
return (Criteria) this;
}
public Criteria andCreateTimeBetween(Long value1, Long value2) {
addCriterion("create_time between", value1, value2, "createTime");
return (Criteria) this;
}
public Criteria andCreateTimeNotBetween(Long value1, Long value2) {
addCriterion("create_time not between", value1, value2, "createTime");
return (Criteria) this;
}
public Criteria andProjectIdIsNull() {
addCriterion("project_id is null");
return (Criteria) this;
}
public Criteria andProjectIdIsNotNull() {
addCriterion("project_id is not null");
return (Criteria) this;
}
public Criteria andProjectIdEqualTo(String value) {
addCriterion("project_id =", value, "projectId");
return (Criteria) this;
}
public Criteria andProjectIdNotEqualTo(String value) {
addCriterion("project_id <>", value, "projectId");
return (Criteria) this;
}
public Criteria andProjectIdGreaterThan(String value) {
addCriterion("project_id >", value, "projectId");
return (Criteria) this;
}
public Criteria andProjectIdGreaterThanOrEqualTo(String value) {
addCriterion("project_id >=", value, "projectId");
return (Criteria) this;
}
public Criteria andProjectIdLessThan(String value) {
addCriterion("project_id <", value, "projectId");
return (Criteria) this;
}
public Criteria andProjectIdLessThanOrEqualTo(String value) {
addCriterion("project_id <=", value, "projectId");
return (Criteria) this;
}
public Criteria andProjectIdLike(String value) {
addCriterion("project_id like", value, "projectId");
return (Criteria) this;
}
public Criteria andProjectIdNotLike(String value) {
addCriterion("project_id not like", value, "projectId");
return (Criteria) this;
}
public Criteria andProjectIdIn(List<String> values) {
addCriterion("project_id in", values, "projectId");
return (Criteria) this;
}
public Criteria andProjectIdNotIn(List<String> values) {
addCriterion("project_id not in", values, "projectId");
return (Criteria) this;
}
public Criteria andProjectIdBetween(String value1, String value2) {
addCriterion("project_id between", value1, value2, "projectId");
return (Criteria) this;
}
public Criteria andProjectIdNotBetween(String value1, String value2) {
addCriterion("project_id not between", value1, value2, "projectId");
return (Criteria) this;
}
}
public static class Criteria extends GeneratedCriteria {
protected Criteria() {
super();
}
}
public static class Criterion {
private String condition;
private Object value;
private Object secondValue;
private boolean noValue;
private boolean singleValue;
private boolean betweenValue;
private boolean listValue;
private String typeHandler;
public String getCondition() {
return condition;
}
public Object getValue() {
return value;
}
public Object getSecondValue() {
return secondValue;
}
public boolean isNoValue() {
return noValue;
}
public boolean isSingleValue() {
return singleValue;
}
public boolean isBetweenValue() {
return betweenValue;
}
public boolean isListValue() {
return listValue;
}
public String getTypeHandler() {
return typeHandler;
}
protected Criterion(String condition) {
super();
this.condition = condition;
this.typeHandler = null;
this.noValue = true;
}
protected Criterion(String condition, Object value, String typeHandler) {
super();
this.condition = condition;
this.value = value;
this.typeHandler = typeHandler;
if (value instanceof List<?>) {
this.listValue = true;
} else {
this.singleValue = true;
}
}
protected Criterion(String condition, Object value) {
this(condition, value, null);
}
protected Criterion(String condition, Object value, Object secondValue, String typeHandler) {
super();
this.condition = condition;
this.value = value;
this.secondValue = secondValue;
this.typeHandler = typeHandler;
this.betweenValue = true;
}
protected Criterion(String condition, Object value, Object secondValue) {
this(condition, value, secondValue, null);
}
}
}

View File

@ -0,0 +1,34 @@
package io.metersphere.api.mapper;
import io.metersphere.api.domain.ApiFileResource;
import io.metersphere.api.domain.ApiFileResourceExample;
import java.util.List;
import org.apache.ibatis.annotations.Param;
public interface ApiFileResourceMapper {
long countByExample(ApiFileResourceExample example);
int deleteByExample(ApiFileResourceExample example);
int deleteByPrimaryKey(@Param("resourceId") String resourceId, @Param("fileId") String fileId);
int insert(ApiFileResource record);
int insertSelective(ApiFileResource record);
List<ApiFileResource> selectByExample(ApiFileResourceExample example);
ApiFileResource selectByPrimaryKey(@Param("resourceId") String resourceId, @Param("fileId") String fileId);
int updateByExampleSelective(@Param("record") ApiFileResource record, @Param("example") ApiFileResourceExample example);
int updateByExample(@Param("record") ApiFileResource record, @Param("example") ApiFileResourceExample example);
int updateByPrimaryKeySelective(ApiFileResource record);
int updateByPrimaryKey(ApiFileResource record);
int batchInsert(@Param("list") List<ApiFileResource> list);
int batchInsertSelective(@Param("list") List<ApiFileResource> list, @Param("selective") ApiFileResource.Column ... selective);
}

View File

@ -0,0 +1,270 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="io.metersphere.api.mapper.ApiFileResourceMapper">
<resultMap id="BaseResultMap" type="io.metersphere.api.domain.ApiFileResource">
<id column="resource_id" jdbcType="VARCHAR" property="resourceId" />
<id column="file_id" jdbcType="VARCHAR" property="fileId" />
<result column="file_name" jdbcType="VARCHAR" property="fileName" />
<result column="resource_type" jdbcType="VARCHAR" property="resourceType" />
<result column="create_time" jdbcType="BIGINT" property="createTime" />
<result column="project_id" jdbcType="VARCHAR" property="projectId" />
</resultMap>
<sql id="Example_Where_Clause">
<where>
<foreach collection="oredCriteria" item="criteria" separator="or">
<if test="criteria.valid">
<trim prefix="(" prefixOverrides="and" suffix=")">
<foreach collection="criteria.criteria" item="criterion">
<choose>
<when test="criterion.noValue">
and ${criterion.condition}
</when>
<when test="criterion.singleValue">
and ${criterion.condition} #{criterion.value}
</when>
<when test="criterion.betweenValue">
and ${criterion.condition} #{criterion.value} and #{criterion.secondValue}
</when>
<when test="criterion.listValue">
and ${criterion.condition}
<foreach close=")" collection="criterion.value" item="listItem" open="(" separator=",">
#{listItem}
</foreach>
</when>
</choose>
</foreach>
</trim>
</if>
</foreach>
</where>
</sql>
<sql id="Update_By_Example_Where_Clause">
<where>
<foreach collection="example.oredCriteria" item="criteria" separator="or">
<if test="criteria.valid">
<trim prefix="(" prefixOverrides="and" suffix=")">
<foreach collection="criteria.criteria" item="criterion">
<choose>
<when test="criterion.noValue">
and ${criterion.condition}
</when>
<when test="criterion.singleValue">
and ${criterion.condition} #{criterion.value}
</when>
<when test="criterion.betweenValue">
and ${criterion.condition} #{criterion.value} and #{criterion.secondValue}
</when>
<when test="criterion.listValue">
and ${criterion.condition}
<foreach close=")" collection="criterion.value" item="listItem" open="(" separator=",">
#{listItem}
</foreach>
</when>
</choose>
</foreach>
</trim>
</if>
</foreach>
</where>
</sql>
<sql id="Base_Column_List">
resource_id, file_id, file_name, resource_type, create_time, project_id
</sql>
<select id="selectByExample" parameterType="io.metersphere.api.domain.ApiFileResourceExample" resultMap="BaseResultMap">
select
<if test="distinct">
distinct
</if>
<include refid="Base_Column_List" />
from api_file_resource
<if test="_parameter != null">
<include refid="Example_Where_Clause" />
</if>
<if test="orderByClause != null">
order by ${orderByClause}
</if>
</select>
<select id="selectByPrimaryKey" parameterType="map" resultMap="BaseResultMap">
select
<include refid="Base_Column_List" />
from api_file_resource
where resource_id = #{resourceId,jdbcType=VARCHAR}
and file_id = #{fileId,jdbcType=VARCHAR}
</select>
<delete id="deleteByPrimaryKey" parameterType="map">
delete from api_file_resource
where resource_id = #{resourceId,jdbcType=VARCHAR}
and file_id = #{fileId,jdbcType=VARCHAR}
</delete>
<delete id="deleteByExample" parameterType="io.metersphere.api.domain.ApiFileResourceExample">
delete from api_file_resource
<if test="_parameter != null">
<include refid="Example_Where_Clause" />
</if>
</delete>
<insert id="insert" parameterType="io.metersphere.api.domain.ApiFileResource">
insert into api_file_resource (resource_id, file_id, file_name,
resource_type, create_time, project_id
)
values (#{resourceId,jdbcType=VARCHAR}, #{fileId,jdbcType=VARCHAR}, #{fileName,jdbcType=VARCHAR},
#{resourceType,jdbcType=VARCHAR}, #{createTime,jdbcType=BIGINT}, #{projectId,jdbcType=VARCHAR}
)
</insert>
<insert id="insertSelective" parameterType="io.metersphere.api.domain.ApiFileResource">
insert into api_file_resource
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="resourceId != null">
resource_id,
</if>
<if test="fileId != null">
file_id,
</if>
<if test="fileName != null">
file_name,
</if>
<if test="resourceType != null">
resource_type,
</if>
<if test="createTime != null">
create_time,
</if>
<if test="projectId != null">
project_id,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="resourceId != null">
#{resourceId,jdbcType=VARCHAR},
</if>
<if test="fileId != null">
#{fileId,jdbcType=VARCHAR},
</if>
<if test="fileName != null">
#{fileName,jdbcType=VARCHAR},
</if>
<if test="resourceType != null">
#{resourceType,jdbcType=VARCHAR},
</if>
<if test="createTime != null">
#{createTime,jdbcType=BIGINT},
</if>
<if test="projectId != null">
#{projectId,jdbcType=VARCHAR},
</if>
</trim>
</insert>
<select id="countByExample" parameterType="io.metersphere.api.domain.ApiFileResourceExample" resultType="java.lang.Long">
select count(*) from api_file_resource
<if test="_parameter != null">
<include refid="Example_Where_Clause" />
</if>
</select>
<update id="updateByExampleSelective" parameterType="map">
update api_file_resource
<set>
<if test="record.resourceId != null">
resource_id = #{record.resourceId,jdbcType=VARCHAR},
</if>
<if test="record.fileId != null">
file_id = #{record.fileId,jdbcType=VARCHAR},
</if>
<if test="record.fileName != null">
file_name = #{record.fileName,jdbcType=VARCHAR},
</if>
<if test="record.resourceType != null">
resource_type = #{record.resourceType,jdbcType=VARCHAR},
</if>
<if test="record.createTime != null">
create_time = #{record.createTime,jdbcType=BIGINT},
</if>
<if test="record.projectId != null">
project_id = #{record.projectId,jdbcType=VARCHAR},
</if>
</set>
<if test="_parameter != null">
<include refid="Update_By_Example_Where_Clause" />
</if>
</update>
<update id="updateByExample" parameterType="map">
update api_file_resource
set resource_id = #{record.resourceId,jdbcType=VARCHAR},
file_id = #{record.fileId,jdbcType=VARCHAR},
file_name = #{record.fileName,jdbcType=VARCHAR},
resource_type = #{record.resourceType,jdbcType=VARCHAR},
create_time = #{record.createTime,jdbcType=BIGINT},
project_id = #{record.projectId,jdbcType=VARCHAR}
<if test="_parameter != null">
<include refid="Update_By_Example_Where_Clause" />
</if>
</update>
<update id="updateByPrimaryKeySelective" parameterType="io.metersphere.api.domain.ApiFileResource">
update api_file_resource
<set>
<if test="fileName != null">
file_name = #{fileName,jdbcType=VARCHAR},
</if>
<if test="resourceType != null">
resource_type = #{resourceType,jdbcType=VARCHAR},
</if>
<if test="createTime != null">
create_time = #{createTime,jdbcType=BIGINT},
</if>
<if test="projectId != null">
project_id = #{projectId,jdbcType=VARCHAR},
</if>
</set>
where resource_id = #{resourceId,jdbcType=VARCHAR}
and file_id = #{fileId,jdbcType=VARCHAR}
</update>
<update id="updateByPrimaryKey" parameterType="io.metersphere.api.domain.ApiFileResource">
update api_file_resource
set file_name = #{fileName,jdbcType=VARCHAR},
resource_type = #{resourceType,jdbcType=VARCHAR},
create_time = #{createTime,jdbcType=BIGINT},
project_id = #{projectId,jdbcType=VARCHAR}
where resource_id = #{resourceId,jdbcType=VARCHAR}
and file_id = #{fileId,jdbcType=VARCHAR}
</update>
<insert id="batchInsert" parameterType="map">
insert into api_file_resource
(resource_id, file_id, file_name, resource_type, create_time, project_id)
values
<foreach collection="list" item="item" separator=",">
(#{item.resourceId,jdbcType=VARCHAR}, #{item.fileId,jdbcType=VARCHAR}, #{item.fileName,jdbcType=VARCHAR},
#{item.resourceType,jdbcType=VARCHAR}, #{item.createTime,jdbcType=BIGINT}, #{item.projectId,jdbcType=VARCHAR}
)
</foreach>
</insert>
<insert id="batchInsertSelective" parameterType="map">
insert into api_file_resource (
<foreach collection="selective" item="column" separator=",">
${column.escapedColumnName}
</foreach>
)
values
<foreach collection="list" item="item" separator=",">
(
<foreach collection="selective" item="column" separator=",">
<if test="'resource_id'.toString() == column.value">
#{item.resourceId,jdbcType=VARCHAR}
</if>
<if test="'file_id'.toString() == column.value">
#{item.fileId,jdbcType=VARCHAR}
</if>
<if test="'file_name'.toString() == column.value">
#{item.fileName,jdbcType=VARCHAR}
</if>
<if test="'resource_type'.toString() == column.value">
#{item.resourceType,jdbcType=VARCHAR}
</if>
<if test="'create_time'.toString() == column.value">
#{item.createTime,jdbcType=BIGINT}
</if>
<if test="'project_id'.toString() == column.value">
#{item.projectId,jdbcType=VARCHAR}
</if>
</foreach>
)
</foreach>
</insert>
</mapper>

View File

@ -508,5 +508,16 @@ CREATE INDEX idx_api_scenario_id ON api_scenario_environment(api_scenario_id);
CREATE INDEX idx_project_id ON api_scenario_environment(project_id);
CREATE INDEX idx_environment_id ON api_scenario_environment(environment_id);
CREATE TABLE IF NOT EXISTS api_file_resource(
`resource_id` VARCHAR(50) NOT NULL COMMENT '资源ID(接口用例等)' ,
`file_id` VARCHAR(50) NOT NULL COMMENT '文件ID' ,
`file_name` VARCHAR(50) NOT NULL COMMENT '文件名称' ,
`resource_type` VARCHAR(50) NOT NULL COMMENT '资源类型(API_DEBUG,API,API_CASE,API_SCENARIO)' ,
`create_time` BIGINT NOT NULL COMMENT '创建时间' ,
`project_id` VARCHAR(50) NOT NULL COMMENT '项目ID' ,
PRIMARY KEY (resource_id,file_id)
) COMMENT = '接口和所需文件资源的关联表';
-- set innodb lock wait timeout to default
SET SESSION innodb_lock_wait_timeout = DEFAULT;

View File

@ -41,6 +41,11 @@ public class DefaultRepositoryDir {
private static final String PROJECT_ENV_SSL_DIR = PROJECT_DIR + "/environment/%s";
private static final String PROJECT_FUNCTIONAL_CASE_DIR = PROJECT_DIR + "/functional-case/%s";
private static final String PROJECT_FILE_MANAGEMENT_DIR = PROJECT_DIR + "/file-management";
/**
* 接口调试相关文件的存储目录
* project/{projectId}/apiCase/{apiDebugId}
*/
private static final String PROJECT_API_DEBUG_DIR = PROJECT_DIR + "/api-debug/%s";
private static final String PROJECT_BUG_DIR = PROJECT_DIR + "/bug/%s";
/*------ end: 项目下资源目录 --------*/
@ -73,4 +78,8 @@ public class DefaultRepositoryDir {
public static String getBugDir(String projectId, String bugId) {
return String.format(PROJECT_BUG_DIR, projectId, bugId);
}
public static String getApiDebugDir(String projectId, String apiDebugId) {
return String.format(PROJECT_API_DEBUG_DIR, projectId, apiDebugId);
}
}

View File

@ -15,4 +15,9 @@ public class FormDataKV extends KeyValueParam {
private Integer maxLength;
private String contentType;
private Boolean encode = false;
/**
* 记录文件的ID防止重名
* 生成脚本时通过 fileId + value(文件名) 获取文件路径
*/
private String fileId;
}

View File

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

View File

@ -17,6 +17,7 @@ import jakarta.annotation.Resource;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.util.List;
@ -50,16 +51,18 @@ public class ApiDebugController {
@Operation(summary = "创建接口调试")
@RequiresPermissions(PermissionConstants.PROJECT_API_DEBUG_ADD)
@Log(type = OperationLogType.ADD, expression = "#msClass.addLog(#request)", msClass = ApiDebugLogService.class)
public ApiDebug add(@Validated @RequestBody ApiDebugAddRequest request) {
return apiDebugService.add(request, SessionUtils.getUserId());
public ApiDebug add(@Validated @RequestPart("request") ApiDebugAddRequest request,
@RequestPart(value = "files", required = false) List<MultipartFile> files) {
return apiDebugService.add(request, files, SessionUtils.getUserId());
}
@PostMapping("/update")
@Operation(summary = "更新接口调试")
@RequiresPermissions(PermissionConstants.PROJECT_API_DEBUG_UPDATE)
@Log(type = OperationLogType.UPDATE, expression = "#msClass.updateLog(#request)", msClass = ApiDebugLogService.class)
public ApiDebug update(@Validated @RequestBody ApiDebugUpdateRequest request) {
return apiDebugService.update(request, SessionUtils.getUserId());
public ApiDebug update(@Validated @RequestPart("request") ApiDebugUpdateRequest request,
@RequestPart(value = "files", required = false) List<MultipartFile> files) {
return apiDebugService.update(request, files, SessionUtils.getUserId());
}
@GetMapping("/delete/{id}")

View File

@ -6,6 +6,7 @@ import jakarta.validation.constraints.Size;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
/**
* @Author: jianxing
@ -45,4 +46,13 @@ public class ApiDebugAddRequest implements Serializable {
@Schema(description = "请求内容")
@NotBlank
private String request;
/**
* 文件ID列表
* 需要和上传的文件顺序保持一致
* 为了解决文件名称重复的问题需要把文件和ID一一对应
* 创建时先按ID创建目录再把文件放入目录
*/
@Schema(description = "接口所需的所有文件资源ID与上传的文件顺序保持一致")
private List<String> fileIds;
}

View File

@ -5,6 +5,8 @@ import io.metersphere.plugin.api.spi.AbstractMsTestElement;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.List;
@Data
public class ApiDebugDTO extends ApiDebug {
@Schema(description = "请求内容")
@ -12,4 +14,7 @@ public class ApiDebugDTO extends ApiDebug {
@Schema(description = "响应内容")
private String response;
@Schema(description = "接口所关联的文件ID列表修改时需要作为参数传入")
private List<String> fileIds;
}

View File

@ -8,6 +8,7 @@ import jakarta.validation.constraints.Size;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
/**
* @Author: jianxing
@ -42,4 +43,15 @@ public class ApiDebugUpdateRequest implements Serializable {
@Schema(description = "请求内容")
@NotBlank
private String request;
@Schema(description = "接口所需的所有文件资源ID")
private List<String> fileIds;
/**
* 新上传的文件ID
* 为了解决文件名称重复的问题需要把文件和ID一一对应
* 创建时先按ID创建目录再把文件放入目录
*/
@Schema(description = "新上传的文件ID与上传的文件顺序保持一致")
private List<String> addFileIds;
}

View File

@ -0,0 +1,44 @@
package io.metersphere.api.dto.debug;
import io.metersphere.api.constants.ApiResourceType;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
/**
* @Author: jianxing
* @CreateTime: 2023-11-06 10:22
*/
@Data
public class ApiFileResourceUpdateRequest implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 项目ID
*/
private String projectId;
/**
* 资源类型
*/
private ApiResourceType apiResourceType;
/**
* 关联的资源ID
*/
private String resourceId;
/**
* 文件存储的目录
*/
private String folder;
/**
* 接口所需的所有文件资源ID
*/
private List<String> fileIds;
/**
* 新上传的文件ID
* 更新时记录新上传的文件ID与上传的文件顺序保持一致
*/
private List<String> addFileIds;
}

View File

@ -0,0 +1,170 @@
package io.metersphere.api.service;
import io.metersphere.api.constants.ApiResourceType;
import io.metersphere.api.domain.ApiFileResource;
import io.metersphere.api.domain.ApiFileResourceExample;
import io.metersphere.api.dto.debug.ApiFileResourceUpdateRequest;
import io.metersphere.api.mapper.ApiFileResourceMapper;
import io.metersphere.sdk.exception.MSException;
import io.metersphere.sdk.util.LogUtils;
import io.metersphere.sdk.util.Translator;
import io.metersphere.system.file.FileCenter;
import io.metersphere.system.file.FileRequest;
import jakarta.annotation.Resource;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections4.ListUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
/**
* @Author: jianxing
* @CreateTime: 2023-11-16 16:49
*/
@Service
@Transactional(rollbackFor = Exception.class)
public class ApiFileResourceService {
@Resource
private ApiFileResourceMapper apiFileResourceMapper;
/**
* 上传接口相关的资源文件
* @param folder
* @param files
*/
public void uploadFileResource(String folder, List<String> addFileIds, List<MultipartFile> files) {
if (CollectionUtils.isEmpty(files) || CollectionUtils.isEmpty(addFileIds)) {
return;
}
int size = addFileIds.size() > files.size() ? addFileIds.size() : files.size();
for (int i = 0; i < size; i++) {
String fileId = addFileIds.get(i);
MultipartFile file = files.get(i);
FileRequest fileRequest = new FileRequest();
fileRequest.setFileName(file.getOriginalFilename());
// 按ID建文件夹避免文件名重复
fileRequest.setFolder(folder + "/" + fileId);
try {
FileCenter.getDefaultRepository()
.saveFile(file, fileRequest);
} catch (Exception e) {
LogUtils.error(e);
throw new MSException(Translator.get("file_upload_fail"));
}
}
}
/**
* 添加接口与文件的关联关系
*/
public void addFileResource(ApiFileResourceUpdateRequest resourceUpdateRequest, List<MultipartFile> files) {
List<String> addFileIds = resourceUpdateRequest.getAddFileIds();
String resourceId = resourceUpdateRequest.getResourceId();
String projectId = resourceUpdateRequest.getProjectId();
ApiResourceType apiResourceType = resourceUpdateRequest.getApiResourceType();
if (CollectionUtils.isEmpty(addFileIds) || CollectionUtils.isEmpty(files)) {
return;
}
// 添加文件与接口的关联关系
int size = addFileIds.size() > files.size() ? addFileIds.size() : files.size();
List<ApiFileResource> apiFileResources = new ArrayList<>();
for (int i = 0; i < size; i++) {
String fileId = addFileIds.get(i);
MultipartFile file = files.get(i);
ApiFileResource apiFileResource = new ApiFileResource();
apiFileResource.setFileId(fileId);
apiFileResource.setResourceId(resourceId);
apiFileResource.setResourceType(apiResourceType.name());
apiFileResource.setProjectId(projectId);
apiFileResource.setCreateTime(System.currentTimeMillis());
apiFileResource.setFileName(file.getOriginalFilename());
apiFileResources.add(apiFileResource);
}
apiFileResourceMapper.batchInsert(apiFileResources);
// 上传文件到对象存储
uploadFileResource(resourceUpdateRequest.getFolder(), addFileIds, files);
}
/**
* 更新接口时更新接口与文件的关联关系
*/
public void updateFileResource(ApiFileResourceUpdateRequest resourceUpdateRequest, List<MultipartFile> files) {
// fileIds == null 则不修改如果是空数组则删除所有关联的文件
List<String> fileIds = resourceUpdateRequest.getFileIds();
String resourceId = resourceUpdateRequest.getResourceId();
if (fileIds == null) {
return;
}
// 查询原本关联的文件
ApiFileResourceExample example = new ApiFileResourceExample();
example.createCriteria().andResourceIdEqualTo(resourceId);
List<ApiFileResource> apiFileResources = apiFileResourceMapper.selectByExample(example);
List<String> originFileIds = apiFileResources.stream()
.map(ApiFileResource::getFileId)
.collect(Collectors.toList());
// 删除没用的文件
List<String> deleteFileIds = ListUtils.subtract(originFileIds, fileIds);
deleteFileResource(deleteFileIds, resourceUpdateRequest);
// 上传新的文件
addFileResource(resourceUpdateRequest, files);
}
private void deleteFileResource(List<String> deleteFileIds, ApiFileResourceUpdateRequest resourceUpdateRequest) {
if (CollectionUtils.isEmpty(deleteFileIds)) {
return;
}
// 删除关联关系
ApiFileResourceExample example = new ApiFileResourceExample();
example.createCriteria()
.andResourceIdEqualTo(resourceUpdateRequest.getResourceId())
.andFileIdIn(deleteFileIds);
apiFileResourceMapper.deleteByExample(example);
deleteFileIds.forEach(fileId -> {
FileRequest request = new FileRequest();
// 删除文件所在目录
request.setFolder(resourceUpdateRequest.getFolder() + "/" + fileId);
try {
FileCenter.getDefaultRepository().deleteFolder(request);
} catch (Exception e) {
LogUtils.error(e);
}
});
}
public void deleteByResourceId(String apiDebugDir, String resourceId) {
ApiFileResourceExample example = new ApiFileResourceExample();
example.createCriteria()
.andResourceIdEqualTo(resourceId);
apiFileResourceMapper.deleteByExample(example);
FileRequest request = new FileRequest();
request.setFolder(apiDebugDir);
try {
FileCenter.getDefaultRepository().deleteFolder(request);
} catch (Exception e) {
LogUtils.error(e);
}
}
public List<ApiFileResource> getByResourceId(String resourceId) {
ApiFileResourceExample example = new ApiFileResourceExample();
example.createCriteria()
.andResourceIdEqualTo(resourceId);
return apiFileResourceMapper.selectByExample(example);
}
public List<String> getFileIdsByResourceId(String resourceId) {
return getByResourceId(resourceId).stream()
.map(ApiFileResource::getFileId)
.collect(Collectors.toList());
}
}

View File

@ -1,18 +1,18 @@
package io.metersphere.api.service.debug;
import io.metersphere.api.constants.ApiResourceType;
import io.metersphere.api.domain.ApiDebug;
import io.metersphere.api.domain.ApiDebugBlob;
import io.metersphere.api.domain.ApiDebugExample;
import io.metersphere.api.dto.debug.ApiDebugAddRequest;
import io.metersphere.api.dto.debug.ApiDebugDTO;
import io.metersphere.api.dto.debug.ApiDebugSimpleDTO;
import io.metersphere.api.dto.debug.ApiDebugUpdateRequest;
import io.metersphere.api.dto.debug.*;
import io.metersphere.api.mapper.ApiDebugBlobMapper;
import io.metersphere.api.mapper.ApiDebugMapper;
import io.metersphere.api.mapper.ExtApiDebugMapper;
import io.metersphere.api.service.ApiFileResourceService;
import io.metersphere.api.util.ApiDataUtils;
import io.metersphere.plugin.api.spi.AbstractMsTestElement;
import io.metersphere.project.service.ProjectService;
import io.metersphere.sdk.constants.DefaultRepositoryDir;
import io.metersphere.sdk.exception.MSException;
import io.metersphere.sdk.util.BeanUtils;
import io.metersphere.system.uid.IDGenerator;
@ -22,6 +22,7 @@ import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import java.util.List;
@ -34,13 +35,14 @@ import static io.metersphere.api.controller.result.ApiResultCode.API_DEBUG_EXIST
@Service
@Transactional(rollbackFor = Exception.class)
public class ApiDebugService {
@Resource
private ApiDebugMapper apiDebugMapper;
@Resource
private ApiDebugBlobMapper apiDebugBlobMapper;
@Resource
private ExtApiDebugMapper extApiDebugMapper;
@Resource
private ApiFileResourceService apiFileResourceService;
public List<ApiDebugSimpleDTO> list(String protocol, String userId) {
return extApiDebugMapper.list(protocol, userId);
@ -54,10 +56,11 @@ public class ApiDebugService {
BeanUtils.copyBean(apiDebugDTO, apiDebug);
apiDebugDTO.setRequest(ApiDataUtils.parseObject(new String(apiDebugBlob.getRequest()), AbstractMsTestElement.class));
apiDebugDTO.setResponse(apiDebugDTO.getResponse());
apiDebugDTO.setFileIds(apiFileResourceService.getFileIdsByResourceId(id));
return apiDebugDTO;
}
public ApiDebug add(ApiDebugAddRequest request, String createUser) {
public ApiDebug add(ApiDebugAddRequest request, List<MultipartFile> files, String createUser) {
ProjectService.checkResourceExist(request.getProjectId());
ApiDebug apiDebug = new ApiDebug();
BeanUtils.copyBean(apiDebug, request);
@ -68,18 +71,27 @@ public class ApiDebugService {
apiDebug.setUpdateTime(System.currentTimeMillis());
apiDebug.setUpdateUser(apiDebug.getCreateUser());
apiDebugMapper.insert(apiDebug);
// todo 处理 body 文件
// todo 校验 moduleId
ApiDebugBlob apiDebugBlob = new ApiDebugBlob();
apiDebugBlob.setId(apiDebug.getId());
apiDebugBlob.setRequest(request.getRequest().getBytes());
apiDebugBlobMapper.insert(apiDebugBlob);
// 处理文件
String apiDebugDir = DefaultRepositoryDir.getApiDebugDir(request.getProjectId(), apiDebug.getId());
ApiFileResourceUpdateRequest resourceUpdateRequest = new ApiFileResourceUpdateRequest();
resourceUpdateRequest.setProjectId(apiDebug.getProjectId());
resourceUpdateRequest.setFileIds(request.getFileIds());
resourceUpdateRequest.setAddFileIds(request.getFileIds());
resourceUpdateRequest.setFolder(apiDebugDir);
resourceUpdateRequest.setResourceId(apiDebug.getId());
resourceUpdateRequest.setApiResourceType(ApiResourceType.API_DEBUG);
apiFileResourceService.addFileResource(resourceUpdateRequest, files);
return apiDebug;
}
public ApiDebug update(ApiDebugUpdateRequest request, String updateUser) {
public ApiDebug update(ApiDebugUpdateRequest request, List<MultipartFile> files, String updateUser) {
checkResourceExist(request.getId());
ApiDebug apiDebug = BeanUtils.copyBean(new ApiDebug(), request);
ApiDebug originApiDebug = apiDebugMapper.selectByPrimaryKey(request.getId());
@ -87,21 +99,34 @@ public class ApiDebugService {
apiDebug.setUpdateUser(updateUser);
apiDebug.setUpdateTime(System.currentTimeMillis());
apiDebugMapper.updateByPrimaryKeySelective(apiDebug);
// todo 处理 body 文件
// todo 校验 moduleId
ApiDebugBlob apiDebugBlob = new ApiDebugBlob();
apiDebugBlob.setId(request.getId());
apiDebugBlob.setRequest(request.getRequest().getBytes());
apiDebugBlobMapper.updateByPrimaryKeySelective(apiDebugBlob);
String apiDebugDir = DefaultRepositoryDir.getApiDebugDir(originApiDebug.getProjectId(), originApiDebug.getId());
// 处理文件
ApiFileResourceUpdateRequest resourceUpdateRequest = new ApiFileResourceUpdateRequest();
resourceUpdateRequest.setProjectId(originApiDebug.getProjectId());
resourceUpdateRequest.setFileIds(request.getFileIds());
resourceUpdateRequest.setAddFileIds(request.getAddFileIds());
resourceUpdateRequest.setFolder(apiDebugDir);
resourceUpdateRequest.setResourceId(apiDebug.getId());
resourceUpdateRequest.setApiResourceType(ApiResourceType.API_DEBUG);
apiFileResourceService.updateFileResource(resourceUpdateRequest, files);
return apiDebug;
}
public void delete(String id) {
ApiDebug apiDebug = apiDebugMapper.selectByPrimaryKey(id);
checkResourceExist(id);
apiDebugMapper.deleteByPrimaryKey(id);
apiDebugBlobMapper.deleteByPrimaryKey(id);
String apiDebugDir = DefaultRepositoryDir.getApiDebugDir(apiDebug.getProjectId(), apiDebug.getId());
apiFileResourceService.deleteByResourceId(apiDebugDir, id);
}
private void checkAddExist(ApiDebug apiDebug) {

View File

@ -1,29 +1,35 @@
package io.metersphere.api.controller;
import io.metersphere.api.controller.param.ApiDebugAddRequestDefinition;
import io.metersphere.api.controller.param.ApiDebugUpdateRequestDefinition;
import io.metersphere.api.domain.ApiDebug;
import io.metersphere.api.domain.ApiDebugBlob;
import io.metersphere.api.domain.ApiFileResource;
import io.metersphere.api.dto.debug.ApiDebugAddRequest;
import io.metersphere.api.dto.debug.ApiDebugDTO;
import io.metersphere.api.dto.debug.ApiDebugSimpleDTO;
import io.metersphere.api.dto.debug.ApiDebugUpdateRequest;
import io.metersphere.api.mapper.ApiDebugBlobMapper;
import io.metersphere.api.mapper.ApiDebugMapper;
import io.metersphere.api.service.ApiFileResourceService;
import io.metersphere.api.util.ApiDataUtils;
import io.metersphere.plugin.api.spi.AbstractMsTestElement;
import io.metersphere.sdk.constants.DefaultRepositoryDir;
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.system.base.BaseTest;
import io.metersphere.system.file.FileCenter;
import io.metersphere.system.file.FileRequest;
import io.metersphere.system.log.constants.OperationLogType;
import io.metersphere.system.uid.IDGenerator;
import jakarta.annotation.Resource;
import org.apache.commons.collections.CollectionUtils;
import org.junit.jupiter.api.*;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.web.servlet.MvcResult;
import java.io.File;
import java.util.List;
import static io.metersphere.api.controller.result.ApiResultCode.API_DEBUG_EXIST;
@ -45,6 +51,8 @@ public class ApiDebugControllerTests extends BaseTest {
private ApiDebugMapper apiDebugMapper;
@Resource
private ApiDebugBlobMapper apiDebugBlobMapper;
@Resource
private ApiFileResourceService apiFileResourceService;
private static ApiDebug addApiDebug;
private static ApiDebug anotherAddApiDebug;
@Override
@ -72,33 +80,42 @@ public class ApiDebugControllerTests extends BaseTest {
request.setProjectId(DEFAULT_PROJECT_ID);
MsHTTPElement msHttpElement = MsHTTPElementTest.getMsHttpElement();
request.setRequest(ApiDataUtils.toJSONString(msHttpElement));
MvcResult mvcResult = this.requestPostWithOkAndReturn(DEFAULT_ADD, request);
File bodyFile = new File(
this.getClass().getClassLoader().getResource("file/file_upload.JPG")
.getPath()
);
request.setFileIds(List.of(IDGenerator.nextStr()));
MvcResult mvcResult = this.requestMultipartWithOkAndReturn(DEFAULT_ADD, getDefaultMultiPartParam(List.of(bodyFile), request));
// 校验请求成功数据
ApiDebug resultData = getResultData(mvcResult, ApiDebug.class);
this.addApiDebug = assertUpdateApiDebug(request, msHttpElement, resultData.getId());
this.addApiDebug = assertUpdateApiDebug(request, msHttpElement, resultData.getId(), request.getFileIds());
// 再插入一条数据便于修改时重名校验
request.setName("test1");
mvcResult = this.requestPostWithOkAndReturn(DEFAULT_ADD, request);
request.setFileIds(null);
mvcResult = this.requestMultipartWithOkAndReturn(DEFAULT_ADD, getDefaultMultiPartParam(List.of(bodyFile), request));
resultData = getResultData(mvcResult, ApiDebug.class);
this.anotherAddApiDebug = assertUpdateApiDebug(request, msHttpElement, resultData.getId());
this.anotherAddApiDebug = assertUpdateApiDebug(request, msHttpElement, resultData.getId(), request.getFileIds());
// 增加覆盖率
apiFileResourceService.uploadFileResource(null, null, null);
// @@重名校验异常
assertErrorCode(this.requestPost(DEFAULT_ADD, request), API_DEBUG_EXIST);
assertErrorCode(this.requestMultipart(DEFAULT_ADD, getDefaultMultiPartParam(List.of(), request)), API_DEBUG_EXIST);
// 校验项目是否存在
request.setProjectId("111");
assertErrorCode(this.requestPost(DEFAULT_ADD, request), NOT_FOUND);
assertErrorCode(this.requestMultipart(DEFAULT_ADD, getDefaultMultiPartParam(List.of(), request)), NOT_FOUND);
// @@校验日志
checkLog(this.addApiDebug.getId(), OperationLogType.ADD);
// @@异常参数校验
createdGroupParamValidateTest(ApiDebugAddRequestDefinition.class, DEFAULT_ADD);
checkLog(this.addApiDebug.getId(), OperationLogType.ADD);
// @@校验权限
request.setProjectId(DEFAULT_PROJECT_ID);
requestPostPermissionTest(PermissionConstants.PROJECT_API_DEBUG_ADD, DEFAULT_ADD, request);
requestMultipartPermissionTest(PermissionConstants.PROJECT_API_DEBUG_ADD, DEFAULT_ADD, getDefaultMultiPartParam(List.of(), request));
}
private ApiDebug assertUpdateApiDebug(Object request, MsHTTPElement msHttpElement, String id) {
private ApiDebug assertUpdateApiDebug(Object request, MsHTTPElement msHttpElement, String id, List<String> fileIds) throws Exception {
ApiDebug apiDebug = apiDebugMapper.selectByPrimaryKey(id);
ApiDebugBlob apiDebugBlob = apiDebugBlobMapper.selectByPrimaryKey(id);
ApiDebug copyApiDebug = BeanUtils.copyBean(new ApiDebug(), apiDebug);
@ -106,6 +123,28 @@ public class ApiDebugControllerTests extends BaseTest {
Assertions.assertEquals(apiDebug, copyApiDebug);
ApiDataUtils.setResolver(MsHTTPElement.class);
Assertions.assertEquals(msHttpElement, ApiDataUtils.parseObject(new String(apiDebugBlob.getRequest()), AbstractMsTestElement.class));
if (fileIds != null) {
// 验证文件的关联关系以及是否存入对象存储
List<ApiFileResource> apiFileResources = apiFileResourceService.getByResourceId(id);
Assertions.assertEquals(apiFileResources.size(), fileIds.size());
String apiDebugDir = DefaultRepositoryDir.getApiDebugDir(apiDebug.getProjectId(), id);
FileRequest fileRequest = new FileRequest();
if (fileIds.size() > 0) {
for (ApiFileResource apiFileResource : apiFileResources) {
Assertions.assertEquals(apiFileResource.getProjectId(), apiDebug.getProjectId());
fileRequest.setFolder(apiDebugDir + "/" + apiFileResource.getFileId());
fileRequest.setFileName(apiFileResource.getFileName());
Assertions.assertNotNull(FileCenter.getDefaultRepository().getFile(fileRequest));
}
fileRequest.setFolder(apiDebugDir);
} else {
fileRequest.setFolder(apiDebugDir);
Assertions.assertTrue(CollectionUtils.isEmpty(FileCenter.getDefaultRepository().getFolderFileNames(fileRequest)));
}
}
return apiDebug;
}
@ -122,20 +161,49 @@ public class ApiDebugControllerTests extends BaseTest {
MsHTTPElement msHttpElement = MsHTTPElementTest.getMsHttpElement();
msHttpElement.setName("test1");
request.setRequest(ApiDataUtils.toJSONString(msHttpElement));
this.requestPostWithOk(DEFAULT_UPDATE, request);
// 不带文件的更新
this.requestMultipartWithOk(DEFAULT_UPDATE, getDefaultMultiPartParam(null, request));
// 校验请求成功数据
assertUpdateApiDebug(request, msHttpElement, request.getId());
assertUpdateApiDebug(request, msHttpElement, request.getId(), request.getFileIds());
File bodyFile = new File(
this.getClass().getClassLoader().getResource("file/file_upload.JPG")
.getPath()
);
// 带文件的更新
request.setAddFileIds(List.of(IDGenerator.nextStr()));
request.setFileIds(request.getAddFileIds());
this.requestMultipartWithOk(DEFAULT_UPDATE, getDefaultMultiPartParam(List.of(bodyFile), request));
// 校验请求成功数据
assertUpdateApiDebug(request, msHttpElement, request.getId(), request.getFileIds());
// 删除了上一次上传的文件重新上传一个文件
request.setAddFileIds(List.of(IDGenerator.nextStr()));
request.setFileIds(request.getAddFileIds());
this.requestMultipartWithOk(DEFAULT_UPDATE, getDefaultMultiPartParam(List.of(bodyFile), request));
assertUpdateApiDebug(request, msHttpElement, request.getId(), request.getFileIds());
// 已有一个文件再上传一个文件
request.setAddFileIds(List.of(IDGenerator.nextStr()));
List<String> fileIds = apiFileResourceService.getFileIdsByResourceId(request.getId());
fileIds.addAll(request.getAddFileIds());
request.setFileIds(fileIds);
this.requestMultipartWithOk(DEFAULT_UPDATE, getDefaultMultiPartParam(List.of(bodyFile), request));
assertUpdateApiDebug(request, msHttpElement, request.getId(), request.getFileIds());
// 校验请求成功数据
assertUpdateApiDebug(request, msHttpElement, request.getId(), request.getFileIds());
// @@重名校验异常
request.setModuleId("default");
assertErrorCode(this.requestPost(DEFAULT_UPDATE, request), API_DEBUG_EXIST);
assertErrorCode(this.requestMultipart(DEFAULT_UPDATE, getDefaultMultiPartParam(List.of(), request)), API_DEBUG_EXIST);
// @@校验日志
checkLog(request.getId(), OperationLogType.UPDATE);
// @@异常参数校验
updatedGroupParamValidateTest(ApiDebugUpdateRequestDefinition.class, DEFAULT_UPDATE);
// @@校验权限
requestPostPermissionTest(PermissionConstants.PROJECT_API_DEBUG_UPDATE, DEFAULT_UPDATE, request);
requestMultipartPermissionTest(PermissionConstants.PROJECT_API_DEBUG_UPDATE, DEFAULT_UPDATE, getDefaultMultiPartParam(List.of(), request));
}
@Test
@Order(3)
@ -168,6 +236,7 @@ public class ApiDebugControllerTests extends BaseTest {
ApiDebugDTO copyApiDebugDTO = BeanUtils.copyBean(new ApiDebugDTO(), apiDebugMapper.selectByPrimaryKey(addApiDebug.getId()));
ApiDebugBlob apiDebugBlob = apiDebugBlobMapper.selectByPrimaryKey(addApiDebug.getId());
copyApiDebugDTO.setRequest(ApiDataUtils.parseObject(new String(apiDebugBlob.getRequest()), AbstractMsTestElement.class));
copyApiDebugDTO.setFileIds(apiFileResourceService.getFileIdsByResourceId(addApiDebug.getId()));
Assertions.assertEquals(apiDebugDTO, copyApiDebugDTO);
// @@校验权限
requestGetPermissionTest(PermissionConstants.PROJECT_API_DEBUG_READ, DEFAULT_GET, apiDebugDTO.getId());
@ -181,6 +250,11 @@ public class ApiDebugControllerTests extends BaseTest {
// 校验请求成功数据
Assertions.assertNull(apiDebugMapper.selectByPrimaryKey(addApiDebug.getId()));
Assertions.assertNull(apiDebugBlobMapper.selectByPrimaryKey(addApiDebug.getId()));
List<ApiFileResource> apiFileResources = apiFileResourceService.getByResourceId(addApiDebug.getId());
Assertions.assertTrue(CollectionUtils.isEmpty(apiFileResources));
FileRequest fileRequest = new FileRequest();
fileRequest.setFolder(DefaultRepositoryDir.getApiDebugDir(addApiDebug.getProjectId(), addApiDebug.getId()));
Assertions.assertTrue(CollectionUtils.isEmpty(FileCenter.getDefaultRepository().getFolderFileNames(fileRequest)));
// @@校验日志
checkLog(addApiDebug.getId(), OperationLogType.DELETE);
// @@校验权限

View File

@ -39,6 +39,7 @@ import org.springframework.test.web.servlet.ResultActions;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
import org.springframework.test.web.servlet.request.MockMultipartHttpServletRequestBuilder;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.util.CollectionUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
@ -56,7 +57,7 @@ import java.util.function.Supplier;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
@SpringBootTest(webEnvironment= SpringBootTest.WebEnvironment.RANDOM_PORT)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureMockMvc
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public abstract class BaseTest {
@ -228,18 +229,29 @@ public abstract class BaseTest {
List list = value;
for (Object o : list) {
try {
MockMultipartFile multipartFile;
if (o instanceof File) {
File file = (File) o;
multipartFile = new MockMultipartFile(key, file.getName(),
MediaType.APPLICATION_OCTET_STREAM_VALUE, Files.readAllBytes(file.toPath()));
} else if (o instanceof MockMultipartFile) {
multipartFile = (MockMultipartFile) o;
} else {
multipartFile = new MockMultipartFile(key, null,
MediaType.APPLICATION_JSON_VALUE, o.toString().getBytes());
if (o == null) {
continue;
}
MockMultipartFile multipartFile;
if (o instanceof List) {
List listObject = ((List) o);
if (CollectionUtils.isEmpty(listObject)) {
continue;
}
if (listObject.get(0) instanceof File || listObject.get(0) instanceof MockMultipartFile) {
// 参数是多个文件时,设置多个文件
for (Object subObject : ((List) o)) {
multipartFile = getMockMultipartFile(key, subObject);
requestBuilder.file(multipartFile);
}
} else {
multipartFile = getMockMultipartFile(key, o);
requestBuilder.file(multipartFile);
}
} else {
multipartFile = getMockMultipartFile(key, o);
requestBuilder.file(multipartFile);
}
requestBuilder.file(multipartFile);
} catch (IOException e) {
throw new RuntimeException(e);
}
@ -249,6 +261,31 @@ public abstract class BaseTest {
return requestBuilder;
}
private static MockMultipartFile getMockMultipartFile(String key, Object value) throws IOException {
MockMultipartFile multipartFile;
if (value instanceof File) {
File file = (File) value;
multipartFile = new MockMultipartFile(key, file.getName(),
MediaType.APPLICATION_OCTET_STREAM_VALUE, Files.readAllBytes(file.toPath()));
} else if (value instanceof MockMultipartFile) {
multipartFile = (MockMultipartFile) value;
// 有些地方的参数 name 写的是文件名这里统一处理成参数名 key
multipartFile = new MockMultipartFile(key, multipartFile.getOriginalFilename(),
MediaType.APPLICATION_OCTET_STREAM_VALUE, multipartFile.getBytes());
} else {
multipartFile = new MockMultipartFile(key, key,
MediaType.APPLICATION_JSON_VALUE, value.toString().getBytes());
}
return multipartFile;
}
/**
* 获取默认的 MultiValue 参数
*
* @param param
* @param file
* @return
*/
protected MultiValueMap<String, Object> getDefaultMultiPartParam(Object param, File file) {
MultiValueMap<String, Object> paramMap = new LinkedMultiValueMap<>();
paramMap.add("file", file);
@ -256,6 +293,23 @@ public abstract class BaseTest {
return paramMap;
}
/**
* 获取默认的 MultiValue 参数
* 这里参数位置与上面的方法对调
* 以便于方法重载时可以 files 可以传 null
* 不会与上面方法混淆
*
* @param files
* @param param
* @return
*/
protected MultiValueMap<String, Object> getDefaultMultiPartParam(List<File> files, Object param) {
MultiValueMap<String, Object> paramMap = new LinkedMultiValueMap<>();
paramMap.add("files", files);
paramMap.add("request", JSON.toJSONString(param));
return paramMap;
}
protected <T> T getResultData(MvcResult mvcResult, Class<T> clazz) throws Exception {
Object data = parseResponse(mvcResult).get("data");
return JSON.parseObject(JSON.toJSONString(data), clazz);