refactor(接口测试): 优化mock

This commit is contained in:
wxg0103 2024-04-28 18:47:54 +08:00 committed by wxg0103
parent 2ff9d70ab7
commit 4a7cef668a
26 changed files with 234 additions and 66 deletions

View File

@ -6,6 +6,7 @@ import jakarta.validation.constraints.*;
import java.io.Serializable; import java.io.Serializable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List;
import lombok.Data; import lombok.Data;
@Data @Data
@ -50,6 +51,9 @@ public class ApiDefinitionMock implements Serializable {
@Size(min = 1, max = 50, message = "{api_definition_mock.api_definition_id.length_range}", groups = {Created.class, Updated.class}) @Size(min = 1, max = 50, message = "{api_definition_mock.api_definition_id.length_range}", groups = {Created.class, Updated.class})
private String apiDefinitionId; private String apiDefinitionId;
@Schema(description = "")
private Integer statusCode;
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
public enum Column { public enum Column {
@ -62,7 +66,8 @@ public class ApiDefinitionMock implements Serializable {
enable("enable", "enable", "BIT", true), enable("enable", "enable", "BIT", true),
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);
private static final String BEGINNING_DELIMITER = "`"; private static final String BEGINNING_DELIMITER = "`";

View File

@ -807,6 +807,66 @@ public class ApiDefinitionMockExample {
addCriterion("api_definition_id not between", value1, value2, "apiDefinitionId"); addCriterion("api_definition_id not between", value1, value2, "apiDefinitionId");
return (Criteria) this; return (Criteria) this;
} }
public Criteria andStatusCodeIsNull() {
addCriterion("status_code is null");
return (Criteria) this;
}
public Criteria andStatusCodeIsNotNull() {
addCriterion("status_code is not null");
return (Criteria) this;
}
public Criteria andStatusCodeEqualTo(Integer value) {
addCriterion("status_code =", value, "statusCode");
return (Criteria) this;
}
public Criteria andStatusCodeNotEqualTo(Integer value) {
addCriterion("status_code <>", value, "statusCode");
return (Criteria) this;
}
public Criteria andStatusCodeGreaterThan(Integer value) {
addCriterion("status_code >", value, "statusCode");
return (Criteria) this;
}
public Criteria andStatusCodeGreaterThanOrEqualTo(Integer value) {
addCriterion("status_code >=", value, "statusCode");
return (Criteria) this;
}
public Criteria andStatusCodeLessThan(Integer value) {
addCriterion("status_code <", value, "statusCode");
return (Criteria) this;
}
public Criteria andStatusCodeLessThanOrEqualTo(Integer value) {
addCriterion("status_code <=", value, "statusCode");
return (Criteria) this;
}
public Criteria andStatusCodeIn(List<Integer> values) {
addCriterion("status_code in", values, "statusCode");
return (Criteria) this;
}
public Criteria andStatusCodeNotIn(List<Integer> values) {
addCriterion("status_code not in", values, "statusCode");
return (Criteria) this;
}
public Criteria andStatusCodeBetween(Integer value1, Integer value2) {
addCriterion("status_code between", value1, value2, "statusCode");
return (Criteria) this;
}
public Criteria andStatusCodeNotBetween(Integer value1, Integer value2) {
addCriterion("status_code not between", value1, value2, "statusCode");
return (Criteria) this;
}
} }
public static class Criteria extends GeneratedCriteria { public static class Criteria extends GeneratedCriteria {

View File

@ -12,6 +12,7 @@
<result column="expect_num" jdbcType="VARCHAR" property="expectNum" /> <result column="expect_num" jdbcType="VARCHAR" property="expectNum" />
<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" />
</resultMap> </resultMap>
<sql id="Example_Where_Clause"> <sql id="Example_Where_Clause">
<where> <where>
@ -111,7 +112,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 api_definition_id, status_code
</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
@ -147,11 +148,11 @@
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) api_definition_id, status_code)
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}) #{apiDefinitionId,jdbcType=VARCHAR}, #{statusCode,jdbcType=INTEGER})
</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
@ -186,6 +187,9 @@
<if test="apiDefinitionId != null"> <if test="apiDefinitionId != null">
api_definition_id, api_definition_id,
</if> </if>
<if test="statusCode != null">
status_code,
</if>
</trim> </trim>
<trim prefix="values (" suffix=")" suffixOverrides=","> <trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="id != null"> <if test="id != null">
@ -218,6 +222,9 @@
<if test="apiDefinitionId != null"> <if test="apiDefinitionId != null">
#{apiDefinitionId,jdbcType=VARCHAR}, #{apiDefinitionId,jdbcType=VARCHAR},
</if> </if>
<if test="statusCode != null">
#{statusCode,jdbcType=INTEGER},
</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">
@ -259,6 +266,9 @@
<if test="record.apiDefinitionId != null"> <if test="record.apiDefinitionId != null">
api_definition_id = #{record.apiDefinitionId,jdbcType=VARCHAR}, api_definition_id = #{record.apiDefinitionId,jdbcType=VARCHAR},
</if> </if>
<if test="record.statusCode != null">
status_code = #{record.statusCode,jdbcType=INTEGER},
</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" />
@ -275,7 +285,8 @@
`enable` = #{record.enable,jdbcType=BIT}, `enable` = #{record.enable,jdbcType=BIT},
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}
<if test="_parameter != null"> <if test="_parameter != null">
<include refid="Update_By_Example_Where_Clause" /> <include refid="Update_By_Example_Where_Clause" />
</if> </if>
@ -310,6 +321,9 @@
<if test="apiDefinitionId != null"> <if test="apiDefinitionId != null">
api_definition_id = #{apiDefinitionId,jdbcType=VARCHAR}, api_definition_id = #{apiDefinitionId,jdbcType=VARCHAR},
</if> </if>
<if test="statusCode != null">
status_code = #{statusCode,jdbcType=INTEGER},
</if>
</set> </set>
where id = #{id,jdbcType=VARCHAR} where id = #{id,jdbcType=VARCHAR}
</update> </update>
@ -323,19 +337,20 @@
`enable` = #{enable,jdbcType=BIT}, `enable` = #{enable,jdbcType=BIT},
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}
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) api_definition_id, status_code)
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.apiDefinitionId,jdbcType=VARCHAR}, #{item.statusCode,jdbcType=INTEGER})
</foreach> </foreach>
</insert> </insert>
<insert id="batchInsertSelective" parameterType="map"> <insert id="batchInsertSelective" parameterType="map">
@ -378,6 +393,9 @@
<if test="'api_definition_id'.toString() == column.value"> <if test="'api_definition_id'.toString() == column.value">
#{item.apiDefinitionId,jdbcType=VARCHAR} #{item.apiDefinitionId,jdbcType=VARCHAR}
</if> </if>
<if test="'status_code'.toString() == column.value">
#{item.statusCode,jdbcType=INTEGER}
</if>
</foreach> </foreach>
) )
</foreach> </foreach>

