feat(接口测试): mock增加批量接口

This commit is contained in:
wxg0103 2024-05-07 16:55:36 +08:00 committed by Craftsman
parent 80fab6c61d
commit 43fa631e3a
18 changed files with 662 additions and 39 deletions

View File

@ -54,6 +54,9 @@ public class ApiDefinitionMock implements Serializable {
@Schema(description = "") @Schema(description = "")
private Integer statusCode; private Integer statusCode;
@Schema(description = "更新人")
private String updateUser;
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
public enum Column { public enum Column {
@ -67,7 +70,8 @@ public class ApiDefinitionMock implements Serializable {
expectNum("expect_num", "expectNum", "VARCHAR", false), expectNum("expect_num", "expectNum", "VARCHAR", false),
projectId("project_id", "projectId", "VARCHAR", false), projectId("project_id", "projectId", "VARCHAR", false),
apiDefinitionId("api_definition_id", "apiDefinitionId", "VARCHAR", false), apiDefinitionId("api_definition_id", "apiDefinitionId", "VARCHAR", false),
statusCode("status_code", "statusCode", "INTEGER", false); statusCode("status_code", "statusCode", "INTEGER", false),
updateUser("update_user", "updateUser", "VARCHAR", false);
private static final String BEGINNING_DELIMITER = "`"; private static final String BEGINNING_DELIMITER = "`";

View File

@ -867,6 +867,76 @@ public class ApiDefinitionMockExample {
addCriterion("status_code not between", value1, value2, "statusCode"); addCriterion("status_code not between", value1, value2, "statusCode");
return (Criteria) this; return (Criteria) this;
} }
public Criteria andUpdateUserIsNull() {
addCriterion("update_user is null");
return (Criteria) this;
}
public Criteria andUpdateUserIsNotNull() {
addCriterion("update_user is not null");
return (Criteria) this;
}
public Criteria andUpdateUserEqualTo(String value) {
addCriterion("update_user =", value, "updateUser");
return (Criteria) this;
}
public Criteria andUpdateUserNotEqualTo(String value) {
addCriterion("update_user <>", value, "updateUser");
return (Criteria) this;
}
public Criteria andUpdateUserGreaterThan(String value) {
addCriterion("update_user >", value, "updateUser");
return (Criteria) this;
}
public Criteria andUpdateUserGreaterThanOrEqualTo(String value) {
addCriterion("update_user >=", value, "updateUser");
return (Criteria) this;
}
public Criteria andUpdateUserLessThan(String value) {
addCriterion("update_user <", value, "updateUser");
return (Criteria) this;
}
public Criteria andUpdateUserLessThanOrEqualTo(String value) {
addCriterion("update_user <=", value, "updateUser");
return (Criteria) this;
}
public Criteria andUpdateUserLike(String value) {
addCriterion("update_user like", value, "updateUser");
return (Criteria) this;
}
public Criteria andUpdateUserNotLike(String value) {
addCriterion("update_user not like", value, "updateUser");
return (Criteria) this;
}
public Criteria andUpdateUserIn(List<String> values) {
addCriterion("update_user in", values, "updateUser");
return (Criteria) this;
}
public Criteria andUpdateUserNotIn(List<String> values) {
addCriterion("update_user not in", values, "updateUser");
return (Criteria) this;
}
public Criteria andUpdateUserBetween(String value1, String value2) {
addCriterion("update_user between", value1, value2, "updateUser");
return (Criteria) this;
}
public Criteria andUpdateUserNotBetween(String value1, String value2) {
addCriterion("update_user not between", value1, value2, "updateUser");
return (Criteria) this;
}
} }
public static class Criteria extends GeneratedCriteria { public static class Criteria extends GeneratedCriteria {

View File

@ -13,6 +13,7 @@
<result column="project_id" jdbcType="VARCHAR" property="projectId" /> <result column="project_id" jdbcType="VARCHAR" property="projectId" />
<result column="api_definition_id" jdbcType="VARCHAR" property="apiDefinitionId" /> <result column="api_definition_id" jdbcType="VARCHAR" property="apiDefinitionId" />
<result column="status_code" jdbcType="INTEGER" property="statusCode" /> <result column="status_code" jdbcType="INTEGER" property="statusCode" />
<result column="update_user" jdbcType="VARCHAR" property="updateUser" />
</resultMap> </resultMap>
<sql id="Example_Where_Clause"> <sql id="Example_Where_Clause">
<where> <where>
@ -112,7 +113,7 @@
</sql> </sql>
<sql id="Base_Column_List"> <sql id="Base_Column_List">
id, create_time, update_time, create_user, `name`, tags, `enable`, expect_num, project_id, id, create_time, update_time, create_user, `name`, tags, `enable`, expect_num, project_id,
api_definition_id, status_code api_definition_id, status_code, update_user
</sql> </sql>
<select id="selectByExample" parameterType="io.metersphere.api.domain.ApiDefinitionMockExample" resultMap="BaseResultMap"> <select id="selectByExample" parameterType="io.metersphere.api.domain.ApiDefinitionMockExample" resultMap="BaseResultMap">
select select
@ -148,11 +149,13 @@
insert into api_definition_mock (id, create_time, update_time, insert into api_definition_mock (id, create_time, update_time,
create_user, `name`, tags, create_user, `name`, tags,
`enable`, expect_num, project_id, `enable`, expect_num, project_id,
api_definition_id, status_code) api_definition_id, status_code, update_user
)
values (#{id,jdbcType=VARCHAR}, #{createTime,jdbcType=BIGINT}, #{updateTime,jdbcType=BIGINT}, values (#{id,jdbcType=VARCHAR}, #{createTime,jdbcType=BIGINT}, #{updateTime,jdbcType=BIGINT},
#{createUser,jdbcType=VARCHAR}, #{name,jdbcType=VARCHAR}, #{tags,jdbcType=VARCHAR,typeHandler=io.metersphere.handler.ListTypeHandler}, #{createUser,jdbcType=VARCHAR}, #{name,jdbcType=VARCHAR}, #{tags,jdbcType=VARCHAR,typeHandler=io.metersphere.handler.ListTypeHandler},
#{enable,jdbcType=BIT}, #{expectNum,jdbcType=VARCHAR}, #{projectId,jdbcType=VARCHAR}, #{enable,jdbcType=BIT}, #{expectNum,jdbcType=VARCHAR}, #{projectId,jdbcType=VARCHAR},
#{apiDefinitionId,jdbcType=VARCHAR}, #{statusCode,jdbcType=INTEGER}) #{apiDefinitionId,jdbcType=VARCHAR}, #{statusCode,jdbcType=INTEGER}, #{updateUser,jdbcType=VARCHAR}
)
</insert> </insert>
<insert id="insertSelective" parameterType="io.metersphere.api.domain.ApiDefinitionMock"> <insert id="insertSelective" parameterType="io.metersphere.api.domain.ApiDefinitionMock">
insert into api_definition_mock insert into api_definition_mock
@ -190,6 +193,9 @@
<if test="statusCode != null"> <if test="statusCode != null">
status_code, status_code,
</if> </if>
<if test="updateUser != null">
update_user,
</if>
</trim> </trim>
<trim prefix="values (" suffix=")" suffixOverrides=","> <trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="id != null"> <if test="id != null">
@ -225,6 +231,9 @@
<if test="statusCode != null"> <if test="statusCode != null">
#{statusCode,jdbcType=INTEGER}, #{statusCode,jdbcType=INTEGER},
</if> </if>
<if test="updateUser != null">
#{updateUser,jdbcType=VARCHAR},
</if>
</trim> </trim>
</insert> </insert>
<select id="countByExample" parameterType="io.metersphere.api.domain.ApiDefinitionMockExample" resultType="java.lang.Long"> <select id="countByExample" parameterType="io.metersphere.api.domain.ApiDefinitionMockExample" resultType="java.lang.Long">
@ -269,6 +278,9 @@
<if test="record.statusCode != null"> <if test="record.statusCode != null">
status_code = #{record.statusCode,jdbcType=INTEGER}, status_code = #{record.statusCode,jdbcType=INTEGER},
</if> </if>
<if test="record.updateUser != null">
update_user = #{record.updateUser,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" />
@ -286,7 +298,8 @@
expect_num = #{record.expectNum,jdbcType=VARCHAR}, expect_num = #{record.expectNum,jdbcType=VARCHAR},
project_id = #{record.projectId,jdbcType=VARCHAR}, project_id = #{record.projectId,jdbcType=VARCHAR},
api_definition_id = #{record.apiDefinitionId,jdbcType=VARCHAR}, api_definition_id = #{record.apiDefinitionId,jdbcType=VARCHAR},
status_code = #{record.statusCode,jdbcType=INTEGER} status_code = #{record.statusCode,jdbcType=INTEGER},
update_user = #{record.updateUser,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>
@ -324,6 +337,9 @@
<if test="statusCode != null"> <if test="statusCode != null">
status_code = #{statusCode,jdbcType=INTEGER}, status_code = #{statusCode,jdbcType=INTEGER},
</if> </if>
<if test="updateUser != null">
update_user = #{updateUser,jdbcType=VARCHAR},
</if>
</set> </set>
where id = #{id,jdbcType=VARCHAR} where id = #{id,jdbcType=VARCHAR}
</update> </update>
@ -338,19 +354,21 @@
expect_num = #{expectNum,jdbcType=VARCHAR}, expect_num = #{expectNum,jdbcType=VARCHAR},
project_id = #{projectId,jdbcType=VARCHAR}, project_id = #{projectId,jdbcType=VARCHAR},
api_definition_id = #{apiDefinitionId,jdbcType=VARCHAR}, api_definition_id = #{apiDefinitionId,jdbcType=VARCHAR},
status_code = #{statusCode,jdbcType=INTEGER} status_code = #{statusCode,jdbcType=INTEGER},
update_user = #{updateUser,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_definition_mock insert into api_definition_mock
(id, create_time, update_time, create_user, `name`, tags, `enable`, expect_num, project_id, (id, create_time, update_time, create_user, `name`, tags, `enable`, expect_num, project_id,
api_definition_id, status_code) api_definition_id, status_code, update_user)
values values
<foreach collection="list" item="item" separator=","> <foreach collection="list" item="item" separator=",">
(#{item.id,jdbcType=VARCHAR}, #{item.createTime,jdbcType=BIGINT}, #{item.updateTime,jdbcType=BIGINT}, (#{item.id,jdbcType=VARCHAR}, #{item.createTime,jdbcType=BIGINT}, #{item.updateTime,jdbcType=BIGINT},
#{item.createUser,jdbcType=VARCHAR}, #{item.name,jdbcType=VARCHAR}, #{item.tags,jdbcType=VARCHAR,typeHandler=io.metersphere.handler.ListTypeHandler}, #{item.createUser,jdbcType=VARCHAR}, #{item.name,jdbcType=VARCHAR}, #{item.tags,jdbcType=VARCHAR,typeHandler=io.metersphere.handler.ListTypeHandler},
#{item.enable,jdbcType=BIT}, #{item.expectNum,jdbcType=VARCHAR}, #{item.projectId,jdbcType=VARCHAR}, #{item.enable,jdbcType=BIT}, #{item.expectNum,jdbcType=VARCHAR}, #{item.projectId,jdbcType=VARCHAR},
#{item.apiDefinitionId,jdbcType=VARCHAR}, #{item.statusCode,jdbcType=INTEGER}) #{item.apiDefinitionId,jdbcType=VARCHAR}, #{item.statusCode,jdbcType=INTEGER},
#{item.updateUser,jdbcType=VARCHAR})
</foreach> </foreach>
</insert> </insert>
<insert id="batchInsertSelective" parameterType="map"> <insert id="batchInsertSelective" parameterType="map">
@ -396,6 +414,9 @@
<if test="'status_code'.toString() == column.value"> <if test="'status_code'.toString() == column.value">
#{item.statusCode,jdbcType=INTEGER} #{item.statusCode,jdbcType=INTEGER}
</if> </if>
<if test="'update_user'.toString() == column.value">
#{item.updateUser,jdbcType=VARCHAR}
</if>
</foreach> </foreach>
) )
</foreach> </foreach>

View File

@ -13,6 +13,8 @@ ALTER TABLE test_plan_config DROP COLUMN run_mode_config;
ALTER TABLE test_plan_config ADD COLUMN test_planning BIT NOT NULL DEFAULT 0 COMMENT '是否开启测试规划'; ALTER TABLE test_plan_config ADD COLUMN test_planning BIT NOT NULL DEFAULT 0 COMMENT '是否开启测试规划';
ALTER TABLE api_definition_mock ADD COLUMN update_user VARCHAR(50) COMMENT '更新人';
-- 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

@ -6,6 +6,8 @@ import io.metersphere.api.constants.ApiResource;
import io.metersphere.api.constants.ApiResourceType; import io.metersphere.api.constants.ApiResourceType;
import io.metersphere.api.domain.ApiDefinitionMock; import io.metersphere.api.domain.ApiDefinitionMock;
import io.metersphere.api.dto.definition.ApiDefinitionMockDTO; import io.metersphere.api.dto.definition.ApiDefinitionMockDTO;
import io.metersphere.api.dto.definition.ApiMockBatchEditRequest;
import io.metersphere.api.dto.definition.ApiTestCaseBatchRequest;
import io.metersphere.api.dto.definition.request.ApiDefinitionMockAddRequest; import io.metersphere.api.dto.definition.request.ApiDefinitionMockAddRequest;
import io.metersphere.api.dto.definition.request.ApiDefinitionMockPageRequest; import io.metersphere.api.dto.definition.request.ApiDefinitionMockPageRequest;
import io.metersphere.api.dto.definition.request.ApiDefinitionMockRequest; import io.metersphere.api.dto.definition.request.ApiDefinitionMockRequest;
@ -104,9 +106,10 @@ public class ApiDefinitionMockController {
@RequiresPermissions(PermissionConstants.PROJECT_API_DEFINITION_MOCK_UPDATE) @RequiresPermissions(PermissionConstants.PROJECT_API_DEFINITION_MOCK_UPDATE)
@Log(type = OperationLogType.UPDATE, expression = "#msClass.updateEnableLog(#id)", msClass = ApiDefinitionMockLogService.class) @Log(type = OperationLogType.UPDATE, expression = "#msClass.updateEnableLog(#id)", msClass = ApiDefinitionMockLogService.class)
@CheckOwner(resourceId = "#id", resourceType = "api_definition_mock") @CheckOwner(resourceId = "#id", resourceType = "api_definition_mock")
@SendNotice(taskType = NoticeConstants.TaskType.API_DEFINITION_TASK, event = NoticeConstants.Event.MOCK_UPDATE, target = "#targetClass.getApiMockDTO(#id)", targetClass = ApiDefinitionMockNoticeService.class)
public void updateEnable(@PathVariable String id) { public void updateEnable(@PathVariable String id) {
apiValidateService.validateApiMenuInProject(id, ApiResource.API_DEFINITION_MOCK.name()); apiValidateService.validateApiMenuInProject(id, ApiResource.API_DEFINITION_MOCK.name());
apiDefinitionMockService.updateEnable(id); apiDefinitionMockService.updateEnable(id, SessionUtils.getUserId());
} }
@PostMapping(value = "/delete") @PostMapping(value = "/delete")
@ -153,5 +156,30 @@ public class ApiDefinitionMockController {
return apiFileResourceService.transfer(request, SessionUtils.getUserId(), ApiResourceType.API_MOCK.name()); return apiFileResourceService.transfer(request, SessionUtils.getUserId(), ApiResourceType.API_MOCK.name());
} }
@GetMapping("/get-url/{id}")
@Operation(summary = "接口测试-接口管理-获取 Mock URL")
@RequiresPermissions(PermissionConstants.PROJECT_API_DEFINITION_MOCK_READ)
@CheckOwner(resourceId = "#id", resourceType = "api_definition_mock")
public String getMockUrl(@PathVariable String id) {
return apiDefinitionMockService.getMockUrl(id);
}
@PostMapping("/batch/delete")
@Operation(summary = "接口测试-接口管理-mock-批量删除")
@RequiresPermissions(PermissionConstants.PROJECT_API_DEFINITION_MOCK_DELETE)
@CheckOwner(resourceId = "#request.getSelectIds()", resourceType = "api_definition_mock")
public void deleteBatchByParam(@RequestBody ApiTestCaseBatchRequest request) {
apiDefinitionMockService.batchDelete(request, SessionUtils.getUserId());
}
@PostMapping("/batch/edit")
@Operation(summary = "接口测试-接口管理-mock-批量编辑")
@RequiresPermissions(PermissionConstants.PROJECT_API_DEFINITION_MOCK_UPDATE)
@CheckOwner(resourceId = "#request.getSelectIds()", resourceType = "api_definition_mock")
public void batchUpdate(@Validated @RequestBody ApiMockBatchEditRequest request) {
apiDefinitionMockService.batchEdit(request, SessionUtils.getUserId());
}
} }

