feat(接口测试): 场景保存接口实现

This commit is contained in:
AgAngle 2024-01-18 18:18:27 +08:00 committed by Craftsman
parent 280723aee0
commit b5a5f6d899
46 changed files with 1606 additions and 187 deletions

View File

@ -31,7 +31,7 @@ public class ApiScenarioStep implements Serializable {
private Boolean enable; private Boolean enable;
@Schema(description = "资源id") @Schema(description = "资源id")
private Long resourceId; private String resourceId;
@Schema(description = "资源编号") @Schema(description = "资源编号")
private String resourceNum; private String resourceNum;
@ -49,7 +49,7 @@ public class ApiScenarioStep implements Serializable {
private String versionId; private String versionId;
@Schema(description = "引用/复制/自定义") @Schema(description = "引用/复制/自定义")
private String source; private String refType;
@Schema(description = "循环等组件基础数据") @Schema(description = "循环等组件基础数据")
private String config; private String config;
@ -62,13 +62,13 @@ public class ApiScenarioStep implements Serializable {
name("name", "name", "VARCHAR", true), name("name", "name", "VARCHAR", true),
sort("sort", "sort", "BIGINT", false), sort("sort", "sort", "BIGINT", false),
enable("enable", "enable", "BIT", true), enable("enable", "enable", "BIT", true),
resourceId("resource_id", "resourceId", "BIGINT", false), resourceId("resource_id", "resourceId", "VARCHAR", false),
resourceNum("resource_num", "resourceNum", "VARCHAR", false), resourceNum("resource_num", "resourceNum", "VARCHAR", false),
stepType("step_type", "stepType", "VARCHAR", false), stepType("step_type", "stepType", "VARCHAR", false),
projectId("project_id", "projectId", "VARCHAR", false), projectId("project_id", "projectId", "VARCHAR", false),
parentId("parent_id", "parentId", "VARCHAR", false), parentId("parent_id", "parentId", "VARCHAR", false),
versionId("version_id", "versionId", "VARCHAR", false), versionId("version_id", "versionId", "VARCHAR", false),
source("source", "source", "VARCHAR", true), refType("ref_type", "refType", "VARCHAR", false),
config("config", "config", "VARCHAR", false); config("config", "config", "VARCHAR", false);
private static final String BEGINNING_DELIMITER = "`"; private static final String BEGINNING_DELIMITER = "`";

View File

@ -15,6 +15,11 @@ public class ApiScenarioStepBlob implements Serializable {
@Size(min = 1, max = 50, message = "{api_scenario_step_blob.id.length_range}", groups = {Created.class, Updated.class}) @Size(min = 1, max = 50, message = "{api_scenario_step_blob.id.length_range}", groups = {Created.class, Updated.class})
private String id; private String id;
@Schema(description = "场景id", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{api_scenario_step_blob.scenario_id.not_blank}", groups = {Created.class})
@Size(min = 1, max = 50, message = "{api_scenario_step_blob.scenario_id.length_range}", groups = {Created.class, Updated.class})
private String scenarioId;
@Schema(description = "场景步骤内容") @Schema(description = "场景步骤内容")
private byte[] content; private byte[] content;
@ -22,6 +27,7 @@ public class ApiScenarioStepBlob implements Serializable {
public enum Column { public enum Column {
id("id", "id", "VARCHAR", false), id("id", "id", "VARCHAR", false),
scenarioId("scenario_id", "scenarioId", "VARCHAR", false),
content("content", "content", "LONGVARBINARY", false); content("content", "content", "LONGVARBINARY", false);
private static final String BEGINNING_DELIMITER = "`"; private static final String BEGINNING_DELIMITER = "`";

View File

@ -173,6 +173,76 @@ public class ApiScenarioStepBlobExample {
addCriterion("id not between", value1, value2, "id"); addCriterion("id not between", value1, value2, "id");
return (Criteria) this; return (Criteria) this;
} }
public Criteria andScenarioIdIsNull() {
addCriterion("scenario_id is null");
return (Criteria) this;
}
public Criteria andScenarioIdIsNotNull() {
addCriterion("scenario_id is not null");
return (Criteria) this;
}
public Criteria andScenarioIdEqualTo(String value) {
addCriterion("scenario_id =", value, "scenarioId");
return (Criteria) this;
}
public Criteria andScenarioIdNotEqualTo(String value) {
addCriterion("scenario_id <>", value, "scenarioId");
return (Criteria) this;
}
public Criteria andScenarioIdGreaterThan(String value) {
addCriterion("scenario_id >", value, "scenarioId");
return (Criteria) this;
}
public Criteria andScenarioIdGreaterThanOrEqualTo(String value) {
addCriterion("scenario_id >=", value, "scenarioId");
return (Criteria) this;
}
public Criteria andScenarioIdLessThan(String value) {
addCriterion("scenario_id <", value, "scenarioId");
return (Criteria) this;
}
public Criteria andScenarioIdLessThanOrEqualTo(String value) {
addCriterion("scenario_id <=", value, "scenarioId");
return (Criteria) this;
}
public Criteria andScenarioIdLike(String value) {
addCriterion("scenario_id like", value, "scenarioId");
return (Criteria) this;
}
public Criteria andScenarioIdNotLike(String value) {
addCriterion("scenario_id not like", value, "scenarioId");
return (Criteria) this;
}
public Criteria andScenarioIdIn(List<String> values) {
addCriterion("scenario_id in", values, "scenarioId");
return (Criteria) this;
}
public Criteria andScenarioIdNotIn(List<String> values) {
addCriterion("scenario_id not in", values, "scenarioId");
return (Criteria) this;
}
public Criteria andScenarioIdBetween(String value1, String value2) {
addCriterion("scenario_id between", value1, value2, "scenarioId");
return (Criteria) this;
}
public Criteria andScenarioIdNotBetween(String value1, String value2) {
addCriterion("scenario_id not between", value1, value2, "scenarioId");
return (Criteria) this;
}
} }
public static class Criteria extends GeneratedCriteria { public static class Criteria extends GeneratedCriteria {

View File

@ -444,52 +444,62 @@ public class ApiScenarioStepExample {
return (Criteria) this; return (Criteria) this;
} }
public Criteria andResourceIdEqualTo(Long value) { public Criteria andResourceIdEqualTo(String value) {
addCriterion("resource_id =", value, "resourceId"); addCriterion("resource_id =", value, "resourceId");
return (Criteria) this; return (Criteria) this;
} }
public Criteria andResourceIdNotEqualTo(Long value) { public Criteria andResourceIdNotEqualTo(String value) {
addCriterion("resource_id <>", value, "resourceId"); addCriterion("resource_id <>", value, "resourceId");
return (Criteria) this; return (Criteria) this;
} }
public Criteria andResourceIdGreaterThan(Long value) { public Criteria andResourceIdGreaterThan(String value) {
addCriterion("resource_id >", value, "resourceId"); addCriterion("resource_id >", value, "resourceId");
return (Criteria) this; return (Criteria) this;
} }
public Criteria andResourceIdGreaterThanOrEqualTo(Long value) { public Criteria andResourceIdGreaterThanOrEqualTo(String value) {
addCriterion("resource_id >=", value, "resourceId"); addCriterion("resource_id >=", value, "resourceId");
return (Criteria) this; return (Criteria) this;
} }
public Criteria andResourceIdLessThan(Long value) { public Criteria andResourceIdLessThan(String value) {
addCriterion("resource_id <", value, "resourceId"); addCriterion("resource_id <", value, "resourceId");
return (Criteria) this; return (Criteria) this;
} }
public Criteria andResourceIdLessThanOrEqualTo(Long value) { public Criteria andResourceIdLessThanOrEqualTo(String value) {
addCriterion("resource_id <=", value, "resourceId"); addCriterion("resource_id <=", value, "resourceId");
return (Criteria) this; return (Criteria) this;
} }
public Criteria andResourceIdIn(List<Long> values) { 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"); addCriterion("resource_id in", values, "resourceId");
return (Criteria) this; return (Criteria) this;
} }
public Criteria andResourceIdNotIn(List<Long> values) { public Criteria andResourceIdNotIn(List<String> values) {
addCriterion("resource_id not in", values, "resourceId"); addCriterion("resource_id not in", values, "resourceId");
return (Criteria) this; return (Criteria) this;
} }
public Criteria andResourceIdBetween(Long value1, Long value2) { public Criteria andResourceIdBetween(String value1, String value2) {
addCriterion("resource_id between", value1, value2, "resourceId"); addCriterion("resource_id between", value1, value2, "resourceId");
return (Criteria) this; return (Criteria) this;
} }
public Criteria andResourceIdNotBetween(Long value1, Long value2) { public Criteria andResourceIdNotBetween(String value1, String value2) {
addCriterion("resource_id not between", value1, value2, "resourceId"); addCriterion("resource_id not between", value1, value2, "resourceId");
return (Criteria) this; return (Criteria) this;
} }
@ -844,73 +854,73 @@ public class ApiScenarioStepExample {
return (Criteria) this; return (Criteria) this;
} }
public Criteria andSourceIsNull() { public Criteria andRefTypeIsNull() {
addCriterion("`source` is null"); addCriterion("ref_type is null");
return (Criteria) this; return (Criteria) this;
} }
public Criteria andSourceIsNotNull() { public Criteria andRefTypeIsNotNull() {
addCriterion("`source` is not null"); addCriterion("ref_type is not null");
return (Criteria) this; return (Criteria) this;
} }
public Criteria andSourceEqualTo(String value) { public Criteria andRefTypeEqualTo(String value) {
addCriterion("`source` =", value, "source"); addCriterion("ref_type =", value, "refType");
return (Criteria) this; return (Criteria) this;
} }
public Criteria andSourceNotEqualTo(String value) { public Criteria andRefTypeNotEqualTo(String value) {
addCriterion("`source` <>", value, "source"); addCriterion("ref_type <>", value, "refType");
return (Criteria) this; return (Criteria) this;
} }
public Criteria andSourceGreaterThan(String value) { public Criteria andRefTypeGreaterThan(String value) {
addCriterion("`source` >", value, "source"); addCriterion("ref_type >", value, "refType");
return (Criteria) this; return (Criteria) this;
} }
public Criteria andSourceGreaterThanOrEqualTo(String value) { public Criteria andRefTypeGreaterThanOrEqualTo(String value) {
addCriterion("`source` >=", value, "source"); addCriterion("ref_type >=", value, "refType");
return (Criteria) this; return (Criteria) this;
} }
public Criteria andSourceLessThan(String value) { public Criteria andRefTypeLessThan(String value) {
addCriterion("`source` <", value, "source"); addCriterion("ref_type <", value, "refType");
return (Criteria) this; return (Criteria) this;
} }
public Criteria andSourceLessThanOrEqualTo(String value) { public Criteria andRefTypeLessThanOrEqualTo(String value) {
addCriterion("`source` <=", value, "source"); addCriterion("ref_type <=", value, "refType");
return (Criteria) this; return (Criteria) this;
} }
public Criteria andSourceLike(String value) { public Criteria andRefTypeLike(String value) {
addCriterion("`source` like", value, "source"); addCriterion("ref_type like", value, "refType");
return (Criteria) this; return (Criteria) this;
} }
public Criteria andSourceNotLike(String value) { public Criteria andRefTypeNotLike(String value) {
addCriterion("`source` not like", value, "source"); addCriterion("ref_type not like", value, "refType");
return (Criteria) this; return (Criteria) this;
} }
public Criteria andSourceIn(List<String> values) { public Criteria andRefTypeIn(List<String> values) {
addCriterion("`source` in", values, "source"); addCriterion("ref_type in", values, "refType");
return (Criteria) this; return (Criteria) this;
} }
public Criteria andSourceNotIn(List<String> values) { public Criteria andRefTypeNotIn(List<String> values) {
addCriterion("`source` not in", values, "source"); addCriterion("ref_type not in", values, "refType");
return (Criteria) this; return (Criteria) this;
} }
public Criteria andSourceBetween(String value1, String value2) { public Criteria andRefTypeBetween(String value1, String value2) {
addCriterion("`source` between", value1, value2, "source"); addCriterion("ref_type between", value1, value2, "refType");
return (Criteria) this; return (Criteria) this;
} }
public Criteria andSourceNotBetween(String value1, String value2) { public Criteria andRefTypeNotBetween(String value1, String value2) {
addCriterion("`source` not between", value1, value2, "source"); addCriterion("ref_type not between", value1, value2, "refType");
return (Criteria) this; return (Criteria) this;
} }

View File

@ -32,6 +32,8 @@ public interface ApiScenarioStepBlobMapper {
int updateByPrimaryKeyWithBLOBs(ApiScenarioStepBlob record); int updateByPrimaryKeyWithBLOBs(ApiScenarioStepBlob record);
int updateByPrimaryKey(ApiScenarioStepBlob record);
int batchInsert(@Param("list") List<ApiScenarioStepBlob> list); int batchInsert(@Param("list") List<ApiScenarioStepBlob> list);
int batchInsertSelective(@Param("list") List<ApiScenarioStepBlob> list, @Param("selective") ApiScenarioStepBlob.Column ... selective); int batchInsertSelective(@Param("list") List<ApiScenarioStepBlob> list, @Param("selective") ApiScenarioStepBlob.Column ... selective);

View File

@ -3,6 +3,7 @@
<mapper namespace="io.metersphere.api.mapper.ApiScenarioStepBlobMapper"> <mapper namespace="io.metersphere.api.mapper.ApiScenarioStepBlobMapper">
<resultMap id="BaseResultMap" type="io.metersphere.api.domain.ApiScenarioStepBlob"> <resultMap id="BaseResultMap" type="io.metersphere.api.domain.ApiScenarioStepBlob">
<id column="id" jdbcType="VARCHAR" property="id" /> <id column="id" jdbcType="VARCHAR" property="id" />
<result column="scenario_id" jdbcType="VARCHAR" property="scenarioId" />
</resultMap> </resultMap>
<resultMap extends="BaseResultMap" id="ResultMapWithBLOBs" type="io.metersphere.api.domain.ApiScenarioStepBlob"> <resultMap extends="BaseResultMap" id="ResultMapWithBLOBs" type="io.metersphere.api.domain.ApiScenarioStepBlob">
<result column="content" jdbcType="LONGVARBINARY" property="content" /> <result column="content" jdbcType="LONGVARBINARY" property="content" />
@ -66,7 +67,7 @@
</where> </where>
</sql> </sql>
<sql id="Base_Column_List"> <sql id="Base_Column_List">
id id, scenario_id
</sql> </sql>
<sql id="Blob_Column_List"> <sql id="Blob_Column_List">
content content
@ -120,8 +121,10 @@
</if> </if>
</delete> </delete>
<insert id="insert" parameterType="io.metersphere.api.domain.ApiScenarioStepBlob"> <insert id="insert" parameterType="io.metersphere.api.domain.ApiScenarioStepBlob">
insert into api_scenario_step_blob (id, content) insert into api_scenario_step_blob (id, scenario_id, content
values (#{id,jdbcType=VARCHAR}, #{content,jdbcType=LONGVARBINARY}) )
values (#{id,jdbcType=VARCHAR}, #{scenarioId,jdbcType=VARCHAR}, #{content,jdbcType=LONGVARBINARY}
)
</insert> </insert>
<insert id="insertSelective" parameterType="io.metersphere.api.domain.ApiScenarioStepBlob"> <insert id="insertSelective" parameterType="io.metersphere.api.domain.ApiScenarioStepBlob">
insert into api_scenario_step_blob insert into api_scenario_step_blob
@ -129,6 +132,9 @@
<if test="id != null"> <if test="id != null">
id, id,
</if> </if>
<if test="scenarioId != null">
scenario_id,
</if>
<if test="content != null"> <if test="content != null">
content, content,
</if> </if>
@ -137,6 +143,9 @@
<if test="id != null"> <if test="id != null">
#{id,jdbcType=VARCHAR}, #{id,jdbcType=VARCHAR},
</if> </if>
<if test="scenarioId != null">
#{scenarioId,jdbcType=VARCHAR},
</if>
<if test="content != null"> <if test="content != null">
#{content,jdbcType=LONGVARBINARY}, #{content,jdbcType=LONGVARBINARY},
</if> </if>
@ -154,6 +163,9 @@
<if test="record.id != null"> <if test="record.id != null">
id = #{record.id,jdbcType=VARCHAR}, id = #{record.id,jdbcType=VARCHAR},
</if> </if>
<if test="record.scenarioId != null">
scenario_id = #{record.scenarioId,jdbcType=VARCHAR},
</if>
<if test="record.content != null"> <if test="record.content != null">
content = #{record.content,jdbcType=LONGVARBINARY}, content = #{record.content,jdbcType=LONGVARBINARY},
</if> </if>
@ -165,6 +177,7 @@
<update id="updateByExampleWithBLOBs" parameterType="map"> <update id="updateByExampleWithBLOBs" parameterType="map">
update api_scenario_step_blob update api_scenario_step_blob
set id = #{record.id,jdbcType=VARCHAR}, set id = #{record.id,jdbcType=VARCHAR},
scenario_id = #{record.scenarioId,jdbcType=VARCHAR},
content = #{record.content,jdbcType=LONGVARBINARY} content = #{record.content,jdbcType=LONGVARBINARY}
<if test="_parameter != null"> <if test="_parameter != null">
<include refid="Update_By_Example_Where_Clause" /> <include refid="Update_By_Example_Where_Clause" />
@ -172,7 +185,8 @@
</update> </update>
<update id="updateByExample" parameterType="map"> <update id="updateByExample" parameterType="map">
update api_scenario_step_blob update api_scenario_step_blob
set id = #{record.id,jdbcType=VARCHAR} set id = #{record.id,jdbcType=VARCHAR},
scenario_id = #{record.scenarioId,jdbcType=VARCHAR}
<if test="_parameter != null"> <if test="_parameter != null">
<include refid="Update_By_Example_Where_Clause" /> <include refid="Update_By_Example_Where_Clause" />
</if> </if>
@ -180,6 +194,9 @@
<update id="updateByPrimaryKeySelective" parameterType="io.metersphere.api.domain.ApiScenarioStepBlob"> <update id="updateByPrimaryKeySelective" parameterType="io.metersphere.api.domain.ApiScenarioStepBlob">
update api_scenario_step_blob update api_scenario_step_blob
<set> <set>
<if test="scenarioId != null">
scenario_id = #{scenarioId,jdbcType=VARCHAR},
</if>
<if test="content != null"> <if test="content != null">
content = #{content,jdbcType=LONGVARBINARY}, content = #{content,jdbcType=LONGVARBINARY},
</if> </if>
@ -188,15 +205,22 @@
</update> </update>
<update id="updateByPrimaryKeyWithBLOBs" parameterType="io.metersphere.api.domain.ApiScenarioStepBlob"> <update id="updateByPrimaryKeyWithBLOBs" parameterType="io.metersphere.api.domain.ApiScenarioStepBlob">
update api_scenario_step_blob update api_scenario_step_blob
set content = #{content,jdbcType=LONGVARBINARY} set scenario_id = #{scenarioId,jdbcType=VARCHAR},
content = #{content,jdbcType=LONGVARBINARY}
where id = #{id,jdbcType=VARCHAR}
</update>
<update id="updateByPrimaryKey" parameterType="io.metersphere.api.domain.ApiScenarioStepBlob">
update api_scenario_step_blob
set scenario_id = #{scenarioId,jdbcType=VARCHAR}
where id = #{id,jdbcType=VARCHAR} where id = #{id,jdbcType=VARCHAR}
</update> </update>
<insert id="batchInsert" parameterType="map"> <insert id="batchInsert" parameterType="map">
insert into api_scenario_step_blob insert into api_scenario_step_blob
(id, content) (id, scenario_id, content)
values values
<foreach collection="list" item="item" separator=","> <foreach collection="list" item="item" separator=",">
(#{item.id,jdbcType=VARCHAR}, #{item.content,jdbcType=LONGVARBINARY}) (#{item.id,jdbcType=VARCHAR}, #{item.scenarioId,jdbcType=VARCHAR}, #{item.content,jdbcType=LONGVARBINARY}
)
</foreach> </foreach>
</insert> </insert>
<insert id="batchInsertSelective" parameterType="map"> <insert id="batchInsertSelective" parameterType="map">
@ -212,6 +236,9 @@
<if test="'id'.toString() == column.value"> <if test="'id'.toString() == column.value">
#{item.id,jdbcType=VARCHAR} #{item.id,jdbcType=VARCHAR}
</if> </if>
<if test="'scenario_id'.toString() == column.value">
#{item.scenarioId,jdbcType=VARCHAR}
</if>
<if test="'content'.toString() == column.value"> <if test="'content'.toString() == column.value">
#{item.content,jdbcType=LONGVARBINARY} #{item.content,jdbcType=LONGVARBINARY}
</if> </if>

View File

@ -7,13 +7,13 @@
<result column="name" jdbcType="VARCHAR" property="name" /> <result column="name" jdbcType="VARCHAR" property="name" />
<result column="sort" jdbcType="BIGINT" property="sort" /> <result column="sort" jdbcType="BIGINT" property="sort" />
<result column="enable" jdbcType="BIT" property="enable" /> <result column="enable" jdbcType="BIT" property="enable" />
<result column="resource_id" jdbcType="BIGINT" property="resourceId" /> <result column="resource_id" jdbcType="VARCHAR" property="resourceId" />
<result column="resource_num" jdbcType="VARCHAR" property="resourceNum" /> <result column="resource_num" jdbcType="VARCHAR" property="resourceNum" />
<result column="step_type" jdbcType="VARCHAR" property="stepType" /> <result column="step_type" jdbcType="VARCHAR" property="stepType" />
<result column="project_id" jdbcType="VARCHAR" property="projectId" /> <result column="project_id" jdbcType="VARCHAR" property="projectId" />
<result column="parent_id" jdbcType="VARCHAR" property="parentId" /> <result column="parent_id" jdbcType="VARCHAR" property="parentId" />
<result column="version_id" jdbcType="VARCHAR" property="versionId" /> <result column="version_id" jdbcType="VARCHAR" property="versionId" />
<result column="source" jdbcType="VARCHAR" property="source" /> <result column="ref_type" jdbcType="VARCHAR" property="refType" />
<result column="config" jdbcType="VARCHAR" property="config" /> <result column="config" jdbcType="VARCHAR" property="config" />
</resultMap> </resultMap>
<sql id="Example_Where_Clause"> <sql id="Example_Where_Clause">
@ -76,7 +76,7 @@
</sql> </sql>
<sql id="Base_Column_List"> <sql id="Base_Column_List">
id, scenario_id, `name`, sort, `enable`, resource_id, resource_num, step_type, project_id, id, scenario_id, `name`, sort, `enable`, resource_id, resource_num, step_type, project_id,
parent_id, version_id, `source`, config parent_id, version_id, ref_type, config
</sql> </sql>
<select id="selectByExample" parameterType="io.metersphere.api.domain.ApiScenarioStepExample" resultMap="BaseResultMap"> <select id="selectByExample" parameterType="io.metersphere.api.domain.ApiScenarioStepExample" resultMap="BaseResultMap">
select select
@ -112,12 +112,12 @@
insert into api_scenario_step (id, scenario_id, `name`, insert into api_scenario_step (id, scenario_id, `name`,
sort, `enable`, resource_id, sort, `enable`, resource_id,
resource_num, step_type, project_id, resource_num, step_type, project_id,
parent_id, version_id, `source`, parent_id, version_id, ref_type,
config) config)
values (#{id,jdbcType=VARCHAR}, #{scenarioId,jdbcType=VARCHAR}, #{name,jdbcType=VARCHAR}, values (#{id,jdbcType=VARCHAR}, #{scenarioId,jdbcType=VARCHAR}, #{name,jdbcType=VARCHAR},
#{sort,jdbcType=BIGINT}, #{enable,jdbcType=BIT}, #{resourceId,jdbcType=BIGINT}, #{sort,jdbcType=BIGINT}, #{enable,jdbcType=BIT}, #{resourceId,jdbcType=VARCHAR},
#{resourceNum,jdbcType=VARCHAR}, #{stepType,jdbcType=VARCHAR}, #{projectId,jdbcType=VARCHAR}, #{resourceNum,jdbcType=VARCHAR}, #{stepType,jdbcType=VARCHAR}, #{projectId,jdbcType=VARCHAR},
#{parentId,jdbcType=VARCHAR}, #{versionId,jdbcType=VARCHAR}, #{source,jdbcType=VARCHAR}, #{parentId,jdbcType=VARCHAR}, #{versionId,jdbcType=VARCHAR}, #{refType,jdbcType=VARCHAR},
#{config,jdbcType=VARCHAR}) #{config,jdbcType=VARCHAR})
</insert> </insert>
<insert id="insertSelective" parameterType="io.metersphere.api.domain.ApiScenarioStep"> <insert id="insertSelective" parameterType="io.metersphere.api.domain.ApiScenarioStep">
@ -156,8 +156,8 @@
<if test="versionId != null"> <if test="versionId != null">
version_id, version_id,
</if> </if>
<if test="source != null"> <if test="refType != null">
`source`, ref_type,
</if> </if>
<if test="config != null"> <if test="config != null">
config, config,
@ -180,7 +180,7 @@
#{enable,jdbcType=BIT}, #{enable,jdbcType=BIT},
</if> </if>
<if test="resourceId != null"> <if test="resourceId != null">
#{resourceId,jdbcType=BIGINT}, #{resourceId,jdbcType=VARCHAR},
</if> </if>
<if test="resourceNum != null"> <if test="resourceNum != null">
#{resourceNum,jdbcType=VARCHAR}, #{resourceNum,jdbcType=VARCHAR},
@ -197,8 +197,8 @@
<if test="versionId != null"> <if test="versionId != null">
#{versionId,jdbcType=VARCHAR}, #{versionId,jdbcType=VARCHAR},
</if> </if>
<if test="source != null"> <if test="refType != null">
#{source,jdbcType=VARCHAR}, #{refType,jdbcType=VARCHAR},
</if> </if>
<if test="config != null"> <if test="config != null">
#{config,jdbcType=VARCHAR}, #{config,jdbcType=VARCHAR},
@ -230,7 +230,7 @@
`enable` = #{record.enable,jdbcType=BIT}, `enable` = #{record.enable,jdbcType=BIT},
</if> </if>
<if test="record.resourceId != null"> <if test="record.resourceId != null">
resource_id = #{record.resourceId,jdbcType=BIGINT}, resource_id = #{record.resourceId,jdbcType=VARCHAR},
</if> </if>
<if test="record.resourceNum != null"> <if test="record.resourceNum != null">
resource_num = #{record.resourceNum,jdbcType=VARCHAR}, resource_num = #{record.resourceNum,jdbcType=VARCHAR},
@ -247,8 +247,8 @@
<if test="record.versionId != null"> <if test="record.versionId != null">
version_id = #{record.versionId,jdbcType=VARCHAR}, version_id = #{record.versionId,jdbcType=VARCHAR},
</if> </if>
<if test="record.source != null"> <if test="record.refType != null">
`source` = #{record.source,jdbcType=VARCHAR}, ref_type = #{record.refType,jdbcType=VARCHAR},
</if> </if>
<if test="record.config != null"> <if test="record.config != null">
config = #{record.config,jdbcType=VARCHAR}, config = #{record.config,jdbcType=VARCHAR},
@ -265,13 +265,13 @@
`name` = #{record.name,jdbcType=VARCHAR}, `name` = #{record.name,jdbcType=VARCHAR},
sort = #{record.sort,jdbcType=BIGINT}, sort = #{record.sort,jdbcType=BIGINT},
`enable` = #{record.enable,jdbcType=BIT}, `enable` = #{record.enable,jdbcType=BIT},
resource_id = #{record.resourceId,jdbcType=BIGINT}, resource_id = #{record.resourceId,jdbcType=VARCHAR},
resource_num = #{record.resourceNum,jdbcType=VARCHAR}, resource_num = #{record.resourceNum,jdbcType=VARCHAR},
step_type = #{record.stepType,jdbcType=VARCHAR}, step_type = #{record.stepType,jdbcType=VARCHAR},
project_id = #{record.projectId,jdbcType=VARCHAR}, project_id = #{record.projectId,jdbcType=VARCHAR},
parent_id = #{record.parentId,jdbcType=VARCHAR}, parent_id = #{record.parentId,jdbcType=VARCHAR},
version_id = #{record.versionId,jdbcType=VARCHAR}, version_id = #{record.versionId,jdbcType=VARCHAR},
`source` = #{record.source,jdbcType=VARCHAR}, ref_type = #{record.refType,jdbcType=VARCHAR},
config = #{record.config,jdbcType=VARCHAR} config = #{record.config,jdbcType=VARCHAR}
<if test="_parameter != null"> <if test="_parameter != null">
<include refid="Update_By_Example_Where_Clause" /> <include refid="Update_By_Example_Where_Clause" />
@ -293,7 +293,7 @@
`enable` = #{enable,jdbcType=BIT}, `enable` = #{enable,jdbcType=BIT},
</if> </if>
<if test="resourceId != null"> <if test="resourceId != null">
resource_id = #{resourceId,jdbcType=BIGINT}, resource_id = #{resourceId,jdbcType=VARCHAR},
</if> </if>
<if test="resourceNum != null"> <if test="resourceNum != null">
resource_num = #{resourceNum,jdbcType=VARCHAR}, resource_num = #{resourceNum,jdbcType=VARCHAR},
@ -310,8 +310,8 @@
<if test="versionId != null"> <if test="versionId != null">
version_id = #{versionId,jdbcType=VARCHAR}, version_id = #{versionId,jdbcType=VARCHAR},
</if> </if>
<if test="source != null"> <if test="refType != null">
`source` = #{source,jdbcType=VARCHAR}, ref_type = #{refType,jdbcType=VARCHAR},
</if> </if>
<if test="config != null"> <if test="config != null">
config = #{config,jdbcType=VARCHAR}, config = #{config,jdbcType=VARCHAR},
@ -325,26 +325,26 @@
`name` = #{name,jdbcType=VARCHAR}, `name` = #{name,jdbcType=VARCHAR},
sort = #{sort,jdbcType=BIGINT}, sort = #{sort,jdbcType=BIGINT},
`enable` = #{enable,jdbcType=BIT}, `enable` = #{enable,jdbcType=BIT},
resource_id = #{resourceId,jdbcType=BIGINT}, resource_id = #{resourceId,jdbcType=VARCHAR},
resource_num = #{resourceNum,jdbcType=VARCHAR}, resource_num = #{resourceNum,jdbcType=VARCHAR},
step_type = #{stepType,jdbcType=VARCHAR}, step_type = #{stepType,jdbcType=VARCHAR},
project_id = #{projectId,jdbcType=VARCHAR}, project_id = #{projectId,jdbcType=VARCHAR},
parent_id = #{parentId,jdbcType=VARCHAR}, parent_id = #{parentId,jdbcType=VARCHAR},
version_id = #{versionId,jdbcType=VARCHAR}, version_id = #{versionId,jdbcType=VARCHAR},
`source` = #{source,jdbcType=VARCHAR}, ref_type = #{refType,jdbcType=VARCHAR},
config = #{config,jdbcType=VARCHAR} config = #{config,jdbcType=VARCHAR}
where id = #{id,jdbcType=VARCHAR} where id = #{id,jdbcType=VARCHAR}
</update> </update>
<insert id="batchInsert" parameterType="map"> <insert id="batchInsert" parameterType="map">
insert into api_scenario_step insert into api_scenario_step
(id, scenario_id, `name`, sort, `enable`, resource_id, resource_num, step_type, project_id, (id, scenario_id, `name`, sort, `enable`, resource_id, resource_num, step_type, project_id,
parent_id, version_id, `source`, config) parent_id, version_id, ref_type, config)
values values
<foreach collection="list" item="item" separator=","> <foreach collection="list" item="item" separator=",">
(#{item.id,jdbcType=VARCHAR}, #{item.scenarioId,jdbcType=VARCHAR}, #{item.name,jdbcType=VARCHAR}, (#{item.id,jdbcType=VARCHAR}, #{item.scenarioId,jdbcType=VARCHAR}, #{item.name,jdbcType=VARCHAR},
#{item.sort,jdbcType=BIGINT}, #{item.enable,jdbcType=BIT}, #{item.resourceId,jdbcType=BIGINT}, #{item.sort,jdbcType=BIGINT}, #{item.enable,jdbcType=BIT}, #{item.resourceId,jdbcType=VARCHAR},
#{item.resourceNum,jdbcType=VARCHAR}, #{item.stepType,jdbcType=VARCHAR}, #{item.projectId,jdbcType=VARCHAR}, #{item.resourceNum,jdbcType=VARCHAR}, #{item.stepType,jdbcType=VARCHAR}, #{item.projectId,jdbcType=VARCHAR},
#{item.parentId,jdbcType=VARCHAR}, #{item.versionId,jdbcType=VARCHAR}, #{item.source,jdbcType=VARCHAR}, #{item.parentId,jdbcType=VARCHAR}, #{item.versionId,jdbcType=VARCHAR}, #{item.refType,jdbcType=VARCHAR},
#{item.config,jdbcType=VARCHAR}) #{item.config,jdbcType=VARCHAR})
</foreach> </foreach>
</insert> </insert>
@ -374,7 +374,7 @@
#{item.enable,jdbcType=BIT} #{item.enable,jdbcType=BIT}
</if> </if>
<if test="'resource_id'.toString() == column.value"> <if test="'resource_id'.toString() == column.value">
#{item.resourceId,jdbcType=BIGINT} #{item.resourceId,jdbcType=VARCHAR}
</if> </if>
<if test="'resource_num'.toString() == column.value"> <if test="'resource_num'.toString() == column.value">
#{item.resourceNum,jdbcType=VARCHAR} #{item.resourceNum,jdbcType=VARCHAR}
@ -391,8 +391,8 @@
<if test="'version_id'.toString() == column.value"> <if test="'version_id'.toString() == column.value">
#{item.versionId,jdbcType=VARCHAR} #{item.versionId,jdbcType=VARCHAR}
</if> </if>
<if test="'source'.toString() == column.value"> <if test="'ref_type'.toString() == column.value">
#{item.source,jdbcType=VARCHAR} #{item.refType,jdbcType=VARCHAR}
</if> </if>
<if test="'config'.toString() == column.value"> <if test="'config'.toString() == column.value">
#{item.config,jdbcType=VARCHAR} #{item.config,jdbcType=VARCHAR}

View File

@ -275,13 +275,13 @@ CREATE TABLE IF NOT EXISTS api_scenario_step(
`name` VARCHAR(255) COMMENT '步骤名称' , `name` VARCHAR(255) COMMENT '步骤名称' ,
`sort` BIGINT NOT NULL COMMENT '序号' , `sort` BIGINT NOT NULL COMMENT '序号' ,
`enable` BIT(1) NOT NULL DEFAULT 1 COMMENT '启用/禁用' , `enable` BIT(1) NOT NULL DEFAULT 1 COMMENT '启用/禁用' ,
`resource_id` BIGINT COMMENT '资源id' , `resource_id` VARCHAR(50) COMMENT '资源id' ,
`resource_num` VARCHAR(50) COMMENT '资源编号' , `resource_num` VARCHAR(50) COMMENT '资源编号' ,
`step_type` VARCHAR(50) COMMENT '步骤类型/API/CASE等' , `step_type` VARCHAR(50) COMMENT '步骤类型/API/CASE等' ,
`project_id` VARCHAR(50) COMMENT '项目fk' , `project_id` VARCHAR(50) COMMENT '项目fk' ,
`parent_id` VARCHAR(50) DEFAULT 'NONE' COMMENT '父级fk' , `parent_id` VARCHAR(50) DEFAULT 'NONE' COMMENT '父级fk' ,
`version_id` VARCHAR(50) COMMENT '版本号' , `version_id` VARCHAR(50) COMMENT '版本号' ,
`source` VARCHAR(10) COMMENT '引用/复制/自定义' , `ref_type` VARCHAR(10) COMMENT '引用/复制/自定义' ,
`config` VARCHAR(500) COMMENT '循环等组件基础数据' , `config` VARCHAR(500) COMMENT '循环等组件基础数据' ,
PRIMARY KEY (id) PRIMARY KEY (id)
) ENGINE = InnoDB ) ENGINE = InnoDB
@ -296,12 +296,15 @@ CREATE INDEX idx_resource_num ON api_scenario_step(resource_num);
CREATE TABLE IF NOT EXISTS api_scenario_step_blob( CREATE TABLE IF NOT EXISTS api_scenario_step_blob(
`id` VARCHAR(50) NOT NULL COMMENT '场景步骤id' , `id` VARCHAR(50) NOT NULL COMMENT '场景步骤id' ,
`scenario_id` VARCHAR(50) NOT NULL COMMENT '场景id' ,
`content` LONGBLOB COMMENT '场景步骤内容' , `content` LONGBLOB COMMENT '场景步骤内容' ,
PRIMARY KEY (id) PRIMARY KEY (id)
) ENGINE = InnoDB ) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4 DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_general_ci COMMENT = '场景步骤内容'; COLLATE = utf8mb4_general_ci COMMENT = '场景步骤内容';
CREATE INDEX idx_scenario_id ON api_scenario_step_blob(scenario_id);
CREATE TABLE IF NOT EXISTS api_scenario_follower( CREATE TABLE IF NOT EXISTS api_scenario_follower(
`api_scenario_id` VARCHAR(50) NOT NULL COMMENT '场景fk' , `api_scenario_id` VARCHAR(50) NOT NULL COMMENT '场景fk' ,

View File

@ -54,6 +54,7 @@ public class DefaultRepositoryDir {
* project/{projectId}/api-debug/{apiDebugId} * project/{projectId}/api-debug/{apiDebugId}
*/ */
private static final String PROJECT_API_DEBUG_DIR = PROJECT_DIR + "/api-debug/%s"; private static final String PROJECT_API_DEBUG_DIR = PROJECT_DIR + "/api-debug/%s";
private static final String PROJECT_API_SCENARIO_DIR = PROJECT_DIR + "/api-scenario/%s";
private static final String PROJECT_BUG_DIR = PROJECT_DIR + "/bug/%s"; private static final String PROJECT_BUG_DIR = PROJECT_DIR + "/bug/%s";
/** /**
@ -112,4 +113,8 @@ public class DefaultRepositoryDir {
public static String getSystemTempDir() { public static String getSystemTempDir() {
return SYSTEM_TEMP_DIR; return SYSTEM_TEMP_DIR;
} }
public static String getApiScenarioDir(String projectId, String apiScenarioId) {
return String.format(PROJECT_API_SCENARIO_DIR, projectId, apiScenarioId);
}
} }

View File

@ -14,6 +14,7 @@ public class FileAssociationSourceUtil {
public static final String SOURCE_TYPE_BUG = "BUG"; public static final String SOURCE_TYPE_BUG = "BUG";
public static final String SOURCE_TYPE_FUNCTIONAL_CASE = "FUNCTIONAL_CASE"; public static final String SOURCE_TYPE_FUNCTIONAL_CASE = "FUNCTIONAL_CASE";
public static final String SOURCE_TYPE_API_DEBUG = "API_DEBUG"; public static final String SOURCE_TYPE_API_DEBUG = "API_DEBUG";
public static final String SOURCE_TYPE_API_SCENARIO= "API_SCENARIO";
public static final String SOURCE_TYPE_API_TEST_CASE = "API_TEST_CASE"; public static final String SOURCE_TYPE_API_TEST_CASE = "API_TEST_CASE";
public static final String SOURCE_TYPE_API_DEFINITION = "API_DEFINITION"; public static final String SOURCE_TYPE_API_DEFINITION = "API_DEFINITION";
public static final String SOURCE_TYPE_API_DEFINITION_MOCK = "API_DEFINITION_MOCK"; public static final String SOURCE_TYPE_API_DEFINITION_MOCK = "API_DEFINITION_MOCK";
@ -23,6 +24,7 @@ public class FileAssociationSourceUtil {
QUERY_SQL.put(SOURCE_TYPE_BUG, "SELECT id AS sourceId,title AS sourceName FROM bug"); QUERY_SQL.put(SOURCE_TYPE_BUG, "SELECT id AS sourceId,title AS sourceName FROM bug");
QUERY_SQL.put(SOURCE_TYPE_FUNCTIONAL_CASE, "SELECT id AS sourceId,name AS sourceName FROM functional_case"); QUERY_SQL.put(SOURCE_TYPE_FUNCTIONAL_CASE, "SELECT id AS sourceId,name AS sourceName FROM functional_case");
QUERY_SQL.put(SOURCE_TYPE_API_DEBUG, "SELECT id AS sourceId,name AS sourceName FROM api_debug"); QUERY_SQL.put(SOURCE_TYPE_API_DEBUG, "SELECT id AS sourceId,name AS sourceName FROM api_debug");
QUERY_SQL.put(SOURCE_TYPE_API_SCENARIO, "SELECT id AS sourceId,name AS sourceName FROM api_scenario");
QUERY_SQL.put(SOURCE_TYPE_API_TEST_CASE, "SELECT id AS sourceId,name AS sourceName FROM api_test_case"); QUERY_SQL.put(SOURCE_TYPE_API_TEST_CASE, "SELECT id AS sourceId,name AS sourceName FROM api_test_case");
QUERY_SQL.put(SOURCE_TYPE_API_DEFINITION, "SELECT id AS sourceId,name AS sourceName FROM api_definition"); QUERY_SQL.put(SOURCE_TYPE_API_DEFINITION, "SELECT id AS sourceId,name AS sourceName FROM api_definition");
QUERY_SQL.put(SOURCE_TYPE_API_DEFINITION_MOCK, "SELECT id AS sourceId,name AS sourceName FROM api_definition_mock"); QUERY_SQL.put(SOURCE_TYPE_API_DEFINITION_MOCK, "SELECT id AS sourceId,name AS sourceName FROM api_definition_mock");

View File

@ -21,6 +21,7 @@ api_definition_template.create_user.not_blank=创建人不能为空
api_definition_template.project_id.length_range=项目ID长度必须在1-50之间 api_definition_template.project_id.length_range=项目ID长度必须在1-50之间
api_definition_template.project_id.not_blank=项目ID不能为空 api_definition_template.project_id.not_blank=项目ID不能为空
#moduleApiScenario #moduleApiScenario
permission.system_api_scenario.name=场景
api_scenario.id.not_blank=不能为空 api_scenario.id.not_blank=不能为空
api_scenario.name.length_range=场景名称长度必须在1-200之间 api_scenario.name.length_range=场景名称长度必须在1-200之间
api_scenario.name.not_blank=场景名称不能为空 api_scenario.name.not_blank=场景名称不能为空
@ -314,5 +315,6 @@ api_definition_mock_exist=接口 MOCK 已存在
execute_resource_pool_not_config_error=请在【项目管理-应用管理-接口测试】中选择资源池 execute_resource_pool_not_config_error=请在【项目管理-应用管理-接口测试】中选择资源池
resource_pool_execute_error=资源池调用失败 resource_pool_execute_error=资源池调用失败
api_swagger_url_error=Swagger url无法连通 api_swagger_url_error=Swagger url无法连通
api_scenario_exist=场景已存在
schedule_not_exist=定时任务不存在 schedule_not_exist=定时任务不存在

View File

@ -21,6 +21,7 @@ api_definition_template.create_user.not_blank=Creator cannot be empty
api_definition_template.project_id.length_range=Item fk length must be between 1-50 api_definition_template.project_id.length_range=Item fk length must be between 1-50
api_definition_template.project_id.not_blank=Item fk cannot be empty api_definition_template.project_id.not_blank=Item fk cannot be empty
#moduleApiScenario #moduleApiScenario
permission.system_api_scenario.name=Scenario
api_scenario.id.not_blank=Can not be empty api_scenario.id.not_blank=Can not be empty
api_scenario.name.length_range=The scene name length must be between 1-200 api_scenario.name.length_range=The scene name length must be between 1-200
api_scenario.name.not_blank=Scene name cannot be empty api_scenario.name.not_blank=Scene name cannot be empty
@ -48,6 +49,7 @@ api_scenario_step.id.length_range=Step ID length must be between 1-50
api_scenario_step.scenario_id.not_blank=Scene ID cannot be empty api_scenario_step.scenario_id.not_blank=Scene ID cannot be empty
api_scenario_step.scenario_id.length_range=Scene ID length must be between 1-50 api_scenario_step.scenario_id.length_range=Scene ID length must be between 1-50
api_scenario_step.sort.not_blank=SORT cannot be empty api_scenario_step.sort.not_blank=SORT cannot be empty
#moduleApiTestCaseFollow #moduleApiTestCaseFollow
api_test_case_follow.case_id.length_range=Use case fk length must be between 1-50 api_test_case_follow.case_id.length_range=Use case fk length must be between 1-50
api_test_case_follow.case_id.not_blank=Use case fk cannot be empty api_test_case_follow.case_id.not_blank=Use case fk cannot be empty
@ -318,4 +320,5 @@ api_definition_mock_exist=The API MOCK already exists
execute_resource_pool_not_config_error=Select a resource pool in 【Project Management - Application Management - Interface Testing】 execute_resource_pool_not_config_error=Select a resource pool in 【Project Management - Application Management - Interface Testing】
resource_pool_execute_error=The resource pool call failed resource_pool_execute_error=The resource pool call failed
api_swagger_url_error=Swagger url unable to connect api_swagger_url_error=Swagger url unable to connect
api_scenario_exist=The scenario already exists
schedule_not_exist=The scheduled task does not exist schedule_not_exist=The scheduled task does not exist

View File

@ -21,6 +21,7 @@ api_definition_template.create_user.not_blank=创建人不能为空
api_definition_template.project_id.length_range=项目ID长度必须在1-50之间 api_definition_template.project_id.length_range=项目ID长度必须在1-50之间
api_definition_template.project_id.not_blank=项目ID不能为空 api_definition_template.project_id.not_blank=项目ID不能为空
#moduleApiScenario #moduleApiScenario
permission.system_api_scenario.name=场景
api_scenario.id.not_blank=不能为空 api_scenario.id.not_blank=不能为空
api_scenario.name.length_range=场景名称长度必须在1-200之间 api_scenario.name.length_range=场景名称长度必须在1-200之间
api_scenario.name.not_blank=场景名称不能为空 api_scenario.name.not_blank=场景名称不能为空
@ -318,4 +319,5 @@ api_definition_mock_exist=接口 MOCK 已存在
execute_resource_pool_not_config_error=请在【项目管理-应用管理-接口测试】中选择资源池 execute_resource_pool_not_config_error=请在【项目管理-应用管理-接口测试】中选择资源池
resource_pool_execute_error=资源池调用失败 resource_pool_execute_error=资源池调用失败
api_swagger_url_error=Swagger url无法连通 api_swagger_url_error=Swagger url无法连通
api_scenario_exist=场景已存在
schedule_not_exist=定时任务不存在 schedule_not_exist=定时任务不存在

View File

@ -21,6 +21,7 @@ api_definition_template.create_user.not_blank=創建人不能為空
api_definition_template.project_id.length_range=項目ID長度必須在1-50之間 api_definition_template.project_id.length_range=項目ID長度必須在1-50之間
api_definition_template.project_id.not_blank=項目ID不能為空 api_definition_template.project_id.not_blank=項目ID不能為空
#moduleApiScenario #moduleApiScenario
permission.system_api_scenario.name=场景
api_scenario.id.not_blank=不能為空 api_scenario.id.not_blank=不能為空
api_scenario.name.length_range=場景名稱長度必須在1-200之間 api_scenario.name.length_range=場景名稱長度必須在1-200之間
api_scenario.name.not_blank=場景名稱不能為空 api_scenario.name.not_blank=場景名稱不能為空
@ -318,4 +319,5 @@ api_definition_mock_exist=接口 MOCK 已存在
execute_resource_pool_not_config_error=請在【項目管理-應用管理-接口測試】中選擇資源池 execute_resource_pool_not_config_error=請在【項目管理-應用管理-接口測試】中選擇資源池
resource_pool_execute_error=資源池調用失敗 resource_pool_execute_error=資源池調用失敗
api_swagger_url_error=Swagger url無法調解 api_swagger_url_error=Swagger url無法調解
api_scenario_exist=場景已存在
schedule_not_exist=定時任務不存在 schedule_not_exist=定時任務不存在

View File

@ -0,0 +1,20 @@
package io.metersphere.api.constants;
/**
* @Author: jianxing
* @CreateTime: 2024-01-11 17:31
*/
public enum ApiScenarioStatus {
/**
* 进行中
*/
UNDERWAY,
/**
* 已完成
*/
COMPLETED,
/**
* 已废弃
*/
DEPRECATED
}

View File

@ -0,0 +1,25 @@
package io.metersphere.api.constants;
/**
* @Author: jianxing
* @CreateTime: 2024-01-10 11:24
*/
public enum ApiScenarioStepRefType {
/**
* 在场景中直接创建的步骤
* 例如 自定义请求逻辑控制器
*/
DIRECT,
/**
* 引用
*/
REF,
/**
* 步骤引用
*/
STEP_REF,
/**
* 复制
*/
COPY
}

View File

@ -0,0 +1,22 @@
package io.metersphere.api.constants;
/**
* 步骤的类型
* @Author: jianxing
* @CreateTime: 2024-01-10 11:24
*/
public enum ApiScenarioStepType {
/**
* 接口定义
*/
API,
/**
* 接口用例
*/
API_CASE,
/**
* 场景
*/
API_SCENARIO
// todo 逻辑控制器等
}

View File

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

View File

@ -2,11 +2,13 @@ package io.metersphere.api.controller.scenario;
import com.github.pagehelper.Page; import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper; import com.github.pagehelper.PageHelper;
import io.metersphere.api.dto.scenario.ApiScenarioBatchEditRequest; import io.metersphere.api.domain.ApiScenario;
import io.metersphere.api.dto.scenario.ApiScenarioDTO; import io.metersphere.api.dto.scenario.*;
import io.metersphere.api.dto.scenario.ApiScenarioPageRequest; import io.metersphere.api.service.scenario.ApiScenarioLogService;
import io.metersphere.api.service.scenario.ApiScenarioService; import io.metersphere.api.service.scenario.ApiScenarioService;
import io.metersphere.sdk.constants.PermissionConstants; import io.metersphere.sdk.constants.PermissionConstants;
import io.metersphere.system.log.annotation.Log;
import io.metersphere.system.log.constants.OperationLogType;
import io.metersphere.system.security.CheckOwner; import io.metersphere.system.security.CheckOwner;
import io.metersphere.system.utils.PageUtils; import io.metersphere.system.utils.PageUtils;
import io.metersphere.system.utils.Pager; import io.metersphere.system.utils.Pager;
@ -15,9 +17,11 @@ import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authz.annotation.Logical;
import org.apache.shiro.authz.annotation.RequiresPermissions; import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.util.List; import java.util.List;
@ -65,4 +69,42 @@ public class ApiScenarioController {
apiScenarioService.follow(id, SessionUtils.getUserId()); apiScenarioService.follow(id, SessionUtils.getUserId());
} }
@PostMapping("/add")
@Operation(summary = "创建场景")
@RequiresPermissions(PermissionConstants.PROJECT_API_SCENARIO_ADD)
@Log(type = OperationLogType.ADD, expression = "#msClass.addLog(#request)", msClass = ApiScenarioLogService.class)
public ApiScenario add(@Validated @RequestBody ApiScenarioAddRequest request) {
return apiScenarioService.add(request, SessionUtils.getUserId());
}
@PostMapping("/upload/temp/file")
@Operation(summary = "上传场景所需的文件资源并返回文件ID")
@RequiresPermissions(logical = Logical.OR, value = {PermissionConstants.PROJECT_API_SCENARIO_ADD, PermissionConstants.PROJECT_API_SCENARIO_UPDATE})
public String uploadTempFile(@RequestParam("file") MultipartFile file) {
return apiScenarioService.uploadTempFile(file);
}
@PostMapping("/update")
@Operation(summary = "更新场景")
@RequiresPermissions(PermissionConstants.PROJECT_API_SCENARIO_UPDATE)
@Log(type = OperationLogType.UPDATE, expression = "#msClass.updateLog(#request)", msClass = ApiScenarioLogService.class)
public ApiScenario update(@Validated @RequestBody ApiScenarioUpdateRequest request) {
return apiScenarioService.update(request, SessionUtils.getUserId());
}
@GetMapping("/delete/{id}")
@Operation(summary = "删除场景")
@RequiresPermissions(PermissionConstants.PROJECT_API_SCENARIO_DELETE)
@Log(type = OperationLogType.DELETE, expression = "#msClass.deleteLog(#id)", msClass = ApiScenarioLogService.class)
public void delete(@PathVariable String id) {
apiScenarioService.delete(id);
}
@GetMapping("/delete-to-gc/{id}")
@Operation(summary = "删除场景到回收站")
@RequiresPermissions(PermissionConstants.PROJECT_API_SCENARIO_DELETE)
@Log(type = OperationLogType.DELETE, expression = "#msClass.deleteLog(#id)", msClass = ApiScenarioLogService.class)
public void deleteToGc(@PathVariable String id) {
apiScenarioService.deleteToGc(id);
}
} }

View File

@ -1,9 +1,9 @@
package io.metersphere.api.dto.request.http.body; package io.metersphere.api.dto;
import lombok.Data; import lombok.Data;
@Data @Data
public class BodyFile { public class ApiFile {
/** /**
* 记录文件的ID防止重名 * 记录文件的ID防止重名
* 生成脚本时通过 fileId + value(文件名) 获取文件路径 * 生成脚本时通过 fileId + value(文件名) 获取文件路径

View File

@ -1,5 +1,6 @@
package io.metersphere.api.dto.request.http.body; package io.metersphere.api.dto.request.http.body;
import io.metersphere.api.dto.ApiFile;
import lombok.Data; import lombok.Data;
/** /**
@ -8,6 +9,6 @@ import lombok.Data;
*/ */
@Data @Data
public class BinaryBody { public class BinaryBody {
private BodyFile bodyFile; private ApiFile bodyFile;
private String description; private String description;
} }

View File

@ -1,5 +1,6 @@
package io.metersphere.api.dto.request.http.body; package io.metersphere.api.dto.request.http.body;
import io.metersphere.api.dto.ApiFile;
import lombok.Data; import lombok.Data;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
@ -12,7 +13,7 @@ import java.util.List;
@Data @Data
public class FormDataKV extends WWWFormKV { public class FormDataKV extends WWWFormKV {
private List<BodyFile> files; private List<ApiFile> files;
public boolean isFile() { public boolean isFile() {
return StringUtils.equalsIgnoreCase(getParamType(), WWWFormParamType.FILE.name()); return StringUtils.equalsIgnoreCase(getParamType(), WWWFormParamType.FILE.name());

View File

@ -0,0 +1,82 @@
package io.metersphere.api.dto.scenario;
import io.metersphere.api.constants.ApiScenarioStatus;
import io.metersphere.system.valid.EnumValue;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.Data;
import java.util.List;
import java.util.Map;
/**
* @Author: jianxing
* @CreateTime: 2024-01-10 11:24
*/
@Data
public class ApiScenarioAddRequest {
@Schema(description = "场景名称", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{api_scenario.name.not_blank}")
@Size(min = 1, max = 255, message = "{api_scenario.name.length_range}")
private String name;
@Schema(description = "场景级别/P0/P1等", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{api_scenario.priority.not_blank}")
@Size(min = 1, max = 10, message = "{api_scenario.priority.length_range}")
private String priority;
@Schema(description = "场景状态/未规划/已完成 等", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{api_scenario.status.not_blank}")
@Size(min = 1, max = 20, message = "{api_scenario.status.length_range}")
@EnumValue(enumClass = ApiScenarioStatus.class)
private String status;
@Schema(description = "项目fk", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{api_scenario.project_id.not_blank}")
@Size(min = 1, max = 50, message = "{api_scenario.project_id.length_range}")
private String projectId;
@Schema(description = "场景模块fk")
@NotBlank(message = "{api_debug.module_id.not_blank}")
private String moduleId;
@Schema(description = "描述信息")
private String description;
@Schema(description = "标签")
private List<String> tags;
@Schema(description = "是否为环境组", requiredMode = Schema.RequiredMode.REQUIRED)
private Boolean grouped;
@Schema(description = "环境或者环境组ID")
private String environmentId;
@Schema(description = "场景的通用配置")
private ScenarioConfig scenarioConfig;
@Schema(description = "步骤集合")
private List<ApiScenarioStepRequest> steps;
/**
* 步骤详情
* key 为步骤ID
* 为详情
*/
@Schema(description = "步骤详情")
private Map<String, Object> stepDetails;
/**
* 新上传的文件ID
* 创建时先按ID创建目录再把文件放入目录
*/
@Schema(description = "新上传的文件ID")
private List<String> uploadFileIds;
/**
* 新关联的文件ID
*/
@Schema(description = "关联文件ID")
private List<String> linkFileIds;
}

View File

@ -0,0 +1,14 @@
package io.metersphere.api.dto.scenario;
import io.metersphere.api.domain.ApiScenario;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
public class ApiScenarioSimpleDTO extends ApiScenario {
@Schema(description = "更新人名称")
private String updateUserName;
@Schema(description = "创建人名称")
private String createUserName;
}

View File

@ -0,0 +1,63 @@
package io.metersphere.api.dto.scenario;
import io.metersphere.api.constants.ApiScenarioStepType;
import io.metersphere.system.valid.EnumValue;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.Data;
import java.util.List;
import java.util.Map;
/**
* @Author: jianxing
* @CreateTime: 2024-01-10 11:24
*/
@Data
public class ApiScenarioStepRequest {
@Schema(description = "步骤id", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{api_scenario_step.id.not_blank}")
@Size(max = 50, message = "{api_scenario_step.id.length_range}")
private String id;
@Schema(description = "步骤名称")
private String name;
@Schema(description = "启用/禁用")
private Boolean enable = true;
@Schema(description = "资源id")
private String resourceId;
@Schema(description = "资源编号")
private String resourceNum;
/**
* @see ApiScenarioStepType
*/
@Schema(description = "步骤类型/API/CASE等")
@NotBlank
@EnumValue(enumClass = ApiScenarioStepType.class)
private String stepType;
@Schema(description = "项目fk")
private String projectId;
@Schema(description = "版本号")
private String versionId;
/**
* 引用模式默认完全引用
* - 完全引用步骤状态不可调整
* - 步骤引用步骤状态可调整
*/
@Schema(description = "引用/复制/自定义")
private String refType;
@Schema(description = "循环等组件基础数据")
private Map<Object, Object> config;
@Schema(description = "子步骤")
private List<ApiScenarioStepRequest> children;
}

View File

@ -0,0 +1,90 @@
package io.metersphere.api.dto.scenario;
import io.metersphere.api.constants.ApiScenarioStatus;
import io.metersphere.system.valid.EnumValue;
import io.metersphere.validation.groups.Created;
import io.metersphere.validation.groups.Updated;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.Data;
import java.util.List;
import java.util.Map;
/**
* @Author: jianxing
* @CreateTime: 2024-01-10 11:24
*/
@Data
public class ApiScenarioUpdateRequest {
@Schema(description = "id", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{api_scenario.id.not_blank}", groups = {Updated.class})
@Size(max = 50, message = "{api_scenario.id.length_range}", groups = {Created.class, Updated.class})
private String id;
@Schema(description = "场景名称", requiredMode = Schema.RequiredMode.REQUIRED)
@Size(max = 255, message = "{api_scenario.name.length_range}")
private String name;
@Schema(description = "场景级别/P0/P1等", requiredMode = Schema.RequiredMode.REQUIRED)
@Size(max = 10, message = "{api_scenario.priority.length_range}")
private String priority;
@Schema(description = "场景状态/未规划/已完成 等", requiredMode = Schema.RequiredMode.REQUIRED)
@Size(max = 20, message = "{api_scenario.status.length_range}")
@EnumValue(enumClass = ApiScenarioStatus.class)
private String status;
@Schema(description = "场景模块fk")
private String moduleId;
@Schema(description = "描述信息")
private String description;
@Schema(description = "标签")
private List<String> tags;
@Schema(description = "是否为环境组", requiredMode = Schema.RequiredMode.REQUIRED)
private Boolean grouped;
@Schema(description = "环境或者环境组ID")
private String environmentId;
@Schema(description = "场景的通用配置")
private ScenarioConfig scenarioConfig;
@Schema(description = "步骤集合")
private List<ApiScenarioStepRequest> steps;
/**
* 步骤详情
* key 为步骤ID
* 为详情
*/
@Schema(description = "步骤详情")
private Map<String, Object> stepDetails;
/**
* 新上传的文件ID
* 创建时先按ID创建目录再把文件放入目录
*/
@Schema(description = "新上传的文件ID")
private List<String> uploadFileIds;
/**
* 新关联的文件ID
*/
@Schema(description = "关联文件ID")
private List<String> linkFileIds;
/**
* 删除本地上传的文件ID
*/
private List<String> deleteFileIds;
/**
* 删除关联的文件ID
*/
private List<String> unLinkRefIds;
}

View File

@ -0,0 +1,76 @@
package io.metersphere.api.dto.scenario;
import io.metersphere.api.dto.ApiFile;
import lombok.Data;
/**
* @Author: jianxing
* @CreateTime: 2024-01-12 10:11
*/
@Data
public class CsvVariable {
private String name;
/**
* @see CsvVariableScope
*/
private String scope;
/**
* 文件信息
*/
private ApiFile file;
/**
* 分隔符
*/
private String delimiter;
/**
* 是否允许带引号
*/
private Boolean allowQuotationMarks = false;
/**
* 是否忽略首行
*/
private Boolean ignoreFirstLine = false;
/**
* 是否随机
*/
private Boolean random = false;
/**
* 遇到文件结束符再次循环
*/
private Boolean endFileLoopsAgain = true;
/**
* 遇到文件结束符停止线程
*/
private Boolean endFileStopThread = false;
/**
* 文件编码
* @see CsvEncodingType
*/
private String encoding;
public enum CsvEncodingType {
UTF8("UTF-8"), UFT16("UTF-16"), ISO885915("ISO-8859-15"), US_ASCII("US-ASCII");
private String value;
CsvEncodingType(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
public enum CsvVariableScope {
/**
* 场景级执行场景前加载CSV当前场景任意步骤均可从CSV中读取到数据
*/
SCENARIO,
/**
* 步骤级需在测试步骤的更多操作中指定使用添加该CSV执行该步骤时加载CSV作用域为步骤内的请求
*/
STEP
}
}

View File

@ -0,0 +1,33 @@
package io.metersphere.api.dto.scenario;
import io.metersphere.api.dto.request.assertion.MsAssertionConfig;
import io.metersphere.api.dto.request.processors.MsProcessorConfig;
import lombok.Data;
/**
* @Author: jianxing
* @CreateTime: 2024-01-12 09:47
*/
@Data
public class ScenarioConfig {
/**
* 场景变量
*/
private ScenarioVariable variable;
/**
* 前置处理器配置
*/
private MsProcessorConfig preProcessorConfig;
/**
* 后置处理器配置
*/
private MsProcessorConfig postProcessorConfig;
/**
* 断言配置
*/
private MsAssertionConfig assertionConfig;
/**
* 其他配置
*/
private ScenarioOtherConfig otherConfig;
}

View File

@ -0,0 +1,40 @@
package io.metersphere.api.dto.scenario;
import lombok.Data;
/**
* @Author: jianxing
* @CreateTime: 2024-01-12 09:47
*/
@Data
public class ScenarioOtherConfig {
/**
* 使用全局cookie
*/
private Boolean enableGlobalCookie = true;
/**
* 是否共享cookie
*/
private Boolean enableCookieShare;
/**
* 场景步骤等待时间
* 每一个步骤执行后都会等待相应的时间
*/
private Integer stepWaitTime;
/**
* 失败策略
* @see FailureStrategy
*/
private String failureStrategy;
public enum FailureStrategy {
/**
* 继续执行
*/
CONTINUE,
/**
* 停止执行
*/
STOP
}
}

View File

@ -0,0 +1,22 @@
package io.metersphere.api.dto.scenario;
import io.metersphere.project.dto.environment.variables.CommonVariables;
import lombok.Data;
import java.util.List;
/**
* @Author: jianxing
* @CreateTime: 2024-01-12 09:49
*/
@Data
public class ScenarioVariable {
/**
* 普通变量
*/
private List<CommonVariables> commonVariables;
/**
* csv变量
*/
private List<CsvVariable> csvVariables;
}

View File

@ -30,4 +30,5 @@ public interface ExtApiScenarioMapper {
List<ApiScenario> getTestCaseByProvider(@Param("request") AssociateOtherCaseRequest request, @Param("deleted") boolean deleted); List<ApiScenario> getTestCaseByProvider(@Param("request") AssociateOtherCaseRequest request, @Param("deleted") boolean deleted);
Long getLastPos(@Param("projectId") String projectId);
} }

View File

@ -362,5 +362,12 @@
</if> </if>
</sql> </sql>
<select id="getLastPos" resultType="java.lang.Long">
SELECT pos
FROM api_scenario
WHERE project_id = #{projectId}
ORDER BY pos DESC
LIMIT 1;
</select>
</mapper> </mapper>

View File

@ -0,0 +1,11 @@
package io.metersphere.api.mapper;
import java.util.List;
/**
* @Author: jianxing
* @CreateTime: 2024-01-16 19:57
*/
public interface ExtApiScenarioStepBlobMapper {
List<String> getStepIdsByScenarioId(String scenarioId);
}

View File

@ -0,0 +1,8 @@
<?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.ExtApiScenarioStepBlobMapper">
<select id="getStepIdsByScenarioId" resultType="java.lang.String">
SELECT id FROM api_scenario_step_blob WHERE scenario_id = #{scenarioId}
</select>
</mapper>

View File

@ -0,0 +1,11 @@
package io.metersphere.api.mapper;
import java.util.List;
/**
* @Author: jianxing
* @CreateTime: 2024-01-16 19:57
*/
public interface ExtApiScenarioStepMapper {
List<String> getStepIdsByScenarioId(String scenarioId);
}

View File

@ -0,0 +1,8 @@
<?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.ExtApiScenarioStepMapper">
<select id="getStepIdsByScenarioId" resultType="java.lang.String">
SELECT id FROM api_scenario_step WHERE scenario_id = #{scenarioId}
</select>
</mapper>

View File

@ -1,7 +1,7 @@
package io.metersphere.api.parser.jmeter.body; package io.metersphere.api.parser.jmeter.body;
import io.metersphere.api.dto.request.http.body.BinaryBody; import io.metersphere.api.dto.request.http.body.BinaryBody;
import io.metersphere.api.dto.request.http.body.BodyFile; import io.metersphere.api.dto.ApiFile;
import io.metersphere.plugin.api.dto.ParameterConfig; import io.metersphere.plugin.api.dto.ParameterConfig;
import org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy; import org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy;
import org.apache.jmeter.protocol.http.util.HTTPFileArg; import org.apache.jmeter.protocol.http.util.HTTPFileArg;
@ -14,7 +14,7 @@ import org.apache.jmeter.protocol.http.util.HTTPFileArg;
public class MsBinaryBodyConverter extends MsBodyConverter<BinaryBody> { public class MsBinaryBodyConverter extends MsBodyConverter<BinaryBody> {
@Override @Override
public void parse(HTTPSamplerProxy sampler, BinaryBody body, ParameterConfig config) { public void parse(HTTPSamplerProxy sampler, BinaryBody body, ParameterConfig config) {
BodyFile bodyFile = body.getBodyFile(); ApiFile bodyFile = body.getBodyFile();
HTTPFileArg httpFileArg = getHttpFileArg(bodyFile); HTTPFileArg httpFileArg = getHttpFileArg(bodyFile);
sampler.setHTTPFiles(new HTTPFileArg[]{httpFileArg}); sampler.setHTTPFiles(new HTTPFileArg[]{httpFileArg});
} }

View File

@ -1,7 +1,7 @@
package io.metersphere.api.parser.jmeter.body; package io.metersphere.api.parser.jmeter.body;
import io.metersphere.api.dto.request.http.body.BodyFile; import io.metersphere.api.dto.ApiFile;
import io.metersphere.api.dto.request.http.body.WWWFormKV; import io.metersphere.api.dto.request.http.body.WWWFormKV;
import io.metersphere.jmeter.mock.Mock; import io.metersphere.jmeter.mock.Mock;
import io.metersphere.plugin.api.dto.ParameterConfig; import io.metersphere.plugin.api.dto.ParameterConfig;
@ -63,7 +63,7 @@ public abstract class MsBodyConverter<T> {
* @param file * @param file
* @return * @return
*/ */
protected HTTPFileArg getHttpFileArg(BodyFile file) { protected HTTPFileArg getHttpFileArg(ApiFile file) {
String fileId = file.getFileId(); String fileId = file.getFileId();
String fileName = file.getFileName(); String fileName = file.getFileName();
// 在对应目录下创建文件ID目录将文件放入 // 在对应目录下创建文件ID目录将文件放入

View File

@ -80,7 +80,6 @@ public class ApiDebugService {
apiDebug.setPos(getNextOrder(request.getProjectId())); apiDebug.setPos(getNextOrder(request.getProjectId()));
apiDebugMapper.insert(apiDebug); apiDebugMapper.insert(apiDebug);
// todo 校验 moduleId
ApiDebugBlob apiDebugBlob = new ApiDebugBlob(); ApiDebugBlob apiDebugBlob = new ApiDebugBlob();
apiDebugBlob.setId(apiDebug.getId()); apiDebugBlob.setId(apiDebug.getId());
apiDebugBlob.setRequest(request.getRequest().getBytes()); apiDebugBlob.setRequest(request.getRequest().getBytes());
@ -120,7 +119,6 @@ public class ApiDebugService {
apiDebug.setUpdateUser(updateUser); apiDebug.setUpdateUser(updateUser);
apiDebug.setUpdateTime(System.currentTimeMillis()); apiDebug.setUpdateTime(System.currentTimeMillis());
apiDebugMapper.updateByPrimaryKeySelective(apiDebug); apiDebugMapper.updateByPrimaryKeySelective(apiDebug);
// todo 校验 moduleId
if (StringUtils.isNotBlank(request.getRequest())) { if (StringUtils.isNotBlank(request.getRequest())) {
ApiDebugBlob apiDebugBlob = new ApiDebugBlob(); ApiDebugBlob apiDebugBlob = new ApiDebugBlob();

View File

@ -2,6 +2,8 @@ package io.metersphere.api.service.scenario;
import io.metersphere.api.domain.ApiScenario; import io.metersphere.api.domain.ApiScenario;
import io.metersphere.api.domain.ApiScenarioExample; import io.metersphere.api.domain.ApiScenarioExample;
import io.metersphere.api.dto.scenario.ApiScenarioAddRequest;
import io.metersphere.api.dto.scenario.ApiScenarioUpdateRequest;
import io.metersphere.api.mapper.ApiScenarioMapper; import io.metersphere.api.mapper.ApiScenarioMapper;
import io.metersphere.project.domain.Project; import io.metersphere.project.domain.Project;
import io.metersphere.project.mapper.ProjectMapper; import io.metersphere.project.mapper.ProjectMapper;
@ -97,4 +99,49 @@ public class ApiScenarioLogService {
dto.setOriginalValue(JSON.toJSONBytes(apiTestCase)); dto.setOriginalValue(JSON.toJSONBytes(apiTestCase));
operationLogService.add(dto); operationLogService.add(dto);
} }
public LogDTO addLog(ApiScenarioAddRequest request) {
// todo 记录完整的场景信息
LogDTO dto = new LogDTO(
null,
null,
null,
null,
OperationLogType.ADD.name(),
OperationLogModule.API_SCENARIO,
request.getName());
dto.setHistory(true);
dto.setOriginalValue(JSON.toJSONBytes(request));
return dto;
}
public LogDTO updateLog(ApiScenarioUpdateRequest request) {
ApiScenario apiScenario = apiScenarioMapper.selectByPrimaryKey(request.getId());
// todo 记录完整的场景信息
LogDTO dto = new LogDTO(
null,
null,
apiScenario.getId(),
null,
OperationLogType.UPDATE.name(),
OperationLogModule.API_SCENARIO,
apiScenario.getName());
dto.setHistory(true);
dto.setOriginalValue(JSON.toJSONBytes(request));
return dto;
}
public LogDTO deleteLog(String id) {
ApiScenario apiScenario = apiScenarioMapper.selectByPrimaryKey(id);
LogDTO dto = new LogDTO(
null,
null,
apiScenario.getId(),
null,
OperationLogType.DELETE.name(),
OperationLogModule.API_SCENARIO,
apiScenario.getName());
dto.setOriginalValue(JSON.toJSONBytes(apiScenario));
return dto;
}
} }

View File

@ -1,14 +1,17 @@
package io.metersphere.api.service.scenario; package io.metersphere.api.service.scenario;
import com.alibaba.excel.util.BooleanUtils; import io.metersphere.api.constants.ApiResourceType;
import io.metersphere.api.constants.ApiScenarioStepRefType;
import io.metersphere.api.constants.ApiScenarioStepType;
import io.metersphere.api.domain.*; import io.metersphere.api.domain.*;
import io.metersphere.api.dto.scenario.ApiScenarioBatchEditRequest; import io.metersphere.api.dto.debug.ApiFileResourceUpdateRequest;
import io.metersphere.api.dto.scenario.ApiScenarioDTO; import io.metersphere.api.dto.scenario.*;
import io.metersphere.api.dto.scenario.ApiScenarioPageRequest; import io.metersphere.api.mapper.*;
import io.metersphere.api.mapper.ApiScenarioFollowerMapper; import io.metersphere.api.service.ApiFileResourceService;
import io.metersphere.api.mapper.ApiScenarioMapper; import io.metersphere.project.mapper.ExtBaseProjectVersionMapper;
import io.metersphere.api.mapper.ApiScenarioModuleMapper; import io.metersphere.project.service.ProjectService;
import io.metersphere.api.mapper.ExtApiScenarioMapper; import io.metersphere.sdk.constants.ApplicationNumScope;
import io.metersphere.sdk.constants.DefaultRepositoryDir;
import io.metersphere.sdk.domain.Environment; import io.metersphere.sdk.domain.Environment;
import io.metersphere.sdk.domain.EnvironmentExample; import io.metersphere.sdk.domain.EnvironmentExample;
import io.metersphere.sdk.domain.EnvironmentGroup; import io.metersphere.sdk.domain.EnvironmentGroup;
@ -16,12 +19,17 @@ import io.metersphere.sdk.domain.EnvironmentGroupExample;
import io.metersphere.sdk.exception.MSException; import io.metersphere.sdk.exception.MSException;
import io.metersphere.sdk.mapper.EnvironmentGroupMapper; import io.metersphere.sdk.mapper.EnvironmentGroupMapper;
import io.metersphere.sdk.mapper.EnvironmentMapper; import io.metersphere.sdk.mapper.EnvironmentMapper;
import io.metersphere.sdk.util.SubListUtils; import io.metersphere.sdk.util.*;
import io.metersphere.sdk.util.Translator; import io.metersphere.system.log.constants.OperationLogModule;
import io.metersphere.system.service.UserLoginService; import io.metersphere.system.service.UserLoginService;
import io.metersphere.system.uid.IDGenerator;
import io.metersphere.system.uid.NumGenerator;
import io.metersphere.system.utils.ServiceUtils;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils; import org.apache.commons.collections.MapUtils;
import org.apache.commons.collections4.ListUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.session.ExecutorType; import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSession;
@ -29,21 +37,17 @@ import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionUtils; import org.mybatis.spring.SqlSessionUtils;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import java.util.ArrayList; import java.util.*;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
import static io.metersphere.api.controller.result.ApiResultCode.API_SCENARIO_EXIST;
@Service @Service
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public class ApiScenarioService { public class ApiScenarioService {
public static final Long ORDER_STEP = 5000L;
@Resource @Resource
private ApiScenarioMapper apiScenarioMapper; private ApiScenarioMapper apiScenarioMapper;
@ -64,6 +68,22 @@ public class ApiScenarioService {
private ApiScenarioFollowerMapper apiScenarioFollowerMapper; private ApiScenarioFollowerMapper apiScenarioFollowerMapper;
@Resource @Resource
private SqlSessionFactory sqlSessionFactory; private SqlSessionFactory sqlSessionFactory;
@Resource
private ProjectService projectService;
@Resource
private ExtBaseProjectVersionMapper extBaseProjectVersionMapper;
@Resource
private ApiFileResourceService apiFileResourceService;
@Resource
private ApiScenarioStepMapper apiScenarioStepMapper;
@Resource
private ExtApiScenarioStepMapper extApiScenarioStepMapper;
@Resource
private ExtApiScenarioStepBlobMapper extApiScenarioStepBlobMapper;
@Resource
private ApiScenarioStepBlobMapper apiScenarioStepBlobMapper;
@Resource
private ApiScenarioBlobMapper apiScenarioBlobMapper;
public static final String PRIORITY = "Priority"; public static final String PRIORITY = "Priority";
public static final String STATUS = "Status"; public static final String STATUS = "Status";
public static final String TAGS = "Tags"; public static final String TAGS = "Tags";
@ -236,7 +256,7 @@ public class ApiScenarioService {
} }
public void follow(String id, String userId) { public void follow(String id, String userId) {
checkApiScenario(id); checkResourceExist(id);
ApiScenarioFollowerExample example = new ApiScenarioFollowerExample(); ApiScenarioFollowerExample example = new ApiScenarioFollowerExample();
example.createCriteria().andApiScenarioIdEqualTo(id).andUserIdEqualTo(userId); example.createCriteria().andApiScenarioIdEqualTo(id).andUserIdEqualTo(userId);
if (apiScenarioFollowerMapper.countByExample(example) > 0) { if (apiScenarioFollowerMapper.countByExample(example) > 0) {
@ -251,11 +271,342 @@ public class ApiScenarioService {
} }
} }
private ApiScenario checkApiScenario(String id) { public ApiScenario add(ApiScenarioAddRequest request, String creator) {
ApiScenario apiScenario = apiScenarioMapper.selectByPrimaryKey(id); checkAddExist(request);
if (apiScenario == null) { ApiScenario scenario = getAddApiScenario(request, creator);
throw new MSException(Translator.get("api_scenario_is_not_exist")); apiScenarioMapper.insert(scenario);
// 更新场景配置
ApiScenarioBlob apiScenarioBlob = new ApiScenarioBlob();
apiScenarioBlob.setId(scenario.getId());
apiScenarioBlob.setConfig(JSON.toJSONString(request.getScenarioConfig()).getBytes());
apiScenarioBlobMapper.insert(apiScenarioBlob);
// 插入步骤
if (CollectionUtils.isNotEmpty(request.getSteps())) {
List<ApiScenarioStepBlob> apiScenarioStepsDetails = new ArrayList<>();
List<ApiScenarioStep> apiScenarioSteps = getUpdateApiScenarioSteps(null, request.getSteps(), apiScenarioStepsDetails);
apiScenarioStepsDetails.addAll(getUpdateStepDetails(apiScenarioSteps, request.getStepDetails()));
apiScenarioSteps.forEach(step -> step.setScenarioId(scenario.getId()));
apiScenarioStepsDetails.forEach(step -> step.setScenarioId(scenario.getId()));
if (CollectionUtils.isNotEmpty(apiScenarioSteps)) {
apiScenarioStepMapper.batchInsert(apiScenarioSteps);
} }
return apiScenario; if (CollectionUtils.isNotEmpty(apiScenarioStepsDetails)) {
apiScenarioStepBlobMapper.batchInsert(apiScenarioStepsDetails);
}
}
// 处理文件
ApiFileResourceUpdateRequest resourceUpdateRequest = getApiFileResourceUpdateRequest(scenario.getId(), scenario.getProjectId(), creator);
resourceUpdateRequest.setUploadFileIds(request.getUploadFileIds());
resourceUpdateRequest.setLinkFileIds(request.getLinkFileIds());
apiFileResourceService.addFileResource(resourceUpdateRequest);
return scenario;
}
private ApiScenario getAddApiScenario(ApiScenarioAddRequest request, String creator) {
ApiScenario scenario = new ApiScenario();
BeanUtils.copyBean(scenario, request);
scenario.setId(IDGenerator.nextStr());
scenario.setNum(getNextNum(request.getProjectId()));
scenario.setPos(getNextOrder(request.getProjectId()));
scenario.setLatest(true);
scenario.setCreateUser(creator);
scenario.setUpdateUser(creator);
scenario.setCreateTime(System.currentTimeMillis());
scenario.setUpdateTime(System.currentTimeMillis());
scenario.setVersionId(extBaseProjectVersionMapper.getDefaultVersion(request.getProjectId()));
scenario.setRefId(scenario.getId());
scenario.setLastReportStatus(StringUtils.EMPTY);
scenario.setDeleted(false);
scenario.setRequestPassRate("0");
scenario.setStepTotal(CollectionUtils.isEmpty(request.getSteps()) ? 0 : request.getSteps().size());
return scenario;
}
public ApiScenario update(ApiScenarioUpdateRequest request, String updater) {
checkResourceExist(request.getId());
checkUpdateExist(request);
// 更新基础信息
ApiScenario scenario = BeanUtils.copyBean(new ApiScenario(), request);
scenario.setUpdateUser(updater);
scenario.setUpdateTime(System.currentTimeMillis());
apiScenarioMapper.updateByPrimaryKeySelective(scenario);
if (request.getScenarioConfig() != null) {
// 更新场景配置
ApiScenarioBlob apiScenarioBlob = new ApiScenarioBlob();
apiScenarioBlob.setId(scenario.getId());
apiScenarioBlob.setConfig(JSON.toJSONString(request.getScenarioConfig()).getBytes());
apiScenarioBlobMapper.updateByPrimaryKeyWithBLOBs(apiScenarioBlob);
}
// 更新场景步骤
updateApiScenarioStep(request, scenario);
ApiScenario originScenario = apiScenarioMapper.selectByPrimaryKey(request.getId());
// 处理文件
ApiFileResourceUpdateRequest resourceUpdateRequest = getApiFileResourceUpdateRequest(scenario.getId(), originScenario.getProjectId(), updater);
resourceUpdateRequest.setUploadFileIds(request.getUploadFileIds());
resourceUpdateRequest.setLinkFileIds(request.getLinkFileIds());
resourceUpdateRequest.setUnLinkRefIds(request.getUnLinkRefIds());
resourceUpdateRequest.setDeleteFileIds(request.getDeleteFileIds());
apiFileResourceService.updateFileResource(resourceUpdateRequest);
return scenario;
}
/**
* 更新场景步骤
* @param request
* @param scenario
*/
private void updateApiScenarioStep(ApiScenarioUpdateRequest request, ApiScenario scenario) {
// steps 不为 null 则修改
if (request.getSteps() != null) {
if (CollectionUtils.isEmpty(request.getSteps())) {
// 如果是空数组则删除所有步骤
deleteStepByScenarioId(scenario.getId());
deleteStepDetailByScenarioId(scenario.getId());
return;
}
List<ApiScenarioStepBlob> apiScenarioStepsDetails = new ArrayList<>();
List<ApiScenarioStep> apiScenarioSteps = getUpdateApiScenarioSteps(null, request.getSteps(), apiScenarioStepsDetails);
apiScenarioStepsDetails.addAll(getUpdateStepDetails(apiScenarioSteps, request.getStepDetails()));
apiScenarioSteps.forEach(step -> step.setScenarioId(scenario.getId()));
apiScenarioStepsDetails.forEach(step -> step.setScenarioId(scenario.getId()));
List<String> stepIds = apiScenarioSteps.stream().map(ApiScenarioStep::getId).collect(Collectors.toList());
List<String> originStepIds = extApiScenarioStepMapper.getStepIdsByScenarioId(scenario.getId());
List<String> deleteStepIds = ListUtils.subtract(originStepIds, stepIds);
// 步骤表-全部先删除再插入
deleteStepByScenarioId(scenario.getId());
apiScenarioStepMapper.batchInsert(apiScenarioSteps);
// 详情表-删除已经删除的步骤详情
SubListUtils.dealForSubList(deleteStepIds, 100, subIds -> {
ApiScenarioStepBlobExample stepBlobExample = new ApiScenarioStepBlobExample();
stepBlobExample.createCriteria().andIdIn(subIds);
apiScenarioStepBlobMapper.deleteByExample(stepBlobExample);
});
// 查询原有的步骤详情
Set<String> originStepDetailIds = extApiScenarioStepBlobMapper.getStepIdsByScenarioId(scenario.getId())
.stream().collect(Collectors.toSet());
// 添加新增的步骤详情
List<ApiScenarioStepBlob> addApiScenarioStepsDetails = apiScenarioStepsDetails.stream()
.filter(step -> !originStepDetailIds.contains(step.getId()))
.collect(Collectors.toList());
if (CollectionUtils.isNotEmpty(addApiScenarioStepsDetails)) {
apiScenarioStepBlobMapper.batchInsert(addApiScenarioStepsDetails);
}
// 更新原有的步骤详情
apiScenarioStepsDetails.stream()
.filter(step -> originStepDetailIds.contains(step.getId()))
.forEach(apiScenarioStepBlobMapper::updateByPrimaryKeySelective);
} else if (MapUtils.isNotEmpty(request.getStepDetails())) {
// steps nullstepDetails 不为 null则只更新详情
// 查询原有的步骤详情
Set<String> originStepDetailIds = extApiScenarioStepBlobMapper.getStepIdsByScenarioId(scenario.getId())
.stream().collect(Collectors.toSet());
// 更新原有的步骤详情
request.getStepDetails().forEach((stepId, stepDetail) -> {
if (originStepDetailIds.contains(stepId)) {
ApiScenarioStepBlob apiScenarioStepBlob = new ApiScenarioStepBlob();
apiScenarioStepBlob.setId(stepId);
apiScenarioStepBlob.setContent(JSON.toJSONString(stepDetail).getBytes());
apiScenarioStepBlobMapper.updateByPrimaryKeySelective(apiScenarioStepBlob);
}
});
}
}
private void deleteStepDetailByScenarioId(String scenarioId) {
ApiScenarioStepBlobExample blobExample = new ApiScenarioStepBlobExample();
blobExample.createCriteria().andScenarioIdEqualTo(scenarioId);
apiScenarioStepBlobMapper.deleteByExample(blobExample);
}
private void deleteStepByScenarioId(String scenarioId) {
ApiScenarioStepExample example = new ApiScenarioStepExample();
example.createCriteria().andScenarioIdEqualTo(scenarioId);
apiScenarioStepMapper.deleteByExample(example);
}
/**
* 获取待更新的 ApiScenarioStepBlob 列表
* @param apiScenarioSteps
* @param stepDetails
* @return
*/
private List<ApiScenarioStepBlob> getUpdateStepDetails(List<ApiScenarioStep> apiScenarioSteps, Map<String, Object> stepDetails) {
if (MapUtils.isEmpty(stepDetails)) {
return Collections.emptyList();
}
Map<String, ApiScenarioStep> scenarioStepMap = apiScenarioSteps.stream()
.collect(Collectors.toMap(ApiScenarioStep::getId, Function.identity()));
List<ApiScenarioStepBlob> apiScenarioStepsDetails = new ArrayList<>();
stepDetails.forEach((stepId, stepDetail) -> {
ApiScenarioStep step = scenarioStepMap.get(stepId);
if (step == null) {
return;
}
boolean isRef = StringUtils.equalsAny(step.getRefType(), ApiScenarioStepRefType.REF.name(), ApiScenarioStepRefType.STEP_REF.name());
if (!isRef || StringUtils.equals(step.getRefType(), ApiScenarioStepType.API.name())) {
// 非引用的步骤如果有编辑内容保存到blob表
// 如果引用的是接口定义也保存详情因为应用接口定义允许修改参数值
ApiScenarioStepBlob apiScenarioStepBlob = new ApiScenarioStepBlob();
apiScenarioStepBlob.setId(stepId);
apiScenarioStepBlob.setContent(JSON.toJSONString(stepDetail).getBytes());
apiScenarioStepsDetails.add(apiScenarioStepBlob);
}
});
return apiScenarioStepsDetails;
}
/**
* 解析步骤树结构
* 获取待更新的 ApiScenarioStep 列表
* @param parent
* @param steps
* @param apiScenarioStepsDetails
* @return
*/
private List<ApiScenarioStep> getUpdateApiScenarioSteps(ApiScenarioStepRequest parent,
List<ApiScenarioStepRequest> steps,
List<ApiScenarioStepBlob> apiScenarioStepsDetails) {
if (CollectionUtils.isEmpty(steps)) {
return Collections.emptyList();
}
List<ApiScenarioStep> apiScenarioSteps = new ArrayList<>();
long sort = 1;
for (ApiScenarioStepRequest step : steps) {
ApiScenarioStep apiScenarioStep = new ApiScenarioStep();
BeanUtils.copyBean(apiScenarioStep, step);
apiScenarioStep.setSort(sort++);
if (parent != null) {
apiScenarioStep.setParentId(parent.getId());
}
if (step.getConfig() != null) {
apiScenarioStep.setConfig(JSON.toJSONString(step.getConfig()));
}
apiScenarioSteps.add(apiScenarioStep);
if (StringUtils.equals(step.getRefType(), ApiScenarioStepRefType.STEP_REF.name())) {
// 如果是步骤引用blob表保存启用的子步骤ID
Set<String> enableStepSet = getEnableStepSet(step.getChildren());
ApiScenarioStepBlob apiScenarioStepBlob = new ApiScenarioStepBlob();
apiScenarioStepBlob.setId(apiScenarioStep.getId());
apiScenarioStepBlob.setContent(JSON.toJSONString(enableStepSet).getBytes());
apiScenarioStepsDetails.add(apiScenarioStepBlob);
}
if (StringUtils.equalsAny(step.getRefType(), ApiScenarioStepRefType.REF.name(), ApiScenarioStepRefType.STEP_REF.name())) {
// 引用的步骤不解析子步骤
continue;
}
// 解析子步骤
apiScenarioSteps.addAll(getUpdateApiScenarioSteps(step, step.getChildren(), apiScenarioStepsDetails));
}
return apiScenarioSteps;
}
/**
* 获取步骤及子步骤中 enable 的步骤ID
*
* @param steps
* @return
*/
private Set<String> getEnableStepSet(List<ApiScenarioStepRequest> steps) {
Set<String> enableSteps = new HashSet<>();
if (CollectionUtils.isEmpty(steps)) {
return Collections.emptySet();
}
for (ApiScenarioStepRequest step : steps) {
if (BooleanUtils.isTrue(step.getEnable())) {
enableSteps.add(step.getId());
}
// 获取子步骤中 enable = true 的步骤
enableSteps.addAll(getEnableStepSet(step.getChildren()));
}
return enableSteps;
}
private static ApiFileResourceUpdateRequest getApiFileResourceUpdateRequest(String sourceId, String projectId, String operator) {
String apiScenarioDir = DefaultRepositoryDir.getApiScenarioDir(projectId, sourceId);
ApiFileResourceUpdateRequest resourceUpdateRequest = new ApiFileResourceUpdateRequest();
resourceUpdateRequest.setProjectId(projectId);
resourceUpdateRequest.setFolder(apiScenarioDir);
resourceUpdateRequest.setResourceId(sourceId);
resourceUpdateRequest.setApiResourceType(ApiResourceType.API_SCENARIO);
resourceUpdateRequest.setOperator(operator);
resourceUpdateRequest.setLogModule(OperationLogModule.API_SCENARIO);
resourceUpdateRequest.setFileAssociationSourceType(FileAssociationSourceUtil.SOURCE_TYPE_API_DEBUG);
return resourceUpdateRequest;
}
public long getNextNum(String projectId) {
return NumGenerator.nextNum(projectId, ApplicationNumScope.API_SCENARIO);
}
public Long getNextOrder(String projectId) {
return projectService.getNextOrder(extApiScenarioMapper::getLastPos, projectId);
}
public void delete(String id) {
checkResourceExist(id);
apiScenarioMapper.deleteByPrimaryKey(id);
apiScenarioBlobMapper.deleteByPrimaryKey(id);
deleteStepByScenarioId(id);
deleteStepDetailByScenarioId(id);
}
public void deleteToGc(String id) {
checkResourceExist(id);
ApiScenario apiScenario = new ApiScenario();
apiScenario.setId(id);
apiScenario.setDeleted(true);
apiScenarioMapper.updateByPrimaryKeySelective(apiScenario);
}
private void checkAddExist(ApiScenarioAddRequest apiScenario) {
ApiScenarioExample example = new ApiScenarioExample();
// 统一模块下名称不能重复
example.createCriteria()
.andNameEqualTo(apiScenario.getName())
.andModuleIdEqualTo(apiScenario.getModuleId());
if (apiScenarioMapper.countByExample(example) > 0) {
throw new MSException(API_SCENARIO_EXIST);
}
}
private void checkUpdateExist(ApiScenarioUpdateRequest request) {
if (StringUtils.isBlank(request.getName())) {
return;
}
// 统一模块下名称不能重复
ApiScenarioExample example = new ApiScenarioExample();
example.createCriteria()
.andIdNotEqualTo(request.getId())
.andModuleIdEqualTo(request.getModuleId())
.andNameEqualTo(request.getName());
if (apiScenarioMapper.countByExample(example) > 0) {
throw new MSException(API_SCENARIO_EXIST);
}
}
private ApiScenario checkResourceExist(String id) {
return ServiceUtils.checkResourceExist(apiScenarioMapper.selectByPrimaryKey(id), "permission.system_api_scenario.name");
}
public String uploadTempFile(MultipartFile file) {
return apiFileResourceService.uploadTempFile(file);
} }
} }

View File

@ -1,25 +1,36 @@
package io.metersphere.api.controller; package io.metersphere.api.controller;
import io.metersphere.api.constants.ApiDefinitionStatus;
import io.metersphere.api.constants.ApiScenarioStatus;
import io.metersphere.api.constants.ApiScenarioStepRefType;
import io.metersphere.api.domain.*; import io.metersphere.api.domain.*;
import io.metersphere.api.dto.scenario.ApiScenarioBatchEditRequest; import io.metersphere.api.dto.definition.ApiDefinitionAddRequest;
import io.metersphere.api.dto.scenario.ApiScenarioDTO; import io.metersphere.api.dto.definition.ApiTestCaseAddRequest;
import io.metersphere.api.dto.scenario.ApiScenarioPageRequest; import io.metersphere.api.dto.request.assertion.MsAssertionConfig;
import io.metersphere.api.mapper.ApiScenarioFollowerMapper; import io.metersphere.api.dto.request.assertion.MsScriptAssertion;
import io.metersphere.api.mapper.ApiScenarioMapper; import io.metersphere.api.dto.request.http.MsHTTPElement;
import io.metersphere.api.mapper.ApiScenarioModuleMapper; import io.metersphere.api.dto.scenario.*;
import io.metersphere.api.mapper.ExtApiScenarioMapper; import io.metersphere.api.mapper.*;
import io.metersphere.api.service.definition.ApiDefinitionService;
import io.metersphere.api.service.definition.ApiTestCaseService;
import io.metersphere.api.utils.ApiDataUtils;
import io.metersphere.project.mapper.ExtBaseProjectVersionMapper;
import io.metersphere.sdk.constants.ApplicationNumScope; import io.metersphere.sdk.constants.ApplicationNumScope;
import io.metersphere.sdk.constants.DefaultRepositoryDir;
import io.metersphere.sdk.constants.PermissionConstants; import io.metersphere.sdk.constants.PermissionConstants;
import io.metersphere.sdk.constants.SessionConstants;
import io.metersphere.sdk.domain.Environment; import io.metersphere.sdk.domain.Environment;
import io.metersphere.sdk.domain.EnvironmentExample; import io.metersphere.sdk.domain.EnvironmentExample;
import io.metersphere.sdk.domain.EnvironmentGroup; import io.metersphere.sdk.domain.EnvironmentGroup;
import io.metersphere.sdk.file.FileCenter;
import io.metersphere.sdk.file.FileRequest;
import io.metersphere.sdk.mapper.EnvironmentGroupMapper; import io.metersphere.sdk.mapper.EnvironmentGroupMapper;
import io.metersphere.sdk.mapper.EnvironmentMapper; import io.metersphere.sdk.mapper.EnvironmentMapper;
import io.metersphere.sdk.util.BeanUtils;
import io.metersphere.sdk.util.JSON; import io.metersphere.sdk.util.JSON;
import io.metersphere.system.base.BaseTest; import io.metersphere.system.base.BaseTest;
import io.metersphere.system.controller.handler.ResultHolder; import io.metersphere.system.controller.handler.ResultHolder;
import io.metersphere.system.log.constants.OperationLogType; import io.metersphere.system.log.constants.OperationLogType;
import io.metersphere.system.uid.IDGenerator;
import io.metersphere.system.uid.NumGenerator; import io.metersphere.system.uid.NumGenerator;
import io.metersphere.system.utils.Pager; import io.metersphere.system.utils.Pager;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
@ -28,18 +39,15 @@ import org.junit.jupiter.api.*;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.test.web.servlet.MvcResult; import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.ResultMatcher; import org.springframework.test.web.servlet.ResultMatcher;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.testcontainers.shaded.org.apache.commons.lang3.StringUtils; import org.testcontainers.shaded.org.apache.commons.lang3.StringUtils;
import java.nio.charset.StandardCharsets; import java.util.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; import static io.metersphere.api.controller.result.ApiResultCode.API_SCENARIO_EXIST;
import static io.metersphere.system.controller.handler.result.MsHttpResultCode.NOT_FOUND;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ -47,10 +55,11 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
@TestMethodOrder(MethodOrderer.OrderAnnotation.class) @TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class ApiScenarioControllerTests extends BaseTest { public class ApiScenarioControllerTests extends BaseTest {
private static final String BASE_PATH = "/api/scenario/"; private static final String BASE_PATH = "/api/scenario/";
private static final String PAGE = BASE_PATH + "page"; private static final String TRASH_PAGE = "trash/page";
private static final String TRASH_PAGE = BASE_PATH + "trash/page"; private static final String BATCH_EDIT = "batch/edit";
private static final String BATCH_EDIT = BASE_PATH + "batch/edit"; private static final String FOLLOW = "follow/";
private static final String FOLLOW = BASE_PATH + "follow/"; protected static final String UPLOAD_TEMP_FILE = "upload/temp/file";
protected static final String DELETE_TO_GC = "delete-to-gc/{0}";
private static final ResultMatcher ERROR_REQUEST_MATCHER = status().is5xxServerError(); private static final ResultMatcher ERROR_REQUEST_MATCHER = status().is5xxServerError();
@Resource @Resource
@ -65,27 +74,26 @@ public class ApiScenarioControllerTests extends BaseTest {
private ApiScenarioFollowerMapper apiScenarioFollowerMapper; private ApiScenarioFollowerMapper apiScenarioFollowerMapper;
@Resource @Resource
private EnvironmentGroupMapper environmentGroupMapper; private EnvironmentGroupMapper environmentGroupMapper;
@Resource
private ApiScenarioStepMapper apiScenarioStepMapper;
@Resource
private ApiScenarioStepBlobMapper apiScenarioStepBlobMapper;
@Resource
private ApiScenarioBlobMapper apiScenarioBlobMapper;
@Resource
private ExtBaseProjectVersionMapper extBaseProjectVersionMapper;
@Resource
private ApiDefinitionService apiDefinitionService;
@Resource
private ApiTestCaseService apiTestCaseService;
private static ApiScenario addApiScenario;
private static ApiScenario anOtherAddApiScenario;
private static ApiDefinition apiDefinition;
private static ApiTestCase apiTestCase;
public static <T> T parseObjectFromMvcResult(MvcResult mvcResult, Class<T> parseClass) { @Override
try { protected String getBasePath() {
String returnData = mvcResult.getResponse().getContentAsString(StandardCharsets.UTF_8); return BASE_PATH;
ResultHolder resultHolder = JSON.parseObject(returnData, ResultHolder.class);
//返回请求正常
Assertions.assertNotNull(resultHolder);
return JSON.parseObject(JSON.toJSONString(resultHolder.getData()), parseClass);
} catch (Exception ignore) {
}
return null;
}
private MvcResult responsePost(String url, Object param) throws Exception {
return mockMvc.perform(MockMvcRequestBuilders.post(url)
.header(SessionConstants.HEADER_TOKEN, sessionId)
.header(SessionConstants.CSRF_TOKEN, csrfToken)
.content(JSON.toJSONString(param))
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON)).andReturn();
} }
public void initApiScenario() { public void initApiScenario() {
@ -177,6 +185,288 @@ public class ApiScenarioControllerTests extends BaseTest {
} }
} }
@Test
@Order(1)
public void add() throws Exception {
initTestData();
// @@请求成功
ApiScenarioAddRequest request = new ApiScenarioAddRequest();
request.setProjectId(DEFAULT_PROJECT_ID);
request.setDescription("desc");
request.setName("test name");
request.setModuleId("default");
request.setGrouped(false);
request.setEnvironmentId("environmentId");
request.setTags(List.of("tag1", "tag2"));
request.setPriority("P0");
request.setStatus(ApiScenarioStatus.COMPLETED.name());
List<ApiScenarioStepRequest> steps = getApiScenarioStepRequests();
Map<String, Object> steptDetailMap = new HashMap<>();
steptDetailMap.put(steps.get(1).getId(), getMsHttpElementParam());
steptDetailMap.put(steps.get(0).getId(), getMsHttpElementParam());
request.setSteps(steps);
request.setStepDetails(steptDetailMap);
request.setScenarioConfig(getScenarioConfig());
MvcResult mvcResult = this.requestPostWithOkAndReturn(DEFAULT_ADD, request);
ApiScenario resultData = getResultData(mvcResult, ApiScenario.class);
this.addApiScenario = apiScenarioMapper.selectByPrimaryKey(resultData.getId());
assertUpdateApiScenario(request, request.getScenarioConfig(), addApiScenario.getId());
assertUpdateSteps(steps, steptDetailMap);
request.setName("anOther name");
ApiScenarioStepRequest stepRequest = new ApiScenarioStepRequest();
stepRequest.setId(IDGenerator.nextStr());
stepRequest.setEnable(true);
stepRequest.setName(addApiScenario.getName());
stepRequest.setResourceId(addApiScenario.getId());
stepRequest.setRefType(ApiScenarioStepRefType.REF.name());
ApiScenarioStepRequest stepRequest2 = new ApiScenarioStepRequest();
stepRequest2.setId(IDGenerator.nextStr());
stepRequest2.setName(addApiScenario.getName());
stepRequest2.setResourceId(addApiScenario.getId());
stepRequest2.setRefType(ApiScenarioStepRefType.STEP_REF.name());
stepRequest2.setChildren(List.of(stepRequest));
steps = List.of(stepRequest, stepRequest2);
request.setSteps(steps);
mvcResult = this.requestPostWithOkAndReturn(DEFAULT_ADD, request);
this.anOtherAddApiScenario = apiScenarioMapper.selectByPrimaryKey(getResultData(mvcResult, ApiScenario.class).getId());
assertUpdateApiScenario(request, request.getScenarioConfig(), anOtherAddApiScenario.getId());
assertUpdateSteps(steps, steptDetailMap);
// @@重名校验异常
assertErrorCode(this.requestPost(DEFAULT_ADD, request), API_SCENARIO_EXIST);
// @@校验日志
checkLog(this.addApiScenario.getId(), OperationLogType.ADD);
// @@校验权限
requestPostPermissionTest(PermissionConstants.PROJECT_API_SCENARIO_ADD, DEFAULT_ADD, request);
}
private Object getMsHttpElementParam() {
return JSON.parseObject(ApiDataUtils.toJSONString(MsHTTPElementTest.getMsHttpElement()));
}
/**
* 校验更新数据
*
* @param request
* @param scenarioConfig
* @param id
* @return
* @throws Exception
*/
private ApiScenario assertUpdateApiScenario(Object request, ScenarioConfig scenarioConfig, String id) {
ApiScenario apiScenario = apiScenarioMapper.selectByPrimaryKey(id);
ApiScenarioBlob apiScenarioBlob = apiScenarioBlobMapper.selectByPrimaryKey(id);
ApiScenario copyApiScenario = BeanUtils.copyBean(new ApiScenario(), apiScenario);
copyApiScenario = BeanUtils.copyBean(copyApiScenario, request);
Assertions.assertEquals(apiScenario, copyApiScenario);
if (scenarioConfig != null) {
Assertions.assertEquals(scenarioConfig, JSON.parseObject(new String(apiScenarioBlob.getConfig()), ScenarioConfig.class));
}
return apiScenario;
}
private void assertUpdateSteps(List<ApiScenarioStepRequest> steps, Map<String, Object> steptDetailMap) {
for (ApiScenarioStepRequest step : steps) {
ApiScenarioStep apiScenarioStep = apiScenarioStepMapper.selectByPrimaryKey(step.getId());
ApiScenarioStep copyApiScenarioStep = BeanUtils.copyBean(new ApiScenarioStep(), apiScenarioStep);
copyApiScenarioStep = BeanUtils.copyBean(copyApiScenarioStep, step);
Assertions.assertEquals(apiScenarioStep, copyApiScenarioStep);
ApiScenarioStepBlob apiScenarioStepBlob = apiScenarioStepBlobMapper.selectByPrimaryKey(step.getId());
if (apiScenarioStepBlob != null && steptDetailMap.get(step.getId()) != null) {
Assertions.assertEquals(steptDetailMap.get(step.getId()), JSON.parseObject(new String(apiScenarioStepBlob.getContent())));
}
}
}
private ScenarioConfig getScenarioConfig() {
ScenarioConfig scenarioConfig = new ScenarioConfig();
MsAssertionConfig msAssertionConfig = new MsAssertionConfig();
MsScriptAssertion scriptAssertion = new MsScriptAssertion();
scriptAssertion.setScript("{}");
scriptAssertion.setName("script");
msAssertionConfig.setAssertions(List.of(scriptAssertion));
scenarioConfig.setAssertionConfig(msAssertionConfig);
ScenarioOtherConfig scenarioOtherConfig = new ScenarioOtherConfig();
scenarioOtherConfig.setStepWaitTime(1000);
scenarioOtherConfig.setFailureStrategy(ScenarioOtherConfig.FailureStrategy.CONTINUE.name());
scenarioOtherConfig.setEnableCookieShare(true);
scenarioConfig.setOtherConfig(scenarioOtherConfig);
return scenarioConfig;
}
private List<ApiScenarioStepRequest> getApiScenarioStepRequests() {
ApiScenarioStepRequest stepRequest = new ApiScenarioStepRequest();
stepRequest.setId(IDGenerator.nextStr());
stepRequest.setVersionId(extBaseProjectVersionMapper.getDefaultVersion(DEFAULT_PROJECT_ID));
stepRequest.setConfig(new HashMap<>());
stepRequest.setEnable(true);
stepRequest.setName(apiTestCase.getName());
stepRequest.setResourceId(apiTestCase.getId());
stepRequest.setRefType(ApiScenarioStepRefType.REF.name());
stepRequest.setConfig(new HashMap<>());
ApiScenarioStepRequest stepRequest2 = new ApiScenarioStepRequest();
stepRequest2.setId(IDGenerator.nextStr());
stepRequest2.setVersionId(extBaseProjectVersionMapper.getDefaultVersion(DEFAULT_PROJECT_ID));
stepRequest2.setConfig(new HashMap<>());
stepRequest2.setEnable(true);
stepRequest2.setResourceId(apiTestCase.getId());
stepRequest.setName(apiTestCase.getName() + "2");
stepRequest2.setRefType(ApiScenarioStepRefType.COPY.name());
ApiScenarioStepRequest stepRequest3 = new ApiScenarioStepRequest();
stepRequest3.setId(IDGenerator.nextStr());
stepRequest3.setVersionId(extBaseProjectVersionMapper.getDefaultVersion(DEFAULT_PROJECT_ID));
stepRequest3.setConfig(new HashMap<>());
stepRequest3.setEnable(true);
stepRequest3.setResourceId(apiDefinition.getId());
stepRequest.setName(apiDefinition.getName() + "3");
stepRequest3.setRefType(ApiScenarioStepRefType.REF.name());
return List.of(stepRequest, stepRequest2);
}
private void initTestData() {
ApiDefinitionAddRequest apiDefinitionAddRequest = new ApiDefinitionAddRequest();
apiDefinitionAddRequest.setName("test scenario");
apiDefinitionAddRequest.setProtocol("HTTP");
apiDefinitionAddRequest.setProjectId(DEFAULT_PROJECT_ID);
apiDefinitionAddRequest.setMethod("POST");
apiDefinitionAddRequest.setPath("/api/admin/posts");
apiDefinitionAddRequest.setStatus(ApiDefinitionStatus.PREPARE.getValue());
apiDefinitionAddRequest.setModuleId("default");
apiDefinitionAddRequest.setVersionId(extBaseProjectVersionMapper.getDefaultVersion(DEFAULT_PROJECT_ID));
apiDefinitionAddRequest.setDescription("描述内容");
apiDefinitionAddRequest.setName("test scenario");
MsHTTPElement msHttpElement = MsHTTPElementTest.getMsHttpElement();
apiDefinitionAddRequest.setRequest(ApiDataUtils.toJSONString(msHttpElement));
apiDefinitionAddRequest.setResponse("{}");
apiDefinition = apiDefinitionService.create(apiDefinitionAddRequest, "admin");
ApiTestCaseAddRequest apiTestCaseAddRequest = new ApiTestCaseAddRequest();
apiTestCaseAddRequest.setApiDefinitionId(apiDefinition.getId());
apiTestCaseAddRequest.setName("test");
apiTestCaseAddRequest.setProjectId(DEFAULT_PROJECT_ID);
apiTestCaseAddRequest.setPriority("P0");
apiTestCaseAddRequest.setStatus("Underway");
apiTestCaseAddRequest.setTags(new LinkedHashSet<>(List.of("tag1", "tag2")));
apiTestCaseAddRequest.setRequest(ApiDataUtils.toJSONString(msHttpElement));
apiTestCase = apiTestCaseService.addCase(apiTestCaseAddRequest, "admin");
}
@Test
@Order(2)
public void update() throws Exception {
// @@请求成功
ApiScenarioUpdateRequest request = new ApiScenarioUpdateRequest();
request.setId(addApiScenario.getId());
request.setDescription("desc update");
request.setName("test name update");
request.setModuleId("default");
request.setGrouped(false);
request.setEnvironmentId("environmentId update");
request.setTags(List.of("tag1 update", "tag2 update"));
request.setPriority("P0 update");
request.setStatus(ApiScenarioStatus.DEPRECATED.name());
List<ApiScenarioStepRequest> steps = getApiScenarioStepRequests();
// 验证修改基础信息
this.requestPostWithOk(DEFAULT_UPDATE, request);
assertUpdateApiScenario(request, request.getScenarioConfig(), addApiScenario.getId());
// 验证清除步骤
request.setSteps(List.of());
this.requestPostWithOk(DEFAULT_UPDATE, request);
assertUpdateApiScenario(request, request.getScenarioConfig(), addApiScenario.getId());
assertUpdateSteps(List.of(), new HashMap<>());
// 验证添加步骤
this.requestPostWithOk(DEFAULT_UPDATE, request);
Map<String, Object> steptDetailMap = new HashMap<>();
steptDetailMap.put(steps.get(0).getId(), getMsHttpElementParam());
steptDetailMap.put(steps.get(1).getId(), getMsHttpElementParam());
request.setSteps(steps);
request.setStepDetails(steptDetailMap);
request.setScenarioConfig(getScenarioConfig());
this.requestPostWithOk(DEFAULT_UPDATE, request);
assertUpdateSteps(steps, steptDetailMap);
// 验证修改步骤
steps.get(0).setName("test name update");
this.requestPostWithOk(DEFAULT_UPDATE, request);
assertUpdateSteps(steps, steptDetailMap);
// @@重名校验异常
request.setName(anOtherAddApiScenario.getName());
assertErrorCode(this.requestPost(DEFAULT_UPDATE, request), API_SCENARIO_EXIST);
// @@校验日志
checkLog(request.getId(), OperationLogType.UPDATE);
// @@校验权限
requestPostPermissionTest(PermissionConstants.PROJECT_API_SCENARIO_UPDATE, DEFAULT_UPDATE, request);
}
@Test
@Order(3)
public void deleteToGc() throws Exception {
// @@请求成功
this.requestGetWithOk(DELETE_TO_GC, addApiScenario.getId());
// todo 校验请求成功数据
// @@校验日志
checkLog(addApiScenario.getId(), OperationLogType.DELETE);
// @@校验权限
requestGetPermissionTest(PermissionConstants.PROJECT_API_SCENARIO_DELETE, DELETE_TO_GC, addApiScenario.getId());
}
@Test
@Order(4)
public void delete() throws Exception {
// @@请求成功
this.requestGetWithOk(DEFAULT_DELETE, addApiScenario.getId());
// todo 校验请求成功数据
// @@校验日志
checkLog(addApiScenario.getId(), OperationLogType.DELETE);
// @@校验权限
requestGetPermissionTest(PermissionConstants.PROJECT_API_SCENARIO_DELETE, DEFAULT_DELETE, addApiScenario.getId());
}
@Test
@Order(5)
public void uploadTempFile() throws Exception {
// @@请求成功
MockMultipartFile file = getMockMultipartFile();
String fileId = doUploadTempFile(file);
// 校验文件存在
FileRequest fileRequest = new FileRequest();
fileRequest.setFolder(DefaultRepositoryDir.getSystemTempDir() + "/" + fileId);
fileRequest.setFileName(file.getOriginalFilename());
Assertions.assertNotNull(FileCenter.getDefaultRepository().getFile(fileRequest));
requestUploadPermissionTest(PermissionConstants.PROJECT_API_SCENARIO_ADD, UPLOAD_TEMP_FILE, file);
requestUploadPermissionTest(PermissionConstants.PROJECT_API_SCENARIO_UPDATE, UPLOAD_TEMP_FILE, file);
}
private String doUploadTempFile(MockMultipartFile file) throws Exception {
return JSON.parseObject(requestUploadFileWithOkAndReturn(UPLOAD_TEMP_FILE, file)
.getResponse()
.getContentAsString(), ResultHolder.class)
.getData().toString();
}
private static MockMultipartFile getMockMultipartFile() {
MockMultipartFile file = new MockMultipartFile(
"file",
"file_upload.JPG",
MediaType.APPLICATION_OCTET_STREAM_VALUE,
"Hello, World!".getBytes()
);
return file;
}
@Test @Test
@Order(11) @Order(11)
public void page() throws Exception { public void page() throws Exception {
@ -186,8 +476,8 @@ public class ApiScenarioControllerTests extends BaseTest {
pageRequest.setProjectId(DEFAULT_PROJECT_ID); pageRequest.setProjectId(DEFAULT_PROJECT_ID);
pageRequest.setPageSize(10); pageRequest.setPageSize(10);
pageRequest.setCurrent(1); pageRequest.setCurrent(1);
MvcResult mvcResult = responsePost(PAGE, pageRequest); MvcResult mvcResult = requestPostAndReturn(DEFAULT_PAGE, pageRequest);
Pager<?> returnPager = parseObjectFromMvcResult(mvcResult, Pager.class); Pager<?> returnPager = getResultData(mvcResult, Pager.class);
//返回值不为空 //返回值不为空
Assertions.assertNotNull(returnPager); Assertions.assertNotNull(returnPager);
//返回值的页码和当前页码相同 //返回值的页码和当前页码相同
@ -197,8 +487,8 @@ public class ApiScenarioControllerTests extends BaseTest {
//查询api-scenario-id1的数据 //查询api-scenario-id1的数据
pageRequest.setScenarioId("api-scenario-id1"); pageRequest.setScenarioId("api-scenario-id1");
mvcResult = responsePost(PAGE, pageRequest); mvcResult = requestPostAndReturn(DEFAULT_PAGE, pageRequest);
returnPager = parseObjectFromMvcResult(mvcResult, Pager.class); returnPager = getResultData(mvcResult, Pager.class);
//返回值不为空 //返回值不为空
Assertions.assertNotNull(returnPager); Assertions.assertNotNull(returnPager);
//返回值的页码和当前页码相同 //返回值的页码和当前页码相同
@ -210,8 +500,8 @@ public class ApiScenarioControllerTests extends BaseTest {
//查询模块为moduleId1的数据 //查询模块为moduleId1的数据
pageRequest.setScenarioId(null); pageRequest.setScenarioId(null);
pageRequest.setModuleIds(List.of("scenario-moduleId")); pageRequest.setModuleIds(List.of("scenario-moduleId"));
mvcResult = responsePost(PAGE, pageRequest); mvcResult = requestPostAndReturn(DEFAULT_PAGE, pageRequest);
returnPager = parseObjectFromMvcResult(mvcResult, Pager.class); returnPager = getResultData(mvcResult, Pager.class);
//返回值不为空 //返回值不为空
Assertions.assertNotNull(returnPager); Assertions.assertNotNull(returnPager);
//返回值的页码和当前页码相同 //返回值的页码和当前页码相同
@ -223,7 +513,7 @@ public class ApiScenarioControllerTests extends BaseTest {
pageRequest.setSort(new HashMap<>() {{ pageRequest.setSort(new HashMap<>() {{
put("createTime", "asc"); put("createTime", "asc");
}}); }});
responsePost(PAGE, pageRequest); requestPostAndReturn(DEFAULT_PAGE, pageRequest);
pageRequest = new ApiScenarioPageRequest(); pageRequest = new ApiScenarioPageRequest();
pageRequest.setProjectId(DEFAULT_PROJECT_ID); pageRequest.setProjectId(DEFAULT_PROJECT_ID);
@ -233,8 +523,8 @@ public class ApiScenarioControllerTests extends BaseTest {
pageRequest.setFilter(new HashMap<>() {{ pageRequest.setFilter(new HashMap<>() {{
put("priority", List.of("P0")); put("priority", List.of("P0"));
}}); }});
mvcResult = responsePost(PAGE, pageRequest); mvcResult = requestPostAndReturn(DEFAULT_PAGE, pageRequest);
returnPager = parseObjectFromMvcResult(mvcResult, Pager.class); returnPager = getResultData(mvcResult, Pager.class);
//返回值不为空 //返回值不为空
Assertions.assertNotNull(returnPager); Assertions.assertNotNull(returnPager);
//返回值的页码和当前页码相同 //返回值的页码和当前页码相同
@ -243,7 +533,7 @@ public class ApiScenarioControllerTests extends BaseTest {
scenarioDTOS.forEach(scenario -> Assertions.assertEquals(scenario.getPriority(), "P0")); scenarioDTOS.forEach(scenario -> Assertions.assertEquals(scenario.getPriority(), "P0"));
//校验权限 //校验权限
requestPostPermissionTest(PermissionConstants.PROJECT_API_SCENARIO_READ, PAGE, pageRequest); requestPostPermissionTest(PermissionConstants.PROJECT_API_SCENARIO_READ, DEFAULT_PAGE, pageRequest);
} }
@Test @Test
@ -257,7 +547,7 @@ public class ApiScenarioControllerTests extends BaseTest {
Assertions.assertTrue(CollectionUtils.isNotEmpty(followers)); Assertions.assertTrue(CollectionUtils.isNotEmpty(followers));
// @@校验日志 // @@校验日志
checkLog("api-scenario-id1", OperationLogType.UPDATE); checkLog("api-scenario-id1", OperationLogType.UPDATE);
this.requestGet(FOLLOW + "111").andExpect(ERROR_REQUEST_MATCHER); assertErrorCode(this.requestGet(FOLLOW + "111"), NOT_FOUND);
// @@校验权限 // @@校验权限
requestGetPermissionTest(PermissionConstants.PROJECT_API_SCENARIO_UPDATE, FOLLOW + "api-scenario-id1"); requestGetPermissionTest(PermissionConstants.PROJECT_API_SCENARIO_UPDATE, FOLLOW + "api-scenario-id1");
@ -269,11 +559,9 @@ public class ApiScenarioControllerTests extends BaseTest {
Assertions.assertTrue(CollectionUtils.isEmpty(followers)); Assertions.assertTrue(CollectionUtils.isEmpty(followers));
// @@校验日志 // @@校验日志
checkLog("api-scenario-id1", OperationLogType.UPDATE); checkLog("api-scenario-id1", OperationLogType.UPDATE);
this.requestGet(FOLLOW + "111").andExpect(ERROR_REQUEST_MATCHER); assertErrorCode(this.requestGet(FOLLOW + "111"), NOT_FOUND);
} }
@Test @Test
@Order(13) @Order(13)
public void batchEdit() throws Exception { public void batchEdit() throws Exception {
@ -284,7 +572,7 @@ public class ApiScenarioControllerTests extends BaseTest {
request.setAppendTag(true); request.setAppendTag(true);
request.setSelectAll(true); request.setSelectAll(true);
request.setTags(new LinkedHashSet<>(List.of("tag1", "tag3", "tag4"))); request.setTags(new LinkedHashSet<>(List.of("tag1", "tag3", "tag4")));
responsePost(BATCH_EDIT, request); requestPostAndReturn(BATCH_EDIT, request);
ApiScenarioExample example = new ApiScenarioExample(); ApiScenarioExample example = new ApiScenarioExample();
List<String> ids = extApiScenarioMapper.getIds(request, false); List<String> ids = extApiScenarioMapper.getIds(request, false);
example.createCriteria().andProjectIdEqualTo(DEFAULT_PROJECT_ID).andDeletedEqualTo(false).andIdIn(ids); example.createCriteria().andProjectIdEqualTo(DEFAULT_PROJECT_ID).andDeletedEqualTo(false).andIdIn(ids);
@ -296,7 +584,7 @@ public class ApiScenarioControllerTests extends BaseTest {
//覆盖标签 //覆盖标签
request.setTags(new LinkedHashSet<>(List.of("tag1"))); request.setTags(new LinkedHashSet<>(List.of("tag1")));
request.setAppendTag(false); request.setAppendTag(false);
responsePost(BATCH_EDIT, request); requestPostAndReturn(BATCH_EDIT, request);
apiScenarioMapper.selectByExample(example).forEach(scenario -> { apiScenarioMapper.selectByExample(example).forEach(scenario -> {
Assertions.assertEquals(scenario.getTags(), List.of("tag1")); Assertions.assertEquals(scenario.getTags(), List.of("tag1"));
}); });
@ -311,7 +599,7 @@ public class ApiScenarioControllerTests extends BaseTest {
List<String> apiIdList = apiScenarioList.stream().map(ApiScenario::getId).toList(); List<String> apiIdList = apiScenarioList.stream().map(ApiScenario::getId).toList();
request.setSelectIds(apiIdList); request.setSelectIds(apiIdList);
request.setExcludeIds(apiIdList); request.setExcludeIds(apiIdList);
responsePost(BATCH_EDIT, request); requestPostAndReturn(BATCH_EDIT, request);
//优先级 //优先级
request.setTags(new LinkedHashSet<>()); request.setTags(new LinkedHashSet<>());
@ -320,7 +608,7 @@ public class ApiScenarioControllerTests extends BaseTest {
request.setModuleIds(List.of("scenario-moduleId")); request.setModuleIds(List.of("scenario-moduleId"));
request.setPriority("P3"); request.setPriority("P3");
request.setExcludeIds(new ArrayList<>()); request.setExcludeIds(new ArrayList<>());
responsePost(BATCH_EDIT, request); requestPostAndReturn(BATCH_EDIT, request);
//判断数据的优先级是不是P3 //判断数据的优先级是不是P3
example.clear(); example.clear();
example.createCriteria().andProjectIdEqualTo(DEFAULT_PROJECT_ID).andDeletedEqualTo(false); example.createCriteria().andProjectIdEqualTo(DEFAULT_PROJECT_ID).andDeletedEqualTo(false);
@ -335,14 +623,14 @@ public class ApiScenarioControllerTests extends BaseTest {
request.setSelectAll(false); request.setSelectAll(false);
request.setSelectIds(List.of("api-scenario-id1")); request.setSelectIds(List.of("api-scenario-id1"));
request.setExcludeIds(List.of("api-scenario-id1")); request.setExcludeIds(List.of("api-scenario-id1"));
responsePost(BATCH_EDIT, request); requestPostAndReturn(BATCH_EDIT, request);
//状态 //状态
request.setPriority(null); request.setPriority(null);
request.setType("Status"); request.setType("Status");
request.setStatus("Completed"); request.setStatus("Completed");
request.setSelectAll(true); request.setSelectAll(true);
request.setExcludeIds(new ArrayList<>()); request.setExcludeIds(new ArrayList<>());
responsePost(BATCH_EDIT, request); requestPostAndReturn(BATCH_EDIT, request);
//判断数据的状态是不是Completed //判断数据的状态是不是Completed
apiScenarios = apiScenarioMapper.selectByExample(example); apiScenarios = apiScenarioMapper.selectByExample(example);
apiScenarios.forEach(apiTestCase -> Assertions.assertEquals(apiTestCase.getStatus(), "Completed")); apiScenarios.forEach(apiTestCase -> Assertions.assertEquals(apiTestCase.getStatus(), "Completed"));
@ -356,7 +644,7 @@ public class ApiScenarioControllerTests extends BaseTest {
environmentExample.createCriteria().andProjectIdEqualTo(DEFAULT_PROJECT_ID).andMockEqualTo(true); environmentExample.createCriteria().andProjectIdEqualTo(DEFAULT_PROJECT_ID).andMockEqualTo(true);
List<Environment> environments = environmentMapper.selectByExample(environmentExample); List<Environment> environments = environmentMapper.selectByExample(environmentExample);
request.setEnvId(environments.get(0).getId()); request.setEnvId(environments.get(0).getId());
responsePost(BATCH_EDIT, request); requestPostAndReturn(BATCH_EDIT, request);
//判断数据的环境是不是environments.get(0).getId() //判断数据的环境是不是environments.get(0).getId()
apiScenarios = apiScenarioMapper.selectByExample(example); apiScenarios = apiScenarioMapper.selectByExample(example);
apiScenarios.forEach(apiTestCase -> Assertions.assertEquals(apiTestCase.getEnvironmentId(), environments.get(0).getId())); apiScenarios.forEach(apiTestCase -> Assertions.assertEquals(apiTestCase.getEnvironmentId(), environments.get(0).getId()));
@ -370,7 +658,7 @@ public class ApiScenarioControllerTests extends BaseTest {
//环境组 //环境组
request.setGrouped(true); request.setGrouped(true);
request.setGroupId("scenario-environment-group-id"); request.setGroupId("scenario-environment-group-id");
responsePost(BATCH_EDIT, request); requestPostAndReturn(BATCH_EDIT, request);
apiScenarios = apiScenarioMapper.selectByExample(example); apiScenarios = apiScenarioMapper.selectByExample(example);
apiScenarios.forEach(apiTestCase -> { apiScenarios.forEach(apiTestCase -> {
Assertions.assertEquals(apiTestCase.getGrouped(), true); Assertions.assertEquals(apiTestCase.getGrouped(), true);
@ -402,8 +690,8 @@ public class ApiScenarioControllerTests extends BaseTest {
pageRequest.setProjectId(DEFAULT_PROJECT_ID); pageRequest.setProjectId(DEFAULT_PROJECT_ID);
pageRequest.setPageSize(10); pageRequest.setPageSize(10);
pageRequest.setCurrent(1); pageRequest.setCurrent(1);
MvcResult mvcResult = responsePost(TRASH_PAGE, pageRequest); MvcResult mvcResult = requestPostAndReturn(TRASH_PAGE, pageRequest);
Pager<?> returnPager = parseObjectFromMvcResult(mvcResult, Pager.class); Pager<?> returnPager = getResultData(mvcResult, Pager.class);
//返回值不为空 //返回值不为空
Assertions.assertNotNull(returnPager); Assertions.assertNotNull(returnPager);
//返回值的页码和当前页码相同 //返回值的页码和当前页码相同
@ -413,8 +701,8 @@ public class ApiScenarioControllerTests extends BaseTest {
//查询api-scenario-id1的数据 //查询api-scenario-id1的数据
pageRequest.setScenarioId("trash-api-scenario-id1"); pageRequest.setScenarioId("trash-api-scenario-id1");
mvcResult = responsePost(TRASH_PAGE, pageRequest); mvcResult = requestPostAndReturn(TRASH_PAGE, pageRequest);
returnPager = parseObjectFromMvcResult(mvcResult, Pager.class); returnPager = getResultData(mvcResult, Pager.class);
//返回值不为空 //返回值不为空
Assertions.assertNotNull(returnPager); Assertions.assertNotNull(returnPager);
//返回值的页码和当前页码相同 //返回值的页码和当前页码相同
@ -426,8 +714,8 @@ public class ApiScenarioControllerTests extends BaseTest {
//查询模块为moduleId1的数据 //查询模块为moduleId1的数据
pageRequest.setScenarioId(null); pageRequest.setScenarioId(null);
pageRequest.setModuleIds(List.of("scenario-moduleId-trash")); pageRequest.setModuleIds(List.of("scenario-moduleId-trash"));
mvcResult = responsePost(TRASH_PAGE, pageRequest); mvcResult = requestPostAndReturn(TRASH_PAGE, pageRequest);
returnPager = parseObjectFromMvcResult(mvcResult, Pager.class); returnPager = getResultData(mvcResult, Pager.class);
//返回值不为空 //返回值不为空
Assertions.assertNotNull(returnPager); Assertions.assertNotNull(returnPager);
//返回值的页码和当前页码相同 //返回值的页码和当前页码相同
@ -439,10 +727,8 @@ public class ApiScenarioControllerTests extends BaseTest {
pageRequest.setSort(new HashMap<>() {{ pageRequest.setSort(new HashMap<>() {{
put("createTime", "asc"); put("createTime", "asc");
}}); }});
responsePost(TRASH_PAGE, pageRequest); requestPostAndReturn(TRASH_PAGE, pageRequest);
//校验权限 //校验权限
requestPostPermissionTest(PermissionConstants.PROJECT_API_SCENARIO_READ, TRASH_PAGE, pageRequest); requestPostPermissionTest(PermissionConstants.PROJECT_API_SCENARIO_READ, TRASH_PAGE, pageRequest);
} }
} }

View File

@ -1,5 +1,6 @@
package io.metersphere.api.controller; package io.metersphere.api.controller;
import io.metersphere.api.dto.ApiFile;
import io.metersphere.api.dto.definition.HttpResponse; import io.metersphere.api.dto.definition.HttpResponse;
import io.metersphere.api.dto.request.MsCommonElement; import io.metersphere.api.dto.request.MsCommonElement;
import io.metersphere.api.dto.request.assertion.*; import io.metersphere.api.dto.request.assertion.*;
@ -64,7 +65,7 @@ public class MsHTTPElementTest {
formDataKV.setValue("@email"); formDataKV.setValue("@email");
formDataKV.setKey("key"); formDataKV.setKey("key");
FormDataKV formDataFileKV = new FormDataKV(); FormDataKV formDataFileKV = new FormDataKV();
BodyFile bodyFile = new BodyFile(); ApiFile bodyFile = new ApiFile();
bodyFile.setFileId("aaa"); bodyFile.setFileId("aaa");
bodyFile.setFileName("aaa"); bodyFile.setFileName("aaa");
formDataFileKV.setFiles(List.of(bodyFile)); formDataFileKV.setFiles(List.of(bodyFile));

View File

@ -34,6 +34,7 @@ import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.function.Function;
@Service @Service
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
@ -59,6 +60,8 @@ public class ProjectService {
@Resource @Resource
private TestResourcePoolOrganizationMapper testResourcePoolOrganizationMapper; private TestResourcePoolOrganizationMapper testResourcePoolOrganizationMapper;
public static final Long ORDER_STEP = 5000L;
public List<Project> getUserProject(String organizationId, String userId) { public List<Project> getUserProject(String organizationId, String userId) {
if (organizationMapper.selectByPrimaryKey(organizationId) == null) { if (organizationMapper.selectByPrimaryKey(organizationId) == null) {
@ -199,4 +202,9 @@ public class ProjectService {
projectVersionExample.createCriteria().andProjectIdEqualTo(projectId); projectVersionExample.createCriteria().andProjectIdEqualTo(projectId);
return projectVersionMapper.selectByExample(projectVersionExample).get(0); return projectVersionMapper.selectByExample(projectVersionExample).get(0);
} }
public Long getNextOrder(Function<String, Long> getLastPosFunc, String projectId) {
Long pos = getLastPosFunc.apply(projectId);
return (pos == null ? 0 : pos) + ORDER_STEP;
}
} }

View File

@ -53,4 +53,19 @@
JSON_CONTAINS(`value`, JSON_ARRAY(#{value})) JSON_CONTAINS(`value`, JSON_ARRAY(#{value}))
</foreach> </foreach>
</sql> </sql>
<sql id="filters">
<if test="${filter} != null and ${filter}.size() > 0">
<foreach collection="${filter}.entrySet()" index="key" item="values">
<if test="values != null and values.size() > 0 fields.contains(key)" >
<where>
${table_name}.${key} in
<foreach collection="values" item="value" separator="," open="(" close=")">
#{value}
</foreach>
</where>
</if>
</foreach>
</if>
</sql>
</mapper> </mapper>

View File

@ -87,6 +87,7 @@ public abstract class BaseTest {
private MinioClient client; private MinioClient client;
protected static final String DEFAULT_LIST = "list"; protected static final String DEFAULT_LIST = "list";
protected static final String DEFAULT_PAGE = "page";
protected static final String DEFAULT_GET = "get/{0}"; protected static final String DEFAULT_GET = "get/{0}";
protected static final String DEFAULT_ADD = "add"; protected static final String DEFAULT_ADD = "add";
protected static final String DEFAULT_UPDATE = "update"; protected static final String DEFAULT_UPDATE = "update";