feat(接口测试): 场景csv功能

This commit is contained in:
AgAngle 2024-05-15 18:10:34 +08:00 committed by Craftsman
parent 21fbfee0b4
commit b74eef8a6c
30 changed files with 545 additions and 307 deletions

View File

@ -25,12 +25,18 @@ public class ApiScenarioCsvStep implements Serializable {
@Size(min = 1, max = 50, message = "{api_scenario_csv_step.step_id.length_range}", groups = {Created.class, Updated.class}) @Size(min = 1, max = 50, message = "{api_scenario_csv_step.step_id.length_range}", groups = {Created.class, Updated.class})
private String stepId; private String stepId;
@Schema(description = "场景ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{api_scenario_csv_step.scenario_id.not_blank}", groups = {Created.class})
@Size(min = 1, max = 50, message = "{api_scenario_csv_step.scenario_id.length_range}", groups = {Created.class, Updated.class})
private String scenarioId;
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
public enum Column { public enum Column {
id("id", "id", "VARCHAR", false), id("id", "id", "VARCHAR", false),
fileId("file_id", "fileId", "VARCHAR", false), fileId("file_id", "fileId", "VARCHAR", false),
stepId("step_id", "stepId", "VARCHAR", false); stepId("step_id", "stepId", "VARCHAR", false),
scenarioId("scenario_id", "scenarioId", "VARCHAR", false);
private static final String BEGINNING_DELIMITER = "`"; private static final String BEGINNING_DELIMITER = "`";

View File

@ -313,6 +313,76 @@ public class ApiScenarioCsvStepExample {
addCriterion("step_id not between", value1, value2, "stepId"); addCriterion("step_id not between", value1, value2, "stepId");
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

@ -5,6 +5,7 @@
<id column="id" jdbcType="VARCHAR" property="id" /> <id column="id" jdbcType="VARCHAR" property="id" />
<result column="file_id" jdbcType="VARCHAR" property="fileId" /> <result column="file_id" jdbcType="VARCHAR" property="fileId" />
<result column="step_id" jdbcType="VARCHAR" property="stepId" /> <result column="step_id" jdbcType="VARCHAR" property="stepId" />
<result column="scenario_id" jdbcType="VARCHAR" property="scenarioId" />
</resultMap> </resultMap>
<sql id="Example_Where_Clause"> <sql id="Example_Where_Clause">
<where> <where>
@ -65,7 +66,7 @@
</where> </where>
</sql> </sql>
<sql id="Base_Column_List"> <sql id="Base_Column_List">
id, file_id, step_id id, file_id, step_id, scenario_id
</sql> </sql>
<select id="selectByExample" parameterType="io.metersphere.api.domain.ApiScenarioCsvStepExample" resultMap="BaseResultMap"> <select id="selectByExample" parameterType="io.metersphere.api.domain.ApiScenarioCsvStepExample" resultMap="BaseResultMap">
select select
@ -98,10 +99,10 @@
</if> </if>
</delete> </delete>
<insert id="insert" parameterType="io.metersphere.api.domain.ApiScenarioCsvStep"> <insert id="insert" parameterType="io.metersphere.api.domain.ApiScenarioCsvStep">
insert into api_scenario_csv_step (id, file_id, step_id insert into api_scenario_csv_step (id, file_id, step_id,
) scenario_id)
values (#{id,jdbcType=VARCHAR}, #{fileId,jdbcType=VARCHAR}, #{stepId,jdbcType=VARCHAR} values (#{id,jdbcType=VARCHAR}, #{fileId,jdbcType=VARCHAR}, #{stepId,jdbcType=VARCHAR},
) #{scenarioId,jdbcType=VARCHAR})
</insert> </insert>
<insert id="insertSelective" parameterType="io.metersphere.api.domain.ApiScenarioCsvStep"> <insert id="insertSelective" parameterType="io.metersphere.api.domain.ApiScenarioCsvStep">
insert into api_scenario_csv_step insert into api_scenario_csv_step
@ -115,6 +116,9 @@
<if test="stepId != null"> <if test="stepId != null">
step_id, step_id,
</if> </if>
<if test="scenarioId != null">
scenario_id,
</if>
</trim> </trim>
<trim prefix="values (" suffix=")" suffixOverrides=","> <trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="id != null"> <if test="id != null">
@ -126,6 +130,9 @@
<if test="stepId != null"> <if test="stepId != null">
#{stepId,jdbcType=VARCHAR}, #{stepId,jdbcType=VARCHAR},
</if> </if>
<if test="scenarioId != null">
#{scenarioId,jdbcType=VARCHAR},
</if>
</trim> </trim>
</insert> </insert>
<select id="countByExample" parameterType="io.metersphere.api.domain.ApiScenarioCsvStepExample" resultType="java.lang.Long"> <select id="countByExample" parameterType="io.metersphere.api.domain.ApiScenarioCsvStepExample" resultType="java.lang.Long">
@ -146,6 +153,9 @@
<if test="record.stepId != null"> <if test="record.stepId != null">
step_id = #{record.stepId,jdbcType=VARCHAR}, step_id = #{record.stepId,jdbcType=VARCHAR},
</if> </if>
<if test="record.scenarioId != null">
scenario_id = #{record.scenarioId,jdbcType=VARCHAR},
</if>
</set> </set>
<if test="_parameter != null"> <if test="_parameter != null">
<include refid="Update_By_Example_Where_Clause" /> <include refid="Update_By_Example_Where_Clause" />
@ -155,7 +165,8 @@
update api_scenario_csv_step update api_scenario_csv_step
set id = #{record.id,jdbcType=VARCHAR}, set id = #{record.id,jdbcType=VARCHAR},
file_id = #{record.fileId,jdbcType=VARCHAR}, file_id = #{record.fileId,jdbcType=VARCHAR},
step_id = #{record.stepId,jdbcType=VARCHAR} step_id = #{record.stepId,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>
@ -169,22 +180,26 @@
<if test="stepId != null"> <if test="stepId != null">
step_id = #{stepId,jdbcType=VARCHAR}, step_id = #{stepId,jdbcType=VARCHAR},
</if> </if>
<if test="scenarioId != null">
scenario_id = #{scenarioId,jdbcType=VARCHAR},
</if>
</set> </set>
where id = #{id,jdbcType=VARCHAR} where id = #{id,jdbcType=VARCHAR}
</update> </update>
<update id="updateByPrimaryKey" parameterType="io.metersphere.api.domain.ApiScenarioCsvStep"> <update id="updateByPrimaryKey" parameterType="io.metersphere.api.domain.ApiScenarioCsvStep">
update api_scenario_csv_step update api_scenario_csv_step
set file_id = #{fileId,jdbcType=VARCHAR}, set file_id = #{fileId,jdbcType=VARCHAR},
step_id = #{stepId,jdbcType=VARCHAR} step_id = #{stepId,jdbcType=VARCHAR},
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_csv_step insert into api_scenario_csv_step
(id, file_id, step_id) (id, file_id, step_id, scenario_id)
values values
<foreach collection="list" item="item" separator=","> <foreach collection="list" item="item" separator=",">
(#{item.id,jdbcType=VARCHAR}, #{item.fileId,jdbcType=VARCHAR}, #{item.stepId,jdbcType=VARCHAR} (#{item.id,jdbcType=VARCHAR}, #{item.fileId,jdbcType=VARCHAR}, #{item.stepId,jdbcType=VARCHAR},
) #{item.scenarioId,jdbcType=VARCHAR})
</foreach> </foreach>
</insert> </insert>
<insert id="batchInsertSelective" parameterType="map"> <insert id="batchInsertSelective" parameterType="map">
@ -206,6 +221,9 @@
<if test="'step_id'.toString() == column.value"> <if test="'step_id'.toString() == column.value">
#{item.stepId,jdbcType=VARCHAR} #{item.stepId,jdbcType=VARCHAR}
</if> </if>
<if test="'scenario_id'.toString() == column.value">
#{item.scenarioId,jdbcType=VARCHAR}
</if>
</foreach> </foreach>
) )
</foreach> </foreach>

View File

@ -127,6 +127,10 @@ CREATE TABLE IF NOT EXISTS test_plan_report_bug(
CREATE UNIQUE INDEX idx_test_plan_report_id ON test_plan_report_bug(test_plan_report_id); CREATE UNIQUE INDEX idx_test_plan_report_id ON test_plan_report_bug(test_plan_report_id);
-- 场景步骤 csv 表增加场景ID字段
ALTER TABLE api_scenario_csv_step ADD scenario_id varchar(50) NOT NULL COMMENT '场景ID';
CREATE INDEX idx_scenario_id USING BTREE ON api_scenario_csv_step (scenario_id);
-- set innodb lock wait timeout to default -- set innodb lock wait timeout to default
SET SESSION innodb_lock_wait_timeout = DEFAULT; SET SESSION innodb_lock_wait_timeout = DEFAULT;

View File

@ -9,6 +9,8 @@ import org.apache.jorphan.collections.HashTree;
import java.lang.reflect.ParameterizedType; import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function; import java.util.function.Function;
/** /**
@ -22,10 +24,25 @@ public abstract class AbstractJmeterElementConverter<T extends MsTestElement> im
/** /**
* 获取转换器的函数 * 获取转换器的函数
* 主应用在实例化转换器的时候会设置
*/ */
@Setter @Setter
private Function<Class<? extends MsTestElement>, AbstractJmeterElementConverter> getConverterFunc; private static Function<Class<? extends MsTestElement>, AbstractJmeterElementConverter> getConverterFunc;
/**
* 解析子步骤前的前置处理函数
*/
private static List<AbstractJmeterElementConverter> childPreConverters = new ArrayList<>();
/**
* 解析子步骤前的前置处理函数
*/
private static List<AbstractJmeterElementConverter> childPostConverters = new ArrayList<>();
public static void registerChildPreConverters(AbstractJmeterElementConverter converter) {
childPreConverters.add(converter);
}
public static void registerChildPostConverters(AbstractJmeterElementConverter converter) {
childPostConverters.add(converter);
}
public AbstractJmeterElementConverter() { public AbstractJmeterElementConverter() {
Type genericSuperclass = getClass().getGenericSuperclass(); Type genericSuperclass = getClass().getGenericSuperclass();
@ -45,10 +62,14 @@ public abstract class AbstractJmeterElementConverter<T extends MsTestElement> im
*/ */
public void parseChild(HashTree tree, AbstractMsTestElement element, ParameterConfig config) { public void parseChild(HashTree tree, AbstractMsTestElement element, ParameterConfig config) {
if (element != null && element.getChildren() != null) { if (element != null && element.getChildren() != null) {
// 解析子步骤前的前置处理函数
childPreConverters.forEach(processor -> processor.toHashTree(tree, element, config));
element.getChildren().forEach(child -> { element.getChildren().forEach(child -> {
child.setParent(element); child.setParent(element);
getConverterFunc.apply(child.getClass()).toHashTree(tree, child, config); getConverterFunc.apply(child.getClass()).toHashTree(tree, child, config);
}); });
// 解析子步骤后的后置处理函数
childPostConverters.forEach(processor -> processor.toHashTree(tree, element, config));
} }
} }

View File

@ -5,6 +5,7 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo;
import lombok.Data; import lombok.Data;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List;
/** /**
* *
@ -50,4 +51,8 @@ public abstract class AbstractMsTestElement implements MsTestElement {
* 父组件 * 父组件
*/ */
private AbstractMsTestElement parent; private AbstractMsTestElement parent;
/**
* 关联的 csv ID
*/
private List<String> csvIds;
} }

View File

@ -1,5 +1,6 @@
package io.metersphere.api.dto; package io.metersphere.api.dto;
import io.metersphere.api.dto.scenario.CsvVariable;
import io.metersphere.plugin.api.dto.ParameterConfig; import io.metersphere.plugin.api.dto.ParameterConfig;
import io.metersphere.plugin.api.spi.AbstractMsTestElement; import io.metersphere.plugin.api.spi.AbstractMsTestElement;
import io.metersphere.project.dto.environment.EnvironmentInfoDTO; import io.metersphere.project.dto.environment.EnvironmentInfoDTO;
@ -80,4 +81,13 @@ public class ApiParamConfig extends ParameterConfig {
public EnvironmentInfoDTO getEnvConfig(String projectId) { public EnvironmentInfoDTO getEnvConfig(String projectId) {
return getEnvConfig(); return getEnvConfig();
} }
/**
* 获取 csv 信息
* @param csvId
* @return
*/
public CsvVariable getCsvVariable(String csvId) {
return null;
}
} }

View File

@ -1,11 +1,13 @@
package io.metersphere.api.dto; package io.metersphere.api.dto;
import io.metersphere.api.dto.scenario.CsvVariable;
import io.metersphere.api.dto.scenario.ScenarioConfig; import io.metersphere.api.dto.scenario.ScenarioConfig;
import io.metersphere.plugin.api.spi.AbstractMsTestElement; import io.metersphere.plugin.api.spi.AbstractMsTestElement;
import io.metersphere.project.dto.environment.EnvironmentInfoDTO; import io.metersphere.project.dto.environment.EnvironmentInfoDTO;
import lombok.Data; import lombok.Data;
import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.BooleanUtils;
import java.util.HashMap;
import java.util.Map; import java.util.Map;
/** /**
@ -31,6 +33,13 @@ public class ApiScenarioParamConfig extends ApiParamConfig {
*/ */
private Boolean grouped; private Boolean grouped;
/**
* csv Map
* key csv ID
* value csv 的信息
*/
private Map<String, CsvVariable> csvMap = new HashMap<>(0);
@Override @Override
public Map<String, Object> getProtocolEnvConfig(AbstractMsTestElement msTestElement) { public Map<String, Object> getProtocolEnvConfig(AbstractMsTestElement msTestElement) {
if (BooleanUtils.isTrue(grouped)) { if (BooleanUtils.isTrue(grouped)) {
@ -53,4 +62,14 @@ public class ApiScenarioParamConfig extends ApiParamConfig {
return getEnvConfig(); return getEnvConfig();
} }
} }
/**
* 获取 csv 信息
* @param csvId
* @return
*/
@Override
public CsvVariable getCsvVariable(String csvId) {
return csvMap.get(csvId);
}
} }

View File

@ -59,6 +59,7 @@ public class ApiScenarioAddRequest {
private String environmentId; private String environmentId;
@Schema(description = "场景的通用配置") @Schema(description = "场景的通用配置")
@Valid
private ScenarioConfig scenarioConfig = new ScenarioConfig(); private ScenarioConfig scenarioConfig = new ScenarioConfig();
@Schema(description = "步骤集合") @Schema(description = "步骤集合")
@ -80,10 +81,4 @@ public class ApiScenarioAddRequest {
*/ */
@Schema(description = "步骤文件操作相关参数") @Schema(description = "步骤文件操作相关参数")
private Map<String, ResourceAddFileParam> stepFileParam; private Map<String, ResourceAddFileParam> stepFileParam;
/**
* 步骤文件操作相关参数
*/
@Schema(description = "场景文件操作相关参数")
private ResourceAddFileParam fileParam;
} }

View File

@ -11,7 +11,7 @@ import java.util.List;
@EqualsAndHashCode(callSuper = false) @EqualsAndHashCode(callSuper = false)
public class ApiScenarioDetail extends ApiScenario { public class ApiScenarioDetail extends ApiScenario {
@Schema(description = "场景的通用配置") @Schema(description = "场景的通用配置")
private ScenarioConfig scenarioConfig; private ScenarioConfig scenarioConfig = new ScenarioConfig();
@Schema(description = "步骤") @Schema(description = "步骤")
private List<ApiScenarioStepDTO> steps; private List<ApiScenarioStepDTO> steps;
} }

View File

@ -56,8 +56,8 @@ public class ApiScenarioStepCommonDTO<T extends ApiScenarioStepCommonDTO> {
@Schema(description = "循环等组件基础数据") @Schema(description = "循环等组件基础数据")
private Object config; private Object config;
@Schema(description = "csv文件id集合") @Schema(description = "csv id集合")
private List<String> csvFileIds; private List<String> csvIds;
@Schema(description = "项目fk") @Schema(description = "项目fk")
@NotBlank @NotBlank

View File

@ -83,10 +83,4 @@ public class ApiScenarioUpdateRequest {
*/ */
@Schema(description = "步骤文件操作相关参数") @Schema(description = "步骤文件操作相关参数")
private Map<String, ResourceUpdateFileParam> stepFileParam; private Map<String, ResourceUpdateFileParam> stepFileParam;
/**
* 步骤文件操作相关参数
*/
@Schema(description = "场景文件操作相关参数")
private ResourceUpdateFileParam fileParam;
} }

View File

@ -1,5 +1,7 @@
package io.metersphere.api.dto.scenario; package io.metersphere.api.dto.scenario;
import io.metersphere.sdk.constants.ValueEnum;
import io.metersphere.system.valid.EnumValue;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size; import jakarta.validation.constraints.Size;
@ -14,50 +16,51 @@ import lombok.Data;
public class CsvVariable { public class CsvVariable {
@Schema(description = "id") @Schema(description = "id")
@NotBlank
@Size(max = 50)
private String id; private String id;
@Schema(description = "文件id/引用文件id", requiredMode = Schema.RequiredMode.REQUIRED) @Schema(description = "文件id/引用文件id", requiredMode = Schema.RequiredMode.REQUIRED)
@Size(min = 1, max = 50, message = "{api_scenario_csv.file_id.length_range}") @Size(max = 50, message = "{api_scenario_csv.file_id.length_range}")
@NotBlank
private String fileId; private String fileId;
@Schema(description = "场景id", requiredMode = Schema.RequiredMode.REQUIRED)
@Size(min = 1, max = 50, message = "{api_scenario_csv.scenario_id.length_range}")
private String scenarioId;
@Schema(description = "csv变量名称", requiredMode = Schema.RequiredMode.REQUIRED)
@Size(min = 1, max = 255, message = "{api_scenario_csv.name.length_range}")
private String name;
@Schema(description = "文件名称") @Schema(description = "文件名称")
@NotBlank
private String fileName; private String fileName;
@Schema(description = "作用域 SCENARIO/STEP") @Schema(description = "是否是关联文件")
private Boolean association = false;
@Schema(description = "csv变量名称", requiredMode = Schema.RequiredMode.REQUIRED)
@Size(max = 255, message = "{api_scenario_csv.name.length_range}")
private String name;
/** /**
* @see CsvVariableScope * @see CsvVariableScope
*/ */
@Schema(description = "作用域 SCENARIO/STEP")
@NotBlank(message = "{api_scenario_csv.scope.not_blank}") @NotBlank(message = "{api_scenario_csv.scope.not_blank}")
@Size(min = 1, max = 50, message = "{api_scenario_csv.scope.length_range}") @Size(min = 1, max = 50, message = "{api_scenario_csv.scope.length_range}")
private String scope; @EnumValue(enumClass = CsvVariableScope.class)
private String scope = CsvVariableScope.SCENARIO.name();
@Schema(description = "启用/禁用") @Schema(description = "启用/禁用")
private Boolean enable = true; private Boolean enable = true;
@Schema(description = "是否引用")
private Boolean association = false;
@Schema(description = "文件编码")
/** /**
* 文件编码 * 文件编码
*
* @see CsvEncodingType * @see CsvEncodingType
*/ */
@Schema(description = "文件编码 UTF-8/UTF-16/ISO-8859-15/US-ASCII")
@Size(max = 50, message = "{api_scenario_csv.encoding.length_range}") @Size(max = 50, message = "{api_scenario_csv.encoding.length_range}")
private String encoding; @EnumValue(enumClass = CsvEncodingType.class)
private String encoding = CsvEncodingType.UTF8.getValue();
@Schema(description = "是否随机") @Schema(description = "是否随机")
private Boolean random = false; private Boolean random = false;
@Schema(description = "变量名称(西文逗号间隔)") @Schema(description = "变量名称(文逗号间隔)")
@Size(max = 255, message = "{api_scenario_csv.variable_names.length_range}") @Size(max = 255, message = "{api_scenario_csv.variable_names.length_range}")
private String variableNames; private String variableNames;
@ -78,7 +81,7 @@ public class CsvVariable {
private Boolean stopThreadOnEof = false; private Boolean stopThreadOnEof = false;
public enum CsvEncodingType { public enum CsvEncodingType implements ValueEnum {
UTF8("UTF-8"), UFT16("UTF-16"), ISO885915("ISO-8859-15"), US_ASCII("US-ASCII"); UTF8("UTF-8"), UFT16("UTF-16"), ISO885915("ISO-8859-15"), US_ASCII("US-ASCII");
private String value; private String value;
@ -86,6 +89,7 @@ public class CsvVariable {
this.value = value; this.value = value;
} }
@Override
public String getValue() { public String getValue() {
return value; return value;
} }

View File

@ -2,6 +2,7 @@ package io.metersphere.api.dto.scenario;
import io.metersphere.api.dto.assertion.MsScenarioAssertionConfig; import io.metersphere.api.dto.assertion.MsScenarioAssertionConfig;
import io.metersphere.api.dto.request.processors.MsProcessorConfig; import io.metersphere.api.dto.request.processors.MsProcessorConfig;
import jakarta.validation.Valid;
import lombok.Data; import lombok.Data;
/** /**
@ -13,6 +14,7 @@ public class ScenarioConfig {
/** /**
* 场景变量 * 场景变量
*/ */
@Valid
private ScenarioVariable variable = new ScenarioVariable(); private ScenarioVariable variable = new ScenarioVariable();
/** /**
* 前置处理器配置 * 前置处理器配置

View File

@ -1,6 +1,7 @@
package io.metersphere.api.dto.scenario; package io.metersphere.api.dto.scenario;
import io.metersphere.project.dto.environment.variables.CommonVariables; import io.metersphere.project.dto.environment.variables.CommonVariables;
import jakarta.validation.Valid;
import lombok.Data; import lombok.Data;
import java.util.ArrayList; import java.util.ArrayList;
@ -15,9 +16,11 @@ public class ScenarioVariable {
/** /**
* 普通变量 * 普通变量
*/ */
@Valid
private List<CommonVariables> commonVariables = new ArrayList<>(0); private List<CommonVariables> commonVariables = new ArrayList<>(0);
/** /**
* csv变量 * csv变量
*/ */
@Valid
private List<CsvVariable> csvVariables = new ArrayList<>(0); private List<CsvVariable> csvVariables = new ArrayList<>(0);
} }

View File

@ -18,7 +18,7 @@ public interface ExtApiScenarioStepMapper {
List<CsvVariable> getCsvVariableByScenarioId(@Param("id") String id); List<CsvVariable> getCsvVariableByScenarioId(@Param("id") String id);
List<ApiScenarioCsvStep> getCsvStepByStepIds(@Param("ids") List<String> stepIds); List<ApiScenarioCsvStep> getCsvStepByScenarioId(@Param("scenarioId") String scenarioId);
/** /**
* 查询有步骤详情的请求类型的步骤 * 查询有步骤详情的请求类型的步骤

View File

@ -18,13 +18,10 @@
<include refid="io.metersphere.api.mapper.ApiScenarioCsvMapper.Base_Column_List"/> <include refid="io.metersphere.api.mapper.ApiScenarioCsvMapper.Base_Column_List"/>
from api_scenario_csv where scenario_id = #{id} from api_scenario_csv where scenario_id = #{id}
</select> </select>
<select id="getCsvStepByStepIds" resultType="io.metersphere.api.domain.ApiScenarioCsvStep"> <select id="getCsvStepByScenarioId" resultType="io.metersphere.api.domain.ApiScenarioCsvStep">
select select
<include refid="io.metersphere.api.mapper.ApiScenarioCsvStepMapper.Base_Column_List"/> <include refid="io.metersphere.api.mapper.ApiScenarioCsvStepMapper.Base_Column_List"/>
from api_scenario_csv_step where step_id in from api_scenario_csv_step where scenario_id = #{scenarioId}
<foreach collection="ids" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</select> </select>
<select id="getHasBlobRequestStepIds" resultType="java.lang.String"> <select id="getHasBlobRequestStepIds" resultType="java.lang.String">
select id select id

View File

@ -5,10 +5,13 @@ import io.metersphere.api.constants.ApiScenarioStepRefType;
import io.metersphere.api.dto.ApiScenarioParamConfig; import io.metersphere.api.dto.ApiScenarioParamConfig;
import io.metersphere.api.dto.request.MsScenario; import io.metersphere.api.dto.request.MsScenario;
import io.metersphere.api.dto.request.processors.MsProcessorConfig; import io.metersphere.api.dto.request.processors.MsProcessorConfig;
import io.metersphere.api.dto.scenario.CsvVariable;
import io.metersphere.api.dto.scenario.ScenarioConfig; import io.metersphere.api.dto.scenario.ScenarioConfig;
import io.metersphere.api.dto.scenario.ScenarioStepConfig; import io.metersphere.api.dto.scenario.ScenarioStepConfig;
import io.metersphere.api.dto.scenario.ScenarioVariable; import io.metersphere.api.dto.scenario.ScenarioVariable;
import io.metersphere.api.parser.jmeter.child.MsCsvChildPreConverter;
import io.metersphere.api.parser.jmeter.constants.JmeterAlias; import io.metersphere.api.parser.jmeter.constants.JmeterAlias;
import io.metersphere.api.parser.jmeter.constants.JmeterProperty;
import io.metersphere.api.parser.jmeter.processor.MsProcessorConverter; import io.metersphere.api.parser.jmeter.processor.MsProcessorConverter;
import io.metersphere.api.parser.jmeter.processor.MsProcessorConverterFactory; import io.metersphere.api.parser.jmeter.processor.MsProcessorConverterFactory;
import io.metersphere.api.parser.jmeter.processor.assertion.AssertionConverterFactory; import io.metersphere.api.parser.jmeter.processor.assertion.AssertionConverterFactory;
@ -65,6 +68,9 @@ public class MsScenarioConverter extends AbstractJmeterElementConverter<MsScenar
tree.add(getCookieManager()); tree.add(getCookieManager());
} }
// 添加 csv 数据集
addCsvDataSet(tree, msScenario.getScenarioConfig());
// 添加场景和环境变量 // 添加场景和环境变量
addUserParameters(tree, msScenario, envInfo, config); addUserParameters(tree, msScenario, envInfo, config);
@ -85,8 +91,17 @@ public class MsScenarioConverter extends AbstractJmeterElementConverter<MsScenar
addScenarioAssertions(tree, msScenario, config); addScenarioAssertions(tree, msScenario, config);
} }
private void addCsvDataSet(HashTree tree, ScenarioConfig scenarioConfig) {
if (scenarioConfig == null || scenarioConfig.getVariable() == null || CollectionUtils.isEmpty(scenarioConfig.getVariable().getCsvVariables())) {
return;
}
List<CsvVariable> csvVariables = scenarioConfig.getVariable().getCsvVariables();
MsCsvChildPreConverter.addCsvDataSet(tree, JmeterProperty.CSVDataSetProperty.SHARE_MODE_GROUP, csvVariables);
}
/** /**
* 添加临界控制器解决变量冲突 * 添加临界控制器解决变量冲突
*
* @param tree * @param tree
* @param msScenario * @param msScenario
* @return * @return
@ -347,6 +362,9 @@ public class MsScenarioConverter extends AbstractJmeterElementConverter<MsScenar
private ApiScenarioParamConfig getEnableConfig(MsScenario msScenario, ApiScenarioParamConfig config) { private ApiScenarioParamConfig getEnableConfig(MsScenario msScenario, ApiScenarioParamConfig config) {
ApiScenarioParamConfig enableConfig = config; ApiScenarioParamConfig enableConfig = config;
if (!isRef(msScenario.getRefType())) { if (!isRef(msScenario.getRefType())) {
if (isRootScenario(msScenario.getRefType())) {
setScenarioConfig(msScenario, enableConfig);
}
// 非引用的场景使用当前环境参数 // 非引用的场景使用当前环境参数
return enableConfig; return enableConfig;
} }
@ -369,13 +387,24 @@ public class MsScenarioConverter extends AbstractJmeterElementConverter<MsScenar
} }
} }
setScenarioConfig(msScenario, enableConfig);
return enableConfig;
}
private void setScenarioConfig(MsScenario msScenario, ApiScenarioParamConfig enableConfig) {
ScenarioConfig scenarioConfig = msScenario.getScenarioConfig(); ScenarioConfig scenarioConfig = msScenario.getScenarioConfig();
if (scenarioConfig != null) { if (scenarioConfig != null) {
// 设置是否使用全局cookie // 设置是否使用全局cookie
enableConfig.setEnableGlobalCookie(scenarioConfig.getOtherConfig().getEnableCookieShare()); enableConfig.setEnableGlobalCookie(scenarioConfig.getOtherConfig().getEnableCookieShare());
}
return enableConfig; ScenarioVariable variable = scenarioConfig.getVariable();
if (variable != null && variable.getCsvVariables() != null) {
for (CsvVariable csvVariable : variable.getCsvVariables()) {
enableConfig.getCsvMap().put(csvVariable.getId(), csvVariable);
}
}
}
} }
private CookieManager getCookieManager() { private CookieManager getCookieManager() {

View File

@ -0,0 +1,72 @@
package io.metersphere.api.parser.jmeter.child;
import io.metersphere.api.dto.ApiParamConfig;
import io.metersphere.api.dto.scenario.CsvVariable;
import io.metersphere.api.parser.jmeter.constants.JmeterAlias;
import io.metersphere.api.parser.jmeter.constants.JmeterProperty;
import io.metersphere.plugin.api.dto.ParameterConfig;
import io.metersphere.plugin.api.spi.AbstractJmeterElementConverter;
import io.metersphere.plugin.api.spi.AbstractMsTestElement;
import io.metersphere.sdk.constants.LocalRepositoryDir;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.jmeter.config.CSVDataSet;
import org.apache.jmeter.save.SaveService;
import org.apache.jmeter.testelement.TestElement;
import org.apache.jorphan.collections.HashTree;
import java.io.File;
import java.nio.charset.StandardCharsets;
import java.util.List;
/**
* 解析 csv 文件
* 在解析子步骤前先添加 csv 配置
* @Author: jianxing
* @CreateTime: 2024-05-15 11:32
*/
public class MsCsvChildPreConverter extends AbstractJmeterElementConverter<AbstractMsTestElement> {
@Override
public void toHashTree(HashTree tree, AbstractMsTestElement element, ParameterConfig config) {
List<String> csvIds = element.getCsvIds();
ApiParamConfig apiParamConfig = (ApiParamConfig) config;
if (CollectionUtils.isEmpty(csvIds)) {
return;
}
csvIds.forEach(csvId -> {
CsvVariable csvVariable = apiParamConfig.getCsvVariable(csvId);
if (csvVariable != null) {
String shareMode = StringUtils.equals(csvVariable.getScope(), CsvVariable.CsvVariableScope.SCENARIO.name()) ?
JmeterProperty.CSVDataSetProperty.SHARE_MODE_GROUP : JmeterProperty.CSVDataSetProperty.SHARE_MODE_THREAD;
addCsvDataSet(tree, shareMode, csvVariable);
}
});
}
public static void addCsvDataSet(HashTree tree, String shareMode, List<CsvVariable> list) {
list.forEach(item -> addCsvDataSet(tree, shareMode, item));
}
private static void addCsvDataSet(HashTree tree, String shareMode, CsvVariable csvVariable) {
// 执行机执行文件存放的缓存目录
String path = LocalRepositoryDir.getSystemCacheDir() + "/" + csvVariable.getFileId() + "/" + csvVariable.getFileName();
if (!StringUtils.equals(File.separator, "/")) {
// windows 系统下运行 / 转换为 \否则jmeter报错
path = path.replace("/", File.separator);
}
CSVDataSet csvDataSet = new CSVDataSet();
csvDataSet.setEnabled(true);
csvDataSet.setIgnoreFirstLine(false);
csvDataSet.setProperty(TestElement.TEST_CLASS, CSVDataSet.class.getName());
csvDataSet.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass(JmeterAlias.TEST_BEAN_GUI));
csvDataSet.setName(StringUtils.isEmpty(csvVariable.getName()) ? CSVDataSet.class.getSimpleName() : csvVariable.getName());
csvDataSet.setProperty(JmeterProperty.FILE_ENCODING, StringUtils.isEmpty(csvVariable.getEncoding()) ? StandardCharsets.UTF_8.name() : csvVariable.getEncoding());
csvDataSet.setProperty(JmeterProperty.CSVDataSetProperty.FILE_NAME, path);
csvDataSet.setProperty(JmeterProperty.CSVDataSetProperty.SHARE_MODE, shareMode);
csvDataSet.setProperty(JmeterProperty.CSVDataSetProperty.RECYCLE, true);
csvDataSet.setProperty(JmeterProperty.CSVDataSetProperty.DELIMITER, csvVariable.getDelimiter());
csvDataSet.setProperty(JmeterProperty.CSVDataSetProperty.QUOTED_DATA, csvVariable.getAllowQuotedData());
tree.add(csvDataSet);
}
}

View File

@ -11,4 +11,16 @@ public class JmeterProperty {
public final static String ASS_OPTION = "ASS_OPTION"; public final static String ASS_OPTION = "ASS_OPTION";
public final static String BEAN_SHELL_ASSERTION_QUERY = "BeanShellAssertion.query"; public final static String BEAN_SHELL_ASSERTION_QUERY = "BeanShellAssertion.query";
public final static String BEAN_SHELL_SAMPLER_QUERY = "BeanShellSampler.query"; public final static String BEAN_SHELL_SAMPLER_QUERY = "BeanShellSampler.query";
public final static String FILE_ENCODING = "fileEncoding";
public class CSVDataSetProperty {
public static final String FILE_NAME = "filename";
public static final String SHARE_MODE = "shareMode";
public static final String RECYCLE = "recycle";
public static final String DELIMITER = "delimiter";
public static final String QUOTED_DATA = "quotedData";
public static final String SHARE_MODE_GROUP = "shareMode.group";
public static final String SHARE_MODE_THREAD = "shareMode.thread";
}
} }

View File

@ -1,4 +1,4 @@
package io.metersphere.api.parser.jmeter; package io.metersphere.api.parser.jmeter.controller;
import io.metersphere.api.dto.request.controller.MsConstantTimerController; import io.metersphere.api.dto.request.controller.MsConstantTimerController;
import io.metersphere.api.parser.jmeter.processor.ScenarioTimeWaitingProcessorConverter; import io.metersphere.api.parser.jmeter.processor.ScenarioTimeWaitingProcessorConverter;

View File

@ -1,4 +1,4 @@
package io.metersphere.api.parser.jmeter; package io.metersphere.api.parser.jmeter.controller;
import io.metersphere.api.dto.request.controller.MsIfController; import io.metersphere.api.dto.request.controller.MsIfController;
import io.metersphere.plugin.api.dto.ParameterConfig; import io.metersphere.plugin.api.dto.ParameterConfig;
@ -21,12 +21,9 @@ public class MsIfControllerConverter extends AbstractJmeterElementConverter<MsIf
LogUtils.info("MsIfController is disabled"); LogUtils.info("MsIfController is disabled");
return; return;
} }
//TODO 这里需要处理csv文件
HashTree groupTree = tree.add(ifController(element)); HashTree groupTree = tree.add(ifController(element));
parseChild(groupTree, element, config); parseChild(groupTree, element, config);
} }
private IfController ifController(MsIfController element) { private IfController ifController(MsIfController element) {

View File

@ -1,4 +1,4 @@
package io.metersphere.api.parser.jmeter; package io.metersphere.api.parser.jmeter.controller;
import io.metersphere.api.dto.request.controller.LoopType; import io.metersphere.api.dto.request.controller.LoopType;
import io.metersphere.api.dto.request.controller.MsLoopController; import io.metersphere.api.dto.request.controller.MsLoopController;
@ -27,7 +27,6 @@ public class MsLoopControllerConverter extends AbstractJmeterElementConverter<Ms
return; return;
} }
final HashTree groupTree = controller(tree, element); final HashTree groupTree = controller(tree, element);
//TODO 这里需要处理csv文件 场景变量
if (groupTree == null) { if (groupTree == null) {
return; return;

View File

@ -1,4 +1,4 @@
package io.metersphere.api.parser.jmeter; package io.metersphere.api.parser.jmeter.controller;
import io.metersphere.api.dto.request.controller.MsOnceOnlyController; import io.metersphere.api.dto.request.controller.MsOnceOnlyController;
import io.metersphere.plugin.api.dto.ParameterConfig; import io.metersphere.plugin.api.dto.ParameterConfig;
@ -20,7 +20,6 @@ public class MsOnceOnlyControllerConverter extends AbstractJmeterElementConverte
LogUtils.info("MsOnceOnlyController is disabled"); LogUtils.info("MsOnceOnlyController is disabled");
return; return;
} }
//TODO 这里需要处理csv文件
HashTree groupTree = tree.add(onceOnlyController(element)); HashTree groupTree = tree.add(onceOnlyController(element));
parseChild(groupTree, element, config); parseChild(groupTree, element, config);