View File

@ -0,0 +1,41 @@
package io.metersphere.api.dto.definition;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
@Data
@EqualsAndHashCode(callSuper = false)
public class ApiMockBatchEditRequest extends ApiTestCaseBatchRequest implements Serializable {
private static final long serialVersionUID = 1L;
@Schema(description = "标签")
private LinkedHashSet<
@NotBlank
@Size(min = 1, max = 64, message = "{api_test_case.tag.length_range}")
String> tags;
@Schema(description = "批量编辑的类型 状态 :Status,标签: Tags")
@NotBlank
private String type;
@Schema(description = "默认覆盖原标签")
private boolean append = false;
@Schema(description = "状态 开启/关闭")
private boolean enable;
public List<String> getTags() {
if (tags == null) {
return new ArrayList<>(0);
} else {
return new ArrayList<>(tags);
}
}
}

View File

@ -1,5 +1,6 @@
package io.metersphere.api.dto.definition.request; package io.metersphere.api.dto.definition.request;
import io.metersphere.sdk.constants.ModuleConstants;
import io.metersphere.system.dto.sdk.BasePageRequest; import io.metersphere.system.dto.sdk.BasePageRequest;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotBlank;
@ -7,6 +8,8 @@ import jakarta.validation.constraints.Size;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import java.util.List;
/** /**
* @author lan * @author lan
*/ */
@ -14,10 +17,6 @@ import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = false) @EqualsAndHashCode(callSuper = false)
public class ApiDefinitionMockPageRequest extends BasePageRequest { public class ApiDefinitionMockPageRequest extends BasePageRequest {
@Schema(description = "接口 mock pk")
@Size(min = 1, max = 50, message = "{api_definition_mock.id.length_range}")
private String id;
@Schema(description = "接口 mock 名称") @Schema(description = "接口 mock 名称")
@Size(min = 1, max = 255, message = "{api_definition_mock.name.length_range}") @Size(min = 1, max = 255, message = "{api_definition_mock.name.length_range}")
private String name; private String name;
@ -34,4 +33,12 @@ public class ApiDefinitionMockPageRequest extends BasePageRequest {
@Size(min = 1, max = 50, message = "{api_definition_mock.api_definition_id.length_range}") @Size(min = 1, max = 50, message = "{api_definition_mock.api_definition_id.length_range}")
private String apiDefinitionId; private String apiDefinitionId;
@Schema(description = "接口协议", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{api_definition.protocol.not_blank}")
@Size(min = 1, max = 20, message = "{api_definition.protocol.length_range}")
private String protocol = ModuleConstants.NODE_PROTOCOL_HTTP;
@Schema(description = "模块ID")
private List<@NotBlank String> moduleIds;
} }

View File

@ -1,5 +1,6 @@
package io.metersphere.api.dto.mockserver; package io.metersphere.api.dto.mockserver;
import io.metersphere.api.dto.definition.ResponseBinaryBody;
import io.metersphere.sdk.util.JSON; import io.metersphere.sdk.util.JSON;
import io.metersphere.sdk.util.XMLUtils; import io.metersphere.sdk.util.XMLUtils;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
@ -18,6 +19,8 @@ public class BodyParamMatchRule {
private keyValueMatchRule formDataMatch; private keyValueMatchRule formDataMatch;
@Schema(description = "文本匹配规则") @Schema(description = "文本匹配规则")
private String raw; private String raw;
@Schema(description = "文件匹配规则")
private ResponseBinaryBody binaryBody = new ResponseBinaryBody();
public boolean matchXml(Map<String, Object> requestMap) { public boolean matchXml(Map<String, Object> requestMap) {
Map<String, Object> mockMap = XMLUtils.xmlStringToJson(raw); Map<String, Object> mockMap = XMLUtils.xmlStringToJson(raw);

View File

@ -13,6 +13,10 @@ public class KeyValueInfo {
private String key; private String key;
@Schema(description = "Value") @Schema(description = "Value")
private String value; private String value;
/**
* 默认等于可选等于不等于长度等于长度大于长度小于包含不包含为空非空正则匹配
* 文件类型的参数等于不等于为空非空
*/
@Schema(description = "条件") @Schema(description = "条件")
private String condition; private String condition;
@Schema(description = "描述") @Schema(description = "描述")
@ -27,28 +31,28 @@ public class KeyValueInfo {
return StringUtils.contains(this.value, value); return StringUtils.contains(this.value, value);
} else if (StringUtils.equals(this.condition, ParamConditionEnums.LENGTH_EQUALS.name())) { } else if (StringUtils.equals(this.condition, ParamConditionEnums.LENGTH_EQUALS.name())) {
try { try {
int length = Integer.parseInt(value); int length = value.length();
return this.value.length() == length; return this.value.length() == length;
} catch (Exception e) { } catch (Exception e) {
return false; return false;
} }
} else if (StringUtils.equals(this.condition, ParamConditionEnums.LENGTH_NOT_EQUALS.name())) { } else if (StringUtils.equals(this.condition, ParamConditionEnums.LENGTH_NOT_EQUALS.name())) {
try { try {
int length = Integer.parseInt(value); int length = value.length();
return this.value.length() != length; return this.value.length() != length;
} catch (Exception e) { } catch (Exception e) {
return false; return false;
} }
} else if (StringUtils.equals(this.condition, ParamConditionEnums.LENGTH_SHOT.name())) { } else if (StringUtils.equals(this.condition, ParamConditionEnums.LENGTH_SHOT.name())) {
try { try {
int length = Integer.parseInt(value); int length = value.length();
return this.value.length() < length; return this.value.length() < length;
} catch (Exception e) { } catch (Exception e) {
return false; return false;
} }
} else if (StringUtils.equals(this.condition, ParamConditionEnums.LENGTH_LARGE.name())) { } else if (StringUtils.equals(this.condition, ParamConditionEnums.LENGTH_LARGE.name())) {
try { try {
int length = Integer.parseInt(value); int length = value.length();
return this.value.length() > length; return this.value.length() > length;
} catch (Exception e) { } catch (Exception e) {
return false; return false;

View File

@ -24,29 +24,29 @@ public class MockMatchRule implements Serializable {
private BodyParamMatchRule body = new BodyParamMatchRule(); private BodyParamMatchRule body = new BodyParamMatchRule();
public boolean keyValueMatch(String matchType, Map<String, String> matchParam) { public boolean keyValueMatch(String matchType, Map<String, String> matchParam) {
keyValueMatchRule matchRole = null; keyValueMatchRule matchRule = null;
switch (matchType) { switch (matchType) {
case "header": case "header":
matchRole = header; matchRule = header;
break; break;
case "query": case "query":
matchRole = query; matchRule = query;
break; break;
case "rest": case "rest":
matchRole = rest; matchRule = rest;
break; break;
case "body": case "body":
if (body != null) { if (body != null) {
matchRole = body.getFormDataMatch(); matchRule = body.getFormDataMatch();
} }
break; break;
default: default:
break; break;
} }
if (matchRole == null) { if (matchRule == null) {
return true; return true;
} }
return matchRole.match(matchParam); return matchRule.match(matchParam);
} }
public boolean requestParamMatch(HttpRequestParam httpRequestParam) { public boolean requestParamMatch(HttpRequestParam httpRequestParam) {

View File

@ -1,6 +1,8 @@
package io.metersphere.api.mapper; package io.metersphere.api.mapper;
import io.metersphere.api.domain.ApiDefinitionMock;
import io.metersphere.api.dto.definition.ApiDefinitionMockDTO; import io.metersphere.api.dto.definition.ApiDefinitionMockDTO;
import io.metersphere.api.dto.definition.ApiTestCaseBatchRequest;
import io.metersphere.api.dto.definition.request.ApiDefinitionMockPageRequest; import io.metersphere.api.dto.definition.request.ApiDefinitionMockPageRequest;
import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Param;
@ -11,4 +13,9 @@ public interface ExtApiDefinitionMockMapper {
List<ApiDefinitionMockDTO> list(@Param("request") ApiDefinitionMockPageRequest request); List<ApiDefinitionMockDTO> list(@Param("request") ApiDefinitionMockPageRequest request);
List<String> getIdsByApiIds(@Param("ids") List<String> ids); List<String> getIdsByApiIds(@Param("ids") List<String> ids);
List<String> getIds(@Param("request")ApiTestCaseBatchRequest request);
List<ApiDefinitionMock> getTagsByIds(@Param("ids") List<String> ids);
List<ApiDefinitionMock> getMockInfoByIds(@Param("ids") List<String> ids);
} }

View File

@ -27,6 +27,65 @@
#{id} #{id}
</foreach> </foreach>
</select> </select>
<select id="getIds" resultType="java.lang.String">
SELECT
m.id
FROM
api_definition_mock m
INNER JOIN api_definition a ON m.api_definition_id = a.id
<include refid="queryWhereConditionByBatch"/>
</select>
<select id="getTagsByIds" resultType="io.metersphere.api.domain.ApiDefinitionMock">
SELECT
m.id, m.tags
FROM
api_definition_mock m
WHERE m.id in
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</select>
<select id="getMockInfoByIds" resultType="io.metersphere.api.domain.ApiDefinitionMock">
SELECT
m.id, m.name
FROM
api_definition_mock m
WHERE m.id in
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</select>
<sql id="queryWhereConditionByBatch">
<if test="request.protocol != null and request.protocol!=''">
and a.protocol = #{request.protocol}
</if>
<if test="request.apiDefinitionId != null and request.apiDefinitionId!=''">
and m.api_definition_id = #{request.apiDefinitionId}
</if>
<if test="request.projectId != null and request.projectId!=''">
and m.project_id = #{request.projectId}
</if>
<if test="request.condition.keyword != null and request.condition.keyword !=''">
and (
m.name like concat('%', #{request.condition.keyword},'%')
or m.expect_num like concat('%', #{request.condition.keyword},'%')
or a.path like concat('%', #{request.condition.keyword},'%')
or m.tags like concat('%', #{request.condition.keyword},'%')
)
</if>
<if test="request.moduleIds != null and request.moduleIds.size() > 0">
and a.module_id in
<foreach collection="request.moduleIds" item="nodeId" separator="," open="(" close=")">
#{nodeId}
</foreach>
</if>
<include refid="filters">
<property name="filter" value="request.condition.filter"/>
</include>
</sql>
<sql id="queryWhereCondition"> <sql id="queryWhereCondition">
@ -44,6 +103,16 @@
<if test="request.apiDefinitionId != null and request.apiDefinitionId != ''"> <if test="request.apiDefinitionId != null and request.apiDefinitionId != ''">
and m.api_definition_id = #{request.apiDefinitionId} and m.api_definition_id = #{request.apiDefinitionId}
</if> </if>
<if test="request.protocol != null and request.protocol!=''">
and d.protocol = #{request.protocol}
</if>
<if test="request.moduleIds != null and request.moduleIds.size() > 0">
and d.module_id in
<foreach collection="request.moduleIds" item="nodeId" separator="," open="(" close=")">
#{nodeId}
</foreach>
</if>
<include refid="filters"> <include refid="filters">
<property name="filter" value="request.filter"/> <property name="filter" value="request.filter"/>

View File

@ -1,21 +1,36 @@
package io.metersphere.api.service.definition; package io.metersphere.api.service.definition;
import io.metersphere.api.domain.ApiDefinitionMock; import io.metersphere.api.domain.*;
import io.metersphere.api.dto.definition.ApiDefinitionMockDTO; import io.metersphere.api.dto.definition.ApiDefinitionMockDTO;
import io.metersphere.api.dto.definition.request.ApiDefinitionMockAddRequest; import io.metersphere.api.dto.definition.request.ApiDefinitionMockAddRequest;
import io.metersphere.api.dto.definition.request.ApiDefinitionMockRequest; import io.metersphere.api.dto.definition.request.ApiDefinitionMockRequest;
import io.metersphere.api.dto.definition.request.ApiDefinitionMockUpdateRequest; import io.metersphere.api.dto.definition.request.ApiDefinitionMockUpdateRequest;
import io.metersphere.api.dto.mockserver.MockMatchRule;
import io.metersphere.api.dto.mockserver.MockResponse;
import io.metersphere.api.mapper.ApiDefinitionMockConfigMapper;
import io.metersphere.api.mapper.ApiDefinitionMockMapper; import io.metersphere.api.mapper.ApiDefinitionMockMapper;
import io.metersphere.api.utils.ApiDataUtils;
import io.metersphere.project.domain.Project;
import io.metersphere.project.mapper.ProjectMapper;
import io.metersphere.sdk.constants.HttpMethodConstants; import io.metersphere.sdk.constants.HttpMethodConstants;
import io.metersphere.sdk.util.BeanUtils; import io.metersphere.sdk.util.BeanUtils;
import io.metersphere.sdk.util.JSON; import io.metersphere.sdk.util.JSON;
import io.metersphere.system.dto.builder.LogDTOBuilder;
import io.metersphere.system.log.aspect.OperationLogAspect;
import io.metersphere.system.log.constants.OperationLogModule; import io.metersphere.system.log.constants.OperationLogModule;
import io.metersphere.system.log.constants.OperationLogType; import io.metersphere.system.log.constants.OperationLogType;
import io.metersphere.system.log.dto.LogDTO; import io.metersphere.system.log.dto.LogDTO;
import io.metersphere.system.log.service.OperationLogService;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
/** /**
* @author: LAN * @author: LAN
* @date: 2023/12/7 17:29 * @date: 2023/12/7 17:29
@ -29,7 +44,11 @@ public class ApiDefinitionMockLogService {
private ApiDefinitionMockMapper apiDefinitionMockMapper; private ApiDefinitionMockMapper apiDefinitionMockMapper;
@Resource @Resource
private ApiDefinitionMockService apiDefinitionMockService; private ApiDefinitionMockConfigMapper apiDefinitionMockConfigMapper;
@Resource
private OperationLogService operationLogService;
@Resource
private ProjectMapper projectMapper;
/** /**
* 添加日志 * 添加日志
@ -150,10 +169,77 @@ public class ApiDefinitionMockLogService {
ApiDefinitionMockDTO apiDefinitionMockDTO = new ApiDefinitionMockDTO(); ApiDefinitionMockDTO apiDefinitionMockDTO = new ApiDefinitionMockDTO();
ApiDefinitionMock apiDefinitionMock = apiDefinitionMockMapper.selectByPrimaryKey(id); ApiDefinitionMock apiDefinitionMock = apiDefinitionMockMapper.selectByPrimaryKey(id);
if(null != apiDefinitionMock){ if(null != apiDefinitionMock){
apiDefinitionMockService.handleMockConfig(id, apiDefinitionMockDTO); handleMockConfig(id, apiDefinitionMockDTO);
BeanUtils.copyBean(apiDefinitionMockDTO, apiDefinitionMock); BeanUtils.copyBean(apiDefinitionMockDTO, apiDefinitionMock);
} }
return apiDefinitionMockDTO; return apiDefinitionMockDTO;
} }
public void handleMockConfig(String id, ApiDefinitionMockDTO apiDefinitionMockDTO) {
Optional<ApiDefinitionMockConfig> apiDefinitionMockConfigOptional = Optional.ofNullable(apiDefinitionMockConfigMapper.selectByPrimaryKey(id));
apiDefinitionMockConfigOptional.ifPresent(config -> {
apiDefinitionMockDTO.setMatching(ApiDataUtils.parseObject(new String(config.getMatching()), MockMatchRule.class));
apiDefinitionMockDTO.setResponse(ApiDataUtils.parseObject(new String(config.getResponse()), MockResponse.class));
});
}
public void batchEditLog(List<ApiDefinitionMock> apiMocks, String operator, String projectId) {
Project project = projectMapper.selectByPrimaryKey(projectId);
List<String> mockIds = apiMocks.stream().map(ApiDefinitionMock::getId).distinct().toList();
ApiDefinitionMockExample example = new ApiDefinitionMockExample();
example.createCriteria().andIdIn(mockIds);
List<ApiDefinitionMock> apiMockLists = apiDefinitionMockMapper.selectByExample(example);
Map<String, ApiDefinitionMock> mockMap = apiMockLists.stream().collect(Collectors.toMap(ApiDefinitionMock::getId, a -> a));
ApiDefinitionMockConfigExample blobExample = new ApiDefinitionMockConfigExample();
blobExample.createCriteria().andIdIn(mockIds);
List<ApiDefinitionMockConfig> blobList = apiDefinitionMockConfigMapper.selectByExampleWithBLOBs(blobExample);
Map<String, ApiDefinitionMockConfig> blobMap = blobList.stream().collect(Collectors.toMap(ApiDefinitionMockConfig::getId, a -> a));
List<LogDTO> logs = new ArrayList<>();
apiMocks.forEach(item -> {
ApiDefinitionMockDTO mockDTO = new ApiDefinitionMockDTO();
BeanUtils.copyBean(mockDTO, mockMap.get(item.getId()));
if (blobMap.get(item.getId()) != null) {
mockDTO.setMatching(ApiDataUtils.parseObject(new String(blobMap.get(item.getId()).getMatching()), MockMatchRule.class));
mockDTO.setResponse(ApiDataUtils.parseObject(new String(blobMap.get(item.getId()).getResponse()), MockResponse.class));
}
LogDTO dto = LogDTOBuilder.builder()
.projectId(project.getId())
.organizationId(project.getOrganizationId())
.type(OperationLogType.UPDATE.name())
.module(OperationLogModule.API_TEST_MANAGEMENT_MOCK)
.method(HttpMethodConstants.POST.name())
.path(OperationLogAspect.getPath())
.sourceId(item.getId())
.content(item.getName())
.createUser(operator)
.originalValue(JSON.toJSONBytes(mockDTO))
.build().getLogDTO();
dto.setHistory(true);
logs.add(dto);
}
);
operationLogService.batchAdd(logs);
}
public void deleteBatchLog(List<ApiDefinitionMock> mockList, String userId, String projectId) {
Project project = projectMapper.selectByPrimaryKey(projectId);
List<LogDTO> logs = new ArrayList<>();
mockList.forEach(item -> {
LogDTO dto = LogDTOBuilder.builder()
.projectId(project.getId())
.organizationId(project.getOrganizationId())
.type(OperationLogType.DELETE.name())
.module(OperationLogModule.API_TEST_MANAGEMENT_MOCK)
.method(HttpMethodConstants.POST.name())
.sourceId(item.getId())
.content(item.getName())
.path(OperationLogAspect.getPath())
.createUser(userId)
.build().getLogDTO();
logs.add(dto);
}
);
operationLogService.batchAdd(logs);
operationLogService.deleteBySourceIds(mockList.stream().map(ApiDefinitionMock::getId).toList());
}
} }

View File

@ -2,15 +2,27 @@ package io.metersphere.api.service.definition;
import io.metersphere.api.domain.ApiDefinition; import io.metersphere.api.domain.ApiDefinition;
import io.metersphere.api.domain.ApiDefinitionMock; import io.metersphere.api.domain.ApiDefinitionMock;
import io.metersphere.api.domain.ApiDefinitionMockExample;
import io.metersphere.api.dto.definition.request.ApiDefinitionMockAddRequest; import io.metersphere.api.dto.definition.request.ApiDefinitionMockAddRequest;
import io.metersphere.api.dto.definition.request.ApiDefinitionMockUpdateRequest; import io.metersphere.api.dto.definition.request.ApiDefinitionMockUpdateRequest;
import io.metersphere.api.mapper.ApiDefinitionMapper; import io.metersphere.api.mapper.ApiDefinitionMapper;
import io.metersphere.api.mapper.ApiDefinitionMockMapper; import io.metersphere.api.mapper.ApiDefinitionMockMapper;
import io.metersphere.sdk.util.BeanUtils; import io.metersphere.sdk.util.BeanUtils;
import io.metersphere.sdk.util.JSON;
import io.metersphere.sdk.util.SubListUtils;
import io.metersphere.system.domain.User;
import io.metersphere.system.dto.sdk.ApiDefinitionCaseDTO; import io.metersphere.system.dto.sdk.ApiDefinitionCaseDTO;
import io.metersphere.system.mapper.UserMapper;
import io.metersphere.system.notice.constants.NoticeConstants;
import io.metersphere.system.service.CommonNoticeSendService;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@Service @Service
public class ApiDefinitionMockNoticeService { public class ApiDefinitionMockNoticeService {
@ -19,6 +31,10 @@ public class ApiDefinitionMockNoticeService {
@Resource @Resource
private ApiDefinitionMapper apiDefinitionMapper; private ApiDefinitionMapper apiDefinitionMapper;
@Resource
private UserMapper userMapper;
@Resource
private CommonNoticeSendService commonNoticeSendService;
public ApiDefinitionCaseDTO getApiMockDTO(ApiDefinitionMockAddRequest request) { public ApiDefinitionCaseDTO getApiMockDTO(ApiDefinitionMockAddRequest request) {
ApiDefinitionCaseDTO mockDTO = new ApiDefinitionCaseDTO(); ApiDefinitionCaseDTO mockDTO = new ApiDefinitionCaseDTO();
@ -45,5 +61,25 @@ public class ApiDefinitionMockNoticeService {
return mockDTO; return mockDTO;
} }
public void batchSendNotice(List<String> ids, String userId, String projectId, String event) {
if (CollectionUtils.isNotEmpty(ids)) {
User user = userMapper.selectByPrimaryKey(userId);
SubListUtils.dealForSubList(ids, 100, (subList) -> {
ApiDefinitionMockExample example = new ApiDefinitionMockExample();
example.createCriteria().andIdIn(subList);
List<ApiDefinitionMock> apiMocks = apiDefinitionMockMapper.selectByExample(example);
List<ApiDefinitionCaseDTO> noticeLists = apiMocks.stream()
.map(apiTestCase -> {
ApiDefinitionCaseDTO apiDefinitionCaseDTO = new ApiDefinitionCaseDTO();
BeanUtils.copyBean(apiDefinitionCaseDTO, apiTestCase);
return apiDefinitionCaseDTO;
})
.toList();
List<Map> resources = new ArrayList<>(JSON.parseArray(JSON.toJSONString(noticeLists), Map.class));
commonNoticeSendService.sendNotice(NoticeConstants.TaskType.API_DEFINITION_TASK, event, resources, user, projectId);
});
}
}
} }

View File

@ -5,6 +5,8 @@ import io.metersphere.api.controller.result.ApiResultCode;
import io.metersphere.api.domain.*; import io.metersphere.api.domain.*;
import io.metersphere.api.dto.debug.ApiFileResourceUpdateRequest; import io.metersphere.api.dto.debug.ApiFileResourceUpdateRequest;
import io.metersphere.api.dto.definition.ApiDefinitionMockDTO; import io.metersphere.api.dto.definition.ApiDefinitionMockDTO;
import io.metersphere.api.dto.definition.ApiMockBatchEditRequest;
import io.metersphere.api.dto.definition.ApiTestCaseBatchRequest;
import io.metersphere.api.dto.definition.request.ApiDefinitionMockAddRequest; import io.metersphere.api.dto.definition.request.ApiDefinitionMockAddRequest;
import io.metersphere.api.dto.definition.request.ApiDefinitionMockPageRequest; import io.metersphere.api.dto.definition.request.ApiDefinitionMockPageRequest;
import io.metersphere.api.dto.definition.request.ApiDefinitionMockRequest; import io.metersphere.api.dto.definition.request.ApiDefinitionMockRequest;
@ -17,25 +19,38 @@ import io.metersphere.api.mapper.ApiDefinitionMockMapper;
import io.metersphere.api.mapper.ExtApiDefinitionMockMapper; import io.metersphere.api.mapper.ExtApiDefinitionMockMapper;
import io.metersphere.api.service.ApiFileResourceService; import io.metersphere.api.service.ApiFileResourceService;
import io.metersphere.api.utils.ApiDataUtils; import io.metersphere.api.utils.ApiDataUtils;
import io.metersphere.project.dto.environment.EnvironmentInfoDTO;
import io.metersphere.project.service.EnvironmentService;
import io.metersphere.project.service.ProjectService; import io.metersphere.project.service.ProjectService;
import io.metersphere.sdk.constants.ApplicationNumScope; import io.metersphere.sdk.constants.ApplicationNumScope;
import io.metersphere.sdk.constants.DefaultRepositoryDir; import io.metersphere.sdk.constants.DefaultRepositoryDir;
import io.metersphere.sdk.domain.Environment;
import io.metersphere.sdk.domain.EnvironmentExample;
import io.metersphere.sdk.exception.MSException; import io.metersphere.sdk.exception.MSException;
import io.metersphere.sdk.util.BeanUtils; import io.metersphere.sdk.mapper.EnvironmentMapper;
import io.metersphere.sdk.util.FileAssociationSourceUtil; import io.metersphere.sdk.util.*;
import io.metersphere.sdk.util.JSON;
import io.metersphere.system.log.constants.OperationLogModule; import io.metersphere.system.log.constants.OperationLogModule;
import io.metersphere.system.notice.constants.NoticeConstants;
import io.metersphere.system.uid.IDGenerator; import io.metersphere.system.uid.IDGenerator;
import io.metersphere.system.uid.NumGenerator; import io.metersphere.system.uid.NumGenerator;
import io.metersphere.system.utils.ServiceUtils; 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.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionUtils;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;
/** /**
* @author: LAN * @author: LAN
@ -60,6 +75,20 @@ public class ApiDefinitionMockService {
@Resource @Resource
private ApiFileResourceService apiFileResourceService; private ApiFileResourceService apiFileResourceService;
@Resource
private EnvironmentService environmentService;
@Resource
private EnvironmentMapper environmentMapper;
@Resource
private ApiTestCaseService apiTestCaseService;
@Resource
private SqlSessionFactory sqlSessionFactory;
@Resource
private ApiDefinitionMockLogService apiDefinitionMockLogService;
@Resource
private ApiDefinitionMockNoticeService apiDefinitionMockNoticeService;
public static final String STATUS = "Status";
public static final String TAGS = "Tags";
public List<ApiDefinitionMockDTO> getPage(ApiDefinitionMockPageRequest request) { public List<ApiDefinitionMockDTO> getPage(ApiDefinitionMockPageRequest request) {
return extApiDefinitionMockMapper.list(request); return extApiDefinitionMockMapper.list(request);
@ -118,6 +147,7 @@ public class ApiDefinitionMockService {
apiDefinitionMock.setUpdateTime(System.currentTimeMillis()); apiDefinitionMock.setUpdateTime(System.currentTimeMillis());
apiDefinitionMock.setCreateUser(userId); apiDefinitionMock.setCreateUser(userId);
if (CollectionUtils.isNotEmpty(request.getTags())) { if (CollectionUtils.isNotEmpty(request.getTags())) {
apiTestCaseService.checkTagLength(request.getTags());
apiDefinitionMock.setTags(request.getTags()); apiDefinitionMock.setTags(request.getTags());
} }
apiDefinitionMock.setEnable(true); apiDefinitionMock.setEnable(true);
@ -177,6 +207,7 @@ public class ApiDefinitionMockService {
ApiDefinitionMock apiDefinitionMock = checkApiDefinitionMock(request.getId()); ApiDefinitionMock apiDefinitionMock = checkApiDefinitionMock(request.getId());
BeanUtils.copyBean(apiDefinitionMock, request); BeanUtils.copyBean(apiDefinitionMock, request);
checkUpdateExist(apiDefinitionMock); checkUpdateExist(apiDefinitionMock);
apiDefinitionMock.setUpdateUser(userId);
apiDefinitionMock.setUpdateTime(System.currentTimeMillis()); apiDefinitionMock.setUpdateTime(System.currentTimeMillis());
if (CollectionUtils.isNotEmpty(request.getTags())) { if (CollectionUtils.isNotEmpty(request.getTags())) {
apiDefinitionMock.setTags(request.getTags()); apiDefinitionMock.setTags(request.getTags());
@ -247,12 +278,13 @@ public class ApiDefinitionMockService {
return copyName; return copyName;
} }
public void updateEnable(String id) { public void updateEnable(String id, String userId) {
ApiDefinitionMock apiDefinitionMock = checkApiDefinitionMock(id); ApiDefinitionMock apiDefinitionMock = checkApiDefinitionMock(id);
ApiDefinitionMock update = new ApiDefinitionMock(); ApiDefinitionMock update = new ApiDefinitionMock();
update.setId(id); update.setId(id);
update.setEnable(!apiDefinitionMock.getEnable()); update.setEnable(!apiDefinitionMock.getEnable());
update.setUpdateTime(System.currentTimeMillis()); update.setUpdateTime(System.currentTimeMillis());
update.setUpdateUser(userId);
apiDefinitionMockMapper.updateByPrimaryKeySelective(update); apiDefinitionMockMapper.updateByPrimaryKeySelective(update);
} }
@ -275,4 +307,123 @@ public class ApiDefinitionMockService {
} }
} }
public String getMockUrl(String id) {
ApiDefinitionMock apiDefinitionMock = checkApiDefinitionMock(id);
//检查接口是否存在
ApiDefinition apiDefinition = apiDefinitionMapper.selectByPrimaryKey(apiDefinitionMock.getApiDefinitionId());
if (apiDefinition == null) {
throw new MSException(ApiResultCode.API_DEFINITION_NOT_EXIST);
}
// 获取mock环境
EnvironmentExample environmentExample = new EnvironmentExample();
environmentExample.createCriteria().andProjectIdEqualTo(apiDefinitionMock.getProjectId()).andMockEqualTo(true);
List<Environment> environments = environmentMapper.selectByExample(environmentExample);
if (CollectionUtils.isNotEmpty(environments)) {
EnvironmentInfoDTO environmentInfoDTO = environmentService.get(environments.getFirst().getId());
return StringUtils.join(environmentInfoDTO.getConfig().getHttpConfig().getFirst().getUrl(), "/", apiDefinition.getNum(), apiDefinition.getPath());
}
return null;
}
public void batchDelete(ApiTestCaseBatchRequest request, String userId) {
List<String> ids = doSelectIds(request);
if (CollectionUtils.isNotEmpty(ids)) {
SubListUtils.dealForSubList(ids, 500, subList -> deleteResourceByIds(subList, request.getProjectId(), userId));
}
}
public void deleteResourceByIds(List<String> ids, String projectId, String userId) {
List<ApiDefinitionMock> mockList = extApiDefinitionMockMapper.getMockInfoByIds(ids);
// 批量删除关联文件
String apiMockDir = DefaultRepositoryDir.getApiMockDir(projectId, StringUtils.EMPTY);
apiFileResourceService.deleteByResourceIds(apiMockDir, ids, projectId, userId, OperationLogModule.API_TEST_MANAGEMENT_MOCK);
ApiDefinitionMockExample example = new ApiDefinitionMockExample();
example.createCriteria().andIdIn(ids);
apiDefinitionMockMapper.deleteByExample(example);
ApiDefinitionMockConfigExample blobExample = new ApiDefinitionMockConfigExample();
blobExample.createCriteria().andIdIn(ids);
apiDefinitionMockConfigMapper.deleteByExample(blobExample);
//记录删除日志
apiDefinitionMockLogService.deleteBatchLog(mockList, userId, projectId);
apiDefinitionMockNoticeService.batchSendNotice(ids, userId, projectId, NoticeConstants.Event.MOCK_DELETE);
}
public void batchEdit(ApiMockBatchEditRequest request, String userId) {
List<String> ids = doSelectIds(request);
if (CollectionUtils.isNotEmpty(ids)) {
SubListUtils.dealForSubList(ids, 500, subList -> batchEditByType(request, subList, userId, request.getProjectId()));
}
}
private void batchEditByType(ApiMockBatchEditRequest request, List<String> ids, String userId, String projectId) {
ApiDefinitionMockExample example = new ApiDefinitionMockExample();
example.createCriteria().andIdIn(ids);
ApiDefinitionMock updateCase = new ApiDefinitionMock();
updateCase.setUpdateUser(userId);
updateCase.setUpdateTime(System.currentTimeMillis());
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
ApiDefinitionMockMapper mapper = sqlSession.getMapper(ApiDefinitionMockMapper.class);
switch (request.getType()) {
case STATUS -> batchUpdateStatus(example, updateCase, request.isEnable(), mapper);
case TAGS -> batchUpdateTags(example, updateCase, request, ids, mapper);
default -> throw new MSException(Translator.get("batch_edit_type_error"));
}
sqlSession.flushStatements();
SqlSessionUtils.closeSqlSession(sqlSession, sqlSessionFactory);
List<ApiDefinitionMock> mockInfoByIds = extApiDefinitionMockMapper.getMockInfoByIds(ids);
apiDefinitionMockLogService.batchEditLog(mockInfoByIds, userId, projectId);
apiDefinitionMockNoticeService.batchSendNotice(ids, userId, projectId, NoticeConstants.Event.MOCK_UPDATE);
}
private void batchUpdateTags(ApiDefinitionMockExample example, ApiDefinitionMock updateMock,
ApiMockBatchEditRequest request, List<String> ids,
ApiDefinitionMockMapper mapper) {
if (CollectionUtils.isEmpty(request.getTags())) {
throw new MSException(Translator.get("tags_is_null"));
}
apiTestCaseService.checkTagLength(request.getTags());
if (request.isAppend()) {
Map<String, ApiDefinitionMock> mockMap = extApiDefinitionMockMapper.getTagsByIds(ids)
.stream()
.collect(Collectors.toMap(ApiDefinitionMock::getId, Function.identity()));
if (MapUtils.isNotEmpty(mockMap)) {
mockMap.forEach((k, v) -> {
if (CollectionUtils.isNotEmpty(v.getTags())) {
List<String> orgTags = v.getTags();
orgTags.addAll(request.getTags());
apiTestCaseService.checkTagLength(orgTags.stream().distinct().toList());
v.setTags(orgTags.stream().distinct().toList());
} else {
v.setTags(request.getTags());
}
v.setUpdateTime(updateMock.getUpdateTime());
v.setUpdateUser(updateMock.getUpdateUser());
mapper.updateByPrimaryKeySelective(v);
});
}
} else {
updateMock.setTags(request.getTags());
mapper.updateByExampleSelective(updateMock, example);
}
}
private void batchUpdateStatus(ApiDefinitionMockExample example, ApiDefinitionMock updateMock, boolean enable, ApiDefinitionMockMapper mapper) {
updateMock.setEnable(enable);
mapper.updateByExampleSelective(updateMock, example);
}
public List<String> doSelectIds(ApiTestCaseBatchRequest request) {
if (request.isSelectAll()) {
List<String> ids = extApiDefinitionMockMapper.getIds(request);
if (CollectionUtils.isNotEmpty(request.getExcludeIds())) {
ids.removeAll(request.getExcludeIds());
}
return new ArrayList<>(ids.stream().distinct().toList());
} else {
request.getSelectIds().removeAll(request.getExcludeIds());
return request.getSelectIds();
}
}
} }

View File

@ -48,7 +48,6 @@ 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.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.*; import java.util.*;
@ -450,7 +449,7 @@ public class ApiTestCaseService extends MoveNodeService {
if (CollectionUtils.isEmpty(ids)) { if (CollectionUtils.isEmpty(ids)) {
return; return;
} }
SubListUtils.dealForSubList(ids, 2000, subList -> batchEditByType(request, subList, userId, request.getProjectId())); SubListUtils.dealForSubList(ids, 500, subList -> batchEditByType(request, subList, userId, request.getProjectId()));
} }
private void batchEditByType(ApiCaseBatchEditRequest request, List<String> ids, String userId, String projectId) { private void batchEditByType(ApiCaseBatchEditRequest request, List<String> ids, String userId, String projectId) {

View File

@ -82,6 +82,8 @@ public class ApiDefinitionMockControllerTests extends BaseTest {
private static final String ENABLE = BASE_PATH + "enable/"; private static final String ENABLE = BASE_PATH + "enable/";
private static final String UPLOAD_TEMP_FILE = BASE_PATH + "/upload/temp/file"; private static final String UPLOAD_TEMP_FILE = BASE_PATH + "/upload/temp/file";
private static final String BATCH_DELETE = BASE_PATH + "batch/delete";
private static final String BATCH_EDIT = BASE_PATH + "batch/edit";
private static final String DEFAULT_API_ID = "1001"; private static final String DEFAULT_API_ID = "1001";
private static Long NO_MOCK_NO_RESPONSE_API_NUM; private static Long NO_MOCK_NO_RESPONSE_API_NUM;
@ -110,6 +112,8 @@ public class ApiDefinitionMockControllerTests extends BaseTest {
private FileMetadataService fileMetadataService; private FileMetadataService fileMetadataService;
@Resource @Resource
private ApiFileResourceMapper apiFileResourceMapper; private ApiFileResourceMapper apiFileResourceMapper;
@Resource
private ExtApiDefinitionMockMapper extApiDefinitionMockMapper;
//文件管理中已存在的ID //文件管理中已存在的ID
private static String fileMetadataId; private static String fileMetadataId;
@ -460,6 +464,27 @@ public class ApiDefinitionMockControllerTests extends BaseTest {
requestPostPermissionTest(PermissionConstants.PROJECT_API_DEFINITION_MOCK_UPDATE, COPY, request); requestPostPermissionTest(PermissionConstants.PROJECT_API_DEFINITION_MOCK_UPDATE, COPY, request);
} }
@Test
@Order(6)
public void getMockUrl() throws Exception {
this.requestGetWithOk(BASE_PATH + "get-url/" + apiDefinitionMock.getId());
//mock不存在
this.requestGet(BASE_PATH + "get-url/111").andExpect(status().is4xxClientError());
// 接口不存在
ApiDefinitionMock apiDefinitionMock1 = apiDefinitionMockMapper.selectByPrimaryKey(apiDefinitionMock.getId());
apiDefinitionMock1.setApiDefinitionId("111");
apiDefinitionMockMapper.updateByPrimaryKey(apiDefinitionMock1);
this.requestGet(BASE_PATH + "get-url/" + apiDefinitionMock.getId()).andExpect(status().is5xxServerError());
// 项目不存在导致的mock查不到
apiDefinitionMock1.setApiDefinitionId(DEFAULT_API_ID);
apiDefinitionMock1.setProjectId("111");
apiDefinitionMockMapper.updateByPrimaryKey(apiDefinitionMock1);
this.requestGet(BASE_PATH + "get-url/" + apiDefinitionMock.getId()).andExpect(status().isOk());
apiDefinitionMock1.setProjectId(DEFAULT_PROJECT_ID);
apiDefinitionMockMapper.updateByPrimaryKey(apiDefinitionMock1);
}
@Test @Test
@Order(9) @Order(9)
@ -539,6 +564,63 @@ public class ApiDefinitionMockControllerTests extends BaseTest {
requestPostPermissionTest(PermissionConstants.PROJECT_API_DEFINITION_MOCK_DELETE, DELETE, apiDefinitionMockRequest); requestPostPermissionTest(PermissionConstants.PROJECT_API_DEFINITION_MOCK_DELETE, DELETE, apiDefinitionMockRequest);
} }
@Test
@Order(13)
public void batchEdit() throws Exception {
// 追加标签
ApiMockBatchEditRequest request = new ApiMockBatchEditRequest();
request.setProjectId(DEFAULT_PROJECT_ID);
request.setType("Tags");
request.setAppend(true);
request.setSelectAll(true);
request.setTags(new LinkedHashSet<>(List.of("tag1", "tag3", "tag4")));
requestPostWithOkAndReturn(BATCH_EDIT, request);
ApiDefinitionMockExample example = new ApiDefinitionMockExample();
List<String> ids = extApiDefinitionMockMapper.getIds(request);
example.createCriteria().andProjectIdEqualTo(DEFAULT_PROJECT_ID).andIdIn(ids);
apiDefinitionMockMapper.selectByExample(example).forEach(apiTestCase -> {
Assertions.assertTrue(apiTestCase.getTags().contains("tag1"));
Assertions.assertTrue(apiTestCase.getTags().contains("tag3"));
Assertions.assertTrue(apiTestCase.getTags().contains("tag4"));
});
//覆盖标签
request.setTags(new LinkedHashSet<>(List.of("tag1")));
request.setAppend(false);
requestPostWithOkAndReturn(BATCH_EDIT, request);
apiDefinitionMockMapper.selectByExample(example).forEach(apiTestCase -> {
Assertions.assertEquals(apiTestCase.getTags(), List.of("tag1"));
});
//标签为空 报错
request.setTags(new LinkedHashSet<>());
this.requestPost(BATCH_EDIT, request, status().is4xxClientError());
//标签超出10个报错
request.setTags(new LinkedHashSet<>(List.of("tag1", "tag2", "tag3", "tag4", "tag5", "tag6", "tag7", "tag8", "tag9", "tag10", "tag11")));
this.requestPost(BATCH_EDIT, request, status().is5xxServerError());
//ids为空的时候
request.setTags(new LinkedHashSet<>(List.of("tag1")));
request.setSelectAll(true);
List<ApiDefinitionMock> caseList1 = apiDefinitionMockMapper.selectByExample(example);
//提取所有的id
List<String> apiIdList = caseList1.stream().map(ApiDefinitionMock::getId).toList();
request.setSelectIds(apiIdList);
request.setExcludeIds(apiIdList);
requestPostWithOkAndReturn(BATCH_EDIT, request);
//状态
request.setType("Status");
request.setEnable(true);
request.setSelectAll(true);
request.setExcludeIds(new ArrayList<>());
requestPostWithOkAndReturn(BATCH_EDIT, request);
//类型错误
request.setType("111");
this.requestPost(BATCH_EDIT, request, status().is5xxServerError());
//校验权限
requestPostPermissionTest(PermissionConstants.PROJECT_API_DEFINITION_MOCK_UPDATE, BATCH_EDIT, request);
}
@Test @Test
@Order(99) @Order(99)
public void mockServerTest() throws Exception { public void mockServerTest() throws Exception {
@ -1056,6 +1138,19 @@ public class ApiDefinitionMockControllerTests extends BaseTest {
} }
} }
@Test
@Order(100)
public void batchDelete() throws Exception {
// 追加标签
ApiTestCaseBatchRequest request = new ApiTestCaseBatchRequest();
request.setProjectId(DEFAULT_PROJECT_ID);
request.setSelectAll(true);
requestPostWithOkAndReturn(BATCH_DELETE, request);
//校验权限
requestPostPermissionTest(PermissionConstants.PROJECT_API_DEFINITION_MOCK_DELETE, BATCH_DELETE, request);
}
public static String getFileMD5(byte[] bytes) { public static String getFileMD5(byte[] bytes) {
try { try {
MessageDigest digest = MessageDigest.getInstance("MD5"); MessageDigest digest = MessageDigest.getInstance("MD5");

View File

@ -51,16 +51,16 @@ INSERT INTO template (id, name, remark, internal, update_time, create_time, crea
DELETE FROM `api_definition_mock` WHERE `id` in ('mock_1', 'mock_2', 'mock_3', 'mock_4','mock_5'); DELETE FROM `api_definition_mock` WHERE `id` in ('mock_1', 'mock_2', 'mock_3', 'mock_4','mock_5');
INSERT INTO `api_definition_mock` VALUES INSERT INTO `api_definition_mock` VALUES
('mock_1', 1641120000000, 1641120000000, 'user1', 'Mock 1', '[\"tag1\",\"tag2\"]', 1, 'EXPECT001', '100001100001', '1001', 200), ('mock_1', 1641120000000, 1641120000000, 'user1', 'Mock 1', '[\"tag1\",\"tag2\"]', 1, 'EXPECT001', '100001100001', '1001', 200, null),
('mock_2', 1641121000000, 1641121000000, 'user2', 'Mock 2', '[\"tag2\",\"tag3\"]', 1, 'EXPECT002', '100001100001', '1002', 200), ('mock_2', 1641121000000, 1641121000000, 'user2', 'Mock 2', '[\"tag2\",\"tag3\"]', 1, 'EXPECT002', '100001100001', '1002', 200, null),
('mock_3', 1641122000000, 1641122000000, 'user3', 'Mock 3', '[\"tag3\",\"tag4\"]', 1, 'EXPECT003', '100001100001', '1003', 200), ('mock_3', 1641122000000, 1641122000000, 'user3', 'Mock 3', '[\"tag3\",\"tag4\"]', 1, 'EXPECT003', '100001100001', '1003', 200, null),
('mock_4', 1641123000000, 1641123000000, 'user1', 'Mock 4', '[\"tag4\",\"tag5\"]', 1, 'EXPECT004', '100001100001', '1005', 400), ('mock_4', 1641123000000, 1641123000000, 'user1', 'Mock 4', '[\"tag4\",\"tag5\"]', 1, 'EXPECT004', '100001100001', '1005', 400, null),
('mock_5', 1641124000000, 1641124000000, 'user2', 'Mock 5', '[\"tag5\",\"tag1\"]', 1, 'EXPECT005', '100001100001', '1005', 400); ('mock_5', 1641124000000, 1641124000000, 'user2', 'Mock 5', '[\"tag5\",\"tag1\"]', 1, 'EXPECT005', '100001100001', '1005', 400, null);
DELETE FROM `api_definition_mock_config` WHERE `id` in ('mock_1', 'mock_2', 'mock_3', 'mock_4','mock_5'); DELETE FROM `api_definition_mock_config` WHERE `id` in ('mock_1', 'mock_2', 'mock_3', 'mock_4','mock_5');
INSERT INTO `api_definition_mock_config` VALUES INSERT INTO `api_definition_mock_config` VALUES
('mock_1', '{"type": "exact", "value": "request_value"}', '{"status": 200, "body": {"message": "Mock Response 1"}}'), ('mock_1', '{"type": "exact", "value": "request_value"}', '{"status": 200, "body": {"message": "Mock Response 1"}}'),
('mock_2', '{"type": "regex", "value": "\\d{3}"}', '{"status": 404, "body": {"error": "Not Found"}}'), ('mock_2', '{"type": "regex", "value": "\d{3}"}', '{"status": 404, "body": {"error": "Not Found"}}'),
('mock_3', '{"type": "contains", "value": "partial_value"}', '{"status": 500, "body": {"error": "Internal Server Error"}}'), ('mock_3', '{"type": "contains", "value": "partial_value"}', '{"status": 500, "body": {"error": "Internal Server Error"}}'),
('mock_4', '{"type": "exact", "value": "another_exact_value"}', '{"status": 200, "body": {"data": "Another Mock Response"}}'), ('mock_4', '{"type": "exact", "value": "another_exact_value"}', '{"status": 200, "body": {"data": "Another Mock Response"}}'),
('mock_5', '{"type": "jsonpath", "value": "$.items[0].name"}', '{"status": 200, "body": {"items": [{"name": "Item 1"}]}}'); ('mock_5', '{"type": "jsonpath", "value": "$.items[0].name"}', '{"status": 200, "body": {"items": [{"name": "Item 1"}]}}');