refactor(接口测试): 完善请求详情java doc

This commit is contained in:
AgAngle 2024-01-28 11:55:31 +08:00 committed by Craftsman
parent 21a711e954
commit ae6fb6184a
48 changed files with 664 additions and 101 deletions

View File

@ -1,16 +1,25 @@
package io.metersphere.api.dto;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.Data;
/**
* 接口执行所需要的文件
*/
@Data
public class ApiFile {
/**
* 记录文件的ID防止重名
* 生成脚本时通过 fileId + value(文件名) 获取文件路径
* 生成脚本时通过 fileId + fileName(文件名) 获取文件路径
*/
@NotBlank
@Size(max = 50)
private String fileId;
/**
* 文件名
* 文件名
*/
@NotBlank
@Size(max = 200)
private String fileName;
}

View File

@ -3,12 +3,15 @@ package io.metersphere.api.dto.request;
import io.metersphere.api.dto.request.assertion.MsAssertionConfig;
import io.metersphere.api.dto.request.processors.MsProcessorConfig;
import io.metersphere.plugin.api.spi.AbstractMsTestElement;
import jakarta.validation.Valid;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 协议插件中通用的配置
* <pre>
* 添加到对应元素的 children 属性中
* </pre>
* @Author: jianxing
* @CreateTime: 2023-12-25 10:50
*/
@ -18,13 +21,16 @@ public class MsCommonElement extends AbstractMsTestElement {
/**
* 前置处理器配置
*/
@Valid
private MsProcessorConfig preProcessorConfig;
/**
* 后置处理器配置
*/
@Valid
private MsProcessorConfig postProcessorConfig;
/**
* 断言配置
*/
@Valid
private MsAssertionConfig assertionConfig;
}

View File

@ -2,10 +2,24 @@ package io.metersphere.api.dto.request.assertion;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import io.metersphere.api.dto.request.processors.extract.JSONPathExtract;
import io.metersphere.api.dto.request.processors.extract.RegexExtract;
import io.metersphere.api.dto.request.processors.extract.XPathExtract;
import jakarta.validation.constraints.Size;
import lombok.Data;
/**
* 断言
* <pre>
* 该参数传参时需要传入 assertionType 字段用于区分是哪种断言
* assertionType 取值为:
* RESPONSE_CODE {@link MsResponseCodeAssertion}
* RESPONSE_HEADER {@link MsResponseHeaderAssertion}
* RESPONSE_BODY {@link MsResponseBodyAssertion}
* RESPONSE_TIME {@link MsResponseTimeAssertion}
* SCRIPT {@link MsScriptAssertion}
* VARIABLE {@link MsVariableAssertion}
* </pre>
*/
@Data
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "assertionType")
@ -20,10 +34,12 @@ import lombok.Data;
public abstract class MsAssertion {
/**
* 是否启用
* 默认启用
*/
private Boolean enable = true;
/**
* 断言名称
*/
@Size(max = 100)
private String name;
}

View File