View File

@ -402,18 +402,27 @@ public class ApiFileResourceService {
fileRequest.setFileName(fileName); fileRequest.setFileName(fileName);
fileRequest.setStorage(StorageType.MINIO.name()); fileRequest.setStorage(StorageType.MINIO.name());
byte[] bytes;
try { try {
fileId = fileMetadataService.transferFile(request.getFileName(), request.getOriginalName(), request.getProjectId(), request.getModuleId(), currentUser, fileService.download(fileRequest)); bytes = fileService.download(fileRequest);
} catch (Exception e) {
LogUtils.error(e);
throw new MSException(Translator.get("file.transfer.failed"), e);
}
fileId = fileMetadataService.transferFile(request.getFileName(), request.getOriginalName(), request.getProjectId(), request.getModuleId(), currentUser, bytes);
if (CollectionUtils.isEmpty(apiFileResources)) { if (CollectionUtils.isEmpty(apiFileResources)) {
// 删除临时文件 // 删除临时文件
FileRequest deleteRequest = new FileRequest(); FileRequest deleteRequest = new FileRequest();
deleteRequest.setFolder(folder); deleteRequest.setFolder(folder);
try {
FileCenter.getDefaultRepository().deleteFolder(deleteRequest); FileCenter.getDefaultRepository().deleteFolder(deleteRequest);
}
} catch (Exception e) { } catch (Exception e) {
LogUtils.error(e); LogUtils.error(e);
throw new MSException(Translator.get("file.transfer.failed")); throw new MSException(Translator.get("file.transfer.failed"), e);
} }
}
return fileId; return fileId;
} }