View File

@ -3,6 +3,8 @@ SET SESSION innodb_lock_wait_timeout = 7200;
ALTER TABLE user_key MODIFY COLUMN description VARCHAR(1000); ALTER TABLE user_key MODIFY COLUMN description VARCHAR(1000);
ALTER TABLE api_definition_mock ADD COLUMN status_code INT(50) ;
-- 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

@ -15,7 +15,7 @@ public class TempFileUtils {
} }
public static boolean isImage(String type) { public static boolean isImage(String type) {
return StringUtils.equalsAnyIgnoreCase(type, "jpg", "jpeg", "png", "gif", "bmp", "svg", "ico"); return StringUtils.equalsAnyIgnoreCase(type, "jpg", "jpeg", "png", "gif", "bmp", "svg", "ico", "webp", "apng", "avif");
} }
public static String getFileNameByPath(String filePath) { public static String getFileNameByPath(String filePath) {

View File

@ -6,6 +6,8 @@ import io.metersphere.api.service.scenario.ApiScenarioReportService;
import io.metersphere.sdk.constants.ApiReportStatus; import io.metersphere.sdk.constants.ApiReportStatus;
import io.metersphere.sdk.file.FileRequest; import io.metersphere.sdk.file.FileRequest;
import io.metersphere.sdk.util.LogUtils; import io.metersphere.sdk.util.LogUtils;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
@ -20,6 +22,7 @@ import java.util.Optional;
*/ */
@RestController @RestController
@RequestMapping("/api/execute/resource") @RequestMapping("/api/execute/resource")
@Tag(name = "接口测试-执行-资源")
public class ApiExecuteResourceController { public class ApiExecuteResourceController {
@Resource @Resource
@ -39,6 +42,7 @@ public class ApiExecuteResourceController {
* @return * @return
*/ */
@GetMapping("script") @GetMapping("script")
@Operation(summary = "获取执行脚本")
public String getScript(@RequestParam("reportId") String reportId, @RequestParam("testId") String testId) { public String getScript(@RequestParam("reportId") String reportId, @RequestParam("testId") String testId) {
String key = apiExecuteService.getScriptRedisKey(reportId, testId); String key = apiExecuteService.getScriptRedisKey(reportId, testId);
LogUtils.info("获取执行脚本: {}", key); LogUtils.info("获取执行脚本: {}", key);
@ -55,6 +59,7 @@ public class ApiExecuteResourceController {
* @return * @return
*/ */
@PostMapping("/file") @PostMapping("/file")
@Operation(summary = "下载执行所需的文件")
public void downloadFile(@RequestParam("reportId") String reportId, public void downloadFile(@RequestParam("reportId") String reportId,
@RequestParam("testId") String testId, @RequestParam("testId") String testId,
@RequestBody FileRequest fileRequest, @RequestBody FileRequest fileRequest,

View File

@ -2,39 +2,84 @@ package io.metersphere.api.controller.mockserver;
import io.metersphere.api.service.mockserver.MockServerService; import io.metersphere.api.service.mockserver.MockServerService;
import io.metersphere.api.utils.MockServerUtils; import io.metersphere.api.utils.MockServerUtils;
import io.metersphere.sdk.constants.HttpMethodConstants;
import io.metersphere.system.controller.handler.annotation.NoResultHolder; import io.metersphere.system.controller.handler.annotation.NoResultHolder;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import org.springframework.web.bind.annotation.PathVariable; import org.springframework.http.HttpMethod;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import java.util.Map;
@RestController @RestController
@RequestMapping(value = "/mock-server") @RequestMapping(value = "/mock-server/{projectNum}/{apiNum}/**")
@Tag(name = "接口测试-接口管理-接口定义-Mock") @Tag(name = "接口测试-接口管理-接口定义-Mock")
public class MockServerController { public class MockServerController {
@Resource @Resource
private MockServerService mockServerService; private MockServerService mockServerService;
@RequestMapping(value = "/{projectNum}/{apiInfo}/**", method = RequestMethod.OPTIONS) @RequestMapping(method = RequestMethod.OPTIONS)
@NoResultHolder @NoResultHolder
public Object optionsRequest(@PathVariable String projectNum, @PathVariable String apiInfo, HttpServletRequest request, HttpServletResponse response) { public Object optionsRequest(@PathVariable String projectNum, @PathVariable String apiNum,
Map<String, String> requestHeaderMap = MockServerUtils.getHttpRequestHeader(request); HttpServletRequest request, HttpServletResponse response) {
return mockServerService.execute(HttpMethodConstants.OPTIONS.name(), requestHeaderMap, projectNum, apiInfo, request, response); var requestHeaderMap = MockServerUtils.getHttpRequestHeader(request);
return mockServerService.execute(HttpMethod.OPTIONS.name(), requestHeaderMap, projectNum, apiNum, request, response);
} }
@RequestMapping(value = "/{projectNum}/{apiInfo}/**") @RequestMapping(method = RequestMethod.HEAD)
@NoResultHolder @NoResultHolder
public Object mockRequest(@PathVariable String projectNum, @PathVariable String apiInfo, HttpServletRequest request, HttpServletResponse response) { public Object headerRequest(@PathVariable String projectNum, @PathVariable String apiNum,
Map<String, String> requestHeaderMap = MockServerUtils.getHttpRequestHeader(request); HttpServletRequest request, HttpServletResponse response) {
String method = request.getMethod(); var requestHeaderMap = MockServerUtils.getHttpRequestHeader(request);
return mockServerService.execute(method, requestHeaderMap, projectNum, apiInfo, request, response); return mockServerService.execute(HttpMethod.HEAD.name(), requestHeaderMap, projectNum, apiNum, request, response);
} }
@RequestMapping(method = RequestMethod.TRACE)
@NoResultHolder
public Object traceRequest(@PathVariable String projectNum, @PathVariable String apiNum,
HttpServletRequest request, HttpServletResponse response) {
var requestHeaderMap = MockServerUtils.getHttpRequestHeader(request);
return mockServerService.execute(HttpMethod.TRACE.name(), requestHeaderMap, projectNum, apiNum, request, response);
}
@GetMapping
@NoResultHolder
public Object getMockRequest(@PathVariable String projectNum, @PathVariable String apiNum,
HttpServletRequest request, HttpServletResponse response) {
return handleMockRequest(HttpMethod.GET.name(), projectNum, apiNum, request, response);
}
@PostMapping
@NoResultHolder
public Object postMockRequest(@PathVariable String projectNum, @PathVariable String apiNum,
HttpServletRequest request, HttpServletResponse response) {
return handleMockRequest(HttpMethod.POST.name(), projectNum, apiNum, request, response);
}
@PutMapping
@NoResultHolder
public Object putMockRequest(@PathVariable String projectNum, @PathVariable String apiNum,
HttpServletRequest request, HttpServletResponse response) {
return handleMockRequest(HttpMethod.PUT.name(), projectNum, apiNum, request, response);
}
@DeleteMapping
@NoResultHolder
public Object deleteMockRequest(@PathVariable String projectNum, @PathVariable String apiNum,
HttpServletRequest request, HttpServletResponse response) {
return handleMockRequest(HttpMethod.DELETE.name(), projectNum, apiNum, request, response);
}
@PatchMapping
@NoResultHolder
public Object patchMockRequest(@PathVariable String projectNum, @PathVariable String apiNum,
HttpServletRequest request, HttpServletResponse response) {
return handleMockRequest(HttpMethod.PATCH.name(), projectNum, apiNum, request, response);
}
private Object handleMockRequest(String method, String projectNum, String apiNum,
HttpServletRequest request, HttpServletResponse response) {
var requestHeaderMap = MockServerUtils.getHttpRequestHeader(request);
return mockServerService.execute(method, requestHeaderMap, projectNum, apiNum, request, response);
}
} }

View File

@ -25,4 +25,5 @@ public class ApiDefinitionExecuteInfo {
* 资源id接口定义接口用例等 * 资源id接口定义接口用例等
*/ */
private String resourceId; private String resourceId;
private Long num;
} }

View File

@ -21,4 +21,7 @@ public class ApiDefinitionRunRequest extends ApiDebugRunRequest {
@Schema(description = "模块fk") @Schema(description = "模块fk")
private String moduleId; private String moduleId;
@Schema(description = "接口编号 mock执行需要")
private Long num;
} }

View File

@ -32,6 +32,9 @@ public class ApiDefinitionMockAddRequest implements Serializable {
@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;
@Schema(description = "响应码")
private int statusCode;
@Schema(description = "标签") @Schema(description = "标签")
private LinkedHashSet< private LinkedHashSet<
@Size(min = 1, max = 64, message = "{api_test_case.tag.length_range}") @Size(min = 1, max = 64, message = "{api_test_case.tag.length_range}")

View File

@ -11,11 +11,11 @@ import java.util.List;
import java.util.Map; import java.util.Map;
@Data @Data
public class BodyParamMatchRole { public class BodyParamMatchRule {
@Schema(description = "参数类型(kv/json/xml/raw 默认为raw)") @Schema(description = "参数类型(kv/json/xml/raw 默认为raw)")
private String paramType; private String paramType;
@Schema(description = "formData的匹配规则") @Schema(description = "formData的匹配规则")
private keyValueMatchRole formDataMatch; private keyValueMatchRule formDataMatch;
@Schema(description = "文本匹配规则") @Schema(description = "文本匹配规则")
private String raw; private String raw;

View File

@ -15,16 +15,16 @@ public class MockMatchRule implements Serializable {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@Schema(description = "请求头匹配规则") @Schema(description = "请求头匹配规则")
private keyValueMatchRole header = new keyValueMatchRole(); private keyValueMatchRule header = new keyValueMatchRule();
@Schema(description = "query参数匹配规则") @Schema(description = "query参数匹配规则")
private keyValueMatchRole query = new keyValueMatchRole(); private keyValueMatchRule query = new keyValueMatchRule();
@Schema(description = "REST参数匹配规则") @Schema(description = "REST参数匹配规则")
private keyValueMatchRole rest = new keyValueMatchRole(); private keyValueMatchRule rest = new keyValueMatchRule();
@Schema(description = "body参数匹配规则") @Schema(description = "body参数匹配规则")
private BodyParamMatchRole body = new BodyParamMatchRole(); private BodyParamMatchRule body = new BodyParamMatchRule();
public boolean keyValueMatch(String matchType, Map<String, String> matchParam) { public boolean keyValueMatch(String matchType, Map<String, String> matchParam) {
keyValueMatchRole matchRole = null; keyValueMatchRule matchRole = null;
switch (matchType) { switch (matchType) {
case "header": case "header":
matchRole = header; matchRole = header;

View File

@ -8,7 +8,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
@Data @Data
public class keyValueMatchRole { public class keyValueMatchRule {
@Schema(description = "是否是全部匹配 false为任意匹配") @Schema(description = "是否是全部匹配 false为任意匹配")
private boolean isMatchAll; private boolean isMatchAll;
@Schema(description = "匹配规则") @Schema(description = "匹配规则")

View File

@ -68,4 +68,8 @@ public class MsHTTPElement extends AbstractMsProtocolTestElement {
* 运行时参数接口无需设置 * 运行时参数接口无需设置
*/ */
private String moduleId; private String moduleId;
/**
* mock执行需要的接口编号
*/
private Long num;
} }

View File

@ -8,7 +8,7 @@
<select id="list" resultMap="ApiDefinitionMockDTO"> <select id="list" resultMap="ApiDefinitionMockDTO">
select select
m.id, m.create_time, m.update_time, m.create_user, m.`name`, m.tags, m.`enable`, m.expect_num, m.project_id, m.id, m.create_time, m.update_time, m.create_user, m.`name`, m.tags, m.`enable`, m.expect_num, m.project_id, m.status_code,
m.api_definition_id, u.name as create_user_name, d.path as api_path m.api_definition_id, u.name as create_user_name, d.path as api_path
from api_definition_mock m from api_definition_mock m
left join `api_definition` d on d.id = m.api_definition_id left join `api_definition` d on d.id = m.api_definition_id

View File

@ -377,6 +377,10 @@ public class MsHTTPElementConverter extends AbstractJmeterElementConverter<MsHTT
match = true; match = true;
} }
if (match) { if (match) {
// 如果是mock 返回的url格式是 /mock-server/projectNum/apiNum
if (BooleanUtils.isTrue(envConfig.getMock())) {
httpConfig.setHostname(StringUtils.join(httpConfig.getHostname(),"/", msHTTPElement.getNum()));
}
return httpConfig; return httpConfig;
} }
} }

View File

@ -381,6 +381,7 @@ public class ApiCommonService {
httpElement.setModuleId(definitionExecuteInfo.getModuleId()); httpElement.setModuleId(definitionExecuteInfo.getModuleId());
httpElement.setMethod(definitionExecuteInfo.getMethod()); httpElement.setMethod(definitionExecuteInfo.getMethod());
httpElement.setPath(definitionExecuteInfo.getPath()); httpElement.setPath(definitionExecuteInfo.getPath());
httpElement.setNum(definitionExecuteInfo.getNum());
} }
} }

View File

@ -59,10 +59,10 @@ public class MockServerService {
} }
public Object execute(String method, Map<String, String> requestHeaderMap, String projectNum, String apiNumInfo, HttpServletRequest request, HttpServletResponse response) { public Object execute(String method, Map<String, String> requestHeaderMap, String projectNum, String apiNum, HttpServletRequest request, HttpServletResponse response) {
ApiDefinition apiDefinition = extApiDefinitionMapper.selectByProjectNumAndApiNum(projectNum, apiNumInfo); ApiDefinition apiDefinition = extApiDefinitionMapper.selectByProjectNumAndApiNum(projectNum, apiNum);
String url = request.getRequestURL().toString(); String url = request.getRequestURL().toString();
String requestUrlSuffix = MockServerUtils.getUrlSuffix(StringUtils.joinWith("/", "/mock-server", projectNum, apiNumInfo), request); String requestUrlSuffix = MockServerUtils.getUrlSuffix(StringUtils.joinWith("/", "/mock-server", projectNum, apiNum), request);
if (apiDefinition == null) { if (apiDefinition == null) {
requestUrlSuffix = MockServerUtils.getUrlSuffix(StringUtils.joinWith("/", "/mock-server", projectNum), request); requestUrlSuffix = MockServerUtils.getUrlSuffix(StringUtils.joinWith("/", "/mock-server", projectNum), request);
apiDefinition = this.selectByProjectNumAndUrl(projectNum, method, requestUrlSuffix); apiDefinition = this.selectByProjectNumAndUrl(projectNum, method, requestUrlSuffix);
@ -201,12 +201,13 @@ public class MockServerService {
return StringUtils.EMPTY; return StringUtils.EMPTY;
} }
} else { } else {
ApiFileResource apiFileResource = apiFileResourceMapper.selectByPrimaryKey(compareMockConfig.getId(), fileId); String resourceId = compareMockConfig != null ? compareMockConfig.getId() : apiId;
ApiFileResource apiFileResource = apiFileResourceMapper.selectByPrimaryKey(resourceId, fileId);
if (apiFileResource != null) { if (apiFileResource != null) {
FileRepository defaultRepository = FileCenter.getDefaultRepository(); FileRepository defaultRepository = FileCenter.getDefaultRepository();
FileRequest fileRequest = new FileRequest(); FileRequest fileRequest = new FileRequest();
fileRequest.setFileName(apiFileResource.getFileName()); fileRequest.setFileName(apiFileResource.getFileName());
fileRequest.setFolder(DefaultRepositoryDir.getApiDefinitionDir(projectId, compareMockConfig.getId()) + "/" + fileId); fileRequest.setFolder(DefaultRepositoryDir.getApiDefinitionDir(projectId, resourceId) + "/" + fileId);
try { try {
bytes = defaultRepository.getFile(fileRequest); bytes = defaultRepository.getFile(fileRequest);
} catch (Exception ignore) { } catch (Exception ignore) {

View File

@ -7,6 +7,7 @@ import io.metersphere.sdk.util.JSON;
import io.metersphere.sdk.util.LogUtils; import io.metersphere.sdk.util.LogUtils;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.http.MediaType;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.IOException; import java.io.IOException;
@ -52,13 +53,13 @@ public class MockServerUtils {
requestParam.setRaw(requestPostString); requestParam.setRaw(requestPostString);
//解析paramType //解析paramType
if (StringUtils.startsWithIgnoreCase(request.getContentType(), "application/json")) { if (StringUtils.startsWithIgnoreCase(request.getContentType(), MediaType.APPLICATION_JSON_VALUE)) {
requestParam.setJsonParam(requestPostString); requestParam.setJsonParam(requestPostString);
} else if (StringUtils.endsWith(request.getContentType(), "/xml")) { } else if (StringUtils.endsWith(request.getContentType(), "/xml")) {
requestParam.setXmlParam(requestPostString); requestParam.setXmlParam(requestPostString);
} else if (StringUtils.startsWithIgnoreCase(request.getContentType(), "application/x-www-form-urlencoded")) { } else if (StringUtils.startsWithIgnoreCase(request.getContentType(), MediaType.APPLICATION_FORM_URLENCODED_VALUE)) {
requestParam.setParamType(Body.BodyType.FORM_DATA.name()); requestParam.setParamType(Body.BodyType.FORM_DATA.name());
} else if (StringUtils.startsWithIgnoreCase(request.getContentType(), "text/plain")) { } else if (StringUtils.startsWithIgnoreCase(request.getContentType(), MediaType.TEXT_PLAIN_VALUE)) {
requestParam.setParamType(Body.BodyType.RAW.name()); requestParam.setParamType(Body.BodyType.RAW.name());
} else if (isPost) { } else if (isPost) {
requestParam.setParamType(Body.BodyType.RAW.name()); requestParam.setParamType(Body.BodyType.RAW.name());

View File

@ -92,11 +92,7 @@
"id": "PROJECT_API_DEFINITION_MOCK:READ+UPDATE" "id": "PROJECT_API_DEFINITION_MOCK:READ+UPDATE"
}, },
{ {
"id": "PROJECT_API_DEFINITION_MOCK:READ+DELETE", "id": "PROJECT_API_DEFINITION_MOCK:READ+DELETE"
"name": "permission.api_definition.delete_and_recover"
},
{
"id": "PROJECT_API_DEFINITION_MOCK:READ+EXECUTE"
} }
] ]
}, },

View File

@ -147,10 +147,6 @@ public class ApiDefinitionControllerTests extends BaseTest {
@Resource @Resource
private OperationHistoryMapper operationHistoryMapper; private OperationHistoryMapper operationHistoryMapper;
@Resource @Resource
private ApiTestCaseMapper apiTestCaseMapper;
@Resource
private ApiTestCaseBlobMapper apiTestCaseBlobMapper;
@Resource
private BaseFileManagementTestService baseFileManagementTestService; private BaseFileManagementTestService baseFileManagementTestService;
@Resource @Resource
private ApiCommonService apiCommonService; private ApiCommonService apiCommonService;
@ -422,6 +418,7 @@ public class ApiDefinitionControllerTests extends BaseTest {
msHTTPElement.setMethod(apiDefinition.getMethod()); msHTTPElement.setMethod(apiDefinition.getMethod());
msHTTPElement.setPath(apiDefinition.getPath()); msHTTPElement.setPath(apiDefinition.getPath());
msHTTPElement.setModuleId(apiDefinition.getModuleId()); msHTTPElement.setModuleId(apiDefinition.getModuleId());
msHTTPElement.setNum(apiDefinition.getNum());
copyApiDefinitionDTO.setRequest(msTestElement); copyApiDefinitionDTO.setRequest(msTestElement);
List<HttpResponse> httpResponses = ApiDataUtils.parseArray(new String(apiDefinitionBlob.getResponse()), HttpResponse.class); List<HttpResponse> httpResponses = ApiDataUtils.parseArray(new String(apiDefinitionBlob.getResponse()), HttpResponse.class);
for (HttpResponse httpResponse : httpResponses) { for (HttpResponse httpResponse : httpResponses) {
@ -434,6 +431,8 @@ public class ApiDefinitionControllerTests extends BaseTest {
Assertions.assertEquals(msHTTPElement.getMethod(), apiDefinition.getMethod()); Assertions.assertEquals(msHTTPElement.getMethod(), apiDefinition.getMethod());
Assertions.assertEquals(msHTTPElement.getPath(), apiDefinition.getPath()); Assertions.assertEquals(msHTTPElement.getPath(), apiDefinition.getPath());
Assertions.assertEquals(msHTTPElement.getModuleId(), apiDefinition.getModuleId()); Assertions.assertEquals(msHTTPElement.getModuleId(), apiDefinition.getModuleId());
Assertions.assertEquals(msHTTPElement.getNum(), apiDefinition.getNum());
Assertions.assertEquals(apiDefinitionDTO, copyApiDefinitionDTO); Assertions.assertEquals(apiDefinitionDTO, copyApiDefinitionDTO);
assertErrorCode(this.requestGet(GET + "111"), ApiResultCode.API_DEFINITION_NOT_EXIST); assertErrorCode(this.requestGet(GET + "111"), ApiResultCode.API_DEFINITION_NOT_EXIST);

View File

@ -554,6 +554,7 @@ public class ApiTestCaseControllerTests extends BaseTest {
msHTTPElement.setMethod(apiDefinition.getMethod()); msHTTPElement.setMethod(apiDefinition.getMethod());
msHTTPElement.setPath(apiDefinition.getPath()); msHTTPElement.setPath(apiDefinition.getPath());
msHTTPElement.setModuleId(apiDefinition.getModuleId()); msHTTPElement.setModuleId(apiDefinition.getModuleId());
msHTTPElement.setNum(apiDefinition.getNum());
copyApiDebugDTO.setRequest(msTestElement); copyApiDebugDTO.setRequest(msTestElement);
msHTTPElement = (MsHTTPElement) apiDebugDTO.getRequest(); msHTTPElement = (MsHTTPElement) apiDebugDTO.getRequest();

View File

@ -136,7 +136,7 @@ public class MockServerTestService {
public MockMatchRule genMockMatchRule(String valuePrefix, boolean hasQuery, boolean hasHeader, String bodyParamType, boolean matchAll) { public MockMatchRule genMockMatchRule(String valuePrefix, boolean hasQuery, boolean hasHeader, String bodyParamType, boolean matchAll) {
MockMatchRule mockMatchRule = new MockMatchRule(); MockMatchRule mockMatchRule = new MockMatchRule();
keyValueMatchRole restMatchRule = new keyValueMatchRole(); keyValueMatchRule restMatchRule = new keyValueMatchRule();
restMatchRule.setMatchAll(matchAll); restMatchRule.setMatchAll(matchAll);
restMatchRule.setMatchRules(new ArrayList<>() {{ restMatchRule.setMatchRules(new ArrayList<>() {{
this.add(new KeyValueInfo() {{ this.add(new KeyValueInfo() {{
@ -151,7 +151,7 @@ public class MockServerTestService {
mockMatchRule.setRest(restMatchRule); mockMatchRule.setRest(restMatchRule);
if (hasQuery) { if (hasQuery) {
keyValueMatchRole queryMatchRule = new keyValueMatchRole(); keyValueMatchRule queryMatchRule = new keyValueMatchRule();
queryMatchRule.setMatchAll(matchAll); queryMatchRule.setMatchAll(matchAll);
queryMatchRule.setMatchRules(new ArrayList<>() {{ queryMatchRule.setMatchRules(new ArrayList<>() {{
this.add(new KeyValueInfo() {{ this.add(new KeyValueInfo() {{
@ -171,7 +171,7 @@ public class MockServerTestService {
} }
if (hasHeader) { if (hasHeader) {
keyValueMatchRole headerMatchRule = new keyValueMatchRole(); keyValueMatchRule headerMatchRule = new keyValueMatchRule();
headerMatchRule.setMatchAll(matchAll); headerMatchRule.setMatchAll(matchAll);
headerMatchRule.setMatchRules(new ArrayList<>() {{ headerMatchRule.setMatchRules(new ArrayList<>() {{
this.add(new KeyValueInfo() {{ this.add(new KeyValueInfo() {{
@ -191,9 +191,9 @@ public class MockServerTestService {
} }
if (StringUtils.equalsIgnoreCase(bodyParamType, "kv")) { if (StringUtils.equalsIgnoreCase(bodyParamType, "kv")) {
mockMatchRule.setBody(new BodyParamMatchRole() {{ mockMatchRule.setBody(new BodyParamMatchRule() {{
this.setParamType(Body.BodyType.FORM_DATA.name()); this.setParamType(Body.BodyType.FORM_DATA.name());
this.setFormDataMatch(new keyValueMatchRole() {{ this.setFormDataMatch(new keyValueMatchRule() {{
this.setMatchAll(matchAll); this.setMatchAll(matchAll);
this.setMatchRules(new ArrayList<>() {{ this.setMatchRules(new ArrayList<>() {{
this.add(new KeyValueInfo() {{ this.add(new KeyValueInfo() {{
@ -212,17 +212,17 @@ public class MockServerTestService {
}}); }});
}}); }});
} else if (StringUtils.equalsIgnoreCase(bodyParamType, "raw")) { } else if (StringUtils.equalsIgnoreCase(bodyParamType, "raw")) {
mockMatchRule.setBody(new BodyParamMatchRole() {{ mockMatchRule.setBody(new BodyParamMatchRule() {{
this.setParamType(Body.BodyType.RAW.name()); this.setParamType(Body.BodyType.RAW.name());
this.setRaw(valuePrefix + "_inputRawBody"); this.setRaw(valuePrefix + "_inputRawBody");
}}); }});
} else if (StringUtils.equalsIgnoreCase(bodyParamType, "json")) { } else if (StringUtils.equalsIgnoreCase(bodyParamType, "json")) {
mockMatchRule.setBody(new BodyParamMatchRole() {{ mockMatchRule.setBody(new BodyParamMatchRule() {{
this.setParamType(Body.BodyType.JSON.name()); this.setParamType(Body.BodyType.JSON.name());
this.setRaw("{\"inputAge\":123}"); this.setRaw("{\"inputAge\":123}");
}}); }});
} else if (StringUtils.equalsIgnoreCase(bodyParamType, "xml")) { } else if (StringUtils.equalsIgnoreCase(bodyParamType, "xml")) {
mockMatchRule.setBody(new BodyParamMatchRole() {{ mockMatchRule.setBody(new BodyParamMatchRule() {{
this.setParamType(Body.BodyType.XML.name()); this.setParamType(Body.BodyType.XML.name());
this.setRaw("<xml>input123</xml>"); this.setRaw("<xml>input123</xml>");
}}); }});

View File

@ -51,11 +51,11 @@ 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'), ('mock_1', 1641120000000, 1641120000000, 'user1', 'Mock 1', '[\"tag1\",\"tag2\"]', 1, 'EXPECT001', '100001100001', '1001', 200),
('mock_2', 1641121000000, 1641121000000, 'user2', 'Mock 2', '[\"tag2\",\"tag3\"]', 1, 'EXPECT002', '100001100001', '1002'), ('mock_2', 1641121000000, 1641121000000, 'user2', 'Mock 2', '[\"tag2\",\"tag3\"]', 1, 'EXPECT002', '100001100001', '1002', 200),
('mock_3', 1641122000000, 1641122000000, 'user3', 'Mock 3', '[\"tag3\",\"tag4\"]', 1, 'EXPECT003', '100001100001', '1003'), ('mock_3', 1641122000000, 1641122000000, 'user3', 'Mock 3', '[\"tag3\",\"tag4\"]', 1, 'EXPECT003', '100001100001', '1003', 200),
('mock_4', 1641123000000, 1641123000000, 'user1', 'Mock 4', '[\"tag4\",\"tag5\"]', 1, 'EXPECT004', '100001100001', '1005'), ('mock_4', 1641123000000, 1641123000000, 'user1', 'Mock 4', '[\"tag4\",\"tag5\"]', 1, 'EXPECT004', '100001100001', '1005', 400),
('mock_5', 1641124000000, 1641124000000, 'user2', 'Mock 5', '[\"tag5\",\"tag1\"]', 1, 'EXPECT005', '100001100001', '1005'); ('mock_5', 1641124000000, 1641124000000, 'user2', 'Mock 5', '[\"tag5\",\"tag1\"]', 1, 'EXPECT005', '100001100001', '1005', 400);
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

View File

@ -1141,13 +1141,13 @@
// //
const { assertionConfig } = requestVModel.value.children[0]; const { assertionConfig } = requestVModel.value.children[0];
return { return {
id: requestVModel.value.id.toString(), id: requestVModel.value.id.toString(),
reportId: reportId.value, reportId: reportId.value,
environmentId: appStore.currentEnvConfig?.id || '', environmentId: appStore.currentEnvConfig?.id || '',
name: requestName, name: requestName,
moduleId: requestModuleId, moduleId: requestModuleId,
num: requestVModel.value.num,
...apiDefinitionParams, ...apiDefinitionParams,
protocol: requestVModel.value.protocol, protocol: requestVModel.value.protocol,
method: isHttpProtocol.value ? requestVModel.value.method : requestVModel.value.protocol, method: isHttpProtocol.value ? requestVModel.value.method : requestVModel.value.protocol,

View File

@ -156,6 +156,25 @@
@change="handleFileChange" @change="handleFileChange"
/> />
</div> </div>
<div class="flex items-center">
<a-switch
v-model:model-value="activeResponse.body.binaryBody.sendAsBody"
class="mr-[8px]"
size="small"
type="line"
></a-switch>
<span>{{ t('apiTestDebug.sendAsMainText') }}</span>
<a-tooltip position="right">
<template #content>
<div>{{ t('apiTestDebug.sendAsMainTextTip1') }}</div>
<div>{{ t('apiTestDebug.sendAsMainTextTip2') }}</div>
</template>
<icon-question-circle
class="ml-[4px] text-[var(--color-text-4)] hover:text-[rgb(var(--primary-5))]"
size="16"
/>
</a-tooltip>
</div>
</div> </div>
</template> </template>
<paramTable <paramTable