@ -13,8 +13,9 @@ import java.util.List;
public class MsAssertionConfig {
/**
* 是否启用全局断言
* 默认为 false
*/
private Boolean enableGlobal;
private Boolean enableGlobal = false;
/**
* 断言列表
*/

View File

@ -5,6 +5,8 @@ import io.metersphere.api.dto.request.assertion.body.MsDocumentAssertion;
import io.metersphere.api.dto.request.assertion.body.MsJSONPathAssertion;
import io.metersphere.api.dto.request.assertion.body.MsRegexAssertion;
import io.metersphere.api.dto.request.assertion.body.MsXPathAssertion;
import io.metersphere.system.valid.EnumValue;
import jakarta.validation.Valid;
import lombok.Data;
import java.util.HashMap;
@ -26,24 +28,29 @@ public class MsResponseBodyAssertion extends MsAssertion {
* 后端从设计层面支持多种断言前端只支持一种
* 同时切换可以同时持久化两种类型
*
* @see MsBodyAssertionType
* 取值参考 {@link MsBodyAssertionType}
*/
@EnumValue(enumClass = MsBodyAssertionType.class)
private String assertionBodyType;
/**
* jsonPath断言
*/
@Valid
private MsJSONPathAssertion jsonPathAssertion;
/**
* xpath断言
*/
@Valid
private MsXPathAssertion xpathAssertion;
/**
* 文档断言
*/
@Valid
private MsDocumentAssertion documentAssertion;
/**
* 正则断言
*/
@Valid
private MsRegexAssertion regexAssertion;
private static Map<MsBodyAssertionType, Class> bodyAssertionClassMap = new HashMap<>();
@ -68,6 +75,9 @@ public class MsResponseBodyAssertion extends MsAssertion {
return boadyAssertionMap.get(MsBodyAssertionType.valueOf(assertionBodyType));
}
/**
* 请求体断言类型
*/
public enum MsBodyAssertionType {
/**
* 正则断言

View File

@ -1,6 +1,7 @@
package io.metersphere.api.dto.request.assertion;
import com.fasterxml.jackson.annotation.JsonTypeName;
import jakarta.validation.constraints.Size;
import lombok.Data;
/**
@ -15,8 +16,9 @@ public class MsResponseCodeAssertion extends MsAssertion {
* 不校验即忽略状态
* 选择其他条件时也忽略状态
* 不校验可搭配其他校验使用
* @see io.metersphere.sdk.constants.MsAssertionCondition
* 取值参考 {@link io.metersphere.sdk.constants.MsAssertionCondition}
*/
@Size(max = 50)
private String condition;
/**
* 匹配值

View File

@ -1,6 +1,8 @@
package io.metersphere.api.dto.request.assertion;
import com.fasterxml.jackson.annotation.JsonTypeName;
import io.metersphere.system.valid.EnumValue;
import jakarta.validation.Valid;
import lombok.Data;
import java.util.List;
@ -13,14 +15,21 @@ import java.util.List;
@Data
@JsonTypeName("RESPONSE_HEADER")
public class MsResponseHeaderAssertion extends MsAssertion {
/**
* 断言列表
*/
@Valid
private List<ResponseHeaderAssertionItem> assertions;
/**
* 响应头断言项
*/
@Data
public static class ResponseHeaderAssertionItem {
/**
* 是否启用
* 默认启用
*/
private Boolean enable = true;
/**
@ -29,8 +38,9 @@ public class MsResponseHeaderAssertion extends MsAssertion {
private String header;
/**
* 匹配条件
* 值为 MsAssertionCondition
* 取值参考 {@link io.metersphere.sdk.constants.MsAssertionCondition}
*/
@EnumValue(enumClass = io.metersphere.sdk.constants.MsAssertionCondition.class)
private String condition;
/**
* 匹配值

View File

@ -9,5 +9,9 @@ import lombok.Data;
*/
@Data
public abstract class MsBodyAssertionItem {
/**
* 是否启用
* 默认启用
*/
private Boolean enable = true;
}

View File

@ -1,5 +1,8 @@
package io.metersphere.api.dto.request.assertion.body;
import io.metersphere.system.valid.EnumValue;
import jakarta.validation.Valid;
import jakarta.validation.constraints.Size;
import lombok.Data;
/**
@ -13,6 +16,7 @@ public class MsDocumentAssertion extends MsBodyAssertionItem {
* 跟随定义的apiId
* 传空为不跟随接口定义
*/
@Size(max = 50)
private String followApiId;
/**
* 文档类型
@ -21,16 +25,20 @@ public class MsDocumentAssertion extends MsBodyAssertionItem {
* 这里跟前端数据结构有差异
* 后端从设计层面支持多种文档格式前端只支持一种
* 同时切换可以同时持久化两种格式
* @see DocumentType
*/
*
* 取值参考 {@link DocumentType}
= */
@EnumValue(enumClass = DocumentType.class)
private String documentType;
/**
* json格式的文档断言
*/
@Valid
private MsDocumentAssertionElement jsonAssertion;
/**
* xml格式的文档断言
*/
@Valid
private MsDocumentAssertionElement xmlAssertion;
public enum DocumentType {

View File

@ -1,10 +1,16 @@
package io.metersphere.api.dto.request.assertion.body;
import io.metersphere.sdk.constants.MsAssertionCondition;
import io.metersphere.sdk.constants.ValueEnum;
import io.metersphere.system.valid.EnumValue;
import jakarta.validation.Valid;
import jakarta.validation.constraints.Size;
import lombok.Data;
import java.util.List;
/**
* 文档断言项
* @Author: jianxing
* @CreateTime: 2023-11-23 11:43
*/
@ -14,6 +20,7 @@ public class MsDocumentAssertionElement {
/**
* 参数名
*/
@Size(max = 100)
private String paramName;
/**
* 必含
@ -21,7 +28,9 @@ public class MsDocumentAssertionElement {
private Boolean include = false;
/**
* 类型
* 取值参考 {@link DocumentAssertionType}
*/
@EnumValue(enumClass = DocumentAssertionType.class)
private String type;
/**
* 类型校验
@ -29,7 +38,9 @@ public class MsDocumentAssertionElement {
private Boolean typeVerification = false;
/**
* 匹配条件
* 取值参考 {@link MsAssertionCondition}
*/
@EnumValue(enumClass = MsAssertionCondition.class)
private String condition;
/**
* 匹配值
@ -43,5 +54,28 @@ public class MsDocumentAssertionElement {
/**
* 子对象
*/
@Valid
private List<MsDocumentAssertionElement> children;
/**
* 文档断言类型
*/
public enum DocumentAssertionType implements ValueEnum {
STRING("string"),
NUMBER("number"),
INTEGER("integer"),
BOOLEAN("boolean"),
ARRAY("array");
private String value;
DocumentAssertionType(String value) {
this.value = value;
}
@Override
public String getValue() {
return value;
}
}
}

View File

@ -4,18 +4,27 @@ import lombok.Data;
import org.apache.commons.lang3.StringUtils;
/**
*
* JSONPath断言
*
* @Author: jianxing
* @CreateTime: 2023-11-23 14:04
*/
@Data
public class MsJSONPathAssertionItem extends MsBodyAssertionItem {
/**
* 表达式
*/
private String expression;
/**
* 匹配条件
* 取值参考 {@link io.metersphere.sdk.constants.MsAssertionCondition}
*/
private String condition;
/**
* 匹配值
*/
private String expectedValue;
public boolean isValid() {
return StringUtils.isNotBlank(expression) && StringUtils.isNotBlank(condition) && StringUtils.isNotBlank(expectedValue);
}

View File

@ -1,5 +1,6 @@
package io.metersphere.api.dto.request.assertion.body;
import jakarta.validation.Valid;
import lombok.Data;
import java.util.List;
@ -14,5 +15,6 @@ public class MsRegexAssertion {
/**
* 断言列表
*/
@Valid
private List<MsRegexAssertionItem> assertions;
}

View File

@ -10,6 +10,9 @@ import org.apache.commons.lang3.StringUtils;
*/
@Data
public class MsRegexAssertionItem extends MsBodyAssertionItem {
/**
* 表达式
*/
private String expression;
public boolean isValid() {

View File

@ -1,6 +1,7 @@
package io.metersphere.api.dto.request.assertion.body;
import io.metersphere.system.valid.EnumValue;
import lombok.Data;
import java.util.List;
@ -14,11 +15,12 @@ import java.util.List;
public class MsXPathAssertion {
/**
* 响应内容格式
* xml 或者 html
* 取值参考 {@link ResponseFormat}
*/
@EnumValue(enumClass = ResponseFormat.class)
private String responseFormat;
/**
* xpath断言
* xpath断言列表
*/
private List<MsXPathAssertionItem> assertions;

View File

@ -11,7 +11,13 @@ import org.apache.commons.lang3.StringUtils;
*/
@Data
public class MsXPathAssertionItem extends MsBodyAssertionItem {
/**
* 表达式
*/
private String expression;
/**
* 匹配值
*/
private String expectedValue;
public boolean isValid() {

View File

@ -3,6 +3,7 @@ package io.metersphere.api.dto.request.http;
import lombok.Data;
/**
* 请求头
* @Author: jianxing
* @CreateTime: 2023-11-06 16:59
*/

View File

@ -1,8 +1,10 @@
package io.metersphere.api.dto.request.http;
import jakarta.validation.constraints.Size;
import lombok.Data;
/**
* 可以启用禁用的键值对参数
* @Author: jianxing
* @CreateTime: 2023-11-06 17:27
*/
@ -10,10 +12,12 @@ import lombok.Data;
public class KeyValueEnableParam extends KeyValueParam {
/**
* 是否启用
* 默认启用
*/
private Boolean enable = true;
/**
* 描述
*/
@Size(max = 500)
private String description;
}

View File

@ -1,9 +1,11 @@
package io.metersphere.api.dto.request.http;
import jakarta.validation.constraints.Size;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;
/**
* 键值对参数
* @Author: jianxing
* @CreateTime: 2023-11-06 17:27
*/
@ -12,10 +14,12 @@ public class KeyValueParam {
/**
*
*/
@Size(max = 100)
private String key;
/**
*
*/
@Size(max = 100)
private String value;
public boolean isValid() {

View File

@ -0,0 +1,43 @@
package io.metersphere.api.dto.request.http;
import io.metersphere.sdk.constants.ValueEnum;
/**
* 键值对参数的参数类型
* rest 参数和 query 参数
* @Author: jianxing
* @CreateTime: 2024-01-27 11:22
*/
public enum KeyValueParamType implements ValueEnum {
/**
* 字符串类型
* 默认 application/text
*/
STRING("string"),
/**
* 整型
* 默认 application/text
*/
INTEGER("integer"),
/**
* 数值型
* 默认 application/text
*/
NUMBER("number"),
/**
* 数组
* 默认 application/text
*/
ARRAY("array");
private String value;
KeyValueParamType(String value) {
this.value = value;
}
@Override
public String getValue() {
return value;
}
}

View File

@ -1,16 +1,37 @@
package io.metersphere.api.dto.request.http;
import jakarta.validation.constraints.Size;
import lombok.Data;
/**
* http 的其他配置项
*
* @Author: jianxing
* @CreateTime: 2023-11-07 10:47
*/
@Data
public class MsHTTPConfig {
/**
* 连接超时
*/
private Long connectTimeout;
/**
* 响应超时
*/
private Long responseTimeout;
/**
* 证书别名
*/
@Size(max = 200)
private String certificateAlias;
/**
* 是否跟随重定向
* 默认 true
*/
private Boolean followRedirects = true;
/**
* 是否自动重定向
* 默认 false
*/
private Boolean autoRedirects = false;
}

View File

@ -3,50 +3,69 @@ package io.metersphere.api.dto.request.http;
import io.metersphere.api.dto.request.http.auth.HTTPAuth;
import io.metersphere.api.dto.request.http.body.Body;
import io.metersphere.plugin.api.spi.AbstractMsTestElement;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.List;
/**
* Http接口详情
* <pre>
* 其中包括接口调试接口定义接口用例场景的自定义请求 的详情
* 接口协议插件的接口详情也类似
* </pre>
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class MsHTTPElement extends AbstractMsTestElement {
// todo 完善字段校验
/**
* 完整请求地址
* 自定义请求时使用该字段
*/
@Size(max = 500)
private String url;
/**
* 接口定义和用例的请求路径
*/
@Size(max = 500)
private String path;
/**
* 请求方法
*/
@NotBlank
@Size(max = 10)
private String method;
/**
* 请求体
*/
@Valid
private Body body;
/**
* 请求头
*/
@Valid
private List<Header> headers;
/**
* rest参数
*/
@Valid
private List<RestParam> rest;
/**
* query参数
*/
@Valid
private List<QueryParam> query;
/**
* 其他配置
*/
@Valid
private MsHTTPConfig otherConfig;
/**
* 认证配置
*/
@Valid
private HTTPAuth authConfig;
}

View File

@ -1,8 +1,10 @@
package io.metersphere.api.dto.request.http;
import io.metersphere.system.valid.EnumValue;
import lombok.Data;
/**
* query 参数
* @Author: jianxing
* @CreateTime: 2023-11-06 16:59
*/
@ -11,10 +13,11 @@ public class QueryParam extends KeyValueEnableParam {
/**
* 参数类型
* 默认string可选integernumberarray
* todo
* 取值参考 {@link KeyValueParamType}
* 默认String
*/
private String paramType;
@EnumValue(enumClass = KeyValueParamType.class)
private String paramType = KeyValueParamType.STRING.getValue();
/**
* 是否必填
*/
@ -29,6 +32,7 @@ public class QueryParam extends KeyValueEnableParam {
private Integer maxLength;
/**
* 是否编码
* 默认 false
*/
private Boolean encode = false;
}

View File

@ -1,8 +1,10 @@
package io.metersphere.api.dto.request.http;
import io.metersphere.system.valid.EnumValue;
import lombok.Data;
/**
* rest参数
* @Author: jianxing
* @CreateTime: 2023-11-06 16:59
*/
@ -10,10 +12,11 @@ import lombok.Data;
public class RestParam extends KeyValueEnableParam {
/**
* 参数类型
* 默认string可选integernumberarray
* todo
* 取值参考 {@link KeyValueParamType}
* 默认String
*/
private String paramType;
@EnumValue(enumClass = KeyValueParamType.class)
private String paramType = KeyValueParamType.STRING.getValue();
/**
* 是否必填
*/

View File

@ -5,6 +5,14 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo;
import lombok.Data;
/**
* http 认证配置
* <pre>
* 该参数传参时需要传入 authType 字段用于区分是哪种认证方式
* authType 取值为:
* BASIC ({@link BasicAuth})
* DIGEST ({@link DigestAuth})
* NONE ({@link NoAuth})
* </pre>
* @Author: jianxing
* @CreateTime: 2023-11-07 11:00
*/

View File

@ -4,11 +4,14 @@ import io.metersphere.api.dto.ApiFile;
import lombok.Data;
/**
* binary 请求体
* @Author: jianxing
* @CreateTime: 2023-11-06 18:25
*/
@Data
public class BinaryBody {
private ApiFile bodyFile;
public class BinaryBody extends ApiFile{
/**
* 描述
*/
private String description;
}

View File

@ -1,11 +1,15 @@
package io.metersphere.api.dto.request.http.body;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.Data;
import java.util.HashMap;
import java.util.Map;
/**
* 请求体
* @Author: jianxing
* @CreateTime: 2023-11-06 16:59
*/
@ -13,18 +17,58 @@ import java.util.Map;
public class Body {
/**
* 当前选择的请求体类型
* @see BodyType
* 可选值为 {@link BodyType}
* 同时持久化多个类型的请求体
*/
@NotBlank
@Size(max = 20)
private String bodyType;
/**
* None 请求体
* bodyType NONE 使用该字段
*/
private NoneBody noneBody;
/**
* form-data 请求体
* bodyType FORM_DATA 使用该字段
*/
@Valid
private FormDataBody formDataBody;
/**
* x-www-form-urlencoded 请求体
* bodyType WWW_FORM 使用该字段
*/
@Valid
private WWWFormBody wwwFormBody;
/**
* json 请求体
* bodyType JSON 使用该字段
*/
@Valid
private JsonBody jsonBody;
/**
* xml 请求体
* bodyType XML 使用该字段
*/
@Valid
private XmlBody xmlBody;
/**
* raw 请求体
* bodyType RAW 使用该字段
*/
@Valid
private RawBody rawBody;
/**
* binary 请求体
* bodyType BINARY 使用该字段
*/
@Valid
private BinaryBody binaryBody;
/**
* 请求体类型与请求体类的映射
* 不需要传惨
*/
private static Map<BodyType, Class> bodyTypeClassMap = new HashMap<>();
static {
@ -54,13 +98,37 @@ public class Body {
}
/**
* 请求体类型
*/
public enum BodyType {
/**
* 二进制文件
*/
BINARY,
/**
* form-data
*/
FORM_DATA,
/**
* none
*/
NONE,
/**
* raw
*/
RAW,
/**
* x-www-form-urlencoded
*/
WWW_FORM,
/**
* xml
*/
XML,
/**
* json
*/
JSON
}
}

View File

@ -0,0 +1,57 @@
package io.metersphere.api.dto.request.http.body;
/**
* 请求体键值参数的参数类型
* x-www-form-urlencoded form-data
* @Author: jianxing
* @CreateTime: 2024-01-26 10:59
*/
import io.metersphere.sdk.constants.ValueEnum;
/**
*
*/
public enum BodyParamType implements ValueEnum {
/**
* 字符串类型
* 默认 application/text
*/
STRING("string"),
/**
* 整型
* 默认 application/text
*/
INTEGER("integer"),
/**
* 数值型
* 默认 application/text
*/
NUMBER("number"),
/**
* 数组
* 默认 application/text
*/
ARRAY("array"),
/**
* 文件类型
* 默认 application/octet-stream
*/
FILE("file"),
/**
* json 类型
* 默认 application/json
*/
JSON("json");
private String value;
BodyParamType(String value) {
this.value = value;
}
@Override
public String getValue() {
return value;
}
}

View File

@ -10,5 +10,8 @@ import java.util.List;
*/
@Data
public class FormDataBody {
/**
* form-data 请求体的键值对列表
*/
private List<FormDataKV> fromValues;
}

View File

@ -1,21 +1,29 @@
package io.metersphere.api.dto.request.http.body;
import io.metersphere.api.dto.ApiFile;
import jakarta.validation.Valid;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;
import java.util.List;
/**
* form-data 请求体的键值对
* @Author: jianxing
* @CreateTime: 2023-11-06 18:11
*/
@Data
public class FormDataKV extends WWWFormKV {
/**
* 参数的文件列表
* paramType FILE 参数值使用该字段
* 其他类型使用 value字段
*/
@Valid
private List<ApiFile> files;
public boolean isFile() {
return StringUtils.equalsIgnoreCase(getParamType(), WWWFormParamType.FILE.name());
return StringUtils.equalsIgnoreCase(getParamType(), BodyParamType.FILE.getValue());
}
}

View File

@ -1,9 +1,11 @@
package io.metersphere.api.dto.request.http.body;
import io.metersphere.api.dto.schema.JsonSchemaItem;
import jakarta.validation.Valid;
import lombok.Data;
/**
* json 请求体
* @Author: jianxing
* @CreateTime: 2023-11-06 18:25
*/
@ -11,19 +13,23 @@ import lombok.Data;
public class JsonBody {
/**
* 是否启用 json-schema
* 默认false
*/
private Boolean enableJsonSchema = false;
/**
* 没有启用 json-schema 时的 json 参数值
* json 参数值
* enableJsonSchema false 时使用该值
*/
private String jsonValue;
/**
* 启用 json-schema 时的参数对象
* todo json-schema 编辑器待调研暂时使用 Object 类型
* enableJsonSchema true 时使用该值
*/
@Valid
private JsonSchemaItem jsonSchema;
/**
* 是否开启转换
* 是否开启动态转换
* 默认为 false
*/
private Boolean enable = false;
private Boolean enableTransition = false;
}

View File

@ -3,10 +3,14 @@ package io.metersphere.api.dto.request.http.body;
import lombok.Data;
/**
* raw 请求体
* @Author: jianxing
* @CreateTime: 2023-11-06 18:25
*/
@Data
public class RawBody {
/**
* 请求体值
*/
private String value;
}

View File

@ -1,14 +1,17 @@
package io.metersphere.api.dto.request.http.body;
import jakarta.validation.Valid;
import lombok.Data;
import java.util.List;
/**
* x-www-form-urlencoded 请求体的键值对列表
* @Author: jianxing
* @CreateTime: 2023-11-06 16:59
*/
@Data
public class WWWFormBody {
@Valid
private List<FormDataKV> fromValues;
}

View File

@ -1,9 +1,13 @@
package io.metersphere.api.dto.request.http.body;
import io.metersphere.api.dto.request.http.KeyValueEnableParam;
import io.metersphere.system.valid.EnumValue;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.Data;
/**
* x-www-form-urlencoded 请求体键值对
* @Author: jianxing
* @CreateTime: 2023-11-06 18:11
*/
@ -11,28 +15,33 @@ import lombok.Data;
public class WWWFormKV extends KeyValueEnableParam {
/**
* 参数类型
*
* @see WWWFormParamType
* 取值参考 {@link BodyParamType} 中的 value 属性
*/
@NotBlank
@Size(max = 20)
@EnumValue(enumClass = BodyParamType.class)
private String paramType;
/**
* 是否必填
* 默认为 false
*/
private Boolean required = false;
/**
* 最小长度
*/
private Integer minLength;
/**
* 最大长度
*/
private Integer maxLength;
/**
* 参数的 contentType
*/
@Size(max = 100)
private String contentType;
/**
* 是否对参数进行编码
* 默认 false
*/
private Boolean encode = false;
enum WWWFormParamType {
/**
* 默认 application/text
*/
STRING, INTEGER, NUMBER, ARRAY,
/**
* 默认 application/octet-stream
*/
FILE,
/**
* 默认 application/json
*/
JSON
}
}

View File

@ -3,10 +3,14 @@ package io.metersphere.api.dto.request.http.body;
import lombok.Data;
/**
* xml 请求体
* @Author: jianxing
* @CreateTime: 2023-11-06 18:25
*/
@Data
public class XmlBody {
/**
* 请求体值
*/
private String value;
}

View File

@ -7,6 +7,7 @@ import lombok.Data;
import java.util.List;
/**
* 提取处理器配置
* @Author: jianxing
* @CreateTime: 2023-11-22 11:08
*/

View File

@ -2,9 +2,20 @@ package io.metersphere.api.dto.request.processors;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.Data;
/**
* 前后置处理器配置
* <pre>
* 该参数传参时需要传入 processorType 字段用于区分是哪种认证处理器
* processorType 取值为
* SCRIPT {@link ScriptProcessor}
* SQL {@link SQLProcessor}
* TIME_WAITING {@link TimeWaitingProcessor}
* EXTRACT {@link ExtractPostProcessor}
* <pre>
* @Author: jianxing
* @CreateTime: 2023-11-07 10:17
*/
@ -20,6 +31,8 @@ public abstract class MsProcessor {
/**
* 名称
*/
@NotBlank
@Size(max = 100)
private String name;
/**
* 是否启用

View File

@ -1,10 +1,12 @@
package io.metersphere.api.dto.request.processors;
import jakarta.validation.Valid;
import lombok.Data;
import java.util.List;
/**
* 前后置处理器配置
* @Author: jianxing
* @CreateTime: 2023-11-07 10:17
*/
@ -12,10 +14,12 @@ import java.util.List;
public class MsProcessorConfig {
/**
* 是否启用全局前置
* 默认为 false
*/
private Boolean enableGlobal;
private Boolean enableGlobal = false;
/**
* 处理器
* 处理器列表
*/
@Valid
private List<MsProcessor> processors;
}

View File

@ -3,11 +3,15 @@ package io.metersphere.api.dto.request.processors;
import com.fasterxml.jackson.annotation.JsonTypeName;
import io.metersphere.api.dto.request.http.KeyValueEnableParam;
import io.metersphere.api.dto.request.http.KeyValueParam;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.Data;
import java.util.List;
/**
* SQL 处理器
* @Author: jianxing
* @CreateTime: 2023-11-06 21:12
*/
@ -25,25 +29,32 @@ public class SQLProcessor extends MsProcessor {
/**
* 存储结果
*/
@Size(max = 200)
private String resultVariable;
/**
* 按列存储
*/
@Size(max = 200)
private String variableNames;
/**
* 变量列表
*/
@Valid
private List<KeyValueEnableParam> variables;
/**
* 环境ID
*/
@Size(max = 50)
private String environmentId;
/**
* 数据源ID
*/
@NotBlank
@Size(max = 50)
private String dataSourceId;
/**
* 提取参数
*/
@Valid
private List<KeyValueParam> extractParams;
}

View File

@ -3,6 +3,8 @@ package io.metersphere.api.dto.request.processors;
import com.fasterxml.jackson.annotation.JsonTypeName;
import io.metersphere.api.dto.request.http.KeyValueParam;
import io.metersphere.project.constants.ScriptLanguageType;
import jakarta.validation.Valid;
import jakarta.validation.constraints.Size;
import lombok.Data;
import java.util.List;
@ -20,19 +22,23 @@ public class ScriptProcessor extends MsProcessor {
private String script;
/**
* 脚本语言
* @see ScriptLanguageType
* {@link ScriptLanguageType}
*/
@Size(max = 20)
private String scriptLanguage;
/**
* 是否启用公共脚本
* 默认为 false
*/
private Boolean enableCommonScript = false;
/**
* 脚本ID
* 公共脚本ID
*/
@Size(max = 50)
private String scriptId;
/**
* 公共脚本入参
*/
@Valid
private List<KeyValueParam> params;
}

View File

@ -4,11 +4,16 @@ import com.fasterxml.jackson.annotation.JsonTypeName;
import lombok.Data;
/**
* 等待时间处理器
* @Author: jianxing
* @CreateTime: 2023-11-07 09:59
*/
@Data
@JsonTypeName("TIME_WAITING")
public class TimeWaitingProcessor extends MsProcessor {
/**
* 等待时间
* 单位毫秒
*/
private Integer delay;
}

View File

@ -2,9 +2,21 @@ package io.metersphere.api.dto.request.processors.extract;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import io.metersphere.system.valid.EnumValue;
import jakarta.validation.constraints.Size;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;
/**
* 提取处理器
* <pre>
* 该参数传参时需要传入 extractType 字段用于区分是哪种提取
* extractType 取值为:
* REGEX {@link RegexExtract}
* JSON_PATH {@link JSONPathExtract}
* X_PATH {@link XPathExtract}
* <pre>
*/
@Data
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "extractType")
@JsonSubTypes({
@ -16,14 +28,19 @@ public abstract class MsExtract {
/**
* 变量名
*/
@Size(max = 100)
private String variableName;
/**
* 参数类型
* 取值参考 {@link MsExtractType}
*/
@Size(max = 100)
@EnumValue(enumClass = MsExtractType.class)
private String variableType;
/**
* 表达式
*/
@Size(max = 200)
private String expression;
/**
* 是否启用
@ -33,4 +50,19 @@ public abstract class MsExtract {
public boolean isValid() {
return StringUtils.isNotBlank(variableName) && StringUtils.isNotBlank(expression);
}
public enum MsExtractType {
/**
* 临时参数
*/
TEMPORARY,
/**
* 环境参数
*/
ENVIRONMENT,
/**
* 全局参数
*/
GLOBAL;
}
}

View File

@ -1,6 +1,7 @@
package io.metersphere.api.dto.request.processors.extract;
import com.fasterxml.jackson.annotation.JsonTypeName;
import io.metersphere.system.valid.EnumValue;
import lombok.Data;
@Data
@ -8,54 +9,43 @@ import lombok.Data;
public class RegexExtract extends ResultMatchingExtract {
/**
* 表达式匹配规则
* @see ExpressionRuleType
* 取值参考 {@link ExpressionRuleType} 中的 value
* 默认为表达式匹配
*/
private String expressionMatchingRule;
@EnumValue(enumClass = ExpressionRuleType.class)
private String expressionMatchingRule = ExpressionRuleType.EXPRESSION.name();
/**
* 提取范围
* @see ExtractScope
* 取值参考 {@link ExtractScope}
*/
@EnumValue(enumClass = ExtractScope.class)
private String extractScope;
/**
* 表达式匹配规则
*/
public enum ExpressionRuleType {
/**
* 匹配表达式
*/
EXPRESSION("$1$"),
EXPRESSION,
/**
* 匹配组
*/
GROUP("$0$");
private String value;
ExpressionRuleType(String value) {
this.value = value;
}
public String getValue() {
return value;
}
GROUP;
}
/**
* 提取对象
*/
public enum ExtractScope {
BODY("false"),
REQUEST_HEADERS("request_headers"),
UNESCAPED_BODY("unescaped"),
BODY_AS_DOCUMENT("as_document"),
RESPONSE_HEADERS("true"),
URL("URL"),
RESPONSE_CODE("code"),
RESPONSE_MESSAGE("message");
private String value;
ExtractScope(String value) {
this.value = value;
}
public String getValue() {
return value;
}
BODY,
REQUEST_HEADERS,
UNESCAPED_BODY,
BODY_AS_DOCUMENT,
RESPONSE_HEADERS,
URL,
RESPONSE_CODE,
RESPONSE_MESSAGE;
}
}

View File

@ -1,19 +1,27 @@
package io.metersphere.api.dto.request.processors.extract;
import io.metersphere.system.valid.EnumValue;
import jakarta.validation.constraints.Size;
import lombok.Data;
@Data
public abstract class ResultMatchingExtract extends MsExtract {
/**
* 结果匹配规则
* 值为 ResultMatchingRuleType
* 取值参考 {@link ResultMatchingRuleType}
* 默认随机匹配
*/
private String resultMatchingRule;
@Size(max = 100)
@EnumValue(enumClass = ResultMatchingRuleType.class)
private String resultMatchingRule = ResultMatchingRuleType.RANDOM.name();
/**
* 匹配第几条结果
*/
private Integer resultMatchingRuleNum;
/**
* 结果匹配规则
*/
public enum ResultMatchingRuleType {
/**
* 随机匹配

View File

@ -1,6 +1,7 @@
package io.metersphere.api.dto.request.processors.extract;
import com.fasterxml.jackson.annotation.JsonTypeName;
import io.metersphere.system.valid.EnumValue;
import lombok.Data;
/**
@ -9,6 +10,11 @@ import lombok.Data;
@Data
@JsonTypeName("X_PATH")
public class XPathExtract extends ResultMatchingExtract {
/**
* 提取范围
* 取值参考 {@link ResponseFormat}
*/
@EnumValue(enumClass = ResponseFormat.class)
private String responseFormat;
public enum ResponseFormat {

View File

@ -1,6 +1,11 @@
package io.metersphere.api.dto.schema;
import io.metersphere.sdk.constants.ValueEnum;
import io.metersphere.system.valid.EnumValue;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.Data;
import java.math.BigDecimal;
@ -8,22 +13,63 @@ import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* json-schema 参数项
*/
@Data
public class JsonSchemaItem {
/**
* 示例
*/
private Object example;
/**
* 参数ID
*/
@NotBlank
@Size(max = 50)
private String id;
/**
* 参数名称
*/
@Size(max = 200)
private String title;
private String type = "string";
/**
* 参数类型
* 取值范围参考 {@link JsonSchemaItemType}
* 默认为 string
*/
@EnumValue(enumClass = JsonSchemaItemType.class)
private String type = JsonSchemaItemType.STRING.value;
/**
* 参数描述
*/
private String description;
/**
* 子级参数
* type object 或者 array 使用该值
*/
@Valid
private JsonSchemaItem items;
private Map<String, Object> mock;
private Map<String, JsonSchemaItem> properties;
private JsonSchemaItem additionalProperties;
private List<String> required;
private String pattern;
/**
* 最大长度
*/
private Integer maxLength;
/**
* 最小长度
*/
private Integer minLength;
/**
* 最小值
*/
private BigDecimal minimum;
/**
* 最大值
*/
private BigDecimal maximum;
private String schema;
private String format;
@ -55,4 +101,28 @@ public class JsonSchemaItem {
this.items = new JsonSchemaItem();
}
}
/**
* json-schema 参数类型
*/
enum JsonSchemaItemType implements ValueEnum {
STRING("string"),
NUMBER("number"),
INTEGER("integer"),
BOOLEAN("boolean"),
OBJECT("object"),
ARRAY("array"),
NULL("null");
private String value;
JsonSchemaItemType(String value) {
this.value = value;
}
@Override
public String getValue() {
return value;
}
}
}

View File

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

View File

@ -8,6 +8,8 @@ import org.apache.jmeter.save.SaveService;
import org.apache.jmeter.testelement.TestElement;
import org.apache.jorphan.collections.HashTree;
import java.util.HashMap;
import static io.metersphere.api.parser.jmeter.constants.JmeterAlias.REGEX_EXTRACTOR_GUI;
/**
@ -23,18 +25,42 @@ public class RegexExtractConverter extends ExtractConverter<RegexExtract> {
extractor.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass(REGEX_EXTRACTOR_GUI));
extractor.setRefName(msExtract.getVariableName());
extractor.setRegex(msExtract.getExpression());
extractor.setUseField(msExtract.getExtractScope());
extractor.setEnabled(msExtract.getEnable());
extractor.setUseField(getUseField(msExtract.getExtractScope()));
extractor.setTemplate(getTemplate(msExtract.getExpressionMatchingRule()));
// 处理匹配多条等匹配规则
extractor.setMatchNumber(parseResultMatchingRule(msExtract));
// $1$提取 JSON 响应中的第一个匹配项 $0$用于提取整个 JSON 响应
if (StringUtils.isBlank(msExtract.getExpressionMatchingRule())) {
extractor.setTemplate(RegexExtract.ExpressionRuleType.EXPRESSION.getValue());
} else {
extractor.setTemplate(msExtract.getExpressionMatchingRule());
}
hashTree.add(extractor);
}
private String getTemplate(String expressionMatchingRule) {
// $1$提取 JSON 响应中的第一个匹配项 $0$用于提取整个 JSON 响应
HashMap<String, String> ruleValueMap = new HashMap<>() {{
put(RegexExtract.ExpressionRuleType.EXPRESSION.name(), "$1$");
put(RegexExtract.ExpressionRuleType.GROUP.name(), "$0$");
}};
if (StringUtils.isBlank(expressionMatchingRule)) {
return ruleValueMap.get(RegexExtract.ExpressionRuleType.EXPRESSION.name());
} else {
return ruleValueMap.get(expressionMatchingRule);
}
}
private String getUseField(String extractScope) {
HashMap<String, String> extractScopeMap = new HashMap<>() {{
put(RegexExtract.ExtractScope.BODY.name(), "false");
put(RegexExtract.ExtractScope.REQUEST_HEADERS.name(), "request_headers");
put(RegexExtract.ExtractScope.UNESCAPED_BODY.name(), "unescaped");
put(RegexExtract.ExtractScope.BODY_AS_DOCUMENT.name(), "as_document");
put(RegexExtract.ExtractScope.RESPONSE_HEADERS.name(), "true");
put(RegexExtract.ExtractScope.URL.name(), "URL");
put(RegexExtract.ExtractScope.RESPONSE_CODE.name(), "code");
put(RegexExtract.ExtractScope.RESPONSE_MESSAGE.name(), "message");
}};
if (StringUtils.isNotBlank(extractScope)) {
return extractScopeMap.get(extractScope);
} else {
return RegexExtract.ExtractScope.BODY.name();
}
}
}

View File

@ -24,6 +24,7 @@ import io.metersphere.plugin.api.dto.ParameterConfig;
import io.metersphere.plugin.api.spi.AbstractMsTestElement;
import io.metersphere.project.constants.ScriptLanguageType;
import io.metersphere.sdk.constants.MsAssertionCondition;
import io.metersphere.sdk.util.BeanUtils;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
@ -93,10 +94,7 @@ public class MsHTTPElementTest {
xmlBody.setValue("<a/>");
body.setXmlBody(xmlBody);
BinaryBody binaryBody = new BinaryBody();
binaryBody.setBodyFile(bodyFile);
body.setBinaryBody(binaryBody);
body.setBinaryBody(BeanUtils.copyBean(new BinaryBody(), bodyFile));
return body;
}