View File

@ -1,6 +1,5 @@
package io.metersphere.api.service.scenario; package io.metersphere.api.service.scenario;
import io.metersphere.sdk.constants.ApiFileResourceType;
import io.metersphere.api.constants.ApiResourceType; import io.metersphere.api.constants.ApiResourceType;
import io.metersphere.api.constants.ApiScenarioStepRefType; import io.metersphere.api.constants.ApiScenarioStepRefType;
import io.metersphere.api.constants.ApiScenarioStepType; import io.metersphere.api.constants.ApiScenarioStepType;
@ -51,9 +50,7 @@ import io.metersphere.sdk.domain.EnvironmentGroupExample;
import io.metersphere.sdk.dto.api.task.ApiRunModeConfigDTO; import io.metersphere.sdk.dto.api.task.ApiRunModeConfigDTO;
import io.metersphere.sdk.dto.api.task.TaskRequestDTO; import io.metersphere.sdk.dto.api.task.TaskRequestDTO;
import io.metersphere.sdk.exception.MSException; import io.metersphere.sdk.exception.MSException;
import io.metersphere.sdk.file.FileCenter;
import io.metersphere.sdk.file.FileCopyRequest; import io.metersphere.sdk.file.FileCopyRequest;
import io.metersphere.sdk.file.FileRequest;
import io.metersphere.sdk.file.MinioRepository; import io.metersphere.sdk.file.MinioRepository;
import io.metersphere.sdk.mapper.EnvironmentGroupMapper; import io.metersphere.sdk.mapper.EnvironmentGroupMapper;
import io.metersphere.sdk.mapper.EnvironmentMapper; import io.metersphere.sdk.mapper.EnvironmentMapper;
@ -152,10 +149,6 @@ public class ApiScenarioService extends MoveNodeService {
@Resource @Resource
private ApiTestCaseService apiTestCaseService; private ApiTestCaseService apiTestCaseService;
@Resource @Resource
private FileAssociationService fileAssociationService;
@Resource
private FileMetadataService fileMetadataService;
@Resource
private ApiScenarioCsvMapper apiScenarioCsvMapper; private ApiScenarioCsvMapper apiScenarioCsvMapper;
@Resource @Resource
private ApiScenarioCsvStepMapper apiScenarioCsvStepMapper; private ApiScenarioCsvStepMapper apiScenarioCsvStepMapper;
@ -442,6 +435,18 @@ public class ApiScenarioService extends MoveNodeService {
apiScenarioBlob.setConfig(JSON.toJSONString(request.getScenarioConfig()).getBytes()); apiScenarioBlob.setConfig(JSON.toJSONString(request.getScenarioConfig()).getBytes());
apiScenarioBlobMapper.insert(apiScenarioBlob); apiScenarioBlobMapper.insert(apiScenarioBlob);
// 处理csv文件
handCsvFilesAdd(request, creator, scenario);
// 处理添加的步骤
handleStepAdd(request, scenario);
// 处理步骤文件
handleStepFilesAdd(request, creator, scenario);
return scenario;
}
private void handleStepAdd(ApiScenarioAddRequest request, ApiScenario scenario) {
// 插入步骤 // 插入步骤
if (CollectionUtils.isNotEmpty(request.getSteps())) { if (CollectionUtils.isNotEmpty(request.getSteps())) {
checkCircularRef(scenario.getId(), request.getSteps()); checkCircularRef(scenario.getId(), request.getSteps());
@ -462,33 +467,25 @@ public class ApiScenarioService extends MoveNodeService {
apiScenarioStepBlobMapper.batchInsert(apiScenarioStepBlobs); apiScenarioStepBlobMapper.batchInsert(apiScenarioStepBlobs);
} }
saveStepCsv(steps, csvSteps); csvSteps.forEach(step -> step.setScenarioId(scenario.getId()));
saveStepCsv(scenario.getId(), steps, csvSteps);
}
} }
// 处理步骤文件 private void handCsvFilesAdd(ApiScenarioAddRequest request, String creator, ApiScenario scenario) {
handleStepFiles(request, creator, scenario); if (request.getScenarioConfig() == null || request.getScenarioConfig().getVariable() == null || request.getScenarioConfig().getVariable().getCsvVariables() == null) {
// 处理场景文件csv等
handScenarioFiles(request, creator, scenario);
return scenario;
}
private void handScenarioFiles(ApiScenarioAddRequest request, String creator, ApiScenario scenario) {
ResourceAddFileParam fileParam = request.getFileParam();
if (fileParam == null) {
return; return;
} }
ApiFileResourceUpdateRequest resourceUpdateRequest = getApiFileResourceUpdateRequest(scenario.getId(), scenario.getProjectId(), creator); List<CsvVariable> csvVariables = request.getScenarioConfig().getVariable().getCsvVariables();
resourceUpdateRequest.setUploadFileIds(fileParam.getUploadFileIds());
resourceUpdateRequest.setLinkFileIds(fileParam.getLinkFileIds()); // 处理 csv 相关数据表
apiFileResourceService.addFileResource(resourceUpdateRequest); handleCsvDataUpdate(csvVariables, scenario, List.of());
if (request.getScenarioConfig() != null
&& request.getScenarioConfig().getVariable() != null) { // 处理文件的上传
saveCsv(request.getScenarioConfig().getVariable().getCsvVariables(), resourceUpdateRequest); handleCsvFileUpdate(csvVariables, List.of(), scenario, creator);
}
} }
private void handleStepFiles(ApiScenarioAddRequest request, String creator, ApiScenario scenario) { private void handleStepFilesAdd(ApiScenarioAddRequest request, String creator, ApiScenario scenario) {
Map<String, ResourceAddFileParam> stepFileParam = request.getStepFileParam(); Map<String, ResourceAddFileParam> stepFileParam = request.getStepFileParam();
if (MapUtils.isNotEmpty(stepFileParam)) { if (MapUtils.isNotEmpty(stepFileParam)) {
stepFileParam.forEach((stepId, fileParam) -> { stepFileParam.forEach((stepId, fileParam) -> {
@ -499,7 +496,7 @@ public class ApiScenarioService extends MoveNodeService {
} }
} }
private void handleStepFiles(ApiScenarioUpdateRequest request, String updater, ApiScenario scenario) { private void handleStepFilesUpdate(ApiScenarioUpdateRequest request, String updater, ApiScenario scenario) {
Map<String, ResourceUpdateFileParam> stepFileParam = request.getStepFileParam(); Map<String, ResourceUpdateFileParam> stepFileParam = request.getStepFileParam();
if (MapUtils.isNotEmpty(stepFileParam)) { if (MapUtils.isNotEmpty(stepFileParam)) {
stepFileParam.forEach((stepId, fileParam) -> { stepFileParam.forEach((stepId, fileParam) -> {
@ -520,156 +517,126 @@ public class ApiScenarioService extends MoveNodeService {
return resourceUpdateRequest; return resourceUpdateRequest;
} }
private void saveStepCsv(List<ApiScenarioStep> steps, List<ApiScenarioCsvStep> csvSteps) { private void saveStepCsv(String scenarioId, List<ApiScenarioStep> steps, List<ApiScenarioCsvStep> csvSteps) {
//获取所有的步骤id 然后删掉历史的关联关系 // 先删除
List<String> stepIds = steps.stream().map(ApiScenarioStep::getId).toList();
SubListUtils.dealForSubList(stepIds, 500, subList -> {
ApiScenarioCsvStepExample csvStepExample = new ApiScenarioCsvStepExample(); ApiScenarioCsvStepExample csvStepExample = new ApiScenarioCsvStepExample();
csvStepExample.createCriteria().andStepIdIn(subList); csvStepExample.createCriteria().andScenarioIdEqualTo(scenarioId);
apiScenarioCsvStepMapper.deleteByExample(csvStepExample); apiScenarioCsvStepMapper.deleteByExample(csvStepExample);
});
//插入csv步骤 // 再添加
if (CollectionUtils.isNotEmpty(csvSteps)) { if (CollectionUtils.isNotEmpty(csvSteps)) {
SubListUtils.dealForSubList(csvSteps, 100, subList -> apiScenarioCsvStepMapper.batchInsert(subList)); SubListUtils.dealForSubList(csvSteps, 100, subList -> apiScenarioCsvStepMapper.batchInsert(subList));
} }
} }
private void saveCsv(List<CsvVariable> csvVariables, ApiFileResourceUpdateRequest resourceUpdateRequest) { private void handleCsvUpdate(ScenarioConfig scenarioConfig, ApiScenario scenario, String userId) {
//进行比较一下 哪些是已存在的 那些是新上传的 if (scenarioConfig == null || scenarioConfig.getVariable() == null || scenarioConfig.getVariable().getCsvVariables() == null) {
//查询已经存在的fileId 这里直接过滤所有的数据 拿到新上传的本地文件id 和已经存在的文件id 关联的文件id 关于新的 需要删除的 已经存在的 return;
ApiScenarioCsvExample apiScenarioCsvExample = new ApiScenarioCsvExample(); }
apiScenarioCsvExample.createCriteria().andScenarioIdEqualTo(resourceUpdateRequest.getResourceId()); List<CsvVariable> csvVariables = scenarioConfig.getVariable().getCsvVariables();
List<ApiScenarioCsv> dbFileIds = apiScenarioCsvMapper.selectByExample(apiScenarioCsvExample); List<ApiScenarioCsv> dbCsv = getApiScenarioCsv(scenario.getId());
//取出所有的fileId Association为false的数据
List<String> dbLocalFileIds = dbFileIds.stream().filter(c -> BooleanUtils.isFalse(c.getAssociation())).map(ApiScenarioCsv::getFileId).toList();
//取出所有的fileId Association为true的数据
List<String> dbRefFileIds = dbFileIds.stream().filter(c -> BooleanUtils.isTrue(c.getAssociation())).map(ApiScenarioCsv::getFileId).toList();
//获取传的所有Association为false的数据 // 更新 csv 相关数据表
List<String> localFileIds = csvVariables.stream().filter(c -> BooleanUtils.isFalse(c.getAssociation())).map(CsvVariable::getFileId).toList(); handleCsvDataUpdate(csvVariables, scenario, dbCsv);
//获取传的所有Association为true的数据
List<String> refFileIds = csvVariables.stream().filter(c -> BooleanUtils.isTrue(c.getAssociation())).map(CsvVariable::getFileId).toList(); // 处理文件的上传和删除
handleCsvFileUpdate(csvVariables, dbCsv, scenario, userId);
}
private void handleCsvDataUpdate(List<CsvVariable> csvVariables, ApiScenario scenario, List<ApiScenarioCsv> dbCsv) {
List<String> dbCsvIds = dbCsv.stream()
.map(ApiScenarioCsv::getId)
.toList();
List<String> csvIds = csvVariables.stream()
.map(CsvVariable::getId)
.toList();
List<String> deleteCsvIds = ListUtils.subtract(dbCsvIds, csvIds);
//取交集 交集数据是已存在的 不需要重新上传 和处理关联关系 但是需要更新apiScenarioCsv表的数据
List<String> intersectionLocal = ListUtils.intersection(dbLocalFileIds, localFileIds);
//取差集 dbFileIds和 intersection的差集是需要删除的数据 本地数据需要删除的数据
List<String> deleteLocals = ListUtils.subtract(dbLocalFileIds, intersectionLocal);
resourceUpdateRequest.setDeleteFileIds(deleteLocals);
List<String> addLocal = ListUtils.subtract(localFileIds, intersectionLocal);
resourceUpdateRequest.setUploadFileIds(addLocal);
//获取 关联文件的交集
List<String> intersectionRef = ListUtils.intersection(dbRefFileIds, refFileIds);
//获取删除的
List<String> deleteRefs = ListUtils.subtract(dbRefFileIds, intersectionRef);
List<String> addRef = ListUtils.subtract(refFileIds, intersectionRef);
resourceUpdateRequest.setLinkFileIds(addRef);
resourceUpdateRequest.setUnLinkFileIds(deleteRefs);
//删除不存在的数据 //删除不存在的数据
deleteCsvResource(resourceUpdateRequest); deleteCsvResource(deleteCsvIds);
addCsvResource(resourceUpdateRequest, csvVariables); Set<String> dbCsvIdSet = dbCsvIds.stream().collect(Collectors.toSet());
}
private void addCsvResource(ApiFileResourceUpdateRequest resourceUpdateRequest, List<ApiScenarioCsv> addCsvList = new ArrayList<>();
List<CsvVariable> csvVariables) { csvVariables.stream().forEach(item -> {
List<ApiScenarioCsv> addData = new ArrayList<>();
List<ApiScenarioCsv> updateData = new ArrayList<>();
List<String> addFileIds = resourceUpdateRequest.getUploadFileIds();
List<String> linkFileIds = resourceUpdateRequest.getLinkFileIds();
Map<String, String> refFilesMap = new HashMap<>();
if (CollectionUtils.isNotEmpty(linkFileIds)) {
//根据fileId查询文件名
List<FileMetadata> fileList = fileMetadataService.selectByList(linkFileIds);
if (CollectionUtils.isNotEmpty(fileList)) {
//生成map key为文件id 值为文件名称 文件名称为name.类型
refFilesMap = fileList.stream().collect(Collectors.toMap(FileMetadata::getId, f -> f.getName() + "." + f.getType()));
}
fileAssociationService.association(resourceUpdateRequest.getResourceId(), FileAssociationSourceUtil.SOURCE_TYPE_API_SCENARIO, linkFileIds,
apiFileResourceService.createFileLogRecord(resourceUpdateRequest.getOperator(), resourceUpdateRequest.getProjectId(), OperationLogModule.API_SCENARIO_MANAGEMENT_SCENARIO));
}
Map<String, String> finalRefFilesMap = refFilesMap;
// 添加文件与接口的关联关系
Map<String, String> addFileMap = new HashMap<>();
csvVariables.forEach(item -> {
ApiScenarioCsv scenarioCsv = new ApiScenarioCsv(); ApiScenarioCsv scenarioCsv = new ApiScenarioCsv();
BeanUtils.copyBean(scenarioCsv, item); BeanUtils.copyBean(scenarioCsv, item);
scenarioCsv.setScenarioId(resourceUpdateRequest.getResourceId()); scenarioCsv.setScenarioId(scenario.getId());
scenarioCsv.setProjectId(resourceUpdateRequest.getProjectId()); scenarioCsv.setProjectId(scenario.getProjectId());
// uploadFileIds里包含的数据 全部需要上传到minio上或者需要重新建立关联关系 if (!dbCsvIdSet.contains(item.getId())) {
if (BooleanUtils.isFalse(item.getAssociation()) addCsvList.add(scenarioCsv);
&& CollectionUtils.isNotEmpty(addFileIds)
&& addFileIds.contains(item.getFileId())) {
scenarioCsv.setFileName(apiFileResourceService.getTempFileNameByFileId(item.getFileId()));
addFileMap.put(item.getFileId(), scenarioCsv.getId());
} else if (BooleanUtils.isTrue(item.getAssociation())
&& finalRefFilesMap.containsKey(item.getFileId())
&& CollectionUtils.isNotEmpty(linkFileIds)
&& linkFileIds.contains(item.getFileId())) {
scenarioCsv.setFileName(finalRefFilesMap.get(item.getFileId()));
}
if (StringUtils.isBlank(item.getId())) {
scenarioCsv.setId(IDGenerator.nextStr());
addData.add(scenarioCsv);
} else { } else {
updateData.add(scenarioCsv); apiScenarioCsvMapper.updateByPrimaryKey(scenarioCsv);
} }
}); });
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
ApiScenarioCsvMapper mapper = sqlSession.getMapper(ApiScenarioCsvMapper.class); if (CollectionUtils.isNotEmpty(addCsvList)) {
if (CollectionUtils.isNotEmpty(updateData)) { apiScenarioCsvMapper.batchInsert(addCsvList);
//更新apiScenarioCsv表
updateData.forEach(mapper::updateByPrimaryKeySelective);
}
if (CollectionUtils.isNotEmpty(addData)) {
//插入apiScenarioCsv表
addData.forEach(mapper::insertSelective);
}
sqlSession.flushStatements();
SqlSessionUtils.closeSqlSession(sqlSession, sqlSessionFactory);
if (MapUtils.isNotEmpty(addFileMap)) {
// 上传文件到对象存储
apiFileResourceService.uploadFileResource(resourceUpdateRequest.getFolder(), addFileMap);
} }
} }
private void deleteCsvResource(ApiFileResourceUpdateRequest resourceUpdateRequest) { private void handleCsvFileUpdate(List<CsvVariable> csvVariables, List<ApiScenarioCsv> dbCsv, ApiScenario scenario, String userId) {
// 处理本地上传文件 ApiFileResourceUpdateRequest resourceUpdateRequest = getApiFileResourceUpdateRequest(scenario.getId(), scenario.getProjectId(), userId);
List<String> deleteFileIds = resourceUpdateRequest.getDeleteFileIds(); // 设置本地文件相关参数
setCsvLocalFileParam(csvVariables, dbCsv, resourceUpdateRequest);
// 设置关联文件相关参数
setCsvLinkFileParam(csvVariables, dbCsv, resourceUpdateRequest);
apiFileResourceService.addFileResource(resourceUpdateRequest);
}
private void setCsvLinkFileParam(List<CsvVariable> csvVariables, List<ApiScenarioCsv> dbCsv, ApiFileResourceUpdateRequest resourceUpdateRequest) {
// 获取数据库中关联的文件id
List<String> dbRefFileIds = dbCsv.stream()
.filter(c -> BooleanUtils.isTrue(c.getAssociation()))
.map(ApiScenarioCsv::getFileId)
.toList();
// 获取请求中关联的文件id
List<String> refFileIds = csvVariables.stream()
.filter(c -> BooleanUtils.isTrue(c.getAssociation())).map(CsvVariable::getFileId).toList();
List<String> unlinkFileIds = ListUtils.subtract(dbRefFileIds, refFileIds);
resourceUpdateRequest.setUnLinkFileIds(unlinkFileIds);
List<String> linkFileIds = ListUtils.subtract(refFileIds, dbRefFileIds);
resourceUpdateRequest.setLinkFileIds(linkFileIds);
}
private void setCsvLocalFileParam(List<CsvVariable> csvVariables, List<ApiScenarioCsv> dbCsv, ApiFileResourceUpdateRequest resourceUpdateRequest) {
// 获取数据库中的本地文件
List<String> dbLocalFileIds = dbCsv.stream()
.filter(c -> BooleanUtils.isFalse(c.getAssociation()))
.map(ApiScenarioCsv::getFileId)
.toList();
// 获取请求中的本地文件
List<String> localFileIds = csvVariables.stream()
.filter(c -> BooleanUtils.isFalse(c.getAssociation()))
.map(CsvVariable::getFileId).toList();
// 待删除文件
List<String> deleteLocals = ListUtils.subtract(dbLocalFileIds, localFileIds);
resourceUpdateRequest.setDeleteFileIds(deleteLocals);
// 新上传文件
List<String> addLocal = ListUtils.subtract(localFileIds, dbLocalFileIds);
resourceUpdateRequest.setUploadFileIds(addLocal);
}
private List<ApiScenarioCsv> getApiScenarioCsv(String scenarioId) {
ApiScenarioCsvExample apiScenarioCsvExample = new ApiScenarioCsvExample();
apiScenarioCsvExample.createCriteria().andScenarioIdEqualTo(scenarioId);
return apiScenarioCsvMapper.selectByExample(apiScenarioCsvExample);
}
private void deleteCsvResource(List<String> deleteCsvIds) {
if (CollectionUtils.isNotEmpty(deleteCsvIds)) {
ApiScenarioCsvExample example = new ApiScenarioCsvExample(); ApiScenarioCsvExample example = new ApiScenarioCsvExample();
example.createCriteria().andIdIn(deleteCsvIds);
apiScenarioCsvMapper.deleteByExample(example);
ApiScenarioCsvStepExample stepExample = new ApiScenarioCsvStepExample(); ApiScenarioCsvStepExample stepExample = new ApiScenarioCsvStepExample();
if (CollectionUtils.isNotEmpty(deleteFileIds)) { stepExample.createCriteria().andIdIn(deleteCsvIds);
// 删除关联关系
deleteFileIds.forEach(fileId -> {
FileRequest request = new FileRequest();
// 删除文件所在目录
request.setFolder(resourceUpdateRequest.getFolder() + "/" + fileId);
try {
FileCenter.getDefaultRepository().deleteFolder(request);
} catch (Exception e) {
LogUtils.error(e);
}
});
example.createCriteria()
.andScenarioIdEqualTo(resourceUpdateRequest.getResourceId())
.andFileIdIn(deleteFileIds);
apiScenarioCsvMapper.deleteByExample(example);
stepExample.createCriteria().andFileIdIn(deleteFileIds);
apiScenarioCsvStepMapper.deleteByExample(stepExample);
}
List<String> unLinkFileIds = resourceUpdateRequest.getUnLinkFileIds();
// 处理关联文件
if (CollectionUtils.isNotEmpty(unLinkFileIds)) {
fileAssociationService.deleteBySourceIdAndFileIds(resourceUpdateRequest.getResourceId(), unLinkFileIds,
apiFileResourceService.createFileLogRecord(resourceUpdateRequest.getOperator(), resourceUpdateRequest.getProjectId(), resourceUpdateRequest.getLogModule()));
example.clear();
example.createCriteria()
.andScenarioIdEqualTo(resourceUpdateRequest.getResourceId())
.andFileIdIn(unLinkFileIds);
apiScenarioCsvMapper.deleteByExample(example);
stepExample.clear();
stepExample.createCriteria().andFileIdIn(deleteFileIds);
apiScenarioCsvStepMapper.deleteByExample(stepExample); apiScenarioCsvStepMapper.deleteByExample(stepExample);
} }
} }
@ -719,32 +686,14 @@ public class ApiScenarioService extends MoveNodeService {
updateApiScenarioStep(request, originScenario, updater); updateApiScenarioStep(request, originScenario, updater);
// 处理步骤文件 // 处理步骤文件
handleStepFiles(request, updater, originScenario); handleStepFilesUpdate(request, updater, originScenario);
// 处理场景文件 // 处理 csv 文件
handleScenarioFiles(request, updater, originScenario); handleCsvUpdate(request.getScenarioConfig(), scenario, updater);
return scenario; return scenario;
} }
private void handleScenarioFiles(ApiScenarioUpdateRequest request, String updater, ApiScenario scenario) {
// 处理场景文件
ApiFileResourceUpdateRequest resourceUpdateRequest = getApiFileResourceUpdateRequest(scenario.getId(), scenario.getProjectId(), updater);
ResourceAddFileParam fileParam = request.getFileParam();
if (fileParam != null) {
resourceUpdateRequest = BeanUtils.copyBean(resourceUpdateRequest, fileParam);
apiFileResourceService.updateFileResource(resourceUpdateRequest);
}
//处理csv变量
if (request.getScenarioConfig() != null
&& request.getScenarioConfig().getVariable() != null) {
saveCsv(request.getScenarioConfig().getVariable().getCsvVariables(), resourceUpdateRequest);
} else {
saveCsv(new ArrayList<>(), resourceUpdateRequest);
}
}
/** /**
* 更新场景步骤 * 更新场景步骤
*/ */
@ -764,8 +713,9 @@ public class ApiScenarioService extends MoveNodeService {
// 获取待更新的步骤 // 获取待更新的步骤
List<ApiScenarioStep> apiScenarioSteps = getApiScenarioSteps(null, steps, scenarioCsvSteps); List<ApiScenarioStep> apiScenarioSteps = getApiScenarioSteps(null, steps, scenarioCsvSteps);
apiScenarioSteps.forEach(step -> step.setScenarioId(scenario.getId())); apiScenarioSteps.forEach(step -> step.setScenarioId(scenario.getId()));
scenarioCsvSteps.forEach(step -> step.setScenarioId(scenario.getId()));
saveStepCsv(apiScenarioSteps, scenarioCsvSteps); saveStepCsv(scenario.getId(), apiScenarioSteps, scenarioCsvSteps);
// 获取待更新的步骤详情 // 获取待更新的步骤详情
addSpecialStepDetails(steps, request.getStepDetails()); addSpecialStepDetails(steps, request.getStepDetails());
List<ApiScenarioStepBlob> updateStepBlobs = getUpdateStepBlobs(apiScenarioSteps, request.getStepDetails()); List<ApiScenarioStepBlob> updateStepBlobs = getUpdateStepBlobs(apiScenarioSteps, request.getStepDetails());
@ -941,9 +891,9 @@ public class ApiScenarioService extends MoveNodeService {
continue; continue;
} }
if (CollectionUtils.isNotEmpty(step.getCsvFileIds())) { if (CollectionUtils.isNotEmpty(step.getCsvIds())) {
//如果是csv文件 需要保存到apiScenarioCsvStep表中 //如果是csv文件 需要保存到apiScenarioCsvStep表中
step.getCsvFileIds().forEach(fileId -> { step.getCsvIds().forEach(fileId -> {
ApiScenarioCsvStep csvStep = new ApiScenarioCsvStep(); ApiScenarioCsvStep csvStep = new ApiScenarioCsvStep();
csvStep.setId(IDGenerator.nextStr()); csvStep.setId(IDGenerator.nextStr());
csvStep.setStepId(apiScenarioStep.getId()); csvStep.setStepId(apiScenarioStep.getId());
@ -1183,16 +1133,11 @@ public class ApiScenarioService extends MoveNodeService {
private void deleteCsvByScenarioId(String id) { private void deleteCsvByScenarioId(String id) {
ApiScenarioCsvExample example = new ApiScenarioCsvExample(); ApiScenarioCsvExample example = new ApiScenarioCsvExample();
example.createCriteria().andScenarioIdEqualTo(id); example.createCriteria().andScenarioIdEqualTo(id);
List<ApiScenarioCsv> apiScenarioCsv = apiScenarioCsvMapper.selectByExample(example);
if (CollectionUtils.isNotEmpty(apiScenarioCsv)) {
List<String> fileIds = apiScenarioCsv.stream().map(ApiScenarioCsv::getFileId).toList();
//删除关联关系
ApiScenarioCsvStepExample stepExample = new ApiScenarioCsvStepExample();
stepExample.createCriteria().andFileIdIn(fileIds);
apiScenarioCsvStepMapper.deleteByExample(stepExample);
}
apiScenarioCsvMapper.deleteByExample(example); apiScenarioCsvMapper.deleteByExample(example);
ApiScenarioCsvStepExample stepExample = new ApiScenarioCsvStepExample();
stepExample.createCriteria().andScenarioIdEqualTo(id);
apiScenarioCsvStepMapper.deleteByExample(stepExample);
} }
//级联删除 //级联删除
@ -1810,6 +1755,7 @@ public class ApiScenarioService extends MoveNodeService {
msTestElement.setName(step.getName()); msTestElement.setName(step.getName());
// 步骤ID设置为唯一ID // 步骤ID设置为唯一ID
msTestElement.setStepId(step.getUniqueId()); msTestElement.setStepId(step.getUniqueId());
msTestElement.setCsvIds(step.getCsvIds());
// 记录引用的资源ID和项目ID下载执行文件时需要使用 // 记录引用的资源ID和项目ID下载执行文件时需要使用
parseParam.getRefProjectIds().add(step.getProjectId()); parseParam.getRefProjectIds().add(step.getProjectId());
@ -2152,15 +2098,14 @@ public class ApiScenarioService extends MoveNodeService {
ApiScenarioDetail apiScenarioDetail = BeanUtils.copyBean(new ApiScenarioDetail(), apiScenario); ApiScenarioDetail apiScenarioDetail = BeanUtils.copyBean(new ApiScenarioDetail(), apiScenario);
apiScenarioDetail.setSteps(List.of()); apiScenarioDetail.setSteps(List.of());
ApiScenarioBlob apiScenarioBlob = apiScenarioBlobMapper.selectByPrimaryKey(scenarioId); ApiScenarioBlob apiScenarioBlob = apiScenarioBlobMapper.selectByPrimaryKey(scenarioId);
if (apiScenarioBlob != null) { if (apiScenarioBlob != null) {
apiScenarioDetail.setScenarioConfig(JSON.parseObject(new String(apiScenarioBlob.getConfig()), ScenarioConfig.class)); apiScenarioDetail.setScenarioConfig(JSON.parseObject(new String(apiScenarioBlob.getConfig()), ScenarioConfig.class));
} }
//存放csv变量 //存放csv变量
List<CsvVariable> csvVariables = extApiScenarioStepMapper.getCsvVariableByScenarioId(scenarioId); List<CsvVariable> csvVariables = extApiScenarioStepMapper.getCsvVariableByScenarioId(scenarioId);
if (CollectionUtils.isNotEmpty(csvVariables) && apiScenarioDetail.getScenarioConfig() != null
&& apiScenarioDetail.getScenarioConfig().getVariable() != null) {
apiScenarioDetail.getScenarioConfig().getVariable().setCsvVariables(csvVariables); apiScenarioDetail.getScenarioConfig().getVariable().setCsvVariables(csvVariables);
}
// 获取所有步骤 // 获取所有步骤
List<ApiScenarioStepDTO> allSteps = getAllStepsByScenarioIds(List.of(scenarioId)) List<ApiScenarioStepDTO> allSteps = getAllStepsByScenarioIds(List.of(scenarioId))
@ -2169,16 +2114,13 @@ public class ApiScenarioService extends MoveNodeService {
.collect(Collectors.toList()); .collect(Collectors.toList());
//获取所有步骤的csv的关联关系 //获取所有步骤的csv的关联关系
List<String> stepIds = allSteps.stream().map(ApiScenarioStepDTO::getId).toList(); List<ApiScenarioCsvStep> csvSteps = extApiScenarioStepMapper.getCsvStepByScenarioId(scenarioId);
if (CollectionUtils.isNotEmpty(stepIds)) {
List<ApiScenarioCsvStep> csvSteps = extApiScenarioStepMapper.getCsvStepByStepIds(stepIds);
// 构造 mapkey 为步骤IDvalue 为csv文件ID列表 // 构造 mapkey 为步骤IDvalue 为csv文件ID列表
Map<String, List<String>> stepsCsvMap = csvSteps.stream() Map<String, List<String>> stepsCsvMap = csvSteps.stream()
.collect(Collectors.groupingBy(ApiScenarioCsvStep::getStepId, Collectors.mapping(ApiScenarioCsvStep::getFileId, Collectors.toList()))); .collect(Collectors.groupingBy(ApiScenarioCsvStep::getStepId, Collectors.mapping(ApiScenarioCsvStep::getFileId, Collectors.toList())));
//将stepsCsvMap根据步骤id放入到allSteps中 //将stepsCsvMap根据步骤id放入到allSteps中
if (CollectionUtils.isNotEmpty(allSteps)) { if (CollectionUtils.isNotEmpty(allSteps)) {
allSteps.forEach(step -> step.setCsvFileIds(stepsCsvMap.get(step.getId()))); allSteps.forEach(step -> step.setCsvIds(stepsCsvMap.get(step.getId())));
}
} }
// 构造 mapkey 为场景IDvalue 为步骤列表 // 构造 mapkey 为场景IDvalue 为步骤列表

View File

@ -1,6 +1,11 @@
package io.metersphere.api.utils; package io.metersphere.api.utils;
import io.metersphere.api.parser.jmeter.*; import io.metersphere.api.parser.jmeter.*;
import io.metersphere.api.parser.jmeter.child.MsCsvChildPreConverter;
import io.metersphere.api.parser.jmeter.controller.MsConstantTimerControllerConverter;
import io.metersphere.api.parser.jmeter.controller.MsIfControllerConverter;
import io.metersphere.api.parser.jmeter.controller.MsLoopControllerConverter;
import io.metersphere.api.parser.jmeter.controller.MsOnceOnlyControllerConverter;
import io.metersphere.plugin.api.spi.AbstractJmeterElementConverter; import io.metersphere.plugin.api.spi.AbstractJmeterElementConverter;
import io.metersphere.plugin.api.spi.MsTestElement; import io.metersphere.plugin.api.spi.MsTestElement;
import io.metersphere.plugin.sdk.util.PluginLogUtils; import io.metersphere.plugin.sdk.util.PluginLogUtils;
@ -24,7 +29,11 @@ public class JmeterElementConverterRegister {
private static final Map<Class<? extends MsTestElement>, AbstractJmeterElementConverter<? extends MsTestElement>> parserMap = new HashMap<>(); private static final Map<Class<? extends MsTestElement>, AbstractJmeterElementConverter<? extends MsTestElement>> parserMap = new HashMap<>();
static { static {
// 注册默认的转换器 todo 注册插件的转换器 // 设置获取转换器的方法
AbstractJmeterElementConverter.setGetConverterFunc(JmeterElementConverterRegister::getConverter);
// 注册子步骤前置解析器
AbstractJmeterElementConverter.registerChildPreConverters(new MsCsvChildPreConverter());
// 注册默认的转换器
register(MsHTTPElementConverter.class); register(MsHTTPElementConverter.class);
register(MsCommonElementConverter.class); register(MsCommonElementConverter.class);
register(MsScriptElementConverter.class); register(MsScriptElementConverter.class);
@ -43,8 +52,6 @@ public class JmeterElementConverterRegister {
public static void register(Class<? extends AbstractJmeterElementConverter<? extends MsTestElement>> elementConverterClass) { public static void register(Class<? extends AbstractJmeterElementConverter<? extends MsTestElement>> elementConverterClass) {
try { try {
AbstractJmeterElementConverter<? extends MsTestElement> elementConverter = elementConverterClass.getDeclaredConstructor().newInstance(); AbstractJmeterElementConverter<? extends MsTestElement> elementConverter = elementConverterClass.getDeclaredConstructor().newInstance();
// 设置获取转换器的方法
elementConverter.setGetConverterFunc(JmeterElementConverterRegister::getConverter);
// 注册到解析器集合中 // 注册到解析器集合中
parserMap.put(elementConverter.testElementClass, elementConverter); parserMap.put(elementConverter.testElementClass, elementConverter);
} catch (InstantiationException | IllegalAccessException | InvocationTargetException | } catch (InstantiationException | IllegalAccessException | InvocationTargetException |

View File

@ -5,7 +5,6 @@ import io.metersphere.api.domain.*;
import io.metersphere.api.dto.ApiFile; import io.metersphere.api.dto.ApiFile;
import io.metersphere.api.dto.ApiRunModeRequest; import io.metersphere.api.dto.ApiRunModeRequest;
import io.metersphere.api.dto.ReferenceRequest; import io.metersphere.api.dto.ReferenceRequest;
import io.metersphere.api.dto.ResourceAddFileParam;
import io.metersphere.api.dto.assertion.MsAssertionConfig; import io.metersphere.api.dto.assertion.MsAssertionConfig;
import io.metersphere.api.dto.debug.ModuleCreateRequest; import io.metersphere.api.dto.debug.ModuleCreateRequest;
import io.metersphere.api.dto.definition.*; import io.metersphere.api.dto.definition.*;
@ -106,6 +105,9 @@ public class ApiScenarioControllerTests extends BaseTest {
private static final String UPDATE_STATUS = "update-status"; private static final String UPDATE_STATUS = "update-status";
private static final String UPDATE_PRIORITY = "update-priority"; private static final String UPDATE_PRIORITY = "update-priority";
private static final String BATCH_RUN = "batch-operation/run"; private static final String BATCH_RUN = "batch-operation/run";
private static final String TRANSFER_OPTIONS = "transfer/options/{0}";
private static final String TRANSFER = "transfer";
private static final String STEP_TRANSFER = "step/transfer";
private static final Map<String, String> BATCH_OPERATION_SCENARIO_MODULE_MAP = new HashMap<>(); private static final Map<String, String> BATCH_OPERATION_SCENARIO_MODULE_MAP = new HashMap<>();
private static final List<String> BATCH_OPERATION_SCENARIO_ID = new ArrayList<>(); private static final List<String> BATCH_OPERATION_SCENARIO_ID = new ArrayList<>();
@ -355,9 +357,6 @@ public class ApiScenarioControllerTests extends BaseTest {
request.setSteps(steps); request.setSteps(steps);
request.setStepDetails(steptDetailMap); request.setStepDetails(steptDetailMap);
request.setScenarioConfig(getScenarioConfig()); request.setScenarioConfig(getScenarioConfig());
String fileId = doUploadTempFile(getMockMultipartFile());
request.setFileParam(new ResourceAddFileParam());
request.getFileParam().setUploadFileIds(List.of(fileId));
MvcResult mvcResult = this.requestPostWithOkAndReturn(DEFAULT_ADD, request); MvcResult mvcResult = this.requestPostWithOkAndReturn(DEFAULT_ADD, request);
ApiScenario resultData = getResultData(mvcResult, ApiScenario.class); ApiScenario resultData = getResultData(mvcResult, ApiScenario.class);
this.addApiScenario = apiScenarioMapper.selectByPrimaryKey(resultData.getId()); this.addApiScenario = apiScenarioMapper.selectByPrimaryKey(resultData.getId());
@ -365,7 +364,6 @@ public class ApiScenarioControllerTests extends BaseTest {
assertUpdateSteps(steps, steptDetailMap); assertUpdateSteps(steps, steptDetailMap);
request.setName("anOther name"); request.setName("anOther name");
request.getFileParam().setUploadFileIds(List.of());
request.setGrouped(true); request.setGrouped(true);
request.setEnvironmentId(envGroupId); request.setEnvironmentId(envGroupId);
ApiScenarioStepRequest stepRequest = new ApiScenarioStepRequest(); ApiScenarioStepRequest stepRequest = new ApiScenarioStepRequest();
@ -387,6 +385,7 @@ public class ApiScenarioControllerTests extends BaseTest {
stepRequest2.setProjectId(DEFAULT_PROJECT_ID); stepRequest2.setProjectId(DEFAULT_PROJECT_ID);
steps = List.of(stepRequest, stepRequest2); steps = List.of(stepRequest, stepRequest2);
request.setSteps(steps); request.setSteps(steps);
request.getScenarioConfig().getVariable().getCsvVariables().forEach(csvVariable -> csvVariable.setId(UUID.randomUUID().toString()));
mvcResult = this.requestPostWithOkAndReturn(DEFAULT_ADD, request); mvcResult = this.requestPostWithOkAndReturn(DEFAULT_ADD, request);
this.anOtherAddApiScenario = apiScenarioMapper.selectByPrimaryKey(getResultData(mvcResult, ApiScenario.class).getId()); this.anOtherAddApiScenario = apiScenarioMapper.selectByPrimaryKey(getResultData(mvcResult, ApiScenario.class).getId());
assertUpdateApiScenario(request, request.getScenarioConfig(), anOtherAddApiScenario.getId()); assertUpdateApiScenario(request, request.getScenarioConfig(), anOtherAddApiScenario.getId());
@ -444,7 +443,7 @@ public class ApiScenarioControllerTests extends BaseTest {
} }
} }
private ScenarioConfig getScenarioConfig() throws Exception { private ScenarioConfig getScenarioConfig() {
ScenarioConfig scenarioConfig = new ScenarioConfig(); ScenarioConfig scenarioConfig = new ScenarioConfig();
MsAssertionConfig msAssertionConfig = new MsAssertionConfig(); MsAssertionConfig msAssertionConfig = new MsAssertionConfig();
MsScriptAssertion scriptAssertion = new MsScriptAssertion(); MsScriptAssertion scriptAssertion = new MsScriptAssertion();
@ -478,16 +477,20 @@ public class ApiScenarioControllerTests extends BaseTest {
fileMetadataId = fileMetadataService.upload(fileUploadRequest, "admin", file); fileMetadataId = fileMetadataService.upload(fileUploadRequest, "admin", file);
} }
public List<CsvVariable> getCsvVariables() throws Exception { public List<CsvVariable> getCsvVariables() {
List<CsvVariable> csvVariables = new ArrayList<>(); List<CsvVariable> csvVariables = new ArrayList<>();
CsvVariable csvVariable = new CsvVariable(); CsvVariable csvVariable = new CsvVariable();
csvVariable.setId(UUID.randomUUID().toString());
csvVariable.setFileId(localFileId); csvVariable.setFileId(localFileId);
csvVariable.setName("csv变量"); csvVariable.setName("csv变量");
csvVariable.setFileName("test.jbc");
csvVariable.setScope(CsvVariable.CsvVariableScope.SCENARIO.name()); csvVariable.setScope(CsvVariable.CsvVariableScope.SCENARIO.name());
csvVariables.add(csvVariable); csvVariables.add(csvVariable);
csvVariable = new CsvVariable(); csvVariable = new CsvVariable();
csvVariable.setId(UUID.randomUUID().toString());
csvVariable.setFileId(fileMetadataId); csvVariable.setFileId(fileMetadataId);
csvVariable.setName("csv-关联的"); csvVariable.setName("csv-关联的");
csvVariable.setFileName("test.jbc");
csvVariable.setScope(CsvVariable.CsvVariableScope.SCENARIO.name()); csvVariable.setScope(CsvVariable.CsvVariableScope.SCENARIO.name());
csvVariable.setAssociation(true); csvVariable.setAssociation(true);
csvVariables.add(csvVariable); csvVariables.add(csvVariable);
@ -506,7 +509,7 @@ public class ApiScenarioControllerTests extends BaseTest {
stepRequest.setStepType(ApiScenarioStepType.API_CASE.name()); stepRequest.setStepType(ApiScenarioStepType.API_CASE.name());
stepRequest.setProjectId(DEFAULT_PROJECT_ID); stepRequest.setProjectId(DEFAULT_PROJECT_ID);
stepRequest.setConfig(new HashMap<>()); stepRequest.setConfig(new HashMap<>());
stepRequest.setCsvFileIds(List.of(fileMetadataId)); stepRequest.setCsvIds(List.of(fileMetadataId));
ApiScenarioStepRequest stepRequest2 = new ApiScenarioStepRequest(); ApiScenarioStepRequest stepRequest2 = new ApiScenarioStepRequest();
stepRequest2.setId(IDGenerator.nextStr()); stepRequest2.setId(IDGenerator.nextStr());
@ -518,7 +521,7 @@ public class ApiScenarioControllerTests extends BaseTest {
stepRequest2.setStepType(ApiScenarioStepType.API_CASE.name()); stepRequest2.setStepType(ApiScenarioStepType.API_CASE.name());
stepRequest2.setRefType(ApiScenarioStepRefType.COPY.name()); stepRequest2.setRefType(ApiScenarioStepRefType.COPY.name());
stepRequest2.setProjectId(DEFAULT_PROJECT_ID); stepRequest2.setProjectId(DEFAULT_PROJECT_ID);
stepRequest2.setCsvFileIds(List.of(fileMetadataId)); stepRequest2.setCsvIds(List.of(fileMetadataId));
ApiScenarioStepRequest stepRequest3 = new ApiScenarioStepRequest(); ApiScenarioStepRequest stepRequest3 = new ApiScenarioStepRequest();
stepRequest3.setId(IDGenerator.nextStr()); stepRequest3.setId(IDGenerator.nextStr());
@ -809,7 +812,7 @@ public class ApiScenarioControllerTests extends BaseTest {
@Test @Test
@Order(7) @Order(7)
public void testTransfer() throws Exception { public void testTransfer() throws Exception {
this.requestGetWithOk("transfer/options/" + "/" + DEFAULT_PROJECT_ID); this.requestGetWithOk(TRANSFER_OPTIONS, DEFAULT_PROJECT_ID);
ApiTransferRequest apiTransferRequest = new ApiTransferRequest(); ApiTransferRequest apiTransferRequest = new ApiTransferRequest();
apiTransferRequest.setSourceId(addApiScenario.getId()); apiTransferRequest.setSourceId(addApiScenario.getId());
apiTransferRequest.setProjectId(DEFAULT_PROJECT_ID); apiTransferRequest.setProjectId(DEFAULT_PROJECT_ID);
@ -819,10 +822,10 @@ public class ApiScenarioControllerTests extends BaseTest {
apiTransferRequest.setOriginalName("test-scenario-file.txt"); apiTransferRequest.setOriginalName("test-scenario-file.txt");
String uploadFileId = doUploadTempFile(getMockMultipartFile()); String uploadFileId = doUploadTempFile(getMockMultipartFile());
apiTransferRequest.setFileId(uploadFileId); apiTransferRequest.setFileId(uploadFileId);
this.requestPost("transfer", apiTransferRequest).andExpect(status().isOk()); this.requestPost(TRANSFER, apiTransferRequest).andExpect(status().isOk());
//文件不存在 //文件不存在
apiTransferRequest.setFileId("111"); apiTransferRequest.setFileId("111");
this.requestPost("transfer", apiTransferRequest).andExpect(status().is5xxServerError()); this.requestPost(TRANSFER, apiTransferRequest).andExpect(status().is5xxServerError());
//文件已经上传 //文件已经上传
ApiFileResourceExample apiFileResourceExample = new ApiFileResourceExample(); ApiFileResourceExample apiFileResourceExample = new ApiFileResourceExample();
apiFileResourceExample.createCriteria().andResourceIdEqualTo(addApiScenario.getId()); apiFileResourceExample.createCriteria().andResourceIdEqualTo(addApiScenario.getId());
@ -831,8 +834,26 @@ public class ApiScenarioControllerTests extends BaseTest {
apiTransferRequest.setFileId(apiFileResources.get(0).getFileId()); apiTransferRequest.setFileId(apiFileResources.get(0).getFileId());
apiTransferRequest.setFileName("test-scenario-file-1"); apiTransferRequest.setFileName("test-scenario-file-1");
apiTransferRequest.setOriginalName("test-scenario-file-1.txt"); apiTransferRequest.setOriginalName("test-scenario-file-1.txt");
this.requestPost("transfer", apiTransferRequest).andExpect(status().isOk()); this.requestPost(TRANSFER, apiTransferRequest).andExpect(status().isOk());
}
@Test
@Order(7)
public void testStepTransfer() throws Exception {
this.requestGetWithOk(TRANSFER_OPTIONS, DEFAULT_PROJECT_ID);
ApiTransferRequest apiTransferRequest = new ApiTransferRequest();
apiTransferRequest.setSourceId(addApiScenarioSteps.get(0).getId());
apiTransferRequest.setProjectId(DEFAULT_PROJECT_ID);
apiTransferRequest.setModuleId("root");
apiTransferRequest.setLocal(true);
apiTransferRequest.setFileName("test-scenario-step-file");
apiTransferRequest.setOriginalName("test-scenario-step-file.txt");
String uploadFileId = doUploadTempFile(getMockMultipartFile());
apiTransferRequest.setFileId(uploadFileId);
this.requestPost(STEP_TRANSFER, apiTransferRequest).andExpect(status().isOk());
//文件不存在
apiTransferRequest.setFileId("111");
this.requestPost(STEP_TRANSFER, apiTransferRequest).andExpect(status().is5xxServerError());
} }
@Test @Test

View File

@ -316,7 +316,7 @@ public class FileAssociationService {
* @return * @return
* @throws Exception * @throws Exception
*/ */
public String transferAndAssociation(@Validated FileAssociationDTO fileAssociationDTO) throws Exception { public String transferAndAssociation(@Validated FileAssociationDTO fileAssociationDTO) {
FileAssociationSource source = extFileAssociationMapper.selectNameBySourceTableAndId(FileAssociationSourceUtil.getQuerySql(fileAssociationDTO.getSourceType()), fileAssociationDTO.getSourceId()); FileAssociationSource source = extFileAssociationMapper.selectNameBySourceTableAndId(FileAssociationSourceUtil.getQuerySql(fileAssociationDTO.getSourceType()), fileAssociationDTO.getSourceId());
this.validateSourceName(source); this.validateSourceName(source);
String fileId = fileMetadataService.transferFile( String fileId = fileMetadataService.transferFile(

View File

@ -22,10 +22,7 @@ import io.metersphere.sdk.exception.MSException;
import io.metersphere.sdk.file.FileRepository; import io.metersphere.sdk.file.FileRepository;
import io.metersphere.sdk.file.FileRequest; import io.metersphere.sdk.file.FileRequest;
import io.metersphere.sdk.file.MinioRepository; import io.metersphere.sdk.file.MinioRepository;
import io.metersphere.sdk.util.CommonBeanFactory; import io.metersphere.sdk.util.*;
import io.metersphere.sdk.util.GitRepositoryUtil;
import io.metersphere.sdk.util.TempFileUtils;
import io.metersphere.sdk.util.Translator;
import io.metersphere.system.mapper.BaseUserMapper; import io.metersphere.system.mapper.BaseUserMapper;
import io.metersphere.system.uid.IDGenerator; import io.metersphere.system.uid.IDGenerator;
import io.metersphere.system.utils.PageUtils; import io.metersphere.system.utils.PageUtils;
@ -214,7 +211,7 @@ public class FileMetadataService {
* @return * @return
* @throws Exception * @throws Exception
*/ */
public String transferFile(String fileName, String originFileName, String projectId, String moduleId, String operator, byte[] fileBytes) throws Exception { public String transferFile(String fileName, String originFileName, String projectId, String moduleId, String operator, byte[] fileBytes) {
if (StringUtils.isBlank(originFileName)) { if (StringUtils.isBlank(originFileName)) {
throw new MSException(Translator.get("file.name.cannot.be.empty")); throw new MSException(Translator.get("file.name.cannot.be.empty"));
} }
@ -228,7 +225,13 @@ public class FileMetadataService {
uploadFileRequest.setStorage(StorageType.MINIO.name()); uploadFileRequest.setStorage(StorageType.MINIO.name());
FileRepository minio = CommonBeanFactory.getBean(MinioRepository.class); FileRepository minio = CommonBeanFactory.getBean(MinioRepository.class);
String filePath = minio.saveFile(fileBytes, uploadFileRequest); String filePath;
try {
filePath = minio.saveFile(fileBytes, uploadFileRequest);
} catch (Exception e) {
LogUtils.error(e);
throw new MSException(Translator.get("file.transfer.failed"), e);
}
fileMetadata.setPath(filePath); fileMetadata.setPath(filePath);
fileMetadata.setFileVersion(fileMetadata.getId()); fileMetadata.setFileVersion(fileMetadata.getId());
fileMetadataMapper.insert(fileMetadata); fileMetadataMapper.insert(fileMetadata);